From 1f80574dc52a267f2185d60fa5f331936135ced1 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Fri, 31 Jul 2009 18:11:33 -0400 Subject: [PATCH] Initial commit of syslinux 3.82 --- BUGS | 4 + COPYING | 340 + MCONFIG | 63 + MCONFIG.embedded | 45 + Makefile | 164 + NEWS | 1260 +++ README | 34 + TODO | 22 + codepage/Makefile | 21 + codepage/UnicodeData | 952 ++ codepage/cp1250.txt | 274 + codepage/cp1251.txt | 274 + codepage/cp1252.txt | 274 + codepage/cp1253.txt | 274 + codepage/cp1254.txt | 274 + codepage/cp1255.txt | 274 + codepage/cp1256.txt | 274 + codepage/cp1257.txt | 274 + codepage/cp1258.txt | 274 + codepage/cp437.txt | 274 + codepage/cp737.txt | 274 + codepage/cp775.txt | 275 + codepage/cp850.txt | 274 + codepage/cp852.txt | 274 + codepage/cp855.txt | 275 + codepage/cp857.txt | 275 + codepage/cp860.txt | 275 + codepage/cp861.txt | 275 + codepage/cp862.txt | 275 + codepage/cp863.txt | 275 + codepage/cp864.txt | 275 + codepage/cp865.txt | 275 + codepage/cp866.txt | 275 + codepage/cp869.txt | 275 + codepage/cp874.txt | 274 + codepage/cptable.pl | 146 + codepage/gensubset.pl | 57 + codepage/iso8859-1.txt | 303 + codepage/iso8859-10.txt | 303 + codepage/iso8859-11.txt | 297 + codepage/iso8859-13.txt | 299 + codepage/iso8859-14.txt | 301 + codepage/iso8859-15.txt | 303 + codepage/iso8859-16.txt | 299 + codepage/iso8859-2.txt | 303 + codepage/iso8859-3.txt | 296 + codepage/iso8859-4.txt | 303 + codepage/iso8859-5.txt | 303 + codepage/iso8859-6.txt | 260 + codepage/iso8859-7.txt | 308 + codepage/iso8859-8.txt | 270 + codepage/iso8859-9.txt | 307 + com32/LICENCE | 32 + com32/MCONFIG | 83 + com32/Makefile | 3 + com32/cmenu/.gitignore | 2 + com32/cmenu/CHANGES | 32 + com32/cmenu/HISTORY | 20 + com32/cmenu/MANUAL | 348 + com32/cmenu/MENU_FORMAT | 262 + com32/cmenu/Makefile | 83 + com32/cmenu/README | 95 + com32/cmenu/TODO | 2 + com32/cmenu/adv_menu.tpl | 452 + com32/cmenu/complex.c | 423 + com32/cmenu/complex.c32 | Bin 0 -> 34628 bytes com32/cmenu/display.c | 37 + com32/cmenu/display.c32 | Bin 0 -> 11612 bytes com32/cmenu/libmenu/com32io.c | 146 + com32/cmenu/libmenu/com32io.h | 112 + com32/cmenu/libmenu/com32io.o | Bin 0 -> 2688 bytes com32/cmenu/libmenu/des.c | 1101 ++ com32/cmenu/libmenu/des.h | 8 + com32/cmenu/libmenu/des.o | Bin 0 -> 8236 bytes com32/cmenu/libmenu/help.c | 200 + com32/cmenu/libmenu/help.h | 49 + com32/cmenu/libmenu/help.o | Bin 0 -> 3256 bytes com32/cmenu/libmenu/libmenu.a | Bin 0 -> 41378 bytes com32/cmenu/libmenu/menu.c | 1300 +++ com32/cmenu/libmenu/menu.h | 291 + com32/cmenu/libmenu/menu.o | Bin 0 -> 15964 bytes com32/cmenu/libmenu/passwords.c | 140 + com32/cmenu/libmenu/passwords.h | 27 + com32/cmenu/libmenu/passwords.o | Bin 0 -> 2508 bytes com32/cmenu/libmenu/scancodes.h | 86 + com32/cmenu/libmenu/syslnx.c | 88 + com32/cmenu/libmenu/syslnx.h | 64 + com32/cmenu/libmenu/syslnx.o | Bin 0 -> 1880 bytes com32/cmenu/libmenu/tui.c | 357 + com32/cmenu/libmenu/tui.h | 88 + com32/cmenu/libmenu/tui.o | Bin 0 -> 4548 bytes com32/cmenu/menugen.py | 307 + com32/cmenu/password | 18 + com32/cmenu/simple.c | 79 + com32/cmenu/simple.c32 | Bin 0 -> 17008 bytes com32/cmenu/test.c32 | Bin 0 -> 32140 bytes com32/cmenu/test.menu | 60 + com32/cmenu/test2.c32 | Bin 0 -> 33804 bytes com32/cmenu/test2.menu | 142 + com32/gdbstub/Makefile | 49 + com32/gdbstub/gdbstub.c | 529 + com32/gdbstub/int.S | 77 + com32/gdbstub/main.c | 148 + com32/gdbstub/serial.c | 190 + com32/gdbstub/serial.h | 14 + com32/gplinclude/README | 1 + com32/gplinclude/cpuid.h | 212 + com32/gplinclude/dmi/dmi.h | 85 + com32/gplinclude/dmi/dmi_base_board.h | 53 + com32/gplinclude/dmi/dmi_battery.h | 57 + com32/gplinclude/dmi/dmi_bios.h | 107 + com32/gplinclude/dmi/dmi_chassis.h | 50 + com32/gplinclude/dmi/dmi_ipmi.h | 33 + com32/gplinclude/dmi/dmi_memory.h | 61 + com32/gplinclude/dmi/dmi_processor.h | 111 + com32/gplinclude/dmi/dmi_system.h | 38 + com32/gplinclude/vpd/vpd.h | 33 + com32/gpllib/Makefile | 45 + com32/gpllib/cpuid.c | 328 + com32/gpllib/dmi/dmi.c | 618 ++ com32/gpllib/dmi/dmi_base_board.c | 37 + com32/gpllib/dmi/dmi_battery.c | 72 + com32/gpllib/dmi/dmi_bios.c | 79 + com32/gpllib/dmi/dmi_chassis.c | 109 + com32/gpllib/dmi/dmi_ipmi.c | 58 + com32/gpllib/dmi/dmi_memory.c | 171 + com32/gpllib/dmi/dmi_processor.c | 432 + com32/gpllib/vpd/vpd.c | 103 + com32/hdt/Makefile | 47 + com32/hdt/hdt-ata.c | 281 + com32/hdt/hdt-ata.h | 122 + com32/hdt/hdt-cli-cpu.c | 194 + com32/hdt/hdt-cli-dmi.c | 495 + com32/hdt/hdt-cli-hdt.c | 347 + com32/hdt/hdt-cli-kernel.c | 158 + com32/hdt/hdt-cli-pci.c | 313 + com32/hdt/hdt-cli-pxe.c | 92 + com32/hdt/hdt-cli-syslinux.c | 61 + com32/hdt/hdt-cli-vesa.c | 103 + com32/hdt/hdt-cli-vpd.c | 74 + com32/hdt/hdt-cli.c | 1025 ++ com32/hdt/hdt-cli.h | 187 + com32/hdt/hdt-common.c | 549 + com32/hdt/hdt-common.h | 145 + com32/hdt/hdt-menu-about.c | 71 + com32/hdt/hdt-menu-disk.c | 181 + com32/hdt/hdt-menu-dmi.c | 540 + com32/hdt/hdt-menu-kernel.c | 91 + com32/hdt/hdt-menu-pci.c | 194 + com32/hdt/hdt-menu-processor.c | 240 + com32/hdt/hdt-menu-pxe.c | 120 + com32/hdt/hdt-menu-summary.c | 209 + com32/hdt/hdt-menu-syslinux.c | 85 + com32/hdt/hdt-menu-vesa.c | 109 + com32/hdt/hdt-menu-vpd.c | 100 + com32/hdt/hdt-menu.c | 319 + com32/hdt/hdt-menu.h | 134 + com32/hdt/hdt.c | 83 + com32/hdt/hdt.c32 | Bin 0 -> 107552 bytes com32/hdt/hdt.h | 44 + com32/hdt/lib-ansi.c | 97 + com32/hdt/lib-ansi.h | 43 + com32/include/alloca.h | 12 + com32/include/assert.h | 7 + com32/include/bitsize/limits.h | 14 + com32/include/bitsize/stddef.h | 18 + com32/include/bitsize/stdint.h | 34 + com32/include/bitsize/stdintconst.h | 18 + com32/include/bitsize/stdintlimits.h | 22 + com32/include/colortbl.h | 53 + com32/include/com32.h | 175 + com32/include/console.h | 63 + com32/include/cpufeature.h | 98 + com32/include/ctype.h | 119 + com32/include/dev.h | 55 + com32/include/dirent.h | 36 + com32/include/elf.h | 11 + com32/include/endian.h | 15 + com32/include/errno.h | 134 + com32/include/fcntl.h | 24 + com32/include/inttypes.h | 226 + com32/include/klibc/archsetjmp.h | 19 + com32/include/klibc/compiler.h | 129 + com32/include/klibc/diverr.h | 4 + com32/include/klibc/endian.h | 38 + com32/include/klibc/extern.h | 14 + com32/include/klibc/sysconfig.h | 34 + com32/include/limits.h | 39 + com32/include/math.h | 15 + com32/include/minmax.h | 42 + com32/include/netinet/in.h | 47 + com32/include/png.h | 3419 +++++++ com32/include/pngconf.h | 1437 +++ com32/include/setjmp.h | 22 + com32/include/stdarg.h | 14 + com32/include/stdbool.h | 32 + com32/include/stddef.h | 24 + com32/include/stdint.h | 142 + com32/include/stdio.h | 116 + com32/include/stdlib.h | 87 + com32/include/string.h | 47 + com32/include/sys/cpu.h | 114 + com32/include/sys/elf32.h | 113 + com32/include/sys/elf64.h | 113 + com32/include/sys/elfcommon.h | 187 + com32/include/sys/fpu.h | 6 + com32/include/sys/io.h | 42 + com32/include/sys/pci.h | 150 + com32/include/sys/stat.h | 52 + com32/include/sys/time.h | 6 + com32/include/sys/times.h | 21 + com32/include/sys/types.h | 16 + com32/include/syslinux/adv.h | 57 + com32/include/syslinux/advconst.h | 45 + com32/include/syslinux/align.h | 47 + com32/include/syslinux/boot.h | 63 + com32/include/syslinux/bootpm.h | 59 + com32/include/syslinux/bootrm.h | 74 + com32/include/syslinux/config.h | 175 + com32/include/syslinux/features.h | 50 + com32/include/syslinux/idle.h | 37 + com32/include/syslinux/io.h | 39 + com32/include/syslinux/keyboard.h | 52 + com32/include/syslinux/linux.h | 74 + com32/include/syslinux/loadfile.h | 15 + com32/include/syslinux/memscan.h | 38 + com32/include/syslinux/movebits.h | 90 + com32/include/syslinux/pxe.h | 565 + com32/include/syslinux/reboot.h | 41 + com32/include/syslinux/resolve.h | 41 + com32/include/syslinux/vesacon.h | 35 + com32/include/syslinux/video.h | 43 + com32/include/syslinux/zio.h | 13 + com32/include/time.h | 6 + com32/include/tinyjpeg.h | 72 + com32/include/unistd.h | 33 + com32/include/zconf.h | 322 + com32/include/zlib.h | 1200 +++ com32/lib/MCONFIG | 69 + com32/lib/Makefile | 147 + com32/lib/abort.c | 11 + com32/lib/asprintf.c | 30 + com32/lib/atexit.c | 10 + com32/lib/atexit.h | 16 + com32/lib/atoi.c | 3 + com32/lib/atol.c | 3 + com32/lib/atoll.c | 3 + com32/lib/atox.c | 14 + com32/lib/calloc.c | 13 + com32/lib/chdir.c | 13 + com32/lib/closedir.c | 29 + com32/lib/com32.ld | 127 + com32/lib/creat.c | 12 + com32/lib/ctypes.c | 284 + com32/lib/errno.c | 7 + com32/lib/exit.c | 41 + com32/lib/fclose.c | 11 + com32/lib/fdopendir.c | 13 + com32/lib/fgetc.c | 19 + com32/lib/fgets.c | 31 + com32/lib/fopen.c | 43 + com32/lib/fopendev.c | 43 + com32/lib/fprintf.c | 19 + com32/lib/fputc.c | 14 + com32/lib/fputs.c | 15 + com32/lib/fread.c | 32 + com32/lib/fread2.c | 13 + com32/lib/free.c | 114 + com32/lib/fwrite.c | 32 + com32/lib/fwrite2.c | 13 + com32/lib/getcwd.c | 30 + com32/lib/getopt.c | 72 + com32/lib/init.h | 15 + com32/lib/jpeg/README | 32 + com32/lib/jpeg/bgr24.c | 362 + com32/lib/jpeg/bgra32.c | 380 + com32/lib/jpeg/decode1.c | 138 + com32/lib/jpeg/decode3.c | 142 + com32/lib/jpeg/grey.c | 170 + com32/lib/jpeg/jidctflt.c | 285 + com32/lib/jpeg/rgb24.c | 361 + com32/lib/jpeg/rgba32.c | 377 + com32/lib/jpeg/tinyjpeg-internal.h | 186 + com32/lib/jpeg/tinyjpeg.c | 910 ++ com32/lib/jpeg/yuv420p.c | 255 + com32/lib/libgcc/__ashldi3.S | 29 + com32/lib/libgcc/__ashrdi3.S | 29 + com32/lib/libgcc/__divdi3.c | 29 + com32/lib/libgcc/__lshrdi3.S | 29 + com32/lib/libgcc/__moddi3.c | 29 + com32/lib/libgcc/__muldi3.S | 34 + com32/lib/libgcc/__negdi2.S | 21 + com32/lib/libgcc/__udivdi3.c | 13 + com32/lib/libgcc/__udivmoddi4.c | 32 + com32/lib/libgcc/__umoddi3.c | 16 + com32/lib/libpng/ANNOUNCE | 32 + com32/lib/libpng/CHANGES | 1419 +++ com32/lib/libpng/KNOWNBUG | 10 + com32/lib/libpng/LICENSE | 113 + com32/lib/libpng/README | 262 + com32/lib/libpng/TODO | 24 + com32/lib/libpng/Y2KINFO | 55 + com32/lib/libpng/example.c | 808 ++ com32/lib/libpng/libpng.3 | 4021 ++++++++ com32/lib/libpng/libpng.txt | 2959 ++++++ com32/lib/libpng/libpngpf.3 | 1096 ++ com32/lib/libpng/png.5 | 73 + com32/lib/libpng/png.c | 828 ++ com32/lib/libpng/pngerror.c | 295 + com32/lib/libpng/pnggccrd.c | 5408 ++++++++++ com32/lib/libpng/pngget.c | 934 ++ com32/lib/libpng/pngmem.c | 595 ++ com32/lib/libpng/pngpread.c | 1573 +++ com32/lib/libpng/pngread.c | 1456 +++ com32/lib/libpng/pngrio.c | 161 + com32/lib/libpng/pngrtran.c | 4177 ++++++++ com32/lib/libpng/pngrutil.c | 3124 ++++++ com32/lib/libpng/pngset.c | 1219 +++ com32/lib/libpng/pngtest.c | 1554 +++ com32/lib/libpng/pngtrans.c | 650 ++ com32/lib/libpng/pngvcrd.c | 3903 +++++++ com32/lib/libpng/pngwio.c | 228 + com32/lib/libpng/pngwrite.c | 1464 +++ com32/lib/libpng/pngwtran.c | 563 + com32/lib/libpng/pngwutil.c | 2730 +++++ com32/lib/lrand48.c | 41 + com32/lib/malloc.c | 157 + com32/lib/malloc.h | 55 + com32/lib/math/pow.S | 25 + com32/lib/math/strtod.c | 161 + com32/lib/memccpy.c | 23 + com32/lib/memchr.c | 18 + com32/lib/memcmp.c | 19 + com32/lib/memcpy.S | 86 + com32/lib/memmem.c | 44 + com32/lib/memmove.S | 139 + com32/lib/mempcpy.S | 85 + com32/lib/memset.S | 86 + com32/lib/memswap.c | 23 + com32/lib/onexit.c | 39 + com32/lib/opendir.c | 41 + com32/lib/pci/cfgtype.c | 89 + com32/lib/pci/pci.h | 16 + com32/lib/pci/readb.c | 4 + com32/lib/pci/readbios.c | 15 + com32/lib/pci/readl.c | 4 + com32/lib/pci/readw.c | 4 + com32/lib/pci/readx.c | 52 + com32/lib/pci/scan.c | 572 ++ com32/lib/pci/writeb.c | 4 + com32/lib/pci/writebios.c | 14 + com32/lib/pci/writel.c | 4 + com32/lib/pci/writew.c | 4 + com32/lib/pci/writex.c | 50 + com32/lib/perror.c | 12 + com32/lib/printf.c | 19 + com32/lib/putchar.c | 16 + com32/lib/puts.c | 13 + com32/lib/qsort.c | 41 + com32/lib/readdir.c | 54 + com32/lib/realloc.c | 98 + com32/lib/seed48.c | 18 + com32/lib/setjmp.S | 63 + com32/lib/snprintf.c | 16 + com32/lib/sprintf.c | 18 + com32/lib/srand48.c | 16 + com32/lib/sscanf.c | 17 + com32/lib/stack.c | 4 + com32/lib/stpcpy.c | 23 + com32/lib/stpncpy.c | 23 + com32/lib/strcasecmp.c | 23 + com32/lib/strcat.c | 11 + com32/lib/strchr.c | 16 + com32/lib/strcmp.c | 20 + com32/lib/strcpy.c | 20 + com32/lib/strdup.c | 17 + com32/lib/strerror.c | 23 + com32/lib/strlcat.c | 29 + com32/lib/strlcpy.c | 24 + com32/lib/strlen.c | 13 + com32/lib/strncasecmp.c | 23 + com32/lib/strncat.c | 11 + com32/lib/strncmp.c | 20 + com32/lib/strncpy.c | 22 + com32/lib/strndup.c | 17 + com32/lib/strnlen.c | 13 + com32/lib/strntoimax.c | 13 + com32/lib/strntoumax.c | 75 + com32/lib/strpcpy.c | 20 + com32/lib/strrchr.c | 18 + com32/lib/strsep.c | 21 + com32/lib/strspn.c | 66 + com32/lib/strstr.c | 10 + com32/lib/strtoimax.c | 3 + com32/lib/strtok.c | 15 + com32/lib/strtol.c | 3 + com32/lib/strtoll.c | 3 + com32/lib/strtoul.c | 3 + com32/lib/strtoull.c | 3 + com32/lib/strtoumax.c | 3 + com32/lib/strtox.c | 13 + com32/lib/sys/ansi.c | 435 + com32/lib/sys/ansi.h | 64 + com32/lib/sys/ansicon_write.c | 258 + com32/lib/sys/ansiserial_write.c | 58 + com32/lib/sys/argv.c | 96 + com32/lib/sys/cfarcall.c | 11 + com32/lib/sys/close.c | 62 + com32/lib/sys/colortable.c | 9 + com32/lib/sys/entry.S | 94 + com32/lib/sys/err_read.c | 53 + com32/lib/sys/err_write.c | 53 + com32/lib/sys/exit.S | 41 + com32/lib/sys/farcall.c | 11 + com32/lib/sys/file.h | 108 + com32/lib/sys/fileclose.c | 52 + com32/lib/sys/fileinfo.c | 3 + com32/lib/sys/fileread.c | 93 + com32/lib/sys/fstat.c | 63 + com32/lib/sys/ftell.c | 16 + com32/lib/sys/intcall.c | 10 + com32/lib/sys/isatty.c | 53 + com32/lib/sys/line_input.c | 88 + com32/lib/sys/null_read.c | 52 + com32/lib/sys/null_write.c | 52 + com32/lib/sys/open.c | 90 + com32/lib/sys/openconsole.c | 51 + com32/lib/sys/opendev.c | 99 + com32/lib/sys/rawcon_read.c | 80 + com32/lib/sys/rawcon_write.c | 66 + com32/lib/sys/read.c | 51 + com32/lib/sys/screensize.c | 23 + com32/lib/sys/serial_write.c | 71 + com32/lib/sys/stdcon_read.c | 77 + com32/lib/sys/stdcon_write.c | 71 + com32/lib/sys/times.c | 44 + com32/lib/sys/vesa/alphatbl.pl | 53 + com32/lib/sys/vesa/background.c | 417 + com32/lib/sys/vesa/debug.h | 36 + com32/lib/sys/vesa/drawtxt.c | 317 + com32/lib/sys/vesa/fill.h | 66 + com32/lib/sys/vesa/fmtpixel.c | 102 + com32/lib/sys/vesa/initvesa.c | 321 + com32/lib/sys/vesa/screencpy.c | 122 + com32/lib/sys/vesa/vesa.h | 113 + com32/lib/sys/vesa/video.h | 91 + com32/lib/sys/vesacon_write.c | 171 + com32/lib/sys/vesaserial_write.c | 60 + com32/lib/sys/write.c | 51 + com32/lib/sys/x86_init_fpu.c | 58 + com32/lib/sys/xserial_write.c | 118 + com32/lib/sys/zeroregs.c | 5 + com32/lib/sys/zfile.c | 171 + com32/lib/sys/zfopen.c | 44 + com32/lib/syslinux/addlist.c | 45 + com32/lib/syslinux/adv.c | 49 + com32/lib/syslinux/advwrite.c | 45 + com32/lib/syslinux/cleanup.c | 40 + com32/lib/syslinux/config.c | 41 + com32/lib/syslinux/dsinfo.c | 45 + com32/lib/syslinux/dump_mmap.c | 48 + com32/lib/syslinux/dump_movelist.c | 47 + com32/lib/syslinux/features.c | 51 + com32/lib/syslinux/floadfile.c | 108 + com32/lib/syslinux/freelist.c | 46 + com32/lib/syslinux/getadv.c | 68 + com32/lib/syslinux/idle.c | 57 + com32/lib/syslinux/initramfs.c | 76 + com32/lib/syslinux/initramfs_archive.c | 47 + com32/lib/syslinux/initramfs_file.c | 163 + com32/lib/syslinux/initramfs_loadfile.c | 48 + com32/lib/syslinux/ipappend.c | 58 + com32/lib/syslinux/keyboard.c | 44 + com32/lib/syslinux/load_linux.c | 509 + com32/lib/syslinux/loadfile.c | 63 + com32/lib/syslinux/localboot.c | 42 + com32/lib/syslinux/memmap.c | 65 + com32/lib/syslinux/memscan.c | 153 + com32/lib/syslinux/movebits.c | 709 ++ com32/lib/syslinux/pxe_get_cached.c | 81 + com32/lib/syslinux/pxe_get_nic.c | 61 + com32/lib/syslinux/reboot.c | 47 + com32/lib/syslinux/run_command.c | 47 + com32/lib/syslinux/run_default.c | 41 + com32/lib/syslinux/runimage.c | 65 + com32/lib/syslinux/serial.c | 52 + com32/lib/syslinux/setadv.c | 116 + com32/lib/syslinux/shuffle.c | 272 + com32/lib/syslinux/shuffle_pm.c | 78 + com32/lib/syslinux/shuffle_rm.c | 171 + com32/lib/syslinux/version.c | 46 + com32/lib/syslinux/zloadfile.c | 59 + com32/lib/syslinux/zonelist.c | 301 + com32/lib/vasprintf.c | 25 + com32/lib/vfprintf.c | 26 + com32/lib/vprintf.c | 11 + com32/lib/vsnprintf.c | 433 + com32/lib/vsprintf.c | 11 + com32/lib/vsscanf.c | 365 + com32/lib/zalloc.c | 17 + com32/lib/zlib/FAQ | 315 + com32/lib/zlib/README | 126 + com32/lib/zlib/adler32.c | 73 + com32/lib/zlib/algorithm.txt | 209 + com32/lib/zlib/compress.c | 78 + com32/lib/zlib/crc32.c | 310 + com32/lib/zlib/crc32.h | 441 + com32/lib/zlib/deflate.c | 1501 +++ com32/lib/zlib/deflate.h | 325 + com32/lib/zlib/example.c | 566 ++ com32/lib/zlib/gzio.c | 1017 ++ com32/lib/zlib/infback.c | 619 ++ com32/lib/zlib/inffast.c | 305 + com32/lib/zlib/inffast.h | 11 + com32/lib/zlib/inffixed.h | 94 + com32/lib/zlib/inflate.c | 1270 +++ com32/lib/zlib/inflate.h | 117 + com32/lib/zlib/inftrees.c | 321 + com32/lib/zlib/inftrees.h | 55 + com32/lib/zlib/minigzip.c | 322 + com32/lib/zlib/trees.c | 1214 +++ com32/lib/zlib/trees.h | 127 + com32/lib/zlib/uncompr.c | 60 + com32/lib/zlib/zconf.in.h | 322 + com32/lib/zlib/zlib.3 | 159 + com32/lib/zlib/zutil.c | 318 + com32/lib/zlib/zutil.h | 257 + com32/libutil/Makefile | 65 + com32/libutil/ansiline.c | 84 + com32/libutil/ansiraw.c | 86 + com32/libutil/base64.c | 94 + com32/libutil/crypt-md5.c | 176 + com32/libutil/get_key.c | 171 + com32/libutil/include/base64.h | 51 + com32/libutil/include/consoles.h | 40 + com32/libutil/include/getkey.h | 80 + com32/libutil/include/libutil.h | 51 + com32/libutil/include/md5.h | 46 + com32/libutil/include/minmax.h | 42 + com32/libutil/include/sha1.h | 18 + com32/libutil/include/xcrypt.h | 10 + com32/libutil/libutil_com.a | Bin 0 -> 36518 bytes com32/libutil/libutil_lnx.a | Bin 0 -> 105746 bytes com32/libutil/md5.c | 280 + com32/libutil/sha1hash.c | 308 + com32/libutil/sha256crypt.c | 725 ++ com32/libutil/sha512crypt.c | 794 ++ com32/libutil/unbase64.c | 77 + com32/mboot/Makefile | 46 + com32/mboot/apm.c | 87 + com32/mboot/map.c | 335 + com32/mboot/mb_header.h | 88 + com32/mboot/mb_info.h | 211 + com32/mboot/mboot.c | 229 + com32/mboot/mboot.c32 | Bin 0 -> 31148 bytes com32/mboot/mboot.h | 95 + com32/mboot/mem.c | 206 + com32/mboot/solaris.c | 52 + com32/menu/Makefile | 49 + com32/menu/background.c | 26 + com32/menu/colors.c | 183 + com32/menu/drain.c | 25 + com32/menu/execute.c | 69 + com32/menu/menu.c | 34 + com32/menu/menu.c32 | Bin 0 -> 57140 bytes com32/menu/menu.h | 227 + com32/menu/menumain.c | 1096 ++ com32/menu/passwd.c | 96 + com32/menu/printmsg.c | 119 + com32/menu/readconfig.c | 1072 ++ com32/menu/refstr.c | 105 + com32/menu/refstr.h | 40 + com32/menu/vesamenu.c | 42 + com32/menu/vesamenu.c32 | Bin 0 -> 147728 bytes com32/modules/Makefile | 58 + com32/modules/chain.c | 768 ++ com32/modules/chain.c32 | Bin 0 -> 13604 bytes com32/modules/cmd.c | 25 + com32/modules/cmd.c32 | Bin 0 -> 520 bytes com32/modules/config.c | 38 + com32/modules/config.c32 | Bin 0 -> 4360 bytes com32/modules/cpuidtest.c | 98 + com32/modules/cpuidtest.c32 | Bin 0 -> 10316 bytes com32/modules/dmi_utils.c | 68 + com32/modules/dmitest.c | 211 + com32/modules/dmitest.c32 | Bin 0 -> 29000 bytes com32/modules/elf.c | 292 + com32/modules/elf.c32 | Bin 0 -> 27176 bytes com32/modules/ethersel.c | 212 + com32/modules/ethersel.c32 | Bin 0 -> 10532 bytes com32/modules/ifcpu64.c | 123 + com32/modules/ifcpu64.c32 | Bin 0 -> 904 bytes com32/modules/kbdmap.c | 54 + com32/modules/kbdmap.c32 | Bin 0 -> 4644 bytes com32/modules/linux.c | 216 + com32/modules/linux.c32 | Bin 0 -> 16556 bytes com32/modules/meminfo.c | 125 + com32/modules/meminfo.c32 | Bin 0 -> 5000 bytes com32/modules/pcitest.c | 148 + com32/modules/pcitest.c32 | Bin 0 -> 14504 bytes com32/modules/pmload.c | 230 + com32/modules/pmload.c32 | Bin 0 -> 12836 bytes com32/modules/reboot.c | 16 + com32/modules/reboot.c32 | Bin 0 -> 520 bytes com32/modules/sanboot.c | 140 + com32/modules/sanboot.c32 | Bin 0 -> 1800 bytes com32/modules/sdi.c | 194 + com32/modules/sdi.c32 | Bin 0 -> 24616 bytes com32/modules/stubs/copydown.asm | 11 + com32/modules/stubs/swapstub.asm | 33 + com32/modules/vesainfo.c | 83 + com32/modules/vesainfo.c32 | Bin 0 -> 4872 bytes com32/modules/vpdtest.c | 68 + com32/modules/vpdtest.c32 | Bin 0 -> 5772 bytes com32/rosh/MCONFIG | 27 + com32/rosh/Makefile | 38 + com32/rosh/rosh.c | 810 ++ com32/rosh/rosh.c32 | Bin 0 -> 17264 bytes com32/rosh/rosh.h | 148 + com32/samples/Makefile | 38 + com32/samples/advdump.c | 53 + com32/samples/advdump.c32 | Bin 0 -> 4876 bytes com32/samples/cat.c | 31 + com32/samples/cat.c32 | Bin 0 -> 5772 bytes com32/samples/entrydump.c | 60 + com32/samples/entrydump.c32 | Bin 0 -> 4488 bytes com32/samples/fancyhello.c | 42 + com32/samples/fancyhello.c32 | Bin 0 -> 9044 bytes com32/samples/fancyhello.lnx | Bin 0 -> 10699 bytes com32/samples/hello.c | 35 + com32/samples/hello.c32 | Bin 0 -> 4748 bytes com32/samples/keytest.c | 81 + com32/samples/keytest.c32 | Bin 0 -> 9812 bytes com32/samples/keytest.lnx | Bin 0 -> 22178 bytes com32/samples/localboot.c | 9 + com32/samples/localboot.c32 | Bin 0 -> 1160 bytes com32/samples/resolv.c | 66 + com32/samples/resolv.c32 | Bin 0 -> 4360 bytes com32/samples/serialinfo.c | 40 + com32/samples/serialinfo.c32 | Bin 0 -> 4232 bytes core/Makefile | 125 + core/abort.inc | 84 + core/adv.inc | 503 + core/bcopy32.inc | 364 + core/bcopyxx.inc | 315 + core/bios.inc | 39 + core/bootsect.inc | 214 + core/cache.inc | 114 + core/checksumiso.pl | 51 + core/cleanup.inc | 55 + core/cmdline.inc | 94 + core/com32.inc | 428 + core/comboot.inc | 1049 ++ core/config.inc | 39 + core/configinit.inc | 59 + core/conio.inc | 431 + core/cpuinit.inc | 52 + core/diskstart.inc | 687 ++ core/dnsresolv.inc | 389 + core/ext2_fs.inc | 183 + core/extlinux.asm | 931 ++ core/extlinux.bin | Bin 0 -> 13414 bytes core/extlinux.bss | Bin 0 -> 512 bytes core/extlinux.sys | Bin 0 -> 12902 bytes core/font.inc | 152 + core/genhash.pl | 26 + core/getc.inc | 415 + core/graphics.inc | 330 + core/head.inc | 32 + core/highmem.inc | 158 + core/idle.inc | 78 + core/init.inc | 47 + core/io.inc | 35 + core/isolinux-debug.asm | 2 + core/isolinux-debug.bin | Bin 0 -> 14336 bytes core/isolinux.asm | 1807 ++++ core/isolinux.bin | Bin 0 -> 14336 bytes core/kernel.inc | 113 + core/keywords | 48 + core/keywords.inc | 101 + core/kwdhash.gen | 48 + core/layout.inc | 126 + core/ldlinux.asm | 1427 +++ core/ldlinux.bin | Bin 0 -> 15430 bytes core/ldlinux.bss | Bin 0 -> 512 bytes core/ldlinux.sys | Bin 0 -> 14918 bytes core/loadhigh.inc | 114 + core/localboot.inc | 75 + core/lstadjust.pl | 57 + core/macros.inc | 116 + core/parsecmd.inc | 134 + core/parseconfig.inc | 478 + core/plaincon.inc | 24 + core/pxe.inc | 154 + core/pxeidle.inc | 122 + core/pxelinux.0 | Bin 0 -> 16138 bytes core/pxelinux.asm | 2768 +++++ core/pxelinux.bin | Bin 0 -> 16138 bytes core/rawcon.inc | 75 + core/regdump.inc | 108 + core/rllpack.inc | 164 + core/runkernel.inc | 683 ++ core/serirq.inc | 191 + core/stack.inc | 47 + core/strcpy.inc | 13 + core/strecpy.inc | 28 + core/syslinux.ld | 150 + core/tracers.inc | 40 + core/ui.inc | 741 ++ core/writedec.inc | 57 + core/writehex.inc | 53 + core/writestr.inc | 48 + doc/SubmittingPatches.txt | 568 ++ doc/comboot.txt | 957 ++ doc/distrib.txt | 29 + doc/extlinux.txt | 132 + doc/gpt.txt | 88 + doc/isolinux.txt | 133 + doc/keytab-lilo.txt | 85 + doc/mboot.txt | 26 + doc/memdisk.txt | 236 + doc/menu.txt | 534 + doc/pxelinux.txt | 418 + doc/rfc5071.txt | 787 ++ doc/sdi.txt | 149 + doc/syslinux.txt | 790 ++ doc/usbkey.txt | 47 + dos/Makefile | 66 + dos/__divdi3.c | 29 + dos/__udivmoddi4.c | 31 + dos/argv.c | 92 + dos/atou.c | 9 + dos/code16.h | 6 + dos/com16.ld | 127 + dos/conio.c | 42 + dos/copybs.asm | 271 + dos/copybs.com | Bin 0 -> 408 bytes dos/crt0.S | 53 + dos/errno.h | 7 + dos/free.c | 78 + dos/inttypes.h | 1 + dos/malloc.c | 114 + dos/malloc.h | 54 + dos/memcpy.S | 23 + dos/memset.S | 21 + dos/mystuff.h | 15 + dos/perror.c | 7 + dos/printf.c | 297 + dos/skipatou.c | 10 + dos/stdint.h | 142 + dos/stdio.h | 21 + dos/stdlib.h | 12 + dos/string.h | 23 + dos/syslinux.c | 680 ++ dos/syslinux.com | Bin 0 -> 23172 bytes dummy.c | 8 + extlinux/Makefile | 59 + extlinux/ext2_fs.h | 508 + extlinux/extlinux | Bin 0 -> 52714 bytes extlinux/main.c | 1026 ++ gen-id.sh | 32 + gpxe/.gitignore | 1 + gpxe/COPYING | 339 + gpxe/COPYRIGHTS | 26 + gpxe/LOG | 12 + gpxe/Makefile | 41 + gpxe/README | 53 + gpxe/VERSION | 1 + gpxe/gpxelinux.0 | Bin 0 -> 76161 bytes gpxe/pxelinux.gpxe | 4 + gpxe/src/.gitignore | 5 + gpxe/src/Makefile | 138 + gpxe/src/Makefile.housekeeping | 858 ++ gpxe/src/README.pixify | 90 + gpxe/src/arch/i386/Makefile | 114 + gpxe/src/arch/i386/Makefile.efi | 10 + gpxe/src/arch/i386/Makefile.pcbios | 55 + gpxe/src/arch/i386/README.i386 | 197 + gpxe/src/arch/i386/core/aout_loader.c | 144 + gpxe/src/arch/i386/core/basemem_packet.c | 30 + gpxe/src/arch/i386/core/cpu.c | 73 + gpxe/src/arch/i386/core/dumpregs.c | 23 + gpxe/src/arch/i386/core/etherboot.prefix.lds | 100 + gpxe/src/arch/i386/core/freebsd_loader.c | 377 + gpxe/src/arch/i386/core/gdbidt.S | 215 + gpxe/src/arch/i386/core/gdbmach.c | 149 + gpxe/src/arch/i386/core/nulltrap.c | 51 + gpxe/src/arch/i386/core/pic8259.c | 64 + gpxe/src/arch/i386/core/prefixudata.lds | 8 + gpxe/src/arch/i386/core/prefixzdata.lds | 8 + gpxe/src/arch/i386/core/rdtsc_timer.c | 87 + gpxe/src/arch/i386/core/realmode.c | 23 + gpxe/src/arch/i386/core/relocate.c | 168 + gpxe/src/arch/i386/core/setjmp.S | 40 + gpxe/src/arch/i386/core/stack.S | 13 + gpxe/src/arch/i386/core/stack16.S | 13 + gpxe/src/arch/i386/core/start16.lds | 8 + gpxe/src/arch/i386/core/start16z.lds | 65 + gpxe/src/arch/i386/core/timer2.c | 85 + gpxe/src/arch/i386/core/video_subr.c | 104 + gpxe/src/arch/i386/core/virtaddr.S | 101 + gpxe/src/arch/i386/core/wince_loader.c | 273 + gpxe/src/arch/i386/core/x86_io.c | 94 + gpxe/src/arch/i386/drivers/net/undi.c | 146 + gpxe/src/arch/i386/drivers/net/undiisr.S | 85 + gpxe/src/arch/i386/drivers/net/undiload.c | 170 + gpxe/src/arch/i386/drivers/net/undinet.c | 796 ++ gpxe/src/arch/i386/drivers/net/undionly.c | 127 + gpxe/src/arch/i386/drivers/net/undipreload.c | 35 + gpxe/src/arch/i386/drivers/net/undirom.c | 232 + gpxe/src/arch/i386/firmware/pcbios/basemem.c | 44 + .../arch/i386/firmware/pcbios/bios_console.c | 296 + .../arch/i386/firmware/pcbios/e820mangler.S | 593 ++ gpxe/src/arch/i386/firmware/pcbios/fakee820.c | 91 + gpxe/src/arch/i386/firmware/pcbios/gateA20.c | 174 + gpxe/src/arch/i386/firmware/pcbios/hidemem.c | 218 + gpxe/src/arch/i386/firmware/pcbios/memmap.c | 310 + gpxe/src/arch/i386/firmware/pcbios/pnpbios.c | 107 + gpxe/src/arch/i386/image/bootsector.c | 112 + gpxe/src/arch/i386/image/bzimage.c | 576 ++ gpxe/src/arch/i386/image/com32.c | 283 + gpxe/src/arch/i386/image/comboot.c | 320 + gpxe/src/arch/i386/image/elfboot.c | 111 + gpxe/src/arch/i386/image/eltorito.c | 334 + gpxe/src/arch/i386/image/multiboot.c | 452 + gpxe/src/arch/i386/image/nbi.c | 442 + gpxe/src/arch/i386/image/pxe_image.c | 119 + gpxe/src/arch/i386/include/basemem.h | 33 + gpxe/src/arch/i386/include/basemem_packet.h | 13 + gpxe/src/arch/i386/include/bios.h | 8 + gpxe/src/arch/i386/include/bios_disks.h | 69 + gpxe/src/arch/i386/include/biosint.h | 31 + gpxe/src/arch/i386/include/bits/byteswap.h | 41 + gpxe/src/arch/i386/include/bits/compiler.h | 25 + gpxe/src/arch/i386/include/bits/cpu.h | 86 + gpxe/src/arch/i386/include/bits/eltorito.h | 3 + gpxe/src/arch/i386/include/bits/endian.h | 6 + gpxe/src/arch/i386/include/bits/errfile.h | 39 + gpxe/src/arch/i386/include/bits/io.h | 12 + gpxe/src/arch/i386/include/bits/nap.h | 13 + gpxe/src/arch/i386/include/bits/smbios.h | 12 + gpxe/src/arch/i386/include/bits/stdint.h | 21 + gpxe/src/arch/i386/include/bits/timer.h | 13 + gpxe/src/arch/i386/include/bits/uaccess.h | 12 + gpxe/src/arch/i386/include/bits/umalloc.h | 12 + gpxe/src/arch/i386/include/bochs.h | 34 + gpxe/src/arch/i386/include/bootsector.h | 12 + gpxe/src/arch/i386/include/bzimage.h | 137 + gpxe/src/arch/i386/include/callbacks_arch.h | 243 + gpxe/src/arch/i386/include/comboot.h | 133 + gpxe/src/arch/i386/include/fakee820.h | 7 + gpxe/src/arch/i386/include/gateA20.h | 7 + gpxe/src/arch/i386/include/gdbmach.h | 64 + gpxe/src/arch/i386/include/gpxe/abft.h | 35 + gpxe/src/arch/i386/include/gpxe/bios_nap.h | 16 + gpxe/src/arch/i386/include/gpxe/bios_smbios.h | 16 + gpxe/src/arch/i386/include/gpxe/bios_timer.h | 42 + gpxe/src/arch/i386/include/gpxe/ibft.h | 300 + .../arch/i386/include/gpxe/memtop_umalloc.h | 16 + gpxe/src/arch/i386/include/gpxe/rdtsc_timer.h | 37 + gpxe/src/arch/i386/include/gpxe/timer2.h | 12 + gpxe/src/arch/i386/include/gpxe/x86_io.h | 151 + gpxe/src/arch/i386/include/int13.h | 290 + gpxe/src/arch/i386/include/kir.h | 18 + gpxe/src/arch/i386/include/libkir.h | 233 + gpxe/src/arch/i386/include/librm.h | 204 + gpxe/src/arch/i386/include/limits.h | 59 + gpxe/src/arch/i386/include/memsizes.h | 17 + gpxe/src/arch/i386/include/multiboot.h | 147 + gpxe/src/arch/i386/include/pic8259.h | 69 + gpxe/src/arch/i386/include/pnpbios.h | 15 + gpxe/src/arch/i386/include/pxe.h | 151 + gpxe/src/arch/i386/include/pxe_api.h | 1841 ++++ gpxe/src/arch/i386/include/pxe_call.h | 35 + gpxe/src/arch/i386/include/pxe_types.h | 125 + gpxe/src/arch/i386/include/realmode.h | 125 + gpxe/src/arch/i386/include/registers.h | 196 + gpxe/src/arch/i386/include/setjmp.h | 38 + gpxe/src/arch/i386/include/undi.h | 104 + gpxe/src/arch/i386/include/undiload.h | 33 + gpxe/src/arch/i386/include/undinet.h | 15 + gpxe/src/arch/i386/include/undipreload.h | 16 + gpxe/src/arch/i386/include/undirom.h | 51 + gpxe/src/arch/i386/include/vga.h | 228 + gpxe/src/arch/i386/interface/pcbios/abft.c | 60 + gpxe/src/arch/i386/interface/pcbios/aoeboot.c | 62 + .../src/arch/i386/interface/pcbios/bios_nap.c | 14 + .../arch/i386/interface/pcbios/bios_smbios.c | 84 + .../arch/i386/interface/pcbios/bios_timer.c | 63 + gpxe/src/arch/i386/interface/pcbios/biosint.c | 90 + gpxe/src/arch/i386/interface/pcbios/ibft.c | 444 + gpxe/src/arch/i386/interface/pcbios/int13.c | 660 ++ .../arch/i386/interface/pcbios/iscsiboot.c | 91 + .../i386/interface/pcbios/memtop_umalloc.c | 200 + gpxe/src/arch/i386/interface/pcbios/pcibios.c | 113 + gpxe/src/arch/i386/interface/pxe/pxe_call.c | 456 + gpxe/src/arch/i386/interface/pxe/pxe_entry.S | 214 + gpxe/src/arch/i386/interface/pxe/pxe_errors.c | 103 + gpxe/src/arch/i386/interface/pxe/pxe_file.c | 264 + gpxe/src/arch/i386/interface/pxe/pxe_loader.c | 51 + .../src/arch/i386/interface/pxe/pxe_preboot.c | 352 + gpxe/src/arch/i386/interface/pxe/pxe_tftp.c | 581 ++ gpxe/src/arch/i386/interface/pxe/pxe_udp.c | 403 + gpxe/src/arch/i386/interface/pxe/pxe_undi.c | 684 ++ .../arch/i386/interface/syslinux/com32_call.c | 188 + .../i386/interface/syslinux/com32_wrapper.S | 92 + .../i386/interface/syslinux/comboot_call.c | 693 ++ .../i386/interface/syslinux/comboot_resolv.c | 58 + gpxe/src/arch/i386/kir-Makefile | 26 + gpxe/src/arch/i386/prefix/bImageprefix.S | 611 ++ gpxe/src/arch/i386/prefix/bootpart.S | 216 + gpxe/src/arch/i386/prefix/comprefix.S | 46 + gpxe/src/arch/i386/prefix/dskprefix.S | 379 + gpxe/src/arch/i386/prefix/elf_dprefix.S | 94 + gpxe/src/arch/i386/prefix/elfprefix.S | 94 + gpxe/src/arch/i386/prefix/exeprefix.S | 41 + gpxe/src/arch/i386/prefix/hdprefix.S | 107 + gpxe/src/arch/i386/prefix/kkpxeprefix.S | 8 + gpxe/src/arch/i386/prefix/kpxeprefix.S | 7 + gpxe/src/arch/i386/prefix/libprefix.S | 817 ++ gpxe/src/arch/i386/prefix/lkrnprefix.S | 214 + gpxe/src/arch/i386/prefix/lmelf_dprefix.S | 161 + gpxe/src/arch/i386/prefix/lmelf_prefix.S | 161 + gpxe/src/arch/i386/prefix/mbr.S | 13 + gpxe/src/arch/i386/prefix/nbiprefix.S | 77 + gpxe/src/arch/i386/prefix/nullprefix.S | 13 + gpxe/src/arch/i386/prefix/pxeprefix.S | 701 ++ gpxe/src/arch/i386/prefix/romprefix.S | 725 ++ gpxe/src/arch/i386/prefix/unnrv2b.S | 182 + gpxe/src/arch/i386/prefix/usbdisk.S | 23 + gpxe/src/arch/i386/scripts/i386-kir.lds | 196 + gpxe/src/arch/i386/scripts/i386.lds | 209 + gpxe/src/arch/i386/transitions/libkir.S | 254 + gpxe/src/arch/i386/transitions/libpm.S | 0 gpxe/src/arch/i386/transitions/librm.S | 579 ++ gpxe/src/arch/i386/transitions/librm_mgmt.c | 56 + gpxe/src/arch/x86/Makefile | 9 + gpxe/src/arch/x86/Makefile.efi | 28 + gpxe/src/arch/x86/core/pcidirect.c | 45 + gpxe/src/arch/x86/core/x86_string.c | 61 + gpxe/src/arch/x86/include/bits/pci_io.h | 13 + gpxe/src/arch/x86/include/bits/string.h | 250 + .../arch/x86/include/gpxe/efi/efix86_nap.h | 16 + gpxe/src/arch/x86/include/gpxe/pcibios.h | 133 + gpxe/src/arch/x86/include/gpxe/pcidirect.h | 139 + gpxe/src/arch/x86/interface/efi/efix86_nap.c | 46 + gpxe/src/arch/x86/prefix/efidrvprefix.c | 44 + gpxe/src/arch/x86/prefix/efiprefix.c | 39 + gpxe/src/arch/x86/scripts/efi.lds | 105 + gpxe/src/arch/x86_64/Makefile | 41 + gpxe/src/arch/x86_64/Makefile.efi | 14 + gpxe/src/arch/x86_64/include/bits/byteswap.h | 22 + gpxe/src/arch/x86_64/include/bits/compiler.h | 14 + gpxe/src/arch/x86_64/include/bits/endian.h | 6 + gpxe/src/arch/x86_64/include/bits/errfile.h | 11 + gpxe/src/arch/x86_64/include/bits/io.h | 10 + gpxe/src/arch/x86_64/include/bits/nap.h | 12 + gpxe/src/arch/x86_64/include/bits/smbios.h | 10 + gpxe/src/arch/x86_64/include/bits/stdint.h | 21 + gpxe/src/arch/x86_64/include/bits/timer.h | 10 + gpxe/src/arch/x86_64/include/bits/uaccess.h | 10 + gpxe/src/arch/x86_64/include/bits/umalloc.h | 10 + gpxe/src/arch/x86_64/include/gdbmach.h | 51 + gpxe/src/arch/x86_64/include/limits.h | 59 + gpxe/src/config/.gitignore | 1 + gpxe/src/config/console.h | 21 + gpxe/src/config/defaults.h | 8 + gpxe/src/config/defaults/efi.h | 21 + gpxe/src/config/defaults/pcbios.h | 29 + gpxe/src/config/general.h | 123 + gpxe/src/config/ioapi.h | 15 + gpxe/src/config/isa.h | 15 + gpxe/src/config/nap.h | 15 + gpxe/src/config/serial.h | 28 + gpxe/src/config/timer.h | 15 + gpxe/src/config/umalloc.h | 12 + gpxe/src/core/acpi.c | 40 + gpxe/src/core/ansiesc.c | 114 + gpxe/src/core/asprintf.c | 47 + gpxe/src/core/base64.c | 66 + gpxe/src/core/basename.c | 62 + gpxe/src/core/bitmap.c | 99 + gpxe/src/core/bitops.c | 11 + gpxe/src/core/btext.c | 5039 +++++++++ gpxe/src/core/config.c | 224 + gpxe/src/core/console.c | 135 + gpxe/src/core/cpio.c | 40 + gpxe/src/core/cwuri.c | 44 + gpxe/src/core/debug.c | 195 + gpxe/src/core/device.c | 110 + gpxe/src/core/downloader.c | 270 + gpxe/src/core/errno.c | 18 + gpxe/src/core/exec.c | 253 + gpxe/src/core/filter.c | 72 + gpxe/src/core/gdbserial.c | 46 + gpxe/src/core/gdbstub.c | 402 + gpxe/src/core/gdbudp.c | 257 + gpxe/src/core/getkey.c | 81 + gpxe/src/core/getopt.c | 271 + gpxe/src/core/hw.c | 74 + gpxe/src/core/i82365.c | 656 ++ gpxe/src/core/image.c | 332 + gpxe/src/core/init.c | 111 + gpxe/src/core/interface.c | 60 + gpxe/src/core/iobuf.c | 94 + gpxe/src/core/job.c | 87 + gpxe/src/core/linebuf.c | 109 + gpxe/src/core/main.c | 94 + gpxe/src/core/malloc.c | 384 + gpxe/src/core/misc.c | 91 + gpxe/src/core/monojob.c | 101 + gpxe/src/core/null_nap.c | 3 + gpxe/src/core/nvo.c | 261 + gpxe/src/core/open.c | 184 + gpxe/src/core/pc_kbd.c | 112 + gpxe/src/core/pcmcia.c | 267 + gpxe/src/core/posix_io.c | 353 + gpxe/src/core/process.c | 104 + gpxe/src/core/proto_eth_slow.c | 406 + gpxe/src/core/random.c | 39 + gpxe/src/core/refcnt.c | 76 + gpxe/src/core/resolv.c | 400 + gpxe/src/core/serial.c | 252 + gpxe/src/core/serial_console.c | 31 + gpxe/src/core/settings.c | 1210 +++ gpxe/src/core/string.c | 353 + gpxe/src/core/stringextra.c | 273 + gpxe/src/core/timer.c | 40 + gpxe/src/core/uri.c | 465 + gpxe/src/core/uuid.c | 48 + gpxe/src/core/vsprintf.c | 421 + gpxe/src/core/xfer.c | 412 + gpxe/src/crypto/asn1.c | 164 + gpxe/src/crypto/axtls/aes.c | 476 + gpxe/src/crypto/axtls/bigint.c | 1496 +++ gpxe/src/crypto/axtls/bigint.h | 91 + gpxe/src/crypto/axtls/bigint_impl.h | 105 + gpxe/src/crypto/axtls/crypto.h | 300 + gpxe/src/crypto/axtls/os_port.h | 61 + gpxe/src/crypto/axtls/rsa.c | 332 + gpxe/src/crypto/axtls/sha1.c | 240 + gpxe/src/crypto/axtls_aes.c | 167 + gpxe/src/crypto/axtls_sha1.c | 25 + gpxe/src/crypto/cbc.c | 99 + gpxe/src/crypto/chap.c | 122 + gpxe/src/crypto/crypto_null.c | 85 + gpxe/src/crypto/hmac.c | 120 + gpxe/src/crypto/md5.c | 234 + gpxe/src/crypto/x509.c | 181 + gpxe/src/doc/build_sys.dox | 419 + gpxe/src/doc/pxe_extensions | 279 + gpxe/src/doxygen.cfg | 1245 +++ gpxe/src/drivers/bitbash/bitbash.c | 55 + gpxe/src/drivers/bitbash/i2c_bit.c | 391 + gpxe/src/drivers/bitbash/spi_bit.c | 196 + gpxe/src/drivers/block/ata.c | 163 + gpxe/src/drivers/block/ramdisk.c | 95 + gpxe/src/drivers/block/scsi.c | 293 + gpxe/src/drivers/bus/eisa.c | 185 + gpxe/src/drivers/bus/isa.c | 175 + gpxe/src/drivers/bus/isa_ids.c | 26 + gpxe/src/drivers/bus/isapnp.c | 758 ++ gpxe/src/drivers/bus/mca.c | 180 + gpxe/src/drivers/bus/pci.c | 344 + gpxe/src/drivers/bus/pciextra.c | 79 + gpxe/src/drivers/bus/virtio-pci.c | 64 + gpxe/src/drivers/bus/virtio-ring.c | 134 + gpxe/src/drivers/infiniband/MT25218_PRM.h | 3460 +++++++ gpxe/src/drivers/infiniband/MT25408_PRM.h | 3313 ++++++ gpxe/src/drivers/infiniband/arbel.c | 2253 ++++ gpxe/src/drivers/infiniband/arbel.h | 542 + gpxe/src/drivers/infiniband/hermon.c | 2324 +++++ gpxe/src/drivers/infiniband/hermon.h | 533 + gpxe/src/drivers/infiniband/ib_packet.c | 234 + gpxe/src/drivers/infiniband/ib_sma.c | 553 + gpxe/src/drivers/infiniband/ib_smc.c | 166 + gpxe/src/drivers/infiniband/linda.c | 2397 +++++ gpxe/src/drivers/infiniband/linda.h | 271 + gpxe/src/drivers/infiniband/linda_fw.c | 1067 ++ gpxe/src/drivers/infiniband/mlx_bitops.h | 209 + gpxe/src/drivers/infiniband/qib_7220_regs.h | 1763 ++++ gpxe/src/drivers/infiniband/qib_genbits.pl | 95 + gpxe/src/drivers/net/3c503.c | 5 + gpxe/src/drivers/net/3c509-eisa.c | 49 + gpxe/src/drivers/net/3c509.c | 430 + gpxe/src/drivers/net/3c509.h | 392 + gpxe/src/drivers/net/3c515.c | 762 ++ gpxe/src/drivers/net/3c515.txt | 31 + gpxe/src/drivers/net/3c529.c | 60 + gpxe/src/drivers/net/3c595.c | 551 + gpxe/src/drivers/net/3c595.h | 435 + gpxe/src/drivers/net/3c5x9.c | 414 + gpxe/src/drivers/net/3c90x.c | 1027 ++ gpxe/src/drivers/net/3c90x.txt | 307 + gpxe/src/drivers/net/amd8111e.c | 691 ++ gpxe/src/drivers/net/amd8111e.h | 629 ++ gpxe/src/drivers/net/b44.c | 949 ++ gpxe/src/drivers/net/b44.h | 467 + gpxe/src/drivers/net/bnx2.c | 2695 +++++ gpxe/src/drivers/net/bnx2.h | 4704 +++++++++ gpxe/src/drivers/net/bnx2_fw.h | 3494 +++++++ gpxe/src/drivers/net/cs89x0.c | 737 ++ gpxe/src/drivers/net/cs89x0.h | 479 + gpxe/src/drivers/net/cs89x0.txt | 45 + gpxe/src/drivers/net/davicom.c | 725 ++ gpxe/src/drivers/net/depca.c | 803 ++ gpxe/src/drivers/net/dmfe.c | 1224 +++ gpxe/src/drivers/net/e1000/e1000.c | 1119 ++ gpxe/src/drivers/net/e1000/e1000.h | 304 + gpxe/src/drivers/net/e1000/e1000_hw.c | 9050 +++++++++++++++++ gpxe/src/drivers/net/e1000/e1000_hw.h | 3413 +++++++ gpxe/src/drivers/net/e1000/e1000_osdep.h | 142 + gpxe/src/drivers/net/eepro.c | 635 ++ gpxe/src/drivers/net/eepro100.c | 851 ++ gpxe/src/drivers/net/epic100.c | 537 + gpxe/src/drivers/net/epic100.h | 188 + gpxe/src/drivers/net/etherfabric.c | 4248 ++++++++ gpxe/src/drivers/net/etherfabric.h | 551 + gpxe/src/drivers/net/etherfabric_nic.h | 201 + gpxe/src/drivers/net/forcedeth.c | 1440 +++ gpxe/src/drivers/net/hfa384x.h | 3067 ++++++ gpxe/src/drivers/net/ipoib.c | 1128 ++ gpxe/src/drivers/net/legacy.c | 155 + gpxe/src/drivers/net/mtd80x.c | 1084 ++ gpxe/src/drivers/net/mtnic.c | 1850 ++++ gpxe/src/drivers/net/mtnic.h | 719 ++ gpxe/src/drivers/net/natsemi.c | 607 ++ gpxe/src/drivers/net/natsemi.h | 230 + gpxe/src/drivers/net/ne.c | 6 + gpxe/src/drivers/net/ne2k_isa.c | 373 + gpxe/src/drivers/net/ns83820.c | 1012 ++ gpxe/src/drivers/net/ns8390.c | 1035 ++ gpxe/src/drivers/net/ns8390.h | 238 + gpxe/src/drivers/net/p80211hdr.h | 299 + gpxe/src/drivers/net/pcnet32.c | 1025 ++ gpxe/src/drivers/net/phantom/nx_bitops.h | 192 + .../drivers/net/phantom/nxhal_nic_interface.h | 499 + gpxe/src/drivers/net/phantom/phantom.c | 2112 ++++ gpxe/src/drivers/net/phantom/phantom.h | 192 + gpxe/src/drivers/net/phantom/phantom_hw.h | 182 + gpxe/src/drivers/net/pnic.c | 279 + gpxe/src/drivers/net/pnic_api.h | 59 + gpxe/src/drivers/net/prism2.c | 855 ++ gpxe/src/drivers/net/prism2_pci.c | 56 + gpxe/src/drivers/net/prism2_plx.c | 120 + gpxe/src/drivers/net/r8169.c | 2282 +++++ gpxe/src/drivers/net/r8169.h | 566 ++ gpxe/src/drivers/net/rtl8139.c | 587 ++ gpxe/src/drivers/net/sis900.c | 1296 +++ gpxe/src/drivers/net/sis900.h | 373 + gpxe/src/drivers/net/smc9000.c | 952 ++ gpxe/src/drivers/net/smc9000.h | 433 + gpxe/src/drivers/net/sundance.c | 902 ++ gpxe/src/drivers/net/tg3.c | 3432 +++++++ gpxe/src/drivers/net/tg3.h | 2226 ++++ gpxe/src/drivers/net/tlan.c | 1720 ++++ gpxe/src/drivers/net/tlan.h | 524 + gpxe/src/drivers/net/tulip.c | 2097 ++++ gpxe/src/drivers/net/tulip.txt | 54 + gpxe/src/drivers/net/via-rhine.c | 1447 +++ gpxe/src/drivers/net/via-velocity.c | 1939 ++++ gpxe/src/drivers/net/via-velocity.h | 1930 ++++ gpxe/src/drivers/net/virtio-net.c | 307 + gpxe/src/drivers/net/virtio-net.h | 44 + gpxe/src/drivers/net/w89c840.c | 962 ++ gpxe/src/drivers/net/wd.c | 6 + gpxe/src/drivers/net/wlan_compat.h | 575 ++ gpxe/src/drivers/nvs/nvs.c | 147 + gpxe/src/drivers/nvs/spi.c | 138 + gpxe/src/drivers/nvs/threewire.c | 88 + gpxe/src/hci/commands/autoboot_cmd.c | 25 + gpxe/src/hci/commands/config_cmd.c | 37 + gpxe/src/hci/commands/dhcp_cmd.c | 188 + gpxe/src/hci/commands/gdbstub_cmd.c | 105 + gpxe/src/hci/commands/ifmgmt_cmd.c | 177 + gpxe/src/hci/commands/image_cmd.c | 593 ++ gpxe/src/hci/commands/login_cmd.c | 27 + gpxe/src/hci/commands/nvo_cmd.c | 77 + gpxe/src/hci/commands/route_cmd.c | 85 + gpxe/src/hci/commands/sanboot_cmd.c | 68 + gpxe/src/hci/editstring.c | 190 + gpxe/src/hci/mucurses/alert.c | 18 + gpxe/src/hci/mucurses/ansi_screen.c | 72 + gpxe/src/hci/mucurses/clear.c | 88 + gpxe/src/hci/mucurses/colour.c | 64 + gpxe/src/hci/mucurses/cursor.h | 35 + gpxe/src/hci/mucurses/edging.c | 111 + gpxe/src/hci/mucurses/kb.c | 143 + gpxe/src/hci/mucurses/mucurses.c | 145 + gpxe/src/hci/mucurses/mucurses.h | 21 + gpxe/src/hci/mucurses/print.c | 84 + gpxe/src/hci/mucurses/print_nadv.c | 26 + gpxe/src/hci/mucurses/slk.c | 363 + gpxe/src/hci/mucurses/widgets/editbox.c | 101 + gpxe/src/hci/mucurses/winattrs.c | 131 + gpxe/src/hci/mucurses/windows.c | 158 + gpxe/src/hci/mucurses/wininit.c | 35 + gpxe/src/hci/readline.c | 117 + gpxe/src/hci/shell.c | 105 + gpxe/src/hci/shell_banner.c | 59 + gpxe/src/hci/strerror.c | 125 + gpxe/src/hci/tui/login_ui.c | 135 + gpxe/src/hci/tui/settings_ui.c | 427 + gpxe/src/image/efi_image.c | 106 + gpxe/src/image/elf.c | 133 + gpxe/src/image/embedded.c | 99 + gpxe/src/image/script.c | 122 + gpxe/src/image/segment.c | 74 + gpxe/src/include/.gitignore | 1 + gpxe/src/include/alloca.h | 25 + gpxe/src/include/assert.h | 65 + gpxe/src/include/big_bswap.h | 33 + gpxe/src/include/bootp.h | 230 + gpxe/src/include/btext.h | 62 + gpxe/src/include/byteswap.h | 57 + gpxe/src/include/cmdline.h | 8 + gpxe/src/include/cmdlinelib.h | 99 + gpxe/src/include/cmdlist.h | 18 + gpxe/src/include/coff.h | 73 + gpxe/src/include/compiler.h | 385 + gpxe/src/include/console.h | 114 + gpxe/src/include/cpu.h | 6 + gpxe/src/include/ctype.h | 27 + gpxe/src/include/curses.h | 753 ++ gpxe/src/include/debug.h | 28 + gpxe/src/include/dhcp.h | 12 + gpxe/src/include/elf.h | 232 + gpxe/src/include/endian.h | 19 + gpxe/src/include/errno.h | 508 + gpxe/src/include/etherboot.h | 42 + gpxe/src/include/fs.h | 41 + gpxe/src/include/getopt.h | 92 + gpxe/src/include/gpxe/acpi.h | 41 + gpxe/src/include/gpxe/aes.h | 8 + gpxe/src/include/gpxe/ansiesc.h | 118 + gpxe/src/include/gpxe/aoe.h | 148 + gpxe/src/include/gpxe/api.h | 82 + gpxe/src/include/gpxe/arp.h | 41 + gpxe/src/include/gpxe/asn1.h | 32 + gpxe/src/include/gpxe/ata.h | 205 + gpxe/src/include/gpxe/base64.h | 24 + gpxe/src/include/gpxe/bitbash.h | 50 + gpxe/src/include/gpxe/bitmap.h | 83 + gpxe/src/include/gpxe/bitops.h | 228 + gpxe/src/include/gpxe/blockdev.h | 51 + gpxe/src/include/gpxe/cbc.h | 98 + gpxe/src/include/gpxe/chap.h | 51 + gpxe/src/include/gpxe/command.h | 22 + gpxe/src/include/gpxe/cpio.h | 51 + gpxe/src/include/gpxe/crypto.h | 152 + gpxe/src/include/gpxe/device.h | 108 + gpxe/src/include/gpxe/dhcp.h | 614 ++ gpxe/src/include/gpxe/dhcpopts.h | 32 + gpxe/src/include/gpxe/dhcppkt.h | 62 + gpxe/src/include/gpxe/dns.h | 90 + gpxe/src/include/gpxe/downloader.h | 17 + gpxe/src/include/gpxe/editbox.h | 59 + gpxe/src/include/gpxe/editstring.h | 31 + gpxe/src/include/gpxe/efi/Base.h | 329 + gpxe/src/include/gpxe/efi/Guid/PcAnsi.h | 58 + gpxe/src/include/gpxe/efi/Guid/SmBios.h | 34 + .../src/include/gpxe/efi/Ia32/ProcessorBind.h | 215 + .../include/gpxe/efi/IndustryStandard/Pci22.h | 601 ++ .../gpxe/efi/IndustryStandard/PeImage.h | 752 ++ gpxe/src/include/gpxe/efi/LICENCE | 40 + gpxe/src/include/gpxe/efi/Pi/PiBootMode.h | 43 + gpxe/src/include/gpxe/efi/Pi/PiDependency.h | 47 + gpxe/src/include/gpxe/efi/Pi/PiDxeCis.h | 642 ++ gpxe/src/include/gpxe/efi/Pi/PiFirmwareFile.h | 242 + .../include/gpxe/efi/Pi/PiFirmwareVolume.h | 154 + gpxe/src/include/gpxe/efi/Pi/PiHob.h | 295 + gpxe/src/include/gpxe/efi/Pi/PiMultiPhase.h | 104 + gpxe/src/include/gpxe/efi/PiDxe.h | 25 + gpxe/src/include/gpxe/efi/ProcessorBind.h | 14 + .../gpxe/efi/Protocol/ComponentName2.h | 174 + gpxe/src/include/gpxe/efi/Protocol/Cpu.h | 291 + gpxe/src/include/gpxe/efi/Protocol/CpuIo.h | 128 + .../include/gpxe/efi/Protocol/DebugSupport.h | 634 ++ .../include/gpxe/efi/Protocol/DevicePath.h | 535 + .../include/gpxe/efi/Protocol/DriverBinding.h | 153 + .../efi/Protocol/NetworkInterfaceIdentifier.h | 74 + gpxe/src/include/gpxe/efi/Protocol/PciIo.h | 519 + .../gpxe/efi/Protocol/PciRootBridgeIo.h | 393 + .../include/gpxe/efi/Protocol/SimpleNetwork.h | 593 ++ .../include/gpxe/efi/Protocol/SimpleTextIn.h | 136 + .../include/gpxe/efi/Protocol/SimpleTextOut.h | 404 + gpxe/src/include/gpxe/efi/Uefi.h | 27 + gpxe/src/include/gpxe/efi/Uefi/UefiBaseType.h | 200 + gpxe/src/include/gpxe/efi/Uefi/UefiGpt.h | 62 + .../efi/Uefi/UefiInternalFormRepresentation.h | 1590 +++ .../include/gpxe/efi/Uefi/UefiMultiPhase.h | 210 + gpxe/src/include/gpxe/efi/Uefi/UefiPxe.h | 1756 ++++ gpxe/src/include/gpxe/efi/Uefi/UefiSpec.h | 1916 ++++ gpxe/src/include/gpxe/efi/X64/ProcessorBind.h | 247 + gpxe/src/include/gpxe/efi/efi.h | 131 + gpxe/src/include/gpxe/efi/efi_io.h | 178 + gpxe/src/include/gpxe/efi/efi_pci.h | 146 + gpxe/src/include/gpxe/efi/efi_smbios.h | 16 + gpxe/src/include/gpxe/efi/efi_timer.h | 16 + gpxe/src/include/gpxe/efi/efi_uaccess.h | 88 + gpxe/src/include/gpxe/efi/efi_umalloc.h | 16 + gpxe/src/include/gpxe/efi/import.pl | 75 + gpxe/src/include/gpxe/eisa.h | 125 + gpxe/src/include/gpxe/elf.h | 15 + gpxe/src/include/gpxe/errfile.h | 178 + gpxe/src/include/gpxe/errortab.h | 19 + gpxe/src/include/gpxe/ethernet.h | 35 + gpxe/src/include/gpxe/fakedhcp.h | 21 + gpxe/src/include/gpxe/features.h | 95 + gpxe/src/include/gpxe/filter.h | 73 + gpxe/src/include/gpxe/ftp.h | 13 + gpxe/src/include/gpxe/gdbserial.h | 19 + gpxe/src/include/gpxe/gdbstub.h | 73 + gpxe/src/include/gpxe/gdbudp.h | 22 + gpxe/src/include/gpxe/hidemem.h | 15 + gpxe/src/include/gpxe/hmac.h | 30 + gpxe/src/include/gpxe/http.h | 21 + gpxe/src/include/gpxe/i2c.h | 169 + gpxe/src/include/gpxe/ib_mad.h | 364 + gpxe/src/include/gpxe/ib_packet.h | 147 + gpxe/src/include/gpxe/ib_sma.h | 63 + gpxe/src/include/gpxe/ib_smc.h | 18 + gpxe/src/include/gpxe/icmp.h | 23 + gpxe/src/include/gpxe/icmp6.h | 57 + gpxe/src/include/gpxe/if_arp.h | 100 + gpxe/src/include/gpxe/if_ether.h | 33 + gpxe/src/include/gpxe/image.h | 190 + gpxe/src/include/gpxe/in.h | 103 + gpxe/src/include/gpxe/infiniband.h | 561 + gpxe/src/include/gpxe/init.h | 74 + gpxe/src/include/gpxe/interface.h | 56 + gpxe/src/include/gpxe/io.h | 504 + gpxe/src/include/gpxe/iobuf.h | 227 + gpxe/src/include/gpxe/ip.h | 95 + gpxe/src/include/gpxe/ip6.h | 78 + gpxe/src/include/gpxe/ipoib.h | 80 + gpxe/src/include/gpxe/isa.h | 92 + gpxe/src/include/gpxe/isa_ids.h | 49 + gpxe/src/include/gpxe/isapnp.h | 273 + gpxe/src/include/gpxe/iscsi.h | 681 ++ gpxe/src/include/gpxe/job.h | 165 + gpxe/src/include/gpxe/keys.h | 79 + gpxe/src/include/gpxe/linebuf.h | 28 + gpxe/src/include/gpxe/linux_compat.h | 25 + gpxe/src/include/gpxe/list.h | 178 + gpxe/src/include/gpxe/login_ui.h | 12 + gpxe/src/include/gpxe/malloc.h | 57 + gpxe/src/include/gpxe/mca.h | 103 + gpxe/src/include/gpxe/md5.h | 22 + gpxe/src/include/gpxe/memmap.h | 34 + gpxe/src/include/gpxe/monojob.h | 15 + gpxe/src/include/gpxe/nap.h | 54 + gpxe/src/include/gpxe/ndp.h | 21 + gpxe/src/include/gpxe/netdevice.h | 461 + gpxe/src/include/gpxe/null_nap.h | 21 + gpxe/src/include/gpxe/nvo.h | 53 + gpxe/src/include/gpxe/nvs.h | 66 + gpxe/src/include/gpxe/open.h | 95 + gpxe/src/include/gpxe/pci.h | 360 + gpxe/src/include/gpxe/pci_ids.h | 348 + gpxe/src/include/gpxe/pci_io.h | 122 + gpxe/src/include/gpxe/posix_io.h | 85 + gpxe/src/include/gpxe/process.h | 75 + gpxe/src/include/gpxe/profile.h | 78 + gpxe/src/include/gpxe/ramdisk.h | 22 + gpxe/src/include/gpxe/rarp.h | 14 + gpxe/src/include/gpxe/refcnt.h | 44 + gpxe/src/include/gpxe/resolv.h | 166 + gpxe/src/include/gpxe/retry.h | 79 + gpxe/src/include/gpxe/rotate.h | 27 + gpxe/src/include/gpxe/rsa.h | 10 + gpxe/src/include/gpxe/sanboot.h | 14 + gpxe/src/include/gpxe/scsi.h | 270 + gpxe/src/include/gpxe/segment.h | 15 + gpxe/src/include/gpxe/serial.h | 14 + gpxe/src/include/gpxe/settings.h | 319 + gpxe/src/include/gpxe/settings_ui.h | 14 + gpxe/src/include/gpxe/sha1.h | 13 + gpxe/src/include/gpxe/shell.h | 12 + gpxe/src/include/gpxe/shell_banner.h | 12 + gpxe/src/include/gpxe/smbios.h | 140 + gpxe/src/include/gpxe/socket.h | 99 + gpxe/src/include/gpxe/spi.h | 248 + gpxe/src/include/gpxe/spi_bit.h | 61 + gpxe/src/include/gpxe/tables.h | 229 + gpxe/src/include/gpxe/tcp.h | 306 + gpxe/src/include/gpxe/tcpip.h | 121 + gpxe/src/include/gpxe/tftp.h | 83 + gpxe/src/include/gpxe/threewire.h | 89 + gpxe/src/include/gpxe/timer.h | 74 + gpxe/src/include/gpxe/tls.h | 185 + gpxe/src/include/gpxe/uaccess.h | 342 + gpxe/src/include/gpxe/udp.h | 46 + gpxe/src/include/gpxe/umalloc.h | 66 + gpxe/src/include/gpxe/uri.h | 142 + gpxe/src/include/gpxe/uuid.h | 31 + gpxe/src/include/gpxe/virtio-pci.h | 97 + gpxe/src/include/gpxe/virtio-ring.h | 142 + gpxe/src/include/gpxe/vsprintf.h | 72 + gpxe/src/include/gpxe/x509.h | 39 + gpxe/src/include/gpxe/xfer.h | 275 + gpxe/src/include/i82365.h | 450 + gpxe/src/include/igmp.h | 42 + gpxe/src/include/lib.h | 42 + gpxe/src/include/libgen.h | 7 + gpxe/src/include/little_bswap.h | 33 + gpxe/src/include/mii.h | 105 + gpxe/src/include/nfs.h | 63 + gpxe/src/include/nic.h | 273 + gpxe/src/include/nmb.h | 22 + gpxe/src/include/old_tcp.h | 37 + gpxe/src/include/pc_kbd.h | 7 + gpxe/src/include/pcmcia-opts.h | 23 + gpxe/src/include/pcmcia.h | 156 + gpxe/src/include/readline/readline.h | 12 + gpxe/src/include/stdarg.h | 10 + gpxe/src/include/stddef.h | 24 + gpxe/src/include/stdint.h | 24 + gpxe/src/include/stdio.h | 45 + gpxe/src/include/stdlib.h | 73 + gpxe/src/include/string.h | 49 + gpxe/src/include/strings.h | 63 + gpxe/src/include/sys/time.h | 20 + gpxe/src/include/sys_info.h | 33 + gpxe/src/include/time.h | 22 + gpxe/src/include/unistd.h | 41 + gpxe/src/include/usr/autoboot.h | 21 + gpxe/src/include/usr/dhcpmgmt.h | 15 + gpxe/src/include/usr/ifmgmt.h | 17 + gpxe/src/include/usr/imgmgmt.h | 20 + gpxe/src/include/usr/route.h | 12 + gpxe/src/interface/efi/efi_console.c | 274 + gpxe/src/interface/efi/efi_init.c | 122 + gpxe/src/interface/efi/efi_io.c | 203 + gpxe/src/interface/efi/efi_pci.c | 81 + gpxe/src/interface/efi/efi_smbios.c | 62 + gpxe/src/interface/efi/efi_snp.c | 1145 +++ gpxe/src/interface/efi/efi_strerror.c | 43 + gpxe/src/interface/efi/efi_timer.c | 116 + gpxe/src/interface/efi/efi_uaccess.c | 37 + gpxe/src/interface/efi/efi_umalloc.c | 96 + gpxe/src/interface/smbios/smbios.c | 178 + gpxe/src/interface/smbios/smbios_settings.c | 201 + gpxe/src/libgcc/__divdi3.c | 26 + gpxe/src/libgcc/__moddi3.c | 26 + gpxe/src/libgcc/__udivdi3.c | 10 + gpxe/src/libgcc/__udivmoddi4.c | 32 + gpxe/src/libgcc/__umoddi3.c | 13 + gpxe/src/libgcc/libgcc.h | 14 + gpxe/src/libgcc/memcpy.c | 18 + gpxe/src/net/aoe.c | 476 + gpxe/src/net/arp.c | 294 + gpxe/src/net/dhcpopts.c | 434 + gpxe/src/net/dhcppkt.c | 277 + gpxe/src/net/ethernet.c | 147 + gpxe/src/net/fakedhcp.c | 215 + gpxe/src/net/icmp.c | 101 + gpxe/src/net/icmpv6.c | 128 + gpxe/src/net/infiniband.c | 600 ++ gpxe/src/net/iobpad.c | 66 + gpxe/src/net/ipv4.c | 632 ++ gpxe/src/net/ipv6.c | 381 + gpxe/src/net/ndp.c | 180 + gpxe/src/net/netdev_settings.c | 90 + gpxe/src/net/netdevice.c | 603 ++ gpxe/src/net/nullnet.c | 58 + gpxe/src/net/rarp.c | 68 + gpxe/src/net/retry.c | 189 + gpxe/src/net/tcp.c | 1107 ++ gpxe/src/net/tcp/ftp.c | 507 + gpxe/src/net/tcp/http.c | 573 ++ gpxe/src/net/tcp/https.c | 49 + gpxe/src/net/tcp/iscsi.c | 1981 ++++ gpxe/src/net/tcpip.c | 146 + gpxe/src/net/tls.c | 1757 ++++ gpxe/src/net/udp.c | 461 + gpxe/src/net/udp/dhcp.c | 1372 +++ gpxe/src/net/udp/dns.c | 601 ++ gpxe/src/net/udp/slam.c | 810 ++ gpxe/src/net/udp/tftp.c | 1187 +++ gpxe/src/proto/fsp.c | 243 + gpxe/src/proto/igmp.c | 167 + gpxe/src/proto/nfs.c | 616 ++ gpxe/src/proto/nmb.c | 110 + gpxe/src/tests/comboot/shuffle-simple.asm | 40 + gpxe/src/tests/comboot/version.asm | 136 + gpxe/src/tests/gdbstub_test.S | 54 + gpxe/src/tests/gdbstub_test.gdb | 116 + gpxe/src/tests/linebuf_test.c | 35 + gpxe/src/tests/memcpy_test.c | 39 + gpxe/src/tests/umalloc_test.c | 26 + gpxe/src/tests/uri_test.c | 145 + gpxe/src/usr/autoboot.c | 243 + gpxe/src/usr/dhcpmgmt.c | 59 + gpxe/src/usr/ifmgmt.c | 121 + gpxe/src/usr/imgmgmt.c | 142 + gpxe/src/usr/pxemenu.c | 381 + gpxe/src/usr/route.c | 43 + gpxe/src/util/.gitignore | 7 + gpxe/src/util/Makefile | 22 + gpxe/src/util/Option/ROM.pm | 501 + gpxe/src/util/catrom.pl | 48 + gpxe/src/util/disrom.pl | 81 + gpxe/src/util/dskpad.pl | 12 + gpxe/src/util/efirom.c | 280 + gpxe/src/util/elf2efi.c | 803 ++ gpxe/src/util/geniso | 56 + gpxe/src/util/genliso | 85 + gpxe/src/util/get-pci-ids | 135 + gpxe/src/util/hijack.c | 628 ++ gpxe/src/util/makerom.pl | 226 + gpxe/src/util/mergerom.pl | 98 + gpxe/src/util/modrom.pl | 226 + gpxe/src/util/mucurses_test.c | 63 + gpxe/src/util/nrv2b.c | 1501 +++ gpxe/src/util/parserom.pl | 64 + gpxe/src/util/sortobjdump.pl | 40 + gpxe/src/util/swapdevids.pl | 49 + gpxe/src/util/symcheck.pl | 191 + gpxe/src/util/zbin.c | 353 + libfat/cache.c | 65 + libfat/fat.h | 110 + libfat/fatchain.c | 132 + libfat/libfat.h | 85 + libfat/libfatint.h | 55 + libfat/open.c | 117 + libfat/searchdir.c | 63 + libfat/ulint.h | 122 + libinstaller/Makefile | 35 + libinstaller/advconst.h | 1 + libinstaller/bin2c.pl | 78 + libinstaller/setadv.c | 159 + libinstaller/syslinux.h | 51 + libinstaller/syslxint.h | 74 + libinstaller/syslxmod.c | 287 + linux/Makefile | 61 + linux/syslinux | Bin 0 -> 25128 bytes linux/syslinux-nomtools | Bin 0 -> 25128 bytes linux/syslinux.c | 560 + man/extlinux.1 | 59 + man/gethostip.1 | 45 + man/lss16toppm.1 | 27 + man/ppmtolss16.1 | 64 + man/syslinux.1 | 388 + man/syslinux2ansi.1 | 41 + mbr/Makefile | 53 + mbr/adjust.h | 53 + mbr/altmbr.S | 304 + mbr/altmbr.bin | Bin 0 -> 439 bytes mbr/altmbr_c.bin | Bin 0 -> 439 bytes mbr/altmbr_f.bin | Bin 0 -> 439 bytes mbr/checksize.pl | 54 + mbr/gptmbr.S | 294 + mbr/gptmbr.bin | Bin 0 -> 424 bytes mbr/gptmbr_c.bin | Bin 0 -> 424 bytes mbr/gptmbr_f.bin | Bin 0 -> 424 bytes mbr/isohdpfx.S | 261 + mbr/isohdpfx.bin | Bin 0 -> 432 bytes mbr/isohdpfx_c.bin | Bin 0 -> 432 bytes mbr/isohdpfx_f.bin | Bin 0 -> 432 bytes mbr/isohdppx.S | 2 + mbr/isohdppx.bin | Bin 0 -> 432 bytes mbr/isohdppx_c.bin | Bin 0 -> 432 bytes mbr/isohdppx_f.bin | Bin 0 -> 432 bytes mbr/mbr.S | 299 + mbr/mbr.bin | Bin 0 -> 440 bytes mbr/mbr.ld | 73 + mbr/mbr_c.bin | Bin 0 -> 440 bytes mbr/mbr_f.bin | Bin 0 -> 440 bytes mbr/oldmbr.asm | 230 + memdisk/Makefile | 95 + memdisk/conio.c | 363 + memdisk/conio.h | 31 + memdisk/e820.h | 33 + memdisk/e820data | 15 + memdisk/e820func.c | 107 + memdisk/e820test.c | 88 + memdisk/fdgeo.pl | 56 + memdisk/inflate.c | 1083 ++ memdisk/memcpy.S | 86 + memdisk/memdisk | Bin 0 -> 19584 bytes memdisk/memdisk.h | 88 + memdisk/memdisk.inc | 978 ++ memdisk/memdisk.ld | 140 + memdisk/memdisk16.asm | 791 ++ memdisk/memdisk_chs.asm | 2 + memdisk/memdisk_edd.asm | 2 + memdisk/memset.S | 86 + memdisk/msetup.c | 178 + memdisk/postprocess.pl | 58 + memdisk/setup.c | 1075 ++ memdisk/start32.S | 29 + memdisk/testdata1 | 13 + memdisk/testdata2 | 10 + memdisk/testdata3 | 14 + memdisk/unzip.c | 390 + memdisk/version.h | 25 + memdump/Makefile | 67 + memdump/README | 19 + memdump/__divdi3.c | 29 + memdump/__udivmoddi4.c | 31 + memdump/argv.c | 92 + memdump/code16.h | 6 + memdump/com16.ld | 127 + memdump/conio.c | 42 + memdump/crt0.S | 53 + memdump/errno.h | 7 + memdump/inttypes.h | 1 + memdump/io.h | 40 + memdump/main.c | 141 + memdump/malloc.h | 54 + memdump/memcpy.S | 23 + memdump/memdump.com | Bin 0 -> 5528 bytes memdump/memset.S | 21 + memdump/mystuff.h | 24 + memdump/printf.c | 297 + memdump/serial.c | 80 + memdump/skipatou.c | 10 + memdump/stdint.h | 142 + memdump/stdio.h | 21 + memdump/stdlib.h | 14 + memdump/string.h | 23 + memdump/strtoul.c | 72 + memdump/ymodem.txt | 2108 ++++ memdump/ymsend.c | 167 + memdump/ymsend.h | 27 + mime/image/x-lss16.xml | 39 + modules/Makefile | 60 + modules/gfxboot.asm | 985 ++ modules/gfxboot.com | Bin 0 -> 1850 bytes modules/poweroff.asm | 102 + modules/poweroff.com | Bin 0 -> 239 bytes modules/pxechain.asm | 558 + modules/pxechain.com | Bin 0 -> 998 bytes mtools/Makefile | 43 + mtools/syslinux | Bin 0 -> 25604 bytes mtools/syslinux.c | 352 + now.pl | 21 + sample/Makefile | 70 + sample/README | 6 + sample/atou.c | 13 + sample/c32echo.c | 48 + sample/c32echo.c32 | Bin 0 -> 162 bytes sample/c32entry.S | 66 + sample/c32exit.S | 10 + sample/comecho.asm | 32 + sample/comecho.com | Bin 0 -> 36 bytes sample/conio.c | 60 + sample/fd.c | 59 + sample/fd.c32 | Bin 0 -> 1844 bytes sample/filetest.c | 98 + sample/filetest.c32 | Bin 0 -> 2687 bytes sample/hello.c | 44 + sample/hello.c32 | Bin 0 -> 141 bytes sample/hello2.c | 60 + sample/hello2.c32 | Bin 0 -> 220 bytes sample/m16-640x640-syslinux.jpg | Bin 0 -> 46191 bytes sample/mdiskchk.c | 148 + sample/mdiskchk.com | Bin 0 -> 9680 bytes sample/printf.c | 306 + sample/sample.msg | 13 + sample/skipatou.c | 14 + sample/syslinux_splash.jpg | Bin 0 -> 56299 bytes sample/syslogo.lss | Bin 0 -> 25771 bytes sample/syslogo.ppm.gz | Bin 0 -> 24364 bytes syslinux-3.82.tar.gz | Bin 0 -> 3795792 bytes syslinux.spec | 264 + utils/Makefile | 60 + utils/bin2hex.pl | 48 + utils/gethostip | Bin 0 -> 4336 bytes utils/gethostip.c | 131 + utils/isohybrid | 358 + utils/isohybrid.in | 258 + utils/keytab-lilo | 95 + utils/lss16toppm | 111 + utils/md5pass | 34 + utils/mkdiskimage | 335 + utils/mkdiskimage.in | 317 + utils/ppmtolss16 | 393 + utils/sha1pass | 34 + utils/syslinux2ansi | 53 + version | 1 + version.gen | 6 + version.h | 6 + version.mk | 6 + version.pl | 39 + win32/Makefile | 87 + win32/README | 8 + win32/find-mingw.sh | 31 + win32/hello.c | 13 + win32/syslinux.c | 540 + win32/syslinux.exe | Bin 0 -> 27648 bytes 1692 files changed, 397159 insertions(+) create mode 100644 BUGS create mode 100644 COPYING create mode 100644 MCONFIG create mode 100644 MCONFIG.embedded create mode 100644 Makefile create mode 100644 NEWS create mode 100644 README create mode 100644 TODO create mode 100644 codepage/Makefile create mode 100644 codepage/UnicodeData create mode 100644 codepage/cp1250.txt create mode 100644 codepage/cp1251.txt create mode 100644 codepage/cp1252.txt create mode 100644 codepage/cp1253.txt create mode 100644 codepage/cp1254.txt create mode 100644 codepage/cp1255.txt create mode 100644 codepage/cp1256.txt create mode 100644 codepage/cp1257.txt create mode 100644 codepage/cp1258.txt create mode 100644 codepage/cp437.txt create mode 100644 codepage/cp737.txt create mode 100644 codepage/cp775.txt create mode 100644 codepage/cp850.txt create mode 100644 codepage/cp852.txt create mode 100644 codepage/cp855.txt create mode 100644 codepage/cp857.txt create mode 100644 codepage/cp860.txt create mode 100644 codepage/cp861.txt create mode 100644 codepage/cp862.txt create mode 100644 codepage/cp863.txt create mode 100644 codepage/cp864.txt create mode 100644 codepage/cp865.txt create mode 100644 codepage/cp866.txt create mode 100644 codepage/cp869.txt create mode 100644 codepage/cp874.txt create mode 100755 codepage/cptable.pl create mode 100755 codepage/gensubset.pl create mode 100644 codepage/iso8859-1.txt create mode 100644 codepage/iso8859-10.txt create mode 100644 codepage/iso8859-11.txt create mode 100644 codepage/iso8859-13.txt create mode 100644 codepage/iso8859-14.txt create mode 100644 codepage/iso8859-15.txt create mode 100644 codepage/iso8859-16.txt create mode 100644 codepage/iso8859-2.txt create mode 100644 codepage/iso8859-3.txt create mode 100644 codepage/iso8859-4.txt create mode 100644 codepage/iso8859-5.txt create mode 100644 codepage/iso8859-6.txt create mode 100644 codepage/iso8859-7.txt create mode 100644 codepage/iso8859-8.txt create mode 100644 codepage/iso8859-9.txt create mode 100644 com32/LICENCE create mode 100644 com32/MCONFIG create mode 100644 com32/Makefile create mode 100644 com32/cmenu/.gitignore create mode 100644 com32/cmenu/CHANGES create mode 100644 com32/cmenu/HISTORY create mode 100644 com32/cmenu/MANUAL create mode 100644 com32/cmenu/MENU_FORMAT create mode 100644 com32/cmenu/Makefile create mode 100644 com32/cmenu/README create mode 100644 com32/cmenu/TODO create mode 100644 com32/cmenu/adv_menu.tpl create mode 100644 com32/cmenu/complex.c create mode 100755 com32/cmenu/complex.c32 create mode 100644 com32/cmenu/display.c create mode 100755 com32/cmenu/display.c32 create mode 100644 com32/cmenu/libmenu/com32io.c create mode 100644 com32/cmenu/libmenu/com32io.h create mode 100644 com32/cmenu/libmenu/com32io.o create mode 100644 com32/cmenu/libmenu/des.c create mode 100644 com32/cmenu/libmenu/des.h create mode 100644 com32/cmenu/libmenu/des.o create mode 100644 com32/cmenu/libmenu/help.c create mode 100644 com32/cmenu/libmenu/help.h create mode 100644 com32/cmenu/libmenu/help.o create mode 100644 com32/cmenu/libmenu/libmenu.a create mode 100644 com32/cmenu/libmenu/menu.c create mode 100644 com32/cmenu/libmenu/menu.h create mode 100644 com32/cmenu/libmenu/menu.o create mode 100644 com32/cmenu/libmenu/passwords.c create mode 100644 com32/cmenu/libmenu/passwords.h create mode 100644 com32/cmenu/libmenu/passwords.o create mode 100644 com32/cmenu/libmenu/scancodes.h create mode 100644 com32/cmenu/libmenu/syslnx.c create mode 100644 com32/cmenu/libmenu/syslnx.h create mode 100644 com32/cmenu/libmenu/syslnx.o create mode 100644 com32/cmenu/libmenu/tui.c create mode 100644 com32/cmenu/libmenu/tui.h create mode 100644 com32/cmenu/libmenu/tui.o create mode 100644 com32/cmenu/menugen.py create mode 100644 com32/cmenu/password create mode 100644 com32/cmenu/simple.c create mode 100755 com32/cmenu/simple.c32 create mode 100755 com32/cmenu/test.c32 create mode 100644 com32/cmenu/test.menu create mode 100755 com32/cmenu/test2.c32 create mode 100644 com32/cmenu/test2.menu create mode 100644 com32/gdbstub/Makefile create mode 100644 com32/gdbstub/gdbstub.c create mode 100644 com32/gdbstub/int.S create mode 100644 com32/gdbstub/main.c create mode 100644 com32/gdbstub/serial.c create mode 100644 com32/gdbstub/serial.h create mode 100644 com32/gplinclude/README create mode 100644 com32/gplinclude/cpuid.h create mode 100644 com32/gplinclude/dmi/dmi.h create mode 100644 com32/gplinclude/dmi/dmi_base_board.h create mode 100644 com32/gplinclude/dmi/dmi_battery.h create mode 100644 com32/gplinclude/dmi/dmi_bios.h create mode 100644 com32/gplinclude/dmi/dmi_chassis.h create mode 100644 com32/gplinclude/dmi/dmi_ipmi.h create mode 100644 com32/gplinclude/dmi/dmi_memory.h create mode 100644 com32/gplinclude/dmi/dmi_processor.h create mode 100644 com32/gplinclude/dmi/dmi_system.h create mode 100644 com32/gplinclude/vpd/vpd.h create mode 100644 com32/gpllib/Makefile create mode 100644 com32/gpllib/cpuid.c create mode 100644 com32/gpllib/dmi/dmi.c create mode 100644 com32/gpllib/dmi/dmi_base_board.c create mode 100644 com32/gpllib/dmi/dmi_battery.c create mode 100644 com32/gpllib/dmi/dmi_bios.c create mode 100644 com32/gpllib/dmi/dmi_chassis.c create mode 100644 com32/gpllib/dmi/dmi_ipmi.c create mode 100644 com32/gpllib/dmi/dmi_memory.c create mode 100644 com32/gpllib/dmi/dmi_processor.c create mode 100644 com32/gpllib/vpd/vpd.c create mode 100644 com32/hdt/Makefile create mode 100644 com32/hdt/hdt-ata.c create mode 100644 com32/hdt/hdt-ata.h create mode 100644 com32/hdt/hdt-cli-cpu.c create mode 100644 com32/hdt/hdt-cli-dmi.c create mode 100644 com32/hdt/hdt-cli-hdt.c create mode 100644 com32/hdt/hdt-cli-kernel.c create mode 100644 com32/hdt/hdt-cli-pci.c create mode 100644 com32/hdt/hdt-cli-pxe.c create mode 100644 com32/hdt/hdt-cli-syslinux.c create mode 100644 com32/hdt/hdt-cli-vesa.c create mode 100644 com32/hdt/hdt-cli-vpd.c create mode 100644 com32/hdt/hdt-cli.c create mode 100644 com32/hdt/hdt-cli.h create mode 100644 com32/hdt/hdt-common.c create mode 100644 com32/hdt/hdt-common.h create mode 100644 com32/hdt/hdt-menu-about.c create mode 100644 com32/hdt/hdt-menu-disk.c create mode 100644 com32/hdt/hdt-menu-dmi.c create mode 100644 com32/hdt/hdt-menu-kernel.c create mode 100644 com32/hdt/hdt-menu-pci.c create mode 100644 com32/hdt/hdt-menu-processor.c create mode 100644 com32/hdt/hdt-menu-pxe.c create mode 100644 com32/hdt/hdt-menu-summary.c create mode 100644 com32/hdt/hdt-menu-syslinux.c create mode 100644 com32/hdt/hdt-menu-vesa.c create mode 100644 com32/hdt/hdt-menu-vpd.c create mode 100644 com32/hdt/hdt-menu.c create mode 100644 com32/hdt/hdt-menu.h create mode 100644 com32/hdt/hdt.c create mode 100755 com32/hdt/hdt.c32 create mode 100644 com32/hdt/hdt.h create mode 100644 com32/hdt/lib-ansi.c create mode 100644 com32/hdt/lib-ansi.h create mode 100644 com32/include/alloca.h create mode 100644 com32/include/assert.h create mode 100644 com32/include/bitsize/limits.h create mode 100644 com32/include/bitsize/stddef.h create mode 100644 com32/include/bitsize/stdint.h create mode 100644 com32/include/bitsize/stdintconst.h create mode 100644 com32/include/bitsize/stdintlimits.h create mode 100644 com32/include/colortbl.h create mode 100644 com32/include/com32.h create mode 100644 com32/include/console.h create mode 100644 com32/include/cpufeature.h create mode 100644 com32/include/ctype.h create mode 100644 com32/include/dev.h create mode 100644 com32/include/dirent.h create mode 100644 com32/include/elf.h create mode 100644 com32/include/endian.h create mode 100644 com32/include/errno.h create mode 100644 com32/include/fcntl.h create mode 100644 com32/include/inttypes.h create mode 100644 com32/include/klibc/archsetjmp.h create mode 100644 com32/include/klibc/compiler.h create mode 100644 com32/include/klibc/diverr.h create mode 100644 com32/include/klibc/endian.h create mode 100644 com32/include/klibc/extern.h create mode 100644 com32/include/klibc/sysconfig.h create mode 100644 com32/include/limits.h create mode 100644 com32/include/math.h create mode 100644 com32/include/minmax.h create mode 100644 com32/include/netinet/in.h create mode 100644 com32/include/png.h create mode 100644 com32/include/pngconf.h create mode 100644 com32/include/setjmp.h create mode 100644 com32/include/stdarg.h create mode 100644 com32/include/stdbool.h create mode 100644 com32/include/stddef.h create mode 100644 com32/include/stdint.h create mode 100644 com32/include/stdio.h create mode 100644 com32/include/stdlib.h create mode 100644 com32/include/string.h create mode 100644 com32/include/sys/cpu.h create mode 100644 com32/include/sys/elf32.h create mode 100644 com32/include/sys/elf64.h create mode 100644 com32/include/sys/elfcommon.h create mode 100644 com32/include/sys/fpu.h create mode 100644 com32/include/sys/io.h create mode 100644 com32/include/sys/pci.h create mode 100644 com32/include/sys/stat.h create mode 100644 com32/include/sys/time.h create mode 100644 com32/include/sys/times.h create mode 100644 com32/include/sys/types.h create mode 100644 com32/include/syslinux/adv.h create mode 100644 com32/include/syslinux/advconst.h create mode 100644 com32/include/syslinux/align.h create mode 100644 com32/include/syslinux/boot.h create mode 100644 com32/include/syslinux/bootpm.h create mode 100644 com32/include/syslinux/bootrm.h create mode 100644 com32/include/syslinux/config.h create mode 100644 com32/include/syslinux/features.h create mode 100644 com32/include/syslinux/idle.h create mode 100644 com32/include/syslinux/io.h create mode 100644 com32/include/syslinux/keyboard.h create mode 100644 com32/include/syslinux/linux.h create mode 100644 com32/include/syslinux/loadfile.h create mode 100644 com32/include/syslinux/memscan.h create mode 100644 com32/include/syslinux/movebits.h create mode 100644 com32/include/syslinux/pxe.h create mode 100644 com32/include/syslinux/reboot.h create mode 100644 com32/include/syslinux/resolve.h create mode 100644 com32/include/syslinux/vesacon.h create mode 100644 com32/include/syslinux/video.h create mode 100644 com32/include/syslinux/zio.h create mode 100644 com32/include/time.h create mode 100644 com32/include/tinyjpeg.h create mode 100644 com32/include/unistd.h create mode 100644 com32/include/zconf.h create mode 100644 com32/include/zlib.h create mode 100644 com32/lib/MCONFIG create mode 100644 com32/lib/Makefile create mode 100644 com32/lib/abort.c create mode 100644 com32/lib/asprintf.c create mode 100644 com32/lib/atexit.c create mode 100644 com32/lib/atexit.h create mode 100644 com32/lib/atoi.c create mode 100644 com32/lib/atol.c create mode 100644 com32/lib/atoll.c create mode 100644 com32/lib/atox.c create mode 100644 com32/lib/calloc.c create mode 100644 com32/lib/chdir.c create mode 100644 com32/lib/closedir.c create mode 100644 com32/lib/com32.ld create mode 100644 com32/lib/creat.c create mode 100644 com32/lib/ctypes.c create mode 100644 com32/lib/errno.c create mode 100644 com32/lib/exit.c create mode 100644 com32/lib/fclose.c create mode 100644 com32/lib/fdopendir.c create mode 100644 com32/lib/fgetc.c create mode 100644 com32/lib/fgets.c create mode 100644 com32/lib/fopen.c create mode 100644 com32/lib/fopendev.c create mode 100644 com32/lib/fprintf.c create mode 100644 com32/lib/fputc.c create mode 100644 com32/lib/fputs.c create mode 100644 com32/lib/fread.c create mode 100644 com32/lib/fread2.c create mode 100644 com32/lib/free.c create mode 100644 com32/lib/fwrite.c create mode 100644 com32/lib/fwrite2.c create mode 100644 com32/lib/getcwd.c create mode 100644 com32/lib/getopt.c create mode 100644 com32/lib/init.h create mode 100644 com32/lib/jpeg/README create mode 100644 com32/lib/jpeg/bgr24.c create mode 100644 com32/lib/jpeg/bgra32.c create mode 100644 com32/lib/jpeg/decode1.c create mode 100644 com32/lib/jpeg/decode3.c create mode 100644 com32/lib/jpeg/grey.c create mode 100644 com32/lib/jpeg/jidctflt.c create mode 100644 com32/lib/jpeg/rgb24.c create mode 100644 com32/lib/jpeg/rgba32.c create mode 100644 com32/lib/jpeg/tinyjpeg-internal.h create mode 100644 com32/lib/jpeg/tinyjpeg.c create mode 100644 com32/lib/jpeg/yuv420p.c create mode 100644 com32/lib/libgcc/__ashldi3.S create mode 100644 com32/lib/libgcc/__ashrdi3.S create mode 100644 com32/lib/libgcc/__divdi3.c create mode 100644 com32/lib/libgcc/__lshrdi3.S create mode 100644 com32/lib/libgcc/__moddi3.c create mode 100644 com32/lib/libgcc/__muldi3.S create mode 100644 com32/lib/libgcc/__negdi2.S create mode 100644 com32/lib/libgcc/__udivdi3.c create mode 100644 com32/lib/libgcc/__udivmoddi4.c create mode 100644 com32/lib/libgcc/__umoddi3.c create mode 100644 com32/lib/libpng/ANNOUNCE create mode 100644 com32/lib/libpng/CHANGES create mode 100644 com32/lib/libpng/KNOWNBUG create mode 100644 com32/lib/libpng/LICENSE create mode 100644 com32/lib/libpng/README create mode 100644 com32/lib/libpng/TODO create mode 100644 com32/lib/libpng/Y2KINFO create mode 100644 com32/lib/libpng/example.c create mode 100644 com32/lib/libpng/libpng.3 create mode 100644 com32/lib/libpng/libpng.txt create mode 100644 com32/lib/libpng/libpngpf.3 create mode 100644 com32/lib/libpng/png.5 create mode 100644 com32/lib/libpng/png.c create mode 100644 com32/lib/libpng/pngerror.c create mode 100644 com32/lib/libpng/pnggccrd.c create mode 100644 com32/lib/libpng/pngget.c create mode 100644 com32/lib/libpng/pngmem.c create mode 100644 com32/lib/libpng/pngpread.c create mode 100644 com32/lib/libpng/pngread.c create mode 100644 com32/lib/libpng/pngrio.c create mode 100644 com32/lib/libpng/pngrtran.c create mode 100644 com32/lib/libpng/pngrutil.c create mode 100644 com32/lib/libpng/pngset.c create mode 100644 com32/lib/libpng/pngtest.c create mode 100644 com32/lib/libpng/pngtrans.c create mode 100644 com32/lib/libpng/pngvcrd.c create mode 100644 com32/lib/libpng/pngwio.c create mode 100644 com32/lib/libpng/pngwrite.c create mode 100644 com32/lib/libpng/pngwtran.c create mode 100644 com32/lib/libpng/pngwutil.c create mode 100644 com32/lib/lrand48.c create mode 100644 com32/lib/malloc.c create mode 100644 com32/lib/malloc.h create mode 100644 com32/lib/math/pow.S create mode 100644 com32/lib/math/strtod.c create mode 100644 com32/lib/memccpy.c create mode 100644 com32/lib/memchr.c create mode 100644 com32/lib/memcmp.c create mode 100644 com32/lib/memcpy.S create mode 100644 com32/lib/memmem.c create mode 100644 com32/lib/memmove.S create mode 100644 com32/lib/mempcpy.S create mode 100644 com32/lib/memset.S create mode 100644 com32/lib/memswap.c create mode 100644 com32/lib/onexit.c create mode 100644 com32/lib/opendir.c create mode 100644 com32/lib/pci/cfgtype.c create mode 100644 com32/lib/pci/pci.h create mode 100644 com32/lib/pci/readb.c create mode 100644 com32/lib/pci/readbios.c create mode 100644 com32/lib/pci/readl.c create mode 100644 com32/lib/pci/readw.c create mode 100644 com32/lib/pci/readx.c create mode 100644 com32/lib/pci/scan.c create mode 100644 com32/lib/pci/writeb.c create mode 100644 com32/lib/pci/writebios.c create mode 100644 com32/lib/pci/writel.c create mode 100644 com32/lib/pci/writew.c create mode 100644 com32/lib/pci/writex.c create mode 100644 com32/lib/perror.c create mode 100644 com32/lib/printf.c create mode 100644 com32/lib/putchar.c create mode 100644 com32/lib/puts.c create mode 100644 com32/lib/qsort.c create mode 100644 com32/lib/readdir.c create mode 100644 com32/lib/realloc.c create mode 100644 com32/lib/seed48.c create mode 100644 com32/lib/setjmp.S create mode 100644 com32/lib/snprintf.c create mode 100644 com32/lib/sprintf.c create mode 100644 com32/lib/srand48.c create mode 100644 com32/lib/sscanf.c create mode 100644 com32/lib/stack.c create mode 100644 com32/lib/stpcpy.c create mode 100644 com32/lib/stpncpy.c create mode 100644 com32/lib/strcasecmp.c create mode 100644 com32/lib/strcat.c create mode 100644 com32/lib/strchr.c create mode 100644 com32/lib/strcmp.c create mode 100644 com32/lib/strcpy.c create mode 100644 com32/lib/strdup.c create mode 100644 com32/lib/strerror.c create mode 100644 com32/lib/strlcat.c create mode 100644 com32/lib/strlcpy.c create mode 100644 com32/lib/strlen.c create mode 100644 com32/lib/strncasecmp.c create mode 100644 com32/lib/strncat.c create mode 100644 com32/lib/strncmp.c create mode 100644 com32/lib/strncpy.c create mode 100644 com32/lib/strndup.c create mode 100644 com32/lib/strnlen.c create mode 100644 com32/lib/strntoimax.c create mode 100644 com32/lib/strntoumax.c create mode 100644 com32/lib/strpcpy.c create mode 100644 com32/lib/strrchr.c create mode 100644 com32/lib/strsep.c create mode 100644 com32/lib/strspn.c create mode 100644 com32/lib/strstr.c create mode 100644 com32/lib/strtoimax.c create mode 100644 com32/lib/strtok.c create mode 100644 com32/lib/strtol.c create mode 100644 com32/lib/strtoll.c create mode 100644 com32/lib/strtoul.c create mode 100644 com32/lib/strtoull.c create mode 100644 com32/lib/strtoumax.c create mode 100644 com32/lib/strtox.c create mode 100644 com32/lib/sys/ansi.c create mode 100644 com32/lib/sys/ansi.h create mode 100644 com32/lib/sys/ansicon_write.c create mode 100644 com32/lib/sys/ansiserial_write.c create mode 100644 com32/lib/sys/argv.c create mode 100644 com32/lib/sys/cfarcall.c create mode 100644 com32/lib/sys/close.c create mode 100644 com32/lib/sys/colortable.c create mode 100644 com32/lib/sys/entry.S create mode 100644 com32/lib/sys/err_read.c create mode 100644 com32/lib/sys/err_write.c create mode 100644 com32/lib/sys/exit.S create mode 100644 com32/lib/sys/farcall.c create mode 100644 com32/lib/sys/file.h create mode 100644 com32/lib/sys/fileclose.c create mode 100644 com32/lib/sys/fileinfo.c create mode 100644 com32/lib/sys/fileread.c create mode 100644 com32/lib/sys/fstat.c create mode 100644 com32/lib/sys/ftell.c create mode 100644 com32/lib/sys/intcall.c create mode 100644 com32/lib/sys/isatty.c create mode 100644 com32/lib/sys/line_input.c create mode 100644 com32/lib/sys/null_read.c create mode 100644 com32/lib/sys/null_write.c create mode 100644 com32/lib/sys/open.c create mode 100644 com32/lib/sys/openconsole.c create mode 100644 com32/lib/sys/opendev.c create mode 100644 com32/lib/sys/rawcon_read.c create mode 100644 com32/lib/sys/rawcon_write.c create mode 100644 com32/lib/sys/read.c create mode 100644 com32/lib/sys/screensize.c create mode 100644 com32/lib/sys/serial_write.c create mode 100644 com32/lib/sys/stdcon_read.c create mode 100644 com32/lib/sys/stdcon_write.c create mode 100644 com32/lib/sys/times.c create mode 100644 com32/lib/sys/vesa/alphatbl.pl create mode 100644 com32/lib/sys/vesa/background.c create mode 100644 com32/lib/sys/vesa/debug.h create mode 100644 com32/lib/sys/vesa/drawtxt.c create mode 100644 com32/lib/sys/vesa/fill.h create mode 100644 com32/lib/sys/vesa/fmtpixel.c create mode 100644 com32/lib/sys/vesa/initvesa.c create mode 100644 com32/lib/sys/vesa/screencpy.c create mode 100644 com32/lib/sys/vesa/vesa.h create mode 100644 com32/lib/sys/vesa/video.h create mode 100644 com32/lib/sys/vesacon_write.c create mode 100644 com32/lib/sys/vesaserial_write.c create mode 100644 com32/lib/sys/write.c create mode 100644 com32/lib/sys/x86_init_fpu.c create mode 100644 com32/lib/sys/xserial_write.c create mode 100644 com32/lib/sys/zeroregs.c create mode 100644 com32/lib/sys/zfile.c create mode 100644 com32/lib/sys/zfopen.c create mode 100644 com32/lib/syslinux/addlist.c create mode 100644 com32/lib/syslinux/adv.c create mode 100644 com32/lib/syslinux/advwrite.c create mode 100644 com32/lib/syslinux/cleanup.c create mode 100644 com32/lib/syslinux/config.c create mode 100644 com32/lib/syslinux/dsinfo.c create mode 100644 com32/lib/syslinux/dump_mmap.c create mode 100644 com32/lib/syslinux/dump_movelist.c create mode 100644 com32/lib/syslinux/features.c create mode 100644 com32/lib/syslinux/floadfile.c create mode 100644 com32/lib/syslinux/freelist.c create mode 100644 com32/lib/syslinux/getadv.c create mode 100644 com32/lib/syslinux/idle.c create mode 100644 com32/lib/syslinux/initramfs.c create mode 100644 com32/lib/syslinux/initramfs_archive.c create mode 100644 com32/lib/syslinux/initramfs_file.c create mode 100644 com32/lib/syslinux/initramfs_loadfile.c create mode 100644 com32/lib/syslinux/ipappend.c create mode 100644 com32/lib/syslinux/keyboard.c create mode 100644 com32/lib/syslinux/load_linux.c create mode 100644 com32/lib/syslinux/loadfile.c create mode 100644 com32/lib/syslinux/localboot.c create mode 100644 com32/lib/syslinux/memmap.c create mode 100644 com32/lib/syslinux/memscan.c create mode 100644 com32/lib/syslinux/movebits.c create mode 100644 com32/lib/syslinux/pxe_get_cached.c create mode 100644 com32/lib/syslinux/pxe_get_nic.c create mode 100644 com32/lib/syslinux/reboot.c create mode 100644 com32/lib/syslinux/run_command.c create mode 100644 com32/lib/syslinux/run_default.c create mode 100644 com32/lib/syslinux/runimage.c create mode 100644 com32/lib/syslinux/serial.c create mode 100644 com32/lib/syslinux/setadv.c create mode 100644 com32/lib/syslinux/shuffle.c create mode 100644 com32/lib/syslinux/shuffle_pm.c create mode 100644 com32/lib/syslinux/shuffle_rm.c create mode 100644 com32/lib/syslinux/version.c create mode 100644 com32/lib/syslinux/zloadfile.c create mode 100644 com32/lib/syslinux/zonelist.c create mode 100644 com32/lib/vasprintf.c create mode 100644 com32/lib/vfprintf.c create mode 100644 com32/lib/vprintf.c create mode 100644 com32/lib/vsnprintf.c create mode 100644 com32/lib/vsprintf.c create mode 100644 com32/lib/vsscanf.c create mode 100644 com32/lib/zalloc.c create mode 100644 com32/lib/zlib/FAQ create mode 100644 com32/lib/zlib/README create mode 100644 com32/lib/zlib/adler32.c create mode 100644 com32/lib/zlib/algorithm.txt create mode 100644 com32/lib/zlib/compress.c create mode 100644 com32/lib/zlib/crc32.c create mode 100644 com32/lib/zlib/crc32.h create mode 100644 com32/lib/zlib/deflate.c create mode 100644 com32/lib/zlib/deflate.h create mode 100644 com32/lib/zlib/example.c create mode 100644 com32/lib/zlib/gzio.c create mode 100644 com32/lib/zlib/infback.c create mode 100644 com32/lib/zlib/inffast.c create mode 100644 com32/lib/zlib/inffast.h create mode 100644 com32/lib/zlib/inffixed.h create mode 100644 com32/lib/zlib/inflate.c create mode 100644 com32/lib/zlib/inflate.h create mode 100644 com32/lib/zlib/inftrees.c create mode 100644 com32/lib/zlib/inftrees.h create mode 100644 com32/lib/zlib/minigzip.c create mode 100644 com32/lib/zlib/trees.c create mode 100644 com32/lib/zlib/trees.h create mode 100644 com32/lib/zlib/uncompr.c create mode 100644 com32/lib/zlib/zconf.in.h create mode 100644 com32/lib/zlib/zlib.3 create mode 100644 com32/lib/zlib/zutil.c create mode 100644 com32/lib/zlib/zutil.h create mode 100644 com32/libutil/Makefile create mode 100644 com32/libutil/ansiline.c create mode 100644 com32/libutil/ansiraw.c create mode 100644 com32/libutil/base64.c create mode 100644 com32/libutil/crypt-md5.c create mode 100644 com32/libutil/get_key.c create mode 100644 com32/libutil/include/base64.h create mode 100644 com32/libutil/include/consoles.h create mode 100644 com32/libutil/include/getkey.h create mode 100644 com32/libutil/include/libutil.h create mode 100644 com32/libutil/include/md5.h create mode 100644 com32/libutil/include/minmax.h create mode 100644 com32/libutil/include/sha1.h create mode 100644 com32/libutil/include/xcrypt.h create mode 100644 com32/libutil/libutil_com.a create mode 100644 com32/libutil/libutil_lnx.a create mode 100644 com32/libutil/md5.c create mode 100644 com32/libutil/sha1hash.c create mode 100644 com32/libutil/sha256crypt.c create mode 100644 com32/libutil/sha512crypt.c create mode 100644 com32/libutil/unbase64.c create mode 100644 com32/mboot/Makefile create mode 100644 com32/mboot/apm.c create mode 100644 com32/mboot/map.c create mode 100644 com32/mboot/mb_header.h create mode 100644 com32/mboot/mb_info.h create mode 100644 com32/mboot/mboot.c create mode 100755 com32/mboot/mboot.c32 create mode 100644 com32/mboot/mboot.h create mode 100644 com32/mboot/mem.c create mode 100644 com32/mboot/solaris.c create mode 100644 com32/menu/Makefile create mode 100644 com32/menu/background.c create mode 100644 com32/menu/colors.c create mode 100644 com32/menu/drain.c create mode 100644 com32/menu/execute.c create mode 100644 com32/menu/menu.c create mode 100755 com32/menu/menu.c32 create mode 100644 com32/menu/menu.h create mode 100644 com32/menu/menumain.c create mode 100644 com32/menu/passwd.c create mode 100644 com32/menu/printmsg.c create mode 100644 com32/menu/readconfig.c create mode 100644 com32/menu/refstr.c create mode 100644 com32/menu/refstr.h create mode 100644 com32/menu/vesamenu.c create mode 100755 com32/menu/vesamenu.c32 create mode 100644 com32/modules/Makefile create mode 100644 com32/modules/chain.c create mode 100755 com32/modules/chain.c32 create mode 100644 com32/modules/cmd.c create mode 100755 com32/modules/cmd.c32 create mode 100644 com32/modules/config.c create mode 100755 com32/modules/config.c32 create mode 100644 com32/modules/cpuidtest.c create mode 100755 com32/modules/cpuidtest.c32 create mode 100644 com32/modules/dmi_utils.c create mode 100644 com32/modules/dmitest.c create mode 100755 com32/modules/dmitest.c32 create mode 100644 com32/modules/elf.c create mode 100755 com32/modules/elf.c32 create mode 100644 com32/modules/ethersel.c create mode 100755 com32/modules/ethersel.c32 create mode 100644 com32/modules/ifcpu64.c create mode 100755 com32/modules/ifcpu64.c32 create mode 100644 com32/modules/kbdmap.c create mode 100755 com32/modules/kbdmap.c32 create mode 100644 com32/modules/linux.c create mode 100755 com32/modules/linux.c32 create mode 100644 com32/modules/meminfo.c create mode 100755 com32/modules/meminfo.c32 create mode 100644 com32/modules/pcitest.c create mode 100755 com32/modules/pcitest.c32 create mode 100644 com32/modules/pmload.c create mode 100755 com32/modules/pmload.c32 create mode 100644 com32/modules/reboot.c create mode 100755 com32/modules/reboot.c32 create mode 100644 com32/modules/sanboot.c create mode 100755 com32/modules/sanboot.c32 create mode 100644 com32/modules/sdi.c create mode 100755 com32/modules/sdi.c32 create mode 100644 com32/modules/stubs/copydown.asm create mode 100644 com32/modules/stubs/swapstub.asm create mode 100644 com32/modules/vesainfo.c create mode 100755 com32/modules/vesainfo.c32 create mode 100644 com32/modules/vpdtest.c create mode 100755 com32/modules/vpdtest.c32 create mode 100644 com32/rosh/MCONFIG create mode 100644 com32/rosh/Makefile create mode 100644 com32/rosh/rosh.c create mode 100755 com32/rosh/rosh.c32 create mode 100644 com32/rosh/rosh.h create mode 100644 com32/samples/Makefile create mode 100644 com32/samples/advdump.c create mode 100755 com32/samples/advdump.c32 create mode 100644 com32/samples/cat.c create mode 100755 com32/samples/cat.c32 create mode 100644 com32/samples/entrydump.c create mode 100755 com32/samples/entrydump.c32 create mode 100644 com32/samples/fancyhello.c create mode 100755 com32/samples/fancyhello.c32 create mode 100755 com32/samples/fancyhello.lnx create mode 100644 com32/samples/hello.c create mode 100755 com32/samples/hello.c32 create mode 100644 com32/samples/keytest.c create mode 100755 com32/samples/keytest.c32 create mode 100755 com32/samples/keytest.lnx create mode 100644 com32/samples/localboot.c create mode 100755 com32/samples/localboot.c32 create mode 100644 com32/samples/resolv.c create mode 100755 com32/samples/resolv.c32 create mode 100644 com32/samples/serialinfo.c create mode 100755 com32/samples/serialinfo.c32 create mode 100644 core/Makefile create mode 100644 core/abort.inc create mode 100644 core/adv.inc create mode 100644 core/bcopy32.inc create mode 100644 core/bcopyxx.inc create mode 100644 core/bios.inc create mode 100644 core/bootsect.inc create mode 100644 core/cache.inc create mode 100755 core/checksumiso.pl create mode 100644 core/cleanup.inc create mode 100644 core/cmdline.inc create mode 100644 core/com32.inc create mode 100644 core/comboot.inc create mode 100644 core/config.inc create mode 100644 core/configinit.inc create mode 100644 core/conio.inc create mode 100644 core/cpuinit.inc create mode 100644 core/diskstart.inc create mode 100644 core/dnsresolv.inc create mode 100644 core/ext2_fs.inc create mode 100644 core/extlinux.asm create mode 100755 core/extlinux.bin create mode 100644 core/extlinux.bss create mode 100644 core/extlinux.sys create mode 100644 core/font.inc create mode 100755 core/genhash.pl create mode 100644 core/getc.inc create mode 100644 core/graphics.inc create mode 100644 core/head.inc create mode 100644 core/highmem.inc create mode 100644 core/idle.inc create mode 100644 core/init.inc create mode 100644 core/io.inc create mode 100644 core/isolinux-debug.asm create mode 100755 core/isolinux-debug.bin create mode 100644 core/isolinux.asm create mode 100755 core/isolinux.bin create mode 100644 core/kernel.inc create mode 100644 core/keywords create mode 100644 core/keywords.inc create mode 100644 core/kwdhash.gen create mode 100644 core/layout.inc create mode 100644 core/ldlinux.asm create mode 100755 core/ldlinux.bin create mode 100644 core/ldlinux.bss create mode 100644 core/ldlinux.sys create mode 100644 core/loadhigh.inc create mode 100644 core/localboot.inc create mode 100755 core/lstadjust.pl create mode 100644 core/macros.inc create mode 100644 core/parsecmd.inc create mode 100644 core/parseconfig.inc create mode 100644 core/plaincon.inc create mode 100644 core/pxe.inc create mode 100644 core/pxeidle.inc create mode 100755 core/pxelinux.0 create mode 100644 core/pxelinux.asm create mode 100755 core/pxelinux.bin create mode 100644 core/rawcon.inc create mode 100644 core/regdump.inc create mode 100644 core/rllpack.inc create mode 100644 core/runkernel.inc create mode 100644 core/serirq.inc create mode 100644 core/stack.inc create mode 100644 core/strcpy.inc create mode 100644 core/strecpy.inc create mode 100644 core/syslinux.ld create mode 100644 core/tracers.inc create mode 100644 core/ui.inc create mode 100644 core/writedec.inc create mode 100644 core/writehex.inc create mode 100644 core/writestr.inc create mode 100644 doc/SubmittingPatches.txt create mode 100644 doc/comboot.txt create mode 100644 doc/distrib.txt create mode 100644 doc/extlinux.txt create mode 100644 doc/gpt.txt create mode 100644 doc/isolinux.txt create mode 100644 doc/keytab-lilo.txt create mode 100644 doc/mboot.txt create mode 100644 doc/memdisk.txt create mode 100644 doc/menu.txt create mode 100644 doc/pxelinux.txt create mode 100644 doc/rfc5071.txt create mode 100644 doc/sdi.txt create mode 100644 doc/syslinux.txt create mode 100644 doc/usbkey.txt create mode 100644 dos/Makefile create mode 100644 dos/__divdi3.c create mode 100644 dos/__udivmoddi4.c create mode 100644 dos/argv.c create mode 100644 dos/atou.c create mode 100644 dos/code16.h create mode 100644 dos/com16.ld create mode 100644 dos/conio.c create mode 100644 dos/copybs.asm create mode 100644 dos/copybs.com create mode 100644 dos/crt0.S create mode 100644 dos/errno.h create mode 100644 dos/free.c create mode 100644 dos/inttypes.h create mode 100644 dos/malloc.c create mode 100644 dos/malloc.h create mode 100644 dos/memcpy.S create mode 100644 dos/memset.S create mode 100644 dos/mystuff.h create mode 100644 dos/perror.c create mode 100644 dos/printf.c create mode 100644 dos/skipatou.c create mode 100644 dos/stdint.h create mode 100644 dos/stdio.h create mode 100644 dos/stdlib.h create mode 100644 dos/string.h create mode 100644 dos/syslinux.c create mode 100755 dos/syslinux.com create mode 100644 dummy.c create mode 100644 extlinux/Makefile create mode 100644 extlinux/ext2_fs.h create mode 100755 extlinux/extlinux create mode 100644 extlinux/main.c create mode 100755 gen-id.sh create mode 100644 gpxe/.gitignore create mode 100644 gpxe/COPYING create mode 100644 gpxe/COPYRIGHTS create mode 100644 gpxe/LOG create mode 100644 gpxe/Makefile create mode 100644 gpxe/README create mode 100644 gpxe/VERSION create mode 100644 gpxe/gpxelinux.0 create mode 100644 gpxe/pxelinux.gpxe create mode 100644 gpxe/src/.gitignore create mode 100644 gpxe/src/Makefile create mode 100644 gpxe/src/Makefile.housekeeping create mode 100644 gpxe/src/README.pixify create mode 100644 gpxe/src/arch/i386/Makefile create mode 100644 gpxe/src/arch/i386/Makefile.efi create mode 100644 gpxe/src/arch/i386/Makefile.pcbios create mode 100644 gpxe/src/arch/i386/README.i386 create mode 100644 gpxe/src/arch/i386/core/aout_loader.c create mode 100644 gpxe/src/arch/i386/core/basemem_packet.c create mode 100644 gpxe/src/arch/i386/core/cpu.c create mode 100644 gpxe/src/arch/i386/core/dumpregs.c create mode 100644 gpxe/src/arch/i386/core/etherboot.prefix.lds create mode 100644 gpxe/src/arch/i386/core/freebsd_loader.c create mode 100644 gpxe/src/arch/i386/core/gdbidt.S create mode 100644 gpxe/src/arch/i386/core/gdbmach.c create mode 100644 gpxe/src/arch/i386/core/nulltrap.c create mode 100644 gpxe/src/arch/i386/core/pic8259.c create mode 100644 gpxe/src/arch/i386/core/prefixudata.lds create mode 100644 gpxe/src/arch/i386/core/prefixzdata.lds create mode 100644 gpxe/src/arch/i386/core/rdtsc_timer.c create mode 100644 gpxe/src/arch/i386/core/realmode.c create mode 100644 gpxe/src/arch/i386/core/relocate.c create mode 100644 gpxe/src/arch/i386/core/setjmp.S create mode 100644 gpxe/src/arch/i386/core/stack.S create mode 100644 gpxe/src/arch/i386/core/stack16.S create mode 100644 gpxe/src/arch/i386/core/start16.lds create mode 100644 gpxe/src/arch/i386/core/start16z.lds create mode 100644 gpxe/src/arch/i386/core/timer2.c create mode 100644 gpxe/src/arch/i386/core/video_subr.c create mode 100644 gpxe/src/arch/i386/core/virtaddr.S create mode 100644 gpxe/src/arch/i386/core/wince_loader.c create mode 100644 gpxe/src/arch/i386/core/x86_io.c create mode 100644 gpxe/src/arch/i386/drivers/net/undi.c create mode 100644 gpxe/src/arch/i386/drivers/net/undiisr.S create mode 100644 gpxe/src/arch/i386/drivers/net/undiload.c create mode 100644 gpxe/src/arch/i386/drivers/net/undinet.c create mode 100644 gpxe/src/arch/i386/drivers/net/undionly.c create mode 100644 gpxe/src/arch/i386/drivers/net/undipreload.c create mode 100644 gpxe/src/arch/i386/drivers/net/undirom.c create mode 100644 gpxe/src/arch/i386/firmware/pcbios/basemem.c create mode 100644 gpxe/src/arch/i386/firmware/pcbios/bios_console.c create mode 100644 gpxe/src/arch/i386/firmware/pcbios/e820mangler.S create mode 100644 gpxe/src/arch/i386/firmware/pcbios/fakee820.c create mode 100644 gpxe/src/arch/i386/firmware/pcbios/gateA20.c create mode 100644 gpxe/src/arch/i386/firmware/pcbios/hidemem.c create mode 100644 gpxe/src/arch/i386/firmware/pcbios/memmap.c create mode 100644 gpxe/src/arch/i386/firmware/pcbios/pnpbios.c create mode 100644 gpxe/src/arch/i386/image/bootsector.c create mode 100644 gpxe/src/arch/i386/image/bzimage.c create mode 100644 gpxe/src/arch/i386/image/com32.c create mode 100644 gpxe/src/arch/i386/image/comboot.c create mode 100644 gpxe/src/arch/i386/image/elfboot.c create mode 100644 gpxe/src/arch/i386/image/eltorito.c create mode 100644 gpxe/src/arch/i386/image/multiboot.c create mode 100644 gpxe/src/arch/i386/image/nbi.c create mode 100644 gpxe/src/arch/i386/image/pxe_image.c create mode 100644 gpxe/src/arch/i386/include/basemem.h create mode 100644 gpxe/src/arch/i386/include/basemem_packet.h create mode 100644 gpxe/src/arch/i386/include/bios.h create mode 100644 gpxe/src/arch/i386/include/bios_disks.h create mode 100644 gpxe/src/arch/i386/include/biosint.h create mode 100644 gpxe/src/arch/i386/include/bits/byteswap.h create mode 100644 gpxe/src/arch/i386/include/bits/compiler.h create mode 100644 gpxe/src/arch/i386/include/bits/cpu.h create mode 100644 gpxe/src/arch/i386/include/bits/eltorito.h create mode 100644 gpxe/src/arch/i386/include/bits/endian.h create mode 100644 gpxe/src/arch/i386/include/bits/errfile.h create mode 100644 gpxe/src/arch/i386/include/bits/io.h create mode 100644 gpxe/src/arch/i386/include/bits/nap.h create mode 100644 gpxe/src/arch/i386/include/bits/smbios.h create mode 100644 gpxe/src/arch/i386/include/bits/stdint.h create mode 100644 gpxe/src/arch/i386/include/bits/timer.h create mode 100644 gpxe/src/arch/i386/include/bits/uaccess.h create mode 100644 gpxe/src/arch/i386/include/bits/umalloc.h create mode 100644 gpxe/src/arch/i386/include/bochs.h create mode 100644 gpxe/src/arch/i386/include/bootsector.h create mode 100644 gpxe/src/arch/i386/include/bzimage.h create mode 100644 gpxe/src/arch/i386/include/callbacks_arch.h create mode 100644 gpxe/src/arch/i386/include/comboot.h create mode 100644 gpxe/src/arch/i386/include/fakee820.h create mode 100644 gpxe/src/arch/i386/include/gateA20.h create mode 100644 gpxe/src/arch/i386/include/gdbmach.h create mode 100644 gpxe/src/arch/i386/include/gpxe/abft.h create mode 100644 gpxe/src/arch/i386/include/gpxe/bios_nap.h create mode 100644 gpxe/src/arch/i386/include/gpxe/bios_smbios.h create mode 100644 gpxe/src/arch/i386/include/gpxe/bios_timer.h create mode 100644 gpxe/src/arch/i386/include/gpxe/ibft.h create mode 100644 gpxe/src/arch/i386/include/gpxe/memtop_umalloc.h create mode 100644 gpxe/src/arch/i386/include/gpxe/rdtsc_timer.h create mode 100644 gpxe/src/arch/i386/include/gpxe/timer2.h create mode 100644 gpxe/src/arch/i386/include/gpxe/x86_io.h create mode 100644 gpxe/src/arch/i386/include/int13.h create mode 100644 gpxe/src/arch/i386/include/kir.h create mode 100644 gpxe/src/arch/i386/include/libkir.h create mode 100755 gpxe/src/arch/i386/include/librm.h create mode 100644 gpxe/src/arch/i386/include/limits.h create mode 100644 gpxe/src/arch/i386/include/memsizes.h create mode 100644 gpxe/src/arch/i386/include/multiboot.h create mode 100644 gpxe/src/arch/i386/include/pic8259.h create mode 100644 gpxe/src/arch/i386/include/pnpbios.h create mode 100644 gpxe/src/arch/i386/include/pxe.h create mode 100644 gpxe/src/arch/i386/include/pxe_api.h create mode 100644 gpxe/src/arch/i386/include/pxe_call.h create mode 100644 gpxe/src/arch/i386/include/pxe_types.h create mode 100644 gpxe/src/arch/i386/include/realmode.h create mode 100644 gpxe/src/arch/i386/include/registers.h create mode 100644 gpxe/src/arch/i386/include/setjmp.h create mode 100644 gpxe/src/arch/i386/include/undi.h create mode 100644 gpxe/src/arch/i386/include/undiload.h create mode 100644 gpxe/src/arch/i386/include/undinet.h create mode 100644 gpxe/src/arch/i386/include/undipreload.h create mode 100644 gpxe/src/arch/i386/include/undirom.h create mode 100644 gpxe/src/arch/i386/include/vga.h create mode 100644 gpxe/src/arch/i386/interface/pcbios/abft.c create mode 100644 gpxe/src/arch/i386/interface/pcbios/aoeboot.c create mode 100644 gpxe/src/arch/i386/interface/pcbios/bios_nap.c create mode 100644 gpxe/src/arch/i386/interface/pcbios/bios_smbios.c create mode 100644 gpxe/src/arch/i386/interface/pcbios/bios_timer.c create mode 100644 gpxe/src/arch/i386/interface/pcbios/biosint.c create mode 100644 gpxe/src/arch/i386/interface/pcbios/ibft.c create mode 100644 gpxe/src/arch/i386/interface/pcbios/int13.c create mode 100644 gpxe/src/arch/i386/interface/pcbios/iscsiboot.c create mode 100644 gpxe/src/arch/i386/interface/pcbios/memtop_umalloc.c create mode 100644 gpxe/src/arch/i386/interface/pcbios/pcibios.c create mode 100644 gpxe/src/arch/i386/interface/pxe/pxe_call.c create mode 100644 gpxe/src/arch/i386/interface/pxe/pxe_entry.S create mode 100644 gpxe/src/arch/i386/interface/pxe/pxe_errors.c create mode 100644 gpxe/src/arch/i386/interface/pxe/pxe_file.c create mode 100644 gpxe/src/arch/i386/interface/pxe/pxe_loader.c create mode 100644 gpxe/src/arch/i386/interface/pxe/pxe_preboot.c create mode 100644 gpxe/src/arch/i386/interface/pxe/pxe_tftp.c create mode 100644 gpxe/src/arch/i386/interface/pxe/pxe_udp.c create mode 100644 gpxe/src/arch/i386/interface/pxe/pxe_undi.c create mode 100644 gpxe/src/arch/i386/interface/syslinux/com32_call.c create mode 100644 gpxe/src/arch/i386/interface/syslinux/com32_wrapper.S create mode 100644 gpxe/src/arch/i386/interface/syslinux/comboot_call.c create mode 100644 gpxe/src/arch/i386/interface/syslinux/comboot_resolv.c create mode 100644 gpxe/src/arch/i386/kir-Makefile create mode 100644 gpxe/src/arch/i386/prefix/bImageprefix.S create mode 100644 gpxe/src/arch/i386/prefix/bootpart.S create mode 100644 gpxe/src/arch/i386/prefix/comprefix.S create mode 100644 gpxe/src/arch/i386/prefix/dskprefix.S create mode 100644 gpxe/src/arch/i386/prefix/elf_dprefix.S create mode 100644 gpxe/src/arch/i386/prefix/elfprefix.S create mode 100755 gpxe/src/arch/i386/prefix/exeprefix.S create mode 100644 gpxe/src/arch/i386/prefix/hdprefix.S create mode 100644 gpxe/src/arch/i386/prefix/kkpxeprefix.S create mode 100644 gpxe/src/arch/i386/prefix/kpxeprefix.S create mode 100644 gpxe/src/arch/i386/prefix/libprefix.S create mode 100644 gpxe/src/arch/i386/prefix/lkrnprefix.S create mode 100644 gpxe/src/arch/i386/prefix/lmelf_dprefix.S create mode 100644 gpxe/src/arch/i386/prefix/lmelf_prefix.S create mode 100644 gpxe/src/arch/i386/prefix/mbr.S create mode 100644 gpxe/src/arch/i386/prefix/nbiprefix.S create mode 100644 gpxe/src/arch/i386/prefix/nullprefix.S create mode 100644 gpxe/src/arch/i386/prefix/pxeprefix.S create mode 100644 gpxe/src/arch/i386/prefix/romprefix.S create mode 100644 gpxe/src/arch/i386/prefix/unnrv2b.S create mode 100644 gpxe/src/arch/i386/prefix/usbdisk.S create mode 100644 gpxe/src/arch/i386/scripts/i386-kir.lds create mode 100644 gpxe/src/arch/i386/scripts/i386.lds create mode 100644 gpxe/src/arch/i386/transitions/libkir.S create mode 100644 gpxe/src/arch/i386/transitions/libpm.S create mode 100755 gpxe/src/arch/i386/transitions/librm.S create mode 100755 gpxe/src/arch/i386/transitions/librm_mgmt.c create mode 100644 gpxe/src/arch/x86/Makefile create mode 100644 gpxe/src/arch/x86/Makefile.efi create mode 100644 gpxe/src/arch/x86/core/pcidirect.c create mode 100644 gpxe/src/arch/x86/core/x86_string.c create mode 100644 gpxe/src/arch/x86/include/bits/pci_io.h create mode 100644 gpxe/src/arch/x86/include/bits/string.h create mode 100644 gpxe/src/arch/x86/include/gpxe/efi/efix86_nap.h create mode 100644 gpxe/src/arch/x86/include/gpxe/pcibios.h create mode 100644 gpxe/src/arch/x86/include/gpxe/pcidirect.h create mode 100644 gpxe/src/arch/x86/interface/efi/efix86_nap.c create mode 100644 gpxe/src/arch/x86/prefix/efidrvprefix.c create mode 100644 gpxe/src/arch/x86/prefix/efiprefix.c create mode 100644 gpxe/src/arch/x86/scripts/efi.lds create mode 100644 gpxe/src/arch/x86_64/Makefile create mode 100644 gpxe/src/arch/x86_64/Makefile.efi create mode 100644 gpxe/src/arch/x86_64/include/bits/byteswap.h create mode 100644 gpxe/src/arch/x86_64/include/bits/compiler.h create mode 100644 gpxe/src/arch/x86_64/include/bits/endian.h create mode 100644 gpxe/src/arch/x86_64/include/bits/errfile.h create mode 100644 gpxe/src/arch/x86_64/include/bits/io.h create mode 100644 gpxe/src/arch/x86_64/include/bits/nap.h create mode 100644 gpxe/src/arch/x86_64/include/bits/smbios.h create mode 100644 gpxe/src/arch/x86_64/include/bits/stdint.h create mode 100644 gpxe/src/arch/x86_64/include/bits/timer.h create mode 100644 gpxe/src/arch/x86_64/include/bits/uaccess.h create mode 100644 gpxe/src/arch/x86_64/include/bits/umalloc.h create mode 100644 gpxe/src/arch/x86_64/include/gdbmach.h create mode 100644 gpxe/src/arch/x86_64/include/limits.h create mode 100644 gpxe/src/config/.gitignore create mode 100644 gpxe/src/config/console.h create mode 100644 gpxe/src/config/defaults.h create mode 100644 gpxe/src/config/defaults/efi.h create mode 100644 gpxe/src/config/defaults/pcbios.h create mode 100644 gpxe/src/config/general.h create mode 100644 gpxe/src/config/ioapi.h create mode 100644 gpxe/src/config/isa.h create mode 100644 gpxe/src/config/nap.h create mode 100644 gpxe/src/config/serial.h create mode 100644 gpxe/src/config/timer.h create mode 100644 gpxe/src/config/umalloc.h create mode 100644 gpxe/src/core/acpi.c create mode 100644 gpxe/src/core/ansiesc.c create mode 100644 gpxe/src/core/asprintf.c create mode 100644 gpxe/src/core/base64.c create mode 100644 gpxe/src/core/basename.c create mode 100644 gpxe/src/core/bitmap.c create mode 100644 gpxe/src/core/bitops.c create mode 100644 gpxe/src/core/btext.c create mode 100644 gpxe/src/core/config.c create mode 100644 gpxe/src/core/console.c create mode 100644 gpxe/src/core/cpio.c create mode 100644 gpxe/src/core/cwuri.c create mode 100644 gpxe/src/core/debug.c create mode 100644 gpxe/src/core/device.c create mode 100644 gpxe/src/core/downloader.c create mode 100644 gpxe/src/core/errno.c create mode 100644 gpxe/src/core/exec.c create mode 100644 gpxe/src/core/filter.c create mode 100644 gpxe/src/core/gdbserial.c create mode 100644 gpxe/src/core/gdbstub.c create mode 100644 gpxe/src/core/gdbudp.c create mode 100644 gpxe/src/core/getkey.c create mode 100644 gpxe/src/core/getopt.c create mode 100644 gpxe/src/core/hw.c create mode 100644 gpxe/src/core/i82365.c create mode 100644 gpxe/src/core/image.c create mode 100644 gpxe/src/core/init.c create mode 100644 gpxe/src/core/interface.c create mode 100644 gpxe/src/core/iobuf.c create mode 100644 gpxe/src/core/job.c create mode 100644 gpxe/src/core/linebuf.c create mode 100644 gpxe/src/core/main.c create mode 100644 gpxe/src/core/malloc.c create mode 100644 gpxe/src/core/misc.c create mode 100644 gpxe/src/core/monojob.c create mode 100644 gpxe/src/core/null_nap.c create mode 100644 gpxe/src/core/nvo.c create mode 100644 gpxe/src/core/open.c create mode 100644 gpxe/src/core/pc_kbd.c create mode 100644 gpxe/src/core/pcmcia.c create mode 100644 gpxe/src/core/posix_io.c create mode 100644 gpxe/src/core/process.c create mode 100644 gpxe/src/core/proto_eth_slow.c create mode 100644 gpxe/src/core/random.c create mode 100644 gpxe/src/core/refcnt.c create mode 100644 gpxe/src/core/resolv.c create mode 100644 gpxe/src/core/serial.c create mode 100644 gpxe/src/core/serial_console.c create mode 100644 gpxe/src/core/settings.c create mode 100644 gpxe/src/core/string.c create mode 100644 gpxe/src/core/stringextra.c create mode 100644 gpxe/src/core/timer.c create mode 100644 gpxe/src/core/uri.c create mode 100644 gpxe/src/core/uuid.c create mode 100644 gpxe/src/core/vsprintf.c create mode 100644 gpxe/src/core/xfer.c create mode 100644 gpxe/src/crypto/asn1.c create mode 100644 gpxe/src/crypto/axtls/aes.c create mode 100644 gpxe/src/crypto/axtls/bigint.c create mode 100644 gpxe/src/crypto/axtls/bigint.h create mode 100644 gpxe/src/crypto/axtls/bigint_impl.h create mode 100644 gpxe/src/crypto/axtls/crypto.h create mode 100644 gpxe/src/crypto/axtls/os_port.h create mode 100644 gpxe/src/crypto/axtls/rsa.c create mode 100644 gpxe/src/crypto/axtls/sha1.c create mode 100644 gpxe/src/crypto/axtls_aes.c create mode 100644 gpxe/src/crypto/axtls_sha1.c create mode 100644 gpxe/src/crypto/cbc.c create mode 100644 gpxe/src/crypto/chap.c create mode 100644 gpxe/src/crypto/crypto_null.c create mode 100644 gpxe/src/crypto/hmac.c create mode 100644 gpxe/src/crypto/md5.c create mode 100644 gpxe/src/crypto/x509.c create mode 100644 gpxe/src/doc/build_sys.dox create mode 100644 gpxe/src/doc/pxe_extensions create mode 100644 gpxe/src/doxygen.cfg create mode 100644 gpxe/src/drivers/bitbash/bitbash.c create mode 100644 gpxe/src/drivers/bitbash/i2c_bit.c create mode 100644 gpxe/src/drivers/bitbash/spi_bit.c create mode 100644 gpxe/src/drivers/block/ata.c create mode 100644 gpxe/src/drivers/block/ramdisk.c create mode 100644 gpxe/src/drivers/block/scsi.c create mode 100644 gpxe/src/drivers/bus/eisa.c create mode 100644 gpxe/src/drivers/bus/isa.c create mode 100644 gpxe/src/drivers/bus/isa_ids.c create mode 100644 gpxe/src/drivers/bus/isapnp.c create mode 100644 gpxe/src/drivers/bus/mca.c create mode 100644 gpxe/src/drivers/bus/pci.c create mode 100644 gpxe/src/drivers/bus/pciextra.c create mode 100644 gpxe/src/drivers/bus/virtio-pci.c create mode 100644 gpxe/src/drivers/bus/virtio-ring.c create mode 100644 gpxe/src/drivers/infiniband/MT25218_PRM.h create mode 100644 gpxe/src/drivers/infiniband/MT25408_PRM.h create mode 100644 gpxe/src/drivers/infiniband/arbel.c create mode 100644 gpxe/src/drivers/infiniband/arbel.h create mode 100644 gpxe/src/drivers/infiniband/hermon.c create mode 100644 gpxe/src/drivers/infiniband/hermon.h create mode 100644 gpxe/src/drivers/infiniband/ib_packet.c create mode 100644 gpxe/src/drivers/infiniband/ib_sma.c create mode 100644 gpxe/src/drivers/infiniband/ib_smc.c create mode 100644 gpxe/src/drivers/infiniband/linda.c create mode 100644 gpxe/src/drivers/infiniband/linda.h create mode 100644 gpxe/src/drivers/infiniband/linda_fw.c create mode 100644 gpxe/src/drivers/infiniband/mlx_bitops.h create mode 100644 gpxe/src/drivers/infiniband/qib_7220_regs.h create mode 100644 gpxe/src/drivers/infiniband/qib_genbits.pl create mode 100644 gpxe/src/drivers/net/3c503.c create mode 100644 gpxe/src/drivers/net/3c509-eisa.c create mode 100644 gpxe/src/drivers/net/3c509.c create mode 100644 gpxe/src/drivers/net/3c509.h create mode 100644 gpxe/src/drivers/net/3c515.c create mode 100644 gpxe/src/drivers/net/3c515.txt create mode 100644 gpxe/src/drivers/net/3c529.c create mode 100644 gpxe/src/drivers/net/3c595.c create mode 100644 gpxe/src/drivers/net/3c595.h create mode 100644 gpxe/src/drivers/net/3c5x9.c create mode 100644 gpxe/src/drivers/net/3c90x.c create mode 100644 gpxe/src/drivers/net/3c90x.txt create mode 100644 gpxe/src/drivers/net/amd8111e.c create mode 100644 gpxe/src/drivers/net/amd8111e.h create mode 100644 gpxe/src/drivers/net/b44.c create mode 100644 gpxe/src/drivers/net/b44.h create mode 100644 gpxe/src/drivers/net/bnx2.c create mode 100644 gpxe/src/drivers/net/bnx2.h create mode 100644 gpxe/src/drivers/net/bnx2_fw.h create mode 100644 gpxe/src/drivers/net/cs89x0.c create mode 100644 gpxe/src/drivers/net/cs89x0.h create mode 100644 gpxe/src/drivers/net/cs89x0.txt create mode 100644 gpxe/src/drivers/net/davicom.c create mode 100644 gpxe/src/drivers/net/depca.c create mode 100644 gpxe/src/drivers/net/dmfe.c create mode 100644 gpxe/src/drivers/net/e1000/e1000.c create mode 100644 gpxe/src/drivers/net/e1000/e1000.h create mode 100644 gpxe/src/drivers/net/e1000/e1000_hw.c create mode 100644 gpxe/src/drivers/net/e1000/e1000_hw.h create mode 100644 gpxe/src/drivers/net/e1000/e1000_osdep.h create mode 100644 gpxe/src/drivers/net/eepro.c create mode 100644 gpxe/src/drivers/net/eepro100.c create mode 100644 gpxe/src/drivers/net/epic100.c create mode 100644 gpxe/src/drivers/net/epic100.h create mode 100644 gpxe/src/drivers/net/etherfabric.c create mode 100644 gpxe/src/drivers/net/etherfabric.h create mode 100644 gpxe/src/drivers/net/etherfabric_nic.h create mode 100644 gpxe/src/drivers/net/forcedeth.c create mode 100644 gpxe/src/drivers/net/hfa384x.h create mode 100644 gpxe/src/drivers/net/ipoib.c create mode 100644 gpxe/src/drivers/net/legacy.c create mode 100644 gpxe/src/drivers/net/mtd80x.c create mode 100644 gpxe/src/drivers/net/mtnic.c create mode 100644 gpxe/src/drivers/net/mtnic.h create mode 100644 gpxe/src/drivers/net/natsemi.c create mode 100644 gpxe/src/drivers/net/natsemi.h create mode 100644 gpxe/src/drivers/net/ne.c create mode 100644 gpxe/src/drivers/net/ne2k_isa.c create mode 100755 gpxe/src/drivers/net/ns83820.c create mode 100644 gpxe/src/drivers/net/ns8390.c create mode 100644 gpxe/src/drivers/net/ns8390.h create mode 100644 gpxe/src/drivers/net/p80211hdr.h create mode 100644 gpxe/src/drivers/net/pcnet32.c create mode 100644 gpxe/src/drivers/net/phantom/nx_bitops.h create mode 100644 gpxe/src/drivers/net/phantom/nxhal_nic_interface.h create mode 100644 gpxe/src/drivers/net/phantom/phantom.c create mode 100644 gpxe/src/drivers/net/phantom/phantom.h create mode 100644 gpxe/src/drivers/net/phantom/phantom_hw.h create mode 100644 gpxe/src/drivers/net/pnic.c create mode 100644 gpxe/src/drivers/net/pnic_api.h create mode 100644 gpxe/src/drivers/net/prism2.c create mode 100644 gpxe/src/drivers/net/prism2_pci.c create mode 100644 gpxe/src/drivers/net/prism2_plx.c create mode 100644 gpxe/src/drivers/net/r8169.c create mode 100644 gpxe/src/drivers/net/r8169.h create mode 100644 gpxe/src/drivers/net/rtl8139.c create mode 100644 gpxe/src/drivers/net/sis900.c create mode 100644 gpxe/src/drivers/net/sis900.h create mode 100644 gpxe/src/drivers/net/smc9000.c create mode 100644 gpxe/src/drivers/net/smc9000.h create mode 100644 gpxe/src/drivers/net/sundance.c create mode 100644 gpxe/src/drivers/net/tg3.c create mode 100644 gpxe/src/drivers/net/tg3.h create mode 100644 gpxe/src/drivers/net/tlan.c create mode 100644 gpxe/src/drivers/net/tlan.h create mode 100644 gpxe/src/drivers/net/tulip.c create mode 100644 gpxe/src/drivers/net/tulip.txt create mode 100644 gpxe/src/drivers/net/via-rhine.c create mode 100644 gpxe/src/drivers/net/via-velocity.c create mode 100644 gpxe/src/drivers/net/via-velocity.h create mode 100644 gpxe/src/drivers/net/virtio-net.c create mode 100644 gpxe/src/drivers/net/virtio-net.h create mode 100644 gpxe/src/drivers/net/w89c840.c create mode 100644 gpxe/src/drivers/net/wd.c create mode 100644 gpxe/src/drivers/net/wlan_compat.h create mode 100644 gpxe/src/drivers/nvs/nvs.c create mode 100644 gpxe/src/drivers/nvs/spi.c create mode 100644 gpxe/src/drivers/nvs/threewire.c create mode 100644 gpxe/src/hci/commands/autoboot_cmd.c create mode 100644 gpxe/src/hci/commands/config_cmd.c create mode 100644 gpxe/src/hci/commands/dhcp_cmd.c create mode 100644 gpxe/src/hci/commands/gdbstub_cmd.c create mode 100644 gpxe/src/hci/commands/ifmgmt_cmd.c create mode 100644 gpxe/src/hci/commands/image_cmd.c create mode 100644 gpxe/src/hci/commands/login_cmd.c create mode 100644 gpxe/src/hci/commands/nvo_cmd.c create mode 100644 gpxe/src/hci/commands/route_cmd.c create mode 100644 gpxe/src/hci/commands/sanboot_cmd.c create mode 100644 gpxe/src/hci/editstring.c create mode 100644 gpxe/src/hci/mucurses/alert.c create mode 100644 gpxe/src/hci/mucurses/ansi_screen.c create mode 100644 gpxe/src/hci/mucurses/clear.c create mode 100644 gpxe/src/hci/mucurses/colour.c create mode 100644 gpxe/src/hci/mucurses/cursor.h create mode 100644 gpxe/src/hci/mucurses/edging.c create mode 100644 gpxe/src/hci/mucurses/kb.c create mode 100644 gpxe/src/hci/mucurses/mucurses.c create mode 100644 gpxe/src/hci/mucurses/mucurses.h create mode 100644 gpxe/src/hci/mucurses/print.c create mode 100644 gpxe/src/hci/mucurses/print_nadv.c create mode 100644 gpxe/src/hci/mucurses/slk.c create mode 100644 gpxe/src/hci/mucurses/widgets/editbox.c create mode 100644 gpxe/src/hci/mucurses/winattrs.c create mode 100644 gpxe/src/hci/mucurses/windows.c create mode 100644 gpxe/src/hci/mucurses/wininit.c create mode 100644 gpxe/src/hci/readline.c create mode 100644 gpxe/src/hci/shell.c create mode 100644 gpxe/src/hci/shell_banner.c create mode 100644 gpxe/src/hci/strerror.c create mode 100644 gpxe/src/hci/tui/login_ui.c create mode 100644 gpxe/src/hci/tui/settings_ui.c create mode 100644 gpxe/src/image/efi_image.c create mode 100644 gpxe/src/image/elf.c create mode 100644 gpxe/src/image/embedded.c create mode 100644 gpxe/src/image/script.c create mode 100644 gpxe/src/image/segment.c create mode 100644 gpxe/src/include/.gitignore create mode 100644 gpxe/src/include/alloca.h create mode 100644 gpxe/src/include/assert.h create mode 100644 gpxe/src/include/big_bswap.h create mode 100644 gpxe/src/include/bootp.h create mode 100644 gpxe/src/include/btext.h create mode 100644 gpxe/src/include/byteswap.h create mode 100644 gpxe/src/include/cmdline.h create mode 100644 gpxe/src/include/cmdlinelib.h create mode 100644 gpxe/src/include/cmdlist.h create mode 100644 gpxe/src/include/coff.h create mode 100644 gpxe/src/include/compiler.h create mode 100644 gpxe/src/include/console.h create mode 100644 gpxe/src/include/cpu.h create mode 100644 gpxe/src/include/ctype.h create mode 100644 gpxe/src/include/curses.h create mode 100644 gpxe/src/include/debug.h create mode 100644 gpxe/src/include/dhcp.h create mode 100644 gpxe/src/include/elf.h create mode 100644 gpxe/src/include/endian.h create mode 100644 gpxe/src/include/errno.h create mode 100644 gpxe/src/include/etherboot.h create mode 100644 gpxe/src/include/fs.h create mode 100644 gpxe/src/include/getopt.h create mode 100644 gpxe/src/include/gpxe/acpi.h create mode 100644 gpxe/src/include/gpxe/aes.h create mode 100644 gpxe/src/include/gpxe/ansiesc.h create mode 100644 gpxe/src/include/gpxe/aoe.h create mode 100644 gpxe/src/include/gpxe/api.h create mode 100644 gpxe/src/include/gpxe/arp.h create mode 100644 gpxe/src/include/gpxe/asn1.h create mode 100644 gpxe/src/include/gpxe/ata.h create mode 100644 gpxe/src/include/gpxe/base64.h create mode 100644 gpxe/src/include/gpxe/bitbash.h create mode 100644 gpxe/src/include/gpxe/bitmap.h create mode 100644 gpxe/src/include/gpxe/bitops.h create mode 100644 gpxe/src/include/gpxe/blockdev.h create mode 100644 gpxe/src/include/gpxe/cbc.h create mode 100644 gpxe/src/include/gpxe/chap.h create mode 100644 gpxe/src/include/gpxe/command.h create mode 100644 gpxe/src/include/gpxe/cpio.h create mode 100644 gpxe/src/include/gpxe/crypto.h create mode 100644 gpxe/src/include/gpxe/device.h create mode 100644 gpxe/src/include/gpxe/dhcp.h create mode 100644 gpxe/src/include/gpxe/dhcpopts.h create mode 100644 gpxe/src/include/gpxe/dhcppkt.h create mode 100644 gpxe/src/include/gpxe/dns.h create mode 100644 gpxe/src/include/gpxe/downloader.h create mode 100644 gpxe/src/include/gpxe/editbox.h create mode 100644 gpxe/src/include/gpxe/editstring.h create mode 100644 gpxe/src/include/gpxe/efi/Base.h create mode 100644 gpxe/src/include/gpxe/efi/Guid/PcAnsi.h create mode 100644 gpxe/src/include/gpxe/efi/Guid/SmBios.h create mode 100644 gpxe/src/include/gpxe/efi/Ia32/ProcessorBind.h create mode 100644 gpxe/src/include/gpxe/efi/IndustryStandard/Pci22.h create mode 100644 gpxe/src/include/gpxe/efi/IndustryStandard/PeImage.h create mode 100644 gpxe/src/include/gpxe/efi/LICENCE create mode 100644 gpxe/src/include/gpxe/efi/Pi/PiBootMode.h create mode 100644 gpxe/src/include/gpxe/efi/Pi/PiDependency.h create mode 100644 gpxe/src/include/gpxe/efi/Pi/PiDxeCis.h create mode 100644 gpxe/src/include/gpxe/efi/Pi/PiFirmwareFile.h create mode 100644 gpxe/src/include/gpxe/efi/Pi/PiFirmwareVolume.h create mode 100644 gpxe/src/include/gpxe/efi/Pi/PiHob.h create mode 100644 gpxe/src/include/gpxe/efi/Pi/PiMultiPhase.h create mode 100644 gpxe/src/include/gpxe/efi/PiDxe.h create mode 100644 gpxe/src/include/gpxe/efi/ProcessorBind.h create mode 100644 gpxe/src/include/gpxe/efi/Protocol/ComponentName2.h create mode 100644 gpxe/src/include/gpxe/efi/Protocol/Cpu.h create mode 100644 gpxe/src/include/gpxe/efi/Protocol/CpuIo.h create mode 100644 gpxe/src/include/gpxe/efi/Protocol/DebugSupport.h create mode 100644 gpxe/src/include/gpxe/efi/Protocol/DevicePath.h create mode 100644 gpxe/src/include/gpxe/efi/Protocol/DriverBinding.h create mode 100644 gpxe/src/include/gpxe/efi/Protocol/NetworkInterfaceIdentifier.h create mode 100644 gpxe/src/include/gpxe/efi/Protocol/PciIo.h create mode 100644 gpxe/src/include/gpxe/efi/Protocol/PciRootBridgeIo.h create mode 100644 gpxe/src/include/gpxe/efi/Protocol/SimpleNetwork.h create mode 100644 gpxe/src/include/gpxe/efi/Protocol/SimpleTextIn.h create mode 100644 gpxe/src/include/gpxe/efi/Protocol/SimpleTextOut.h create mode 100644 gpxe/src/include/gpxe/efi/Uefi.h create mode 100644 gpxe/src/include/gpxe/efi/Uefi/UefiBaseType.h create mode 100644 gpxe/src/include/gpxe/efi/Uefi/UefiGpt.h create mode 100644 gpxe/src/include/gpxe/efi/Uefi/UefiInternalFormRepresentation.h create mode 100644 gpxe/src/include/gpxe/efi/Uefi/UefiMultiPhase.h create mode 100644 gpxe/src/include/gpxe/efi/Uefi/UefiPxe.h create mode 100644 gpxe/src/include/gpxe/efi/Uefi/UefiSpec.h create mode 100644 gpxe/src/include/gpxe/efi/X64/ProcessorBind.h create mode 100644 gpxe/src/include/gpxe/efi/efi.h create mode 100644 gpxe/src/include/gpxe/efi/efi_io.h create mode 100644 gpxe/src/include/gpxe/efi/efi_pci.h create mode 100644 gpxe/src/include/gpxe/efi/efi_smbios.h create mode 100644 gpxe/src/include/gpxe/efi/efi_timer.h create mode 100644 gpxe/src/include/gpxe/efi/efi_uaccess.h create mode 100644 gpxe/src/include/gpxe/efi/efi_umalloc.h create mode 100755 gpxe/src/include/gpxe/efi/import.pl create mode 100644 gpxe/src/include/gpxe/eisa.h create mode 100644 gpxe/src/include/gpxe/elf.h create mode 100644 gpxe/src/include/gpxe/errfile.h create mode 100644 gpxe/src/include/gpxe/errortab.h create mode 100644 gpxe/src/include/gpxe/ethernet.h create mode 100644 gpxe/src/include/gpxe/fakedhcp.h create mode 100644 gpxe/src/include/gpxe/features.h create mode 100644 gpxe/src/include/gpxe/filter.h create mode 100644 gpxe/src/include/gpxe/ftp.h create mode 100644 gpxe/src/include/gpxe/gdbserial.h create mode 100644 gpxe/src/include/gpxe/gdbstub.h create mode 100644 gpxe/src/include/gpxe/gdbudp.h create mode 100644 gpxe/src/include/gpxe/hidemem.h create mode 100644 gpxe/src/include/gpxe/hmac.h create mode 100644 gpxe/src/include/gpxe/http.h create mode 100644 gpxe/src/include/gpxe/i2c.h create mode 100644 gpxe/src/include/gpxe/ib_mad.h create mode 100644 gpxe/src/include/gpxe/ib_packet.h create mode 100644 gpxe/src/include/gpxe/ib_sma.h create mode 100644 gpxe/src/include/gpxe/ib_smc.h create mode 100644 gpxe/src/include/gpxe/icmp.h create mode 100644 gpxe/src/include/gpxe/icmp6.h create mode 100644 gpxe/src/include/gpxe/if_arp.h create mode 100644 gpxe/src/include/gpxe/if_ether.h create mode 100644 gpxe/src/include/gpxe/image.h create mode 100644 gpxe/src/include/gpxe/in.h create mode 100644 gpxe/src/include/gpxe/infiniband.h create mode 100644 gpxe/src/include/gpxe/init.h create mode 100644 gpxe/src/include/gpxe/interface.h create mode 100644 gpxe/src/include/gpxe/io.h create mode 100644 gpxe/src/include/gpxe/iobuf.h create mode 100644 gpxe/src/include/gpxe/ip.h create mode 100644 gpxe/src/include/gpxe/ip6.h create mode 100644 gpxe/src/include/gpxe/ipoib.h create mode 100644 gpxe/src/include/gpxe/isa.h create mode 100644 gpxe/src/include/gpxe/isa_ids.h create mode 100644 gpxe/src/include/gpxe/isapnp.h create mode 100644 gpxe/src/include/gpxe/iscsi.h create mode 100644 gpxe/src/include/gpxe/job.h create mode 100644 gpxe/src/include/gpxe/keys.h create mode 100644 gpxe/src/include/gpxe/linebuf.h create mode 100644 gpxe/src/include/gpxe/linux_compat.h create mode 100644 gpxe/src/include/gpxe/list.h create mode 100644 gpxe/src/include/gpxe/login_ui.h create mode 100644 gpxe/src/include/gpxe/malloc.h create mode 100644 gpxe/src/include/gpxe/mca.h create mode 100644 gpxe/src/include/gpxe/md5.h create mode 100644 gpxe/src/include/gpxe/memmap.h create mode 100644 gpxe/src/include/gpxe/monojob.h create mode 100644 gpxe/src/include/gpxe/nap.h create mode 100644 gpxe/src/include/gpxe/ndp.h create mode 100644 gpxe/src/include/gpxe/netdevice.h create mode 100644 gpxe/src/include/gpxe/null_nap.h create mode 100644 gpxe/src/include/gpxe/nvo.h create mode 100644 gpxe/src/include/gpxe/nvs.h create mode 100644 gpxe/src/include/gpxe/open.h create mode 100644 gpxe/src/include/gpxe/pci.h create mode 100644 gpxe/src/include/gpxe/pci_ids.h create mode 100644 gpxe/src/include/gpxe/pci_io.h create mode 100644 gpxe/src/include/gpxe/posix_io.h create mode 100644 gpxe/src/include/gpxe/process.h create mode 100644 gpxe/src/include/gpxe/profile.h create mode 100644 gpxe/src/include/gpxe/ramdisk.h create mode 100644 gpxe/src/include/gpxe/rarp.h create mode 100644 gpxe/src/include/gpxe/refcnt.h create mode 100644 gpxe/src/include/gpxe/resolv.h create mode 100644 gpxe/src/include/gpxe/retry.h create mode 100644 gpxe/src/include/gpxe/rotate.h create mode 100644 gpxe/src/include/gpxe/rsa.h create mode 100644 gpxe/src/include/gpxe/sanboot.h create mode 100644 gpxe/src/include/gpxe/scsi.h create mode 100644 gpxe/src/include/gpxe/segment.h create mode 100644 gpxe/src/include/gpxe/serial.h create mode 100644 gpxe/src/include/gpxe/settings.h create mode 100644 gpxe/src/include/gpxe/settings_ui.h create mode 100644 gpxe/src/include/gpxe/sha1.h create mode 100644 gpxe/src/include/gpxe/shell.h create mode 100644 gpxe/src/include/gpxe/shell_banner.h create mode 100644 gpxe/src/include/gpxe/smbios.h create mode 100644 gpxe/src/include/gpxe/socket.h create mode 100644 gpxe/src/include/gpxe/spi.h create mode 100644 gpxe/src/include/gpxe/spi_bit.h create mode 100644 gpxe/src/include/gpxe/tables.h create mode 100644 gpxe/src/include/gpxe/tcp.h create mode 100644 gpxe/src/include/gpxe/tcpip.h create mode 100644 gpxe/src/include/gpxe/tftp.h create mode 100644 gpxe/src/include/gpxe/threewire.h create mode 100644 gpxe/src/include/gpxe/timer.h create mode 100644 gpxe/src/include/gpxe/tls.h create mode 100644 gpxe/src/include/gpxe/uaccess.h create mode 100644 gpxe/src/include/gpxe/udp.h create mode 100644 gpxe/src/include/gpxe/umalloc.h create mode 100644 gpxe/src/include/gpxe/uri.h create mode 100644 gpxe/src/include/gpxe/uuid.h create mode 100644 gpxe/src/include/gpxe/virtio-pci.h create mode 100644 gpxe/src/include/gpxe/virtio-ring.h create mode 100644 gpxe/src/include/gpxe/vsprintf.h create mode 100644 gpxe/src/include/gpxe/x509.h create mode 100644 gpxe/src/include/gpxe/xfer.h create mode 100644 gpxe/src/include/i82365.h create mode 100644 gpxe/src/include/igmp.h create mode 100644 gpxe/src/include/lib.h create mode 100644 gpxe/src/include/libgen.h create mode 100644 gpxe/src/include/little_bswap.h create mode 100644 gpxe/src/include/mii.h create mode 100644 gpxe/src/include/nfs.h create mode 100644 gpxe/src/include/nic.h create mode 100644 gpxe/src/include/nmb.h create mode 100644 gpxe/src/include/old_tcp.h create mode 100644 gpxe/src/include/pc_kbd.h create mode 100644 gpxe/src/include/pcmcia-opts.h create mode 100644 gpxe/src/include/pcmcia.h create mode 100644 gpxe/src/include/readline/readline.h create mode 100644 gpxe/src/include/stdarg.h create mode 100644 gpxe/src/include/stddef.h create mode 100644 gpxe/src/include/stdint.h create mode 100644 gpxe/src/include/stdio.h create mode 100644 gpxe/src/include/stdlib.h create mode 100644 gpxe/src/include/string.h create mode 100644 gpxe/src/include/strings.h create mode 100644 gpxe/src/include/sys/time.h create mode 100644 gpxe/src/include/sys_info.h create mode 100644 gpxe/src/include/time.h create mode 100644 gpxe/src/include/unistd.h create mode 100644 gpxe/src/include/usr/autoboot.h create mode 100644 gpxe/src/include/usr/dhcpmgmt.h create mode 100644 gpxe/src/include/usr/ifmgmt.h create mode 100644 gpxe/src/include/usr/imgmgmt.h create mode 100644 gpxe/src/include/usr/route.h create mode 100644 gpxe/src/interface/efi/efi_console.c create mode 100644 gpxe/src/interface/efi/efi_init.c create mode 100644 gpxe/src/interface/efi/efi_io.c create mode 100644 gpxe/src/interface/efi/efi_pci.c create mode 100644 gpxe/src/interface/efi/efi_smbios.c create mode 100644 gpxe/src/interface/efi/efi_snp.c create mode 100644 gpxe/src/interface/efi/efi_strerror.c create mode 100644 gpxe/src/interface/efi/efi_timer.c create mode 100644 gpxe/src/interface/efi/efi_uaccess.c create mode 100644 gpxe/src/interface/efi/efi_umalloc.c create mode 100644 gpxe/src/interface/smbios/smbios.c create mode 100644 gpxe/src/interface/smbios/smbios_settings.c create mode 100644 gpxe/src/libgcc/__divdi3.c create mode 100644 gpxe/src/libgcc/__moddi3.c create mode 100644 gpxe/src/libgcc/__udivdi3.c create mode 100644 gpxe/src/libgcc/__udivmoddi4.c create mode 100644 gpxe/src/libgcc/__umoddi3.c create mode 100644 gpxe/src/libgcc/libgcc.h create mode 100644 gpxe/src/libgcc/memcpy.c create mode 100644 gpxe/src/net/aoe.c create mode 100644 gpxe/src/net/arp.c create mode 100644 gpxe/src/net/dhcpopts.c create mode 100644 gpxe/src/net/dhcppkt.c create mode 100644 gpxe/src/net/ethernet.c create mode 100644 gpxe/src/net/fakedhcp.c create mode 100644 gpxe/src/net/icmp.c create mode 100644 gpxe/src/net/icmpv6.c create mode 100644 gpxe/src/net/infiniband.c create mode 100644 gpxe/src/net/iobpad.c create mode 100644 gpxe/src/net/ipv4.c create mode 100644 gpxe/src/net/ipv6.c create mode 100644 gpxe/src/net/ndp.c create mode 100644 gpxe/src/net/netdev_settings.c create mode 100644 gpxe/src/net/netdevice.c create mode 100644 gpxe/src/net/nullnet.c create mode 100644 gpxe/src/net/rarp.c create mode 100644 gpxe/src/net/retry.c create mode 100644 gpxe/src/net/tcp.c create mode 100644 gpxe/src/net/tcp/ftp.c create mode 100644 gpxe/src/net/tcp/http.c create mode 100644 gpxe/src/net/tcp/https.c create mode 100644 gpxe/src/net/tcp/iscsi.c create mode 100644 gpxe/src/net/tcpip.c create mode 100644 gpxe/src/net/tls.c create mode 100644 gpxe/src/net/udp.c create mode 100644 gpxe/src/net/udp/dhcp.c create mode 100644 gpxe/src/net/udp/dns.c create mode 100644 gpxe/src/net/udp/slam.c create mode 100644 gpxe/src/net/udp/tftp.c create mode 100644 gpxe/src/proto/fsp.c create mode 100644 gpxe/src/proto/igmp.c create mode 100644 gpxe/src/proto/nfs.c create mode 100644 gpxe/src/proto/nmb.c create mode 100644 gpxe/src/tests/comboot/shuffle-simple.asm create mode 100644 gpxe/src/tests/comboot/version.asm create mode 100644 gpxe/src/tests/gdbstub_test.S create mode 100755 gpxe/src/tests/gdbstub_test.gdb create mode 100644 gpxe/src/tests/linebuf_test.c create mode 100644 gpxe/src/tests/memcpy_test.c create mode 100644 gpxe/src/tests/umalloc_test.c create mode 100644 gpxe/src/tests/uri_test.c create mode 100644 gpxe/src/usr/autoboot.c create mode 100644 gpxe/src/usr/dhcpmgmt.c create mode 100644 gpxe/src/usr/ifmgmt.c create mode 100644 gpxe/src/usr/imgmgmt.c create mode 100644 gpxe/src/usr/pxemenu.c create mode 100644 gpxe/src/usr/route.c create mode 100644 gpxe/src/util/.gitignore create mode 100644 gpxe/src/util/Makefile create mode 100644 gpxe/src/util/Option/ROM.pm create mode 100755 gpxe/src/util/catrom.pl create mode 100755 gpxe/src/util/disrom.pl create mode 100755 gpxe/src/util/dskpad.pl create mode 100644 gpxe/src/util/efirom.c create mode 100644 gpxe/src/util/elf2efi.c create mode 100755 gpxe/src/util/geniso create mode 100755 gpxe/src/util/genliso create mode 100755 gpxe/src/util/get-pci-ids create mode 100644 gpxe/src/util/hijack.c create mode 100755 gpxe/src/util/makerom.pl create mode 100755 gpxe/src/util/mergerom.pl create mode 100755 gpxe/src/util/modrom.pl create mode 100644 gpxe/src/util/mucurses_test.c create mode 100644 gpxe/src/util/nrv2b.c create mode 100644 gpxe/src/util/parserom.pl create mode 100755 gpxe/src/util/sortobjdump.pl create mode 100755 gpxe/src/util/swapdevids.pl create mode 100755 gpxe/src/util/symcheck.pl create mode 100644 gpxe/src/util/zbin.c create mode 100644 libfat/cache.c create mode 100644 libfat/fat.h create mode 100644 libfat/fatchain.c create mode 100644 libfat/libfat.h create mode 100644 libfat/libfatint.h create mode 100644 libfat/open.c create mode 100644 libfat/searchdir.c create mode 100644 libfat/ulint.h create mode 100644 libinstaller/Makefile create mode 120000 libinstaller/advconst.h create mode 100755 libinstaller/bin2c.pl create mode 100644 libinstaller/setadv.c create mode 100644 libinstaller/syslinux.h create mode 100644 libinstaller/syslxint.h create mode 100644 libinstaller/syslxmod.c create mode 100644 linux/Makefile create mode 100755 linux/syslinux create mode 100755 linux/syslinux-nomtools create mode 100644 linux/syslinux.c create mode 100644 man/extlinux.1 create mode 100644 man/gethostip.1 create mode 100644 man/lss16toppm.1 create mode 100644 man/ppmtolss16.1 create mode 100644 man/syslinux.1 create mode 100644 man/syslinux2ansi.1 create mode 100644 mbr/Makefile create mode 100644 mbr/adjust.h create mode 100644 mbr/altmbr.S create mode 100644 mbr/altmbr.bin create mode 100644 mbr/altmbr_c.bin create mode 100644 mbr/altmbr_f.bin create mode 100755 mbr/checksize.pl create mode 100644 mbr/gptmbr.S create mode 100644 mbr/gptmbr.bin create mode 100644 mbr/gptmbr_c.bin create mode 100644 mbr/gptmbr_f.bin create mode 100644 mbr/isohdpfx.S create mode 100644 mbr/isohdpfx.bin create mode 100644 mbr/isohdpfx_c.bin create mode 100644 mbr/isohdpfx_f.bin create mode 100644 mbr/isohdppx.S create mode 100644 mbr/isohdppx.bin create mode 100644 mbr/isohdppx_c.bin create mode 100644 mbr/isohdppx_f.bin create mode 100644 mbr/mbr.S create mode 100644 mbr/mbr.bin create mode 100644 mbr/mbr.ld create mode 100644 mbr/mbr_c.bin create mode 100644 mbr/mbr_f.bin create mode 100644 mbr/oldmbr.asm create mode 100644 memdisk/Makefile create mode 100644 memdisk/conio.c create mode 100644 memdisk/conio.h create mode 100644 memdisk/e820.h create mode 100644 memdisk/e820data create mode 100644 memdisk/e820func.c create mode 100644 memdisk/e820test.c create mode 100755 memdisk/fdgeo.pl create mode 100644 memdisk/inflate.c create mode 100644 memdisk/memcpy.S create mode 100644 memdisk/memdisk create mode 100644 memdisk/memdisk.h create mode 100644 memdisk/memdisk.inc create mode 100644 memdisk/memdisk.ld create mode 100644 memdisk/memdisk16.asm create mode 100644 memdisk/memdisk_chs.asm create mode 100644 memdisk/memdisk_edd.asm create mode 100644 memdisk/memset.S create mode 100644 memdisk/msetup.c create mode 100755 memdisk/postprocess.pl create mode 100644 memdisk/setup.c create mode 100644 memdisk/start32.S create mode 100644 memdisk/testdata1 create mode 100644 memdisk/testdata2 create mode 100644 memdisk/testdata3 create mode 100644 memdisk/unzip.c create mode 100644 memdisk/version.h create mode 100644 memdump/Makefile create mode 100644 memdump/README create mode 100644 memdump/__divdi3.c create mode 100644 memdump/__udivmoddi4.c create mode 100644 memdump/argv.c create mode 100644 memdump/code16.h create mode 100644 memdump/com16.ld create mode 100644 memdump/conio.c create mode 100644 memdump/crt0.S create mode 100644 memdump/errno.h create mode 100644 memdump/inttypes.h create mode 100644 memdump/io.h create mode 100644 memdump/main.c create mode 100644 memdump/malloc.h create mode 100644 memdump/memcpy.S create mode 100755 memdump/memdump.com create mode 100644 memdump/memset.S create mode 100644 memdump/mystuff.h create mode 100644 memdump/printf.c create mode 100644 memdump/serial.c create mode 100644 memdump/skipatou.c create mode 100644 memdump/stdint.h create mode 100644 memdump/stdio.h create mode 100644 memdump/stdlib.h create mode 100644 memdump/string.h create mode 100644 memdump/strtoul.c create mode 100644 memdump/ymodem.txt create mode 100644 memdump/ymsend.c create mode 100644 memdump/ymsend.h create mode 100644 mime/image/x-lss16.xml create mode 100644 modules/Makefile create mode 100644 modules/gfxboot.asm create mode 100644 modules/gfxboot.com create mode 100644 modules/poweroff.asm create mode 100644 modules/poweroff.com create mode 100644 modules/pxechain.asm create mode 100644 modules/pxechain.com create mode 100644 mtools/Makefile create mode 100755 mtools/syslinux create mode 100644 mtools/syslinux.c create mode 100644 now.pl create mode 100644 sample/Makefile create mode 100644 sample/README create mode 100644 sample/atou.c create mode 100644 sample/c32echo.c create mode 100755 sample/c32echo.c32 create mode 100644 sample/c32entry.S create mode 100644 sample/c32exit.S create mode 100644 sample/comecho.asm create mode 100644 sample/comecho.com create mode 100644 sample/conio.c create mode 100644 sample/fd.c create mode 100755 sample/fd.c32 create mode 100644 sample/filetest.c create mode 100755 sample/filetest.c32 create mode 100644 sample/hello.c create mode 100755 sample/hello.c32 create mode 100644 sample/hello2.c create mode 100755 sample/hello2.c32 create mode 100644 sample/m16-640x640-syslinux.jpg create mode 100644 sample/mdiskchk.c create mode 100755 sample/mdiskchk.com create mode 100644 sample/printf.c create mode 100644 sample/sample.msg create mode 100644 sample/skipatou.c create mode 100644 sample/syslinux_splash.jpg create mode 100644 sample/syslogo.lss create mode 100644 sample/syslogo.ppm.gz create mode 100644 syslinux-3.82.tar.gz create mode 100644 syslinux.spec create mode 100644 utils/Makefile create mode 100644 utils/bin2hex.pl create mode 100755 utils/gethostip create mode 100644 utils/gethostip.c create mode 100755 utils/isohybrid create mode 100644 utils/isohybrid.in create mode 100755 utils/keytab-lilo create mode 100755 utils/lss16toppm create mode 100755 utils/md5pass create mode 100755 utils/mkdiskimage create mode 100644 utils/mkdiskimage.in create mode 100755 utils/ppmtolss16 create mode 100755 utils/sha1pass create mode 100755 utils/syslinux2ansi create mode 100644 version create mode 100644 version.gen create mode 100644 version.h create mode 100644 version.mk create mode 100755 version.pl create mode 100644 win32/Makefile create mode 100644 win32/README create mode 100755 win32/find-mingw.sh create mode 100644 win32/hello.c create mode 100644 win32/syslinux.c create mode 100755 win32/syslinux.exe diff --git a/BUGS b/BUGS new file mode 100644 index 0000000..9639da8 --- /dev/null +++ b/BUGS @@ -0,0 +1,4 @@ +Known bugs that have not yet been fixed: + +- PXELINUX: Some PXE stacks fail with routing enabled, some with + routing disabled. Try both? diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..60549be --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + 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 + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/MCONFIG b/MCONFIG new file mode 100644 index 0000000..daeb9d4 --- /dev/null +++ b/MCONFIG @@ -0,0 +1,63 @@ +## -*- makefile -*- ------------------------------------------------------- +## +## Copyright 2008 H. Peter Anvin - 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, Inc., 51 Franklin St, Fifth Floor, +## Boston MA 02110-1301, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +## +## Common configurables +## + +# No builtin rules +MAKEFLAGS += -r +MAKE += -r + +BINDIR = /usr/bin +SBINDIR = /sbin +LIBDIR = /usr/lib +DATADIR = /usr/share +AUXDIR = $(DATADIR)/syslinux +MANDIR = /usr/man +INCDIR = /usr/include +TFTPBOOT = /tftpboot +COM32DIR = $(AUXDIR)/com32 + +BOOTDIR = /boot +EXTLINUXDIR = $(BOOTDIR)/extlinux + +NASM = nasm +NASMOPT = -O9999 + +PERL = perl + +CHMOD = chmod + +CC = gcc +gcc_ok = $(shell tmpf=gcc_ok.$$$$.tmp; \ + if $(CC) $(1) -c $(topdir)/dummy.c -o $$tmpf 2>/dev/null ; \ + then echo '$(1)'; else echo '$(2)'; fi; \ + rm -f $$tmpf) + +LD = ld +OBJDUMP = objdump +OBJCOPY = objcopy +AR = ar +NM = nm +RANLIB = ranlib +GZIPPROG = gzip +PNGTOPNM = pngtopnm + +com32 = $(topdir)/com32 + +# Common stanza to make gcc generate .*.d dependency files +MAKEDEPS = -Wp,-MT,$@,-MD,$(dir $@).$(notdir $@).d + +# Dependencies that exclude system headers; use whenever we use +# header files from the platform. +UMAKEDEPS = -Wp,-MT,$@,-MMD,$(dir $@).$(notdir $@).d diff --git a/MCONFIG.embedded b/MCONFIG.embedded new file mode 100644 index 0000000..586afc3 --- /dev/null +++ b/MCONFIG.embedded @@ -0,0 +1,45 @@ +## -*- makefile -*- ------------------------------------------------------ +## +## Copyright 2001-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +## +## Make configuration for embedded directories +## + +include $(topdir)/MCONFIG + +GCCOPT := $(call gcc_ok,-m32,) \ + $(call gcc_ok,-ffreestanding,) \ + $(call gcc_ok,-fno-stack-protector,) \ + $(call gcc_ok,-falign-functions=0,-malign-functions=0) \ + $(call gcc_ok,-falign-jumps=0,-malign-jumps=0) \ + $(call gcc_ok,-falign-loops=0,-malign-loops=0) \ + -march=i386 -Os -fomit-frame-pointer -mregparm=3 -DREGPARM=3 \ + -msoft-float + +LIBGCC := $(shell $(CC) $(GCCOPT) --print-libgcc) + +LD += -m elf_i386 +CFLAGS = $(GCCOPT) -g -W -Wall -Wno-sign-compare $(OPTFLAGS) $(INCLUDES) +SFLAGS = $(CFLAGS) -D__ASSEMBLY__ + +.SUFFIXES: .c .o .S .s .i .elf .com .bin .asm .lst .c32 .lss + +%.o: %.c + $(CC) $(MAKEDEPS) $(CFLAGS) -c -o $@ $< +%.i: %.c + $(CC) $(MAKEDEPS) $(CFLAGS) -E -o $@ $< +%.s: %.c + $(CC) $(MAKEDEPS) $(CFLAGS) -S -o $@ $< +%.o: %.S + $(CC) $(MAKEDEPS) $(SFLAGS) -Wa,-a=$*.lst -c -o $@ $< +%.s: %.S + $(CC) $(MAKEDEPS) $(SFLAGS) -E -o $@ $< diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..76abff5 --- /dev/null +++ b/Makefile @@ -0,0 +1,164 @@ +## ----------------------------------------------------------------------- +## +## Copyright 1998-2009 H. Peter Anvin - All Rights Reserved +## Copyright 2009 Intel Corporation; author: H. Peter Anvin +## +## 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +# +# Main Makefile for SYSLINUX +# +topdir = . +include $(topdir)/MCONFIG +-include $(topdir)/version.mk + +# +# The BTARGET refers to objects that are derived from ldlinux.asm; we +# like to keep those uniform for debugging reasons; however, distributors +# want to recompile the installers (ITARGET). +# +# BOBJECTS and IOBJECTS are the same thing, except used for +# installation, so they include objects that may be in subdirectories +# with their own Makefiles. Finally, there is a list of those +# directories. +# + +# List of module objects that should be installed for all derivatives +MODULES = memdisk/memdisk memdump/memdump.com modules/*.com \ + com32/menu/*.c32 com32/modules/*.c32 com32/mboot/*.c32 \ + com32/hdt/*.c32 com32/rosh/*.c32 + +# syslinux.exe is BTARGET so as to not require everyone to have the +# mingw suite installed +BTARGET = version.gen version.h version.mk +BOBJECTS = $(BTARGET) \ + mbr/mbr.bin mbr/altmbr.bin mbr/gptmbr.bin \ + mbr/mbr_c.bin mbr/altmbr_c.bin mbr/gptmbr_c.bin \ + mbr/mbr_f.bin mbr/altmbr_f.bin mbr/gptmbr_f.bin \ + core/pxelinux.0 core/isolinux.bin core/isolinux-debug.bin \ + gpxe/gpxelinux.0 dos/syslinux.com win32/syslinux.exe \ + $(MODULES) + +# BSUBDIRs build the on-target binary components. +# ISUBDIRs build the installer (host) components. +# +# Note: libinstaller is both a BSUBDIR and an ISUBDIR. It contains +# files that depend only on the B phase, but may have to be regenerated +# for "make installer". +BSUBDIRS = codepage core memdisk modules com32 mbr memdump gpxe sample \ + libinstaller dos win32 +ITARGET = +IOBJECTS = $(ITARGET) dos/copybs.com \ + utils/gethostip utils/isohybrid utils/mkdiskimage \ + mtools/syslinux linux/syslinux extlinux/extlinux +ISUBDIRS = libinstaller mtools linux extlinux utils + +# Things to install in /usr/bin +INSTALL_BIN = mtools/syslinux +# Things to install in /sbin +INSTALL_SBIN = extlinux/extlinux +# Things to install in /usr/lib/syslinux +INSTALL_AUX = core/pxelinux.0 gpxe/gpxelinux.0 core/isolinux.bin \ + core/isolinux-debug.bin \ + dos/syslinux.com dos/copybs.com win32/syslinux.exe \ + mbr/*.bin $(MODULES) +INSTALL_AUX_OPT = win32/syslinux.exe + +# These directories manage their own installables +INSTALLSUBDIRS = com32 utils + +# Things to install in /boot/extlinux +EXTBOOTINSTALL = $(MODULES) + +# Things to install in /tftpboot +NETINSTALLABLE = core/pxelinux.0 gpxe/gpxelinux.0 \ + $(MODULES) + +all: + $(MAKE) all-local + set -e ; for i in $(BSUBDIRS) $(ISUBDIRS) ; do $(MAKE) -C $$i $@ ; done + -ls -l $(BOBJECTS) $(IOBJECTS) + +all-local: $(BTARGET) $(ITARGET) + +installer: + $(MAKE) installer-local + set -e ; for i in $(ISUBDIRS); do $(MAKE) -C $$i all ; done + -ls -l $(BOBJECTS) $(IOBJECTS) + +installer-local: $(ITARGET) $(BINFILES) + +version.gen: version version.pl + $(PERL) version.pl $< $@ '%define < @' +version.h: version version.pl + $(PERL) version.pl $< $@ '#define < @' +version.mk: version version.pl + $(PERL) version.pl $< $@ '< := @' + +local-install: installer + mkdir -m 755 -p $(INSTALLROOT)$(BINDIR) + install -m 755 -c $(INSTALL_BIN) $(INSTALLROOT)$(BINDIR) + mkdir -m 755 -p $(INSTALLROOT)$(SBINDIR) + install -m 755 -c $(INSTALL_SBIN) $(INSTALLROOT)$(SBINDIR) + mkdir -m 755 -p $(INSTALLROOT)$(AUXDIR) + install -m 644 -c $(INSTALL_AUX) $(INSTALLROOT)$(AUXDIR) + -install -m 644 -c $(INSTALL_AUX_OPT) $(INSTALLROOT)$(AUXDIR) + mkdir -m 755 -p $(INSTALLROOT)$(MANDIR)/man1 + install -m 644 -c man/*.1 $(INSTALLROOT)$(MANDIR)/man1 + : mkdir -m 755 -p $(INSTALLROOT)$(MANDIR)/man8 + : install -m 644 -c man/*.8 $(INSTALLROOT)$(MANDIR)/man8 + +install: local-install + set -e ; for i in $(INSTALLSUBDIRS) ; do $(MAKE) -C $$i $@ ; done + +netinstall: installer + mkdir -p $(INSTALLROOT)$(TFTPBOOT) + install -m 644 $(NETINSTALLABLE) $(INSTALLROOT)$(TFTPBOOT) + +extbootinstall: installer + mkdir -m 755 -p $(INSTALLROOT)$(EXTLINUXDIR) + install -m 644 $(EXTBOOTINSTALL) $(INSTALLROOT)$(EXTLINUXDIR) + +install-all: install netinstall extbootinstall + +local-tidy: + rm -f *.o *.elf *_bin.c stupid.* patch.offset + rm -f *.lsr *.lst *.map *.sec *.tmp + rm -f $(OBSOLETE) + +tidy: local-tidy + set -e ; for i in $(BESUBDIRS) $(IESUBDIRS) $(BSUBDIRS) $(ISUBDIRS) ; do $(MAKE) -C $$i $@ ; done + +local-clean: + rm -f $(ITARGET) + +clean: local-tidy local-clean + set -e ; for i in $(BESUBDIRS) $(IESUBDIRS) $(BSUBDIRS) $(ISUBDIRS) ; do $(MAKE) -C $$i $@ ; done + +local-dist: + find . \( -name '*~' -o -name '#*' -o -name core \ + -o -name '.*.d' -o -name .depend \) -type f -print0 \ + | xargs -0rt rm -f + +dist: local-dist local-tidy + set -e ; for i in $(BESUBDIRS) $(IESUBDIRS) $(BSUBDIRS) $(ISUBDIRS) ; do $(MAKE) -C $$i $@ ; done + +local-spotless: + rm -f $(BTARGET) .depend *.so.* + +spotless: local-clean local-dist local-spotless + set -e ; for i in $(BESUBDIRS) $(IESUBDIRS) $(BSUBDIRS) $(ISUBDIRS) ; do $(MAKE) -C $$i $@ ; done + +# Shortcut to build linux/syslinux using klibc +klibc: + $(MAKE) clean + $(MAKE) CC=klcc ITARGET= ISUBDIRS='linux extlinux' BSUBDIRS= + +# Hook to add private Makefile targets for the maintainer. +-include Makefile.private diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..5a18525 --- /dev/null +++ b/NEWS @@ -0,0 +1,1260 @@ +Starting with 1.47, changes marked with SYSLINUX, PXELINUX, ISOLINUX +or EXTLINUX apply to that specific program only; other changes apply +to all derivatives. + +Changes in 3.82: + * isohybrid: fix the -partok logic for loading from a partition. + * ISOLINUX: deal with systems which return from INT 13h with + interrupts disabled. + * Do not invoke the idle handler during large file loads. + * Simple menu: make ONTIMEOUT work with MENU HIDDEN. + * PXELINUX: handle TFTP servers which have extra NULs at the + end of an OACK packet. + +Changes in 3.81: + * Shuffler: fix bug in real-mode entry. This affected a + number of modules, probably in relatively unimportant ways, + but it completely broke linux.c32. + * Improved performance. + * Attempt to halt the processor while idle. This can cause + bad reponsiveness when using a serial console especially for + automated input; if that ends up being a problem, use the + new "NOHALT 1" configuration command. + * linux.c32 now suppresses all messages if the "quiet" flag is + specified. + * isohybrid: add a variety of options, and a help message. + * ISOLINUX: fix booting in hybrid mode when CBIOS is used. + This unfortunately means that the isohybrid handoff protocol + has changed, so the isohybrid utility must version-match + isolinux.bin. + * Drop support for ACPI 3 extended memory flags. + * Menu system: don't set the autocr flag on the serial + console. + * altmbr: fix handling of logical partitions. + * altmbr: cap at 439 bytes so the partition select byte isn't + part of the file. + +Changes in 3.80: + * New shuffler mechanism and API. + * Rewritten mboot.c32 module. + * The syslinux_boot_linux() function has been simplified. + * Don't hang trying to boot a "menu quit" label from the CLI. + * Fix problem with boot-once "sticking" on some BIOSes. + * isohybrid: fix problem with images over 2 GB in size. + * APM poweroff module (poweroff.com) by Sebastian Herbszt. + * ISOLINUX: fix the handling of large directories. Bug found + and fixed by Steffen Winterfeldt. + +Changes in 3.75: + * PXELINUX: fix the "keeppxe" option, which was broken in + 3.74. + * MEMDISK: correct the extraction of geometry information from + the MBR of a hard disk image, again broken in 3.74. + * extlinux(1) man page from Brian Pellin. + * Simple menu: MENU SAVE is now controllable on a menu-by-menu + or entry-by-entry basis. + * gPXELINUX: fix interrupt-disabling bug. + * HDT: fix lockup on machines with certain PCI configurations. + +Changes in 3.74: + * New UI directive, which allows a more natural way to specify + a menu system (or not.) With the UI directive specifying + the menu system, the DEFAULT directive can be used to select + the default entry inside the menus. + * kbdmap.c32: new module to load a new keyboard map + dynamically. + * isohybrid: workaround bug in some versions of binutils. + * Fix issue with the placement of the initrd on some machines. + * ifcpu64: fix handling of less than three arguments. + * Fix bug in the shuffle library when dealing with a very + large number of fragments. + * Documentation fixes by Vicente Jimenez Aguilar. + * gPXE updated to version 0.9.7. + * hdt.c32: Hardware Detection Tool, an interactive hardware + analyzer module by Erwan Velu. + * MEMDISK: enable automatic determination of the disk geometry + for a large floppy disk image if (and only if) it is + formatted with a FAT filesystem. + * SYSLINUX: fix the handling of .bss files on FAT12/16. + * Suppress the Loading ... message if "quiet" is specified on + the kernel command line. + * Fix the use of "CONSOLE 0" with menu.c32. + * Allow COM32 modules to be aware of all memory even in the + presence of a memory hole. The "linux.c32" module can be + used to load a kernel (or memdisk) plus large initrd on + such a system. + * MBR: produce alternate MBR variants which force the drive + number to hd0 (_f variants), or force the drive number to + hd0 if the Ctrl key is pressed (_c variants.) Furthermore, + add an MBR variant (altmbr*.bin) which ignores the active + flag and instead boots the partition number specified in the + byte at offset 439 decimal. + * Add IPAPPEND strings to com32 modules, especially needed for + linux.c32. + * New MENU SAVE directive which saves the latest menu + selection until the next boot. Currently only implemented for + EXTLINUX. + * gfxboot.com: *experimental* interface module to Steffen + Winterfeldt's "gfxboot" graphical front end + (http://gfxboot.sourceforge.net/). Module by Sebastian Herbszt. + +Changes in 3.73: + * Upgrade gPXE to release version 0.9.5. + * Fix a number of build errors on various platforms. + * Handle systems with E820 "extended attributes" per ACPI 3. + Someone "cleverly" decided to change the E820 spec in a + backwards-incompatible manner! + * MEMDISK: default to "safeint". + * Adopt the moniker "The Syslinux Project", standard proper + noun capitalization, to refer to the project as a whole. + Thus, reserve the all-caps "SYSLINUX" to refer to the FAT + loader. + * mboot.c32: add "-solaris" option to pass DHCP information to + the Solaris kernel; required for automatic Solaris boot + without using Solaris' pxeboot program. + * config.c32: trivial COM32 module to restart Syslinux with + another configuration file from the command line (equivalent + to the CONFIG command in the configuratin file.) + +Changes in 3.72: + * Include the pxechain.com module from Jeffery Hutzelman at + Carnegie Mellon University. This allows chaining another + PXE boot program while changing the DHCP packet passed to + it. + * Reorganize the Makefile system. + * Major PCI core cleanups and other source cleanup. + * gPXE code updated. + * Try to avoid memory-snooping attacks on passwords. Note + that if someone has root on the box, they generally don't + need to compromise the boot loader... + * ISOLINUX: fix crash when given a zero-length file. + * sdi.c32: support gzipped SDI images. + * ISOLINUX: support generating images which can be either + a CD-ROM or a hard disk (USB disk, etc.) See + doc/isolinux.txt for more information. + * Remote gdb support for COM32 modules; patch from Stefan + Hajnoczi. + * Support beeps in F-key help in the simple menu system. + * Tab display of labels, based on a patch from Sebastian + Herbszt. Can be disabled with the NOCOMPLETE configuration + command. + * "menu default" can now be specified after "menu begin", to + indicate that a specific submenu should be the default entry. + +Changes in 3.71: + * Workaround for a VESA BIOS which tries to make DOS system + calls(!!) + * Simple menu: fix navigation around disabled entries + (or at least try to...) + * EXTLINUX: proper error message when confused about mount + point. + * MEMDISK: be smarter about incompletely disabled floppies in + the BIOS and about being the only floppy. + * Optionally allow initrd to be specified on a separate line + rather than as part of the "append" line. This is not + recommended, but apparently makes life easier for some + tools. + * SYSLINUX: if no config file is present, set the current + directory to the root directory (Sebastian Herbszt). + * chain.c32: option "hide" to support hiding and unhiding of + primary partitions on the boot drive with DOS, Win, or OS/2 + partition types (01, 04, 06, 07, 0b, 0c, 0e). + * Unbreak the KBDMAP command (broken in 3.70). + * EXTLINUX: fix the handling of the ADV when using CBIOS. + * ifcpu64.c32: simple COM32 module to select a 32- or 64-bit + kernel (and optionally 32-bit kernels with or without PAE.) + Eventually we want a scripting language for this + kind of stuff; a Lua module is under development. + * Fix parsing of the SERIAL command without a baud rate + specified. + * chain.c32: error out when try to boot an unbootable + partition. + * SYSLINUX: when building the Win32 installer, search for + MinGW under a large number of possible names. + +Changes in 3.70: + * PXELINUX: Support enhanced capabilities when running on top + of gPXE (http://www.etherboot.org/). In particular, support + URL-style syntax for filenames, and any protocol that gPXE + supports (except, currently, iSCSI and AoE.) This feature + is currently highly experimental. + * Substantial infrastructure changes to support files whose + length aren't known at open time (typically network + connections.) Please note that the semantics of some of the + comboot APIs have changed slightly; please see doc/comboot.txt. + * PXELINUX: We no longer require a TFTP server which supports + the tsize option for all transfers. + * PXELINUX: Integrate with the gPXE source base; unified image + now included as "gpxelinux.0". + * The source tree has been restructured; files that were + previously in the root have moved into the core, dos, gpxe, + and utils directories. + * "make install", "make netinstall", and "make extbootinstall" + have been updated massively. "make install-all" now installs + all three. + * Change default dir for auxiliary files from + /usr/lib/syslinux to /usr/share/syslinux. + * SYSLINUX: VFAT long filename support. + * MEMDISK: Any image less than 4096K (4 MB) is treated as a + floppy disk. The geometry-guessing code will recognize all + common extended formats, but it is still possible some very + exotic formats need geometry specification. Large floppies + and very small harddisks still need explicit specification. + * chain.c32: option "swap" to support swapping of BIOS drive + numbers. This is necessary to boot certain operating systems + (DOS, Windows) from a secondary drive. + * chain.c32: option "file=" to support loading a boot file from + the SYSLINUX filesystem instead of loading the boot sector + from the drive. + * chain.c32: option "seg=" to control the load location. + * chain.c32: option "ntldr=" as a shorthand for "seg=0x2000 + file="; use this to load one of WinNT's loaders: + + chain.c32 hd0 1 ntldr=/MiniNT/setupldr.bin + + Note that the file needs to be in the SYSLINUX filesystem. + * chain.32: options "freedos=" and "msdos="/"pcdos=" as + shorthands for "seg=0x60 file=" and "seg=0x70 file=" + respectively; use this to load FreeDOS's kernel.sys, MS-DOS's + io.sys or PC-DOS's ibmbio.sys. + * Change to the A20 algorithm which *MIGHT* help systems that + have systems which freeze when Syslinux is used with USB + keyboards. Note that this has been hard do verify, so I + would greatly appreciate feedback on it. + * Complex menu system: unbreak menus which has unnamed + submenus, like complex.c. + * Fix newline on the serial port for some com32 modules. + * chain.c32: support "boot" as the drive specification, + indicating the drive from which it was booted + (for syslinux/extlinux). + * SYSLINUX/EXTLINUX: support "localboot" with the same feature + set as ISOLINUX. + * Add an experimental MBR for GPT partition tables. + * Use $(CC) when determining compile flags. + * chain.c32: fix booting from logical partitions (Sergey + Vlasov.) + +Changes in 3.63: + * Fix errors in the PCI and DMI detection modules (Erwan Velu, + Sebastian Herbszt). + * Fix host dependencies and other issues in the build system. + * PXELINUX: Allow class E addresses as unicast. + * sdi.c32: module to load Microsoft System Deployment Images. + For additional information, please see: + http://msdn2.microsoft.com/en-us/library/ms838543.aspx + * EXTLINUX: Correct reading directories with deleted entries. + * Shuffle library: correct the handling of certain geometries + (an upward move with source material blocking the move); as + required by sdi.c32 but theoretically possible for other + formats as well. + * Add "make netinstall" to install /tftpboot. + * Fix some documentation files that didn't get moved/renamed. + +Changes in 3.62: + * Clean up garbage after "aborted." message. + * Clean up memdump.com filenames. + * Support SHA256 and SHA512 encrypted passwords. + * The shuffle library now can generate chained descriptors, + thus allowing pretty much arbitrarily complex memory maps. + * Handle command lines up to 2047 characters, the current + Linux kernel limit. + * vesamenu: support systems without linear framebuffer support + (sigh, what is this, 1993?) and 15-bit RGB modes. + * Move the label storage (for the command-line interface) to + high memory, removing the size limit and freeing up 64K of + low memory. + * Get rid of 4096-entry limit in the simple menu system. + * New hierarchial submenu support: see MENU BEGIN, MENU END, + MENU GOTO in doc/menu.txt. + * MENU QUIT allows creating a menu entry for returning to the + command line. + * ISOLINUX: Work around bug in certain Adaptec BIOSes, + patch by Bruce Robson. + * pngtopnm dependency removed from samples/ directory. + * Text documentation files (in doc/) renamed *.doc -> *.txt. + +Changes in 3.61: + * EXTLINUX: fix crash when accessing an empty file. + * elf.c32: If a PHDR segment is present, load it. + * Fix SHA-1 and MD5 passwords. + * ISOLINUX: fix booting when mastered without + mkisofs -boot-info-table (broken since 3.50, sigh...) + * Handle BIOSes which emit multiple contiguous valid + memory regions in the e820 map. + +Changes in 3.60: + * Support for "auxilliary data vector", a small amount of + writable storage. Currently only supported for EXTLINUX, + but the infrastructure is there for the other derivatives, + assuming a suitable storage location can be found. + * EXTLINUX: boot-once support (--once, --clear-once, and + --reset-adv). + * A command is now required to the EXTLINUX installer, i.e. at + least one of --install, --update, --once, --clear-once, or + --reset-adv. + +Changes in 3.55: + * PXELINUX: as per RFC 5071, PXELINUX no longer requires the + use of the magic cookie option (208) for unencapsulated + options. Currently it does not require it for + vendor-encapsulated options (vendor-option-space) either, + but that MAY be reverted in the future if it causes + problems. + * Documentation text files moved to a common "doc" directory; + man pages from the Debian project added to the "man" + directory. + * Correct bug with self-overlapping memory areas when using + the shuffle interface. + +Changes in 3.54: + * Add "menu separator", "menu indent", "menu disabled" + (see README.menu). + * vesamenu: fix handing of VESA modes with noncontiguous + memory buffers. In particular, Qemu/KVM sets up such a mode + when Cirrus Logic emulation is enabled (which is the + default.) + * Support for calling real mode functions using far calls, + with argument on the stack. This was implemented to support + the BIOS BBS specification, but subsequent experiments show + that the at least one of the most common BIOS cores, Award, + passes the presence check but doesn't actually implement the + functionality. + +Changes in 3.53: + * Fix bugs related to the $PnP BIOS functionality on some + platforms. + * PXELINUX: Fix the "naked" version of :: (suppress prefix.) + * elf.c32: better error messages. + * Faster operation under Intel VT virtualization. + * PXELINUX: Fix DHCP bootfile option. + * mkdiskimage: Support more than 1024 cylinders. + * (Hopefully) fix installer on non-x86 platforms. + * Fix shuffle_and_boot_rm, used by linux.c32. + * Fix shuffle_and_boot_pm on 386/486. + * ISOLINUX (at least): fix bss memory overwrite hang. + * MBR: Fix booting from logical partitions. + * Code cleanups. + +Changes in 3.52: + * Handle capitalized F-key commands in the menu system. + * Fix padding error when loading multiple ramdisks. + * Workaround for VMware crashing when trying to print a + message during early kernel boot (does not seem to work, + consider deleting.) + * chain.c32: add the ability to search for a specific MBR + signature at runtime. + * Fall back to the server identity option if the siaddr field + in the DHCP header isn't set. This seems to match the + behaviour of most PXE stacks. + * mkdiskimage: give the generated disk image an MBR signature. + * MEMDISK: Fix failures on some BIOSes. + * Simple menu system: new "MENU HIDDEN" option to not display + the menu unless the user presses a key. + * Simple menu system: support MD5-encrypted passwords (modern + Unix standard style, with "$1$" prefixes.) + * pcitest.c32: now functions as a full "lspci". Thanks to + Erwan Velu for this work. + * MEMDISK: Make EDD actually work. + * ISOLINUX: Fix for certain broken CD-ROM BIOSes which + randomly corrupted register FS. + * Simple menu system: fix memory overwrite bug that caused + some systems to lock up or behave weirdly. + * Fix building on 64-bit systems without a 32-bit libc installed. + +Changes in 3.51: + * EXTLINUX: Fix failure to find the configuration file. + +Changes in 3.50: + * New keywords allow the type of file to be specified in the + configuration file. + * It is now supported to load a different configuration file + with the CONFIG keyword. + * Fix API call 0x0019 (Read Disk.) + * MENU AUTOBOOT, MENU TABMSG, MENU PASSPROMPT allows + internationalization of menu messages. + * A new feature, TEXT HELP, allows the administrator to set + a multi-line help message for individual selections. + * Fix API call 0x0012 (Cleanup, shuffle and boot.) + * New API call "Cleanup, shuffle and boot to flat protected mode" + * New API call "Cleanup, shuffle and boot to real mode", + similar to API call 0x0012 but allows arbitrary register setting. + * Introduce a library interface for loading arbitrary binary + formats with relatively easily understood code. See + the elf.c32 module for an example on how to use it. + * New module "elf.c32", to load a protected-mode ELF kernel. + * MBR (old and new): Fix bug in CHS mode when LBA > + 65535*sectors. + * vesamenu: fix decoding of palettized PNG images. + * Update the Linux kernel boot protocol. + * PXELINUX: Press Ctrl-N at the boot prompt to read out the + network info. + * Instead of the (non-existent) MAC, use the client identifier + for networks like Infiniband and Firewire/1394. + * Add a new INCLUDE command to the core syslinux parser. + * Allow binding help text to F11 and F12. + * F-key help now available in the simple menu system. + * Disabled the polling for ARP during idle. It is simply too + slow on some (broken!) PXE stacks. + * PXELINUX: also try to fetch the config file based on UUID. + * SYSLINUX/EXTLINUX: New RAID mode (-r) which calls the BIOS + to load the next device (typically the next drive) on boot + failure. + +Changes in 3.36: + * MEMDISK: Disable EDD by default on floppy disks. EDD can be + enabled with the "edd" option and disabled with "noedd". + This (hopefully) should make Ghost work again. + * SYSLINUX: "unix" installer now uses Linux ioctls instead of + using libfat. + * New MBR which can boot from logical partitions. + * SYSLINUX: Fix bug in detecting special extensions which was + introduced in 3.35 :( + * PXELINUX: Unbreak chainbooting FreeBSD (and possibly others.) + +Changes in 3.35: + * MEMDISK: New "safeint" mode. + * MEMDISK: Be more compliant with the PnP BIOS spec. + * MEMDISK: Turn on EDD support by default. + * MEMDISK: Try to work on some machines on which it would not + work when there was no floppy drive in the system. + * Simple menu system: fix serial console support (broken in + 3.30). + * SYSLINUX: Support subdirectories. Like ISOLINUX, the + "current directory" is the directory in which syslinux.cfg + is found; this is searched for in the sequence + /boot/syslinux, /syslinux, /. As a side benefit, label names + like "linux-2.6.18" and "linux-2.6.19" are now supported. + + To install ldlinux.sys in a subdirectory, pass the -d + directory option to the SYSLINUX installer. + + This work was sponsored by slax.org (thanks, Tomas!) + * New API call: read disk. + * Invoke ONERROR on initrd load failure. + +Changes in 3.31: + * The simple menu system (menu.c32 and vesamenu.c32) now + support loading more than one configuration file at a time, + using MENU INCLUDE or by specifying multiple filenames. + * The MENU COLOR statement can now control the shadowing mode. + +Changes in 3.30: + * libcom32 extended to support graphics mode and graphical console. + * vesamenu.c32, new graphical version of the Simple + Menu System, see README.menu. + * New com32 modules by Erwan Velu do selection based on CPUID + or PCI devices present. + * RPM spec: add syslinux-tftpboot module; move syslinux by + default to the /usr/share/syslinux directory. + * RPM spec: extlinux is now a separate package. + +Changes in 3.20: + * EXTLINUX: New options --install (-i) and --update (-U), to + make it clear if a boot loader should be installed or + updated. For now, defaults to --install for compatibility; + a future version will require one of these options. + * New library functions to load and place files in memory. + * mboot.c32 bug fixes. + * Remove 8 MB kernel size restriction. + * Add "klibc" target for building unix/syslinux and + extlinux/extlinux with klcc (klibc-1.4.27 or later.) + * PXELINUX: Fail (and eventually reboot) if no configuration + file was found. + * COM32 module by Erwan Velu to make decisions based on DMI + info. + * Fix issue where going back and forth between menus a lot + would cause a hang. + * ISOLINUX: Fix bug which made "cd boot sectors" not work. + +Changes in 3.11: + * MEMDISK: Fix bug by which accessing the real floppy disk + as B: in MS-DOS was broken. + * Simple menu system: allow tweaking of the screen layout. + * Simple menu system: better command-line editing, correctly + handle command lines above 256 characters. + * PXELINUX: revert memory allocation change that caused + problems on some network cards. + * MEMDISK: Try work around a bug on some BIOSes when no + physical floppy disk is present in the system. + * Enable the 16550A FIFOs, if present, when doing serial + console. + * New "totaltimeout" command establishes a timeout without + regard for any user input. + * Simple menu system: timeout behaviour now more in line with + user expectations. + * Simple menu system: "ontimeout" should now work correctly. + +Changes in 3.10: + * gcc 4.0.1 compilation fixes. + * Add support for querying the PCI BIOS in libcom32 + (used by ethersel et al.) + * Fix PCI handing (ethersel etc) on several old chipsets (and + VMWare.) + * Try to deal with systems with broken EBIOS. + * New API call to do "localboot". + * New API call to query features. + * New API call to run kernel image, a lower-level call than + "run command". See comboot.doc. + * Fix for bug in EBIOS code discovered by Arwin Vosselman. + * NOESCAPE security fix. + * Comments are now recognized even without a space following #. + * Fix incorrect handling of mixes of entries with and without + MENU PASSWD. + * The idle API call now harmlessly returns failure if it is a + no-op. That way the caller can decide whether or not to + bother invoking it again. + * Temporarily disable the idle API call on PXELINUX, due to + some platforms on which the idle API call seems to hang; this + problem has not yet been debugged. + * MEMDISK: the handling of DOSEMU-headered images was broken; + fix it. + * EXTLINUX: symlinks are now supported. + * Simple menu system: N and P now work correctly as hotkeys. + * MEMDISK: more BIOS bug workarounds. + +Changes in 3.09: + * gcc4 compilation fix. + * (Ctrl-G) in message files now causes a beep. + * Reduce the command line to 511 characters; 1023 caused + memory overflows. + +Changes in 3.08: + * SYSLINUX: Fix performance regression (-s mode always + enabled.) + * Add API function for idle loop. + * libutil: Add do_idle() function for idle loop, make + get_key() use it. + * libutil: Add SHA-1 and base64 functions. + * Simple menu system: add password support. + * EXTLINUX: Sparse files now handled correctly. + * EXTLINUX: Large directories now handled correctly. + * ALL: At the prompt, Ctrl-X now forces text mode. + * Fix configuration file parsing error, that could cause + hangs. + * Rewritten advanced menuing system from Murali Ganapathy. + * MEMDISK: New "bigraw" mode to work around certain broken + BIOS flash programs. + * COM32 module to boot Multiboot systems, including Xen. See + com32/modules/mboot.doc. + * Max command line changed to 1023 characters. Note that the + kernel proper still can only handle 255 characters without + patching, and COM16 binaries can only handle 125 characters. + +Changes in 3.07: + * Fix chainloading (chain.c32). + * Fix zlib build problem. + * Use a private copy of . + +Changes in 3.06: + * Fix typo that caused the ramdisk to load in the wrong place. + +Changes in 3.05: + * New API function "shuffle and boot"; allows COM32 modules to + load or construct (almost) arbitrarily complex objects, + e.g. a kernel and its initrd/initramfs in pieces, and have + the API core reorganize memory for booting. (A library API + for this function will be introduced in a later version.) + * The initrd= option now supports multiple filenames separated + by commas. This is mostly useful for initramfs, which can + be composed of multiple separate cpio or cpio.gz archives. + (Note: all files except the last one are zero-padded to a 4K + page boundary. This should not affect initramfs.) + * EXTLINUX: Fix API function 000Ah (get derivative-specific + info). + * libcom32/ethersel: Support PCI Config Mechanism #2 on + machines still infested with that hideous old hack. + * SYSLINUX: Fix directory-parsing bug. + +Changes in 3.02: + * SYSLINUX: The "unix" installer now sets the MS-DOS + attributes (hidden, system, readonly.) + * COM32 library: build the .lnx (test modules for running + under Linux) as architecture native modules, in case + i386 devel libraries aren't installed. + * EXTLINUX: Hack for systems which don't have BLKGETSIZE64 + defined in the standard header files. + * Simple menu system: minor aestetic improvements, and try to + work better over a serial console (speed, and readability on + monochrome terminal emulators.) + * New CONSOLE directive to control output on the video console + (useful for dealing with some broken serial-forwarding + BIOSes.) + * New com32 module "ethersel" for searching for an Ethernet + card and selecting the proper version of Etherboot. + * EXTLINUX: Allow the user to override the detected geometry. + Add help. + +Changes in 3.01: + * EXTLINUX, SYSLINUX: Fix compile errors on some systems. + * EXTLINUX: Default to zipdrive geometry (64 heads, 32 + sectors) if no other geometry can be detected. + +Changes in 3.00: + * SYSLINUX: Support FAT32 and EDD. As an unfortunate + consequence, LDLINUX.SYS is no longer an ordinary file; it + is block-mapped at install time, which means it can only be + written using the syslinux installers. + * SYSLINUX: Reorganize the source code for the installers; + each one of the installers (dos, win32, unix, mtools) is now + built in its own subdirectory. In particular, "mtools" is + the unprivileged installer which uses mtools; "unix" is the + privileged installer which uses system calls. + * SYSLINUX: Completely rewritten DOS installer in C. + * ALL: "label" statement information is now stored in a + compressed format, which means that a lot more labels are + permitted (500-1000 in a typical configuration, but depends + on the complexity.) + * EXTLINUX: New derivative, which boots from an ext2/ext3 + filesystem. + * SYSLINUX: The DOS and Win32 installers can now optionally + write the boot sector to a file instead of the real boot + sector. This is not supported in the Linux installers, + however. + * ALL: New NOESCAPE command, disables the "hold down the Shift + key to display the prompt" behaviour. + * New simple menu system, as an alternative to the advanced + menu system already present. See README.menu for details. + * PXELINUX: Filenames can now be prefixed with an IP address + or DNS name plus :: (e.g. 192.0.2.1::filename or + server.domain.com::filename), which downloads a file from an + alternate TFTP server, or just a :: (e.g. ::filename), which + suppresses the common pathname prefix. See pxelinux.doc. + * SYSLINUX: Add an -m option to the DOS and Win32 installers + to write an MBR and -a to mark the partition SYSLINUX is + being installed on active. + * MEMDISK: Give a way to query the boot loader type while + running MEMDISK; see memdisk/memdisk.doc and + sample/mdiskchk.c. + * mkdiskimage: substantially improved mkdiskimage which, among + other things, can now be used to initialize USB keys as + zipdrives; see README.usbkey for more information. + +Changes in 2.13: + * MEMDISK: Fix command-line parsing "brown paper bag" class + bug. + * MEMDISK: Add "raw" mode to support the DOS boot disk from + WinME/XP, which seems to deliberately crash the system + when someone uses the "INT 15h mover" highmem API. + * Make "make install" do something sane for the com32 + development environment. + * In the spec file, create a separate -devel RPM for the com32 + development environment. + +Changes in 2.12: + * Simple C library, based on klibc, for writing COM32 + programs. + * Fix the handling of file length in loading of COM32 + programs. + * MEMDISK: Work around a linker bug by rearranging the code to + not use the linker for the 16-bit code. + * SYSLINUX: If we're building on a machine without a Win32 + (mingw) compiler, just skip building syslinux.exe. + * ISOLINUX: Support non-mkisofs mastering programs, at least + as long as the image is single-session. For best results, + ISOLINUX should be the only boot loader installed. + * MEMDISK: Allow the user to specify that the simulated disk + should be write-protected. + +Changes in 2.11: + * ALL: Add an API call to get the configuration file name. + * SYSLINUX: Fix bug in 2.10 that prevented it from working + correctly for a lot of people. + * SYSLINUX: In the installer, make mtools a bit less fussy. + * Make the menu system compile with newer gcc's. + +Changes in 2.10: + * MEMDISK: Handle images compressed with zip as well as with + gzip. Some Windows-based image tools apparently generate + these kinds of images by default. Patch by Patrick + LoPresti. + * Major menu improvement from Murali Ganapathy. + * ISOLINUX: Wonderfully sick and brilliant workaround for + severe bugs in certain Award BIOSes; from Knut Petersen. + * SYSLINUX: Fix for the nomtools installed, from Frederic + Pasteleurs. + * PXELINUX: Fix handling of IP numbers in the ranges + 100-109 and 200-209. + * ALL: New option "allowoptions" (defaults to 1), which + controls if options are allowed on the command line or not. + * SYSLINUX: Support building under klibc (see the klibc + distribution for details.) + +Changes in 2.09: + * SYSLINUX: Remove residual setuid crap from + syslinux-nomtools. + * Handle video pages correctly when using the API functions. + * Handle compiling on an x86-64 platform correctly. + * Menu system from Murali Krishnan Ganapathy; see the menu + directory for information. + * COMBOOT: Allow COMBOOT programs to change screen text mode. + * COMBOOT: Correct the documentation of how to detect + SYSLINUX from COMBOOT!!!! + * COMBOOT: Fix "get key without echo" API function. + * SYSLINUX: Fix bug that affected the API open function. + * ALL: Improve the E820 memory parser, to work around some + buggy BIOSes. + +Changes in 2.08: + * Add new configuration command "ontimeout" to allow timeout + to have a different action than just pressing Enter. + * Add new configuration command "onerror" to allow a custom + command to be executed in case the kernel image is not found. + * Fix bugs in the COMBOOT/COM32 command-line parsing. APPEND + now works with COMBOOT/COM32 images. + * PXELINUX: Poll for ARP requests while sitting at the + prompt. This makes some boot servers a lot less unhappy. + * PXELINUX: Actually free sockets when we get a failure + (including file not found.) This bug would cause us to run + out of sockets and thus "go deaf" after a while. + * MEMDISK: Add an API to query for the existence of MEMDISK. + * SYSLINUX: Fix loading boot sectors (.bs/.bss) from floppy + disk. + * .c32 is now one of the extensions searched for + automatically. + * PXELINUX: RFBG.exe seems to randomly overwrite memory + location 0x5700. Thus, don't use it! + * PXELINUX: Change pathname length max from 63 to 127; change + max vkernels from 128 to 64. + * Support Ctrl-U -> kill entire command line input. + * The "samples" directory contains a (barely at all tested) + chain loading example, chain.c32, which may be used to + chainload local floppy disks and hard disks. Use with + "chain fdN" or "chain hdN [partition]"; N = 0 for the first + drive of each type. + +Changes in 2.07: + * MEMDISK: Workaround for BIOSes which go into a snit when + they get a RESET command for the floppy system when there is + no floppy in the system. + * PXELINUX: Add "ipappend 2", which passes the hardware + address of the boot interface to the kernel as a + command-line option. + * mkdiskimage: fix the generation of the end limit. + * PXELINUX: Fix multiple bugs in chainloading of other NBPs. + * MEMDISK: Fix bug that would occationally cause "ran out of + input data" when using compressed disk images. + * SYSLINUX: Updates for the win32 installer (from Lars Munch.) + * PXELINUX: PXELINUX-specific options are now recognized both + in a vendor-option-space (a.k.a. type 43 encapsulated) as + well as in a site-option-space (unencapsulated.) + * COM32: Don't crash when DS != 0. + * COMBOOT/COM32: Make file reading work correctly. Thanks to + Phung Chi Kien for submitting a test program. + +Changes in 2.06: + * ALL: Fix problem that would occationally cause a + boot failure, depending on the length of the kernel. + * ISOLINUX: Fix problem that would occationally cause a + boot failure, depending on the length of directories. + * SYSLINUX: Win32 installer now flushes buffers. + * ppmtolss16: Be fully compliant with the PNM spec; + actually process comments in the header and odd + alignments of the various parameters, as well as + "plain" (not raw) files and PBM and PGM files. + * PXELINUX: Lower the default MTU to 1472 in order to deal + with systems with slightly nonstandard MTUs, and PXE + stacks which don't defragment correctly. Unfortunately this + is very hard to test dynamically. + +Changes in 2.05: + * PXELINUX: Add a default query based on the hardware address + of the boot device. This is in lower case hexadecimal form + separated by dashes and including the hardware type, for + example, the Ethernet (type 1) address 88:99:AA:BB:CC:DD + would query the file pxelinux.cfg/01-88-99-aa-bb-cc-dd. + * PXELINUX: Fix bug involving non-IP-based config file names. + * SYSLINUX: New installer for WinNT-based systems, from Lars + Munch. + * MEMDISK: Fix handling of memory region overlap when + decompressing. Thanks to Mikhail Kupchik for identifying + the problem. + +Changes in 2.04: + * ALL: Reclaim even more low memory by observing that + comboot_seg == real_mode_seg is perfectly fine, and by the + fact that the 1000h segment managed to get unused in all + derivatives... + * PXELINUX: Attempt to negotiate full Ethernet-sized blocks + (1468 bytes) using the blksize option. + * SYSLINUX: Resurrect the old no-mtools version of the + installer, although as a root-only tool. Some distributors + have indicated that they need a small standalone installer. + * MEMDISK: Fix a memory offset computation error when + installing compressed disks which generally would cause + 1 MB of memory to be wasted. + * MEMDISK: Fix installing the E820 memory map. Calling + INT 15h AX=0E820h with MEMDISK 2.03 loaded would give a + completely corrupt memory map. + * SYSLINUX: Make libsyslinux a dynamic library, so that it can + be updated separately from client programs. The whole idea, + after all, is to enable alternate programs to become + syslinux installers. + * Include an rpm spec file in the distribution, so rpmbuild + -ta works. + +Changes in 2.03: + * Actually support comment lines in the configuration file. + * PXELINUX: Try to resolve some problems with stack switches. + * PXELINUX: Handle PXE stacks with broken routing. + With these workarounds, the remote install PXE boot floppy + (rbfg.exe) from Argon Technologies should work correctly. + * Fix problems with Perl scripts in UTF-8 locales. + * You probably need NASM 0.98.34 or later to compile this + version. 0.98.36 is recommended. + * MEMDISK: Now supports gzip compressed images. + +Changes in 2.02: + * SYSLINUX: Security flaws have been found in the SYSLINUX + installer when running setuid root. Rewrite the SYSLINUX + installer so it uses mtools instead. It therefore now + requires mtools (specifically mcopy and mattrib) to exist on + your system, but it will not require root privileges and + SHOULD NOT be setuid. + +Changes in 2.01: + * MEMDISK: Fix memory sizing bug when the ramdisk crosses the + 16 MB boundary. + * MEMDISK: Add a "pause" option to stop immediately before + booting, to read off the messages. + * MEMDISK: Support disk images with DOSEMU headers. + * Update the mkdiskimage script to handle newer mtools + versions, and be able to generate disk images with DOSEMU + headers (controlled by the -d option). + * Fix the COM32 sample program. + * PXELINUX, ISOLINUX: Fix some COMBOOT API calls. + * PXELINUX: Doc fix. + * Build SYSLINUX into a small library for encapsulation into + other programs (however, please keep in mind this is a GPL'd + library.) + * SYSLINUX: Make installer work with "owner" in /etc/fstab. + * SYSLINUX: Fix issue with working on nonpartitioned hard disk + devices. THIS CONFIGURATION IS NOT RECOMMENDED. + +Changes in 2.00: + * ALL: Add support for "COM32" (32-bit COMBOOT) images. + * ALL: Add an API for COMBOOT/COM32 images. See comboot.doc + for details. There is a C development environment for + COM32 being created; it should be ready at some point in + the future. + * Fix mbr.asm so that it actually works. + * SYSLINUX: The syslinux installer *SHOULD* now be safe to + run setuid root. + * PXELINUX: Fix bug where PXELINUX would override random + chunks of the UNDI code segment! Thanks to Kevin Tran for + finding this bug. + * ISOLINUX: Fix a bug related to slashes in pathnames. + * ISOLINUX: Fix a bug in handling initrds over 128 MB. + * ALL: Make the key print out the version; this is + to help debugging. + * Add a small script, mkdiskimage, to create a DOS-formatted + hard disk image using mtools. This may be useful in + conjunction with MEMDISK. + * ISOLINUX: Search for a /boot/isolinux directory as well as + /isolinux. + * ALL: Fix a bug related to very long configuration files. + * PXELINUX: Work around a NASM bug which would result in no + delay before reset if an error occurs. + +Changes in 1.76: + * ISOLINUX: Remove code no longer used which caused hangs on + some Toshiba laptops. + +Changes in 1.75: + * ALL: NASM 0.98.32 or later is now required to build + SYSLINUX from sources. + * SYSLINUX: put back in the workaround for the BIOS floppy + table. This seems to be a requirement for "extended" floppy + formats to work correctly. + * SYSLINUX: No longer warn if one is trying to boot on a 286 + or older. The above BIOS workaround no longer fits if the + requirement to use only 8086-compatible code in the early + boot is maintained. It made sense in 1994, but in 2002 a + 286 or older is a museum object. + * SYSLINUX: Use a downright bizarre, stateful algorithm to try + to guess the maximum transfer size. I am *hoping* this will + cut down on the number of systems for which -s is required + to work at any acceptable speed. + * ISOLINUX: Add a few more workarounds for various broken El + Torito BIOSes. + * Make sure .depend files aren't accidentally packed... + * ALL: Fix bugs in the extension-detect code; this caused + files like COMBOOT images and CD boot sectors to be + mis-identified as Linux kernels and rejected. + * ALL: Fix the return from COMBOOT. + * ALL: Do some of the early groundwork for supporting DOS + system calls in COMBOOT. + * Get rid of unnecessary "near" directives, making the code + bigger. + * PXELINUX: Put the PXE stack back in the init state before + invoking a chain-loaded NBP. + * PXELINUX: Actually found the combination of calls that + allows some (most?) PXE 2+ stacks to be unloaded from memory + properly. + * PXELINUX: Add "keeppxe" command-line option to disable + the standard unloading of the PXE stack. + +Changes in 1.74: + * SYSLINUX: fix bug that would cause valid kernel images to be + labelled "invalid". + +Changes in 1.73: + * Work on removing gratuitous differences between modules. + * Break up the source in common and module-specific files. + * PXELINUX: Allow chaining of other PXE NBPs. + * ISOLINUX: Allow loading "CD-ROM boot sectors". + * ALL: generalize the definition of a boot sector/NBP. + +Changes in 1.72: + * PXELINUX, ISOLINUX: Fix bugs in the new core code. + +Changes in 1.71: + * Fix a "brown paper bag" class bug in the new core code. + +Changes in 1.70: + * Major code restructuring. + * Relax the conventional memory limits somewhat. + * MEMDISK: Set up the "version number string" pointer in the + header correctly. + * SYSLINUX: Fix, again, "the bug that won't die": the use of + the offset parameter with the SYSLINUX installer. + * SYSLINUX: Fix possible superblock corruption problem in the + SYSLINUX installer. + +Changes in 1.67: + * Handle bug in the location of initrd. + +Changes in 1.66: + * MEMDISK: Make compile with newer versions of gcc. + +Changes in 1.65: + * ISOLINUX: Support booting disk image files (to boot DOS or + other non-Linux operating systems), *IF* the BIOS works + correctly; unfortunately many BIOSes apparently don't. + * Support Linux boot protocol version 2.03 (explicitly + specify the initrd address limit.) + * Handle small "pseudo-kernels"; images that use the Linux + kernel boot protocols but are less than 64K in size. + * MEMDISK: New subsystem; this is a driver which allows + legacy OSes to boot using an in-memory simulated disk. + See memdisk/memdisk.doc for more info. + * PXELINUX, ISOLINUX: Correctly handle files larger than 65535 + blocks (32 MB for PXELINUX, 128 MB for ISOLINUX.) + * PXELINUX: Make a best-effort attempt at freeing all memory + claimed. From the looks of it, it will fail on most PXE + stacks. + +Changes in 1.64: + * Limited support for hardware flow control when using a + serial port console. + * Support specifying the serial port I/O address explicitly. + * Make DOS installer hopefully behave more nicely when used on + recent Windows versions. + * Fix returning to text mode when a font has been specified. + * Attempt to detect missing serial port hardware and disable + the serial port if there is nothing there. + +Changes in 1.63: + * Make the ppmtolss16 program handle color conversion more + correctly. + * Clean up "make install" target, honour INSTALLROOT if it + exists. + * SYSLINUX: Fix stack-smash bug identified by Steffen + Winterfeldt. + * Hopefully fix return-to-text-mode on some graphics cards. + * ISOLINUX: Bug workaround for Award BIOS 4.51, and perhaps + other buggy BIOSes as well. + +Changes in 1.62: + * PXELINUX: Allow the DHCP server to override the + configuration file name and pathname prefix, using + "site-specific" DHCP options. + * PXELINUX: Documentation fixes. + * PXELINUX: Fix the "ipappend" option; the last two values + were reversed vs. what the kernel expected. + * Introduce a way to return to text mode once we are already + in graphics mode. This may be useful for F-key help + screens. + * Fix several bugs in the way return to text mode was handled. + +Changes in 1.61: + * ISOLINUX: Support full pathname searches. Max length for a + pathname is 255 characters. As a result, only 64 "label" + statements are supported in ISOLINUX. + * PXELINUX: Max filename length extended to 63 characters. + +Changes in 1.60: + * Add support for graphical splash screens. + * Add mode control characters, which allows you to control + message display output depending on output mode (text, + graphics, or serial port.) + * ISOLINUX: New program, which boots Linux from a CD-ROM + without using floppy emulation mode. See isolinux.doc for + more details. + * PXELINUX: Don't search for boot sector file types, since + they don't work anyway. + * SYSLINUX: Document the LOCK command for Win9x, and the error + dialog box for WinNT/2K. + +Changes in 1.54: + * PXELINUX: Fix code for finding !PXE from PXENV+. This was + due to a spec bug; match the most recent spec since that + seems to be what implementations actually do. + * SYSLINUX: Add some smarts to the boot sector, which + hopefully should reduce the number of systems which require + stupid mode ("syslinux -s"). + * PXELINUX: Document further some of the pathologies with old + PXE stacks. + * When specifying a "default" command line, no longer + automatically appent "auto". See the "DEFAULT" command in + syslinux.doc for more information. + * PXELINUX: Clean up the allocation of local socket numbers. + +Changes in 1.53: + * PXELINUX: Rename pxelinux.bin to pxelinux.0, to match what + most PXE servers seem to expect. + * PXELINUX: Update the DHCP/boot server setup documentation. + * PXELINUX: Support new "localboot" option for "label" + sections. + * PXELINUX: More robust parsing of DHCP/boot server packets. + * PXELINUX: Include a small utility program "gethostip" to + compute hexadecimal IP addresses. + +Changes in 1.52: + * PXELINUX: Fix bugs introduced by new A20 code. (SYSLINUX + has also been changed for code consistency reasons, but I'm + pretty sure the changes are don't care on SYSLINUX.) + * Documentation updates. + * PXELINUX: Add "ipappend" option to generate an ip= option to + the kernel. + +Changes in 1.51: + * PXELINUX: Not all PXE stacks fill in the IP address for a + type 3 cached info query. Use a type 2 cached info query + for this information (only.) + * Yet another attempt at A20 coding. Now support BIOS call + 15:2401 as well, and handle machines which always have A20 + on separately. + * Support memory detection using INT 15h, AX=0E820h. BIOS + manufacturers have apparently gotten sloppy about keeping + INT 15h, AX=0E801h working properly. + * Don't issue onto the serial port when we're doing + screen wraparound. + +Changes in 1.50: + * Yet another A20-code update. It seems some "legacy-free" + machines and embedded gear simply don't have a KBC to talk + to, and that waiting for one will wait forever. Sigh. + +Changes in 1.49: + * SYSLINUX: Implement a hack for BIOS drivers which hog significant + chunks of low memory during boot. (Note: PXELINUX already + had this modification. SYSLINUX does still require that the + low 512K is available; PXELINUX requires 384K. Machines + with a physical memory hole in the low 640K cannot boot + Linux no matter what.) Depending what the reason is for the + memory hole, a new kernel (2.4.0-test3-pre3 or later) may be + required. + * SYSLINUX: Default installer binary now compiled against + glibc 2.1. If this is inappropriate for your system and you + still want to use the offical version of SYSLINUX, please + follow the instructions in "distrib.doc" to rebuild the + installer. + * SYSLINUX: Linux installer program now supports -o + option which does a loopback mount with the + -o loop,offset=<> option. Useful to run SYSLINUX on an + individual partition of a whole-harddisk image. + * Include the source code to a Master Boot Record (MBR) + functionally equivalent to the one installed DOS except it + includes EBIOS support, and should be resistant to geometry + changes. The MBR code is public domain. + * PXELINUX: Fix "double p" bug: if the tftp prefix was null, + all filenames would get a "p" preprended, e.g. + "ppxelinux.cfg" and "pvmlinux". + +Changes in 1.48: + * PXELINUX: Workaround for PXE ROMs based on the Intel PXE PDK + 3.0 build 071 and earlier: missing !PXE structure pointer. + * PXELINUX: Handle larger BOOTP/DHCP packages. + * PXELINUX: The command line passing was broken; fix. + * PXELINUX: Make COMBOOT images work. + * PXELINUX: Documentation on how to make booting work using + the PDK 3.0-derived clients, which aren't so generous as to + allow booting with only "PXEClient" specified. + +Changes in 1.47: + * PXELINUX: RFC 1123 states that a TFTP implementation MUST + use adaptive timeout, "at least an exponential backoff of + retransmission timeout is necessary." Implement a very + simple exponential backoff for retransmits. + * PXELINUX: Updated documentation, including pointer to new + TFTP server. + * PXELINUX: When sending ERROR due to bad OACK, use the proper + destination port number (why are TFTP port numbers so odd?) + * PXELINUX: If the boot dies in the middle somewhere, + eventually give up and reset the machine (unattended + operation.) + +Changes in 1.46: + * New program PXELINUX to do network booting using a + PXE-compliant (Pre-Execution Environment) network booting + PROM. See pxelinux.doc for details. + +Changes in 1.45: + * Serial console support. See syslinux.doc for details. + +Changes in 1.44: + * Change HIGHMEM_MAX to 38000000h to (hopefully) avoid the + kernel stepping on it; 3f000000h was apparently a higher + limit than the kernel used! + +Changes in 1.43: + * Add sys2ansi.pl script to display the contents of a + colorized SYSLINUX file. + * Changed the io_delay once again, after a report that the + old delay port causes hangs on some systems. + +Changes in 1.42: + * Frob the "fast A20 gate" port as well as the keyboard + controller; will this help systems with problems? + * Be even more paranoid about A20, unfortunately even this + seems to be not paranoid enough... what I don't understand + is that if there is hardware out there *this broken*, how + can it run Linux at all? Report an error message rather + than hang forever if A20 is stuck. + * Include some intermediate files in the distribution, plus + provide a "make installer" target for distributors to relink + the install programs only. I would prefer the syslinux boot + loader proper to be "binary clean" for debuggablity -- use + "make clean ; make installer" to rebuild the installers only. + +Changes in 1.41: + * Don't get confused by directories, volume labels, or VFAT + long names. + * Use INT 15h, AX=0E801h to query memory size before trying + INT 15h, AH=88h. This not only provides more headroom + between the kernel and the initrd on large-memory machines, + but it appears some recent BIOSes actually have started + returning garbage for the AH=88h (older) call. + * Trust high memory beyond the 15 MB mark if the user has + specified it, or if obtained with INT 15h, AH=0E801h (with + no memory holes above 1 MB.) + +Changes in 1.40: + * Increase A20M delay and put in a test to avoid problems on + certain IBM Thinkpads (thanks to Donnie Barnes of RedHat + for vital info on this one.) + * Support COMBOOT style boot command images. + * Support chain loading (foreign operating systems, e.g. DOS). + * Include a new "copybs" DOS utility to copy a boot sector to + a file (under Linux, use "dd".) + * Fix the DOS installer to work for disks over 32 MB. + * SYSLINUX should now handle disks with more than 65536 tracks. + +Changes in 1.37: + * Fix a bug that caused "label" statements in syslinux.cfg to + not be handled properly. + * Updated the documentation. Among other things, we now allow + up to 128 "label" statements. + +Changes in 1.36: + * Fix for booting old (pre-initrd) kernels. + * It seems at least some versions of OS/2 doesn't set up all + the fields in the superblock correctly. Account for that. + * Fix bug that caused boot failure when using the mem= option. + +Changes in 1.35: + * Loading from partitions now should work properly. (Actually + tested, this time. You should even be able to dd a floppy + to a partition and boot from it.) + * Removed large workaround code for an alleged ancient BIOS + bug I have never actually seen. The -s option should work + on those machines, anyway. + * Support for simple keyboard remappings, same as used by + LILO (once again to support localization.) The program + keytab-lilo.pl from the LILO distribution included to + generate such maps. + * Added a "safe, slow and stupid" (-s) option to the + installers. This option will lobotomize the boot sector to + hopefully work on even very buggy BIOSes. + +Changes in 1.34: + * Ability to load a VGA font on bootup (for localized Linux + distributions.) + +Changes in 1.33: + * Bug fix in the Linux installer. + * Added a workaround for a bug in certain AMI/Intel BIOSes + when booting from CD-ROM. + * Documentation changes. + +Changes in 1.32: + * FAT16 filesystems are now supported. + +Changes in 1.31: + * Now compiles under Linux, using NASM, rather than using + Turbo Assembler under DOS. See http://www.cryogen.com/Nasm + for information about NASM. + * Linux-hosted SYSLINUX installer, as well as a + rewritten DOS installer (now is written in assembler, so we + don't need Turbo C.) + +Changes in 1.30: + * Added support for loading bzImage and initrd loading, and made + SYSLINUX new-setup-code aware (SYSLINUX 1.30 id=0x31). + * Added LILO-style kernel labels; see the LABEL and IMPLICIT + keywords in README file. + * Added support for colorization of intro and help screens. + * The vga= option is now handled correctly. + * Massive rewrite of large chunks of the code in order to + support the first two new features. + +Changes in 1.20: + * Added simple online help at the "boot:" prompt. + * Removed 2880K image as I no longer have access to such a + floppy drive. (Donations accepted!!) + * Decided to distribute the source in a subdirectory rather + than in a nested zipfile. + +Changes in 1.11: + * Removed a sanity check which would cause booting to fail on + Phoenix BIOS version 4.03. Apparently this BIOS is buggy. + +Changes in 1.10: + * Added configuration file SYSLINUX.CFG. This file contains all + configurable options, and can be edited from any OS which can + access an MS-DOS filesystem; there is no longer a need to run + SYSLINUX.EXE except to write the boot sector. + * Default command line now given by "default" line in config + file. + * LINUXMSG.TXT and BOOTMSG.TXT hard-coded file names replaced by + "display" and "prompt" lines in config file. + * LILO-style option appending now supported ("append" line in + config file). + * Prompt timeout is now supported ("timeout" line in config + file). The timeout is cancelled when anything is typed on the + command line. + * Pressing or at the Loading... stage now aborts + the kernel loading in progress and returns the user to the + boot: prompt. + * The installer now automatically sets the READONLY flag on + LDLINUX.SYS. + * Added 2880K disk image. + +Changes in 1.03: + * Fixed bug that would prevent booting from double-density + floppies and other DOS filesystems with multiple sectors per + cluster. + * Added 720K disk image. + * Changed default kernel name on disk images to LINUX. + +Changes in 1.02: + * Fixed bug that would garble the command line on recent kernels + with more than 4 sectors of setup code (this wasn't really a + *bug*; rather, a kernel change broke the code. Unfortunately + the Linux boot interface is still sorely undocumented). + * Added BOOTMSG.TXT file support (message file which does not + force display of the boot prompt). + +Changes in 1.01: + * Fixed bug on some (most?) 386 BIOSes would require two boot + attempts. diff --git a/README b/README new file mode 100644 index 0000000..0e92ea2 --- /dev/null +++ b/README @@ -0,0 +1,34 @@ +See the files in the doc directory for documentation about SYSLINUX: + + syslinux.txt - Usage instructions; manual. + distrib.txt - For creators of Linux distributions. + pxelinux.txt - Documentation specific to PXELINUX. + isolinux.txt - Documentation specific to ISOLINUX. + extlinux.txt - Documentation specific to EXTLINUX. + menu.txt - About the menu systems. + usbkey.txt - About using SYSLINUX on USB keys. + comboot.txt - About the extension API. + memdisk.txt - Documentation about MEMDISK. + +Also see the files: + + NEWS - List of changes from previous releases. + TODO - About features planned for future releases. + COPYING - For the license terms of this software. + +SYSLINUX now builds in a Linux environment, using nasm. You need nasm +version 0.98.39 or later to build SYSLINUX from source. See +http://nasm.sf.net/ for information about nasm. + +There is now a mailing list for SYSLINUX. See the end of syslinux.txt +for details. + +SYSLINUX is: + + Copyright 1994-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + Boston MA 02111-1307, USA; either version 2 of the License, or + (at your option) any later version; incorporated herein by reference. diff --git a/TODO b/TODO new file mode 100644 index 0000000..530b4bf --- /dev/null +++ b/TODO @@ -0,0 +1,22 @@ +*** To do in the short term: + +- PXELINUX: Figure out localboot/idle problems. + +- PXELINUX: Support changing the default server and boot file prefix? + +- Library support for all the comboot system calls. + +- Deal with non-512-byte sectors (if I can get media which does...) + + +*** Future projects: + +- Clean up the command-line parsing. + +- Cleaned up documentation, with a real man page. + +- API call to get directory listing. + +- COM32-based CLI. + +- Rewrite the filesystems to run in protected mode C code. diff --git a/codepage/Makefile b/codepage/Makefile new file mode 100644 index 0000000..2a6fd12 --- /dev/null +++ b/codepage/Makefile @@ -0,0 +1,21 @@ +PERL = perl +CPSRC = $(wildcard *.txt) +GENFILES = $(patsubst %.txt,%.cp,$(CPSRC)) + +.SUFFIXES: .txt .cp + +all: $(GENFILES) + +# This generates codepage files where the display and filesystem +# codepages are both the same. +%.cp: %.txt cptable.pl UnicodeData + $(PERL) cptable.pl UnicodeData $< $< $@ + +tidy: + rm -f *.cp *.bin + +clean: tidy + +dist: tidy + +spotless: clean diff --git a/codepage/UnicodeData b/codepage/UnicodeData new file mode 100644 index 0000000..7a1916e --- /dev/null +++ b/codepage/UnicodeData @@ -0,0 +1,952 @@ +0000;;Cc;0;BN;;;;;N;NULL;;;; +0001;;Cc;0;BN;;;;;N;START OF HEADING;;;; +0002;;Cc;0;BN;;;;;N;START OF TEXT;;;; +0003;;Cc;0;BN;;;;;N;END OF TEXT;;;; +0004;;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; +0005;;Cc;0;BN;;;;;N;ENQUIRY;;;; +0006;;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; +0007;;Cc;0;BN;;;;;N;BELL;;;; +0008;;Cc;0;BN;;;;;N;BACKSPACE;;;; +0009;;Cc;0;S;;;;;N;CHARACTER TABULATION;;;; +000A;;Cc;0;B;;;;;N;LINE FEED (LF);;;; +000B;;Cc;0;S;;;;;N;LINE TABULATION;;;; +000C;;Cc;0;WS;;;;;N;FORM FEED (FF);;;; +000D;;Cc;0;B;;;;;N;CARRIAGE RETURN (CR);;;; +000E;;Cc;0;BN;;;;;N;SHIFT OUT;;;; +000F;;Cc;0;BN;;;;;N;SHIFT IN;;;; +0010;;Cc;0;BN;;;;;N;DATA LINK ESCAPE;;;; +0011;;Cc;0;BN;;;;;N;DEVICE CONTROL ONE;;;; +0012;;Cc;0;BN;;;;;N;DEVICE CONTROL TWO;;;; +0013;;Cc;0;BN;;;;;N;DEVICE CONTROL THREE;;;; +0014;;Cc;0;BN;;;;;N;DEVICE CONTROL FOUR;;;; +0015;;Cc;0;BN;;;;;N;NEGATIVE ACKNOWLEDGE;;;; +0016;;Cc;0;BN;;;;;N;SYNCHRONOUS IDLE;;;; +0017;;Cc;0;BN;;;;;N;END OF TRANSMISSION BLOCK;;;; +0018;;Cc;0;BN;;;;;N;CANCEL;;;; +0019;;Cc;0;BN;;;;;N;END OF MEDIUM;;;; +001A;;Cc;0;BN;;;;;N;SUBSTITUTE;;;; +001B;;Cc;0;BN;;;;;N;ESCAPE;;;; +001C;;Cc;0;B;;;;;N;INFORMATION SEPARATOR FOUR;;;; +001D;;Cc;0;B;;;;;N;INFORMATION SEPARATOR THREE;;;; +001E;;Cc;0;B;;;;;N;INFORMATION SEPARATOR TWO;;;; +001F;;Cc;0;S;;;;;N;INFORMATION SEPARATOR ONE;;;; +0020;SPACE;Zs;0;WS;;;;;N;;;;; +0021;EXCLAMATION MARK;Po;0;ON;;;;;N;;;;; +0022;QUOTATION MARK;Po;0;ON;;;;;N;;;;; +0023;NUMBER SIGN;Po;0;ET;;;;;N;;;;; +0024;DOLLAR SIGN;Sc;0;ET;;;;;N;;;;; +0025;PERCENT SIGN;Po;0;ET;;;;;N;;;;; +0026;AMPERSAND;Po;0;ON;;;;;N;;;;; +0027;APOSTROPHE;Po;0;ON;;;;;N;APOSTROPHE-QUOTE;;;; +0028;LEFT PARENTHESIS;Ps;0;ON;;;;;Y;OPENING PARENTHESIS;;;; +0029;RIGHT PARENTHESIS;Pe;0;ON;;;;;Y;CLOSING PARENTHESIS;;;; +002A;ASTERISK;Po;0;ON;;;;;N;;;;; +002B;PLUS SIGN;Sm;0;ES;;;;;N;;;;; +002C;COMMA;Po;0;CS;;;;;N;;;;; +002D;HYPHEN-MINUS;Pd;0;ES;;;;;N;;;;; +002E;FULL STOP;Po;0;CS;;;;;N;PERIOD;;;; +002F;SOLIDUS;Po;0;CS;;;;;N;SLASH;;;; +0030;DIGIT ZERO;Nd;0;EN;;0;0;0;N;;;;; +0031;DIGIT ONE;Nd;0;EN;;1;1;1;N;;;;; +0032;DIGIT TWO;Nd;0;EN;;2;2;2;N;;;;; +0033;DIGIT THREE;Nd;0;EN;;3;3;3;N;;;;; +0034;DIGIT FOUR;Nd;0;EN;;4;4;4;N;;;;; +0035;DIGIT FIVE;Nd;0;EN;;5;5;5;N;;;;; +0036;DIGIT SIX;Nd;0;EN;;6;6;6;N;;;;; +0037;DIGIT SEVEN;Nd;0;EN;;7;7;7;N;;;;; +0038;DIGIT EIGHT;Nd;0;EN;;8;8;8;N;;;;; +0039;DIGIT NINE;Nd;0;EN;;9;9;9;N;;;;; +003A;COLON;Po;0;CS;;;;;N;;;;; +003B;SEMICOLON;Po;0;ON;;;;;N;;;;; +003C;LESS-THAN SIGN;Sm;0;ON;;;;;Y;;;;; +003D;EQUALS SIGN;Sm;0;ON;;;;;N;;;;; +003E;GREATER-THAN SIGN;Sm;0;ON;;;;;Y;;;;; +003F;QUESTION MARK;Po;0;ON;;;;;N;;;;; +0040;COMMERCIAL AT;Po;0;ON;;;;;N;;;;; +0041;LATIN CAPITAL LETTER A;Lu;0;L;;;;;N;;;;0061; +0042;LATIN CAPITAL LETTER B;Lu;0;L;;;;;N;;;;0062; +0043;LATIN CAPITAL LETTER C;Lu;0;L;;;;;N;;;;0063; +0044;LATIN CAPITAL LETTER D;Lu;0;L;;;;;N;;;;0064; +0045;LATIN CAPITAL LETTER E;Lu;0;L;;;;;N;;;;0065; +0046;LATIN CAPITAL LETTER F;Lu;0;L;;;;;N;;;;0066; +0047;LATIN CAPITAL LETTER G;Lu;0;L;;;;;N;;;;0067; +0048;LATIN CAPITAL LETTER H;Lu;0;L;;;;;N;;;;0068; +0049;LATIN CAPITAL LETTER I;Lu;0;L;;;;;N;;;;0069; +004A;LATIN CAPITAL LETTER J;Lu;0;L;;;;;N;;;;006A; +004B;LATIN CAPITAL LETTER K;Lu;0;L;;;;;N;;;;006B; +004C;LATIN CAPITAL LETTER L;Lu;0;L;;;;;N;;;;006C; +004D;LATIN CAPITAL LETTER M;Lu;0;L;;;;;N;;;;006D; +004E;LATIN CAPITAL LETTER N;Lu;0;L;;;;;N;;;;006E; +004F;LATIN CAPITAL LETTER O;Lu;0;L;;;;;N;;;;006F; +0050;LATIN CAPITAL LETTER P;Lu;0;L;;;;;N;;;;0070; +0051;LATIN CAPITAL LETTER Q;Lu;0;L;;;;;N;;;;0071; +0052;LATIN CAPITAL LETTER R;Lu;0;L;;;;;N;;;;0072; +0053;LATIN CAPITAL LETTER S;Lu;0;L;;;;;N;;;;0073; +0054;LATIN CAPITAL LETTER T;Lu;0;L;;;;;N;;;;0074; +0055;LATIN CAPITAL LETTER U;Lu;0;L;;;;;N;;;;0075; +0056;LATIN CAPITAL LETTER V;Lu;0;L;;;;;N;;;;0076; +0057;LATIN CAPITAL LETTER W;Lu;0;L;;;;;N;;;;0077; +0058;LATIN CAPITAL LETTER X;Lu;0;L;;;;;N;;;;0078; +0059;LATIN CAPITAL LETTER Y;Lu;0;L;;;;;N;;;;0079; +005A;LATIN CAPITAL LETTER Z;Lu;0;L;;;;;N;;;;007A; +005B;LEFT SQUARE BRACKET;Ps;0;ON;;;;;Y;OPENING SQUARE BRACKET;;;; +005C;REVERSE SOLIDUS;Po;0;ON;;;;;N;BACKSLASH;;;; +005D;RIGHT SQUARE BRACKET;Pe;0;ON;;;;;Y;CLOSING SQUARE BRACKET;;;; +005E;CIRCUMFLEX ACCENT;Sk;0;ON;;;;;N;SPACING CIRCUMFLEX;;;; +005F;LOW LINE;Pc;0;ON;;;;;N;SPACING UNDERSCORE;;;; +0060;GRAVE ACCENT;Sk;0;ON;;;;;N;SPACING GRAVE;;;; +0061;LATIN SMALL LETTER A;Ll;0;L;;;;;N;;;0041;;0041 +0062;LATIN SMALL LETTER B;Ll;0;L;;;;;N;;;0042;;0042 +0063;LATIN SMALL LETTER C;Ll;0;L;;;;;N;;;0043;;0043 +0064;LATIN SMALL LETTER D;Ll;0;L;;;;;N;;;0044;;0044 +0065;LATIN SMALL LETTER E;Ll;0;L;;;;;N;;;0045;;0045 +0066;LATIN SMALL LETTER F;Ll;0;L;;;;;N;;;0046;;0046 +0067;LATIN SMALL LETTER G;Ll;0;L;;;;;N;;;0047;;0047 +0068;LATIN SMALL LETTER H;Ll;0;L;;;;;N;;;0048;;0048 +0069;LATIN SMALL LETTER I;Ll;0;L;;;;;N;;;0049;;0049 +006A;LATIN SMALL LETTER J;Ll;0;L;;;;;N;;;004A;;004A +006B;LATIN SMALL LETTER K;Ll;0;L;;;;;N;;;004B;;004B +006C;LATIN SMALL LETTER L;Ll;0;L;;;;;N;;;004C;;004C +006D;LATIN SMALL LETTER M;Ll;0;L;;;;;N;;;004D;;004D +006E;LATIN SMALL LETTER N;Ll;0;L;;;;;N;;;004E;;004E +006F;LATIN SMALL LETTER O;Ll;0;L;;;;;N;;;004F;;004F +0070;LATIN SMALL LETTER P;Ll;0;L;;;;;N;;;0050;;0050 +0071;LATIN SMALL LETTER Q;Ll;0;L;;;;;N;;;0051;;0051 +0072;LATIN SMALL LETTER R;Ll;0;L;;;;;N;;;0052;;0052 +0073;LATIN SMALL LETTER S;Ll;0;L;;;;;N;;;0053;;0053 +0074;LATIN SMALL LETTER T;Ll;0;L;;;;;N;;;0054;;0054 +0075;LATIN SMALL LETTER U;Ll;0;L;;;;;N;;;0055;;0055 +0076;LATIN SMALL LETTER V;Ll;0;L;;;;;N;;;0056;;0056 +0077;LATIN SMALL LETTER W;Ll;0;L;;;;;N;;;0057;;0057 +0078;LATIN SMALL LETTER X;Ll;0;L;;;;;N;;;0058;;0058 +0079;LATIN SMALL LETTER Y;Ll;0;L;;;;;N;;;0059;;0059 +007A;LATIN SMALL LETTER Z;Ll;0;L;;;;;N;;;005A;;005A +007B;LEFT CURLY BRACKET;Ps;0;ON;;;;;Y;OPENING CURLY BRACKET;;;; +007C;VERTICAL LINE;Sm;0;ON;;;;;N;VERTICAL BAR;;;; +007D;RIGHT CURLY BRACKET;Pe;0;ON;;;;;Y;CLOSING CURLY BRACKET;;;; +007E;TILDE;Sm;0;ON;;;;;N;;;;; +007F;;Cc;0;BN;;;;;N;DELETE;;;; +0080;;Cc;0;BN;;;;;N;;;;; +0081;;Cc;0;BN;;;;;N;;;;; +0082;;Cc;0;BN;;;;;N;BREAK PERMITTED HERE;;;; +0083;;Cc;0;BN;;;;;N;NO BREAK HERE;;;; +0084;;Cc;0;BN;;;;;N;;;;; +0085;;Cc;0;B;;;;;N;NEXT LINE (NEL);;;; +0086;;Cc;0;BN;;;;;N;START OF SELECTED AREA;;;; +0087;;Cc;0;BN;;;;;N;END OF SELECTED AREA;;;; +0088;;Cc;0;BN;;;;;N;CHARACTER TABULATION SET;;;; +0089;;Cc;0;BN;;;;;N;CHARACTER TABULATION WITH JUSTIFICATION;;;; +008A;;Cc;0;BN;;;;;N;LINE TABULATION SET;;;; +008B;;Cc;0;BN;;;;;N;PARTIAL LINE FORWARD;;;; +008C;;Cc;0;BN;;;;;N;PARTIAL LINE BACKWARD;;;; +008D;;Cc;0;BN;;;;;N;REVERSE LINE FEED;;;; +008E;;Cc;0;BN;;;;;N;SINGLE SHIFT TWO;;;; +008F;;Cc;0;BN;;;;;N;SINGLE SHIFT THREE;;;; +0090;;Cc;0;BN;;;;;N;DEVICE CONTROL STRING;;;; +0091;;Cc;0;BN;;;;;N;PRIVATE USE ONE;;;; +0092;;Cc;0;BN;;;;;N;PRIVATE USE TWO;;;; +0093;;Cc;0;BN;;;;;N;SET TRANSMIT STATE;;;; +0094;;Cc;0;BN;;;;;N;CANCEL CHARACTER;;;; +0095;;Cc;0;BN;;;;;N;MESSAGE WAITING;;;; +0096;;Cc;0;BN;;;;;N;START OF GUARDED AREA;;;; +0097;;Cc;0;BN;;;;;N;END OF GUARDED AREA;;;; +0098;;Cc;0;BN;;;;;N;START OF STRING;;;; +0099;;Cc;0;BN;;;;;N;;;;; +009A;;Cc;0;BN;;;;;N;SINGLE CHARACTER INTRODUCER;;;; +009B;;Cc;0;BN;;;;;N;CONTROL SEQUENCE INTRODUCER;;;; +009C;;Cc;0;BN;;;;;N;STRING TERMINATOR;;;; +009D;;Cc;0;BN;;;;;N;OPERATING SYSTEM COMMAND;;;; +009E;;Cc;0;BN;;;;;N;PRIVACY MESSAGE;;;; +009F;;Cc;0;BN;;;;;N;APPLICATION PROGRAM COMMAND;;;; +00A0;NO-BREAK SPACE;Zs;0;CS; 0020;;;;N;NON-BREAKING SPACE;;;; +00A1;INVERTED EXCLAMATION MARK;Po;0;ON;;;;;N;;;;; +00A2;CENT SIGN;Sc;0;ET;;;;;N;;;;; +00A3;POUND SIGN;Sc;0;ET;;;;;N;;;;; +00A4;CURRENCY SIGN;Sc;0;ET;;;;;N;;;;; +00A5;YEN SIGN;Sc;0;ET;;;;;N;;;;; +00A6;BROKEN BAR;So;0;ON;;;;;N;BROKEN VERTICAL BAR;;;; +00A7;SECTION SIGN;So;0;ON;;;;;N;;;;; +00A8;DIAERESIS;Sk;0;ON; 0020 0308;;;;N;SPACING DIAERESIS;;;; +00A9;COPYRIGHT SIGN;So;0;ON;;;;;N;;;;; +00AA;FEMININE ORDINAL INDICATOR;Ll;0;L; 0061;;;;N;;;;; +00AB;LEFT-POINTING DOUBLE ANGLE QUOTATION MARK;Pi;0;ON;;;;;Y;LEFT POINTING GUILLEMET;*;;; +00AC;NOT SIGN;Sm;0;ON;;;;;N;;;;; +00AD;SOFT HYPHEN;Cf;0;BN;;;;;N;;;;; +00AE;REGISTERED SIGN;So;0;ON;;;;;N;REGISTERED TRADE MARK SIGN;;;; +00AF;MACRON;Sk;0;ON; 0020 0304;;;;N;SPACING MACRON;;;; +00B0;DEGREE SIGN;So;0;ET;;;;;N;;;;; +00B1;PLUS-MINUS SIGN;Sm;0;ET;;;;;N;PLUS-OR-MINUS SIGN;;;; +00B2;SUPERSCRIPT TWO;No;0;EN; 0032;;2;2;N;SUPERSCRIPT DIGIT TWO;;;; +00B3;SUPERSCRIPT THREE;No;0;EN; 0033;;3;3;N;SUPERSCRIPT DIGIT THREE;;;; +00B4;ACUTE ACCENT;Sk;0;ON; 0020 0301;;;;N;SPACING ACUTE;;;; +00B5;MICRO SIGN;Ll;0;L; 03BC;;;;N;;;039C;;039C +00B6;PILCROW SIGN;So;0;ON;;;;;N;PARAGRAPH SIGN;;;; +00B7;MIDDLE DOT;Po;0;ON;;;;;N;;;;; +00B8;CEDILLA;Sk;0;ON; 0020 0327;;;;N;SPACING CEDILLA;;;; +00B9;SUPERSCRIPT ONE;No;0;EN; 0031;;1;1;N;SUPERSCRIPT DIGIT ONE;;;; +00BA;MASCULINE ORDINAL INDICATOR;Ll;0;L; 006F;;;;N;;;;; +00BB;RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK;Pf;0;ON;;;;;Y;RIGHT POINTING GUILLEMET;*;;; +00BC;VULGAR FRACTION ONE QUARTER;No;0;ON; 0031 2044 0034;;;1/4;N;FRACTION ONE QUARTER;;;; +00BD;VULGAR FRACTION ONE HALF;No;0;ON; 0031 2044 0032;;;1/2;N;FRACTION ONE HALF;;;; +00BE;VULGAR FRACTION THREE QUARTERS;No;0;ON; 0033 2044 0034;;;3/4;N;FRACTION THREE QUARTERS;;;; +00BF;INVERTED QUESTION MARK;Po;0;ON;;;;;N;;;;; +00C0;LATIN CAPITAL LETTER A WITH GRAVE;Lu;0;L;0041 0300;;;;N;LATIN CAPITAL LETTER A GRAVE;;;00E0; +00C1;LATIN CAPITAL LETTER A WITH ACUTE;Lu;0;L;0041 0301;;;;N;LATIN CAPITAL LETTER A ACUTE;;;00E1; +00C2;LATIN CAPITAL LETTER A WITH CIRCUMFLEX;Lu;0;L;0041 0302;;;;N;LATIN CAPITAL LETTER A CIRCUMFLEX;;;00E2; +00C3;LATIN CAPITAL LETTER A WITH TILDE;Lu;0;L;0041 0303;;;;N;LATIN CAPITAL LETTER A TILDE;;;00E3; +00C4;LATIN CAPITAL LETTER A WITH DIAERESIS;Lu;0;L;0041 0308;;;;N;LATIN CAPITAL LETTER A DIAERESIS;;;00E4; +00C5;LATIN CAPITAL LETTER A WITH RING ABOVE;Lu;0;L;0041 030A;;;;N;LATIN CAPITAL LETTER A RING;;;00E5; +00C6;LATIN CAPITAL LETTER AE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER A E;ash *;;00E6; +00C7;LATIN CAPITAL LETTER C WITH CEDILLA;Lu;0;L;0043 0327;;;;N;LATIN CAPITAL LETTER C CEDILLA;;;00E7; +00C8;LATIN CAPITAL LETTER E WITH GRAVE;Lu;0;L;0045 0300;;;;N;LATIN CAPITAL LETTER E GRAVE;;;00E8; +00C9;LATIN CAPITAL LETTER E WITH ACUTE;Lu;0;L;0045 0301;;;;N;LATIN CAPITAL LETTER E ACUTE;;;00E9; +00CA;LATIN CAPITAL LETTER E WITH CIRCUMFLEX;Lu;0;L;0045 0302;;;;N;LATIN CAPITAL LETTER E CIRCUMFLEX;;;00EA; +00CB;LATIN CAPITAL LETTER E WITH DIAERESIS;Lu;0;L;0045 0308;;;;N;LATIN CAPITAL LETTER E DIAERESIS;;;00EB; +00CC;LATIN CAPITAL LETTER I WITH GRAVE;Lu;0;L;0049 0300;;;;N;LATIN CAPITAL LETTER I GRAVE;;;00EC; +00CD;LATIN CAPITAL LETTER I WITH ACUTE;Lu;0;L;0049 0301;;;;N;LATIN CAPITAL LETTER I ACUTE;;;00ED; +00CE;LATIN CAPITAL LETTER I WITH CIRCUMFLEX;Lu;0;L;0049 0302;;;;N;LATIN CAPITAL LETTER I CIRCUMFLEX;;;00EE; +00CF;LATIN CAPITAL LETTER I WITH DIAERESIS;Lu;0;L;0049 0308;;;;N;LATIN CAPITAL LETTER I DIAERESIS;;;00EF; +00D0;LATIN CAPITAL LETTER ETH;Lu;0;L;;;;;N;;Icelandic;;00F0; +00D1;LATIN CAPITAL LETTER N WITH TILDE;Lu;0;L;004E 0303;;;;N;LATIN CAPITAL LETTER N TILDE;;;00F1; +00D2;LATIN CAPITAL LETTER O WITH GRAVE;Lu;0;L;004F 0300;;;;N;LATIN CAPITAL LETTER O GRAVE;;;00F2; +00D3;LATIN CAPITAL LETTER O WITH ACUTE;Lu;0;L;004F 0301;;;;N;LATIN CAPITAL LETTER O ACUTE;;;00F3; +00D4;LATIN CAPITAL LETTER O WITH CIRCUMFLEX;Lu;0;L;004F 0302;;;;N;LATIN CAPITAL LETTER O CIRCUMFLEX;;;00F4; +00D5;LATIN CAPITAL LETTER O WITH TILDE;Lu;0;L;004F 0303;;;;N;LATIN CAPITAL LETTER O TILDE;;;00F5; +00D6;LATIN CAPITAL LETTER O WITH DIAERESIS;Lu;0;L;004F 0308;;;;N;LATIN CAPITAL LETTER O DIAERESIS;;;00F6; +00D7;MULTIPLICATION SIGN;Sm;0;ON;;;;;N;;;;; +00D8;LATIN CAPITAL LETTER O WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O SLASH;;;00F8; +00D9;LATIN CAPITAL LETTER U WITH GRAVE;Lu;0;L;0055 0300;;;;N;LATIN CAPITAL LETTER U GRAVE;;;00F9; +00DA;LATIN CAPITAL LETTER U WITH ACUTE;Lu;0;L;0055 0301;;;;N;LATIN CAPITAL LETTER U ACUTE;;;00FA; +00DB;LATIN CAPITAL LETTER U WITH CIRCUMFLEX;Lu;0;L;0055 0302;;;;N;LATIN CAPITAL LETTER U CIRCUMFLEX;;;00FB; +00DC;LATIN CAPITAL LETTER U WITH DIAERESIS;Lu;0;L;0055 0308;;;;N;LATIN CAPITAL LETTER U DIAERESIS;;;00FC; +00DD;LATIN CAPITAL LETTER Y WITH ACUTE;Lu;0;L;0059 0301;;;;N;LATIN CAPITAL LETTER Y ACUTE;;;00FD; +00DE;LATIN CAPITAL LETTER THORN;Lu;0;L;;;;;N;;Icelandic;;00FE; +00DF;LATIN SMALL LETTER SHARP S;Ll;0;L;;;;;N;;German;;; +00E0;LATIN SMALL LETTER A WITH GRAVE;Ll;0;L;0061 0300;;;;N;LATIN SMALL LETTER A GRAVE;;00C0;;00C0 +00E1;LATIN SMALL LETTER A WITH ACUTE;Ll;0;L;0061 0301;;;;N;LATIN SMALL LETTER A ACUTE;;00C1;;00C1 +00E2;LATIN SMALL LETTER A WITH CIRCUMFLEX;Ll;0;L;0061 0302;;;;N;LATIN SMALL LETTER A CIRCUMFLEX;;00C2;;00C2 +00E3;LATIN SMALL LETTER A WITH TILDE;Ll;0;L;0061 0303;;;;N;LATIN SMALL LETTER A TILDE;;00C3;;00C3 +00E4;LATIN SMALL LETTER A WITH DIAERESIS;Ll;0;L;0061 0308;;;;N;LATIN SMALL LETTER A DIAERESIS;;00C4;;00C4 +00E5;LATIN SMALL LETTER A WITH RING ABOVE;Ll;0;L;0061 030A;;;;N;LATIN SMALL LETTER A RING;;00C5;;00C5 +00E6;LATIN SMALL LETTER AE;Ll;0;L;;;;;N;LATIN SMALL LETTER A E;ash *;00C6;;00C6 +00E7;LATIN SMALL LETTER C WITH CEDILLA;Ll;0;L;0063 0327;;;;N;LATIN SMALL LETTER C CEDILLA;;00C7;;00C7 +00E8;LATIN SMALL LETTER E WITH GRAVE;Ll;0;L;0065 0300;;;;N;LATIN SMALL LETTER E GRAVE;;00C8;;00C8 +00E9;LATIN SMALL LETTER E WITH ACUTE;Ll;0;L;0065 0301;;;;N;LATIN SMALL LETTER E ACUTE;;00C9;;00C9 +00EA;LATIN SMALL LETTER E WITH CIRCUMFLEX;Ll;0;L;0065 0302;;;;N;LATIN SMALL LETTER E CIRCUMFLEX;;00CA;;00CA +00EB;LATIN SMALL LETTER E WITH DIAERESIS;Ll;0;L;0065 0308;;;;N;LATIN SMALL LETTER E DIAERESIS;;00CB;;00CB +00EC;LATIN SMALL LETTER I WITH GRAVE;Ll;0;L;0069 0300;;;;N;LATIN SMALL LETTER I GRAVE;;00CC;;00CC +00ED;LATIN SMALL LETTER I WITH ACUTE;Ll;0;L;0069 0301;;;;N;LATIN SMALL LETTER I ACUTE;;00CD;;00CD +00EE;LATIN SMALL LETTER I WITH CIRCUMFLEX;Ll;0;L;0069 0302;;;;N;LATIN SMALL LETTER I CIRCUMFLEX;;00CE;;00CE +00EF;LATIN SMALL LETTER I WITH DIAERESIS;Ll;0;L;0069 0308;;;;N;LATIN SMALL LETTER I DIAERESIS;;00CF;;00CF +00F0;LATIN SMALL LETTER ETH;Ll;0;L;;;;;N;;Icelandic;00D0;;00D0 +00F1;LATIN SMALL LETTER N WITH TILDE;Ll;0;L;006E 0303;;;;N;LATIN SMALL LETTER N TILDE;;00D1;;00D1 +00F2;LATIN SMALL LETTER O WITH GRAVE;Ll;0;L;006F 0300;;;;N;LATIN SMALL LETTER O GRAVE;;00D2;;00D2 +00F3;LATIN SMALL LETTER O WITH ACUTE;Ll;0;L;006F 0301;;;;N;LATIN SMALL LETTER O ACUTE;;00D3;;00D3 +00F4;LATIN SMALL LETTER O WITH CIRCUMFLEX;Ll;0;L;006F 0302;;;;N;LATIN SMALL LETTER O CIRCUMFLEX;;00D4;;00D4 +00F5;LATIN SMALL LETTER O WITH TILDE;Ll;0;L;006F 0303;;;;N;LATIN SMALL LETTER O TILDE;;00D5;;00D5 +00F6;LATIN SMALL LETTER O WITH DIAERESIS;Ll;0;L;006F 0308;;;;N;LATIN SMALL LETTER O DIAERESIS;;00D6;;00D6 +00F7;DIVISION SIGN;Sm;0;ON;;;;;N;;;;; +00F8;LATIN SMALL LETTER O WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER O SLASH;;00D8;;00D8 +00F9;LATIN SMALL LETTER U WITH GRAVE;Ll;0;L;0075 0300;;;;N;LATIN SMALL LETTER U GRAVE;;00D9;;00D9 +00FA;LATIN SMALL LETTER U WITH ACUTE;Ll;0;L;0075 0301;;;;N;LATIN SMALL LETTER U ACUTE;;00DA;;00DA +00FB;LATIN SMALL LETTER U WITH CIRCUMFLEX;Ll;0;L;0075 0302;;;;N;LATIN SMALL LETTER U CIRCUMFLEX;;00DB;;00DB +00FC;LATIN SMALL LETTER U WITH DIAERESIS;Ll;0;L;0075 0308;;;;N;LATIN SMALL LETTER U DIAERESIS;;00DC;;00DC +00FD;LATIN SMALL LETTER Y WITH ACUTE;Ll;0;L;0079 0301;;;;N;LATIN SMALL LETTER Y ACUTE;;00DD;;00DD +00FE;LATIN SMALL LETTER THORN;Ll;0;L;;;;;N;;Icelandic;00DE;;00DE +00FF;LATIN SMALL LETTER Y WITH DIAERESIS;Ll;0;L;0079 0308;;;;N;LATIN SMALL LETTER Y DIAERESIS;;0178;;0178 +0100;LATIN CAPITAL LETTER A WITH MACRON;Lu;0;L;0041 0304;;;;N;LATIN CAPITAL LETTER A MACRON;;;0101; +0101;LATIN SMALL LETTER A WITH MACRON;Ll;0;L;0061 0304;;;;N;LATIN SMALL LETTER A MACRON;;0100;;0100 +0102;LATIN CAPITAL LETTER A WITH BREVE;Lu;0;L;0041 0306;;;;N;LATIN CAPITAL LETTER A BREVE;;;0103; +0103;LATIN SMALL LETTER A WITH BREVE;Ll;0;L;0061 0306;;;;N;LATIN SMALL LETTER A BREVE;;0102;;0102 +0104;LATIN CAPITAL LETTER A WITH OGONEK;Lu;0;L;0041 0328;;;;N;LATIN CAPITAL LETTER A OGONEK;;;0105; +0105;LATIN SMALL LETTER A WITH OGONEK;Ll;0;L;0061 0328;;;;N;LATIN SMALL LETTER A OGONEK;;0104;;0104 +0106;LATIN CAPITAL LETTER C WITH ACUTE;Lu;0;L;0043 0301;;;;N;LATIN CAPITAL LETTER C ACUTE;;;0107; +0107;LATIN SMALL LETTER C WITH ACUTE;Ll;0;L;0063 0301;;;;N;LATIN SMALL LETTER C ACUTE;;0106;;0106 +0108;LATIN CAPITAL LETTER C WITH CIRCUMFLEX;Lu;0;L;0043 0302;;;;N;LATIN CAPITAL LETTER C CIRCUMFLEX;;;0109; +0109;LATIN SMALL LETTER C WITH CIRCUMFLEX;Ll;0;L;0063 0302;;;;N;LATIN SMALL LETTER C CIRCUMFLEX;;0108;;0108 +010A;LATIN CAPITAL LETTER C WITH DOT ABOVE;Lu;0;L;0043 0307;;;;N;LATIN CAPITAL LETTER C DOT;;;010B; +010B;LATIN SMALL LETTER C WITH DOT ABOVE;Ll;0;L;0063 0307;;;;N;LATIN SMALL LETTER C DOT;;010A;;010A +010C;LATIN CAPITAL LETTER C WITH CARON;Lu;0;L;0043 030C;;;;N;LATIN CAPITAL LETTER C HACEK;;;010D; +010D;LATIN SMALL LETTER C WITH CARON;Ll;0;L;0063 030C;;;;N;LATIN SMALL LETTER C HACEK;;010C;;010C +010E;LATIN CAPITAL LETTER D WITH CARON;Lu;0;L;0044 030C;;;;N;LATIN CAPITAL LETTER D HACEK;;;010F; +010F;LATIN SMALL LETTER D WITH CARON;Ll;0;L;0064 030C;;;;N;LATIN SMALL LETTER D HACEK;;010E;;010E +0110;LATIN CAPITAL LETTER D WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D BAR;;;0111; +0111;LATIN SMALL LETTER D WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER D BAR;;0110;;0110 +0112;LATIN CAPITAL LETTER E WITH MACRON;Lu;0;L;0045 0304;;;;N;LATIN CAPITAL LETTER E MACRON;;;0113; +0113;LATIN SMALL LETTER E WITH MACRON;Ll;0;L;0065 0304;;;;N;LATIN SMALL LETTER E MACRON;;0112;;0112 +0116;LATIN CAPITAL LETTER E WITH DOT ABOVE;Lu;0;L;0045 0307;;;;N;LATIN CAPITAL LETTER E DOT;;;0117; +0117;LATIN SMALL LETTER E WITH DOT ABOVE;Ll;0;L;0065 0307;;;;N;LATIN SMALL LETTER E DOT;;0116;;0116 +0118;LATIN CAPITAL LETTER E WITH OGONEK;Lu;0;L;0045 0328;;;;N;LATIN CAPITAL LETTER E OGONEK;;;0119; +0119;LATIN SMALL LETTER E WITH OGONEK;Ll;0;L;0065 0328;;;;N;LATIN SMALL LETTER E OGONEK;;0118;;0118 +011A;LATIN CAPITAL LETTER E WITH CARON;Lu;0;L;0045 030C;;;;N;LATIN CAPITAL LETTER E HACEK;;;011B; +011B;LATIN SMALL LETTER E WITH CARON;Ll;0;L;0065 030C;;;;N;LATIN SMALL LETTER E HACEK;;011A;;011A +011C;LATIN CAPITAL LETTER G WITH CIRCUMFLEX;Lu;0;L;0047 0302;;;;N;LATIN CAPITAL LETTER G CIRCUMFLEX;;;011D; +011D;LATIN SMALL LETTER G WITH CIRCUMFLEX;Ll;0;L;0067 0302;;;;N;LATIN SMALL LETTER G CIRCUMFLEX;;011C;;011C +011E;LATIN CAPITAL LETTER G WITH BREVE;Lu;0;L;0047 0306;;;;N;LATIN CAPITAL LETTER G BREVE;;;011F; +011F;LATIN SMALL LETTER G WITH BREVE;Ll;0;L;0067 0306;;;;N;LATIN SMALL LETTER G BREVE;;011E;;011E +0120;LATIN CAPITAL LETTER G WITH DOT ABOVE;Lu;0;L;0047 0307;;;;N;LATIN CAPITAL LETTER G DOT;;;0121; +0121;LATIN SMALL LETTER G WITH DOT ABOVE;Ll;0;L;0067 0307;;;;N;LATIN SMALL LETTER G DOT;;0120;;0120 +0122;LATIN CAPITAL LETTER G WITH CEDILLA;Lu;0;L;0047 0327;;;;N;LATIN CAPITAL LETTER G CEDILLA;;;0123; +0123;LATIN SMALL LETTER G WITH CEDILLA;Ll;0;L;0067 0327;;;;N;LATIN SMALL LETTER G CEDILLA;;0122;;0122 +0124;LATIN CAPITAL LETTER H WITH CIRCUMFLEX;Lu;0;L;0048 0302;;;;N;LATIN CAPITAL LETTER H CIRCUMFLEX;;;0125; +0125;LATIN SMALL LETTER H WITH CIRCUMFLEX;Ll;0;L;0068 0302;;;;N;LATIN SMALL LETTER H CIRCUMFLEX;;0124;;0124 +0126;LATIN CAPITAL LETTER H WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER H BAR;;;0127; +0127;LATIN SMALL LETTER H WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER H BAR;;0126;;0126 +0128;LATIN CAPITAL LETTER I WITH TILDE;Lu;0;L;0049 0303;;;;N;LATIN CAPITAL LETTER I TILDE;;;0129; +0129;LATIN SMALL LETTER I WITH TILDE;Ll;0;L;0069 0303;;;;N;LATIN SMALL LETTER I TILDE;;0128;;0128 +012A;LATIN CAPITAL LETTER I WITH MACRON;Lu;0;L;0049 0304;;;;N;LATIN CAPITAL LETTER I MACRON;;;012B; +012B;LATIN SMALL LETTER I WITH MACRON;Ll;0;L;0069 0304;;;;N;LATIN SMALL LETTER I MACRON;;012A;;012A +012E;LATIN CAPITAL LETTER I WITH OGONEK;Lu;0;L;0049 0328;;;;N;LATIN CAPITAL LETTER I OGONEK;;;012F; +012F;LATIN SMALL LETTER I WITH OGONEK;Ll;0;L;0069 0328;;;;N;LATIN SMALL LETTER I OGONEK;;012E;;012E +0130;LATIN CAPITAL LETTER I WITH DOT ABOVE;Lu;0;L;0049 0307;;;;N;LATIN CAPITAL LETTER I DOT;;;0069; +0131;LATIN SMALL LETTER DOTLESS I;Ll;0;L;;;;;N;;;0049;;0049 +0134;LATIN CAPITAL LETTER J WITH CIRCUMFLEX;Lu;0;L;004A 0302;;;;N;LATIN CAPITAL LETTER J CIRCUMFLEX;;;0135; +0135;LATIN SMALL LETTER J WITH CIRCUMFLEX;Ll;0;L;006A 0302;;;;N;LATIN SMALL LETTER J CIRCUMFLEX;;0134;;0134 +0136;LATIN CAPITAL LETTER K WITH CEDILLA;Lu;0;L;004B 0327;;;;N;LATIN CAPITAL LETTER K CEDILLA;;;0137; +0137;LATIN SMALL LETTER K WITH CEDILLA;Ll;0;L;006B 0327;;;;N;LATIN SMALL LETTER K CEDILLA;;0136;;0136 +0138;LATIN SMALL LETTER KRA;Ll;0;L;;;;;N;;Greenlandic;;; +0139;LATIN CAPITAL LETTER L WITH ACUTE;Lu;0;L;004C 0301;;;;N;LATIN CAPITAL LETTER L ACUTE;;;013A; +013A;LATIN SMALL LETTER L WITH ACUTE;Ll;0;L;006C 0301;;;;N;LATIN SMALL LETTER L ACUTE;;0139;;0139 +013B;LATIN CAPITAL LETTER L WITH CEDILLA;Lu;0;L;004C 0327;;;;N;LATIN CAPITAL LETTER L CEDILLA;;;013C; +013C;LATIN SMALL LETTER L WITH CEDILLA;Ll;0;L;006C 0327;;;;N;LATIN SMALL LETTER L CEDILLA;;013B;;013B +013D;LATIN CAPITAL LETTER L WITH CARON;Lu;0;L;004C 030C;;;;N;LATIN CAPITAL LETTER L HACEK;;;013E; +013E;LATIN SMALL LETTER L WITH CARON;Ll;0;L;006C 030C;;;;N;LATIN SMALL LETTER L HACEK;;013D;;013D +0141;LATIN CAPITAL LETTER L WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER L SLASH;;;0142; +0142;LATIN SMALL LETTER L WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER L SLASH;;0141;;0141 +0143;LATIN CAPITAL LETTER N WITH ACUTE;Lu;0;L;004E 0301;;;;N;LATIN CAPITAL LETTER N ACUTE;;;0144; +0144;LATIN SMALL LETTER N WITH ACUTE;Ll;0;L;006E 0301;;;;N;LATIN SMALL LETTER N ACUTE;;0143;;0143 +0145;LATIN CAPITAL LETTER N WITH CEDILLA;Lu;0;L;004E 0327;;;;N;LATIN CAPITAL LETTER N CEDILLA;;;0146; +0146;LATIN SMALL LETTER N WITH CEDILLA;Ll;0;L;006E 0327;;;;N;LATIN SMALL LETTER N CEDILLA;;0145;;0145 +0147;LATIN CAPITAL LETTER N WITH CARON;Lu;0;L;004E 030C;;;;N;LATIN CAPITAL LETTER N HACEK;;;0148; +0148;LATIN SMALL LETTER N WITH CARON;Ll;0;L;006E 030C;;;;N;LATIN SMALL LETTER N HACEK;;0147;;0147 +014A;LATIN CAPITAL LETTER ENG;Lu;0;L;;;;;N;;Sami;;014B; +014B;LATIN SMALL LETTER ENG;Ll;0;L;;;;;N;;Sami;014A;;014A +014C;LATIN CAPITAL LETTER O WITH MACRON;Lu;0;L;004F 0304;;;;N;LATIN CAPITAL LETTER O MACRON;;;014D; +014D;LATIN SMALL LETTER O WITH MACRON;Ll;0;L;006F 0304;;;;N;LATIN SMALL LETTER O MACRON;;014C;;014C +0150;LATIN CAPITAL LETTER O WITH DOUBLE ACUTE;Lu;0;L;004F 030B;;;;N;LATIN CAPITAL LETTER O DOUBLE ACUTE;;;0151; +0151;LATIN SMALL LETTER O WITH DOUBLE ACUTE;Ll;0;L;006F 030B;;;;N;LATIN SMALL LETTER O DOUBLE ACUTE;;0150;;0150 +0152;LATIN CAPITAL LIGATURE OE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O E;;;0153; +0153;LATIN SMALL LIGATURE OE;Ll;0;L;;;;;N;LATIN SMALL LETTER O E;;0152;;0152 +0154;LATIN CAPITAL LETTER R WITH ACUTE;Lu;0;L;0052 0301;;;;N;LATIN CAPITAL LETTER R ACUTE;;;0155; +0155;LATIN SMALL LETTER R WITH ACUTE;Ll;0;L;0072 0301;;;;N;LATIN SMALL LETTER R ACUTE;;0154;;0154 +0156;LATIN CAPITAL LETTER R WITH CEDILLA;Lu;0;L;0052 0327;;;;N;LATIN CAPITAL LETTER R CEDILLA;;;0157; +0157;LATIN SMALL LETTER R WITH CEDILLA;Ll;0;L;0072 0327;;;;N;LATIN SMALL LETTER R CEDILLA;;0156;;0156 +0158;LATIN CAPITAL LETTER R WITH CARON;Lu;0;L;0052 030C;;;;N;LATIN CAPITAL LETTER R HACEK;;;0159; +0159;LATIN SMALL LETTER R WITH CARON;Ll;0;L;0072 030C;;;;N;LATIN SMALL LETTER R HACEK;;0158;;0158 +015A;LATIN CAPITAL LETTER S WITH ACUTE;Lu;0;L;0053 0301;;;;N;LATIN CAPITAL LETTER S ACUTE;;;015B; +015B;LATIN SMALL LETTER S WITH ACUTE;Ll;0;L;0073 0301;;;;N;LATIN SMALL LETTER S ACUTE;;015A;;015A +015C;LATIN CAPITAL LETTER S WITH CIRCUMFLEX;Lu;0;L;0053 0302;;;;N;LATIN CAPITAL LETTER S CIRCUMFLEX;;;015D; +015D;LATIN SMALL LETTER S WITH CIRCUMFLEX;Ll;0;L;0073 0302;;;;N;LATIN SMALL LETTER S CIRCUMFLEX;;015C;;015C +015E;LATIN CAPITAL LETTER S WITH CEDILLA;Lu;0;L;0053 0327;;;;N;LATIN CAPITAL LETTER S CEDILLA;*;;015F; +015F;LATIN SMALL LETTER S WITH CEDILLA;Ll;0;L;0073 0327;;;;N;LATIN SMALL LETTER S CEDILLA;*;015E;;015E +0160;LATIN CAPITAL LETTER S WITH CARON;Lu;0;L;0053 030C;;;;N;LATIN CAPITAL LETTER S HACEK;;;0161; +0161;LATIN SMALL LETTER S WITH CARON;Ll;0;L;0073 030C;;;;N;LATIN SMALL LETTER S HACEK;;0160;;0160 +0162;LATIN CAPITAL LETTER T WITH CEDILLA;Lu;0;L;0054 0327;;;;N;LATIN CAPITAL LETTER T CEDILLA;*;;0163; +0163;LATIN SMALL LETTER T WITH CEDILLA;Ll;0;L;0074 0327;;;;N;LATIN SMALL LETTER T CEDILLA;*;0162;;0162 +0164;LATIN CAPITAL LETTER T WITH CARON;Lu;0;L;0054 030C;;;;N;LATIN CAPITAL LETTER T HACEK;;;0165; +0165;LATIN SMALL LETTER T WITH CARON;Ll;0;L;0074 030C;;;;N;LATIN SMALL LETTER T HACEK;;0164;;0164 +0166;LATIN CAPITAL LETTER T WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T BAR;;;0167; +0167;LATIN SMALL LETTER T WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER T BAR;;0166;;0166 +0168;LATIN CAPITAL LETTER U WITH TILDE;Lu;0;L;0055 0303;;;;N;LATIN CAPITAL LETTER U TILDE;;;0169; +0169;LATIN SMALL LETTER U WITH TILDE;Ll;0;L;0075 0303;;;;N;LATIN SMALL LETTER U TILDE;;0168;;0168 +016A;LATIN CAPITAL LETTER U WITH MACRON;Lu;0;L;0055 0304;;;;N;LATIN CAPITAL LETTER U MACRON;;;016B; +016B;LATIN SMALL LETTER U WITH MACRON;Ll;0;L;0075 0304;;;;N;LATIN SMALL LETTER U MACRON;;016A;;016A +016C;LATIN CAPITAL LETTER U WITH BREVE;Lu;0;L;0055 0306;;;;N;LATIN CAPITAL LETTER U BREVE;;;016D; +016D;LATIN SMALL LETTER U WITH BREVE;Ll;0;L;0075 0306;;;;N;LATIN SMALL LETTER U BREVE;;016C;;016C +016E;LATIN CAPITAL LETTER U WITH RING ABOVE;Lu;0;L;0055 030A;;;;N;LATIN CAPITAL LETTER U RING;;;016F; +016F;LATIN SMALL LETTER U WITH RING ABOVE;Ll;0;L;0075 030A;;;;N;LATIN SMALL LETTER U RING;;016E;;016E +0170;LATIN CAPITAL LETTER U WITH DOUBLE ACUTE;Lu;0;L;0055 030B;;;;N;LATIN CAPITAL LETTER U DOUBLE ACUTE;;;0171; +0171;LATIN SMALL LETTER U WITH DOUBLE ACUTE;Ll;0;L;0075 030B;;;;N;LATIN SMALL LETTER U DOUBLE ACUTE;;0170;;0170 +0172;LATIN CAPITAL LETTER U WITH OGONEK;Lu;0;L;0055 0328;;;;N;LATIN CAPITAL LETTER U OGONEK;;;0173; +0173;LATIN SMALL LETTER U WITH OGONEK;Ll;0;L;0075 0328;;;;N;LATIN SMALL LETTER U OGONEK;;0172;;0172 +0174;LATIN CAPITAL LETTER W WITH CIRCUMFLEX;Lu;0;L;0057 0302;;;;N;LATIN CAPITAL LETTER W CIRCUMFLEX;;;0175; +0175;LATIN SMALL LETTER W WITH CIRCUMFLEX;Ll;0;L;0077 0302;;;;N;LATIN SMALL LETTER W CIRCUMFLEX;;0174;;0174 +0176;LATIN CAPITAL LETTER Y WITH CIRCUMFLEX;Lu;0;L;0059 0302;;;;N;LATIN CAPITAL LETTER Y CIRCUMFLEX;;;0177; +0177;LATIN SMALL LETTER Y WITH CIRCUMFLEX;Ll;0;L;0079 0302;;;;N;LATIN SMALL LETTER Y CIRCUMFLEX;;0176;;0176 +0178;LATIN CAPITAL LETTER Y WITH DIAERESIS;Lu;0;L;0059 0308;;;;N;LATIN CAPITAL LETTER Y DIAERESIS;;;00FF; +0179;LATIN CAPITAL LETTER Z WITH ACUTE;Lu;0;L;005A 0301;;;;N;LATIN CAPITAL LETTER Z ACUTE;;;017A; +017A;LATIN SMALL LETTER Z WITH ACUTE;Ll;0;L;007A 0301;;;;N;LATIN SMALL LETTER Z ACUTE;;0179;;0179 +017B;LATIN CAPITAL LETTER Z WITH DOT ABOVE;Lu;0;L;005A 0307;;;;N;LATIN CAPITAL LETTER Z DOT;;;017C; +017C;LATIN SMALL LETTER Z WITH DOT ABOVE;Ll;0;L;007A 0307;;;;N;LATIN SMALL LETTER Z DOT;;017B;;017B +017D;LATIN CAPITAL LETTER Z WITH CARON;Lu;0;L;005A 030C;;;;N;LATIN CAPITAL LETTER Z HACEK;;;017E; +017E;LATIN SMALL LETTER Z WITH CARON;Ll;0;L;007A 030C;;;;N;LATIN SMALL LETTER Z HACEK;;017D;;017D +0191;LATIN CAPITAL LETTER F WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER F HOOK;;;0192; +0192;LATIN SMALL LETTER F WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT F;;0191;;0191 +01A0;LATIN CAPITAL LETTER O WITH HORN;Lu;0;L;004F 031B;;;;N;LATIN CAPITAL LETTER O HORN;;;01A1; +01A1;LATIN SMALL LETTER O WITH HORN;Ll;0;L;006F 031B;;;;N;LATIN SMALL LETTER O HORN;;01A0;;01A0 +01AF;LATIN CAPITAL LETTER U WITH HORN;Lu;0;L;0055 031B;;;;N;LATIN CAPITAL LETTER U HORN;;;01B0; +01B0;LATIN SMALL LETTER U WITH HORN;Ll;0;L;0075 031B;;;;N;LATIN SMALL LETTER U HORN;;01AF;;01AF +0218;LATIN CAPITAL LETTER S WITH COMMA BELOW;Lu;0;L;0053 0326;;;;N;;*;;0219; +0219;LATIN SMALL LETTER S WITH COMMA BELOW;Ll;0;L;0073 0326;;;;N;;*;0218;;0218 +021A;LATIN CAPITAL LETTER T WITH COMMA BELOW;Lu;0;L;0054 0326;;;;N;;*;;021B; +021B;LATIN SMALL LETTER T WITH COMMA BELOW;Ll;0;L;0074 0326;;;;N;;*;021A;;021A +02C6;MODIFIER LETTER CIRCUMFLEX ACCENT;Lm;0;ON;;;;;N;MODIFIER LETTER CIRCUMFLEX;;;; +02C7;CARON;Lm;0;ON;;;;;N;MODIFIER LETTER HACEK;Mandarin Chinese third tone;;; +02D8;BREVE;Sk;0;ON; 0020 0306;;;;N;SPACING BREVE;;;; +02D9;DOT ABOVE;Sk;0;ON; 0020 0307;;;;N;SPACING DOT ABOVE;Mandarin Chinese light tone;;; +02DB;OGONEK;Sk;0;ON; 0020 0328;;;;N;SPACING OGONEK;;;; +02DC;SMALL TILDE;Sk;0;ON; 0020 0303;;;;N;SPACING TILDE;;;; +02DD;DOUBLE ACUTE ACCENT;Sk;0;ON; 0020 030B;;;;N;SPACING DOUBLE ACUTE;;;; +0300;COMBINING GRAVE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING GRAVE;Varia;;; +0301;COMBINING ACUTE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING ACUTE;Oxia, Tonos;;; +0303;COMBINING TILDE;Mn;230;NSM;;;;;N;NON-SPACING TILDE;;;; +0309;COMBINING HOOK ABOVE;Mn;230;NSM;;;;;N;NON-SPACING HOOK ABOVE;;;; +0323;COMBINING DOT BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOT BELOW;;;; +037A;GREEK YPOGEGRAMMENI;Lm;0;L; 0020 0345;;;;N;GREEK SPACING IOTA BELOW;;;; +0384;GREEK TONOS;Sk;0;ON; 0020 0301;;;;N;GREEK SPACING TONOS;;;; +0385;GREEK DIALYTIKA TONOS;Sk;0;ON;00A8 0301;;;;N;GREEK SPACING DIAERESIS TONOS;;;; +0386;GREEK CAPITAL LETTER ALPHA WITH TONOS;Lu;0;L;0391 0301;;;;N;GREEK CAPITAL LETTER ALPHA TONOS;;;03AC; +0388;GREEK CAPITAL LETTER EPSILON WITH TONOS;Lu;0;L;0395 0301;;;;N;GREEK CAPITAL LETTER EPSILON TONOS;;;03AD; +0389;GREEK CAPITAL LETTER ETA WITH TONOS;Lu;0;L;0397 0301;;;;N;GREEK CAPITAL LETTER ETA TONOS;;;03AE; +038A;GREEK CAPITAL LETTER IOTA WITH TONOS;Lu;0;L;0399 0301;;;;N;GREEK CAPITAL LETTER IOTA TONOS;;;03AF; +038C;GREEK CAPITAL LETTER OMICRON WITH TONOS;Lu;0;L;039F 0301;;;;N;GREEK CAPITAL LETTER OMICRON TONOS;;;03CC; +038E;GREEK CAPITAL LETTER UPSILON WITH TONOS;Lu;0;L;03A5 0301;;;;N;GREEK CAPITAL LETTER UPSILON TONOS;;;03CD; +038F;GREEK CAPITAL LETTER OMEGA WITH TONOS;Lu;0;L;03A9 0301;;;;N;GREEK CAPITAL LETTER OMEGA TONOS;;;03CE; +0390;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS;Ll;0;L;03CA 0301;;;;N;GREEK SMALL LETTER IOTA DIAERESIS TONOS;;;; +0391;GREEK CAPITAL LETTER ALPHA;Lu;0;L;;;;;N;;;;03B1; +0392;GREEK CAPITAL LETTER BETA;Lu;0;L;;;;;N;;;;03B2; +0393;GREEK CAPITAL LETTER GAMMA;Lu;0;L;;;;;N;;;;03B3; +0394;GREEK CAPITAL LETTER DELTA;Lu;0;L;;;;;N;;;;03B4; +0395;GREEK CAPITAL LETTER EPSILON;Lu;0;L;;;;;N;;;;03B5; +0396;GREEK CAPITAL LETTER ZETA;Lu;0;L;;;;;N;;;;03B6; +0397;GREEK CAPITAL LETTER ETA;Lu;0;L;;;;;N;;;;03B7; +0398;GREEK CAPITAL LETTER THETA;Lu;0;L;;;;;N;;;;03B8; +0399;GREEK CAPITAL LETTER IOTA;Lu;0;L;;;;;N;;;;03B9; +039A;GREEK CAPITAL LETTER KAPPA;Lu;0;L;;;;;N;;;;03BA; +039B;GREEK CAPITAL LETTER LAMDA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER LAMBDA;;;03BB; +039C;GREEK CAPITAL LETTER MU;Lu;0;L;;;;;N;;;;03BC; +039D;GREEK CAPITAL LETTER NU;Lu;0;L;;;;;N;;;;03BD; +039E;GREEK CAPITAL LETTER XI;Lu;0;L;;;;;N;;;;03BE; +039F;GREEK CAPITAL LETTER OMICRON;Lu;0;L;;;;;N;;;;03BF; +03A0;GREEK CAPITAL LETTER PI;Lu;0;L;;;;;N;;;;03C0; +03A1;GREEK CAPITAL LETTER RHO;Lu;0;L;;;;;N;;;;03C1; +03A3;GREEK CAPITAL LETTER SIGMA;Lu;0;L;;;;;N;;;;03C3; +03A4;GREEK CAPITAL LETTER TAU;Lu;0;L;;;;;N;;;;03C4; +03A5;GREEK CAPITAL LETTER UPSILON;Lu;0;L;;;;;N;;;;03C5; +03A6;GREEK CAPITAL LETTER PHI;Lu;0;L;;;;;N;;;;03C6; +03A7;GREEK CAPITAL LETTER CHI;Lu;0;L;;;;;N;;;;03C7; +03A8;GREEK CAPITAL LETTER PSI;Lu;0;L;;;;;N;;;;03C8; +03A9;GREEK CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;03C9; +03AA;GREEK CAPITAL LETTER IOTA WITH DIALYTIKA;Lu;0;L;0399 0308;;;;N;GREEK CAPITAL LETTER IOTA DIAERESIS;;;03CA; +03AB;GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA;Lu;0;L;03A5 0308;;;;N;GREEK CAPITAL LETTER UPSILON DIAERESIS;;;03CB; +03AC;GREEK SMALL LETTER ALPHA WITH TONOS;Ll;0;L;03B1 0301;;;;N;GREEK SMALL LETTER ALPHA TONOS;;0386;;0386 +03AD;GREEK SMALL LETTER EPSILON WITH TONOS;Ll;0;L;03B5 0301;;;;N;GREEK SMALL LETTER EPSILON TONOS;;0388;;0388 +03AE;GREEK SMALL LETTER ETA WITH TONOS;Ll;0;L;03B7 0301;;;;N;GREEK SMALL LETTER ETA TONOS;;0389;;0389 +03AF;GREEK SMALL LETTER IOTA WITH TONOS;Ll;0;L;03B9 0301;;;;N;GREEK SMALL LETTER IOTA TONOS;;038A;;038A +03B0;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS;Ll;0;L;03CB 0301;;;;N;GREEK SMALL LETTER UPSILON DIAERESIS TONOS;;;; +03B1;GREEK SMALL LETTER ALPHA;Ll;0;L;;;;;N;;;0391;;0391 +03B2;GREEK SMALL LETTER BETA;Ll;0;L;;;;;N;;;0392;;0392 +03B3;GREEK SMALL LETTER GAMMA;Ll;0;L;;;;;N;;;0393;;0393 +03B4;GREEK SMALL LETTER DELTA;Ll;0;L;;;;;N;;;0394;;0394 +03B5;GREEK SMALL LETTER EPSILON;Ll;0;L;;;;;N;;;0395;;0395 +03B6;GREEK SMALL LETTER ZETA;Ll;0;L;;;;;N;;;0396;;0396 +03B7;GREEK SMALL LETTER ETA;Ll;0;L;;;;;N;;;0397;;0397 +03B8;GREEK SMALL LETTER THETA;Ll;0;L;;;;;N;;;0398;;0398 +03B9;GREEK SMALL LETTER IOTA;Ll;0;L;;;;;N;;;0399;;0399 +03BA;GREEK SMALL LETTER KAPPA;Ll;0;L;;;;;N;;;039A;;039A +03BB;GREEK SMALL LETTER LAMDA;Ll;0;L;;;;;N;GREEK SMALL LETTER LAMBDA;;039B;;039B +03BC;GREEK SMALL LETTER MU;Ll;0;L;;;;;N;;;039C;;039C +03BD;GREEK SMALL LETTER NU;Ll;0;L;;;;;N;;;039D;;039D +03BE;GREEK SMALL LETTER XI;Ll;0;L;;;;;N;;;039E;;039E +03BF;GREEK SMALL LETTER OMICRON;Ll;0;L;;;;;N;;;039F;;039F +03C0;GREEK SMALL LETTER PI;Ll;0;L;;;;;N;;;03A0;;03A0 +03C1;GREEK SMALL LETTER RHO;Ll;0;L;;;;;N;;;03A1;;03A1 +03C2;GREEK SMALL LETTER FINAL SIGMA;Ll;0;L;;;;;N;;;03A3;;03A3 +03C3;GREEK SMALL LETTER SIGMA;Ll;0;L;;;;;N;;;03A3;;03A3 +03C4;GREEK SMALL LETTER TAU;Ll;0;L;;;;;N;;;03A4;;03A4 +03C5;GREEK SMALL LETTER UPSILON;Ll;0;L;;;;;N;;;03A5;;03A5 +03C6;GREEK SMALL LETTER PHI;Ll;0;L;;;;;N;;;03A6;;03A6 +03C7;GREEK SMALL LETTER CHI;Ll;0;L;;;;;N;;;03A7;;03A7 +03C8;GREEK SMALL LETTER PSI;Ll;0;L;;;;;N;;;03A8;;03A8 +03C9;GREEK SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;03A9;;03A9 +03CA;GREEK SMALL LETTER IOTA WITH DIALYTIKA;Ll;0;L;03B9 0308;;;;N;GREEK SMALL LETTER IOTA DIAERESIS;;03AA;;03AA +03CB;GREEK SMALL LETTER UPSILON WITH DIALYTIKA;Ll;0;L;03C5 0308;;;;N;GREEK SMALL LETTER UPSILON DIAERESIS;;03AB;;03AB +03CC;GREEK SMALL LETTER OMICRON WITH TONOS;Ll;0;L;03BF 0301;;;;N;GREEK SMALL LETTER OMICRON TONOS;;038C;;038C +03CD;GREEK SMALL LETTER UPSILON WITH TONOS;Ll;0;L;03C5 0301;;;;N;GREEK SMALL LETTER UPSILON TONOS;;038E;;038E +03CE;GREEK SMALL LETTER OMEGA WITH TONOS;Ll;0;L;03C9 0301;;;;N;GREEK SMALL LETTER OMEGA TONOS;;038F;;038F +0401;CYRILLIC CAPITAL LETTER IO;Lu;0;L;0415 0308;;;;N;;;;0451; +0402;CYRILLIC CAPITAL LETTER DJE;Lu;0;L;;;;;N;;Serbocroatian;;0452; +0403;CYRILLIC CAPITAL LETTER GJE;Lu;0;L;0413 0301;;;;N;;;;0453; +0404;CYRILLIC CAPITAL LETTER UKRAINIAN IE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER E;;;0454; +0405;CYRILLIC CAPITAL LETTER DZE;Lu;0;L;;;;;N;;;;0455; +0406;CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER I;;;0456; +0407;CYRILLIC CAPITAL LETTER YI;Lu;0;L;0406 0308;;;;N;;Ukrainian;;0457; +0408;CYRILLIC CAPITAL LETTER JE;Lu;0;L;;;;;N;;;;0458; +0409;CYRILLIC CAPITAL LETTER LJE;Lu;0;L;;;;;N;;;;0459; +040A;CYRILLIC CAPITAL LETTER NJE;Lu;0;L;;;;;N;;;;045A; +040B;CYRILLIC CAPITAL LETTER TSHE;Lu;0;L;;;;;N;;Serbocroatian;;045B; +040C;CYRILLIC CAPITAL LETTER KJE;Lu;0;L;041A 0301;;;;N;;;;045C; +040E;CYRILLIC CAPITAL LETTER SHORT U;Lu;0;L;0423 0306;;;;N;;Byelorussian;;045E; +040F;CYRILLIC CAPITAL LETTER DZHE;Lu;0;L;;;;;N;;;;045F; +0410;CYRILLIC CAPITAL LETTER A;Lu;0;L;;;;;N;;;;0430; +0411;CYRILLIC CAPITAL LETTER BE;Lu;0;L;;;;;N;;;;0431; +0412;CYRILLIC CAPITAL LETTER VE;Lu;0;L;;;;;N;;;;0432; +0413;CYRILLIC CAPITAL LETTER GHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE;;;0433; +0414;CYRILLIC CAPITAL LETTER DE;Lu;0;L;;;;;N;;;;0434; +0415;CYRILLIC CAPITAL LETTER IE;Lu;0;L;;;;;N;;;;0435; +0416;CYRILLIC CAPITAL LETTER ZHE;Lu;0;L;;;;;N;;;;0436; +0417;CYRILLIC CAPITAL LETTER ZE;Lu;0;L;;;;;N;;;;0437; +0418;CYRILLIC CAPITAL LETTER I;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER II;;;0438; +0419;CYRILLIC CAPITAL LETTER SHORT I;Lu;0;L;0418 0306;;;;N;CYRILLIC CAPITAL LETTER SHORT II;;;0439; +041A;CYRILLIC CAPITAL LETTER KA;Lu;0;L;;;;;N;;;;043A; +041B;CYRILLIC CAPITAL LETTER EL;Lu;0;L;;;;;N;;;;043B; +041C;CYRILLIC CAPITAL LETTER EM;Lu;0;L;;;;;N;;;;043C; +041D;CYRILLIC CAPITAL LETTER EN;Lu;0;L;;;;;N;;;;043D; +041E;CYRILLIC CAPITAL LETTER O;Lu;0;L;;;;;N;;;;043E; +041F;CYRILLIC CAPITAL LETTER PE;Lu;0;L;;;;;N;;;;043F; +0420;CYRILLIC CAPITAL LETTER ER;Lu;0;L;;;;;N;;;;0440; +0421;CYRILLIC CAPITAL LETTER ES;Lu;0;L;;;;;N;;;;0441; +0422;CYRILLIC CAPITAL LETTER TE;Lu;0;L;;;;;N;;;;0442; +0423;CYRILLIC CAPITAL LETTER U;Lu;0;L;;;;;N;;;;0443; +0424;CYRILLIC CAPITAL LETTER EF;Lu;0;L;;;;;N;;;;0444; +0425;CYRILLIC CAPITAL LETTER HA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KHA;;;0445; +0426;CYRILLIC CAPITAL LETTER TSE;Lu;0;L;;;;;N;;;;0446; +0427;CYRILLIC CAPITAL LETTER CHE;Lu;0;L;;;;;N;;;;0447; +0428;CYRILLIC CAPITAL LETTER SHA;Lu;0;L;;;;;N;;;;0448; +0429;CYRILLIC CAPITAL LETTER SHCHA;Lu;0;L;;;;;N;;;;0449; +042A;CYRILLIC CAPITAL LETTER HARD SIGN;Lu;0;L;;;;;N;;;;044A; +042B;CYRILLIC CAPITAL LETTER YERU;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER YERI;;;044B; +042C;CYRILLIC CAPITAL LETTER SOFT SIGN;Lu;0;L;;;;;N;;;;044C; +042D;CYRILLIC CAPITAL LETTER E;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER REVERSED E;;;044D; +042E;CYRILLIC CAPITAL LETTER YU;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IU;;;044E; +042F;CYRILLIC CAPITAL LETTER YA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IA;;;044F; +0430;CYRILLIC SMALL LETTER A;Ll;0;L;;;;;N;;;0410;;0410 +0431;CYRILLIC SMALL LETTER BE;Ll;0;L;;;;;N;;;0411;;0411 +0432;CYRILLIC SMALL LETTER VE;Ll;0;L;;;;;N;;;0412;;0412 +0433;CYRILLIC SMALL LETTER GHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE;;0413;;0413 +0434;CYRILLIC SMALL LETTER DE;Ll;0;L;;;;;N;;;0414;;0414 +0435;CYRILLIC SMALL LETTER IE;Ll;0;L;;;;;N;;;0415;;0415 +0436;CYRILLIC SMALL LETTER ZHE;Ll;0;L;;;;;N;;;0416;;0416 +0437;CYRILLIC SMALL LETTER ZE;Ll;0;L;;;;;N;;;0417;;0417 +0438;CYRILLIC SMALL LETTER I;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER II;;0418;;0418 +0439;CYRILLIC SMALL LETTER SHORT I;Ll;0;L;0438 0306;;;;N;CYRILLIC SMALL LETTER SHORT II;;0419;;0419 +043A;CYRILLIC SMALL LETTER KA;Ll;0;L;;;;;N;;;041A;;041A +043B;CYRILLIC SMALL LETTER EL;Ll;0;L;;;;;N;;;041B;;041B +043C;CYRILLIC SMALL LETTER EM;Ll;0;L;;;;;N;;;041C;;041C +043D;CYRILLIC SMALL LETTER EN;Ll;0;L;;;;;N;;;041D;;041D +043E;CYRILLIC SMALL LETTER O;Ll;0;L;;;;;N;;;041E;;041E +043F;CYRILLIC SMALL LETTER PE;Ll;0;L;;;;;N;;;041F;;041F +0440;CYRILLIC SMALL LETTER ER;Ll;0;L;;;;;N;;;0420;;0420 +0441;CYRILLIC SMALL LETTER ES;Ll;0;L;;;;;N;;;0421;;0421 +0442;CYRILLIC SMALL LETTER TE;Ll;0;L;;;;;N;;;0422;;0422 +0443;CYRILLIC SMALL LETTER U;Ll;0;L;;;;;N;;;0423;;0423 +0444;CYRILLIC SMALL LETTER EF;Ll;0;L;;;;;N;;;0424;;0424 +0445;CYRILLIC SMALL LETTER HA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KHA;;0425;;0425 +0446;CYRILLIC SMALL LETTER TSE;Ll;0;L;;;;;N;;;0426;;0426 +0447;CYRILLIC SMALL LETTER CHE;Ll;0;L;;;;;N;;;0427;;0427 +0448;CYRILLIC SMALL LETTER SHA;Ll;0;L;;;;;N;;;0428;;0428 +0449;CYRILLIC SMALL LETTER SHCHA;Ll;0;L;;;;;N;;;0429;;0429 +044A;CYRILLIC SMALL LETTER HARD SIGN;Ll;0;L;;;;;N;;;042A;;042A +044B;CYRILLIC SMALL LETTER YERU;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER YERI;;042B;;042B +044C;CYRILLIC SMALL LETTER SOFT SIGN;Ll;0;L;;;;;N;;;042C;;042C +044D;CYRILLIC SMALL LETTER E;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER REVERSED E;;042D;;042D +044E;CYRILLIC SMALL LETTER YU;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IU;;042E;;042E +044F;CYRILLIC SMALL LETTER YA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IA;;042F;;042F +0451;CYRILLIC SMALL LETTER IO;Ll;0;L;0435 0308;;;;N;;;0401;;0401 +0452;CYRILLIC SMALL LETTER DJE;Ll;0;L;;;;;N;;Serbocroatian;0402;;0402 +0453;CYRILLIC SMALL LETTER GJE;Ll;0;L;0433 0301;;;;N;;;0403;;0403 +0454;CYRILLIC SMALL LETTER UKRAINIAN IE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER E;;0404;;0404 +0455;CYRILLIC SMALL LETTER DZE;Ll;0;L;;;;;N;;;0405;;0405 +0456;CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER I;;0406;;0406 +0457;CYRILLIC SMALL LETTER YI;Ll;0;L;0456 0308;;;;N;;Ukrainian;0407;;0407 +0458;CYRILLIC SMALL LETTER JE;Ll;0;L;;;;;N;;;0408;;0408 +0459;CYRILLIC SMALL LETTER LJE;Ll;0;L;;;;;N;;;0409;;0409 +045A;CYRILLIC SMALL LETTER NJE;Ll;0;L;;;;;N;;;040A;;040A +045B;CYRILLIC SMALL LETTER TSHE;Ll;0;L;;;;;N;;Serbocroatian;040B;;040B +045C;CYRILLIC SMALL LETTER KJE;Ll;0;L;043A 0301;;;;N;;;040C;;040C +045E;CYRILLIC SMALL LETTER SHORT U;Ll;0;L;0443 0306;;;;N;;Byelorussian;040E;;040E +045F;CYRILLIC SMALL LETTER DZHE;Ll;0;L;;;;;N;;;040F;;040F +0490;CYRILLIC CAPITAL LETTER GHE WITH UPTURN;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE WITH UPTURN;;;0491; +0491;CYRILLIC SMALL LETTER GHE WITH UPTURN;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE WITH UPTURN;;0490;;0490 +05B0;HEBREW POINT SHEVA;Mn;10;NSM;;;;;N;;;;; +05B1;HEBREW POINT HATAF SEGOL;Mn;11;NSM;;;;;N;;;;; +05B2;HEBREW POINT HATAF PATAH;Mn;12;NSM;;;;;N;;;;; +05B3;HEBREW POINT HATAF QAMATS;Mn;13;NSM;;;;;N;;;;; +05B4;HEBREW POINT HIRIQ;Mn;14;NSM;;;;;N;;;;; +05B5;HEBREW POINT TSERE;Mn;15;NSM;;;;;N;;;;; +05B6;HEBREW POINT SEGOL;Mn;16;NSM;;;;;N;;;;; +05B7;HEBREW POINT PATAH;Mn;17;NSM;;;;;N;;;;; +05B8;HEBREW POINT QAMATS;Mn;18;NSM;;;;;N;;;;; +05B9;HEBREW POINT HOLAM;Mn;19;NSM;;;;;N;;;;; +05BB;HEBREW POINT QUBUTS;Mn;20;NSM;;;;;N;;;;; +05BC;HEBREW POINT DAGESH OR MAPIQ;Mn;21;NSM;;;;;N;HEBREW POINT DAGESH;or shuruq;;; +05BD;HEBREW POINT METEG;Mn;22;NSM;;;;;N;;*;;; +05BE;HEBREW PUNCTUATION MAQAF;Pd;0;R;;;;;N;;;;; +05BF;HEBREW POINT RAFE;Mn;23;NSM;;;;;N;;;;; +05C0;HEBREW PUNCTUATION PASEQ;Po;0;R;;;;;N;HEBREW POINT PASEQ;*;;; +05C1;HEBREW POINT SHIN DOT;Mn;24;NSM;;;;;N;;;;; +05C2;HEBREW POINT SIN DOT;Mn;25;NSM;;;;;N;;;;; +05C3;HEBREW PUNCTUATION SOF PASUQ;Po;0;R;;;;;N;;*;;; +05D0;HEBREW LETTER ALEF;Lo;0;R;;;;;N;;;;; +05D1;HEBREW LETTER BET;Lo;0;R;;;;;N;;;;; +05D2;HEBREW LETTER GIMEL;Lo;0;R;;;;;N;;;;; +05D3;HEBREW LETTER DALET;Lo;0;R;;;;;N;;;;; +05D4;HEBREW LETTER HE;Lo;0;R;;;;;N;;;;; +05D5;HEBREW LETTER VAV;Lo;0;R;;;;;N;;;;; +05D6;HEBREW LETTER ZAYIN;Lo;0;R;;;;;N;;;;; +05D7;HEBREW LETTER HET;Lo;0;R;;;;;N;;;;; +05D8;HEBREW LETTER TET;Lo;0;R;;;;;N;;;;; +05D9;HEBREW LETTER YOD;Lo;0;R;;;;;N;;;;; +05DA;HEBREW LETTER FINAL KAF;Lo;0;R;;;;;N;;;;; +05DB;HEBREW LETTER KAF;Lo;0;R;;;;;N;;;;; +05DC;HEBREW LETTER LAMED;Lo;0;R;;;;;N;;;;; +05DD;HEBREW LETTER FINAL MEM;Lo;0;R;;;;;N;;;;; +05DE;HEBREW LETTER MEM;Lo;0;R;;;;;N;;;;; +05DF;HEBREW LETTER FINAL NUN;Lo;0;R;;;;;N;;;;; +05E0;HEBREW LETTER NUN;Lo;0;R;;;;;N;;;;; +05E1;HEBREW LETTER SAMEKH;Lo;0;R;;;;;N;;;;; +05E2;HEBREW LETTER AYIN;Lo;0;R;;;;;N;;;;; +05E3;HEBREW LETTER FINAL PE;Lo;0;R;;;;;N;;;;; +05E4;HEBREW LETTER PE;Lo;0;R;;;;;N;;;;; +05E5;HEBREW LETTER FINAL TSADI;Lo;0;R;;;;;N;;;;; +05E6;HEBREW LETTER TSADI;Lo;0;R;;;;;N;;;;; +05E7;HEBREW LETTER QOF;Lo;0;R;;;;;N;;;;; +05E8;HEBREW LETTER RESH;Lo;0;R;;;;;N;;;;; +05E9;HEBREW LETTER SHIN;Lo;0;R;;;;;N;;;;; +05EA;HEBREW LETTER TAV;Lo;0;R;;;;;N;;;;; +05F0;HEBREW LIGATURE YIDDISH DOUBLE VAV;Lo;0;R;;;;;N;HEBREW LETTER DOUBLE VAV;;;; +05F1;HEBREW LIGATURE YIDDISH VAV YOD;Lo;0;R;;;;;N;HEBREW LETTER VAV YOD;;;; +05F2;HEBREW LIGATURE YIDDISH DOUBLE YOD;Lo;0;R;;;;;N;HEBREW LETTER DOUBLE YOD;;;; +05F3;HEBREW PUNCTUATION GERESH;Po;0;R;;;;;N;;;;; +05F4;HEBREW PUNCTUATION GERSHAYIM;Po;0;R;;;;;N;;;;; +060C;ARABIC COMMA;Po;0;CS;;;;;N;;;;; +061B;ARABIC SEMICOLON;Po;0;AL;;;;;N;;;;; +061F;ARABIC QUESTION MARK;Po;0;AL;;;;;N;;;;; +0621;ARABIC LETTER HAMZA;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAH;;;; +0622;ARABIC LETTER ALEF WITH MADDA ABOVE;Lo;0;AL;0627 0653;;;;N;ARABIC LETTER MADDAH ON ALEF;;;; +0623;ARABIC LETTER ALEF WITH HAMZA ABOVE;Lo;0;AL;0627 0654;;;;N;ARABIC LETTER HAMZAH ON ALEF;;;; +0624;ARABIC LETTER WAW WITH HAMZA ABOVE;Lo;0;AL;0648 0654;;;;N;ARABIC LETTER HAMZAH ON WAW;;;; +0625;ARABIC LETTER ALEF WITH HAMZA BELOW;Lo;0;AL;0627 0655;;;;N;ARABIC LETTER HAMZAH UNDER ALEF;;;; +0626;ARABIC LETTER YEH WITH HAMZA ABOVE;Lo;0;AL;064A 0654;;;;N;ARABIC LETTER HAMZAH ON YA;;;; +0627;ARABIC LETTER ALEF;Lo;0;AL;;;;;N;;;;; +0628;ARABIC LETTER BEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA;;;; +0629;ARABIC LETTER TEH MARBUTA;Lo;0;AL;;;;;N;ARABIC LETTER TAA MARBUTAH;;;; +062A;ARABIC LETTER TEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA;;;; +062B;ARABIC LETTER THEH;Lo;0;AL;;;;;N;ARABIC LETTER THAA;;;; +062C;ARABIC LETTER JEEM;Lo;0;AL;;;;;N;;;;; +062D;ARABIC LETTER HAH;Lo;0;AL;;;;;N;ARABIC LETTER HAA;;;; +062E;ARABIC LETTER KHAH;Lo;0;AL;;;;;N;ARABIC LETTER KHAA;;;; +062F;ARABIC LETTER DAL;Lo;0;AL;;;;;N;;;;; +0630;ARABIC LETTER THAL;Lo;0;AL;;;;;N;;;;; +0631;ARABIC LETTER REH;Lo;0;AL;;;;;N;ARABIC LETTER RA;;;; +0632;ARABIC LETTER ZAIN;Lo;0;AL;;;;;N;;;;; +0633;ARABIC LETTER SEEN;Lo;0;AL;;;;;N;;;;; +0634;ARABIC LETTER SHEEN;Lo;0;AL;;;;;N;;;;; +0635;ARABIC LETTER SAD;Lo;0;AL;;;;;N;;;;; +0636;ARABIC LETTER DAD;Lo;0;AL;;;;;N;;;;; +0637;ARABIC LETTER TAH;Lo;0;AL;;;;;N;;;;; +0638;ARABIC LETTER ZAH;Lo;0;AL;;;;;N;ARABIC LETTER DHAH;;;; +0639;ARABIC LETTER AIN;Lo;0;AL;;;;;N;;;;; +063A;ARABIC LETTER GHAIN;Lo;0;AL;;;;;N;;;;; +0640;ARABIC TATWEEL;Lm;0;AL;;;;;N;;;;; +0641;ARABIC LETTER FEH;Lo;0;AL;;;;;N;ARABIC LETTER FA;;;; +0642;ARABIC LETTER QAF;Lo;0;AL;;;;;N;;;;; +0643;ARABIC LETTER KAF;Lo;0;AL;;;;;N;ARABIC LETTER CAF;;;; +0644;ARABIC LETTER LAM;Lo;0;AL;;;;;N;;;;; +0645;ARABIC LETTER MEEM;Lo;0;AL;;;;;N;;;;; +0646;ARABIC LETTER NOON;Lo;0;AL;;;;;N;;;;; +0647;ARABIC LETTER HEH;Lo;0;AL;;;;;N;ARABIC LETTER HA;;;; +0648;ARABIC LETTER WAW;Lo;0;AL;;;;;N;;;;; +0649;ARABIC LETTER ALEF MAKSURA;Lo;0;AL;;;;;N;ARABIC LETTER ALEF MAQSURAH;;;; +064A;ARABIC LETTER YEH;Lo;0;AL;;;;;N;ARABIC LETTER YA;;;; +064B;ARABIC FATHATAN;Mn;27;NSM;;;;;N;;;;; +064C;ARABIC DAMMATAN;Mn;28;NSM;;;;;N;;;;; +064D;ARABIC KASRATAN;Mn;29;NSM;;;;;N;;;;; +064E;ARABIC FATHA;Mn;30;NSM;;;;;N;ARABIC FATHAH;;;; +064F;ARABIC DAMMA;Mn;31;NSM;;;;;N;ARABIC DAMMAH;;;; +0650;ARABIC KASRA;Mn;32;NSM;;;;;N;ARABIC KASRAH;;;; +0651;ARABIC SHADDA;Mn;33;NSM;;;;;N;ARABIC SHADDAH;;;; +0652;ARABIC SUKUN;Mn;34;NSM;;;;;N;;;;; +0660;ARABIC-INDIC DIGIT ZERO;Nd;0;AN;;0;0;0;N;;;;; +0661;ARABIC-INDIC DIGIT ONE;Nd;0;AN;;1;1;1;N;;;;; +0662;ARABIC-INDIC DIGIT TWO;Nd;0;AN;;2;2;2;N;;;;; +0663;ARABIC-INDIC DIGIT THREE;Nd;0;AN;;3;3;3;N;;;;; +0664;ARABIC-INDIC DIGIT FOUR;Nd;0;AN;;4;4;4;N;;;;; +0665;ARABIC-INDIC DIGIT FIVE;Nd;0;AN;;5;5;5;N;;;;; +0666;ARABIC-INDIC DIGIT SIX;Nd;0;AN;;6;6;6;N;;;;; +0667;ARABIC-INDIC DIGIT SEVEN;Nd;0;AN;;7;7;7;N;;;;; +0668;ARABIC-INDIC DIGIT EIGHT;Nd;0;AN;;8;8;8;N;;;;; +0669;ARABIC-INDIC DIGIT NINE;Nd;0;AN;;9;9;9;N;;;;; +066A;ARABIC PERCENT SIGN;Po;0;ET;;;;;N;;;;; +0679;ARABIC LETTER TTEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH SMALL TAH;;;; +067E;ARABIC LETTER PEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH THREE DOTS BELOW;;;; +0686;ARABIC LETTER TCHEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE THREE DOTS DOWNWARD;;;; +0688;ARABIC LETTER DDAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH SMALL TAH;;;; +0691;ARABIC LETTER RREH;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL TAH;;;; +0698;ARABIC LETTER JEH;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH THREE DOTS ABOVE;;;; +06A9;ARABIC LETTER KEHEH;Lo;0;AL;;;;;N;ARABIC LETTER OPEN CAF;;;; +06AF;ARABIC LETTER GAF;Lo;0;AL;;;;;N;;*;;; +06BA;ARABIC LETTER NOON GHUNNA;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS NOON;;;; +06BE;ARABIC LETTER HEH DOACHASHMEE;Lo;0;AL;;;;;N;ARABIC LETTER KNOTTED HA;;;; +06C1;ARABIC LETTER HEH GOAL;Lo;0;AL;;;;;N;ARABIC LETTER HA GOAL;;;; +06D2;ARABIC LETTER YEH BARREE;Lo;0;AL;;;;;N;ARABIC LETTER YA BARREE;;;; +0E01;THAI CHARACTER KO KAI;Lo;0;L;;;;;N;THAI LETTER KO KAI;;;; +0E02;THAI CHARACTER KHO KHAI;Lo;0;L;;;;;N;THAI LETTER KHO KHAI;;;; +0E03;THAI CHARACTER KHO KHUAT;Lo;0;L;;;;;N;THAI LETTER KHO KHUAT;;;; +0E04;THAI CHARACTER KHO KHWAI;Lo;0;L;;;;;N;THAI LETTER KHO KHWAI;;;; +0E05;THAI CHARACTER KHO KHON;Lo;0;L;;;;;N;THAI LETTER KHO KHON;;;; +0E06;THAI CHARACTER KHO RAKHANG;Lo;0;L;;;;;N;THAI LETTER KHO RAKHANG;;;; +0E07;THAI CHARACTER NGO NGU;Lo;0;L;;;;;N;THAI LETTER NGO NGU;;;; +0E08;THAI CHARACTER CHO CHAN;Lo;0;L;;;;;N;THAI LETTER CHO CHAN;;;; +0E09;THAI CHARACTER CHO CHING;Lo;0;L;;;;;N;THAI LETTER CHO CHING;;;; +0E0A;THAI CHARACTER CHO CHANG;Lo;0;L;;;;;N;THAI LETTER CHO CHANG;;;; +0E0B;THAI CHARACTER SO SO;Lo;0;L;;;;;N;THAI LETTER SO SO;;;; +0E0C;THAI CHARACTER CHO CHOE;Lo;0;L;;;;;N;THAI LETTER CHO CHOE;;;; +0E0D;THAI CHARACTER YO YING;Lo;0;L;;;;;N;THAI LETTER YO YING;;;; +0E0E;THAI CHARACTER DO CHADA;Lo;0;L;;;;;N;THAI LETTER DO CHADA;;;; +0E0F;THAI CHARACTER TO PATAK;Lo;0;L;;;;;N;THAI LETTER TO PATAK;;;; +0E10;THAI CHARACTER THO THAN;Lo;0;L;;;;;N;THAI LETTER THO THAN;;;; +0E11;THAI CHARACTER THO NANGMONTHO;Lo;0;L;;;;;N;THAI LETTER THO NANGMONTHO;;;; +0E12;THAI CHARACTER THO PHUTHAO;Lo;0;L;;;;;N;THAI LETTER THO PHUTHAO;;;; +0E13;THAI CHARACTER NO NEN;Lo;0;L;;;;;N;THAI LETTER NO NEN;;;; +0E14;THAI CHARACTER DO DEK;Lo;0;L;;;;;N;THAI LETTER DO DEK;;;; +0E15;THAI CHARACTER TO TAO;Lo;0;L;;;;;N;THAI LETTER TO TAO;;;; +0E16;THAI CHARACTER THO THUNG;Lo;0;L;;;;;N;THAI LETTER THO THUNG;;;; +0E17;THAI CHARACTER THO THAHAN;Lo;0;L;;;;;N;THAI LETTER THO THAHAN;;;; +0E18;THAI CHARACTER THO THONG;Lo;0;L;;;;;N;THAI LETTER THO THONG;;;; +0E19;THAI CHARACTER NO NU;Lo;0;L;;;;;N;THAI LETTER NO NU;;;; +0E1A;THAI CHARACTER BO BAIMAI;Lo;0;L;;;;;N;THAI LETTER BO BAIMAI;;;; +0E1B;THAI CHARACTER PO PLA;Lo;0;L;;;;;N;THAI LETTER PO PLA;;;; +0E1C;THAI CHARACTER PHO PHUNG;Lo;0;L;;;;;N;THAI LETTER PHO PHUNG;;;; +0E1D;THAI CHARACTER FO FA;Lo;0;L;;;;;N;THAI LETTER FO FA;;;; +0E1E;THAI CHARACTER PHO PHAN;Lo;0;L;;;;;N;THAI LETTER PHO PHAN;;;; +0E1F;THAI CHARACTER FO FAN;Lo;0;L;;;;;N;THAI LETTER FO FAN;;;; +0E20;THAI CHARACTER PHO SAMPHAO;Lo;0;L;;;;;N;THAI LETTER PHO SAMPHAO;;;; +0E21;THAI CHARACTER MO MA;Lo;0;L;;;;;N;THAI LETTER MO MA;;;; +0E22;THAI CHARACTER YO YAK;Lo;0;L;;;;;N;THAI LETTER YO YAK;;;; +0E23;THAI CHARACTER RO RUA;Lo;0;L;;;;;N;THAI LETTER RO RUA;;;; +0E24;THAI CHARACTER RU;Lo;0;L;;;;;N;THAI LETTER RU;;;; +0E25;THAI CHARACTER LO LING;Lo;0;L;;;;;N;THAI LETTER LO LING;;;; +0E26;THAI CHARACTER LU;Lo;0;L;;;;;N;THAI LETTER LU;;;; +0E27;THAI CHARACTER WO WAEN;Lo;0;L;;;;;N;THAI LETTER WO WAEN;;;; +0E28;THAI CHARACTER SO SALA;Lo;0;L;;;;;N;THAI LETTER SO SALA;;;; +0E29;THAI CHARACTER SO RUSI;Lo;0;L;;;;;N;THAI LETTER SO RUSI;;;; +0E2A;THAI CHARACTER SO SUA;Lo;0;L;;;;;N;THAI LETTER SO SUA;;;; +0E2B;THAI CHARACTER HO HIP;Lo;0;L;;;;;N;THAI LETTER HO HIP;;;; +0E2C;THAI CHARACTER LO CHULA;Lo;0;L;;;;;N;THAI LETTER LO CHULA;;;; +0E2D;THAI CHARACTER O ANG;Lo;0;L;;;;;N;THAI LETTER O ANG;;;; +0E2E;THAI CHARACTER HO NOKHUK;Lo;0;L;;;;;N;THAI LETTER HO NOK HUK;;;; +0E2F;THAI CHARACTER PAIYANNOI;Lo;0;L;;;;;N;THAI PAI YAN NOI;paiyan noi;;; +0E30;THAI CHARACTER SARA A;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA A;;;; +0E31;THAI CHARACTER MAI HAN-AKAT;Mn;0;NSM;;;;;N;THAI VOWEL SIGN MAI HAN-AKAT;;;; +0E32;THAI CHARACTER SARA AA;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA AA;;;; +0E33;THAI CHARACTER SARA AM;Lo;0;L; 0E4D 0E32;;;;N;THAI VOWEL SIGN SARA AM;;;; +0E34;THAI CHARACTER SARA I;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA I;;;; +0E35;THAI CHARACTER SARA II;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA II;;;; +0E36;THAI CHARACTER SARA UE;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA UE;;;; +0E37;THAI CHARACTER SARA UEE;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA UEE;sara uue;;; +0E38;THAI CHARACTER SARA U;Mn;103;NSM;;;;;N;THAI VOWEL SIGN SARA U;;;; +0E39;THAI CHARACTER SARA UU;Mn;103;NSM;;;;;N;THAI VOWEL SIGN SARA UU;;;; +0E3A;THAI CHARACTER PHINTHU;Mn;9;NSM;;;;;N;THAI VOWEL SIGN PHINTHU;;;; +0E3F;THAI CURRENCY SYMBOL BAHT;Sc;0;ET;;;;;N;THAI BAHT SIGN;;;; +0E40;THAI CHARACTER SARA E;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA E;;;; +0E41;THAI CHARACTER SARA AE;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA AE;;;; +0E42;THAI CHARACTER SARA O;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA O;;;; +0E43;THAI CHARACTER SARA AI MAIMUAN;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA MAI MUAN;sara ai mai muan;;; +0E44;THAI CHARACTER SARA AI MAIMALAI;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA MAI MALAI;sara ai mai malai;;; +0E45;THAI CHARACTER LAKKHANGYAO;Lo;0;L;;;;;N;THAI LAK KHANG YAO;lakkhang yao;;; +0E46;THAI CHARACTER MAIYAMOK;Lm;0;L;;;;;N;THAI MAI YAMOK;mai yamok;;; +0E47;THAI CHARACTER MAITAIKHU;Mn;0;NSM;;;;;N;THAI VOWEL SIGN MAI TAI KHU;mai taikhu;;; +0E48;THAI CHARACTER MAI EK;Mn;107;NSM;;;;;N;THAI TONE MAI EK;;;; +0E49;THAI CHARACTER MAI THO;Mn;107;NSM;;;;;N;THAI TONE MAI THO;;;; +0E4A;THAI CHARACTER MAI TRI;Mn;107;NSM;;;;;N;THAI TONE MAI TRI;;;; +0E4B;THAI CHARACTER MAI CHATTAWA;Mn;107;NSM;;;;;N;THAI TONE MAI CHATTAWA;;;; +0E4C;THAI CHARACTER THANTHAKHAT;Mn;0;NSM;;;;;N;THAI THANTHAKHAT;;;; +0E4D;THAI CHARACTER NIKHAHIT;Mn;0;NSM;;;;;N;THAI NIKKHAHIT;nikkhahit;;; +0E4E;THAI CHARACTER YAMAKKAN;Mn;0;NSM;;;;;N;THAI YAMAKKAN;;;; +0E4F;THAI CHARACTER FONGMAN;Po;0;L;;;;;N;THAI FONGMAN;;;; +0E50;THAI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0E51;THAI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0E52;THAI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0E53;THAI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0E54;THAI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0E55;THAI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0E56;THAI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0E57;THAI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0E58;THAI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0E59;THAI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0E5A;THAI CHARACTER ANGKHANKHU;Po;0;L;;;;;N;THAI ANGKHANKHU;;;; +0E5B;THAI CHARACTER KHOMUT;Po;0;L;;;;;N;THAI KHOMUT;;;; +1E02;LATIN CAPITAL LETTER B WITH DOT ABOVE;Lu;0;L;0042 0307;;;;N;;;;1E03; +1E03;LATIN SMALL LETTER B WITH DOT ABOVE;Ll;0;L;0062 0307;;;;N;;;1E02;;1E02 +1E0A;LATIN CAPITAL LETTER D WITH DOT ABOVE;Lu;0;L;0044 0307;;;;N;;;;1E0B; +1E0B;LATIN SMALL LETTER D WITH DOT ABOVE;Ll;0;L;0064 0307;;;;N;;;1E0A;;1E0A +1E1E;LATIN CAPITAL LETTER F WITH DOT ABOVE;Lu;0;L;0046 0307;;;;N;;;;1E1F; +1E1F;LATIN SMALL LETTER F WITH DOT ABOVE;Ll;0;L;0066 0307;;;;N;;;1E1E;;1E1E +1E40;LATIN CAPITAL LETTER M WITH DOT ABOVE;Lu;0;L;004D 0307;;;;N;;;;1E41; +1E41;LATIN SMALL LETTER M WITH DOT ABOVE;Ll;0;L;006D 0307;;;;N;;;1E40;;1E40 +1E56;LATIN CAPITAL LETTER P WITH DOT ABOVE;Lu;0;L;0050 0307;;;;N;;;;1E57; +1E57;LATIN SMALL LETTER P WITH DOT ABOVE;Ll;0;L;0070 0307;;;;N;;;1E56;;1E56 +1E60;LATIN CAPITAL LETTER S WITH DOT ABOVE;Lu;0;L;0053 0307;;;;N;;;;1E61; +1E61;LATIN SMALL LETTER S WITH DOT ABOVE;Ll;0;L;0073 0307;;;;N;;;1E60;;1E60 +1E6A;LATIN CAPITAL LETTER T WITH DOT ABOVE;Lu;0;L;0054 0307;;;;N;;;;1E6B; +1E6B;LATIN SMALL LETTER T WITH DOT ABOVE;Ll;0;L;0074 0307;;;;N;;;1E6A;;1E6A +1E80;LATIN CAPITAL LETTER W WITH GRAVE;Lu;0;L;0057 0300;;;;N;;;;1E81; +1E81;LATIN SMALL LETTER W WITH GRAVE;Ll;0;L;0077 0300;;;;N;;;1E80;;1E80 +1E82;LATIN CAPITAL LETTER W WITH ACUTE;Lu;0;L;0057 0301;;;;N;;;;1E83; +1E83;LATIN SMALL LETTER W WITH ACUTE;Ll;0;L;0077 0301;;;;N;;;1E82;;1E82 +1E84;LATIN CAPITAL LETTER W WITH DIAERESIS;Lu;0;L;0057 0308;;;;N;;;;1E85; +1E85;LATIN SMALL LETTER W WITH DIAERESIS;Ll;0;L;0077 0308;;;;N;;;1E84;;1E84 +1EF2;LATIN CAPITAL LETTER Y WITH GRAVE;Lu;0;L;0059 0300;;;;N;;;;1EF3; +1EF3;LATIN SMALL LETTER Y WITH GRAVE;Ll;0;L;0079 0300;;;;N;;;1EF2;;1EF2 +200C;ZERO WIDTH NON-JOINER;Cf;0;BN;;;;;N;;;;; +200D;ZERO WIDTH JOINER;Cf;0;BN;;;;;N;;;;; +200E;LEFT-TO-RIGHT MARK;Cf;0;L;;;;;N;;;;; +200F;RIGHT-TO-LEFT MARK;Cf;0;R;;;;;N;;;;; +2013;EN DASH;Pd;0;ON;;;;;N;;;;; +2014;EM DASH;Pd;0;ON;;;;;N;;;;; +2015;HORIZONTAL BAR;Pd;0;ON;;;;;N;QUOTATION DASH;;;; +2017;DOUBLE LOW LINE;Po;0;ON; 0020 0333;;;;N;SPACING DOUBLE UNDERSCORE;;;; +2018;LEFT SINGLE QUOTATION MARK;Pi;0;ON;;;;;N;SINGLE TURNED COMMA QUOTATION MARK;;;; +2019;RIGHT SINGLE QUOTATION MARK;Pf;0;ON;;;;;N;SINGLE COMMA QUOTATION MARK;;;; +201A;SINGLE LOW-9 QUOTATION MARK;Ps;0;ON;;;;;N;LOW SINGLE COMMA QUOTATION MARK;;;; +201C;LEFT DOUBLE QUOTATION MARK;Pi;0;ON;;;;;N;DOUBLE TURNED COMMA QUOTATION MARK;;;; +201D;RIGHT DOUBLE QUOTATION MARK;Pf;0;ON;;;;;N;DOUBLE COMMA QUOTATION MARK;;;; +201E;DOUBLE LOW-9 QUOTATION MARK;Ps;0;ON;;;;;N;LOW DOUBLE COMMA QUOTATION MARK;;;; +2020;DAGGER;Po;0;ON;;;;;N;;;;; +2021;DOUBLE DAGGER;Po;0;ON;;;;;N;;;;; +2022;BULLET;Po;0;ON;;;;;N;;;;; +2026;HORIZONTAL ELLIPSIS;Po;0;ON; 002E 002E 002E;;;;N;;;;; +2030;PER MILLE SIGN;Po;0;ET;;;;;N;;;;; +2039;SINGLE LEFT-POINTING ANGLE QUOTATION MARK;Pi;0;ON;;;;;Y;LEFT POINTING SINGLE GUILLEMET;;;; +203A;SINGLE RIGHT-POINTING ANGLE QUOTATION MARK;Pf;0;ON;;;;;Y;RIGHT POINTING SINGLE GUILLEMET;;;; +207F;SUPERSCRIPT LATIN SMALL LETTER N;Ll;0;L; 006E;;;;N;;;;; +20A7;PESETA SIGN;Sc;0;ET;;;;;N;;;;; +20AA;NEW SHEQEL SIGN;Sc;0;ET;;;;;N;;;;; +20AB;DONG SIGN;Sc;0;ET;;;;;N;;;;; +20AC;EURO SIGN;Sc;0;ET;;;;;N;;;;; +20AF;DRACHMA SIGN;Sc;0;ET;;;;;N;;;;; +2116;NUMERO SIGN;So;0;ON; 004E 006F;;;;N;NUMERO;;;; +2122;TRADE MARK SIGN;So;0;ON; 0054 004D;;;;N;TRADEMARK;;;; +2219;BULLET OPERATOR;Sm;0;ON;;;;;N;;;;; +221A;SQUARE ROOT;Sm;0;ON;;;;;Y;;;;; +221E;INFINITY;Sm;0;ON;;;;;N;;;;; +2229;INTERSECTION;Sm;0;ON;;;;;N;;;;; +2248;ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2261;IDENTICAL TO;Sm;0;ON;;;;;N;;;;; +2264;LESS-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN OR EQUAL TO;;;; +2265;GREATER-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN OR EQUAL TO;;;; +2310;REVERSED NOT SIGN;So;0;ON;;;;;N;;;;; +2320;TOP HALF INTEGRAL;Sm;0;ON;;;;;Y;;;;; +2321;BOTTOM HALF INTEGRAL;Sm;0;ON;;;;;Y;;;;; +2500;BOX DRAWINGS LIGHT HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT HORIZONTAL;;;; +2502;BOX DRAWINGS LIGHT VERTICAL;So;0;ON;;;;;N;FORMS LIGHT VERTICAL;;;; +250C;BOX DRAWINGS LIGHT DOWN AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT DOWN AND RIGHT;;;; +2510;BOX DRAWINGS LIGHT DOWN AND LEFT;So;0;ON;;;;;N;FORMS LIGHT DOWN AND LEFT;;;; +2514;BOX DRAWINGS LIGHT UP AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT UP AND RIGHT;;;; +2518;BOX DRAWINGS LIGHT UP AND LEFT;So;0;ON;;;;;N;FORMS LIGHT UP AND LEFT;;;; +251C;BOX DRAWINGS LIGHT VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND RIGHT;;;; +2524;BOX DRAWINGS LIGHT VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND LEFT;;;; +252C;BOX DRAWINGS LIGHT DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT DOWN AND HORIZONTAL;;;; +2534;BOX DRAWINGS LIGHT UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT UP AND HORIZONTAL;;;; +253C;BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND HORIZONTAL;;;; +2550;BOX DRAWINGS DOUBLE HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE HORIZONTAL;;;; +2551;BOX DRAWINGS DOUBLE VERTICAL;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL;;;; +2552;BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND RIGHT DOUBLE;;;; +2553;BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND RIGHT SINGLE;;;; +2554;BOX DRAWINGS DOUBLE DOWN AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND RIGHT;;;; +2555;BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND LEFT DOUBLE;;;; +2556;BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND LEFT SINGLE;;;; +2557;BOX DRAWINGS DOUBLE DOWN AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND LEFT;;;; +2558;BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND RIGHT DOUBLE;;;; +2559;BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND RIGHT SINGLE;;;; +255A;BOX DRAWINGS DOUBLE UP AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE UP AND RIGHT;;;; +255B;BOX DRAWINGS UP SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND LEFT DOUBLE;;;; +255C;BOX DRAWINGS UP DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND LEFT SINGLE;;;; +255D;BOX DRAWINGS DOUBLE UP AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE UP AND LEFT;;;; +255E;BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND RIGHT DOUBLE;;;; +255F;BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND RIGHT SINGLE;;;; +2560;BOX DRAWINGS DOUBLE VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND RIGHT;;;; +2561;BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND LEFT DOUBLE;;;; +2562;BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND LEFT SINGLE;;;; +2563;BOX DRAWINGS DOUBLE VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND LEFT;;;; +2564;BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND HORIZONTAL DOUBLE;;;; +2565;BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND HORIZONTAL SINGLE;;;; +2566;BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND HORIZONTAL;;;; +2567;BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND HORIZONTAL DOUBLE;;;; +2568;BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND HORIZONTAL SINGLE;;;; +2569;BOX DRAWINGS DOUBLE UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE UP AND HORIZONTAL;;;; +256A;BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND HORIZONTAL DOUBLE;;;; +256B;BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND HORIZONTAL SINGLE;;;; +256C;BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND HORIZONTAL;;;; +2580;UPPER HALF BLOCK;So;0;ON;;;;;N;;;;; +2584;LOWER HALF BLOCK;So;0;ON;;;;;N;;;;; +2588;FULL BLOCK;So;0;ON;;;;;N;;;;; +258C;LEFT HALF BLOCK;So;0;ON;;;;;N;;;;; +2590;RIGHT HALF BLOCK;So;0;ON;;;;;N;;;;; +2591;LIGHT SHADE;So;0;ON;;;;;N;;;;; +2592;MEDIUM SHADE;So;0;ON;;;;;N;;;;; +2593;DARK SHADE;So;0;ON;;;;;N;;;;; +25A0;BLACK SQUARE;So;0;ON;;;;;N;;;;; +FE7D;ARABIC SHADDA MEDIAL FORM;Lo;0;AL; 0640 0651;;;;N;ARABIC SHADDAH ON TATWEEL;;;; +FE80;ARABIC LETTER HAMZA ISOLATED FORM;Lo;0;AL; 0621;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH;;;; +FE81;ARABIC LETTER ALEF WITH MADDA ABOVE ISOLATED FORM;Lo;0;AL; 0622;;;;N;GLYPH FOR ISOLATE ARABIC MADDAH ON ALEF;;;; +FE82;ARABIC LETTER ALEF WITH MADDA ABOVE FINAL FORM;Lo;0;AL; 0622;;;;N;GLYPH FOR FINAL ARABIC MADDAH ON ALEF;;;; +FE83;ARABIC LETTER ALEF WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL; 0623;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON ALEF;;;; +FE84;ARABIC LETTER ALEF WITH HAMZA ABOVE FINAL FORM;Lo;0;AL; 0623;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON ALEF;;;; +FE85;ARABIC LETTER WAW WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL; 0624;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON WAW;;;; +FE8B;ARABIC LETTER YEH WITH HAMZA ABOVE INITIAL FORM;Lo;0;AL; 0626;;;;N;GLYPH FOR INITIAL ARABIC HAMZAH ON YA;;;; +FE8D;ARABIC LETTER ALEF ISOLATED FORM;Lo;0;AL; 0627;;;;N;GLYPH FOR ISOLATE ARABIC ALEF;;;; +FE8E;ARABIC LETTER ALEF FINAL FORM;Lo;0;AL; 0627;;;;N;GLYPH FOR FINAL ARABIC ALEF;;;; +FE8F;ARABIC LETTER BEH ISOLATED FORM;Lo;0;AL; 0628;;;;N;GLYPH FOR ISOLATE ARABIC BAA;;;; +FE91;ARABIC LETTER BEH INITIAL FORM;Lo;0;AL; 0628;;;;N;GLYPH FOR INITIAL ARABIC BAA;;;; +FE93;ARABIC LETTER TEH MARBUTA ISOLATED FORM;Lo;0;AL; 0629;;;;N;GLYPH FOR ISOLATE ARABIC TAA MARBUTAH;;;; +FE95;ARABIC LETTER TEH ISOLATED FORM;Lo;0;AL; 062A;;;;N;GLYPH FOR ISOLATE ARABIC TAA;;;; +FE97;ARABIC LETTER TEH INITIAL FORM;Lo;0;AL; 062A;;;;N;GLYPH FOR INITIAL ARABIC TAA;;;; +FE99;ARABIC LETTER THEH ISOLATED FORM;Lo;0;AL; 062B;;;;N;GLYPH FOR ISOLATE ARABIC THAA;;;; +FE9B;ARABIC LETTER THEH INITIAL FORM;Lo;0;AL; 062B;;;;N;GLYPH FOR INITIAL ARABIC THAA;;;; +FE9D;ARABIC LETTER JEEM ISOLATED FORM;Lo;0;AL; 062C;;;;N;GLYPH FOR ISOLATE ARABIC JEEM;;;; +FE9F;ARABIC LETTER JEEM INITIAL FORM;Lo;0;AL; 062C;;;;N;GLYPH FOR INITIAL ARABIC JEEM;;;; +FEA1;ARABIC LETTER HAH ISOLATED FORM;Lo;0;AL; 062D;;;;N;GLYPH FOR ISOLATE ARABIC HAA;;;; +FEA3;ARABIC LETTER HAH INITIAL FORM;Lo;0;AL; 062D;;;;N;GLYPH FOR INITIAL ARABIC HAA;;;; +FEA5;ARABIC LETTER KHAH ISOLATED FORM;Lo;0;AL; 062E;;;;N;GLYPH FOR ISOLATE ARABIC KHAA;;;; +FEA7;ARABIC LETTER KHAH INITIAL FORM;Lo;0;AL; 062E;;;;N;GLYPH FOR INITIAL ARABIC KHAA;;;; +FEA9;ARABIC LETTER DAL ISOLATED FORM;Lo;0;AL; 062F;;;;N;GLYPH FOR ISOLATE ARABIC DAL;;;; +FEAB;ARABIC LETTER THAL ISOLATED FORM;Lo;0;AL; 0630;;;;N;GLYPH FOR ISOLATE ARABIC THAL;;;; +FEAD;ARABIC LETTER REH ISOLATED FORM;Lo;0;AL; 0631;;;;N;GLYPH FOR ISOLATE ARABIC RA;;;; +FEAF;ARABIC LETTER ZAIN ISOLATED FORM;Lo;0;AL; 0632;;;;N;GLYPH FOR ISOLATE ARABIC ZAIN;;;; +FEB1;ARABIC LETTER SEEN ISOLATED FORM;Lo;0;AL; 0633;;;;N;GLYPH FOR ISOLATE ARABIC SEEN;;;; +FEB3;ARABIC LETTER SEEN INITIAL FORM;Lo;0;AL; 0633;;;;N;GLYPH FOR INITIAL ARABIC SEEN;;;; +FEB5;ARABIC LETTER SHEEN ISOLATED FORM;Lo;0;AL; 0634;;;;N;GLYPH FOR ISOLATE ARABIC SHEEN;;;; +FEB7;ARABIC LETTER SHEEN INITIAL FORM;Lo;0;AL; 0634;;;;N;GLYPH FOR INITIAL ARABIC SHEEN;;;; +FEB9;ARABIC LETTER SAD ISOLATED FORM;Lo;0;AL; 0635;;;;N;GLYPH FOR ISOLATE ARABIC SAD;;;; +FEBB;ARABIC LETTER SAD INITIAL FORM;Lo;0;AL; 0635;;;;N;GLYPH FOR INITIAL ARABIC SAD;;;; +FEBD;ARABIC LETTER DAD ISOLATED FORM;Lo;0;AL; 0636;;;;N;GLYPH FOR ISOLATE ARABIC DAD;;;; +FEBF;ARABIC LETTER DAD INITIAL FORM;Lo;0;AL; 0636;;;;N;GLYPH FOR INITIAL ARABIC DAD;;;; +FEC1;ARABIC LETTER TAH ISOLATED FORM;Lo;0;AL; 0637;;;;N;GLYPH FOR ISOLATE ARABIC TAH;;;; +FEC5;ARABIC LETTER ZAH ISOLATED FORM;Lo;0;AL; 0638;;;;N;GLYPH FOR ISOLATE ARABIC DHAH;;;; +FEC9;ARABIC LETTER AIN ISOLATED FORM;Lo;0;AL; 0639;;;;N;GLYPH FOR ISOLATE ARABIC AIN;;;; +FECA;ARABIC LETTER AIN FINAL FORM;Lo;0;AL; 0639;;;;N;GLYPH FOR FINAL ARABIC AIN;;;; +FECB;ARABIC LETTER AIN INITIAL FORM;Lo;0;AL; 0639;;;;N;GLYPH FOR INITIAL ARABIC AIN;;;; +FECC;ARABIC LETTER AIN MEDIAL FORM;Lo;0;AL; 0639;;;;N;GLYPH FOR MEDIAL ARABIC AIN;;;; +FECD;ARABIC LETTER GHAIN ISOLATED FORM;Lo;0;AL; 063A;;;;N;GLYPH FOR ISOLATE ARABIC GHAIN;;;; +FECE;ARABIC LETTER GHAIN FINAL FORM;Lo;0;AL; 063A;;;;N;GLYPH FOR FINAL ARABIC GHAIN;;;; +FECF;ARABIC LETTER GHAIN INITIAL FORM;Lo;0;AL; 063A;;;;N;GLYPH FOR INITIAL ARABIC GHAIN;;;; +FED0;ARABIC LETTER GHAIN MEDIAL FORM;Lo;0;AL; 063A;;;;N;GLYPH FOR MEDIAL ARABIC GHAIN;;;; +FED1;ARABIC LETTER FEH ISOLATED FORM;Lo;0;AL; 0641;;;;N;GLYPH FOR ISOLATE ARABIC FA;;;; +FED3;ARABIC LETTER FEH INITIAL FORM;Lo;0;AL; 0641;;;;N;GLYPH FOR INITIAL ARABIC FA;;;; +FED5;ARABIC LETTER QAF ISOLATED FORM;Lo;0;AL; 0642;;;;N;GLYPH FOR ISOLATE ARABIC QAF;;;; +FED7;ARABIC LETTER QAF INITIAL FORM;Lo;0;AL; 0642;;;;N;GLYPH FOR INITIAL ARABIC QAF;;;; +FED9;ARABIC LETTER KAF ISOLATED FORM;Lo;0;AL; 0643;;;;N;GLYPH FOR ISOLATE ARABIC CAF;;;; +FEDB;ARABIC LETTER KAF INITIAL FORM;Lo;0;AL; 0643;;;;N;GLYPH FOR INITIAL ARABIC CAF;;;; +FEDD;ARABIC LETTER LAM ISOLATED FORM;Lo;0;AL; 0644;;;;N;GLYPH FOR ISOLATE ARABIC LAM;;;; +FEDF;ARABIC LETTER LAM INITIAL FORM;Lo;0;AL; 0644;;;;N;GLYPH FOR INITIAL ARABIC LAM;;;; +FEE1;ARABIC LETTER MEEM ISOLATED FORM;Lo;0;AL; 0645;;;;N;GLYPH FOR ISOLATE ARABIC MEEM;;;; +FEE3;ARABIC LETTER MEEM INITIAL FORM;Lo;0;AL; 0645;;;;N;GLYPH FOR INITIAL ARABIC MEEM;;;; +FEE5;ARABIC LETTER NOON ISOLATED FORM;Lo;0;AL; 0646;;;;N;GLYPH FOR ISOLATE ARABIC NOON;;;; +FEE7;ARABIC LETTER NOON INITIAL FORM;Lo;0;AL; 0646;;;;N;GLYPH FOR INITIAL ARABIC NOON;;;; +FEE9;ARABIC LETTER HEH ISOLATED FORM;Lo;0;AL; 0647;;;;N;GLYPH FOR ISOLATE ARABIC HA;;;; +FEEB;ARABIC LETTER HEH INITIAL FORM;Lo;0;AL; 0647;;;;N;GLYPH FOR INITIAL ARABIC HA;;;; +FEEC;ARABIC LETTER HEH MEDIAL FORM;Lo;0;AL; 0647;;;;N;GLYPH FOR MEDIAL ARABIC HA;;;; +FEED;ARABIC LETTER WAW ISOLATED FORM;Lo;0;AL; 0648;;;;N;GLYPH FOR ISOLATE ARABIC WAW;;;; +FEEF;ARABIC LETTER ALEF MAKSURA ISOLATED FORM;Lo;0;AL; 0649;;;;N;GLYPH FOR ISOLATE ARABIC ALEF MAQSURAH;;;; +FEF0;ARABIC LETTER ALEF MAKSURA FINAL FORM;Lo;0;AL; 0649;;;;N;GLYPH FOR FINAL ARABIC ALEF MAQSURAH;;;; +FEF1;ARABIC LETTER YEH ISOLATED FORM;Lo;0;AL; 064A;;;;N;GLYPH FOR ISOLATE ARABIC YA;;;; +FEF2;ARABIC LETTER YEH FINAL FORM;Lo;0;AL; 064A;;;;N;GLYPH FOR FINAL ARABIC YA;;;; +FEF3;ARABIC LETTER YEH INITIAL FORM;Lo;0;AL; 064A;;;;N;GLYPH FOR INITIAL ARABIC YA;;;; +FEF5;ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM;Lo;0;AL; 0644 0622;;;;N;GLYPH FOR ISOLATE ARABIC MADDAH ON LIGATURE LAM ALEF;;;; +FEF6;ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM;Lo;0;AL; 0644 0622;;;;N;GLYPH FOR FINAL ARABIC MADDAH ON LIGATURE LAM ALEF;;;; +FEF7;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL; 0644 0623;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON LIGATURE LAM ALEF;;;; +FEF8;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM;Lo;0;AL; 0644 0623;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON LIGATURE LAM ALEF;;;; +FEFB;ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM;Lo;0;AL; 0644 0627;;;;N;GLYPH FOR ISOLATE ARABIC LIGATURE LAM ALEF;;;; +FEFC;ARABIC LIGATURE LAM WITH ALEF FINAL FORM;Lo;0;AL; 0644 0627;;;;N;GLYPH FOR FINAL ARABIC LIGATURE LAM ALEF;;;; diff --git a/codepage/cp1250.txt b/codepage/cp1250.txt new file mode 100644 index 0000000..6bfab93 --- /dev/null +++ b/codepage/cp1250.txt @@ -0,0 +1,274 @@ +# +# Name: cp1250 to Unicode table +# Unicode version: 2.0 +# Table version: 2.01 +# Table format: Format A +# Date: 04/15/98 +# +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp1250 code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp1250 order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0A 0x000A #LINE FEED +0x0B 0x000B #VERTICAL TABULATION +0x0C 0x000C #FORM FEED +0x0D 0x000D #CARRIAGE RETURN +0x0E 0x000E #SHIFT OUT +0x0F 0x000F #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1A 0x001A #SUBSTITUTE +0x1B 0x001B #ESCAPE +0x1C 0x001C #FILE SEPARATOR +0x1D 0x001D #GROUP SEPARATOR +0x1E 0x001E #RECORD SEPARATOR +0x1F 0x001F #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2A 0x002A #ASTERISK +0x2B 0x002B #PLUS SIGN +0x2C 0x002C #COMMA +0x2D 0x002D #HYPHEN-MINUS +0x2E 0x002E #FULL STOP +0x2F 0x002F #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3A 0x003A #COLON +0x3B 0x003B #SEMICOLON +0x3C 0x003C #LESS-THAN SIGN +0x3D 0x003D #EQUALS SIGN +0x3E 0x003E #GREATER-THAN SIGN +0x3F 0x003F #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4A 0x004A #LATIN CAPITAL LETTER J +0x4B 0x004B #LATIN CAPITAL LETTER K +0x4C 0x004C #LATIN CAPITAL LETTER L +0x4D 0x004D #LATIN CAPITAL LETTER M +0x4E 0x004E #LATIN CAPITAL LETTER N +0x4F 0x004F #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5A 0x005A #LATIN CAPITAL LETTER Z +0x5B 0x005B #LEFT SQUARE BRACKET +0x5C 0x005C #REVERSE SOLIDUS +0x5D 0x005D #RIGHT SQUARE BRACKET +0x5E 0x005E #CIRCUMFLEX ACCENT +0x5F 0x005F #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6A 0x006A #LATIN SMALL LETTER J +0x6B 0x006B #LATIN SMALL LETTER K +0x6C 0x006C #LATIN SMALL LETTER L +0x6D 0x006D #LATIN SMALL LETTER M +0x6E 0x006E #LATIN SMALL LETTER N +0x6F 0x006F #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7A 0x007A #LATIN SMALL LETTER Z +0x7B 0x007B #LEFT CURLY BRACKET +0x7C 0x007C #VERTICAL LINE +0x7D 0x007D #RIGHT CURLY BRACKET +0x7E 0x007E #TILDE +0x7F 0x007F #DELETE +0x80 0x20AC #EURO SIGN +0x81 #UNDEFINED +0x82 0x201A #SINGLE LOW-9 QUOTATION MARK +0x83 #UNDEFINED +0x84 0x201E #DOUBLE LOW-9 QUOTATION MARK +0x85 0x2026 #HORIZONTAL ELLIPSIS +0x86 0x2020 #DAGGER +0x87 0x2021 #DOUBLE DAGGER +0x88 #UNDEFINED +0x89 0x2030 #PER MILLE SIGN +0x8A 0x0160 #LATIN CAPITAL LETTER S WITH CARON +0x8B 0x2039 #SINGLE LEFT-POINTING ANGLE QUOTATION MARK +0x8C 0x015A #LATIN CAPITAL LETTER S WITH ACUTE +0x8D 0x0164 #LATIN CAPITAL LETTER T WITH CARON +0x8E 0x017D #LATIN CAPITAL LETTER Z WITH CARON +0x8F 0x0179 #LATIN CAPITAL LETTER Z WITH ACUTE +0x90 #UNDEFINED +0x91 0x2018 #LEFT SINGLE QUOTATION MARK +0x92 0x2019 #RIGHT SINGLE QUOTATION MARK +0x93 0x201C #LEFT DOUBLE QUOTATION MARK +0x94 0x201D #RIGHT DOUBLE QUOTATION MARK +0x95 0x2022 #BULLET +0x96 0x2013 #EN DASH +0x97 0x2014 #EM DASH +0x98 #UNDEFINED +0x99 0x2122 #TRADE MARK SIGN +0x9A 0x0161 #LATIN SMALL LETTER S WITH CARON +0x9B 0x203A #SINGLE RIGHT-POINTING ANGLE QUOTATION MARK +0x9C 0x015B #LATIN SMALL LETTER S WITH ACUTE +0x9D 0x0165 #LATIN SMALL LETTER T WITH CARON +0x9E 0x017E #LATIN SMALL LETTER Z WITH CARON +0x9F 0x017A #LATIN SMALL LETTER Z WITH ACUTE +0xA0 0x00A0 #NO-BREAK SPACE +0xA1 0x02C7 #CARON +0xA2 0x02D8 #BREVE +0xA3 0x0141 #LATIN CAPITAL LETTER L WITH STROKE +0xA4 0x00A4 #CURRENCY SIGN +0xA5 0x0104 #LATIN CAPITAL LETTER A WITH OGONEK +0xA6 0x00A6 #BROKEN BAR +0xA7 0x00A7 #SECTION SIGN +0xA8 0x00A8 #DIAERESIS +0xA9 0x00A9 #COPYRIGHT SIGN +0xAA 0x015E #LATIN CAPITAL LETTER S WITH CEDILLA +0xAB 0x00AB #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xAC 0x00AC #NOT SIGN +0xAD 0x00AD #SOFT HYPHEN +0xAE 0x00AE #REGISTERED SIGN +0xAF 0x017B #LATIN CAPITAL LETTER Z WITH DOT ABOVE +0xB0 0x00B0 #DEGREE SIGN +0xB1 0x00B1 #PLUS-MINUS SIGN +0xB2 0x02DB #OGONEK +0xB3 0x0142 #LATIN SMALL LETTER L WITH STROKE +0xB4 0x00B4 #ACUTE ACCENT +0xB5 0x00B5 #MICRO SIGN +0xB6 0x00B6 #PILCROW SIGN +0xB7 0x00B7 #MIDDLE DOT +0xB8 0x00B8 #CEDILLA +0xB9 0x0105 #LATIN SMALL LETTER A WITH OGONEK +0xBA 0x015F #LATIN SMALL LETTER S WITH CEDILLA +0xBB 0x00BB #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xBC 0x013D #LATIN CAPITAL LETTER L WITH CARON +0xBD 0x02DD #DOUBLE ACUTE ACCENT +0xBE 0x013E #LATIN SMALL LETTER L WITH CARON +0xBF 0x017C #LATIN SMALL LETTER Z WITH DOT ABOVE +0xC0 0x0154 #LATIN CAPITAL LETTER R WITH ACUTE +0xC1 0x00C1 #LATIN CAPITAL LETTER A WITH ACUTE +0xC2 0x00C2 #LATIN CAPITAL LETTER A WITH CIRCUMFLEX +0xC3 0x0102 #LATIN CAPITAL LETTER A WITH BREVE +0xC4 0x00C4 #LATIN CAPITAL LETTER A WITH DIAERESIS +0xC5 0x0139 #LATIN CAPITAL LETTER L WITH ACUTE +0xC6 0x0106 #LATIN CAPITAL LETTER C WITH ACUTE +0xC7 0x00C7 #LATIN CAPITAL LETTER C WITH CEDILLA +0xC8 0x010C #LATIN CAPITAL LETTER C WITH CARON +0xC9 0x00C9 #LATIN CAPITAL LETTER E WITH ACUTE +0xCA 0x0118 #LATIN CAPITAL LETTER E WITH OGONEK +0xCB 0x00CB #LATIN CAPITAL LETTER E WITH DIAERESIS +0xCC 0x011A #LATIN CAPITAL LETTER E WITH CARON +0xCD 0x00CD #LATIN CAPITAL LETTER I WITH ACUTE +0xCE 0x00CE #LATIN CAPITAL LETTER I WITH CIRCUMFLEX +0xCF 0x010E #LATIN CAPITAL LETTER D WITH CARON +0xD0 0x0110 #LATIN CAPITAL LETTER D WITH STROKE +0xD1 0x0143 #LATIN CAPITAL LETTER N WITH ACUTE +0xD2 0x0147 #LATIN CAPITAL LETTER N WITH CARON +0xD3 0x00D3 #LATIN CAPITAL LETTER O WITH ACUTE +0xD4 0x00D4 #LATIN CAPITAL LETTER O WITH CIRCUMFLEX +0xD5 0x0150 #LATIN CAPITAL LETTER O WITH DOUBLE ACUTE +0xD6 0x00D6 #LATIN CAPITAL LETTER O WITH DIAERESIS +0xD7 0x00D7 #MULTIPLICATION SIGN +0xD8 0x0158 #LATIN CAPITAL LETTER R WITH CARON +0xD9 0x016E #LATIN CAPITAL LETTER U WITH RING ABOVE +0xDA 0x00DA #LATIN CAPITAL LETTER U WITH ACUTE +0xDB 0x0170 #LATIN CAPITAL LETTER U WITH DOUBLE ACUTE +0xDC 0x00DC #LATIN CAPITAL LETTER U WITH DIAERESIS +0xDD 0x00DD #LATIN CAPITAL LETTER Y WITH ACUTE +0xDE 0x0162 #LATIN CAPITAL LETTER T WITH CEDILLA +0xDF 0x00DF #LATIN SMALL LETTER SHARP S +0xE0 0x0155 #LATIN SMALL LETTER R WITH ACUTE +0xE1 0x00E1 #LATIN SMALL LETTER A WITH ACUTE +0xE2 0x00E2 #LATIN SMALL LETTER A WITH CIRCUMFLEX +0xE3 0x0103 #LATIN SMALL LETTER A WITH BREVE +0xE4 0x00E4 #LATIN SMALL LETTER A WITH DIAERESIS +0xE5 0x013A #LATIN SMALL LETTER L WITH ACUTE +0xE6 0x0107 #LATIN SMALL LETTER C WITH ACUTE +0xE7 0x00E7 #LATIN SMALL LETTER C WITH CEDILLA +0xE8 0x010D #LATIN SMALL LETTER C WITH CARON +0xE9 0x00E9 #LATIN SMALL LETTER E WITH ACUTE +0xEA 0x0119 #LATIN SMALL LETTER E WITH OGONEK +0xEB 0x00EB #LATIN SMALL LETTER E WITH DIAERESIS +0xEC 0x011B #LATIN SMALL LETTER E WITH CARON +0xED 0x00ED #LATIN SMALL LETTER I WITH ACUTE +0xEE 0x00EE #LATIN SMALL LETTER I WITH CIRCUMFLEX +0xEF 0x010F #LATIN SMALL LETTER D WITH CARON +0xF0 0x0111 #LATIN SMALL LETTER D WITH STROKE +0xF1 0x0144 #LATIN SMALL LETTER N WITH ACUTE +0xF2 0x0148 #LATIN SMALL LETTER N WITH CARON +0xF3 0x00F3 #LATIN SMALL LETTER O WITH ACUTE +0xF4 0x00F4 #LATIN SMALL LETTER O WITH CIRCUMFLEX +0xF5 0x0151 #LATIN SMALL LETTER O WITH DOUBLE ACUTE +0xF6 0x00F6 #LATIN SMALL LETTER O WITH DIAERESIS +0xF7 0x00F7 #DIVISION SIGN +0xF8 0x0159 #LATIN SMALL LETTER R WITH CARON +0xF9 0x016F #LATIN SMALL LETTER U WITH RING ABOVE +0xFA 0x00FA #LATIN SMALL LETTER U WITH ACUTE +0xFB 0x0171 #LATIN SMALL LETTER U WITH DOUBLE ACUTE +0xFC 0x00FC #LATIN SMALL LETTER U WITH DIAERESIS +0xFD 0x00FD #LATIN SMALL LETTER Y WITH ACUTE +0xFE 0x0163 #LATIN SMALL LETTER T WITH CEDILLA +0xFF 0x02D9 #DOT ABOVE diff --git a/codepage/cp1251.txt b/codepage/cp1251.txt new file mode 100644 index 0000000..4d9b355 --- /dev/null +++ b/codepage/cp1251.txt @@ -0,0 +1,274 @@ +# +# Name: cp1251 to Unicode table +# Unicode version: 2.0 +# Table version: 2.01 +# Table format: Format A +# Date: 04/15/98 +# +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp1251 code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp1251 order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0A 0x000A #LINE FEED +0x0B 0x000B #VERTICAL TABULATION +0x0C 0x000C #FORM FEED +0x0D 0x000D #CARRIAGE RETURN +0x0E 0x000E #SHIFT OUT +0x0F 0x000F #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1A 0x001A #SUBSTITUTE +0x1B 0x001B #ESCAPE +0x1C 0x001C #FILE SEPARATOR +0x1D 0x001D #GROUP SEPARATOR +0x1E 0x001E #RECORD SEPARATOR +0x1F 0x001F #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2A 0x002A #ASTERISK +0x2B 0x002B #PLUS SIGN +0x2C 0x002C #COMMA +0x2D 0x002D #HYPHEN-MINUS +0x2E 0x002E #FULL STOP +0x2F 0x002F #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3A 0x003A #COLON +0x3B 0x003B #SEMICOLON +0x3C 0x003C #LESS-THAN SIGN +0x3D 0x003D #EQUALS SIGN +0x3E 0x003E #GREATER-THAN SIGN +0x3F 0x003F #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4A 0x004A #LATIN CAPITAL LETTER J +0x4B 0x004B #LATIN CAPITAL LETTER K +0x4C 0x004C #LATIN CAPITAL LETTER L +0x4D 0x004D #LATIN CAPITAL LETTER M +0x4E 0x004E #LATIN CAPITAL LETTER N +0x4F 0x004F #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5A 0x005A #LATIN CAPITAL LETTER Z +0x5B 0x005B #LEFT SQUARE BRACKET +0x5C 0x005C #REVERSE SOLIDUS +0x5D 0x005D #RIGHT SQUARE BRACKET +0x5E 0x005E #CIRCUMFLEX ACCENT +0x5F 0x005F #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6A 0x006A #LATIN SMALL LETTER J +0x6B 0x006B #LATIN SMALL LETTER K +0x6C 0x006C #LATIN SMALL LETTER L +0x6D 0x006D #LATIN SMALL LETTER M +0x6E 0x006E #LATIN SMALL LETTER N +0x6F 0x006F #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7A 0x007A #LATIN SMALL LETTER Z +0x7B 0x007B #LEFT CURLY BRACKET +0x7C 0x007C #VERTICAL LINE +0x7D 0x007D #RIGHT CURLY BRACKET +0x7E 0x007E #TILDE +0x7F 0x007F #DELETE +0x80 0x0402 #CYRILLIC CAPITAL LETTER DJE +0x81 0x0403 #CYRILLIC CAPITAL LETTER GJE +0x82 0x201A #SINGLE LOW-9 QUOTATION MARK +0x83 0x0453 #CYRILLIC SMALL LETTER GJE +0x84 0x201E #DOUBLE LOW-9 QUOTATION MARK +0x85 0x2026 #HORIZONTAL ELLIPSIS +0x86 0x2020 #DAGGER +0x87 0x2021 #DOUBLE DAGGER +0x88 0x20AC #EURO SIGN +0x89 0x2030 #PER MILLE SIGN +0x8A 0x0409 #CYRILLIC CAPITAL LETTER LJE +0x8B 0x2039 #SINGLE LEFT-POINTING ANGLE QUOTATION MARK +0x8C 0x040A #CYRILLIC CAPITAL LETTER NJE +0x8D 0x040C #CYRILLIC CAPITAL LETTER KJE +0x8E 0x040B #CYRILLIC CAPITAL LETTER TSHE +0x8F 0x040F #CYRILLIC CAPITAL LETTER DZHE +0x90 0x0452 #CYRILLIC SMALL LETTER DJE +0x91 0x2018 #LEFT SINGLE QUOTATION MARK +0x92 0x2019 #RIGHT SINGLE QUOTATION MARK +0x93 0x201C #LEFT DOUBLE QUOTATION MARK +0x94 0x201D #RIGHT DOUBLE QUOTATION MARK +0x95 0x2022 #BULLET +0x96 0x2013 #EN DASH +0x97 0x2014 #EM DASH +0x98 #UNDEFINED +0x99 0x2122 #TRADE MARK SIGN +0x9A 0x0459 #CYRILLIC SMALL LETTER LJE +0x9B 0x203A #SINGLE RIGHT-POINTING ANGLE QUOTATION MARK +0x9C 0x045A #CYRILLIC SMALL LETTER NJE +0x9D 0x045C #CYRILLIC SMALL LETTER KJE +0x9E 0x045B #CYRILLIC SMALL LETTER TSHE +0x9F 0x045F #CYRILLIC SMALL LETTER DZHE +0xA0 0x00A0 #NO-BREAK SPACE +0xA1 0x040E #CYRILLIC CAPITAL LETTER SHORT U +0xA2 0x045E #CYRILLIC SMALL LETTER SHORT U +0xA3 0x0408 #CYRILLIC CAPITAL LETTER JE +0xA4 0x00A4 #CURRENCY SIGN +0xA5 0x0490 #CYRILLIC CAPITAL LETTER GHE WITH UPTURN +0xA6 0x00A6 #BROKEN BAR +0xA7 0x00A7 #SECTION SIGN +0xA8 0x0401 #CYRILLIC CAPITAL LETTER IO +0xA9 0x00A9 #COPYRIGHT SIGN +0xAA 0x0404 #CYRILLIC CAPITAL LETTER UKRAINIAN IE +0xAB 0x00AB #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xAC 0x00AC #NOT SIGN +0xAD 0x00AD #SOFT HYPHEN +0xAE 0x00AE #REGISTERED SIGN +0xAF 0x0407 #CYRILLIC CAPITAL LETTER YI +0xB0 0x00B0 #DEGREE SIGN +0xB1 0x00B1 #PLUS-MINUS SIGN +0xB2 0x0406 #CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I +0xB3 0x0456 #CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I +0xB4 0x0491 #CYRILLIC SMALL LETTER GHE WITH UPTURN +0xB5 0x00B5 #MICRO SIGN +0xB6 0x00B6 #PILCROW SIGN +0xB7 0x00B7 #MIDDLE DOT +0xB8 0x0451 #CYRILLIC SMALL LETTER IO +0xB9 0x2116 #NUMERO SIGN +0xBA 0x0454 #CYRILLIC SMALL LETTER UKRAINIAN IE +0xBB 0x00BB #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xBC 0x0458 #CYRILLIC SMALL LETTER JE +0xBD 0x0405 #CYRILLIC CAPITAL LETTER DZE +0xBE 0x0455 #CYRILLIC SMALL LETTER DZE +0xBF 0x0457 #CYRILLIC SMALL LETTER YI +0xC0 0x0410 #CYRILLIC CAPITAL LETTER A +0xC1 0x0411 #CYRILLIC CAPITAL LETTER BE +0xC2 0x0412 #CYRILLIC CAPITAL LETTER VE +0xC3 0x0413 #CYRILLIC CAPITAL LETTER GHE +0xC4 0x0414 #CYRILLIC CAPITAL LETTER DE +0xC5 0x0415 #CYRILLIC CAPITAL LETTER IE +0xC6 0x0416 #CYRILLIC CAPITAL LETTER ZHE +0xC7 0x0417 #CYRILLIC CAPITAL LETTER ZE +0xC8 0x0418 #CYRILLIC CAPITAL LETTER I +0xC9 0x0419 #CYRILLIC CAPITAL LETTER SHORT I +0xCA 0x041A #CYRILLIC CAPITAL LETTER KA +0xCB 0x041B #CYRILLIC CAPITAL LETTER EL +0xCC 0x041C #CYRILLIC CAPITAL LETTER EM +0xCD 0x041D #CYRILLIC CAPITAL LETTER EN +0xCE 0x041E #CYRILLIC CAPITAL LETTER O +0xCF 0x041F #CYRILLIC CAPITAL LETTER PE +0xD0 0x0420 #CYRILLIC CAPITAL LETTER ER +0xD1 0x0421 #CYRILLIC CAPITAL LETTER ES +0xD2 0x0422 #CYRILLIC CAPITAL LETTER TE +0xD3 0x0423 #CYRILLIC CAPITAL LETTER U +0xD4 0x0424 #CYRILLIC CAPITAL LETTER EF +0xD5 0x0425 #CYRILLIC CAPITAL LETTER HA +0xD6 0x0426 #CYRILLIC CAPITAL LETTER TSE +0xD7 0x0427 #CYRILLIC CAPITAL LETTER CHE +0xD8 0x0428 #CYRILLIC CAPITAL LETTER SHA +0xD9 0x0429 #CYRILLIC CAPITAL LETTER SHCHA +0xDA 0x042A #CYRILLIC CAPITAL LETTER HARD SIGN +0xDB 0x042B #CYRILLIC CAPITAL LETTER YERU +0xDC 0x042C #CYRILLIC CAPITAL LETTER SOFT SIGN +0xDD 0x042D #CYRILLIC CAPITAL LETTER E +0xDE 0x042E #CYRILLIC CAPITAL LETTER YU +0xDF 0x042F #CYRILLIC CAPITAL LETTER YA +0xE0 0x0430 #CYRILLIC SMALL LETTER A +0xE1 0x0431 #CYRILLIC SMALL LETTER BE +0xE2 0x0432 #CYRILLIC SMALL LETTER VE +0xE3 0x0433 #CYRILLIC SMALL LETTER GHE +0xE4 0x0434 #CYRILLIC SMALL LETTER DE +0xE5 0x0435 #CYRILLIC SMALL LETTER IE +0xE6 0x0436 #CYRILLIC SMALL LETTER ZHE +0xE7 0x0437 #CYRILLIC SMALL LETTER ZE +0xE8 0x0438 #CYRILLIC SMALL LETTER I +0xE9 0x0439 #CYRILLIC SMALL LETTER SHORT I +0xEA 0x043A #CYRILLIC SMALL LETTER KA +0xEB 0x043B #CYRILLIC SMALL LETTER EL +0xEC 0x043C #CYRILLIC SMALL LETTER EM +0xED 0x043D #CYRILLIC SMALL LETTER EN +0xEE 0x043E #CYRILLIC SMALL LETTER O +0xEF 0x043F #CYRILLIC SMALL LETTER PE +0xF0 0x0440 #CYRILLIC SMALL LETTER ER +0xF1 0x0441 #CYRILLIC SMALL LETTER ES +0xF2 0x0442 #CYRILLIC SMALL LETTER TE +0xF3 0x0443 #CYRILLIC SMALL LETTER U +0xF4 0x0444 #CYRILLIC SMALL LETTER EF +0xF5 0x0445 #CYRILLIC SMALL LETTER HA +0xF6 0x0446 #CYRILLIC SMALL LETTER TSE +0xF7 0x0447 #CYRILLIC SMALL LETTER CHE +0xF8 0x0448 #CYRILLIC SMALL LETTER SHA +0xF9 0x0449 #CYRILLIC SMALL LETTER SHCHA +0xFA 0x044A #CYRILLIC SMALL LETTER HARD SIGN +0xFB 0x044B #CYRILLIC SMALL LETTER YERU +0xFC 0x044C #CYRILLIC SMALL LETTER SOFT SIGN +0xFD 0x044D #CYRILLIC SMALL LETTER E +0xFE 0x044E #CYRILLIC SMALL LETTER YU +0xFF 0x044F #CYRILLIC SMALL LETTER YA diff --git a/codepage/cp1252.txt b/codepage/cp1252.txt new file mode 100644 index 0000000..8ff4b20 --- /dev/null +++ b/codepage/cp1252.txt @@ -0,0 +1,274 @@ +# +# Name: cp1252 to Unicode table +# Unicode version: 2.0 +# Table version: 2.01 +# Table format: Format A +# Date: 04/15/98 +# +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp1252 code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp1252 order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0A 0x000A #LINE FEED +0x0B 0x000B #VERTICAL TABULATION +0x0C 0x000C #FORM FEED +0x0D 0x000D #CARRIAGE RETURN +0x0E 0x000E #SHIFT OUT +0x0F 0x000F #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1A 0x001A #SUBSTITUTE +0x1B 0x001B #ESCAPE +0x1C 0x001C #FILE SEPARATOR +0x1D 0x001D #GROUP SEPARATOR +0x1E 0x001E #RECORD SEPARATOR +0x1F 0x001F #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2A 0x002A #ASTERISK +0x2B 0x002B #PLUS SIGN +0x2C 0x002C #COMMA +0x2D 0x002D #HYPHEN-MINUS +0x2E 0x002E #FULL STOP +0x2F 0x002F #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3A 0x003A #COLON +0x3B 0x003B #SEMICOLON +0x3C 0x003C #LESS-THAN SIGN +0x3D 0x003D #EQUALS SIGN +0x3E 0x003E #GREATER-THAN SIGN +0x3F 0x003F #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4A 0x004A #LATIN CAPITAL LETTER J +0x4B 0x004B #LATIN CAPITAL LETTER K +0x4C 0x004C #LATIN CAPITAL LETTER L +0x4D 0x004D #LATIN CAPITAL LETTER M +0x4E 0x004E #LATIN CAPITAL LETTER N +0x4F 0x004F #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5A 0x005A #LATIN CAPITAL LETTER Z +0x5B 0x005B #LEFT SQUARE BRACKET +0x5C 0x005C #REVERSE SOLIDUS +0x5D 0x005D #RIGHT SQUARE BRACKET +0x5E 0x005E #CIRCUMFLEX ACCENT +0x5F 0x005F #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6A 0x006A #LATIN SMALL LETTER J +0x6B 0x006B #LATIN SMALL LETTER K +0x6C 0x006C #LATIN SMALL LETTER L +0x6D 0x006D #LATIN SMALL LETTER M +0x6E 0x006E #LATIN SMALL LETTER N +0x6F 0x006F #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7A 0x007A #LATIN SMALL LETTER Z +0x7B 0x007B #LEFT CURLY BRACKET +0x7C 0x007C #VERTICAL LINE +0x7D 0x007D #RIGHT CURLY BRACKET +0x7E 0x007E #TILDE +0x7F 0x007F #DELETE +0x80 0x20AC #EURO SIGN +0x81 #UNDEFINED +0x82 0x201A #SINGLE LOW-9 QUOTATION MARK +0x83 0x0192 #LATIN SMALL LETTER F WITH HOOK +0x84 0x201E #DOUBLE LOW-9 QUOTATION MARK +0x85 0x2026 #HORIZONTAL ELLIPSIS +0x86 0x2020 #DAGGER +0x87 0x2021 #DOUBLE DAGGER +0x88 0x02C6 #MODIFIER LETTER CIRCUMFLEX ACCENT +0x89 0x2030 #PER MILLE SIGN +0x8A 0x0160 #LATIN CAPITAL LETTER S WITH CARON +0x8B 0x2039 #SINGLE LEFT-POINTING ANGLE QUOTATION MARK +0x8C 0x0152 #LATIN CAPITAL LIGATURE OE +0x8D #UNDEFINED +0x8E 0x017D #LATIN CAPITAL LETTER Z WITH CARON +0x8F #UNDEFINED +0x90 #UNDEFINED +0x91 0x2018 #LEFT SINGLE QUOTATION MARK +0x92 0x2019 #RIGHT SINGLE QUOTATION MARK +0x93 0x201C #LEFT DOUBLE QUOTATION MARK +0x94 0x201D #RIGHT DOUBLE QUOTATION MARK +0x95 0x2022 #BULLET +0x96 0x2013 #EN DASH +0x97 0x2014 #EM DASH +0x98 0x02DC #SMALL TILDE +0x99 0x2122 #TRADE MARK SIGN +0x9A 0x0161 #LATIN SMALL LETTER S WITH CARON +0x9B 0x203A #SINGLE RIGHT-POINTING ANGLE QUOTATION MARK +0x9C 0x0153 #LATIN SMALL LIGATURE OE +0x9D #UNDEFINED +0x9E 0x017E #LATIN SMALL LETTER Z WITH CARON +0x9F 0x0178 #LATIN CAPITAL LETTER Y WITH DIAERESIS +0xA0 0x00A0 #NO-BREAK SPACE +0xA1 0x00A1 #INVERTED EXCLAMATION MARK +0xA2 0x00A2 #CENT SIGN +0xA3 0x00A3 #POUND SIGN +0xA4 0x00A4 #CURRENCY SIGN +0xA5 0x00A5 #YEN SIGN +0xA6 0x00A6 #BROKEN BAR +0xA7 0x00A7 #SECTION SIGN +0xA8 0x00A8 #DIAERESIS +0xA9 0x00A9 #COPYRIGHT SIGN +0xAA 0x00AA #FEMININE ORDINAL INDICATOR +0xAB 0x00AB #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xAC 0x00AC #NOT SIGN +0xAD 0x00AD #SOFT HYPHEN +0xAE 0x00AE #REGISTERED SIGN +0xAF 0x00AF #MACRON +0xB0 0x00B0 #DEGREE SIGN +0xB1 0x00B1 #PLUS-MINUS SIGN +0xB2 0x00B2 #SUPERSCRIPT TWO +0xB3 0x00B3 #SUPERSCRIPT THREE +0xB4 0x00B4 #ACUTE ACCENT +0xB5 0x00B5 #MICRO SIGN +0xB6 0x00B6 #PILCROW SIGN +0xB7 0x00B7 #MIDDLE DOT +0xB8 0x00B8 #CEDILLA +0xB9 0x00B9 #SUPERSCRIPT ONE +0xBA 0x00BA #MASCULINE ORDINAL INDICATOR +0xBB 0x00BB #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xBC 0x00BC #VULGAR FRACTION ONE QUARTER +0xBD 0x00BD #VULGAR FRACTION ONE HALF +0xBE 0x00BE #VULGAR FRACTION THREE QUARTERS +0xBF 0x00BF #INVERTED QUESTION MARK +0xC0 0x00C0 #LATIN CAPITAL LETTER A WITH GRAVE +0xC1 0x00C1 #LATIN CAPITAL LETTER A WITH ACUTE +0xC2 0x00C2 #LATIN CAPITAL LETTER A WITH CIRCUMFLEX +0xC3 0x00C3 #LATIN CAPITAL LETTER A WITH TILDE +0xC4 0x00C4 #LATIN CAPITAL LETTER A WITH DIAERESIS +0xC5 0x00C5 #LATIN CAPITAL LETTER A WITH RING ABOVE +0xC6 0x00C6 #LATIN CAPITAL LETTER AE +0xC7 0x00C7 #LATIN CAPITAL LETTER C WITH CEDILLA +0xC8 0x00C8 #LATIN CAPITAL LETTER E WITH GRAVE +0xC9 0x00C9 #LATIN CAPITAL LETTER E WITH ACUTE +0xCA 0x00CA #LATIN CAPITAL LETTER E WITH CIRCUMFLEX +0xCB 0x00CB #LATIN CAPITAL LETTER E WITH DIAERESIS +0xCC 0x00CC #LATIN CAPITAL LETTER I WITH GRAVE +0xCD 0x00CD #LATIN CAPITAL LETTER I WITH ACUTE +0xCE 0x00CE #LATIN CAPITAL LETTER I WITH CIRCUMFLEX +0xCF 0x00CF #LATIN CAPITAL LETTER I WITH DIAERESIS +0xD0 0x00D0 #LATIN CAPITAL LETTER ETH +0xD1 0x00D1 #LATIN CAPITAL LETTER N WITH TILDE +0xD2 0x00D2 #LATIN CAPITAL LETTER O WITH GRAVE +0xD3 0x00D3 #LATIN CAPITAL LETTER O WITH ACUTE +0xD4 0x00D4 #LATIN CAPITAL LETTER O WITH CIRCUMFLEX +0xD5 0x00D5 #LATIN CAPITAL LETTER O WITH TILDE +0xD6 0x00D6 #LATIN CAPITAL LETTER O WITH DIAERESIS +0xD7 0x00D7 #MULTIPLICATION SIGN +0xD8 0x00D8 #LATIN CAPITAL LETTER O WITH STROKE +0xD9 0x00D9 #LATIN CAPITAL LETTER U WITH GRAVE +0xDA 0x00DA #LATIN CAPITAL LETTER U WITH ACUTE +0xDB 0x00DB #LATIN CAPITAL LETTER U WITH CIRCUMFLEX +0xDC 0x00DC #LATIN CAPITAL LETTER U WITH DIAERESIS +0xDD 0x00DD #LATIN CAPITAL LETTER Y WITH ACUTE +0xDE 0x00DE #LATIN CAPITAL LETTER THORN +0xDF 0x00DF #LATIN SMALL LETTER SHARP S +0xE0 0x00E0 #LATIN SMALL LETTER A WITH GRAVE +0xE1 0x00E1 #LATIN SMALL LETTER A WITH ACUTE +0xE2 0x00E2 #LATIN SMALL LETTER A WITH CIRCUMFLEX +0xE3 0x00E3 #LATIN SMALL LETTER A WITH TILDE +0xE4 0x00E4 #LATIN SMALL LETTER A WITH DIAERESIS +0xE5 0x00E5 #LATIN SMALL LETTER A WITH RING ABOVE +0xE6 0x00E6 #LATIN SMALL LETTER AE +0xE7 0x00E7 #LATIN SMALL LETTER C WITH CEDILLA +0xE8 0x00E8 #LATIN SMALL LETTER E WITH GRAVE +0xE9 0x00E9 #LATIN SMALL LETTER E WITH ACUTE +0xEA 0x00EA #LATIN SMALL LETTER E WITH CIRCUMFLEX +0xEB 0x00EB #LATIN SMALL LETTER E WITH DIAERESIS +0xEC 0x00EC #LATIN SMALL LETTER I WITH GRAVE +0xED 0x00ED #LATIN SMALL LETTER I WITH ACUTE +0xEE 0x00EE #LATIN SMALL LETTER I WITH CIRCUMFLEX +0xEF 0x00EF #LATIN SMALL LETTER I WITH DIAERESIS +0xF0 0x00F0 #LATIN SMALL LETTER ETH +0xF1 0x00F1 #LATIN SMALL LETTER N WITH TILDE +0xF2 0x00F2 #LATIN SMALL LETTER O WITH GRAVE +0xF3 0x00F3 #LATIN SMALL LETTER O WITH ACUTE +0xF4 0x00F4 #LATIN SMALL LETTER O WITH CIRCUMFLEX +0xF5 0x00F5 #LATIN SMALL LETTER O WITH TILDE +0xF6 0x00F6 #LATIN SMALL LETTER O WITH DIAERESIS +0xF7 0x00F7 #DIVISION SIGN +0xF8 0x00F8 #LATIN SMALL LETTER O WITH STROKE +0xF9 0x00F9 #LATIN SMALL LETTER U WITH GRAVE +0xFA 0x00FA #LATIN SMALL LETTER U WITH ACUTE +0xFB 0x00FB #LATIN SMALL LETTER U WITH CIRCUMFLEX +0xFC 0x00FC #LATIN SMALL LETTER U WITH DIAERESIS +0xFD 0x00FD #LATIN SMALL LETTER Y WITH ACUTE +0xFE 0x00FE #LATIN SMALL LETTER THORN +0xFF 0x00FF #LATIN SMALL LETTER Y WITH DIAERESIS diff --git a/codepage/cp1253.txt b/codepage/cp1253.txt new file mode 100644 index 0000000..20a55b0 --- /dev/null +++ b/codepage/cp1253.txt @@ -0,0 +1,274 @@ +# +# Name: cp1253 to Unicode table +# Unicode version: 2.0 +# Table version: 2.01 +# Table format: Format A +# Date: 04/15/98 +# +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp1253 code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp1253 order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0A 0x000A #LINE FEED +0x0B 0x000B #VERTICAL TABULATION +0x0C 0x000C #FORM FEED +0x0D 0x000D #CARRIAGE RETURN +0x0E 0x000E #SHIFT OUT +0x0F 0x000F #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1A 0x001A #SUBSTITUTE +0x1B 0x001B #ESCAPE +0x1C 0x001C #FILE SEPARATOR +0x1D 0x001D #GROUP SEPARATOR +0x1E 0x001E #RECORD SEPARATOR +0x1F 0x001F #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2A 0x002A #ASTERISK +0x2B 0x002B #PLUS SIGN +0x2C 0x002C #COMMA +0x2D 0x002D #HYPHEN-MINUS +0x2E 0x002E #FULL STOP +0x2F 0x002F #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3A 0x003A #COLON +0x3B 0x003B #SEMICOLON +0x3C 0x003C #LESS-THAN SIGN +0x3D 0x003D #EQUALS SIGN +0x3E 0x003E #GREATER-THAN SIGN +0x3F 0x003F #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4A 0x004A #LATIN CAPITAL LETTER J +0x4B 0x004B #LATIN CAPITAL LETTER K +0x4C 0x004C #LATIN CAPITAL LETTER L +0x4D 0x004D #LATIN CAPITAL LETTER M +0x4E 0x004E #LATIN CAPITAL LETTER N +0x4F 0x004F #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5A 0x005A #LATIN CAPITAL LETTER Z +0x5B 0x005B #LEFT SQUARE BRACKET +0x5C 0x005C #REVERSE SOLIDUS +0x5D 0x005D #RIGHT SQUARE BRACKET +0x5E 0x005E #CIRCUMFLEX ACCENT +0x5F 0x005F #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6A 0x006A #LATIN SMALL LETTER J +0x6B 0x006B #LATIN SMALL LETTER K +0x6C 0x006C #LATIN SMALL LETTER L +0x6D 0x006D #LATIN SMALL LETTER M +0x6E 0x006E #LATIN SMALL LETTER N +0x6F 0x006F #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7A 0x007A #LATIN SMALL LETTER Z +0x7B 0x007B #LEFT CURLY BRACKET +0x7C 0x007C #VERTICAL LINE +0x7D 0x007D #RIGHT CURLY BRACKET +0x7E 0x007E #TILDE +0x7F 0x007F #DELETE +0x80 0x20AC #EURO SIGN +0x81 #UNDEFINED +0x82 0x201A #SINGLE LOW-9 QUOTATION MARK +0x83 0x0192 #LATIN SMALL LETTER F WITH HOOK +0x84 0x201E #DOUBLE LOW-9 QUOTATION MARK +0x85 0x2026 #HORIZONTAL ELLIPSIS +0x86 0x2020 #DAGGER +0x87 0x2021 #DOUBLE DAGGER +0x88 #UNDEFINED +0x89 0x2030 #PER MILLE SIGN +0x8A #UNDEFINED +0x8B 0x2039 #SINGLE LEFT-POINTING ANGLE QUOTATION MARK +0x8C #UNDEFINED +0x8D #UNDEFINED +0x8E #UNDEFINED +0x8F #UNDEFINED +0x90 #UNDEFINED +0x91 0x2018 #LEFT SINGLE QUOTATION MARK +0x92 0x2019 #RIGHT SINGLE QUOTATION MARK +0x93 0x201C #LEFT DOUBLE QUOTATION MARK +0x94 0x201D #RIGHT DOUBLE QUOTATION MARK +0x95 0x2022 #BULLET +0x96 0x2013 #EN DASH +0x97 0x2014 #EM DASH +0x98 #UNDEFINED +0x99 0x2122 #TRADE MARK SIGN +0x9A #UNDEFINED +0x9B 0x203A #SINGLE RIGHT-POINTING ANGLE QUOTATION MARK +0x9C #UNDEFINED +0x9D #UNDEFINED +0x9E #UNDEFINED +0x9F #UNDEFINED +0xA0 0x00A0 #NO-BREAK SPACE +0xA1 0x0385 #GREEK DIALYTIKA TONOS +0xA2 0x0386 #GREEK CAPITAL LETTER ALPHA WITH TONOS +0xA3 0x00A3 #POUND SIGN +0xA4 0x00A4 #CURRENCY SIGN +0xA5 0x00A5 #YEN SIGN +0xA6 0x00A6 #BROKEN BAR +0xA7 0x00A7 #SECTION SIGN +0xA8 0x00A8 #DIAERESIS +0xA9 0x00A9 #COPYRIGHT SIGN +0xAA #UNDEFINED +0xAB 0x00AB #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xAC 0x00AC #NOT SIGN +0xAD 0x00AD #SOFT HYPHEN +0xAE 0x00AE #REGISTERED SIGN +0xAF 0x2015 #HORIZONTAL BAR +0xB0 0x00B0 #DEGREE SIGN +0xB1 0x00B1 #PLUS-MINUS SIGN +0xB2 0x00B2 #SUPERSCRIPT TWO +0xB3 0x00B3 #SUPERSCRIPT THREE +0xB4 0x0384 #GREEK TONOS +0xB5 0x00B5 #MICRO SIGN +0xB6 0x00B6 #PILCROW SIGN +0xB7 0x00B7 #MIDDLE DOT +0xB8 0x0388 #GREEK CAPITAL LETTER EPSILON WITH TONOS +0xB9 0x0389 #GREEK CAPITAL LETTER ETA WITH TONOS +0xBA 0x038A #GREEK CAPITAL LETTER IOTA WITH TONOS +0xBB 0x00BB #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xBC 0x038C #GREEK CAPITAL LETTER OMICRON WITH TONOS +0xBD 0x00BD #VULGAR FRACTION ONE HALF +0xBE 0x038E #GREEK CAPITAL LETTER UPSILON WITH TONOS +0xBF 0x038F #GREEK CAPITAL LETTER OMEGA WITH TONOS +0xC0 0x0390 #GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS +0xC1 0x0391 #GREEK CAPITAL LETTER ALPHA +0xC2 0x0392 #GREEK CAPITAL LETTER BETA +0xC3 0x0393 #GREEK CAPITAL LETTER GAMMA +0xC4 0x0394 #GREEK CAPITAL LETTER DELTA +0xC5 0x0395 #GREEK CAPITAL LETTER EPSILON +0xC6 0x0396 #GREEK CAPITAL LETTER ZETA +0xC7 0x0397 #GREEK CAPITAL LETTER ETA +0xC8 0x0398 #GREEK CAPITAL LETTER THETA +0xC9 0x0399 #GREEK CAPITAL LETTER IOTA +0xCA 0x039A #GREEK CAPITAL LETTER KAPPA +0xCB 0x039B #GREEK CAPITAL LETTER LAMDA +0xCC 0x039C #GREEK CAPITAL LETTER MU +0xCD 0x039D #GREEK CAPITAL LETTER NU +0xCE 0x039E #GREEK CAPITAL LETTER XI +0xCF 0x039F #GREEK CAPITAL LETTER OMICRON +0xD0 0x03A0 #GREEK CAPITAL LETTER PI +0xD1 0x03A1 #GREEK CAPITAL LETTER RHO +0xD2 #UNDEFINED +0xD3 0x03A3 #GREEK CAPITAL LETTER SIGMA +0xD4 0x03A4 #GREEK CAPITAL LETTER TAU +0xD5 0x03A5 #GREEK CAPITAL LETTER UPSILON +0xD6 0x03A6 #GREEK CAPITAL LETTER PHI +0xD7 0x03A7 #GREEK CAPITAL LETTER CHI +0xD8 0x03A8 #GREEK CAPITAL LETTER PSI +0xD9 0x03A9 #GREEK CAPITAL LETTER OMEGA +0xDA 0x03AA #GREEK CAPITAL LETTER IOTA WITH DIALYTIKA +0xDB 0x03AB #GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA +0xDC 0x03AC #GREEK SMALL LETTER ALPHA WITH TONOS +0xDD 0x03AD #GREEK SMALL LETTER EPSILON WITH TONOS +0xDE 0x03AE #GREEK SMALL LETTER ETA WITH TONOS +0xDF 0x03AF #GREEK SMALL LETTER IOTA WITH TONOS +0xE0 0x03B0 #GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS +0xE1 0x03B1 #GREEK SMALL LETTER ALPHA +0xE2 0x03B2 #GREEK SMALL LETTER BETA +0xE3 0x03B3 #GREEK SMALL LETTER GAMMA +0xE4 0x03B4 #GREEK SMALL LETTER DELTA +0xE5 0x03B5 #GREEK SMALL LETTER EPSILON +0xE6 0x03B6 #GREEK SMALL LETTER ZETA +0xE7 0x03B7 #GREEK SMALL LETTER ETA +0xE8 0x03B8 #GREEK SMALL LETTER THETA +0xE9 0x03B9 #GREEK SMALL LETTER IOTA +0xEA 0x03BA #GREEK SMALL LETTER KAPPA +0xEB 0x03BB #GREEK SMALL LETTER LAMDA +0xEC 0x03BC #GREEK SMALL LETTER MU +0xED 0x03BD #GREEK SMALL LETTER NU +0xEE 0x03BE #GREEK SMALL LETTER XI +0xEF 0x03BF #GREEK SMALL LETTER OMICRON +0xF0 0x03C0 #GREEK SMALL LETTER PI +0xF1 0x03C1 #GREEK SMALL LETTER RHO +0xF2 0x03C2 #GREEK SMALL LETTER FINAL SIGMA +0xF3 0x03C3 #GREEK SMALL LETTER SIGMA +0xF4 0x03C4 #GREEK SMALL LETTER TAU +0xF5 0x03C5 #GREEK SMALL LETTER UPSILON +0xF6 0x03C6 #GREEK SMALL LETTER PHI +0xF7 0x03C7 #GREEK SMALL LETTER CHI +0xF8 0x03C8 #GREEK SMALL LETTER PSI +0xF9 0x03C9 #GREEK SMALL LETTER OMEGA +0xFA 0x03CA #GREEK SMALL LETTER IOTA WITH DIALYTIKA +0xFB 0x03CB #GREEK SMALL LETTER UPSILON WITH DIALYTIKA +0xFC 0x03CC #GREEK SMALL LETTER OMICRON WITH TONOS +0xFD 0x03CD #GREEK SMALL LETTER UPSILON WITH TONOS +0xFE 0x03CE #GREEK SMALL LETTER OMEGA WITH TONOS +0xFF #UNDEFINED diff --git a/codepage/cp1254.txt b/codepage/cp1254.txt new file mode 100644 index 0000000..987ed98 --- /dev/null +++ b/codepage/cp1254.txt @@ -0,0 +1,274 @@ +# +# Name: cp1254 to Unicode table +# Unicode version: 2.0 +# Table version: 2.01 +# Table format: Format A +# Date: 04/15/98 +# +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp1254 code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp1254 order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0A 0x000A #LINE FEED +0x0B 0x000B #VERTICAL TABULATION +0x0C 0x000C #FORM FEED +0x0D 0x000D #CARRIAGE RETURN +0x0E 0x000E #SHIFT OUT +0x0F 0x000F #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1A 0x001A #SUBSTITUTE +0x1B 0x001B #ESCAPE +0x1C 0x001C #FILE SEPARATOR +0x1D 0x001D #GROUP SEPARATOR +0x1E 0x001E #RECORD SEPARATOR +0x1F 0x001F #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2A 0x002A #ASTERISK +0x2B 0x002B #PLUS SIGN +0x2C 0x002C #COMMA +0x2D 0x002D #HYPHEN-MINUS +0x2E 0x002E #FULL STOP +0x2F 0x002F #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3A 0x003A #COLON +0x3B 0x003B #SEMICOLON +0x3C 0x003C #LESS-THAN SIGN +0x3D 0x003D #EQUALS SIGN +0x3E 0x003E #GREATER-THAN SIGN +0x3F 0x003F #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4A 0x004A #LATIN CAPITAL LETTER J +0x4B 0x004B #LATIN CAPITAL LETTER K +0x4C 0x004C #LATIN CAPITAL LETTER L +0x4D 0x004D #LATIN CAPITAL LETTER M +0x4E 0x004E #LATIN CAPITAL LETTER N +0x4F 0x004F #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5A 0x005A #LATIN CAPITAL LETTER Z +0x5B 0x005B #LEFT SQUARE BRACKET +0x5C 0x005C #REVERSE SOLIDUS +0x5D 0x005D #RIGHT SQUARE BRACKET +0x5E 0x005E #CIRCUMFLEX ACCENT +0x5F 0x005F #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6A 0x006A #LATIN SMALL LETTER J +0x6B 0x006B #LATIN SMALL LETTER K +0x6C 0x006C #LATIN SMALL LETTER L +0x6D 0x006D #LATIN SMALL LETTER M +0x6E 0x006E #LATIN SMALL LETTER N +0x6F 0x006F #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7A 0x007A #LATIN SMALL LETTER Z +0x7B 0x007B #LEFT CURLY BRACKET +0x7C 0x007C #VERTICAL LINE +0x7D 0x007D #RIGHT CURLY BRACKET +0x7E 0x007E #TILDE +0x7F 0x007F #DELETE +0x80 0x20AC #EURO SIGN +0x81 #UNDEFINED +0x82 0x201A #SINGLE LOW-9 QUOTATION MARK +0x83 0x0192 #LATIN SMALL LETTER F WITH HOOK +0x84 0x201E #DOUBLE LOW-9 QUOTATION MARK +0x85 0x2026 #HORIZONTAL ELLIPSIS +0x86 0x2020 #DAGGER +0x87 0x2021 #DOUBLE DAGGER +0x88 0x02C6 #MODIFIER LETTER CIRCUMFLEX ACCENT +0x89 0x2030 #PER MILLE SIGN +0x8A 0x0160 #LATIN CAPITAL LETTER S WITH CARON +0x8B 0x2039 #SINGLE LEFT-POINTING ANGLE QUOTATION MARK +0x8C 0x0152 #LATIN CAPITAL LIGATURE OE +0x8D #UNDEFINED +0x8E #UNDEFINED +0x8F #UNDEFINED +0x90 #UNDEFINED +0x91 0x2018 #LEFT SINGLE QUOTATION MARK +0x92 0x2019 #RIGHT SINGLE QUOTATION MARK +0x93 0x201C #LEFT DOUBLE QUOTATION MARK +0x94 0x201D #RIGHT DOUBLE QUOTATION MARK +0x95 0x2022 #BULLET +0x96 0x2013 #EN DASH +0x97 0x2014 #EM DASH +0x98 0x02DC #SMALL TILDE +0x99 0x2122 #TRADE MARK SIGN +0x9A 0x0161 #LATIN SMALL LETTER S WITH CARON +0x9B 0x203A #SINGLE RIGHT-POINTING ANGLE QUOTATION MARK +0x9C 0x0153 #LATIN SMALL LIGATURE OE +0x9D #UNDEFINED +0x9E #UNDEFINED +0x9F 0x0178 #LATIN CAPITAL LETTER Y WITH DIAERESIS +0xA0 0x00A0 #NO-BREAK SPACE +0xA1 0x00A1 #INVERTED EXCLAMATION MARK +0xA2 0x00A2 #CENT SIGN +0xA3 0x00A3 #POUND SIGN +0xA4 0x00A4 #CURRENCY SIGN +0xA5 0x00A5 #YEN SIGN +0xA6 0x00A6 #BROKEN BAR +0xA7 0x00A7 #SECTION SIGN +0xA8 0x00A8 #DIAERESIS +0xA9 0x00A9 #COPYRIGHT SIGN +0xAA 0x00AA #FEMININE ORDINAL INDICATOR +0xAB 0x00AB #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xAC 0x00AC #NOT SIGN +0xAD 0x00AD #SOFT HYPHEN +0xAE 0x00AE #REGISTERED SIGN +0xAF 0x00AF #MACRON +0xB0 0x00B0 #DEGREE SIGN +0xB1 0x00B1 #PLUS-MINUS SIGN +0xB2 0x00B2 #SUPERSCRIPT TWO +0xB3 0x00B3 #SUPERSCRIPT THREE +0xB4 0x00B4 #ACUTE ACCENT +0xB5 0x00B5 #MICRO SIGN +0xB6 0x00B6 #PILCROW SIGN +0xB7 0x00B7 #MIDDLE DOT +0xB8 0x00B8 #CEDILLA +0xB9 0x00B9 #SUPERSCRIPT ONE +0xBA 0x00BA #MASCULINE ORDINAL INDICATOR +0xBB 0x00BB #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xBC 0x00BC #VULGAR FRACTION ONE QUARTER +0xBD 0x00BD #VULGAR FRACTION ONE HALF +0xBE 0x00BE #VULGAR FRACTION THREE QUARTERS +0xBF 0x00BF #INVERTED QUESTION MARK +0xC0 0x00C0 #LATIN CAPITAL LETTER A WITH GRAVE +0xC1 0x00C1 #LATIN CAPITAL LETTER A WITH ACUTE +0xC2 0x00C2 #LATIN CAPITAL LETTER A WITH CIRCUMFLEX +0xC3 0x00C3 #LATIN CAPITAL LETTER A WITH TILDE +0xC4 0x00C4 #LATIN CAPITAL LETTER A WITH DIAERESIS +0xC5 0x00C5 #LATIN CAPITAL LETTER A WITH RING ABOVE +0xC6 0x00C6 #LATIN CAPITAL LETTER AE +0xC7 0x00C7 #LATIN CAPITAL LETTER C WITH CEDILLA +0xC8 0x00C8 #LATIN CAPITAL LETTER E WITH GRAVE +0xC9 0x00C9 #LATIN CAPITAL LETTER E WITH ACUTE +0xCA 0x00CA #LATIN CAPITAL LETTER E WITH CIRCUMFLEX +0xCB 0x00CB #LATIN CAPITAL LETTER E WITH DIAERESIS +0xCC 0x00CC #LATIN CAPITAL LETTER I WITH GRAVE +0xCD 0x00CD #LATIN CAPITAL LETTER I WITH ACUTE +0xCE 0x00CE #LATIN CAPITAL LETTER I WITH CIRCUMFLEX +0xCF 0x00CF #LATIN CAPITAL LETTER I WITH DIAERESIS +0xD0 0x011E #LATIN CAPITAL LETTER G WITH BREVE +0xD1 0x00D1 #LATIN CAPITAL LETTER N WITH TILDE +0xD2 0x00D2 #LATIN CAPITAL LETTER O WITH GRAVE +0xD3 0x00D3 #LATIN CAPITAL LETTER O WITH ACUTE +0xD4 0x00D4 #LATIN CAPITAL LETTER O WITH CIRCUMFLEX +0xD5 0x00D5 #LATIN CAPITAL LETTER O WITH TILDE +0xD6 0x00D6 #LATIN CAPITAL LETTER O WITH DIAERESIS +0xD7 0x00D7 #MULTIPLICATION SIGN +0xD8 0x00D8 #LATIN CAPITAL LETTER O WITH STROKE +0xD9 0x00D9 #LATIN CAPITAL LETTER U WITH GRAVE +0xDA 0x00DA #LATIN CAPITAL LETTER U WITH ACUTE +0xDB 0x00DB #LATIN CAPITAL LETTER U WITH CIRCUMFLEX +0xDC 0x00DC #LATIN CAPITAL LETTER U WITH DIAERESIS +0xDD 0x0130 #LATIN CAPITAL LETTER I WITH DOT ABOVE +0xDE 0x015E #LATIN CAPITAL LETTER S WITH CEDILLA +0xDF 0x00DF #LATIN SMALL LETTER SHARP S +0xE0 0x00E0 #LATIN SMALL LETTER A WITH GRAVE +0xE1 0x00E1 #LATIN SMALL LETTER A WITH ACUTE +0xE2 0x00E2 #LATIN SMALL LETTER A WITH CIRCUMFLEX +0xE3 0x00E3 #LATIN SMALL LETTER A WITH TILDE +0xE4 0x00E4 #LATIN SMALL LETTER A WITH DIAERESIS +0xE5 0x00E5 #LATIN SMALL LETTER A WITH RING ABOVE +0xE6 0x00E6 #LATIN SMALL LETTER AE +0xE7 0x00E7 #LATIN SMALL LETTER C WITH CEDILLA +0xE8 0x00E8 #LATIN SMALL LETTER E WITH GRAVE +0xE9 0x00E9 #LATIN SMALL LETTER E WITH ACUTE +0xEA 0x00EA #LATIN SMALL LETTER E WITH CIRCUMFLEX +0xEB 0x00EB #LATIN SMALL LETTER E WITH DIAERESIS +0xEC 0x00EC #LATIN SMALL LETTER I WITH GRAVE +0xED 0x00ED #LATIN SMALL LETTER I WITH ACUTE +0xEE 0x00EE #LATIN SMALL LETTER I WITH CIRCUMFLEX +0xEF 0x00EF #LATIN SMALL LETTER I WITH DIAERESIS +0xF0 0x011F #LATIN SMALL LETTER G WITH BREVE +0xF1 0x00F1 #LATIN SMALL LETTER N WITH TILDE +0xF2 0x00F2 #LATIN SMALL LETTER O WITH GRAVE +0xF3 0x00F3 #LATIN SMALL LETTER O WITH ACUTE +0xF4 0x00F4 #LATIN SMALL LETTER O WITH CIRCUMFLEX +0xF5 0x00F5 #LATIN SMALL LETTER O WITH TILDE +0xF6 0x00F6 #LATIN SMALL LETTER O WITH DIAERESIS +0xF7 0x00F7 #DIVISION SIGN +0xF8 0x00F8 #LATIN SMALL LETTER O WITH STROKE +0xF9 0x00F9 #LATIN SMALL LETTER U WITH GRAVE +0xFA 0x00FA #LATIN SMALL LETTER U WITH ACUTE +0xFB 0x00FB #LATIN SMALL LETTER U WITH CIRCUMFLEX +0xFC 0x00FC #LATIN SMALL LETTER U WITH DIAERESIS +0xFD 0x0131 #LATIN SMALL LETTER DOTLESS I +0xFE 0x015F #LATIN SMALL LETTER S WITH CEDILLA +0xFF 0x00FF #LATIN SMALL LETTER Y WITH DIAERESIS diff --git a/codepage/cp1255.txt b/codepage/cp1255.txt new file mode 100644 index 0000000..585f993 --- /dev/null +++ b/codepage/cp1255.txt @@ -0,0 +1,274 @@ +# +# Name: cp1255 to Unicode table +# Unicode version: 2.0 +# Table version: 2.01 +# Table format: Format A +# Date: 1/7/2000 +# +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp1255 code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp1255 order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0A 0x000A #LINE FEED +0x0B 0x000B #VERTICAL TABULATION +0x0C 0x000C #FORM FEED +0x0D 0x000D #CARRIAGE RETURN +0x0E 0x000E #SHIFT OUT +0x0F 0x000F #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1A 0x001A #SUBSTITUTE +0x1B 0x001B #ESCAPE +0x1C 0x001C #FILE SEPARATOR +0x1D 0x001D #GROUP SEPARATOR +0x1E 0x001E #RECORD SEPARATOR +0x1F 0x001F #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2A 0x002A #ASTERISK +0x2B 0x002B #PLUS SIGN +0x2C 0x002C #COMMA +0x2D 0x002D #HYPHEN-MINUS +0x2E 0x002E #FULL STOP +0x2F 0x002F #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3A 0x003A #COLON +0x3B 0x003B #SEMICOLON +0x3C 0x003C #LESS-THAN SIGN +0x3D 0x003D #EQUALS SIGN +0x3E 0x003E #GREATER-THAN SIGN +0x3F 0x003F #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4A 0x004A #LATIN CAPITAL LETTER J +0x4B 0x004B #LATIN CAPITAL LETTER K +0x4C 0x004C #LATIN CAPITAL LETTER L +0x4D 0x004D #LATIN CAPITAL LETTER M +0x4E 0x004E #LATIN CAPITAL LETTER N +0x4F 0x004F #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5A 0x005A #LATIN CAPITAL LETTER Z +0x5B 0x005B #LEFT SQUARE BRACKET +0x5C 0x005C #REVERSE SOLIDUS +0x5D 0x005D #RIGHT SQUARE BRACKET +0x5E 0x005E #CIRCUMFLEX ACCENT +0x5F 0x005F #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6A 0x006A #LATIN SMALL LETTER J +0x6B 0x006B #LATIN SMALL LETTER K +0x6C 0x006C #LATIN SMALL LETTER L +0x6D 0x006D #LATIN SMALL LETTER M +0x6E 0x006E #LATIN SMALL LETTER N +0x6F 0x006F #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7A 0x007A #LATIN SMALL LETTER Z +0x7B 0x007B #LEFT CURLY BRACKET +0x7C 0x007C #VERTICAL LINE +0x7D 0x007D #RIGHT CURLY BRACKET +0x7E 0x007E #TILDE +0x7F 0x007F #DELETE +0x80 0x20AC #EURO SIGN +0x81 #UNDEFINED +0x82 0x201A #SINGLE LOW-9 QUOTATION MARK +0x83 0x0192 #LATIN SMALL LETTER F WITH HOOK +0x84 0x201E #DOUBLE LOW-9 QUOTATION MARK +0x85 0x2026 #HORIZONTAL ELLIPSIS +0x86 0x2020 #DAGGER +0x87 0x2021 #DOUBLE DAGGER +0x88 0x02C6 #MODIFIER LETTER CIRCUMFLEX ACCENT +0x89 0x2030 #PER MILLE SIGN +0x8A #UNDEFINED +0x8B 0x2039 #SINGLE LEFT-POINTING ANGLE QUOTATION MARK +0x8C #UNDEFINED +0x8D #UNDEFINED +0x8E #UNDEFINED +0x8F #UNDEFINED +0x90 #UNDEFINED +0x91 0x2018 #LEFT SINGLE QUOTATION MARK +0x92 0x2019 #RIGHT SINGLE QUOTATION MARK +0x93 0x201C #LEFT DOUBLE QUOTATION MARK +0x94 0x201D #RIGHT DOUBLE QUOTATION MARK +0x95 0x2022 #BULLET +0x96 0x2013 #EN DASH +0x97 0x2014 #EM DASH +0x98 0x02DC #SMALL TILDE +0x99 0x2122 #TRADE MARK SIGN +0x9A #UNDEFINED +0x9B 0x203A #SINGLE RIGHT-POINTING ANGLE QUOTATION MARK +0x9C #UNDEFINED +0x9D #UNDEFINED +0x9E #UNDEFINED +0x9F #UNDEFINED +0xA0 0x00A0 #NO-BREAK SPACE +0xA1 0x00A1 #INVERTED EXCLAMATION MARK +0xA2 0x00A2 #CENT SIGN +0xA3 0x00A3 #POUND SIGN +0xA4 0x20AA #NEW SHEQEL SIGN +0xA5 0x00A5 #YEN SIGN +0xA6 0x00A6 #BROKEN BAR +0xA7 0x00A7 #SECTION SIGN +0xA8 0x00A8 #DIAERESIS +0xA9 0x00A9 #COPYRIGHT SIGN +0xAA 0x00D7 #MULTIPLICATION SIGN +0xAB 0x00AB #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xAC 0x00AC #NOT SIGN +0xAD 0x00AD #SOFT HYPHEN +0xAE 0x00AE #REGISTERED SIGN +0xAF 0x00AF #MACRON +0xB0 0x00B0 #DEGREE SIGN +0xB1 0x00B1 #PLUS-MINUS SIGN +0xB2 0x00B2 #SUPERSCRIPT TWO +0xB3 0x00B3 #SUPERSCRIPT THREE +0xB4 0x00B4 #ACUTE ACCENT +0xB5 0x00B5 #MICRO SIGN +0xB6 0x00B6 #PILCROW SIGN +0xB7 0x00B7 #MIDDLE DOT +0xB8 0x00B8 #CEDILLA +0xB9 0x00B9 #SUPERSCRIPT ONE +0xBA 0x00F7 #DIVISION SIGN +0xBB 0x00BB #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xBC 0x00BC #VULGAR FRACTION ONE QUARTER +0xBD 0x00BD #VULGAR FRACTION ONE HALF +0xBE 0x00BE #VULGAR FRACTION THREE QUARTERS +0xBF 0x00BF #INVERTED QUESTION MARK +0xC0 0x05B0 #HEBREW POINT SHEVA +0xC1 0x05B1 #HEBREW POINT HATAF SEGOL +0xC2 0x05B2 #HEBREW POINT HATAF PATAH +0xC3 0x05B3 #HEBREW POINT HATAF QAMATS +0xC4 0x05B4 #HEBREW POINT HIRIQ +0xC5 0x05B5 #HEBREW POINT TSERE +0xC6 0x05B6 #HEBREW POINT SEGOL +0xC7 0x05B7 #HEBREW POINT PATAH +0xC8 0x05B8 #HEBREW POINT QAMATS +0xC9 0x05B9 #HEBREW POINT HOLAM +0xCA #UNDEFINED +0xCB 0x05BB #HEBREW POINT QUBUTS +0xCC 0x05BC #HEBREW POINT DAGESH OR MAPIQ +0xCD 0x05BD #HEBREW POINT METEG +0xCE 0x05BE #HEBREW PUNCTUATION MAQAF +0xCF 0x05BF #HEBREW POINT RAFE +0xD0 0x05C0 #HEBREW PUNCTUATION PASEQ +0xD1 0x05C1 #HEBREW POINT SHIN DOT +0xD2 0x05C2 #HEBREW POINT SIN DOT +0xD3 0x05C3 #HEBREW PUNCTUATION SOF PASUQ +0xD4 0x05F0 #HEBREW LIGATURE YIDDISH DOUBLE VAV +0xD5 0x05F1 #HEBREW LIGATURE YIDDISH VAV YOD +0xD6 0x05F2 #HEBREW LIGATURE YIDDISH DOUBLE YOD +0xD7 0x05F3 #HEBREW PUNCTUATION GERESH +0xD8 0x05F4 #HEBREW PUNCTUATION GERSHAYIM +0xD9 #UNDEFINED +0xDA #UNDEFINED +0xDB #UNDEFINED +0xDC #UNDEFINED +0xDD #UNDEFINED +0xDE #UNDEFINED +0xDF #UNDEFINED +0xE0 0x05D0 #HEBREW LETTER ALEF +0xE1 0x05D1 #HEBREW LETTER BET +0xE2 0x05D2 #HEBREW LETTER GIMEL +0xE3 0x05D3 #HEBREW LETTER DALET +0xE4 0x05D4 #HEBREW LETTER HE +0xE5 0x05D5 #HEBREW LETTER VAV +0xE6 0x05D6 #HEBREW LETTER ZAYIN +0xE7 0x05D7 #HEBREW LETTER HET +0xE8 0x05D8 #HEBREW LETTER TET +0xE9 0x05D9 #HEBREW LETTER YOD +0xEA 0x05DA #HEBREW LETTER FINAL KAF +0xEB 0x05DB #HEBREW LETTER KAF +0xEC 0x05DC #HEBREW LETTER LAMED +0xED 0x05DD #HEBREW LETTER FINAL MEM +0xEE 0x05DE #HEBREW LETTER MEM +0xEF 0x05DF #HEBREW LETTER FINAL NUN +0xF0 0x05E0 #HEBREW LETTER NUN +0xF1 0x05E1 #HEBREW LETTER SAMEKH +0xF2 0x05E2 #HEBREW LETTER AYIN +0xF3 0x05E3 #HEBREW LETTER FINAL PE +0xF4 0x05E4 #HEBREW LETTER PE +0xF5 0x05E5 #HEBREW LETTER FINAL TSADI +0xF6 0x05E6 #HEBREW LETTER TSADI +0xF7 0x05E7 #HEBREW LETTER QOF +0xF8 0x05E8 #HEBREW LETTER RESH +0xF9 0x05E9 #HEBREW LETTER SHIN +0xFA 0x05EA #HEBREW LETTER TAV +0xFB #UNDEFINED +0xFC #UNDEFINED +0xFD 0x200E #LEFT-TO-RIGHT MARK +0xFE 0x200F #RIGHT-TO-LEFT MARK +0xFF #UNDEFINED diff --git a/codepage/cp1256.txt b/codepage/cp1256.txt new file mode 100644 index 0000000..244dcce --- /dev/null +++ b/codepage/cp1256.txt @@ -0,0 +1,274 @@ +# +# Name: cp1256 to Unicode table +# Unicode version: 2.1 +# Table version: 2.01 +# Table format: Format A +# Date: 01/5/99 +# +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp1256 code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp1256 order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0A 0x000A #LINE FEED +0x0B 0x000B #VERTICAL TABULATION +0x0C 0x000C #FORM FEED +0x0D 0x000D #CARRIAGE RETURN +0x0E 0x000E #SHIFT OUT +0x0F 0x000F #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1A 0x001A #SUBSTITUTE +0x1B 0x001B #ESCAPE +0x1C 0x001C #FILE SEPARATOR +0x1D 0x001D #GROUP SEPARATOR +0x1E 0x001E #RECORD SEPARATOR +0x1F 0x001F #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2A 0x002A #ASTERISK +0x2B 0x002B #PLUS SIGN +0x2C 0x002C #COMMA +0x2D 0x002D #HYPHEN-MINUS +0x2E 0x002E #FULL STOP +0x2F 0x002F #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3A 0x003A #COLON +0x3B 0x003B #SEMICOLON +0x3C 0x003C #LESS-THAN SIGN +0x3D 0x003D #EQUALS SIGN +0x3E 0x003E #GREATER-THAN SIGN +0x3F 0x003F #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4A 0x004A #LATIN CAPITAL LETTER J +0x4B 0x004B #LATIN CAPITAL LETTER K +0x4C 0x004C #LATIN CAPITAL LETTER L +0x4D 0x004D #LATIN CAPITAL LETTER M +0x4E 0x004E #LATIN CAPITAL LETTER N +0x4F 0x004F #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5A 0x005A #LATIN CAPITAL LETTER Z +0x5B 0x005B #LEFT SQUARE BRACKET +0x5C 0x005C #REVERSE SOLIDUS +0x5D 0x005D #RIGHT SQUARE BRACKET +0x5E 0x005E #CIRCUMFLEX ACCENT +0x5F 0x005F #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6A 0x006A #LATIN SMALL LETTER J +0x6B 0x006B #LATIN SMALL LETTER K +0x6C 0x006C #LATIN SMALL LETTER L +0x6D 0x006D #LATIN SMALL LETTER M +0x6E 0x006E #LATIN SMALL LETTER N +0x6F 0x006F #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7A 0x007A #LATIN SMALL LETTER Z +0x7B 0x007B #LEFT CURLY BRACKET +0x7C 0x007C #VERTICAL LINE +0x7D 0x007D #RIGHT CURLY BRACKET +0x7E 0x007E #TILDE +0x7F 0x007F #DELETE +0x80 0x20AC #EURO SIGN +0x81 0x067E #ARABIC LETTER PEH +0x82 0x201A #SINGLE LOW-9 QUOTATION MARK +0x83 0x0192 #LATIN SMALL LETTER F WITH HOOK +0x84 0x201E #DOUBLE LOW-9 QUOTATION MARK +0x85 0x2026 #HORIZONTAL ELLIPSIS +0x86 0x2020 #DAGGER +0x87 0x2021 #DOUBLE DAGGER +0x88 0x02C6 #MODIFIER LETTER CIRCUMFLEX ACCENT +0x89 0x2030 #PER MILLE SIGN +0x8A 0x0679 #ARABIC LETTER TTEH +0x8B 0x2039 #SINGLE LEFT-POINTING ANGLE QUOTATION MARK +0x8C 0x0152 #LATIN CAPITAL LIGATURE OE +0x8D 0x0686 #ARABIC LETTER TCHEH +0x8E 0x0698 #ARABIC LETTER JEH +0x8F 0x0688 #ARABIC LETTER DDAL +0x90 0x06AF #ARABIC LETTER GAF +0x91 0x2018 #LEFT SINGLE QUOTATION MARK +0x92 0x2019 #RIGHT SINGLE QUOTATION MARK +0x93 0x201C #LEFT DOUBLE QUOTATION MARK +0x94 0x201D #RIGHT DOUBLE QUOTATION MARK +0x95 0x2022 #BULLET +0x96 0x2013 #EN DASH +0x97 0x2014 #EM DASH +0x98 0x06A9 #ARABIC LETTER KEHEH +0x99 0x2122 #TRADE MARK SIGN +0x9A 0x0691 #ARABIC LETTER RREH +0x9B 0x203A #SINGLE RIGHT-POINTING ANGLE QUOTATION MARK +0x9C 0x0153 #LATIN SMALL LIGATURE OE +0x9D 0x200C #ZERO WIDTH NON-JOINER +0x9E 0x200D #ZERO WIDTH JOINER +0x9F 0x06BA #ARABIC LETTER NOON GHUNNA +0xA0 0x00A0 #NO-BREAK SPACE +0xA1 0x060C #ARABIC COMMA +0xA2 0x00A2 #CENT SIGN +0xA3 0x00A3 #POUND SIGN +0xA4 0x00A4 #CURRENCY SIGN +0xA5 0x00A5 #YEN SIGN +0xA6 0x00A6 #BROKEN BAR +0xA7 0x00A7 #SECTION SIGN +0xA8 0x00A8 #DIAERESIS +0xA9 0x00A9 #COPYRIGHT SIGN +0xAA 0x06BE #ARABIC LETTER HEH DOACHASHMEE +0xAB 0x00AB #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xAC 0x00AC #NOT SIGN +0xAD 0x00AD #SOFT HYPHEN +0xAE 0x00AE #REGISTERED SIGN +0xAF 0x00AF #MACRON +0xB0 0x00B0 #DEGREE SIGN +0xB1 0x00B1 #PLUS-MINUS SIGN +0xB2 0x00B2 #SUPERSCRIPT TWO +0xB3 0x00B3 #SUPERSCRIPT THREE +0xB4 0x00B4 #ACUTE ACCENT +0xB5 0x00B5 #MICRO SIGN +0xB6 0x00B6 #PILCROW SIGN +0xB7 0x00B7 #MIDDLE DOT +0xB8 0x00B8 #CEDILLA +0xB9 0x00B9 #SUPERSCRIPT ONE +0xBA 0x061B #ARABIC SEMICOLON +0xBB 0x00BB #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xBC 0x00BC #VULGAR FRACTION ONE QUARTER +0xBD 0x00BD #VULGAR FRACTION ONE HALF +0xBE 0x00BE #VULGAR FRACTION THREE QUARTERS +0xBF 0x061F #ARABIC QUESTION MARK +0xC0 0x06C1 #ARABIC LETTER HEH GOAL +0xC1 0x0621 #ARABIC LETTER HAMZA +0xC2 0x0622 #ARABIC LETTER ALEF WITH MADDA ABOVE +0xC3 0x0623 #ARABIC LETTER ALEF WITH HAMZA ABOVE +0xC4 0x0624 #ARABIC LETTER WAW WITH HAMZA ABOVE +0xC5 0x0625 #ARABIC LETTER ALEF WITH HAMZA BELOW +0xC6 0x0626 #ARABIC LETTER YEH WITH HAMZA ABOVE +0xC7 0x0627 #ARABIC LETTER ALEF +0xC8 0x0628 #ARABIC LETTER BEH +0xC9 0x0629 #ARABIC LETTER TEH MARBUTA +0xCA 0x062A #ARABIC LETTER TEH +0xCB 0x062B #ARABIC LETTER THEH +0xCC 0x062C #ARABIC LETTER JEEM +0xCD 0x062D #ARABIC LETTER HAH +0xCE 0x062E #ARABIC LETTER KHAH +0xCF 0x062F #ARABIC LETTER DAL +0xD0 0x0630 #ARABIC LETTER THAL +0xD1 0x0631 #ARABIC LETTER REH +0xD2 0x0632 #ARABIC LETTER ZAIN +0xD3 0x0633 #ARABIC LETTER SEEN +0xD4 0x0634 #ARABIC LETTER SHEEN +0xD5 0x0635 #ARABIC LETTER SAD +0xD6 0x0636 #ARABIC LETTER DAD +0xD7 0x00D7 #MULTIPLICATION SIGN +0xD8 0x0637 #ARABIC LETTER TAH +0xD9 0x0638 #ARABIC LETTER ZAH +0xDA 0x0639 #ARABIC LETTER AIN +0xDB 0x063A #ARABIC LETTER GHAIN +0xDC 0x0640 #ARABIC TATWEEL +0xDD 0x0641 #ARABIC LETTER FEH +0xDE 0x0642 #ARABIC LETTER QAF +0xDF 0x0643 #ARABIC LETTER KAF +0xE0 0x00E0 #LATIN SMALL LETTER A WITH GRAVE +0xE1 0x0644 #ARABIC LETTER LAM +0xE2 0x00E2 #LATIN SMALL LETTER A WITH CIRCUMFLEX +0xE3 0x0645 #ARABIC LETTER MEEM +0xE4 0x0646 #ARABIC LETTER NOON +0xE5 0x0647 #ARABIC LETTER HEH +0xE6 0x0648 #ARABIC LETTER WAW +0xE7 0x00E7 #LATIN SMALL LETTER C WITH CEDILLA +0xE8 0x00E8 #LATIN SMALL LETTER E WITH GRAVE +0xE9 0x00E9 #LATIN SMALL LETTER E WITH ACUTE +0xEA 0x00EA #LATIN SMALL LETTER E WITH CIRCUMFLEX +0xEB 0x00EB #LATIN SMALL LETTER E WITH DIAERESIS +0xEC 0x0649 #ARABIC LETTER ALEF MAKSURA +0xED 0x064A #ARABIC LETTER YEH +0xEE 0x00EE #LATIN SMALL LETTER I WITH CIRCUMFLEX +0xEF 0x00EF #LATIN SMALL LETTER I WITH DIAERESIS +0xF0 0x064B #ARABIC FATHATAN +0xF1 0x064C #ARABIC DAMMATAN +0xF2 0x064D #ARABIC KASRATAN +0xF3 0x064E #ARABIC FATHA +0xF4 0x00F4 #LATIN SMALL LETTER O WITH CIRCUMFLEX +0xF5 0x064F #ARABIC DAMMA +0xF6 0x0650 #ARABIC KASRA +0xF7 0x00F7 #DIVISION SIGN +0xF8 0x0651 #ARABIC SHADDA +0xF9 0x00F9 #LATIN SMALL LETTER U WITH GRAVE +0xFA 0x0652 #ARABIC SUKUN +0xFB 0x00FB #LATIN SMALL LETTER U WITH CIRCUMFLEX +0xFC 0x00FC #LATIN SMALL LETTER U WITH DIAERESIS +0xFD 0x200E #LEFT-TO-RIGHT MARK +0xFE 0x200F #RIGHT-TO-LEFT MARK +0xFF 0x06D2 #ARABIC LETTER YEH BARREE diff --git a/codepage/cp1257.txt b/codepage/cp1257.txt new file mode 100644 index 0000000..0dc475e --- /dev/null +++ b/codepage/cp1257.txt @@ -0,0 +1,274 @@ +# +# Name: cp1257 to Unicode table +# Unicode version: 2.0 +# Table version: 2.01 +# Table format: Format A +# Date: 04/15/98 +# +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp1257 code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp1257 order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0A 0x000A #LINE FEED +0x0B 0x000B #VERTICAL TABULATION +0x0C 0x000C #FORM FEED +0x0D 0x000D #CARRIAGE RETURN +0x0E 0x000E #SHIFT OUT +0x0F 0x000F #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1A 0x001A #SUBSTITUTE +0x1B 0x001B #ESCAPE +0x1C 0x001C #FILE SEPARATOR +0x1D 0x001D #GROUP SEPARATOR +0x1E 0x001E #RECORD SEPARATOR +0x1F 0x001F #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2A 0x002A #ASTERISK +0x2B 0x002B #PLUS SIGN +0x2C 0x002C #COMMA +0x2D 0x002D #HYPHEN-MINUS +0x2E 0x002E #FULL STOP +0x2F 0x002F #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3A 0x003A #COLON +0x3B 0x003B #SEMICOLON +0x3C 0x003C #LESS-THAN SIGN +0x3D 0x003D #EQUALS SIGN +0x3E 0x003E #GREATER-THAN SIGN +0x3F 0x003F #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4A 0x004A #LATIN CAPITAL LETTER J +0x4B 0x004B #LATIN CAPITAL LETTER K +0x4C 0x004C #LATIN CAPITAL LETTER L +0x4D 0x004D #LATIN CAPITAL LETTER M +0x4E 0x004E #LATIN CAPITAL LETTER N +0x4F 0x004F #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5A 0x005A #LATIN CAPITAL LETTER Z +0x5B 0x005B #LEFT SQUARE BRACKET +0x5C 0x005C #REVERSE SOLIDUS +0x5D 0x005D #RIGHT SQUARE BRACKET +0x5E 0x005E #CIRCUMFLEX ACCENT +0x5F 0x005F #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6A 0x006A #LATIN SMALL LETTER J +0x6B 0x006B #LATIN SMALL LETTER K +0x6C 0x006C #LATIN SMALL LETTER L +0x6D 0x006D #LATIN SMALL LETTER M +0x6E 0x006E #LATIN SMALL LETTER N +0x6F 0x006F #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7A 0x007A #LATIN SMALL LETTER Z +0x7B 0x007B #LEFT CURLY BRACKET +0x7C 0x007C #VERTICAL LINE +0x7D 0x007D #RIGHT CURLY BRACKET +0x7E 0x007E #TILDE +0x7F 0x007F #DELETE +0x80 0x20AC #EURO SIGN +0x81 #UNDEFINED +0x82 0x201A #SINGLE LOW-9 QUOTATION MARK +0x83 #UNDEFINED +0x84 0x201E #DOUBLE LOW-9 QUOTATION MARK +0x85 0x2026 #HORIZONTAL ELLIPSIS +0x86 0x2020 #DAGGER +0x87 0x2021 #DOUBLE DAGGER +0x88 #UNDEFINED +0x89 0x2030 #PER MILLE SIGN +0x8A #UNDEFINED +0x8B 0x2039 #SINGLE LEFT-POINTING ANGLE QUOTATION MARK +0x8C #UNDEFINED +0x8D 0x00A8 #DIAERESIS +0x8E 0x02C7 #CARON +0x8F 0x00B8 #CEDILLA +0x90 #UNDEFINED +0x91 0x2018 #LEFT SINGLE QUOTATION MARK +0x92 0x2019 #RIGHT SINGLE QUOTATION MARK +0x93 0x201C #LEFT DOUBLE QUOTATION MARK +0x94 0x201D #RIGHT DOUBLE QUOTATION MARK +0x95 0x2022 #BULLET +0x96 0x2013 #EN DASH +0x97 0x2014 #EM DASH +0x98 #UNDEFINED +0x99 0x2122 #TRADE MARK SIGN +0x9A #UNDEFINED +0x9B 0x203A #SINGLE RIGHT-POINTING ANGLE QUOTATION MARK +0x9C #UNDEFINED +0x9D 0x00AF #MACRON +0x9E 0x02DB #OGONEK +0x9F #UNDEFINED +0xA0 0x00A0 #NO-BREAK SPACE +0xA1 #UNDEFINED +0xA2 0x00A2 #CENT SIGN +0xA3 0x00A3 #POUND SIGN +0xA4 0x00A4 #CURRENCY SIGN +0xA5 #UNDEFINED +0xA6 0x00A6 #BROKEN BAR +0xA7 0x00A7 #SECTION SIGN +0xA8 0x00D8 #LATIN CAPITAL LETTER O WITH STROKE +0xA9 0x00A9 #COPYRIGHT SIGN +0xAA 0x0156 #LATIN CAPITAL LETTER R WITH CEDILLA +0xAB 0x00AB #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xAC 0x00AC #NOT SIGN +0xAD 0x00AD #SOFT HYPHEN +0xAE 0x00AE #REGISTERED SIGN +0xAF 0x00C6 #LATIN CAPITAL LETTER AE +0xB0 0x00B0 #DEGREE SIGN +0xB1 0x00B1 #PLUS-MINUS SIGN +0xB2 0x00B2 #SUPERSCRIPT TWO +0xB3 0x00B3 #SUPERSCRIPT THREE +0xB4 0x00B4 #ACUTE ACCENT +0xB5 0x00B5 #MICRO SIGN +0xB6 0x00B6 #PILCROW SIGN +0xB7 0x00B7 #MIDDLE DOT +0xB8 0x00F8 #LATIN SMALL LETTER O WITH STROKE +0xB9 0x00B9 #SUPERSCRIPT ONE +0xBA 0x0157 #LATIN SMALL LETTER R WITH CEDILLA +0xBB 0x00BB #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xBC 0x00BC #VULGAR FRACTION ONE QUARTER +0xBD 0x00BD #VULGAR FRACTION ONE HALF +0xBE 0x00BE #VULGAR FRACTION THREE QUARTERS +0xBF 0x00E6 #LATIN SMALL LETTER AE +0xC0 0x0104 #LATIN CAPITAL LETTER A WITH OGONEK +0xC1 0x012E #LATIN CAPITAL LETTER I WITH OGONEK +0xC2 0x0100 #LATIN CAPITAL LETTER A WITH MACRON +0xC3 0x0106 #LATIN CAPITAL LETTER C WITH ACUTE +0xC4 0x00C4 #LATIN CAPITAL LETTER A WITH DIAERESIS +0xC5 0x00C5 #LATIN CAPITAL LETTER A WITH RING ABOVE +0xC6 0x0118 #LATIN CAPITAL LETTER E WITH OGONEK +0xC7 0x0112 #LATIN CAPITAL LETTER E WITH MACRON +0xC8 0x010C #LATIN CAPITAL LETTER C WITH CARON +0xC9 0x00C9 #LATIN CAPITAL LETTER E WITH ACUTE +0xCA 0x0179 #LATIN CAPITAL LETTER Z WITH ACUTE +0xCB 0x0116 #LATIN CAPITAL LETTER E WITH DOT ABOVE +0xCC 0x0122 #LATIN CAPITAL LETTER G WITH CEDILLA +0xCD 0x0136 #LATIN CAPITAL LETTER K WITH CEDILLA +0xCE 0x012A #LATIN CAPITAL LETTER I WITH MACRON +0xCF 0x013B #LATIN CAPITAL LETTER L WITH CEDILLA +0xD0 0x0160 #LATIN CAPITAL LETTER S WITH CARON +0xD1 0x0143 #LATIN CAPITAL LETTER N WITH ACUTE +0xD2 0x0145 #LATIN CAPITAL LETTER N WITH CEDILLA +0xD3 0x00D3 #LATIN CAPITAL LETTER O WITH ACUTE +0xD4 0x014C #LATIN CAPITAL LETTER O WITH MACRON +0xD5 0x00D5 #LATIN CAPITAL LETTER O WITH TILDE +0xD6 0x00D6 #LATIN CAPITAL LETTER O WITH DIAERESIS +0xD7 0x00D7 #MULTIPLICATION SIGN +0xD8 0x0172 #LATIN CAPITAL LETTER U WITH OGONEK +0xD9 0x0141 #LATIN CAPITAL LETTER L WITH STROKE +0xDA 0x015A #LATIN CAPITAL LETTER S WITH ACUTE +0xDB 0x016A #LATIN CAPITAL LETTER U WITH MACRON +0xDC 0x00DC #LATIN CAPITAL LETTER U WITH DIAERESIS +0xDD 0x017B #LATIN CAPITAL LETTER Z WITH DOT ABOVE +0xDE 0x017D #LATIN CAPITAL LETTER Z WITH CARON +0xDF 0x00DF #LATIN SMALL LETTER SHARP S +0xE0 0x0105 #LATIN SMALL LETTER A WITH OGONEK +0xE1 0x012F #LATIN SMALL LETTER I WITH OGONEK +0xE2 0x0101 #LATIN SMALL LETTER A WITH MACRON +0xE3 0x0107 #LATIN SMALL LETTER C WITH ACUTE +0xE4 0x00E4 #LATIN SMALL LETTER A WITH DIAERESIS +0xE5 0x00E5 #LATIN SMALL LETTER A WITH RING ABOVE +0xE6 0x0119 #LATIN SMALL LETTER E WITH OGONEK +0xE7 0x0113 #LATIN SMALL LETTER E WITH MACRON +0xE8 0x010D #LATIN SMALL LETTER C WITH CARON +0xE9 0x00E9 #LATIN SMALL LETTER E WITH ACUTE +0xEA 0x017A #LATIN SMALL LETTER Z WITH ACUTE +0xEB 0x0117 #LATIN SMALL LETTER E WITH DOT ABOVE +0xEC 0x0123 #LATIN SMALL LETTER G WITH CEDILLA +0xED 0x0137 #LATIN SMALL LETTER K WITH CEDILLA +0xEE 0x012B #LATIN SMALL LETTER I WITH MACRON +0xEF 0x013C #LATIN SMALL LETTER L WITH CEDILLA +0xF0 0x0161 #LATIN SMALL LETTER S WITH CARON +0xF1 0x0144 #LATIN SMALL LETTER N WITH ACUTE +0xF2 0x0146 #LATIN SMALL LETTER N WITH CEDILLA +0xF3 0x00F3 #LATIN SMALL LETTER O WITH ACUTE +0xF4 0x014D #LATIN SMALL LETTER O WITH MACRON +0xF5 0x00F5 #LATIN SMALL LETTER O WITH TILDE +0xF6 0x00F6 #LATIN SMALL LETTER O WITH DIAERESIS +0xF7 0x00F7 #DIVISION SIGN +0xF8 0x0173 #LATIN SMALL LETTER U WITH OGONEK +0xF9 0x0142 #LATIN SMALL LETTER L WITH STROKE +0xFA 0x015B #LATIN SMALL LETTER S WITH ACUTE +0xFB 0x016B #LATIN SMALL LETTER U WITH MACRON +0xFC 0x00FC #LATIN SMALL LETTER U WITH DIAERESIS +0xFD 0x017C #LATIN SMALL LETTER Z WITH DOT ABOVE +0xFE 0x017E #LATIN SMALL LETTER Z WITH CARON +0xFF 0x02D9 #DOT ABOVE diff --git a/codepage/cp1258.txt b/codepage/cp1258.txt new file mode 100644 index 0000000..f402b34 --- /dev/null +++ b/codepage/cp1258.txt @@ -0,0 +1,274 @@ +# +# Name: cp1258 to Unicode table +# Unicode version: 2.0 +# Table version: 2.01 +# Table format: Format A +# Date: 04/15/98 +# +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp1258 code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp1258 order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0A 0x000A #LINE FEED +0x0B 0x000B #VERTICAL TABULATION +0x0C 0x000C #FORM FEED +0x0D 0x000D #CARRIAGE RETURN +0x0E 0x000E #SHIFT OUT +0x0F 0x000F #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1A 0x001A #SUBSTITUTE +0x1B 0x001B #ESCAPE +0x1C 0x001C #FILE SEPARATOR +0x1D 0x001D #GROUP SEPARATOR +0x1E 0x001E #RECORD SEPARATOR +0x1F 0x001F #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2A 0x002A #ASTERISK +0x2B 0x002B #PLUS SIGN +0x2C 0x002C #COMMA +0x2D 0x002D #HYPHEN-MINUS +0x2E 0x002E #FULL STOP +0x2F 0x002F #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3A 0x003A #COLON +0x3B 0x003B #SEMICOLON +0x3C 0x003C #LESS-THAN SIGN +0x3D 0x003D #EQUALS SIGN +0x3E 0x003E #GREATER-THAN SIGN +0x3F 0x003F #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4A 0x004A #LATIN CAPITAL LETTER J +0x4B 0x004B #LATIN CAPITAL LETTER K +0x4C 0x004C #LATIN CAPITAL LETTER L +0x4D 0x004D #LATIN CAPITAL LETTER M +0x4E 0x004E #LATIN CAPITAL LETTER N +0x4F 0x004F #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5A 0x005A #LATIN CAPITAL LETTER Z +0x5B 0x005B #LEFT SQUARE BRACKET +0x5C 0x005C #REVERSE SOLIDUS +0x5D 0x005D #RIGHT SQUARE BRACKET +0x5E 0x005E #CIRCUMFLEX ACCENT +0x5F 0x005F #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6A 0x006A #LATIN SMALL LETTER J +0x6B 0x006B #LATIN SMALL LETTER K +0x6C 0x006C #LATIN SMALL LETTER L +0x6D 0x006D #LATIN SMALL LETTER M +0x6E 0x006E #LATIN SMALL LETTER N +0x6F 0x006F #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7A 0x007A #LATIN SMALL LETTER Z +0x7B 0x007B #LEFT CURLY BRACKET +0x7C 0x007C #VERTICAL LINE +0x7D 0x007D #RIGHT CURLY BRACKET +0x7E 0x007E #TILDE +0x7F 0x007F #DELETE +0x80 0x20AC #EURO SIGN +0x81 #UNDEFINED +0x82 0x201A #SINGLE LOW-9 QUOTATION MARK +0x83 0x0192 #LATIN SMALL LETTER F WITH HOOK +0x84 0x201E #DOUBLE LOW-9 QUOTATION MARK +0x85 0x2026 #HORIZONTAL ELLIPSIS +0x86 0x2020 #DAGGER +0x87 0x2021 #DOUBLE DAGGER +0x88 0x02C6 #MODIFIER LETTER CIRCUMFLEX ACCENT +0x89 0x2030 #PER MILLE SIGN +0x8A #UNDEFINED +0x8B 0x2039 #SINGLE LEFT-POINTING ANGLE QUOTATION MARK +0x8C 0x0152 #LATIN CAPITAL LIGATURE OE +0x8D #UNDEFINED +0x8E #UNDEFINED +0x8F #UNDEFINED +0x90 #UNDEFINED +0x91 0x2018 #LEFT SINGLE QUOTATION MARK +0x92 0x2019 #RIGHT SINGLE QUOTATION MARK +0x93 0x201C #LEFT DOUBLE QUOTATION MARK +0x94 0x201D #RIGHT DOUBLE QUOTATION MARK +0x95 0x2022 #BULLET +0x96 0x2013 #EN DASH +0x97 0x2014 #EM DASH +0x98 0x02DC #SMALL TILDE +0x99 0x2122 #TRADE MARK SIGN +0x9A #UNDEFINED +0x9B 0x203A #SINGLE RIGHT-POINTING ANGLE QUOTATION MARK +0x9C 0x0153 #LATIN SMALL LIGATURE OE +0x9D #UNDEFINED +0x9E #UNDEFINED +0x9F 0x0178 #LATIN CAPITAL LETTER Y WITH DIAERESIS +0xA0 0x00A0 #NO-BREAK SPACE +0xA1 0x00A1 #INVERTED EXCLAMATION MARK +0xA2 0x00A2 #CENT SIGN +0xA3 0x00A3 #POUND SIGN +0xA4 0x00A4 #CURRENCY SIGN +0xA5 0x00A5 #YEN SIGN +0xA6 0x00A6 #BROKEN BAR +0xA7 0x00A7 #SECTION SIGN +0xA8 0x00A8 #DIAERESIS +0xA9 0x00A9 #COPYRIGHT SIGN +0xAA 0x00AA #FEMININE ORDINAL INDICATOR +0xAB 0x00AB #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xAC 0x00AC #NOT SIGN +0xAD 0x00AD #SOFT HYPHEN +0xAE 0x00AE #REGISTERED SIGN +0xAF 0x00AF #MACRON +0xB0 0x00B0 #DEGREE SIGN +0xB1 0x00B1 #PLUS-MINUS SIGN +0xB2 0x00B2 #SUPERSCRIPT TWO +0xB3 0x00B3 #SUPERSCRIPT THREE +0xB4 0x00B4 #ACUTE ACCENT +0xB5 0x00B5 #MICRO SIGN +0xB6 0x00B6 #PILCROW SIGN +0xB7 0x00B7 #MIDDLE DOT +0xB8 0x00B8 #CEDILLA +0xB9 0x00B9 #SUPERSCRIPT ONE +0xBA 0x00BA #MASCULINE ORDINAL INDICATOR +0xBB 0x00BB #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xBC 0x00BC #VULGAR FRACTION ONE QUARTER +0xBD 0x00BD #VULGAR FRACTION ONE HALF +0xBE 0x00BE #VULGAR FRACTION THREE QUARTERS +0xBF 0x00BF #INVERTED QUESTION MARK +0xC0 0x00C0 #LATIN CAPITAL LETTER A WITH GRAVE +0xC1 0x00C1 #LATIN CAPITAL LETTER A WITH ACUTE +0xC2 0x00C2 #LATIN CAPITAL LETTER A WITH CIRCUMFLEX +0xC3 0x0102 #LATIN CAPITAL LETTER A WITH BREVE +0xC4 0x00C4 #LATIN CAPITAL LETTER A WITH DIAERESIS +0xC5 0x00C5 #LATIN CAPITAL LETTER A WITH RING ABOVE +0xC6 0x00C6 #LATIN CAPITAL LETTER AE +0xC7 0x00C7 #LATIN CAPITAL LETTER C WITH CEDILLA +0xC8 0x00C8 #LATIN CAPITAL LETTER E WITH GRAVE +0xC9 0x00C9 #LATIN CAPITAL LETTER E WITH ACUTE +0xCA 0x00CA #LATIN CAPITAL LETTER E WITH CIRCUMFLEX +0xCB 0x00CB #LATIN CAPITAL LETTER E WITH DIAERESIS +0xCC 0x0300 #COMBINING GRAVE ACCENT +0xCD 0x00CD #LATIN CAPITAL LETTER I WITH ACUTE +0xCE 0x00CE #LATIN CAPITAL LETTER I WITH CIRCUMFLEX +0xCF 0x00CF #LATIN CAPITAL LETTER I WITH DIAERESIS +0xD0 0x0110 #LATIN CAPITAL LETTER D WITH STROKE +0xD1 0x00D1 #LATIN CAPITAL LETTER N WITH TILDE +0xD2 0x0309 #COMBINING HOOK ABOVE +0xD3 0x00D3 #LATIN CAPITAL LETTER O WITH ACUTE +0xD4 0x00D4 #LATIN CAPITAL LETTER O WITH CIRCUMFLEX +0xD5 0x01A0 #LATIN CAPITAL LETTER O WITH HORN +0xD6 0x00D6 #LATIN CAPITAL LETTER O WITH DIAERESIS +0xD7 0x00D7 #MULTIPLICATION SIGN +0xD8 0x00D8 #LATIN CAPITAL LETTER O WITH STROKE +0xD9 0x00D9 #LATIN CAPITAL LETTER U WITH GRAVE +0xDA 0x00DA #LATIN CAPITAL LETTER U WITH ACUTE +0xDB 0x00DB #LATIN CAPITAL LETTER U WITH CIRCUMFLEX +0xDC 0x00DC #LATIN CAPITAL LETTER U WITH DIAERESIS +0xDD 0x01AF #LATIN CAPITAL LETTER U WITH HORN +0xDE 0x0303 #COMBINING TILDE +0xDF 0x00DF #LATIN SMALL LETTER SHARP S +0xE0 0x00E0 #LATIN SMALL LETTER A WITH GRAVE +0xE1 0x00E1 #LATIN SMALL LETTER A WITH ACUTE +0xE2 0x00E2 #LATIN SMALL LETTER A WITH CIRCUMFLEX +0xE3 0x0103 #LATIN SMALL LETTER A WITH BREVE +0xE4 0x00E4 #LATIN SMALL LETTER A WITH DIAERESIS +0xE5 0x00E5 #LATIN SMALL LETTER A WITH RING ABOVE +0xE6 0x00E6 #LATIN SMALL LETTER AE +0xE7 0x00E7 #LATIN SMALL LETTER C WITH CEDILLA +0xE8 0x00E8 #LATIN SMALL LETTER E WITH GRAVE +0xE9 0x00E9 #LATIN SMALL LETTER E WITH ACUTE +0xEA 0x00EA #LATIN SMALL LETTER E WITH CIRCUMFLEX +0xEB 0x00EB #LATIN SMALL LETTER E WITH DIAERESIS +0xEC 0x0301 #COMBINING ACUTE ACCENT +0xED 0x00ED #LATIN SMALL LETTER I WITH ACUTE +0xEE 0x00EE #LATIN SMALL LETTER I WITH CIRCUMFLEX +0xEF 0x00EF #LATIN SMALL LETTER I WITH DIAERESIS +0xF0 0x0111 #LATIN SMALL LETTER D WITH STROKE +0xF1 0x00F1 #LATIN SMALL LETTER N WITH TILDE +0xF2 0x0323 #COMBINING DOT BELOW +0xF3 0x00F3 #LATIN SMALL LETTER O WITH ACUTE +0xF4 0x00F4 #LATIN SMALL LETTER O WITH CIRCUMFLEX +0xF5 0x01A1 #LATIN SMALL LETTER O WITH HORN +0xF6 0x00F6 #LATIN SMALL LETTER O WITH DIAERESIS +0xF7 0x00F7 #DIVISION SIGN +0xF8 0x00F8 #LATIN SMALL LETTER O WITH STROKE +0xF9 0x00F9 #LATIN SMALL LETTER U WITH GRAVE +0xFA 0x00FA #LATIN SMALL LETTER U WITH ACUTE +0xFB 0x00FB #LATIN SMALL LETTER U WITH CIRCUMFLEX +0xFC 0x00FC #LATIN SMALL LETTER U WITH DIAERESIS +0xFD 0x01B0 #LATIN SMALL LETTER U WITH HORN +0xFE 0x20AB #DONG SIGN +0xFF 0x00FF #LATIN SMALL LETTER Y WITH DIAERESIS diff --git a/codepage/cp437.txt b/codepage/cp437.txt new file mode 100644 index 0000000..8f74def --- /dev/null +++ b/codepage/cp437.txt @@ -0,0 +1,274 @@ +# +# Name: cp437_DOSLatinUS to Unicode table +# Unicode version: 2.0 +# Table version: 2.00 +# Table format: Format A +# Date: 04/24/96 +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp437_DOSLatinUS code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp437_DOSLatinUS order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0a 0x000a #LINE FEED +0x0b 0x000b #VERTICAL TABULATION +0x0c 0x000c #FORM FEED +0x0d 0x000d #CARRIAGE RETURN +0x0e 0x000e #SHIFT OUT +0x0f 0x000f #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1a 0x001a #SUBSTITUTE +0x1b 0x001b #ESCAPE +0x1c 0x001c #FILE SEPARATOR +0x1d 0x001d #GROUP SEPARATOR +0x1e 0x001e #RECORD SEPARATOR +0x1f 0x001f #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2a 0x002a #ASTERISK +0x2b 0x002b #PLUS SIGN +0x2c 0x002c #COMMA +0x2d 0x002d #HYPHEN-MINUS +0x2e 0x002e #FULL STOP +0x2f 0x002f #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3a 0x003a #COLON +0x3b 0x003b #SEMICOLON +0x3c 0x003c #LESS-THAN SIGN +0x3d 0x003d #EQUALS SIGN +0x3e 0x003e #GREATER-THAN SIGN +0x3f 0x003f #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4a 0x004a #LATIN CAPITAL LETTER J +0x4b 0x004b #LATIN CAPITAL LETTER K +0x4c 0x004c #LATIN CAPITAL LETTER L +0x4d 0x004d #LATIN CAPITAL LETTER M +0x4e 0x004e #LATIN CAPITAL LETTER N +0x4f 0x004f #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5a 0x005a #LATIN CAPITAL LETTER Z +0x5b 0x005b #LEFT SQUARE BRACKET +0x5c 0x005c #REVERSE SOLIDUS +0x5d 0x005d #RIGHT SQUARE BRACKET +0x5e 0x005e #CIRCUMFLEX ACCENT +0x5f 0x005f #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6a 0x006a #LATIN SMALL LETTER J +0x6b 0x006b #LATIN SMALL LETTER K +0x6c 0x006c #LATIN SMALL LETTER L +0x6d 0x006d #LATIN SMALL LETTER M +0x6e 0x006e #LATIN SMALL LETTER N +0x6f 0x006f #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7a 0x007a #LATIN SMALL LETTER Z +0x7b 0x007b #LEFT CURLY BRACKET +0x7c 0x007c #VERTICAL LINE +0x7d 0x007d #RIGHT CURLY BRACKET +0x7e 0x007e #TILDE +0x7f 0x007f #DELETE +0x80 0x00c7 #LATIN CAPITAL LETTER C WITH CEDILLA +0x81 0x00fc #LATIN SMALL LETTER U WITH DIAERESIS +0x82 0x00e9 #LATIN SMALL LETTER E WITH ACUTE +0x83 0x00e2 #LATIN SMALL LETTER A WITH CIRCUMFLEX +0x84 0x00e4 #LATIN SMALL LETTER A WITH DIAERESIS +0x85 0x00e0 #LATIN SMALL LETTER A WITH GRAVE +0x86 0x00e5 #LATIN SMALL LETTER A WITH RING ABOVE +0x87 0x00e7 #LATIN SMALL LETTER C WITH CEDILLA +0x88 0x00ea #LATIN SMALL LETTER E WITH CIRCUMFLEX +0x89 0x00eb #LATIN SMALL LETTER E WITH DIAERESIS +0x8a 0x00e8 #LATIN SMALL LETTER E WITH GRAVE +0x8b 0x00ef #LATIN SMALL LETTER I WITH DIAERESIS +0x8c 0x00ee #LATIN SMALL LETTER I WITH CIRCUMFLEX +0x8d 0x00ec #LATIN SMALL LETTER I WITH GRAVE +0x8e 0x00c4 #LATIN CAPITAL LETTER A WITH DIAERESIS +0x8f 0x00c5 #LATIN CAPITAL LETTER A WITH RING ABOVE +0x90 0x00c9 #LATIN CAPITAL LETTER E WITH ACUTE +0x91 0x00e6 #LATIN SMALL LIGATURE AE +0x92 0x00c6 #LATIN CAPITAL LIGATURE AE +0x93 0x00f4 #LATIN SMALL LETTER O WITH CIRCUMFLEX +0x94 0x00f6 #LATIN SMALL LETTER O WITH DIAERESIS +0x95 0x00f2 #LATIN SMALL LETTER O WITH GRAVE +0x96 0x00fb #LATIN SMALL LETTER U WITH CIRCUMFLEX +0x97 0x00f9 #LATIN SMALL LETTER U WITH GRAVE +0x98 0x00ff #LATIN SMALL LETTER Y WITH DIAERESIS +0x99 0x00d6 #LATIN CAPITAL LETTER O WITH DIAERESIS +0x9a 0x00dc #LATIN CAPITAL LETTER U WITH DIAERESIS +0x9b 0x00a2 #CENT SIGN +0x9c 0x00a3 #POUND SIGN +0x9d 0x00a5 #YEN SIGN +0x9e 0x20a7 #PESETA SIGN +0x9f 0x0192 #LATIN SMALL LETTER F WITH HOOK +0xa0 0x00e1 #LATIN SMALL LETTER A WITH ACUTE +0xa1 0x00ed #LATIN SMALL LETTER I WITH ACUTE +0xa2 0x00f3 #LATIN SMALL LETTER O WITH ACUTE +0xa3 0x00fa #LATIN SMALL LETTER U WITH ACUTE +0xa4 0x00f1 #LATIN SMALL LETTER N WITH TILDE +0xa5 0x00d1 #LATIN CAPITAL LETTER N WITH TILDE +0xa6 0x00aa #FEMININE ORDINAL INDICATOR +0xa7 0x00ba #MASCULINE ORDINAL INDICATOR +0xa8 0x00bf #INVERTED QUESTION MARK +0xa9 0x2310 #REVERSED NOT SIGN +0xaa 0x00ac #NOT SIGN +0xab 0x00bd #VULGAR FRACTION ONE HALF +0xac 0x00bc #VULGAR FRACTION ONE QUARTER +0xad 0x00a1 #INVERTED EXCLAMATION MARK +0xae 0x00ab #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xaf 0x00bb #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xb0 0x2591 #LIGHT SHADE +0xb1 0x2592 #MEDIUM SHADE +0xb2 0x2593 #DARK SHADE +0xb3 0x2502 #BOX DRAWINGS LIGHT VERTICAL +0xb4 0x2524 #BOX DRAWINGS LIGHT VERTICAL AND LEFT +0xb5 0x2561 #BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE +0xb6 0x2562 #BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE +0xb7 0x2556 #BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE +0xb8 0x2555 #BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE +0xb9 0x2563 #BOX DRAWINGS DOUBLE VERTICAL AND LEFT +0xba 0x2551 #BOX DRAWINGS DOUBLE VERTICAL +0xbb 0x2557 #BOX DRAWINGS DOUBLE DOWN AND LEFT +0xbc 0x255d #BOX DRAWINGS DOUBLE UP AND LEFT +0xbd 0x255c #BOX DRAWINGS UP DOUBLE AND LEFT SINGLE +0xbe 0x255b #BOX DRAWINGS UP SINGLE AND LEFT DOUBLE +0xbf 0x2510 #BOX DRAWINGS LIGHT DOWN AND LEFT +0xc0 0x2514 #BOX DRAWINGS LIGHT UP AND RIGHT +0xc1 0x2534 #BOX DRAWINGS LIGHT UP AND HORIZONTAL +0xc2 0x252c #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL +0xc3 0x251c #BOX DRAWINGS LIGHT VERTICAL AND RIGHT +0xc4 0x2500 #BOX DRAWINGS LIGHT HORIZONTAL +0xc5 0x253c #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL +0xc6 0x255e #BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE +0xc7 0x255f #BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE +0xc8 0x255a #BOX DRAWINGS DOUBLE UP AND RIGHT +0xc9 0x2554 #BOX DRAWINGS DOUBLE DOWN AND RIGHT +0xca 0x2569 #BOX DRAWINGS DOUBLE UP AND HORIZONTAL +0xcb 0x2566 #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL +0xcc 0x2560 #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT +0xcd 0x2550 #BOX DRAWINGS DOUBLE HORIZONTAL +0xce 0x256c #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL +0xcf 0x2567 #BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE +0xd0 0x2568 #BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE +0xd1 0x2564 #BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE +0xd2 0x2565 #BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE +0xd3 0x2559 #BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE +0xd4 0x2558 #BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE +0xd5 0x2552 #BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE +0xd6 0x2553 #BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE +0xd7 0x256b #BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE +0xd8 0x256a #BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE +0xd9 0x2518 #BOX DRAWINGS LIGHT UP AND LEFT +0xda 0x250c #BOX DRAWINGS LIGHT DOWN AND RIGHT +0xdb 0x2588 #FULL BLOCK +0xdc 0x2584 #LOWER HALF BLOCK +0xdd 0x258c #LEFT HALF BLOCK +0xde 0x2590 #RIGHT HALF BLOCK +0xdf 0x2580 #UPPER HALF BLOCK +0xe0 0x03b1 #GREEK SMALL LETTER ALPHA +0xe1 0x00df #LATIN SMALL LETTER SHARP S +0xe2 0x0393 #GREEK CAPITAL LETTER GAMMA +0xe3 0x03c0 #GREEK SMALL LETTER PI +0xe4 0x03a3 #GREEK CAPITAL LETTER SIGMA +0xe5 0x03c3 #GREEK SMALL LETTER SIGMA +0xe6 0x00b5 #MICRO SIGN +0xe7 0x03c4 #GREEK SMALL LETTER TAU +0xe8 0x03a6 #GREEK CAPITAL LETTER PHI +0xe9 0x0398 #GREEK CAPITAL LETTER THETA +0xea 0x03a9 #GREEK CAPITAL LETTER OMEGA +0xeb 0x03b4 #GREEK SMALL LETTER DELTA +0xec 0x221e #INFINITY +0xed 0x03c6 #GREEK SMALL LETTER PHI +0xee 0x03b5 #GREEK SMALL LETTER EPSILON +0xef 0x2229 #INTERSECTION +0xf0 0x2261 #IDENTICAL TO +0xf1 0x00b1 #PLUS-MINUS SIGN +0xf2 0x2265 #GREATER-THAN OR EQUAL TO +0xf3 0x2264 #LESS-THAN OR EQUAL TO +0xf4 0x2320 #TOP HALF INTEGRAL +0xf5 0x2321 #BOTTOM HALF INTEGRAL +0xf6 0x00f7 #DIVISION SIGN +0xf7 0x2248 #ALMOST EQUAL TO +0xf8 0x00b0 #DEGREE SIGN +0xf9 0x2219 #BULLET OPERATOR +0xfa 0x00b7 #MIDDLE DOT +0xfb 0x221a #SQUARE ROOT +0xfc 0x207f #SUPERSCRIPT LATIN SMALL LETTER N +0xfd 0x00b2 #SUPERSCRIPT TWO +0xfe 0x25a0 #BLACK SQUARE +0xff 0x00a0 #NO-BREAK SPACE + \ No newline at end of file diff --git a/codepage/cp737.txt b/codepage/cp737.txt new file mode 100644 index 0000000..6b8b5cc --- /dev/null +++ b/codepage/cp737.txt @@ -0,0 +1,274 @@ +# +# Name: cp737_DOSGreek to Unicode table +# Unicode version: 2.0 +# Table version: 2.00 +# Table format: Format A +# Date: 04/24/96 +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp737_DOSGreek code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp737_DOSGreek order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0a 0x000a #LINE FEED +0x0b 0x000b #VERTICAL TABULATION +0x0c 0x000c #FORM FEED +0x0d 0x000d #CARRIAGE RETURN +0x0e 0x000e #SHIFT OUT +0x0f 0x000f #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1a 0x001a #SUBSTITUTE +0x1b 0x001b #ESCAPE +0x1c 0x001c #FILE SEPARATOR +0x1d 0x001d #GROUP SEPARATOR +0x1e 0x001e #RECORD SEPARATOR +0x1f 0x001f #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2a 0x002a #ASTERISK +0x2b 0x002b #PLUS SIGN +0x2c 0x002c #COMMA +0x2d 0x002d #HYPHEN-MINUS +0x2e 0x002e #FULL STOP +0x2f 0x002f #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3a 0x003a #COLON +0x3b 0x003b #SEMICOLON +0x3c 0x003c #LESS-THAN SIGN +0x3d 0x003d #EQUALS SIGN +0x3e 0x003e #GREATER-THAN SIGN +0x3f 0x003f #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4a 0x004a #LATIN CAPITAL LETTER J +0x4b 0x004b #LATIN CAPITAL LETTER K +0x4c 0x004c #LATIN CAPITAL LETTER L +0x4d 0x004d #LATIN CAPITAL LETTER M +0x4e 0x004e #LATIN CAPITAL LETTER N +0x4f 0x004f #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5a 0x005a #LATIN CAPITAL LETTER Z +0x5b 0x005b #LEFT SQUARE BRACKET +0x5c 0x005c #REVERSE SOLIDUS +0x5d 0x005d #RIGHT SQUARE BRACKET +0x5e 0x005e #CIRCUMFLEX ACCENT +0x5f 0x005f #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6a 0x006a #LATIN SMALL LETTER J +0x6b 0x006b #LATIN SMALL LETTER K +0x6c 0x006c #LATIN SMALL LETTER L +0x6d 0x006d #LATIN SMALL LETTER M +0x6e 0x006e #LATIN SMALL LETTER N +0x6f 0x006f #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7a 0x007a #LATIN SMALL LETTER Z +0x7b 0x007b #LEFT CURLY BRACKET +0x7c 0x007c #VERTICAL LINE +0x7d 0x007d #RIGHT CURLY BRACKET +0x7e 0x007e #TILDE +0x7f 0x007f #DELETE +0x80 0x0391 #GREEK CAPITAL LETTER ALPHA +0x81 0x0392 #GREEK CAPITAL LETTER BETA +0x82 0x0393 #GREEK CAPITAL LETTER GAMMA +0x83 0x0394 #GREEK CAPITAL LETTER DELTA +0x84 0x0395 #GREEK CAPITAL LETTER EPSILON +0x85 0x0396 #GREEK CAPITAL LETTER ZETA +0x86 0x0397 #GREEK CAPITAL LETTER ETA +0x87 0x0398 #GREEK CAPITAL LETTER THETA +0x88 0x0399 #GREEK CAPITAL LETTER IOTA +0x89 0x039a #GREEK CAPITAL LETTER KAPPA +0x8a 0x039b #GREEK CAPITAL LETTER LAMDA +0x8b 0x039c #GREEK CAPITAL LETTER MU +0x8c 0x039d #GREEK CAPITAL LETTER NU +0x8d 0x039e #GREEK CAPITAL LETTER XI +0x8e 0x039f #GREEK CAPITAL LETTER OMICRON +0x8f 0x03a0 #GREEK CAPITAL LETTER PI +0x90 0x03a1 #GREEK CAPITAL LETTER RHO +0x91 0x03a3 #GREEK CAPITAL LETTER SIGMA +0x92 0x03a4 #GREEK CAPITAL LETTER TAU +0x93 0x03a5 #GREEK CAPITAL LETTER UPSILON +0x94 0x03a6 #GREEK CAPITAL LETTER PHI +0x95 0x03a7 #GREEK CAPITAL LETTER CHI +0x96 0x03a8 #GREEK CAPITAL LETTER PSI +0x97 0x03a9 #GREEK CAPITAL LETTER OMEGA +0x98 0x03b1 #GREEK SMALL LETTER ALPHA +0x99 0x03b2 #GREEK SMALL LETTER BETA +0x9a 0x03b3 #GREEK SMALL LETTER GAMMA +0x9b 0x03b4 #GREEK SMALL LETTER DELTA +0x9c 0x03b5 #GREEK SMALL LETTER EPSILON +0x9d 0x03b6 #GREEK SMALL LETTER ZETA +0x9e 0x03b7 #GREEK SMALL LETTER ETA +0x9f 0x03b8 #GREEK SMALL LETTER THETA +0xa0 0x03b9 #GREEK SMALL LETTER IOTA +0xa1 0x03ba #GREEK SMALL LETTER KAPPA +0xa2 0x03bb #GREEK SMALL LETTER LAMDA +0xa3 0x03bc #GREEK SMALL LETTER MU +0xa4 0x03bd #GREEK SMALL LETTER NU +0xa5 0x03be #GREEK SMALL LETTER XI +0xa6 0x03bf #GREEK SMALL LETTER OMICRON +0xa7 0x03c0 #GREEK SMALL LETTER PI +0xa8 0x03c1 #GREEK SMALL LETTER RHO +0xa9 0x03c3 #GREEK SMALL LETTER SIGMA +0xaa 0x03c2 #GREEK SMALL LETTER FINAL SIGMA +0xab 0x03c4 #GREEK SMALL LETTER TAU +0xac 0x03c5 #GREEK SMALL LETTER UPSILON +0xad 0x03c6 #GREEK SMALL LETTER PHI +0xae 0x03c7 #GREEK SMALL LETTER CHI +0xaf 0x03c8 #GREEK SMALL LETTER PSI +0xb0 0x2591 #LIGHT SHADE +0xb1 0x2592 #MEDIUM SHADE +0xb2 0x2593 #DARK SHADE +0xb3 0x2502 #BOX DRAWINGS LIGHT VERTICAL +0xb4 0x2524 #BOX DRAWINGS LIGHT VERTICAL AND LEFT +0xb5 0x2561 #BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE +0xb6 0x2562 #BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE +0xb7 0x2556 #BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE +0xb8 0x2555 #BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE +0xb9 0x2563 #BOX DRAWINGS DOUBLE VERTICAL AND LEFT +0xba 0x2551 #BOX DRAWINGS DOUBLE VERTICAL +0xbb 0x2557 #BOX DRAWINGS DOUBLE DOWN AND LEFT +0xbc 0x255d #BOX DRAWINGS DOUBLE UP AND LEFT +0xbd 0x255c #BOX DRAWINGS UP DOUBLE AND LEFT SINGLE +0xbe 0x255b #BOX DRAWINGS UP SINGLE AND LEFT DOUBLE +0xbf 0x2510 #BOX DRAWINGS LIGHT DOWN AND LEFT +0xc0 0x2514 #BOX DRAWINGS LIGHT UP AND RIGHT +0xc1 0x2534 #BOX DRAWINGS LIGHT UP AND HORIZONTAL +0xc2 0x252c #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL +0xc3 0x251c #BOX DRAWINGS LIGHT VERTICAL AND RIGHT +0xc4 0x2500 #BOX DRAWINGS LIGHT HORIZONTAL +0xc5 0x253c #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL +0xc6 0x255e #BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE +0xc7 0x255f #BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE +0xc8 0x255a #BOX DRAWINGS DOUBLE UP AND RIGHT +0xc9 0x2554 #BOX DRAWINGS DOUBLE DOWN AND RIGHT +0xca 0x2569 #BOX DRAWINGS DOUBLE UP AND HORIZONTAL +0xcb 0x2566 #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL +0xcc 0x2560 #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT +0xcd 0x2550 #BOX DRAWINGS DOUBLE HORIZONTAL +0xce 0x256c #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL +0xcf 0x2567 #BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE +0xd0 0x2568 #BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE +0xd1 0x2564 #BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE +0xd2 0x2565 #BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE +0xd3 0x2559 #BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE +0xd4 0x2558 #BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE +0xd5 0x2552 #BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE +0xd6 0x2553 #BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE +0xd7 0x256b #BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE +0xd8 0x256a #BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE +0xd9 0x2518 #BOX DRAWINGS LIGHT UP AND LEFT +0xda 0x250c #BOX DRAWINGS LIGHT DOWN AND RIGHT +0xdb 0x2588 #FULL BLOCK +0xdc 0x2584 #LOWER HALF BLOCK +0xdd 0x258c #LEFT HALF BLOCK +0xde 0x2590 #RIGHT HALF BLOCK +0xdf 0x2580 #UPPER HALF BLOCK +0xe0 0x03c9 #GREEK SMALL LETTER OMEGA +0xe1 0x03ac #GREEK SMALL LETTER ALPHA WITH TONOS +0xe2 0x03ad #GREEK SMALL LETTER EPSILON WITH TONOS +0xe3 0x03ae #GREEK SMALL LETTER ETA WITH TONOS +0xe4 0x03ca #GREEK SMALL LETTER IOTA WITH DIALYTIKA +0xe5 0x03af #GREEK SMALL LETTER IOTA WITH TONOS +0xe6 0x03cc #GREEK SMALL LETTER OMICRON WITH TONOS +0xe7 0x03cd #GREEK SMALL LETTER UPSILON WITH TONOS +0xe8 0x03cb #GREEK SMALL LETTER UPSILON WITH DIALYTIKA +0xe9 0x03ce #GREEK SMALL LETTER OMEGA WITH TONOS +0xea 0x0386 #GREEK CAPITAL LETTER ALPHA WITH TONOS +0xeb 0x0388 #GREEK CAPITAL LETTER EPSILON WITH TONOS +0xec 0x0389 #GREEK CAPITAL LETTER ETA WITH TONOS +0xed 0x038a #GREEK CAPITAL LETTER IOTA WITH TONOS +0xee 0x038c #GREEK CAPITAL LETTER OMICRON WITH TONOS +0xef 0x038e #GREEK CAPITAL LETTER UPSILON WITH TONOS +0xf0 0x038f #GREEK CAPITAL LETTER OMEGA WITH TONOS +0xf1 0x00b1 #PLUS-MINUS SIGN +0xf2 0x2265 #GREATER-THAN OR EQUAL TO +0xf3 0x2264 #LESS-THAN OR EQUAL TO +0xf4 0x03aa #GREEK CAPITAL LETTER IOTA WITH DIALYTIKA +0xf5 0x03ab #GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA +0xf6 0x00f7 #DIVISION SIGN +0xf7 0x2248 #ALMOST EQUAL TO +0xf8 0x00b0 #DEGREE SIGN +0xf9 0x2219 #BULLET OPERATOR +0xfa 0x00b7 #MIDDLE DOT +0xfb 0x221a #SQUARE ROOT +0xfc 0x207f #SUPERSCRIPT LATIN SMALL LETTER N +0xfd 0x00b2 #SUPERSCRIPT TWO +0xfe 0x25a0 #BLACK SQUARE +0xff 0x00a0 #NO-BREAK SPACE + \ No newline at end of file diff --git a/codepage/cp775.txt b/codepage/cp775.txt new file mode 100644 index 0000000..49f2cd3 --- /dev/null +++ b/codepage/cp775.txt @@ -0,0 +1,275 @@ +# +# Name: cp775_DOSBaltRim to Unicode table +# Unicode version: 2.0 +# Table version: 2.00 +# Table format: Format A +# Date: 04/24/96 +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp775_DOSBaltRim code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp775_DOSBaltRim order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0a 0x000a #LINE FEED +0x0b 0x000b #VERTICAL TABULATION +0x0c 0x000c #FORM FEED +0x0d 0x000d #CARRIAGE RETURN +0x0e 0x000e #SHIFT OUT +0x0f 0x000f #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1a 0x001a #SUBSTITUTE +0x1b 0x001b #ESCAPE +0x1c 0x001c #FILE SEPARATOR +0x1d 0x001d #GROUP SEPARATOR +0x1e 0x001e #RECORD SEPARATOR +0x1f 0x001f #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2a 0x002a #ASTERISK +0x2b 0x002b #PLUS SIGN +0x2c 0x002c #COMMA +0x2d 0x002d #HYPHEN-MINUS +0x2e 0x002e #FULL STOP +0x2f 0x002f #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3a 0x003a #COLON +0x3b 0x003b #SEMICOLON +0x3c 0x003c #LESS-THAN SIGN +0x3d 0x003d #EQUALS SIGN +0x3e 0x003e #GREATER-THAN SIGN +0x3f 0x003f #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4a 0x004a #LATIN CAPITAL LETTER J +0x4b 0x004b #LATIN CAPITAL LETTER K +0x4c 0x004c #LATIN CAPITAL LETTER L +0x4d 0x004d #LATIN CAPITAL LETTER M +0x4e 0x004e #LATIN CAPITAL LETTER N +0x4f 0x004f #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5a 0x005a #LATIN CAPITAL LETTER Z +0x5b 0x005b #LEFT SQUARE BRACKET +0x5c 0x005c #REVERSE SOLIDUS +0x5d 0x005d #RIGHT SQUARE BRACKET +0x5e 0x005e #CIRCUMFLEX ACCENT +0x5f 0x005f #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6a 0x006a #LATIN SMALL LETTER J +0x6b 0x006b #LATIN SMALL LETTER K +0x6c 0x006c #LATIN SMALL LETTER L +0x6d 0x006d #LATIN SMALL LETTER M +0x6e 0x006e #LATIN SMALL LETTER N +0x6f 0x006f #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7a 0x007a #LATIN SMALL LETTER Z +0x7b 0x007b #LEFT CURLY BRACKET +0x7c 0x007c #VERTICAL LINE +0x7d 0x007d #RIGHT CURLY BRACKET +0x7e 0x007e #TILDE +0x7f 0x007f #DELETE +0x80 0x0106 #LATIN CAPITAL LETTER C WITH ACUTE +0x81 0x00fc #LATIN SMALL LETTER U WITH DIAERESIS +0x82 0x00e9 #LATIN SMALL LETTER E WITH ACUTE +0x83 0x0101 #LATIN SMALL LETTER A WITH MACRON +0x84 0x00e4 #LATIN SMALL LETTER A WITH DIAERESIS +0x85 0x0123 #LATIN SMALL LETTER G WITH CEDILLA +0x86 0x00e5 #LATIN SMALL LETTER A WITH RING ABOVE +0x87 0x0107 #LATIN SMALL LETTER C WITH ACUTE +0x88 0x0142 #LATIN SMALL LETTER L WITH STROKE +0x89 0x0113 #LATIN SMALL LETTER E WITH MACRON +0x8a 0x0156 #LATIN CAPITAL LETTER R WITH CEDILLA +0x8b 0x0157 #LATIN SMALL LETTER R WITH CEDILLA +0x8c 0x012b #LATIN SMALL LETTER I WITH MACRON +0x8d 0x0179 #LATIN CAPITAL LETTER Z WITH ACUTE +0x8e 0x00c4 #LATIN CAPITAL LETTER A WITH DIAERESIS +0x8f 0x00c5 #LATIN CAPITAL LETTER A WITH RING ABOVE +0x90 0x00c9 #LATIN CAPITAL LETTER E WITH ACUTE +0x91 0x00e6 #LATIN SMALL LIGATURE AE +0x92 0x00c6 #LATIN CAPITAL LIGATURE AE +0x93 0x014d #LATIN SMALL LETTER O WITH MACRON +0x94 0x00f6 #LATIN SMALL LETTER O WITH DIAERESIS +0x95 0x0122 #LATIN CAPITAL LETTER G WITH CEDILLA +0x96 0x00a2 #CENT SIGN +0x97 0x015a #LATIN CAPITAL LETTER S WITH ACUTE +0x98 0x015b #LATIN SMALL LETTER S WITH ACUTE +0x99 0x00d6 #LATIN CAPITAL LETTER O WITH DIAERESIS +0x9a 0x00dc #LATIN CAPITAL LETTER U WITH DIAERESIS +0x9b 0x00f8 #LATIN SMALL LETTER O WITH STROKE +0x9c 0x00a3 #POUND SIGN +0x9d 0x00d8 #LATIN CAPITAL LETTER O WITH STROKE +0x9e 0x00d7 #MULTIPLICATION SIGN +0x9f 0x00a4 #CURRENCY SIGN +0xa0 0x0100 #LATIN CAPITAL LETTER A WITH MACRON +0xa1 0x012a #LATIN CAPITAL LETTER I WITH MACRON +0xa2 0x00f3 #LATIN SMALL LETTER O WITH ACUTE +0xa3 0x017b #LATIN CAPITAL LETTER Z WITH DOT ABOVE +0xa4 0x017c #LATIN SMALL LETTER Z WITH DOT ABOVE +0xa5 0x017a #LATIN SMALL LETTER Z WITH ACUTE +0xa6 0x201d #RIGHT DOUBLE QUOTATION MARK +0xa7 0x00a6 #BROKEN BAR +0xa8 0x00a9 #COPYRIGHT SIGN +0xa9 0x00ae #REGISTERED SIGN +0xaa 0x00ac #NOT SIGN +0xab 0x00bd #VULGAR FRACTION ONE HALF +0xac 0x00bc #VULGAR FRACTION ONE QUARTER +0xad 0x0141 #LATIN CAPITAL LETTER L WITH STROKE +0xae 0x00ab #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xaf 0x00bb #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xb0 0x2591 #LIGHT SHADE +0xb1 0x2592 #MEDIUM SHADE +0xb2 0x2593 #DARK SHADE +0xb3 0x2502 #BOX DRAWINGS LIGHT VERTICAL +0xb4 0x2524 #BOX DRAWINGS LIGHT VERTICAL AND LEFT +0xb5 0x0104 #LATIN CAPITAL LETTER A WITH OGONEK +0xb6 0x010c #LATIN CAPITAL LETTER C WITH CARON +0xb7 0x0118 #LATIN CAPITAL LETTER E WITH OGONEK +0xb8 0x0116 #LATIN CAPITAL LETTER E WITH DOT ABOVE +0xb9 0x2563 #BOX DRAWINGS DOUBLE VERTICAL AND LEFT +0xba 0x2551 #BOX DRAWINGS DOUBLE VERTICAL +0xbb 0x2557 #BOX DRAWINGS DOUBLE DOWN AND LEFT +0xbc 0x255d #BOX DRAWINGS DOUBLE UP AND LEFT +0xbd 0x012e #LATIN CAPITAL LETTER I WITH OGONEK +0xbe 0x0160 #LATIN CAPITAL LETTER S WITH CARON +0xbf 0x2510 #BOX DRAWINGS LIGHT DOWN AND LEFT +0xc0 0x2514 #BOX DRAWINGS LIGHT UP AND RIGHT +0xc1 0x2534 #BOX DRAWINGS LIGHT UP AND HORIZONTAL +0xc2 0x252c #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL +0xc3 0x251c #BOX DRAWINGS LIGHT VERTICAL AND RIGHT +0xc4 0x2500 #BOX DRAWINGS LIGHT HORIZONTAL +0xc5 0x253c #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL +0xc6 0x0172 #LATIN CAPITAL LETTER U WITH OGONEK +0xc7 0x016a #LATIN CAPITAL LETTER U WITH MACRON +0xc8 0x255a #BOX DRAWINGS DOUBLE UP AND RIGHT +0xc9 0x2554 #BOX DRAWINGS DOUBLE DOWN AND RIGHT +0xca 0x2569 #BOX DRAWINGS DOUBLE UP AND HORIZONTAL +0xcb 0x2566 #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL +0xcc 0x2560 #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT +0xcd 0x2550 #BOX DRAWINGS DOUBLE HORIZONTAL +0xce 0x256c #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL +0xcf 0x017d #LATIN CAPITAL LETTER Z WITH CARON +0xd0 0x0105 #LATIN SMALL LETTER A WITH OGONEK +0xd1 0x010d #LATIN SMALL LETTER C WITH CARON +0xd2 0x0119 #LATIN SMALL LETTER E WITH OGONEK +0xd3 0x0117 #LATIN SMALL LETTER E WITH DOT ABOVE +0xd4 0x012f #LATIN SMALL LETTER I WITH OGONEK +0xd5 0x0161 #LATIN SMALL LETTER S WITH CARON +0xd6 0x0173 #LATIN SMALL LETTER U WITH OGONEK +0xd7 0x016b #LATIN SMALL LETTER U WITH MACRON +0xd8 0x017e #LATIN SMALL LETTER Z WITH CARON +0xd9 0x2518 #BOX DRAWINGS LIGHT UP AND LEFT +0xda 0x250c #BOX DRAWINGS LIGHT DOWN AND RIGHT +0xdb 0x2588 #FULL BLOCK +0xdc 0x2584 #LOWER HALF BLOCK +0xdd 0x258c #LEFT HALF BLOCK +0xde 0x2590 #RIGHT HALF BLOCK +0xdf 0x2580 #UPPER HALF BLOCK +0xe0 0x00d3 #LATIN CAPITAL LETTER O WITH ACUTE +0xe1 0x00df #LATIN SMALL LETTER SHARP S (GERMAN) +0xe2 0x014c #LATIN CAPITAL LETTER O WITH MACRON +0xe3 0x0143 #LATIN CAPITAL LETTER N WITH ACUTE +0xe4 0x00f5 #LATIN SMALL LETTER O WITH TILDE +0xe5 0x00d5 #LATIN CAPITAL LETTER O WITH TILDE +0xe6 0x00b5 #MICRO SIGN +0xe7 0x0144 #LATIN SMALL LETTER N WITH ACUTE +0xe8 0x0136 #LATIN CAPITAL LETTER K WITH CEDILLA +0xe9 0x0137 #LATIN SMALL LETTER K WITH CEDILLA +0xea 0x013b #LATIN CAPITAL LETTER L WITH CEDILLA +0xeb 0x013c #LATIN SMALL LETTER L WITH CEDILLA +0xec 0x0146 #LATIN SMALL LETTER N WITH CEDILLA +0xed 0x0112 #LATIN CAPITAL LETTER E WITH MACRON +0xee 0x0145 #LATIN CAPITAL LETTER N WITH CEDILLA +0xef 0x2019 #RIGHT SINGLE QUOTATION MARK +0xf0 0x00ad #SOFT HYPHEN +0xf1 0x00b1 #PLUS-MINUS SIGN +0xf2 0x201c #LEFT DOUBLE QUOTATION MARK +0xf3 0x00be #VULGAR FRACTION THREE QUARTERS +0xf4 0x00b6 #PILCROW SIGN +0xf5 0x00a7 #SECTION SIGN +0xf6 0x00f7 #DIVISION SIGN +0xf7 0x201e #DOUBLE LOW-9 QUOTATION MARK +0xf8 0x00b0 #DEGREE SIGN +0xf9 0x2219 #BULLET OPERATOR +0xfa 0x00b7 #MIDDLE DOT +0xfb 0x00b9 #SUPERSCRIPT ONE +0xfc 0x00b3 #SUPERSCRIPT THREE +0xfd 0x00b2 #SUPERSCRIPT TWO +0xfe 0x25a0 #BLACK SQUARE +0xff 0x00a0 #NO-BREAK SPACE + + \ No newline at end of file diff --git a/codepage/cp850.txt b/codepage/cp850.txt new file mode 100644 index 0000000..ef47b43 --- /dev/null +++ b/codepage/cp850.txt @@ -0,0 +1,274 @@ +# +# Name: cp850_DOSLatin1 to Unicode table +# Unicode version: 2.0 +# Table version: 2.00 +# Table format: Format A +# Date: 04/24/96 +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp850_DOSLatin1 code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp850_DOSLatin1 order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0a 0x000a #LINE FEED +0x0b 0x000b #VERTICAL TABULATION +0x0c 0x000c #FORM FEED +0x0d 0x000d #CARRIAGE RETURN +0x0e 0x000e #SHIFT OUT +0x0f 0x000f #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1a 0x001a #SUBSTITUTE +0x1b 0x001b #ESCAPE +0x1c 0x001c #FILE SEPARATOR +0x1d 0x001d #GROUP SEPARATOR +0x1e 0x001e #RECORD SEPARATOR +0x1f 0x001f #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2a 0x002a #ASTERISK +0x2b 0x002b #PLUS SIGN +0x2c 0x002c #COMMA +0x2d 0x002d #HYPHEN-MINUS +0x2e 0x002e #FULL STOP +0x2f 0x002f #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3a 0x003a #COLON +0x3b 0x003b #SEMICOLON +0x3c 0x003c #LESS-THAN SIGN +0x3d 0x003d #EQUALS SIGN +0x3e 0x003e #GREATER-THAN SIGN +0x3f 0x003f #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4a 0x004a #LATIN CAPITAL LETTER J +0x4b 0x004b #LATIN CAPITAL LETTER K +0x4c 0x004c #LATIN CAPITAL LETTER L +0x4d 0x004d #LATIN CAPITAL LETTER M +0x4e 0x004e #LATIN CAPITAL LETTER N +0x4f 0x004f #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5a 0x005a #LATIN CAPITAL LETTER Z +0x5b 0x005b #LEFT SQUARE BRACKET +0x5c 0x005c #REVERSE SOLIDUS +0x5d 0x005d #RIGHT SQUARE BRACKET +0x5e 0x005e #CIRCUMFLEX ACCENT +0x5f 0x005f #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6a 0x006a #LATIN SMALL LETTER J +0x6b 0x006b #LATIN SMALL LETTER K +0x6c 0x006c #LATIN SMALL LETTER L +0x6d 0x006d #LATIN SMALL LETTER M +0x6e 0x006e #LATIN SMALL LETTER N +0x6f 0x006f #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7a 0x007a #LATIN SMALL LETTER Z +0x7b 0x007b #LEFT CURLY BRACKET +0x7c 0x007c #VERTICAL LINE +0x7d 0x007d #RIGHT CURLY BRACKET +0x7e 0x007e #TILDE +0x7f 0x007f #DELETE +0x80 0x00c7 #LATIN CAPITAL LETTER C WITH CEDILLA +0x81 0x00fc #LATIN SMALL LETTER U WITH DIAERESIS +0x82 0x00e9 #LATIN SMALL LETTER E WITH ACUTE +0x83 0x00e2 #LATIN SMALL LETTER A WITH CIRCUMFLEX +0x84 0x00e4 #LATIN SMALL LETTER A WITH DIAERESIS +0x85 0x00e0 #LATIN SMALL LETTER A WITH GRAVE +0x86 0x00e5 #LATIN SMALL LETTER A WITH RING ABOVE +0x87 0x00e7 #LATIN SMALL LETTER C WITH CEDILLA +0x88 0x00ea #LATIN SMALL LETTER E WITH CIRCUMFLEX +0x89 0x00eb #LATIN SMALL LETTER E WITH DIAERESIS +0x8a 0x00e8 #LATIN SMALL LETTER E WITH GRAVE +0x8b 0x00ef #LATIN SMALL LETTER I WITH DIAERESIS +0x8c 0x00ee #LATIN SMALL LETTER I WITH CIRCUMFLEX +0x8d 0x00ec #LATIN SMALL LETTER I WITH GRAVE +0x8e 0x00c4 #LATIN CAPITAL LETTER A WITH DIAERESIS +0x8f 0x00c5 #LATIN CAPITAL LETTER A WITH RING ABOVE +0x90 0x00c9 #LATIN CAPITAL LETTER E WITH ACUTE +0x91 0x00e6 #LATIN SMALL LIGATURE AE +0x92 0x00c6 #LATIN CAPITAL LIGATURE AE +0x93 0x00f4 #LATIN SMALL LETTER O WITH CIRCUMFLEX +0x94 0x00f6 #LATIN SMALL LETTER O WITH DIAERESIS +0x95 0x00f2 #LATIN SMALL LETTER O WITH GRAVE +0x96 0x00fb #LATIN SMALL LETTER U WITH CIRCUMFLEX +0x97 0x00f9 #LATIN SMALL LETTER U WITH GRAVE +0x98 0x00ff #LATIN SMALL LETTER Y WITH DIAERESIS +0x99 0x00d6 #LATIN CAPITAL LETTER O WITH DIAERESIS +0x9a 0x00dc #LATIN CAPITAL LETTER U WITH DIAERESIS +0x9b 0x00f8 #LATIN SMALL LETTER O WITH STROKE +0x9c 0x00a3 #POUND SIGN +0x9d 0x00d8 #LATIN CAPITAL LETTER O WITH STROKE +0x9e 0x00d7 #MULTIPLICATION SIGN +0x9f 0x0192 #LATIN SMALL LETTER F WITH HOOK +0xa0 0x00e1 #LATIN SMALL LETTER A WITH ACUTE +0xa1 0x00ed #LATIN SMALL LETTER I WITH ACUTE +0xa2 0x00f3 #LATIN SMALL LETTER O WITH ACUTE +0xa3 0x00fa #LATIN SMALL LETTER U WITH ACUTE +0xa4 0x00f1 #LATIN SMALL LETTER N WITH TILDE +0xa5 0x00d1 #LATIN CAPITAL LETTER N WITH TILDE +0xa6 0x00aa #FEMININE ORDINAL INDICATOR +0xa7 0x00ba #MASCULINE ORDINAL INDICATOR +0xa8 0x00bf #INVERTED QUESTION MARK +0xa9 0x00ae #REGISTERED SIGN +0xaa 0x00ac #NOT SIGN +0xab 0x00bd #VULGAR FRACTION ONE HALF +0xac 0x00bc #VULGAR FRACTION ONE QUARTER +0xad 0x00a1 #INVERTED EXCLAMATION MARK +0xae 0x00ab #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xaf 0x00bb #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xb0 0x2591 #LIGHT SHADE +0xb1 0x2592 #MEDIUM SHADE +0xb2 0x2593 #DARK SHADE +0xb3 0x2502 #BOX DRAWINGS LIGHT VERTICAL +0xb4 0x2524 #BOX DRAWINGS LIGHT VERTICAL AND LEFT +0xb5 0x00c1 #LATIN CAPITAL LETTER A WITH ACUTE +0xb6 0x00c2 #LATIN CAPITAL LETTER A WITH CIRCUMFLEX +0xb7 0x00c0 #LATIN CAPITAL LETTER A WITH GRAVE +0xb8 0x00a9 #COPYRIGHT SIGN +0xb9 0x2563 #BOX DRAWINGS DOUBLE VERTICAL AND LEFT +0xba 0x2551 #BOX DRAWINGS DOUBLE VERTICAL +0xbb 0x2557 #BOX DRAWINGS DOUBLE DOWN AND LEFT +0xbc 0x255d #BOX DRAWINGS DOUBLE UP AND LEFT +0xbd 0x00a2 #CENT SIGN +0xbe 0x00a5 #YEN SIGN +0xbf 0x2510 #BOX DRAWINGS LIGHT DOWN AND LEFT +0xc0 0x2514 #BOX DRAWINGS LIGHT UP AND RIGHT +0xc1 0x2534 #BOX DRAWINGS LIGHT UP AND HORIZONTAL +0xc2 0x252c #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL +0xc3 0x251c #BOX DRAWINGS LIGHT VERTICAL AND RIGHT +0xc4 0x2500 #BOX DRAWINGS LIGHT HORIZONTAL +0xc5 0x253c #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL +0xc6 0x00e3 #LATIN SMALL LETTER A WITH TILDE +0xc7 0x00c3 #LATIN CAPITAL LETTER A WITH TILDE +0xc8 0x255a #BOX DRAWINGS DOUBLE UP AND RIGHT +0xc9 0x2554 #BOX DRAWINGS DOUBLE DOWN AND RIGHT +0xca 0x2569 #BOX DRAWINGS DOUBLE UP AND HORIZONTAL +0xcb 0x2566 #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL +0xcc 0x2560 #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT +0xcd 0x2550 #BOX DRAWINGS DOUBLE HORIZONTAL +0xce 0x256c #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL +0xcf 0x00a4 #CURRENCY SIGN +0xd0 0x00f0 #LATIN SMALL LETTER ETH +0xd1 0x00d0 #LATIN CAPITAL LETTER ETH +0xd2 0x00ca #LATIN CAPITAL LETTER E WITH CIRCUMFLEX +0xd3 0x00cb #LATIN CAPITAL LETTER E WITH DIAERESIS +0xd4 0x00c8 #LATIN CAPITAL LETTER E WITH GRAVE +0xd5 0x0131 #LATIN SMALL LETTER DOTLESS I +0xd6 0x00cd #LATIN CAPITAL LETTER I WITH ACUTE +0xd7 0x00ce #LATIN CAPITAL LETTER I WITH CIRCUMFLEX +0xd8 0x00cf #LATIN CAPITAL LETTER I WITH DIAERESIS +0xd9 0x2518 #BOX DRAWINGS LIGHT UP AND LEFT +0xda 0x250c #BOX DRAWINGS LIGHT DOWN AND RIGHT +0xdb 0x2588 #FULL BLOCK +0xdc 0x2584 #LOWER HALF BLOCK +0xdd 0x00a6 #BROKEN BAR +0xde 0x00cc #LATIN CAPITAL LETTER I WITH GRAVE +0xdf 0x2580 #UPPER HALF BLOCK +0xe0 0x00d3 #LATIN CAPITAL LETTER O WITH ACUTE +0xe1 0x00df #LATIN SMALL LETTER SHARP S +0xe2 0x00d4 #LATIN CAPITAL LETTER O WITH CIRCUMFLEX +0xe3 0x00d2 #LATIN CAPITAL LETTER O WITH GRAVE +0xe4 0x00f5 #LATIN SMALL LETTER O WITH TILDE +0xe5 0x00d5 #LATIN CAPITAL LETTER O WITH TILDE +0xe6 0x00b5 #MICRO SIGN +0xe7 0x00fe #LATIN SMALL LETTER THORN +0xe8 0x00de #LATIN CAPITAL LETTER THORN +0xe9 0x00da #LATIN CAPITAL LETTER U WITH ACUTE +0xea 0x00db #LATIN CAPITAL LETTER U WITH CIRCUMFLEX +0xeb 0x00d9 #LATIN CAPITAL LETTER U WITH GRAVE +0xec 0x00fd #LATIN SMALL LETTER Y WITH ACUTE +0xed 0x00dd #LATIN CAPITAL LETTER Y WITH ACUTE +0xee 0x00af #MACRON +0xef 0x00b4 #ACUTE ACCENT +0xf0 0x00ad #SOFT HYPHEN +0xf1 0x00b1 #PLUS-MINUS SIGN +0xf2 0x2017 #DOUBLE LOW LINE +0xf3 0x00be #VULGAR FRACTION THREE QUARTERS +0xf4 0x00b6 #PILCROW SIGN +0xf5 0x00a7 #SECTION SIGN +0xf6 0x00f7 #DIVISION SIGN +0xf7 0x00b8 #CEDILLA +0xf8 0x00b0 #DEGREE SIGN +0xf9 0x00a8 #DIAERESIS +0xfa 0x00b7 #MIDDLE DOT +0xfb 0x00b9 #SUPERSCRIPT ONE +0xfc 0x00b3 #SUPERSCRIPT THREE +0xfd 0x00b2 #SUPERSCRIPT TWO +0xfe 0x25a0 #BLACK SQUARE +0xff 0x00a0 #NO-BREAK SPACE + \ No newline at end of file diff --git a/codepage/cp852.txt b/codepage/cp852.txt new file mode 100644 index 0000000..c5f3564 --- /dev/null +++ b/codepage/cp852.txt @@ -0,0 +1,274 @@ +# +# Name: cp852_DOSLatin2 to Unicode table +# Unicode version: 2.0 +# Table version: 2.00 +# Table format: Format A +# Date: 04/24/96 +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp852_DOSLatin2 code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp852_DOSLatin2 order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0a 0x000a #LINE FEED +0x0b 0x000b #VERTICAL TABULATION +0x0c 0x000c #FORM FEED +0x0d 0x000d #CARRIAGE RETURN +0x0e 0x000e #SHIFT OUT +0x0f 0x000f #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1a 0x001a #SUBSTITUTE +0x1b 0x001b #ESCAPE +0x1c 0x001c #FILE SEPARATOR +0x1d 0x001d #GROUP SEPARATOR +0x1e 0x001e #RECORD SEPARATOR +0x1f 0x001f #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2a 0x002a #ASTERISK +0x2b 0x002b #PLUS SIGN +0x2c 0x002c #COMMA +0x2d 0x002d #HYPHEN-MINUS +0x2e 0x002e #FULL STOP +0x2f 0x002f #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3a 0x003a #COLON +0x3b 0x003b #SEMICOLON +0x3c 0x003c #LESS-THAN SIGN +0x3d 0x003d #EQUALS SIGN +0x3e 0x003e #GREATER-THAN SIGN +0x3f 0x003f #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4a 0x004a #LATIN CAPITAL LETTER J +0x4b 0x004b #LATIN CAPITAL LETTER K +0x4c 0x004c #LATIN CAPITAL LETTER L +0x4d 0x004d #LATIN CAPITAL LETTER M +0x4e 0x004e #LATIN CAPITAL LETTER N +0x4f 0x004f #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5a 0x005a #LATIN CAPITAL LETTER Z +0x5b 0x005b #LEFT SQUARE BRACKET +0x5c 0x005c #REVERSE SOLIDUS +0x5d 0x005d #RIGHT SQUARE BRACKET +0x5e 0x005e #CIRCUMFLEX ACCENT +0x5f 0x005f #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6a 0x006a #LATIN SMALL LETTER J +0x6b 0x006b #LATIN SMALL LETTER K +0x6c 0x006c #LATIN SMALL LETTER L +0x6d 0x006d #LATIN SMALL LETTER M +0x6e 0x006e #LATIN SMALL LETTER N +0x6f 0x006f #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7a 0x007a #LATIN SMALL LETTER Z +0x7b 0x007b #LEFT CURLY BRACKET +0x7c 0x007c #VERTICAL LINE +0x7d 0x007d #RIGHT CURLY BRACKET +0x7e 0x007e #TILDE +0x7f 0x007f #DELETE +0x80 0x00c7 #LATIN CAPITAL LETTER C WITH CEDILLA +0x81 0x00fc #LATIN SMALL LETTER U WITH DIAERESIS +0x82 0x00e9 #LATIN SMALL LETTER E WITH ACUTE +0x83 0x00e2 #LATIN SMALL LETTER A WITH CIRCUMFLEX +0x84 0x00e4 #LATIN SMALL LETTER A WITH DIAERESIS +0x85 0x016f #LATIN SMALL LETTER U WITH RING ABOVE +0x86 0x0107 #LATIN SMALL LETTER C WITH ACUTE +0x87 0x00e7 #LATIN SMALL LETTER C WITH CEDILLA +0x88 0x0142 #LATIN SMALL LETTER L WITH STROKE +0x89 0x00eb #LATIN SMALL LETTER E WITH DIAERESIS +0x8a 0x0150 #LATIN CAPITAL LETTER O WITH DOUBLE ACUTE +0x8b 0x0151 #LATIN SMALL LETTER O WITH DOUBLE ACUTE +0x8c 0x00ee #LATIN SMALL LETTER I WITH CIRCUMFLEX +0x8d 0x0179 #LATIN CAPITAL LETTER Z WITH ACUTE +0x8e 0x00c4 #LATIN CAPITAL LETTER A WITH DIAERESIS +0x8f 0x0106 #LATIN CAPITAL LETTER C WITH ACUTE +0x90 0x00c9 #LATIN CAPITAL LETTER E WITH ACUTE +0x91 0x0139 #LATIN CAPITAL LETTER L WITH ACUTE +0x92 0x013a #LATIN SMALL LETTER L WITH ACUTE +0x93 0x00f4 #LATIN SMALL LETTER O WITH CIRCUMFLEX +0x94 0x00f6 #LATIN SMALL LETTER O WITH DIAERESIS +0x95 0x013d #LATIN CAPITAL LETTER L WITH CARON +0x96 0x013e #LATIN SMALL LETTER L WITH CARON +0x97 0x015a #LATIN CAPITAL LETTER S WITH ACUTE +0x98 0x015b #LATIN SMALL LETTER S WITH ACUTE +0x99 0x00d6 #LATIN CAPITAL LETTER O WITH DIAERESIS +0x9a 0x00dc #LATIN CAPITAL LETTER U WITH DIAERESIS +0x9b 0x0164 #LATIN CAPITAL LETTER T WITH CARON +0x9c 0x0165 #LATIN SMALL LETTER T WITH CARON +0x9d 0x0141 #LATIN CAPITAL LETTER L WITH STROKE +0x9e 0x00d7 #MULTIPLICATION SIGN +0x9f 0x010d #LATIN SMALL LETTER C WITH CARON +0xa0 0x00e1 #LATIN SMALL LETTER A WITH ACUTE +0xa1 0x00ed #LATIN SMALL LETTER I WITH ACUTE +0xa2 0x00f3 #LATIN SMALL LETTER O WITH ACUTE +0xa3 0x00fa #LATIN SMALL LETTER U WITH ACUTE +0xa4 0x0104 #LATIN CAPITAL LETTER A WITH OGONEK +0xa5 0x0105 #LATIN SMALL LETTER A WITH OGONEK +0xa6 0x017d #LATIN CAPITAL LETTER Z WITH CARON +0xa7 0x017e #LATIN SMALL LETTER Z WITH CARON +0xa8 0x0118 #LATIN CAPITAL LETTER E WITH OGONEK +0xa9 0x0119 #LATIN SMALL LETTER E WITH OGONEK +0xaa 0x00ac #NOT SIGN +0xab 0x017a #LATIN SMALL LETTER Z WITH ACUTE +0xac 0x010c #LATIN CAPITAL LETTER C WITH CARON +0xad 0x015f #LATIN SMALL LETTER S WITH CEDILLA +0xae 0x00ab #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xaf 0x00bb #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xb0 0x2591 #LIGHT SHADE +0xb1 0x2592 #MEDIUM SHADE +0xb2 0x2593 #DARK SHADE +0xb3 0x2502 #BOX DRAWINGS LIGHT VERTICAL +0xb4 0x2524 #BOX DRAWINGS LIGHT VERTICAL AND LEFT +0xb5 0x00c1 #LATIN CAPITAL LETTER A WITH ACUTE +0xb6 0x00c2 #LATIN CAPITAL LETTER A WITH CIRCUMFLEX +0xb7 0x011a #LATIN CAPITAL LETTER E WITH CARON +0xb8 0x015e #LATIN CAPITAL LETTER S WITH CEDILLA +0xb9 0x2563 #BOX DRAWINGS DOUBLE VERTICAL AND LEFT +0xba 0x2551 #BOX DRAWINGS DOUBLE VERTICAL +0xbb 0x2557 #BOX DRAWINGS DOUBLE DOWN AND LEFT +0xbc 0x255d #BOX DRAWINGS DOUBLE UP AND LEFT +0xbd 0x017b #LATIN CAPITAL LETTER Z WITH DOT ABOVE +0xbe 0x017c #LATIN SMALL LETTER Z WITH DOT ABOVE +0xbf 0x2510 #BOX DRAWINGS LIGHT DOWN AND LEFT +0xc0 0x2514 #BOX DRAWINGS LIGHT UP AND RIGHT +0xc1 0x2534 #BOX DRAWINGS LIGHT UP AND HORIZONTAL +0xc2 0x252c #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL +0xc3 0x251c #BOX DRAWINGS LIGHT VERTICAL AND RIGHT +0xc4 0x2500 #BOX DRAWINGS LIGHT HORIZONTAL +0xc5 0x253c #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL +0xc6 0x0102 #LATIN CAPITAL LETTER A WITH BREVE +0xc7 0x0103 #LATIN SMALL LETTER A WITH BREVE +0xc8 0x255a #BOX DRAWINGS DOUBLE UP AND RIGHT +0xc9 0x2554 #BOX DRAWINGS DOUBLE DOWN AND RIGHT +0xca 0x2569 #BOX DRAWINGS DOUBLE UP AND HORIZONTAL +0xcb 0x2566 #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL +0xcc 0x2560 #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT +0xcd 0x2550 #BOX DRAWINGS DOUBLE HORIZONTAL +0xce 0x256c #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL +0xcf 0x00a4 #CURRENCY SIGN +0xd0 0x0111 #LATIN SMALL LETTER D WITH STROKE +0xd1 0x0110 #LATIN CAPITAL LETTER D WITH STROKE +0xd2 0x010e #LATIN CAPITAL LETTER D WITH CARON +0xd3 0x00cb #LATIN CAPITAL LETTER E WITH DIAERESIS +0xd4 0x010f #LATIN SMALL LETTER D WITH CARON +0xd5 0x0147 #LATIN CAPITAL LETTER N WITH CARON +0xd6 0x00cd #LATIN CAPITAL LETTER I WITH ACUTE +0xd7 0x00ce #LATIN CAPITAL LETTER I WITH CIRCUMFLEX +0xd8 0x011b #LATIN SMALL LETTER E WITH CARON +0xd9 0x2518 #BOX DRAWINGS LIGHT UP AND LEFT +0xda 0x250c #BOX DRAWINGS LIGHT DOWN AND RIGHT +0xdb 0x2588 #FULL BLOCK +0xdc 0x2584 #LOWER HALF BLOCK +0xdd 0x0162 #LATIN CAPITAL LETTER T WITH CEDILLA +0xde 0x016e #LATIN CAPITAL LETTER U WITH RING ABOVE +0xdf 0x2580 #UPPER HALF BLOCK +0xe0 0x00d3 #LATIN CAPITAL LETTER O WITH ACUTE +0xe1 0x00df #LATIN SMALL LETTER SHARP S +0xe2 0x00d4 #LATIN CAPITAL LETTER O WITH CIRCUMFLEX +0xe3 0x0143 #LATIN CAPITAL LETTER N WITH ACUTE +0xe4 0x0144 #LATIN SMALL LETTER N WITH ACUTE +0xe5 0x0148 #LATIN SMALL LETTER N WITH CARON +0xe6 0x0160 #LATIN CAPITAL LETTER S WITH CARON +0xe7 0x0161 #LATIN SMALL LETTER S WITH CARON +0xe8 0x0154 #LATIN CAPITAL LETTER R WITH ACUTE +0xe9 0x00da #LATIN CAPITAL LETTER U WITH ACUTE +0xea 0x0155 #LATIN SMALL LETTER R WITH ACUTE +0xeb 0x0170 #LATIN CAPITAL LETTER U WITH DOUBLE ACUTE +0xec 0x00fd #LATIN SMALL LETTER Y WITH ACUTE +0xed 0x00dd #LATIN CAPITAL LETTER Y WITH ACUTE +0xee 0x0163 #LATIN SMALL LETTER T WITH CEDILLA +0xef 0x00b4 #ACUTE ACCENT +0xf0 0x00ad #SOFT HYPHEN +0xf1 0x02dd #DOUBLE ACUTE ACCENT +0xf2 0x02db #OGONEK +0xf3 0x02c7 #CARON +0xf4 0x02d8 #BREVE +0xf5 0x00a7 #SECTION SIGN +0xf6 0x00f7 #DIVISION SIGN +0xf7 0x00b8 #CEDILLA +0xf8 0x00b0 #DEGREE SIGN +0xf9 0x00a8 #DIAERESIS +0xfa 0x02d9 #DOT ABOVE +0xfb 0x0171 #LATIN SMALL LETTER U WITH DOUBLE ACUTE +0xfc 0x0158 #LATIN CAPITAL LETTER R WITH CARON +0xfd 0x0159 #LATIN SMALL LETTER R WITH CARON +0xfe 0x25a0 #BLACK SQUARE +0xff 0x00a0 #NO-BREAK SPACE + \ No newline at end of file diff --git a/codepage/cp855.txt b/codepage/cp855.txt new file mode 100644 index 0000000..42063d8 --- /dev/null +++ b/codepage/cp855.txt @@ -0,0 +1,275 @@ +# +# Name: cp855_DOSCyrillic to Unicode table +# Unicode version: 2.0 +# Table version: 2.00 +# Table format: Format A +# Date: 04/24/96 +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp855_DOSCyrillic code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp855_DOSCyrillic order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0a 0x000a #LINE FEED +0x0b 0x000b #VERTICAL TABULATION +0x0c 0x000c #FORM FEED +0x0d 0x000d #CARRIAGE RETURN +0x0e 0x000e #SHIFT OUT +0x0f 0x000f #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1a 0x001a #SUBSTITUTE +0x1b 0x001b #ESCAPE +0x1c 0x001c #FILE SEPARATOR +0x1d 0x001d #GROUP SEPARATOR +0x1e 0x001e #RECORD SEPARATOR +0x1f 0x001f #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2a 0x002a #ASTERISK +0x2b 0x002b #PLUS SIGN +0x2c 0x002c #COMMA +0x2d 0x002d #HYPHEN-MINUS +0x2e 0x002e #FULL STOP +0x2f 0x002f #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3a 0x003a #COLON +0x3b 0x003b #SEMICOLON +0x3c 0x003c #LESS-THAN SIGN +0x3d 0x003d #EQUALS SIGN +0x3e 0x003e #GREATER-THAN SIGN +0x3f 0x003f #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4a 0x004a #LATIN CAPITAL LETTER J +0x4b 0x004b #LATIN CAPITAL LETTER K +0x4c 0x004c #LATIN CAPITAL LETTER L +0x4d 0x004d #LATIN CAPITAL LETTER M +0x4e 0x004e #LATIN CAPITAL LETTER N +0x4f 0x004f #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5a 0x005a #LATIN CAPITAL LETTER Z +0x5b 0x005b #LEFT SQUARE BRACKET +0x5c 0x005c #REVERSE SOLIDUS +0x5d 0x005d #RIGHT SQUARE BRACKET +0x5e 0x005e #CIRCUMFLEX ACCENT +0x5f 0x005f #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6a 0x006a #LATIN SMALL LETTER J +0x6b 0x006b #LATIN SMALL LETTER K +0x6c 0x006c #LATIN SMALL LETTER L +0x6d 0x006d #LATIN SMALL LETTER M +0x6e 0x006e #LATIN SMALL LETTER N +0x6f 0x006f #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7a 0x007a #LATIN SMALL LETTER Z +0x7b 0x007b #LEFT CURLY BRACKET +0x7c 0x007c #VERTICAL LINE +0x7d 0x007d #RIGHT CURLY BRACKET +0x7e 0x007e #TILDE +0x7f 0x007f #DELETE +0x80 0x0452 #CYRILLIC SMALL LETTER DJE +0x81 0x0402 #CYRILLIC CAPITAL LETTER DJE +0x82 0x0453 #CYRILLIC SMALL LETTER GJE +0x83 0x0403 #CYRILLIC CAPITAL LETTER GJE +0x84 0x0451 #CYRILLIC SMALL LETTER IO +0x85 0x0401 #CYRILLIC CAPITAL LETTER IO +0x86 0x0454 #CYRILLIC SMALL LETTER UKRAINIAN IE +0x87 0x0404 #CYRILLIC CAPITAL LETTER UKRAINIAN IE +0x88 0x0455 #CYRILLIC SMALL LETTER DZE +0x89 0x0405 #CYRILLIC CAPITAL LETTER DZE +0x8a 0x0456 #CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I +0x8b 0x0406 #CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I +0x8c 0x0457 #CYRILLIC SMALL LETTER YI +0x8d 0x0407 #CYRILLIC CAPITAL LETTER YI +0x8e 0x0458 #CYRILLIC SMALL LETTER JE +0x8f 0x0408 #CYRILLIC CAPITAL LETTER JE +0x90 0x0459 #CYRILLIC SMALL LETTER LJE +0x91 0x0409 #CYRILLIC CAPITAL LETTER LJE +0x92 0x045a #CYRILLIC SMALL LETTER NJE +0x93 0x040a #CYRILLIC CAPITAL LETTER NJE +0x94 0x045b #CYRILLIC SMALL LETTER TSHE +0x95 0x040b #CYRILLIC CAPITAL LETTER TSHE +0x96 0x045c #CYRILLIC SMALL LETTER KJE +0x97 0x040c #CYRILLIC CAPITAL LETTER KJE +0x98 0x045e #CYRILLIC SMALL LETTER SHORT U +0x99 0x040e #CYRILLIC CAPITAL LETTER SHORT U +0x9a 0x045f #CYRILLIC SMALL LETTER DZHE +0x9b 0x040f #CYRILLIC CAPITAL LETTER DZHE +0x9c 0x044e #CYRILLIC SMALL LETTER YU +0x9d 0x042e #CYRILLIC CAPITAL LETTER YU +0x9e 0x044a #CYRILLIC SMALL LETTER HARD SIGN +0x9f 0x042a #CYRILLIC CAPITAL LETTER HARD SIGN +0xa0 0x0430 #CYRILLIC SMALL LETTER A +0xa1 0x0410 #CYRILLIC CAPITAL LETTER A +0xa2 0x0431 #CYRILLIC SMALL LETTER BE +0xa3 0x0411 #CYRILLIC CAPITAL LETTER BE +0xa4 0x0446 #CYRILLIC SMALL LETTER TSE +0xa5 0x0426 #CYRILLIC CAPITAL LETTER TSE +0xa6 0x0434 #CYRILLIC SMALL LETTER DE +0xa7 0x0414 #CYRILLIC CAPITAL LETTER DE +0xa8 0x0435 #CYRILLIC SMALL LETTER IE +0xa9 0x0415 #CYRILLIC CAPITAL LETTER IE +0xaa 0x0444 #CYRILLIC SMALL LETTER EF +0xab 0x0424 #CYRILLIC CAPITAL LETTER EF +0xac 0x0433 #CYRILLIC SMALL LETTER GHE +0xad 0x0413 #CYRILLIC CAPITAL LETTER GHE +0xae 0x00ab #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xaf 0x00bb #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xb0 0x2591 #LIGHT SHADE +0xb1 0x2592 #MEDIUM SHADE +0xb2 0x2593 #DARK SHADE +0xb3 0x2502 #BOX DRAWINGS LIGHT VERTICAL +0xb4 0x2524 #BOX DRAWINGS LIGHT VERTICAL AND LEFT +0xb5 0x0445 #CYRILLIC SMALL LETTER HA +0xb6 0x0425 #CYRILLIC CAPITAL LETTER HA +0xb7 0x0438 #CYRILLIC SMALL LETTER I +0xb8 0x0418 #CYRILLIC CAPITAL LETTER I +0xb9 0x2563 #BOX DRAWINGS DOUBLE VERTICAL AND LEFT +0xba 0x2551 #BOX DRAWINGS DOUBLE VERTICAL +0xbb 0x2557 #BOX DRAWINGS DOUBLE DOWN AND LEFT +0xbc 0x255d #BOX DRAWINGS DOUBLE UP AND LEFT +0xbd 0x0439 #CYRILLIC SMALL LETTER SHORT I +0xbe 0x0419 #CYRILLIC CAPITAL LETTER SHORT I +0xbf 0x2510 #BOX DRAWINGS LIGHT DOWN AND LEFT +0xc0 0x2514 #BOX DRAWINGS LIGHT UP AND RIGHT +0xc1 0x2534 #BOX DRAWINGS LIGHT UP AND HORIZONTAL +0xc2 0x252c #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL +0xc3 0x251c #BOX DRAWINGS LIGHT VERTICAL AND RIGHT +0xc4 0x2500 #BOX DRAWINGS LIGHT HORIZONTAL +0xc5 0x253c #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL +0xc6 0x043a #CYRILLIC SMALL LETTER KA +0xc7 0x041a #CYRILLIC CAPITAL LETTER KA +0xc8 0x255a #BOX DRAWINGS DOUBLE UP AND RIGHT +0xc9 0x2554 #BOX DRAWINGS DOUBLE DOWN AND RIGHT +0xca 0x2569 #BOX DRAWINGS DOUBLE UP AND HORIZONTAL +0xcb 0x2566 #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL +0xcc 0x2560 #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT +0xcd 0x2550 #BOX DRAWINGS DOUBLE HORIZONTAL +0xce 0x256c #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL +0xcf 0x00a4 #CURRENCY SIGN +0xd0 0x043b #CYRILLIC SMALL LETTER EL +0xd1 0x041b #CYRILLIC CAPITAL LETTER EL +0xd2 0x043c #CYRILLIC SMALL LETTER EM +0xd3 0x041c #CYRILLIC CAPITAL LETTER EM +0xd4 0x043d #CYRILLIC SMALL LETTER EN +0xd5 0x041d #CYRILLIC CAPITAL LETTER EN +0xd6 0x043e #CYRILLIC SMALL LETTER O +0xd7 0x041e #CYRILLIC CAPITAL LETTER O +0xd8 0x043f #CYRILLIC SMALL LETTER PE +0xd9 0x2518 #BOX DRAWINGS LIGHT UP AND LEFT +0xda 0x250c #BOX DRAWINGS LIGHT DOWN AND RIGHT +0xdb 0x2588 #FULL BLOCK +0xdc 0x2584 #LOWER HALF BLOCK +0xdd 0x041f #CYRILLIC CAPITAL LETTER PE +0xde 0x044f #CYRILLIC SMALL LETTER YA +0xdf 0x2580 #UPPER HALF BLOCK +0xe0 0x042f #CYRILLIC CAPITAL LETTER YA +0xe1 0x0440 #CYRILLIC SMALL LETTER ER +0xe2 0x0420 #CYRILLIC CAPITAL LETTER ER +0xe3 0x0441 #CYRILLIC SMALL LETTER ES +0xe4 0x0421 #CYRILLIC CAPITAL LETTER ES +0xe5 0x0442 #CYRILLIC SMALL LETTER TE +0xe6 0x0422 #CYRILLIC CAPITAL LETTER TE +0xe7 0x0443 #CYRILLIC SMALL LETTER U +0xe8 0x0423 #CYRILLIC CAPITAL LETTER U +0xe9 0x0436 #CYRILLIC SMALL LETTER ZHE +0xea 0x0416 #CYRILLIC CAPITAL LETTER ZHE +0xeb 0x0432 #CYRILLIC SMALL LETTER VE +0xec 0x0412 #CYRILLIC CAPITAL LETTER VE +0xed 0x044c #CYRILLIC SMALL LETTER SOFT SIGN +0xee 0x042c #CYRILLIC CAPITAL LETTER SOFT SIGN +0xef 0x2116 #NUMERO SIGN +0xf0 0x00ad #SOFT HYPHEN +0xf1 0x044b #CYRILLIC SMALL LETTER YERU +0xf2 0x042b #CYRILLIC CAPITAL LETTER YERU +0xf3 0x0437 #CYRILLIC SMALL LETTER ZE +0xf4 0x0417 #CYRILLIC CAPITAL LETTER ZE +0xf5 0x0448 #CYRILLIC SMALL LETTER SHA +0xf6 0x0428 #CYRILLIC CAPITAL LETTER SHA +0xf7 0x044d #CYRILLIC SMALL LETTER E +0xf8 0x042d #CYRILLIC CAPITAL LETTER E +0xf9 0x0449 #CYRILLIC SMALL LETTER SHCHA +0xfa 0x0429 #CYRILLIC CAPITAL LETTER SHCHA +0xfb 0x0447 #CYRILLIC SMALL LETTER CHE +0xfc 0x0427 #CYRILLIC CAPITAL LETTER CHE +0xfd 0x00a7 #SECTION SIGN +0xfe 0x25a0 #BLACK SQUARE +0xff 0x00a0 #NO-BREAK SPACE + + \ No newline at end of file diff --git a/codepage/cp857.txt b/codepage/cp857.txt new file mode 100644 index 0000000..5995085 --- /dev/null +++ b/codepage/cp857.txt @@ -0,0 +1,275 @@ +# +# Name: cp857_DOSTurkish to Unicode table +# Unicode version: 2.0 +# Table version: 2.00 +# Table format: Format A +# Date: 04/24/96 +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp857_DOSTurkish code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp857_DOSTurkish order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0a 0x000a #LINE FEED +0x0b 0x000b #VERTICAL TABULATION +0x0c 0x000c #FORM FEED +0x0d 0x000d #CARRIAGE RETURN +0x0e 0x000e #SHIFT OUT +0x0f 0x000f #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1a 0x001a #SUBSTITUTE +0x1b 0x001b #ESCAPE +0x1c 0x001c #FILE SEPARATOR +0x1d 0x001d #GROUP SEPARATOR +0x1e 0x001e #RECORD SEPARATOR +0x1f 0x001f #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2a 0x002a #ASTERISK +0x2b 0x002b #PLUS SIGN +0x2c 0x002c #COMMA +0x2d 0x002d #HYPHEN-MINUS +0x2e 0x002e #FULL STOP +0x2f 0x002f #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3a 0x003a #COLON +0x3b 0x003b #SEMICOLON +0x3c 0x003c #LESS-THAN SIGN +0x3d 0x003d #EQUALS SIGN +0x3e 0x003e #GREATER-THAN SIGN +0x3f 0x003f #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4a 0x004a #LATIN CAPITAL LETTER J +0x4b 0x004b #LATIN CAPITAL LETTER K +0x4c 0x004c #LATIN CAPITAL LETTER L +0x4d 0x004d #LATIN CAPITAL LETTER M +0x4e 0x004e #LATIN CAPITAL LETTER N +0x4f 0x004f #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5a 0x005a #LATIN CAPITAL LETTER Z +0x5b 0x005b #LEFT SQUARE BRACKET +0x5c 0x005c #REVERSE SOLIDUS +0x5d 0x005d #RIGHT SQUARE BRACKET +0x5e 0x005e #CIRCUMFLEX ACCENT +0x5f 0x005f #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6a 0x006a #LATIN SMALL LETTER J +0x6b 0x006b #LATIN SMALL LETTER K +0x6c 0x006c #LATIN SMALL LETTER L +0x6d 0x006d #LATIN SMALL LETTER M +0x6e 0x006e #LATIN SMALL LETTER N +0x6f 0x006f #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7a 0x007a #LATIN SMALL LETTER Z +0x7b 0x007b #LEFT CURLY BRACKET +0x7c 0x007c #VERTICAL LINE +0x7d 0x007d #RIGHT CURLY BRACKET +0x7e 0x007e #TILDE +0x7f 0x007f #DELETE +0x80 0x00c7 #LATIN CAPITAL LETTER C WITH CEDILLA +0x81 0x00fc #LATIN SMALL LETTER U WITH DIAERESIS +0x82 0x00e9 #LATIN SMALL LETTER E WITH ACUTE +0x83 0x00e2 #LATIN SMALL LETTER A WITH CIRCUMFLEX +0x84 0x00e4 #LATIN SMALL LETTER A WITH DIAERESIS +0x85 0x00e0 #LATIN SMALL LETTER A WITH GRAVE +0x86 0x00e5 #LATIN SMALL LETTER A WITH RING ABOVE +0x87 0x00e7 #LATIN SMALL LETTER C WITH CEDILLA +0x88 0x00ea #LATIN SMALL LETTER E WITH CIRCUMFLEX +0x89 0x00eb #LATIN SMALL LETTER E WITH DIAERESIS +0x8a 0x00e8 #LATIN SMALL LETTER E WITH GRAVE +0x8b 0x00ef #LATIN SMALL LETTER I WITH DIAERESIS +0x8c 0x00ee #LATIN SMALL LETTER I WITH CIRCUMFLEX +0x8d 0x0131 #LATIN SMALL LETTER DOTLESS I +0x8e 0x00c4 #LATIN CAPITAL LETTER A WITH DIAERESIS +0x8f 0x00c5 #LATIN CAPITAL LETTER A WITH RING ABOVE +0x90 0x00c9 #LATIN CAPITAL LETTER E WITH ACUTE +0x91 0x00e6 #LATIN SMALL LIGATURE AE +0x92 0x00c6 #LATIN CAPITAL LIGATURE AE +0x93 0x00f4 #LATIN SMALL LETTER O WITH CIRCUMFLEX +0x94 0x00f6 #LATIN SMALL LETTER O WITH DIAERESIS +0x95 0x00f2 #LATIN SMALL LETTER O WITH GRAVE +0x96 0x00fb #LATIN SMALL LETTER U WITH CIRCUMFLEX +0x97 0x00f9 #LATIN SMALL LETTER U WITH GRAVE +0x98 0x0130 #LATIN CAPITAL LETTER I WITH DOT ABOVE +0x99 0x00d6 #LATIN CAPITAL LETTER O WITH DIAERESIS +0x9a 0x00dc #LATIN CAPITAL LETTER U WITH DIAERESIS +0x9b 0x00f8 #LATIN SMALL LETTER O WITH STROKE +0x9c 0x00a3 #POUND SIGN +0x9d 0x00d8 #LATIN CAPITAL LETTER O WITH STROKE +0x9e 0x015e #LATIN CAPITAL LETTER S WITH CEDILLA +0x9f 0x015f #LATIN SMALL LETTER S WITH CEDILLA +0xa0 0x00e1 #LATIN SMALL LETTER A WITH ACUTE +0xa1 0x00ed #LATIN SMALL LETTER I WITH ACUTE +0xa2 0x00f3 #LATIN SMALL LETTER O WITH ACUTE +0xa3 0x00fa #LATIN SMALL LETTER U WITH ACUTE +0xa4 0x00f1 #LATIN SMALL LETTER N WITH TILDE +0xa5 0x00d1 #LATIN CAPITAL LETTER N WITH TILDE +0xa6 0x011e #LATIN CAPITAL LETTER G WITH BREVE +0xa7 0x011f #LATIN SMALL LETTER G WITH BREVE +0xa8 0x00bf #INVERTED QUESTION MARK +0xa9 0x00ae #REGISTERED SIGN +0xaa 0x00ac #NOT SIGN +0xab 0x00bd #VULGAR FRACTION ONE HALF +0xac 0x00bc #VULGAR FRACTION ONE QUARTER +0xad 0x00a1 #INVERTED EXCLAMATION MARK +0xae 0x00ab #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xaf 0x00bb #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xb0 0x2591 #LIGHT SHADE +0xb1 0x2592 #MEDIUM SHADE +0xb2 0x2593 #DARK SHADE +0xb3 0x2502 #BOX DRAWINGS LIGHT VERTICAL +0xb4 0x2524 #BOX DRAWINGS LIGHT VERTICAL AND LEFT +0xb5 0x00c1 #LATIN CAPITAL LETTER A WITH ACUTE +0xb6 0x00c2 #LATIN CAPITAL LETTER A WITH CIRCUMFLEX +0xb7 0x00c0 #LATIN CAPITAL LETTER A WITH GRAVE +0xb8 0x00a9 #COPYRIGHT SIGN +0xb9 0x2563 #BOX DRAWINGS DOUBLE VERTICAL AND LEFT +0xba 0x2551 #BOX DRAWINGS DOUBLE VERTICAL +0xbb 0x2557 #BOX DRAWINGS DOUBLE DOWN AND LEFT +0xbc 0x255d #BOX DRAWINGS DOUBLE UP AND LEFT +0xbd 0x00a2 #CENT SIGN +0xbe 0x00a5 #YEN SIGN +0xbf 0x2510 #BOX DRAWINGS LIGHT DOWN AND LEFT +0xc0 0x2514 #BOX DRAWINGS LIGHT UP AND RIGHT +0xc1 0x2534 #BOX DRAWINGS LIGHT UP AND HORIZONTAL +0xc2 0x252c #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL +0xc3 0x251c #BOX DRAWINGS LIGHT VERTICAL AND RIGHT +0xc4 0x2500 #BOX DRAWINGS LIGHT HORIZONTAL +0xc5 0x253c #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL +0xc6 0x00e3 #LATIN SMALL LETTER A WITH TILDE +0xc7 0x00c3 #LATIN CAPITAL LETTER A WITH TILDE +0xc8 0x255a #BOX DRAWINGS DOUBLE UP AND RIGHT +0xc9 0x2554 #BOX DRAWINGS DOUBLE DOWN AND RIGHT +0xca 0x2569 #BOX DRAWINGS DOUBLE UP AND HORIZONTAL +0xcb 0x2566 #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL +0xcc 0x2560 #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT +0xcd 0x2550 #BOX DRAWINGS DOUBLE HORIZONTAL +0xce 0x256c #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL +0xcf 0x00a4 #CURRENCY SIGN +0xd0 0x00ba #MASCULINE ORDINAL INDICATOR +0xd1 0x00aa #FEMININE ORDINAL INDICATOR +0xd2 0x00ca #LATIN CAPITAL LETTER E WITH CIRCUMFLEX +0xd3 0x00cb #LATIN CAPITAL LETTER E WITH DIAERESIS +0xd4 0x00c8 #LATIN CAPITAL LETTER E WITH GRAVE +0xd5 #UNDEFINED +0xd6 0x00cd #LATIN CAPITAL LETTER I WITH ACUTE +0xd7 0x00ce #LATIN CAPITAL LETTER I WITH CIRCUMFLEX +0xd8 0x00cf #LATIN CAPITAL LETTER I WITH DIAERESIS +0xd9 0x2518 #BOX DRAWINGS LIGHT UP AND LEFT +0xda 0x250c #BOX DRAWINGS LIGHT DOWN AND RIGHT +0xdb 0x2588 #FULL BLOCK +0xdc 0x2584 #LOWER HALF BLOCK +0xdd 0x00a6 #BROKEN BAR +0xde 0x00cc #LATIN CAPITAL LETTER I WITH GRAVE +0xdf 0x2580 #UPPER HALF BLOCK +0xe0 0x00d3 #LATIN CAPITAL LETTER O WITH ACUTE +0xe1 0x00df #LATIN SMALL LETTER SHARP S +0xe2 0x00d4 #LATIN CAPITAL LETTER O WITH CIRCUMFLEX +0xe3 0x00d2 #LATIN CAPITAL LETTER O WITH GRAVE +0xe4 0x00f5 #LATIN SMALL LETTER O WITH TILDE +0xe5 0x00d5 #LATIN CAPITAL LETTER O WITH TILDE +0xe6 0x00b5 #MICRO SIGN +0xe7 #UNDEFINED +0xe8 0x00d7 #MULTIPLICATION SIGN +0xe9 0x00da #LATIN CAPITAL LETTER U WITH ACUTE +0xea 0x00db #LATIN CAPITAL LETTER U WITH CIRCUMFLEX +0xeb 0x00d9 #LATIN CAPITAL LETTER U WITH GRAVE +0xec 0x00ec #LATIN SMALL LETTER I WITH GRAVE +0xed 0x00ff #LATIN SMALL LETTER Y WITH DIAERESIS +0xee 0x00af #MACRON +0xef 0x00b4 #ACUTE ACCENT +0xf0 0x00ad #SOFT HYPHEN +0xf1 0x00b1 #PLUS-MINUS SIGN +0xf2 #UNDEFINED +0xf3 0x00be #VULGAR FRACTION THREE QUARTERS +0xf4 0x00b6 #PILCROW SIGN +0xf5 0x00a7 #SECTION SIGN +0xf6 0x00f7 #DIVISION SIGN +0xf7 0x00b8 #CEDILLA +0xf8 0x00b0 #DEGREE SIGN +0xf9 0x00a8 #DIAERESIS +0xfa 0x00b7 #MIDDLE DOT +0xfb 0x00b9 #SUPERSCRIPT ONE +0xfc 0x00b3 #SUPERSCRIPT THREE +0xfd 0x00b2 #SUPERSCRIPT TWO +0xfe 0x25a0 #BLACK SQUARE +0xff 0x00a0 #NO-BREAK SPACE + + \ No newline at end of file diff --git a/codepage/cp860.txt b/codepage/cp860.txt new file mode 100644 index 0000000..64e9378 --- /dev/null +++ b/codepage/cp860.txt @@ -0,0 +1,275 @@ +# +# Name: cp860_DOSPortuguese to Unicode table +# Unicode version: 2.0 +# Table version: 2.00 +# Table format: Format A +# Date: 04/24/96 +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp860_DOSPortuguese code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp860_DOSPortuguese order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0a 0x000a #LINE FEED +0x0b 0x000b #VERTICAL TABULATION +0x0c 0x000c #FORM FEED +0x0d 0x000d #CARRIAGE RETURN +0x0e 0x000e #SHIFT OUT +0x0f 0x000f #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1a 0x001a #SUBSTITUTE +0x1b 0x001b #ESCAPE +0x1c 0x001c #FILE SEPARATOR +0x1d 0x001d #GROUP SEPARATOR +0x1e 0x001e #RECORD SEPARATOR +0x1f 0x001f #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2a 0x002a #ASTERISK +0x2b 0x002b #PLUS SIGN +0x2c 0x002c #COMMA +0x2d 0x002d #HYPHEN-MINUS +0x2e 0x002e #FULL STOP +0x2f 0x002f #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3a 0x003a #COLON +0x3b 0x003b #SEMICOLON +0x3c 0x003c #LESS-THAN SIGN +0x3d 0x003d #EQUALS SIGN +0x3e 0x003e #GREATER-THAN SIGN +0x3f 0x003f #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4a 0x004a #LATIN CAPITAL LETTER J +0x4b 0x004b #LATIN CAPITAL LETTER K +0x4c 0x004c #LATIN CAPITAL LETTER L +0x4d 0x004d #LATIN CAPITAL LETTER M +0x4e 0x004e #LATIN CAPITAL LETTER N +0x4f 0x004f #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5a 0x005a #LATIN CAPITAL LETTER Z +0x5b 0x005b #LEFT SQUARE BRACKET +0x5c 0x005c #REVERSE SOLIDUS +0x5d 0x005d #RIGHT SQUARE BRACKET +0x5e 0x005e #CIRCUMFLEX ACCENT +0x5f 0x005f #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6a 0x006a #LATIN SMALL LETTER J +0x6b 0x006b #LATIN SMALL LETTER K +0x6c 0x006c #LATIN SMALL LETTER L +0x6d 0x006d #LATIN SMALL LETTER M +0x6e 0x006e #LATIN SMALL LETTER N +0x6f 0x006f #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7a 0x007a #LATIN SMALL LETTER Z +0x7b 0x007b #LEFT CURLY BRACKET +0x7c 0x007c #VERTICAL LINE +0x7d 0x007d #RIGHT CURLY BRACKET +0x7e 0x007e #TILDE +0x7f 0x007f #DELETE +0x80 0x00c7 #LATIN CAPITAL LETTER C WITH CEDILLA +0x81 0x00fc #LATIN SMALL LETTER U WITH DIAERESIS +0x82 0x00e9 #LATIN SMALL LETTER E WITH ACUTE +0x83 0x00e2 #LATIN SMALL LETTER A WITH CIRCUMFLEX +0x84 0x00e3 #LATIN SMALL LETTER A WITH TILDE +0x85 0x00e0 #LATIN SMALL LETTER A WITH GRAVE +0x86 0x00c1 #LATIN CAPITAL LETTER A WITH ACUTE +0x87 0x00e7 #LATIN SMALL LETTER C WITH CEDILLA +0x88 0x00ea #LATIN SMALL LETTER E WITH CIRCUMFLEX +0x89 0x00ca #LATIN CAPITAL LETTER E WITH CIRCUMFLEX +0x8a 0x00e8 #LATIN SMALL LETTER E WITH GRAVE +0x8b 0x00cd #LATIN CAPITAL LETTER I WITH ACUTE +0x8c 0x00d4 #LATIN CAPITAL LETTER O WITH CIRCUMFLEX +0x8d 0x00ec #LATIN SMALL LETTER I WITH GRAVE +0x8e 0x00c3 #LATIN CAPITAL LETTER A WITH TILDE +0x8f 0x00c2 #LATIN CAPITAL LETTER A WITH CIRCUMFLEX +0x90 0x00c9 #LATIN CAPITAL LETTER E WITH ACUTE +0x91 0x00c0 #LATIN CAPITAL LETTER A WITH GRAVE +0x92 0x00c8 #LATIN CAPITAL LETTER E WITH GRAVE +0x93 0x00f4 #LATIN SMALL LETTER O WITH CIRCUMFLEX +0x94 0x00f5 #LATIN SMALL LETTER O WITH TILDE +0x95 0x00f2 #LATIN SMALL LETTER O WITH GRAVE +0x96 0x00da #LATIN CAPITAL LETTER U WITH ACUTE +0x97 0x00f9 #LATIN SMALL LETTER U WITH GRAVE +0x98 0x00cc #LATIN CAPITAL LETTER I WITH GRAVE +0x99 0x00d5 #LATIN CAPITAL LETTER O WITH TILDE +0x9a 0x00dc #LATIN CAPITAL LETTER U WITH DIAERESIS +0x9b 0x00a2 #CENT SIGN +0x9c 0x00a3 #POUND SIGN +0x9d 0x00d9 #LATIN CAPITAL LETTER U WITH GRAVE +0x9e 0x20a7 #PESETA SIGN +0x9f 0x00d3 #LATIN CAPITAL LETTER O WITH ACUTE +0xa0 0x00e1 #LATIN SMALL LETTER A WITH ACUTE +0xa1 0x00ed #LATIN SMALL LETTER I WITH ACUTE +0xa2 0x00f3 #LATIN SMALL LETTER O WITH ACUTE +0xa3 0x00fa #LATIN SMALL LETTER U WITH ACUTE +0xa4 0x00f1 #LATIN SMALL LETTER N WITH TILDE +0xa5 0x00d1 #LATIN CAPITAL LETTER N WITH TILDE +0xa6 0x00aa #FEMININE ORDINAL INDICATOR +0xa7 0x00ba #MASCULINE ORDINAL INDICATOR +0xa8 0x00bf #INVERTED QUESTION MARK +0xa9 0x00d2 #LATIN CAPITAL LETTER O WITH GRAVE +0xaa 0x00ac #NOT SIGN +0xab 0x00bd #VULGAR FRACTION ONE HALF +0xac 0x00bc #VULGAR FRACTION ONE QUARTER +0xad 0x00a1 #INVERTED EXCLAMATION MARK +0xae 0x00ab #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xaf 0x00bb #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xb0 0x2591 #LIGHT SHADE +0xb1 0x2592 #MEDIUM SHADE +0xb2 0x2593 #DARK SHADE +0xb3 0x2502 #BOX DRAWINGS LIGHT VERTICAL +0xb4 0x2524 #BOX DRAWINGS LIGHT VERTICAL AND LEFT +0xb5 0x2561 #BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE +0xb6 0x2562 #BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE +0xb7 0x2556 #BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE +0xb8 0x2555 #BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE +0xb9 0x2563 #BOX DRAWINGS DOUBLE VERTICAL AND LEFT +0xba 0x2551 #BOX DRAWINGS DOUBLE VERTICAL +0xbb 0x2557 #BOX DRAWINGS DOUBLE DOWN AND LEFT +0xbc 0x255d #BOX DRAWINGS DOUBLE UP AND LEFT +0xbd 0x255c #BOX DRAWINGS UP DOUBLE AND LEFT SINGLE +0xbe 0x255b #BOX DRAWINGS UP SINGLE AND LEFT DOUBLE +0xbf 0x2510 #BOX DRAWINGS LIGHT DOWN AND LEFT +0xc0 0x2514 #BOX DRAWINGS LIGHT UP AND RIGHT +0xc1 0x2534 #BOX DRAWINGS LIGHT UP AND HORIZONTAL +0xc2 0x252c #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL +0xc3 0x251c #BOX DRAWINGS LIGHT VERTICAL AND RIGHT +0xc4 0x2500 #BOX DRAWINGS LIGHT HORIZONTAL +0xc5 0x253c #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL +0xc6 0x255e #BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE +0xc7 0x255f #BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE +0xc8 0x255a #BOX DRAWINGS DOUBLE UP AND RIGHT +0xc9 0x2554 #BOX DRAWINGS DOUBLE DOWN AND RIGHT +0xca 0x2569 #BOX DRAWINGS DOUBLE UP AND HORIZONTAL +0xcb 0x2566 #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL +0xcc 0x2560 #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT +0xcd 0x2550 #BOX DRAWINGS DOUBLE HORIZONTAL +0xce 0x256c #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL +0xcf 0x2567 #BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE +0xd0 0x2568 #BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE +0xd1 0x2564 #BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE +0xd2 0x2565 #BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE +0xd3 0x2559 #BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE +0xd4 0x2558 #BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE +0xd5 0x2552 #BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE +0xd6 0x2553 #BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE +0xd7 0x256b #BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE +0xd8 0x256a #BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE +0xd9 0x2518 #BOX DRAWINGS LIGHT UP AND LEFT +0xda 0x250c #BOX DRAWINGS LIGHT DOWN AND RIGHT +0xdb 0x2588 #FULL BLOCK +0xdc 0x2584 #LOWER HALF BLOCK +0xdd 0x258c #LEFT HALF BLOCK +0xde 0x2590 #RIGHT HALF BLOCK +0xdf 0x2580 #UPPER HALF BLOCK +0xe0 0x03b1 #GREEK SMALL LETTER ALPHA +0xe1 0x00df #LATIN SMALL LETTER SHARP S +0xe2 0x0393 #GREEK CAPITAL LETTER GAMMA +0xe3 0x03c0 #GREEK SMALL LETTER PI +0xe4 0x03a3 #GREEK CAPITAL LETTER SIGMA +0xe5 0x03c3 #GREEK SMALL LETTER SIGMA +0xe6 0x00b5 #MICRO SIGN +0xe7 0x03c4 #GREEK SMALL LETTER TAU +0xe8 0x03a6 #GREEK CAPITAL LETTER PHI +0xe9 0x0398 #GREEK CAPITAL LETTER THETA +0xea 0x03a9 #GREEK CAPITAL LETTER OMEGA +0xeb 0x03b4 #GREEK SMALL LETTER DELTA +0xec 0x221e #INFINITY +0xed 0x03c6 #GREEK SMALL LETTER PHI +0xee 0x03b5 #GREEK SMALL LETTER EPSILON +0xef 0x2229 #INTERSECTION +0xf0 0x2261 #IDENTICAL TO +0xf1 0x00b1 #PLUS-MINUS SIGN +0xf2 0x2265 #GREATER-THAN OR EQUAL TO +0xf3 0x2264 #LESS-THAN OR EQUAL TO +0xf4 0x2320 #TOP HALF INTEGRAL +0xf5 0x2321 #BOTTOM HALF INTEGRAL +0xf6 0x00f7 #DIVISION SIGN +0xf7 0x2248 #ALMOST EQUAL TO +0xf8 0x00b0 #DEGREE SIGN +0xf9 0x2219 #BULLET OPERATOR +0xfa 0x00b7 #MIDDLE DOT +0xfb 0x221a #SQUARE ROOT +0xfc 0x207f #SUPERSCRIPT LATIN SMALL LETTER N +0xfd 0x00b2 #SUPERSCRIPT TWO +0xfe 0x25a0 #BLACK SQUARE +0xff 0x00a0 #NO-BREAK SPACE + + \ No newline at end of file diff --git a/codepage/cp861.txt b/codepage/cp861.txt new file mode 100644 index 0000000..163b0b1 --- /dev/null +++ b/codepage/cp861.txt @@ -0,0 +1,275 @@ +# +# Name: cp861_DOSIcelandic to Unicode table +# Unicode version: 2.0 +# Table version: 2.00 +# Table format: Format A +# Date: 04/24/96 +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp861_DOSIcelandic code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp861_DOSIcelandic order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0a 0x000a #LINE FEED +0x0b 0x000b #VERTICAL TABULATION +0x0c 0x000c #FORM FEED +0x0d 0x000d #CARRIAGE RETURN +0x0e 0x000e #SHIFT OUT +0x0f 0x000f #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1a 0x001a #SUBSTITUTE +0x1b 0x001b #ESCAPE +0x1c 0x001c #FILE SEPARATOR +0x1d 0x001d #GROUP SEPARATOR +0x1e 0x001e #RECORD SEPARATOR +0x1f 0x001f #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2a 0x002a #ASTERISK +0x2b 0x002b #PLUS SIGN +0x2c 0x002c #COMMA +0x2d 0x002d #HYPHEN-MINUS +0x2e 0x002e #FULL STOP +0x2f 0x002f #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3a 0x003a #COLON +0x3b 0x003b #SEMICOLON +0x3c 0x003c #LESS-THAN SIGN +0x3d 0x003d #EQUALS SIGN +0x3e 0x003e #GREATER-THAN SIGN +0x3f 0x003f #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4a 0x004a #LATIN CAPITAL LETTER J +0x4b 0x004b #LATIN CAPITAL LETTER K +0x4c 0x004c #LATIN CAPITAL LETTER L +0x4d 0x004d #LATIN CAPITAL LETTER M +0x4e 0x004e #LATIN CAPITAL LETTER N +0x4f 0x004f #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5a 0x005a #LATIN CAPITAL LETTER Z +0x5b 0x005b #LEFT SQUARE BRACKET +0x5c 0x005c #REVERSE SOLIDUS +0x5d 0x005d #RIGHT SQUARE BRACKET +0x5e 0x005e #CIRCUMFLEX ACCENT +0x5f 0x005f #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6a 0x006a #LATIN SMALL LETTER J +0x6b 0x006b #LATIN SMALL LETTER K +0x6c 0x006c #LATIN SMALL LETTER L +0x6d 0x006d #LATIN SMALL LETTER M +0x6e 0x006e #LATIN SMALL LETTER N +0x6f 0x006f #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7a 0x007a #LATIN SMALL LETTER Z +0x7b 0x007b #LEFT CURLY BRACKET +0x7c 0x007c #VERTICAL LINE +0x7d 0x007d #RIGHT CURLY BRACKET +0x7e 0x007e #TILDE +0x7f 0x007f #DELETE +0x80 0x00c7 #LATIN CAPITAL LETTER C WITH CEDILLA +0x81 0x00fc #LATIN SMALL LETTER U WITH DIAERESIS +0x82 0x00e9 #LATIN SMALL LETTER E WITH ACUTE +0x83 0x00e2 #LATIN SMALL LETTER A WITH CIRCUMFLEX +0x84 0x00e4 #LATIN SMALL LETTER A WITH DIAERESIS +0x85 0x00e0 #LATIN SMALL LETTER A WITH GRAVE +0x86 0x00e5 #LATIN SMALL LETTER A WITH RING ABOVE +0x87 0x00e7 #LATIN SMALL LETTER C WITH CEDILLA +0x88 0x00ea #LATIN SMALL LETTER E WITH CIRCUMFLEX +0x89 0x00eb #LATIN SMALL LETTER E WITH DIAERESIS +0x8a 0x00e8 #LATIN SMALL LETTER E WITH GRAVE +0x8b 0x00d0 #LATIN CAPITAL LETTER ETH +0x8c 0x00f0 #LATIN SMALL LETTER ETH +0x8d 0x00de #LATIN CAPITAL LETTER THORN +0x8e 0x00c4 #LATIN CAPITAL LETTER A WITH DIAERESIS +0x8f 0x00c5 #LATIN CAPITAL LETTER A WITH RING ABOVE +0x90 0x00c9 #LATIN CAPITAL LETTER E WITH ACUTE +0x91 0x00e6 #LATIN SMALL LIGATURE AE +0x92 0x00c6 #LATIN CAPITAL LIGATURE AE +0x93 0x00f4 #LATIN SMALL LETTER O WITH CIRCUMFLEX +0x94 0x00f6 #LATIN SMALL LETTER O WITH DIAERESIS +0x95 0x00fe #LATIN SMALL LETTER THORN +0x96 0x00fb #LATIN SMALL LETTER U WITH CIRCUMFLEX +0x97 0x00dd #LATIN CAPITAL LETTER Y WITH ACUTE +0x98 0x00fd #LATIN SMALL LETTER Y WITH ACUTE +0x99 0x00d6 #LATIN CAPITAL LETTER O WITH DIAERESIS +0x9a 0x00dc #LATIN CAPITAL LETTER U WITH DIAERESIS +0x9b 0x00f8 #LATIN SMALL LETTER O WITH STROKE +0x9c 0x00a3 #POUND SIGN +0x9d 0x00d8 #LATIN CAPITAL LETTER O WITH STROKE +0x9e 0x20a7 #PESETA SIGN +0x9f 0x0192 #LATIN SMALL LETTER F WITH HOOK +0xa0 0x00e1 #LATIN SMALL LETTER A WITH ACUTE +0xa1 0x00ed #LATIN SMALL LETTER I WITH ACUTE +0xa2 0x00f3 #LATIN SMALL LETTER O WITH ACUTE +0xa3 0x00fa #LATIN SMALL LETTER U WITH ACUTE +0xa4 0x00c1 #LATIN CAPITAL LETTER A WITH ACUTE +0xa5 0x00cd #LATIN CAPITAL LETTER I WITH ACUTE +0xa6 0x00d3 #LATIN CAPITAL LETTER O WITH ACUTE +0xa7 0x00da #LATIN CAPITAL LETTER U WITH ACUTE +0xa8 0x00bf #INVERTED QUESTION MARK +0xa9 0x2310 #REVERSED NOT SIGN +0xaa 0x00ac #NOT SIGN +0xab 0x00bd #VULGAR FRACTION ONE HALF +0xac 0x00bc #VULGAR FRACTION ONE QUARTER +0xad 0x00a1 #INVERTED EXCLAMATION MARK +0xae 0x00ab #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xaf 0x00bb #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xb0 0x2591 #LIGHT SHADE +0xb1 0x2592 #MEDIUM SHADE +0xb2 0x2593 #DARK SHADE +0xb3 0x2502 #BOX DRAWINGS LIGHT VERTICAL +0xb4 0x2524 #BOX DRAWINGS LIGHT VERTICAL AND LEFT +0xb5 0x2561 #BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE +0xb6 0x2562 #BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE +0xb7 0x2556 #BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE +0xb8 0x2555 #BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE +0xb9 0x2563 #BOX DRAWINGS DOUBLE VERTICAL AND LEFT +0xba 0x2551 #BOX DRAWINGS DOUBLE VERTICAL +0xbb 0x2557 #BOX DRAWINGS DOUBLE DOWN AND LEFT +0xbc 0x255d #BOX DRAWINGS DOUBLE UP AND LEFT +0xbd 0x255c #BOX DRAWINGS UP DOUBLE AND LEFT SINGLE +0xbe 0x255b #BOX DRAWINGS UP SINGLE AND LEFT DOUBLE +0xbf 0x2510 #BOX DRAWINGS LIGHT DOWN AND LEFT +0xc0 0x2514 #BOX DRAWINGS LIGHT UP AND RIGHT +0xc1 0x2534 #BOX DRAWINGS LIGHT UP AND HORIZONTAL +0xc2 0x252c #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL +0xc3 0x251c #BOX DRAWINGS LIGHT VERTICAL AND RIGHT +0xc4 0x2500 #BOX DRAWINGS LIGHT HORIZONTAL +0xc5 0x253c #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL +0xc6 0x255e #BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE +0xc7 0x255f #BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE +0xc8 0x255a #BOX DRAWINGS DOUBLE UP AND RIGHT +0xc9 0x2554 #BOX DRAWINGS DOUBLE DOWN AND RIGHT +0xca 0x2569 #BOX DRAWINGS DOUBLE UP AND HORIZONTAL +0xcb 0x2566 #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL +0xcc 0x2560 #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT +0xcd 0x2550 #BOX DRAWINGS DOUBLE HORIZONTAL +0xce 0x256c #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL +0xcf 0x2567 #BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE +0xd0 0x2568 #BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE +0xd1 0x2564 #BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE +0xd2 0x2565 #BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE +0xd3 0x2559 #BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE +0xd4 0x2558 #BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE +0xd5 0x2552 #BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE +0xd6 0x2553 #BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE +0xd7 0x256b #BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE +0xd8 0x256a #BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE +0xd9 0x2518 #BOX DRAWINGS LIGHT UP AND LEFT +0xda 0x250c #BOX DRAWINGS LIGHT DOWN AND RIGHT +0xdb 0x2588 #FULL BLOCK +0xdc 0x2584 #LOWER HALF BLOCK +0xdd 0x258c #LEFT HALF BLOCK +0xde 0x2590 #RIGHT HALF BLOCK +0xdf 0x2580 #UPPER HALF BLOCK +0xe0 0x03b1 #GREEK SMALL LETTER ALPHA +0xe1 0x00df #LATIN SMALL LETTER SHARP S +0xe2 0x0393 #GREEK CAPITAL LETTER GAMMA +0xe3 0x03c0 #GREEK SMALL LETTER PI +0xe4 0x03a3 #GREEK CAPITAL LETTER SIGMA +0xe5 0x03c3 #GREEK SMALL LETTER SIGMA +0xe6 0x00b5 #MICRO SIGN +0xe7 0x03c4 #GREEK SMALL LETTER TAU +0xe8 0x03a6 #GREEK CAPITAL LETTER PHI +0xe9 0x0398 #GREEK CAPITAL LETTER THETA +0xea 0x03a9 #GREEK CAPITAL LETTER OMEGA +0xeb 0x03b4 #GREEK SMALL LETTER DELTA +0xec 0x221e #INFINITY +0xed 0x03c6 #GREEK SMALL LETTER PHI +0xee 0x03b5 #GREEK SMALL LETTER EPSILON +0xef 0x2229 #INTERSECTION +0xf0 0x2261 #IDENTICAL TO +0xf1 0x00b1 #PLUS-MINUS SIGN +0xf2 0x2265 #GREATER-THAN OR EQUAL TO +0xf3 0x2264 #LESS-THAN OR EQUAL TO +0xf4 0x2320 #TOP HALF INTEGRAL +0xf5 0x2321 #BOTTOM HALF INTEGRAL +0xf6 0x00f7 #DIVISION SIGN +0xf7 0x2248 #ALMOST EQUAL TO +0xf8 0x00b0 #DEGREE SIGN +0xf9 0x2219 #BULLET OPERATOR +0xfa 0x00b7 #MIDDLE DOT +0xfb 0x221a #SQUARE ROOT +0xfc 0x207f #SUPERSCRIPT LATIN SMALL LETTER N +0xfd 0x00b2 #SUPERSCRIPT TWO +0xfe 0x25a0 #BLACK SQUARE +0xff 0x00a0 #NO-BREAK SPACE + + \ No newline at end of file diff --git a/codepage/cp862.txt b/codepage/cp862.txt new file mode 100644 index 0000000..d33547a --- /dev/null +++ b/codepage/cp862.txt @@ -0,0 +1,275 @@ +# +# Name: cp862_DOSHebrew to Unicode table +# Unicode version: 2.0 +# Table version: 2.00 +# Table format: Format A +# Date: 04/24/96 +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp862_DOSHebrew code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp862_DOSHebrew order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0a 0x000a #LINE FEED +0x0b 0x000b #VERTICAL TABULATION +0x0c 0x000c #FORM FEED +0x0d 0x000d #CARRIAGE RETURN +0x0e 0x000e #SHIFT OUT +0x0f 0x000f #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1a 0x001a #SUBSTITUTE +0x1b 0x001b #ESCAPE +0x1c 0x001c #FILE SEPARATOR +0x1d 0x001d #GROUP SEPARATOR +0x1e 0x001e #RECORD SEPARATOR +0x1f 0x001f #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2a 0x002a #ASTERISK +0x2b 0x002b #PLUS SIGN +0x2c 0x002c #COMMA +0x2d 0x002d #HYPHEN-MINUS +0x2e 0x002e #FULL STOP +0x2f 0x002f #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3a 0x003a #COLON +0x3b 0x003b #SEMICOLON +0x3c 0x003c #LESS-THAN SIGN +0x3d 0x003d #EQUALS SIGN +0x3e 0x003e #GREATER-THAN SIGN +0x3f 0x003f #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4a 0x004a #LATIN CAPITAL LETTER J +0x4b 0x004b #LATIN CAPITAL LETTER K +0x4c 0x004c #LATIN CAPITAL LETTER L +0x4d 0x004d #LATIN CAPITAL LETTER M +0x4e 0x004e #LATIN CAPITAL LETTER N +0x4f 0x004f #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5a 0x005a #LATIN CAPITAL LETTER Z +0x5b 0x005b #LEFT SQUARE BRACKET +0x5c 0x005c #REVERSE SOLIDUS +0x5d 0x005d #RIGHT SQUARE BRACKET +0x5e 0x005e #CIRCUMFLEX ACCENT +0x5f 0x005f #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6a 0x006a #LATIN SMALL LETTER J +0x6b 0x006b #LATIN SMALL LETTER K +0x6c 0x006c #LATIN SMALL LETTER L +0x6d 0x006d #LATIN SMALL LETTER M +0x6e 0x006e #LATIN SMALL LETTER N +0x6f 0x006f #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7a 0x007a #LATIN SMALL LETTER Z +0x7b 0x007b #LEFT CURLY BRACKET +0x7c 0x007c #VERTICAL LINE +0x7d 0x007d #RIGHT CURLY BRACKET +0x7e 0x007e #TILDE +0x7f 0x007f #DELETE +0x80 0x05d0 #HEBREW LETTER ALEF +0x81 0x05d1 #HEBREW LETTER BET +0x82 0x05d2 #HEBREW LETTER GIMEL +0x83 0x05d3 #HEBREW LETTER DALET +0x84 0x05d4 #HEBREW LETTER HE +0x85 0x05d5 #HEBREW LETTER VAV +0x86 0x05d6 #HEBREW LETTER ZAYIN +0x87 0x05d7 #HEBREW LETTER HET +0x88 0x05d8 #HEBREW LETTER TET +0x89 0x05d9 #HEBREW LETTER YOD +0x8a 0x05da #HEBREW LETTER FINAL KAF +0x8b 0x05db #HEBREW LETTER KAF +0x8c 0x05dc #HEBREW LETTER LAMED +0x8d 0x05dd #HEBREW LETTER FINAL MEM +0x8e 0x05de #HEBREW LETTER MEM +0x8f 0x05df #HEBREW LETTER FINAL NUN +0x90 0x05e0 #HEBREW LETTER NUN +0x91 0x05e1 #HEBREW LETTER SAMEKH +0x92 0x05e2 #HEBREW LETTER AYIN +0x93 0x05e3 #HEBREW LETTER FINAL PE +0x94 0x05e4 #HEBREW LETTER PE +0x95 0x05e5 #HEBREW LETTER FINAL TSADI +0x96 0x05e6 #HEBREW LETTER TSADI +0x97 0x05e7 #HEBREW LETTER QOF +0x98 0x05e8 #HEBREW LETTER RESH +0x99 0x05e9 #HEBREW LETTER SHIN +0x9a 0x05ea #HEBREW LETTER TAV +0x9b 0x00a2 #CENT SIGN +0x9c 0x00a3 #POUND SIGN +0x9d 0x00a5 #YEN SIGN +0x9e 0x20a7 #PESETA SIGN +0x9f 0x0192 #LATIN SMALL LETTER F WITH HOOK +0xa0 0x00e1 #LATIN SMALL LETTER A WITH ACUTE +0xa1 0x00ed #LATIN SMALL LETTER I WITH ACUTE +0xa2 0x00f3 #LATIN SMALL LETTER O WITH ACUTE +0xa3 0x00fa #LATIN SMALL LETTER U WITH ACUTE +0xa4 0x00f1 #LATIN SMALL LETTER N WITH TILDE +0xa5 0x00d1 #LATIN CAPITAL LETTER N WITH TILDE +0xa6 0x00aa #FEMININE ORDINAL INDICATOR +0xa7 0x00ba #MASCULINE ORDINAL INDICATOR +0xa8 0x00bf #INVERTED QUESTION MARK +0xa9 0x2310 #REVERSED NOT SIGN +0xaa 0x00ac #NOT SIGN +0xab 0x00bd #VULGAR FRACTION ONE HALF +0xac 0x00bc #VULGAR FRACTION ONE QUARTER +0xad 0x00a1 #INVERTED EXCLAMATION MARK +0xae 0x00ab #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xaf 0x00bb #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xb0 0x2591 #LIGHT SHADE +0xb1 0x2592 #MEDIUM SHADE +0xb2 0x2593 #DARK SHADE +0xb3 0x2502 #BOX DRAWINGS LIGHT VERTICAL +0xb4 0x2524 #BOX DRAWINGS LIGHT VERTICAL AND LEFT +0xb5 0x2561 #BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE +0xb6 0x2562 #BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE +0xb7 0x2556 #BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE +0xb8 0x2555 #BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE +0xb9 0x2563 #BOX DRAWINGS DOUBLE VERTICAL AND LEFT +0xba 0x2551 #BOX DRAWINGS DOUBLE VERTICAL +0xbb 0x2557 #BOX DRAWINGS DOUBLE DOWN AND LEFT +0xbc 0x255d #BOX DRAWINGS DOUBLE UP AND LEFT +0xbd 0x255c #BOX DRAWINGS UP DOUBLE AND LEFT SINGLE +0xbe 0x255b #BOX DRAWINGS UP SINGLE AND LEFT DOUBLE +0xbf 0x2510 #BOX DRAWINGS LIGHT DOWN AND LEFT +0xc0 0x2514 #BOX DRAWINGS LIGHT UP AND RIGHT +0xc1 0x2534 #BOX DRAWINGS LIGHT UP AND HORIZONTAL +0xc2 0x252c #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL +0xc3 0x251c #BOX DRAWINGS LIGHT VERTICAL AND RIGHT +0xc4 0x2500 #BOX DRAWINGS LIGHT HORIZONTAL +0xc5 0x253c #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL +0xc6 0x255e #BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE +0xc7 0x255f #BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE +0xc8 0x255a #BOX DRAWINGS DOUBLE UP AND RIGHT +0xc9 0x2554 #BOX DRAWINGS DOUBLE DOWN AND RIGHT +0xca 0x2569 #BOX DRAWINGS DOUBLE UP AND HORIZONTAL +0xcb 0x2566 #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL +0xcc 0x2560 #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT +0xcd 0x2550 #BOX DRAWINGS DOUBLE HORIZONTAL +0xce 0x256c #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL +0xcf 0x2567 #BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE +0xd0 0x2568 #BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE +0xd1 0x2564 #BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE +0xd2 0x2565 #BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE +0xd3 0x2559 #BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE +0xd4 0x2558 #BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE +0xd5 0x2552 #BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE +0xd6 0x2553 #BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE +0xd7 0x256b #BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE +0xd8 0x256a #BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE +0xd9 0x2518 #BOX DRAWINGS LIGHT UP AND LEFT +0xda 0x250c #BOX DRAWINGS LIGHT DOWN AND RIGHT +0xdb 0x2588 #FULL BLOCK +0xdc 0x2584 #LOWER HALF BLOCK +0xdd 0x258c #LEFT HALF BLOCK +0xde 0x2590 #RIGHT HALF BLOCK +0xdf 0x2580 #UPPER HALF BLOCK +0xe0 0x03b1 #GREEK SMALL LETTER ALPHA +0xe1 0x00df #LATIN SMALL LETTER SHARP S (GERMAN) +0xe2 0x0393 #GREEK CAPITAL LETTER GAMMA +0xe3 0x03c0 #GREEK SMALL LETTER PI +0xe4 0x03a3 #GREEK CAPITAL LETTER SIGMA +0xe5 0x03c3 #GREEK SMALL LETTER SIGMA +0xe6 0x00b5 #MICRO SIGN +0xe7 0x03c4 #GREEK SMALL LETTER TAU +0xe8 0x03a6 #GREEK CAPITAL LETTER PHI +0xe9 0x0398 #GREEK CAPITAL LETTER THETA +0xea 0x03a9 #GREEK CAPITAL LETTER OMEGA +0xeb 0x03b4 #GREEK SMALL LETTER DELTA +0xec 0x221e #INFINITY +0xed 0x03c6 #GREEK SMALL LETTER PHI +0xee 0x03b5 #GREEK SMALL LETTER EPSILON +0xef 0x2229 #INTERSECTION +0xf0 0x2261 #IDENTICAL TO +0xf1 0x00b1 #PLUS-MINUS SIGN +0xf2 0x2265 #GREATER-THAN OR EQUAL TO +0xf3 0x2264 #LESS-THAN OR EQUAL TO +0xf4 0x2320 #TOP HALF INTEGRAL +0xf5 0x2321 #BOTTOM HALF INTEGRAL +0xf6 0x00f7 #DIVISION SIGN +0xf7 0x2248 #ALMOST EQUAL TO +0xf8 0x00b0 #DEGREE SIGN +0xf9 0x2219 #BULLET OPERATOR +0xfa 0x00b7 #MIDDLE DOT +0xfb 0x221a #SQUARE ROOT +0xfc 0x207f #SUPERSCRIPT LATIN SMALL LETTER N +0xfd 0x00b2 #SUPERSCRIPT TWO +0xfe 0x25a0 #BLACK SQUARE +0xff 0x00a0 #NO-BREAK SPACE + + \ No newline at end of file diff --git a/codepage/cp863.txt b/codepage/cp863.txt new file mode 100644 index 0000000..19f3e8b --- /dev/null +++ b/codepage/cp863.txt @@ -0,0 +1,275 @@ +# +# Name: cp863_DOSCanadaF to Unicode table +# Unicode version: 2.0 +# Table version: 2.00 +# Table format: Format A +# Date: 04/24/96 +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp863_DOSCanadaF code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp863_DOSCanadaF order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0a 0x000a #LINE FEED +0x0b 0x000b #VERTICAL TABULATION +0x0c 0x000c #FORM FEED +0x0d 0x000d #CARRIAGE RETURN +0x0e 0x000e #SHIFT OUT +0x0f 0x000f #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1a 0x001a #SUBSTITUTE +0x1b 0x001b #ESCAPE +0x1c 0x001c #FILE SEPARATOR +0x1d 0x001d #GROUP SEPARATOR +0x1e 0x001e #RECORD SEPARATOR +0x1f 0x001f #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2a 0x002a #ASTERISK +0x2b 0x002b #PLUS SIGN +0x2c 0x002c #COMMA +0x2d 0x002d #HYPHEN-MINUS +0x2e 0x002e #FULL STOP +0x2f 0x002f #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3a 0x003a #COLON +0x3b 0x003b #SEMICOLON +0x3c 0x003c #LESS-THAN SIGN +0x3d 0x003d #EQUALS SIGN +0x3e 0x003e #GREATER-THAN SIGN +0x3f 0x003f #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4a 0x004a #LATIN CAPITAL LETTER J +0x4b 0x004b #LATIN CAPITAL LETTER K +0x4c 0x004c #LATIN CAPITAL LETTER L +0x4d 0x004d #LATIN CAPITAL LETTER M +0x4e 0x004e #LATIN CAPITAL LETTER N +0x4f 0x004f #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5a 0x005a #LATIN CAPITAL LETTER Z +0x5b 0x005b #LEFT SQUARE BRACKET +0x5c 0x005c #REVERSE SOLIDUS +0x5d 0x005d #RIGHT SQUARE BRACKET +0x5e 0x005e #CIRCUMFLEX ACCENT +0x5f 0x005f #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6a 0x006a #LATIN SMALL LETTER J +0x6b 0x006b #LATIN SMALL LETTER K +0x6c 0x006c #LATIN SMALL LETTER L +0x6d 0x006d #LATIN SMALL LETTER M +0x6e 0x006e #LATIN SMALL LETTER N +0x6f 0x006f #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7a 0x007a #LATIN SMALL LETTER Z +0x7b 0x007b #LEFT CURLY BRACKET +0x7c 0x007c #VERTICAL LINE +0x7d 0x007d #RIGHT CURLY BRACKET +0x7e 0x007e #TILDE +0x7f 0x007f #DELETE +0x80 0x00c7 #LATIN CAPITAL LETTER C WITH CEDILLA +0x81 0x00fc #LATIN SMALL LETTER U WITH DIAERESIS +0x82 0x00e9 #LATIN SMALL LETTER E WITH ACUTE +0x83 0x00e2 #LATIN SMALL LETTER A WITH CIRCUMFLEX +0x84 0x00c2 #LATIN CAPITAL LETTER A WITH CIRCUMFLEX +0x85 0x00e0 #LATIN SMALL LETTER A WITH GRAVE +0x86 0x00b6 #PILCROW SIGN +0x87 0x00e7 #LATIN SMALL LETTER C WITH CEDILLA +0x88 0x00ea #LATIN SMALL LETTER E WITH CIRCUMFLEX +0x89 0x00eb #LATIN SMALL LETTER E WITH DIAERESIS +0x8a 0x00e8 #LATIN SMALL LETTER E WITH GRAVE +0x8b 0x00ef #LATIN SMALL LETTER I WITH DIAERESIS +0x8c 0x00ee #LATIN SMALL LETTER I WITH CIRCUMFLEX +0x8d 0x2017 #DOUBLE LOW LINE +0x8e 0x00c0 #LATIN CAPITAL LETTER A WITH GRAVE +0x8f 0x00a7 #SECTION SIGN +0x90 0x00c9 #LATIN CAPITAL LETTER E WITH ACUTE +0x91 0x00c8 #LATIN CAPITAL LETTER E WITH GRAVE +0x92 0x00ca #LATIN CAPITAL LETTER E WITH CIRCUMFLEX +0x93 0x00f4 #LATIN SMALL LETTER O WITH CIRCUMFLEX +0x94 0x00cb #LATIN CAPITAL LETTER E WITH DIAERESIS +0x95 0x00cf #LATIN CAPITAL LETTER I WITH DIAERESIS +0x96 0x00fb #LATIN SMALL LETTER U WITH CIRCUMFLEX +0x97 0x00f9 #LATIN SMALL LETTER U WITH GRAVE +0x98 0x00a4 #CURRENCY SIGN +0x99 0x00d4 #LATIN CAPITAL LETTER O WITH CIRCUMFLEX +0x9a 0x00dc #LATIN CAPITAL LETTER U WITH DIAERESIS +0x9b 0x00a2 #CENT SIGN +0x9c 0x00a3 #POUND SIGN +0x9d 0x00d9 #LATIN CAPITAL LETTER U WITH GRAVE +0x9e 0x00db #LATIN CAPITAL LETTER U WITH CIRCUMFLEX +0x9f 0x0192 #LATIN SMALL LETTER F WITH HOOK +0xa0 0x00a6 #BROKEN BAR +0xa1 0x00b4 #ACUTE ACCENT +0xa2 0x00f3 #LATIN SMALL LETTER O WITH ACUTE +0xa3 0x00fa #LATIN SMALL LETTER U WITH ACUTE +0xa4 0x00a8 #DIAERESIS +0xa5 0x00b8 #CEDILLA +0xa6 0x00b3 #SUPERSCRIPT THREE +0xa7 0x00af #MACRON +0xa8 0x00ce #LATIN CAPITAL LETTER I WITH CIRCUMFLEX +0xa9 0x2310 #REVERSED NOT SIGN +0xaa 0x00ac #NOT SIGN +0xab 0x00bd #VULGAR FRACTION ONE HALF +0xac 0x00bc #VULGAR FRACTION ONE QUARTER +0xad 0x00be #VULGAR FRACTION THREE QUARTERS +0xae 0x00ab #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xaf 0x00bb #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xb0 0x2591 #LIGHT SHADE +0xb1 0x2592 #MEDIUM SHADE +0xb2 0x2593 #DARK SHADE +0xb3 0x2502 #BOX DRAWINGS LIGHT VERTICAL +0xb4 0x2524 #BOX DRAWINGS LIGHT VERTICAL AND LEFT +0xb5 0x2561 #BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE +0xb6 0x2562 #BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE +0xb7 0x2556 #BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE +0xb8 0x2555 #BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE +0xb9 0x2563 #BOX DRAWINGS DOUBLE VERTICAL AND LEFT +0xba 0x2551 #BOX DRAWINGS DOUBLE VERTICAL +0xbb 0x2557 #BOX DRAWINGS DOUBLE DOWN AND LEFT +0xbc 0x255d #BOX DRAWINGS DOUBLE UP AND LEFT +0xbd 0x255c #BOX DRAWINGS UP DOUBLE AND LEFT SINGLE +0xbe 0x255b #BOX DRAWINGS UP SINGLE AND LEFT DOUBLE +0xbf 0x2510 #BOX DRAWINGS LIGHT DOWN AND LEFT +0xc0 0x2514 #BOX DRAWINGS LIGHT UP AND RIGHT +0xc1 0x2534 #BOX DRAWINGS LIGHT UP AND HORIZONTAL +0xc2 0x252c #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL +0xc3 0x251c #BOX DRAWINGS LIGHT VERTICAL AND RIGHT +0xc4 0x2500 #BOX DRAWINGS LIGHT HORIZONTAL +0xc5 0x253c #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL +0xc6 0x255e #BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE +0xc7 0x255f #BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE +0xc8 0x255a #BOX DRAWINGS DOUBLE UP AND RIGHT +0xc9 0x2554 #BOX DRAWINGS DOUBLE DOWN AND RIGHT +0xca 0x2569 #BOX DRAWINGS DOUBLE UP AND HORIZONTAL +0xcb 0x2566 #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL +0xcc 0x2560 #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT +0xcd 0x2550 #BOX DRAWINGS DOUBLE HORIZONTAL +0xce 0x256c #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL +0xcf 0x2567 #BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE +0xd0 0x2568 #BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE +0xd1 0x2564 #BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE +0xd2 0x2565 #BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE +0xd3 0x2559 #BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE +0xd4 0x2558 #BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE +0xd5 0x2552 #BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE +0xd6 0x2553 #BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE +0xd7 0x256b #BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE +0xd8 0x256a #BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE +0xd9 0x2518 #BOX DRAWINGS LIGHT UP AND LEFT +0xda 0x250c #BOX DRAWINGS LIGHT DOWN AND RIGHT +0xdb 0x2588 #FULL BLOCK +0xdc 0x2584 #LOWER HALF BLOCK +0xdd 0x258c #LEFT HALF BLOCK +0xde 0x2590 #RIGHT HALF BLOCK +0xdf 0x2580 #UPPER HALF BLOCK +0xe0 0x03b1 #GREEK SMALL LETTER ALPHA +0xe1 0x00df #LATIN SMALL LETTER SHARP S +0xe2 0x0393 #GREEK CAPITAL LETTER GAMMA +0xe3 0x03c0 #GREEK SMALL LETTER PI +0xe4 0x03a3 #GREEK CAPITAL LETTER SIGMA +0xe5 0x03c3 #GREEK SMALL LETTER SIGMA +0xe6 0x00b5 #MICRO SIGN +0xe7 0x03c4 #GREEK SMALL LETTER TAU +0xe8 0x03a6 #GREEK CAPITAL LETTER PHI +0xe9 0x0398 #GREEK CAPITAL LETTER THETA +0xea 0x03a9 #GREEK CAPITAL LETTER OMEGA +0xeb 0x03b4 #GREEK SMALL LETTER DELTA +0xec 0x221e #INFINITY +0xed 0x03c6 #GREEK SMALL LETTER PHI +0xee 0x03b5 #GREEK SMALL LETTER EPSILON +0xef 0x2229 #INTERSECTION +0xf0 0x2261 #IDENTICAL TO +0xf1 0x00b1 #PLUS-MINUS SIGN +0xf2 0x2265 #GREATER-THAN OR EQUAL TO +0xf3 0x2264 #LESS-THAN OR EQUAL TO +0xf4 0x2320 #TOP HALF INTEGRAL +0xf5 0x2321 #BOTTOM HALF INTEGRAL +0xf6 0x00f7 #DIVISION SIGN +0xf7 0x2248 #ALMOST EQUAL TO +0xf8 0x00b0 #DEGREE SIGN +0xf9 0x2219 #BULLET OPERATOR +0xfa 0x00b7 #MIDDLE DOT +0xfb 0x221a #SQUARE ROOT +0xfc 0x207f #SUPERSCRIPT LATIN SMALL LETTER N +0xfd 0x00b2 #SUPERSCRIPT TWO +0xfe 0x25a0 #BLACK SQUARE +0xff 0x00a0 #NO-BREAK SPACE + + \ No newline at end of file diff --git a/codepage/cp864.txt b/codepage/cp864.txt new file mode 100644 index 0000000..d0079b0 --- /dev/null +++ b/codepage/cp864.txt @@ -0,0 +1,275 @@ +# +# Name: cp864_DOSArabic to Unicode table +# Unicode version: 2.0 +# Table version: 2.00 +# Table format: Format A +# Date: 04/24/96 +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp864_DOSArabic code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp864_DOSArabic order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0a 0x000a #LINE FEED +0x0b 0x000b #VERTICAL TABULATION +0x0c 0x000c #FORM FEED +0x0d 0x000d #CARRIAGE RETURN +0x0e 0x000e #SHIFT OUT +0x0f 0x000f #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1a 0x001a #SUBSTITUTE +0x1b 0x001b #ESCAPE +0x1c 0x001c #FILE SEPARATOR +0x1d 0x001d #GROUP SEPARATOR +0x1e 0x001e #RECORD SEPARATOR +0x1f 0x001f #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x066a #ARABIC PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2a 0x002a #ASTERISK +0x2b 0x002b #PLUS SIGN +0x2c 0x002c #COMMA +0x2d 0x002d #HYPHEN-MINUS +0x2e 0x002e #FULL STOP +0x2f 0x002f #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3a 0x003a #COLON +0x3b 0x003b #SEMICOLON +0x3c 0x003c #LESS-THAN SIGN +0x3d 0x003d #EQUALS SIGN +0x3e 0x003e #GREATER-THAN SIGN +0x3f 0x003f #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4a 0x004a #LATIN CAPITAL LETTER J +0x4b 0x004b #LATIN CAPITAL LETTER K +0x4c 0x004c #LATIN CAPITAL LETTER L +0x4d 0x004d #LATIN CAPITAL LETTER M +0x4e 0x004e #LATIN CAPITAL LETTER N +0x4f 0x004f #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5a 0x005a #LATIN CAPITAL LETTER Z +0x5b 0x005b #LEFT SQUARE BRACKET +0x5c 0x005c #REVERSE SOLIDUS +0x5d 0x005d #RIGHT SQUARE BRACKET +0x5e 0x005e #CIRCUMFLEX ACCENT +0x5f 0x005f #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6a 0x006a #LATIN SMALL LETTER J +0x6b 0x006b #LATIN SMALL LETTER K +0x6c 0x006c #LATIN SMALL LETTER L +0x6d 0x006d #LATIN SMALL LETTER M +0x6e 0x006e #LATIN SMALL LETTER N +0x6f 0x006f #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7a 0x007a #LATIN SMALL LETTER Z +0x7b 0x007b #LEFT CURLY BRACKET +0x7c 0x007c #VERTICAL LINE +0x7d 0x007d #RIGHT CURLY BRACKET +0x7e 0x007e #TILDE +0x7f 0x007f #DELETE +0x80 0x00b0 #DEGREE SIGN +0x81 0x00b7 #MIDDLE DOT +0x82 0x2219 #BULLET OPERATOR +0x83 0x221a #SQUARE ROOT +0x84 0x2592 #MEDIUM SHADE +0x85 0x2500 #FORMS LIGHT HORIZONTAL +0x86 0x2502 #FORMS LIGHT VERTICAL +0x87 0x253c #FORMS LIGHT VERTICAL AND HORIZONTAL +0x88 0x2524 #FORMS LIGHT VERTICAL AND LEFT +0x89 0x252c #FORMS LIGHT DOWN AND HORIZONTAL +0x8a 0x251c #FORMS LIGHT VERTICAL AND RIGHT +0x8b 0x2534 #FORMS LIGHT UP AND HORIZONTAL +0x8c 0x2510 #FORMS LIGHT DOWN AND LEFT +0x8d 0x250c #FORMS LIGHT DOWN AND RIGHT +0x8e 0x2514 #FORMS LIGHT UP AND RIGHT +0x8f 0x2518 #FORMS LIGHT UP AND LEFT +0x90 0x03b2 #GREEK SMALL BETA +0x91 0x221e #INFINITY +0x92 0x03c6 #GREEK SMALL PHI +0x93 0x00b1 #PLUS-OR-MINUS SIGN +0x94 0x00bd #FRACTION 1/2 +0x95 0x00bc #FRACTION 1/4 +0x96 0x2248 #ALMOST EQUAL TO +0x97 0x00ab #LEFT POINTING GUILLEMET +0x98 0x00bb #RIGHT POINTING GUILLEMET +0x99 0xfef7 #ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM +0x9a 0xfef8 #ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM +0x9b #UNDEFINED +0x9c #UNDEFINED +0x9d 0xfefb #ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM +0x9e 0xfefc #ARABIC LIGATURE LAM WITH ALEF FINAL FORM +0x9f #UNDEFINED +0xa0 0x00a0 #NON-BREAKING SPACE +0xa1 0x00ad #SOFT HYPHEN +0xa2 0xfe82 #ARABIC LETTER ALEF WITH MADDA ABOVE FINAL FORM +0xa3 0x00a3 #POUND SIGN +0xa4 0x00a4 #CURRENCY SIGN +0xa5 0xfe84 #ARABIC LETTER ALEF WITH HAMZA ABOVE FINAL FORM +0xa6 #UNDEFINED +0xa7 #UNDEFINED +0xa8 0xfe8e #ARABIC LETTER ALEF FINAL FORM +0xa9 0xfe8f #ARABIC LETTER BEH ISOLATED FORM +0xaa 0xfe95 #ARABIC LETTER TEH ISOLATED FORM +0xab 0xfe99 #ARABIC LETTER THEH ISOLATED FORM +0xac 0x060c #ARABIC COMMA +0xad 0xfe9d #ARABIC LETTER JEEM ISOLATED FORM +0xae 0xfea1 #ARABIC LETTER HAH ISOLATED FORM +0xaf 0xfea5 #ARABIC LETTER KHAH ISOLATED FORM +0xb0 0x0660 #ARABIC-INDIC DIGIT ZERO +0xb1 0x0661 #ARABIC-INDIC DIGIT ONE +0xb2 0x0662 #ARABIC-INDIC DIGIT TWO +0xb3 0x0663 #ARABIC-INDIC DIGIT THREE +0xb4 0x0664 #ARABIC-INDIC DIGIT FOUR +0xb5 0x0665 #ARABIC-INDIC DIGIT FIVE +0xb6 0x0666 #ARABIC-INDIC DIGIT SIX +0xb7 0x0667 #ARABIC-INDIC DIGIT SEVEN +0xb8 0x0668 #ARABIC-INDIC DIGIT EIGHT +0xb9 0x0669 #ARABIC-INDIC DIGIT NINE +0xba 0xfed1 #ARABIC LETTER FEH ISOLATED FORM +0xbb 0x061b #ARABIC SEMICOLON +0xbc 0xfeb1 #ARABIC LETTER SEEN ISOLATED FORM +0xbd 0xfeb5 #ARABIC LETTER SHEEN ISOLATED FORM +0xbe 0xfeb9 #ARABIC LETTER SAD ISOLATED FORM +0xbf 0x061f #ARABIC QUESTION MARK +0xc0 0x00a2 #CENT SIGN +0xc1 0xfe80 #ARABIC LETTER HAMZA ISOLATED FORM +0xc2 0xfe81 #ARABIC LETTER ALEF WITH MADDA ABOVE ISOLATED FORM +0xc3 0xfe83 #ARABIC LETTER ALEF WITH HAMZA ABOVE ISOLATED FORM +0xc4 0xfe85 #ARABIC LETTER WAW WITH HAMZA ABOVE ISOLATED FORM +0xc5 0xfeca #ARABIC LETTER AIN FINAL FORM +0xc6 0xfe8b #ARABIC LETTER YEH WITH HAMZA ABOVE INITIAL FORM +0xc7 0xfe8d #ARABIC LETTER ALEF ISOLATED FORM +0xc8 0xfe91 #ARABIC LETTER BEH INITIAL FORM +0xc9 0xfe93 #ARABIC LETTER TEH MARBUTA ISOLATED FORM +0xca 0xfe97 #ARABIC LETTER TEH INITIAL FORM +0xcb 0xfe9b #ARABIC LETTER THEH INITIAL FORM +0xcc 0xfe9f #ARABIC LETTER JEEM INITIAL FORM +0xcd 0xfea3 #ARABIC LETTER HAH INITIAL FORM +0xce 0xfea7 #ARABIC LETTER KHAH INITIAL FORM +0xcf 0xfea9 #ARABIC LETTER DAL ISOLATED FORM +0xd0 0xfeab #ARABIC LETTER THAL ISOLATED FORM +0xd1 0xfead #ARABIC LETTER REH ISOLATED FORM +0xd2 0xfeaf #ARABIC LETTER ZAIN ISOLATED FORM +0xd3 0xfeb3 #ARABIC LETTER SEEN INITIAL FORM +0xd4 0xfeb7 #ARABIC LETTER SHEEN INITIAL FORM +0xd5 0xfebb #ARABIC LETTER SAD INITIAL FORM +0xd6 0xfebf #ARABIC LETTER DAD INITIAL FORM +0xd7 0xfec1 #ARABIC LETTER TAH ISOLATED FORM +0xd8 0xfec5 #ARABIC LETTER ZAH ISOLATED FORM +0xd9 0xfecb #ARABIC LETTER AIN INITIAL FORM +0xda 0xfecf #ARABIC LETTER GHAIN INITIAL FORM +0xdb 0x00a6 #BROKEN VERTICAL BAR +0xdc 0x00ac #NOT SIGN +0xdd 0x00f7 #DIVISION SIGN +0xde 0x00d7 #MULTIPLICATION SIGN +0xdf 0xfec9 #ARABIC LETTER AIN ISOLATED FORM +0xe0 0x0640 #ARABIC TATWEEL +0xe1 0xfed3 #ARABIC LETTER FEH INITIAL FORM +0xe2 0xfed7 #ARABIC LETTER QAF INITIAL FORM +0xe3 0xfedb #ARABIC LETTER KAF INITIAL FORM +0xe4 0xfedf #ARABIC LETTER LAM INITIAL FORM +0xe5 0xfee3 #ARABIC LETTER MEEM INITIAL FORM +0xe6 0xfee7 #ARABIC LETTER NOON INITIAL FORM +0xe7 0xfeeb #ARABIC LETTER HEH INITIAL FORM +0xe8 0xfeed #ARABIC LETTER WAW ISOLATED FORM +0xe9 0xfeef #ARABIC LETTER ALEF MAKSURA ISOLATED FORM +0xea 0xfef3 #ARABIC LETTER YEH INITIAL FORM +0xeb 0xfebd #ARABIC LETTER DAD ISOLATED FORM +0xec 0xfecc #ARABIC LETTER AIN MEDIAL FORM +0xed 0xfece #ARABIC LETTER GHAIN FINAL FORM +0xee 0xfecd #ARABIC LETTER GHAIN ISOLATED FORM +0xef 0xfee1 #ARABIC LETTER MEEM ISOLATED FORM +0xf0 0xfe7d #ARABIC SHADDA MEDIAL FORM +0xf1 0x0651 #ARABIC SHADDAH +0xf2 0xfee5 #ARABIC LETTER NOON ISOLATED FORM +0xf3 0xfee9 #ARABIC LETTER HEH ISOLATED FORM +0xf4 0xfeec #ARABIC LETTER HEH MEDIAL FORM +0xf5 0xfef0 #ARABIC LETTER ALEF MAKSURA FINAL FORM +0xf6 0xfef2 #ARABIC LETTER YEH FINAL FORM +0xf7 0xfed0 #ARABIC LETTER GHAIN MEDIAL FORM +0xf8 0xfed5 #ARABIC LETTER QAF ISOLATED FORM +0xf9 0xfef5 #ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM +0xfa 0xfef6 #ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM +0xfb 0xfedd #ARABIC LETTER LAM ISOLATED FORM +0xfc 0xfed9 #ARABIC LETTER KAF ISOLATED FORM +0xfd 0xfef1 #ARABIC LETTER YEH ISOLATED FORM +0xfe 0x25a0 #BLACK SQUARE +0xff #UNDEFINED + + \ No newline at end of file diff --git a/codepage/cp865.txt b/codepage/cp865.txt new file mode 100644 index 0000000..41d73da --- /dev/null +++ b/codepage/cp865.txt @@ -0,0 +1,275 @@ +# +# Name: cp865_DOSNordic to Unicode table +# Unicode version: 2.0 +# Table version: 2.00 +# Table format: Format A +# Date: 04/24/96 +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp865_DOSNordic code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp865_DOSNordic order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0a 0x000a #LINE FEED +0x0b 0x000b #VERTICAL TABULATION +0x0c 0x000c #FORM FEED +0x0d 0x000d #CARRIAGE RETURN +0x0e 0x000e #SHIFT OUT +0x0f 0x000f #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1a 0x001a #SUBSTITUTE +0x1b 0x001b #ESCAPE +0x1c 0x001c #FILE SEPARATOR +0x1d 0x001d #GROUP SEPARATOR +0x1e 0x001e #RECORD SEPARATOR +0x1f 0x001f #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2a 0x002a #ASTERISK +0x2b 0x002b #PLUS SIGN +0x2c 0x002c #COMMA +0x2d 0x002d #HYPHEN-MINUS +0x2e 0x002e #FULL STOP +0x2f 0x002f #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3a 0x003a #COLON +0x3b 0x003b #SEMICOLON +0x3c 0x003c #LESS-THAN SIGN +0x3d 0x003d #EQUALS SIGN +0x3e 0x003e #GREATER-THAN SIGN +0x3f 0x003f #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4a 0x004a #LATIN CAPITAL LETTER J +0x4b 0x004b #LATIN CAPITAL LETTER K +0x4c 0x004c #LATIN CAPITAL LETTER L +0x4d 0x004d #LATIN CAPITAL LETTER M +0x4e 0x004e #LATIN CAPITAL LETTER N +0x4f 0x004f #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5a 0x005a #LATIN CAPITAL LETTER Z +0x5b 0x005b #LEFT SQUARE BRACKET +0x5c 0x005c #REVERSE SOLIDUS +0x5d 0x005d #RIGHT SQUARE BRACKET +0x5e 0x005e #CIRCUMFLEX ACCENT +0x5f 0x005f #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6a 0x006a #LATIN SMALL LETTER J +0x6b 0x006b #LATIN SMALL LETTER K +0x6c 0x006c #LATIN SMALL LETTER L +0x6d 0x006d #LATIN SMALL LETTER M +0x6e 0x006e #LATIN SMALL LETTER N +0x6f 0x006f #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7a 0x007a #LATIN SMALL LETTER Z +0x7b 0x007b #LEFT CURLY BRACKET +0x7c 0x007c #VERTICAL LINE +0x7d 0x007d #RIGHT CURLY BRACKET +0x7e 0x007e #TILDE +0x7f 0x007f #DELETE +0x80 0x00c7 #LATIN CAPITAL LETTER C WITH CEDILLA +0x81 0x00fc #LATIN SMALL LETTER U WITH DIAERESIS +0x82 0x00e9 #LATIN SMALL LETTER E WITH ACUTE +0x83 0x00e2 #LATIN SMALL LETTER A WITH CIRCUMFLEX +0x84 0x00e4 #LATIN SMALL LETTER A WITH DIAERESIS +0x85 0x00e0 #LATIN SMALL LETTER A WITH GRAVE +0x86 0x00e5 #LATIN SMALL LETTER A WITH RING ABOVE +0x87 0x00e7 #LATIN SMALL LETTER C WITH CEDILLA +0x88 0x00ea #LATIN SMALL LETTER E WITH CIRCUMFLEX +0x89 0x00eb #LATIN SMALL LETTER E WITH DIAERESIS +0x8a 0x00e8 #LATIN SMALL LETTER E WITH GRAVE +0x8b 0x00ef #LATIN SMALL LETTER I WITH DIAERESIS +0x8c 0x00ee #LATIN SMALL LETTER I WITH CIRCUMFLEX +0x8d 0x00ec #LATIN SMALL LETTER I WITH GRAVE +0x8e 0x00c4 #LATIN CAPITAL LETTER A WITH DIAERESIS +0x8f 0x00c5 #LATIN CAPITAL LETTER A WITH RING ABOVE +0x90 0x00c9 #LATIN CAPITAL LETTER E WITH ACUTE +0x91 0x00e6 #LATIN SMALL LIGATURE AE +0x92 0x00c6 #LATIN CAPITAL LIGATURE AE +0x93 0x00f4 #LATIN SMALL LETTER O WITH CIRCUMFLEX +0x94 0x00f6 #LATIN SMALL LETTER O WITH DIAERESIS +0x95 0x00f2 #LATIN SMALL LETTER O WITH GRAVE +0x96 0x00fb #LATIN SMALL LETTER U WITH CIRCUMFLEX +0x97 0x00f9 #LATIN SMALL LETTER U WITH GRAVE +0x98 0x00ff #LATIN SMALL LETTER Y WITH DIAERESIS +0x99 0x00d6 #LATIN CAPITAL LETTER O WITH DIAERESIS +0x9a 0x00dc #LATIN CAPITAL LETTER U WITH DIAERESIS +0x9b 0x00f8 #LATIN SMALL LETTER O WITH STROKE +0x9c 0x00a3 #POUND SIGN +0x9d 0x00d8 #LATIN CAPITAL LETTER O WITH STROKE +0x9e 0x20a7 #PESETA SIGN +0x9f 0x0192 #LATIN SMALL LETTER F WITH HOOK +0xa0 0x00e1 #LATIN SMALL LETTER A WITH ACUTE +0xa1 0x00ed #LATIN SMALL LETTER I WITH ACUTE +0xa2 0x00f3 #LATIN SMALL LETTER O WITH ACUTE +0xa3 0x00fa #LATIN SMALL LETTER U WITH ACUTE +0xa4 0x00f1 #LATIN SMALL LETTER N WITH TILDE +0xa5 0x00d1 #LATIN CAPITAL LETTER N WITH TILDE +0xa6 0x00aa #FEMININE ORDINAL INDICATOR +0xa7 0x00ba #MASCULINE ORDINAL INDICATOR +0xa8 0x00bf #INVERTED QUESTION MARK +0xa9 0x2310 #REVERSED NOT SIGN +0xaa 0x00ac #NOT SIGN +0xab 0x00bd #VULGAR FRACTION ONE HALF +0xac 0x00bc #VULGAR FRACTION ONE QUARTER +0xad 0x00a1 #INVERTED EXCLAMATION MARK +0xae 0x00ab #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xaf 0x00a4 #CURRENCY SIGN +0xb0 0x2591 #LIGHT SHADE +0xb1 0x2592 #MEDIUM SHADE +0xb2 0x2593 #DARK SHADE +0xb3 0x2502 #BOX DRAWINGS LIGHT VERTICAL +0xb4 0x2524 #BOX DRAWINGS LIGHT VERTICAL AND LEFT +0xb5 0x2561 #BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE +0xb6 0x2562 #BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE +0xb7 0x2556 #BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE +0xb8 0x2555 #BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE +0xb9 0x2563 #BOX DRAWINGS DOUBLE VERTICAL AND LEFT +0xba 0x2551 #BOX DRAWINGS DOUBLE VERTICAL +0xbb 0x2557 #BOX DRAWINGS DOUBLE DOWN AND LEFT +0xbc 0x255d #BOX DRAWINGS DOUBLE UP AND LEFT +0xbd 0x255c #BOX DRAWINGS UP DOUBLE AND LEFT SINGLE +0xbe 0x255b #BOX DRAWINGS UP SINGLE AND LEFT DOUBLE +0xbf 0x2510 #BOX DRAWINGS LIGHT DOWN AND LEFT +0xc0 0x2514 #BOX DRAWINGS LIGHT UP AND RIGHT +0xc1 0x2534 #BOX DRAWINGS LIGHT UP AND HORIZONTAL +0xc2 0x252c #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL +0xc3 0x251c #BOX DRAWINGS LIGHT VERTICAL AND RIGHT +0xc4 0x2500 #BOX DRAWINGS LIGHT HORIZONTAL +0xc5 0x253c #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL +0xc6 0x255e #BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE +0xc7 0x255f #BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE +0xc8 0x255a #BOX DRAWINGS DOUBLE UP AND RIGHT +0xc9 0x2554 #BOX DRAWINGS DOUBLE DOWN AND RIGHT +0xca 0x2569 #BOX DRAWINGS DOUBLE UP AND HORIZONTAL +0xcb 0x2566 #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL +0xcc 0x2560 #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT +0xcd 0x2550 #BOX DRAWINGS DOUBLE HORIZONTAL +0xce 0x256c #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL +0xcf 0x2567 #BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE +0xd0 0x2568 #BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE +0xd1 0x2564 #BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE +0xd2 0x2565 #BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE +0xd3 0x2559 #BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE +0xd4 0x2558 #BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE +0xd5 0x2552 #BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE +0xd6 0x2553 #BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE +0xd7 0x256b #BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE +0xd8 0x256a #BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE +0xd9 0x2518 #BOX DRAWINGS LIGHT UP AND LEFT +0xda 0x250c #BOX DRAWINGS LIGHT DOWN AND RIGHT +0xdb 0x2588 #FULL BLOCK +0xdc 0x2584 #LOWER HALF BLOCK +0xdd 0x258c #LEFT HALF BLOCK +0xde 0x2590 #RIGHT HALF BLOCK +0xdf 0x2580 #UPPER HALF BLOCK +0xe0 0x03b1 #GREEK SMALL LETTER ALPHA +0xe1 0x00df #LATIN SMALL LETTER SHARP S +0xe2 0x0393 #GREEK CAPITAL LETTER GAMMA +0xe3 0x03c0 #GREEK SMALL LETTER PI +0xe4 0x03a3 #GREEK CAPITAL LETTER SIGMA +0xe5 0x03c3 #GREEK SMALL LETTER SIGMA +0xe6 0x00b5 #MICRO SIGN +0xe7 0x03c4 #GREEK SMALL LETTER TAU +0xe8 0x03a6 #GREEK CAPITAL LETTER PHI +0xe9 0x0398 #GREEK CAPITAL LETTER THETA +0xea 0x03a9 #GREEK CAPITAL LETTER OMEGA +0xeb 0x03b4 #GREEK SMALL LETTER DELTA +0xec 0x221e #INFINITY +0xed 0x03c6 #GREEK SMALL LETTER PHI +0xee 0x03b5 #GREEK SMALL LETTER EPSILON +0xef 0x2229 #INTERSECTION +0xf0 0x2261 #IDENTICAL TO +0xf1 0x00b1 #PLUS-MINUS SIGN +0xf2 0x2265 #GREATER-THAN OR EQUAL TO +0xf3 0x2264 #LESS-THAN OR EQUAL TO +0xf4 0x2320 #TOP HALF INTEGRAL +0xf5 0x2321 #BOTTOM HALF INTEGRAL +0xf6 0x00f7 #DIVISION SIGN +0xf7 0x2248 #ALMOST EQUAL TO +0xf8 0x00b0 #DEGREE SIGN +0xf9 0x2219 #BULLET OPERATOR +0xfa 0x00b7 #MIDDLE DOT +0xfb 0x221a #SQUARE ROOT +0xfc 0x207f #SUPERSCRIPT LATIN SMALL LETTER N +0xfd 0x00b2 #SUPERSCRIPT TWO +0xfe 0x25a0 #BLACK SQUARE +0xff 0x00a0 #NO-BREAK SPACE + + \ No newline at end of file diff --git a/codepage/cp866.txt b/codepage/cp866.txt new file mode 100644 index 0000000..b0213a1 --- /dev/null +++ b/codepage/cp866.txt @@ -0,0 +1,275 @@ +# +# Name: cp866_DOSCyrillicRussian to Unicode table +# Unicode version: 2.0 +# Table version: 2.00 +# Table format: Format A +# Date: 04/24/96 +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp866_DOSCyrillicRussian code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp866_DOSCyrillicRussian order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0a 0x000a #LINE FEED +0x0b 0x000b #VERTICAL TABULATION +0x0c 0x000c #FORM FEED +0x0d 0x000d #CARRIAGE RETURN +0x0e 0x000e #SHIFT OUT +0x0f 0x000f #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1a 0x001a #SUBSTITUTE +0x1b 0x001b #ESCAPE +0x1c 0x001c #FILE SEPARATOR +0x1d 0x001d #GROUP SEPARATOR +0x1e 0x001e #RECORD SEPARATOR +0x1f 0x001f #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2a 0x002a #ASTERISK +0x2b 0x002b #PLUS SIGN +0x2c 0x002c #COMMA +0x2d 0x002d #HYPHEN-MINUS +0x2e 0x002e #FULL STOP +0x2f 0x002f #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3a 0x003a #COLON +0x3b 0x003b #SEMICOLON +0x3c 0x003c #LESS-THAN SIGN +0x3d 0x003d #EQUALS SIGN +0x3e 0x003e #GREATER-THAN SIGN +0x3f 0x003f #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4a 0x004a #LATIN CAPITAL LETTER J +0x4b 0x004b #LATIN CAPITAL LETTER K +0x4c 0x004c #LATIN CAPITAL LETTER L +0x4d 0x004d #LATIN CAPITAL LETTER M +0x4e 0x004e #LATIN CAPITAL LETTER N +0x4f 0x004f #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5a 0x005a #LATIN CAPITAL LETTER Z +0x5b 0x005b #LEFT SQUARE BRACKET +0x5c 0x005c #REVERSE SOLIDUS +0x5d 0x005d #RIGHT SQUARE BRACKET +0x5e 0x005e #CIRCUMFLEX ACCENT +0x5f 0x005f #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6a 0x006a #LATIN SMALL LETTER J +0x6b 0x006b #LATIN SMALL LETTER K +0x6c 0x006c #LATIN SMALL LETTER L +0x6d 0x006d #LATIN SMALL LETTER M +0x6e 0x006e #LATIN SMALL LETTER N +0x6f 0x006f #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7a 0x007a #LATIN SMALL LETTER Z +0x7b 0x007b #LEFT CURLY BRACKET +0x7c 0x007c #VERTICAL LINE +0x7d 0x007d #RIGHT CURLY BRACKET +0x7e 0x007e #TILDE +0x7f 0x007f #DELETE +0x80 0x0410 #CYRILLIC CAPITAL LETTER A +0x81 0x0411 #CYRILLIC CAPITAL LETTER BE +0x82 0x0412 #CYRILLIC CAPITAL LETTER VE +0x83 0x0413 #CYRILLIC CAPITAL LETTER GHE +0x84 0x0414 #CYRILLIC CAPITAL LETTER DE +0x85 0x0415 #CYRILLIC CAPITAL LETTER IE +0x86 0x0416 #CYRILLIC CAPITAL LETTER ZHE +0x87 0x0417 #CYRILLIC CAPITAL LETTER ZE +0x88 0x0418 #CYRILLIC CAPITAL LETTER I +0x89 0x0419 #CYRILLIC CAPITAL LETTER SHORT I +0x8a 0x041a #CYRILLIC CAPITAL LETTER KA +0x8b 0x041b #CYRILLIC CAPITAL LETTER EL +0x8c 0x041c #CYRILLIC CAPITAL LETTER EM +0x8d 0x041d #CYRILLIC CAPITAL LETTER EN +0x8e 0x041e #CYRILLIC CAPITAL LETTER O +0x8f 0x041f #CYRILLIC CAPITAL LETTER PE +0x90 0x0420 #CYRILLIC CAPITAL LETTER ER +0x91 0x0421 #CYRILLIC CAPITAL LETTER ES +0x92 0x0422 #CYRILLIC CAPITAL LETTER TE +0x93 0x0423 #CYRILLIC CAPITAL LETTER U +0x94 0x0424 #CYRILLIC CAPITAL LETTER EF +0x95 0x0425 #CYRILLIC CAPITAL LETTER HA +0x96 0x0426 #CYRILLIC CAPITAL LETTER TSE +0x97 0x0427 #CYRILLIC CAPITAL LETTER CHE +0x98 0x0428 #CYRILLIC CAPITAL LETTER SHA +0x99 0x0429 #CYRILLIC CAPITAL LETTER SHCHA +0x9a 0x042a #CYRILLIC CAPITAL LETTER HARD SIGN +0x9b 0x042b #CYRILLIC CAPITAL LETTER YERU +0x9c 0x042c #CYRILLIC CAPITAL LETTER SOFT SIGN +0x9d 0x042d #CYRILLIC CAPITAL LETTER E +0x9e 0x042e #CYRILLIC CAPITAL LETTER YU +0x9f 0x042f #CYRILLIC CAPITAL LETTER YA +0xa0 0x0430 #CYRILLIC SMALL LETTER A +0xa1 0x0431 #CYRILLIC SMALL LETTER BE +0xa2 0x0432 #CYRILLIC SMALL LETTER VE +0xa3 0x0433 #CYRILLIC SMALL LETTER GHE +0xa4 0x0434 #CYRILLIC SMALL LETTER DE +0xa5 0x0435 #CYRILLIC SMALL LETTER IE +0xa6 0x0436 #CYRILLIC SMALL LETTER ZHE +0xa7 0x0437 #CYRILLIC SMALL LETTER ZE +0xa8 0x0438 #CYRILLIC SMALL LETTER I +0xa9 0x0439 #CYRILLIC SMALL LETTER SHORT I +0xaa 0x043a #CYRILLIC SMALL LETTER KA +0xab 0x043b #CYRILLIC SMALL LETTER EL +0xac 0x043c #CYRILLIC SMALL LETTER EM +0xad 0x043d #CYRILLIC SMALL LETTER EN +0xae 0x043e #CYRILLIC SMALL LETTER O +0xaf 0x043f #CYRILLIC SMALL LETTER PE +0xb0 0x2591 #LIGHT SHADE +0xb1 0x2592 #MEDIUM SHADE +0xb2 0x2593 #DARK SHADE +0xb3 0x2502 #BOX DRAWINGS LIGHT VERTICAL +0xb4 0x2524 #BOX DRAWINGS LIGHT VERTICAL AND LEFT +0xb5 0x2561 #BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE +0xb6 0x2562 #BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE +0xb7 0x2556 #BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE +0xb8 0x2555 #BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE +0xb9 0x2563 #BOX DRAWINGS DOUBLE VERTICAL AND LEFT +0xba 0x2551 #BOX DRAWINGS DOUBLE VERTICAL +0xbb 0x2557 #BOX DRAWINGS DOUBLE DOWN AND LEFT +0xbc 0x255d #BOX DRAWINGS DOUBLE UP AND LEFT +0xbd 0x255c #BOX DRAWINGS UP DOUBLE AND LEFT SINGLE +0xbe 0x255b #BOX DRAWINGS UP SINGLE AND LEFT DOUBLE +0xbf 0x2510 #BOX DRAWINGS LIGHT DOWN AND LEFT +0xc0 0x2514 #BOX DRAWINGS LIGHT UP AND RIGHT +0xc1 0x2534 #BOX DRAWINGS LIGHT UP AND HORIZONTAL +0xc2 0x252c #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL +0xc3 0x251c #BOX DRAWINGS LIGHT VERTICAL AND RIGHT +0xc4 0x2500 #BOX DRAWINGS LIGHT HORIZONTAL +0xc5 0x253c #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL +0xc6 0x255e #BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE +0xc7 0x255f #BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE +0xc8 0x255a #BOX DRAWINGS DOUBLE UP AND RIGHT +0xc9 0x2554 #BOX DRAWINGS DOUBLE DOWN AND RIGHT +0xca 0x2569 #BOX DRAWINGS DOUBLE UP AND HORIZONTAL +0xcb 0x2566 #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL +0xcc 0x2560 #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT +0xcd 0x2550 #BOX DRAWINGS DOUBLE HORIZONTAL +0xce 0x256c #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL +0xcf 0x2567 #BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE +0xd0 0x2568 #BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE +0xd1 0x2564 #BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE +0xd2 0x2565 #BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE +0xd3 0x2559 #BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE +0xd4 0x2558 #BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE +0xd5 0x2552 #BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE +0xd6 0x2553 #BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE +0xd7 0x256b #BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE +0xd8 0x256a #BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE +0xd9 0x2518 #BOX DRAWINGS LIGHT UP AND LEFT +0xda 0x250c #BOX DRAWINGS LIGHT DOWN AND RIGHT +0xdb 0x2588 #FULL BLOCK +0xdc 0x2584 #LOWER HALF BLOCK +0xdd 0x258c #LEFT HALF BLOCK +0xde 0x2590 #RIGHT HALF BLOCK +0xdf 0x2580 #UPPER HALF BLOCK +0xe0 0x0440 #CYRILLIC SMALL LETTER ER +0xe1 0x0441 #CYRILLIC SMALL LETTER ES +0xe2 0x0442 #CYRILLIC SMALL LETTER TE +0xe3 0x0443 #CYRILLIC SMALL LETTER U +0xe4 0x0444 #CYRILLIC SMALL LETTER EF +0xe5 0x0445 #CYRILLIC SMALL LETTER HA +0xe6 0x0446 #CYRILLIC SMALL LETTER TSE +0xe7 0x0447 #CYRILLIC SMALL LETTER CHE +0xe8 0x0448 #CYRILLIC SMALL LETTER SHA +0xe9 0x0449 #CYRILLIC SMALL LETTER SHCHA +0xea 0x044a #CYRILLIC SMALL LETTER HARD SIGN +0xeb 0x044b #CYRILLIC SMALL LETTER YERU +0xec 0x044c #CYRILLIC SMALL LETTER SOFT SIGN +0xed 0x044d #CYRILLIC SMALL LETTER E +0xee 0x044e #CYRILLIC SMALL LETTER YU +0xef 0x044f #CYRILLIC SMALL LETTER YA +0xf0 0x0401 #CYRILLIC CAPITAL LETTER IO +0xf1 0x0451 #CYRILLIC SMALL LETTER IO +0xf2 0x0404 #CYRILLIC CAPITAL LETTER UKRAINIAN IE +0xf3 0x0454 #CYRILLIC SMALL LETTER UKRAINIAN IE +0xf4 0x0407 #CYRILLIC CAPITAL LETTER YI +0xf5 0x0457 #CYRILLIC SMALL LETTER YI +0xf6 0x040e #CYRILLIC CAPITAL LETTER SHORT U +0xf7 0x045e #CYRILLIC SMALL LETTER SHORT U +0xf8 0x00b0 #DEGREE SIGN +0xf9 0x2219 #BULLET OPERATOR +0xfa 0x00b7 #MIDDLE DOT +0xfb 0x221a #SQUARE ROOT +0xfc 0x2116 #NUMERO SIGN +0xfd 0x00a4 #CURRENCY SIGN +0xfe 0x25a0 #BLACK SQUARE +0xff 0x00a0 #NO-BREAK SPACE + + \ No newline at end of file diff --git a/codepage/cp869.txt b/codepage/cp869.txt new file mode 100644 index 0000000..3cd3f77 --- /dev/null +++ b/codepage/cp869.txt @@ -0,0 +1,275 @@ +# +# Name: cp869_DOSGreek2 to Unicode table +# Unicode version: 2.0 +# Table version: 2.00 +# Table format: Format A +# Date: 04/24/96 +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp869_DOSGreek2 code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp869_DOSGreek2 order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0a 0x000a #LINE FEED +0x0b 0x000b #VERTICAL TABULATION +0x0c 0x000c #FORM FEED +0x0d 0x000d #CARRIAGE RETURN +0x0e 0x000e #SHIFT OUT +0x0f 0x000f #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1a 0x001a #SUBSTITUTE +0x1b 0x001b #ESCAPE +0x1c 0x001c #FILE SEPARATOR +0x1d 0x001d #GROUP SEPARATOR +0x1e 0x001e #RECORD SEPARATOR +0x1f 0x001f #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2a 0x002a #ASTERISK +0x2b 0x002b #PLUS SIGN +0x2c 0x002c #COMMA +0x2d 0x002d #HYPHEN-MINUS +0x2e 0x002e #FULL STOP +0x2f 0x002f #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3a 0x003a #COLON +0x3b 0x003b #SEMICOLON +0x3c 0x003c #LESS-THAN SIGN +0x3d 0x003d #EQUALS SIGN +0x3e 0x003e #GREATER-THAN SIGN +0x3f 0x003f #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4a 0x004a #LATIN CAPITAL LETTER J +0x4b 0x004b #LATIN CAPITAL LETTER K +0x4c 0x004c #LATIN CAPITAL LETTER L +0x4d 0x004d #LATIN CAPITAL LETTER M +0x4e 0x004e #LATIN CAPITAL LETTER N +0x4f 0x004f #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5a 0x005a #LATIN CAPITAL LETTER Z +0x5b 0x005b #LEFT SQUARE BRACKET +0x5c 0x005c #REVERSE SOLIDUS +0x5d 0x005d #RIGHT SQUARE BRACKET +0x5e 0x005e #CIRCUMFLEX ACCENT +0x5f 0x005f #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6a 0x006a #LATIN SMALL LETTER J +0x6b 0x006b #LATIN SMALL LETTER K +0x6c 0x006c #LATIN SMALL LETTER L +0x6d 0x006d #LATIN SMALL LETTER M +0x6e 0x006e #LATIN SMALL LETTER N +0x6f 0x006f #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7a 0x007a #LATIN SMALL LETTER Z +0x7b 0x007b #LEFT CURLY BRACKET +0x7c 0x007c #VERTICAL LINE +0x7d 0x007d #RIGHT CURLY BRACKET +0x7e 0x007e #TILDE +0x7f 0x007f #DELETE +0x80 #UNDEFINED +0x81 #UNDEFINED +0x82 #UNDEFINED +0x83 #UNDEFINED +0x84 #UNDEFINED +0x85 #UNDEFINED +0x86 0x0386 #GREEK CAPITAL LETTER ALPHA WITH TONOS +0x87 #UNDEFINED +0x88 0x00b7 #MIDDLE DOT +0x89 0x00ac #NOT SIGN +0x8a 0x00a6 #BROKEN BAR +0x8b 0x2018 #LEFT SINGLE QUOTATION MARK +0x8c 0x2019 #RIGHT SINGLE QUOTATION MARK +0x8d 0x0388 #GREEK CAPITAL LETTER EPSILON WITH TONOS +0x8e 0x2015 #HORIZONTAL BAR +0x8f 0x0389 #GREEK CAPITAL LETTER ETA WITH TONOS +0x90 0x038a #GREEK CAPITAL LETTER IOTA WITH TONOS +0x91 0x03aa #GREEK CAPITAL LETTER IOTA WITH DIALYTIKA +0x92 0x038c #GREEK CAPITAL LETTER OMICRON WITH TONOS +0x93 #UNDEFINED +0x94 #UNDEFINED +0x95 0x038e #GREEK CAPITAL LETTER UPSILON WITH TONOS +0x96 0x03ab #GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA +0x97 0x00a9 #COPYRIGHT SIGN +0x98 0x038f #GREEK CAPITAL LETTER OMEGA WITH TONOS +0x99 0x00b2 #SUPERSCRIPT TWO +0x9a 0x00b3 #SUPERSCRIPT THREE +0x9b 0x03ac #GREEK SMALL LETTER ALPHA WITH TONOS +0x9c 0x00a3 #POUND SIGN +0x9d 0x03ad #GREEK SMALL LETTER EPSILON WITH TONOS +0x9e 0x03ae #GREEK SMALL LETTER ETA WITH TONOS +0x9f 0x03af #GREEK SMALL LETTER IOTA WITH TONOS +0xa0 0x03ca #GREEK SMALL LETTER IOTA WITH DIALYTIKA +0xa1 0x0390 #GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS +0xa2 0x03cc #GREEK SMALL LETTER OMICRON WITH TONOS +0xa3 0x03cd #GREEK SMALL LETTER UPSILON WITH TONOS +0xa4 0x0391 #GREEK CAPITAL LETTER ALPHA +0xa5 0x0392 #GREEK CAPITAL LETTER BETA +0xa6 0x0393 #GREEK CAPITAL LETTER GAMMA +0xa7 0x0394 #GREEK CAPITAL LETTER DELTA +0xa8 0x0395 #GREEK CAPITAL LETTER EPSILON +0xa9 0x0396 #GREEK CAPITAL LETTER ZETA +0xaa 0x0397 #GREEK CAPITAL LETTER ETA +0xab 0x00bd #VULGAR FRACTION ONE HALF +0xac 0x0398 #GREEK CAPITAL LETTER THETA +0xad 0x0399 #GREEK CAPITAL LETTER IOTA +0xae 0x00ab #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xaf 0x00bb #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xb0 0x2591 #LIGHT SHADE +0xb1 0x2592 #MEDIUM SHADE +0xb2 0x2593 #DARK SHADE +0xb3 0x2502 #BOX DRAWINGS LIGHT VERTICAL +0xb4 0x2524 #BOX DRAWINGS LIGHT VERTICAL AND LEFT +0xb5 0x039a #GREEK CAPITAL LETTER KAPPA +0xb6 0x039b #GREEK CAPITAL LETTER LAMDA +0xb7 0x039c #GREEK CAPITAL LETTER MU +0xb8 0x039d #GREEK CAPITAL LETTER NU +0xb9 0x2563 #BOX DRAWINGS DOUBLE VERTICAL AND LEFT +0xba 0x2551 #BOX DRAWINGS DOUBLE VERTICAL +0xbb 0x2557 #BOX DRAWINGS DOUBLE DOWN AND LEFT +0xbc 0x255d #BOX DRAWINGS DOUBLE UP AND LEFT +0xbd 0x039e #GREEK CAPITAL LETTER XI +0xbe 0x039f #GREEK CAPITAL LETTER OMICRON +0xbf 0x2510 #BOX DRAWINGS LIGHT DOWN AND LEFT +0xc0 0x2514 #BOX DRAWINGS LIGHT UP AND RIGHT +0xc1 0x2534 #BOX DRAWINGS LIGHT UP AND HORIZONTAL +0xc2 0x252c #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL +0xc3 0x251c #BOX DRAWINGS LIGHT VERTICAL AND RIGHT +0xc4 0x2500 #BOX DRAWINGS LIGHT HORIZONTAL +0xc5 0x253c #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL +0xc6 0x03a0 #GREEK CAPITAL LETTER PI +0xc7 0x03a1 #GREEK CAPITAL LETTER RHO +0xc8 0x255a #BOX DRAWINGS DOUBLE UP AND RIGHT +0xc9 0x2554 #BOX DRAWINGS DOUBLE DOWN AND RIGHT +0xca 0x2569 #BOX DRAWINGS DOUBLE UP AND HORIZONTAL +0xcb 0x2566 #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL +0xcc 0x2560 #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT +0xcd 0x2550 #BOX DRAWINGS DOUBLE HORIZONTAL +0xce 0x256c #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL +0xcf 0x03a3 #GREEK CAPITAL LETTER SIGMA +0xd0 0x03a4 #GREEK CAPITAL LETTER TAU +0xd1 0x03a5 #GREEK CAPITAL LETTER UPSILON +0xd2 0x03a6 #GREEK CAPITAL LETTER PHI +0xd3 0x03a7 #GREEK CAPITAL LETTER CHI +0xd4 0x03a8 #GREEK CAPITAL LETTER PSI +0xd5 0x03a9 #GREEK CAPITAL LETTER OMEGA +0xd6 0x03b1 #GREEK SMALL LETTER ALPHA +0xd7 0x03b2 #GREEK SMALL LETTER BETA +0xd8 0x03b3 #GREEK SMALL LETTER GAMMA +0xd9 0x2518 #BOX DRAWINGS LIGHT UP AND LEFT +0xda 0x250c #BOX DRAWINGS LIGHT DOWN AND RIGHT +0xdb 0x2588 #FULL BLOCK +0xdc 0x2584 #LOWER HALF BLOCK +0xdd 0x03b4 #GREEK SMALL LETTER DELTA +0xde 0x03b5 #GREEK SMALL LETTER EPSILON +0xdf 0x2580 #UPPER HALF BLOCK +0xe0 0x03b6 #GREEK SMALL LETTER ZETA +0xe1 0x03b7 #GREEK SMALL LETTER ETA +0xe2 0x03b8 #GREEK SMALL LETTER THETA +0xe3 0x03b9 #GREEK SMALL LETTER IOTA +0xe4 0x03ba #GREEK SMALL LETTER KAPPA +0xe5 0x03bb #GREEK SMALL LETTER LAMDA +0xe6 0x03bc #GREEK SMALL LETTER MU +0xe7 0x03bd #GREEK SMALL LETTER NU +0xe8 0x03be #GREEK SMALL LETTER XI +0xe9 0x03bf #GREEK SMALL LETTER OMICRON +0xea 0x03c0 #GREEK SMALL LETTER PI +0xeb 0x03c1 #GREEK SMALL LETTER RHO +0xec 0x03c3 #GREEK SMALL LETTER SIGMA +0xed 0x03c2 #GREEK SMALL LETTER FINAL SIGMA +0xee 0x03c4 #GREEK SMALL LETTER TAU +0xef 0x0384 #GREEK TONOS +0xf0 0x00ad #SOFT HYPHEN +0xf1 0x00b1 #PLUS-MINUS SIGN +0xf2 0x03c5 #GREEK SMALL LETTER UPSILON +0xf3 0x03c6 #GREEK SMALL LETTER PHI +0xf4 0x03c7 #GREEK SMALL LETTER CHI +0xf5 0x00a7 #SECTION SIGN +0xf6 0x03c8 #GREEK SMALL LETTER PSI +0xf7 0x0385 #GREEK DIALYTIKA TONOS +0xf8 0x00b0 #DEGREE SIGN +0xf9 0x00a8 #DIAERESIS +0xfa 0x03c9 #GREEK SMALL LETTER OMEGA +0xfb 0x03cb #GREEK SMALL LETTER UPSILON WITH DIALYTIKA +0xfc 0x03b0 #GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS +0xfd 0x03ce #GREEK SMALL LETTER OMEGA WITH TONOS +0xfe 0x25a0 #BLACK SQUARE +0xff 0x00a0 #NO-BREAK SPACE + + \ No newline at end of file diff --git a/codepage/cp874.txt b/codepage/cp874.txt new file mode 100644 index 0000000..0ab6504 --- /dev/null +++ b/codepage/cp874.txt @@ -0,0 +1,274 @@ +# +# Name: cp874 to Unicode table +# Unicode version: 2.0 +# Table version: 2.01 +# Table format: Format A +# Date: 02/28/98 +# +# Contact: Shawn.Steele@microsoft.com +# +# General notes: none +# +# Format: Three tab-separated columns +# Column #1 is the cp874 code (in hex) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in cp874 order +# +0x00 0x0000 #NULL +0x01 0x0001 #START OF HEADING +0x02 0x0002 #START OF TEXT +0x03 0x0003 #END OF TEXT +0x04 0x0004 #END OF TRANSMISSION +0x05 0x0005 #ENQUIRY +0x06 0x0006 #ACKNOWLEDGE +0x07 0x0007 #BELL +0x08 0x0008 #BACKSPACE +0x09 0x0009 #HORIZONTAL TABULATION +0x0A 0x000A #LINE FEED +0x0B 0x000B #VERTICAL TABULATION +0x0C 0x000C #FORM FEED +0x0D 0x000D #CARRIAGE RETURN +0x0E 0x000E #SHIFT OUT +0x0F 0x000F #SHIFT IN +0x10 0x0010 #DATA LINK ESCAPE +0x11 0x0011 #DEVICE CONTROL ONE +0x12 0x0012 #DEVICE CONTROL TWO +0x13 0x0013 #DEVICE CONTROL THREE +0x14 0x0014 #DEVICE CONTROL FOUR +0x15 0x0015 #NEGATIVE ACKNOWLEDGE +0x16 0x0016 #SYNCHRONOUS IDLE +0x17 0x0017 #END OF TRANSMISSION BLOCK +0x18 0x0018 #CANCEL +0x19 0x0019 #END OF MEDIUM +0x1A 0x001A #SUBSTITUTE +0x1B 0x001B #ESCAPE +0x1C 0x001C #FILE SEPARATOR +0x1D 0x001D #GROUP SEPARATOR +0x1E 0x001E #RECORD SEPARATOR +0x1F 0x001F #UNIT SEPARATOR +0x20 0x0020 #SPACE +0x21 0x0021 #EXCLAMATION MARK +0x22 0x0022 #QUOTATION MARK +0x23 0x0023 #NUMBER SIGN +0x24 0x0024 #DOLLAR SIGN +0x25 0x0025 #PERCENT SIGN +0x26 0x0026 #AMPERSAND +0x27 0x0027 #APOSTROPHE +0x28 0x0028 #LEFT PARENTHESIS +0x29 0x0029 #RIGHT PARENTHESIS +0x2A 0x002A #ASTERISK +0x2B 0x002B #PLUS SIGN +0x2C 0x002C #COMMA +0x2D 0x002D #HYPHEN-MINUS +0x2E 0x002E #FULL STOP +0x2F 0x002F #SOLIDUS +0x30 0x0030 #DIGIT ZERO +0x31 0x0031 #DIGIT ONE +0x32 0x0032 #DIGIT TWO +0x33 0x0033 #DIGIT THREE +0x34 0x0034 #DIGIT FOUR +0x35 0x0035 #DIGIT FIVE +0x36 0x0036 #DIGIT SIX +0x37 0x0037 #DIGIT SEVEN +0x38 0x0038 #DIGIT EIGHT +0x39 0x0039 #DIGIT NINE +0x3A 0x003A #COLON +0x3B 0x003B #SEMICOLON +0x3C 0x003C #LESS-THAN SIGN +0x3D 0x003D #EQUALS SIGN +0x3E 0x003E #GREATER-THAN SIGN +0x3F 0x003F #QUESTION MARK +0x40 0x0040 #COMMERCIAL AT +0x41 0x0041 #LATIN CAPITAL LETTER A +0x42 0x0042 #LATIN CAPITAL LETTER B +0x43 0x0043 #LATIN CAPITAL LETTER C +0x44 0x0044 #LATIN CAPITAL LETTER D +0x45 0x0045 #LATIN CAPITAL LETTER E +0x46 0x0046 #LATIN CAPITAL LETTER F +0x47 0x0047 #LATIN CAPITAL LETTER G +0x48 0x0048 #LATIN CAPITAL LETTER H +0x49 0x0049 #LATIN CAPITAL LETTER I +0x4A 0x004A #LATIN CAPITAL LETTER J +0x4B 0x004B #LATIN CAPITAL LETTER K +0x4C 0x004C #LATIN CAPITAL LETTER L +0x4D 0x004D #LATIN CAPITAL LETTER M +0x4E 0x004E #LATIN CAPITAL LETTER N +0x4F 0x004F #LATIN CAPITAL LETTER O +0x50 0x0050 #LATIN CAPITAL LETTER P +0x51 0x0051 #LATIN CAPITAL LETTER Q +0x52 0x0052 #LATIN CAPITAL LETTER R +0x53 0x0053 #LATIN CAPITAL LETTER S +0x54 0x0054 #LATIN CAPITAL LETTER T +0x55 0x0055 #LATIN CAPITAL LETTER U +0x56 0x0056 #LATIN CAPITAL LETTER V +0x57 0x0057 #LATIN CAPITAL LETTER W +0x58 0x0058 #LATIN CAPITAL LETTER X +0x59 0x0059 #LATIN CAPITAL LETTER Y +0x5A 0x005A #LATIN CAPITAL LETTER Z +0x5B 0x005B #LEFT SQUARE BRACKET +0x5C 0x005C #REVERSE SOLIDUS +0x5D 0x005D #RIGHT SQUARE BRACKET +0x5E 0x005E #CIRCUMFLEX ACCENT +0x5F 0x005F #LOW LINE +0x60 0x0060 #GRAVE ACCENT +0x61 0x0061 #LATIN SMALL LETTER A +0x62 0x0062 #LATIN SMALL LETTER B +0x63 0x0063 #LATIN SMALL LETTER C +0x64 0x0064 #LATIN SMALL LETTER D +0x65 0x0065 #LATIN SMALL LETTER E +0x66 0x0066 #LATIN SMALL LETTER F +0x67 0x0067 #LATIN SMALL LETTER G +0x68 0x0068 #LATIN SMALL LETTER H +0x69 0x0069 #LATIN SMALL LETTER I +0x6A 0x006A #LATIN SMALL LETTER J +0x6B 0x006B #LATIN SMALL LETTER K +0x6C 0x006C #LATIN SMALL LETTER L +0x6D 0x006D #LATIN SMALL LETTER M +0x6E 0x006E #LATIN SMALL LETTER N +0x6F 0x006F #LATIN SMALL LETTER O +0x70 0x0070 #LATIN SMALL LETTER P +0x71 0x0071 #LATIN SMALL LETTER Q +0x72 0x0072 #LATIN SMALL LETTER R +0x73 0x0073 #LATIN SMALL LETTER S +0x74 0x0074 #LATIN SMALL LETTER T +0x75 0x0075 #LATIN SMALL LETTER U +0x76 0x0076 #LATIN SMALL LETTER V +0x77 0x0077 #LATIN SMALL LETTER W +0x78 0x0078 #LATIN SMALL LETTER X +0x79 0x0079 #LATIN SMALL LETTER Y +0x7A 0x007A #LATIN SMALL LETTER Z +0x7B 0x007B #LEFT CURLY BRACKET +0x7C 0x007C #VERTICAL LINE +0x7D 0x007D #RIGHT CURLY BRACKET +0x7E 0x007E #TILDE +0x7F 0x007F #DELETE +0x80 0x20AC #EURO SIGN +0x81 #UNDEFINED +0x82 #UNDEFINED +0x83 #UNDEFINED +0x84 #UNDEFINED +0x85 0x2026 #HORIZONTAL ELLIPSIS +0x86 #UNDEFINED +0x87 #UNDEFINED +0x88 #UNDEFINED +0x89 #UNDEFINED +0x8A #UNDEFINED +0x8B #UNDEFINED +0x8C #UNDEFINED +0x8D #UNDEFINED +0x8E #UNDEFINED +0x8F #UNDEFINED +0x90 #UNDEFINED +0x91 0x2018 #LEFT SINGLE QUOTATION MARK +0x92 0x2019 #RIGHT SINGLE QUOTATION MARK +0x93 0x201C #LEFT DOUBLE QUOTATION MARK +0x94 0x201D #RIGHT DOUBLE QUOTATION MARK +0x95 0x2022 #BULLET +0x96 0x2013 #EN DASH +0x97 0x2014 #EM DASH +0x98 #UNDEFINED +0x99 #UNDEFINED +0x9A #UNDEFINED +0x9B #UNDEFINED +0x9C #UNDEFINED +0x9D #UNDEFINED +0x9E #UNDEFINED +0x9F #UNDEFINED +0xA0 0x00A0 #NO-BREAK SPACE +0xA1 0x0E01 #THAI CHARACTER KO KAI +0xA2 0x0E02 #THAI CHARACTER KHO KHAI +0xA3 0x0E03 #THAI CHARACTER KHO KHUAT +0xA4 0x0E04 #THAI CHARACTER KHO KHWAI +0xA5 0x0E05 #THAI CHARACTER KHO KHON +0xA6 0x0E06 #THAI CHARACTER KHO RAKHANG +0xA7 0x0E07 #THAI CHARACTER NGO NGU +0xA8 0x0E08 #THAI CHARACTER CHO CHAN +0xA9 0x0E09 #THAI CHARACTER CHO CHING +0xAA 0x0E0A #THAI CHARACTER CHO CHANG +0xAB 0x0E0B #THAI CHARACTER SO SO +0xAC 0x0E0C #THAI CHARACTER CHO CHOE +0xAD 0x0E0D #THAI CHARACTER YO YING +0xAE 0x0E0E #THAI CHARACTER DO CHADA +0xAF 0x0E0F #THAI CHARACTER TO PATAK +0xB0 0x0E10 #THAI CHARACTER THO THAN +0xB1 0x0E11 #THAI CHARACTER THO NANGMONTHO +0xB2 0x0E12 #THAI CHARACTER THO PHUTHAO +0xB3 0x0E13 #THAI CHARACTER NO NEN +0xB4 0x0E14 #THAI CHARACTER DO DEK +0xB5 0x0E15 #THAI CHARACTER TO TAO +0xB6 0x0E16 #THAI CHARACTER THO THUNG +0xB7 0x0E17 #THAI CHARACTER THO THAHAN +0xB8 0x0E18 #THAI CHARACTER THO THONG +0xB9 0x0E19 #THAI CHARACTER NO NU +0xBA 0x0E1A #THAI CHARACTER BO BAIMAI +0xBB 0x0E1B #THAI CHARACTER PO PLA +0xBC 0x0E1C #THAI CHARACTER PHO PHUNG +0xBD 0x0E1D #THAI CHARACTER FO FA +0xBE 0x0E1E #THAI CHARACTER PHO PHAN +0xBF 0x0E1F #THAI CHARACTER FO FAN +0xC0 0x0E20 #THAI CHARACTER PHO SAMPHAO +0xC1 0x0E21 #THAI CHARACTER MO MA +0xC2 0x0E22 #THAI CHARACTER YO YAK +0xC3 0x0E23 #THAI CHARACTER RO RUA +0xC4 0x0E24 #THAI CHARACTER RU +0xC5 0x0E25 #THAI CHARACTER LO LING +0xC6 0x0E26 #THAI CHARACTER LU +0xC7 0x0E27 #THAI CHARACTER WO WAEN +0xC8 0x0E28 #THAI CHARACTER SO SALA +0xC9 0x0E29 #THAI CHARACTER SO RUSI +0xCA 0x0E2A #THAI CHARACTER SO SUA +0xCB 0x0E2B #THAI CHARACTER HO HIP +0xCC 0x0E2C #THAI CHARACTER LO CHULA +0xCD 0x0E2D #THAI CHARACTER O ANG +0xCE 0x0E2E #THAI CHARACTER HO NOKHUK +0xCF 0x0E2F #THAI CHARACTER PAIYANNOI +0xD0 0x0E30 #THAI CHARACTER SARA A +0xD1 0x0E31 #THAI CHARACTER MAI HAN-AKAT +0xD2 0x0E32 #THAI CHARACTER SARA AA +0xD3 0x0E33 #THAI CHARACTER SARA AM +0xD4 0x0E34 #THAI CHARACTER SARA I +0xD5 0x0E35 #THAI CHARACTER SARA II +0xD6 0x0E36 #THAI CHARACTER SARA UE +0xD7 0x0E37 #THAI CHARACTER SARA UEE +0xD8 0x0E38 #THAI CHARACTER SARA U +0xD9 0x0E39 #THAI CHARACTER SARA UU +0xDA 0x0E3A #THAI CHARACTER PHINTHU +0xDB #UNDEFINED +0xDC #UNDEFINED +0xDD #UNDEFINED +0xDE #UNDEFINED +0xDF 0x0E3F #THAI CURRENCY SYMBOL BAHT +0xE0 0x0E40 #THAI CHARACTER SARA E +0xE1 0x0E41 #THAI CHARACTER SARA AE +0xE2 0x0E42 #THAI CHARACTER SARA O +0xE3 0x0E43 #THAI CHARACTER SARA AI MAIMUAN +0xE4 0x0E44 #THAI CHARACTER SARA AI MAIMALAI +0xE5 0x0E45 #THAI CHARACTER LAKKHANGYAO +0xE6 0x0E46 #THAI CHARACTER MAIYAMOK +0xE7 0x0E47 #THAI CHARACTER MAITAIKHU +0xE8 0x0E48 #THAI CHARACTER MAI EK +0xE9 0x0E49 #THAI CHARACTER MAI THO +0xEA 0x0E4A #THAI CHARACTER MAI TRI +0xEB 0x0E4B #THAI CHARACTER MAI CHATTAWA +0xEC 0x0E4C #THAI CHARACTER THANTHAKHAT +0xED 0x0E4D #THAI CHARACTER NIKHAHIT +0xEE 0x0E4E #THAI CHARACTER YAMAKKAN +0xEF 0x0E4F #THAI CHARACTER FONGMAN +0xF0 0x0E50 #THAI DIGIT ZERO +0xF1 0x0E51 #THAI DIGIT ONE +0xF2 0x0E52 #THAI DIGIT TWO +0xF3 0x0E53 #THAI DIGIT THREE +0xF4 0x0E54 #THAI DIGIT FOUR +0xF5 0x0E55 #THAI DIGIT FIVE +0xF6 0x0E56 #THAI DIGIT SIX +0xF7 0x0E57 #THAI DIGIT SEVEN +0xF8 0x0E58 #THAI DIGIT EIGHT +0xF9 0x0E59 #THAI DIGIT NINE +0xFA 0x0E5A #THAI CHARACTER ANGKHANKHU +0xFB 0x0E5B #THAI CHARACTER KHOMUT +0xFC #UNDEFINED +0xFD #UNDEFINED +0xFE #UNDEFINED +0xFF #UNDEFINED diff --git a/codepage/cptable.pl b/codepage/cptable.pl new file mode 100755 index 0000000..05cfc3e --- /dev/null +++ b/codepage/cptable.pl @@ -0,0 +1,146 @@ +#!/usr/bin/perl +# +# Produce a codepage matching table. For each 8-bit character, list +# a primary and an alternate match (the latter used for case-insensitive +# matching.) +# +# Usage: +# cptable.pl UnicodeData console-cp.txt filesystem-cp.txt output.cp +# +# Note: for the format of the UnicodeData file, see: +# http://www.unicode.org/Public/UNIDATA/UCD.html +# + +($ucd, $cpco, $cpfs, $cpout) = @ARGV; + +if (!defined($cpout)) { + die "Usage: $0 UnicodeData console-cp.txt fs-cp.txt output.cp\n"; +} + +%ucase = (); +%lcase = (); +%tcase = (); +%decomp = (); + +open(UCD, '<', $ucd) + or die "$0: could not open unicode data: $ucd: $!\n"; +while (defined($line = )) { + chomp $line; + @f = split(/;/, $line); + $n = hex $f[0]; + $ucase{$n} = ($f[12] ne '') ? hex $f[12] : $n; + $lcase{$n} = ($f[13] ne '') ? hex $f[13] : $n; + $tcase{$n} = ($f[14] ne '') ? hex $f[14] : $n; + if ($f[5] =~ /^[0-9A-F\s]+$/) { + # This character has a canonical decomposition. + # The regular expression rejects angle brackets, so other + # decompositions aren't permitted. + $decomp{$n} = []; + foreach my $dch (split(' ', $f[5])) { + push(@{$decomp{$n}}, hex $dch); + } + } +} +close(UCD); + +# +# Filesystem and console codepages. The filesystem codepage is used +# for FAT shortnames, whereas the console codepage is whatever is used +# on the screen and keyboard. +# +@xtab = (undef) x 256; +%tabx = (); +open(CPFS, '<', $cpfs) + or die "$0: could not open fs codepage: $cpfs: $!\n"; +while (defined($line = )) { + $line =~ s/\s*(\#.*|)$//; + @f = split(/\s+/, $line); + next if (scalar @f != 2); + next if (hex $f[0] > 255); + $xtab[hex $f[0]] = hex $f[1]; # Codepage -> Unicode + $tabx{hex $f[1]} = hex $f[0]; # Unicode -> Codepage +} +close(CPFS); + +@ytab = (undef) x 256; +%taby = (); +open(CPCO, '<', $cpco) + or die "$0: could not open console codepage: $cpco: $!\n"; +while (defined($line = )) { + $line =~ s/\s*(\#.*|)$//; + @f = split(/\s+/, $line); + next if (scalar @f != 2); + next if (hex $f[0] > 255); + $ytab[hex $f[0]] = hex $f[1]; # Codepage -> Unicode + $taby{hex $f[1]} = hex $f[0]; # Unicode -> Codepage +} +close(CPCO); + +open(CPOUT, '>', $cpout) + or die "$0: could not open output file: $cpout: $!\n"; +# +# Magic number, in anticipation of being able to load these +# files dynamically... +# +print CPOUT pack("VV", 0x8fad232b, 0x9c295319); + +# Header fields available for future use... +print CPOUT pack("VVVVVV", 0, 0, 0, 0, 0, 0); + +# +# Self (shortname) uppercase table. +# This depends both on the console codepage and the filesystem codepage; +# the logical transcoding operation is: +# +# $tabx{$ucase{$ytab[$i]}} +# +# ... where @ytab is console codepage -> Unicode and +# %tabx is Unicode -> filesystem codepage. +# +for ($i = 0; $i < 256; $i++) { + $uuc = $ucase{$ytab[$i]}; # Unicode upper case + if (defined($tabx{$uuc})) { + # Straight-forward conversion + $u = $tabx{$uuc}; + } elsif (defined($tabx{${$decomp{$uuc}}[0]})) { + # Upper case equivalent stripped of accents + $u = $tabx{${$decomp{$uuc}}[0]}; + } else { + # No equivalent at all found. Set this to zero, which should + # prevent shortname matching altogether (still making longname + # matching possible, of course.) + $u = 0; + } + print CPOUT pack("C", $u); +} + +# +# Unicode (longname) matching table. +# This only depends on the console codepage. +# +$pp0 = ''; $pp1 = ''; +for ($i = 0; $i < 256; $i++) { + if (!defined($ytab[$i])) { + $p0 = $p1 = 0xffff; + } else { + $p0 = $ytab[$i]; + if ($ucase{$p0} != $p0) { + $p1 = $ucase{$p0}; + } elsif ($lcase{$p0} != $p0) { + $p1 = $lcase{$p0}; + } elsif ($tcase{$p0} != $p0) { + $p1 = $tcase{$p0}; + } else { + $p1 = $p0; + } + } + # Only the BMP is supported... + $p0 = 0xffff if ($p0 > 0xffff); + $p1 = 0xffff if ($p1 > 0xffff); + $pp0 .= pack("v", $p0); + $pp1 .= pack("v", $p1); +} +print CPOUT $pp0, $pp1; +close (CPOUT); + + diff --git a/codepage/gensubset.pl b/codepage/gensubset.pl new file mode 100755 index 0000000..4dd7f2c --- /dev/null +++ b/codepage/gensubset.pl @@ -0,0 +1,57 @@ +#!/usr/bin/perl +# +# Generate a subset of the UnicodeData.txt file, available from +# ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt +# +# Usage: +# gensubset.pl [subset files] < UnicodeData.txt > MiniUCD.txt +# + +%need_these = (); + +# Mark as needed all the characters mentioned in the relevant files +foreach $file (@ARGV) { + open(F, '<', $file) or die; + while (defined($line = )) { + $line =~ s/\s*(\#.*|)$//; # Remove comments and final blanks + @f = split(/\s+/, $line); + next if (scalar @f != 2); + $need_these{hex $f[1]}++; + } + close(F); +} + +# Also mark as needed any case variants of those +# (Note: this doesn't necessarily provide the full transitive closure, +# but we shouldn't need it.) +while (defined($line = )) { + @f = split(/;/, $line); + if ($f[0] =~ /^([0-9a-f]+)$/i) { + $r = hex $f[0]; + if ($need_these{$r}) { + $need_these{hex $f[12]}++ if ($f[12] ne ''); + $need_these{hex $f[13]}++ if ($f[13] ne ''); + $need_these{hex $f[14]}++ if ($f[14] ne ''); + } + } +} + +# Finally, write out the subset +seek(STDIN, 0, 0); +while (defined($line = )) { + ($v, $l) = split(/;/, $line, 2); + if ($v =~ /^([0-9a-f]+)\-([0-9a-f]+)$/i) { + # This isn't actually the format... fix that if it ever matters + $r1 = hex $1; + $r2 = hex $2; + } elsif ($v =~ /^([0-9a-f]+)$/i) { + $r1 = $r2 = hex $1; + } else { + next; + } + for ($r = $r1; $r <= $r2; $r++) { + printf "%04X;%s", $r, $l if ($need_these{$r}); + } +} + + diff --git a/codepage/iso8859-1.txt b/codepage/iso8859-1.txt new file mode 100644 index 0000000..473ecab --- /dev/null +++ b/codepage/iso8859-1.txt @@ -0,0 +1,303 @@ +# +# Name: ISO/IEC 8859-1:1998 to Unicode +# Unicode version: 3.0 +# Table version: 1.0 +# Table format: Format A +# Date: 1999 July 27 +# Authors: Ken Whistler +# +# Copyright (c) 1991-1999 Unicode, Inc. All Rights reserved. +# +# This file is provided as-is by Unicode, Inc. (The Unicode Consortium). +# No claims are made as to fitness for any particular purpose. No +# warranties of any kind are expressed or implied. The recipient +# agrees to determine applicability of information provided. If this +# file has been provided on optical media by Unicode, Inc., the sole +# remedy for any claim will be exchange of defective media within 90 +# days of receipt. +# +# Unicode, Inc. hereby grants the right to freely use the information +# supplied in this file in the creation of products supporting the +# Unicode Standard, and to make copies of this file in any form for +# internal or external distribution as long as this notice remains +# attached. +# +# General notes: +# +# This table contains the data the Unicode Consortium has on how +# ISO/IEC 8859-1:1998 characters map into Unicode. +# +# Format: Three tab-separated columns +# Column #1 is the ISO/IEC 8859-1 code (in hex as 0xXX) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 the Unicode name (follows a comment sign, '#') +# +# The entries are in ISO/IEC 8859-1 order. +# +# Version history +# 1.0 version updates 0.1 version by adding mappings for all +# control characters. +# +# Updated versions of this file may be found in: +# +# +# Any comments or problems, contact +# Please note that is an archival address; +# notices will be checked, but do not expect an immediate response. +# +0x00 0x0000 # NULL +0x01 0x0001 # START OF HEADING +0x02 0x0002 # START OF TEXT +0x03 0x0003 # END OF TEXT +0x04 0x0004 # END OF TRANSMISSION +0x05 0x0005 # ENQUIRY +0x06 0x0006 # ACKNOWLEDGE +0x07 0x0007 # BELL +0x08 0x0008 # BACKSPACE +0x09 0x0009 # HORIZONTAL TABULATION +0x0A 0x000A # LINE FEED +0x0B 0x000B # VERTICAL TABULATION +0x0C 0x000C # FORM FEED +0x0D 0x000D # CARRIAGE RETURN +0x0E 0x000E # SHIFT OUT +0x0F 0x000F # SHIFT IN +0x10 0x0010 # DATA LINK ESCAPE +0x11 0x0011 # DEVICE CONTROL ONE +0x12 0x0012 # DEVICE CONTROL TWO +0x13 0x0013 # DEVICE CONTROL THREE +0x14 0x0014 # DEVICE CONTROL FOUR +0x15 0x0015 # NEGATIVE ACKNOWLEDGE +0x16 0x0016 # SYNCHRONOUS IDLE +0x17 0x0017 # END OF TRANSMISSION BLOCK +0x18 0x0018 # CANCEL +0x19 0x0019 # END OF MEDIUM +0x1A 0x001A # SUBSTITUTE +0x1B 0x001B # ESCAPE +0x1C 0x001C # FILE SEPARATOR +0x1D 0x001D # GROUP SEPARATOR +0x1E 0x001E # RECORD SEPARATOR +0x1F 0x001F # UNIT SEPARATOR +0x20 0x0020 # SPACE +0x21 0x0021 # EXCLAMATION MARK +0x22 0x0022 # QUOTATION MARK +0x23 0x0023 # NUMBER SIGN +0x24 0x0024 # DOLLAR SIGN +0x25 0x0025 # PERCENT SIGN +0x26 0x0026 # AMPERSAND +0x27 0x0027 # APOSTROPHE +0x28 0x0028 # LEFT PARENTHESIS +0x29 0x0029 # RIGHT PARENTHESIS +0x2A 0x002A # ASTERISK +0x2B 0x002B # PLUS SIGN +0x2C 0x002C # COMMA +0x2D 0x002D # HYPHEN-MINUS +0x2E 0x002E # FULL STOP +0x2F 0x002F # SOLIDUS +0x30 0x0030 # DIGIT ZERO +0x31 0x0031 # DIGIT ONE +0x32 0x0032 # DIGIT TWO +0x33 0x0033 # DIGIT THREE +0x34 0x0034 # DIGIT FOUR +0x35 0x0035 # DIGIT FIVE +0x36 0x0036 # DIGIT SIX +0x37 0x0037 # DIGIT SEVEN +0x38 0x0038 # DIGIT EIGHT +0x39 0x0039 # DIGIT NINE +0x3A 0x003A # COLON +0x3B 0x003B # SEMICOLON +0x3C 0x003C # LESS-THAN SIGN +0x3D 0x003D # EQUALS SIGN +0x3E 0x003E # GREATER-THAN SIGN +0x3F 0x003F # QUESTION MARK +0x40 0x0040 # COMMERCIAL AT +0x41 0x0041 # LATIN CAPITAL LETTER A +0x42 0x0042 # LATIN CAPITAL LETTER B +0x43 0x0043 # LATIN CAPITAL LETTER C +0x44 0x0044 # LATIN CAPITAL LETTER D +0x45 0x0045 # LATIN CAPITAL LETTER E +0x46 0x0046 # LATIN CAPITAL LETTER F +0x47 0x0047 # LATIN CAPITAL LETTER G +0x48 0x0048 # LATIN CAPITAL LETTER H +0x49 0x0049 # LATIN CAPITAL LETTER I +0x4A 0x004A # LATIN CAPITAL LETTER J +0x4B 0x004B # LATIN CAPITAL LETTER K +0x4C 0x004C # LATIN CAPITAL LETTER L +0x4D 0x004D # LATIN CAPITAL LETTER M +0x4E 0x004E # LATIN CAPITAL LETTER N +0x4F 0x004F # LATIN CAPITAL LETTER O +0x50 0x0050 # LATIN CAPITAL LETTER P +0x51 0x0051 # LATIN CAPITAL LETTER Q +0x52 0x0052 # LATIN CAPITAL LETTER R +0x53 0x0053 # LATIN CAPITAL LETTER S +0x54 0x0054 # LATIN CAPITAL LETTER T +0x55 0x0055 # LATIN CAPITAL LETTER U +0x56 0x0056 # LATIN CAPITAL LETTER V +0x57 0x0057 # LATIN CAPITAL LETTER W +0x58 0x0058 # LATIN CAPITAL LETTER X +0x59 0x0059 # LATIN CAPITAL LETTER Y +0x5A 0x005A # LATIN CAPITAL LETTER Z +0x5B 0x005B # LEFT SQUARE BRACKET +0x5C 0x005C # REVERSE SOLIDUS +0x5D 0x005D # RIGHT SQUARE BRACKET +0x5E 0x005E # CIRCUMFLEX ACCENT +0x5F 0x005F # LOW LINE +0x60 0x0060 # GRAVE ACCENT +0x61 0x0061 # LATIN SMALL LETTER A +0x62 0x0062 # LATIN SMALL LETTER B +0x63 0x0063 # LATIN SMALL LETTER C +0x64 0x0064 # LATIN SMALL LETTER D +0x65 0x0065 # LATIN SMALL LETTER E +0x66 0x0066 # LATIN SMALL LETTER F +0x67 0x0067 # LATIN SMALL LETTER G +0x68 0x0068 # LATIN SMALL LETTER H +0x69 0x0069 # LATIN SMALL LETTER I +0x6A 0x006A # LATIN SMALL LETTER J +0x6B 0x006B # LATIN SMALL LETTER K +0x6C 0x006C # LATIN SMALL LETTER L +0x6D 0x006D # LATIN SMALL LETTER M +0x6E 0x006E # LATIN SMALL LETTER N +0x6F 0x006F # LATIN SMALL LETTER O +0x70 0x0070 # LATIN SMALL LETTER P +0x71 0x0071 # LATIN SMALL LETTER Q +0x72 0x0072 # LATIN SMALL LETTER R +0x73 0x0073 # LATIN SMALL LETTER S +0x74 0x0074 # LATIN SMALL LETTER T +0x75 0x0075 # LATIN SMALL LETTER U +0x76 0x0076 # LATIN SMALL LETTER V +0x77 0x0077 # LATIN SMALL LETTER W +0x78 0x0078 # LATIN SMALL LETTER X +0x79 0x0079 # LATIN SMALL LETTER Y +0x7A 0x007A # LATIN SMALL LETTER Z +0x7B 0x007B # LEFT CURLY BRACKET +0x7C 0x007C # VERTICAL LINE +0x7D 0x007D # RIGHT CURLY BRACKET +0x7E 0x007E # TILDE +0x7F 0x007F # DELETE +0x80 0x0080 # +0x81 0x0081 # +0x82 0x0082 # +0x83 0x0083 # +0x84 0x0084 # +0x85 0x0085 # +0x86 0x0086 # +0x87 0x0087 # +0x88 0x0088 # +0x89 0x0089 # +0x8A 0x008A # +0x8B 0x008B # +0x8C 0x008C # +0x8D 0x008D # +0x8E 0x008E # +0x8F 0x008F # +0x90 0x0090 # +0x91 0x0091 # +0x92 0x0092 # +0x93 0x0093 # +0x94 0x0094 # +0x95 0x0095 # +0x96 0x0096 # +0x97 0x0097 # +0x98 0x0098 # +0x99 0x0099 # +0x9A 0x009A # +0x9B 0x009B # +0x9C 0x009C # +0x9D 0x009D # +0x9E 0x009E # +0x9F 0x009F # +0xA0 0x00A0 # NO-BREAK SPACE +0xA1 0x00A1 # INVERTED EXCLAMATION MARK +0xA2 0x00A2 # CENT SIGN +0xA3 0x00A3 # POUND SIGN +0xA4 0x00A4 # CURRENCY SIGN +0xA5 0x00A5 # YEN SIGN +0xA6 0x00A6 # BROKEN BAR +0xA7 0x00A7 # SECTION SIGN +0xA8 0x00A8 # DIAERESIS +0xA9 0x00A9 # COPYRIGHT SIGN +0xAA 0x00AA # FEMININE ORDINAL INDICATOR +0xAB 0x00AB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xAC 0x00AC # NOT SIGN +0xAD 0x00AD # SOFT HYPHEN +0xAE 0x00AE # REGISTERED SIGN +0xAF 0x00AF # MACRON +0xB0 0x00B0 # DEGREE SIGN +0xB1 0x00B1 # PLUS-MINUS SIGN +0xB2 0x00B2 # SUPERSCRIPT TWO +0xB3 0x00B3 # SUPERSCRIPT THREE +0xB4 0x00B4 # ACUTE ACCENT +0xB5 0x00B5 # MICRO SIGN +0xB6 0x00B6 # PILCROW SIGN +0xB7 0x00B7 # MIDDLE DOT +0xB8 0x00B8 # CEDILLA +0xB9 0x00B9 # SUPERSCRIPT ONE +0xBA 0x00BA # MASCULINE ORDINAL INDICATOR +0xBB 0x00BB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xBC 0x00BC # VULGAR FRACTION ONE QUARTER +0xBD 0x00BD # VULGAR FRACTION ONE HALF +0xBE 0x00BE # VULGAR FRACTION THREE QUARTERS +0xBF 0x00BF # INVERTED QUESTION MARK +0xC0 0x00C0 # LATIN CAPITAL LETTER A WITH GRAVE +0xC1 0x00C1 # LATIN CAPITAL LETTER A WITH ACUTE +0xC2 0x00C2 # LATIN CAPITAL LETTER A WITH CIRCUMFLEX +0xC3 0x00C3 # LATIN CAPITAL LETTER A WITH TILDE +0xC4 0x00C4 # LATIN CAPITAL LETTER A WITH DIAERESIS +0xC5 0x00C5 # LATIN CAPITAL LETTER A WITH RING ABOVE +0xC6 0x00C6 # LATIN CAPITAL LETTER AE +0xC7 0x00C7 # LATIN CAPITAL LETTER C WITH CEDILLA +0xC8 0x00C8 # LATIN CAPITAL LETTER E WITH GRAVE +0xC9 0x00C9 # LATIN CAPITAL LETTER E WITH ACUTE +0xCA 0x00CA # LATIN CAPITAL LETTER E WITH CIRCUMFLEX +0xCB 0x00CB # LATIN CAPITAL LETTER E WITH DIAERESIS +0xCC 0x00CC # LATIN CAPITAL LETTER I WITH GRAVE +0xCD 0x00CD # LATIN CAPITAL LETTER I WITH ACUTE +0xCE 0x00CE # LATIN CAPITAL LETTER I WITH CIRCUMFLEX +0xCF 0x00CF # LATIN CAPITAL LETTER I WITH DIAERESIS +0xD0 0x00D0 # LATIN CAPITAL LETTER ETH (Icelandic) +0xD1 0x00D1 # LATIN CAPITAL LETTER N WITH TILDE +0xD2 0x00D2 # LATIN CAPITAL LETTER O WITH GRAVE +0xD3 0x00D3 # LATIN CAPITAL LETTER O WITH ACUTE +0xD4 0x00D4 # LATIN CAPITAL LETTER O WITH CIRCUMFLEX +0xD5 0x00D5 # LATIN CAPITAL LETTER O WITH TILDE +0xD6 0x00D6 # LATIN CAPITAL LETTER O WITH DIAERESIS +0xD7 0x00D7 # MULTIPLICATION SIGN +0xD8 0x00D8 # LATIN CAPITAL LETTER O WITH STROKE +0xD9 0x00D9 # LATIN CAPITAL LETTER U WITH GRAVE +0xDA 0x00DA # LATIN CAPITAL LETTER U WITH ACUTE +0xDB 0x00DB # LATIN CAPITAL LETTER U WITH CIRCUMFLEX +0xDC 0x00DC # LATIN CAPITAL LETTER U WITH DIAERESIS +0xDD 0x00DD # LATIN CAPITAL LETTER Y WITH ACUTE +0xDE 0x00DE # LATIN CAPITAL LETTER THORN (Icelandic) +0xDF 0x00DF # LATIN SMALL LETTER SHARP S (German) +0xE0 0x00E0 # LATIN SMALL LETTER A WITH GRAVE +0xE1 0x00E1 # LATIN SMALL LETTER A WITH ACUTE +0xE2 0x00E2 # LATIN SMALL LETTER A WITH CIRCUMFLEX +0xE3 0x00E3 # LATIN SMALL LETTER A WITH TILDE +0xE4 0x00E4 # LATIN SMALL LETTER A WITH DIAERESIS +0xE5 0x00E5 # LATIN SMALL LETTER A WITH RING ABOVE +0xE6 0x00E6 # LATIN SMALL LETTER AE +0xE7 0x00E7 # LATIN SMALL LETTER C WITH CEDILLA +0xE8 0x00E8 # LATIN SMALL LETTER E WITH GRAVE +0xE9 0x00E9 # LATIN SMALL LETTER E WITH ACUTE +0xEA 0x00EA # LATIN SMALL LETTER E WITH CIRCUMFLEX +0xEB 0x00EB # LATIN SMALL LETTER E WITH DIAERESIS +0xEC 0x00EC # LATIN SMALL LETTER I WITH GRAVE +0xED 0x00ED # LATIN SMALL LETTER I WITH ACUTE +0xEE 0x00EE # LATIN SMALL LETTER I WITH CIRCUMFLEX +0xEF 0x00EF # LATIN SMALL LETTER I WITH DIAERESIS +0xF0 0x00F0 # LATIN SMALL LETTER ETH (Icelandic) +0xF1 0x00F1 # LATIN SMALL LETTER N WITH TILDE +0xF2 0x00F2 # LATIN SMALL LETTER O WITH GRAVE +0xF3 0x00F3 # LATIN SMALL LETTER O WITH ACUTE +0xF4 0x00F4 # LATIN SMALL LETTER O WITH CIRCUMFLEX +0xF5 0x00F5 # LATIN SMALL LETTER O WITH TILDE +0xF6 0x00F6 # LATIN SMALL LETTER O WITH DIAERESIS +0xF7 0x00F7 # DIVISION SIGN +0xF8 0x00F8 # LATIN SMALL LETTER O WITH STROKE +0xF9 0x00F9 # LATIN SMALL LETTER U WITH GRAVE +0xFA 0x00FA # LATIN SMALL LETTER U WITH ACUTE +0xFB 0x00FB # LATIN SMALL LETTER U WITH CIRCUMFLEX +0xFC 0x00FC # LATIN SMALL LETTER U WITH DIAERESIS +0xFD 0x00FD # LATIN SMALL LETTER Y WITH ACUTE +0xFE 0x00FE # LATIN SMALL LETTER THORN (Icelandic) +0xFF 0x00FF # LATIN SMALL LETTER Y WITH DIAERESIS diff --git a/codepage/iso8859-10.txt b/codepage/iso8859-10.txt new file mode 100644 index 0000000..374a42b --- /dev/null +++ b/codepage/iso8859-10.txt @@ -0,0 +1,303 @@ +# +# Name: ISO/IEC 8859-10:1998 to Unicode +# Unicode version: 3.0 +# Table version: 1.1 +# Table format: Format A +# Date: 1999 October 11 +# Authors: Ken Whistler +# +# Copyright (c) 1999 Unicode, Inc. All Rights reserved. +# +# This file is provided as-is by Unicode, Inc. (The Unicode Consortium). +# No claims are made as to fitness for any particular purpose. No +# warranties of any kind are expressed or implied. The recipient +# agrees to determine applicability of information provided. If this +# file has been provided on optical media by Unicode, Inc., the sole +# remedy for any claim will be exchange of defective media within 90 +# days of receipt. +# +# Unicode, Inc. hereby grants the right to freely use the information +# supplied in this file in the creation of products supporting the +# Unicode Standard, and to make copies of this file in any form for +# internal or external distribution as long as this notice remains +# attached. +# +# General notes: +# +# This table contains the data the Unicode Consortium has on how +# ISO/IEC 8859-10:1998 characters map into Unicode. +# +# Format: Three tab-separated columns +# Column #1 is the ISO/IEC 8859-10 code (in hex as 0xXX) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 the Unicode name (follows a comment sign, '#') +# +# The entries are in ISO/IEC 8859-10 order. +# +# Version history +# 1.0 version new. +# 1.1 corrected mistake in mapping of 0xA4 +# +# Updated versions of this file may be found in: +# +# +# Any comments or problems, contact +# Please note that is an archival address; +# notices will be checked, but do not expect an immediate response. +# +0x00 0x0000 # NULL +0x01 0x0001 # START OF HEADING +0x02 0x0002 # START OF TEXT +0x03 0x0003 # END OF TEXT +0x04 0x0004 # END OF TRANSMISSION +0x05 0x0005 # ENQUIRY +0x06 0x0006 # ACKNOWLEDGE +0x07 0x0007 # BELL +0x08 0x0008 # BACKSPACE +0x09 0x0009 # HORIZONTAL TABULATION +0x0A 0x000A # LINE FEED +0x0B 0x000B # VERTICAL TABULATION +0x0C 0x000C # FORM FEED +0x0D 0x000D # CARRIAGE RETURN +0x0E 0x000E # SHIFT OUT +0x0F 0x000F # SHIFT IN +0x10 0x0010 # DATA LINK ESCAPE +0x11 0x0011 # DEVICE CONTROL ONE +0x12 0x0012 # DEVICE CONTROL TWO +0x13 0x0013 # DEVICE CONTROL THREE +0x14 0x0014 # DEVICE CONTROL FOUR +0x15 0x0015 # NEGATIVE ACKNOWLEDGE +0x16 0x0016 # SYNCHRONOUS IDLE +0x17 0x0017 # END OF TRANSMISSION BLOCK +0x18 0x0018 # CANCEL +0x19 0x0019 # END OF MEDIUM +0x1A 0x001A # SUBSTITUTE +0x1B 0x001B # ESCAPE +0x1C 0x001C # FILE SEPARATOR +0x1D 0x001D # GROUP SEPARATOR +0x1E 0x001E # RECORD SEPARATOR +0x1F 0x001F # UNIT SEPARATOR +0x20 0x0020 # SPACE +0x21 0x0021 # EXCLAMATION MARK +0x22 0x0022 # QUOTATION MARK +0x23 0x0023 # NUMBER SIGN +0x24 0x0024 # DOLLAR SIGN +0x25 0x0025 # PERCENT SIGN +0x26 0x0026 # AMPERSAND +0x27 0x0027 # APOSTROPHE +0x28 0x0028 # LEFT PARENTHESIS +0x29 0x0029 # RIGHT PARENTHESIS +0x2A 0x002A # ASTERISK +0x2B 0x002B # PLUS SIGN +0x2C 0x002C # COMMA +0x2D 0x002D # HYPHEN-MINUS +0x2E 0x002E # FULL STOP +0x2F 0x002F # SOLIDUS +0x30 0x0030 # DIGIT ZERO +0x31 0x0031 # DIGIT ONE +0x32 0x0032 # DIGIT TWO +0x33 0x0033 # DIGIT THREE +0x34 0x0034 # DIGIT FOUR +0x35 0x0035 # DIGIT FIVE +0x36 0x0036 # DIGIT SIX +0x37 0x0037 # DIGIT SEVEN +0x38 0x0038 # DIGIT EIGHT +0x39 0x0039 # DIGIT NINE +0x3A 0x003A # COLON +0x3B 0x003B # SEMICOLON +0x3C 0x003C # LESS-THAN SIGN +0x3D 0x003D # EQUALS SIGN +0x3E 0x003E # GREATER-THAN SIGN +0x3F 0x003F # QUESTION MARK +0x40 0x0040 # COMMERCIAL AT +0x41 0x0041 # LATIN CAPITAL LETTER A +0x42 0x0042 # LATIN CAPITAL LETTER B +0x43 0x0043 # LATIN CAPITAL LETTER C +0x44 0x0044 # LATIN CAPITAL LETTER D +0x45 0x0045 # LATIN CAPITAL LETTER E +0x46 0x0046 # LATIN CAPITAL LETTER F +0x47 0x0047 # LATIN CAPITAL LETTER G +0x48 0x0048 # LATIN CAPITAL LETTER H +0x49 0x0049 # LATIN CAPITAL LETTER I +0x4A 0x004A # LATIN CAPITAL LETTER J +0x4B 0x004B # LATIN CAPITAL LETTER K +0x4C 0x004C # LATIN CAPITAL LETTER L +0x4D 0x004D # LATIN CAPITAL LETTER M +0x4E 0x004E # LATIN CAPITAL LETTER N +0x4F 0x004F # LATIN CAPITAL LETTER O +0x50 0x0050 # LATIN CAPITAL LETTER P +0x51 0x0051 # LATIN CAPITAL LETTER Q +0x52 0x0052 # LATIN CAPITAL LETTER R +0x53 0x0053 # LATIN CAPITAL LETTER S +0x54 0x0054 # LATIN CAPITAL LETTER T +0x55 0x0055 # LATIN CAPITAL LETTER U +0x56 0x0056 # LATIN CAPITAL LETTER V +0x57 0x0057 # LATIN CAPITAL LETTER W +0x58 0x0058 # LATIN CAPITAL LETTER X +0x59 0x0059 # LATIN CAPITAL LETTER Y +0x5A 0x005A # LATIN CAPITAL LETTER Z +0x5B 0x005B # LEFT SQUARE BRACKET +0x5C 0x005C # REVERSE SOLIDUS +0x5D 0x005D # RIGHT SQUARE BRACKET +0x5E 0x005E # CIRCUMFLEX ACCENT +0x5F 0x005F # LOW LINE +0x60 0x0060 # GRAVE ACCENT +0x61 0x0061 # LATIN SMALL LETTER A +0x62 0x0062 # LATIN SMALL LETTER B +0x63 0x0063 # LATIN SMALL LETTER C +0x64 0x0064 # LATIN SMALL LETTER D +0x65 0x0065 # LATIN SMALL LETTER E +0x66 0x0066 # LATIN SMALL LETTER F +0x67 0x0067 # LATIN SMALL LETTER G +0x68 0x0068 # LATIN SMALL LETTER H +0x69 0x0069 # LATIN SMALL LETTER I +0x6A 0x006A # LATIN SMALL LETTER J +0x6B 0x006B # LATIN SMALL LETTER K +0x6C 0x006C # LATIN SMALL LETTER L +0x6D 0x006D # LATIN SMALL LETTER M +0x6E 0x006E # LATIN SMALL LETTER N +0x6F 0x006F # LATIN SMALL LETTER O +0x70 0x0070 # LATIN SMALL LETTER P +0x71 0x0071 # LATIN SMALL LETTER Q +0x72 0x0072 # LATIN SMALL LETTER R +0x73 0x0073 # LATIN SMALL LETTER S +0x74 0x0074 # LATIN SMALL LETTER T +0x75 0x0075 # LATIN SMALL LETTER U +0x76 0x0076 # LATIN SMALL LETTER V +0x77 0x0077 # LATIN SMALL LETTER W +0x78 0x0078 # LATIN SMALL LETTER X +0x79 0x0079 # LATIN SMALL LETTER Y +0x7A 0x007A # LATIN SMALL LETTER Z +0x7B 0x007B # LEFT CURLY BRACKET +0x7C 0x007C # VERTICAL LINE +0x7D 0x007D # RIGHT CURLY BRACKET +0x7E 0x007E # TILDE +0x7F 0x007F # DELETE +0x80 0x0080 # +0x81 0x0081 # +0x82 0x0082 # +0x83 0x0083 # +0x84 0x0084 # +0x85 0x0085 # +0x86 0x0086 # +0x87 0x0087 # +0x88 0x0088 # +0x89 0x0089 # +0x8A 0x008A # +0x8B 0x008B # +0x8C 0x008C # +0x8D 0x008D # +0x8E 0x008E # +0x8F 0x008F # +0x90 0x0090 # +0x91 0x0091 # +0x92 0x0092 # +0x93 0x0093 # +0x94 0x0094 # +0x95 0x0095 # +0x96 0x0096 # +0x97 0x0097 # +0x98 0x0098 # +0x99 0x0099 # +0x9A 0x009A # +0x9B 0x009B # +0x9C 0x009C # +0x9D 0x009D # +0x9E 0x009E # +0x9F 0x009F # +0xA0 0x00A0 # NO-BREAK SPACE +0xA1 0x0104 # LATIN CAPITAL LETTER A WITH OGONEK +0xA2 0x0112 # LATIN CAPITAL LETTER E WITH MACRON +0xA3 0x0122 # LATIN CAPITAL LETTER G WITH CEDILLA +0xA4 0x012A # LATIN CAPITAL LETTER I WITH MACRON +0xA5 0x0128 # LATIN CAPITAL LETTER I WITH TILDE +0xA6 0x0136 # LATIN CAPITAL LETTER K WITH CEDILLA +0xA7 0x00A7 # SECTION SIGN +0xA8 0x013B # LATIN CAPITAL LETTER L WITH CEDILLA +0xA9 0x0110 # LATIN CAPITAL LETTER D WITH STROKE +0xAA 0x0160 # LATIN CAPITAL LETTER S WITH CARON +0xAB 0x0166 # LATIN CAPITAL LETTER T WITH STROKE +0xAC 0x017D # LATIN CAPITAL LETTER Z WITH CARON +0xAD 0x00AD # SOFT HYPHEN +0xAE 0x016A # LATIN CAPITAL LETTER U WITH MACRON +0xAF 0x014A # LATIN CAPITAL LETTER ENG +0xB0 0x00B0 # DEGREE SIGN +0xB1 0x0105 # LATIN SMALL LETTER A WITH OGONEK +0xB2 0x0113 # LATIN SMALL LETTER E WITH MACRON +0xB3 0x0123 # LATIN SMALL LETTER G WITH CEDILLA +0xB4 0x012B # LATIN SMALL LETTER I WITH MACRON +0xB5 0x0129 # LATIN SMALL LETTER I WITH TILDE +0xB6 0x0137 # LATIN SMALL LETTER K WITH CEDILLA +0xB7 0x00B7 # MIDDLE DOT +0xB8 0x013C # LATIN SMALL LETTER L WITH CEDILLA +0xB9 0x0111 # LATIN SMALL LETTER D WITH STROKE +0xBA 0x0161 # LATIN SMALL LETTER S WITH CARON +0xBB 0x0167 # LATIN SMALL LETTER T WITH STROKE +0xBC 0x017E # LATIN SMALL LETTER Z WITH CARON +0xBD 0x2015 # HORIZONTAL BAR +0xBE 0x016B # LATIN SMALL LETTER U WITH MACRON +0xBF 0x014B # LATIN SMALL LETTER ENG +0xC0 0x0100 # LATIN CAPITAL LETTER A WITH MACRON +0xC1 0x00C1 # LATIN CAPITAL LETTER A WITH ACUTE +0xC2 0x00C2 # LATIN CAPITAL LETTER A WITH CIRCUMFLEX +0xC3 0x00C3 # LATIN CAPITAL LETTER A WITH TILDE +0xC4 0x00C4 # LATIN CAPITAL LETTER A WITH DIAERESIS +0xC5 0x00C5 # LATIN CAPITAL LETTER A WITH RING ABOVE +0xC6 0x00C6 # LATIN CAPITAL LETTER AE +0xC7 0x012E # LATIN CAPITAL LETTER I WITH OGONEK +0xC8 0x010C # LATIN CAPITAL LETTER C WITH CARON +0xC9 0x00C9 # LATIN CAPITAL LETTER E WITH ACUTE +0xCA 0x0118 # LATIN CAPITAL LETTER E WITH OGONEK +0xCB 0x00CB # LATIN CAPITAL LETTER E WITH DIAERESIS +0xCC 0x0116 # LATIN CAPITAL LETTER E WITH DOT ABOVE +0xCD 0x00CD # LATIN CAPITAL LETTER I WITH ACUTE +0xCE 0x00CE # LATIN CAPITAL LETTER I WITH CIRCUMFLEX +0xCF 0x00CF # LATIN CAPITAL LETTER I WITH DIAERESIS +0xD0 0x00D0 # LATIN CAPITAL LETTER ETH (Icelandic) +0xD1 0x0145 # LATIN CAPITAL LETTER N WITH CEDILLA +0xD2 0x014C # LATIN CAPITAL LETTER O WITH MACRON +0xD3 0x00D3 # LATIN CAPITAL LETTER O WITH ACUTE +0xD4 0x00D4 # LATIN CAPITAL LETTER O WITH CIRCUMFLEX +0xD5 0x00D5 # LATIN CAPITAL LETTER O WITH TILDE +0xD6 0x00D6 # LATIN CAPITAL LETTER O WITH DIAERESIS +0xD7 0x0168 # LATIN CAPITAL LETTER U WITH TILDE +0xD8 0x00D8 # LATIN CAPITAL LETTER O WITH STROKE +0xD9 0x0172 # LATIN CAPITAL LETTER U WITH OGONEK +0xDA 0x00DA # LATIN CAPITAL LETTER U WITH ACUTE +0xDB 0x00DB # LATIN CAPITAL LETTER U WITH CIRCUMFLEX +0xDC 0x00DC # LATIN CAPITAL LETTER U WITH DIAERESIS +0xDD 0x00DD # LATIN CAPITAL LETTER Y WITH ACUTE +0xDE 0x00DE # LATIN CAPITAL LETTER THORN (Icelandic) +0xDF 0x00DF # LATIN SMALL LETTER SHARP S (German) +0xE0 0x0101 # LATIN SMALL LETTER A WITH MACRON +0xE1 0x00E1 # LATIN SMALL LETTER A WITH ACUTE +0xE2 0x00E2 # LATIN SMALL LETTER A WITH CIRCUMFLEX +0xE3 0x00E3 # LATIN SMALL LETTER A WITH TILDE +0xE4 0x00E4 # LATIN SMALL LETTER A WITH DIAERESIS +0xE5 0x00E5 # LATIN SMALL LETTER A WITH RING ABOVE +0xE6 0x00E6 # LATIN SMALL LETTER AE +0xE7 0x012F # LATIN SMALL LETTER I WITH OGONEK +0xE8 0x010D # LATIN SMALL LETTER C WITH CARON +0xE9 0x00E9 # LATIN SMALL LETTER E WITH ACUTE +0xEA 0x0119 # LATIN SMALL LETTER E WITH OGONEK +0xEB 0x00EB # LATIN SMALL LETTER E WITH DIAERESIS +0xEC 0x0117 # LATIN SMALL LETTER E WITH DOT ABOVE +0xED 0x00ED # LATIN SMALL LETTER I WITH ACUTE +0xEE 0x00EE # LATIN SMALL LETTER I WITH CIRCUMFLEX +0xEF 0x00EF # LATIN SMALL LETTER I WITH DIAERESIS +0xF0 0x00F0 # LATIN SMALL LETTER ETH (Icelandic) +0xF1 0x0146 # LATIN SMALL LETTER N WITH CEDILLA +0xF2 0x014D # LATIN SMALL LETTER O WITH MACRON +0xF3 0x00F3 # LATIN SMALL LETTER O WITH ACUTE +0xF4 0x00F4 # LATIN SMALL LETTER O WITH CIRCUMFLEX +0xF5 0x00F5 # LATIN SMALL LETTER O WITH TILDE +0xF6 0x00F6 # LATIN SMALL LETTER O WITH DIAERESIS +0xF7 0x0169 # LATIN SMALL LETTER U WITH TILDE +0xF8 0x00F8 # LATIN SMALL LETTER O WITH STROKE +0xF9 0x0173 # LATIN SMALL LETTER U WITH OGONEK +0xFA 0x00FA # LATIN SMALL LETTER U WITH ACUTE +0xFB 0x00FB # LATIN SMALL LETTER U WITH CIRCUMFLEX +0xFC 0x00FC # LATIN SMALL LETTER U WITH DIAERESIS +0xFD 0x00FD # LATIN SMALL LETTER Y WITH ACUTE +0xFE 0x00FE # LATIN SMALL LETTER THORN (Icelandic) +0xFF 0x0138 # LATIN SMALL LETTER KRA diff --git a/codepage/iso8859-11.txt b/codepage/iso8859-11.txt new file mode 100644 index 0000000..192bd9d --- /dev/null +++ b/codepage/iso8859-11.txt @@ -0,0 +1,297 @@ +# +# Name: ISO/IEC 8859-11:2001 to Unicode +# Unicode version: 3.2 +# Table version: 1.0 +# Table format: Format A +# Date: 2002 October 7 +# Authors: Ken Whistler +# +# Copyright (c) 2002 Unicode, Inc. All Rights reserved. +# +# This file is provided as-is by Unicode, Inc. (The Unicode Consortium). +# No claims are made as to fitness for any particular purpose. No +# warranties of any kind are expressed or implied. The recipient +# agrees to determine applicability of information provided. If this +# file has been provided on optical media by Unicode, Inc., the sole +# remedy for any claim will be exchange of defective media within 90 +# days of receipt. +# +# Unicode, Inc. hereby grants the right to freely use the information +# supplied in this file in the creation of products supporting the +# Unicode Standard, and to make copies of this file in any form for +# internal or external distribution as long as this notice remains +# attached. +# +# General notes: +# +# This table contains the data the Unicode Consortium has on how +# ISO/IEC 8859-11:2001 characters map into Unicode. +# +# ISO/IEC 8859-11:2001 is equivalent to TIS 620-2533 (1990) with +# the addition of 0xA0 NO-BREAK SPACE. +# +# Format: Three tab-separated columns +# Column #1 is the ISO/IEC 8859-11 code (in hex as 0xXX) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 the Unicode name (follows a comment sign, '#') +# +# The entries are in ISO/IEC 8859-11 order. +# +# Version history: +# 2002 October 7 Created +# +# Updated versions of this file may be found in: +# +# +# For any comments or problems, please use the Unicode +# web contact form at: +# http://www.unicode.org/unicode/reporting.html +# +0x00 0x0000 # NULL +0x01 0x0001 # START OF HEADING +0x02 0x0002 # START OF TEXT +0x03 0x0003 # END OF TEXT +0x04 0x0004 # END OF TRANSMISSION +0x05 0x0005 # ENQUIRY +0x06 0x0006 # ACKNOWLEDGE +0x07 0x0007 # BELL +0x08 0x0008 # BACKSPACE +0x09 0x0009 # HORIZONTAL TABULATION +0x0A 0x000A # LINE FEED +0x0B 0x000B # VERTICAL TABULATION +0x0C 0x000C # FORM FEED +0x0D 0x000D # CARRIAGE RETURN +0x0E 0x000E # SHIFT OUT +0x0F 0x000F # SHIFT IN +0x10 0x0010 # DATA LINK ESCAPE +0x11 0x0011 # DEVICE CONTROL ONE +0x12 0x0012 # DEVICE CONTROL TWO +0x13 0x0013 # DEVICE CONTROL THREE +0x14 0x0014 # DEVICE CONTROL FOUR +0x15 0x0015 # NEGATIVE ACKNOWLEDGE +0x16 0x0016 # SYNCHRONOUS IDLE +0x17 0x0017 # END OF TRANSMISSION BLOCK +0x18 0x0018 # CANCEL +0x19 0x0019 # END OF MEDIUM +0x1A 0x001A # SUBSTITUTE +0x1B 0x001B # ESCAPE +0x1C 0x001C # FILE SEPARATOR +0x1D 0x001D # GROUP SEPARATOR +0x1E 0x001E # RECORD SEPARATOR +0x1F 0x001F # UNIT SEPARATOR +0x20 0x0020 # SPACE +0x21 0x0021 # EXCLAMATION MARK +0x22 0x0022 # QUOTATION MARK +0x23 0x0023 # NUMBER SIGN +0x24 0x0024 # DOLLAR SIGN +0x25 0x0025 # PERCENT SIGN +0x26 0x0026 # AMPERSAND +0x27 0x0027 # APOSTROPHE +0x28 0x0028 # LEFT PARENTHESIS +0x29 0x0029 # RIGHT PARENTHESIS +0x2A 0x002A # ASTERISK +0x2B 0x002B # PLUS SIGN +0x2C 0x002C # COMMA +0x2D 0x002D # HYPHEN-MINUS +0x2E 0x002E # FULL STOP +0x2F 0x002F # SOLIDUS +0x30 0x0030 # DIGIT ZERO +0x31 0x0031 # DIGIT ONE +0x32 0x0032 # DIGIT TWO +0x33 0x0033 # DIGIT THREE +0x34 0x0034 # DIGIT FOUR +0x35 0x0035 # DIGIT FIVE +0x36 0x0036 # DIGIT SIX +0x37 0x0037 # DIGIT SEVEN +0x38 0x0038 # DIGIT EIGHT +0x39 0x0039 # DIGIT NINE +0x3A 0x003A # COLON +0x3B 0x003B # SEMICOLON +0x3C 0x003C # LESS-THAN SIGN +0x3D 0x003D # EQUALS SIGN +0x3E 0x003E # GREATER-THAN SIGN +0x3F 0x003F # QUESTION MARK +0x40 0x0040 # COMMERCIAL AT +0x41 0x0041 # LATIN CAPITAL LETTER A +0x42 0x0042 # LATIN CAPITAL LETTER B +0x43 0x0043 # LATIN CAPITAL LETTER C +0x44 0x0044 # LATIN CAPITAL LETTER D +0x45 0x0045 # LATIN CAPITAL LETTER E +0x46 0x0046 # LATIN CAPITAL LETTER F +0x47 0x0047 # LATIN CAPITAL LETTER G +0x48 0x0048 # LATIN CAPITAL LETTER H +0x49 0x0049 # LATIN CAPITAL LETTER I +0x4A 0x004A # LATIN CAPITAL LETTER J +0x4B 0x004B # LATIN CAPITAL LETTER K +0x4C 0x004C # LATIN CAPITAL LETTER L +0x4D 0x004D # LATIN CAPITAL LETTER M +0x4E 0x004E # LATIN CAPITAL LETTER N +0x4F 0x004F # LATIN CAPITAL LETTER O +0x50 0x0050 # LATIN CAPITAL LETTER P +0x51 0x0051 # LATIN CAPITAL LETTER Q +0x52 0x0052 # LATIN CAPITAL LETTER R +0x53 0x0053 # LATIN CAPITAL LETTER S +0x54 0x0054 # LATIN CAPITAL LETTER T +0x55 0x0055 # LATIN CAPITAL LETTER U +0x56 0x0056 # LATIN CAPITAL LETTER V +0x57 0x0057 # LATIN CAPITAL LETTER W +0x58 0x0058 # LATIN CAPITAL LETTER X +0x59 0x0059 # LATIN CAPITAL LETTER Y +0x5A 0x005A # LATIN CAPITAL LETTER Z +0x5B 0x005B # LEFT SQUARE BRACKET +0x5C 0x005C # REVERSE SOLIDUS +0x5D 0x005D # RIGHT SQUARE BRACKET +0x5E 0x005E # CIRCUMFLEX ACCENT +0x5F 0x005F # LOW LINE +0x60 0x0060 # GRAVE ACCENT +0x61 0x0061 # LATIN SMALL LETTER A +0x62 0x0062 # LATIN SMALL LETTER B +0x63 0x0063 # LATIN SMALL LETTER C +0x64 0x0064 # LATIN SMALL LETTER D +0x65 0x0065 # LATIN SMALL LETTER E +0x66 0x0066 # LATIN SMALL LETTER F +0x67 0x0067 # LATIN SMALL LETTER G +0x68 0x0068 # LATIN SMALL LETTER H +0x69 0x0069 # LATIN SMALL LETTER I +0x6A 0x006A # LATIN SMALL LETTER J +0x6B 0x006B # LATIN SMALL LETTER K +0x6C 0x006C # LATIN SMALL LETTER L +0x6D 0x006D # LATIN SMALL LETTER M +0x6E 0x006E # LATIN SMALL LETTER N +0x6F 0x006F # LATIN SMALL LETTER O +0x70 0x0070 # LATIN SMALL LETTER P +0x71 0x0071 # LATIN SMALL LETTER Q +0x72 0x0072 # LATIN SMALL LETTER R +0x73 0x0073 # LATIN SMALL LETTER S +0x74 0x0074 # LATIN SMALL LETTER T +0x75 0x0075 # LATIN SMALL LETTER U +0x76 0x0076 # LATIN SMALL LETTER V +0x77 0x0077 # LATIN SMALL LETTER W +0x78 0x0078 # LATIN SMALL LETTER X +0x79 0x0079 # LATIN SMALL LETTER Y +0x7A 0x007A # LATIN SMALL LETTER Z +0x7B 0x007B # LEFT CURLY BRACKET +0x7C 0x007C # VERTICAL LINE +0x7D 0x007D # RIGHT CURLY BRACKET +0x7E 0x007E # TILDE +0x7F 0x007F # DELETE +0x80 0x0080 # +0x81 0x0081 # +0x82 0x0082 # +0x83 0x0083 # +0x84 0x0084 # +0x85 0x0085 # +0x86 0x0086 # +0x87 0x0087 # +0x88 0x0088 # +0x89 0x0089 # +0x8A 0x008A # +0x8B 0x008B # +0x8C 0x008C # +0x8D 0x008D # +0x8E 0x008E # +0x8F 0x008F # +0x90 0x0090 # +0x91 0x0091 # +0x92 0x0092 # +0x93 0x0093 # +0x94 0x0094 # +0x95 0x0095 # +0x96 0x0096 # +0x97 0x0097 # +0x98 0x0098 # +0x99 0x0099 # +0x9A 0x009A # +0x9B 0x009B # +0x9C 0x009C # +0x9D 0x009D # +0x9E 0x009E # +0x9F 0x009F # +0xA0 0x00A0 # NO-BREAK SPACE +0xA1 0x0E01 # THAI CHARACTER KO KAI +0xA2 0x0E02 # THAI CHARACTER KHO KHAI +0xA3 0x0E03 # THAI CHARACTER KHO KHUAT +0xA4 0x0E04 # THAI CHARACTER KHO KHWAI +0xA5 0x0E05 # THAI CHARACTER KHO KHON +0xA6 0x0E06 # THAI CHARACTER KHO RAKHANG +0xA7 0x0E07 # THAI CHARACTER NGO NGU +0xA8 0x0E08 # THAI CHARACTER CHO CHAN +0xA9 0x0E09 # THAI CHARACTER CHO CHING +0xAA 0x0E0A # THAI CHARACTER CHO CHANG +0xAB 0x0E0B # THAI CHARACTER SO SO +0xAC 0x0E0C # THAI CHARACTER CHO CHOE +0xAD 0x0E0D # THAI CHARACTER YO YING +0xAE 0x0E0E # THAI CHARACTER DO CHADA +0xAF 0x0E0F # THAI CHARACTER TO PATAK +0xB0 0x0E10 # THAI CHARACTER THO THAN +0xB1 0x0E11 # THAI CHARACTER THO NANGMONTHO +0xB2 0x0E12 # THAI CHARACTER THO PHUTHAO +0xB3 0x0E13 # THAI CHARACTER NO NEN +0xB4 0x0E14 # THAI CHARACTER DO DEK +0xB5 0x0E15 # THAI CHARACTER TO TAO +0xB6 0x0E16 # THAI CHARACTER THO THUNG +0xB7 0x0E17 # THAI CHARACTER THO THAHAN +0xB8 0x0E18 # THAI CHARACTER THO THONG +0xB9 0x0E19 # THAI CHARACTER NO NU +0xBA 0x0E1A # THAI CHARACTER BO BAIMAI +0xBB 0x0E1B # THAI CHARACTER PO PLA +0xBC 0x0E1C # THAI CHARACTER PHO PHUNG +0xBD 0x0E1D # THAI CHARACTER FO FA +0xBE 0x0E1E # THAI CHARACTER PHO PHAN +0xBF 0x0E1F # THAI CHARACTER FO FAN +0xC0 0x0E20 # THAI CHARACTER PHO SAMPHAO +0xC1 0x0E21 # THAI CHARACTER MO MA +0xC2 0x0E22 # THAI CHARACTER YO YAK +0xC3 0x0E23 # THAI CHARACTER RO RUA +0xC4 0x0E24 # THAI CHARACTER RU +0xC5 0x0E25 # THAI CHARACTER LO LING +0xC6 0x0E26 # THAI CHARACTER LU +0xC7 0x0E27 # THAI CHARACTER WO WAEN +0xC8 0x0E28 # THAI CHARACTER SO SALA +0xC9 0x0E29 # THAI CHARACTER SO RUSI +0xCA 0x0E2A # THAI CHARACTER SO SUA +0xCB 0x0E2B # THAI CHARACTER HO HIP +0xCC 0x0E2C # THAI CHARACTER LO CHULA +0xCD 0x0E2D # THAI CHARACTER O ANG +0xCE 0x0E2E # THAI CHARACTER HO NOKHUK +0xCF 0x0E2F # THAI CHARACTER PAIYANNOI +0xD0 0x0E30 # THAI CHARACTER SARA A +0xD1 0x0E31 # THAI CHARACTER MAI HAN-AKAT +0xD2 0x0E32 # THAI CHARACTER SARA AA +0xD3 0x0E33 # THAI CHARACTER SARA AM +0xD4 0x0E34 # THAI CHARACTER SARA I +0xD5 0x0E35 # THAI CHARACTER SARA II +0xD6 0x0E36 # THAI CHARACTER SARA UE +0xD7 0x0E37 # THAI CHARACTER SARA UEE +0xD8 0x0E38 # THAI CHARACTER SARA U +0xD9 0x0E39 # THAI CHARACTER SARA UU +0xDA 0x0E3A # THAI CHARACTER PHINTHU +0xDF 0x0E3F # THAI CURRENCY SYMBOL BAHT +0xE0 0x0E40 # THAI CHARACTER SARA E +0xE1 0x0E41 # THAI CHARACTER SARA AE +0xE2 0x0E42 # THAI CHARACTER SARA O +0xE3 0x0E43 # THAI CHARACTER SARA AI MAIMUAN +0xE4 0x0E44 # THAI CHARACTER SARA AI MAIMALAI +0xE5 0x0E45 # THAI CHARACTER LAKKHANGYAO +0xE6 0x0E46 # THAI CHARACTER MAIYAMOK +0xE7 0x0E47 # THAI CHARACTER MAITAIKHU +0xE8 0x0E48 # THAI CHARACTER MAI EK +0xE9 0x0E49 # THAI CHARACTER MAI THO +0xEA 0x0E4A # THAI CHARACTER MAI TRI +0xEB 0x0E4B # THAI CHARACTER MAI CHATTAWA +0xEC 0x0E4C # THAI CHARACTER THANTHAKHAT +0xED 0x0E4D # THAI CHARACTER NIKHAHIT +0xEE 0x0E4E # THAI CHARACTER YAMAKKAN +0xEF 0x0E4F # THAI CHARACTER FONGMAN +0xF0 0x0E50 # THAI DIGIT ZERO +0xF1 0x0E51 # THAI DIGIT ONE +0xF2 0x0E52 # THAI DIGIT TWO +0xF3 0x0E53 # THAI DIGIT THREE +0xF4 0x0E54 # THAI DIGIT FOUR +0xF5 0x0E55 # THAI DIGIT FIVE +0xF6 0x0E56 # THAI DIGIT SIX +0xF7 0x0E57 # THAI DIGIT SEVEN +0xF8 0x0E58 # THAI DIGIT EIGHT +0xF9 0x0E59 # THAI DIGIT NINE +0xFA 0x0E5A # THAI CHARACTER ANGKHANKHU +0xFB 0x0E5B # THAI CHARACTER KHOMUT diff --git a/codepage/iso8859-13.txt b/codepage/iso8859-13.txt new file mode 100644 index 0000000..cd11b53 --- /dev/null +++ b/codepage/iso8859-13.txt @@ -0,0 +1,299 @@ +# +# Name: ISO/IEC 8859-13:1998 to Unicode +# Unicode version: 3.0 +# Table version: 1.0 +# Table format: Format A +# Date: 1999 July 27 +# Authors: Ken Whistler +# +# Copyright (c) 1998 - 1999 Unicode, Inc. All Rights reserved. +# +# This file is provided as-is by Unicode, Inc. (The Unicode Consortium). +# No claims are made as to fitness for any particular purpose. No +# warranties of any kind are expressed or implied. The recipient +# agrees to determine applicability of information provided. If this +# file has been provided on optical media by Unicode, Inc., the sole +# remedy for any claim will be exchange of defective media within 90 +# days of receipt. +# +# Unicode, Inc. hereby grants the right to freely use the information +# supplied in this file in the creation of products supporting the +# Unicode Standard, and to make copies of this file in any form for +# internal or external distribution as long as this notice remains +# attached. +# +# General notes: +# +# This table contains the data the Unicode Consortium has on how +# ISO/IEC 8859-13:1998 characters map into Unicode. +# +# Format: Three tab-separated columns +# Column #1 is the ISO/IEC 8859-13 code (in hex as 0xXX) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 the Unicode name (follows a comment sign, '#') +# +# The entries are in ISO/IEC 8859-13 order. +# +# Updated versions of this file may be found in: +# +# +# Any comments or problems, contact +# Please note that is an archival address; +# notices will be checked, but do not expect an immediate response. +# +0x00 0x0000 # NULL +0x01 0x0001 # START OF HEADING +0x02 0x0002 # START OF TEXT +0x03 0x0003 # END OF TEXT +0x04 0x0004 # END OF TRANSMISSION +0x05 0x0005 # ENQUIRY +0x06 0x0006 # ACKNOWLEDGE +0x07 0x0007 # BELL +0x08 0x0008 # BACKSPACE +0x09 0x0009 # HORIZONTAL TABULATION +0x0A 0x000A # LINE FEED +0x0B 0x000B # VERTICAL TABULATION +0x0C 0x000C # FORM FEED +0x0D 0x000D # CARRIAGE RETURN +0x0E 0x000E # SHIFT OUT +0x0F 0x000F # SHIFT IN +0x10 0x0010 # DATA LINK ESCAPE +0x11 0x0011 # DEVICE CONTROL ONE +0x12 0x0012 # DEVICE CONTROL TWO +0x13 0x0013 # DEVICE CONTROL THREE +0x14 0x0014 # DEVICE CONTROL FOUR +0x15 0x0015 # NEGATIVE ACKNOWLEDGE +0x16 0x0016 # SYNCHRONOUS IDLE +0x17 0x0017 # END OF TRANSMISSION BLOCK +0x18 0x0018 # CANCEL +0x19 0x0019 # END OF MEDIUM +0x1A 0x001A # SUBSTITUTE +0x1B 0x001B # ESCAPE +0x1C 0x001C # FILE SEPARATOR +0x1D 0x001D # GROUP SEPARATOR +0x1E 0x001E # RECORD SEPARATOR +0x1F 0x001F # UNIT SEPARATOR +0x20 0x0020 # SPACE +0x21 0x0021 # EXCLAMATION MARK +0x22 0x0022 # QUOTATION MARK +0x23 0x0023 # NUMBER SIGN +0x24 0x0024 # DOLLAR SIGN +0x25 0x0025 # PERCENT SIGN +0x26 0x0026 # AMPERSAND +0x27 0x0027 # APOSTROPHE +0x28 0x0028 # LEFT PARENTHESIS +0x29 0x0029 # RIGHT PARENTHESIS +0x2A 0x002A # ASTERISK +0x2B 0x002B # PLUS SIGN +0x2C 0x002C # COMMA +0x2D 0x002D # HYPHEN-MINUS +0x2E 0x002E # FULL STOP +0x2F 0x002F # SOLIDUS +0x30 0x0030 # DIGIT ZERO +0x31 0x0031 # DIGIT ONE +0x32 0x0032 # DIGIT TWO +0x33 0x0033 # DIGIT THREE +0x34 0x0034 # DIGIT FOUR +0x35 0x0035 # DIGIT FIVE +0x36 0x0036 # DIGIT SIX +0x37 0x0037 # DIGIT SEVEN +0x38 0x0038 # DIGIT EIGHT +0x39 0x0039 # DIGIT NINE +0x3A 0x003A # COLON +0x3B 0x003B # SEMICOLON +0x3C 0x003C # LESS-THAN SIGN +0x3D 0x003D # EQUALS SIGN +0x3E 0x003E # GREATER-THAN SIGN +0x3F 0x003F # QUESTION MARK +0x40 0x0040 # COMMERCIAL AT +0x41 0x0041 # LATIN CAPITAL LETTER A +0x42 0x0042 # LATIN CAPITAL LETTER B +0x43 0x0043 # LATIN CAPITAL LETTER C +0x44 0x0044 # LATIN CAPITAL LETTER D +0x45 0x0045 # LATIN CAPITAL LETTER E +0x46 0x0046 # LATIN CAPITAL LETTER F +0x47 0x0047 # LATIN CAPITAL LETTER G +0x48 0x0048 # LATIN CAPITAL LETTER H +0x49 0x0049 # LATIN CAPITAL LETTER I +0x4A 0x004A # LATIN CAPITAL LETTER J +0x4B 0x004B # LATIN CAPITAL LETTER K +0x4C 0x004C # LATIN CAPITAL LETTER L +0x4D 0x004D # LATIN CAPITAL LETTER M +0x4E 0x004E # LATIN CAPITAL LETTER N +0x4F 0x004F # LATIN CAPITAL LETTER O +0x50 0x0050 # LATIN CAPITAL LETTER P +0x51 0x0051 # LATIN CAPITAL LETTER Q +0x52 0x0052 # LATIN CAPITAL LETTER R +0x53 0x0053 # LATIN CAPITAL LETTER S +0x54 0x0054 # LATIN CAPITAL LETTER T +0x55 0x0055 # LATIN CAPITAL LETTER U +0x56 0x0056 # LATIN CAPITAL LETTER V +0x57 0x0057 # LATIN CAPITAL LETTER W +0x58 0x0058 # LATIN CAPITAL LETTER X +0x59 0x0059 # LATIN CAPITAL LETTER Y +0x5A 0x005A # LATIN CAPITAL LETTER Z +0x5B 0x005B # LEFT SQUARE BRACKET +0x5C 0x005C # REVERSE SOLIDUS +0x5D 0x005D # RIGHT SQUARE BRACKET +0x5E 0x005E # CIRCUMFLEX ACCENT +0x5F 0x005F # LOW LINE +0x60 0x0060 # GRAVE ACCENT +0x61 0x0061 # LATIN SMALL LETTER A +0x62 0x0062 # LATIN SMALL LETTER B +0x63 0x0063 # LATIN SMALL LETTER C +0x64 0x0064 # LATIN SMALL LETTER D +0x65 0x0065 # LATIN SMALL LETTER E +0x66 0x0066 # LATIN SMALL LETTER F +0x67 0x0067 # LATIN SMALL LETTER G +0x68 0x0068 # LATIN SMALL LETTER H +0x69 0x0069 # LATIN SMALL LETTER I +0x6A 0x006A # LATIN SMALL LETTER J +0x6B 0x006B # LATIN SMALL LETTER K +0x6C 0x006C # LATIN SMALL LETTER L +0x6D 0x006D # LATIN SMALL LETTER M +0x6E 0x006E # LATIN SMALL LETTER N +0x6F 0x006F # LATIN SMALL LETTER O +0x70 0x0070 # LATIN SMALL LETTER P +0x71 0x0071 # LATIN SMALL LETTER Q +0x72 0x0072 # LATIN SMALL LETTER R +0x73 0x0073 # LATIN SMALL LETTER S +0x74 0x0074 # LATIN SMALL LETTER T +0x75 0x0075 # LATIN SMALL LETTER U +0x76 0x0076 # LATIN SMALL LETTER V +0x77 0x0077 # LATIN SMALL LETTER W +0x78 0x0078 # LATIN SMALL LETTER X +0x79 0x0079 # LATIN SMALL LETTER Y +0x7A 0x007A # LATIN SMALL LETTER Z +0x7B 0x007B # LEFT CURLY BRACKET +0x7C 0x007C # VERTICAL LINE +0x7D 0x007D # RIGHT CURLY BRACKET +0x7E 0x007E # TILDE +0x7F 0x007F # DELETE +0x80 0x0080 # +0x81 0x0081 # +0x82 0x0082 # +0x83 0x0083 # +0x84 0x0084 # +0x85 0x0085 # +0x86 0x0086 # +0x87 0x0087 # +0x88 0x0088 # +0x89 0x0089 # +0x8A 0x008A # +0x8B 0x008B # +0x8C 0x008C # +0x8D 0x008D # +0x8E 0x008E # +0x8F 0x008F # +0x90 0x0090 # +0x91 0x0091 # +0x92 0x0092 # +0x93 0x0093 # +0x94 0x0094 # +0x95 0x0095 # +0x96 0x0096 # +0x97 0x0097 # +0x98 0x0098 # +0x99 0x0099 # +0x9A 0x009A # +0x9B 0x009B # +0x9C 0x009C # +0x9D 0x009D # +0x9E 0x009E # +0x9F 0x009F # +0xA0 0x00A0 # NO-BREAK SPACE +0xA1 0x201D # RIGHT DOUBLE QUOTATION MARK +0xA2 0x00A2 # CENT SIGN +0xA3 0x00A3 # POUND SIGN +0xA4 0x00A4 # CURRENCY SIGN +0xA5 0x201E # DOUBLE LOW-9 QUOTATION MARK +0xA6 0x00A6 # BROKEN BAR +0xA7 0x00A7 # SECTION SIGN +0xA8 0x00D8 # LATIN CAPITAL LETTER O WITH STROKE +0xA9 0x00A9 # COPYRIGHT SIGN +0xAA 0x0156 # LATIN CAPITAL LETTER R WITH CEDILLA +0xAB 0x00AB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xAC 0x00AC # NOT SIGN +0xAD 0x00AD # SOFT HYPHEN +0xAE 0x00AE # REGISTERED SIGN +0xAF 0x00C6 # LATIN CAPITAL LETTER AE +0xB0 0x00B0 # DEGREE SIGN +0xB1 0x00B1 # PLUS-MINUS SIGN +0xB2 0x00B2 # SUPERSCRIPT TWO +0xB3 0x00B3 # SUPERSCRIPT THREE +0xB4 0x201C # LEFT DOUBLE QUOTATION MARK +0xB5 0x00B5 # MICRO SIGN +0xB6 0x00B6 # PILCROW SIGN +0xB7 0x00B7 # MIDDLE DOT +0xB8 0x00F8 # LATIN SMALL LETTER O WITH STROKE +0xB9 0x00B9 # SUPERSCRIPT ONE +0xBA 0x0157 # LATIN SMALL LETTER R WITH CEDILLA +0xBB 0x00BB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xBC 0x00BC # VULGAR FRACTION ONE QUARTER +0xBD 0x00BD # VULGAR FRACTION ONE HALF +0xBE 0x00BE # VULGAR FRACTION THREE QUARTERS +0xBF 0x00E6 # LATIN SMALL LETTER AE +0xC0 0x0104 # LATIN CAPITAL LETTER A WITH OGONEK +0xC1 0x012E # LATIN CAPITAL LETTER I WITH OGONEK +0xC2 0x0100 # LATIN CAPITAL LETTER A WITH MACRON +0xC3 0x0106 # LATIN CAPITAL LETTER C WITH ACUTE +0xC4 0x00C4 # LATIN CAPITAL LETTER A WITH DIAERESIS +0xC5 0x00C5 # LATIN CAPITAL LETTER A WITH RING ABOVE +0xC6 0x0118 # LATIN CAPITAL LETTER E WITH OGONEK +0xC7 0x0112 # LATIN CAPITAL LETTER E WITH MACRON +0xC8 0x010C # LATIN CAPITAL LETTER C WITH CARON +0xC9 0x00C9 # LATIN CAPITAL LETTER E WITH ACUTE +0xCA 0x0179 # LATIN CAPITAL LETTER Z WITH ACUTE +0xCB 0x0116 # LATIN CAPITAL LETTER E WITH DOT ABOVE +0xCC 0x0122 # LATIN CAPITAL LETTER G WITH CEDILLA +0xCD 0x0136 # LATIN CAPITAL LETTER K WITH CEDILLA +0xCE 0x012A # LATIN CAPITAL LETTER I WITH MACRON +0xCF 0x013B # LATIN CAPITAL LETTER L WITH CEDILLA +0xD0 0x0160 # LATIN CAPITAL LETTER S WITH CARON +0xD1 0x0143 # LATIN CAPITAL LETTER N WITH ACUTE +0xD2 0x0145 # LATIN CAPITAL LETTER N WITH CEDILLA +0xD3 0x00D3 # LATIN CAPITAL LETTER O WITH ACUTE +0xD4 0x014C # LATIN CAPITAL LETTER O WITH MACRON +0xD5 0x00D5 # LATIN CAPITAL LETTER O WITH TILDE +0xD6 0x00D6 # LATIN CAPITAL LETTER O WITH DIAERESIS +0xD7 0x00D7 # MULTIPLICATION SIGN +0xD8 0x0172 # LATIN CAPITAL LETTER U WITH OGONEK +0xD9 0x0141 # LATIN CAPITAL LETTER L WITH STROKE +0xDA 0x015A # LATIN CAPITAL LETTER S WITH ACUTE +0xDB 0x016A # LATIN CAPITAL LETTER U WITH MACRON +0xDC 0x00DC # LATIN CAPITAL LETTER U WITH DIAERESIS +0xDD 0x017B # LATIN CAPITAL LETTER Z WITH DOT ABOVE +0xDE 0x017D # LATIN CAPITAL LETTER Z WITH CARON +0xDF 0x00DF # LATIN SMALL LETTER SHARP S (German) +0xE0 0x0105 # LATIN SMALL LETTER A WITH OGONEK +0xE1 0x012F # LATIN SMALL LETTER I WITH OGONEK +0xE2 0x0101 # LATIN SMALL LETTER A WITH MACRON +0xE3 0x0107 # LATIN SMALL LETTER C WITH ACUTE +0xE4 0x00E4 # LATIN SMALL LETTER A WITH DIAERESIS +0xE5 0x00E5 # LATIN SMALL LETTER A WITH RING ABOVE +0xE6 0x0119 # LATIN SMALL LETTER E WITH OGONEK +0xE7 0x0113 # LATIN SMALL LETTER E WITH MACRON +0xE8 0x010D # LATIN SMALL LETTER C WITH CARON +0xE9 0x00E9 # LATIN SMALL LETTER E WITH ACUTE +0xEA 0x017A # LATIN SMALL LETTER Z WITH ACUTE +0xEB 0x0117 # LATIN SMALL LETTER E WITH DOT ABOVE +0xEC 0x0123 # LATIN SMALL LETTER G WITH CEDILLA +0xED 0x0137 # LATIN SMALL LETTER K WITH CEDILLA +0xEE 0x012B # LATIN SMALL LETTER I WITH MACRON +0xEF 0x013C # LATIN SMALL LETTER L WITH CEDILLA +0xF0 0x0161 # LATIN SMALL LETTER S WITH CARON +0xF1 0x0144 # LATIN SMALL LETTER N WITH ACUTE +0xF2 0x0146 # LATIN SMALL LETTER N WITH CEDILLA +0xF3 0x00F3 # LATIN SMALL LETTER O WITH ACUTE +0xF4 0x014D # LATIN SMALL LETTER O WITH MACRON +0xF5 0x00F5 # LATIN SMALL LETTER O WITH TILDE +0xF6 0x00F6 # LATIN SMALL LETTER O WITH DIAERESIS +0xF7 0x00F7 # DIVISION SIGN +0xF8 0x0173 # LATIN SMALL LETTER U WITH OGONEK +0xF9 0x0142 # LATIN SMALL LETTER L WITH STROKE +0xFA 0x015B # LATIN SMALL LETTER S WITH ACUTE +0xFB 0x016B # LATIN SMALL LETTER U WITH MACRON +0xFC 0x00FC # LATIN SMALL LETTER U WITH DIAERESIS +0xFD 0x017C # LATIN SMALL LETTER Z WITH DOT ABOVE +0xFE 0x017E # LATIN SMALL LETTER Z WITH CARON +0xFF 0x2019 # RIGHT SINGLE QUOTATION MARK diff --git a/codepage/iso8859-14.txt b/codepage/iso8859-14.txt new file mode 100644 index 0000000..73e9855 --- /dev/null +++ b/codepage/iso8859-14.txt @@ -0,0 +1,301 @@ +# +# Name: ISO/IEC 8859-14:1998 to Unicode +# Unicode version: 3.0 +# Table version: 1.0 +# Table format: Format A +# Date: 1999 July 27 +# Authors: Markus Kuhn +# Ken Whistler +# +# Copyright (c) 1998 - 1999 Unicode, Inc. All Rights reserved. +# +# This file is provided as-is by Unicode, Inc. (The Unicode Consortium). +# No claims are made as to fitness for any particular purpose. No +# warranties of any kind are expressed or implied. The recipient +# agrees to determine applicability of information provided. If this +# file has been provided on optical media by Unicode, Inc., the sole +# remedy for any claim will be exchange of defective media within 90 +# days of receipt. +# +# Unicode, Inc. hereby grants the right to freely use the information +# supplied in this file in the creation of products supporting the +# Unicode Standard, and to make copies of this file in any form for +# internal or external distribution as long as this notice remains +# attached. +# +# General notes: +# +# This table contains the data the Unicode Consortium has on how +# ISO/IEC 8859-14:1998 characters map into Unicode. +# +# Format: Three tab-separated columns +# Column #1 is the ISO/IEC 8859-14 code (in hex as 0xXX) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 the Unicode name (follows a comment sign, '#') +# +# The entries are in ISO/IEC 8859-14 order. +# +# Updated versions of this file may be found in: +# +# +# Any comments or problems, contact +# Please note that is an archival address; +# notices will be checked, but do not expect an immediate response. +# +0x00 0x0000 # NULL +0x01 0x0001 # START OF HEADING +0x02 0x0002 # START OF TEXT +0x03 0x0003 # END OF TEXT +0x04 0x0004 # END OF TRANSMISSION +0x05 0x0005 # ENQUIRY +0x06 0x0006 # ACKNOWLEDGE +0x07 0x0007 # BELL +0x08 0x0008 # BACKSPACE +0x09 0x0009 # HORIZONTAL TABULATION +0x0A 0x000A # LINE FEED +0x0B 0x000B # VERTICAL TABULATION +0x0C 0x000C # FORM FEED +0x0D 0x000D # CARRIAGE RETURN +0x0E 0x000E # SHIFT OUT +0x0F 0x000F # SHIFT IN +0x10 0x0010 # DATA LINK ESCAPE +0x11 0x0011 # DEVICE CONTROL ONE +0x12 0x0012 # DEVICE CONTROL TWO +0x13 0x0013 # DEVICE CONTROL THREE +0x14 0x0014 # DEVICE CONTROL FOUR +0x15 0x0015 # NEGATIVE ACKNOWLEDGE +0x16 0x0016 # SYNCHRONOUS IDLE +0x17 0x0017 # END OF TRANSMISSION BLOCK +0x18 0x0018 # CANCEL +0x19 0x0019 # END OF MEDIUM +0x1A 0x001A # SUBSTITUTE +0x1B 0x001B # ESCAPE +0x1C 0x001C # FILE SEPARATOR +0x1D 0x001D # GROUP SEPARATOR +0x1E 0x001E # RECORD SEPARATOR +0x1F 0x001F # UNIT SEPARATOR +0x20 0x0020 # SPACE +0x21 0x0021 # EXCLAMATION MARK +0x22 0x0022 # QUOTATION MARK +0x23 0x0023 # NUMBER SIGN +0x24 0x0024 # DOLLAR SIGN +0x25 0x0025 # PERCENT SIGN +0x26 0x0026 # AMPERSAND +0x27 0x0027 # APOSTROPHE +0x28 0x0028 # LEFT PARENTHESIS +0x29 0x0029 # RIGHT PARENTHESIS +0x2A 0x002A # ASTERISK +0x2B 0x002B # PLUS SIGN +0x2C 0x002C # COMMA +0x2D 0x002D # HYPHEN-MINUS +0x2E 0x002E # FULL STOP +0x2F 0x002F # SOLIDUS +0x30 0x0030 # DIGIT ZERO +0x31 0x0031 # DIGIT ONE +0x32 0x0032 # DIGIT TWO +0x33 0x0033 # DIGIT THREE +0x34 0x0034 # DIGIT FOUR +0x35 0x0035 # DIGIT FIVE +0x36 0x0036 # DIGIT SIX +0x37 0x0037 # DIGIT SEVEN +0x38 0x0038 # DIGIT EIGHT +0x39 0x0039 # DIGIT NINE +0x3A 0x003A # COLON +0x3B 0x003B # SEMICOLON +0x3C 0x003C # LESS-THAN SIGN +0x3D 0x003D # EQUALS SIGN +0x3E 0x003E # GREATER-THAN SIGN +0x3F 0x003F # QUESTION MARK +0x40 0x0040 # COMMERCIAL AT +0x41 0x0041 # LATIN CAPITAL LETTER A +0x42 0x0042 # LATIN CAPITAL LETTER B +0x43 0x0043 # LATIN CAPITAL LETTER C +0x44 0x0044 # LATIN CAPITAL LETTER D +0x45 0x0045 # LATIN CAPITAL LETTER E +0x46 0x0046 # LATIN CAPITAL LETTER F +0x47 0x0047 # LATIN CAPITAL LETTER G +0x48 0x0048 # LATIN CAPITAL LETTER H +0x49 0x0049 # LATIN CAPITAL LETTER I +0x4A 0x004A # LATIN CAPITAL LETTER J +0x4B 0x004B # LATIN CAPITAL LETTER K +0x4C 0x004C # LATIN CAPITAL LETTER L +0x4D 0x004D # LATIN CAPITAL LETTER M +0x4E 0x004E # LATIN CAPITAL LETTER N +0x4F 0x004F # LATIN CAPITAL LETTER O +0x50 0x0050 # LATIN CAPITAL LETTER P +0x51 0x0051 # LATIN CAPITAL LETTER Q +0x52 0x0052 # LATIN CAPITAL LETTER R +0x53 0x0053 # LATIN CAPITAL LETTER S +0x54 0x0054 # LATIN CAPITAL LETTER T +0x55 0x0055 # LATIN CAPITAL LETTER U +0x56 0x0056 # LATIN CAPITAL LETTER V +0x57 0x0057 # LATIN CAPITAL LETTER W +0x58 0x0058 # LATIN CAPITAL LETTER X +0x59 0x0059 # LATIN CAPITAL LETTER Y +0x5A 0x005A # LATIN CAPITAL LETTER Z +0x5B 0x005B # LEFT SQUARE BRACKET +0x5C 0x005C # REVERSE SOLIDUS +0x5D 0x005D # RIGHT SQUARE BRACKET +0x5E 0x005E # CIRCUMFLEX ACCENT +0x5F 0x005F # LOW LINE +0x60 0x0060 # GRAVE ACCENT +0x61 0x0061 # LATIN SMALL LETTER A +0x62 0x0062 # LATIN SMALL LETTER B +0x63 0x0063 # LATIN SMALL LETTER C +0x64 0x0064 # LATIN SMALL LETTER D +0x65 0x0065 # LATIN SMALL LETTER E +0x66 0x0066 # LATIN SMALL LETTER F +0x67 0x0067 # LATIN SMALL LETTER G +0x68 0x0068 # LATIN SMALL LETTER H +0x69 0x0069 # LATIN SMALL LETTER I +0x6A 0x006A # LATIN SMALL LETTER J +0x6B 0x006B # LATIN SMALL LETTER K +0x6C 0x006C # LATIN SMALL LETTER L +0x6D 0x006D # LATIN SMALL LETTER M +0x6E 0x006E # LATIN SMALL LETTER N +0x6F 0x006F # LATIN SMALL LETTER O +0x70 0x0070 # LATIN SMALL LETTER P +0x71 0x0071 # LATIN SMALL LETTER Q +0x72 0x0072 # LATIN SMALL LETTER R +0x73 0x0073 # LATIN SMALL LETTER S +0x74 0x0074 # LATIN SMALL LETTER T +0x75 0x0075 # LATIN SMALL LETTER U +0x76 0x0076 # LATIN SMALL LETTER V +0x77 0x0077 # LATIN SMALL LETTER W +0x78 0x0078 # LATIN SMALL LETTER X +0x79 0x0079 # LATIN SMALL LETTER Y +0x7A 0x007A # LATIN SMALL LETTER Z +0x7B 0x007B # LEFT CURLY BRACKET +0x7C 0x007C # VERTICAL LINE +0x7D 0x007D # RIGHT CURLY BRACKET +0x7E 0x007E # TILDE +0x7F 0x007F # DELETE +0x80 0x0080 # +0x81 0x0081 # +0x82 0x0082 # +0x83 0x0083 # +0x84 0x0084 # +0x85 0x0085 # +0x86 0x0086 # +0x87 0x0087 # +0x88 0x0088 # +0x89 0x0089 # +0x8A 0x008A # +0x8B 0x008B # +0x8C 0x008C # +0x8D 0x008D # +0x8E 0x008E # +0x8F 0x008F # +0x90 0x0090 # +0x91 0x0091 # +0x92 0x0092 # +0x93 0x0093 # +0x94 0x0094 # +0x95 0x0095 # +0x96 0x0096 # +0x97 0x0097 # +0x98 0x0098 # +0x99 0x0099 # +0x9A 0x009A # +0x9B 0x009B # +0x9C 0x009C # +0x9D 0x009D # +0x9E 0x009E # +0x9F 0x009F # +0xA0 0x00A0 # NO-BREAK SPACE +0xA1 0x1E02 # LATIN CAPITAL LETTER B WITH DOT ABOVE +0xA2 0x1E03 # LATIN SMALL LETTER B WITH DOT ABOVE +0xA3 0x00A3 # POUND SIGN +0xA4 0x010A # LATIN CAPITAL LETTER C WITH DOT ABOVE +0xA5 0x010B # LATIN SMALL LETTER C WITH DOT ABOVE +0xA6 0x1E0A # LATIN CAPITAL LETTER D WITH DOT ABOVE +0xA7 0x00A7 # SECTION SIGN +0xA8 0x1E80 # LATIN CAPITAL LETTER W WITH GRAVE +0xA9 0x00A9 # COPYRIGHT SIGN +0xAA 0x1E82 # LATIN CAPITAL LETTER W WITH ACUTE +0xAB 0x1E0B # LATIN SMALL LETTER D WITH DOT ABOVE +0xAC 0x1EF2 # LATIN CAPITAL LETTER Y WITH GRAVE +0xAD 0x00AD # SOFT HYPHEN +0xAE 0x00AE # REGISTERED SIGN +0xAF 0x0178 # LATIN CAPITAL LETTER Y WITH DIAERESIS +0xB0 0x1E1E # LATIN CAPITAL LETTER F WITH DOT ABOVE +0xB1 0x1E1F # LATIN SMALL LETTER F WITH DOT ABOVE +0xB2 0x0120 # LATIN CAPITAL LETTER G WITH DOT ABOVE +0xB3 0x0121 # LATIN SMALL LETTER G WITH DOT ABOVE +0xB4 0x1E40 # LATIN CAPITAL LETTER M WITH DOT ABOVE +0xB5 0x1E41 # LATIN SMALL LETTER M WITH DOT ABOVE +0xB6 0x00B6 # PILCROW SIGN +0xB7 0x1E56 # LATIN CAPITAL LETTER P WITH DOT ABOVE +0xB8 0x1E81 # LATIN SMALL LETTER W WITH GRAVE +0xB9 0x1E57 # LATIN SMALL LETTER P WITH DOT ABOVE +0xBA 0x1E83 # LATIN SMALL LETTER W WITH ACUTE +0xBB 0x1E60 # LATIN CAPITAL LETTER S WITH DOT ABOVE +0xBC 0x1EF3 # LATIN SMALL LETTER Y WITH GRAVE +0xBD 0x1E84 # LATIN CAPITAL LETTER W WITH DIAERESIS +0xBE 0x1E85 # LATIN SMALL LETTER W WITH DIAERESIS +0xBF 0x1E61 # LATIN SMALL LETTER S WITH DOT ABOVE +0xC0 0x00C0 # LATIN CAPITAL LETTER A WITH GRAVE +0xC1 0x00C1 # LATIN CAPITAL LETTER A WITH ACUTE +0xC2 0x00C2 # LATIN CAPITAL LETTER A WITH CIRCUMFLEX +0xC3 0x00C3 # LATIN CAPITAL LETTER A WITH TILDE +0xC4 0x00C4 # LATIN CAPITAL LETTER A WITH DIAERESIS +0xC5 0x00C5 # LATIN CAPITAL LETTER A WITH RING ABOVE +0xC6 0x00C6 # LATIN CAPITAL LETTER AE +0xC7 0x00C7 # LATIN CAPITAL LETTER C WITH CEDILLA +0xC8 0x00C8 # LATIN CAPITAL LETTER E WITH GRAVE +0xC9 0x00C9 # LATIN CAPITAL LETTER E WITH ACUTE +0xCA 0x00CA # LATIN CAPITAL LETTER E WITH CIRCUMFLEX +0xCB 0x00CB # LATIN CAPITAL LETTER E WITH DIAERESIS +0xCC 0x00CC # LATIN CAPITAL LETTER I WITH GRAVE +0xCD 0x00CD # LATIN CAPITAL LETTER I WITH ACUTE +0xCE 0x00CE # LATIN CAPITAL LETTER I WITH CIRCUMFLEX +0xCF 0x00CF # LATIN CAPITAL LETTER I WITH DIAERESIS +0xD0 0x0174 # LATIN CAPITAL LETTER W WITH CIRCUMFLEX +0xD1 0x00D1 # LATIN CAPITAL LETTER N WITH TILDE +0xD2 0x00D2 # LATIN CAPITAL LETTER O WITH GRAVE +0xD3 0x00D3 # LATIN CAPITAL LETTER O WITH ACUTE +0xD4 0x00D4 # LATIN CAPITAL LETTER O WITH CIRCUMFLEX +0xD5 0x00D5 # LATIN CAPITAL LETTER O WITH TILDE +0xD6 0x00D6 # LATIN CAPITAL LETTER O WITH DIAERESIS +0xD7 0x1E6A # LATIN CAPITAL LETTER T WITH DOT ABOVE +0xD8 0x00D8 # LATIN CAPITAL LETTER O WITH STROKE +0xD9 0x00D9 # LATIN CAPITAL LETTER U WITH GRAVE +0xDA 0x00DA # LATIN CAPITAL LETTER U WITH ACUTE +0xDB 0x00DB # LATIN CAPITAL LETTER U WITH CIRCUMFLEX +0xDC 0x00DC # LATIN CAPITAL LETTER U WITH DIAERESIS +0xDD 0x00DD # LATIN CAPITAL LETTER Y WITH ACUTE +0xDE 0x0176 # LATIN CAPITAL LETTER Y WITH CIRCUMFLEX +0xDF 0x00DF # LATIN SMALL LETTER SHARP S +0xE0 0x00E0 # LATIN SMALL LETTER A WITH GRAVE +0xE1 0x00E1 # LATIN SMALL LETTER A WITH ACUTE +0xE2 0x00E2 # LATIN SMALL LETTER A WITH CIRCUMFLEX +0xE3 0x00E3 # LATIN SMALL LETTER A WITH TILDE +0xE4 0x00E4 # LATIN SMALL LETTER A WITH DIAERESIS +0xE5 0x00E5 # LATIN SMALL LETTER A WITH RING ABOVE +0xE6 0x00E6 # LATIN SMALL LETTER AE +0xE7 0x00E7 # LATIN SMALL LETTER C WITH CEDILLA +0xE8 0x00E8 # LATIN SMALL LETTER E WITH GRAVE +0xE9 0x00E9 # LATIN SMALL LETTER E WITH ACUTE +0xEA 0x00EA # LATIN SMALL LETTER E WITH CIRCUMFLEX +0xEB 0x00EB # LATIN SMALL LETTER E WITH DIAERESIS +0xEC 0x00EC # LATIN SMALL LETTER I WITH GRAVE +0xED 0x00ED # LATIN SMALL LETTER I WITH ACUTE +0xEE 0x00EE # LATIN SMALL LETTER I WITH CIRCUMFLEX +0xEF 0x00EF # LATIN SMALL LETTER I WITH DIAERESIS +0xF0 0x0175 # LATIN SMALL LETTER W WITH CIRCUMFLEX +0xF1 0x00F1 # LATIN SMALL LETTER N WITH TILDE +0xF2 0x00F2 # LATIN SMALL LETTER O WITH GRAVE +0xF3 0x00F3 # LATIN SMALL LETTER O WITH ACUTE +0xF4 0x00F4 # LATIN SMALL LETTER O WITH CIRCUMFLEX +0xF5 0x00F5 # LATIN SMALL LETTER O WITH TILDE +0xF6 0x00F6 # LATIN SMALL LETTER O WITH DIAERESIS +0xF7 0x1E6B # LATIN SMALL LETTER T WITH DOT ABOVE +0xF8 0x00F8 # LATIN SMALL LETTER O WITH STROKE +0xF9 0x00F9 # LATIN SMALL LETTER U WITH GRAVE +0xFA 0x00FA # LATIN SMALL LETTER U WITH ACUTE +0xFB 0x00FB # LATIN SMALL LETTER U WITH CIRCUMFLEX +0xFC 0x00FC # LATIN SMALL LETTER U WITH DIAERESIS +0xFD 0x00FD # LATIN SMALL LETTER Y WITH ACUTE +0xFE 0x0177 # LATIN SMALL LETTER Y WITH CIRCUMFLEX +0xFF 0x00FF # LATIN SMALL LETTER Y WITH DIAERESIS + diff --git a/codepage/iso8859-15.txt b/codepage/iso8859-15.txt new file mode 100644 index 0000000..ab2f32f --- /dev/null +++ b/codepage/iso8859-15.txt @@ -0,0 +1,303 @@ +# +# Name: ISO/IEC 8859-15:1999 to Unicode +# Unicode version: 3.0 +# Table version: 1.0 +# Table format: Format A +# Date: 1999 July 27 +# Authors: Markus Kuhn +# Ken Whistler +# +# Copyright (c) 1998 - 1999 Unicode, Inc. All Rights reserved. +# +# This file is provided as-is by Unicode, Inc. (The Unicode Consortium). +# No claims are made as to fitness for any particular purpose. No +# warranties of any kind are expressed or implied. The recipient +# agrees to determine applicability of information provided. If this +# file has been provided on optical media by Unicode, Inc., the sole +# remedy for any claim will be exchange of defective media within 90 +# days of receipt. +# +# Unicode, Inc. hereby grants the right to freely use the information +# supplied in this file in the creation of products supporting the +# Unicode Standard, and to make copies of this file in any form for +# internal or external distribution as long as this notice remains +# attached. +# +# General notes: +# +# This table contains the data the Unicode Consortium has on how +# ISO/IEC 8859-15:1999 characters map into Unicode. +# +# Format: Three tab-separated columns +# Column #1 is the ISO/IEC 8859-15 code (in hex as 0xXX) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 the Unicode name (follows a comment sign, '#') +# +# The entries are in ISO/IEC 8859-15 order. +# +# Version history +# +# Updated versions of this file may be found in: +# +# +# Any comments or problems, contact +# Please note that is an archival address; +# notices will be checked, but do not expect an immediate response. +# +0x00 0x0000 # NULL +0x01 0x0001 # START OF HEADING +0x02 0x0002 # START OF TEXT +0x03 0x0003 # END OF TEXT +0x04 0x0004 # END OF TRANSMISSION +0x05 0x0005 # ENQUIRY +0x06 0x0006 # ACKNOWLEDGE +0x07 0x0007 # BELL +0x08 0x0008 # BACKSPACE +0x09 0x0009 # HORIZONTAL TABULATION +0x0A 0x000A # LINE FEED +0x0B 0x000B # VERTICAL TABULATION +0x0C 0x000C # FORM FEED +0x0D 0x000D # CARRIAGE RETURN +0x0E 0x000E # SHIFT OUT +0x0F 0x000F # SHIFT IN +0x10 0x0010 # DATA LINK ESCAPE +0x11 0x0011 # DEVICE CONTROL ONE +0x12 0x0012 # DEVICE CONTROL TWO +0x13 0x0013 # DEVICE CONTROL THREE +0x14 0x0014 # DEVICE CONTROL FOUR +0x15 0x0015 # NEGATIVE ACKNOWLEDGE +0x16 0x0016 # SYNCHRONOUS IDLE +0x17 0x0017 # END OF TRANSMISSION BLOCK +0x18 0x0018 # CANCEL +0x19 0x0019 # END OF MEDIUM +0x1A 0x001A # SUBSTITUTE +0x1B 0x001B # ESCAPE +0x1C 0x001C # FILE SEPARATOR +0x1D 0x001D # GROUP SEPARATOR +0x1E 0x001E # RECORD SEPARATOR +0x1F 0x001F # UNIT SEPARATOR +0x20 0x0020 # SPACE +0x21 0x0021 # EXCLAMATION MARK +0x22 0x0022 # QUOTATION MARK +0x23 0x0023 # NUMBER SIGN +0x24 0x0024 # DOLLAR SIGN +0x25 0x0025 # PERCENT SIGN +0x26 0x0026 # AMPERSAND +0x27 0x0027 # APOSTROPHE +0x28 0x0028 # LEFT PARENTHESIS +0x29 0x0029 # RIGHT PARENTHESIS +0x2A 0x002A # ASTERISK +0x2B 0x002B # PLUS SIGN +0x2C 0x002C # COMMA +0x2D 0x002D # HYPHEN-MINUS +0x2E 0x002E # FULL STOP +0x2F 0x002F # SOLIDUS +0x30 0x0030 # DIGIT ZERO +0x31 0x0031 # DIGIT ONE +0x32 0x0032 # DIGIT TWO +0x33 0x0033 # DIGIT THREE +0x34 0x0034 # DIGIT FOUR +0x35 0x0035 # DIGIT FIVE +0x36 0x0036 # DIGIT SIX +0x37 0x0037 # DIGIT SEVEN +0x38 0x0038 # DIGIT EIGHT +0x39 0x0039 # DIGIT NINE +0x3A 0x003A # COLON +0x3B 0x003B # SEMICOLON +0x3C 0x003C # LESS-THAN SIGN +0x3D 0x003D # EQUALS SIGN +0x3E 0x003E # GREATER-THAN SIGN +0x3F 0x003F # QUESTION MARK +0x40 0x0040 # COMMERCIAL AT +0x41 0x0041 # LATIN CAPITAL LETTER A +0x42 0x0042 # LATIN CAPITAL LETTER B +0x43 0x0043 # LATIN CAPITAL LETTER C +0x44 0x0044 # LATIN CAPITAL LETTER D +0x45 0x0045 # LATIN CAPITAL LETTER E +0x46 0x0046 # LATIN CAPITAL LETTER F +0x47 0x0047 # LATIN CAPITAL LETTER G +0x48 0x0048 # LATIN CAPITAL LETTER H +0x49 0x0049 # LATIN CAPITAL LETTER I +0x4A 0x004A # LATIN CAPITAL LETTER J +0x4B 0x004B # LATIN CAPITAL LETTER K +0x4C 0x004C # LATIN CAPITAL LETTER L +0x4D 0x004D # LATIN CAPITAL LETTER M +0x4E 0x004E # LATIN CAPITAL LETTER N +0x4F 0x004F # LATIN CAPITAL LETTER O +0x50 0x0050 # LATIN CAPITAL LETTER P +0x51 0x0051 # LATIN CAPITAL LETTER Q +0x52 0x0052 # LATIN CAPITAL LETTER R +0x53 0x0053 # LATIN CAPITAL LETTER S +0x54 0x0054 # LATIN CAPITAL LETTER T +0x55 0x0055 # LATIN CAPITAL LETTER U +0x56 0x0056 # LATIN CAPITAL LETTER V +0x57 0x0057 # LATIN CAPITAL LETTER W +0x58 0x0058 # LATIN CAPITAL LETTER X +0x59 0x0059 # LATIN CAPITAL LETTER Y +0x5A 0x005A # LATIN CAPITAL LETTER Z +0x5B 0x005B # LEFT SQUARE BRACKET +0x5C 0x005C # REVERSE SOLIDUS +0x5D 0x005D # RIGHT SQUARE BRACKET +0x5E 0x005E # CIRCUMFLEX ACCENT +0x5F 0x005F # LOW LINE +0x60 0x0060 # GRAVE ACCENT +0x61 0x0061 # LATIN SMALL LETTER A +0x62 0x0062 # LATIN SMALL LETTER B +0x63 0x0063 # LATIN SMALL LETTER C +0x64 0x0064 # LATIN SMALL LETTER D +0x65 0x0065 # LATIN SMALL LETTER E +0x66 0x0066 # LATIN SMALL LETTER F +0x67 0x0067 # LATIN SMALL LETTER G +0x68 0x0068 # LATIN SMALL LETTER H +0x69 0x0069 # LATIN SMALL LETTER I +0x6A 0x006A # LATIN SMALL LETTER J +0x6B 0x006B # LATIN SMALL LETTER K +0x6C 0x006C # LATIN SMALL LETTER L +0x6D 0x006D # LATIN SMALL LETTER M +0x6E 0x006E # LATIN SMALL LETTER N +0x6F 0x006F # LATIN SMALL LETTER O +0x70 0x0070 # LATIN SMALL LETTER P +0x71 0x0071 # LATIN SMALL LETTER Q +0x72 0x0072 # LATIN SMALL LETTER R +0x73 0x0073 # LATIN SMALL LETTER S +0x74 0x0074 # LATIN SMALL LETTER T +0x75 0x0075 # LATIN SMALL LETTER U +0x76 0x0076 # LATIN SMALL LETTER V +0x77 0x0077 # LATIN SMALL LETTER W +0x78 0x0078 # LATIN SMALL LETTER X +0x79 0x0079 # LATIN SMALL LETTER Y +0x7A 0x007A # LATIN SMALL LETTER Z +0x7B 0x007B # LEFT CURLY BRACKET +0x7C 0x007C # VERTICAL LINE +0x7D 0x007D # RIGHT CURLY BRACKET +0x7E 0x007E # TILDE +0x7F 0x007F # DELETE +0x80 0x0080 # +0x81 0x0081 # +0x82 0x0082 # +0x83 0x0083 # +0x84 0x0084 # +0x85 0x0085 # +0x86 0x0086 # +0x87 0x0087 # +0x88 0x0088 # +0x89 0x0089 # +0x8A 0x008A # +0x8B 0x008B # +0x8C 0x008C # +0x8D 0x008D # +0x8E 0x008E # +0x8F 0x008F # +0x90 0x0090 # +0x91 0x0091 # +0x92 0x0092 # +0x93 0x0093 # +0x94 0x0094 # +0x95 0x0095 # +0x96 0x0096 # +0x97 0x0097 # +0x98 0x0098 # +0x99 0x0099 # +0x9A 0x009A # +0x9B 0x009B # +0x9C 0x009C # +0x9D 0x009D # +0x9E 0x009E # +0x9F 0x009F # +0xA0 0x00A0 # NO-BREAK SPACE +0xA1 0x00A1 # INVERTED EXCLAMATION MARK +0xA2 0x00A2 # CENT SIGN +0xA3 0x00A3 # POUND SIGN +0xA4 0x20AC # EURO SIGN +0xA5 0x00A5 # YEN SIGN +0xA6 0x0160 # LATIN CAPITAL LETTER S WITH CARON +0xA7 0x00A7 # SECTION SIGN +0xA8 0x0161 # LATIN SMALL LETTER S WITH CARON +0xA9 0x00A9 # COPYRIGHT SIGN +0xAA 0x00AA # FEMININE ORDINAL INDICATOR +0xAB 0x00AB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xAC 0x00AC # NOT SIGN +0xAD 0x00AD # SOFT HYPHEN +0xAE 0x00AE # REGISTERED SIGN +0xAF 0x00AF # MACRON +0xB0 0x00B0 # DEGREE SIGN +0xB1 0x00B1 # PLUS-MINUS SIGN +0xB2 0x00B2 # SUPERSCRIPT TWO +0xB3 0x00B3 # SUPERSCRIPT THREE +0xB4 0x017D # LATIN CAPITAL LETTER Z WITH CARON +0xB5 0x00B5 # MICRO SIGN +0xB6 0x00B6 # PILCROW SIGN +0xB7 0x00B7 # MIDDLE DOT +0xB8 0x017E # LATIN SMALL LETTER Z WITH CARON +0xB9 0x00B9 # SUPERSCRIPT ONE +0xBA 0x00BA # MASCULINE ORDINAL INDICATOR +0xBB 0x00BB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xBC 0x0152 # LATIN CAPITAL LIGATURE OE +0xBD 0x0153 # LATIN SMALL LIGATURE OE +0xBE 0x0178 # LATIN CAPITAL LETTER Y WITH DIAERESIS +0xBF 0x00BF # INVERTED QUESTION MARK +0xC0 0x00C0 # LATIN CAPITAL LETTER A WITH GRAVE +0xC1 0x00C1 # LATIN CAPITAL LETTER A WITH ACUTE +0xC2 0x00C2 # LATIN CAPITAL LETTER A WITH CIRCUMFLEX +0xC3 0x00C3 # LATIN CAPITAL LETTER A WITH TILDE +0xC4 0x00C4 # LATIN CAPITAL LETTER A WITH DIAERESIS +0xC5 0x00C5 # LATIN CAPITAL LETTER A WITH RING ABOVE +0xC6 0x00C6 # LATIN CAPITAL LETTER AE +0xC7 0x00C7 # LATIN CAPITAL LETTER C WITH CEDILLA +0xC8 0x00C8 # LATIN CAPITAL LETTER E WITH GRAVE +0xC9 0x00C9 # LATIN CAPITAL LETTER E WITH ACUTE +0xCA 0x00CA # LATIN CAPITAL LETTER E WITH CIRCUMFLEX +0xCB 0x00CB # LATIN CAPITAL LETTER E WITH DIAERESIS +0xCC 0x00CC # LATIN CAPITAL LETTER I WITH GRAVE +0xCD 0x00CD # LATIN CAPITAL LETTER I WITH ACUTE +0xCE 0x00CE # LATIN CAPITAL LETTER I WITH CIRCUMFLEX +0xCF 0x00CF # LATIN CAPITAL LETTER I WITH DIAERESIS +0xD0 0x00D0 # LATIN CAPITAL LETTER ETH +0xD1 0x00D1 # LATIN CAPITAL LETTER N WITH TILDE +0xD2 0x00D2 # LATIN CAPITAL LETTER O WITH GRAVE +0xD3 0x00D3 # LATIN CAPITAL LETTER O WITH ACUTE +0xD4 0x00D4 # LATIN CAPITAL LETTER O WITH CIRCUMFLEX +0xD5 0x00D5 # LATIN CAPITAL LETTER O WITH TILDE +0xD6 0x00D6 # LATIN CAPITAL LETTER O WITH DIAERESIS +0xD7 0x00D7 # MULTIPLICATION SIGN +0xD8 0x00D8 # LATIN CAPITAL LETTER O WITH STROKE +0xD9 0x00D9 # LATIN CAPITAL LETTER U WITH GRAVE +0xDA 0x00DA # LATIN CAPITAL LETTER U WITH ACUTE +0xDB 0x00DB # LATIN CAPITAL LETTER U WITH CIRCUMFLEX +0xDC 0x00DC # LATIN CAPITAL LETTER U WITH DIAERESIS +0xDD 0x00DD # LATIN CAPITAL LETTER Y WITH ACUTE +0xDE 0x00DE # LATIN CAPITAL LETTER THORN +0xDF 0x00DF # LATIN SMALL LETTER SHARP S +0xE0 0x00E0 # LATIN SMALL LETTER A WITH GRAVE +0xE1 0x00E1 # LATIN SMALL LETTER A WITH ACUTE +0xE2 0x00E2 # LATIN SMALL LETTER A WITH CIRCUMFLEX +0xE3 0x00E3 # LATIN SMALL LETTER A WITH TILDE +0xE4 0x00E4 # LATIN SMALL LETTER A WITH DIAERESIS +0xE5 0x00E5 # LATIN SMALL LETTER A WITH RING ABOVE +0xE6 0x00E6 # LATIN SMALL LETTER AE +0xE7 0x00E7 # LATIN SMALL LETTER C WITH CEDILLA +0xE8 0x00E8 # LATIN SMALL LETTER E WITH GRAVE +0xE9 0x00E9 # LATIN SMALL LETTER E WITH ACUTE +0xEA 0x00EA # LATIN SMALL LETTER E WITH CIRCUMFLEX +0xEB 0x00EB # LATIN SMALL LETTER E WITH DIAERESIS +0xEC 0x00EC # LATIN SMALL LETTER I WITH GRAVE +0xED 0x00ED # LATIN SMALL LETTER I WITH ACUTE +0xEE 0x00EE # LATIN SMALL LETTER I WITH CIRCUMFLEX +0xEF 0x00EF # LATIN SMALL LETTER I WITH DIAERESIS +0xF0 0x00F0 # LATIN SMALL LETTER ETH +0xF1 0x00F1 # LATIN SMALL LETTER N WITH TILDE +0xF2 0x00F2 # LATIN SMALL LETTER O WITH GRAVE +0xF3 0x00F3 # LATIN SMALL LETTER O WITH ACUTE +0xF4 0x00F4 # LATIN SMALL LETTER O WITH CIRCUMFLEX +0xF5 0x00F5 # LATIN SMALL LETTER O WITH TILDE +0xF6 0x00F6 # LATIN SMALL LETTER O WITH DIAERESIS +0xF7 0x00F7 # DIVISION SIGN +0xF8 0x00F8 # LATIN SMALL LETTER O WITH STROKE +0xF9 0x00F9 # LATIN SMALL LETTER U WITH GRAVE +0xFA 0x00FA # LATIN SMALL LETTER U WITH ACUTE +0xFB 0x00FB # LATIN SMALL LETTER U WITH CIRCUMFLEX +0xFC 0x00FC # LATIN SMALL LETTER U WITH DIAERESIS +0xFD 0x00FD # LATIN SMALL LETTER Y WITH ACUTE +0xFE 0x00FE # LATIN SMALL LETTER THORN +0xFF 0x00FF # LATIN SMALL LETTER Y WITH DIAERESIS + diff --git a/codepage/iso8859-16.txt b/codepage/iso8859-16.txt new file mode 100644 index 0000000..c0dcf0d --- /dev/null +++ b/codepage/iso8859-16.txt @@ -0,0 +1,299 @@ +# +# Name: ISO/IEC 8859-16:2001 to Unicode +# Unicode version: 3.0 +# Table version: 1.0 +# Table format: Format A +# Date: 2001 July 26 +# Authors: Markus Kuhn +# +# Copyright (c) 1999-2001 Unicode, Inc. All Rights reserved. +# +# This file is provided as-is by Unicode, Inc. (The Unicode Consortium). +# No claims are made as to fitness for any particular purpose. No +# warranties of any kind are expressed or implied. The recipient +# agrees to determine applicability of information provided. If this +# file has been provided on optical media by Unicode, Inc., the sole +# remedy for any claim will be exchange of defective media within 90 +# days of receipt. +# +# Unicode, Inc. hereby grants the right to freely use the information +# supplied in this file in the creation of products supporting the +# Unicode Standard, and to make copies of this file in any form for +# internal or external distribution as long as this notice remains +# attached. +# +# General notes: +# +# This table contains the data the Unicode Consortium has on how +# ISO/IEC 8859-16:2001 characters map into Unicode. +# +# Format: Three tab-separated columns +# Column #1 is the ISO/IEC 8859-16 code (in hex as 0xXX) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 the Unicode name (follows a comment sign, '#') +# +# The entries are in ISO/IEC 8859-16 order. +# +# Updated versions of this file may be found in: +# +# +# Any comments or problems, contact +# Please note that is an archival address; +# notices will be checked, but do not expect an immediate response. +# +0x00 0x0000 # NULL +0x01 0x0001 # START OF HEADING +0x02 0x0002 # START OF TEXT +0x03 0x0003 # END OF TEXT +0x04 0x0004 # END OF TRANSMISSION +0x05 0x0005 # ENQUIRY +0x06 0x0006 # ACKNOWLEDGE +0x07 0x0007 # BELL +0x08 0x0008 # BACKSPACE +0x09 0x0009 # HORIZONTAL TABULATION +0x0A 0x000A # LINE FEED +0x0B 0x000B # VERTICAL TABULATION +0x0C 0x000C # FORM FEED +0x0D 0x000D # CARRIAGE RETURN +0x0E 0x000E # SHIFT OUT +0x0F 0x000F # SHIFT IN +0x10 0x0010 # DATA LINK ESCAPE +0x11 0x0011 # DEVICE CONTROL ONE +0x12 0x0012 # DEVICE CONTROL TWO +0x13 0x0013 # DEVICE CONTROL THREE +0x14 0x0014 # DEVICE CONTROL FOUR +0x15 0x0015 # NEGATIVE ACKNOWLEDGE +0x16 0x0016 # SYNCHRONOUS IDLE +0x17 0x0017 # END OF TRANSMISSION BLOCK +0x18 0x0018 # CANCEL +0x19 0x0019 # END OF MEDIUM +0x1A 0x001A # SUBSTITUTE +0x1B 0x001B # ESCAPE +0x1C 0x001C # FILE SEPARATOR +0x1D 0x001D # GROUP SEPARATOR +0x1E 0x001E # RECORD SEPARATOR +0x1F 0x001F # UNIT SEPARATOR +0x20 0x0020 # SPACE +0x21 0x0021 # EXCLAMATION MARK +0x22 0x0022 # QUOTATION MARK +0x23 0x0023 # NUMBER SIGN +0x24 0x0024 # DOLLAR SIGN +0x25 0x0025 # PERCENT SIGN +0x26 0x0026 # AMPERSAND +0x27 0x0027 # APOSTROPHE +0x28 0x0028 # LEFT PARENTHESIS +0x29 0x0029 # RIGHT PARENTHESIS +0x2A 0x002A # ASTERISK +0x2B 0x002B # PLUS SIGN +0x2C 0x002C # COMMA +0x2D 0x002D # HYPHEN-MINUS +0x2E 0x002E # FULL STOP +0x2F 0x002F # SOLIDUS +0x30 0x0030 # DIGIT ZERO +0x31 0x0031 # DIGIT ONE +0x32 0x0032 # DIGIT TWO +0x33 0x0033 # DIGIT THREE +0x34 0x0034 # DIGIT FOUR +0x35 0x0035 # DIGIT FIVE +0x36 0x0036 # DIGIT SIX +0x37 0x0037 # DIGIT SEVEN +0x38 0x0038 # DIGIT EIGHT +0x39 0x0039 # DIGIT NINE +0x3A 0x003A # COLON +0x3B 0x003B # SEMICOLON +0x3C 0x003C # LESS-THAN SIGN +0x3D 0x003D # EQUALS SIGN +0x3E 0x003E # GREATER-THAN SIGN +0x3F 0x003F # QUESTION MARK +0x40 0x0040 # COMMERCIAL AT +0x41 0x0041 # LATIN CAPITAL LETTER A +0x42 0x0042 # LATIN CAPITAL LETTER B +0x43 0x0043 # LATIN CAPITAL LETTER C +0x44 0x0044 # LATIN CAPITAL LETTER D +0x45 0x0045 # LATIN CAPITAL LETTER E +0x46 0x0046 # LATIN CAPITAL LETTER F +0x47 0x0047 # LATIN CAPITAL LETTER G +0x48 0x0048 # LATIN CAPITAL LETTER H +0x49 0x0049 # LATIN CAPITAL LETTER I +0x4A 0x004A # LATIN CAPITAL LETTER J +0x4B 0x004B # LATIN CAPITAL LETTER K +0x4C 0x004C # LATIN CAPITAL LETTER L +0x4D 0x004D # LATIN CAPITAL LETTER M +0x4E 0x004E # LATIN CAPITAL LETTER N +0x4F 0x004F # LATIN CAPITAL LETTER O +0x50 0x0050 # LATIN CAPITAL LETTER P +0x51 0x0051 # LATIN CAPITAL LETTER Q +0x52 0x0052 # LATIN CAPITAL LETTER R +0x53 0x0053 # LATIN CAPITAL LETTER S +0x54 0x0054 # LATIN CAPITAL LETTER T +0x55 0x0055 # LATIN CAPITAL LETTER U +0x56 0x0056 # LATIN CAPITAL LETTER V +0x57 0x0057 # LATIN CAPITAL LETTER W +0x58 0x0058 # LATIN CAPITAL LETTER X +0x59 0x0059 # LATIN CAPITAL LETTER Y +0x5A 0x005A # LATIN CAPITAL LETTER Z +0x5B 0x005B # LEFT SQUARE BRACKET +0x5C 0x005C # REVERSE SOLIDUS +0x5D 0x005D # RIGHT SQUARE BRACKET +0x5E 0x005E # CIRCUMFLEX ACCENT +0x5F 0x005F # LOW LINE +0x60 0x0060 # GRAVE ACCENT +0x61 0x0061 # LATIN SMALL LETTER A +0x62 0x0062 # LATIN SMALL LETTER B +0x63 0x0063 # LATIN SMALL LETTER C +0x64 0x0064 # LATIN SMALL LETTER D +0x65 0x0065 # LATIN SMALL LETTER E +0x66 0x0066 # LATIN SMALL LETTER F +0x67 0x0067 # LATIN SMALL LETTER G +0x68 0x0068 # LATIN SMALL LETTER H +0x69 0x0069 # LATIN SMALL LETTER I +0x6A 0x006A # LATIN SMALL LETTER J +0x6B 0x006B # LATIN SMALL LETTER K +0x6C 0x006C # LATIN SMALL LETTER L +0x6D 0x006D # LATIN SMALL LETTER M +0x6E 0x006E # LATIN SMALL LETTER N +0x6F 0x006F # LATIN SMALL LETTER O +0x70 0x0070 # LATIN SMALL LETTER P +0x71 0x0071 # LATIN SMALL LETTER Q +0x72 0x0072 # LATIN SMALL LETTER R +0x73 0x0073 # LATIN SMALL LETTER S +0x74 0x0074 # LATIN SMALL LETTER T +0x75 0x0075 # LATIN SMALL LETTER U +0x76 0x0076 # LATIN SMALL LETTER V +0x77 0x0077 # LATIN SMALL LETTER W +0x78 0x0078 # LATIN SMALL LETTER X +0x79 0x0079 # LATIN SMALL LETTER Y +0x7A 0x007A # LATIN SMALL LETTER Z +0x7B 0x007B # LEFT CURLY BRACKET +0x7C 0x007C # VERTICAL LINE +0x7D 0x007D # RIGHT CURLY BRACKET +0x7E 0x007E # TILDE +0x7F 0x007F # DELETE +0x80 0x0080 # +0x81 0x0081 # +0x82 0x0082 # +0x83 0x0083 # +0x84 0x0084 # +0x85 0x0085 # +0x86 0x0086 # +0x87 0x0087 # +0x88 0x0088 # +0x89 0x0089 # +0x8A 0x008A # +0x8B 0x008B # +0x8C 0x008C # +0x8D 0x008D # +0x8E 0x008E # +0x8F 0x008F # +0x90 0x0090 # +0x91 0x0091 # +0x92 0x0092 # +0x93 0x0093 # +0x94 0x0094 # +0x95 0x0095 # +0x96 0x0096 # +0x97 0x0097 # +0x98 0x0098 # +0x99 0x0099 # +0x9A 0x009A # +0x9B 0x009B # +0x9C 0x009C # +0x9D 0x009D # +0x9E 0x009E # +0x9F 0x009F # +0xA0 0x00A0 # NO-BREAK SPACE +0xA1 0x0104 # LATIN CAPITAL LETTER A WITH OGONEK +0xA2 0x0105 # LATIN SMALL LETTER A WITH OGONEK +0xA3 0x0141 # LATIN CAPITAL LETTER L WITH STROKE +0xA4 0x20AC # EURO SIGN +0xA5 0x201E # DOUBLE LOW-9 QUOTATION MARK +0xA6 0x0160 # LATIN CAPITAL LETTER S WITH CARON +0xA7 0x00A7 # SECTION SIGN +0xA8 0x0161 # LATIN SMALL LETTER S WITH CARON +0xA9 0x00A9 # COPYRIGHT SIGN +0xAA 0x0218 # LATIN CAPITAL LETTER S WITH COMMA BELOW +0xAB 0x00AB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xAC 0x0179 # LATIN CAPITAL LETTER Z WITH ACUTE +0xAD 0x00AD # SOFT HYPHEN +0xAE 0x017A # LATIN SMALL LETTER Z WITH ACUTE +0xAF 0x017B # LATIN CAPITAL LETTER Z WITH DOT ABOVE +0xB0 0x00B0 # DEGREE SIGN +0xB1 0x00B1 # PLUS-MINUS SIGN +0xB2 0x010C # LATIN CAPITAL LETTER C WITH CARON +0xB3 0x0142 # LATIN SMALL LETTER L WITH STROKE +0xB4 0x017D # LATIN CAPITAL LETTER Z WITH CARON +0xB5 0x201D # RIGHT DOUBLE QUOTATION MARK +0xB6 0x00B6 # PILCROW SIGN +0xB7 0x00B7 # MIDDLE DOT +0xB8 0x017E # LATIN SMALL LETTER Z WITH CARON +0xB9 0x010D # LATIN SMALL LETTER C WITH CARON +0xBA 0x0219 # LATIN SMALL LETTER S WITH COMMA BELOW +0xBB 0x00BB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xBC 0x0152 # LATIN CAPITAL LIGATURE OE +0xBD 0x0153 # LATIN SMALL LIGATURE OE +0xBE 0x0178 # LATIN CAPITAL LETTER Y WITH DIAERESIS +0xBF 0x017C # LATIN SMALL LETTER Z WITH DOT ABOVE +0xC0 0x00C0 # LATIN CAPITAL LETTER A WITH GRAVE +0xC1 0x00C1 # LATIN CAPITAL LETTER A WITH ACUTE +0xC2 0x00C2 # LATIN CAPITAL LETTER A WITH CIRCUMFLEX +0xC3 0x0102 # LATIN CAPITAL LETTER A WITH BREVE +0xC4 0x00C4 # LATIN CAPITAL LETTER A WITH DIAERESIS +0xC5 0x0106 # LATIN CAPITAL LETTER C WITH ACUTE +0xC6 0x00C6 # LATIN CAPITAL LETTER AE +0xC7 0x00C7 # LATIN CAPITAL LETTER C WITH CEDILLA +0xC8 0x00C8 # LATIN CAPITAL LETTER E WITH GRAVE +0xC9 0x00C9 # LATIN CAPITAL LETTER E WITH ACUTE +0xCA 0x00CA # LATIN CAPITAL LETTER E WITH CIRCUMFLEX +0xCB 0x00CB # LATIN CAPITAL LETTER E WITH DIAERESIS +0xCC 0x00CC # LATIN CAPITAL LETTER I WITH GRAVE +0xCD 0x00CD # LATIN CAPITAL LETTER I WITH ACUTE +0xCE 0x00CE # LATIN CAPITAL LETTER I WITH CIRCUMFLEX +0xCF 0x00CF # LATIN CAPITAL LETTER I WITH DIAERESIS +0xD0 0x0110 # LATIN CAPITAL LETTER D WITH STROKE +0xD1 0x0143 # LATIN CAPITAL LETTER N WITH ACUTE +0xD2 0x00D2 # LATIN CAPITAL LETTER O WITH GRAVE +0xD3 0x00D3 # LATIN CAPITAL LETTER O WITH ACUTE +0xD4 0x00D4 # LATIN CAPITAL LETTER O WITH CIRCUMFLEX +0xD5 0x0150 # LATIN CAPITAL LETTER O WITH DOUBLE ACUTE +0xD6 0x00D6 # LATIN CAPITAL LETTER O WITH DIAERESIS +0xD7 0x015A # LATIN CAPITAL LETTER S WITH ACUTE +0xD8 0x0170 # LATIN CAPITAL LETTER U WITH DOUBLE ACUTE +0xD9 0x00D9 # LATIN CAPITAL LETTER U WITH GRAVE +0xDA 0x00DA # LATIN CAPITAL LETTER U WITH ACUTE +0xDB 0x00DB # LATIN CAPITAL LETTER U WITH CIRCUMFLEX +0xDC 0x00DC # LATIN CAPITAL LETTER U WITH DIAERESIS +0xDD 0x0118 # LATIN CAPITAL LETTER E WITH OGONEK +0xDE 0x021A # LATIN CAPITAL LETTER T WITH COMMA BELOW +0xDF 0x00DF # LATIN SMALL LETTER SHARP S +0xE0 0x00E0 # LATIN SMALL LETTER A WITH GRAVE +0xE1 0x00E1 # LATIN SMALL LETTER A WITH ACUTE +0xE2 0x00E2 # LATIN SMALL LETTER A WITH CIRCUMFLEX +0xE3 0x0103 # LATIN SMALL LETTER A WITH BREVE +0xE4 0x00E4 # LATIN SMALL LETTER A WITH DIAERESIS +0xE5 0x0107 # LATIN SMALL LETTER C WITH ACUTE +0xE6 0x00E6 # LATIN SMALL LETTER AE +0xE7 0x00E7 # LATIN SMALL LETTER C WITH CEDILLA +0xE8 0x00E8 # LATIN SMALL LETTER E WITH GRAVE +0xE9 0x00E9 # LATIN SMALL LETTER E WITH ACUTE +0xEA 0x00EA # LATIN SMALL LETTER E WITH CIRCUMFLEX +0xEB 0x00EB # LATIN SMALL LETTER E WITH DIAERESIS +0xEC 0x00EC # LATIN SMALL LETTER I WITH GRAVE +0xED 0x00ED # LATIN SMALL LETTER I WITH ACUTE +0xEE 0x00EE # LATIN SMALL LETTER I WITH CIRCUMFLEX +0xEF 0x00EF # LATIN SMALL LETTER I WITH DIAERESIS +0xF0 0x0111 # LATIN SMALL LETTER D WITH STROKE +0xF1 0x0144 # LATIN SMALL LETTER N WITH ACUTE +0xF2 0x00F2 # LATIN SMALL LETTER O WITH GRAVE +0xF3 0x00F3 # LATIN SMALL LETTER O WITH ACUTE +0xF4 0x00F4 # LATIN SMALL LETTER O WITH CIRCUMFLEX +0xF5 0x0151 # LATIN SMALL LETTER O WITH DOUBLE ACUTE +0xF6 0x00F6 # LATIN SMALL LETTER O WITH DIAERESIS +0xF7 0x015B # LATIN SMALL LETTER S WITH ACUTE +0xF8 0x0171 # LATIN SMALL LETTER U WITH DOUBLE ACUTE +0xF9 0x00F9 # LATIN SMALL LETTER U WITH GRAVE +0xFA 0x00FA # LATIN SMALL LETTER U WITH ACUTE +0xFB 0x00FB # LATIN SMALL LETTER U WITH CIRCUMFLEX +0xFC 0x00FC # LATIN SMALL LETTER U WITH DIAERESIS +0xFD 0x0119 # LATIN SMALL LETTER E WITH OGONEK +0xFE 0x021B # LATIN SMALL LETTER T WITH COMMA BELOW +0xFF 0x00FF # LATIN SMALL LETTER Y WITH DIAERESIS diff --git a/codepage/iso8859-2.txt b/codepage/iso8859-2.txt new file mode 100644 index 0000000..e45df25 --- /dev/null +++ b/codepage/iso8859-2.txt @@ -0,0 +1,303 @@ +# +# Name: ISO 8859-2:1999 to Unicode +# Unicode version: 3.0 +# Table version: 1.0 +# Table format: Format A +# Date: 1999 July 27 +# Authors: Ken Whistler +# +# Copyright (c) 1991-1999 Unicode, Inc. All Rights reserved. +# +# This file is provided as-is by Unicode, Inc. (The Unicode Consortium). +# No claims are made as to fitness for any particular purpose. No +# warranties of any kind are expressed or implied. The recipient +# agrees to determine applicability of information provided. If this +# file has been provided on optical media by Unicode, Inc., the sole +# remedy for any claim will be exchange of defective media within 90 +# days of receipt. +# +# Unicode, Inc. hereby grants the right to freely use the information +# supplied in this file in the creation of products supporting the +# Unicode Standard, and to make copies of this file in any form for +# internal or external distribution as long as this notice remains +# attached. +# +# General notes: +# +# This table contains the data the Unicode Consortium has on how +# ISO/IEC 8859-2:1999 characters map into Unicode. +# +# Format: Three tab-separated columns +# Column #1 is the ISO/IEC 8859-2 code (in hex as 0xXX) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 the Unicode name (follows a comment sign, '#') +# +# The entries are in ISO/IEC 8859-2 order. +# +# Version history +# 1.0 version updates 0.1 version by adding mappings for all +# control characters. +# +# Updated versions of this file may be found in: +# +# +# Any comments or problems, contact +# Please note that is an archival address; +# notices will be checked, but do not expect an immediate response. +# +0x00 0x0000 # NULL +0x01 0x0001 # START OF HEADING +0x02 0x0002 # START OF TEXT +0x03 0x0003 # END OF TEXT +0x04 0x0004 # END OF TRANSMISSION +0x05 0x0005 # ENQUIRY +0x06 0x0006 # ACKNOWLEDGE +0x07 0x0007 # BELL +0x08 0x0008 # BACKSPACE +0x09 0x0009 # HORIZONTAL TABULATION +0x0A 0x000A # LINE FEED +0x0B 0x000B # VERTICAL TABULATION +0x0C 0x000C # FORM FEED +0x0D 0x000D # CARRIAGE RETURN +0x0E 0x000E # SHIFT OUT +0x0F 0x000F # SHIFT IN +0x10 0x0010 # DATA LINK ESCAPE +0x11 0x0011 # DEVICE CONTROL ONE +0x12 0x0012 # DEVICE CONTROL TWO +0x13 0x0013 # DEVICE CONTROL THREE +0x14 0x0014 # DEVICE CONTROL FOUR +0x15 0x0015 # NEGATIVE ACKNOWLEDGE +0x16 0x0016 # SYNCHRONOUS IDLE +0x17 0x0017 # END OF TRANSMISSION BLOCK +0x18 0x0018 # CANCEL +0x19 0x0019 # END OF MEDIUM +0x1A 0x001A # SUBSTITUTE +0x1B 0x001B # ESCAPE +0x1C 0x001C # FILE SEPARATOR +0x1D 0x001D # GROUP SEPARATOR +0x1E 0x001E # RECORD SEPARATOR +0x1F 0x001F # UNIT SEPARATOR +0x20 0x0020 # SPACE +0x21 0x0021 # EXCLAMATION MARK +0x22 0x0022 # QUOTATION MARK +0x23 0x0023 # NUMBER SIGN +0x24 0x0024 # DOLLAR SIGN +0x25 0x0025 # PERCENT SIGN +0x26 0x0026 # AMPERSAND +0x27 0x0027 # APOSTROPHE +0x28 0x0028 # LEFT PARENTHESIS +0x29 0x0029 # RIGHT PARENTHESIS +0x2A 0x002A # ASTERISK +0x2B 0x002B # PLUS SIGN +0x2C 0x002C # COMMA +0x2D 0x002D # HYPHEN-MINUS +0x2E 0x002E # FULL STOP +0x2F 0x002F # SOLIDUS +0x30 0x0030 # DIGIT ZERO +0x31 0x0031 # DIGIT ONE +0x32 0x0032 # DIGIT TWO +0x33 0x0033 # DIGIT THREE +0x34 0x0034 # DIGIT FOUR +0x35 0x0035 # DIGIT FIVE +0x36 0x0036 # DIGIT SIX +0x37 0x0037 # DIGIT SEVEN +0x38 0x0038 # DIGIT EIGHT +0x39 0x0039 # DIGIT NINE +0x3A 0x003A # COLON +0x3B 0x003B # SEMICOLON +0x3C 0x003C # LESS-THAN SIGN +0x3D 0x003D # EQUALS SIGN +0x3E 0x003E # GREATER-THAN SIGN +0x3F 0x003F # QUESTION MARK +0x40 0x0040 # COMMERCIAL AT +0x41 0x0041 # LATIN CAPITAL LETTER A +0x42 0x0042 # LATIN CAPITAL LETTER B +0x43 0x0043 # LATIN CAPITAL LETTER C +0x44 0x0044 # LATIN CAPITAL LETTER D +0x45 0x0045 # LATIN CAPITAL LETTER E +0x46 0x0046 # LATIN CAPITAL LETTER F +0x47 0x0047 # LATIN CAPITAL LETTER G +0x48 0x0048 # LATIN CAPITAL LETTER H +0x49 0x0049 # LATIN CAPITAL LETTER I +0x4A 0x004A # LATIN CAPITAL LETTER J +0x4B 0x004B # LATIN CAPITAL LETTER K +0x4C 0x004C # LATIN CAPITAL LETTER L +0x4D 0x004D # LATIN CAPITAL LETTER M +0x4E 0x004E # LATIN CAPITAL LETTER N +0x4F 0x004F # LATIN CAPITAL LETTER O +0x50 0x0050 # LATIN CAPITAL LETTER P +0x51 0x0051 # LATIN CAPITAL LETTER Q +0x52 0x0052 # LATIN CAPITAL LETTER R +0x53 0x0053 # LATIN CAPITAL LETTER S +0x54 0x0054 # LATIN CAPITAL LETTER T +0x55 0x0055 # LATIN CAPITAL LETTER U +0x56 0x0056 # LATIN CAPITAL LETTER V +0x57 0x0057 # LATIN CAPITAL LETTER W +0x58 0x0058 # LATIN CAPITAL LETTER X +0x59 0x0059 # LATIN CAPITAL LETTER Y +0x5A 0x005A # LATIN CAPITAL LETTER Z +0x5B 0x005B # LEFT SQUARE BRACKET +0x5C 0x005C # REVERSE SOLIDUS +0x5D 0x005D # RIGHT SQUARE BRACKET +0x5E 0x005E # CIRCUMFLEX ACCENT +0x5F 0x005F # LOW LINE +0x60 0x0060 # GRAVE ACCENT +0x61 0x0061 # LATIN SMALL LETTER A +0x62 0x0062 # LATIN SMALL LETTER B +0x63 0x0063 # LATIN SMALL LETTER C +0x64 0x0064 # LATIN SMALL LETTER D +0x65 0x0065 # LATIN SMALL LETTER E +0x66 0x0066 # LATIN SMALL LETTER F +0x67 0x0067 # LATIN SMALL LETTER G +0x68 0x0068 # LATIN SMALL LETTER H +0x69 0x0069 # LATIN SMALL LETTER I +0x6A 0x006A # LATIN SMALL LETTER J +0x6B 0x006B # LATIN SMALL LETTER K +0x6C 0x006C # LATIN SMALL LETTER L +0x6D 0x006D # LATIN SMALL LETTER M +0x6E 0x006E # LATIN SMALL LETTER N +0x6F 0x006F # LATIN SMALL LETTER O +0x70 0x0070 # LATIN SMALL LETTER P +0x71 0x0071 # LATIN SMALL LETTER Q +0x72 0x0072 # LATIN SMALL LETTER R +0x73 0x0073 # LATIN SMALL LETTER S +0x74 0x0074 # LATIN SMALL LETTER T +0x75 0x0075 # LATIN SMALL LETTER U +0x76 0x0076 # LATIN SMALL LETTER V +0x77 0x0077 # LATIN SMALL LETTER W +0x78 0x0078 # LATIN SMALL LETTER X +0x79 0x0079 # LATIN SMALL LETTER Y +0x7A 0x007A # LATIN SMALL LETTER Z +0x7B 0x007B # LEFT CURLY BRACKET +0x7C 0x007C # VERTICAL LINE +0x7D 0x007D # RIGHT CURLY BRACKET +0x7E 0x007E # TILDE +0x7F 0x007F # DELETE +0x80 0x0080 # +0x81 0x0081 # +0x82 0x0082 # +0x83 0x0083 # +0x84 0x0084 # +0x85 0x0085 # +0x86 0x0086 # +0x87 0x0087 # +0x88 0x0088 # +0x89 0x0089 # +0x8A 0x008A # +0x8B 0x008B # +0x8C 0x008C # +0x8D 0x008D # +0x8E 0x008E # +0x8F 0x008F # +0x90 0x0090 # +0x91 0x0091 # +0x92 0x0092 # +0x93 0x0093 # +0x94 0x0094 # +0x95 0x0095 # +0x96 0x0096 # +0x97 0x0097 # +0x98 0x0098 # +0x99 0x0099 # +0x9A 0x009A # +0x9B 0x009B # +0x9C 0x009C # +0x9D 0x009D # +0x9E 0x009E # +0x9F 0x009F # +0xA0 0x00A0 # NO-BREAK SPACE +0xA1 0x0104 # LATIN CAPITAL LETTER A WITH OGONEK +0xA2 0x02D8 # BREVE +0xA3 0x0141 # LATIN CAPITAL LETTER L WITH STROKE +0xA4 0x00A4 # CURRENCY SIGN +0xA5 0x013D # LATIN CAPITAL LETTER L WITH CARON +0xA6 0x015A # LATIN CAPITAL LETTER S WITH ACUTE +0xA7 0x00A7 # SECTION SIGN +0xA8 0x00A8 # DIAERESIS +0xA9 0x0160 # LATIN CAPITAL LETTER S WITH CARON +0xAA 0x015E # LATIN CAPITAL LETTER S WITH CEDILLA +0xAB 0x0164 # LATIN CAPITAL LETTER T WITH CARON +0xAC 0x0179 # LATIN CAPITAL LETTER Z WITH ACUTE +0xAD 0x00AD # SOFT HYPHEN +0xAE 0x017D # LATIN CAPITAL LETTER Z WITH CARON +0xAF 0x017B # LATIN CAPITAL LETTER Z WITH DOT ABOVE +0xB0 0x00B0 # DEGREE SIGN +0xB1 0x0105 # LATIN SMALL LETTER A WITH OGONEK +0xB2 0x02DB # OGONEK +0xB3 0x0142 # LATIN SMALL LETTER L WITH STROKE +0xB4 0x00B4 # ACUTE ACCENT +0xB5 0x013E # LATIN SMALL LETTER L WITH CARON +0xB6 0x015B # LATIN SMALL LETTER S WITH ACUTE +0xB7 0x02C7 # CARON +0xB8 0x00B8 # CEDILLA +0xB9 0x0161 # LATIN SMALL LETTER S WITH CARON +0xBA 0x015F # LATIN SMALL LETTER S WITH CEDILLA +0xBB 0x0165 # LATIN SMALL LETTER T WITH CARON +0xBC 0x017A # LATIN SMALL LETTER Z WITH ACUTE +0xBD 0x02DD # DOUBLE ACUTE ACCENT +0xBE 0x017E # LATIN SMALL LETTER Z WITH CARON +0xBF 0x017C # LATIN SMALL LETTER Z WITH DOT ABOVE +0xC0 0x0154 # LATIN CAPITAL LETTER R WITH ACUTE +0xC1 0x00C1 # LATIN CAPITAL LETTER A WITH ACUTE +0xC2 0x00C2 # LATIN CAPITAL LETTER A WITH CIRCUMFLEX +0xC3 0x0102 # LATIN CAPITAL LETTER A WITH BREVE +0xC4 0x00C4 # LATIN CAPITAL LETTER A WITH DIAERESIS +0xC5 0x0139 # LATIN CAPITAL LETTER L WITH ACUTE +0xC6 0x0106 # LATIN CAPITAL LETTER C WITH ACUTE +0xC7 0x00C7 # LATIN CAPITAL LETTER C WITH CEDILLA +0xC8 0x010C # LATIN CAPITAL LETTER C WITH CARON +0xC9 0x00C9 # LATIN CAPITAL LETTER E WITH ACUTE +0xCA 0x0118 # LATIN CAPITAL LETTER E WITH OGONEK +0xCB 0x00CB # LATIN CAPITAL LETTER E WITH DIAERESIS +0xCC 0x011A # LATIN CAPITAL LETTER E WITH CARON +0xCD 0x00CD # LATIN CAPITAL LETTER I WITH ACUTE +0xCE 0x00CE # LATIN CAPITAL LETTER I WITH CIRCUMFLEX +0xCF 0x010E # LATIN CAPITAL LETTER D WITH CARON +0xD0 0x0110 # LATIN CAPITAL LETTER D WITH STROKE +0xD1 0x0143 # LATIN CAPITAL LETTER N WITH ACUTE +0xD2 0x0147 # LATIN CAPITAL LETTER N WITH CARON +0xD3 0x00D3 # LATIN CAPITAL LETTER O WITH ACUTE +0xD4 0x00D4 # LATIN CAPITAL LETTER O WITH CIRCUMFLEX +0xD5 0x0150 # LATIN CAPITAL LETTER O WITH DOUBLE ACUTE +0xD6 0x00D6 # LATIN CAPITAL LETTER O WITH DIAERESIS +0xD7 0x00D7 # MULTIPLICATION SIGN +0xD8 0x0158 # LATIN CAPITAL LETTER R WITH CARON +0xD9 0x016E # LATIN CAPITAL LETTER U WITH RING ABOVE +0xDA 0x00DA # LATIN CAPITAL LETTER U WITH ACUTE +0xDB 0x0170 # LATIN CAPITAL LETTER U WITH DOUBLE ACUTE +0xDC 0x00DC # LATIN CAPITAL LETTER U WITH DIAERESIS +0xDD 0x00DD # LATIN CAPITAL LETTER Y WITH ACUTE +0xDE 0x0162 # LATIN CAPITAL LETTER T WITH CEDILLA +0xDF 0x00DF # LATIN SMALL LETTER SHARP S +0xE0 0x0155 # LATIN SMALL LETTER R WITH ACUTE +0xE1 0x00E1 # LATIN SMALL LETTER A WITH ACUTE +0xE2 0x00E2 # LATIN SMALL LETTER A WITH CIRCUMFLEX +0xE3 0x0103 # LATIN SMALL LETTER A WITH BREVE +0xE4 0x00E4 # LATIN SMALL LETTER A WITH DIAERESIS +0xE5 0x013A # LATIN SMALL LETTER L WITH ACUTE +0xE6 0x0107 # LATIN SMALL LETTER C WITH ACUTE +0xE7 0x00E7 # LATIN SMALL LETTER C WITH CEDILLA +0xE8 0x010D # LATIN SMALL LETTER C WITH CARON +0xE9 0x00E9 # LATIN SMALL LETTER E WITH ACUTE +0xEA 0x0119 # LATIN SMALL LETTER E WITH OGONEK +0xEB 0x00EB # LATIN SMALL LETTER E WITH DIAERESIS +0xEC 0x011B # LATIN SMALL LETTER E WITH CARON +0xED 0x00ED # LATIN SMALL LETTER I WITH ACUTE +0xEE 0x00EE # LATIN SMALL LETTER I WITH CIRCUMFLEX +0xEF 0x010F # LATIN SMALL LETTER D WITH CARON +0xF0 0x0111 # LATIN SMALL LETTER D WITH STROKE +0xF1 0x0144 # LATIN SMALL LETTER N WITH ACUTE +0xF2 0x0148 # LATIN SMALL LETTER N WITH CARON +0xF3 0x00F3 # LATIN SMALL LETTER O WITH ACUTE +0xF4 0x00F4 # LATIN SMALL LETTER O WITH CIRCUMFLEX +0xF5 0x0151 # LATIN SMALL LETTER O WITH DOUBLE ACUTE +0xF6 0x00F6 # LATIN SMALL LETTER O WITH DIAERESIS +0xF7 0x00F7 # DIVISION SIGN +0xF8 0x0159 # LATIN SMALL LETTER R WITH CARON +0xF9 0x016F # LATIN SMALL LETTER U WITH RING ABOVE +0xFA 0x00FA # LATIN SMALL LETTER U WITH ACUTE +0xFB 0x0171 # LATIN SMALL LETTER U WITH DOUBLE ACUTE +0xFC 0x00FC # LATIN SMALL LETTER U WITH DIAERESIS +0xFD 0x00FD # LATIN SMALL LETTER Y WITH ACUTE +0xFE 0x0163 # LATIN SMALL LETTER T WITH CEDILLA +0xFF 0x02D9 # DOT ABOVE diff --git a/codepage/iso8859-3.txt b/codepage/iso8859-3.txt new file mode 100644 index 0000000..9b6ac69 --- /dev/null +++ b/codepage/iso8859-3.txt @@ -0,0 +1,296 @@ +# +# Name: ISO/IEC 8859-3:1999 to Unicode +# Unicode version: 3.0 +# Table version: 1.0 +# Table format: Format A +# Date: 1999 July 27 +# Authors: Ken Whistler +# +# Copyright (c) 1991-1999 Unicode, Inc. All Rights reserved. +# +# This file is provided as-is by Unicode, Inc. (The Unicode Consortium). +# No claims are made as to fitness for any particular purpose. No +# warranties of any kind are expressed or implied. The recipient +# agrees to determine applicability of information provided. If this +# file has been provided on optical media by Unicode, Inc., the sole +# remedy for any claim will be exchange of defective media within 90 +# days of receipt. +# +# Unicode, Inc. hereby grants the right to freely use the information +# supplied in this file in the creation of products supporting the +# Unicode Standard, and to make copies of this file in any form for +# internal or external distribution as long as this notice remains +# attached. +# +# General notes: +# +# This table contains the data the Unicode Consortium has on how +# ISO/IEC 8859-3:1999 characters map into Unicode. +# +# Format: Three tab-separated columns +# Column #1 is the ISO/IEC 8859-3 code (in hex as 0xXX) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 the Unicode name (follows a comment sign, '#') +# +# The entries are in ISO/IEC 8859-3 order. +# +# Version history +# 1.0 version updates 0.1 version by adding mappings for all +# control characters. +# +# Updated versions of this file may be found in: +# +# +# Any comments or problems, contact +# Please note that is an archival address; +# notices will be checked, but do not expect an immediate response. +# +0x00 0x0000 # NULL +0x01 0x0001 # START OF HEADING +0x02 0x0002 # START OF TEXT +0x03 0x0003 # END OF TEXT +0x04 0x0004 # END OF TRANSMISSION +0x05 0x0005 # ENQUIRY +0x06 0x0006 # ACKNOWLEDGE +0x07 0x0007 # BELL +0x08 0x0008 # BACKSPACE +0x09 0x0009 # HORIZONTAL TABULATION +0x0A 0x000A # LINE FEED +0x0B 0x000B # VERTICAL TABULATION +0x0C 0x000C # FORM FEED +0x0D 0x000D # CARRIAGE RETURN +0x0E 0x000E # SHIFT OUT +0x0F 0x000F # SHIFT IN +0x10 0x0010 # DATA LINK ESCAPE +0x11 0x0011 # DEVICE CONTROL ONE +0x12 0x0012 # DEVICE CONTROL TWO +0x13 0x0013 # DEVICE CONTROL THREE +0x14 0x0014 # DEVICE CONTROL FOUR +0x15 0x0015 # NEGATIVE ACKNOWLEDGE +0x16 0x0016 # SYNCHRONOUS IDLE +0x17 0x0017 # END OF TRANSMISSION BLOCK +0x18 0x0018 # CANCEL +0x19 0x0019 # END OF MEDIUM +0x1A 0x001A # SUBSTITUTE +0x1B 0x001B # ESCAPE +0x1C 0x001C # FILE SEPARATOR +0x1D 0x001D # GROUP SEPARATOR +0x1E 0x001E # RECORD SEPARATOR +0x1F 0x001F # UNIT SEPARATOR +0x20 0x0020 # SPACE +0x21 0x0021 # EXCLAMATION MARK +0x22 0x0022 # QUOTATION MARK +0x23 0x0023 # NUMBER SIGN +0x24 0x0024 # DOLLAR SIGN +0x25 0x0025 # PERCENT SIGN +0x26 0x0026 # AMPERSAND +0x27 0x0027 # APOSTROPHE +0x28 0x0028 # LEFT PARENTHESIS +0x29 0x0029 # RIGHT PARENTHESIS +0x2A 0x002A # ASTERISK +0x2B 0x002B # PLUS SIGN +0x2C 0x002C # COMMA +0x2D 0x002D # HYPHEN-MINUS +0x2E 0x002E # FULL STOP +0x2F 0x002F # SOLIDUS +0x30 0x0030 # DIGIT ZERO +0x31 0x0031 # DIGIT ONE +0x32 0x0032 # DIGIT TWO +0x33 0x0033 # DIGIT THREE +0x34 0x0034 # DIGIT FOUR +0x35 0x0035 # DIGIT FIVE +0x36 0x0036 # DIGIT SIX +0x37 0x0037 # DIGIT SEVEN +0x38 0x0038 # DIGIT EIGHT +0x39 0x0039 # DIGIT NINE +0x3A 0x003A # COLON +0x3B 0x003B # SEMICOLON +0x3C 0x003C # LESS-THAN SIGN +0x3D 0x003D # EQUALS SIGN +0x3E 0x003E # GREATER-THAN SIGN +0x3F 0x003F # QUESTION MARK +0x40 0x0040 # COMMERCIAL AT +0x41 0x0041 # LATIN CAPITAL LETTER A +0x42 0x0042 # LATIN CAPITAL LETTER B +0x43 0x0043 # LATIN CAPITAL LETTER C +0x44 0x0044 # LATIN CAPITAL LETTER D +0x45 0x0045 # LATIN CAPITAL LETTER E +0x46 0x0046 # LATIN CAPITAL LETTER F +0x47 0x0047 # LATIN CAPITAL LETTER G +0x48 0x0048 # LATIN CAPITAL LETTER H +0x49 0x0049 # LATIN CAPITAL LETTER I +0x4A 0x004A # LATIN CAPITAL LETTER J +0x4B 0x004B # LATIN CAPITAL LETTER K +0x4C 0x004C # LATIN CAPITAL LETTER L +0x4D 0x004D # LATIN CAPITAL LETTER M +0x4E 0x004E # LATIN CAPITAL LETTER N +0x4F 0x004F # LATIN CAPITAL LETTER O +0x50 0x0050 # LATIN CAPITAL LETTER P +0x51 0x0051 # LATIN CAPITAL LETTER Q +0x52 0x0052 # LATIN CAPITAL LETTER R +0x53 0x0053 # LATIN CAPITAL LETTER S +0x54 0x0054 # LATIN CAPITAL LETTER T +0x55 0x0055 # LATIN CAPITAL LETTER U +0x56 0x0056 # LATIN CAPITAL LETTER V +0x57 0x0057 # LATIN CAPITAL LETTER W +0x58 0x0058 # LATIN CAPITAL LETTER X +0x59 0x0059 # LATIN CAPITAL LETTER Y +0x5A 0x005A # LATIN CAPITAL LETTER Z +0x5B 0x005B # LEFT SQUARE BRACKET +0x5C 0x005C # REVERSE SOLIDUS +0x5D 0x005D # RIGHT SQUARE BRACKET +0x5E 0x005E # CIRCUMFLEX ACCENT +0x5F 0x005F # LOW LINE +0x60 0x0060 # GRAVE ACCENT +0x61 0x0061 # LATIN SMALL LETTER A +0x62 0x0062 # LATIN SMALL LETTER B +0x63 0x0063 # LATIN SMALL LETTER C +0x64 0x0064 # LATIN SMALL LETTER D +0x65 0x0065 # LATIN SMALL LETTER E +0x66 0x0066 # LATIN SMALL LETTER F +0x67 0x0067 # LATIN SMALL LETTER G +0x68 0x0068 # LATIN SMALL LETTER H +0x69 0x0069 # LATIN SMALL LETTER I +0x6A 0x006A # LATIN SMALL LETTER J +0x6B 0x006B # LATIN SMALL LETTER K +0x6C 0x006C # LATIN SMALL LETTER L +0x6D 0x006D # LATIN SMALL LETTER M +0x6E 0x006E # LATIN SMALL LETTER N +0x6F 0x006F # LATIN SMALL LETTER O +0x70 0x0070 # LATIN SMALL LETTER P +0x71 0x0071 # LATIN SMALL LETTER Q +0x72 0x0072 # LATIN SMALL LETTER R +0x73 0x0073 # LATIN SMALL LETTER S +0x74 0x0074 # LATIN SMALL LETTER T +0x75 0x0075 # LATIN SMALL LETTER U +0x76 0x0076 # LATIN SMALL LETTER V +0x77 0x0077 # LATIN SMALL LETTER W +0x78 0x0078 # LATIN SMALL LETTER X +0x79 0x0079 # LATIN SMALL LETTER Y +0x7A 0x007A # LATIN SMALL LETTER Z +0x7B 0x007B # LEFT CURLY BRACKET +0x7C 0x007C # VERTICAL LINE +0x7D 0x007D # RIGHT CURLY BRACKET +0x7E 0x007E # TILDE +0x7F 0x007F # DELETE +0x80 0x0080 # +0x81 0x0081 # +0x82 0x0082 # +0x83 0x0083 # +0x84 0x0084 # +0x85 0x0085 # +0x86 0x0086 # +0x87 0x0087 # +0x88 0x0088 # +0x89 0x0089 # +0x8A 0x008A # +0x8B 0x008B # +0x8C 0x008C # +0x8D 0x008D # +0x8E 0x008E # +0x8F 0x008F # +0x90 0x0090 # +0x91 0x0091 # +0x92 0x0092 # +0x93 0x0093 # +0x94 0x0094 # +0x95 0x0095 # +0x96 0x0096 # +0x97 0x0097 # +0x98 0x0098 # +0x99 0x0099 # +0x9A 0x009A # +0x9B 0x009B # +0x9C 0x009C # +0x9D 0x009D # +0x9E 0x009E # +0x9F 0x009F # +0xA0 0x00A0 # NO-BREAK SPACE +0xA1 0x0126 # LATIN CAPITAL LETTER H WITH STROKE +0xA2 0x02D8 # BREVE +0xA3 0x00A3 # POUND SIGN +0xA4 0x00A4 # CURRENCY SIGN +0xA6 0x0124 # LATIN CAPITAL LETTER H WITH CIRCUMFLEX +0xA7 0x00A7 # SECTION SIGN +0xA8 0x00A8 # DIAERESIS +0xA9 0x0130 # LATIN CAPITAL LETTER I WITH DOT ABOVE +0xAA 0x015E # LATIN CAPITAL LETTER S WITH CEDILLA +0xAB 0x011E # LATIN CAPITAL LETTER G WITH BREVE +0xAC 0x0134 # LATIN CAPITAL LETTER J WITH CIRCUMFLEX +0xAD 0x00AD # SOFT HYPHEN +0xAF 0x017B # LATIN CAPITAL LETTER Z WITH DOT ABOVE +0xB0 0x00B0 # DEGREE SIGN +0xB1 0x0127 # LATIN SMALL LETTER H WITH STROKE +0xB2 0x00B2 # SUPERSCRIPT TWO +0xB3 0x00B3 # SUPERSCRIPT THREE +0xB4 0x00B4 # ACUTE ACCENT +0xB5 0x00B5 # MICRO SIGN +0xB6 0x0125 # LATIN SMALL LETTER H WITH CIRCUMFLEX +0xB7 0x00B7 # MIDDLE DOT +0xB8 0x00B8 # CEDILLA +0xB9 0x0131 # LATIN SMALL LETTER DOTLESS I +0xBA 0x015F # LATIN SMALL LETTER S WITH CEDILLA +0xBB 0x011F # LATIN SMALL LETTER G WITH BREVE +0xBC 0x0135 # LATIN SMALL LETTER J WITH CIRCUMFLEX +0xBD 0x00BD # VULGAR FRACTION ONE HALF +0xBF 0x017C # LATIN SMALL LETTER Z WITH DOT ABOVE +0xC0 0x00C0 # LATIN CAPITAL LETTER A WITH GRAVE +0xC1 0x00C1 # LATIN CAPITAL LETTER A WITH ACUTE +0xC2 0x00C2 # LATIN CAPITAL LETTER A WITH CIRCUMFLEX +0xC4 0x00C4 # LATIN CAPITAL LETTER A WITH DIAERESIS +0xC5 0x010A # LATIN CAPITAL LETTER C WITH DOT ABOVE +0xC6 0x0108 # LATIN CAPITAL LETTER C WITH CIRCUMFLEX +0xC7 0x00C7 # LATIN CAPITAL LETTER C WITH CEDILLA +0xC8 0x00C8 # LATIN CAPITAL LETTER E WITH GRAVE +0xC9 0x00C9 # LATIN CAPITAL LETTER E WITH ACUTE +0xCA 0x00CA # LATIN CAPITAL LETTER E WITH CIRCUMFLEX +0xCB 0x00CB # LATIN CAPITAL LETTER E WITH DIAERESIS +0xCC 0x00CC # LATIN CAPITAL LETTER I WITH GRAVE +0xCD 0x00CD # LATIN CAPITAL LETTER I WITH ACUTE +0xCE 0x00CE # LATIN CAPITAL LETTER I WITH CIRCUMFLEX +0xCF 0x00CF # LATIN CAPITAL LETTER I WITH DIAERESIS +0xD1 0x00D1 # LATIN CAPITAL LETTER N WITH TILDE +0xD2 0x00D2 # LATIN CAPITAL LETTER O WITH GRAVE +0xD3 0x00D3 # LATIN CAPITAL LETTER O WITH ACUTE +0xD4 0x00D4 # LATIN CAPITAL LETTER O WITH CIRCUMFLEX +0xD5 0x0120 # LATIN CAPITAL LETTER G WITH DOT ABOVE +0xD6 0x00D6 # LATIN CAPITAL LETTER O WITH DIAERESIS +0xD7 0x00D7 # MULTIPLICATION SIGN +0xD8 0x011C # LATIN CAPITAL LETTER G WITH CIRCUMFLEX +0xD9 0x00D9 # LATIN CAPITAL LETTER U WITH GRAVE +0xDA 0x00DA # LATIN CAPITAL LETTER U WITH ACUTE +0xDB 0x00DB # LATIN CAPITAL LETTER U WITH CIRCUMFLEX +0xDC 0x00DC # LATIN CAPITAL LETTER U WITH DIAERESIS +0xDD 0x016C # LATIN CAPITAL LETTER U WITH BREVE +0xDE 0x015C # LATIN CAPITAL LETTER S WITH CIRCUMFLEX +0xDF 0x00DF # LATIN SMALL LETTER SHARP S +0xE0 0x00E0 # LATIN SMALL LETTER A WITH GRAVE +0xE1 0x00E1 # LATIN SMALL LETTER A WITH ACUTE +0xE2 0x00E2 # LATIN SMALL LETTER A WITH CIRCUMFLEX +0xE4 0x00E4 # LATIN SMALL LETTER A WITH DIAERESIS +0xE5 0x010B # LATIN SMALL LETTER C WITH DOT ABOVE +0xE6 0x0109 # LATIN SMALL LETTER C WITH CIRCUMFLEX +0xE7 0x00E7 # LATIN SMALL LETTER C WITH CEDILLA +0xE8 0x00E8 # LATIN SMALL LETTER E WITH GRAVE +0xE9 0x00E9 # LATIN SMALL LETTER E WITH ACUTE +0xEA 0x00EA # LATIN SMALL LETTER E WITH CIRCUMFLEX +0xEB 0x00EB # LATIN SMALL LETTER E WITH DIAERESIS +0xEC 0x00EC # LATIN SMALL LETTER I WITH GRAVE +0xED 0x00ED # LATIN SMALL LETTER I WITH ACUTE +0xEE 0x00EE # LATIN SMALL LETTER I WITH CIRCUMFLEX +0xEF 0x00EF # LATIN SMALL LETTER I WITH DIAERESIS +0xF1 0x00F1 # LATIN SMALL LETTER N WITH TILDE +0xF2 0x00F2 # LATIN SMALL LETTER O WITH GRAVE +0xF3 0x00F3 # LATIN SMALL LETTER O WITH ACUTE +0xF4 0x00F4 # LATIN SMALL LETTER O WITH CIRCUMFLEX +0xF5 0x0121 # LATIN SMALL LETTER G WITH DOT ABOVE +0xF6 0x00F6 # LATIN SMALL LETTER O WITH DIAERESIS +0xF7 0x00F7 # DIVISION SIGN +0xF8 0x011D # LATIN SMALL LETTER G WITH CIRCUMFLEX +0xF9 0x00F9 # LATIN SMALL LETTER U WITH GRAVE +0xFA 0x00FA # LATIN SMALL LETTER U WITH ACUTE +0xFB 0x00FB # LATIN SMALL LETTER U WITH CIRCUMFLEX +0xFC 0x00FC # LATIN SMALL LETTER U WITH DIAERESIS +0xFD 0x016D # LATIN SMALL LETTER U WITH BREVE +0xFE 0x015D # LATIN SMALL LETTER S WITH CIRCUMFLEX +0xFF 0x02D9 # DOT ABOVE diff --git a/codepage/iso8859-4.txt b/codepage/iso8859-4.txt new file mode 100644 index 0000000..662e698 --- /dev/null +++ b/codepage/iso8859-4.txt @@ -0,0 +1,303 @@ +# +# Name: ISO/IEC 8859-4:1998 to Unicode +# Unicode version: 3.0 +# Table version: 1.0 +# Table format: Format A +# Date: 1999 July 27 +# Authors: Ken Whistler +# +# Copyright (c) 1991-1999 Unicode, Inc. All Rights reserved. +# +# This file is provided as-is by Unicode, Inc. (The Unicode Consortium). +# No claims are made as to fitness for any particular purpose. No +# warranties of any kind are expressed or implied. The recipient +# agrees to determine applicability of information provided. If this +# file has been provided on optical media by Unicode, Inc., the sole +# remedy for any claim will be exchange of defective media within 90 +# days of receipt. +# +# Unicode, Inc. hereby grants the right to freely use the information +# supplied in this file in the creation of products supporting the +# Unicode Standard, and to make copies of this file in any form for +# internal or external distribution as long as this notice remains +# attached. +# +# General notes: +# +# This table contains the data the Unicode Consortium has on how +# ISO/IEC 8859-4:1998 characters map into Unicode. +# +# Format: Three tab-separated columns +# Column #1 is the ISO/IEC 8859-4 code (in hex as 0xXX) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 the Unicode name (follows a comment sign, '#') +# +# The entries are in ISO/IEC 8859-4 order. +# +# Version history +# 1.0 version updates 0.1 version by adding mappings for all +# control characters. +# +# Updated versions of this file may be found in: +# +# +# Any comments or problems, contact +# Please note that is an archival address; +# notices will be checked, but do not expect an immediate response. +# +0x00 0x0000 # NULL +0x01 0x0001 # START OF HEADING +0x02 0x0002 # START OF TEXT +0x03 0x0003 # END OF TEXT +0x04 0x0004 # END OF TRANSMISSION +0x05 0x0005 # ENQUIRY +0x06 0x0006 # ACKNOWLEDGE +0x07 0x0007 # BELL +0x08 0x0008 # BACKSPACE +0x09 0x0009 # HORIZONTAL TABULATION +0x0A 0x000A # LINE FEED +0x0B 0x000B # VERTICAL TABULATION +0x0C 0x000C # FORM FEED +0x0D 0x000D # CARRIAGE RETURN +0x0E 0x000E # SHIFT OUT +0x0F 0x000F # SHIFT IN +0x10 0x0010 # DATA LINK ESCAPE +0x11 0x0011 # DEVICE CONTROL ONE +0x12 0x0012 # DEVICE CONTROL TWO +0x13 0x0013 # DEVICE CONTROL THREE +0x14 0x0014 # DEVICE CONTROL FOUR +0x15 0x0015 # NEGATIVE ACKNOWLEDGE +0x16 0x0016 # SYNCHRONOUS IDLE +0x17 0x0017 # END OF TRANSMISSION BLOCK +0x18 0x0018 # CANCEL +0x19 0x0019 # END OF MEDIUM +0x1A 0x001A # SUBSTITUTE +0x1B 0x001B # ESCAPE +0x1C 0x001C # FILE SEPARATOR +0x1D 0x001D # GROUP SEPARATOR +0x1E 0x001E # RECORD SEPARATOR +0x1F 0x001F # UNIT SEPARATOR +0x20 0x0020 # SPACE +0x21 0x0021 # EXCLAMATION MARK +0x22 0x0022 # QUOTATION MARK +0x23 0x0023 # NUMBER SIGN +0x24 0x0024 # DOLLAR SIGN +0x25 0x0025 # PERCENT SIGN +0x26 0x0026 # AMPERSAND +0x27 0x0027 # APOSTROPHE +0x28 0x0028 # LEFT PARENTHESIS +0x29 0x0029 # RIGHT PARENTHESIS +0x2A 0x002A # ASTERISK +0x2B 0x002B # PLUS SIGN +0x2C 0x002C # COMMA +0x2D 0x002D # HYPHEN-MINUS +0x2E 0x002E # FULL STOP +0x2F 0x002F # SOLIDUS +0x30 0x0030 # DIGIT ZERO +0x31 0x0031 # DIGIT ONE +0x32 0x0032 # DIGIT TWO +0x33 0x0033 # DIGIT THREE +0x34 0x0034 # DIGIT FOUR +0x35 0x0035 # DIGIT FIVE +0x36 0x0036 # DIGIT SIX +0x37 0x0037 # DIGIT SEVEN +0x38 0x0038 # DIGIT EIGHT +0x39 0x0039 # DIGIT NINE +0x3A 0x003A # COLON +0x3B 0x003B # SEMICOLON +0x3C 0x003C # LESS-THAN SIGN +0x3D 0x003D # EQUALS SIGN +0x3E 0x003E # GREATER-THAN SIGN +0x3F 0x003F # QUESTION MARK +0x40 0x0040 # COMMERCIAL AT +0x41 0x0041 # LATIN CAPITAL LETTER A +0x42 0x0042 # LATIN CAPITAL LETTER B +0x43 0x0043 # LATIN CAPITAL LETTER C +0x44 0x0044 # LATIN CAPITAL LETTER D +0x45 0x0045 # LATIN CAPITAL LETTER E +0x46 0x0046 # LATIN CAPITAL LETTER F +0x47 0x0047 # LATIN CAPITAL LETTER G +0x48 0x0048 # LATIN CAPITAL LETTER H +0x49 0x0049 # LATIN CAPITAL LETTER I +0x4A 0x004A # LATIN CAPITAL LETTER J +0x4B 0x004B # LATIN CAPITAL LETTER K +0x4C 0x004C # LATIN CAPITAL LETTER L +0x4D 0x004D # LATIN CAPITAL LETTER M +0x4E 0x004E # LATIN CAPITAL LETTER N +0x4F 0x004F # LATIN CAPITAL LETTER O +0x50 0x0050 # LATIN CAPITAL LETTER P +0x51 0x0051 # LATIN CAPITAL LETTER Q +0x52 0x0052 # LATIN CAPITAL LETTER R +0x53 0x0053 # LATIN CAPITAL LETTER S +0x54 0x0054 # LATIN CAPITAL LETTER T +0x55 0x0055 # LATIN CAPITAL LETTER U +0x56 0x0056 # LATIN CAPITAL LETTER V +0x57 0x0057 # LATIN CAPITAL LETTER W +0x58 0x0058 # LATIN CAPITAL LETTER X +0x59 0x0059 # LATIN CAPITAL LETTER Y +0x5A 0x005A # LATIN CAPITAL LETTER Z +0x5B 0x005B # LEFT SQUARE BRACKET +0x5C 0x005C # REVERSE SOLIDUS +0x5D 0x005D # RIGHT SQUARE BRACKET +0x5E 0x005E # CIRCUMFLEX ACCENT +0x5F 0x005F # LOW LINE +0x60 0x0060 # GRAVE ACCENT +0x61 0x0061 # LATIN SMALL LETTER A +0x62 0x0062 # LATIN SMALL LETTER B +0x63 0x0063 # LATIN SMALL LETTER C +0x64 0x0064 # LATIN SMALL LETTER D +0x65 0x0065 # LATIN SMALL LETTER E +0x66 0x0066 # LATIN SMALL LETTER F +0x67 0x0067 # LATIN SMALL LETTER G +0x68 0x0068 # LATIN SMALL LETTER H +0x69 0x0069 # LATIN SMALL LETTER I +0x6A 0x006A # LATIN SMALL LETTER J +0x6B 0x006B # LATIN SMALL LETTER K +0x6C 0x006C # LATIN SMALL LETTER L +0x6D 0x006D # LATIN SMALL LETTER M +0x6E 0x006E # LATIN SMALL LETTER N +0x6F 0x006F # LATIN SMALL LETTER O +0x70 0x0070 # LATIN SMALL LETTER P +0x71 0x0071 # LATIN SMALL LETTER Q +0x72 0x0072 # LATIN SMALL LETTER R +0x73 0x0073 # LATIN SMALL LETTER S +0x74 0x0074 # LATIN SMALL LETTER T +0x75 0x0075 # LATIN SMALL LETTER U +0x76 0x0076 # LATIN SMALL LETTER V +0x77 0x0077 # LATIN SMALL LETTER W +0x78 0x0078 # LATIN SMALL LETTER X +0x79 0x0079 # LATIN SMALL LETTER Y +0x7A 0x007A # LATIN SMALL LETTER Z +0x7B 0x007B # LEFT CURLY BRACKET +0x7C 0x007C # VERTICAL LINE +0x7D 0x007D # RIGHT CURLY BRACKET +0x7E 0x007E # TILDE +0x7F 0x007F # DELETE +0x80 0x0080 # +0x81 0x0081 # +0x82 0x0082 # +0x83 0x0083 # +0x84 0x0084 # +0x85 0x0085 # +0x86 0x0086 # +0x87 0x0087 # +0x88 0x0088 # +0x89 0x0089 # +0x8A 0x008A # +0x8B 0x008B # +0x8C 0x008C # +0x8D 0x008D # +0x8E 0x008E # +0x8F 0x008F # +0x90 0x0090 # +0x91 0x0091 # +0x92 0x0092 # +0x93 0x0093 # +0x94 0x0094 # +0x95 0x0095 # +0x96 0x0096 # +0x97 0x0097 # +0x98 0x0098 # +0x99 0x0099 # +0x9A 0x009A # +0x9B 0x009B # +0x9C 0x009C # +0x9D 0x009D # +0x9E 0x009E # +0x9F 0x009F # +0xA0 0x00A0 # NO-BREAK SPACE +0xA1 0x0104 # LATIN CAPITAL LETTER A WITH OGONEK +0xA2 0x0138 # LATIN SMALL LETTER KRA +0xA3 0x0156 # LATIN CAPITAL LETTER R WITH CEDILLA +0xA4 0x00A4 # CURRENCY SIGN +0xA5 0x0128 # LATIN CAPITAL LETTER I WITH TILDE +0xA6 0x013B # LATIN CAPITAL LETTER L WITH CEDILLA +0xA7 0x00A7 # SECTION SIGN +0xA8 0x00A8 # DIAERESIS +0xA9 0x0160 # LATIN CAPITAL LETTER S WITH CARON +0xAA 0x0112 # LATIN CAPITAL LETTER E WITH MACRON +0xAB 0x0122 # LATIN CAPITAL LETTER G WITH CEDILLA +0xAC 0x0166 # LATIN CAPITAL LETTER T WITH STROKE +0xAD 0x00AD # SOFT HYPHEN +0xAE 0x017D # LATIN CAPITAL LETTER Z WITH CARON +0xAF 0x00AF # MACRON +0xB0 0x00B0 # DEGREE SIGN +0xB1 0x0105 # LATIN SMALL LETTER A WITH OGONEK +0xB2 0x02DB # OGONEK +0xB3 0x0157 # LATIN SMALL LETTER R WITH CEDILLA +0xB4 0x00B4 # ACUTE ACCENT +0xB5 0x0129 # LATIN SMALL LETTER I WITH TILDE +0xB6 0x013C # LATIN SMALL LETTER L WITH CEDILLA +0xB7 0x02C7 # CARON +0xB8 0x00B8 # CEDILLA +0xB9 0x0161 # LATIN SMALL LETTER S WITH CARON +0xBA 0x0113 # LATIN SMALL LETTER E WITH MACRON +0xBB 0x0123 # LATIN SMALL LETTER G WITH CEDILLA +0xBC 0x0167 # LATIN SMALL LETTER T WITH STROKE +0xBD 0x014A # LATIN CAPITAL LETTER ENG +0xBE 0x017E # LATIN SMALL LETTER Z WITH CARON +0xBF 0x014B # LATIN SMALL LETTER ENG +0xC0 0x0100 # LATIN CAPITAL LETTER A WITH MACRON +0xC1 0x00C1 # LATIN CAPITAL LETTER A WITH ACUTE +0xC2 0x00C2 # LATIN CAPITAL LETTER A WITH CIRCUMFLEX +0xC3 0x00C3 # LATIN CAPITAL LETTER A WITH TILDE +0xC4 0x00C4 # LATIN CAPITAL LETTER A WITH DIAERESIS +0xC5 0x00C5 # LATIN CAPITAL LETTER A WITH RING ABOVE +0xC6 0x00C6 # LATIN CAPITAL LETTER AE +0xC7 0x012E # LATIN CAPITAL LETTER I WITH OGONEK +0xC8 0x010C # LATIN CAPITAL LETTER C WITH CARON +0xC9 0x00C9 # LATIN CAPITAL LETTER E WITH ACUTE +0xCA 0x0118 # LATIN CAPITAL LETTER E WITH OGONEK +0xCB 0x00CB # LATIN CAPITAL LETTER E WITH DIAERESIS +0xCC 0x0116 # LATIN CAPITAL LETTER E WITH DOT ABOVE +0xCD 0x00CD # LATIN CAPITAL LETTER I WITH ACUTE +0xCE 0x00CE # LATIN CAPITAL LETTER I WITH CIRCUMFLEX +0xCF 0x012A # LATIN CAPITAL LETTER I WITH MACRON +0xD0 0x0110 # LATIN CAPITAL LETTER D WITH STROKE +0xD1 0x0145 # LATIN CAPITAL LETTER N WITH CEDILLA +0xD2 0x014C # LATIN CAPITAL LETTER O WITH MACRON +0xD3 0x0136 # LATIN CAPITAL LETTER K WITH CEDILLA +0xD4 0x00D4 # LATIN CAPITAL LETTER O WITH CIRCUMFLEX +0xD5 0x00D5 # LATIN CAPITAL LETTER O WITH TILDE +0xD6 0x00D6 # LATIN CAPITAL LETTER O WITH DIAERESIS +0xD7 0x00D7 # MULTIPLICATION SIGN +0xD8 0x00D8 # LATIN CAPITAL LETTER O WITH STROKE +0xD9 0x0172 # LATIN CAPITAL LETTER U WITH OGONEK +0xDA 0x00DA # LATIN CAPITAL LETTER U WITH ACUTE +0xDB 0x00DB # LATIN CAPITAL LETTER U WITH CIRCUMFLEX +0xDC 0x00DC # LATIN CAPITAL LETTER U WITH DIAERESIS +0xDD 0x0168 # LATIN CAPITAL LETTER U WITH TILDE +0xDE 0x016A # LATIN CAPITAL LETTER U WITH MACRON +0xDF 0x00DF # LATIN SMALL LETTER SHARP S +0xE0 0x0101 # LATIN SMALL LETTER A WITH MACRON +0xE1 0x00E1 # LATIN SMALL LETTER A WITH ACUTE +0xE2 0x00E2 # LATIN SMALL LETTER A WITH CIRCUMFLEX +0xE3 0x00E3 # LATIN SMALL LETTER A WITH TILDE +0xE4 0x00E4 # LATIN SMALL LETTER A WITH DIAERESIS +0xE5 0x00E5 # LATIN SMALL LETTER A WITH RING ABOVE +0xE6 0x00E6 # LATIN SMALL LETTER AE +0xE7 0x012F # LATIN SMALL LETTER I WITH OGONEK +0xE8 0x010D # LATIN SMALL LETTER C WITH CARON +0xE9 0x00E9 # LATIN SMALL LETTER E WITH ACUTE +0xEA 0x0119 # LATIN SMALL LETTER E WITH OGONEK +0xEB 0x00EB # LATIN SMALL LETTER E WITH DIAERESIS +0xEC 0x0117 # LATIN SMALL LETTER E WITH DOT ABOVE +0xED 0x00ED # LATIN SMALL LETTER I WITH ACUTE +0xEE 0x00EE # LATIN SMALL LETTER I WITH CIRCUMFLEX +0xEF 0x012B # LATIN SMALL LETTER I WITH MACRON +0xF0 0x0111 # LATIN SMALL LETTER D WITH STROKE +0xF1 0x0146 # LATIN SMALL LETTER N WITH CEDILLA +0xF2 0x014D # LATIN SMALL LETTER O WITH MACRON +0xF3 0x0137 # LATIN SMALL LETTER K WITH CEDILLA +0xF4 0x00F4 # LATIN SMALL LETTER O WITH CIRCUMFLEX +0xF5 0x00F5 # LATIN SMALL LETTER O WITH TILDE +0xF6 0x00F6 # LATIN SMALL LETTER O WITH DIAERESIS +0xF7 0x00F7 # DIVISION SIGN +0xF8 0x00F8 # LATIN SMALL LETTER O WITH STROKE +0xF9 0x0173 # LATIN SMALL LETTER U WITH OGONEK +0xFA 0x00FA # LATIN SMALL LETTER U WITH ACUTE +0xFB 0x00FB # LATIN SMALL LETTER U WITH CIRCUMFLEX +0xFC 0x00FC # LATIN SMALL LETTER U WITH DIAERESIS +0xFD 0x0169 # LATIN SMALL LETTER U WITH TILDE +0xFE 0x016B # LATIN SMALL LETTER U WITH MACRON +0xFF 0x02D9 # DOT ABOVE diff --git a/codepage/iso8859-5.txt b/codepage/iso8859-5.txt new file mode 100644 index 0000000..a7ed1ce --- /dev/null +++ b/codepage/iso8859-5.txt @@ -0,0 +1,303 @@ +# +# Name: ISO 8859-5:1999 to Unicode +# Unicode version: 3.0 +# Table version: 1.0 +# Table format: Format A +# Date: 1999 July 27 +# Authors: Ken Whistler +# +# Copyright (c) 1991-1999 Unicode, Inc. All Rights reserved. +# +# This file is provided as-is by Unicode, Inc. (The Unicode Consortium). +# No claims are made as to fitness for any particular purpose. No +# warranties of any kind are expressed or implied. The recipient +# agrees to determine applicability of information provided. If this +# file has been provided on optical media by Unicode, Inc., the sole +# remedy for any claim will be exchange of defective media within 90 +# days of receipt. +# +# Unicode, Inc. hereby grants the right to freely use the information +# supplied in this file in the creation of products supporting the +# Unicode Standard, and to make copies of this file in any form for +# internal or external distribution as long as this notice remains +# attached. +# +# General notes: +# +# This table contains the data the Unicode Consortium has on how +# ISO/IEC 8859-5:1999 characters map into Unicode. +# +# Format: Three tab-separated columns +# Column #1 is the ISO/IEC 8859-5 code (in hex as 0xXX) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 the Unicode name (follows a comment sign, '#') +# +# The entries are in ISO/IEC 8859-5 order. +# +# Version history +# 1.0 version updates 0.1 version by adding mappings for all +# control characters. +# +# Updated versions of this file may be found in: +# +# +# Any comments or problems, contact +# Please note that is an archival address; +# notices will be checked, but do not expect an immediate response. +# +0x00 0x0000 # NULL +0x01 0x0001 # START OF HEADING +0x02 0x0002 # START OF TEXT +0x03 0x0003 # END OF TEXT +0x04 0x0004 # END OF TRANSMISSION +0x05 0x0005 # ENQUIRY +0x06 0x0006 # ACKNOWLEDGE +0x07 0x0007 # BELL +0x08 0x0008 # BACKSPACE +0x09 0x0009 # HORIZONTAL TABULATION +0x0A 0x000A # LINE FEED +0x0B 0x000B # VERTICAL TABULATION +0x0C 0x000C # FORM FEED +0x0D 0x000D # CARRIAGE RETURN +0x0E 0x000E # SHIFT OUT +0x0F 0x000F # SHIFT IN +0x10 0x0010 # DATA LINK ESCAPE +0x11 0x0011 # DEVICE CONTROL ONE +0x12 0x0012 # DEVICE CONTROL TWO +0x13 0x0013 # DEVICE CONTROL THREE +0x14 0x0014 # DEVICE CONTROL FOUR +0x15 0x0015 # NEGATIVE ACKNOWLEDGE +0x16 0x0016 # SYNCHRONOUS IDLE +0x17 0x0017 # END OF TRANSMISSION BLOCK +0x18 0x0018 # CANCEL +0x19 0x0019 # END OF MEDIUM +0x1A 0x001A # SUBSTITUTE +0x1B 0x001B # ESCAPE +0x1C 0x001C # FILE SEPARATOR +0x1D 0x001D # GROUP SEPARATOR +0x1E 0x001E # RECORD SEPARATOR +0x1F 0x001F # UNIT SEPARATOR +0x20 0x0020 # SPACE +0x21 0x0021 # EXCLAMATION MARK +0x22 0x0022 # QUOTATION MARK +0x23 0x0023 # NUMBER SIGN +0x24 0x0024 # DOLLAR SIGN +0x25 0x0025 # PERCENT SIGN +0x26 0x0026 # AMPERSAND +0x27 0x0027 # APOSTROPHE +0x28 0x0028 # LEFT PARENTHESIS +0x29 0x0029 # RIGHT PARENTHESIS +0x2A 0x002A # ASTERISK +0x2B 0x002B # PLUS SIGN +0x2C 0x002C # COMMA +0x2D 0x002D # HYPHEN-MINUS +0x2E 0x002E # FULL STOP +0x2F 0x002F # SOLIDUS +0x30 0x0030 # DIGIT ZERO +0x31 0x0031 # DIGIT ONE +0x32 0x0032 # DIGIT TWO +0x33 0x0033 # DIGIT THREE +0x34 0x0034 # DIGIT FOUR +0x35 0x0035 # DIGIT FIVE +0x36 0x0036 # DIGIT SIX +0x37 0x0037 # DIGIT SEVEN +0x38 0x0038 # DIGIT EIGHT +0x39 0x0039 # DIGIT NINE +0x3A 0x003A # COLON +0x3B 0x003B # SEMICOLON +0x3C 0x003C # LESS-THAN SIGN +0x3D 0x003D # EQUALS SIGN +0x3E 0x003E # GREATER-THAN SIGN +0x3F 0x003F # QUESTION MARK +0x40 0x0040 # COMMERCIAL AT +0x41 0x0041 # LATIN CAPITAL LETTER A +0x42 0x0042 # LATIN CAPITAL LETTER B +0x43 0x0043 # LATIN CAPITAL LETTER C +0x44 0x0044 # LATIN CAPITAL LETTER D +0x45 0x0045 # LATIN CAPITAL LETTER E +0x46 0x0046 # LATIN CAPITAL LETTER F +0x47 0x0047 # LATIN CAPITAL LETTER G +0x48 0x0048 # LATIN CAPITAL LETTER H +0x49 0x0049 # LATIN CAPITAL LETTER I +0x4A 0x004A # LATIN CAPITAL LETTER J +0x4B 0x004B # LATIN CAPITAL LETTER K +0x4C 0x004C # LATIN CAPITAL LETTER L +0x4D 0x004D # LATIN CAPITAL LETTER M +0x4E 0x004E # LATIN CAPITAL LETTER N +0x4F 0x004F # LATIN CAPITAL LETTER O +0x50 0x0050 # LATIN CAPITAL LETTER P +0x51 0x0051 # LATIN CAPITAL LETTER Q +0x52 0x0052 # LATIN CAPITAL LETTER R +0x53 0x0053 # LATIN CAPITAL LETTER S +0x54 0x0054 # LATIN CAPITAL LETTER T +0x55 0x0055 # LATIN CAPITAL LETTER U +0x56 0x0056 # LATIN CAPITAL LETTER V +0x57 0x0057 # LATIN CAPITAL LETTER W +0x58 0x0058 # LATIN CAPITAL LETTER X +0x59 0x0059 # LATIN CAPITAL LETTER Y +0x5A 0x005A # LATIN CAPITAL LETTER Z +0x5B 0x005B # LEFT SQUARE BRACKET +0x5C 0x005C # REVERSE SOLIDUS +0x5D 0x005D # RIGHT SQUARE BRACKET +0x5E 0x005E # CIRCUMFLEX ACCENT +0x5F 0x005F # LOW LINE +0x60 0x0060 # GRAVE ACCENT +0x61 0x0061 # LATIN SMALL LETTER A +0x62 0x0062 # LATIN SMALL LETTER B +0x63 0x0063 # LATIN SMALL LETTER C +0x64 0x0064 # LATIN SMALL LETTER D +0x65 0x0065 # LATIN SMALL LETTER E +0x66 0x0066 # LATIN SMALL LETTER F +0x67 0x0067 # LATIN SMALL LETTER G +0x68 0x0068 # LATIN SMALL LETTER H +0x69 0x0069 # LATIN SMALL LETTER I +0x6A 0x006A # LATIN SMALL LETTER J +0x6B 0x006B # LATIN SMALL LETTER K +0x6C 0x006C # LATIN SMALL LETTER L +0x6D 0x006D # LATIN SMALL LETTER M +0x6E 0x006E # LATIN SMALL LETTER N +0x6F 0x006F # LATIN SMALL LETTER O +0x70 0x0070 # LATIN SMALL LETTER P +0x71 0x0071 # LATIN SMALL LETTER Q +0x72 0x0072 # LATIN SMALL LETTER R +0x73 0x0073 # LATIN SMALL LETTER S +0x74 0x0074 # LATIN SMALL LETTER T +0x75 0x0075 # LATIN SMALL LETTER U +0x76 0x0076 # LATIN SMALL LETTER V +0x77 0x0077 # LATIN SMALL LETTER W +0x78 0x0078 # LATIN SMALL LETTER X +0x79 0x0079 # LATIN SMALL LETTER Y +0x7A 0x007A # LATIN SMALL LETTER Z +0x7B 0x007B # LEFT CURLY BRACKET +0x7C 0x007C # VERTICAL LINE +0x7D 0x007D # RIGHT CURLY BRACKET +0x7E 0x007E # TILDE +0x7F 0x007F # DELETE +0x80 0x0080 # +0x81 0x0081 # +0x82 0x0082 # +0x83 0x0083 # +0x84 0x0084 # +0x85 0x0085 # +0x86 0x0086 # +0x87 0x0087 # +0x88 0x0088 # +0x89 0x0089 # +0x8A 0x008A # +0x8B 0x008B # +0x8C 0x008C # +0x8D 0x008D # +0x8E 0x008E # +0x8F 0x008F # +0x90 0x0090 # +0x91 0x0091 # +0x92 0x0092 # +0x93 0x0093 # +0x94 0x0094 # +0x95 0x0095 # +0x96 0x0096 # +0x97 0x0097 # +0x98 0x0098 # +0x99 0x0099 # +0x9A 0x009A # +0x9B 0x009B # +0x9C 0x009C # +0x9D 0x009D # +0x9E 0x009E # +0x9F 0x009F # +0xA0 0x00A0 # NO-BREAK SPACE +0xA1 0x0401 # CYRILLIC CAPITAL LETTER IO +0xA2 0x0402 # CYRILLIC CAPITAL LETTER DJE +0xA3 0x0403 # CYRILLIC CAPITAL LETTER GJE +0xA4 0x0404 # CYRILLIC CAPITAL LETTER UKRAINIAN IE +0xA5 0x0405 # CYRILLIC CAPITAL LETTER DZE +0xA6 0x0406 # CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I +0xA7 0x0407 # CYRILLIC CAPITAL LETTER YI +0xA8 0x0408 # CYRILLIC CAPITAL LETTER JE +0xA9 0x0409 # CYRILLIC CAPITAL LETTER LJE +0xAA 0x040A # CYRILLIC CAPITAL LETTER NJE +0xAB 0x040B # CYRILLIC CAPITAL LETTER TSHE +0xAC 0x040C # CYRILLIC CAPITAL LETTER KJE +0xAD 0x00AD # SOFT HYPHEN +0xAE 0x040E # CYRILLIC CAPITAL LETTER SHORT U +0xAF 0x040F # CYRILLIC CAPITAL LETTER DZHE +0xB0 0x0410 # CYRILLIC CAPITAL LETTER A +0xB1 0x0411 # CYRILLIC CAPITAL LETTER BE +0xB2 0x0412 # CYRILLIC CAPITAL LETTER VE +0xB3 0x0413 # CYRILLIC CAPITAL LETTER GHE +0xB4 0x0414 # CYRILLIC CAPITAL LETTER DE +0xB5 0x0415 # CYRILLIC CAPITAL LETTER IE +0xB6 0x0416 # CYRILLIC CAPITAL LETTER ZHE +0xB7 0x0417 # CYRILLIC CAPITAL LETTER ZE +0xB8 0x0418 # CYRILLIC CAPITAL LETTER I +0xB9 0x0419 # CYRILLIC CAPITAL LETTER SHORT I +0xBA 0x041A # CYRILLIC CAPITAL LETTER KA +0xBB 0x041B # CYRILLIC CAPITAL LETTER EL +0xBC 0x041C # CYRILLIC CAPITAL LETTER EM +0xBD 0x041D # CYRILLIC CAPITAL LETTER EN +0xBE 0x041E # CYRILLIC CAPITAL LETTER O +0xBF 0x041F # CYRILLIC CAPITAL LETTER PE +0xC0 0x0420 # CYRILLIC CAPITAL LETTER ER +0xC1 0x0421 # CYRILLIC CAPITAL LETTER ES +0xC2 0x0422 # CYRILLIC CAPITAL LETTER TE +0xC3 0x0423 # CYRILLIC CAPITAL LETTER U +0xC4 0x0424 # CYRILLIC CAPITAL LETTER EF +0xC5 0x0425 # CYRILLIC CAPITAL LETTER HA +0xC6 0x0426 # CYRILLIC CAPITAL LETTER TSE +0xC7 0x0427 # CYRILLIC CAPITAL LETTER CHE +0xC8 0x0428 # CYRILLIC CAPITAL LETTER SHA +0xC9 0x0429 # CYRILLIC CAPITAL LETTER SHCHA +0xCA 0x042A # CYRILLIC CAPITAL LETTER HARD SIGN +0xCB 0x042B # CYRILLIC CAPITAL LETTER YERU +0xCC 0x042C # CYRILLIC CAPITAL LETTER SOFT SIGN +0xCD 0x042D # CYRILLIC CAPITAL LETTER E +0xCE 0x042E # CYRILLIC CAPITAL LETTER YU +0xCF 0x042F # CYRILLIC CAPITAL LETTER YA +0xD0 0x0430 # CYRILLIC SMALL LETTER A +0xD1 0x0431 # CYRILLIC SMALL LETTER BE +0xD2 0x0432 # CYRILLIC SMALL LETTER VE +0xD3 0x0433 # CYRILLIC SMALL LETTER GHE +0xD4 0x0434 # CYRILLIC SMALL LETTER DE +0xD5 0x0435 # CYRILLIC SMALL LETTER IE +0xD6 0x0436 # CYRILLIC SMALL LETTER ZHE +0xD7 0x0437 # CYRILLIC SMALL LETTER ZE +0xD8 0x0438 # CYRILLIC SMALL LETTER I +0xD9 0x0439 # CYRILLIC SMALL LETTER SHORT I +0xDA 0x043A # CYRILLIC SMALL LETTER KA +0xDB 0x043B # CYRILLIC SMALL LETTER EL +0xDC 0x043C # CYRILLIC SMALL LETTER EM +0xDD 0x043D # CYRILLIC SMALL LETTER EN +0xDE 0x043E # CYRILLIC SMALL LETTER O +0xDF 0x043F # CYRILLIC SMALL LETTER PE +0xE0 0x0440 # CYRILLIC SMALL LETTER ER +0xE1 0x0441 # CYRILLIC SMALL LETTER ES +0xE2 0x0442 # CYRILLIC SMALL LETTER TE +0xE3 0x0443 # CYRILLIC SMALL LETTER U +0xE4 0x0444 # CYRILLIC SMALL LETTER EF +0xE5 0x0445 # CYRILLIC SMALL LETTER HA +0xE6 0x0446 # CYRILLIC SMALL LETTER TSE +0xE7 0x0447 # CYRILLIC SMALL LETTER CHE +0xE8 0x0448 # CYRILLIC SMALL LETTER SHA +0xE9 0x0449 # CYRILLIC SMALL LETTER SHCHA +0xEA 0x044A # CYRILLIC SMALL LETTER HARD SIGN +0xEB 0x044B # CYRILLIC SMALL LETTER YERU +0xEC 0x044C # CYRILLIC SMALL LETTER SOFT SIGN +0xED 0x044D # CYRILLIC SMALL LETTER E +0xEE 0x044E # CYRILLIC SMALL LETTER YU +0xEF 0x044F # CYRILLIC SMALL LETTER YA +0xF0 0x2116 # NUMERO SIGN +0xF1 0x0451 # CYRILLIC SMALL LETTER IO +0xF2 0x0452 # CYRILLIC SMALL LETTER DJE +0xF3 0x0453 # CYRILLIC SMALL LETTER GJE +0xF4 0x0454 # CYRILLIC SMALL LETTER UKRAINIAN IE +0xF5 0x0455 # CYRILLIC SMALL LETTER DZE +0xF6 0x0456 # CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I +0xF7 0x0457 # CYRILLIC SMALL LETTER YI +0xF8 0x0458 # CYRILLIC SMALL LETTER JE +0xF9 0x0459 # CYRILLIC SMALL LETTER LJE +0xFA 0x045A # CYRILLIC SMALL LETTER NJE +0xFB 0x045B # CYRILLIC SMALL LETTER TSHE +0xFC 0x045C # CYRILLIC SMALL LETTER KJE +0xFD 0x00A7 # SECTION SIGN +0xFE 0x045E # CYRILLIC SMALL LETTER SHORT U +0xFF 0x045F # CYRILLIC SMALL LETTER DZHE diff --git a/codepage/iso8859-6.txt b/codepage/iso8859-6.txt new file mode 100644 index 0000000..69ac7f5 --- /dev/null +++ b/codepage/iso8859-6.txt @@ -0,0 +1,260 @@ +# +# Name: ISO 8859-6:1999 to Unicode +# Unicode version: 3.0 +# Table version: 1.0 +# Table format: Format A +# Date: 1999 July 27 +# Authors: Ken Whistler +# +# Copyright (c) 1991-1999 Unicode, Inc. All Rights reserved. +# +# This file is provided as-is by Unicode, Inc. (The Unicode Consortium). +# No claims are made as to fitness for any particular purpose. No +# warranties of any kind are expressed or implied. The recipient +# agrees to determine applicability of information provided. If this +# file has been provided on optical media by Unicode, Inc., the sole +# remedy for any claim will be exchange of defective media within 90 +# days of receipt. +# +# Unicode, Inc. hereby grants the right to freely use the information +# supplied in this file in the creation of products supporting the +# Unicode Standard, and to make copies of this file in any form for +# internal or external distribution as long as this notice remains +# attached. +# +# General notes: +# +# This table contains the data the Unicode Consortium has on how +# ISO/IEC 8859-6:1999 characters map into Unicode. +# +# Format: Three tab-separated columns +# Column #1 is the ISO/IEC 8859-6 code (in hex as 0xXX) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 the Unicode name (follows a comment sign, '#') +# +# The entries are in ISO/IEC 8859-6 order. +# +# Version history +# 1.0 version updates 0.1 version by adding mappings for all +# control characters. +# 0x30..0x39 remapped to the ASCII digits (U+0030..U+0039) instead +# of the Arabic digits (U+0660..U+0669). +# +# Updated versions of this file may be found in: +# +# +# Any comments or problems, contact +# Please note that is an archival address; +# notices will be checked, but do not expect an immediate response. +# +0x00 0x0000 # NULL +0x01 0x0001 # START OF HEADING +0x02 0x0002 # START OF TEXT +0x03 0x0003 # END OF TEXT +0x04 0x0004 # END OF TRANSMISSION +0x05 0x0005 # ENQUIRY +0x06 0x0006 # ACKNOWLEDGE +0x07 0x0007 # BELL +0x08 0x0008 # BACKSPACE +0x09 0x0009 # HORIZONTAL TABULATION +0x0A 0x000A # LINE FEED +0x0B 0x000B # VERTICAL TABULATION +0x0C 0x000C # FORM FEED +0x0D 0x000D # CARRIAGE RETURN +0x0E 0x000E # SHIFT OUT +0x0F 0x000F # SHIFT IN +0x10 0x0010 # DATA LINK ESCAPE +0x11 0x0011 # DEVICE CONTROL ONE +0x12 0x0012 # DEVICE CONTROL TWO +0x13 0x0013 # DEVICE CONTROL THREE +0x14 0x0014 # DEVICE CONTROL FOUR +0x15 0x0015 # NEGATIVE ACKNOWLEDGE +0x16 0x0016 # SYNCHRONOUS IDLE +0x17 0x0017 # END OF TRANSMISSION BLOCK +0x18 0x0018 # CANCEL +0x19 0x0019 # END OF MEDIUM +0x1A 0x001A # SUBSTITUTE +0x1B 0x001B # ESCAPE +0x1C 0x001C # FILE SEPARATOR +0x1D 0x001D # GROUP SEPARATOR +0x1E 0x001E # RECORD SEPARATOR +0x1F 0x001F # UNIT SEPARATOR +0x20 0x0020 # SPACE +0x21 0x0021 # EXCLAMATION MARK +0x22 0x0022 # QUOTATION MARK +0x23 0x0023 # NUMBER SIGN +0x24 0x0024 # DOLLAR SIGN +0x25 0x0025 # PERCENT SIGN +0x26 0x0026 # AMPERSAND +0x27 0x0027 # APOSTROPHE +0x28 0x0028 # LEFT PARENTHESIS +0x29 0x0029 # RIGHT PARENTHESIS +0x2A 0x002A # ASTERISK +0x2B 0x002B # PLUS SIGN +0x2C 0x002C # COMMA +0x2D 0x002D # HYPHEN-MINUS +0x2E 0x002E # FULL STOP +0x2F 0x002F # SOLIDUS +0x30 0x0030 # DIGIT ZERO +0x31 0x0031 # DIGIT ONE +0x32 0x0032 # DIGIT TWO +0x33 0x0033 # DIGIT THREE +0x34 0x0034 # DIGIT FOUR +0x35 0x0035 # DIGIT FIVE +0x36 0x0036 # DIGIT SIX +0x37 0x0037 # DIGIT SEVEN +0x38 0x0038 # DIGIT EIGHT +0x39 0x0039 # DIGIT NINE +0x3A 0x003A # COLON +0x3B 0x003B # SEMICOLON +0x3C 0x003C # LESS-THAN SIGN +0x3D 0x003D # EQUALS SIGN +0x3E 0x003E # GREATER-THAN SIGN +0x3F 0x003F # QUESTION MARK +0x40 0x0040 # COMMERCIAL AT +0x41 0x0041 # LATIN CAPITAL LETTER A +0x42 0x0042 # LATIN CAPITAL LETTER B +0x43 0x0043 # LATIN CAPITAL LETTER C +0x44 0x0044 # LATIN CAPITAL LETTER D +0x45 0x0045 # LATIN CAPITAL LETTER E +0x46 0x0046 # LATIN CAPITAL LETTER F +0x47 0x0047 # LATIN CAPITAL LETTER G +0x48 0x0048 # LATIN CAPITAL LETTER H +0x49 0x0049 # LATIN CAPITAL LETTER I +0x4A 0x004A # LATIN CAPITAL LETTER J +0x4B 0x004B # LATIN CAPITAL LETTER K +0x4C 0x004C # LATIN CAPITAL LETTER L +0x4D 0x004D # LATIN CAPITAL LETTER M +0x4E 0x004E # LATIN CAPITAL LETTER N +0x4F 0x004F # LATIN CAPITAL LETTER O +0x50 0x0050 # LATIN CAPITAL LETTER P +0x51 0x0051 # LATIN CAPITAL LETTER Q +0x52 0x0052 # LATIN CAPITAL LETTER R +0x53 0x0053 # LATIN CAPITAL LETTER S +0x54 0x0054 # LATIN CAPITAL LETTER T +0x55 0x0055 # LATIN CAPITAL LETTER U +0x56 0x0056 # LATIN CAPITAL LETTER V +0x57 0x0057 # LATIN CAPITAL LETTER W +0x58 0x0058 # LATIN CAPITAL LETTER X +0x59 0x0059 # LATIN CAPITAL LETTER Y +0x5A 0x005A # LATIN CAPITAL LETTER Z +0x5B 0x005B # LEFT SQUARE BRACKET +0x5C 0x005C # REVERSE SOLIDUS +0x5D 0x005D # RIGHT SQUARE BRACKET +0x5E 0x005E # CIRCUMFLEX ACCENT +0x5F 0x005F # LOW LINE +0x60 0x0060 # GRAVE ACCENT +0x61 0x0061 # LATIN SMALL LETTER A +0x62 0x0062 # LATIN SMALL LETTER B +0x63 0x0063 # LATIN SMALL LETTER C +0x64 0x0064 # LATIN SMALL LETTER D +0x65 0x0065 # LATIN SMALL LETTER E +0x66 0x0066 # LATIN SMALL LETTER F +0x67 0x0067 # LATIN SMALL LETTER G +0x68 0x0068 # LATIN SMALL LETTER H +0x69 0x0069 # LATIN SMALL LETTER I +0x6A 0x006A # LATIN SMALL LETTER J +0x6B 0x006B # LATIN SMALL LETTER K +0x6C 0x006C # LATIN SMALL LETTER L +0x6D 0x006D # LATIN SMALL LETTER M +0x6E 0x006E # LATIN SMALL LETTER N +0x6F 0x006F # LATIN SMALL LETTER O +0x70 0x0070 # LATIN SMALL LETTER P +0x71 0x0071 # LATIN SMALL LETTER Q +0x72 0x0072 # LATIN SMALL LETTER R +0x73 0x0073 # LATIN SMALL LETTER S +0x74 0x0074 # LATIN SMALL LETTER T +0x75 0x0075 # LATIN SMALL LETTER U +0x76 0x0076 # LATIN SMALL LETTER V +0x77 0x0077 # LATIN SMALL LETTER W +0x78 0x0078 # LATIN SMALL LETTER X +0x79 0x0079 # LATIN SMALL LETTER Y +0x7A 0x007A # LATIN SMALL LETTER Z +0x7B 0x007B # LEFT CURLY BRACKET +0x7C 0x007C # VERTICAL LINE +0x7D 0x007D # RIGHT CURLY BRACKET +0x7E 0x007E # TILDE +0x7F 0x007F # DELETE +0x80 0x0080 # +0x81 0x0081 # +0x82 0x0082 # +0x83 0x0083 # +0x84 0x0084 # +0x85 0x0085 # +0x86 0x0086 # +0x87 0x0087 # +0x88 0x0088 # +0x89 0x0089 # +0x8A 0x008A # +0x8B 0x008B # +0x8C 0x008C # +0x8D 0x008D # +0x8E 0x008E # +0x8F 0x008F # +0x90 0x0090 # +0x91 0x0091 # +0x92 0x0092 # +0x93 0x0093 # +0x94 0x0094 # +0x95 0x0095 # +0x96 0x0096 # +0x97 0x0097 # +0x98 0x0098 # +0x99 0x0099 # +0x9A 0x009A # +0x9B 0x009B # +0x9C 0x009C # +0x9D 0x009D # +0x9E 0x009E # +0x9F 0x009F # +0xA0 0x00A0 # NO-BREAK SPACE +0xA4 0x00A4 # CURRENCY SIGN +0xAC 0x060C # ARABIC COMMA +0xAD 0x00AD # SOFT HYPHEN +0xBB 0x061B # ARABIC SEMICOLON +0xBF 0x061F # ARABIC QUESTION MARK +0xC1 0x0621 # ARABIC LETTER HAMZA +0xC2 0x0622 # ARABIC LETTER ALEF WITH MADDA ABOVE +0xC3 0x0623 # ARABIC LETTER ALEF WITH HAMZA ABOVE +0xC4 0x0624 # ARABIC LETTER WAW WITH HAMZA ABOVE +0xC5 0x0625 # ARABIC LETTER ALEF WITH HAMZA BELOW +0xC6 0x0626 # ARABIC LETTER YEH WITH HAMZA ABOVE +0xC7 0x0627 # ARABIC LETTER ALEF +0xC8 0x0628 # ARABIC LETTER BEH +0xC9 0x0629 # ARABIC LETTER TEH MARBUTA +0xCA 0x062A # ARABIC LETTER TEH +0xCB 0x062B # ARABIC LETTER THEH +0xCC 0x062C # ARABIC LETTER JEEM +0xCD 0x062D # ARABIC LETTER HAH +0xCE 0x062E # ARABIC LETTER KHAH +0xCF 0x062F # ARABIC LETTER DAL +0xD0 0x0630 # ARABIC LETTER THAL +0xD1 0x0631 # ARABIC LETTER REH +0xD2 0x0632 # ARABIC LETTER ZAIN +0xD3 0x0633 # ARABIC LETTER SEEN +0xD4 0x0634 # ARABIC LETTER SHEEN +0xD5 0x0635 # ARABIC LETTER SAD +0xD6 0x0636 # ARABIC LETTER DAD +0xD7 0x0637 # ARABIC LETTER TAH +0xD8 0x0638 # ARABIC LETTER ZAH +0xD9 0x0639 # ARABIC LETTER AIN +0xDA 0x063A # ARABIC LETTER GHAIN +0xE0 0x0640 # ARABIC TATWEEL +0xE1 0x0641 # ARABIC LETTER FEH +0xE2 0x0642 # ARABIC LETTER QAF +0xE3 0x0643 # ARABIC LETTER KAF +0xE4 0x0644 # ARABIC LETTER LAM +0xE5 0x0645 # ARABIC LETTER MEEM +0xE6 0x0646 # ARABIC LETTER NOON +0xE7 0x0647 # ARABIC LETTER HEH +0xE8 0x0648 # ARABIC LETTER WAW +0xE9 0x0649 # ARABIC LETTER ALEF MAKSURA +0xEA 0x064A # ARABIC LETTER YEH +0xEB 0x064B # ARABIC FATHATAN +0xEC 0x064C # ARABIC DAMMATAN +0xED 0x064D # ARABIC KASRATAN +0xEE 0x064E # ARABIC FATHA +0xEF 0x064F # ARABIC DAMMA +0xF0 0x0650 # ARABIC KASRA +0xF1 0x0651 # ARABIC SHADDA +0xF2 0x0652 # ARABIC SUKUN diff --git a/codepage/iso8859-7.txt b/codepage/iso8859-7.txt new file mode 100644 index 0000000..bc46b74 --- /dev/null +++ b/codepage/iso8859-7.txt @@ -0,0 +1,308 @@ +# +# Name: ISO 8859-7:2003 to Unicode +# Unicode version: 4.0 +# Table version: 2.0 +# Table format: Format A +# Date: 2003-Nov-12 +# Authors: Ken Whistler +# +# Copyright (c) 1991-2003 Unicode, Inc. All Rights reserved. +# +# This file is provided as-is by Unicode, Inc. (The Unicode Consortium). +# No claims are made as to fitness for any particular purpose. No +# warranties of any kind are expressed or implied. The recipient +# agrees to determine applicability of information provided. If this +# file has been provided on optical media by Unicode, Inc., the sole +# remedy for any claim will be exchange of defective media within 90 +# days of receipt. +# +# Unicode, Inc. hereby grants the right to freely use the information +# supplied in this file in the creation of products supporting the +# Unicode Standard, and to make copies of this file in any form for +# internal or external distribution as long as this notice remains +# attached. +# +# General notes: +# +# This table contains the data the Unicode Consortium has on how +# ISO 8859-7:2003 characters map into Unicode. +# +# ISO 8859-7:1987 is equivalent to ISO-IR-126, ELOT 928, +# and ECMA 118. ISO 8859-7:2003 adds two currency signs +# and one other character not in the earlier standard. +# +# Format: Three tab-separated columns +# Column #1 is the ISO 8859-7 code (in hex as 0xXX) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 the Unicode name (follows a comment sign, '#') +# +# The entries are in ISO 8859-7 order. +# +# Version history +# 1.0 version updates 0.1 version by adding mappings for all +# control characters. +# Remap 0xA1 to U+2018 (instead of 0x02BD) to match text of 8859-7 +# Remap 0xA2 to U+2019 (instead of 0x02BC) to match text of 8859-7 +# +# 2.0 version updates 1.0 version by adding mappings for the +# three newly added characters 0xA4, 0xA5, 0xAA. +# +# Updated versions of this file may be found in: +# +# +# Any comments or problems, contact the Unicode Consortium at: +# +# +0x00 0x0000 # NULL +0x01 0x0001 # START OF HEADING +0x02 0x0002 # START OF TEXT +0x03 0x0003 # END OF TEXT +0x04 0x0004 # END OF TRANSMISSION +0x05 0x0005 # ENQUIRY +0x06 0x0006 # ACKNOWLEDGE +0x07 0x0007 # BELL +0x08 0x0008 # BACKSPACE +0x09 0x0009 # HORIZONTAL TABULATION +0x0A 0x000A # LINE FEED +0x0B 0x000B # VERTICAL TABULATION +0x0C 0x000C # FORM FEED +0x0D 0x000D # CARRIAGE RETURN +0x0E 0x000E # SHIFT OUT +0x0F 0x000F # SHIFT IN +0x10 0x0010 # DATA LINK ESCAPE +0x11 0x0011 # DEVICE CONTROL ONE +0x12 0x0012 # DEVICE CONTROL TWO +0x13 0x0013 # DEVICE CONTROL THREE +0x14 0x0014 # DEVICE CONTROL FOUR +0x15 0x0015 # NEGATIVE ACKNOWLEDGE +0x16 0x0016 # SYNCHRONOUS IDLE +0x17 0x0017 # END OF TRANSMISSION BLOCK +0x18 0x0018 # CANCEL +0x19 0x0019 # END OF MEDIUM +0x1A 0x001A # SUBSTITUTE +0x1B 0x001B # ESCAPE +0x1C 0x001C # FILE SEPARATOR +0x1D 0x001D # GROUP SEPARATOR +0x1E 0x001E # RECORD SEPARATOR +0x1F 0x001F # UNIT SEPARATOR +0x20 0x0020 # SPACE +0x21 0x0021 # EXCLAMATION MARK +0x22 0x0022 # QUOTATION MARK +0x23 0x0023 # NUMBER SIGN +0x24 0x0024 # DOLLAR SIGN +0x25 0x0025 # PERCENT SIGN +0x26 0x0026 # AMPERSAND +0x27 0x0027 # APOSTROPHE +0x28 0x0028 # LEFT PARENTHESIS +0x29 0x0029 # RIGHT PARENTHESIS +0x2A 0x002A # ASTERISK +0x2B 0x002B # PLUS SIGN +0x2C 0x002C # COMMA +0x2D 0x002D # HYPHEN-MINUS +0x2E 0x002E # FULL STOP +0x2F 0x002F # SOLIDUS +0x30 0x0030 # DIGIT ZERO +0x31 0x0031 # DIGIT ONE +0x32 0x0032 # DIGIT TWO +0x33 0x0033 # DIGIT THREE +0x34 0x0034 # DIGIT FOUR +0x35 0x0035 # DIGIT FIVE +0x36 0x0036 # DIGIT SIX +0x37 0x0037 # DIGIT SEVEN +0x38 0x0038 # DIGIT EIGHT +0x39 0x0039 # DIGIT NINE +0x3A 0x003A # COLON +0x3B 0x003B # SEMICOLON +0x3C 0x003C # LESS-THAN SIGN +0x3D 0x003D # EQUALS SIGN +0x3E 0x003E # GREATER-THAN SIGN +0x3F 0x003F # QUESTION MARK +0x40 0x0040 # COMMERCIAL AT +0x41 0x0041 # LATIN CAPITAL LETTER A +0x42 0x0042 # LATIN CAPITAL LETTER B +0x43 0x0043 # LATIN CAPITAL LETTER C +0x44 0x0044 # LATIN CAPITAL LETTER D +0x45 0x0045 # LATIN CAPITAL LETTER E +0x46 0x0046 # LATIN CAPITAL LETTER F +0x47 0x0047 # LATIN CAPITAL LETTER G +0x48 0x0048 # LATIN CAPITAL LETTER H +0x49 0x0049 # LATIN CAPITAL LETTER I +0x4A 0x004A # LATIN CAPITAL LETTER J +0x4B 0x004B # LATIN CAPITAL LETTER K +0x4C 0x004C # LATIN CAPITAL LETTER L +0x4D 0x004D # LATIN CAPITAL LETTER M +0x4E 0x004E # LATIN CAPITAL LETTER N +0x4F 0x004F # LATIN CAPITAL LETTER O +0x50 0x0050 # LATIN CAPITAL LETTER P +0x51 0x0051 # LATIN CAPITAL LETTER Q +0x52 0x0052 # LATIN CAPITAL LETTER R +0x53 0x0053 # LATIN CAPITAL LETTER S +0x54 0x0054 # LATIN CAPITAL LETTER T +0x55 0x0055 # LATIN CAPITAL LETTER U +0x56 0x0056 # LATIN CAPITAL LETTER V +0x57 0x0057 # LATIN CAPITAL LETTER W +0x58 0x0058 # LATIN CAPITAL LETTER X +0x59 0x0059 # LATIN CAPITAL LETTER Y +0x5A 0x005A # LATIN CAPITAL LETTER Z +0x5B 0x005B # LEFT SQUARE BRACKET +0x5C 0x005C # REVERSE SOLIDUS +0x5D 0x005D # RIGHT SQUARE BRACKET +0x5E 0x005E # CIRCUMFLEX ACCENT +0x5F 0x005F # LOW LINE +0x60 0x0060 # GRAVE ACCENT +0x61 0x0061 # LATIN SMALL LETTER A +0x62 0x0062 # LATIN SMALL LETTER B +0x63 0x0063 # LATIN SMALL LETTER C +0x64 0x0064 # LATIN SMALL LETTER D +0x65 0x0065 # LATIN SMALL LETTER E +0x66 0x0066 # LATIN SMALL LETTER F +0x67 0x0067 # LATIN SMALL LETTER G +0x68 0x0068 # LATIN SMALL LETTER H +0x69 0x0069 # LATIN SMALL LETTER I +0x6A 0x006A # LATIN SMALL LETTER J +0x6B 0x006B # LATIN SMALL LETTER K +0x6C 0x006C # LATIN SMALL LETTER L +0x6D 0x006D # LATIN SMALL LETTER M +0x6E 0x006E # LATIN SMALL LETTER N +0x6F 0x006F # LATIN SMALL LETTER O +0x70 0x0070 # LATIN SMALL LETTER P +0x71 0x0071 # LATIN SMALL LETTER Q +0x72 0x0072 # LATIN SMALL LETTER R +0x73 0x0073 # LATIN SMALL LETTER S +0x74 0x0074 # LATIN SMALL LETTER T +0x75 0x0075 # LATIN SMALL LETTER U +0x76 0x0076 # LATIN SMALL LETTER V +0x77 0x0077 # LATIN SMALL LETTER W +0x78 0x0078 # LATIN SMALL LETTER X +0x79 0x0079 # LATIN SMALL LETTER Y +0x7A 0x007A # LATIN SMALL LETTER Z +0x7B 0x007B # LEFT CURLY BRACKET +0x7C 0x007C # VERTICAL LINE +0x7D 0x007D # RIGHT CURLY BRACKET +0x7E 0x007E # TILDE +0x7F 0x007F # DELETE +0x80 0x0080 # +0x81 0x0081 # +0x82 0x0082 # +0x83 0x0083 # +0x84 0x0084 # +0x85 0x0085 # +0x86 0x0086 # +0x87 0x0087 # +0x88 0x0088 # +0x89 0x0089 # +0x8A 0x008A # +0x8B 0x008B # +0x8C 0x008C # +0x8D 0x008D # +0x8E 0x008E # +0x8F 0x008F # +0x90 0x0090 # +0x91 0x0091 # +0x92 0x0092 # +0x93 0x0093 # +0x94 0x0094 # +0x95 0x0095 # +0x96 0x0096 # +0x97 0x0097 # +0x98 0x0098 # +0x99 0x0099 # +0x9A 0x009A # +0x9B 0x009B # +0x9C 0x009C # +0x9D 0x009D # +0x9E 0x009E # +0x9F 0x009F # +0xA0 0x00A0 # NO-BREAK SPACE +0xA1 0x2018 # LEFT SINGLE QUOTATION MARK +0xA2 0x2019 # RIGHT SINGLE QUOTATION MARK +0xA3 0x00A3 # POUND SIGN +0xA4 0x20AC # EURO SIGN +0xA5 0x20AF # DRACHMA SIGN +0xA6 0x00A6 # BROKEN BAR +0xA7 0x00A7 # SECTION SIGN +0xA8 0x00A8 # DIAERESIS +0xA9 0x00A9 # COPYRIGHT SIGN +0xAA 0x037A # GREEK YPOGEGRAMMENI +0xAB 0x00AB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xAC 0x00AC # NOT SIGN +0xAD 0x00AD # SOFT HYPHEN +0xAF 0x2015 # HORIZONTAL BAR +0xB0 0x00B0 # DEGREE SIGN +0xB1 0x00B1 # PLUS-MINUS SIGN +0xB2 0x00B2 # SUPERSCRIPT TWO +0xB3 0x00B3 # SUPERSCRIPT THREE +0xB4 0x0384 # GREEK TONOS +0xB5 0x0385 # GREEK DIALYTIKA TONOS +0xB6 0x0386 # GREEK CAPITAL LETTER ALPHA WITH TONOS +0xB7 0x00B7 # MIDDLE DOT +0xB8 0x0388 # GREEK CAPITAL LETTER EPSILON WITH TONOS +0xB9 0x0389 # GREEK CAPITAL LETTER ETA WITH TONOS +0xBA 0x038A # GREEK CAPITAL LETTER IOTA WITH TONOS +0xBB 0x00BB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xBC 0x038C # GREEK CAPITAL LETTER OMICRON WITH TONOS +0xBD 0x00BD # VULGAR FRACTION ONE HALF +0xBE 0x038E # GREEK CAPITAL LETTER UPSILON WITH TONOS +0xBF 0x038F # GREEK CAPITAL LETTER OMEGA WITH TONOS +0xC0 0x0390 # GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS +0xC1 0x0391 # GREEK CAPITAL LETTER ALPHA +0xC2 0x0392 # GREEK CAPITAL LETTER BETA +0xC3 0x0393 # GREEK CAPITAL LETTER GAMMA +0xC4 0x0394 # GREEK CAPITAL LETTER DELTA +0xC5 0x0395 # GREEK CAPITAL LETTER EPSILON +0xC6 0x0396 # GREEK CAPITAL LETTER ZETA +0xC7 0x0397 # GREEK CAPITAL LETTER ETA +0xC8 0x0398 # GREEK CAPITAL LETTER THETA +0xC9 0x0399 # GREEK CAPITAL LETTER IOTA +0xCA 0x039A # GREEK CAPITAL LETTER KAPPA +0xCB 0x039B # GREEK CAPITAL LETTER LAMDA +0xCC 0x039C # GREEK CAPITAL LETTER MU +0xCD 0x039D # GREEK CAPITAL LETTER NU +0xCE 0x039E # GREEK CAPITAL LETTER XI +0xCF 0x039F # GREEK CAPITAL LETTER OMICRON +0xD0 0x03A0 # GREEK CAPITAL LETTER PI +0xD1 0x03A1 # GREEK CAPITAL LETTER RHO +0xD3 0x03A3 # GREEK CAPITAL LETTER SIGMA +0xD4 0x03A4 # GREEK CAPITAL LETTER TAU +0xD5 0x03A5 # GREEK CAPITAL LETTER UPSILON +0xD6 0x03A6 # GREEK CAPITAL LETTER PHI +0xD7 0x03A7 # GREEK CAPITAL LETTER CHI +0xD8 0x03A8 # GREEK CAPITAL LETTER PSI +0xD9 0x03A9 # GREEK CAPITAL LETTER OMEGA +0xDA 0x03AA # GREEK CAPITAL LETTER IOTA WITH DIALYTIKA +0xDB 0x03AB # GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA +0xDC 0x03AC # GREEK SMALL LETTER ALPHA WITH TONOS +0xDD 0x03AD # GREEK SMALL LETTER EPSILON WITH TONOS +0xDE 0x03AE # GREEK SMALL LETTER ETA WITH TONOS +0xDF 0x03AF # GREEK SMALL LETTER IOTA WITH TONOS +0xE0 0x03B0 # GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS +0xE1 0x03B1 # GREEK SMALL LETTER ALPHA +0xE2 0x03B2 # GREEK SMALL LETTER BETA +0xE3 0x03B3 # GREEK SMALL LETTER GAMMA +0xE4 0x03B4 # GREEK SMALL LETTER DELTA +0xE5 0x03B5 # GREEK SMALL LETTER EPSILON +0xE6 0x03B6 # GREEK SMALL LETTER ZETA +0xE7 0x03B7 # GREEK SMALL LETTER ETA +0xE8 0x03B8 # GREEK SMALL LETTER THETA +0xE9 0x03B9 # GREEK SMALL LETTER IOTA +0xEA 0x03BA # GREEK SMALL LETTER KAPPA +0xEB 0x03BB # GREEK SMALL LETTER LAMDA +0xEC 0x03BC # GREEK SMALL LETTER MU +0xED 0x03BD # GREEK SMALL LETTER NU +0xEE 0x03BE # GREEK SMALL LETTER XI +0xEF 0x03BF # GREEK SMALL LETTER OMICRON +0xF0 0x03C0 # GREEK SMALL LETTER PI +0xF1 0x03C1 # GREEK SMALL LETTER RHO +0xF2 0x03C2 # GREEK SMALL LETTER FINAL SIGMA +0xF3 0x03C3 # GREEK SMALL LETTER SIGMA +0xF4 0x03C4 # GREEK SMALL LETTER TAU +0xF5 0x03C5 # GREEK SMALL LETTER UPSILON +0xF6 0x03C6 # GREEK SMALL LETTER PHI +0xF7 0x03C7 # GREEK SMALL LETTER CHI +0xF8 0x03C8 # GREEK SMALL LETTER PSI +0xF9 0x03C9 # GREEK SMALL LETTER OMEGA +0xFA 0x03CA # GREEK SMALL LETTER IOTA WITH DIALYTIKA +0xFB 0x03CB # GREEK SMALL LETTER UPSILON WITH DIALYTIKA +0xFC 0x03CC # GREEK SMALL LETTER OMICRON WITH TONOS +0xFD 0x03CD # GREEK SMALL LETTER UPSILON WITH TONOS +0xFE 0x03CE # GREEK SMALL LETTER OMEGA WITH TONOS diff --git a/codepage/iso8859-8.txt b/codepage/iso8859-8.txt new file mode 100644 index 0000000..bc8da4c --- /dev/null +++ b/codepage/iso8859-8.txt @@ -0,0 +1,270 @@ +# +# Name: ISO/IEC 8859-8:1999 to Unicode +# Unicode version: 3.0 +# Table version: 1.1 +# Table format: Format A +# Date: 2000-Jan-03 +# Authors: Ken Whistler +# +# Copyright (c) 1991-1999 Unicode, Inc. All Rights reserved. +# +# This file is provided as-is by Unicode, Inc. (The Unicode Consortium). +# No claims are made as to fitness for any particular purpose. No +# warranties of any kind are expressed or implied. The recipient +# agrees to determine applicability of information provided. If this +# file has been provided on optical media by Unicode, Inc., the sole +# remedy for any claim will be exchange of defective media within 90 +# days of receipt. +# +# Unicode, Inc. hereby grants the right to freely use the information +# supplied in this file in the creation of products supporting the +# Unicode Standard, and to make copies of this file in any form for +# internal or external distribution as long as this notice remains +# attached. +# +# General notes: +# +# This table contains the data the Unicode Consortium has on how +# ISO/IEC 8859-8:1999 characters map into Unicode. +# +# Format: Three tab-separated columns +# Column #1 is the ISO/IEC 8859-8 code (in hex as 0xXX) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 the Unicode name (follows a comment sign, '#') +# +# The entries are in ISO/IEC 8859-8 order. +# +# Version history +# 1.0 version updates 0.1 version by adding mappings for all +# control characters. +# 1.1 version updates to the published 8859-8:1999, correcting +# the mapping of 0xAF and adding mappings for LRM and RLM. +# +# Updated versions of this file may be found in: +# +# +# Any comments or problems, contact +# Please note that is an archival address; +# notices will be checked, but do not expect an immediate response. +# +0x00 0x0000 # NULL +0x01 0x0001 # START OF HEADING +0x02 0x0002 # START OF TEXT +0x03 0x0003 # END OF TEXT +0x04 0x0004 # END OF TRANSMISSION +0x05 0x0005 # ENQUIRY +0x06 0x0006 # ACKNOWLEDGE +0x07 0x0007 # BELL +0x08 0x0008 # BACKSPACE +0x09 0x0009 # HORIZONTAL TABULATION +0x0A 0x000A # LINE FEED +0x0B 0x000B # VERTICAL TABULATION +0x0C 0x000C # FORM FEED +0x0D 0x000D # CARRIAGE RETURN +0x0E 0x000E # SHIFT OUT +0x0F 0x000F # SHIFT IN +0x10 0x0010 # DATA LINK ESCAPE +0x11 0x0011 # DEVICE CONTROL ONE +0x12 0x0012 # DEVICE CONTROL TWO +0x13 0x0013 # DEVICE CONTROL THREE +0x14 0x0014 # DEVICE CONTROL FOUR +0x15 0x0015 # NEGATIVE ACKNOWLEDGE +0x16 0x0016 # SYNCHRONOUS IDLE +0x17 0x0017 # END OF TRANSMISSION BLOCK +0x18 0x0018 # CANCEL +0x19 0x0019 # END OF MEDIUM +0x1A 0x001A # SUBSTITUTE +0x1B 0x001B # ESCAPE +0x1C 0x001C # FILE SEPARATOR +0x1D 0x001D # GROUP SEPARATOR +0x1E 0x001E # RECORD SEPARATOR +0x1F 0x001F # UNIT SEPARATOR +0x20 0x0020 # SPACE +0x21 0x0021 # EXCLAMATION MARK +0x22 0x0022 # QUOTATION MARK +0x23 0x0023 # NUMBER SIGN +0x24 0x0024 # DOLLAR SIGN +0x25 0x0025 # PERCENT SIGN +0x26 0x0026 # AMPERSAND +0x27 0x0027 # APOSTROPHE +0x28 0x0028 # LEFT PARENTHESIS +0x29 0x0029 # RIGHT PARENTHESIS +0x2A 0x002A # ASTERISK +0x2B 0x002B # PLUS SIGN +0x2C 0x002C # COMMA +0x2D 0x002D # HYPHEN-MINUS +0x2E 0x002E # FULL STOP +0x2F 0x002F # SOLIDUS +0x30 0x0030 # DIGIT ZERO +0x31 0x0031 # DIGIT ONE +0x32 0x0032 # DIGIT TWO +0x33 0x0033 # DIGIT THREE +0x34 0x0034 # DIGIT FOUR +0x35 0x0035 # DIGIT FIVE +0x36 0x0036 # DIGIT SIX +0x37 0x0037 # DIGIT SEVEN +0x38 0x0038 # DIGIT EIGHT +0x39 0x0039 # DIGIT NINE +0x3A 0x003A # COLON +0x3B 0x003B # SEMICOLON +0x3C 0x003C # LESS-THAN SIGN +0x3D 0x003D # EQUALS SIGN +0x3E 0x003E # GREATER-THAN SIGN +0x3F 0x003F # QUESTION MARK +0x40 0x0040 # COMMERCIAL AT +0x41 0x0041 # LATIN CAPITAL LETTER A +0x42 0x0042 # LATIN CAPITAL LETTER B +0x43 0x0043 # LATIN CAPITAL LETTER C +0x44 0x0044 # LATIN CAPITAL LETTER D +0x45 0x0045 # LATIN CAPITAL LETTER E +0x46 0x0046 # LATIN CAPITAL LETTER F +0x47 0x0047 # LATIN CAPITAL LETTER G +0x48 0x0048 # LATIN CAPITAL LETTER H +0x49 0x0049 # LATIN CAPITAL LETTER I +0x4A 0x004A # LATIN CAPITAL LETTER J +0x4B 0x004B # LATIN CAPITAL LETTER K +0x4C 0x004C # LATIN CAPITAL LETTER L +0x4D 0x004D # LATIN CAPITAL LETTER M +0x4E 0x004E # LATIN CAPITAL LETTER N +0x4F 0x004F # LATIN CAPITAL LETTER O +0x50 0x0050 # LATIN CAPITAL LETTER P +0x51 0x0051 # LATIN CAPITAL LETTER Q +0x52 0x0052 # LATIN CAPITAL LETTER R +0x53 0x0053 # LATIN CAPITAL LETTER S +0x54 0x0054 # LATIN CAPITAL LETTER T +0x55 0x0055 # LATIN CAPITAL LETTER U +0x56 0x0056 # LATIN CAPITAL LETTER V +0x57 0x0057 # LATIN CAPITAL LETTER W +0x58 0x0058 # LATIN CAPITAL LETTER X +0x59 0x0059 # LATIN CAPITAL LETTER Y +0x5A 0x005A # LATIN CAPITAL LETTER Z +0x5B 0x005B # LEFT SQUARE BRACKET +0x5C 0x005C # REVERSE SOLIDUS +0x5D 0x005D # RIGHT SQUARE BRACKET +0x5E 0x005E # CIRCUMFLEX ACCENT +0x5F 0x005F # LOW LINE +0x60 0x0060 # GRAVE ACCENT +0x61 0x0061 # LATIN SMALL LETTER A +0x62 0x0062 # LATIN SMALL LETTER B +0x63 0x0063 # LATIN SMALL LETTER C +0x64 0x0064 # LATIN SMALL LETTER D +0x65 0x0065 # LATIN SMALL LETTER E +0x66 0x0066 # LATIN SMALL LETTER F +0x67 0x0067 # LATIN SMALL LETTER G +0x68 0x0068 # LATIN SMALL LETTER H +0x69 0x0069 # LATIN SMALL LETTER I +0x6A 0x006A # LATIN SMALL LETTER J +0x6B 0x006B # LATIN SMALL LETTER K +0x6C 0x006C # LATIN SMALL LETTER L +0x6D 0x006D # LATIN SMALL LETTER M +0x6E 0x006E # LATIN SMALL LETTER N +0x6F 0x006F # LATIN SMALL LETTER O +0x70 0x0070 # LATIN SMALL LETTER P +0x71 0x0071 # LATIN SMALL LETTER Q +0x72 0x0072 # LATIN SMALL LETTER R +0x73 0x0073 # LATIN SMALL LETTER S +0x74 0x0074 # LATIN SMALL LETTER T +0x75 0x0075 # LATIN SMALL LETTER U +0x76 0x0076 # LATIN SMALL LETTER V +0x77 0x0077 # LATIN SMALL LETTER W +0x78 0x0078 # LATIN SMALL LETTER X +0x79 0x0079 # LATIN SMALL LETTER Y +0x7A 0x007A # LATIN SMALL LETTER Z +0x7B 0x007B # LEFT CURLY BRACKET +0x7C 0x007C # VERTICAL LINE +0x7D 0x007D # RIGHT CURLY BRACKET +0x7E 0x007E # TILDE +0x7F 0x007F # DELETE +0x80 0x0080 # +0x81 0x0081 # +0x82 0x0082 # +0x83 0x0083 # +0x84 0x0084 # +0x85 0x0085 # +0x86 0x0086 # +0x87 0x0087 # +0x88 0x0088 # +0x89 0x0089 # +0x8A 0x008A # +0x8B 0x008B # +0x8C 0x008C # +0x8D 0x008D # +0x8E 0x008E # +0x8F 0x008F # +0x90 0x0090 # +0x91 0x0091 # +0x92 0x0092 # +0x93 0x0093 # +0x94 0x0094 # +0x95 0x0095 # +0x96 0x0096 # +0x97 0x0097 # +0x98 0x0098 # +0x99 0x0099 # +0x9A 0x009A # +0x9B 0x009B # +0x9C 0x009C # +0x9D 0x009D # +0x9E 0x009E # +0x9F 0x009F # +0xA0 0x00A0 # NO-BREAK SPACE +0xA2 0x00A2 # CENT SIGN +0xA3 0x00A3 # POUND SIGN +0xA4 0x00A4 # CURRENCY SIGN +0xA5 0x00A5 # YEN SIGN +0xA6 0x00A6 # BROKEN BAR +0xA7 0x00A7 # SECTION SIGN +0xA8 0x00A8 # DIAERESIS +0xA9 0x00A9 # COPYRIGHT SIGN +0xAA 0x00D7 # MULTIPLICATION SIGN +0xAB 0x00AB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xAC 0x00AC # NOT SIGN +0xAD 0x00AD # SOFT HYPHEN +0xAE 0x00AE # REGISTERED SIGN +0xAF 0x00AF # MACRON +0xB0 0x00B0 # DEGREE SIGN +0xB1 0x00B1 # PLUS-MINUS SIGN +0xB2 0x00B2 # SUPERSCRIPT TWO +0xB3 0x00B3 # SUPERSCRIPT THREE +0xB4 0x00B4 # ACUTE ACCENT +0xB5 0x00B5 # MICRO SIGN +0xB6 0x00B6 # PILCROW SIGN +0xB7 0x00B7 # MIDDLE DOT +0xB8 0x00B8 # CEDILLA +0xB9 0x00B9 # SUPERSCRIPT ONE +0xBA 0x00F7 # DIVISION SIGN +0xBB 0x00BB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xBC 0x00BC # VULGAR FRACTION ONE QUARTER +0xBD 0x00BD # VULGAR FRACTION ONE HALF +0xBE 0x00BE # VULGAR FRACTION THREE QUARTERS +0xDF 0x2017 # DOUBLE LOW LINE +0xE0 0x05D0 # HEBREW LETTER ALEF +0xE1 0x05D1 # HEBREW LETTER BET +0xE2 0x05D2 # HEBREW LETTER GIMEL +0xE3 0x05D3 # HEBREW LETTER DALET +0xE4 0x05D4 # HEBREW LETTER HE +0xE5 0x05D5 # HEBREW LETTER VAV +0xE6 0x05D6 # HEBREW LETTER ZAYIN +0xE7 0x05D7 # HEBREW LETTER HET +0xE8 0x05D8 # HEBREW LETTER TET +0xE9 0x05D9 # HEBREW LETTER YOD +0xEA 0x05DA # HEBREW LETTER FINAL KAF +0xEB 0x05DB # HEBREW LETTER KAF +0xEC 0x05DC # HEBREW LETTER LAMED +0xED 0x05DD # HEBREW LETTER FINAL MEM +0xEE 0x05DE # HEBREW LETTER MEM +0xEF 0x05DF # HEBREW LETTER FINAL NUN +0xF0 0x05E0 # HEBREW LETTER NUN +0xF1 0x05E1 # HEBREW LETTER SAMEKH +0xF2 0x05E2 # HEBREW LETTER AYIN +0xF3 0x05E3 # HEBREW LETTER FINAL PE +0xF4 0x05E4 # HEBREW LETTER PE +0xF5 0x05E5 # HEBREW LETTER FINAL TSADI +0xF6 0x05E6 # HEBREW LETTER TSADI +0xF7 0x05E7 # HEBREW LETTER QOF +0xF8 0x05E8 # HEBREW LETTER RESH +0xF9 0x05E9 # HEBREW LETTER SHIN +0xFA 0x05EA # HEBREW LETTER TAV +0xFD 0x200E # LEFT-TO-RIGHT MARK +0xFE 0x200F # RIGHT-TO-LEFT MARK + diff --git a/codepage/iso8859-9.txt b/codepage/iso8859-9.txt new file mode 100644 index 0000000..22901f1 --- /dev/null +++ b/codepage/iso8859-9.txt @@ -0,0 +1,307 @@ +# +# Name: ISO/IEC 8859-9:1999 to Unicode +# Unicode version: 3.0 +# Table version: 1.0 +# Table format: Format A +# Date: 1999 July 27 +# Authors: Ken Whistler +# +# Copyright (c) 1991-1999 Unicode, Inc. All Rights reserved. +# +# This file is provided as-is by Unicode, Inc. (The Unicode Consortium). +# No claims are made as to fitness for any particular purpose. No +# warranties of any kind are expressed or implied. The recipient +# agrees to determine applicability of information provided. If this +# file has been provided on magnetic media by Unicode, Inc., the sole +# remedy for any claim will be exchange of defective media within 90 +# days of receipt. +# +# Unicode, Inc. hereby grants the right to freely use the information +# supplied in this file in the creation of products supporting the +# Unicode Standard, and to make copies of this file in any form for +# internal or external distribution as long as this notice remains +# attached. +# +# General notes: +# +# This table contains the data the Unicode Consortium has on how +# ISO/IEC 8859-9:1999 characters map into Unicode. +# +# Format: Three tab-separated columns +# Column #1 is the ISO/IEC 8859-9 code (in hex as 0xXX) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 the Unicode name (follows a comment sign, '#') +# +# The entries are in ISO/IEC 8859-9 order. +# +# ISO/IEC 8859-9 is also equivalent to ISO-IR-148. +# +# Version history +# 1.0 version updates 0.1 version by adding mappings for all +# control characters. +# +# Updated versions of this file may be found in: +# +# +# Any comments or problems, contact +# Please note that is an archival address; +# notices will be checked, but do not expect an immediate response. +# +0x00 0x0000 # NULL +0x01 0x0001 # START OF HEADING +0x02 0x0002 # START OF TEXT +0x03 0x0003 # END OF TEXT +0x04 0x0004 # END OF TRANSMISSION +0x05 0x0005 # ENQUIRY +0x06 0x0006 # ACKNOWLEDGE +0x07 0x0007 # BELL +0x08 0x0008 # BACKSPACE +0x09 0x0009 # HORIZONTAL TABULATION +0x0A 0x000A # LINE FEED +0x0B 0x000B # VERTICAL TABULATION +0x0C 0x000C # FORM FEED +0x0D 0x000D # CARRIAGE RETURN +0x0E 0x000E # SHIFT OUT +0x0F 0x000F # SHIFT IN +0x10 0x0010 # DATA LINK ESCAPE +0x11 0x0011 # DEVICE CONTROL ONE +0x12 0x0012 # DEVICE CONTROL TWO +0x13 0x0013 # DEVICE CONTROL THREE +0x14 0x0014 # DEVICE CONTROL FOUR +0x15 0x0015 # NEGATIVE ACKNOWLEDGE +0x16 0x0016 # SYNCHRONOUS IDLE +0x17 0x0017 # END OF TRANSMISSION BLOCK +0x18 0x0018 # CANCEL +0x19 0x0019 # END OF MEDIUM +0x1A 0x001A # SUBSTITUTE +0x1B 0x001B # ESCAPE +0x1C 0x001C # FILE SEPARATOR +0x1D 0x001D # GROUP SEPARATOR +0x1E 0x001E # RECORD SEPARATOR +0x1F 0x001F # UNIT SEPARATOR +0x20 0x0020 # SPACE +0x21 0x0021 # EXCLAMATION MARK +0x22 0x0022 # QUOTATION MARK +0x23 0x0023 # NUMBER SIGN +0x24 0x0024 # DOLLAR SIGN +0x25 0x0025 # PERCENT SIGN +0x26 0x0026 # AMPERSAND +0x27 0x0027 # APOSTROPHE +0x28 0x0028 # LEFT PARENTHESIS +0x29 0x0029 # RIGHT PARENTHESIS +0x2A 0x002A # ASTERISK +0x2B 0x002B # PLUS SIGN +0x2C 0x002C # COMMA +0x2D 0x002D # HYPHEN-MINUS +0x2E 0x002E # FULL STOP +0x2F 0x002F # SOLIDUS +0x30 0x0030 # DIGIT ZERO +0x31 0x0031 # DIGIT ONE +0x32 0x0032 # DIGIT TWO +0x33 0x0033 # DIGIT THREE +0x34 0x0034 # DIGIT FOUR +0x35 0x0035 # DIGIT FIVE +0x36 0x0036 # DIGIT SIX +0x37 0x0037 # DIGIT SEVEN +0x38 0x0038 # DIGIT EIGHT +0x39 0x0039 # DIGIT NINE +0x3A 0x003A # COLON +0x3B 0x003B # SEMICOLON +0x3C 0x003C # LESS-THAN SIGN +0x3D 0x003D # EQUALS SIGN +0x3E 0x003E # GREATER-THAN SIGN +0x3F 0x003F # QUESTION MARK +0x40 0x0040 # COMMERCIAL AT +0x41 0x0041 # LATIN CAPITAL LETTER A +0x42 0x0042 # LATIN CAPITAL LETTER B +0x43 0x0043 # LATIN CAPITAL LETTER C +0x44 0x0044 # LATIN CAPITAL LETTER D +0x45 0x0045 # LATIN CAPITAL LETTER E +0x46 0x0046 # LATIN CAPITAL LETTER F +0x47 0x0047 # LATIN CAPITAL LETTER G +0x48 0x0048 # LATIN CAPITAL LETTER H +0x49 0x0049 # LATIN CAPITAL LETTER I +0x4A 0x004A # LATIN CAPITAL LETTER J +0x4B 0x004B # LATIN CAPITAL LETTER K +0x4C 0x004C # LATIN CAPITAL LETTER L +0x4D 0x004D # LATIN CAPITAL LETTER M +0x4E 0x004E # LATIN CAPITAL LETTER N +0x4F 0x004F # LATIN CAPITAL LETTER O +0x50 0x0050 # LATIN CAPITAL LETTER P +0x51 0x0051 # LATIN CAPITAL LETTER Q +0x52 0x0052 # LATIN CAPITAL LETTER R +0x53 0x0053 # LATIN CAPITAL LETTER S +0x54 0x0054 # LATIN CAPITAL LETTER T +0x55 0x0055 # LATIN CAPITAL LETTER U +0x56 0x0056 # LATIN CAPITAL LETTER V +0x57 0x0057 # LATIN CAPITAL LETTER W +0x58 0x0058 # LATIN CAPITAL LETTER X +0x59 0x0059 # LATIN CAPITAL LETTER Y +0x5A 0x005A # LATIN CAPITAL LETTER Z +0x5B 0x005B # LEFT SQUARE BRACKET +0x5C 0x005C # REVERSE SOLIDUS +0x5D 0x005D # RIGHT SQUARE BRACKET +0x5E 0x005E # CIRCUMFLEX ACCENT +0x5F 0x005F # LOW LINE +0x60 0x0060 # GRAVE ACCENT +0x61 0x0061 # LATIN SMALL LETTER A +0x62 0x0062 # LATIN SMALL LETTER B +0x63 0x0063 # LATIN SMALL LETTER C +0x64 0x0064 # LATIN SMALL LETTER D +0x65 0x0065 # LATIN SMALL LETTER E +0x66 0x0066 # LATIN SMALL LETTER F +0x67 0x0067 # LATIN SMALL LETTER G +0x68 0x0068 # LATIN SMALL LETTER H +0x69 0x0069 # LATIN SMALL LETTER I +0x6A 0x006A # LATIN SMALL LETTER J +0x6B 0x006B # LATIN SMALL LETTER K +0x6C 0x006C # LATIN SMALL LETTER L +0x6D 0x006D # LATIN SMALL LETTER M +0x6E 0x006E # LATIN SMALL LETTER N +0x6F 0x006F # LATIN SMALL LETTER O +0x70 0x0070 # LATIN SMALL LETTER P +0x71 0x0071 # LATIN SMALL LETTER Q +0x72 0x0072 # LATIN SMALL LETTER R +0x73 0x0073 # LATIN SMALL LETTER S +0x74 0x0074 # LATIN SMALL LETTER T +0x75 0x0075 # LATIN SMALL LETTER U +0x76 0x0076 # LATIN SMALL LETTER V +0x77 0x0077 # LATIN SMALL LETTER W +0x78 0x0078 # LATIN SMALL LETTER X +0x79 0x0079 # LATIN SMALL LETTER Y +0x7A 0x007A # LATIN SMALL LETTER Z +0x7B 0x007B # LEFT CURLY BRACKET +0x7C 0x007C # VERTICAL LINE +0x7D 0x007D # RIGHT CURLY BRACKET +0x7E 0x007E # TILDE +0x7F 0x007F # DELETE +0x80 0x0080 # +0x81 0x0081 # +0x82 0x0082 # +0x83 0x0083 # +0x84 0x0084 # +0x85 0x0085 # +0x86 0x0086 # +0x87 0x0087 # +0x88 0x0088 # +0x89 0x0089 # +0x8A 0x008A # +0x8B 0x008B # +0x8C 0x008C # +0x8D 0x008D # +0x8E 0x008E # +0x8F 0x008F # +0x90 0x0090 # +0x91 0x0091 # +0x92 0x0092 # +0x93 0x0093 # +0x94 0x0094 # +0x95 0x0095 # +0x96 0x0096 # +0x97 0x0097 # +0x98 0x0098 # +0x99 0x0099 # +0x9A 0x009A # +0x9B 0x009B # +0x9C 0x009C # +0x9D 0x009D # +0x9E 0x009E # +0x9F 0x009F # +0xA0 0x00A0 # NO-BREAK SPACE +0xA1 0x00A1 # INVERTED EXCLAMATION MARK +0xA2 0x00A2 # CENT SIGN +0xA3 0x00A3 # POUND SIGN +0xA4 0x00A4 # CURRENCY SIGN +0xA5 0x00A5 # YEN SIGN +0xA6 0x00A6 # BROKEN BAR +0xA7 0x00A7 # SECTION SIGN +0xA8 0x00A8 # DIAERESIS +0xA9 0x00A9 # COPYRIGHT SIGN +0xAA 0x00AA # FEMININE ORDINAL INDICATOR +0xAB 0x00AB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xAC 0x00AC # NOT SIGN +0xAD 0x00AD # SOFT HYPHEN +0xAE 0x00AE # REGISTERED SIGN +0xAF 0x00AF # MACRON +0xB0 0x00B0 # DEGREE SIGN +0xB1 0x00B1 # PLUS-MINUS SIGN +0xB2 0x00B2 # SUPERSCRIPT TWO +0xB3 0x00B3 # SUPERSCRIPT THREE +0xB4 0x00B4 # ACUTE ACCENT +0xB5 0x00B5 # MICRO SIGN +0xB6 0x00B6 # PILCROW SIGN +0xB7 0x00B7 # MIDDLE DOT +0xB8 0x00B8 # CEDILLA +0xB9 0x00B9 # SUPERSCRIPT ONE +0xBA 0x00BA # MASCULINE ORDINAL INDICATOR +0xBB 0x00BB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xBC 0x00BC # VULGAR FRACTION ONE QUARTER +0xBD 0x00BD # VULGAR FRACTION ONE HALF +0xBE 0x00BE # VULGAR FRACTION THREE QUARTERS +0xBF 0x00BF # INVERTED QUESTION MARK +0xC0 0x00C0 # LATIN CAPITAL LETTER A WITH GRAVE +0xC1 0x00C1 # LATIN CAPITAL LETTER A WITH ACUTE +0xC2 0x00C2 # LATIN CAPITAL LETTER A WITH CIRCUMFLEX +0xC3 0x00C3 # LATIN CAPITAL LETTER A WITH TILDE +0xC4 0x00C4 # LATIN CAPITAL LETTER A WITH DIAERESIS +0xC5 0x00C5 # LATIN CAPITAL LETTER A WITH RING ABOVE +0xC6 0x00C6 # LATIN CAPITAL LETTER AE +0xC7 0x00C7 # LATIN CAPITAL LETTER C WITH CEDILLA +0xC8 0x00C8 # LATIN CAPITAL LETTER E WITH GRAVE +0xC9 0x00C9 # LATIN CAPITAL LETTER E WITH ACUTE +0xCA 0x00CA # LATIN CAPITAL LETTER E WITH CIRCUMFLEX +0xCB 0x00CB # LATIN CAPITAL LETTER E WITH DIAERESIS +0xCC 0x00CC # LATIN CAPITAL LETTER I WITH GRAVE +0xCD 0x00CD # LATIN CAPITAL LETTER I WITH ACUTE +0xCE 0x00CE # LATIN CAPITAL LETTER I WITH CIRCUMFLEX +0xCF 0x00CF # LATIN CAPITAL LETTER I WITH DIAERESIS +0xD0 0x011E # LATIN CAPITAL LETTER G WITH BREVE +0xD1 0x00D1 # LATIN CAPITAL LETTER N WITH TILDE +0xD2 0x00D2 # LATIN CAPITAL LETTER O WITH GRAVE +0xD3 0x00D3 # LATIN CAPITAL LETTER O WITH ACUTE +0xD4 0x00D4 # LATIN CAPITAL LETTER O WITH CIRCUMFLEX +0xD5 0x00D5 # LATIN CAPITAL LETTER O WITH TILDE +0xD6 0x00D6 # LATIN CAPITAL LETTER O WITH DIAERESIS +0xD7 0x00D7 # MULTIPLICATION SIGN +0xD8 0x00D8 # LATIN CAPITAL LETTER O WITH STROKE +0xD9 0x00D9 # LATIN CAPITAL LETTER U WITH GRAVE +0xDA 0x00DA # LATIN CAPITAL LETTER U WITH ACUTE +0xDB 0x00DB # LATIN CAPITAL LETTER U WITH CIRCUMFLEX +0xDC 0x00DC # LATIN CAPITAL LETTER U WITH DIAERESIS +0xDD 0x0130 # LATIN CAPITAL LETTER I WITH DOT ABOVE +0xDE 0x015E # LATIN CAPITAL LETTER S WITH CEDILLA +0xDF 0x00DF # LATIN SMALL LETTER SHARP S +0xE0 0x00E0 # LATIN SMALL LETTER A WITH GRAVE +0xE1 0x00E1 # LATIN SMALL LETTER A WITH ACUTE +0xE2 0x00E2 # LATIN SMALL LETTER A WITH CIRCUMFLEX +0xE3 0x00E3 # LATIN SMALL LETTER A WITH TILDE +0xE4 0x00E4 # LATIN SMALL LETTER A WITH DIAERESIS +0xE5 0x00E5 # LATIN SMALL LETTER A WITH RING ABOVE +0xE6 0x00E6 # LATIN SMALL LETTER AE +0xE7 0x00E7 # LATIN SMALL LETTER C WITH CEDILLA +0xE8 0x00E8 # LATIN SMALL LETTER E WITH GRAVE +0xE9 0x00E9 # LATIN SMALL LETTER E WITH ACUTE +0xEA 0x00EA # LATIN SMALL LETTER E WITH CIRCUMFLEX +0xEB 0x00EB # LATIN SMALL LETTER E WITH DIAERESIS +0xEC 0x00EC # LATIN SMALL LETTER I WITH GRAVE +0xED 0x00ED # LATIN SMALL LETTER I WITH ACUTE +0xEE 0x00EE # LATIN SMALL LETTER I WITH CIRCUMFLEX +0xEF 0x00EF # LATIN SMALL LETTER I WITH DIAERESIS +0xF0 0x011F # LATIN SMALL LETTER G WITH BREVE +0xF1 0x00F1 # LATIN SMALL LETTER N WITH TILDE +0xF2 0x00F2 # LATIN SMALL LETTER O WITH GRAVE +0xF3 0x00F3 # LATIN SMALL LETTER O WITH ACUTE +0xF4 0x00F4 # LATIN SMALL LETTER O WITH CIRCUMFLEX +0xF5 0x00F5 # LATIN SMALL LETTER O WITH TILDE +0xF6 0x00F6 # LATIN SMALL LETTER O WITH DIAERESIS +0xF7 0x00F7 # DIVISION SIGN +0xF8 0x00F8 # LATIN SMALL LETTER O WITH STROKE +0xF9 0x00F9 # LATIN SMALL LETTER U WITH GRAVE +0xFA 0x00FA # LATIN SMALL LETTER U WITH ACUTE +0xFB 0x00FB # LATIN SMALL LETTER U WITH CIRCUMFLEX +0xFC 0x00FC # LATIN SMALL LETTER U WITH DIAERESIS +0xFD 0x0131 # LATIN SMALL LETTER DOTLESS I +0xFE 0x015F # LATIN SMALL LETTER S WITH CEDILLA +0xFF 0x00FF # LATIN SMALL LETTER Y WITH DIAERESIS + + diff --git a/com32/LICENCE b/com32/LICENCE new file mode 100644 index 0000000..7eec786 --- /dev/null +++ b/com32/LICENCE @@ -0,0 +1,32 @@ +libcom32 and libutil are licensed under the MIT license: + +## ----------------------------------------------------------------------- +## +## Copyright 2004-2009 H. Peter Anvin - All Rights Reserved +## Portions Copyright 2009 Intel Corporation; author: H. Peter Anvin +## +## Permission is hereby granted, free of charge, to any person +## obtaining a copy of this software and associated documentation +## files (the "Software"), to deal in the Software without +## restriction, including without limitation the rights to use, +## copy, modify, merge, publish, distribute, sublicense, and/or +## sell copies of the Software, and to permit persons to whom +## the Software is furnished to do so, subject to the following +## conditions: +## +## The above copyright notice and this permission notice shall +## be included in all copies or substantial portions of the Software. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +## OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +## HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +## WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +## FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +## OTHER DEALINGS IN THE SOFTWARE. +## +## ----------------------------------------------------------------------- + +The files in the sample, modules and libgpl directories are mostly under the +GNU GPL (see the file COPYING in the directory above.) diff --git a/com32/MCONFIG b/com32/MCONFIG new file mode 100644 index 0000000..f8d9d97 --- /dev/null +++ b/com32/MCONFIG @@ -0,0 +1,83 @@ +## -*- makefile -*- ------------------------------------------------------- +## +## Copyright 2008-2009 H. Peter Anvin - All Rights Reserved +## Copyright 2009 Intel Corporation; author: H. Peter Anvin +## +## 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, Inc., 51 Franklin St, Fifth Floor, +## Boston MA 02110-1301, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +## +## COM32 common configurables +## + +include $(topdir)/MCONFIG + +GCCOPT := $(call gcc_ok,-std=gnu99,) \ + $(call gcc_ok,-m32,) \ + $(call gcc_ok,-fno-stack-protector,) \ + -mregparm=3 -DREGPARM=3 -march=i386 -Os + +com32 = $(topdir)/com32 + +ifneq ($(NOGPL),1) +GPLLIB = $(com32)/gpllib/libcom32gpl.a +GPLINCLUDE = -I$(com32)/gplinclude +else +GPLLIB = +GPLINCLUDE = +endif + +CFLAGS = $(GCCOPT) -W -Wall -march=i386 \ + -fomit-frame-pointer -D__COM32__ \ + -nostdinc -iwithprefix include \ + -I$(com32)/libutil/include -I$(com32)/include $(GPLINCLUDE) +SFLAGS = $(GCCOPT) -W -Wall -march=i386 \ + -fomit-frame-pointer -D__COM32__ \ + -nostdinc -iwithprefix include \ + -I$(com32)/libutil/include -I$(com32)/include $(GPLINCLUDE) + +COM32LD = $(com32)/lib/com32.ld +LDFLAGS = -m elf_i386 -T $(COM32LD) +LIBGCC := $(shell $(CC) $(GCCOPT) --print-libgcc) + +LNXCFLAGS = -I$(com32)/libutil/include -W -Wall -O -g -D_GNU_SOURCE +LNXSFLAGS = -g +LNXLDFLAGS = -g + +C_LIBS = $(com32)/libutil/libutil_com.a $(GPLLIB) \ + $(com32)/lib/libcom32.a $(LIBGCC) +C_LNXLIBS = $(com32)/libutil/libutil_lnx.a + +.SUFFIXES: .lss .c .lo .o .elf .c32 .lnx + +.PRECIOUS: %.o +%.o: %.S + $(CC) $(MAKEDEPS) $(SFLAGS) -c -o $@ $< + +.PRECIOUS: %.o +%.o: %.c + $(CC) $(MAKEDEPS) $(CFLAGS) -c -o $@ $< + +.PRECIOUS: %.elf +%.elf: %.o $(LIBS) $(C_LIBS) $(COM32LD) + $(LD) $(LDFLAGS) -o $@ $(filter-out $(COM32LD),$^) + +.PRECIOUS: %.lo +%.lo: %.S + $(CC) $(MAKEDEPS) $(LNXSFLAGS) -c -o $@ $< + +.PRECIOUS: %.lo +%.lo: %.c + $(CC) $(MAKEDEPS) $(LNXCFLAGS) -c -o $@ $< + +.PRECIOUS: %.lnx +%.lnx: %.lo $(LNXLIBS) $(C_LNXLIBS) + $(CC) $(LNXCFLAGS) -o $@ $^ + +%.c32: %.elf + $(OBJCOPY) -O binary $< $@ diff --git a/com32/Makefile b/com32/Makefile new file mode 100644 index 0000000..4a58485 --- /dev/null +++ b/com32/Makefile @@ -0,0 +1,3 @@ +SUBDIRS = lib gpllib libutil modules mboot menu samples rosh cmenu hdt +all tidy dist clean spotless install: + set -e; for d in $(SUBDIRS); do $(MAKE) -C $$d $@; done diff --git a/com32/cmenu/.gitignore b/com32/cmenu/.gitignore new file mode 100644 index 0000000..4add14b --- /dev/null +++ b/com32/cmenu/.gitignore @@ -0,0 +1,2 @@ +test.c +test2.c diff --git a/com32/cmenu/CHANGES b/com32/cmenu/CHANGES new file mode 100644 index 0000000..cce2183 --- /dev/null +++ b/com32/cmenu/CHANGES @@ -0,0 +1,32 @@ +Changes in v1.2 +--------------- +* Allowed menu's to have names. Submenu's can be referred to by names + instead of their index in the menu system. This allows user to refer + to submenus which are not yet part of the menusystem. +* help pages can be longer than one screen +* menugen.py: Python script for converting .menu files to C source code + .menu files can be used to descibe most static uses of menu system, + including SubMenus, Checkboxes, RadioButtons, User Authentication and + Context sensitive help. You can also restrict use of certain items + to users with certain credentials. + + See MENU_FORMAT for the format of .menu files + +* display.c32: Takes the name of the text file to display and displays it + allowing user to scroll through the text. + + USAGE: display.c32 + + must be an absolute filename (including the /isolinux part) + +Changes in v1.1 +--------------- +* Additional handler type: Keys handler +* Menuitem handlers now have a return value of type t_handler_return. + For all simple cases, you just return ACTION_VALID or ACTION_INVALID + (For some types of menu items, handlers are ignored, and for + others the return value is ignored) +* add_menu takes an extra argument (to better control memory footprint) + You can just set it to -1 to choose the default value +* Now the menu system support long menu's using scroll bars. +* Support for passwords and context sensitive help is added. diff --git a/com32/cmenu/HISTORY b/com32/cmenu/HISTORY new file mode 100644 index 0000000..8e9beb3 --- /dev/null +++ b/com32/cmenu/HISTORY @@ -0,0 +1,20 @@ + +GCC & 32-bit code +----------------- +Due to the limitations of the COM file format, +(64KB limit on memory footprint) the code has been changed +so that the code compiles to a 32-bit COMBOOT program. +Since the code makes use of BIOS calls, this code cannot be +compiled into a format which can execute under Linux. As a +side effect, there is no nice way to debug this code. In order +to debug this code, you will have to run the code under syslinux. + +GCC & 16-bit code +----------------- +The code was ported to GCC by Peter Anvin. + +OpenWatcom & 16-bit code +------------------------ +Originally this code was written for the Openwatcom compiler +and generated .COM files, which could execute under DOS as well as +SYSLINUX. diff --git a/com32/cmenu/MANUAL b/com32/cmenu/MANUAL new file mode 100644 index 0000000..4e70149 --- /dev/null +++ b/com32/cmenu/MANUAL @@ -0,0 +1,348 @@ + Overview of writing code using the menu system + ---------------------------------------------- + +This file contains implementation and developer documentation. +For simple cases, you should start by using simple.c as a template. +complex.c illustrates most of the features available in the menu system. + +Menu Features currently supported are: +* menu items, +* submenus, +* disabled items, +* checkboxes, +* invisible items (useful for dynamic menus), and +* Radio menus, +* Context sensitive help +* Authenticated users + +The keys used are: + +* Arrow Keys, PgUp, PgDn, Home, End Keys +* Space to switch state of a checkbox +* Enter to choose the item +* Escape to exit from it +* Shortcut keys + +1. Overview +----------- + +The code usually consists of many stages. + + * Configuring the menusytem + * Installing global handlers [optional] + * Populating the menusystem + * Executing the menusystem + * Processing the result + +1.1 Configuring the menusystem +------------------------------ +This includes setting the window the menu system should use, +the choice of colors, the title of the menu etc. In most functions +calls, a value of -1 indicates that the default value be used. +For details about what the arguments are look at function +declarations in menu.h + + + // Choose the default title and setup default values for all attributes.... + init_menusystem(NULL); + set_window_size(1,1,23,78); // Leave one row/col border all around + + // Choose the default values for all attributes and char's + // -1 means choose defaults (Actually the next 4 lines are not needed) + set_normal_attr (-1,-1,-1,-1); + set_status_info (-1,-1); + set_title_info (-1,-1); + set_misc_info(-1,-1,-1,-1); + + +1.2 Populating the menusystem +----------------------------- +This involves adding a menu to the system, and the options which +should appear in the menu. An example is given below. + + + MAINMENU = add_menu(" Menu Title ",-1); + CHECKED = 1; + add_item("option1","Status 1",OPT_RUN,"kernel1 arg1=val1",0); + add_item("selfloop","Status 2",OPT_SUBMENU,NULL,MAINMENU); + add_item("othermenu","Status 3",OPT_SUBMENU,"menuname",0); + add_sep(); + add_item("checkbox,"Checkbox Info",OPT_CHECKBOX,NULL,CHECKED); + add_item("Exit ","Status String",OPT_EXITMENU,NULL,0); + + +The call to add_menu has two arguments, the first being the title of +the menu and the second an upper bound on the number of items in the menu. +Putting a -1, will use the default (see MENUSIZE in menu.h). If you try +to add more items than specified, the extra items will not appear in +the menu. The accuracy of this number affects the memory required +to run the system. + +If you do not want to keep track of the return values, you can also use +the following variant of add_menu + + +add_named_menu("main"," Menu Title ",-1) + + +This creates a new menu as before and gives it a name "main". When using named +menus, you get an alternate way for adding submenu's. See below for details. + +The call to add_item has five arguments. +The first argument is the text which appears in the menu itself. +The second argument is the text displayed in the status line. +The third argument indicates the type of this menuitem. It is one of +the following + + * OPT_RUN : executable content + * OPT_EXITMENU : exits menu to parent + * OPT_SUBMENU : if selected, displays a submenu + * OPT_CHECKBOX : associates a boolean with this item which can be toggled + * OPT_RADIOMENU: associates this with a radio menu. + After execution, the data field of this item will point + to the option selected. + * OPT_SEP : A menu seperator (visually divide menu into parts) + * OPT_RADIOITEM: this item is one of the options in a RADIOMENU + * OPT_INACTIVE : A disabled item (user cannot select this) + * OPT_INVISIBLE: This item will not be displayed. + +The fourth argument is the value of the data field always a string. +Usually this string is just copied and nothing is done with it. Two +cases, where it is used. + +In case of a radiomenu the input string is ignored and the "data" field +points to the menuitem chosen (Dont forget to typecast this pointer to +(t_menuitem *) when reading this info). + +In case of a submenu, this string if non-trivial is interpreted as the +name of the submenu which should be linked there. This interpretation +happens when the menu is first run and not when the menu system is being +created. This allows the user to create the menusystem in an arbitrary +order. + + +The fifth argument is a number whose meaning depends on the type of the +item. For a CHECKBOX it should be 0/1 setting the initial state of the +checkbox. For a SUBMENU it should be the index of the menu which should +be displayed if this option is chosen. Incase the data field is non-trivial, +this number is ignored and computed later. For a RADIOMENU it should be the +index of the menu which contains all the options (All items in that menu +not of type RADIOITEM are ignored). For all other types, this +argument has no meaning at all. + +A call to add_sep is a convenient shorthand for calling add_item +with the type set to OPT_SEP. + +1.3 Executing the menusystem +---------------------------- +This is the simplest of all. Just call showmenus, with the index +of the main menu as its argument. It returns a pointer to the menu +item which was selected by the user. + + + choice = showmenus(MAIN); // Initial menu is the one with index MAIN + // or choice = showmenus(find_menu_num("main")); // Initial menu is the one named "main" + + +1.4 Processing the result +------------------------- +This pointer will either be NULL (user hit Escape) or always point +to a menuitem which can be "executed", i.e. it will be of type OPT_RUN. +Usually at this point, all we need to do is to ask syslinux to run +the command associated with this menuitem. The following code executes +the command stored in choice->data (there is no other use for the data +field, except for radiomenu's) + + + if (choice) + { + if (choice->action == OPT_RUN) + { + if (syslinux) runcommand(choice->data); + else csprint(choice->data,0x07); + return 1; + } + csprint("Error in programming!",0x07); + } + + +2. Advanced features +-------------------- +Everycall to add_item actually returns a pointer to the menuitem +created. This can be useful when using any of the advanced features. + +2.1 extra_data +-------------- +For example, every menuitem has an "extra_data" field (a pointer) +which the user can use to point any data he/she pleases. The menusystem +itself does not use this field in anyway. + +2.2 helpid +---------- +Every item also has a field called "helpid". It is meant to hold some +kind of identifier which can be referenced and used to generate +a context sensitive help system. This can be set after a call to +add_item as follows + + add_item("selfloop","Status 2",OPT_SUBMENU,NULL,MAINMENU); + set_item_options('A',4516); + + +The first is the shortcut key for this entry. You can put -1 to ensure +that the shortcut key is not reset. The second is some unsigned integer. +If this value is 0xFFFF, then the helpid is not changed. + +2.3 Installing global handlers +------------------------------ +It is possible to register handlers for the menu system. These are +user functions which are called by the menusystem in certain +situations. Usually the handlers get a pointer to the menusystem +datastructure as well as a pointer to the current item selected. +Some handlers may get additional information. Some handlers are +required to return values while others are not required to do so. + +Currently the menusystem support three types of global handlers +* timeout handler +* screen handler +* keys handler + +2.3.1 timeout handler +--------------------- +This is installed using a call to "reg_ontimeout(fn,numsteps,stepsize)" +function. fn is a pointer to a function which takes no arguments and +returns one of CODE_WAIT, CODE_ENTER, CODE_ESCAPE. This function is +called when numsteps*stepsize Centiseconds have gone by without any +user input. If the function returns CODE_WAIT then the menusystem +waits for user input (for another numsteps*stepsize Centiseconds). If +CODE_ENTER or CODE_ESCAPE is returned, then the system pretends that +the user hit ENTER or ESCAPE on the keyboard and acts accordingly. + +2.3.2 Screen handler +-------------------- +This is installed using a call to "reg_handler(HDLR_SCREEN,fn)". fn is +a pointer to a function which takes a pointer to the menusystem +datastructure and the current item selected and returns nothing. +This is called everytime a menu is drawn (i.e. everytime user changes +the current selection). This is meant for displaying any additional +information which reflects the current state of the system. + +2.3.3 Keys handler +------------------ +This is installed using a call to "reg_handler(HDLR_KEYS,fn)". fn is +a pointer to a function which takes a pointer to the menusystem +datastructure, the current item and the scan code of a key and returns +nothing. This function is called when the user presses a key which +the menusystem does not know to dealwith. In any case, when this call +returns the screen should not have changed in any way. Usually, +one can change the active page and display any output needed and +reset the active page when you return from this call. + +complex.c implements a key_handler, which implements a simple +context sensitive help system, by displaying the contents of a +file whose name is based on the helpid of the active item. + +Also, complex.c's handler allows certain users to make changes +to edit the commands associated with a menu item. + +2.4 Installing item level handlers +---------------------------------- +In addition to global handlers, one can also install handlers for each +individual item. A handler for an individual item is a function which +takes a pointer to the menusystem datastructure and a pointer to the +current item and return a structure of type t_handler_return. Currently +it has two bit fields "valid" and "refresh". + +This handler is called when the user hits "enter" on a RUN item, or +changes the status of a CHECKBOX, or called *after* a radio menu choice +has been set. In all other cases, installing a handler has no effect. + +The handler can change any of the internal datastructures it pleases. +For e.g. in a radiomenu handler, one can change the text displayed +on the menuitem depending on which choice was selected (see complex.c +for an example). The return values are ignored for RADIOMENU's. + +In case of RUN items: the return values are used as follows. If the +return value of "valid" was false, then this user choice is ignored. +This is useful if the handler has useful side effects. For e.g. +complex.c has a Login item, whose handler always return INVALID. It +sets a global variable to the name of the user logged in, and enables +some menu items, and makes some invisible items visible. + +* If the handler does not change the visibility status of any items, + the handler should set "refresh" to 0. +* If the handler changes the visibility status of items in the current + menu set "refresh" to 1. +* If you are changing the visibility status of items in menu's currently + not displayed, then you can set "refresh" to 0. +* Changing the visibility status of items in another menu + which is currently displayed, is not supported. If you do it, + the screen contents may not reflect the change until you get to the + menu which was changed. When you do get to that menu, you may notice + pieces of the old menu still on the screen. + +In case of CHECKBOXES: the return value of "valid" is ignored. Because, +the handler can change the value of checkbox if the user selected value +is not appropriate. only the value of "refresh" is honored. In this case +all the caveats in the previous paragraph apply. + +menu.h defines two instances of t_handler_return +ACTION_VALID and ACTION_INVALID for common use. These set the valid flag +to 1 and 0 respectively and the refresh flag to 0. + +3. Things to look out for +------------------------- +When you define the menu system, always declare it in the opposite +order, i.e. all lower level menu's should be defined before the higher +level menus. This is because in order to define the MAINMENU, you need +to know the index assigned to all its submenus. + +4. Additional Modules +--------------------- +You can make use of the following additional modules, in writing your +handlers. + +* Passwords +* Help + +4.1 Passwords +------------- +This module was written by Th. Gebhardt. This is basically a modification +of the DES crypt function obtained by removing the dependence of the +original crypt function on C libraries. The following functions are +defined + + init_passwords(PWDFILE) + // Read in the password database from the file + authenticate_user(user,pwd) + // Checks if user,pwd is valid + isallowed(user,perm) + // Checks if the user has a specified permission + close_passwords() + // Unloads password database from memory + + See the sample password file for more details about the file format + and the implementation of permissions. + +See complex.c for a example of how to use this. + +4.2 Help +-------- +This can be used to set up a context sensitive help system. The following +functions are defined + + init_help(HELPBASEDIR) + // Initialises the help system. All help files will be loaded + // from the directory specified. + runhelpsystem(context) + // Displays the contents of HELPBASEDIR/hlp.txt + +In order to have a functioning help system, you just need to create +the hlp.txt files and initialize the help system by specifying +the base directory. + +The first line of this file assumed to be the title of the help screen. +You can use ^N and ^O to change attributes absolutely and relatively, +i.e. [^O]46 (i.e. Ctrl-O followed by chars 4 and 6) will set the +attribute to 46, while [^N]08 will XOR the current attribute with +specified number, thus in this case the first [^N]08 will turn on +highlighting and the second one will turn it off. diff --git a/com32/cmenu/MENU_FORMAT b/com32/cmenu/MENU_FORMAT new file mode 100644 index 0000000..24cb02f --- /dev/null +++ b/com32/cmenu/MENU_FORMAT @@ -0,0 +1,262 @@ +A .menu file can be used to describe basic menu structures which can be converted +into C code which can then be compiled into a .c32 file for use with SYSLINUX. +The format of a .menu file is similar to an ini file, but with important +differences. + +Lines starting with # and ; are treated as comments. Blank lines are used to +separate the attributes of one menu item from another. Multiple blank lines are +equivalent to a single one. In other contexts Blank lines are not significant. + +Menus +----- +Each menu declaration starts with a line containing the name of menu in [ ]. +This is the "nickname" of the menu and should be different for different menus. +This is not visible to the user of the menu system. The initial menu must +be called "main" + +The menu declaration is followed by lines which set the attributes of the menu. +This is followed by a blank line and followed by declaration of menu items in +that menu. + +All lines which occur before the first menu declaration is considered as +a global declaration. + +Abstract Format +--------------- + +The overall format should look like this + +-------------------------------------------------------- + + +[menuname1] + + + + + +... + + + +[menuname2] + + + + + + + +---------------------------------------------------------- + +GLOBAL SETTINGS +--------------- +The following global settings are now supported. Many of the keywords +accept what we call a "DOT COMMAND" as argument. Simply put they are +instructions to the menu system to perform certain actions. +The syntax and semantics of DOT COMMANDS are given later in the section +titled "DOT COMMANDS". + +videomode: (default 0xFF) [Use with care] + The textmode in which the whole menu system should operate. + Must be a number (use 0x notation for hexadecimal). + Lookup Ralph Brown Interrupt List and search for Video Mode + to find a number to put here. + + setting to 0xFF will mean, menu system will use the current + video mode. + +title: + The title of the whole menu system + +top, left, bot, right: (default 0,0,21,79) + The area of the screen used by the menu system. The remaining + part of the screen can be used by the user for anything. + +helpdir: (default /isolinux/help) + Location of the directory where help information is stored. The + help files must be called "hlpNNNNN.txt" where NNNNN is the helpid. + +pwdfile: (default /isolinux/passwd) + The name of the password file which contains user, password and permissions + See "passwd" file for details regarding format of this file + +editrow: (default 23) + the row on the screen where one can edit the command line. This must + be outside the menu area. Set this to a negative number to disable + editing the command line. In case of authenticated users, the current + user must have "editcmd" permissions to edit the command line + +pwdrow: (default 23) + The row on the screen used for user authentication. Must be outside + menu area (can be same as editrow). Set to negative to disable + user authentication + +skipif: (default 0) + The OR of the bits in the Shift-flags any of which can cause the menu system + to be skipped all together (0 means menu system always runs). It can also + be a combination of "Alt","Ctrl","Shift","Caps","Ins","Scroll". + When menu system starts it checks if any of the specified keys are On/pressed. + If true, the system exits immediately and executes the skipcmd. + + e.g. setting it to "shift-alt-caps" means menu will be skipped if alt OR shift + is pressed OR caps is on. setting to "0" means menu will always run. + +skipcmd: (default .exit) + valid terminal commands: .exit .ignore or any syslinux command + command to execute if menu system is skipped. This must be a non-trivial + syslinux command if skipcondn is not "0". ".exit" means menu system + quits back to the boot prompt. + +startfile: (default "") + if non-empty the system will display the contents of this file before launching + the menusystem. This happens only if the menusystem is not skipped. Can be used + to display licensing, usage or welcome messages. A file with given name + is expected to be found in the helpdir directory. + +exitcmd: (default .exit) + valid terminal commands: .exit .repeat or any syslinux command + The default command to execute when user quits the menu system. + +exitcmdroot: (default =exitcmd) + Same as exitcmd except applies when current user has "root" privileges. If not + specified, it is assumed to be the same as exitcmd + +timeout: (default 3000) + The amount of time (in multiple of 0.1 seconds) to wait for user keypress. If no + key pressed for specified duration then the timeoutcmd is executed. + +totaltimeout: (default 0) + The total amount of time (in multiples of 0.1 seconds) the system will wait for + user to make a decision. If no decision has been made in the specified duration + totaltimeoutcmd will be executed + + NOTE: This does not include the time spent browsing the help system or + the time taken for the user to enter his/her authentication credentials. + +timeoutcmd: (default .beep) + valid terminal commands: .wait .enter .escape or any syslinux command + command to execute when we timeout waiting for user input. The commands + .enter and .escape tell the menu system to pretend the user typed ENTER or + ESCAPE on the keyboard, while .wait tells the menusystem to wait for one + more time period + +totaltimeoutcmd: (default .wait) + choices are the same as for timeoutcmd + +MENU SETTINGS +------------- + +title: (must be specified) + Title of this menu + +row,col: [Usage not recomended] + position in screen where this menu should be placed. By default the + system will choose an appropriate location. + +ITEM ATTRIBUTES +--------------- + +item: + The string displayed to the user. Characters enclosed in < > are highlighted. + +shortcut: (default -1) valid values A-Za-z0-9 or -1 [Usage not recommended] + Sets the shortcut key for this item. If set to -1, the system scans for the first + highlighted letter in the given range and sets that as the shortcut key. + +info: (default same as data) + Additional textual information displayed in the status bar + +type: + the type of entry this item represents. This is one of the following: + + run: choosing this will run something in SYSLINUX + use data to specify the actual command to execute + exitmenu: exit to parent menu + submenu: choosing will open up submenu + use data to specify the "nickname" of the menu + which should come here + sep: Position a separator here + inactive: menu item is disabled + checkbox: this is a checkbox + use state to set initial state + invisible: User does not see this item + radioitem: One choice in a radiomenu + radiomenu: Allow user to choose one of many choices + (initial choice is always NULL) + login: Selecting this will allow user to login to system + +data: + for run items, the syslinux command to execute + for submenus and radiomenus, nickname of menu + for checkboxes, string to be added to kernel command line (if set) + for radioitems, string to be added to kernel command line (if chosen) + +ipappend: + ipappend flag to pass to PXELINUX (harmless for other variants of SYSLINUX) + See syslinux documentation for meaning of the FLAGS + +helpid: (default 65535 which is not a valid id) + associates a context for the help system. + +state: (default 0) + Initial state of a checkbox (for other items this has no meaning) + +perms: (default "") + string containing the name of the permission which user must + have to activate this item. For eg. if this item is a submenu + then user needs the permission in order to open the submenu + +argsmenu: (default "") + Name of the menu to be scanned for setting additional arguments to + pass to command line when this item is chosen for execution. Submenus + of specified menu are also scanned. Only checkboxes and radiomenu's + are scanned. Items of other type in this menu is silently ignored. + + +DOT COMMANDS +------------ +Dot commands are basically instructions to the menu system to do certain things. + +A "single command" is one of the following + +[NT] A syslinux command (any DOT command not starting with a "." is assumed to be this) +[NT] .beep [n] +[NT] .help +[NT] .nop +[T] .exit or .quit (equivalent) +[T] .repeat or .wait or .ignore (all three are equivalent) + +A dot command is a sequence of "single commands" separated by a "%". When a dot command +is executed the system executes all the given "single commands" in the specified order. +All the commands marked "[T]" are terminal commands, i.e. when the system encounters +such a command it stops processing the dot command and returns the terminal command +which caused the termination to the caller (who usually interprets the command +appropriately). + +All commands marked with [NT] are non-terminal commands, i.e. once these commands are +processed the system continues to process the remaining "single commands" specified in +the "DOT COMMAND". + +Note: The case of a syslinux command is tricky. When executed, the command should never return +(if the specified kernel exists) so the fact that we consider it a [NT] should be taken with +a pinch of salt. However, if the syslinux command does return (in case of no kernel), then +remaining "single commands" are processed. In particular "ker1 arg1 % ker2 arg2 % ker3 args" +has the effect of executing the first kernel which exists + +.nop: + Does nothing. +.beep: + .beep [n] produces a beep n times. n must be between 0 and 9. If not specified n=1. + (hence .beep 0 is equivalent to .nop) +.help: + .help + Displays the help file which is assumed to be in the "help" directory. Its name + does not have to be in the form "hlpNNNNN.txt" (as required by the context sensitive help). + Executing this command will mean the appropriate help screen is displayed till the user hits + ESCAPE + +The meaning of the Terminal commands can vary with the context in which it is used. For example, +a ".enter" or ".escape" does not have any meaning in the "onerrorcmd" context but it has a meaning +in the "ontimeout" context. In case the user gives a Terminal command which does not make sense, it +is upto the code (in this case adv_menu.tpl) to do what it pleases. diff --git a/com32/cmenu/Makefile b/com32/cmenu/Makefile new file mode 100644 index 0000000..0ede58d --- /dev/null +++ b/com32/cmenu/Makefile @@ -0,0 +1,83 @@ +## ----------------------------------------------------------------------- +## +## Copyright 2001-2009 H. Peter Anvin - All Rights Reserved +## Copyright 2009 Intel Corporation; author: H. Peter Anvin +## +## 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +## +## Makefile for the complex menu system +## + +NOGPL := 1 + +# This must be defined before MCONFIG is included +LIBS = libmenu/libmenu.a + +topdir = ../.. +include ../MCONFIG + +CFLAGS += -I./libmenu + +LIBMENU = libmenu/syslnx.o libmenu/com32io.o libmenu/tui.o \ + libmenu/menu.o libmenu/passwords.o libmenu/des.o libmenu/help.o + +CMENUS = $(patsubst %.c,%.c32,$(wildcard *.c)) +IMENUS = $(patsubst %.menu,%.c32,$(wildcard *.menu)) + +MENUS = $(CMENUS) $(IMENUS) + +.SUFFIXES: .S .c .o .elf .c32 .menu + +.PRECIOUS: %.c +%.c: %.menu adv_menu.tpl + python menugen.py --input=$< --output=$@ --template=adv_menu.tpl + +.PRECIOUS: %.o +%.o: %.S + $(CC) $(MAKEDEPS) $(SFLAGS) -c -o $@ $< + +.PRECIOUS: %.o +%.o: %.c %.h + $(CC) $(MAKEDEPS) $(CFLAGS) -c -o $@ $< + +.PRECIOUS: %.elf +%.elf: %.o libmenu/libmenu.a $(LIBS) + $(LD) $(LDFLAGS) -o $@ $^ + +%.c32: %.elf + $(OBJCOPY) -O binary $< $@ + +all: menus + +libmenu/libmenu.a: $(LIBMENU) + -rm -f $@ + $(AR) cq $@ $^ + $(RANLIB) $@ + +tidy dist: + rm -f *.o *.lo *.a *.lst *.elf .*.d */.*.d + +libclean: + rm -f libmenu/*.o libmenu/*.a + +clean: tidy menuclean libclean + rm -f *.lss *.c32 *.com + +menuclean: + rm -f $(patsubst %.menu,%.c,$(wildcard *.menu)) + +spotless: clean libclean menuclean + rm -f *~ \#* + +menus: $(MENUS) + +install: # Don't install samples + +-include .*.d */.*.d diff --git a/com32/cmenu/README b/com32/cmenu/README new file mode 100644 index 0000000..d585d2f --- /dev/null +++ b/com32/cmenu/README @@ -0,0 +1,95 @@ + Text User Interface using comboot + --------------------------------- + +This is a menu system written by Murali Krishnan Ganapathy and ported +from OpenWatcom to gcc by HPA. It is currently being maintained by the +original author. + +To configure the menus, you need to set up a menu configuration file +to have the menu items you desire, then build the menu system using +make. You can use either simple.c or complex.c as a starting point +for your own menu configuration file; If your menu system is only going +to have entries corresponding to things which can be executed directly, +then you can create a file in ".menu" format instead of the C code. + +See MENU_FORMAT for the syntax of .menu files + +The resulting code is a 32-bit COMBOOT code, and hence can be executed +only under syslinux. You can use tools like bochs to help debug your +code. + +Menu Features currently supported are: +* menu items, +* submenus, +* disabled items, +* checkboxes, +* invisible items (useful for dynamic menus), and +* Radio menus, +* Context sensitive help +* Authenticated users +* Editing commands associated with items + +The keys used are: + +* Arrow Keys, PgUp, PgDn, Home, End Keys +* Space to switch state of a checkbox +* Enter to choose the item +* Escape to exit from it +* Shortcut keys + +Features +-------- +This is a general purpose menu system implemented using only BIOS calls, +so it can be executed in a COMBOOT environment as well. It is highly +customizable. Some features include: + +* Status line + Display any help information associated with each menu item. +* Window + Specify a window within which the menu system draws all its menu's. + It is upto the user to ensure that the menu's fit within the window. +* Positioning submenus + By default, each submenu is positioned just below the corresponding + entry of the parent menu. However, the user may position each menu + at a specific location of his choice. This is useful, when the menu's + have lots of options. +* Registering handlers for each menu item + This is mainly used for checkboxes and radiomenu's, where a selection may + result in disabling other menu items/checkboxes +* Global Screen Handler + This is called every time the menu is redrawn. The user can display + additional information (usually outside the window where the menu is + being displayed). See the complex.c for an example, where the global + handler is used to display the choices made so far. +* Global Keys Handler + This is called every time the user presses a key which the menu + system does not understand. This can be used to display context + sensitive help. See complex.c for how to use this hook to implement + a context sensitive help system as well as "On the fly" editing + of commands associated with menus. +* Shortcut Keys + With each item one can register a shortcut key from [A-Za-z0-9]. + Pressing a key within that range, will take you to the next item + with that shortcut key (so you can have multiple items with the + same shortcut key). The default shortcut key for each item, is + the lower case version of the first char of the item in the range + [A-Za-z0-9]. +* Escape Keys + Each item entry can have a substring enclosed in < and >. This part + is highlighted. Can be used to highlight the shortcut keys. By default + if an item has a <, then the first char inside < and > in the range + [A-Za-z0-9] is converted to lower case and set as the shortcut key. +* Ontimeout handler + The user can register an ontimeout handler, which gets called if + no key has been pressed for a user specific amount of time (default 5 min). + For an example see the complex.c file. + +Credits +------- +* The Watcom developers and Peter Anvin for figuring out an OS + independent startup code. +* Thomas for porting the crypt function and removing all C library + dependencies +* Peter Anvin for porting the code to GCC + +- Murali (gmurali+guicd@cs.uchicago.edu) diff --git a/com32/cmenu/TODO b/com32/cmenu/TODO new file mode 100644 index 0000000..d2ee82c --- /dev/null +++ b/com32/cmenu/TODO @@ -0,0 +1,2 @@ +* Write COMBOOT code to read .menu files and parse them directly + - take the name of menu file to parse from commandline diff --git a/com32/cmenu/adv_menu.tpl b/com32/cmenu/adv_menu.tpl new file mode 100644 index 0000000..6ce4acf --- /dev/null +++ b/com32/cmenu/adv_menu.tpl @@ -0,0 +1,452 @@ +All the lines in this file not in between --something BEGINS-- and --something ENDS-- +is ignored completely and will not make it into the generated C file + +This file has sections of C code each section delimited by --secname BEGINS-- +and --secname ENDS--. In the generated C code certain section may be used multiple +times. Currently the different section which must be defined are + +header, system, item, login and footer + +Any additional sections you define will be processed but will probably not make it +to the C code if you do not modify menugen.py to use it. + +header and footer go through unmolested. The remaining are % substituted using +python rules. Basically it means %(var)s gets replaced by the value of the variable +"var" which is a processed form of what is read from the .menu file + +NOTE: There is absolutely no C code in the python script, so you are free to +modify this template to suit your needs + +--header BEGINS-- +/* -*- c -*- ------------------------------------------------------------- * + * + * Copyright 2004-2006 Murali Krishnan Ganapathy - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +#include "menu.h" +#include "help.h" +#include "passwords.h" +#include "com32io.h" +#include +#include + +#define MAX_CMD_LINE_LENGTH 514 + +typedef struct s_xtra { + long ipappend; // Stores the ipappend flag to send (useful for PXELINUX only) + char *argsmenu; // Stores the name of menu which contains options for the given RUN item + char *perms; // stores the permissions required to activate the item +} t_xtra; + +typedef t_xtra *pt_xtra; // Pointer to extra datastructure + +// Types of dot commands for which caller takes responsibility of handling +// In some case some commands may not make sense, it is up to the caller +// to handle cases which do not make sense +typedef enum {QUIT_CMD, REPEAT_CMD, ENTER_CMD, ESCAPE_CMD} t_dotcmd; + + +/*----------------- Global Variables */ + +// default user +#define GUEST_USER "guest" + +// for local commands. return value of execdotcmd +#define QUIT_CMD 0 +#define RPT_CMD 1 + +char username[12]; // Name of user currently using the system + +int PWD_ROW; // Line number where user authentication happens +int EDIT_ROW; // row where User Tab + +char loginstr[] = "ogin "; +char logoutstr[30]; + +int vmode; // The video mode we want to be in +char timeoutcmd[MAX_CMD_LINE_LENGTH]; // Command to execute on timeout +char totaltimeoutcmd[MAX_CMD_LINE_LENGTH]; // Command to execute on totaltimeout + +char QUITSTR[] = ".quit"; // same as exit +char IGNORESTR[]=".ignore"; // same as repeat, wait + +/*---------------- End globals */ + +// returns pointer to first non-space char +// and advances end of str by removing trailing spaces +char * strip(char *str) +{ + char *p,*s,*e; + if (!str) return NULL; + p = str; + s = NULL; + e = NULL; + while (*p) { + if (*p != ' ') { + // mark start of string or record the last visited non-space char + if (!s) s=p; else e=p; + } + p++; + } + *(++e)='\0'; // kill string earlier + return s; +} + +// executes a list of % separated commands +// non-dot commands are assumed to be syslinux commands +// All syslinux commands are appended with the contents of kerargs +// If it fails (kernel not found) then the next one is tried in the +// list +// returns QUIT_CMD or RPT_CMD +t_dotcmd execdotcmd(const char *cmd, char *defcmd, const char *kerargs) +{ + char cmdline[MAX_CMD_LINE_LENGTH]; + char dotcmd[MAX_CMD_LINE_LENGTH]; + char *curr,*next,*p,*args; + char ctr; + + strcpy(dotcmd,cmd); + next = dotcmd; + cmdline[0] = '\0'; + while (*next) { // if something to do + curr = next; + p = strchr(next,'%'); + if (p) { + *p--='\0'; next=p+2; + while (*p == ' ') p--; + *(++p)='\0'; // remove trailing spaces + } else { + if (*defcmd) { // execute defcmd next + next=defcmd; + defcmd=NULL; // exec def cmd only once + } else next=NULL; + } + // now we just need to execute the command "curr" + curr = strip(curr); + if (curr[0] != '.') { // just run the kernel + strcpy(cmdline,curr); + if (kerargs) strcat(cmdline,kerargs); + runsyslinuximage(cmdline,0); // No IPAppend + } else { // We have a DOT command + // split command into command and args (may be empty) + args = curr; + while ( (*args != ' ') && (*args != '\0') ) args++; + if (*args) { // found a space + *args++ = '\0'; + while (*args == ' ') args++; // skip over spaces + } + if ( (strcmp(curr,".exit")==0) || + (strcmp(curr,".quit")==0) + ) + return QUIT_CMD; + if ( (strcmp(curr,".repeat")==0) || + (strcmp(curr,".ignore")==0) || + (strcmp(curr,".wait")==0) + ) + return RPT_CMD; + if (strcmp(curr,".beep")==0) { + if ((args) && ('0' <= args[0]) && (args[0] <= '9')) + ctr = args[0]-'0'; + else ctr=1; + for (;ctr>0; ctr--) beep(); + } + if (strcmp(curr,".help")==0) runhelp(args); + } + } + return RPT_CMD; // by default we do not quit +} + + +TIMEOUTCODE timeout(const char *cmd) +{ + t_dotcmd c; + c = execdotcmd(cmd,".wait",NULL); + switch(c) { + case ENTER_CMD: + return CODE_ENTER; + case ESCAPE_CMD: + return CODE_ESCAPE; + default: + return CODE_WAIT; + } +} + +TIMEOUTCODE ontimeout() +{ + return timeout(timeoutcmd); +} + +TIMEOUTCODE ontotaltimeout() +{ + return timeout(totaltimeoutcmd); +} + +void keys_handler(t_menusystem *ms, t_menuitem *mi,unsigned int scancode) +{ + char nc; + + if ( ((scancode >> 8) == F1) && (mi->helpid != 0xFFFF) ) { // If scancode of F1 and non-trivial helpid + runhelpsystem(mi->helpid); + } + + // If user hit TAB, and item is an "executable" item + // and user has privileges to edit it, edit it in place. + if (((scancode & 0xFF) == 0x09) && (mi->action == OPT_RUN) && + (EDIT_ROW < getnumrows()) && (EDIT_ROW > 0) && + (isallowed(username,"editcmd") || isallowed(username,"root"))) { + nc = getnumcols(); + // User typed TAB and has permissions to edit command line + gotoxy(EDIT_ROW,1,ms->menupage); + csprint("Command line:",0x07); + editstring(mi->data,ACTIONLEN); + gotoxy(EDIT_ROW,1,ms->menupage); + cprint(' ',0x07,nc-1,ms->menupage); + } +} + +t_handler_return login_handler(t_menusystem *ms, t_menuitem *mi) +{ + (void)mi; // Unused + char pwd[40]; + char login[40]; + char nc; + t_handler_return rv; + + rv = ACTION_INVALID; + if (PWD_ROW < 0) return rv; // No need to authenticate + + if (mi->item == loginstr) { /* User wants to login */ + nc = getnumcols(); + gotoxy(PWD_ROW,1,ms->menupage); + csprint("Enter Username: ",0x07); + getstring(login, sizeof username); + gotoxy(PWD_ROW,1,ms->menupage); + cprint(' ',0x07,nc,ms->menupage); + csprint("Enter Password: ",0x07); + getpwd(pwd, sizeof pwd); + gotoxy(PWD_ROW,1,ms->menupage); + cprint(' ',0x07,nc,ms->menupage); + + if (authenticate_user(login,pwd)) + { + strcpy(username,login); + strcpy(logoutstr,"ogout "); + strcat(logoutstr,username); + mi->item = logoutstr; // Change item to read "Logout" + rv.refresh = 1; // refresh the screen (as item contents changed) + } + else strcpy(username,GUEST_USER); + } + else // User needs to logout + { + strcpy(username,GUEST_USER); + strcpy(logoutstr,""); + mi->item = loginstr; + } + + return rv; +} + +t_handler_return check_perms(t_menusystem *ms, t_menuitem *mi) +{ + char *perms; + pt_xtra x; + + (void) ms; // To keep compiler happy + + x = (pt_xtra) mi->extra_data; + perms = ( x ? x->perms : NULL); + if (!perms) return ACTION_VALID; + + if (isallowed(username,"root") || isallowed(username,perms)) // If allowed + return ACTION_VALID; + else return ACTION_INVALID; +} + +// Compute the full command line to add and if non-trivial +// prepend the string prepend to final command line +// Assume cmdline points to buffer long enough to hold answer +void gencommand(pt_menuitem mi, char *cmdline) +{ + pt_xtra x; + cmdline[0] = '\0'; + strcat(cmdline,mi->data); + x = (pt_xtra) mi->extra_data; + if ( (x) && (x->argsmenu)) gen_append_line(x->argsmenu,cmdline); +} + + +// run the given command together with additional options which may need passing +void runcommand(pt_menuitem mi) +{ + char *line; + pt_xtra x; + long ipappend; + + line = (char *)malloc(sizeof(char)*MAX_CMD_LINE_LENGTH); + gencommand(mi,line); + x = (pt_xtra) mi->extra_data; + ipappend = (x ? x->ipappend : 0); + + runsyslinuximage(line,x->ipappend); + free(line); +} + +// set the extra info for the specified menu item. +void set_xtra(pt_menuitem mi, const char *argsmenu, const char *perms, unsigned int helpid, long ipappend) +{ + pt_xtra xtra; + int bad_argsmenu, bad_perms, bad_ipappend; + + mi->extra_data = NULL; // initalize + mi->helpid = helpid; // set help id + + if (mi->action != OPT_RUN) return; + + bad_argsmenu = bad_perms = bad_ipappend = 0; + if ( (argsmenu==NULL) || (strlen(argsmenu)==0)) bad_argsmenu = 1; + if ( (perms==NULL) || (strlen(perms)==0)) bad_perms = 1; + if ( ipappend==0) bad_ipappend = 1; + + if (bad_argsmenu && bad_perms && bad_ipappend) return; + + xtra = (pt_xtra) malloc(sizeof(t_xtra)); + mi->extra_data = (void *) xtra; + xtra->argsmenu = xtra->perms = NULL; + xtra->ipappend = ipappend; + if (!bad_argsmenu) { + xtra->argsmenu = (char *) malloc(sizeof(char)*(strlen(argsmenu)+1)); + strcpy(xtra->argsmenu,argsmenu); + } + if (!bad_perms) { + xtra->perms = (char *) malloc(sizeof(char)*(strlen(perms)+1)); + strcpy(xtra->perms,perms); + mi->handler = &check_perms; + } +} + +int main(void) +{ + pt_menuitem curr; + char quit; + char exitcmd[MAX_CMD_LINE_LENGTH]; + char exitcmdroot[MAX_CMD_LINE_LENGTH]; + char onerrcmd[MAX_CMD_LINE_LENGTH]; + char startfile[MAX_CMD_LINE_LENGTH]; + char *ecmd; // effective exit command or onerrorcmd + char skipbits; + char skipcmd[MAX_CMD_LINE_LENGTH]; + int timeout; // time in multiples of 0.1 seconds + int totaltimeout; // time in multiples of 0.1 seconds + t_dotcmd dotrv; // to store the return value of execdotcmd + int temp; + + strcpy(username,GUEST_USER); +--header ENDS-- +--system BEGINS-- +/* ---- Initializing menu system parameters --- */ + vmode = %(videomode)s; + skipbits = %(skipcondn)s; + PWD_ROW = %(pwdrow)s; + EDIT_ROW = %(editrow)s; + strcpy(onerrcmd,"%(onerrorcmd)s"); + strcpy(exitcmd,"%(exitcmd)s"); + strcpy(exitcmdroot,"%(exitcmdroot)s"); + // If not specified exitcmdroot = exitcmd + if (exitcmdroot[0] == '\0') strcpy(exitcmdroot,exitcmd); + // Timeout stuff + timeout = %(timeout)s; + strcpy(timeoutcmd,"%(timeoutcmd)s"); + totaltimeout = %(totaltimeout)s; + strcpy(totaltimeoutcmd,"%(totaltimeoutcmd)s"); + strcpy(startfile,"%(startfile)s"); + + init_help("%(helpdir)s"); + init_passwords("%(pwdfile)s"); + init_menusystem("%(title)s"); + set_window_size(%(top)s,%(left)s,%(bot)s,%(right)s); + + // Register the ontimeout handler, with a time out of 10 seconds + reg_ontimeout(ontimeout,timeout*10,0); + reg_ontotaltimeout(ontotaltimeout,totaltimeout*10); + + // Register menusystem handlers + reg_handler(HDLR_KEYS,&keys_handler); +/* ---- End of initialization --- */ +--system ENDS-- +--item BEGINS-- + + curr = add_item("%(item)s","%(info)s",%(type)s,"%(data)s",%(state)d); + set_xtra(curr,"%(argsmenu)s","%(perms)s",%(helpid)d,%(ipappend)d); // Set associated extra info + set_shortcut(%(shortcut)s); +--item ENDS-- +--login BEGINS-- + + curr = add_item(loginstr,"Login/Logout of authentication system",OPT_RUN,NULL,0); + curr->helpid = %(helpid)d; + curr->handler = &login_handler; +--login ENDS-- +--menu BEGINS-- + +/* ------- MENU %(name)s ----- */ + add_named_menu("%(name)s","%(title)s",-1); +--menu ENDS-- +--footer BEGINS-- +/* ------- END OF MENU declarations ----- */ + +// Check if we should skip the menu altogether + quit = 0; // Dont skip the menu + if (getshiftflags() & skipbits) { // we must skip the menu altogther and execute skipcmd + dotrv = execdotcmd(skipcmd,".beep%.exit",NULL); // Worst case we beep and exit + if (dotrv == QUIT_CMD) quit = 1; + } + +// Switch vide mode if required + if (vmode != 0xFF) setvideomode(vmode); + +// Do we have a startfile to display? + if (startfile[0] != '\0') runhelp(startfile); + +// The main loop + while (quit == 0) { // As long as quit is zero repeat + curr = showmenus(find_menu_num("main")); // Initial menu is the one called "main" + + if (curr) { + if (curr->action == OPT_RUN) { + ecmd = (char *)malloc(sizeof(char)*MAX_CMD_LINE_LENGTH); + gencommand(curr,ecmd); + temp = (curr->extra_data ? ((pt_xtra)curr->extra_data)->ipappend : 0); + runsyslinuximage(ecmd,temp); + // kernel not found so execute the appropriate dot command + dotrv = execdotcmd(onerrcmd,".quit",ecmd); // pass bad cmdline as arg + if (dotrv== QUIT_CMD) quit = 1; + free(ecmd); ecmd = NULL; + } + else csprint("Error in programming!",0x07); + } else { + // find effective exit command + ecmd = ( isallowed(username,"root") ? exitcmdroot : exitcmd); + dotrv = execdotcmd(ecmd,".repeat",NULL); + quit = (dotrv == QUIT_CMD ? 1 : 0); // should we exit now + } + } + +// Deallocate space used and quit + close_passwords(); + close_help(); + close_menusystem(); + return 0; +} + +--footer ENDS-- diff --git a/com32/cmenu/complex.c b/com32/cmenu/complex.c new file mode 100644 index 0000000..94627c4 --- /dev/null +++ b/com32/cmenu/complex.c @@ -0,0 +1,423 @@ +/* -*- c -*- ------------------------------------------------------------- * + * + * Copyright 2004-2005 Murali Krishnan Ganapathy - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +#include "menu.h" +#include "com32io.h" +#include "help.h" +#include "passwords.h" +#include "des.h" +#include +#include + +/* Global variables */ +char infoline[160]; +char buffer[80]; + +// Different network options +static char nonet[] = "etwork [none]"; +static char dhcpnet[]="etwork [dhcp]"; +static char statnet[]="etwork [static]"; + +static char loginstr[] = "ogin "; +static char logoutstr[]= "ogout "; + +struct { + unsigned int baseurl : 1; // Do we need to specify by url + unsigned int mountcd : 1; // Should we mount the cd + unsigned int winrep : 1; // Want to repair windows? + unsigned int linrep : 1; // Want to repair linux? +} flags; + +// Some menu options +t_menuitem *baseurl,*mountcd,*network,*runprep,*winrep,*linrep; +t_menuitem * stat,*dhcp,*none,*prepopt,*secret; + +// all the menus we are going to declare +unsigned char TESTING,RESCUE,MAIN,PREPMENU,NETMENU,LONGMENU,SECRETMENU; + +char username[12]; // Name of user currently using the system + +/* End globals */ + +TIMEOUTCODE ontimeout() +{ + beep(); + return CODE_WAIT; +} + + +#define INFLINE 22 +#define PWDLINE 3 +#define PWDPROMPT 21 +#define PWDCOLUMN 60 +#define PWDATTR 0x74 +#define EDITPROMPT 21 + +void keys_handler(t_menusystem *ms, t_menuitem *mi,unsigned int scancode) +{ + char nc; + + if ((scancode >> 8) == F1) { // If scancode of F1 + runhelpsystem(mi->helpid); + } + // If user hit TAB, and item is an "executable" item + // and user has privileges to edit it, edit it in place. + if (((scancode & 0xFF) == 0x09) && (mi->action == OPT_RUN) && + (isallowed(username,"editcmd") || isallowed(username,"root"))) { + nc = getnumcols(); + // User typed TAB and has permissions to edit command line + gotoxy(EDITPROMPT,1,ms->menupage); + csprint("Command line:",0x07); + editstring(mi->data,ACTIONLEN); + gotoxy(EDITPROMPT,1,ms->menupage); + cprint(' ',0x07,nc-1,ms->menupage); + } +} + +t_handler_return login_handler(t_menusystem *ms, t_menuitem *mi) +{ + (void)mi; // Unused + char pwd[40]; + char login[40]; + char nc; + t_handler_return rv; + + if (mi->item == loginstr) { /* User wants to login */ + nc = getnumcols(); + gotoxy(PWDPROMPT,1,ms->menupage); + csprint("Enter Username: ",0x07); + getstring(login, sizeof username); + gotoxy(PWDPROMPT,1,ms->menupage); + cprint(' ',0x07,nc,ms->menupage); + csprint("Enter Password: ",0x07); + getpwd(pwd, sizeof pwd); + gotoxy(PWDPROMPT,1,ms->menupage); + cprint(' ',0x07,nc,ms->menupage); + + if (authenticate_user(login,pwd)) + { + strcpy(username,login); + mi->item = logoutstr; // Change item to read "Logout" + } + else strcpy(username,GUEST_USER); + } + else // User needs to logout + { + strcpy(username,GUEST_USER); + mi->item = loginstr; + } + + if (strcmp(username,GUEST_USER)==0) + { + prepopt->action = OPT_INACTIVE; + secret->action = OPT_INVISIBLE; + } + else + { + prepopt->action = OPT_SUBMENU; + prepopt->itemdata.radiomenunum = PREPMENU; + secret->action = OPT_SUBMENU; + secret->itemdata.submenunum = SECRETMENU; + } + rv.valid = 0; + rv.refresh = 1; + return rv; +} + +void msys_handler(t_menusystem *ms, t_menuitem *mi) +{ + char nc; + void *v; + nc = getnumcols(); // Get number of columns + + gotoxy(PWDLINE,PWDCOLUMN,ms->menupage); + csprint("User: ",PWDATTR); + cprint(ms->fillchar,ms->fillattr,sizeof username,ms->menupage); + gotoxy(PWDLINE,PWDCOLUMN +6,ms->menupage); + csprint(username,PWDATTR); + + if (mi->parindex != PREPMENU) // If we are not in the PREP MENU + { + gotoxy(INFLINE,0,ms->menupage); + cprint(' ',0x07,nc,ms->menupage); + gotoxy(INFLINE+1,0,ms->menupage); + cprint(' ',0x07,nc,ms->menupage); + return; + } + strcpy (infoline," "); + if (flags.baseurl) strcat(infoline,"baseurl=http://192.168.11.12/gui "); + if (flags.mountcd) strcat(infoline,"mountcd=yes "); + v = (void *)network->data; + if (v!=NULL) // Some network option specified + { + strcat(infoline,"network="); + strcat(infoline,(char *)(((t_menuitem *)v)->data)); + } + if (flags.winrep) strcat(infoline,"repair=win "); + if (flags.linrep) strcat(infoline,"repair=lin "); + + gotoxy(INFLINE,0,ms->menupage); + cprint(' ',0x07,nc,ms->menupage); + gotoxy(INFLINE+1,0,ms->menupage); + cprint(' ',0x07,nc,ms->menupage); + gotoxy(INFLINE,0,ms->menupage); + csprint("Kernel Arguments:",0x07); + gotoxy(INFLINE,17,ms->menupage); + csprint(infoline,0x07); +} + +t_handler_return network_handler(t_menusystem *ms, t_menuitem *mi) +{ + // mi=network since this is handler only for that. + (void)ms; // Unused + + if (mi->data == (void *)none) mi->item = nonet; + if (mi->data == (void *)stat) mi->item = statnet; + if (mi->data == (void *)dhcp) mi->item = dhcpnet; + return ACTION_INVALID; // VALID or INVALID does not matter +} + +t_handler_return checkbox_handler(t_menusystem *ms, t_menuitem *mi) +{ + (void)ms; /* Unused */ + + if (mi->action != OPT_CHECKBOX) return ACTION_INVALID; + + if (strcmp(mi->data,"baseurl") == 0) flags.baseurl = (mi->itemdata.checked ? 1 : 0); + if (strcmp(mi->data,"winrepair") == 0) { + if (mi->itemdata.checked) + { + flags.winrep = 1; + linrep->action = OPT_INACTIVE; + } + else + { + flags.winrep = 0; + linrep->action = OPT_CHECKBOX; + } + } + if (strcmp(mi->data,"linrepair") == 0) { + if (mi->itemdata.checked) + { + flags.linrep = 1; + winrep->action = OPT_INACTIVE; + } + else + { + flags.winrep = 0; + winrep->action = OPT_CHECKBOX; + } + } + if (strcmp(mi->data,"mountcd") == 0) flags.mountcd = (mi->itemdata.checked ? 1 : 0); + return ACTION_VALID; +} + +/* + Clears keyboard buffer and then + wait for stepsize*numsteps milliseconds for user to press any key + checks for keypress every stepsize milliseconds. + Returns: 1 if user pressed a key (not read from the buffer), + 0 if time elapsed +*/ +int checkkeypress(int stepsize, int numsteps) +{ + int i; + clearkbdbuf(); + for (i=0; i < numsteps; i++) + { + if (checkkbdbuf()) return 1; + sleep(stepsize); + } + return 0; +} + +int main() +{ + t_menuitem * curr; + char cmd[160]; + char ip[30]; + + // Set default username as guest + strcpy(username,GUEST_USER); + + // Switch video mode here + // setvideomode(0x18); // or whatever mode you want + + // Choose the default title and setup default values for all attributes.... + init_passwords("/isolinux/password"); + init_help("/isolinux/help"); + init_menusystem(NULL); + set_window_size(1,1,20,78); // Leave some space around + + // Choose the default values for all attributes and char's + // -1 means choose defaults (Actually the next 4 lines are not needed) + //set_normal_attr (-1,-1,-1,-1); + //set_status_info (-1,-1); // Display status on the last line + //set_title_info (-1,-1); + //set_misc_info(-1,-1,-1,-1); + + // Register the menusystem handler + reg_handler(HDLR_SCREEN,&msys_handler); + reg_handler(HDLR_KEYS,&keys_handler); + // Register the ontimeout handler, with a time out of 10 seconds + reg_ontimeout(ontimeout,1000,0); + + NETMENU = add_menu(" Init Network ",-1); + none = add_item("one","Dont start network",OPT_RADIOITEM,"no ",0); + dhcp = add_item("hcp","Use DHCP",OPT_RADIOITEM,"dhcp ",0); + stat = add_item("tatic","Use static IP I will specify later",OPT_RADIOITEM,"static ",0); + + TESTING = add_menu(" Testing ",-1); + set_menu_pos(5,55); + add_item("emory Test","Perform extensive memory testing",OPT_RUN, "memtest",0); + add_item("nvisible","You dont see this",OPT_INVISIBLE,"junk",0); + add_item("xit this menu","Go one level up",OPT_EXITMENU,"exit",0); + + RESCUE = add_menu(" Rescue Options ",-1); + add_item("inux Rescue","linresc",OPT_RUN,"linresc",0); + add_item("os Rescue","dosresc",OPT_RUN,"dosresc",0); + add_item("indows Rescue","winresc",OPT_RUN,"winresc",0); + add_item("xit this menu","Go one level up",OPT_EXITMENU,"exit",0); + + PREPMENU = add_menu(" Prep options ",-1); + baseurl = add_item("aseurl by IP?","Specify gui baseurl by IP address",OPT_CHECKBOX,"baseurl",0); + mountcd = add_item("ountcd?","Mount the cdrom drive?",OPT_CHECKBOX,"mountcd",0); + network = add_item(dhcpnet,"How to initialise network device?",OPT_RADIOMENU,NULL,NETMENU); + add_sep(); + winrep = add_item("Reinstall indows","Re-install the windows side of a dual boot setup",OPT_CHECKBOX,"winrepair",0); + linrep = add_item("Reinstall inux","Re-install the linux side of a dual boot setup",OPT_CHECKBOX,"linrepair",0); + add_sep(); + runprep = add_item("un prep now","Execute prep with the above options",OPT_RUN,"prep",0); + add_item("xit this menu","Go up one level",OPT_EXITMENU,"exitmenu",0); + baseurl->handler = &checkbox_handler; + mountcd->handler = &checkbox_handler; + winrep->handler = &checkbox_handler; + linrep->handler = &checkbox_handler; + network->handler = &network_handler; + flags.baseurl = 0; + flags.mountcd = 0; + flags.winrep = 0; + flags.linrep = 0; + + SECRETMENU = add_menu(" Secret Menu ",-1); + add_item("secret 1","Secret",OPT_RUN,"A",0); + add_item("secret 2","Secret",OPT_RUN,"A",0); + + LONGMENU = add_menu(" Long Menu ",40); // Override default here + add_item("a","Aa",OPT_RUN,"A",0); + add_item("b","Ab",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("

","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("","A",OPT_RUN,"A",0); + add_item("<1>","A",OPT_RUN,"A",0); + add_item("<2>","A",OPT_RUN,"A",0); + add_item("<3>","A",OPT_RUN,"A",0); + add_item("<4>","A",OPT_RUN,"A",0); + add_item("<5>","A",OPT_RUN,"A",0); + add_item("<6>","A",OPT_RUN,"A",0); + add_item("<7>","A",OPT_RUN,"A",0); + add_item("<8>","A",OPT_RUN,"A",0); + add_item("<9>","A",OPT_RUN,"A",0); + + MAIN = add_menu(" Main Menu ",8); + curr = add_item(loginstr,"Login as a privileged user",OPT_RUN,NULL,0); + set_item_options(-1,23); + curr->handler = &login_handler; + + add_item("

repare","prep",OPT_RUN,"prep",0); + set_item_options(-1,24); + prepopt = add_item("

rep options...","Options for prep image: Requires authenticated user",OPT_INACTIVE,NULL,PREPMENU); + set_item_options(-1,25); + + add_item("escue options...","Troubleshoot a system",OPT_SUBMENU,NULL,RESCUE); + set_item_options(-1,26); + add_item("esting...","Options to test hardware",OPT_SUBMENU,NULL,TESTING); + set_item_options(-1,27); + add_item("ong Menu...","test menu system",OPT_SUBMENU,NULL,LONGMENU); + set_item_options(-1,28); + secret = add_item("ecret Menu...","Secret menu",OPT_INVISIBLE,NULL,SECRETMENU); + set_item_options(-1,29); + add_item("xit to prompt", "Exit the menu system", OPT_EXITMENU, "exit", 0); + set_item_options(-1,30); + csprint("Press any key within 5 seconds to show menu...",0x07); + if (!checkkeypress(100,50)) // Granularity of 100 milliseconds + { + csprint("Sorry! Time's up.\r\n",0x07); + return 1; + } + else clearkbdbuf(); // Just in case user pressed something important + curr = showmenus(MAIN); + if (curr) + { + if (curr->action == OPT_RUN) + { + strcpy(cmd,curr->data); + if (curr == runprep) + { + strcat(cmd,infoline); + if (network->data == (void *)stat) // We want static + { + csprint("Enter IP address (last two octets only): ",0x07); + strcpy(ip, "Junk"); + editstring(ip, sizeof ip); + strcat(cmd,"ipaddr=192.168."); + strcat(cmd,ip); + } + } + if (issyslinux()) + runsyslinuxcmd(cmd); + else csprint(cmd,0x07); + return 1; // Should not happen when run from SYSLINUX + } + } + // If user quits the menu system, control comes here + // If you want to execute some specific command uncomment the next two lines + + // if (syslinux) runcommand(YOUR_COMMAND_HERE); + // else csprint(YOUR_COMMAND_HERE,0x07); + + // Deallocate space used for these data structures + close_passwords(); + close_help(); + close_menusystem(); + + // Return to prompt + return 0; +} diff --git a/com32/cmenu/complex.c32 b/com32/cmenu/complex.c32 new file mode 100755 index 0000000000000000000000000000000000000000..6d86ddd6c2f8b2ea632f771fbdfad3cd093abf73 GIT binary patch literal 34628 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 literal 0 HcmV?d00001 diff --git a/com32/cmenu/test.menu b/com32/cmenu/test.menu new file mode 100644 index 0000000..061c548 --- /dev/null +++ b/com32/cmenu/test.menu @@ -0,0 +1,60 @@ +# choose default title +title = "A test of the test.menu file" +top = 1 +left = 1 +bot = 23 +right = 78 + +[testing] +title = " Testing " + +item="Self Loop" +info="Go to Testing" +type=submenu +data=testing + +item="Memory Test" +info="Perform extensive memory testing" +data="memtest" + +item="Exit this menu" +info="Go one level up" +type=exitmenu + +[rescue] +title = " Rescue Options " +row = 10 +col = 10 + +item="Linux Rescue" +data="linresc" + +item="Dos Rescue" +data="dosresc" + +item="Windows Rescue" +data="winresc" + +item="Exit this menu" +info="Go one level up" +type=exitmenu + +[main] +title = " Main Menu " + +item="Prepare" +data="prep" + +item="Rescue options..." +info="Troubleshoot a system" +type=submenu +data="rescue" + +item="Testing..." +info="Options to test hardware" +type=submenu +data="testing" + +item="Exit this menu" +info="Go one level up" +type=exitmenu diff --git a/com32/cmenu/test2.c32 b/com32/cmenu/test2.c32 new file mode 100755 index 0000000000000000000000000000000000000000..55f0857bfc3d5e4400b0e542d6685d48d446c357 GIT binary patch literal 33804 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 Ri4TqMUp#2%a?B&R{{>vIP6+@2 literal 0 HcmV?d00001 diff --git a/com32/cmenu/test2.menu b/com32/cmenu/test2.menu new file mode 100644 index 0000000..4570dc2 --- /dev/null +++ b/com32/cmenu/test2.menu @@ -0,0 +1,142 @@ + +title=" COMBOOT Menu System " + +# location of help directory +helpdir="/isolinux/help" +pwdfile="/isolinux/password" + +# skip the menu if shift is pressed or Caps is on +# if the menu is skipped run "skipcmd" +# in our case we run the OS on the first harddisk +skipcondn=shift-caps +skipcmd="chain.c32 hd 0" + +# person with root privileges can exit menu +# others just repeat +exitcmd=".exit" +onerrorcmd=".beep 2 % % .help hlp00025.txt % .exit" + +startfile="hlp00026.txt" + +timeoutcmd=".wait" +totaltimeoutcmd="chain.c32 hd 0" + +[netmenu] +title=" Init Network " + +item="one" +info="Dont start network" +type=radioitem +data="network=no" + +item="hcp" +info="Use DHCP" +type=radioitem +data="network=dhcp" + +[testing] +title=" Testing " + +item="emory Test" +info="Perform extensive memory testing" +data="memtest" +helpid=25 +ipappend=3 + +item="nvisible" +info="You dont see this" +type=invisible + +item="xit menu" +info="Go one level up" +type=exitmenu + +[rescue] +title=" Rescue Options " + +item="inux Rescue" +info="Run linresc" +data="linresc" + +item="os Rescue" +info="dosresc" +data="dosresc" + +item="indows Rescue" +info="winresc" +data="winresc" + +item="xit this menu" +info="Go one level up" +type=exitmenu + +[prep] +title=" Prep options " + +item="aseurl by IP?" +info="Specify gui baseurl by IP address" +type=checkbox +data="baseurl=http://192.168.0.1" + +item="ountcd?" +info="Mount the cdrom drive?" +type=checkbox +data="mountcd" + +item="Network Initialization" +info="How to initialise network device?" +type=radiomenu +data="netmenu" + +type=sep + +item="Reinstall indows" +info="Re-install the windows side of a dual boot setup" +type=checkbox +data="repair=win" + +item="Reinstall inux" +info="Re-install the linux side of a dual boot setup" +type=checkbox +data="repair=lin" + +type=sep + +item="un prep now" +info="Execute prep with the above options" +data="prep" +argsmenu="prep" + +item="xit this menu" +info="Go up one level" +type=exitmenu + +[main] + +title=" Main Menu " + +type=login + +item="

repare" +info="prep" +data="prep" + +item="

rep options..." +info="Options for prep" +type=submenu +data="prep" + +item="escue options..." +info="Troubleshoot a system" +type=submenu +data="rescue" +helpid=26 + +item="esting..." +info="Options to test hardware" +type=submenu +data="testing" + +item="xit to prompt" +info="Exit the menu system" +type=exitmenu diff --git a/com32/gdbstub/Makefile b/com32/gdbstub/Makefile new file mode 100644 index 0000000..5513876 --- /dev/null +++ b/com32/gdbstub/Makefile @@ -0,0 +1,49 @@ +## ----------------------------------------------------------------------- +## +## Copyright 2001-2008 H. Peter Anvin - 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, Inc., 51 Franklin St, Fifth Floor, +## Boston MA 02110-1301, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +## +## GDB remote debugging +## + +topdir = ../.. +include ../MCONFIG + +CFLAGS += -fPIE + +LIBS = ../libutil/libutil_com.a ../lib/libcom32.a $(LIBGCC) +LNXLIBS = ../libutil/libutil_lnx.a + +MODULES = gdbstub.c32 +TESTFILES = + +OBJS = main.o int.o serial.o gdbstub.o + +all: $(MODULES) $(TESTFILES) + +gdbstub.elf : $(OBJS) $(LIBS) $(C_LIBS) + $(LD) $(LDFLAGS) -o $@ $^ + +tidy dist clean: + rm -f *.o *.lo *.a *.lst *.elf .*.d *.tmp + +clean: tidy + rm -f *.lnx + +spotless: clean + rm -f *.lss *.c32 *.com + rm -f *~ \#* + +install: + mkdir -m 755 -p $(INSTALLROOT)$(AUXDIR) + install -m 644 $(MODULES) $(INSTALLROOT)$(AUXDIR) + +-include .*.d diff --git a/com32/gdbstub/gdbstub.c b/com32/gdbstub/gdbstub.c new file mode 100644 index 0000000..f235a34 --- /dev/null +++ b/com32/gdbstub/gdbstub.c @@ -0,0 +1,529 @@ +/* + * Copyright (C) 2008 Stefan Hajnoczi . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * @file + * + * GDB stub for remote debugging + * + */ + +#include +#include +#include "serial.h" + +typedef uint32_t gdbreg_t; + +enum { + POSIX_EINVAL = 0x1c, /* used to report bad arguments to GDB */ + SIZEOF_PAYLOAD = 256, /* buffer size of GDB payload data */ + DR7_CLEAR = 0x00000400, /* disable hardware breakpoints */ + DR6_CLEAR = 0xffff0ff0, /* clear breakpoint status */ +}; + +/* The register snapshot, this must be in sync with interrupt handler and the + * GDB protocol. */ +enum { + GDBMACH_EAX, + GDBMACH_ECX, + GDBMACH_EDX, + GDBMACH_EBX, + GDBMACH_ESP, + GDBMACH_EBP, + GDBMACH_ESI, + GDBMACH_EDI, + GDBMACH_EIP, + GDBMACH_EFLAGS, + GDBMACH_CS, + GDBMACH_SS, + GDBMACH_DS, + GDBMACH_ES, + GDBMACH_FS, + GDBMACH_GS, + GDBMACH_NREGS, + GDBMACH_SIZEOF_REGS = GDBMACH_NREGS * sizeof ( gdbreg_t ) +}; + +/* Breakpoint types */ +enum { + GDBMACH_BPMEM, + GDBMACH_BPHW, + GDBMACH_WATCH, + GDBMACH_RWATCH, + GDBMACH_AWATCH, +}; + +struct gdbstub { + int exit_handler; /* leave interrupt handler */ + + int signo; + gdbreg_t *regs; + + void ( * parse ) ( struct gdbstub *stub, char ch ); + uint8_t cksum1; + + /* Buffer for payload data when parsing a packet. Once the + * packet has been received, this buffer is used to hold + * the reply payload. */ + char buf [ SIZEOF_PAYLOAD + 4 ]; /* $...PAYLOAD...#XX */ + char *payload; /* start of payload */ + int len; /* length of payload */ +}; + +/** Hardware breakpoint, fields stored in x86 bit pattern form */ +struct hwbp { + int type; /* type (1=write watchpoint, 3=access watchpoint) */ + unsigned long addr; /* linear address */ + size_t len; /* length (0=1-byte, 1=2-byte, 3=4-byte) */ + int enabled; +}; + +static struct hwbp hwbps [ 4 ]; +static gdbreg_t dr7 = DR7_CLEAR; + +static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) { + regs [ GDBMACH_EIP ] = pc; +} + +static inline void gdbmach_set_single_step ( gdbreg_t *regs, int step ) { + regs [ GDBMACH_EFLAGS ] &= ~( 1 << 8 ); /* Trace Flag (TF) */ + regs [ GDBMACH_EFLAGS ] |= ( step << 8 ); +} + +static inline void gdbmach_breakpoint ( void ) { + __asm__ __volatile__ ( "int $3\n" ); +} + +static struct hwbp *gdbmach_find_hwbp ( int type, unsigned long addr, size_t len ) { + struct hwbp *available = NULL; + unsigned int i; + for ( i = 0; i < sizeof hwbps / sizeof hwbps [ 0 ]; i++ ) { + if ( hwbps [ i ].type == type && hwbps [ i ].addr == addr && hwbps [ i ].len == len ) { + return &hwbps [ i ]; + } + if ( !hwbps [ i ].enabled ) { + available = &hwbps [ i ]; + } + } + return available; +} + +static void gdbmach_commit_hwbp ( struct hwbp *bp ) { + int regnum = bp - hwbps; + + /* Set breakpoint address */ + switch ( regnum ) { + case 0: + __asm__ __volatile__ ( "movl %0, %%dr0\n" : : "r" ( bp->addr ) ); + break; + case 1: + __asm__ __volatile__ ( "movl %0, %%dr1\n" : : "r" ( bp->addr ) ); + break; + case 2: + __asm__ __volatile__ ( "movl %0, %%dr2\n" : : "r" ( bp->addr ) ); + break; + case 3: + __asm__ __volatile__ ( "movl %0, %%dr3\n" : : "r" ( bp->addr ) ); + break; + } + + /* Set type */ + dr7 &= ~( 0x3 << ( 16 + 4 * regnum ) ); + dr7 |= bp->type << ( 16 + 4 * regnum ); + + /* Set length */ + dr7 &= ~( 0x3 << ( 18 + 4 * regnum ) ); + dr7 |= bp->len << ( 18 + 4 * regnum ); + + /* Set/clear local enable bit */ + dr7 &= ~( 0x3 << 2 * regnum ); + dr7 |= bp->enabled << 2 * regnum; +} + +int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, int enable ) { + struct hwbp *bp; + + /* Check and convert breakpoint type to x86 type */ + switch ( type ) { + case GDBMACH_WATCH: + type = 0x1; + break; + case GDBMACH_AWATCH: + type = 0x3; + break; + default: + return 0; /* unsupported breakpoint type */ + } + + /* Only lengths 1, 2, and 4 are supported */ + if ( len != 2 && len != 4 ) { + len = 1; + } + len--; /* convert to x86 breakpoint length bit pattern */ + + /* Set up the breakpoint */ + bp = gdbmach_find_hwbp ( type, addr, len ); + if ( !bp ) { + return 0; /* ran out of hardware breakpoints */ + } + bp->type = type; + bp->addr = addr; + bp->len = len; + bp->enabled = enable; + gdbmach_commit_hwbp ( bp ); + return 1; +} + +static void gdbmach_disable_hwbps ( void ) { + /* Store and clear hardware breakpoints */ + __asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( DR7_CLEAR ) ); +} + +static void gdbmach_enable_hwbps ( void ) { + /* Clear breakpoint status register */ + __asm__ __volatile__ ( "movl %0, %%dr6\n" : : "r" ( DR6_CLEAR ) ); + + /* Restore hardware breakpoints */ + __asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( dr7 ) ); +} + +/* Packet parser states */ +static void gdbstub_state_new ( struct gdbstub *stub, char ch ); +static void gdbstub_state_data ( struct gdbstub *stub, char ch ); +static void gdbstub_state_cksum1 ( struct gdbstub *stub, char ch ); +static void gdbstub_state_cksum2 ( struct gdbstub *stub, char ch ); +static void gdbstub_state_wait_ack ( struct gdbstub *stub, char ch ); + +static void serial_write ( void *buf, size_t len ) { + char *p = buf; + while ( len-- > 0 ) + serial_putc ( *p++ ); +} + +static uint8_t gdbstub_from_hex_digit ( char ch ) { + if ( ch >= '0' && ch <= '9' ) + return ch - '0'; + else if ( ch >= 'A' && ch <= 'F' ) + return ch - 'A' + 0xa; + else + return ( ch - 'a' + 0xa ) & 0xf; +} + +static uint8_t gdbstub_to_hex_digit ( uint8_t b ) { + b &= 0xf; + return ( b < 0xa ? '0' : 'a' - 0xa ) + b; +} + +/* + * To make reading/writing device memory atomic, we check for + * 2- or 4-byte aligned operations and handle them specially. + */ + +static void gdbstub_from_hex_buf ( char *dst, char *src, int lenbytes ) { + if ( lenbytes == 2 && ( ( unsigned long ) dst & 0x1 ) == 0 ) { + uint16_t i = gdbstub_from_hex_digit ( src [ 2 ] ) << 12 | + gdbstub_from_hex_digit ( src [ 3 ] ) << 8 | + gdbstub_from_hex_digit ( src [ 0 ] ) << 4 | + gdbstub_from_hex_digit ( src [ 1 ] ); + * ( uint16_t * ) dst = i; + } else if ( lenbytes == 4 && ( ( unsigned long ) dst & 0x3 ) == 0 ) { + uint32_t i = gdbstub_from_hex_digit ( src [ 6 ] ) << 28 | + gdbstub_from_hex_digit ( src [ 7 ] ) << 24 | + gdbstub_from_hex_digit ( src [ 4 ] ) << 20 | + gdbstub_from_hex_digit ( src [ 5 ] ) << 16 | + gdbstub_from_hex_digit ( src [ 2 ] ) << 12 | + gdbstub_from_hex_digit ( src [ 3 ] ) << 8 | + gdbstub_from_hex_digit ( src [ 0 ] ) << 4 | + gdbstub_from_hex_digit ( src [ 1 ] ); + * ( uint32_t * ) dst = i; + } else { + while ( lenbytes-- > 0 ) { + *dst++ = gdbstub_from_hex_digit ( src [ 0 ] ) << 4 | + gdbstub_from_hex_digit ( src [ 1 ] ); + src += 2; + } + } +} + +static void gdbstub_to_hex_buf ( char *dst, char *src, int lenbytes ) { + if ( lenbytes == 2 && ( ( unsigned long ) src & 0x1 ) == 0 ) { + uint16_t i = * ( uint16_t * ) src; + dst [ 0 ] = gdbstub_to_hex_digit ( i >> 4 ); + dst [ 1 ] = gdbstub_to_hex_digit ( i ); + dst [ 2 ] = gdbstub_to_hex_digit ( i >> 12 ); + dst [ 3 ] = gdbstub_to_hex_digit ( i >> 8 ); + } else if ( lenbytes == 4 && ( ( unsigned long ) src & 0x3 ) == 0 ) { + uint32_t i = * ( uint32_t * ) src; + dst [ 0 ] = gdbstub_to_hex_digit ( i >> 4 ); + dst [ 1 ] = gdbstub_to_hex_digit ( i ); + dst [ 2 ] = gdbstub_to_hex_digit ( i >> 12 ); + dst [ 3 ] = gdbstub_to_hex_digit ( i >> 8 ); + dst [ 4 ] = gdbstub_to_hex_digit ( i >> 20 ); + dst [ 5 ] = gdbstub_to_hex_digit ( i >> 16); + dst [ 6 ] = gdbstub_to_hex_digit ( i >> 28 ); + dst [ 7 ] = gdbstub_to_hex_digit ( i >> 24 ); + } else { + while ( lenbytes-- > 0 ) { + *dst++ = gdbstub_to_hex_digit ( *src >> 4 ); + *dst++ = gdbstub_to_hex_digit ( *src ); + src++; + } + } +} + +static uint8_t gdbstub_cksum ( char *data, int len ) { + uint8_t cksum = 0; + while ( len-- > 0 ) { + cksum += ( uint8_t ) *data++; + } + return cksum; +} + +static void gdbstub_tx_packet ( struct gdbstub *stub ) { + uint8_t cksum = gdbstub_cksum ( stub->payload, stub->len ); + stub->buf [ 0 ] = '$'; + stub->buf [ stub->len + 1 ] = '#'; + stub->buf [ stub->len + 2 ] = gdbstub_to_hex_digit ( cksum >> 4 ); + stub->buf [ stub->len + 3 ] = gdbstub_to_hex_digit ( cksum ); + serial_write ( stub->buf, stub->len + 4 ); + stub->parse = gdbstub_state_wait_ack; +} + +/* GDB commands */ +static void gdbstub_send_ok ( struct gdbstub *stub ) { + stub->payload [ 0 ] = 'O'; + stub->payload [ 1 ] = 'K'; + stub->len = 2; + gdbstub_tx_packet ( stub ); +} + +static void gdbstub_send_num_packet ( struct gdbstub *stub, char reply, int num ) { + stub->payload [ 0 ] = reply; + stub->payload [ 1 ] = gdbstub_to_hex_digit ( ( char ) num >> 4 ); + stub->payload [ 2 ] = gdbstub_to_hex_digit ( ( char ) num ); + stub->len = 3; + gdbstub_tx_packet ( stub ); +} + +/* Format is arg1,arg2,...,argn:data where argn are hex integers and data is not an argument */ +static int gdbstub_get_packet_args ( struct gdbstub *stub, unsigned long *args, int nargs, int *stop_idx ) { + int i; + char ch = 0; + int argc = 0; + unsigned long val = 0; + for ( i = 1; i < stub->len && argc < nargs; i++ ) { + ch = stub->payload [ i ]; + if ( ch == ':' ) { + break; + } else if ( ch == ',' ) { + args [ argc++ ] = val; + val = 0; + } else { + val = ( val << 4 ) | gdbstub_from_hex_digit ( ch ); + } + } + if ( stop_idx ) { + *stop_idx = i; + } + if ( argc < nargs ) { + args [ argc++ ] = val; + } + return ( ( i == stub->len || ch == ':' ) && argc == nargs ); +} + +static void gdbstub_send_errno ( struct gdbstub *stub, int errno ) { + gdbstub_send_num_packet ( stub, 'E', errno ); +} + +static void gdbstub_report_signal ( struct gdbstub *stub ) { + gdbstub_send_num_packet ( stub, 'S', stub->signo ); +} + +static void gdbstub_read_regs ( struct gdbstub *stub ) { + gdbstub_to_hex_buf ( stub->payload, ( char * ) stub->regs, GDBMACH_SIZEOF_REGS ); + stub->len = GDBMACH_SIZEOF_REGS * 2; + gdbstub_tx_packet ( stub ); +} + +static void gdbstub_write_regs ( struct gdbstub *stub ) { + if ( stub->len != 1 + GDBMACH_SIZEOF_REGS * 2 ) { + gdbstub_send_errno ( stub, POSIX_EINVAL ); + return; + } + gdbstub_from_hex_buf ( ( char * ) stub->regs, &stub->payload [ 1 ], GDBMACH_SIZEOF_REGS ); + gdbstub_send_ok ( stub ); +} + +static void gdbstub_read_mem ( struct gdbstub *stub ) { + unsigned long args [ 2 ]; + if ( !gdbstub_get_packet_args ( stub, args, sizeof args / sizeof args [ 0 ], NULL ) ) { + gdbstub_send_errno ( stub, POSIX_EINVAL ); + return; + } + args [ 1 ] = ( args [ 1 ] < SIZEOF_PAYLOAD / 2 ) ? args [ 1 ] : SIZEOF_PAYLOAD / 2; + gdbstub_to_hex_buf ( stub->payload, ( char * ) args [ 0 ], args [ 1 ] ); + stub->len = args [ 1 ] * 2; + gdbstub_tx_packet ( stub ); +} + +static void gdbstub_write_mem ( struct gdbstub *stub ) { + unsigned long args [ 2 ]; + int colon; + if ( !gdbstub_get_packet_args ( stub, args, sizeof args / sizeof args [ 0 ], &colon ) || + colon >= stub->len || stub->payload [ colon ] != ':' || + ( stub->len - colon - 1 ) % 2 != 0 ) { + gdbstub_send_errno ( stub, POSIX_EINVAL ); + return; + } + gdbstub_from_hex_buf ( ( char * ) args [ 0 ], &stub->payload [ colon + 1 ], ( stub->len - colon - 1 ) / 2 ); + gdbstub_send_ok ( stub ); +} + +static void gdbstub_continue ( struct gdbstub *stub, int single_step ) { + gdbreg_t pc; + if ( stub->len > 1 && gdbstub_get_packet_args ( stub, (unsigned long *)&pc, 1, NULL ) ) { + gdbmach_set_pc ( stub->regs, pc ); + } + gdbmach_set_single_step ( stub->regs, single_step ); + stub->exit_handler = 1; + /* Reply will be sent when we hit the next breakpoint or interrupt */ +} + +static void gdbstub_breakpoint ( struct gdbstub *stub ) { + unsigned long args [ 3 ]; + int enable = stub->payload [ 0 ] == 'Z' ? 1 : 0; + if ( !gdbstub_get_packet_args ( stub, args, sizeof args / sizeof args [ 0 ], NULL ) ) { + gdbstub_send_errno ( stub, POSIX_EINVAL ); + return; + } + if ( gdbmach_set_breakpoint ( args [ 0 ], args [ 1 ], args [ 2 ], enable ) ) { + gdbstub_send_ok ( stub ); + } else { + /* Not supported */ + stub->len = 0; + gdbstub_tx_packet ( stub ); + } +} + +static void gdbstub_rx_packet ( struct gdbstub *stub ) { + switch ( stub->payload [ 0 ] ) { + case '?': + gdbstub_report_signal ( stub ); + break; + case 'g': + gdbstub_read_regs ( stub ); + break; + case 'G': + gdbstub_write_regs ( stub ); + break; + case 'm': + gdbstub_read_mem ( stub ); + break; + case 'M': + gdbstub_write_mem ( stub ); + break; + case 'c': /* Continue */ + case 'k': /* Kill */ + case 's': /* Step */ + case 'D': /* Detach */ + gdbstub_continue ( stub, stub->payload [ 0 ] == 's' ); + if ( stub->payload [ 0 ] == 'D' ) { + gdbstub_send_ok ( stub ); + } + break; + case 'Z': /* Insert breakpoint */ + case 'z': /* Remove breakpoint */ + gdbstub_breakpoint ( stub ); + break; + default: + stub->len = 0; + gdbstub_tx_packet ( stub ); + break; + } +} + +/* GDB packet parser */ +static void gdbstub_state_new ( struct gdbstub *stub, char ch ) { + if ( ch == '$' ) { + stub->len = 0; + stub->parse = gdbstub_state_data; + } +} + +static void gdbstub_state_data ( struct gdbstub *stub, char ch ) { + if ( ch == '#' ) { + stub->parse = gdbstub_state_cksum1; + } else if ( ch == '$' ) { + stub->len = 0; /* retry new packet */ + } else { + /* If the length exceeds our buffer, let the checksum fail */ + if ( stub->len < SIZEOF_PAYLOAD ) { + stub->payload [ stub->len++ ] = ch; + } + } +} + +static void gdbstub_state_cksum1 ( struct gdbstub *stub, char ch ) { + stub->cksum1 = gdbstub_from_hex_digit ( ch ) << 4; + stub->parse = gdbstub_state_cksum2; +} + +static void gdbstub_state_cksum2 ( struct gdbstub *stub, char ch ) { + uint8_t their_cksum; + uint8_t our_cksum; + + stub->parse = gdbstub_state_new; + their_cksum = stub->cksum1 + gdbstub_from_hex_digit ( ch ); + our_cksum = gdbstub_cksum ( stub->payload, stub->len ); + + if ( their_cksum == our_cksum ) { + serial_write ( "+", 1 ); + if ( stub->len > 0 ) { + gdbstub_rx_packet ( stub ); + } + } else { + serial_write ( "-", 1 ); + } +} + +static void gdbstub_state_wait_ack ( struct gdbstub *stub, char ch ) { + if ( ch == '+' ) { + stub->parse = gdbstub_state_new; + } else { + /* This retransmit is very aggressive but necessary to keep + * in sync with GDB. */ + gdbstub_tx_packet ( stub ); + } +} + +void gdbstub_handler ( int signo, gdbreg_t *regs ) { + struct gdbstub stub; + + gdbmach_disable_hwbps(); + + stub.parse = gdbstub_state_new; + stub.payload = &stub.buf [ 1 ]; + stub.signo = signo; + stub.regs = regs; + stub.exit_handler = 0; + gdbstub_report_signal ( &stub ); + while ( !stub.exit_handler ) + stub.parse ( &stub, serial_getc() ); + + gdbmach_enable_hwbps(); +} diff --git a/com32/gdbstub/int.S b/com32/gdbstub/int.S new file mode 100644 index 0000000..2a1ff9d --- /dev/null +++ b/com32/gdbstub/int.S @@ -0,0 +1,77 @@ + .section ".text","ax" + +#define SIGTRAP 5 + +#define SIZEOF_I386_REGS 32 +#define SIZEOF_I386_FLAGS 4 + +/* When invoked, the stack contains: eflags, cs, eip, signo. */ +#define IH_OFFSET_GDB_REGS ( 0 ) +#define IH_OFFSET_GDB_EIP ( IH_OFFSET_GDB_REGS + SIZEOF_I386_REGS ) +#define IH_OFFSET_GDB_EFLAGS ( IH_OFFSET_GDB_EIP + 4 ) +#define IH_OFFSET_GDB_SEG_REGS ( IH_OFFSET_GDB_EFLAGS + SIZEOF_I386_FLAGS ) +#define IH_OFFSET_GDB_END ( IH_OFFSET_GDB_SEG_REGS + 6 * 4 ) +#define IH_OFFSET_OLD_EIP ( IH_OFFSET_GDB_END ) +#define IH_OFFSET_OLD_CS ( IH_OFFSET_OLD_EIP + 4 ) +#define IH_OFFSET_OLD_EFLAGS ( IH_OFFSET_OLD_CS + 4 ) +#define IH_OFFSET_END ( IH_OFFSET_OLD_EFLAGS + 4 ) + +/* We also access the stack whilst still storing or restoring + * the register snapshot. Since ESP is in flux, we need + * special offsets. + */ +#define IH_OFFSET_FLUX_OLD_CS ( IH_OFFSET_OLD_CS - 44 ) +#define IH_OFFSET_FLUX_OLD_EFLAGS ( IH_OFFSET_OLD_EFLAGS - 40 ) +#define IH_OFFSET_FLUX_OLD_EIP ( IH_OFFSET_OLD_EIP - 36 ) +#define IH_OFFSET_FLUX_END ( IH_OFFSET_END - 20 ) + + .global int_handler +int_handler: + /* Store CPU state in GDB register snapshot */ + pushw $0 + pushw %gs + pushw $0 + pushw %fs + pushw $0 + pushw %es + pushw $0 + pushw %ds + pushw $0 + pushw %ss + pushw $0 + pushw IH_OFFSET_FLUX_OLD_CS + 2(%esp) + pushl IH_OFFSET_FLUX_OLD_EFLAGS(%esp) + pushl IH_OFFSET_FLUX_OLD_EIP(%esp) + pushl %edi + pushl %esi + pushl %ebp + leal IH_OFFSET_FLUX_END(%esp), %edi + pushl %edi /* old ESP */ + pushl %ebx + pushl %edx + pushl %ecx + pushl %eax + + /* Call GDB stub exception handler */ + movl $SIGTRAP, %eax + movl %esp, %edx + call gdbstub_handler + + /* Restore CPU state from GDB register snapshot */ + popl %eax + popl %ecx + popl %edx + popl %ebx + addl $4, %esp /* Changing ESP currently not supported */ + popl %ebp + popl %esi + popl %edi + popl IH_OFFSET_FLUX_OLD_EIP(%esp) + popl IH_OFFSET_FLUX_OLD_EFLAGS(%esp) + popl IH_OFFSET_FLUX_OLD_CS(%esp) + popl %ss + popl %ds + popl %es + popl %fs + popl %gs + iret diff --git a/com32/gdbstub/main.c b/com32/gdbstub/main.c new file mode 100644 index 0000000..8bb1dc5 --- /dev/null +++ b/com32/gdbstub/main.c @@ -0,0 +1,148 @@ +#include +#include +#include +#include +#include +#include +#include "serial.h" + +#define X86_INT_DB 1 +#define X86_INT_BP 3 +#define COM32_IDT ((void*)0x100000) +#define COM32_LOAD_ADDR ((void*)0x101000) +#define STACK_SIZE 0x1000 + +extern char _start[], _end[]; + +struct reloc_info { + void *data; + size_t len; + uint32_t old_esp; + uint32_t reloc_base; +}; + +static inline void error(const char *msg) +{ + fputs(msg, stderr); +} + +static inline uint32_t reloc_ptr(struct reloc_info *ri, void *ptr) +{ + return ri->reloc_base + (uint32_t)((char*)ptr - _start); +} + +static void hijack_interrupt(int intn, uint32_t handler) +{ + struct { + uint32_t lo; + uint32_t hi; + } *idt = COM32_IDT; + + idt[intn].lo = (idt[intn].lo & 0xffff0000) | (handler & 0x0000ffff); + idt[intn].hi = (idt[intn].hi & 0x0000ffff) | (handler & 0xffff0000); +} + +static void shift_cmdline(struct com32_sys_args *com32) +{ + char *p; + + /* Skip leading whitespace */ + for (p = com32->cs_cmdline; *p != '\0' && *p == ' '; p++) + ; + + /* Skip first word */ + for (; *p != '\0' && *p != ' '; p++) + ; + + /* Skip whitespace after first word */ + for (; *p != '\0' && *p == ' '; p++) + ; + + com32->cs_cmdline = p; +} + +static __noreturn reloc_entry(struct reloc_info *ri) +{ + extern char int_handler[]; + size_t stack_frame_size = sizeof(struct com32_sys_args) + 4; + struct com32_sys_args *com32; + uint32_t module_esp; + + hijack_interrupt(X86_INT_DB, reloc_ptr(ri, int_handler)); + hijack_interrupt(X86_INT_BP, reloc_ptr(ri, int_handler)); + + /* Copy module to load address */ + memcpy(COM32_LOAD_ADDR, ri->data, ri->len); + + /* Copy stack frame onto module stack */ + module_esp = (ri->reloc_base - stack_frame_size) & ~15; + memcpy((void*)module_esp, (void*)ri->old_esp, stack_frame_size); + + /* Fix up command line */ + com32 = (struct com32_sys_args*)(module_esp + 4); + shift_cmdline(com32); + + /* Set up CPU state to run module and enter GDB */ + asm volatile ( + "movl %0, %%esp\n\t" + "pushf\n\t" + "pushl %%cs\n\t" + "pushl %1\n\t" + "jmp *%2\n\t" + : : "r"(module_esp), + "c"(COM32_LOAD_ADDR), + "r"(reloc_ptr(ri, int_handler)) + ); + for(;;); /* shut the compiler up */ +} + +static inline __noreturn reloc(void *ptr, size_t len) +{ + extern uint32_t __entry_esp; + size_t total_size = _end - _start; + __noreturn (*entry_fn)(struct reloc_info*); + struct reloc_info ri; + uint32_t esp; + char *dest; + + /* Calculate relocation address, preserve current stack */ + asm volatile ("movl %%esp, %0\n\t" : "=m"(esp)); + dest = (char*)((esp - STACK_SIZE - total_size) & ~3); + + /* Calculate entry point in relocated code */ + entry_fn = (void*)(dest + ((char*)reloc_entry - _start)); + + /* Copy all sections to relocation address */ + printf("Relocating %d bytes from %p to %p\n", total_size, _start, dest); + memcpy(dest, _start, total_size); + + /* Call into relocated code */ + ri.data = ptr; + ri.len = len; + ri.old_esp = __entry_esp; + ri.reloc_base = (uint32_t)dest; + entry_fn(&ri); +} + +int main(int argc, char *argv[]) +{ + void *data; + size_t data_len; + + openconsole(&dev_null_r, &dev_stdcon_w); + + if (argc < 2) { + error("Usage: gdbstub.c32 com32_file arguments...\n"); + return 1; + } + + if (loadfile(argv[1], &data, &data_len)) { + error("Unable to load file\n"); + return 1; + } + + serial_init(); + + /* No more lib calls after this point */ + reloc(data, data_len); +} diff --git a/com32/gdbstub/serial.c b/com32/gdbstub/serial.c new file mode 100644 index 0000000..2b9fc2e --- /dev/null +++ b/com32/gdbstub/serial.c @@ -0,0 +1,190 @@ +/* + * The serial port interface routines implement a simple polled i/o + * interface to a standard serial port. Due to the space restrictions + * for the boot blocks, no BIOS support is used (since BIOS requires + * expensive real/protected mode switches), instead the rudimentary + * BIOS support is duplicated here. + * + * The base address and speed for the i/o port are passed from the + * Makefile in the COMCONSOLE and CONSPEED preprocessor macros. The + * line control parameters are currently hard-coded to 8 bits, no + * parity, 1 stop bit (8N1). This can be changed in init_serial(). + */ + +#include +#include +#include "serial.h" + +/* Set default values if none specified */ + +#ifndef COMCONSOLE +#define COMCONSOLE 0x3f8 +#endif + +#ifndef COMSPEED +#define COMSPEED 9600 +#endif + +#ifndef COMDATA +#define COMDATA 8 +#endif + +#ifndef COMPARITY +#define COMPARITY 0 +#endif + +#ifndef COMSTOP +#define COMSTOP 1 +#endif + +#undef UART_BASE +#define UART_BASE ( COMCONSOLE ) + +#undef UART_BAUD +#define UART_BAUD ( COMSPEED ) + +#if ((115200%UART_BAUD) != 0) +#error Bad ttys0 baud rate +#endif + +#define COMBRD (115200/UART_BAUD) + +/* Line Control Settings */ +#define UART_LCS ( ( ( (COMDATA) - 5 ) << 0 ) | \ + ( ( (COMPARITY) ) << 3 ) | \ + ( ( (COMSTOP) - 1 ) << 2 ) ) + +/* Data */ +#define UART_RBR 0x00 +#define UART_TBR 0x00 + +/* Control */ +#define UART_IER 0x01 +#define UART_IIR 0x02 +#define UART_FCR 0x02 +#define UART_LCR 0x03 +#define UART_MCR 0x04 +#define UART_DLL 0x00 +#define UART_DLM 0x01 + +/* Status */ +#define UART_LSR 0x05 +#define UART_LSR_TEMPT 0x40 /* Transmitter empty */ +#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ +#define UART_LSR_BI 0x10 /* Break interrupt indicator */ +#define UART_LSR_FE 0x08 /* Frame error indicator */ +#define UART_LSR_PE 0x04 /* Parity error indicator */ +#define UART_LSR_OE 0x02 /* Overrun error indicator */ +#define UART_LSR_DR 0x01 /* Receiver data ready */ + +#define UART_MSR 0x06 +#define UART_SCR 0x07 + +#define uart_readb(addr) inb(addr) +#define uart_writeb(val,addr) outb((val),(addr)) + +/* + * void serial_putc(int ch); + * Write character `ch' to port UART_BASE. + */ +void serial_putc ( int ch ) { + int status; + for (;;) { + status = uart_readb(UART_BASE + UART_LSR); + if (status & UART_LSR_THRE) { + /* TX buffer emtpy */ + uart_writeb(ch, UART_BASE + UART_TBR); + break; + } + } +} + +/* + * 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_init(void); + * Initialize port UART_BASE to speed COMSPEED, line settings 8N1. + */ +void serial_init ( void ) { + int status; + int divisor, lcs; + + divisor = COMBRD; + lcs = UART_LCS; + + +#ifdef COMPRESERVE + lcs = uart_readb(UART_BASE + UART_LCR) & 0x7f; + uart_writeb(0x80 | lcs, UART_BASE + UART_LCR); + divisor = (uart_readb(UART_BASE + UART_DLM) << 8) | uart_readb(UART_BASE + UART_DLL); + uart_writeb(lcs, UART_BASE + UART_LCR); +#endif + + /* Set Baud Rate Divisor to COMSPEED, and test to see if the + * serial port appears to be present. + */ + uart_writeb(0x80 | lcs, UART_BASE + UART_LCR); + uart_writeb(0xaa, UART_BASE + UART_DLL); + if (uart_readb(UART_BASE + UART_DLL) != 0xaa) { + 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) + */ + (void) uart_readb(UART_BASE + UART_RBR); + /* line status reg */ + status = uart_readb(UART_BASE + UART_LSR); + } while(status & UART_LSR_DR); + out: + return; +} diff --git a/com32/gdbstub/serial.h b/com32/gdbstub/serial.h new file mode 100644 index 0000000..c78a575 --- /dev/null +++ b/com32/gdbstub/serial.h @@ -0,0 +1,14 @@ +#ifndef _GPXE_SERIAL_H +#define _GPXE_SERIAL_H + +/** @file + * + * Serial driver functions + * + */ + +extern void serial_putc ( int ch ); +extern int serial_getc ( void ); +extern void serial_init ( void ); + +#endif /* _GPXE_SERIAL_H */ diff --git a/com32/gplinclude/README b/com32/gplinclude/README new file mode 100644 index 0000000..ac1bf6a --- /dev/null +++ b/com32/gplinclude/README @@ -0,0 +1 @@ +Put header files for LGPL or GPL library functions in this directory. diff --git a/com32/gplinclude/cpuid.h b/com32/gplinclude/cpuid.h new file mode 100644 index 0000000..ca1c314 --- /dev/null +++ b/com32/gplinclude/cpuid.h @@ -0,0 +1,212 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006-2009 Erwan Velu - All Rights Reserved + * + * Portions of this file taken from the Linux kernel, + * Copyright 1991-2009 Linus Torvalds and contributors + * + * 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, Inc., + * 51 Franklin St, Fifth Floor, Boston MA 02110-1301; + * incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef _CPUID_H +#define _CPUID_H + +#include +#include +#include +#include +#include + +#define PAGE_SIZE 4096 + +#define CPU_MODEL_SIZE 48 +#define CPU_VENDOR_SIZE 48 + +typedef struct { + bool fpu; /* Onboard FPU */ + bool vme; /* Virtual Mode Extensions */ + bool de; /* Debugging Extensions */ + bool pse; /* Page Size Extensions */ + bool tsc; /* Time Stamp Counter */ + bool msr; /* Model-Specific Registers, RDMSR, WRMSR */ + bool pae; /* Physical Address Extensions */ + bool mce; /* Machine Check Architecture */ + bool cx8; /* CMPXCHG8 instruction */ + bool apic;/* Onboard APIC */ + bool sep; /* SYSENTER/SYSEXIT */ + bool mtrr;/* Memory Type Range Registers */ + bool pge; /* Page Global Enable */ + bool mca; /* Machine Check Architecture */ + bool cmov;/* CMOV instruction (FCMOVCC and FCOMI too if FPU present) */ + bool pat; /* Page Attribute Table */ + bool pse_36; /* 36-bit PSEs */ + bool psn; /* Processor serial number */ + bool clflsh; /* Supports the CLFLUSH instruction */ + bool dts; /* Debug Trace Store */ + bool acpi;/* ACPI via MSR */ + bool mmx; /* Multimedia Extensions */ + bool fxsr;/* FXSAVE and FXRSTOR instructions (fast save and restore */ + /* of FPU context), and CR4.OSFXSR available */ + bool sse; /* Streaming SIMD Extensions */ + bool sse2;/* Streaming SIMD Extensions 2*/ + bool ss; /* CPU self snoop */ + bool htt; /* Hyper-Threading */ + bool acc; /* Automatic clock control */ + bool syscall; /* SYSCALL/SYSRET */ + bool mp; /* MP Capable. */ + bool nx; /* Execute Disable */ + bool mmxext; /* AMD MMX extensions */ + bool lm; /* Long Mode (x86-64) */ + bool nowext;/* AMD 3DNow! extensions */ + bool now; /* 3DNow! */ + bool smp; /* A smp configuration has been found*/ +} s_cpu_flags; + +typedef struct { + char vendor[CPU_VENDOR_SIZE]; + uint8_t vendor_id; + uint8_t family; + char model[CPU_MODEL_SIZE]; + uint8_t model_id; + uint8_t stepping; + s_cpu_flags flags; +} s_cpu; + +/**********************************************************************************/ +/**********************************************************************************/ +/* From this point this is some internal stuff mainly taken from the linux kernel */ +/**********************************************************************************/ +/**********************************************************************************/ + +/* + * 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 */ + +#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_NUM 9 +#define X86_VENDOR_UNKNOWN 0xff + +static inline __purefunc bool test_bit(int nr, const uint32_t *addr) +{ + return ((1UL << (nr & 31)) & (addr[nr >> 5])) != 0; +} + +#define cpu_has(c, bit) test_bit(bit, (c)->x86_capability) + +/* + * CPU type and hardware bug flags. Kept separately for each CPU. + * Members of this structure are referenced in head.S, so think twice + * before touching them. [mj] + */ + +struct cpuinfo_x86 { + uint8_t x86; /* CPU family */ + uint8_t x86_vendor; /* CPU vendor */ + uint8_t x86_model; + uint8_t x86_mask; + char wp_works_ok; /* It doesn't on 386's */ + char hlt_works_ok; /* Problems on some 486Dx4's and old 386's */ + char hard_math; + char rfu; + int cpuid_level; /* Maximum supported CPUID level, -1=no CPUID */ + uint32_t x86_capability[NCAPINTS]; + char x86_vendor_id[16]; + char x86_model_id[64]; + int x86_cache_size; /* in KB, if available */ + int x86_cache_alignment; /* in bytes */ + char fdiv_bug; + char f00f_bug; + char coma_bug; + char pad0; + int x86_power; + unsigned long loops_per_jiffy; +#ifdef CONFIG_SMP + cpumask_t llc_shared_map; /* cpus sharing the last level cache */ +#endif + unsigned char x86_max_cores; /* cpuid returned max cores value */ + unsigned char booted_cores; /* number of cores as seen by OS */ + unsigned char apicid; + unsigned char x86_clflush_size; + +} __attribute__((__packed__)); +#endif + +struct cpu_model_info { + int vendor; + int family; + char *model_names[16]; +}; + +/* attempt to consolidate cpu attributes */ +struct cpu_dev { + char * c_vendor; + + /* some have two possibilities for cpuid string */ + char * c_ident[2]; + + struct cpu_model_info c_models[4]; + + void (*c_init)(struct cpuinfo_x86 * c); + void (*c_identify)(struct cpuinfo_x86 * c); + unsigned int (*c_size_cache)(struct cpuinfo_x86 * c, unsigned int size); +}; + +/* + * Structure definitions for SMP machines following the + * Intel Multiprocessing Specification 1.1 and 1.4. + */ + +/* + * This tag identifies where the SMP configuration + * information is. + */ + +#define SMP_MAGIC_IDENT (('_'<<24)|('P'<<16)|('M'<<8)|'_') + +struct intel_mp_floating +{ + char mpf_signature[4]; /* "_MP_" */ + uint32_t mpf_physptr; /* Configuration table address */ + uint8_t mpf_length; /* Our length (paragraphs) */ + uint8_t mpf_specification; /* Specification version */ + uint8_t mpf_checksum; /* Checksum (makes sum 0) */ + uint8_t mpf_feature1; /* Standard or configuration ? */ + uint8_t mpf_feature2; /* Bit7 set for IMCR|PIC */ + uint8_t mpf_feature3; /* Unused (0) */ + uint8_t mpf_feature4; /* Unused (0) */ + uint8_t mpf_feature5; /* Unused (0) */ +}; + + +extern void get_cpu_vendor(struct cpuinfo_x86 *c); +extern void detect_cpu(s_cpu *cpu); diff --git a/com32/gplinclude/dmi/dmi.h b/com32/gplinclude/dmi/dmi.h new file mode 100644 index 0000000..89782a9 --- /dev/null +++ b/com32/gplinclude/dmi/dmi.h @@ -0,0 +1,85 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006 Erwan Velu - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef DMI_H +#define DMI_H +#include +#define MAX_DMI_MEMORY_ITEMS 32 + +#define PAGE_SIZE 4096 + +extern const char *out_of_spec; +extern const char *bad_index; + +#define WORD(x) (uint16_t)(*(const uint16_t *)(x)) +#define DWORD(x) (uint32_t)(*(const uint32_t *)(x)) +#define QWORD(x) (*(const uint64_t *)(x)) + +enum {DMI_TABLE_PRESENT = 100, ENODMITABLE}; + +#include "dmi_bios.h" +#include "dmi_system.h" +#include "dmi_base_board.h" +#include "dmi_chassis.h" +#include "dmi_processor.h" +#include "dmi_memory.h" +#include "dmi_battery.h" +#include "dmi_ipmi.h" + +extern char display_line; +#define moreprintf(...) do { display_line++; if (display_line == 24) { char tempbuf[10]; display_line=0; printf("Press enter to continue"); fgets(tempbuf, sizeof tempbuf, stdin);} printf ( __VA_ARGS__); } while (0); + +typedef struct { +uint16_t num; +uint16_t len; +uint16_t ver; +uint32_t base; +uint16_t major_version; +uint16_t minor_version; +} dmi_table; + + + +struct dmi_header +{ + uint8_t type; + uint8_t length; + uint16_t handle; + uint8_t *data; +}; + +typedef struct { + s_bios bios; + s_system system; + s_base_board base_board; + s_chassis chassis; + s_processor processor; + s_battery battery; + s_memory memory[MAX_DMI_MEMORY_ITEMS]; + s_ipmi ipmi; + int memory_count; + dmi_table dmitable; +} s_dmi; + +void to_dmi_header(struct dmi_header *h, uint8_t *data); +void dmi_bios_runtime_size(uint32_t code, s_dmi *dmi); +const char *dmi_string(struct dmi_header *dm, uint8_t s); +int dmi_checksum(uint8_t *buf); +void parse_dmitable(s_dmi *dmi); +void dmi_decode(struct dmi_header *h, uint16_t ver, s_dmi *dmi); +int dmi_iterate(s_dmi *dmi); + +/* dmi_utils.c */ +void display_bios_characteristics(s_dmi *dmi); +void display_base_board_features(s_dmi *dmi); +void display_processor_flags(s_dmi *dmi); +#endif diff --git a/com32/gplinclude/dmi/dmi_base_board.h b/com32/gplinclude/dmi/dmi_base_board.h new file mode 100644 index 0000000..5b92b84 --- /dev/null +++ b/com32/gplinclude/dmi/dmi_base_board.h @@ -0,0 +1,53 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006 Erwan Velu - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef DMI_BASE_BOARD_H +#define DMI_BASE_BOARD_H + +#include "stdbool.h" +#define BASE_BOARD_MANUFACTURER_SIZE 32 +#define BASE_BOARD_PRODUCT_NAME_SIZE 32 +#define BASE_BOARD_VERSION_SIZE 16 +#define BASE_BOARD_SERIAL_SIZE 32 +#define BASE_BOARD_ASSET_TAG_SIZE 32 +#define BASE_BOARD_LOCATION_SIZE 32 +#define BASE_BOARD_FEATURES_SIZE 32 +#define BASE_BOARD_TYPE_SIZE 32 + +#define BASE_BOARD_NB_ELEMENTS 5 + +extern const char *base_board_features_strings[]; + +/* this struct have BASE_BOARD_NB_ELEMENTS */ +/* each bool is associated to the relevant message above */ +typedef struct { +bool hosting; +bool board_needs_daughter; +bool removable; +bool replaceable; +bool hot_swappable; +} __attribute__((__packed__)) s_base_board_features; + +typedef struct { +char manufacturer[BASE_BOARD_MANUFACTURER_SIZE]; +char product_name[BASE_BOARD_PRODUCT_NAME_SIZE]; +char version[BASE_BOARD_VERSION_SIZE]; +char serial[BASE_BOARD_SERIAL_SIZE]; +char asset_tag[BASE_BOARD_ASSET_TAG_SIZE]; +char location[BASE_BOARD_LOCATION_SIZE]; +char type[BASE_BOARD_TYPE_SIZE]; +s_base_board_features features; +/* The filled field have to be set to true when the dmitable implement that item */ +bool filled; +} s_base_board; + +#endif diff --git a/com32/gplinclude/dmi/dmi_battery.h b/com32/gplinclude/dmi/dmi_battery.h new file mode 100644 index 0000000..72c1969 --- /dev/null +++ b/com32/gplinclude/dmi/dmi_battery.h @@ -0,0 +1,57 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef DMI_BATTERY_H +#define DMI_BATTERY_H + +#include +#include + +#define BATTERY_LOCATION_SIZE 255 +#define BATTERY_MANUFACTURER_SIZE 255 +#define BATTERY_MANUFACTURE_DATE_SIZE 255 +#define BATTERY_SERIAL_SIZE 255 +#define BATTERY_DEVICE_NAME_SIZE 255 +#define BATTERY_CHEMISTRY_SIZE 32 +#define BATTERY_CAPACITY_SIZE 16 +#define BATTERY_DESIGN_VOLTAGE_SIZE 16 +#define BATTERY_SBDS_SIZE 255 +#define BATTERY_MAXIMUM_ERROR_SIZE 32 +#define BATTERY_SBDS_SERIAL_SIZE 32 +#define BATTERY_SBDS_MANUFACTURE_DATE_SIZE 255 +#define BATTERY_SBDS_CHEMISTRY_SIZE 16 +#define BATTERY_OEM_INFO_SIZE 255 + +typedef struct { +char location[BATTERY_LOCATION_SIZE]; +char manufacturer[BATTERY_MANUFACTURER_SIZE]; +char manufacture_date[BATTERY_MANUFACTURE_DATE_SIZE]; +char serial[BATTERY_SERIAL_SIZE]; +char name[BATTERY_DEVICE_NAME_SIZE]; +char chemistry[BATTERY_CHEMISTRY_SIZE]; +char design_capacity[BATTERY_CAPACITY_SIZE]; +char design_voltage[BATTERY_DESIGN_VOLTAGE_SIZE]; +char sbds[BATTERY_SBDS_SIZE]; +char sbds_serial[BATTERY_SBDS_SERIAL_SIZE]; +char maximum_error[BATTERY_MAXIMUM_ERROR_SIZE]; +char sbds_manufacture_date[BATTERY_SBDS_MANUFACTURE_DATE_SIZE]; +char sbds_chemistry[BATTERY_SBDS_CHEMISTRY_SIZE]; +char oem_info[BATTERY_OEM_INFO_SIZE]; +/* The filled field have to be set to true when the dmitable implement that item */ +bool filled; +} s_battery; + +const char *dmi_battery_chemistry(uint8_t code); +void dmi_battery_capacity(uint16_t code, uint8_t multiplier,char *capacity); +void dmi_battery_voltage(uint16_t code, char *voltage); +void dmi_battery_maximum_error(uint8_t code, char *error); +#endif diff --git a/com32/gplinclude/dmi/dmi_bios.h b/com32/gplinclude/dmi/dmi_bios.h new file mode 100644 index 0000000..546bbef --- /dev/null +++ b/com32/gplinclude/dmi/dmi_bios.h @@ -0,0 +1,107 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006 Erwan Velu - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef DMI_BIOS_H +#define DMI_BIOS_H + +#include "stdbool.h" +#define BIOS_VENDOR_SIZE 32 +#define BIOS_VERSION_SIZE 32 +#define BIOS_RELEASE_SIZE 16 +#define BIOS_RUNTIME_SIZE_UNIT_SIZE 16 +#define BIOS_ROM_UNIT_SIZE 16 +#define BIOS_BIOS_REVISION_SIZE 16 +#define BIOS_FIRMWARE_REVISION_SIZE 16 + +#define BIOS_CHAR_NB_ELEMENTS 28 +#define BIOS_CHAR_X1_NB_ELEMENTS 8 +#define BIOS_CHAR_X2_NB_ELEMENTS 3 + +extern const char *bios_charac_strings[]; + +/* this struct has BIOS_CHAR_NB_ELEMENTS */ +/* each bool is associated with the relevant message above */ +typedef struct { +bool bios_characteristics_not_supported; +bool isa; +bool mca; +bool eisa; +bool pci; +bool pc_card; +bool pnp; +bool apm; +bool bios_upgreadable; +bool bios_shadowing; +bool vlb; +bool escd; +bool boot_from_cd; +bool selectable_boot; +bool bios_rom_socketed; +bool edd; +bool japanese_floppy_nec_9800_1_2MB; +bool japanese_floppy_toshiba_1_2MB; +bool floppy_5_25_360KB; +bool floppy_5_25_1_2MB; +bool floppy_3_5_720KB; +bool floppy_3_5_2_88MB; +bool print_screen; +bool keyboard_8042_support; +bool serial_support; +bool printer_support; +bool cga_mono_support; +bool nec_pc_98; +} __attribute__((__packed__)) s_characteristics; + +extern const char *bios_charac_x1_strings[]; + +/* this struct has BIOS_CHAR_X1_NB_ELEMENTS */ +/* each bool is associated with the relevant message above */ +typedef struct { +bool acpi; +bool usb_legacy; +bool agp; +bool i2o_boot; +bool ls_120_boot; +bool zip_drive_boot; +bool ieee_1394_boot; +bool smart_battery; +} __attribute__((__packed__)) s_characteristics_x1; + +extern const char *bios_charac_x2_strings[]; + +/* this struct has BIOS_CHAR_X2_NB_ELEMENTS */ +/* each bool is associated with the relevant message above */ +typedef struct { +bool bios_boot_specification; +bool bios_network_boot_by_keypress; +bool target_content_distribution; +} __attribute__((__packed__)) s_characteristics_x2; + +typedef struct { +char vendor[BIOS_VENDOR_SIZE]; +char version[BIOS_VERSION_SIZE]; +char release_date[BIOS_RELEASE_SIZE]; +uint16_t address; +uint16_t runtime_size; +char runtime_size_unit[BIOS_RUNTIME_SIZE_UNIT_SIZE]; +uint16_t rom_size; +char rom_size_unit[BIOS_ROM_UNIT_SIZE]; +s_characteristics characteristics; +s_characteristics_x1 characteristics_x1; +s_characteristics_x2 characteristics_x2; +char bios_revision [BIOS_BIOS_REVISION_SIZE]; +char firmware_revision [BIOS_FIRMWARE_REVISION_SIZE]; +/* The filled field have to be set to true when the dmitable implement that item */ +bool filled; +} s_bios; + +#endif diff --git a/com32/gplinclude/dmi/dmi_chassis.h b/com32/gplinclude/dmi/dmi_chassis.h new file mode 100644 index 0000000..96711ed --- /dev/null +++ b/com32/gplinclude/dmi/dmi_chassis.h @@ -0,0 +1,50 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006 Erwan Velu - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef DMI_CHASSIS_H +#define DMI_CHASSIS_H + +#define CHASSIS_MANUFACTURER_SIZE 32 +#define CHASSIS_TYPE_SIZE 16 +#define CHASSIS_LOCK_SIZE 16 +#define CHASSIS_VERSION_SIZE 16 +#define CHASSIS_SERIAL_SIZE 32 +#define CHASSIS_ASSET_TAG_SIZE 32 +#define CHASSIS_BOOT_UP_STATE_SIZE 32 +#define CHASSIS_POWER_SUPPLY_STATE_SIZE 32 +#define CHASSIS_THERMAL_STATE_SIZE 32 +#define CHASSIS_SECURITY_STATUS_SIZE 32 +#define CHASSIS_OEM_INFORMATION_SIZE 32 + +typedef struct { +char manufacturer[CHASSIS_MANUFACTURER_SIZE]; +char type[CHASSIS_TYPE_SIZE]; +char lock[CHASSIS_LOCK_SIZE]; +char version[CHASSIS_VERSION_SIZE]; +char serial[CHASSIS_SERIAL_SIZE]; +char asset_tag[CHASSIS_ASSET_TAG_SIZE]; +char boot_up_state[CHASSIS_BOOT_UP_STATE_SIZE]; +char power_supply_state[CHASSIS_POWER_SUPPLY_STATE_SIZE]; +char thermal_state[CHASSIS_THERMAL_STATE_SIZE]; +char security_status[CHASSIS_SECURITY_STATUS_SIZE]; +char oem_information[CHASSIS_OEM_INFORMATION_SIZE]; +uint16_t height; +uint16_t nb_power_cords; +/* The filled field have to be set to true when the dmitable implement that item */ +bool filled; +} s_chassis; + +const char *dmi_chassis_type(uint8_t code); +const char *dmi_chassis_lock(uint8_t code); +const char *dmi_chassis_state(uint8_t code); +const char *dmi_chassis_security_status(uint8_t code); +#endif diff --git a/com32/gplinclude/dmi/dmi_ipmi.h b/com32/gplinclude/dmi/dmi_ipmi.h new file mode 100644 index 0000000..7dcb71b --- /dev/null +++ b/com32/gplinclude/dmi/dmi_ipmi.h @@ -0,0 +1,33 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006-2009 Erwan Velu - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef DMI_IPMI_H +#define DMI_IPMI_H + +#define IPMI_INTERFACE_TYPE_SIZE 32 +#define IPMI_MEMORY_MODEL_SIZE 16 + +typedef struct { +char interface_type[IPMI_INTERFACE_TYPE_SIZE]; +uint8_t major_specification_version; +uint8_t minor_specification_version; +uint8_t I2C_slave_address; +uint16_t nv_address; +uint64_t base_address; + +uint8_t irq; +bool filled; +} s_ipmi; + +void dmi_ipmi_base_address(uint8_t type, const uint8_t *p, s_ipmi *ipmi); +const char *dmi_ipmi_interface_type(uint8_t code); +#endif diff --git a/com32/gplinclude/dmi/dmi_memory.h b/com32/gplinclude/dmi/dmi_memory.h new file mode 100644 index 0000000..2b744b5 --- /dev/null +++ b/com32/gplinclude/dmi/dmi_memory.h @@ -0,0 +1,61 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef DMI_MEMORY_H +#define DMI_MEMORY_H + +#define MEMORY_MANUFACTURER_SIZE 32 +#define MEMORY_ERROR_SIZE 16 +#define MEMORY_TOTAL_WIDTH_SIZE 16 +#define MEMORY_DATA_WIDTH_SIZE 16 +#define MEMORY_SIZE_SIZE 32 +#define MEMORY_FORM_FACTOR_SIZE 32 +#define MEMORY_DEVICE_SET_SIZE 32 +#define MEMORY_DEVICE_LOCATOR_SIZE 32 +#define MEMORY_BANK_LOCATOR_SIZE 32 +#define MEMORY_TYPE_SIZE 32 +#define MEMORY_TYPE_DETAIL_SIZE 16 +#define MEMORY_SPEED_SIZE 16 +#define MEMORY_SERIAL_SIZE 16 +#define MEMORY_ASSET_TAG_SIZE 16 +#define MEMORY_PART_NUMBER_SIZE 16 + +typedef struct { +char manufacturer[MEMORY_MANUFACTURER_SIZE]; +char error[MEMORY_ERROR_SIZE]; +char total_width[MEMORY_TOTAL_WIDTH_SIZE]; +char data_width[MEMORY_DATA_WIDTH_SIZE]; +char size[MEMORY_SIZE_SIZE]; +char form_factor[MEMORY_FORM_FACTOR_SIZE]; +char device_set[MEMORY_DEVICE_SET_SIZE]; +char device_locator[MEMORY_DEVICE_LOCATOR_SIZE]; +char bank_locator[MEMORY_BANK_LOCATOR_SIZE]; +char type[MEMORY_TYPE_SIZE]; +char type_detail[MEMORY_TYPE_DETAIL_SIZE]; +char speed[MEMORY_SPEED_SIZE]; +char serial[MEMORY_SERIAL_SIZE]; +char asset_tag[MEMORY_ASSET_TAG_SIZE]; +char part_number[MEMORY_PART_NUMBER_SIZE]; +/* The filled field have to be set to true when the dmitable implement that item */ +bool filled; +} s_memory; + +void dmi_memory_array_error_handle(uint16_t code,char *array); +void dmi_memory_device_width(uint16_t code, char *width); +void dmi_memory_device_size(uint16_t code, char *size); +const char *dmi_memory_device_form_factor(uint8_t code); +void dmi_memory_device_set(uint8_t code, char *set); +const char *dmi_memory_device_type(uint8_t code); +void dmi_memory_device_type_detail(uint16_t code,char *type_detail); +void dmi_memory_device_speed(uint16_t code, char *speed); + +#endif diff --git a/com32/gplinclude/dmi/dmi_processor.h b/com32/gplinclude/dmi/dmi_processor.h new file mode 100644 index 0000000..79ee43d --- /dev/null +++ b/com32/gplinclude/dmi/dmi_processor.h @@ -0,0 +1,111 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006 Erwan Velu - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef DMI_PROCESSOR_H +#define DMI_PROCESSOR_H + +#include "stdbool.h" +#include "string.h" +#define PROCESSOR_SOCKET_DESIGNATION_SIZE 32 +#define PROCESSOR_TYPE_SIZE 32 +#define PROCESSOR_FAMILY_SIZE 32 +#define PROCESSOR_MANUFACTURER_SIZE 64 +#define PROCESSOR_VERSION_SIZE 32 +#define PROCESSOR_VOLTAGE_SIZE 16 +#define PROCESSOR_STATUS_SIZE 16 +#define PROCESSOR_UPGRADE_SIZE 16 +#define PROCESSOR_CACHE_SIZE 16 +#define PROCESSOR_SERIAL_SIZE 32 +#define PROCESSOR_ASSET_TAG_SIZE 32 +#define PROCESSOR_PART_NUMBER_SIZE 32 +#define PROCESSOR_ID_SIZE 32 + +#define PROCESSOR_FLAGS_ELEMENTS 32 +/* Intel AP-485 revision 28, table 5 */ +extern const char *cpu_flags_strings[PROCESSOR_FLAGS_ELEMENTS]; + +/* this struct have PROCESSOR_FLAGS_ELEMENTS */ +/* each bool is associated to the relevant message above */ +typedef struct { +bool fpu; +bool vme; +bool de; +bool pse; +bool tsc; +bool msr; +bool pae; +bool mce; +bool cx8; +bool apic; +bool null_10; +bool sep; +bool mtrr; +bool pge; +bool mca; +bool cmov; +bool pat; +bool pse_36; +bool psn; +bool clfsh; +bool null_20; +bool ds; +bool acpi; +bool mmx; +bool fxsr; +bool sse; +bool sse2; +bool ss; +bool htt; +bool tm; +bool null_30; +bool pbe; +} __attribute__((__packed__)) s_dmi_cpu_flags; + +typedef struct { +uint8_t type; +uint8_t family; +uint8_t model; +uint8_t stepping; +uint8_t minor_stepping; +} __attribute__((__packed__)) s_signature; + +typedef struct { +char socket_designation[PROCESSOR_SOCKET_DESIGNATION_SIZE]; +char type[PROCESSOR_TYPE_SIZE]; +char family[PROCESSOR_FAMILY_SIZE]; +char manufacturer[PROCESSOR_MANUFACTURER_SIZE]; +char version[PROCESSOR_VERSION_SIZE]; +float voltage; +uint16_t external_clock; +uint16_t max_speed; +uint16_t current_speed; +char status[PROCESSOR_STATUS_SIZE]; +char upgrade[PROCESSOR_UPGRADE_SIZE]; +char cache1[PROCESSOR_CACHE_SIZE]; +char cache2[PROCESSOR_CACHE_SIZE]; +char cache3[PROCESSOR_CACHE_SIZE]; +char serial[PROCESSOR_SERIAL_SIZE]; +char asset_tag[PROCESSOR_ASSET_TAG_SIZE]; +char part_number[PROCESSOR_PART_NUMBER_SIZE]; +char id[PROCESSOR_ID_SIZE]; +s_dmi_cpu_flags cpu_flags; +s_signature signature; +/* The filled field have to be set to true when the dmitable implement that item */ +bool filled; +} s_processor; + +const char *dmi_processor_type(uint8_t code); +const char *dmi_processor_family(uint8_t code, char *manufacturer); +const char *dmi_processor_status(uint8_t code); +const char *dmi_processor_upgrade(uint8_t code); +void dmi_processor_cache(uint16_t code, const char *level, uint16_t ver, char *cache); +#endif diff --git a/com32/gplinclude/dmi/dmi_system.h b/com32/gplinclude/dmi/dmi_system.h new file mode 100644 index 0000000..5a461d5 --- /dev/null +++ b/com32/gplinclude/dmi/dmi_system.h @@ -0,0 +1,38 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006 Erwan Velu - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef DMI_SYSTEM_H +#define DMI_SYSTEM_H + +#define SYSTEM_MANUFACTURER_SIZE 32 +#define SYSTEM_PRODUCT_NAME_SIZE 32 +#define SYSTEM_VERSION_SIZE 16 +#define SYSTEM_SERIAL_SIZE 32 +#define SYSTEM_UUID_SIZE 40 +#define SYSTEM_WAKEUP_TYPE_SIZE 32 +#define SYSTEM_SKU_NUMBER_SIZE 32 +#define SYSTEM_FAMILY_SIZE 32 + +typedef struct { +char manufacturer[SYSTEM_MANUFACTURER_SIZE]; +char product_name[SYSTEM_PRODUCT_NAME_SIZE]; +char version[SYSTEM_VERSION_SIZE]; +char serial[SYSTEM_SERIAL_SIZE]; +char uuid[SYSTEM_UUID_SIZE]; +char wakeup_type[SYSTEM_WAKEUP_TYPE_SIZE]; +char sku_number[SYSTEM_SKU_NUMBER_SIZE]; +char family[SYSTEM_FAMILY_SIZE]; +/* The filled field have to be set to true when the dmitable implement that item */ +bool filled; +} s_system; + +#endif diff --git a/com32/gplinclude/vpd/vpd.h b/com32/gplinclude/vpd/vpd.h new file mode 100644 index 0000000..4bb1afc --- /dev/null +++ b/com32/gplinclude/vpd/vpd.h @@ -0,0 +1,33 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006 Erwan Velu - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef VPD_H +#define VPD_H +#include +#include + +enum {VPD_TABLE_PRESENT = 100, ENOVPDTABLE}; + +typedef struct { + char bios_build_id[10]; + char box_serial_number[8]; + char motherboard_serial_number[12]; + char machine_type_model[8]; + char bios_release_date[9]; + char default_flash_filename[13]; + char bios_version[255]; + char base_address[6]; + bool filled; +} s_vpd; + +int vpd_decode(s_vpd *vpd); +#endif diff --git a/com32/gpllib/Makefile b/com32/gpllib/Makefile new file mode 100644 index 0000000..8e47d93 --- /dev/null +++ b/com32/gpllib/Makefile @@ -0,0 +1,45 @@ +# +# LGPL/GPL code library +# + +# Include configuration rules +topdir = ../.. +include ../lib/MCONFIG + +REQFLAGS += -I../gplinclude + +LIBOBJS = dmi/dmi_battery.o dmi/dmi_chassis.o dmi/dmi_memory.o \ + dmi/dmi_processor.o dmi/dmi.o dmi/dmi_bios.o dmi/dmi_base_board.o \ + dmi/dmi_ipmi.o cpuid.o vpd/vpd.o + +BINDIR = /usr/bin +LIBDIR = /usr/lib +DATADIR = /usr/share +AUXDIR = $(DATADIR)/syslinux +INCDIR = /usr/include +COM32DIR = $(AUXDIR)/com32 + +all: libcom32gpl.a + +libcom32gpl.a : $(LIBOBJS) + rm -f $@ + $(AR) cq $@ $^ + $(RANLIB) $@ + +tidy dist clean: + find . \( -name \*.o -o -name \*.a -o -name .\*.d -o -name \*.tmp \) -print0 | \ + xargs -0r rm -f + +spotless: clean + rm -f *.a + rm -f *~ \#* */*~ */\#* + +# Mixing in the GPL include files is suboptimal, but I'm not sure +# there is a better way to do it. +install: all + mkdir -m 755 -p $(INSTALLROOT)$(COM32DIR) + install -m 644 libcom32gpl.a $(INSTALLROOT)$(COM32DIR) + mkdir -p $(INSTALLROOT)$(COM32DIR)/include/ + cp -r ../gplinclude $(INSTALLROOT)$(COM32DIR)/include/ + +-include .*.d */.*.d */*/.*.d diff --git a/com32/gpllib/cpuid.c b/com32/gpllib/cpuid.c new file mode 100644 index 0000000..ed3224d --- /dev/null +++ b/com32/gpllib/cpuid.c @@ -0,0 +1,328 @@ +/* + * Portions of this file taken from the Linux kernel, + * Copyright 1991-2009 Linus Torvalds and contributors + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include "cpuid.h" + +struct cpu_dev * cpu_devs[X86_VENDOR_NUM] = {}; + +/* +* CPUID functions returning a single datum +*/ + +/* Probe for the CPUID instruction */ +static int have_cpuid_p(void) +{ + return cpu_has_eflag(X86_EFLAGS_ID); +} + +static struct cpu_dev amd_cpu_dev = { + .c_vendor = "AMD", + .c_ident = { "AuthenticAMD" } +}; + +static struct cpu_dev intel_cpu_dev = { + .c_vendor = "Intel", + .c_ident = { "GenuineIntel" } +}; + +static struct cpu_dev cyrix_cpu_dev = { + .c_vendor = "Cyrix", + .c_ident = { "CyrixInstead" } +}; + +static struct cpu_dev umc_cpu_dev = { + .c_vendor = "UMC", + .c_ident = { "UMC UMC UMC" } + +}; + +static struct cpu_dev nexgen_cpu_dev = { + .c_vendor = "Nexgen", + .c_ident = { "NexGenDriven" } +}; + +static struct cpu_dev centaur_cpu_dev = { + .c_vendor = "Centaur", + .c_ident = { "CentaurHauls" } +}; + +static struct cpu_dev rise_cpu_dev = { + .c_vendor = "Rise", + .c_ident = { "RiseRiseRise" } +}; + +static struct cpu_dev transmeta_cpu_dev = { + .c_vendor = "Transmeta", + .c_ident = { "GenuineTMx86", "TransmetaCPU" } +}; + +void init_cpu_devs(void) +{ + cpu_devs[X86_VENDOR_INTEL] = &intel_cpu_dev; + cpu_devs[X86_VENDOR_CYRIX] = &cyrix_cpu_dev; + cpu_devs[X86_VENDOR_AMD] = &amd_cpu_dev; + cpu_devs[X86_VENDOR_UMC] = &umc_cpu_dev; + cpu_devs[X86_VENDOR_NEXGEN] = &nexgen_cpu_dev; + cpu_devs[X86_VENDOR_CENTAUR] = ¢aur_cpu_dev; + cpu_devs[X86_VENDOR_RISE] = &rise_cpu_dev; + cpu_devs[X86_VENDOR_TRANSMETA] = &transmeta_cpu_dev; +} + +void get_cpu_vendor(struct cpuinfo_x86 *c) +{ + char *v = c->x86_vendor_id; + int i; + init_cpu_devs(); + for (i = 0; i < X86_VENDOR_NUM; i++) { + if (cpu_devs[i]) { + if (!strcmp(v,cpu_devs[i]->c_ident[0]) || + (cpu_devs[i]->c_ident[1] && + !strcmp(v,cpu_devs[i]->c_ident[1]))) { + c->x86_vendor = i; + return; + } + } + } + + c->x86_vendor = X86_VENDOR_UNKNOWN; +} + +int get_model_name(struct cpuinfo_x86 *c) +{ + unsigned int *v; + char *p, *q; + + if (cpuid_eax(0x80000000) < 0x80000004) + return 0; + + v = (unsigned int *) c->x86_model_id; + cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]); + cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]); + cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]); + c->x86_model_id[48] = 0; + + /* Intel chips right-justify this string for some dumb reason; + undo that brain damage */ + p = q = &c->x86_model_id[0]; + while ( *p == ' ' ) + p++; + if ( p != q ) { + while ( *p ) + *q++ = *p++; + while ( q <= &c->x86_model_id[48] ) + *q++ = '\0'; /* Zero-pad the rest */ + } + + return 1; +} + +void generic_identify(struct cpuinfo_x86 *c) +{ + uint32_t tfms, xlvl; + unsigned int ebx; + + /* Get vendor name */ + cpuid(0x00000000, + (uint32_t *)&c->cpuid_level, + (uint32_t *)&c->x86_vendor_id[0], + (uint32_t *)&c->x86_vendor_id[8], + (uint32_t *)&c->x86_vendor_id[4]); + + get_cpu_vendor(c); + /* Intel-defined flags: level 0x00000001 */ + if ( c->cpuid_level >= 0x00000001 ) { + uint32_t capability, excap; + cpuid(0x00000001, &tfms, &ebx, &excap, &capability); + c->x86_capability[0] = capability; + c->x86_capability[4] = excap; + c->x86 = (tfms >> 8) & 15; + c->x86_model = (tfms >> 4) & 15; + if (c->x86 == 0xf) + c->x86 += (tfms >> 20) & 0xff; + if (c->x86 >= 0x6) + c->x86_model += ((tfms >> 16) & 0xF) << 4; + c->x86_mask = tfms & 15; + if (cpu_has(c, X86_FEATURE_CLFLSH)) + c->x86_clflush_size = ((ebx >> 8) & 0xff) * 8; + } else { + /* Have CPUID level 0 only - unheard of */ + c->x86 = 4; + } + + /* AMD-defined flags: level 0x80000001 */ + xlvl = cpuid_eax(0x80000000); + if ( (xlvl & 0xffff0000) == 0x80000000 ) { + if ( xlvl >= 0x80000001 ) { + c->x86_capability[1] = cpuid_edx(0x80000001); + c->x86_capability[6] = cpuid_ecx(0x80000001); + } + if ( xlvl >= 0x80000004 ) + get_model_name(c); /* Default name */ + } +} + +/* + * Checksum an MP configuration block. + */ + +static int mpf_checksum(unsigned char *mp, int len) +{ + int sum = 0; + + while (len--) + sum += *mp++; + + return sum & 0xFF; +} + +static int smp_scan_config (unsigned long base, unsigned long length) +{ + unsigned long *bp = (unsigned long *)base; + struct intel_mp_floating *mpf; + +// printf("Scan SMP from %p for %ld bytes.\n", bp,length); + if (sizeof(*mpf) != 16) { + printf("Error: MPF size\n"); + return 0; + } + + while (length > 0) { + mpf = (struct intel_mp_floating *)bp; + if ((*bp == SMP_MAGIC_IDENT) && + (mpf->mpf_length == 1) && + !mpf_checksum((unsigned char *)bp, 16) && + ((mpf->mpf_specification == 1) + || (mpf->mpf_specification == 4)) ) { + return 1; + } + bp += 4; + length -= 16; + } + return 0; +} + +int find_smp_config (void) +{ +// unsigned int address; + + /* + * FIXME: Linux assumes you have 640K of base ram.. + * this continues the error... + * + * 1) Scan the bottom 1K for a signature + * 2) Scan the top 1K of base RAM + * 3) Scan the 64K of bios + */ + if (smp_scan_config(0x0,0x400) || + smp_scan_config(639*0x400,0x400) || + smp_scan_config(0xF0000,0x10000)) + return 1; + /* + * If it is an SMP machine we should know now, unless the + * configuration is in an EISA/MCA bus machine with an + * extended bios data area. + * + * there is a real-mode segmented pointer pointing to the + * 4K EBDA area at 0x40E, calculate and scan it here. + * + * NOTE! There are Linux loaders that will corrupt the EBDA + * area, and as such this kind of SMP config may be less + * trustworthy, simply because the SMP table may have been + * stomped on during early boot. These loaders are buggy and + * should be fixed. + * + * MP1.4 SPEC states to only scan first 1K of 4K EBDA. + */ + +// address = get_bios_ebda(); +// if (address) +// smp_scan_config(address, 0x400); + return 0; +} + + +void set_cpu_flags(struct cpuinfo_x86 *c, s_cpu *cpu) { +cpu->flags.fpu=cpu_has(c, X86_FEATURE_FPU); +cpu->flags.vme=cpu_has(c, X86_FEATURE_VME); +cpu->flags.de=cpu_has(c, X86_FEATURE_DE); +cpu->flags.pse=cpu_has(c, X86_FEATURE_PSE); +cpu->flags.tsc=cpu_has(c, X86_FEATURE_TSC); +cpu->flags.msr=cpu_has(c, X86_FEATURE_MSR); +cpu->flags.pae=cpu_has(c, X86_FEATURE_PAE); +cpu->flags.mce=cpu_has(c, X86_FEATURE_MCE); +cpu->flags.cx8=cpu_has(c, X86_FEATURE_CX8); +cpu->flags.apic=cpu_has(c, X86_FEATURE_APIC); +cpu->flags.sep=cpu_has(c, X86_FEATURE_SEP); +cpu->flags.mtrr=cpu_has(c, X86_FEATURE_MTRR); +cpu->flags.pge=cpu_has(c, X86_FEATURE_PGE); +cpu->flags.mca=cpu_has(c, X86_FEATURE_MCA); +cpu->flags.cmov=cpu_has(c, X86_FEATURE_CMOV); +cpu->flags.pat=cpu_has(c, X86_FEATURE_PAT); +cpu->flags.pse_36=cpu_has(c, X86_FEATURE_PSE36); +cpu->flags.psn=cpu_has(c, X86_FEATURE_PN); +cpu->flags.clflsh=cpu_has(c, X86_FEATURE_CLFLSH); +cpu->flags.dts=cpu_has(c, X86_FEATURE_DTES); +cpu->flags.acpi=cpu_has(c, X86_FEATURE_ACPI); +cpu->flags.mmx=cpu_has(c, X86_FEATURE_MMX); +cpu->flags.fxsr=cpu_has(c, X86_FEATURE_FXSR); +cpu->flags.sse=cpu_has(c, X86_FEATURE_XMM); +cpu->flags.sse2=cpu_has(c, X86_FEATURE_XMM2); +cpu->flags.ss=cpu_has(c, X86_FEATURE_SELFSNOOP); +cpu->flags.htt=cpu_has(c, X86_FEATURE_HT); +cpu->flags.acc=cpu_has(c, X86_FEATURE_ACC); +cpu->flags.syscall=cpu_has(c, X86_FEATURE_SYSCALL); +cpu->flags.mp=cpu_has(c, X86_FEATURE_MP); +cpu->flags.nx=cpu_has(c, X86_FEATURE_NX); +cpu->flags.mmxext=cpu_has(c, X86_FEATURE_MMXEXT); +cpu->flags.lm=cpu_has(c, X86_FEATURE_LM); +cpu->flags.nowext=cpu_has(c, X86_FEATURE_3DNOWEXT); +cpu->flags.now=cpu_has(c, X86_FEATURE_3DNOW); +cpu->flags.smp = find_smp_config(); +} + +void set_generic_info(struct cpuinfo_x86 *c,s_cpu *cpu) { + cpu->family=c->x86; + cpu->vendor_id=c->x86_vendor; + cpu->model_id=c->x86_model; + cpu->stepping=c->x86_mask; + strncpy(cpu->vendor,cpu_devs[c->x86_vendor]->c_vendor,sizeof(cpu->vendor)); + strncpy(cpu->model,c->x86_model_id,sizeof(cpu->model)); +} + +void detect_cpu(s_cpu *cpu) +{ + struct cpuinfo_x86 c; + c.x86_clflush_size = 32; + c.x86_cache_size = -1; + c.x86_vendor = X86_VENDOR_UNKNOWN; + c.cpuid_level = -1; /* CPUID not detected */ + c.x86_model = c.x86_mask = 0; /* So far unknown... */ + c.x86_max_cores = 1; + memset(&c.x86_capability, 0, sizeof(c.x86_capability)); + memset(&c.x86_vendor_id,0,sizeof(c.x86_vendor_id)); + memset(&c.x86_model_id,0,sizeof(c.x86_model_id)); + + if (!have_cpuid_p()) + return; + + generic_identify(&c); + set_generic_info(&c,cpu); + set_cpu_flags(&c,cpu); +} diff --git a/com32/gpllib/dmi/dmi.c b/com32/gpllib/dmi/dmi.c new file mode 100644 index 0000000..23caa33 --- /dev/null +++ b/com32/gpllib/dmi/dmi.c @@ -0,0 +1,618 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- +*/ + +#include +#include +#include "dmi/dmi.h" + +const char *out_of_spec = ""; +const char *bad_index = ""; + +void dmi_bios_runtime_size(uint32_t code, s_dmi *dmi) +{ + if(code&0x000003FF) { + dmi->bios.runtime_size=code; + strcpy(dmi->bios.runtime_size_unit,"bytes"); + } else { + dmi->bios.runtime_size=code >>10; + strcpy(dmi->bios.runtime_size_unit,"KB"); + + } +} + +void dmi_bios_characteristics(uint64_t code, s_dmi *dmi) +{ + int i; + /* + * This isn't very clear what this bit is supposed to mean + */ + //if(code.l&(1<<3)) + if(code&&(1<<3)) + { + ((bool *)(& dmi->bios.characteristics))[0]=true; + return; + } + + for(i=4; i<=31; i++) + //if(code.l&(1<bios.characteristics))[i-3]=true; +} + +void dmi_bios_characteristics_x1(uint8_t code, s_dmi *dmi) +{ + int i; + + for(i=0; i<=7; i++) + if(code&(1<bios.characteristics_x1))[i]=true; +} + +void dmi_bios_characteristics_x2(uint8_t code, s_dmi *dmi) +{ + int i; + + for(i=0; i<=2; i++) + if(code&(1<bios.characteristics_x2))[i]=true; +} + + +void dmi_system_uuid(uint8_t *p, s_dmi *dmi) +{ + int only0xFF=1, only0x00=1; + int i; + + for(i=0; i<16 && (only0x00 || only0xFF); i++) + { + if(p[i]!=0x00) only0x00=0; + if(p[i]!=0xFF) only0xFF=0; + } + + if(only0xFF) + { + sprintf(dmi->system.uuid,"Not Present"); + return; + } + if(only0x00) + { + sprintf(dmi->system.uuid,"Not Settable"); + return; + } + + sprintf(dmi->system.uuid,"%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], + p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); +} + +void dmi_system_wake_up_type(uint8_t code, s_dmi *dmi) +{ + /* 3.3.2.1 */ + static const char *type[]={ + "Reserved", /* 0x00 */ + "Other", + "Unknown", + "APM Timer", + "Modem Ring", + "LAN Remote", + "Power Switch", + "PCI PME#", + "AC Power Restored" /* 0x08 */ + }; + + if(code<=0x08) { + strcpy(dmi->system.wakeup_type,type[code]); + } else { + strcpy(dmi->system.wakeup_type,out_of_spec); + } +return; +} + +static void dmi_base_board_features(uint8_t code, s_dmi *dmi) +{ + if((code&0x1F)!=0) + { + int i; + + for(i=0; i<=4; i++) + if(code&(1<base_board.features))[i]=true; + } +} + +static void dmi_processor_voltage(uint8_t code, s_dmi *dmi) +{ + /* 3.3.5.4 */ + static const float voltage[]={ + 5.0, + 3.3, + 2.9 + }; + int i; + + if(code&0x80) + dmi->processor.voltage=((float)(code&0x7f)/10); + else + { + for(i=0; i<=2; i++) + if(code&(1<processor.voltage=voltage[i]; + } +} + +static void dmi_processor_id(uint8_t type, uint8_t *p, const char *version, s_dmi *dmi) +{ + /* + * Extra flags are now returned in the ECX register when one calls + * the CPUID instruction. Their meaning is explained in table 6, but + * DMI doesn't support this yet. + */ + uint32_t eax, edx; + int sig=0; + + /* + * This might help learn about new processors supporting the + * CPUID instruction or another form of identification. + */ + sprintf(dmi->processor.id,"ID: %02X %02X %02X %02X %02X %02X %02X %02X\n", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); + + if(type==0x05) /* 80386 */ + { + uint16_t dx=WORD(p); + /* + * 80386 have a different signature. + */ + dmi->processor.signature.type=(dx >>12); + dmi->processor.signature.family=((dx>>8)&0xF); + dmi->processor.signature.stepping=(dx>>4)&0xF; + dmi->processor.signature.minor_stepping=(dx&0xF); + return; + } + if(type==0x06) /* 80486 */ + { + uint16_t dx=WORD(p); + /* + * Not all 80486 CPU support the CPUID instruction, we have to find + * wether the one we have here does or not. Note that this trick + * works only because we know that 80486 must be little-endian. + */ + if((dx&0x0F00)==0x0400 + && ((dx&0x00F0)==0x0040 || (dx&0x00F0)>=0x0070) + && ((dx&0x000F)>=0x0003)) + sig=1; + else + { + dmi->processor.signature.type=((dx >>12)&0x3); + dmi->processor.signature.family=((dx>>8)&0xF); + dmi->processor.signature.model=((dx>>4)&0xF); + dmi->processor.signature.stepping=(dx&0xF); + return; + } + } + else if((type>=0x0B && type<=0x13) /* Intel, Cyrix */ + || (type>=0xB0 && type<=0xB3) /* Intel */ + || type==0xB5 /* Intel */ + || type==0xB9) /* Intel */ + sig=1; + else if((type>=0x18 && type<=0x1D) /* AMD */ + || type==0x1F /* AMD */ + || (type>=0xB6 && type<=0xB7) /* AMD */ + || (type>=0x83 && type<=0x85)) /* AMD */ + sig=2; + else if(type==0x01 || type==0x02) + { + /* + * Some X86-class CPU have family "Other" or "Unknown". In this case, + * we use the version string to determine if they are known to + * support the CPUID instruction. + */ + if(strncmp(version, "Pentium III MMX", 15)==0) + sig=1; + else if(strncmp(version, "AMD Athlon(TM)", 14)==0 + || strncmp(version, "AMD Opteron(tm)", 15)==0) + sig=2; + else + return; + } + else /* not X86-class */ + return; + + eax=DWORD(p); + edx=DWORD(p+4); + switch(sig) + { + case 1: /* Intel */ + dmi->processor.signature.type=((eax >>12)&0x3); + dmi->processor.signature.family=(((eax>>16)&0xFF0)+((eax>>8)&0x00F)); + dmi->processor.signature.model=(((eax>>12)&0xF0)+((eax>>4)&0x0F)); + dmi->processor.signature.stepping=(eax&0xF); + break; + case 2: /* AMD */ + dmi->processor.signature.family=(((eax>>8)&0xF)==0xF?(eax>>20)&0xFF:(eax>>8)&0xF); + dmi->processor.signature.model =(((eax>>4)&0xF)==0xF?(eax>>16)&0xF :(eax>>4)&0xF); + dmi->processor.signature.stepping=(eax&0xF); + break; + } + + edx=DWORD(p+4); + if((edx&0x3FF7FDFF)!=0) + { + int i; + for(i=0; i<=31; i++) + if(cpu_flags_strings[i]!=NULL && edx&(1<processor.cpu_flags))[i]=true; + //printf("%s\t%s\n", prefix, flags[i]); + } +} + + +void to_dmi_header(struct dmi_header *h, uint8_t *data) +{ + h->type=data[0]; + h->length=data[1]; + h->handle=WORD(data+2); + h->data=data; +} + +const char *dmi_string(struct dmi_header *dm, uint8_t s) +{ + char *bp=(char *)dm->data; + size_t i, len; + + if(s==0) + return "Not Specified"; + + bp+=dm->length; + while(s>1 && *bp) + { + bp+=strlen(bp); + bp++; + s--; + } + + if(!*bp) + return bad_index; + + /* ASCII filtering */ + len=strlen(bp); + for(i=0; ibase_board,0,sizeof (s_base_board)); + memset(&dmi->battery,0,sizeof (s_battery)); + memset(&dmi->bios,0,sizeof (s_bios)); + memset(&dmi->chassis,0,sizeof (s_chassis)); + for (int i=0;imemory[i],0,sizeof (s_memory)); + memset(&dmi->processor,0,sizeof (s_processor)); + memset(&dmi->system,0,sizeof (s_system)); + + /* Until we found this elements in the dmitable, we consider them as not filled */ + dmi->base_board.filled=false; + dmi->battery.filled=false; + dmi->bios.filled=false; + dmi->chassis.filled=false; + for (int i=0;imemory[i].filled=false; + dmi->processor.filled=false; + dmi->system.filled=false; + + p=(char *)0xF0000; /* The start address to look at the dmi table */ + for (q = p; q < p + 0x10000; q += 16) { + memcpy(buf, q, 15); + if(memcmp(buf, "_DMI_", 5)==0 && dmi_checksum(buf)) { + dmi->dmitable.num = buf[13]<<8|buf[12]; + dmi->dmitable.len = buf[7]<<8|buf[6]; + dmi->dmitable.base = buf[11]<<24|buf[10]<<16|buf[9]<<8|buf[8]; + dmi->dmitable.ver = (buf[0x06]<<8)+buf[0x07]; + + /* + * DMI version 0.0 means that the real version is taken from + * the SMBIOS version, which we don't know at this point. + */ + if(buf[14]!=0) { + dmi->dmitable.major_version=buf[14]>>4; + dmi->dmitable.minor_version=buf[14]&0x0F; + } + else { + dmi->dmitable.major_version=0; + dmi->dmitable.minor_version=0; + + } +/* printf("DMI present (version %d.%d)\n", dmitable.major_version,dmitable.minor_version); + printf("%d structures occupying %d bytes.\n",dmitable.num, dmitable.len); + printf("DMI table at 0x%08X.\n",dmitable.base);*/ + return DMI_TABLE_PRESENT; + } + } + dmi->dmitable.base=0; + dmi->dmitable.num=0; + dmi->dmitable.ver=0; + dmi->dmitable.len=0; + return -ENODMITABLE; +} + +void dmi_decode(struct dmi_header *h, uint16_t ver, s_dmi *dmi) +{ + uint8_t *data=h->data; + + /* + * Note: DMI types 37, 38 and 39 are untested + */ + switch(h->type) + { + case 0: /* 3.3.1 BIOS Information */ +// printf("BIOS Information\n"); + if(h->length<0x12) break; + dmi->bios.filled=true; + strcpy(dmi->bios.vendor,dmi_string(h,data[0x04])); + strcpy(dmi->bios.version,dmi_string(h,data[0x05])); + strcpy(dmi->bios.release_date,dmi_string(h,data[0x08])); + dmi->bios.address=WORD(data+0x06); + dmi_bios_runtime_size((0x10000-WORD(data+0x06))<<4,dmi); + dmi->bios.rom_size=(data[0x09]+1)<<6; + strcpy(dmi->bios.rom_size_unit,"kB"); + dmi_bios_characteristics(QWORD(data+0x0A),dmi); + + if(h->length<0x13) break; + dmi_bios_characteristics_x1(data[0x12], dmi); + if(h->length<0x14) break; + dmi_bios_characteristics_x2(data[0x13], dmi); + if(h->length<0x18) break; + if(data[0x14]!=0xFF && data[0x15]!=0xFF) + sprintf(dmi->bios.bios_revision,"%u.%u", + data[0x14], data[0x15]); + if(data[0x16]!=0xFF && data[0x17]!=0xFF) + sprintf(dmi->bios.firmware_revision,"%u.%u", + data[0x16], data[0x17]); + break; + case 1: /* 3.3.2 System Information */ +// printf("System Information\n"); + if(h->length<0x08) break; + dmi->system.filled=true; + strcpy(dmi->system.manufacturer,dmi_string(h,data[0x04])); + strcpy(dmi->system.product_name,dmi_string(h,data[0x05])); + strcpy(dmi->system.version,dmi_string(h,data[0x06])); + strcpy(dmi->system.serial,dmi_string(h,data[0x07])); + if(h->length<0x19) break; + dmi_system_uuid(data+0x08,dmi); + dmi_system_wake_up_type(data[0x18],dmi); + if(h->length<0x1B) break; + strcpy(dmi->system.sku_number,dmi_string(h,data[0x19])); + strcpy(dmi->system.family,dmi_string(h,data[0x1A])); + break; + + case 2: /* 3.3.3 Base Board Information */ +// printf("Base Board Information\n"); + if(h->length<0x08) break; + dmi->base_board.filled=true; + strcpy(dmi->base_board.manufacturer,dmi_string(h,data[0x04])); + strcpy(dmi->base_board.product_name,dmi_string(h,data[0x05])); + strcpy(dmi->base_board.version,dmi_string(h,data[0x06])); + strcpy(dmi->base_board.serial,dmi_string(h,data[0x07])); + if(h->length<0x0F) break; + strcpy(dmi->base_board.asset_tag,dmi_string(h,data[0x08])); + dmi_base_board_features(data[0x09], dmi); + strcpy(dmi->base_board.location,dmi_string(h,data[0x0A])); + strcpy(dmi->base_board.type,dmi_string(h,data[0x0D])); + if(h->length<0x0F+data[0x0E]*sizeof(uint16_t)) break; + break; + case 3: /* 3.3.4 Chassis Information */ +// printf("Chassis Information\n"); + if(h->length<0x09) break; + dmi->chassis.filled=true; + strcpy(dmi->chassis.manufacturer,dmi_string(h,data[0x04])); + strcpy(dmi->chassis.type,dmi_chassis_type(data[0x05]&0x7F)); + strcpy(dmi->chassis.lock,dmi_chassis_lock(data[0x05]>>7)); + strcpy(dmi->chassis.version,dmi_string(h,data[0x06])); + strcpy(dmi->chassis.serial,dmi_string(h,data[0x07])); + strcpy(dmi->chassis.asset_tag,dmi_string(h,data[0x08])); + if(h->length<0x0D) break; + strcpy(dmi->chassis.boot_up_state,dmi_chassis_state(data[0x09])); + strcpy(dmi->chassis.power_supply_state,dmi_chassis_state(data[0x0A])); + strcpy(dmi->chassis.thermal_state,dmi_chassis_state(data[0x0B])); + strcpy(dmi->chassis.security_status,dmi_chassis_security_status(data[0x0C])); + if(h->length<0x11) break; + sprintf(dmi->chassis.oem_information,"0x%08X\n",DWORD(data+0x0D)); + if(h->length<0x15) break; + dmi->chassis.height=data[0x11]; + dmi->chassis.nb_power_cords=data[0x12]; + break; + + case 4: /* 3.3.5 Processor Information */ +// printf("Processor Information\n"); + if(h->length<0x1A) break; + dmi->processor.filled=true; + strcpy(dmi->processor.socket_designation,dmi_string(h, data[0x04])); + strcpy(dmi->processor.type,dmi_processor_type(data[0x05])); + strcpy(dmi->processor.manufacturer,dmi_string(h, data[0x07])); + strcpy(dmi->processor.family,dmi_processor_family(data[0x06],dmi->processor.manufacturer)); + dmi_processor_id(data[0x06], data+8, dmi_string(h, data[0x10]), dmi); + strcpy(dmi->processor.version,dmi_string(h, data[0x10])); + dmi_processor_voltage(data[0x11],dmi); + dmi->processor.external_clock=WORD(data+0x12); + dmi->processor.max_speed=WORD(data+0x14); + dmi->processor.current_speed=WORD(data+0x16); + if(data[0x18]&(1<<6)) + strcpy(dmi->processor.status,dmi_processor_status(data[0x18]&0x07)); + else + sprintf(dmi->processor.status,"Unpopulated"); + sprintf(dmi->processor.upgrade,dmi_processor_upgrade(data[0x19])); + if(h->length<0x20) break; + dmi_processor_cache(WORD(data+0x1A), "L1", ver,dmi->processor.cache1); + dmi_processor_cache(WORD(data+0x1C), "L2", ver,dmi->processor.cache2); + dmi_processor_cache(WORD(data+0x1E), "L3", ver,dmi->processor.cache3); + if(h->length<0x23) break; + strcpy(dmi->processor.serial,dmi_string(h, data[0x20])); + strcpy(dmi->processor.asset_tag,dmi_string(h, data[0x21])); + strcpy(dmi->processor.part_number,dmi_string(h, data[0x22])); + break; + case 17: /* 3.3.18 Memory Device */ + if (h->length < 0x15) break; + dmi->memory_count++; + s_memory *mem = &dmi->memory[dmi->memory_count-1]; + dmi->memory[dmi->memory_count-1].filled=true; + dmi_memory_array_error_handle(WORD(data + 0x06),mem->error); + dmi_memory_device_width(WORD(data + 0x08),mem->total_width); + dmi_memory_device_width(WORD(data + 0x0A),mem->data_width); + dmi_memory_device_size(WORD(data + 0x0C),mem->size); + strcpy(mem->form_factor,dmi_memory_device_form_factor(data[0x0E])); + dmi_memory_device_set(data[0x0F],mem->device_set); + strcpy(mem->device_locator,dmi_string(h, data[0x10])); + strcpy(mem->bank_locator,dmi_string(h, data[0x11])); + strcpy(mem->type,dmi_memory_device_type(data[0x12])); + dmi_memory_device_type_detail(WORD(data + 0x13),mem->type_detail); + if (h->length < 0x17) break; + dmi_memory_device_speed(WORD(data + 0x15),mem->speed); + if (h->length < 0x1B) break; + strcpy(mem->manufacturer, dmi_string(h, data[0x17])); + strcpy(mem->serial,dmi_string(h, data[0x18])); + strcpy(mem->asset_tag,dmi_string(h, data[0x19])); + strcpy(mem->part_number,dmi_string(h, data[0x1A])); + break; + case 22: /* 3.3.23 Portable Battery */ + if (h->length < 0x10) break; + dmi->battery.filled=true; + strcpy(dmi->battery.location,dmi_string(h, data[0x04])); + strcpy(dmi->battery.manufacturer,dmi_string(h, data[0x05])); + + if (data[0x06] || h->length < 0x1A) + strcpy(dmi->battery.manufacture_date, dmi_string(h, data[0x06])); + + if (data[0x07] || h->length < 0x1A) + strcpy(dmi->battery.serial, dmi_string(h, data[0x07])); + + strcpy(dmi->battery.name,dmi_string(h, data[0x08])); + + if (data[0x09] != 0x02 || h->length < 0x1A) + strcpy(dmi->battery.chemistry,dmi_battery_chemistry(data[0x09])); + + if (h->length < 0x1A) + dmi_battery_capacity(WORD(data + 0x0A), 1,dmi->battery.design_capacity); + else + dmi_battery_capacity(WORD(data + 0x0A), data[0x15],dmi->battery.design_capacity); + dmi_battery_voltage(WORD(data + 0x0C),dmi->battery.design_voltage); + strcpy(dmi->battery.sbds,dmi_string(h, data[0x0E])); + dmi_battery_maximum_error(data[0x0F],dmi->battery.maximum_error); + if (h->length < 0x1A) break; + if (data[0x07] == 0) + sprintf(dmi->battery.sbds_serial,"%04X", WORD(data + 0x10)); + + if (data[0x06] == 0) + sprintf(dmi->battery.sbds_manufacture_date,"%u-%02u-%02u", + 1980 + (WORD(data + 0x12) >> 9), + (WORD(data + 0x12) >> 5) & 0x0F, + WORD(data + 0x12) & 0x1F); + if (data[0x09] == 0x02) + strcpy(dmi->battery.sbds_chemistry, dmi_string(h, data[0x14])); + + // sprintf(dmi->battery.oem_info,"0x%08X",DWORD(h, data+0x16)); + break; + case 38: /* 3.3.39 IPMI Device Information */ + if (h->length < 0x10) break; + dmi->ipmi.filled=true; + snprintf(dmi->ipmi.interface_type,sizeof(dmi->ipmi.interface_type), + "%s", dmi_ipmi_interface_type(data[0x04])); + dmi->ipmi.major_specification_version=data[0x05] >> 4; + dmi->ipmi.minor_specification_version=data[0x05] & 0x0F; + dmi->ipmi.I2C_slave_address=data[0x06] >> 1; + if (data[0x07] != 0xFF) + dmi->ipmi.nv_address=data[0x07]; + else + dmi->ipmi.nv_address=0; /* Not Present */ + dmi_ipmi_base_address(data[0x04], data + 0x08, + &dmi->ipmi); + if (h->length < 0x12) break; + if (data[0x11] != 0x00) + { + dmi->ipmi.irq=data[0x11]; + } + break; + } +} + +void parse_dmitable(s_dmi *dmi) { + int i=0; + uint8_t *data = NULL; + uint8_t buf[dmi->dmitable.len]; + + memcpy(buf,(int *)dmi->dmitable.base,sizeof(uint8_t) * dmi->dmitable.len); + data=buf; + dmi->memory_count=0; + while(idmitable.num && data+4<=buf+dmi->dmitable.len) /* 4 is the length of an SMBIOS structure header */ { + uint8_t *next; + struct dmi_header h; + to_dmi_header(&h, data); + + /* + * If a short entry is found (less than 4 bytes), not only it + * is invalid, but we cannot reliably locate the next entry. + * Better stop at this point, and let the user know his/her + * table is broken. + */ + if(h.length<4) + { + printf("Invalid entry length (%u). DMI table is broken! Stop.\n\n", (unsigned int)h.length); + break; + } + +// printf("Handle 0x%04X, DMI type %d, %d bytes\n", h.handle, h.type, h.length); + + /* loo for the next handle */ + next=data+h.length; + while(next-buf+1dmitable.len && (next[0]!=0 || next[1]!=0)) + next++; + next+=2; + if(next-buf<=dmi->dmitable.len) + { + dmi_decode(&h, dmi->dmitable.ver,dmi); + } + data=next; + i++; + } +} diff --git a/com32/gpllib/dmi/dmi_base_board.c b/com32/gpllib/dmi/dmi_base_board.c new file mode 100644 index 0000000..2f19fda --- /dev/null +++ b/com32/gpllib/dmi/dmi_base_board.c @@ -0,0 +1,37 @@ +/* ----------------------------------------------------------------------- * + * + * Pportions of this file taken from the dmidecode project + * + * Copyright (C) 2000-2002 Alan Cox + * Copyright (C) 2002-2008 Jean Delvare + * + * 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 + * + * For the avoidance of doubt the "preferred form" of this code is one which + * is in an open unpatent encumbered format. Where cryptographic key signing + * forms part of the process of creating an executable the information + * including keys needed to generate an equivalently functional executable + * are deemed to be part of the source code. +*/ + +#include +#include +const char *base_board_features_strings[]={ + "Board is a hosting board", /* 0 */ + "Board requires at least one daughter board", + "Board is removable", + "Board is replaceable", + "Board is hot swappable" /* 4 */ +}; diff --git a/com32/gpllib/dmi/dmi_battery.c b/com32/gpllib/dmi/dmi_battery.c new file mode 100644 index 0000000..02b5e6d --- /dev/null +++ b/com32/gpllib/dmi/dmi_battery.c @@ -0,0 +1,72 @@ +/* ----------------------------------------------------------------------- * + * + * Pportions of this file taken from the dmidecode project + * + * Copyright (C) 2000-2002 Alan Cox + * Copyright (C) 2002-2008 Jean Delvare + * + * 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 + * + * For the avoidance of doubt the "preferred form" of this code is one which + * is in an open unpatent encumbered format. Where cryptographic key signing + * forms part of the process of creating an executable the information + * including keys needed to generate an equivalently functional executable + * are deemed to be part of the source code. +*/ + +#include +#include +const char *dmi_battery_chemistry(uint8_t code) +{ + /* 3.3.23.1 */ + static const char *chemistry[] = { + "Other", /* 0x01 */ + "Unknown", + "Lead Acid", + "Nickel Cadmium", + "Nickel Metal Hydride", + "Lithium Ion", + "Zinc Air", + "Lithium Polymer" /* 0x08 */ + }; + + if (code >= 0x01 && code <= 0x08) + return chemistry[code - 0x01]; + return out_of_spec; +} + +void dmi_battery_capacity(uint16_t code, uint8_t multiplier,char *capacity) +{ + if (code == 0) + sprintf(capacity,"%s","Unknown"); + else + sprintf(capacity,"%u mWh", code * multiplier); +} + +void dmi_battery_voltage(uint16_t code, char *voltage) +{ + if (code == 0) + sprintf(voltage,"%s","Unknown"); + else + sprintf(voltage,"%u mV", code); +} + +void dmi_battery_maximum_error(uint8_t code, char *error) +{ + if (code == 0xFF) + sprintf(error,"%s","Unknown"); + else + sprintf(error,"%u%%", code); +} diff --git a/com32/gpllib/dmi/dmi_bios.c b/com32/gpllib/dmi/dmi_bios.c new file mode 100644 index 0000000..1fea752 --- /dev/null +++ b/com32/gpllib/dmi/dmi_bios.c @@ -0,0 +1,79 @@ +/* ----------------------------------------------------------------------- * + * + * Pportions of this file taken from the dmidecode project + * + * Copyright (C) 2000-2002 Alan Cox + * Copyright (C) 2002-2008 Jean Delvare + * + * 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 + * + * For the avoidance of doubt the "preferred form" of this code is one which + * is in an open unpatent encumbered format. Where cryptographic key signing + * forms part of the process of creating an executable the information + * including keys needed to generate an equivalently functional executable + * are deemed to be part of the source code. +*/ + +#include +#include + +const char *bios_charac_strings[]={ + "BIOS characteristics not supported", /* 3 */ + "ISA is supported", + "MCA is supported", + "EISA is supported", + "PCI is supported", + "PC Card (PCMCIA) is supported", + "PNP is supported", + "APM is supported", + "BIOS is upgradeable", + "BIOS shadowing is allowed", + "VLB is supported", + "ESCD support is available", + "Boot from CD is supported", + "Selectable boot is supported", + "BIOS ROM is socketed", + "Boot from PC Card (PCMCIA) is supported", + "EDD is supported", + "Japanese floppy for NEC 9800 1.2 MB is supported (int 13h)", + "Japanese floppy for Toshiba 1.2 MB is supported (int 13h)", + "5.25\"/360 KB floppy services are supported (int 13h)", + "5.25\"/1.2 MB floppy services are supported (int 13h)", + "3.5\"/720 KB floppy services are supported (int 13h)", + "3.5\"/2.88 MB floppy services are supported (int 13h)", + "Print screen service is supported (int 5h)", + "8042 keyboard services are supported (int 9h)", + "Serial services are supported (int 14h)", + "Printer services are supported (int 17h)", + "CGA/mono video services are supported (int 10h)", + "NEC PC-98" /* 31 */ +}; + +const char *bios_charac_x1_strings[]={ + "ACPI is supported", /* 0 */ + "USB legacy is supported", + "AGP is supported", + "I2O boot is supported", + "LS-120 boot is supported", + "ATAPI Zip drive boot is supported", + "IEEE 1394 boot is supported", + "Smart battery is supported" /* 7 */ +}; + +const char *bios_charac_x2_strings[]={ + "BIOS boot specification is supported", /* 0 */ + "Function key-initiated network boot is supported", + "Targeted content distribution is supported" /* 2 */ +}; diff --git a/com32/gpllib/dmi/dmi_chassis.c b/com32/gpllib/dmi/dmi_chassis.c new file mode 100644 index 0000000..6150016 --- /dev/null +++ b/com32/gpllib/dmi/dmi_chassis.c @@ -0,0 +1,109 @@ +/* ----------------------------------------------------------------------- * + * + * Pportions of this file taken from the dmidecode project + * + * Copyright (C) 2000-2002 Alan Cox + * Copyright (C) 2002-2008 Jean Delvare + * + * 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 + * + * For the avoidance of doubt the "preferred form" of this code is one which + * is in an open unpatent encumbered format. Where cryptographic key signing + * forms part of the process of creating an executable the information + * including keys needed to generate an equivalently functional executable + * are deemed to be part of the source code. +*/ + +#include +#include + +const char *dmi_chassis_type(uint8_t code) +{ + /* 3.3.4.1 */ + static const char *type[]={ + "Other", /* 0x01 */ + "Unknown", + "Desktop", + "Low Profile Desktop", + "Pizza Box", + "Mini Tower", + "Tower", + "Portable", + "Laptop", + "Notebook", + "Hand Held", + "Docking Station", + "All In One", + "Sub Notebook", + "Space-saving", + "Lunch Box", + "Main Server Chassis", /* master.mif says System */ + "Expansion Chassis", + "Sub Chassis", + "Bus Expansion Chassis", + "Peripheral Chassis", + "RAID Chassis", + "Rack Mount Chassis", + "Sealed-case PC", + "Multi-system" /* 0x19 */ + }; + + if(code>=0x01 && code<=0x19) + return type[code-0x01]; + return out_of_spec; +} + +const char *dmi_chassis_lock(uint8_t code) +{ + static const char *lock[]={ + "Not Present", /* 0x00 */ + "Present" /* 0x01 */ + }; + + return lock[code]; +} + +const char *dmi_chassis_state(uint8_t code) +{ + /* 3.3.4.2 */ + static const char *state[]={ + "Other", /* 0x01 */ + "Unknown", + "Safe", /* master.mif says OK */ + "Warning", + "Critical", + "Non-recoverable" /* 0x06 */ + }; + + if(code>=0x01 && code<=0x06) + return(state[code-0x01]); + return out_of_spec; +} + +const char *dmi_chassis_security_status(uint8_t code) +{ + /* 3.3.4.3 */ + static const char *status[]={ + "Other", /* 0x01 */ + "Unknown", + "None", + "External Interface Locked Out", + "External Interface Enabled" /* 0x05 */ + }; + + if(code>=0x01 && code<=0x05) + return(status[code-0x01]); + return out_of_spec; +} diff --git a/com32/gpllib/dmi/dmi_ipmi.c b/com32/gpllib/dmi/dmi_ipmi.c new file mode 100644 index 0000000..8081375 --- /dev/null +++ b/com32/gpllib/dmi/dmi_ipmi.c @@ -0,0 +1,58 @@ +/* ----------------------------------------------------------------------- * + * + * Portions of this file taken from the dmidecode project + * + * Copyright (C) 2000-2002 Alan Cox + * Copyright (C) 2002-2008 Jean Delvare + * + * 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 + * + * For the avoidance of doubt the "preferred form" of this code is one which + * is in an open unpatent encumbered format. Where cryptographic key signing + * forms part of the process of creating an executable the information + * including keys needed to generate an equivalently functional executable + * are deemed to be part of the source code. +*/ + +#include +#include + +const char *dmi_ipmi_interface_type(uint8_t code) +{ + /* 3.3.39.1 and IPMI 2.0, appendix C1, table C1-2 */ + static const char *type[] = { + "Unknown", /* 0x00 */ + "KCS (Keyboard Control Style)", + "SMIC (Server Management Interface Chip)", + "BT (Block Transfer)", + "SSIF (SMBus System Interface)" /* 0x04 */ + }; + + if (code <= 0x04) + return type[code]; + return out_of_spec; +} + +void dmi_ipmi_base_address(uint8_t type, const uint8_t *p, s_ipmi *ipmi) +{ + if (type == 0x04) /* SSIF */ + { + ipmi->base_address= (*p) >> 1; + } + else + { + ipmi->base_address = QWORD(p); + } +} diff --git a/com32/gpllib/dmi/dmi_memory.c b/com32/gpllib/dmi/dmi_memory.c new file mode 100644 index 0000000..9067009 --- /dev/null +++ b/com32/gpllib/dmi/dmi_memory.c @@ -0,0 +1,171 @@ +/* ----------------------------------------------------------------------- * + * + * Pportions of this file taken from the dmidecode project + * + * Copyright (C) 2000-2002 Alan Cox + * Copyright (C) 2002-2008 Jean Delvare + * + * 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 + * + * For the avoidance of doubt the "preferred form" of this code is one which + * is in an open unpatent encumbered format. Where cryptographic key signing + * forms part of the process of creating an executable the information + * including keys needed to generate an equivalently functional executable + * are deemed to be part of the source code. +*/ + +#include +#include + +void dmi_memory_array_error_handle(uint16_t code,char *array) +{ + if (code == 0xFFFE) + sprintf(array,"%s","Not Provided"); + else if (code == 0xFFFF) + sprintf(array,"%s","No Error"); + else + sprintf(array,"0x%04X", code); +} + +void dmi_memory_device_width(uint16_t code, char *width) +{ + /* + * 3.3.18 Memory Device (Type 17) + * If no memory module is present, width may be 0 + */ + if (code == 0xFFFF || code == 0) + sprintf(width,"%s","Unknown"); + else + sprintf(width,"%u bits", code); +} + +void dmi_memory_device_size(uint16_t code, char *size) +{ + if (code == 0) + sprintf(size,"%s","Free"); + else if (code == 0xFFFF) + sprintf(size,"%s","Unknown"); + else { + if (code & 0x8000) + sprintf(size, "%u kB", code & 0x7FFF); + else + sprintf(size,"%u MB", code); + } +} + +const char *dmi_memory_device_form_factor(uint8_t code) +{ + /* 3.3.18.1 */ + static const char *form_factor[] = { + "Other", /* 0x01 */ + "Unknown", + "SIMM", + "SIP", + "Chip", + "DIP", + "ZIP", + "Proprietary Card", + "DIMM", + "TSOP", + "Row Of Chips", + "RIMM", + "SODIMM", + "SRIMM", + "FB-DIMM" /* 0x0F */ + }; + + if (code >= 0x01 && code <= 0x0F) + return form_factor[code - 0x01]; + return out_of_spec; +} + +void dmi_memory_device_set(uint8_t code, char *set) +{ + if (code == 0) + sprintf(set,"%s","None"); + else if (code == 0xFF) + sprintf(set,"%s","Unknown"); + else + sprintf(set,"%u", code); +} + +const char *dmi_memory_device_type(uint8_t code) +{ + /* 3.3.18.2 */ + static const char *type[] = { + "Other", /* 0x01 */ + "Unknown", + "DRAM", + "EDRAM", + "VRAM", + "SRAM", + "RAM", + "ROM", + "Flash", + "EEPROM", + "FEPROM", + "EPROM", + "CDRAM", + "3DRAM", + "SDRAM", + "SGRAM", + "RDRAM", + "DDR", + "DDR2", + "DDR2 FB-DIMM" /* 0x14 */ + }; + + if (code >= 0x01 && code <= 0x14) + return type[code - 0x01]; + return out_of_spec; +} + +void dmi_memory_device_type_detail(uint16_t code,char *type_detail) +{ + /* 3.3.18.3 */ + static const char *detail[] = { + "Other", /* 1 */ + "Unknown", + "Fast-paged", + "Static Column", + "Pseudo-static", + "RAMBus", + "Synchronous", + "CMOS", + "EDO", + "Window DRAM", + "Cache DRAM", + "Non-Volatile" /* 12 */ + }; + + if ((code & 0x1FFE) == 0) + sprintf(type_detail,"%s","None"); + else + { + int i; + + for (i = 1; i <= 12; i++) + if (code & (1 << i)) + sprintf(type_detail,"%s", detail[i - 1]); + } +} + +void dmi_memory_device_speed(uint16_t code, char *speed) +{ + if (code == 0) + sprintf(speed,"%s","Unknown"); + else + sprintf(speed,"%u MHz", code); +} diff --git a/com32/gpllib/dmi/dmi_processor.c b/com32/gpllib/dmi/dmi_processor.c new file mode 100644 index 0000000..321f6e6 --- /dev/null +++ b/com32/gpllib/dmi/dmi_processor.c @@ -0,0 +1,432 @@ +/* ----------------------------------------------------------------------- * + * + * Pportions of this file taken from the dmidecode project + * + * Copyright (C) 2000-2002 Alan Cox + * Copyright (C) 2002-2008 Jean Delvare + * + * 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 + * + * For the avoidance of doubt the "preferred form" of this code is one which + * is in an open unpatent encumbered format. Where cryptographic key signing + * forms part of the process of creating an executable the information + * including keys needed to generate an equivalently functional executable + * are deemed to be part of the source code. +*/ + +#include +#include + +const char *dmi_processor_type(uint8_t code) +{ + /* 3.3.5.1 */ + static const char *type[]={ + "Other", /* 0x01 */ + "Unknown", + "Central Processor", + "Math Processor", + "DSP Processor", + "Video Processor" /* 0x06 */ + }; + + if(code>=0x01 && code<=0x06) + return type[code-0x01]; + return out_of_spec; +} + +const char *dmi_processor_family(uint8_t code, char *manufacturer) +{ + /* 3.3.5.2 */ + static const char *family[]={ + NULL, /* 0x00 */ + "Other", + "Unknown", + "8086", + "80286", + "80386", + "80486", + "8087", + "80287", + "80387", + "80487", + "Pentium", + "Pentium Pro", + "Pentium II", + "Pentium MMX", + "Celeron", + "Pentium II Xeon", + "Pentium III", + "M1", + "M2", + "Celeron M", /* 0x14 */ + "Pentium 4 HT", + NULL, + NULL, /* 0x17 */ + "Duron", + "K5", + "K6", + "K6-2", + "K6-3", + "Athlon", + "AMD2900", + "K6-2+", + "Power PC", + "Power PC 601", + "Power PC 603", + "Power PC 603+", + "Power PC 604", + "Power PC 620", + "Power PC x704", + "Power PC 750", + "Core 2 Duo", /* 0x28 */ + "Core 2 Duo Mobile", + "Core Solo Mobile", + "Atom", + NULL, + NULL, + NULL, + NULL,/* 0x2F */ + "Alpha", + "Alpha 21064", + "Alpha 21066", + "Alpha 21164", + "Alpha 21164PC", + "Alpha 21164a", + "Alpha 21264", + "Alpha 21364", + NULL, /* 0x38 */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, /* 0x3F */ + "MIPS", + "MIPS R4000", + "MIPS R4200", + "MIPS R4400", + "MIPS R4600", + "MIPS R10000", + NULL, /* 0x46 */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, /* 0x4F */ + "SPARC", + "SuperSPARC", + "MicroSPARC II", + "MicroSPARC IIep", + "UltraSPARC", + "UltraSPARC II", + "UltraSPARC IIi", + "UltraSPARC III", + "UltraSPARC IIIi", + NULL, /* 0x59 */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, /* 0x5F */ + "68040", + "68xxx", + "68000", + "68010", + "68020", + "68030", + NULL, /* 0x66 */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, /* 0x6F */ + "Hobbit", + NULL, /* 0x71 */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, /* 0x77 */ + "Crusoe TM5000", + "Crusoe TM3000", + "Efficeon TM8000", + NULL, /* 0x7B */ + NULL, + NULL, + NULL, + NULL, /* 0x7F */ + "Weitek", + NULL, /* 0x81 */ + "Itanium", + "Athlon 64", + "Opteron", + "Sempron", + "Turion 64", /* 0x86 */ + "Dual-Core Opteron", + "Atlhon 64 X2", + "Turion 64 X2", + "Quad-Core Opteron", + "Third-Generation Opteron", + "Phenom FX", + "Phenom X4", + "Phenom X2", + "Athlon X2",/* 0x8F */ + "PA-RISC", + "PA-RISC 8500", + "PA-RISC 8000", + "PA-RISC 7300LC", + "PA-RISC 7200", + "PA-RISC 7100LC", + "PA-RISC 7100", + NULL, /* 0x97 */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, /* 0x9F */ + "V30", + "Quad-Core Xeon 3200", /* 0xA1 */ + "Dual-Core Xeon 3000", + "Quad-Core Xeon 5300", + "Dual-Core Xeon 5100", + "Dual-Core Xeon 5000", + "Dual-Core Xeon LV", + "Dual-Core Xeon ULV", + "Dual-Core Xeon 7100", + "Quad-Core Xeon 5400", + "Quad-Core Xeon", /* 0xAA */ + NULL, + NULL, + NULL, + NULL, + NULL, /* 0xAF */ + "Pentium III Xeon", + "Pentium III Speedstep", + "Pentium 4", + "Xeon", + "AS400", + "Xeon MP", + "Athlon XP", + "Athlon MP", + "Itanium 2", + "Pentium M", + "Celeron D", /* 0xBA */ + "Pentium D", + "Pentium EE", + "Core Solo", /* 0xBD */ + NULL, + "Core 2 Duo", + "Core 2 Solo", + "Core 2 Extreme", + "Core 2 Quad", + "Core 2 Extreme Mobile", + "Core 2 Duo Mobile", + "Core 2 Solo Mobile", + NULL, + NULL, /* 0xC7 */ + "IBM390", + "G4", + "G5", + "ESA/390 G6", /* 0xCB */ + "z/Architectur", + NULL, + NULL, + NULL, + NULL, /*0xD0*/ + NULL, + "C7-M", + "C7-D", + "C7", + "Eden", + NULL,/*0xD6*/ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, /*0xE0*/ + NULL, + NULL, + NULL, + NULL, + NULL, + "Embedded Opteron Quad-Core",/* 0xE6*/ + "Phenom Triple-Core" , + "Turion Ultra Dual-Core Mobile", + "Turion Dual-Core Mobile", + "Athlon Dual-Core", + "Sempron SI",/*0xEB*/ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, /* 0xF9 */ + "i860", + "i960", + NULL, /* 0xFC */ + NULL, + NULL, + NULL /* 0xFF */ + /* master.mif has values beyond that, but they can't be used for DMI */ + }; + /* Special case for ambiguous value 0xBE */ + if (code == 0xBE) + { + /* Best bet based on manufacturer string */ + if (strstr(manufacturer, "Intel") != NULL + || strncasecmp(manufacturer, "Intel", 5) == 0) + return "Core 2"; + if (strstr(manufacturer, "AMD") != NULL + || strncasecmp(manufacturer, "AMD", 3) == 0) + return "K7"; + return "Core 2 or K7"; + } + + if(family[code]!=NULL) { + return family[code]; + } + return out_of_spec; +} + +const char *dmi_processor_status(uint8_t code) +{ + static const char *status[]={ + "Unknown", /* 0x00 */ + "Enabled", + "Disabled By User", + "Disabled By BIOS", + "Idle", /* 0x04 */ + "", + "", + "Other" /* 0x07 */ + }; + + if(code<=0x04) + return status[code]; + if(code==0x07) + return status[0x05]; + return out_of_spec; +} + +const char *dmi_processor_upgrade(uint8_t code) +{ + /* 3.3.5.5 */ + static const char *upgrade[]={ + "Other", /* 0x01 */ + "Unknown", + "Daughter Board", + "ZIF Socket", + "Replaceable Piggy Back", + "None", + "LIF Socket", + "Slot 1", + "Slot 2", + "370-pin Socket", + "Slot A", + "Slot M", + "Socket 423", + "Socket A (Socket 462)", + "Socket 478", + "Socket 754", + "Socket 940", + "Socket 939" /* 0x12 */ + "Socket mPGA604", + "Socket LGA771", + "Socket LGA775", + "Socket S1", + "Socket AM2", + "Socket F (1207)" + }; + + if(code>=0x01 && code<=0x11) + return upgrade[code-0x01]; + return out_of_spec; +} + +void dmi_processor_cache(uint16_t code, const char *level, uint16_t ver, char *cache) +{ + if(code==0xFFFF) + { + if(ver>=0x0203) + sprintf(cache,"Not Provided"); + else + sprintf(cache,"No %s Cache", level); + } + else + sprintf(cache,"0x%04X", code); +} + +/* Intel AP-485 revision 28, table 5 */ +const char *cpu_flags_strings[PROCESSOR_FLAGS_ELEMENTS]={ + "FPU (Floating-point unit on-chip)", /* 0 */ + "VME (Virtual mode extension)", + "DE (Debugging extension)", + "PSE (Page size extension)", + "TSC (Time stamp counter)", + "MSR (Model specific registers)", + "PAE (Physical address extension)", + "MCE (Machine check exception)", + "CX8 (CMPXCHG8 instruction supported)", + "APIC (On-chip APIC hardware supported)", + NULL, /* 10 */ + "SEP (Fast system call)", + "MTRR (Memory type range registers)", + "PGE (Page global enable)", + "MCA (Machine check architecture)", + "CMOV (Conditional move instruction supported)", + "PAT (Page attribute table)", + "PSE-36 (36-bit page size extension)", + "PSN (Processor serial number present and enabled)", + "CLFSH (CLFLUSH instruction supported)", + NULL, /* 20 */ + "DS (Debug store)", + "ACPI (ACPI supported)", + "MMX (MMX technology supported)", + "FXSR (Fast floating-point save and restore)", + "SSE (Streaming SIMD extensions)", + "SSE2 (Streaming SIMD extensions 2)", + "SS (Self-snoop)", + "HTT (Hyper-threading technology)", + "TM (Thermal monitor supported)", + "IA64 (IA64 capabilities)", /* 30 */ + "PBE (Pending break enabled)" /* 31 */ +}; diff --git a/com32/gpllib/vpd/vpd.c b/com32/gpllib/vpd/vpd.c new file mode 100644 index 0000000..ff04b8b --- /dev/null +++ b/com32/gpllib/vpd/vpd.c @@ -0,0 +1,103 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- +*/ + +#include +#include +#include "vpd/vpd.h" + +int vpd_checksum(char *buf, int len) +{ + uint8_t sum=0; + int a; + + for(a=0; abase_address,0,sizeof (vpd->base_address)); + memset(&vpd->bios_build_id,0,sizeof (vpd->bios_build_id)); + memset(&vpd->box_serial_number,0,sizeof (vpd->box_serial_number)); + memset(&vpd->motherboard_serial_number,0,sizeof (vpd->motherboard_serial_number)); + memset(&vpd->machine_type_model,0,sizeof (vpd->machine_type_model)); + memset(&vpd->bios_release_date,0,sizeof (vpd->bios_release_date)); + memset(&vpd->default_flash_filename,0,sizeof (vpd->default_flash_filename)); + memset(&vpd->bios_version,0,sizeof (vpd->bios_version)); + + /* Until we found elements in the vpdtable, we consider them as not filled */ + vpd->filled=false; + + p=(char *)0xF0000; /* The start address to look at the dmi table */ + for (q = p; q < p + 0x10000; q +=4) { + memcpy(buf, q, 5); + if(memcmp(buf, "\252\125VPD", 5)==0) { + snprintf(&vpd->base_address,5,"%X",q); + if (q[5] < 0x30) + return -ENOVPDTABLE; + + vpd->filled=true; + /* XSeries have longer records, exact length seems to vary. */ + if (!(q[5] >= 0x45 && vpd_checksum(q, q[5])) + /* Some Netvista seem to work with this. */ + && !(vpd_checksum(q, 0x30)) + /* The Thinkpad/Thinkcentre checksum does *not* include the first 13 bytes. */ + && !(vpd_checksum(q + 0x0D, 0x30 - 0x0D))) + { + /* A few systems have a bad checksum (xSeries 325, 330, 335 + and 345 with early BIOS) but the record is otherwise + valid. */ + printf("VPD: Bad checksum!\n"); + } + + strncpy(vpd->bios_build_id,q+0x0D, 9); + strncpy(vpd->box_serial_number,q+0x16, 7); + strncpy(vpd->motherboard_serial_number,q+0x1D, 11); + strncpy(vpd->machine_type_model,q+0x28, 7); + + if (q[5] < 0x44) + return VPD_TABLE_PRESENT; + + strncpy(vpd->bios_release_date,q+0x30, 8); + strncpy(vpd->default_flash_filename,q+0x38, 12); + + if (q[5] >= 0x46 && q[0x44] != 0x00) { + strncpy(vpd->bios_version,q+0x44, 255); + } + + return VPD_TABLE_PRESENT; + } + } + return -ENOVPDTABLE; +} + + diff --git a/com32/hdt/Makefile b/com32/hdt/Makefile new file mode 100644 index 0000000..8f8cae2 --- /dev/null +++ b/com32/hdt/Makefile @@ -0,0 +1,47 @@ +## ----------------------------------------------------------------------- +## +## Copyright 2001-2008 H. Peter Anvin - All Rights Reserved +## Copyright 2009 Intel Corporation; author: H. Peter Anvin +## +## 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, Inc., 51 Franklin St, Fifth Floor, +## Boston MA 02110-1301, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +## +## Hardware Detection Tool +## + +topdir = ../.. +include ../MCONFIG + +LIBS = ../cmenu/libmenu/libmenu.a ../libutil/libutil_com.a \ + ../lib/libcom32.a $(LIBGCC) +CFLAGS += -I$(com32)/cmenu/libmenu + +MODULES = hdt.c32 +TESTFILES = + +OBJS = $(patsubst %.c,%.o,$(wildcard *.c)) + +all: $(MODULES) $(TESTFILES) + +hdt.elf : $(OBJS) $(LIBS) $(C_LIBS) + $(LD) $(LDFLAGS) -o $@ $^ + +tidy dist: + rm -f *.o *.lo *.a *.lst *.elf .*.d *.tmp + +clean: tidy + rm -f *.lnx + +spotless: clean + rm -f *.lss *.c32 *.com + rm -f *~ \#* + +install: + +-include .*.d diff --git a/com32/hdt/hdt-ata.c b/com32/hdt/hdt-ata.c new file mode 100644 index 0000000..e0d6015 --- /dev/null +++ b/com32/hdt/hdt-ata.c @@ -0,0 +1,281 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include + +#include "com32io.h" +#include "hdt-common.h" +#include "hdt-ata.h" + +#ifdef ATA +/** + * ata_id_string - Convert IDENTIFY DEVICE page into string + * @id: IDENTIFY DEVICE results we will examine + * @s: string into which data is output + * @ofs: offset into identify device page + * @len: length of string to return. must be an even number. + * + * The strings in the IDENTIFY DEVICE page are broken up into + * 16-bit chunks. Run through the string, and output each + * 8-bit chunk linearly, regardless of platform. + * + * LOCKING: + * caller. + */ +void ata_id_string(const uint16_t * id, unsigned char *s, + unsigned int ofs, unsigned int len) +{ + unsigned int c; + + while (len > 0) { + c = id[ofs] >> 8; + *s = c; + s++; + + c = id[ofs] & 0xff; + *s = c; + s++; + + ofs++; + len -= 2; + } +} + +/** + * ata_id_c_string - Convert IDENTIFY DEVICE page into C string + * @id: IDENTIFY DEVICE results we will examine + * @s: string into which data is output + * @ofs: offset into identify device page + * @len: length of string to return. must be an odd number. + * + * This function is identical to ata_id_string except that it + * trims trailing spaces and terminates the resulting string with + * null. @len must be actual maximum length (even number) + 1. + * + * LOCKING: + * caller. + */ +void ata_id_c_string(const uint16_t * id, unsigned char *s, + unsigned int ofs, unsigned int len) +{ + unsigned char *p; + + //WARN_ON(!(len & 1)); + + ata_id_string(id, s, ofs, len - 1); + + p = s + strnlen(s, len - 1); + while (p > s && p[-1] == ' ') + p--; + *p = '\0'; +} +#endif + +/** + * Call int 13h, but with retry on failure. Especially floppies need this. + */ +int int13_retry(const com32sys_t * inreg, com32sys_t * outreg) +{ + int retry = 6; /* Number of retries */ + com32sys_t tmpregs; + + if (!outreg) + outreg = &tmpregs; + + while (retry--) { + __intcall(0x13, inreg, outreg); + if (!(outreg->eflags.l & EFLAGS_CF)) + return 0; /* CF=0, OK */ + } + + return -1; /* Error */ +} + +/* Display CPU registers for debugging purposes */ +void printregs(const com32sys_t * r) +{ + printf("eflags = %08x ds = %04x es = %04x fs = %04x gs = %04x\n" + "eax = %08x ebx = %08x ecx = %08x edx = %08x\n" + "ebp = %08x esi = %08x edi = %08x esp = %08x\n", + r->eflags.l, r->ds, r->es, r->fs, r->gs, + r->eax.l, r->ebx.l, r->ecx.l, r->edx.l, + r->ebp.l, r->esi.l, r->edi.l, r->_unused_esp.l); +} + +/* Try to get information for a given disk */ +int get_disk_params(int disk, struct diskinfo *disk_info) +{ + static com32sys_t getparm, parm, getebios, ebios, inreg, outreg; + struct device_parameter dp; +#ifdef ATA + struct ata_identify_device aid; +#endif + + memset(&(disk_info[disk]), 0, sizeof(struct diskinfo)); + + disk_info[disk].disk = disk; + disk_info[disk].ebios = disk_info[disk].cbios = 0; + + /* Sending int 13h func 41h to query EBIOS information */ + memset(&getebios, 0, sizeof(com32sys_t)); + memset(&ebios, 0, sizeof(com32sys_t)); + + /* Get EBIOS support */ + getebios.eax.w[0] = 0x4100; + getebios.ebx.w[0] = 0x55aa; + getebios.edx.b[0] = disk; + getebios.eflags.b[0] = 0x3; /* CF set */ + + __intcall(0x13, &getebios, &ebios); + + /* Detecting EDD support */ + if (!(ebios.eflags.l & EFLAGS_CF) && + ebios.ebx.w[0] == 0xaa55 && (ebios.ecx.b[0] & 1)) { + disk_info[disk].ebios = 1; + switch (ebios.eax.b[1]) { + case 32: + strlcpy(disk_info[disk].edd_version, "1.0", 3); + break; + case 33: + strlcpy(disk_info[disk].edd_version, "1.1", 3); + break; + case 48: + strlcpy(disk_info[disk].edd_version, "3.0", 3); + break; + default: + strlcpy(disk_info[disk].edd_version, "0", 1); + break; + } + } + /* Get disk parameters -- really only useful for + * hard disks, but if we have a partitioned floppy + * it's actually our best chance... + */ + memset(&getparm, 0, sizeof(com32sys_t)); + memset(&parm, 0, sizeof(com32sys_t)); + getparm.eax.b[1] = 0x08; + getparm.edx.b[0] = disk; + + __intcall(0x13, &getparm, &parm); + + if (parm.eflags.l & EFLAGS_CF) + return disk_info[disk].ebios ? 0 : -1; + + disk_info[disk].heads = parm.edx.b[1] + 1; + disk_info[disk].sectors_per_track = parm.ecx.b[0] & 0x3f; + if (disk_info[disk].sectors_per_track == 0) { + disk_info[disk].sectors_per_track = 1; + } else { + disk_info[disk].cbios = 1; /* Valid geometry */ + } + + /* If geometry isn't valid, no need to try to get more info about the drive*/ + /* Looks like in can confuse some optical drives */ + if (disk_info[disk].cbios != 1) return 0; + +/* FIXME: memset to 0 make it fails + * memset(__com32.cs_bounce, 0, sizeof(struct device_pairameter)); */ + memset(&dp, 0, sizeof(struct device_parameter)); + memset(&inreg, 0, sizeof(com32sys_t)); + + /* Requesting Extended Read Drive Parameters via int13h func 48h */ + inreg.esi.w[0] = OFFS(__com32.cs_bounce); + inreg.ds = SEG(__com32.cs_bounce); + inreg.eax.w[0] = 0x4800; + inreg.edx.b[0] = disk; + + __intcall(0x13, &inreg, &outreg); + + /* Saving bounce buffer before anything corrupt it */ + memcpy(&dp, __com32.cs_bounce, sizeof(struct device_parameter)); + + if (outreg.eflags.l & EFLAGS_CF) { + more_printf("Disk 0x%X doesn't supports EDD 3.0\n", disk); + return -1; + } + + /* Copying result to the disk_info structure + * host_bus_type, interface_type, sectors & cylinders */ + snprintf(disk_info[disk].host_bus_type, + sizeof disk_info[disk].host_bus_type, "%c%c%c%c", + dp.host_bus_type[0], dp.host_bus_type[1], dp.host_bus_type[2], + dp.host_bus_type[3]); + snprintf(disk_info[disk].interface_type, + sizeof disk_info[disk].interface_type, "%c%c%c%c%c%c%c%c", + dp.interface_type[0], dp.interface_type[1], + dp.interface_type[2], dp.interface_type[3], + dp.interface_type[4], dp.interface_type[5], + dp.interface_type[6], dp.interface_type[7]); + disk_info[disk].sectors = dp.sectors; + disk_info[disk].cylinders = dp.cylinders; + + /*FIXME: we have to find a way to grab the model & fw + * We do put dummy data until we found a solution */ + snprintf(disk_info[disk].aid.model, sizeof disk_info[disk].aid.model, + "0x%X", disk); + snprintf(disk_info[disk].aid.fw_rev, sizeof disk_info[disk].aid.fw_rev, + "%s", "N/A"); + snprintf(disk_info[disk].aid.serial_no, + sizeof disk_info[disk].aid.serial_no, "%s", "N/A"); + + /* Useless stuff before I figure how to send ata packets */ +#ifdef ATA + memset(__com32.cs_bounce, 0, sizeof(struct device_parameter)); + memset(&aid, 0, sizeof(struct ata_identify_device)); + memset(&inreg, 0, sizeof inreg); + inreg.ebx.w[0] = OFFS(__com32.cs_bounce + 1024); + inreg.es = SEG(__com32.cs_bounce + 1024); + inreg.eax.w[0] = 0x2500; + inreg.edx.b[0] = disk; + + __intcall(0x13, &inreg, &outreg); + + memcpy(&aid, __com32.cs_bounce, sizeof(struct ata_identify_device)); + + if (outreg.eflags.l & EFLAGS_CF) { + more_printf("Disk 0x%X: Failed to Identify Device\n", disk); + //FIXME + return 0; + } +// ata_id_c_string(aid, disk_info[disk].fwrev, ATA_ID_FW_REV, sizeof(disk_info[disk].fwrev)); +// ata_id_c_string(aid, disk_info[disk].model, ATA_ID_PROD, sizeof(disk_info[disk].model)); + + char buff[sizeof(struct ata_identify_device)]; + memcpy(buff, &aid, sizeof(struct ata_identify_device)); + for (int j = 0; j < sizeof(struct ata_identify_device); j++) + more_printf("model=|%c|\n", buff[j]); + more_printf("Disk 0x%X : %s %s %s\n", disk, aid.model, aid.fw_rev, + aid.serial_no); +#endif + + return 0; +} diff --git a/com32/hdt/hdt-ata.h b/com32/hdt/hdt-ata.h new file mode 100644 index 0000000..fee4d59 --- /dev/null +++ b/com32/hdt/hdt-ata.h @@ -0,0 +1,122 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#ifndef DEFINE_HDT_ATA_H +#define DEFINE_HDT_ATA_H +#include + +#include "hdt.h" + +struct ata_identify_device { + unsigned short words000_009[10]; + unsigned char serial_no[20]; + unsigned short words020_022[3]; + unsigned char fw_rev[8]; + unsigned char model[40]; + unsigned short words047_079[33]; + unsigned short major_rev_num; + unsigned short minor_rev_num; + unsigned short command_set_1; + unsigned short command_set_2; + unsigned short command_set_extension; + unsigned short cfs_enable_1; + unsigned short word086; + unsigned short csf_default; + unsigned short words088_255[168]; +} ATTR_PACKED; + +struct diskinfo { + int disk; + int ebios; /* EBIOS supported on this disk */ + int cbios; /* CHS geometry is valid */ + int heads; + int sectors_per_track; + int sectors; + int cylinders; + char edd_version[4]; + struct ata_identify_device aid; /* IDENTIFY xxx DEVICE data */ + char host_bus_type[5]; + char interface_type[9]; + char interface_port; +} ATTR_PACKED; + +/* + * Get a disk block and return a malloc'd buffer. + * Uses the disk number and information from disk_info. + */ +struct ebios_dapa { + uint16_t len; + uint16_t count; + uint16_t off; + uint16_t seg; + uint64_t lba; +}; + +// BYTE=8 +// WORD=16 +// DWORD=32 +// QWORD=64 +struct device_parameter { + uint16_t len; + uint16_t info; + uint32_t cylinders; + uint32_t heads; + uint32_t sectors_per_track; + uint64_t sectors; + uint16_t bytes_per_sector; + uint32_t dpte_pointer; + uint16_t device_path_information; + uint8_t device_path_lenght; + uint8_t device_path_reserved; + uint16_t device_path_reserved_2; + uint8_t host_bus_type[4]; + uint8_t interface_type[8]; + uint64_t interace_path; + uint64_t device_path[2]; + uint8_t reserved; + uint8_t cheksum; +} ATTR_PACKED; + +/* Useless stuff until I manage how to send ata packets */ +#ifdef ATA +enum { + ATA_ID_FW_REV = 23, + ATA_ID_PROD = 27, + ATA_ID_FW_REV_LEN = 8, + ATA_ID_PROD_LEN = 40, +}; +void ata_id_c_string(const uint16_t * id, unsigned char *s, unsigned int ofs, + unsigned int len); +void ata_id_string(const uint16_t * id, unsigned char *s, unsigned int ofs, + unsigned int len); +int int13_retry(const com32sys_t * inreg, com32sys_t * outreg); +void printregs(const com32sys_t * r); +#endif + +int get_disk_params(int disk, struct diskinfo *disk_info); +#endif diff --git a/com32/hdt/hdt-cli-cpu.c b/com32/hdt/hdt-cli-cpu.c new file mode 100644 index 0000000..f0f0a1b --- /dev/null +++ b/com32/hdt/hdt-cli-cpu.c @@ -0,0 +1,194 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include +#include +#include +#include + +#include "hdt-cli.h" +#include "hdt-common.h" + +void main_show_cpu(int argc __unused, char **argv __unused, + struct s_hardware *hardware) +{ + cpu_detect(hardware); + detect_dmi(hardware); + more_printf("CPU\n"); + more_printf(" Manufacturer : %s \n", hardware->cpu.vendor); + more_printf(" Product : %s \n", del_multi_spaces(hardware->cpu.model)); + if ((hardware->cpu.flags.lm == false) + && (hardware->cpu.flags.smp == false)) { + more_printf(" Features : %d MhZ : x86 32bits\n", + hardware->dmi.processor.current_speed); + } else if ((hardware->cpu.flags.lm == false) + && (hardware->cpu.flags.smp == true)) { + more_printf(" Features : %d MhZ : x86 32bits SMP\n", + hardware->dmi.processor.current_speed); + } else if ((hardware->cpu.flags.lm == true) + && (hardware->cpu.flags.smp == false)) { + more_printf(" Features : %d MhZ : x86_64 64bits\n", + hardware->dmi.processor.current_speed); + } else { + more_printf(" Features : %d MhZ : x86_64 64bits SMP\n", + hardware->dmi.processor.current_speed); + } +} + +static void show_cpu(int argc __unused, char **argv __unused, + struct s_hardware *hardware) +{ + char buffer[81]; + char buffer1[81]; + reset_more_printf(); + more_printf("CPU\n"); + more_printf("Vendor : %s\n", hardware->cpu.vendor); + more_printf("Model : %s\n", hardware->cpu.model); + more_printf("Vendor ID : %d\n", hardware->cpu.vendor_id); + more_printf("Family ID : %d\n", hardware->cpu.family); + more_printf("Model ID : %d\n", hardware->cpu.model_id); + more_printf("Stepping : %d\n", hardware->cpu.stepping); + more_printf("FSB : %d MHz\n", + hardware->dmi.processor.external_clock); + more_printf("Cur. Speed: %d MHz\n", + hardware->dmi.processor.current_speed); + more_printf("Max Speed : %d MHz\n", hardware->dmi.processor.max_speed); + more_printf("Upgrade : %s\n", hardware->dmi.processor.upgrade); + if (hardware->cpu.flags.smp) { + more_printf("SMP : yes\n"); + } else { + more_printf("SMP : no\n"); + } + if (hardware->cpu.flags.lm) { + more_printf("x86_64 : yes\n"); + } else { + more_printf("x86_64 : no\n"); + } + + memset(buffer, 0, sizeof(buffer)); + memset(buffer1, 0, sizeof(buffer1)); + if (hardware->cpu.flags.fpu) + strcat(buffer1, "fpu "); + if (hardware->cpu.flags.vme) + strcat(buffer1, "vme "); + if (hardware->cpu.flags.de) + strcat(buffer1, "de "); + if (hardware->cpu.flags.pse) + strcat(buffer1, "pse "); + if (hardware->cpu.flags.tsc) + strcat(buffer1, "tsc "); + if (hardware->cpu.flags.msr) + strcat(buffer1, "msr "); + if (hardware->cpu.flags.pae) + strcat(buffer1, "pae "); + if (hardware->cpu.flags.mce) + strcat(buffer1, "mce "); + if (hardware->cpu.flags.cx8) + strcat(buffer1, "cx8 "); + if (hardware->cpu.flags.apic) + strcat(buffer1, "apic "); + if (hardware->cpu.flags.sep) + strcat(buffer1, "sep "); + if (hardware->cpu.flags.mtrr) + strcat(buffer1, "mtrr "); + if (hardware->cpu.flags.pge) + strcat(buffer1, "pge "); + if (hardware->cpu.flags.mca) + strcat(buffer1, "mca "); + if (buffer1[0]) { + snprintf(buffer, sizeof buffer, "Flags : %s\n", buffer1); + more_printf(buffer); + } + + memset(buffer, 0, sizeof(buffer)); + memset(buffer1, 0, sizeof(buffer1)); + if (hardware->cpu.flags.cmov) + strcat(buffer1, "cmov "); + if (hardware->cpu.flags.pat) + strcat(buffer1, "pat "); + if (hardware->cpu.flags.pse_36) + strcat(buffer1, "pse_36 "); + if (hardware->cpu.flags.psn) + strcat(buffer1, "psn "); + if (hardware->cpu.flags.clflsh) + strcat(buffer1, "clflsh "); + if (hardware->cpu.flags.dts) + strcat(buffer1, "dts "); + if (hardware->cpu.flags.acpi) + strcat(buffer1, "acpi "); + if (hardware->cpu.flags.mmx) + strcat(buffer1, "mmx "); + if (hardware->cpu.flags.sse) + strcat(buffer1, "sse "); + if (hardware->cpu.flags.sse2) + strcat(buffer1, "sse2 "); + if (hardware->cpu.flags.ss) + strcat(buffer1, "ss "); + if (buffer1[0]) { + snprintf(buffer, sizeof buffer, "Flags : %s\n", buffer1); + more_printf(buffer); + } + + memset(buffer, 0, sizeof(buffer)); + memset(buffer1, 0, sizeof(buffer1)); + if (hardware->cpu.flags.htt) + strcat(buffer1, "ht "); + if (hardware->cpu.flags.acc) + strcat(buffer1, "acc "); + if (hardware->cpu.flags.syscall) + strcat(buffer1, "syscall "); + if (hardware->cpu.flags.mp) + strcat(buffer1, "mp "); + if (hardware->cpu.flags.nx) + strcat(buffer1, "nx "); + if (hardware->cpu.flags.mmxext) + strcat(buffer1, "mmxext "); + if (hardware->cpu.flags.lm) + strcat(buffer1, "lm "); + if (hardware->cpu.flags.nowext) + strcat(buffer1, "3dnowext "); + if (hardware->cpu.flags.now) + strcat(buffer1, "3dnow! "); + if (buffer1[0]) { + snprintf(buffer, sizeof buffer, "Flags : %s\n", buffer1); + more_printf(buffer); + } +} + +struct cli_module_descr cpu_show_modules = { + .modules = NULL, + .default_callback = show_cpu, +}; + +struct cli_mode_descr cpu_mode = { + .mode = CPU_MODE, + .name = CLI_CPU, + .default_modules = NULL, + .show_modules = &cpu_show_modules, + .set_modules = NULL, +}; diff --git a/com32/hdt/hdt-cli-dmi.c b/com32/hdt/hdt-cli-dmi.c new file mode 100644 index 0000000..d395c67 --- /dev/null +++ b/com32/hdt/hdt-cli-dmi.c @@ -0,0 +1,495 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include +#include +#include +#include + +#include "hdt-cli.h" +#include "hdt-common.h" + +static void show_dmi_modules(int argc __unused, char** argv __unused, + struct s_hardware *hardware) +{ + char available_dmi_commands[1024]; + memset(available_dmi_commands, 0, sizeof(available_dmi_commands)); + + printf("Available DMI modules on your system:\n"); + if (hardware->dmi.base_board.filled == true) + printf("\t%s\n", CLI_DMI_BASE_BOARD); + if (hardware->dmi.battery.filled == true) + printf("\t%s\n", CLI_DMI_BATTERY); + if (hardware->dmi.bios.filled == true) + printf("\t%s\n", CLI_DMI_BIOS); + if (hardware->dmi.chassis.filled == true) + printf("\t%s\n", CLI_DMI_CHASSIS); + for (int i = 0; i < hardware->dmi.memory_count; i++) { + if (hardware->dmi.memory[i].filled == true) { + printf("\tbank \n"); + break; + } + } + if (hardware->dmi.processor.filled == true) + printf("\t%s\n", CLI_DMI_PROCESSOR); + if (hardware->dmi.system.filled == true) + printf("\t%s\n", CLI_DMI_SYSTEM); + if (hardware->dmi.ipmi.filled == true) + printf("\t%s\n", CLI_DMI_IPMI); +} + +static void show_dmi_base_board(int argc __unused, char** argv __unused, + struct s_hardware *hardware) +{ + if (hardware->dmi.base_board.filled == false) { + printf("base_board information not found on your system, see " + "`show list' to see which module is available.\n"); + return; + } + reset_more_printf(); + more_printf("Base board\n"); + more_printf(" Manufacturer : %s\n", + hardware->dmi.base_board.manufacturer); + more_printf(" Product Name : %s\n", + hardware->dmi.base_board.product_name); + more_printf(" Version : %s\n", hardware->dmi.base_board.version); + more_printf(" Serial : %s\n", hardware->dmi.base_board.serial); + more_printf(" Asset Tag : %s\n", hardware->dmi.base_board.asset_tag); + more_printf(" Location : %s\n", hardware->dmi.base_board.location); + more_printf(" Type : %s\n", hardware->dmi.base_board.type); + for (int i = 0; i < BASE_BOARD_NB_ELEMENTS; i++) { + if (((bool *) (&hardware->dmi.base_board.features))[i] == true) { + more_printf(" %s\n", base_board_features_strings[i]); + } + } +} + +static void show_dmi_system(int argc __unused, char** argv __unused, + struct s_hardware *hardware) +{ + if (hardware->dmi.system.filled == false) { + printf("system information not found on your system, see " + "`show list' to see which module is available.\n"); + return; + } + printf("System\n"); + printf(" Manufacturer : %s\n", hardware->dmi.system.manufacturer); + printf(" Product Name : %s\n", hardware->dmi.system.product_name); + printf(" Version : %s\n", hardware->dmi.system.version); + printf(" Serial : %s\n", hardware->dmi.system.serial); + printf(" UUID : %s\n", hardware->dmi.system.uuid); + printf(" Wakeup Type : %s\n", hardware->dmi.system.wakeup_type); + printf(" SKU Number : %s\n", hardware->dmi.system.sku_number); + printf(" Family : %s\n", hardware->dmi.system.family); +} + +static void show_dmi_bios(int argc __unused, char** argv __unused, + struct s_hardware *hardware) +{ + if (hardware->dmi.bios.filled == false) { + printf("bios information not found on your system, see " + "`show list' to see which module is available.\n"); + return; + } + reset_more_printf(); + more_printf("BIOS\n"); + more_printf(" Vendor : %s\n", hardware->dmi.bios.vendor); + more_printf(" Version : %s\n", hardware->dmi.bios.version); + more_printf(" Release : %s\n", + hardware->dmi.bios.release_date); + more_printf(" Bios Revision : %s\n", + hardware->dmi.bios.bios_revision); + more_printf(" Firmware Revision : %s\n", + hardware->dmi.bios.firmware_revision); + more_printf(" Address : 0x%04X0\n", + hardware->dmi.bios.address); + more_printf(" Runtime address : %u %s\n", + hardware->dmi.bios.runtime_size, + hardware->dmi.bios.runtime_size_unit); + more_printf(" Rom size : %u %s\n", hardware->dmi.bios.rom_size, + hardware->dmi.bios.rom_size_unit); + + for (int i = 0; i < BIOS_CHAR_NB_ELEMENTS; i++) { + if (((bool *) (&hardware->dmi.bios.characteristics))[i] == true) { + more_printf(" %s\n", bios_charac_strings[i]); + } + } + for (int i = 0; i < BIOS_CHAR_X1_NB_ELEMENTS; i++) { + if (((bool *) (&hardware->dmi.bios.characteristics_x1))[i] == + true) { + more_printf(" %s\n", bios_charac_x1_strings[i]); + } + } + + for (int i = 0; i < BIOS_CHAR_X2_NB_ELEMENTS; i++) { + if (((bool *) (&hardware->dmi.bios.characteristics_x2))[i] == + true) { + more_printf(" %s\n", bios_charac_x2_strings[i]); + } + } + +} + +static void show_dmi_chassis(int argc __unused, char** argv __unused, + struct s_hardware *hardware) +{ + if (hardware->dmi.chassis.filled == false) { + more_printf("chassis information not found on your system, see " + "`show list' to see which module is available.\n"); + return; + } + printf("Chassis\n"); + printf(" Manufacturer : %s\n", + hardware->dmi.chassis.manufacturer); + printf(" Type : %s\n", hardware->dmi.chassis.type); + printf(" Lock : %s\n", hardware->dmi.chassis.lock); + printf(" Version : %s\n", + hardware->dmi.chassis.version); + printf(" Serial : %s\n", hardware->dmi.chassis.serial); + printf(" Asset Tag : %s\n", + del_multi_spaces(hardware->dmi.chassis.asset_tag)); + printf(" Boot up state : %s\n", + hardware->dmi.chassis.boot_up_state); + printf(" Power supply state : %s\n", + hardware->dmi.chassis.power_supply_state); + printf(" Thermal state : %s\n", + hardware->dmi.chassis.thermal_state); + printf(" Security Status : %s\n", + hardware->dmi.chassis.security_status); + printf(" OEM Information : %s\n", + hardware->dmi.chassis.oem_information); + printf(" Height : %u\n", hardware->dmi.chassis.height); + printf(" NB Power Cords : %u\n", + hardware->dmi.chassis.nb_power_cords); +} + +static void show_dmi_ipmi(int argc __unused, char **argv __unused, + struct s_hardware *hardware) +{ + if (hardware->dmi.ipmi.filled == false) { + more_printf("IPMI module not available\n"); + return; + } + printf("IPMI\n"); + printf(" Interface Type : %s\n", + hardware->dmi.ipmi.interface_type); + printf(" Specification Ver. : %u.%u\n", + hardware->dmi.ipmi.major_specification_version, + hardware->dmi.ipmi.minor_specification_version); + printf(" I2C Slave Address : 0x%02x\n", + hardware->dmi.ipmi.I2C_slave_address); + printf(" Nv Storage Address : %u\n", + hardware->dmi.ipmi.nv_address); + uint32_t high = hardware->dmi.ipmi.base_address >> 32; + uint32_t low = hardware->dmi.ipmi.base_address & 0xFFFF; + printf(" Base Address : %08X%08X\n", + high,(low & ~1)); + printf(" IRQ : %d\n", + hardware->dmi.ipmi.irq); +} + +static void show_dmi_battery(int argc __unused, char** argv __unused, + struct s_hardware *hardware) +{ + if (hardware->dmi.battery.filled == false) { + printf("battery information not found on your system, see " + "`show list' to see which module is available.\n"); + return; + } + printf("Battery \n"); + printf(" Vendor : %s\n", + hardware->dmi.battery.manufacturer); + printf(" Manufacture Date : %s\n", + hardware->dmi.battery.manufacture_date); + printf(" Serial : %s\n", hardware->dmi.battery.serial); + printf(" Name : %s\n", hardware->dmi.battery.name); + printf(" Chemistry : %s\n", + hardware->dmi.battery.chemistry); + printf(" Design Capacity : %s\n", + hardware->dmi.battery.design_capacity); + printf(" Design Voltage : %s\n", + hardware->dmi.battery.design_voltage); + printf(" SBDS : %s\n", hardware->dmi.battery.sbds); + printf(" SBDS Manuf. Date : %s\n", + hardware->dmi.battery.sbds_manufacture_date); + printf(" SBDS Chemistry : %s\n", + hardware->dmi.battery.sbds_chemistry); + printf(" Maximum Error : %s\n", + hardware->dmi.battery.maximum_error); + printf(" OEM Info : %s\n", + hardware->dmi.battery.oem_info); +} + +static void show_dmi_cpu(int argc __unused, char** argv __unused, + struct s_hardware *hardware) +{ + if (hardware->dmi.processor.filled == false) { + printf("processor information not found on your system, see " + "`show list' to see which module is available.\n"); + return; + } + reset_more_printf(); + more_printf("CPU\n"); + more_printf(" Socket Designation : %s\n", + hardware->dmi.processor.socket_designation); + more_printf(" Type : %s\n", hardware->dmi.processor.type); + more_printf(" Family : %s\n", + hardware->dmi.processor.family); + more_printf(" Manufacturer : %s\n", + hardware->dmi.processor.manufacturer); + more_printf(" Version : %s\n", + hardware->dmi.processor.version); + more_printf(" External Clock : %u\n", + hardware->dmi.processor.external_clock); + more_printf(" Max Speed : %u\n", + hardware->dmi.processor.max_speed); + more_printf(" Current Speed : %u\n", + hardware->dmi.processor.current_speed); + more_printf(" Cpu Type : %u\n", + hardware->dmi.processor.signature.type); + more_printf(" Cpu Family : %u\n", + hardware->dmi.processor.signature.family); + more_printf(" Cpu Model : %u\n", + hardware->dmi.processor.signature.model); + more_printf(" Cpu Stepping : %u\n", + hardware->dmi.processor.signature.stepping); + more_printf(" Cpu Minor Stepping : %u\n", + hardware->dmi.processor.signature.minor_stepping); +// more_printf(" Voltage : %f\n",hardware->dmi.processor.voltage); + more_printf(" Status : %s\n", + hardware->dmi.processor.status); + more_printf(" Upgrade : %s\n", + hardware->dmi.processor.upgrade); + more_printf(" Cache L1 Handle : %s\n", + hardware->dmi.processor.cache1); + more_printf(" Cache L2 Handle : %s\n", + hardware->dmi.processor.cache2); + more_printf(" Cache L3 Handle : %s\n", + hardware->dmi.processor.cache3); + more_printf(" Serial : %s\n", + hardware->dmi.processor.serial); + more_printf(" Part Number : %s\n", + hardware->dmi.processor.part_number); + more_printf(" ID : %s\n", hardware->dmi.processor.id); + for (int i = 0; i < PROCESSOR_FLAGS_ELEMENTS; i++) { + if (((bool *) (&hardware->dmi.processor.cpu_flags))[i] == true) { + more_printf(" %s\n", cpu_flags_strings[i]); + } + } +} + +static void show_dmi_memory_bank(int argc, char** argv, + struct s_hardware *hardware) +{ + int bank = -1; + + /* Sanitize arguments */ + if (argc > 0) + bank = strtol(argv[0], (char **)NULL, 10); + + if (errno == ERANGE || bank < 0) { + printf("This bank number is incorrect\n"); + return; + } + + if ((bank >= hardware->dmi.memory_count) || (bank < 0)) { + printf("Bank %d number doesn't exists\n", bank); + return; + } + if (hardware->dmi.memory[bank].filled == false) { + printf("Bank %d doesn't contain any information\n", bank); + return; + } + + printf("Memory Bank %d\n", bank); + printf(" Form Factor : %s\n", + hardware->dmi.memory[bank].form_factor); + printf(" Type : %s\n", hardware->dmi.memory[bank].type); + printf(" Type Detail : %s\n", + hardware->dmi.memory[bank].type_detail); + printf(" Speed : %s\n", hardware->dmi.memory[bank].speed); + printf(" Size : %s\n", hardware->dmi.memory[bank].size); + printf(" Device Set : %s\n", + hardware->dmi.memory[bank].device_set); + printf(" Device Loc. : %s\n", + hardware->dmi.memory[bank].device_locator); + printf(" Bank Locator : %s\n", + hardware->dmi.memory[bank].bank_locator); + printf(" Total Width : %s\n", + hardware->dmi.memory[bank].total_width); + printf(" Data Width : %s\n", + hardware->dmi.memory[bank].data_width); + printf(" Error : %s\n", hardware->dmi.memory[bank].error); + printf(" Vendor : %s\n", + hardware->dmi.memory[bank].manufacturer); + printf(" Serial : %s\n", hardware->dmi.memory[bank].serial); + printf(" Asset Tag : %s\n", + hardware->dmi.memory[bank].asset_tag); + printf(" Part Number : %s\n", + hardware->dmi.memory[bank].part_number); +} + +void main_show_dmi(int argc __unused, char **argv __unused, + struct s_hardware *hardware) +{ + + detect_dmi(hardware); + + if (hardware->is_dmi_valid == false) { + printf("No valid DMI table found, exiting.\n"); + return; + } + printf("DMI Table version %d.%d found\n", + hardware->dmi.dmitable.major_version, + hardware->dmi.dmitable.minor_version); + + show_dmi_modules(0, NULL, hardware); +} + +void show_dmi_memory_modules(int argc __unused, char** argv __unused, + struct s_hardware *hardware) +{ + int clear = 1, show_free_banks = 1; + + /* Sanitize arguments */ + if (argc > 0) { + clear = strtol(argv[0], NULL, 10); + if (errno == ERANGE || clear < 0 || clear > 1) + goto usage; + + if (argc > 1) { + show_free_banks = strtol(argv[1], NULL, 10); + if (errno == ERANGE || show_free_banks < 0 || show_free_banks > 1) + goto usage; + } + } + + char bank_number[10]; + char available_dmi_commands[1024]; + memset(available_dmi_commands, 0, sizeof(available_dmi_commands)); + + if (hardware->dmi.memory_count <= 0) { + more_printf("No memory module found\n"); + return; + } + + if (clear) + clear_screen(); + more_printf("Memory Banks\n"); + for (int i = 0; i < hardware->dmi.memory_count; i++) { + if (hardware->dmi.memory[i].filled == true) { + /* When discovering the first item, let's clear the screen */ + strncat(available_dmi_commands, CLI_DMI_MEMORY_BANK, + sizeof(CLI_DMI_MEMORY_BANK) - 1); + memset(bank_number, 0, sizeof(bank_number)); + snprintf(bank_number, sizeof(bank_number), "%d ", i); + strncat(available_dmi_commands, bank_number, + sizeof(bank_number)); + if (show_free_banks == false) { + if (strncmp + (hardware->dmi.memory[i].size, "Free", 4)) + more_printf(" bank %02d : %s %s@%s\n", + i, hardware->dmi.memory[i].size, + hardware->dmi.memory[i].type, + hardware->dmi.memory[i].speed); + } else { + more_printf(" bank %02d : %s %s@%s\n", i, + hardware->dmi.memory[i].size, + hardware->dmi.memory[i].type, + hardware->dmi.memory[i].speed); + } + } + } + + return; + //printf("Type 'show bank' for more details.\n"); + +usage: + more_printf("show memory >\n"); + return; +} + +struct cli_callback_descr list_dmi_show_modules[] = { + { + .name = CLI_DMI_BASE_BOARD, + .exec = show_dmi_base_board, + }, + { + .name = CLI_DMI_BIOS, + .exec = show_dmi_bios, + }, + { + .name = CLI_DMI_BATTERY, + .exec = show_dmi_battery, + }, + { + .name = CLI_DMI_CHASSIS, + .exec = show_dmi_chassis, + }, + { + .name = CLI_DMI_MEMORY, + .exec = show_dmi_memory_modules, + }, + { + .name = CLI_DMI_MEMORY_BANK, + .exec = show_dmi_memory_bank, + }, + { + .name = CLI_DMI_PROCESSOR, + .exec = show_dmi_cpu, + }, + { + .name = CLI_DMI_SYSTEM, + .exec = show_dmi_system, + }, + { + .name = CLI_DMI_IPMI, + .exec = show_dmi_ipmi, + }, + { + .name = CLI_DMI_LIST, + .exec = show_dmi_modules, + }, + { + .name = NULL, + .exec = NULL, + }, +}; + +struct cli_module_descr dmi_show_modules = { + .modules = list_dmi_show_modules, + .default_callback = main_show_dmi, +}; + +struct cli_mode_descr dmi_mode = { + .mode = DMI_MODE, + .name = CLI_DMI, + .default_modules = NULL, + .show_modules = &dmi_show_modules, + .set_modules = NULL, +}; diff --git a/com32/hdt/hdt-cli-hdt.c b/com32/hdt/hdt-cli-hdt.c new file mode 100644 index 0000000..46f3669 --- /dev/null +++ b/com32/hdt/hdt-cli-hdt.c @@ -0,0 +1,347 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Pierre-Alexandre Meyer - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include +#include +#include +#include + +#include "hdt-menu.h" +#include "hdt-cli.h" +#include "hdt-common.h" + +/** + * cli_clear_screen - clear (erase) the entire screen + **/ +static void cli_clear_screen(int argc __unused, char** argv __unused, + struct s_hardware *hardware __unused) +{ + clear_screen(); +} + +/** + * main_show_modes - show availables modes + **/ +static void main_show_modes(int argc __unused, char** argv __unused, + struct s_hardware *hardware __unused) +{ + int i = 0; + + printf("Available modes:\n"); + while (list_modes[i]) { + printf("\t%s\n", list_modes[i]->name); + i++; + } +} + +/** + * cli_set_mode - set the mode of the cli, in the cli + * + * The mode number must be supplied in argv, position 0. + **/ +static void cli_set_mode(int argc, char **argv, + struct s_hardware *hardware) +{ + cli_mode_t new_mode; + + if (argc <= 0) { + printf("Which mode?\n"); + return; + } + + /* + * Note! argv[0] is a string representing the mode, we need the + * equivalent cli_mode_t to pass it to set_mode. + */ + new_mode = mode_s_to_mode_t(argv[0]); + set_mode(new_mode, hardware); +} + +/** + * do_exit - shared helper to exit a mode + **/ +static void do_exit(int argc __unused, char** argv __unused, + struct s_hardware *hardware) +{ + int new_mode = HDT_MODE; + + switch (hdt_cli.mode) { + case HDT_MODE: + new_mode = EXIT_MODE; + break; + default: + new_mode = HDT_MODE; + break; + } + + dprintf("CLI DEBUG: Switching from mode %d to mode %d\n", hdt_cli.mode, + new_mode); + set_mode(new_mode, hardware); +} + +/** + * show_cli_help - shared helper to show available commands + **/ +static void show_cli_help(int argc __unused, char** argv __unused, + struct s_hardware *hardware __unused) +{ + int j = 0; + struct cli_mode_descr *current_mode; + struct cli_callback_descr* associated_module = NULL; + + find_cli_mode_descr(hdt_cli.mode, ¤t_mode); + + printf("Available commands are:\n"); + + /* List first default modules of the mode */ + if (current_mode->default_modules && + current_mode->default_modules->modules) { + while (current_mode->default_modules->modules[j].name) { + printf("%s ", + current_mode->default_modules->modules[j].name); + j++; + } + printf("\n"); + } + + /* List secondly the show modules of the mode */ + if (current_mode->show_modules && + current_mode->show_modules->modules) { + printf("show commands:\n"); + j = 0; + while (current_mode->show_modules->modules[j].name) { + printf("\t%s\n", + current_mode->show_modules->modules[j].name); + j++; + } + } + + /* List thirdly the set modules of the mode */ + if (current_mode->set_modules && + current_mode->set_modules->modules) { + printf("set commands:\n"); + j = 0; + while (current_mode->set_modules->modules[j].name) { + printf("\t%s\n", + current_mode->set_modules->modules[j].name); + j++; + } + } + + /* List finally the default modules of the hdt mode */ + if (current_mode->mode != hdt_mode.mode && + hdt_mode.default_modules && + hdt_mode.default_modules->modules) { + j = 0; + while (hdt_mode.default_modules->modules[j].name) { + /* + * Any default command that is present in hdt mode but + * not in the current mode is available. A default + * command can be redefined in the current mode though. + * This next call test this use case: if it is + * overwritten, do not print it again. + */ + find_cli_callback_descr(hdt_mode.default_modules->modules[j].name, + current_mode->default_modules, + &associated_module); + if (associated_module == NULL) + printf("%s ", + hdt_mode.default_modules->modules[j].name); + j++; + } + printf("\n"); + } + + main_show_modes(argc, argv, hardware); +} + +/** + * show_cli_help - shared helper to show available commands + **/ +static void goto_menu(int argc __unused, char** argv __unused, + struct s_hardware *hardware) +{ + char version_string[256]; + snprintf(version_string, sizeof version_string, "%s %s by %s", + PRODUCT_NAME, VERSION, AUTHOR); + start_menu_mode(hardware, version_string); + return; +} + +/** + * main_show_summary - give an overview of the system + **/ +void main_show_summary(int argc __unused, char **argv __unused, + struct s_hardware *hardware) +{ + detect_pci(hardware); /* pxe is detected in the pci */ + detect_dmi(hardware); + cpu_detect(hardware); + clear_screen(); + main_show_cpu(argc, argv, hardware); + if (hardware->is_dmi_valid) { + more_printf("System\n"); + more_printf(" Manufacturer : %s\n", + hardware->dmi.system.manufacturer); + more_printf(" Product Name : %s\n", + hardware->dmi.system.product_name); + more_printf(" Serial : %s\n", + hardware->dmi.system.serial); + more_printf("Bios\n"); + more_printf(" Version : %s\n", hardware->dmi.bios.version); + more_printf(" Release : %s\n", + hardware->dmi.bios.release_date); + + int argc = 2; + char *argv[2] = { "0", "0" }; + show_dmi_memory_modules(argc, argv, hardware); + } + main_show_pci(argc, argv, hardware); + + if (hardware->is_pxe_valid) + main_show_pxe(argc, argv, hardware); + + main_show_kernel(argc, argv, hardware); +} + +void main_show_hdt(int argc __unused, char **argv __unused, + struct s_hardware *hardware __unused) +{ + printf("HDT\n"); + printf(" Product : %s\n", PRODUCT_NAME); + printf(" Version : %s\n", VERSION); + printf(" Author : %s\n", AUTHOR); + printf(" Contact : %s\n", CONTACT); + char *contributors[NB_CONTRIBUTORS] = CONTRIBUTORS; + for (int c = 0; c < NB_CONTRIBUTORS; c++) { + printf(" Contributor : %s\n", contributors[c]); + } +} + +/* Default hdt mode */ +struct cli_callback_descr list_hdt_default_modules[] = { + { + .name = CLI_CLEAR, + .exec = cli_clear_screen, + }, + { + .name = CLI_EXIT, + .exec = do_exit, + }, + { + .name = CLI_HELP, + .exec = show_cli_help, + }, + { + .name = CLI_MENU, + .exec = goto_menu, + }, + { + .name = NULL, + .exec = NULL + }, +}; + +struct cli_callback_descr list_hdt_show_modules[] = { + { + .name = CLI_SUMMARY, + .exec = main_show_summary, + }, + { + .name = CLI_PCI, + .exec = main_show_pci, + }, + { + .name = CLI_DMI, + .exec = main_show_dmi, + }, + { + .name = CLI_CPU, + .exec = main_show_cpu, + }, + { + .name = CLI_PXE, + .exec = main_show_pxe, + }, + { + .name = CLI_SYSLINUX, + .exec = main_show_syslinux, + }, + { + .name = CLI_KERNEL, + .exec = main_show_kernel, + }, + { + .name = CLI_VESA, + .exec = main_show_vesa, + }, + { + .name = CLI_HDT, + .exec = main_show_hdt, + }, + { + .name = "modes", + .exec = main_show_modes, + }, + { + .name = NULL, + .exec = NULL, + }, +}; + +struct cli_callback_descr list_hdt_set_modules[] = { + { + .name = CLI_MODE, + .exec = cli_set_mode, + }, + { + .name = NULL, + .exec = NULL, + }, +}; + +struct cli_module_descr hdt_default_modules = { + .modules = list_hdt_default_modules, +}; + +struct cli_module_descr hdt_show_modules = { + .modules = list_hdt_show_modules, + .default_callback = main_show_summary, +}; + +struct cli_module_descr hdt_set_modules = { + .modules = list_hdt_set_modules, +}; + +struct cli_mode_descr hdt_mode = { + .mode = HDT_MODE, + .name = CLI_HDT, + .default_modules = &hdt_default_modules, + .show_modules = &hdt_show_modules, + .set_modules = &hdt_set_modules, +}; diff --git a/com32/hdt/hdt-cli-kernel.c b/com32/hdt/hdt-cli-kernel.c new file mode 100644 index 0000000..d9ba27f --- /dev/null +++ b/com32/hdt/hdt-cli-kernel.c @@ -0,0 +1,158 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include +#include +#include +#include + +#include "hdt-cli.h" +#include "hdt-common.h" + +void main_show_kernel(int argc __unused, char **argv __unused, + struct s_hardware *hardware) +{ + char buffer[1024]; + struct pci_device *pci_device; + bool found = false; + char kernel_modules[LINUX_KERNEL_MODULE_SIZE * + MAX_KERNEL_MODULES_PER_PCI_DEVICE]; + + memset(buffer, 0, sizeof(buffer)); + + detect_pci(hardware); + more_printf("Kernel modules\n"); + +// more_printf(" PCI device no: %d \n", p->pci_device_pos); + + if (hardware->modules_pcimap_return_code == -ENOMODULESPCIMAP) { + more_printf(" modules.pcimap is missing\n"); + return; + } + + /* For every detected pci device, compute its submenu */ + for_each_pci_func(pci_device, hardware->pci_domain) { + memset(kernel_modules, 0, sizeof kernel_modules); + + for (int kmod = 0; + kmod < pci_device->dev_info->linux_kernel_module_count; + kmod++) { + if (kmod > 0) { + strncat(kernel_modules, " | ", 3); + } + strncat(kernel_modules, + pci_device->dev_info->linux_kernel_module[kmod], + LINUX_KERNEL_MODULE_SIZE - 1); + } + + if ((pci_device->dev_info->linux_kernel_module_count > 0) + && (!strstr(buffer, kernel_modules))) { + found = true; + if (pci_device->dev_info->linux_kernel_module_count > 1) + strncat(buffer, "(", 1); + strncat(buffer, kernel_modules, sizeof(kernel_modules)); + if (pci_device->dev_info->linux_kernel_module_count > 1) + strncat(buffer, ")", 1); + strncat(buffer, " # ", 3); + } + + } + if (found == true) { + strncat(buffer, "\n", 1); + more_printf(buffer); + } +} + +static void show_kernel_modules(int argc __unused, char **argv __unused, + struct s_hardware *hardware) +{ + struct pci_device *pci_device; + char kernel_modules[LINUX_KERNEL_MODULE_SIZE * + MAX_KERNEL_MODULES_PER_PCI_DEVICE]; + bool nopciids = false; + bool nomodulespcimap = false; + char modules[MAX_PCI_CLASSES][256]; + char category_name[MAX_PCI_CLASSES][256]; + + detect_pci(hardware); + memset(&modules, 0, sizeof(modules)); + + if (hardware->pci_ids_return_code == -ENOPCIIDS) { + nopciids = true; + printf(" Missing pci.ids, we can't compute the list\n"); + return; + } + + if (hardware->modules_pcimap_return_code == -ENOMODULESPCIMAP) { + nomodulespcimap = true; + printf + (" Missing modules.pcimap, we can't compute the list\n"); + return; + } + + for_each_pci_func(pci_device, hardware->pci_domain) { + memset(kernel_modules, 0, sizeof kernel_modules); + + for (int kmod = 0; + kmod < pci_device->dev_info->linux_kernel_module_count; + kmod++) { + strncat(kernel_modules, + pci_device->dev_info->linux_kernel_module[kmod], + LINUX_KERNEL_MODULE_SIZE - 1); + strncat(kernel_modules, " ", 1); + } + + if ((pci_device->dev_info->linux_kernel_module_count > 0) + && (!strstr(modules[pci_device->class[2]], kernel_modules))) { + strncat(modules[pci_device->class[2]], kernel_modules, + sizeof(kernel_modules)); + snprintf(category_name[pci_device->class[2]], + sizeof(category_name[pci_device->class[2]]), + "%s", pci_device->dev_info->category_name); + } + } + /* Print the found items */ + for (int i = 0; i < MAX_PCI_CLASSES; i++) { + if (strlen(category_name[i]) > 1) { + printf("%s : %s\n", category_name[i], modules[i]); + } + } +} + +struct cli_module_descr kernel_show_modules = { + .modules = NULL, + .default_callback = show_kernel_modules, +}; + +struct cli_mode_descr kernel_mode = { + .mode = KERNEL_MODE, + .name = CLI_KERNEL, + .default_modules = NULL, + .show_modules = &kernel_show_modules, + .set_modules = NULL, +}; diff --git a/com32/hdt/hdt-cli-pci.c b/com32/hdt/hdt-cli-pci.c new file mode 100644 index 0000000..1213c32 --- /dev/null +++ b/com32/hdt/hdt-cli-pci.c @@ -0,0 +1,313 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include +#include +#include + +#include "hdt-cli.h" +#include "hdt-common.h" + +void main_show_pci(int argc __unused, char **argv __unused, + struct s_hardware *hardware) +{ + cli_detect_pci(hardware); + + more_printf("PCI\n"); + more_printf(" NB Devices : %d\n", hardware->nb_pci_devices); +} + +static void show_pci_device(int argc, char **argv, + struct s_hardware *hardware) +{ + int i = 0; + struct pci_device *pci_device = NULL, *temp_pci_device; + int pcidev = -1; + bool nopciids = false; + bool nomodulespcimap = false; + char kernel_modules[LINUX_KERNEL_MODULE_SIZE * + MAX_KERNEL_MODULES_PER_PCI_DEVICE]; + int bus = 0, slot = 0, func = 0; + + /* Sanitize arguments */ + if (argc <= 0) { + printf("show device \n"); + return; + } else + pcidev = strtol(argv[0], (char **)NULL, 10); + + if (errno == ERANGE) { + printf("This PCI device number is incorrect\n"); + return; + } + if ((pcidev > hardware->nb_pci_devices) || (pcidev <= 0)) { + printf("PCI device %d doesn't exists\n", pcidev); + return; + } + if (hardware->pci_ids_return_code == -ENOPCIIDS) { + nopciids = true; + } + if (hardware->modules_pcimap_return_code == -ENOMODULESPCIMAP) { + nomodulespcimap = true; + } + + for_each_pci_func(temp_pci_device, hardware->pci_domain) { + i++; + if (i == pcidev) { + bus = __pci_bus; + slot = __pci_slot; + func = __pci_func; + pci_device = temp_pci_device; + } + } + + if (pci_device == NULL) { + printf("We were enabled to find PCI device %d\n", pcidev); + return; + } + + memset(kernel_modules, 0, sizeof kernel_modules); + for (int kmod = 0; + kmod < pci_device->dev_info->linux_kernel_module_count; kmod++) { + if (kmod > 0) { + strncat(kernel_modules, " | ", 3); + } + strncat(kernel_modules, + pci_device->dev_info->linux_kernel_module[kmod], + LINUX_KERNEL_MODULE_SIZE - 1); + } + if (pci_device->dev_info->linux_kernel_module_count == 0) + strlcpy(kernel_modules, "unknown", 7); + + printf("PCI Device %d\n", pcidev); + + if (nopciids == false) { + printf("Vendor Name : %s\n", + pci_device->dev_info->vendor_name); + printf("Product Name : %s\n", + pci_device->dev_info->product_name); + printf("Class Name : %s\n", + pci_device->dev_info->class_name); + } + + if (nomodulespcimap == false) { + printf("Kernel module : %s\n", kernel_modules); + } + + printf("Vendor ID : %04x\n", pci_device->vendor); + printf("Product ID : %04x\n", pci_device->product); + printf("SubVendor ID : %04x\n", pci_device->sub_vendor); + printf("SubProduct ID : %04x\n", pci_device->sub_product); + printf("Class ID : %02x.%02x.%02x\n", pci_device->class[2], + pci_device->class[1], pci_device->class[0]); + printf("Revision : %02x\n", pci_device->revision); + if ((pci_device->dev_info->irq > 0) + && (pci_device->dev_info->irq < 255)) + more_printf("IRQ : %0d\n", pci_device->dev_info->irq); + printf("Latency : %0d\n", pci_device->dev_info->latency); + printf("PCI Bus : %02d\n", bus); + printf("PCI Slot : %02d\n", slot); + printf("PCI Func : %02d\n", func); + + if (hardware->is_pxe_valid == true) { + if ((hardware->pxe.pci_device != NULL) + && (hardware->pxe.pci_device == pci_device)) { + printf("Mac Address : %s\n", hardware->pxe.mac_addr); + printf("PXE : Current boot device\n"); + } + } +} + +static void show_pci_devices(int argc __unused, char **argv __unused, + struct s_hardware *hardware) +{ + int i = 1; + struct pci_device *pci_device; + char kernel_modules[LINUX_KERNEL_MODULE_SIZE * + MAX_KERNEL_MODULES_PER_PCI_DEVICE]; + bool nopciids = false; + bool nomodulespcimap = false; + char first_line[81]; + char second_line[81]; + + reset_more_printf(); + more_printf("%d PCI devices detected\n", hardware->nb_pci_devices); + + if (hardware->pci_ids_return_code == -ENOPCIIDS) { + nopciids = true; + } + if (hardware->modules_pcimap_return_code == -ENOMODULESPCIMAP) { + nomodulespcimap = true; + } + + /* For every detected pci device, compute its submenu */ + for_each_pci_func(pci_device, hardware->pci_domain) { + memset(kernel_modules, 0, sizeof kernel_modules); + for (int kmod = 0; + kmod < pci_device->dev_info->linux_kernel_module_count; + kmod++) { + if (kmod > 0) { + strncat(kernel_modules, " | ", 3); + } + strncat(kernel_modules, + pci_device->dev_info->linux_kernel_module[kmod], + LINUX_KERNEL_MODULE_SIZE - 1); + } + if (pci_device->dev_info->linux_kernel_module_count == 0) + strlcpy(kernel_modules, "unknown", 7); + + if (nopciids == false) { + snprintf(first_line, sizeof(first_line), + "%02d: %s %s \n", i, + pci_device->dev_info->vendor_name, + pci_device->dev_info->product_name); + if (nomodulespcimap == false) + snprintf(second_line, sizeof(second_line), + " # %-25s # Kmod: %s\n", + pci_device->dev_info->class_name, + kernel_modules); + else + snprintf(second_line, sizeof(second_line), + " # %-25s # ID:%04x:%04x[%04x:%04x]\n", + pci_device->dev_info->class_name, + pci_device->vendor, + pci_device->product, + pci_device->sub_vendor, + pci_device->sub_product); + + more_printf(first_line); + more_printf(second_line); + more_printf("\n"); + } else if (nopciids == true) { + if (nomodulespcimap == true) { + more_printf("%02d: %04x:%04x [%04x:%04x] \n", + i, pci_device->vendor, + pci_device->product, + pci_device->sub_vendor, + pci_device->sub_product); + } else { + more_printf + ("%02d: %04x:%04x [%04x:%04x] Kmod:%s\n", i, + pci_device->vendor, pci_device->product, + pci_device->sub_vendor, + pci_device->sub_product, kernel_modules); + } + } + i++; + } +} + +static void show_pci_irq(int argc __unused, char **argv __unused, + struct s_hardware *hardware) +{ + struct pci_device *pci_device; + bool nopciids = false; + + reset_more_printf(); + more_printf("%d PCI devices detected\n", hardware->nb_pci_devices); + more_printf("IRQ : product\n"); + more_printf("-------------\n"); + + if (hardware->pci_ids_return_code == -ENOPCIIDS) { + nopciids = true; + } + + /* For every detected pci device, compute its submenu */ + for_each_pci_func(pci_device, hardware->pci_domain) { + /* Only display valid IRQs */ + if ((pci_device->dev_info->irq > 0) + && (pci_device->dev_info->irq < 255)) { + if (nopciids == false) { + more_printf("%02d : %s %s \n", + pci_device->dev_info->irq, + pci_device->dev_info->vendor_name, + pci_device->dev_info->product_name); + } else { + more_printf("%02d : %04x:%04x [%04x:%04x] \n", + pci_device->dev_info->irq, + pci_device->vendor, + pci_device->product, + pci_device->sub_vendor, + pci_device->sub_product); + } + } + } +} + +struct cli_callback_descr list_pci_show_modules[] = { + { + .name = CLI_IRQ, + .exec = show_pci_irq, + }, + { + .name = CLI_PCI_DEVICE, + .exec = show_pci_device, + }, + { + .name = NULL, + .exec = NULL, + }, +}; + +struct cli_module_descr pci_show_modules = { + .modules = list_pci_show_modules, + .default_callback = show_pci_devices, +}; + +struct cli_mode_descr pci_mode = { + .mode = PCI_MODE, + .name = CLI_PCI, + .default_modules = NULL, + .show_modules = &pci_show_modules, + .set_modules = NULL, +}; + +void cli_detect_pci(struct s_hardware *hardware) +{ + bool error = false; + if (hardware->pci_detection == false) { + detect_pci(hardware); + if (hardware->pci_ids_return_code == -ENOPCIIDS) { + printf + ("The pci.ids file is missing, device names can't be computed.\n"); + printf("Please put one in same dir as hdt\n"); + error = true; + } + if (hardware->modules_pcimap_return_code == -ENOMODULESPCIMAP) { + printf + ("The modules.pcimap file is missing, device names can't be computed.\n"); + printf("Please put one in same dir as hdt\n"); + error = true; + } + if (error == true) { + char tempbuf[10]; + printf("Press enter to continue\n"); + fgets(tempbuf, sizeof(tempbuf), stdin); + } + } +} diff --git a/com32/hdt/hdt-cli-pxe.c b/com32/hdt/hdt-cli-pxe.c new file mode 100644 index 0000000..1d00cae --- /dev/null +++ b/com32/hdt/hdt-cli-pxe.c @@ -0,0 +1,92 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include + +#include "hdt-cli.h" +#include "hdt-common.h" + +void main_show_pxe(int argc __unused, char **argv __unused, + struct s_hardware *hardware) +{ + char buffer[81]; + memset(buffer, 0, sizeof(81)); + if (hardware->sv->filesystem != SYSLINUX_FS_PXELINUX) { + printf("You are not currently using PXELINUX\n"); + return; + } + + detect_pxe(hardware); + printf("PXE\n"); + if (hardware->is_pxe_valid == false) { + printf(" No valid PXE ROM found\n"); + return; + } + + struct s_pxe *p = &hardware->pxe; + printf(" PCI device no: %d \n", p->pci_device_pos); + + if (hardware->pci_ids_return_code == -ENOPCIIDS || + (p->pci_device == NULL)) { + snprintf(buffer, sizeof(buffer), + " PCI ID : %04x:%04x[%04x:%04X] rev(%02x)\n", + p->vendor_id, p->product_id, p->subvendor_id, + p->subproduct_id, p->rev); + snprintf(buffer, sizeof(buffer), + " PCI Bus pos. : %02x:%02x.%02x\n", p->pci_bus, + p->pci_dev, p->pci_func); + printf(buffer); + } else { + snprintf(buffer, sizeof(buffer), " Manufacturer : %s \n", + p->pci_device->dev_info->vendor_name); + printf(buffer); + snprintf(buffer, sizeof(buffer), " Product : %s \n", + p->pci_device->dev_info->product_name); + printf(buffer); + } + printf(" Addresses : %d.%d.%d.%d @ %s\n", p->ip_addr[0], + p->ip_addr[1], p->ip_addr[2], p->ip_addr[3], p->mac_addr); +} + +struct cli_module_descr pxe_show_modules = { + .modules = NULL, + .default_callback = main_show_pxe, +}; + +struct cli_mode_descr pxe_mode = { + .mode = PXE_MODE, + .name = CLI_PXE, + .default_modules = NULL, + .show_modules = &pxe_show_modules, + .set_modules = NULL, +}; diff --git a/com32/hdt/hdt-cli-syslinux.c b/com32/hdt/hdt-cli-syslinux.c new file mode 100644 index 0000000..0d78bc6 --- /dev/null +++ b/com32/hdt/hdt-cli-syslinux.c @@ -0,0 +1,61 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include + +#include "hdt-cli.h" +#include "hdt-common.h" + +void main_show_syslinux(int argc __unused, char **argv __unused, + struct s_hardware *hardware) +{ + printf("SYSLINUX\n"); + printf(" Bootloader : %s\n", hardware->syslinux_fs); + printf(" Version : %s\n", hardware->sv->version_string + 2); + printf(" Version : %u\n", hardware->sv->version); + printf(" Max API : %u\n", hardware->sv->max_api); + printf(" Copyright : %s\n", hardware->sv->copyright_string + 1); +} + +struct cli_module_descr syslinux_show_modules = { + .modules = NULL, + .default_callback = main_show_syslinux, +}; + +struct cli_mode_descr syslinux_mode = { + .mode = SYSLINUX_MODE, + .name = CLI_SYSLINUX, + .default_modules = NULL, + .show_modules = &syslinux_show_modules, + .set_modules = NULL, +}; diff --git a/com32/hdt/hdt-cli-vesa.c b/com32/hdt/hdt-cli-vesa.c new file mode 100644 index 0000000..70d1dc6 --- /dev/null +++ b/com32/hdt/hdt-cli-vesa.c @@ -0,0 +1,103 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- +*/ + +#include "hdt-cli.h" +#include "hdt-common.h" +#include +#include +#include +#include + +void main_show_vesa(int argc __unused, char **argv __unused, + struct s_hardware *hardware) +{ + detect_vesa(hardware); + if (hardware->is_vesa_valid == false) { + printf("No VESA BIOS detected\n"); + return; + } + printf("VESA\n"); + printf(" Vesa version : %d.%d\n", hardware->vesa.major_version, + hardware->vesa.minor_version); + printf(" Vendor : %s\n", hardware->vesa.vendor); + printf(" Product : %s\n", hardware->vesa.product); + printf(" Product rev. : %s\n", hardware->vesa.product_revision); + printf(" Software rev.: %d\n", hardware->vesa.software_rev); + printf(" Memory (KB) : %d\n", hardware->vesa.total_memory * 64); + printf(" Modes : %d\n", hardware->vesa.vmi_count); +} + +static void show_vesa_modes(int argc __unused, char **argv __unused, + struct s_hardware *hardware) +{ + detect_vesa(hardware); + if (hardware->is_vesa_valid == false) { + printf("No VESA BIOS detected\n"); + return; + } + reset_more_printf(); + printf(" ResH. x ResV x Bits : vga= : Vesa Mode\n"); + printf("----------------------------------------\n"); + + for (int i = 0; i < hardware->vesa.vmi_count; i++) { + struct vesa_mode_info *mi = &hardware->vesa.vmi[i].mi; + /* + * Sometimes, vesa bios reports 0x0 modes. + * We don't need to display that ones. + */ + if ((mi->h_res == 0) || (mi->v_res == 0)) continue; + printf("%5u %5u %3u %3d 0x%04x\n", + mi->h_res, mi->v_res, mi->bpp, + hardware->vesa.vmi[i].mode + 0x200, + hardware->vesa.vmi[i].mode); + } +} + +struct cli_callback_descr list_vesa_show_modules[] = { + { + .name = CLI_MODES, + .exec = show_vesa_modes, + }, + { + .name = NULL, + .exec = NULL, + }, +}; + +struct cli_module_descr vesa_show_modules = { + .modules = list_vesa_show_modules, + .default_callback = main_show_vesa, +}; + +struct cli_mode_descr vesa_mode = { + .mode = VESA_MODE, + .name = CLI_VESA, + .default_modules = NULL, + .show_modules = &vesa_show_modules, + .set_modules = NULL, +}; diff --git a/com32/hdt/hdt-cli-vpd.c b/com32/hdt/hdt-cli-vpd.c new file mode 100644 index 0000000..03614d3 --- /dev/null +++ b/com32/hdt/hdt-cli-vpd.c @@ -0,0 +1,74 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Pierre-Alexandre Meyer - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include +#include + +#include "hdt-cli.h" +#include "hdt-common.h" + +void main_show_vpd(int argc __unused, char **argv __unused, + struct s_hardware *hardware) +{ + reset_more_printf(); + detect_vpd(hardware); + + if (!hardware->is_vpd_valid) { + printf("No VPD structure detected.\n"); + return; + } + + printf("VPD present at address : 0x%s\n", hardware->vpd.base_address); + if (strlen(hardware->vpd.bios_build_id) > 0) + more_printf("Bios Build ID : %s\n", hardware->vpd.bios_build_id); + if (strlen(hardware->vpd.bios_release_date) > 0) + more_printf("Bios Release Date : %s\n", hardware->vpd.bios_release_date); + if (strlen(hardware->vpd.bios_version) > 0) + more_printf("Bios Version : %s\n", hardware->vpd.bios_version); + if (strlen(hardware->vpd.default_flash_filename) > 0) + more_printf("Default Flash Filename : %s\n", hardware->vpd.default_flash_filename); + if (strlen(hardware->vpd.box_serial_number) > 0) + more_printf("Box Serial Number : %s\n", hardware->vpd.box_serial_number); + if (strlen(hardware->vpd.motherboard_serial_number) > 0) + more_printf("Motherboard Serial Number : %s\n", hardware->vpd.motherboard_serial_number); + if (strlen(hardware->vpd.machine_type_model) > 0) + more_printf("Machine Type/Model : %s\n", hardware->vpd.machine_type_model); +} + +struct cli_module_descr vpd_show_modules = { + .modules = NULL, + .default_callback = main_show_vpd, +}; + +struct cli_mode_descr vpd_mode = { + .mode = VPD_MODE, + .name = CLI_VPD, + .default_modules = NULL, + .show_modules = &vpd_show_modules, + .set_modules = NULL, +}; diff --git a/com32/hdt/hdt-cli.c b/com32/hdt/hdt-cli.c new file mode 100644 index 0000000..871fd76 --- /dev/null +++ b/com32/hdt/hdt-cli.c @@ -0,0 +1,1025 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include "hdt-cli.h" +#include "hdt-common.h" +#include "lib-ansi.h" + +struct cli_mode_descr *list_modes[] = { + &hdt_mode, + &dmi_mode, + &syslinux_mode, + &pxe_mode, + &kernel_mode, + &cpu_mode, + &pci_mode, + &vesa_mode, + &vpd_mode, + NULL, +}; + +/* + * .aliases = {"q", "quit"} won't work since it is an array of pointers, not an + * array of variables. There is no easy way around it besides declaring the arrays of + * strings first. + */ +char *exit_aliases[] = {"q", "quit"}; +char *help_aliases[] = {"h", "?"}; + +/* List of aliases */ +struct cli_alias hdt_aliases[] = { + { + .command = CLI_EXIT, + .nb_aliases = 2, + .aliases = exit_aliases, + }, + { + .command = CLI_HELP, + .nb_aliases = 2, + .aliases = help_aliases, + }, +}; + +struct cli_mode_descr *current_mode; +int autocomplete_backlog; + +struct autocomplete_list { + char autocomplete_token[MAX_LINE_SIZE]; + struct autocomplete_list *next; +}; +struct autocomplete_list* autocomplete_head = NULL; +struct autocomplete_list* autocomplete_tail = NULL; +struct autocomplete_list* autocomplete_last_seen = NULL; + +static void autocomplete_add_token_to_list(const char *token) +{ + struct autocomplete_list *new = malloc(sizeof(struct autocomplete_list)); + + strncpy(new->autocomplete_token, token, sizeof(new->autocomplete_token)); + new->next = NULL; + autocomplete_backlog++; + + if (autocomplete_tail != NULL) + autocomplete_tail->next = new; + if (autocomplete_head == NULL) + autocomplete_head = new; + autocomplete_tail = new; +} + +static void autocomplete_destroy_list() +{ + struct autocomplete_list* tmp = NULL; + + while (autocomplete_head != NULL) { + tmp = autocomplete_head->next; + free(autocomplete_head); + autocomplete_head = tmp; + } + autocomplete_backlog = 0; + autocomplete_tail = NULL; + autocomplete_last_seen = NULL; +} + +/** + * set_mode - set the current mode of the cli + * @mode: mode to set + * + * Unlike cli_set_mode, this function is not used by the cli directly. + **/ +void set_mode(cli_mode_t mode, struct s_hardware* hardware) +{ + int i = 0; + + switch (mode) { + case EXIT_MODE: + hdt_cli.mode = mode; + break; + case HDT_MODE: + hdt_cli.mode = mode; + snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", + CLI_HDT); + break; + case PXE_MODE: + if (hardware->sv->filesystem != SYSLINUX_FS_PXELINUX) { + printf("You are not currently using PXELINUX\n"); + break; + } + hdt_cli.mode = mode; + snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", + CLI_PXE); + break; + case KERNEL_MODE: + detect_pci(hardware); + hdt_cli.mode = mode; + snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", + CLI_KERNEL); + break; + case SYSLINUX_MODE: + hdt_cli.mode = mode; + snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", + CLI_SYSLINUX); + break; + case VESA_MODE: + hdt_cli.mode = mode; + snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", + CLI_VESA); + break; + case PCI_MODE: + hdt_cli.mode = mode; + snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", + CLI_PCI); + if (!hardware->pci_detection) + cli_detect_pci(hardware); + break; + case CPU_MODE: + hdt_cli.mode = mode; + snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", + CLI_CPU); + if (!hardware->dmi_detection) + detect_dmi(hardware); + if (!hardware->cpu_detection) + cpu_detect(hardware); + break; + case DMI_MODE: + detect_dmi(hardware); + if (!hardware->is_dmi_valid) { + printf("No valid DMI table found, exiting.\n"); + break; + } + hdt_cli.mode = mode; + snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", + CLI_DMI); + break; + case VPD_MODE: + detect_vpd(hardware); + if (!hardware->is_vpd_valid) { + printf("No valid VPD table found, exiting.\n"); + break; + } + hdt_cli.mode = mode; + snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", + CLI_VPD); + break; + + default: + /* Invalid mode */ + printf("Unknown mode, please choose among:\n"); + while (list_modes[i]) { + printf("\t%s\n", list_modes[i]->name); + i++; + } + } + + find_cli_mode_descr(hdt_cli.mode, ¤t_mode); + /* There is not cli_mode_descr struct for the exit mode */ + if (current_mode == NULL && hdt_cli.mode != EXIT_MODE) { + /* Shouldn't get here... */ + printf("!!! BUG: Mode '%d' unknown.\n", hdt_cli.mode); + } +} + +/** + * mode_s_to_mode_t - given a mode string, return the cli_mode_t representation + **/ +cli_mode_t mode_s_to_mode_t(char *name) +{ + int i = 0; + + while (list_modes[i]) { + if (!strncmp(name, list_modes[i]->name, + sizeof(list_modes[i]->name))) + break; + i++; + } + + if (!list_modes[i]) + return INVALID_MODE; + else + return list_modes[i]->mode; +} + +/** + * find_cli_mode_descr - find the cli_mode_descr struct associated to a mode + * @mode: mode to look for + * @mode_found: store the mode if found, NULL otherwise + * + * Given a mode name, return a pointer to the associated cli_mode_descr + * structure. + * Note: the current mode name is stored in hdt_cli.mode. + **/ +void find_cli_mode_descr(cli_mode_t mode, struct cli_mode_descr **mode_found) +{ + int i = 0; + + while (list_modes[i] && + list_modes[i]->mode != mode) + i++; + + /* Shouldn't get here... */ + if (!list_modes[i]) + *mode_found = NULL; + else + *mode_found = list_modes[i]; +} + +/** + * expand_aliases - resolve aliases mapping + * @line: command line to parse + * @command: first token in the line + * @module: second token in the line + * @argc: number of arguments + * @argv: array of arguments + * + * We maintain a small list of static alises to enhance user experience. + * Only commands can be aliased (first token). Otherwise it can become really hairy... + **/ +static void expand_aliases(char *line __unused, char **command, char **module, + int *argc, char **argv) +{ + struct cli_mode_descr *mode; + int i, j; + + find_cli_mode_descr(mode_s_to_mode_t(*command), &mode); + if (mode != NULL && *module == NULL) { + /* + * The user specified a mode instead of `set mode...', e.g. + * `dmi' instead of `set mode dmi' + */ + + /* *argv is NULL since *module is NULL */ + *argc = 1; + *argv = malloc(*argc * sizeof(char *)); + argv[0] = malloc((sizeof(*command) + 1) * sizeof(char)); + strncpy(argv[0], *command, sizeof(*command) + 1); + dprintf("CLI DEBUG: ALIAS %s ", *command); + + strncpy(*command, CLI_SET, sizeof(CLI_SET)); /* set */ + + *module = malloc(sizeof(CLI_MODE) * sizeof(char)); + strncpy(*module, CLI_MODE, sizeof(CLI_MODE)); /* mode */ + + dprintf("--> %s %s %s\n", *command, *module, argv[0]); + goto out; + } + + /* Simple aliases mapping a single command to another one */ + for (i = 0; i < MAX_ALIASES; i++) { + for (j = 0; j < hdt_aliases[i].nb_aliases; j++) { + if (!strncmp(*command, hdt_aliases[i].aliases[j], + sizeof(hdt_aliases[i].aliases[j]))) { + dprintf("CLI DEBUG: ALIAS %s ", *command); + strncpy(*command, hdt_aliases[i].command, + sizeof(hdt_aliases[i].command) + 1); + dprintf("--> %s\n", *command); + goto out; /* Don't allow chaining aliases */ + } + } + } + return; + +out: + dprintf("CLI DEBUG: New parameters:\n"); + dprintf("CLI DEBUG: command = %s\n", *command); + dprintf("CLI DEBUG: module = %s\n", *module); + dprintf("CLI DEBUG: argc = %d\n", *argc); + for (i = 0; i < *argc; i++) + dprintf("CLI DEBUG: argv[%d] = %s\n", i, argv[0]); + return; +} + +/** + * parse_command_line - low level parser for the command line + * @line: command line to parse + * @command: first token in the line + * @module: second token in the line + * @argc: number of arguments + * @argv: array of arguments + * + * The format of the command line is: + *

[ []] + **/ +static void parse_command_line(char *line, char **command, char **module, + int *argc, char **argv) +{ + int argc_iter = 0, args_pos = 0, token_found = 0, token_len = 0; + int args_len = 0; + char *pch = NULL, *pch_next = NULL, *tmp_pch_next = NULL; + + *command = NULL; + *module = NULL; + *argc = 0; + + pch = line; + while (pch != NULL) { + pch_next = strchr(pch + 1, ' '); + tmp_pch_next = pch_next; + + /* + * Skip whitespaces if the user entered + * 'set mode foo' for 'set mode foo' + * ^ ^ + * |___|___ pch + * |___ pch_next <- wrong! + * + * We still keep the position into tmp_pch_next to compute + * the lenght of the current token. + */ + while (pch_next != NULL && !strncmp(pch_next, CLI_SPACE, 1)) + pch_next++; + + /* End of line guaranteed to be zeroed */ + if (pch_next == NULL) { + token_len = (int) (strchr(pch + 1, '\0') - pch); + args_len = token_len; + } + else { + token_len = (int) (tmp_pch_next - pch); + args_len = (int) (pch_next - pch); + } + + if (token_found == 0) { + /* Main command to execute */ + *command = malloc((token_len + 1) * sizeof(char)); + strncpy(*command, pch, token_len); + (*command)[token_len] = '\0'; + dprintf("CLI DEBUG: command = %s\n", *command); + args_pos += args_len; + } else if (token_found == 1) { + /* Module */ + *module = malloc((token_len + 1) * sizeof(char)); + strncpy(*module, pch, token_len); + (*module)[token_len] = '\0'; + dprintf("CLI DEBUG: module = %s\n", *module); + args_pos += args_len; + } else + (*argc)++; + + token_found++; + pch = pch_next; + } + dprintf("CLI DEBUG: argc = %d\n", *argc); + + /* Skip arguments handling if none is supplied */ + if (!*argc) + return; + + /* Transform the arguments string into an array */ + *argv = malloc(*argc * sizeof(char *)); + pch = strtok(line + args_pos, CLI_SPACE); + while (pch != NULL) { + dprintf("CLI DEBUG: argv[%d] = %s\n", argc_iter, pch); + argv[argc_iter] = malloc(sizeof(pch) * sizeof(char)); + strncpy(argv[argc_iter], pch, sizeof(pch)); + argc_iter++; + pch = strtok(NULL, CLI_SPACE); + /* + * strtok(NULL, CLI_SPACE) over a stream of spaces + * will return an empty string + */ + while (pch != NULL && !strncmp(pch, "", 1)) + pch = strtok(NULL, CLI_SPACE); + } +} + +/** + * find_cli_callback_descr - find a callback in a list of modules + * @module_name: Name of the module to find + * @modules_list: Lits of modules among which to find @module_name + * @module_found: Pointer to the matched module, NULL if not found + * + * Given a module name and a list of possible modules, find the corresponding + * module structure that matches the module name and store it in @module_found. + **/ +void find_cli_callback_descr(const char* module_name, + struct cli_module_descr* modules_list, + struct cli_callback_descr** module_found) +{ + int modules_iter = 0; + int module_len = strlen(module_name); + + if (modules_list == NULL) + goto not_found; + + /* Find the callback to execute */ + while (modules_list->modules[modules_iter].name && + strncmp(module_name, + modules_list->modules[modules_iter].name, + module_len) != 0) + modules_iter++; + + if (modules_list->modules[modules_iter].name) { + *module_found = &(modules_list->modules[modules_iter]); + dprintf("CLI DEBUG: module %s found\n", (*module_found)->name); + return; + } + +not_found: + *module_found = NULL; + return; +} + +/** + * autocomplete_command - print matching commands + * @command: Beginning of the command + * + * Given a string @command, print all availables commands starting with + * @command. Commands are found within the list of commands for the current + * mode and the hdt mode (if the current mode is not hdt). + **/ +static void autocomplete_command(char *command) +{ + int j = 0; + struct cli_callback_descr* associated_module = NULL; + + /* First take care of the two special commands: 'show' and 'set' */ + if (strncmp(CLI_SHOW, command, strlen(command)) == 0) { + printf("%s\n", CLI_SHOW); + autocomplete_add_token_to_list(CLI_SHOW); + } + if (strncmp(CLI_SET, command, strlen(command)) == 0) { + printf("%s\n", CLI_SET); + autocomplete_add_token_to_list(CLI_SET); + } + + /* + * Then, go through the modes for the special case + * '' -> 'set mode ' + */ + while (list_modes[j]) { + if (strncmp(list_modes[j]->name, command, strlen(command)) == 0) { + printf("%s\n", list_modes[j]->name); + autocomplete_add_token_to_list(list_modes[j]->name); + } + j++; + } + + /* + * Let's go now through the list of default_modules for the current mode + * (single token commands for the current_mode) + */ + j = 0; + while (current_mode->default_modules->modules[j].name) { + if (strncmp(current_mode->default_modules->modules[j].name, + command, + strlen(command)) == 0) { + printf("%s\n", + current_mode->default_modules->modules[j].name); + autocomplete_add_token_to_list(current_mode->default_modules->modules[j].name); + } + j++; + } + + /* + * Finally, if the current_mode is not hdt, list the available + * default_modules of hdt (these are always available from any mode). + */ + if (current_mode->mode == HDT_MODE) + return; + + j = 0; + while (hdt_mode.default_modules->modules[j].name) { + /* + * Any default command that is present in hdt mode but + * not in the current mode is available. A default + * command can be redefined in the current mode though. + * This next call tests this use case: if it is + * overwritten, do not print it again. + */ + find_cli_callback_descr(hdt_mode.default_modules->modules[j].name, + current_mode->default_modules, + &associated_module); + if (associated_module == NULL && + strncmp(command, + hdt_mode.default_modules->modules[j].name, + strlen(command)) == 0) { + printf("%s\n", + hdt_mode.default_modules->modules[j].name); + autocomplete_add_token_to_list(hdt_mode.default_modules->modules[j].name); + } + j++; + } +} + +/** + * autocomplete_module - print matching modules + * @command: Command on the command line (not NULL) + * @module: Beginning of the module + * + * Given a command @command and a string @module, print all availables modules + * starting with @module for command @command. Commands are found within the + * list of commands for the current mode and the hdt mode (if the current mode + * is not hdt). + **/ +static void autocomplete_module(char *command, char* module) +{ + int j = 0; + char autocomplete_full_line[MAX_LINE_SIZE]; + + if (strncmp(CLI_SHOW, command, strlen(command)) == 0) { + while (current_mode->show_modules->modules[j].name) { + if (strncmp(current_mode->show_modules->modules[j].name, + module, + strlen(module)) == 0) { + printf("%s\n", + current_mode->show_modules->modules[j].name); + sprintf(autocomplete_full_line, "%s %s", + CLI_SHOW, current_mode->show_modules->modules[j].name); + autocomplete_add_token_to_list(autocomplete_full_line); + } + j++; + } + } else if (strncmp(CLI_SET, command, strlen(command)) == 0) { + j = 0; + while (current_mode->set_modules->modules[j].name) { + if (strncmp(current_mode->set_modules->modules[j].name, + module, + strlen(module)) == 0) { + printf("%s\n", + current_mode->set_modules->modules[j].name); + sprintf(autocomplete_full_line, "%s %s", + CLI_SET, current_mode->set_modules->modules[j].name); + autocomplete_add_token_to_list(autocomplete_full_line); + } + j++; + } + } +} + +/** + * autocomplete - find possible matches for a command line + * @line: command line to parse + **/ +static void autocomplete(char *line) +{ + int i; + int argc = 0; + char *command = NULL, *module = NULL; + char **argv = NULL; + + parse_command_line(line, &command, &module, &argc, argv); + + /* If the user specified arguments, there is nothing we can complete */ + if (argc != 0) + goto out; + + /* No argument, (the start of) a module has been specified */ + if (module != NULL) { + autocomplete_module(command, module); + goto out; + } + + /* No argument, no module, (the start of) a command has been specified */ + if (command != NULL) { + autocomplete_command(command); + goto out; + } + + /* Nothing specified, list available commands */ + //autocomplete_commands(); + +out: + /* Let's not forget to clean ourselves */ + free(command); + free(module); + for (i = 0; i < argc; i++) + free(argv[i]); + free(argv); + return; +} + + +/** + * exec_command - main logic to map the command line to callbacks + **/ +static void exec_command(char *line, + struct s_hardware *hardware) +{ + int argc, i = 0; + char *command = NULL, *module = NULL; + char **argv = NULL; + struct cli_callback_descr* current_module = NULL; + + /* This will allocate memory that will need to be freed */ + parse_command_line(line, &command, &module, &argc, argv); + + /* Expand shortcuts, if needed */ + expand_aliases(line, &command, &module, &argc, argv); + + if (module == NULL) { + dprintf("CLI DEBUG: single command detected\n", CLI_SHOW); + /* + * A single word was specified: look at the list of default + * commands in the current mode to see if there is a match. + * If not, it may be a generic function (exit, help, ...). These + * are stored in the list of default commands of the hdt mode. + */ + find_cli_callback_descr(command, current_mode->default_modules, + ¤t_module); + if (current_module != NULL) + return current_module->exec(argc, argv, hardware); + else if (!strncmp(command, CLI_SHOW, sizeof(CLI_SHOW) - 1) && + current_mode->show_modules != NULL && + current_mode->show_modules->default_callback != NULL) + return current_mode->show_modules + ->default_callback(argc, + argv, + hardware); + else if (!strncmp(command, CLI_SET, sizeof(CLI_SET) - 1) && + current_mode->set_modules != NULL && + current_mode->set_modules->default_callback != NULL) + return current_mode->set_modules + ->default_callback(argc, + argv, + hardware); + else { + find_cli_callback_descr(command, hdt_mode.default_modules, + ¤t_module); + if (current_module != NULL) + return current_module->exec(argc, argv, hardware); + } + + printf("unknown command: '%s'\n", command); + return; + } + + /* + * A module has been specified! We now need to find the type of command. + * + * The syntax of the cli is the following: + * + * e.g. + * dmi> show system + * dmi> show bank 1 + * dmi> show memory 0 1 + * pci> show device 12 + * hdt> set mode dmi + */ + if (!strncmp(command, CLI_SHOW, sizeof(CLI_SHOW) - 1)) { + dprintf("CLI DEBUG: %s command detected\n", CLI_SHOW); + find_cli_callback_descr(module, current_mode->show_modules, + ¤t_module); + /* Execute the callback */ + if (current_module != NULL) + return current_module->exec(argc, argv, hardware); + else { + find_cli_callback_descr(module, hdt_mode.show_modules, + ¤t_module); + if (current_module != NULL) + return current_module->exec(argc, argv, hardware); + } + + printf("unknown module: '%s'\n", module); + return; + + } else if (!strncmp(command, CLI_SET, sizeof(CLI_SET) - 1)) { + dprintf("CLI DEBUG: %s command detected\n", CLI_SET); + find_cli_callback_descr(module, current_mode->set_modules, + ¤t_module); + /* Execute the callback */ + if (current_module != NULL) + return current_module->exec(argc, argv, hardware); + else { + find_cli_callback_descr(module, hdt_mode.set_modules, + ¤t_module); + if (current_module != NULL) + return current_module->exec(argc, argv, hardware); + } + + printf("unknown module: '%s'\n", module); + return; + + } + + printf("I don't understand: '%s'. Try 'help'.\n", line); + + /* Let's not forget to clean ourselves */ + free(command); + free(module); + for (i = 0; i < argc; i++) + free(argv[i]); + free(argv); +} + +static void reset_prompt() +{ + /* No need to display the prompt if we exit */ + if (hdt_cli.mode != EXIT_MODE) { + printf("%s", hdt_cli.prompt); + /* Reset the line */ + memset(hdt_cli.input, '\0', MAX_LINE_SIZE); + hdt_cli.cursor_pos = 0; + } +} + +/* Code that manages the cli mode */ +void start_cli_mode(struct s_hardware *hardware) +{ + int current_key = 0; + int future_history_pos=1; /* Temp variable*/ + bool display_history=true; /* Temp Variable*/ + char temp_command[MAX_LINE_SIZE]; + + hdt_cli.cursor_pos=0; + memset(hdt_cli.input, '\0', MAX_LINE_SIZE); + memset(hdt_cli.history, '\0', sizeof(hdt_cli.history)); + hdt_cli.history_pos=1; + hdt_cli.max_history_pos=1; + + /* Find the mode selected */ + set_mode(HDT_MODE, hardware); + find_cli_mode_descr(hdt_cli.mode, ¤t_mode); + if (current_mode == NULL) { + /* Shouldn't get here... */ + printf("!!! BUG: Mode '%d' unknown.\n", hdt_cli.mode); + return; + } + + printf("Entering CLI mode\n"); + + /* Display the cursor */ + display_cursor(true); + + reset_prompt(); + + while (hdt_cli.mode != EXIT_MODE) { + + //fgets(cli_line, sizeof cli_line, stdin); + current_key = get_key(stdin, 0); + + /* Reset autocomplete buffer unless TAB is pressed */ + if (current_key != KEY_TAB) + autocomplete_destroy_list(); + + switch (current_key) { + /* clear until then end of line */ + case KEY_CTRL('k'): + /* Clear the end of the line */ + clear_end_of_line(); + memset(&hdt_cli.input[hdt_cli.cursor_pos], 0, + strlen(hdt_cli.input) - hdt_cli.cursor_pos); + break; + + case KEY_CTRL('c'): + printf("\n"); + reset_prompt(); + break; + + case KEY_LEFT: + if (hdt_cli.cursor_pos > 0) { + move_cursor_left(1); + hdt_cli.cursor_pos--; + } + break; + + case KEY_RIGHT: + if (hdt_cli.cursor_pos < (int)strlen(hdt_cli.input)) { + move_cursor_right(1); + hdt_cli.cursor_pos++; + } + break; + + case KEY_CTRL('e'): + case KEY_END: + /* Calling with a 0 value will make the cursor move */ + /* So, let's move the cursor only if needed */ + if ((strlen(hdt_cli.input) - hdt_cli.cursor_pos) > 0) { + /* Return to the begining of line */ + move_cursor_right(strlen(hdt_cli.input) - hdt_cli.cursor_pos); + hdt_cli.cursor_pos = strlen(hdt_cli.input); + } + break; + + case KEY_CTRL('a'): + case KEY_HOME: + /* Calling with a 0 value will make the cursor move */ + /* So, let's move the cursor only if needed */ + if (hdt_cli.cursor_pos > 0) { + /* Return to the begining of line */ + move_cursor_left(hdt_cli.cursor_pos); + hdt_cli.cursor_pos = 0; + } + break; + + case KEY_UP: + /* We have to compute the next position*/ + future_history_pos=hdt_cli.history_pos; + if (future_history_pos==1) { + future_history_pos=MAX_HISTORY_SIZE-1; + } else { + future_history_pos--; + } + /* Does the next position is valid */ + if (strlen(hdt_cli.history[future_history_pos])==0) break; + + /* Let's make that future position the one we use*/ + hdt_cli.history_pos=future_history_pos; + + /* Clear the line */ + clear_line(); + + /* Move to the begining of line*/ + move_cursor_to_column(0); + + reset_prompt(); + printf("%s",hdt_cli.history[hdt_cli.history_pos]); + strncpy(hdt_cli.input,hdt_cli.history[hdt_cli.history_pos],sizeof(hdt_cli.input)); + hdt_cli.cursor_pos=strlen(hdt_cli.input); + break; + + case KEY_DOWN: + display_history=true; + + /* We have to compute the next position*/ + future_history_pos=hdt_cli.history_pos; + if (future_history_pos==MAX_HISTORY_SIZE-1) { + future_history_pos=1; + } else { + future_history_pos++; + } + /* Does the next position is valid */ + if (strlen(hdt_cli.history[future_history_pos])==0) display_history = false; + + /* An exception is made to reach the last empty line */ + if (future_history_pos==hdt_cli.max_history_pos) display_history=true; + if (display_history==false) break; + + /* Let's make that future position the one we use*/ + hdt_cli.history_pos=future_history_pos; + + /* Clear the line */ + clear_line(); + + /* Move to the begining of line*/ + move_cursor_to_column(0); + + reset_prompt(); + printf("%s",hdt_cli.history[hdt_cli.history_pos]); + strncpy(hdt_cli.input,hdt_cli.history[hdt_cli.history_pos],sizeof(hdt_cli.input)); + hdt_cli.cursor_pos=strlen(hdt_cli.input); + break; + + case KEY_TAB: + if (autocomplete_backlog) { + clear_line(); + /* Move to the begining of line*/ + move_cursor_to_column(0); + reset_prompt(); + printf("%s",autocomplete_last_seen->autocomplete_token); + strncpy(hdt_cli.input,autocomplete_last_seen->autocomplete_token,sizeof(hdt_cli.input)); + hdt_cli.cursor_pos=strlen(hdt_cli.input); + + /* Cycle through the list */ + autocomplete_last_seen = autocomplete_last_seen->next; + if (autocomplete_last_seen == NULL) + autocomplete_last_seen = autocomplete_head; + } else { + printf("\n"); + autocomplete(skip_spaces(hdt_cli.input)); + autocomplete_last_seen = autocomplete_head; + + printf("%s%s", hdt_cli.prompt, hdt_cli.input); + } + break; + + case KEY_ENTER: + printf("\n"); + if (strlen(remove_spaces(hdt_cli.input)) < 1) { + reset_prompt(); + break; + } + if (hdt_cli.history_pos == MAX_HISTORY_SIZE-1) hdt_cli.history_pos=1; + strncpy(hdt_cli.history[hdt_cli.history_pos],remove_spaces(hdt_cli.input),sizeof(hdt_cli.history[hdt_cli.history_pos])); + hdt_cli.history_pos++; + if (hdt_cli.history_pos>hdt_cli.max_history_pos) hdt_cli.max_history_pos=hdt_cli.history_pos; + exec_command(remove_spaces(hdt_cli.input), hardware); + reset_prompt(); + break; + + case KEY_CTRL('d'): + case KEY_DELETE: + /* No need to delete when input is empty */ + if (strlen(hdt_cli.input)==0) break; + /* Don't delete when cursor is at the end of the line */ + if (hdt_cli.cursor_pos>=strlen(hdt_cli.input)) break; + + for (int c = hdt_cli.cursor_pos; + c < (int)strlen(hdt_cli.input) - 1; c++) + hdt_cli.input[c] = hdt_cli.input[c + 1]; + hdt_cli.input[strlen(hdt_cli.input) - 1] = '\0'; + + /* Clear the end of the line */ + clear_end_of_line(); + + /* Print the resulting buffer */ + printf("%s", hdt_cli.input + hdt_cli.cursor_pos); + + /* Replace the cursor at the proper place */ + if (strlen(hdt_cli.input + hdt_cli.cursor_pos)>0) + move_cursor_left(strlen(hdt_cli.input + hdt_cli.cursor_pos)); + break; + + case KEY_DEL: + case KEY_BACKSPACE: + /* Don't delete prompt */ + if (hdt_cli.cursor_pos == 0) + break; + + for (int c = hdt_cli.cursor_pos - 1; + c < (int)strlen(hdt_cli.input) - 1; c++) + hdt_cli.input[c] = hdt_cli.input[c + 1]; + hdt_cli.input[strlen(hdt_cli.input) - 1] = '\0'; + + /* Get one char back */ + move_cursor_left(1); + + /* Clear the end of the line */ + clear_end_of_line(); + + /* Print the resulting buffer */ + printf("%s", hdt_cli.input + hdt_cli.cursor_pos - 1); + + /* Realing to the place we were */ + move_cursor_left(strlen(hdt_cli.input + hdt_cli.cursor_pos - 1)); + move_cursor_right(1); + + /* Don't decrement the position unless + * if we are at then end of the line*/ + if (hdt_cli.cursor_pos > (int)strlen(hdt_cli.input)) + hdt_cli.cursor_pos--; + break; + + case KEY_F1: + printf("\n"); + exec_command(CLI_HELP, hardware); + reset_prompt(); + break; + + default: + if ( ( current_key < 0x20 ) || ( current_key > 0x7e ) ) break; + /* Prevent overflow */ + if (hdt_cli.cursor_pos > MAX_LINE_SIZE - 2) + break; + /* If we aren't at the end of the input line, let's insert */ + if (hdt_cli.cursor_pos < (int)strlen(hdt_cli.input)) { + char key[2]; + int trailing_chars = + strlen(hdt_cli.input) - hdt_cli.cursor_pos; + memset(temp_command, 0, sizeof(temp_command)); + strncpy(temp_command, hdt_cli.input, + hdt_cli.cursor_pos); + sprintf(key, "%c", current_key); + strncat(temp_command, key, 1); + strncat(temp_command, + hdt_cli.input + hdt_cli.cursor_pos, + trailing_chars); + memset(hdt_cli.input, 0, sizeof(hdt_cli.input)); + snprintf(hdt_cli.input, sizeof(hdt_cli.input), "%s", + temp_command); + + /* Clear the end of the line */ + clear_end_of_line(); + + /* Print the resulting buffer */ + printf("%s", hdt_cli.input + hdt_cli.cursor_pos); + + /* Return where we must put the new char */ + move_cursor_left(trailing_chars); + + } else { + putchar(current_key); + hdt_cli.input[hdt_cli.cursor_pos] = current_key; + } + hdt_cli.cursor_pos++; + break; + } + } +} diff --git a/com32/hdt/hdt-cli.h b/com32/hdt/hdt-cli.h new file mode 100644 index 0000000..516d2fc --- /dev/null +++ b/com32/hdt/hdt-cli.h @@ -0,0 +1,187 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#ifndef DEFINE_HDT_CLI_H +#define DEFINE_HDT_CLI_H +#include +#include + +#include "hdt-common.h" + +#define DEBUG 0 +#if DEBUG +# define dprintf printf +#else +# define dprintf(f, ...) ((void)0) +#endif + +/* Declare a variable or data structure as unused. */ +#define __unused __attribute__ (( unused )) + +#define MAX_LINE_SIZE 256 + +#define CLI_SPACE " " +#define CLI_LF "\n" +#define CLI_MENU "menu" +#define CLI_CLEAR "clear" +#define CLI_EXIT "exit" +#define CLI_HELP "help" +#define CLI_SHOW "show" +#define CLI_SET "set" +#define CLI_MODE "mode" +#define CLI_HDT "hdt" +#define CLI_PCI "pci" +#define CLI_PXE "pxe" +#define CLI_KERNEL "kernel" +#define CLI_SYSLINUX "syslinux" +#define CLI_VESA "vesa" +#define CLI_SUMMARY "summary" +#define CLI_COMMANDS "commands" +#define CLI_DMI "dmi" +#define CLI_CPU "cpu" +#define CLI_SHOW_LIST "list" +#define CLI_IRQ "irq" +#define CLI_MODES "modes" +#define CLI_VPD "vpd" + +typedef enum { + INVALID_MODE, + EXIT_MODE, + HDT_MODE, + PCI_MODE, + DMI_MODE, + CPU_MODE, + PXE_MODE, + KERNEL_MODE, + SYSLINUX_MODE, + VESA_MODE, + VPD_MODE, +} cli_mode_t; + +#define PROMPT_SIZE 32 +#define MAX_HISTORY_SIZE 32 +struct s_cli { + cli_mode_t mode; + char prompt[PROMPT_SIZE]; + char input[MAX_LINE_SIZE]; + uint8_t cursor_pos; + char history[MAX_HISTORY_SIZE][MAX_LINE_SIZE]; + int history_pos; + int max_history_pos; +}; +struct s_cli hdt_cli; + +/* Describe a cli mode */ +struct cli_mode_descr { + const unsigned int mode; + const char* name; + /* Handle 1-token commands */ + struct cli_module_descr* default_modules; + /* Handle show */ + struct cli_module_descr* show_modules; + /* Handle set */ + struct cli_module_descr* set_modules; +}; + +/* Describe a subset of commands in a module (default, show, set, ...) */ +struct cli_module_descr { + struct cli_callback_descr* modules; + void ( * default_callback ) ( int argc, char** argv, struct s_hardware *hardware ); +}; + +/* Describe a callback (belongs to a mode and a module) */ +struct cli_callback_descr { + const char *name; + void ( * exec ) ( int argc, char** argv, struct s_hardware *hardware ); +}; + +/* Manage aliases */ +#define MAX_ALIASES 2 +struct cli_alias { + const char *command; /* Original command */ + const int nb_aliases; /* Size of aliases array */ + const char **aliases; /* List of aliases */ +}; + +/* List of implemented modes */ +extern struct cli_mode_descr *list_modes[]; +struct cli_mode_descr hdt_mode; +struct cli_mode_descr dmi_mode; +struct cli_mode_descr syslinux_mode; +struct cli_mode_descr pxe_mode; +struct cli_mode_descr kernel_mode; +struct cli_mode_descr cpu_mode; +struct cli_mode_descr pci_mode; +struct cli_mode_descr vesa_mode; +struct cli_mode_descr vpd_mode; + +/* cli helpers */ +void find_cli_mode_descr(cli_mode_t mode, struct cli_mode_descr **mode_found); +void find_cli_callback_descr(const char *module_name, + struct cli_module_descr *modules_list, + struct cli_callback_descr **module_found); +cli_mode_t mode_s_to_mode_t(char *name); + +void set_mode(cli_mode_t mode, struct s_hardware *hardware); +void start_cli_mode(struct s_hardware *hardware); +void main_show(char *item, struct s_hardware *hardware); + +// DMI STUFF +#define CLI_DMI_BASE_BOARD "base_board" +#define CLI_DMI_BATTERY "battery" +#define CLI_DMI_BIOS "bios" +#define CLI_DMI_CHASSIS "chassis" +#define CLI_DMI_MEMORY "memory" +#define CLI_DMI_MEMORY_BANK "bank" +#define CLI_DMI_PROCESSOR "cpu" +#define CLI_DMI_SYSTEM "system" +#define CLI_DMI_IPMI "ipmi" +#define CLI_DMI_LIST CLI_SHOW_LIST +void main_show_dmi(int argc, char **argv, struct s_hardware *hardware); +void show_dmi_memory_modules(int argc, char** argv, struct s_hardware *hardware); + +// PCI STUFF +#define CLI_PCI_DEVICE "device" +void main_show_pci(int argc, char **argv, struct s_hardware *hardware); +void cli_detect_pci(struct s_hardware *hardware); + +// CPU STUFF +void main_show_cpu(int argc, char **argv, struct s_hardware *hardware); + +// PXE STUFF +void main_show_pxe(int argc, char **argv, struct s_hardware *hardware); + +// KERNEL STUFF +void main_show_kernel(int argc, char **argv, struct s_hardware *hardware); + +// SYSLINUX STUFF +void main_show_syslinux(int argc, char **argv, struct s_hardware *hardware); + +// VESA STUFF +void main_show_vesa(int argc, char **argv, struct s_hardware *hardware); +#endif diff --git a/com32/hdt/hdt-common.c b/com32/hdt/hdt-common.c new file mode 100644 index 0000000..8cda7f0 --- /dev/null +++ b/com32/hdt/hdt-common.c @@ -0,0 +1,549 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- +*/ + +#include +#include +#include +#include +#include "syslinux/config.h" +#include "../lib/sys/vesa/vesa.h" +#include "hdt-common.h" +#include "lib-ansi.h" + +/* ISOlinux requires a 8.3 format */ +void convert_isolinux_filename(char *filename, struct s_hardware *hardware) { + /* Exit if we are not running ISOLINUX */ + if (hardware->sv->filesystem != SYSLINUX_FS_ISOLINUX) return; + /* Searching the dot */ + char *dot=strchr(filename,'.'); + /* Exiting if not dot exists in that string */ + if (dot==NULL) return; + /* Exiting if the extension is 3 char or less */ + if (strlen(dot)<=4) return; + + /* We have an extension bigger than .blah + * so we have to shorten it to 3*/ + dot[4]='\0'; +} + +void detect_parameters(const int argc, const char *argv[], + struct s_hardware *hardware) +{ + for (int i = 1; i < argc; i++) { + if (!strncmp(argv[i], "modules=", 8)) { + strncpy(hardware->modules_pcimap_path, argv[i] + 8, + sizeof(hardware->modules_pcimap_path)); + convert_isolinux_filename(hardware->modules_pcimap_path,hardware); + } else if (!strncmp(argv[i], "pciids=", 7)) { + strncpy(hardware->pciids_path, argv[i] + 7, + sizeof(hardware->pciids_path)); + convert_isolinux_filename(hardware->pciids_path,hardware); + } else if (!strncmp(argv[i], "memtest=", 8)) { + strncpy(hardware->memtest_label, argv[i] + 8, + sizeof(hardware->memtest_label)); + convert_isolinux_filename(hardware->memtest_label,hardware); + } + } +} + +void detect_syslinux(struct s_hardware *hardware) +{ + hardware->sv = syslinux_version(); + switch (hardware->sv->filesystem) { + case SYSLINUX_FS_SYSLINUX: + strlcpy(hardware->syslinux_fs, "SYSlinux", 9); + break; + case SYSLINUX_FS_PXELINUX: + strlcpy(hardware->syslinux_fs, "PXElinux", 9); + break; + case SYSLINUX_FS_ISOLINUX: + strlcpy(hardware->syslinux_fs, "ISOlinux", 9); + break; + case SYSLINUX_FS_EXTLINUX: + strlcpy(hardware->syslinux_fs, "EXTlinux", 9); + break; + case SYSLINUX_FS_UNKNOWN: + default: + strlcpy(hardware->syslinux_fs, "Unknown Bootloader", + sizeof hardware->syslinux_fs); + break; + } +} + +void init_hardware(struct s_hardware *hardware) +{ + hardware->pci_ids_return_code = 0; + hardware->modules_pcimap_return_code = 0; + hardware->cpu_detection = false; + hardware->pci_detection = false; + hardware->disk_detection = false; + hardware->disks_count=0; + hardware->dmi_detection = false; + hardware->pxe_detection = false; + hardware->vesa_detection = false; + hardware->vpd_detection = false; + hardware->nb_pci_devices = 0; + hardware->is_dmi_valid = false; + hardware->is_pxe_valid = false; + hardware->is_vpd_valid = false; + hardware->pci_domain = NULL; + + /* Cleaning structures */ + memset(hardware->disk_info, 0, sizeof(hardware->disk_info)); + memset(&hardware->dmi, 0, sizeof(s_dmi)); + memset(&hardware->cpu, 0, sizeof(s_cpu)); + memset(&hardware->pxe, 0, sizeof(struct s_pxe)); + memset(&hardware->vesa, 0, sizeof(struct s_vesa)); + memset(&hardware->vpd, 0, sizeof(s_vpd)); + memset(hardware->syslinux_fs, 0, sizeof hardware->syslinux_fs); + memset(hardware->pciids_path, 0, sizeof hardware->pciids_path); + memset(hardware->modules_pcimap_path, 0, + sizeof hardware->modules_pcimap_path); + memset(hardware->memtest_label, 0, sizeof hardware->memtest_label); + strcat(hardware->pciids_path, "pci.ids"); + strcat(hardware->modules_pcimap_path, "modules.pcimap"); + strcat(hardware->memtest_label, "memtest"); +} + +/* + * Detecting if a DMI table exist + * if yes, let's parse it + */ +int detect_dmi(struct s_hardware *hardware) +{ + if (hardware->dmi_detection == true) + return -1; + hardware->dmi_detection = true; + if (dmi_iterate(&hardware->dmi) == -ENODMITABLE) { + hardware->is_dmi_valid = false; + return -ENODMITABLE; + } + + parse_dmitable(&hardware->dmi); + hardware->is_dmi_valid = true; + return 0; +} + +/** + * vpd_detection - populate the VPD structure + * + * VPD is a structure available on IBM machines. + * It is documented at: + * http://www.pc.ibm.com/qtechinfo/MIGR-45120.html + * (XXX the page seems to be gone) + **/ +int detect_vpd(struct s_hardware *hardware) +{ + if (hardware->vpd_detection) + return -1; + else + hardware->vpd_detection = true; + + if (vpd_decode(&hardware->vpd) == -ENOVPDTABLE) { + hardware->is_vpd_valid = false; + return -ENOVPDTABLE; + } else { + hardware->is_vpd_valid = true; + return 0; + } +} + +/* Detection vesa stuff*/ +int detect_vesa(struct s_hardware *hardware) { + static com32sys_t rm; + struct vesa_general_info *gi; + struct vesa_mode_info *mi; + uint16_t mode, *mode_ptr; + char *oem_ptr; + + if (hardware->vesa_detection == true) return -1; + + hardware->vesa_detection=true; + hardware->is_vesa_valid=false; + + /* Allocate space in the bounce buffer for these structures */ + gi = &((struct vesa_info *)__com32.cs_bounce)->gi; + mi = &((struct vesa_info *)__com32.cs_bounce)->mi; + + gi->signature = VBE2_MAGIC; /* Get VBE2 extended data */ + rm.eax.w[0] = 0x4F00; /* Get SVGA general information */ + rm.edi.w[0] = OFFS(gi); + rm.es = SEG(gi); + __intcall(0x10, &rm, &rm); + + if ( rm.eax.w[0] != 0x004F ) { + return -1; + }; + + mode_ptr = GET_PTR(gi->video_mode_ptr); + oem_ptr = GET_PTR(gi->oem_vendor_name_ptr); + strncpy(hardware->vesa.vendor,oem_ptr,sizeof(hardware->vesa.vendor)); + oem_ptr = GET_PTR(gi->oem_product_name_ptr); + strncpy(hardware->vesa.product,oem_ptr,sizeof(hardware->vesa.product)); + oem_ptr = GET_PTR(gi->oem_product_rev_ptr); + strncpy(hardware->vesa.product_revision,oem_ptr,sizeof(hardware->vesa.product_revision)); + + hardware->vesa.major_version=(gi->version >> 8) & 0xff; + hardware->vesa.minor_version=gi->version & 0xff; + hardware->vesa.total_memory=gi->total_memory; + hardware->vesa.software_rev=gi->oem_software_rev; + + hardware->vesa.vmi_count=0; + + while ((mode = *mode_ptr++) != 0xFFFF) { + + rm.eax.w[0] = 0x4F01; /* Get SVGA mode information */ + rm.ecx.w[0] = mode; + rm.edi.w[0] = OFFS(mi); + rm.es = SEG(mi); + __intcall(0x10, &rm, &rm); + + /* Must be a supported mode */ + if ( rm.eax.w[0] != 0x004f ) + continue; + + /* Saving detected values*/ + memcpy(&hardware->vesa.vmi[hardware->vesa.vmi_count].mi, mi, + sizeof(struct vesa_mode_info)); + hardware->vesa.vmi[hardware->vesa.vmi_count].mode = mode; + + hardware->vesa.vmi_count++; + } + hardware->is_vesa_valid = true; + return 0; +} + +/* Try to detect disks from port 0x80 to 0xff */ +void detect_disks(struct s_hardware *hardware) +{ + hardware->disk_detection = true; + for (int drive = 0x80; drive < 0xff; drive++) { + if (get_disk_params(drive, hardware->disk_info) != 0) + continue; + struct diskinfo *d = &hardware->disk_info[drive]; + hardware->disks_count++; + printf + (" DISK 0x%X: %s : %s %s: sectors=%d, s/t=%d head=%d : EDD=%s\n", + drive, d->aid.model, d->host_bus_type, d->interface_type, + d->sectors, d->sectors_per_track, d->heads, + d->edd_version); + } +} + +int detect_pxe(struct s_hardware *hardware) +{ + void *dhcpdata; + + size_t dhcplen; + t_PXENV_UNDI_GET_NIC_TYPE gnt; + + if (hardware->pxe_detection == true) + return -1; + hardware->pxe_detection = true; + hardware->is_pxe_valid = false; + memset(&gnt, 0, sizeof(t_PXENV_UNDI_GET_NIC_TYPE)); + memset(&hardware->pxe, 0, sizeof(struct s_pxe)); + + /* This code can only work if pxelinux is loaded */ + if (hardware->sv->filesystem != SYSLINUX_FS_PXELINUX) { + return -1; + } +// printf("PXE: PXElinux detected\n"); + if (!pxe_get_cached_info + (PXENV_PACKET_TYPE_DHCP_ACK, &dhcpdata, &dhcplen)) { + pxe_bootp_t *dhcp = &hardware->pxe.dhcpdata; + memcpy(&hardware->pxe.dhcpdata, dhcpdata, + sizeof(hardware->pxe.dhcpdata)); + snprintf(hardware->pxe.mac_addr, sizeof(hardware->pxe.mac_addr), + "%02x:%02x:%02x:%02x:%02x:%02x", dhcp->CAddr[0], + dhcp->CAddr[1], dhcp->CAddr[2], dhcp->CAddr[3], + dhcp->CAddr[4], dhcp->CAddr[5]); + + /* Saving our IP address in a easy format */ + hardware->pxe.ip_addr[0] = hardware->pxe.dhcpdata.yip & 0xff; + hardware->pxe.ip_addr[1] = + hardware->pxe.dhcpdata.yip >> 8 & 0xff; + hardware->pxe.ip_addr[2] = + hardware->pxe.dhcpdata.yip >> 16 & 0xff; + hardware->pxe.ip_addr[3] = + hardware->pxe.dhcpdata.yip >> 24 & 0xff; + + if (!pxe_get_nic_type(&gnt)) { + switch (gnt.NicType) { + case PCI_NIC: + hardware->is_pxe_valid = true; + hardware->pxe.vendor_id = + gnt.info.pci.Vendor_ID; + hardware->pxe.product_id = gnt.info.pci.Dev_ID; + hardware->pxe.subvendor_id = + gnt.info.pci.SubVendor_ID; + hardware->pxe.subproduct_id = + gnt.info.pci.SubDevice_ID, + hardware->pxe.rev = gnt.info.pci.Rev; + hardware->pxe.pci_bus = + (gnt.info.pci.BusDevFunc >> 8) & 0xff; + hardware->pxe.pci_dev = + (gnt.info.pci.BusDevFunc >> 3) & 0x7; + hardware->pxe.pci_func = + gnt.info.pci.BusDevFunc & 0x03; + hardware->pxe.base_class = + gnt.info.pci.Base_Class; + hardware->pxe.sub_class = + gnt.info.pci.Sub_Class; + hardware->pxe.prog_intf = + gnt.info.pci.Prog_Intf; + hardware->pxe.nictype = gnt.NicType; + break; + case CardBus_NIC: + hardware->is_pxe_valid = true; + hardware->pxe.vendor_id = + gnt.info.cardbus.Vendor_ID; + hardware->pxe.product_id = + gnt.info.cardbus.Dev_ID; + hardware->pxe.subvendor_id = + gnt.info.cardbus.SubVendor_ID; + hardware->pxe.subproduct_id = + gnt.info.cardbus.SubDevice_ID, + hardware->pxe.rev = gnt.info.cardbus.Rev; + hardware->pxe.pci_bus = + (gnt.info.cardbus.BusDevFunc >> 8) & 0xff; + hardware->pxe.pci_dev = + (gnt.info.cardbus.BusDevFunc >> 3) & 0x7; + hardware->pxe.pci_func = + gnt.info.cardbus.BusDevFunc & 0x03; + hardware->pxe.base_class = + gnt.info.cardbus.Base_Class; + hardware->pxe.sub_class = + gnt.info.cardbus.Sub_Class; + hardware->pxe.prog_intf = + gnt.info.cardbus.Prog_Intf; + hardware->pxe.nictype = gnt.NicType; + break; + case PnP_NIC: + default: + return -1; + break; + } + /* Let's try to find the associated pci device */ + detect_pci(hardware); + + /* The firt pass try to find the exact pci device */ + hardware->pxe.pci_device = NULL; + hardware->pxe.pci_device_pos = 0; + struct pci_device *pci_device; + int pci_number = 0; + for_each_pci_func(pci_device, hardware->pci_domain) { + pci_number++; + if ((__pci_bus == hardware->pxe.pci_bus) && + (__pci_slot == hardware->pxe.pci_dev) && + (__pci_func == hardware->pxe.pci_func) && + (pci_device->vendor == + hardware->pxe.vendor_id) + && (pci_device->product == + hardware->pxe.product_id)) { + hardware->pxe.pci_device = pci_device; + hardware->pxe.pci_device_pos = + pci_number; + return 0; + } + } + + /* If we reach that part, it means the pci device pointed by + * the pxe rom wasn't found in our list. + * Let's try to find the device only by its pci ids. + * The pci device we'll match is maybe not exactly the good one + * as we can have the same pci id several times. + * At least, the pci id, the vendor/product will be right. + * That's clearly a workaround for some weird cases. + * This should happend very unlikely */ + hardware->pxe.pci_device = NULL; + hardware->pxe.pci_device_pos = 0; + pci_number = 0; + for_each_pci_func(pci_device, hardware->pci_domain) { + pci_number++; + if ((pci_device->vendor == + hardware->pxe.vendor_id) + && (pci_device->product == + hardware->pxe.product_id)) { + hardware->pxe.pci_device = pci_device; + hardware->pxe.pci_device_pos = + pci_number; + return 0; + } + } + + } + } + return 0; +} + +void detect_pci(struct s_hardware *hardware) +{ + if (hardware->pci_detection == true) + return; + hardware->pci_detection = true; + + hardware->nb_pci_devices = 0; + + /* Scanning to detect pci buses and devices */ + hardware->pci_domain = pci_scan(); + + if (!hardware->pci_domain) + return; + + /* Gathering addtional information*/ + gather_additional_pci_config(hardware->pci_domain); + + struct pci_device *pci_device; + for_each_pci_func(pci_device, hardware->pci_domain) { + hardware->nb_pci_devices++; + } + + printf("PCI: %d devices detected\n", hardware->nb_pci_devices); + printf("PCI: Resolving names\n"); + /* Assigning product & vendor name for each device */ + hardware->pci_ids_return_code = + get_name_from_pci_ids(hardware->pci_domain, hardware->pciids_path); + + printf("PCI: Resolving class names\n"); + /* Assigning class name for each device */ + hardware->pci_ids_return_code = + get_class_name_from_pci_ids(hardware->pci_domain, + hardware->pciids_path); + + printf("PCI: Resolving module names\n"); + /* Detecting which kernel module should match each device */ + hardware->modules_pcimap_return_code = + get_module_name_from_pcimap(hardware->pci_domain, + hardware->modules_pcimap_path); + + /* We try to detect the pxe stuff to populate the PXE: field of pci devices */ + detect_pxe(hardware); +} + +void cpu_detect(struct s_hardware *hardware) +{ + if (hardware->cpu_detection == true) + return; + detect_cpu(&hardware->cpu); + hardware->cpu_detection = true; +} + +/* + * Find the last instance of a particular command line argument + * (which should include the final =; do not use for boolean arguments) + */ +const char *find_argument(const char **argv, const char *argument) +{ + int la = strlen(argument); + const char **arg; + const char *ptr = NULL; + + for (arg = argv; *arg; arg++) { + if (!memcmp(*arg, argument, la)) + ptr = *arg + la; + } + + return ptr; +} + +void clear_screen(void) +{ + move_cursor_to_next_line(); + disable_utf8(); + set_g1_special_char(); + set_us_g0_charset(); + display_cursor(false); + clear_entire_screen(); + reset_more_printf(); +} + +/* remove begining spaces */ +char *skip_spaces(char *p) +{ + while (*p && *p <= ' ') { + p++; + } + + return p; +} + +/* remove trailing & begining spaces */ +char *remove_spaces(char *p) +{ + char *save=p; + p+=strlen(p)-1; + while (*p && *p <= ' ') { + *p='\0'; + p--; + } + p=save; + while (*p && *p <= ' ') { + p++; + } + + return p; +} + +/* delete multiple spaces, one is enough */ +char *del_multi_spaces(char *p) { + /* Saving the original pointer*/ + char *save=p; + + /* Let's parse the complete string + * As we search for a double spacing + * we have to be sure then string is + * long enough to be processed */ + while (*p && *p+1) { + + /* If we have two consecutive spaces*/ + if ((*p == ' ') && (*(p+1) == ' ')) { + + /* Let's copy to the current position + * the content from the second space*/ + strncpy(p,p+1,strlen(p+1)); + + /* The string is 1 char smaller*/ + *(p+strlen(p)-1)='\0'; + + /* Don't increment the pointer as we + * changed the content of the current position*/ + continue; + } + + /* Nothing as been found, let's see on the next char*/ + p++; + } + /* Returning the original pointer*/ + return save; +} + +/* Reset the more_printf counter */ +void reset_more_printf() { + display_line_nb=0; +} diff --git a/com32/hdt/hdt-common.h b/com32/hdt/hdt-common.h new file mode 100644 index 0000000..7e3dc7d --- /dev/null +++ b/com32/hdt/hdt-common.h @@ -0,0 +1,145 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#ifndef DEFINE_HDT_COMMON_H +#define DEFINE_HDT_COMMON_H +#include +#include +#include "sys/pci.h" + +#include "cpuid.h" +#include "dmi/dmi.h" +#include "hdt-ata.h" +#include "../lib/sys/vesa/vesa.h" +#include + +/* This two values are used for switching for the menu to the CLI mode */ +#define HDT_SWITCH_TO_CLI "hdt_switch_to_cli" +#define HDT_RETURN_TO_CLI 100 +#define MAX_VESA_MODES 255 + +extern int display_line_nb; + +#define more_printf(...) do {\ + if (display_line_nb == 23) {\ + printf("Press any key to continue\n");\ + display_line_nb=0;\ + get_key(stdin, 0);\ + }\ + printf ( __VA_ARGS__);\ + display_line_nb++; \ +} while (0); + +struct s_pxe { + uint16_t vendor_id; + uint16_t product_id; + uint16_t subvendor_id; + uint16_t subproduct_id; + uint8_t rev; + uint8_t pci_bus; + uint8_t pci_dev; + uint8_t pci_func; + uint8_t base_class; + uint8_t sub_class; + uint8_t prog_intf; + uint8_t nictype; + char mac_addr[18]; /* The current mac address */ + uint8_t ip_addr[4]; + pxe_bootp_t dhcpdata; /* The dhcp answer */ + struct pci_device *pci_device; /* The matching pci device */ + uint8_t pci_device_pos; /* It position in our pci sorted list */ +}; + +struct s_vesa_mode_info { + struct vesa_mode_info mi; + uint16_t mode; +}; + +struct s_vesa { + uint8_t major_version; + uint8_t minor_version; + struct s_vesa_mode_info vmi[MAX_VESA_MODES]; + uint8_t vmi_count; + uint16_t total_memory; + char vendor[256]; + char product[256]; + char product_revision[256]; + uint16_t software_rev; +}; + +struct s_hardware { + s_dmi dmi; /* DMI table */ + s_cpu cpu; /* CPU information */ + s_vpd vpd; /* VPD information */ + struct pci_domain *pci_domain; /* PCI Devices */ + struct diskinfo disk_info[256]; /* Disk Information */ + int disks_count; /* Number of detected disks */ + struct s_pxe pxe; + struct s_vesa vesa; + + int pci_ids_return_code; + int modules_pcimap_return_code; + int nb_pci_devices; + bool is_dmi_valid; + bool is_pxe_valid; + bool is_vesa_valid; + bool is_vpd_valid; + + bool dmi_detection; /* Does the dmi stuff has already been detected? */ + bool pci_detection; /* Does the pci stuff has already been detected? */ + bool cpu_detection; /* Does the cpu stuff has already been detected? */ + bool disk_detection;/* Does the disk stuff has already been detected? */ + bool pxe_detection; /* Does the pxe stuff has already been detected? */ + bool vesa_detection;/* Does the vesa sutff have been already detected? */ + bool vpd_detection; /* Does the vpd stuff has already been detected? */ + + char syslinux_fs[22]; + const struct syslinux_version *sv; + char modules_pcimap_path[255]; + char pciids_path[255]; + char memtest_label[255]; +}; + +void reset_more_printf(); +const char *find_argument(const char **argv, const char *argument); +char *remove_spaces(char *p); +char *skip_spaces(char *p); +char *del_multi_spaces(char *p); +int detect_dmi(struct s_hardware *hardware); +int detect_vpd(struct s_hardware *hardware); +void detect_disks(struct s_hardware *hardware); +void detect_pci(struct s_hardware *hardware); +void cpu_detect(struct s_hardware *hardware); +int detect_pxe(struct s_hardware *hardware); +void init_hardware(struct s_hardware *hardware); +void clear_screen(void); +void detect_syslinux(struct s_hardware *hardware); +void detect_parameters(const int argc, const char *argv[], + struct s_hardware *hardware); +int detect_vesa(struct s_hardware *hardware); +#endif diff --git a/com32/hdt/hdt-menu-about.c b/com32/hdt/hdt-menu-about.c new file mode 100644 index 0000000..f19dc36 --- /dev/null +++ b/com32/hdt/hdt-menu-about.c @@ -0,0 +1,71 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include "hdt-menu.h" + +/* Computing About menu*/ +void compute_aboutmenu(struct s_my_menu *menu) +{ + char buffer[SUBMENULEN + 1]; + char statbuffer[STATLEN + 1]; + + menu->menu = add_menu(" About ", -1); + menu->items_count = 0; + + set_menu_pos(SUBMENU_Y, SUBMENU_X); + + snprintf(buffer, sizeof buffer, "Product : %s", PRODUCT_NAME); + snprintf(statbuffer, sizeof statbuffer, "Product : %s", PRODUCT_NAME); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Version : %s", VERSION); + snprintf(statbuffer, sizeof statbuffer, "Version : %s", VERSION); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Author : %s", AUTHOR); + snprintf(statbuffer, sizeof statbuffer, "Author : %s", AUTHOR); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Contact : %s", CONTACT); + snprintf(statbuffer, sizeof statbuffer, "Contact : %s", CONTACT); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + char *contributors[NB_CONTRIBUTORS] = CONTRIBUTORS; + for (int c=0; citems_count++; + } + + printf("MENU: About menu done (%d items)\n", menu->items_count); +} diff --git a/com32/hdt/hdt-menu-disk.c b/com32/hdt/hdt-menu-disk.c new file mode 100644 index 0000000..02f12f7 --- /dev/null +++ b/com32/hdt/hdt-menu-disk.c @@ -0,0 +1,181 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include "hdt-menu.h" + +/* Compute the disk submenu */ +int compute_disk_module(struct s_my_menu *menu, int nb_sub_disk_menu, + struct diskinfo *d, int disk_number) +{ + char buffer[MENULEN + 1]; + char statbuffer[STATLEN + 1]; + + /* No need to add no existing devices */ + if (strlen(d[disk_number].aid.model) <= 0) + return -1; + + snprintf(buffer, sizeof buffer, " Disk <%d> ", nb_sub_disk_menu); + menu[nb_sub_disk_menu].menu = add_menu(buffer, -1); + menu[nb_sub_disk_menu].items_count = 0; + + snprintf(buffer, sizeof buffer, "Model : %s", + d[disk_number].aid.model); + snprintf(statbuffer, sizeof statbuffer, "Model: %s", + d[disk_number].aid.model); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu[nb_sub_disk_menu].items_count++; + + /* Compute device size */ + char previous_unit[3], unit[3]; //GB + int previous_size, size = d[disk_number].sectors / 2; // Converting to bytes + strlcpy(unit, "KB", 2); + strlcpy(previous_unit, unit, 2); + previous_size = size; + if (size > 1000) { + size = size / 1000; + strlcpy(unit, "MB", 2); + if (size > 1000) { + previous_size = size; + size = size / 1000; + strlcpy(previous_unit, unit, 2); + strlcpy(unit, "GB", 2); + if (size > 1000) { + previous_size = size; + size = size / 1000; + strlcpy(previous_unit, unit, 2); + strlcpy(unit, "TB", 2); + } + } + } + + snprintf(buffer, sizeof buffer, "Size : %d %s (%d %s)", size, + unit, previous_size, previous_unit); + snprintf(statbuffer, sizeof statbuffer, "Size: %d %s (%d %s)", size, + unit, previous_size, previous_unit); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu[nb_sub_disk_menu].items_count++; + + snprintf(buffer, sizeof buffer, "Firmware Rev.: %s", + d[disk_number].aid.fw_rev); + snprintf(statbuffer, sizeof statbuffer, "Firmware Revision: %s", + d[disk_number].aid.fw_rev); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu[nb_sub_disk_menu].items_count++; + + snprintf(buffer, sizeof buffer, "Serial Number: %s", + d[disk_number].aid.serial_no); + snprintf(statbuffer, sizeof statbuffer, "Serial Number: %s", + d[disk_number].aid.serial_no); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu[nb_sub_disk_menu].items_count++; + + snprintf(buffer, sizeof buffer, "Interface : %s", + d[disk_number].interface_type); + snprintf(statbuffer, sizeof statbuffer, "Interface: %s", + d[disk_number].interface_type); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu[nb_sub_disk_menu].items_count++; + + snprintf(buffer, sizeof buffer, "Host Bus : %s", + d[disk_number].host_bus_type); + snprintf(statbuffer, sizeof statbuffer, "Host Bus Type: %s", + d[disk_number].host_bus_type); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu[nb_sub_disk_menu].items_count++; + + snprintf(buffer, sizeof buffer, "Sectors : %d", + d[disk_number].sectors); + snprintf(statbuffer, sizeof statbuffer, "Sectors: %d", + d[disk_number].sectors); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu[nb_sub_disk_menu].items_count++; + + snprintf(buffer, sizeof buffer, "Heads : %d", + d[disk_number].heads); + snprintf(statbuffer, sizeof statbuffer, "Heads: %d", + d[disk_number].heads); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu[nb_sub_disk_menu].items_count++; + + snprintf(buffer, sizeof buffer, "Cylinders : %d", + d[disk_number].cylinders); + snprintf(statbuffer, sizeof statbuffer, "Cylinders: %d", + d[disk_number].cylinders); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu[nb_sub_disk_menu].items_count++; + + snprintf(buffer, sizeof buffer, "Sectors/Track: %d", + d[disk_number].sectors_per_track); + snprintf(statbuffer, sizeof statbuffer, "Sectors per Track: %d", + d[disk_number].sectors_per_track); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu[nb_sub_disk_menu].items_count++; + + snprintf(buffer, sizeof buffer, "Port : 0x%X", disk_number); + snprintf(statbuffer, sizeof statbuffer, "Port: 0x%X", disk_number); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu[nb_sub_disk_menu].items_count++; + + snprintf(buffer, sizeof buffer, "EDD Version : %s", + d[disk_number].edd_version); + snprintf(statbuffer, sizeof statbuffer, "EDD Version: %s", + d[disk_number].edd_version); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu[nb_sub_disk_menu].items_count++; + + return 0; +} + +/* Compute the Disks menu */ +void compute_disks(struct s_hdt_menu *menu, struct diskinfo *disk_info, struct s_hardware *hardware) +{ + char buffer[MENULEN + 1]; + int nb_sub_disk_menu = 0; + + /* No need to compute that menu if no disks were detected */ + menu->disk_menu.items_count = 0; + if (hardware->disks_count == 0) return; + + for (int i = 0; i < 0xff; i++) { + if (compute_disk_module + ((struct s_my_menu*) &(menu->disk_sub_menu), nb_sub_disk_menu, disk_info, + i) == 0) + nb_sub_disk_menu++; + } + + menu->disk_menu.menu = add_menu(" Disks ", -1); + + for (int i = 0; i < nb_sub_disk_menu; i++) { + snprintf(buffer, sizeof buffer, " Disk <%d> ", i); + add_item(buffer, "Disk", OPT_SUBMENU, NULL, + menu->disk_sub_menu[i].menu); + menu->disk_menu.items_count++; + } + printf("MENU: Disks menu done (%d items)\n", + menu->disk_menu.items_count); +} diff --git a/com32/hdt/hdt-menu-dmi.c b/com32/hdt/hdt-menu-dmi.c new file mode 100644 index 0000000..a3a3a8e --- /dev/null +++ b/com32/hdt/hdt-menu-dmi.c @@ -0,0 +1,540 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include "hdt-menu.h" + +/* Compute System main menu */ +void compute_system(struct s_my_menu *menu, s_dmi * dmi) +{ + char buffer[SUBMENULEN + 1]; + char statbuffer[STATLEN + 1]; + + menu->menu = add_menu(" System ", -1); + menu->items_count = 0; + set_menu_pos(SUBMENU_Y, SUBMENU_X); + + snprintf(buffer, sizeof buffer, "Vendor : %s", + dmi->system.manufacturer); + snprintf(statbuffer, sizeof statbuffer, "Vendor: %s", + dmi->system.manufacturer); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Product : %s", + dmi->system.product_name); + snprintf(statbuffer, sizeof statbuffer, "Product Name: %s", + dmi->system.product_name); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Version : %s", dmi->system.version); + snprintf(statbuffer, sizeof statbuffer, "Version: %s", + dmi->system.version); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Serial : %s", dmi->system.serial); + snprintf(statbuffer, sizeof statbuffer, "Serial Number: %s", + dmi->system.serial); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "UUID : %s", dmi->system.uuid); + snprintf(statbuffer, sizeof statbuffer, "UUID: %s", dmi->system.uuid); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Wakeup : %s", + dmi->system.wakeup_type); + snprintf(statbuffer, sizeof statbuffer, "Wakeup Type: %s", + dmi->system.wakeup_type); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "SKU Number: %s", + dmi->system.sku_number); + snprintf(statbuffer, sizeof statbuffer, "SKU Number: %s", + dmi->system.sku_number); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Family : %s", dmi->system.family); + snprintf(statbuffer, sizeof statbuffer, "Family: %s", + dmi->system.family); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + printf("MENU: System menu done (%d items)\n", menu->items_count); +} + +/* Compute Chassis menu */ +void compute_chassis(struct s_my_menu *menu, s_dmi * dmi) +{ + char buffer[SUBMENULEN + 1]; + char statbuffer[STATLEN + 1]; + menu->menu = add_menu(" Chassis ", -1); + menu->items_count = 0; + set_menu_pos(SUBMENU_Y, SUBMENU_X); + + snprintf(buffer, sizeof buffer, "Vendor : %s", + dmi->chassis.manufacturer); + snprintf(statbuffer, sizeof statbuffer, "Vendor: %s", + dmi->chassis.manufacturer); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Type : %s", dmi->chassis.type); + snprintf(statbuffer, sizeof statbuffer, "Type: %s", dmi->chassis.type); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Version : %s", dmi->chassis.version); + snprintf(statbuffer, sizeof statbuffer, "Version: %s", + dmi->chassis.version); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Serial : %s", dmi->chassis.serial); + snprintf(statbuffer, sizeof statbuffer, "Serial Number: %s", + dmi->chassis.serial); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Asset Tag : %s", + del_multi_spaces(dmi->chassis.asset_tag)); + snprintf(statbuffer, sizeof statbuffer, "Asset Tag: %s", + del_multi_spaces(dmi->chassis.asset_tag)); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Lock : %s", dmi->chassis.lock); + snprintf(statbuffer, sizeof statbuffer, "Lock: %s", dmi->chassis.lock); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + printf("MENU: Chassis menu done (%d items)\n", menu->items_count); +} + +/* Compute BIOS menu */ +void compute_bios(struct s_my_menu *menu, s_dmi * dmi) +{ + char buffer[SUBMENULEN + 1]; + char statbuffer[STATLEN + 1]; + + menu->menu = add_menu(" BIOS ", -1); + menu->items_count = 0; + set_menu_pos(SUBMENU_Y, SUBMENU_X); + + snprintf(buffer, sizeof buffer, "Vendor : %s", dmi->bios.vendor); + snprintf(statbuffer, sizeof statbuffer, "Vendor: %s", dmi->bios.vendor); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Version : %s", dmi->bios.version); + snprintf(statbuffer, sizeof statbuffer, "Version: %s", + dmi->bios.version); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Release : %s", + dmi->bios.release_date); + snprintf(statbuffer, sizeof statbuffer, "Release Date: %s", + dmi->bios.release_date); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Bios Rev. : %s", + dmi->bios.bios_revision); + snprintf(statbuffer, sizeof statbuffer, "Bios Revision: %s", + dmi->bios.bios_revision); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Fw. Rev. : %s", + dmi->bios.firmware_revision); + snprintf(statbuffer, sizeof statbuffer, "Firmware Revision : %s", + dmi->bios.firmware_revision); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + + printf("MENU: BIOS menu done (%d items)\n", menu->items_count); +} + +/* Compute Motherboard main menu */ +void compute_motherboard(struct s_my_menu *menu, s_dmi * dmi) +{ + char buffer[SUBMENULEN + 1]; + char statbuffer[STATLEN + 1]; + + menu->menu = add_menu(" Motherboard ", -1); + menu->items_count = 0; + set_menu_pos(SUBMENU_Y, SUBMENU_X); + + snprintf(buffer, sizeof buffer, "Vendor : %s", + dmi->base_board.manufacturer); + snprintf(statbuffer, sizeof statbuffer, "Vendor: %s", + dmi->base_board.manufacturer); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Product : %s", + dmi->base_board.product_name); + snprintf(statbuffer, sizeof statbuffer, "Product Name: %s", + dmi->base_board.product_name); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Version : %s", + dmi->base_board.version); + snprintf(statbuffer, sizeof statbuffer, "Version: %s", + dmi->base_board.version); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Serial : %s", + dmi->base_board.serial); + snprintf(statbuffer, sizeof statbuffer, "Serial Number: %s", + dmi->base_board.serial); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Asset Tag : %s", + dmi->base_board.asset_tag); + snprintf(statbuffer, sizeof statbuffer, "Asset Tag: %s", + dmi->base_board.asset_tag); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Location : %s", + dmi->base_board.location); + snprintf(statbuffer, sizeof statbuffer, "Location: %s", + dmi->base_board.location); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Type : %s", dmi->base_board.type); + snprintf(statbuffer, sizeof statbuffer, "Type: %s", + dmi->base_board.type); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + printf("MENU: Motherboard menu done (%d items)\n", menu->items_count); +} + +/* Compute the Memory submenu */ +static void compute_memory_module(struct s_my_menu *menu, s_dmi * dmi, + int slot_number) +{ + int i = slot_number; + char buffer[MENULEN + 1]; + char statbuffer[STATLEN + 1]; + + sprintf(buffer, " Bank <%d> ", i); + menu->items_count = 0; + menu->menu = add_menu(buffer, -1); + + snprintf(buffer, sizeof buffer, "Form Factor : %s", + dmi->memory[i].form_factor); + snprintf(statbuffer, sizeof statbuffer, "Form Factor: %s", + dmi->memory[i].form_factor); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Type : %s", + dmi->memory[i].type); + snprintf(statbuffer, sizeof statbuffer, "Type: %s", + dmi->memory[i].type); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Type Details : %s", + dmi->memory[i].type_detail); + snprintf(statbuffer, sizeof statbuffer, "Type Details: %s", + dmi->memory[i].type_detail); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Speed : %s", + dmi->memory[i].speed); + snprintf(statbuffer, sizeof statbuffer, "Speed (Mhz): %s", + dmi->memory[i].speed); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Size : %s", + dmi->memory[i].size); + snprintf(statbuffer, sizeof statbuffer, "Size: %s", + dmi->memory[i].size); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Device Set : %s", + dmi->memory[i].device_set); + snprintf(statbuffer, sizeof statbuffer, "Device Set: %s", + dmi->memory[i].device_set); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Device Loc. : %s", + dmi->memory[i].device_locator); + snprintf(statbuffer, sizeof statbuffer, "Device Location: %s", + dmi->memory[i].device_locator); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Bank Locator : %s", + dmi->memory[i].bank_locator); + snprintf(statbuffer, sizeof statbuffer, "Bank Locator: %s", + dmi->memory[i].bank_locator); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Total Width : %s", + dmi->memory[i].total_width); + snprintf(statbuffer, sizeof statbuffer, "Total bit Width: %s", + dmi->memory[i].total_width); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Data Width : %s", + dmi->memory[i].data_width); + snprintf(statbuffer, sizeof statbuffer, "Data bit Width: %s", + dmi->memory[i].data_width); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Error : %s", + dmi->memory[i].error); + snprintf(statbuffer, sizeof statbuffer, "Error: %s", + dmi->memory[i].error); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Vendor : %s", + dmi->memory[i].manufacturer); + snprintf(statbuffer, sizeof statbuffer, "Vendor: %s", + dmi->memory[i].manufacturer); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Serial : %s", + dmi->memory[i].serial); + snprintf(statbuffer, sizeof statbuffer, "Serial: %s", + dmi->memory[i].serial); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Asset Tag : %s", + dmi->memory[i].asset_tag); + snprintf(statbuffer, sizeof statbuffer, "Asset Tag: %s", + dmi->memory[i].asset_tag); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Part Number : %s", + dmi->memory[i].part_number); + snprintf(buffer, sizeof statbuffer, "Part Number: %s", + dmi->memory[i].part_number); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + +} + +/* Compute the Memory menu */ +void compute_memory(struct s_hdt_menu *menu, s_dmi * dmi, struct s_hardware *hardware) +{ + char buffer[MENULEN + 1]; + for (int i = 0; i < dmi->memory_count; i++) { + compute_memory_module(&(menu->memory_sub_menu[i]), dmi, i); + } + + menu->memory_menu.menu = add_menu(" Memory Banks ", -1); + menu->memory_menu.items_count = 0; + + for (int i = 0; i < dmi->memory_count; i++) { + snprintf(buffer, sizeof buffer, " Bank <%d> ", i); + add_item(buffer, "Memory Bank", OPT_SUBMENU, NULL, + menu->memory_sub_menu[i].menu); + menu->memory_menu.items_count++; + } + printf("MENU: Memory menu done (%d items)\n", + menu->memory_menu.items_count); + add_item("Run Test", "Run Test", OPT_RUN, hardware->memtest_label, 0); +} + +/* Compute Main IPMI menu */ +void compute_ipmi(struct s_my_menu *menu, s_dmi * dmi) +{ + char buffer[SUBMENULEN + 1]; + char statbuffer[STATLEN + 1]; + menu->menu = add_menu(" IPMI ", -1); + menu->items_count = 0; + set_menu_pos(SUBMENU_Y, SUBMENU_X); + + snprintf(buffer, sizeof buffer, "Interface Type : %s", + dmi->ipmi.interface_type); + snprintf(statbuffer, sizeof statbuffer, "Interface Type: %s", + dmi->ipmi.interface_type); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Spec. Version : %u.%u", + dmi->ipmi.major_specification_version, + dmi->ipmi.minor_specification_version); + snprintf(statbuffer, sizeof statbuffer, "Specification Version: %u.%u", + dmi->ipmi.major_specification_version, + dmi->ipmi.minor_specification_version); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "I2C Slave @ : 0x%02x", + dmi->ipmi.I2C_slave_address); + snprintf(statbuffer, sizeof statbuffer, "I2C Slave Address: 0x%02x", + dmi->ipmi.I2C_slave_address); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "NV Storage @ : %u", + dmi->ipmi.nv_address); + snprintf(statbuffer, sizeof statbuffer, "NV Storage Address: %u", + dmi->ipmi.nv_address); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + uint32_t high = dmi->ipmi.base_address >> 32; + uint32_t low = dmi->ipmi.base_address & 0xFFFF; + + snprintf(buffer, sizeof buffer, "Base Address : %08X%08X", + high,(low & ~1)); + snprintf(statbuffer, sizeof statbuffer, "Base Address : %08X%08X", + high,(low & ~1)); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "IRQ : %d", + dmi->ipmi.irq); + snprintf(statbuffer, sizeof statbuffer, "IRQ : %d", + dmi->ipmi.irq); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + printf("MENU: IPMI menu done (%d items)\n", menu->items_count); +} + +/* Compute Main Battery menu */ +void compute_battery(struct s_my_menu *menu, s_dmi * dmi) +{ + char buffer[SUBMENULEN + 1]; + char statbuffer[STATLEN + 1]; + menu->menu = add_menu(" Battery ", -1); + menu->items_count = 0; + set_menu_pos(SUBMENU_Y, SUBMENU_X); + + snprintf(buffer, sizeof buffer, "Vendor : %s", + dmi->battery.manufacturer); + snprintf(statbuffer, sizeof statbuffer, "Vendor: %s", + dmi->battery.manufacturer); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Manufacture Date: %s", + dmi->battery.manufacture_date); + snprintf(statbuffer, sizeof statbuffer, "Manufacture Date: %s", + dmi->battery.manufacture_date); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Serial : %s", + dmi->battery.serial); + snprintf(statbuffer, sizeof statbuffer, "Serial: %s", + dmi->battery.serial); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Name : %s", + dmi->battery.name); + snprintf(statbuffer, sizeof statbuffer, "Name: %s", dmi->battery.name); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Chemistry : %s", + dmi->battery.chemistry); + snprintf(statbuffer, sizeof statbuffer, "Chemistry: %s", + dmi->battery.chemistry); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Design Capacity : %s", + dmi->battery.design_capacity); + snprintf(statbuffer, sizeof statbuffer, "Design Capacity: %s", + dmi->battery.design_capacity); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Design Voltage : %s", + dmi->battery.design_voltage); + snprintf(statbuffer, sizeof statbuffer, "Design Voltage : %s", + dmi->battery.design_voltage); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "SBDS : %s", + dmi->battery.sbds); + snprintf(statbuffer, sizeof statbuffer, "SBDS: %s", dmi->battery.sbds); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "SBDS Manuf. Date: %s", + dmi->battery.sbds_manufacture_date); + snprintf(statbuffer, sizeof statbuffer, "SBDS Manufacture Date: %s", + dmi->battery.sbds_manufacture_date); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "SBDS Chemistry : %s", + dmi->battery.sbds_chemistry); + snprintf(statbuffer, sizeof statbuffer, "SBDS Chemistry : %s", + dmi->battery.sbds_chemistry); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Maximum Error : %s", + dmi->battery.maximum_error); + snprintf(statbuffer, sizeof statbuffer, "Maximum Error (percent) : %s", + dmi->battery.maximum_error); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "OEM Info : %s", + dmi->battery.oem_info); + snprintf(statbuffer, sizeof statbuffer, "OEM Info: %s", + dmi->battery.oem_info); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + printf("MENU: Battery menu done (%d items)\n", menu->items_count); +} diff --git a/com32/hdt/hdt-menu-kernel.c b/com32/hdt/hdt-menu-kernel.c new file mode 100644 index 0000000..b94d1fe --- /dev/null +++ b/com32/hdt/hdt-menu-kernel.c @@ -0,0 +1,91 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include "hdt-menu.h" + +/* Main Kernel menu */ +void compute_kernel(struct s_my_menu *menu, struct s_hardware *hardware) +{ + char buffer[SUBMENULEN + 1]; + char infobar[STATLEN + 1]; + char kernel_modules[LINUX_KERNEL_MODULE_SIZE * + MAX_KERNEL_MODULES_PER_PCI_DEVICE]; + struct pci_device *pci_device; + + menu->menu = add_menu(" Kernel Modules ", -1); + menu->items_count = 0; + set_menu_pos(SUBMENU_Y, SUBMENU_X); + + if (hardware->modules_pcimap_return_code == -ENOMODULESPCIMAP) { + add_item("The modules.pcimap file is missing", + "Missing modules.pcimap file", OPT_INACTIVE, NULL, 0); + add_item("Kernel modules can't be computed.", + "Missing modules.pcimap file", OPT_INACTIVE, NULL, 0); + add_item("Please put one in same dir as hdt", + "Missing modules.pcimap file", OPT_INACTIVE, NULL, 0); + add_item("", "", OPT_SEP, "", 0); + } else { + /* + * For every detected pci device, grab its kernel module to + * compute this submenu + */ + for_each_pci_func(pci_device, hardware->pci_domain) { + memset(kernel_modules, 0, sizeof kernel_modules); + for (int i = 0; + i < + pci_device->dev_info->linux_kernel_module_count; + i++) { + if (i > 0) { + strncat(kernel_modules, " | ", 3); + } + strncat(kernel_modules, + pci_device->dev_info-> + linux_kernel_module[i], + LINUX_KERNEL_MODULE_SIZE - 1); + } + /* No need to add unknown kernel modules */ + if (strlen(kernel_modules) > 0) { + snprintf(buffer, sizeof buffer, "%s (%s)", + kernel_modules, + pci_device->dev_info->class_name); + snprintf(infobar, sizeof infobar, + "%04x:%04x %s : %s\n", + pci_device->vendor, + pci_device->product, + pci_device->dev_info->vendor_name, + pci_device->dev_info->product_name); + + add_item(buffer, infobar, OPT_INACTIVE, NULL, + 0); + menu->items_count++; + } + } + } + + printf("MENU: Kernel menu done (%d items)\n", menu->items_count); +} diff --git a/com32/hdt/hdt-menu-pci.c b/com32/hdt/hdt-menu-pci.c new file mode 100644 index 0000000..74c0710 --- /dev/null +++ b/com32/hdt/hdt-menu-pci.c @@ -0,0 +1,194 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include "hdt-menu.h" + +/* Dynamic submenu for pci devices */ +static void compute_pci_device(struct s_my_menu *menu, + struct pci_device *pci_device, + int pci_bus, int pci_slot, int pci_func, + struct s_hardware *hardware) +{ + char buffer[56]; + char statbuffer[STATLEN]; + char kernel_modules[LINUX_KERNEL_MODULE_SIZE * + MAX_KERNEL_MODULES_PER_PCI_DEVICE]; + + menu->menu = add_menu(" Details ", -1); + menu->items_count = 0; + set_menu_pos(5, 17); + + snprintf(buffer, sizeof buffer, "Vendor : %s", + pci_device->dev_info->vendor_name); + snprintf(statbuffer, sizeof statbuffer, "Vendor Name: %s", + pci_device->dev_info->vendor_name); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Product : %s", + pci_device->dev_info->product_name); + snprintf(statbuffer, sizeof statbuffer, "Product Name %s", + pci_device->dev_info->product_name); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Class : %s", + pci_device->dev_info->class_name); + snprintf(statbuffer, sizeof statbuffer, "Class Name: %s", + pci_device->dev_info->class_name); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Location: %02x:%02x.%01x", pci_bus, + pci_slot, pci_func); + snprintf(statbuffer, sizeof statbuffer, + "Location on the PCI Bus: %02x:%02x.%01x", pci_bus, pci_slot, + pci_func); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "PCI ID : %04x:%04x[%04x:%04x]", + pci_device->vendor, pci_device->product, + pci_device->sub_vendor, pci_device->sub_product); + snprintf(statbuffer, sizeof statbuffer, + "vendor:product[sub_vendor:sub_product] : %04x:%04x[%04x:%04x]", + pci_device->vendor, pci_device->product, + pci_device->sub_vendor, pci_device->sub_product); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + if ((pci_device->dev_info->irq>0) && (pci_device->dev_info->irq<255)) { + snprintf(buffer, sizeof buffer,"IRQ : %d", pci_device->dev_info->irq); + snprintf(statbuffer, sizeof statbuffer,"IRQ : %d", pci_device->dev_info->irq); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + } + + snprintf(buffer, sizeof buffer, "Latency : %d",pci_device->dev_info->latency); + snprintf(statbuffer,sizeof statbuffer,"Latency : %d",pci_device->dev_info->latency); + add_item(buffer,statbuffer,OPT_INACTIVE,NULL,0); + menu->items_count++; + + if (pci_device->dev_info->linux_kernel_module_count > 1) { + for (int i = 0; + i < pci_device->dev_info->linux_kernel_module_count; i++) { + if (i > 0) { + strncat(kernel_modules, " | ", 3); + } + strncat(kernel_modules, + pci_device->dev_info->linux_kernel_module[i], + LINUX_KERNEL_MODULE_SIZE - 1); + } + snprintf(buffer, sizeof buffer, "Modules : %s", kernel_modules); + snprintf(statbuffer, sizeof statbuffer, "Kernel Modules: %s", + kernel_modules); + } else { + snprintf(buffer, sizeof buffer, "Module : %s", + pci_device->dev_info->linux_kernel_module[0]); + snprintf(statbuffer, sizeof statbuffer, "Kernel Module: %s", + pci_device->dev_info->linux_kernel_module[0]); + } + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + if (hardware->is_pxe_valid == true) { + snprintf(buffer,sizeof buffer,"MAC Addr. : %s",hardware->pxe.mac_addr); + snprintf(statbuffer,sizeof statbuffer,"MAC Address : %s",hardware->pxe.mac_addr); + add_item(buffer,statbuffer,OPT_INACTIVE,NULL,0); + menu->items_count++; + + if ((hardware->pxe.pci_device != NULL) + && (hardware->pxe.pci_device == pci_device)) { + snprintf(buffer,sizeof buffer,"PXE : %s","Current Boot device"); + snprintf(statbuffer,sizeof statbuffer,"PXE : %s","Current Boot device"); + add_item(buffer,statbuffer,OPT_INACTIVE,NULL,0); + menu->items_count++; + } + } +} + +/* Main PCI menu */ +int compute_PCI(struct s_hdt_menu *hdt_menu, struct s_hardware *hardware) +{ + int i = 0; + char menuname[255][MENULEN + 1]; + char infobar[255][STATLEN + 1]; + struct pci_device *pci_device; + char kernel_modules[LINUX_KERNEL_MODULE_SIZE * + MAX_KERNEL_MODULES_PER_PCI_DEVICE]; + + /* For every detected pci device, compute its submenu */ + for_each_pci_func(pci_device, hardware->pci_domain) { + memset(kernel_modules, 0, sizeof kernel_modules); + for (int kmod = 0; + kmod < pci_device->dev_info->linux_kernel_module_count; + kmod++) { + if (kmod > 0) { + strncat(kernel_modules, " | ", 3); + } + strncat(kernel_modules, + pci_device->dev_info->linux_kernel_module[kmod], + LINUX_KERNEL_MODULE_SIZE - 1); + } + if (pci_device->dev_info->linux_kernel_module_count == 0) + strlcpy(kernel_modules, "unknown", 7); + + compute_pci_device(&(hdt_menu->pci_sub_menu[i]), pci_device, + __pci_bus, __pci_slot, __pci_func, hardware); + snprintf(menuname[i], 59, "%s|%s", + pci_device->dev_info->vendor_name, + pci_device->dev_info->product_name); + snprintf(infobar[i], STATLEN, + "%02x:%02x.%01x # %s # ID:%04x:%04x[%04x:%04x] # Kmod:%s\n", + __pci_bus, __pci_slot, __pci_func, + pci_device->dev_info->class_name, pci_device->vendor, + pci_device->product, pci_device->sub_vendor, + pci_device->sub_product, kernel_modules); + i++; + } + + hdt_menu->pci_menu.menu = add_menu(" PCI Devices ", -1); + hdt_menu->pci_menu.items_count = 0; + if (hardware->pci_ids_return_code == -ENOPCIIDS) { + add_item("The pci.ids file is missing", "Missing pci.ids file", + OPT_INACTIVE, NULL, 0); + add_item("PCI Device names can't be computed.", + "Missing pci.ids file", OPT_INACTIVE, NULL, 0); + add_item("Please put one in same dir as hdt", + "Missing pci.ids file", OPT_INACTIVE, NULL, 0); + add_item("", "", OPT_SEP, "", 0); + } + for (int j = 0; j < i; j++) { + add_item(menuname[j], infobar[j], OPT_SUBMENU, NULL, + hdt_menu->pci_sub_menu[j].menu); + hdt_menu->pci_menu.items_count++; + } + printf("MENU: PCI menu done (%d items)\n", + hdt_menu->pci_menu.items_count); + return 0; +} diff --git a/com32/hdt/hdt-menu-processor.c b/com32/hdt/hdt-menu-processor.c new file mode 100644 index 0000000..a30cf0f --- /dev/null +++ b/com32/hdt/hdt-menu-processor.c @@ -0,0 +1,240 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include "hdt-menu.h" + +/* Compute Processor menu */ +void compute_processor(struct s_my_menu *menu, struct s_hardware *hardware) +{ + char buffer[SUBMENULEN + 1]; + char buffer1[SUBMENULEN + 1]; + char statbuffer[STATLEN + 1]; + + menu->menu = add_menu(" Main Processor ", -1); + menu->items_count = 0; + set_menu_pos(SUBMENU_Y, SUBMENU_X); + + snprintf(buffer, sizeof buffer, "Vendor : %s", hardware->cpu.vendor); + snprintf(statbuffer, sizeof statbuffer, "Vendor: %s", + hardware->cpu.vendor); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Model : %s", del_multi_spaces(hardware->cpu.model)); + snprintf(statbuffer, sizeof statbuffer, "Model: %s", + del_multi_spaces(hardware->cpu.model)); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Vendor ID : %d", + hardware->cpu.vendor_id); + snprintf(statbuffer, sizeof statbuffer, "Vendor ID: %d", + hardware->cpu.vendor_id); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Family ID : %d", hardware->cpu.family); + snprintf(statbuffer, sizeof statbuffer, "Family ID: %d", + hardware->cpu.family); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Model ID : %d", + hardware->cpu.model_id); + snprintf(statbuffer, sizeof statbuffer, "Model ID: %d", + hardware->cpu.model_id); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Stepping : %d", + hardware->cpu.stepping); + snprintf(statbuffer, sizeof statbuffer, "Stepping: %d", + hardware->cpu.stepping); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + if (hardware->is_dmi_valid) { + snprintf(buffer, sizeof buffer, "FSB : %d", + hardware->dmi.processor.external_clock); + snprintf(statbuffer, sizeof statbuffer, + "Front Side Bus (MHz): %d", + hardware->dmi.processor.external_clock); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Cur. Speed: %d", + hardware->dmi.processor.current_speed); + snprintf(statbuffer, sizeof statbuffer, + "Current Speed (MHz): %d", + hardware->dmi.processor.current_speed); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Max Speed : %d", + hardware->dmi.processor.max_speed); + snprintf(statbuffer, sizeof statbuffer, "Max Speed (MHz): %d", + hardware->dmi.processor.max_speed); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Upgrade : %s", + hardware->dmi.processor.upgrade); + snprintf(statbuffer, sizeof statbuffer, "Upgrade: %s", + hardware->dmi.processor.upgrade); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + } + + if (hardware->cpu.flags.smp) { + snprintf(buffer, sizeof buffer, "SMP : Yes"); + snprintf(statbuffer, sizeof statbuffer, "SMP: Yes"); + } else { + snprintf(buffer, sizeof buffer, "SMP : No"); + snprintf(statbuffer, sizeof statbuffer, "SMP: No"); + } + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + if (hardware->cpu.flags.lm) { + snprintf(buffer, sizeof buffer, "x86_64 : Yes"); + snprintf(statbuffer, sizeof statbuffer, + "x86_64 compatible processor: Yes"); + } else { + snprintf(buffer, sizeof buffer, "X86_64 : No"); + snprintf(statbuffer, sizeof statbuffer, + "X86_64 compatible processor: No"); + } + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + buffer1[0] = '\0'; + if (hardware->cpu.flags.fpu) + strcat(buffer1, "fpu "); + if (hardware->cpu.flags.vme) + strcat(buffer1, "vme "); + if (hardware->cpu.flags.de) + strcat(buffer1, "de "); + if (hardware->cpu.flags.pse) + strcat(buffer1, "pse "); + if (hardware->cpu.flags.tsc) + strcat(buffer1, "tsc "); + if (hardware->cpu.flags.msr) + strcat(buffer1, "msr "); + if (hardware->cpu.flags.pae) + strcat(buffer1, "pae "); + snprintf(buffer, sizeof buffer, "Flags : %s", buffer1); + snprintf(statbuffer, sizeof statbuffer, "Flags: %s", buffer1); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + buffer1[0] = '\0'; + if (hardware->cpu.flags.mce) + strcat(buffer1, "mce "); + if (hardware->cpu.flags.cx8) + strcat(buffer1, "cx8 "); + if (hardware->cpu.flags.apic) + strcat(buffer1, "apic "); + if (hardware->cpu.flags.sep) + strcat(buffer1, "sep "); + if (hardware->cpu.flags.mtrr) + strcat(buffer1, "mtrr "); + if (hardware->cpu.flags.pge) + strcat(buffer1, "pge "); + if (hardware->cpu.flags.mca) + strcat(buffer1, "mca "); + snprintf(buffer, sizeof buffer, "Flags : %s", buffer1); + snprintf(statbuffer, sizeof statbuffer, "Flags: %s", buffer1); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + buffer1[0] = '\0'; + if (hardware->cpu.flags.cmov) + strcat(buffer1, "cmov "); + if (hardware->cpu.flags.pat) + strcat(buffer1, "pat "); + if (hardware->cpu.flags.pse_36) + strcat(buffer1, "pse_36 "); + if (hardware->cpu.flags.psn) + strcat(buffer1, "psn "); + if (hardware->cpu.flags.clflsh) + strcat(buffer1, "clflsh "); + snprintf(buffer, sizeof buffer, "Flags : %s", buffer1); + snprintf(statbuffer, sizeof statbuffer, "Flags: %s", buffer1); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + buffer1[0] = '\0'; + if (hardware->cpu.flags.dts) + strcat(buffer1, "dts "); + if (hardware->cpu.flags.acpi) + strcat(buffer1, "acpi "); + if (hardware->cpu.flags.mmx) + strcat(buffer1, "mmx "); + if (hardware->cpu.flags.sse) + strcat(buffer1, "sse "); + snprintf(buffer, sizeof buffer, "Flags : %s", buffer1); + snprintf(statbuffer, sizeof statbuffer, "Flags: %s", buffer1); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + buffer1[0] = '\0'; + if (hardware->cpu.flags.sse2) + strcat(buffer1, "sse2 "); + if (hardware->cpu.flags.ss) + strcat(buffer1, "ss "); + if (hardware->cpu.flags.htt) + strcat(buffer1, "ht "); + if (hardware->cpu.flags.acc) + strcat(buffer1, "acc "); + if (hardware->cpu.flags.syscall) + strcat(buffer1, "syscall "); + if (hardware->cpu.flags.mp) + strcat(buffer1, "mp "); + snprintf(buffer, sizeof buffer, "Flags : %s", buffer1); + snprintf(statbuffer, sizeof statbuffer, "Flags: %s", buffer1); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + buffer1[0] = '\0'; + if (hardware->cpu.flags.nx) + strcat(buffer1, "nx "); + if (hardware->cpu.flags.mmxext) + strcat(buffer1, "mmxext "); + if (hardware->cpu.flags.lm) + strcat(buffer1, "lm "); + if (hardware->cpu.flags.nowext) + strcat(buffer1, "3dnowext "); + if (hardware->cpu.flags.now) + strcat(buffer1, "3dnow! "); + snprintf(buffer, sizeof buffer, "Flags : %s", buffer1); + snprintf(statbuffer, sizeof statbuffer, "Flags: %s", buffer1); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + printf("MENU: Processor menu done (%d items)\n", menu->items_count); +} diff --git a/com32/hdt/hdt-menu-pxe.c b/com32/hdt/hdt-menu-pxe.c new file mode 100644 index 0000000..497fa77 --- /dev/null +++ b/com32/hdt/hdt-menu-pxe.c @@ -0,0 +1,120 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include "hdt-menu.h" + +/* Main Kernel menu */ +void compute_PXE(struct s_my_menu *menu, struct s_hardware *hardware) +{ + char buffer[SUBMENULEN + 1]; + char infobar[STATLEN + 1]; + + if (hardware->is_pxe_valid == false) + return; + + menu->menu = add_menu(" PXE ", -1); + menu->items_count = 0; + set_menu_pos(SUBMENU_Y, SUBMENU_X); + + struct s_pxe *p = &hardware->pxe; + + if ((hardware->pci_ids_return_code == -ENOPCIIDS) || (p->pci_device==NULL)) { + snprintf(buffer, sizeof buffer, "PCI Vendor : %d", + p->vendor_id); + snprintf(infobar, sizeof infobar, "PCI Vendor : %d", + p->vendor_id); + add_item(buffer, infobar, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "PCI Product : %d", + p->vendor_id); + snprintf(infobar, sizeof infobar, "PCI Product : %d", + p->vendor_id); + add_item(buffer, infobar, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "PCI SubVendor : %d", + p->subvendor_id); + snprintf(infobar, sizeof infobar, "PCI SubVendor : %d", + p->subvendor_id); + add_item(buffer, infobar, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "PCI SubProduct : %d", + p->subproduct_id); + snprintf(infobar, sizeof infobar, "PCI SubProduct : %d", + p->subproduct_id); + add_item(buffer, infobar, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "PCI Revision : %d", p->rev); + snprintf(infobar, sizeof infobar, "PCI Revision : %d", + p->rev); + add_item(buffer, infobar, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, + "PCI Bus Pos. : %02x:%02x.%02x", p->pci_bus, + p->pci_dev, p->pci_func); + snprintf(infobar, sizeof infobar, + "PCI Bus Pos. : %02x:%02x.%02x", p->pci_bus, + p->pci_dev, p->pci_func); + add_item(buffer, infobar, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + } else { + + snprintf(buffer, sizeof buffer, "Manufacturer : %s", + p->pci_device->dev_info->vendor_name); + snprintf(infobar, sizeof infobar, "Manufacturer : %s", + p->pci_device->dev_info->vendor_name); + add_item(buffer, infobar, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Product : %s", + p->pci_device->dev_info->product_name); + snprintf(infobar, sizeof infobar, "Product : %s", + p->pci_device->dev_info->product_name); + add_item(buffer, infobar, OPT_INACTIVE, NULL, 0); + menu->items_count++; + } + + snprintf(buffer, sizeof buffer, "MAC Address : %s", p->mac_addr); + snprintf(infobar, sizeof infobar, "MAC Address : %s", p->mac_addr); + add_item(buffer, infobar, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "IP Address : %d.%d.%d.%d", + p->ip_addr[0], p->ip_addr[1], p->ip_addr[2], p->ip_addr[3]); + snprintf(infobar, sizeof infobar, "IP Address : %d.%d.%d.%d", + p->ip_addr[0], p->ip_addr[1], p->ip_addr[2], p->ip_addr[3]); + add_item(buffer, infobar, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + printf("MENU: PXE menu done (%d items)\n", menu->items_count); +} diff --git a/com32/hdt/hdt-menu-summary.c b/com32/hdt/hdt-menu-summary.c new file mode 100644 index 0000000..6401221 --- /dev/null +++ b/com32/hdt/hdt-menu-summary.c @@ -0,0 +1,209 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- +*/ + +#include "hdt-menu.h" + +/* Computing Summary menu */ +void compute_summarymenu(struct s_my_menu *menu, struct s_hardware *hardware) +{ + char buffer[SUBMENULEN + 1]; + char statbuffer[STATLEN + 1]; + char bank_number[10]; + + menu->menu = add_menu(" Summary ", -1); + menu->items_count = 0; + + set_menu_pos(SUBMENU_Y, SUBMENU_X); + + snprintf(buffer, sizeof buffer, "CPU Vendor : %s", + hardware->cpu.vendor); + snprintf(statbuffer, sizeof statbuffer, "CPU Vendor: %s", + hardware->cpu.vendor); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "CPU Model : %s", + hardware->cpu.model); + snprintf(statbuffer, sizeof statbuffer, "CPU Model: %s", + hardware->cpu.model); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + add_item("", "", OPT_SEP, "", 0); + if (hardware->is_dmi_valid == true) { + + snprintf(buffer, sizeof buffer, "System Vendor : %s", + hardware->dmi.system.manufacturer); + snprintf(statbuffer, sizeof statbuffer, "System Vendor: %s", + hardware->dmi.system.manufacturer); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "System Product: %s", + hardware->dmi.system.product_name); + snprintf(statbuffer, sizeof statbuffer, + "System Product Name: %s", + hardware->dmi.system.product_name); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "System Serial : %s", + hardware->dmi.system.serial); + snprintf(statbuffer, sizeof statbuffer, + "System Serial Number: %s", + hardware->dmi.system.serial); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + add_item("", "", OPT_SEP, "", 0); + + snprintf(buffer, sizeof buffer, "Bios Version : %s", + hardware->dmi.bios.version); + snprintf(statbuffer, sizeof statbuffer, "Bios Version: %s", + hardware->dmi.bios.version); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Bios Release : %s", + hardware->dmi.bios.release_date); + snprintf(statbuffer, sizeof statbuffer, "Bios Release Date: %s", + hardware->dmi.bios.release_date); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + add_item("", "", OPT_SEP, "", 0); + + for (int i = 0; i < hardware->dmi.memory_count; i++) { + if (hardware->dmi.memory[i].filled == true) { + /* When discovering the first item, let's clear the screen */ + memset(bank_number, 0, sizeof(bank_number)); + snprintf(bank_number, sizeof(bank_number), + "%d ", i); + if (strncmp + (hardware->dmi.memory[i].size, "Free", 4)) { + snprintf(buffer, sizeof buffer, + "Mem bank %02d : %s %s@%s", + i, + hardware->dmi.memory[i].size, + hardware->dmi.memory[i].type, + hardware->dmi.memory[i].speed); + snprintf(statbuffer, sizeof statbuffer, + "Memory bank %02d : %s %s@%s", + i, + hardware->dmi.memory[i].size, + hardware->dmi.memory[i].type, + hardware->dmi.memory[i].speed); + add_item(buffer, statbuffer, + OPT_INACTIVE, NULL, 0); + menu->items_count++; + } + } + } + + add_item("", "", OPT_SEP, "", 0); + } + + snprintf(buffer, sizeof buffer, "Nb PCI Devices: %d", + hardware->nb_pci_devices); + snprintf(statbuffer, sizeof statbuffer, "Number of PCI Devices: %d", + hardware->nb_pci_devices); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + add_item("", "", OPT_SEP, "", 0); + + if (hardware->is_pxe_valid == true) { + struct s_pxe *p = &hardware->pxe; + + snprintf(buffer, sizeof buffer, "PXE MAC Address: %s", + p->mac_addr); + snprintf(statbuffer, sizeof statbuffer, "PXE MAC Address: %s", + p->mac_addr); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "PXE IP Address : %d.%d.%d.%d", + p->ip_addr[0], p->ip_addr[1], p->ip_addr[2], + p->ip_addr[3]); + snprintf(statbuffer, sizeof statbuffer, + "PXE IP Address: %d.%d.%d.%d", p->ip_addr[0], + p->ip_addr[1], p->ip_addr[2], p->ip_addr[3]); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + add_item("", "", OPT_SEP, "", 0); + } + + if (hardware->modules_pcimap_return_code != -ENOMODULESPCIMAP) { + bool kmod = false; + struct pci_device *pci_device; + char kernel_modules[LINUX_KERNEL_MODULE_SIZE * + MAX_KERNEL_MODULES_PER_PCI_DEVICE]; + + /* + * For every detected pci device, grab its kernel module to compute + * this submenu + */ + for_each_pci_func(pci_device, hardware->pci_domain) { + memset(kernel_modules, 0, sizeof kernel_modules); + for (int i = 0; + i < + pci_device->dev_info->linux_kernel_module_count; + i++) { + if (i > 0) { + strncat(kernel_modules, " | ", 3); + } + strncat(kernel_modules, + pci_device->dev_info-> + linux_kernel_module[i], + LINUX_KERNEL_MODULE_SIZE - 1); + } + /* No need to add unknown kernel modules */ + if (strlen(kernel_modules) > 0) { + snprintf(buffer, sizeof buffer, "%s (%s)", + kernel_modules, + pci_device->dev_info->class_name); + snprintf(statbuffer, sizeof statbuffer, + "%04x:%04x %s : %s\n", + pci_device->vendor, + pci_device->product, + pci_device->dev_info->vendor_name, + pci_device->dev_info->product_name); + + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, + 0); + menu->items_count++; + kmod = true; + } + } + if (kmod == true) + add_item("", "", OPT_SEP, "", 0); + } + + printf("MENU: Summary menu done (%d items)\n", menu->items_count); +} diff --git a/com32/hdt/hdt-menu-syslinux.c b/com32/hdt/hdt-menu-syslinux.c new file mode 100644 index 0000000..4c874d0 --- /dev/null +++ b/com32/hdt/hdt-menu-syslinux.c @@ -0,0 +1,85 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include "syslinux/config.h" +#include "hdt-menu.h" + +/* Computing Syslinux menu */ +void compute_syslinuxmenu(struct s_my_menu *menu, struct s_hardware *hardware) +{ + char syslinux_fs_menu[24]; + char buffer[SUBMENULEN + 1]; + char statbuffer[STATLEN + 1]; + + memset(syslinux_fs_menu, 0, sizeof syslinux_fs_menu); + + snprintf(syslinux_fs_menu, sizeof syslinux_fs_menu, " %s ", + hardware->syslinux_fs); + menu->menu = add_menu(syslinux_fs_menu, -1); + menu->items_count = 0; + set_menu_pos(SUBMENU_Y, SUBMENU_X); + + snprintf(buffer, sizeof buffer, "Bootloader : %s", + hardware->syslinux_fs); + snprintf(statbuffer, sizeof statbuffer, "Bootloader: %s", + hardware->syslinux_fs); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Version : %s", + hardware->sv->version_string + 2); + snprintf(statbuffer, sizeof statbuffer, "Version: %s", + hardware->sv->version_string + 2); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Version : %u", + hardware->sv->version); + snprintf(statbuffer, sizeof statbuffer, "Version: %u", + hardware->sv->version); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Max API : %u", + hardware->sv->max_api); + snprintf(statbuffer, sizeof statbuffer, "Max API: %u", + hardware->sv->max_api); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + add_item("", "", OPT_SEP, "", 0); + + snprintf(buffer, sizeof buffer, "%s", + hardware->sv->copyright_string + 1); + snprintf(statbuffer, sizeof statbuffer, "%s", + hardware->sv->copyright_string + 1); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + printf("MENU: Syslinux menu done (%d items)\n", menu->items_count); +} diff --git a/com32/hdt/hdt-menu-vesa.c b/com32/hdt/hdt-menu-vesa.c new file mode 100644 index 0000000..ed03ff2 --- /dev/null +++ b/com32/hdt/hdt-menu-vesa.c @@ -0,0 +1,109 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- +*/ + +#include "hdt-menu.h" + + +/* Submenu for the vesa card */ +static void compute_vesa_card(struct s_my_menu *menu, struct s_hardware *hardware) { + char buffer[SUBMENULEN+1]; + char statbuffer[STATLEN+1]; + + menu->menu = add_menu(" VESA Bios ",-1); + menu->items_count=0; + set_menu_pos(SUBMENU_Y,SUBMENU_X); + + snprintf(buffer,sizeof buffer,"VESA Version: %d.%d",hardware->vesa.major_version,hardware->vesa.minor_version); + snprintf(statbuffer,sizeof statbuffer,"Version: %d.%d",hardware->vesa.major_version,hardware->vesa.minor_version); + add_item(buffer,statbuffer,OPT_INACTIVE,NULL,0); + menu->items_count++; + + snprintf(buffer,sizeof buffer,"Vendor : %s",hardware->vesa.vendor); + snprintf(statbuffer,sizeof statbuffer,"Vendor Name: %s",hardware->vesa.vendor); + add_item(buffer,statbuffer,OPT_INACTIVE,NULL,0); + menu->items_count++; + + snprintf(buffer,sizeof buffer,"Product : %s",hardware->vesa.product); + snprintf(statbuffer,sizeof statbuffer,"Product Name: %s",hardware->vesa.product); + add_item(buffer,statbuffer,OPT_INACTIVE,NULL,0); + menu->items_count++; + + snprintf(buffer,sizeof buffer,"Product Rev.: %s",hardware->vesa.product_revision); + snprintf(statbuffer,sizeof statbuffer,"Produt Revision: %s",hardware->vesa.product_revision); + add_item(buffer,statbuffer,OPT_INACTIVE,NULL,0); + menu->items_count++; + + snprintf(buffer,sizeof buffer,"Software Rev: %d",hardware->vesa.software_rev); + snprintf(statbuffer,sizeof statbuffer,"Software Revision: %d",hardware->vesa.software_rev); + add_item(buffer,statbuffer,OPT_INACTIVE,NULL,0); + menu->items_count++; + + snprintf(buffer,sizeof buffer,"Memory (KB) : %d",hardware->vesa.total_memory*64); + snprintf(statbuffer,sizeof statbuffer,"Memory (KB): %d",hardware->vesa.total_memory*64); + add_item(buffer,statbuffer,OPT_INACTIVE,NULL,0); + menu->items_count++; +} + +/* Submenu for the vesa card */ +void compute_vesa_modes(struct s_my_menu *menu, struct s_hardware *hardware) { + char buffer[56]; + char statbuffer[STATLEN]; + + menu->menu = add_menu(" VESA Modes ",-1); + menu->items_count=0; + set_menu_pos(SUBMENU_Y,SUBMENU_X); + for (int i=0;ivesa.vmi_count;i++) { + struct vesa_mode_info *mi=&hardware->vesa.vmi[i].mi; + /* Sometimes, vesa bios reports 0x0 modes + * We don't need to display that ones */ + if ((mi->h_res==0) || (mi->v_res==0)) continue; + snprintf(buffer,sizeof buffer,"%4u x %4u x %2ubits vga=%3d", + mi->h_res, mi->v_res, mi->bpp, hardware->vesa.vmi[i].mode+0x200); + snprintf(statbuffer,sizeof statbuffer,"%4ux%4ux%2ubits vga=%3d", + mi->h_res, mi->v_res, mi->bpp, hardware->vesa.vmi[i].mode+0x200); + add_item(buffer,statbuffer,OPT_INACTIVE,NULL,0); + menu->items_count++; + } +} + +/* Main VESA Menu*/ +int compute_VESA(struct s_hdt_menu *hdt_menu, struct s_hardware *hardware) { + char buffer[15]; + compute_vesa_card(&hdt_menu->vesa_card_menu,hardware); + compute_vesa_modes(&hdt_menu->vesa_modes_menu,hardware); + hdt_menu->vesa_menu.menu = add_menu(" VESA ",-1); + hdt_menu->vesa_menu.items_count=0; + + add_item("VESA Bios","VESA Bios",OPT_SUBMENU,NULL,hdt_menu->vesa_card_menu.menu); + hdt_menu->vesa_menu.items_count++; + snprintf(buffer,sizeof buffer,"%s (%d)","Modes",hardware->vesa.vmi_count); + add_item(buffer,"VESA Modes",OPT_SUBMENU,NULL,hdt_menu->vesa_modes_menu.menu); + hdt_menu->vesa_menu.items_count++; + printf("MENU: VESA menu done (%d items)\n",hdt_menu->vesa_menu.items_count); + return 0; +} diff --git a/com32/hdt/hdt-menu-vpd.c b/com32/hdt/hdt-menu-vpd.c new file mode 100644 index 0000000..817c107 --- /dev/null +++ b/com32/hdt/hdt-menu-vpd.c @@ -0,0 +1,100 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Pierre-Alexandre Meyer - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include "hdt-menu.h" + +/** + * compute_vpd - generate vpd menu + **/ +void compute_vpd(struct s_my_menu *menu, struct s_hardware *hardware) +{ + char buffer[SUBMENULEN + 1]; + char statbuffer[STATLEN + 1]; /* Status bar */ + + menu->menu = add_menu(" VPD ", -1); + menu->items_count = 0; + set_menu_pos(SUBMENU_Y, SUBMENU_X); + + snprintf(buffer, sizeof buffer, "Address : %s", + hardware->vpd.base_address); + snprintf(statbuffer, sizeof statbuffer, "Address: %s", + hardware->cpu.vendor); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Bios Build ID : %s", + hardware->vpd.bios_build_id); + snprintf(statbuffer, sizeof statbuffer, "Bios Build ID: %s", + hardware->vpd.bios_build_id); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Bios Release Date : %s", + hardware->vpd.bios_release_date); + snprintf(statbuffer, sizeof statbuffer, "Bios Release Date: %s", + hardware->vpd.bios_release_date); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Bios Version : %s", + hardware->vpd.bios_version); + snprintf(statbuffer, sizeof statbuffer, "Bios Version: %s", + hardware->vpd.bios_version); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Default Flash Filename : %s", + hardware->vpd.default_flash_filename); + snprintf(statbuffer, sizeof statbuffer, "Default Flash Filename: %s", + hardware->vpd.default_flash_filename); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Box Serial Number : %s", + hardware->vpd.box_serial_number); + snprintf(statbuffer, sizeof statbuffer, "Box Serial Number: %s", + hardware->vpd.box_serial_number); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Motherboard Serial Number: %s", + hardware->vpd.motherboard_serial_number); + snprintf(statbuffer, sizeof statbuffer, "Motherboard Serial Number: %s", + hardware->vpd.motherboard_serial_number); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + snprintf(buffer, sizeof buffer, "Machine Type/Model : %s", + hardware->vpd.machine_type_model); + snprintf(statbuffer, sizeof statbuffer, "Machine Type/Model: %s", + hardware->vpd.machine_type_model); + add_item(buffer, statbuffer, OPT_INACTIVE, NULL, 0); + menu->items_count++; + + printf("MENU: VPD menu done (%d items)\n", menu->items_count); +} diff --git a/com32/hdt/hdt-menu.c b/com32/hdt/hdt-menu.c new file mode 100644 index 0000000..18158ae --- /dev/null +++ b/com32/hdt/hdt-menu.c @@ -0,0 +1,319 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include "hdt-menu.h" + +int start_menu_mode(struct s_hardware *hardware, char *version_string) +{ + struct s_hdt_menu hdt_menu; + + memset(&hdt_menu, 0, sizeof(hdt_menu)); + + /* Detect every kind of hardware */ + detect_hardware(hardware); + + /* Setup the menu system */ + setup_menu(version_string); + + /* Compute all submenus */ + compute_submenus(&hdt_menu, hardware); + + /* Compute the main menu */ + compute_main_menu(&hdt_menu, hardware); + +#ifdef WITH_MENU_DISPLAY + t_menuitem *curr; + char cmd[160]; + + printf("Starting Menu (%d menus)\n", hdt_menu.total_menu_count); + curr = showmenus(hdt_menu.main_menu.menu); + /* When we exit the menu, do we have something to do? */ + if (curr) { + /* When want to execute something */ + if (curr->action == OPT_RUN) { + /* Tweak, we want to switch to the cli */ + if (!strncmp + (curr->data, HDT_SWITCH_TO_CLI, + sizeof(HDT_SWITCH_TO_CLI))) { + return HDT_RETURN_TO_CLI; + } + strcpy(cmd, curr->data); + + /* Use specific syslinux call if needed */ + if (issyslinux()) + runsyslinuxcmd(cmd); + else + csprint(cmd, 0x07); + return 1; // Should not happen when run from SYSLINUX + } + } +#endif + return 0; +} + +/* In the menu system, what to do on keyboard timeout */ +TIMEOUTCODE ontimeout() +{ + // beep(); + return CODE_WAIT; +} + +/* Keyboard handler for the menu system */ +void keys_handler(t_menusystem * ms, t_menuitem * mi, unsigned int scancode) +{ + char nc; + + if ((scancode >> 8) == F1) { // If scancode of F1 + runhelpsystem(mi->helpid); + } + /* + * If user hit TAB, and item is an "executable" item + * and user has privileges to edit it, edit it in place. + */ + if (((scancode & 0xFF) == 0x09) && (mi->action == OPT_RUN)) { +//(isallowed(username,"editcmd") || isallowed(username,"root"))) { + nc = getnumcols(); + /* User typed TAB and has permissions to edit command line */ + gotoxy(EDITPROMPT, 1, ms->menupage); + csprint("Command line:", 0x07); + editstring(mi->data, ACTIONLEN); + gotoxy(EDITPROMPT, 1, ms->menupage); + cprint(' ', 0x07, nc - 1, ms->menupage); + } +} + +/* Setup the Menu system */ +void setup_menu(char *version) +{ + /* Creating the menu */ + init_menusystem(version); + set_window_size(0, 0, 25, 80); + + /* Register the menusystem handler */ + // reg_handler(HDLR_SCREEN,&msys_handler); + reg_handler(HDLR_KEYS, &keys_handler); + + /* Register the ontimeout handler, with a time out of 10 seconds */ + reg_ontimeout(ontimeout, 1000, 0); +} + +/* Compute Main' submenus */ +void compute_submenus(struct s_hdt_menu *hdt_menu, struct s_hardware *hardware) +{ + + /* Compute this menu if a DMI table exists */ + if (hardware->is_dmi_valid) { + if (hardware->dmi.ipmi.filled == true) + compute_ipmi(&hdt_menu->ipmi_menu, &hardware->dmi); + if (hardware->dmi.base_board.filled == true) + compute_motherboard(&(hdt_menu->mobo_menu), + &(hardware->dmi)); + if (hardware->dmi.chassis.filled == true) + compute_chassis(&(hdt_menu->chassis_menu), + &(hardware->dmi)); + if (hardware->dmi.system.filled == true) + compute_system(&(hdt_menu->system_menu), + &(hardware->dmi)); + for (int i = 0; i < hardware->dmi.memory_count; i++) { + if (hardware->dmi.memory[i].filled == true) { + compute_memory(hdt_menu, &(hardware->dmi),hardware); + break; + } + } + if (hardware->dmi.bios.filled == true) + compute_bios(&(hdt_menu->bios_menu), &(hardware->dmi)); + if (hardware->dmi.battery.filled == true) + compute_battery(&(hdt_menu->battery_menu), + &(hardware->dmi)); + } + + compute_processor(&(hdt_menu->cpu_menu), hardware); + compute_vpd(&(hdt_menu->vpd_menu), hardware); + compute_disks(hdt_menu, hardware->disk_info, hardware); + +#ifdef WITH_PCI + compute_PCI(hdt_menu, hardware); + compute_PXE(&(hdt_menu->pxe_menu), hardware); + compute_kernel(&(hdt_menu->kernel_menu), hardware); +#endif + compute_summarymenu(&(hdt_menu->summary_menu), hardware); + compute_syslinuxmenu(&(hdt_menu->syslinux_menu), hardware); + compute_VESA(hdt_menu,hardware); + compute_aboutmenu(&(hdt_menu->about_menu)); +} + +void compute_main_menu(struct s_hdt_menu *hdt_menu, struct s_hardware *hardware) +{ + char menu_item[64]; + /* Let's count the number of menus we have */ + hdt_menu->total_menu_count = 0; + hdt_menu->main_menu.items_count = 0; + + hdt_menu->main_menu.menu = add_menu(" Main Menu ", -1); + set_item_options(-1, 24); + +#ifdef WITH_PCI + snprintf(menu_item, sizeof(menu_item), "PC Devices(%2d)\n", + hardware->nb_pci_devices); + add_item(menu_item, "PCI Devices Menu", OPT_SUBMENU, NULL, + hdt_menu->pci_menu.menu); + hdt_menu->main_menu.items_count++; + hdt_menu->total_menu_count += hdt_menu->pci_menu.items_count; +#endif + if (hdt_menu->disk_menu.items_count > 0) { + snprintf(menu_item, sizeof(menu_item), "isks (%2d)\n", + hdt_menu->disk_menu.items_count); + add_item(menu_item, "Disks Menu", OPT_SUBMENU, NULL, + hdt_menu->disk_menu.menu); + hdt_menu->main_menu.items_count++; + hdt_menu->total_menu_count += hdt_menu->disk_menu.items_count; + } + + if (hdt_menu->memory_menu.items_count > 0) { + snprintf(menu_item, sizeof(menu_item), "emory (%2d)\n", + hdt_menu->memory_menu.items_count); + add_item(menu_item, "Memory Menu", OPT_SUBMENU, NULL, + hdt_menu->memory_menu.menu); + hdt_menu->main_menu.items_count++; + hdt_menu->total_menu_count += hdt_menu->memory_menu.items_count; + } + add_item("

rocessor", "Main Processor Menu", OPT_SUBMENU, NULL, + hdt_menu->cpu_menu.menu); + hdt_menu->main_menu.items_count++; + + if (hardware->is_dmi_valid) { + if (hardware->dmi.base_board.filled == true) { + add_item("Mtherboard", "Motherboard Menu", + OPT_SUBMENU, NULL, hdt_menu->mobo_menu.menu); + hdt_menu->main_menu.items_count++; + } + + if (hardware->dmi.bios.filled == true) { + add_item("ios", "Bios Menu", OPT_SUBMENU, NULL, + hdt_menu->bios_menu.menu); + hdt_menu->main_menu.items_count++; + } + + if (hardware->dmi.chassis.filled == true) { + add_item("hassis", "Chassis Menu", OPT_SUBMENU, NULL, + hdt_menu->chassis_menu.menu); + hdt_menu->main_menu.items_count++; + } + + if (hardware->dmi.system.filled == true) { + add_item("ystem", "System Menu", OPT_SUBMENU, NULL, + hdt_menu->system_menu.menu); + hdt_menu->main_menu.items_count++; + } + + if (hardware->dmi.battery.filled == true) { + add_item("Batery", "Battery Menu", OPT_SUBMENU, NULL, + hdt_menu->battery_menu.menu); + hdt_menu->main_menu.items_count++; + } + if (hardware->dmi.ipmi.filled == true) { + add_item("I

MI", "IPMI Menu", OPT_SUBMENU, NULL, + hdt_menu->ipmi_menu.menu); + hdt_menu->main_menu.items_count++; + } + } + + if (hardware->is_vpd_valid == true) { + add_item("VPD","VPD Information Menu", OPT_SUBMENU, NULL, + hdt_menu->vpd_menu.menu); + hdt_menu->main_menu.items_count++; + } + + if (hardware->is_pxe_valid == true) { + add_item("PE", "PXE Information Menu", OPT_SUBMENU, NULL, + hdt_menu->pxe_menu.menu); + hdt_menu->main_menu.items_count++; + } + + if (hardware->is_vesa_valid == true) { + add_item("ESA","VESA Information Menu", OPT_SUBMENU, NULL, + hdt_menu->vesa_menu.menu); + hdt_menu->main_menu.items_count++; + } + + add_item("", "", OPT_SEP, "", 0); +#ifdef WITH_PCI + if (hardware->modules_pcimap_return_code != -ENOMODULESPCIMAP) { + add_item("ernel Modules", "Kernel Modules Menu", OPT_SUBMENU, + NULL, hdt_menu->kernel_menu.menu); + hdt_menu->main_menu.items_count++; + } +#endif + add_item("Sslinux", "Syslinux Information Menu", OPT_SUBMENU, NULL, + hdt_menu->syslinux_menu.menu); + hdt_menu->main_menu.items_count++; + add_item("Smmary", "Summary Information Menu", OPT_SUBMENU, NULL, + hdt_menu->summary_menu.menu); + hdt_menu->main_menu.items_count++; + + add_item("", "", OPT_SEP, "", 0); + + add_item("Sitch to CLI", "Switch to Command Line", OPT_RUN, + HDT_SWITCH_TO_CLI, 0); + add_item("bout", "About Menu", OPT_SUBMENU, NULL, + hdt_menu->about_menu.menu); + add_item("Eit","Exit", OPT_EXITMENU,NULL,0); + hdt_menu->main_menu.items_count++; + + hdt_menu->total_menu_count += hdt_menu->main_menu.items_count; +} + +void detect_hardware(struct s_hardware *hardware) +{ + printf("CPU: Detecting\n"); + cpu_detect(hardware); + + printf("DISKS: Detecting\n"); + detect_disks(hardware); + + printf("DMI: Detecting Table\n"); + if (detect_dmi(hardware) == -ENODMITABLE) { + printf("DMI: ERROR ! Table not found ! \n"); + printf + ("DMI: Many hardware components will not be detected ! \n"); + } else { + printf("DMI: Table found ! (version %d.%d)\n", + hardware->dmi.dmitable.major_version, + hardware->dmi.dmitable.minor_version); + } + + printf("VPD: Detecting\n"); + detect_vpd(hardware); + +#ifdef WITH_PCI + detect_pci(hardware); + printf("PCI: %d Devices Found\n", hardware->nb_pci_devices); +#endif + printf("VESA: Detecting\n"); + detect_vesa(hardware); +} diff --git a/com32/hdt/hdt-menu.h b/com32/hdt/hdt-menu.h new file mode 100644 index 0000000..70fdb38 --- /dev/null +++ b/com32/hdt/hdt-menu.h @@ -0,0 +1,134 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#ifndef DEFINE_HDT_MENU_H +#define DEFINE_HDT_MENU_H +#include + +#include "menu.h" +#include "help.h" +//#include "cpuid.h" +#include "sys/pci.h" +#include "hdt-common.h" +#include "dmi/dmi.h" +#include "hdt-ata.h" + +#define EDITPROMPT 21 + +#define SUBMENULEN 46 + +#define SUBMENU_Y 3 +#define SUBMENU_X 29 + +#define MAX_PCI_SUB_MENU 128 +#define MAX_MEMORY_SUB_MENU 32 +#define MAX_DISK_SUB_MENU 32 + +struct s_my_menu { + unsigned char menu; + int items_count; +}; + +struct s_hdt_menu { + struct s_my_menu main_menu; + struct s_my_menu cpu_menu; + struct s_my_menu mobo_menu; + struct s_my_menu chassis_menu; + struct s_my_menu bios_menu; + struct s_my_menu ipmi_menu; + struct s_my_menu system_menu; + struct s_my_menu pci_menu; + struct s_my_menu pci_sub_menu[MAX_PCI_SUB_MENU]; + struct s_my_menu kernel_menu; + struct s_my_menu memory_menu; + struct s_my_menu memory_sub_menu[MAX_MEMORY_SUB_MENU]; + struct s_my_menu disk_menu; + struct s_my_menu disk_sub_menu[MAX_DISK_SUB_MENU]; + struct s_my_menu battery_menu; + struct s_my_menu syslinux_menu; + struct s_my_menu about_menu; + struct s_my_menu summary_menu; + struct s_my_menu pxe_menu; + struct s_my_menu vesa_menu; + struct s_my_menu vesa_card_menu; + struct s_my_menu vesa_modes_menu; + struct s_my_menu vpd_menu; + int total_menu_count; // Sum of all menus we have +}; + +TIMEOUTCODE ontimeout(); +void keys_handler(t_menusystem * ms, t_menuitem * mi, unsigned int scancode); + +// PCI Stuff +int compute_PCI(struct s_hdt_menu *hdt_menu, struct s_hardware *hardware); + +// KERNEL Stuff +void compute_kernel(struct s_my_menu *menu, struct s_hardware *hardware); + +// Disk Stuff +int compute_disk_module(struct s_my_menu *menu, int nb_sub_disk_menu, + struct diskinfo *d, int disk_number); +void compute_disks(struct s_hdt_menu *menu, struct diskinfo *disk_info, struct s_hardware *hardware); + +// DMI Stuff +void compute_motherboard(struct s_my_menu *menu, s_dmi * dmi); +void compute_battery(struct s_my_menu *menu, s_dmi * dmi); +void compute_system(struct s_my_menu *menu, s_dmi * dmi); +void compute_chassis(struct s_my_menu *menu, s_dmi * dmi); +void compute_bios(struct s_my_menu *menu, s_dmi * dmi); +void compute_memory(struct s_hdt_menu *menu, s_dmi * dmi, struct s_hardware *hardware); +void compute_ipmi(struct s_my_menu *menu, s_dmi * dmi); + +// VPD Stuff +void compute_vpd(struct s_my_menu *menu, struct s_hardware *hardware); + +// Processor Stuff +void compute_processor(struct s_my_menu *menu, struct s_hardware *hardware); + +// Syslinux stuff +void compute_syslinuxmenu(struct s_my_menu *menu, struct s_hardware *hardware); + +// About menu +void compute_aboutmenu(struct s_my_menu *menu); + +// Summary menu +void compute_summarymenu(struct s_my_menu *menu, struct s_hardware *hardware); + +// PXE menu +void compute_PXE(struct s_my_menu *menu, struct s_hardware *hardware); + +//VESA menu +int compute_VESA(struct s_hdt_menu *hdt_menu, struct s_hardware *hardware); + +int start_menu_mode(struct s_hardware *hardware, char *version_string); +void setup_menu(char *version); +void compute_main_menu(struct s_hdt_menu *hdt_menu, + struct s_hardware *hardware); +void compute_submenus(struct s_hdt_menu *hdt_menu, struct s_hardware *hardware); +void detect_hardware(struct s_hardware *hardware); +#endif diff --git a/com32/hdt/hdt.c b/com32/hdt/hdt.c new file mode 100644 index 0000000..a2b9b53 --- /dev/null +++ b/com32/hdt/hdt.c @@ -0,0 +1,83 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +/* + * hdt.c + * + * An Hardware Detection Tool + */ + +#include +#include +#include +#include "hdt.h" +#include "hdt-menu.h" +#include "hdt-cli.h" +#include "hdt-common.h" + +int display_line_nb = 0; + +int main(const int argc, const char *argv[]) +{ + char version_string[256]; + const char *arg; + struct s_hardware hardware; + + snprintf(version_string, sizeof version_string, "%s %s by %s", + PRODUCT_NAME,VERSION,AUTHOR); + + console_ansi_raw(); + + /* Cleaning structures */ + init_hardware(&hardware); + + /* Detecting Syslinux version */ + detect_syslinux(&hardware); + + /* Detecting parameters */ + detect_parameters(argc, argv, &hardware); + + /* Opening the Syslinux console */ +// openconsole(&dev_stdcon_r, &dev_ansicon_w); + + clear_screen(); + printf("%s\n", version_string); + + if ((arg = find_argument(argv + 1, "nomenu"))) + start_cli_mode(&hardware); + else { + int return_code = start_menu_mode(&hardware, version_string); + + if (return_code == HDT_RETURN_TO_CLI) + start_cli_mode(&hardware); + else + return return_code; + } + + return 0; +} diff --git a/com32/hdt/hdt.c32 b/com32/hdt/hdt.c32 new file mode 100755 index 0000000000000000000000000000000000000000..3ea9e16f4ff9d4acaa6e17192920b0ae731ceccd GIT binary patch literal 107552 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 A-v9sr literal 0 HcmV?d00001 diff --git a/com32/hdt/hdt.h b/com32/hdt/hdt.h new file mode 100644 index 0000000..dfcb436 --- /dev/null +++ b/com32/hdt/hdt.h @@ -0,0 +1,44 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#ifndef DEFINE_HDT_H +#define DEFINE_HDT_H + +#define PRODUCT_NAME "Hardware Detection Tool" +#define AUTHOR "Erwan Velu" +#define CONTACT "hdt@zytor.com" +#define VERSION "0.3.1" +#define NB_CONTRIBUTORS 2 +#define CONTRIBUTORS {"Pierre-Alexandre Meyer", "Sebastien Gonzalve"} + +#define ATTR_PACKED __attribute__((packed)) + +#define WITH_PCI 1 +#define WITH_MENU_DISPLAY 1 + +#endif diff --git a/com32/hdt/lib-ansi.c b/com32/hdt/lib-ansi.c new file mode 100644 index 0000000..12e5ecc --- /dev/null +++ b/com32/hdt/lib-ansi.c @@ -0,0 +1,97 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + * Ansi Sequences can be found here : + * http://ascii-table.com/ansi-escape-sequences-vt-100.php + * http://en.wikipedia.org/wiki/ANSI_escape_code + */ + +#include +#include +#include +#include + +void display_cursor(bool status) +{ + if (status == true) { + fputs("\033[?25h", stdout); + } else { + fputs("\033[?25l", stdout); + } +} + +void clear_end_of_line() { + fputs("\033[0K", stdout); +} + +void move_cursor_left(int count) { + char buffer[10]; + memset(buffer,0,sizeof(buffer)); + sprintf(buffer,"\033[%dD",count); + fputs(buffer, stdout); +} + +void move_cursor_right(int count) { + char buffer[10]; + memset(buffer,0,sizeof(buffer)); + sprintf(buffer,"\033[%dC",count); + fputs(buffer, stdout); +} + +void clear_line() { + fputs("\033[2K", stdout); +} + +void clear_beginning_of_line() { + fputs("\033[1K", stdout); +} + +void move_cursor_to_column(int count) { + char buffer[10]; + memset(buffer,0,sizeof(buffer)); + sprintf(buffer,"\033[%dG",count); + fputs(buffer, stdout); +} + +void move_cursor_to_next_line() { + fputs("\033e", stdout); +} + +void disable_utf8() { + fputs("\033%@", stdout); +} + +void set_g1_special_char(){ + fputs("\033)0", stdout); +} + +void set_us_g0_charset() { + fputs("\033(B\1#0", stdout); +} + +void clear_entire_screen() { + fputs("\033[2J", stdout); +} diff --git a/com32/hdt/lib-ansi.h b/com32/hdt/lib-ansi.h new file mode 100644 index 0000000..2a17766 --- /dev/null +++ b/com32/hdt/lib-ansi.h @@ -0,0 +1,43 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#ifndef DEFINE_LIB_ANSI_H +#define DEFINE_LIB_ANSI_H +void display_cursor(bool status); +void clear_end_of_line(); +void move_cursor_left(int count); +void move_cursor_right(int count); +void clear_line(); +void clear_beginning_of_line(); +void move_cursor_to_column(int count); +void move_cursor_to_next_line(); +void disable_utf8(); +void set_g1_special_char(); +void set_us_g0_charset(); +void clear_entire_screen(); +#endif diff --git a/com32/include/alloca.h b/com32/include/alloca.h new file mode 100644 index 0000000..91ef4c0 --- /dev/null +++ b/com32/include/alloca.h @@ -0,0 +1,12 @@ +/* + * alloca.h + * + * Just call the builtin alloca() function + */ + +#ifndef _ALLOCA_H +#define _ALLOCA_H + +#define alloca(size) __builtin_alloca(size) + +#endif /* _ALLOCA_H */ diff --git a/com32/include/assert.h b/com32/include/assert.h new file mode 100644 index 0000000..b303bb9 --- /dev/null +++ b/com32/include/assert.h @@ -0,0 +1,7 @@ +#ifndef _ASSERT_H +#define _ASSERT_H + +/* Assert not currently supported */ +#define assert(X) ((void)0) + +#endif /* _ASSERT_H */ diff --git a/com32/include/bitsize/limits.h b/com32/include/bitsize/limits.h new file mode 100644 index 0000000..f90e524 --- /dev/null +++ b/com32/include/bitsize/limits.h @@ -0,0 +1,14 @@ +/* + * bits32/limits.h + */ + +#ifndef _BITSIZE_LIMITS_H +#define _BITSIZE_LIMITS_H + +#define LONG_BIT 32 + +#define LONG_MIN (-2147483647L-1) +#define LONG_MAX 2147483647L +#define ULONG_MAX 4294967295UL + +#endif /* _BITSIZE_LIMITS_H */ diff --git a/com32/include/bitsize/stddef.h b/com32/include/bitsize/stddef.h new file mode 100644 index 0000000..c486041 --- /dev/null +++ b/com32/include/bitsize/stddef.h @@ -0,0 +1,18 @@ +/* + * bits32/stddef.h + */ + +#ifndef _BITSIZE_STDDEF_H +#define _BITSIZE_STDDEF_H + +#define _SIZE_T +#if defined(__s390__) || defined(__hppa__) || defined(__cris__) +typedef unsigned long size_t; +#else +typedef unsigned int size_t; +#endif + +#define _PTRDIFF_T +typedef signed int ptrdiff_t; + +#endif /* _BITSIZE_STDDEF_H */ diff --git a/com32/include/bitsize/stdint.h b/com32/include/bitsize/stdint.h new file mode 100644 index 0000000..bdfe132 --- /dev/null +++ b/com32/include/bitsize/stdint.h @@ -0,0 +1,34 @@ +/* + * bits32/stdint.h + */ + +#ifndef _BITSIZE_STDINT_H +#define _BITSIZE_STDINT_H + +typedef signed char int8_t; +typedef short int int16_t; +typedef int int32_t; +typedef long long int int64_t; + +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; + +typedef int int_fast16_t; +typedef int int_fast32_t; + +typedef unsigned int uint_fast16_t; +typedef unsigned int uint_fast32_t; + +typedef int intptr_t; +typedef unsigned int uintptr_t; + +#define __INT64_C(c) c ## LL +#define __UINT64_C(c) c ## ULL + +#define __PRI64_RANK "ll" +#define __PRIFAST_RANK "" +#define __PRIPTR_RANK "" + +#endif /* _BITSIZE_STDINT_H */ diff --git a/com32/include/bitsize/stdintconst.h b/com32/include/bitsize/stdintconst.h new file mode 100644 index 0000000..8157dd0 --- /dev/null +++ b/com32/include/bitsize/stdintconst.h @@ -0,0 +1,18 @@ +/* + * bits32/stdintconst.h + */ + +#ifndef _BITSIZE_STDINTCONST_H +#define _BITSIZE_STDINTCONST_H + +#define INT_FAST16_C(c) INT32_C(c) +#define INT_FAST32_C(c) INT32_C(c) + +#define UINT_FAST16_C(c) UINT32_C(c) +#define UINT_FAST32_C(c) UINT32_C(c) + +#define INTPTR_C(c) INT32_C(c) +#define UINTPTR_C(c) UINT32_C(c) +#define PTRDIFF_C(c) INT32_C(c) + +#endif /* _BITSIZE_STDINTCONST_H */ diff --git a/com32/include/bitsize/stdintlimits.h b/com32/include/bitsize/stdintlimits.h new file mode 100644 index 0000000..b44fe01 --- /dev/null +++ b/com32/include/bitsize/stdintlimits.h @@ -0,0 +1,22 @@ +/* + * bits32/stdintlimits.h + */ + +#ifndef _BITSIZE_STDINTLIMITS_H +#define _BITSIZE_STDINTLIMITS_H + +#define INT_FAST16_MIN INT32_MIN +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST16_MAX INT32_MAX +#define INT_FAST32_MAX INT32_MAX +#define UINT_FAST16_MAX UINT32_MAX +#define UINT_FAST32_MAX UINT32_MAX + +#define INTPTR_MIN INT32_MIN +#define INTPTR_MAX INT32_MAX +#define UINTPTR_MAX UINT32_MAX + +#define PTRDIFF_MIN INT32_MIN +#define PTRDIFF_MAX INT32_MAX + +#endif /* _BITSIZE_STDINTLIMITS_H */ diff --git a/com32/include/colortbl.h b/com32/include/colortbl.h new file mode 100644 index 0000000..fb5196c --- /dev/null +++ b/com32/include/colortbl.h @@ -0,0 +1,53 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#ifndef _COLORTBL_H +#define _COLORTBL_H + +/* Attribute/color table used by ansicon and vesacon to abstract + out the color selection. */ + +/* Note: vesacon relies on the encoding of these numbers */ +enum color_table_shadow { + SHADOW_NONE = 0, + SHADOW_ALL = 1, + SHADOW_NORMAL = 2, + SHADOW_REVERSE = 3, +}; + +struct color_table { + const char *name; /* Attribute name (used for customization) */ + const char *ansi; /* ANSI attribute */ + unsigned int argb_fg; /* ARGB for foreground */ + unsigned int argb_bg; /* ARGB for background */ + enum color_table_shadow shadow; /* Shadow mode */ +}; + +extern struct color_table *console_color_table; +extern int console_color_table_size; + +#endif /* _COLORTBL_H */ diff --git a/com32/include/com32.h b/com32/include/com32.h new file mode 100644 index 0000000..da2edfb --- /dev/null +++ b/com32/include/com32.h @@ -0,0 +1,175 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2002-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * com32.h + * + * Common declarations for com32 programs. + */ + +#ifndef _COM32_H +#define _COM32_H + +#include +#include /* For __cdecl */ + +/* + * This structure defines the register frame used by the + * system call interface. + * + * The syscall interface is: + * + * __intcall(interrupt_#, source_regs, return_regs) + * __farcall(seg, offs, source_regs, return_regs) + */ +typedef union { + uint32_t l; + uint16_t w[2]; + uint8_t b[4]; +} reg32_t; + +typedef struct { + uint16_t gs; /* Offset 0 */ + uint16_t fs; /* Offset 2 */ + uint16_t es; /* Offset 4 */ + uint16_t ds; /* Offset 6 */ + + reg32_t edi; /* Offset 8 */ + reg32_t esi; /* Offset 12 */ + reg32_t ebp; /* Offset 16 */ + reg32_t _unused_esp; /* Offset 20 */ + reg32_t ebx; /* Offset 24 */ + reg32_t edx; /* Offset 28 */ + reg32_t ecx; /* Offset 32 */ + reg32_t eax; /* Offset 36 */ + + reg32_t eflags; /* Offset 40 */ +} com32sys_t; + +/* EFLAGS definitions */ +#define EFLAGS_CF 0x00000001 +#define EFLAGS_PF 0x00000004 +#define EFLAGS_AF 0x00000010 +#define EFLAGS_ZF 0x00000040 +#define EFLAGS_SF 0x00000080 +#define EFLAGS_TF 0x00000100 +#define EFLAGS_IF 0x00000200 +#define EFLAGS_DF 0x00000400 +#define EFLAGS_OF 0x00000800 +#define EFLAGS_IOPL 0x00003000 +#define EFLAGS_NT 0x00004000 +#define EFLAGS_RF 0x00010000 +#define EFLAGS_VM 0x00020000 +#define EFLAGS_AC 0x00040000 +#define EFLAGS_VIF 0x00080000 +#define EFLAGS_VIP 0x00100000 +#define EFLAGS_ID 0x00200000 + +extern struct com32_sys_args { + uint32_t cs_sysargs; + char *cs_cmdline; + void __cdecl (*cs_intcall)(uint8_t, const com32sys_t *, com32sys_t *); + void *cs_bounce; + uint32_t cs_bounce_size; + void __cdecl (*cs_farcall)(uint32_t, const com32sys_t *, com32sys_t *); + int __cdecl (*cs_cfarcall)(uint32_t, const void *, uint32_t); + uint32_t cs_memsize; +} __com32; + +/* + * System call wrapper functions + */ +void __intcall(uint8_t __i, const com32sys_t *__sr, com32sys_t *__dr); +void __farcall(uint16_t __cs, uint16_t __ip, + const com32sys_t *__sr, com32sys_t *__dr); +int __cfarcall(uint16_t __cs, uint16_t __ip, + const void *__stack, uint32_t __stack_size); +extern const com32sys_t __com32_zero_regs; + +/* + * These functions convert between linear pointers in the range + * 0..0xFFFFF and real-mode style SEG:OFFS pointers. Note that a + * 32-bit linear pointer is not compatible with a SEG:OFFS pointer + * stored in two consecutive 16-bit words. + * + * Use OFFS_WRT() if you want to compute an offset relative to a + * specific segment. OFFS_VALID() will return whether or not the + * pointer is actually reachable from the target segment. + */ +static inline uint16_t SEG(const volatile void *__p) +{ + return (uint16_t)(((uintptr_t)__p) >> 4); +} + +static inline uint16_t OFFS(const volatile void *__p) +{ + /* The double cast here is to shut up gcc */ + return (uint16_t)(uintptr_t)__p & 0x000F; +} + +static inline uint16_t OFFS_WRT(const volatile void *__p, uint16_t seg) +{ + return (uint16_t)((uintptr_t)__p - ((uintptr_t)seg << 4)); +} + +static inline int OFFS_VALID(const volatile void *__p, uint16_t seg) +{ + uintptr_t __segstart = (uintptr_t)seg << 4; + uintptr_t __ptr = (uintptr_t)__p; + + return (__ptr >= __segstart) && (__ptr <= __segstart+0xffff); +} + +static inline void *MK_PTR(uint16_t __seg, uint16_t __offs) +{ + return (void *)((__seg << 4) + __offs); +} + +/* Some tools to handle 16:16 far pointers in memory */ + +struct __far_ptr { + uint32_t __ptr; +} __attribute__((packed)); + +typedef struct __far_ptr far_ptr_t; + + +static inline void *GET_PTR(far_ptr_t __fptr) +{ + return MK_PTR(__fptr.__ptr >> 16, __fptr.__ptr); +} + +static inline far_ptr_t FAR_PTR(void *__ptr) +{ + far_ptr_t __fptr; + + __fptr.__ptr = (SEG(__ptr) << 16) + OFFS(__ptr); + return __fptr; +} + +#endif /* _COM32_H */ diff --git a/com32/include/console.h b/com32/include/console.h new file mode 100644 index 0000000..973aedd --- /dev/null +++ b/com32/include/console.h @@ -0,0 +1,63 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * console.h + * + * Alternative consoles + */ + +#ifndef _CONSOLE_H +#define _CONSOLE_H + +#include +#include + +__extern int openconsole(const struct input_dev *, const struct output_dev *); + +/* Standard line-oriented console */ +extern const struct input_dev dev_stdcon_r; +extern const struct output_dev dev_stdcon_w; +/* Raw character-oriented console */ +extern const struct input_dev dev_rawcon_r; +extern const struct output_dev dev_rawcon_w; + +/* These are output-only consoles; combine with one of the input methods */ + +/* Serial port only */ +extern const struct output_dev dev_serial_w; +/* ANSI console (output only; combine with one of the input methods) */ +extern const struct output_dev dev_ansicon_w; +/* ANSI plus serial port */ +extern const struct output_dev dev_ansiserial_w; + +/* VESA graphics console (output only) */ +extern const struct output_dev dev_vesacon_w; +/* VESA plus serial port */ +extern const struct output_dev dev_vesaserial_w; + +#endif /* _CONSOLE_H */ diff --git a/com32/include/cpufeature.h b/com32/include/cpufeature.h new file mode 100644 index 0000000..df1d1a1 --- /dev/null +++ b/com32/include/cpufeature.h @@ -0,0 +1,98 @@ +/* + * cpufeature.h + * + * Defines x86 CPU feature bits + */ + +#ifndef __ASM_I386_CPUFEATURE_H +#define __ASM_I386_CPUFEATURE_H + +#define NCAPINTS 7 /* N 32-bit words worth of info */ + +/* Intel-defined CPU features, CPUID level 0x00000001 (edx), 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_MP (1*32+19) /* MP Capable. */ +#define X86_FEATURE_NX (1*32+20) /* Execute Disable */ +#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) */ +/* cpu types for specific tunings: */ +#define X86_FEATURE_K8 (3*32+ 4) /* Opteron, Athlon64 */ +#define X86_FEATURE_K7 (3*32+ 5) /* Athlon */ +#define X86_FEATURE_P3 (3*32+ 6) /* P3 */ +#define X86_FEATURE_P4 (3*32+ 7) /* P4 */ + +/* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */ +#define X86_FEATURE_XMM3 (4*32+ 0) /* Streaming SIMD Extensions-3 */ +#define X86_FEATURE_MWAIT (4*32+ 3) /* Monitor/Mwait support */ +#define X86_FEATURE_DSCPL (4*32+ 4) /* CPL Qualified Debug Store */ +#define X86_FEATURE_EST (4*32+ 7) /* Enhanced SpeedStep */ +#define X86_FEATURE_TM2 (4*32+ 8) /* Thermal Monitor 2 */ +#define X86_FEATURE_CID (4*32+10) /* Context ID */ +#define X86_FEATURE_CX16 (4*32+13) /* CMPXCHG16B */ +#define X86_FEATURE_XTPR (4*32+14) /* Send Task Priority Messages */ + +/* VIA/Cyrix/Centaur-defined CPU features, CPUID level 0xC0000001, word 5 */ +#define X86_FEATURE_XSTORE (5*32+ 2) /* on-CPU RNG present (xstore insn) */ +#define X86_FEATURE_XSTORE_EN (5*32+ 3) /* on-CPU RNG enabled */ +#define X86_FEATURE_XCRYPT (5*32+ 6) /* on-CPU crypto (xcrypt insn) */ +#define X86_FEATURE_XCRYPT_EN (5*32+ 7) /* on-CPU crypto enabled */ + +/* More extended AMD flags: CPUID level 0x80000001, ecx, word 6 */ +#define X86_FEATURE_LAHF_LM (6*32+ 0) /* LAHF/SAHF in long mode */ +#define X86_FEATURE_CMP_LEGACY (6*32+ 1) /* If yes HyperThreading not valid */ + +#endif /* __ASM_I386_CPUFEATURE_H */ + +/* + * Local Variables: + * mode:c + * comment-column:42 + * End: + */ diff --git a/com32/include/ctype.h b/com32/include/ctype.h new file mode 100644 index 0000000..89b6e6d --- /dev/null +++ b/com32/include/ctype.h @@ -0,0 +1,119 @@ +/* + * ctype.h + * + * This assumes ISO 8859-1, being a reasonable superset of ASCII. + */ + +#ifndef _CTYPE_H +#define _CTYPE_H + +#ifndef __CTYPE_NO_INLINE +# define __ctype_inline static __inline__ +#else +# define __ctype_inline +#endif + +/* + * This relies on the following definitions: + * + * cntrl = !print + * alpha = upper|lower + * graph = punct|alpha|digit + * blank = '\t' || ' ' (per POSIX requirement) + */ +enum { + __ctype_upper = (1 << 0), + __ctype_lower = (1 << 1), + __ctype_digit = (1 << 2), + __ctype_xdigit = (1 << 3), + __ctype_space = (1 << 4), + __ctype_print = (1 << 5), + __ctype_punct = (1 << 6), + __ctype_cntrl = (1 << 7), +}; + +extern const unsigned char __ctypes[]; + +__ctype_inline int isalnum(int __c) +{ + return __ctypes[__c+1] & + (__ctype_upper|__ctype_lower|__ctype_digit); +} + +__ctype_inline int isalpha(int __c) +{ + return __ctypes[__c+1] & + (__ctype_upper|__ctype_lower); +} + +__ctype_inline int isascii(int __c) +{ + return !(__c & ~0x7f); +} + +__ctype_inline int isblank(int __c) +{ + return (__c == '\t') || (__c == ' '); +} + +__ctype_inline int iscntrl(int __c) +{ + return __ctypes[__c+1] & __ctype_cntrl; +} + +__ctype_inline int isdigit(int __c) +{ + return ((unsigned)__c - '0') <= 9; +} + +__ctype_inline int isgraph(int __c) +{ + return __ctypes[__c+1] & + (__ctype_upper|__ctype_lower|__ctype_digit|__ctype_punct); +} + +__ctype_inline int islower(int __c) +{ + return __ctypes[__c+1] & __ctype_lower; +} + +__ctype_inline int isprint(int __c) +{ + return __ctypes[__c+1] & __ctype_print; +} + +__ctype_inline int ispunct(int __c) +{ + return __ctypes[__c+1] & __ctype_punct; +} + +__ctype_inline int isspace(int __c) +{ + return __ctypes[__c+1] & __ctype_space; +} + +__ctype_inline int isupper(int __c) +{ + return __ctypes[__c+1] & __ctype_upper; +} + +__ctype_inline int isxdigit(int __c) +{ + return __ctypes[__c+1] & __ctype_xdigit; +} + +/* Note: this is decimal, not hex, to avoid accidental promotion to unsigned */ +#define _toupper(__c) ((__c) & ~32) +#define _tolower(__c) ((__c) | 32) + +__ctype_inline int toupper(int __c) +{ + return islower(__c) ? _toupper(__c) : __c; +} + +__ctype_inline int tolower(int __c) +{ + return isupper(__c) ? _tolower(__c) : __c; +} + +#endif /* _CTYPE_H */ diff --git a/com32/include/dev.h b/com32/include/dev.h new file mode 100644 index 0000000..54198e8 --- /dev/null +++ b/com32/include/dev.h @@ -0,0 +1,55 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * console.h + * + * Alternative consoles + */ + +#ifndef _DEV_H +#define _DEV_H + +#include +#include + +struct input_dev; +struct output_dev; + +__extern int opendev(const struct input_dev *, const struct output_dev *, int); + +/* Common generic devices */ + +/* Null device */ +extern const struct input_dev dev_null_r; +extern const struct output_dev dev_null_w; + +/* Error device */ +extern const struct input_dev dev_error_r; +extern const struct output_dev dev_error_w; + +#endif /* _DEV_H */ diff --git a/com32/include/dirent.h b/com32/include/dirent.h new file mode 100644 index 0000000..956b911 --- /dev/null +++ b/com32/include/dirent.h @@ -0,0 +1,36 @@ +/* + * dirent.h + */ + +#ifndef _DIRENT_H +#define _DIRENT_H + +#include +#include +#include +#include + +#ifndef NAME_MAX +#define NAME_MAX 255 +#endif + +struct dirent { + long d_ino; /* Inode/File number */ + off_t d_size; /* Size of file */ + mode_t d_mode; /* Type of file */ + char d_name[NAME_MAX + 1]; +}; + +typedef struct { + short dd_stat; /* status return from last lookup */ + uint16_t dd_fd; + size_t dd_sect; + char dd_name[NAME_MAX + 1]; /* directory */ +} DIR; + +__extern DIR *opendir(const char *); +__extern struct dirent *readdir(DIR *); +__extern int closedir(DIR *); +__extern DIR *fdopendir(int); + +#endif /* Not _DIRENT_H */ diff --git a/com32/include/elf.h b/com32/include/elf.h new file mode 100644 index 0000000..c543a81 --- /dev/null +++ b/com32/include/elf.h @@ -0,0 +1,11 @@ +/* + * elf.h + */ + +#ifndef _ELF_H +#define _ELF_H + +#include +#include + +#endif /* _ELF_H */ diff --git a/com32/include/endian.h b/com32/include/endian.h new file mode 100644 index 0000000..a6cd6d9 --- /dev/null +++ b/com32/include/endian.h @@ -0,0 +1,15 @@ +/* + * endian.h + */ + +#ifndef _ENDIAN_H +#define _ENDIAN_H + +#include + +#define LITTLE_ENDIAN __LITTLE_ENDIAN +#define BIG_ENDIAN __BIG_ENDIAN +#define PDP_ENDIAN __PDP_ENDIAN +#define BYTE_ORDER __BYTE_ORDER + +#endif /* _ENDIAN_H */ diff --git a/com32/include/errno.h b/com32/include/errno.h new file mode 100644 index 0000000..36690bf --- /dev/null +++ b/com32/include/errno.h @@ -0,0 +1,134 @@ +#ifndef _ERRNO_H +#define _ERRNO_H + +extern int errno; + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Arg list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device or resource busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math argument out of domain of func */ +#define ERANGE 34 /* Math result not representable */ +#define EDEADLK 35 /* Resource deadlock would occur */ +#define ENAMETOOLONG 36 /* File name too long */ +#define ENOLCK 37 /* No record locks available */ +#define ENOSYS 38 /* Function not implemented */ +#define ENOTEMPTY 39 /* Directory not empty */ +#define ELOOP 40 /* Too many symbolic links encountered */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define ENOMSG 42 /* No message of desired type */ +#define EIDRM 43 /* Identifier removed */ +#define ECHRNG 44 /* Channel number out of range */ +#define EL2NSYNC 45 /* Level 2 not synchronized */ +#define EL3HLT 46 /* Level 3 halted */ +#define EL3RST 47 /* Level 3 reset */ +#define ELNRNG 48 /* Link number out of range */ +#define EUNATCH 49 /* Protocol driver not attached */ +#define ENOCSI 50 /* No CSI structure available */ +#define EL2HLT 51 /* Level 2 halted */ +#define EBADE 52 /* Invalid exchange */ +#define EBADR 53 /* Invalid request descriptor */ +#define EXFULL 54 /* Exchange full */ +#define ENOANO 55 /* No anode */ +#define EBADRQC 56 /* Invalid request code */ +#define EBADSLT 57 /* Invalid slot */ + +#define EDEADLOCK EDEADLK + +#define EBFONT 59 /* Bad font file format */ +#define ENOSTR 60 /* Device not a stream */ +#define ENODATA 61 /* No data available */ +#define ETIME 62 /* Timer expired */ +#define ENOSR 63 /* Out of streams resources */ +#define ENONET 64 /* Machine is not on the network */ +#define ENOPKG 65 /* Package not installed */ +#define EREMOTE 66 /* Object is remote */ +#define ENOLINK 67 /* Link has been severed */ +#define EADV 68 /* Advertise error */ +#define ESRMNT 69 /* Srmount error */ +#define ECOMM 70 /* Communication error on send */ +#define EPROTO 71 /* Protocol error */ +#define EMULTIHOP 72 /* Multihop attempted */ +#define EDOTDOT 73 /* RFS specific error */ +#define EBADMSG 74 /* Not a data message */ +#define EOVERFLOW 75 /* Value too large for defined data type */ +#define ENOTUNIQ 76 /* Name not unique on network */ +#define EBADFD 77 /* File descriptor in bad state */ +#define EREMCHG 78 /* Remote address changed */ +#define ELIBACC 79 /* Can not access a needed shared library */ +#define ELIBBAD 80 /* Accessing a corrupted shared library */ +#define ELIBSCN 81 /* .lib section in a.out corrupted */ +#define ELIBMAX 82 /* Attempting to link in too many shared libraries */ +#define ELIBEXEC 83 /* Cannot exec a shared library directly */ +#define EILSEQ 84 /* Illegal byte sequence */ +#define ERESTART 85 /* Interrupted system call should be restarted */ +#define ESTRPIPE 86 /* Streams pipe error */ +#define EUSERS 87 /* Too many users */ +#define ENOTSOCK 88 /* Socket operation on non-socket */ +#define EDESTADDRREQ 89 /* Destination address required */ +#define EMSGSIZE 90 /* Message too long */ +#define EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 92 /* Protocol not available */ +#define EPROTONOSUPPORT 93 /* Protocol not supported */ +#define ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define EPFNOSUPPORT 96 /* Protocol family not supported */ +#define EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define EADDRINUSE 98 /* Address already in use */ +#define EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ENETDOWN 100 /* Network is down */ +#define ENETUNREACH 101 /* Network is unreachable */ +#define ENETRESET 102 /* Network dropped connection because of reset */ +#define ECONNABORTED 103 /* Software caused connection abort */ +#define ECONNRESET 104 /* Connection reset by peer */ +#define ENOBUFS 105 /* No buffer space available */ +#define EISCONN 106 /* Transport endpoint is already connected */ +#define ENOTCONN 107 /* Transport endpoint is not connected */ +#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ETIMEDOUT 110 /* Connection timed out */ +#define ECONNREFUSED 111 /* Connection refused */ +#define EHOSTDOWN 112 /* Host is down */ +#define EHOSTUNREACH 113 /* No route to host */ +#define EALREADY 114 /* Operation already in progress */ +#define EINPROGRESS 115 /* Operation now in progress */ +#define ESTALE 116 /* Stale NFS file handle */ +#define EUCLEAN 117 /* Structure needs cleaning */ +#define ENOTNAM 118 /* Not a XENIX named type file */ +#define ENAVAIL 119 /* No XENIX semaphores available */ +#define EISNAM 120 /* Is a named type file */ +#define EREMOTEIO 121 /* Remote I/O error */ +#define EDQUOT 122 /* Quota exceeded */ + +#define ENOMEDIUM 123 /* No medium found */ +#define EMEDIUMTYPE 124 /* Wrong medium type */ + +#endif /* _ERRNO_H */ diff --git a/com32/include/fcntl.h b/com32/include/fcntl.h new file mode 100644 index 0000000..b691b5c --- /dev/null +++ b/com32/include/fcntl.h @@ -0,0 +1,24 @@ +/* + * fcntl.h + */ + +#ifndef _FCNTL_H +#define _FCNTL_H + +#include +#include +#include + +/* None of these are actually supported, although O_RDONLY works */ +/* Note this is different from the classical Unix way of doing it */ +#define O_RDONLY 1 +#define O_WRONLY 2 +#define O_RDWR 3 +#define O_CREAT 0100 +#define O_EXCL 0200 +#define O_TRUNC 01000 +#define O_APPEND 02000 + +__extern int open(const char *, int, ...); + +#endif /* _FCNTL_H */ diff --git a/com32/include/inttypes.h b/com32/include/inttypes.h new file mode 100644 index 0000000..e00fa63 --- /dev/null +++ b/com32/include/inttypes.h @@ -0,0 +1,226 @@ +/* + * inttypes.h + */ + +#ifndef _INTTYPES_H +#define _INTTYPES_H + +#include +#include +#include + +static __inline__ intmax_t imaxabs(intmax_t __n) +{ + return (__n < (intmax_t)0) ? -__n : __n; +} + +__extern intmax_t strtoimax(const char *, char **, int); +__extern uintmax_t strtoumax(const char *, char **, int); + +/* extensions */ +__extern intmax_t strntoimax(const char *, char **, int, size_t); +__extern uintmax_t strntoumax(const char *, char **, int, size_t); + +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) + +#define PRId8 "d" +#define PRId16 "d" +#define PRId32 "d" +#define PRId64 __PRI64_RANK "d" + +#define PRIdLEAST8 "d" +#define PRIdLEAST16 "d" +#define PRIdLEAST32 "d" +#define PRIdLEAST64 __PRI64_RANK "d" + +#define PRIdFAST8 "d" +#define PRIdFAST16 __PRIFAST_RANK "d" +#define PRIdFAST32 __PRIFAST_RANK "d" +#define PRIdFAST64 __PRI64_RANK "d" + +#define PRIdMAX __PRI64_RANK "d" +#define PRIdPTR __PRIPTR_RANK "d" + +#define PRIi8 "i" +#define PRIi16 "i" +#define PRIi32 "i" +#define PRIi64 __PRI64_RANK "i" + +#define PRIiLEAST8 "i" +#define PRIiLEAST16 "i" +#define PRIiLEAST32 "i" +#define PRIiLEAST64 __PRI64_RANK "i" + +#define PRIiFAST8 "i" +#define PRIiFAST16 __PRIFAST_RANK "i" +#define PRIiFAST32 __PRIFAST_RANK "i" +#define PRIiFAST64 __PRI64_RANK "i" + +#define PRIiMAX __PRI64_RANK "i" +#define PRIiPTR __PRIPTR_RANK "i" + +#define PRIo8 "o" +#define PRIo16 "o" +#define PRIo32 "o" +#define PRIo64 __PRI64_RANK "o" + +#define PRIoLEAST8 "o" +#define PRIoLEAST16 "o" +#define PRIoLEAST32 "o" +#define PRIoLEAST64 __PRI64_RANK "o" + +#define PRIoFAST8 "o" +#define PRIoFAST16 __PRIFAST_RANK "o" +#define PRIoFAST32 __PRIFAST_RANK "o" +#define PRIoFAST64 __PRI64_RANK "o" + +#define PRIoMAX __PRI64_RANK "o" +#define PRIoPTR __PRIPTR_RANK "o" + +#define PRIu8 "u" +#define PRIu16 "u" +#define PRIu32 "u" +#define PRIu64 __PRI64_RANK "u" + +#define PRIuLEAST8 "u" +#define PRIuLEAST16 "u" +#define PRIuLEAST32 "u" +#define PRIuLEAST64 __PRI64_RANK "u" + +#define PRIuFAST8 "u" +#define PRIuFAST16 __PRIFAST_RANK "u" +#define PRIuFAST32 __PRIFAST_RANK "u" +#define PRIuFAST64 __PRI64_RANK "u" + +#define PRIuMAX __PRI64_RANK "u" +#define PRIuPTR __PRIPTR_RANK "u" + +#define PRIx8 "x" +#define PRIx16 "x" +#define PRIx32 "x" +#define PRIx64 __PRI64_RANK "x" + +#define PRIxLEAST8 "x" +#define PRIxLEAST16 "x" +#define PRIxLEAST32 "x" +#define PRIxLEAST64 __PRI64_RANK "x" + +#define PRIxFAST8 "x" +#define PRIxFAST16 __PRIFAST_RANK "x" +#define PRIxFAST32 __PRIFAST_RANK "x" +#define PRIxFAST64 __PRI64_RANK "x" + +#define PRIxMAX __PRI64_RANK "x" +#define PRIxPTR __PRIPTR_RANK "x" + +#define PRIX8 "X" +#define PRIX16 "X" +#define PRIX32 "X" +#define PRIX64 __PRI64_RANK "X" + +#define PRIXLEAST8 "X" +#define PRIXLEAST16 "X" +#define PRIXLEAST32 "X" +#define PRIXLEAST64 __PRI64_RANK "X" + +#define PRIXFAST8 "X" +#define PRIXFAST16 __PRIFAST_RANK "X" +#define PRIXFAST32 __PRIFAST_RANK "X" +#define PRIXFAST64 __PRI64_RANK "X" + +#define PRIXMAX __PRI64_RANK "X" +#define PRIXPTR __PRIPTR_RANK "X" + +#define SCNd8 "hhd" +#define SCNd16 "hd" +#define SCNd32 "d" +#define SCNd64 __PRI64_RANK "d" + +#define SCNdLEAST8 "hhd" +#define SCNdLEAST16 "hd" +#define SCNdLEAST32 "d" +#define SCNdLEAST64 __PRI64_RANK "d" + +#define SCNdFAST8 "hhd" +#define SCNdFAST16 __PRIFAST_RANK "d" +#define SCNdFAST32 __PRIFAST_RANK "d" +#define SCNdFAST64 __PRI64_RANK "d" + +#define SCNdMAX __PRI64_RANK "d" +#define SCNdPTR __PRIPTR_RANK "d" + +#define SCNi8 "hhi" +#define SCNi16 "hi" +#define SCNi32 "i" +#define SCNi64 __PRI64_RANK "i" + +#define SCNiLEAST8 "hhi" +#define SCNiLEAST16 "hi" +#define SCNiLEAST32 "i" +#define SCNiLEAST64 __PRI64_RANK "i" + +#define SCNiFAST8 "hhi" +#define SCNiFAST16 __PRIFAST_RANK "i" +#define SCNiFAST32 __PRIFAST_RANK "i" +#define SCNiFAST64 __PRI64_RANK "i" + +#define SCNiMAX __PRI64_RANK "i" +#define SCNiPTR __PRIPTR_RANK "i" + +#define SCNo8 "hho" +#define SCNo16 "ho" +#define SCNo32 "o" +#define SCNo64 __PRI64_RANK "o" + +#define SCNoLEAST8 "hho" +#define SCNoLEAST16 "ho" +#define SCNoLEAST32 "o" +#define SCNoLEAST64 __PRI64_RANK "o" + +#define SCNoFAST8 "hho" +#define SCNoFAST16 __PRIFAST_RANK "o" +#define SCNoFAST32 __PRIFAST_RANK "o" +#define SCNoFAST64 __PRI64_RANK "o" + +#define SCNoMAX __PRI64_RANK "o" +#define SCNoPTR __PRIPTR_RANK "o" + +#define SCNu8 "hhu" +#define SCNu16 "hu" +#define SCNu32 "u" +#define SCNu64 __PRI64_RANK "u" + +#define SCNuLEAST8 "hhu" +#define SCNuLEAST16 "hu" +#define SCNuLEAST32 "u" +#define SCNuLEAST64 __PRI64_RANK "u" + +#define SCNuFAST8 "hhu" +#define SCNuFAST16 __PRIFAST_RANK "u" +#define SCNuFAST32 __PRIFAST_RANK "u" +#define SCNuFAST64 __PRI64_RANK "u" + +#define SCNuMAX __PRI64_RANK "u" +#define SCNuPTR __PRIPTR_RANK "u" + +#define SCNx8 "hhx" +#define SCNx16 "hx" +#define SCNx32 "x" +#define SCNx64 __PRI64_RANK "x" + +#define SCNxLEAST8 "hhx" +#define SCNxLEAST16 "hx" +#define SCNxLEAST32 "x" +#define SCNxLEAST64 __PRI64_RANK "x" + +#define SCNxFAST8 "hhx" +#define SCNxFAST16 __PRIFAST_RANK "x" +#define SCNxFAST32 __PRIFAST_RANK "x" +#define SCNxFAST64 __PRI64_RANK "x" + +#define SCNxMAX __PRI64_RANK "x" +#define SCNxPTR __PRIPTR_RANK "x" + +#endif + +#endif /* _INTTYPES_H */ diff --git a/com32/include/klibc/archsetjmp.h b/com32/include/klibc/archsetjmp.h new file mode 100644 index 0000000..db04314 --- /dev/null +++ b/com32/include/klibc/archsetjmp.h @@ -0,0 +1,19 @@ +/* + * arch/i386/include/klibc/archsetjmp.h + */ + +#ifndef _KLIBC_ARCHSETJMP_H +#define _KLIBC_ARCHSETJMP_H + +struct __jmp_buf { + unsigned int __ebx; + unsigned int __esp; + unsigned int __ebp; + unsigned int __esi; + unsigned int __edi; + unsigned int __eip; +}; + +typedef struct __jmp_buf jmp_buf[1]; + +#endif /* _SETJMP_H */ diff --git a/com32/include/klibc/compiler.h b/com32/include/klibc/compiler.h new file mode 100644 index 0000000..5ac2118 --- /dev/null +++ b/com32/include/klibc/compiler.h @@ -0,0 +1,129 @@ +/* + * klibc/compiler.h + * + * Various compiler features + */ + +#ifndef _KLIBC_COMPILER_H +#define _KLIBC_COMPILER_H + +#define __user + +/* Specific calling conventions */ +/* __cdecl is used when we want varadic and non-varadic functions to have + the same binary calling convention. */ +#ifdef __i386__ +# ifdef __GNUC__ +# define __cdecl __attribute__((cdecl,regparm(0))) +# else + /* Most other C compilers have __cdecl as a keyword */ +# endif +#else +# define __cdecl /* Meaningless on non-i386 */ +#endif + +/* How to declare a function that *must* be inlined */ +#ifdef __GNUC__ +# if __GNUC_MAJOR__ >= 3 +# define __must_inline static __inline__ __attribute__((always_inline)) +# else +# define __must_inline extern __inline__ +# endif +#else +# define __must_inline inline /* Just hope this works... */ +#endif + +/* How to declare a function that does not return */ +#ifdef __GNUC__ +# define __noreturn void __attribute__((noreturn)) +#else +# define __noreturn void +#endif + +/* "const" function: + + Many functions do not examine any values except their arguments, + and have no effects except the return value. Basically this is + just slightly more strict class than the `pure' attribute above, + since function is not allowed to read global memory. + + Note that a function that has pointer arguments and examines the + data pointed to must _not_ be declared `const'. Likewise, a + function that calls a non-`const' function usually must not be + `const'. It does not make sense for a `const' function to return + `void'. +*/ +#ifdef __GNUC__ +# define __constfunc __attribute__((const)) +#else +# define __constfunc +#endif +#undef __attribute_const__ +#define __attribute_const__ __constfunc + +/* "pure" function: + + Many functions have no effects except the return value and their + return value depends only on the parameters and/or global + variables. Such a function can be subject to common subexpression + elimination and loop optimization just as an arithmetic operator + would be. These functions should be declared with the attribute + `pure'. +*/ +#ifdef __GNUC__ +# define __purefunc __attribute__((pure)) +#else +# define __purefunc +#endif +#undef __attribute_pure__ +#define __attribute_pure__ __purefunc + +/* Format attribute */ +#ifdef __GNUC__ +# define __formatfunc(t,f,a) __attribute__((format(t,f,a))) +#else +# define __formatfunc(t,f,a) +#endif + +/* malloc() function (returns unaliased pointer) */ +#if defined(__GNUC__) && (__GNUC_MAJOR__ >= 3) +# define __mallocfunc __attribute__((malloc)) +#else +# define __mallocfunc +#endif + +/* likely/unlikely */ +#if defined(__GNUC__) && (__GNUC_MAJOR__ > 2 || (__GNUC_MAJOR__ == 2 && __GNUC_MINOR__ >= 95)) +# define __likely(x) __builtin_expect(!!(x), 1) +# define __unlikely(x) __builtin_expect(!!(x), 0) +#else +# define __likely(x) (!!(x)) +# define __unlikely(x) (!!(x)) +#endif + +/* Possibly unused function */ +#ifdef __GNUC__ +# define __unusedfunc __attribute__((unused)) +#else +# define __unusedfunc +#endif + +/* Constructors and destructors */ +#define __constructor __attribute__((constructor)) +#define __destructor __attribute__((destructor)) + +/* Packed structures */ +#define __packed __attribute__((packed)) + +/* Weak symbols */ +#define __weak __attribute__((weak)) + +/* Alignment */ +#define __aligned(x) __attribute__((aligned(x))) +#define __alignas(x) __attribute__((aligned(__alignof__(x)))) + +/* Handling of common (affect constructors/destructors) */ +#define __common __attribute__((common)) +#define __nocommon __attribute__((nocommon)) + +#endif diff --git a/com32/include/klibc/diverr.h b/com32/include/klibc/diverr.h new file mode 100644 index 0000000..a4bbedd --- /dev/null +++ b/com32/include/klibc/diverr.h @@ -0,0 +1,4 @@ +static inline void __divide_error(void) +{ + asm volatile("int $0"); /* Divide by zero */ +} diff --git a/com32/include/klibc/endian.h b/com32/include/klibc/endian.h new file mode 100644 index 0000000..7537a47 --- /dev/null +++ b/com32/include/klibc/endian.h @@ -0,0 +1,38 @@ +/* + * klibc/endian.h + * + * Like , but export only double-underscore symbols + */ + +#ifndef _KLIBC_ENDIAN_H +#define _KLIBC_ENDIAN_H + +#define __LITTLE_ENDIAN /* we're on i386, littleendian */ + +/* Linux' asm/byteorder.h defines either __LITTLE_ENDIAN or + __BIG_ENDIAN, but the glibc/BSD-ish macros expect both to be + defined with __BYTE_ORDER defining which is actually used... */ + +#if defined(__LITTLE_ENDIAN) +# undef __LITTLE_ENDIAN +# define __LITTLE_ENDIAN 1234 +# define __BIG_ENDIAN 4321 +# define __PDP_ENDIAN 3412 +# define __BYTE_ORDER __LITTLE_ENDIAN +#elif defined(__BIG_ENDIAN) +# undef __BIG_ENDIAN +# define __LITTLE_ENDIAN 1234 +# define __BIG_ENDIAN 4321 +# define __PDP_ENDIAN 3412 +# define __BYTE_ORDER __BIG_ENDIAN +#elif defined(__PDP_ENDIAN) +# undef __PDP_ENDIAN +# define __LITTLE_ENDIAN 1234 +# define __BIG_ENDIAN 4321 +# define __PDP_ENDIAN 3412 +# define __BYTE_ORDER __PDP_ENDIAN +#else +# error "Unknown byte order!" +#endif + +#endif /* _KLIBC_ENDIAN_H */ diff --git a/com32/include/klibc/extern.h b/com32/include/klibc/extern.h new file mode 100644 index 0000000..f9c3467 --- /dev/null +++ b/com32/include/klibc/extern.h @@ -0,0 +1,14 @@ +/* + * klibc/extern.h + */ + +#ifndef _KLIBC_EXTERN_H +#define _KLIBC_EXTERN_H + +#ifdef __cplusplus +#define __extern extern "C" +#else +#define __extern extern +#endif + +#endif /* _KLIBC_EXTERN_H */ diff --git a/com32/include/klibc/sysconfig.h b/com32/include/klibc/sysconfig.h new file mode 100644 index 0000000..efaaaf5 --- /dev/null +++ b/com32/include/klibc/sysconfig.h @@ -0,0 +1,34 @@ +/* + * klibc/sysconfig.h + * + * Allows for definitions of some things which may be system-dependent + */ + +#ifndef _KLIBC_SYSCONFIG_H +#define _KLIBC_SYSCONFIG_H + +/* + * Define this to obtain memory using sbrk() instead + * of mmap(). This should make it friendlier on + * non-MMU architectures. This should become a + * per-architecture configurable. + */ +#define MALLOC_USING_SBRK + +/* + * This is the minimum chunk size we will ask the kernel for using + * malloc(); this should be a multiple of the page size on all + * architectures. + */ +#define MALLOC_CHUNK_SIZE 4096 +#define MALLOC_CHUNK_MASK (MALLOC_CHUNK_SIZE-1) + +/* + * This is the minimum alignment for the memory returned by sbrk(). + * It must be a power of 2. If MALLOC_USING_SBRK is defined it should + * be no smaller than the size of struct arena_header in malloc.h (4 + * pointers.) + */ +#define SBRK_ALIGNMENT 32 + +#endif /* _KLIBC_SYSCONFIG_H */ diff --git a/com32/include/limits.h b/com32/include/limits.h new file mode 100644 index 0000000..64ef974 --- /dev/null +++ b/com32/include/limits.h @@ -0,0 +1,39 @@ +/* + * limits.h + */ + +#ifndef _LIMITS_H +#define _LIMITS_H + +#define CHAR_BIT 8 +#define SHRT_BIT 16 +#define INT_BIT 32 +#define LONGLONG_BIT 64 + +#define SCHAR_MIN (-128) +#define SCHAR_MAX 127 +#define UCHAR_MAX 255 + +#ifdef __CHAR_UNSIGNED__ +# define CHAR_MIN 0 +# define CHAR_MAX UCHAR_MAX +#else +# define CHAR_MIN SCHAR_MIN +# define CHAR_MAX SCHAR_MAX +#endif + +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 +#define USHRT_MAX 65535 + +#define INT_MIN (-2147483647-1) +#define INT_MAX 2147483647 +#define UINT_MAX 4294967295U + +#define LONGLONG_MIN (-9223372036854775807LL-1) +#define LONGLONG_MAX 9223372036854775807LL +#define ULONGLONG_MAX 18446744073709551615ULL + +#include + +#endif /* _LIMITS_H */ diff --git a/com32/include/math.h b/com32/include/math.h new file mode 100644 index 0000000..e3d248f --- /dev/null +++ b/com32/include/math.h @@ -0,0 +1,15 @@ +#ifndef _MATH_H +#define _MATH_H + +#ifndef __DBL_MIN_EXP__ +# define __DBL_MIN_EXP__ (-1021) +#endif +#ifndef __DBL_MAX_EXP__ +# define __DBL_MAX_EXP__ 1024 +#endif + +double pow(double, double); +double fabs(double); +double strtod(const char *, char **); + +#endif /* _MATH_H */ diff --git a/com32/include/minmax.h b/com32/include/minmax.h new file mode 100644 index 0000000..eb6e39a --- /dev/null +++ b/com32/include/minmax.h @@ -0,0 +1,42 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#ifndef _MINMAX_H +#define _MINMAX_H + +/* + * minmax.h: Type-independent safe min/max macros + */ + +#define min(x,y) ({ __typeof(x) xx = (x); \ + __typeof(y) yy = (y); \ + xx < yy ? xx : yy; }) +#define max(x,y) ({ __typeof(x) xx = (x); \ + __typeof(y) yy = (y); \ + xx > yy ? xx : yy; }) + +#endif /* _MINMAX_H */ diff --git a/com32/include/netinet/in.h b/com32/include/netinet/in.h new file mode 100644 index 0000000..5fd71a6 --- /dev/null +++ b/com32/include/netinet/in.h @@ -0,0 +1,47 @@ +#ifndef _NETINET_IN_H +#define _NETINET_IN_H + +/* COM32 will be running on an i386 platform */ + +#include + +static inline uint16_t __htons(uint16_t v) +{ + return ((v) << 8) | ((v) >> 8); +} + +#define htons(x) __htons(x) +#define ntohs(x) __htons(x) + +static inline uint32_t __htonl(uint32_t v) +{ + if ( __builtin_constant_p(v) ) { + return (((v) & 0x000000ff) << 24) | + (((v) & 0x0000ff00) << 8) | + (((v) & 0x00ff0000) >> 8) | + (((v) & 0xff000000) >> 24); + } else { + asm("xchgb %h0,%b0 ; roll $16,%0 ; xchgb %h0,%b0" : "+abcd" (v)); + return v; + } +} + +#define htonl(x) __htonl(x) +#define ntohl(x) __htonl(x) + +static inline uint64_t __htonq(uint64_t v) +{ + return ((uint64_t) __htonl(v) << 32) | __htonl(v >> 32); +} + +#define htonq(x) __htonq(x) +#define ntohq(x) __htonq(x) + +typedef uint32_t in_addr_t; +typedef uint16_t in_port_t; + +struct in_addr { + in_addr_t s_addr; +}; + +#endif /* _NETINET_IN_H */ diff --git a/com32/include/png.h b/com32/include/png.h new file mode 100644 index 0000000..800b912 --- /dev/null +++ b/com32/include/png.h @@ -0,0 +1,3419 @@ +/* png.h - header file for PNG reference library + * + * libpng version 1.2.8 - December 3, 2004 + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.2.8 - December 3, 2004: Glenn + * See also "Contributing Authors", below. + * + * Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * 1.0.8beta1-4 1 10008 2.1.0.8beta1-4 + * 1.0.8rc1 1 10008 2.1.0.8rc1 + * 1.0.8 1 10008 2.1.0.8 + * 1.0.9beta1-6 1 10009 2.1.0.9beta1-6 + * 1.0.9rc1 1 10009 2.1.0.9rc1 + * 1.0.9beta7-10 1 10009 2.1.0.9beta7-10 + * 1.0.9rc2 1 10009 2.1.0.9rc2 + * 1.0.9 1 10009 2.1.0.9 + * 1.0.10beta1 1 10010 2.1.0.10beta1 + * 1.0.10rc1 1 10010 2.1.0.10rc1 + * 1.0.10 1 10010 2.1.0.10 + * 1.0.11beta1-3 1 10011 2.1.0.11beta1-3 + * 1.0.11rc1 1 10011 2.1.0.11rc1 + * 1.0.11 1 10011 2.1.0.11 + * 1.0.12beta1-2 2 10012 2.1.0.12beta1-2 + * 1.0.12rc1 2 10012 2.1.0.12rc1 + * 1.0.12 2 10012 2.1.0.12 + * 1.1.0a-f - 10100 2.1.1.0a-f (branch abandoned) + * 1.2.0beta1-2 2 10200 2.1.2.0beta1-2 + * 1.2.0beta3-5 3 10200 3.1.2.0beta3-5 + * 1.2.0rc1 3 10200 3.1.2.0rc1 + * 1.2.0 3 10200 3.1.2.0 + * 1.2.1beta1-4 3 10201 3.1.2.1beta1-4 + * 1.2.1rc1-2 3 10201 3.1.2.1rc1-2 + * 1.2.1 3 10201 3.1.2.1 + * 1.2.2beta1-6 12 10202 12.so.0.1.2.2beta1-6 + * 1.0.13beta1 10 10013 10.so.0.1.0.13beta1 + * 1.0.13rc1 10 10013 10.so.0.1.0.13rc1 + * 1.2.2rc1 12 10202 12.so.0.1.2.2rc1 + * 1.0.13 10 10013 10.so.0.1.0.13 + * 1.2.2 12 10202 12.so.0.1.2.2 + * 1.2.3rc1-6 12 10203 12.so.0.1.2.3rc1-6 + * 1.2.3 12 10203 12.so.0.1.2.3 + * 1.2.4beta1-3 13 10204 12.so.0.1.2.4beta1-3 + * 1.0.14rc1 13 10014 10.so.0.1.0.14rc1 + * 1.2.4rc1 13 10204 12.so.0.1.2.4rc1 + * 1.0.14 10 10014 10.so.0.1.0.14 + * 1.2.4 13 10204 12.so.0.1.2.4 + * 1.2.5beta1-2 13 10205 12.so.0.1.2.5beta1-2 + * 1.0.15rc1-3 10 10015 10.so.0.1.0.15rc1-3 + * 1.2.5rc1-3 13 10205 12.so.0.1.2.5rc1-3 + * 1.0.15 10 10015 10.so.0.1.0.15 + * 1.2.5 13 10205 12.so.0.1.2.5 + * 1.2.6beta1-4 13 10206 12.so.0.1.2.6beta1-4 + * 1.0.16 10 10016 10.so.0.1.0.16 + * 1.2.6 13 10206 12.so.0.1.2.6 + * 1.2.7beta1-2 13 10207 12.so.0.1.2.7beta1-2 + * 1.0.17rc1 10 10017 12.so.0.1.0.17rc1 + * 1.2.7rc1 13 10207 12.so.0.1.2.7rc1 + * 1.0.17 10 10017 12.so.0.1.0.17 + * 1.2.7 13 10207 12.so.0.1.2.7 + * 1.2.8beta1-5 13 10208 12.so.0.1.2.8beta1-5 + * 1.0.18rc1-5 10 10018 12.so.0.1.0.18rc1-5 + * 1.2.8rc1-5 13 10208 12.so.0.1.2.8rc1-5 + * 1.0.18 10 10018 12.so.0.1.0.18 + * 1.2.8 13 10208 12.so.0.1.2.8 + * + * Henceforth the source version will match the shared-library major + * and minor numbers; the shared-library major version number will be + * used for changes in backward compatibility, as it is intended. The + * PNG_LIBPNG_VER macro, which is not used within libpng but is available + * for applications, is an unsigned integer of the form xyyzz corresponding + * to the source version x.y.z (leading zeros in y and z). Beta versions + * were given the previous public release number plus a letter, until + * version 1.0.6j; from then on they were given the upcoming public + * release number plus "betaNN" or "rcN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO Specification, + * defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001 +#define PNG_INFO_sBIT 0x0002 +#define PNG_INFO_cHRM 0x0004 +#define PNG_INFO_PLTE 0x0008 +#define PNG_INFO_tRNS 0x0010 +#define PNG_INFO_bKGD 0x0020 +#define PNG_INFO_hIST 0x0040 +#define PNG_INFO_pHYs 0x0080 +#define PNG_INFO_oFFs 0x0100 +#define PNG_INFO_tIME 0x0200 +#define PNG_INFO_pCAL 0x0400 +#define PNG_INFO_sRGB 0x0800 /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000 /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000 /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000 /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000L /* ESR, 1.0.6 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + png_uint_32 rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info FAR * png_row_infop; +typedef png_row_info FAR * FAR * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. + */ +typedef struct png_struct_def png_struct; +typedef png_struct FAR * png_structp; + +typedef void (PNGAPI *png_error_ptr) PNGARG((png_structp, png_const_charp)); +typedef void (PNGAPI *png_rw_ptr) PNGARG((png_structp, png_bytep, png_size_t)); +typedef void (PNGAPI *png_flush_ptr) PNGARG((png_structp)); +typedef void (PNGAPI *png_read_status_ptr) PNGARG((png_structp, png_uint_32, + int)); +typedef void (PNGAPI *png_write_status_ptr) PNGARG((png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef void (PNGAPI *png_progressive_info_ptr) PNGARG((png_structp, png_infop)); +typedef void (PNGAPI *png_progressive_end_ptr) PNGARG((png_structp, png_infop)); +typedef void (PNGAPI *png_progressive_row_ptr) PNGARG((png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +typedef void (PNGAPI *png_user_transform_ptr) PNGARG((png_structp, + png_row_infop, png_bytep)); +#endif + +#if defined(PNG_USER_CHUNKS_SUPPORTED) +typedef int (PNGAPI *png_user_chunk_ptr) PNGARG((png_structp, png_unknown_chunkp)); +#endif +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +typedef void (PNGAPI *png_unknown_chunk_ptr) PNGARG((png_structp)); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* WRITE only */ + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +typedef png_voidp (*png_malloc_ptr) PNGARG((png_structp, png_size_t)); +typedef void (*png_free_ptr) PNGARG((png_structp, png_voidp)); + +/* The structure that holds the information to read and write PNG files. + * The only people who need to care about what is inside of this are the + * people who will be modifying the library for their own special needs. + * It should NOT be accessed directly by an application, except to store + * the jmp_buf. + */ + +struct png_struct_def +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf jmpbuf; /* used in png_error */ +#endif + png_error_ptr error_fn; /* function for printing errors and aborting */ + png_error_ptr warning_fn; /* function for printing warnings */ + png_voidp error_ptr; /* user supplied struct for error functions */ + png_rw_ptr write_data_fn; /* function for writing output data */ + png_rw_ptr read_data_fn; /* function for reading input data */ + png_voidp io_ptr; /* ptr to application struct for I/O functions */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + png_user_transform_ptr read_user_transform_fn; /* user read transform */ +#endif + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + png_user_transform_ptr write_user_transform_fn; /* user write transform */ +#endif + +/* These were added in libpng-1.0.2 */ +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + png_voidp user_transform_ptr; /* user supplied struct for user transform */ + png_byte user_transform_depth; /* bit depth of user transformed pixels */ + png_byte user_transform_channels; /* channels in user transformed pixels */ +#endif +#endif + + png_uint_32 mode; /* tells us where we are in the PNG file */ + png_uint_32 flags; /* flags indicating various things to libpng */ + png_uint_32 transformations; /* which transformations to perform */ + + z_stream zstream; /* pointer to decompression structure (below) */ + png_bytep zbuf; /* buffer for zlib */ + png_size_t zbuf_size; /* size of zbuf */ + int zlib_level; /* holds zlib compression level */ + int zlib_method; /* holds zlib compression method */ + int zlib_window_bits; /* holds zlib compression window bits */ + int zlib_mem_level; /* holds zlib compression memory level */ + int zlib_strategy; /* holds zlib compression strategy */ + + png_uint_32 width; /* width of image in pixels */ + png_uint_32 height; /* height of image in pixels */ + png_uint_32 num_rows; /* number of rows in current pass */ + png_uint_32 usr_width; /* width of row at start of write */ + png_uint_32 rowbytes; /* size of row in bytes */ + png_uint_32 irowbytes; /* size of current interlaced row in bytes */ + png_uint_32 iwidth; /* width of current interlaced row in pixels */ + png_uint_32 row_number; /* current row in interlace pass */ + png_bytep prev_row; /* buffer to save previous (unfiltered) row */ + png_bytep row_buf; /* buffer to save current (unfiltered) row */ + png_bytep sub_row; /* buffer to save "sub" row when filtering */ + png_bytep up_row; /* buffer to save "up" row when filtering */ + png_bytep avg_row; /* buffer to save "avg" row when filtering */ + png_bytep paeth_row; /* buffer to save "Paeth" row when filtering */ + png_row_info row_info; /* used for transformation routines */ + + png_uint_32 idat_size; /* current IDAT size for read */ + png_uint_32 crc; /* current chunk CRC value */ + png_colorp palette; /* palette from the input file */ + png_uint_16 num_palette; /* number of color entries in palette */ + png_uint_16 num_trans; /* number of transparency values */ + png_byte chunk_name[5]; /* null-terminated name of current chunk */ + png_byte compression; /* file compression type (always 0) */ + png_byte filter; /* file filter type (always 0) */ + png_byte interlaced; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ + png_byte pass; /* current interlace pass (0 - 6) */ + png_byte do_filter; /* row filter flags (see PNG_FILTER_ below ) */ + png_byte color_type; /* color type of file */ + png_byte bit_depth; /* bit depth of file */ + png_byte usr_bit_depth; /* bit depth of users row */ + png_byte pixel_depth; /* number of bits per pixel */ + png_byte channels; /* number of channels in file */ + png_byte usr_channels; /* channels at start of write */ + png_byte sig_bytes; /* magic bytes read/written from start of file */ + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +#ifdef PNG_LEGACY_SUPPORTED + png_byte filler; /* filler byte for pixel expansion */ +#else + png_uint_16 filler; /* filler bytes for pixel expansion */ +#endif +#endif + +#if defined(PNG_bKGD_SUPPORTED) + png_byte background_gamma_type; +# ifdef PNG_FLOATING_POINT_SUPPORTED + float background_gamma; +# endif + png_color_16 background; /* background color in screen gamma space */ +#if defined(PNG_READ_GAMMA_SUPPORTED) + png_color_16 background_1; /* background normalized to gamma 1.0 */ +#endif +#endif /* PNG_bKGD_SUPPORTED */ + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) + png_flush_ptr output_flush_fn;/* Function for flushing output */ + png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */ + png_uint_32 flush_rows; /* number of rows written since last flush */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + int gamma_shift; /* number of "insignificant" bits 16-bit gamma */ +#ifdef PNG_FLOATING_POINT_SUPPORTED + float gamma; /* file gamma value */ + float screen_gamma; /* screen gamma value (display_exponent) */ +#endif +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep gamma_table; /* gamma table for 8-bit depth files */ + png_bytep gamma_from_1; /* converts from 1.0 to screen */ + png_bytep gamma_to_1; /* converts from file to 1.0 */ + png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */ + png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */ + png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED) + png_color_8 sig_bit; /* significant bits in each available channel */ +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) + png_color_8 shift; /* shift for significant bit tranformation */ +#endif + +#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \ + || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep trans; /* transparency values for paletted files */ + png_color_16 trans_values; /* transparency values for non-paletted files */ +#endif + + png_read_status_ptr read_row_fn; /* called after each row is decoded */ + png_write_status_ptr write_row_fn; /* called after each row is encoded */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_progressive_info_ptr info_fn; /* called after header data fully read */ + png_progressive_row_ptr row_fn; /* called after each prog. row is decoded */ + png_progressive_end_ptr end_fn; /* called after image is complete */ + png_bytep save_buffer_ptr; /* current location in save_buffer */ + png_bytep save_buffer; /* buffer for previously read data */ + png_bytep current_buffer_ptr; /* current location in current_buffer */ + png_bytep current_buffer; /* buffer for recently used data */ + png_uint_32 push_length; /* size of current input chunk */ + png_uint_32 skip_length; /* bytes to skip in input data */ + png_size_t save_buffer_size; /* amount of data now in save_buffer */ + png_size_t save_buffer_max; /* total size of save_buffer */ + png_size_t buffer_size; /* total amount of available input data */ + png_size_t current_buffer_size; /* amount of data now in current_buffer */ + int process_mode; /* what push library is currently doing */ + int cur_palette; /* current push library palette index */ + +# if defined(PNG_TEXT_SUPPORTED) + png_size_t current_text_size; /* current size of text input data */ + png_size_t current_text_left; /* how much text left to read in input */ + png_charp current_text; /* current text chunk buffer */ + png_charp current_text_ptr; /* current location in current_text */ +# endif /* PNG_PROGRESSIVE_READ_SUPPORTED && PNG_TEXT_SUPPORTED */ + +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) +/* for the Borland special 64K segment handler */ + png_bytepp offset_table_ptr; + png_bytep offset_table; + png_uint_16 offset_table_number; + png_uint_16 offset_table_count; + png_uint_16 offset_table_count_free; +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) + png_bytep palette_lookup; /* lookup table for dithering */ + png_bytep dither_index; /* index translation for palette files */ +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) || defined(PNG_hIST_SUPPORTED) + png_uint_16p hist; /* histogram */ +#endif + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_byte heuristic_method; /* heuristic for row filter selection */ + png_byte num_prev_filters; /* number of weights for previous rows */ + png_bytep prev_filters; /* filter type(s) of previous row(s) */ + png_uint_16p filter_weights; /* weight(s) for previous line(s) */ + png_uint_16p inv_filter_weights; /* 1/weight(s) for previous line(s) */ + png_uint_16p filter_costs; /* relative filter calculation cost */ + png_uint_16p inv_filter_costs; /* 1/relative filter calculation cost */ +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) + png_charp time_buffer; /* String to hold RFC 1123 time text */ +#endif + +/* New members added in libpng-1.0.6 */ + +#ifdef PNG_FREE_ME_SUPPORTED + png_uint_32 free_me; /* flags items libpng is responsible for freeing */ +#endif + +#if defined(PNG_USER_CHUNKS_SUPPORTED) + png_voidp user_chunk_ptr; + png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */ +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + int num_chunk_list; + png_bytep chunk_list; +#endif + +/* New members added in libpng-1.0.3 */ +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + png_byte rgb_to_gray_status; + /* These were changed from png_byte in libpng-1.0.6 */ + png_uint_16 rgb_to_gray_red_coeff; + png_uint_16 rgb_to_gray_green_coeff; + png_uint_16 rgb_to_gray_blue_coeff; +#endif + +/* New member added in libpng-1.0.4 (renamed in 1.0.9) */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) || \ + defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +/* changed from png_byte to png_uint_32 at version 1.2.0 */ +#ifdef PNG_1_0_X + png_byte mng_features_permitted; +#else + png_uint_32 mng_features_permitted; +#endif /* PNG_1_0_X */ +#endif + +/* New member added in libpng-1.0.7 */ +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_fixed_point int_gamma; +#endif + +/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) + png_byte filter_type; +#endif + +#if defined(PNG_1_0_X) || (defined(PNG_DEBUG) && defined(PNG_USE_PNGGCCRD)) +/* New member added in libpng-1.0.10, ifdef'ed out in 1.2.0 */ + png_uint_32 row_buf_size; +#endif + +/* New members added in libpng-1.2.0 */ +#if !defined(PNG_1_0_X) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) + png_byte mmx_bitdepth_threshold; + png_uint_32 mmx_rowbytes_threshold; + png_uint_32 asm_flags; +#endif + +/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */ +#ifdef PNG_USER_MEM_SUPPORTED + png_voidp mem_ptr; /* user supplied struct for mem functions */ + png_malloc_ptr malloc_fn; /* function for allocating memory */ + png_free_ptr free_fn; /* function for freeing memory */ +#endif + +/* New member added in libpng-1.0.13 and 1.2.0 */ + png_bytep big_row_buf; /* buffer to save current (unfiltered) row */ + +#if defined(PNG_READ_DITHER_SUPPORTED) +/* The following three members were added at version 1.0.14 and 1.2.4 */ + png_bytep dither_sort; /* working sort array */ + png_bytep index_to_palette; /* where the original index currently is */ + /* in the palette */ + png_bytep palette_to_index; /* which original index points to this */ + /* palette color */ +#endif + +/* New members added in libpng-1.0.16 and 1.2.6 */ + png_byte compression_type; + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_uint_32 user_width_max; + png_uint_32 user_height_max; +#endif + +}; + + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef png_structp version_1_2_8; + +typedef png_struct FAR * FAR * png_structpp; + +/* Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + */ + +/* Returns the version number of the library */ +extern PNG_EXPORT(png_uint_32,png_access_version_number) PNGARG((void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +extern PNG_EXPORT(void,png_set_sig_bytes) PNGARG((png_structp png_ptr, + int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +extern PNG_EXPORT(int,png_sig_cmp) PNGARG((png_bytep sig, png_size_t start, + png_size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +extern PNG_EXPORT(int,png_check_sig) PNGARG((png_bytep sig, int num)); + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +extern PNG_EXPORT(png_structp,png_create_read_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)); + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +extern PNG_EXPORT(png_structp,png_create_write_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)); + +#ifdef PNG_WRITE_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_compression_buffer_size) + PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_WRITE_SUPPORTED +extern PNG_EXPORT(void,png_set_compression_buffer_size) + PNGARG((png_structp png_ptr, png_uint_32 size)); +#endif + +/* Reset the compression stream */ +extern PNG_EXPORT(int,png_reset_zstream) PNGARG((png_structp png_ptr)); + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +extern PNG_EXPORT(png_structp,png_create_read_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +extern PNG_EXPORT(png_structp,png_create_write_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +#endif + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +extern PNG_EXPORT(void,png_write_chunk) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_bytep data, png_size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +extern PNG_EXPORT(void,png_write_chunk_start) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +extern PNG_EXPORT(void,png_write_chunk_data) PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +extern PNG_EXPORT(void,png_write_chunk_end) PNGARG((png_structp png_ptr)); + +/* Allocate and initialize the info structure */ +extern PNG_EXPORT(png_infop,png_create_info_struct) + PNGARG((png_structp png_ptr)); + +/* Initialize the info structure (old interface - DEPRECATED) */ +extern PNG_EXPORT(void,png_info_init) PNGARG((png_infop info_ptr)); +#undef png_info_init +#define png_info_init(info_ptr) png_info_init_3(&info_ptr,\ + png_sizeof(png_info)); +extern PNG_EXPORT(void,png_info_init_3) PNGARG((png_infopp info_ptr, + png_size_t png_info_struct_size)); + +/* Writes all the PNG information before the image. */ +extern PNG_EXPORT(void,png_write_info_before_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +extern PNG_EXPORT(void,png_write_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read the information before the actual image data. */ +extern PNG_EXPORT(void,png_read_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) +extern PNG_EXPORT(png_charp,png_convert_to_rfc1123) + PNGARG((png_structp png_ptr, png_timep ptime)); +#endif + +#if !defined(_WIN32_WCE) +/* "time.h" functions are not supported on WindowsCE */ +#if defined(PNG_WRITE_tIME_SUPPORTED) +/* convert from a struct tm to png_time */ +extern PNG_EXPORT(void,png_convert_from_struct_tm) PNGARG((png_timep ptime, + struct tm FAR * ttime)); + +/* convert from time_t to png_time. Uses gmtime() */ +extern PNG_EXPORT(void,png_convert_from_time_t) PNGARG((png_timep ptime, + time_t ttime)); +#endif /* PNG_WRITE_tIME_SUPPORTED */ +#endif /* _WIN32_WCE */ + +#if defined(PNG_READ_EXPAND_SUPPORTED) +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +extern PNG_EXPORT(void,png_set_expand) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(void,png_set_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(void,png_set_palette_to_rgb) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(void,png_set_tRNS_to_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +extern PNG_EXPORT(void,png_set_bgr) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +/* Expand the grayscale to 24-bit RGB if necessary. */ +extern PNG_EXPORT(void,png_set_gray_to_rgb) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +/* Reduce RGB to grayscale. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_rgb_to_gray) PNGARG((png_structp png_ptr, + int error_action, double red, double green )); +#endif +extern PNG_EXPORT(void,png_set_rgb_to_gray_fixed) PNGARG((png_structp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green )); +extern PNG_EXPORT(png_byte,png_get_rgb_to_gray_status) PNGARG((png_structp + png_ptr)); +#endif + +extern PNG_EXPORT(void,png_build_grayscale_palette) PNGARG((int bit_depth, + png_colorp palette)); + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_strip_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_swap_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_invert_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit Gray or 24-bit RGB images. */ +extern PNG_EXPORT(void,png_set_filler) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +#define PNG_FILLER_BEFORE 0 +#define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */ +#if !defined(PNG_1_0_X) +extern PNG_EXPORT(void,png_set_add_alpha) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +#endif +#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +extern PNG_EXPORT(void,png_set_swap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +extern PNG_EXPORT(void,png_set_packing) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +extern PNG_EXPORT(void,png_set_packswap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +extern PNG_EXPORT(void,png_set_shift) PNGARG((png_structp png_ptr, + png_color_8p true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. */ +extern PNG_EXPORT(int,png_set_interlace_handling) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +extern PNG_EXPORT(void,png_set_invert_mono) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) +/* Handle alpha and tRNS by replacing with a background color. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_background) PNGARG((png_structp png_ptr, + png_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)); +#endif +#define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +#define PNG_BACKGROUND_GAMMA_SCREEN 1 +#define PNG_BACKGROUND_GAMMA_FILE 2 +#define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +/* strip the second byte of information from a 16-bit depth file. */ +extern PNG_EXPORT(void,png_set_strip_16) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +/* Turn on dithering, and reduce the palette to the number of colors available. */ +extern PNG_EXPORT(void,png_set_dither) PNGARG((png_structp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_uint_16p histogram, int full_dither)); +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) +/* Handle gamma correction. Screen_gamma=(display_exponent) */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_gamma) PNGARG((png_structp png_ptr, + double screen_gamma, double default_file_gamma)); +#endif +#endif + +#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +/* Permit or disallow empty PLTE (0: not permitted, 1: permitted) */ +/* Deprecated and will be removed. Use png_permit_mng_features() instead. */ +extern PNG_EXPORT(void,png_permit_empty_plte) PNGARG((png_structp png_ptr, + int empty_plte_permitted)); +#endif + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +/* Set how many lines between output flushes - 0 for no flushing */ +extern PNG_EXPORT(void,png_set_flush) PNGARG((png_structp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +extern PNG_EXPORT(void,png_write_flush) PNGARG((png_structp png_ptr)); +#endif + +/* optional update palette with requested transformations */ +extern PNG_EXPORT(void,png_start_read_image) PNGARG((png_structp png_ptr)); + +/* optional call to update the users info structure */ +extern PNG_EXPORT(void,png_read_update_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read one or more rows of image data. */ +extern PNG_EXPORT(void,png_read_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read a row of data. */ +extern PNG_EXPORT(void,png_read_row) PNGARG((png_structp png_ptr, + png_bytep row, + png_bytep display_row)); +#endif + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read the whole image into memory at once. */ +extern PNG_EXPORT(void,png_read_image) PNGARG((png_structp png_ptr, + png_bytepp image)); +#endif + +/* write a row of image data */ +extern PNG_EXPORT(void,png_write_row) PNGARG((png_structp png_ptr, + png_bytep row)); + +/* write a few rows of image data */ +extern PNG_EXPORT(void,png_write_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_uint_32 num_rows)); + +/* write the image data */ +extern PNG_EXPORT(void,png_write_image) PNGARG((png_structp png_ptr, + png_bytepp image)); + +/* writes the end of the PNG file. */ +extern PNG_EXPORT(void,png_write_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read the end of the PNG file. */ +extern PNG_EXPORT(void,png_read_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +/* free any memory associated with the png_info_struct */ +extern PNG_EXPORT(void,png_destroy_info_struct) PNGARG((png_structp png_ptr, + png_infopp info_ptr_ptr)); + +/* free any memory associated with the png_struct and the png_info_structs */ +extern PNG_EXPORT(void,png_destroy_read_struct) PNGARG((png_structpp + png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* free all memory used by the read (old method - NOT DLL EXPORTED) */ +extern void png_read_destroy PNGARG((png_structp png_ptr, png_infop info_ptr, + png_infop end_info_ptr)); + +/* free any memory associated with the png_struct and the png_info_structs */ +extern PNG_EXPORT(void,png_destroy_write_struct) + PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)); + +/* free any memory used in png_ptr struct (old method - NOT DLL EXPORTED) */ +extern void png_write_destroy PNGARG((png_structp png_ptr)); + +/* set the libpng method of handling chunk CRC errors */ +extern PNG_EXPORT(void,png_set_crc_action) PNGARG((png_structp png_ptr, + int crit_action, int ancil_action)); + +/* Values for png_set_crc_action() to say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explination of the compression functions. + */ + +/* set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +extern PNG_EXPORT(void,png_set_filter) PNGARG((png_structp png_ptr, int method, + int filters)); + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \ + PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* EXPERIMENTAL */ +/* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_ + * defines, either the default (minimum-sum-of-absolute-differences), or + * the experimental method (weighted-minimum-sum-of-absolute-differences). + * + * Weights are factors >= 1.0, indicating how important it is to keep the + * filter type consistent between rows. Larger numbers mean the current + * filter is that many times as likely to be the same as the "num_weights" + * previous filters. This is cumulative for each previous row with a weight. + * There needs to be "num_weights" values in "filter_weights", or it can be + * NULL if the weights aren't being specified. Weights have no influence on + * the selection of the first row filter. Well chosen weights can (in theory) + * improve the compression for a given image. + * + * Costs are factors >= 1.0 indicating the relative decoding costs of a + * filter type. Higher costs indicate more decoding expense, and are + * therefore less likely to be selected over a filter with lower computational + * costs. There needs to be a value in "filter_costs" for each valid filter + * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't + * setting the costs. Costs try to improve the speed of decompression without + * unduly increasing the compressed image size. + * + * A negative weight or cost indicates the default value is to be used, and + * values in the range [0.0, 1.0) indicate the value is to remain unchanged. + * The default values for both weights and costs are currently 1.0, but may + * change if good general weighting/cost heuristics can be found. If both + * the weights and costs are set to 1.0, this degenerates the WEIGHTED method + * to the UNWEIGHTED method, but with added encoding time/computation. + */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_filter_heuristics) PNGARG((png_structp png_ptr, + int heuristic_method, int num_weights, png_doublep filter_weights, + png_doublep filter_costs)); +#endif +#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ + +/* Heuristic used for row filter selection. These defines should NOT be + * changed. + */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer caclulations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +extern PNG_EXPORT(void,png_set_compression_level) PNGARG((png_structp png_ptr, + int level)); + +extern PNG_EXPORT(void,png_set_compression_mem_level) + PNGARG((png_structp png_ptr, int mem_level)); + +extern PNG_EXPORT(void,png_set_compression_strategy) + PNGARG((png_structp png_ptr, int strategy)); + +extern PNG_EXPORT(void,png_set_compression_window_bits) + PNGARG((png_structp png_ptr, int window_bits)); + +extern PNG_EXPORT(void,png_set_compression_method) PNGARG((png_structp png_ptr, + int method)); + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng.txt for + * more information. + */ + +#if !defined(PNG_NO_STDIO) +/* Initialize the input/output for the PNG file to the default functions. */ +extern PNG_EXPORT(void,png_init_io) PNGARG((png_structp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +extern PNG_EXPORT(void,png_set_error_fn) PNGARG((png_structp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +extern PNG_EXPORT(png_voidp,png_get_error_ptr) PNGARG((png_structp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + */ +extern PNG_EXPORT(void,png_set_write_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +extern PNG_EXPORT(void,png_set_read_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +extern PNG_EXPORT(png_voidp,png_get_io_ptr) PNGARG((png_structp png_ptr)); + +extern PNG_EXPORT(void,png_set_read_status_fn) PNGARG((png_structp png_ptr, + png_read_status_ptr read_row_fn)); + +extern PNG_EXPORT(void,png_set_write_status_fn) PNGARG((png_structp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +extern PNG_EXPORT(void,png_set_mem_fn) PNGARG((png_structp png_ptr, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +extern PNG_EXPORT(png_voidp,png_get_mem_ptr) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_read_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr read_user_transform_fn)); +#endif + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_write_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr write_user_transform_fn)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_user_transform_info) PNGARG((png_structp + png_ptr, png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +extern PNG_EXPORT(png_voidp,png_get_user_transform_ptr) + PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +extern PNG_EXPORT(void,png_set_read_user_chunk_fn) PNGARG((png_structp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +extern PNG_EXPORT(png_voidp,png_get_user_chunk_ptr) PNGARG((png_structp + png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +extern PNG_EXPORT(void,png_set_progressive_read_fn) PNGARG((png_structp png_ptr, + png_voidp progressive_ptr, + png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, + png_progressive_end_ptr end_fn)); + +/* returns the user pointer associated with the push read functions */ +extern PNG_EXPORT(png_voidp,png_get_progressive_ptr) + PNGARG((png_structp png_ptr)); + +/* function to be called when data becomes available */ +extern PNG_EXPORT(void,png_process_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep buffer, png_size_t buffer_size)); + +/* function that combines rows. Not very much different than the + * png_combine_row() call. Is this even used????? + */ +extern PNG_EXPORT(void,png_progressive_combine_row) PNGARG((png_structp png_ptr, + png_bytep old_row, png_bytep new_row)); +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +extern PNG_EXPORT(png_voidp,png_malloc) PNGARG((png_structp png_ptr, + png_uint_32 size)); + +#if defined(PNG_1_0_X) +# define png_malloc_warn png_malloc +#else +/* Added at libpng version 1.2.4 */ +extern PNG_EXPORT(png_voidp,png_malloc_warn) PNGARG((png_structp png_ptr, + png_uint_32 size)); +#endif + +/* frees a pointer allocated by png_malloc() */ +extern PNG_EXPORT(void,png_free) PNGARG((png_structp png_ptr, png_voidp ptr)); + +#if defined(PNG_1_0_X) +/* Function to allocate memory for zlib. */ +extern PNG_EXPORT(voidpf,png_zalloc) PNGARG((voidpf png_ptr, uInt items, + uInt size)); + +/* Function to free memory for zlib */ +extern PNG_EXPORT(void,png_zfree) PNGARG((voidpf png_ptr, voidpf ptr)); +#endif + +/* Free data that was allocated internally */ +extern PNG_EXPORT(void,png_free_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 free_me, int num)); +#ifdef PNG_FREE_ME_SUPPORTED +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application */ +extern PNG_EXPORT(void,png_data_freer) PNGARG((png_structp png_ptr, + png_infop info_ptr, int freer, png_uint_32 mask)); +#endif +/* assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008 +#define PNG_FREE_ICCP 0x0010 +#define PNG_FREE_SPLT 0x0020 +#define PNG_FREE_ROWS 0x0040 +#define PNG_FREE_PCAL 0x0080 +#define PNG_FREE_SCAL 0x0100 +#define PNG_FREE_UNKN 0x0200 +#define PNG_FREE_LIST 0x0400 +#define PNG_FREE_PLTE 0x1000 +#define PNG_FREE_TRNS 0x2000 +#define PNG_FREE_TEXT 0x4000 +#define PNG_FREE_ALL 0x7fff +#define PNG_FREE_MUL 0x4220 /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +extern PNG_EXPORT(png_voidp,png_malloc_default) PNGARG((png_structp png_ptr, + png_uint_32 size)); +extern PNG_EXPORT(void,png_free_default) PNGARG((png_structp png_ptr, + png_voidp ptr)); +#endif + +extern PNG_EXPORT(png_voidp,png_memcpy_check) PNGARG((png_structp png_ptr, + png_voidp s1, png_voidp s2, png_uint_32 size)); + +extern PNG_EXPORT(png_voidp,png_memset_check) PNGARG((png_structp png_ptr, + png_voidp s1, int value, png_uint_32 size)); + +#if defined(USE_FAR_KEYWORD) /* memory model conversion function */ +extern void *png_far_to_near PNGARG((png_structp png_ptr,png_voidp ptr, + int check)); +#endif /* USE_FAR_KEYWORD */ + +/* Fatal error in PNG image of libpng - can't continue */ +extern PNG_EXPORT(void,png_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)); + +/* The same, but the chunk name is prepended to the error string. */ +extern PNG_EXPORT(void,png_chunk_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)); + +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +extern PNG_EXPORT(void,png_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +/* Non-fatal error in libpng, chunk name is prepended to message. */ +extern PNG_EXPORT(void,png_chunk_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +extern PNG_EXPORT(png_uint_32,png_get_valid) PNGARG((png_structp png_ptr, +png_infop info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +extern PNG_EXPORT(png_uint_32,png_get_rowbytes) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +/* Returns row_pointers, which is an array of pointers to scanlines that was +returned from png_read_png(). */ +extern PNG_EXPORT(png_bytepp,png_get_rows) PNGARG((png_structp png_ptr, +png_infop info_ptr)); +/* Set row_pointers, which is an array of pointers to scanlines for use +by png_write_png(). */ +extern PNG_EXPORT(void,png_set_rows) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +extern PNG_EXPORT(png_byte,png_get_channels) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +extern PNG_EXPORT(png_uint_32, png_get_image_width) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image height in pixels. */ +extern PNG_EXPORT(png_uint_32, png_get_image_height) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image bit_depth. */ +extern PNG_EXPORT(png_byte, png_get_bit_depth) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image color_type. */ +extern PNG_EXPORT(png_byte, png_get_color_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image filter_type. */ +extern PNG_EXPORT(png_byte, png_get_filter_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image interlace_type. */ +extern PNG_EXPORT(png_byte, png_get_interlace_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image compression_type. */ +extern PNG_EXPORT(png_byte, png_get_compression_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +extern PNG_EXPORT(png_uint_32, png_get_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_uint_32, png_get_x_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_uint_32, png_get_y_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(float, png_get_pixel_aspect_ratio) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +#endif + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +extern PNG_EXPORT(png_int_32, png_get_x_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_y_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_x_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_y_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +#endif /* PNG_EASY_ACCESS_SUPPORTED */ + +/* Returns pointer to signature string read from PNG header */ +extern PNG_EXPORT(png_bytep,png_get_signature) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#if defined(PNG_bKGD_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p *background)); +#endif + +#if defined(PNG_bKGD_SUPPORTED) +extern PNG_EXPORT(void,png_set_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p background)); +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_white_x, png_fixed_point + *int_white_y, png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, png_fixed_point + *int_blue_x, png_fixed_point *int_blue_y)); +#endif +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double white_x, double white_y, double red_x, + double red_y, double green_x, double green_y, double blue_x, double blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)); +#endif +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *file_gamma)); +#endif +extern PNG_EXPORT(png_uint_32,png_get_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_file_gamma)); +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double file_gamma)); +#endif +extern PNG_EXPORT(void,png_set_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_file_gamma)); +#endif + +#if defined(PNG_hIST_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p *hist)); +#endif + +#if defined(PNG_hIST_SUPPORTED) +extern PNG_EXPORT(void,png_set_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p hist)); +#endif + +extern PNG_EXPORT(png_uint_32,png_get_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +extern PNG_EXPORT(void,png_set_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#if defined(PNG_oFFs_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#if defined(PNG_oFFs_SUPPORTED) +extern PNG_EXPORT(void,png_set_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#if defined(PNG_pCAL_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp *purpose, png_int_32 *X0, png_int_32 *X1, + int *type, int *nparams, png_charp *units, png_charpp *params)); +#endif + +#if defined(PNG_pCAL_SUPPORTED) +extern PNG_EXPORT(void,png_set_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_charp units, png_charpp params)); +#endif + +#if defined(PNG_pHYs_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); +#endif + +#if defined(PNG_pHYs_SUPPORTED) +extern PNG_EXPORT(void,png_set_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +extern PNG_EXPORT(png_uint_32,png_get_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp *palette, int *num_palette)); + +extern PNG_EXPORT(void,png_set_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp palette, int num_palette)); + +#if defined(PNG_sBIT_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p *sig_bit)); +#endif + +#if defined(PNG_sBIT_SUPPORTED) +extern PNG_EXPORT(void,png_set_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p sig_bit)); +#endif + +#if defined(PNG_sRGB_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *intent)); +#endif + +#if defined(PNG_sRGB_SUPPORTED) +extern PNG_EXPORT(void,png_set_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +extern PNG_EXPORT(void,png_set_sRGB_gAMA_and_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +#endif + +#if defined(PNG_iCCP_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charpp name, int *compression_type, + png_charpp profile, png_uint_32 *proflen)); + /* Note to maintainer: profile should be png_bytepp */ +#endif + +#if defined(PNG_iCCP_SUPPORTED) +extern PNG_EXPORT(void,png_set_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp name, int compression_type, + png_charp profile, png_uint_32 proflen)); + /* Note to maintainer: profile should be png_bytep */ +#endif + +#if defined(PNG_sPLT_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tpp entries)); +#endif + +#if defined(PNG_sPLT_SUPPORTED) +extern PNG_EXPORT(void,png_set_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tp entries, int nentries)); +#endif + +#if defined(PNG_TEXT_SUPPORTED) +/* png_get_text also returns the number of text chunks in *num_text */ +extern PNG_EXPORT(png_uint_32,png_get_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* + * Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#if defined(PNG_TEXT_SUPPORTED) +extern PNG_EXPORT(void,png_set_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp text_ptr, int num_text)); +#endif + +#if defined(PNG_tIME_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep *mod_time)); +#endif + +#if defined(PNG_tIME_SUPPORTED) +extern PNG_EXPORT(void,png_set_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep mod_time)); +#endif + +#if defined(PNG_tRNS_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep *trans, int *num_trans, + png_color_16p *trans_values)); +#endif + +#if defined(PNG_tRNS_SUPPORTED) +extern PNG_EXPORT(void,png_set_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep trans, int num_trans, + png_color_16p trans_values)); +#endif + +#if defined(PNG_tRNS_SUPPORTED) +#endif + +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, double *width, double *height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, png_charpp swidth, png_charpp sheight)); +#endif +#endif +#endif /* PNG_sCAL_SUPPORTED */ + +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, double width, double height)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, png_charp swidth, png_charp sheight)); +#endif +#endif /* PNG_sCAL_SUPPORTED || PNG_WRITE_sCAL_SUPPORTED */ + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +/* provide a list of chunks and how they are to be handled, if the built-in + handling or default unknown chunk handling is not desired. Any chunks not + listed will be handled in the default manner. The IHDR and IEND chunks + must not be listed. + keep = 0: follow default behavour + = 1: do not keep + = 2: keep only if safe-to-copy + = 3: keep even if unsafe-to-copy +*/ +extern PNG_EXPORT(void, png_set_keep_unknown_chunks) PNGARG((png_structp + png_ptr, int keep, png_bytep chunk_list, int num_chunks)); +extern PNG_EXPORT(void, png_set_unknown_chunks) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns)); +extern PNG_EXPORT(void, png_set_unknown_chunk_location) + PNGARG((png_structp png_ptr, png_infop info_ptr, int chunk, int location)); +extern PNG_EXPORT(png_uint_32,png_get_unknown_chunks) PNGARG((png_structp + png_ptr, png_infop info_ptr, png_unknown_chunkpp entries)); +#endif +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +PNG_EXPORT(int,png_handle_as_unknown) PNGARG((png_structp png_ptr, png_bytep + chunk_name)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + If you need to turn it off for a chunk that your application has freed, + you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); */ +extern PNG_EXPORT(void, png_set_invalid) PNGARG((png_structp png_ptr, + png_infop info_ptr, int mask)); + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +/* The "params" pointer is currently not used and is for future expansion. */ +extern PNG_EXPORT(void, png_read_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +extern PNG_EXPORT(void, png_write_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +#endif + +/* Define PNG_DEBUG at compile time for debugging information. Higher + * numbers for PNG_DEBUG mean more debugging information. This has + * only been added since version 0.95 so it is not implemented throughout + * libpng yet, but more support will be added as needed. + */ +#ifdef PNG_DEBUG +#if (PNG_DEBUG > 0) +#if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER) +#include +#if (PNG_DEBUG > 1) +#define png_debug(l,m) _RPT0(_CRT_WARN,m) +#define png_debug1(l,m,p1) _RPT1(_CRT_WARN,m,p1) +#define png_debug2(l,m,p1,p2) _RPT2(_CRT_WARN,m,p1,p2) +#endif +#else /* PNG_DEBUG_FILE || !_MSC_VER */ +#ifndef PNG_DEBUG_FILE +#define PNG_DEBUG_FILE stderr +#endif /* PNG_DEBUG_FILE */ +#if (PNG_DEBUG > 1) +#define png_debug(l,m) \ +{ \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":"")))); \ +} +#define png_debug1(l,m,p1) \ +{ \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1); \ +} +#define png_debug2(l,m,p1,p2) \ +{ \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1,p2); \ +} +#endif /* (PNG_DEBUG > 1) */ +#endif /* _MSC_VER */ +#endif /* (PNG_DEBUG > 0) */ +#endif /* PNG_DEBUG */ +#ifndef png_debug +#define png_debug(l, m) +#endif +#ifndef png_debug1 +#define png_debug1(l, m, p1) +#endif +#ifndef png_debug2 +#define png_debug2(l, m, p1, p2) +#endif + +extern PNG_EXPORT(png_bytep,png_sig_bytes) PNGARG((void)); + +extern PNG_EXPORT(png_charp,png_get_copyright) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_header_ver) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_header_version) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_libpng_ver) PNGARG((png_structp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_permit_mng_features) PNGARG((png_structp + png_ptr, png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 + +/* Added to version 1.2.0 */ +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +#define PNG_ASM_FLAG_MMX_SUPPORT_COMPILED 0x01 /* not user-settable */ +#define PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU 0x02 /* not user-settable */ +#define PNG_ASM_FLAG_MMX_READ_COMBINE_ROW 0x04 +#define PNG_ASM_FLAG_MMX_READ_INTERLACE 0x08 +#define PNG_ASM_FLAG_MMX_READ_FILTER_SUB 0x10 +#define PNG_ASM_FLAG_MMX_READ_FILTER_UP 0x20 +#define PNG_ASM_FLAG_MMX_READ_FILTER_AVG 0x40 +#define PNG_ASM_FLAG_MMX_READ_FILTER_PAETH 0x80 +#define PNG_ASM_FLAGS_INITIALIZED 0x80000000 /* not user-settable */ + +#define PNG_MMX_READ_FLAGS ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \ + | PNG_ASM_FLAG_MMX_READ_INTERLACE \ + | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \ + | PNG_ASM_FLAG_MMX_READ_FILTER_UP \ + | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \ + | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ) +#define PNG_MMX_WRITE_FLAGS ( 0 ) + +#define PNG_MMX_FLAGS ( PNG_ASM_FLAG_MMX_SUPPORT_COMPILED \ + | PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU \ + | PNG_MMX_READ_FLAGS \ + | PNG_MMX_WRITE_FLAGS ) + +#define PNG_SELECT_READ 1 +#define PNG_SELECT_WRITE 2 + +#if !defined(PNG_1_0_X) +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_mmx_flagmask) + PNGARG((int flag_select, int *compilerID)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_asm_flagmask) + PNGARG((int flag_select)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_asm_flags) + PNGARG((png_structp png_ptr)); + +/* pngget.c */ +extern PNG_EXPORT(png_byte,png_get_mmx_bitdepth_threshold) + PNGARG((png_structp png_ptr)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_mmx_rowbytes_threshold) + PNGARG((png_structp png_ptr)); + +/* pngset.c */ +extern PNG_EXPORT(void,png_set_asm_flags) + PNGARG((png_structp png_ptr, png_uint_32 asm_flags)); + +/* pngset.c */ +extern PNG_EXPORT(void,png_set_mmx_thresholds) + PNGARG((png_structp png_ptr, png_byte mmx_bitdepth_threshold, + png_uint_32 mmx_rowbytes_threshold)); + +#endif /* PNG_1_0_X */ +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + +#if !defined(PNG_1_0_X) +/* png.c, pnggccrd.c, or pngvcrd.c */ +extern PNG_EXPORT(int,png_mmx_support) PNGARG((void)); + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +extern PNG_EXPORT(void,png_set_strip_error_numbers) PNGARG((png_structp + png_ptr, png_uint_32 strip_mode)); +#endif + +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +extern PNG_EXPORT(void,png_set_user_limits) PNGARG((png_structp + png_ptr, png_uint_32 user_width_max, png_uint_32 user_height_max)); +extern PNG_EXPORT(png_uint_32,png_get_user_width_max) PNGARG((png_structp + png_ptr)); +extern PNG_EXPORT(png_uint_32,png_get_user_height_max) PNGARG((png_structp + png_ptr)); +#endif + +/* Maintainer: Put new public prototypes here ^, in libpng.3, and project defs */ + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 - \ + (png_uint_16)(alpha)) + (png_uint_16)128); \ + (composite) = (png_byte)((temp + (temp >> 8)) >> 8); } + +# define png_composite_16(composite, fg, alpha, bg) \ + { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(png_uint_32)(65535L - \ + (png_uint_32)(alpha)) + (png_uint_32)32768L); \ + (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); } + +#else /* standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + (png_uint_16)127) / 255) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535L - (png_uint_32)(alpha)) + \ + (png_uint_32)32767) / (png_uint_32)65535L) + +#endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */ + +/* These next functions are used internally in the code. They generally + * shouldn't be used unless you are writing code to add or replace some + * functionality in libpng. More information about most functions can + * be found in the files where the functions are located. + */ + +#if defined(PNG_INTERNAL) + +/* Various modes of operation. Note that after an init, mode is set to + * zero automatically when the structure is created. + */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_HAVE_IDAT 0x04 +#define PNG_AFTER_IDAT 0x08 +#define PNG_HAVE_IEND 0x10 +#define PNG_HAVE_gAMA 0x20 +#define PNG_HAVE_cHRM 0x40 +#define PNG_HAVE_sRGB 0x80 +#define PNG_HAVE_CHUNK_HEADER 0x100 +#define PNG_WROTE_tIME 0x200 +#define PNG_WROTE_INFO_BEFORE_PLTE 0x400 +#define PNG_BACKGROUND_IS_GRAY 0x800 +#define PNG_HAVE_PNG_SIGNATURE 0x1000 + +/* flags for the transformations the PNG library does on the image data */ +#define PNG_BGR 0x0001 +#define PNG_INTERLACE 0x0002 +#define PNG_PACK 0x0004 +#define PNG_SHIFT 0x0008 +#define PNG_SWAP_BYTES 0x0010 +#define PNG_INVERT_MONO 0x0020 +#define PNG_DITHER 0x0040 +#define PNG_BACKGROUND 0x0080 +#define PNG_BACKGROUND_EXPAND 0x0100 + /* 0x0200 unused */ +#define PNG_16_TO_8 0x0400 +#define PNG_RGBA 0x0800 +#define PNG_EXPAND 0x1000 +#define PNG_GAMMA 0x2000 +#define PNG_GRAY_TO_RGB 0x4000 +#define PNG_FILLER 0x8000L +#define PNG_PACKSWAP 0x10000L +#define PNG_SWAP_ALPHA 0x20000L +#define PNG_STRIP_ALPHA 0x40000L +#define PNG_INVERT_ALPHA 0x80000L +#define PNG_USER_TRANSFORM 0x100000L +#define PNG_RGB_TO_GRAY_ERR 0x200000L +#define PNG_RGB_TO_GRAY_WARN 0x400000L +#define PNG_RGB_TO_GRAY 0x600000L /* two bits, RGB_TO_GRAY_ERR|WARN */ + /* 0x800000L Unused */ +#define PNG_ADD_ALPHA 0x1000000L /* Added to libpng-1.2.7 */ + /* 0x2000000L unused */ + /* 0x4000000L unused */ + /* 0x8000000L unused */ + /* 0x10000000L unused */ + /* 0x20000000L unused */ + /* 0x40000000L unused */ + +/* flags for png_create_struct */ +#define PNG_STRUCT_PNG 0x0001 +#define PNG_STRUCT_INFO 0x0002 + +/* Scaling factor for filter heuristic weighting calculations */ +#define PNG_WEIGHT_SHIFT 8 +#define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT)) +#define PNG_COST_SHIFT 3 +#define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT)) + +/* flags for the png_ptr->flags rather than declaring a byte for each one */ +#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x0001 +#define PNG_FLAG_ZLIB_CUSTOM_LEVEL 0x0002 +#define PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL 0x0004 +#define PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS 0x0008 +#define PNG_FLAG_ZLIB_CUSTOM_METHOD 0x0010 +#define PNG_FLAG_ZLIB_FINISHED 0x0020 +#define PNG_FLAG_ROW_INIT 0x0040 +#define PNG_FLAG_FILLER_AFTER 0x0080 +#define PNG_FLAG_CRC_ANCILLARY_USE 0x0100 +#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200 +#define PNG_FLAG_CRC_CRITICAL_USE 0x0400 +#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800 +#define PNG_FLAG_FREE_PLTE 0x1000 +#define PNG_FLAG_FREE_TRNS 0x2000 +#define PNG_FLAG_FREE_HIST 0x4000 +#define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000L +#define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000L +#define PNG_FLAG_LIBRARY_MISMATCH 0x20000L +#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000L +#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000L +#define PNG_FLAG_MALLOC_NULL_MEM_OK 0x100000L +#define PNG_FLAG_ADD_ALPHA 0x200000L /* Added to libpng-1.2.8 */ +#define PNG_FLAG_STRIP_ALPHA 0x400000L /* Added to libpng-1.2.8 */ + /* 0x800000L unused */ + /* 0x1000000L unused */ + /* 0x2000000L unused */ + /* 0x4000000L unused */ + /* 0x8000000L unused */ + /* 0x10000000L unused */ + /* 0x20000000L unused */ + /* 0x40000000L unused */ + +#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \ + PNG_FLAG_CRC_ANCILLARY_NOWARN) + +#define PNG_FLAG_CRC_CRITICAL_MASK (PNG_FLAG_CRC_CRITICAL_USE | \ + PNG_FLAG_CRC_CRITICAL_IGNORE) + +#define PNG_FLAG_CRC_MASK (PNG_FLAG_CRC_ANCILLARY_MASK | \ + PNG_FLAG_CRC_CRITICAL_MASK) + +/* save typing and make code easier to understand */ + +#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \ + abs((int)((c1).green) - (int)((c2).green)) + \ + abs((int)((c1).blue) - (int)((c2).blue))) + +/* Added to libpng-1.2.6 JB */ +#define PNG_ROWBYTES(pixel_bits, width) \ + ((pixel_bits) >= 8 ? \ + ((width) * (((png_uint_32)(pixel_bits)) >> 3)) : \ + (( ((width) * ((png_uint_32)(pixel_bits))) + 7) >> 3) ) + +/* PNG_OUT_OF_RANGE returns true if value is outside the range + ideal-delta..ideal+delta. Each argument is evaluated twice. + "ideal" and "delta" should be constants, normally simple + integers, "value" a variable. Added to libpng-1.2.6 JB */ +#define PNG_OUT_OF_RANGE(value, ideal, delta) \ + ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) ) + +/* variables declared in png.c - only it needs to define PNG_NO_EXTERN */ +#if !defined(PNG_NO_EXTERN) || defined(PNG_ALWAYS_EXTERN) +/* place to hold the signature string for a PNG file. */ +#ifdef PNG_USE_GLOBAL_ARRAYS + PNG_EXPORT_VAR (const png_byte FARDATA) png_sig[8]; +#else +#define png_sig png_sig_bytes(NULL) +#endif +#endif /* PNG_NO_EXTERN */ + +/* Constant strings for known chunk types. If you need to add a chunk, + * define the name here, and add an invocation of the macro in png.c and + * wherever it's needed. + */ +#define PNG_IHDR const png_byte png_IHDR[5] = { 73, 72, 68, 82, '\0'} +#define PNG_IDAT const png_byte png_IDAT[5] = { 73, 68, 65, 84, '\0'} +#define PNG_IEND const png_byte png_IEND[5] = { 73, 69, 78, 68, '\0'} +#define PNG_PLTE const png_byte png_PLTE[5] = { 80, 76, 84, 69, '\0'} +#define PNG_bKGD const png_byte png_bKGD[5] = { 98, 75, 71, 68, '\0'} +#define PNG_cHRM const png_byte png_cHRM[5] = { 99, 72, 82, 77, '\0'} +#define PNG_gAMA const png_byte png_gAMA[5] = {103, 65, 77, 65, '\0'} +#define PNG_hIST const png_byte png_hIST[5] = {104, 73, 83, 84, '\0'} +#define PNG_iCCP const png_byte png_iCCP[5] = {105, 67, 67, 80, '\0'} +#define PNG_iTXt const png_byte png_iTXt[5] = {105, 84, 88, 116, '\0'} +#define PNG_oFFs const png_byte png_oFFs[5] = {111, 70, 70, 115, '\0'} +#define PNG_pCAL const png_byte png_pCAL[5] = {112, 67, 65, 76, '\0'} +#define PNG_sCAL const png_byte png_sCAL[5] = {115, 67, 65, 76, '\0'} +#define PNG_pHYs const png_byte png_pHYs[5] = {112, 72, 89, 115, '\0'} +#define PNG_sBIT const png_byte png_sBIT[5] = {115, 66, 73, 84, '\0'} +#define PNG_sPLT const png_byte png_sPLT[5] = {115, 80, 76, 84, '\0'} +#define PNG_sRGB const png_byte png_sRGB[5] = {115, 82, 71, 66, '\0'} +#define PNG_tEXt const png_byte png_tEXt[5] = {116, 69, 88, 116, '\0'} +#define PNG_tIME const png_byte png_tIME[5] = {116, 73, 77, 69, '\0'} +#define PNG_tRNS const png_byte png_tRNS[5] = {116, 82, 78, 83, '\0'} +#define PNG_zTXt const png_byte png_zTXt[5] = {122, 84, 88, 116, '\0'} + +#ifdef PNG_USE_GLOBAL_ARRAYS +PNG_EXPORT_VAR (const png_byte FARDATA) png_IHDR[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_IDAT[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_IEND[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_PLTE[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_bKGD[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_cHRM[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_gAMA[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_hIST[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_iCCP[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_iTXt[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_oFFs[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_pCAL[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_sCAL[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_pHYs[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_sBIT[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_sPLT[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_sRGB[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_tEXt[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_tIME[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_tRNS[5]; +PNG_EXPORT_VAR (const png_byte FARDATA) png_zTXt[5]; +#endif /* PNG_USE_GLOBAL_ARRAYS */ + + +/* Inline macros to do direct reads of bytes from the input buffer. These + * require that you are using an architecture that uses PNG byte ordering + * (MSB first) and supports unaligned data storage. I think that PowerPC + * in big-endian mode and 680x0 are the only ones that will support this. + * The x86 line of processors definitely do not. The png_get_int_32() + * routine also assumes we are using two's complement format for negative + * values, which is almost certainly true. + */ +#if defined(PNG_READ_BIG_ENDIAN_SUPPORTED) +# if defined(PNG_pCAL_SUPPORTED) || defined(PNG_oFFs_SUPPORTED) +# define png_get_int_32(buf) ( *((png_int_32p) (buf))) +# endif +# define png_get_uint_32(buf) ( *((png_uint_32p) (buf))) +# define png_get_uint_16(buf) ( *((png_uint_16p) (buf))) +#else +# if defined(PNG_pCAL_SUPPORTED) || defined(PNG_oFFs_SUPPORTED) +PNG_EXTERN png_int_32 png_get_int_32 PNGARG((png_bytep buf)); +# endif +PNG_EXTERN png_uint_32 png_get_uint_32 PNGARG((png_bytep buf)); +PNG_EXTERN png_uint_16 png_get_uint_16 PNGARG((png_bytep buf)); +#endif /* !PNG_READ_BIG_ENDIAN_SUPPORTED */ +PNG_EXTERN png_uint_32 png_get_uint_31 PNGARG((png_structp png_ptr, + png_bytep buf)); + +/* Initialize png_ptr struct for reading, and allocate any other memory. + * (old interface - DEPRECATED - use png_create_read_struct instead). + */ +extern PNG_EXPORT(void,png_read_init) PNGARG((png_structp png_ptr)); +#undef png_read_init +#define png_read_init(png_ptr) png_read_init_3(&png_ptr, \ + PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); +extern PNG_EXPORT(void,png_read_init_3) PNGARG((png_structpp ptr_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size)); +extern PNG_EXPORT(void,png_read_init_2) PNGARG((png_structp png_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t + png_info_size)); + +/* Initialize png_ptr struct for writing, and allocate any other memory. + * (old interface - DEPRECATED - use png_create_write_struct instead). + */ +extern PNG_EXPORT(void,png_write_init) PNGARG((png_structp png_ptr)); +#undef png_write_init +#define png_write_init(png_ptr) png_write_init_3(&png_ptr, \ + PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); +extern PNG_EXPORT(void,png_write_init_3) PNGARG((png_structpp ptr_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size)); +extern PNG_EXPORT(void,png_write_init_2) PNGARG((png_structp png_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t + png_info_size)); + +/* Allocate memory for an internal libpng struct */ +PNG_EXTERN png_voidp png_create_struct PNGARG((int type)); + +/* Free memory from internal libpng struct */ +PNG_EXTERN void png_destroy_struct PNGARG((png_voidp struct_ptr)); + +PNG_EXTERN png_voidp png_create_struct_2 PNGARG((int type, png_malloc_ptr + malloc_fn, png_voidp mem_ptr)); +PNG_EXTERN void png_destroy_struct_2 PNGARG((png_voidp struct_ptr, + png_free_ptr free_fn, png_voidp mem_ptr)); + +/* Free any memory that info_ptr points to and reset struct. */ +PNG_EXTERN void png_info_destroy PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_1_0_X +/* Function to allocate memory for zlib. */ +PNG_EXTERN voidpf png_zalloc PNGARG((voidpf png_ptr, uInt items, uInt size)); + +/* Function to free memory for zlib */ +PNG_EXTERN void png_zfree PNGARG((voidpf png_ptr, voidpf ptr)); + +#ifdef PNG_SIZE_T +/* Function to convert a sizeof an item to png_sizeof item */ + PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)); +#endif + +/* Next four functions are used internally as callbacks. PNGAPI is required + * but not PNG_EXPORT. PNGAPI added at libpng version 1.2.3. */ + +PNG_EXTERN void PNGAPI png_default_read_data PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void PNGAPI png_push_fill_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t length)); +#endif + +PNG_EXTERN void PNGAPI png_default_write_data PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +#if !defined(PNG_NO_STDIO) +PNG_EXTERN void PNGAPI png_default_flush PNGARG((png_structp png_ptr)); +#endif +#endif +#else /* PNG_1_0_X */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void png_push_fill_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t length)); +#endif +#endif /* PNG_1_0_X */ + +/* Reset the CRC variable */ +PNG_EXTERN void png_reset_crc PNGARG((png_structp png_ptr)); + +/* Write the "data" buffer to whatever output you are using. */ +PNG_EXTERN void png_write_data PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +/* Read data from whatever input you are using into the "data" buffer */ +PNG_EXTERN void png_read_data PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +/* Read bytes into buf, and update png_ptr->crc */ +PNG_EXTERN void png_crc_read PNGARG((png_structp png_ptr, png_bytep buf, + png_size_t length)); + +/* Decompress data in a chunk that uses compression */ +#if defined(PNG_zTXt_SUPPORTED) || defined(PNG_iTXt_SUPPORTED) || \ + defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) +PNG_EXTERN png_charp png_decompress_chunk PNGARG((png_structp png_ptr, + int comp_type, png_charp chunkdata, png_size_t chunklength, + png_size_t prefix_length, png_size_t *data_length)); +#endif + +/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */ +PNG_EXTERN int png_crc_finish PNGARG((png_structp png_ptr, png_uint_32 skip)); + +/* Read the CRC from the file and compare it to the libpng calculated CRC */ +PNG_EXTERN int png_crc_error PNGARG((png_structp png_ptr)); + +/* Calculate the CRC over a section of data. Note that we are only + * passing a maximum of 64K on systems that have this as a memory limit, + * since this is the maximum buffer size we can specify. + */ +PNG_EXTERN void png_calculate_crc PNGARG((png_structp png_ptr, png_bytep ptr, + png_size_t length)); + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +PNG_EXTERN void png_flush PNGARG((png_structp png_ptr)); +#endif + + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). + * The only currently known PNG chunks that use signed numbers are + * the ancillary extension chunks, oFFs and pCAL. + */ +PNG_EXTERN void png_save_uint_32 PNGARG((png_bytep buf, png_uint_32 i)); + +#if defined(PNG_WRITE_pCAL_SUPPORTED) || defined(PNG_WRITE_oFFs_SUPPORTED) +PNG_EXTERN void png_save_int_32 PNGARG((png_bytep buf, png_int_32 i)); +#endif + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +PNG_EXTERN void png_save_uint_16 PNGARG((png_bytep buf, unsigned int i)); + +/* simple function to write the signature */ +PNG_EXTERN void png_write_sig PNGARG((png_structp png_ptr)); + +/* write various chunks */ + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. + */ +PNG_EXTERN void png_write_IHDR PNGARG((png_structp png_ptr, png_uint_32 width, + png_uint_32 height, + int bit_depth, int color_type, int compression_method, int filter_method, + int interlace_method)); + +PNG_EXTERN void png_write_PLTE PNGARG((png_structp png_ptr, png_colorp palette, + png_uint_32 num_pal)); + +PNG_EXTERN void png_write_IDAT PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +PNG_EXTERN void png_write_IEND PNGARG((png_structp png_ptr)); + +#if defined(PNG_WRITE_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXTERN void png_write_gAMA PNGARG((png_structp png_ptr, double file_gamma)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_gAMA_fixed PNGARG((png_structp png_ptr, png_fixed_point + file_gamma)); +#endif +#endif + +#if defined(PNG_WRITE_sBIT_SUPPORTED) +PNG_EXTERN void png_write_sBIT PNGARG((png_structp png_ptr, png_color_8p sbit, + int color_type)); +#endif + +#if defined(PNG_WRITE_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXTERN void png_write_cHRM PNGARG((png_structp png_ptr, + double white_x, double white_y, + double red_x, double red_y, double green_x, double green_y, + double blue_x, double blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_cHRM_fixed PNGARG((png_structp png_ptr, + png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)); +#endif +#endif + +#if defined(PNG_WRITE_sRGB_SUPPORTED) +PNG_EXTERN void png_write_sRGB PNGARG((png_structp png_ptr, + int intent)); +#endif + +#if defined(PNG_WRITE_iCCP_SUPPORTED) +PNG_EXTERN void png_write_iCCP PNGARG((png_structp png_ptr, + png_charp name, int compression_type, + png_charp profile, int proflen)); + /* Note to maintainer: profile should be png_bytep */ +#endif + +#if defined(PNG_WRITE_sPLT_SUPPORTED) +PNG_EXTERN void png_write_sPLT PNGARG((png_structp png_ptr, + png_sPLT_tp palette)); +#endif + +#if defined(PNG_WRITE_tRNS_SUPPORTED) +PNG_EXTERN void png_write_tRNS PNGARG((png_structp png_ptr, png_bytep trans, + png_color_16p values, int number, int color_type)); +#endif + +#if defined(PNG_WRITE_bKGD_SUPPORTED) +PNG_EXTERN void png_write_bKGD PNGARG((png_structp png_ptr, + png_color_16p values, int color_type)); +#endif + +#if defined(PNG_WRITE_hIST_SUPPORTED) +PNG_EXTERN void png_write_hIST PNGARG((png_structp png_ptr, png_uint_16p hist, + int num_hist)); +#endif + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ + defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) +PNG_EXTERN png_size_t png_check_keyword PNGARG((png_structp png_ptr, + png_charp key, png_charpp new_key)); +#endif + +#if defined(PNG_WRITE_tEXt_SUPPORTED) +PNG_EXTERN void png_write_tEXt PNGARG((png_structp png_ptr, png_charp key, + png_charp text, png_size_t text_len)); +#endif + +#if defined(PNG_WRITE_zTXt_SUPPORTED) +PNG_EXTERN void png_write_zTXt PNGARG((png_structp png_ptr, png_charp key, + png_charp text, png_size_t text_len, int compression)); +#endif + +#if defined(PNG_WRITE_iTXt_SUPPORTED) +PNG_EXTERN void png_write_iTXt PNGARG((png_structp png_ptr, + int compression, png_charp key, png_charp lang, png_charp lang_key, + png_charp text)); +#endif + +#if defined(PNG_TEXT_SUPPORTED) /* Added at version 1.0.14 and 1.2.4 */ +PNG_EXTERN int png_set_text_2 PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp text_ptr, int num_text)); +#endif + +#if defined(PNG_WRITE_oFFs_SUPPORTED) +PNG_EXTERN void png_write_oFFs PNGARG((png_structp png_ptr, + png_int_32 x_offset, png_int_32 y_offset, int unit_type)); +#endif + +#if defined(PNG_WRITE_pCAL_SUPPORTED) +PNG_EXTERN void png_write_pCAL PNGARG((png_structp png_ptr, png_charp purpose, + png_int_32 X0, png_int_32 X1, int type, int nparams, + png_charp units, png_charpp params)); +#endif + +#if defined(PNG_WRITE_pHYs_SUPPORTED) +PNG_EXTERN void png_write_pHYs PNGARG((png_structp png_ptr, + png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit, + int unit_type)); +#endif + +#if defined(PNG_WRITE_tIME_SUPPORTED) +PNG_EXTERN void png_write_tIME PNGARG((png_structp png_ptr, + png_timep mod_time)); +#endif + +#if defined(PNG_WRITE_sCAL_SUPPORTED) +#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) +PNG_EXTERN void png_write_sCAL PNGARG((png_structp png_ptr, + int unit, double width, double height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_sCAL_s PNGARG((png_structp png_ptr, + int unit, png_charp width, png_charp height)); +#endif +#endif +#endif + +/* Called when finished processing a row of data */ +PNG_EXTERN void png_write_finish_row PNGARG((png_structp png_ptr)); + +/* Internal use only. Called before first row of data */ +PNG_EXTERN void png_write_start_row PNGARG((png_structp png_ptr)); + +#if defined(PNG_READ_GAMMA_SUPPORTED) +PNG_EXTERN void png_build_gamma_table PNGARG((png_structp png_ptr)); +#endif + +/* combine a row of data, dealing with alpha, etc. if requested */ +PNG_EXTERN void png_combine_row PNGARG((png_structp png_ptr, png_bytep row, + int mask)); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) +/* expand an interlaced row */ +/* OLD pre-1.0.9 interface: +PNG_EXTERN void png_do_read_interlace PNGARG((png_row_infop row_info, + png_bytep row, int pass, png_uint_32 transformations)); + */ +PNG_EXTERN void png_do_read_interlace PNGARG((png_structp png_ptr)); +#endif + +/* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */ + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* grab pixels out of a row for an interlaced pass */ +PNG_EXTERN void png_do_write_interlace PNGARG((png_row_infop row_info, + png_bytep row, int pass)); +#endif + +/* unfilter a row */ +PNG_EXTERN void png_read_filter_row PNGARG((png_structp png_ptr, + png_row_infop row_info, png_bytep row, png_bytep prev_row, int filter)); + +/* Choose the best filter to use and filter the row data */ +PNG_EXTERN void png_write_find_filter PNGARG((png_structp png_ptr, + png_row_infop row_info)); + +/* Write out the filtered row. */ +PNG_EXTERN void png_write_filtered_row PNGARG((png_structp png_ptr, + png_bytep filtered_row)); +/* finish a row while reading, dealing with interlacing passes, etc. */ +PNG_EXTERN void png_read_finish_row PNGARG((png_structp png_ptr)); + +/* initialize the row buffers, etc. */ +PNG_EXTERN void png_read_start_row PNGARG((png_structp png_ptr)); +/* optional call to update the users info structure */ +PNG_EXTERN void png_read_transform_info PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +/* these are the functions that do the transformations */ +#if defined(PNG_READ_FILLER_SUPPORTED) +PNG_EXTERN void png_do_read_filler PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 filler, png_uint_32 flags)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_read_swap_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_write_swap_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_read_invert_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_write_invert_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_strip_filler PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 flags)); +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +PNG_EXTERN void png_do_swap PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) +PNG_EXTERN void png_do_packswap PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +PNG_EXTERN int png_do_rgb_to_gray PNGARG((png_structp png_ptr, png_row_infop + row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +PNG_EXTERN void png_do_gray_to_rgb PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) +PNG_EXTERN void png_do_unpack PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) +PNG_EXTERN void png_do_unshift PNGARG((png_row_infop row_info, png_bytep row, + png_color_8p sig_bits)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +PNG_EXTERN void png_do_invert PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +PNG_EXTERN void png_do_chop PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +PNG_EXTERN void png_do_dither PNGARG((png_row_infop row_info, + png_bytep row, png_bytep palette_lookup, png_bytep dither_lookup)); + +# if defined(PNG_CORRECT_PALETTE_SUPPORTED) +PNG_EXTERN void png_correct_palette PNGARG((png_structp png_ptr, + png_colorp palette, int num_palette)); +# endif +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +PNG_EXTERN void png_do_bgr PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_WRITE_PACK_SUPPORTED) +PNG_EXTERN void png_do_pack PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 bit_depth)); +#endif + +#if defined(PNG_WRITE_SHIFT_SUPPORTED) +PNG_EXTERN void png_do_shift PNGARG((png_row_infop row_info, png_bytep row, + png_color_8p bit_depth)); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) +#if defined(PNG_READ_GAMMA_SUPPORTED) +PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background, + png_color_16p background_1, + png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, + png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, + png_uint_16pp gamma_16_to_1, int gamma_shift)); +#else +PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background)); +#endif +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) +PNG_EXTERN void png_do_gamma PNGARG((png_row_infop row_info, png_bytep row, + png_bytep gamma_table, png_uint_16pp gamma_16_table, + int gamma_shift)); +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) +PNG_EXTERN void png_do_expand_palette PNGARG((png_row_infop row_info, + png_bytep row, png_colorp palette, png_bytep trans, int num_trans)); +PNG_EXTERN void png_do_expand PNGARG((png_row_infop row_info, + png_bytep row, png_color_16p trans_value)); +#endif + +/* The following decodes the appropriate chunks, and does error correction, + * then calls the appropriate callback for the chunk if it is valid. + */ + +/* decode the IHDR chunk */ +PNG_EXTERN void png_handle_IHDR PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +PNG_EXTERN void png_handle_PLTE PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +PNG_EXTERN void png_handle_IEND PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); + +#if defined(PNG_READ_bKGD_SUPPORTED) +PNG_EXTERN void png_handle_bKGD PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_cHRM_SUPPORTED) +PNG_EXTERN void png_handle_cHRM PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_gAMA_SUPPORTED) +PNG_EXTERN void png_handle_gAMA PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_hIST_SUPPORTED) +PNG_EXTERN void png_handle_hIST PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_iCCP_SUPPORTED) +extern void png_handle_iCCP PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif /* PNG_READ_iCCP_SUPPORTED */ + +#if defined(PNG_READ_iTXt_SUPPORTED) +PNG_EXTERN void png_handle_iTXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_oFFs_SUPPORTED) +PNG_EXTERN void png_handle_oFFs PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_pCAL_SUPPORTED) +PNG_EXTERN void png_handle_pCAL PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_pHYs_SUPPORTED) +PNG_EXTERN void png_handle_pHYs PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_sBIT_SUPPORTED) +PNG_EXTERN void png_handle_sBIT PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_sCAL_SUPPORTED) +PNG_EXTERN void png_handle_sCAL PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_sPLT_SUPPORTED) +extern void png_handle_sPLT PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif /* PNG_READ_sPLT_SUPPORTED */ + +#if defined(PNG_READ_sRGB_SUPPORTED) +PNG_EXTERN void png_handle_sRGB PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_tEXt_SUPPORTED) +PNG_EXTERN void png_handle_tEXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_tIME_SUPPORTED) +PNG_EXTERN void png_handle_tIME PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_tRNS_SUPPORTED) +PNG_EXTERN void png_handle_tRNS PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_zTXt_SUPPORTED) +PNG_EXTERN void png_handle_zTXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +PNG_EXTERN void png_handle_unknown PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); + +PNG_EXTERN void png_check_chunk_name PNGARG((png_structp png_ptr, + png_bytep chunk_name)); + +/* handle the transformations for reading and writing */ +PNG_EXTERN void png_do_read_transformations PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_do_write_transformations PNGARG((png_structp png_ptr)); + +PNG_EXTERN void png_init_read_transformations PNGARG((png_structp png_ptr)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void png_push_read_chunk PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_read_sig PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_check_crc PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_crc_skip PNGARG((png_structp png_ptr, + png_uint_32 length)); +PNG_EXTERN void png_push_crc_finish PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_save_buffer PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_restore_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t buffer_length)); +PNG_EXTERN void png_push_read_IDAT PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_process_IDAT_data PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t buffer_length)); +PNG_EXTERN void png_push_process_row PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_handle_unknown PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_have_info PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_have_end PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_have_row PNGARG((png_structp png_ptr, png_bytep row)); +PNG_EXTERN void png_push_read_end PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_process_some_data PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_read_push_finish_row PNGARG((png_structp png_ptr)); +#if defined(PNG_READ_tEXt_SUPPORTED) +PNG_EXTERN void png_push_handle_tEXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_tEXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) +PNG_EXTERN void png_push_handle_zTXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_zTXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) +PNG_EXTERN void png_push_handle_iTXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_iTXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXTERN void png_do_read_intrapixel PNGARG((png_row_infop row_info, + png_bytep row)); +PNG_EXTERN void png_do_write_intrapixel PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +/* png.c */ /* PRIVATE */ +PNG_EXTERN void png_init_mmx_flags PNGARG((png_structp png_ptr)); +#endif +/* Maintainer: Put new private prototypes here ^ and in libpngpf.3 */ + +#endif /* PNG_INTERNAL */ + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* do not put anything past this line */ +#endif /* PNG_H */ diff --git a/com32/include/pngconf.h b/com32/include/pngconf.h new file mode 100644 index 0000000..3ac628c --- /dev/null +++ b/com32/include/pngconf.h @@ -0,0 +1,1437 @@ + +/* pngconf.h - machine configurable file for libpng + * + * libpng version 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +/* Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +#define PNG_1_2_X + +/* + * PNG_USER_CONFIG has to be defined on the compiler command line. This + * includes the resource compiler for Windows DLL configurations. + */ +#ifdef PNG_USER_CONFIG +#include "pngusr.h" +#endif + +/* + * Added at libpng-1.2.8 + * + * If you create a private DLL you need to define in "pngusr.h" the followings: + * #define PNG_USER_PRIVATEBUILD + * e.g. #define PNG_USER_PRIVATEBUILD "Build by MyCompany for xyz reasons." + * #define PNG_USER_DLLFNAME_POSTFIX + * e.g. // private DLL "libpng13gx.dll" + * #define PNG_USER_DLLFNAME_POSTFIX "gx" + * + * The following macros are also at your disposal if you want to complete the + * DLL VERSIONINFO structure. + * - PNG_USER_VERSIONINFO_COMMENTS + * - PNG_USER_VERSIONINFO_COMPANYNAME + * - PNG_USER_VERSIONINFO_LEGALTRADEMARKS + */ + +#ifdef __STDC__ +#ifdef SPECIALBUILD +# pragma message("PNG_LIBPNG_SPECIALBUILD (and deprecated SPECIALBUILD)\ + are now LIBPNG reserved macros. Use PNG_USER_PRIVATEBUILD instead.") +#endif + +#ifdef PRIVATEBUILD +# pragma message("PRIVATEBUILD is deprecated. Use\ + PNG_USER_PRIVATEBUILD instead.") +# define PNG_USER_PRIVATEBUILD PRIVATEBUILD +#endif +#endif /* __STDC__ */ + +#ifndef PNG_VERSION_INFO_ONLY + +/* End of material added to libpng-1.2.8 */ + +/* This is the size of the compression buffer, and thus the size of + * an IDAT chunk. Make this whatever size you feel is best for your + * machine. One of these will be allocated per png_struct. When this + * is full, it writes the data to the disk, and does some other + * calculations. Making this an extremely small size will slow + * the library down, but you may want to experiment to determine + * where it becomes significant, if you are concerned with memory + * usage. Note that zlib allocates at least 32Kb also. For readers, + * this describes the size of the buffer available to read the data in. + * Unless this gets smaller than the size of a row (compressed), + * it should not make much difference how big this is. + */ + +#ifndef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 8192 +#endif + +/* Enable if you want a write-only libpng */ + +#ifndef PNG_NO_READ_SUPPORTED +# define PNG_READ_SUPPORTED +#endif + +/* Enable if you want a read-only libpng */ + +#ifndef PNG_NO_WRITE_SUPPORTED +# define PNG_WRITE_SUPPORTED +#endif + +/* Enabled by default in 1.2.0. You can disable this if you don't need to + support PNGs that are embedded in MNG datastreams */ +#if !defined(PNG_1_0_X) && !defined(PNG_NO_MNG_FEATURES) +# ifndef PNG_MNG_FEATURES_SUPPORTED +# define PNG_MNG_FEATURES_SUPPORTED +# endif +#endif + +#ifndef PNG_NO_FLOATING_POINT_SUPPORTED +# ifndef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FLOATING_POINT_SUPPORTED +# endif +#endif + +/* If you are running on a machine where you cannot allocate more + * than 64K of memory at once, uncomment this. While libpng will not + * normally need that much memory in a chunk (unless you load up a very + * large file), zlib needs to know how big of a chunk it can use, and + * libpng thus makes sure to check any memory allocation to verify it + * will fit into memory. +#define PNG_MAX_MALLOC_64K + */ +#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) +# define PNG_MAX_MALLOC_64K +#endif + +/* Special munging to support doing things the 'cygwin' way: + * 'Normal' png-on-win32 defines/defaults: + * PNG_BUILD_DLL -- building dll + * PNG_USE_DLL -- building an application, linking to dll + * (no define) -- building static library, or building an + * application and linking to the static lib + * 'Cygwin' defines/defaults: + * PNG_BUILD_DLL -- (ignored) building the dll + * (no define) -- (ignored) building an application, linking to the dll + * PNG_STATIC -- (ignored) building the static lib, or building an + * application that links to the static lib. + * ALL_STATIC -- (ignored) building various static libs, or building an + * application that links to the static libs. + * Thus, + * a cygwin user should define either PNG_BUILD_DLL or PNG_STATIC, and + * this bit of #ifdefs will define the 'correct' config variables based on + * that. If a cygwin user *wants* to define 'PNG_USE_DLL' that's okay, but + * unnecessary. + * + * Also, the precedence order is: + * ALL_STATIC (since we can't #undef something outside our namespace) + * PNG_BUILD_DLL + * PNG_STATIC + * (nothing) == PNG_USE_DLL + * + * CYGWIN (2002-01-20): The preceding is now obsolete. With the advent + * of auto-import in binutils, we no longer need to worry about + * __declspec(dllexport) / __declspec(dllimport) and friends. Therefore, + * we don't need to worry about PNG_STATIC or ALL_STATIC when it comes + * to __declspec() stuff. However, we DO need to worry about + * PNG_BUILD_DLL and PNG_STATIC because those change some defaults + * such as CONSOLE_IO and whether GLOBAL_ARRAYS are allowed. + */ +#if defined(__CYGWIN__) +# if defined(ALL_STATIC) +# if defined(PNG_BUILD_DLL) +# undef PNG_BUILD_DLL +# endif +# if defined(PNG_USE_DLL) +# undef PNG_USE_DLL +# endif +# if defined(PNG_DLL) +# undef PNG_DLL +# endif +# if !defined(PNG_STATIC) +# define PNG_STATIC +# endif +# else +# if defined (PNG_BUILD_DLL) +# if defined(PNG_STATIC) +# undef PNG_STATIC +# endif +# if defined(PNG_USE_DLL) +# undef PNG_USE_DLL +# endif +# if !defined(PNG_DLL) +# define PNG_DLL +# endif +# else +# if defined(PNG_STATIC) +# if defined(PNG_USE_DLL) +# undef PNG_USE_DLL +# endif +# if defined(PNG_DLL) +# undef PNG_DLL +# endif +# else +# if !defined(PNG_USE_DLL) +# define PNG_USE_DLL +# endif +# if !defined(PNG_DLL) +# define PNG_DLL +# endif +# endif +# endif +# endif +#endif + +/* This protects us against compilers that run on a windowing system + * and thus don't have or would rather us not use the stdio types: + * stdin, stdout, and stderr. The only one currently used is stderr + * in png_error() and png_warning(). #defining PNG_NO_CONSOLE_IO will + * prevent these from being compiled and used. #defining PNG_NO_STDIO + * will also prevent these, plus will prevent the entire set of stdio + * macros and functions (FILE *, printf, etc.) from being compiled and used, + * unless (PNG_DEBUG > 0) has been #defined. + * + * #define PNG_NO_CONSOLE_IO + * #define PNG_NO_STDIO + */ + +#if defined(_WIN32_WCE) +# include + /* Console I/O functions are not supported on WindowsCE */ +# define PNG_NO_CONSOLE_IO +# ifdef PNG_DEBUG +# undef PNG_DEBUG +# endif +#endif + +#ifdef PNG_BUILD_DLL +# ifndef PNG_CONSOLE_IO_SUPPORTED +# ifndef PNG_NO_CONSOLE_IO +# define PNG_NO_CONSOLE_IO +# endif +# endif +#endif + +# ifdef PNG_NO_STDIO +# ifndef PNG_NO_CONSOLE_IO +# define PNG_NO_CONSOLE_IO +# endif +# ifdef PNG_DEBUG +# if (PNG_DEBUG > 0) +# include +# endif +# endif +# else +# if !defined(_WIN32_WCE) +/* "stdio.h" functions are not supported on WindowsCE */ +# include +# endif +# endif + +/* This macro protects us against machines that don't have function + * prototypes (ie K&R style headers). If your compiler does not handle + * function prototypes, define this macro and use the included ansi2knr. + * I've always been able to use _NO_PROTO as the indicator, but you may + * need to drag the empty declaration out in front of here, or change the + * ifdef to suit your own needs. + */ +#ifndef PNGARG + +#ifdef OF /* zlib prototype munger */ +# define PNGARG(arglist) OF(arglist) +#else + +#ifdef _NO_PROTO +# define PNGARG(arglist) () +# ifndef PNG_TYPECAST_NULL +# define PNG_TYPECAST_NULL +# endif +#else +# define PNGARG(arglist) arglist +#endif /* _NO_PROTO */ + +#endif /* OF */ + +#endif /* PNGARG */ + +/* Try to determine if we are compiling on a Mac. Note that testing for + * just __MWERKS__ is not good enough, because the Codewarrior is now used + * on non-Mac platforms. + */ +#ifndef MACOS +# if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \ + defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC) +# define MACOS +# endif +#endif + +/* enough people need this for various reasons to include it here */ +#if !defined(MACOS) && !defined(RISCOS) && !defined(_WIN32_WCE) +# include +#endif + +#if !defined(PNG_SETJMP_NOT_SUPPORTED) && !defined(PNG_NO_SETJMP_SUPPORTED) +# define PNG_SETJMP_SUPPORTED +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This is an attempt to force a single setjmp behaviour on Linux. If + * the X config stuff didn't define _BSD_SOURCE we wouldn't need this. + */ + +# ifdef __linux__ +# ifdef _BSD_SOURCE +# define PNG_SAVE_BSD_SOURCE +# undef _BSD_SOURCE +# endif +# ifdef _SETJMP_H + /* If you encounter a compiler error here, see the explanation + * near the end of INSTALL. + */ + __png.h__ already includes setjmp.h; + __dont__ include it again.; +# endif +# endif /* __linux__ */ + + /* include setjmp.h for error handling */ +# include + +# ifdef __linux__ +# ifdef PNG_SAVE_BSD_SOURCE +# define _BSD_SOURCE +# undef PNG_SAVE_BSD_SOURCE +# endif +# endif /* __linux__ */ +#endif /* PNG_SETJMP_SUPPORTED */ + +#ifdef BSD +# include +#else +# include +#endif + +/* Other defines for things like memory and the like can go here. */ +#ifdef PNG_INTERNAL + +#include + +/* The functions exported by PNG_EXTERN are PNG_INTERNAL functions, which + * aren't usually used outside the library (as far as I know), so it is + * debatable if they should be exported at all. In the future, when it is + * possible to have run-time registry of chunk-handling functions, some of + * these will be made available again. +#define PNG_EXTERN extern + */ +#define PNG_EXTERN + +/* Other defines specific to compilers can go here. Try to keep + * them inside an appropriate ifdef/endif pair for portability. + */ + +#if defined(PNG_FLOATING_POINT_SUPPORTED) +# if defined(MACOS) + /* We need to check that hasn't already been included earlier + * as it seems it doesn't agree with , yet we should really use + * if possible. + */ +# if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__) +# include +# endif +# else +# include +# endif +# if defined(_AMIGA) && defined(__SASC) && defined(_M68881) + /* Amiga SAS/C: We must include builtin FPU functions when compiling using + * MATH=68881 + */ +# include +# endif +#endif + +/* Codewarrior on NT has linking problems without this. */ +#if (defined(__MWERKS__) && defined(WIN32)) || defined(__STDC__) +# define PNG_ALWAYS_EXTERN +#endif + +/* This provides the non-ANSI (far) memory allocation routines. */ +#if defined(__TURBOC__) && defined(__MSDOS__) +# include +# include +#endif + +/* I have no idea why is this necessary... */ +#if defined(_MSC_VER) && (defined(WIN32) || defined(_Windows) || \ + defined(_WINDOWS) || defined(_WIN32) || defined(__WIN32__)) +# include +#endif + +/* This controls how fine the dithering gets. As this allocates + * a largish chunk of memory (32K), those who are not as concerned + * with dithering quality can decrease some or all of these. + */ +#ifndef PNG_DITHER_RED_BITS +# define PNG_DITHER_RED_BITS 5 +#endif +#ifndef PNG_DITHER_GREEN_BITS +# define PNG_DITHER_GREEN_BITS 5 +#endif +#ifndef PNG_DITHER_BLUE_BITS +# define PNG_DITHER_BLUE_BITS 5 +#endif + +/* This controls how fine the gamma correction becomes when you + * are only interested in 8 bits anyway. Increasing this value + * results in more memory being used, and more pow() functions + * being called to fill in the gamma tables. Don't set this value + * less then 8, and even that may not work (I haven't tested it). + */ + +#ifndef PNG_MAX_GAMMA_8 +# define PNG_MAX_GAMMA_8 11 +#endif + +/* This controls how much a difference in gamma we can tolerate before + * we actually start doing gamma conversion. + */ +#ifndef PNG_GAMMA_THRESHOLD +# define PNG_GAMMA_THRESHOLD 0.05 +#endif + +#endif /* PNG_INTERNAL */ + +/* The following uses const char * instead of char * for error + * and warning message functions, so some compilers won't complain. + * If you do not want to use const, define PNG_NO_CONST here. + */ + +#ifndef PNG_NO_CONST +# define PNG_CONST const +#else +# define PNG_CONST +#endif + +/* The following defines give you the ability to remove code from the + * library that you will not be using. I wish I could figure out how to + * automate this, but I can't do that without making it seriously hard + * on the users. So if you are not using an ability, change the #define + * to and #undef, and that part of the library will not be compiled. If + * your linker can't find a function, you may want to make sure the + * ability is defined here. Some of these depend upon some others being + * defined. I haven't figured out all the interactions here, so you may + * have to experiment awhile to get everything to compile. If you are + * creating or using a shared library, you probably shouldn't touch this, + * as it will affect the size of the structures, and this will cause bad + * things to happen if the library and/or application ever change. + */ + +/* Any features you will not be using can be undef'ed here */ + +/* GR-P, 0.96a: Set "*TRANSFORMS_SUPPORTED as default but allow user + * to turn it off with "*TRANSFORMS_NOT_SUPPORTED" or *PNG_NO_*_TRANSFORMS + * on the compile line, then pick and choose which ones to define without + * having to edit this file. It is safe to use the *TRANSFORMS_NOT_SUPPORTED + * if you only want to have a png-compliant reader/writer but don't need + * any of the extra transformations. This saves about 80 kbytes in a + * typical installation of the library. (PNG_NO_* form added in version + * 1.0.1c, for consistency) + */ + +/* The size of the png_text structure changed in libpng-1.0.6 when + * iTXt is supported. It is turned off by default, to support old apps + * that malloc the png_text structure instead of calling png_set_text() + * and letting libpng malloc it. It will be turned on by default in + * libpng-1.3.0. + */ + +#ifndef PNG_iTXt_SUPPORTED +# if !defined(PNG_READ_iTXt_SUPPORTED) && !defined(PNG_NO_READ_iTXt) +# define PNG_NO_READ_iTXt +# endif +# if !defined(PNG_WRITE_iTXt_SUPPORTED) && !defined(PNG_NO_WRITE_iTXt) +# define PNG_NO_WRITE_iTXt +# endif +#endif + +/* The following support, added after version 1.0.0, can be turned off here en + * masse by defining PNG_LEGACY_SUPPORTED in case you need binary compatibility + * with old applications that require the length of png_struct and png_info + * to remain unchanged. + */ + +#ifdef PNG_LEGACY_SUPPORTED +# define PNG_NO_FREE_ME +# define PNG_NO_READ_UNKNOWN_CHUNKS +# define PNG_NO_WRITE_UNKNOWN_CHUNKS +# define PNG_NO_READ_USER_CHUNKS +# define PNG_NO_READ_iCCP +# define PNG_NO_WRITE_iCCP +# define PNG_NO_READ_iTXt +# define PNG_NO_WRITE_iTXt +# define PNG_NO_READ_sCAL +# define PNG_NO_WRITE_sCAL +# define PNG_NO_READ_sPLT +# define PNG_NO_WRITE_sPLT +# define PNG_NO_INFO_IMAGE +# define PNG_NO_READ_RGB_TO_GRAY +# define PNG_NO_READ_USER_TRANSFORM +# define PNG_NO_WRITE_USER_TRANSFORM +# define PNG_NO_USER_MEM +# define PNG_NO_READ_EMPTY_PLTE +# define PNG_NO_MNG_FEATURES +# define PNG_NO_FIXED_POINT_SUPPORTED +#endif + +/* Ignore attempt to turn off both floating and fixed point support */ +#if !defined(PNG_FLOATING_POINT_SUPPORTED) || \ + !defined(PNG_NO_FIXED_POINT_SUPPORTED) +# define PNG_FIXED_POINT_SUPPORTED +#endif + +#ifndef PNG_NO_FREE_ME +# define PNG_FREE_ME_SUPPORTED +#endif + +#if defined(PNG_READ_SUPPORTED) + +#if !defined(PNG_READ_TRANSFORMS_NOT_SUPPORTED) && \ + !defined(PNG_NO_READ_TRANSFORMS) +# define PNG_READ_TRANSFORMS_SUPPORTED +#endif + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +# ifndef PNG_NO_READ_EXPAND +# define PNG_READ_EXPAND_SUPPORTED +# endif +# ifndef PNG_NO_READ_SHIFT +# define PNG_READ_SHIFT_SUPPORTED +# endif +# ifndef PNG_NO_READ_PACK +# define PNG_READ_PACK_SUPPORTED +# endif +# ifndef PNG_NO_READ_BGR +# define PNG_READ_BGR_SUPPORTED +# endif +# ifndef PNG_NO_READ_SWAP +# define PNG_READ_SWAP_SUPPORTED +# endif +# ifndef PNG_NO_READ_PACKSWAP +# define PNG_READ_PACKSWAP_SUPPORTED +# endif +# ifndef PNG_NO_READ_INVERT +# define PNG_READ_INVERT_SUPPORTED +# endif +# ifndef PNG_NO_READ_DITHER +# define PNG_READ_DITHER_SUPPORTED +# endif +# ifndef PNG_NO_READ_BACKGROUND +# define PNG_READ_BACKGROUND_SUPPORTED +# endif +# ifndef PNG_NO_READ_16_TO_8 +# define PNG_READ_16_TO_8_SUPPORTED +# endif +# ifndef PNG_NO_READ_FILLER +# define PNG_READ_FILLER_SUPPORTED +# endif +# ifndef PNG_NO_READ_GAMMA +# define PNG_READ_GAMMA_SUPPORTED +# endif +# ifndef PNG_NO_READ_GRAY_TO_RGB +# define PNG_READ_GRAY_TO_RGB_SUPPORTED +# endif +# ifndef PNG_NO_READ_SWAP_ALPHA +# define PNG_READ_SWAP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_INVERT_ALPHA +# define PNG_READ_INVERT_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_STRIP_ALPHA +# define PNG_READ_STRIP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_USER_TRANSFORM +# define PNG_READ_USER_TRANSFORM_SUPPORTED +# endif +# ifndef PNG_NO_READ_RGB_TO_GRAY +# define PNG_READ_RGB_TO_GRAY_SUPPORTED +# endif +#endif /* PNG_READ_TRANSFORMS_SUPPORTED */ + +#if !defined(PNG_NO_PROGRESSIVE_READ) && \ + !defined(PNG_PROGRESSIVE_READ_NOT_SUPPORTED) /* if you don't do progressive */ +# define PNG_PROGRESSIVE_READ_SUPPORTED /* reading. This is not talking */ +#endif /* about interlacing capability! You'll */ + /* still have interlacing unless you change the following line: */ + +#define PNG_READ_INTERLACING_SUPPORTED /* required for PNG-compliant decoders */ + +#ifndef PNG_NO_READ_COMPOSITE_NODIV +# ifndef PNG_NO_READ_COMPOSITED_NODIV /* libpng-1.0.x misspelling */ +# define PNG_READ_COMPOSITE_NODIV_SUPPORTED /* well tested on Intel, SGI */ +# endif +#endif + +/* Deprecated, will be removed from version 2.0.0. + Use PNG_MNG_FEATURES_SUPPORTED instead. */ +#ifndef PNG_NO_READ_EMPTY_PLTE +# define PNG_READ_EMPTY_PLTE_SUPPORTED +#endif + +#endif /* PNG_READ_SUPPORTED */ + +#if defined(PNG_WRITE_SUPPORTED) + +# if !defined(PNG_WRITE_TRANSFORMS_NOT_SUPPORTED) && \ + !defined(PNG_NO_WRITE_TRANSFORMS) +# define PNG_WRITE_TRANSFORMS_SUPPORTED +#endif + +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED +# ifndef PNG_NO_WRITE_SHIFT +# define PNG_WRITE_SHIFT_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_PACK +# define PNG_WRITE_PACK_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_BGR +# define PNG_WRITE_BGR_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_SWAP +# define PNG_WRITE_SWAP_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_PACKSWAP +# define PNG_WRITE_PACKSWAP_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_INVERT +# define PNG_WRITE_INVERT_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_FILLER +# define PNG_WRITE_FILLER_SUPPORTED /* same as WRITE_STRIP_ALPHA */ +# endif +# ifndef PNG_NO_WRITE_SWAP_ALPHA +# define PNG_WRITE_SWAP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_INVERT_ALPHA +# define PNG_WRITE_INVERT_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_USER_TRANSFORM +# define PNG_WRITE_USER_TRANSFORM_SUPPORTED +# endif +#endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */ + +#define PNG_WRITE_INTERLACING_SUPPORTED /* not required for PNG-compliant + encoders, but can cause trouble + if left undefined */ + +#if !defined(PNG_NO_WRITE_WEIGHTED_FILTER) && \ + defined(PNG_FLOATING_POINT_SUPPORTED) +# define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#endif + +#ifndef PNG_NO_WRITE_FLUSH +# define PNG_WRITE_FLUSH_SUPPORTED +#endif + +/* Deprecated, see PNG_MNG_FEATURES_SUPPORTED, above */ +#ifndef PNG_NO_WRITE_EMPTY_PLTE +# define PNG_WRITE_EMPTY_PLTE_SUPPORTED +#endif + +#endif /* PNG_WRITE_SUPPORTED */ + +#ifndef PNG_1_0_X +# ifndef PNG_NO_ERROR_NUMBERS +# define PNG_ERROR_NUMBERS_SUPPORTED +# endif +#endif /* PNG_1_0_X */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +# ifndef PNG_NO_USER_TRANSFORM_PTR +# define PNG_USER_TRANSFORM_PTR_SUPPORTED +# endif +#endif + +#ifndef PNG_NO_STDIO +# define PNG_TIME_RFC1123_SUPPORTED +#endif + +/* This adds extra functions in pngget.c for accessing data from the + * info pointer (added in version 0.99) + * png_get_image_width() + * png_get_image_height() + * png_get_bit_depth() + * png_get_color_type() + * png_get_compression_type() + * png_get_filter_type() + * png_get_interlace_type() + * png_get_pixel_aspect_ratio() + * png_get_pixels_per_meter() + * png_get_x_offset_pixels() + * png_get_y_offset_pixels() + * png_get_x_offset_microns() + * png_get_y_offset_microns() + */ +#if !defined(PNG_NO_EASY_ACCESS) && !defined(PNG_EASY_ACCESS_SUPPORTED) +# define PNG_EASY_ACCESS_SUPPORTED +#endif + +/* PNG_ASSEMBLER_CODE was enabled by default in version 1.2.0 + even when PNG_USE_PNGVCRD or PNG_USE_PNGGCCRD is not defined */ +#if defined(PNG_READ_SUPPORTED) && !defined(PNG_NO_ASSEMBLER_CODE) +# ifndef PNG_ASSEMBLER_CODE_SUPPORTED +# define PNG_ASSEMBLER_CODE_SUPPORTED +# endif +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_MMX_CODE_SUPPORTED +# endif +#endif + +/* If you are sure that you don't need thread safety and you are compiling + with PNG_USE_PNGCCRD for an MMX application, you can define this for + faster execution. See pnggccrd.c. +#define PNG_THREAD_UNSAFE_OK +*/ + +#if !defined(PNG_1_0_X) +#if !defined(PNG_NO_USER_MEM) && !defined(PNG_USER_MEM_SUPPORTED) +# define PNG_USER_MEM_SUPPORTED +#endif +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.2.6 */ +#if !defined(PNG_1_0_X) +#ifndef PNG_SET_USER_LIMITS_SUPPORTED +#if !defined(PNG_NO_SET_USER_LIMITS) && !defined(PNG_SET_USER_LIMITS_SUPPORTED) +# define PNG_SET_USER_LIMITS_SUPPORTED +#endif +#endif +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.0.16 and 1.2.6. To accept all valid PNGS no matter + * how large, set these limits to 0x7fffffffL + */ +#ifndef PNG_USER_WIDTH_MAX +# define PNG_USER_WIDTH_MAX 1000000L +#endif +#ifndef PNG_USER_HEIGHT_MAX +# define PNG_USER_HEIGHT_MAX 1000000L +#endif + +/* These are currently experimental features, define them if you want */ + +/* very little testing */ +/* +#ifdef PNG_READ_SUPPORTED +# ifndef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +# define PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +# endif +#endif +*/ + +/* This is only for PowerPC big-endian and 680x0 systems */ +/* some testing */ +/* +#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED +# define PNG_READ_BIG_ENDIAN_SUPPORTED +#endif +*/ + +/* Buggy compilers (e.g., gcc 2.7.2.2) need this */ +/* +#define PNG_NO_POINTER_INDEXING +*/ + +/* These functions are turned off by default, as they will be phased out. */ +/* +#define PNG_USELESS_TESTS_SUPPORTED +#define PNG_CORRECT_PALETTE_SUPPORTED +*/ + +/* Any chunks you are not interested in, you can undef here. The + * ones that allocate memory may be expecially important (hIST, + * tEXt, zTXt, tRNS, pCAL). Others will just save time and make png_info + * a bit smaller. + */ + +#if defined(PNG_READ_SUPPORTED) && \ + !defined(PNG_READ_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ + !defined(PNG_NO_READ_ANCILLARY_CHUNKS) +# define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#endif + +#if defined(PNG_WRITE_SUPPORTED) && \ + !defined(PNG_WRITE_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ + !defined(PNG_NO_WRITE_ANCILLARY_CHUNKS) +# define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#endif + +#ifdef PNG_READ_ANCILLARY_CHUNKS_SUPPORTED + +#ifdef PNG_NO_READ_TEXT +# define PNG_NO_READ_iTXt +# define PNG_NO_READ_tEXt +# define PNG_NO_READ_zTXt +#endif +#ifndef PNG_NO_READ_bKGD +# define PNG_READ_bKGD_SUPPORTED +# define PNG_bKGD_SUPPORTED +#endif +#ifndef PNG_NO_READ_cHRM +# define PNG_READ_cHRM_SUPPORTED +# define PNG_cHRM_SUPPORTED +#endif +#ifndef PNG_NO_READ_gAMA +# define PNG_READ_gAMA_SUPPORTED +# define PNG_gAMA_SUPPORTED +#endif +#ifndef PNG_NO_READ_hIST +# define PNG_READ_hIST_SUPPORTED +# define PNG_hIST_SUPPORTED +#endif +#ifndef PNG_NO_READ_iCCP +# define PNG_READ_iCCP_SUPPORTED +# define PNG_iCCP_SUPPORTED +#endif +#ifndef PNG_NO_READ_iTXt +# ifndef PNG_READ_iTXt_SUPPORTED +# define PNG_READ_iTXt_SUPPORTED +# endif +# ifndef PNG_iTXt_SUPPORTED +# define PNG_iTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_READ_oFFs +# define PNG_READ_oFFs_SUPPORTED +# define PNG_oFFs_SUPPORTED +#endif +#ifndef PNG_NO_READ_pCAL +# define PNG_READ_pCAL_SUPPORTED +# define PNG_pCAL_SUPPORTED +#endif +#ifndef PNG_NO_READ_sCAL +# define PNG_READ_sCAL_SUPPORTED +# define PNG_sCAL_SUPPORTED +#endif +#ifndef PNG_NO_READ_pHYs +# define PNG_READ_pHYs_SUPPORTED +# define PNG_pHYs_SUPPORTED +#endif +#ifndef PNG_NO_READ_sBIT +# define PNG_READ_sBIT_SUPPORTED +# define PNG_sBIT_SUPPORTED +#endif +#ifndef PNG_NO_READ_sPLT +# define PNG_READ_sPLT_SUPPORTED +# define PNG_sPLT_SUPPORTED +#endif +#ifndef PNG_NO_READ_sRGB +# define PNG_READ_sRGB_SUPPORTED +# define PNG_sRGB_SUPPORTED +#endif +#ifndef PNG_NO_READ_tEXt +# define PNG_READ_tEXt_SUPPORTED +# define PNG_tEXt_SUPPORTED +#endif +#ifndef PNG_NO_READ_tIME +# define PNG_READ_tIME_SUPPORTED +# define PNG_tIME_SUPPORTED +#endif +#ifndef PNG_NO_READ_tRNS +# define PNG_READ_tRNS_SUPPORTED +# define PNG_tRNS_SUPPORTED +#endif +#ifndef PNG_NO_READ_zTXt +# define PNG_READ_zTXt_SUPPORTED +# define PNG_zTXt_SUPPORTED +#endif +#ifndef PNG_NO_READ_UNKNOWN_CHUNKS +# define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_UNKNOWN_CHUNKS_SUPPORTED +# endif +# ifndef PNG_NO_HANDLE_AS_UNKNOWN +# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# endif +#endif +#if !defined(PNG_NO_READ_USER_CHUNKS) && \ + defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) +# define PNG_READ_USER_CHUNKS_SUPPORTED +# define PNG_USER_CHUNKS_SUPPORTED +# ifdef PNG_NO_READ_UNKNOWN_CHUNKS +# undef PNG_NO_READ_UNKNOWN_CHUNKS +# endif +# ifdef PNG_NO_HANDLE_AS_UNKNOWN +# undef PNG_NO_HANDLE_AS_UNKNOWN +# endif +#endif +#ifndef PNG_NO_READ_OPT_PLTE +# define PNG_READ_OPT_PLTE_SUPPORTED /* only affects support of the */ +#endif /* optional PLTE chunk in RGB and RGBA images */ +#if defined(PNG_READ_iTXt_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) || \ + defined(PNG_READ_zTXt_SUPPORTED) +# define PNG_READ_TEXT_SUPPORTED +# define PNG_TEXT_SUPPORTED +#endif + +#endif /* PNG_READ_ANCILLARY_CHUNKS_SUPPORTED */ + +#ifdef PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED + +#ifdef PNG_NO_WRITE_TEXT +# define PNG_NO_WRITE_iTXt +# define PNG_NO_WRITE_tEXt +# define PNG_NO_WRITE_zTXt +#endif +#ifndef PNG_NO_WRITE_bKGD +# define PNG_WRITE_bKGD_SUPPORTED +# ifndef PNG_bKGD_SUPPORTED +# define PNG_bKGD_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_cHRM +# define PNG_WRITE_cHRM_SUPPORTED +# ifndef PNG_cHRM_SUPPORTED +# define PNG_cHRM_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_gAMA +# define PNG_WRITE_gAMA_SUPPORTED +# ifndef PNG_gAMA_SUPPORTED +# define PNG_gAMA_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_hIST +# define PNG_WRITE_hIST_SUPPORTED +# ifndef PNG_hIST_SUPPORTED +# define PNG_hIST_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_iCCP +# define PNG_WRITE_iCCP_SUPPORTED +# ifndef PNG_iCCP_SUPPORTED +# define PNG_iCCP_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_iTXt +# ifndef PNG_WRITE_iTXt_SUPPORTED +# define PNG_WRITE_iTXt_SUPPORTED +# endif +# ifndef PNG_iTXt_SUPPORTED +# define PNG_iTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_oFFs +# define PNG_WRITE_oFFs_SUPPORTED +# ifndef PNG_oFFs_SUPPORTED +# define PNG_oFFs_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_pCAL +# define PNG_WRITE_pCAL_SUPPORTED +# ifndef PNG_pCAL_SUPPORTED +# define PNG_pCAL_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sCAL +# define PNG_WRITE_sCAL_SUPPORTED +# ifndef PNG_sCAL_SUPPORTED +# define PNG_sCAL_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_pHYs +# define PNG_WRITE_pHYs_SUPPORTED +# ifndef PNG_pHYs_SUPPORTED +# define PNG_pHYs_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sBIT +# define PNG_WRITE_sBIT_SUPPORTED +# ifndef PNG_sBIT_SUPPORTED +# define PNG_sBIT_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sPLT +# define PNG_WRITE_sPLT_SUPPORTED +# ifndef PNG_sPLT_SUPPORTED +# define PNG_sPLT_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sRGB +# define PNG_WRITE_sRGB_SUPPORTED +# ifndef PNG_sRGB_SUPPORTED +# define PNG_sRGB_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tEXt +# define PNG_WRITE_tEXt_SUPPORTED +# ifndef PNG_tEXt_SUPPORTED +# define PNG_tEXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tIME +# define PNG_WRITE_tIME_SUPPORTED +# ifndef PNG_tIME_SUPPORTED +# define PNG_tIME_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tRNS +# define PNG_WRITE_tRNS_SUPPORTED +# ifndef PNG_tRNS_SUPPORTED +# define PNG_tRNS_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_zTXt +# define PNG_WRITE_zTXt_SUPPORTED +# ifndef PNG_zTXt_SUPPORTED +# define PNG_zTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_UNKNOWN_CHUNKS +# define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_UNKNOWN_CHUNKS_SUPPORTED +# endif +# ifndef PNG_NO_HANDLE_AS_UNKNOWN +# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# endif +# endif +#endif +#if defined(PNG_WRITE_iTXt_SUPPORTED) || defined(PNG_WRITE_tEXt_SUPPORTED) || \ + defined(PNG_WRITE_zTXt_SUPPORTED) +# define PNG_WRITE_TEXT_SUPPORTED +# ifndef PNG_TEXT_SUPPORTED +# define PNG_TEXT_SUPPORTED +# endif +#endif + +#endif /* PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED */ + +/* Turn this off to disable png_read_png() and + * png_write_png() and leave the row_pointers member + * out of the info structure. + */ +#ifndef PNG_NO_INFO_IMAGE +# define PNG_INFO_IMAGE_SUPPORTED +#endif + +/* need the time information for reading tIME chunks */ +#if defined(PNG_tIME_SUPPORTED) +# if !defined(_WIN32_WCE) && !defined(__COM32__) + /* "time.h" functions are not supported on WindowsCE */ +# include +# endif +#endif + +/* Some typedefs to get us started. These should be safe on most of the + * common platforms. The typedefs should be at least as large as the + * numbers suggest (a png_uint_32 must be at least 32 bits long), but they + * don't have to be exactly that size. Some compilers dislike passing + * unsigned shorts as function parameters, so you may be better off using + * unsigned int for png_uint_16. Likewise, for 64-bit systems, you may + * want to have unsigned int for png_uint_32 instead of unsigned long. + */ + +typedef unsigned long png_uint_32; +typedef long png_int_32; +typedef unsigned short png_uint_16; +typedef short png_int_16; +typedef unsigned char png_byte; + +/* This is usually size_t. It is typedef'ed just in case you need it to + change (I'm not sure if you will or not, so I thought I'd be safe) */ +#ifdef PNG_SIZE_T + typedef PNG_SIZE_T png_size_t; +# define png_sizeof(x) png_convert_size(sizeof (x)) +#else + typedef size_t png_size_t; +# define png_sizeof(x) sizeof (x) +#endif + +/* The following is needed for medium model support. It cannot be in the + * PNG_INTERNAL section. Needs modification for other compilers besides + * MSC. Model independent support declares all arrays and pointers to be + * large using the far keyword. The zlib version used must also support + * model independent data. As of version zlib 1.0.4, the necessary changes + * have been made in zlib. The USE_FAR_KEYWORD define triggers other + * changes that are needed. (Tim Wegner) + */ + +/* Separate compiler dependencies (problem here is that zlib.h always + defines FAR. (SJT) */ +#ifdef __BORLANDC__ +# if defined(__LARGE__) || defined(__HUGE__) || defined(__COMPACT__) +# define LDATA 1 +# else +# define LDATA 0 +# endif + /* GRR: why is Cygwin in here? Cygwin is not Borland C... */ +# if !defined(__WIN32__) && !defined(__FLAT__) && !defined(__CYGWIN__) +# define PNG_MAX_MALLOC_64K +# if (LDATA != 1) +# ifndef FAR +# define FAR __far +# endif +# define USE_FAR_KEYWORD +# endif /* LDATA != 1 */ + /* Possibly useful for moving data out of default segment. + * Uncomment it if you want. Could also define FARDATA as + * const if your compiler supports it. (SJT) +# define FARDATA FAR + */ +# endif /* __WIN32__, __FLAT__, __CYGWIN__ */ +#endif /* __BORLANDC__ */ + + +/* Suggest testing for specific compiler first before testing for + * FAR. The Watcom compiler defines both __MEDIUM__ and M_I86MM, + * making reliance oncertain keywords suspect. (SJT) + */ + +/* MSC Medium model */ +#if defined(FAR) +# if defined(M_I86MM) +# define USE_FAR_KEYWORD +# define FARDATA FAR +# include +# endif +#endif + +/* SJT: default case */ +#ifndef FAR +# define FAR +#endif + +/* At this point FAR is always defined */ +#ifndef FARDATA +# define FARDATA +#endif + +/* Typedef for floating-point numbers that are converted + to fixed-point with a multiple of 100,000, e.g., int_gamma */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void FAR * png_voidp; +typedef png_byte FAR * png_bytep; +typedef png_uint_32 FAR * png_uint_32p; +typedef png_int_32 FAR * png_int_32p; +typedef png_uint_16 FAR * png_uint_16p; +typedef png_int_16 FAR * png_int_16p; +typedef PNG_CONST char FAR * png_const_charp; +typedef char FAR * png_charp; +typedef png_fixed_point FAR * png_fixed_point_p; + +#ifndef PNG_NO_STDIO +#if defined(_WIN32_WCE) +typedef HANDLE png_FILE_p; +#else +typedef FILE * png_FILE_p; +#endif +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double FAR * png_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte FAR * FAR * png_bytepp; +typedef png_uint_32 FAR * FAR * png_uint_32pp; +typedef png_int_32 FAR * FAR * png_int_32pp; +typedef png_uint_16 FAR * FAR * png_uint_16pp; +typedef png_int_16 FAR * FAR * png_int_16pp; +typedef PNG_CONST char FAR * FAR * png_const_charpp; +typedef char FAR * FAR * png_charpp; +typedef png_fixed_point FAR * FAR * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double FAR * FAR * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char FAR * FAR * FAR * png_charppp; + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* SPC - Is this stuff deprecated? */ +/* It'll be removed as of libpng-1.3.0 - GR-P */ +/* libpng typedefs for types in zlib. If zlib changes + * or another compression library is used, then change these. + * Eliminates need to change all the source files. + */ +typedef charf * png_zcharp; +typedef charf * FAR * png_zcharpp; +typedef z_stream FAR * png_zstreamp; +#endif /* (PNG_1_0_X) || defined(PNG_1_2_X) */ + +/* + * Define PNG_BUILD_DLL if the module being built is a Windows + * LIBPNG DLL. + * + * Define PNG_USE_DLL if you want to *link* to the Windows LIBPNG DLL. + * It is equivalent to Microsoft predefined macro _DLL that is + * automatically defined when you compile using the share + * version of the CRT (C Run-Time library) + * + * The cygwin mods make this behavior a little different: + * Define PNG_BUILD_DLL if you are building a dll for use with cygwin + * Define PNG_STATIC if you are building a static library for use with cygwin, + * -or- if you are building an application that you want to link to the + * static library. + * PNG_USE_DLL is defined by default (no user action needed) unless one of + * the other flags is defined. + */ + +#if !defined(PNG_DLL) && (defined(PNG_BUILD_DLL) || defined(PNG_USE_DLL)) +# define PNG_DLL +#endif +/* If CYGWIN, then disallow GLOBAL ARRAYS unless building a static lib. + * When building a static lib, default to no GLOBAL ARRAYS, but allow + * command-line override + */ +#if defined(__CYGWIN__) +# if !defined(PNG_STATIC) +# if defined(PNG_USE_GLOBAL_ARRAYS) +# undef PNG_USE_GLOBAL_ARRAYS +# endif +# if !defined(PNG_USE_LOCAL_ARRAYS) +# define PNG_USE_LOCAL_ARRAYS +# endif +# else +# if defined(PNG_USE_LOCAL_ARRAYS) || defined(PNG_NO_GLOBAL_ARRAYS) +# if defined(PNG_USE_GLOBAL_ARRAYS) +# undef PNG_USE_GLOBAL_ARRAYS +# endif +# endif +# endif +# if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS) +# define PNG_USE_LOCAL_ARRAYS +# endif +#endif + +/* Do not use global arrays (helps with building DLL's) + * They are no longer used in libpng itself, since version 1.0.5c, + * but might be required for some pre-1.0.5c applications. + */ +#if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS) +# if defined(PNG_NO_GLOBAL_ARRAYS) || (defined(__GNUC__) && defined(PNG_DLL)) +# define PNG_USE_LOCAL_ARRAYS +# else +# define PNG_USE_GLOBAL_ARRAYS +# endif +#endif + +#if defined(__CYGWIN__) +# undef PNGAPI +# define PNGAPI __cdecl +# undef PNG_IMPEXP +# define PNG_IMPEXP +#endif + +/* If you define PNGAPI, e.g., with compiler option "-DPNGAPI=__stdcall", + * you may get warnings regarding the linkage of png_zalloc and png_zfree. + * Don't ignore those warnings; you must also reset the default calling + * convention in your compiler to match your PNGAPI, and you must build + * zlib and your applications the same way you build libpng. + */ + +#if defined(__MINGW32__) && !defined(PNG_MODULEDEF) +# ifndef PNG_NO_MODULEDEF +# define PNG_NO_MODULEDEF +# endif +#endif + +#if !defined(PNG_IMPEXP) && defined(PNG_BUILD_DLL) && !defined(PNG_NO_MODULEDEF) +# define PNG_IMPEXP +#endif + +#if defined(PNG_DLL) || defined(_DLL) || defined(__DLL__ ) || \ + (( defined(_Windows) || defined(_WINDOWS) || \ + defined(WIN32) || defined(_WIN32) || defined(__WIN32__) )) + +# ifndef PNGAPI +# if defined(__GNUC__) || (defined (_MSC_VER) && (_MSC_VER >= 800)) +# define PNGAPI __cdecl +# else +# define PNGAPI _cdecl +# endif +# endif + +# if !defined(PNG_IMPEXP) && (!defined(PNG_DLL) || \ + 0 /* WINCOMPILER_WITH_NO_SUPPORT_FOR_DECLIMPEXP */) +# define PNG_IMPEXP +# endif + +# if !defined(PNG_IMPEXP) + +# define PNG_EXPORT_TYPE1(type,symbol) PNG_IMPEXP type PNGAPI symbol +# define PNG_EXPORT_TYPE2(type,symbol) type PNG_IMPEXP PNGAPI symbol + + /* Borland/Microsoft */ +# if defined(_MSC_VER) || defined(__BORLANDC__) +# if (_MSC_VER >= 800) || (__BORLANDC__ >= 0x500) +# define PNG_EXPORT PNG_EXPORT_TYPE1 +# else +# define PNG_EXPORT PNG_EXPORT_TYPE2 +# if defined(PNG_BUILD_DLL) +# define PNG_IMPEXP __export +# else +# define PNG_IMPEXP /*__import */ /* doesn't exist AFAIK in + VC++ */ +# endif /* Exists in Borland C++ for + C++ classes (== huge) */ +# endif +# endif + +# if !defined(PNG_IMPEXP) +# if defined(PNG_BUILD_DLL) +# define PNG_IMPEXP __declspec(dllexport) +# else +# define PNG_IMPEXP __declspec(dllimport) +# endif +# endif +# endif /* PNG_IMPEXP */ +#else /* !(DLL || non-cygwin WINDOWS) */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# ifndef PNGAPI +# define PNGAPI _System +# endif +# else +# if 0 /* ... other platforms, with other meanings */ +# endif +# endif +#endif + +#ifndef PNGAPI +# define PNGAPI +#endif +#ifndef PNG_IMPEXP +# define PNG_IMPEXP +#endif + +#ifdef PNG_BUILDSYMS +# ifndef PNG_EXPORT +# define PNG_EXPORT(type,symbol) PNG_FUNCTION_EXPORT symbol END +# endif +# ifdef PNG_USE_GLOBAL_ARRAYS +# ifndef PNG_EXPORT_VAR +# define PNG_EXPORT_VAR(type) PNG_DATA_EXPORT +# endif +# endif +#endif + +#ifndef PNG_EXPORT +# define PNG_EXPORT(type,symbol) PNG_IMPEXP type PNGAPI symbol +#endif + +#ifdef PNG_USE_GLOBAL_ARRAYS +# ifndef PNG_EXPORT_VAR +# define PNG_EXPORT_VAR(type) extern PNG_IMPEXP type +# endif +#endif + +/* User may want to use these so they are not in PNG_INTERNAL. Any library + * functions that are passed far data must be model independent. + */ + +#ifndef PNG_ABORT +# define PNG_ABORT() abort() +#endif + +#ifdef PNG_SETJMP_SUPPORTED +# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_SETJMP_NOT_SUPPORTED) +#endif + +#if defined(USE_FAR_KEYWORD) /* memory model independent fns */ +/* use this to make far-to-near assignments */ +# define CHECK 1 +# define NOCHECK 0 +# define CVT_PTR(ptr) (png_far_to_near(png_ptr,ptr,CHECK)) +# define CVT_PTR_NOCHECK(ptr) (png_far_to_near(png_ptr,ptr,NOCHECK)) +# define png_strcpy _fstrcpy +# define png_strncpy _fstrncpy /* Added to v 1.2.6 */ +# define png_strlen _fstrlen +# define png_memcmp _fmemcmp /* SJT: added */ +# define png_memcpy _fmemcpy +# define png_memset _fmemset +#else /* use the usual functions */ +# define CVT_PTR(ptr) (ptr) +# define CVT_PTR_NOCHECK(ptr) (ptr) +# define png_strcpy strcpy +# define png_strncpy strncpy /* Added to v 1.2.6 */ +# define png_strlen strlen +# define png_memcmp memcmp /* SJT: added */ +# define png_memcpy memcpy +# define png_memset memset +#endif +/* End of memory model independent support */ + +/* Just a little check that someone hasn't tried to define something + * contradictory. + */ +#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K) +# undef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 65536L +#endif + +#ifdef PNG_READ_SUPPORTED +/* Prior to libpng-1.0.9, this block was in pngasmrd.h */ +#if defined(PNG_INTERNAL) + +/* These are the default thresholds before the MMX code kicks in; if either + * rowbytes or bitdepth is below the threshold, plain C code is used. These + * can be overridden at runtime via the png_set_mmx_thresholds() call in + * libpng 1.2.0 and later. The values below were chosen by Intel. + */ + +#ifndef PNG_MMX_ROWBYTES_THRESHOLD_DEFAULT +# define PNG_MMX_ROWBYTES_THRESHOLD_DEFAULT 128 /* >= */ +#endif +#ifndef PNG_MMX_BITDEPTH_THRESHOLD_DEFAULT +# define PNG_MMX_BITDEPTH_THRESHOLD_DEFAULT 9 /* >= */ +#endif + +/* Set this in the makefile for VC++ on Pentium, not here. */ +/* Platform must be Pentium. Makefile must assemble and load pngvcrd.c . + * MMX will be detected at run time and used if present. + */ +#ifdef PNG_USE_PNGVCRD +# define PNG_HAVE_ASSEMBLER_COMBINE_ROW +# define PNG_HAVE_ASSEMBLER_READ_INTERLACE +# define PNG_HAVE_ASSEMBLER_READ_FILTER_ROW +#endif + +/* Set this in the makefile for gcc/as on Pentium, not here. */ +/* Platform must be Pentium. Makefile must assemble and load pnggccrd.c . + * MMX will be detected at run time and used if present. + */ +#ifdef PNG_USE_PNGGCCRD +# define PNG_HAVE_ASSEMBLER_COMBINE_ROW +# define PNG_HAVE_ASSEMBLER_READ_INTERLACE +# define PNG_HAVE_ASSEMBLER_READ_FILTER_ROW +#endif +/* - see pnggccrd.c for info about what is currently enabled */ + +#endif /* PNG_INTERNAL */ +#endif /* PNG_READ_SUPPORTED */ + +/* Added at libpng-1.2.8 */ +#endif /* PNG_VERSION_INFO_ONLY */ + +#endif /* PNGCONF_H */ diff --git a/com32/include/setjmp.h b/com32/include/setjmp.h new file mode 100644 index 0000000..11b18fb --- /dev/null +++ b/com32/include/setjmp.h @@ -0,0 +1,22 @@ +/* + * setjmp.h + */ + +#ifndef _SETJMP_H +#define _SETJMP_H + +#include +#include +#include + +#include + +__extern int setjmp(jmp_buf); +__extern __noreturn longjmp(jmp_buf, int); + +typedef jmp_buf sigjmp_buf; + +#define sigsetjmp(__env, __save) setjmp(__env) +#define siglongjmp(__env, __val) longjmp(__env, __val) + +#endif /* _SETJMP_H */ diff --git a/com32/include/stdarg.h b/com32/include/stdarg.h new file mode 100644 index 0000000..cc324b8 --- /dev/null +++ b/com32/include/stdarg.h @@ -0,0 +1,14 @@ +/* + * stdarg.h + * + * This is just a wrapper for the gcc one, but defines va_copy() + * even if gcc doesn't. + */ + +/* Note: the _STDARG_H macro belongs to the gcc header... */ +#include_next + +/* Older gcc considers this an extension, so it's double underbar only */ +#ifndef va_copy +#define va_copy(d,s) __va_copy(d,s) +#endif diff --git a/com32/include/stdbool.h b/com32/include/stdbool.h new file mode 100644 index 0000000..6bc1c76 --- /dev/null +++ b/com32/include/stdbool.h @@ -0,0 +1,32 @@ +/* + * + * stdbool.h + */ + +#ifndef _STDBOOL_H +#define _STDBOOL_H + +#ifndef __cplusplus + +#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) +# if !defined(__GNUC__) ||(__GNUC__ < 3) + typedef char _Bool; /* For C compilers without _Bool */ +# endif +#endif + +#define bool _Bool +#define true 1 +#define false 0 + +#else + +/* C++ */ +#define bool bool +#define true true +#define false false + +#endif + +#define __bool_true_false_are_defined 1 + +#endif /* _STDBOOL_H */ diff --git a/com32/include/stddef.h b/com32/include/stddef.h new file mode 100644 index 0000000..125d235 --- /dev/null +++ b/com32/include/stddef.h @@ -0,0 +1,24 @@ +/* + * stddef.h + */ + +#ifndef _STDDEF_H +#define _STDDEF_H + +#ifndef __KLIBC__ +# define __KLIBC__ 1 +#endif + +#include + +#undef NULL +#ifdef __cplusplus +# define NULL 0 +#else +# define NULL ((void *)0) +#endif + +#undef offsetof +#define offsetof(t,m) ((size_t)&((t *)0)->m) + +#endif /* _STDDEF_H */ diff --git a/com32/include/stdint.h b/com32/include/stdint.h new file mode 100644 index 0000000..9a5553b --- /dev/null +++ b/com32/include/stdint.h @@ -0,0 +1,142 @@ +/* + * stdint.h + */ + +#ifndef _STDINT_H +#define _STDINT_H + +/* Exact types */ + +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long long int64_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +/* Small types */ + +typedef signed char int_least8_t; +typedef signed short int_least16_t; +typedef signed int int_least32_t; +typedef signed long long int_least64_t; + +typedef unsigned char uint_least8_t; +typedef unsigned short uint_least16_t; +typedef unsigned int uint_least32_t; +typedef unsigned long long uint_least64_t; + +/* Fast types */ + +typedef signed char int_fast8_t; +typedef signed short int_fast16_t; +typedef signed int int_fast32_t; +typedef signed long long int_fast64_t; + +typedef unsigned char uint_fast8_t; +typedef unsigned short uint_fast16_t; +typedef unsigned int uint_fast32_t; +typedef unsigned long long uint_fast64_t; + +/* Pointer types */ + +typedef int32_t intptr_t; +typedef uint32_t uintptr_t; + +/* Maximal types */ + +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + +/* + * To be strictly correct... + */ +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) + +# define INT8_MIN (-128) +# define INT16_MIN (-32767-1) +# define INT32_MIN (-2147483647-1) +# define INT64_MIN (-9223372036854775807LL-1) + +# define INT8_MAX (127) +# define INT16_MAX (32767) +# define INT32_MAX (2147483647) +# define INT64_MAX (9223372036854775807LL) + +# define UINT8_MAX (255U) +# define UINT16_MAX (65535U) +# define UINT32_MAX (4294967295U) +# define UINT64_MAX (18446744073709551615ULL) + +# define INT_LEAST8_MIN (-128) +# define INT_LEAST16_MIN (-32767-1) +# define INT_LEAST32_MIN (-2147483647-1) +# define INT_LEAST64_MIN (-9223372036854775807LL-1) + +# define INT_LEAST8_MAX (127) +# define INT_LEAST16_MAX (32767) +# define INT_LEAST32_MAX (2147483647) +# define INT_LEAST64_MAX (9223372036854775807LL) + +# define UINT_LEAST8_MAX (255U) +# define UINT_LEAST16_MAX (65535U) +# define UINT_LEAST32_MAX (4294967295U) +# define UINT_LEAST64_MAX (18446744073709551615ULL) + +# define INT_FAST8_MIN (-128) +# define INT_FAST16_MIN (-32767-1) +# define INT_FAST32_MIN (-2147483647-1) +# define INT_FAST64_MIN (-9223372036854775807LL-1) + +# define INT_FAST8_MAX (127) +# define INT_FAST16_MAX (32767) +# define INT_FAST32_MAX (2147483647) +# define INT_FAST64_MAX (9223372036854775807LL) + +# define UINT_FAST8_MAX (255U) +# define UINT_FAST16_MAX (65535U) +# define UINT_FAST32_MAX (4294967295U) +# define UINT_FAST64_MAX (18446744073709551615ULL) + +# define INTPTR_MIN (-2147483647-1) +# define INTPTR_MAX (2147483647) +# define UINTPTR_MAX (4294967295U) + +# define INTMAX_MIN (-9223372036854775807LL-1) +# define INTMAX_MAX (9223372036854775807LL) +# define UINTMAX_MAX (18446744073709551615ULL) + +/* ptrdiff_t limit */ +# define PTRDIFF_MIN (-2147483647-1) +# define PTRDIFF_MAX (2147483647) + +/* sig_atomic_t limit */ +# define SIG_ATOMIC_MIN (-2147483647-1) +# define SIG_ATOMIC_MAX (2147483647) + +/* size_t limit */ +# define SIZE_MAX (4294967295U) + +#endif /* STDC_LIMIT_MACROS */ + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) + +# define INT8_C(n) n +# define INT16_C(n) n +# define INT32_C(n) n +# define INT64_C(n) n ## LL + +# define UINT8_C(n) n ## U +# define UINT16_C(n) n ## U +# define UINT32_C(n) n ## U +# define UINT64_C(n) n ## ULL + +# define INTMAX_C(n) n ## LL +# define UINTMAX_C(n) n ## ULL + +#endif /* STDC_CONSTANT_MACROS */ + +#endif /* _STDINT_H */ diff --git a/com32/include/stdio.h b/com32/include/stdio.h new file mode 100644 index 0000000..39d2797 --- /dev/null +++ b/com32/include/stdio.h @@ -0,0 +1,116 @@ +/* + * stdio.h + */ + +#ifndef _STDIO_H +#define _STDIO_H + +#include +#include +#include + +/* This structure doesn't really exist, but it gives us something + to define FILE * with */ +struct _IO_file; +typedef struct _IO_file FILE; + +#ifndef EOF +# define EOF (-1) +#endif + +#ifndef BUFSIZ +# define BUFSIZ 4096 +#endif + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +/* + * Convert between a FILE * and a file descriptor. We don't actually + * have any in-memory data, so we just abuse the pointer itself to + * hold the data. Note, however, that for file descriptors, -1 is + * error and 0 is a valid value; for FILE *, NULL (0) is error and + * non-NULL are valid. + */ +static __inline__ int fileno(FILE *__f) +{ + /* This should really be intptr_t, but size_t should be the same size */ + return (int)(size_t)__f - 1; +} + +/* This is a macro so it can be used as initializer */ +#define __create_file(__fd) ((FILE *)(size_t)((__fd) + 1)) + +#define stdin __create_file(0) +#define stdout __create_file(1) +#define stderr __create_file(2) + +__extern FILE *fopen(const char *, const char *); +struct dev_info; +__extern FILE *fopendev(const struct dev_info *, const char *); + +static __inline__ FILE *fdopen(int __fd, const char *__m) +{ + (void)__m; return __create_file(__fd); +} +__extern int fclose(FILE *__f); +__extern int fputs(const char *, FILE *); +__extern int puts(const char *); +__extern int fputc(int, FILE *); +#define putc(c,f) fputc((c),(f)) +#define putchar(c) fputc((c),stdout) + +__extern int fgetc(FILE *); +__extern char * fgets(char *, int, FILE *); +#define getc(f) fgetc(f) + +__extern size_t _fread(void *, size_t, FILE *); +__extern size_t _fwrite(const void *, size_t, FILE *); + +__extern size_t fread(void *, size_t, size_t, FILE *); +__extern size_t fwrite(const void *, size_t, size_t, FILE *); + +#ifndef __NO_FREAD_FWRITE_INLINES +#define fread(__p, __s, __n, __f) \ + ( (__builtin_constant_p(__s) && __s == 1) \ + ? _fread(__p, __n, __f) \ + : fread(__p,__s,__n,__f) ) + +#define fwrite(__p, __s, __n, __f) \ + ( (__builtin_constant_p(__s) && __s == 1) \ + ? _fwrite(__p, __n, __f) \ + : fwrite(__p,__s,__n,__f) ) +#endif + +/* No seek, but we can tell */ +__extern long ftell(FILE *); + +__extern int printf(const char *, ...); +__extern int vprintf(const char *, va_list); +__extern int fprintf(FILE *, const char *, ...); +__extern int vfprintf(FILE *, const char *, va_list); +__extern int sprintf(char *, const char *, ...); +__extern int vsprintf(char *, const char *, va_list); +__extern int snprintf(char *, size_t n, const char *, ...); +__extern int vsnprintf(char *, size_t n, const char *, va_list); + +__extern int asprintf(char **, const char *, ...); +__extern int vasprintf(char **, const char *, va_list); + +/* No buffering, so no flushing needed */ +static __inline__ int +fflush(FILE *__f) +{ + (void)__f; + return 0; +} + +__extern int sscanf(const char *, const char *, ...); +__extern int vsscanf(const char *, const char *, va_list); + +__extern void perror(const char *); + +__extern int rename(const char *, const char *); + +#endif /* _STDIO_H */ diff --git a/com32/include/stdlib.h b/com32/include/stdlib.h new file mode 100644 index 0000000..24cf602 --- /dev/null +++ b/com32/include/stdlib.h @@ -0,0 +1,87 @@ +/* + * stdlib.h + */ + +#ifndef _STDLIB_H +#define _STDLIB_H + +#include +#include +#include + +#define EXIT_FAILURE 1 +#define EXIT_SUCCESS 0 + +__extern __noreturn abort(void); +static __inline__ int abs(int __n) { + return (__n < 0) ? -__n : __n; +} +__extern int atexit(void (*)(void)); +__extern int on_exit(void (*)(int, void *), void *); +__extern int atoi(const char *); +__extern long atol(const char *); +__extern long long atoll(const char *); +__extern __noreturn exit(int); +__extern __noreturn _Exit(int); +__extern void free(void *); +static __inline__ long labs(long __n) { + return (__n < 0L) ? -__n : __n; +} + +static __inline__ long long llabs(long long __n) { + return (__n < 0LL) ? -__n : __n; +} + +__extern __mallocfunc void *malloc(size_t); +__extern __mallocfunc void *zalloc(size_t); +__extern __mallocfunc void *calloc(size_t, size_t); +__extern __mallocfunc void *realloc(void *, size_t); +__extern long strtol(const char *, char **, int); +__extern long long strtoll(const char *, char **, int); +__extern unsigned long strtoul(const char *, char **, int); +__extern unsigned long long strtoull(const char *, char **, int); + +__extern char *getenv(const char *); +__extern int putenv(const char *); +__extern int setenv(const char *, const char *, int); +__extern int unsetenv(const char *); + +__extern void qsort(void *, size_t, size_t, int (*)(const void *, const void *)); + + +__extern long jrand48(unsigned short *); +__extern long mrand48(void); +__extern long nrand48(unsigned short *); +__extern long lrand48(void); +__extern unsigned short *seed48(const unsigned short *); +__extern void srand48(long); + +#define RAND_MAX 0x7fffffff +static __inline__ int rand(void) { + return (int)lrand48(); +} +static __inline__ void srand(unsigned int __s) { + srand48(__s); +} +static __inline__ long random(void) +{ + return lrand48(); +} +static __inline__ void srandom(unsigned int __s) +{ + srand48(__s); +} + +/* Basic PTY functions. These only work if devpts is mounted! */ + +__extern int unlockpt(int); +__extern char *ptsname(int); +__extern int getpt(void); + +static __inline__ int grantpt(int __fd) +{ + (void)__fd; + return 0; /* devpts does this all for us! */ +} + +#endif /* _STDLIB_H */ diff --git a/com32/include/string.h b/com32/include/string.h new file mode 100644 index 0000000..c964ee3 --- /dev/null +++ b/com32/include/string.h @@ -0,0 +1,47 @@ +/* + * string.h + */ + +#ifndef _STRING_H +#define _STRING_H + +#include +#include + +__extern void *memccpy(void *, const void *, int, size_t); +__extern void *memchr(const void *, int, size_t); +__extern int memcmp(const void *, const void *, size_t); +__extern void *memcpy(void *, const void *, size_t); +__extern void *mempcpy(void *, const void *, size_t); +__extern void *memmove(void *, const void *, size_t); +__extern void *memset(void *, int, size_t); +__extern void *memmem(const void *, size_t, const void *, size_t); +__extern void memswap(void *, void *, size_t); +__extern int strcasecmp(const char *, const char *); +__extern int strncasecmp(const char *, const char *, size_t); +__extern char *strcat(char *, const char *); +__extern char *strchr(const char *, int); +__extern int strcmp(const char *, const char *); +__extern char *strcpy(char *, const char *); +__extern char *strpcpy(char *, const char *); +__extern size_t strcspn(const char *, const char *); +__extern char *strdup(const char *); +__extern char *strndup(const char *, size_t); +__extern char *strerror(int); +__extern size_t strlen(const char *); +__extern size_t strnlen(const char *, size_t); +__extern char *strncat(char *, const char *, size_t); +__extern size_t strlcat(char *, const char *, size_t); +__extern int strncmp(const char *, const char *, size_t); +__extern char *strncpy(char *, const char *, size_t); +__extern char *stpcpy(char *, const char *); +__extern char *stpncpy(char *, const char *, size_t); +__extern size_t strlcpy(char *, const char *, size_t); +__extern char *strpbrk(const char *, const char *); +__extern char *strrchr(const char *, int); +__extern char *strsep(char **, const char *); +__extern size_t strspn(const char *, const char *); +__extern char *strstr(const char *, const char *); +__extern char *strtok(char *, const char *); + +#endif /* _STRING_H */ diff --git a/com32/include/sys/cpu.h b/com32/include/sys/cpu.h new file mode 100644 index 0000000..fcae2da --- /dev/null +++ b/com32/include/sys/cpu.h @@ -0,0 +1,114 @@ +#ifndef _CPU_H +#define _CPU_H + +#include +#include +#include + +static inline uint64_t rdtsc(void) +{ + uint64_t v; + asm volatile("rdtsc" : "=A" (v)); + return v; +} + +static inline uint32_t rdtscl(void) +{ + uint32_t v; + asm volatile("rdtsc" : "=a" (v) : : "edx"); + return v; +} + +static inline void cpuid_count(uint32_t op, uint32_t cnt, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + asm("cpuid" + : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx) + : "a" (op), "c" (cnt)); +} +static inline void cpuid(uint32_t op, uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + cpuid_count(op, 0, eax, ebx, ecx, edx); +} +static inline __constfunc uint32_t cpuid_eax(uint32_t level) +{ + uint32_t v; + + asm("cpuid" : "=a" (v) : "a" (level) : "ebx", "ecx", "edx"); + return v; +} +static inline __constfunc uint32_t cpuid_ebx(uint32_t level) +{ + uint32_t v; + + asm("cpuid" : "=b" (v), "+a" (level) : : "ecx", "edx"); + return v; +} +static inline __constfunc uint32_t cpuid_ecx(uint32_t level) +{ + uint32_t v; + + asm("cpuid" : "=c" (v), "+a" (level) : : "ebx", "edx"); + return v; +} +static inline __constfunc uint32_t cpuid_edx(uint32_t level) +{ + uint32_t v; + + asm("cpuid" : "=d" (v), "+a" (level) : : "ebx", "ecx"); + return v; +} + +/* Standard macro to see if a specific flag is changeable */ +static inline __constfunc bool cpu_has_eflag(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; +} + +static inline uint64_t rdmsr(uint32_t msr) +{ + uint64_t v; + + asm volatile("rdmsr" : "=A" (v) : "c" (msr)); + return v; +} +static inline void wrmsr(uint64_t v, uint32_t msr) +{ + asm volatile("wrmsr" : : "A" (v), "c" (msr)); +} + +static inline void cpu_relax(void) +{ + asm volatile("rep ; nop"); +} + +/* These are local cli/sti; not SMP-safe!!! */ + +static inline void cli(void) +{ + asm volatile("cli"); +} + +static inline void sti(void) +{ + asm volatile("sti"); +} + +#endif diff --git a/com32/include/sys/elf32.h b/com32/include/sys/elf32.h new file mode 100644 index 0000000..e4df8ce --- /dev/null +++ b/com32/include/sys/elf32.h @@ -0,0 +1,113 @@ +/* + * sys/elf32.h + */ + +#ifndef _SYS_ELF32_H +#define _SYS_ELF32_H + +#include + +/* ELF standard typedefs (yet more proof that was way overdue) */ +typedef uint16_t Elf32_Half; +typedef int16_t Elf32_SHalf; +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint64_t Elf32_Xword; +typedef int64_t Elf32_Sxword; + +typedef uint32_t Elf32_Off; +typedef uint32_t Elf32_Addr; +typedef uint16_t Elf32_Section; + +/* Dynamic header */ + +typedef struct elf32_dyn { + Elf32_Sword d_tag; + union { + Elf32_Sword d_val; + Elf32_Addr d_ptr; + } d_un; +} Elf32_Dyn; + +/* Relocations */ + +#define ELF32_R_SYM(x) ((x) >> 8) +#define ELF32_R_TYPE(x) ((x) & 0xff) + +typedef struct elf32_rel { + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +typedef struct elf32_rela { + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + +/* Symbol */ + +typedef struct elf32_sym { + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Half st_shndx; +} Elf32_Sym; + +/* Main file header */ + +typedef struct elf32_hdr { + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} Elf32_Ehdr; + +/* Program header */ + +typedef struct elf32_phdr { + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +/* Section header */ + +typedef struct elf32_shdr { + Elf32_Word sh_name; + Elf32_Word sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +} Elf32_Shdr; + +/* Note header */ +typedef struct elf32_note { + Elf32_Word n_namesz; /* Name size */ + Elf32_Word n_descsz; /* Content size */ + Elf32_Word n_type; /* Content type */ +} Elf32_Nhdr; + +#endif /* _SYS_ELF32_H */ diff --git a/com32/include/sys/elf64.h b/com32/include/sys/elf64.h new file mode 100644 index 0000000..0b486ac --- /dev/null +++ b/com32/include/sys/elf64.h @@ -0,0 +1,113 @@ +/* + * sys/elf64.h + */ + +#ifndef _SYS_ELF64_H +#define _SYS_ELF64_H + +#include + +/* ELF standard typedefs (yet more proof that was way overdue) */ +typedef uint16_t Elf64_Half; +typedef int16_t Elf64_SHalf; +typedef uint32_t Elf64_Word; +typedef int32_t Elf64_Sword; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; + +typedef uint64_t Elf64_Off; +typedef uint64_t Elf64_Addr; +typedef uint16_t Elf64_Section; + +/* Dynamic header */ + +typedef struct elf64_dyn { + Elf64_Sxword d_tag; + union { + Elf64_Xword d_val; + Elf64_Addr d_ptr; + } d_un; +} Elf64_Dyn; + +/* Relocations */ + +#define ELF64_R_SYM(x) ((x) >> 32) +#define ELF64_R_TYPE(x) ((x) & 0xffffffff) + +typedef struct elf64_rel { + Elf64_Addr r_offset; + Elf64_Xword r_info; +} Elf64_Rel; + +typedef struct elf64_rela { + Elf64_Addr r_offset; + Elf64_Xword r_info; + Elf64_Sxword r_addend; +} Elf64_Rela; + +/* Symbol */ + +typedef struct elf64_sym { + Elf64_Word st_name; + unsigned char st_info; + unsigned char st_other; + Elf64_Half st_shndx; + Elf64_Addr st_value; + Elf64_Xword st_size; +} Elf64_Sym; + +/* Main file header */ + +typedef struct elf64_hdr { + unsigned char e_ident[EI_NIDENT]; + Elf64_Half e_type; + Elf64_Half e_machine; + Elf64_Word e_version; + Elf64_Addr e_entry; + Elf64_Off e_phoff; + Elf64_Off e_shoff; + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; +} Elf64_Ehdr; + +/* Program header */ + +typedef struct elf64_phdr { + Elf64_Word p_type; + Elf64_Word p_flags; + Elf64_Off p_offset; + Elf64_Addr p_vaddr; + Elf64_Addr p_paddr; + Elf64_Xword p_filesz; + Elf64_Xword p_memsz; + Elf64_Xword p_align; +} Elf64_Phdr; + +/* Section header */ + +typedef struct elf64_shdr { + Elf64_Word sh_name; + Elf64_Word sh_type; + Elf64_Xword sh_flags; + Elf64_Addr sh_addr; + Elf64_Off sh_offset; + Elf64_Xword sh_size; + Elf64_Word sh_link; + Elf64_Word sh_info; + Elf64_Xword sh_addralign; + Elf64_Xword sh_entsize; +} Elf64_Shdr; + +/* Note header */ +typedef struct elf64_note { + Elf64_Word n_namesz; /* Name size */ + Elf64_Word n_descsz; /* Content size */ + Elf64_Word n_type; /* Content type */ +} Elf64_Nhdr; + +#endif /* _SYS_ELF64_H */ diff --git a/com32/include/sys/elfcommon.h b/com32/include/sys/elfcommon.h new file mode 100644 index 0000000..cc8f923 --- /dev/null +++ b/com32/include/sys/elfcommon.h @@ -0,0 +1,187 @@ +/* + * sys/elfcommon.h + */ + +#ifndef _SYS_ELFCOMMON_H +#define _SYS_ELFCOMMON_H + +#include + +/* Segment types */ +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 +#define PT_LOOS 0x60000000 +#define PT_HIOS 0x6fffffff +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7fffffff +#define PT_GNU_EH_FRAME 0x6474e550 /* Extension, eh? */ + +/* ELF file types */ +#define ET_NONE 0 +#define ET_REL 1 +#define ET_EXEC 2 +#define ET_DYN 3 +#define ET_CORE 4 +#define ET_LOPROC 0xff00 +#define ET_HIPROC 0xffff + +/* ELF machine types */ +#define EM_NONE 0 +#define EM_M32 1 +#define EM_SPARC 2 +#define EM_386 3 +#define EM_68K 4 +#define EM_88K 5 +#define EM_486 6 /* Not used in Linux at least */ +#define EM_860 7 +#define EM_MIPS 8 /* R3k, bigendian(?) */ +#define EM_MIPS_RS4_BE 10 /* R4k BE */ +#define EM_PARISC 15 +#define EM_SPARC32PLUS 18 +#define EM_PPC 20 +#define EM_PPC64 21 +#define EM_S390 22 +#define EM_SH 42 +#define EM_SPARCV9 43 /* v9 = SPARC64 */ +#define EM_H8_300H 47 +#define EM_H8S 48 +#define EM_IA_64 50 /* Itanic */ +#define EM_X86_64 62 +#define EM_CRIS 76 +#define EM_V850 87 +#define EM_ALPHA 0x9026 /* Interrim Alpha that stuck around */ +#define EM_CYGNUS_V850 0x9080 /* Old v850 ID used by Cygnus */ +#define EM_S390_OLD 0xA390 /* Obsolete interrim value for S/390 */ + +/* Dynamic type values */ +#define DT_NULL 0 +#define DT_NEEDED 1 +#define DT_PLTRELSZ 2 +#define DT_PLTGOT 3 +#define DT_HASH 4 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_RELA 7 +#define DT_RELASZ 8 +#define DT_RELAENT 9 +#define DT_STRSZ 10 +#define DT_SYMENT 11 +#define DT_INIT 12 +#define DT_FINI 13 +#define DT_SONAME 14 +#define DT_RPATH 15 +#define DT_SYMBOLIC 16 +#define DT_REL 17 +#define DT_RELSZ 18 +#define DT_RELENT 19 +#define DT_PLTREL 20 +#define DT_DEBUG 21 +#define DT_TEXTREL 22 +#define DT_JMPREL 23 +#define DT_LOPROC 0x70000000 +#define DT_HIPROC 0x7fffffff + +/* Auxilliary table entries */ +#define AT_NULL 0 /* end of vector */ +#define AT_IGNORE 1 /* entry should be ignored */ +#define AT_EXECFD 2 /* file descriptor of program */ +#define AT_PHDR 3 /* program headers for program */ +#define AT_PHENT 4 /* size of program header entry */ +#define AT_PHNUM 5 /* number of program headers */ +#define AT_PAGESZ 6 /* system page size */ +#define AT_BASE 7 /* base address of interpreter */ +#define AT_FLAGS 8 /* flags */ +#define AT_ENTRY 9 /* entry point of program */ +#define AT_NOTELF 10 /* program is not ELF */ +#define AT_UID 11 /* real uid */ +#define AT_EUID 12 /* effective uid */ +#define AT_GID 13 /* real gid */ +#define AT_EGID 14 /* effective gid */ +#define AT_PLATFORM 15 /* string identifying CPU for optimizations */ +#define AT_HWCAP 16 /* arch dependent hints at CPU capabilities */ +#define AT_CLKTCK 17 /* frequency at which times() increments */ +/* 18..22 = ? */ +#define AT_SECURE 23 /* secure mode boolean */ + +/* Program header permission flags */ +#define PF_X 0x1 +#define PF_W 0x2 +#define PF_R 0x4 + +/* Section header types */ +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_NUM 12 +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xffffffff + +/* Section header flags */ +#define SHF_WRITE 0x1 +#define SHF_ALLOC 0x2 +#define SHF_EXECINSTR 0x4 +#define SHF_MASKPROC 0xf0000000 + +/* Special section numbers */ +#define SHN_UNDEF 0 +#define SHN_LORESERVE 0xff00 +#define SHN_LOPROC 0xff00 +#define SHN_HIPROC 0xff1f +#define SHN_ABS 0xfff1 +#define SHN_COMMON 0xfff2 +#define SHN_HIRESERVE 0xffff + +/* Lenght of magic at the start of a file */ +#define EI_NIDENT 16 + +/* Magic number constants... */ +#define EI_MAG0 0 /* e_ident[] indexes */ +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 +#define EI_OSABI 7 +#define EI_PAD 8 + +#define ELFMAG0 0x7f /* EI_MAG */ +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define ELFCLASSNONE 0 /* EI_CLASS */ +#define ELFCLASS32 1 +#define ELFCLASS64 2 +#define ELFCLASSNUM 3 + +#define ELFDATANONE 0 /* e_ident[EI_DATA] */ +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 + +#define EV_NONE 0 /* e_version, EI_VERSION */ +#define EV_CURRENT 1 +#define EV_NUM 2 + +#define ELFOSABI_NONE 0 +#define ELFOSABI_LINUX 3 + +#endif /* _SYS_ELFCOMMON_H */ diff --git a/com32/include/sys/fpu.h b/com32/include/sys/fpu.h new file mode 100644 index 0000000..134ae59 --- /dev/null +++ b/com32/include/sys/fpu.h @@ -0,0 +1,6 @@ +#ifndef _SYS_FPU_H +#define _SYS_FPU_H + +extern int x86_init_fpu(void); + +#endif /* _SYS_FPU_H */ diff --git a/com32/include/sys/io.h b/com32/include/sys/io.h new file mode 100644 index 0000000..460f230 --- /dev/null +++ b/com32/include/sys/io.h @@ -0,0 +1,42 @@ +#ifndef _SYS_IO_H +#define _SYS_IO_H + +#include + +static inline uint8_t inb(uint16_t p) +{ + uint8_t v; + asm volatile("inb %1,%0" : "=a" (v) : "Nd" (p)); + return v; +} + +static inline uint16_t inw(uint16_t p) +{ + uint16_t v; + asm volatile("inw %1,%0" : "=a" (v) : "Nd" (p)); + return v; +} + +static inline uint32_t inl(uint16_t p) +{ + uint32_t v; + asm volatile("inl %1,%0" : "=a" (v) : "Nd" (p)); + return v; +} + +static inline void outb(uint8_t v, uint16_t p) +{ + asm volatile("outb %0,%1" : : "a" (v), "Nd" (p)); +} + +static inline void outw(uint16_t v, uint16_t p) +{ + asm volatile("outw %0,%1" : : "a" (v), "Nd" (p)); +} + +static inline void outl(uint32_t v, uint16_t p) +{ + asm volatile("outl %0,%1" : : "a" (v), "Nd" (p)); +} + +#endif /* _SYS_IO_H */ diff --git a/com32/include/sys/pci.h b/com32/include/sys/pci.h new file mode 100644 index 0000000..b6556ff --- /dev/null +++ b/com32/include/sys/pci.h @@ -0,0 +1,150 @@ +#ifndef _SYS_PCI_H +#define _SYS_PCI_H + +#include +#include + +#define MAX_PCI_FUNC 8 +#define MAX_PCI_DEVICES 32 +#define MAX_PCI_BUSES 256 +#define LINUX_KERNEL_MODULE_SIZE 64 +#define PCI_VENDOR_NAME_SIZE 256 +#define PCI_PRODUCT_NAME_SIZE 256 +#define PCI_CLASS_NAME_SIZE 256 +#define MAX_KERNEL_MODULES_PER_PCI_DEVICE 10 +#define MAX_PCI_CLASSES 256 + +typedef uint32_t pciaddr_t; + +enum { + ENOPCIIDS = 100, + ENOMODULESPCIMAP +}; + +/* a structure for extended pci information */ +/* XXX: use pointers for these? */ +struct pci_dev_info { + char vendor_name[PCI_VENDOR_NAME_SIZE]; + char product_name[PCI_PRODUCT_NAME_SIZE]; + char linux_kernel_module[LINUX_KERNEL_MODULE_SIZE][MAX_KERNEL_MODULES_PER_PCI_DEVICE]; + int linux_kernel_module_count; + char class_name[PCI_CLASS_NAME_SIZE]; /* The most precise class name */ + char category_name[PCI_CLASS_NAME_SIZE]; /*The general category*/ + uint8_t irq; + uint8_t latency; +}; + +/* PCI device (really, function) */ +struct pci_device { + union { + struct { + uint16_t vendor; + uint16_t product; + uint16_t sub_vendor; + uint16_t sub_product; + uint8_t revision; + uint8_t class[3]; + }; + struct { + uint32_t vid_did; + uint32_t svid_sdid; + uint32_t rid_class; + }; + }; + struct pci_dev_info *dev_info; + struct pci_device *next; +}; + +/* PCI device ("slot") structure */ +struct pci_slot { + struct pci_device *func[MAX_PCI_FUNC]; +}; + +/* PCI bus structure */ +struct pci_bus { + struct pci_slot *slot[MAX_PCI_DEVICES]; +}; + +/* PCI domain structure */ +struct pci_domain { + struct pci_bus *bus[MAX_PCI_BUSES]; +}; + +/* Iterate over a PCI domain */ +#define for_each_pci_func(funcp, domain) \ + for (int __pci_bus = 0; __pci_bus < MAX_PCI_BUSES; __pci_bus++) \ + if ((domain)->bus[__pci_bus]) \ + for (int __pci_slot = 0; __pci_slot < MAX_PCI_DEVICES; __pci_slot++) \ + if ((domain)->bus[__pci_bus]->slot[__pci_slot]) \ + for (int __pci_func = 0; __pci_func < MAX_PCI_FUNC; __pci_func++) \ + if (((funcp) = (domain)->bus[__pci_bus]->slot[__pci_slot]-> \ + func[__pci_func])) + +#define for_each_pci_func3(funcp, domain, addr) \ + for (int __pci_bus = 0; __pci_bus < MAX_PCI_BUSES; __pci_bus++) \ + if ((domain)->bus[__pci_bus]) \ + for (int __pci_slot = 0; __pci_slot < MAX_PCI_DEVICES; __pci_slot++) \ + if ((domain)->bus[__pci_bus]->slot[__pci_slot]) \ + for (int __pci_func = 0; __pci_func < MAX_PCI_FUNC; __pci_func++) \ + if (((addr) = pci_mkaddr(__pci_bus, __pci_slot, __pci_func, 0)), \ + ((funcp) = (domain)->bus[__pci_bus]->slot[__pci_slot]-> \ + func[__pci_func])) + +struct match { + struct match *next; + uint32_t did; + uint32_t did_mask; + uint32_t sid; + uint32_t sid_mask; + uint8_t rid_min, rid_max; + char *filename; +}; + +static inline pciaddr_t pci_mkaddr(uint32_t bus, uint32_t dev, + uint32_t func, uint32_t reg) +{ + return 0x80000000 | ((bus & 0xff) << 16) | ((dev & 0x1f) << 11) | + ((func & 0x07) << 8) | (reg & 0xff); +} + +static inline int pci_bus(pciaddr_t addr) +{ + return (addr >> 16) & 0xff; +} + +static inline int pci_dev(pciaddr_t addr) +{ + return (addr >> 11) & 0x1f; +} + +static inline int pci_func(pciaddr_t addr) +{ + return (addr >> 8) & 0x07; +} + +enum pci_config_type { + PCI_CFG_NONE = -1, /* badness */ + PCI_CFG_AUTO = 0, /* autodetect */ + PCI_CFG_TYPE1 = 1, + PCI_CFG_TYPE2 = 2, + PCI_CFG_BIOS = 3, +}; + +enum pci_config_type pci_set_config_type(enum pci_config_type); + +uint8_t pci_readb(pciaddr_t); +uint16_t pci_readw(pciaddr_t); +uint32_t pci_readl(pciaddr_t); +void pci_writeb(uint8_t, pciaddr_t); +void pci_writew(uint16_t, pciaddr_t); +void pci_writel(uint32_t, pciaddr_t); + +struct pci_domain *pci_scan(void); +void free_pci_domain(struct pci_domain *domain); +struct match * find_pci_device(const struct pci_domain *pci_domain, + struct match *list); +int get_name_from_pci_ids(struct pci_domain *pci_domain, char *pciids_path); +int get_module_name_from_pcimap(struct pci_domain *pci_domain, char *modules_pcimap_path); +int get_class_name_from_pci_ids(struct pci_domain *pci_domain, char *pciids_path); +void gather_additional_pci_config(struct pci_domain *domain); +#endif /* _SYS_PCI_H */ diff --git a/com32/include/sys/stat.h b/com32/include/sys/stat.h new file mode 100644 index 0000000..ffc4105 --- /dev/null +++ b/com32/include/sys/stat.h @@ -0,0 +1,52 @@ +/* + * sys/stat.h + */ + +#ifndef _SYS_STAT_H +#define _SYS_STAT_H + +#include + +/* We don't use this, but it's there for compatibility */ + +#define S_IFMT 00170000 +#define S_IFSOCK 0140000 +#define S_IFLNK 0120000 +#define S_IFREG 0100000 +#define S_IFBLK 0060000 +#define S_IFDIR 0040000 +#define S_IFCHR 0020000 +#define S_IFIFO 0010000 +#define S_ISUID 0004000 +#define S_ISGID 0002000 +#define S_ISVTX 0001000 +#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 S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +#define S_IRWXU 00700 +#define S_IRUSR 00400 +#define S_IWUSR 00200 +#define S_IXUSR 00100 +#define S_IRWXG 00070 +#define S_IRGRP 00040 +#define S_IWGRP 00020 +#define S_IXGRP 00010 +#define S_IRWXO 00007 +#define S_IROTH 00004 +#define S_IWOTH 00002 +#define S_IXOTH 00001 + +/* These are the only fields in struct stat we emulate */ +struct stat { + mode_t st_mode; + off_t st_size; +}; + +/* Only fstat() supported */ +int fstat(int, struct stat *); + +#endif /* _SYS_STAT_H */ diff --git a/com32/include/sys/time.h b/com32/include/sys/time.h new file mode 100644 index 0000000..c5bff0d --- /dev/null +++ b/com32/include/sys/time.h @@ -0,0 +1,6 @@ +#ifndef _SYS_TIME_H +#define _SYS_TIME_H + +/* empty */ + +#endif diff --git a/com32/include/sys/times.h b/com32/include/sys/times.h new file mode 100644 index 0000000..dd60a3c --- /dev/null +++ b/com32/include/sys/times.h @@ -0,0 +1,21 @@ +/* + * sys/times.h + */ + +#ifndef _SYS_TIMES_H +#define _SYS_TIMES_H + +#include + +struct tms { + /* Empty */ +}; + +#define HZ 18 /* Piddly resolution... */ +#define CLK_TCK HZ + +typedef uint16_t clock_t; + +clock_t times(struct tms *); + +#endif /* _SYS_TIMES_H */ diff --git a/com32/include/sys/types.h b/com32/include/sys/types.h new file mode 100644 index 0000000..2ab518c --- /dev/null +++ b/com32/include/sys/types.h @@ -0,0 +1,16 @@ +/* + * sys/types.h + */ + +#ifndef _SYS_TYPES_H +#define _SYS_TYPES_H + +#include +#include +#include + +typedef ptrdiff_t ssize_t; +typedef int mode_t; +typedef size_t off_t; + +#endif diff --git a/com32/include/syslinux/adv.h b/com32/include/syslinux/adv.h new file mode 100644 index 0000000..d64b89c --- /dev/null +++ b/com32/include/syslinux/adv.h @@ -0,0 +1,57 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux/adv.h + */ + +#ifndef _SYSLINUX_ADV_H +#define _SYSLINUX_ADV_H + +#include +#include +#include + +__extern void *__syslinux_adv_ptr; +__extern size_t __syslinux_adv_size; + +static inline void *syslinux_adv_ptr(void) +{ + return __syslinux_adv_ptr; +} + +static inline size_t syslinux_adv_size(void) +{ + return __syslinux_adv_size; +} + +__extern int syslinux_adv_write(void); + +__extern int syslinux_setadv(int, size_t, const void *); +__extern const void *syslinux_getadv(int, size_t *); + +#endif /* _SYSLINUX_ADV_H */ diff --git a/com32/include/syslinux/advconst.h b/com32/include/syslinux/advconst.h new file mode 100644 index 0000000..b7c775f --- /dev/null +++ b/com32/include/syslinux/advconst.h @@ -0,0 +1,45 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux/advconst.h + * + * ADV defined constants + * + * Defined in a separate file so it can be used by non-COM32 code. + * Some of these constants are also defined in adv.inc, they better match... + */ + +#ifndef _SYSLINUX_ADVCONST_H +#define _SYSLINUX_ADVCONST_H + +#define ADV_END 0 +#define ADV_BOOTONCE 1 +#define ADV_MENUSAVE 2 + +#endif /* _SYSLINUX_ADVCONST_H */ diff --git a/com32/include/syslinux/align.h b/com32/include/syslinux/align.h new file mode 100644 index 0000000..5b01bf2 --- /dev/null +++ b/com32/include/syslinux/align.h @@ -0,0 +1,47 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Intel Corporation; author H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#ifndef _SYSLINUX_ALIGN_H +#define _SYSLINUX_ALIGN_H + +#include + +static inline uintptr_t __align_down(uintptr_t __p, uintptr_t __a) +{ + return __p & ~(__a - 1); +} +static inline uintptr_t __align_up(uintptr_t __p, uintptr_t __a) +{ + return (__p + __a - 1) & ~(__a - 1); +} + +#define ALIGN_UP(p,a) ((__typeof__(p))__align_up((uintptr_t)(p), (a))) +#define ALIGN_DOWN(p,a) ((__typeof__(p))__align_down((uintptr_t)(p), (a))) +#define ALIGN_UP_FOR(p,t) ALIGN_UP(p,sizeof(t)) +#define ALIGN_DOWN_FOR(p,t) ALIGN_DOWN(p,sizeof(t)) + +#endif /* _SYSLINUX_ALIGN_H */ diff --git a/com32/include/syslinux/boot.h b/com32/include/syslinux/boot.h new file mode 100644 index 0000000..87c05e9 --- /dev/null +++ b/com32/include/syslinux/boot.h @@ -0,0 +1,63 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux/boot.h + * + * SYSLINUX boot API invocation + */ + +#ifndef _SYSLINUX_BOOT_H +#define _SYSLINUX_BOOT_H + +#include +#include + +__noreturn syslinux_run_command(const char *); +__noreturn syslinux_run_default(void); + +void syslinux_local_boot(uint16_t flags); + +void syslinux_final_cleanup(uint16_t flags); + +void syslinux_chain_bootstrap(uint16_t flags, const void *bootstrap, + uint32_t bootstrap_len, uint32_t edx, + uint32_t esi, uint16_t ds); + +#define IMAGE_TYPE_KERNEL 0 +#define IMAGE_TYPE_LINUX 1 +#define IMAGE_TYPE_BOOT 2 +#define IMAGE_TYPE_BSS 3 +#define IMAGE_TYPE_PXE 4 +#define IMAGE_TYPE_FDIMAGE 5 +#define IMAGE_TYPE_COMBOOT 6 +#define IMAGE_TYPE_COM32 7 +#define IMAGE_TYPE_CONFIG 8 +void syslinux_run_kernel_image(const char *filename, const char *cmdline, + uint32_t ipappend_flags, uint32_t type); + +#endif /* _SYSLINUX_BOOT_H */ diff --git a/com32/include/syslinux/bootpm.h b/com32/include/syslinux/bootpm.h new file mode 100644 index 0000000..a52d992 --- /dev/null +++ b/com32/include/syslinux/bootpm.h @@ -0,0 +1,59 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux/bootpm.h + * + * Data structures for shuffle and boot to protected mode + */ + +#ifndef _SYSLINUX_BOOTPM_H +#define _SYSLINUX_BOOTPM_H + +#include +#include + +struct syslinux_pm_regs { + uint32_t eax; /* Offset 0 */ + uint32_t ecx; /* Offset 4 */ + uint32_t edx; /* Offset 8 */ + uint32_t ebx; /* Offset 12 */ + uint32_t esp; /* Offset 16 */ + uint32_t ebp; /* Offset 20 */ + uint32_t esi; /* Offset 24 */ + uint32_t edi; /* Offset 28 */ + + uint32_t eip; /* Offset 32 */ +}; + +int syslinux_shuffle_boot_pm(struct syslinux_movelist *fraglist, + struct syslinux_memmap *memmap, + uint16_t bootflags, + struct syslinux_pm_regs *regs); + + +#endif /* _SYSLINUX_BOOTPM_H */ diff --git a/com32/include/syslinux/bootrm.h b/com32/include/syslinux/bootrm.h new file mode 100644 index 0000000..304c081 --- /dev/null +++ b/com32/include/syslinux/bootrm.h @@ -0,0 +1,74 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux/bootrm.h + * + * Data structures for shuffle and boot to protected mode + */ + +#ifndef _SYSLINUX_BOOTRM_H +#define _SYSLINUX_BOOTRM_H + +#include +#include +#include +#include + +/* This register set is used by the shuffle and boot interface. It is + a completely different structure from what the __intcall() and + __farcall() interfaces use! */ +struct syslinux_rm_regs { + uint16_t es; /* Offset 0 */ + uint16_t _unused_cs; /* Offset 2 */ + uint16_t ss; /* Offset 4 */ + uint16_t ds; /* Offset 6 */ + uint16_t fs; /* Offset 8 */ + uint16_t gs; /* Offset 10 */ + + reg32_t eax; /* Offset 12 */ + reg32_t ecx; /* Offset 16 */ + reg32_t edx; /* Offset 20 */ + reg32_t ebx; /* Offset 24 */ + reg32_t esp; /* Offset 28 */ + reg32_t ebp; /* Offset 32 */ + reg32_t esi; /* Offset 36 */ + reg32_t edi; /* Offset 40 */ + + uint16_t ip; /* Offset 44 */ + uint16_t cs; /* Offset 46 */ + + bool sti; /* Offset 48 */ +}; + +int syslinux_shuffle_boot_rm(struct syslinux_movelist *fraglist, + struct syslinux_memmap *memmap, + uint16_t bootflags, + struct syslinux_rm_regs *regs); + +#endif /* _SYSLINUX_BOOTRM_H */ diff --git a/com32/include/syslinux/config.h b/com32/include/syslinux/config.h new file mode 100644 index 0000000..ade2c4b --- /dev/null +++ b/com32/include/syslinux/config.h @@ -0,0 +1,175 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux/config.h + * + * Query syslinux configuration information. + */ + +#ifndef _SYSLINUX_CONFIG_H +#define _SYSLINUX_CONFIG_H + +#include +#include + +enum syslinux_filesystem { + SYSLINUX_FS_UNKNOWN = 0x30, + SYSLINUX_FS_SYSLINUX = 0x31, + SYSLINUX_FS_PXELINUX = 0x32, + SYSLINUX_FS_ISOLINUX = 0x33, + SYSLINUX_FS_EXTLINUX = 0x34, +}; + +struct syslinux_version { + uint16_t version; /* (major << 8)+minor */ + uint16_t max_api; + enum syslinux_filesystem filesystem; + const char *version_string; + const char *copyright_string; +}; + +extern __nocommon struct syslinux_version __syslinux_version; +static inline const struct syslinux_version * +syslinux_version(void) +{ + return &__syslinux_version; +} + +union syslinux_derivative_info { + struct { + com32sys_t r; + const void *esbx; + const void *fssi; + const void *gsdi; + } rr; /* real raw */ + struct { + uint16_t _pad1[4]; + uint32_t _pad2[7]; + uint8_t filesystem; + uint8_t ah; + uint16_t axh; + } c; /* common */ + struct { + uint16_t gs; + uint16_t fs; + uint16_t es; + uint16_t ds; + uint16_t di, dih; + uint16_t si, sih; + uint16_t bp, bph; + uint16_t sp, sph; + uint16_t bx, bxh; + uint16_t dx, dxh; + uint16_t cx, cxh; + uint16_t ax, axh; + uint32_t eflags; + const void *esbx; + const void *fssi; + const void *gsdi; + } r; /* raw */ + struct { + uint16_t _gs, _fs, _es, _ds; + uint32_t _edi, _esi, _ebp, _esp, _ebx; + uint8_t drive_number, dh; + uint16_t _dxh; + uint8_t sector_shift, ch; + uint16_t _cxh; + uint8_t filesystem, ah; + uint16_t _axh; + uint32_t _eflags; + const void *ptab_ptr; + const uint32_t *esdi_ptr; + } disk; /* syslinux/extlinux */ + struct { + uint16_t _gs, _fs, _es, _ds; + uint32_t _edi, _esi, _ebp, _esp, _ebx; + uint16_t apiver; + uint16_t _dxh; + uint32_t _ecx; + uint8_t filesystem, ah; + uint16_t _axh; + uint32_t _eflags; + const void *pxenvptr; + const void *stack; + } pxe; /* pxelinux */ + struct { + uint16_t _gs, _fs, _es, _ds; + uint32_t _edi, _esi, _ebp, _esp, _ebx; + uint8_t drive_number, dh; + uint16_t _dxh; + uint8_t sector_shift, cd_mode; + uint16_t _cxh; + uint8_t filesystem, ah; + uint16_t _axh; + uint32_t _eflags; + const void *spec_packet; + const uint32_t *esdi_ptr; + } iso; /* isolinux */ +}; + +extern __nocommon union syslinux_derivative_info __syslinux_derivative_info; +static inline const union syslinux_derivative_info * +syslinux_derivative_info(void) +{ + return &__syslinux_derivative_info; +} + +struct syslinux_serial_console_info { + uint16_t iobase; + uint16_t divisor; + uint16_t flowctl; +}; + +extern __nocommon struct syslinux_serial_console_info + __syslinux_serial_console_info; +static inline const struct syslinux_serial_console_info * +syslinux_serial_console_info(void) +{ + return &__syslinux_serial_console_info; +} + +extern __nocommon const char *__syslinux_config_file; +static inline const char *syslinux_config_file(void) +{ + return __syslinux_config_file; +} + +struct syslinux_ipappend_strings { + int count; + const char * const *ptr; +}; +extern __nocommon struct syslinux_ipappend_strings + __syslinux_ipappend_strings; +static inline const struct syslinux_ipappend_strings * +syslinux_ipappend_strings(void) +{ + return &__syslinux_ipappend_strings; +} + +#endif /* _SYSLINUX_CONFIG_H */ diff --git a/com32/include/syslinux/features.h b/com32/include/syslinux/features.h new file mode 100644 index 0000000..f2317dd --- /dev/null +++ b/com32/include/syslinux/features.h @@ -0,0 +1,50 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#ifndef _SYSLINUX_FEATURES_H +#define _SYSLINUX_FEATURES_H + +#define SYSLINUX_FEATURE_LOCAL_BOOT (0*8+0) +#define SYSLINUX_FEATURE_NOOP_IDLE (0*8+1) + +extern struct __syslinux_feature_flags { + unsigned int len; + const unsigned char *ptr; +} __syslinux_feature_flags; + +static inline int syslinux_has_feature(unsigned int __flag) +{ + unsigned int __byte = __flag >> 3; + unsigned int __bit = __flag & 7; + + if (__byte <= __syslinux_feature_flags.len) + return (__syslinux_feature_flags.ptr[__byte] >> __bit) & 1; + else + return 0; +} + +#endif /* _SYSLINUX_FEATURE_H */ diff --git a/com32/include/syslinux/idle.h b/com32/include/syslinux/idle.h new file mode 100644 index 0000000..4c5947b --- /dev/null +++ b/com32/include/syslinux/idle.h @@ -0,0 +1,37 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2005-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux/idle.h + */ + +#ifndef _SYSLINUX_IDLE_H +#define _SYSLINUX_IDLE_H + +void syslinux_idle(void); + +#endif diff --git a/com32/include/syslinux/io.h b/com32/include/syslinux/io.h new file mode 100644 index 0000000..329377d --- /dev/null +++ b/com32/include/syslinux/io.h @@ -0,0 +1,39 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux/io.h + * + * SYSLINUX low-level I/O functions + */ + +#ifndef _SYSLINUX_IO_H +#define _SYSLINUX_IO_H + +int syslinux_read_disk(void *buf, uint32_t sector, uint16_t count); + +#endif /* _SYSLINUX_IO_H */ diff --git a/com32/include/syslinux/keyboard.h b/com32/include/syslinux/keyboard.h new file mode 100644 index 0000000..290cff3 --- /dev/null +++ b/com32/include/syslinux/keyboard.h @@ -0,0 +1,52 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux/keyboard.h + * + * Query syslinux keyboard map. + */ + +#ifndef _SYSLINUX_KEYBOARD_H +#define _SYSLINUX_KEYBOARD_H + +#include + +struct syslinux_keyboard_map { + uint16_t version; + uint16_t length; + void *map; +}; + +extern struct syslinux_keyboard_map __syslinux_keyboard_map; +static inline const struct syslinux_keyboard_map * +syslinux_keyboard_map(void) +{ + return &__syslinux_keyboard_map; +} + +#endif /* _SYSLINUX_KEYBOARD_H */ diff --git a/com32/include/syslinux/linux.h b/com32/include/syslinux/linux.h new file mode 100644 index 0000000..0e47721 --- /dev/null +++ b/com32/include/syslinux/linux.h @@ -0,0 +1,74 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux/linux.h + * + * Definitions used to boot a Linux kernel + */ + +#ifndef _SYSLINUX_LINUX_H +#define _SYSLINUX_LINUX_H + +#include +#include + +/* A chunk of an initramfs. These are kept as a doubly-linked + circular list with headnode; the headnode is distinguished by + having len == 0. The data pointer can be NULL if data_len is zero; + if data_len < len then the balance of the region is zeroed. */ + +struct initramfs { + struct initramfs *prev, *next; + size_t len; + size_t align; + const void *data; + size_t data_len; +}; +#define INITRAMFS_MAX_ALIGN 4096 + +int syslinux_boot_linux(void *kernel_buf, size_t kernel_size, + struct initramfs *initramfs, char *cmdline); + +/* Initramfs manipulation functions */ + +struct initramfs *initramfs_init(void); +int initramfs_add_data(struct initramfs *ihead, const void *data, + size_t data_len, size_t len, size_t align); +int initramfs_mknod(struct initramfs *ihead, const char *filename, + int do_mkdir, + uint16_t mode, size_t len, + uint32_t major, uint32_t minor); +int initramfs_add_file(struct initramfs *ihead, const void *data, + size_t data_len, size_t len, + const char *filename, int do_mkdir, uint32_t mode); +int initramfs_load_file(struct initramfs *ihead, const char *src_filename, + const char *dst_filename, int do_mkdir, uint32_t mode); +int initramfs_add_trailer(struct initramfs *ihead); +int initramfs_load_archive(struct initramfs *ihead, const char *filename); + +#endif /* _SYSLINUX_LINUX_H */ diff --git a/com32/include/syslinux/loadfile.h b/com32/include/syslinux/loadfile.h new file mode 100644 index 0000000..1a04c51 --- /dev/null +++ b/com32/include/syslinux/loadfile.h @@ -0,0 +1,15 @@ +#ifndef LIBUTIL_LOADFILE_H +#define LIBUTIL_LOADFILE_H + +#include +#include + +/* loadfile() returns the true size of the file, but will guarantee valid, + zero-padded memory out to this boundary. */ +#define LOADFILE_ZERO_PAD 64 + +int loadfile(const char *, void **, size_t *); +int zloadfile(const char *, void **, size_t *); +int floadfile(FILE *, void **, size_t *, const void *, size_t); + +#endif diff --git a/com32/include/syslinux/memscan.h b/com32/include/syslinux/memscan.h new file mode 100644 index 0000000..24c4eda --- /dev/null +++ b/com32/include/syslinux/memscan.h @@ -0,0 +1,38 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#ifndef _SYSLINUX_MEMSCAN_H +#define _SYSLINUX_MEMSCAN_H + +#include +#include /* addr_t */ + +typedef int (*scan_memory_callback_t)(void *, addr_t, addr_t, bool); +int syslinux_scan_memory(scan_memory_callback_t callback, void *data); + +#endif /* _SYSLINUX_MEMSCAN_H */ diff --git a/com32/include/syslinux/movebits.h b/com32/include/syslinux/movebits.h new file mode 100644 index 0000000..f0cb277 --- /dev/null +++ b/com32/include/syslinux/movebits.h @@ -0,0 +1,90 @@ +#ifndef _SYSLINUX_MOVEBITS_H +#define _SYSLINUX_MOVEBITS_H + +#include +#include + +typedef uint32_t addr_t; + +/* + * A syslinux_movelist is a linked list of move operations. The ordering + * is important, so no sorting requirement can be imposed. + */ +struct syslinux_movelist { + addr_t dst; + addr_t src; + addr_t len; + struct syslinux_movelist *next; +}; + +/* + * A syslinux_memmap is a sorted, linked list of memory regions, + * guaranteed to satisfy the constraint that no adjacent zones have + * the same type. Undefined memory ranges are represented with entries; + * they have type SMT_UNDEFINED. + * + * Note that there is no length field. The length of a region is obtained + * by looking at the start of the next entry in the chain. + */ +enum syslinux_memmap_types { + SMT_ERROR = -2, /* Internal error token */ + SMT_END = -1, /* End of list */ + SMT_UNDEFINED = 0, /* Unknown range */ + SMT_FREE = 1, /* Available memory */ + SMT_RESERVED, /* Unusable memory */ + SMT_ALLOC, /* Memory allocated by user */ + SMT_ZERO, /* Memory that should be zeroed */ +}; + +struct syslinux_memmap { + addr_t start; + enum syslinux_memmap_types type; + struct syslinux_memmap *next; +}; + + +/* + * moves is computed from "fraglist" and "memmap". Areas that are + * to be zeroed should be marked as such in the memmap, not in the + * fraglist. + */ + +int syslinux_compute_movelist(struct syslinux_movelist **movelist, + struct syslinux_movelist *fraglist, + struct syslinux_memmap *memmap); + +struct syslinux_memmap *syslinux_memory_map(void); +void syslinux_free_movelist(struct syslinux_movelist *); +int syslinux_add_movelist(struct syslinux_movelist **, + addr_t dst, addr_t src, addr_t len); +int syslinux_allocate_from_list(struct syslinux_movelist **freelist, + addr_t dst, addr_t len); +int syslinux_do_shuffle(struct syslinux_movelist *fraglist, + struct syslinux_memmap *memmap, + addr_t entry_point, addr_t entry_type, + uint16_t bootflags); +struct syslinux_memmap * +syslinux_target_memmap(struct syslinux_movelist *fraglist, + struct syslinux_memmap *memmap); + +/* Operatons on struct syslinux_memmap */ +struct syslinux_memmap *syslinux_init_memmap(void); +int syslinux_add_memmap(struct syslinux_memmap **list, + addr_t start, addr_t len, + enum syslinux_memmap_types type); +enum syslinux_memmap_types syslinux_memmap_type(struct syslinux_memmap *list, + addr_t start, addr_t len); +int syslinux_memmap_largest(struct syslinux_memmap *list, + enum syslinux_memmap_types type, + addr_t *start, addr_t *len); +void syslinux_free_memmap(struct syslinux_memmap *list); +struct syslinux_memmap *syslinux_dup_memmap(struct syslinux_memmap *list); +int syslinux_memmap_find(struct syslinux_memmap *list, + enum syslinux_memmap_types type, + addr_t *start, addr_t *len, addr_t align); + +/* Debugging functions */ +void syslinux_dump_movelist(FILE *file, struct syslinux_movelist *ml); +void syslinux_dump_memmap(FILE *file, struct syslinux_memmap *memmap); + +#endif /* _SYSLINUX_MOVEBITS_H */ diff --git a/com32/include/syslinux/pxe.h b/com32/include/syslinux/pxe.h new file mode 100644 index 0000000..31a4041 --- /dev/null +++ b/com32/include/syslinux/pxe.h @@ -0,0 +1,565 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux/pxe.h + * + * PXE definitions and function prototypes for SYSLINUX + */ + +#ifndef _SYSLINUX_PXE_H +#define _SYSLINUX_PXE_H + +#include +#include +#include + +/* PXE spec structures and definitions. These mostly follow the PXE + spec, except when the PXE spec is unnecessarily stupid. Of course, + that is most of the time. */ + +/* Basic types; use Unix-like _t convention instead of SCREAMING; also + re-use types we already have, like in_addr_t. */ + +typedef uint16_t pxenv_status_t; + +#define MAC_ADDR_LEN 16 +typedef uint8_t mac_addr_t[MAC_ADDR_LEN]; + +/* "Protected mode segment descriptor" according to PXE... */ +typedef struct { + uint16_t segaddr; + uint32_t physaddr; + uint16_t segsize; +} __packed pxe_segdesc_t; + +typedef struct { + uint16_t offs; + uint16_t seg; +} segoff16_t; + +typedef struct { +uint8_t opcode; +#define BOOTP_REQ 1 +#define BOOTP_REP 2 +uint8_t Hardware; +uint8_t Hardlen; +uint8_t Gatehops; +uint32_t ident; +uint16_t seconds; +uint16_t Flags; +#define BOOTP_BCAST 0x8000 +in_addr_t cip; /* Client IP address*/ +in_addr_t yip; /* You IP address*/ +in_addr_t sip; /* next server IP address*/ +in_addr_t gip; /*relay agent IP address */ +mac_addr_t CAddr; +uint8_t Sname[64]; +uint8_t bootfile[128]; +union + { + #define BOOTP_DHCPVEND 1024 + uint8_t d[BOOTP_DHCPVEND]; + struct { + uint8_t magic[4]; + #define VM_RFC1048 0x63825363L + uint32_t flags; + uint8_t pad[56]; + } v; + } vendor; +} __packed pxe_bootp_t; + + +/* Function calling structures and constants */ + +typedef struct s_PXENV_GET_CACHED_INFO +{ + pxenv_status_t Status; + uint16_t PacketType; +#define PXENV_PACKET_TYPE_DHCP_DISCOVER 1 +#define PXENV_PACKET_TYPE_DHCP_ACK 2 +#define PXENV_PACKET_TYPE_CACHED_REPLY 3 + uint16_t BufferSize; + segoff16_t Buffer; + uint16_t BufferLimit; +} __packed t_PXENV_GET_CACHED_INFO; + +typedef struct s_PXENV_START_UNDI { + pxenv_status_t Status; + uint16_t AX; + uint16_t BX; + uint16_t DX; + uint16_t DI; + uint16_t ES; +} __packed t_PXENV_START_UNDI; + +typedef struct s_PXENV_STOP_UNDI +{ + pxenv_status_t Status; +} __packed t_PXENV_STOP_UNDI; + +typedef struct s_PXENV_START_BASE +{ + pxenv_status_t Status; +} __packed t_PXENV_START_BASE; + +typedef struct s_PXENV_STOP_BASE +{ + pxenv_status_t Status; +} __packed t_PXENV_STOP_BASE; + +typedef struct s_PXENV_TFTP_OPEN +{ + pxenv_status_t Status; + in_addr_t ServerIPAddress; + in_addr_t GatewayIPAddress; + uint8_t FileName[128]; + in_port_t TFTPPort; + uint16_t PacketSize; +} __packed t_PXENV_TFTP_OPEN; + +typedef struct s_PXENV_TFTP_CLOSE +{ + pxenv_status_t Status; +} __packed t_PXENV_TFTP_CLOSE; + +typedef struct s_PXENV_TFTP_READ +{ + pxenv_status_t Status; + uint16_t PacketNumber; + uint16_t BufferSize; + segoff16_t Buffer; +} __packed t_PXENV_TFTP_READ; + +typedef struct s_PXENV_TFTP_READ_FILE +{ + pxenv_status_t Status; + uint8_t FileName[128]; + uint32_t BufferSize; + void * Buffer; + in_addr_t ServerIPAddress; + in_addr_t GatewayIPAddress; + in_addr_t McastIPAddress; + in_port_t TFTPClntPort; + in_port_t TFTPSrvPort; + uint16_t TFTPOpenTimeOut; + uint16_t TFTPReopenDelay; +} __packed t_PXENV_TFTP_READ_FILE; + +typedef struct s_PXENV_TFTP_GET_FSIZE +{ + pxenv_status_t Status; + in_addr_t ServerIPAddress; + in_addr_t GatewayIPAddress; + uint8_t FileName[128]; + uint32_t FileSize; +} __packed t_PXENV_TFTP_GET_FSIZE; + +typedef struct s_PXENV_UDP_OPEN +{ + pxenv_status_t status; + in_addr_t src_ip; +} __packed t_PXENV_UDP_OPEN; + +typedef struct s_PXENV_UDP_CLOSE +{ + pxenv_status_t status; +} __packed t_PXENV_UDP_CLOSE; + +typedef struct s_PXENV_UDP_WRITE +{ + pxenv_status_t status; + in_addr_t ip; + in_addr_t gw; + in_port_t src_port; + in_port_t dst_port; + uint16_t buffer_size; + segoff16_t buffer; +} __packed t_PXENV_UDP_WRITE; + +typedef struct s_PXENV_UDP_READ +{ + pxenv_status_t status; + in_addr_t src_ip; + in_addr_t dest_ip; + in_port_t s_port; + in_port_t d_port; + uint16_t buffer_size; + segoff16_t buffer; +} __packed t_PXENV_UDP_READ; + +typedef struct s_PXENV_UNDI_STARTUP +{ + pxenv_status_t Status; +} __packed t_PXENV_UNDI_STARTUP; + +typedef struct s_PXENV_UNDI_CLEANUP { + pxenv_status_t Status; +} __packed t_PXENV_UNDI_CLEANUP; + +typedef struct s_PXENV_UNDI_INITIALIZE { + pxenv_status_t Status; + void * ProtocolIni; + uint8_t reserved[8]; +} __packed t_PXENV_UNDI_INITIALIZE; + +#define MAXNUM_MCADDR 8 +typedef struct s_PXENV_UNDI_MCAST_ADDRESS +{ + uint16_t MCastAddrCount; + mac_addr_t McastAddr[MAXNUM_MCADDR]; +} __packed t_PXENV_UNDI_MCAST_ADDRESS; + +typedef struct s_PXENV_UNDI_RESET +{ + pxenv_status_t Status; + t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf; +} __packed t_PXENV_UNDI_RESET; + +typedef struct s_PXENV_UNDI_SHUTDOWN +{ + pxenv_status_t Status; +} __packed t_PXENV_UNDI_SHUTDOWN; + +typedef struct s_PXENV_UNDI_OPEN +{ + pxenv_status_t Status; + uint16_t OpenFlag; + uint16_t PktFilter; +#define FLTR_DIRECTED 0x0001 +#define FLTR_BRDCST 0x0002 +#define FLTR_PRMSCS 0x0004 +#define FLTR_SRC_RTG 0x0008 + t_PXENV_UNDI_MCAST_ADDRESS + R_Mcast_Buf; +} __packed t_PXENV_UNDI_OPEN; + +typedef struct s_PXENV_UNDI_CLOSE +{ + pxenv_status_t Status; +} __packed t_PXENV_UNDI_CLOSE; + +typedef struct s_PXENV_UNDI_TRANSMIT +{ + 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 s_PXENV_UNDI_TBD +{ + 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; + +typedef struct s_PXENV_UNDI_SET_MCAST_ADDRESS +{ + pxenv_status_t Status; + t_PXENV_UNDI_MCAST_ADDRESS + R_Mcast_Buf; +} __packed t_PXENV_UNDI_SET_MCAST_ADDR; + +typedef struct s_PXENV_UNDI_SET_STATION_ADDRESS +{ + pxenv_status_t Status; + mac_addr_t StationAddress; +} __packed t_PXENV_UNDI_SET_STATION_ADDR; + +typedef struct s_PXENV_UNDI_SET_PACKET_FILTER +{ + pxenv_status_t Status; + uint8_t filter; +} __packed t_PXENV_UNDI_SET_PACKET_FILTER; + +typedef struct s_PXENV_UNDI_GET_INFORMATION +{ + pxenv_status_t Status; + uint16_t BaseIo; + uint16_t IntNumber; + uint16_t MaxTranUnit; + uint16_t HwType; +#define ETHER_TYPE 1 +#define EXP_ETHER_TYPE 2 +#define IEEE_TYPE 6 +#define ARCNET_TYPE 7 + uint16_t HwAddrLen; + mac_addr_t CurrentNodeAddress; + mac_addr_t PermNodeAddress; + uint16_t ROMAddress; + uint16_t RxBufCt; + uint16_t TxBufCt; +} __packed t_PXENV_UNDI_GET_INFORMATION; + +typedef struct s_PXENV_UNDI_GET_STATISTICS +{ + pxenv_status_t Status; + uint32_t XmtGoodFrames; + uint32_t RcvGoodFrames; + uint32_t RcvCRCErrors; + uint32_t RcvResourceErrors; +} __packed t_PXENV_UNDI_GET_STATISTICS; + +typedef struct s_PXENV_UNDI_CLEAR_STATISTICS +{ + pxenv_status_t Status; +} __packed t_PXENV_UNDI_CLEAR_STATISTICS; + +typedef struct s_PXENV_UNDI_INITIATE_DIAGS +{ + pxenv_status_t Status; +} __packed t_PXENV_UNDI_INITIATE_DIAGS; + +typedef struct s_PXENV_UNDI_FORCE_INTERRUPT +{ + pxenv_status_t Status; +} __packed t_PXENV_UNDI_FORCE_INTERRUPT; + +typedef struct s_PXENV_UNDI_GET_MCAST_ADDRESS +{ + pxenv_status_t Status; + in_addr_t InetAddr; + mac_addr_t MediaAddr; +} __packed t_PXENV_UNDI_GET_MCAST_ADDR; + +typedef struct s_PXENV_UNDI_GET_NIC_TYPE +{ + pxenv_status_t Status; + uint8_t NicType; +#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; + } __packed pnp; + } __packed info; +} __packed t_PXENV_UNDI_GET_NIC_TYPE; + +typedef struct s_PXENV_UNDI_GET_IFACE_INFO +{ + pxenv_status_t Status; + uint8_t IfaceType[16]; + uint32_t LinkSpeed; + uint32_t ServiceFlags; + uint32_t Reserved[4]; +} __packed t_PXENV_UNDI_GET_NDIS_INFO; + +typedef struct s_PXENV_UNDI_GET_STATE +{ +#define PXE_UNDI_GET_STATE_STARTED 1 +#define PXE_UNDI_GET_STATE_INITIALIZED 2 +#define PXE_UNDI_GET_STATE_OPENED 3 + pxenv_status_t Status; + uint8_t UNDIstate; +} __packed t_PXENV_UNDI_GET_STATE; + +typedef struct s_PXENV_UNDI_ISR +{ + pxenv_status_t Status; + uint16_t FuncFlag; + uint16_t BufferLength; + uint16_t FrameLength; + uint16_t FrameHeaderLength; + segoff16_t Frame; + uint8_t ProtType; + uint8_t PktType; +} __packed t_PXENV_UNDI_ISR; +#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_USR_OUT_NOT_OURS 1 +/* One of these will be returned for + PXENV_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 + +/* Function numbers and error codes */ + +#define PXENV_TFTP_OPEN 0x0020 +#define PXENV_TFTP_CLOSE 0x0021 +#define PXENV_TFTP_READ 0x0022 +#define PXENV_TFTP_READ_FILE 0x0023 +#define PXENV_TFTP_READ_FILE_PMODE 0x0024 +#define PXENV_TFTP_GET_FSIZE 0x0025 + +#define PXENV_UDP_OPEN 0x0030 +#define PXENV_UDP_CLOSE 0x0031 +#define PXENV_UDP_READ 0x0032 +#define PXENV_UDP_WRITE 0x0033 + +#define PXENV_START_UNDI 0x0000 +#define PXENV_UNDI_STARTUP 0x0001 +#define PXENV_UNDI_CLEANUP 0x0002 +#define PXENV_UNDI_INITIALIZE 0x0003 +#define PXENV_UNDI_RESET_NIC 0x0004 +#define PXENV_UNDI_SHUTDOWN 0x0005 +#define PXENV_UNDI_OPEN 0x0006 +#define PXENV_UNDI_CLOSE 0x0007 +#define PXENV_UNDI_TRANSMIT 0x0008 +#define PXENV_UNDI_SET_MCAST_ADDR 0x0009 +#define PXENV_UNDI_SET_STATION_ADDR 0x000A +#define PXENV_UNDI_SET_PACKET_FILTER 0x000B +#define PXENV_UNDI_GET_INFORMATION 0x000C +#define PXENV_UNDI_GET_STATISTICS 0x000D +#define PXENV_UNDI_CLEAR_STATISTICS 0x000E +#define PXENV_UNDI_INITIATE_DIAGS 0x000F +#define PXENV_UNDI_FORCE_INTERRUPT 0x0010 +#define PXENV_UNDI_GET_MCAST_ADDR 0x0011 +#define PXENV_UNDI_GET_NIC_TYPE 0x0012 +#define PXENV_UNDI_GET_IFACE_INFO 0x0013 +#define PXENV_UNDI_ISR 0x0014 +#define PXENV_STOP_UNDI 0x0015 /* Overlap...? */ +#define PXENV_UNDI_GET_STATE 0x0015 /* Overlap...? */ + +#define PXENV_UNLOAD_STACK 0x0070 +#define PXENV_GET_CACHED_INFO 0x0071 +#define PXENV_RESTART_DHCP 0x0072 +#define PXENV_RESTART_TFTP 0x0073 +#define PXENV_MODE_SWITCH 0x0074 +#define PXENV_START_BASE 0x0075 +#define PXENV_STOP_BASE 0x0076 + +#define PXENV_EXIT_SUCCESS 0x0000 +#define PXENV_EXIT_FAILURE 0x0001 + +#define PXENV_STATUS_SUCCESS 0x00 +#define PXENV_STATUS_FAILURE 0x01 +#define PXENV_STATUS_BAD_FUNC 0x02 +#define PXENV_STATUS_UNSUPPORTED 0x03 +#define PXENV_STATUS_KEEP_UNDI 0x04 +#define PXENV_STATUS_KEEP_ALL 0x05 +#define PXENV_STATUS_OUT_OF_RESOURCES 0x06 +#define PXENV_STATUS_ARP_TIMEOUT 0x11 +#define PXENV_STATUS_UDP_CLOSED 0x18 +#define PXENV_STATUS_UDP_OPEN 0x19 +#define PXENV_STATUS_TFTP_CLOSED 0x1A +#define PXENV_STATUS_TFTP_OPEN 0x1B +#define PXENV_STATUS_MCOPY_PROBLEM 0x20 +#define PXENV_STATUS_BIS_INTEGRITY_FAILURE 0x21 +#define PXENV_STATUS_BIS_VALIDATE_FAILURE 0x22 +#define PXENV_STATUS_BIS_INIT_FAILURE 0x23 +#define PXENV_STATUS_BIS_SHUTDOWN_FAILURE 0x24 +#define PXENV_STATUS_BIS_GBOA_FAILURE 0x25 +#define PXENV_STATUS_BIS_FREE_FAILURE 0x26 +#define PXENV_STATUS_BIS_GSI_FAILURE 0x27 +#define PXENV_STATUS_BIS_BAD_CKSUM 0x28 +#define PXENV_STATUS_TFTP_CANNOT_ARP_ADDRESS 0x30 +#define PXENV_STATUS_TFTP_OPEN_TIMEOUT 0x32 + +#define PXENV_STATUS_TFTP_UNKNOWN_OPCODE 0x33 +#define PXENV_STATUS_TFTP_READ_TIMEOUT 0x35 +#define PXENV_STATUS_TFTP_ERROR_OPCODE 0x36 +#define PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION 0x38 +#define PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION 0x39 +#define PXENV_STATUS_TFTP_TOO_MANY_PACKAGES 0x3A +#define PXENV_STATUS_TFTP_FILE_NOT_FOUND 0x3B +#define PXENV_STATUS_TFTP_ACCESS_VIOLATION 0x3C +#define PXENV_STATUS_TFTP_NO_MCAST_ADDRESS 0x3D +#define PXENV_STATUS_TFTP_NO_FILESIZE 0x3E +#define PXENV_STATUS_TFTP_INVALID_PACKET_SIZE 0x3F +#define PXENV_STATUS_DHCP_TIMEOUT 0x51 +#define PXENV_STATUS_DHCP_NO_IP_ADDRESS 0x52 +#define PXENV_STATUS_DHCP_NO_BOOTFILE_NAME 0x53 +#define PXENV_STATUS_DHCP_BAD_IP_ADDRESS 0x54 +#define PXENV_STATUS_UNDI_INVALID_FUNCTION 0x60 +#define PXENV_STATUS_UNDI_MEDIATEST_FAILED 0x61 +#define PXENV_STATUS_UNDI_CANNOT_INIT_NIC_FOR_MCAST 0x62 +#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC 0x63 +#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_PHY 0x64 +#define PXENV_STATUS_UNDI_CANNOT_READ_CONFIG_DATA 0x65 +#define PXENV_STATUS_UNDI_CANNOT_READ_INIT_DATA 0x66 +#define PXENV_STATUS_UNDI_BAD_MAC_ADDRESS 0x67 +#define PXENV_STATUS_UNDI_BAD_EEPROM_CHECKSUM 0x68 +#define PXENV_STATUS_UNDI_ERROR_SETTING_ISR 0x69 +#define PXENV_STATUS_UNDI_INVALID_STATE 0x6A +#define PXENV_STATUS_UNDI_TRANSMIT_ERROR 0x6B +#define PXENV_STATUS_UNDI_INVALID_PARAMETER 0x6C +#define PXENV_STATUS_BSTRAP_PROMPT_MENU 0x74 +#define PXENV_STATUS_BSTRAP_MCAST_ADDR 0x76 +#define PXENV_STATUS_BSTRAP_MISSING_LIST 0x77 +#define PXENV_STATUS_BSTRAP_NO_RESPONSE 0x78 +#define PXENV_STATUS_BSTRAP_FILE_TOO_BIG 0x79 +#define PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE 0xA0 +#define PXENV_STATUS_BINL_NO_PXE_SERVER 0xA1 +#define PXENV_STATUS_NOT_AVAILABLE_IN_PMODE 0xA2 +#define PXENV_STATUS_NOT_AVAILABLE_IN_RMODE 0xA3 +#define PXENV_STATUS_BUSD_DEVICE_NOT_SUPPORTED 0xB0 +#define PXENV_STATUS_LOADER_NO_FREE_BASE_MEMORY 0xC0 +#define PXENV_STATUS_LOADER_NO_BC_ROMID 0xC1 +#define PXENV_STATUS_LOADER_BAD_BC_ROMID 0xC2 +#define PXENV_STATUS_LOADER_BAD_BC_RUNTIME_IMAGE 0xC3 +#define PXENV_STATUS_LOADER_NO_UNDI_ROMID 0xC4 +#define PXENV_STATUS_LOADER_BAD_UNDI_ROMID 0xC5 +#define PXENV_STATUS_LOADER_BAD_UNDI_DRIVER_IMAGE 0xC6 +#define PXENV_STATUS_LOADER_NO_PXE_STRUCT 0xC8 +#define PXENV_STATUS_LOADER_NO_PXENV_STRUCT 0xC9 +#define PXENV_STATUS_LOADER_UNDI_START 0xCA +#define PXENV_STATUS_LOADER_BC_START 0xCB + +/* SYSLINUX-defined PXE utility functions */ +int pxe_get_cached_info(int level, void **buf, size_t *len); +int pxe_get_nic_type(t_PXENV_UNDI_GET_NIC_TYPE *gnt); + +#endif /* _SYSLINUX_PXE_H */ diff --git a/com32/include/syslinux/reboot.h b/com32/include/syslinux/reboot.h new file mode 100644 index 0000000..e589982 --- /dev/null +++ b/com32/include/syslinux/reboot.h @@ -0,0 +1,41 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux/reboot.h + * + * Reboot the system + */ + +#ifndef _SYSLINUX_REBOOT_H +#define _SYSLINUX_REBOOT_H + +#include + +__noreturn syslinux_reboot(int warm); + +#endif /* _SYSLINUX_REBOOT_H */ diff --git a/com32/include/syslinux/resolve.h b/com32/include/syslinux/resolve.h new file mode 100644 index 0000000..0589c33 --- /dev/null +++ b/com32/include/syslinux/resolve.h @@ -0,0 +1,41 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux/resolve.h + * + * Interface to the syslinux DNS query API + */ + +#ifndef _SYSLINUX_RESOLVE_H +#define _SYSLINUX_RESOLVE_H + +#include + +in_addr_t syslinux_resolve_hostname(const char *); + +#endif /* _SYSLINUX_RESOLVE_H */ diff --git a/com32/include/syslinux/vesacon.h b/com32/include/syslinux/vesacon.h new file mode 100644 index 0000000..dad9fb5 --- /dev/null +++ b/com32/include/syslinux/vesacon.h @@ -0,0 +1,35 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#ifndef _SYSLINUX_VESACON_H +#define _SYSLINUX_VESACON_H + +int vesacon_default_background(void); +int vesacon_load_background(const char *); +int vesacon_set_background(unsigned int); + +#endif /* _SYSLINUX_VESACON_H */ diff --git a/com32/include/syslinux/video.h b/com32/include/syslinux/video.h new file mode 100644 index 0000000..737c742 --- /dev/null +++ b/com32/include/syslinux/video.h @@ -0,0 +1,43 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux/video.h + * + * SYSLINUX video API functions. + */ + +#ifndef _SYSLINUX_VIDEO_H +#define _SYSLINUX_VIDEO_H + +#include + +void syslinux_force_text_mode(void); +int syslinux_report_video_mode(uint16_t flags, uint16_t xsize, uint16_t ysize); +int syslinux_font_query(void **font); + +#endif /* _SYSLINUX_API_H */ diff --git a/com32/include/syslinux/zio.h b/com32/include/syslinux/zio.h new file mode 100644 index 0000000..23991e5 --- /dev/null +++ b/com32/include/syslinux/zio.h @@ -0,0 +1,13 @@ +/* + * + */ + +#ifndef _SYSLINUX_ZIO_H +#define _SYSLINUX_ZIO_H + +#include + +int zopen(const char *, int, ...); +FILE *zfopen(const char *, const char *); + +#endif /* _SYSLINUX_ZIO_H */ diff --git a/com32/include/time.h b/com32/include/time.h new file mode 100644 index 0000000..259386a --- /dev/null +++ b/com32/include/time.h @@ -0,0 +1,6 @@ +#ifndef _TIME_H +#define _TIME_H + +/* empty */ + +#endif diff --git a/com32/include/tinyjpeg.h b/com32/include/tinyjpeg.h new file mode 100644 index 0000000..e88d09a --- /dev/null +++ b/com32/include/tinyjpeg.h @@ -0,0 +1,72 @@ +/* + * Small jpeg decoder library (header file) + * + * Copyright (c) 2006, Luc Saillard + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * - Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + * + */ + + +#ifndef __JPEGDEC_H__ +#define __JPEGDEC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct jdec_private; + +/* Flags that can be set by any applications */ +#define TINYJPEG_FLAGS_MJPEG_TABLE (1<<1) + +/* Format accepted in outout */ +struct tinyjpeg_colorspace; +typedef const struct tinyjpeg_colorspace *tinyjpeg_colorspace_t; + +extern const tinyjpeg_colorspace_t TINYJPEG_FMT_GREY, TINYJPEG_FMT_BGR24, + TINYJPEG_FMT_RGB24, TINYJPEG_FMT_YUV420P, TINYJPEG_FMT_BGRA32, + TINYJPEG_FMT_RGBA32; + +struct jdec_private *tinyjpeg_init(void); +void tinyjpeg_free(struct jdec_private *priv); + +int tinyjpeg_parse_header(struct jdec_private *priv, const unsigned char *buf, unsigned int size); +int tinyjpeg_decode(struct jdec_private *priv, tinyjpeg_colorspace_t pixel_format); +const char *tinyjpeg_get_errorstring(struct jdec_private *priv); +void tinyjpeg_get_size(struct jdec_private *priv, unsigned int *width, unsigned int *height); +int tinyjpeg_get_components(struct jdec_private *priv, unsigned char **components, unsigned int ncomponents); +int tinyjpeg_set_components(struct jdec_private *priv, unsigned char * const *components, unsigned int ncomponents); +int tinyjpeg_get_bytes_per_row(struct jdec_private *priv, unsigned int *bytes, unsigned int ncomponents); +int tinyjpeg_set_bytes_per_row(struct jdec_private *priv, const unsigned int *bytes, unsigned int ncomponents); +int tinyjpeg_set_flags(struct jdec_private *priv, int flags); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/com32/include/unistd.h b/com32/include/unistd.h new file mode 100644 index 0000000..c0b52d6 --- /dev/null +++ b/com32/include/unistd.h @@ -0,0 +1,33 @@ +/* + * unistd.h + */ + +#ifndef _UNISTD_H +#define _UNISTD_H + +#include +#include +#include +#include + +__extern __noreturn _exit(int); + +__extern int open(const char *, int, ...); +__extern int close(int); + +__extern ssize_t read(int, void *, size_t); +__extern ssize_t write(int, const void *, size_t); + +__extern int isatty(int); + +__extern int getscreensize(int, int *, int *); + +__extern char *getcwd(char *, int); +__extern int chdir(const char *); + +/* Standard file descriptor numbers. */ +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 + +#endif /* _UNISTD_H */ diff --git a/com32/include/zconf.h b/com32/include/zconf.h new file mode 100644 index 0000000..5d10eaa --- /dev/null +++ b/com32/include/zconf.h @@ -0,0 +1,322 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflatePrime z_deflatePrime +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table + +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) +# define WIN32 +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +#define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/com32/include/zlib.h b/com32/include/zlib.h new file mode 100644 index 0000000..92edf96 --- /dev/null +++ b/com32/include/zlib.h @@ -0,0 +1,1200 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.1, November 17th, 2003 + + Copyright (C) 1995-2003 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.1" +#define ZLIB_VERNUM 0x1210 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by the in-memory functions is the zlib + format, which is a zlib wrapper documented in RFC 1950, wrapped around a + deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + This library does not provide any functions to write gzip files in memory. + However such functions could be easily written using zlib's deflate function, + the documentation in the gzip RFC, and the examples in gzio.c. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: ascii or binary */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + the compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + the value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update data_type if it can make a good guess about + the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it get to the next deflate block boundary. When decoding the zlib + or gzip format, this will cause inflate() to return immediately after the + header and before the first block. When doing a raw inflate, inflate() will + go ahead and process the first block, and will return when it gets to the end + of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster approach + may be used for the single inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm-adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), + no header crc, and the operating system will be set to 255 (unknown). + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as + Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy + parameter only affects the compression ratio but not the correctness of the + compressed output even if it is not set appropriately. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() + or deflateInit2(). This would be used to allocate an output buffer + for deflation in a single pass, and so would be called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the + bits leftover from a previous deflate stream when appending to it. As such, + this function can only be used for raw deflate, and must be used before the + first deflate() call after a deflateInit2() or deflateReset(). bits must be + less than or equal to 16, and that many of the least significant bits of + value will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative + memLevel). msg is set to null if there is no error message. inflateInit2 + does not perform any decompression apart from reading the zlib header if + present: this will be done by inflate(). (So next_in and avail_in may be + modified, but next_out and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate + if this call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by this call of + inflate. The compressor and decompressor must use exactly the same + dictionary (see deflateSetDictionary). + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_stream FAR *strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not + be allocated, or Z_VERSION_ERROR if the version of the library does not + match the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_stream FAR *strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free + the allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects + only the raw deflate stream to decompress. This is different from the + normal behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format + error in the deflate stream (in which case strm->msg is set to indicate the + nature of the error), or Z_STREAM_ERROR if the stream was not properly + initialized. In the case of Z_BUF_ERROR, an input or output error can be + distinguished using strm->next_in which will be Z_NULL only if in() returned + an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to + out() returning non-zero. (in() will always be called before out(), so + strm->next_in is assured to be defined if out() returns non-zero.) Note + that inflateBack() cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_stream FAR *strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least the value returned + by compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before + a compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h", or 'R' for run-length encoding + as in "wb1R". (See the description of deflateInit2 for more information + about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). The number of + uncompressed bytes written is limited to 4095. The caller should assure that + this limit is not exceeded. If it is exceeded, then gzprintf() will return + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read again later. + Only one character of push-back is allowed. gzungetc() returns the + character pushed, or -1 on failure. gzungetc() will fail if a + character has been pushed but not read yet, or if c is -1. The pushed + character will be discarded if the stream is repositioned with gzseek() + or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); + +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running crc with the bytes buf[0..len-1] and return the updated + crc. If buf is NULL, this function returns the required initial value + for the crc. Pre- and post-conditioning (one's complement) is performed + within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_stream FAR *strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int err)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/com32/lib/MCONFIG b/com32/lib/MCONFIG new file mode 100644 index 0000000..1eb18db --- /dev/null +++ b/com32/lib/MCONFIG @@ -0,0 +1,69 @@ +# -*- makefile -*- + +include $(topdir)/MCONFIG + +GCCOPT := $(call gcc_ok,-std=gnu99,) \ + $(call gcc_ok,-m32,) \ + $(call gcc_ok,-fno-stack-protector,) \ + +INCLUDE = -I. +STRIP = strip --strip-all -R .comment -R .note + +# zlib and libpng configuration flags +LIBFLAGS = -DDYNAMIC_CRC_TABLE -DPNG_NO_CONSOLE_IO \ + -DPNG_NO_WRITE_SUPPORTED \ + -DPNG_NO_MNG_FEATURES \ + -DPNG_NO_READ_tIME -DPNG_NO_WRITE_tIME + +# We need some features in libpng which apparently aren't available in the +# fixed-point versions. It's OK, because we have to have a non-graphical +# fallback anyway, just use that on old machines... +# LIBFLAGS += -DPNG_NO_FLOATING_POINT_SUPPORTED + +REQFLAGS = $(GCCOPT) -g -mregparm=3 -DREGPARM=3 -D__COM32__ \ + -nostdinc -iwithprefix include -I. -I./sys -I../include +OPTFLAGS = -Os -march=i386 -falign-functions=0 -falign-jumps=0 \ + -falign-labels=0 -ffast-math -fomit-frame-pointer +WARNFLAGS = -W -Wall -Wpointer-arith -Wwrite-strings -Wstrict-prototypes -Winline + +CFLAGS = $(OPTFLAGS) $(REQFLAGS) $(WARNFLAGS) $(LIBFLAGS) +LDFLAGS = -m elf32_i386 + +.SUFFIXES: .c .o .a .so .lo .i .S .s .ls .ss .lss + +% : %.c # Cancel default rule + +% : %.S + +.c.o: + $(CC) $(MAKEDEPS) $(CFLAGS) -c -o $@ $< + +.c.i: + $(CC) $(MAKEDEPS) $(CFLAGS) -E -o $@ $< + +.c.s: + $(CC) $(MAKEDEPS) $(CFLAGS) -S -o $@ $< + +.S.o: + $(CC) $(MAKEDEPS) $(CFLAGS) -D__ASSEMBLY__ -c -o $@ $< + +.S.s: + $(CC) $(MAKEDEPS) $(CFLAGS) -D__ASSEMBLY__ -E -o $@ $< + +.S.lo: + $(CC) $(MAKEDEPS) $(CFLAGS) $(SOFLAGS) -D__ASSEMBLY__ -c -o $@ $< + +.S.ls: + $(CC) $(MAKEDEPS) $(CFLAGS) $(SOFLAGS) -D__ASSEMBLY__ -E -o $@ $< + +.s.o: + $(CC) $(MAKEDEPS) $(CFLAGS) -x assembler -c -o $@ $< + +.ls.lo: + $(CC) $(MAKEDEPS) $(CFLAGS) $(SOFLAGS) -x assembler -c -o $@ $< + +.c.lo: + $(CC) $(MAKEDEPS) $(CFLAGS) $(SOFLAGS) -c -o $@ $< + +.c.ls: + $(CC) $(MAKEDEPS) $(CFLAGS) $(SOFLAGS) -S -o $@ $< diff --git a/com32/lib/Makefile b/com32/lib/Makefile new file mode 100644 index 0000000..5e0e15a --- /dev/null +++ b/com32/lib/Makefile @@ -0,0 +1,147 @@ +# +# ONLY INCLUDE MIT OR 2/3-BSD-LICENSED CODE IN THIS LIBRARY +# + +# Include configuration rules +NOGPL := 1 +topdir = ../.. +include MCONFIG + +LIBOBJS = \ + abort.o atexit.o atoi.o atol.o atoll.o calloc.o creat.o \ + ctypes.o errno.o fgetc.o fgets.o fopen.o fprintf.o fputc.o \ + fclose.o putchar.o setjmp.o \ + fputs.o fread2.o fread.o free.o fwrite2.o fwrite.o getopt.o \ + lrand48.o malloc.o stack.o memccpy.o memchr.o memcmp.o \ + memcpy.o mempcpy.o memmem.o memmove.o memset.o memswap.o \ + exit.o onexit.o \ + perror.o printf.o puts.o qsort.o realloc.o seed48.o snprintf.o \ + sprintf.o srand48.o sscanf.o stack.o strcasecmp.o strcat.o \ + strchr.o strcmp.o strcpy.o strpcpy.o strdup.o strlen.o \ + strerror.o strnlen.o \ + strncasecmp.o strncat.o strncmp.o strncpy.o strndup.o \ + stpcpy.o stpncpy.o \ + strntoimax.o strntoumax.o strrchr.o strsep.o strspn.o strstr.o \ + strtoimax.o strtok.o strtol.o strtoll.o strtoul.o strtoull.o \ + strtoumax.o vfprintf.o vprintf.o vsnprintf.o vsprintf.o \ + asprintf.o vasprintf.o strlcpy.o strlcat.o \ + vsscanf.o zalloc.o \ + \ + opendir.o readdir.o closedir.o getcwd.o chdir.o fdopendir.o \ + \ + libgcc/__ashldi3.o libgcc/__udivdi3.o \ + libgcc/__negdi2.o libgcc/__ashrdi3.o libgcc/__lshrdi3.o \ + libgcc/__muldi3.o libgcc/__udivmoddi4.o libgcc/__umoddi3.o \ + libgcc/__divdi3.o libgcc/__moddi3.o \ + \ + sys/intcall.o sys/farcall.o sys/cfarcall.o sys/zeroregs.o \ + sys/entry.o sys/exit.o sys/argv.o sys/times.o \ + sys/fileinfo.o sys/opendev.o sys/read.o sys/write.o sys/ftell.o \ + sys/close.o sys/open.o sys/fileread.o sys/fileclose.o \ + sys/isatty.o sys/fstat.o \ + \ + sys/zfile.o sys/zfopen.o \ + \ + sys/openconsole.o sys/line_input.o \ + sys/colortable.o sys/screensize.o \ + \ + sys/stdcon_read.o sys/stdcon_write.o sys/rawcon_read.o \ + sys/rawcon_write.o sys/err_read.o sys/err_write.o \ + sys/null_read.o sys/null_write.o sys/serial_write.o \ + \ + sys/xserial_write.o \ + \ + sys/ansi.o \ + \ + sys/ansicon_write.o sys/ansiserial_write.o \ + \ + sys/vesacon_write.o sys/vesaserial_write.o \ + sys/vesa/initvesa.o sys/vesa/drawtxt.o sys/vesa/background.o \ + sys/vesa/alphatbl.o sys/vesa/screencpy.o sys/vesa/fmtpixel.o \ + \ + pci/cfgtype.o pci/scan.o \ + pci/readb.o pci/readw.o pci/readl.o pci/readbios.o \ + pci/writeb.o pci/writew.o pci/writel.o pci/writebios.o \ + \ + zlib/adler32.o zlib/compress.o zlib/crc32.o zlib/gzio.o \ + zlib/uncompr.o zlib/deflate.o zlib/trees.o zlib/zutil.o \ + zlib/inflate.o zlib/infback.o zlib/inftrees.o zlib/inffast.o \ + \ + libpng/png.o libpng/pngset.o libpng/pngget.o libpng/pngrutil.o \ + libpng/pngtrans.o libpng/pngwutil.o libpng/pngread.o \ + libpng/pngrio.o libpng/pngwio.o libpng/pngwrite.o \ + libpng/pngrtran.o libpng/pngwtran.o libpng/pngmem.o \ + libpng/pngerror.o libpng/pngpread.o \ + \ + jpeg/tinyjpeg.o jpeg/jidctflt.o jpeg/decode1.o jpeg/decode3.o \ + jpeg/rgb24.o jpeg/bgr24.o jpeg/yuv420p.o jpeg/grey.o \ + jpeg/rgba32.o jpeg/bgra32.o \ + \ + sys/x86_init_fpu.o math/pow.o math/strtod.o \ + \ + syslinux/idle.o syslinux/reboot.o \ + syslinux/features.o syslinux/config.o syslinux/serial.o \ + syslinux/ipappend.o syslinux/dsinfo.o syslinux/version.o \ + syslinux/keyboard.o \ + \ + syslinux/memscan.o \ + \ + syslinux/addlist.o syslinux/freelist.o syslinux/memmap.o \ + syslinux/movebits.o syslinux/shuffle.o syslinux/shuffle_pm.o \ + syslinux/shuffle_rm.o syslinux/zonelist.o \ + syslinux/dump_mmap.o syslinux/dump_movelist.o \ + \ + syslinux/run_default.o syslinux/run_command.o \ + syslinux/cleanup.o syslinux/localboot.o syslinux/runimage.o \ + \ + syslinux/loadfile.o syslinux/floadfile.o syslinux/zloadfile.o \ + \ + syslinux/load_linux.o syslinux/initramfs.o \ + syslinux/initramfs_file.o syslinux/initramfs_loadfile.o \ + syslinux/initramfs_archive.o \ + \ + syslinux/pxe_get_cached.o syslinux/pxe_get_nic.o \ + \ + syslinux/adv.o syslinux/advwrite.o syslinux/getadv.o \ + syslinux/setadv.o + +BINDIR = /usr/bin +LIBDIR = /usr/lib +DATADIR = /usr/share +AUXDIR = $(DATADIR)/syslinux +INCDIR = /usr/include +COM32DIR = $(AUXDIR)/com32 + +all: libcom32.a + +libcom32.a : $(LIBOBJS) + rm -f $@ + $(AR) cq $@ $^ + $(RANLIB) $@ + +tidy dist clean: + rm -f sys/vesa/alphatbl.c + find . \( -name \*.o -o -name \*.a -o -name .\*.d -o -name \*.tmp \) -print0 | \ + xargs -0r rm -f + +spotless: clean + rm -f *.a + rm -f *~ \#* */*~ */\#* + +install: all + mkdir -m 755 -p $(INSTALLROOT)$(COM32DIR) + install -m 644 libcom32.a com32.ld $(INSTALLROOT)$(COM32DIR) + -rm -rf $(INSTALLROOT)$(COM32DIR)/include + cp -r ../include $(INSTALLROOT)$(COM32DIR) + +# These files are performance critical, and doesn't compile well with -Os +sys/vesa/drawtxt.o: sys/vesa/drawtxt.c + $(CC) $(MAKEDEPS) $(CFLAGS) -O3 -c -o $@ $< + +sys/vesa/alphatbl.c: sys/vesa/alphatbl.pl + $(PERL) $< > $@ + +jpeg/jidctflt.o: jpeg/jidctflt.c + $(CC) $(MAKEDEPS) $(CFLAGS) -O3 -c -o $@ $< + +-include .*.d */.*.d */*/.*.d diff --git a/com32/lib/abort.c b/com32/lib/abort.c new file mode 100644 index 0000000..1c63464 --- /dev/null +++ b/com32/lib/abort.c @@ -0,0 +1,11 @@ +/* + * abort.c + */ + +#include +#include + +void abort(void) +{ + _exit(255); +} diff --git a/com32/lib/asprintf.c b/com32/lib/asprintf.c new file mode 100644 index 0000000..6b1b4ff --- /dev/null +++ b/com32/lib/asprintf.c @@ -0,0 +1,30 @@ +/* + * asprintf.c + */ + +#include +#include +#include + +int asprintf(char **bufp, const char *format, ...) +{ + va_list ap, ap1; + int rv; + int bytes; + char *p; + + va_start(ap, format); + va_copy(ap1, ap); + + bytes = vsnprintf(NULL, 0, format, ap1) + 1; + va_end(ap1); + + *bufp = p = malloc(bytes); + if ( !p ) + return -1; + + rv = vsnprintf(p, bytes, format, ap); + va_end(ap); + + return rv; +} diff --git a/com32/lib/atexit.c b/com32/lib/atexit.c new file mode 100644 index 0000000..078dd8b --- /dev/null +++ b/com32/lib/atexit.c @@ -0,0 +1,10 @@ +/* + * atexit.c + */ + +#include + +int atexit(void (*fctn)(void)) +{ + return on_exit((void (*)(int, void *))fctn, NULL); +} diff --git a/com32/lib/atexit.h b/com32/lib/atexit.h new file mode 100644 index 0000000..b3aabaf --- /dev/null +++ b/com32/lib/atexit.h @@ -0,0 +1,16 @@ +/* + * atexit.h + * + * atexit()/on_exit() internal definitions + */ + +#ifndef ATEXIT_H +#define ATEXIT_H + +struct atexit { + void (*fctn)(int, void *); + void *arg; /* on_exit() parameter */ + struct atexit *next; +}; + +#endif /* ATEXIT_H */ diff --git a/com32/lib/atoi.c b/com32/lib/atoi.c new file mode 100644 index 0000000..a6ec0bf --- /dev/null +++ b/com32/lib/atoi.c @@ -0,0 +1,3 @@ +#define TYPE int +#define NAME atoi +#include "atox.c" diff --git a/com32/lib/atol.c b/com32/lib/atol.c new file mode 100644 index 0000000..e65484e --- /dev/null +++ b/com32/lib/atol.c @@ -0,0 +1,3 @@ +#define TYPE long +#define NAME atol +#include "atox.c" diff --git a/com32/lib/atoll.c b/com32/lib/atoll.c new file mode 100644 index 0000000..25df79e --- /dev/null +++ b/com32/lib/atoll.c @@ -0,0 +1,3 @@ +#define TYPE long long +#define NAME atoll +#include "atox.c" diff --git a/com32/lib/atox.c b/com32/lib/atox.c new file mode 100644 index 0000000..56f8d93 --- /dev/null +++ b/com32/lib/atox.c @@ -0,0 +1,14 @@ +/* + * atox.c + * + * atoi(), atol(), atoll() + */ + +#include +#include +#include + +TYPE NAME (const char *nptr) +{ + return (TYPE) strntoumax(nptr, (char **)NULL, 10, ~(size_t)0); +} diff --git a/com32/lib/calloc.c b/com32/lib/calloc.c new file mode 100644 index 0000000..97659b1 --- /dev/null +++ b/com32/lib/calloc.c @@ -0,0 +1,13 @@ +/* + * calloc.c + */ + +#include +#include + +/* FIXME: This should look for multiplication overflow */ + +void *calloc(size_t nmemb, size_t size) +{ + return zalloc(size*nmemb); +} diff --git a/com32/lib/chdir.c b/com32/lib/chdir.c new file mode 100644 index 0000000..88dceec --- /dev/null +++ b/com32/lib/chdir.c @@ -0,0 +1,13 @@ +/* + * chdir.c + */ + +#include +#include +#include + +int chdir(const char *path) +{ + errno = ENOSYS; + return -1; +} diff --git a/com32/lib/closedir.c b/com32/lib/closedir.c new file mode 100644 index 0000000..8a0430e --- /dev/null +++ b/com32/lib/closedir.c @@ -0,0 +1,29 @@ +/* + * closedir.c + */ + +#include +#include +#include + +#include +#include +#include +#include + +int closedir(DIR *dir) +{ + int rv; + com32sys_t regs; + if (dir == NULL) { + rv = 0; + } else { + memset(®s, 0, sizeof regs); /* ?Needed? */ + regs.eax.w[0] = 0x0022; + regs.esi.w[0] = dir->dd_fd; + __com32.cs_intcall(0x22, ®s, ®s); + free(dir); /* garbage collection? */ + rv = 0; + } + return rv; +} diff --git a/com32/lib/com32.ld b/com32/lib/com32.ld new file mode 100644 index 0000000..36d5b6e --- /dev/null +++ b/com32/lib/com32.ld @@ -0,0 +1,127 @@ +/* + * Linker script for COM32 binaries using libcom32 + */ + +/* Script for -z combreloc: combine and sort reloc sections */ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", + "elf32-i386") +OUTPUT_ARCH(i386) +EXTERN(_start) +ENTRY(_start) +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = 0x101000; + PROVIDE (__executable_start = .); + + .init : + { + KEEP (*(.init)) + } =0x90909090 + .text : + { + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + } =0x90909090 + .fini : + { + KEEP (*(.fini)) + } =0x90909090 + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + + /* Ensure the __preinit_array_start label is properly aligned. We + could instead move the label definition inside the section, but + the linker would then create the section even if it turns out to + be empty, which isn't pretty. */ + . = ALIGN(4); + PROVIDE (__preinit_array_start = .); + .preinit_array : { *(.preinit_array) } + PROVIDE (__preinit_array_end = .); + PROVIDE (__init_array_start = .); + .init_array : { *(.init_array) } + PROVIDE (__init_array_end = .); + PROVIDE (__fini_array_start = .); + .fini_array : { *(.fini_array) } + PROVIDE (__fini_array_end = .); + PROVIDE (__ctors_start = .); + .ctors : + { + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + PROVIDE (__ctors_end = .); + PROVIDE (__dtors_start = .); + .dtors : + { + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + PROVIDE (__dtors_end = .); + + /* Adjust the address for the data segment. Avoid mixing code and + data within same 128-byte chunk. */ + . = ALIGN(128); + + .data : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + _edata = .; + PROVIDE (edata = .); + __bss_start = .; + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. */ + . = ALIGN(32 / 8); + } + . = ALIGN(32 / 8); + _end = .; + PROVIDE (end = .); + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /DISCARD/ : { *(.note.GNU-stack) } +} diff --git a/com32/lib/creat.c b/com32/lib/creat.c new file mode 100644 index 0000000..9bd2217 --- /dev/null +++ b/com32/lib/creat.c @@ -0,0 +1,12 @@ +/* + * creat.c + */ + +#include +#include +#include + +int creat(const char *pathname, mode_t mode) +{ + return open(pathname, O_CREAT|O_WRONLY|O_TRUNC, mode); +} diff --git a/com32/lib/ctypes.c b/com32/lib/ctypes.c new file mode 100644 index 0000000..cfe4821 --- /dev/null +++ b/com32/lib/ctypes.c @@ -0,0 +1,284 @@ +/* + * ctypes.c + * + * This is the array that defines classes. + * This assumes ISO 8859-1. + */ + +#include + +const unsigned char __ctypes[257] = { + 0, /* EOF */ + + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl|__ctype_space, /* BS */ + __ctype_cntrl|__ctype_space, /* TAB */ + __ctype_cntrl|__ctype_space, /* LF */ + __ctype_cntrl|__ctype_space, /* VT */ + __ctype_cntrl|__ctype_space, /* FF */ + __ctype_cntrl|__ctype_space, /* CR */ + __ctype_cntrl, /* control character */ + + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + + __ctype_print|__ctype_space, /* space */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + + __ctype_print|__ctype_digit|__ctype_xdigit, /* digit */ + __ctype_print|__ctype_digit|__ctype_xdigit, /* digit */ + __ctype_print|__ctype_digit|__ctype_xdigit, /* digit */ + __ctype_print|__ctype_digit|__ctype_xdigit, /* digit */ + __ctype_print|__ctype_digit|__ctype_xdigit, /* digit */ + __ctype_print|__ctype_digit|__ctype_xdigit, /* digit */ + __ctype_print|__ctype_digit|__ctype_xdigit, /* digit */ + __ctype_print|__ctype_digit|__ctype_xdigit, /* digit */ + __ctype_print|__ctype_digit|__ctype_xdigit, /* digit */ + __ctype_print|__ctype_digit|__ctype_xdigit, /* digit */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_upper|__ctype_xdigit, /* A-F */ + __ctype_print|__ctype_upper|__ctype_xdigit, /* A-F */ + __ctype_print|__ctype_upper|__ctype_xdigit, /* A-F */ + __ctype_print|__ctype_upper|__ctype_xdigit, /* A-F */ + __ctype_print|__ctype_upper|__ctype_xdigit, /* A-F */ + __ctype_print|__ctype_upper|__ctype_xdigit, /* A-F */ + __ctype_print|__ctype_upper, /* G-Z */ + __ctype_print|__ctype_upper, /* G-Z */ + __ctype_print|__ctype_upper, /* G-Z */ + __ctype_print|__ctype_upper, /* G-Z */ + __ctype_print|__ctype_upper, /* G-Z */ + __ctype_print|__ctype_upper, /* G-Z */ + __ctype_print|__ctype_upper, /* G-Z */ + __ctype_print|__ctype_upper, /* G-Z */ + __ctype_print|__ctype_upper, /* G-Z */ + + __ctype_print|__ctype_upper, /* G-Z */ + __ctype_print|__ctype_upper, /* G-Z */ + __ctype_print|__ctype_upper, /* G-Z */ + __ctype_print|__ctype_upper, /* G-Z */ + __ctype_print|__ctype_upper, /* G-Z */ + __ctype_print|__ctype_upper, /* G-Z */ + __ctype_print|__ctype_upper, /* G-Z */ + __ctype_print|__ctype_upper, /* G-Z */ + __ctype_print|__ctype_upper, /* G-Z */ + __ctype_print|__ctype_upper, /* G-Z */ + __ctype_print|__ctype_upper, /* G-Z */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_lower|__ctype_xdigit, /* a-f */ + __ctype_print|__ctype_lower|__ctype_xdigit, /* a-f */ + __ctype_print|__ctype_lower|__ctype_xdigit, /* a-f */ + __ctype_print|__ctype_lower|__ctype_xdigit, /* a-f */ + __ctype_print|__ctype_lower|__ctype_xdigit, /* a-f */ + __ctype_print|__ctype_lower|__ctype_xdigit, /* a-f */ + __ctype_print|__ctype_lower, /* g-z */ + __ctype_print|__ctype_lower, /* g-z */ + __ctype_print|__ctype_lower, /* g-z */ + __ctype_print|__ctype_lower, /* g-z */ + __ctype_print|__ctype_lower, /* g-z */ + __ctype_print|__ctype_lower, /* g-z */ + __ctype_print|__ctype_lower, /* g-z */ + __ctype_print|__ctype_lower, /* g-z */ + __ctype_print|__ctype_lower, /* g-z */ + + __ctype_print|__ctype_lower, /* g-z */ + __ctype_print|__ctype_lower, /* g-z */ + __ctype_print|__ctype_lower, /* g-z */ + __ctype_print|__ctype_lower, /* g-z */ + __ctype_print|__ctype_lower, /* g-z */ + __ctype_print|__ctype_lower, /* g-z */ + __ctype_print|__ctype_lower, /* g-z */ + __ctype_print|__ctype_lower, /* g-z */ + __ctype_print|__ctype_lower, /* g-z */ + __ctype_print|__ctype_lower, /* g-z */ + __ctype_print|__ctype_lower, /* g-z */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_cntrl, /* control character */ + + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + __ctype_cntrl, /* control character */ + + __ctype_print|__ctype_space, /* NBSP */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_punct, /* punctuation */ + + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_upper, /* upper accented */ + __ctype_print|__ctype_lower, /* lower accented */ + + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_punct, /* punctuation */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ + __ctype_print|__ctype_lower, /* lower accented */ +}; diff --git a/com32/lib/errno.c b/com32/lib/errno.c new file mode 100644 index 0000000..f280e30 --- /dev/null +++ b/com32/lib/errno.c @@ -0,0 +1,7 @@ +/* + * errno.c + * + */ +#include + +int errno; diff --git a/com32/lib/exit.c b/com32/lib/exit.c new file mode 100644 index 0000000..68ff5f8 --- /dev/null +++ b/com32/lib/exit.c @@ -0,0 +1,41 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * exit.c + * + * The regular exit + */ + +#include + +extern __noreturn (*__exit_handler)(int); + +__noreturn exit(int rv) +{ + __exit_handler(rv); +} diff --git a/com32/lib/fclose.c b/com32/lib/fclose.c new file mode 100644 index 0000000..41f6a62 --- /dev/null +++ b/com32/lib/fclose.c @@ -0,0 +1,11 @@ +/* + * fclose.c + */ + +#include +#include + +int fclose(FILE *__f) +{ + return close(fileno(__f)); +} diff --git a/com32/lib/fdopendir.c b/com32/lib/fdopendir.c new file mode 100644 index 0000000..83a7ac6 --- /dev/null +++ b/com32/lib/fdopendir.c @@ -0,0 +1,13 @@ +/* + * fdopendir.c + */ + +#include +#include +#include + +DIR *fdopendir(int __fd) +{ + errno = ENOSYS; + return NULL; +} diff --git a/com32/lib/fgetc.c b/com32/lib/fgetc.c new file mode 100644 index 0000000..5aa6f76 --- /dev/null +++ b/com32/lib/fgetc.c @@ -0,0 +1,19 @@ +/* + * fgetc.c + * + * Extremely slow fgetc implementation, using _fread(). If people + * actually need character-oriented input to be fast, we may actually + * have to implement buffering. Sigh. + */ + +#include +#include +#include +#include + +int fgetc(FILE *f) +{ + unsigned char ch; + + return (_fread(&ch, 1, f) == 1) ? (int)ch : EOF; +} diff --git a/com32/lib/fgets.c b/com32/lib/fgets.c new file mode 100644 index 0000000..474de49 --- /dev/null +++ b/com32/lib/fgets.c @@ -0,0 +1,31 @@ +/* + * fgets.c + * + * This will be very slow due to the implementation of getc(), + * but we can't afford to drain characters we don't need from + * the input. + */ + +#include + +char *fgets(char *s, int n, FILE *f) +{ + int ch; + char *p = s; + + while ( n > 1 ) { + ch = getc(f); + if ( ch == EOF ) { + *p = '\0'; + return (p == s) ? NULL : s; + } + *p++ = ch; + if ( ch == '\n' ) + break; + n--; + } + if ( n ) + *p = '\0'; + + return s; +} diff --git a/com32/lib/fopen.c b/com32/lib/fopen.c new file mode 100644 index 0000000..bf1e452 --- /dev/null +++ b/com32/lib/fopen.c @@ -0,0 +1,43 @@ +/* + * fopen.c + */ + +#include +#include +#include + +FILE *fopen(const char *file, const char *mode) +{ + int flags = O_RDONLY; + int plus = 0; + int fd; + + while ( *mode ) { + switch ( *mode ) { + case 'r': + flags = O_RDONLY; + break; + case 'w': + flags = O_WRONLY|O_CREAT|O_TRUNC; + break; + case 'a': + flags = O_WRONLY|O_CREAT|O_APPEND; + break; + case '+': + plus = 1; + break; + } + mode++; + } + + if ( plus ) { + flags = (flags & ~(O_RDONLY|O_WRONLY)) | O_RDWR; + } + + fd = open(file, flags, 0666); + + if ( fd < 0 ) + return NULL; + else + return fdopen(fd, mode); +} diff --git a/com32/lib/fopendev.c b/com32/lib/fopendev.c new file mode 100644 index 0000000..2eb2a5a --- /dev/null +++ b/com32/lib/fopendev.c @@ -0,0 +1,43 @@ +/* + * fopendev.c + */ + +#include +#include +#include + +FILE *fopendev(const struct dev_info *dev, const char *mode) +{ + int flags = O_RDONLY; + int plus = 0; + int fd; + + while ( *mode ) { + switch ( *mode ) { + case 'r': + flags = O_RDONLY; + break; + case 'w': + flags = O_WRONLY|O_CREAT|O_TRUNC; + break; + case 'a': + flags = O_WRONLY|O_CREAT|O_APPEND; + break; + case '+': + plus = 1; + break; + } + mode++; + } + + if ( plus ) { + flags = (flags & ~(O_RDONLY|O_WRONLY)) | O_RDWR; + } + + fd = opendev(file, flags); + + if ( fd < 0 ) + return NULL; + else + return fdopen(fd, mode); +} diff --git a/com32/lib/fprintf.c b/com32/lib/fprintf.c new file mode 100644 index 0000000..df3823e --- /dev/null +++ b/com32/lib/fprintf.c @@ -0,0 +1,19 @@ +/* + * fprintf.c + */ + +#include +#include + +#define BUFFER_SIZE 16384 + +int fprintf(FILE *file, const char *format, ...) +{ + va_list ap; + int rv; + + va_start(ap, format); + rv = vfprintf(file, format, ap); + va_end(ap); + return rv; +} diff --git a/com32/lib/fputc.c b/com32/lib/fputc.c new file mode 100644 index 0000000..61aff16 --- /dev/null +++ b/com32/lib/fputc.c @@ -0,0 +1,14 @@ +/* + * fputc.c + * + * gcc "printf decompilation" expects this to exist... + */ + +#include + +int fputc(int c, FILE *f) +{ + unsigned char ch = c; + + return _fwrite(&ch, 1, f) == 1 ? ch : EOF; +} diff --git a/com32/lib/fputs.c b/com32/lib/fputs.c new file mode 100644 index 0000000..4b68f96 --- /dev/null +++ b/com32/lib/fputs.c @@ -0,0 +1,15 @@ +/* + * fputs.c + * + * This isn't quite fputs() in the stdio sense, since we don't + * have stdio, but it takes a file descriptor argument instead + * of the FILE *. + */ + +#include +#include + +int fputs(const char *s, FILE *file) +{ + return _fwrite(s, strlen(s), file); +} diff --git a/com32/lib/fread.c b/com32/lib/fread.c new file mode 100644 index 0000000..76fdda8 --- /dev/null +++ b/com32/lib/fread.c @@ -0,0 +1,32 @@ +/* + * fread.c + */ + +#include +#include +#include + +size_t _fread(void *buf, size_t count, FILE *f) +{ + size_t bytes = 0; + ssize_t rv; + char *p = buf; + + while ( count ) { + rv = read(fileno(f), p, count); + if ( rv == -1 ) { + if ( errno == EINTR || errno == EAGAIN ) + continue; + else + break; + } else if ( rv == 0 ) { + break; + } + + p += rv; + bytes += rv; + count -= rv; + } + + return bytes; +} diff --git a/com32/lib/fread2.c b/com32/lib/fread2.c new file mode 100644 index 0000000..9e5ac81 --- /dev/null +++ b/com32/lib/fread2.c @@ -0,0 +1,13 @@ +/* + * fread2.c + * + * The actual fread() function as a non-inline + */ + +#define __NO_FREAD_FWRITE_INLINES +#include + +size_t fread(void *ptr, size_t size, size_t nmemb, FILE *f) +{ + return _fread(ptr, size*nmemb, f)/size; +} diff --git a/com32/lib/free.c b/com32/lib/free.c new file mode 100644 index 0000000..2004407 --- /dev/null +++ b/com32/lib/free.c @@ -0,0 +1,114 @@ +/* + * free.c + * + * Very simple linked-list based malloc()/free(). + */ + +#include +#include "malloc.h" + +static struct free_arena_header * +__free_block(struct free_arena_header *ah) +{ + struct free_arena_header *pah, *nah; + + pah = ah->a.prev; + nah = ah->a.next; + if ( pah->a.type == ARENA_TYPE_FREE && + (char *)pah+pah->a.size == (char *)ah ) { + /* Coalesce into the previous block */ + pah->a.size += ah->a.size; + pah->a.next = nah; + nah->a.prev = pah; + +#ifdef DEBUG_MALLOC + ah->a.type = ARENA_TYPE_DEAD; +#endif + + ah = pah; + pah = ah->a.prev; + } else { + /* Need to add this block to the free chain */ + ah->a.type = ARENA_TYPE_FREE; + + ah->next_free = __malloc_head.next_free; + ah->prev_free = &__malloc_head; + __malloc_head.next_free = ah; + ah->next_free->prev_free = ah; + } + + /* In either of the previous cases, we might be able to merge + with the subsequent block... */ + if ( nah->a.type == ARENA_TYPE_FREE && + (char *)ah+ah->a.size == (char *)nah ) { + ah->a.size += nah->a.size; + + /* Remove the old block from the chains */ + nah->next_free->prev_free = nah->prev_free; + nah->prev_free->next_free = nah->next_free; + ah->a.next = nah->a.next; + nah->a.next->a.prev = ah; + +#ifdef DEBUG_MALLOC + nah->a.type = ARENA_TYPE_DEAD; +#endif + } + + /* Return the block that contains the called block */ + return ah; +} + +/* + * This is used to insert a block which is not previously on the + * free list. Only the a.size field of the arena header is assumed + * to be valid. + */ +void __inject_free_block(struct free_arena_header *ah) +{ + struct free_arena_header *nah; + size_t a_end = (size_t)ah + ah->a.size; + size_t n_end; + + for (nah = __malloc_head.a.next; nah->a.type != ARENA_TYPE_HEAD; + nah = nah->a.next) { + n_end = (size_t)nah + nah->a.size; + + /* Is nah entirely beyond this block? */ + if ((size_t)nah >= a_end) + break; + + /* Is this block entirely beyond nah? */ + if ((size_t)ah >= n_end) + continue; + + /* Otherwise we have some sort of overlap - reject this block */ + return; + } + + /* Now, nah should point to the successor block */ + ah->a.next = nah; + ah->a.prev = nah->a.prev; + nah->a.prev = ah; + ah->a.prev->a.next = ah; + + __free_block(ah); +} + +void free(void *ptr) +{ + struct free_arena_header *ah; + + if ( !ptr ) + return; + + ah = (struct free_arena_header *) + ((struct arena_header *)ptr - 1); + +#ifdef DEBUG_MALLOC + assert( ah->a.type == ARENA_TYPE_USED ); +#endif + + __free_block(ah); + + /* Here we could insert code to return memory to the system. */ +} diff --git a/com32/lib/fwrite.c b/com32/lib/fwrite.c new file mode 100644 index 0000000..fd7dab7 --- /dev/null +++ b/com32/lib/fwrite.c @@ -0,0 +1,32 @@ +/* + * fwrite.c + */ + +#include +#include +#include + +size_t _fwrite(const void *buf, size_t count, FILE *f) +{ + size_t bytes = 0; + ssize_t rv; + const char *p = buf; + + while ( count ) { + rv = write(fileno(f), p, count); + if ( rv == -1 ) { + if ( errno == EINTR || errno == EAGAIN ) + continue; + else + break; + } else if ( rv == 0 ) { + break; + } + + p += rv; + bytes += rv; + count -= rv; + } + + return bytes; +} diff --git a/com32/lib/fwrite2.c b/com32/lib/fwrite2.c new file mode 100644 index 0000000..82ec832 --- /dev/null +++ b/com32/lib/fwrite2.c @@ -0,0 +1,13 @@ +/* + * fwrite2.c + * + * The actual fwrite() function as a non-inline + */ + +#define __NO_FREAD_FWRITE_INLINES +#include + +size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *f) +{ + return _fwrite(ptr, size*nmemb, f)/size; +} diff --git a/com32/lib/getcwd.c b/com32/lib/getcwd.c new file mode 100644 index 0000000..bb0a30d --- /dev/null +++ b/com32/lib/getcwd.c @@ -0,0 +1,30 @@ +/* + * getcwd.c + */ + +#include +#include +#include + +#include +#include +#include +#include + +char *getcwd(char *buf, size_t size) +{ + static com32sys_t reg; + char *pwdstr, *ret; + + reg.eax.w[0] = 0x001f; + __intcall(0x22, ®, ®); + pwdstr = MK_PTR(reg.es, reg.ebx.w[0]); + if ((strlen(pwdstr) < size) && (buf != NULL)) { + strcpy(buf, pwdstr); + ret = buf; + } else { + ret = NULL; + errno = ERANGE; + } + return ret; +} diff --git a/com32/lib/getopt.c b/com32/lib/getopt.c new file mode 100644 index 0000000..49b007a --- /dev/null +++ b/com32/lib/getopt.c @@ -0,0 +1,72 @@ +/* + * getopt.c + * + * Simple POSIX getopt(), no GNU extensions... + */ + +#include +#include +#include + +char *optarg; +int optind = 1; +int opterr, optopt; +static const char *__optptr; + +int getopt(int argc, char * const *argv, const char *optstring) +{ + const char *carg = argv[optind]; + const char *osptr; + int opt; + + /* We don't actually need argc */ + (void)argc; + + /* First, eliminate all non-option cases */ + + if ( !carg || carg[0] != '-' || !carg[1] ) { + return -1; + } + + if ( carg[1] == '-' && !carg[2] ) { + optind++; + return -1; + } + + if ( (uintptr_t)(__optptr-carg) > (uintptr_t)strlen(carg) ) + __optptr = carg+1; /* Someone frobbed optind, change to new opt. */ + + opt = *__optptr++; + + if ( opt != ':' && (osptr = strchr(optstring, opt)) ) { + if ( osptr[1] == ':' ) { + if ( *__optptr ) { + /* Argument-taking option with attached argument */ + optarg = (char *)__optptr; + optind++; + } else { + /* Argument-taking option with non-attached argument */ + if ( argv[optind+1] ) { + optarg = (char *)argv[optind+1]; + optind += 2; + } else { + /* Missing argument */ + return (optstring[0] == ':') ? ':' : '?'; + } + } + return opt; + } else { + /* Non-argument-taking option */ + /* __optptr will remember the exact position to resume at */ + if ( ! *__optptr ) + optind++; + return opt; + } + } else { + /* Unknown option */ + optopt = opt; + if ( ! *__optptr ) + optind++; + return '?'; + } +} diff --git a/com32/lib/init.h b/com32/lib/init.h new file mode 100644 index 0000000..2d98342 --- /dev/null +++ b/com32/lib/init.h @@ -0,0 +1,15 @@ +/* + * init.h + * + * Magic to set up initializers + */ + +#ifndef _INIT_H +#define _INIT_H 1 + +#include + +#define COM32_INIT(x) static const void * const __COM32_INIT \ + __attribute__((section(".init_array"),unused)) = (const void * const)&x + +#endif /* _INIT_H */ diff --git a/com32/lib/jpeg/README b/com32/lib/jpeg/README new file mode 100644 index 0000000..6adeef4 --- /dev/null +++ b/com32/lib/jpeg/README @@ -0,0 +1,32 @@ +/* + * Small jpeg decoder library + * + * Copyright (c) 2006, Luc Saillard + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * - Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + * + */ diff --git a/com32/lib/jpeg/bgr24.c b/com32/lib/jpeg/bgr24.c new file mode 100644 index 0000000..ffdcbdf --- /dev/null +++ b/com32/lib/jpeg/bgr24.c @@ -0,0 +1,362 @@ +/* + * Small jpeg decoder library + * + * Copyright (c) 2006, Luc Saillard + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * - Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + * + */ + +#include +#include +#include +#include + +#include "tinyjpeg.h" +#include "tinyjpeg-internal.h" + +/******************************************************************************* + * + * Colorspace conversion routine + * + * + * Note: + * YCbCr is defined per CCIR 601-1, except that Cb and Cr are + * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. + * The conversion equations to be implemented are therefore + * R = Y + 1.40200 * Cr + * G = Y - 0.34414 * Cb - 0.71414 * Cr + * B = Y + 1.77200 * Cb + * + ******************************************************************************/ +static unsigned char clamp(int i) +{ + if (i<0) + return 0; + else if (i>255) + return 255; + else + return i; +} + +/** + * YCrCb -> BGR24 (1x1) + * .---. + * | 1 | + * `---' + */ +static void YCrCB_to_BGR24_1x1(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p; + int i,j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS-1)) +#define FIX(x) ((int)((x) * (1UL<plane[0]; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = priv->bytes_per_row[0] - 8*3; + for (i=0; i<8; i++) { + + for (j=0;j<8;j++) { + + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b; + + y = (*Y++) << SCALEBITS; + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = - FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + + } + + p += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX + +} + + +/* + * YCrCb -> BGR24 (2x1) + * .-------. + * | 1 | 2 | + * `-------' + */ +static void YCrCB_to_BGR24_2x1(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p; + int i,j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS-1)) +#define FIX(x) ((int)((x) * (1UL<plane[0]; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = priv->bytes_per_row[0] - 16*3; + for (i=0; i<8; i++) { + + for (j=0; j<8; j++) { + + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b; + + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = - FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + y = (*Y++) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + + y = (*Y++) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + + } + + p += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX + +} + +/* + * YCrCb -> BGR24 (1x2) + * .---. + * | 1 | + * |---| + * | 2 | + * `---' + */ +static void YCrCB_to_BGR24_1x2(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p, *p2; + int i,j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS-1)) +#define FIX(x) ((int)((x) * (1UL<plane[0]; + p2 = priv->plane[0] + priv->bytes_per_row[0]; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = 2*priv->bytes_per_row[0] - 8*3; + for (i=0; i<8; i++) { + + for (j=0; j<8; j++) { + + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b; + + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = - FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + y = (*Y++) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + + y = (Y[8-1]) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p2++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p2++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p2++ = clamp(r); + + } + Y += 8; + p += offset_to_next_row; + p2 += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX + +} + + +/* + * YCrCb -> BGR24 (2x2) + * .-------. + * | 1 | 2 | + * |---+---| + * | 3 | 4 | + * `-------' + */ +static void YCrCB_to_BGR24_2x2(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p, *p2; + int i,j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS-1)) +#define FIX(x) ((int)((x) * (1UL<plane[0]; + p2 = priv->plane[0] + priv->bytes_per_row[0]; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = 2*priv->bytes_per_row[0] - 16*3; + for (i=0; i<8; i++) { + + for (j=0;j<8;j++) { + + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b; + + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = - FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + y = (*Y++) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + + y = (*Y++) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + + y = (Y[16-2]) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p2++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p2++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p2++ = clamp(r); + + y = (Y[16-1]) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p2++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p2++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p2++ = clamp(r); + } + Y += 16; + p += offset_to_next_row; + p2 += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX + +} + +static int initialize_bgr24(struct jdec_private *priv, + unsigned int *bytes_per_blocklines, + unsigned int *bytes_per_mcu) +{ + if (priv->components[0] == NULL) + priv->components[0] = (uint8_t *)malloc(priv->width * priv->height * 3); + if (!priv->bytes_per_row[0]) + priv->bytes_per_row[0] = priv->width * 3; + + bytes_per_blocklines[0] = priv->bytes_per_row[0]; + bytes_per_mcu[0] = 3*8; + + return !priv->components[0]; +} + +static const struct tinyjpeg_colorspace format_bgr24 = + { + { + YCrCB_to_BGR24_1x1, + YCrCB_to_BGR24_1x2, + YCrCB_to_BGR24_2x1, + YCrCB_to_BGR24_2x2, + }, + tinyjpeg_decode_mcu_3comp_table, + initialize_bgr24 + }; + +const tinyjpeg_colorspace_t TINYJPEG_FMT_BGR24 = &format_bgr24; diff --git a/com32/lib/jpeg/bgra32.c b/com32/lib/jpeg/bgra32.c new file mode 100644 index 0000000..7e2c72b --- /dev/null +++ b/com32/lib/jpeg/bgra32.c @@ -0,0 +1,380 @@ +/* + * Small jpeg decoder library + * + * Copyright (c) 2006, Luc Saillard + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * - Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + * + */ + +#include +#include +#include +#include + +#include "tinyjpeg.h" +#include "tinyjpeg-internal.h" + +/******************************************************************************* + * + * Colorspace conversion routine + * + * + * Note: + * YCbCr is defined per CCIR 601-1, except that Cb and Cr are + * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. + * The conversion equations to be implemented are therefore + * R = Y + 1.40200 * Cr + * G = Y - 0.34414 * Cb - 0.71414 * Cr + * B = Y + 1.77200 * Cb + * + ******************************************************************************/ +static unsigned char clamp(int i) +{ + if (i<0) + return 0; + else if (i>255) + return 255; + else + return i; +} + +/** + * YCrCb -> BGRA32 (1x1) + * .---. + * | 1 | + * `---' + */ +static void YCrCB_to_BGRA32_1x1(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p; + int i,j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS-1)) +#define FIX(x) ((int)((x) * (1UL<plane[0]; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = priv->bytes_per_row[0] - 8*4; + for (i=0; i<8; i++) { + + for (j=0;j<8;j++) { + + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b, a; + + y = (*Y++) << SCALEBITS; + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = - FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + a = 255; + *p++ = a; + + } + + p += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX + +} + + +/* + * YCrCb -> BGRA32 (2x1) + * .-------. + * | 1 | 2 | + * `-------' + */ +static void YCrCB_to_BGRA32_2x1(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p; + int i,j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS-1)) +#define FIX(x) ((int)((x) * (1UL<plane[0]; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = priv->bytes_per_row[0] - 16*4; + for (i=0; i<8; i++) { + + for (j=0; j<8; j++) { + + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b, a; + + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = - FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + y = (*Y++) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + a = 255; + *p++ = a; + + y = (*Y++) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + a = 255; + *p++ = a; + + } + + p += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX + +} + +/* + * YCrCb -> BGRA32 (1x2) + * .---. + * | 1 | + * |---| + * | 2 | + * `---' + */ +static void YCrCB_to_BGRA32_1x2(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p, *p2; + int i,j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS-1)) +#define FIX(x) ((int)((x) * (1UL<plane[0]; + p2 = priv->plane[0] + priv->bytes_per_row[0]; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = 2*priv->bytes_per_row[0] - 8*4; + for (i=0; i<8; i++) { + + for (j=0; j<8; j++) { + + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b, a; + + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = - FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + y = (*Y++) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + a = 255; + *p++ = a; + + y = (Y[8-1]) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p2++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p2++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p2++ = clamp(r); + a = 255; + *p2++ = a; + + } + Y += 8; + p += offset_to_next_row; + p2 += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX + +} + + +/* + * YCrCb -> BGRA32 (2x2) + * .-------. + * | 1 | 2 | + * |---+---| + * | 3 | 4 | + * `-------' + */ +static void YCrCB_to_BGRA32_2x2(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p, *p2; + int i,j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS-1)) +#define FIX(x) ((int)((x) * (1UL<plane[0]; + p2 = priv->plane[0] + priv->bytes_per_row[0]; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = 2*priv->bytes_per_row[0] - 16*4; + for (i=0; i<8; i++) { + + for (j=0;j<8;j++) { + + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b, a; + + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = - FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + y = (*Y++) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + a = 255; + *p++ = a; + + y = (*Y++) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + a = 255; + *p++ = a; + + y = (Y[16-2]) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p2++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p2++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p2++ = clamp(r); + a = 255; + *p2++ = a; + + y = (Y[16-1]) << SCALEBITS; + b = (y + add_b) >> SCALEBITS; + *p2++ = clamp(b); + g = (y + add_g) >> SCALEBITS; + *p2++ = clamp(g); + r = (y + add_r) >> SCALEBITS; + *p2++ = clamp(r); + a = 255; + *p2++ = a; + } + Y += 16; + p += offset_to_next_row; + p2 += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX + +} + +static int initialize_bgra32(struct jdec_private *priv, + unsigned int *bytes_per_blocklines, + unsigned int *bytes_per_mcu) +{ + if (priv->components[0] == NULL) + priv->components[0] = (uint8_t *)malloc(priv->width * priv->height * 4); + if (!priv->bytes_per_row[0]) + priv->bytes_per_row[0] = priv->width * 4; + + bytes_per_blocklines[0] = priv->bytes_per_row[0]; + bytes_per_mcu[0] = 4*8; + + return !priv->components[0]; +} + +static const struct tinyjpeg_colorspace format_bgra32 = + { + { + YCrCB_to_BGRA32_1x1, + YCrCB_to_BGRA32_1x2, + YCrCB_to_BGRA32_2x1, + YCrCB_to_BGRA32_2x2, + }, + tinyjpeg_decode_mcu_3comp_table, + initialize_bgra32 + }; + +const tinyjpeg_colorspace_t TINYJPEG_FMT_BGRA32 = &format_bgra32; diff --git a/com32/lib/jpeg/decode1.c b/com32/lib/jpeg/decode1.c new file mode 100644 index 0000000..554f7f1 --- /dev/null +++ b/com32/lib/jpeg/decode1.c @@ -0,0 +1,138 @@ +/* + * Small jpeg decoder library + * + * Copyright (c) 2006, Luc Saillard + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * - Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + * + */ + +#include +#include +#include +#include + +#include "tinyjpeg.h" +#include "tinyjpeg-internal.h" + +/* + * Decode a 1x1 directly in 1 color + */ +static void decode_MCU_1x1_1plane(struct jdec_private *priv) +{ + // Y + tinyjpeg_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y, 8); + + // Cb + tinyjpeg_process_Huffman_data_unit(priv, cCb); + IDCT(&priv->component_infos[cCb], priv->Cb, 8); + + // Cr + tinyjpeg_process_Huffman_data_unit(priv, cCr); + IDCT(&priv->component_infos[cCr], priv->Cr, 8); +} + + +/* + * Decode a 2x1 + * .-------. + * | 1 | 2 | + * `-------' + */ +static void decode_MCU_2x1_1plane(struct jdec_private *priv) +{ + // Y + tinyjpeg_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y, 16); + tinyjpeg_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y+8, 16); + + // Cb + tinyjpeg_process_Huffman_data_unit(priv, cCb); + + // Cr + tinyjpeg_process_Huffman_data_unit(priv, cCr); +} + + +/* + * Decode a 2x2 directly in GREY format (8bits) + * .-------. + * | 1 | 2 | + * |---+---| + * | 3 | 4 | + * `-------' + */ +static void decode_MCU_2x2_1plane(struct jdec_private *priv) +{ + // Y + tinyjpeg_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y, 16); + tinyjpeg_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y+8, 16); + tinyjpeg_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y+64*2, 16); + tinyjpeg_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y+64*2+8, 16); + + // Cb + tinyjpeg_process_Huffman_data_unit(priv, cCb); + + // Cr + tinyjpeg_process_Huffman_data_unit(priv, cCr); +} + +/* + * Decode a 1x2 mcu + * .---. + * | 1 | + * |---| + * | 2 | + * `---' + */ +static void decode_MCU_1x2_1plane(struct jdec_private *priv) +{ + // Y + tinyjpeg_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y, 8); + tinyjpeg_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y+64, 8); + + // Cb + tinyjpeg_process_Huffman_data_unit(priv, cCb); + + // Cr + tinyjpeg_process_Huffman_data_unit(priv, cCr); +} + +const decode_MCU_fct tinyjpeg_decode_mcu_1comp_table[4] = { + decode_MCU_1x1_1plane, + decode_MCU_1x2_1plane, + decode_MCU_2x1_1plane, + decode_MCU_2x2_1plane, +}; diff --git a/com32/lib/jpeg/decode3.c b/com32/lib/jpeg/decode3.c new file mode 100644 index 0000000..2f79ec9 --- /dev/null +++ b/com32/lib/jpeg/decode3.c @@ -0,0 +1,142 @@ +/* + * Small jpeg decoder library + * + * Copyright (c) 2006, Luc Saillard + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * - Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + * + */ + +#include +#include +#include +#include + +#include "tinyjpeg.h" +#include "tinyjpeg-internal.h" + +/* + * Decode all the 3 components for 1x1 + */ +static void decode_MCU_1x1_3planes(struct jdec_private *priv) +{ + // Y + tinyjpeg_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y, 8); + + // Cb + tinyjpeg_process_Huffman_data_unit(priv, cCb); + IDCT(&priv->component_infos[cCb], priv->Cb, 8); + + // Cr + tinyjpeg_process_Huffman_data_unit(priv, cCr); + IDCT(&priv->component_infos[cCr], priv->Cr, 8); +} + +/* + * Decode a 2x1 + * .-------. + * | 1 | 2 | + * `-------' + */ +static void decode_MCU_2x1_3planes(struct jdec_private *priv) +{ + // Y + tinyjpeg_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y, 16); + tinyjpeg_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y+8, 16); + + // Cb + tinyjpeg_process_Huffman_data_unit(priv, cCb); + IDCT(&priv->component_infos[cCb], priv->Cb, 8); + + // Cr + tinyjpeg_process_Huffman_data_unit(priv, cCr); + IDCT(&priv->component_infos[cCr], priv->Cr, 8); +} + +/* + * Decode a 2x2 + * .-------. + * | 1 | 2 | + * |---+---| + * | 3 | 4 | + * `-------' + */ +static void decode_MCU_2x2_3planes(struct jdec_private *priv) +{ + // Y + tinyjpeg_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y, 16); + tinyjpeg_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y+8, 16); + tinyjpeg_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y+64*2, 16); + tinyjpeg_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y+64*2+8, 16); + + // Cb + tinyjpeg_process_Huffman_data_unit(priv, cCb); + IDCT(&priv->component_infos[cCb], priv->Cb, 8); + + // Cr + tinyjpeg_process_Huffman_data_unit(priv, cCr); + IDCT(&priv->component_infos[cCr], priv->Cr, 8); +} + +/* + * Decode a 1x2 mcu + * .---. + * | 1 | + * |---| + * | 2 | + * `---' + */ +static void decode_MCU_1x2_3planes(struct jdec_private *priv) +{ + // Y + tinyjpeg_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y, 8); + tinyjpeg_process_Huffman_data_unit(priv, cY); + IDCT(&priv->component_infos[cY], priv->Y+64, 8); + + // Cb + tinyjpeg_process_Huffman_data_unit(priv, cCb); + IDCT(&priv->component_infos[cCb], priv->Cb, 8); + + // Cr + tinyjpeg_process_Huffman_data_unit(priv, cCr); + IDCT(&priv->component_infos[cCr], priv->Cr, 8); +} + +const decode_MCU_fct tinyjpeg_decode_mcu_3comp_table[4] = { + decode_MCU_1x1_3planes, + decode_MCU_1x2_3planes, + decode_MCU_2x1_3planes, + decode_MCU_2x2_3planes, +}; diff --git a/com32/lib/jpeg/grey.c b/com32/lib/jpeg/grey.c new file mode 100644 index 0000000..0034ce2 --- /dev/null +++ b/com32/lib/jpeg/grey.c @@ -0,0 +1,170 @@ +/* + * Small jpeg decoder library + * + * Copyright (c) 2006, Luc Saillard + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * - Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + * + */ + +#include +#include +#include +#include + +#include "tinyjpeg.h" +#include "tinyjpeg-internal.h" + +/** + * YCrCb -> Grey (1x1) + * .---. + * | 1 | + * `---' + */ +static void YCrCB_to_Grey_1x1(struct jdec_private *priv) +{ + const unsigned char *y; + unsigned char *p; + unsigned int i; + int offset_to_next_row; + + p = priv->plane[0]; + y = priv->Y; + offset_to_next_row = priv->bytes_per_row[0]; + + for (i=0; i<8; i++) { + memcpy(p, y, 8); + y+=8; + p += offset_to_next_row; + } +} + +/** + * YCrCb -> Grey (2x1) + * .-------. + * | 1 | 2 | + * `-------' + */ +static void YCrCB_to_Grey_2x1(struct jdec_private *priv) +{ + const unsigned char *y; + unsigned char *p; + unsigned int i; + int offset_to_next_row; + + p = priv->plane[0]; + y = priv->Y; + offset_to_next_row = priv->bytes_per_row[0]; + + for (i=0; i<8; i++) { + memcpy(p, y, 16); + y += 16; + p += offset_to_next_row; + } +} + + +/** + * YCrCb -> Grey (1x2) + * .---. + * | 1 | + * |---| + * | 2 | + * `---' + */ +static void YCrCB_to_Grey_1x2(struct jdec_private *priv) +{ + const unsigned char *y; + unsigned char *p; + unsigned int i; + int offset_to_next_row; + + p = priv->plane[0]; + y = priv->Y; + offset_to_next_row = priv->bytes_per_row[0]; + + for (i=0; i<16; i++) { + memcpy(p, y, 8); + y += 8; + p += offset_to_next_row; + } +} + +/** + * YCrCb -> Grey (2x2) + * .-------. + * | 1 | 2 | + * |---+---| + * | 3 | 4 | + * `-------' + */ +static void YCrCB_to_Grey_2x2(struct jdec_private *priv) +{ + const unsigned char *y; + unsigned char *p; + unsigned int i; + int offset_to_next_row; + + p = priv->plane[0]; + y = priv->Y; + offset_to_next_row = priv->bytes_per_row[0]; + + for (i=0; i<16; i++) { + memcpy(p, y, 16); + y += 16; + p += offset_to_next_row; + } +} + +static int initialize_grey(struct jdec_private *priv, + unsigned int *bytes_per_blocklines, + unsigned int *bytes_per_mcu) +{ + if (priv->components[0] == NULL) + priv->components[0] = (uint8_t *)malloc(priv->width * priv->height * 3); + if (!priv->bytes_per_row[0]) + priv->bytes_per_row[0] = priv->width * 3; + + bytes_per_blocklines[0] = priv->bytes_per_row[0]; + bytes_per_mcu[0] = 3*8; + + return !priv->components[0]; +} + +static const struct tinyjpeg_colorspace format_grey = + { + { + YCrCB_to_Grey_1x1, + YCrCB_to_Grey_1x2, + YCrCB_to_Grey_2x1, + YCrCB_to_Grey_2x2, + }, + tinyjpeg_decode_mcu_1comp_table, + initialize_grey + }; + +const tinyjpeg_colorspace_t TINYJPEG_FMT_GREY = &format_grey; diff --git a/com32/lib/jpeg/jidctflt.c b/com32/lib/jpeg/jidctflt.c new file mode 100644 index 0000000..e5e66ed --- /dev/null +++ b/com32/lib/jpeg/jidctflt.c @@ -0,0 +1,285 @@ +/* + * jidctflt.c + * + * Copyright (C) 1994-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * + * The authors make NO WARRANTY or representation, either express or implied, + * with respect to this software, its quality, accuracy, merchantability, or + * fitness for a particular purpose. This software is provided "AS IS", and you, + * its user, assume the entire risk as to its quality and accuracy. + * + * This software is copyright (C) 1991-1998, Thomas G. Lane. + * All Rights Reserved except as specified below. + * + * Permission is hereby granted to use, copy, modify, and distribute this + * software (or portions thereof) for any purpose, without fee, subject to these + * conditions: + * (1) If any part of the source code for this software is distributed, then this + * README file must be included, with this copyright and no-warranty notice + * unaltered; and any additions, deletions, or changes to the original files + * must be clearly indicated in accompanying documentation. + * (2) If only executable code is distributed, then the accompanying + * documentation must state that "this software is based in part on the work of + * the Independent JPEG Group". + * (3) Permission for use of this software is granted only if the user accepts + * full responsibility for any undesirable consequences; the authors accept + * NO LIABILITY for damages of any kind. + * + * These conditions apply to any software derived from or based on the IJG code, + * not just to the unmodified library. If you use our work, you ought to + * acknowledge us. + * + * Permission is NOT granted for the use of any IJG author's name or company name + * in advertising or publicity relating to this software or products derived from + * it. This software may be referred to only as "the Independent JPEG Group's + * software". + * + * We specifically permit and encourage the use of this software as the basis of + * commercial products, provided that all warranty or liability claims are + * assumed by the product vendor. + * + * + * This file contains a floating-point implementation of the + * inverse DCT (Discrete Cosine Transform). In the IJG code, this routine + * must also perform dequantization of the input coefficients. + * + * This implementation should be more accurate than either of the integer + * IDCT implementations. However, it may not give the same results on all + * machines because of differences in roundoff behavior. Speed will depend + * on the hardware's floating point capacity. + * + * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT + * on each row (or vice versa, but it's more convenient to emit a row at + * a time). Direct algorithms are also available, but they are much more + * complex and seem not to be any faster when reduced to code. + * + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + * JPEG textbook (see REFERENCES section in file README). The following code + * is based directly on figure 4-8 in P&M. + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + * possible to arrange the computation so that many of the multiplies are + * simple scalings of the final outputs. These multiplies can then be + * folded into the multiplications or divisions by the JPEG quantization + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + * to be done in the DCT itself. + * The primary disadvantage of this method is that with a fixed-point + * implementation, accuracy is lost due to imprecise representation of the + * scaled quantization values. However, that problem does not arise if + * we use floating point arithmetic. + */ + +#include +#include "tinyjpeg-internal.h" + +#define FAST_FLOAT float +#define DCTSIZE 8 +#define DCTSIZE2 (DCTSIZE*DCTSIZE) + +#define DEQUANTIZE(coef,quantval) (((FAST_FLOAT) (coef)) * (quantval)) + +#if defined(__GNUC__) && defined(__i686__) || defined(__x86_64__) + +static inline unsigned char descale_and_clamp(int x, int shift) +{ + __asm__ ( + "add %3,%1\n" + "\tsar %2,%1\n" + "\tsub $-128,%1\n" + "\tcmovl %5,%1\n" /* Use the sub to compare to 0 */ + "\tcmpl %4,%1\n" + "\tcmovg %4,%1\n" + : "=r"(x) + : "0"(x), "i"(shift), "i"(1UL<<(shift-1)), "r" (0xff), "r" (0) + ); + return x; +} + +#else +static inline unsigned char descale_and_clamp(int x, int shift) +{ + x += (1UL<<(shift-1)); + if (x<0) + x = (x >> shift) | ((~(0UL)) << (32-(shift))); + else + x >>= shift; + x += 128; + if (x>255) + return 255; + else if (x<0) + return 0; + else + return x; +} +#endif + +/* + * Perform dequantization and inverse DCT on one block of coefficients. + */ + +void +jpeg_idct_float (struct component *compptr, uint8_t *output_buf, int stride) +{ + FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + FAST_FLOAT tmp10, tmp11, tmp12, tmp13; + FAST_FLOAT z5, z10, z11, z12, z13; + int16_t *inptr; + FAST_FLOAT *quantptr; + FAST_FLOAT *wsptr; + uint8_t *outptr; + int ctr; + FAST_FLOAT workspace[DCTSIZE2]; /* buffers data between passes */ + + /* Pass 1: process columns from input, store into work array. */ + + inptr = compptr->DCT; + quantptr = compptr->Q_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; ctr--) { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any column in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * column DCT calculations can be simplified this way. + */ + + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && + inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 && + inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 && + inptr[DCTSIZE*7] == 0) { + /* AC terms all zero */ + FAST_FLOAT dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + wsptr[DCTSIZE*2] = dcval; + wsptr[DCTSIZE*3] = dcval; + wsptr[DCTSIZE*4] = dcval; + wsptr[DCTSIZE*5] = dcval; + wsptr[DCTSIZE*6] = dcval; + wsptr[DCTSIZE*7] = dcval; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + continue; + } + + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + tmp3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + tmp10 = tmp0 + tmp2; /* phase 3 */ + tmp11 = tmp0 - tmp2; + + tmp13 = tmp1 + tmp3; /* phases 5-3 */ + tmp12 = (tmp1 - tmp3) * ((FAST_FLOAT) 1.414213562) - tmp13; /* 2*c4 */ + + tmp0 = tmp10 + tmp13; /* phase 2 */ + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + tmp4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + tmp5 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + tmp6 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp7 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + z13 = tmp6 + tmp5; /* phase 6 */ + z10 = tmp6 - tmp5; + z11 = tmp4 + tmp7; + z12 = tmp4 - tmp7; + + tmp7 = z11 + z13; /* phase 5 */ + tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); /* 2*c4 */ + + z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ + tmp10 = ((FAST_FLOAT) 1.082392200) * z12 - z5; /* 2*(c2-c6) */ + tmp12 = ((FAST_FLOAT) -2.613125930) * z10 + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; /* phase 2 */ + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + wsptr[DCTSIZE*0] = tmp0 + tmp7; + wsptr[DCTSIZE*7] = tmp0 - tmp7; + wsptr[DCTSIZE*1] = tmp1 + tmp6; + wsptr[DCTSIZE*6] = tmp1 - tmp6; + wsptr[DCTSIZE*2] = tmp2 + tmp5; + wsptr[DCTSIZE*5] = tmp2 - tmp5; + wsptr[DCTSIZE*4] = tmp3 + tmp4; + wsptr[DCTSIZE*3] = tmp3 - tmp4; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + } + + /* Pass 2: process rows from work array, store into output array. */ + /* Note that we must descale the results by a factor of 8 == 2**3. */ + + wsptr = workspace; + outptr = output_buf; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + /* Rows of zeroes can be exploited in the same way as we did with columns. + * However, the column calculation has created many nonzero AC terms, so + * the simplification applies less often (typically 5% to 10% of the time). + * And testing floats for zero is relatively expensive, so we don't bother. + */ + + /* Even part */ + + tmp10 = wsptr[0] + wsptr[4]; + tmp11 = wsptr[0] - wsptr[4]; + + tmp13 = wsptr[2] + wsptr[6]; + tmp12 = (wsptr[2] - wsptr[6]) * ((FAST_FLOAT) 1.414213562) - tmp13; + + tmp0 = tmp10 + tmp13; + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + z13 = wsptr[5] + wsptr[3]; + z10 = wsptr[5] - wsptr[3]; + z11 = wsptr[1] + wsptr[7]; + z12 = wsptr[1] - wsptr[7]; + + tmp7 = z11 + z13; + tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); + + z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ + tmp10 = ((FAST_FLOAT) 1.082392200) * z12 - z5; /* 2*(c2-c6) */ + tmp12 = ((FAST_FLOAT) -2.613125930) * z10 + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + /* Final output stage: scale down by a factor of 8 and range-limit */ + + outptr[0] = descale_and_clamp(tmp0 + tmp7, 3); + outptr[7] = descale_and_clamp(tmp0 - tmp7, 3); + outptr[1] = descale_and_clamp(tmp1 + tmp6, 3); + outptr[6] = descale_and_clamp(tmp1 - tmp6, 3); + outptr[2] = descale_and_clamp(tmp2 + tmp5, 3); + outptr[5] = descale_and_clamp(tmp2 - tmp5, 3); + outptr[4] = descale_and_clamp(tmp3 + tmp4, 3); + outptr[3] = descale_and_clamp(tmp3 - tmp4, 3); + + + wsptr += DCTSIZE; /* advance pointer to next row */ + outptr += stride; + } +} diff --git a/com32/lib/jpeg/rgb24.c b/com32/lib/jpeg/rgb24.c new file mode 100644 index 0000000..e37cc77 --- /dev/null +++ b/com32/lib/jpeg/rgb24.c @@ -0,0 +1,361 @@ +/* + * Small jpeg decoder library + * + * Copyright (c) 2006, Luc Saillard + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * - Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + * + */ + +#include +#include +#include +#include + +#include "tinyjpeg.h" +#include "tinyjpeg-internal.h" + +/******************************************************************************* + * + * Colorspace conversion routine + * + * + * Note: + * YCbCr is defined per CCIR 601-1, except that Cb and Cr are + * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. + * The conversion equations to be implemented are therefore + * R = Y + 1.40200 * Cr + * G = Y - 0.34414 * Cb - 0.71414 * Cr + * B = Y + 1.77200 * Cb + * + ******************************************************************************/ +static unsigned char clamp(int i) +{ + if (i<0) + return 0; + else if (i>255) + return 255; + else + return i; +} + +/** + * YCrCb -> RGB24 (1x1) + * .---. + * | 1 | + * `---' + */ +static void YCrCB_to_RGB24_1x1(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p; + int i,j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS-1)) +#define FIX(x) ((int)((x) * (1UL<plane[0]; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = priv->bytes_per_row[0] - 8*3; + for (i=0; i<8; i++) { + + for (j=0;j<8;j++) { + + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b; + + y = (*Y++) << SCALEBITS; + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = - FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + + } + + p += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX + +} + +/** + * YCrCb -> RGB24 (2x1) + * .-------. + * | 1 | 2 | + * `-------' + */ +static void YCrCB_to_RGB24_2x1(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p; + int i,j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS-1)) +#define FIX(x) ((int)((x) * (1UL<plane[0]; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = priv->bytes_per_row[0] - 16*3; + for (i=0; i<8; i++) { + + for (j=0; j<8; j++) { + + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b; + + y = (*Y++) << SCALEBITS; + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = - FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + + y = (*Y++) << SCALEBITS; + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + + } + + p += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX + +} + + +/** + * YCrCb -> RGB24 (1x2) + * .---. + * | 1 | + * |---| + * | 2 | + * `---' + */ +static void YCrCB_to_RGB24_1x2(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p, *p2; + int i,j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS-1)) +#define FIX(x) ((int)((x) * (1UL<plane[0]; + p2 = priv->plane[0] + priv->bytes_per_row[0]; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = 2*priv->bytes_per_row[0] - 8*3; + for (i=0; i<8; i++) { + + for (j=0; j<8; j++) { + + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b; + + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = - FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + y = (*Y++) << SCALEBITS; + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + + y = (Y[8-1]) << SCALEBITS; + r = (y + add_r) >> SCALEBITS; + *p2++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p2++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p2++ = clamp(b); + + } + Y += 8; + p += offset_to_next_row; + p2 += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX + +} + +/** + * YCrCb -> RGB24 (2x2) + * .-------. + * | 1 | 2 | + * |---+---| + * | 3 | 4 | + * `-------' + */ +static void YCrCB_to_RGB24_2x2(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p, *p2; + int i,j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS-1)) +#define FIX(x) ((int)((x) * (1UL<plane[0]; + p2 = priv->plane[0] + priv->bytes_per_row[0]; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = 2*priv->bytes_per_row[0] - 16*3; + for (i=0; i<8; i++) { + + for (j=0;j<8;j++) { + + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b; + + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = - FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + y = (*Y++) << SCALEBITS; + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + + y = (*Y++) << SCALEBITS; + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + + y = (Y[16-2]) << SCALEBITS; + r = (y + add_r) >> SCALEBITS; + *p2++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p2++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p2++ = clamp(b); + + y = (Y[16-1]) << SCALEBITS; + r = (y + add_r) >> SCALEBITS; + *p2++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p2++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p2++ = clamp(b); + } + Y += 16; + p += offset_to_next_row; + p2 += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX + +} + +static int initialize_rgb24(struct jdec_private *priv, + unsigned int *bytes_per_blocklines, + unsigned int *bytes_per_mcu) +{ + if (priv->components[0] == NULL) + priv->components[0] = (uint8_t *)malloc(priv->width * priv->height * 3); + if (!priv->bytes_per_row[0]) + priv->bytes_per_row[0] = priv->width * 3; + + bytes_per_blocklines[0] = priv->bytes_per_row[0]; + bytes_per_mcu[0] = 3*8; + + return !priv->components[0]; +} + +static const struct tinyjpeg_colorspace format_rgb24 = + { + { + YCrCB_to_RGB24_1x1, + YCrCB_to_RGB24_1x2, + YCrCB_to_RGB24_2x1, + YCrCB_to_RGB24_2x2, + }, + tinyjpeg_decode_mcu_3comp_table, + initialize_rgb24 + }; + +const tinyjpeg_colorspace_t TINYJPEG_FMT_RGB24 = &format_rgb24; diff --git a/com32/lib/jpeg/rgba32.c b/com32/lib/jpeg/rgba32.c new file mode 100644 index 0000000..5fcbe99 --- /dev/null +++ b/com32/lib/jpeg/rgba32.c @@ -0,0 +1,377 @@ +/* + * Small jpeg decoder library + * + * Copyright (c) 2006, Luc Saillard + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * - Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + * + */ + +#include +#include +#include +#include + +#include "tinyjpeg.h" +#include "tinyjpeg-internal.h" + +/******************************************************************************* + * + * Colorspace conversion routine + * + * + * Note: + * YCbCr is defined per CCIR 601-1, except that Cb and Cr are + * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. + * The conversion equations to be implemented are therefore + * R = Y + 1.40200 * Cr + * G = Y - 0.34414 * Cb - 0.71414 * Cr + * B = Y + 1.77200 * Cb + * + ******************************************************************************/ +static unsigned char clamp(int i) +{ + if (i<0) + return 0; + else if (i>255) + return 255; + else + return i; +} + +/** + * YCrCb -> RGBA32 (1x1) + * .---. + * | 1 | + * `---' + */ +static void YCrCB_to_RGBA32_1x1(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p; + int i,j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS-1)) +#define FIX(x) ((int)((x) * (1UL<plane[0]; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = priv->bytes_per_row[0] - 8*4; + for (i=0; i<8; i++) { + + for (j=0;j<8;j++) { + + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b, a; + + y = (*Y++) << SCALEBITS; + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = - FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + a = 255; + *p++ = a; + } + + p += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX + +} + +/** + * YCrCb -> RGBA32 (2x1) + * .-------. + * | 1 | 2 | + * `-------' + */ +static void YCrCB_to_RGBA32_2x1(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p; + int i,j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS-1)) +#define FIX(x) ((int)((x) * (1UL<plane[0]; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = priv->bytes_per_row[0] - 16*4; + for (i=0; i<8; i++) { + + for (j=0; j<8; j++) { + + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b, a; + + y = (*Y++) << SCALEBITS; + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = - FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + a = 255; + *p++ = a; + + y = (*Y++) << SCALEBITS; + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + a = 255; + *p++ = a; + } + + p += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX + +} + + +/** + * YCrCb -> RGBA32 (1x2) + * .---. + * | 1 | + * |---| + * | 2 | + * `---' + */ +static void YCrCB_to_RGBA32_1x2(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p, *p2; + int i,j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS-1)) +#define FIX(x) ((int)((x) * (1UL<plane[0]; + p2 = priv->plane[0] + priv->bytes_per_row[0]; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = 2*priv->bytes_per_row[0] - 8*4; + for (i=0; i<8; i++) { + + for (j=0; j<8; j++) { + + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b, a; + + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = - FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + y = (*Y++) << SCALEBITS; + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + a = 255; + *p++ = a; + + y = (Y[8-1]) << SCALEBITS; + r = (y + add_r) >> SCALEBITS; + *p2++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p2++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p2++ = clamp(b); + a = 255; + *p2++ = a; + + } + Y += 8; + p += offset_to_next_row; + p2 += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX + +} + +/** + * YCrCb -> RGBA32 (2x2) + * .-------. + * | 1 | 2 | + * |---+---| + * | 3 | 4 | + * `-------' + */ +static void YCrCB_to_RGBA32_2x2(struct jdec_private *priv) +{ + const unsigned char *Y, *Cb, *Cr; + unsigned char *p, *p2; + int i,j; + int offset_to_next_row; + +#define SCALEBITS 10 +#define ONE_HALF (1UL << (SCALEBITS-1)) +#define FIX(x) ((int)((x) * (1UL<plane[0]; + p2 = priv->plane[0] + priv->bytes_per_row[0]; + Y = priv->Y; + Cb = priv->Cb; + Cr = priv->Cr; + offset_to_next_row = 2*priv->bytes_per_row[0] - 16*4; + for (i=0; i<8; i++) { + + for (j=0;j<8;j++) { + + int y, cb, cr; + int add_r, add_g, add_b; + int r, g , b, a; + + cb = *Cb++ - 128; + cr = *Cr++ - 128; + add_r = FIX(1.40200) * cr + ONE_HALF; + add_g = - FIX(0.34414) * cb - FIX(0.71414) * cr + ONE_HALF; + add_b = FIX(1.77200) * cb + ONE_HALF; + + y = (*Y++) << SCALEBITS; + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + a = 255; + *p++ = a; + + y = (*Y++) << SCALEBITS; + r = (y + add_r) >> SCALEBITS; + *p++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p++ = clamp(b); + a = 255; + *p++ = a; + + y = (Y[16-2]) << SCALEBITS; + r = (y + add_r) >> SCALEBITS; + *p2++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p2++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p2++ = clamp(b); + a = 255; + *p2++ = a; + + y = (Y[16-1]) << SCALEBITS; + r = (y + add_r) >> SCALEBITS; + *p2++ = clamp(r); + g = (y + add_g) >> SCALEBITS; + *p2++ = clamp(g); + b = (y + add_b) >> SCALEBITS; + *p2++ = clamp(b); + a = 255; + *p2++ = a; + } + Y += 16; + p += offset_to_next_row; + p2 += offset_to_next_row; + } + +#undef SCALEBITS +#undef ONE_HALF +#undef FIX + +} + +static int initialize_rgba32(struct jdec_private *priv, + unsigned int *bytes_per_blocklines, + unsigned int *bytes_per_mcu) +{ + if (priv->components[0] == NULL) + priv->components[0] = (uint8_t *)malloc(priv->width * priv->height * 4); + if (!priv->bytes_per_row[0]) + priv->bytes_per_row[0] = priv->width * 4; + + bytes_per_blocklines[0] = priv->bytes_per_row[0]; + bytes_per_mcu[0] = 4*8; + + return !priv->components[0]; +} + +static const struct tinyjpeg_colorspace format_rgba32 = + { + { + YCrCB_to_RGBA32_1x1, + YCrCB_to_RGBA32_1x2, + YCrCB_to_RGBA32_2x1, + YCrCB_to_RGBA32_2x2, + }, + tinyjpeg_decode_mcu_3comp_table, + initialize_rgba32 + }; + +const tinyjpeg_colorspace_t TINYJPEG_FMT_RGBA32 = &format_rgba32; diff --git a/com32/lib/jpeg/tinyjpeg-internal.h b/com32/lib/jpeg/tinyjpeg-internal.h new file mode 100644 index 0000000..78cb042 --- /dev/null +++ b/com32/lib/jpeg/tinyjpeg-internal.h @@ -0,0 +1,186 @@ +/* + * Small jpeg decoder library (Internal header) + * + * Copyright (c) 2006, Luc Saillard + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * - Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + * + */ + + +#ifndef __TINYJPEG_INTERNAL_H_ +#define __TINYJPEG_INTERNAL_H_ + +struct jdec_private; + +#define HUFFMAN_HASH_NBITS 9 +#define HUFFMAN_HASH_SIZE (1UL<0) + { + if (value & (1UL<=0) + { + bitstr[j++] = (value & (1UL< + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * - Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + * + */ + +#include +#include +#include +#include + +#include "tinyjpeg.h" +#include "tinyjpeg-internal.h" + +/* Global variable to return the last error found while deconding */ +static char error_string[256]; + +static const unsigned char zigzag[64] = +{ + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63 +}; + +/* Set up the standard Huffman tables (cf. JPEG standard section K.3) */ +/* IMPORTANT: these are only valid for 8-bit data precision! */ +static const unsigned char bits_dc_luminance[17] = +{ + 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 +}; +static const unsigned char val_dc_luminance[] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 +}; + +static const unsigned char bits_dc_chrominance[17] = +{ + 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 +}; +static const unsigned char val_dc_chrominance[] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 +}; + +static const unsigned char bits_ac_luminance[17] = +{ + 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d +}; +static const unsigned char val_ac_luminance[] = +{ + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa +}; + +static const unsigned char bits_ac_chrominance[17] = +{ + 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 +}; + +static const unsigned char val_ac_chrominance[] = +{ + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, + 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa +}; + + +/* + * 4 functions to manage the stream + * + * fill_nbits: put at least nbits in the reservoir of bits. + * But convert any 0xff,0x00 into 0xff + * get_nbits: read nbits from the stream, and put it in result, + * bits is removed from the stream and the reservoir is filled + * automaticaly. The result is signed according to the number of + * bits. + * look_nbits: read nbits from the stream without marking as read. + * skip_nbits: read nbits from the stream but do not return the result. + * + * stream: current pointer in the jpeg data (read bytes per bytes) + * nbits_in_reservoir: number of bits filled into the reservoir + * reservoir: register that contains bits information. Only nbits_in_reservoir + * is valid. + * nbits_in_reservoir + * <-- 17 bits --> + * Ex: 0000 0000 1010 0000 1111 0000 <== reservoir + * ^ + * bit 1 + * To get two bits from this example + * result = (reservoir >> 15) & 3 + * + */ +#define fill_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted) do { \ + while (nbits_in_reservoir>(nbits_in_reservoir-(nbits_wanted))); \ + nbits_in_reservoir -= (nbits_wanted); \ + reservoir &= ((1U<>(nbits_in_reservoir-(nbits_wanted))); \ +} while(0); + +#define skip_nbits(reservoir,nbits_in_reservoir,stream,nbits_wanted) do { \ + fill_nbits(reservoir,nbits_in_reservoir,stream,(nbits_wanted)); \ + nbits_in_reservoir -= (nbits_wanted); \ + reservoir &= ((1U<reservoir, priv->nbits_in_reservoir, priv->stream, HUFFMAN_HASH_NBITS, hcode); + value = huffman_table->lookup[hcode]; + if (value>=0) + { + int code_size = huffman_table->code_size[value]; + skip_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, code_size); + return value; + } + + /* Decode more bits each time ... */ + for (extra_nbits=0; extra_nbits<16-HUFFMAN_HASH_NBITS; extra_nbits++) + { + nbits = HUFFMAN_HASH_NBITS + 1 + extra_nbits; + + look_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, nbits, hcode); + slowtable = huffman_table->slowtable[extra_nbits]; + /* Search if the code is in this array */ + while (slowtable[0]) { + if (slowtable[0] == hcode) { + skip_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, nbits); + return slowtable[1]; + } + slowtable+=2; + } + } + return 0; +} + + + + +/** + * + * Decode a single block that contains the DCT coefficients. + * The table coefficients is already dezigzaged at the end of the operation. + * + */ +void tinyjpeg_process_Huffman_data_unit(struct jdec_private *priv, int component) +{ + unsigned char j; + int huff_code; + unsigned char size_val, count_0; + + struct component *c = &priv->component_infos[component]; + short int DCT[64]; + + /* Initialize the DCT coef table */ + memset(DCT, 0, sizeof(DCT)); + + /* DC coefficient decoding */ + huff_code = get_next_huffman_code(priv, c->DC_table); + if (huff_code) { + get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, huff_code, DCT[0]); + DCT[0] += c->previous_DC; + c->previous_DC = DCT[0]; + } else { + DCT[0] = c->previous_DC; + } + + /* AC coefficient decoding */ + j = 1; + while (j<64) + { + huff_code = get_next_huffman_code(priv, c->AC_table); + + size_val = huff_code & 0xF; + count_0 = huff_code >> 4; + + if (size_val == 0) + { /* RLE */ + if (count_0 == 0) + break; /* EOB found, go out */ + else if (count_0 == 0xF) + j += 16; /* skip 16 zeros */ + } + else + { + j += count_0; /* skip count_0 zeroes */ + get_nbits(priv->reservoir, priv->nbits_in_reservoir, priv->stream, size_val, DCT[j]); + j++; + } + } + + for (j = 0; j < 64; j++) + c->DCT[j] = DCT[zigzag[j]]; + +} + +/* + * Takes two array of bits, and build the huffman table for size, and code + * + * lookup will return the symbol if the code is less or equal than HUFFMAN_HASH_NBITS. + * code_size will be used to known how many bits this symbol is encoded. + * slowtable will be used when the first lookup didn't give the result. + */ +static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table) +{ + unsigned int i, j, code, code_size, val, nbits; + unsigned char huffsize[257], *hz; + unsigned int huffcode[257], *hc; + int next_free_entry; + + /* + * Build a temp array + * huffsize[X] => numbers of bits to write vals[X] + */ + hz = huffsize; + for (i=1; i<=16; i++) + { + for (j=1; j<=bits[i]; j++) + *hz++ = i; + } + *hz = 0; + + memset(table->lookup, 0xff, sizeof(table->lookup)); + for (i=0; i<(16-HUFFMAN_HASH_NBITS); i++) + table->slowtable[i][0] = 0; + + /* Build a temp array + * huffcode[X] => code used to write vals[X] + */ + code = 0; + hc = huffcode; + hz = huffsize; + nbits = *hz; + while (*hz) + { + while (*hz == nbits) { + *hc++ = code++; + hz++; + } + code <<= 1; + nbits++; + } + + /* + * Build the lookup table, and the slowtable if needed. + */ + next_free_entry = -1; + for (i=0; huffsize[i]; i++) + { + val = vals[i]; + code = huffcode[i]; + code_size = huffsize[i]; + + trace("val=%2.2x code=%8.8x codesize=%2.2d\n", i, code, code_size); + + table->code_size[val] = code_size; + if (code_size <= HUFFMAN_HASH_NBITS) + { + /* + * Good: val can be put in the lookup table, so fill all value of this + * column with value val + */ + int repeat = 1UL<<(HUFFMAN_HASH_NBITS - code_size); + code <<= HUFFMAN_HASH_NBITS - code_size; + while ( repeat-- ) + table->lookup[code++] = val; + + } + else + { + /* Perhaps sorting the array will be an optimization */ + uint16_t *slowtable = table->slowtable[code_size-HUFFMAN_HASH_NBITS-1]; + while(slowtable[0]) + slowtable+=2; + slowtable[0] = code; + slowtable[1] = val; + slowtable[2] = 0; + /* TODO: NEED TO CHECK FOR AN OVERFLOW OF THE TABLE */ + } + + } + +} + +static void build_default_huffman_tables(struct jdec_private *priv) +{ + if ( (priv->flags & TINYJPEG_FLAGS_MJPEG_TABLE) + && priv->default_huffman_table_initialized) + return; + + build_huffman_table(bits_dc_luminance, val_dc_luminance, &priv->HTDC[0]); + build_huffman_table(bits_ac_luminance, val_ac_luminance, &priv->HTAC[0]); + + build_huffman_table(bits_dc_chrominance, val_dc_chrominance, &priv->HTDC[1]); + build_huffman_table(bits_ac_chrominance, val_ac_chrominance, &priv->HTAC[1]); + + priv->default_huffman_table_initialized = 1; +} + + + +/******************************************************************************* + * + * Colorspace conversion routine + * + * + * Note: + * YCbCr is defined per CCIR 601-1, except that Cb and Cr are + * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. + * The conversion equations to be implemented are therefore + * R = Y + 1.40200 * Cr + * G = Y - 0.34414 * Cb - 0.71414 * Cr + * B = Y + 1.77200 * Cb + * + ******************************************************************************/ + +static void print_SOF(const unsigned char *stream) +{ + int width, height, nr_components, precision; +#if DEBUG + const char *nr_components_to_string[] = { + "????", + "Grayscale", + "????", + "YCbCr", + "CYMK" + }; +#endif + + precision = stream[2]; + height = be16_to_cpu(stream+3); + width = be16_to_cpu(stream+5); + nr_components = stream[7]; + + trace("> SOF marker\n"); + trace("Size:%dx%d nr_components:%d (%s) precision:%d\n", + width, height, + nr_components, nr_components_to_string[nr_components], + precision); +} + +/******************************************************************************* + * + * JPEG/JFIF Parsing functions + * + * Note: only a small subset of the jpeg file format is supported. No markers, + * nor progressive stream is supported. + * + ******************************************************************************/ + +static void build_quantization_table(float *qtable, const unsigned char *ref_table) +{ + /* Taken from libjpeg. Copyright Independent JPEG Group's LLM idct. + * For float AA&N IDCT method, divisors are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + * We apply a further scale factor of 8. + * What's actually stored is 1/divisor so that the inner loop can + * use a multiplication rather than a division. + */ + int i, j; + static const double aanscalefactor[8] = { + 1.0, 1.387039845, 1.306562965, 1.175875602, + 1.0, 0.785694958, 0.541196100, 0.275899379 + }; + const unsigned char *zz = zigzag; + + for (i=0; i<8; i++) { + for (j=0; j<8; j++) { + *qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j]; + } + } + +} + +static int parse_DQT(struct jdec_private *priv, const unsigned char *stream) +{ + int length, qi; + float *table; + + trace("> DQT marker\n"); + length = be16_to_cpu(stream) - 2; + stream += 2; /* Skip length */ + + while (length>0) + { + qi = *stream++; +#if SANITY_CHECK + if (qi>>4) + error("16 bits quantization table is not supported\n"); + if (qi>4) + error("No more 4 quantization table is supported (got %d)\n", qi); +#endif + table = priv->Q_tables[qi]; + build_quantization_table(table, stream); + stream += 64; + length -= 65; + } + return 0; +} + +static int parse_SOF(struct jdec_private *priv, const unsigned char *stream) +{ + int i, width, height, nr_components, cid, sampling_factor; + int Q_table; + struct component *c; + + print_SOF(stream); + + height = be16_to_cpu(stream+3); + width = be16_to_cpu(stream+5); + nr_components = stream[7]; +#if SANITY_CHECK + if (stream[2] != 8) + error("Precision other than 8 is not supported\n"); + if (width>2048 || height>2048) + error("Width and Height (%dx%d) seems suspicious\n", width, height); + if (nr_components != 3) + error("We only support YUV images\n"); + if (height%16) + error("Height need to be a multiple of 16 (current height is %d)\n", height); + if (width%16) + error("Width need to be a multiple of 16 (current Width is %d)\n", width); +#endif + stream += 8; + for (i=0; icomponent_infos[cid]; + c->Vfactor = sampling_factor&0xf; + c->Hfactor = sampling_factor>>4; + c->Q_table = priv->Q_tables[Q_table]; + trace("Component:%d factor:%dx%d Quantization table:%d\n", + cid, c->Hfactor, c->Hfactor, Q_table ); + + } + priv->width = width; + priv->height = height; + + return 0; +} + +static int parse_SOS(struct jdec_private *priv, const unsigned char *stream) +{ + unsigned int i, cid, table; + unsigned int nr_components = stream[2]; + + trace("> SOS marker\n"); + +#if SANITY_CHECK + if (nr_components != 3) + error("We only support YCbCr image\n"); +#endif + + stream += 3; + for (i=0;i=4) + error("We do not support more than 2 AC Huffman table\n"); + if ((table>>4)>=4) + error("We do not support more than 2 DC Huffman table\n"); + trace("ComponentId:%d tableAC:%d tableDC:%d\n", cid, table&0xf, table>>4); +#endif + priv->component_infos[cid].AC_table = &priv->HTAC[table&0xf]; + priv->component_infos[cid].DC_table = &priv->HTDC[table>>4]; + } + priv->stream = stream+3; + return 0; +} + +static int parse_DHT(struct jdec_private *priv, const unsigned char *stream) +{ + unsigned int count, i; + unsigned char huff_bits[17]; + int length, index; + + length = be16_to_cpu(stream) - 2; + stream += 2; /* Skip length */ + + trace("> DHT marker (length=%d)\n", length); + + while (length>0) { + index = *stream++; + + /* We need to calculate the number of bytes 'vals' will takes */ + huff_bits[0] = 0; + count = 0; + for (i=1; i<17; i++) { + huff_bits[i] = *stream++; + count += huff_bits[i]; + } +#if SANITY_CHECK + if (count > 1024) + error("No more than 1024 bytes is allowed to describe a huffman table"); + if ( (index &0xf) >= HUFFMAN_TABLES) + error("No mode than %d Huffman tables is supported\n", HUFFMAN_TABLES); + trace("Huffman table %s n%d\n", (index&0xf0)?"AC":"DC", index&0xf); + trace("Length of the table: %d\n", count); +#endif + + if (index & 0xf0 ) + build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]); + else + build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]); + + length -= 1; + length -= 16; + length -= count; + } + trace("< DHT marker\n"); + return 0; +} + +static void resync(struct jdec_private *priv) +{ + int i; + + /* Init DC coefficients */ + for (i=0; icomponent_infos[i].previous_DC = 0; + + priv->reservoir = 0; + priv->nbits_in_reservoir = 0; + +} + + +static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream) +{ + int chuck_len; + int marker; + int sos_marker_found = 0; + int dht_marker_found = 0; + const unsigned char *next_chunck; + + /* Parse marker */ + while (!sos_marker_found) + { + if (*stream++ != 0xff) + goto bogus_jpeg_format; + /* Skip any padding ff byte (this is normal) */ + while (*stream == 0xff) + stream++; + + marker = *stream++; + chuck_len = be16_to_cpu(stream); + next_chunck = stream + chuck_len; + switch (marker) + { + case SOF: + if (parse_SOF(priv, stream) < 0) + return -1; + break; + case DQT: + if (parse_DQT(priv, stream) < 0) + return -1; + break; + case SOS: + if (parse_SOS(priv, stream) < 0) + return -1; + sos_marker_found = 1; + break; + case DHT: + if (parse_DHT(priv, stream) < 0) + return -1; + dht_marker_found = 1; + break; + default: + trace("> Unknown marker %2.2x\n", marker); + break; + } + + stream = next_chunck; + } + + if (!dht_marker_found) { + trace("No Huffman table loaded, using the default one\n"); + build_default_huffman_tables(priv); + } + +#ifdef SANITY_CHECK + if ( (priv->component_infos[cY].Hfactor < priv->component_infos[cCb].Hfactor) + || (priv->component_infos[cY].Hfactor < priv->component_infos[cCr].Hfactor)) + error("Horizontal sampling factor for Y should be greater than horitontal sampling factor for Cb or Cr\n"); + if ( (priv->component_infos[cY].Vfactor < priv->component_infos[cCb].Vfactor) + || (priv->component_infos[cY].Vfactor < priv->component_infos[cCr].Vfactor)) + error("Vertical sampling factor for Y should be greater than vertical sampling factor for Cb or Cr\n"); + if ( (priv->component_infos[cCb].Hfactor!=1) + || (priv->component_infos[cCr].Hfactor!=1) + || (priv->component_infos[cCb].Vfactor!=1) + || (priv->component_infos[cCr].Vfactor!=1)) + error("Sampling other than 1x1 for Cr and Cb is not supported"); +#endif + + return 0; +bogus_jpeg_format: + trace("Bogus jpeg format\n"); + return -1; +} + +/******************************************************************************* + * + * Functions exported of the library. + * + * Note: Some applications can access directly to internal pointer of the + * structure. It's is not recommended, but if you have many images to + * uncompress with the same parameters, some functions can be called to speedup + * the decoding. + * + ******************************************************************************/ + +/** + * Allocate a new tinyjpeg decoder object. + * + * Before calling any other functions, an object need to be called. + */ +struct jdec_private *tinyjpeg_init(void) +{ + struct jdec_private *priv; + + priv = (struct jdec_private *)calloc(1, sizeof(struct jdec_private)); + if (priv == NULL) + return NULL; + return priv; +} + +/** + * Free a tinyjpeg object. + * + * No others function can be called after this one. + */ +void tinyjpeg_free(struct jdec_private *priv) +{ + int i; + for (i=0; icomponents[i]) + free(priv->components[i]); + priv->components[i] = NULL; + } + free(priv); +} + +/** + * Initialize the tinyjpeg object and prepare the decoding of the stream. + * + * Check if the jpeg can be decoded with this jpeg decoder. + * Fill some table used for preprocessing. + */ +int tinyjpeg_parse_header(struct jdec_private *priv, const unsigned char *buf, unsigned int size) +{ + int ret; + + /* Identify the file */ + if ((buf[0] != 0xFF) || (buf[1] != SOI)) + error("Not a JPG file ?\n"); + + priv->stream_begin = buf+2; + priv->stream_length = size-2; + + ret = parse_JFIF(priv, priv->stream_begin); + + return ret; +} + +/** + * Decode and convert the jpeg image into @pixfmt@ image + * + * Note: components will be automaticaly allocated if no memory is attached. + */ +int tinyjpeg_decode(struct jdec_private *priv, + const struct tinyjpeg_colorspace *pixfmt) +{ + unsigned int x, y, xstride_by_mcu, ystride_by_mcu; + unsigned int bytes_per_blocklines[3], bytes_per_mcu[3]; + decode_MCU_fct decode_MCU; + const decode_MCU_fct *decode_mcu_table; + convert_colorspace_fct convert_to_pixfmt; + + decode_mcu_table = pixfmt->decode_mcu_table; + + /* Fix: check return value */ + pixfmt->initialize(priv, bytes_per_blocklines, bytes_per_mcu); + + xstride_by_mcu = ystride_by_mcu = 8; + if ((priv->component_infos[cY].Hfactor | priv->component_infos[cY].Vfactor) == 1) { + decode_MCU = decode_mcu_table[0]; + convert_to_pixfmt = pixfmt->convert_colorspace[0]; + trace("Use decode 1x1 sampling\n"); + } else if (priv->component_infos[cY].Hfactor == 1) { + decode_MCU = decode_mcu_table[1]; + convert_to_pixfmt = pixfmt->convert_colorspace[1]; + ystride_by_mcu = 16; + trace("Use decode 1x2 sampling (not supported)\n"); + } else if (priv->component_infos[cY].Vfactor == 2) { + decode_MCU = decode_mcu_table[3]; + convert_to_pixfmt = pixfmt->convert_colorspace[3]; + xstride_by_mcu = 16; + ystride_by_mcu = 16; + trace("Use decode 2x2 sampling\n"); + } else { + decode_MCU = decode_mcu_table[2]; + convert_to_pixfmt = pixfmt->convert_colorspace[2]; + xstride_by_mcu = 16; + trace("Use decode 2x1 sampling\n"); + } + + resync(priv); + + /* Don't forget to that block can be either 8 or 16 lines */ + bytes_per_blocklines[0] *= ystride_by_mcu; + bytes_per_blocklines[1] *= ystride_by_mcu; + bytes_per_blocklines[2] *= ystride_by_mcu; + + bytes_per_mcu[0] *= xstride_by_mcu/8; + bytes_per_mcu[1] *= xstride_by_mcu/8; + bytes_per_mcu[2] *= xstride_by_mcu/8; + + /* Just the decode the image by macroblock (size is 8x8, 8x16, or 16x16) */ + for (y=0; y < priv->height/ystride_by_mcu; y++) + { + //trace("Decoding row %d\n", y); + priv->plane[0] = priv->components[0] + (y * bytes_per_blocklines[0]); + priv->plane[1] = priv->components[1] + (y * bytes_per_blocklines[1]); + priv->plane[2] = priv->components[2] + (y * bytes_per_blocklines[2]); + for (x=0; x < priv->width; x+=xstride_by_mcu) + { + decode_MCU(priv); + convert_to_pixfmt(priv); + priv->plane[0] += bytes_per_mcu[0]; + priv->plane[1] += bytes_per_mcu[1]; + priv->plane[2] += bytes_per_mcu[2]; + + } + } + + return 0; +} + +const char *tinyjpeg_get_errorstring(struct jdec_private *priv) +{ + return error_string; +} + +void tinyjpeg_get_size(struct jdec_private *priv, unsigned int *width, unsigned int *height) +{ + *width = priv->width; + *height = priv->height; +} + +int tinyjpeg_get_components(struct jdec_private *priv, unsigned char **components, unsigned int ncomponents) +{ + int i; + if (ncomponents > COMPONENTS) + ncomponents = COMPONENTS; + for (i=0; icomponents[i]; + return 0; +} + +int tinyjpeg_set_components(struct jdec_private *priv, unsigned char * const *components, unsigned int ncomponents) +{ + int i; + if (ncomponents > COMPONENTS) + ncomponents = COMPONENTS; + for (i=0; icomponents[i] = components[i]; + return 0; +} + +int tinyjpeg_get_bytes_per_row(struct jdec_private *priv, + unsigned int *bytes, + unsigned int ncomponents) +{ + int i; + if (ncomponents > COMPONENTS) + ncomponents = COMPONENTS; + for (i=0; ibytes_per_row[i]; + return 0; +} + +int tinyjpeg_set_bytes_per_row(struct jdec_private *priv, + const unsigned int *bytes, + unsigned int ncomponents) +{ + int i; + if (ncomponents > COMPONENTS) + ncomponents = COMPONENTS; + for (i=0; ibytes_per_row[i] = bytes[i]; + return 0; +} + +int tinyjpeg_set_flags(struct jdec_private *priv, int flags) +{ + int oldflags = priv->flags; + priv->flags = flags; + return oldflags; +} diff --git a/com32/lib/jpeg/yuv420p.c b/com32/lib/jpeg/yuv420p.c new file mode 100644 index 0000000..0c5edf7 --- /dev/null +++ b/com32/lib/jpeg/yuv420p.c @@ -0,0 +1,255 @@ +/* + * Small jpeg decoder library + * + * Copyright (c) 2006, Luc Saillard + * All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * - Neither the name of the author nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + * + */ + +/* + * yuv420p.c + */ + +#include +#include +#include +#include + +#include "tinyjpeg.h" +#include "tinyjpeg-internal.h" + +/** + * YCrCb -> YUV420P (1x1) + * .---. + * | 1 | + * `---' + */ +static void YCrCB_to_YUV420P_1x1(struct jdec_private *priv) +{ + const unsigned char *s, *y; + unsigned char *p; + int i,j; + + p = priv->plane[0]; + y = priv->Y; + for (i=0; i<8; i++) + { + memcpy(p, y, 8); + p += priv->bytes_per_row[0]; + y += 8; + } + + p = priv->plane[1]; + s = priv->Cb; + for (i=0; i<8; i+=2) + { + for (j=0; j<8; j+=2, s+=2) + *p++ = *s; + s += 8; /* Skip one line */ + p += priv->bytes_per_row[1] - 4; + } + + p = priv->plane[2]; + s = priv->Cr; + for (i=0; i<8; i+=2) + { + for (j=0; j<8; j+=2, s+=2) + *p++ = *s; + s += 8; /* Skip one line */ + p += priv->bytes_per_row[2] - 4; + } +} + +/** + * YCrCb -> YUV420P (2x1) + * .-------. + * | 1 | 2 | + * `-------' + */ +static void YCrCB_to_YUV420P_2x1(struct jdec_private *priv) +{ + unsigned char *p; + const unsigned char *s, *y1; + int i,j; + + p = priv->plane[0]; + y1 = priv->Y; + for (i=0; i<8; i++) + { + memcpy(p, y1, 16); + p += priv->bytes_per_row[0]; + y1 += 16; + } + + p = priv->plane[1]; + s = priv->Cb; + for (i=0; i<8; i+=2) + { + for (j=0; j<8; j+=1, s+=1) + *p++ = *s; + s += 8; /* Skip one line */ + p += priv->bytes_per_row[1] - 8; + } + + p = priv->plane[2]; + s = priv->Cr; + for (i=0; i<8; i+=2) + { + for (j=0; j<8; j+=1, s+=1) + *p++ = *s; + s += 8; /* Skip one line */ + p += priv->bytes_per_row[2] - 8; + } +} + + +/** + * YCrCb -> YUV420P (1x2) + * .---. + * | 1 | + * |---| + * | 2 | + * `---' + */ +static void YCrCB_to_YUV420P_1x2(struct jdec_private *priv) +{ + const unsigned char *s, *y; + unsigned char *p; + int i,j; + + p = priv->plane[0]; + y = priv->Y; + for (i=0; i<16; i++) + { + memcpy(p, y, 8); + p+=priv->bytes_per_row[0]; + y+=8; + } + + p = priv->plane[1]; + s = priv->Cb; + for (i=0; i<8; i++) + { + for (j=0; j<8; j+=2, s+=2) + *p++ = *s; + p += priv->bytes_per_row[1] - 4; + } + + p = priv->plane[2]; + s = priv->Cr; + for (i=0; i<8; i++) + { + for (j=0; j<8; j+=2, s+=2) + *p++ = *s; + p += priv->bytes_per_row[2] - 4; + } +} + +/** + * YCrCb -> YUV420P (2x2) + * .-------. + * | 1 | 2 | + * |---+---| + * | 3 | 4 | + * `-------' + */ +static void YCrCB_to_YUV420P_2x2(struct jdec_private *priv) +{ + unsigned char *p; + const unsigned char *s, *y1; + int i; + + p = priv->plane[0]; + y1 = priv->Y; + for (i=0; i<16; i++) + { + memcpy(p, y1, 16); + p += priv->bytes_per_row[0]; + y1 += 16; + } + + p = priv->plane[1]; + s = priv->Cb; + for (i=0; i<8; i++) + { + memcpy(p, s, 8); + s += 8; + p += priv->bytes_per_row[1]; + } + + p = priv->plane[2]; + s = priv->Cr; + for (i=0; i<8; i++) + { + memcpy(p, s, 8); + s += 8; + p += priv->bytes_per_row[2]; + } +} + +static int initialize_yuv420p(struct jdec_private *priv, + unsigned int *bytes_per_blocklines, + unsigned int *bytes_per_mcu) +{ + if (priv->components[0] == NULL) + priv->components[0] = (uint8_t *)malloc(priv->width * priv->height); + if (priv->components[1] == NULL) + priv->components[1] = (uint8_t *)malloc(priv->width * priv->height/4); + if (priv->components[2] == NULL) + priv->components[2] = (uint8_t *)malloc(priv->width * priv->height/4); + if (!priv->bytes_per_row[0]) + priv->bytes_per_row[0] = priv->width; + if (!priv->bytes_per_row[1]) + priv->bytes_per_row[1] = priv->width/2; + if (!priv->bytes_per_row[2]) + priv->bytes_per_row[2] = priv->width/2; + + bytes_per_blocklines[0] = priv->bytes_per_row[0]; + bytes_per_blocklines[1] = priv->bytes_per_row[1]/2; + bytes_per_blocklines[2] = priv->bytes_per_row[2]/2; + bytes_per_mcu[0] = 8; + bytes_per_mcu[1] = 4; + bytes_per_mcu[2] = 4; + + /* Return nonzero on failure */ + return !priv->components[0] || !priv->components[1] || !priv->components[2]; +} + +static const struct tinyjpeg_colorspace format_yuv420p = + { + { + YCrCB_to_YUV420P_1x1, + YCrCB_to_YUV420P_1x2, + YCrCB_to_YUV420P_2x1, + YCrCB_to_YUV420P_2x2, + }, + tinyjpeg_decode_mcu_3comp_table, + initialize_yuv420p + }; + +const tinyjpeg_colorspace_t TINYJPEG_FMT_YUV420P = &format_yuv420p; diff --git a/com32/lib/libgcc/__ashldi3.S b/com32/lib/libgcc/__ashldi3.S new file mode 100644 index 0000000..f2cc11c --- /dev/null +++ b/com32/lib/libgcc/__ashldi3.S @@ -0,0 +1,29 @@ +/* + * arch/i386/libgcc/__ashldi3.S + * + * 64-bit shl + */ + .text + .align 4 + .globl __ashldi3 + .type __ashldi3,@function +__ashldi3: +#ifndef REGPARM + movl 4(%esp),%eax + movl 8(%esp),%edx + movb 12(%esp),%cl +#endif + cmpb $32,%cl + jae 1f + + shldl %cl,%eax,%edx + shl %cl,%eax + ret + +1: + xorl %edx,%edx + shl %cl,%eax + xchgl %edx,%eax + ret + + .size __ashldi3,.-__ashldi3 diff --git a/com32/lib/libgcc/__ashrdi3.S b/com32/lib/libgcc/__ashrdi3.S new file mode 100644 index 0000000..3f9c520 --- /dev/null +++ b/com32/lib/libgcc/__ashrdi3.S @@ -0,0 +1,29 @@ +/* + * arch/i386/libgcc/__ashrdi3.S + * + * 64-bit sar + */ + .text + .align 4 + .globl __ashrdi3 + .type __ashrdi3,@function +__ashrdi3: +#ifndef REGPARM + movl 4(%esp),%eax + movl 8(%esp),%edx + movb 12(%esp),%cl +#endif + cmpb $32,%cl + jae 1f + + shrdl %cl,%edx,%eax + sarl %cl,%edx + ret + +1: + sarl %cl,%edx + movl %edx,%eax + cdq + ret + + .size __ashrdi3,.-__ashrdi3 diff --git a/com32/lib/libgcc/__divdi3.c b/com32/lib/libgcc/__divdi3.c new file mode 100644 index 0000000..3641396 --- /dev/null +++ b/com32/lib/libgcc/__divdi3.c @@ -0,0 +1,29 @@ +/* + * arch/i386/libgcc/__divdi3.c + */ + +#include +#include + +extern uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t *rem); + +int64_t __divdi3(int64_t num, int64_t den) +{ + int minus = 0; + int64_t v; + + if ( num < 0 ) { + num = -num; + minus = 1; + } + if ( den < 0 ) { + den = -den; + minus ^= 1; + } + + v = __udivmoddi4(num, den, NULL); + if ( minus ) + v = -v; + + return v; +} diff --git a/com32/lib/libgcc/__lshrdi3.S b/com32/lib/libgcc/__lshrdi3.S new file mode 100644 index 0000000..eee3aac --- /dev/null +++ b/com32/lib/libgcc/__lshrdi3.S @@ -0,0 +1,29 @@ +/* + * arch/i386/libgcc/__lshrdi3.S + * + * 64-bit shr + */ + .text + .align 4 + .globl __lshrdi3 + .type __lshrdi3,@function +__lshrdi3: +#ifndef REGPARM + movl 4(%esp),%eax + movl 8(%esp),%edx + movb 12(%esp),%cl +#endif + cmpb $32,%cl + jae 1f + + shrdl %cl,%edx,%eax + shrl %cl,%edx + ret + +1: + shrl %cl,%edx + xorl %eax,%eax + xchgl %edx,%eax + ret + + .size __lshrdi3,.-__lshrdi3 diff --git a/com32/lib/libgcc/__moddi3.c b/com32/lib/libgcc/__moddi3.c new file mode 100644 index 0000000..af94c23 --- /dev/null +++ b/com32/lib/libgcc/__moddi3.c @@ -0,0 +1,29 @@ +/* + * arch/i386/libgcc/__moddi3.c + */ + +#include +#include + +extern uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t *rem); + +int64_t __moddi3(int64_t num, int64_t den) +{ + int minus = 0; + int64_t v; + + if ( num < 0 ) { + num = -num; + minus = 1; + } + if ( den < 0 ) { + den = -den; + minus ^= 1; + } + + (void) __udivmoddi4(num, den, &v); + if ( minus ) + v = -v; + + return v; +} diff --git a/com32/lib/libgcc/__muldi3.S b/com32/lib/libgcc/__muldi3.S new file mode 100644 index 0000000..648a88a --- /dev/null +++ b/com32/lib/libgcc/__muldi3.S @@ -0,0 +1,34 @@ +/* + * arch/i386/libgcc/__muldi3.S + * + * 64*64 = 64 bit unsigned multiplication + */ + + .text + .align 4 + .globl __muldi3 + .type __muldi3,@function +__muldi3: + push %esi +#ifndef REGPARM + movl 8(%esp),%eax + movl %eax,%esi + movl 16(%esp),%ecx + mull %ecx + imull 12(%esp),%ecx + imull 20(%esp),%esi + addl %ecx,%edx + addl %esi,%edx +#else + movl %eax,%esi + push %edx + mull %ecx + imull 8(%esp),%esi + addl %esi,%edx + pop %esi + imull %esi,%ecx + addl %ecx,%edx +#endif + pop %esi + ret + .size __muldi3,.-__muldi3 diff --git a/com32/lib/libgcc/__negdi2.S b/com32/lib/libgcc/__negdi2.S new file mode 100644 index 0000000..37b0d2d --- /dev/null +++ b/com32/lib/libgcc/__negdi2.S @@ -0,0 +1,21 @@ +/* + * arch/i386/libgcc/__negdi2.S + * + * 64-bit negation + */ + + .text + .align 4 + .globl __negdi2 + .type __negdi2,@function +__negdi2: +#ifndef REGPARM + movl 4(%esp),%eax + movl 8(%esp),%edx +#endif + negl %edx + negl %eax + sbbl $0,%edx + ret + + .size __negdi2,.-__negdi2 diff --git a/com32/lib/libgcc/__udivdi3.c b/com32/lib/libgcc/__udivdi3.c new file mode 100644 index 0000000..901ce2a --- /dev/null +++ b/com32/lib/libgcc/__udivdi3.c @@ -0,0 +1,13 @@ +/* + * arch/i386/libgcc/__divdi3.c + */ + +#include +#include + +extern uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t *rem); + +uint64_t __udivdi3(uint64_t num, uint64_t den) +{ + return __udivmoddi4(num, den, NULL); +} diff --git a/com32/lib/libgcc/__udivmoddi4.c b/com32/lib/libgcc/__udivmoddi4.c new file mode 100644 index 0000000..ad57680 --- /dev/null +++ b/com32/lib/libgcc/__udivmoddi4.c @@ -0,0 +1,32 @@ +#include +#include + +uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t *rem_p) +{ + uint64_t quot = 0, qbit = 1; + + if ( den == 0 ) { + __divide_error(); + return 0; /* If trap returns... */ + } + + /* Left-justify denominator and count shift */ + while ( (int64_t)den >= 0 ) { + den <<= 1; + qbit <<= 1; + } + + while ( qbit ) { + if ( den <= num ) { + num -= den; + quot += qbit; + } + den >>= 1; + qbit >>= 1; + } + + if ( rem_p ) + *rem_p = num; + + return quot; +} diff --git a/com32/lib/libgcc/__umoddi3.c b/com32/lib/libgcc/__umoddi3.c new file mode 100644 index 0000000..c007d48 --- /dev/null +++ b/com32/lib/libgcc/__umoddi3.c @@ -0,0 +1,16 @@ +/* + * arch/i386/libgcc/__umoddi3.c + */ + +#include +#include + +extern uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t *rem); + +uint64_t __umoddi3(uint64_t num, uint64_t den) +{ + uint64_t v; + + (void) __udivmoddi4(num, den, &v); + return v; +} diff --git a/com32/lib/libpng/ANNOUNCE b/com32/lib/libpng/ANNOUNCE new file mode 100644 index 0000000..e40b94a --- /dev/null +++ b/com32/lib/libpng/ANNOUNCE @@ -0,0 +1,32 @@ + +Libpng 1.2.8 - December 3, 2004 + +This is a public release of libpng, intended for use in production codes. + +Changes since the last public release (1.2.7): + + Fixed bug in png_text_compress() that would fail to complete a large block. + Fixed bug, introduced in libpng-1.2.7, that overruns a buffer during + strip alpha operation in png_do_strip_filler(). + Added PNG_1_2_X definition in pngconf.h + #ifdef out png_info_init in png.c and png_read_init in pngread.c (as of 1.3.0) + Reduce color_type to a nonalpha type after strip alpha operation in + png_do_strip_filler(). + Revised definitions of PNG_MAX_UINT_32, PNG_MAX_SIZE, and PNG_MAXSUM + Fixed (again) definition of PNG_LIBPNG_VER_DLLNUM in png.h (Cosmin). + Added PNG_LIBPNG_BUILD_PRIVATE in png.h (Cosmin). + Set png_ptr->zstream.data_type to Z_BINARY, to avoid unnecessary detection + of data type in deflate (Cosmin). + Deprecated but continue to support SPECIALBUILD and PRIVATEBUILD in favor of + PNG_LIBPNG_BUILD_SPECIAL_STRING and PNG_LIBPNG_BUILD_PRIVATE_STRING. + Despammed mailing addresses by masking "@" with "at". + Added scripts/makefile.elf with supporting code in pngconf.h for symbol + versioning (John Bowler). + Added projects/visualc71 (Simon-pierre). + +Send comments/corrections/commendations to +png-implement at ccrc.wustl.edu (subscription required; write to +majordomo at ccrc.wustl.edu with "subscribe png-implement" in the message) +or to glennrp at users.sourceforge.net + +Glenn R-P diff --git a/com32/lib/libpng/CHANGES b/com32/lib/libpng/CHANGES new file mode 100644 index 0000000..46f43dc --- /dev/null +++ b/com32/lib/libpng/CHANGES @@ -0,0 +1,1419 @@ + +CHANGES - changes for libpng + +version 0.2 + added reader into png.h + fixed small problems in stub file + +version 0.3 + added pull reader + split up pngwrite.c to several files + added pnglib.txt + added example.c + cleaned up writer, adding a few new tranformations + fixed some bugs in writer + interfaced with zlib 0.5 + added K&R support + added check for 64 KB blocks for 16 bit machines + +version 0.4 + cleaned up code and commented code + simplified time handling into png_time + created png_color_16 and png_color_8 to handle color needs + cleaned up color type defines + fixed various bugs + made various names more consistant + interfaced with zlib 0.71 + cleaned up zTXt reader and writer (using zlib's Reset functions) + split transformations into pngrtran.c and pngwtran.c + +version 0.5 + interfaced with zlib 0.8 + fixed many reading and writing bugs + saved using 3 spaces instead of tabs + +version 0.6 + added png_large_malloc() and png_large_free() + added png_size_t + cleaned up some compiler warnings + added png_start_read_image() + +version 0.7 + cleaned up lots of bugs + finished dithering and other stuff + added test program + changed name from pnglib to libpng + +version 0.71 [June, 1995] + changed pngtest.png for zlib 0.93 + fixed error in libpng.txt and example.c + +version 0.8 + cleaned up some bugs + added png_set_filler() + split up pngstub.c into pngmem.c, pngio.c, and pngerror.c + added #define's to remove unwanted code + moved png_info_init() to png.c + added old_size into png_realloc() + added functions to manually set filtering and compression info + changed compression parameters based on image type + optimized filter selection code + added version info + changed external functions passing floats to doubles (k&r problems?) + put all the configurable stuff in pngconf.h + enabled png_set_shift to work with paletted images on read + added png_read_update_info() - updates info structure with + transformations + +version 0.81 [August, 1995] + incorporated Tim Wegner's medium model code (thanks, Tim) + +version 0.82 [September, 1995] + [unspecified changes] + +version 0.85 [December, 1995] + added more medium model code (almost everything's a far) + added i/o, error, and memory callback functions + fixed some bugs (16 bit, 4 bit interlaced, etc.) + added first run progressive reader (barely tested) + +version 0.86 [January, 1996] + fixed bugs + improved documentation + +version 0.87 [January, 1996] + fixed medium model bugs + fixed other bugs introduced in 0.85 and 0.86 + added some minor documentation + +version 0.88 [January, 1996] + fixed progressive bugs + replaced tabs with spaces + cleaned up documentation + added callbacks for read/write and warning/error functions + +version 0.89 [July, 1996] + added new initialization API to make libpng work better with shared libs + we now have png_create_read_struct(), png_create_write_struct(), + png_create_info_struct(), png_destroy_read_struct(), and + png_destroy_write_struct() instead of the separate calls to + malloc and png_read_init(), png_info_init(), and png_write_init() + changed warning/error callback functions to fix bug - this means you + should use the new initialization API if you were using the old + png_set_message_fn() calls, and that the old API no longer exists + so that people are aware that they need to change their code + changed filter selection API to allow selection of multiple filters + since it didn't work in previous versions of libpng anyways + optimized filter selection code + fixed png_set_background() to allow using an arbitrary RGB color for + paletted images + fixed gamma and background correction for paletted images, so + png_correct_palette is not needed unless you are correcting an + external palette (you will need to #define PNG_CORRECT_PALETTE_SUPPORTED + in pngconf.h) - if nobody uses this, it may disappear in the future. + fixed bug with Borland 64K memory allocation (Alexander Lehmann) + fixed bug in interlace handling (Smarasderagd, I think) + added more error checking for writing and image to reduce invalid files + separated read and write functions so that they won't both be linked + into a binary when only reading or writing functionality is used + new pngtest image also has interlacing and zTXt + updated documentation to reflect new API + +version 0.90 [January, 1997] + made CRC errors/warnings on critical and ancillary chunks configurable + libpng will use the zlib CRC routines by (compile-time) default + changed DOS small/medium model memory support - needs zlib 1.04 (Tim Wegner) + added external C++ wrapper statements to png.h (Gilles Dauphin) + allow PNG file to be read when some or all of file signature has already + been read from the beginning of the stream. ****This affects the size + of info_struct and invalidates all programs that use a shared libpng**** + fixed png_filler() declarations + fixed? background color conversions + fixed order of error function pointers to match documentation + current chunk name is now available in png_struct to reduce the number + of nearly identical error messages (will simplify multi-lingual + support when available) + try to get ready for unknown-chunk callback functions: + - previously read critical chunks are flagged, so the chunk handling + routines can determine if the chunk is in the right place + - all chunk handling routines have the same prototypes, so we will + be able to handle all chunks via a callback mechanism + try to fix Linux "setjmp" buffer size problems + removed png_large_malloc, png_large_free, and png_realloc functions. + +version 0.95 [March, 1997] + fixed bug in pngwutil.c allocating "up_row" twice and "avg_row" never + fixed bug in PNG file signature compares when start != 0 + changed parameter type of png_set_filler(...filler...) from png_byte + to png_uint_32 + added test for MACOS to ensure that both math.h and fp.h are not #included + added macros for libpng to be compiled as a Windows DLL (Andreas Kupries) + added "packswap" transformation, which changes the endianness of + packed-pixel bytes (Kevin Bracey) + added "strip_alpha" transformation, which removes the alpha channel of + input images without using it (not neccesarily a good idea) + added "swap_alpha" transformation, which puts the alpha channel in front + of the color bytes instead of after + removed all implicit variable tests which assume NULL == 0 (I think) + changed several variables to "png_size_t" to show 16/32-bit limitations + added new pCAL chunk read/write support + added experimental filter selection weighting (Greg Roelofs) + removed old png_set_rgbx() and png_set_xrgb() functions that have been + obsolete for about 2 years now (use png_set_filler() instead) + added macros to read 16- and 32-bit ints directly from buffer, to be + used only on those systems that support it (namely PowerPC and 680x0) + With some testing, this may become the default for MACOS/PPC systems. + only calculate CRC on data if we are going to use it + added macros for zTXt compression type PNG_zTXt_COMPRESSION_??? + added macros for simple libpng debugging output selectable at compile time + removed PNG_READ_END_MODE in progressive reader (Smarasderagd) + more description of info_struct in libpng.txt and png.h + more instructions in example.c + more chunk types tested in pngtest.c + renamed pngrcb.c to pngset.c, and all png_read_ functions to be + png_set_. We now have corresponding png_get_ + functions in pngget.c to get infomation in info_ptr. This isolates + the application from the internal organization of png_info_struct + (good for shared library implementations). + +version 0.96 [May, 1997] + fixed serious bug with < 8bpp images introduced in 0.95 + fixed 256-color transparency bug (Greg Roelofs) + fixed up documentation (Greg Roelofs, Laszlo Nyul) + fixed "error" in pngconf.h for Linux setjmp() behaviour + fixed DOS medium model support (Tim Wegner) + fixed png_check_keyword() for case with error in static string text + added read of CRC after IEND chunk for embedded PNGs (Laszlo Nyul) + added typecasts to quiet compiler errors + added more debugging info + +version 0.97 [January, 1998] + removed PNG_USE_OWN_CRC capability + relocated png_set_crc_action from pngrutil.c to pngrtran.c + fixed typecasts of "new_key", etc. (Andreas Dilger) + added RFC 1152 [sic] date support + fixed bug in gamma handling of 4-bit grayscale + added 2-bit grayscale gamma handling (Glenn R-P) + added more typecasts. 65536L becomes (png_uint_32)65536L, etc. (Glenn R-P) + minor corrections in libpng.txt + added simple sRGB support (Glenn R-P) + easier conditional compiling, e.g. define PNG_READ/WRITE_NOT_FULLY_SUPPORTED; + all configurable options can be selected from command-line instead + of having to edit pngconf.h (Glenn R-P) + fixed memory leak in pngwrite.c (free info_ptr->text) (Glenn R-P) + added more conditions for png_do_background, to avoid changing + black pixels to background when a background is supplied and + no pixels are transparent + repaired PNG_NO_STDIO behaviour + tested NODIV support and made it default behaviour (Greg Roelofs) + added "-m" option and PNGTEST_DEBUG_MEMORY to pngtest (John Bowler) + regularized version numbering scheme and bumped shared-library major + version number to 2 to avoid problems with libpng 0.89 apps (Greg Roelofs) + +version 0.98 [January, 1998] + cleaned up some typos in libpng.txt and in code documentation + fixed memory leaks in pCAL chunk processing (Glenn R-P and John Bowler) + cosmetic change "display_gamma" to "screen_gamma" in pngrtran.c + changed recommendation about file_gamma for PC images to .51 from .45, + in example.c and libpng.txt, added comments to distinguish between + screen_gamma, viewing_gamma, and display_gamma. + changed all references to RFC1152 to read RFC1123 and changed the + PNG_TIME_RFC1152_SUPPORTED macro to PNG_TIME_RFC1123_SUPPORTED + added png_invert_alpha capability (Glenn R-P -- suggestion by Jon Vincent) + changed srgb_intent from png_byte to int to avoid compiler bugs + +version 0.99 [January 30, 1998] + free info_ptr->text instead of end_info_ptr->text in pngread.c (John Bowler) + fixed a longstanding "packswap" bug in pngtrans.c + fixed some inconsistencies in pngconf.h that prevented compiling with + PNG_READ_GAMMA_SUPPORTED and PNG_READ_hIST_SUPPORTED undefined + fixed some typos and made other minor rearrangement of libpng.txt (Andreas) + changed recommendation about file_gamma for PC images to .50 from .51 in + example.c and libpng.txt, and changed file_gamma for sRGB images to .45 + added a number of functions to access information from the png structure + png_get_image_height(), etc. (Glenn R-P, suggestion by Brad Pettit) + added TARGET_MACOS similar to zlib-1.0.8 + define PNG_ALWAYS_EXTERN when __MWERKS__ && WIN32 are defined + added type casting to all png_malloc() function calls +version 0.99a [January 31, 1998] + Added type casts and parentheses to all returns that return a value.(Tim W.) +version 0.99b [February 4, 1998] + Added type cast png_uint_32 on malloc function calls where needed. + Changed type of num_hist from png_uint_32 to int (same as num_palette). + Added checks for rowbytes overflow, in case png_size_t is less than 32 bits. + Renamed makefile.elf to makefile.lnx. +version 0.99c [February 7, 1998] + More type casting. Removed erroneous overflow test in pngmem.c. + Added png_buffered_memcpy() and png_buffered_memset(), apply them to rowbytes. + Added UNIX manual pages libpng.3 (incorporating libpng.txt) and png.5. +version 0.99d [February 11, 1998] + Renamed "far_to_near()" "png_far_to_near()" + Revised libpng.3 + Version 99c "buffered" operations didn't work as intended. Replaced them + with png_memcpy_check() and png_memset_check(). + Added many "if (png_ptr == NULL) return" to quell compiler warnings about + unused png_ptr, mostly in pngget.c and pngset.c. + Check for overlength tRNS chunk present when indexed-color PLTE is read. + Cleaned up spelling errors in libpng.3/libpng.txt + Corrected a problem with png_get_tRNS() which returned undefined trans array +version 0.99e [February 28, 1998] + Corrected png_get_tRNS() again. + Add parentheses for easier reading of pngget.c, fixed "||" should be "&&". + Touched up example.c to make more of it compileable, although the entire + file still can't be compiled (Willem van Schaik) + Fixed a bug in png_do_shift() (Bryan Tsai) + Added a space in png.h prototype for png_write_chunk_start() + Replaced pngtest.png with one created with zlib 1.1.1 + Changed pngtest to report PASS even when file size is different (Jean-loup G.) + Corrected some logic errors in png_do_invert_alpha() (Chris Patterson) +version 0.99f [March 5, 1998] + Corrected a bug in pngpread() introduced in version 99c (Kevin Bracey) + Moved makefiles into a "scripts" directory, and added INSTALL instruction file + Added makefile.os2 and pngos2.def (A. Zabolotny) and makefile.s2x (W. Sebok) + Added pointers to "note on libpng versions" in makefile.lnx and README + Added row callback feature when reading and writing nonprogressive rows + and added a test of this feature in pngtest.c + Added user transform callbacks, with test of the feature in pngtest.c +version 0.99g [March 6, 1998, morning] + Minor changes to pngtest.c to suppress compiler warnings. + Removed "beta" language from documentation. +version 0.99h [March 6, 1998, evening] + Minor changes to previous minor changes to pngtest.c + Changed PNG_READ_NOT_FULLY_SUPPORTED to PNG_READ_TRANSFORMS_NOT_SUPPORTED + and added PNG_PROGRESSIVE_READ_NOT_SUPPORTED macro + Added user transform capability + +version 1.00 [March 7, 1998] + Changed several typedefs in pngrutil.c + Added makefile.wat (Pawel Mrochen), updated makefile.tc3 (Willem van Schaik) + replaced "while(1)" with "for(;;)" + added PNGARG() to prototypes in pngtest.c and removed some prototypes + updated some of the makefiles (Tom Lane) + changed some typedefs (s_start, etc.) in pngrutil.c + fixed dimensions of "short_months" array in pngwrite.c + Replaced ansi2knr.c with the one from jpeg-v6 + +version 1.0.0 [March 8, 1998] + Changed name from 1.00 to 1.0.0 (Adam Costello) + Added smakefile.ppc (with SCOPTIONS.ppc) for Amiga PPC (Andreas Kleinert) +version 1.0.0a [March 9, 1998] + Fixed three bugs in pngrtran.c to make gamma+background handling consistent + (Greg Roelofs) + Changed format of the PNG_LIBPNG_VER integer to xyyzz instead of xyz + for major, minor, and bugfix releases. This is 10001. (Adam Costello, + Tom Lane) + Make months range from 1-12 in png_convert_to_rfc1123 +version 1.0.0b [March 13, 1998] + Quieted compiler complaints about two empty "for" loops in pngrutil.c + Minor changes to makefile.s2x + Removed #ifdef/#endif around a png_free() in pngread.c + +version 1.0.1 [March 14, 1998] + Changed makefile.s2x to reduce security risk of using a relative pathname + Fixed some typos in the documentation (Greg). + Fixed a problem with value of "channels" returned by png_read_update_info() +version 1.0.1a [April 21, 1998] + Optimized Paeth calculations by replacing abs() function calls with intrinsics + plus other loop optimizations. Improves avg decoding speed by about 20%. + Commented out i386istic "align" compiler flags in makefile.lnx. + Reduced the default warning level in some makefiles, to make them consistent. + Removed references to IJG and JPEG in the ansi2knr.c copyright statement. + Fixed a bug in png_do_strip_filler with XXRRGGBB => RRGGBB transformation. + Added grayscale and 16-bit capability to png_do_read_filler(). + Fixed a bug in pngset.c, introduced in version 0.99c, that sets rowbytes + too large when writing an image with bit_depth < 8 (Bob Dellaca). + Corrected some bugs in the experimental weighted filtering heuristics. + Moved a misplaced pngrutil code block that truncates tRNS if it has more + than num_palette entries -- test was done before num_palette was defined. + Fixed a png_convert_to_rfc1123() bug that converts day 31 to 0 (Steve Eddins). + Changed compiler flags in makefile.wat for better optimization (Pawel Mrochen). +version 1.0.1b [May 2, 1998] + Relocated png_do_gray_to_rgb() within png_do_read_transformations() (Greg). + Relocated the png_composite macros from pngrtran.c to png.h (Greg). + Added makefile.sco (contributed by Mike Hopkirk). + Fixed two bugs (missing definitions of "istop") introduced in libpng-1.0.1a. + Fixed a bug in pngrtran.c that would set channels=5 under some circumstances. + More work on the Paeth-filtering, achieving imperceptible speedup (A Kleinert). + More work on loop optimization which may help when compiled with C++ compilers. + Added warnings when people try to use transforms they've defined out. + Collapsed 4 "i" and "c" loops into single "i" loops in pngrtran and pngwtran. + Revised paragraph about png_set_expand() in libpng.txt and libpng.3 (Greg) +version 1.0.1c [May 11, 1998] + Fixed a bug in pngrtran.c (introduced in libpng-1.0.1a) where the masks for + filler bytes should have been 0xff instead of 0xf. + Added max_pixel_depth=32 in pngrutil.c when using FILLER with palette images. + Moved PNG_WRITE_WEIGHTED_FILTER_SUPPORTED and PNG_WRITE_FLUSH_SUPPORTED + out of the PNG_WRITE_TRANSFORMS_NOT_SUPPORTED block of pngconf.h + Added "PNG_NO_WRITE_TRANSFORMS" etc., as alternatives for *_NOT_SUPPORTED, + for consistency, in pngconf.h + Added individual "ifndef PNG_NO_[CAPABILITY]" in pngconf.h to make it easier + to remove unwanted capabilities via the compile line + Made some corrections to grammar (which, it's) in documentation (Greg). + Corrected example.c, use of row_pointers in png_write_image(). +version 1.0.1d [May 24, 1998] + Corrected several statements that used side effects illegally in pngrutil.c + and pngtrans.c, that were introduced in version 1.0.1b + Revised png_read_rows() to avoid repeated if-testing for NULL (A Kleinert) + More corrections to example.c, use of row_pointers in png_write_image() + and png_read_rows(). + Added pngdll.mak and pngdef.pas to scripts directory, contributed by + Bob Dellaca, to make a png32bd.dll with Borland C++ 4.5 + Fixed error in example.c with png_set_text: num_text is 3, not 2 (Guido V.) + Changed several loops from count-down to count-up, for consistency. +version 1.0.1e [June 6, 1998] + Revised libpng.txt and libpng.3 description of png_set_read|write_fn(), and + added warnings when people try to set png_read_fn and png_write_fn in + the same structure. + Added a test such that png_do_gamma will be done when num_trans==0 + for truecolor images that have defined a background. This corrects an + error that was introduced in libpng-0.90 that can cause gamma processing + to be skipped. + Added tests in png.h to include "trans" and "trans_values" in structures + when PNG_READ_BACKGROUND_SUPPORTED or PNG_READ_EXPAND_SUPPORTED is defined. + Add png_free(png_ptr->time_buffer) in png_destroy_read_struct() + Moved png_convert_to_rfc_1123() from pngwrite.c to png.c + Added capability for user-provided malloc_fn() and free_fn() functions, + and revised pngtest.c to demonstrate their use, replacing the + PNGTEST_DEBUG_MEM feature. + Added makefile.w32, for Microsoft C++ 4.0 and later (Tim Wegner). + +version 1.0.2 [June 14, 1998] + Fixed two bugs in makefile.bor . +version 1.0.2a [December 30, 1998] + Replaced and extended code that was removed from png_set_filler() in 1.0.1a. + Fixed a bug in png_do_filler() that made it fail to write filler bytes in + the left-most pixel of each row (Kevin Bracey). + Changed "static pngcharp tIME_string" to "static char tIME_string[30]" + in pngtest.c (Duncan Simpson). + Fixed a bug in pngtest.c that caused pngtest to try to write a tIME chunk + even when no tIME chunk was present in the source file. + Fixed a problem in pngrutil.c: gray_to_rgb didn't always work with 16-bit. + Fixed a problem in png_read_push_finish_row(), which would not skip some + passes that it should skip, for images that are less than 3 pixels high. + Interchanged the order of calls to png_do_swap() and png_do_shift() + in pngwtran.c (John Cromer). + Added #ifdef PNG_DEBUG/#endif surrounding use of PNG_DEBUG in png.h . + Changed "bad adaptive filter type" from error to warning in pngrutil.c . + Fixed a documentation error about default filtering with 8-bit indexed-color. + Separated the PNG_NO_STDIO macro into PNG_NO_STDIO and PNG_NO_CONSOLE_IO + (L. Peter Deutsch). + Added png_set_rgb_to_gray() and png_get_rgb_to_gray_status() functions. + Added png_get_copyright() and png_get_header_version() functions. + Revised comments on png_set_progressive_read_fn() in libpng.txt and example.c + Added information about debugging in libpng.txt and libpng.3 . + Changed "ln -sf" to "ln -s -f" in makefile.s2x, makefile.lnx, and makefile.sco. + Removed lines after Dynamic Dependencies" in makefile.aco . + Revised makefile.dec to make a shared library (Jeremie Petit). + Removed trailing blanks from all files. +version 1.0.2a [January 6, 1999] + Removed misplaced #endif and #ifdef PNG_NO_EXTERN near the end of png.h + Added "if" tests to silence complaints about unused png_ptr in png.h and png.c + Changed "check_if_png" function in example.c to return true (nonzero) if PNG. + Changed libpng.txt to demonstrate png_sig_cmp() instead of png_check_sig() + which is obsolete. + +version 1.0.3 [January 14, 1999] + Added makefile.hux, for Hewlett Packard HPUX 10.20 and 11.00 (Jim Rice) + Added a statement of Y2K compliance in png.h, libpng.3, and Y2KINFO. +version 1.0.3a [August 12, 1999] + Added check for PNG_READ_INTERLACE_SUPPORTED in pngread.c; issue a warning + if an attempt is made to read an interlaced image when it's not supported. + Added check if png_ptr->trans is defined before freeing it in pngread.c + Modified the Y2K statement to include versions back to version 0.71 + Fixed a bug in the check for valid IHDR bit_depth/color_types in pngrutil.c + Modified makefile.wat (added -zp8 flag, ".symbolic", changed some comments) + Replaced leading blanks with tab characters in makefile.hux + Changed "dworkin.wustl.edu" to "ccrc.wustl.edu" in various documents. + Changed (float)red and (float)green to (double)red, (double)green + in png_set_rgb_to_gray() to avoid "promotion" problems in AIX. + Fixed a bug in pngconf.h that omitted when PNG_DEBUG==0 (K Bracey). + Reformatted libpng.3 and libpngpf.3 with proper fonts (script by J. vanZandt). + Updated documentation to refer to the PNG-1.2 specification. + Removed ansi2knr.c and left pointers to the latest source for ansi2knr.c + in makefile.knr, INSTALL, and README (L. Peter Deutsch) + Fixed bugs in calculation of the length of rowbytes when adding alpha + channels to 16-bit images, in pngrtran.c (Chris Nokleberg) + Added function png_set_user_transform_info() to store user_transform_ptr, + user_depth, and user_channels into the png_struct, and a function + png_get_user_transform_ptr() to retrieve the pointer (Chris Nokleberg) + Added function png_set_empty_plte_permitted() to make libpng useable + in MNG applications. + Corrected the typedef for png_free_ptr in png.h (Jesse Jones). + Correct gamma with srgb is 45455 instead of 45000 in pngrutil.c, to be + consistent with PNG-1.2, and allow variance of 500 before complaining. + Added assembler code contributed by Intel in file pngvcrd.c and modified + makefile.w32 to use it (Nirav Chhatrapati, INTEL Corporation, Gilles Vollant) + Changed "ln -s -f" to "ln -f -s" in the makefiles to make Solaris happy. + Added some aliases for png_set_expand() in pngrtran.c, namely + png_set_expand_PLTE(), png_set_expand_depth(), and png_set_expand_tRNS() + (Greg Roelofs, in "PNG: The Definitive Guide"). + Added makefile.beo for BEOS on X86, contributed by Sander Stok. +version 1.0.3b [August 26, 1999] + Replaced 2147483647L several places with PNG_MAX_UINT macro, defined in png.h + Changed leading blanks to tabs in all makefiles. + Define PNG_USE_PNGVCRD in makefile.w32, to get MMX assembler code. + Made alternate versions of png_set_expand() in pngrtran.c, namely + png_set_gray_1_2_4_to_8, png_set_palette_to_rgb, and png_set_tRNS_to_alpha + (Greg Roelofs, in "PNG: The Definitive Guide"). Deleted the 1.0.3a aliases. + Relocated start of 'extern "C"' block in png.h so it doesn't include pngconf.h + Revised calculation of num_blocks in pngmem.c to avoid a potentially + negative shift distance, whose results are undefined in the C language. + Added a check in pngset.c to prevent writing multiple tIME chunks. + Added a check in pngwrite.c to detect invalid small window_bits sizes. +version 1.0.3d [September 4, 1999] + Fixed type casting of igamma in pngrutil.c + Added new png_expand functions to scripts/pngdef.pas and pngos2.def + Added a demo read_user_transform_fn that examines the row filters in pngtest.c + +version 1.0.4 [September 24, 1999] + Define PNG_ALWAYS_EXTERN in pngconf.h if __STDC__ is defined + Delete #define PNG_INTERNAL and include "png.h" from pngasmrd.h + Made several minor corrections to pngtest.c + Renamed the makefiles with longer but more user friendly extensions. + Copied the PNG copyright and license to a separate LICENSE file. + Revised documentation, png.h, and example.c to remove reference to + "viewing_gamma" which no longer appears in the PNG specification. + Revised pngvcrd.c to use MMX code for interlacing only on the final pass. + Updated pngvcrd.c to use the faster C filter algorithms from libpng-1.0.1a + Split makefile.win32vc into two versions, makefile.vcawin32 (uses MMX + assembler code) and makefile.vcwin32 (doesn't). + Added a CPU timing report to pngtest.c (enabled by defining PNGTEST_TIMING) + Added a copy of pngnow.png to the distribution. +version 1.0.4a [September 25, 1999] + Increase max_pixel_depth in pngrutil.c if a user transform needs it. + Changed several division operations to right-shifts in pngvcrd.c +version 1.0.4b [September 30, 1999] + Added parentheses in line 3732 of pngvcrd.c + Added a comment in makefile.linux warning about buggy -O3 in pgcc 2.95.1 +version 1.0.4c [October 1, 1999] + Added a "png_check_version" function in png.c and pngtest.c that will generate + a helpful compiler error if an old png.h is found in the search path. + Changed type of png_user_transform_depth|channels from int to png_byte. +version 1.0.4d [October 6, 1999] + Changed 0.45 to 0.45455 in png_set_sRGB() + Removed unused PLTE entries from pngnow.png + Re-enabled some parts of pngvcrd.c (png_combine_row) that work properly. +version 1.0.4e [October 10, 1999] + Fixed sign error in pngvcrd.c (Greg Roelofs) + Replaced some instances of memcpy with simple assignments in pngvcrd (GR-P) +version 1.0.4f [October 15, 1999] + Surrounded example.c code with #if 0 .. #endif to prevent people from + inadvertently trying to compile it. + Changed png_get_header_version() from a function to a macro in png.h + Added type casting mostly in pngrtran.c and pngwtran.c + Removed some pointless "ptr = NULL" in pngmem.c + Added a "contrib" directory containing the source code from Greg's book. + +version 1.0.5 [October 15, 1999] + Minor editing of the INSTALL and README files. +version 1.0.5a [October 23, 1999] + Added contrib/pngsuite and contrib/pngminus (Willem van Schaik) + Fixed a typo in the png_set_sRGB() function call in example.c (Jan Nijtmans) + Further optimization and bugfix of pngvcrd.c + Revised pngset.c so that it does not allocate or free memory in the user's + text_ptr structure. Instead, it makes its own copy. + Created separate write_end_info_struct in pngtest.c for a more severe test. + Added code in pngwrite.c to free info_ptr->text[i].key to stop a memory leak. +version 1.0.5b [November 23, 1999] + Moved PNG_FLAG_HAVE_CHUNK_HEADER, PNG_FLAG_BACKGROUND_IS_GRAY and + PNG_FLAG_WROTE_tIME from flags to mode. + Added png_write_info_before_PLTE() function. + Fixed some typecasting in contrib/gregbook/*.c + Updated scripts/makevms.com and added makevms.com to contrib/gregbook + and contrib/pngminus (Martin Zinser) +version 1.0.5c [November 26, 1999] + Moved png_get_header_version from png.h to png.c, to accomodate ansi2knr. + Removed all global arrays (according to PNG_NO_GLOBAL_ARRAYS macro), to + accomodate making DLL's: Moved usr_png_ver from global variable to function + png_get_header_ver() in png.c. Moved png_sig to png_sig_bytes in png.c and + eliminated use of png_sig in pngwutil.c. Moved the various png_CHNK arrays + into pngtypes.h. Eliminated use of global png_pass arrays. Declared the + png_CHNK and png_pass arrays to be "const". Made the global arrays + available to applications (although none are used in libpng itself) when + PNG_NO_GLOBAL_ARRAYS is not defined or when PNG_GLOBAL_ARRAYS is defined. + Removed some extraneous "-I" from contrib/pngminus/makefile.std + Changed the PNG_sRGB_INTENT macros in png.h to be consistent with PNG-1.2. + Change PNG_SRGB_INTENT to PNG_sRGB_INTENT in libpng.txt and libpng.3 +version 1.0.5d [November 29, 1999] + Add type cast (png_const_charp) two places in png.c + Eliminated pngtypes.h; use macros instead to declare PNG_CHNK arrays. + Renamed "PNG_GLOBAL_ARRAYS" to "PNG_USE_GLOBAL_ARRAYS" and made available + to applications a macro "PNG_USE_LOCAL_ARRAYS". + #ifdef out all the new declarations when PNG_USE_GLOBAL_ARRAYS is defined. + Added PNG_EXPORT_VAR macro to accommodate making DLL's. +version 1.0.5e [November 30, 1999] + Added iCCP, iTXt, and sPLT support; added "lang" member to the png_text + structure; refactored the inflate/deflate support to make adding new chunks + with trailing compressed parts easier in the future, and added new functions + png_free_iCCP, png_free_pCAL, png_free_sPLT, png_free_text, png_get_iCCP, + png_get_spalettes, png_set_iCCP, png_set_spalettes (Eric S. Raymond). + NOTE: Applications that write text chunks MUST define png_text->lang + before calling png_set_text(). It must be set to NULL if you want to + write tEXt or zTXt chunks. If you want your application to be able to + run with older versions of libpng, use + + #ifdef PNG_iTXt_SUPPORTED + png_text[i].lang = NULL; + #endif + + Changed png_get_oFFs() and png_set_oFFs() to use signed rather than unsigned + offsets (Eric S. Raymond). + Combined PNG_READ_cHNK_SUPPORTED and PNG_WRITE_cHNK_SUPPORTED macros into + PNG_cHNK_SUPPORTED and combined the three types of PNG_text_SUPPORTED + macros, leaving the separate macros also available. + Removed comments on #endifs at the end of many short, non-nested #if-blocks. +version 1.0.5f [December 6, 1999] + Changed makefile.solaris to issue a warning about potential problems when + the ucb "ld" is in the path ahead of the ccs "ld". + Removed "- [date]" from the "synopsis" line in libpng.3 and libpngpf.3. + Added sCAL chunk support (Eric S. Raymond). +version 1.0.5g [December 7, 1999] + Fixed "png_free_spallettes" typo in png.h + Added code to handle new chunks in pngpread.c + Moved PNG_CHNK string macro definitions outside of PNG_NO_EXTERN block + Added "translated_key" to png_text structure and png_write_iTXt(). + Added code in pngwrite.c to work around a newly discovered zlib bug. +version 1.0.5h [December 10, 1999] + NOTE: regarding the note for version 1.0.5e, the following must also + be included in your code: + png_text[i].translated_key = NULL; + Unknown chunk handling is now supported. + Option to eliminate all floating point support was added. Some new + fixed-point functions such as png_set_gAMA_fixed() were added. + Expanded tabs and removed trailing blanks in source files. +version 1.0.5i [December 13, 1999] + Added some type casts to silence compiler warnings. + Renamed "png_free_spalette" to "png_free_spalettes" for consistency. + Removed leading blanks from a #define in pngvcrd.c + Added some parameters to the new png_set_keep_unknown_chunks() function. + Added a test for up->location != 0 in the first instance of writing + unknown chunks in pngwrite.c + Changed "num" to "i" in png_free_spalettes() and png_free_unknowns() to + prevent recursion. + Added png_free_hIST() function. + Various patches to fix bugs in the sCAL and integer cHRM processing, + and to add some convenience macros for use with sCAL. +version 1.0.5j [December 21, 1999] + Changed "unit" parameter of png_write_sCAL from png_byte to int, to work + around buggy compilers. + Added new type "png_fixed_point" for integers that hold float*100000 values + Restored backward compatibility of tEXt/zTXt chunk processing: + Restored the first four members of png_text to the same order as v.1.0.5d. + Added members "lang_key" and "itxt_length" to png_text struct. Set + text_length=0 when "text" contains iTXt data. Use the "compression" + member to distinguish among tEXt/zTXt/iTXt types. Added + PNG_ITXT_COMPRESSION_NONE (1) and PNG_ITXT_COMPRESSION_zTXt(2) macros. + The "Note" above, about backward incompatibility of libpng-1.0.5e, no + longer applies. + Fixed png_read|write_iTXt() to read|write parameters in the right order, + and to write the iTXt chunk after IDAT if it appears in the end_ptr. + Added pnggccrd.c, version of pngvcrd.c Intel assembler for gcc (Greg Roelofs) + Reversed the order of trying to write floating-point and fixed-point gAMA. +version 1.0.5k [December 27, 1999] + Added many parentheses, e.g., "if (a && b & c)" becomes "if (a && (b & c))" + Added png_handle_as_unknown() function (Glenn) + Added png_free_chunk_list() function and chunk_list and num_chunk_list members + of png_ptr. + Eliminated erroneous warnings about multiple sPLT chunks and sPLT-after-PLTE. + Fixed a libpng-1.0.5h bug in pngrutil.c that was issuing erroneous warnings + about ignoring incorrect gAMA with sRGB (gAMA was in fact not ignored) + Added png_free_tRNS(); png_set_tRNS() now malloc's its own trans array (ESR). + Define png_get_int_32 when oFFs chunk is supported as well as when pCAL is. + Changed type of proflen from png_int_32 to png_uint_32 in png_get_iCCP(). +version 1.0.5l [January 1, 2000] + Added functions png_set_read_user_chunk_fn() and png_get_user_chunk_ptr() + for setting a callback function to handle unknown chunks and for + retrieving the associated user pointer (Glenn). +version 1.0.5m [January 7, 2000] + Added high-level functions png_read_png(), png_write_png(), png_free_pixels(). +version 1.0.5n [January 9, 2000] + Added png_free_PLTE() function, and modified png_set_PLTE() to malloc its + own memory for info_ptr->palette. This makes it safe for the calling + application to free its copy of the palette any time after it calls + png_set_PLTE(). +version 1.0.5o [January 20, 2000] + Cosmetic changes only (removed some trailing blanks and TABs) +version 1.0.5p [January 31, 2000] + Renamed pngdll.mak to makefile.bd32 + Cosmetic changes in pngtest.c +version 1.0.5q [February 5, 2000] + Relocated the makefile.solaris warning about PATH problems. + Fixed pngvcrd.c bug by pushing/popping registers in mmxsupport (Bruce Oberg) + Revised makefile.gcmmx + Added PNG_SETJMP_SUPPORTED, PNG_SETJMP_NOT_SUPPORTED, and PNG_ABORT() macros +version 1.0.5r [February 7, 2000] + Removed superfluous prototype for png_get_itxt from png.h + Fixed a bug in pngrtran.c that improperly expanded the background color. + Return *num_text=0 from png_get_text() when appropriate, and fix documentation + of png_get_text() in libpng.txt/libpng.3. +version 1.0.5s [February 18, 2000] + Added "png_jmp_env()" macro to pngconf.h, to help people migrate to the + new error handler that's planned for the next libpng release, and changed + example.c, pngtest.c, and contrib programs to use this macro. + Revised some of the DLL-export macros in pngconf.h (Greg Roelofs) + Fixed a bug in png_read_png() that caused it to fail to expand some images + that it should have expanded. + Fixed some mistakes in the unused and undocumented INCH_CONVERSIONS functions + in pngget.c + Changed the allocation of palette, history, and trans arrays back to + the version 1.0.5 method (linking instead of copying) which restores + backward compatibility with version 1.0.5. Added some remarks about + that in example.c. Added "free_me" member to info_ptr and png_ptr + and added png_free_data() function. + Updated makefile.linux and makefile.gccmmx to make directories conditionally. + Made cosmetic changes to pngasmrd.h + Added png_set_rows() and png_get_rows(), for use with png_read|write_png(). + Modified png_read_png() to allocate info_ptr->row_pointers only if it + hasn't already been allocated. +version 1.0.5t [March 4, 2000] + Changed png_jmp_env() migration aiding macro to png_jmpbuf(). + Fixed "interlace" typo (should be "interlaced") in contrib/gregbook/read2-x.c + Fixed bug with use of PNG_BEFORE_IHDR bit in png_ptr->mode, introduced when + PNG_FLAG_HAVE_CHUNK_HEADER was moved into png_ptr->mode in version 1.0.5b + Files in contrib/gregbook were revised to use png_jmpbuf() and to select + a 24-bit visual if one is available, and to allow abbreviated options. + Files in contrib/pngminus were revised to use the png_jmpbuf() macro. + Removed spaces in makefile.linux and makefile.gcmmx, introduced in 1.0.5s +version 1.0.5u [March 5, 2000] + Simplified the code that detects old png.h in png.c and pngtest.c + Renamed png_spalette (_p, _pp) to png_sPLT_t (_tp, _tpp) + Increased precision of rgb_to_gray calculations from 8 to 15 bits and + added png_set_rgb_to_gray_fixed() function. + Added makefile.bc32 (32-bit Borland C++, C mode) +version 1.0.5v [March 11, 2000] + Added some parentheses to the png_jmpbuf macro definition. + Updated references to the zlib home page, which has moved to freesoftware.com. + Corrected bugs in documentation regarding png_read_row() and png_write_row(). + Updated documentation of png_rgb_to_gray calculations in libpng.3/libpng.txt. + Renamed makefile.borland,turboc3 back to makefile.bor,tc3 as in version 1.0.3, + revised borland makefiles; added makefile.ibmvac3 and makefile.gcc (Cosmin) + +version 1.0.6 [March 20, 2000] + Minor revisions of makefile.bor, libpng.txt, and gregbook/rpng2-win.c + Added makefile.sggcc (SGI IRIX with gcc) +version 1.0.6d [April 7, 2000] + Changed sprintf() to strcpy() in png_write_sCAL_s() to work without STDIO + Added data_length parameter to png_decompress_chunk() function + Revised documentation to remove reference to abandoned png_free_chnk functions + Fixed an error in png_rgb_to_gray_fixed() + Revised example.c, usage of png_destroy_write_struct(). + Renamed makefile.ibmvac3 to makefile.ibmc, added libpng.icc IBM project file + Added a check for info_ptr->free_me&PNG_FREE_TEXT when freeing text in png.c + Simplify png_sig_bytes() function to remove use of non-ISO-C strdup(). +version 1.0.6e [April 9, 2000] + Added png_data_freer() function. + In the code that checks for over-length tRNS chunks, added check of + info_ptr->num_trans as well as png_ptr->num_trans (Matthias Benckmann) + Minor revisions of libpng.txt/libpng.3. + Check for existing data and free it if the free_me flag is set, in png_set_*() + and png_handle_*(). + Only define PNG_WEIGHTED_FILTERS_SUPPORTED when PNG_FLOATING_POINT_SUPPORTED + is defined. + Changed several instances of PNG_NO_CONSOLE_ID to PNG_NO_STDIO in pngrutil.c + and mentioned the purposes of the two macros in libpng.txt/libpng.3. +version 1.0.6f [April 14, 2000] + Revised png_set_iCCP() and png_set_rows() to avoid prematurely freeing data. + Add checks in png_set_text() for NULL members of the input text structure. + Revised libpng.txt/libpng.3. + Removed superfluous prototype for png_set_itxt from png.h + Removed "else" from pngread.c, after png_error(), and changed "0" to "length". + Changed several png_errors about malformed ancillary chunks to png_warnings. +version 1.0.6g [April 24, 2000] + Added png_pass-* arrays to pnggccrd.c when PNG_USE_LOCAL_ARRAYS is defined. + Relocated paragraph about png_set_background() in libpng.3/libpng.txt + and other revisions (Matthias Benckmann) + Relocated info_ptr->free_me, png_ptr->free_me, and other info_ptr and + png_ptr members to restore binary compatibility with libpng-1.0.5 + (breaks compatibility with libpng-1.0.6). +version 1.0.6h [April 24, 2000] + Changed shared library so-number pattern from 2.x.y.z to xy.z (this builds + libpng.so.10 & libpng.so.10.6h instead of libpng.so.2 & libpng.so.2.1.0.6h) + This is a temporary change for test purposes. +version 1.0.6i [May 2, 2000] + Rearranged some members at the end of png_info and png_struct, to put + unknown_chunks_num and free_me within the original size of the png_structs + and free_me, png_read_user_fn, and png_free_fn within the original png_info, + because some old applications allocate the structs directly instead of + using png_create_*(). + Added documentation of user memory functions in libpng.txt/libpng.3 + Modified png_read_png so that it will use user_allocated row_pointers + if present, unless free_me directs that it be freed, and added description + of the use of png_set_rows() and png_get_rows() in libpng.txt/libpng.3. + Added PNG_LEGACY_SUPPORTED macro, and #ifdef out all new (since version + 1.00) members of png_struct and png_info, to regain binary compatibility + when you define this macro. Capabilities lost in this event + are user transforms (new in version 1.0.0),the user transform pointer + (new in version 1.0.2), rgb_to_gray (new in 1.0.5), iCCP, sCAL, sPLT, + the high-level interface, and unknown chunks support (all new in 1.0.6). + This was necessary because of old applications that allocate the structs + directly as authors were instructed to do in libpng-0.88 and earlier, + instead of using png_create_*(). + Added modes PNG_CREATED_READ_STRUCT and PNG_CREATED_WRITE_STRUCT which + can be used to detect codes that directly allocate the structs, and + code to check these modes in png_read_init() and png_write_init() and + generate a libpng error if the modes aren't set and PNG_LEGACY_SUPPORTED + was not defined. + Added makefile.intel and updated makefile.watcom (Pawel Mrochen) +version 1.0.6j [May 3, 2000] + Overloaded png_read_init() and png_write_init() with macros that convert + calls to png_read_init_2() or png_write_init_2() that check the version + and structure sizes. +version 1.0.7beta11 [May 7, 2000] + Removed the new PNG_CREATED_READ_STRUCT and PNG_CREATED_WRITE_STRUCT modes + which are no longer used. + Eliminated the three new members of png_text when PNG_LEGACY_SUPPORTED is + defined or when neither PNG_READ_iTXt_SUPPORTED nor PNG_WRITE_iTXT_SUPPORTED + is defined. + Made PNG_NO_READ|WRITE_iTXt the default setting, to avoid memory + overrun when old applications fill the info_ptr->text structure directly. + Added PNGAPI macro, and added it to the definitions of all exported functions. + Relocated version macro definitions ahead of the includes of zlib.h and + pngconf.h in png.h. +version 1.0.7beta12 [May 12, 2000] + Revised pngset.c to avoid a problem with expanding the png_debug macro. + Deleted some extraneous defines from pngconf.h + Made PNG_NO_CONSOLE_IO the default condition when PNG_BUILD_DLL is defined. + Use MSC _RPTn debugging instead of fprintf if _MSC_VER is defined. + Added png_access_version_number() function. + Check for mask&PNG_FREE_CHNK (for TEXT, SCAL, PCAL) in png_free_data(). + Expanded libpng.3/libpng.txt information about png_data_freer(). +version 1.0.7beta14 [May 17, 2000] (beta13 was not published) + Changed pnggccrd.c and pngvcrd.c to handle bad adaptive filter types as + warnings instead of errors, as pngrutil.c does. + Set the PNG_INFO_IDAT valid flag in png_set_rows() so png_write_png() + will actually write IDATs. + Made the default PNG_USE_LOCAL_ARRAYS depend on PNG_DLL instead of WIN32. + Make png_free_data() ignore its final parameter except when freeing data + that can have multiple instances (text, sPLT, unknowns). + Fixed a new bug in png_set_rows(). + Removed info_ptr->valid tests from png_free_data(), as in version 1.0.5. + Added png_set_invalid() function. + Fixed incorrect illustrations of png_destroy_write_struct() in example.c. +version 1.0.7beta15 [May 30, 2000] + Revised the deliberately erroneous Linux setjmp code in pngconf.h to produce + fewer error messages. + Rearranged checks for Z_OK to check the most likely path first in pngpread.c + and pngwutil.c. + Added checks in pngtest.c for png_create_*() returning NULL, and mentioned + in libpng.txt/libpng.3 the need for applications to check this. + Changed names of png_default_*() functions in pngtest to pngtest_*(). + Changed return type of png_get_x|y_offset_*() from png_uint_32 to png_int_32. + Fixed some bugs in the unused PNG_INCH_CONVERSIONS functions in pngget.c + Set each pointer to NULL after freeing it in png_free_data(). + Worked around a problem in pngconf.h; AIX's strings.h defines an "index" + macro that conflicts with libpng's png_color_16.index. (Dimitri Papadapoulos) + Added "msvc" directory with MSVC++ project files (Simon-Pierre Cadieux). +version 1.0.7beta16 [June 4, 2000] + Revised the workaround of AIX string.h "index" bug. + Added a check for overlength PLTE chunk in pngrutil.c. + Added PNG_NO_POINTER_INDEXING macro to use array-indexing instead of pointer + indexing in pngrutil.c and pngwutil.c to accommodate a buggy compiler. + Added a warning in png_decompress_chunk() when it runs out of data, e.g. + when it tries to read an erroneous PhotoShop iCCP chunk. + Added PNG_USE_DLL macro. + Revised the copyright/disclaimer/license notice. + Added contrib/msvctest directory +version 1.0.7rc1 [June 9, 2000] + Corrected the definition of PNG_TRANSFORM_INVERT_ALPHA (0x0400 not 0x0200) + Added contrib/visupng directory (Willem van Schaik) +version 1.0.7beta18 [June 23, 2000] + Revised PNGAPI definition, and pngvcrd.c to work with __GCC__ + and do not redefine PNGAPI if it is passed in via a compiler directive. + Revised visupng/PngFile.c to remove returns from within the Try block. + Removed leading underscores from "_PNG_H" and "_PNG_SAVE_BSD_SOURCE" macros. + Updated contrib/visupng/cexcept.h to version 1.0.0. + Fixed bugs in pngwrite.c and pngwutil.c that prevented writing iCCP chunks. +version 1.0.7rc2 [June 28, 2000] + Updated license to include disclaimers required by UCITA. + Fixed "DJBPP" typo in pnggccrd.c introduced in beta18. + +version 1.0.7 [July 1, 2000] + Revised the definition of "trans_values" in libpng.3/libpng.txt +version 1.0.8beta1 [July 8, 2000] + Added png_free(png_ptr, key) two places in pngpread.c to stop memory leaks. + Changed PNG_NO_STDIO to PNG_NO_CONSOLE_IO, several places in pngrutil.c and + pngwutil.c. + Changed PNG_EXPORT_VAR to use PNG_IMPEXP, in pngconf.h. + Removed unused "#include " from png.c + Added WindowsCE support. + Revised pnggccrd.c to work with gcc-2.95.2 and in the Cygwin environment. +version 1.0.8beta2 [July 10, 2000] + Added project files to the wince directory and made further revisions + of pngtest.c, pngrio.c, and pngwio.c in support of WindowsCE. +version 1.0.8beta3 [July 11, 2000] + Only set the PNG_FLAG_FREE_TRNS or PNG_FREE_TRNS flag in png_handle_tRNS() + for indexed-color input files to avoid potential double-freeing trans array + under some unusual conditions; problem was introduced in version 1.0.6f. + Further revisions to pngtest.c and files in the wince subdirectory. +version 1.0.8beta4 [July 14, 2000] + Added the files pngbar.png and pngbar.jpg to the distribution. + Added makefile.cygwin, and cygwin support in pngconf.h + Added PNG_NO_ZALLOC_ZERO macro (makes png_zalloc skip zeroing memory) +version 1.0.8rc1 [July 16, 2000] + Revised png_debug() macros and statements to eliminate compiler warnings. + +version 1.0.8 [July 24, 2000] + Added png_flush() in pngwrite.c, after png_write_IEND(). + Updated makefile.hpux to build a shared library. +version 1.0.9beta1 [November 10, 2000] + Fixed typo in scripts/makefile.hpux + Updated makevms.com in scripts and contrib/* and contrib/* (Martin Zinser) + Fixed seqence-point bug in contrib/pngminus/png2pnm (Martin Zinser) + Changed "cdrom.com" in documentation to "libpng.org" + Revised pnggccrd.c to get it all working, and updated makefile.gcmmx (Greg). + Changed type of "params" from voidp to png_voidp in png_read|write_png(). + Make sure PNGAPI and PNG_IMPEXP are defined in pngconf.h. + Revised the 3 instances of WRITEFILE in pngtest.c. + Relocated "msvc" and "wince" project subdirectories into "dll" subdirectory. + Updated png.rc in dll/msvc project + Revised makefile.dec to define and use LIBPATH and INCPATH + Increased size of global png_libpng_ver[] array from 12 to 18 chars. + Made global png_libpng_ver[], png_sig[] and png_pass_*[] arrays const. + Removed duplicate png_crc_finish() from png_handle_bKGD() function. + Added a warning when application calls png_read_update_info() multiple times. + Revised makefile.cygwin + Fixed bugs in iCCP support in pngrutil.c and pngwutil.c. + Replaced png_set_empty_plte_permitted() with png_permit_mng_features(). +version 1.0.9beta2 [November 19, 2000] + Renamed the "dll" subdirectory "projects". + Added borland project files to "projects" subdirectory. + Set VS_FF_PRERELEASE and VS_FF_PATCHED flags in msvc/png.rc when appropriate. + Add error message in png_set_compression_buffer_size() when malloc fails. +version 1.0.9beta3 [November 23, 2000] + Revised PNG_LIBPNG_BUILD_TYPE macro in png.h, used in the msvc project. + Removed the png_flush() in pngwrite.c that crashes some applications + that don't set png_output_flush_fn. + Added makefile.macosx and makefile.aix to scripts directory. +version 1.0.9beta4 [December 1, 2000] + Change png_chunk_warning to png_warning in png_check_keyword(). + Increased the first part of msg buffer from 16 to 18 in png_chunk_error(). +version 1.0.9beta5 [December 15, 2000] + Added support for filter method 64 (for PNG datastreams embedded in MNG). +version 1.0.9beta6 [December 18, 2000] + Revised png_set_filter() to accept filter method 64 when appropriate. + Added new PNG_HAVE_PNG_SIGNATURE bit to png_ptr->mode and use it to + help prevent applications from using MNG features in PNG datastreams. + Added png_permit_mng_features() function. + Revised libpng.3/libpng.txt. Changed "filter type" to "filter method". +version 1.0.9rc1 [December 23, 2000] + Revised test for PNG_HAVE_PNG_SIGNATURE in pngrutil.c + Fixed error handling of unknown compression type in png_decompress_chunk(). + In pngconf.h, define __cdecl when _MSC_VER is defined. +version 1.0.9beta7 [December 28, 2000] + Changed PNG_TEXT_COMPRESSION_zTXt to PNG_COMPRESSION_TYPE_BASE several places. + Revised memory management in png_set_hIST and png_handle_hIST in a backward + compatible manner. PLTE and tRNS were revised similarly. + Revised the iCCP chunk reader to ignore trailing garbage. +version 1.0.9beta8 [January 12, 2001] + Moved pngasmrd.h into pngconf.h. + Improved handling of out-of-spec garbage iCCP chunks generated by PhotoShop. +version 1.0.9beta9 [January 15, 2001] + Added png_set_invalid, png_permit_mng_features, and png_mmx_supported to + wince and msvc project module definition files. + Minor revision of makefile.cygwin. + Fixed bug with progressive reading of narrow interlaced images in pngpread.c +version 1.0.9beta10 [January 16, 2001] + Do not typedef png_FILE_p in pngconf.h when PNG_NO_STDIO is defined. + Fixed "png_mmx_supported" typo in project definition files. +version 1.0.9beta11 [January 19, 2001] + Updated makefile.sgi to make shared library. + Removed png_mmx_support() function and disabled PNG_MNG_FEATURES_SUPPORTED + by default, for the benefit of DLL forward compatibility. These will + be re-enabled in version 1.2.0. +version 1.0.9rc2 [January 22, 2001] + Revised cygwin support. + +version 1.0.9 [January 31, 2001] + Added check of cygwin's ALL_STATIC in pngconf.h + Added "-nommx" parameter to contrib/gregbook/rpng2-win and rpng2-x demos. +version 1.0.10beta1 [March 14, 2001] + Revised makefile.dec, makefile.sgi, and makefile.sggcc; added makefile.hpgcc. + Reformatted libpng.3 to eliminate bad line breaks. + Added checks for _mmx_supported in the read_filter_row function of pnggccrd.c + Added prototype for png_mmx_support() near the top of pnggccrd.c + Moved some error checking from png_handle_IHDR to png_set_IHDR. + Added PNG_NO_READ_SUPPORTED and PNG_NO_WRITE_SUPPORTED macros. + Revised png_mmx_support() function in pnggccrd.c + Restored version 1.0.8 PNG_WRITE_EMPTY_PLTE_SUPPORTED behavior in pngwutil.c + Fixed memory leak in contrib/visupng/PngFile.c + Fixed bugs in png_combine_row() in pnggccrd.c and pngvcrd.c (C version) + Added warnings when retrieving or setting gamma=0. + Increased the first part of msg buffer from 16 to 18 in png_chunk_warning(). +version 1.0.10rc1 [March 23, 2001] + Changed all instances of memcpy, strcpy, and strlen to png_memcpy, png_strcpy, + and png_strlen. + Revised png_mmx_supported() function in pnggccrd.c to return proper value. + Fixed bug in progressive reading (pngpread.c) with small images (height < 8). + +version 1.0.10 [March 30, 2001] + Deleted extraneous space (introduced in 1.0.9) from line 42 of makefile.cygwin + Added beos project files (Chris Herborth) +version 1.0.11beta1 [April 3, 2001] + Added type casts on several png_malloc() calls (Dimitri Papadapoulos). + Removed a no-longer needed AIX work-around from pngconf.h + Changed several "//" single-line comments to C-style in pnggccrd.c +version 1.0.11beta2 [April 11, 2001] + Removed PNGAPI from several functions whose prototypes did not have PNGAPI. + Updated scripts/pngos2.def +version 1.0.11beta3 [April 14, 2001] + Added checking the results of many instances of png_malloc() for NULL +version 1.0.11beta4 [April 20, 2001] + Undid the changes from version 1.0.11beta3. Added a check for NULL return + from user's malloc_fn(). + Removed some useless type casts of the NULL pointer. + Added makefile.netbsd + +version 1.0.11 [April 27, 2001] + Revised makefile.netbsd +version 1.0.12beta1 [May 14, 2001] + Test for Windows platform in pngconf.h when including malloc.h (Emmanuel Blot) + Updated makefile.cygwin and handling of Cygwin's ALL_STATIC in pngconf.h + Added some never-to-be-executed code in pnggccrd.c to quiet compiler warnings. + Eliminated the png_error about apps using png_read|write_init(). Instead, + libpng will reallocate the png_struct and info_struct if they are too small. + This retains future binary compatibility for old applications written for + libpng-0.88 and earlier. +version 1.2.0beta1 [May 6, 2001] + Bumped DLLNUM to 2. + Re-enabled PNG_MNG_FEATURES_SUPPORTED and enabled PNG_ASSEMBLER_CODE_SUPPORTED + by default. + Added runtime selection of MMX features. + Added png_set_strip_error_numbers function and related macros. +version 1.2.0beta2 [May 7, 2001] + Finished merging 1.2.0beta1 with version 1.0.11 + Added a check for attempts to read or write PLTE in grayscale PNG datastreams. +version 1.2.0beta3 [May 17, 2001] + Enabled user memory function by default. + Modified png_create_struct so it passes user mem_ptr to user memory allocator. + Increased png_mng_features flag from png_byte to png_uint_32. + Bumped shared-library (so-number) and dll-number to 3. +version 1.2.0beta4 [June 23, 2001] + Check for missing profile length field in iCCP chunk and free chunk_data + in case of truncated iCCP chunk. + Bumped shared-library number to 3 in makefile.sgi and makefile.sggcc + Bumped dll-number from 2 to 3 in makefile.cygwin + Revised contrib/gregbook/rpng*-x.c to avoid a memory leak and to exit cleanly + if user attempts to run it on an 8-bit display. + Updated contrib/gregbook + Use png_malloc instead of png_zalloc to allocate palette in pngset.c + Updated makefile.ibmc + Added some typecasts to eliminate gcc 3.0 warnings. Changed prototypes + of png_write_oFFS width and height from png_uint_32 to png_int_32. + Updated example.c + Revised prototypes for png_debug_malloc and png_debug_free in pngtest.c +version 1.2.0beta5 [August 8, 2001] + Revised contrib/gregbook + Revised makefile.gcmmx + Revised pnggccrd.c to conditionally compile some thread-unsafe code only + when PNG_THREAD_UNSAFE_OK is defined. + Added tests to prevent pngwutil.c from writing a bKGD or tRNS chunk with + value exceeding 2^bit_depth-1 + Revised makefile.sgi and makefile.sggcc + Replaced calls to fprintf(stderr,...) with png_warning() in pnggccrd.c + Removed restriction that do_invert_mono only operate on 1-bit opaque files + +version 1.2.0 [September 1, 2001] + Changed a png_warning() to png_debug() in pnggccrd.c + Fixed contrib/gregbook/rpng-x.c, rpng2-x.c to avoid crash with XFreeGC(). +version 1.2.1beta1 [October 19, 2001] + Revised makefile.std in contrib/pngminus + Include background_1 in png_struct regardless of gamma support. + Revised makefile.netbsd and makefile.macosx, added makefile.darwin. + Revised example.c to provide more details about using row_callback(). +version 1.2.1beta2 [October 25, 2001] + Added type cast to each NULL appearing in a function call, except for + WINCE functions. + Added makefile.so9. +version 1.2.1beta3 [October 27, 2001] + Removed type casts from all NULLs. + Simplified png_create_struct_2(). +version 1.2.1beta4 [November 7, 2001] + Revised png_create_info_struct() and png_creat_struct_2(). + Added error message if png_write_info() was omitted. + Type cast NULLs appearing in function calls when _NO_PROTO or + PNG_TYPECAST_NULL is defined. +version 1.2.1rc1 [November 24, 2001] + Type cast NULLs appearing in function calls except when PNG_NO_TYPECAST_NULL + is defined. + Changed typecast of "size" argument to png_size_t in pngmem.c calls to + the user malloc_fn, to agree with the prototype in png.h + Added a pop/push operation to pnggccrd.c, to preserve Eflag (Maxim Sobolev) + Updated makefile.sgi to recognize LIBPATH and INCPATH. + Updated various makefiles so "make clean" does not remove previous major + version of the shared library. +version 1.2.1rc2 [December 4, 2001] + Always allocate 256-entry internal palette, hist, and trans arrays, to + avoid out-of-bounds memory reference caused by invalid PNG datastreams. + Added a check for prefix_length > data_length in iCCP chunk handler. + +version 1.2.1 [December 7, 2001] + None. +version 1.2.2beta1 [February 22, 2002] + Fixed a bug with reading the length of iCCP profiles (Larry Reeves). + Revised makefile.linux, makefile.gcmmx, and makefile.sgi to generate + libpng.a, libpng12.so (not libpng.so.3), and libpng12/png.h + Revised makefile.darwin to remove "-undefined suppress" option. + Added checks for gamma and chromaticity values over 21474.83, which exceed + the limit for PNG unsigned 32-bit integers when encoded. + Revised calls to png_create_read_struct() and png_create_write_struct() + for simpler debugging. + Revised png_zalloc() so zlib handles errors (uses PNG_FLAG_MALLOC_NULL_MEM_OK) +version 1.2.2beta2 [February 23, 2002] + Check chunk_length and idat_size for invalid (over PNG_MAX_UINT) lengths. + Check for invalid image dimensions in png_get_IHDR. + Added missing "fi;" in the install target of the SGI makefiles. + Added install-static to all makefiles that make shared libraries. + Always do gamma compensation when image is partially transparent. +version 1.2.2beta3 [March 7, 2002] + Compute background.gray and background_1.gray even when color_type is RGB + in case image gets reduced to gray later. + Modified shared-library makefiles to install pkgconfig/libpngNN.pc. + Export (with PNGAPI) png_zalloc, png_zfree, and png_handle_as_unknown + Removed unused png_write_destroy_info prototype from png.h + Eliminated incorrect use of width_mmx from pnggccrd.c in pixel_bytes == 8 case + Added install-shared target to all makefiles that make shared libraries. + Stopped a double free of palette, hist, and trans when not using free_me. + Added makefile.32sunu for Sun Ultra 32 and makefile.64sunu for Sun Ultra 64. +version 1.2.2beta4 [March 8, 2002] + Compute background.gray and background_1.gray even when color_type is RGB + in case image gets reduced to gray later (Jason Summers). + Relocated a misplaced /bin/rm in the "install-shared" makefile targets + Added PNG_1_0_X macro which can be used to build a 1.0.x-compatible library. +version 1.2.2beta5 [March 26, 2002] + Added missing PNGAPI to several function definitions. + Check for invalid bit_depth or color_type in png_get_IHDR(), and + check for missing PLTE or IHDR in png_push_read_chunk() (Matthias Clasen). + Revised iTXt support to accept NULL for lang and lang_key. + Compute gamma for color components of background even when color_type is gray. + Changed "()" to "{}" in scripts/libpng.pc.in. + Revised makefiles to put png.h and pngconf.h only in $prefix/include/libpngNN + Revised makefiles to make symlink to libpng.so.NN in addition to libpngNN.so +version 1.2.2beta6 [March 31, 2002] +version 1.0.13beta1 [March 31, 2002] + Prevent png_zalloc() from trying to memset memory that it failed to acquire. + Add typecasts of PNG_MAX_UINT in pngset_cHRM_fixed() (Matt Holgate). + Ensure that the right function (user or default) is used to free the + png_struct after an error in png_create_read_struct_2(). +version 1.2.2rc1 [April 7, 2002] +version 1.0.13rc1 [April 7, 2002] + Save the ebx register in pnggccrd.c (Sami Farin) + Add "mem_ptr = png_ptr->mem_ptr" in png_destroy_write_struct() (Paul Gardner). + Updated makefiles to put headers in include/libpng and remove old include/*.h. + +version 1.2.2 [April 15, 2002] +version 1.0.13 [April 15, 2002] + Revised description of png_set_filter() in libpng.3/libpng.txt. + Revised makefile.netbsd and added makefile.neNNbsd and makefile.freebsd +version 1.0.13patch01 [April 17, 2002] +version 1.2.2patch01 [April 17, 2002] + Changed ${PNGMAJ}.${PNGVER} bug to ${PNGVER} in makefile.sgi and makefile.sggcc + Fixed VER -> PNGVER typo in makefile.macosx and added install-static to install + Added install: target to makefile.32sunu and makefile.64sunu +version 1.0.13patch03 [April 18, 2002] +version 1.2.2patch03 [April 18, 2002] + Revised 15 makefiles to link libpng.a to libpngNN.a and the include libpng + subdirectory to libpngNN subdirectory without the full pathname. + Moved generation of libpng.pc from "install" to "all" in 15 makefiles. +version 1.2.3rc1 [April 28, 2002] + Added install-man target to 15 makefiles (Dimitri Papadopolous-Orfanos). + Added $(DESTDIR) feature to 24 makefiles (Tim Mooney) + Fixed bug with $prefix, should be $(prefix) in makefile.hpux. + Updated cygwin-specific portion of pngconf.h and revised makefile.cygwin + Added a link from libpngNN.pc to libpng.pc in 15 makefiles. + Added links from include/libpngNN/*.h to include/*.h in 24 makefiles. + Revised makefile.darwin to make relative links without full pathname. + Added setjmp() at the end of png_create_*_struct_2() in case user forgets + to put one in their application. + Restored png_zalloc() and png_zfree() prototypes to version 1.2.1 and + removed them from module definition files. +version 1.2.3rc2 [May 1, 2002] + Fixed bug in reporting number of channels in pngget.c and pngset.c, + that was introduced in version 1.2.2beta5. + Exported png_zalloc(), png_zfree(), png_default_read(), png_default_write(), + png_default_flush(), and png_push_fill_buffer() and included them in + module definition files. + Added "libpng.pc" dependency to the "install-shared" target in 15 makefiles. +version 1.2.3rc3 [May 1, 2002] + Revised prototype for png_default_flush() + Remove old libpng.pc and libpngNN.pc before installing new ones. +version 1.2.3rc4 [May 2, 2002] + Typos in *.def files (png_default_read|write -> png_default_read|write_data) + In makefiles, changed rm libpng.NN.pc to rm libpngNN.pc + Added libpng-config and libpngNN-config and modified makefiles to install them. + Changed $(MANPATH) to $(DESTDIR)$(MANPATH) in makefiles + Added "Win32 DLL VB" configuration to projects/msvc/libpng.dsp +version 1.2.3rc5 [May 11, 2002] + Changed "error" and "message" in prototypes to "error_message" and + "warning_message" to avoid namespace conflict. + Revised 15 makefiles to build libpng-config from libpng-config-*.in + Once more restored png_zalloc and png_zfree to regular nonexported form. + Restored png_default_read|write_data, png_default_flush, png_read_fill_buffer + to nonexported form, but with PNGAPI, and removed them from module def files. +version 1.2.3rc6 [May 14, 2002] + Removed "PNGAPI" from png_zalloc() and png_zfree() in png.c + Changed "Gz" to "Gd" in projects/msvc/libpng.dsp and zlib.dsp. + Removed leftover libpng-config "sed" script from four makefiles. + Revised libpng-config creating script in 16 makefiles. + +version 1.2.3 [May 22, 2002] + Revised libpng-config target in makefile.cygwin. + Removed description of png_set_mem_fn() from documentation. + Revised makefile.freebsd. + Minor cosmetic changes to 15 makefiles, e.g., $(DI) = $(DESTDIR)/$(INCDIR). + Revised projects/msvc/README.txt + Changed -lpng to -lpngNN in LDFLAGS in several makefiles. +version 1.2.4beta1 [May 24, 2002] + Added libpng.pc and libpng-config to "all:" target in 16 makefiles. + Fixed bug in 16 makefiles: $(DESTDIR)/$(LIBPATH) to $(DESTDIR)$(LIBPATH) + Added missing "\" before closing double quote in makefile.gcmmx. + Plugged various memory leaks; added png_malloc_warn() and png_set_text_2() + functions. +version 1.2.4beta2 [June 25, 2002] + Plugged memory leak of png_ptr->current_text (Matt Holgate). + Check for buffer overflow before reading CRC in pngpread.c (Warwick Allison) + Added -soname to the loader flags in makefile.dec, makefile.sgi, and + makefile.sggcc. + Added "test-installed" target to makefile.linux, makefile.gcmmx, + makefile.sgi, and makefile.sggcc. +version 1.2.4beta3 [June 28, 2002] + Plugged memory leak of row_buf in pngtest.c when there is a png_error(). + Detect buffer overflow in pngpread.c when IDAT is corrupted with extra data. + Added "test-installed" target to makefile.32sunu, makefile.64sunu, + makefile.beos, makefile.darwin, makefile.dec, makefile.macosx, + makefile.solaris, makefile.hpux, makefile.hpgcc, and makefile.so9. +version 1.2.4rc1 and 1.0.14rc1 [July 2, 2002] + Added "test-installed" target to makefile.cygwin and makefile.sco. + Revised pnggccrd.c to be able to back out version 1.0.x via PNG_1_0_X macro. + +version 1.2.4 and 1.0.14 [July 8, 2002] + Changed png_warning() to png_error() when width is too large to process. +version 1.2.4patch01 [July 20, 2002] + Revised makefile.cygwin to use DLL number 12 instead of 13. +version 1.2.5beta1 [August 6, 2002] + Added code to contrib/gregbook/readpng2.c to ignore unused chunks. + Replaced toucan.png in contrib/gregbook (it has been corrupt since 1.0.11) + Removed some stray *.o files from contrib/gregbook. + Changed png_error() to png_warning() about "Too much data" in pngpread.c + and about "Extra compressed data" in pngrutil.c. + Prevent png_ptr->pass from exceeding 7 in png_push_finish_row(). + Updated makefile.hpgcc + Updated png.c and pnggccrd.c handling of return from png_mmx_support() +version 1.2.5beta2 [August 15, 2002] + Only issue png_warning() about "Too much data" in pngpread.c when avail_in + is nonzero. + Updated makefiles to install a separate libpng.so.3 with its own rpath. +version 1.2.5rc1 and 1.0.15rc1 [August 24, 2002] + Revised makefiles to not remove previous minor versions of shared libraries. +version 1.2.5rc2 and 1.0.15rc2 [September 16, 2002] + Revised 13 makefiles to remove "-lz" and "-L$(ZLIBLIB)", etc., from shared + library loader directive. + Added missing "$OBJSDLL" line to makefile.gcmmx. + Added missing "; fi" to makefile.32sunu. +version 1.2.5rc3 and 1.0.15rc3 [September 18, 2002] + Revised libpng-config script. + +version 1.2.5 and 1.0.15 [October 3, 2002] + Revised makefile.macosx, makefile.darwin, makefile.hpgcc, and makefile.hpux, + and makefile.aix. + Relocated two misplaced PNGAPI lines in pngtest.c +version 1.2.6beta1 [October 22, 2002] + Commented out warning about uninitialized mmx_support in pnggccrd.c. + Changed "IBMCPP__" flag to "__IBMCPP__" in pngconf.h. + Relocated two more misplaced PNGAPI lines in pngtest.c + Fixed memory overrun bug in png_do_read_filler() with 16-bit datastreams, + introduced in version 1.0.2. + Revised makefile.macosx, makefile.dec, makefile.aix, and makefile.32sunu. +version 1.2.6beta2 [November 1, 2002] + Added libpng-config "--ldopts" output. + Added "AR=ar" and "ARFLAGS=rc" and changed "ar rc" to "$(AR) $(ARFLAGS)" + in makefiles. +version 1.2.6beta3 [July 18, 2004] + Reverted makefile changes from version 1.2.6beta2 and some of the changes + from version 1.2.6beta1; these will be postponed until version 1.2.7. + Version 1.2.6 is going to be a simple bugfix release. + Changed the one instance of "ln -sf" to "ln -f -s" in each Sun makefile. + Fixed potential overrun in pngerror.c by using strncpy instead of memcpy. + Added "#!/bin/sh" at the top of configure, for recognition of the + 'x' flag under Cygwin (Cosmin). + Optimized vacuous tests that silence compiler warnings, in png.c (Cosmin). + Added support for PNG_USER_CONFIG, in pngconf.h (Cosmin). + Fixed the special memory handler for Borland C under DOS, in pngmem.c + (Cosmin). + Removed some spurious assignments in pngrutil.c (Cosmin). + Replaced 65536 with 65536L, and 0xffff with 0xffffL, to silence warnings + on 16-bit platforms (Cosmin). + Enclosed shift op expressions in parentheses, to silence warnings (Cosmin). + Used proper type png_fixed_point, to avoid problems on 16-bit platforms, + in png_handle_sRGB() (Cosmin). + Added compression_type to png_struct, and optimized the window size + inside the deflate stream (Cosmin). + Fixed definition of isnonalpha(), in pngerror.c and pngrutil.c (Cosmin). + Fixed handling of unknown chunks that come after IDAT (Cosmin). + Allowed png_error() and png_warning() to work even if png_ptr == NULL + (Cosmin). + Replaced row_info->rowbytes with row_bytes in png_write_find_filter() + (Cosmin). + Fixed definition of PNG_LIBPNG_VER_DLLNUM (Simon-Pierre). + Used PNG_LIBPNG_VER and PNG_LIBPNG_VER_STRING instead of the hardcoded + values in png.c (Simon-Pierre, Cosmin). + Initialized png_libpng_ver[] with PNG_LIBPNG_VER_STRING (Simon-Pierre). + Replaced PNG_LIBPNG_VER_MAJOR with PNG_LIBPNG_VER_DLLNUM in png.rc + (Simon-Pierre). + Moved the definition of PNG_HEADER_VERSION_STRING near the definitions + of the other PNG_LIBPNG_VER_... symbols in png.h (Cosmin). + Relocated #ifndef PNGAPI guards in pngconf.h (Simon-Pierre, Cosmin). + Updated scripts/makefile.vc(a)win32 (Cosmin). + Updated the MSVC project (Simon-Pierre, Cosmin). + Updated the Borland C++ Builder project (Cosmin). + Avoided access to asm_flags in pngvcrd.c, if PNG_1_0_X is defined (Cosmin). + Commented out warning about uninitialized mmx_support in pngvcrd.c (Cosmin). + Removed scripts/makefile.bd32 and scripts/pngdef.pas (Cosmin). + Added extra guard around inclusion of Turbo C memory headers, in pngconf.h + (Cosmin). + Renamed projects/msvc/ to projects/visualc6/, and projects/borland/ to + projects/cbuilder5/ (Cosmin). + Moved projects/visualc6/png32ms.def to scripts/pngw32.def, + and projects/visualc6/png.rc to scripts/pngw32.rc (Cosmin). + Added projects/visualc6/pngtest.dsp; removed contrib/msvctest/ (Cosmin). + Changed line endings to DOS style in cbuilder5 and visualc6 files, even + in the tar.* distributions (Cosmin). + Updated contrib/visupng/VisualPng.dsp (Cosmin). + Updated contrib/visupng/cexcept.h to version 2.0.0 (Cosmin). + Added a separate distribution with "configure" and supporting files (Junichi). +version 1.2.6beta4 [July 28, 2004] + Added user ability to change png_size_t via a PNG_SIZE_T macro. + Added png_sizeof() and png_convert_size() functions. + Added PNG_SIZE_MAX (maximum value of a png_size_t variable. + Added check in png_malloc_default() for (size_t)size != (png_uint_32)size + which would indicate an overflow. + Changed sPLT failure action from png_error to png_warning and abandon chunk. + Changed sCAL and iCCP failures from png_error to png_warning and abandon. + Added png_get_uint_31(png_ptr, buf) function. + Added PNG_UINT_32_MAX macro. + Renamed PNG_MAX_UINT to PNG_UINT_31_MAX. + Made png_zalloc() issue a png_warning and return NULL on potential + overflow. + Turn on PNG_NO_ZALLOC_ZERO by default in version 1.2.x + Revised "clobber list" in pnggccrd.c so it will compile under gcc-3.4. + Revised Borland portion of png_malloc() to return NULL or issue + png_error() according to setting of PNG_FLAG_MALLOC_NULL_MEM_OK. + Added PNG_NO_SEQUENTIAL_READ_SUPPORTED macro to conditionally remove + sequential read support. + Added some "#if PNG_WRITE_SUPPORTED" blocks. + #ifdef'ed out some redundancy in png_malloc_default(). + Use png_malloc instead of png_zalloc to allocate the pallete. +version 1.0.16rc1 and 1.2.6rc1 [August 4, 2004] + Fixed buffer overflow vulnerability in png_handle_tRNS() + Fixed integer arithmetic overflow vulnerability in png_read_png(). + Fixed some harmless bugs in png_handle_sBIT, etc, that would cause + duplicate chunk types to go undetected. + Fixed some timestamps in the -config version + Rearranged order of processing of color types in png_handle_tRNS(). + Added ROWBYTES macro to calculate rowbytes without integer overflow. + Updated makefile.darwin and removed makefile.macosx from scripts directory. + Imposed default one million column, one-million row limits on the image + dimensions, and added png_set_user_limits() function to override them. + Revised use of PNG_SET_USER_LIMITS_SUPPORTED macro. + Fixed wrong cast of returns from png_get_user_width|height_max(). + Changed some "keep the compiler happy" from empty statements to returns, + Revised libpng.txt to remove 1.2.x stuff from the 1.0.x distribution +version 1.0.16rc2 and 1.2.6rc2 [August 7, 2004] + Revised makefile.darwin and makefile.solaris. Removed makefile.macosx. + Revised pngtest's png_debug_malloc() to use png_malloc() instead of + png_malloc_default() which is not supposed to be exported. + Fixed off-by-one error in one of the conversions to PNG_ROWBYTES() in + pngpread.c. Bug was introduced in 1.2.6rc1. + Fixed bug in RGB to RGBX transformation introduced in 1.2.6rc1. + Fixed old bug in RGB to Gray transformation. + Fixed problem with 64-bit compilers by casting arguments to abs() + to png_int_32. + Changed "ln -sf" to "ln -f -s" in three makefiles (solaris, sco, so9). + Changed "HANDLE_CHUNK_*" to "PNG_HANDLE_CHUNK_*" (Cosmin) + Added "-@/bin/rm -f $(DL)/$(LIBNAME).so.$(PNGMAJ)" to 15 *NIX makefiles. + Added code to update the row_info->colortype in png_do_read_filler() (MSB). +version 1.0.16rc3 and 1.2.6rc3 [August 9, 2004] + Eliminated use of "abs()" in testing cHRM and gAMA values, to avoid + trouble with some 64-bit compilers. Created PNG_OUT_OF_RANGE() macro. + Revised documentation of png_set_keep_unknown_chunks(). + Check handle_as_unknown status in pngpread.c, as in pngread.c previously. + Moved "PNG_HANDLE_CHUNK_*" macros out of PNG_INTERNAL section of png.h + Added "rim" definitions for CONST4 and CONST6 in pnggccrd.c +version 1.0.16rc4 and 1.2.6rc4 [August 10, 2004] + Fixed mistake in pngtest.c introduced in 1.2.6rc2 (declaration of + "pinfo" was out of place). +version 1.0.16rc5 and 1.2.6rc5 [August 10, 2004] + Moved "PNG_HANDLE_CHUNK_*" macros out of PNG_ASSEMBLER_CODE_SUPPORTED + section of png.h where they were inadvertently placed in version rc3. + +version 1.0.16 and 1.2.6 [August 15, 2004] + Revised pngtest so memory allocation testing is only done when PNG_DEBUG==1. +version 1.2.7beta1 [August 26, 2004] + Removed unused pngasmrd.h file. + Removed references to uu.net for archived files. Added references to + PNG Spec (second edition) and the PNG ISO/IEC Standard. + Added "test-dd" target in 15 makefiles, to run pngtest in DESTDIR. + Fixed bug with "optimized window size" in the IDAT datastream, that + causes libpng to write PNG files with incorrect zlib header bytes. +version 1.2.7beta2 [August 28, 2004] + Fixed bug with sCAL chunk and big-endian machines (David Munro). + Undid new code added in 1.2.6rc2 to update the color_type in + png_set_filler(). + Added png_set_add_alpha() that updates color type. +version 1.0.17rc1 and 1.2.7rc1 [September 4, 2004] + Revised png_set_strip_filler() to not remove alpha if color_type has alpha. +version 1.0.17 and 1.2.7 [September 12, 2004] + Added makefile.hp64 + Changed projects/msvc/png32ms.def to scripts/png32ms.def in makefile.cygwin +version 1.2.8beta1 [November 1, 2004] + Fixed bug in png_text_compress() that would fail to complete a large block. + Fixed bug, introduced in libpng-1.2.7, that overruns a buffer during + strip alpha operation in png_do_strip_filler(). + Added PNG_1_2_X definition in pngconf.h + #ifdef out png_info_init in png.c and png_read_init in pngread.c (as of 1.3.0) +version 1.2.8beta2 [November 2, 2004] + Reduce color_type to a nonalpha type after strip alpha operation in + png_do_strip_filler(). +version 1.2.8beta3 [November 3, 2004] + Revised definitions of PNG_MAX_UINT_32, PNG_MAX_SIZE, and PNG_MAXSUM +version 1.2.8beta4 [November 12, 2004] + Fixed (again) definition of PNG_LIBPNG_VER_DLLNUM in png.h (Cosmin). + Added PNG_LIBPNG_BUILD_PRIVATE in png.h (Cosmin). + Set png_ptr->zstream.data_type to Z_BINARY, to avoid unnecessary detection + of data type in deflate (Cosmin). + Deprecated but continue to support SPECIALBUILD and PRIVATEBUILD in favor of + PNG_LIBPNG_BUILD_SPECIAL_STRING and PNG_LIBPNG_BUILD_PRIVATE_STRING. +version 1.2.8beta5 [November 20, 2004] + Use png_ptr->flags instead of png_ptr->transformations to pass + PNG_STRIP_ALPHA info to png_do_strip_filler(), to preserve ABI + compatibility. + Revised handling of SPECIALBUILD, PRIVATEBUILD, + PNG_LIBPNG_BUILD_SPECIAL_STRING and PNG_LIBPNG_BUILD_PRIVATE_STRING. +version 1.2.8rc1 [November 24, 2004] + Moved handling of BUILD macros from pngconf.h to png.h + Added definition of PNG_LIBPNG_BASE_TYPE in png.h, inadvertently + omitted from beta5. + Revised scripts/pngw32.rc + Despammed mailing addresses by masking "@" with "at". + Inadvertently installed a supposedly faster test version of pngrutil.c +version 1.2.8rc2 [November 26, 2004] + Added two missing "\" in png.h + Change tests in pngread.c and pngpread.c to + if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA)) + png_do_read_transformations(png_ptr); +version 1.2.8rc3 [November 28, 2004] + Reverted pngrutil.c to version libpng-1.2.8beta5. + Added scripts/makefile.elf with supporting code in pngconf.h for symbol + versioning (John Bowler). +version 1.2.8rc4 [November 29, 2004] + Added projects/visualc7 (Simon-pierre). +version 1.2.8rc5 [November 29, 2004] + Fixed new typo in scripts/pngw32.rc +version 1.2.8 [December 3, 2004] + Removed projects/visualc7, added projects/visualc71. + +Send comments/corrections/commendations to +png-implement at ccrc.wustl.edu (subscription required; write to +majordomo at ccrc.wustl.edu with "subscribe png-implement" in the message) +or to glennrp at users.sourceforge.net + +Glenn R-P diff --git a/com32/lib/libpng/KNOWNBUG b/com32/lib/libpng/KNOWNBUG new file mode 100644 index 0000000..48147bb --- /dev/null +++ b/com32/lib/libpng/KNOWNBUG @@ -0,0 +1,10 @@ + +Known bugs in libpng version 1.2.8 + +1. April 22, 2001: pnggccrd.c has been reported to crash on NetBSD when + reading interlaced PNG files, when assembler code is enabled but running + on a non-MMX i386 platform. + + STATUS: Under investigation. The change to pnggccrd.c in libpng-1.2.1 + fixed a problem under FreeBSD but not the problem with NetBSD, which + still fails as of libpng-1.2.2rc1. diff --git a/com32/lib/libpng/LICENSE b/com32/lib/libpng/LICENSE new file mode 100644 index 0000000..7ef3667 --- /dev/null +++ b/com32/lib/libpng/LICENSE @@ -0,0 +1,113 @@ + +This copy of the libpng notices is provided for your convenience. In case of +any discrepancy between this copy and the notices in the file png.h that is +included in the libpng distribution, the latter shall prevail. + +COPYRIGHT NOTICE, DISCLAIMER, and LICENSE: + +If you modify libpng you may insert additional notices immediately following +this sentence. + +*** This version of libpng has been slightly modified to fit into the + libcom32 framework. In particular, it uses the libcom32 Makefile + system instead of its own. + +libpng version 1.2.6, December 3, 2004, is +Copyright (c) 2004 Glenn Randers-Pehrson, and is +distributed according to the same disclaimer and license as libpng-1.2.5 +with the following individual added to the list of Contributing Authors + + Cosmin Truta + +libpng versions 1.0.7, July 1, 2000, through 1.2.5 - October 3, 2002, are +Copyright (c) 2000-2002 Glenn Randers-Pehrson, and are +distributed according to the same disclaimer and license as libpng-1.0.6 +with the following individuals added to the list of Contributing Authors + + Simon-Pierre Cadieux + Eric S. Raymond + Gilles Vollant + +and with the following additions to the disclaimer: + + There is no warranty against interference with your enjoyment of the + library or against infringement. There is no warranty that our + efforts or the library will fulfill any of your particular purposes + or needs. This library is provided with all faults, and the entire + risk of satisfactory quality, performance, accuracy, and effort is with + the user. + +libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are +Copyright (c) 1998, 1999 Glenn Randers-Pehrson, and are +distributed according to the same disclaimer and license as libpng-0.96, +with the following individuals added to the list of Contributing Authors: + + Tom Lane + Glenn Randers-Pehrson + Willem van Schaik + +libpng versions 0.89, June 1996, through 0.96, May 1997, are +Copyright (c) 1996, 1997 Andreas Dilger +Distributed according to the same disclaimer and license as libpng-0.88, +with the following individuals added to the list of Contributing Authors: + + John Bowler + Kevin Bracey + Sam Bushell + Magnus Holmgren + Greg Roelofs + Tom Tanner + +libpng versions 0.5, May 1995, through 0.88, January 1996, are +Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc. + +For the purposes of this copyright and license, "Contributing Authors" +is defined as the following set of individuals: + + Andreas Dilger + Dave Martindale + Guy Eric Schalnat + Paul Schmidt + Tim Wegner + +The PNG Reference Library is supplied "AS IS". The Contributing Authors +and Group 42, Inc. disclaim all warranties, expressed or implied, +including, without limitation, the warranties of merchantability and of +fitness for any purpose. The Contributing Authors and Group 42, Inc. +assume no liability for direct, indirect, incidental, special, exemplary, +or consequential damages, which may result from the use of the PNG +Reference Library, even if advised of the possibility of such damage. + +Permission is hereby granted to use, copy, modify, and distribute this +source code, or portions hereof, for any purpose, without fee, subject +to the following restrictions: + +1. The origin of this source code must not be misrepresented. + +2. Altered versions must be plainly marked as such and must not + be misrepresented as being the original source. + +3. This Copyright notice may not be removed or altered from any + source or altered source distribution. + +The Contributing Authors and Group 42, Inc. specifically permit, without +fee, and encourage the use of this source code as a component to +supporting the PNG file format in commercial products. If you use this +source code in a product, acknowledgment is not required but would be +appreciated. + + +A "png_get_copyright" function is available, for convenient use in "about" +boxes and the like: + + printf("%s",png_get_copyright(NULL)); + +Also, the PNG logo (in PNG format, of course) is supplied in the +files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). + +Libpng is OSI Certified Open Source Software. OSI Certified Open Source is a +certification mark of the Open Source Initiative. + +Glenn Randers-Pehrson +glennrp at users.sourceforge.net +December 3, 2004 diff --git a/com32/lib/libpng/README b/com32/lib/libpng/README new file mode 100644 index 0000000..06f9fe4 --- /dev/null +++ b/com32/lib/libpng/README @@ -0,0 +1,262 @@ +README for libpng version 1.2.8 - December 3, 2004 (shared library 12.0) +See the note about version numbers near the top of png.h + +See INSTALL for instructions on how to install libpng. + +Libpng comes in several distribution formats. Get libpng-*.tar.gz +or libpng-*.tar.bz2 if you want UNIX-style line endings in the text +files, or lpng*.zip if you want DOS-style line endings. + +Version 0.89 was the first official release of libpng. Don't let the +fact that it's the first release fool you. The libpng library has been in +extensive use and testing since mid-1995. By late 1997 it had +finally gotten to the stage where there hadn't been significant +changes to the API in some time, and people have a bad feeling about +libraries with versions < 1.0. Version 1.0.0 was released in +March 1998. + +**** +Note that some of the changes to the png_info structure render this +version of the library binary incompatible with libpng-0.89 or +earlier versions if you are using a shared library. The type of the +"filler" parameter for png_set_filler() has changed from png_byte to +png_uint_32, which will affect shared-library applications that use +this function. + +To avoid problems with changes to the internals of png_info_struct, +new APIs have been made available in 0.95 to avoid direct application +access to info_ptr. These functions are the png_set_ and +png_get_ functions. These functions should be used when +accessing/storing the info_struct data, rather than manipulating it +directly, to avoid such problems in the future. + +It is important to note that the APIs do not make current programs +that access the info struct directly incompatible with the new +library. However, it is strongly suggested that new programs use +the new APIs (as shown in example.c and pngtest.c), and older programs +be converted to the new format, to facilitate upgrades in the future. +**** + +Additions since 0.90 include the ability to compile libpng as a +Windows DLL, and new APIs for accessing data in the info struct. +Experimental functions include the ability to set weighting and cost +factors for row filter selection, direct reads of integers from buffers +on big-endian processors that support misaligned data access, faster +methods of doing alpha composition, and more accurate 16->8 bit color +conversion. + +The additions since 0.89 include the ability to read from a PNG stream +which has had some (or all) of the signature bytes read by the calling +application. This also allows the reading of embedded PNG streams that +do not have the PNG file signature. As well, it is now possible to set +the library action on the detection of chunk CRC errors. It is possible +to set different actions based on whether the CRC error occurred in a +critical or an ancillary chunk. + +The changes made to the library, and bugs fixed are based on discussions +on the PNG-implement mailing list +and not on material submitted privately to Guy, Andreas, or Glenn. They will +forward any good suggestions to the list. + +For a detailed description on using libpng, read libpng.txt. For +examples of libpng in a program, see example.c and pngtest.c. For usage +information and restrictions (what little they are) on libpng, see +png.h. For a description on using zlib (the compression library used by +libpng) and zlib's restrictions, see zlib.h + +I have included a general makefile, as well as several machine and +compiler specific ones, but you may have to modify one for your own needs. + +You should use zlib 1.0.4 or later to run this, but it MAY work with +versions as old as zlib 0.95. Even so, there are bugs in older zlib +versions which can cause the output of invalid compression streams for +some images. You will definitely need zlib 1.0.4 or later if you are +taking advantage of the MS-DOS "far" structure allocation for the small +and medium memory models. You should also note that zlib is a +compression library that is useful for more things than just PNG files. +You can use zlib as a drop-in replacement for fread() and fwrite() if +you are so inclined. + +zlib should be available at the same place that libpng is, or at. +ftp://ftp.info-zip.org/pub/infozip/zlib + +You may also want a copy of the PNG specification. It is available +as an RFC, a W3C Recommendation, and an ISO/IEC Standard. You can find +these at http://www.libpng.org/pub/png/documents/ + +This code is currently being archived at libpng.sf.net in the +[DOWNLOAD] area, and on CompuServe, Lib 20 (PNG SUPPORT) +at GO GRAPHSUP. If you can't find it in any of those places, +e-mail me, and I'll help you find it. + +If you have any code changes, requests, problems, etc., please e-mail +them to me. Also, I'd appreciate any make files or project files, +and any modifications you needed to make to get libpng to compile, +along with a #define variable to tell what compiler/system you are on. +If you needed to add transformations to libpng, or wish libpng would +provide the image in a different way, drop me a note (and code, if +possible), so I can consider supporting the transformation. +Finally, if you get any warning messages when compiling libpng +(note: not zlib), and they are easy to fix, I'd appreciate the +fix. Please mention "libpng" somewhere in the subject line. Thanks. + +This release was created and will be supported by myself (of course +based in a large way on Guy's and Andreas' earlier work), and the PNG group. + +glennrp at users.sourceforge.net +png-implement at ccrc.wustl.edu (subscription required; write to +majordomo at ccrc.wustl.edu with "subscribe png-implement" in the message). + +You can't reach Guy, the original libpng author, at the addresses +given in previous versions of this document. He and Andreas will read mail +addressed to the png-implement list, however. + +Please do not send general questions about PNG. Send them to +the (png-list at ccrc.wustl.edu, subscription required, write to +majordomo at ccrc.wustl.edu with "subscribe png-list" in your message). +On the other hand, +please do not send libpng questions to that address, send them to me +or to the png-implement list. I'll +get them in the end anyway. If you have a question about something +in the PNG specification that is related to using libpng, send it +to me. Send me any questions that start with "I was using libpng, +and ...". If in doubt, send questions to me. I'll bounce them +to others, if necessary. + +Please do not send suggestions on how to change PNG. We have +been discussing PNG for nine years now, and it is official and +finished. If you have suggestions for libpng, however, I'll +gladly listen. Even if your suggestion is not used immediately, +it may be used later. + +Files in this distribution: + + ANNOUNCE => Announcement of this version, with recent changes + CHANGES => Description of changes between libpng versions + KNOWNBUG => List of known bugs and deficiencies + LICENSE => License to use and redistribute libpng + README => This file + TODO => Things not implemented in the current library + Y2KINFO => Statement of Y2K compliance + example.c => Example code for using libpng functions + libpng.3 => manual page for libpng (includes libpng.txt) + libpng.txt => Description of libpng and its functions + libpngpf.3 => manual page for libpng's private functions + png.5 => manual page for the PNG format + png.c => Basic interface functions common to library + png.h => Library function and interface declarations + pngconf.h => System specific library configuration + pngasmrd.h => Header file for assembler-coded functions + pngerror.c => Error/warning message I/O functions + pngget.c => Functions for retrieving info from struct + pngmem.c => Memory handling functions + pngbar.png => PNG logo, 88x31 + pngnow.png => PNG logo, 98x31 + pngpread.c => Progressive reading functions + pngread.c => Read data/helper high-level functions + pngrio.c => Lowest-level data read I/O functions + pngrtran.c => Read data transformation functions + pngrutil.c => Read data utility functions + pngset.c => Functions for storing data into the info_struct + pngtest.c => Library test program + pngtest.png => Library test sample image + pngtrans.c => Common data transformation functions + pngwio.c => Lowest-level write I/O functions + pngwrite.c => High-level write functions + pngwtran.c => Write data transformations + pngwutil.c => Write utility functions + contrib => Contributions + gregbook => source code for PNG reading and writing, from + Greg Roelofs' "PNG: The Definitive Guide", + O'Reilly, 1999 + msvctest => Builds and runs pngtest using a MSVC workspace + pngminus => Simple pnm2png and png2pnm programs + pngsuite => Test images + visupng => Contains a MSVC workspace for VisualPng + projects => Contains project files and workspaces for building DLL + beos => Contains a Beos workspace for building libpng + c5builder => Contains a Borland workspace for building libpng + and zlib + visualc6 => Contains a Microsoft Visual C++ (MSVC) workspace + for building libpng and zlib + netware.txt => Contains instructions for downloading a set of + project files for building libpng and zlib on + Netware. + wince.txt => Contains instructions for downloading a Microsoft + Visual C++ (Windows CD Toolkit) workspace for + building libpng and zlib on WindowsCE + scripts => Directory containing scripts for building libpng: + descrip.mms => VMS makefile for MMS or MMK + makefile.std => Generic UNIX makefile (cc, creates static libpng.a) + makefile.elf => Linux/ELF makefile symbol versioning, + gcc, creates libpng12.so.0.1.2.8) + makefile.linux => Linux/ELF makefile + (gcc, creates libpng12.so.0.1.2.8) + makefile.gcmmx => Linux/ELF makefile + (gcc, creates libpng12.so.0.1.2.8, + uses assembler code tuned for Intel MMX platform) + makefile.gcc => Generic makefile (gcc, creates static libpng.a) + makefile.knr => Archaic UNIX Makefile that converts files with + ansi2knr (Requires ansi2knr.c from + ftp://ftp.cs.wisc.edu/ghost) + makefile.aix => AIX makefile + makefile.cygwin => Cygwin/gcc makefile + makefile.darwin => Darwin makefile + makefile.dec => DEC Alpha UNIX makefile + makefile.freebsd => FreeBSD makefile + makefile.hpgcc => HPUX makefile using gcc + makefile.hpux => HPUX (10.20 and 11.00) makefile + makefile.hp64 => HPUX (10.20 and 11.00) makefile, 64 bit + makefile.ibmc => IBM C/C++ version 3.x for Win32 and OS/2 (static) + makefile.intel => Intel C/C++ version 4.0 and later + libpng.icc => Project file, IBM VisualAge/C++ 4.0 or later + makefile.netbsd => NetBSD/cc makefile, PNGGCCRD, makes libpng.so. + makefile.ne12bsd => NetBSD/cc makefile, PNGGCCRD, makes libpng12.so + makefile.openbsd => OpenBSD makefile + makefile.sgi => Silicon Graphics IRIX (cc, creates static lib) + makefile.sggcc => Silicon Graphics + (gcc, creates libpng12.so.0.1.2.8) + makefile.sunos => Sun makefile + makefile.solaris => Solaris 2.X makefile + (gcc, creates libpng12.so.0.1.2.8) + makefile.so9 => Solaris 9 makefile + (gcc, creates libpng12.so.0.1.2.8) + makefile.32sunu => Sun Ultra 32-bit makefile + makefile.64sunu => Sun Ultra 64-bit makefile + makefile.sco => For SCO OSr5 ELF and Unixware 7 with Native cc + makefile.mips => MIPS makefile + makefile.acorn => Acorn makefile + makefile.amiga => Amiga makefile + smakefile.ppc => AMIGA smakefile for SAS C V6.58/7.00 PPC + compiler (Requires SCOPTIONS, copied from + scripts/SCOPTIONS.ppc) + makefile.atari => Atari makefile + makefile.beos => BEOS makefile for X86 + makefile.bor => Borland makefile (uses bcc) + makefile.bc32 => 32-bit Borland C++ (all modules compiled in C mode) + makefile.tc3 => Turbo C 3.0 makefile + makefile.dj2 => DJGPP 2 makefile + makefile.msc => Microsoft C makefile + makefile.vcawin32=> makefile for Microsoft Visual C++ 5.0 and + later (uses assembler code tuned for Intel MMX + platform) + makefile.vcwin32 => makefile for Microsoft Visual C++ 4.0 and + later (does not use assembler code) + makefile.os2 => OS/2 Makefile (gcc and emx, requires pngos2.def) + pngos2.def => OS/2 module definition file used by makefile.os2 + makefile.watcom => Watcom 10a+ Makefile, 32-bit flat memory model + makevms.com => VMS build script + SCOPTIONS.ppc => Used with smakefile.ppc + +Good luck, and happy coding. + +-Glenn Randers-Pehrson (current maintainer) + Internet: glennrp at users.sourceforge.net + +-Andreas Eric Dilger (former maintainer, 1996-1997) + Internet: adilger at enel.ucalgary.ca + Web: http://www-mddsp.enel.ucalgary.ca/People/adilger/ + +-Guy Eric Schalnat (original author and former maintainer, 1995-1996) + (formerly of Group 42, Inc) + Internet: gschal at infinet.com diff --git a/com32/lib/libpng/TODO b/com32/lib/libpng/TODO new file mode 100644 index 0000000..a5f6395 --- /dev/null +++ b/com32/lib/libpng/TODO @@ -0,0 +1,24 @@ +TODO - list of things to do for libpng: + +Final bug fixes. +Improve API by hiding the png_struct and png_info structs. +Finish work on the no-floating-point version (including gamma compensation) +Better C++ wrapper/full C++ implementation? +Fix problem with C++ and EXTERN "C". +cHRM transformation. +Improve setjmp/longjmp usage or remove it in favor of returning error codes. +Add "grayscale->palette" transformation and "palette->grayscale" detection. +Improved dithering. +Multi-lingual error and warning message support. +Complete sRGB transformation (presently it simply uses gamma=0.45455). +Man pages for function calls. +Better documentation. +Better filter selection + (counting huffman bits/precompression? filter inertia? filter costs?). +Histogram creation. +Text conversion between different code pages (Latin-1 -> Mac and DOS). +Should we always malloc 2^bit_depth PLTE/tRNS/hIST entries for safety? +Build gamma tables using fixed point (and do away with floating point entirely). +Use greater precision when changing to linear gamma for compositing against + background and doing rgb-to-gray transformation. +Investigate pre-incremented loop counters and other loop constructions. diff --git a/com32/lib/libpng/Y2KINFO b/com32/lib/libpng/Y2KINFO new file mode 100644 index 0000000..4db294f --- /dev/null +++ b/com32/lib/libpng/Y2KINFO @@ -0,0 +1,55 @@ + Y2K compliance in libpng: + ========================= + + December 3, 2004 + + Since the PNG Development group is an ad-hoc body, we can't make + an official declaration. + + This is your unofficial assurance that libpng from version 0.71 and + upward through 1.2.8 are Y2K compliant. It is my belief that earlier + versions were also Y2K compliant. + + Libpng only has three year fields. One is a 2-byte unsigned integer + that will hold years up to 65535. The other two hold the date in text + format, and will hold years up to 9999. + + The integer is + "png_uint_16 year" in png_time_struct. + + The strings are + "png_charp time_buffer" in png_struct and + "near_time_buffer", which is a local character string in png.c. + + There are seven time-related functions: + + png_convert_to_rfc_1123() in png.c + (formerly png_convert_to_rfc_1152() in error) + png_convert_from_struct_tm() in pngwrite.c, called in pngwrite.c + png_convert_from_time_t() in pngwrite.c + png_get_tIME() in pngget.c + png_handle_tIME() in pngrutil.c, called in pngread.c + png_set_tIME() in pngset.c + png_write_tIME() in pngwutil.c, called in pngwrite.c + + All appear to handle dates properly in a Y2K environment. The + png_convert_from_time_t() function calls gmtime() to convert from system + clock time, which returns (year - 1900), which we properly convert to + the full 4-digit year. There is a possibility that applications using + libpng are not passing 4-digit years into the png_convert_to_rfc_1123() + function, or that they are incorrectly passing only a 2-digit year + instead of "year - 1900" into the png_convert_from_struct_tm() function, + but this is not under our control. The libpng documentation has always + stated that it works with 4-digit years, and the APIs have been + documented as such. + + The tIME chunk itself is also Y2K compliant. It uses a 2-byte unsigned + integer to hold the year, and can hold years as large as 65535. + + zlib, upon which libpng depends, is also Y2K compliant. It contains + no date-related code. + + + Glenn Randers-Pehrson + libpng maintainer + PNG Development Group diff --git a/com32/lib/libpng/example.c b/com32/lib/libpng/example.c new file mode 100644 index 0000000..8a90295 --- /dev/null +++ b/com32/lib/libpng/example.c @@ -0,0 +1,808 @@ + +#if 0 /* in case someone actually tries to compile this */ + +/* example.c - an example of using libpng */ + +/* This is an example of how to use libpng to read and write PNG files. + * The file libpng.txt is much more verbose then this. If you have not + * read it, do so first. This was designed to be a starting point of an + * implementation. This is not officially part of libpng, is hereby placed + * in the public domain, and therefore does not require a copyright notice. + * + * This file does not currently compile, because it is missing certain + * parts, like allocating memory to hold an image. You will have to + * supply these parts to get it to compile. For an example of a minimal + * working PNG reader/writer, see pngtest.c, included in this distribution; + * see also the programs in the contrib directory. + */ + +#include "png.h" + + /* The png_jmpbuf() macro, used in error handling, became available in + * libpng version 1.0.6. If you want to be able to run your code with older + * versions of libpng, you must define the macro yourself (but only if it + * is not already defined by libpng!). + */ + +#ifndef png_jmpbuf +# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) +#endif + +/* Check to see if a file is a PNG file using png_sig_cmp(). png_sig_cmp() + * returns zero if the image is a PNG and nonzero if it isn't a PNG. + * + * The function check_if_png() shown here, but not used, returns nonzero (true) + * if the file can be opened and is a PNG, 0 (false) otherwise. + * + * If this call is successful, and you are going to keep the file open, + * you should call png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); once + * you have created the png_ptr, so that libpng knows your application + * has read that many bytes from the start of the file. Make sure you + * don't call png_set_sig_bytes() with more than 8 bytes read or give it + * an incorrect number of bytes read, or you will either have read too + * many bytes (your fault), or you are telling libpng to read the wrong + * number of magic bytes (also your fault). + * + * Many applications already read the first 2 or 4 bytes from the start + * of the image to determine the file type, so it would be easiest just + * to pass the bytes to png_sig_cmp() or even skip that if you know + * you have a PNG file, and call png_set_sig_bytes(). + */ +#define PNG_BYTES_TO_CHECK 4 +int check_if_png(char *file_name, FILE **fp) +{ + char buf[PNG_BYTES_TO_CHECK]; + + /* Open the prospective PNG file. */ + if ((*fp = fopen(file_name, "rb")) == NULL) + return 0; + + /* Read in some of the signature bytes */ + if (fread(buf, 1, PNG_BYTES_TO_CHECK, *fp) != PNG_BYTES_TO_CHECK) + return 0; + + /* Compare the first PNG_BYTES_TO_CHECK bytes of the signature. + Return nonzero (true) if they match */ + + return(!png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK)); +} + +/* Read a PNG file. You may want to return an error code if the read + * fails (depending upon the failure). There are two "prototypes" given + * here - one where we are given the filename, and we need to open the + * file, and the other where we are given an open file (possibly with + * some or all of the magic bytes read - see comments above). + */ +#ifdef open_file /* prototype 1 */ +void read_png(char *file_name) /* We need to open the file */ +{ + png_structp png_ptr; + png_infop info_ptr; + unsigned int sig_read = 0; + png_uint_32 width, height; + int bit_depth, color_type, interlace_type; + FILE *fp; + + if ((fp = fopen(file_name, "rb")) == NULL) + return (ERROR); +#else no_open_file /* prototype 2 */ +void read_png(FILE *fp, unsigned int sig_read) /* file is already open */ +{ + png_structp png_ptr; + png_infop info_ptr; + png_uint_32 width, height; + int bit_depth, color_type, interlace_type; +#endif no_open_file /* only use one prototype! */ + + /* Create and initialize the png_struct with the desired error handler + * functions. If you want to use the default stderr and longjump method, + * you can supply NULL for the last three parameters. We also supply the + * the compiler header file version, so that we know if the application + * was compiled with a compatible version of the library. REQUIRED + */ + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, + png_voidp user_error_ptr, user_error_fn, user_warning_fn); + + if (png_ptr == NULL) + { + fclose(fp); + return (ERROR); + } + + /* Allocate/initialize the memory for image information. REQUIRED. */ + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) + { + fclose(fp); + png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL); + return (ERROR); + } + + /* Set error handling if you are using the setjmp/longjmp method (this is + * the normal method of doing things with libpng). REQUIRED unless you + * set up your own error handlers in the png_create_read_struct() earlier. + */ + + if (setjmp(png_jmpbuf(png_ptr))) + { + /* Free all of the memory associated with the png_ptr and info_ptr */ + png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL); + fclose(fp); + /* If we get here, we had a problem reading the file */ + return (ERROR); + } + + /* One of the following I/O initialization methods is REQUIRED */ +#ifdef streams /* PNG file I/O method 1 */ + /* Set up the input control if you are using standard C streams */ + png_init_io(png_ptr, fp); + +#else no_streams /* PNG file I/O method 2 */ + /* If you are using replacement read functions, instead of calling + * png_init_io() here you would call: + */ + png_set_read_fn(png_ptr, (void *)user_io_ptr, user_read_fn); + /* where user_io_ptr is a structure you want available to the callbacks */ +#endif no_streams /* Use only one I/O method! */ + + /* If we have already read some of the signature */ + png_set_sig_bytes(png_ptr, sig_read); + +#ifdef hilevel + /* + * If you have enough memory to read in the entire image at once, + * and you need to specify only transforms that can be controlled + * with one of the PNG_TRANSFORM_* bits (this presently excludes + * dithering, filling, setting background, and doing gamma + * adjustment), then you can read the entire image (including + * pixels) into the info structure with this call: + */ + png_read_png(png_ptr, info_ptr, png_transforms, png_voidp_NULL); +#else + /* OK, you're doing it the hard way, with the lower-level functions */ + + /* The call to png_read_info() gives us all of the information from the + * PNG file before the first IDAT (image data chunk). REQUIRED + */ + png_read_info(png_ptr, info_ptr); + + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, + &interlace_type, int_p_NULL, int_p_NULL); + +/* Set up the data transformations you want. Note that these are all + * optional. Only call them if you want/need them. Many of the + * transformations only work on specific types of images, and many + * are mutually exclusive. + */ + + /* tell libpng to strip 16 bit/color files down to 8 bits/color */ + png_set_strip_16(png_ptr); + + /* Strip alpha bytes from the input data without combining with the + * background (not recommended). + */ + png_set_strip_alpha(png_ptr); + + /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single + * byte into separate bytes (useful for paletted and grayscale images). + */ + png_set_packing(png_ptr); + + /* Change the order of packed pixels to least significant bit first + * (not useful if you are using png_set_packing). */ + png_set_packswap(png_ptr); + + /* Expand paletted colors into true RGB triplets */ + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_rgb(png_ptr); + + /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */ + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_gray_1_2_4_to_8(png_ptr); + + /* Expand paletted or RGB images with transparency to full alpha channels + * so the data will be available as RGBA quartets. + */ + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(png_ptr); + + /* Set the background color to draw transparent and alpha images over. + * It is possible to set the red, green, and blue components directly + * for paletted images instead of supplying a palette index. Note that + * even if the PNG file supplies a background, you are not required to + * use it - you should use the (solid) application background if it has one. + */ + + png_color_16 my_background, *image_background; + + if (png_get_bKGD(png_ptr, info_ptr, &image_background)) + png_set_background(png_ptr, image_background, + PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); + else + png_set_background(png_ptr, &my_background, + PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0); + + /* Some suggestions as to how to get a screen gamma value */ + + /* Note that screen gamma is the display_exponent, which includes + * the CRT_exponent and any correction for viewing conditions */ + if (/* We have a user-defined screen gamma value */) + { + screen_gamma = user-defined screen_gamma; + } + /* This is one way that applications share the same screen gamma value */ + else if ((gamma_str = getenv("SCREEN_GAMMA")) != NULL) + { + screen_gamma = atof(gamma_str); + } + /* If we don't have another value */ + else + { + screen_gamma = 2.2; /* A good guess for a PC monitors in a dimly + lit room */ + screen_gamma = 1.7 or 1.0; /* A good guess for Mac systems */ + } + + /* Tell libpng to handle the gamma conversion for you. The final call + * is a good guess for PC generated images, but it should be configurable + * by the user at run time by the user. It is strongly suggested that + * your application support gamma correction. + */ + + int intent; + + if (png_get_sRGB(png_ptr, info_ptr, &intent)) + png_set_gamma(png_ptr, screen_gamma, 0.45455); + else + { + double image_gamma; + if (png_get_gAMA(png_ptr, info_ptr, &image_gamma)) + png_set_gamma(png_ptr, screen_gamma, image_gamma); + else + png_set_gamma(png_ptr, screen_gamma, 0.45455); + } + + /* Dither RGB files down to 8 bit palette or reduce palettes + * to the number of colors available on your screen. + */ + if (color_type & PNG_COLOR_MASK_COLOR) + { + int num_palette; + png_colorp palette; + + /* This reduces the image to the application supplied palette */ + if (/* we have our own palette */) + { + /* An array of colors to which the image should be dithered */ + png_color std_color_cube[MAX_SCREEN_COLORS]; + + png_set_dither(png_ptr, std_color_cube, MAX_SCREEN_COLORS, + MAX_SCREEN_COLORS, png_uint_16p_NULL, 0); + } + /* This reduces the image to the palette supplied in the file */ + else if (png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)) + { + png_uint_16p histogram = NULL; + + png_get_hIST(png_ptr, info_ptr, &histogram); + + png_set_dither(png_ptr, palette, num_palette, + max_screen_colors, histogram, 0); + } + } + + /* invert monochrome files to have 0 as white and 1 as black */ + png_set_invert_mono(png_ptr); + + /* If you want to shift the pixel values from the range [0,255] or + * [0,65535] to the original [0,7] or [0,31], or whatever range the + * colors were originally in: + */ + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT)) + { + png_color_8p sig_bit; + + png_get_sBIT(png_ptr, info_ptr, &sig_bit); + png_set_shift(png_ptr, sig_bit); + } + + /* flip the RGB pixels to BGR (or RGBA to BGRA) */ + if (color_type & PNG_COLOR_MASK_COLOR) + png_set_bgr(png_ptr); + + /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */ + png_set_swap_alpha(png_ptr); + + /* swap bytes of 16 bit files to least significant byte first */ + png_set_swap(png_ptr); + + /* Add filler (or alpha) byte (before/after each RGB triplet) */ + png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); + + /* Turn on interlace handling. REQUIRED if you are not using + * png_read_image(). To see how to handle interlacing passes, + * see the png_read_row() method below: + */ + number_passes = png_set_interlace_handling(png_ptr); + + /* Optional call to gamma correct and add the background to the palette + * and update info structure. REQUIRED if you are expecting libpng to + * update the palette for you (ie you selected such a transform above). + */ + png_read_update_info(png_ptr, info_ptr); + + /* Allocate the memory to hold the image using the fields of info_ptr. */ + + /* The easiest way to read the image: */ + png_bytep row_pointers[height]; + + for (row = 0; row < height; row++) + { + row_pointers[row] = png_malloc(png_ptr, png_get_rowbytes(png_ptr, + info_ptr)); + } + + /* Now it's time to read the image. One of these methods is REQUIRED */ +#ifdef entire /* Read the entire image in one go */ + png_read_image(png_ptr, row_pointers); + +#else no_entire /* Read the image one or more scanlines at a time */ + /* The other way to read images - deal with interlacing: */ + + for (pass = 0; pass < number_passes; pass++) + { +#ifdef single /* Read the image a single row at a time */ + for (y = 0; y < height; y++) + { + png_read_rows(png_ptr, &row_pointers[y], png_bytepp_NULL, 1); + } + +#else no_single /* Read the image several rows at a time */ + for (y = 0; y < height; y += number_of_rows) + { +#ifdef sparkle /* Read the image using the "sparkle" effect. */ + png_read_rows(png_ptr, &row_pointers[y], png_bytepp_NULL, + number_of_rows); +#else no_sparkle /* Read the image using the "rectangle" effect */ + png_read_rows(png_ptr, png_bytepp_NULL, &row_pointers[y], + number_of_rows); +#endif no_sparkle /* use only one of these two methods */ + } + + /* if you want to display the image after every pass, do + so here */ +#endif no_single /* use only one of these two methods */ + } +#endif no_entire /* use only one of these two methods */ + + /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ + png_read_end(png_ptr, info_ptr); +#endif hilevel + + /* At this point you have read the entire image */ + + /* clean up after the read, and free any memory allocated - REQUIRED */ + png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL); + + /* close the file */ + fclose(fp); + + /* that's it */ + return (OK); +} + +/* progressively read a file */ + +int +initialize_png_reader(png_structp *png_ptr, png_infop *info_ptr) +{ + /* Create and initialize the png_struct with the desired error handler + * functions. If you want to use the default stderr and longjump method, + * you can supply NULL for the last three parameters. We also check that + * the library version is compatible in case we are using dynamically + * linked libraries. + */ + *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, + png_voidp user_error_ptr, user_error_fn, user_warning_fn); + + if (*png_ptr == NULL) + { + *info_ptr = NULL; + return (ERROR); + } + + *info_ptr = png_create_info_struct(png_ptr); + + if (*info_ptr == NULL) + { + png_destroy_read_struct(png_ptr, info_ptr, png_infopp_NULL); + return (ERROR); + } + + if (setjmp(png_jmpbuf((*png_ptr)))) + { + png_destroy_read_struct(png_ptr, info_ptr, png_infopp_NULL); + return (ERROR); + } + + /* This one's new. You will need to provide all three + * function callbacks, even if you aren't using them all. + * If you aren't using all functions, you can specify NULL + * parameters. Even when all three functions are NULL, + * you need to call png_set_progressive_read_fn(). + * These functions shouldn't be dependent on global or + * static variables if you are decoding several images + * simultaneously. You should store stream specific data + * in a separate struct, given as the second parameter, + * and retrieve the pointer from inside the callbacks using + * the function png_get_progressive_ptr(png_ptr). + */ + png_set_progressive_read_fn(*png_ptr, (void *)stream_data, + info_callback, row_callback, end_callback); + + return (OK); +} + +int +process_data(png_structp *png_ptr, png_infop *info_ptr, + png_bytep buffer, png_uint_32 length) +{ + if (setjmp(png_jmpbuf((*png_ptr)))) + { + /* Free the png_ptr and info_ptr memory on error */ + png_destroy_read_struct(png_ptr, info_ptr, png_infopp_NULL); + return (ERROR); + } + + /* This one's new also. Simply give it chunks of data as + * they arrive from the data stream (in order, of course). + * On Segmented machines, don't give it any more than 64K. + * The library seems to run fine with sizes of 4K, although + * you can give it much less if necessary (I assume you can + * give it chunks of 1 byte, but I haven't tried with less + * than 256 bytes yet). When this function returns, you may + * want to display any rows that were generated in the row + * callback, if you aren't already displaying them there. + */ + png_process_data(*png_ptr, *info_ptr, buffer, length); + return (OK); +} + +info_callback(png_structp png_ptr, png_infop info) +{ +/* do any setup here, including setting any of the transformations + * mentioned in the Reading PNG files section. For now, you _must_ + * call either png_start_read_image() or png_read_update_info() + * after all the transformations are set (even if you don't set + * any). You may start getting rows before png_process_data() + * returns, so this is your last chance to prepare for that. + */ +} + +row_callback(png_structp png_ptr, png_bytep new_row, + png_uint_32 row_num, int pass) +{ +/* + * This function is called for every row in the image. If the + * image is interlaced, and you turned on the interlace handler, + * this function will be called for every row in every pass. + * + * In this function you will receive a pointer to new row data from + * libpng called new_row that is to replace a corresponding row (of + * the same data format) in a buffer allocated by your application. + * + * The new row data pointer new_row may be NULL, indicating there is + * no new data to be replaced (in cases of interlace loading). + * + * If new_row is not NULL then you need to call + * png_progressive_combine_row() to replace the corresponding row as + * shown below: + */ + /* Check if row_num is in bounds. */ + if((row_num >= 0) && (row_num < height)) + { + /* Get pointer to corresponding row in our + * PNG read buffer. + */ + png_bytep old_row = ((png_bytep *)our_data)[row_num]; + + /* If both rows are allocated then copy the new row + * data to the corresponding row data. + */ + if((old_row != NULL) && (new_row != NULL)) + png_progressive_combine_row(png_ptr, old_row, new_row); + } +/* + * The rows and passes are called in order, so you don't really + * need the row_num and pass, but I'm supplying them because it + * may make your life easier. + * + * For the non-NULL rows of interlaced images, you must call + * png_progressive_combine_row() passing in the new row and the + * old row, as demonstrated above. You can call this function for + * NULL rows (it will just return) and for non-interlaced images + * (it just does the png_memcpy for you) if it will make the code + * easier. Thus, you can just do this for all cases: + */ + + png_progressive_combine_row(png_ptr, old_row, new_row); + +/* where old_row is what was displayed for previous rows. Note + * that the first pass (pass == 0 really) will completely cover + * the old row, so the rows do not have to be initialized. After + * the first pass (and only for interlaced images), you will have + * to pass the current row as new_row, and the function will combine + * the old row and the new row. + */ +} + +end_callback(png_structp png_ptr, png_infop info) +{ +/* this function is called when the whole image has been read, + * including any chunks after the image (up to and including + * the IEND). You will usually have the same info chunk as you + * had in the header, although some data may have been added + * to the comments and time fields. + * + * Most people won't do much here, perhaps setting a flag that + * marks the image as finished. + */ +} + +/* write a png file */ +void write_png(char *file_name /* , ... other image information ... */) +{ + FILE *fp; + png_structp png_ptr; + png_infop info_ptr; + png_colorp palette; + + /* open the file */ + fp = fopen(file_name, "wb"); + if (fp == NULL) + return (ERROR); + + /* Create and initialize the png_struct with the desired error handler + * functions. If you want to use the default stderr and longjump method, + * you can supply NULL for the last three parameters. We also check that + * the library version is compatible with the one used at compile time, + * in case we are using dynamically linked libraries. REQUIRED. + */ + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + png_voidp user_error_ptr, user_error_fn, user_warning_fn); + + if (png_ptr == NULL) + { + fclose(fp); + return (ERROR); + } + + /* Allocate/initialize the image information data. REQUIRED */ + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) + { + fclose(fp); + png_destroy_write_struct(&png_ptr, png_infopp_NULL); + return (ERROR); + } + + /* Set error handling. REQUIRED if you aren't supplying your own + * error handling functions in the png_create_write_struct() call. + */ + if (setjmp(png_jmpbuf(png_ptr))) + { + /* If we get here, we had a problem reading the file */ + fclose(fp); + png_destroy_write_struct(&png_ptr, &info_ptr); + return (ERROR); + } + + /* One of the following I/O initialization functions is REQUIRED */ +#ifdef streams /* I/O initialization method 1 */ + /* set up the output control if you are using standard C streams */ + png_init_io(png_ptr, fp); +#else no_streams /* I/O initialization method 2 */ + /* If you are using replacement read functions, instead of calling + * png_init_io() here you would call */ + png_set_write_fn(png_ptr, (void *)user_io_ptr, user_write_fn, + user_IO_flush_function); + /* where user_io_ptr is a structure you want available to the callbacks */ +#endif no_streams /* only use one initialization method */ + +#ifdef hilevel + /* This is the easy way. Use it if you already have all the + * image info living info in the structure. You could "|" many + * PNG_TRANSFORM flags into the png_transforms integer here. + */ + png_write_png(png_ptr, info_ptr, png_transforms, png_voidp_NULL); +#else + /* This is the hard way */ + + /* Set the image information here. Width and height are up to 2^31, + * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on + * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, + * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, + * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or + * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST + * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED + */ + png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_???, + PNG_INTERLACE_????, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + /* set the palette if there is one. REQUIRED for indexed-color images */ + palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH + * png_sizeof (png_color)); + /* ... set palette colors ... */ + png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH); + /* You must not free palette here, because png_set_PLTE only makes a link to + the palette that you malloced. Wait until you are about to destroy + the png structure. */ + + /* optional significant bit chunk */ + /* if we are dealing with a grayscale image then */ + sig_bit.gray = true_bit_depth; + /* otherwise, if we are dealing with a color image then */ + sig_bit.red = true_red_bit_depth; + sig_bit.green = true_green_bit_depth; + sig_bit.blue = true_blue_bit_depth; + /* if the image has an alpha channel then */ + sig_bit.alpha = true_alpha_bit_depth; + png_set_sBIT(png_ptr, info_ptr, sig_bit); + + + /* Optional gamma chunk is strongly suggested if you have any guess + * as to the correct gamma of the image. + */ + png_set_gAMA(png_ptr, info_ptr, gamma); + + /* Optionally write comments into the image */ + text_ptr[0].key = "Title"; + text_ptr[0].text = "Mona Lisa"; + text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE; + text_ptr[1].key = "Author"; + text_ptr[1].text = "Leonardo DaVinci"; + text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE; + text_ptr[2].key = "Description"; + text_ptr[2].text = ""; + text_ptr[2].compression = PNG_TEXT_COMPRESSION_zTXt; +#ifdef PNG_iTXt_SUPPORTED + text_ptr[0].lang = NULL; + text_ptr[1].lang = NULL; + text_ptr[2].lang = NULL; +#endif + png_set_text(png_ptr, info_ptr, text_ptr, 3); + + /* other optional chunks like cHRM, bKGD, tRNS, tIME, oFFs, pHYs, */ + /* note that if sRGB is present the gAMA and cHRM chunks must be ignored + * on read and must be written in accordance with the sRGB profile */ + + /* Write the file header information. REQUIRED */ + png_write_info(png_ptr, info_ptr); + + /* If you want, you can write the info in two steps, in case you need to + * write your private chunk ahead of PLTE: + * + * png_write_info_before_PLTE(write_ptr, write_info_ptr); + * write_my_chunk(); + * png_write_info(png_ptr, info_ptr); + * + * However, given the level of known- and unknown-chunk support in 1.1.0 + * and up, this should no longer be necessary. + */ + + /* Once we write out the header, the compression type on the text + * chunks gets changed to PNG_TEXT_COMPRESSION_NONE_WR or + * PNG_TEXT_COMPRESSION_zTXt_WR, so it doesn't get written out again + * at the end. + */ + + /* set up the transformations you want. Note that these are + * all optional. Only call them if you want them. + */ + + /* invert monochrome pixels */ + png_set_invert_mono(png_ptr); + + /* Shift the pixels up to a legal bit depth and fill in + * as appropriate to correctly scale the image. + */ + png_set_shift(png_ptr, &sig_bit); + + /* pack pixels into bytes */ + png_set_packing(png_ptr); + + /* swap location of alpha bytes from ARGB to RGBA */ + png_set_swap_alpha(png_ptr); + + /* Get rid of filler (OR ALPHA) bytes, pack XRGB/RGBX/ARGB/RGBA into + * RGB (4 channels -> 3 channels). The second parameter is not used. + */ + png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); + + /* flip BGR pixels to RGB */ + png_set_bgr(png_ptr); + + /* swap bytes of 16-bit files to most significant byte first */ + png_set_swap(png_ptr); + + /* swap bits of 1, 2, 4 bit packed pixel formats */ + png_set_packswap(png_ptr); + + /* turn on interlace handling if you are not using png_write_image() */ + if (interlacing) + number_passes = png_set_interlace_handling(png_ptr); + else + number_passes = 1; + + /* The easiest way to write the image (you may have a different memory + * layout, however, so choose what fits your needs best). You need to + * use the first method if you aren't handling interlacing yourself. + */ + png_uint_32 k, height, width; + png_byte image[height][width*bytes_per_pixel]; + png_bytep row_pointers[height]; + + if (height > PNG_UINT_32_MAX/png_sizeof(png_bytep)) + png_error (png_ptr, "Image is too tall to process in memory"); + + for (k = 0; k < height; k++) + row_pointers[k] = image + k*width*bytes_per_pixel; + + /* One of the following output methods is REQUIRED */ +#ifdef entire /* write out the entire image data in one call */ + png_write_image(png_ptr, row_pointers); + + /* the other way to write the image - deal with interlacing */ + +#else no_entire /* write out the image data by one or more scanlines */ + /* The number of passes is either 1 for non-interlaced images, + * or 7 for interlaced images. + */ + for (pass = 0; pass < number_passes; pass++) + { + /* Write a few rows at a time. */ + png_write_rows(png_ptr, &row_pointers[first_row], number_of_rows); + + /* If you are only writing one row at a time, this works */ + for (y = 0; y < height; y++) + { + png_write_rows(png_ptr, &row_pointers[y], 1); + } + } +#endif no_entire /* use only one output method */ + + /* You can write optional chunks like tEXt, zTXt, and tIME at the end + * as well. Shouldn't be necessary in 1.1.0 and up as all the public + * chunks are supported and you can use png_set_unknown_chunks() to + * register unknown chunks into the info structure to be written out. + */ + + /* It is REQUIRED to call this to finish writing the rest of the file */ + png_write_end(png_ptr, info_ptr); +#endif hilevel + + /* If you png_malloced a palette, free it here (don't free info_ptr->palette, + as recommended in versions 1.0.5m and earlier of this example; if + libpng mallocs info_ptr->palette, libpng will free it). If you + allocated it with malloc() instead of png_malloc(), use free() instead + of png_free(). */ + png_free(png_ptr, palette); + palette=NULL; + + /* Similarly, if you png_malloced any data that you passed in with + png_set_something(), such as a hist or trans array, free it here, + when you can be sure that libpng is through with it. */ + png_free(png_ptr, trans); + trans=NULL; + + /* clean up after the write, and free any memory allocated */ + png_destroy_write_struct(&png_ptr, &info_ptr); + + /* close the file */ + fclose(fp); + + /* that's it */ + return (OK); +} + +#endif /* if 0 */ diff --git a/com32/lib/libpng/libpng.3 b/com32/lib/libpng/libpng.3 new file mode 100644 index 0000000..151e603 --- /dev/null +++ b/com32/lib/libpng/libpng.3 @@ -0,0 +1,4021 @@ +.TH LIBPNG 3 "December 3, 2004" +.SH NAME +libpng \- Portable Network Graphics (PNG) Reference Library 1.2.8 +.SH SYNOPSIS +\fI\fB + +\fB#include \fP + +\fI\fB + +\fBpng_uint_32 png_access_version_number \fI(void\fP\fB);\fP + +\fI\fB + +\fBint png_check_sig (png_bytep \fP\fIsig\fP\fB, int \fInum\fP\fB);\fP + +\fI\fB + +\fBvoid png_chunk_error (png_structp \fP\fIpng_ptr\fP\fB, png_const_charp \fIerror\fP\fB);\fP + +\fI\fB + +\fBvoid png_chunk_warning (png_structp \fP\fIpng_ptr\fP\fB, png_const_charp \fImessage\fP\fB);\fP + +\fI\fB + +\fBvoid png_convert_from_struct_tm (png_timep \fP\fIptime\fP\fB, struct tm FAR * \fIttime\fP\fB);\fP + +\fI\fB + +\fBvoid png_convert_from_time_t (png_timep \fP\fIptime\fP\fB, time_t \fIttime\fP\fB);\fP + +\fI\fB + +\fBpng_charp png_convert_to_rfc1123 (png_structp \fP\fIpng_ptr\fP\fB, png_timep \fIptime\fP\fB);\fP + +\fI\fB + +\fBpng_infop png_create_info_struct (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_structp png_create_read_struct (png_const_charp \fP\fIuser_png_ver\fP\fB, png_voidp \fP\fIerror_ptr\fP\fB, png_error_ptr \fP\fIerror_fn\fP\fB, png_error_ptr \fIwarn_fn\fP\fB);\fP + +\fI\fB + +\fBpng_structp png_create_read_struct_2(png_const_charp \fP\fIuser_png_ver\fP\fB, png_voidp \fP\fIerror_ptr\fP\fB, png_error_ptr \fP\fIerror_fn\fP\fB, png_error_ptr \fP\fIwarn_fn\fP\fB, png_voidp \fP\fImem_ptr\fP\fB, png_malloc_ptr \fP\fImalloc_fn\fP\fB, png_free_ptr \fIfree_fn\fP\fB);\fP + +\fI\fB + +\fBpng_structp png_create_write_struct (png_const_charp \fP\fIuser_png_ver\fP\fB, png_voidp \fP\fIerror_ptr\fP\fB, png_error_ptr \fP\fIerror_fn\fP\fB, png_error_ptr \fIwarn_fn\fP\fB);\fP + +\fI\fB + +\fBpng_structp png_create_write_struct_2(png_const_charp \fP\fIuser_png_ver\fP\fB, png_voidp \fP\fIerror_ptr\fP\fB, png_error_ptr \fP\fIerror_fn\fP\fB, png_error_ptr \fP\fIwarn_fn\fP\fB, png_voidp \fP\fImem_ptr\fP\fB, png_malloc_ptr \fP\fImalloc_fn\fP\fB, png_free_ptr \fIfree_fn\fP\fB);\fP + +\fI\fB + +\fBint png_debug(int \fP\fIlevel\fP\fB, png_const_charp \fImessage\fP\fB);\fP + +\fI\fB + +\fBint png_debug1(int \fP\fIlevel\fP\fB, png_const_charp \fP\fImessage\fP\fB, \fIp1\fP\fB);\fP + +\fI\fB + +\fBint png_debug2(int \fP\fIlevel\fP\fB, png_const_charp \fP\fImessage\fP\fB, \fP\fIp1\fP\fB, \fIp2\fP\fB);\fP + +\fI\fB + +\fBvoid png_destroy_info_struct (png_structp \fP\fIpng_ptr\fP\fB, png_infopp \fIinfo_ptr_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_destroy_read_struct (png_structpp \fP\fIpng_ptr_ptr\fP\fB, png_infopp \fP\fIinfo_ptr_ptr\fP\fB, png_infopp \fIend_info_ptr_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_destroy_write_struct (png_structpp \fP\fIpng_ptr_ptr\fP\fB, png_infopp \fIinfo_ptr_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_error (png_structp \fP\fIpng_ptr\fP\fB, png_const_charp \fIerror\fP\fB);\fP + +\fI\fB + +\fBvoid png_free (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fIptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_free_chunk_list (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_free_default(png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fIptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_free_data (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fInum\fP\fB);\fP + +\fI\fB + +\fBpng_byte png_get_bit_depth (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_bKGD (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_color_16p \fI*background\fP\fB);\fP + +\fI\fB + +\fBpng_byte png_get_channels (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_cHRM (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, double \fP\fI*white_x\fP\fB, double \fP\fI*white_y\fP\fB, double \fP\fI*red_x\fP\fB, double \fP\fI*red_y\fP\fB, double \fP\fI*green_x\fP\fB, double \fP\fI*green_y\fP\fB, double \fP\fI*blue_x\fP\fB, double \fI*blue_y\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_cHRM_fixed (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fI*white_x\fP\fB, png_uint_32 \fP\fI*white_y\fP\fB, png_uint_32 \fP\fI*red_x\fP\fB, png_uint_32 \fP\fI*red_y\fP\fB, png_uint_32 \fP\fI*green_x\fP\fB, png_uint_32 \fP\fI*green_y\fP\fB, png_uint_32 \fP\fI*blue_x\fP\fB, png_uint_32 \fI*blue_y\fP\fB);\fP + +\fI\fB + +\fBpng_byte png_get_color_type (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_byte png_get_compression_type (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_byte png_get_copyright (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_voidp png_get_error_ptr (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_byte png_get_filter_type (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_gAMA (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, double \fI*file_gamma\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_gAMA_fixed (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fI*int_file_gamma\fP\fB);\fP + +\fI\fB + +\fBpng_byte png_get_header_ver (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_byte png_get_header_version (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_hIST (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_16p \fI*hist\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_iCCP (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_charpp \fP\fIname\fP\fB, int \fP\fI*compression_type\fP\fB, png_charpp \fP\fIprofile\fP\fB, png_uint_32 \fI*proflen\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_IHDR (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fI*width\fP\fB, png_uint_32 \fP\fI*height\fP\fB, int \fP\fI*bit_depth\fP\fB, int \fP\fI*color_type\fP\fB, int \fP\fI*interlace_type\fP\fB, int \fP\fI*compression_type\fP\fB, int \fI*filter_type\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_image_height (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_image_width (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_byte png_get_interlace_type (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_voidp png_get_io_ptr (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_byte png_get_libpng_ver (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_voidp png_get_mem_ptr(png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_oFFs (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fI*offset_x\fP\fB, png_uint_32 \fP\fI*offset_y\fP\fB, int \fI*unit_type\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_pCAL (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_charp \fP\fI*purpose\fP\fB, png_int_32 \fP\fI*X0\fP\fB, png_int_32 \fP\fI*X1\fP\fB, int \fP\fI*type\fP\fB, int \fP\fI*nparams\fP\fB, png_charp \fP\fI*units\fP\fB, png_charpp \fI*params\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_pHYs (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fI*res_x\fP\fB, png_uint_32 \fP\fI*res_y\fP\fB, int \fI*unit_type\fP\fB);\fP + +\fI\fB + +\fBfloat png_get_pixel_aspect_ratio (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_pixels_per_meter (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_voidp png_get_progressive_ptr (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_PLTE (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_colorp \fP\fI*palette\fP\fB, int \fI*num_palette\fP\fB);\fP + +\fI\fB + +\fBpng_byte png_get_rgb_to_gray_status (png_structp \fIpng_ptr) + +\fBpng_uint_32 png_get_rowbytes (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_bytepp png_get_rows (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_sBIT (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_color_8p \fI*sig_bit\fP\fB);\fP + +\fI\fB + +\fBpng_bytep png_get_signature (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_sPLT (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_spalette_p \fI*splt_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_sRGB (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fI*intent\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_text (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_textp \fP\fI*text_ptr\fP\fB, int \fI*num_text\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_tIME (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_timep \fI*mod_time\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_tRNS (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_bytep \fP\fI*trans\fP\fB, int \fP\fI*num_trans\fP\fB, png_color_16p \fI*trans_values\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_unknown_chunks (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_unknown_chunkpp \fIunknowns\fP\fB);\fP + +\fI\fB + +\fBpng_voidp png_get_user_chunk_ptr (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_user_height_max( png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_voidp png_get_user_transform_ptr (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_user_width_max (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_valid (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIflag\fP\fB);\fP + +\fI\fB + +\fBpng_int_32 png_get_x_offset_microns (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_int_32 png_get_x_offset_pixels (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_x_pixels_per_meter (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_int_32 png_get_y_offset_microns (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_int_32 png_get_y_offset_pixels (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_y_pixels_per_meter (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_get_compression_buffer_size (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBint png_handle_as_unknown (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fIchunk_name\fP\fB);\fP + +\fI\fB + +\fBvoid png_init_io (png_structp \fP\fIpng_ptr\fP\fB, FILE \fI*fp\fP\fB);\fP + +\fI\fB + +\fBDEPRECATED: void png_info_init (png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBDEPRECATED: void png_info_init_2 (png_infopp \fP\fIptr_ptr\fP\fB, png_size_t \fIpng_info_struct_size\fP\fB);\fP + +\fI\fB + +\fBpng_voidp png_malloc (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fIsize\fP\fB);\fP + +\fI\fB + +\fBpng_voidp png_malloc_default(png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fIsize\fP\fB);\fP + +\fI\fB + +\fBvoidp png_memcpy (png_voidp \fP\fIs1\fP\fB, png_voidp \fP\fIs2\fP\fB, png_size_t \fIsize\fP\fB);\fP + +\fI\fB + +\fBpng_voidp png_memcpy_check (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIs1\fP\fB, png_voidp \fP\fIs2\fP\fB, png_uint_32 \fIsize\fP\fB);\fP + +\fI\fB + +\fBvoidp png_memset (png_voidp \fP\fIs1\fP\fB, int \fP\fIvalue\fP\fB, png_size_t \fIsize\fP\fB);\fP + +\fI\fB + +\fBpng_voidp png_memset_check (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIs1\fP\fB, int \fP\fIvalue\fP\fB, png_uint_32 \fIsize\fP\fB);\fP + +\fI\fB + +\fBDEPRECATED: void png_permit_empty_plte (png_structp \fP\fIpng_ptr\fP\fB, int \fIempty_plte_permitted\fP\fB);\fP + +\fI\fB + +\fBvoid png_process_data (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_bytep \fP\fIbuffer\fP\fB, png_size_t \fIbuffer_size\fP\fB);\fP + +\fI\fB + +\fBvoid png_progressive_combine_row (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIold_row\fP\fB, png_bytep \fInew_row\fP\fB);\fP + +\fI\fB + +\fBvoid png_read_destroy (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_infop \fIend_info_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_read_end (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_read_image (png_structp \fP\fIpng_ptr\fP\fB, png_bytepp \fIimage\fP\fB);\fP + +\fI\fB + +\fBDEPRECATED: void png_read_init (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBDEPRECATED: void png_read_init_2 (png_structpp \fP\fIptr_ptr\fP\fB, png_const_charp \fP\fIuser_png_ver\fP\fB, png_size_t \fP\fIpng_struct_size\fP\fB, png_size_t \fIpng_info_size\fP\fB);\fP + +\fI\fB + +\fBvoid png_read_info (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_read_png (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fP\fItransforms\fP\fB, png_voidp \fIparams\fP\fB);\fP + +\fI\fB + +\fBvoid png_read_row (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIrow\fP\fB, png_bytep \fIdisplay_row\fP\fB);\fP + +\fI\fB + +\fBvoid png_read_rows (png_structp \fP\fIpng_ptr\fP\fB, png_bytepp \fP\fIrow\fP\fB, png_bytepp \fP\fIdisplay_row\fP\fB, png_uint_32 \fInum_rows\fP\fB);\fP + +\fI\fB + +\fBvoid png_read_update_info (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fB#if \fI!defined(PNG_1_0_X) + +\fBvoid png_set_add_alpha (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fP\fIfiller\fP\fB, int \fIflags\fP\fB);\fP + +\fI\fB#endif + +\fI\fB + +\fBvoid png_set_background (png_structp \fP\fIpng_ptr\fP\fB, png_color_16p \fP\fIbackground_color\fP\fB, int \fP\fIbackground_gamma_code\fP\fB, int \fP\fIneed_expand\fP\fB, double \fIbackground_gamma\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_bgr (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_bKGD (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_color_16p \fIbackground\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_cHRM (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, double \fP\fIwhite_x\fP\fB, double \fP\fIwhite_y\fP\fB, double \fP\fIred_x\fP\fB, double \fP\fIred_y\fP\fB, double \fP\fIgreen_x\fP\fB, double \fP\fIgreen_y\fP\fB, double \fP\fIblue_x\fP\fB, double \fIblue_y\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_cHRM_fixed (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fIwhite_x\fP\fB, png_uint_32 \fP\fIwhite_y\fP\fB, png_uint_32 \fP\fIred_x\fP\fB, png_uint_32 \fP\fIred_y\fP\fB, png_uint_32 \fP\fIgreen_x\fP\fB, png_uint_32 \fP\fIgreen_y\fP\fB, png_uint_32 \fP\fIblue_x\fP\fB, png_uint_32 \fIblue_y\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_compression_level (png_structp \fP\fIpng_ptr\fP\fB, int \fIlevel\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_compression_mem_level (png_structp \fP\fIpng_ptr\fP\fB, int \fImem_level\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_compression_method (png_structp \fP\fIpng_ptr\fP\fB, int \fImethod\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_compression_strategy (png_structp \fP\fIpng_ptr\fP\fB, int \fIstrategy\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_compression_window_bits (png_structp \fP\fIpng_ptr\fP\fB, int \fIwindow_bits\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_crc_action (png_structp \fP\fIpng_ptr\fP\fB, int \fP\fIcrit_action\fP\fB, int \fIancil_action\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_dither (png_structp \fP\fIpng_ptr\fP\fB, png_colorp \fP\fIpalette\fP\fB, int \fP\fInum_palette\fP\fB, int \fP\fImaximum_colors\fP\fB, png_uint_16p \fP\fIhistogram\fP\fB, int \fIfull_dither\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_error_fn (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIerror_ptr\fP\fB, png_error_ptr \fP\fIerror_fn\fP\fB, png_error_ptr \fIwarning_fn\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_expand (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_filler (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fP\fIfiller\fP\fB, int \fIflags\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_filter (png_structp \fP\fIpng_ptr\fP\fB, int \fP\fImethod\fP\fB, int \fIfilters\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_filter_heuristics (png_structp \fP\fIpng_ptr\fP\fB, int \fP\fIheuristic_method\fP\fB, int \fP\fInum_weights\fP\fB, png_doublep \fP\fIfilter_weights\fP\fB, png_doublep \fIfilter_costs\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_flush (png_structp \fP\fIpng_ptr\fP\fB, int \fInrows\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_gamma (png_structp \fP\fIpng_ptr\fP\fB, double \fP\fIscreen_gamma\fP\fB, double \fIdefault_file_gamma\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_gAMA (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, double \fIfile_gamma\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_gAMA_fixed (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIfile_gamma\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_gray_1_2_4_to_8(png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_gray_to_rgb (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_hIST (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_16p \fIhist\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_iCCP (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_charp \fP\fIname\fP\fB, int \fP\fIcompression_type\fP\fB, png_charp \fP\fIprofile\fP\fB, png_uint_32 \fIproflen\fP\fB);\fP + +\fI\fB + +\fBint png_set_interlace_handling (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_invalid (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fImask\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_invert_alpha (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_invert_mono (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_IHDR (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fIwidth\fP\fB, png_uint_32 \fP\fIheight\fP\fB, int \fP\fIbit_depth\fP\fB, int \fP\fIcolor_type\fP\fB, int \fP\fIinterlace_type\fP\fB, int \fP\fIcompression_type\fP\fB, int \fIfilter_type\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_keep_unknown_chunks (png_structp \fP\fIpng_ptr\fP\fB, int \fP\fIkeep\fP\fB, png_bytep \fP\fIchunk_list\fP\fB, int \fInum_chunks\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_mem_fn(png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fImem_ptr\fP\fB, png_malloc_ptr \fP\fImalloc_fn\fP\fB, png_free_ptr \fIfree_fn\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_oFFs (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fIoffset_x\fP\fB, png_uint_32 \fP\fIoffset_y\fP\fB, int \fIunit_type\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_packing (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_packswap (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_palette_to_rgb(png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_pCAL (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_charp \fP\fIpurpose\fP\fB, png_int_32 \fP\fIX0\fP\fB, png_int_32 \fP\fIX1\fP\fB, int \fP\fItype\fP\fB, int \fP\fInparams\fP\fB, png_charp \fP\fIunits\fP\fB, png_charpp \fIparams\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_pHYs (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fIres_x\fP\fB, png_uint_32 \fP\fIres_y\fP\fB, int \fIunit_type\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_progressive_read_fn (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIprogressive_ptr\fP\fB, png_progressive_info_ptr \fP\fIinfo_fn\fP\fB, png_progressive_row_ptr \fP\fIrow_fn\fP\fB, png_progressive_end_ptr \fIend_fn\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_PLTE (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_colorp \fP\fIpalette\fP\fB, int \fInum_palette\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_read_fn (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIio_ptr\fP\fB, png_rw_ptr \fIread_data_fn\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_read_status_fn (png_structp \fP\fIpng_ptr\fP\fB, png_read_status_ptr \fIread_row_fn\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_read_user_transform_fn (png_structp \fP\fIpng_ptr\fP\fB, png_user_transform_ptr \fIread_user_transform_fn\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_rgb_to_gray (png_structp \fP\fIpng_ptr\fP\fB, int \fP\fIerror_action\fP\fB, double \fP\fIred\fP\fB, double \fIgreen\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_rgb_to_gray_fixed (png_structp \fP\fIpng_ptr\fP\fB, int error_action png_fixed_point \fP\fIred\fP\fB, png_fixed_point \fIgreen\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_rows (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_bytepp \fIrow_pointers\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_sBIT (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_color_8p \fIsig_bit\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_sCAL (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_charp \fP\fIunit\fP\fB, double \fP\fIwidth\fP\fB, double \fIheight\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_shift (png_structp \fP\fIpng_ptr\fP\fB, png_color_8p \fItrue_bits\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_sig_bytes (png_structp \fP\fIpng_ptr\fP\fB, int \fInum_bytes\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_sPLT (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_spalette_p \fP\fIsplt_ptr\fP\fB, int \fInum_spalettes\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_sRGB (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fIintent\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_sRGB_gAMA_and_cHRM (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fIintent\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_strip_16 (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_strip_alpha (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_swap (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_swap_alpha (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_text (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_textp \fP\fItext_ptr\fP\fB, int \fInum_text\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_tIME (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_timep \fImod_time\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_tRNS (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_bytep \fP\fItrans\fP\fB, int \fP\fInum_trans\fP\fB, png_color_16p \fItrans_values\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_tRNS_to_alpha(png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBpng_uint_32 png_set_unknown_chunks (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_unknown_chunkp \fP\fIunknowns\fP\fB, int \fP\fInum\fP\fB, int \fIlocation\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_unknown_chunk_location(png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fP\fIchunk\fP\fB, int \fIlocation\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_read_user_chunk_fn (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIuser_chunk_ptr\fP\fB, png_user_chunk_ptr \fIread_user_chunk_fn\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_user_limits (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fP\fIuser_width_max\fP\fB, png_uint_32 \fIuser_height_max\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_user_transform_info (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIuser_transform_ptr\fP\fB, int \fP\fIuser_transform_depth\fP\fB, int \fIuser_transform_channels\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_write_fn (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIio_ptr\fP\fB, png_rw_ptr \fP\fIwrite_data_fn\fP\fB, png_flush_ptr \fIoutput_flush_fn\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_write_status_fn (png_structp \fP\fIpng_ptr\fP\fB, png_write_status_ptr \fIwrite_row_fn\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_write_user_transform_fn (png_structp \fP\fIpng_ptr\fP\fB, png_user_transform_ptr \fIwrite_user_transform_fn\fP\fB);\fP + +\fI\fB + +\fBvoid png_set_compression_buffer_size(png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fIsize\fP\fB);\fP + +\fI\fB + +\fBint png_sig_cmp (png_bytep \fP\fIsig\fP\fB, png_size_t \fP\fIstart\fP\fB, png_size_t \fInum_to_check\fP\fB);\fP + +\fI\fB + +\fBvoid png_start_read_image (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_warning (png_structp \fP\fIpng_ptr\fP\fB, png_const_charp \fImessage\fP\fB);\fP + +\fI\fB + +\fBvoid png_write_chunk (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIchunk_name\fP\fB, png_bytep \fP\fIdata\fP\fB, png_size_t \fIlength\fP\fB);\fP + +\fI\fB + +\fBvoid png_write_chunk_data (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIdata\fP\fB, png_size_t \fIlength\fP\fB);\fP + +\fI\fB + +\fBvoid png_write_chunk_end (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_write_chunk_start (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIchunk_name\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fI\fB + +\fBvoid png_write_destroy (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_write_end (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_write_flush (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_write_image (png_structp \fP\fIpng_ptr\fP\fB, png_bytepp \fIimage\fP\fB);\fP + +\fI\fB + +\fBDEPRECATED: void png_write_init (png_structp \fIpng_ptr\fP\fB);\fP + +\fI\fB + +\fBDEPRECATED: void png_write_init_2 (png_structpp \fP\fIptr_ptr\fP\fB, png_const_charp \fP\fIuser_png_ver\fP\fB, png_size_t \fP\fIpng_struct_size\fP\fB, png_size_t \fIpng_info_size\fP\fB);\fP + +\fI\fB + +\fBvoid png_write_info (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_write_info_before_PLTE (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fI\fB + +\fBvoid png_write_png (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fP\fItransforms\fP\fB, png_voidp \fIparams\fP\fB);\fP + +\fI\fB + +\fBvoid png_write_row (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fIrow\fP\fB);\fP + +\fI\fB + +\fBvoid png_write_rows (png_structp \fP\fIpng_ptr\fP\fB, png_bytepp \fP\fIrow\fP\fB, png_uint_32 \fInum_rows\fP\fB);\fP + +\fI\fB + +\fBvoidpf png_zalloc (voidpf \fP\fIpng_ptr\fP\fB, uInt \fP\fIitems\fP\fB, uInt \fIsize\fP\fB);\fP + +\fI\fB + +\fBvoid png_zfree (voidpf \fP\fIpng_ptr\fP\fB, voidpf \fIptr\fP\fB);\fP + +\fI\fB + +.SH DESCRIPTION +The +.I libpng +library supports encoding, decoding, and various manipulations of +the Portable Network Graphics (PNG) format image files. It uses the +.IR zlib(3) +compression library. +Following is a copy of the libpng.txt file that accompanies libpng. +.SH LIBPNG.TXT +libpng.txt - A description on how to use and modify libpng + + libpng version 1.2.8 - December 3, 2004 + Updated and distributed by Glenn Randers-Pehrson + + Copyright (c) 1998-2004 Glenn Randers-Pehrson + For conditions of distribution and use, see copyright + notice in png.h. + + based on: + + libpng 1.0 beta 6 version 0.96 May 28, 1997 + Updated and distributed by Andreas Dilger + Copyright (c) 1996, 1997 Andreas Dilger + + libpng 1.0 beta 2 - version 0.88 January 26, 1996 + For conditions of distribution and use, see copyright + notice in png.h. Copyright (c) 1995, 1996 Guy Eric + Schalnat, Group 42, Inc. + + Updated/rewritten per request in the libpng FAQ + Copyright (c) 1995, 1996 Frank J. T. Wojcik + December 18, 1995 & January 20, 1996 + +.SH I. Introduction + +This file describes how to use and modify the PNG reference library +(known as libpng) for your own use. There are five sections to this +file: introduction, structures, reading, writing, and modification and +configuration notes for various special platforms. In addition to this +file, example.c is a good starting point for using the library, as +it is heavily commented and should include everything most people +will need. We assume that libpng is already installed; see the +INSTALL file for instructions on how to install libpng. + +Libpng was written as a companion to the PNG specification, as a way +of reducing the amount of time and effort it takes to support the PNG +file format in application programs. + +The PNG specification (second edition), November 2003, is available as +a W3C Recommendation and as an ISO Standard (ISO/IEC 15948:2003 (E)) at + + +The PNG-1.0 specification is available +as RFC 2083 and as a +W3C Recommendation . Some +additional chunks are described in the special-purpose public chunks +documents at . + +Other information +about PNG, and the latest version of libpng, can be found at the PNG home +page, . + +Most users will not have to modify the library significantly; advanced +users may want to modify it more. All attempts were made to make it as +complete as possible, while keeping the code easy to understand. +Currently, this library only supports C. Support for other languages +is being considered. + +Libpng has been designed to handle multiple sessions at one time, +to be easily modifiable, to be portable to the vast majority of +machines (ANSI, K&R, 16-, 32-, and 64-bit) available, and to be easy +to use. The ultimate goal of libpng is to promote the acceptance of +the PNG file format in whatever way possible. While there is still +work to be done (see the TODO file), libpng should cover the +majority of the needs of its users. + +Libpng uses zlib for its compression and decompression of PNG files. +Further information about zlib, and the latest version of zlib, can +be found at the zlib home page, . +The zlib compression utility is a general purpose utility that is +useful for more than PNG files, and can be used without libpng. +See the documentation delivered with zlib for more details. +You can usually find the source files for the zlib utility wherever you +find the libpng source files. + +Libpng is thread safe, provided the threads are using different +instances of the structures. Each thread should have its own +png_struct and png_info instances, and thus its own image. +Libpng does not protect itself against two threads using the +same instance of a structure. Note: thread safety may be defeated +by use of some of the MMX assembler code in pnggccrd.c, which is only +compiled when the user defines PNG_THREAD_UNSAFE_OK. + +.SH II. Structures + +There are two main structures that are important to libpng, png_struct +and png_info. The first, png_struct, is an internal structure that +will not, for the most part, be used by a user except as the first +variable passed to every libpng function call. + +The png_info structure is designed to provide information about the +PNG file. At one time, the fields of png_info were intended to be +directly accessible to the user. However, this tended to cause problems +with applications using dynamically loaded libraries, and as a result +a set of interface functions for png_info (the png_get_*() and png_set_*() +functions) was developed. The fields of png_info are still available for +older applications, but it is suggested that applications use the new +interfaces if at all possible. + +Applications that do make direct access to the members of png_struct (except +for png_ptr->jmpbuf) must be recompiled whenever the library is updated, +and applications that make direct access to the members of png_info must +be recompiled if they were compiled or loaded with libpng version 1.0.6, +in which the members were in a different order. In version 1.0.7, the +members of the png_info structure reverted to the old order, as they were +in versions 0.97c through 1.0.5. Starting with version 2.0.0, both +structures are going to be hidden, and the contents of the structures will +only be accessible through the png_get/png_set functions. + +The png.h header file is an invaluable reference for programming with libpng. +And while I'm on the topic, make sure you include the libpng header file: + +#include + +.SH III. Reading + +We'll now walk you through the possible functions to call when reading +in a PNG file sequentially, briefly explaining the syntax and purpose +of each one. See example.c and png.h for more detail. While +progressive reading is covered in the next section, you will still +need some of the functions discussed in this section to read a PNG +file. + +.SS Setup + +You will want to do the I/O initialization(*) before you get into libpng, +so if it doesn't work, you don't have much to undo. Of course, you +will also want to insure that you are, in fact, dealing with a PNG +file. Libpng provides a simple check to see if a file is a PNG file. +To use it, pass in the first 1 to 8 bytes of the file to the function +png_sig_cmp(), and it will return 0 if the bytes match the corresponding +bytes of the PNG signature, or nonzero otherwise. Of course, the more bytes +you pass in, the greater the accuracy of the prediction. + +If you are intending to keep the file pointer open for use in libpng, +you must ensure you don't read more than 8 bytes from the beginning +of the file, and you also have to make a call to png_set_sig_bytes_read() +with the number of bytes you read from the beginning. Libpng will +then only check the bytes (if any) that your program didn't read. + +(*): If you are not using the standard I/O functions, you will need +to replace them with custom functions. See the discussion under +Customizing libpng. + + + FILE *fp = fopen(file_name, "rb"); + if (!fp) + { + return (ERROR); + } + fread(header, 1, number, fp); + is_png = !png_sig_cmp(header, 0, number); + if (!is_png) + { + return (NOT_PNG); + } + + +Next, png_struct and png_info need to be allocated and initialized. In +order to ensure that the size of these structures is correct even with a +dynamically linked libpng, there are functions to initialize and +allocate the structures. We also pass the library version, optional +pointers to error handling functions, and a pointer to a data struct for +use by the error functions, if necessary (the pointer and functions can +be NULL if the default error handlers are to be used). See the section +on Changes to Libpng below regarding the old initialization functions. +The structure allocation functions quietly return NULL if they fail to +create the structure, so your application should check for that. + + png_structp png_ptr = png_create_read_struct + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn); + if (!png_ptr) + return (ERROR); + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr, + (png_infopp)NULL, (png_infopp)NULL); + return (ERROR); + } + + png_infop end_info = png_create_info_struct(png_ptr); + if (!end_info) + { + png_destroy_read_struct(&png_ptr, &info_ptr, + (png_infopp)NULL); + return (ERROR); + } + +If you want to use your own memory allocation routines, +define PNG_USER_MEM_SUPPORTED and use +png_create_read_struct_2() instead of png_create_read_struct(): + + png_structp png_ptr = png_create_read_struct_2 + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn, (png_voidp) + user_mem_ptr, user_malloc_fn, user_free_fn); + +The error handling routines passed to png_create_read_struct() +and the memory alloc/free routines passed to png_create_struct_2() +are only necessary if you are not using the libpng supplied error +handling and memory alloc/free functions. + +When libpng encounters an error, it expects to longjmp back +to your routine. Therefore, you will need to call setjmp and pass +your png_jmpbuf(png_ptr). If you read the file from different +routines, you will need to update the jmpbuf field every time you enter +a new routine that will call a png_*() function. + +See your documentation of setjmp/longjmp for your compiler for more +information on setjmp/longjmp. See the discussion on libpng error +handling in the Customizing Libpng section below for more information +on the libpng error handling. If an error occurs, and libpng longjmp's +back to your setjmp, you will want to call png_destroy_read_struct() to +free any memory. + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, + &end_info); + fclose(fp); + return (ERROR); + } + +If you would rather avoid the complexity of setjmp/longjmp issues, +you can compile libpng with PNG_SETJMP_NOT_SUPPORTED, in which case +errors will result in a call to PNG_ABORT() which defaults to abort(). + +Now you need to set up the input code. The default for libpng is to +use the C function fread(). If you use this, you will need to pass a +valid FILE * in the function png_init_io(). Be sure that the file is +opened in binary mode. If you wish to handle reading data in another +way, you need not call the png_init_io() function, but you must then +implement the libpng I/O methods discussed in the Customizing Libpng +section below. + + png_init_io(png_ptr, fp); + +If you had previously opened the file and read any of the signature from +the beginning in order to see if this was a PNG file, you need to let +libpng know that there are some bytes missing from the start of the file. + + png_set_sig_bytes(png_ptr, number); + +.SS Setting up callback code + +You can set up a callback function to handle any unknown chunks in the +input stream. You must supply the function + + read_chunk_callback(png_ptr ptr, + png_unknown_chunkp chunk); + { + /* The unknown chunk structure contains your + chunk data: */ + png_byte name[5]; + png_byte *data; + png_size_t size; + /* Note that libpng has already taken care of + the CRC handling */ + + /* put your code here. Return one of the + following: */ + + return (-n); /* chunk had an error */ + return (0); /* did not recognize */ + return (n); /* success */ + } + +(You can give your function another name that you like instead of +"read_chunk_callback") + +To inform libpng about your function, use + + png_set_read_user_chunk_fn(png_ptr, user_chunk_ptr, + read_chunk_callback); + +This names not only the callback function, but also a user pointer that +you can retrieve with + + png_get_user_chunk_ptr(png_ptr); + +At this point, you can set up a callback function that will be +called after each row has been read, which you can use to control +a progress meter or the like. It's demonstrated in pngtest.c. +You must supply a function + + void read_row_callback(png_ptr ptr, png_uint_32 row, + int pass); + { + /* put your code here */ + } + +(You can give it another name that you like instead of "read_row_callback") + +To inform libpng about your function, use + + png_set_read_status_fn(png_ptr, read_row_callback); + +.SS Width and height limits + +The PNG specification allows the width and height of an image to be as +large as 2^31-1 (0x7fffffff), or about 2.147 billion rows and columns. +Since very few applications really need to process such large images, +we have imposed an arbitrary 1-million limit on rows and columns. +Larger images will be rejected immediately with a png_error() call. If +you wish to override this limit, you can use + + png_set_user_limits(png_ptr, width_max, height_max); + +to set your own limits, or use width_max = height_max = 0x7fffffffL +to allow all valid dimensions (libpng may reject some very large images +anyway because of potential buffer overflow conditions). + +You should put this statement after you create the PNG structure and +before calling png_read_info(), png_read_png(), or png_process_data(). +If you need to retrieve the limits that are being applied, use + + width_max = png_get_user_width_max(png_ptr); + height_max = png_get_user_height_max(png_ptr); + +.SS Unknown-chunk handling + +Now you get to set the way the library processes unknown chunks in the +input PNG stream. Both known and unknown chunks will be read. Normal +behavior is that known chunks will be parsed into information in +various info_ptr members; unknown chunks will be discarded. To change +this, you can call: + + png_set_keep_unknown_chunks(png_ptr, keep, + chunk_list, num_chunks); + keep - 0: do not handle as unknown + 1: do not keep + 2: keep only if safe-to-copy + 3: keep even if unsafe-to-copy + You can use these definitions: + PNG_HANDLE_CHUNK_AS_DEFAULT 0 + PNG_HANDLE_CHUNK_NEVER 1 + PNG_HANDLE_CHUNK_IF_SAFE 2 + PNG_HANDLE_CHUNK_ALWAYS 3 + chunk_list - list of chunks affected (a byte string, + five bytes per chunk, NULL or '\0' if + num_chunks is 0) + num_chunks - number of chunks affected; if 0, all + unknown chunks are affected. If nonzero, + only the chunks in the list are affected + +Unknown chunks declared in this way will be saved as raw data onto a +list of png_unknown_chunk structures. If a chunk that is normally +known to libpng is named in the list, it will be handled as unknown, +according to the "keep" directive. If a chunk is named in successive +instances of png_set_keep_unknown_chunks(), the final instance will +take precedence. The IHDR and IEND chunks should not be named in +chunk_list; if they are, libpng will process them normally anyway. + +.SS The high-level read interface + +At this point there are two ways to proceed; through the high-level +read interface, or through a sequence of low-level read operations. +You can use the high-level interface if (a) you are willing to read +the entire image into memory, and (b) the input transformations +you want to do are limited to the following set: + + PNG_TRANSFORM_IDENTITY No transformation + PNG_TRANSFORM_STRIP_16 Strip 16-bit samples to + 8 bits + PNG_TRANSFORM_STRIP_ALPHA Discard the alpha channel + PNG_TRANSFORM_PACKING Expand 1, 2 and 4-bit + samples to bytes + PNG_TRANSFORM_PACKSWAP Change order of packed + pixels to LSB first + PNG_TRANSFORM_EXPAND Perform set_expand() + PNG_TRANSFORM_INVERT_MONO Invert monochrome images + PNG_TRANSFORM_SHIFT Normalize pixels to the + sBIT depth + PNG_TRANSFORM_BGR Flip RGB to BGR, RGBA + to BGRA + PNG_TRANSFORM_SWAP_ALPHA Flip RGBA to ARGB or GA + to AG + PNG_TRANSFORM_INVERT_ALPHA Change alpha from opacity + to transparency + PNG_TRANSFORM_SWAP_ENDIAN Byte-swap 16-bit samples + +(This excludes setting a background color, doing gamma transformation, +dithering, and setting filler.) If this is the case, simply do this: + + png_read_png(png_ptr, info_ptr, png_transforms, NULL) + +where png_transforms is an integer containing the logical OR of +some set of transformation flags. This call is equivalent to png_read_info(), +followed the set of transformations indicated by the transform mask, +then png_read_image(), and finally png_read_end(). + +(The final parameter of this call is not yet used. Someday it might point +to transformation parameters required by some future input transform.) + +You must use png_transforms and not call any png_set_transform() functions +when you use png_read_png(). + +After you have called png_read_png(), you can retrieve the image data +with + + row_pointers = png_get_rows(png_ptr, info_ptr); + +where row_pointers is an array of pointers to the pixel data for each row: + + png_bytep row_pointers[height]; + +If you know your image size and pixel size ahead of time, you can allocate +row_pointers prior to calling png_read_png() with + + if (height > PNG_UINT_32_MAX/png_sizeof(png_byte)) + png_error (png_ptr, + "Image is too tall to process in memory"); + if (width > PNG_UINT_32_MAX/pixel_size) + png_error (png_ptr, + "Image is too wide to process in memory"); + row_pointers = png_malloc(png_ptr, + height*png_sizeof(png_bytep)); + for (int i=0; i) and +png_get_(png_ptr, info_ptr, ...) functions return non-zero if the +data has been read, or zero if it is missing. The parameters to the +png_get_ are set directly if they are simple data types, or a pointer +into the info_ptr is returned for any complex types. + + png_get_PLTE(png_ptr, info_ptr, &palette, + &num_palette); + palette - the palette for the file + (array of png_color) + num_palette - number of entries in the palette + + png_get_gAMA(png_ptr, info_ptr, &gamma); + gamma - the gamma the file is written + at (PNG_INFO_gAMA) + + png_get_sRGB(png_ptr, info_ptr, &srgb_intent); + srgb_intent - the rendering intent (PNG_INFO_sRGB) + The presence of the sRGB chunk + means that the pixel data is in the + sRGB color space. This chunk also + implies specific values of gAMA and + cHRM. + + png_get_iCCP(png_ptr, info_ptr, &name, + &compression_type, &profile, &proflen); + name - The profile name. + compression - The compression type; always + PNG_COMPRESSION_TYPE_BASE for PNG 1.0. + You may give NULL to this argument to + ignore it. + profile - International Color Consortium color + profile data. May contain NULs. + proflen - length of profile data in bytes. + + png_get_sBIT(png_ptr, info_ptr, &sig_bit); + sig_bit - the number of significant bits for + (PNG_INFO_sBIT) each of the gray, + red, green, and blue channels, + whichever are appropriate for the + given color type (png_color_16) + + png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, + &trans_values); + trans - array of transparent entries for + palette (PNG_INFO_tRNS) + trans_values - graylevel or color sample values of + the single transparent color for + non-paletted images (PNG_INFO_tRNS) + num_trans - number of transparent entries + (PNG_INFO_tRNS) + + png_get_hIST(png_ptr, info_ptr, &hist); + (PNG_INFO_hIST) + hist - histogram of palette (array of + png_uint_16) + + png_get_tIME(png_ptr, info_ptr, &mod_time); + mod_time - time image was last modified + (PNG_VALID_tIME) + + png_get_bKGD(png_ptr, info_ptr, &background); + background - background color (PNG_VALID_bKGD) + valid 16-bit red, green and blue + values, regardless of color_type + + num_comments = png_get_text(png_ptr, info_ptr, + &text_ptr, &num_text); + num_comments - number of comments + text_ptr - array of png_text holding image + comments + text_ptr[i].compression - type of compression used + on "text" PNG_TEXT_COMPRESSION_NONE + PNG_TEXT_COMPRESSION_zTXt + PNG_ITXT_COMPRESSION_NONE + PNG_ITXT_COMPRESSION_zTXt + text_ptr[i].key - keyword for comment. Must contain + 1-79 characters. + text_ptr[i].text - text comments for current + keyword. Can be empty. + text_ptr[i].text_length - length of text string, + after decompression, 0 for iTXt + text_ptr[i].itxt_length - length of itxt string, + after decompression, 0 for tEXt/zTXt + text_ptr[i].lang - language of comment (empty + string for unknown). + text_ptr[i].lang_key - keyword in UTF-8 + (empty string for unknown). + num_text - number of comments (same as + num_comments; you can put NULL here + to avoid the duplication) + Note while png_set_text() will accept text, language, + and translated keywords that can be NULL pointers, the + structure returned by png_get_text will always contain + regular zero-terminated C strings. They might be + empty strings but they will never be NULL pointers. + + num_spalettes = png_get_sPLT(png_ptr, info_ptr, + &palette_ptr); + palette_ptr - array of palette structures holding + contents of one or more sPLT chunks + read. + num_spalettes - number of sPLT chunks read. + + png_get_oFFs(png_ptr, info_ptr, &offset_x, &offset_y, + &unit_type); + offset_x - positive offset from the left edge + of the screen + offset_y - positive offset from the top edge + of the screen + unit_type - PNG_OFFSET_PIXEL, PNG_OFFSET_MICROMETER + + png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, + &unit_type); + res_x - pixels/unit physical resolution in + x direction + res_y - pixels/unit physical resolution in + x direction + unit_type - PNG_RESOLUTION_UNKNOWN, + PNG_RESOLUTION_METER + + png_get_sCAL(png_ptr, info_ptr, &unit, &width, + &height) + unit - physical scale units (an integer) + width - width of a pixel in physical scale units + height - height of a pixel in physical scale units + (width and height are doubles) + + png_get_sCAL_s(png_ptr, info_ptr, &unit, &width, + &height) + unit - physical scale units (an integer) + width - width of a pixel in physical scale units + height - height of a pixel in physical scale units + (width and height are strings like "2.54") + + num_unknown_chunks = png_get_unknown_chunks(png_ptr, + info_ptr, &unknowns) + unknowns - array of png_unknown_chunk + structures holding unknown chunks + unknowns[i].name - name of unknown chunk + unknowns[i].data - data of unknown chunk + unknowns[i].size - size of unknown chunk's data + unknowns[i].location - position of chunk in file + + The value of "i" corresponds to the order in which the + chunks were read from the PNG file or inserted with the + png_set_unknown_chunks() function. + +The data from the pHYs chunk can be retrieved in several convenient +forms: + + res_x = png_get_x_pixels_per_meter(png_ptr, + info_ptr) + res_y = png_get_y_pixels_per_meter(png_ptr, + info_ptr) + res_x_and_y = png_get_pixels_per_meter(png_ptr, + info_ptr) + res_x = png_get_x_pixels_per_inch(png_ptr, + info_ptr) + res_y = png_get_y_pixels_per_inch(png_ptr, + info_ptr) + res_x_and_y = png_get_pixels_per_inch(png_ptr, + info_ptr) + aspect_ratio = png_get_pixel_aspect_ratio(png_ptr, + info_ptr) + + (Each of these returns 0 [signifying "unknown"] if + the data is not present or if res_x is 0; + res_x_and_y is 0 if res_x != res_y) + +The data from the oFFs chunk can be retrieved in several convenient +forms: + + x_offset = png_get_x_offset_microns(png_ptr, info_ptr); + y_offset = png_get_y_offset_microns(png_ptr, info_ptr); + x_offset = png_get_x_offset_inches(png_ptr, info_ptr); + y_offset = png_get_y_offset_inches(png_ptr, info_ptr); + + (Each of these returns 0 [signifying "unknown" if both + x and y are 0] if the data is not present or if the + chunk is present but the unit is the pixel) + +For more information, see the png_info definition in png.h and the +PNG specification for chunk contents. Be careful with trusting +rowbytes, as some of the transformations could increase the space +needed to hold a row (expand, filler, gray_to_rgb, etc.). +See png_read_update_info(), below. + +A quick word about text_ptr and num_text. PNG stores comments in +keyword/text pairs, one pair per chunk, with no limit on the number +of text chunks, and a 2^31 byte limit on their size. While there are +suggested keywords, there is no requirement to restrict the use to these +strings. It is strongly suggested that keywords and text be sensible +to humans (that's the point), so don't use abbreviations. Non-printing +symbols are not allowed. See the PNG specification for more details. +There is also no requirement to have text after the keyword. + +Keywords should be limited to 79 Latin-1 characters without leading or +trailing spaces, but non-consecutive spaces are allowed within the +keyword. It is possible to have the same keyword any number of times. +The text_ptr is an array of png_text structures, each holding a +pointer to a language string, a pointer to a keyword and a pointer to +a text string. The text string, language code, and translated +keyword may be empty or NULL pointers. The keyword/text +pairs are put into the array in the order that they are received. +However, some or all of the text chunks may be after the image, so, to +make sure you have read all the text chunks, don't mess with these +until after you read the stuff after the image. This will be +mentioned again below in the discussion that goes with png_read_end(). + +.SS Input transformations + +After you've read the header information, you can set up the library +to handle any special transformations of the image data. The various +ways to transform the data will be described in the order that they +should occur. This is important, as some of these change the color +type and/or bit depth of the data, and some others only work on +certain color types and bit depths. Even though each transformation +checks to see if it has data that it can do something with, you should +make sure to only enable a transformation if it will be valid for the +data. For example, don't swap red and blue on grayscale data. + +The colors used for the background and transparency values should be +supplied in the same format/depth as the current image data. They +are stored in the same format/depth as the image data in a bKGD or tRNS +chunk, so this is what libpng expects for this data. The colors are +transformed to keep in sync with the image data when an application +calls the png_read_update_info() routine (see below). + +Data will be decoded into the supplied row buffers packed into bytes +unless the library has been told to transform it into another format. +For example, 4 bit/pixel paletted or grayscale data will be returned +2 pixels/byte with the leftmost pixel in the high-order bits of the +byte, unless png_set_packing() is called. 8-bit RGB data will be stored +in RGB RGB RGB format unless png_set_filler() or png_set_add_alpha() +is called to insert filler bytes, either before or after each RGB triplet. +16-bit RGB data will be returned RRGGBB RRGGBB, with the most significant +byte of the color value first, unless png_set_strip_16() is called to +transform it to regular RGB RGB triplets, or png_set_filler() or +png_set_add alpha() is called to insert filler bytes, either before or +after each RRGGBB triplet. Similarly, 8-bit or 16-bit grayscale data can +be modified with +png_set_filler(), png_set_add_alpha(), or png_set_strip_16(). + +The following code transforms grayscale images of less than 8 to 8 bits, +changes paletted images to RGB, and adds a full alpha channel if there is +transparency information in a tRNS chunk. This is most useful on +grayscale images with bit depths of 2 or 4 or if there is a multiple-image +viewing application that wishes to treat all images in the same way. + + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png_ptr); + + if (color_type == PNG_COLOR_TYPE_GRAY && + bit_depth < 8) png_set_gray_1_2_4_to_8(png_ptr); + + if (png_get_valid(png_ptr, info_ptr, + PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); + +These three functions are actually aliases for png_set_expand(), added +in libpng version 1.0.4, with the function names expanded to improve code +readability. In some future version they may actually do different +things. + +PNG can have files with 16 bits per channel. If you only can handle +8 bits per channel, this will strip the pixels down to 8 bit. + + if (bit_depth == 16) + png_set_strip_16(png_ptr); + +If, for some reason, you don't need the alpha channel on an image, +and you want to remove it rather than combining it with the background +(but the image author certainly had in mind that you *would* combine +it with the background, so that's what you should probably do): + + if (color_type & PNG_COLOR_MASK_ALPHA) + png_set_strip_alpha(png_ptr); + +In PNG files, the alpha channel in an image +is the level of opacity. If you need the alpha channel in an image to +be the level of transparency instead of opacity, you can invert the +alpha channel (or the tRNS chunk data) after it's read, so that 0 is +fully opaque and 255 (in 8-bit or paletted images) or 65535 (in 16-bit +images) is fully transparent, with + + png_set_invert_alpha(png_ptr); + +PNG files pack pixels of bit depths 1, 2, and 4 into bytes as small as +they can, resulting in, for example, 8 pixels per byte for 1 bit +files. This code expands to 1 pixel per byte without changing the +values of the pixels: + + if (bit_depth < 8) + png_set_packing(png_ptr); + +PNG files have possible bit depths of 1, 2, 4, 8, and 16. All pixels +stored in a PNG image have been "scaled" or "shifted" up to the next +higher possible bit depth (e.g. from 5 bits/sample in the range [0,31] to +8 bits/sample in the range [0, 255]). However, it is also possible to +convert the PNG pixel data back to the original bit depth of the image. +This call reduces the pixels back down to the original bit depth: + + png_color_8p sig_bit; + + if (png_get_sBIT(png_ptr, info_ptr, &sig_bit)) + png_set_shift(png_ptr, sig_bit); + +PNG files store 3-color pixels in red, green, blue order. This code +changes the storage of the pixels to blue, green, red: + + if (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) + png_set_bgr(png_ptr); + +PNG files store RGB pixels packed into 3 or 6 bytes. This code expands them +into 4 or 8 bytes for windowing systems that need them in this format: + + if (color_type == PNG_COLOR_TYPE_RGB) + png_set_filler(png_ptr, filler, PNG_FILLER_BEFORE); + +where "filler" is the 8 or 16-bit number to fill with, and the location is +either PNG_FILLER_BEFORE or PNG_FILLER_AFTER, depending upon whether +you want the filler before the RGB or after. This transformation +does not affect images that already have full alpha channels. To add an +opaque alpha channel, use filler=0xff or 0xffff and PNG_FILLER_AFTER which +will generate RGBA pixels. + +Note that png_set_filler() does not change the color type. If you want +to do that, you can add a true alpha channel with + + if (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY) + png_set_add_alpha(png_ptr, filler, PNG_FILLER_AFTER); + +where "filler" contains the alpha value to assign to each pixel. +This function was added in libpng-1.2.7. + +If you are reading an image with an alpha channel, and you need the +data as ARGB instead of the normal PNG format RGBA: + + if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) + png_set_swap_alpha(png_ptr); + +For some uses, you may want a grayscale image to be represented as +RGB. This code will do that conversion: + + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + +Conversely, you can convert an RGB or RGBA image to grayscale or grayscale +with alpha. + + if (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) + png_set_rgb_to_gray_fixed(png_ptr, error_action, + int red_weight, int green_weight); + + error_action = 1: silently do the conversion + error_action = 2: issue a warning if the original + image has any pixel where + red != green or red != blue + error_action = 3: issue an error and abort the + conversion if the original + image has any pixel where + red != green or red != blue + + red_weight: weight of red component times 100000 + green_weight: weight of green component times 100000 + If either weight is negative, default + weights (21268, 71514) are used. + +If you have set error_action = 1 or 2, you can +later check whether the image really was gray, after processing +the image rows, with the png_get_rgb_to_gray_status(png_ptr) function. +It will return a png_byte that is zero if the image was gray or +1 if there were any non-gray pixels. bKGD and sBIT data +will be silently converted to grayscale, using the green channel +data, regardless of the error_action setting. + +With red_weight+green_weight<=100000, +the normalized graylevel is computed: + + int rw = red_weight * 65536; + int gw = green_weight * 65536; + int bw = 65536 - (rw + gw); + gray = (rw*red + gw*green + bw*blue)/65536; + +The default values approximate those recommended in the Charles +Poynton's Color FAQ, +Copyright (c) 1998-01-04 Charles Poynton + + Y = 0.212671 * R + 0.715160 * G + 0.072169 * B + +Libpng approximates this with + + Y = 0.21268 * R + 0.7151 * G + 0.07217 * B + +which can be expressed with integers as + + Y = (6969 * R + 23434 * G + 2365 * B)/32768 + +The calculation is done in a linear colorspace, if the image gamma +is known. + +If you have a grayscale and you are using png_set_expand_depth(), +png_set_expand(), or png_set_gray_to_rgb to change to truecolor or to +a higher bit-depth, you must either supply the background color as a gray +value at the original file bit-depth (need_expand = 1) or else supply the +background color as an RGB triplet at the final, expanded bit depth +(need_expand = 0). Similarly, if you are reading a paletted image, you +must either supply the background color as a palette index (need_expand = 1) +or as an RGB triplet that may or may not be in the palette (need_expand = 0). + + png_color_16 my_background; + png_color_16p image_background; + + if (png_get_bKGD(png_ptr, info_ptr, &image_background)) + png_set_background(png_ptr, image_background, + PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); + else + png_set_background(png_ptr, &my_background, + PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0); + +The png_set_background() function tells libpng to composite images +with alpha or simple transparency against the supplied background +color. If the PNG file contains a bKGD chunk (PNG_INFO_bKGD valid), +you may use this color, or supply another color more suitable for +the current display (e.g., the background color from a web page). You +need to tell libpng whether the color is in the gamma space of the +display (PNG_BACKGROUND_GAMMA_SCREEN for colors you supply), the file +(PNG_BACKGROUND_GAMMA_FILE for colors from the bKGD chunk), or one +that is neither of these gammas (PNG_BACKGROUND_GAMMA_UNIQUE - I don't +know why anyone would use this, but it's here). + +To properly display PNG images on any kind of system, the application needs +to know what the display gamma is. Ideally, the user will know this, and +the application will allow them to set it. One method of allowing the user +to set the display gamma separately for each system is to check for a +SCREEN_GAMMA or DISPLAY_GAMMA environment variable, which will hopefully be +correctly set. + +Note that display_gamma is the overall gamma correction required to produce +pleasing results, which depends on the lighting conditions in the surrounding +environment. In a dim or brightly lit room, no compensation other than +the physical gamma exponent of the monitor is needed, while in a dark room +a slightly smaller exponent is better. + + double gamma, screen_gamma; + + if (/* We have a user-defined screen + gamma value */) + { + screen_gamma = user_defined_screen_gamma; + } + /* One way that applications can share the same + screen gamma value */ + else if ((gamma_str = getenv("SCREEN_GAMMA")) + != NULL) + { + screen_gamma = (double)atof(gamma_str); + } + /* If we don't have another value */ + else + { + screen_gamma = 2.2; /* A good guess for a + PC monitor in a bright office or a dim room */ + screen_gamma = 2.0; /* A good guess for a + PC monitor in a dark room */ + screen_gamma = 1.7 or 1.0; /* A good + guess for Mac systems */ + } + +The png_set_gamma() function handles gamma transformations of the data. +Pass both the file gamma and the current screen_gamma. If the file does +not have a gamma value, you can pass one anyway if you have an idea what +it is (usually 0.45455 is a good guess for GIF images on PCs). Note +that file gammas are inverted from screen gammas. See the discussions +on gamma in the PNG specification for an excellent description of what +gamma is, and why all applications should support it. It is strongly +recommended that PNG viewers support gamma correction. + + if (png_get_gAMA(png_ptr, info_ptr, &gamma)) + png_set_gamma(png_ptr, screen_gamma, gamma); + else + png_set_gamma(png_ptr, screen_gamma, 0.45455); + +If you need to reduce an RGB file to a paletted file, or if a paletted +file has more entries then will fit on your screen, png_set_dither() +will do that. Note that this is a simple match dither that merely +finds the closest color available. This should work fairly well with +optimized palettes, and fairly badly with linear color cubes. If you +pass a palette that is larger then maximum_colors, the file will +reduce the number of colors in the palette so it will fit into +maximum_colors. If there is a histogram, it will use it to make +more intelligent choices when reducing the palette. If there is no +histogram, it may not do as good a job. + + if (color_type & PNG_COLOR_MASK_COLOR) + { + if (png_get_valid(png_ptr, info_ptr, + PNG_INFO_PLTE)) + { + png_uint_16p histogram = NULL; + + png_get_hIST(png_ptr, info_ptr, + &histogram); + png_set_dither(png_ptr, palette, num_palette, + max_screen_colors, histogram, 1); + } + else + { + png_color std_color_cube[MAX_SCREEN_COLORS] = + { ... colors ... }; + + png_set_dither(png_ptr, std_color_cube, + MAX_SCREEN_COLORS, MAX_SCREEN_COLORS, + NULL,0); + } + } + +PNG files describe monochrome as black being zero and white being one. +The following code will reverse this (make black be one and white be +zero): + + if (bit_depth == 1 && color_type == PNG_COLOR_TYPE_GRAY) + png_set_invert_mono(png_ptr); + +This function can also be used to invert grayscale and gray-alpha images: + + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_invert_mono(png_ptr); + +PNG files store 16 bit pixels in network byte order (big-endian, +ie. most significant bits first). This code changes the storage to the +other way (little-endian, i.e. least significant bits first, the +way PCs store them): + + if (bit_depth == 16) + png_set_swap(png_ptr); + +If you are using packed-pixel images (1, 2, or 4 bits/pixel), and you +need to change the order the pixels are packed into bytes, you can use: + + if (bit_depth < 8) + png_set_packswap(png_ptr); + +Finally, you can write your own transformation function if none of +the existing ones meets your needs. This is done by setting a callback +with + + png_set_read_user_transform_fn(png_ptr, + read_transform_fn); + +You must supply the function + + void read_transform_fn(png_ptr ptr, row_info_ptr + row_info, png_bytep data) + +See pngtest.c for a working example. Your function will be called +after all of the other transformations have been processed. + +You can also set up a pointer to a user structure for use by your +callback function, and you can inform libpng that your transform +function will change the number of channels or bit depth with the +function + + png_set_user_transform_info(png_ptr, user_ptr, + user_depth, user_channels); + +The user's application, not libpng, is responsible for allocating and +freeing any memory required for the user structure. + +You can retrieve the pointer via the function +png_get_user_transform_ptr(). For example: + + voidp read_user_transform_ptr = + png_get_user_transform_ptr(png_ptr); + +The last thing to handle is interlacing; this is covered in detail below, +but you must call the function here if you want libpng to handle expansion +of the interlaced image. + + number_of_passes = png_set_interlace_handling(png_ptr); + +After setting the transformations, libpng can update your png_info +structure to reflect any transformations you've requested with this +call. This is most useful to update the info structure's rowbytes +field so you can use it to allocate your image memory. This function +will also update your palette with the correct screen_gamma and +background if these have been given with the calls above. + + png_read_update_info(png_ptr, info_ptr); + +After you call png_read_update_info(), you can allocate any +memory you need to hold the image. The row data is simply +raw byte data for all forms of images. As the actual allocation +varies among applications, no example will be given. If you +are allocating one large chunk, you will need to build an +array of pointers to each row, as it will be needed for some +of the functions below. + +.SS Reading image data + +After you've allocated memory, you can read the image data. +The simplest way to do this is in one function call. If you are +allocating enough memory to hold the whole image, you can just +call png_read_image() and libpng will read in all the image data +and put it in the memory area supplied. You will need to pass in +an array of pointers to each row. + +This function automatically handles interlacing, so you don't need +to call png_set_interlace_handling() or call this function multiple +times, or any of that other stuff necessary with png_read_rows(). + + png_read_image(png_ptr, row_pointers); + +where row_pointers is: + + png_bytep row_pointers[height]; + +You can point to void or char or whatever you use for pixels. + +If you don't want to read in the whole image at once, you can +use png_read_rows() instead. If there is no interlacing (check +interlace_type == PNG_INTERLACE_NONE), this is simple: + + png_read_rows(png_ptr, row_pointers, NULL, + number_of_rows); + +where row_pointers is the same as in the png_read_image() call. + +If you are doing this just one row at a time, you can do this with +a single row_pointer instead of an array of row_pointers: + + png_bytep row_pointer = row; + png_read_row(png_ptr, row_pointer, NULL); + +If the file is interlaced (interlace_type != 0 in the IHDR chunk), things +get somewhat harder. The only current (PNG Specification version 1.2) +interlacing type for PNG is (interlace_type == PNG_INTERLACE_ADAM7) +is a somewhat complicated 2D interlace scheme, known as Adam7, that +breaks down an image into seven smaller images of varying size, based +on an 8x8 grid. + +libpng can fill out those images or it can give them to you "as is". +If you want them filled out, there are two ways to do that. The one +mentioned in the PNG specification is to expand each pixel to cover +those pixels that have not been read yet (the "rectangle" method). +This results in a blocky image for the first pass, which gradually +smooths out as more pixels are read. The other method is the "sparkle" +method, where pixels are drawn only in their final locations, with the +rest of the image remaining whatever colors they were initialized to +before the start of the read. The first method usually looks better, +but tends to be slower, as there are more pixels to put in the rows. + +If you don't want libpng to handle the interlacing details, just call +png_read_rows() seven times to read in all seven images. Each of the +images is a valid image by itself, or they can all be combined on an +8x8 grid to form a single image (although if you intend to combine them +you would be far better off using the libpng interlace handling). + +The first pass will return an image 1/8 as wide as the entire image +(every 8th column starting in column 0) and 1/8 as high as the original +(every 8th row starting in row 0), the second will be 1/8 as wide +(starting in column 4) and 1/8 as high (also starting in row 0). The +third pass will be 1/4 as wide (every 4th pixel starting in column 0) and +1/8 as high (every 8th row starting in row 4), and the fourth pass will +be 1/4 as wide and 1/4 as high (every 4th column starting in column 2, +and every 4th row starting in row 0). The fifth pass will return an +image 1/2 as wide, and 1/4 as high (starting at column 0 and row 2), +while the sixth pass will be 1/2 as wide and 1/2 as high as the original +(starting in column 1 and row 0). The seventh and final pass will be as +wide as the original, and 1/2 as high, containing all of the odd +numbered scanlines. Phew! + +If you want libpng to expand the images, call this before calling +png_start_read_image() or png_read_update_info(): + + if (interlace_type == PNG_INTERLACE_ADAM7) + number_of_passes + = png_set_interlace_handling(png_ptr); + +This will return the number of passes needed. Currently, this +is seven, but may change if another interlace type is added. +This function can be called even if the file is not interlaced, +where it will return one pass. + +If you are not going to display the image after each pass, but are +going to wait until the entire image is read in, use the sparkle +effect. This effect is faster and the end result of either method +is exactly the same. If you are planning on displaying the image +after each pass, the "rectangle" effect is generally considered the +better looking one. + +If you only want the "sparkle" effect, just call png_read_rows() as +normal, with the third parameter NULL. Make sure you make pass over +the image number_of_passes times, and you don't change the data in the +rows between calls. You can change the locations of the data, just +not the data. Each pass only writes the pixels appropriate for that +pass, and assumes the data from previous passes is still valid. + + png_read_rows(png_ptr, row_pointers, NULL, + number_of_rows); + +If you only want the first effect (the rectangles), do the same as +before except pass the row buffer in the third parameter, and leave +the second parameter NULL. + + png_read_rows(png_ptr, NULL, row_pointers, + number_of_rows); + +.SS Finishing a sequential read + +After you are finished reading the image through either the high- or +low-level interfaces, you can finish reading the file. If you are +interested in comments or time, which may be stored either before or +after the image data, you should pass the separate png_info struct if +you want to keep the comments from before and after the image +separate. If you are not interested, you can pass NULL. + + png_read_end(png_ptr, end_info); + +When you are done, you can free all memory allocated by libpng like this: + + png_destroy_read_struct(&png_ptr, &info_ptr, + &end_info); + +It is also possible to individually free the info_ptr members that +point to libpng-allocated storage with the following function: + + png_free_data(png_ptr, info_ptr, mask, seq) + mask - identifies data to be freed, a mask + containing the logical OR of one or + more of + PNG_FREE_PLTE, PNG_FREE_TRNS, + PNG_FREE_HIST, PNG_FREE_ICCP, + PNG_FREE_PCAL, PNG_FREE_ROWS, + PNG_FREE_SCAL, PNG_FREE_SPLT, + PNG_FREE_TEXT, PNG_FREE_UNKN, + or simply PNG_FREE_ALL + seq - sequence number of item to be freed + (-1 for all items) + +This function may be safely called when the relevant storage has +already been freed, or has not yet been allocated, or was allocated +by the user and not by libpng, and will in those +cases do nothing. The "seq" parameter is ignored if only one item +of the selected data type, such as PLTE, is allowed. If "seq" is not +-1, and multiple items are allowed for the data type identified in +the mask, such as text or sPLT, only the n'th item in the structure +is freed, where n is "seq". + +The default behavior is only to free data that was allocated internally +by libpng. This can be changed, so that libpng will not free the data, +or so that it will free data that was allocated by the user with png_malloc() +or png_zalloc() and passed in via a png_set_*() function, with + + png_data_freer(png_ptr, info_ptr, freer, mask) + mask - which data elements are affected + same choices as in png_free_data() + freer - one of + PNG_DESTROY_WILL_FREE_DATA + PNG_SET_WILL_FREE_DATA + PNG_USER_WILL_FREE_DATA + +This function only affects data that has already been allocated. +You can call this function after reading the PNG data but before calling +any png_set_*() functions, to control whether the user or the png_set_*() +function is responsible for freeing any existing data that might be present, +and again after the png_set_*() functions to control whether the user +or png_destroy_*() is supposed to free the data. When the user assumes +responsibility for libpng-allocated data, the application must use +png_free() to free it, and when the user transfers responsibility to libpng +for data that the user has allocated, the user must have used png_malloc() +or png_zalloc() to allocate it. + +If you allocated your row_pointers in a single block, as suggested above in +the description of the high level read interface, you must not transfer +responsibility for freeing it to the png_set_rows or png_read_destroy function, +because they would also try to free the individual row_pointers[i]. + +If you allocated text_ptr.text, text_ptr.lang, and text_ptr.translated_keyword +separately, do not transfer responsibility for freeing text_ptr to libpng, +because when libpng fills a png_text structure it combines these members with +the key member, and png_free_data() will free only text_ptr.key. Similarly, +if you transfer responsibility for free'ing text_ptr from libpng to your +application, your application must not separately free those members. + +The png_free_data() function will turn off the "valid" flag for anything +it frees. If you need to turn the flag off for a chunk that was freed by your +application instead of by libpng, you can use + + png_set_invalid(png_ptr, info_ptr, mask); + mask - identifies the chunks to be made invalid, + containing the logical OR of one or + more of + PNG_INFO_gAMA, PNG_INFO_sBIT, + PNG_INFO_cHRM, PNG_INFO_PLTE, + PNG_INFO_tRNS, PNG_INFO_bKGD, + PNG_INFO_hIST, PNG_INFO_pHYs, + PNG_INFO_oFFs, PNG_INFO_tIME, + PNG_INFO_pCAL, PNG_INFO_sRGB, + PNG_INFO_iCCP, PNG_INFO_sPLT, + PNG_INFO_sCAL, PNG_INFO_IDAT + +For a more compact example of reading a PNG image, see the file example.c. + +.SS Reading PNG files progressively + +The progressive reader is slightly different then the non-progressive +reader. Instead of calling png_read_info(), png_read_rows(), and +png_read_end(), you make one call to png_process_data(), which calls +callbacks when it has the info, a row, or the end of the image. You +set up these callbacks with png_set_progressive_read_fn(). You don't +have to worry about the input/output functions of libpng, as you are +giving the library the data directly in png_process_data(). I will +assume that you have read the section on reading PNG files above, +so I will only highlight the differences (although I will show +all of the code). + +png_structp png_ptr; +png_infop info_ptr; + + /* An example code fragment of how you would + initialize the progressive reader in your + application. */ + int + initialize_png_reader() + { + png_ptr = png_create_read_struct + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn); + if (!png_ptr) + return (ERROR); + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, + (png_infopp)NULL); + return (ERROR); + } + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, + (png_infopp)NULL); + return (ERROR); + } + + /* This one's new. You can provide functions + to be called when the header info is valid, + when each row is completed, and when the image + is finished. If you aren't using all functions, + you can specify NULL parameters. Even when all + three functions are NULL, you need to call + png_set_progressive_read_fn(). You can use + any struct as the user_ptr (cast to a void pointer + for the function call), and retrieve the pointer + from inside the callbacks using the function + + png_get_progressive_ptr(png_ptr); + + which will return a void pointer, which you have + to cast appropriately. + */ + png_set_progressive_read_fn(png_ptr, (void *)user_ptr, + info_callback, row_callback, end_callback); + + return 0; + } + + /* A code fragment that you call as you receive blocks + of data */ + int + process_data(png_bytep buffer, png_uint_32 length) + { + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, + (png_infopp)NULL); + return (ERROR); + } + + /* This one's new also. Simply give it a chunk + of data from the file stream (in order, of + course). On machines with segmented memory + models machines, don't give it any more than + 64K. The library seems to run fine with sizes + of 4K. Although you can give it much less if + necessary (I assume you can give it chunks of + 1 byte, I haven't tried less then 256 bytes + yet). When this function returns, you may + want to display any rows that were generated + in the row callback if you don't already do + so there. + */ + png_process_data(png_ptr, info_ptr, buffer, length); + return 0; + } + + /* This function is called (as set by + png_set_progressive_read_fn() above) when enough data + has been supplied so all of the header has been + read. + */ + void + info_callback(png_structp png_ptr, png_infop info) + { + /* Do any setup here, including setting any of + the transformations mentioned in the Reading + PNG files section. For now, you _must_ call + either png_start_read_image() or + png_read_update_info() after all the + transformations are set (even if you don't set + any). You may start getting rows before + png_process_data() returns, so this is your + last chance to prepare for that. + */ + } + + /* This function is called when each row of image + data is complete */ + void + row_callback(png_structp png_ptr, png_bytep new_row, + png_uint_32 row_num, int pass) + { + /* If the image is interlaced, and you turned + on the interlace handler, this function will + be called for every row in every pass. Some + of these rows will not be changed from the + previous pass. When the row is not changed, + the new_row variable will be NULL. The rows + and passes are called in order, so you don't + really need the row_num and pass, but I'm + supplying them because it may make your life + easier. + + For the non-NULL rows of interlaced images, + you must call png_progressive_combine_row() + passing in the row and the old row. You can + call this function for NULL rows (it will just + return) and for non-interlaced images (it just + does the memcpy for you) if it will make the + code easier. Thus, you can just do this for + all cases: + */ + + png_progressive_combine_row(png_ptr, old_row, + new_row); + + /* where old_row is what was displayed for + previously for the row. Note that the first + pass (pass == 0, really) will completely cover + the old row, so the rows do not have to be + initialized. After the first pass (and only + for interlaced images), you will have to pass + the current row, and the function will combine + the old row and the new row. + */ + } + + void + end_callback(png_structp png_ptr, png_infop info) + { + /* This function is called after the whole image + has been read, including any chunks after the + image (up to and including the IEND). You + will usually have the same info chunk as you + had in the header, although some data may have + been added to the comments and time fields. + + Most people won't do much here, perhaps setting + a flag that marks the image as finished. + */ + } + + + +.SH IV. Writing + +Much of this is very similar to reading. However, everything of +importance is repeated here, so you won't have to constantly look +back up in the reading section to understand writing. + +.SS Setup + +You will want to do the I/O initialization before you get into libpng, +so if it doesn't work, you don't have anything to undo. If you are not +using the standard I/O functions, you will need to replace them with +custom writing functions. See the discussion under Customizing libpng. + + FILE *fp = fopen(file_name, "wb"); + if (!fp) + { + return (ERROR); + } + +Next, png_struct and png_info need to be allocated and initialized. +As these can be both relatively large, you may not want to store these +on the stack, unless you have stack space to spare. Of course, you +will want to check if they return NULL. If you are also reading, +you won't want to name your read structure and your write structure +both "png_ptr"; you can call them anything you like, such as +"read_ptr" and "write_ptr". Look at pngtest.c, for example. + + png_structp png_ptr = png_create_write_struct + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn); + if (!png_ptr) + return (ERROR); + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_write_struct(&png_ptr, + (png_infopp)NULL); + return (ERROR); + } + +If you want to use your own memory allocation routines, +define PNG_USER_MEM_SUPPORTED and use +png_create_write_struct_2() instead of png_create_write_struct(): + + png_structp png_ptr = png_create_write_struct_2 + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn, (png_voidp) + user_mem_ptr, user_malloc_fn, user_free_fn); + +After you have these structures, you will need to set up the +error handling. When libpng encounters an error, it expects to +longjmp() back to your routine. Therefore, you will need to call +setjmp() and pass the png_jmpbuf(png_ptr). If you +write the file from different routines, you will need to update +the png_jmpbuf(png_ptr) every time you enter a new routine that will +call a png_*() function. See your documentation of setjmp/longjmp +for your compiler for more information on setjmp/longjmp. See +the discussion on libpng error handling in the Customizing Libpng +section below for more information on the libpng error handling. + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_write_struct(&png_ptr, &info_ptr); + fclose(fp); + return (ERROR); + } + ... + return; + +If you would rather avoid the complexity of setjmp/longjmp issues, +you can compile libpng with PNG_SETJMP_NOT_SUPPORTED, in which case +errors will result in a call to PNG_ABORT() which defaults to abort(). + +Now you need to set up the output code. The default for libpng is to +use the C function fwrite(). If you use this, you will need to pass a +valid FILE * in the function png_init_io(). Be sure that the file is +opened in binary mode. Again, if you wish to handle writing data in +another way, see the discussion on libpng I/O handling in the Customizing +Libpng section below. + + png_init_io(png_ptr, fp); + +.SS Write callbacks + +At this point, you can set up a callback function that will be +called after each row has been written, which you can use to control +a progress meter or the like. It's demonstrated in pngtest.c. +You must supply a function + + void write_row_callback(png_ptr, png_uint_32 row, + int pass); + { + /* put your code here */ + } + +(You can give it another name that you like instead of "write_row_callback") + +To inform libpng about your function, use + + png_set_write_status_fn(png_ptr, write_row_callback); + +You now have the option of modifying how the compression library will +run. The following functions are mainly for testing, but may be useful +in some cases, like if you need to write PNG files extremely fast and +are willing to give up some compression, or if you want to get the +maximum possible compression at the expense of slower writing. If you +have no special needs in this area, let the library do what it wants by +not calling this function at all, as it has been tuned to deliver a good +speed/compression ratio. The second parameter to png_set_filter() is +the filter method, for which the only valid values are 0 (as of the +July 1999 PNG specification, version 1.2) or 64 (if you are writing +a PNG datastream that is to be embedded in a MNG datastream). The third +parameter is a flag that indicates which filter type(s) are to be tested +for each scanline. See the PNG specification for details on the specific filter +types. + + + /* turn on or off filtering, and/or choose + specific filters. You can use either a single + PNG_FILTER_VALUE_NAME or the logical OR of one + or more PNG_FILTER_NAME masks. */ + png_set_filter(png_ptr, 0, + PNG_FILTER_NONE | PNG_FILTER_VALUE_NONE | + PNG_FILTER_SUB | PNG_FILTER_VALUE_SUB | + PNG_FILTER_UP | PNG_FILTER_VALUE_UP | + PNG_FILTER_AVE | PNG_FILTER_VALUE_AVE | + PNG_FILTER_PAETH | PNG_FILTER_VALUE_PAETH| + PNG_ALL_FILTERS); + +If an application +wants to start and stop using particular filters during compression, +it should start out with all of the filters (to ensure that the previous +row of pixels will be stored in case it's needed later), and then add +and remove them after the start of compression. + +If you are writing a PNG datastream that is to be embedded in a MNG +datastream, the second parameter can be either 0 or 64. + +The png_set_compression_*() functions interface to the zlib compression +library, and should mostly be ignored unless you really know what you are +doing. The only generally useful call is png_set_compression_level() +which changes how much time zlib spends on trying to compress the image +data. See the Compression Library (zlib.h and algorithm.txt, distributed +with zlib) for details on the compression levels. + + /* set the zlib compression level */ + png_set_compression_level(png_ptr, + Z_BEST_COMPRESSION); + + /* set other zlib parameters */ + png_set_compression_mem_level(png_ptr, 8); + png_set_compression_strategy(png_ptr, + Z_DEFAULT_STRATEGY); + png_set_compression_window_bits(png_ptr, 15); + png_set_compression_method(png_ptr, 8); + png_set_compression_buffer_size(png_ptr, 8192) + +extern PNG_EXPORT(void,png_set_zbuf_size) + +.SS Setting the contents of info for output + +You now need to fill in the png_info structure with all the data you +wish to write before the actual image. Note that the only thing you +are allowed to write after the image is the text chunks and the time +chunk (as of PNG Specification 1.2, anyway). See png_write_end() and +the latest PNG specification for more information on that. If you +wish to write them before the image, fill them in now, and flag that +data as being valid. If you want to wait until after the data, don't +fill them until png_write_end(). For all the fields in png_info and +their data types, see png.h. For explanations of what the fields +contain, see the PNG specification. + +Some of the more important parts of the png_info are: + + png_set_IHDR(png_ptr, info_ptr, width, height, + bit_depth, color_type, interlace_type, + compression_type, filter_method) + width - holds the width of the image + in pixels (up to 2^31). + height - holds the height of the image + in pixels (up to 2^31). + bit_depth - holds the bit depth of one of the + image channels. + (valid values are 1, 2, 4, 8, 16 + and depend also on the + color_type. See also significant + bits (sBIT) below). + color_type - describes which color/alpha + channels are present. + PNG_COLOR_TYPE_GRAY + (bit depths 1, 2, 4, 8, 16) + PNG_COLOR_TYPE_GRAY_ALPHA + (bit depths 8, 16) + PNG_COLOR_TYPE_PALETTE + (bit depths 1, 2, 4, 8) + PNG_COLOR_TYPE_RGB + (bit_depths 8, 16) + PNG_COLOR_TYPE_RGB_ALPHA + (bit_depths 8, 16) + + PNG_COLOR_MASK_PALETTE + PNG_COLOR_MASK_COLOR + PNG_COLOR_MASK_ALPHA + + interlace_type - PNG_INTERLACE_NONE or + PNG_INTERLACE_ADAM7 + compression_type - (must be + PNG_COMPRESSION_TYPE_DEFAULT) + filter_method - (must be PNG_FILTER_TYPE_DEFAULT + or, if you are writing a PNG to + be embedded in a MNG datastream, + can also be + PNG_INTRAPIXEL_DIFFERENCING) + + png_set_PLTE(png_ptr, info_ptr, palette, + num_palette); + palette - the palette for the file + (array of png_color) + num_palette - number of entries in the palette + + png_set_gAMA(png_ptr, info_ptr, gamma); + gamma - the gamma the image was created + at (PNG_INFO_gAMA) + + png_set_sRGB(png_ptr, info_ptr, srgb_intent); + srgb_intent - the rendering intent + (PNG_INFO_sRGB) The presence of + the sRGB chunk means that the pixel + data is in the sRGB color space. + This chunk also implies specific + values of gAMA and cHRM. Rendering + intent is the CSS-1 property that + has been defined by the International + Color Consortium + (http://www.color.org). + It can be one of + PNG_sRGB_INTENT_SATURATION, + PNG_sRGB_INTENT_PERCEPTUAL, + PNG_sRGB_INTENT_ABSOLUTE, or + PNG_sRGB_INTENT_RELATIVE. + + + png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, + srgb_intent); + srgb_intent - the rendering intent + (PNG_INFO_sRGB) The presence of the + sRGB chunk means that the pixel + data is in the sRGB color space. + This function also causes gAMA and + cHRM chunks with the specific values + that are consistent with sRGB to be + written. + + png_set_iCCP(png_ptr, info_ptr, name, compression_type, + profile, proflen); + name - The profile name. + compression - The compression type; always + PNG_COMPRESSION_TYPE_BASE for PNG 1.0. + You may give NULL to this argument to + ignore it. + profile - International Color Consortium color + profile data. May contain NULs. + proflen - length of profile data in bytes. + + png_set_sBIT(png_ptr, info_ptr, sig_bit); + sig_bit - the number of significant bits for + (PNG_INFO_sBIT) each of the gray, red, + green, and blue channels, whichever are + appropriate for the given color type + (png_color_16) + + png_set_tRNS(png_ptr, info_ptr, trans, num_trans, + trans_values); + trans - array of transparent entries for + palette (PNG_INFO_tRNS) + trans_values - graylevel or color sample values of + the single transparent color for + non-paletted images (PNG_INFO_tRNS) + num_trans - number of transparent entries + (PNG_INFO_tRNS) + + png_set_hIST(png_ptr, info_ptr, hist); + (PNG_INFO_hIST) + hist - histogram of palette (array of + png_uint_16) + + png_set_tIME(png_ptr, info_ptr, mod_time); + mod_time - time image was last modified + (PNG_VALID_tIME) + + png_set_bKGD(png_ptr, info_ptr, background); + background - background color (PNG_VALID_bKGD) + + png_set_text(png_ptr, info_ptr, text_ptr, num_text); + text_ptr - array of png_text holding image + comments + text_ptr[i].compression - type of compression used + on "text" PNG_TEXT_COMPRESSION_NONE + PNG_TEXT_COMPRESSION_zTXt + PNG_ITXT_COMPRESSION_NONE + PNG_ITXT_COMPRESSION_zTXt + text_ptr[i].key - keyword for comment. Must contain + 1-79 characters. + text_ptr[i].text - text comments for current + keyword. Can be NULL or empty. + text_ptr[i].text_length - length of text string, + after decompression, 0 for iTXt + text_ptr[i].itxt_length - length of itxt string, + after decompression, 0 for tEXt/zTXt + text_ptr[i].lang - language of comment (NULL or + empty for unknown). + text_ptr[i].translated_keyword - keyword in UTF-8 (NULL + or empty for unknown). + num_text - number of comments + + png_set_sPLT(png_ptr, info_ptr, &palette_ptr, + num_spalettes); + palette_ptr - array of png_sPLT_struct structures + to be added to the list of palettes + in the info structure. + num_spalettes - number of palette structures to be + added. + + png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, + unit_type); + offset_x - positive offset from the left + edge of the screen + offset_y - positive offset from the top + edge of the screen + unit_type - PNG_OFFSET_PIXEL, PNG_OFFSET_MICROMETER + + png_set_pHYs(png_ptr, info_ptr, res_x, res_y, + unit_type); + res_x - pixels/unit physical resolution + in x direction + res_y - pixels/unit physical resolution + in y direction + unit_type - PNG_RESOLUTION_UNKNOWN, + PNG_RESOLUTION_METER + + png_set_sCAL(png_ptr, info_ptr, unit, width, height) + unit - physical scale units (an integer) + width - width of a pixel in physical scale units + height - height of a pixel in physical scale units + (width and height are doubles) + + png_set_sCAL_s(png_ptr, info_ptr, unit, width, height) + unit - physical scale units (an integer) + width - width of a pixel in physical scale units + height - height of a pixel in physical scale units + (width and height are strings like "2.54") + + png_set_unknown_chunks(png_ptr, info_ptr, &unknowns, + num_unknowns) + unknowns - array of png_unknown_chunk + structures holding unknown chunks + unknowns[i].name - name of unknown chunk + unknowns[i].data - data of unknown chunk + unknowns[i].size - size of unknown chunk's data + unknowns[i].location - position to write chunk in file + 0: do not write chunk + PNG_HAVE_IHDR: before PLTE + PNG_HAVE_PLTE: before IDAT + PNG_AFTER_IDAT: after IDAT + +The "location" member is set automatically according to +what part of the output file has already been written. +You can change its value after calling png_set_unknown_chunks() +as demonstrated in pngtest.c. Within each of the "locations", +the chunks are sequenced according to their position in the +structure (that is, the value of "i", which is the order in which +the chunk was either read from the input file or defined with +png_set_unknown_chunks). + +A quick word about text and num_text. text is an array of png_text +structures. num_text is the number of valid structures in the array. +Each png_text structure holds a language code, a keyword, a text value, +and a compression type. + +The compression types have the same valid numbers as the compression +types of the image data. Currently, the only valid number is zero. +However, you can store text either compressed or uncompressed, unlike +images, which always have to be compressed. So if you don't want the +text compressed, set the compression type to PNG_TEXT_COMPRESSION_NONE. +Because tEXt and zTXt chunks don't have a language field, if you +specify PNG_TEXT_COMPRESSION_NONE or PNG_TEXT_COMPRESSION_zTXt +any language code or translated keyword will not be written out. + +Until text gets around 1000 bytes, it is not worth compressing it. +After the text has been written out to the file, the compression type +is set to PNG_TEXT_COMPRESSION_NONE_WR or PNG_TEXT_COMPRESSION_zTXt_WR, +so that it isn't written out again at the end (in case you are calling +png_write_end() with the same struct. + +The keywords that are given in the PNG Specification are: + + Title Short (one line) title or + caption for image + Author Name of image's creator + Description Description of image (possibly long) + Copyright Copyright notice + Creation Time Time of original image creation + (usually RFC 1123 format, see below) + Software Software used to create the image + Disclaimer Legal disclaimer + Warning Warning of nature of content + Source Device used to create the image + Comment Miscellaneous comment; conversion + from other image format + +The keyword-text pairs work like this. Keywords should be short +simple descriptions of what the comment is about. Some typical +keywords are found in the PNG specification, as is some recommendations +on keywords. You can repeat keywords in a file. You can even write +some text before the image and some after. For example, you may want +to put a description of the image before the image, but leave the +disclaimer until after, so viewers working over modem connections +don't have to wait for the disclaimer to go over the modem before +they start seeing the image. Finally, keywords should be full +words, not abbreviations. Keywords and text are in the ISO 8859-1 +(Latin-1) character set (a superset of regular ASCII) and can not +contain NUL characters, and should not contain control or other +unprintable characters. To make the comments widely readable, stick +with basic ASCII, and avoid machine specific character set extensions +like the IBM-PC character set. The keyword must be present, but +you can leave off the text string on non-compressed pairs. +Compressed pairs must have a text string, as only the text string +is compressed anyway, so the compression would be meaningless. + +PNG supports modification time via the png_time structure. Two +conversion routines are provided, png_convert_from_time_t() for +time_t and png_convert_from_struct_tm() for struct tm. The +time_t routine uses gmtime(). You don't have to use either of +these, but if you wish to fill in the png_time structure directly, +you should provide the time in universal time (GMT) if possible +instead of your local time. Note that the year number is the full +year (e.g. 1998, rather than 98 - PNG is year 2000 compliant!), and +that months start with 1. + +If you want to store the time of the original image creation, you should +use a plain tEXt chunk with the "Creation Time" keyword. This is +necessary because the "creation time" of a PNG image is somewhat vague, +depending on whether you mean the PNG file, the time the image was +created in a non-PNG format, a still photo from which the image was +scanned, or possibly the subject matter itself. In order to facilitate +machine-readable dates, it is recommended that the "Creation Time" +tEXt chunk use RFC 1123 format dates (e.g. "22 May 1997 18:07:10 GMT"), +although this isn't a requirement. Unlike the tIME chunk, the +"Creation Time" tEXt chunk is not expected to be automatically changed +by the software. To facilitate the use of RFC 1123 dates, a function +png_convert_to_rfc1123(png_timep) is provided to convert from PNG +time to an RFC 1123 format string. + +.SS Writing unknown chunks + +You can use the png_set_unknown_chunks function to queue up chunks +for writing. You give it a chunk name, raw data, and a size; that's +all there is to it. The chunks will be written by the next following +png_write_info_before_PLTE, png_write_info, or png_write_end function. +Any chunks previously read into the info structure's unknown-chunk +list will also be written out in a sequence that satisfies the PNG +specification's ordering rules. + +.SS The high-level write interface + +At this point there are two ways to proceed; through the high-level +write interface, or through a sequence of low-level write operations. +You can use the high-level interface if your image data is present +in the info structure. All defined output +transformations are permitted, enabled by the following masks. + + PNG_TRANSFORM_IDENTITY No transformation + PNG_TRANSFORM_PACKING Pack 1, 2 and 4-bit samples + PNG_TRANSFORM_PACKSWAP Change order of packed + pixels to LSB first + PNG_TRANSFORM_INVERT_MONO Invert monochrome images + PNG_TRANSFORM_SHIFT Normalize pixels to the + sBIT depth + PNG_TRANSFORM_BGR Flip RGB to BGR, RGBA + to BGRA + PNG_TRANSFORM_SWAP_ALPHA Flip RGBA to ARGB or GA + to AG + PNG_TRANSFORM_INVERT_ALPHA Change alpha from opacity + to transparency + PNG_TRANSFORM_SWAP_ENDIAN Byte-swap 16-bit samples + PNG_TRANSFORM_STRIP_FILLER Strip out filler bytes. + +If you have valid image data in the info structure (you can use +png_set_rows() to put image data in the info structure), simply do this: + + png_write_png(png_ptr, info_ptr, png_transforms, NULL) + +where png_transforms is an integer containing the logical OR of some set of +transformation flags. This call is equivalent to png_write_info(), +followed the set of transformations indicated by the transform mask, +then png_write_image(), and finally png_write_end(). + +(The final parameter of this call is not yet used. Someday it might point +to transformation parameters required by some future output transform.) + +You must use png_transforms and not call any png_set_transform() functions +when you use png_write_png(). + +.SS The low-level write interface + +If you are going the low-level route instead, you are now ready to +write all the file information up to the actual image data. You do +this with a call to png_write_info(). + + png_write_info(png_ptr, info_ptr); + +Note that there is one transformation you may need to do before +png_write_info(). In PNG files, the alpha channel in an image is the +level of opacity. If your data is supplied as a level of +transparency, you can invert the alpha channel before you write it, so +that 0 is fully transparent and 255 (in 8-bit or paletted images) or +65535 (in 16-bit images) is fully opaque, with + + png_set_invert_alpha(png_ptr); + +This must appear before png_write_info() instead of later with the +other transformations because in the case of paletted images the tRNS +chunk data has to be inverted before the tRNS chunk is written. If +your image is not a paletted image, the tRNS data (which in such cases +represents a single color to be rendered as transparent) won't need to +be changed, and you can safely do this transformation after your +png_write_info() call. + +If you need to write a private chunk that you want to appear before +the PLTE chunk when PLTE is present, you can write the PNG info in +two steps, and insert code to write your own chunk between them: + + png_write_info_before_PLTE(png_ptr, info_ptr); + png_set_unknown_chunks(png_ptr, info_ptr, ...); + png_write_info(png_ptr, info_ptr); + +After you've written the file information, you can set up the library +to handle any special transformations of the image data. The various +ways to transform the data will be described in the order that they +should occur. This is important, as some of these change the color +type and/or bit depth of the data, and some others only work on +certain color types and bit depths. Even though each transformation +checks to see if it has data that it can do something with, you should +make sure to only enable a transformation if it will be valid for the +data. For example, don't swap red and blue on grayscale data. + +PNG files store RGB pixels packed into 3 or 6 bytes. This code tells +the library to strip input data that has 4 or 8 bytes per pixel down +to 3 or 6 bytes (or strip 2 or 4-byte grayscale+filler data to 1 or 2 +bytes per pixel). + + png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); + +where the 0 is unused, and the location is either PNG_FILLER_BEFORE or +PNG_FILLER_AFTER, depending upon whether the filler byte in the pixel +is stored XRGB or RGBX. + +PNG files pack pixels of bit depths 1, 2, and 4 into bytes as small as +they can, resulting in, for example, 8 pixels per byte for 1 bit files. +If the data is supplied at 1 pixel per byte, use this code, which will +correctly pack the pixels into a single byte: + + png_set_packing(png_ptr); + +PNG files reduce possible bit depths to 1, 2, 4, 8, and 16. If your +data is of another bit depth, you can write an sBIT chunk into the +file so that decoders can recover the original data if desired. + + /* Set the true bit depth of the image data */ + if (color_type & PNG_COLOR_MASK_COLOR) + { + sig_bit.red = true_bit_depth; + sig_bit.green = true_bit_depth; + sig_bit.blue = true_bit_depth; + } + else + { + sig_bit.gray = true_bit_depth; + } + if (color_type & PNG_COLOR_MASK_ALPHA) + { + sig_bit.alpha = true_bit_depth; + } + + png_set_sBIT(png_ptr, info_ptr, &sig_bit); + +If the data is stored in the row buffer in a bit depth other than +one supported by PNG (e.g. 3 bit data in the range 0-7 for a 4-bit PNG), +this will scale the values to appear to be the correct bit depth as +is required by PNG. + + png_set_shift(png_ptr, &sig_bit); + +PNG files store 16 bit pixels in network byte order (big-endian, +ie. most significant bits first). This code would be used if they are +supplied the other way (little-endian, i.e. least significant bits +first, the way PCs store them): + + if (bit_depth > 8) + png_set_swap(png_ptr); + +If you are using packed-pixel images (1, 2, or 4 bits/pixel), and you +need to change the order the pixels are packed into bytes, you can use: + + if (bit_depth < 8) + png_set_packswap(png_ptr); + +PNG files store 3 color pixels in red, green, blue order. This code +would be used if they are supplied as blue, green, red: + + png_set_bgr(png_ptr); + +PNG files describe monochrome as black being zero and white being +one. This code would be used if the pixels are supplied with this reversed +(black being one and white being zero): + + png_set_invert_mono(png_ptr); + +Finally, you can write your own transformation function if none of +the existing ones meets your needs. This is done by setting a callback +with + + png_set_write_user_transform_fn(png_ptr, + write_transform_fn); + +You must supply the function + + void write_transform_fn(png_ptr ptr, row_info_ptr + row_info, png_bytep data) + +See pngtest.c for a working example. Your function will be called +before any of the other transformations are processed. + +You can also set up a pointer to a user structure for use by your +callback function. + + png_set_user_transform_info(png_ptr, user_ptr, 0, 0); + +The user_channels and user_depth parameters of this function are ignored +when writing; you can set them to zero as shown. + +You can retrieve the pointer via the function png_get_user_transform_ptr(). +For example: + + voidp write_user_transform_ptr = + png_get_user_transform_ptr(png_ptr); + +It is possible to have libpng flush any pending output, either manually, +or automatically after a certain number of lines have been written. To +flush the output stream a single time call: + + png_write_flush(png_ptr); + +and to have libpng flush the output stream periodically after a certain +number of scanlines have been written, call: + + png_set_flush(png_ptr, nrows); + +Note that the distance between rows is from the last time png_write_flush() +was called, or the first row of the image if it has never been called. +So if you write 50 lines, and then png_set_flush 25, it will flush the +output on the next scanline, and every 25 lines thereafter, unless +png_write_flush() is called before 25 more lines have been written. +If nrows is too small (less than about 10 lines for a 640 pixel wide +RGB image) the image compression may decrease noticeably (although this +may be acceptable for real-time applications). Infrequent flushing will +only degrade the compression performance by a few percent over images +that do not use flushing. + +.SS Writing the image data + +That's it for the transformations. Now you can write the image data. +The simplest way to do this is in one function call. If you have the +whole image in memory, you can just call png_write_image() and libpng +will write the image. You will need to pass in an array of pointers to +each row. This function automatically handles interlacing, so you don't +need to call png_set_interlace_handling() or call this function multiple +times, or any of that other stuff necessary with png_write_rows(). + + png_write_image(png_ptr, row_pointers); + +where row_pointers is: + + png_byte *row_pointers[height]; + +You can point to void or char or whatever you use for pixels. + +If you don't want to write the whole image at once, you can +use png_write_rows() instead. If the file is not interlaced, +this is simple: + + png_write_rows(png_ptr, row_pointers, + number_of_rows); + +row_pointers is the same as in the png_write_image() call. + +If you are just writing one row at a time, you can do this with +a single row_pointer instead of an array of row_pointers: + + png_bytep row_pointer = row; + + png_write_row(png_ptr, row_pointer); + +When the file is interlaced, things can get a good deal more +complicated. The only currently (as of the PNG Specification +version 1.2, dated July 1999) defined interlacing scheme for PNG files +is the "Adam7" interlace scheme, that breaks down an +image into seven smaller images of varying size. libpng will build +these images for you, or you can do them yourself. If you want to +build them yourself, see the PNG specification for details of which +pixels to write when. + +If you don't want libpng to handle the interlacing details, just +use png_set_interlace_handling() and call png_write_rows() the +correct number of times to write all seven sub-images. + +If you want libpng to build the sub-images, call this before you start +writing any rows: + + number_of_passes = + png_set_interlace_handling(png_ptr); + +This will return the number of passes needed. Currently, this +is seven, but may change if another interlace type is added. + +Then write the complete image number_of_passes times. + + png_write_rows(png_ptr, row_pointers, + number_of_rows); + +As some of these rows are not used, and thus return immediately, +you may want to read about interlacing in the PNG specification, +and only update the rows that are actually used. + +.SS Finishing a sequential write + +After you are finished writing the image, you should finish writing +the file. If you are interested in writing comments or time, you should +pass an appropriately filled png_info pointer. If you are not interested, +you can pass NULL. + + png_write_end(png_ptr, info_ptr); + +When you are done, you can free all memory used by libpng like this: + + png_destroy_write_struct(&png_ptr, &info_ptr); + +It is also possible to individually free the info_ptr members that +point to libpng-allocated storage with the following function: + + png_free_data(png_ptr, info_ptr, mask, seq) + mask - identifies data to be freed, a mask + containing the logical OR of one or + more of + PNG_FREE_PLTE, PNG_FREE_TRNS, + PNG_FREE_HIST, PNG_FREE_ICCP, + PNG_FREE_PCAL, PNG_FREE_ROWS, + PNG_FREE_SCAL, PNG_FREE_SPLT, + PNG_FREE_TEXT, PNG_FREE_UNKN, + or simply PNG_FREE_ALL + seq - sequence number of item to be freed + (-1 for all items) + +This function may be safely called when the relevant storage has +already been freed, or has not yet been allocated, or was allocated +by the user and not by libpng, and will in those +cases do nothing. The "seq" parameter is ignored if only one item +of the selected data type, such as PLTE, is allowed. If "seq" is not +-1, and multiple items are allowed for the data type identified in +the mask, such as text or sPLT, only the n'th item in the structure +is freed, where n is "seq". + +If you allocated data such as a palette that you passed +in to libpng with png_set_*, you must not free it until just before the call to +png_destroy_write_struct(). + +The default behavior is only to free data that was allocated internally +by libpng. This can be changed, so that libpng will not free the data, +or so that it will free data that was allocated by the user with png_malloc() +or png_zalloc() and passed in via a png_set_*() function, with + + png_data_freer(png_ptr, info_ptr, freer, mask) + mask - which data elements are affected + same choices as in png_free_data() + freer - one of + PNG_DESTROY_WILL_FREE_DATA + PNG_SET_WILL_FREE_DATA + PNG_USER_WILL_FREE_DATA + +For example, to transfer responsibility for some data from a read structure +to a write structure, you could use + + png_data_freer(read_ptr, read_info_ptr, + PNG_USER_WILL_FREE_DATA, + PNG_FREE_PLTE|PNG_FREE_tRNS|PNG_FREE_hIST) + png_data_freer(write_ptr, write_info_ptr, + PNG_DESTROY_WILL_FREE_DATA, + PNG_FREE_PLTE|PNG_FREE_tRNS|PNG_FREE_hIST) + +thereby briefly reassigning responsibility for freeing to the user but +immediately afterwards reassigning it once more to the write_destroy +function. Having done this, it would then be safe to destroy the read +structure and continue to use the PLTE, tRNS, and hIST data in the write +structure. + +This function only affects data that has already been allocated. +You can call this function before calling after the png_set_*() functions +to control whether the user or png_destroy_*() is supposed to free the data. +When the user assumes responsibility for libpng-allocated data, the +application must use +png_free() to free it, and when the user transfers responsibility to libpng +for data that the user has allocated, the user must have used png_malloc() +or png_zalloc() to allocate it. + +If you allocated text_ptr.text, text_ptr.lang, and text_ptr.translated_keyword +separately, do not transfer responsibility for freeing text_ptr to libpng, +because when libpng fills a png_text structure it combines these members with +the key member, and png_free_data() will free only text_ptr.key. Similarly, +if you transfer responsibility for free'ing text_ptr from libpng to your +application, your application must not separately free those members. +For a more compact example of writing a PNG image, see the file example.c. + +.SH V. Modifying/Customizing libpng: + +There are three issues here. The first is changing how libpng does +standard things like memory allocation, input/output, and error handling. +The second deals with more complicated things like adding new chunks, +adding new transformations, and generally changing how libpng works. +Both of those are compile-time issues; that is, they are generally +determined at the time the code is written, and there is rarely a need +to provide the user with a means of changing them. The third is a +run-time issue: choosing between and/or tuning one or more alternate +versions of computationally intensive routines; specifically, optimized +assembly-language (and therefore compiler- and platform-dependent) +versions. + +Memory allocation, input/output, and error handling + +All of the memory allocation, input/output, and error handling in libpng +goes through callbacks that are user-settable. The default routines are +in pngmem.c, pngrio.c, pngwio.c, and pngerror.c, respectively. To change +these functions, call the appropriate png_set_*_fn() function. + +Memory allocation is done through the functions png_malloc() +and png_free(). These currently just call the standard C functions. If +your pointers can't access more then 64K at a time, you will want to set +MAXSEG_64K in zlib.h. Since it is unlikely that the method of handling +memory allocation on a platform will change between applications, these +functions must be modified in the library at compile time. If you prefer +to use a different method of allocating and freeing data, you can use +png_create_read_struct_2() or png_create_write_struct_2() to register +your own functions as described above. +These functions also provide a void pointer that can be retrieved via + + mem_ptr=png_get_mem_ptr(png_ptr); + +Your replacement memory functions must have prototypes as follows: + + png_voidp malloc_fn(png_structp png_ptr, + png_size_t size); + void free_fn(png_structp png_ptr, png_voidp ptr); + +Your malloc_fn() must return NULL in case of failure. The png_malloc() +function will normally call png_error() if it receives a NULL from the +system memory allocator or from your replacement malloc_fn(). + +Input/Output in libpng is done through png_read() and png_write(), +which currently just call fread() and fwrite(). The FILE * is stored in +png_struct and is initialized via png_init_io(). If you wish to change +the method of I/O, the library supplies callbacks that you can set +through the function png_set_read_fn() and png_set_write_fn() at run +time, instead of calling the png_init_io() function. These functions +also provide a void pointer that can be retrieved via the function +png_get_io_ptr(). For example: + + png_set_read_fn(png_structp read_ptr, + voidp read_io_ptr, png_rw_ptr read_data_fn) + + png_set_write_fn(png_structp write_ptr, + voidp write_io_ptr, png_rw_ptr write_data_fn, + png_flush_ptr output_flush_fn); + + voidp read_io_ptr = png_get_io_ptr(read_ptr); + voidp write_io_ptr = png_get_io_ptr(write_ptr); + +The replacement I/O functions must have prototypes as follows: + + void user_read_data(png_structp png_ptr, + png_bytep data, png_size_t length); + void user_write_data(png_structp png_ptr, + png_bytep data, png_size_t length); + void user_flush_data(png_structp png_ptr); + +Supplying NULL for the read, write, or flush functions sets them back +to using the default C stream functions. It is an error to read from +a write stream, and vice versa. + +Error handling in libpng is done through png_error() and png_warning(). +Errors handled through png_error() are fatal, meaning that png_error() +should never return to its caller. Currently, this is handled via +setjmp() and longjmp() (unless you have compiled libpng with +PNG_SETJMP_NOT_SUPPORTED, in which case it is handled via PNG_ABORT()), +but you could change this to do things like exit() if you should wish. + +On non-fatal errors, png_warning() is called +to print a warning message, and then control returns to the calling code. +By default png_error() and png_warning() print a message on stderr via +fprintf() unless the library is compiled with PNG_NO_CONSOLE_IO defined +(because you don't want the messages) or PNG_NO_STDIO defined (because +fprintf() isn't available). If you wish to change the behavior of the error +functions, you will need to set up your own message callbacks. These +functions are normally supplied at the time that the png_struct is created. +It is also possible to redirect errors and warnings to your own replacement +functions after png_create_*_struct() has been called by calling: + + png_set_error_fn(png_structp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warning_fn); + + png_voidp error_ptr = png_get_error_ptr(png_ptr); + +If NULL is supplied for either error_fn or warning_fn, then the libpng +default function will be used, calling fprintf() and/or longjmp() if a +problem is encountered. The replacement error functions should have +parameters as follows: + + void user_error_fn(png_structp png_ptr, + png_const_charp error_msg); + void user_warning_fn(png_structp png_ptr, + png_const_charp warning_msg); + +The motivation behind using setjmp() and longjmp() is the C++ throw and +catch exception handling methods. This makes the code much easier to write, +as there is no need to check every return code of every function call. +However, there are some uncertainties about the status of local variables +after a longjmp, so the user may want to be careful about doing anything after +setjmp returns non-zero besides returning itself. Consult your compiler +documentation for more details. For an alternative approach, you may wish +to use the "cexcept" facility (see http://cexcept.sourceforge.net). + +.SS Custom chunks + +If you need to read or write custom chunks, you may need to get deeper +into the libpng code. The library now has mechanisms for storing +and writing chunks of unknown type; you can even declare callbacks +for custom chunks. Hoewver, this may not be good enough if the +library code itself needs to know about interactions between your +chunk and existing `intrinsic' chunks. + +If you need to write a new intrinsic chunk, first read the PNG +specification. Acquire a first level of +understanding of how it works. Pay particular attention to the +sections that describe chunk names, and look at how other chunks were +designed, so you can do things similarly. Second, check out the +sections of libpng that read and write chunks. Try to find a chunk +that is similar to yours and use it as a template. More details can +be found in the comments inside the code. It is best to handle unknown +chunks in a generic method, via callback functions, instead of by +modifying libpng functions. + +If you wish to write your own transformation for the data, look through +the part of the code that does the transformations, and check out some of +the simpler ones to get an idea of how they work. Try to find a similar +transformation to the one you want to add and copy off of it. More details +can be found in the comments inside the code itself. + +.SS Configuring for 16 bit platforms + +You will want to look into zconf.h to tell zlib (and thus libpng) that +it cannot allocate more then 64K at a time. Even if you can, the memory +won't be accessible. So limit zlib and libpng to 64K by defining MAXSEG_64K. + +.SS Configuring for DOS + +For DOS users who only have access to the lower 640K, you will +have to limit zlib's memory usage via a png_set_compression_mem_level() +call. See zlib.h or zconf.h in the zlib library for more information. + +.SS Configuring for Medium Model + +Libpng's support for medium model has been tested on most of the popular +compilers. Make sure MAXSEG_64K gets defined, USE_FAR_KEYWORD gets +defined, and FAR gets defined to far in pngconf.h, and you should be +all set. Everything in the library (except for zlib's structure) is +expecting far data. You must use the typedefs with the p or pp on +the end for pointers (or at least look at them and be careful). Make +note that the rows of data are defined as png_bytepp, which is an +unsigned char far * far *. + +.SS Configuring for gui/windowing platforms: + +You will need to write new error and warning functions that use the GUI +interface, as described previously, and set them to be the error and +warning functions at the time that png_create_*_struct() is called, +in order to have them available during the structure initialization. +They can be changed later via png_set_error_fn(). On some compilers, +you may also have to change the memory allocators (png_malloc, etc.). + +.SS Configuring for compiler xxx: + +All includes for libpng are in pngconf.h. If you need to add/change/delete +an include, this is the place to do it. The includes that are not +needed outside libpng are protected by the PNG_INTERNAL definition, +which is only defined for those routines inside libpng itself. The +files in libpng proper only include png.h, which includes pngconf.h. + +.SS Configuring zlib: + +There are special functions to configure the compression. Perhaps the +most useful one changes the compression level, which currently uses +input compression values in the range 0 - 9. The library normally +uses the default compression level (Z_DEFAULT_COMPRESSION = 6). Tests +have shown that for a large majority of images, compression values in +the range 3-6 compress nearly as well as higher levels, and do so much +faster. For online applications it may be desirable to have maximum speed +(Z_BEST_SPEED = 1). With versions of zlib after v0.99, you can also +specify no compression (Z_NO_COMPRESSION = 0), but this would create +files larger than just storing the raw bitmap. You can specify the +compression level by calling: + + png_set_compression_level(png_ptr, level); + +Another useful one is to reduce the memory level used by the library. +The memory level defaults to 8, but it can be lowered if you are +short on memory (running DOS, for example, where you only have 640K). +Note that the memory level does have an effect on compression; among +other things, lower levels will result in sections of incompressible +data being emitted in smaller stored blocks, with a correspondingly +larger relative overhead of up to 15% in the worst case. + + png_set_compression_mem_level(png_ptr, level); + +The other functions are for configuring zlib. They are not recommended +for normal use and may result in writing an invalid PNG file. See +zlib.h for more information on what these mean. + + png_set_compression_strategy(png_ptr, + strategy); + png_set_compression_window_bits(png_ptr, + window_bits); + png_set_compression_method(png_ptr, method); + png_set_compression_buffer_size(png_ptr, size); + +.SS Controlling row filtering + +If you want to control whether libpng uses filtering or not, which +filters are used, and how it goes about picking row filters, you +can call one of these functions. The selection and configuration +of row filters can have a significant impact on the size and +encoding speed and a somewhat lesser impact on the decoding speed +of an image. Filtering is enabled by default for RGB and grayscale +images (with and without alpha), but not for paletted images nor +for any images with bit depths less than 8 bits/pixel. + +The 'method' parameter sets the main filtering method, which is +currently only '0' in the PNG 1.2 specification. The 'filters' +parameter sets which filter(s), if any, should be used for each +scanline. Possible values are PNG_ALL_FILTERS and PNG_NO_FILTERS +to turn filtering on and off, respectively. + +Individual filter types are PNG_FILTER_NONE, PNG_FILTER_SUB, +PNG_FILTER_UP, PNG_FILTER_AVG, PNG_FILTER_PAETH, which can be bitwise +ORed together with '|' to specify one or more filters to use. +These filters are described in more detail in the PNG specification. +If you intend to change the filter type during the course of writing +the image, you should start with flags set for all of the filters +you intend to use so that libpng can initialize its internal +structures appropriately for all of the filter types. (Note that this +means the first row must always be adaptively filtered, because libpng +currently does not allocate the filter buffers until png_write_row() +is called for the first time.) + + filters = PNG_FILTER_NONE | PNG_FILTER_SUB + PNG_FILTER_UP | PNG_FILTER_AVE | + PNG_FILTER_PAETH | PNG_ALL_FILTERS; + + png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, + filters); + The second parameter can also be + PNG_INTRAPIXEL_DIFFERENCING if you are + writing a PNG to be embedded in a MNG + datastream. This parameter must be the + same as the value of filter_method used + in png_set_IHDR(). + +It is also possible to influence how libpng chooses from among the +available filters. This is done in one or both of two ways - by +telling it how important it is to keep the same filter for successive +rows, and by telling it the relative computational costs of the filters. + + double weights[3] = {1.5, 1.3, 1.1}, + costs[PNG_FILTER_VALUE_LAST] = + {1.0, 1.3, 1.3, 1.5, 1.7}; + + png_set_filter_heuristics(png_ptr, + PNG_FILTER_HEURISTIC_WEIGHTED, 3, + weights, costs); + +The weights are multiplying factors that indicate to libpng that the +row filter should be the same for successive rows unless another row filter +is that many times better than the previous filter. In the above example, +if the previous 3 filters were SUB, SUB, NONE, the SUB filter could have a +"sum of absolute differences" 1.5 x 1.3 times higher than other filters +and still be chosen, while the NONE filter could have a sum 1.1 times +higher than other filters and still be chosen. Unspecified weights are +taken to be 1.0, and the specified weights should probably be declining +like those above in order to emphasize recent filters over older filters. + +The filter costs specify for each filter type a relative decoding cost +to be considered when selecting row filters. This means that filters +with higher costs are less likely to be chosen over filters with lower +costs, unless their "sum of absolute differences" is that much smaller. +The costs do not necessarily reflect the exact computational speeds of +the various filters, since this would unduly influence the final image +size. + +Note that the numbers above were invented purely for this example and +are given only to help explain the function usage. Little testing has +been done to find optimum values for either the costs or the weights. + +.SS Removing unwanted object code + +There are a bunch of #define's in pngconf.h that control what parts of +libpng are compiled. All the defines end in _SUPPORTED. If you are +never going to use a capability, you can change the #define to #undef +before recompiling libpng and save yourself code and data space, or +you can turn off individual capabilities with defines that begin with +PNG_NO_. + +You can also turn all of the transforms and ancillary chunk capabilities +off en masse with compiler directives that define +PNG_NO_READ[or WRITE]_TRANSFORMS, or PNG_NO_READ[or WRITE]_ANCILLARY_CHUNKS, +or all four, +along with directives to turn on any of the capabilities that you do +want. The PNG_NO_READ[or WRITE]_TRANSFORMS directives disable +the extra transformations but still leave the library fully capable of reading +and writing PNG files with all known public chunks +Use of the PNG_NO_READ[or WRITE]_ANCILLARY_CHUNKS directive +produces a library that is incapable of reading or writing ancillary chunks. +If you are not using the progressive reading capability, you can +turn that off with PNG_NO_PROGRESSIVE_READ (don't confuse +this with the INTERLACING capability, which you'll still have). + +All the reading and writing specific code are in separate files, so the +linker should only grab the files it needs. However, if you want to +make sure, or if you are building a stand alone library, all the +reading files start with pngr and all the writing files start with +pngw. The files that don't match either (like png.c, pngtrans.c, etc.) +are used for both reading and writing, and always need to be included. +The progressive reader is in pngpread.c + +If you are creating or distributing a dynamically linked library (a .so +or DLL file), you should not remove or disable any parts of the library, +as this will cause applications linked with different versions of the +library to fail if they call functions not available in your library. +The size of the library itself should not be an issue, because only +those sections that are actually used will be loaded into memory. + +.SS Requesting debug printout + +The macro definition PNG_DEBUG can be used to request debugging +printout. Set it to an integer value in the range 0 to 3. Higher +numbers result in increasing amounts of debugging information. The +information is printed to the "stderr" file, unless another file +name is specified in the PNG_DEBUG_FILE macro definition. + +When PNG_DEBUG > 0, the following functions (macros) become available: + + png_debug(level, message) + png_debug1(level, message, p1) + png_debug2(level, message, p1, p2) + +in which "level" is compared to PNG_DEBUG to decide whether to print +the message, "message" is the formatted string to be printed, +and p1 and p2 are parameters that are to be embedded in the string +according to printf-style formatting directives. For example, + + png_debug1(2, "foo=%d\n", foo); + +is expanded to + + if(PNG_DEBUG > 2) + fprintf(PNG_DEBUG_FILE, "foo=%d\n", foo); + +When PNG_DEBUG is defined but is zero, the macros aren't defined, but you +can still use PNG_DEBUG to control your own debugging: + + #ifdef PNG_DEBUG + fprintf(stderr, ... + #endif + +When PNG_DEBUG = 1, the macros are defined, but only png_debug statements +having level = 0 will be printed. There aren't any such statements in +this version of libpng, but if you insert some they will be printed. + +.SH VI. Runtime optimization + +A new feature in libpng 1.2.0 is the ability to dynamically switch between +standard and optimized versions of some routines. Currently these are +limited to three computationally intensive tasks when reading PNG files: +decoding row filters, expanding interlacing, and combining interlaced or +transparent row data with previous row data. Currently the optimized +versions are available only for x86 (Intel, AMD, etc.) platforms with +MMX support, though this may change in future versions. (For example, +the non-MMX assembler optimizations for zlib might become similarly +runtime-selectable in future releases, in which case libpng could be +extended to support them. Alternatively, the compile-time choice of +floating-point versus integer routines for gamma correction might become +runtime-selectable.) + +Because such optimizations tend to be very platform- and compiler-dependent, +both in how they are written and in how they perform, the new runtime code +in libpng has been written to allow programs to query, enable, and disable +either specific optimizations or all such optimizations. For example, to +enable all possible optimizations (bearing in mind that some "optimizations" +may actually run more slowly in rare cases): + + #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200) + png_uint_32 mask, flags; + + flags = png_get_asm_flags(png_ptr); + mask = png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE); + png_set_asm_flags(png_ptr, flags | mask); + #endif + +To enable only optimizations relevant to reading PNGs, use PNG_SELECT_READ +by itself when calling png_get_asm_flagmask(); similarly for optimizing +only writing. To disable all optimizations: + + #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200) + flags = png_get_asm_flags(png_ptr); + mask = png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE); + png_set_asm_flags(png_ptr, flags & ~mask); + #endif + +To enable or disable only MMX-related features, use png_get_mmx_flagmask() +in place of png_get_asm_flagmask(). The mmx version takes one additional +parameter: + + #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200) + int selection = PNG_SELECT_READ | PNG_SELECT_WRITE; + int compilerID; + + mask = png_get_mmx_flagmask(selection, &compilerID); + #endif + +On return, compilerID will indicate which version of the MMX assembler +optimizations was compiled. Currently two flavors exist: Microsoft +Visual C++ (compilerID == 1) and GNU C (a.k.a. gcc/gas, compilerID == 2). +On non-x86 platforms or on systems compiled without MMX optimizations, a +value of -1 is used. + +Note that both png_get_asm_flagmask() and png_get_mmx_flagmask() return +all valid, settable optimization bits for the version of the library that's +currently in use. In the case of shared (dynamically linked) libraries, +this may include optimizations that did not exist at the time the code was +written and compiled. It is also possible, of course, to enable only known, +specific optimizations; for example: + + #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200) + flags = PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \ + | PNG_ASM_FLAG_MMX_READ_INTERLACE \ + | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \ + | PNG_ASM_FLAG_MMX_READ_FILTER_UP \ + | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \ + | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ; + png_set_asm_flags(png_ptr, flags); + #endif + +This method would enable only the MMX read-optimizations available at the +time of libpng 1.2.0's release, regardless of whether a later version of +the DLL were actually being used. (Also note that these functions did not +exist in versions older than 1.2.0, so any attempt to run a dynamically +linked app on such an older version would fail.) + +To determine whether the processor supports MMX instructions at all, use +the png_mmx_support() function: + + #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200) + mmxsupport = png_mmx_support(); + #endif + +It returns -1 if MMX support is not compiled into libpng, 0 if MMX code +is compiled but MMX is not supported by the processor, or 1 if MMX support +is fully available. Note that png_mmx_support(), png_get_mmx_flagmask(), +and png_get_asm_flagmask() all may be called without allocating and ini- +tializing any PNG structures (for example, as part of a usage screen or +"about" box). + +The following code can be used to prevent an application from using the +thread_unsafe features, even if libpng was built with PNG_THREAD_UNSAFE_OK +defined: + +#if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) \ + && defined(PNG_THREAD_UNSAFE_OK) + /* Disable thread-unsafe features of pnggccrd */ + if (png_access_version() >= 10200) + { + png_uint_32 mmx_disable_mask = 0; + png_uint_32 asm_flags; + + mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \ + | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \ + | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \ + | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ); + asm_flags = png_get_asm_flags(png_ptr); + png_set_asm_flags(png_ptr, asm_flags & ~mmx_disable_mask); + } +#endif + +For more extensive examples of runtime querying, enabling and disabling +of optimized features, see contrib/gregbook/readpng2.c in the libpng +source-code distribution. + +.SH VII. MNG support + +The MNG specification (available at http://www.libpng.org/pub/mng) allows +certain extensions to PNG for PNG images that are embedded in MNG datastreams. +Libpng can support some of these extensions. To enable them, use the +png_permit_mng_features() function: + + feature_set = png_permit_mng_features(png_ptr, mask) + mask is a png_uint_32 containing the logical OR of the + features you want to enable. These include + PNG_FLAG_MNG_EMPTY_PLTE + PNG_FLAG_MNG_FILTER_64 + PNG_ALL_MNG_FEATURES + feature_set is a png_uint_32 that is the logical AND of + your mask with the set of MNG features that is + supported by the version of libpng that you are using. + +It is an error to use this function when reading or writing a standalone +PNG file with the PNG 8-byte signature. The PNG datastream must be wrapped +in a MNG datastream. As a minimum, it must have the MNG 8-byte signature +and the MHDR and MEND chunks. Libpng does not provide support for these +or any other MNG chunks; your application must provide its own support for +them. You may wish to consider using libmng (available at +http://www.libmng.com) instead. + +.SH VIII. Changes to Libpng from version 0.88 + +It should be noted that versions of libpng later than 0.96 are not +distributed by the original libpng author, Guy Schalnat, nor by +Andreas Dilger, who had taken over from Guy during 1996 and 1997, and +distributed versions 0.89 through 0.96, but rather by another member +of the original PNG Group, Glenn Randers-Pehrson. Guy and Andreas are +still alive and well, but they have moved on to other things. + +The old libpng functions png_read_init(), png_write_init(), +png_info_init(), png_read_destroy(), and png_write_destroy() have been +moved to PNG_INTERNAL in version 0.95 to discourage their use. These +functions will be removed from libpng version 2.0.0. + +The preferred method of creating and initializing the libpng structures is +via the png_create_read_struct(), png_create_write_struct(), and +png_create_info_struct() because they isolate the size of the structures +from the application, allow version error checking, and also allow the +use of custom error handling routines during the initialization, which +the old functions do not. The functions png_read_destroy() and +png_write_destroy() do not actually free the memory that libpng +allocated for these structs, but just reset the data structures, so they +can be used instead of png_destroy_read_struct() and +png_destroy_write_struct() if you feel there is too much system overhead +allocating and freeing the png_struct for each image read. + +Setting the error callbacks via png_set_message_fn() before +png_read_init() as was suggested in libpng-0.88 is no longer supported +because this caused applications that do not use custom error functions +to fail if the png_ptr was not initialized to zero. It is still possible +to set the error callbacks AFTER png_read_init(), or to change them with +png_set_error_fn(), which is essentially the same function, but with a new +name to force compilation errors with applications that try to use the old +method. + +Starting with version 1.0.7, you can find out which version of the library +you are using at run-time: + + png_uint_32 libpng_vn = png_access_version_number(); + +The number libpng_vn is constructed from the major version, minor +version with leading zero, and release number with leading zero, +(e.g., libpng_vn for version 1.0.7 is 10007). + +You can also check which version of png.h you used when compiling your +application: + + png_uint_32 application_vn = PNG_LIBPNG_VER; + +.SH IX. Y2K Compliance in libpng + +December 3, 2004 + +Since the PNG Development group is an ad-hoc body, we can't make +an official declaration. + +This is your unofficial assurance that libpng from version 0.71 and +upward through 1.2.8 are Y2K compliant. It is my belief that earlier +versions were also Y2K compliant. + +Libpng only has three year fields. One is a 2-byte unsigned integer that +will hold years up to 65535. The other two hold the date in text +format, and will hold years up to 9999. + +The integer is + "png_uint_16 year" in png_time_struct. + +The strings are + "png_charp time_buffer" in png_struct and + "near_time_buffer", which is a local character string in png.c. + +There are seven time-related functions: + + png_convert_to_rfc_1123() in png.c + (formerly png_convert_to_rfc_1152() in error) + png_convert_from_struct_tm() in pngwrite.c, called + in pngwrite.c + png_convert_from_time_t() in pngwrite.c + png_get_tIME() in pngget.c + png_handle_tIME() in pngrutil.c, called in pngread.c + png_set_tIME() in pngset.c + png_write_tIME() in pngwutil.c, called in pngwrite.c + +All appear to handle dates properly in a Y2K environment. The +png_convert_from_time_t() function calls gmtime() to convert from system +clock time, which returns (year - 1900), which we properly convert to +the full 4-digit year. There is a possibility that applications using +libpng are not passing 4-digit years into the png_convert_to_rfc_1123() +function, or that they are incorrectly passing only a 2-digit year +instead of "year - 1900" into the png_convert_from_struct_tm() function, +but this is not under our control. The libpng documentation has always +stated that it works with 4-digit years, and the APIs have been +documented as such. + +The tIME chunk itself is also Y2K compliant. It uses a 2-byte unsigned +integer to hold the year, and can hold years as large as 65535. + +zlib, upon which libpng depends, is also Y2K compliant. It contains +no date-related code. + + + Glenn Randers-Pehrson + libpng maintainer + PNG Development Group + +.SH NOTE + +Note about libpng version numbers: + +Due to various miscommunications, unforeseen code incompatibilities +and occasional factors outside the authors' control, version numbering +on the library has not always been consistent and straightforward. +The following table summarizes matters since version 0.89c, which was +the first widely used release: + + source png.h png.h shared-lib + version string int version + ------- ------ ----- ---------- + 0.89c ("beta 3") 0.89 89 1.0.89 + 0.90 ("beta 4") 0.90 90 0.90 + 0.95 ("beta 5") 0.95 95 0.95 + 0.96 ("beta 6") 0.96 96 0.96 + 0.97b ("beta 7") 1.00.97 97 1.0.1 + 0.97c 0.97 97 2.0.97 + 0.98 0.98 98 2.0.98 + 0.99 0.99 98 2.0.99 + 0.99a-m 0.99 99 2.0.99 + 1.00 1.00 100 2.1.0 + 1.0.0 1.0.0 100 2.1.0 + 1.0.0 (from here on, the 100 2.1.0 + 1.0.1 png.h string is 10001 2.1.0 + 1.0.1a-e identical to the 10002 from here on, the + 1.0.2 source version) 10002 shared library is 2.V + 1.0.2a-b 10003 where V is the source + 1.0.1 10001 code version except as + 1.0.1a-e 10002 2.1.0.1a-e noted. + 1.0.2 10002 2.1.0.2 + 1.0.2a-b 10003 2.1.0.2a-b + 1.0.3 10003 2.1.0.3 + 1.0.3a-d 10004 2.1.0.3a-d + 1.0.4 10004 2.1.0.4 + 1.0.4a-f 10005 2.1.0.4a-f + 1.0.5 (+ 2 patches) 10005 2.1.0.5 + 1.0.5a-d 10006 2.1.0.5a-d + 1.0.5e-r 10100 2.1.0.5e-r + 1.0.5s-v 10006 2.1.0.5s-v + 1.0.6 (+ 3 patches) 10006 2.1.0.6 + 1.0.6d-g 10007 2.1.0.6d-g + 1.0.6h 10007 10.6h + 1.0.6i 10007 10.6i + 1.0.6j 10007 2.1.0.6j + 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 + 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 + 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 + 1.0.7 1 10007 2.1.0.7 + 1.0.8beta1-4 1 10008 2.1.0.8beta1-4 + 1.0.8rc1 1 10008 2.1.0.8rc1 + 1.0.8 1 10008 2.1.0.8 + 1.0.9beta1-6 1 10009 2.1.0.9beta1-6 + 1.0.9rc1 1 10009 2.1.0.9rc1 + 1.0.9beta7-10 1 10009 2.1.0.9beta7-10 + 1.0.9rc2 1 10009 2.1.0.9rc2 + 1.0.9 1 10009 2.1.0.9 + 1.0.10beta1 1 10010 2.1.0.10beta1 + 1.0.10rc1 1 10010 2.1.0.10rc1 + 1.0.10 1 10010 2.1.0.10 + 1.0.11beta1-3 1 10011 2.1.0.11beta1-3 + 1.0.11rc1 1 10011 2.1.0.11rc1 + 1.0.11 1 10011 2.1.0.11 + 1.0.12beta1-2 2 10012 2.1.0.12beta1-2 + 1.0.12rc1 2 10012 2.1.0.12rc1 + 1.0.12 2 10012 2.1.0.12 + 1.1.0a-f - 10100 2.1.1.0a-f abandoned + 1.2.0beta1-2 2 10200 2.1.2.0beta1-2 + 1.2.0beta3-5 3 10200 3.1.2.0beta3-5 + 1.2.0rc1 3 10200 3.1.2.0rc1 + 1.2.0 3 10200 3.1.2.0 + 1.2.1beta-4 3 10201 3.1.2.1beta1-4 + 1.2.1rc1-2 3 10201 3.1.2.1rc1-2 + 1.2.1 3 10201 3.1.2.1 + 1.2.2beta1-6 12 10202 12.so.0.1.2.2beta1-6 + 1.0.13beta1 10 10013 10.so.0.1.0.13beta1 + 1.0.13rc1 10 10013 10.so.0.1.0.13rc1 + 1.2.2rc1 12 10202 12.so.0.1.2.2rc1 + 1.0.13 10 10013 10.so.0.1.0.13 + 1.2.2 12 10202 12.so.0.1.2.2 + 1.2.3rc1-6 12 10203 12.so.0.1.2.3rc1-6 + 1.2.3 12 10203 12.so.0.1.2.3 + 1.2.4beta1-3 13 10204 12.so.0.1.2.4beta1-3 + 1.2.4rc1 13 10204 12.so.0.1.2.4rc1 + 1.0.14 10 10014 10.so.0.1.0.14 + 1.2.4 13 10204 12.so.0.1.2.4 + 1.2.5beta1-2 13 10205 12.so.0.1.2.5beta1-2 + 1.0.15rc1 10 10015 10.so.0.1.0.15rc1 + 1.0.15 10 10015 10.so.0.1.0.15 + 1.2.5 13 10205 12.so.0.1.2.5 + 1.2.6beta1-4 13 10206 12.so.0.1.2.6beta1-4 + 1.2.6rc1-5 13 10206 12.so.0.1.2.6rc1-5 + 1.0.16 10 10016 10.so.0.1.0.16 + 1.2.6 13 10206 12.so.0.1.2.6 + 1.2.7beta1-2 13 10207 12.so.0.1.2.7beta1-2 + 1.0.17rc1 10 10017 12.so.0.1.0.17rc1 + 1.2.7rc1 13 10207 12.so.0.1.2.7rc1 + 1.0.17 10 10017 12.so.0.1.0.17 + 1.2.7 13 10207 12.so.0.1.2.7 + 1.2.8beta1-5 13 10208 12.so.0.1.2.8beta1-5 + 1.0.18rc1-5 10 10018 12.so.0.1.0.18rc1-5 + 1.2.8rc1-5 13 10208 12.so.0.1.2.8rc1-5 + 1.0.18 10 10018 12.so.0.1.0.18 + 1.2.8 13 10208 12.so.0.1.2.8 + +Henceforth the source version will match the shared-library minor +and patch numbers; the shared-library major version number will be +used for changes in backward compatibility, as it is intended. The +PNG_PNGLIB_VER macro, which is not used within libpng but is available +for applications, is an unsigned integer of the form xyyzz corresponding +to the source version x.y.z (leading zeros in y and z). Beta versions +were given the previous public release number plus a letter, until +version 1.0.6j; from then on they were given the upcoming public +release number plus "betaNN" or "rcN". + +.SH "SEE ALSO" +libpngpf(3), png(5) +.LP +.IR libpng : +.IP +http://libpng.sourceforge.net (follow the [DOWNLOAD] link) +http://www.libpng.org/pub/png + +.LP +.IR zlib : +.IP +(generally) at the same location as +.I libpng +or at +.br +ftp://ftp.info-zip.org/pub/infozip/zlib + +.LP +.IR PNG specification: RFC 2083 +.IP +(generally) at the same location as +.I libpng +or at +.br +ftp://ds.internic.net/rfc/rfc2083.txt +.br +or (as a W3C Recommendation) at +.br +http://www.w3.org/TR/REC-png.html + +.LP +In the case of any inconsistency between the PNG specification +and this library, the specification takes precedence. + +.SH AUTHORS +This man page: Glenn Randers-Pehrson + + +The contributing authors would like to thank all those who helped +with testing, bug fixes, and patience. This wouldn't have been +possible without all of you. + +Thanks to Frank J. T. Wojcik for helping with the documentation. + +Libpng version 1.2.8 - December 3, 2004: +Initially created in 1995 by Guy Eric Schalnat, then of Group 42, Inc. +Currently maintained by Glenn Randers-Pehrson (glennrp at users.sourceforge.net). + +Supported by the PNG development group +.br +png-implement at ccrc.wustl.edu (subscription required; write to +majordomo at ccrc.wustl.edu with "subscribe png-implement" in the message). + +.SH COPYRIGHT NOTICE, DISCLAIMER, and LICENSE: + +(This copy of the libpng notices is provided for your convenience. In case of +any discrepancy between this copy and the notices in the file png.h that is +included in the libpng distribution, the latter shall prevail.) + +If you modify libpng you may insert additional notices immediately following +this sentence. + +libpng version 1.2.6, December 3, 2004, is +Copyright (c) 2004 Glenn Randers-Pehrson, and is +distributed according to the same disclaimer and license as libpng-1.2.5 +with the following individual added to the list of Contributing Authors + + Cosmin Truta + +libpng versions 1.0.7, July 1, 2000, through 1.2.5 - October 3, 2002, are +Copyright (c) 2000-2002 Glenn Randers-Pehrson, and are +distributed according to the same disclaimer and license as libpng-1.0.6 +with the following individuals added to the list of Contributing Authors + + Simon-Pierre Cadieux + Eric S. Raymond + Gilles Vollant + +and with the following additions to the disclaimer: + + There is no warranty against interference with your + enjoyment of the library or against infringement. + There is no warranty that our efforts or the library + will fulfill any of your particular purposes or needs. + This library is provided with all faults, and the entire + risk of satisfactory quality, performance, accuracy, and + effort is with the user. + +libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are +Copyright (c) 1998, 1999 Glenn Randers-Pehrson +Distributed according to the same disclaimer and license as libpng-0.96, +with the following individuals added to the list of Contributing Authors: + + Tom Lane + Glenn Randers-Pehrson + Willem van Schaik + +libpng versions 0.89, June 1996, through 0.96, May 1997, are +Copyright (c) 1996, 1997 Andreas Dilger +Distributed according to the same disclaimer and license as libpng-0.88, +with the following individuals added to the list of Contributing Authors: + + John Bowler + Kevin Bracey + Sam Bushell + Magnus Holmgren + Greg Roelofs + Tom Tanner + +libpng versions 0.5, May 1995, through 0.88, January 1996, are +Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc. + +For the purposes of this copyright and license, "Contributing Authors" +is defined as the following set of individuals: + + Andreas Dilger + Dave Martindale + Guy Eric Schalnat + Paul Schmidt + Tim Wegner + +The PNG Reference Library is supplied "AS IS". The Contributing Authors +and Group 42, Inc. disclaim all warranties, expressed or implied, +including, without limitation, the warranties of merchantability and of +fitness for any purpose. The Contributing Authors and Group 42, Inc. +assume no liability for direct, indirect, incidental, special, exemplary, +or consequential damages, which may result from the use of the PNG +Reference Library, even if advised of the possibility of such damage. + +Permission is hereby granted to use, copy, modify, and distribute this +source code, or portions hereof, for any purpose, without fee, subject +to the following restrictions: + +1. The origin of this source code must not be misrepresented. + +2. Altered versions must be plainly marked as such and + must not be misrepresented as being the original source. + +3. This Copyright notice may not be removed or altered from + any source or altered source distribution. + +The Contributing Authors and Group 42, Inc. specifically permit, without +fee, and encourage the use of this source code as a component to +supporting the PNG file format in commercial products. If you use this +source code in a product, acknowledgment is not required but would be +appreciated. + + +A "png_get_copyright" function is available, for convenient use in "about" +boxes and the like: + + printf("%s",png_get_copyright(NULL)); + +Also, the PNG logo (in PNG format, of course) is supplied in the +files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). + +Libpng is OSI Certified Open Source Software. OSI Certified Open Source is a +certification mark of the Open Source Initiative. + +Glenn Randers-Pehrson +glennrp at users.sourceforge.net +December 3, 2004 + +.\" end of man page diff --git a/com32/lib/libpng/libpng.txt b/com32/lib/libpng/libpng.txt new file mode 100644 index 0000000..9360f33 --- /dev/null +++ b/com32/lib/libpng/libpng.txt @@ -0,0 +1,2959 @@ +libpng.txt - A description on how to use and modify libpng + + libpng version 1.2.8 - December 3, 2004 + Updated and distributed by Glenn Randers-Pehrson + + Copyright (c) 1998-2004 Glenn Randers-Pehrson + For conditions of distribution and use, see copyright + notice in png.h. + + based on: + + libpng 1.0 beta 6 version 0.96 May 28, 1997 + Updated and distributed by Andreas Dilger + Copyright (c) 1996, 1997 Andreas Dilger + + libpng 1.0 beta 2 - version 0.88 January 26, 1996 + For conditions of distribution and use, see copyright + notice in png.h. Copyright (c) 1995, 1996 Guy Eric + Schalnat, Group 42, Inc. + + Updated/rewritten per request in the libpng FAQ + Copyright (c) 1995, 1996 Frank J. T. Wojcik + December 18, 1995 & January 20, 1996 + +I. Introduction + +This file describes how to use and modify the PNG reference library +(known as libpng) for your own use. There are five sections to this +file: introduction, structures, reading, writing, and modification and +configuration notes for various special platforms. In addition to this +file, example.c is a good starting point for using the library, as +it is heavily commented and should include everything most people +will need. We assume that libpng is already installed; see the +INSTALL file for instructions on how to install libpng. + +Libpng was written as a companion to the PNG specification, as a way +of reducing the amount of time and effort it takes to support the PNG +file format in application programs. + +The PNG specification (second edition), November 2003, is available as +a W3C Recommendation and as an ISO Standard (ISO/IEC 15948:2003 (E)) at + + +The PNG-1.0 specification is available +as RFC 2083 and as a +W3C Recommendation . Some +additional chunks are described in the special-purpose public chunks +documents at . + +Other information +about PNG, and the latest version of libpng, can be found at the PNG home +page, . + +Most users will not have to modify the library significantly; advanced +users may want to modify it more. All attempts were made to make it as +complete as possible, while keeping the code easy to understand. +Currently, this library only supports C. Support for other languages +is being considered. + +Libpng has been designed to handle multiple sessions at one time, +to be easily modifiable, to be portable to the vast majority of +machines (ANSI, K&R, 16-, 32-, and 64-bit) available, and to be easy +to use. The ultimate goal of libpng is to promote the acceptance of +the PNG file format in whatever way possible. While there is still +work to be done (see the TODO file), libpng should cover the +majority of the needs of its users. + +Libpng uses zlib for its compression and decompression of PNG files. +Further information about zlib, and the latest version of zlib, can +be found at the zlib home page, . +The zlib compression utility is a general purpose utility that is +useful for more than PNG files, and can be used without libpng. +See the documentation delivered with zlib for more details. +You can usually find the source files for the zlib utility wherever you +find the libpng source files. + +Libpng is thread safe, provided the threads are using different +instances of the structures. Each thread should have its own +png_struct and png_info instances, and thus its own image. +Libpng does not protect itself against two threads using the +same instance of a structure. Note: thread safety may be defeated +by use of some of the MMX assembler code in pnggccrd.c, which is only +compiled when the user defines PNG_THREAD_UNSAFE_OK. + +II. Structures + +There are two main structures that are important to libpng, png_struct +and png_info. The first, png_struct, is an internal structure that +will not, for the most part, be used by a user except as the first +variable passed to every libpng function call. + +The png_info structure is designed to provide information about the +PNG file. At one time, the fields of png_info were intended to be +directly accessible to the user. However, this tended to cause problems +with applications using dynamically loaded libraries, and as a result +a set of interface functions for png_info (the png_get_*() and png_set_*() +functions) was developed. The fields of png_info are still available for +older applications, but it is suggested that applications use the new +interfaces if at all possible. + +Applications that do make direct access to the members of png_struct (except +for png_ptr->jmpbuf) must be recompiled whenever the library is updated, +and applications that make direct access to the members of png_info must +be recompiled if they were compiled or loaded with libpng version 1.0.6, +in which the members were in a different order. In version 1.0.7, the +members of the png_info structure reverted to the old order, as they were +in versions 0.97c through 1.0.5. Starting with version 2.0.0, both +structures are going to be hidden, and the contents of the structures will +only be accessible through the png_get/png_set functions. + +The png.h header file is an invaluable reference for programming with libpng. +And while I'm on the topic, make sure you include the libpng header file: + +#include + +III. Reading + +We'll now walk you through the possible functions to call when reading +in a PNG file sequentially, briefly explaining the syntax and purpose +of each one. See example.c and png.h for more detail. While +progressive reading is covered in the next section, you will still +need some of the functions discussed in this section to read a PNG +file. + +Setup + +You will want to do the I/O initialization(*) before you get into libpng, +so if it doesn't work, you don't have much to undo. Of course, you +will also want to insure that you are, in fact, dealing with a PNG +file. Libpng provides a simple check to see if a file is a PNG file. +To use it, pass in the first 1 to 8 bytes of the file to the function +png_sig_cmp(), and it will return 0 if the bytes match the corresponding +bytes of the PNG signature, or nonzero otherwise. Of course, the more bytes +you pass in, the greater the accuracy of the prediction. + +If you are intending to keep the file pointer open for use in libpng, +you must ensure you don't read more than 8 bytes from the beginning +of the file, and you also have to make a call to png_set_sig_bytes_read() +with the number of bytes you read from the beginning. Libpng will +then only check the bytes (if any) that your program didn't read. + +(*): If you are not using the standard I/O functions, you will need +to replace them with custom functions. See the discussion under +Customizing libpng. + + + FILE *fp = fopen(file_name, "rb"); + if (!fp) + { + return (ERROR); + } + fread(header, 1, number, fp); + is_png = !png_sig_cmp(header, 0, number); + if (!is_png) + { + return (NOT_PNG); + } + + +Next, png_struct and png_info need to be allocated and initialized. In +order to ensure that the size of these structures is correct even with a +dynamically linked libpng, there are functions to initialize and +allocate the structures. We also pass the library version, optional +pointers to error handling functions, and a pointer to a data struct for +use by the error functions, if necessary (the pointer and functions can +be NULL if the default error handlers are to be used). See the section +on Changes to Libpng below regarding the old initialization functions. +The structure allocation functions quietly return NULL if they fail to +create the structure, so your application should check for that. + + png_structp png_ptr = png_create_read_struct + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn); + if (!png_ptr) + return (ERROR); + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr, + (png_infopp)NULL, (png_infopp)NULL); + return (ERROR); + } + + png_infop end_info = png_create_info_struct(png_ptr); + if (!end_info) + { + png_destroy_read_struct(&png_ptr, &info_ptr, + (png_infopp)NULL); + return (ERROR); + } + +If you want to use your own memory allocation routines, +define PNG_USER_MEM_SUPPORTED and use +png_create_read_struct_2() instead of png_create_read_struct(): + + png_structp png_ptr = png_create_read_struct_2 + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn, (png_voidp) + user_mem_ptr, user_malloc_fn, user_free_fn); + +The error handling routines passed to png_create_read_struct() +and the memory alloc/free routines passed to png_create_struct_2() +are only necessary if you are not using the libpng supplied error +handling and memory alloc/free functions. + +When libpng encounters an error, it expects to longjmp back +to your routine. Therefore, you will need to call setjmp and pass +your png_jmpbuf(png_ptr). If you read the file from different +routines, you will need to update the jmpbuf field every time you enter +a new routine that will call a png_*() function. + +See your documentation of setjmp/longjmp for your compiler for more +information on setjmp/longjmp. See the discussion on libpng error +handling in the Customizing Libpng section below for more information +on the libpng error handling. If an error occurs, and libpng longjmp's +back to your setjmp, you will want to call png_destroy_read_struct() to +free any memory. + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, + &end_info); + fclose(fp); + return (ERROR); + } + +If you would rather avoid the complexity of setjmp/longjmp issues, +you can compile libpng with PNG_SETJMP_NOT_SUPPORTED, in which case +errors will result in a call to PNG_ABORT() which defaults to abort(). + +Now you need to set up the input code. The default for libpng is to +use the C function fread(). If you use this, you will need to pass a +valid FILE * in the function png_init_io(). Be sure that the file is +opened in binary mode. If you wish to handle reading data in another +way, you need not call the png_init_io() function, but you must then +implement the libpng I/O methods discussed in the Customizing Libpng +section below. + + png_init_io(png_ptr, fp); + +If you had previously opened the file and read any of the signature from +the beginning in order to see if this was a PNG file, you need to let +libpng know that there are some bytes missing from the start of the file. + + png_set_sig_bytes(png_ptr, number); + +Setting up callback code + +You can set up a callback function to handle any unknown chunks in the +input stream. You must supply the function + + read_chunk_callback(png_ptr ptr, + png_unknown_chunkp chunk); + { + /* The unknown chunk structure contains your + chunk data: */ + png_byte name[5]; + png_byte *data; + png_size_t size; + /* Note that libpng has already taken care of + the CRC handling */ + + /* put your code here. Return one of the + following: */ + + return (-n); /* chunk had an error */ + return (0); /* did not recognize */ + return (n); /* success */ + } + +(You can give your function another name that you like instead of +"read_chunk_callback") + +To inform libpng about your function, use + + png_set_read_user_chunk_fn(png_ptr, user_chunk_ptr, + read_chunk_callback); + +This names not only the callback function, but also a user pointer that +you can retrieve with + + png_get_user_chunk_ptr(png_ptr); + +At this point, you can set up a callback function that will be +called after each row has been read, which you can use to control +a progress meter or the like. It's demonstrated in pngtest.c. +You must supply a function + + void read_row_callback(png_ptr ptr, png_uint_32 row, + int pass); + { + /* put your code here */ + } + +(You can give it another name that you like instead of "read_row_callback") + +To inform libpng about your function, use + + png_set_read_status_fn(png_ptr, read_row_callback); + +Width and height limits + +The PNG specification allows the width and height of an image to be as +large as 2^31-1 (0x7fffffff), or about 2.147 billion rows and columns. +Since very few applications really need to process such large images, +we have imposed an arbitrary 1-million limit on rows and columns. +Larger images will be rejected immediately with a png_error() call. If +you wish to override this limit, you can use + + png_set_user_limits(png_ptr, width_max, height_max); + +to set your own limits, or use width_max = height_max = 0x7fffffffL +to allow all valid dimensions (libpng may reject some very large images +anyway because of potential buffer overflow conditions). + +You should put this statement after you create the PNG structure and +before calling png_read_info(), png_read_png(), or png_process_data(). +If you need to retrieve the limits that are being applied, use + + width_max = png_get_user_width_max(png_ptr); + height_max = png_get_user_height_max(png_ptr); + +Unknown-chunk handling + +Now you get to set the way the library processes unknown chunks in the +input PNG stream. Both known and unknown chunks will be read. Normal +behavior is that known chunks will be parsed into information in +various info_ptr members; unknown chunks will be discarded. To change +this, you can call: + + png_set_keep_unknown_chunks(png_ptr, keep, + chunk_list, num_chunks); + keep - 0: do not handle as unknown + 1: do not keep + 2: keep only if safe-to-copy + 3: keep even if unsafe-to-copy + You can use these definitions: + PNG_HANDLE_CHUNK_AS_DEFAULT 0 + PNG_HANDLE_CHUNK_NEVER 1 + PNG_HANDLE_CHUNK_IF_SAFE 2 + PNG_HANDLE_CHUNK_ALWAYS 3 + chunk_list - list of chunks affected (a byte string, + five bytes per chunk, NULL or '\0' if + num_chunks is 0) + num_chunks - number of chunks affected; if 0, all + unknown chunks are affected. If nonzero, + only the chunks in the list are affected + +Unknown chunks declared in this way will be saved as raw data onto a +list of png_unknown_chunk structures. If a chunk that is normally +known to libpng is named in the list, it will be handled as unknown, +according to the "keep" directive. If a chunk is named in successive +instances of png_set_keep_unknown_chunks(), the final instance will +take precedence. The IHDR and IEND chunks should not be named in +chunk_list; if they are, libpng will process them normally anyway. + +The high-level read interface + +At this point there are two ways to proceed; through the high-level +read interface, or through a sequence of low-level read operations. +You can use the high-level interface if (a) you are willing to read +the entire image into memory, and (b) the input transformations +you want to do are limited to the following set: + + PNG_TRANSFORM_IDENTITY No transformation + PNG_TRANSFORM_STRIP_16 Strip 16-bit samples to + 8 bits + PNG_TRANSFORM_STRIP_ALPHA Discard the alpha channel + PNG_TRANSFORM_PACKING Expand 1, 2 and 4-bit + samples to bytes + PNG_TRANSFORM_PACKSWAP Change order of packed + pixels to LSB first + PNG_TRANSFORM_EXPAND Perform set_expand() + PNG_TRANSFORM_INVERT_MONO Invert monochrome images + PNG_TRANSFORM_SHIFT Normalize pixels to the + sBIT depth + PNG_TRANSFORM_BGR Flip RGB to BGR, RGBA + to BGRA + PNG_TRANSFORM_SWAP_ALPHA Flip RGBA to ARGB or GA + to AG + PNG_TRANSFORM_INVERT_ALPHA Change alpha from opacity + to transparency + PNG_TRANSFORM_SWAP_ENDIAN Byte-swap 16-bit samples + +(This excludes setting a background color, doing gamma transformation, +dithering, and setting filler.) If this is the case, simply do this: + + png_read_png(png_ptr, info_ptr, png_transforms, NULL) + +where png_transforms is an integer containing the logical OR of +some set of transformation flags. This call is equivalent to png_read_info(), +followed the set of transformations indicated by the transform mask, +then png_read_image(), and finally png_read_end(). + +(The final parameter of this call is not yet used. Someday it might point +to transformation parameters required by some future input transform.) + +You must use png_transforms and not call any png_set_transform() functions +when you use png_read_png(). + +After you have called png_read_png(), you can retrieve the image data +with + + row_pointers = png_get_rows(png_ptr, info_ptr); + +where row_pointers is an array of pointers to the pixel data for each row: + + png_bytep row_pointers[height]; + +If you know your image size and pixel size ahead of time, you can allocate +row_pointers prior to calling png_read_png() with + + if (height > PNG_UINT_32_MAX/png_sizeof(png_byte)) + png_error (png_ptr, + "Image is too tall to process in memory"); + if (width > PNG_UINT_32_MAX/pixel_size) + png_error (png_ptr, + "Image is too wide to process in memory"); + row_pointers = png_malloc(png_ptr, + height*png_sizeof(png_bytep)); + for (int i=0; i) and +png_get_(png_ptr, info_ptr, ...) functions return non-zero if the +data has been read, or zero if it is missing. The parameters to the +png_get_ are set directly if they are simple data types, or a pointer +into the info_ptr is returned for any complex types. + + png_get_PLTE(png_ptr, info_ptr, &palette, + &num_palette); + palette - the palette for the file + (array of png_color) + num_palette - number of entries in the palette + + png_get_gAMA(png_ptr, info_ptr, &gamma); + gamma - the gamma the file is written + at (PNG_INFO_gAMA) + + png_get_sRGB(png_ptr, info_ptr, &srgb_intent); + srgb_intent - the rendering intent (PNG_INFO_sRGB) + The presence of the sRGB chunk + means that the pixel data is in the + sRGB color space. This chunk also + implies specific values of gAMA and + cHRM. + + png_get_iCCP(png_ptr, info_ptr, &name, + &compression_type, &profile, &proflen); + name - The profile name. + compression - The compression type; always + PNG_COMPRESSION_TYPE_BASE for PNG 1.0. + You may give NULL to this argument to + ignore it. + profile - International Color Consortium color + profile data. May contain NULs. + proflen - length of profile data in bytes. + + png_get_sBIT(png_ptr, info_ptr, &sig_bit); + sig_bit - the number of significant bits for + (PNG_INFO_sBIT) each of the gray, + red, green, and blue channels, + whichever are appropriate for the + given color type (png_color_16) + + png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, + &trans_values); + trans - array of transparent entries for + palette (PNG_INFO_tRNS) + trans_values - graylevel or color sample values of + the single transparent color for + non-paletted images (PNG_INFO_tRNS) + num_trans - number of transparent entries + (PNG_INFO_tRNS) + + png_get_hIST(png_ptr, info_ptr, &hist); + (PNG_INFO_hIST) + hist - histogram of palette (array of + png_uint_16) + + png_get_tIME(png_ptr, info_ptr, &mod_time); + mod_time - time image was last modified + (PNG_VALID_tIME) + + png_get_bKGD(png_ptr, info_ptr, &background); + background - background color (PNG_VALID_bKGD) + valid 16-bit red, green and blue + values, regardless of color_type + + num_comments = png_get_text(png_ptr, info_ptr, + &text_ptr, &num_text); + num_comments - number of comments + text_ptr - array of png_text holding image + comments + text_ptr[i].compression - type of compression used + on "text" PNG_TEXT_COMPRESSION_NONE + PNG_TEXT_COMPRESSION_zTXt + PNG_ITXT_COMPRESSION_NONE + PNG_ITXT_COMPRESSION_zTXt + text_ptr[i].key - keyword for comment. Must contain + 1-79 characters. + text_ptr[i].text - text comments for current + keyword. Can be empty. + text_ptr[i].text_length - length of text string, + after decompression, 0 for iTXt + text_ptr[i].itxt_length - length of itxt string, + after decompression, 0 for tEXt/zTXt + text_ptr[i].lang - language of comment (empty + string for unknown). + text_ptr[i].lang_key - keyword in UTF-8 + (empty string for unknown). + num_text - number of comments (same as + num_comments; you can put NULL here + to avoid the duplication) + Note while png_set_text() will accept text, language, + and translated keywords that can be NULL pointers, the + structure returned by png_get_text will always contain + regular zero-terminated C strings. They might be + empty strings but they will never be NULL pointers. + + num_spalettes = png_get_sPLT(png_ptr, info_ptr, + &palette_ptr); + palette_ptr - array of palette structures holding + contents of one or more sPLT chunks + read. + num_spalettes - number of sPLT chunks read. + + png_get_oFFs(png_ptr, info_ptr, &offset_x, &offset_y, + &unit_type); + offset_x - positive offset from the left edge + of the screen + offset_y - positive offset from the top edge + of the screen + unit_type - PNG_OFFSET_PIXEL, PNG_OFFSET_MICROMETER + + png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, + &unit_type); + res_x - pixels/unit physical resolution in + x direction + res_y - pixels/unit physical resolution in + x direction + unit_type - PNG_RESOLUTION_UNKNOWN, + PNG_RESOLUTION_METER + + png_get_sCAL(png_ptr, info_ptr, &unit, &width, + &height) + unit - physical scale units (an integer) + width - width of a pixel in physical scale units + height - height of a pixel in physical scale units + (width and height are doubles) + + png_get_sCAL_s(png_ptr, info_ptr, &unit, &width, + &height) + unit - physical scale units (an integer) + width - width of a pixel in physical scale units + height - height of a pixel in physical scale units + (width and height are strings like "2.54") + + num_unknown_chunks = png_get_unknown_chunks(png_ptr, + info_ptr, &unknowns) + unknowns - array of png_unknown_chunk + structures holding unknown chunks + unknowns[i].name - name of unknown chunk + unknowns[i].data - data of unknown chunk + unknowns[i].size - size of unknown chunk's data + unknowns[i].location - position of chunk in file + + The value of "i" corresponds to the order in which the + chunks were read from the PNG file or inserted with the + png_set_unknown_chunks() function. + +The data from the pHYs chunk can be retrieved in several convenient +forms: + + res_x = png_get_x_pixels_per_meter(png_ptr, + info_ptr) + res_y = png_get_y_pixels_per_meter(png_ptr, + info_ptr) + res_x_and_y = png_get_pixels_per_meter(png_ptr, + info_ptr) + res_x = png_get_x_pixels_per_inch(png_ptr, + info_ptr) + res_y = png_get_y_pixels_per_inch(png_ptr, + info_ptr) + res_x_and_y = png_get_pixels_per_inch(png_ptr, + info_ptr) + aspect_ratio = png_get_pixel_aspect_ratio(png_ptr, + info_ptr) + + (Each of these returns 0 [signifying "unknown"] if + the data is not present or if res_x is 0; + res_x_and_y is 0 if res_x != res_y) + +The data from the oFFs chunk can be retrieved in several convenient +forms: + + x_offset = png_get_x_offset_microns(png_ptr, info_ptr); + y_offset = png_get_y_offset_microns(png_ptr, info_ptr); + x_offset = png_get_x_offset_inches(png_ptr, info_ptr); + y_offset = png_get_y_offset_inches(png_ptr, info_ptr); + + (Each of these returns 0 [signifying "unknown" if both + x and y are 0] if the data is not present or if the + chunk is present but the unit is the pixel) + +For more information, see the png_info definition in png.h and the +PNG specification for chunk contents. Be careful with trusting +rowbytes, as some of the transformations could increase the space +needed to hold a row (expand, filler, gray_to_rgb, etc.). +See png_read_update_info(), below. + +A quick word about text_ptr and num_text. PNG stores comments in +keyword/text pairs, one pair per chunk, with no limit on the number +of text chunks, and a 2^31 byte limit on their size. While there are +suggested keywords, there is no requirement to restrict the use to these +strings. It is strongly suggested that keywords and text be sensible +to humans (that's the point), so don't use abbreviations. Non-printing +symbols are not allowed. See the PNG specification for more details. +There is also no requirement to have text after the keyword. + +Keywords should be limited to 79 Latin-1 characters without leading or +trailing spaces, but non-consecutive spaces are allowed within the +keyword. It is possible to have the same keyword any number of times. +The text_ptr is an array of png_text structures, each holding a +pointer to a language string, a pointer to a keyword and a pointer to +a text string. The text string, language code, and translated +keyword may be empty or NULL pointers. The keyword/text +pairs are put into the array in the order that they are received. +However, some or all of the text chunks may be after the image, so, to +make sure you have read all the text chunks, don't mess with these +until after you read the stuff after the image. This will be +mentioned again below in the discussion that goes with png_read_end(). + +Input transformations + +After you've read the header information, you can set up the library +to handle any special transformations of the image data. The various +ways to transform the data will be described in the order that they +should occur. This is important, as some of these change the color +type and/or bit depth of the data, and some others only work on +certain color types and bit depths. Even though each transformation +checks to see if it has data that it can do something with, you should +make sure to only enable a transformation if it will be valid for the +data. For example, don't swap red and blue on grayscale data. + +The colors used for the background and transparency values should be +supplied in the same format/depth as the current image data. They +are stored in the same format/depth as the image data in a bKGD or tRNS +chunk, so this is what libpng expects for this data. The colors are +transformed to keep in sync with the image data when an application +calls the png_read_update_info() routine (see below). + +Data will be decoded into the supplied row buffers packed into bytes +unless the library has been told to transform it into another format. +For example, 4 bit/pixel paletted or grayscale data will be returned +2 pixels/byte with the leftmost pixel in the high-order bits of the +byte, unless png_set_packing() is called. 8-bit RGB data will be stored +in RGB RGB RGB format unless png_set_filler() or png_set_add_alpha() +is called to insert filler bytes, either before or after each RGB triplet. +16-bit RGB data will be returned RRGGBB RRGGBB, with the most significant +byte of the color value first, unless png_set_strip_16() is called to +transform it to regular RGB RGB triplets, or png_set_filler() or +png_set_add alpha() is called to insert filler bytes, either before or +after each RRGGBB triplet. Similarly, 8-bit or 16-bit grayscale data can +be modified with +png_set_filler(), png_set_add_alpha(), or png_set_strip_16(). + +The following code transforms grayscale images of less than 8 to 8 bits, +changes paletted images to RGB, and adds a full alpha channel if there is +transparency information in a tRNS chunk. This is most useful on +grayscale images with bit depths of 2 or 4 or if there is a multiple-image +viewing application that wishes to treat all images in the same way. + + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png_ptr); + + if (color_type == PNG_COLOR_TYPE_GRAY && + bit_depth < 8) png_set_gray_1_2_4_to_8(png_ptr); + + if (png_get_valid(png_ptr, info_ptr, + PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); + +These three functions are actually aliases for png_set_expand(), added +in libpng version 1.0.4, with the function names expanded to improve code +readability. In some future version they may actually do different +things. + +PNG can have files with 16 bits per channel. If you only can handle +8 bits per channel, this will strip the pixels down to 8 bit. + + if (bit_depth == 16) + png_set_strip_16(png_ptr); + +If, for some reason, you don't need the alpha channel on an image, +and you want to remove it rather than combining it with the background +(but the image author certainly had in mind that you *would* combine +it with the background, so that's what you should probably do): + + if (color_type & PNG_COLOR_MASK_ALPHA) + png_set_strip_alpha(png_ptr); + +In PNG files, the alpha channel in an image +is the level of opacity. If you need the alpha channel in an image to +be the level of transparency instead of opacity, you can invert the +alpha channel (or the tRNS chunk data) after it's read, so that 0 is +fully opaque and 255 (in 8-bit or paletted images) or 65535 (in 16-bit +images) is fully transparent, with + + png_set_invert_alpha(png_ptr); + +PNG files pack pixels of bit depths 1, 2, and 4 into bytes as small as +they can, resulting in, for example, 8 pixels per byte for 1 bit +files. This code expands to 1 pixel per byte without changing the +values of the pixels: + + if (bit_depth < 8) + png_set_packing(png_ptr); + +PNG files have possible bit depths of 1, 2, 4, 8, and 16. All pixels +stored in a PNG image have been "scaled" or "shifted" up to the next +higher possible bit depth (e.g. from 5 bits/sample in the range [0,31] to +8 bits/sample in the range [0, 255]). However, it is also possible to +convert the PNG pixel data back to the original bit depth of the image. +This call reduces the pixels back down to the original bit depth: + + png_color_8p sig_bit; + + if (png_get_sBIT(png_ptr, info_ptr, &sig_bit)) + png_set_shift(png_ptr, sig_bit); + +PNG files store 3-color pixels in red, green, blue order. This code +changes the storage of the pixels to blue, green, red: + + if (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) + png_set_bgr(png_ptr); + +PNG files store RGB pixels packed into 3 or 6 bytes. This code expands them +into 4 or 8 bytes for windowing systems that need them in this format: + + if (color_type == PNG_COLOR_TYPE_RGB) + png_set_filler(png_ptr, filler, PNG_FILLER_BEFORE); + +where "filler" is the 8 or 16-bit number to fill with, and the location is +either PNG_FILLER_BEFORE or PNG_FILLER_AFTER, depending upon whether +you want the filler before the RGB or after. This transformation +does not affect images that already have full alpha channels. To add an +opaque alpha channel, use filler=0xff or 0xffff and PNG_FILLER_AFTER which +will generate RGBA pixels. + +Note that png_set_filler() does not change the color type. If you want +to do that, you can add a true alpha channel with + + if (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY) + png_set_add_alpha(png_ptr, filler, PNG_FILLER_AFTER); + +where "filler" contains the alpha value to assign to each pixel. +This function was added in libpng-1.2.7. + +If you are reading an image with an alpha channel, and you need the +data as ARGB instead of the normal PNG format RGBA: + + if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) + png_set_swap_alpha(png_ptr); + +For some uses, you may want a grayscale image to be represented as +RGB. This code will do that conversion: + + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + +Conversely, you can convert an RGB or RGBA image to grayscale or grayscale +with alpha. + + if (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) + png_set_rgb_to_gray_fixed(png_ptr, error_action, + int red_weight, int green_weight); + + error_action = 1: silently do the conversion + error_action = 2: issue a warning if the original + image has any pixel where + red != green or red != blue + error_action = 3: issue an error and abort the + conversion if the original + image has any pixel where + red != green or red != blue + + red_weight: weight of red component times 100000 + green_weight: weight of green component times 100000 + If either weight is negative, default + weights (21268, 71514) are used. + +If you have set error_action = 1 or 2, you can +later check whether the image really was gray, after processing +the image rows, with the png_get_rgb_to_gray_status(png_ptr) function. +It will return a png_byte that is zero if the image was gray or +1 if there were any non-gray pixels. bKGD and sBIT data +will be silently converted to grayscale, using the green channel +data, regardless of the error_action setting. + +With red_weight+green_weight<=100000, +the normalized graylevel is computed: + + int rw = red_weight * 65536; + int gw = green_weight * 65536; + int bw = 65536 - (rw + gw); + gray = (rw*red + gw*green + bw*blue)/65536; + +The default values approximate those recommended in the Charles +Poynton's Color FAQ, +Copyright (c) 1998-01-04 Charles Poynton + + Y = 0.212671 * R + 0.715160 * G + 0.072169 * B + +Libpng approximates this with + + Y = 0.21268 * R + 0.7151 * G + 0.07217 * B + +which can be expressed with integers as + + Y = (6969 * R + 23434 * G + 2365 * B)/32768 + +The calculation is done in a linear colorspace, if the image gamma +is known. + +If you have a grayscale and you are using png_set_expand_depth(), +png_set_expand(), or png_set_gray_to_rgb to change to truecolor or to +a higher bit-depth, you must either supply the background color as a gray +value at the original file bit-depth (need_expand = 1) or else supply the +background color as an RGB triplet at the final, expanded bit depth +(need_expand = 0). Similarly, if you are reading a paletted image, you +must either supply the background color as a palette index (need_expand = 1) +or as an RGB triplet that may or may not be in the palette (need_expand = 0). + + png_color_16 my_background; + png_color_16p image_background; + + if (png_get_bKGD(png_ptr, info_ptr, &image_background)) + png_set_background(png_ptr, image_background, + PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); + else + png_set_background(png_ptr, &my_background, + PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0); + +The png_set_background() function tells libpng to composite images +with alpha or simple transparency against the supplied background +color. If the PNG file contains a bKGD chunk (PNG_INFO_bKGD valid), +you may use this color, or supply another color more suitable for +the current display (e.g., the background color from a web page). You +need to tell libpng whether the color is in the gamma space of the +display (PNG_BACKGROUND_GAMMA_SCREEN for colors you supply), the file +(PNG_BACKGROUND_GAMMA_FILE for colors from the bKGD chunk), or one +that is neither of these gammas (PNG_BACKGROUND_GAMMA_UNIQUE - I don't +know why anyone would use this, but it's here). + +To properly display PNG images on any kind of system, the application needs +to know what the display gamma is. Ideally, the user will know this, and +the application will allow them to set it. One method of allowing the user +to set the display gamma separately for each system is to check for a +SCREEN_GAMMA or DISPLAY_GAMMA environment variable, which will hopefully be +correctly set. + +Note that display_gamma is the overall gamma correction required to produce +pleasing results, which depends on the lighting conditions in the surrounding +environment. In a dim or brightly lit room, no compensation other than +the physical gamma exponent of the monitor is needed, while in a dark room +a slightly smaller exponent is better. + + double gamma, screen_gamma; + + if (/* We have a user-defined screen + gamma value */) + { + screen_gamma = user_defined_screen_gamma; + } + /* One way that applications can share the same + screen gamma value */ + else if ((gamma_str = getenv("SCREEN_GAMMA")) + != NULL) + { + screen_gamma = (double)atof(gamma_str); + } + /* If we don't have another value */ + else + { + screen_gamma = 2.2; /* A good guess for a + PC monitor in a bright office or a dim room */ + screen_gamma = 2.0; /* A good guess for a + PC monitor in a dark room */ + screen_gamma = 1.7 or 1.0; /* A good + guess for Mac systems */ + } + +The png_set_gamma() function handles gamma transformations of the data. +Pass both the file gamma and the current screen_gamma. If the file does +not have a gamma value, you can pass one anyway if you have an idea what +it is (usually 0.45455 is a good guess for GIF images on PCs). Note +that file gammas are inverted from screen gammas. See the discussions +on gamma in the PNG specification for an excellent description of what +gamma is, and why all applications should support it. It is strongly +recommended that PNG viewers support gamma correction. + + if (png_get_gAMA(png_ptr, info_ptr, &gamma)) + png_set_gamma(png_ptr, screen_gamma, gamma); + else + png_set_gamma(png_ptr, screen_gamma, 0.45455); + +If you need to reduce an RGB file to a paletted file, or if a paletted +file has more entries then will fit on your screen, png_set_dither() +will do that. Note that this is a simple match dither that merely +finds the closest color available. This should work fairly well with +optimized palettes, and fairly badly with linear color cubes. If you +pass a palette that is larger then maximum_colors, the file will +reduce the number of colors in the palette so it will fit into +maximum_colors. If there is a histogram, it will use it to make +more intelligent choices when reducing the palette. If there is no +histogram, it may not do as good a job. + + if (color_type & PNG_COLOR_MASK_COLOR) + { + if (png_get_valid(png_ptr, info_ptr, + PNG_INFO_PLTE)) + { + png_uint_16p histogram = NULL; + + png_get_hIST(png_ptr, info_ptr, + &histogram); + png_set_dither(png_ptr, palette, num_palette, + max_screen_colors, histogram, 1); + } + else + { + png_color std_color_cube[MAX_SCREEN_COLORS] = + { ... colors ... }; + + png_set_dither(png_ptr, std_color_cube, + MAX_SCREEN_COLORS, MAX_SCREEN_COLORS, + NULL,0); + } + } + +PNG files describe monochrome as black being zero and white being one. +The following code will reverse this (make black be one and white be +zero): + + if (bit_depth == 1 && color_type == PNG_COLOR_TYPE_GRAY) + png_set_invert_mono(png_ptr); + +This function can also be used to invert grayscale and gray-alpha images: + + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_invert_mono(png_ptr); + +PNG files store 16 bit pixels in network byte order (big-endian, +ie. most significant bits first). This code changes the storage to the +other way (little-endian, i.e. least significant bits first, the +way PCs store them): + + if (bit_depth == 16) + png_set_swap(png_ptr); + +If you are using packed-pixel images (1, 2, or 4 bits/pixel), and you +need to change the order the pixels are packed into bytes, you can use: + + if (bit_depth < 8) + png_set_packswap(png_ptr); + +Finally, you can write your own transformation function if none of +the existing ones meets your needs. This is done by setting a callback +with + + png_set_read_user_transform_fn(png_ptr, + read_transform_fn); + +You must supply the function + + void read_transform_fn(png_ptr ptr, row_info_ptr + row_info, png_bytep data) + +See pngtest.c for a working example. Your function will be called +after all of the other transformations have been processed. + +You can also set up a pointer to a user structure for use by your +callback function, and you can inform libpng that your transform +function will change the number of channels or bit depth with the +function + + png_set_user_transform_info(png_ptr, user_ptr, + user_depth, user_channels); + +The user's application, not libpng, is responsible for allocating and +freeing any memory required for the user structure. + +You can retrieve the pointer via the function +png_get_user_transform_ptr(). For example: + + voidp read_user_transform_ptr = + png_get_user_transform_ptr(png_ptr); + +The last thing to handle is interlacing; this is covered in detail below, +but you must call the function here if you want libpng to handle expansion +of the interlaced image. + + number_of_passes = png_set_interlace_handling(png_ptr); + +After setting the transformations, libpng can update your png_info +structure to reflect any transformations you've requested with this +call. This is most useful to update the info structure's rowbytes +field so you can use it to allocate your image memory. This function +will also update your palette with the correct screen_gamma and +background if these have been given with the calls above. + + png_read_update_info(png_ptr, info_ptr); + +After you call png_read_update_info(), you can allocate any +memory you need to hold the image. The row data is simply +raw byte data for all forms of images. As the actual allocation +varies among applications, no example will be given. If you +are allocating one large chunk, you will need to build an +array of pointers to each row, as it will be needed for some +of the functions below. + +Reading image data + +After you've allocated memory, you can read the image data. +The simplest way to do this is in one function call. If you are +allocating enough memory to hold the whole image, you can just +call png_read_image() and libpng will read in all the image data +and put it in the memory area supplied. You will need to pass in +an array of pointers to each row. + +This function automatically handles interlacing, so you don't need +to call png_set_interlace_handling() or call this function multiple +times, or any of that other stuff necessary with png_read_rows(). + + png_read_image(png_ptr, row_pointers); + +where row_pointers is: + + png_bytep row_pointers[height]; + +You can point to void or char or whatever you use for pixels. + +If you don't want to read in the whole image at once, you can +use png_read_rows() instead. If there is no interlacing (check +interlace_type == PNG_INTERLACE_NONE), this is simple: + + png_read_rows(png_ptr, row_pointers, NULL, + number_of_rows); + +where row_pointers is the same as in the png_read_image() call. + +If you are doing this just one row at a time, you can do this with +a single row_pointer instead of an array of row_pointers: + + png_bytep row_pointer = row; + png_read_row(png_ptr, row_pointer, NULL); + +If the file is interlaced (interlace_type != 0 in the IHDR chunk), things +get somewhat harder. The only current (PNG Specification version 1.2) +interlacing type for PNG is (interlace_type == PNG_INTERLACE_ADAM7) +is a somewhat complicated 2D interlace scheme, known as Adam7, that +breaks down an image into seven smaller images of varying size, based +on an 8x8 grid. + +libpng can fill out those images or it can give them to you "as is". +If you want them filled out, there are two ways to do that. The one +mentioned in the PNG specification is to expand each pixel to cover +those pixels that have not been read yet (the "rectangle" method). +This results in a blocky image for the first pass, which gradually +smooths out as more pixels are read. The other method is the "sparkle" +method, where pixels are drawn only in their final locations, with the +rest of the image remaining whatever colors they were initialized to +before the start of the read. The first method usually looks better, +but tends to be slower, as there are more pixels to put in the rows. + +If you don't want libpng to handle the interlacing details, just call +png_read_rows() seven times to read in all seven images. Each of the +images is a valid image by itself, or they can all be combined on an +8x8 grid to form a single image (although if you intend to combine them +you would be far better off using the libpng interlace handling). + +The first pass will return an image 1/8 as wide as the entire image +(every 8th column starting in column 0) and 1/8 as high as the original +(every 8th row starting in row 0), the second will be 1/8 as wide +(starting in column 4) and 1/8 as high (also starting in row 0). The +third pass will be 1/4 as wide (every 4th pixel starting in column 0) and +1/8 as high (every 8th row starting in row 4), and the fourth pass will +be 1/4 as wide and 1/4 as high (every 4th column starting in column 2, +and every 4th row starting in row 0). The fifth pass will return an +image 1/2 as wide, and 1/4 as high (starting at column 0 and row 2), +while the sixth pass will be 1/2 as wide and 1/2 as high as the original +(starting in column 1 and row 0). The seventh and final pass will be as +wide as the original, and 1/2 as high, containing all of the odd +numbered scanlines. Phew! + +If you want libpng to expand the images, call this before calling +png_start_read_image() or png_read_update_info(): + + if (interlace_type == PNG_INTERLACE_ADAM7) + number_of_passes + = png_set_interlace_handling(png_ptr); + +This will return the number of passes needed. Currently, this +is seven, but may change if another interlace type is added. +This function can be called even if the file is not interlaced, +where it will return one pass. + +If you are not going to display the image after each pass, but are +going to wait until the entire image is read in, use the sparkle +effect. This effect is faster and the end result of either method +is exactly the same. If you are planning on displaying the image +after each pass, the "rectangle" effect is generally considered the +better looking one. + +If you only want the "sparkle" effect, just call png_read_rows() as +normal, with the third parameter NULL. Make sure you make pass over +the image number_of_passes times, and you don't change the data in the +rows between calls. You can change the locations of the data, just +not the data. Each pass only writes the pixels appropriate for that +pass, and assumes the data from previous passes is still valid. + + png_read_rows(png_ptr, row_pointers, NULL, + number_of_rows); + +If you only want the first effect (the rectangles), do the same as +before except pass the row buffer in the third parameter, and leave +the second parameter NULL. + + png_read_rows(png_ptr, NULL, row_pointers, + number_of_rows); + +Finishing a sequential read + +After you are finished reading the image through either the high- or +low-level interfaces, you can finish reading the file. If you are +interested in comments or time, which may be stored either before or +after the image data, you should pass the separate png_info struct if +you want to keep the comments from before and after the image +separate. If you are not interested, you can pass NULL. + + png_read_end(png_ptr, end_info); + +When you are done, you can free all memory allocated by libpng like this: + + png_destroy_read_struct(&png_ptr, &info_ptr, + &end_info); + +It is also possible to individually free the info_ptr members that +point to libpng-allocated storage with the following function: + + png_free_data(png_ptr, info_ptr, mask, seq) + mask - identifies data to be freed, a mask + containing the logical OR of one or + more of + PNG_FREE_PLTE, PNG_FREE_TRNS, + PNG_FREE_HIST, PNG_FREE_ICCP, + PNG_FREE_PCAL, PNG_FREE_ROWS, + PNG_FREE_SCAL, PNG_FREE_SPLT, + PNG_FREE_TEXT, PNG_FREE_UNKN, + or simply PNG_FREE_ALL + seq - sequence number of item to be freed + (-1 for all items) + +This function may be safely called when the relevant storage has +already been freed, or has not yet been allocated, or was allocated +by the user and not by libpng, and will in those +cases do nothing. The "seq" parameter is ignored if only one item +of the selected data type, such as PLTE, is allowed. If "seq" is not +-1, and multiple items are allowed for the data type identified in +the mask, such as text or sPLT, only the n'th item in the structure +is freed, where n is "seq". + +The default behavior is only to free data that was allocated internally +by libpng. This can be changed, so that libpng will not free the data, +or so that it will free data that was allocated by the user with png_malloc() +or png_zalloc() and passed in via a png_set_*() function, with + + png_data_freer(png_ptr, info_ptr, freer, mask) + mask - which data elements are affected + same choices as in png_free_data() + freer - one of + PNG_DESTROY_WILL_FREE_DATA + PNG_SET_WILL_FREE_DATA + PNG_USER_WILL_FREE_DATA + +This function only affects data that has already been allocated. +You can call this function after reading the PNG data but before calling +any png_set_*() functions, to control whether the user or the png_set_*() +function is responsible for freeing any existing data that might be present, +and again after the png_set_*() functions to control whether the user +or png_destroy_*() is supposed to free the data. When the user assumes +responsibility for libpng-allocated data, the application must use +png_free() to free it, and when the user transfers responsibility to libpng +for data that the user has allocated, the user must have used png_malloc() +or png_zalloc() to allocate it. + +If you allocated your row_pointers in a single block, as suggested above in +the description of the high level read interface, you must not transfer +responsibility for freeing it to the png_set_rows or png_read_destroy function, +because they would also try to free the individual row_pointers[i]. + +If you allocated text_ptr.text, text_ptr.lang, and text_ptr.translated_keyword +separately, do not transfer responsibility for freeing text_ptr to libpng, +because when libpng fills a png_text structure it combines these members with +the key member, and png_free_data() will free only text_ptr.key. Similarly, +if you transfer responsibility for free'ing text_ptr from libpng to your +application, your application must not separately free those members. + +The png_free_data() function will turn off the "valid" flag for anything +it frees. If you need to turn the flag off for a chunk that was freed by your +application instead of by libpng, you can use + + png_set_invalid(png_ptr, info_ptr, mask); + mask - identifies the chunks to be made invalid, + containing the logical OR of one or + more of + PNG_INFO_gAMA, PNG_INFO_sBIT, + PNG_INFO_cHRM, PNG_INFO_PLTE, + PNG_INFO_tRNS, PNG_INFO_bKGD, + PNG_INFO_hIST, PNG_INFO_pHYs, + PNG_INFO_oFFs, PNG_INFO_tIME, + PNG_INFO_pCAL, PNG_INFO_sRGB, + PNG_INFO_iCCP, PNG_INFO_sPLT, + PNG_INFO_sCAL, PNG_INFO_IDAT + +For a more compact example of reading a PNG image, see the file example.c. + +Reading PNG files progressively + +The progressive reader is slightly different then the non-progressive +reader. Instead of calling png_read_info(), png_read_rows(), and +png_read_end(), you make one call to png_process_data(), which calls +callbacks when it has the info, a row, or the end of the image. You +set up these callbacks with png_set_progressive_read_fn(). You don't +have to worry about the input/output functions of libpng, as you are +giving the library the data directly in png_process_data(). I will +assume that you have read the section on reading PNG files above, +so I will only highlight the differences (although I will show +all of the code). + +png_structp png_ptr; +png_infop info_ptr; + + /* An example code fragment of how you would + initialize the progressive reader in your + application. */ + int + initialize_png_reader() + { + png_ptr = png_create_read_struct + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn); + if (!png_ptr) + return (ERROR); + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, + (png_infopp)NULL); + return (ERROR); + } + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, + (png_infopp)NULL); + return (ERROR); + } + + /* This one's new. You can provide functions + to be called when the header info is valid, + when each row is completed, and when the image + is finished. If you aren't using all functions, + you can specify NULL parameters. Even when all + three functions are NULL, you need to call + png_set_progressive_read_fn(). You can use + any struct as the user_ptr (cast to a void pointer + for the function call), and retrieve the pointer + from inside the callbacks using the function + + png_get_progressive_ptr(png_ptr); + + which will return a void pointer, which you have + to cast appropriately. + */ + png_set_progressive_read_fn(png_ptr, (void *)user_ptr, + info_callback, row_callback, end_callback); + + return 0; + } + + /* A code fragment that you call as you receive blocks + of data */ + int + process_data(png_bytep buffer, png_uint_32 length) + { + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, + (png_infopp)NULL); + return (ERROR); + } + + /* This one's new also. Simply give it a chunk + of data from the file stream (in order, of + course). On machines with segmented memory + models machines, don't give it any more than + 64K. The library seems to run fine with sizes + of 4K. Although you can give it much less if + necessary (I assume you can give it chunks of + 1 byte, I haven't tried less then 256 bytes + yet). When this function returns, you may + want to display any rows that were generated + in the row callback if you don't already do + so there. + */ + png_process_data(png_ptr, info_ptr, buffer, length); + return 0; + } + + /* This function is called (as set by + png_set_progressive_read_fn() above) when enough data + has been supplied so all of the header has been + read. + */ + void + info_callback(png_structp png_ptr, png_infop info) + { + /* Do any setup here, including setting any of + the transformations mentioned in the Reading + PNG files section. For now, you _must_ call + either png_start_read_image() or + png_read_update_info() after all the + transformations are set (even if you don't set + any). You may start getting rows before + png_process_data() returns, so this is your + last chance to prepare for that. + */ + } + + /* This function is called when each row of image + data is complete */ + void + row_callback(png_structp png_ptr, png_bytep new_row, + png_uint_32 row_num, int pass) + { + /* If the image is interlaced, and you turned + on the interlace handler, this function will + be called for every row in every pass. Some + of these rows will not be changed from the + previous pass. When the row is not changed, + the new_row variable will be NULL. The rows + and passes are called in order, so you don't + really need the row_num and pass, but I'm + supplying them because it may make your life + easier. + + For the non-NULL rows of interlaced images, + you must call png_progressive_combine_row() + passing in the row and the old row. You can + call this function for NULL rows (it will just + return) and for non-interlaced images (it just + does the memcpy for you) if it will make the + code easier. Thus, you can just do this for + all cases: + */ + + png_progressive_combine_row(png_ptr, old_row, + new_row); + + /* where old_row is what was displayed for + previously for the row. Note that the first + pass (pass == 0, really) will completely cover + the old row, so the rows do not have to be + initialized. After the first pass (and only + for interlaced images), you will have to pass + the current row, and the function will combine + the old row and the new row. + */ + } + + void + end_callback(png_structp png_ptr, png_infop info) + { + /* This function is called after the whole image + has been read, including any chunks after the + image (up to and including the IEND). You + will usually have the same info chunk as you + had in the header, although some data may have + been added to the comments and time fields. + + Most people won't do much here, perhaps setting + a flag that marks the image as finished. + */ + } + + + +IV. Writing + +Much of this is very similar to reading. However, everything of +importance is repeated here, so you won't have to constantly look +back up in the reading section to understand writing. + +Setup + +You will want to do the I/O initialization before you get into libpng, +so if it doesn't work, you don't have anything to undo. If you are not +using the standard I/O functions, you will need to replace them with +custom writing functions. See the discussion under Customizing libpng. + + FILE *fp = fopen(file_name, "wb"); + if (!fp) + { + return (ERROR); + } + +Next, png_struct and png_info need to be allocated and initialized. +As these can be both relatively large, you may not want to store these +on the stack, unless you have stack space to spare. Of course, you +will want to check if they return NULL. If you are also reading, +you won't want to name your read structure and your write structure +both "png_ptr"; you can call them anything you like, such as +"read_ptr" and "write_ptr". Look at pngtest.c, for example. + + png_structp png_ptr = png_create_write_struct + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn); + if (!png_ptr) + return (ERROR); + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_write_struct(&png_ptr, + (png_infopp)NULL); + return (ERROR); + } + +If you want to use your own memory allocation routines, +define PNG_USER_MEM_SUPPORTED and use +png_create_write_struct_2() instead of png_create_write_struct(): + + png_structp png_ptr = png_create_write_struct_2 + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn, (png_voidp) + user_mem_ptr, user_malloc_fn, user_free_fn); + +After you have these structures, you will need to set up the +error handling. When libpng encounters an error, it expects to +longjmp() back to your routine. Therefore, you will need to call +setjmp() and pass the png_jmpbuf(png_ptr). If you +write the file from different routines, you will need to update +the png_jmpbuf(png_ptr) every time you enter a new routine that will +call a png_*() function. See your documentation of setjmp/longjmp +for your compiler for more information on setjmp/longjmp. See +the discussion on libpng error handling in the Customizing Libpng +section below for more information on the libpng error handling. + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_write_struct(&png_ptr, &info_ptr); + fclose(fp); + return (ERROR); + } + ... + return; + +If you would rather avoid the complexity of setjmp/longjmp issues, +you can compile libpng with PNG_SETJMP_NOT_SUPPORTED, in which case +errors will result in a call to PNG_ABORT() which defaults to abort(). + +Now you need to set up the output code. The default for libpng is to +use the C function fwrite(). If you use this, you will need to pass a +valid FILE * in the function png_init_io(). Be sure that the file is +opened in binary mode. Again, if you wish to handle writing data in +another way, see the discussion on libpng I/O handling in the Customizing +Libpng section below. + + png_init_io(png_ptr, fp); + +Write callbacks + +At this point, you can set up a callback function that will be +called after each row has been written, which you can use to control +a progress meter or the like. It's demonstrated in pngtest.c. +You must supply a function + + void write_row_callback(png_ptr, png_uint_32 row, + int pass); + { + /* put your code here */ + } + +(You can give it another name that you like instead of "write_row_callback") + +To inform libpng about your function, use + + png_set_write_status_fn(png_ptr, write_row_callback); + +You now have the option of modifying how the compression library will +run. The following functions are mainly for testing, but may be useful +in some cases, like if you need to write PNG files extremely fast and +are willing to give up some compression, or if you want to get the +maximum possible compression at the expense of slower writing. If you +have no special needs in this area, let the library do what it wants by +not calling this function at all, as it has been tuned to deliver a good +speed/compression ratio. The second parameter to png_set_filter() is +the filter method, for which the only valid values are 0 (as of the +July 1999 PNG specification, version 1.2) or 64 (if you are writing +a PNG datastream that is to be embedded in a MNG datastream). The third +parameter is a flag that indicates which filter type(s) are to be tested +for each scanline. See the PNG specification for details on the specific filter +types. + + + /* turn on or off filtering, and/or choose + specific filters. You can use either a single + PNG_FILTER_VALUE_NAME or the logical OR of one + or more PNG_FILTER_NAME masks. */ + png_set_filter(png_ptr, 0, + PNG_FILTER_NONE | PNG_FILTER_VALUE_NONE | + PNG_FILTER_SUB | PNG_FILTER_VALUE_SUB | + PNG_FILTER_UP | PNG_FILTER_VALUE_UP | + PNG_FILTER_AVE | PNG_FILTER_VALUE_AVE | + PNG_FILTER_PAETH | PNG_FILTER_VALUE_PAETH| + PNG_ALL_FILTERS); + +If an application +wants to start and stop using particular filters during compression, +it should start out with all of the filters (to ensure that the previous +row of pixels will be stored in case it's needed later), and then add +and remove them after the start of compression. + +If you are writing a PNG datastream that is to be embedded in a MNG +datastream, the second parameter can be either 0 or 64. + +The png_set_compression_*() functions interface to the zlib compression +library, and should mostly be ignored unless you really know what you are +doing. The only generally useful call is png_set_compression_level() +which changes how much time zlib spends on trying to compress the image +data. See the Compression Library (zlib.h and algorithm.txt, distributed +with zlib) for details on the compression levels. + + /* set the zlib compression level */ + png_set_compression_level(png_ptr, + Z_BEST_COMPRESSION); + + /* set other zlib parameters */ + png_set_compression_mem_level(png_ptr, 8); + png_set_compression_strategy(png_ptr, + Z_DEFAULT_STRATEGY); + png_set_compression_window_bits(png_ptr, 15); + png_set_compression_method(png_ptr, 8); + png_set_compression_buffer_size(png_ptr, 8192) + +extern PNG_EXPORT(void,png_set_zbuf_size) + +Setting the contents of info for output + +You now need to fill in the png_info structure with all the data you +wish to write before the actual image. Note that the only thing you +are allowed to write after the image is the text chunks and the time +chunk (as of PNG Specification 1.2, anyway). See png_write_end() and +the latest PNG specification for more information on that. If you +wish to write them before the image, fill them in now, and flag that +data as being valid. If you want to wait until after the data, don't +fill them until png_write_end(). For all the fields in png_info and +their data types, see png.h. For explanations of what the fields +contain, see the PNG specification. + +Some of the more important parts of the png_info are: + + png_set_IHDR(png_ptr, info_ptr, width, height, + bit_depth, color_type, interlace_type, + compression_type, filter_method) + width - holds the width of the image + in pixels (up to 2^31). + height - holds the height of the image + in pixels (up to 2^31). + bit_depth - holds the bit depth of one of the + image channels. + (valid values are 1, 2, 4, 8, 16 + and depend also on the + color_type. See also significant + bits (sBIT) below). + color_type - describes which color/alpha + channels are present. + PNG_COLOR_TYPE_GRAY + (bit depths 1, 2, 4, 8, 16) + PNG_COLOR_TYPE_GRAY_ALPHA + (bit depths 8, 16) + PNG_COLOR_TYPE_PALETTE + (bit depths 1, 2, 4, 8) + PNG_COLOR_TYPE_RGB + (bit_depths 8, 16) + PNG_COLOR_TYPE_RGB_ALPHA + (bit_depths 8, 16) + + PNG_COLOR_MASK_PALETTE + PNG_COLOR_MASK_COLOR + PNG_COLOR_MASK_ALPHA + + interlace_type - PNG_INTERLACE_NONE or + PNG_INTERLACE_ADAM7 + compression_type - (must be + PNG_COMPRESSION_TYPE_DEFAULT) + filter_method - (must be PNG_FILTER_TYPE_DEFAULT + or, if you are writing a PNG to + be embedded in a MNG datastream, + can also be + PNG_INTRAPIXEL_DIFFERENCING) + + png_set_PLTE(png_ptr, info_ptr, palette, + num_palette); + palette - the palette for the file + (array of png_color) + num_palette - number of entries in the palette + + png_set_gAMA(png_ptr, info_ptr, gamma); + gamma - the gamma the image was created + at (PNG_INFO_gAMA) + + png_set_sRGB(png_ptr, info_ptr, srgb_intent); + srgb_intent - the rendering intent + (PNG_INFO_sRGB) The presence of + the sRGB chunk means that the pixel + data is in the sRGB color space. + This chunk also implies specific + values of gAMA and cHRM. Rendering + intent is the CSS-1 property that + has been defined by the International + Color Consortium + (http://www.color.org). + It can be one of + PNG_sRGB_INTENT_SATURATION, + PNG_sRGB_INTENT_PERCEPTUAL, + PNG_sRGB_INTENT_ABSOLUTE, or + PNG_sRGB_INTENT_RELATIVE. + + + png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, + srgb_intent); + srgb_intent - the rendering intent + (PNG_INFO_sRGB) The presence of the + sRGB chunk means that the pixel + data is in the sRGB color space. + This function also causes gAMA and + cHRM chunks with the specific values + that are consistent with sRGB to be + written. + + png_set_iCCP(png_ptr, info_ptr, name, compression_type, + profile, proflen); + name - The profile name. + compression - The compression type; always + PNG_COMPRESSION_TYPE_BASE for PNG 1.0. + You may give NULL to this argument to + ignore it. + profile - International Color Consortium color + profile data. May contain NULs. + proflen - length of profile data in bytes. + + png_set_sBIT(png_ptr, info_ptr, sig_bit); + sig_bit - the number of significant bits for + (PNG_INFO_sBIT) each of the gray, red, + green, and blue channels, whichever are + appropriate for the given color type + (png_color_16) + + png_set_tRNS(png_ptr, info_ptr, trans, num_trans, + trans_values); + trans - array of transparent entries for + palette (PNG_INFO_tRNS) + trans_values - graylevel or color sample values of + the single transparent color for + non-paletted images (PNG_INFO_tRNS) + num_trans - number of transparent entries + (PNG_INFO_tRNS) + + png_set_hIST(png_ptr, info_ptr, hist); + (PNG_INFO_hIST) + hist - histogram of palette (array of + png_uint_16) + + png_set_tIME(png_ptr, info_ptr, mod_time); + mod_time - time image was last modified + (PNG_VALID_tIME) + + png_set_bKGD(png_ptr, info_ptr, background); + background - background color (PNG_VALID_bKGD) + + png_set_text(png_ptr, info_ptr, text_ptr, num_text); + text_ptr - array of png_text holding image + comments + text_ptr[i].compression - type of compression used + on "text" PNG_TEXT_COMPRESSION_NONE + PNG_TEXT_COMPRESSION_zTXt + PNG_ITXT_COMPRESSION_NONE + PNG_ITXT_COMPRESSION_zTXt + text_ptr[i].key - keyword for comment. Must contain + 1-79 characters. + text_ptr[i].text - text comments for current + keyword. Can be NULL or empty. + text_ptr[i].text_length - length of text string, + after decompression, 0 for iTXt + text_ptr[i].itxt_length - length of itxt string, + after decompression, 0 for tEXt/zTXt + text_ptr[i].lang - language of comment (NULL or + empty for unknown). + text_ptr[i].translated_keyword - keyword in UTF-8 (NULL + or empty for unknown). + num_text - number of comments + + png_set_sPLT(png_ptr, info_ptr, &palette_ptr, + num_spalettes); + palette_ptr - array of png_sPLT_struct structures + to be added to the list of palettes + in the info structure. + num_spalettes - number of palette structures to be + added. + + png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, + unit_type); + offset_x - positive offset from the left + edge of the screen + offset_y - positive offset from the top + edge of the screen + unit_type - PNG_OFFSET_PIXEL, PNG_OFFSET_MICROMETER + + png_set_pHYs(png_ptr, info_ptr, res_x, res_y, + unit_type); + res_x - pixels/unit physical resolution + in x direction + res_y - pixels/unit physical resolution + in y direction + unit_type - PNG_RESOLUTION_UNKNOWN, + PNG_RESOLUTION_METER + + png_set_sCAL(png_ptr, info_ptr, unit, width, height) + unit - physical scale units (an integer) + width - width of a pixel in physical scale units + height - height of a pixel in physical scale units + (width and height are doubles) + + png_set_sCAL_s(png_ptr, info_ptr, unit, width, height) + unit - physical scale units (an integer) + width - width of a pixel in physical scale units + height - height of a pixel in physical scale units + (width and height are strings like "2.54") + + png_set_unknown_chunks(png_ptr, info_ptr, &unknowns, + num_unknowns) + unknowns - array of png_unknown_chunk + structures holding unknown chunks + unknowns[i].name - name of unknown chunk + unknowns[i].data - data of unknown chunk + unknowns[i].size - size of unknown chunk's data + unknowns[i].location - position to write chunk in file + 0: do not write chunk + PNG_HAVE_IHDR: before PLTE + PNG_HAVE_PLTE: before IDAT + PNG_AFTER_IDAT: after IDAT + +The "location" member is set automatically according to +what part of the output file has already been written. +You can change its value after calling png_set_unknown_chunks() +as demonstrated in pngtest.c. Within each of the "locations", +the chunks are sequenced according to their position in the +structure (that is, the value of "i", which is the order in which +the chunk was either read from the input file or defined with +png_set_unknown_chunks). + +A quick word about text and num_text. text is an array of png_text +structures. num_text is the number of valid structures in the array. +Each png_text structure holds a language code, a keyword, a text value, +and a compression type. + +The compression types have the same valid numbers as the compression +types of the image data. Currently, the only valid number is zero. +However, you can store text either compressed or uncompressed, unlike +images, which always have to be compressed. So if you don't want the +text compressed, set the compression type to PNG_TEXT_COMPRESSION_NONE. +Because tEXt and zTXt chunks don't have a language field, if you +specify PNG_TEXT_COMPRESSION_NONE or PNG_TEXT_COMPRESSION_zTXt +any language code or translated keyword will not be written out. + +Until text gets around 1000 bytes, it is not worth compressing it. +After the text has been written out to the file, the compression type +is set to PNG_TEXT_COMPRESSION_NONE_WR or PNG_TEXT_COMPRESSION_zTXt_WR, +so that it isn't written out again at the end (in case you are calling +png_write_end() with the same struct. + +The keywords that are given in the PNG Specification are: + + Title Short (one line) title or + caption for image + Author Name of image's creator + Description Description of image (possibly long) + Copyright Copyright notice + Creation Time Time of original image creation + (usually RFC 1123 format, see below) + Software Software used to create the image + Disclaimer Legal disclaimer + Warning Warning of nature of content + Source Device used to create the image + Comment Miscellaneous comment; conversion + from other image format + +The keyword-text pairs work like this. Keywords should be short +simple descriptions of what the comment is about. Some typical +keywords are found in the PNG specification, as is some recommendations +on keywords. You can repeat keywords in a file. You can even write +some text before the image and some after. For example, you may want +to put a description of the image before the image, but leave the +disclaimer until after, so viewers working over modem connections +don't have to wait for the disclaimer to go over the modem before +they start seeing the image. Finally, keywords should be full +words, not abbreviations. Keywords and text are in the ISO 8859-1 +(Latin-1) character set (a superset of regular ASCII) and can not +contain NUL characters, and should not contain control or other +unprintable characters. To make the comments widely readable, stick +with basic ASCII, and avoid machine specific character set extensions +like the IBM-PC character set. The keyword must be present, but +you can leave off the text string on non-compressed pairs. +Compressed pairs must have a text string, as only the text string +is compressed anyway, so the compression would be meaningless. + +PNG supports modification time via the png_time structure. Two +conversion routines are provided, png_convert_from_time_t() for +time_t and png_convert_from_struct_tm() for struct tm. The +time_t routine uses gmtime(). You don't have to use either of +these, but if you wish to fill in the png_time structure directly, +you should provide the time in universal time (GMT) if possible +instead of your local time. Note that the year number is the full +year (e.g. 1998, rather than 98 - PNG is year 2000 compliant!), and +that months start with 1. + +If you want to store the time of the original image creation, you should +use a plain tEXt chunk with the "Creation Time" keyword. This is +necessary because the "creation time" of a PNG image is somewhat vague, +depending on whether you mean the PNG file, the time the image was +created in a non-PNG format, a still photo from which the image was +scanned, or possibly the subject matter itself. In order to facilitate +machine-readable dates, it is recommended that the "Creation Time" +tEXt chunk use RFC 1123 format dates (e.g. "22 May 1997 18:07:10 GMT"), +although this isn't a requirement. Unlike the tIME chunk, the +"Creation Time" tEXt chunk is not expected to be automatically changed +by the software. To facilitate the use of RFC 1123 dates, a function +png_convert_to_rfc1123(png_timep) is provided to convert from PNG +time to an RFC 1123 format string. + +Writing unknown chunks + +You can use the png_set_unknown_chunks function to queue up chunks +for writing. You give it a chunk name, raw data, and a size; that's +all there is to it. The chunks will be written by the next following +png_write_info_before_PLTE, png_write_info, or png_write_end function. +Any chunks previously read into the info structure's unknown-chunk +list will also be written out in a sequence that satisfies the PNG +specification's ordering rules. + +The high-level write interface + +At this point there are two ways to proceed; through the high-level +write interface, or through a sequence of low-level write operations. +You can use the high-level interface if your image data is present +in the info structure. All defined output +transformations are permitted, enabled by the following masks. + + PNG_TRANSFORM_IDENTITY No transformation + PNG_TRANSFORM_PACKING Pack 1, 2 and 4-bit samples + PNG_TRANSFORM_PACKSWAP Change order of packed + pixels to LSB first + PNG_TRANSFORM_INVERT_MONO Invert monochrome images + PNG_TRANSFORM_SHIFT Normalize pixels to the + sBIT depth + PNG_TRANSFORM_BGR Flip RGB to BGR, RGBA + to BGRA + PNG_TRANSFORM_SWAP_ALPHA Flip RGBA to ARGB or GA + to AG + PNG_TRANSFORM_INVERT_ALPHA Change alpha from opacity + to transparency + PNG_TRANSFORM_SWAP_ENDIAN Byte-swap 16-bit samples + PNG_TRANSFORM_STRIP_FILLER Strip out filler bytes. + +If you have valid image data in the info structure (you can use +png_set_rows() to put image data in the info structure), simply do this: + + png_write_png(png_ptr, info_ptr, png_transforms, NULL) + +where png_transforms is an integer containing the logical OR of some set of +transformation flags. This call is equivalent to png_write_info(), +followed the set of transformations indicated by the transform mask, +then png_write_image(), and finally png_write_end(). + +(The final parameter of this call is not yet used. Someday it might point +to transformation parameters required by some future output transform.) + +You must use png_transforms and not call any png_set_transform() functions +when you use png_write_png(). + +The low-level write interface + +If you are going the low-level route instead, you are now ready to +write all the file information up to the actual image data. You do +this with a call to png_write_info(). + + png_write_info(png_ptr, info_ptr); + +Note that there is one transformation you may need to do before +png_write_info(). In PNG files, the alpha channel in an image is the +level of opacity. If your data is supplied as a level of +transparency, you can invert the alpha channel before you write it, so +that 0 is fully transparent and 255 (in 8-bit or paletted images) or +65535 (in 16-bit images) is fully opaque, with + + png_set_invert_alpha(png_ptr); + +This must appear before png_write_info() instead of later with the +other transformations because in the case of paletted images the tRNS +chunk data has to be inverted before the tRNS chunk is written. If +your image is not a paletted image, the tRNS data (which in such cases +represents a single color to be rendered as transparent) won't need to +be changed, and you can safely do this transformation after your +png_write_info() call. + +If you need to write a private chunk that you want to appear before +the PLTE chunk when PLTE is present, you can write the PNG info in +two steps, and insert code to write your own chunk between them: + + png_write_info_before_PLTE(png_ptr, info_ptr); + png_set_unknown_chunks(png_ptr, info_ptr, ...); + png_write_info(png_ptr, info_ptr); + +After you've written the file information, you can set up the library +to handle any special transformations of the image data. The various +ways to transform the data will be described in the order that they +should occur. This is important, as some of these change the color +type and/or bit depth of the data, and some others only work on +certain color types and bit depths. Even though each transformation +checks to see if it has data that it can do something with, you should +make sure to only enable a transformation if it will be valid for the +data. For example, don't swap red and blue on grayscale data. + +PNG files store RGB pixels packed into 3 or 6 bytes. This code tells +the library to strip input data that has 4 or 8 bytes per pixel down +to 3 or 6 bytes (or strip 2 or 4-byte grayscale+filler data to 1 or 2 +bytes per pixel). + + png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); + +where the 0 is unused, and the location is either PNG_FILLER_BEFORE or +PNG_FILLER_AFTER, depending upon whether the filler byte in the pixel +is stored XRGB or RGBX. + +PNG files pack pixels of bit depths 1, 2, and 4 into bytes as small as +they can, resulting in, for example, 8 pixels per byte for 1 bit files. +If the data is supplied at 1 pixel per byte, use this code, which will +correctly pack the pixels into a single byte: + + png_set_packing(png_ptr); + +PNG files reduce possible bit depths to 1, 2, 4, 8, and 16. If your +data is of another bit depth, you can write an sBIT chunk into the +file so that decoders can recover the original data if desired. + + /* Set the true bit depth of the image data */ + if (color_type & PNG_COLOR_MASK_COLOR) + { + sig_bit.red = true_bit_depth; + sig_bit.green = true_bit_depth; + sig_bit.blue = true_bit_depth; + } + else + { + sig_bit.gray = true_bit_depth; + } + if (color_type & PNG_COLOR_MASK_ALPHA) + { + sig_bit.alpha = true_bit_depth; + } + + png_set_sBIT(png_ptr, info_ptr, &sig_bit); + +If the data is stored in the row buffer in a bit depth other than +one supported by PNG (e.g. 3 bit data in the range 0-7 for a 4-bit PNG), +this will scale the values to appear to be the correct bit depth as +is required by PNG. + + png_set_shift(png_ptr, &sig_bit); + +PNG files store 16 bit pixels in network byte order (big-endian, +ie. most significant bits first). This code would be used if they are +supplied the other way (little-endian, i.e. least significant bits +first, the way PCs store them): + + if (bit_depth > 8) + png_set_swap(png_ptr); + +If you are using packed-pixel images (1, 2, or 4 bits/pixel), and you +need to change the order the pixels are packed into bytes, you can use: + + if (bit_depth < 8) + png_set_packswap(png_ptr); + +PNG files store 3 color pixels in red, green, blue order. This code +would be used if they are supplied as blue, green, red: + + png_set_bgr(png_ptr); + +PNG files describe monochrome as black being zero and white being +one. This code would be used if the pixels are supplied with this reversed +(black being one and white being zero): + + png_set_invert_mono(png_ptr); + +Finally, you can write your own transformation function if none of +the existing ones meets your needs. This is done by setting a callback +with + + png_set_write_user_transform_fn(png_ptr, + write_transform_fn); + +You must supply the function + + void write_transform_fn(png_ptr ptr, row_info_ptr + row_info, png_bytep data) + +See pngtest.c for a working example. Your function will be called +before any of the other transformations are processed. + +You can also set up a pointer to a user structure for use by your +callback function. + + png_set_user_transform_info(png_ptr, user_ptr, 0, 0); + +The user_channels and user_depth parameters of this function are ignored +when writing; you can set them to zero as shown. + +You can retrieve the pointer via the function png_get_user_transform_ptr(). +For example: + + voidp write_user_transform_ptr = + png_get_user_transform_ptr(png_ptr); + +It is possible to have libpng flush any pending output, either manually, +or automatically after a certain number of lines have been written. To +flush the output stream a single time call: + + png_write_flush(png_ptr); + +and to have libpng flush the output stream periodically after a certain +number of scanlines have been written, call: + + png_set_flush(png_ptr, nrows); + +Note that the distance between rows is from the last time png_write_flush() +was called, or the first row of the image if it has never been called. +So if you write 50 lines, and then png_set_flush 25, it will flush the +output on the next scanline, and every 25 lines thereafter, unless +png_write_flush() is called before 25 more lines have been written. +If nrows is too small (less than about 10 lines for a 640 pixel wide +RGB image) the image compression may decrease noticeably (although this +may be acceptable for real-time applications). Infrequent flushing will +only degrade the compression performance by a few percent over images +that do not use flushing. + +Writing the image data + +That's it for the transformations. Now you can write the image data. +The simplest way to do this is in one function call. If you have the +whole image in memory, you can just call png_write_image() and libpng +will write the image. You will need to pass in an array of pointers to +each row. This function automatically handles interlacing, so you don't +need to call png_set_interlace_handling() or call this function multiple +times, or any of that other stuff necessary with png_write_rows(). + + png_write_image(png_ptr, row_pointers); + +where row_pointers is: + + png_byte *row_pointers[height]; + +You can point to void or char or whatever you use for pixels. + +If you don't want to write the whole image at once, you can +use png_write_rows() instead. If the file is not interlaced, +this is simple: + + png_write_rows(png_ptr, row_pointers, + number_of_rows); + +row_pointers is the same as in the png_write_image() call. + +If you are just writing one row at a time, you can do this with +a single row_pointer instead of an array of row_pointers: + + png_bytep row_pointer = row; + + png_write_row(png_ptr, row_pointer); + +When the file is interlaced, things can get a good deal more +complicated. The only currently (as of the PNG Specification +version 1.2, dated July 1999) defined interlacing scheme for PNG files +is the "Adam7" interlace scheme, that breaks down an +image into seven smaller images of varying size. libpng will build +these images for you, or you can do them yourself. If you want to +build them yourself, see the PNG specification for details of which +pixels to write when. + +If you don't want libpng to handle the interlacing details, just +use png_set_interlace_handling() and call png_write_rows() the +correct number of times to write all seven sub-images. + +If you want libpng to build the sub-images, call this before you start +writing any rows: + + number_of_passes = + png_set_interlace_handling(png_ptr); + +This will return the number of passes needed. Currently, this +is seven, but may change if another interlace type is added. + +Then write the complete image number_of_passes times. + + png_write_rows(png_ptr, row_pointers, + number_of_rows); + +As some of these rows are not used, and thus return immediately, +you may want to read about interlacing in the PNG specification, +and only update the rows that are actually used. + +Finishing a sequential write + +After you are finished writing the image, you should finish writing +the file. If you are interested in writing comments or time, you should +pass an appropriately filled png_info pointer. If you are not interested, +you can pass NULL. + + png_write_end(png_ptr, info_ptr); + +When you are done, you can free all memory used by libpng like this: + + png_destroy_write_struct(&png_ptr, &info_ptr); + +It is also possible to individually free the info_ptr members that +point to libpng-allocated storage with the following function: + + png_free_data(png_ptr, info_ptr, mask, seq) + mask - identifies data to be freed, a mask + containing the logical OR of one or + more of + PNG_FREE_PLTE, PNG_FREE_TRNS, + PNG_FREE_HIST, PNG_FREE_ICCP, + PNG_FREE_PCAL, PNG_FREE_ROWS, + PNG_FREE_SCAL, PNG_FREE_SPLT, + PNG_FREE_TEXT, PNG_FREE_UNKN, + or simply PNG_FREE_ALL + seq - sequence number of item to be freed + (-1 for all items) + +This function may be safely called when the relevant storage has +already been freed, or has not yet been allocated, or was allocated +by the user and not by libpng, and will in those +cases do nothing. The "seq" parameter is ignored if only one item +of the selected data type, such as PLTE, is allowed. If "seq" is not +-1, and multiple items are allowed for the data type identified in +the mask, such as text or sPLT, only the n'th item in the structure +is freed, where n is "seq". + +If you allocated data such as a palette that you passed +in to libpng with png_set_*, you must not free it until just before the call to +png_destroy_write_struct(). + +The default behavior is only to free data that was allocated internally +by libpng. This can be changed, so that libpng will not free the data, +or so that it will free data that was allocated by the user with png_malloc() +or png_zalloc() and passed in via a png_set_*() function, with + + png_data_freer(png_ptr, info_ptr, freer, mask) + mask - which data elements are affected + same choices as in png_free_data() + freer - one of + PNG_DESTROY_WILL_FREE_DATA + PNG_SET_WILL_FREE_DATA + PNG_USER_WILL_FREE_DATA + +For example, to transfer responsibility for some data from a read structure +to a write structure, you could use + + png_data_freer(read_ptr, read_info_ptr, + PNG_USER_WILL_FREE_DATA, + PNG_FREE_PLTE|PNG_FREE_tRNS|PNG_FREE_hIST) + png_data_freer(write_ptr, write_info_ptr, + PNG_DESTROY_WILL_FREE_DATA, + PNG_FREE_PLTE|PNG_FREE_tRNS|PNG_FREE_hIST) + +thereby briefly reassigning responsibility for freeing to the user but +immediately afterwards reassigning it once more to the write_destroy +function. Having done this, it would then be safe to destroy the read +structure and continue to use the PLTE, tRNS, and hIST data in the write +structure. + +This function only affects data that has already been allocated. +You can call this function before calling after the png_set_*() functions +to control whether the user or png_destroy_*() is supposed to free the data. +When the user assumes responsibility for libpng-allocated data, the +application must use +png_free() to free it, and when the user transfers responsibility to libpng +for data that the user has allocated, the user must have used png_malloc() +or png_zalloc() to allocate it. + +If you allocated text_ptr.text, text_ptr.lang, and text_ptr.translated_keyword +separately, do not transfer responsibility for freeing text_ptr to libpng, +because when libpng fills a png_text structure it combines these members with +the key member, and png_free_data() will free only text_ptr.key. Similarly, +if you transfer responsibility for free'ing text_ptr from libpng to your +application, your application must not separately free those members. +For a more compact example of writing a PNG image, see the file example.c. + +V. Modifying/Customizing libpng: + +There are three issues here. The first is changing how libpng does +standard things like memory allocation, input/output, and error handling. +The second deals with more complicated things like adding new chunks, +adding new transformations, and generally changing how libpng works. +Both of those are compile-time issues; that is, they are generally +determined at the time the code is written, and there is rarely a need +to provide the user with a means of changing them. The third is a +run-time issue: choosing between and/or tuning one or more alternate +versions of computationally intensive routines; specifically, optimized +assembly-language (and therefore compiler- and platform-dependent) +versions. + +Memory allocation, input/output, and error handling + +All of the memory allocation, input/output, and error handling in libpng +goes through callbacks that are user-settable. The default routines are +in pngmem.c, pngrio.c, pngwio.c, and pngerror.c, respectively. To change +these functions, call the appropriate png_set_*_fn() function. + +Memory allocation is done through the functions png_malloc() +and png_free(). These currently just call the standard C functions. If +your pointers can't access more then 64K at a time, you will want to set +MAXSEG_64K in zlib.h. Since it is unlikely that the method of handling +memory allocation on a platform will change between applications, these +functions must be modified in the library at compile time. If you prefer +to use a different method of allocating and freeing data, you can use +png_create_read_struct_2() or png_create_write_struct_2() to register +your own functions as described above. +These functions also provide a void pointer that can be retrieved via + + mem_ptr=png_get_mem_ptr(png_ptr); + +Your replacement memory functions must have prototypes as follows: + + png_voidp malloc_fn(png_structp png_ptr, + png_size_t size); + void free_fn(png_structp png_ptr, png_voidp ptr); + +Your malloc_fn() must return NULL in case of failure. The png_malloc() +function will normally call png_error() if it receives a NULL from the +system memory allocator or from your replacement malloc_fn(). + +Input/Output in libpng is done through png_read() and png_write(), +which currently just call fread() and fwrite(). The FILE * is stored in +png_struct and is initialized via png_init_io(). If you wish to change +the method of I/O, the library supplies callbacks that you can set +through the function png_set_read_fn() and png_set_write_fn() at run +time, instead of calling the png_init_io() function. These functions +also provide a void pointer that can be retrieved via the function +png_get_io_ptr(). For example: + + png_set_read_fn(png_structp read_ptr, + voidp read_io_ptr, png_rw_ptr read_data_fn) + + png_set_write_fn(png_structp write_ptr, + voidp write_io_ptr, png_rw_ptr write_data_fn, + png_flush_ptr output_flush_fn); + + voidp read_io_ptr = png_get_io_ptr(read_ptr); + voidp write_io_ptr = png_get_io_ptr(write_ptr); + +The replacement I/O functions must have prototypes as follows: + + void user_read_data(png_structp png_ptr, + png_bytep data, png_size_t length); + void user_write_data(png_structp png_ptr, + png_bytep data, png_size_t length); + void user_flush_data(png_structp png_ptr); + +Supplying NULL for the read, write, or flush functions sets them back +to using the default C stream functions. It is an error to read from +a write stream, and vice versa. + +Error handling in libpng is done through png_error() and png_warning(). +Errors handled through png_error() are fatal, meaning that png_error() +should never return to its caller. Currently, this is handled via +setjmp() and longjmp() (unless you have compiled libpng with +PNG_SETJMP_NOT_SUPPORTED, in which case it is handled via PNG_ABORT()), +but you could change this to do things like exit() if you should wish. + +On non-fatal errors, png_warning() is called +to print a warning message, and then control returns to the calling code. +By default png_error() and png_warning() print a message on stderr via +fprintf() unless the library is compiled with PNG_NO_CONSOLE_IO defined +(because you don't want the messages) or PNG_NO_STDIO defined (because +fprintf() isn't available). If you wish to change the behavior of the error +functions, you will need to set up your own message callbacks. These +functions are normally supplied at the time that the png_struct is created. +It is also possible to redirect errors and warnings to your own replacement +functions after png_create_*_struct() has been called by calling: + + png_set_error_fn(png_structp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warning_fn); + + png_voidp error_ptr = png_get_error_ptr(png_ptr); + +If NULL is supplied for either error_fn or warning_fn, then the libpng +default function will be used, calling fprintf() and/or longjmp() if a +problem is encountered. The replacement error functions should have +parameters as follows: + + void user_error_fn(png_structp png_ptr, + png_const_charp error_msg); + void user_warning_fn(png_structp png_ptr, + png_const_charp warning_msg); + +The motivation behind using setjmp() and longjmp() is the C++ throw and +catch exception handling methods. This makes the code much easier to write, +as there is no need to check every return code of every function call. +However, there are some uncertainties about the status of local variables +after a longjmp, so the user may want to be careful about doing anything after +setjmp returns non-zero besides returning itself. Consult your compiler +documentation for more details. For an alternative approach, you may wish +to use the "cexcept" facility (see http://cexcept.sourceforge.net). + +Custom chunks + +If you need to read or write custom chunks, you may need to get deeper +into the libpng code. The library now has mechanisms for storing +and writing chunks of unknown type; you can even declare callbacks +for custom chunks. Hoewver, this may not be good enough if the +library code itself needs to know about interactions between your +chunk and existing `intrinsic' chunks. + +If you need to write a new intrinsic chunk, first read the PNG +specification. Acquire a first level of +understanding of how it works. Pay particular attention to the +sections that describe chunk names, and look at how other chunks were +designed, so you can do things similarly. Second, check out the +sections of libpng that read and write chunks. Try to find a chunk +that is similar to yours and use it as a template. More details can +be found in the comments inside the code. It is best to handle unknown +chunks in a generic method, via callback functions, instead of by +modifying libpng functions. + +If you wish to write your own transformation for the data, look through +the part of the code that does the transformations, and check out some of +the simpler ones to get an idea of how they work. Try to find a similar +transformation to the one you want to add and copy off of it. More details +can be found in the comments inside the code itself. + +Configuring for 16 bit platforms + +You will want to look into zconf.h to tell zlib (and thus libpng) that +it cannot allocate more then 64K at a time. Even if you can, the memory +won't be accessible. So limit zlib and libpng to 64K by defining MAXSEG_64K. + +Configuring for DOS + +For DOS users who only have access to the lower 640K, you will +have to limit zlib's memory usage via a png_set_compression_mem_level() +call. See zlib.h or zconf.h in the zlib library for more information. + +Configuring for Medium Model + +Libpng's support for medium model has been tested on most of the popular +compilers. Make sure MAXSEG_64K gets defined, USE_FAR_KEYWORD gets +defined, and FAR gets defined to far in pngconf.h, and you should be +all set. Everything in the library (except for zlib's structure) is +expecting far data. You must use the typedefs with the p or pp on +the end for pointers (or at least look at them and be careful). Make +note that the rows of data are defined as png_bytepp, which is an +unsigned char far * far *. + +Configuring for gui/windowing platforms: + +You will need to write new error and warning functions that use the GUI +interface, as described previously, and set them to be the error and +warning functions at the time that png_create_*_struct() is called, +in order to have them available during the structure initialization. +They can be changed later via png_set_error_fn(). On some compilers, +you may also have to change the memory allocators (png_malloc, etc.). + +Configuring for compiler xxx: + +All includes for libpng are in pngconf.h. If you need to add/change/delete +an include, this is the place to do it. The includes that are not +needed outside libpng are protected by the PNG_INTERNAL definition, +which is only defined for those routines inside libpng itself. The +files in libpng proper only include png.h, which includes pngconf.h. + +Configuring zlib: + +There are special functions to configure the compression. Perhaps the +most useful one changes the compression level, which currently uses +input compression values in the range 0 - 9. The library normally +uses the default compression level (Z_DEFAULT_COMPRESSION = 6). Tests +have shown that for a large majority of images, compression values in +the range 3-6 compress nearly as well as higher levels, and do so much +faster. For online applications it may be desirable to have maximum speed +(Z_BEST_SPEED = 1). With versions of zlib after v0.99, you can also +specify no compression (Z_NO_COMPRESSION = 0), but this would create +files larger than just storing the raw bitmap. You can specify the +compression level by calling: + + png_set_compression_level(png_ptr, level); + +Another useful one is to reduce the memory level used by the library. +The memory level defaults to 8, but it can be lowered if you are +short on memory (running DOS, for example, where you only have 640K). +Note that the memory level does have an effect on compression; among +other things, lower levels will result in sections of incompressible +data being emitted in smaller stored blocks, with a correspondingly +larger relative overhead of up to 15% in the worst case. + + png_set_compression_mem_level(png_ptr, level); + +The other functions are for configuring zlib. They are not recommended +for normal use and may result in writing an invalid PNG file. See +zlib.h for more information on what these mean. + + png_set_compression_strategy(png_ptr, + strategy); + png_set_compression_window_bits(png_ptr, + window_bits); + png_set_compression_method(png_ptr, method); + png_set_compression_buffer_size(png_ptr, size); + +Controlling row filtering + +If you want to control whether libpng uses filtering or not, which +filters are used, and how it goes about picking row filters, you +can call one of these functions. The selection and configuration +of row filters can have a significant impact on the size and +encoding speed and a somewhat lesser impact on the decoding speed +of an image. Filtering is enabled by default for RGB and grayscale +images (with and without alpha), but not for paletted images nor +for any images with bit depths less than 8 bits/pixel. + +The 'method' parameter sets the main filtering method, which is +currently only '0' in the PNG 1.2 specification. The 'filters' +parameter sets which filter(s), if any, should be used for each +scanline. Possible values are PNG_ALL_FILTERS and PNG_NO_FILTERS +to turn filtering on and off, respectively. + +Individual filter types are PNG_FILTER_NONE, PNG_FILTER_SUB, +PNG_FILTER_UP, PNG_FILTER_AVG, PNG_FILTER_PAETH, which can be bitwise +ORed together with '|' to specify one or more filters to use. +These filters are described in more detail in the PNG specification. +If you intend to change the filter type during the course of writing +the image, you should start with flags set for all of the filters +you intend to use so that libpng can initialize its internal +structures appropriately for all of the filter types. (Note that this +means the first row must always be adaptively filtered, because libpng +currently does not allocate the filter buffers until png_write_row() +is called for the first time.) + + filters = PNG_FILTER_NONE | PNG_FILTER_SUB + PNG_FILTER_UP | PNG_FILTER_AVE | + PNG_FILTER_PAETH | PNG_ALL_FILTERS; + + png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, + filters); + The second parameter can also be + PNG_INTRAPIXEL_DIFFERENCING if you are + writing a PNG to be embedded in a MNG + datastream. This parameter must be the + same as the value of filter_method used + in png_set_IHDR(). + +It is also possible to influence how libpng chooses from among the +available filters. This is done in one or both of two ways - by +telling it how important it is to keep the same filter for successive +rows, and by telling it the relative computational costs of the filters. + + double weights[3] = {1.5, 1.3, 1.1}, + costs[PNG_FILTER_VALUE_LAST] = + {1.0, 1.3, 1.3, 1.5, 1.7}; + + png_set_filter_heuristics(png_ptr, + PNG_FILTER_HEURISTIC_WEIGHTED, 3, + weights, costs); + +The weights are multiplying factors that indicate to libpng that the +row filter should be the same for successive rows unless another row filter +is that many times better than the previous filter. In the above example, +if the previous 3 filters were SUB, SUB, NONE, the SUB filter could have a +"sum of absolute differences" 1.5 x 1.3 times higher than other filters +and still be chosen, while the NONE filter could have a sum 1.1 times +higher than other filters and still be chosen. Unspecified weights are +taken to be 1.0, and the specified weights should probably be declining +like those above in order to emphasize recent filters over older filters. + +The filter costs specify for each filter type a relative decoding cost +to be considered when selecting row filters. This means that filters +with higher costs are less likely to be chosen over filters with lower +costs, unless their "sum of absolute differences" is that much smaller. +The costs do not necessarily reflect the exact computational speeds of +the various filters, since this would unduly influence the final image +size. + +Note that the numbers above were invented purely for this example and +are given only to help explain the function usage. Little testing has +been done to find optimum values for either the costs or the weights. + +Removing unwanted object code + +There are a bunch of #define's in pngconf.h that control what parts of +libpng are compiled. All the defines end in _SUPPORTED. If you are +never going to use a capability, you can change the #define to #undef +before recompiling libpng and save yourself code and data space, or +you can turn off individual capabilities with defines that begin with +PNG_NO_. + +You can also turn all of the transforms and ancillary chunk capabilities +off en masse with compiler directives that define +PNG_NO_READ[or WRITE]_TRANSFORMS, or PNG_NO_READ[or WRITE]_ANCILLARY_CHUNKS, +or all four, +along with directives to turn on any of the capabilities that you do +want. The PNG_NO_READ[or WRITE]_TRANSFORMS directives disable +the extra transformations but still leave the library fully capable of reading +and writing PNG files with all known public chunks +Use of the PNG_NO_READ[or WRITE]_ANCILLARY_CHUNKS directive +produces a library that is incapable of reading or writing ancillary chunks. +If you are not using the progressive reading capability, you can +turn that off with PNG_NO_PROGRESSIVE_READ (don't confuse +this with the INTERLACING capability, which you'll still have). + +All the reading and writing specific code are in separate files, so the +linker should only grab the files it needs. However, if you want to +make sure, or if you are building a stand alone library, all the +reading files start with pngr and all the writing files start with +pngw. The files that don't match either (like png.c, pngtrans.c, etc.) +are used for both reading and writing, and always need to be included. +The progressive reader is in pngpread.c + +If you are creating or distributing a dynamically linked library (a .so +or DLL file), you should not remove or disable any parts of the library, +as this will cause applications linked with different versions of the +library to fail if they call functions not available in your library. +The size of the library itself should not be an issue, because only +those sections that are actually used will be loaded into memory. + +Requesting debug printout + +The macro definition PNG_DEBUG can be used to request debugging +printout. Set it to an integer value in the range 0 to 3. Higher +numbers result in increasing amounts of debugging information. The +information is printed to the "stderr" file, unless another file +name is specified in the PNG_DEBUG_FILE macro definition. + +When PNG_DEBUG > 0, the following functions (macros) become available: + + png_debug(level, message) + png_debug1(level, message, p1) + png_debug2(level, message, p1, p2) + +in which "level" is compared to PNG_DEBUG to decide whether to print +the message, "message" is the formatted string to be printed, +and p1 and p2 are parameters that are to be embedded in the string +according to printf-style formatting directives. For example, + + png_debug1(2, "foo=%d\n", foo); + +is expanded to + + if(PNG_DEBUG > 2) + fprintf(PNG_DEBUG_FILE, "foo=%d\n", foo); + +When PNG_DEBUG is defined but is zero, the macros aren't defined, but you +can still use PNG_DEBUG to control your own debugging: + + #ifdef PNG_DEBUG + fprintf(stderr, ... + #endif + +When PNG_DEBUG = 1, the macros are defined, but only png_debug statements +having level = 0 will be printed. There aren't any such statements in +this version of libpng, but if you insert some they will be printed. + +VI. Runtime optimization + +A new feature in libpng 1.2.0 is the ability to dynamically switch between +standard and optimized versions of some routines. Currently these are +limited to three computationally intensive tasks when reading PNG files: +decoding row filters, expanding interlacing, and combining interlaced or +transparent row data with previous row data. Currently the optimized +versions are available only for x86 (Intel, AMD, etc.) platforms with +MMX support, though this may change in future versions. (For example, +the non-MMX assembler optimizations for zlib might become similarly +runtime-selectable in future releases, in which case libpng could be +extended to support them. Alternatively, the compile-time choice of +floating-point versus integer routines for gamma correction might become +runtime-selectable.) + +Because such optimizations tend to be very platform- and compiler-dependent, +both in how they are written and in how they perform, the new runtime code +in libpng has been written to allow programs to query, enable, and disable +either specific optimizations or all such optimizations. For example, to +enable all possible optimizations (bearing in mind that some "optimizations" +may actually run more slowly in rare cases): + + #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200) + png_uint_32 mask, flags; + + flags = png_get_asm_flags(png_ptr); + mask = png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE); + png_set_asm_flags(png_ptr, flags | mask); + #endif + +To enable only optimizations relevant to reading PNGs, use PNG_SELECT_READ +by itself when calling png_get_asm_flagmask(); similarly for optimizing +only writing. To disable all optimizations: + + #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200) + flags = png_get_asm_flags(png_ptr); + mask = png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE); + png_set_asm_flags(png_ptr, flags & ~mask); + #endif + +To enable or disable only MMX-related features, use png_get_mmx_flagmask() +in place of png_get_asm_flagmask(). The mmx version takes one additional +parameter: + + #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200) + int selection = PNG_SELECT_READ | PNG_SELECT_WRITE; + int compilerID; + + mask = png_get_mmx_flagmask(selection, &compilerID); + #endif + +On return, compilerID will indicate which version of the MMX assembler +optimizations was compiled. Currently two flavors exist: Microsoft +Visual C++ (compilerID == 1) and GNU C (a.k.a. gcc/gas, compilerID == 2). +On non-x86 platforms or on systems compiled without MMX optimizations, a +value of -1 is used. + +Note that both png_get_asm_flagmask() and png_get_mmx_flagmask() return +all valid, settable optimization bits for the version of the library that's +currently in use. In the case of shared (dynamically linked) libraries, +this may include optimizations that did not exist at the time the code was +written and compiled. It is also possible, of course, to enable only known, +specific optimizations; for example: + + #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200) + flags = PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \ + | PNG_ASM_FLAG_MMX_READ_INTERLACE \ + | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \ + | PNG_ASM_FLAG_MMX_READ_FILTER_UP \ + | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \ + | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ; + png_set_asm_flags(png_ptr, flags); + #endif + +This method would enable only the MMX read-optimizations available at the +time of libpng 1.2.0's release, regardless of whether a later version of +the DLL were actually being used. (Also note that these functions did not +exist in versions older than 1.2.0, so any attempt to run a dynamically +linked app on such an older version would fail.) + +To determine whether the processor supports MMX instructions at all, use +the png_mmx_support() function: + + #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200) + mmxsupport = png_mmx_support(); + #endif + +It returns -1 if MMX support is not compiled into libpng, 0 if MMX code +is compiled but MMX is not supported by the processor, or 1 if MMX support +is fully available. Note that png_mmx_support(), png_get_mmx_flagmask(), +and png_get_asm_flagmask() all may be called without allocating and ini- +tializing any PNG structures (for example, as part of a usage screen or +"about" box). + +The following code can be used to prevent an application from using the +thread_unsafe features, even if libpng was built with PNG_THREAD_UNSAFE_OK +defined: + +#if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) \ + && defined(PNG_THREAD_UNSAFE_OK) + /* Disable thread-unsafe features of pnggccrd */ + if (png_access_version() >= 10200) + { + png_uint_32 mmx_disable_mask = 0; + png_uint_32 asm_flags; + + mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \ + | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \ + | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \ + | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ); + asm_flags = png_get_asm_flags(png_ptr); + png_set_asm_flags(png_ptr, asm_flags & ~mmx_disable_mask); + } +#endif + +For more extensive examples of runtime querying, enabling and disabling +of optimized features, see contrib/gregbook/readpng2.c in the libpng +source-code distribution. + +VII. MNG support + +The MNG specification (available at http://www.libpng.org/pub/mng) allows +certain extensions to PNG for PNG images that are embedded in MNG datastreams. +Libpng can support some of these extensions. To enable them, use the +png_permit_mng_features() function: + + feature_set = png_permit_mng_features(png_ptr, mask) + mask is a png_uint_32 containing the logical OR of the + features you want to enable. These include + PNG_FLAG_MNG_EMPTY_PLTE + PNG_FLAG_MNG_FILTER_64 + PNG_ALL_MNG_FEATURES + feature_set is a png_uint_32 that is the logical AND of + your mask with the set of MNG features that is + supported by the version of libpng that you are using. + +It is an error to use this function when reading or writing a standalone +PNG file with the PNG 8-byte signature. The PNG datastream must be wrapped +in a MNG datastream. As a minimum, it must have the MNG 8-byte signature +and the MHDR and MEND chunks. Libpng does not provide support for these +or any other MNG chunks; your application must provide its own support for +them. You may wish to consider using libmng (available at +http://www.libmng.com) instead. + +VIII. Changes to Libpng from version 0.88 + +It should be noted that versions of libpng later than 0.96 are not +distributed by the original libpng author, Guy Schalnat, nor by +Andreas Dilger, who had taken over from Guy during 1996 and 1997, and +distributed versions 0.89 through 0.96, but rather by another member +of the original PNG Group, Glenn Randers-Pehrson. Guy and Andreas are +still alive and well, but they have moved on to other things. + +The old libpng functions png_read_init(), png_write_init(), +png_info_init(), png_read_destroy(), and png_write_destroy() have been +moved to PNG_INTERNAL in version 0.95 to discourage their use. These +functions will be removed from libpng version 2.0.0. + +The preferred method of creating and initializing the libpng structures is +via the png_create_read_struct(), png_create_write_struct(), and +png_create_info_struct() because they isolate the size of the structures +from the application, allow version error checking, and also allow the +use of custom error handling routines during the initialization, which +the old functions do not. The functions png_read_destroy() and +png_write_destroy() do not actually free the memory that libpng +allocated for these structs, but just reset the data structures, so they +can be used instead of png_destroy_read_struct() and +png_destroy_write_struct() if you feel there is too much system overhead +allocating and freeing the png_struct for each image read. + +Setting the error callbacks via png_set_message_fn() before +png_read_init() as was suggested in libpng-0.88 is no longer supported +because this caused applications that do not use custom error functions +to fail if the png_ptr was not initialized to zero. It is still possible +to set the error callbacks AFTER png_read_init(), or to change them with +png_set_error_fn(), which is essentially the same function, but with a new +name to force compilation errors with applications that try to use the old +method. + +Starting with version 1.0.7, you can find out which version of the library +you are using at run-time: + + png_uint_32 libpng_vn = png_access_version_number(); + +The number libpng_vn is constructed from the major version, minor +version with leading zero, and release number with leading zero, +(e.g., libpng_vn for version 1.0.7 is 10007). + +You can also check which version of png.h you used when compiling your +application: + + png_uint_32 application_vn = PNG_LIBPNG_VER; + +IX. Y2K Compliance in libpng + +December 3, 2004 + +Since the PNG Development group is an ad-hoc body, we can't make +an official declaration. + +This is your unofficial assurance that libpng from version 0.71 and +upward through 1.2.8 are Y2K compliant. It is my belief that earlier +versions were also Y2K compliant. + +Libpng only has three year fields. One is a 2-byte unsigned integer that +will hold years up to 65535. The other two hold the date in text +format, and will hold years up to 9999. + +The integer is + "png_uint_16 year" in png_time_struct. + +The strings are + "png_charp time_buffer" in png_struct and + "near_time_buffer", which is a local character string in png.c. + +There are seven time-related functions: + + png_convert_to_rfc_1123() in png.c + (formerly png_convert_to_rfc_1152() in error) + png_convert_from_struct_tm() in pngwrite.c, called + in pngwrite.c + png_convert_from_time_t() in pngwrite.c + png_get_tIME() in pngget.c + png_handle_tIME() in pngrutil.c, called in pngread.c + png_set_tIME() in pngset.c + png_write_tIME() in pngwutil.c, called in pngwrite.c + +All appear to handle dates properly in a Y2K environment. The +png_convert_from_time_t() function calls gmtime() to convert from system +clock time, which returns (year - 1900), which we properly convert to +the full 4-digit year. There is a possibility that applications using +libpng are not passing 4-digit years into the png_convert_to_rfc_1123() +function, or that they are incorrectly passing only a 2-digit year +instead of "year - 1900" into the png_convert_from_struct_tm() function, +but this is not under our control. The libpng documentation has always +stated that it works with 4-digit years, and the APIs have been +documented as such. + +The tIME chunk itself is also Y2K compliant. It uses a 2-byte unsigned +integer to hold the year, and can hold years as large as 65535. + +zlib, upon which libpng depends, is also Y2K compliant. It contains +no date-related code. + + + Glenn Randers-Pehrson + libpng maintainer + PNG Development Group diff --git a/com32/lib/libpng/libpngpf.3 b/com32/lib/libpng/libpngpf.3 new file mode 100644 index 0000000..d196d70 --- /dev/null +++ b/com32/lib/libpng/libpngpf.3 @@ -0,0 +1,1096 @@ +.TH LIBPNGPF 3 "December 3, 2004" +.SH NAME +libpng \- Portable Network Graphics (PNG) Reference Library 1.2.8 +(private functions) +.SH SYNOPSIS +\fB\fB#include \fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_build_gamma_table (png_structp \fI\fIpng_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_build_grayscale_palette (int \fP\fI\fP\fIbit_depth\fP\fB\fP\fB, png_colorp \fI\fIpalette\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_calculate_crc (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIptr\fP\fB\fP\fB, png_size_t \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_check_chunk_name (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_bytep \fI\fIchunk_name\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBpng_size_t png_check_keyword (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_charp \fP\fI\fP\fIkey\fP\fB\fP\fB, png_charpp \fI\fInew_key\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_combine_row (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIrow\fP\fB\fP\fB, int \fI\fImask\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_correct_palette (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_colorp \fP\fI\fP\fIpalette\fP\fB\fP\fB, int \fI\fInum_palette\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBint png_crc_error (png_structp \fI\fIpng_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBint png_crc_finish (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIskip\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_crc_read (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIbuf\fP\fB\fP\fB, png_size_t \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBpng_voidp png_create_struct (int \fI\fItype\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBpng_voidp png_create_struct_2 (int \fP\fI\fP\fItype\fP\fB\fP\fB, png_malloc_ptr \fP\fI\fP\fImalloc_fn\fP\fB\fP\fB, png_voidp \fI\fImem_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBpng_charp png_decompress_chunk (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, int \fP\fI\fP\fIcomp_type\fP\fB\fP\fB, png_charp \fP\fI\fP\fIchunkdata\fP\fB\fP\fB, png_size_t \fP\fI\fP\fIchunklength\fP\fB\fP\fB, png_size_t \fP\fI\fP\fIprefix_length\fP\fB\fP\fB, png_size_t \fI\fI*data_length\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_destroy_struct (png_voidp \fI\fIstruct_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_destroy_struct_2 (png_voidp \fP\fI\fP\fIstruct_ptr\fP\fB\fP\fB, png_free_ptr \fP\fI\fP\fIfree_fn\fP\fB\fP\fB, png_voidp \fI\fImem_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_background (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIrow\fP\fB\fP\fB, png_color_16p \fP\fI\fP\fItrans_values\fP\fB\fP\fB, png_color_16p \fP\fI\fP\fIbackground\fP\fB\fP\fB, png_color_16p \fP\fI\fP\fIbackground_1\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIgamma_table\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIgamma_from_1\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIgamma_to_1\fP\fB\fP\fB, png_uint_16pp \fP\fI\fP\fIgamma_16\fP\fB\fP\fB, png_uint_16pp \fP\fI\fP\fIgamma_16_from_1\fP\fB\fP\fB, png_uint_16pp \fP\fI\fP\fIgamma_16_to_1\fP\fB\fP\fB, int \fI\fIgamma_shift\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_bgr (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fI\fIrow\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_chop (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fI\fIrow\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_dither (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIrow\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIpalette_lookup\fP\fB\fP\fB, png_bytep \fI\fIdither_lookup\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_expand (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIrow\fP\fB\fP\fB, png_color_16p \fI\fItrans_value\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_expand_palette (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIrow\fP\fB\fP\fB, png_colorp \fP\fI\fP\fIpalette\fP\fB\fP\fB, png_bytep \fP\fI\fP\fItrans\fP\fB\fP\fB, int \fI\fInum_trans\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_gamma (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIrow\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIgamma_table\fP\fB\fP\fB, png_uint_16pp \fP\fI\fP\fIgamma_16_table\fP\fB\fP\fB, int \fI\fIgamma_shift\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_gray_to_rgb (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fI\fIrow\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_invert (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fI\fIrow\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_pack (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIrow\fP\fB\fP\fB, png_uint_32 \fI\fIbit_depth\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_packswap (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fI\fIrow\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_read_filler (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIrow\fP\fB\fP\fB, png_uint_32 \fP\fI\fP\fIfiller\fP\fB\fP\fB, png_uint_32 \fI\fIflags\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_read_interlace (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIrow\fP\fB\fP\fB, int \fP\fI\fP\fIpass\fP\fB\fP\fB, png_uint_32 \fI\fItransformations\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_read_invert_alpha (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fI\fIrow\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_read_swap_alpha (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fI\fIrow\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_read_transformations (png_structp \fI\fIpng_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBint png_do_rgb_to_gray (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fI\fIrow\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_shift (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIrow\fP\fB\fP\fB, png_color_8p \fI\fIbit_depth\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_strip_filler (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIrow\fP\fB\fP\fB, png_uint_32 \fI\fIflags\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_swap (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fI\fIrow\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_unpack (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fI\fIrow\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_unshift (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIrow\fP\fB\fP\fB, png_color_8p \fI\fIsig_bits\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_write_interlace (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIrow\fP\fB\fP\fB, int \fI\fIpass\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_write_invert_alpha (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fI\fIrow\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_write_swap_alpha (png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fI\fIrow\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_do_write_transformations (png_structp \fI\fIpng_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid *png_far_to_near (png_structp png_ptr,png_voidp \fP\fI\fP\fIptr\fP\fB\fP\fB, int \fI\fIcheck\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_flush (png_structp \fI\fIpng_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBpng_int_32 png_get_int_32 (png_bytep \fI\fIbuf\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBpng_uint_16 png_get_uint_16 (png_bytep \fI\fIbuf\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBpng_uint_32 png_get_uint_31 (png_bytep \fI\fIbuf\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBpng_uint_32 png_get_uint_32 (png_bytep \fI\fIbuf\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_handle_bKGD (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_handle_cHRM (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_handle_gAMA (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_handle_hIST (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_handle_IEND (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_handle_IHDR (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_handle_iCCP (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_handle_iTXt (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_handle_oFFs (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_handle_pCAL (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_handle_pHYs (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_handle_PLTE (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_handle_sBIT (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_handle_sCAL (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_handle_sPLT (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_handle_sRGB (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_handle_tEXt (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_handle_tIME (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_handle_tRNS (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_handle_unknown (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_handle_zTXt (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_info_destroy (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fI\fIinfo_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_init_mmx_flags (png_structp \fI\fIpng_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_init_read_transformations (png_structp \fI\fIpng_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_process_IDAT_data (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIbuffer\fP\fB\fP\fB, png_size_t \fI\fIbuffer_length\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_process_some_data (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fI\fIinfo_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_push_check_crc (png_structp \fI\fIpng_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_push_crc_finish (png_structp \fI\fIpng_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_push_crc_skip (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_push_fill_buffer (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIbuffer\fP\fB\fP\fB, png_size_t \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_push_handle_tEXt (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_push_handle_unknown (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_push_handle_zTXt (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_push_have_end (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fI\fIinfo_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_push_have_info (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fI\fIinfo_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_push_have_row (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_bytep \fI\fIrow\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_push_process_row (png_structp \fI\fIpng_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_push_read_chunk (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fI\fIinfo_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_push_read_end (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fI\fIinfo_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_push_read_IDAT (png_structp \fI\fIpng_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_push_read_sig (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fI\fIinfo_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_push_read_tEXt (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fI\fIinfo_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_push_read_zTXt (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fI\fIinfo_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_push_restore_buffer (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIbuffer\fP\fB\fP\fB, png_size_t \fI\fIbuffer_length\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_push_save_buffer (png_structp \fI\fIpng_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_read_data (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIdata\fP\fB\fP\fB, png_size_t \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_read_filter_row (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_row_infop \fP\fI\fP\fIrow_info\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIrow\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIprev_row\fP\fB\fP\fB, int \fI\fIfilter\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_read_finish_row (png_structp \fI\fIpng_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_read_push_finish_row (png_structp \fI\fIpng_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_read_start_row (png_structp \fI\fIpng_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_read_transform_info (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fI\fIinfo_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_reset_crc (png_structp \fI\fIpng_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_save_int_32 (png_bytep \fP\fI\fP\fIbuf\fP\fB\fP\fB, png_int_32 \fI\fIi\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_save_uint_16 (png_bytep \fP\fI\fP\fIbuf\fP\fB\fP\fB, unsigned int \fI\fIi\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_save_uint_32 (png_bytep \fP\fI\fP\fIbuf\fP\fB\fP\fB, png_uint_32 \fI\fIi\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBint png_set_text_2 (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_infop \fP\fI\fP\fIinfo_ptr\fP\fB\fP\fB, png_textp \fP\fI\fP\fItext_ptr\fP\fB\fP\fB, int \fI\fInum_text\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_cHRM (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, double \fP\fI\fP\fIwhite_x\fP\fB\fP\fB, double \fP\fI\fP\fIwhite_y\fP\fB\fP\fB, double \fP\fI\fP\fIred_x\fP\fB\fP\fB, double \fP\fI\fP\fIred_y\fP\fB\fP\fB, double \fP\fI\fP\fIgreen_x\fP\fB\fP\fB, double \fP\fI\fP\fIgreen_y\fP\fB\fP\fB, double \fP\fI\fP\fIblue_x\fP\fB\fP\fB, double \fI\fIblue_y\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_cHRM_fixed (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_uint_32 \fP\fI\fP\fIwhite_x\fP\fB\fP\fB, png_uint_32 \fP\fI\fP\fIwhite_y\fP\fB\fP\fB, png_uint_32 \fP\fI\fP\fIred_x\fP\fB\fP\fB, png_uint_32 \fP\fI\fP\fIred_y\fP\fB\fP\fB, png_uint_32 \fP\fI\fP\fIgreen_x\fP\fB\fP\fB, png_uint_32 \fP\fI\fP\fIgreen_y\fP\fB\fP\fB, png_uint_32 \fP\fI\fP\fIblue_x\fP\fB\fP\fB, png_uint_32 \fI\fIblue_y\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_data (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIdata\fP\fB\fP\fB, png_size_t \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_filtered_row (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_bytep \fI\fIfiltered_row\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_find_filter (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_row_infop \fI\fIrow_info\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_finish_row (png_structp \fI\fIpng_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_gAMA (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, double \fI\fIfile_gamma\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_gAMA_fixed (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_uint_32 \fI\fIint_file_gamma\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_hIST (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_uint_16p \fP\fI\fP\fIhist\fP\fB\fP\fB, int \fI\fInum_hist\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_iCCP (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_charp \fP\fI\fP\fIname\fP\fB\fP\fB, int \fP\fI\fP\fIcompression_type\fP\fB\fP\fB, png_charp \fP\fI\fP\fIprofile\fP\fB\fP\fB, int \fI\fIproflen\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_IDAT (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_bytep \fP\fI\fP\fIdata\fP\fB\fP\fB, png_size_t \fI\fIlength\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_IEND (png_structp \fI\fIpng_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_IHDR (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_uint_32 \fP\fI\fP\fIwidth\fP\fB\fP\fB, png_uint_32 \fP\fI\fP\fIheight\fP\fB\fP\fB, int \fP\fI\fP\fIbit_depth\fP\fB\fP\fB, int \fP\fI\fP\fIcolor_type\fP\fB\fP\fB, int \fP\fI\fP\fIcompression_type\fP\fB\fP\fB, int \fP\fI\fP\fIfilter_type\fP\fB\fP\fB, int \fI\fIinterlace_type\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_iTXt (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, int \fP\fI\fP\fIcompression\fP\fB\fP\fB, png_charp \fP\fI\fP\fIkey\fP\fB\fP\fB, png_charp \fP\fI\fP\fIlang\fP\fB\fP\fB, png_charp \fP\fI\fP\fItranslated_key\fP\fB\fP\fB, png_charp \fI\fItext\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_oFFs (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_uint_32 \fP\fI\fP\fIx_offset\fP\fB\fP\fB, png_uint_32 \fP\fI\fP\fIy_offset\fP\fB\fP\fB, int \fI\fIunit_type\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_pCAL (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_charp \fP\fI\fP\fIpurpose\fP\fB\fP\fB, png_int_32 \fP\fI\fP\fIX0\fP\fB\fP\fB, png_int_32 \fP\fI\fP\fIX1\fP\fB\fP\fB, int \fP\fI\fP\fItype\fP\fB\fP\fB, int \fP\fI\fP\fInparams\fP\fB\fP\fB, png_charp \fP\fI\fP\fIunits\fP\fB\fP\fB, png_charpp \fI\fIparams\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_pHYs (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_uint_32 \fP\fI\fP\fIx_pixels_per_unit\fP\fB\fP\fB, png_uint_32 \fP\fI\fP\fIy_pixels_per_unit\fP\fB\fP\fB, int \fI\fIunit_type\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_PLTE (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_colorp \fP\fI\fP\fIpalette\fP\fB\fP\fB, png_uint_32 \fI\fInum_pal\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_sBIT (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_color_8p \fP\fI\fP\fIsbit\fP\fB\fP\fB, int \fI\fIcolor_type\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_sCAL (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_charp \fP\fI\fP\fIunit\fP\fB\fP\fB, double \fP\fI\fP\fIwidth\fP\fB\fP\fB, double \fI\fIheight\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_sCAL_s (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_charp \fP\fI\fP\fIunit\fP\fB\fP\fB, png_charp \fP\fI\fP\fIwidth\fP\fB\fP\fB, png_charp \fI\fIheight\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_sig (png_structp \fI\fIpng_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_sRGB (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, int \fI\fIintent\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_sPLT (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_spalette_p \fI\fIpalette\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_start_row (png_structp \fI\fIpng_ptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_tEXt (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_charp \fP\fI\fP\fIkey\fP\fB\fP\fB, png_charp \fP\fI\fP\fItext\fP\fB\fP\fB, png_size_t \fI\fItext_len\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_tIME (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_timep \fI\fImod_time\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_tRNS (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_bytep \fP\fI\fP\fItrans\fP\fB\fP\fB, png_color_16p \fP\fI\fP\fIvalues\fP\fB\fP\fB, int \fP\fI\fP\fInumber\fP\fB\fP\fB, int \fI\fIcolor_type\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_write_zTXt (png_structp \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, png_charp \fP\fI\fP\fIkey\fP\fB\fP\fB, png_charp \fP\fI\fP\fItext\fP\fB\fP\fB, png_size_t \fP\fI\fP\fItext_len\fP\fB\fP\fB, int \fI\fIcompression\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoidpf png_zalloc (voidpf \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, uInt \fP\fI\fP\fIitems\fP\fB\fP\fB, uInt \fI\fIsize\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +\fB\fBvoid png_zfree (voidpf \fP\fI\fP\fIpng_ptr\fP\fB\fP\fB, voidpf \fI\fIptr\fP\fB\fP\fB);\fP\fP + +\fI\fB + +\fI\fB\fI\fB + +\fI\fB + +.SH DESCRIPTION +The functions listed above are used privately by libpng +and are not recommended for use by applications. They are +not "exported" to applications using shared libraries. They +are listed alphabetically here as an aid to libpng maintainers. +See png.h for more information on these functions. + +.SH SEE ALSO +libpng(3), png(5) +.SH AUTHOR +Glenn Randers-Pehrson diff --git a/com32/lib/libpng/png.5 b/com32/lib/libpng/png.5 new file mode 100644 index 0000000..990de51 --- /dev/null +++ b/com32/lib/libpng/png.5 @@ -0,0 +1,73 @@ +.TH PNG 5 "December 3, 2004" +.SH NAME +png \- Portable Network Graphics (PNG) format +.SH DESCRIPTION +PNG (Portable Network Graphics) is an extensible file format for the +lossless, portable, well-compressed storage of raster images. PNG provides +a patent-free replacement for GIF and can also replace many +common uses of TIFF. Indexed-color, grayscale, and truecolor images are +supported, plus an optional alpha channel. Sample depths range from +1 to 16 bits. +.br + +PNG is designed to work well in online viewing applications, such as the +World Wide Web, so it is fully streamable with a progressive display +option. PNG is robust, providing both full file integrity checking and +fast, simple detection of common transmission errors. Also, PNG can store +gamma and chromaticity data for improved color matching on heterogeneous +platforms. + +.SH "SEE ALSO" +.IR libpng(3), zlib(3), deflate(5), and zlib(5) +.LP +PNG specification (second edition), November 2003: +.IP +.br + 8) + png_error(png_ptr, "Too many bytes for PNG signature."); + + png_ptr->sig_bytes = (png_byte)(num_bytes < 0 ? 0 : num_bytes); +} + +/* Checks whether the supplied bytes match the PNG signature. We allow + * checking less than the full 8-byte signature so that those apps that + * already read the first few bytes of a file to determine the file type + * can simply check the remaining bytes for extra assurance. Returns + * an integer less than, equal to, or greater than zero if sig is found, + * respectively, to be less than, to match, or be greater than the correct + * PNG signature (this is the same behaviour as strcmp, memcmp, etc). + */ +int PNGAPI +png_sig_cmp(png_bytep sig, png_size_t start, png_size_t num_to_check) +{ + png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + if (num_to_check > 8) + num_to_check = 8; + else if (num_to_check < 1) + return (0); + + if (start > 7) + return (0); + + if (start + num_to_check > 8) + num_to_check = 8 - start; + + return ((int)(png_memcmp(&sig[start], &png_signature[start], num_to_check))); +} + +/* (Obsolete) function to check signature bytes. It does not allow one + * to check a partial signature. This function might be removed in the + * future - use png_sig_cmp(). Returns true (nonzero) if the file is a PNG. + */ +int PNGAPI +png_check_sig(png_bytep sig, int num) +{ + return ((int)!png_sig_cmp(sig, (png_size_t)0, (png_size_t)num)); +} + +/* Function to allocate memory for zlib and clear it to 0. */ +#ifdef PNG_1_0_X +voidpf PNGAPI +#else +voidpf /* private */ +#endif +png_zalloc(voidpf png_ptr, uInt items, uInt size) +{ + png_voidp ptr; + png_structp p=png_ptr; + png_uint_32 save_flags=p->flags; + png_uint_32 num_bytes; + + if (items > PNG_UINT_32_MAX/size) + { + png_warning (png_ptr, "Potential overflow in png_zalloc()"); + return (NULL); + } + num_bytes = (png_uint_32)items * size; + + p->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK; + ptr = (png_voidp)png_malloc((png_structp)png_ptr, num_bytes); + p->flags=save_flags; + +#if defined(PNG_1_0_X) && !defined(PNG_NO_ZALLOC_ZERO) + if (ptr == NULL) + return ((voidpf)ptr); + + if (num_bytes > (png_uint_32)0x8000L) + { + png_memset(ptr, 0, (png_size_t)0x8000L); + png_memset((png_bytep)ptr + (png_size_t)0x8000L, 0, + (png_size_t)(num_bytes - (png_uint_32)0x8000L)); + } + else + { + png_memset(ptr, 0, (png_size_t)num_bytes); + } +#endif + return ((voidpf)ptr); +} + +/* function to free memory for zlib */ +#ifdef PNG_1_0_X +void PNGAPI +#else +void /* private */ +#endif +png_zfree(voidpf png_ptr, voidpf ptr) +{ + png_free((png_structp)png_ptr, (png_voidp)ptr); +} + +/* Reset the CRC variable to 32 bits of 1's. Care must be taken + * in case CRC is > 32 bits to leave the top bits 0. + */ +void /* PRIVATE */ +png_reset_crc(png_structp png_ptr) +{ + png_ptr->crc = crc32(0, Z_NULL, 0); +} + +/* Calculate the CRC over a section of data. We can only pass as + * much data to this routine as the largest single buffer size. We + * also check that this data will actually be used before going to the + * trouble of calculating it. + */ +void /* PRIVATE */ +png_calculate_crc(png_structp png_ptr, png_bytep ptr, png_size_t length) +{ + int need_crc = 1; + + if (png_ptr->chunk_name[0] & 0x20) /* ancillary */ + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == + (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) + need_crc = 0; + } + else /* critical */ + { + if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) + need_crc = 0; + } + + if (need_crc) + png_ptr->crc = crc32(png_ptr->crc, ptr, (uInt)length); +} + +/* Allocate the memory for an info_struct for the application. We don't + * really need the png_ptr, but it could potentially be useful in the + * future. This should be used in favour of malloc(png_sizeof(png_info)) + * and png_info_init() so that applications that want to use a shared + * libpng don't have to be recompiled if png_info changes size. + */ +png_infop PNGAPI +png_create_info_struct(png_structp png_ptr) +{ + png_infop info_ptr; + + png_debug(1, "in png_create_info_struct\n"); + if(png_ptr == NULL) return (NULL); +#ifdef PNG_USER_MEM_SUPPORTED + info_ptr = (png_infop)png_create_struct_2(PNG_STRUCT_INFO, + png_ptr->malloc_fn, png_ptr->mem_ptr); +#else + info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); +#endif + if (info_ptr != NULL) + png_info_init_3(&info_ptr, png_sizeof(png_info)); + + return (info_ptr); +} + +/* This function frees the memory associated with a single info struct. + * Normally, one would use either png_destroy_read_struct() or + * png_destroy_write_struct() to free an info struct, but this may be + * useful for some applications. + */ +void PNGAPI +png_destroy_info_struct(png_structp png_ptr, png_infopp info_ptr_ptr) +{ + png_infop info_ptr = NULL; + + png_debug(1, "in png_destroy_info_struct\n"); + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (info_ptr != NULL) + { + png_info_destroy(png_ptr, info_ptr); + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, png_ptr->free_fn, + png_ptr->mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } +} + +/* Initialize the info structure. This is now an internal function (0.89) + * and applications using it are urged to use png_create_info_struct() + * instead. + */ +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +#undef png_info_init +void PNGAPI +png_info_init(png_infop info_ptr) +{ + /* We only come here via pre-1.0.12-compiled applications */ + png_info_init_3(&info_ptr, 0); +} +#endif + +void PNGAPI +png_info_init_3(png_infopp ptr_ptr, png_size_t png_info_struct_size) +{ + png_infop info_ptr = *ptr_ptr; + + png_debug(1, "in png_info_init_3\n"); + + if(png_sizeof(png_info) > png_info_struct_size) + { + png_destroy_struct(info_ptr); + info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); + *ptr_ptr = info_ptr; + } + + /* set everything to 0 */ + png_memset(info_ptr, 0, png_sizeof (png_info)); +} + +#ifdef PNG_FREE_ME_SUPPORTED +void PNGAPI +png_data_freer(png_structp png_ptr, png_infop info_ptr, + int freer, png_uint_32 mask) +{ + png_debug(1, "in png_data_freer\n"); + if (png_ptr == NULL || info_ptr == NULL) + return; + if(freer == PNG_DESTROY_WILL_FREE_DATA) + info_ptr->free_me |= mask; + else if(freer == PNG_USER_WILL_FREE_DATA) + info_ptr->free_me &= ~mask; + else + png_warning(png_ptr, + "Unknown freer parameter in png_data_freer."); +} +#endif + +void PNGAPI +png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask, + int num) +{ + png_debug(1, "in png_free_data\n"); + if (png_ptr == NULL || info_ptr == NULL) + return; + +#if defined(PNG_TEXT_SUPPORTED) +/* free text item num or (if num == -1) all text items */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_TEXT) & info_ptr->free_me) +#else +if (mask & PNG_FREE_TEXT) +#endif +{ + if (num != -1) + { + if (info_ptr->text && info_ptr->text[num].key) + { + png_free(png_ptr, info_ptr->text[num].key); + info_ptr->text[num].key = NULL; + } + } + else + { + int i; + for (i = 0; i < info_ptr->num_text; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, i); + png_free(png_ptr, info_ptr->text); + info_ptr->text = NULL; + info_ptr->num_text=0; + } +} +#endif + +#if defined(PNG_tRNS_SUPPORTED) +/* free any tRNS entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_TRNS) & info_ptr->free_me) +#else +if ((mask & PNG_FREE_TRNS) && (png_ptr->flags & PNG_FLAG_FREE_TRNS)) +#endif +{ + png_free(png_ptr, info_ptr->trans); + info_ptr->valid &= ~PNG_INFO_tRNS; +#ifndef PNG_FREE_ME_SUPPORTED + png_ptr->flags &= ~PNG_FLAG_FREE_TRNS; +#endif + info_ptr->trans = NULL; +} +#endif + +#if defined(PNG_sCAL_SUPPORTED) +/* free any sCAL entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_SCAL) & info_ptr->free_me) +#else +if (mask & PNG_FREE_SCAL) +#endif +{ +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, info_ptr->scal_s_width); + png_free(png_ptr, info_ptr->scal_s_height); + info_ptr->scal_s_width = NULL; + info_ptr->scal_s_height = NULL; +#endif + info_ptr->valid &= ~PNG_INFO_sCAL; +} +#endif + +#if defined(PNG_pCAL_SUPPORTED) +/* free any pCAL entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_PCAL) & info_ptr->free_me) +#else +if (mask & PNG_FREE_PCAL) +#endif +{ + png_free(png_ptr, info_ptr->pcal_purpose); + png_free(png_ptr, info_ptr->pcal_units); + info_ptr->pcal_purpose = NULL; + info_ptr->pcal_units = NULL; + if (info_ptr->pcal_params != NULL) + { + int i; + for (i = 0; i < (int)info_ptr->pcal_nparams; i++) + { + png_free(png_ptr, info_ptr->pcal_params[i]); + info_ptr->pcal_params[i]=NULL; + } + png_free(png_ptr, info_ptr->pcal_params); + info_ptr->pcal_params = NULL; + } + info_ptr->valid &= ~PNG_INFO_pCAL; +} +#endif + +#if defined(PNG_iCCP_SUPPORTED) +/* free any iCCP entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_ICCP) & info_ptr->free_me) +#else +if (mask & PNG_FREE_ICCP) +#endif +{ + png_free(png_ptr, info_ptr->iccp_name); + png_free(png_ptr, info_ptr->iccp_profile); + info_ptr->iccp_name = NULL; + info_ptr->iccp_profile = NULL; + info_ptr->valid &= ~PNG_INFO_iCCP; +} +#endif + +#if defined(PNG_sPLT_SUPPORTED) +/* free a given sPLT entry, or (if num == -1) all sPLT entries */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_SPLT) & info_ptr->free_me) +#else +if (mask & PNG_FREE_SPLT) +#endif +{ + if (num != -1) + { + if(info_ptr->splt_palettes) + { + png_free(png_ptr, info_ptr->splt_palettes[num].name); + png_free(png_ptr, info_ptr->splt_palettes[num].entries); + info_ptr->splt_palettes[num].name = NULL; + info_ptr->splt_palettes[num].entries = NULL; + } + } + else + { + if(info_ptr->splt_palettes_num) + { + int i; + for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_SPLT, i); + + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes = NULL; + info_ptr->splt_palettes_num = 0; + } + info_ptr->valid &= ~PNG_INFO_sPLT; + } +} +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_UNKN) & info_ptr->free_me) +#else +if (mask & PNG_FREE_UNKN) +#endif +{ + if (num != -1) + { + if(info_ptr->unknown_chunks) + { + png_free(png_ptr, info_ptr->unknown_chunks[num].data); + info_ptr->unknown_chunks[num].data = NULL; + } + } + else + { + int i; + + if(info_ptr->unknown_chunks_num) + { + for (i = 0; i < (int)info_ptr->unknown_chunks_num; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_UNKN, i); + + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks = NULL; + info_ptr->unknown_chunks_num = 0; + } + } +} +#endif + +#if defined(PNG_hIST_SUPPORTED) +/* free any hIST entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_HIST) & info_ptr->free_me) +#else +if ((mask & PNG_FREE_HIST) && (png_ptr->flags & PNG_FLAG_FREE_HIST)) +#endif +{ + png_free(png_ptr, info_ptr->hist); + info_ptr->hist = NULL; + info_ptr->valid &= ~PNG_INFO_hIST; +#ifndef PNG_FREE_ME_SUPPORTED + png_ptr->flags &= ~PNG_FLAG_FREE_HIST; +#endif +} +#endif + +/* free any PLTE entry that was internally allocated */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_PLTE) & info_ptr->free_me) +#else +if ((mask & PNG_FREE_PLTE) && (png_ptr->flags & PNG_FLAG_FREE_PLTE)) +#endif +{ + png_zfree(png_ptr, info_ptr->palette); + info_ptr->palette = NULL; + info_ptr->valid &= ~PNG_INFO_PLTE; +#ifndef PNG_FREE_ME_SUPPORTED + png_ptr->flags &= ~PNG_FLAG_FREE_PLTE; +#endif + info_ptr->num_palette = 0; +} + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +/* free any image bits attached to the info structure */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_ROWS) & info_ptr->free_me) +#else +if (mask & PNG_FREE_ROWS) +#endif +{ + if(info_ptr->row_pointers) + { + int row; + for (row = 0; row < (int)info_ptr->height; row++) + { + png_free(png_ptr, info_ptr->row_pointers[row]); + info_ptr->row_pointers[row]=NULL; + } + png_free(png_ptr, info_ptr->row_pointers); + info_ptr->row_pointers=NULL; + } + info_ptr->valid &= ~PNG_INFO_IDAT; +} +#endif + +#ifdef PNG_FREE_ME_SUPPORTED + if(num == -1) + info_ptr->free_me &= ~mask; + else + info_ptr->free_me &= ~(mask & ~PNG_FREE_MUL); +#endif +} + +/* This is an internal routine to free any memory that the info struct is + * pointing to before re-using it or freeing the struct itself. Recall + * that png_free() checks for NULL pointers for us. + */ +void /* PRIVATE */ +png_info_destroy(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_info_destroy\n"); + + png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + if (png_ptr->num_chunk_list) + { + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list=NULL; + png_ptr->num_chunk_list=0; + } +#endif + + png_info_init_3(&info_ptr, png_sizeof(png_info)); +} + +/* This function returns a pointer to the io_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy() or png_read_destroy() are called. + */ +png_voidp PNGAPI +png_get_io_ptr(png_structp png_ptr) +{ + return (png_ptr->io_ptr); +} + +#if !defined(PNG_NO_STDIO) +/* Initialize the default input/output functions for the PNG file. If you + * use your own read or write routines, you can call either png_set_read_fn() + * or png_set_write_fn() instead of png_init_io(). If you have defined + * PNG_NO_STDIO, you must use a function of your own because "FILE *" isn't + * necessarily available. + */ +void PNGAPI +png_init_io(png_structp png_ptr, png_FILE_p fp) +{ + png_debug(1, "in png_init_io\n"); + png_ptr->io_ptr = (png_voidp)fp; +} +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) +/* Convert the supplied time into an RFC 1123 string suitable for use in + * a "Creation Time" or other text-based time string. + */ +png_charp PNGAPI +png_convert_to_rfc1123(png_structp png_ptr, png_timep ptime) +{ + static PNG_CONST char short_months[12][4] = + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + if (png_ptr->time_buffer == NULL) + { + png_ptr->time_buffer = (png_charp)png_malloc(png_ptr, (png_uint_32)(29* + png_sizeof(char))); + } + +#if defined(_WIN32_WCE) + { + wchar_t time_buf[29]; + wsprintf(time_buf, TEXT("%d %S %d %02d:%02d:%02d +0000"), + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); + WideCharToMultiByte(CP_ACP, 0, time_buf, -1, png_ptr->time_buffer, 29, + NULL, NULL); + } +#else +#ifdef USE_FAR_KEYWORD + { + char near_time_buf[29]; + sprintf(near_time_buf, "%d %s %d %02d:%02d:%02d +0000", + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); + png_memcpy(png_ptr->time_buffer, near_time_buf, + 29*png_sizeof(char)); + } +#else + sprintf(png_ptr->time_buffer, "%d %s %d %02d:%02d:%02d +0000", + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); +#endif +#endif /* _WIN32_WCE */ + return ((png_charp)png_ptr->time_buffer); +} +#endif /* PNG_TIME_RFC1123_SUPPORTED */ + +#if 0 +/* Signature string for a PNG file. */ +png_bytep PNGAPI +png_sig_bytes(void) +{ + return ((png_bytep)"\211\120\116\107\015\012\032\012"); +} +#endif + +png_charp PNGAPI +png_get_copyright(png_structp png_ptr) +{ + if (&png_ptr != NULL) /* silence compiler warning about unused png_ptr */ + return ((png_charp) "\n libpng version 1.2.8 - December 3, 2004\n\ + Copyright (c) 1998-2004 Glenn Randers-Pehrson\n\ + Copyright (c) 1996-1997 Andreas Dilger\n\ + Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.\n"); + return ((png_charp) ""); +} + +/* The following return the library version as a short string in the + * format 1.0.0 through 99.99.99zz. To get the version of *.h files + * used with your application, print out PNG_LIBPNG_VER_STRING, which + * is defined in png.h. + * Note: now there is no difference between png_get_libpng_ver() and + * png_get_header_ver(). Due to the version_nn_nn_nn typedef guard, + * it is guaranteed that png.c uses the correct version of png.h. + */ +png_charp PNGAPI +png_get_libpng_ver(png_structp png_ptr) +{ + /* Version of *.c files used when building libpng */ + if (&png_ptr != NULL) /* silence compiler warning about unused png_ptr */ + return ((png_charp) PNG_LIBPNG_VER_STRING); + return ((png_charp) ""); +} + +png_charp PNGAPI +png_get_header_ver(png_structp png_ptr) +{ + /* Version of *.h files used when building libpng */ + if (&png_ptr != NULL) /* silence compiler warning about unused png_ptr */ + return ((png_charp) PNG_LIBPNG_VER_STRING); + return ((png_charp) ""); +} + +png_charp PNGAPI +png_get_header_version(png_structp png_ptr) +{ + /* Returns longer string containing both version and date */ + if (&png_ptr != NULL) /* silence compiler warning about unused png_ptr */ + return ((png_charp) PNG_HEADER_VERSION_STRING); + return ((png_charp) ""); +} + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +int PNGAPI +png_handle_as_unknown(png_structp png_ptr, png_bytep chunk_name) +{ + /* check chunk_name and return "keep" value if it's on the list, else 0 */ + int i; + png_bytep p; + if((png_ptr == NULL && chunk_name == NULL) || png_ptr->num_chunk_list<=0) + return 0; + p=png_ptr->chunk_list+png_ptr->num_chunk_list*5-5; + for (i = png_ptr->num_chunk_list; i; i--, p-=5) + if (!png_memcmp(chunk_name, p, 4)) + return ((int)*(p+4)); + return 0; +} +#endif + +/* This function, added to libpng-1.0.6g, is untested. */ +int PNGAPI +png_reset_zstream(png_structp png_ptr) +{ + return (inflateReset(&png_ptr->zstream)); +} + +/* This function was added to libpng-1.0.7 */ +png_uint_32 PNGAPI +png_access_version_number(void) +{ + /* Version of *.c files used when building libpng */ + return((png_uint_32) PNG_LIBPNG_VER); +} + + +#if !defined(PNG_1_0_X) +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) + /* GRR: could add this: && defined(PNG_MMX_CODE_SUPPORTED) */ +/* this INTERNAL function was added to libpng 1.2.0 */ +void /* PRIVATE */ +png_init_mmx_flags (png_structp png_ptr) +{ + png_ptr->mmx_rowbytes_threshold = 0; + png_ptr->mmx_bitdepth_threshold = 0; + +# if (defined(PNG_USE_PNGVCRD) || defined(PNG_USE_PNGGCCRD)) + + png_ptr->asm_flags |= PNG_ASM_FLAG_MMX_SUPPORT_COMPILED; + + if (png_mmx_support() > 0) { + png_ptr->asm_flags |= PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU +# ifdef PNG_HAVE_ASSEMBLER_COMBINE_ROW + | PNG_ASM_FLAG_MMX_READ_COMBINE_ROW +# endif +# ifdef PNG_HAVE_ASSEMBLER_READ_INTERLACE + | PNG_ASM_FLAG_MMX_READ_INTERLACE +# endif +# ifndef PNG_HAVE_ASSEMBLER_READ_FILTER_ROW + ; +# else + | PNG_ASM_FLAG_MMX_READ_FILTER_SUB + | PNG_ASM_FLAG_MMX_READ_FILTER_UP + | PNG_ASM_FLAG_MMX_READ_FILTER_AVG + | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ; + + png_ptr->mmx_rowbytes_threshold = PNG_MMX_ROWBYTES_THRESHOLD_DEFAULT; + png_ptr->mmx_bitdepth_threshold = PNG_MMX_BITDEPTH_THRESHOLD_DEFAULT; +# endif + } else { + png_ptr->asm_flags &= ~( PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU + | PNG_MMX_READ_FLAGS + | PNG_MMX_WRITE_FLAGS ); + } + +# else /* !((PNGVCRD || PNGGCCRD) && PNG_ASSEMBLER_CODE_SUPPORTED)) */ + + /* clear all MMX flags; no support is compiled in */ + png_ptr->asm_flags &= ~( PNG_MMX_FLAGS ); + +# endif /* ?(PNGVCRD || PNGGCCRD) */ +} + +#endif /* !(PNG_ASSEMBLER_CODE_SUPPORTED) */ + +/* this function was added to libpng 1.2.0 */ +#if !defined(PNG_USE_PNGGCCRD) && \ + !(defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_USE_PNGVCRD)) +int PNGAPI +png_mmx_support(void) +{ + return -1; +} +#endif +#endif /* PNG_1_0_X */ + +#ifdef PNG_SIZE_T +/* Added at libpng version 1.2.6 */ + PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)); +png_size_t PNGAPI +png_convert_size(size_t size) +{ + if (size > (png_size_t)-1) + PNG_ABORT(); /* We haven't got access to png_ptr, so no png_error() */ + return ((png_size_t)size); +} +#endif /* PNG_SIZE_T */ diff --git a/com32/lib/libpng/pngerror.c b/com32/lib/libpng/pngerror.c new file mode 100644 index 0000000..9d4ec53 --- /dev/null +++ b/com32/lib/libpng/pngerror.c @@ -0,0 +1,295 @@ + +/* pngerror.c - stub functions for i/o and memory allocation + * + * libpng version 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file provides a location for all error handling. Users who + * need special error handling are expected to write replacement functions + * and use png_set_error_fn() to use those functions. See the instructions + * at each function. + */ + +#define PNG_INTERNAL +#include "png.h" + +static void /* PRIVATE */ +png_default_error PNGARG((png_structp png_ptr, + png_const_charp error_message)); +static void /* PRIVATE */ +png_default_warning PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +/* This function is called whenever there is a fatal error. This function + * should not be changed. If there is a need to handle errors differently, + * you should supply a replacement error function and use png_set_error_fn() + * to replace the error function at run-time. + */ +void PNGAPI +png_error(png_structp png_ptr, png_const_charp error_message) +{ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + char msg[16]; + if (png_ptr->flags&(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) + { + if (*error_message == '#') + { + int offset; + for (offset=1; offset<15; offset++) + if (*(error_message+offset) == ' ') + break; + if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT) + { + int i; + for (i=0; iflags&PNG_FLAG_STRIP_ERROR_TEXT) + { + msg[0]='0'; + msg[1]='\0'; + error_message=msg; + } + } + } +#endif + if (png_ptr != NULL && png_ptr->error_fn != NULL) + (*(png_ptr->error_fn))(png_ptr, error_message); + + /* If the custom handler doesn't exist, or if it returns, + use the default handler, which will not return. */ + png_default_error(png_ptr, error_message); +} + +/* This function is called whenever there is a non-fatal error. This function + * should not be changed. If there is a need to handle warnings differently, + * you should supply a replacement warning function and use + * png_set_error_fn() to replace the warning function at run-time. + */ +void PNGAPI +png_warning(png_structp png_ptr, png_const_charp warning_message) +{ + int offset = 0; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (png_ptr->flags&(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) +#endif + { + if (*warning_message == '#') + { + for (offset=1; offset<15; offset++) + if (*(warning_message+offset) == ' ') + break; + } + } + if (png_ptr != NULL && png_ptr->warning_fn != NULL) + (*(png_ptr->warning_fn))(png_ptr, warning_message+offset); + else + png_default_warning(png_ptr, warning_message+offset); +} + +/* These utilities are used internally to build an error message that relates + * to the current chunk. The chunk name comes from png_ptr->chunk_name, + * this is used to prefix the message. The message is limited in length + * to 63 bytes, the name characters are output as hex digits wrapped in [] + * if the character is invalid. + */ +#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) +static PNG_CONST char png_digit[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F' +}; + +static void /* PRIVATE */ +png_format_buffer(png_structp png_ptr, png_charp buffer, png_const_charp + error_message) +{ + int iout = 0, iin = 0; + + while (iin < 4) + { + int c = png_ptr->chunk_name[iin++]; + if (isnonalpha(c)) + { + buffer[iout++] = '['; + buffer[iout++] = png_digit[(c & 0xf0) >> 4]; + buffer[iout++] = png_digit[c & 0x0f]; + buffer[iout++] = ']'; + } + else + { + buffer[iout++] = (png_byte)c; + } + } + + if (error_message == NULL) + buffer[iout] = 0; + else + { + buffer[iout++] = ':'; + buffer[iout++] = ' '; + png_strncpy(buffer+iout, error_message, 63); + buffer[iout+63] = 0; + } +} + +void PNGAPI +png_chunk_error(png_structp png_ptr, png_const_charp error_message) +{ + char msg[18+64]; + png_format_buffer(png_ptr, msg, error_message); + png_error(png_ptr, msg); +} + +void PNGAPI +png_chunk_warning(png_structp png_ptr, png_const_charp warning_message) +{ + char msg[18+64]; + png_format_buffer(png_ptr, msg, warning_message); + png_warning(png_ptr, msg); +} + +/* This is the default error handling function. Note that replacements for + * this function MUST NOT RETURN, or the program will likely crash. This + * function is used by default, or if the program supplies NULL for the + * error function pointer in png_set_error_fn(). + */ +static void /* PRIVATE */ +png_default_error(png_structp png_ptr, png_const_charp error_message) +{ +#ifndef PNG_NO_CONSOLE_IO +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (*error_message == '#') + { + int offset; + char error_number[16]; + for (offset=0; offset<15; offset++) + { + error_number[offset] = *(error_message+offset+1); + if (*(error_message+offset) == ' ') + break; + } + if((offset > 1) && (offset < 15)) + { + error_number[offset-1]='\0'; + fprintf(stderr, "libpng error no. %s: %s\n", error_number, + error_message+offset); + } + else + fprintf(stderr, "libpng error: %s, offset=%d\n", error_message,offset); + } + else +#endif + fprintf(stderr, "libpng error: %s\n", error_message); +#endif + +#ifdef PNG_SETJMP_SUPPORTED +# ifdef USE_FAR_KEYWORD + { + jmp_buf jmpbuf; + png_memcpy(jmpbuf,png_ptr->jmpbuf,png_sizeof(jmp_buf)); + longjmp(jmpbuf, 1); + } +# else + longjmp(png_ptr->jmpbuf, 1); +# endif +#else + /* make compiler happy */ ; + if (png_ptr) + PNG_ABORT(); +#endif +#ifdef PNG_NO_CONSOLE_IO + /* make compiler happy */ ; + if (&error_message != NULL) + return; +#endif +} + +/* This function is called when there is a warning, but the library thinks + * it can continue anyway. Replacement functions don't have to do anything + * here if you don't want them to. In the default configuration, png_ptr is + * not used, but it is passed in case it may be useful. + */ +static void /* PRIVATE */ +png_default_warning(png_structp png_ptr, png_const_charp warning_message) +{ +#ifndef PNG_NO_CONSOLE_IO +# ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (*warning_message == '#') + { + int offset; + char warning_number[16]; + for (offset=0; offset<15; offset++) + { + warning_number[offset]=*(warning_message+offset+1); + if (*(warning_message+offset) == ' ') + break; + } + if((offset > 1) && (offset < 15)) + { + warning_number[offset-1]='\0'; + fprintf(stderr, "libpng warning no. %s: %s\n", warning_number, + warning_message+offset); + } + else + fprintf(stderr, "libpng warning: %s\n", warning_message); + } + else +# endif + fprintf(stderr, "libpng warning: %s\n", warning_message); +#else + /* make compiler happy */ ; + if (warning_message) + return; +#endif + /* make compiler happy */ ; + if (png_ptr) + return; +} + +/* This function is called when the application wants to use another method + * of handling errors and warnings. Note that the error function MUST NOT + * return to the calling routine or serious problems will occur. The return + * method used in the default routine calls longjmp(png_ptr->jmpbuf, 1) + */ +void PNGAPI +png_set_error_fn(png_structp png_ptr, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warning_fn) +{ + png_ptr->error_ptr = error_ptr; + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; +} + + +/* This function returns a pointer to the error_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy and png_read_destroy are called. + */ +png_voidp PNGAPI +png_get_error_ptr(png_structp png_ptr) +{ + return ((png_voidp)png_ptr->error_ptr); +} + + +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +void PNGAPI +png_set_strip_error_numbers(png_structp png_ptr, png_uint_32 strip_mode) +{ + if(png_ptr != NULL) + { + png_ptr->flags &= + ((~(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode); + } +} +#endif diff --git a/com32/lib/libpng/pnggccrd.c b/com32/lib/libpng/pnggccrd.c new file mode 100644 index 0000000..ee518f1 --- /dev/null +++ b/com32/lib/libpng/pnggccrd.c @@ -0,0 +1,5408 @@ +/* pnggccrd.c - mixed C/assembler version of utilities to read a PNG file + * + * For Intel x86 CPU (Pentium-MMX or later) and GNU C compiler. + * + * See http://www.intel.com/drg/pentiumII/appnotes/916/916.htm + * and http://www.intel.com/drg/pentiumII/appnotes/923/923.htm + * for Intel's performance analysis of the MMX vs. non-MMX code. + * + * libpng version 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * Copyright (c) 1998, Intel Corporation + * + * Based on MSVC code contributed by Nirav Chhatrapati, Intel Corp., 1998. + * Interface to libpng contributed by Gilles Vollant, 1999. + * GNU C port by Greg Roelofs, 1999-2001. + * + * Lines 2350-4300 converted in place with intel2gas 1.3.1: + * + * intel2gas -mdI pnggccrd.c.partially-msvc -o pnggccrd.c + * + * and then cleaned up by hand. See http://hermes.terminal.at/intel2gas/ . + * + * NOTE: A sufficiently recent version of GNU as (or as.exe under DOS/Windows) + * is required to assemble the newer MMX instructions such as movq. + * For djgpp, see + * + * ftp://ftp.simtel.net/pub/simtelnet/gnu/djgpp/v2gnu/bnu281b.zip + * + * (or a later version in the same directory). For Linux, check your + * distribution's web site(s) or try these links: + * + * http://rufus.w3.org/linux/RPM/binutils.html + * http://www.debian.org/Packages/stable/devel/binutils.html + * ftp://ftp.slackware.com/pub/linux/slackware/slackware/slakware/d1/ + * binutils.tgz + * + * For other platforms, see the main GNU site: + * + * ftp://ftp.gnu.org/pub/gnu/binutils/ + * + * Version 2.5.2l.15 is definitely too old... + */ + +/* + * TEMPORARY PORTING NOTES AND CHANGELOG (mostly by Greg Roelofs) + * ===================================== + * + * 19991006: + * - fixed sign error in post-MMX cleanup code (16- & 32-bit cases) + * + * 19991007: + * - additional optimizations (possible or definite): + * x [DONE] write MMX code for 64-bit case (pixel_bytes == 8) [not tested] + * - write MMX code for 48-bit case (pixel_bytes == 6) + * - figure out what's up with 24-bit case (pixel_bytes == 3): + * why subtract 8 from width_mmx in the pass 4/5 case? + * (only width_mmx case) (near line 1606) + * x [DONE] replace pixel_bytes within each block with the true + * constant value (or are compilers smart enough to do that?) + * - rewrite all MMX interlacing code so it's aligned with + * the *beginning* of the row buffer, not the end. This + * would not only allow one to eliminate half of the memory + * writes for odd passes (that is, pass == odd), it may also + * eliminate some unaligned-data-access exceptions (assuming + * there's a penalty for not aligning 64-bit accesses on + * 64-bit boundaries). The only catch is that the "leftover" + * pixel(s) at the end of the row would have to be saved, + * but there are enough unused MMX registers in every case, + * so this is not a problem. A further benefit is that the + * post-MMX cleanup code (C code) in at least some of the + * cases could be done within the assembler block. + * x [DONE] the "v3 v2 v1 v0 v7 v6 v5 v4" comments are confusing, + * inconsistent, and don't match the MMX Programmer's Reference + * Manual conventions anyway. They should be changed to + * "b7 b6 b5 b4 b3 b2 b1 b0," where b0 indicates the byte that + * was lowest in memory (e.g., corresponding to a left pixel) + * and b7 is the byte that was highest (e.g., a right pixel). + * + * 19991016: + * - Brennan's Guide notwithstanding, gcc under Linux does *not* + * want globals prefixed by underscores when referencing them-- + * i.e., if the variable is const4, then refer to it as const4, + * not _const4. This seems to be a djgpp-specific requirement. + * Also, such variables apparently *must* be declared outside + * of functions; neither static nor automatic variables work if + * defined within the scope of a single function, but both + * static and truly global (multi-module) variables work fine. + * + * 19991023: + * - fixed png_combine_row() non-MMX replication bug (odd passes only?) + * - switched from string-concatenation-with-macros to cleaner method of + * renaming global variables for djgpp--i.e., always use prefixes in + * inlined assembler code (== strings) and conditionally rename the + * variables, not the other way around. Hence _const4, _mask8_0, etc. + * + * 19991024: + * - fixed mmxsupport()/png_do_read_interlace() first-row bug + * This one was severely weird: even though mmxsupport() doesn't touch + * ebx (where "row" pointer was stored), it nevertheless managed to zero + * the register (even in static/non-fPIC code--see below), which in turn + * caused png_do_read_interlace() to return prematurely on the first row of + * interlaced images (i.e., without expanding the interlaced pixels). + * Inspection of the generated assembly code didn't turn up any clues, + * although it did point at a minor optimization (i.e., get rid of + * mmx_supported_local variable and just use eax). Possibly the CPUID + * instruction is more destructive than it looks? (Not yet checked.) + * - "info gcc" was next to useless, so compared fPIC and non-fPIC assembly + * listings... Apparently register spillage has to do with ebx, since + * it's used to index the global offset table. Commenting it out of the + * input-reg lists in png_combine_row() eliminated compiler barfage, so + * ifdef'd with __PIC__ macro: if defined, use a global for unmask + * + * 19991107: + * - verified CPUID clobberage: 12-char string constant ("GenuineIntel", + * "AuthenticAMD", etc.) placed in ebx:ecx:edx. Still need to polish. + * + * 19991120: + * - made "diff" variable (now "_dif") global to simplify conversion of + * filtering routines (running out of regs, sigh). "diff" is still used + * in interlacing routines, however. + * - fixed up both versions of mmxsupport() (ORIG_THAT_USED_TO_CLOBBER_EBX + * macro determines which is used); original not yet tested. + * + * 20000213: + * - when compiling with gcc, be sure to use -fomit-frame-pointer + * + * 20000319: + * - fixed a register-name typo in png_do_read_interlace(), default (MMX) case, + * pass == 4 or 5, that caused visible corruption of interlaced images + * + * 20000623: + * - Various problems were reported with gcc 2.95.2 in the Cygwin environment, + * many of the form "forbidden register 0 (ax) was spilled for class AREG." + * This is explained at http://gcc.gnu.org/fom_serv/cache/23.html, and + * Chuck Wilson supplied a patch involving dummy output registers. See + * http://sourceforge.net/bugs/?func=detailbug&bug_id=108741&group_id=5624 + * for the original (anonymous) SourceForge bug report. + * + * 20000706: + * - Chuck Wilson passed along these remaining gcc 2.95.2 errors: + * pnggccrd.c: In function `png_combine_row': + * pnggccrd.c:525: more than 10 operands in `asm' + * pnggccrd.c:669: more than 10 operands in `asm' + * pnggccrd.c:828: more than 10 operands in `asm' + * pnggccrd.c:994: more than 10 operands in `asm' + * pnggccrd.c:1177: more than 10 operands in `asm' + * They are all the same problem and can be worked around by using the + * global _unmask variable unconditionally, not just in the -fPIC case. + * Reportedly earlier versions of gcc also have the problem with more than + * 10 operands; they just don't report it. Much strangeness ensues, etc. + * + * 20000729: + * - enabled png_read_filter_row_mmx_up() (shortest remaining unconverted + * MMX routine); began converting png_read_filter_row_mmx_sub() + * - to finish remaining sections: + * - clean up indentation and comments + * - preload local variables + * - add output and input regs (order of former determines numerical + * mapping of latter) + * - avoid all usage of ebx (including bx, bh, bl) register [20000823] + * - remove "$" from addressing of Shift and Mask variables [20000823] + * + * 20000731: + * - global union vars causing segfaults in png_read_filter_row_mmx_sub()? + * + * 20000822: + * - ARGH, stupid png_read_filter_row_mmx_sub() segfault only happens with + * shared-library (-fPIC) version! Code works just fine as part of static + * library. Damn damn damn damn damn, should have tested that sooner. + * ebx is getting clobbered again (explicitly this time); need to save it + * on stack or rewrite asm code to avoid using it altogether. Blargh! + * + * 20000823: + * - first section was trickiest; all remaining sections have ebx -> edx now. + * (-fPIC works again.) Also added missing underscores to various Shift* + * and *Mask* globals and got rid of leading "$" signs. + * + * 20000826: + * - added visual separators to help navigate microscopic printed copies + * (http://pobox.com/~newt/code/gpr-latest.zip, mode 10); started working + * on png_read_filter_row_mmx_avg() + * + * 20000828: + * - finished png_read_filter_row_mmx_avg(): only Paeth left! (930 lines...) + * What the hell, did png_read_filter_row_mmx_paeth(), too. Comments not + * cleaned up/shortened in either routine, but functionality is complete + * and seems to be working fine. + * + * 20000829: + * - ahhh, figured out last(?) bit of gcc/gas asm-fu: if register is listed + * as an input reg (with dummy output variables, etc.), then it *cannot* + * also appear in the clobber list or gcc 2.95.2 will barf. The solution + * is simple enough... + * + * 20000914: + * - bug in png_read_filter_row_mmx_avg(): 16-bit grayscale not handled + * correctly (but 48-bit RGB just fine) + * + * 20000916: + * - fixed bug in png_read_filter_row_mmx_avg(), bpp == 2 case; three errors: + * - "_ShiftBpp.use = 24;" should have been "_ShiftBpp.use = 16;" + * - "_ShiftRem.use = 40;" should have been "_ShiftRem.use = 48;" + * - "psllq _ShiftRem, %%mm2" should have been "psrlq _ShiftRem, %%mm2" + * + * 20010101: + * - added new png_init_mmx_flags() function (here only because it needs to + * call mmxsupport(), which should probably become global png_mmxsupport()); + * modified other MMX routines to run conditionally (png_ptr->asm_flags) + * + * 20010103: + * - renamed mmxsupport() to png_mmx_support(), with auto-set of mmx_supported, + * and made it public; moved png_init_mmx_flags() to png.c as internal func + * + * 20010104: + * - removed dependency on png_read_filter_row_c() (C code already duplicated + * within MMX version of png_read_filter_row()) so no longer necessary to + * compile it into pngrutil.o + * + * 20010310: + * - fixed buffer-overrun bug in png_combine_row() C code (non-MMX) + * + * 20020304: + * - eliminated incorrect use of width_mmx in pixel_bytes == 8 case + * + * 20040724: + * - more tinkering with clobber list at lines 4529 and 5033, to get + * it to compile on gcc-3.4. + * + * STILL TO DO: + * - test png_do_read_interlace() 64-bit case (pixel_bytes == 8) + * - write MMX code for 48-bit case (pixel_bytes == 6) + * - figure out what's up with 24-bit case (pixel_bytes == 3): + * why subtract 8 from width_mmx in the pass 4/5 case? + * (only width_mmx case) (near line 1606) + * - rewrite all MMX interlacing code so it's aligned with beginning + * of the row buffer, not the end (see 19991007 for details) + * x pick one version of mmxsupport() and get rid of the other + * - add error messages to any remaining bogus default cases + * - enable pixel_depth == 8 cases in png_read_filter_row()? (test speed) + * x add support for runtime enable/disable/query of various MMX routines + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_USE_PNGGCCRD) + +int PNGAPI png_mmx_support(void); + +#ifdef PNG_USE_LOCAL_ARRAYS +static const int FARDATA png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; +static const int FARDATA png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; +static const int FARDATA png_pass_width[7] = {8, 4, 4, 2, 2, 1, 1}; +#endif + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +/* djgpp, Win32, and Cygwin add their own underscores to global variables, + * so define them without: */ +#if defined(__DJGPP__) || defined(WIN32) || defined(__CYGWIN__) +# define _mmx_supported mmx_supported +# define _const4 const4 +# define _const6 const6 +# define _mask8_0 mask8_0 +# define _mask16_1 mask16_1 +# define _mask16_0 mask16_0 +# define _mask24_2 mask24_2 +# define _mask24_1 mask24_1 +# define _mask24_0 mask24_0 +# define _mask32_3 mask32_3 +# define _mask32_2 mask32_2 +# define _mask32_1 mask32_1 +# define _mask32_0 mask32_0 +# define _mask48_5 mask48_5 +# define _mask48_4 mask48_4 +# define _mask48_3 mask48_3 +# define _mask48_2 mask48_2 +# define _mask48_1 mask48_1 +# define _mask48_0 mask48_0 +# define _LBCarryMask LBCarryMask +# define _HBClearMask HBClearMask +# define _ActiveMask ActiveMask +# define _ActiveMask2 ActiveMask2 +# define _ActiveMaskEnd ActiveMaskEnd +# define _ShiftBpp ShiftBpp +# define _ShiftRem ShiftRem +#ifdef PNG_THREAD_UNSAFE_OK +# define _unmask unmask +# define _FullLength FullLength +# define _MMXLength MMXLength +# define _dif dif +# define _patemp patemp +# define _pbtemp pbtemp +# define _pctemp pctemp +#endif +#endif + + +/* These constants are used in the inlined MMX assembly code. + Ignore gcc's "At top level: defined but not used" warnings. */ + +/* GRR 20000706: originally _unmask was needed only when compiling with -fPIC, + * since that case uses the %ebx register for indexing the Global Offset Table + * and there were no other registers available. But gcc 2.95 and later emit + * "more than 10 operands in `asm'" errors when %ebx is used to preload unmask + * in the non-PIC case, so we'll just use the global unconditionally now. + */ +#ifdef PNG_THREAD_UNSAFE_OK +static int _unmask; +#endif + +static unsigned long long _mask8_0 = 0x0102040810204080LL; + +static unsigned long long _mask16_1 = 0x0101020204040808LL; +static unsigned long long _mask16_0 = 0x1010202040408080LL; + +static unsigned long long _mask24_2 = 0x0101010202020404LL; +static unsigned long long _mask24_1 = 0x0408080810101020LL; +static unsigned long long _mask24_0 = 0x2020404040808080LL; + +static unsigned long long _mask32_3 = 0x0101010102020202LL; +static unsigned long long _mask32_2 = 0x0404040408080808LL; +static unsigned long long _mask32_1 = 0x1010101020202020LL; +static unsigned long long _mask32_0 = 0x4040404080808080LL; + +static unsigned long long _mask48_5 = 0x0101010101010202LL; +static unsigned long long _mask48_4 = 0x0202020204040404LL; +static unsigned long long _mask48_3 = 0x0404080808080808LL; +static unsigned long long _mask48_2 = 0x1010101010102020LL; +static unsigned long long _mask48_1 = 0x2020202040404040LL; +static unsigned long long _mask48_0 = 0x4040808080808080LL; + +static unsigned long long _const4 = 0x0000000000FFFFFFLL; +//static unsigned long long _const5 = 0x000000FFFFFF0000LL; // NOT USED +static unsigned long long _const6 = 0x00000000000000FFLL; + +// These are used in the row-filter routines and should/would be local +// variables if not for gcc addressing limitations. +// WARNING: Their presence probably defeats the thread safety of libpng. + +#ifdef PNG_THREAD_UNSAFE_OK +static png_uint_32 _FullLength; +static png_uint_32 _MMXLength; +static int _dif; +static int _patemp; // temp variables for Paeth routine +static int _pbtemp; +static int _pctemp; +#endif + +void /* PRIVATE */ +png_squelch_warnings(void) +{ +#ifdef PNG_THREAD_UNSAFE_OK + _dif = _dif; + _patemp = _patemp; + _pbtemp = _pbtemp; + _pctemp = _pctemp; + _MMXLength = _MMXLength; +#endif + _const4 = _const4; + _const6 = _const6; + _mask8_0 = _mask8_0; + _mask16_1 = _mask16_1; + _mask16_0 = _mask16_0; + _mask24_2 = _mask24_2; + _mask24_1 = _mask24_1; + _mask24_0 = _mask24_0; + _mask32_3 = _mask32_3; + _mask32_2 = _mask32_2; + _mask32_1 = _mask32_1; + _mask32_0 = _mask32_0; + _mask48_5 = _mask48_5; + _mask48_4 = _mask48_4; + _mask48_3 = _mask48_3; + _mask48_2 = _mask48_2; + _mask48_1 = _mask48_1; + _mask48_0 = _mask48_0; +} +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + + +static int _mmx_supported = 2; + +/*===========================================================================*/ +/* */ +/* P N G _ C O M B I N E _ R O W */ +/* */ +/*===========================================================================*/ + +#if defined(PNG_HAVE_ASSEMBLER_COMBINE_ROW) + +#define BPP2 2 +#define BPP3 3 /* bytes per pixel (a.k.a. pixel_bytes) */ +#define BPP4 4 +#define BPP6 6 /* (defined only to help avoid cut-and-paste errors) */ +#define BPP8 8 + +/* Combines the row recently read in with the previous row. + This routine takes care of alpha and transparency if requested. + This routine also handles the two methods of progressive display + of interlaced images, depending on the mask value. + The mask value describes which pixels are to be combined with + the row. The pattern always repeats every 8 pixels, so just 8 + bits are needed. A one indicates the pixel is to be combined; a + zero indicates the pixel is to be skipped. This is in addition + to any alpha or transparency value associated with the pixel. + If you want all pixels to be combined, pass 0xff (255) in mask. */ + +/* Use this routine for the x86 platform - it uses a faster MMX routine + if the machine supports MMX. */ + +void /* PRIVATE */ +png_combine_row(png_structp png_ptr, png_bytep row, int mask) +{ + png_debug(1, "in png_combine_row (pnggccrd.c)\n"); + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) + if (_mmx_supported == 2) { +#if !defined(PNG_1_0_X) + /* this should have happened in png_init_mmx_flags() already */ + png_warning(png_ptr, "asm_flags may not have been initialized"); +#endif + png_mmx_support(); + } +#endif + + if (mask == 0xff) + { + png_debug(2,"mask == 0xff: doing single png_memcpy()\n"); + png_memcpy(row, png_ptr->row_buf + 1, + (png_size_t)PNG_ROWBYTES(png_ptr->row_info.pixel_depth,png_ptr->width)); + } + else /* (png_combine_row() is never called with mask == 0) */ + { + switch (png_ptr->row_info.pixel_depth) + { + case 1: /* png_ptr->row_info.pixel_depth */ + { + png_bytep sp; + png_bytep dp; + int s_inc, s_start, s_end; + int m; + int shift; + png_uint_32 i; + + sp = png_ptr->row_buf + 1; + dp = row; + m = 0x80; +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 7; + s_inc = 1; + } + else +#endif + { + s_start = 7; + s_end = 0; + s_inc = -1; + } + + shift = s_start; + + for (i = 0; i < png_ptr->width; i++) + { + if (m & mask) + { + int value; + + value = (*sp >> shift) & 0x1; + *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + + case 2: /* png_ptr->row_info.pixel_depth */ + { + png_bytep sp; + png_bytep dp; + int s_start, s_end, s_inc; + int m; + int shift; + png_uint_32 i; + int value; + + sp = png_ptr->row_buf + 1; + dp = row; + m = 0x80; +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 6; + s_inc = 2; + } + else +#endif + { + s_start = 6; + s_end = 0; + s_inc = -2; + } + + shift = s_start; + + for (i = 0; i < png_ptr->width; i++) + { + if (m & mask) + { + value = (*sp >> shift) & 0x3; + *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + + case 4: /* png_ptr->row_info.pixel_depth */ + { + png_bytep sp; + png_bytep dp; + int s_start, s_end, s_inc; + int m; + int shift; + png_uint_32 i; + int value; + + sp = png_ptr->row_buf + 1; + dp = row; + m = 0x80; +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 4; + s_inc = 4; + } + else +#endif + { + s_start = 4; + s_end = 0; + s_inc = -4; + } + shift = s_start; + + for (i = 0; i < png_ptr->width; i++) + { + if (m & mask) + { + value = (*sp >> shift) & 0xf; + *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + + case 8: /* png_ptr->row_info.pixel_depth */ + { + png_bytep srcptr; + png_bytep dstptr; + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK) +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) + /* && _mmx_supported */ ) +#else + if (_mmx_supported) +#endif + { + png_uint_32 len; + int diff; + int dummy_value_a; // fix 'forbidden register spilled' error + int dummy_value_d; + int dummy_value_c; + int dummy_value_S; + int dummy_value_D; + _unmask = ~mask; // global variable for -fPIC version + srcptr = png_ptr->row_buf + 1; + dstptr = row; + len = png_ptr->width &~7; // reduce to multiple of 8 + diff = (int) (png_ptr->width & 7); // amount lost + + __asm__ __volatile__ ( + "movd _unmask, %%mm7 \n\t" // load bit pattern + "psubb %%mm6, %%mm6 \n\t" // zero mm6 + "punpcklbw %%mm7, %%mm7 \n\t" + "punpcklwd %%mm7, %%mm7 \n\t" + "punpckldq %%mm7, %%mm7 \n\t" // fill reg with 8 masks + + "movq _mask8_0, %%mm0 \n\t" + "pand %%mm7, %%mm0 \n\t" // nonzero if keep byte + "pcmpeqb %%mm6, %%mm0 \n\t" // zeros->1s, v versa + +// preload "movl len, %%ecx \n\t" // load length of line +// preload "movl srcptr, %%esi \n\t" // load source +// preload "movl dstptr, %%edi \n\t" // load dest + + "cmpl $0, %%ecx \n\t" // len == 0 ? + "je mainloop8end \n\t" + + "mainloop8: \n\t" + "movq (%%esi), %%mm4 \n\t" // *srcptr + "pand %%mm0, %%mm4 \n\t" + "movq %%mm0, %%mm6 \n\t" + "pandn (%%edi), %%mm6 \n\t" // *dstptr + "por %%mm6, %%mm4 \n\t" + "movq %%mm4, (%%edi) \n\t" + "addl $8, %%esi \n\t" // inc by 8 bytes processed + "addl $8, %%edi \n\t" + "subl $8, %%ecx \n\t" // dec by 8 pixels processed + "ja mainloop8 \n\t" + + "mainloop8end: \n\t" +// preload "movl diff, %%ecx \n\t" // (diff is in eax) + "movl %%eax, %%ecx \n\t" + "cmpl $0, %%ecx \n\t" + "jz end8 \n\t" +// preload "movl mask, %%edx \n\t" + "sall $24, %%edx \n\t" // make low byte, high byte + + "secondloop8: \n\t" + "sall %%edx \n\t" // move high bit to CF + "jnc skip8 \n\t" // if CF = 0 + "movb (%%esi), %%al \n\t" + "movb %%al, (%%edi) \n\t" + + "skip8: \n\t" + "incl %%esi \n\t" + "incl %%edi \n\t" + "decl %%ecx \n\t" + "jnz secondloop8 \n\t" + + "end8: \n\t" + "EMMS \n\t" // DONE + + : "=a" (dummy_value_a), // output regs (dummy) + "=d" (dummy_value_d), + "=c" (dummy_value_c), + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "3" (srcptr), // esi // input regs + "4" (dstptr), // edi + "0" (diff), // eax +// was (unmask) "b" RESERVED // ebx // Global Offset Table idx + "2" (len), // ecx + "1" (mask) // edx + +#if 0 /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */ + : "%mm0", "%mm4", "%mm6", "%mm7" // clobber list +#endif + ); + } + else /* mmx _not supported - Use modified C routine */ +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + { + register png_uint_32 i; + png_uint_32 initial_val = png_pass_start[png_ptr->pass]; + /* png.c: png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */ + register int stride = png_pass_inc[png_ptr->pass]; + /* png.c: png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */ + register int rep_bytes = png_pass_width[png_ptr->pass]; + /* png.c: png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */ + png_uint_32 len = png_ptr->width &~7; /* reduce to mult. of 8 */ + int diff = (int) (png_ptr->width & 7); /* amount lost */ + register png_uint_32 final_val = len; /* GRR bugfix */ + + srcptr = png_ptr->row_buf + 1 + initial_val; + dstptr = row + initial_val; + + for (i = initial_val; i < final_val; i += stride) + { + png_memcpy(dstptr, srcptr, rep_bytes); + srcptr += stride; + dstptr += stride; + } + if (diff) /* number of leftover pixels: 3 for pngtest */ + { + final_val+=diff /* *BPP1 */ ; + for (; i < final_val; i += stride) + { + if (rep_bytes > (int)(final_val-i)) + rep_bytes = (int)(final_val-i); + png_memcpy(dstptr, srcptr, rep_bytes); + srcptr += stride; + dstptr += stride; + } + } + + } /* end of else (_mmx_supported) */ + + break; + } /* end 8 bpp */ + + case 16: /* png_ptr->row_info.pixel_depth */ + { + png_bytep srcptr; + png_bytep dstptr; + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK) +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) + /* && _mmx_supported */ ) +#else + if (_mmx_supported) +#endif + { + png_uint_32 len; + int diff; + int dummy_value_a; // fix 'forbidden register spilled' error + int dummy_value_d; + int dummy_value_c; + int dummy_value_S; + int dummy_value_D; + _unmask = ~mask; // global variable for -fPIC version + srcptr = png_ptr->row_buf + 1; + dstptr = row; + len = png_ptr->width &~7; // reduce to multiple of 8 + diff = (int) (png_ptr->width & 7); // amount lost // + + __asm__ __volatile__ ( + "movd _unmask, %%mm7 \n\t" // load bit pattern + "psubb %%mm6, %%mm6 \n\t" // zero mm6 + "punpcklbw %%mm7, %%mm7 \n\t" + "punpcklwd %%mm7, %%mm7 \n\t" + "punpckldq %%mm7, %%mm7 \n\t" // fill reg with 8 masks + + "movq _mask16_0, %%mm0 \n\t" + "movq _mask16_1, %%mm1 \n\t" + + "pand %%mm7, %%mm0 \n\t" + "pand %%mm7, %%mm1 \n\t" + + "pcmpeqb %%mm6, %%mm0 \n\t" + "pcmpeqb %%mm6, %%mm1 \n\t" + +// preload "movl len, %%ecx \n\t" // load length of line +// preload "movl srcptr, %%esi \n\t" // load source +// preload "movl dstptr, %%edi \n\t" // load dest + + "cmpl $0, %%ecx \n\t" + "jz mainloop16end \n\t" + + "mainloop16: \n\t" + "movq (%%esi), %%mm4 \n\t" + "pand %%mm0, %%mm4 \n\t" + "movq %%mm0, %%mm6 \n\t" + "movq (%%edi), %%mm7 \n\t" + "pandn %%mm7, %%mm6 \n\t" + "por %%mm6, %%mm4 \n\t" + "movq %%mm4, (%%edi) \n\t" + + "movq 8(%%esi), %%mm5 \n\t" + "pand %%mm1, %%mm5 \n\t" + "movq %%mm1, %%mm7 \n\t" + "movq 8(%%edi), %%mm6 \n\t" + "pandn %%mm6, %%mm7 \n\t" + "por %%mm7, %%mm5 \n\t" + "movq %%mm5, 8(%%edi) \n\t" + + "addl $16, %%esi \n\t" // inc by 16 bytes processed + "addl $16, %%edi \n\t" + "subl $8, %%ecx \n\t" // dec by 8 pixels processed + "ja mainloop16 \n\t" + + "mainloop16end: \n\t" +// preload "movl diff, %%ecx \n\t" // (diff is in eax) + "movl %%eax, %%ecx \n\t" + "cmpl $0, %%ecx \n\t" + "jz end16 \n\t" +// preload "movl mask, %%edx \n\t" + "sall $24, %%edx \n\t" // make low byte, high byte + + "secondloop16: \n\t" + "sall %%edx \n\t" // move high bit to CF + "jnc skip16 \n\t" // if CF = 0 + "movw (%%esi), %%ax \n\t" + "movw %%ax, (%%edi) \n\t" + + "skip16: \n\t" + "addl $2, %%esi \n\t" + "addl $2, %%edi \n\t" + "decl %%ecx \n\t" + "jnz secondloop16 \n\t" + + "end16: \n\t" + "EMMS \n\t" // DONE + + : "=a" (dummy_value_a), // output regs (dummy) + "=c" (dummy_value_c), + "=d" (dummy_value_d), + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "0" (diff), // eax // input regs +// was (unmask) " " RESERVED // ebx // Global Offset Table idx + "1" (len), // ecx + "2" (mask), // edx + "3" (srcptr), // esi + "4" (dstptr) // edi + +#if 0 /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */ + : "%mm0", "%mm1", "%mm4" // clobber list + , "%mm5", "%mm6", "%mm7" +#endif + ); + } + else /* mmx _not supported - Use modified C routine */ +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + { + register png_uint_32 i; + png_uint_32 initial_val = BPP2 * png_pass_start[png_ptr->pass]; + /* png.c: png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */ + register int stride = BPP2 * png_pass_inc[png_ptr->pass]; + /* png.c: png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */ + register int rep_bytes = BPP2 * png_pass_width[png_ptr->pass]; + /* png.c: png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */ + png_uint_32 len = png_ptr->width &~7; /* reduce to mult. of 8 */ + int diff = (int) (png_ptr->width & 7); /* amount lost */ + register png_uint_32 final_val = BPP2 * len; /* GRR bugfix */ + + srcptr = png_ptr->row_buf + 1 + initial_val; + dstptr = row + initial_val; + + for (i = initial_val; i < final_val; i += stride) + { + png_memcpy(dstptr, srcptr, rep_bytes); + srcptr += stride; + dstptr += stride; + } + if (diff) /* number of leftover pixels: 3 for pngtest */ + { + final_val+=diff*BPP2; + for (; i < final_val; i += stride) + { + if (rep_bytes > (int)(final_val-i)) + rep_bytes = (int)(final_val-i); + png_memcpy(dstptr, srcptr, rep_bytes); + srcptr += stride; + dstptr += stride; + } + } + } /* end of else (_mmx_supported) */ + + break; + } /* end 16 bpp */ + + case 24: /* png_ptr->row_info.pixel_depth */ + { + png_bytep srcptr; + png_bytep dstptr; + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK) +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) + /* && _mmx_supported */ ) +#else + if (_mmx_supported) +#endif + { + png_uint_32 len; + int diff; + int dummy_value_a; // fix 'forbidden register spilled' error + int dummy_value_d; + int dummy_value_c; + int dummy_value_S; + int dummy_value_D; + _unmask = ~mask; // global variable for -fPIC version + srcptr = png_ptr->row_buf + 1; + dstptr = row; + len = png_ptr->width &~7; // reduce to multiple of 8 + diff = (int) (png_ptr->width & 7); // amount lost // + + __asm__ __volatile__ ( + "movd _unmask, %%mm7 \n\t" // load bit pattern + "psubb %%mm6, %%mm6 \n\t" // zero mm6 + "punpcklbw %%mm7, %%mm7 \n\t" + "punpcklwd %%mm7, %%mm7 \n\t" + "punpckldq %%mm7, %%mm7 \n\t" // fill reg with 8 masks + + "movq _mask24_0, %%mm0 \n\t" + "movq _mask24_1, %%mm1 \n\t" + "movq _mask24_2, %%mm2 \n\t" + + "pand %%mm7, %%mm0 \n\t" + "pand %%mm7, %%mm1 \n\t" + "pand %%mm7, %%mm2 \n\t" + + "pcmpeqb %%mm6, %%mm0 \n\t" + "pcmpeqb %%mm6, %%mm1 \n\t" + "pcmpeqb %%mm6, %%mm2 \n\t" + +// preload "movl len, %%ecx \n\t" // load length of line +// preload "movl srcptr, %%esi \n\t" // load source +// preload "movl dstptr, %%edi \n\t" // load dest + + "cmpl $0, %%ecx \n\t" + "jz mainloop24end \n\t" + + "mainloop24: \n\t" + "movq (%%esi), %%mm4 \n\t" + "pand %%mm0, %%mm4 \n\t" + "movq %%mm0, %%mm6 \n\t" + "movq (%%edi), %%mm7 \n\t" + "pandn %%mm7, %%mm6 \n\t" + "por %%mm6, %%mm4 \n\t" + "movq %%mm4, (%%edi) \n\t" + + "movq 8(%%esi), %%mm5 \n\t" + "pand %%mm1, %%mm5 \n\t" + "movq %%mm1, %%mm7 \n\t" + "movq 8(%%edi), %%mm6 \n\t" + "pandn %%mm6, %%mm7 \n\t" + "por %%mm7, %%mm5 \n\t" + "movq %%mm5, 8(%%edi) \n\t" + + "movq 16(%%esi), %%mm6 \n\t" + "pand %%mm2, %%mm6 \n\t" + "movq %%mm2, %%mm4 \n\t" + "movq 16(%%edi), %%mm7 \n\t" + "pandn %%mm7, %%mm4 \n\t" + "por %%mm4, %%mm6 \n\t" + "movq %%mm6, 16(%%edi) \n\t" + + "addl $24, %%esi \n\t" // inc by 24 bytes processed + "addl $24, %%edi \n\t" + "subl $8, %%ecx \n\t" // dec by 8 pixels processed + + "ja mainloop24 \n\t" + + "mainloop24end: \n\t" +// preload "movl diff, %%ecx \n\t" // (diff is in eax) + "movl %%eax, %%ecx \n\t" + "cmpl $0, %%ecx \n\t" + "jz end24 \n\t" +// preload "movl mask, %%edx \n\t" + "sall $24, %%edx \n\t" // make low byte, high byte + + "secondloop24: \n\t" + "sall %%edx \n\t" // move high bit to CF + "jnc skip24 \n\t" // if CF = 0 + "movw (%%esi), %%ax \n\t" + "movw %%ax, (%%edi) \n\t" + "xorl %%eax, %%eax \n\t" + "movb 2(%%esi), %%al \n\t" + "movb %%al, 2(%%edi) \n\t" + + "skip24: \n\t" + "addl $3, %%esi \n\t" + "addl $3, %%edi \n\t" + "decl %%ecx \n\t" + "jnz secondloop24 \n\t" + + "end24: \n\t" + "EMMS \n\t" // DONE + + : "=a" (dummy_value_a), // output regs (dummy) + "=d" (dummy_value_d), + "=c" (dummy_value_c), + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "3" (srcptr), // esi // input regs + "4" (dstptr), // edi + "0" (diff), // eax +// was (unmask) "b" RESERVED // ebx // Global Offset Table idx + "2" (len), // ecx + "1" (mask) // edx + +#if 0 /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */ + : "%mm0", "%mm1", "%mm2" // clobber list + , "%mm4", "%mm5", "%mm6", "%mm7" +#endif + ); + } + else /* mmx _not supported - Use modified C routine */ +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + { + register png_uint_32 i; + png_uint_32 initial_val = BPP3 * png_pass_start[png_ptr->pass]; + /* png.c: png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */ + register int stride = BPP3 * png_pass_inc[png_ptr->pass]; + /* png.c: png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */ + register int rep_bytes = BPP3 * png_pass_width[png_ptr->pass]; + /* png.c: png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */ + png_uint_32 len = png_ptr->width &~7; /* reduce to mult. of 8 */ + int diff = (int) (png_ptr->width & 7); /* amount lost */ + register png_uint_32 final_val = BPP3 * len; /* GRR bugfix */ + + srcptr = png_ptr->row_buf + 1 + initial_val; + dstptr = row + initial_val; + + for (i = initial_val; i < final_val; i += stride) + { + png_memcpy(dstptr, srcptr, rep_bytes); + srcptr += stride; + dstptr += stride; + } + if (diff) /* number of leftover pixels: 3 for pngtest */ + { + final_val+=diff*BPP3; + for (; i < final_val; i += stride) + { + if (rep_bytes > (int)(final_val-i)) + rep_bytes = (int)(final_val-i); + png_memcpy(dstptr, srcptr, rep_bytes); + srcptr += stride; + dstptr += stride; + } + } + } /* end of else (_mmx_supported) */ + + break; + } /* end 24 bpp */ + + case 32: /* png_ptr->row_info.pixel_depth */ + { + png_bytep srcptr; + png_bytep dstptr; + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK) +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) + /* && _mmx_supported */ ) +#else + if (_mmx_supported) +#endif + { + png_uint_32 len; + int diff; + int dummy_value_a; // fix 'forbidden register spilled' error + int dummy_value_d; + int dummy_value_c; + int dummy_value_S; + int dummy_value_D; + _unmask = ~mask; // global variable for -fPIC version + srcptr = png_ptr->row_buf + 1; + dstptr = row; + len = png_ptr->width &~7; // reduce to multiple of 8 + diff = (int) (png_ptr->width & 7); // amount lost // + + __asm__ __volatile__ ( + "movd _unmask, %%mm7 \n\t" // load bit pattern + "psubb %%mm6, %%mm6 \n\t" // zero mm6 + "punpcklbw %%mm7, %%mm7 \n\t" + "punpcklwd %%mm7, %%mm7 \n\t" + "punpckldq %%mm7, %%mm7 \n\t" // fill reg with 8 masks + + "movq _mask32_0, %%mm0 \n\t" + "movq _mask32_1, %%mm1 \n\t" + "movq _mask32_2, %%mm2 \n\t" + "movq _mask32_3, %%mm3 \n\t" + + "pand %%mm7, %%mm0 \n\t" + "pand %%mm7, %%mm1 \n\t" + "pand %%mm7, %%mm2 \n\t" + "pand %%mm7, %%mm3 \n\t" + + "pcmpeqb %%mm6, %%mm0 \n\t" + "pcmpeqb %%mm6, %%mm1 \n\t" + "pcmpeqb %%mm6, %%mm2 \n\t" + "pcmpeqb %%mm6, %%mm3 \n\t" + +// preload "movl len, %%ecx \n\t" // load length of line +// preload "movl srcptr, %%esi \n\t" // load source +// preload "movl dstptr, %%edi \n\t" // load dest + + "cmpl $0, %%ecx \n\t" // lcr + "jz mainloop32end \n\t" + + "mainloop32: \n\t" + "movq (%%esi), %%mm4 \n\t" + "pand %%mm0, %%mm4 \n\t" + "movq %%mm0, %%mm6 \n\t" + "movq (%%edi), %%mm7 \n\t" + "pandn %%mm7, %%mm6 \n\t" + "por %%mm6, %%mm4 \n\t" + "movq %%mm4, (%%edi) \n\t" + + "movq 8(%%esi), %%mm5 \n\t" + "pand %%mm1, %%mm5 \n\t" + "movq %%mm1, %%mm7 \n\t" + "movq 8(%%edi), %%mm6 \n\t" + "pandn %%mm6, %%mm7 \n\t" + "por %%mm7, %%mm5 \n\t" + "movq %%mm5, 8(%%edi) \n\t" + + "movq 16(%%esi), %%mm6 \n\t" + "pand %%mm2, %%mm6 \n\t" + "movq %%mm2, %%mm4 \n\t" + "movq 16(%%edi), %%mm7 \n\t" + "pandn %%mm7, %%mm4 \n\t" + "por %%mm4, %%mm6 \n\t" + "movq %%mm6, 16(%%edi) \n\t" + + "movq 24(%%esi), %%mm7 \n\t" + "pand %%mm3, %%mm7 \n\t" + "movq %%mm3, %%mm5 \n\t" + "movq 24(%%edi), %%mm4 \n\t" + "pandn %%mm4, %%mm5 \n\t" + "por %%mm5, %%mm7 \n\t" + "movq %%mm7, 24(%%edi) \n\t" + + "addl $32, %%esi \n\t" // inc by 32 bytes processed + "addl $32, %%edi \n\t" + "subl $8, %%ecx \n\t" // dec by 8 pixels processed + "ja mainloop32 \n\t" + + "mainloop32end: \n\t" +// preload "movl diff, %%ecx \n\t" // (diff is in eax) + "movl %%eax, %%ecx \n\t" + "cmpl $0, %%ecx \n\t" + "jz end32 \n\t" +// preload "movl mask, %%edx \n\t" + "sall $24, %%edx \n\t" // low byte => high byte + + "secondloop32: \n\t" + "sall %%edx \n\t" // move high bit to CF + "jnc skip32 \n\t" // if CF = 0 + "movl (%%esi), %%eax \n\t" + "movl %%eax, (%%edi) \n\t" + + "skip32: \n\t" + "addl $4, %%esi \n\t" + "addl $4, %%edi \n\t" + "decl %%ecx \n\t" + "jnz secondloop32 \n\t" + + "end32: \n\t" + "EMMS \n\t" // DONE + + : "=a" (dummy_value_a), // output regs (dummy) + "=d" (dummy_value_d), + "=c" (dummy_value_c), + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "3" (srcptr), // esi // input regs + "4" (dstptr), // edi + "0" (diff), // eax +// was (unmask) "b" RESERVED // ebx // Global Offset Table idx + "2" (len), // ecx + "1" (mask) // edx + +#if 0 /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */ + : "%mm0", "%mm1", "%mm2", "%mm3" // clobber list + , "%mm4", "%mm5", "%mm6", "%mm7" +#endif + ); + } + else /* mmx _not supported - Use modified C routine */ +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + { + register png_uint_32 i; + png_uint_32 initial_val = BPP4 * png_pass_start[png_ptr->pass]; + /* png.c: png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */ + register int stride = BPP4 * png_pass_inc[png_ptr->pass]; + /* png.c: png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */ + register int rep_bytes = BPP4 * png_pass_width[png_ptr->pass]; + /* png.c: png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */ + png_uint_32 len = png_ptr->width &~7; /* reduce to mult. of 8 */ + int diff = (int) (png_ptr->width & 7); /* amount lost */ + register png_uint_32 final_val = BPP4 * len; /* GRR bugfix */ + + srcptr = png_ptr->row_buf + 1 + initial_val; + dstptr = row + initial_val; + + for (i = initial_val; i < final_val; i += stride) + { + png_memcpy(dstptr, srcptr, rep_bytes); + srcptr += stride; + dstptr += stride; + } + if (diff) /* number of leftover pixels: 3 for pngtest */ + { + final_val+=diff*BPP4; + for (; i < final_val; i += stride) + { + if (rep_bytes > (int)(final_val-i)) + rep_bytes = (int)(final_val-i); + png_memcpy(dstptr, srcptr, rep_bytes); + srcptr += stride; + dstptr += stride; + } + } + } /* end of else (_mmx_supported) */ + + break; + } /* end 32 bpp */ + + case 48: /* png_ptr->row_info.pixel_depth */ + { + png_bytep srcptr; + png_bytep dstptr; + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK) +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) + /* && _mmx_supported */ ) +#else + if (_mmx_supported) +#endif + { + png_uint_32 len; + int diff; + int dummy_value_a; // fix 'forbidden register spilled' error + int dummy_value_d; + int dummy_value_c; + int dummy_value_S; + int dummy_value_D; + _unmask = ~mask; // global variable for -fPIC version + srcptr = png_ptr->row_buf + 1; + dstptr = row; + len = png_ptr->width &~7; // reduce to multiple of 8 + diff = (int) (png_ptr->width & 7); // amount lost // + + __asm__ __volatile__ ( + "movd _unmask, %%mm7 \n\t" // load bit pattern + "psubb %%mm6, %%mm6 \n\t" // zero mm6 + "punpcklbw %%mm7, %%mm7 \n\t" + "punpcklwd %%mm7, %%mm7 \n\t" + "punpckldq %%mm7, %%mm7 \n\t" // fill reg with 8 masks + + "movq _mask48_0, %%mm0 \n\t" + "movq _mask48_1, %%mm1 \n\t" + "movq _mask48_2, %%mm2 \n\t" + "movq _mask48_3, %%mm3 \n\t" + "movq _mask48_4, %%mm4 \n\t" + "movq _mask48_5, %%mm5 \n\t" + + "pand %%mm7, %%mm0 \n\t" + "pand %%mm7, %%mm1 \n\t" + "pand %%mm7, %%mm2 \n\t" + "pand %%mm7, %%mm3 \n\t" + "pand %%mm7, %%mm4 \n\t" + "pand %%mm7, %%mm5 \n\t" + + "pcmpeqb %%mm6, %%mm0 \n\t" + "pcmpeqb %%mm6, %%mm1 \n\t" + "pcmpeqb %%mm6, %%mm2 \n\t" + "pcmpeqb %%mm6, %%mm3 \n\t" + "pcmpeqb %%mm6, %%mm4 \n\t" + "pcmpeqb %%mm6, %%mm5 \n\t" + +// preload "movl len, %%ecx \n\t" // load length of line +// preload "movl srcptr, %%esi \n\t" // load source +// preload "movl dstptr, %%edi \n\t" // load dest + + "cmpl $0, %%ecx \n\t" + "jz mainloop48end \n\t" + + "mainloop48: \n\t" + "movq (%%esi), %%mm7 \n\t" + "pand %%mm0, %%mm7 \n\t" + "movq %%mm0, %%mm6 \n\t" + "pandn (%%edi), %%mm6 \n\t" + "por %%mm6, %%mm7 \n\t" + "movq %%mm7, (%%edi) \n\t" + + "movq 8(%%esi), %%mm6 \n\t" + "pand %%mm1, %%mm6 \n\t" + "movq %%mm1, %%mm7 \n\t" + "pandn 8(%%edi), %%mm7 \n\t" + "por %%mm7, %%mm6 \n\t" + "movq %%mm6, 8(%%edi) \n\t" + + "movq 16(%%esi), %%mm6 \n\t" + "pand %%mm2, %%mm6 \n\t" + "movq %%mm2, %%mm7 \n\t" + "pandn 16(%%edi), %%mm7 \n\t" + "por %%mm7, %%mm6 \n\t" + "movq %%mm6, 16(%%edi) \n\t" + + "movq 24(%%esi), %%mm7 \n\t" + "pand %%mm3, %%mm7 \n\t" + "movq %%mm3, %%mm6 \n\t" + "pandn 24(%%edi), %%mm6 \n\t" + "por %%mm6, %%mm7 \n\t" + "movq %%mm7, 24(%%edi) \n\t" + + "movq 32(%%esi), %%mm6 \n\t" + "pand %%mm4, %%mm6 \n\t" + "movq %%mm4, %%mm7 \n\t" + "pandn 32(%%edi), %%mm7 \n\t" + "por %%mm7, %%mm6 \n\t" + "movq %%mm6, 32(%%edi) \n\t" + + "movq 40(%%esi), %%mm7 \n\t" + "pand %%mm5, %%mm7 \n\t" + "movq %%mm5, %%mm6 \n\t" + "pandn 40(%%edi), %%mm6 \n\t" + "por %%mm6, %%mm7 \n\t" + "movq %%mm7, 40(%%edi) \n\t" + + "addl $48, %%esi \n\t" // inc by 48 bytes processed + "addl $48, %%edi \n\t" + "subl $8, %%ecx \n\t" // dec by 8 pixels processed + + "ja mainloop48 \n\t" + + "mainloop48end: \n\t" +// preload "movl diff, %%ecx \n\t" // (diff is in eax) + "movl %%eax, %%ecx \n\t" + "cmpl $0, %%ecx \n\t" + "jz end48 \n\t" +// preload "movl mask, %%edx \n\t" + "sall $24, %%edx \n\t" // make low byte, high byte + + "secondloop48: \n\t" + "sall %%edx \n\t" // move high bit to CF + "jnc skip48 \n\t" // if CF = 0 + "movl (%%esi), %%eax \n\t" + "movl %%eax, (%%edi) \n\t" + + "skip48: \n\t" + "addl $4, %%esi \n\t" + "addl $4, %%edi \n\t" + "decl %%ecx \n\t" + "jnz secondloop48 \n\t" + + "end48: \n\t" + "EMMS \n\t" // DONE + + : "=a" (dummy_value_a), // output regs (dummy) + "=d" (dummy_value_d), + "=c" (dummy_value_c), + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "3" (srcptr), // esi // input regs + "4" (dstptr), // edi + "0" (diff), // eax +// was (unmask) "b" RESERVED // ebx // Global Offset Table idx + "2" (len), // ecx + "1" (mask) // edx + +#if 0 /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */ + : "%mm0", "%mm1", "%mm2", "%mm3" // clobber list + , "%mm4", "%mm5", "%mm6", "%mm7" +#endif + ); + } + else /* mmx _not supported - Use modified C routine */ +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + { + register png_uint_32 i; + png_uint_32 initial_val = BPP6 * png_pass_start[png_ptr->pass]; + /* png.c: png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */ + register int stride = BPP6 * png_pass_inc[png_ptr->pass]; + /* png.c: png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */ + register int rep_bytes = BPP6 * png_pass_width[png_ptr->pass]; + /* png.c: png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */ + png_uint_32 len = png_ptr->width &~7; /* reduce to mult. of 8 */ + int diff = (int) (png_ptr->width & 7); /* amount lost */ + register png_uint_32 final_val = BPP6 * len; /* GRR bugfix */ + + srcptr = png_ptr->row_buf + 1 + initial_val; + dstptr = row + initial_val; + + for (i = initial_val; i < final_val; i += stride) + { + png_memcpy(dstptr, srcptr, rep_bytes); + srcptr += stride; + dstptr += stride; + } + if (diff) /* number of leftover pixels: 3 for pngtest */ + { + final_val+=diff*BPP6; + for (; i < final_val; i += stride) + { + if (rep_bytes > (int)(final_val-i)) + rep_bytes = (int)(final_val-i); + png_memcpy(dstptr, srcptr, rep_bytes); + srcptr += stride; + dstptr += stride; + } + } + } /* end of else (_mmx_supported) */ + + break; + } /* end 48 bpp */ + + case 64: /* png_ptr->row_info.pixel_depth */ + { + png_bytep srcptr; + png_bytep dstptr; + register png_uint_32 i; + png_uint_32 initial_val = BPP8 * png_pass_start[png_ptr->pass]; + /* png.c: png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */ + register int stride = BPP8 * png_pass_inc[png_ptr->pass]; + /* png.c: png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */ + register int rep_bytes = BPP8 * png_pass_width[png_ptr->pass]; + /* png.c: png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */ + png_uint_32 len = png_ptr->width &~7; /* reduce to mult. of 8 */ + int diff = (int) (png_ptr->width & 7); /* amount lost */ + register png_uint_32 final_val = BPP8 * len; /* GRR bugfix */ + + srcptr = png_ptr->row_buf + 1 + initial_val; + dstptr = row + initial_val; + + for (i = initial_val; i < final_val; i += stride) + { + png_memcpy(dstptr, srcptr, rep_bytes); + srcptr += stride; + dstptr += stride; + } + if (diff) /* number of leftover pixels: 3 for pngtest */ + { + final_val+=diff*BPP8; + for (; i < final_val; i += stride) + { + if (rep_bytes > (int)(final_val-i)) + rep_bytes = (int)(final_val-i); + png_memcpy(dstptr, srcptr, rep_bytes); + srcptr += stride; + dstptr += stride; + } + } + + break; + } /* end 64 bpp */ + + default: /* png_ptr->row_info.pixel_depth != 1,2,4,8,16,24,32,48,64 */ + { + /* this should never happen */ + png_warning(png_ptr, "Invalid row_info.pixel_depth in pnggccrd"); + break; + } + } /* end switch (png_ptr->row_info.pixel_depth) */ + + } /* end if (non-trivial mask) */ + +} /* end png_combine_row() */ + +#endif /* PNG_HAVE_ASSEMBLER_COMBINE_ROW */ + + + + +/*===========================================================================*/ +/* */ +/* P N G _ D O _ R E A D _ I N T E R L A C E */ +/* */ +/*===========================================================================*/ + +#if defined(PNG_READ_INTERLACING_SUPPORTED) +#if defined(PNG_HAVE_ASSEMBLER_READ_INTERLACE) + +/* png_do_read_interlace() is called after any 16-bit to 8-bit conversion + * has taken place. [GRR: what other steps come before and/or after?] + */ + +void /* PRIVATE */ +png_do_read_interlace(png_structp png_ptr) +{ + png_row_infop row_info = &(png_ptr->row_info); + png_bytep row = png_ptr->row_buf + 1; + int pass = png_ptr->pass; +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + png_uint_32 transformations = png_ptr->transformations; +#endif + + png_debug(1, "in png_do_read_interlace (pnggccrd.c)\n"); + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) + if (_mmx_supported == 2) { +#if !defined(PNG_1_0_X) + /* this should have happened in png_init_mmx_flags() already */ + png_warning(png_ptr, "asm_flags may not have been initialized"); +#endif + png_mmx_support(); + } +#endif + + if (row != NULL && row_info != NULL) + { + png_uint_32 final_width; + + final_width = row_info->width * png_pass_inc[pass]; + + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp, dp; + int sshift, dshift; + int s_start, s_end, s_inc; + png_byte v; + png_uint_32 i; + int j; + + sp = row + (png_size_t)((row_info->width - 1) >> 3); + dp = row + (png_size_t)((final_width - 1) >> 3); +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (int)((row_info->width + 7) & 7); + dshift = (int)((final_width + 7) & 7); + s_start = 7; + s_end = 0; + s_inc = -1; + } + else +#endif + { + sshift = 7 - (int)((row_info->width + 7) & 7); + dshift = 7 - (int)((final_width + 7) & 7); + s_start = 0; + s_end = 7; + s_inc = 1; + } + + for (i = row_info->width; i; i--) + { + v = (png_byte)((*sp >> sshift) & 0x1); + for (j = 0; j < png_pass_inc[pass]; j++) + { + *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + + case 2: + { + png_bytep sp, dp; + int sshift, dshift; + int s_start, s_end, s_inc; + png_uint_32 i; + + sp = row + (png_size_t)((row_info->width - 1) >> 2); + dp = row + (png_size_t)((final_width - 1) >> 2); +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (png_size_t)(((row_info->width + 3) & 3) << 1); + dshift = (png_size_t)(((final_width + 3) & 3) << 1); + s_start = 6; + s_end = 0; + s_inc = -2; + } + else +#endif + { + sshift = (png_size_t)((3 - ((row_info->width + 3) & 3)) << 1); + dshift = (png_size_t)((3 - ((final_width + 3) & 3)) << 1); + s_start = 0; + s_end = 6; + s_inc = 2; + } + + for (i = row_info->width; i; i--) + { + png_byte v; + int j; + + v = (png_byte)((*sp >> sshift) & 0x3); + for (j = 0; j < png_pass_inc[pass]; j++) + { + *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + + case 4: + { + png_bytep sp, dp; + int sshift, dshift; + int s_start, s_end, s_inc; + png_uint_32 i; + + sp = row + (png_size_t)((row_info->width - 1) >> 1); + dp = row + (png_size_t)((final_width - 1) >> 1); +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (png_size_t)(((row_info->width + 1) & 1) << 2); + dshift = (png_size_t)(((final_width + 1) & 1) << 2); + s_start = 4; + s_end = 0; + s_inc = -4; + } + else +#endif + { + sshift = (png_size_t)((1 - ((row_info->width + 1) & 1)) << 2); + dshift = (png_size_t)((1 - ((final_width + 1) & 1)) << 2); + s_start = 0; + s_end = 4; + s_inc = 4; + } + + for (i = row_info->width; i; i--) + { + png_byte v; + int j; + + v = (png_byte)((*sp >> sshift) & 0xf); + for (j = 0; j < png_pass_inc[pass]; j++) + { + *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + + /*====================================================================*/ + + default: /* 8-bit or larger (this is where the routine is modified) */ + { +#if 0 +// static unsigned long long _const4 = 0x0000000000FFFFFFLL; no good +// static unsigned long long const4 = 0x0000000000FFFFFFLL; no good +// unsigned long long _const4 = 0x0000000000FFFFFFLL; no good +// unsigned long long const4 = 0x0000000000FFFFFFLL; no good +#endif + png_bytep sptr, dp; + png_uint_32 i; + png_size_t pixel_bytes; + int width = (int)row_info->width; + + pixel_bytes = (row_info->pixel_depth >> 3); + + /* point sptr at the last pixel in the pre-expanded row: */ + sptr = row + (width - 1) * pixel_bytes; + + /* point dp at the last pixel position in the expanded row: */ + dp = row + (final_width - 1) * pixel_bytes; + + /* New code by Nirav Chhatrapati - Intel Corporation */ + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_INTERLACE) + /* && _mmx_supported */ ) +#else + if (_mmx_supported) +#endif + { + //-------------------------------------------------------------- + if (pixel_bytes == 3) + { + if (((pass == 0) || (pass == 1)) && width) + { + int dummy_value_c; // fix 'forbidden register spilled' + int dummy_value_S; + int dummy_value_D; + + __asm__ __volatile__ ( + "subl $21, %%edi \n\t" + // (png_pass_inc[pass] - 1)*pixel_bytes + + ".loop3_pass0: \n\t" + "movd (%%esi), %%mm0 \n\t" // x x x x x 2 1 0 + "pand _const4, %%mm0 \n\t" // z z z z z 2 1 0 + "movq %%mm0, %%mm1 \n\t" // z z z z z 2 1 0 + "psllq $16, %%mm0 \n\t" // z z z 2 1 0 z z + "movq %%mm0, %%mm2 \n\t" // z z z 2 1 0 z z + "psllq $24, %%mm0 \n\t" // 2 1 0 z z z z z + "psrlq $8, %%mm1 \n\t" // z z z z z z 2 1 + "por %%mm2, %%mm0 \n\t" // 2 1 0 2 1 0 z z + "por %%mm1, %%mm0 \n\t" // 2 1 0 2 1 0 2 1 + "movq %%mm0, %%mm3 \n\t" // 2 1 0 2 1 0 2 1 + "psllq $16, %%mm0 \n\t" // 0 2 1 0 2 1 z z + "movq %%mm3, %%mm4 \n\t" // 2 1 0 2 1 0 2 1 + "punpckhdq %%mm0, %%mm3 \n\t" // 0 2 1 0 2 1 0 2 + "movq %%mm4, 16(%%edi) \n\t" + "psrlq $32, %%mm0 \n\t" // z z z z 0 2 1 0 + "movq %%mm3, 8(%%edi) \n\t" + "punpckldq %%mm4, %%mm0 \n\t" // 1 0 2 1 0 2 1 0 + "subl $3, %%esi \n\t" + "movq %%mm0, (%%edi) \n\t" + "subl $24, %%edi \n\t" + "decl %%ecx \n\t" + "jnz .loop3_pass0 \n\t" + "EMMS \n\t" // DONE + + : "=c" (dummy_value_c), // output regs (dummy) + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "1" (sptr), // esi // input regs + "2" (dp), // edi + "0" (width), // ecx + "rim" (_const4) // %1(?) (0x0000000000FFFFFFLL) + +#if 0 /* %mm0, ..., %mm4 not supported by gcc 2.7.2.3 or egcs 1.1 */ + : "%mm0", "%mm1", "%mm2" // clobber list + , "%mm3", "%mm4" +#endif + ); + } + else if (((pass == 2) || (pass == 3)) && width) + { + int dummy_value_c; // fix 'forbidden register spilled' + int dummy_value_S; + int dummy_value_D; + + __asm__ __volatile__ ( + "subl $9, %%edi \n\t" + // (png_pass_inc[pass] - 1)*pixel_bytes + + ".loop3_pass2: \n\t" + "movd (%%esi), %%mm0 \n\t" // x x x x x 2 1 0 + "pand _const4, %%mm0 \n\t" // z z z z z 2 1 0 + "movq %%mm0, %%mm1 \n\t" // z z z z z 2 1 0 + "psllq $16, %%mm0 \n\t" // z z z 2 1 0 z z + "movq %%mm0, %%mm2 \n\t" // z z z 2 1 0 z z + "psllq $24, %%mm0 \n\t" // 2 1 0 z z z z z + "psrlq $8, %%mm1 \n\t" // z z z z z z 2 1 + "por %%mm2, %%mm0 \n\t" // 2 1 0 2 1 0 z z + "por %%mm1, %%mm0 \n\t" // 2 1 0 2 1 0 2 1 + "movq %%mm0, 4(%%edi) \n\t" + "psrlq $16, %%mm0 \n\t" // z z 2 1 0 2 1 0 + "subl $3, %%esi \n\t" + "movd %%mm0, (%%edi) \n\t" + "subl $12, %%edi \n\t" + "decl %%ecx \n\t" + "jnz .loop3_pass2 \n\t" + "EMMS \n\t" // DONE + + : "=c" (dummy_value_c), // output regs (dummy) + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "1" (sptr), // esi // input regs + "2" (dp), // edi + "0" (width), // ecx + "rim" (_const4) // (0x0000000000FFFFFFLL) + +#if 0 /* %mm0, ..., %mm2 not supported by gcc 2.7.2.3 or egcs 1.1 */ + : "%mm0", "%mm1", "%mm2" // clobber list +#endif + ); + } + else if (width) /* && ((pass == 4) || (pass == 5)) */ + { + int width_mmx = ((width >> 1) << 1) - 8; // GRR: huh? + if (width_mmx < 0) + width_mmx = 0; + width -= width_mmx; // 8 or 9 pix, 24 or 27 bytes + if (width_mmx) + { + // png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; + // sptr points at last pixel in pre-expanded row + // dp points at last pixel position in expanded row + int dummy_value_c; // fix 'forbidden register spilled' + int dummy_value_S; + int dummy_value_D; + + __asm__ __volatile__ ( + "subl $3, %%esi \n\t" + "subl $9, %%edi \n\t" + // (png_pass_inc[pass] + 1)*pixel_bytes + + ".loop3_pass4: \n\t" + "movq (%%esi), %%mm0 \n\t" // x x 5 4 3 2 1 0 + "movq %%mm0, %%mm1 \n\t" // x x 5 4 3 2 1 0 + "movq %%mm0, %%mm2 \n\t" // x x 5 4 3 2 1 0 + "psllq $24, %%mm0 \n\t" // 4 3 2 1 0 z z z + "pand _const4, %%mm1 \n\t" // z z z z z 2 1 0 + "psrlq $24, %%mm2 \n\t" // z z z x x 5 4 3 + "por %%mm1, %%mm0 \n\t" // 4 3 2 1 0 2 1 0 + "movq %%mm2, %%mm3 \n\t" // z z z x x 5 4 3 + "psllq $8, %%mm2 \n\t" // z z x x 5 4 3 z + "movq %%mm0, (%%edi) \n\t" + "psrlq $16, %%mm3 \n\t" // z z z z z x x 5 + "pand _const6, %%mm3 \n\t" // z z z z z z z 5 + "por %%mm3, %%mm2 \n\t" // z z x x 5 4 3 5 + "subl $6, %%esi \n\t" + "movd %%mm2, 8(%%edi) \n\t" + "subl $12, %%edi \n\t" + "subl $2, %%ecx \n\t" + "jnz .loop3_pass4 \n\t" + "EMMS \n\t" // DONE + + : "=c" (dummy_value_c), // output regs (dummy) + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "1" (sptr), // esi // input regs + "2" (dp), // edi + "0" (width_mmx), // ecx + "rim" (_const4), // 0x0000000000FFFFFFLL + "rim" (_const6) // 0x00000000000000FFLL + +#if 0 /* %mm0, ..., %mm3 not supported by gcc 2.7.2.3 or egcs 1.1 */ + : "%mm0", "%mm1" // clobber list + , "%mm2", "%mm3" +#endif + ); + } + + sptr -= width_mmx*3; + dp -= width_mmx*6; + for (i = width; i; i--) + { + png_byte v[8]; + int j; + + png_memcpy(v, sptr, 3); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, 3); + dp -= 3; + } + sptr -= 3; + } + } + } /* end of pixel_bytes == 3 */ + + //-------------------------------------------------------------- + else if (pixel_bytes == 1) + { + if (((pass == 0) || (pass == 1)) && width) + { + int width_mmx = ((width >> 2) << 2); + width -= width_mmx; // 0-3 pixels => 0-3 bytes + if (width_mmx) + { + int dummy_value_c; // fix 'forbidden register spilled' + int dummy_value_S; + int dummy_value_D; + + __asm__ __volatile__ ( + "subl $3, %%esi \n\t" + "subl $31, %%edi \n\t" + + ".loop1_pass0: \n\t" + "movd (%%esi), %%mm0 \n\t" // x x x x 3 2 1 0 + "movq %%mm0, %%mm1 \n\t" // x x x x 3 2 1 0 + "punpcklbw %%mm0, %%mm0 \n\t" // 3 3 2 2 1 1 0 0 + "movq %%mm0, %%mm2 \n\t" // 3 3 2 2 1 1 0 0 + "punpcklwd %%mm0, %%mm0 \n\t" // 1 1 1 1 0 0 0 0 + "movq %%mm0, %%mm3 \n\t" // 1 1 1 1 0 0 0 0 + "punpckldq %%mm0, %%mm0 \n\t" // 0 0 0 0 0 0 0 0 + "punpckhdq %%mm3, %%mm3 \n\t" // 1 1 1 1 1 1 1 1 + "movq %%mm0, (%%edi) \n\t" + "punpckhwd %%mm2, %%mm2 \n\t" // 3 3 3 3 2 2 2 2 + "movq %%mm3, 8(%%edi) \n\t" + "movq %%mm2, %%mm4 \n\t" // 3 3 3 3 2 2 2 2 + "punpckldq %%mm2, %%mm2 \n\t" // 2 2 2 2 2 2 2 2 + "punpckhdq %%mm4, %%mm4 \n\t" // 3 3 3 3 3 3 3 3 + "movq %%mm2, 16(%%edi) \n\t" + "subl $4, %%esi \n\t" + "movq %%mm4, 24(%%edi) \n\t" + "subl $32, %%edi \n\t" + "subl $4, %%ecx \n\t" + "jnz .loop1_pass0 \n\t" + "EMMS \n\t" // DONE + + : "=c" (dummy_value_c), // output regs (dummy) + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "1" (sptr), // esi // input regs + "2" (dp), // edi + "0" (width_mmx) // ecx + +#if 0 /* %mm0, ..., %mm4 not supported by gcc 2.7.2.3 or egcs 1.1 */ + : "%mm0", "%mm1", "%mm2" // clobber list + , "%mm3", "%mm4" +#endif + ); + } + + sptr -= width_mmx; + dp -= width_mmx*8; + for (i = width; i; i--) + { + int j; + + /* I simplified this part in version 1.0.4e + * here and in several other instances where + * pixel_bytes == 1 -- GR-P + * + * Original code: + * + * png_byte v[8]; + * png_memcpy(v, sptr, pixel_bytes); + * for (j = 0; j < png_pass_inc[pass]; j++) + * { + * png_memcpy(dp, v, pixel_bytes); + * dp -= pixel_bytes; + * } + * sptr -= pixel_bytes; + * + * Replacement code is in the next three lines: + */ + + for (j = 0; j < png_pass_inc[pass]; j++) + { + *dp-- = *sptr; + } + --sptr; + } + } + else if (((pass == 2) || (pass == 3)) && width) + { + int width_mmx = ((width >> 2) << 2); + width -= width_mmx; // 0-3 pixels => 0-3 bytes + if (width_mmx) + { + int dummy_value_c; // fix 'forbidden register spilled' + int dummy_value_S; + int dummy_value_D; + + __asm__ __volatile__ ( + "subl $3, %%esi \n\t" + "subl $15, %%edi \n\t" + + ".loop1_pass2: \n\t" + "movd (%%esi), %%mm0 \n\t" // x x x x 3 2 1 0 + "punpcklbw %%mm0, %%mm0 \n\t" // 3 3 2 2 1 1 0 0 + "movq %%mm0, %%mm1 \n\t" // 3 3 2 2 1 1 0 0 + "punpcklwd %%mm0, %%mm0 \n\t" // 1 1 1 1 0 0 0 0 + "punpckhwd %%mm1, %%mm1 \n\t" // 3 3 3 3 2 2 2 2 + "movq %%mm0, (%%edi) \n\t" + "subl $4, %%esi \n\t" + "movq %%mm1, 8(%%edi) \n\t" + "subl $16, %%edi \n\t" + "subl $4, %%ecx \n\t" + "jnz .loop1_pass2 \n\t" + "EMMS \n\t" // DONE + + : "=c" (dummy_value_c), // output regs (dummy) + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "1" (sptr), // esi // input regs + "2" (dp), // edi + "0" (width_mmx) // ecx + +#if 0 /* %mm0, %mm1 not supported by gcc 2.7.2.3 or egcs 1.1 */ + : "%mm0", "%mm1" // clobber list +#endif + ); + } + + sptr -= width_mmx; + dp -= width_mmx*4; + for (i = width; i; i--) + { + int j; + + for (j = 0; j < png_pass_inc[pass]; j++) + { + *dp-- = *sptr; + } + --sptr; + } + } + else if (width) /* && ((pass == 4) || (pass == 5)) */ + { + int width_mmx = ((width >> 3) << 3); + width -= width_mmx; // 0-3 pixels => 0-3 bytes + if (width_mmx) + { + int dummy_value_c; // fix 'forbidden register spilled' + int dummy_value_S; + int dummy_value_D; + + __asm__ __volatile__ ( + "subl $7, %%esi \n\t" + "subl $15, %%edi \n\t" + + ".loop1_pass4: \n\t" + "movq (%%esi), %%mm0 \n\t" // 7 6 5 4 3 2 1 0 + "movq %%mm0, %%mm1 \n\t" // 7 6 5 4 3 2 1 0 + "punpcklbw %%mm0, %%mm0 \n\t" // 3 3 2 2 1 1 0 0 + "punpckhbw %%mm1, %%mm1 \n\t" // 7 7 6 6 5 5 4 4 + "movq %%mm1, 8(%%edi) \n\t" + "subl $8, %%esi \n\t" + "movq %%mm0, (%%edi) \n\t" + "subl $16, %%edi \n\t" + "subl $8, %%ecx \n\t" + "jnz .loop1_pass4 \n\t" + "EMMS \n\t" // DONE + + : "=c" (dummy_value_c), // output regs (none) + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "1" (sptr), // esi // input regs + "2" (dp), // edi + "0" (width_mmx) // ecx + +#if 0 /* %mm0, %mm1 not supported by gcc 2.7.2.3 or egcs 1.1 */ + : "%mm0", "%mm1" // clobber list +#endif + ); + } + + sptr -= width_mmx; + dp -= width_mmx*2; + for (i = width; i; i--) + { + int j; + + for (j = 0; j < png_pass_inc[pass]; j++) + { + *dp-- = *sptr; + } + --sptr; + } + } + } /* end of pixel_bytes == 1 */ + + //-------------------------------------------------------------- + else if (pixel_bytes == 2) + { + if (((pass == 0) || (pass == 1)) && width) + { + int width_mmx = ((width >> 1) << 1); + width -= width_mmx; // 0,1 pixels => 0,2 bytes + if (width_mmx) + { + int dummy_value_c; // fix 'forbidden register spilled' + int dummy_value_S; + int dummy_value_D; + + __asm__ __volatile__ ( + "subl $2, %%esi \n\t" + "subl $30, %%edi \n\t" + + ".loop2_pass0: \n\t" + "movd (%%esi), %%mm0 \n\t" // x x x x 3 2 1 0 + "punpcklwd %%mm0, %%mm0 \n\t" // 3 2 3 2 1 0 1 0 + "movq %%mm0, %%mm1 \n\t" // 3 2 3 2 1 0 1 0 + "punpckldq %%mm0, %%mm0 \n\t" // 1 0 1 0 1 0 1 0 + "punpckhdq %%mm1, %%mm1 \n\t" // 3 2 3 2 3 2 3 2 + "movq %%mm0, (%%edi) \n\t" + "movq %%mm0, 8(%%edi) \n\t" + "movq %%mm1, 16(%%edi) \n\t" + "subl $4, %%esi \n\t" + "movq %%mm1, 24(%%edi) \n\t" + "subl $32, %%edi \n\t" + "subl $2, %%ecx \n\t" + "jnz .loop2_pass0 \n\t" + "EMMS \n\t" // DONE + + : "=c" (dummy_value_c), // output regs (dummy) + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "1" (sptr), // esi // input regs + "2" (dp), // edi + "0" (width_mmx) // ecx + +#if 0 /* %mm0, %mm1 not supported by gcc 2.7.2.3 or egcs 1.1 */ + : "%mm0", "%mm1" // clobber list +#endif + ); + } + + sptr -= (width_mmx*2 - 2); // sign fixed + dp -= (width_mmx*16 - 2); // sign fixed + for (i = width; i; i--) + { + png_byte v[8]; + int j; + sptr -= 2; + png_memcpy(v, sptr, 2); + for (j = 0; j < png_pass_inc[pass]; j++) + { + dp -= 2; + png_memcpy(dp, v, 2); + } + } + } + else if (((pass == 2) || (pass == 3)) && width) + { + int width_mmx = ((width >> 1) << 1) ; + width -= width_mmx; // 0,1 pixels => 0,2 bytes + if (width_mmx) + { + int dummy_value_c; // fix 'forbidden register spilled' + int dummy_value_S; + int dummy_value_D; + + __asm__ __volatile__ ( + "subl $2, %%esi \n\t" + "subl $14, %%edi \n\t" + + ".loop2_pass2: \n\t" + "movd (%%esi), %%mm0 \n\t" // x x x x 3 2 1 0 + "punpcklwd %%mm0, %%mm0 \n\t" // 3 2 3 2 1 0 1 0 + "movq %%mm0, %%mm1 \n\t" // 3 2 3 2 1 0 1 0 + "punpckldq %%mm0, %%mm0 \n\t" // 1 0 1 0 1 0 1 0 + "punpckhdq %%mm1, %%mm1 \n\t" // 3 2 3 2 3 2 3 2 + "movq %%mm0, (%%edi) \n\t" + "subl $4, %%esi \n\t" + "movq %%mm1, 8(%%edi) \n\t" + "subl $16, %%edi \n\t" + "subl $2, %%ecx \n\t" + "jnz .loop2_pass2 \n\t" + "EMMS \n\t" // DONE + + : "=c" (dummy_value_c), // output regs (dummy) + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "1" (sptr), // esi // input regs + "2" (dp), // edi + "0" (width_mmx) // ecx + +#if 0 /* %mm0, %mm1 not supported by gcc 2.7.2.3 or egcs 1.1 */ + : "%mm0", "%mm1" // clobber list +#endif + ); + } + + sptr -= (width_mmx*2 - 2); // sign fixed + dp -= (width_mmx*8 - 2); // sign fixed + for (i = width; i; i--) + { + png_byte v[8]; + int j; + sptr -= 2; + png_memcpy(v, sptr, 2); + for (j = 0; j < png_pass_inc[pass]; j++) + { + dp -= 2; + png_memcpy(dp, v, 2); + } + } + } + else if (width) // pass == 4 or 5 + { + int width_mmx = ((width >> 1) << 1) ; + width -= width_mmx; // 0,1 pixels => 0,2 bytes + if (width_mmx) + { + int dummy_value_c; // fix 'forbidden register spilled' + int dummy_value_S; + int dummy_value_D; + + __asm__ __volatile__ ( + "subl $2, %%esi \n\t" + "subl $6, %%edi \n\t" + + ".loop2_pass4: \n\t" + "movd (%%esi), %%mm0 \n\t" // x x x x 3 2 1 0 + "punpcklwd %%mm0, %%mm0 \n\t" // 3 2 3 2 1 0 1 0 + "subl $4, %%esi \n\t" + "movq %%mm0, (%%edi) \n\t" + "subl $8, %%edi \n\t" + "subl $2, %%ecx \n\t" + "jnz .loop2_pass4 \n\t" + "EMMS \n\t" // DONE + + : "=c" (dummy_value_c), // output regs (dummy) + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "1" (sptr), // esi // input regs + "2" (dp), // edi + "0" (width_mmx) // ecx + +#if 0 /* %mm0 not supported by gcc 2.7.2.3 or egcs 1.1 */ + : "%mm0" // clobber list +#endif + ); + } + + sptr -= (width_mmx*2 - 2); // sign fixed + dp -= (width_mmx*4 - 2); // sign fixed + for (i = width; i; i--) + { + png_byte v[8]; + int j; + sptr -= 2; + png_memcpy(v, sptr, 2); + for (j = 0; j < png_pass_inc[pass]; j++) + { + dp -= 2; + png_memcpy(dp, v, 2); + } + } + } + } /* end of pixel_bytes == 2 */ + + //-------------------------------------------------------------- + else if (pixel_bytes == 4) + { + if (((pass == 0) || (pass == 1)) && width) + { + int width_mmx = ((width >> 1) << 1); + width -= width_mmx; // 0,1 pixels => 0,4 bytes + if (width_mmx) + { + int dummy_value_c; // fix 'forbidden register spilled' + int dummy_value_S; + int dummy_value_D; + + __asm__ __volatile__ ( + "subl $4, %%esi \n\t" + "subl $60, %%edi \n\t" + + ".loop4_pass0: \n\t" + "movq (%%esi), %%mm0 \n\t" // 7 6 5 4 3 2 1 0 + "movq %%mm0, %%mm1 \n\t" // 7 6 5 4 3 2 1 0 + "punpckldq %%mm0, %%mm0 \n\t" // 3 2 1 0 3 2 1 0 + "punpckhdq %%mm1, %%mm1 \n\t" // 7 6 5 4 7 6 5 4 + "movq %%mm0, (%%edi) \n\t" + "movq %%mm0, 8(%%edi) \n\t" + "movq %%mm0, 16(%%edi) \n\t" + "movq %%mm0, 24(%%edi) \n\t" + "movq %%mm1, 32(%%edi) \n\t" + "movq %%mm1, 40(%%edi) \n\t" + "movq %%mm1, 48(%%edi) \n\t" + "subl $8, %%esi \n\t" + "movq %%mm1, 56(%%edi) \n\t" + "subl $64, %%edi \n\t" + "subl $2, %%ecx \n\t" + "jnz .loop4_pass0 \n\t" + "EMMS \n\t" // DONE + + : "=c" (dummy_value_c), // output regs (dummy) + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "1" (sptr), // esi // input regs + "2" (dp), // edi + "0" (width_mmx) // ecx + +#if 0 /* %mm0, %mm1 not supported by gcc 2.7.2.3 or egcs 1.1 */ + : "%mm0", "%mm1" // clobber list +#endif + ); + } + + sptr -= (width_mmx*4 - 4); // sign fixed + dp -= (width_mmx*32 - 4); // sign fixed + for (i = width; i; i--) + { + png_byte v[8]; + int j; + sptr -= 4; + png_memcpy(v, sptr, 4); + for (j = 0; j < png_pass_inc[pass]; j++) + { + dp -= 4; + png_memcpy(dp, v, 4); + } + } + } + else if (((pass == 2) || (pass == 3)) && width) + { + int width_mmx = ((width >> 1) << 1); + width -= width_mmx; // 0,1 pixels => 0,4 bytes + if (width_mmx) + { + int dummy_value_c; // fix 'forbidden register spilled' + int dummy_value_S; + int dummy_value_D; + + __asm__ __volatile__ ( + "subl $4, %%esi \n\t" + "subl $28, %%edi \n\t" + + ".loop4_pass2: \n\t" + "movq (%%esi), %%mm0 \n\t" // 7 6 5 4 3 2 1 0 + "movq %%mm0, %%mm1 \n\t" // 7 6 5 4 3 2 1 0 + "punpckldq %%mm0, %%mm0 \n\t" // 3 2 1 0 3 2 1 0 + "punpckhdq %%mm1, %%mm1 \n\t" // 7 6 5 4 7 6 5 4 + "movq %%mm0, (%%edi) \n\t" + "movq %%mm0, 8(%%edi) \n\t" + "movq %%mm1, 16(%%edi) \n\t" + "movq %%mm1, 24(%%edi) \n\t" + "subl $8, %%esi \n\t" + "subl $32, %%edi \n\t" + "subl $2, %%ecx \n\t" + "jnz .loop4_pass2 \n\t" + "EMMS \n\t" // DONE + + : "=c" (dummy_value_c), // output regs (dummy) + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "1" (sptr), // esi // input regs + "2" (dp), // edi + "0" (width_mmx) // ecx + +#if 0 /* %mm0, %mm1 not supported by gcc 2.7.2.3 or egcs 1.1 */ + : "%mm0", "%mm1" // clobber list +#endif + ); + } + + sptr -= (width_mmx*4 - 4); // sign fixed + dp -= (width_mmx*16 - 4); // sign fixed + for (i = width; i; i--) + { + png_byte v[8]; + int j; + sptr -= 4; + png_memcpy(v, sptr, 4); + for (j = 0; j < png_pass_inc[pass]; j++) + { + dp -= 4; + png_memcpy(dp, v, 4); + } + } + } + else if (width) // pass == 4 or 5 + { + int width_mmx = ((width >> 1) << 1) ; + width -= width_mmx; // 0,1 pixels => 0,4 bytes + if (width_mmx) + { + int dummy_value_c; // fix 'forbidden register spilled' + int dummy_value_S; + int dummy_value_D; + + __asm__ __volatile__ ( + "subl $4, %%esi \n\t" + "subl $12, %%edi \n\t" + + ".loop4_pass4: \n\t" + "movq (%%esi), %%mm0 \n\t" // 7 6 5 4 3 2 1 0 + "movq %%mm0, %%mm1 \n\t" // 7 6 5 4 3 2 1 0 + "punpckldq %%mm0, %%mm0 \n\t" // 3 2 1 0 3 2 1 0 + "punpckhdq %%mm1, %%mm1 \n\t" // 7 6 5 4 7 6 5 4 + "movq %%mm0, (%%edi) \n\t" + "subl $8, %%esi \n\t" + "movq %%mm1, 8(%%edi) \n\t" + "subl $16, %%edi \n\t" + "subl $2, %%ecx \n\t" + "jnz .loop4_pass4 \n\t" + "EMMS \n\t" // DONE + + : "=c" (dummy_value_c), // output regs (dummy) + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "1" (sptr), // esi // input regs + "2" (dp), // edi + "0" (width_mmx) // ecx + +#if 0 /* %mm0, %mm1 not supported by gcc 2.7.2.3 or egcs 1.1 */ + : "%mm0", "%mm1" // clobber list +#endif + ); + } + + sptr -= (width_mmx*4 - 4); // sign fixed + dp -= (width_mmx*8 - 4); // sign fixed + for (i = width; i; i--) + { + png_byte v[8]; + int j; + sptr -= 4; + png_memcpy(v, sptr, 4); + for (j = 0; j < png_pass_inc[pass]; j++) + { + dp -= 4; + png_memcpy(dp, v, 4); + } + } + } + } /* end of pixel_bytes == 4 */ + + //-------------------------------------------------------------- + else if (pixel_bytes == 8) + { +// GRR TEST: should work, but needs testing (special 64-bit version of rpng2?) + // GRR NOTE: no need to combine passes here! + if (((pass == 0) || (pass == 1)) && width) + { + int dummy_value_c; // fix 'forbidden register spilled' + int dummy_value_S; + int dummy_value_D; + + // source is 8-byte RRGGBBAA + // dest is 64-byte RRGGBBAA RRGGBBAA RRGGBBAA RRGGBBAA ... + __asm__ __volatile__ ( + "subl $56, %%edi \n\t" // start of last block + + ".loop8_pass0: \n\t" + "movq (%%esi), %%mm0 \n\t" // 7 6 5 4 3 2 1 0 + "movq %%mm0, (%%edi) \n\t" + "movq %%mm0, 8(%%edi) \n\t" + "movq %%mm0, 16(%%edi) \n\t" + "movq %%mm0, 24(%%edi) \n\t" + "movq %%mm0, 32(%%edi) \n\t" + "movq %%mm0, 40(%%edi) \n\t" + "movq %%mm0, 48(%%edi) \n\t" + "subl $8, %%esi \n\t" + "movq %%mm0, 56(%%edi) \n\t" + "subl $64, %%edi \n\t" + "decl %%ecx \n\t" + "jnz .loop8_pass0 \n\t" + "EMMS \n\t" // DONE + + : "=c" (dummy_value_c), // output regs (dummy) + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "1" (sptr), // esi // input regs + "2" (dp), // edi + "0" (width) // ecx + +#if 0 /* %mm0 not supported by gcc 2.7.2.3 or egcs 1.1 */ + : "%mm0" // clobber list +#endif + ); + } + else if (((pass == 2) || (pass == 3)) && width) + { + // source is 8-byte RRGGBBAA + // dest is 32-byte RRGGBBAA RRGGBBAA RRGGBBAA RRGGBBAA + // (recall that expansion is _in place_: sptr and dp + // both point at locations within same row buffer) + { + int dummy_value_c; // fix 'forbidden register spilled' + int dummy_value_S; + int dummy_value_D; + + __asm__ __volatile__ ( + "subl $24, %%edi \n\t" // start of last block + + ".loop8_pass2: \n\t" + "movq (%%esi), %%mm0 \n\t" // 7 6 5 4 3 2 1 0 + "movq %%mm0, (%%edi) \n\t" + "movq %%mm0, 8(%%edi) \n\t" + "movq %%mm0, 16(%%edi) \n\t" + "subl $8, %%esi \n\t" + "movq %%mm0, 24(%%edi) \n\t" + "subl $32, %%edi \n\t" + "decl %%ecx \n\t" + "jnz .loop8_pass2 \n\t" + "EMMS \n\t" // DONE + + : "=c" (dummy_value_c), // output regs (dummy) + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "1" (sptr), // esi // input regs + "2" (dp), // edi + "0" (width) // ecx + +#if 0 /* %mm0 not supported by gcc 2.7.2.3 or egcs 1.1 */ + : "%mm0" // clobber list +#endif + ); + } + } + else if (width) // pass == 4 or 5 + { + // source is 8-byte RRGGBBAA + // dest is 16-byte RRGGBBAA RRGGBBAA + { + int dummy_value_c; // fix 'forbidden register spilled' + int dummy_value_S; + int dummy_value_D; + + __asm__ __volatile__ ( + "subl $8, %%edi \n\t" // start of last block + + ".loop8_pass4: \n\t" + "movq (%%esi), %%mm0 \n\t" // 7 6 5 4 3 2 1 0 + "movq %%mm0, (%%edi) \n\t" + "subl $8, %%esi \n\t" + "movq %%mm0, 8(%%edi) \n\t" + "subl $16, %%edi \n\t" + "decl %%ecx \n\t" + "jnz .loop8_pass4 \n\t" + "EMMS \n\t" // DONE + + : "=c" (dummy_value_c), // output regs (dummy) + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "1" (sptr), // esi // input regs + "2" (dp), // edi + "0" (width) // ecx + +#if 0 /* %mm0 not supported by gcc 2.7.2.3 or egcs 1.1 */ + : "%mm0" // clobber list +#endif + ); + } + } + + } /* end of pixel_bytes == 8 */ + + //-------------------------------------------------------------- + else if (pixel_bytes == 6) + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, 6); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, 6); + dp -= 6; + } + sptr -= 6; + } + } /* end of pixel_bytes == 6 */ + + //-------------------------------------------------------------- + else + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, pixel_bytes); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + sptr-= pixel_bytes; + } + } + } // end of _mmx_supported ======================================== + + else /* MMX not supported: use modified C code - takes advantage + * of inlining of png_memcpy for a constant */ + /* GRR 19991007: does it? or should pixel_bytes in each + * block be replaced with immediate value (e.g., 1)? */ + /* GRR 19991017: replaced with constants in each case */ +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + { + if (pixel_bytes == 1) + { + for (i = width; i; i--) + { + int j; + for (j = 0; j < png_pass_inc[pass]; j++) + { + *dp-- = *sptr; + } + --sptr; + } + } + else if (pixel_bytes == 3) + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, 3); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, 3); + dp -= 3; + } + sptr -= 3; + } + } + else if (pixel_bytes == 2) + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, 2); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, 2); + dp -= 2; + } + sptr -= 2; + } + } + else if (pixel_bytes == 4) + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, 4); + for (j = 0; j < png_pass_inc[pass]; j++) + { +#ifdef PNG_DEBUG + if (dp < row || dp+3 > row+png_ptr->row_buf_size) + { + printf("dp out of bounds: row=%d, dp=%d, rp=%d\n", + row, dp, row+png_ptr->row_buf_size); + printf("row_buf=%d\n",png_ptr->row_buf_size); + } +#endif + png_memcpy(dp, v, 4); + dp -= 4; + } + sptr -= 4; + } + } + else if (pixel_bytes == 6) + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, 6); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, 6); + dp -= 6; + } + sptr -= 6; + } + } + else if (pixel_bytes == 8) + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, 8); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, 8); + dp -= 8; + } + sptr -= 8; + } + } + else /* GRR: should never be reached */ + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, pixel_bytes); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + sptr -= pixel_bytes; + } + } + + } /* end if (MMX not supported) */ + break; + } + } /* end switch (row_info->pixel_depth) */ + + row_info->width = final_width; + + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,final_width); + } + +} /* end png_do_read_interlace() */ + +#endif /* PNG_HAVE_ASSEMBLER_READ_INTERLACE */ +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + + + +#if defined(PNG_HAVE_ASSEMBLER_READ_FILTER_ROW) +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) + +// These variables are utilized in the functions below. They are declared +// globally here to ensure alignment on 8-byte boundaries. + +union uAll { + long long use; + double align; +} _LBCarryMask = {0x0101010101010101LL}, + _HBClearMask = {0x7f7f7f7f7f7f7f7fLL}, + _ActiveMask, _ActiveMask2, _ActiveMaskEnd, _ShiftBpp, _ShiftRem; + +#ifdef PNG_THREAD_UNSAFE_OK +//===========================================================================// +// // +// P N G _ R E A D _ F I L T E R _ R O W _ M M X _ A V G // +// // +//===========================================================================// + +// Optimized code for PNG Average filter decoder + +static void /* PRIVATE */ +png_read_filter_row_mmx_avg(png_row_infop row_info, png_bytep row, + png_bytep prev_row) +{ + int bpp; + int dummy_value_c; // fix 'forbidden register 2 (cx) was spilled' error + int dummy_value_S; + int dummy_value_D; + + bpp = (row_info->pixel_depth + 7) >> 3; // get # bytes per pixel + _FullLength = row_info->rowbytes; // # of bytes to filter + + __asm__ __volatile__ ( + // initialize address pointers and offset +#ifdef __PIC__ + "pushl %%ebx \n\t" // save index to Global Offset Table +#endif +//pre "movl row, %%edi \n\t" // edi: Avg(x) + "xorl %%ebx, %%ebx \n\t" // ebx: x + "movl %%edi, %%edx \n\t" +//pre "movl prev_row, %%esi \n\t" // esi: Prior(x) +//pre "subl bpp, %%edx \n\t" // (bpp is preloaded into ecx) + "subl %%ecx, %%edx \n\t" // edx: Raw(x-bpp) + + "xorl %%eax,%%eax \n\t" + + // Compute the Raw value for the first bpp bytes + // Raw(x) = Avg(x) + (Prior(x)/2) + "avg_rlp: \n\t" + "movb (%%esi,%%ebx,),%%al \n\t" // load al with Prior(x) + "incl %%ebx \n\t" + "shrb %%al \n\t" // divide by 2 + "addb -1(%%edi,%%ebx,),%%al \n\t" // add Avg(x); -1 to offset inc ebx +//pre "cmpl bpp, %%ebx \n\t" // (bpp is preloaded into ecx) + "cmpl %%ecx, %%ebx \n\t" + "movb %%al,-1(%%edi,%%ebx,) \n\t" // write Raw(x); -1 to offset inc ebx + "jb avg_rlp \n\t" // mov does not affect flags + + // get # of bytes to alignment + "movl %%edi, _dif \n\t" // take start of row + "addl %%ebx, _dif \n\t" // add bpp + "addl $0xf, _dif \n\t" // add 7+8 to incr past alignment bdry + "andl $0xfffffff8, _dif \n\t" // mask to alignment boundary + "subl %%edi, _dif \n\t" // subtract from start => value ebx at + "jz avg_go \n\t" // alignment + + // fix alignment + // Compute the Raw value for the bytes up to the alignment boundary + // Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2) + "xorl %%ecx, %%ecx \n\t" + + "avg_lp1: \n\t" + "xorl %%eax, %%eax \n\t" + "movb (%%esi,%%ebx,), %%cl \n\t" // load cl with Prior(x) + "movb (%%edx,%%ebx,), %%al \n\t" // load al with Raw(x-bpp) + "addw %%cx, %%ax \n\t" + "incl %%ebx \n\t" + "shrw %%ax \n\t" // divide by 2 + "addb -1(%%edi,%%ebx,), %%al \n\t" // add Avg(x); -1 to offset inc ebx + "cmpl _dif, %%ebx \n\t" // check if at alignment boundary + "movb %%al, -1(%%edi,%%ebx,) \n\t" // write Raw(x); -1 to offset inc ebx + "jb avg_lp1 \n\t" // repeat until at alignment boundary + + "avg_go: \n\t" + "movl _FullLength, %%eax \n\t" + "movl %%eax, %%ecx \n\t" + "subl %%ebx, %%eax \n\t" // subtract alignment fix + "andl $0x00000007, %%eax \n\t" // calc bytes over mult of 8 + "subl %%eax, %%ecx \n\t" // drop over bytes from original length + "movl %%ecx, _MMXLength \n\t" +#ifdef __PIC__ + "popl %%ebx \n\t" // restore index to Global Offset Table +#endif + + : "=c" (dummy_value_c), // output regs (dummy) + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "0" (bpp), // ecx // input regs + "1" (prev_row), // esi + "2" (row) // edi + + : "%eax", "%edx" // clobber list +#ifndef __PIC__ + , "%ebx" +#endif + // GRR: INCLUDE "memory" as clobbered? (_dif, _MMXLength) + // (seems to work fine without...) + ); + + // now do the math for the rest of the row + switch (bpp) + { + case 3: + { + _ActiveMask.use = 0x0000000000ffffffLL; + _ShiftBpp.use = 24; // == 3 * 8 + _ShiftRem.use = 40; // == 64 - 24 + + __asm__ __volatile__ ( + // re-init address pointers and offset + "movq _ActiveMask, %%mm7 \n\t" + "movl _dif, %%ecx \n\t" // ecx: x = offset to + "movq _LBCarryMask, %%mm5 \n\t" // alignment boundary +// preload "movl row, %%edi \n\t" // edi: Avg(x) + "movq _HBClearMask, %%mm4 \n\t" +// preload "movl prev_row, %%esi \n\t" // esi: Prior(x) + + // prime the pump: load the first Raw(x-bpp) data set + "movq -8(%%edi,%%ecx,), %%mm2 \n\t" // load previous aligned 8 bytes + // (correct pos. in loop below) + "avg_3lp: \n\t" + "movq (%%edi,%%ecx,), %%mm0 \n\t" // load mm0 with Avg(x) + "movq %%mm5, %%mm3 \n\t" + "psrlq _ShiftRem, %%mm2 \n\t" // correct position Raw(x-bpp) + // data + "movq (%%esi,%%ecx,), %%mm1 \n\t" // load mm1 with Prior(x) + "movq %%mm7, %%mm6 \n\t" + "pand %%mm1, %%mm3 \n\t" // get lsb for each prev_row byte + "psrlq $1, %%mm1 \n\t" // divide prev_row bytes by 2 + "pand %%mm4, %%mm1 \n\t" // clear invalid bit 7 of each + // byte + "paddb %%mm1, %%mm0 \n\t" // add (Prev_row/2) to Avg for + // each byte + // add 1st active group (Raw(x-bpp)/2) to average with LBCarry + "movq %%mm3, %%mm1 \n\t" // now use mm1 for getting + // LBCarrys + "pand %%mm2, %%mm1 \n\t" // get LBCarrys for each byte + // where both + // lsb's were == 1 (only valid for active group) + "psrlq $1, %%mm2 \n\t" // divide raw bytes by 2 + "pand %%mm4, %%mm2 \n\t" // clear invalid bit 7 of each + // byte + "paddb %%mm1, %%mm2 \n\t" // add LBCarrys to (Raw(x-bpp)/2) + // for each byte + "pand %%mm6, %%mm2 \n\t" // leave only Active Group 1 + // bytes to add to Avg + "paddb %%mm2, %%mm0 \n\t" // add (Raw/2) + LBCarrys to + // Avg for each Active + // byte + // add 2nd active group (Raw(x-bpp)/2) to average with _LBCarry + "psllq _ShiftBpp, %%mm6 \n\t" // shift the mm6 mask to cover + // bytes 3-5 + "movq %%mm0, %%mm2 \n\t" // mov updated Raws to mm2 + "psllq _ShiftBpp, %%mm2 \n\t" // shift data to pos. correctly + "movq %%mm3, %%mm1 \n\t" // now use mm1 for getting + // LBCarrys + "pand %%mm2, %%mm1 \n\t" // get LBCarrys for each byte + // where both + // lsb's were == 1 (only valid for active group) + "psrlq $1, %%mm2 \n\t" // divide raw bytes by 2 + "pand %%mm4, %%mm2 \n\t" // clear invalid bit 7 of each + // byte + "paddb %%mm1, %%mm2 \n\t" // add LBCarrys to (Raw(x-bpp)/2) + // for each byte + "pand %%mm6, %%mm2 \n\t" // leave only Active Group 2 + // bytes to add to Avg + "paddb %%mm2, %%mm0 \n\t" // add (Raw/2) + LBCarrys to + // Avg for each Active + // byte + + // add 3rd active group (Raw(x-bpp)/2) to average with _LBCarry + "psllq _ShiftBpp, %%mm6 \n\t" // shift mm6 mask to cover last + // two + // bytes + "movq %%mm0, %%mm2 \n\t" // mov updated Raws to mm2 + "psllq _ShiftBpp, %%mm2 \n\t" // shift data to pos. correctly + // Data only needs to be shifted once here to + // get the correct x-bpp offset. + "movq %%mm3, %%mm1 \n\t" // now use mm1 for getting + // LBCarrys + "pand %%mm2, %%mm1 \n\t" // get LBCarrys for each byte + // where both + // lsb's were == 1 (only valid for active group) + "psrlq $1, %%mm2 \n\t" // divide raw bytes by 2 + "pand %%mm4, %%mm2 \n\t" // clear invalid bit 7 of each + // byte + "paddb %%mm1, %%mm2 \n\t" // add LBCarrys to (Raw(x-bpp)/2) + // for each byte + "pand %%mm6, %%mm2 \n\t" // leave only Active Group 2 + // bytes to add to Avg + "addl $8, %%ecx \n\t" + "paddb %%mm2, %%mm0 \n\t" // add (Raw/2) + LBCarrys to + // Avg for each Active + // byte + // now ready to write back to memory + "movq %%mm0, -8(%%edi,%%ecx,) \n\t" + // move updated Raw(x) to use as Raw(x-bpp) for next loop + "cmpl _MMXLength, %%ecx \n\t" + "movq %%mm0, %%mm2 \n\t" // mov updated Raw(x) to mm2 + "jb avg_3lp \n\t" + + : "=S" (dummy_value_S), // output regs (dummy) + "=D" (dummy_value_D) + + : "0" (prev_row), // esi // input regs + "1" (row) // edi + + : "%ecx" // clobber list +#if 0 /* %mm0, ..., %mm7 not supported by gcc 2.7.2.3 or egcs 1.1 */ + , "%mm0", "%mm1", "%mm2", "%mm3" + , "%mm4", "%mm5", "%mm6", "%mm7" +#endif + ); + } + break; // end 3 bpp + + case 6: + case 4: + //case 7: // who wrote this? PNG doesn't support 5 or 7 bytes/pixel + //case 5: // GRR BOGUS + { + _ActiveMask.use = 0xffffffffffffffffLL; // use shift below to clear + // appropriate inactive bytes + _ShiftBpp.use = bpp << 3; + _ShiftRem.use = 64 - _ShiftBpp.use; + + __asm__ __volatile__ ( + "movq _HBClearMask, %%mm4 \n\t" + + // re-init address pointers and offset + "movl _dif, %%ecx \n\t" // ecx: x = offset to + // alignment boundary + + // load _ActiveMask and clear all bytes except for 1st active group + "movq _ActiveMask, %%mm7 \n\t" +// preload "movl row, %%edi \n\t" // edi: Avg(x) + "psrlq _ShiftRem, %%mm7 \n\t" +// preload "movl prev_row, %%esi \n\t" // esi: Prior(x) + "movq %%mm7, %%mm6 \n\t" + "movq _LBCarryMask, %%mm5 \n\t" + "psllq _ShiftBpp, %%mm6 \n\t" // create mask for 2nd active + // group + + // prime the pump: load the first Raw(x-bpp) data set + "movq -8(%%edi,%%ecx,), %%mm2 \n\t" // load previous aligned 8 bytes + // (we correct pos. in loop below) + "avg_4lp: \n\t" + "movq (%%edi,%%ecx,), %%mm0 \n\t" + "psrlq _ShiftRem, %%mm2 \n\t" // shift data to pos. correctly + "movq (%%esi,%%ecx,), %%mm1 \n\t" + // add (Prev_row/2) to average + "movq %%mm5, %%mm3 \n\t" + "pand %%mm1, %%mm3 \n\t" // get lsb for each prev_row byte + "psrlq $1, %%mm1 \n\t" // divide prev_row bytes by 2 + "pand %%mm4, %%mm1 \n\t" // clear invalid bit 7 of each + // byte + "paddb %%mm1, %%mm0 \n\t" // add (Prev_row/2) to Avg for + // each byte + // add 1st active group (Raw(x-bpp)/2) to average with _LBCarry + "movq %%mm3, %%mm1 \n\t" // now use mm1 for getting + // LBCarrys + "pand %%mm2, %%mm1 \n\t" // get LBCarrys for each byte + // where both + // lsb's were == 1 (only valid for active group) + "psrlq $1, %%mm2 \n\t" // divide raw bytes by 2 + "pand %%mm4, %%mm2 \n\t" // clear invalid bit 7 of each + // byte + "paddb %%mm1, %%mm2 \n\t" // add LBCarrys to (Raw(x-bpp)/2) + // for each byte + "pand %%mm7, %%mm2 \n\t" // leave only Active Group 1 + // bytes to add to Avg + "paddb %%mm2, %%mm0 \n\t" // add (Raw/2) + LBCarrys to Avg + // for each Active + // byte + // add 2nd active group (Raw(x-bpp)/2) to average with _LBCarry + "movq %%mm0, %%mm2 \n\t" // mov updated Raws to mm2 + "psllq _ShiftBpp, %%mm2 \n\t" // shift data to pos. correctly + "addl $8, %%ecx \n\t" + "movq %%mm3, %%mm1 \n\t" // now use mm1 for getting + // LBCarrys + "pand %%mm2, %%mm1 \n\t" // get LBCarrys for each byte + // where both + // lsb's were == 1 (only valid for active group) + "psrlq $1, %%mm2 \n\t" // divide raw bytes by 2 + "pand %%mm4, %%mm2 \n\t" // clear invalid bit 7 of each + // byte + "paddb %%mm1, %%mm2 \n\t" // add LBCarrys to (Raw(x-bpp)/2) + // for each byte + "pand %%mm6, %%mm2 \n\t" // leave only Active Group 2 + // bytes to add to Avg + "paddb %%mm2, %%mm0 \n\t" // add (Raw/2) + LBCarrys to + // Avg for each Active + // byte + "cmpl _MMXLength, %%ecx \n\t" + // now ready to write back to memory + "movq %%mm0, -8(%%edi,%%ecx,) \n\t" + // prep Raw(x-bpp) for next loop + "movq %%mm0, %%mm2 \n\t" // mov updated Raws to mm2 + "jb avg_4lp \n\t" + + : "=S" (dummy_value_S), // output regs (dummy) + "=D" (dummy_value_D) + + : "0" (prev_row), // esi // input regs + "1" (row) // edi + + : "%ecx" // clobber list +#if 0 /* %mm0, ..., %mm7 not supported by gcc 2.7.2.3 or egcs 1.1 */ + , "%mm0", "%mm1", "%mm2", "%mm3" + , "%mm4", "%mm5", "%mm6", "%mm7" +#endif + ); + } + break; // end 4,6 bpp + + case 2: + { + _ActiveMask.use = 0x000000000000ffffLL; + _ShiftBpp.use = 16; // == 2 * 8 + _ShiftRem.use = 48; // == 64 - 16 + + __asm__ __volatile__ ( + // load _ActiveMask + "movq _ActiveMask, %%mm7 \n\t" + // re-init address pointers and offset + "movl _dif, %%ecx \n\t" // ecx: x = offset to alignment + // boundary + "movq _LBCarryMask, %%mm5 \n\t" +// preload "movl row, %%edi \n\t" // edi: Avg(x) + "movq _HBClearMask, %%mm4 \n\t" +// preload "movl prev_row, %%esi \n\t" // esi: Prior(x) + + // prime the pump: load the first Raw(x-bpp) data set + "movq -8(%%edi,%%ecx,), %%mm2 \n\t" // load previous aligned 8 bytes + // (we correct pos. in loop below) + "avg_2lp: \n\t" + "movq (%%edi,%%ecx,), %%mm0 \n\t" + "psrlq _ShiftRem, %%mm2 \n\t" // shift data to pos. correctly + "movq (%%esi,%%ecx,), %%mm1 \n\t" // (GRR BUGFIX: was psllq) + // add (Prev_row/2) to average + "movq %%mm5, %%mm3 \n\t" + "pand %%mm1, %%mm3 \n\t" // get lsb for each prev_row byte + "psrlq $1, %%mm1 \n\t" // divide prev_row bytes by 2 + "pand %%mm4, %%mm1 \n\t" // clear invalid bit 7 of each + // byte + "movq %%mm7, %%mm6 \n\t" + "paddb %%mm1, %%mm0 \n\t" // add (Prev_row/2) to Avg for + // each byte + + // add 1st active group (Raw(x-bpp)/2) to average with _LBCarry + "movq %%mm3, %%mm1 \n\t" // now use mm1 for getting + // LBCarrys + "pand %%mm2, %%mm1 \n\t" // get LBCarrys for each byte + // where both + // lsb's were == 1 (only valid + // for active group) + "psrlq $1, %%mm2 \n\t" // divide raw bytes by 2 + "pand %%mm4, %%mm2 \n\t" // clear invalid bit 7 of each + // byte + "paddb %%mm1, %%mm2 \n\t" // add LBCarrys to (Raw(x-bpp)/2) + // for each byte + "pand %%mm6, %%mm2 \n\t" // leave only Active Group 1 + // bytes to add to Avg + "paddb %%mm2, %%mm0 \n\t" // add (Raw/2) + LBCarrys to Avg + // for each Active byte + + // add 2nd active group (Raw(x-bpp)/2) to average with _LBCarry + "psllq _ShiftBpp, %%mm6 \n\t" // shift the mm6 mask to cover + // bytes 2 & 3 + "movq %%mm0, %%mm2 \n\t" // mov updated Raws to mm2 + "psllq _ShiftBpp, %%mm2 \n\t" // shift data to pos. correctly + "movq %%mm3, %%mm1 \n\t" // now use mm1 for getting + // LBCarrys + "pand %%mm2, %%mm1 \n\t" // get LBCarrys for each byte + // where both + // lsb's were == 1 (only valid + // for active group) + "psrlq $1, %%mm2 \n\t" // divide raw bytes by 2 + "pand %%mm4, %%mm2 \n\t" // clear invalid bit 7 of each + // byte + "paddb %%mm1, %%mm2 \n\t" // add LBCarrys to (Raw(x-bpp)/2) + // for each byte + "pand %%mm6, %%mm2 \n\t" // leave only Active Group 2 + // bytes to add to Avg + "paddb %%mm2, %%mm0 \n\t" // add (Raw/2) + LBCarrys to + // Avg for each Active byte + + // add 3rd active group (Raw(x-bpp)/2) to average with _LBCarry + "psllq _ShiftBpp, %%mm6 \n\t" // shift the mm6 mask to cover + // bytes 4 & 5 + "movq %%mm0, %%mm2 \n\t" // mov updated Raws to mm2 + "psllq _ShiftBpp, %%mm2 \n\t" // shift data to pos. correctly + "movq %%mm3, %%mm1 \n\t" // now use mm1 for getting + // LBCarrys + "pand %%mm2, %%mm1 \n\t" // get LBCarrys for each byte + // where both lsb's were == 1 + // (only valid for active group) + "psrlq $1, %%mm2 \n\t" // divide raw bytes by 2 + "pand %%mm4, %%mm2 \n\t" // clear invalid bit 7 of each + // byte + "paddb %%mm1, %%mm2 \n\t" // add LBCarrys to (Raw(x-bpp)/2) + // for each byte + "pand %%mm6, %%mm2 \n\t" // leave only Active Group 2 + // bytes to add to Avg + "paddb %%mm2, %%mm0 \n\t" // add (Raw/2) + LBCarrys to + // Avg for each Active byte + + // add 4th active group (Raw(x-bpp)/2) to average with _LBCarry + "psllq _ShiftBpp, %%mm6 \n\t" // shift the mm6 mask to cover + // bytes 6 & 7 + "movq %%mm0, %%mm2 \n\t" // mov updated Raws to mm2 + "psllq _ShiftBpp, %%mm2 \n\t" // shift data to pos. correctly + "addl $8, %%ecx \n\t" + "movq %%mm3, %%mm1 \n\t" // now use mm1 for getting + // LBCarrys + "pand %%mm2, %%mm1 \n\t" // get LBCarrys for each byte + // where both + // lsb's were == 1 (only valid + // for active group) + "psrlq $1, %%mm2 \n\t" // divide raw bytes by 2 + "pand %%mm4, %%mm2 \n\t" // clear invalid bit 7 of each + // byte + "paddb %%mm1, %%mm2 \n\t" // add LBCarrys to (Raw(x-bpp)/2) + // for each byte + "pand %%mm6, %%mm2 \n\t" // leave only Active Group 2 + // bytes to add to Avg + "paddb %%mm2, %%mm0 \n\t" // add (Raw/2) + LBCarrys to + // Avg for each Active byte + + "cmpl _MMXLength, %%ecx \n\t" + // now ready to write back to memory + "movq %%mm0, -8(%%edi,%%ecx,) \n\t" + // prep Raw(x-bpp) for next loop + "movq %%mm0, %%mm2 \n\t" // mov updated Raws to mm2 + "jb avg_2lp \n\t" + + : "=S" (dummy_value_S), // output regs (dummy) + "=D" (dummy_value_D) + + : "0" (prev_row), // esi // input regs + "1" (row) // edi + + : "%ecx" // clobber list +#if 0 /* %mm0, ..., %mm7 not supported by gcc 2.7.2.3 or egcs 1.1 */ + , "%mm0", "%mm1", "%mm2", "%mm3" + , "%mm4", "%mm5", "%mm6", "%mm7" +#endif + ); + } + break; // end 2 bpp + + case 1: + { + __asm__ __volatile__ ( + // re-init address pointers and offset +#ifdef __PIC__ + "pushl %%ebx \n\t" // save Global Offset Table index +#endif + "movl _dif, %%ebx \n\t" // ebx: x = offset to alignment + // boundary +// preload "movl row, %%edi \n\t" // edi: Avg(x) + "cmpl _FullLength, %%ebx \n\t" // test if offset at end of array + "jnb avg_1end \n\t" + // do Paeth decode for remaining bytes +// preload "movl prev_row, %%esi \n\t" // esi: Prior(x) + "movl %%edi, %%edx \n\t" +// preload "subl bpp, %%edx \n\t" // (bpp is preloaded into ecx) + "subl %%ecx, %%edx \n\t" // edx: Raw(x-bpp) + "xorl %%ecx, %%ecx \n\t" // zero ecx before using cl & cx + // in loop below + "avg_1lp: \n\t" + // Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2) + "xorl %%eax, %%eax \n\t" + "movb (%%esi,%%ebx,), %%cl \n\t" // load cl with Prior(x) + "movb (%%edx,%%ebx,), %%al \n\t" // load al with Raw(x-bpp) + "addw %%cx, %%ax \n\t" + "incl %%ebx \n\t" + "shrw %%ax \n\t" // divide by 2 + "addb -1(%%edi,%%ebx,), %%al \n\t" // add Avg(x); -1 to offset + // inc ebx + "cmpl _FullLength, %%ebx \n\t" // check if at end of array + "movb %%al, -1(%%edi,%%ebx,) \n\t" // write back Raw(x); + // mov does not affect flags; -1 to offset inc ebx + "jb avg_1lp \n\t" + + "avg_1end: \n\t" +#ifdef __PIC__ + "popl %%ebx \n\t" // Global Offset Table index +#endif + + : "=c" (dummy_value_c), // output regs (dummy) + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "0" (bpp), // ecx // input regs + "1" (prev_row), // esi + "2" (row) // edi + + : "%eax", "%edx" // clobber list +#ifndef __PIC__ + , "%ebx" +#endif + ); + } + return; // end 1 bpp + + case 8: + { + __asm__ __volatile__ ( + // re-init address pointers and offset + "movl _dif, %%ecx \n\t" // ecx: x == offset to alignment + "movq _LBCarryMask, %%mm5 \n\t" // boundary +// preload "movl row, %%edi \n\t" // edi: Avg(x) + "movq _HBClearMask, %%mm4 \n\t" +// preload "movl prev_row, %%esi \n\t" // esi: Prior(x) + + // prime the pump: load the first Raw(x-bpp) data set + "movq -8(%%edi,%%ecx,), %%mm2 \n\t" // load previous aligned 8 bytes + // (NO NEED to correct pos. in loop below) + + "avg_8lp: \n\t" + "movq (%%edi,%%ecx,), %%mm0 \n\t" + "movq %%mm5, %%mm3 \n\t" + "movq (%%esi,%%ecx,), %%mm1 \n\t" + "addl $8, %%ecx \n\t" + "pand %%mm1, %%mm3 \n\t" // get lsb for each prev_row byte + "psrlq $1, %%mm1 \n\t" // divide prev_row bytes by 2 + "pand %%mm2, %%mm3 \n\t" // get LBCarrys for each byte + // where both lsb's were == 1 + "psrlq $1, %%mm2 \n\t" // divide raw bytes by 2 + "pand %%mm4, %%mm1 \n\t" // clear invalid bit 7, each byte + "paddb %%mm3, %%mm0 \n\t" // add LBCarrys to Avg, each byte + "pand %%mm4, %%mm2 \n\t" // clear invalid bit 7, each byte + "paddb %%mm1, %%mm0 \n\t" // add (Prev_row/2) to Avg, each + "paddb %%mm2, %%mm0 \n\t" // add (Raw/2) to Avg for each + "cmpl _MMXLength, %%ecx \n\t" + "movq %%mm0, -8(%%edi,%%ecx,) \n\t" + "movq %%mm0, %%mm2 \n\t" // reuse as Raw(x-bpp) + "jb avg_8lp \n\t" + + : "=S" (dummy_value_S), // output regs (dummy) + "=D" (dummy_value_D) + + : "0" (prev_row), // esi // input regs + "1" (row) // edi + + : "%ecx" // clobber list +#if 0 /* %mm0, ..., %mm5 not supported by gcc 2.7.2.3 or egcs 1.1 */ + , "%mm0", "%mm1", "%mm2" + , "%mm3", "%mm4", "%mm5" +#endif + ); + } + break; // end 8 bpp + + default: // bpp greater than 8 (!= 1,2,3,4,[5],6,[7],8) + { + +#ifdef PNG_DEBUG + // GRR: PRINT ERROR HERE: SHOULD NEVER BE REACHED + png_debug(1, + "Internal logic error in pnggccrd (png_read_filter_row_mmx_avg())\n"); +#endif + +#if 0 + __asm__ __volatile__ ( + "movq _LBCarryMask, %%mm5 \n\t" + // re-init address pointers and offset + "movl _dif, %%ebx \n\t" // ebx: x = offset to + // alignment boundary + "movl row, %%edi \n\t" // edi: Avg(x) + "movq _HBClearMask, %%mm4 \n\t" + "movl %%edi, %%edx \n\t" + "movl prev_row, %%esi \n\t" // esi: Prior(x) + "subl bpp, %%edx \n\t" // edx: Raw(x-bpp) + "avg_Alp: \n\t" + "movq (%%edi,%%ebx,), %%mm0 \n\t" + "movq %%mm5, %%mm3 \n\t" + "movq (%%esi,%%ebx,), %%mm1 \n\t" + "pand %%mm1, %%mm3 \n\t" // get lsb for each prev_row byte + "movq (%%edx,%%ebx,), %%mm2 \n\t" + "psrlq $1, %%mm1 \n\t" // divide prev_row bytes by 2 + "pand %%mm2, %%mm3 \n\t" // get LBCarrys for each byte + // where both lsb's were == 1 + "psrlq $1, %%mm2 \n\t" // divide raw bytes by 2 + "pand %%mm4, %%mm1 \n\t" // clear invalid bit 7 of each + // byte + "paddb %%mm3, %%mm0 \n\t" // add LBCarrys to Avg for each + // byte + "pand %%mm4, %%mm2 \n\t" // clear invalid bit 7 of each + // byte + "paddb %%mm1, %%mm0 \n\t" // add (Prev_row/2) to Avg for + // each byte + "addl $8, %%ebx \n\t" + "paddb %%mm2, %%mm0 \n\t" // add (Raw/2) to Avg for each + // byte + "cmpl _MMXLength, %%ebx \n\t" + "movq %%mm0, -8(%%edi,%%ebx,) \n\t" + "jb avg_Alp \n\t" + + : // FIXASM: output regs/vars go here, e.g.: "=m" (memory_var) + + : // FIXASM: input regs, e.g.: "c" (count), "S" (src), "D" (dest) + + : "%ebx", "%edx", "%edi", "%esi" // CHECKASM: clobber list + ); +#endif /* 0 - NEVER REACHED */ + } + break; + + } // end switch (bpp) + + __asm__ __volatile__ ( + // MMX acceleration complete; now do clean-up + // check if any remaining bytes left to decode +#ifdef __PIC__ + "pushl %%ebx \n\t" // save index to Global Offset Table +#endif + "movl _MMXLength, %%ebx \n\t" // ebx: x == offset bytes after MMX +//pre "movl row, %%edi \n\t" // edi: Avg(x) + "cmpl _FullLength, %%ebx \n\t" // test if offset at end of array + "jnb avg_end \n\t" + + // do Avg decode for remaining bytes +//pre "movl prev_row, %%esi \n\t" // esi: Prior(x) + "movl %%edi, %%edx \n\t" +//pre "subl bpp, %%edx \n\t" // (bpp is preloaded into ecx) + "subl %%ecx, %%edx \n\t" // edx: Raw(x-bpp) + "xorl %%ecx, %%ecx \n\t" // zero ecx before using cl & cx below + + "avg_lp2: \n\t" + // Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2) + "xorl %%eax, %%eax \n\t" + "movb (%%esi,%%ebx,), %%cl \n\t" // load cl with Prior(x) + "movb (%%edx,%%ebx,), %%al \n\t" // load al with Raw(x-bpp) + "addw %%cx, %%ax \n\t" + "incl %%ebx \n\t" + "shrw %%ax \n\t" // divide by 2 + "addb -1(%%edi,%%ebx,), %%al \n\t" // add Avg(x); -1 to offset inc ebx + "cmpl _FullLength, %%ebx \n\t" // check if at end of array + "movb %%al, -1(%%edi,%%ebx,) \n\t" // write back Raw(x) [mov does not + "jb avg_lp2 \n\t" // affect flags; -1 to offset inc ebx] + + "avg_end: \n\t" + "EMMS \n\t" // end MMX; prep for poss. FP instrs. +#ifdef __PIC__ + "popl %%ebx \n\t" // restore index to Global Offset Table +#endif + + : "=c" (dummy_value_c), // output regs (dummy) + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "0" (bpp), // ecx // input regs + "1" (prev_row), // esi + "2" (row) // edi + + : "%eax", "%edx" // clobber list +#ifndef __PIC__ + , "%ebx" +#endif + ); + +} /* end png_read_filter_row_mmx_avg() */ +#endif + + + +#ifdef PNG_THREAD_UNSAFE_OK +//===========================================================================// +// // +// P N G _ R E A D _ F I L T E R _ R O W _ M M X _ P A E T H // +// // +//===========================================================================// + +// Optimized code for PNG Paeth filter decoder + +static void /* PRIVATE */ +png_read_filter_row_mmx_paeth(png_row_infop row_info, png_bytep row, + png_bytep prev_row) +{ + int bpp; + int dummy_value_c; // fix 'forbidden register 2 (cx) was spilled' error + int dummy_value_S; + int dummy_value_D; + + bpp = (row_info->pixel_depth + 7) >> 3; // Get # bytes per pixel + _FullLength = row_info->rowbytes; // # of bytes to filter + + __asm__ __volatile__ ( +#ifdef __PIC__ + "pushl %%ebx \n\t" // save index to Global Offset Table +#endif + "xorl %%ebx, %%ebx \n\t" // ebx: x offset +//pre "movl row, %%edi \n\t" + "xorl %%edx, %%edx \n\t" // edx: x-bpp offset +//pre "movl prev_row, %%esi \n\t" + "xorl %%eax, %%eax \n\t" + + // Compute the Raw value for the first bpp bytes + // Note: the formula works out to be always + // Paeth(x) = Raw(x) + Prior(x) where x < bpp + "paeth_rlp: \n\t" + "movb (%%edi,%%ebx,), %%al \n\t" + "addb (%%esi,%%ebx,), %%al \n\t" + "incl %%ebx \n\t" +//pre "cmpl bpp, %%ebx \n\t" (bpp is preloaded into ecx) + "cmpl %%ecx, %%ebx \n\t" + "movb %%al, -1(%%edi,%%ebx,) \n\t" + "jb paeth_rlp \n\t" + // get # of bytes to alignment + "movl %%edi, _dif \n\t" // take start of row + "addl %%ebx, _dif \n\t" // add bpp + "xorl %%ecx, %%ecx \n\t" + "addl $0xf, _dif \n\t" // add 7 + 8 to incr past alignment + // boundary + "andl $0xfffffff8, _dif \n\t" // mask to alignment boundary + "subl %%edi, _dif \n\t" // subtract from start ==> value ebx + // at alignment + "jz paeth_go \n\t" + // fix alignment + + "paeth_lp1: \n\t" + "xorl %%eax, %%eax \n\t" + // pav = p - a = (a + b - c) - a = b - c + "movb (%%esi,%%ebx,), %%al \n\t" // load Prior(x) into al + "movb (%%esi,%%edx,), %%cl \n\t" // load Prior(x-bpp) into cl + "subl %%ecx, %%eax \n\t" // subtract Prior(x-bpp) + "movl %%eax, _patemp \n\t" // Save pav for later use + "xorl %%eax, %%eax \n\t" + // pbv = p - b = (a + b - c) - b = a - c + "movb (%%edi,%%edx,), %%al \n\t" // load Raw(x-bpp) into al + "subl %%ecx, %%eax \n\t" // subtract Prior(x-bpp) + "movl %%eax, %%ecx \n\t" + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + "addl _patemp, %%eax \n\t" // pcv = pav + pbv + // pc = abs(pcv) + "testl $0x80000000, %%eax \n\t" + "jz paeth_pca \n\t" + "negl %%eax \n\t" // reverse sign of neg values + + "paeth_pca: \n\t" + "movl %%eax, _pctemp \n\t" // save pc for later use + // pb = abs(pbv) + "testl $0x80000000, %%ecx \n\t" + "jz paeth_pba \n\t" + "negl %%ecx \n\t" // reverse sign of neg values + + "paeth_pba: \n\t" + "movl %%ecx, _pbtemp \n\t" // save pb for later use + // pa = abs(pav) + "movl _patemp, %%eax \n\t" + "testl $0x80000000, %%eax \n\t" + "jz paeth_paa \n\t" + "negl %%eax \n\t" // reverse sign of neg values + + "paeth_paa: \n\t" + "movl %%eax, _patemp \n\t" // save pa for later use + // test if pa <= pb + "cmpl %%ecx, %%eax \n\t" + "jna paeth_abb \n\t" + // pa > pb; now test if pb <= pc + "cmpl _pctemp, %%ecx \n\t" + "jna paeth_bbc \n\t" + // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp) + "movb (%%esi,%%edx,), %%cl \n\t" // load Prior(x-bpp) into cl + "jmp paeth_paeth \n\t" + + "paeth_bbc: \n\t" + // pb <= pc; Raw(x) = Paeth(x) + Prior(x) + "movb (%%esi,%%ebx,), %%cl \n\t" // load Prior(x) into cl + "jmp paeth_paeth \n\t" + + "paeth_abb: \n\t" + // pa <= pb; now test if pa <= pc + "cmpl _pctemp, %%eax \n\t" + "jna paeth_abc \n\t" + // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp) + "movb (%%esi,%%edx,), %%cl \n\t" // load Prior(x-bpp) into cl + "jmp paeth_paeth \n\t" + + "paeth_abc: \n\t" + // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp) + "movb (%%edi,%%edx,), %%cl \n\t" // load Raw(x-bpp) into cl + + "paeth_paeth: \n\t" + "incl %%ebx \n\t" + "incl %%edx \n\t" + // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256 + "addb %%cl, -1(%%edi,%%ebx,) \n\t" + "cmpl _dif, %%ebx \n\t" + "jb paeth_lp1 \n\t" + + "paeth_go: \n\t" + "movl _FullLength, %%ecx \n\t" + "movl %%ecx, %%eax \n\t" + "subl %%ebx, %%eax \n\t" // subtract alignment fix + "andl $0x00000007, %%eax \n\t" // calc bytes over mult of 8 + "subl %%eax, %%ecx \n\t" // drop over bytes from original length + "movl %%ecx, _MMXLength \n\t" +#ifdef __PIC__ + "popl %%ebx \n\t" // restore index to Global Offset Table +#endif + + : "=c" (dummy_value_c), // output regs (dummy) + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "0" (bpp), // ecx // input regs + "1" (prev_row), // esi + "2" (row) // edi + + : "%eax", "%edx" // clobber list +#ifndef __PIC__ + , "%ebx" +#endif + ); + + // now do the math for the rest of the row + switch (bpp) + { + case 3: + { + _ActiveMask.use = 0x0000000000ffffffLL; + _ActiveMaskEnd.use = 0xffff000000000000LL; + _ShiftBpp.use = 24; // == bpp(3) * 8 + _ShiftRem.use = 40; // == 64 - 24 + + __asm__ __volatile__ ( + "movl _dif, %%ecx \n\t" +// preload "movl row, %%edi \n\t" +// preload "movl prev_row, %%esi \n\t" + "pxor %%mm0, %%mm0 \n\t" + // prime the pump: load the first Raw(x-bpp) data set + "movq -8(%%edi,%%ecx,), %%mm1 \n\t" + "paeth_3lp: \n\t" + "psrlq _ShiftRem, %%mm1 \n\t" // shift last 3 bytes to 1st + // 3 bytes + "movq (%%esi,%%ecx,), %%mm2 \n\t" // load b=Prior(x) + "punpcklbw %%mm0, %%mm1 \n\t" // unpack High bytes of a + "movq -8(%%esi,%%ecx,), %%mm3 \n\t" // prep c=Prior(x-bpp) bytes + "punpcklbw %%mm0, %%mm2 \n\t" // unpack High bytes of b + "psrlq _ShiftRem, %%mm3 \n\t" // shift last 3 bytes to 1st + // 3 bytes + // pav = p - a = (a + b - c) - a = b - c + "movq %%mm2, %%mm4 \n\t" + "punpcklbw %%mm0, %%mm3 \n\t" // unpack High bytes of c + // pbv = p - b = (a + b - c) - b = a - c + "movq %%mm1, %%mm5 \n\t" + "psubw %%mm3, %%mm4 \n\t" + "pxor %%mm7, %%mm7 \n\t" + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + "movq %%mm4, %%mm6 \n\t" + "psubw %%mm3, %%mm5 \n\t" + + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + "pcmpgtw %%mm4, %%mm0 \n\t" // create mask pav bytes < 0 + "paddw %%mm5, %%mm6 \n\t" + "pand %%mm4, %%mm0 \n\t" // only pav bytes < 0 in mm7 + "pcmpgtw %%mm5, %%mm7 \n\t" // create mask pbv bytes < 0 + "psubw %%mm0, %%mm4 \n\t" + "pand %%mm5, %%mm7 \n\t" // only pbv bytes < 0 in mm0 + "psubw %%mm0, %%mm4 \n\t" + "psubw %%mm7, %%mm5 \n\t" + "pxor %%mm0, %%mm0 \n\t" + "pcmpgtw %%mm6, %%mm0 \n\t" // create mask pcv bytes < 0 + "pand %%mm6, %%mm0 \n\t" // only pav bytes < 0 in mm7 + "psubw %%mm7, %%mm5 \n\t" + "psubw %%mm0, %%mm6 \n\t" + // test pa <= pb + "movq %%mm4, %%mm7 \n\t" + "psubw %%mm0, %%mm6 \n\t" + "pcmpgtw %%mm5, %%mm7 \n\t" // pa > pb? + "movq %%mm7, %%mm0 \n\t" + // use mm7 mask to merge pa & pb + "pand %%mm7, %%mm5 \n\t" + // use mm0 mask copy to merge a & b + "pand %%mm0, %%mm2 \n\t" + "pandn %%mm4, %%mm7 \n\t" + "pandn %%mm1, %%mm0 \n\t" + "paddw %%mm5, %%mm7 \n\t" + "paddw %%mm2, %%mm0 \n\t" + // test ((pa <= pb)? pa:pb) <= pc + "pcmpgtw %%mm6, %%mm7 \n\t" // pab > pc? + "pxor %%mm1, %%mm1 \n\t" + "pand %%mm7, %%mm3 \n\t" + "pandn %%mm0, %%mm7 \n\t" + "paddw %%mm3, %%mm7 \n\t" + "pxor %%mm0, %%mm0 \n\t" + "packuswb %%mm1, %%mm7 \n\t" + "movq (%%esi,%%ecx,), %%mm3 \n\t" // load c=Prior(x-bpp) + "pand _ActiveMask, %%mm7 \n\t" + "movq %%mm3, %%mm2 \n\t" // load b=Prior(x) step 1 + "paddb (%%edi,%%ecx,), %%mm7 \n\t" // add Paeth predictor with Raw(x) + "punpcklbw %%mm0, %%mm3 \n\t" // unpack High bytes of c + "movq %%mm7, (%%edi,%%ecx,) \n\t" // write back updated value + "movq %%mm7, %%mm1 \n\t" // now mm1 will be used as + // Raw(x-bpp) + // now do Paeth for 2nd set of bytes (3-5) + "psrlq _ShiftBpp, %%mm2 \n\t" // load b=Prior(x) step 2 + "punpcklbw %%mm0, %%mm1 \n\t" // unpack High bytes of a + "pxor %%mm7, %%mm7 \n\t" + "punpcklbw %%mm0, %%mm2 \n\t" // unpack High bytes of b + // pbv = p - b = (a + b - c) - b = a - c + "movq %%mm1, %%mm5 \n\t" + // pav = p - a = (a + b - c) - a = b - c + "movq %%mm2, %%mm4 \n\t" + "psubw %%mm3, %%mm5 \n\t" + "psubw %%mm3, %%mm4 \n\t" + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = + // pav + pbv = pbv + pav + "movq %%mm5, %%mm6 \n\t" + "paddw %%mm4, %%mm6 \n\t" + + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + "pcmpgtw %%mm5, %%mm0 \n\t" // create mask pbv bytes < 0 + "pcmpgtw %%mm4, %%mm7 \n\t" // create mask pav bytes < 0 + "pand %%mm5, %%mm0 \n\t" // only pbv bytes < 0 in mm0 + "pand %%mm4, %%mm7 \n\t" // only pav bytes < 0 in mm7 + "psubw %%mm0, %%mm5 \n\t" + "psubw %%mm7, %%mm4 \n\t" + "psubw %%mm0, %%mm5 \n\t" + "psubw %%mm7, %%mm4 \n\t" + "pxor %%mm0, %%mm0 \n\t" + "pcmpgtw %%mm6, %%mm0 \n\t" // create mask pcv bytes < 0 + "pand %%mm6, %%mm0 \n\t" // only pav bytes < 0 in mm7 + "psubw %%mm0, %%mm6 \n\t" + // test pa <= pb + "movq %%mm4, %%mm7 \n\t" + "psubw %%mm0, %%mm6 \n\t" + "pcmpgtw %%mm5, %%mm7 \n\t" // pa > pb? + "movq %%mm7, %%mm0 \n\t" + // use mm7 mask to merge pa & pb + "pand %%mm7, %%mm5 \n\t" + // use mm0 mask copy to merge a & b + "pand %%mm0, %%mm2 \n\t" + "pandn %%mm4, %%mm7 \n\t" + "pandn %%mm1, %%mm0 \n\t" + "paddw %%mm5, %%mm7 \n\t" + "paddw %%mm2, %%mm0 \n\t" + // test ((pa <= pb)? pa:pb) <= pc + "pcmpgtw %%mm6, %%mm7 \n\t" // pab > pc? + "movq (%%esi,%%ecx,), %%mm2 \n\t" // load b=Prior(x) + "pand %%mm7, %%mm3 \n\t" + "pandn %%mm0, %%mm7 \n\t" + "pxor %%mm1, %%mm1 \n\t" + "paddw %%mm3, %%mm7 \n\t" + "pxor %%mm0, %%mm0 \n\t" + "packuswb %%mm1, %%mm7 \n\t" + "movq %%mm2, %%mm3 \n\t" // load c=Prior(x-bpp) step 1 + "pand _ActiveMask, %%mm7 \n\t" + "punpckhbw %%mm0, %%mm2 \n\t" // unpack High bytes of b + "psllq _ShiftBpp, %%mm7 \n\t" // shift bytes to 2nd group of + // 3 bytes + // pav = p - a = (a + b - c) - a = b - c + "movq %%mm2, %%mm4 \n\t" + "paddb (%%edi,%%ecx,), %%mm7 \n\t" // add Paeth predictor with Raw(x) + "psllq _ShiftBpp, %%mm3 \n\t" // load c=Prior(x-bpp) step 2 + "movq %%mm7, (%%edi,%%ecx,) \n\t" // write back updated value + "movq %%mm7, %%mm1 \n\t" + "punpckhbw %%mm0, %%mm3 \n\t" // unpack High bytes of c + "psllq _ShiftBpp, %%mm1 \n\t" // shift bytes + // now mm1 will be used as Raw(x-bpp) + // now do Paeth for 3rd, and final, set of bytes (6-7) + "pxor %%mm7, %%mm7 \n\t" + "punpckhbw %%mm0, %%mm1 \n\t" // unpack High bytes of a + "psubw %%mm3, %%mm4 \n\t" + // pbv = p - b = (a + b - c) - b = a - c + "movq %%mm1, %%mm5 \n\t" + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + "movq %%mm4, %%mm6 \n\t" + "psubw %%mm3, %%mm5 \n\t" + "pxor %%mm0, %%mm0 \n\t" + "paddw %%mm5, %%mm6 \n\t" + + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + "pcmpgtw %%mm4, %%mm0 \n\t" // create mask pav bytes < 0 + "pcmpgtw %%mm5, %%mm7 \n\t" // create mask pbv bytes < 0 + "pand %%mm4, %%mm0 \n\t" // only pav bytes < 0 in mm7 + "pand %%mm5, %%mm7 \n\t" // only pbv bytes < 0 in mm0 + "psubw %%mm0, %%mm4 \n\t" + "psubw %%mm7, %%mm5 \n\t" + "psubw %%mm0, %%mm4 \n\t" + "psubw %%mm7, %%mm5 \n\t" + "pxor %%mm0, %%mm0 \n\t" + "pcmpgtw %%mm6, %%mm0 \n\t" // create mask pcv bytes < 0 + "pand %%mm6, %%mm0 \n\t" // only pav bytes < 0 in mm7 + "psubw %%mm0, %%mm6 \n\t" + // test pa <= pb + "movq %%mm4, %%mm7 \n\t" + "psubw %%mm0, %%mm6 \n\t" + "pcmpgtw %%mm5, %%mm7 \n\t" // pa > pb? + "movq %%mm7, %%mm0 \n\t" + // use mm0 mask copy to merge a & b + "pand %%mm0, %%mm2 \n\t" + // use mm7 mask to merge pa & pb + "pand %%mm7, %%mm5 \n\t" + "pandn %%mm1, %%mm0 \n\t" + "pandn %%mm4, %%mm7 \n\t" + "paddw %%mm2, %%mm0 \n\t" + "paddw %%mm5, %%mm7 \n\t" + // test ((pa <= pb)? pa:pb) <= pc + "pcmpgtw %%mm6, %%mm7 \n\t" // pab > pc? + "pand %%mm7, %%mm3 \n\t" + "pandn %%mm0, %%mm7 \n\t" + "paddw %%mm3, %%mm7 \n\t" + "pxor %%mm1, %%mm1 \n\t" + "packuswb %%mm7, %%mm1 \n\t" + // step ecx to next set of 8 bytes and repeat loop til done + "addl $8, %%ecx \n\t" + "pand _ActiveMaskEnd, %%mm1 \n\t" + "paddb -8(%%edi,%%ecx,), %%mm1 \n\t" // add Paeth predictor with + // Raw(x) + + "cmpl _MMXLength, %%ecx \n\t" + "pxor %%mm0, %%mm0 \n\t" // pxor does not affect flags + "movq %%mm1, -8(%%edi,%%ecx,) \n\t" // write back updated value + // mm1 will be used as Raw(x-bpp) next loop + // mm3 ready to be used as Prior(x-bpp) next loop + "jb paeth_3lp \n\t" + + : "=S" (dummy_value_S), // output regs (dummy) + "=D" (dummy_value_D) + + : "0" (prev_row), // esi // input regs + "1" (row) // edi + + : "%ecx" // clobber list +#if 0 /* %mm0, ..., %mm7 not supported by gcc 2.7.2.3 or egcs 1.1 */ + , "%mm0", "%mm1", "%mm2", "%mm3" + , "%mm4", "%mm5", "%mm6", "%mm7" +#endif + ); + } + break; // end 3 bpp + + case 6: + //case 7: // GRR BOGUS + //case 5: // GRR BOGUS + { + _ActiveMask.use = 0x00000000ffffffffLL; + _ActiveMask2.use = 0xffffffff00000000LL; + _ShiftBpp.use = bpp << 3; // == bpp * 8 + _ShiftRem.use = 64 - _ShiftBpp.use; + + __asm__ __volatile__ ( + "movl _dif, %%ecx \n\t" +// preload "movl row, %%edi \n\t" +// preload "movl prev_row, %%esi \n\t" + // prime the pump: load the first Raw(x-bpp) data set + "movq -8(%%edi,%%ecx,), %%mm1 \n\t" + "pxor %%mm0, %%mm0 \n\t" + + "paeth_6lp: \n\t" + // must shift to position Raw(x-bpp) data + "psrlq _ShiftRem, %%mm1 \n\t" + // do first set of 4 bytes + "movq -8(%%esi,%%ecx,), %%mm3 \n\t" // read c=Prior(x-bpp) bytes + "punpcklbw %%mm0, %%mm1 \n\t" // unpack Low bytes of a + "movq (%%esi,%%ecx,), %%mm2 \n\t" // load b=Prior(x) + "punpcklbw %%mm0, %%mm2 \n\t" // unpack Low bytes of b + // must shift to position Prior(x-bpp) data + "psrlq _ShiftRem, %%mm3 \n\t" + // pav = p - a = (a + b - c) - a = b - c + "movq %%mm2, %%mm4 \n\t" + "punpcklbw %%mm0, %%mm3 \n\t" // unpack Low bytes of c + // pbv = p - b = (a + b - c) - b = a - c + "movq %%mm1, %%mm5 \n\t" + "psubw %%mm3, %%mm4 \n\t" + "pxor %%mm7, %%mm7 \n\t" + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + "movq %%mm4, %%mm6 \n\t" + "psubw %%mm3, %%mm5 \n\t" + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + "pcmpgtw %%mm4, %%mm0 \n\t" // create mask pav bytes < 0 + "paddw %%mm5, %%mm6 \n\t" + "pand %%mm4, %%mm0 \n\t" // only pav bytes < 0 in mm7 + "pcmpgtw %%mm5, %%mm7 \n\t" // create mask pbv bytes < 0 + "psubw %%mm0, %%mm4 \n\t" + "pand %%mm5, %%mm7 \n\t" // only pbv bytes < 0 in mm0 + "psubw %%mm0, %%mm4 \n\t" + "psubw %%mm7, %%mm5 \n\t" + "pxor %%mm0, %%mm0 \n\t" + "pcmpgtw %%mm6, %%mm0 \n\t" // create mask pcv bytes < 0 + "pand %%mm6, %%mm0 \n\t" // only pav bytes < 0 in mm7 + "psubw %%mm7, %%mm5 \n\t" + "psubw %%mm0, %%mm6 \n\t" + // test pa <= pb + "movq %%mm4, %%mm7 \n\t" + "psubw %%mm0, %%mm6 \n\t" + "pcmpgtw %%mm5, %%mm7 \n\t" // pa > pb? + "movq %%mm7, %%mm0 \n\t" + // use mm7 mask to merge pa & pb + "pand %%mm7, %%mm5 \n\t" + // use mm0 mask copy to merge a & b + "pand %%mm0, %%mm2 \n\t" + "pandn %%mm4, %%mm7 \n\t" + "pandn %%mm1, %%mm0 \n\t" + "paddw %%mm5, %%mm7 \n\t" + "paddw %%mm2, %%mm0 \n\t" + // test ((pa <= pb)? pa:pb) <= pc + "pcmpgtw %%mm6, %%mm7 \n\t" // pab > pc? + "pxor %%mm1, %%mm1 \n\t" + "pand %%mm7, %%mm3 \n\t" + "pandn %%mm0, %%mm7 \n\t" + "paddw %%mm3, %%mm7 \n\t" + "pxor %%mm0, %%mm0 \n\t" + "packuswb %%mm1, %%mm7 \n\t" + "movq -8(%%esi,%%ecx,), %%mm3 \n\t" // load c=Prior(x-bpp) + "pand _ActiveMask, %%mm7 \n\t" + "psrlq _ShiftRem, %%mm3 \n\t" + "movq (%%esi,%%ecx,), %%mm2 \n\t" // load b=Prior(x) step 1 + "paddb (%%edi,%%ecx,), %%mm7 \n\t" // add Paeth predictor and Raw(x) + "movq %%mm2, %%mm6 \n\t" + "movq %%mm7, (%%edi,%%ecx,) \n\t" // write back updated value + "movq -8(%%edi,%%ecx,), %%mm1 \n\t" + "psllq _ShiftBpp, %%mm6 \n\t" + "movq %%mm7, %%mm5 \n\t" + "psrlq _ShiftRem, %%mm1 \n\t" + "por %%mm6, %%mm3 \n\t" + "psllq _ShiftBpp, %%mm5 \n\t" + "punpckhbw %%mm0, %%mm3 \n\t" // unpack High bytes of c + "por %%mm5, %%mm1 \n\t" + // do second set of 4 bytes + "punpckhbw %%mm0, %%mm2 \n\t" // unpack High bytes of b + "punpckhbw %%mm0, %%mm1 \n\t" // unpack High bytes of a + // pav = p - a = (a + b - c) - a = b - c + "movq %%mm2, %%mm4 \n\t" + // pbv = p - b = (a + b - c) - b = a - c + "movq %%mm1, %%mm5 \n\t" + "psubw %%mm3, %%mm4 \n\t" + "pxor %%mm7, %%mm7 \n\t" + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + "movq %%mm4, %%mm6 \n\t" + "psubw %%mm3, %%mm5 \n\t" + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + "pcmpgtw %%mm4, %%mm0 \n\t" // create mask pav bytes < 0 + "paddw %%mm5, %%mm6 \n\t" + "pand %%mm4, %%mm0 \n\t" // only pav bytes < 0 in mm7 + "pcmpgtw %%mm5, %%mm7 \n\t" // create mask pbv bytes < 0 + "psubw %%mm0, %%mm4 \n\t" + "pand %%mm5, %%mm7 \n\t" // only pbv bytes < 0 in mm0 + "psubw %%mm0, %%mm4 \n\t" + "psubw %%mm7, %%mm5 \n\t" + "pxor %%mm0, %%mm0 \n\t" + "pcmpgtw %%mm6, %%mm0 \n\t" // create mask pcv bytes < 0 + "pand %%mm6, %%mm0 \n\t" // only pav bytes < 0 in mm7 + "psubw %%mm7, %%mm5 \n\t" + "psubw %%mm0, %%mm6 \n\t" + // test pa <= pb + "movq %%mm4, %%mm7 \n\t" + "psubw %%mm0, %%mm6 \n\t" + "pcmpgtw %%mm5, %%mm7 \n\t" // pa > pb? + "movq %%mm7, %%mm0 \n\t" + // use mm7 mask to merge pa & pb + "pand %%mm7, %%mm5 \n\t" + // use mm0 mask copy to merge a & b + "pand %%mm0, %%mm2 \n\t" + "pandn %%mm4, %%mm7 \n\t" + "pandn %%mm1, %%mm0 \n\t" + "paddw %%mm5, %%mm7 \n\t" + "paddw %%mm2, %%mm0 \n\t" + // test ((pa <= pb)? pa:pb) <= pc + "pcmpgtw %%mm6, %%mm7 \n\t" // pab > pc? + "pxor %%mm1, %%mm1 \n\t" + "pand %%mm7, %%mm3 \n\t" + "pandn %%mm0, %%mm7 \n\t" + "pxor %%mm1, %%mm1 \n\t" + "paddw %%mm3, %%mm7 \n\t" + "pxor %%mm0, %%mm0 \n\t" + // step ecx to next set of 8 bytes and repeat loop til done + "addl $8, %%ecx \n\t" + "packuswb %%mm7, %%mm1 \n\t" + "paddb -8(%%edi,%%ecx,), %%mm1 \n\t" // add Paeth predictor with Raw(x) + "cmpl _MMXLength, %%ecx \n\t" + "movq %%mm1, -8(%%edi,%%ecx,) \n\t" // write back updated value + // mm1 will be used as Raw(x-bpp) next loop + "jb paeth_6lp \n\t" + + : "=S" (dummy_value_S), // output regs (dummy) + "=D" (dummy_value_D) + + : "0" (prev_row), // esi // input regs + "1" (row) // edi + + : "%ecx" // clobber list +#if 0 /* %mm0, ..., %mm7 not supported by gcc 2.7.2.3 or egcs 1.1 */ + , "%mm0", "%mm1", "%mm2", "%mm3" + , "%mm4", "%mm5", "%mm6", "%mm7" +#endif + ); + } + break; // end 6 bpp + + case 4: + { + _ActiveMask.use = 0x00000000ffffffffLL; + + __asm__ __volatile__ ( + "movl _dif, %%ecx \n\t" +// preload "movl row, %%edi \n\t" +// preload "movl prev_row, %%esi \n\t" + "pxor %%mm0, %%mm0 \n\t" + // prime the pump: load the first Raw(x-bpp) data set + "movq -8(%%edi,%%ecx,), %%mm1 \n\t" // only time should need to read + // a=Raw(x-bpp) bytes + "paeth_4lp: \n\t" + // do first set of 4 bytes + "movq -8(%%esi,%%ecx,), %%mm3 \n\t" // read c=Prior(x-bpp) bytes + "punpckhbw %%mm0, %%mm1 \n\t" // unpack Low bytes of a + "movq (%%esi,%%ecx,), %%mm2 \n\t" // load b=Prior(x) + "punpcklbw %%mm0, %%mm2 \n\t" // unpack High bytes of b + // pav = p - a = (a + b - c) - a = b - c + "movq %%mm2, %%mm4 \n\t" + "punpckhbw %%mm0, %%mm3 \n\t" // unpack High bytes of c + // pbv = p - b = (a + b - c) - b = a - c + "movq %%mm1, %%mm5 \n\t" + "psubw %%mm3, %%mm4 \n\t" + "pxor %%mm7, %%mm7 \n\t" + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + "movq %%mm4, %%mm6 \n\t" + "psubw %%mm3, %%mm5 \n\t" + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + "pcmpgtw %%mm4, %%mm0 \n\t" // create mask pav bytes < 0 + "paddw %%mm5, %%mm6 \n\t" + "pand %%mm4, %%mm0 \n\t" // only pav bytes < 0 in mm7 + "pcmpgtw %%mm5, %%mm7 \n\t" // create mask pbv bytes < 0 + "psubw %%mm0, %%mm4 \n\t" + "pand %%mm5, %%mm7 \n\t" // only pbv bytes < 0 in mm0 + "psubw %%mm0, %%mm4 \n\t" + "psubw %%mm7, %%mm5 \n\t" + "pxor %%mm0, %%mm0 \n\t" + "pcmpgtw %%mm6, %%mm0 \n\t" // create mask pcv bytes < 0 + "pand %%mm6, %%mm0 \n\t" // only pav bytes < 0 in mm7 + "psubw %%mm7, %%mm5 \n\t" + "psubw %%mm0, %%mm6 \n\t" + // test pa <= pb + "movq %%mm4, %%mm7 \n\t" + "psubw %%mm0, %%mm6 \n\t" + "pcmpgtw %%mm5, %%mm7 \n\t" // pa > pb? + "movq %%mm7, %%mm0 \n\t" + // use mm7 mask to merge pa & pb + "pand %%mm7, %%mm5 \n\t" + // use mm0 mask copy to merge a & b + "pand %%mm0, %%mm2 \n\t" + "pandn %%mm4, %%mm7 \n\t" + "pandn %%mm1, %%mm0 \n\t" + "paddw %%mm5, %%mm7 \n\t" + "paddw %%mm2, %%mm0 \n\t" + // test ((pa <= pb)? pa:pb) <= pc + "pcmpgtw %%mm6, %%mm7 \n\t" // pab > pc? + "pxor %%mm1, %%mm1 \n\t" + "pand %%mm7, %%mm3 \n\t" + "pandn %%mm0, %%mm7 \n\t" + "paddw %%mm3, %%mm7 \n\t" + "pxor %%mm0, %%mm0 \n\t" + "packuswb %%mm1, %%mm7 \n\t" + "movq (%%esi,%%ecx,), %%mm3 \n\t" // load c=Prior(x-bpp) + "pand _ActiveMask, %%mm7 \n\t" + "movq %%mm3, %%mm2 \n\t" // load b=Prior(x) step 1 + "paddb (%%edi,%%ecx,), %%mm7 \n\t" // add Paeth predictor with Raw(x) + "punpcklbw %%mm0, %%mm3 \n\t" // unpack High bytes of c + "movq %%mm7, (%%edi,%%ecx,) \n\t" // write back updated value + "movq %%mm7, %%mm1 \n\t" // now mm1 will be used as Raw(x-bpp) + // do second set of 4 bytes + "punpckhbw %%mm0, %%mm2 \n\t" // unpack Low bytes of b + "punpcklbw %%mm0, %%mm1 \n\t" // unpack Low bytes of a + // pav = p - a = (a + b - c) - a = b - c + "movq %%mm2, %%mm4 \n\t" + // pbv = p - b = (a + b - c) - b = a - c + "movq %%mm1, %%mm5 \n\t" + "psubw %%mm3, %%mm4 \n\t" + "pxor %%mm7, %%mm7 \n\t" + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + "movq %%mm4, %%mm6 \n\t" + "psubw %%mm3, %%mm5 \n\t" + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + "pcmpgtw %%mm4, %%mm0 \n\t" // create mask pav bytes < 0 + "paddw %%mm5, %%mm6 \n\t" + "pand %%mm4, %%mm0 \n\t" // only pav bytes < 0 in mm7 + "pcmpgtw %%mm5, %%mm7 \n\t" // create mask pbv bytes < 0 + "psubw %%mm0, %%mm4 \n\t" + "pand %%mm5, %%mm7 \n\t" // only pbv bytes < 0 in mm0 + "psubw %%mm0, %%mm4 \n\t" + "psubw %%mm7, %%mm5 \n\t" + "pxor %%mm0, %%mm0 \n\t" + "pcmpgtw %%mm6, %%mm0 \n\t" // create mask pcv bytes < 0 + "pand %%mm6, %%mm0 \n\t" // only pav bytes < 0 in mm7 + "psubw %%mm7, %%mm5 \n\t" + "psubw %%mm0, %%mm6 \n\t" + // test pa <= pb + "movq %%mm4, %%mm7 \n\t" + "psubw %%mm0, %%mm6 \n\t" + "pcmpgtw %%mm5, %%mm7 \n\t" // pa > pb? + "movq %%mm7, %%mm0 \n\t" + // use mm7 mask to merge pa & pb + "pand %%mm7, %%mm5 \n\t" + // use mm0 mask copy to merge a & b + "pand %%mm0, %%mm2 \n\t" + "pandn %%mm4, %%mm7 \n\t" + "pandn %%mm1, %%mm0 \n\t" + "paddw %%mm5, %%mm7 \n\t" + "paddw %%mm2, %%mm0 \n\t" + // test ((pa <= pb)? pa:pb) <= pc + "pcmpgtw %%mm6, %%mm7 \n\t" // pab > pc? + "pxor %%mm1, %%mm1 \n\t" + "pand %%mm7, %%mm3 \n\t" + "pandn %%mm0, %%mm7 \n\t" + "pxor %%mm1, %%mm1 \n\t" + "paddw %%mm3, %%mm7 \n\t" + "pxor %%mm0, %%mm0 \n\t" + // step ecx to next set of 8 bytes and repeat loop til done + "addl $8, %%ecx \n\t" + "packuswb %%mm7, %%mm1 \n\t" + "paddb -8(%%edi,%%ecx,), %%mm1 \n\t" // add predictor with Raw(x) + "cmpl _MMXLength, %%ecx \n\t" + "movq %%mm1, -8(%%edi,%%ecx,) \n\t" // write back updated value + // mm1 will be used as Raw(x-bpp) next loop + "jb paeth_4lp \n\t" + + : "=S" (dummy_value_S), // output regs (dummy) + "=D" (dummy_value_D) + + : "0" (prev_row), // esi // input regs + "1" (row) // edi + + : "%ecx" // clobber list +#if 0 /* %mm0, ..., %mm7 not supported by gcc 2.7.2.3 or egcs 1.1 */ + , "%mm0", "%mm1", "%mm2", "%mm3" + , "%mm4", "%mm5", "%mm6", "%mm7" +#endif + ); + } + break; // end 4 bpp + + case 8: // bpp == 8 + { + _ActiveMask.use = 0x00000000ffffffffLL; + + __asm__ __volatile__ ( + "movl _dif, %%ecx \n\t" +// preload "movl row, %%edi \n\t" +// preload "movl prev_row, %%esi \n\t" + "pxor %%mm0, %%mm0 \n\t" + // prime the pump: load the first Raw(x-bpp) data set + "movq -8(%%edi,%%ecx,), %%mm1 \n\t" // only time should need to read + // a=Raw(x-bpp) bytes + "paeth_8lp: \n\t" + // do first set of 4 bytes + "movq -8(%%esi,%%ecx,), %%mm3 \n\t" // read c=Prior(x-bpp) bytes + "punpcklbw %%mm0, %%mm1 \n\t" // unpack Low bytes of a + "movq (%%esi,%%ecx,), %%mm2 \n\t" // load b=Prior(x) + "punpcklbw %%mm0, %%mm2 \n\t" // unpack Low bytes of b + // pav = p - a = (a + b - c) - a = b - c + "movq %%mm2, %%mm4 \n\t" + "punpcklbw %%mm0, %%mm3 \n\t" // unpack Low bytes of c + // pbv = p - b = (a + b - c) - b = a - c + "movq %%mm1, %%mm5 \n\t" + "psubw %%mm3, %%mm4 \n\t" + "pxor %%mm7, %%mm7 \n\t" + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + "movq %%mm4, %%mm6 \n\t" + "psubw %%mm3, %%mm5 \n\t" + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + "pcmpgtw %%mm4, %%mm0 \n\t" // create mask pav bytes < 0 + "paddw %%mm5, %%mm6 \n\t" + "pand %%mm4, %%mm0 \n\t" // only pav bytes < 0 in mm7 + "pcmpgtw %%mm5, %%mm7 \n\t" // create mask pbv bytes < 0 + "psubw %%mm0, %%mm4 \n\t" + "pand %%mm5, %%mm7 \n\t" // only pbv bytes < 0 in mm0 + "psubw %%mm0, %%mm4 \n\t" + "psubw %%mm7, %%mm5 \n\t" + "pxor %%mm0, %%mm0 \n\t" + "pcmpgtw %%mm6, %%mm0 \n\t" // create mask pcv bytes < 0 + "pand %%mm6, %%mm0 \n\t" // only pav bytes < 0 in mm7 + "psubw %%mm7, %%mm5 \n\t" + "psubw %%mm0, %%mm6 \n\t" + // test pa <= pb + "movq %%mm4, %%mm7 \n\t" + "psubw %%mm0, %%mm6 \n\t" + "pcmpgtw %%mm5, %%mm7 \n\t" // pa > pb? + "movq %%mm7, %%mm0 \n\t" + // use mm7 mask to merge pa & pb + "pand %%mm7, %%mm5 \n\t" + // use mm0 mask copy to merge a & b + "pand %%mm0, %%mm2 \n\t" + "pandn %%mm4, %%mm7 \n\t" + "pandn %%mm1, %%mm0 \n\t" + "paddw %%mm5, %%mm7 \n\t" + "paddw %%mm2, %%mm0 \n\t" + // test ((pa <= pb)? pa:pb) <= pc + "pcmpgtw %%mm6, %%mm7 \n\t" // pab > pc? + "pxor %%mm1, %%mm1 \n\t" + "pand %%mm7, %%mm3 \n\t" + "pandn %%mm0, %%mm7 \n\t" + "paddw %%mm3, %%mm7 \n\t" + "pxor %%mm0, %%mm0 \n\t" + "packuswb %%mm1, %%mm7 \n\t" + "movq -8(%%esi,%%ecx,), %%mm3 \n\t" // read c=Prior(x-bpp) bytes + "pand _ActiveMask, %%mm7 \n\t" + "movq (%%esi,%%ecx,), %%mm2 \n\t" // load b=Prior(x) + "paddb (%%edi,%%ecx,), %%mm7 \n\t" // add Paeth predictor with Raw(x) + "punpckhbw %%mm0, %%mm3 \n\t" // unpack High bytes of c + "movq %%mm7, (%%edi,%%ecx,) \n\t" // write back updated value + "movq -8(%%edi,%%ecx,), %%mm1 \n\t" // read a=Raw(x-bpp) bytes + + // do second set of 4 bytes + "punpckhbw %%mm0, %%mm2 \n\t" // unpack High bytes of b + "punpckhbw %%mm0, %%mm1 \n\t" // unpack High bytes of a + // pav = p - a = (a + b - c) - a = b - c + "movq %%mm2, %%mm4 \n\t" + // pbv = p - b = (a + b - c) - b = a - c + "movq %%mm1, %%mm5 \n\t" + "psubw %%mm3, %%mm4 \n\t" + "pxor %%mm7, %%mm7 \n\t" + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + "movq %%mm4, %%mm6 \n\t" + "psubw %%mm3, %%mm5 \n\t" + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + "pcmpgtw %%mm4, %%mm0 \n\t" // create mask pav bytes < 0 + "paddw %%mm5, %%mm6 \n\t" + "pand %%mm4, %%mm0 \n\t" // only pav bytes < 0 in mm7 + "pcmpgtw %%mm5, %%mm7 \n\t" // create mask pbv bytes < 0 + "psubw %%mm0, %%mm4 \n\t" + "pand %%mm5, %%mm7 \n\t" // only pbv bytes < 0 in mm0 + "psubw %%mm0, %%mm4 \n\t" + "psubw %%mm7, %%mm5 \n\t" + "pxor %%mm0, %%mm0 \n\t" + "pcmpgtw %%mm6, %%mm0 \n\t" // create mask pcv bytes < 0 + "pand %%mm6, %%mm0 \n\t" // only pav bytes < 0 in mm7 + "psubw %%mm7, %%mm5 \n\t" + "psubw %%mm0, %%mm6 \n\t" + // test pa <= pb + "movq %%mm4, %%mm7 \n\t" + "psubw %%mm0, %%mm6 \n\t" + "pcmpgtw %%mm5, %%mm7 \n\t" // pa > pb? + "movq %%mm7, %%mm0 \n\t" + // use mm7 mask to merge pa & pb + "pand %%mm7, %%mm5 \n\t" + // use mm0 mask copy to merge a & b + "pand %%mm0, %%mm2 \n\t" + "pandn %%mm4, %%mm7 \n\t" + "pandn %%mm1, %%mm0 \n\t" + "paddw %%mm5, %%mm7 \n\t" + "paddw %%mm2, %%mm0 \n\t" + // test ((pa <= pb)? pa:pb) <= pc + "pcmpgtw %%mm6, %%mm7 \n\t" // pab > pc? + "pxor %%mm1, %%mm1 \n\t" + "pand %%mm7, %%mm3 \n\t" + "pandn %%mm0, %%mm7 \n\t" + "pxor %%mm1, %%mm1 \n\t" + "paddw %%mm3, %%mm7 \n\t" + "pxor %%mm0, %%mm0 \n\t" + // step ecx to next set of 8 bytes and repeat loop til done + "addl $8, %%ecx \n\t" + "packuswb %%mm7, %%mm1 \n\t" + "paddb -8(%%edi,%%ecx,), %%mm1 \n\t" // add Paeth predictor with Raw(x) + "cmpl _MMXLength, %%ecx \n\t" + "movq %%mm1, -8(%%edi,%%ecx,) \n\t" // write back updated value + // mm1 will be used as Raw(x-bpp) next loop + "jb paeth_8lp \n\t" + + : "=S" (dummy_value_S), // output regs (dummy) + "=D" (dummy_value_D) + + : "0" (prev_row), // esi // input regs + "1" (row) // edi + + : "%ecx" // clobber list +#if 0 /* %mm0, ..., %mm7 not supported by gcc 2.7.2.3 or egcs 1.1 */ + , "%mm0", "%mm1", "%mm2", "%mm3" + , "%mm4", "%mm5", "%mm6", "%mm7" +#endif + ); + } + break; // end 8 bpp + + case 1: // bpp = 1 + case 2: // bpp = 2 + default: // bpp > 8 + { + __asm__ __volatile__ ( +#ifdef __PIC__ + "pushl %%ebx \n\t" // save Global Offset Table index +#endif + "movl _dif, %%ebx \n\t" + "cmpl _FullLength, %%ebx \n\t" + "jnb paeth_dend \n\t" + +// preload "movl row, %%edi \n\t" +// preload "movl prev_row, %%esi \n\t" + // do Paeth decode for remaining bytes + "movl %%ebx, %%edx \n\t" +// preload "subl bpp, %%edx \n\t" // (bpp is preloaded into ecx) + "subl %%ecx, %%edx \n\t" // edx = ebx - bpp + "xorl %%ecx, %%ecx \n\t" // zero ecx before using cl & cx + + "paeth_dlp: \n\t" + "xorl %%eax, %%eax \n\t" + // pav = p - a = (a + b - c) - a = b - c + "movb (%%esi,%%ebx,), %%al \n\t" // load Prior(x) into al + "movb (%%esi,%%edx,), %%cl \n\t" // load Prior(x-bpp) into cl + "subl %%ecx, %%eax \n\t" // subtract Prior(x-bpp) + "movl %%eax, _patemp \n\t" // Save pav for later use + "xorl %%eax, %%eax \n\t" + // pbv = p - b = (a + b - c) - b = a - c + "movb (%%edi,%%edx,), %%al \n\t" // load Raw(x-bpp) into al + "subl %%ecx, %%eax \n\t" // subtract Prior(x-bpp) + "movl %%eax, %%ecx \n\t" + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + "addl _patemp, %%eax \n\t" // pcv = pav + pbv + // pc = abs(pcv) + "testl $0x80000000, %%eax \n\t" + "jz paeth_dpca \n\t" + "negl %%eax \n\t" // reverse sign of neg values + + "paeth_dpca: \n\t" + "movl %%eax, _pctemp \n\t" // save pc for later use + // pb = abs(pbv) + "testl $0x80000000, %%ecx \n\t" + "jz paeth_dpba \n\t" + "negl %%ecx \n\t" // reverse sign of neg values + + "paeth_dpba: \n\t" + "movl %%ecx, _pbtemp \n\t" // save pb for later use + // pa = abs(pav) + "movl _patemp, %%eax \n\t" + "testl $0x80000000, %%eax \n\t" + "jz paeth_dpaa \n\t" + "negl %%eax \n\t" // reverse sign of neg values + + "paeth_dpaa: \n\t" + "movl %%eax, _patemp \n\t" // save pa for later use + // test if pa <= pb + "cmpl %%ecx, %%eax \n\t" + "jna paeth_dabb \n\t" + // pa > pb; now test if pb <= pc + "cmpl _pctemp, %%ecx \n\t" + "jna paeth_dbbc \n\t" + // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp) + "movb (%%esi,%%edx,), %%cl \n\t" // load Prior(x-bpp) into cl + "jmp paeth_dpaeth \n\t" + + "paeth_dbbc: \n\t" + // pb <= pc; Raw(x) = Paeth(x) + Prior(x) + "movb (%%esi,%%ebx,), %%cl \n\t" // load Prior(x) into cl + "jmp paeth_dpaeth \n\t" + + "paeth_dabb: \n\t" + // pa <= pb; now test if pa <= pc + "cmpl _pctemp, %%eax \n\t" + "jna paeth_dabc \n\t" + // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp) + "movb (%%esi,%%edx,), %%cl \n\t" // load Prior(x-bpp) into cl + "jmp paeth_dpaeth \n\t" + + "paeth_dabc: \n\t" + // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp) + "movb (%%edi,%%edx,), %%cl \n\t" // load Raw(x-bpp) into cl + + "paeth_dpaeth: \n\t" + "incl %%ebx \n\t" + "incl %%edx \n\t" + // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256 + "addb %%cl, -1(%%edi,%%ebx,) \n\t" + "cmpl _FullLength, %%ebx \n\t" + "jb paeth_dlp \n\t" + + "paeth_dend: \n\t" +#ifdef __PIC__ + "popl %%ebx \n\t" // index to Global Offset Table +#endif + + : "=c" (dummy_value_c), // output regs (dummy) + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "0" (bpp), // ecx // input regs + "1" (prev_row), // esi + "2" (row) // edi + + : "%eax", "%edx" // clobber list +#ifndef __PIC__ + , "%ebx" +#endif + ); + } + return; // No need to go further with this one + + } // end switch (bpp) + + __asm__ __volatile__ ( + // MMX acceleration complete; now do clean-up + // check if any remaining bytes left to decode +#ifdef __PIC__ + "pushl %%ebx \n\t" // save index to Global Offset Table +#endif + "movl _MMXLength, %%ebx \n\t" + "cmpl _FullLength, %%ebx \n\t" + "jnb paeth_end \n\t" +//pre "movl row, %%edi \n\t" +//pre "movl prev_row, %%esi \n\t" + // do Paeth decode for remaining bytes + "movl %%ebx, %%edx \n\t" +//pre "subl bpp, %%edx \n\t" // (bpp is preloaded into ecx) + "subl %%ecx, %%edx \n\t" // edx = ebx - bpp + "xorl %%ecx, %%ecx \n\t" // zero ecx before using cl & cx below + + "paeth_lp2: \n\t" + "xorl %%eax, %%eax \n\t" + // pav = p - a = (a + b - c) - a = b - c + "movb (%%esi,%%ebx,), %%al \n\t" // load Prior(x) into al + "movb (%%esi,%%edx,), %%cl \n\t" // load Prior(x-bpp) into cl + "subl %%ecx, %%eax \n\t" // subtract Prior(x-bpp) + "movl %%eax, _patemp \n\t" // Save pav for later use + "xorl %%eax, %%eax \n\t" + // pbv = p - b = (a + b - c) - b = a - c + "movb (%%edi,%%edx,), %%al \n\t" // load Raw(x-bpp) into al + "subl %%ecx, %%eax \n\t" // subtract Prior(x-bpp) + "movl %%eax, %%ecx \n\t" + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + "addl _patemp, %%eax \n\t" // pcv = pav + pbv + // pc = abs(pcv) + "testl $0x80000000, %%eax \n\t" + "jz paeth_pca2 \n\t" + "negl %%eax \n\t" // reverse sign of neg values + + "paeth_pca2: \n\t" + "movl %%eax, _pctemp \n\t" // save pc for later use + // pb = abs(pbv) + "testl $0x80000000, %%ecx \n\t" + "jz paeth_pba2 \n\t" + "negl %%ecx \n\t" // reverse sign of neg values + + "paeth_pba2: \n\t" + "movl %%ecx, _pbtemp \n\t" // save pb for later use + // pa = abs(pav) + "movl _patemp, %%eax \n\t" + "testl $0x80000000, %%eax \n\t" + "jz paeth_paa2 \n\t" + "negl %%eax \n\t" // reverse sign of neg values + + "paeth_paa2: \n\t" + "movl %%eax, _patemp \n\t" // save pa for later use + // test if pa <= pb + "cmpl %%ecx, %%eax \n\t" + "jna paeth_abb2 \n\t" + // pa > pb; now test if pb <= pc + "cmpl _pctemp, %%ecx \n\t" + "jna paeth_bbc2 \n\t" + // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp) + "movb (%%esi,%%edx,), %%cl \n\t" // load Prior(x-bpp) into cl + "jmp paeth_paeth2 \n\t" + + "paeth_bbc2: \n\t" + // pb <= pc; Raw(x) = Paeth(x) + Prior(x) + "movb (%%esi,%%ebx,), %%cl \n\t" // load Prior(x) into cl + "jmp paeth_paeth2 \n\t" + + "paeth_abb2: \n\t" + // pa <= pb; now test if pa <= pc + "cmpl _pctemp, %%eax \n\t" + "jna paeth_abc2 \n\t" + // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp) + "movb (%%esi,%%edx,), %%cl \n\t" // load Prior(x-bpp) into cl + "jmp paeth_paeth2 \n\t" + + "paeth_abc2: \n\t" + // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp) + "movb (%%edi,%%edx,), %%cl \n\t" // load Raw(x-bpp) into cl + + "paeth_paeth2: \n\t" + "incl %%ebx \n\t" + "incl %%edx \n\t" + // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256 + "addb %%cl, -1(%%edi,%%ebx,) \n\t" + "cmpl _FullLength, %%ebx \n\t" + "jb paeth_lp2 \n\t" + + "paeth_end: \n\t" + "EMMS \n\t" // end MMX; prep for poss. FP instrs. +#ifdef __PIC__ + "popl %%ebx \n\t" // restore index to Global Offset Table +#endif + + : "=c" (dummy_value_c), // output regs (dummy) + "=S" (dummy_value_S), + "=D" (dummy_value_D) + + : "0" (bpp), // ecx // input regs + "1" (prev_row), // esi + "2" (row) // edi + + : "%eax", "%edx" // clobber list (no input regs!) +#ifndef __PIC__ + , "%ebx" +#endif + ); + +} /* end png_read_filter_row_mmx_paeth() */ +#endif + + + + +#ifdef PNG_THREAD_UNSAFE_OK +//===========================================================================// +// // +// P N G _ R E A D _ F I L T E R _ R O W _ M M X _ S U B // +// // +//===========================================================================// + +// Optimized code for PNG Sub filter decoder + +static void /* PRIVATE */ +png_read_filter_row_mmx_sub(png_row_infop row_info, png_bytep row) +{ + int bpp; + int dummy_value_a; + int dummy_value_D; + + bpp = (row_info->pixel_depth + 7) >> 3; // calc number of bytes per pixel + _FullLength = row_info->rowbytes - bpp; // number of bytes to filter + + __asm__ __volatile__ ( +//pre "movl row, %%edi \n\t" + "movl %%edi, %%esi \n\t" // lp = row +//pre "movl bpp, %%eax \n\t" + "addl %%eax, %%edi \n\t" // rp = row + bpp +//irr "xorl %%eax, %%eax \n\t" + // get # of bytes to alignment + "movl %%edi, _dif \n\t" // take start of row + "addl $0xf, _dif \n\t" // add 7 + 8 to incr past + // alignment boundary + "xorl %%ecx, %%ecx \n\t" + "andl $0xfffffff8, _dif \n\t" // mask to alignment boundary + "subl %%edi, _dif \n\t" // subtract from start ==> value + "jz sub_go \n\t" // ecx at alignment + + "sub_lp1: \n\t" // fix alignment + "movb (%%esi,%%ecx,), %%al \n\t" + "addb %%al, (%%edi,%%ecx,) \n\t" + "incl %%ecx \n\t" + "cmpl _dif, %%ecx \n\t" + "jb sub_lp1 \n\t" + + "sub_go: \n\t" + "movl _FullLength, %%eax \n\t" + "movl %%eax, %%edx \n\t" + "subl %%ecx, %%edx \n\t" // subtract alignment fix + "andl $0x00000007, %%edx \n\t" // calc bytes over mult of 8 + "subl %%edx, %%eax \n\t" // drop over bytes from length + "movl %%eax, _MMXLength \n\t" + + : "=a" (dummy_value_a), // 0 // output regs (dummy) + "=D" (dummy_value_D) // 1 + + : "0" (bpp), // eax // input regs + "1" (row) // edi + + : "%esi", "%ecx", "%edx" // clobber list + +#if 0 /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */ + , "%mm0", "%mm1", "%mm2", "%mm3" + , "%mm4", "%mm5", "%mm6", "%mm7" +#endif + ); + + // now do the math for the rest of the row + switch (bpp) + { + case 3: + { + _ActiveMask.use = 0x0000ffffff000000LL; + _ShiftBpp.use = 24; // == 3 * 8 + _ShiftRem.use = 40; // == 64 - 24 + + __asm__ __volatile__ ( +// preload "movl row, %%edi \n\t" + "movq _ActiveMask, %%mm7 \n\t" // load _ActiveMask for 2nd + // active byte group + "movl %%edi, %%esi \n\t" // lp = row +// preload "movl bpp, %%eax \n\t" + "addl %%eax, %%edi \n\t" // rp = row + bpp + "movq %%mm7, %%mm6 \n\t" + "movl _dif, %%edx \n\t" + "psllq _ShiftBpp, %%mm6 \n\t" // move mask in mm6 to cover + // 3rd active byte group + // prime the pump: load the first Raw(x-bpp) data set + "movq -8(%%edi,%%edx,), %%mm1 \n\t" + + "sub_3lp: \n\t" // shift data for adding first + "psrlq _ShiftRem, %%mm1 \n\t" // bpp bytes (no need for mask; + // shift clears inactive bytes) + // add 1st active group + "movq (%%edi,%%edx,), %%mm0 \n\t" + "paddb %%mm1, %%mm0 \n\t" + + // add 2nd active group + "movq %%mm0, %%mm1 \n\t" // mov updated Raws to mm1 + "psllq _ShiftBpp, %%mm1 \n\t" // shift data to pos. correctly + "pand %%mm7, %%mm1 \n\t" // mask to use 2nd active group + "paddb %%mm1, %%mm0 \n\t" + + // add 3rd active group + "movq %%mm0, %%mm1 \n\t" // mov updated Raws to mm1 + "psllq _ShiftBpp, %%mm1 \n\t" // shift data to pos. correctly + "pand %%mm6, %%mm1 \n\t" // mask to use 3rd active group + "addl $8, %%edx \n\t" + "paddb %%mm1, %%mm0 \n\t" + + "cmpl _MMXLength, %%edx \n\t" + "movq %%mm0, -8(%%edi,%%edx,) \n\t" // write updated Raws to array + "movq %%mm0, %%mm1 \n\t" // prep 1st add at top of loop + "jb sub_3lp \n\t" + + : "=a" (dummy_value_a), // 0 // output regs (dummy) + "=D" (dummy_value_D) // 1 + + : "0" (bpp), // eax // input regs + "1" (row) // edi + + : "%edx", "%esi" // clobber list +#if 0 /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */ + , "%mm0", "%mm1", "%mm6", "%mm7" +#endif + ); + } + break; + + case 1: + { + __asm__ __volatile__ ( + "movl _dif, %%edx \n\t" +// preload "movl row, %%edi \n\t" + "cmpl _FullLength, %%edx \n\t" + "jnb sub_1end \n\t" + "movl %%edi, %%esi \n\t" // lp = row + "xorl %%eax, %%eax \n\t" +// preload "movl bpp, %%eax \n\t" + "addl %%eax, %%edi \n\t" // rp = row + bpp + + "sub_1lp: \n\t" + "movb (%%esi,%%edx,), %%al \n\t" + "addb %%al, (%%edi,%%edx,) \n\t" + "incl %%edx \n\t" + "cmpl _FullLength, %%edx \n\t" + "jb sub_1lp \n\t" + + "sub_1end: \n\t" + + : "=a" (dummy_value_a), // 0 // output regs (dummy) + "=D" (dummy_value_D) // 1 + + : "0" (bpp), // eax // input regs + "1" (row) // edi + + : "%edx", "%esi" // clobber list + ); + } + return; + + case 6: + case 4: + //case 7: // GRR BOGUS + //case 5: // GRR BOGUS + { + _ShiftBpp.use = bpp << 3; + _ShiftRem.use = 64 - _ShiftBpp.use; + + __asm__ __volatile__ ( +// preload "movl row, %%edi \n\t" + "movl _dif, %%edx \n\t" + "movl %%edi, %%esi \n\t" // lp = row +// preload "movl bpp, %%eax \n\t" + "addl %%eax, %%edi \n\t" // rp = row + bpp + + // prime the pump: load the first Raw(x-bpp) data set + "movq -8(%%edi,%%edx,), %%mm1 \n\t" + + "sub_4lp: \n\t" // shift data for adding first + "psrlq _ShiftRem, %%mm1 \n\t" // bpp bytes (no need for mask; + // shift clears inactive bytes) + "movq (%%edi,%%edx,), %%mm0 \n\t" + "paddb %%mm1, %%mm0 \n\t" + + // add 2nd active group + "movq %%mm0, %%mm1 \n\t" // mov updated Raws to mm1 + "psllq _ShiftBpp, %%mm1 \n\t" // shift data to pos. correctly + "addl $8, %%edx \n\t" + "paddb %%mm1, %%mm0 \n\t" + + "cmpl _MMXLength, %%edx \n\t" + "movq %%mm0, -8(%%edi,%%edx,) \n\t" + "movq %%mm0, %%mm1 \n\t" // prep 1st add at top of loop + "jb sub_4lp \n\t" + + : "=a" (dummy_value_a), // 0 // output regs (dummy) + "=D" (dummy_value_D) // 1 + + : "0" (bpp), // eax // input regs + "1" (row) // edi + + : "%edx", "%esi" // clobber list +#if 0 /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */ + , "%mm0", "%mm1" +#endif + ); + } + break; + + case 2: + { + _ActiveMask.use = 0x00000000ffff0000LL; + _ShiftBpp.use = 16; // == 2 * 8 + _ShiftRem.use = 48; // == 64 - 16 + + __asm__ __volatile__ ( + "movq _ActiveMask, %%mm7 \n\t" // load _ActiveMask for 2nd + // active byte group + "movl _dif, %%edx \n\t" + "movq %%mm7, %%mm6 \n\t" +// preload "movl row, %%edi \n\t" + "psllq _ShiftBpp, %%mm6 \n\t" // move mask in mm6 to cover + // 3rd active byte group + "movl %%edi, %%esi \n\t" // lp = row + "movq %%mm6, %%mm5 \n\t" +// preload "movl bpp, %%eax \n\t" + "addl %%eax, %%edi \n\t" // rp = row + bpp + "psllq _ShiftBpp, %%mm5 \n\t" // move mask in mm5 to cover + // 4th active byte group + // prime the pump: load the first Raw(x-bpp) data set + "movq -8(%%edi,%%edx,), %%mm1 \n\t" + + "sub_2lp: \n\t" // shift data for adding first + "psrlq _ShiftRem, %%mm1 \n\t" // bpp bytes (no need for mask; + // shift clears inactive bytes) + // add 1st active group + "movq (%%edi,%%edx,), %%mm0 \n\t" + "paddb %%mm1, %%mm0 \n\t" + + // add 2nd active group + "movq %%mm0, %%mm1 \n\t" // mov updated Raws to mm1 + "psllq _ShiftBpp, %%mm1 \n\t" // shift data to pos. correctly + "pand %%mm7, %%mm1 \n\t" // mask to use 2nd active group + "paddb %%mm1, %%mm0 \n\t" + + // add 3rd active group + "movq %%mm0, %%mm1 \n\t" // mov updated Raws to mm1 + "psllq _ShiftBpp, %%mm1 \n\t" // shift data to pos. correctly + "pand %%mm6, %%mm1 \n\t" // mask to use 3rd active group + "paddb %%mm1, %%mm0 \n\t" + + // add 4th active group + "movq %%mm0, %%mm1 \n\t" // mov updated Raws to mm1 + "psllq _ShiftBpp, %%mm1 \n\t" // shift data to pos. correctly + "pand %%mm5, %%mm1 \n\t" // mask to use 4th active group + "addl $8, %%edx \n\t" + "paddb %%mm1, %%mm0 \n\t" + "cmpl _MMXLength, %%edx \n\t" + "movq %%mm0, -8(%%edi,%%edx,) \n\t" // write updated Raws to array + "movq %%mm0, %%mm1 \n\t" // prep 1st add at top of loop + "jb sub_2lp \n\t" + + : "=a" (dummy_value_a), // 0 // output regs (dummy) + "=D" (dummy_value_D) // 1 + + : "0" (bpp), // eax // input regs + "1" (row) // edi + + : "%edx", "%esi" // clobber list +#if 0 /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */ + , "%mm0", "%mm1", "%mm5", "%mm6", "%mm7" +#endif + ); + } + break; + + case 8: + { + __asm__ __volatile__ ( +// preload "movl row, %%edi \n\t" + "movl _dif, %%edx \n\t" + "movl %%edi, %%esi \n\t" // lp = row +// preload "movl bpp, %%eax \n\t" + "addl %%eax, %%edi \n\t" // rp = row + bpp + "movl _MMXLength, %%ecx \n\t" + + // prime the pump: load the first Raw(x-bpp) data set + "movq -8(%%edi,%%edx,), %%mm7 \n\t" + "andl $0x0000003f, %%ecx \n\t" // calc bytes over mult of 64 + + "sub_8lp: \n\t" + "movq (%%edi,%%edx,), %%mm0 \n\t" // load Sub(x) for 1st 8 bytes + "paddb %%mm7, %%mm0 \n\t" + "movq 8(%%edi,%%edx,), %%mm1 \n\t" // load Sub(x) for 2nd 8 bytes + "movq %%mm0, (%%edi,%%edx,) \n\t" // write Raw(x) for 1st 8 bytes + + // Now mm0 will be used as Raw(x-bpp) for the 2nd group of 8 bytes. + // This will be repeated for each group of 8 bytes with the 8th + // group being used as the Raw(x-bpp) for the 1st group of the + // next loop. + + "paddb %%mm0, %%mm1 \n\t" + "movq 16(%%edi,%%edx,), %%mm2 \n\t" // load Sub(x) for 3rd 8 bytes + "movq %%mm1, 8(%%edi,%%edx,) \n\t" // write Raw(x) for 2nd 8 bytes + "paddb %%mm1, %%mm2 \n\t" + "movq 24(%%edi,%%edx,), %%mm3 \n\t" // load Sub(x) for 4th 8 bytes + "movq %%mm2, 16(%%edi,%%edx,) \n\t" // write Raw(x) for 3rd 8 bytes + "paddb %%mm2, %%mm3 \n\t" + "movq 32(%%edi,%%edx,), %%mm4 \n\t" // load Sub(x) for 5th 8 bytes + "movq %%mm3, 24(%%edi,%%edx,) \n\t" // write Raw(x) for 4th 8 bytes + "paddb %%mm3, %%mm4 \n\t" + "movq 40(%%edi,%%edx,), %%mm5 \n\t" // load Sub(x) for 6th 8 bytes + "movq %%mm4, 32(%%edi,%%edx,) \n\t" // write Raw(x) for 5th 8 bytes + "paddb %%mm4, %%mm5 \n\t" + "movq 48(%%edi,%%edx,), %%mm6 \n\t" // load Sub(x) for 7th 8 bytes + "movq %%mm5, 40(%%edi,%%edx,) \n\t" // write Raw(x) for 6th 8 bytes + "paddb %%mm5, %%mm6 \n\t" + "movq 56(%%edi,%%edx,), %%mm7 \n\t" // load Sub(x) for 8th 8 bytes + "movq %%mm6, 48(%%edi,%%edx,) \n\t" // write Raw(x) for 7th 8 bytes + "addl $64, %%edx \n\t" + "paddb %%mm6, %%mm7 \n\t" + "cmpl %%ecx, %%edx \n\t" + "movq %%mm7, -8(%%edi,%%edx,) \n\t" // write Raw(x) for 8th 8 bytes + "jb sub_8lp \n\t" + + "cmpl _MMXLength, %%edx \n\t" + "jnb sub_8lt8 \n\t" + + "sub_8lpA: \n\t" + "movq (%%edi,%%edx,), %%mm0 \n\t" + "addl $8, %%edx \n\t" + "paddb %%mm7, %%mm0 \n\t" + "cmpl _MMXLength, %%edx \n\t" + "movq %%mm0, -8(%%edi,%%edx,) \n\t" // -8 to offset early addl edx + "movq %%mm0, %%mm7 \n\t" // move calculated Raw(x) data + // to mm1 to be new Raw(x-bpp) + // for next loop + "jb sub_8lpA \n\t" + + "sub_8lt8: \n\t" + + : "=a" (dummy_value_a), // 0 // output regs (dummy) + "=D" (dummy_value_D) // 1 + + : "0" (bpp), // eax // input regs + "1" (row) // edi + + : "%ecx", "%edx", "%esi" // clobber list +#if 0 /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */ + , "%mm0", "%mm1", "%mm2", "%mm3", "%mm4", "%mm5", "%mm6", "%mm7" +#endif + ); + } + break; + + default: // bpp greater than 8 bytes GRR BOGUS + { + __asm__ __volatile__ ( + "movl _dif, %%edx \n\t" +// preload "movl row, %%edi \n\t" + "movl %%edi, %%esi \n\t" // lp = row +// preload "movl bpp, %%eax \n\t" + "addl %%eax, %%edi \n\t" // rp = row + bpp + + "sub_Alp: \n\t" + "movq (%%edi,%%edx,), %%mm0 \n\t" + "movq (%%esi,%%edx,), %%mm1 \n\t" + "addl $8, %%edx \n\t" + "paddb %%mm1, %%mm0 \n\t" + "cmpl _MMXLength, %%edx \n\t" + "movq %%mm0, -8(%%edi,%%edx,) \n\t" // mov does not affect flags; + // -8 to offset addl edx + "jb sub_Alp \n\t" + + : "=a" (dummy_value_a), // 0 // output regs (dummy) + "=D" (dummy_value_D) // 1 + + : "0" (bpp), // eax // input regs + "1" (row) // edi + + : "%edx", "%esi" // clobber list +#if 0 /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */ + , "%mm0", "%mm1" +#endif + ); + } + break; + + } // end switch (bpp) + + __asm__ __volatile__ ( + "movl _MMXLength, %%edx \n\t" +//pre "movl row, %%edi \n\t" + "cmpl _FullLength, %%edx \n\t" + "jnb sub_end \n\t" + + "movl %%edi, %%esi \n\t" // lp = row +//pre "movl bpp, %%eax \n\t" + "addl %%eax, %%edi \n\t" // rp = row + bpp + "xorl %%eax, %%eax \n\t" + + "sub_lp2: \n\t" + "movb (%%esi,%%edx,), %%al \n\t" + "addb %%al, (%%edi,%%edx,) \n\t" + "incl %%edx \n\t" + "cmpl _FullLength, %%edx \n\t" + "jb sub_lp2 \n\t" + + "sub_end: \n\t" + "EMMS \n\t" // end MMX instructions + + : "=a" (dummy_value_a), // 0 // output regs (dummy) + "=D" (dummy_value_D) // 1 + + : "0" (bpp), // eax // input regs + "1" (row) // edi + + : "%edx", "%esi" // clobber list + ); + +} // end of png_read_filter_row_mmx_sub() +#endif + + + + +//===========================================================================// +// // +// P N G _ R E A D _ F I L T E R _ R O W _ M M X _ U P // +// // +//===========================================================================// + +// Optimized code for PNG Up filter decoder + +static void /* PRIVATE */ +png_read_filter_row_mmx_up(png_row_infop row_info, png_bytep row, + png_bytep prev_row) +{ + png_uint_32 len; + int dummy_value_d; // fix 'forbidden register 3 (dx) was spilled' error + int dummy_value_S; + int dummy_value_D; + + len = row_info->rowbytes; // number of bytes to filter + + __asm__ __volatile__ ( +//pre "movl row, %%edi \n\t" + // get # of bytes to alignment +#ifdef __PIC__ + "pushl %%ebx \n\t" +#endif + "movl %%edi, %%ecx \n\t" + "xorl %%ebx, %%ebx \n\t" + "addl $0x7, %%ecx \n\t" + "xorl %%eax, %%eax \n\t" + "andl $0xfffffff8, %%ecx \n\t" +//pre "movl prev_row, %%esi \n\t" + "subl %%edi, %%ecx \n\t" + "jz up_go \n\t" + + "up_lp1: \n\t" // fix alignment + "movb (%%edi,%%ebx,), %%al \n\t" + "addb (%%esi,%%ebx,), %%al \n\t" + "incl %%ebx \n\t" + "cmpl %%ecx, %%ebx \n\t" + "movb %%al, -1(%%edi,%%ebx,) \n\t" // mov does not affect flags; -1 to + "jb up_lp1 \n\t" // offset incl ebx + + "up_go: \n\t" +//pre "movl len, %%edx \n\t" + "movl %%edx, %%ecx \n\t" + "subl %%ebx, %%edx \n\t" // subtract alignment fix + "andl $0x0000003f, %%edx \n\t" // calc bytes over mult of 64 + "subl %%edx, %%ecx \n\t" // drop over bytes from length + + // unrolled loop - use all MMX registers and interleave to reduce + // number of branch instructions (loops) and reduce partial stalls + "up_loop: \n\t" + "movq (%%esi,%%ebx,), %%mm1 \n\t" + "movq (%%edi,%%ebx,), %%mm0 \n\t" + "movq 8(%%esi,%%ebx,), %%mm3 \n\t" + "paddb %%mm1, %%mm0 \n\t" + "movq 8(%%edi,%%ebx,), %%mm2 \n\t" + "movq %%mm0, (%%edi,%%ebx,) \n\t" + "paddb %%mm3, %%mm2 \n\t" + "movq 16(%%esi,%%ebx,), %%mm5 \n\t" + "movq %%mm2, 8(%%edi,%%ebx,) \n\t" + "movq 16(%%edi,%%ebx,), %%mm4 \n\t" + "movq 24(%%esi,%%ebx,), %%mm7 \n\t" + "paddb %%mm5, %%mm4 \n\t" + "movq 24(%%edi,%%ebx,), %%mm6 \n\t" + "movq %%mm4, 16(%%edi,%%ebx,) \n\t" + "paddb %%mm7, %%mm6 \n\t" + "movq 32(%%esi,%%ebx,), %%mm1 \n\t" + "movq %%mm6, 24(%%edi,%%ebx,) \n\t" + "movq 32(%%edi,%%ebx,), %%mm0 \n\t" + "movq 40(%%esi,%%ebx,), %%mm3 \n\t" + "paddb %%mm1, %%mm0 \n\t" + "movq 40(%%edi,%%ebx,), %%mm2 \n\t" + "movq %%mm0, 32(%%edi,%%ebx,) \n\t" + "paddb %%mm3, %%mm2 \n\t" + "movq 48(%%esi,%%ebx,), %%mm5 \n\t" + "movq %%mm2, 40(%%edi,%%ebx,) \n\t" + "movq 48(%%edi,%%ebx,), %%mm4 \n\t" + "movq 56(%%esi,%%ebx,), %%mm7 \n\t" + "paddb %%mm5, %%mm4 \n\t" + "movq 56(%%edi,%%ebx,), %%mm6 \n\t" + "movq %%mm4, 48(%%edi,%%ebx,) \n\t" + "addl $64, %%ebx \n\t" + "paddb %%mm7, %%mm6 \n\t" + "cmpl %%ecx, %%ebx \n\t" + "movq %%mm6, -8(%%edi,%%ebx,) \n\t" // (+56)movq does not affect flags; + "jb up_loop \n\t" // -8 to offset addl ebx + + "cmpl $0, %%edx \n\t" // test for bytes over mult of 64 + "jz up_end \n\t" + + "cmpl $8, %%edx \n\t" // test for less than 8 bytes + "jb up_lt8 \n\t" // [added by lcreeve at netins.net] + + "addl %%edx, %%ecx \n\t" + "andl $0x00000007, %%edx \n\t" // calc bytes over mult of 8 + "subl %%edx, %%ecx \n\t" // drop over bytes from length + "jz up_lt8 \n\t" + + "up_lpA: \n\t" // use MMX regs to update 8 bytes sim. + "movq (%%esi,%%ebx,), %%mm1 \n\t" + "movq (%%edi,%%ebx,), %%mm0 \n\t" + "addl $8, %%ebx \n\t" + "paddb %%mm1, %%mm0 \n\t" + "cmpl %%ecx, %%ebx \n\t" + "movq %%mm0, -8(%%edi,%%ebx,) \n\t" // movq does not affect flags; -8 to + "jb up_lpA \n\t" // offset add ebx + "cmpl $0, %%edx \n\t" // test for bytes over mult of 8 + "jz up_end \n\t" + + "up_lt8: \n\t" + "xorl %%eax, %%eax \n\t" + "addl %%edx, %%ecx \n\t" // move over byte count into counter + + "up_lp2: \n\t" // use x86 regs for remaining bytes + "movb (%%edi,%%ebx,), %%al \n\t" + "addb (%%esi,%%ebx,), %%al \n\t" + "incl %%ebx \n\t" + "cmpl %%ecx, %%ebx \n\t" + "movb %%al, -1(%%edi,%%ebx,) \n\t" // mov does not affect flags; -1 to + "jb up_lp2 \n\t" // offset inc ebx + + "up_end: \n\t" + "EMMS \n\t" // conversion of filtered row complete +#ifdef __PIC__ + "popl %%ebx \n\t" +#endif + + : "=d" (dummy_value_d), // 0 // output regs (dummy) + "=S" (dummy_value_S), // 1 + "=D" (dummy_value_D) // 2 + + : "0" (len), // edx // input regs + "1" (prev_row), // esi + "2" (row) // edi + + : "%eax", "%ecx" // clobber list (no input regs!) +#ifndef __PIC__ + , "%ebx" +#endif + +#if 0 /* MMX regs (%mm0, etc.) not supported by gcc 2.7.2.3 or egcs 1.1 */ + , "%mm0", "%mm1", "%mm2", "%mm3" + , "%mm4", "%mm5", "%mm6", "%mm7" +#endif + ); + +} // end of png_read_filter_row_mmx_up() + +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + + + + +/*===========================================================================*/ +/* */ +/* P N G _ R E A D _ F I L T E R _ R O W */ +/* */ +/*===========================================================================*/ + + +/* Optimized png_read_filter_row routines */ + +void /* PRIVATE */ +png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep + row, png_bytep prev_row, int filter) +{ +#ifdef PNG_DEBUG + char filnm[10]; +#endif + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +/* GRR: these are superseded by png_ptr->asm_flags: */ +#define UseMMX_sub 1 // GRR: converted 20000730 +#define UseMMX_up 1 // GRR: converted 20000729 +#define UseMMX_avg 1 // GRR: converted 20000828 (+ 16-bit bugfix 20000916) +#define UseMMX_paeth 1 // GRR: converted 20000828 + + if (_mmx_supported == 2) { + /* this should have happened in png_init_mmx_flags() already */ +#if !defined(PNG_1_0_X) + png_warning(png_ptr, "asm_flags may not have been initialized"); +#endif + png_mmx_support(); + } +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + +#ifdef PNG_DEBUG + png_debug(1, "in png_read_filter_row (pnggccrd.c)\n"); + switch (filter) + { + case 0: sprintf(filnm, "none"); + break; + case 1: sprintf(filnm, "sub-%s", +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK) +#if !defined(PNG_1_0_X) + (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_SUB)? "MMX" : +#endif +#endif +"x86"); + break; + case 2: sprintf(filnm, "up-%s", +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +#if !defined(PNG_1_0_X) + (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_UP)? "MMX" : +#endif +#endif + "x86"); + break; + case 3: sprintf(filnm, "avg-%s", +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK) +#if !defined(PNG_1_0_X) + (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_AVG)? "MMX" : +#endif +#endif + "x86"); + break; + case 4: sprintf(filnm, "Paeth-%s", +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK) +#if !defined(PNG_1_0_X) + (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH)? "MMX": +#endif +#endif +"x86"); + break; + default: sprintf(filnm, "unknw"); + break; + } + png_debug2(0, "row_number=%5ld, %5s, ", png_ptr->row_number, filnm); + png_debug1(0, "row=0x%08lx, ", (unsigned long)row); + png_debug2(0, "pixdepth=%2d, bytes=%d, ", (int)row_info->pixel_depth, + (int)((row_info->pixel_depth + 7) >> 3)); + png_debug1(0,"rowbytes=%8ld\n", row_info->rowbytes); +#endif /* PNG_DEBUG */ + + switch (filter) + { + case PNG_FILTER_VALUE_NONE: + break; + + case PNG_FILTER_VALUE_SUB: +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK) +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_SUB) && + (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) && + (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold)) +#else + if (_mmx_supported) +#endif + { + png_read_filter_row_mmx_sub(row_info, row); + } + else +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + { + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_bytep rp = row + bpp; + png_bytep lp = row; + + for (i = bpp; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff); + rp++; + } + } /* end !UseMMX_sub */ + break; + + case PNG_FILTER_VALUE_UP: +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_UP) && + (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) && + (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold)) +#else + if (_mmx_supported) +#endif + { + png_read_filter_row_mmx_up(row_info, row, prev_row); + } + else +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + { + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_bytep rp = row; + png_bytep pp = prev_row; + + for (i = 0; i < istop; ++i) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + } /* end !UseMMX_up */ + break; + + case PNG_FILTER_VALUE_AVG: +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK) +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_AVG) && + (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) && + (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold)) +#else + if (_mmx_supported) +#endif + { + png_read_filter_row_mmx_avg(row_info, row, prev_row); + } + else +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + { + png_uint_32 i; + png_bytep rp = row; + png_bytep pp = prev_row; + png_bytep lp = row; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_uint_32 istop = row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + + ((int)(*pp++) >> 1)) & 0xff); + rp++; + } + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + + ((int)(*pp++ + *lp++) >> 1)) & 0xff); + rp++; + } + } /* end !UseMMX_avg */ + break; + + case PNG_FILTER_VALUE_PAETH: +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_THREAD_UNSAFE_OK) +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH) && + (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) && + (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold)) +#else + if (_mmx_supported) +#endif + { + png_read_filter_row_mmx_paeth(row_info, row, prev_row); + } + else +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + { + png_uint_32 i; + png_bytep rp = row; + png_bytep pp = prev_row; + png_bytep lp = row; + png_bytep cp = prev_row; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_uint_32 istop = row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + + for (i = 0; i < istop; i++) /* use leftover rp,pp */ + { + int a, b, c, pa, pb, pc, p; + + a = *lp++; + b = *pp++; + c = *cp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + /* + if (pa <= pb && pa <= pc) + p = a; + else if (pb <= pc) + p = b; + else + p = c; + */ + + p = (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c; + + *rp = (png_byte)(((int)(*rp) + p) & 0xff); + rp++; + } + } /* end !UseMMX_paeth */ + break; + + default: + png_warning(png_ptr, "Ignoring bad row-filter type"); + *row=0; + break; + } +} + +#endif /* PNG_HAVE_ASSEMBLER_READ_FILTER_ROW */ + + +/*===========================================================================*/ +/* */ +/* P N G _ M M X _ S U P P O R T */ +/* */ +/*===========================================================================*/ + +/* GRR NOTES: (1) the following code assumes 386 or better (pushfl/popfl) + * (2) all instructions compile with gcc 2.7.2.3 and later + * (3) the function is moved down here to prevent gcc from + * inlining it in multiple places and then barfing be- + * cause the ".NOT_SUPPORTED" label is multiply defined + * [is there a way to signal that a *single* function should + * not be inlined? is there a way to modify the label for + * each inlined instance, e.g., by appending _1, _2, etc.? + * maybe if don't use leading "." in label name? (nope...sigh)] + */ + +int PNGAPI +png_mmx_support(void) +{ +#if defined(PNG_MMX_CODE_SUPPORTED) + __asm__ __volatile__ ( + "pushl %%ebx \n\t" // ebx gets clobbered by CPUID instruction + "pushl %%ecx \n\t" // so does ecx... + "pushl %%edx \n\t" // ...and edx (but ecx & edx safe on Linux) +// ".byte 0x66 \n\t" // convert 16-bit pushf to 32-bit pushfd +// "pushf \n\t" // 16-bit pushf + "pushfl \n\t" // save Eflag to stack + "popl %%eax \n\t" // get Eflag from stack into eax + "movl %%eax, %%ecx \n\t" // make another copy of Eflag in ecx + "xorl $0x200000, %%eax \n\t" // toggle ID bit in Eflag (i.e., bit 21) + "pushl %%eax \n\t" // save modified Eflag back to stack +// ".byte 0x66 \n\t" // convert 16-bit popf to 32-bit popfd +// "popf \n\t" // 16-bit popf + "popfl \n\t" // restore modified value to Eflag reg + "pushfl \n\t" // save Eflag to stack + "popl %%eax \n\t" // get Eflag from stack + "pushl %%ecx \n\t" // save original Eflag to stack + "popfl \n\t" // restore original Eflag + "xorl %%ecx, %%eax \n\t" // compare new Eflag with original Eflag + "jz 0f \n\t" // if same, CPUID instr. is not supported + + "xorl %%eax, %%eax \n\t" // set eax to zero +// ".byte 0x0f, 0xa2 \n\t" // CPUID instruction (two-byte opcode) + "cpuid \n\t" // get the CPU identification info + "cmpl $1, %%eax \n\t" // make sure eax return non-zero value + "jl 0f \n\t" // if eax is zero, MMX is not supported + + "xorl %%eax, %%eax \n\t" // set eax to zero and... + "incl %%eax \n\t" // ...increment eax to 1. This pair is + // faster than the instruction "mov eax, 1" + "cpuid \n\t" // get the CPU identification info again + "andl $0x800000, %%edx \n\t" // mask out all bits but MMX bit (23) + "cmpl $0, %%edx \n\t" // 0 = MMX not supported + "jz 0f \n\t" // non-zero = yes, MMX IS supported + + "movl $1, %%eax \n\t" // set return value to 1 + "jmp 1f \n\t" // DONE: have MMX support + + "0: \n\t" // .NOT_SUPPORTED: target label for jump instructions + "movl $0, %%eax \n\t" // set return value to 0 + "1: \n\t" // .RETURN: target label for jump instructions + "movl %%eax, _mmx_supported \n\t" // save in global static variable, too + "popl %%edx \n\t" // restore edx + "popl %%ecx \n\t" // restore ecx + "popl %%ebx \n\t" // restore ebx + +// "ret \n\t" // DONE: no MMX support + // (fall through to standard C "ret") + + : // output list (none) + + : // any variables used on input (none) + + : "%eax" // clobber list +// , "%ebx", "%ecx", "%edx" // GRR: we handle these manually +// , "memory" // if write to a variable gcc thought was in a reg +// , "cc" // "condition codes" (flag bits) + ); +#else + _mmx_supported = 0; +#endif /* PNG_MMX_CODE_SUPPORTED */ + + return _mmx_supported; +} + + +#endif /* PNG_USE_PNGGCCRD */ diff --git a/com32/lib/libpng/pngget.c b/com32/lib/libpng/pngget.c new file mode 100644 index 0000000..8eefa77 --- /dev/null +++ b/com32/lib/libpng/pngget.c @@ -0,0 +1,934 @@ + +/* pngget.c - retrieval of values from info struct + * + * libpng 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" + +png_uint_32 PNGAPI +png_get_valid(png_structp png_ptr, png_infop info_ptr, png_uint_32 flag) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->valid & flag); + else + return(0); +} + +png_uint_32 PNGAPI +png_get_rowbytes(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->rowbytes); + else + return(0); +} + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +png_bytepp PNGAPI +png_get_rows(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->row_pointers); + else + return(0); +} +#endif + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* easy access to info, added in libpng-0.99 */ +png_uint_32 PNGAPI +png_get_image_width(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->width; + } + return (0); +} + +png_uint_32 PNGAPI +png_get_image_height(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->height; + } + return (0); +} + +png_byte PNGAPI +png_get_bit_depth(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->bit_depth; + } + return (0); +} + +png_byte PNGAPI +png_get_color_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->color_type; + } + return (0); +} + +png_byte PNGAPI +png_get_filter_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->filter_type; + } + return (0); +} + +png_byte PNGAPI +png_get_interlace_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->interlace_type; + } + return (0); +} + +png_byte PNGAPI +png_get_compression_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->compression_type; + } + return (0); +} + +png_uint_32 PNGAPI +png_get_x_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_x_pixels_per_meter"); + if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER) + return (0); + else return (info_ptr->x_pixels_per_unit); + } +#else + return (0); +#endif + return (0); +} + +png_uint_32 PNGAPI +png_get_y_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_y_pixels_per_meter"); + if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER) + return (0); + else return (info_ptr->y_pixels_per_unit); + } +#else + return (0); +#endif + return (0); +} + +png_uint_32 PNGAPI +png_get_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_pixels_per_meter"); + if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER || + info_ptr->x_pixels_per_unit != info_ptr->y_pixels_per_unit) + return (0); + else return (info_ptr->x_pixels_per_unit); + } +#else + return (0); +#endif + return (0); +} + +#ifdef PNG_FLOATING_POINT_SUPPORTED +float PNGAPI +png_get_pixel_aspect_ratio(png_structp png_ptr, png_infop info_ptr) + { + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_aspect_ratio"); + if (info_ptr->x_pixels_per_unit == 0) + return ((float)0.0); + else + return ((float)((float)info_ptr->y_pixels_per_unit + /(float)info_ptr->x_pixels_per_unit)); + } +#else + return (0.0); +#endif + return ((float)0.0); +} +#endif + +png_int_32 PNGAPI +png_get_x_offset_microns(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_x_offset_microns"); + if(info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER) + return (0); + else return (info_ptr->x_offset); + } +#else + return (0); +#endif + return (0); +} + +png_int_32 PNGAPI +png_get_y_offset_microns(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_y_offset_microns"); + if(info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER) + return (0); + else return (info_ptr->y_offset); + } +#else + return (0); +#endif + return (0); +} + +png_int_32 PNGAPI +png_get_x_offset_pixels(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_x_offset_microns"); + if(info_ptr->offset_unit_type != PNG_OFFSET_PIXEL) + return (0); + else return (info_ptr->x_offset); + } +#else + return (0); +#endif + return (0); +} + +png_int_32 PNGAPI +png_get_y_offset_pixels(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_y_offset_microns"); + if(info_ptr->offset_unit_type != PNG_OFFSET_PIXEL) + return (0); + else return (info_ptr->y_offset); + } +#else + return (0); +#endif + return (0); +} + +#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) +png_uint_32 PNGAPI +png_get_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +{ + return ((png_uint_32)((float)png_get_pixels_per_meter(png_ptr, info_ptr) + *.0254 +.5)); +} + +png_uint_32 PNGAPI +png_get_x_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +{ + return ((png_uint_32)((float)png_get_x_pixels_per_meter(png_ptr, info_ptr) + *.0254 +.5)); +} + +png_uint_32 PNGAPI +png_get_y_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +{ + return ((png_uint_32)((float)png_get_y_pixels_per_meter(png_ptr, info_ptr) + *.0254 +.5)); +} + +float PNGAPI +png_get_x_offset_inches(png_structp png_ptr, png_infop info_ptr) +{ + return ((float)png_get_x_offset_microns(png_ptr, info_ptr) + *.00003937); +} + +float PNGAPI +png_get_y_offset_inches(png_structp png_ptr, png_infop info_ptr) +{ + return ((float)png_get_y_offset_microns(png_ptr, info_ptr) + *.00003937); +} + +#if defined(PNG_pHYs_SUPPORTED) +png_uint_32 PNGAPI +png_get_pHYs_dpi(png_structp png_ptr, png_infop info_ptr, + png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) +{ + png_uint_32 retval = 0; + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + { + png_debug1(1, "in %s retrieval function\n", "pHYs"); + if (res_x != NULL) + { + *res_x = info_ptr->x_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (res_y != NULL) + { + *res_y = info_ptr->y_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (unit_type != NULL) + { + *unit_type = (int)info_ptr->phys_unit_type; + retval |= PNG_INFO_pHYs; + if(*unit_type == 1) + { + if (res_x != NULL) *res_x = (png_uint_32)(*res_x * .0254 + .50); + if (res_y != NULL) *res_y = (png_uint_32)(*res_y * .0254 + .50); + } + } + } + return (retval); +} +#endif /* PNG_pHYs_SUPPORTED */ +#endif /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */ + +/* png_get_channels really belongs in here, too, but it's been around longer */ + +#endif /* PNG_EASY_ACCESS_SUPPORTED */ + +png_byte PNGAPI +png_get_channels(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->channels); + else + return (0); +} + +png_bytep PNGAPI +png_get_signature(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->signature); + else + return (NULL); +} + +#if defined(PNG_bKGD_SUPPORTED) +png_uint_32 PNGAPI +png_get_bKGD(png_structp png_ptr, png_infop info_ptr, + png_color_16p *background) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) + && background != NULL) + { + png_debug1(1, "in %s retrieval function\n", "bKGD"); + *background = &(info_ptr->background); + return (PNG_INFO_bKGD); + } + return (0); +} +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cHRM(png_structp png_ptr, png_infop info_ptr, + double *white_x, double *white_y, double *red_x, double *red_y, + double *green_x, double *green_y, double *blue_x, double *blue_y) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + { + png_debug1(1, "in %s retrieval function\n", "cHRM"); + if (white_x != NULL) + *white_x = (double)info_ptr->x_white; + if (white_y != NULL) + *white_y = (double)info_ptr->y_white; + if (red_x != NULL) + *red_x = (double)info_ptr->x_red; + if (red_y != NULL) + *red_y = (double)info_ptr->y_red; + if (green_x != NULL) + *green_x = (double)info_ptr->x_green; + if (green_y != NULL) + *green_y = (double)info_ptr->y_green; + if (blue_x != NULL) + *blue_x = (double)info_ptr->x_blue; + if (blue_y != NULL) + *blue_y = (double)info_ptr->y_blue; + return (PNG_INFO_cHRM); + } + return (0); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cHRM_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x, + png_fixed_point *red_y, png_fixed_point *green_x, png_fixed_point *green_y, + png_fixed_point *blue_x, png_fixed_point *blue_y) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + { + png_debug1(1, "in %s retrieval function\n", "cHRM"); + if (white_x != NULL) + *white_x = info_ptr->int_x_white; + if (white_y != NULL) + *white_y = info_ptr->int_y_white; + if (red_x != NULL) + *red_x = info_ptr->int_x_red; + if (red_y != NULL) + *red_y = info_ptr->int_y_red; + if (green_x != NULL) + *green_x = info_ptr->int_x_green; + if (green_y != NULL) + *green_y = info_ptr->int_y_green; + if (blue_x != NULL) + *blue_x = info_ptr->int_x_blue; + if (blue_y != NULL) + *blue_y = info_ptr->int_y_blue; + return (PNG_INFO_cHRM); + } + return (0); +} +#endif +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_gAMA(png_structp png_ptr, png_infop info_ptr, double *file_gamma) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) + && file_gamma != NULL) + { + png_debug1(1, "in %s retrieval function\n", "gAMA"); + *file_gamma = (double)info_ptr->gamma; + return (PNG_INFO_gAMA); + } + return (0); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point *int_file_gamma) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) + && int_file_gamma != NULL) + { + png_debug1(1, "in %s retrieval function\n", "gAMA"); + *int_file_gamma = info_ptr->int_gamma; + return (PNG_INFO_gAMA); + } + return (0); +} +#endif +#endif + +#if defined(PNG_sRGB_SUPPORTED) +png_uint_32 PNGAPI +png_get_sRGB(png_structp png_ptr, png_infop info_ptr, int *file_srgb_intent) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB) + && file_srgb_intent != NULL) + { + png_debug1(1, "in %s retrieval function\n", "sRGB"); + *file_srgb_intent = (int)info_ptr->srgb_intent; + return (PNG_INFO_sRGB); + } + return (0); +} +#endif + +#if defined(PNG_iCCP_SUPPORTED) +png_uint_32 PNGAPI +png_get_iCCP(png_structp png_ptr, png_infop info_ptr, + png_charpp name, int *compression_type, + png_charpp profile, png_uint_32 *proflen) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP) + && name != NULL && profile != NULL && proflen != NULL) + { + png_debug1(1, "in %s retrieval function\n", "iCCP"); + *name = info_ptr->iccp_name; + *profile = info_ptr->iccp_profile; + /* compression_type is a dummy so the API won't have to change + if we introduce multiple compression types later. */ + *proflen = (int)info_ptr->iccp_proflen; + *compression_type = (int)info_ptr->iccp_compression; + return (PNG_INFO_iCCP); + } + return (0); +} +#endif + +#if defined(PNG_sPLT_SUPPORTED) +png_uint_32 PNGAPI +png_get_sPLT(png_structp png_ptr, png_infop info_ptr, + png_sPLT_tpp spalettes) +{ + if (png_ptr != NULL && info_ptr != NULL && spalettes != NULL) + *spalettes = info_ptr->splt_palettes; + return ((png_uint_32)info_ptr->splt_palettes_num); +} +#endif + +#if defined(PNG_hIST_SUPPORTED) +png_uint_32 PNGAPI +png_get_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p *hist) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) + && hist != NULL) + { + png_debug1(1, "in %s retrieval function\n", "hIST"); + *hist = info_ptr->hist; + return (PNG_INFO_hIST); + } + return (0); +} +#endif + +png_uint_32 PNGAPI +png_get_IHDR(png_structp png_ptr, png_infop info_ptr, + png_uint_32 *width, png_uint_32 *height, int *bit_depth, + int *color_type, int *interlace_type, int *compression_type, + int *filter_type) + +{ + if (png_ptr != NULL && info_ptr != NULL && width != NULL && height != NULL && + bit_depth != NULL && color_type != NULL) + { + png_debug1(1, "in %s retrieval function\n", "IHDR"); + *width = info_ptr->width; + *height = info_ptr->height; + *bit_depth = info_ptr->bit_depth; + if (info_ptr->bit_depth < 1 || info_ptr->bit_depth > 16) + png_error(png_ptr, "Invalid bit depth"); + *color_type = info_ptr->color_type; + if (info_ptr->color_type > 6) + png_error(png_ptr, "Invalid color type"); + if (compression_type != NULL) + *compression_type = info_ptr->compression_type; + if (filter_type != NULL) + *filter_type = info_ptr->filter_type; + if (interlace_type != NULL) + *interlace_type = info_ptr->interlace_type; + + /* check for potential overflow of rowbytes */ + if (*width == 0 || *width > PNG_UINT_31_MAX) + png_error(png_ptr, "Invalid image width"); + if (*height == 0 || *height > PNG_UINT_31_MAX) + png_error(png_ptr, "Invalid image height"); + if (info_ptr->width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 64 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + { + png_warning(png_ptr, + "Width too large for libpng to process image data."); + } + return (1); + } + return (0); +} + +#if defined(PNG_oFFs_SUPPORTED) +png_uint_32 PNGAPI +png_get_oFFs(png_structp png_ptr, png_infop info_ptr, + png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs) + && offset_x != NULL && offset_y != NULL && unit_type != NULL) + { + png_debug1(1, "in %s retrieval function\n", "oFFs"); + *offset_x = info_ptr->x_offset; + *offset_y = info_ptr->y_offset; + *unit_type = (int)info_ptr->offset_unit_type; + return (PNG_INFO_oFFs); + } + return (0); +} +#endif + +#if defined(PNG_pCAL_SUPPORTED) +png_uint_32 PNGAPI +png_get_pCAL(png_structp png_ptr, png_infop info_ptr, + png_charp *purpose, png_int_32 *X0, png_int_32 *X1, int *type, int *nparams, + png_charp *units, png_charpp *params) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL) + && purpose != NULL && X0 != NULL && X1 != NULL && type != NULL && + nparams != NULL && units != NULL && params != NULL) + { + png_debug1(1, "in %s retrieval function\n", "pCAL"); + *purpose = info_ptr->pcal_purpose; + *X0 = info_ptr->pcal_X0; + *X1 = info_ptr->pcal_X1; + *type = (int)info_ptr->pcal_type; + *nparams = (int)info_ptr->pcal_nparams; + *units = info_ptr->pcal_units; + *params = info_ptr->pcal_params; + return (PNG_INFO_pCAL); + } + return (0); +} +#endif + +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_sCAL(png_structp png_ptr, png_infop info_ptr, + int *unit, double *width, double *height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL)) + { + *unit = info_ptr->scal_unit; + *width = info_ptr->scal_pixel_width; + *height = info_ptr->scal_pixel_height; + return (PNG_INFO_sCAL); + } + return(0); +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_sCAL_s(png_structp png_ptr, png_infop info_ptr, + int *unit, png_charpp width, png_charpp height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL)) + { + *unit = info_ptr->scal_unit; + *width = info_ptr->scal_s_width; + *height = info_ptr->scal_s_height; + return (PNG_INFO_sCAL); + } + return(0); +} +#endif +#endif +#endif + +#if defined(PNG_pHYs_SUPPORTED) +png_uint_32 PNGAPI +png_get_pHYs(png_structp png_ptr, png_infop info_ptr, + png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) +{ + png_uint_32 retval = 0; + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pHYs)) + { + png_debug1(1, "in %s retrieval function\n", "pHYs"); + if (res_x != NULL) + { + *res_x = info_ptr->x_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (res_y != NULL) + { + *res_y = info_ptr->y_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (unit_type != NULL) + { + *unit_type = (int)info_ptr->phys_unit_type; + retval |= PNG_INFO_pHYs; + } + } + return (retval); +} +#endif + +png_uint_32 PNGAPI +png_get_PLTE(png_structp png_ptr, png_infop info_ptr, png_colorp *palette, + int *num_palette) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_PLTE) + && palette != NULL) + { + png_debug1(1, "in %s retrieval function\n", "PLTE"); + *palette = info_ptr->palette; + *num_palette = info_ptr->num_palette; + png_debug1(3, "num_palette = %d\n", *num_palette); + return (PNG_INFO_PLTE); + } + return (0); +} + +#if defined(PNG_sBIT_SUPPORTED) +png_uint_32 PNGAPI +png_get_sBIT(png_structp png_ptr, png_infop info_ptr, png_color_8p *sig_bit) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT) + && sig_bit != NULL) + { + png_debug1(1, "in %s retrieval function\n", "sBIT"); + *sig_bit = &(info_ptr->sig_bit); + return (PNG_INFO_sBIT); + } + return (0); +} +#endif + +#if defined(PNG_TEXT_SUPPORTED) +png_uint_32 PNGAPI +png_get_text(png_structp png_ptr, png_infop info_ptr, png_textp *text_ptr, + int *num_text) +{ + if (png_ptr != NULL && info_ptr != NULL && info_ptr->num_text > 0) + { + png_debug1(1, "in %s retrieval function\n", + (png_ptr->chunk_name[0] == '\0' ? "text" + : (png_const_charp)png_ptr->chunk_name)); + if (text_ptr != NULL) + *text_ptr = info_ptr->text; + if (num_text != NULL) + *num_text = info_ptr->num_text; + return ((png_uint_32)info_ptr->num_text); + } + if (num_text != NULL) + *num_text = 0; + return(0); +} +#endif + +#if defined(PNG_tIME_SUPPORTED) +png_uint_32 PNGAPI +png_get_tIME(png_structp png_ptr, png_infop info_ptr, png_timep *mod_time) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME) + && mod_time != NULL) + { + png_debug1(1, "in %s retrieval function\n", "tIME"); + *mod_time = &(info_ptr->mod_time); + return (PNG_INFO_tIME); + } + return (0); +} +#endif + +#if defined(PNG_tRNS_SUPPORTED) +png_uint_32 PNGAPI +png_get_tRNS(png_structp png_ptr, png_infop info_ptr, + png_bytep *trans, int *num_trans, png_color_16p *trans_values) +{ + png_uint_32 retval = 0; + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + png_debug1(1, "in %s retrieval function\n", "tRNS"); + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (trans != NULL) + { + *trans = info_ptr->trans; + retval |= PNG_INFO_tRNS; + } + if (trans_values != NULL) + *trans_values = &(info_ptr->trans_values); + } + else /* if (info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) */ + { + if (trans_values != NULL) + { + *trans_values = &(info_ptr->trans_values); + retval |= PNG_INFO_tRNS; + } + if(trans != NULL) + *trans = NULL; + } + if(num_trans != NULL) + { + *num_trans = info_ptr->num_trans; + retval |= PNG_INFO_tRNS; + } + } + return (retval); +} +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +png_uint_32 PNGAPI +png_get_unknown_chunks(png_structp png_ptr, png_infop info_ptr, + png_unknown_chunkpp unknowns) +{ + if (png_ptr != NULL && info_ptr != NULL && unknowns != NULL) + *unknowns = info_ptr->unknown_chunks; + return ((png_uint_32)info_ptr->unknown_chunks_num); +} +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +png_byte PNGAPI +png_get_rgb_to_gray_status (png_structp png_ptr) +{ + return (png_byte)(png_ptr? png_ptr->rgb_to_gray_status : 0); +} +#endif + +#if defined(PNG_USER_CHUNKS_SUPPORTED) +png_voidp PNGAPI +png_get_user_chunk_ptr(png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_chunk_ptr : NULL); +} +#endif + +#ifdef PNG_WRITE_SUPPORTED +png_uint_32 PNGAPI +png_get_compression_buffer_size(png_structp png_ptr) +{ + return (png_uint_32)(png_ptr? png_ptr->zbuf_size : 0L); +} +#endif + +#ifndef PNG_1_0_X +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +/* this function was added to libpng 1.2.0 and should exist by default */ +png_uint_32 PNGAPI +png_get_asm_flags (png_structp png_ptr) +{ + return (png_uint_32)(png_ptr? png_ptr->asm_flags : 0L); +} + +/* this function was added to libpng 1.2.0 and should exist by default */ +png_uint_32 PNGAPI +png_get_asm_flagmask (int flag_select) +{ + png_uint_32 settable_asm_flags = 0; + + if (flag_select & PNG_SELECT_READ) + settable_asm_flags |= + PNG_ASM_FLAG_MMX_READ_COMBINE_ROW | + PNG_ASM_FLAG_MMX_READ_INTERLACE | + PNG_ASM_FLAG_MMX_READ_FILTER_SUB | + PNG_ASM_FLAG_MMX_READ_FILTER_UP | + PNG_ASM_FLAG_MMX_READ_FILTER_AVG | + PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ; + /* no non-MMX flags yet */ + +#if 0 + /* GRR: no write-flags yet, either, but someday... */ + if (flag_select & PNG_SELECT_WRITE) + settable_asm_flags |= + PNG_ASM_FLAG_MMX_WRITE_ [whatever] ; +#endif /* 0 */ + + return settable_asm_flags; /* _theoretically_ settable capabilities only */ +} +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) + /* GRR: could add this: && defined(PNG_MMX_CODE_SUPPORTED) */ +/* this function was added to libpng 1.2.0 */ +png_uint_32 PNGAPI +png_get_mmx_flagmask (int flag_select, int *compilerID) +{ + png_uint_32 settable_mmx_flags = 0; + + if (flag_select & PNG_SELECT_READ) + settable_mmx_flags |= + PNG_ASM_FLAG_MMX_READ_COMBINE_ROW | + PNG_ASM_FLAG_MMX_READ_INTERLACE | + PNG_ASM_FLAG_MMX_READ_FILTER_SUB | + PNG_ASM_FLAG_MMX_READ_FILTER_UP | + PNG_ASM_FLAG_MMX_READ_FILTER_AVG | + PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ; +#if 0 + /* GRR: no MMX write support yet, but someday... */ + if (flag_select & PNG_SELECT_WRITE) + settable_mmx_flags |= + PNG_ASM_FLAG_MMX_WRITE_ [whatever] ; +#endif /* 0 */ + + if (compilerID != NULL) { +#ifdef PNG_USE_PNGVCRD + *compilerID = 1; /* MSVC */ +#else +#ifdef PNG_USE_PNGGCCRD + *compilerID = 2; /* gcc/gas */ +#else + *compilerID = -1; /* unknown (i.e., no asm/MMX code compiled) */ +#endif +#endif + } + + return settable_mmx_flags; /* _theoretically_ settable capabilities only */ +} + +/* this function was added to libpng 1.2.0 */ +png_byte PNGAPI +png_get_mmx_bitdepth_threshold (png_structp png_ptr) +{ + return (png_byte)(png_ptr? png_ptr->mmx_bitdepth_threshold : 0); +} + +/* this function was added to libpng 1.2.0 */ +png_uint_32 PNGAPI +png_get_mmx_rowbytes_threshold (png_structp png_ptr) +{ + return (png_uint_32)(png_ptr? png_ptr->mmx_rowbytes_threshold : 0L); +} +#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */ + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +/* these functions were added to libpng 1.2.6 */ +png_uint_32 PNGAPI +png_get_user_width_max (png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_width_max : 0); +} +png_uint_32 PNGAPI +png_get_user_height_max (png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_height_max : 0); +} +#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ + +#endif /* ?PNG_1_0_X */ diff --git a/com32/lib/libpng/pngmem.c b/com32/lib/libpng/pngmem.c new file mode 100644 index 0000000..f1cb693 --- /dev/null +++ b/com32/lib/libpng/pngmem.c @@ -0,0 +1,595 @@ + +/* pngmem.c - stub functions for memory allocation + * + * libpng version 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file provides a location for all memory allocation. Users who + * need special memory handling are expected to supply replacement + * functions for png_malloc() and png_free(), and to use + * png_create_read_struct_2() and png_create_write_struct_2() to + * identify the replacement functions. + */ + +#define PNG_INTERNAL +#include "png.h" + +/* Borland DOS special memory handler */ +#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) +/* if you change this, be sure to change the one in png.h also */ + +/* Allocate memory for a png_struct. The malloc and memset can be replaced + by a single call to calloc() if this is thought to improve performance. */ +png_voidp /* PRIVATE */ +png_create_struct(int type) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_struct_2(type, png_malloc_ptr_NULL, png_voidp_NULL)); +} + +/* Alternate version of png_create_struct, for use with user-defined malloc. */ +png_voidp /* PRIVATE */ +png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + png_size_t size; + png_voidp struct_ptr; + + if (type == PNG_STRUCT_INFO) + size = png_sizeof(png_info); + else if (type == PNG_STRUCT_PNG) + size = png_sizeof(png_struct); + else + return (png_get_copyright(NULL)); + +#ifdef PNG_USER_MEM_SUPPORTED + if(malloc_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + struct_ptr = (*(malloc_fn))(png_ptr, (png_uint_32)size); + } + else +#endif /* PNG_USER_MEM_SUPPORTED */ + struct_ptr = (png_voidp)farmalloc(size); + if (struct_ptr != NULL) + png_memset(struct_ptr, 0, size); + return (struct_ptr); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct(png_voidp struct_ptr) +{ +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2(struct_ptr, png_free_ptr_NULL, png_voidp_NULL); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, + png_voidp mem_ptr) +{ +#endif + if (struct_ptr != NULL) + { +#ifdef PNG_USER_MEM_SUPPORTED + if(free_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + (*(free_fn))(png_ptr, struct_ptr); + return; + } +#endif /* PNG_USER_MEM_SUPPORTED */ + farfree (struct_ptr); + } +} + +/* Allocate memory. For reasonable files, size should never exceed + * 64K. However, zlib may allocate more then 64K if you don't tell + * it not to. See zconf.h and png.h for more information. zlib does + * need to allocate exactly 64K, so whatever you call here must + * have the ability to do that. + * + * Borland seems to have a problem in DOS mode for exactly 64K. + * It gives you a segment with an offset of 8 (perhaps to store its + * memory stuff). zlib doesn't like this at all, so we have to + * detect and deal with it. This code should not be needed in + * Windows or OS/2 modes, and only in 16 bit mode. This code has + * been updated by Alexander Lehmann for version 0.89 to waste less + * memory. + * + * Note that we can't use png_size_t for the "size" declaration, + * since on some systems a png_size_t is a 16-bit quantity, and as a + * result, we would be truncating potentially larger memory requests + * (which should cause a fatal error) and introducing major problems. + */ + +png_voidp PNGAPI +png_malloc(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; + + if (png_ptr == NULL || size == 0) + return (NULL); + +#ifdef PNG_USER_MEM_SUPPORTED + if(png_ptr->malloc_fn != NULL) + ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size)); + else + ret = (png_malloc_default(png_ptr, size)); + if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of memory!"); + return (ret); +} + +png_voidp PNGAPI +png_malloc_default(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; +#endif /* PNG_USER_MEM_SUPPORTED */ + +#ifdef PNG_MAX_MALLOC_64K + if (size > (png_uint_32)65536L) + { + png_warning(png_ptr, "Cannot Allocate > 64K"); + ret = NULL; + } + else +#endif + + if (size != (size_t)size) + ret = NULL; + else if (size == (png_uint_32)65536L) + { + if (png_ptr->offset_table == NULL) + { + /* try to see if we need to do any of this fancy stuff */ + ret = farmalloc(size); + if (ret == NULL || ((png_size_t)ret & 0xffff)) + { + int num_blocks; + png_uint_32 total_size; + png_bytep table; + int i; + png_byte huge * hptr; + + if (ret != NULL) + { + farfree(ret); + ret = NULL; + } + + if(png_ptr->zlib_window_bits > 14) + num_blocks = (int)(1 << (png_ptr->zlib_window_bits - 14)); + else + num_blocks = 1; + if (png_ptr->zlib_mem_level >= 7) + num_blocks += (int)(1 << (png_ptr->zlib_mem_level - 7)); + else + num_blocks++; + + total_size = ((png_uint_32)65536L) * (png_uint_32)num_blocks+16; + + table = farmalloc(total_size); + + if (table == NULL) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out Of Memory."); /* Note "O" and "M" */ + else + png_warning(png_ptr, "Out Of Memory."); +#endif + return (NULL); + } + + if ((png_size_t)table & 0xfff0) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, + "Farmalloc didn't return normalized pointer"); + else + png_warning(png_ptr, + "Farmalloc didn't return normalized pointer"); +#endif + return (NULL); + } + + png_ptr->offset_table = table; + png_ptr->offset_table_ptr = farmalloc(num_blocks * + png_sizeof (png_bytep)); + + if (png_ptr->offset_table_ptr == NULL) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out Of memory."); /* Note "O" and "M" */ + else + png_warning(png_ptr, "Out Of memory."); +#endif + return (NULL); + } + + hptr = (png_byte huge *)table; + if ((png_size_t)hptr & 0xf) + { + hptr = (png_byte huge *)((long)(hptr) & 0xfffffff0L); + hptr = hptr + 16L; /* "hptr += 16L" fails on Turbo C++ 3.0 */ + } + for (i = 0; i < num_blocks; i++) + { + png_ptr->offset_table_ptr[i] = (png_bytep)hptr; + hptr = hptr + (png_uint_32)65536L; /* "+=" fails on TC++3.0 */ + } + + png_ptr->offset_table_number = num_blocks; + png_ptr->offset_table_count = 0; + png_ptr->offset_table_count_free = 0; + } + } + + if (png_ptr->offset_table_count >= png_ptr->offset_table_number) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of Memory."); /* Note "o" and "M" */ + else + png_warning(png_ptr, "Out of Memory."); +#endif + return (NULL); + } + + ret = png_ptr->offset_table_ptr[png_ptr->offset_table_count++]; + } + else + ret = farmalloc(size); + +#ifndef PNG_USER_MEM_SUPPORTED + if (ret == NULL) + { + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of memory."); /* Note "o" and "m" */ + else + png_warning(png_ptr, "Out of memory."); /* Note "o" and "m" */ + } +#endif + + return (ret); +} + +/* free a pointer allocated by png_malloc(). In the default + configuration, png_ptr is not used, but is passed in case it + is needed. If ptr is NULL, return without taking any action. */ +void PNGAPI +png_free(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr->free_fn != NULL) + { + (*(png_ptr->free_fn))(png_ptr, ptr); + return; + } + else png_free_default(png_ptr, ptr); +} + +void PNGAPI +png_free_default(png_structp png_ptr, png_voidp ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + + if (png_ptr->offset_table != NULL) + { + int i; + + for (i = 0; i < png_ptr->offset_table_count; i++) + { + if (ptr == png_ptr->offset_table_ptr[i]) + { + ptr = NULL; + png_ptr->offset_table_count_free++; + break; + } + } + if (png_ptr->offset_table_count_free == png_ptr->offset_table_count) + { + farfree(png_ptr->offset_table); + farfree(png_ptr->offset_table_ptr); + png_ptr->offset_table = NULL; + png_ptr->offset_table_ptr = NULL; + } + } + + if (ptr != NULL) + { + farfree(ptr); + } +} + +#else /* Not the Borland DOS special memory handler */ + +/* Allocate memory for a png_struct or a png_info. The malloc and + memset can be replaced by a single call to calloc() if this is thought + to improve performance noticably. */ +png_voidp /* PRIVATE */ +png_create_struct(int type) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_struct_2(type, png_malloc_ptr_NULL, png_voidp_NULL)); +} + +/* Allocate memory for a png_struct or a png_info. The malloc and + memset can be replaced by a single call to calloc() if this is thought + to improve performance noticably. */ +png_voidp /* PRIVATE */ +png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + png_size_t size; + png_voidp struct_ptr; + + if (type == PNG_STRUCT_INFO) + size = png_sizeof(png_info); + else if (type == PNG_STRUCT_PNG) + size = png_sizeof(png_struct); + else + return (NULL); + +#ifdef PNG_USER_MEM_SUPPORTED + if(malloc_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + struct_ptr = (*(malloc_fn))(png_ptr, size); + if (struct_ptr != NULL) + png_memset(struct_ptr, 0, size); + return (struct_ptr); + } +#endif /* PNG_USER_MEM_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(__FLAT__) + struct_ptr = (png_voidp)farmalloc(size); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + struct_ptr = (png_voidp)halloc(size,1); +# else + struct_ptr = (png_voidp)malloc(size); +# endif +#endif + if (struct_ptr != NULL) + png_memset(struct_ptr, 0, size); + + return (struct_ptr); +} + + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct(png_voidp struct_ptr) +{ +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2(struct_ptr, png_free_ptr_NULL, png_voidp_NULL); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, + png_voidp mem_ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + if (struct_ptr != NULL) + { +#ifdef PNG_USER_MEM_SUPPORTED + if(free_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + (*(free_fn))(png_ptr, struct_ptr); + return; + } +#endif /* PNG_USER_MEM_SUPPORTED */ +#if defined(__TURBOC__) && !defined(__FLAT__) + farfree(struct_ptr); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + hfree(struct_ptr); +# else + free(struct_ptr); +# endif +#endif + } +} + +/* Allocate memory. For reasonable files, size should never exceed + 64K. However, zlib may allocate more then 64K if you don't tell + it not to. See zconf.h and png.h for more information. zlib does + need to allocate exactly 64K, so whatever you call here must + have the ability to do that. */ + +png_voidp PNGAPI +png_malloc(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr == NULL || size == 0) + return (NULL); + + if(png_ptr->malloc_fn != NULL) + ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size)); + else + ret = (png_malloc_default(png_ptr, size)); + if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of Memory!"); + return (ret); +} + +png_voidp PNGAPI +png_malloc_default(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; +#endif /* PNG_USER_MEM_SUPPORTED */ + + if (png_ptr == NULL || size == 0) + return (NULL); + +#ifdef PNG_MAX_MALLOC_64K + if (size > (png_uint_32)65536L) + { +#ifndef PNG_USER_MEM_SUPPORTED + if(png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Cannot Allocate > 64K"); + else +#endif + return NULL; + } +#endif + + /* Check for overflow */ +#if defined(__TURBOC__) && !defined(__FLAT__) + if (size != (unsigned long)size) + ret = NULL; + else + ret = farmalloc(size); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + if (size != (unsigned long)size) + ret = NULL; + else + ret = halloc(size, 1); +# else + if (size != (size_t)size) + ret = NULL; + else + ret = malloc((size_t)size); +# endif +#endif + +#ifndef PNG_USER_MEM_SUPPORTED + if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of Memory"); +#endif + + return (ret); +} + +/* Free a pointer allocated by png_malloc(). If ptr is NULL, return + without taking any action. */ +void PNGAPI +png_free(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr->free_fn != NULL) + { + (*(png_ptr->free_fn))(png_ptr, ptr); + return; + } + else png_free_default(png_ptr, ptr); +} +void PNGAPI +png_free_default(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#endif /* PNG_USER_MEM_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(__FLAT__) + farfree(ptr); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + hfree(ptr); +# else + free(ptr); +# endif +#endif +} + +#endif /* Not Borland DOS special memory handler */ + +#if defined(PNG_1_0_X) +# define png_malloc_warn png_malloc +#else +/* This function was added at libpng version 1.2.3. The png_malloc_warn() + * function will set up png_malloc() to issue a png_warning and return NULL + * instead of issuing a png_error, if it fails to allocate the requested + * memory. + */ +png_voidp PNGAPI +png_malloc_warn(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ptr; + png_uint_32 save_flags=png_ptr->flags; + + png_ptr->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK; + ptr = (png_voidp)png_malloc((png_structp)png_ptr, size); + png_ptr->flags=save_flags; + return(ptr); +} +#endif + +png_voidp PNGAPI +png_memcpy_check (png_structp png_ptr, png_voidp s1, png_voidp s2, + png_uint_32 length) +{ + png_size_t size; + + size = (png_size_t)length; + if ((png_uint_32)size != length) + png_error(png_ptr,"Overflow in png_memcpy_check."); + + return(png_memcpy (s1, s2, size)); +} + +png_voidp PNGAPI +png_memset_check (png_structp png_ptr, png_voidp s1, int value, + png_uint_32 length) +{ + png_size_t size; + + size = (png_size_t)length; + if ((png_uint_32)size != length) + png_error(png_ptr,"Overflow in png_memset_check."); + + return (png_memset (s1, value, size)); + +} + +#ifdef PNG_USER_MEM_SUPPORTED +/* This function is called when the application wants to use another method + * of allocating and freeing memory. + */ +void PNGAPI +png_set_mem_fn(png_structp png_ptr, png_voidp mem_ptr, png_malloc_ptr + malloc_fn, png_free_ptr free_fn) +{ + png_ptr->mem_ptr = mem_ptr; + png_ptr->malloc_fn = malloc_fn; + png_ptr->free_fn = free_fn; +} + +/* This function returns a pointer to the mem_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy and png_read_destroy are called. + */ +png_voidp PNGAPI +png_get_mem_ptr(png_structp png_ptr) +{ + return ((png_voidp)png_ptr->mem_ptr); +} +#endif /* PNG_USER_MEM_SUPPORTED */ diff --git a/com32/lib/libpng/pngpread.c b/com32/lib/libpng/pngpread.c new file mode 100644 index 0000000..7ef6fa4 --- /dev/null +++ b/com32/lib/libpng/pngpread.c @@ -0,0 +1,1573 @@ + +/* pngpread.c - read a png file in push mode + * + * libpng version 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + +/* push model modes */ +#define PNG_READ_SIG_MODE 0 +#define PNG_READ_CHUNK_MODE 1 +#define PNG_READ_IDAT_MODE 2 +#define PNG_SKIP_MODE 3 +#define PNG_READ_tEXt_MODE 4 +#define PNG_READ_zTXt_MODE 5 +#define PNG_READ_DONE_MODE 6 +#define PNG_READ_iTXt_MODE 7 +#define PNG_ERROR_MODE 8 + +void PNGAPI +png_process_data(png_structp png_ptr, png_infop info_ptr, + png_bytep buffer, png_size_t buffer_size) +{ + png_push_restore_buffer(png_ptr, buffer, buffer_size); + + while (png_ptr->buffer_size) + { + png_process_some_data(png_ptr, info_ptr); + } +} + +/* What we do with the incoming data depends on what we were previously + * doing before we ran out of data... + */ +void /* PRIVATE */ +png_process_some_data(png_structp png_ptr, png_infop info_ptr) +{ + switch (png_ptr->process_mode) + { + case PNG_READ_SIG_MODE: + { + png_push_read_sig(png_ptr, info_ptr); + break; + } + case PNG_READ_CHUNK_MODE: + { + png_push_read_chunk(png_ptr, info_ptr); + break; + } + case PNG_READ_IDAT_MODE: + { + png_push_read_IDAT(png_ptr); + break; + } +#if defined(PNG_READ_tEXt_SUPPORTED) + case PNG_READ_tEXt_MODE: + { + png_push_read_tEXt(png_ptr, info_ptr); + break; + } +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + case PNG_READ_zTXt_MODE: + { + png_push_read_zTXt(png_ptr, info_ptr); + break; + } +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + case PNG_READ_iTXt_MODE: + { + png_push_read_iTXt(png_ptr, info_ptr); + break; + } +#endif + case PNG_SKIP_MODE: + { + png_push_crc_finish(png_ptr); + break; + } + default: + { + png_ptr->buffer_size = 0; + break; + } + } +} + +/* Read any remaining signature bytes from the stream and compare them with + * the correct PNG signature. It is possible that this routine is called + * with bytes already read from the signature, either because they have been + * checked by the calling application, or because of multiple calls to this + * routine. + */ +void /* PRIVATE */ +png_push_read_sig(png_structp png_ptr, png_infop info_ptr) +{ + png_size_t num_checked = png_ptr->sig_bytes, + num_to_check = 8 - num_checked; + + if (png_ptr->buffer_size < num_to_check) + { + num_to_check = png_ptr->buffer_size; + } + + png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]), + num_to_check); + png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes+num_to_check); + + if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) + { + if (num_checked < 4 && + png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) + png_error(png_ptr, "Not a PNG file"); + else + png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + } + else + { + if (png_ptr->sig_bytes >= 8) + { + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + } + } +} + +void /* PRIVATE */ +png_push_read_chunk(png_structp png_ptr, png_infop info_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IHDR; + PNG_IDAT; + PNG_IEND; + PNG_PLTE; +#if defined(PNG_READ_bKGD_SUPPORTED) + PNG_bKGD; +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + PNG_cHRM; +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + PNG_gAMA; +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + PNG_hIST; +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + PNG_iCCP; +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + PNG_iTXt; +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + PNG_oFFs; +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + PNG_pCAL; +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + PNG_pHYs; +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + PNG_sBIT; +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + PNG_sCAL; +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + PNG_sRGB; +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + PNG_sPLT; +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + PNG_tEXt; +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + PNG_tIME; +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + PNG_tRNS; +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + PNG_zTXt; +#endif +#endif /* PNG_USE_LOCAL_ARRAYS */ + /* First we make sure we have enough data for the 4 byte chunk name + * and the 4 byte chunk length before proceeding with decoding the + * chunk data. To fully decode each of these chunks, we also make + * sure we have enough data in the buffer for the 4 byte CRC at the + * end of every chunk (except IDAT, which is handled separately). + */ + if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) + { + png_byte chunk_length[4]; + + if (png_ptr->buffer_size < 8) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_fill_buffer(png_ptr, chunk_length, 4); + png_ptr->push_length = png_get_uint_31(png_ptr,chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; + } + + if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length); + } + else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length); + + png_ptr->process_mode = PNG_READ_DONE_MODE; + png_push_have_end(png_ptr, info_ptr); + } +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_ptr->mode |= PNG_HAVE_IDAT; + png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length); + if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_ptr->mode |= PNG_HAVE_PLTE; + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + } + } +#endif + else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length); + } + else if (!png_memcmp(png_ptr->chunk_name, (png_bytep)png_IDAT, 4)) + { + /* If we reach an IDAT chunk, this means we have read all of the + * header chunks, and we can start reading the image (or if this + * is called after the image has been read - we have an error). + */ + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + { + if (png_ptr->push_length == 0) + return; + + if (png_ptr->mode & PNG_AFTER_IDAT) + png_error(png_ptr, "Too many IDAT's found"); + } + + png_ptr->idat_size = png_ptr->push_length; + png_ptr->mode |= PNG_HAVE_IDAT; + png_ptr->process_mode = PNG_READ_IDAT_MODE; + png_push_have_info(png_ptr, info_ptr); + png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes; + png_ptr->zstream.next_out = png_ptr->row_buf; + return; + } +#if defined(PNG_READ_gAMA_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_bKGD_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_push_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_push_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_push_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length); + } +#endif + else + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_push_handle_unknown(png_ptr, info_ptr, png_ptr->push_length); + } + + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; +} + +void /* PRIVATE */ +png_push_crc_skip(png_structp png_ptr, png_uint_32 skip) +{ + png_ptr->process_mode = PNG_SKIP_MODE; + png_ptr->skip_length = skip; +} + +void /* PRIVATE */ +png_push_crc_finish(png_structp png_ptr) +{ + if (png_ptr->skip_length && png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (png_ptr->skip_length < (png_uint_32)png_ptr->save_buffer_size) + save_size = (png_size_t)png_ptr->skip_length; + else + save_size = png_ptr->save_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); + + png_ptr->skip_length -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (png_ptr->skip_length && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (png_ptr->skip_length < (png_uint_32)png_ptr->current_buffer_size) + save_size = (png_size_t)png_ptr->skip_length; + else + save_size = png_ptr->current_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_ptr->skip_length -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } + if (!png_ptr->skip_length) + { + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_crc_finish(png_ptr, 0); + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + } +} + +void PNGAPI +png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) +{ + png_bytep ptr; + + ptr = buffer; + if (png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (length < png_ptr->save_buffer_size) + save_size = length; + else + save_size = png_ptr->save_buffer_size; + + png_memcpy(ptr, png_ptr->save_buffer_ptr, save_size); + length -= save_size; + ptr += save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (length && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (length < png_ptr->current_buffer_size) + save_size = length; + else + save_size = png_ptr->current_buffer_size; + + png_memcpy(ptr, png_ptr->current_buffer_ptr, save_size); + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } +} + +void /* PRIVATE */ +png_push_save_buffer(png_structp png_ptr) +{ + if (png_ptr->save_buffer_size) + { + if (png_ptr->save_buffer_ptr != png_ptr->save_buffer) + { + png_size_t i,istop; + png_bytep sp; + png_bytep dp; + + istop = png_ptr->save_buffer_size; + for (i = 0, sp = png_ptr->save_buffer_ptr, dp = png_ptr->save_buffer; + i < istop; i++, sp++, dp++) + { + *dp = *sp; + } + } + } + if (png_ptr->save_buffer_size + png_ptr->current_buffer_size > + png_ptr->save_buffer_max) + { + png_size_t new_max; + png_bytep old_buffer; + + if (png_ptr->save_buffer_size > PNG_SIZE_MAX - + (png_ptr->current_buffer_size + 256)) + { + png_error(png_ptr, "Potential overflow of save_buffer"); + } + new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256; + old_buffer = png_ptr->save_buffer; + png_ptr->save_buffer = (png_bytep)png_malloc(png_ptr, + (png_uint_32)new_max); + png_memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size); + png_free(png_ptr, old_buffer); + png_ptr->save_buffer_max = new_max; + } + if (png_ptr->current_buffer_size) + { + png_memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size, + png_ptr->current_buffer_ptr, png_ptr->current_buffer_size); + png_ptr->save_buffer_size += png_ptr->current_buffer_size; + png_ptr->current_buffer_size = 0; + } + png_ptr->save_buffer_ptr = png_ptr->save_buffer; + png_ptr->buffer_size = 0; +} + +void /* PRIVATE */ +png_push_restore_buffer(png_structp png_ptr, png_bytep buffer, + png_size_t buffer_length) +{ + png_ptr->current_buffer = buffer; + png_ptr->current_buffer_size = buffer_length; + png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size; + png_ptr->current_buffer_ptr = png_ptr->current_buffer; +} + +void /* PRIVATE */ +png_push_read_IDAT(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IDAT; +#endif + if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) + { + png_byte chunk_length[4]; + + if (png_ptr->buffer_size < 8) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_fill_buffer(png_ptr, chunk_length, 4); + png_ptr->push_length = png_get_uint_31(png_ptr,chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; + + if (png_memcmp(png_ptr->chunk_name, (png_bytep)png_IDAT, 4)) + { + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + png_error(png_ptr, "Not enough compressed data"); + return; + } + + png_ptr->idat_size = png_ptr->push_length; + } + if (png_ptr->idat_size && png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (png_ptr->idat_size < (png_uint_32)png_ptr->save_buffer_size) + { + save_size = (png_size_t)png_ptr->idat_size; + /* check for overflow */ + if((png_uint_32)save_size != png_ptr->idat_size) + png_error(png_ptr, "save_size overflowed in pngpread"); + } + else + save_size = png_ptr->save_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size); + png_ptr->idat_size -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (png_ptr->idat_size && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (png_ptr->idat_size < (png_uint_32)png_ptr->current_buffer_size) + { + save_size = (png_size_t)png_ptr->idat_size; + /* check for overflow */ + if((png_uint_32)save_size != png_ptr->idat_size) + png_error(png_ptr, "save_size overflowed in pngpread"); + } + else + save_size = png_ptr->current_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_ptr->idat_size -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } + if (!png_ptr->idat_size) + { + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_crc_finish(png_ptr, 0); + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; + png_ptr->mode |= PNG_AFTER_IDAT; + } +} + +void /* PRIVATE */ +png_process_IDAT_data(png_structp png_ptr, png_bytep buffer, + png_size_t buffer_length) +{ + int ret; + + if ((png_ptr->flags & PNG_FLAG_ZLIB_FINISHED) && buffer_length) + png_error(png_ptr, "Extra compression data"); + + png_ptr->zstream.next_in = buffer; + png_ptr->zstream.avail_in = (uInt)buffer_length; + for(;;) + { + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret != Z_OK) + { + if (ret == Z_STREAM_END) + { + if (png_ptr->zstream.avail_in) + png_error(png_ptr, "Extra compressed data"); + if (!(png_ptr->zstream.avail_out)) + { + png_push_process_row(png_ptr); + } + + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + else if (ret == Z_BUF_ERROR) + break; + else + png_error(png_ptr, "Decompression Error"); + } + if (!(png_ptr->zstream.avail_out)) + { + if (( +#if defined(PNG_READ_INTERLACING_SUPPORTED) + png_ptr->interlaced && png_ptr->pass > 6) || + (!png_ptr->interlaced && +#endif + png_ptr->row_number == png_ptr->num_rows)) + { + if (png_ptr->zstream.avail_in) + png_warning(png_ptr, "Too much data in IDAT chunks"); + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + png_push_process_row(png_ptr); + png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes; + png_ptr->zstream.next_out = png_ptr->row_buf; + } + else + break; + } +} + +void /* PRIVATE */ +png_push_process_row(png_structp png_ptr) +{ + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->iwidth; + png_ptr->row_info.channels = png_ptr->channels; + png_ptr->row_info.bit_depth = png_ptr->bit_depth; + png_ptr->row_info.pixel_depth = png_ptr->pixel_depth; + + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + png_read_filter_row(png_ptr, &(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->prev_row + 1, + (int)(png_ptr->row_buf[0])); + + png_memcpy_check(png_ptr, png_ptr->prev_row, png_ptr->row_buf, + png_ptr->rowbytes + 1); + + if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA)) + png_do_read_transformations(png_ptr); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) + /* blow up interlaced rows to full size */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + if (png_ptr->pass < 6) +/* old interface (pre-1.0.9): + png_do_read_interlace(&(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations); + */ + png_do_read_interlace(png_ptr); + + switch (png_ptr->pass) + { + case 0: + { + int i; + for (i = 0; i < 8 && png_ptr->pass == 0; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); /* updates png_ptr->pass */ + } + if (png_ptr->pass == 2) /* pass 1 might be empty */ + { + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + if (png_ptr->pass == 4 && png_ptr->height <= 4) + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + if (png_ptr->pass == 6 && png_ptr->height <= 4) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + break; + } + case 1: + { + int i; + for (i = 0; i < 8 && png_ptr->pass == 1; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 2) /* skip top 4 generated rows */ + { + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + break; + } + case 2: + { + int i; + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 4) /* pass 3 might be empty */ + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + break; + } + case 3: + { + int i; + for (i = 0; i < 4 && png_ptr->pass == 3; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 4) /* skip top two generated rows */ + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + break; + } + case 4: + { + int i; + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 6) /* pass 5 might be empty */ + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + break; + } + case 5: + { + int i; + for (i = 0; i < 2 && png_ptr->pass == 5; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 6) /* skip top generated row */ + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + break; + } + case 6: + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + if (png_ptr->pass != 6) + break; + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + } + else +#endif + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } +} + +void /* PRIVATE */ +png_read_push_finish_row(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + const int FARDATA png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + const int FARDATA png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + const int FARDATA png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + const int FARDATA png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; + + /* Width of interlace block. This is not currently used - if you need + * it, uncomment it here and in png.h + const int FARDATA png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; + */ + + /* Height of interlace block. This is not currently used - if you need + * it, uncomment it here and in png.h + const int FARDATA png_pass_height[] = {8, 8, 4, 4, 2, 2, 1}; + */ +#endif + + png_ptr->row_number++; + if (png_ptr->row_number < png_ptr->num_rows) + return; + + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + png_memset_check(png_ptr, png_ptr->prev_row, 0, + png_ptr->rowbytes + 1); + do + { + png_ptr->pass++; + if ((png_ptr->pass == 1 && png_ptr->width < 5) || + (png_ptr->pass == 3 && png_ptr->width < 3) || + (png_ptr->pass == 5 && png_ptr->width < 2)) + png_ptr->pass++; + + if (png_ptr->pass > 7) + png_ptr->pass--; + if (png_ptr->pass >= 7) + break; + + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + png_ptr->irowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1; + + if (png_ptr->transformations & PNG_INTERLACE) + break; + + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + + } while (png_ptr->iwidth == 0 || png_ptr->num_rows == 0); + } +} + +#if defined(PNG_READ_tEXt_SUPPORTED) +void /* PRIVATE */ +png_push_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) + { + png_error(png_ptr, "Out of place tEXt"); + /* to quiet some compiler warnings */ + if(info_ptr == NULL) return; + } + +#ifdef PNG_MAX_MALLOC_64K + png_ptr->skip_length = 0; /* This may not be necessary */ + + if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */ + { + png_warning(png_ptr, "tEXt chunk too large to fit in memory"); + png_ptr->skip_length = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_ptr->current_text = (png_charp)png_malloc(png_ptr, + (png_uint_32)(length+1)); + png_ptr->current_text[length] = '\0'; + png_ptr->current_text_ptr = png_ptr->current_text; + png_ptr->current_text_size = (png_size_t)length; + png_ptr->current_text_left = (png_size_t)length; + png_ptr->process_mode = PNG_READ_tEXt_MODE; +} + +void /* PRIVATE */ +png_push_read_tEXt(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->buffer_size && png_ptr->current_text_left) + { + png_size_t text_size; + + if (png_ptr->buffer_size < png_ptr->current_text_left) + text_size = png_ptr->buffer_size; + else + text_size = png_ptr->current_text_left; + png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); + png_ptr->current_text_left -= text_size; + png_ptr->current_text_ptr += text_size; + } + if (!(png_ptr->current_text_left)) + { + png_textp text_ptr; + png_charp text; + png_charp key; + int ret; + + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_crc_finish(png_ptr); + +#if defined(PNG_MAX_MALLOC_64K) + if (png_ptr->skip_length) + return; +#endif + + key = png_ptr->current_text; + + for (text = key; *text; text++) + /* empty loop */ ; + + if (text != key + png_ptr->current_text_size) + text++; + + text_ptr = (png_textp)png_malloc(png_ptr, + (png_uint_32)png_sizeof(png_text)); + text_ptr->compression = PNG_TEXT_COMPRESSION_NONE; + text_ptr->key = key; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; +#endif + text_ptr->text = text; + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, key); + png_free(png_ptr, text_ptr); + png_ptr->current_text = NULL; + + if (ret) + png_warning(png_ptr, "Insufficient memory to store text chunk."); + } +} +#endif + +#if defined(PNG_READ_zTXt_SUPPORTED) +void /* PRIVATE */ +png_push_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) + { + png_error(png_ptr, "Out of place zTXt"); + /* to quiet some compiler warnings */ + if(info_ptr == NULL) return; + } + +#ifdef PNG_MAX_MALLOC_64K + /* We can't handle zTXt chunks > 64K, since we don't have enough space + * to be able to store the uncompressed data. Actually, the threshold + * is probably around 32K, but it isn't as definite as 64K is. + */ + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "zTXt chunk too large to fit in memory"); + png_push_crc_skip(png_ptr, length); + return; + } +#endif + + png_ptr->current_text = (png_charp)png_malloc(png_ptr, + (png_uint_32)(length+1)); + png_ptr->current_text[length] = '\0'; + png_ptr->current_text_ptr = png_ptr->current_text; + png_ptr->current_text_size = (png_size_t)length; + png_ptr->current_text_left = (png_size_t)length; + png_ptr->process_mode = PNG_READ_zTXt_MODE; +} + +void /* PRIVATE */ +png_push_read_zTXt(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->buffer_size && png_ptr->current_text_left) + { + png_size_t text_size; + + if (png_ptr->buffer_size < (png_uint_32)png_ptr->current_text_left) + text_size = png_ptr->buffer_size; + else + text_size = png_ptr->current_text_left; + png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); + png_ptr->current_text_left -= text_size; + png_ptr->current_text_ptr += text_size; + } + if (!(png_ptr->current_text_left)) + { + png_textp text_ptr; + png_charp text; + png_charp key; + int ret; + png_size_t text_size, key_size; + + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_crc_finish(png_ptr); + + key = png_ptr->current_text; + + for (text = key; *text; text++) + /* empty loop */ ; + + /* zTXt can't have zero text */ + if (text == key + png_ptr->current_text_size) + { + png_ptr->current_text = NULL; + png_free(png_ptr, key); + return; + } + + text++; + + if (*text != PNG_TEXT_COMPRESSION_zTXt) /* check compression byte */ + { + png_ptr->current_text = NULL; + png_free(png_ptr, key); + return; + } + + text++; + + png_ptr->zstream.next_in = (png_bytep )text; + png_ptr->zstream.avail_in = (uInt)(png_ptr->current_text_size - + (text - key)); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + key_size = text - key; + text_size = 0; + text = NULL; + ret = Z_STREAM_END; + + while (png_ptr->zstream.avail_in) + { + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret != Z_OK && ret != Z_STREAM_END) + { + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + png_ptr->current_text = NULL; + png_free(png_ptr, key); + png_free(png_ptr, text); + return; + } + if (!(png_ptr->zstream.avail_out) || ret == Z_STREAM_END) + { + if (text == NULL) + { + text = (png_charp)png_malloc(png_ptr, + (png_uint_32)(png_ptr->zbuf_size - png_ptr->zstream.avail_out + + key_size + 1)); + png_memcpy(text + key_size, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + png_memcpy(text, key, key_size); + text_size = key_size + png_ptr->zbuf_size - + png_ptr->zstream.avail_out; + *(text + text_size) = '\0'; + } + else + { + png_charp tmp; + + tmp = text; + text = (png_charp)png_malloc(png_ptr, text_size + + (png_uint_32)(png_ptr->zbuf_size - png_ptr->zstream.avail_out + + 1)); + png_memcpy(text, tmp, text_size); + png_free(png_ptr, tmp); + png_memcpy(text + text_size, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out; + *(text + text_size) = '\0'; + } + if (ret != Z_STREAM_END) + { + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + } + else + { + break; + } + + if (ret == Z_STREAM_END) + break; + } + + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + + if (ret != Z_STREAM_END) + { + png_ptr->current_text = NULL; + png_free(png_ptr, key); + png_free(png_ptr, text); + return; + } + + png_ptr->current_text = NULL; + png_free(png_ptr, key); + key = text; + text += key_size; + + text_ptr = (png_textp)png_malloc(png_ptr, + (png_uint_32)png_sizeof(png_text)); + text_ptr->compression = PNG_TEXT_COMPRESSION_zTXt; + text_ptr->key = key; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; +#endif + text_ptr->text = text; + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, key); + png_free(png_ptr, text_ptr); + + if (ret) + png_warning(png_ptr, "Insufficient memory to store text chunk."); + } +} +#endif + +#if defined(PNG_READ_iTXt_SUPPORTED) +void /* PRIVATE */ +png_push_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) + { + png_error(png_ptr, "Out of place iTXt"); + /* to quiet some compiler warnings */ + if(info_ptr == NULL) return; + } + +#ifdef PNG_MAX_MALLOC_64K + png_ptr->skip_length = 0; /* This may not be necessary */ + + if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */ + { + png_warning(png_ptr, "iTXt chunk too large to fit in memory"); + png_ptr->skip_length = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_ptr->current_text = (png_charp)png_malloc(png_ptr, + (png_uint_32)(length+1)); + png_ptr->current_text[length] = '\0'; + png_ptr->current_text_ptr = png_ptr->current_text; + png_ptr->current_text_size = (png_size_t)length; + png_ptr->current_text_left = (png_size_t)length; + png_ptr->process_mode = PNG_READ_iTXt_MODE; +} + +void /* PRIVATE */ +png_push_read_iTXt(png_structp png_ptr, png_infop info_ptr) +{ + + if (png_ptr->buffer_size && png_ptr->current_text_left) + { + png_size_t text_size; + + if (png_ptr->buffer_size < png_ptr->current_text_left) + text_size = png_ptr->buffer_size; + else + text_size = png_ptr->current_text_left; + png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); + png_ptr->current_text_left -= text_size; + png_ptr->current_text_ptr += text_size; + } + if (!(png_ptr->current_text_left)) + { + png_textp text_ptr; + png_charp key; + int comp_flag; + png_charp lang; + png_charp lang_key; + png_charp text; + int ret; + + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_crc_finish(png_ptr); + +#if defined(PNG_MAX_MALLOC_64K) + if (png_ptr->skip_length) + return; +#endif + + key = png_ptr->current_text; + + for (lang = key; *lang; lang++) + /* empty loop */ ; + + if (lang != key + png_ptr->current_text_size) + lang++; + + comp_flag = *lang++; + lang++; /* skip comp_type, always zero */ + + for (lang_key = lang; *lang_key; lang_key++) + /* empty loop */ ; + lang_key++; /* skip NUL separator */ + + for (text = lang_key; *text; text++) + /* empty loop */ ; + + if (text != key + png_ptr->current_text_size) + text++; + + text_ptr = (png_textp)png_malloc(png_ptr, + (png_uint_32)png_sizeof(png_text)); + text_ptr->compression = comp_flag + 2; + text_ptr->key = key; + text_ptr->lang = lang; + text_ptr->lang_key = lang_key; + text_ptr->text = text; + text_ptr->text_length = 0; + text_ptr->itxt_length = png_strlen(text); + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_ptr->current_text = NULL; + + png_free(png_ptr, text_ptr); + if (ret) + png_warning(png_ptr, "Insufficient memory to store iTXt chunk."); + } +} +#endif + +/* This function is called when we haven't found a handler for this + * chunk. If there isn't a problem with the chunk itself (ie a bad chunk + * name or a critical chunk), the chunk is (currently) silently ignored. + */ +void /* PRIVATE */ +png_push_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + png_uint_32 skip=0; + png_check_chunk_name(png_ptr, png_ptr->chunk_name); + + if (!(png_ptr->chunk_name[0] & 0x20)) + { +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) + && png_ptr->read_user_chunk_fn == NULL +#endif + ) +#endif + png_chunk_error(png_ptr, "unknown critical chunk"); + + /* to quiet compiler warnings about unused info_ptr */ + if (info_ptr == NULL) + return; + } + +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + if (png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS) + { + png_unknown_chunk chunk; + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "unknown chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_strcpy((png_charp)chunk.name, (png_charp)png_ptr->chunk_name); + chunk.data = (png_bytep)png_malloc(png_ptr, length); + png_crc_read(png_ptr, chunk.data, length); + chunk.size = length; +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) + if(png_ptr->read_user_chunk_fn != NULL) + { + /* callback to user unknown chunk handler */ + if ((*(png_ptr->read_user_chunk_fn)) (png_ptr, &chunk) <= 0) + { + if (!(png_ptr->chunk_name[0] & 0x20)) + if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS) + png_chunk_error(png_ptr, "unknown critical chunk"); + } + png_set_unknown_chunks(png_ptr, info_ptr, &chunk, 1); + } + else +#endif + png_set_unknown_chunks(png_ptr, info_ptr, &chunk, 1); + png_free(png_ptr, chunk.data); + } + else +#endif + skip=length; + png_push_crc_skip(png_ptr, skip); +} + +void /* PRIVATE */ +png_push_have_info(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->info_fn != NULL) + (*(png_ptr->info_fn))(png_ptr, info_ptr); +} + +void /* PRIVATE */ +png_push_have_end(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->end_fn != NULL) + (*(png_ptr->end_fn))(png_ptr, info_ptr); +} + +void /* PRIVATE */ +png_push_have_row(png_structp png_ptr, png_bytep row) +{ + if (png_ptr->row_fn != NULL) + (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number, + (int)png_ptr->pass); +} + +void PNGAPI +png_progressive_combine_row (png_structp png_ptr, + png_bytep old_row, png_bytep new_row) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + const int FARDATA png_pass_dsp_mask[7] = + {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff}; +#endif + if (new_row != NULL) /* new_row must == png_ptr->row_buf here. */ + png_combine_row(png_ptr, old_row, png_pass_dsp_mask[png_ptr->pass]); +} + +void PNGAPI +png_set_progressive_read_fn(png_structp png_ptr, png_voidp progressive_ptr, + png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, + png_progressive_end_ptr end_fn) +{ + png_ptr->info_fn = info_fn; + png_ptr->row_fn = row_fn; + png_ptr->end_fn = end_fn; + + png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer); +} + +png_voidp PNGAPI +png_get_progressive_ptr(png_structp png_ptr) +{ + return png_ptr->io_ptr; +} +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ diff --git a/com32/lib/libpng/pngread.c b/com32/lib/libpng/pngread.c new file mode 100644 index 0000000..b92a138 --- /dev/null +++ b/com32/lib/libpng/pngread.c @@ -0,0 +1,1456 @@ + +/* pngread.c - read a PNG file + * + * libpng 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file contains routines that an application calls directly to + * read a PNG file or stream. + */ + +#define PNG_INTERNAL +#include "png.h" + +/* Create a PNG structure for reading, and allocate any memory needed. */ +png_structp PNGAPI +png_create_read_struct(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn) +{ + +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_read_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, png_voidp_NULL, png_malloc_ptr_NULL, png_free_ptr_NULL)); +} + +/* Alternate create PNG structure for reading, and allocate any memory needed. */ +png_structp PNGAPI +png_create_read_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + + png_structp png_ptr; + +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + + int i; + + png_debug(1, "in png_create_read_struct\n"); +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG, + (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr); +#else + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); +#endif + if (png_ptr == NULL) + return (NULL); + +#if !defined(PNG_1_0_X) +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED + png_init_mmx_flags(png_ptr); /* 1.2.0 addition */ +#endif +#endif /* PNG_1_0_X */ + + /* added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max=PNG_USER_WIDTH_MAX; + png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; +#endif + +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_ptr->jmpbuf)) +#endif + { + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf=NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, + (png_free_ptr)free_fn, (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + return (NULL); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); +#endif +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn); +#endif + + png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn); + + i=0; + do + { + if(user_png_ver[i] != png_libpng_ver[i]) + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + } while (png_libpng_ver[i++]); + + if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) + { + /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so + * we must recompile any applications that use any older library version. + * For versions after libpng 1.0, we will be compatible, so we need + * only check the first digit. + */ + if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] || + (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) || + (user_png_ver[0] == '0' && user_png_ver[2] < '9')) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[80]; + if (user_png_ver) + { + sprintf(msg, "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + sprintf(msg, "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); +#endif +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "Incompatible libpng version in application and library"); + } + } + + /* initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = (voidpf)png_ptr; + + switch (inflateInit(&png_ptr->zstream)) + { + case Z_OK: /* Do nothing */ break; + case Z_MEM_ERROR: + case Z_STREAM_ERROR: png_error(png_ptr, "zlib memory error"); break; + case Z_VERSION_ERROR: png_error(png_ptr, "zlib version error"); break; + default: png_error(png_ptr, "Unknown zlib error"); + } + + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + png_set_read_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL); + +#ifdef PNG_SETJMP_SUPPORTED +/* Applications that neglect to set up their own setjmp() and then encounter + a png_error() will longjmp here. Since the jmpbuf is then meaningless we + abort instead of returning. */ +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) + PNG_ABORT(); + png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); +#else + if (setjmp(png_ptr->jmpbuf)) + PNG_ABORT(); +#endif +#endif + return (png_ptr); +} + +/* Initialize PNG structure for reading, and allocate any memory needed. + This interface is deprecated in favour of the png_create_read_struct(), + and it will eventually disappear. */ +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +#undef png_read_init +void PNGAPI +png_read_init(png_structp png_ptr) +{ + /* We only come here via pre-1.0.7-compiled applications */ + png_read_init_2(png_ptr, "1.0.6 or earlier", 0, 0); +} +#endif + +void PNGAPI +png_read_init_2(png_structp png_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size, png_size_t png_info_size) +{ + /* We only come here via pre-1.0.12-compiled applications */ +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + if(png_sizeof(png_struct) > png_struct_size || + png_sizeof(png_info) > png_info_size) + { + char msg[80]; + png_ptr->warning_fn=NULL; + if (user_png_ver) + { + sprintf(msg, "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + sprintf(msg, "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); + } +#endif + if(png_sizeof(png_struct) > png_struct_size) + { + png_ptr->error_fn=NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "The png struct allocated by the application for reading is too small."); + } + if(png_sizeof(png_info) > png_info_size) + { + png_ptr->error_fn=NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "The info struct allocated by application for reading is too small."); + } + png_read_init_3(&png_ptr, user_png_ver, png_struct_size); +} + +void PNGAPI +png_read_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size) +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; /* to save current jump buffer */ +#endif + + int i=0; + + png_structp png_ptr=*ptr_ptr; + + do + { + if(user_png_ver[i] != png_libpng_ver[i]) + { +#ifdef PNG_LEGACY_SUPPORTED + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; +#else + png_ptr->warning_fn=NULL; + png_warning(png_ptr, + "Application uses deprecated png_read_init() and should be recompiled."); + break; +#endif + } + } while (png_libpng_ver[i++]); + + png_debug(1, "in png_read_init_3\n"); + +#ifdef PNG_SETJMP_SUPPORTED + /* save jump buffer and error functions */ + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); +#endif + + if(png_sizeof(png_struct) > png_struct_size) + { + png_destroy_struct(png_ptr); + *ptr_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); + png_ptr = *ptr_ptr; + } + + /* reset all variables to 0 */ + png_memset(png_ptr, 0, png_sizeof (png_struct)); + +#ifdef PNG_SETJMP_SUPPORTED + /* restore jump buffer */ + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); +#endif + + /* added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max=PNG_USER_WIDTH_MAX; + png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; +#endif + + /* initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = (voidpf)png_ptr; + + switch (inflateInit(&png_ptr->zstream)) + { + case Z_OK: /* Do nothing */ break; + case Z_MEM_ERROR: + case Z_STREAM_ERROR: png_error(png_ptr, "zlib memory"); break; + case Z_VERSION_ERROR: png_error(png_ptr, "zlib version"); break; + default: png_error(png_ptr, "Unknown zlib error"); + } + + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + png_set_read_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL); +} + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. This has been + * changed in v0.90 to allow reading a file that already has the magic + * bytes read from the stream. You can tell libpng how many bytes have + * been read from the beginning of the stream (up to the maximum of 8) + * via png_set_sig_bytes(), and we will only check the remaining bytes + * here. The application can then have access to the signature bytes we + * read if it is determined that this isn't a valid PNG file. + */ +void PNGAPI +png_read_info(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_read_info\n"); + /* If we haven't checked all of the PNG signature bytes, do so now. */ + if (png_ptr->sig_bytes < 8) + { + png_size_t num_checked = png_ptr->sig_bytes, + num_to_check = 8 - num_checked; + + png_read_data(png_ptr, &(info_ptr->signature[num_checked]), num_to_check); + png_ptr->sig_bytes = 8; + + if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) + { + if (num_checked < 4 && + png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) + png_error(png_ptr, "Not a PNG file"); + else + png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + } + if (num_checked < 3) + png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; + } + + for(;;) + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IHDR; + PNG_IDAT; + PNG_IEND; + PNG_PLTE; +#if defined(PNG_READ_bKGD_SUPPORTED) + PNG_bKGD; +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + PNG_cHRM; +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + PNG_gAMA; +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + PNG_hIST; +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + PNG_iCCP; +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + PNG_iTXt; +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + PNG_oFFs; +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + PNG_pCAL; +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + PNG_pHYs; +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + PNG_sBIT; +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + PNG_sCAL; +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + PNG_sPLT; +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + PNG_sRGB; +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + PNG_tEXt; +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + PNG_tIME; +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + PNG_tRNS; +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + PNG_zTXt; +#endif +#endif /* PNG_USE_LOCAL_ARRAYS */ + png_byte chunk_length[4]; + png_uint_32 length; + + png_read_data(png_ptr, chunk_length, 4); + length = png_get_uint_31(png_ptr,chunk_length); + + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + + png_debug2(0, "Reading %s chunk, length=%lu.\n", png_ptr->chunk_name, + length); + + /* This should be a binary subdivision search or a hash for + * matching the chunk name rather than a linear search. + */ + if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4)) + png_handle_IHDR(png_ptr, info_ptr, length); + else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4)) + png_handle_IEND(png_ptr, info_ptr, length); +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name)) + { + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_ptr->mode |= PNG_HAVE_IDAT; + png_handle_unknown(png_ptr, info_ptr, length); + if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_ptr->mode |= PNG_HAVE_PLTE; + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + break; + } + } +#endif + else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_handle_PLTE(png_ptr, info_ptr, length); + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + + png_ptr->idat_size = length; + png_ptr->mode |= PNG_HAVE_IDAT; + break; + } +#if defined(PNG_READ_bKGD_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4)) + png_handle_bKGD(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4)) + png_handle_cHRM(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4)) + png_handle_gAMA(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4)) + png_handle_hIST(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4)) + png_handle_oFFs(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4)) + png_handle_pCAL(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4)) + png_handle_sCAL(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4)) + png_handle_pHYs(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4)) + png_handle_sBIT(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4)) + png_handle_sRGB(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4)) + png_handle_iCCP(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4)) + png_handle_sPLT(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4)) + png_handle_tEXt(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4)) + png_handle_tIME(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4)) + png_handle_tRNS(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4)) + png_handle_zTXt(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4)) + png_handle_iTXt(png_ptr, info_ptr, length); +#endif + else + png_handle_unknown(png_ptr, info_ptr, length); + } +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +/* optional call to update the users info_ptr structure */ +void PNGAPI +png_read_update_info(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_read_update_info\n"); + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); + else + png_warning(png_ptr, + "Ignoring extra png_read_update_info() call; row buffer not reallocated"); + png_read_transform_info(png_ptr, info_ptr); +} + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Initialize palette, background, etc, after transformations + * are set, but before any reading takes place. This allows + * the user to obtain a gamma-corrected palette, for example. + * If the user doesn't call this, we will do it ourselves. + */ +void PNGAPI +png_start_read_image(png_structp png_ptr) +{ + png_debug(1, "in png_start_read_image\n"); + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +void PNGAPI +png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IDAT; + const int png_pass_dsp_mask[7] = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff}; + const int png_pass_mask[7] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff}; +#endif + int ret; + png_debug2(1, "in png_read_row (row %lu, pass %d)\n", + png_ptr->row_number, png_ptr->pass); + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); + if (png_ptr->row_number == 0 && png_ptr->pass == 0) + { + /* check for transforms that have been set but were defined out */ +#if defined(PNG_WRITE_INVERT_SUPPORTED) && !defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_warning(png_ptr, "PNG_READ_INVERT_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && !defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined."); +#endif + } + +#if defined(PNG_READ_INTERLACING_SUPPORTED) + /* if interlaced and we do not need a new row, combine row and return */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + switch (png_ptr->pass) + { + case 0: + if (png_ptr->row_number & 0x07) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 1: + if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 2: + if ((png_ptr->row_number & 0x07) != 4) + { + if (dsp_row != NULL && (png_ptr->row_number & 4)) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 3: + if ((png_ptr->row_number & 3) || png_ptr->width < 3) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 4: + if ((png_ptr->row_number & 3) != 2) + { + if (dsp_row != NULL && (png_ptr->row_number & 2)) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 5: + if ((png_ptr->row_number & 1) || png_ptr->width < 2) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 6: + if (!(png_ptr->row_number & 1)) + { + png_read_finish_row(png_ptr); + return; + } + break; + } + } +#endif + + if (!(png_ptr->mode & PNG_HAVE_IDAT)) + png_error(png_ptr, "Invalid attempt to read row data"); + + png_ptr->zstream.next_out = png_ptr->row_buf; + png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes; + do + { + if (!(png_ptr->zstream.avail_in)) + { + while (!png_ptr->idat_size) + { + png_byte chunk_length[4]; + + png_crc_finish(png_ptr, 0); + + png_read_data(png_ptr, chunk_length, 4); + png_ptr->idat_size = png_get_uint_31(png_ptr,chunk_length); + + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_error(png_ptr, "Not enough image data"); + } + png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_in = png_ptr->zbuf; + if (png_ptr->zbuf_size > png_ptr->idat_size) + png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size; + png_crc_read(png_ptr, png_ptr->zbuf, + (png_size_t)png_ptr->zstream.avail_in); + png_ptr->idat_size -= png_ptr->zstream.avail_in; + } + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret == Z_STREAM_END) + { + if (png_ptr->zstream.avail_out || png_ptr->zstream.avail_in || + png_ptr->idat_size) + png_error(png_ptr, "Extra compressed data"); + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + if (ret != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg : + "Decompression error"); + + } while (png_ptr->zstream.avail_out); + + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->iwidth; + png_ptr->row_info.channels = png_ptr->channels; + png_ptr->row_info.bit_depth = png_ptr->bit_depth; + png_ptr->row_info.pixel_depth = png_ptr->pixel_depth; + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + if(png_ptr->row_buf[0]) + png_read_filter_row(png_ptr, &(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->prev_row + 1, + (int)(png_ptr->row_buf[0])); + + png_memcpy_check(png_ptr, png_ptr->prev_row, png_ptr->row_buf, + png_ptr->rowbytes + 1); + +#if defined(PNG_MNG_FEATURES_SUPPORTED) + if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) + { + /* Intrapixel differencing */ + png_do_read_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1); + } +#endif + + + if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA)) + png_do_read_transformations(png_ptr); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) + /* blow up interlaced rows to full size */ + if (png_ptr->interlaced && + (png_ptr->transformations & PNG_INTERLACE)) + { + if (png_ptr->pass < 6) +/* old interface (pre-1.0.9): + png_do_read_interlace(&(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations); + */ + png_do_read_interlace(png_ptr); + + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + if (row != NULL) + png_combine_row(png_ptr, row, + png_pass_mask[png_ptr->pass]); + } + else +#endif + { + if (row != NULL) + png_combine_row(png_ptr, row, 0xff); + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, 0xff); + } + png_read_finish_row(png_ptr); + + if (png_ptr->read_row_fn != NULL) + (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. If the image is interlaced, + * and png_set_interlace_handling() has been called, the rows need to + * contain the contents of the rows from the previous pass. If the + * image has alpha or transparency, and png_handle_alpha()[*] has been + * called, the rows contents must be initialized to the contents of the + * screen. + * + * "row" holds the actual image, and pixels are placed in it + * as they arrive. If the image is displayed after each pass, it will + * appear to "sparkle" in. "display_row" can be used to display a + * "chunky" progressive image, with finer detail added as it becomes + * available. If you do not want this "chunky" display, you may pass + * NULL for display_row. If you do not want the sparkle display, and + * you have not called png_handle_alpha(), you may pass NULL for rows. + * If you have called png_handle_alpha(), and the image has either an + * alpha channel or a transparency chunk, you must provide a buffer for + * rows. In this case, you do not have to provide a display_row buffer + * also, but you may. If the image is not interlaced, or if you have + * not called png_set_interlace_handling(), the display_row buffer will + * be ignored, so pass NULL to it. + * + * [*] png_handle_alpha() does not exist yet, as of libpng version 1.2.8 + */ + +void PNGAPI +png_read_rows(png_structp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows) +{ + png_uint_32 i; + png_bytepp rp; + png_bytepp dp; + + png_debug(1, "in png_read_rows\n"); + rp = row; + dp = display_row; + if (rp != NULL && dp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep rptr = *rp++; + png_bytep dptr = *dp++; + + png_read_row(png_ptr, rptr, dptr); + } + else if(rp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep rptr = *rp; + png_read_row(png_ptr, rptr, png_bytep_NULL); + rp++; + } + else if(dp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep dptr = *dp; + png_read_row(png_ptr, png_bytep_NULL, dptr); + dp++; + } +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read the entire image. If the image has an alpha channel or a tRNS + * chunk, and you have called png_handle_alpha()[*], you will need to + * initialize the image to the current image that PNG will be overlaying. + * We set the num_rows again here, in case it was incorrectly set in + * png_read_start_row() by a call to png_read_update_info() or + * png_start_read_image() if png_set_interlace_handling() wasn't called + * prior to either of these functions like it should have been. You can + * only call this function once. If you desire to have an image for + * each pass of a interlaced image, use png_read_rows() instead. + * + * [*] png_handle_alpha() does not exist yet, as of libpng version 1.2.8 + */ +void PNGAPI +png_read_image(png_structp png_ptr, png_bytepp image) +{ + png_uint_32 i,image_height; + int pass, j; + png_bytepp rp; + + png_debug(1, "in png_read_image\n"); + +#ifdef PNG_READ_INTERLACING_SUPPORTED + pass = png_set_interlace_handling(png_ptr); +#else + if (png_ptr->interlaced) + png_error(png_ptr, + "Cannot read interlaced image -- interlace handler disabled."); + pass = 1; +#endif + + + image_height=png_ptr->height; + png_ptr->num_rows = image_height; /* Make sure this is set correctly */ + + for (j = 0; j < pass; j++) + { + rp = image; + for (i = 0; i < image_height; i++) + { + png_read_row(png_ptr, *rp, png_bytep_NULL); + rp++; + } + } +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. Will not read past the end of the + * file, will verify the end is accurate, and will read any comments + * or time information at the end of the file, if info is not NULL. + */ +void PNGAPI +png_read_end(png_structp png_ptr, png_infop info_ptr) +{ + png_byte chunk_length[4]; + png_uint_32 length; + + png_debug(1, "in png_read_end\n"); + png_crc_finish(png_ptr, 0); /* Finish off CRC from last IDAT chunk */ + + do + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IHDR; + PNG_IDAT; + PNG_IEND; + PNG_PLTE; +#if defined(PNG_READ_bKGD_SUPPORTED) + PNG_bKGD; +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + PNG_cHRM; +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + PNG_gAMA; +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + PNG_hIST; +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + PNG_iCCP; +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + PNG_iTXt; +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + PNG_oFFs; +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + PNG_pCAL; +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + PNG_pHYs; +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + PNG_sBIT; +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + PNG_sCAL; +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + PNG_sPLT; +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + PNG_sRGB; +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + PNG_tEXt; +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + PNG_tIME; +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + PNG_tRNS; +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + PNG_zTXt; +#endif +#endif /* PNG_USE_LOCAL_ARRAYS */ + + png_read_data(png_ptr, chunk_length, 4); + length = png_get_uint_31(png_ptr,chunk_length); + + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + + png_debug1(0, "Reading %s chunk.\n", png_ptr->chunk_name); + + if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4)) + png_handle_IHDR(png_ptr, info_ptr, length); + else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4)) + png_handle_IEND(png_ptr, info_ptr, length); +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name)) + { + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if (length > 0 || png_ptr->mode & PNG_AFTER_IDAT) + png_error(png_ptr, "Too many IDAT's found"); + } + else + png_ptr->mode |= PNG_AFTER_IDAT; + png_handle_unknown(png_ptr, info_ptr, length); + if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_ptr->mode |= PNG_HAVE_PLTE; + } +#endif + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + /* Zero length IDATs are legal after the last IDAT has been + * read, but not after other chunks have been read. + */ + if (length > 0 || png_ptr->mode & PNG_AFTER_IDAT) + png_error(png_ptr, "Too many IDAT's found"); + png_crc_finish(png_ptr, length); + } + else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_handle_PLTE(png_ptr, info_ptr, length); +#if defined(PNG_READ_bKGD_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4)) + png_handle_bKGD(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4)) + png_handle_cHRM(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4)) + png_handle_gAMA(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4)) + png_handle_hIST(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4)) + png_handle_oFFs(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4)) + png_handle_pCAL(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4)) + png_handle_sCAL(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4)) + png_handle_pHYs(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4)) + png_handle_sBIT(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4)) + png_handle_sRGB(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4)) + png_handle_iCCP(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4)) + png_handle_sPLT(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4)) + png_handle_tEXt(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4)) + png_handle_tIME(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4)) + png_handle_tRNS(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4)) + png_handle_zTXt(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4)) + png_handle_iTXt(png_ptr, info_ptr, length); +#endif + else + png_handle_unknown(png_ptr, info_ptr, length); + } while (!(png_ptr->mode & PNG_HAVE_IEND)); +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +/* free all memory used by the read */ +void PNGAPI +png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, + png_infopp end_info_ptr_ptr) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL, end_info_ptr = NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn; + png_voidp mem_ptr; +#endif + + png_debug(1, "in png_destroy_read_struct\n"); + if (png_ptr_ptr != NULL) + png_ptr = *png_ptr_ptr; + + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (end_info_ptr_ptr != NULL) + end_info_ptr = *end_info_ptr_ptr; + +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; + mem_ptr = png_ptr->mem_ptr; +#endif + + png_read_destroy(png_ptr, info_ptr, end_info_ptr); + + if (info_ptr != NULL) + { +#if defined(PNG_TEXT_SUPPORTED) + png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, -1); +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } + + if (end_info_ptr != NULL) + { +#if defined(PNG_READ_TEXT_SUPPORTED) + png_free_data(png_ptr, end_info_ptr, PNG_FREE_TEXT, -1); +#endif +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)end_info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)end_info_ptr); +#endif + *end_info_ptr_ptr = NULL; + } + + if (png_ptr != NULL) + { +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + *png_ptr_ptr = NULL; + } +} + +/* free all memory used by the read (old method) */ +void /* PRIVATE */ +png_read_destroy(png_structp png_ptr, png_infop info_ptr, png_infop end_info_ptr) +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; +#endif + png_error_ptr error_fn; + png_error_ptr warning_fn; + png_voidp error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn; +#endif + + png_debug(1, "in png_read_destroy\n"); + if (info_ptr != NULL) + png_info_destroy(png_ptr, info_ptr); + + if (end_info_ptr != NULL) + png_info_destroy(png_ptr, end_info_ptr); + + png_free(png_ptr, png_ptr->zbuf); + png_free(png_ptr, png_ptr->big_row_buf); + png_free(png_ptr, png_ptr->prev_row); +#if defined(PNG_READ_DITHER_SUPPORTED) + png_free(png_ptr, png_ptr->palette_lookup); + png_free(png_ptr, png_ptr->dither_index); +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) + png_free(png_ptr, png_ptr->gamma_table); +#endif +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + png_free(png_ptr, png_ptr->gamma_from_1); + png_free(png_ptr, png_ptr->gamma_to_1); +#endif +#ifdef PNG_FREE_ME_SUPPORTED + if (png_ptr->free_me & PNG_FREE_PLTE) + png_zfree(png_ptr, png_ptr->palette); + png_ptr->free_me &= ~PNG_FREE_PLTE; +#else + if (png_ptr->flags & PNG_FLAG_FREE_PLTE) + png_zfree(png_ptr, png_ptr->palette); + png_ptr->flags &= ~PNG_FLAG_FREE_PLTE; +#endif +#if defined(PNG_tRNS_SUPPORTED) || \ + defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) +#ifdef PNG_FREE_ME_SUPPORTED + if (png_ptr->free_me & PNG_FREE_TRNS) + png_free(png_ptr, png_ptr->trans); + png_ptr->free_me &= ~PNG_FREE_TRNS; +#else + if (png_ptr->flags & PNG_FLAG_FREE_TRNS) + png_free(png_ptr, png_ptr->trans); + png_ptr->flags &= ~PNG_FLAG_FREE_TRNS; +#endif +#endif +#if defined(PNG_READ_hIST_SUPPORTED) +#ifdef PNG_FREE_ME_SUPPORTED + if (png_ptr->free_me & PNG_FREE_HIST) + png_free(png_ptr, png_ptr->hist); + png_ptr->free_me &= ~PNG_FREE_HIST; +#else + if (png_ptr->flags & PNG_FLAG_FREE_HIST) + png_free(png_ptr, png_ptr->hist); + png_ptr->flags &= ~PNG_FLAG_FREE_HIST; +#endif +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (png_ptr->gamma_16_table != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_table[i]); + } + png_free(png_ptr, png_ptr->gamma_16_table); + } +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_16_from_1 != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_from_1[i]); + } + png_free(png_ptr, png_ptr->gamma_16_from_1); + } + if (png_ptr->gamma_16_to_1 != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_to_1[i]); + } + png_free(png_ptr, png_ptr->gamma_16_to_1); + } +#endif +#endif +#if defined(PNG_TIME_RFC1123_SUPPORTED) + png_free(png_ptr, png_ptr->time_buffer); +#endif + + inflateEnd(&png_ptr->zstream); +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_free(png_ptr, png_ptr->save_buffer); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +#ifdef PNG_TEXT_SUPPORTED + png_free(png_ptr, png_ptr->current_text); +#endif /* PNG_TEXT_SUPPORTED */ +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + + /* Save the important info out of the png_struct, in case it is + * being used again. + */ +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); +#endif + + error_fn = png_ptr->error_fn; + warning_fn = png_ptr->warning_fn; + error_ptr = png_ptr->error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; +#endif + + png_memset(png_ptr, 0, png_sizeof (png_struct)); + + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; + png_ptr->error_ptr = error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr->free_fn = free_fn; +#endif + +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); +#endif + +} + +void PNGAPI +png_set_read_status_fn(png_structp png_ptr, png_read_status_ptr read_row_fn) +{ + png_ptr->read_row_fn = read_row_fn; +} + + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +#if defined(PNG_INFO_IMAGE_SUPPORTED) +void PNGAPI +png_read_png(png_structp png_ptr, png_infop info_ptr, + int transforms, + voidp params) +{ + int row; + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) + /* invert the alpha channel from opacity to transparency + */ + if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + png_set_invert_alpha(png_ptr); +#endif + + /* png_read_info() gives us all of the information from the + * PNG file before the first IDAT (image data chunk). + */ + png_read_info(png_ptr, info_ptr); + if (info_ptr->height > PNG_UINT_32_MAX/png_sizeof(png_bytep)) + png_error(png_ptr,"Image is too high to process with png_read_png()"); + + /* -------------- image transformations start here ------------------- */ + +#if defined(PNG_READ_16_TO_8_SUPPORTED) + /* tell libpng to strip 16 bit/color files down to 8 bits per color + */ + if (transforms & PNG_TRANSFORM_STRIP_16) + png_set_strip_16(png_ptr); +#endif + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) + /* Strip alpha bytes from the input data without combining with + * the background (not recommended). + */ + if (transforms & PNG_TRANSFORM_STRIP_ALPHA) + png_set_strip_alpha(png_ptr); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) && !defined(PNG_READ_EXPAND_SUPPORTED) + /* Extract multiple pixels with bit depths of 1, 2, or 4 from a single + * byte into separate bytes (useful for paletted and grayscale images). + */ + if (transforms & PNG_TRANSFORM_PACKING) + png_set_packing(png_ptr); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + /* Change the order of packed pixels to least significant bit first + * (not useful if you are using png_set_packing). + */ + if (transforms & PNG_TRANSFORM_PACKSWAP) + png_set_packswap(png_ptr); +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) + /* Expand paletted colors into true RGB triplets + * Expand grayscale images to full 8 bits from 1, 2, or 4 bits/pixel + * Expand paletted or RGB images with transparency to full alpha + * channels so the data will be available as RGBA quartets. + */ + if (transforms & PNG_TRANSFORM_EXPAND) + if ((png_ptr->bit_depth < 8) || + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) || + (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))) + png_set_expand(png_ptr); +#endif + + /* We don't handle background color or gamma transformation or dithering. + */ + +#if defined(PNG_READ_INVERT_SUPPORTED) + /* invert monochrome files to have 0 as white and 1 as black + */ + if (transforms & PNG_TRANSFORM_INVERT_MONO) + png_set_invert_mono(png_ptr); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) + /* If you want to shift the pixel values from the range [0,255] or + * [0,65535] to the original [0,7] or [0,31], or whatever range the + * colors were originally in: + */ + if ((transforms & PNG_TRANSFORM_SHIFT) + && png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT)) + { + png_color_8p sig_bit; + + png_get_sBIT(png_ptr, info_ptr, &sig_bit); + png_set_shift(png_ptr, sig_bit); + } +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) + /* flip the RGB pixels to BGR (or RGBA to BGRA) + */ + if (transforms & PNG_TRANSFORM_BGR) + png_set_bgr(png_ptr); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) + /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) + */ + if (transforms & PNG_TRANSFORM_SWAP_ALPHA) + png_set_swap_alpha(png_ptr); +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) + /* swap bytes of 16 bit files to least significant byte first + */ + if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) + png_set_swap(png_ptr); +#endif + + /* We don't handle adding filler bytes */ + + /* Optional call to gamma correct and add the background to the palette + * and update info structure. REQUIRED if you are expecting libpng to + * update the palette for you (i.e., you selected such a transform above). + */ + png_read_update_info(png_ptr, info_ptr); + + /* -------------- image transformations end here ------------------- */ + +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); +#endif + if(info_ptr->row_pointers == NULL) + { + info_ptr->row_pointers = (png_bytepp)png_malloc(png_ptr, + info_ptr->height * png_sizeof(png_bytep)); +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_ROWS; +#endif + for (row = 0; row < (int)info_ptr->height; row++) + { + info_ptr->row_pointers[row] = (png_bytep)png_malloc(png_ptr, + png_get_rowbytes(png_ptr, info_ptr)); + } + } + + png_read_image(png_ptr, info_ptr->row_pointers); + info_ptr->valid |= PNG_INFO_IDAT; + + /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ + png_read_end(png_ptr, info_ptr); + + if(transforms == 0 || params == NULL) + /* quiet compiler warnings */ return; + +} +#endif +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ diff --git a/com32/lib/libpng/pngrio.c b/com32/lib/libpng/pngrio.c new file mode 100644 index 0000000..cae501a --- /dev/null +++ b/com32/lib/libpng/pngrio.c @@ -0,0 +1,161 @@ + +/* pngrio.c - functions for data input + * + * libpng 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file provides a location for all input. Users who need + * special handling are expected to write a function that has the same + * arguments as this and performs a similar function, but that possibly + * has a different input method. Note that you shouldn't change this + * function, but rather write a replacement function and then make + * libpng use it at run time with png_set_read_fn(...). + */ + +#define PNG_INTERNAL +#include "png.h" + +/* Read the data from whatever input you are using. The default routine + reads from a file pointer. Note that this routine sometimes gets called + with very small lengths, so you should implement some kind of simple + buffering if you are using unbuffered reads. This should never be asked + to read more then 64K on a 16 bit machine. */ +void /* PRIVATE */ +png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_debug1(4,"reading %d bytes\n", (int)length); + if (png_ptr->read_data_fn != NULL) + (*(png_ptr->read_data_fn))(png_ptr, data, length); + else + png_error(png_ptr, "Call to NULL read function"); +} + +#if !defined(PNG_NO_STDIO) +/* This is the function that does the actual reading of data. If you are + not reading from a standard C stream, you should create a replacement + read_data function and use it at run time with png_set_read_fn(), rather + than changing the library. */ +#ifndef USE_FAR_KEYWORD +void PNGAPI +png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check; + + /* fread() returns 0 on error, so it is OK to store this in a png_size_t + * instead of an int, which is what fread() actually returns. + */ +#if defined(_WIN32_WCE) + if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) + check = 0; +#else + check = (png_size_t)fread(data, (png_size_t)1, length, + (png_FILE_p)png_ptr->io_ptr); +#endif + + if (check != length) + png_error(png_ptr, "Read Error"); +} +#else +/* this is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +static void /* PRIVATE */ +png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + int check; + png_byte *n_data; + png_FILE_p io_ptr; + + /* Check if data really is near. If so, use usual code. */ + n_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)n_data == data) + { +#if defined(_WIN32_WCE) + if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) + check = 0; +#else + check = fread(n_data, 1, length, io_ptr); +#endif + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t read, remaining, err; + check = 0; + remaining = length; + do + { + read = MIN(NEAR_BUF_SIZE, remaining); +#if defined(_WIN32_WCE) + if ( !ReadFile((HANDLE)(io_ptr), buf, read, &err, NULL) ) + err = 0; +#else + err = fread(buf, (png_size_t)1, read, io_ptr); +#endif + png_memcpy(data, buf, read); /* copy far buffer to near buffer */ + if(err != read) + break; + else + check += err; + data += read; + remaining -= read; + } + while (remaining != 0); + } + if ((png_uint_32)check != (png_uint_32)length) + png_error(png_ptr, "read Error"); +} +#endif +#endif + +/* This function allows the application to supply a new input function + for libpng if standard C streams aren't being used. + + This function takes as its arguments: + png_ptr - pointer to a png input data structure + io_ptr - pointer to user supplied structure containing info about + the input functions. May be NULL. + read_data_fn - pointer to a new input function that takes as its + arguments a pointer to a png_struct, a pointer to + a location where input data can be stored, and a 32-bit + unsigned int that is the number of bytes to be read. + To exit and output any fatal error messages the new write + function should call png_error(png_ptr, "Error msg"). */ +void PNGAPI +png_set_read_fn(png_structp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn) +{ + png_ptr->io_ptr = io_ptr; + +#if !defined(PNG_NO_STDIO) + if (read_data_fn != NULL) + png_ptr->read_data_fn = read_data_fn; + else + png_ptr->read_data_fn = png_default_read_data; +#else + png_ptr->read_data_fn = read_data_fn; +#endif + + /* It is an error to write to a read device */ + if (png_ptr->write_data_fn != NULL) + { + png_ptr->write_data_fn = NULL; + png_warning(png_ptr, + "It's an error to set both read_data_fn and write_data_fn in the "); + png_warning(png_ptr, + "same structure. Resetting write_data_fn to NULL."); + } + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) + png_ptr->output_flush_fn = NULL; +#endif +} diff --git a/com32/lib/libpng/pngrtran.c b/com32/lib/libpng/pngrtran.c new file mode 100644 index 0000000..da9ef6e --- /dev/null +++ b/com32/lib/libpng/pngrtran.c @@ -0,0 +1,4177 @@ + +/* pngrtran.c - transforms the data in a row for PNG readers + * + * libpng version 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file contains functions optionally called by an application + * in order to tell libpng how to handle data when reading a PNG. + * Transformations that are used in both reading and writing are + * in pngtrans.c. + */ + +#define PNG_INTERNAL +#include "png.h" + +/* Set the action on getting a CRC error for an ancillary or critical chunk. */ +void PNGAPI +png_set_crc_action(png_structp png_ptr, int crit_action, int ancil_action) +{ + png_debug(1, "in png_set_crc_action\n"); + /* Tell libpng how we react to CRC errors in critical chunks */ + switch (crit_action) + { + case PNG_CRC_NO_CHANGE: /* leave setting as is */ + break; + case PNG_CRC_WARN_USE: /* warn/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE; + break; + case PNG_CRC_QUIET_USE: /* quiet/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE | + PNG_FLAG_CRC_CRITICAL_IGNORE; + break; + case PNG_CRC_WARN_DISCARD: /* not a valid action for critical data */ + png_warning(png_ptr, "Can't discard critical data on CRC error."); + case PNG_CRC_ERROR_QUIT: /* error/quit */ + case PNG_CRC_DEFAULT: + default: + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + break; + } + + switch (ancil_action) + { + case PNG_CRC_NO_CHANGE: /* leave setting as is */ + break; + case PNG_CRC_WARN_USE: /* warn/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE; + break; + case PNG_CRC_QUIET_USE: /* quiet/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE | + PNG_FLAG_CRC_ANCILLARY_NOWARN; + break; + case PNG_CRC_ERROR_QUIT: /* error/quit */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN; + break; + case PNG_CRC_WARN_DISCARD: /* warn/discard data */ + case PNG_CRC_DEFAULT: + default: + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + break; + } +} + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ + defined(PNG_FLOATING_POINT_SUPPORTED) +/* handle alpha and tRNS via a background color */ +void PNGAPI +png_set_background(png_structp png_ptr, + png_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma) +{ + png_debug(1, "in png_set_background\n"); + if (background_gamma_code == PNG_BACKGROUND_GAMMA_UNKNOWN) + { + png_warning(png_ptr, "Application must supply a known background gamma"); + return; + } + + png_ptr->transformations |= PNG_BACKGROUND; + png_memcpy(&(png_ptr->background), background_color, + png_sizeof(png_color_16)); + png_ptr->background_gamma = (float)background_gamma; + png_ptr->background_gamma_type = (png_byte)(background_gamma_code); + png_ptr->transformations |= (need_expand ? PNG_BACKGROUND_EXPAND : 0); + + /* Note: if need_expand is set and color_type is either RGB or RGB_ALPHA + * (in which case need_expand is superfluous anyway), the background color + * might actually be gray yet not be flagged as such. This is not a problem + * for the current code, which uses PNG_BACKGROUND_IS_GRAY only to + * decide when to do the png_do_gray_to_rgb() transformation. + */ + if ((need_expand && !(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) || + (!need_expand && background_color->red == background_color->green && + background_color->red == background_color->blue)) + png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; +} +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +/* strip 16 bit depth files to 8 bit depth */ +void PNGAPI +png_set_strip_16(png_structp png_ptr) +{ + png_debug(1, "in png_set_strip_16\n"); + png_ptr->transformations |= PNG_16_TO_8; +} +#endif + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +void PNGAPI +png_set_strip_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_strip_alpha\n"); + png_ptr->flags |= PNG_FLAG_STRIP_ALPHA; +} +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +/* Dither file to 8 bit. Supply a palette, the current number + * of elements in the palette, the maximum number of elements + * allowed, and a histogram if possible. If the current number + * of colors is greater then the maximum number, the palette will be + * modified to fit in the maximum number. "full_dither" indicates + * whether we need a dithering cube set up for RGB images, or if we + * simply are reducing the number of colors in a paletted image. + */ + +typedef struct png_dsort_struct +{ + struct png_dsort_struct FAR * next; + png_byte left; + png_byte right; +} png_dsort; +typedef png_dsort FAR * png_dsortp; +typedef png_dsort FAR * FAR * png_dsortpp; + +void PNGAPI +png_set_dither(png_structp png_ptr, png_colorp palette, + int num_palette, int maximum_colors, png_uint_16p histogram, + int full_dither) +{ + png_debug(1, "in png_set_dither\n"); + png_ptr->transformations |= PNG_DITHER; + + if (!full_dither) + { + int i; + + png_ptr->dither_index = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof (png_byte))); + for (i = 0; i < num_palette; i++) + png_ptr->dither_index[i] = (png_byte)i; + } + + if (num_palette > maximum_colors) + { + if (histogram != NULL) + { + /* This is easy enough, just throw out the least used colors. + Perhaps not the best solution, but good enough. */ + + int i; + + /* initialize an array to sort colors */ + png_ptr->dither_sort = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof (png_byte))); + + /* initialize the dither_sort array */ + for (i = 0; i < num_palette; i++) + png_ptr->dither_sort[i] = (png_byte)i; + + /* Find the least used palette entries by starting a + bubble sort, and running it until we have sorted + out enough colors. Note that we don't care about + sorting all the colors, just finding which are + least used. */ + + for (i = num_palette - 1; i >= maximum_colors; i--) + { + int done; /* to stop early if the list is pre-sorted */ + int j; + + done = 1; + for (j = 0; j < i; j++) + { + if (histogram[png_ptr->dither_sort[j]] + < histogram[png_ptr->dither_sort[j + 1]]) + { + png_byte t; + + t = png_ptr->dither_sort[j]; + png_ptr->dither_sort[j] = png_ptr->dither_sort[j + 1]; + png_ptr->dither_sort[j + 1] = t; + done = 0; + } + } + if (done) + break; + } + + /* swap the palette around, and set up a table, if necessary */ + if (full_dither) + { + int j = num_palette; + + /* put all the useful colors within the max, but don't + move the others */ + for (i = 0; i < maximum_colors; i++) + { + if ((int)png_ptr->dither_sort[i] >= maximum_colors) + { + do + j--; + while ((int)png_ptr->dither_sort[j] >= maximum_colors); + palette[i] = palette[j]; + } + } + } + else + { + int j = num_palette; + + /* move all the used colors inside the max limit, and + develop a translation table */ + for (i = 0; i < maximum_colors; i++) + { + /* only move the colors we need to */ + if ((int)png_ptr->dither_sort[i] >= maximum_colors) + { + png_color tmp_color; + + do + j--; + while ((int)png_ptr->dither_sort[j] >= maximum_colors); + + tmp_color = palette[j]; + palette[j] = palette[i]; + palette[i] = tmp_color; + /* indicate where the color went */ + png_ptr->dither_index[j] = (png_byte)i; + png_ptr->dither_index[i] = (png_byte)j; + } + } + + /* find closest color for those colors we are not using */ + for (i = 0; i < num_palette; i++) + { + if ((int)png_ptr->dither_index[i] >= maximum_colors) + { + int min_d, k, min_k, d_index; + + /* find the closest color to one we threw out */ + d_index = png_ptr->dither_index[i]; + min_d = PNG_COLOR_DIST(palette[d_index], palette[0]); + for (k = 1, min_k = 0; k < maximum_colors; k++) + { + int d; + + d = PNG_COLOR_DIST(palette[d_index], palette[k]); + + if (d < min_d) + { + min_d = d; + min_k = k; + } + } + /* point to closest color */ + png_ptr->dither_index[i] = (png_byte)min_k; + } + } + } + png_free(png_ptr, png_ptr->dither_sort); + png_ptr->dither_sort=NULL; + } + else + { + /* This is much harder to do simply (and quickly). Perhaps + we need to go through a median cut routine, but those + don't always behave themselves with only a few colors + as input. So we will just find the closest two colors, + and throw out one of them (chosen somewhat randomly). + [We don't understand this at all, so if someone wants to + work on improving it, be our guest - AED, GRP] + */ + int i; + int max_d; + int num_new_palette; + png_dsortp t; + png_dsortpp hash; + + t=NULL; + + /* initialize palette index arrays */ + png_ptr->index_to_palette = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof (png_byte))); + png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof (png_byte))); + + /* initialize the sort array */ + for (i = 0; i < num_palette; i++) + { + png_ptr->index_to_palette[i] = (png_byte)i; + png_ptr->palette_to_index[i] = (png_byte)i; + } + + hash = (png_dsortpp)png_malloc(png_ptr, (png_uint_32)(769 * + png_sizeof (png_dsortp))); + for (i = 0; i < 769; i++) + hash[i] = NULL; +/* png_memset(hash, 0, 769 * png_sizeof (png_dsortp)); */ + + num_new_palette = num_palette; + + /* initial wild guess at how far apart the farthest pixel + pair we will be eliminating will be. Larger + numbers mean more areas will be allocated, Smaller + numbers run the risk of not saving enough data, and + having to do this all over again. + + I have not done extensive checking on this number. + */ + max_d = 96; + + while (num_new_palette > maximum_colors) + { + for (i = 0; i < num_new_palette - 1; i++) + { + int j; + + for (j = i + 1; j < num_new_palette; j++) + { + int d; + + d = PNG_COLOR_DIST(palette[i], palette[j]); + + if (d <= max_d) + { + + t = (png_dsortp)png_malloc_warn(png_ptr, + (png_uint_32)(png_sizeof(png_dsort))); + if (t == NULL) + break; + t->next = hash[d]; + t->left = (png_byte)i; + t->right = (png_byte)j; + hash[d] = t; + } + } + if (t == NULL) + break; + } + + if (t != NULL) + for (i = 0; i <= max_d; i++) + { + if (hash[i] != NULL) + { + png_dsortp p; + + for (p = hash[i]; p; p = p->next) + { + if ((int)png_ptr->index_to_palette[p->left] + < num_new_palette && + (int)png_ptr->index_to_palette[p->right] + < num_new_palette) + { + int j, next_j; + + if (num_new_palette & 0x01) + { + j = p->left; + next_j = p->right; + } + else + { + j = p->right; + next_j = p->left; + } + + num_new_palette--; + palette[png_ptr->index_to_palette[j]] + = palette[num_new_palette]; + if (!full_dither) + { + int k; + + for (k = 0; k < num_palette; k++) + { + if (png_ptr->dither_index[k] == + png_ptr->index_to_palette[j]) + png_ptr->dither_index[k] = + png_ptr->index_to_palette[next_j]; + if ((int)png_ptr->dither_index[k] == + num_new_palette) + png_ptr->dither_index[k] = + png_ptr->index_to_palette[j]; + } + } + + png_ptr->index_to_palette[png_ptr->palette_to_index + [num_new_palette]] = png_ptr->index_to_palette[j]; + png_ptr->palette_to_index[png_ptr->index_to_palette[j]] + = png_ptr->palette_to_index[num_new_palette]; + + png_ptr->index_to_palette[j] = (png_byte)num_new_palette; + png_ptr->palette_to_index[num_new_palette] = (png_byte)j; + } + if (num_new_palette <= maximum_colors) + break; + } + if (num_new_palette <= maximum_colors) + break; + } + } + + for (i = 0; i < 769; i++) + { + if (hash[i] != NULL) + { + png_dsortp p = hash[i]; + while (p) + { + t = p->next; + png_free(png_ptr, p); + p = t; + } + } + hash[i] = 0; + } + max_d += 96; + } + png_free(png_ptr, hash); + png_free(png_ptr, png_ptr->palette_to_index); + png_free(png_ptr, png_ptr->index_to_palette); + png_ptr->palette_to_index=NULL; + png_ptr->index_to_palette=NULL; + } + num_palette = maximum_colors; + } + if (png_ptr->palette == NULL) + { + png_ptr->palette = palette; + } + png_ptr->num_palette = (png_uint_16)num_palette; + + if (full_dither) + { + int i; + png_bytep distance; + int total_bits = PNG_DITHER_RED_BITS + PNG_DITHER_GREEN_BITS + + PNG_DITHER_BLUE_BITS; + int num_red = (1 << PNG_DITHER_RED_BITS); + int num_green = (1 << PNG_DITHER_GREEN_BITS); + int num_blue = (1 << PNG_DITHER_BLUE_BITS); + png_size_t num_entries = ((png_size_t)1 << total_bits); + + png_ptr->palette_lookup = (png_bytep )png_malloc(png_ptr, + (png_uint_32)(num_entries * png_sizeof (png_byte))); + + png_memset(png_ptr->palette_lookup, 0, num_entries * + png_sizeof (png_byte)); + + distance = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_entries * + png_sizeof(png_byte))); + + png_memset(distance, 0xff, num_entries * png_sizeof(png_byte)); + + for (i = 0; i < num_palette; i++) + { + int ir, ig, ib; + int r = (palette[i].red >> (8 - PNG_DITHER_RED_BITS)); + int g = (palette[i].green >> (8 - PNG_DITHER_GREEN_BITS)); + int b = (palette[i].blue >> (8 - PNG_DITHER_BLUE_BITS)); + + for (ir = 0; ir < num_red; ir++) + { + /* int dr = abs(ir - r); */ + int dr = ((ir > r) ? ir - r : r - ir); + int index_r = (ir << (PNG_DITHER_BLUE_BITS + PNG_DITHER_GREEN_BITS)); + + for (ig = 0; ig < num_green; ig++) + { + /* int dg = abs(ig - g); */ + int dg = ((ig > g) ? ig - g : g - ig); + int dt = dr + dg; + int dm = ((dr > dg) ? dr : dg); + int index_g = index_r | (ig << PNG_DITHER_BLUE_BITS); + + for (ib = 0; ib < num_blue; ib++) + { + int d_index = index_g | ib; + /* int db = abs(ib - b); */ + int db = ((ib > b) ? ib - b : b - ib); + int dmax = ((dm > db) ? dm : db); + int d = dmax + dt + db; + + if (d < (int)distance[d_index]) + { + distance[d_index] = (png_byte)d; + png_ptr->palette_lookup[d_index] = (png_byte)i; + } + } + } + } + } + + png_free(png_ptr, distance); + } +} +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) +/* Transform the image from the file_gamma to the screen_gamma. We + * only do transformations on images where the file_gamma and screen_gamma + * are not close reciprocals, otherwise it slows things down slightly, and + * also needlessly introduces small errors. + * + * We will turn off gamma transformation later if no semitransparent entries + * are present in the tRNS array for palette images. We can't do it here + * because we don't necessarily have the tRNS chunk yet. + */ +void PNGAPI +png_set_gamma(png_structp png_ptr, double scrn_gamma, double file_gamma) +{ + png_debug(1, "in png_set_gamma\n"); + if ((fabs(scrn_gamma * file_gamma - 1.0) > PNG_GAMMA_THRESHOLD) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) || + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)) + png_ptr->transformations |= PNG_GAMMA; + png_ptr->gamma = (float)file_gamma; + png_ptr->screen_gamma = (float)scrn_gamma; +} +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) +/* Expand paletted images to RGB, expand grayscale images of + * less than 8-bit depth to 8-bit depth, and expand tRNS chunks + * to alpha channels. + */ +void PNGAPI +png_set_expand(png_structp png_ptr) +{ + png_debug(1, "in png_set_expand\n"); + png_ptr->transformations |= PNG_EXPAND; +} + +/* GRR 19990627: the following three functions currently are identical + * to png_set_expand(). However, it is entirely reasonable that someone + * might wish to expand an indexed image to RGB but *not* expand a single, + * fully transparent palette entry to a full alpha channel--perhaps instead + * convert tRNS to the grayscale/RGB format (16-bit RGB value), or replace + * the transparent color with a particular RGB value, or drop tRNS entirely. + * IOW, a future version of the library may make the transformations flag + * a bit more fine-grained, with separate bits for each of these three + * functions. + * + * More to the point, these functions make it obvious what libpng will be + * doing, whereas "expand" can (and does) mean any number of things. + */ + +/* Expand paletted images to RGB. */ +void PNGAPI +png_set_palette_to_rgb(png_structp png_ptr) +{ + png_debug(1, "in png_set_expand\n"); + png_ptr->transformations |= PNG_EXPAND; +} + +/* Expand grayscale images of less than 8-bit depth to 8 bits. */ +void PNGAPI +png_set_gray_1_2_4_to_8(png_structp png_ptr) +{ + png_debug(1, "in png_set_expand\n"); + png_ptr->transformations |= PNG_EXPAND; +} + +/* Expand tRNS chunks to alpha channels. */ +void PNGAPI +png_set_tRNS_to_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_expand\n"); + png_ptr->transformations |= PNG_EXPAND; +} +#endif /* defined(PNG_READ_EXPAND_SUPPORTED) */ + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +void PNGAPI +png_set_gray_to_rgb(png_structp png_ptr) +{ + png_debug(1, "in png_set_gray_to_rgb\n"); + png_ptr->transformations |= PNG_GRAY_TO_RGB; +} +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +#if defined(PNG_FLOATING_POINT_SUPPORTED) +/* Convert a RGB image to a grayscale of the same width. This allows us, + * for example, to convert a 24 bpp RGB image into an 8 bpp grayscale image. + */ + +void PNGAPI +png_set_rgb_to_gray(png_structp png_ptr, int error_action, double red, + double green) +{ + int red_fixed = (int)((float)red*100000.0 + 0.5); + int green_fixed = (int)((float)green*100000.0 + 0.5); + png_set_rgb_to_gray_fixed(png_ptr, error_action, red_fixed, green_fixed); +} +#endif + +void PNGAPI +png_set_rgb_to_gray_fixed(png_structp png_ptr, int error_action, + png_fixed_point red, png_fixed_point green) +{ + png_debug(1, "in png_set_rgb_to_gray\n"); + switch(error_action) + { + case 1: png_ptr->transformations |= PNG_RGB_TO_GRAY; + break; + case 2: png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN; + break; + case 3: png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR; + } + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) +#if defined(PNG_READ_EXPAND_SUPPORTED) + png_ptr->transformations |= PNG_EXPAND; +#else + { + png_warning(png_ptr, "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED."); + png_ptr->transformations &= ~PNG_RGB_TO_GRAY; + } +#endif + { + png_uint_16 red_int, green_int; + if(red < 0 || green < 0) + { + red_int = 6968; /* .212671 * 32768 + .5 */ + green_int = 23434; /* .715160 * 32768 + .5 */ + } + else if(red + green < 100000L) + { + red_int = (png_uint_16)(((png_uint_32)red*32768L)/100000L); + green_int = (png_uint_16)(((png_uint_32)green*32768L)/100000L); + } + else + { + png_warning(png_ptr, "ignoring out of range rgb_to_gray coefficients"); + red_int = 6968; + green_int = 23434; + } + png_ptr->rgb_to_gray_red_coeff = red_int; + png_ptr->rgb_to_gray_green_coeff = green_int; + png_ptr->rgb_to_gray_blue_coeff = (png_uint_16)(32768-red_int-green_int); + } +} +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +void PNGAPI +png_set_read_user_transform_fn(png_structp png_ptr, png_user_transform_ptr + read_user_transform_fn) +{ + png_debug(1, "in png_set_read_user_transform_fn\n"); +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + png_ptr->transformations |= PNG_USER_TRANSFORM; + png_ptr->read_user_transform_fn = read_user_transform_fn; +#endif +#ifdef PNG_LEGACY_SUPPORTED + if(read_user_transform_fn) + png_warning(png_ptr, + "This version of libpng does not support user transforms"); +#endif +} +#endif + +/* Initialize everything needed for the read. This includes modifying + * the palette. + */ +void /* PRIVATE */ +png_init_read_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_init_read_transformations\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if(png_ptr != NULL) +#endif + { +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || defined(PNG_READ_SHIFT_SUPPORTED) \ + || defined(PNG_READ_GAMMA_SUPPORTED) + int color_type = png_ptr->color_type; +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED) + if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + (png_ptr->transformations & PNG_EXPAND)) + { + if (!(color_type & PNG_COLOR_MASK_COLOR)) /* i.e., GRAY or GRAY_ALPHA */ + { + /* expand background chunk. */ + switch (png_ptr->bit_depth) + { + case 1: + png_ptr->background.gray *= (png_uint_16)0xff; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + break; + case 2: + png_ptr->background.gray *= (png_uint_16)0x55; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + break; + case 4: + png_ptr->background.gray *= (png_uint_16)0x11; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + break; + case 8: + case 16: + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + break; + } + } + else if (color_type == PNG_COLOR_TYPE_PALETTE) + { + png_ptr->background.red = + png_ptr->palette[png_ptr->background.index].red; + png_ptr->background.green = + png_ptr->palette[png_ptr->background.index].green; + png_ptr->background.blue = + png_ptr->palette[png_ptr->background.index].blue; + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_ALPHA) + { +#if defined(PNG_READ_EXPAND_SUPPORTED) + if (!(png_ptr->transformations & PNG_EXPAND)) +#endif + { + /* invert the alpha channel (in tRNS) unless the pixels are + going to be expanded, in which case leave it for later */ + int i,istop; + istop=(int)png_ptr->num_trans; + for (i=0; itrans[i] = (png_byte)(255 - png_ptr->trans[i]); + } + } +#endif + + } + } +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED) + png_ptr->background_1 = png_ptr->background; +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) + + if ((color_type == PNG_COLOR_TYPE_PALETTE && png_ptr->num_trans != 0) + && (fabs(png_ptr->screen_gamma * png_ptr->gamma - 1.0) + < PNG_GAMMA_THRESHOLD)) + { + int i,k; + k=0; + for (i=0; inum_trans; i++) + { + if (png_ptr->trans[i] != 0 && png_ptr->trans[i] != 0xff) + k=1; /* partial transparency is present */ + } + if (k == 0) + png_ptr->transformations &= (~PNG_GAMMA); + } + + if (png_ptr->transformations & (PNG_GAMMA | PNG_RGB_TO_GRAY)) + { + png_build_gamma_table(png_ptr); +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->transformations & PNG_BACKGROUND) + { + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + /* could skip if no transparency and + */ + png_color back, back_1; + png_colorp palette = png_ptr->palette; + int num_palette = png_ptr->num_palette; + int i; + if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE) + { + back.red = png_ptr->gamma_table[png_ptr->background.red]; + back.green = png_ptr->gamma_table[png_ptr->background.green]; + back.blue = png_ptr->gamma_table[png_ptr->background.blue]; + + back_1.red = png_ptr->gamma_to_1[png_ptr->background.red]; + back_1.green = png_ptr->gamma_to_1[png_ptr->background.green]; + back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue]; + } + else + { + double g, gs; + + switch (png_ptr->background_gamma_type) + { + case PNG_BACKGROUND_GAMMA_SCREEN: + g = (png_ptr->screen_gamma); + gs = 1.0; + break; + case PNG_BACKGROUND_GAMMA_FILE: + g = 1.0 / (png_ptr->gamma); + gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + break; + case PNG_BACKGROUND_GAMMA_UNIQUE: + g = 1.0 / (png_ptr->background_gamma); + gs = 1.0 / (png_ptr->background_gamma * + png_ptr->screen_gamma); + break; + default: + g = 1.0; /* back_1 */ + gs = 1.0; /* back */ + } + + if ( fabs(gs - 1.0) < PNG_GAMMA_THRESHOLD) + { + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + } + else + { + back.red = (png_byte)(pow( + (double)png_ptr->background.red/255, gs) * 255.0 + .5); + back.green = (png_byte)(pow( + (double)png_ptr->background.green/255, gs) * 255.0 + .5); + back.blue = (png_byte)(pow( + (double)png_ptr->background.blue/255, gs) * 255.0 + .5); + } + + back_1.red = (png_byte)(pow( + (double)png_ptr->background.red/255, g) * 255.0 + .5); + back_1.green = (png_byte)(pow( + (double)png_ptr->background.green/255, g) * 255.0 + .5); + back_1.blue = (png_byte)(pow( + (double)png_ptr->background.blue/255, g) * 255.0 + .5); + } + for (i = 0; i < num_palette; i++) + { + if (i < (int)png_ptr->num_trans && png_ptr->trans[i] != 0xff) + { + if (png_ptr->trans[i] == 0) + { + palette[i] = back; + } + else /* if (png_ptr->trans[i] != 0xff) */ + { + png_byte v, w; + + v = png_ptr->gamma_to_1[palette[i].red]; + png_composite(w, v, png_ptr->trans[i], back_1.red); + palette[i].red = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].green]; + png_composite(w, v, png_ptr->trans[i], back_1.green); + palette[i].green = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].blue]; + png_composite(w, v, png_ptr->trans[i], back_1.blue); + palette[i].blue = png_ptr->gamma_from_1[w]; + } + } + else + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + } + /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */ + else + /* color_type != PNG_COLOR_TYPE_PALETTE */ + { + double m = (double)(((png_uint_32)1 << png_ptr->bit_depth) - 1); + double g = 1.0; + double gs = 1.0; + + switch (png_ptr->background_gamma_type) + { + case PNG_BACKGROUND_GAMMA_SCREEN: + g = (png_ptr->screen_gamma); + gs = 1.0; + break; + case PNG_BACKGROUND_GAMMA_FILE: + g = 1.0 / (png_ptr->gamma); + gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + break; + case PNG_BACKGROUND_GAMMA_UNIQUE: + g = 1.0 / (png_ptr->background_gamma); + gs = 1.0 / (png_ptr->background_gamma * + png_ptr->screen_gamma); + break; + } + + png_ptr->background_1.gray = (png_uint_16)(pow( + (double)png_ptr->background.gray / m, g) * m + .5); + png_ptr->background.gray = (png_uint_16)(pow( + (double)png_ptr->background.gray / m, gs) * m + .5); + + if ((png_ptr->background.red != png_ptr->background.green) || + (png_ptr->background.red != png_ptr->background.blue) || + (png_ptr->background.red != png_ptr->background.gray)) + { + /* RGB or RGBA with color background */ + png_ptr->background_1.red = (png_uint_16)(pow( + (double)png_ptr->background.red / m, g) * m + .5); + png_ptr->background_1.green = (png_uint_16)(pow( + (double)png_ptr->background.green / m, g) * m + .5); + png_ptr->background_1.blue = (png_uint_16)(pow( + (double)png_ptr->background.blue / m, g) * m + .5); + png_ptr->background.red = (png_uint_16)(pow( + (double)png_ptr->background.red / m, gs) * m + .5); + png_ptr->background.green = (png_uint_16)(pow( + (double)png_ptr->background.green / m, gs) * m + .5); + png_ptr->background.blue = (png_uint_16)(pow( + (double)png_ptr->background.blue / m, gs) * m + .5); + } + else + { + /* GRAY, GRAY ALPHA, RGB, or RGBA with gray background */ + png_ptr->background_1.red = png_ptr->background_1.green + = png_ptr->background_1.blue = png_ptr->background_1.gray; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + } + } + } + else + /* transformation does not include PNG_BACKGROUND */ +#endif /* PNG_READ_BACKGROUND_SUPPORTED */ + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + png_colorp palette = png_ptr->palette; + int num_palette = png_ptr->num_palette; + int i; + + for (i = 0; i < num_palette; i++) + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + } +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + else +#endif +#endif /* PNG_READ_GAMMA_SUPPORTED && PNG_FLOATING_POINT_SUPPORTED */ +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + /* No GAMMA transformation */ + if ((png_ptr->transformations & PNG_BACKGROUND) && + (color_type == PNG_COLOR_TYPE_PALETTE)) + { + int i; + int istop = (int)png_ptr->num_trans; + png_color back; + png_colorp palette = png_ptr->palette; + + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + + for (i = 0; i < istop; i++) + { + if (png_ptr->trans[i] == 0) + { + palette[i] = back; + } + else if (png_ptr->trans[i] != 0xff) + { + /* The png_composite() macro is defined in png.h */ + png_composite(palette[i].red, palette[i].red, + png_ptr->trans[i], back.red); + png_composite(palette[i].green, palette[i].green, + png_ptr->trans[i], back.green); + png_composite(palette[i].blue, palette[i].blue, + png_ptr->trans[i], back.blue); + } + } + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED */ + +#if defined(PNG_READ_SHIFT_SUPPORTED) + if ((png_ptr->transformations & PNG_SHIFT) && + (color_type == PNG_COLOR_TYPE_PALETTE)) + { + png_uint_16 i; + png_uint_16 istop = png_ptr->num_palette; + int sr = 8 - png_ptr->sig_bit.red; + int sg = 8 - png_ptr->sig_bit.green; + int sb = 8 - png_ptr->sig_bit.blue; + + if (sr < 0 || sr > 8) + sr = 0; + if (sg < 0 || sg > 8) + sg = 0; + if (sb < 0 || sb > 8) + sb = 0; + for (i = 0; i < istop; i++) + { + png_ptr->palette[i].red >>= sr; + png_ptr->palette[i].green >>= sg; + png_ptr->palette[i].blue >>= sb; + } + } +#endif /* PNG_READ_SHIFT_SUPPORTED */ + } +#if !defined(PNG_READ_GAMMA_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) \ + && !defined(PNG_READ_BACKGROUND_SUPPORTED) + if(png_ptr) + return; +#endif +} + +/* Modify the info structure to reflect the transformations. The + * info should be updated so a PNG file could be written with it, + * assuming the transformations result in valid PNG data. + */ +void /* PRIVATE */ +png_read_transform_info(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_read_transform_info\n"); +#if defined(PNG_READ_EXPAND_SUPPORTED) + if (png_ptr->transformations & PNG_EXPAND) + { + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (png_ptr->num_trans) + info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA; + else + info_ptr->color_type = PNG_COLOR_TYPE_RGB; + info_ptr->bit_depth = 8; + info_ptr->num_trans = 0; + } + else + { + if (png_ptr->num_trans) + info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; + if (info_ptr->bit_depth < 8) + info_ptr->bit_depth = 8; + info_ptr->num_trans = 0; + } + } +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->transformations & PNG_BACKGROUND) + { + info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA; + info_ptr->num_trans = 0; + info_ptr->background = png_ptr->background; + } +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (png_ptr->transformations & PNG_GAMMA) + { +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->gamma = png_ptr->gamma; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = png_ptr->int_gamma; +#endif + } +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) + if ((png_ptr->transformations & PNG_16_TO_8) && (info_ptr->bit_depth == 16)) + info_ptr->bit_depth = 8; +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) + if (png_ptr->transformations & PNG_DITHER) + { + if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || + (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) && + png_ptr->palette_lookup && info_ptr->bit_depth == 8) + { + info_ptr->color_type = PNG_COLOR_TYPE_PALETTE; + } + } +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) + if ((png_ptr->transformations & PNG_PACK) && (info_ptr->bit_depth < 8)) + info_ptr->bit_depth = 8; +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + if (png_ptr->transformations & PNG_GRAY_TO_RGB) + info_ptr->color_type |= PNG_COLOR_MASK_COLOR; +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & PNG_RGB_TO_GRAY) + info_ptr->color_type &= ~PNG_COLOR_MASK_COLOR; +#endif + + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + info_ptr->channels = 1; + else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) + info_ptr->channels = 3; + else + info_ptr->channels = 1; + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) + if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA) + info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA; +#endif + + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + info_ptr->channels++; + +#if defined(PNG_READ_FILLER_SUPPORTED) + /* STRIP_ALPHA and FILLER allowed: MASK_ALPHA bit stripped above */ + if ((png_ptr->transformations & PNG_FILLER) && + ((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || + (info_ptr->color_type == PNG_COLOR_TYPE_GRAY))) + { + info_ptr->channels++; + /* if adding a true alpha channel not just filler */ +#if !defined(PNG_1_0_X) + if (png_ptr->transformations & PNG_ADD_ALPHA) + info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; +#endif + } +#endif + +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \ +defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + if(png_ptr->transformations & PNG_USER_TRANSFORM) + { + if(info_ptr->bit_depth < png_ptr->user_transform_depth) + info_ptr->bit_depth = png_ptr->user_transform_depth; + if(info_ptr->channels < png_ptr->user_transform_channels) + info_ptr->channels = png_ptr->user_transform_channels; + } +#endif + + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * + info_ptr->bit_depth); + + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth,info_ptr->width); + +#if !defined(PNG_READ_EXPAND_SUPPORTED) + if(png_ptr) + return; +#endif +} + +/* Transform the row. The order of transformations is significant, + * and is very touchy. If you add a transformation, take care to + * decide how it fits in with the other transformations here. + */ +void /* PRIVATE */ +png_do_read_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_do_read_transformations\n"); +#if !defined(PNG_USELESS_TESTS_SUPPORTED) + if (png_ptr->row_buf == NULL) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[50]; + + sprintf(msg, "NULL row buffer for row %ld, pass %d", png_ptr->row_number, + png_ptr->pass); + png_error(png_ptr, msg); +#else + png_error(png_ptr, "NULL row buffer"); +#endif + } +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) + if (png_ptr->transformations & PNG_EXPAND) + { + if (png_ptr->row_info.color_type == PNG_COLOR_TYPE_PALETTE) + { + png_do_expand_palette(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->palette, png_ptr->trans, png_ptr->num_trans); + } + else + { + if (png_ptr->num_trans) + png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->trans_values)); + else + png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1, + NULL); + } + } +#endif + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) + if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA) + png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + PNG_FLAG_FILLER_AFTER | (png_ptr->flags & PNG_FLAG_STRIP_ALPHA)); +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & PNG_RGB_TO_GRAY) + { + int rgb_error = + png_do_rgb_to_gray(png_ptr, &(png_ptr->row_info), png_ptr->row_buf + 1); + if(rgb_error) + { + png_ptr->rgb_to_gray_status=1; + if(png_ptr->transformations == PNG_RGB_TO_GRAY_WARN) + png_warning(png_ptr, "png_do_rgb_to_gray found nongray pixel"); + if(png_ptr->transformations == PNG_RGB_TO_GRAY_ERR) + png_error(png_ptr, "png_do_rgb_to_gray found nongray pixel"); + } + } +#endif + +/* +From Andreas Dilger e-mail to png-implement, 26 March 1998: + + In most cases, the "simple transparency" should be done prior to doing + gray-to-RGB, or you will have to test 3x as many bytes to check if a + pixel is transparent. You would also need to make sure that the + transparency information is upgraded to RGB. + + To summarize, the current flow is: + - Gray + simple transparency -> compare 1 or 2 gray bytes and composite + with background "in place" if transparent, + convert to RGB if necessary + - Gray + alpha -> composite with gray background and remove alpha bytes, + convert to RGB if necessary + + To support RGB backgrounds for gray images we need: + - Gray + simple transparency -> convert to RGB + simple transparency, compare + 3 or 6 bytes and composite with background + "in place" if transparent (3x compare/pixel + compared to doing composite with gray bkgrnd) + - Gray + alpha -> convert to RGB + alpha, composite with background and + remove alpha bytes (3x float operations/pixel + compared with composite on gray background) + + Greg's change will do this. The reason it wasn't done before is for + performance, as this increases the per-pixel operations. If we would check + in advance if the background was gray or RGB, and position the gray-to-RGB + transform appropriately, then it would save a lot of work/time. + */ + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + /* if gray -> RGB, do so now only if background is non-gray; else do later + * for performance reasons */ + if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && + !(png_ptr->mode & PNG_BACKGROUND_IS_GRAY)) + png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if ((png_ptr->transformations & PNG_BACKGROUND) && + ((png_ptr->num_trans != 0 ) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) + png_do_background(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->trans_values), &(png_ptr->background) +#if defined(PNG_READ_GAMMA_SUPPORTED) + , &(png_ptr->background_1), + png_ptr->gamma_table, png_ptr->gamma_from_1, + png_ptr->gamma_to_1, png_ptr->gamma_16_table, + png_ptr->gamma_16_from_1, png_ptr->gamma_16_to_1, + png_ptr->gamma_shift +#endif +); +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) + if ((png_ptr->transformations & PNG_GAMMA) && +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + !((png_ptr->transformations & PNG_BACKGROUND) && + ((png_ptr->num_trans != 0) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) && +#endif + (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)) + png_do_gamma(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->gamma_table, png_ptr->gamma_16_table, + png_ptr->gamma_shift); +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) + if (png_ptr->transformations & PNG_16_TO_8) + png_do_chop(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) + if (png_ptr->transformations & PNG_DITHER) + { + png_do_dither((png_row_infop)&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->palette_lookup, png_ptr->dither_index); + if(png_ptr->row_info.rowbytes == (png_uint_32)0) + png_error(png_ptr, "png_do_dither returned rowbytes=0"); + } +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_do_unshift(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->shift)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_do_unpack(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + /* if gray -> RGB, do so now only if we did not do so above */ + if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && + (png_ptr->mode & PNG_BACKGROUND_IS_GRAY)) + png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_do_read_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + (png_uint_32)png_ptr->filler, png_ptr->flags); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_ALPHA) + png_do_read_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_ALPHA) + png_do_read_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + if (png_ptr->transformations & PNG_USER_TRANSFORM) + { + if(png_ptr->read_user_transform_fn != NULL) + (*(png_ptr->read_user_transform_fn)) /* user read transform function */ + (png_ptr, /* png_ptr */ + &(png_ptr->row_info), /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_uint_32 rowbytes; number of bytes in row */ + /* png_byte color_type; color type of pixels */ + /* png_byte bit_depth; bit depth of samples */ + /* png_byte channels; number of channels (1-4) */ + /* png_byte pixel_depth; bits per pixel (depth*channels) */ + png_ptr->row_buf + 1); /* start of pixel data for row */ +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + if(png_ptr->user_transform_depth) + png_ptr->row_info.bit_depth = png_ptr->user_transform_depth; + if(png_ptr->user_transform_channels) + png_ptr->row_info.channels = png_ptr->user_transform_channels; +#endif + png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth * + png_ptr->row_info.channels); + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + } +#endif + +} + +#if defined(PNG_READ_PACK_SUPPORTED) +/* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel, + * without changing the actual values. Thus, if you had a row with + * a bit depth of 1, you would end up with bytes that only contained + * the numbers 0 or 1. If you would rather they contain 0 and 255, use + * png_do_shift() after this. + */ +void /* PRIVATE */ +png_do_unpack(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_unpack\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL && row_info->bit_depth < 8) +#else + if (row_info->bit_depth < 8) +#endif + { + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + switch (row_info->bit_depth) + { + case 1: + { + png_bytep sp = row + (png_size_t)((row_width - 1) >> 3); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x01); + if (shift == 7) + { + shift = 0; + sp--; + } + else + shift++; + + dp--; + } + break; + } + case 2: + { + + png_bytep sp = row + (png_size_t)((row_width - 1) >> 2); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x03); + if (shift == 6) + { + shift = 0; + sp--; + } + else + shift += 2; + + dp--; + } + break; + } + case 4: + { + png_bytep sp = row + (png_size_t)((row_width - 1) >> 1); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x0f); + if (shift == 4) + { + shift = 0; + sp--; + } + else + shift = 4; + + dp--; + } + break; + } + } + row_info->bit_depth = 8; + row_info->pixel_depth = (png_byte)(8 * row_info->channels); + row_info->rowbytes = row_width * row_info->channels; + } +} +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) +/* Reverse the effects of png_do_shift. This routine merely shifts the + * pixels back to their significant bits values. Thus, if you have + * a row of bit depth 8, but only 5 are significant, this will shift + * the values back to 0 through 31. + */ +void /* PRIVATE */ +png_do_unshift(png_row_infop row_info, png_bytep row, png_color_8p sig_bits) +{ + png_debug(1, "in png_do_unshift\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && sig_bits != NULL && +#endif + row_info->color_type != PNG_COLOR_TYPE_PALETTE) + { + int shift[4]; + int channels = 0; + int c; + png_uint_16 value = 0; + png_uint_32 row_width = row_info->width; + + if (row_info->color_type & PNG_COLOR_MASK_COLOR) + { + shift[channels++] = row_info->bit_depth - sig_bits->red; + shift[channels++] = row_info->bit_depth - sig_bits->green; + shift[channels++] = row_info->bit_depth - sig_bits->blue; + } + else + { + shift[channels++] = row_info->bit_depth - sig_bits->gray; + } + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + shift[channels++] = row_info->bit_depth - sig_bits->alpha; + } + + for (c = 0; c < channels; c++) + { + if (shift[c] <= 0) + shift[c] = 0; + else + value = 1; + } + + if (!value) + return; + + switch (row_info->bit_depth) + { + case 2: + { + png_bytep bp; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (bp = row, i = 0; i < istop; i++) + { + *bp >>= 1; + *bp++ &= 0x55; + } + break; + } + case 4: + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_byte mask = (png_byte)((((int)0xf0 >> shift[0]) & (int)0xf0) | + (png_byte)((int)0xf >> shift[0])); + + for (i = 0; i < istop; i++) + { + *bp >>= shift[0]; + *bp++ &= mask; + } + break; + } + case 8: + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = row_width * channels; + + for (i = 0; i < istop; i++) + { + *bp++ >>= shift[i%channels]; + } + break; + } + case 16: + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = channels * row_width; + + for (i = 0; i < istop; i++) + { + value = (png_uint_16)((*bp << 8) + *(bp + 1)); + value >>= shift[i%channels]; + *bp++ = (png_byte)(value >> 8); + *bp++ = (png_byte)(value & 0xff); + } + break; + } + } + } +} +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +/* chop rows of bit depth 16 down to 8 */ +void /* PRIVATE */ +png_do_chop(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_chop\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL && row_info->bit_depth == 16) +#else + if (row_info->bit_depth == 16) +#endif + { + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; + png_uint_32 istop = row_info->width * row_info->channels; + + for (i = 0; i> 8)) >> 8; + * + * Approximate calculation with shift/add instead of multiply/divide: + * *dp = ((((png_uint_32)(*sp) << 8) | + * (png_uint_32)((int)(*(sp + 1)) - *sp)) + 128) >> 8; + * + * What we actually do to avoid extra shifting and conversion: + */ + + *dp = *sp + ((((int)(*(sp + 1)) - *sp) > 128) ? 1 : 0); +#else + /* Simply discard the low order byte */ + *dp = *sp; +#endif + } + row_info->bit_depth = 8; + row_info->pixel_depth = (png_byte)(8 * row_info->channels); + row_info->rowbytes = row_info->width * row_info->channels; + } +} +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_swap_alpha\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + png_uint_32 row_width = row_info->width; + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This converts from RGBA to ARGB */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save; + } + } + /* This converts from RRGGBBAA to AARRGGBB */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save[2]; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save[0] = *(--sp); + save[1] = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save[0]; + *(--dp) = save[1]; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This converts from GA to AG */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save; + } + } + /* This converts from GGAA to AAGG */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save[2]; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save[0] = *(--sp); + save[1] = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save[0]; + *(--dp) = save[1]; + } + } + } + } +} +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_read_invert_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_invert_alpha\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + png_uint_32 row_width = row_info->width; + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This inverts the alpha channel in RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + +/* This does nothing: + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + We can replace it with: +*/ + sp-=3; + dp=sp; + } + } + /* This inverts the alpha channel in RRGGBBAA */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = (png_byte)(255 - *(--sp)); + +/* This does nothing: + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + We can replace it with: +*/ + sp-=6; + dp=sp; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This inverts the alpha channel in GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = *(--sp); + } + } + /* This inverts the alpha channel in GGAA */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = (png_byte)(255 - *(--sp)); +/* + *(--dp) = *(--sp); + *(--dp) = *(--sp); +*/ + sp-=2; + dp=sp; + } + } + } + } +} +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) +/* Add filler channel if we have RGB color */ +void /* PRIVATE */ +png_do_read_filler(png_row_infop row_info, png_bytep row, + png_uint_32 filler, png_uint_32 flags) +{ + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + png_byte hi_filler = (png_byte)((filler>>8) & 0xff); + png_byte lo_filler = (png_byte)(filler & 0xff); + + png_debug(1, "in png_do_read_filler\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + if(row_info->bit_depth == 8) + { + /* This changes the data from G to GX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 1; i < row_width; i++) + { + *(--dp) = lo_filler; + *(--dp) = *(--sp); + } + *(--dp) = lo_filler; + row_info->channels = 2; + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + /* This changes the data from G to XG */ + else + { + png_bytep sp = row + (png_size_t)row_width; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = lo_filler; + } + row_info->channels = 2; + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + } + else if(row_info->bit_depth == 16) + { + /* This changes the data from GG to GGXX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width * 2; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 1; i < row_width; i++) + { + *(--dp) = hi_filler; + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = hi_filler; + *(--dp) = lo_filler; + row_info->channels = 2; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + /* This changes the data from GG to XXGG */ + else + { + png_bytep sp = row + (png_size_t)row_width * 2; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = hi_filler; + *(--dp) = lo_filler; + } + row_info->channels = 2; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + } + } /* COLOR_TYPE == GRAY */ + else if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + if(row_info->bit_depth == 8) + { + /* This changes the data from RGB to RGBX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width * 3; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 1; i < row_width; i++) + { + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = lo_filler; + row_info->channels = 4; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + /* This changes the data from RGB to XRGB */ + else + { + png_bytep sp = row + (png_size_t)row_width * 3; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = lo_filler; + } + row_info->channels = 4; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + } + else if(row_info->bit_depth == 16) + { + /* This changes the data from RRGGBB to RRGGBBXX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width * 6; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 1; i < row_width; i++) + { + *(--dp) = hi_filler; + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = hi_filler; + *(--dp) = lo_filler; + row_info->channels = 4; + row_info->pixel_depth = 64; + row_info->rowbytes = row_width * 8; + } + /* This changes the data from RRGGBB to XXRRGGBB */ + else + { + png_bytep sp = row + (png_size_t)row_width * 6; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = hi_filler; + *(--dp) = lo_filler; + } + row_info->channels = 4; + row_info->pixel_depth = 64; + row_info->rowbytes = row_width * 8; + } + } + } /* COLOR_TYPE == RGB */ +} +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +/* expand grayscale files to RGB, with or without alpha */ +void /* PRIVATE */ +png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) +{ + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + png_debug(1, "in png_do_gray_to_rgb\n"); + if (row_info->bit_depth >= 8 && +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + !(row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + if (row_info->bit_depth == 8) + { + png_bytep sp = row + (png_size_t)row_width - 1; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(dp--) = *sp; + *(dp--) = *sp; + *(dp--) = *(sp--); + } + } + else + { + png_bytep sp = row + (png_size_t)row_width * 2 - 1; + png_bytep dp = sp + (png_size_t)row_width * 4; + for (i = 0; i < row_width; i++) + { + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *(sp--); + *(dp--) = *(sp--); + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (row_info->bit_depth == 8) + { + png_bytep sp = row + (png_size_t)row_width * 2 - 1; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(dp--) = *(sp--); + *(dp--) = *sp; + *(dp--) = *sp; + *(dp--) = *(sp--); + } + } + else + { + png_bytep sp = row + (png_size_t)row_width * 4 - 1; + png_bytep dp = sp + (png_size_t)row_width * 4; + for (i = 0; i < row_width; i++) + { + *(dp--) = *(sp--); + *(dp--) = *(sp--); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *(sp--); + *(dp--) = *(sp--); + } + } + } + row_info->channels += (png_byte)2; + row_info->color_type |= PNG_COLOR_MASK_COLOR; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } +} +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +/* reduce RGB files to grayscale, with or without alpha + * using the equation given in Poynton's ColorFAQ at + * + * Copyright (c) 1998-01-04 Charles Poynton poynton at inforamp.net + * + * Y = 0.212671 * R + 0.715160 * G + 0.072169 * B + * + * We approximate this with + * + * Y = 0.21268 * R + 0.7151 * G + 0.07217 * B + * + * which can be expressed with integers as + * + * Y = (6969 * R + 23434 * G + 2365 * B)/32768 + * + * The calculation is to be done in a linear colorspace. + * + * Other integer coefficents can be used via png_set_rgb_to_gray(). + */ +int /* PRIVATE */ +png_do_rgb_to_gray(png_structp png_ptr, png_row_infop row_info, png_bytep row) + +{ + png_uint_32 i; + + png_uint_32 row_width = row_info->width; + int rgb_error = 0; + + png_debug(1, "in png_do_rgb_to_gray\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff; + png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff; + png_uint_32 bc = png_ptr->rgb_to_gray_blue_coeff; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + + for (i = 0; i < row_width; i++) + { + png_byte red = png_ptr->gamma_to_1[*(sp++)]; + png_byte green = png_ptr->gamma_to_1[*(sp++)]; + png_byte blue = png_ptr->gamma_to_1[*(sp++)]; + if(red != green || red != blue) + { + rgb_error |= 1; + *(dp++) = png_ptr->gamma_from_1[ + (rc*red+gc*green+bc*blue)>>15]; + } + else + *(dp++) = *(sp-1); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); + if(red != green || red != blue) + { + rgb_error |= 1; + *(dp++) = (png_byte)((rc*red+gc*green+bc*blue)>>15); + } + else + *(dp++) = *(sp-1); + } + } + } + + else /* RGB bit_depth == 16 */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_16_to_1 != NULL && + png_ptr->gamma_16_from_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, w; + + red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + + if(red == green && red == blue) + w = red; + else + { + png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) >> + png_ptr->gamma_shift][red>>8]; + png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green&0xff) >> + png_ptr->gamma_shift][green>>8]; + png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) >> + png_ptr->gamma_shift][blue>>8]; + png_uint_16 gray16 = (png_uint_16)((rc*red_1 + gc*green_1 + + bc*blue_1)>>15); + w = png_ptr->gamma_16_from_1[(gray16&0xff) >> + png_ptr->gamma_shift][gray16 >> 8]; + rgb_error |= 1; + } + + *(dp++) = (png_byte)((w>>8) & 0xff); + *(dp++) = (png_byte)(w & 0xff); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, gray16; + + red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + + if(red != green || red != blue) + rgb_error |= 1; + gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = (png_byte)((gray16>>8) & 0xff); + *(dp++) = (png_byte)(gray16 & 0xff); + } + } + } + } + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_byte red = png_ptr->gamma_to_1[*(sp++)]; + png_byte green = png_ptr->gamma_to_1[*(sp++)]; + png_byte blue = png_ptr->gamma_to_1[*(sp++)]; + if(red != green || red != blue) + rgb_error |= 1; + *(dp++) = png_ptr->gamma_from_1 + [(rc*red + gc*green + bc*blue)>>15]; + *(dp++) = *(sp++); /* alpha */ + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); + if(red != green || red != blue) + rgb_error |= 1; + *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = *(sp++); /* alpha */ + } + } + } + else /* RGBA bit_depth == 16 */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_16_to_1 != NULL && + png_ptr->gamma_16_from_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, w; + + red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + + if(red == green && red == blue) + w = red; + else + { + png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) >> + png_ptr->gamma_shift][red>>8]; + png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green&0xff) >> + png_ptr->gamma_shift][green>>8]; + png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) >> + png_ptr->gamma_shift][blue>>8]; + png_uint_16 gray16 = (png_uint_16)((rc * red_1 + + gc * green_1 + bc * blue_1)>>15); + w = png_ptr->gamma_16_from_1[(gray16&0xff) >> + png_ptr->gamma_shift][gray16 >> 8]; + rgb_error |= 1; + } + + *(dp++) = (png_byte)((w>>8) & 0xff); + *(dp++) = (png_byte)(w & 0xff); + *(dp++) = *(sp++); /* alpha */ + *(dp++) = *(sp++); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, gray16; + red = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; + if(red != green || red != blue) + rgb_error |= 1; + gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = (png_byte)((gray16>>8) & 0xff); + *(dp++) = (png_byte)(gray16 & 0xff); + *(dp++) = *(sp++); /* alpha */ + *(dp++) = *(sp++); + } + } + } + } + row_info->channels -= (png_byte)2; + row_info->color_type &= ~PNG_COLOR_MASK_COLOR; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } + return rgb_error; +} +#endif + +/* Build a grayscale palette. Palette is assumed to be 1 << bit_depth + * large of png_color. This lets grayscale images be treated as + * paletted. Most useful for gamma correction and simplification + * of code. + */ +void PNGAPI +png_build_grayscale_palette(int bit_depth, png_colorp palette) +{ + int num_palette; + int color_inc; + int i; + int v; + + png_debug(1, "in png_do_build_grayscale_palette\n"); + if (palette == NULL) + return; + + switch (bit_depth) + { + case 1: + num_palette = 2; + color_inc = 0xff; + break; + case 2: + num_palette = 4; + color_inc = 0x55; + break; + case 4: + num_palette = 16; + color_inc = 0x11; + break; + case 8: + num_palette = 256; + color_inc = 1; + break; + default: + num_palette = 0; + color_inc = 0; + break; + } + + for (i = 0, v = 0; i < num_palette; i++, v += color_inc) + { + palette[i].red = (png_byte)v; + palette[i].green = (png_byte)v; + palette[i].blue = (png_byte)v; + } +} + +/* This function is currently unused. Do we really need it? */ +#if defined(PNG_READ_DITHER_SUPPORTED) && defined(PNG_CORRECT_PALETTE_SUPPORTED) +void /* PRIVATE */ +png_correct_palette(png_structp png_ptr, png_colorp palette, + int num_palette) +{ + png_debug(1, "in png_correct_palette\n"); +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ + defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) + if (png_ptr->transformations & (PNG_GAMMA | PNG_BACKGROUND)) + { + png_color back, back_1; + + if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE) + { + back.red = png_ptr->gamma_table[png_ptr->background.red]; + back.green = png_ptr->gamma_table[png_ptr->background.green]; + back.blue = png_ptr->gamma_table[png_ptr->background.blue]; + + back_1.red = png_ptr->gamma_to_1[png_ptr->background.red]; + back_1.green = png_ptr->gamma_to_1[png_ptr->background.green]; + back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue]; + } + else + { + double g; + + g = 1.0 / (png_ptr->background_gamma * png_ptr->screen_gamma); + + if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_SCREEN || + fabs(g - 1.0) < PNG_GAMMA_THRESHOLD) + { + back.red = png_ptr->background.red; + back.green = png_ptr->background.green; + back.blue = png_ptr->background.blue; + } + else + { + back.red = + (png_byte)(pow((double)png_ptr->background.red/255, g) * + 255.0 + 0.5); + back.green = + (png_byte)(pow((double)png_ptr->background.green/255, g) * + 255.0 + 0.5); + back.blue = + (png_byte)(pow((double)png_ptr->background.blue/255, g) * + 255.0 + 0.5); + } + + g = 1.0 / png_ptr->background_gamma; + + back_1.red = + (png_byte)(pow((double)png_ptr->background.red/255, g) * + 255.0 + 0.5); + back_1.green = + (png_byte)(pow((double)png_ptr->background.green/255, g) * + 255.0 + 0.5); + back_1.blue = + (png_byte)(pow((double)png_ptr->background.blue/255, g) * + 255.0 + 0.5); + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_uint_32 i; + + for (i = 0; i < (png_uint_32)num_palette; i++) + { + if (i < png_ptr->num_trans && png_ptr->trans[i] == 0) + { + palette[i] = back; + } + else if (i < png_ptr->num_trans && png_ptr->trans[i] != 0xff) + { + png_byte v, w; + + v = png_ptr->gamma_to_1[png_ptr->palette[i].red]; + png_composite(w, v, png_ptr->trans[i], back_1.red); + palette[i].red = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[png_ptr->palette[i].green]; + png_composite(w, v, png_ptr->trans[i], back_1.green); + palette[i].green = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[png_ptr->palette[i].blue]; + png_composite(w, v, png_ptr->trans[i], back_1.blue); + palette[i].blue = png_ptr->gamma_from_1[w]; + } + else + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + } + else + { + int i; + + for (i = 0; i < num_palette; i++) + { + if (palette[i].red == (png_byte)png_ptr->trans_values.gray) + { + palette[i] = back; + } + else + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + } + } + else +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (png_ptr->transformations & PNG_GAMMA) + { + int i; + + for (i = 0; i < num_palette; i++) + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + else +#endif +#endif +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->transformations & PNG_BACKGROUND) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_color back; + + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + + for (i = 0; i < (int)png_ptr->num_trans; i++) + { + if (png_ptr->trans[i] == 0) + { + palette[i].red = back.red; + palette[i].green = back.green; + palette[i].blue = back.blue; + } + else if (png_ptr->trans[i] != 0xff) + { + png_composite(palette[i].red, png_ptr->palette[i].red, + png_ptr->trans[i], back.red); + png_composite(palette[i].green, png_ptr->palette[i].green, + png_ptr->trans[i], back.green); + png_composite(palette[i].blue, png_ptr->palette[i].blue, + png_ptr->trans[i], back.blue); + } + } + } + else /* assume grayscale palette (what else could it be?) */ + { + int i; + + for (i = 0; i < num_palette; i++) + { + if (i == (png_byte)png_ptr->trans_values.gray) + { + palette[i].red = (png_byte)png_ptr->background.red; + palette[i].green = (png_byte)png_ptr->background.green; + palette[i].blue = (png_byte)png_ptr->background.blue; + } + } + } + } +#endif +} +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) +/* Replace any alpha or transparency with the supplied background color. + * "background" is already in the screen gamma, while "background_1" is + * at a gamma of 1.0. Paletted files have already been taken care of. + */ +void /* PRIVATE */ +png_do_background(png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background +#if defined(PNG_READ_GAMMA_SUPPORTED) + , png_color_16p background_1, + png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, + png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, + png_uint_16pp gamma_16_to_1, int gamma_shift +#endif + ) +{ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + int shift; + + png_debug(1, "in png_do_background\n"); + if (background != NULL && +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (!(row_info->color_type & PNG_COLOR_MASK_ALPHA) || + (row_info->color_type != PNG_COLOR_TYPE_PALETTE && trans_values))) + { + switch (row_info->color_type) + { + case PNG_COLOR_TYPE_GRAY: + { + switch (row_info->bit_depth) + { + case 1: + { + sp = row; + shift = 7; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x01) + == trans_values->gray) + { + *sp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + if (!shift) + { + shift = 7; + sp++; + } + else + shift--; + } + break; + } + case 2: + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_table != NULL) + { + sp = row; + shift = 6; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x03) + == trans_values->gray) + { + *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + else + { + png_byte p = (png_byte)((*sp >> shift) & 0x03); + png_byte g = (png_byte)((gamma_table [p | (p << 2) | + (p << 4) | (p << 6)] >> 6) & 0x03); + *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *sp |= (png_byte)(g << shift); + } + if (!shift) + { + shift = 6; + sp++; + } + else + shift -= 2; + } + } + else +#endif + { + sp = row; + shift = 6; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x03) + == trans_values->gray) + { + *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + if (!shift) + { + shift = 6; + sp++; + } + else + shift -= 2; + } + } + break; + } + case 4: + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_table != NULL) + { + sp = row; + shift = 4; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x0f) + == trans_values->gray) + { + *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + else + { + png_byte p = (png_byte)((*sp >> shift) & 0x0f); + png_byte g = (png_byte)((gamma_table[p | + (p << 4)] >> 4) & 0x0f); + *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *sp |= (png_byte)(g << shift); + } + if (!shift) + { + shift = 4; + sp++; + } + else + shift -= 4; + } + } + else +#endif + { + sp = row; + shift = 4; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x0f) + == trans_values->gray) + { + *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + if (!shift) + { + shift = 4; + sp++; + } + else + shift -= 4; + } + } + break; + } + case 8: + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + if (*sp == trans_values->gray) + { + *sp = (png_byte)background->gray; + } + else + { + *sp = gamma_table[*sp]; + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + if (*sp == trans_values->gray) + { + *sp = (png_byte)background->gray; + } + } + } + break; + } + case 16: + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_16 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_uint_16 v; + + v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + if (v == trans_values->gray) + { + /* background is already in screen gamma */ + *sp = (png_byte)((background->gray >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->gray & 0xff); + } + else + { + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_uint_16 v; + + v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + if (v == trans_values->gray) + { + *sp = (png_byte)((background->gray >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->gray & 0xff); + } + } + } + break; + } + } + break; + } + case PNG_COLOR_TYPE_RGB: + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 3) + { + if (*sp == trans_values->red && + *(sp + 1) == trans_values->green && + *(sp + 2) == trans_values->blue) + { + *sp = (png_byte)background->red; + *(sp + 1) = (png_byte)background->green; + *(sp + 2) = (png_byte)background->blue; + } + else + { + *sp = gamma_table[*sp]; + *(sp + 1) = gamma_table[*(sp + 1)]; + *(sp + 2) = gamma_table[*(sp + 2)]; + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 3) + { + if (*sp == trans_values->red && + *(sp + 1) == trans_values->green && + *(sp + 2) == trans_values->blue) + { + *sp = (png_byte)background->red; + *(sp + 1) = (png_byte)background->green; + *(sp + 2) = (png_byte)background->blue; + } + } + } + } + else /* if (row_info->bit_depth == 16) */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_16 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 6) + { + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5)); + if (r == trans_values->red && g == trans_values->green && + b == trans_values->blue) + { + /* background is already in screen gamma */ + *sp = (png_byte)((background->red >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->red & 0xff); + *(sp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(sp + 3) = (png_byte)(background->green & 0xff); + *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(sp + 5) = (png_byte)(background->blue & 0xff); + } + else + { + png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + *(sp + 2) = (png_byte)((v >> 8) & 0xff); + *(sp + 3) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + *(sp + 4) = (png_byte)((v >> 8) & 0xff); + *(sp + 5) = (png_byte)(v & 0xff); + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 6) + { + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp+1)); + png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5)); + + if (r == trans_values->red && g == trans_values->green && + b == trans_values->blue) + { + *sp = (png_byte)((background->red >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->red & 0xff); + *(sp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(sp + 3) = (png_byte)(background->green & 0xff); + *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(sp + 5) = (png_byte)(background->blue & 0xff); + } + } + } + } + break; + } + case PNG_COLOR_TYPE_GRAY_ALPHA: + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_to_1 != NULL && gamma_from_1 != NULL && + gamma_table != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 2, dp++) + { + png_uint_16 a = *(sp + 1); + + if (a == 0xff) + { + *dp = gamma_table[*sp]; + } + else if (a == 0) + { + /* background is already in screen gamma */ + *dp = (png_byte)background->gray; + } + else + { + png_byte v, w; + + v = gamma_to_1[*sp]; + png_composite(w, v, a, background_1->gray); + *dp = gamma_from_1[w]; + } + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 2, dp++) + { + png_byte a = *(sp + 1); + + if (a == 0xff) + { + *dp = *sp; + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else if (a == 0) + { + *dp = (png_byte)background->gray; + } + else + { + png_composite(*dp, *sp, a, background_1->gray); + } +#else + *dp = (png_byte)background->gray; +#endif + } + } + } + else /* if (png_ptr->bit_depth == 16) */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_16 != NULL && gamma_16_from_1 != NULL && + gamma_16_to_1 != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 2) + { + png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + + if (a == (png_uint_16)0xffff) + { + png_uint_16 v; + + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else if (a == 0) +#else + else +#endif + { + /* background is already in screen gamma */ + *dp = (png_byte)((background->gray >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->gray & 0xff); + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else + { + png_uint_16 g, v, w; + + g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + png_composite_16(v, g, a, background_1->gray); + w = gamma_16_from_1[(v&0xff) >> gamma_shift][v >> 8]; + *dp = (png_byte)((w >> 8) & 0xff); + *(dp + 1) = (png_byte)(w & 0xff); + } +#endif + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 2) + { + png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + if (a == (png_uint_16)0xffff) + { + png_memcpy(dp, sp, 2); + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else if (a == 0) +#else + else +#endif + { + *dp = (png_byte)((background->gray >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->gray & 0xff); + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else + { + png_uint_16 g, v; + + g = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_composite_16(v, g, a, background_1->gray); + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + } +#endif + } + } + } + break; + } + case PNG_COLOR_TYPE_RGB_ALPHA: + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_to_1 != NULL && gamma_from_1 != NULL && + gamma_table != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 3) + { + png_byte a = *(sp + 3); + + if (a == 0xff) + { + *dp = gamma_table[*sp]; + *(dp + 1) = gamma_table[*(sp + 1)]; + *(dp + 2) = gamma_table[*(sp + 2)]; + } + else if (a == 0) + { + /* background is already in screen gamma */ + *dp = (png_byte)background->red; + *(dp + 1) = (png_byte)background->green; + *(dp + 2) = (png_byte)background->blue; + } + else + { + png_byte v, w; + + v = gamma_to_1[*sp]; + png_composite(w, v, a, background_1->red); + *dp = gamma_from_1[w]; + v = gamma_to_1[*(sp + 1)]; + png_composite(w, v, a, background_1->green); + *(dp + 1) = gamma_from_1[w]; + v = gamma_to_1[*(sp + 2)]; + png_composite(w, v, a, background_1->blue); + *(dp + 2) = gamma_from_1[w]; + } + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 3) + { + png_byte a = *(sp + 3); + + if (a == 0xff) + { + *dp = *sp; + *(dp + 1) = *(sp + 1); + *(dp + 2) = *(sp + 2); + } + else if (a == 0) + { + *dp = (png_byte)background->red; + *(dp + 1) = (png_byte)background->green; + *(dp + 2) = (png_byte)background->blue; + } + else + { + png_composite(*dp, *sp, a, background->red); + png_composite(*(dp + 1), *(sp + 1), a, + background->green); + png_composite(*(dp + 2), *(sp + 2), a, + background->blue); + } + } + } + } + else /* if (row_info->bit_depth == 16) */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_16 != NULL && gamma_16_from_1 != NULL && + gamma_16_to_1 != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 8, dp += 6) + { + png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) + << 8) + (png_uint_16)(*(sp + 7))); + if (a == (png_uint_16)0xffff) + { + png_uint_16 v; + + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + *(dp + 2) = (png_byte)((v >> 8) & 0xff); + *(dp + 3) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + *(dp + 4) = (png_byte)((v >> 8) & 0xff); + *(dp + 5) = (png_byte)(v & 0xff); + } + else if (a == 0) + { + /* background is already in screen gamma */ + *dp = (png_byte)((background->red >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->red & 0xff); + *(dp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(dp + 3) = (png_byte)(background->green & 0xff); + *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(dp + 5) = (png_byte)(background->blue & 0xff); + } + else + { + png_uint_16 v, w, x; + + v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + png_composite_16(w, v, a, background_1->red); + x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8]; + *dp = (png_byte)((x >> 8) & 0xff); + *(dp + 1) = (png_byte)(x & 0xff); + v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)]; + png_composite_16(w, v, a, background_1->green); + x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8]; + *(dp + 2) = (png_byte)((x >> 8) & 0xff); + *(dp + 3) = (png_byte)(x & 0xff); + v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)]; + png_composite_16(w, v, a, background_1->blue); + x = gamma_16_from_1[(w & 0xff) >> gamma_shift][w >> 8]; + *(dp + 4) = (png_byte)((x >> 8) & 0xff); + *(dp + 5) = (png_byte)(x & 0xff); + } + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 8, dp += 6) + { + png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) + << 8) + (png_uint_16)(*(sp + 7))); + if (a == (png_uint_16)0xffff) + { + png_memcpy(dp, sp, 6); + } + else if (a == 0) + { + *dp = (png_byte)((background->red >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->red & 0xff); + *(dp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(dp + 3) = (png_byte)(background->green & 0xff); + *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(dp + 5) = (png_byte)(background->blue & 0xff); + } + else + { + png_uint_16 v; + + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) + + *(sp + 5)); + + png_composite_16(v, r, a, background->red); + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + png_composite_16(v, g, a, background->green); + *(dp + 2) = (png_byte)((v >> 8) & 0xff); + *(dp + 3) = (png_byte)(v & 0xff); + png_composite_16(v, b, a, background->blue); + *(dp + 4) = (png_byte)((v >> 8) & 0xff); + *(dp + 5) = (png_byte)(v & 0xff); + } + } + } + } + break; + } + } + + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + row_info->color_type &= ~PNG_COLOR_MASK_ALPHA; + row_info->channels--; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } + } +} +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) +/* Gamma correct the image, avoiding the alpha channel. Make sure + * you do this after you deal with the transparency issue on grayscale + * or RGB images. If your bit depth is 8, use gamma_table, if it + * is 16, use gamma_16_table and gamma_shift. Build these with + * build_gamma_table(). + */ +void /* PRIVATE */ +png_do_gamma(png_row_infop row_info, png_bytep row, + png_bytep gamma_table, png_uint_16pp gamma_16_table, + int gamma_shift) +{ + png_bytep sp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_gamma\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + ((row_info->bit_depth <= 8 && gamma_table != NULL) || + (row_info->bit_depth == 16 && gamma_16_table != NULL))) + { + switch (row_info->color_type) + { + case PNG_COLOR_TYPE_RGB: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + } + } + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v; + + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + } + } + break; + } + case PNG_COLOR_TYPE_RGB_ALPHA: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + sp++; + } + } + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 4; + } + } + break; + } + case PNG_COLOR_TYPE_GRAY_ALPHA: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp += 2; + } + } + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 4; + } + } + break; + } + case PNG_COLOR_TYPE_GRAY: + { + if (row_info->bit_depth == 2) + { + sp = row; + for (i = 0; i < row_width; i += 4) + { + int a = *sp & 0xc0; + int b = *sp & 0x30; + int c = *sp & 0x0c; + int d = *sp & 0x03; + + *sp = (png_byte)( + ((((int)gamma_table[a|(a>>2)|(a>>4)|(a>>6)]) ) & 0xc0)| + ((((int)gamma_table[(b<<2)|b|(b>>2)|(b>>4)])>>2) & 0x30)| + ((((int)gamma_table[(c<<4)|(c<<2)|c|(c>>2)])>>4) & 0x0c)| + ((((int)gamma_table[(d<<6)|(d<<4)|(d<<2)|d])>>6) )); + sp++; + } + } + if (row_info->bit_depth == 4) + { + sp = row; + for (i = 0; i < row_width; i += 2) + { + int msb = *sp & 0xf0; + int lsb = *sp & 0x0f; + + *sp = (png_byte)((((int)gamma_table[msb | (msb >> 4)]) & 0xf0) + | (((int)gamma_table[(lsb << 4) | lsb]) >> 4)); + sp++; + } + } + else if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + } + } + else if (row_info->bit_depth == 16) + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + } + } + break; + } + } + } +} +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) +/* Expands a palette row to an RGB or RGBA row depending + * upon whether you supply trans and num_trans. + */ +void /* PRIVATE */ +png_do_expand_palette(png_row_infop row_info, png_bytep row, + png_colorp palette, png_bytep trans, int num_trans) +{ + int shift, value; + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_expand_palette\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (row_info->bit_depth < 8) + { + switch (row_info->bit_depth) + { + case 1: + { + sp = row + (png_size_t)((row_width - 1) >> 3); + dp = row + (png_size_t)row_width - 1; + shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + if ((*sp >> shift) & 0x01) + *dp = 1; + else + *dp = 0; + if (shift == 7) + { + shift = 0; + sp--; + } + else + shift++; + + dp--; + } + break; + } + case 2: + { + sp = row + (png_size_t)((row_width - 1) >> 2); + dp = row + (png_size_t)row_width - 1; + shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x03; + *dp = (png_byte)value; + if (shift == 6) + { + shift = 0; + sp--; + } + else + shift += 2; + + dp--; + } + break; + } + case 4: + { + sp = row + (png_size_t)((row_width - 1) >> 1); + dp = row + (png_size_t)row_width - 1; + shift = (int)((row_width & 0x01) << 2); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x0f; + *dp = (png_byte)value; + if (shift == 4) + { + shift = 0; + sp--; + } + else + shift += 4; + + dp--; + } + break; + } + } + row_info->bit_depth = 8; + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + switch (row_info->bit_depth) + { + case 8: + { + if (trans != NULL) + { + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width << 2) - 1; + + for (i = 0; i < row_width; i++) + { + if ((int)(*sp) >= num_trans) + *dp-- = 0xff; + else + *dp-- = trans[*sp]; + *dp-- = palette[*sp].blue; + *dp-- = palette[*sp].green; + *dp-- = palette[*sp].red; + sp--; + } + row_info->bit_depth = 8; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + row_info->color_type = 6; + row_info->channels = 4; + } + else + { + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width * 3) - 1; + + for (i = 0; i < row_width; i++) + { + *dp-- = palette[*sp].blue; + *dp-- = palette[*sp].green; + *dp-- = palette[*sp].red; + sp--; + } + row_info->bit_depth = 8; + row_info->pixel_depth = 24; + row_info->rowbytes = row_width * 3; + row_info->color_type = 2; + row_info->channels = 3; + } + break; + } + } + } +} + +/* If the bit depth < 8, it is expanded to 8. Also, if the + * transparency value is supplied, an alpha channel is built. + */ +void /* PRIVATE */ +png_do_expand(png_row_infop row_info, png_bytep row, + png_color_16p trans_value) +{ + int shift, value; + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_expand\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + png_uint_16 gray = (png_uint_16)(trans_value ? trans_value->gray : 0); + + if (row_info->bit_depth < 8) + { + switch (row_info->bit_depth) + { + case 1: + { + gray = (png_uint_16)(gray*0xff); + sp = row + (png_size_t)((row_width - 1) >> 3); + dp = row + (png_size_t)row_width - 1; + shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + if ((*sp >> shift) & 0x01) + *dp = 0xff; + else + *dp = 0; + if (shift == 7) + { + shift = 0; + sp--; + } + else + shift++; + + dp--; + } + break; + } + case 2: + { + gray = (png_uint_16)(gray*0x55); + sp = row + (png_size_t)((row_width - 1) >> 2); + dp = row + (png_size_t)row_width - 1; + shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x03; + *dp = (png_byte)(value | (value << 2) | (value << 4) | + (value << 6)); + if (shift == 6) + { + shift = 0; + sp--; + } + else + shift += 2; + + dp--; + } + break; + } + case 4: + { + gray = (png_uint_16)(gray*0x11); + sp = row + (png_size_t)((row_width - 1) >> 1); + dp = row + (png_size_t)row_width - 1; + shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x0f; + *dp = (png_byte)(value | (value << 4)); + if (shift == 4) + { + shift = 0; + sp--; + } + else + shift = 4; + + dp--; + } + break; + } + } + row_info->bit_depth = 8; + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + + if (trans_value != NULL) + { + if (row_info->bit_depth == 8) + { + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width << 1) - 1; + for (i = 0; i < row_width; i++) + { + if (*sp == gray) + *dp-- = 0; + else + *dp-- = 0xff; + *dp-- = *sp--; + } + } + else if (row_info->bit_depth == 16) + { + sp = row + row_info->rowbytes - 1; + dp = row + (row_info->rowbytes << 1) - 1; + for (i = 0; i < row_width; i++) + { + if (((png_uint_16)*(sp) | + ((png_uint_16)*(sp - 1) << 8)) == gray) + { + *dp-- = 0; + *dp-- = 0; + } + else + { + *dp-- = 0xff; + *dp-- = 0xff; + } + *dp-- = *sp--; + *dp-- = *sp--; + } + } + row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA; + row_info->channels = 2; + row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_width); + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB && trans_value) + { + if (row_info->bit_depth == 8) + { + sp = row + (png_size_t)row_info->rowbytes - 1; + dp = row + (png_size_t)(row_width << 2) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 2) == trans_value->red && + *(sp - 1) == trans_value->green && + *(sp - 0) == trans_value->blue) + *dp-- = 0; + else + *dp-- = 0xff; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + } + } + else if (row_info->bit_depth == 16) + { + sp = row + row_info->rowbytes - 1; + dp = row + (png_size_t)(row_width << 3) - 1; + for (i = 0; i < row_width; i++) + { + if ((((png_uint_16)*(sp - 4) | + ((png_uint_16)*(sp - 5) << 8)) == trans_value->red) && + (((png_uint_16)*(sp - 2) | + ((png_uint_16)*(sp - 3) << 8)) == trans_value->green) && + (((png_uint_16)*(sp - 0) | + ((png_uint_16)*(sp - 1) << 8)) == trans_value->blue)) + { + *dp-- = 0; + *dp-- = 0; + } + else + { + *dp-- = 0xff; + *dp-- = 0xff; + } + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + } + } + row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA; + row_info->channels = 4; + row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } + } +} +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +void /* PRIVATE */ +png_do_dither(png_row_infop row_info, png_bytep row, + png_bytep palette_lookup, png_bytep dither_lookup) +{ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_dither\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB && + palette_lookup && row_info->bit_depth == 8) + { + int r, g, b, p; + sp = row; + dp = row; + for (i = 0; i < row_width; i++) + { + r = *sp++; + g = *sp++; + b = *sp++; + + /* this looks real messy, but the compiler will reduce + it down to a reasonable formula. For example, with + 5 bits per color, we get: + p = (((r >> 3) & 0x1f) << 10) | + (((g >> 3) & 0x1f) << 5) | + ((b >> 3) & 0x1f); + */ + p = (((r >> (8 - PNG_DITHER_RED_BITS)) & + ((1 << PNG_DITHER_RED_BITS) - 1)) << + (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS)) | + (((g >> (8 - PNG_DITHER_GREEN_BITS)) & + ((1 << PNG_DITHER_GREEN_BITS) - 1)) << + (PNG_DITHER_BLUE_BITS)) | + ((b >> (8 - PNG_DITHER_BLUE_BITS)) & + ((1 << PNG_DITHER_BLUE_BITS) - 1)); + + *dp++ = palette_lookup[p]; + } + row_info->color_type = PNG_COLOR_TYPE_PALETTE; + row_info->channels = 1; + row_info->pixel_depth = row_info->bit_depth; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && + palette_lookup != NULL && row_info->bit_depth == 8) + { + int r, g, b, p; + sp = row; + dp = row; + for (i = 0; i < row_width; i++) + { + r = *sp++; + g = *sp++; + b = *sp++; + sp++; + + p = (((r >> (8 - PNG_DITHER_RED_BITS)) & + ((1 << PNG_DITHER_RED_BITS) - 1)) << + (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS)) | + (((g >> (8 - PNG_DITHER_GREEN_BITS)) & + ((1 << PNG_DITHER_GREEN_BITS) - 1)) << + (PNG_DITHER_BLUE_BITS)) | + ((b >> (8 - PNG_DITHER_BLUE_BITS)) & + ((1 << PNG_DITHER_BLUE_BITS) - 1)); + + *dp++ = palette_lookup[p]; + } + row_info->color_type = PNG_COLOR_TYPE_PALETTE; + row_info->channels = 1; + row_info->pixel_depth = row_info->bit_depth; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } + else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE && + dither_lookup && row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + *sp = dither_lookup[*sp]; + } + } + } +} +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +#if defined(PNG_READ_GAMMA_SUPPORTED) +static int png_gamma_shift[] = + {0x10, 0x21, 0x42, 0x84, 0x110, 0x248, 0x550, 0xff0}; + +/* We build the 8- or 16-bit gamma tables here. Note that for 16-bit + * tables, we don't make a full table if we are reducing to 8-bit in + * the future. Note also how the gamma_16 tables are segmented so that + * we don't need to allocate > 64K chunks for a full 16-bit table. + */ +void /* PRIVATE */ +png_build_gamma_table(png_structp png_ptr) +{ + png_debug(1, "in png_build_gamma_table\n"); + if(png_ptr->gamma != 0.0) + { + if (png_ptr->bit_depth <= 8) + { + int i; + double g; + + if (png_ptr->screen_gamma > .000001) + g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + else + g = 1.0; + + png_ptr->gamma_table = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + + for (i = 0; i < 256; i++) + { + png_ptr->gamma_table[i] = (png_byte)(pow((double)i / 255.0, + g) * 255.0 + .5); + } + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & ((PNG_BACKGROUND) | PNG_RGB_TO_GRAY)) + { + + g = 1.0 / (png_ptr->gamma); + + png_ptr->gamma_to_1 = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + + for (i = 0; i < 256; i++) + { + png_ptr->gamma_to_1[i] = (png_byte)(pow((double)i / 255.0, + g) * 255.0 + .5); + } + + + png_ptr->gamma_from_1 = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + + if(png_ptr->screen_gamma > 0.000001) + g = 1.0 / png_ptr->screen_gamma; + else + g = png_ptr->gamma; /* probably doing rgb_to_gray */ + + for (i = 0; i < 256; i++) + { + png_ptr->gamma_from_1[i] = (png_byte)(pow((double)i / 255.0, + g) * 255.0 + .5); + + } + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */ + } + else + { + double g; + int i, j, shift, num; + int sig_bit; + png_uint_32 ig; + + if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + { + sig_bit = (int)png_ptr->sig_bit.red; + if ((int)png_ptr->sig_bit.green > sig_bit) + sig_bit = png_ptr->sig_bit.green; + if ((int)png_ptr->sig_bit.blue > sig_bit) + sig_bit = png_ptr->sig_bit.blue; + } + else + { + sig_bit = (int)png_ptr->sig_bit.gray; + } + + if (sig_bit > 0) + shift = 16 - sig_bit; + else + shift = 0; + + if (png_ptr->transformations & PNG_16_TO_8) + { + if (shift < (16 - PNG_MAX_GAMMA_8)) + shift = (16 - PNG_MAX_GAMMA_8); + } + + if (shift > 8) + shift = 8; + if (shift < 0) + shift = 0; + + png_ptr->gamma_shift = (png_byte)shift; + + num = (1 << (8 - shift)); + + if (png_ptr->screen_gamma > .000001) + g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + else + g = 1.0; + + png_ptr->gamma_16_table = (png_uint_16pp)png_malloc(png_ptr, + (png_uint_32)(num * png_sizeof (png_uint_16p))); + + if (png_ptr->transformations & (PNG_16_TO_8 | PNG_BACKGROUND)) + { + double fin, fout; + png_uint_32 last, max; + + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof (png_uint_16))); + } + + g = 1.0 / g; + last = 0; + for (i = 0; i < 256; i++) + { + fout = ((double)i + 0.5) / 256.0; + fin = pow(fout, g); + max = (png_uint_32)(fin * (double)((png_uint_32)num << 8)); + while (last <= max) + { + png_ptr->gamma_16_table[(int)(last & (0xff >> shift))] + [(int)(last >> (8 - shift))] = (png_uint_16)( + (png_uint_16)i | ((png_uint_16)i << 8)); + last++; + } + } + while (last < ((png_uint_32)num << 8)) + { + png_ptr->gamma_16_table[(int)(last & (0xff >> shift))] + [(int)(last >> (8 - shift))] = (png_uint_16)65535L; + last++; + } + } + else + { + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof (png_uint_16))); + + ig = (((png_uint_32)i * (png_uint_32)png_gamma_shift[shift]) >> 4); + for (j = 0; j < 256; j++) + { + png_ptr->gamma_16_table[i][j] = + (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / + 65535.0, g) * 65535.0 + .5); + } + } + } + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & (PNG_BACKGROUND | PNG_RGB_TO_GRAY)) + { + + g = 1.0 / (png_ptr->gamma); + + png_ptr->gamma_16_to_1 = (png_uint_16pp)png_malloc(png_ptr, + (png_uint_32)(num * png_sizeof (png_uint_16p ))); + + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_to_1[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof (png_uint_16))); + + ig = (((png_uint_32)i * + (png_uint_32)png_gamma_shift[shift]) >> 4); + for (j = 0; j < 256; j++) + { + png_ptr->gamma_16_to_1[i][j] = + (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / + 65535.0, g) * 65535.0 + .5); + } + } + + if(png_ptr->screen_gamma > 0.000001) + g = 1.0 / png_ptr->screen_gamma; + else + g = png_ptr->gamma; /* probably doing rgb_to_gray */ + + png_ptr->gamma_16_from_1 = (png_uint_16pp)png_malloc(png_ptr, + (png_uint_32)(num * png_sizeof (png_uint_16p))); + + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_from_1[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof (png_uint_16))); + + ig = (((png_uint_32)i * + (png_uint_32)png_gamma_shift[shift]) >> 4); + for (j = 0; j < 256; j++) + { + png_ptr->gamma_16_from_1[i][j] = + (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / + 65535.0, g) * 65535.0 + .5); + } + } + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */ + } + } +} +#endif +/* To do: install integer version of png_build_gamma_table here */ +#endif + +#if defined(PNG_MNG_FEATURES_SUPPORTED) +/* undoes intrapixel differencing */ +void /* PRIVATE */ +png_do_read_intrapixel(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_intrapixel\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + int bytes_per_pixel; + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 3; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 4; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + *(rp) = (png_byte)((256 + *rp + *(rp+1))&0xff); + *(rp+2) = (png_byte)((256 + *(rp+2) + *(rp+1))&0xff); + } + } + else if (row_info->bit_depth == 16) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 6; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 8; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + png_uint_32 s0 = (*(rp ) << 8) | *(rp+1); + png_uint_32 s1 = (*(rp+2) << 8) | *(rp+3); + png_uint_32 s2 = (*(rp+4) << 8) | *(rp+5); + png_uint_32 red = (png_uint_32)((s0+s1+65536L) & 0xffffL); + png_uint_32 blue = (png_uint_32)((s2+s1+65536L) & 0xffffL); + *(rp ) = (png_byte)((red >> 8) & 0xff); + *(rp+1) = (png_byte)(red & 0xff); + *(rp+4) = (png_byte)((blue >> 8) & 0xff); + *(rp+5) = (png_byte)(blue & 0xff); + } + } + } +} +#endif /* PNG_MNG_FEATURES_SUPPORTED */ diff --git a/com32/lib/libpng/pngrutil.c b/com32/lib/libpng/pngrutil.c new file mode 100644 index 0000000..99e466f --- /dev/null +++ b/com32/lib/libpng/pngrutil.c @@ -0,0 +1,3124 @@ +/* pngrutil.c - utilities to read a PNG file + * + * libpng version 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file contains routines that are only called from within + * libpng itself during the course of reading an image. + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(_WIN32_WCE) +/* strtod() function is not supported on WindowsCE */ +# ifdef PNG_FLOATING_POINT_SUPPORTED +__inline double strtod(const char *nptr, char **endptr) +{ + double result = 0; + int len; + wchar_t *str, *end; + + len = MultiByteToWideChar(CP_ACP, 0, nptr, -1, NULL, 0); + str = (wchar_t *)malloc(len * sizeof(wchar_t)); + if ( NULL != str ) + { + MultiByteToWideChar(CP_ACP, 0, nptr, -1, str, len); + result = wcstod(str, &end); + len = WideCharToMultiByte(CP_ACP, 0, end, -1, NULL, 0, NULL, NULL); + *endptr = (char *)nptr + (png_strlen(nptr) - len + 1); + free(str); + } + return result; +} +# endif +#endif + +png_uint_32 /* PRIVATE */ +png_get_uint_31(png_structp png_ptr, png_bytep buf) +{ + png_uint_32 i = png_get_uint_32(buf); + if (i > PNG_UINT_31_MAX) + png_error(png_ptr, "PNG unsigned integer out of range.\n"); + return (i); +} +#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED +/* Grab an unsigned 32-bit integer from a buffer in big-endian format. */ +png_uint_32 /* PRIVATE */ +png_get_uint_32(png_bytep buf) +{ + png_uint_32 i = ((png_uint_32)(*buf) << 24) + + ((png_uint_32)(*(buf + 1)) << 16) + + ((png_uint_32)(*(buf + 2)) << 8) + + (png_uint_32)(*(buf + 3)); + + return (i); +} + +#if defined(PNG_READ_pCAL_SUPPORTED) || defined(PNG_READ_oFFs_SUPPORTED) +/* Grab a signed 32-bit integer from a buffer in big-endian format. The + * data is stored in the PNG file in two's complement format, and it is + * assumed that the machine format for signed integers is the same. */ +png_int_32 /* PRIVATE */ +png_get_int_32(png_bytep buf) +{ + png_int_32 i = ((png_int_32)(*buf) << 24) + + ((png_int_32)(*(buf + 1)) << 16) + + ((png_int_32)(*(buf + 2)) << 8) + + (png_int_32)(*(buf + 3)); + + return (i); +} +#endif /* PNG_READ_pCAL_SUPPORTED */ + +/* Grab an unsigned 16-bit integer from a buffer in big-endian format. */ +png_uint_16 /* PRIVATE */ +png_get_uint_16(png_bytep buf) +{ + png_uint_16 i = (png_uint_16)(((png_uint_16)(*buf) << 8) + + (png_uint_16)(*(buf + 1))); + + return (i); +} +#endif /* PNG_READ_BIG_ENDIAN_SUPPORTED */ + +/* Read data, and (optionally) run it through the CRC. */ +void /* PRIVATE */ +png_crc_read(png_structp png_ptr, png_bytep buf, png_size_t length) +{ + png_read_data(png_ptr, buf, length); + png_calculate_crc(png_ptr, buf, length); +} + +/* Optionally skip data and then check the CRC. Depending on whether we + are reading a ancillary or critical chunk, and how the program has set + things up, we may calculate the CRC on the data and print a message. + Returns '1' if there was a CRC error, '0' otherwise. */ +int /* PRIVATE */ +png_crc_finish(png_structp png_ptr, png_uint_32 skip) +{ + png_size_t i; + png_size_t istop = png_ptr->zbuf_size; + + for (i = (png_size_t)skip; i > istop; i -= istop) + { + png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + } + if (i) + { + png_crc_read(png_ptr, png_ptr->zbuf, i); + } + + if (png_crc_error(png_ptr)) + { + if (((png_ptr->chunk_name[0] & 0x20) && /* Ancillary */ + !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) || + (!(png_ptr->chunk_name[0] & 0x20) && /* Critical */ + (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE))) + { + png_chunk_warning(png_ptr, "CRC error"); + } + else + { + png_chunk_error(png_ptr, "CRC error"); + } + return (1); + } + + return (0); +} + +/* Compare the CRC stored in the PNG file with that calculated by libpng from + the data it has read thus far. */ +int /* PRIVATE */ +png_crc_error(png_structp png_ptr) +{ + png_byte crc_bytes[4]; + png_uint_32 crc; + int need_crc = 1; + + if (png_ptr->chunk_name[0] & 0x20) /* ancillary */ + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == + (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) + need_crc = 0; + } + else /* critical */ + { + if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) + need_crc = 0; + } + + png_read_data(png_ptr, crc_bytes, 4); + + if (need_crc) + { + crc = png_get_uint_32(crc_bytes); + return ((int)(crc != png_ptr->crc)); + } + else + return (0); +} + +#if defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) || \ + defined(PNG_READ_iCCP_SUPPORTED) +/* + * Decompress trailing data in a chunk. The assumption is that chunkdata + * points at an allocated area holding the contents of a chunk with a + * trailing compressed part. What we get back is an allocated area + * holding the original prefix part and an uncompressed version of the + * trailing part (the malloc area passed in is freed). + */ +png_charp /* PRIVATE */ +png_decompress_chunk(png_structp png_ptr, int comp_type, + png_charp chunkdata, png_size_t chunklength, + png_size_t prefix_size, png_size_t *newlength) +{ + static char msg[] = "Error decoding compressed text"; + png_charp text; + png_size_t text_size; + + if (comp_type == PNG_COMPRESSION_TYPE_BASE) + { + int ret = Z_OK; + png_ptr->zstream.next_in = (png_bytep)(chunkdata + prefix_size); + png_ptr->zstream.avail_in = (uInt)(chunklength - prefix_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + text_size = 0; + text = NULL; + + while (png_ptr->zstream.avail_in) + { + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret != Z_OK && ret != Z_STREAM_END) + { + if (png_ptr->zstream.msg != NULL) + png_warning(png_ptr, png_ptr->zstream.msg); + else + png_warning(png_ptr, msg); + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + + if (text == NULL) + { + text_size = prefix_size + png_sizeof(msg) + 1; + text = (png_charp)png_malloc_warn(png_ptr, text_size); + if (text == NULL) + { + png_free(png_ptr,chunkdata); + png_error(png_ptr,"Not enough memory to decompress chunk"); + } + png_memcpy(text, chunkdata, prefix_size); + } + + text[text_size - 1] = 0x00; + + /* Copy what we can of the error message into the text chunk */ + text_size = (png_size_t)(chunklength - (text - chunkdata) - 1); + text_size = png_sizeof(msg) > text_size ? text_size : + png_sizeof(msg); + png_memcpy(text + prefix_size, msg, text_size + 1); + break; + } + if (!png_ptr->zstream.avail_out || ret == Z_STREAM_END) + { + if (text == NULL) + { + text_size = prefix_size + + png_ptr->zbuf_size - png_ptr->zstream.avail_out; + text = (png_charp)png_malloc_warn(png_ptr, text_size + 1); + if (text == NULL) + { + png_free(png_ptr,chunkdata); + png_error(png_ptr,"Not enough memory to decompress chunk."); + } + png_memcpy(text + prefix_size, png_ptr->zbuf, + text_size - prefix_size); + png_memcpy(text, chunkdata, prefix_size); + *(text + text_size) = 0x00; + } + else + { + png_charp tmp; + + tmp = text; + text = (png_charp)png_malloc_warn(png_ptr, + (png_uint_32)(text_size + + png_ptr->zbuf_size - png_ptr->zstream.avail_out + 1)); + if (text == NULL) + { + png_free(png_ptr, tmp); + png_free(png_ptr, chunkdata); + png_error(png_ptr,"Not enough memory to decompress chunk.."); + } + png_memcpy(text, tmp, text_size); + png_free(png_ptr, tmp); + png_memcpy(text + text_size, png_ptr->zbuf, + (png_ptr->zbuf_size - png_ptr->zstream.avail_out)); + text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out; + *(text + text_size) = 0x00; + } + if (ret == Z_STREAM_END) + break; + else + { + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + } + } + if (ret != Z_STREAM_END) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char umsg[50]; + + if (ret == Z_BUF_ERROR) + sprintf(umsg,"Buffer error in compressed datastream in %s chunk", + png_ptr->chunk_name); + else if (ret == Z_DATA_ERROR) + sprintf(umsg,"Data error in compressed datastream in %s chunk", + png_ptr->chunk_name); + else + sprintf(umsg,"Incomplete compressed datastream in %s chunk", + png_ptr->chunk_name); + png_warning(png_ptr, umsg); +#else + png_warning(png_ptr, + "Incomplete compressed datastream in chunk other than IDAT"); +#endif + text_size=prefix_size; + if (text == NULL) + { + text = (png_charp)png_malloc_warn(png_ptr, text_size+1); + if (text == NULL) + { + png_free(png_ptr, chunkdata); + png_error(png_ptr,"Not enough memory for text."); + } + png_memcpy(text, chunkdata, prefix_size); + } + *(text + text_size) = 0x00; + } + + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + + png_free(png_ptr, chunkdata); + chunkdata = text; + *newlength=text_size; + } + else /* if (comp_type != PNG_COMPRESSION_TYPE_BASE) */ + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char umsg[50]; + + sprintf(umsg, "Unknown zTXt compression type %d", comp_type); + png_warning(png_ptr, umsg); +#else + png_warning(png_ptr, "Unknown zTXt compression type"); +#endif + + *(chunkdata + prefix_size) = 0x00; + *newlength=prefix_size; + } + + return chunkdata; +} +#endif + +/* read and check the IDHR chunk */ +void /* PRIVATE */ +png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[13]; + png_uint_32 width, height; + int bit_depth, color_type, compression_type, filter_type; + int interlace_type; + + png_debug(1, "in png_handle_IHDR\n"); + + if (png_ptr->mode & PNG_HAVE_IHDR) + png_error(png_ptr, "Out of place IHDR"); + + /* check the length */ + if (length != 13) + png_error(png_ptr, "Invalid IHDR chunk"); + + png_ptr->mode |= PNG_HAVE_IHDR; + + png_crc_read(png_ptr, buf, 13); + png_crc_finish(png_ptr, 0); + + width = png_get_uint_31(png_ptr, buf); + height = png_get_uint_31(png_ptr, buf + 4); + bit_depth = buf[8]; + color_type = buf[9]; + compression_type = buf[10]; + filter_type = buf[11]; + interlace_type = buf[12]; + + /* set internal variables */ + png_ptr->width = width; + png_ptr->height = height; + png_ptr->bit_depth = (png_byte)bit_depth; + png_ptr->interlaced = (png_byte)interlace_type; + png_ptr->color_type = (png_byte)color_type; +#if defined(PNG_MNG_FEATURES_SUPPORTED) + png_ptr->filter_type = (png_byte)filter_type; +#endif + png_ptr->compression_type = (png_byte)compression_type; + + /* find number of channels */ + switch (png_ptr->color_type) + { + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_PALETTE: + png_ptr->channels = 1; + break; + case PNG_COLOR_TYPE_RGB: + png_ptr->channels = 3; + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + png_ptr->channels = 2; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + png_ptr->channels = 4; + break; + } + + /* set up other useful info */ + png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth * + png_ptr->channels); + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth,png_ptr->width); + png_debug1(3,"bit_depth = %d\n", png_ptr->bit_depth); + png_debug1(3,"channels = %d\n", png_ptr->channels); + png_debug1(3,"rowbytes = %lu\n", png_ptr->rowbytes); + png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, + color_type, interlace_type, compression_type, filter_type); +} + +/* read and check the palette */ +void /* PRIVATE */ +png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_color palette[PNG_MAX_PALETTE_LENGTH]; + int num, i; +#ifndef PNG_NO_POINTER_INDEXING + png_colorp pal_ptr; +#endif + + png_debug(1, "in png_handle_PLTE\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before PLTE"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid PLTE after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + png_error(png_ptr, "Duplicate PLTE chunk"); + + png_ptr->mode |= PNG_HAVE_PLTE; + + if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) + { + png_warning(png_ptr, + "Ignoring PLTE chunk in grayscale PNG"); + png_crc_finish(png_ptr, length); + return; + } +#if !defined(PNG_READ_OPT_PLTE_SUPPORTED) + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + { + png_crc_finish(png_ptr, length); + return; + } +#endif + + if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3) + { + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + { + png_warning(png_ptr, "Invalid palette chunk"); + png_crc_finish(png_ptr, length); + return; + } + else + { + png_error(png_ptr, "Invalid palette chunk"); + } + } + + num = (int)length / 3; + +#ifndef PNG_NO_POINTER_INDEXING + for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++) + { + png_byte buf[3]; + + png_crc_read(png_ptr, buf, 3); + pal_ptr->red = buf[0]; + pal_ptr->green = buf[1]; + pal_ptr->blue = buf[2]; + } +#else + for (i = 0; i < num; i++) + { + png_byte buf[3]; + + png_crc_read(png_ptr, buf, 3); + /* don't depend upon png_color being any order */ + palette[i].red = buf[0]; + palette[i].green = buf[1]; + palette[i].blue = buf[2]; + } +#endif + + /* If we actually NEED the PLTE chunk (ie for a paletted image), we do + whatever the normal CRC configuration tells us. However, if we + have an RGB image, the PLTE can be considered ancillary, so + we will act as though it is. */ +#if !defined(PNG_READ_OPT_PLTE_SUPPORTED) + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) +#endif + { + png_crc_finish(png_ptr, 0); + } +#if !defined(PNG_READ_OPT_PLTE_SUPPORTED) + else if (png_crc_error(png_ptr)) /* Only if we have a CRC error */ + { + /* If we don't want to use the data from an ancillary chunk, + we have two options: an error abort, or a warning and we + ignore the data in this chunk (which should be OK, since + it's considered ancillary for a RGB or RGBA image). */ + if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE)) + { + if (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) + { + png_chunk_error(png_ptr, "CRC error"); + } + else + { + png_chunk_warning(png_ptr, "CRC error"); + return; + } + } + /* Otherwise, we (optionally) emit a warning and use the chunk. */ + else if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) + { + png_chunk_warning(png_ptr, "CRC error"); + } + } +#endif + + png_set_PLTE(png_ptr, info_ptr, palette, num); + +#if defined(PNG_READ_tRNS_SUPPORTED) + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + if (png_ptr->num_trans > (png_uint_16)num) + { + png_warning(png_ptr, "Truncating incorrect tRNS chunk length"); + png_ptr->num_trans = (png_uint_16)num; + } + if (info_ptr->num_trans > (png_uint_16)num) + { + png_warning(png_ptr, "Truncating incorrect info tRNS chunk length"); + info_ptr->num_trans = (png_uint_16)num; + } + } + } +#endif + +} + +void /* PRIVATE */ +png_handle_IEND(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_debug(1, "in png_handle_IEND\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR) || !(png_ptr->mode & PNG_HAVE_IDAT)) + { + png_error(png_ptr, "No image in file"); + } + + png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND); + + if (length != 0) + { + png_warning(png_ptr, "Incorrect IEND chunk length"); + } + png_crc_finish(png_ptr, length); + + if (&info_ptr == NULL) /* quiet compiler warnings about unused info_ptr */ + return; +} + +#if defined(PNG_READ_gAMA_SUPPORTED) +void /* PRIVATE */ +png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_fixed_point igamma; +#ifdef PNG_FLOATING_POINT_SUPPORTED + float file_gamma; +#endif + png_byte buf[4]; + + png_debug(1, "in png_handle_gAMA\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before gAMA"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid gAMA after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place gAMA chunk"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) +#if defined(PNG_READ_sRGB_SUPPORTED) + && !(info_ptr->valid & PNG_INFO_sRGB) +#endif + ) + { + png_warning(png_ptr, "Duplicate gAMA chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 4) + { + png_warning(png_ptr, "Incorrect gAMA chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 4); + if (png_crc_finish(png_ptr, 0)) + return; + + igamma = (png_fixed_point)png_get_uint_32(buf); + /* check for zero gamma */ + if (igamma == 0) + { + png_warning(png_ptr, + "Ignoring gAMA chunk with gamma=0"); + return; + } + +#if defined(PNG_READ_sRGB_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sRGB) + if (PNG_OUT_OF_RANGE(igamma, 45500L, 500)) + { + png_warning(png_ptr, + "Ignoring incorrect gAMA value when sRGB is also present"); +#ifndef PNG_NO_CONSOLE_IO + fprintf(stderr, "gamma = (%d/100000)\n", (int)igamma); +#endif + return; + } +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED + file_gamma = (float)igamma / (float)100000.0; +# ifdef PNG_READ_GAMMA_SUPPORTED + png_ptr->gamma = file_gamma; +# endif + png_set_gAMA(png_ptr, info_ptr, file_gamma); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_gAMA_fixed(png_ptr, info_ptr, igamma); +#endif +} +#endif + +#if defined(PNG_READ_sBIT_SUPPORTED) +void /* PRIVATE */ +png_handle_sBIT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_size_t truelen; + png_byte buf[4]; + + png_debug(1, "in png_handle_sBIT\n"); + + buf[0] = buf[1] = buf[2] = buf[3] = 0; + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sBIT"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sBIT after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + { + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place sBIT chunk"); + } + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT)) + { + png_warning(png_ptr, "Duplicate sBIT chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + truelen = 3; + else + truelen = (png_size_t)png_ptr->channels; + + if (length != truelen || length > 4) + { + png_warning(png_ptr, "Incorrect sBIT chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, truelen); + if (png_crc_finish(png_ptr, 0)) + return; + + if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + { + png_ptr->sig_bit.red = buf[0]; + png_ptr->sig_bit.green = buf[1]; + png_ptr->sig_bit.blue = buf[2]; + png_ptr->sig_bit.alpha = buf[3]; + } + else + { + png_ptr->sig_bit.gray = buf[0]; + png_ptr->sig_bit.red = buf[0]; + png_ptr->sig_bit.green = buf[0]; + png_ptr->sig_bit.blue = buf[0]; + png_ptr->sig_bit.alpha = buf[1]; + } + png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit)); +} +#endif + +#if defined(PNG_READ_cHRM_SUPPORTED) +void /* PRIVATE */ +png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[4]; +#ifdef PNG_FLOATING_POINT_SUPPORTED + float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; +#endif + png_fixed_point int_x_white, int_y_white, int_x_red, int_y_red, int_x_green, + int_y_green, int_x_blue, int_y_blue; + + png_uint_32 uint_x, uint_y; + + png_debug(1, "in png_handle_cHRM\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before cHRM"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid cHRM after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Missing PLTE before cHRM"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM) +#if defined(PNG_READ_sRGB_SUPPORTED) + && !(info_ptr->valid & PNG_INFO_sRGB) +#endif + ) + { + png_warning(png_ptr, "Duplicate cHRM chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 32) + { + png_warning(png_ptr, "Incorrect cHRM chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 4); + uint_x = png_get_uint_32(buf); + + png_crc_read(png_ptr, buf, 4); + uint_y = png_get_uint_32(buf); + + if (uint_x > 80000L || uint_y > 80000L || + uint_x + uint_y > 100000L) + { + png_warning(png_ptr, "Invalid cHRM white point"); + png_crc_finish(png_ptr, 24); + return; + } + int_x_white = (png_fixed_point)uint_x; + int_y_white = (png_fixed_point)uint_y; + + png_crc_read(png_ptr, buf, 4); + uint_x = png_get_uint_32(buf); + + png_crc_read(png_ptr, buf, 4); + uint_y = png_get_uint_32(buf); + + if (uint_x > 80000L || uint_y > 80000L || + uint_x + uint_y > 100000L) + { + png_warning(png_ptr, "Invalid cHRM red point"); + png_crc_finish(png_ptr, 16); + return; + } + int_x_red = (png_fixed_point)uint_x; + int_y_red = (png_fixed_point)uint_y; + + png_crc_read(png_ptr, buf, 4); + uint_x = png_get_uint_32(buf); + + png_crc_read(png_ptr, buf, 4); + uint_y = png_get_uint_32(buf); + + if (uint_x > 80000L || uint_y > 80000L || + uint_x + uint_y > 100000L) + { + png_warning(png_ptr, "Invalid cHRM green point"); + png_crc_finish(png_ptr, 8); + return; + } + int_x_green = (png_fixed_point)uint_x; + int_y_green = (png_fixed_point)uint_y; + + png_crc_read(png_ptr, buf, 4); + uint_x = png_get_uint_32(buf); + + png_crc_read(png_ptr, buf, 4); + uint_y = png_get_uint_32(buf); + + if (uint_x > 80000L || uint_y > 80000L || + uint_x + uint_y > 100000L) + { + png_warning(png_ptr, "Invalid cHRM blue point"); + png_crc_finish(png_ptr, 0); + return; + } + int_x_blue = (png_fixed_point)uint_x; + int_y_blue = (png_fixed_point)uint_y; + +#ifdef PNG_FLOATING_POINT_SUPPORTED + white_x = (float)int_x_white / (float)100000.0; + white_y = (float)int_y_white / (float)100000.0; + red_x = (float)int_x_red / (float)100000.0; + red_y = (float)int_y_red / (float)100000.0; + green_x = (float)int_x_green / (float)100000.0; + green_y = (float)int_y_green / (float)100000.0; + blue_x = (float)int_x_blue / (float)100000.0; + blue_y = (float)int_y_blue / (float)100000.0; +#endif + +#if defined(PNG_READ_sRGB_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sRGB) + { + if (PNG_OUT_OF_RANGE(int_x_white, 31270, 1000) || + PNG_OUT_OF_RANGE(int_y_white, 32900, 1000) || + PNG_OUT_OF_RANGE(int_x_red, 64000L, 1000) || + PNG_OUT_OF_RANGE(int_y_red, 33000, 1000) || + PNG_OUT_OF_RANGE(int_x_green, 30000, 1000) || + PNG_OUT_OF_RANGE(int_y_green, 60000L, 1000) || + PNG_OUT_OF_RANGE(int_x_blue, 15000, 1000) || + PNG_OUT_OF_RANGE(int_y_blue, 6000, 1000)) + { + + png_warning(png_ptr, + "Ignoring incorrect cHRM value when sRGB is also present"); +#ifndef PNG_NO_CONSOLE_IO +#ifdef PNG_FLOATING_POINT_SUPPORTED + fprintf(stderr,"wx=%f, wy=%f, rx=%f, ry=%f\n", + white_x, white_y, red_x, red_y); + fprintf(stderr,"gx=%f, gy=%f, bx=%f, by=%f\n", + green_x, green_y, blue_x, blue_y); +#else + fprintf(stderr,"wx=%ld, wy=%ld, rx=%ld, ry=%ld\n", + int_x_white, int_y_white, int_x_red, int_y_red); + fprintf(stderr,"gx=%ld, gy=%ld, bx=%ld, by=%ld\n", + int_x_green, int_y_green, int_x_blue, int_y_blue); +#endif +#endif /* PNG_NO_CONSOLE_IO */ + } + png_crc_finish(png_ptr, 0); + return; + } +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_set_cHRM(png_ptr, info_ptr, + white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_cHRM_fixed(png_ptr, info_ptr, + int_x_white, int_y_white, int_x_red, int_y_red, int_x_green, + int_y_green, int_x_blue, int_y_blue); +#endif + if (png_crc_finish(png_ptr, 0)) + return; +} +#endif + +#if defined(PNG_READ_sRGB_SUPPORTED) +void /* PRIVATE */ +png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + int intent; + png_byte buf[1]; + + png_debug(1, "in png_handle_sRGB\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sRGB"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sRGB after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place sRGB chunk"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)) + { + png_warning(png_ptr, "Duplicate sRGB chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 1) + { + png_warning(png_ptr, "Incorrect sRGB chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 1); + if (png_crc_finish(png_ptr, 0)) + return; + + intent = buf[0]; + /* check for bad intent */ + if (intent >= PNG_sRGB_INTENT_LAST) + { + png_warning(png_ptr, "Unknown sRGB intent"); + return; + } + +#if defined(PNG_READ_gAMA_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED) + if ((info_ptr->valid & PNG_INFO_gAMA)) + { + png_fixed_point igamma; +#ifdef PNG_FIXED_POINT_SUPPORTED + igamma=info_ptr->int_gamma; +#else +# ifdef PNG_FLOATING_POINT_SUPPORTED + igamma=(png_fixed_point)(info_ptr->gamma * 100000.); +# endif +#endif + if (PNG_OUT_OF_RANGE(igamma, 45500L, 500)) + { + png_warning(png_ptr, + "Ignoring incorrect gAMA value when sRGB is also present"); +#ifndef PNG_NO_CONSOLE_IO +# ifdef PNG_FIXED_POINT_SUPPORTED + fprintf(stderr,"incorrect gamma=(%d/100000)\n",(int)png_ptr->int_gamma); +# else +# ifdef PNG_FLOATING_POINT_SUPPORTED + fprintf(stderr,"incorrect gamma=%f\n",png_ptr->gamma); +# endif +# endif +#endif + } + } +#endif /* PNG_READ_gAMA_SUPPORTED */ + +#ifdef PNG_READ_cHRM_SUPPORTED +#ifdef PNG_FIXED_POINT_SUPPORTED + if (info_ptr->valid & PNG_INFO_cHRM) + if (PNG_OUT_OF_RANGE(info_ptr->int_x_white, 31270, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_white, 32900, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_x_red, 64000L, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_red, 33000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_x_green, 30000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_green, 60000L, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_x_blue, 15000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_blue, 6000, 1000)) + { + png_warning(png_ptr, + "Ignoring incorrect cHRM value when sRGB is also present"); + } +#endif /* PNG_FIXED_POINT_SUPPORTED */ +#endif /* PNG_READ_cHRM_SUPPORTED */ + + png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, intent); +} +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#if defined(PNG_READ_iCCP_SUPPORTED) +void /* PRIVATE */ +png_handle_iCCP(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +/* Note: this does not properly handle chunks that are > 64K under DOS */ +{ + png_charp chunkdata; + png_byte compression_type; + png_bytep pC; + png_charp profile; + png_uint_32 skip = 0; + png_uint_32 profile_size, profile_length; + png_size_t slength, prefix_length, data_length; + + png_debug(1, "in png_handle_iCCP\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before iCCP"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid iCCP after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place iCCP chunk"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP)) + { + png_warning(png_ptr, "Duplicate iCCP chunk"); + png_crc_finish(png_ptr, length); + return; + } + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "iCCP chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + chunkdata = (png_charp)png_malloc(png_ptr, length + 1); + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)chunkdata, slength); + + if (png_crc_finish(png_ptr, skip)) + { + png_free(png_ptr, chunkdata); + return; + } + + chunkdata[slength] = 0x00; + + for (profile = chunkdata; *profile; profile++) + /* empty loop to find end of name */ ; + + ++profile; + + /* there should be at least one zero (the compression type byte) + following the separator, and we should be on it */ + if ( profile >= chunkdata + slength) + { + png_free(png_ptr, chunkdata); + png_warning(png_ptr, "Malformed iCCP chunk"); + return; + } + + /* compression_type should always be zero */ + compression_type = *profile++; + if (compression_type) + { + png_warning(png_ptr, "Ignoring nonzero compression type in iCCP chunk"); + compression_type=0x00; /* Reset it to zero (libpng-1.0.6 through 1.0.8 + wrote nonzero) */ + } + + prefix_length = profile - chunkdata; + chunkdata = png_decompress_chunk(png_ptr, compression_type, chunkdata, + slength, prefix_length, &data_length); + + profile_length = data_length - prefix_length; + + if ( prefix_length > data_length || profile_length < 4) + { + png_free(png_ptr, chunkdata); + png_warning(png_ptr, "Profile size field missing from iCCP chunk"); + return; + } + + /* Check the profile_size recorded in the first 32 bits of the ICC profile */ + pC = (png_bytep)(chunkdata+prefix_length); + profile_size = ((*(pC ))<<24) | + ((*(pC+1))<<16) | + ((*(pC+2))<< 8) | + ((*(pC+3)) ); + + if(profile_size < profile_length) + profile_length = profile_size; + + if(profile_size > profile_length) + { + png_free(png_ptr, chunkdata); + png_warning(png_ptr, "Ignoring truncated iCCP profile.\n"); + return; + } + + png_set_iCCP(png_ptr, info_ptr, chunkdata, compression_type, + chunkdata + prefix_length, profile_length); + png_free(png_ptr, chunkdata); +} +#endif /* PNG_READ_iCCP_SUPPORTED */ + +#if defined(PNG_READ_sPLT_SUPPORTED) +void /* PRIVATE */ +png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +/* Note: this does not properly handle chunks that are > 64K under DOS */ +{ + png_bytep chunkdata; + png_bytep entry_start; + png_sPLT_t new_palette; +#ifdef PNG_NO_POINTER_INDEXING + png_sPLT_entryp pp; +#endif + int data_length, entry_size, i; + png_uint_32 skip = 0; + png_size_t slength; + + png_debug(1, "in png_handle_sPLT\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sPLT"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sPLT after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "sPLT chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + chunkdata = (png_bytep)png_malloc(png_ptr, length + 1); + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)chunkdata, slength); + + if (png_crc_finish(png_ptr, skip)) + { + png_free(png_ptr, chunkdata); + return; + } + + chunkdata[slength] = 0x00; + + for (entry_start = chunkdata; *entry_start; entry_start++) + /* empty loop to find end of name */ ; + ++entry_start; + + /* a sample depth should follow the separator, and we should be on it */ + if (entry_start > chunkdata + slength) + { + png_free(png_ptr, chunkdata); + png_warning(png_ptr, "malformed sPLT chunk"); + return; + } + + new_palette.depth = *entry_start++; + entry_size = (new_palette.depth == 8 ? 6 : 10); + data_length = (slength - (entry_start - chunkdata)); + + /* integrity-check the data length */ + if (data_length % entry_size) + { + png_free(png_ptr, chunkdata); + png_warning(png_ptr, "sPLT chunk has bad length"); + return; + } + + new_palette.nentries = (png_uint_32) (data_length / entry_size); + if ((png_uint_32) new_palette.nentries > (png_uint_32) (PNG_SIZE_MAX / + png_sizeof(png_sPLT_entry))) + { + png_warning(png_ptr, "sPLT chunk too long"); + return; + } + new_palette.entries = (png_sPLT_entryp)png_malloc_warn( + png_ptr, new_palette.nentries * png_sizeof(png_sPLT_entry)); + if (new_palette.entries == NULL) + { + png_warning(png_ptr, "sPLT chunk requires too much memory"); + return; + } + +#ifndef PNG_NO_POINTER_INDEXING + for (i = 0; i < new_palette.nentries; i++) + { + png_sPLT_entryp pp = new_palette.entries + i; + + if (new_palette.depth == 8) + { + pp->red = *entry_start++; + pp->green = *entry_start++; + pp->blue = *entry_start++; + pp->alpha = *entry_start++; + } + else + { + pp->red = png_get_uint_16(entry_start); entry_start += 2; + pp->green = png_get_uint_16(entry_start); entry_start += 2; + pp->blue = png_get_uint_16(entry_start); entry_start += 2; + pp->alpha = png_get_uint_16(entry_start); entry_start += 2; + } + pp->frequency = png_get_uint_16(entry_start); entry_start += 2; + } +#else + pp = new_palette.entries; + for (i = 0; i < new_palette.nentries; i++) + { + + if (new_palette.depth == 8) + { + pp[i].red = *entry_start++; + pp[i].green = *entry_start++; + pp[i].blue = *entry_start++; + pp[i].alpha = *entry_start++; + } + else + { + pp[i].red = png_get_uint_16(entry_start); entry_start += 2; + pp[i].green = png_get_uint_16(entry_start); entry_start += 2; + pp[i].blue = png_get_uint_16(entry_start); entry_start += 2; + pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2; + } + pp->frequency = png_get_uint_16(entry_start); entry_start += 2; + } +#endif + + /* discard all chunk data except the name and stash that */ + new_palette.name = (png_charp)chunkdata; + + png_set_sPLT(png_ptr, info_ptr, &new_palette, 1); + + png_free(png_ptr, chunkdata); + png_free(png_ptr, new_palette.entries); +} +#endif /* PNG_READ_sPLT_SUPPORTED */ + +#if defined(PNG_READ_tRNS_SUPPORTED) +void /* PRIVATE */ +png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte readbuf[PNG_MAX_PALETTE_LENGTH]; + + png_debug(1, "in png_handle_tRNS\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before tRNS"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid tRNS after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + png_warning(png_ptr, "Duplicate tRNS chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + png_byte buf[2]; + + if (length != 2) + { + png_warning(png_ptr, "Incorrect tRNS chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 2); + png_ptr->num_trans = 1; + png_ptr->trans_values.gray = png_get_uint_16(buf); + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + png_byte buf[6]; + + if (length != 6) + { + png_warning(png_ptr, "Incorrect tRNS chunk length"); + png_crc_finish(png_ptr, length); + return; + } + png_crc_read(png_ptr, buf, (png_size_t)length); + png_ptr->num_trans = 1; + png_ptr->trans_values.red = png_get_uint_16(buf); + png_ptr->trans_values.green = png_get_uint_16(buf + 2); + png_ptr->trans_values.blue = png_get_uint_16(buf + 4); + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (!(png_ptr->mode & PNG_HAVE_PLTE)) + { + /* Should be an error, but we can cope with it. */ + png_warning(png_ptr, "Missing PLTE before tRNS"); + } + if (length > (png_uint_32)png_ptr->num_palette || + length > PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, "Incorrect tRNS chunk length"); + png_crc_finish(png_ptr, length); + return; + } + if (length == 0) + { + png_warning(png_ptr, "Zero length tRNS chunk"); + png_crc_finish(png_ptr, length); + return; + } + png_crc_read(png_ptr, readbuf, (png_size_t)length); + png_ptr->num_trans = (png_uint_16)length; + } + else + { + png_warning(png_ptr, "tRNS chunk not allowed with alpha channel"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_crc_finish(png_ptr, 0)) + return; + + png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans, + &(png_ptr->trans_values)); +} +#endif + +#if defined(PNG_READ_bKGD_SUPPORTED) +void /* PRIVATE */ +png_handle_bKGD(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_size_t truelen; + png_byte buf[6]; + + png_debug(1, "in png_handle_bKGD\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before bKGD"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid bKGD after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + { + png_warning(png_ptr, "Missing PLTE before bKGD"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD)) + { + png_warning(png_ptr, "Duplicate bKGD chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + truelen = 1; + else if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + truelen = 6; + else + truelen = 2; + + if (length != truelen) + { + png_warning(png_ptr, "Incorrect bKGD chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, truelen); + if (png_crc_finish(png_ptr, 0)) + return; + + /* We convert the index value into RGB components so that we can allow + * arbitrary RGB values for background when we have transparency, and + * so it is easy to determine the RGB values of the background color + * from the info_ptr struct. */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_ptr->background.index = buf[0]; + if(info_ptr->num_palette) + { + if(buf[0] > info_ptr->num_palette) + { + png_warning(png_ptr, "Incorrect bKGD chunk index value"); + return; + } + png_ptr->background.red = + (png_uint_16)png_ptr->palette[buf[0]].red; + png_ptr->background.green = + (png_uint_16)png_ptr->palette[buf[0]].green; + png_ptr->background.blue = + (png_uint_16)png_ptr->palette[buf[0]].blue; + } + } + else if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) /* GRAY */ + { + png_ptr->background.red = + png_ptr->background.green = + png_ptr->background.blue = + png_ptr->background.gray = png_get_uint_16(buf); + } + else + { + png_ptr->background.red = png_get_uint_16(buf); + png_ptr->background.green = png_get_uint_16(buf + 2); + png_ptr->background.blue = png_get_uint_16(buf + 4); + } + + png_set_bKGD(png_ptr, info_ptr, &(png_ptr->background)); +} +#endif + +#if defined(PNG_READ_hIST_SUPPORTED) +void /* PRIVATE */ +png_handle_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + unsigned int num, i; + png_uint_16 readbuf[PNG_MAX_PALETTE_LENGTH]; + + png_debug(1, "in png_handle_hIST\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before hIST"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid hIST after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (!(png_ptr->mode & PNG_HAVE_PLTE)) + { + png_warning(png_ptr, "Missing PLTE before hIST"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST)) + { + png_warning(png_ptr, "Duplicate hIST chunk"); + png_crc_finish(png_ptr, length); + return; + } + + num = length / 2 ; + if (num != (unsigned int) png_ptr->num_palette || num > + (unsigned int) PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, "Incorrect hIST chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + for (i = 0; i < num; i++) + { + png_byte buf[2]; + + png_crc_read(png_ptr, buf, 2); + readbuf[i] = png_get_uint_16(buf); + } + + if (png_crc_finish(png_ptr, 0)) + return; + + png_set_hIST(png_ptr, info_ptr, readbuf); +} +#endif + +#if defined(PNG_READ_pHYs_SUPPORTED) +void /* PRIVATE */ +png_handle_pHYs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[9]; + png_uint_32 res_x, res_y; + int unit_type; + + png_debug(1, "in png_handle_pHYs\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before pHYs"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid pHYs after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + { + png_warning(png_ptr, "Duplicate pHYs chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 9) + { + png_warning(png_ptr, "Incorrect pHYs chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 9); + if (png_crc_finish(png_ptr, 0)) + return; + + res_x = png_get_uint_32(buf); + res_y = png_get_uint_32(buf + 4); + unit_type = buf[8]; + png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type); +} +#endif + +#if defined(PNG_READ_oFFs_SUPPORTED) +void /* PRIVATE */ +png_handle_oFFs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[9]; + png_int_32 offset_x, offset_y; + int unit_type; + + png_debug(1, "in png_handle_oFFs\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before oFFs"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid oFFs after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) + { + png_warning(png_ptr, "Duplicate oFFs chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 9) + { + png_warning(png_ptr, "Incorrect oFFs chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 9); + if (png_crc_finish(png_ptr, 0)) + return; + + offset_x = png_get_int_32(buf); + offset_y = png_get_int_32(buf + 4); + unit_type = buf[8]; + png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type); +} +#endif + +#if defined(PNG_READ_pCAL_SUPPORTED) +/* read the pCAL chunk (described in the PNG Extensions document) */ +void /* PRIVATE */ +png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_charp purpose; + png_int_32 X0, X1; + png_byte type, nparams; + png_charp buf, units, endptr; + png_charpp params; + png_size_t slength; + int i; + + png_debug(1, "in png_handle_pCAL\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before pCAL"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid pCAL after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL)) + { + png_warning(png_ptr, "Duplicate pCAL chunk"); + png_crc_finish(png_ptr, length); + return; + } + + png_debug1(2, "Allocating and reading pCAL chunk data (%lu bytes)\n", + length + 1); + purpose = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (purpose == NULL) + { + png_warning(png_ptr, "No memory for pCAL purpose."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)purpose, slength); + + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, purpose); + return; + } + + purpose[slength] = 0x00; /* null terminate the last string */ + + png_debug(3, "Finding end of pCAL purpose string\n"); + for (buf = purpose; *buf; buf++) + /* empty loop */ ; + + endptr = purpose + slength; + + /* We need to have at least 12 bytes after the purpose string + in order to get the parameter information. */ + if (endptr <= buf + 12) + { + png_warning(png_ptr, "Invalid pCAL data"); + png_free(png_ptr, purpose); + return; + } + + png_debug(3, "Reading pCAL X0, X1, type, nparams, and units\n"); + X0 = png_get_int_32((png_bytep)buf+1); + X1 = png_get_int_32((png_bytep)buf+5); + type = buf[9]; + nparams = buf[10]; + units = buf + 11; + + png_debug(3, "Checking pCAL equation type and number of parameters\n"); + /* Check that we have the right number of parameters for known + equation types. */ + if ((type == PNG_EQUATION_LINEAR && nparams != 2) || + (type == PNG_EQUATION_BASE_E && nparams != 3) || + (type == PNG_EQUATION_ARBITRARY && nparams != 3) || + (type == PNG_EQUATION_HYPERBOLIC && nparams != 4)) + { + png_warning(png_ptr, "Invalid pCAL parameters for equation type"); + png_free(png_ptr, purpose); + return; + } + else if (type >= PNG_EQUATION_LAST) + { + png_warning(png_ptr, "Unrecognized equation type for pCAL chunk"); + } + + for (buf = units; *buf; buf++) + /* Empty loop to move past the units string. */ ; + + png_debug(3, "Allocating pCAL parameters array\n"); + params = (png_charpp)png_malloc_warn(png_ptr, (png_uint_32)(nparams + *png_sizeof(png_charp))) ; + if (params == NULL) + { + png_free(png_ptr, purpose); + png_warning(png_ptr, "No memory for pCAL params."); + return; + } + + /* Get pointers to the start of each parameter string. */ + for (i = 0; i < (int)nparams; i++) + { + buf++; /* Skip the null string terminator from previous parameter. */ + + png_debug1(3, "Reading pCAL parameter %d\n", i); + for (params[i] = buf; *buf != 0x00 && buf <= endptr; buf++) + /* Empty loop to move past each parameter string */ ; + + /* Make sure we haven't run out of data yet */ + if (buf > endptr) + { + png_warning(png_ptr, "Invalid pCAL data"); + png_free(png_ptr, purpose); + png_free(png_ptr, params); + return; + } + } + + png_set_pCAL(png_ptr, info_ptr, purpose, X0, X1, type, nparams, + units, params); + + png_free(png_ptr, purpose); + png_free(png_ptr, params); +} +#endif + +#if defined(PNG_READ_sCAL_SUPPORTED) +/* read the sCAL chunk */ +void /* PRIVATE */ +png_handle_sCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_charp buffer, ep; +#ifdef PNG_FLOATING_POINT_SUPPORTED + double width, height; + png_charp vp; +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_charp swidth, sheight; +#endif +#endif + png_size_t slength; + + png_debug(1, "in png_handle_sCAL\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sCAL"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sCAL after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL)) + { + png_warning(png_ptr, "Duplicate sCAL chunk"); + png_crc_finish(png_ptr, length); + return; + } + + png_debug1(2, "Allocating and reading sCAL chunk data (%lu bytes)\n", + length + 1); + buffer = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (buffer == NULL) + { + png_warning(png_ptr, "Out of memory while processing sCAL chunk"); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)buffer, slength); + + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, buffer); + return; + } + + buffer[slength] = 0x00; /* null terminate the last string */ + + ep = buffer + 1; /* skip unit byte */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED + width = strtod(ep, &vp); + if (*vp) + { + png_warning(png_ptr, "malformed width string in sCAL chunk"); + return; + } +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + swidth = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1); + if (swidth == NULL) + { + png_warning(png_ptr, "Out of memory while processing sCAL chunk width"); + return; + } + png_memcpy(swidth, ep, (png_size_t)png_strlen(ep)); +#endif +#endif + + for (ep = buffer; *ep; ep++) + /* empty loop */ ; + ep++; + +#ifdef PNG_FLOATING_POINT_SUPPORTED + height = strtod(ep, &vp); + if (*vp) + { + png_warning(png_ptr, "malformed height string in sCAL chunk"); + return; + } +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + sheight = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1); + if (swidth == NULL) + { + png_warning(png_ptr, "Out of memory while processing sCAL chunk height"); + return; + } + png_memcpy(sheight, ep, (png_size_t)png_strlen(ep)); +#endif +#endif + + if (buffer + slength < ep +#ifdef PNG_FLOATING_POINT_SUPPORTED + || width <= 0. || height <= 0. +#endif + ) + { + png_warning(png_ptr, "Invalid sCAL data"); + png_free(png_ptr, buffer); +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, swidth); + png_free(png_ptr, sheight); +#endif + return; + } + + +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_set_sCAL(png_ptr, info_ptr, buffer[0], width, height); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_sCAL_s(png_ptr, info_ptr, buffer[0], swidth, sheight); +#endif +#endif + + png_free(png_ptr, buffer); +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, swidth); + png_free(png_ptr, sheight); +#endif +} +#endif + +#if defined(PNG_READ_tIME_SUPPORTED) +void /* PRIVATE */ +png_handle_tIME(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[7]; + png_time mod_time; + + png_debug(1, "in png_handle_tIME\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Out of place tIME chunk"); + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME)) + { + png_warning(png_ptr, "Duplicate tIME chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + + if (length != 7) + { + png_warning(png_ptr, "Incorrect tIME chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 7); + if (png_crc_finish(png_ptr, 0)) + return; + + mod_time.second = buf[6]; + mod_time.minute = buf[5]; + mod_time.hour = buf[4]; + mod_time.day = buf[3]; + mod_time.month = buf[2]; + mod_time.year = png_get_uint_16(buf); + + png_set_tIME(png_ptr, info_ptr, &mod_time); +} +#endif + +#if defined(PNG_READ_tEXt_SUPPORTED) +/* Note: this does not properly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_textp text_ptr; + png_charp key; + png_charp text; + png_uint_32 skip = 0; + png_size_t slength; + int ret; + + png_debug(1, "in png_handle_tEXt\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before tEXt"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "tEXt chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + key = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (key == NULL) + { + png_warning(png_ptr, "No memory to process text chunk."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)key, slength); + + if (png_crc_finish(png_ptr, skip)) + { + png_free(png_ptr, key); + return; + } + + key[slength] = 0x00; + + for (text = key; *text; text++) + /* empty loop to find end of key */ ; + + if (text != key + slength) + text++; + + text_ptr = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)png_sizeof(png_text)); + if (text_ptr == NULL) + { + png_warning(png_ptr, "Not enough memory to process text chunk."); + png_free(png_ptr, key); + return; + } + text_ptr->compression = PNG_TEXT_COMPRESSION_NONE; + text_ptr->key = key; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; + text_ptr->itxt_length = 0; +#endif + text_ptr->text = text; + text_ptr->text_length = png_strlen(text); + + ret=png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, key); + png_free(png_ptr, text_ptr); + if (ret) + png_warning(png_ptr, "Insufficient memory to process text chunk."); +} +#endif + +#if defined(PNG_READ_zTXt_SUPPORTED) +/* note: this does not correctly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_textp text_ptr; + png_charp chunkdata; + png_charp text; + int comp_type; + int ret; + png_size_t slength, prefix_len, data_len; + + png_debug(1, "in png_handle_zTXt\n"); + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before zTXt"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + /* We will no doubt have problems with chunks even half this size, but + there is no hard and fast rule to tell us where to stop. */ + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr,"zTXt chunk too large to fit in memory"); + png_crc_finish(png_ptr, length); + return; + } +#endif + + chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (chunkdata == NULL) + { + png_warning(png_ptr,"Out of memory processing zTXt chunk."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)chunkdata, slength); + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, chunkdata); + return; + } + + chunkdata[slength] = 0x00; + + for (text = chunkdata; *text; text++) + /* empty loop */ ; + + /* zTXt must have some text after the chunkdataword */ + if (text == chunkdata + slength) + { + comp_type = PNG_TEXT_COMPRESSION_NONE; + png_warning(png_ptr, "Zero length zTXt chunk"); + } + else + { + comp_type = *(++text); + if (comp_type != PNG_TEXT_COMPRESSION_zTXt) + { + png_warning(png_ptr, "Unknown compression type in zTXt chunk"); + comp_type = PNG_TEXT_COMPRESSION_zTXt; + } + text++; /* skip the compression_method byte */ + } + prefix_len = text - chunkdata; + + chunkdata = (png_charp)png_decompress_chunk(png_ptr, comp_type, chunkdata, + (png_size_t)length, prefix_len, &data_len); + + text_ptr = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)png_sizeof(png_text)); + if (text_ptr == NULL) + { + png_warning(png_ptr,"Not enough memory to process zTXt chunk."); + png_free(png_ptr, chunkdata); + return; + } + text_ptr->compression = comp_type; + text_ptr->key = chunkdata; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; + text_ptr->itxt_length = 0; +#endif + text_ptr->text = chunkdata + prefix_len; + text_ptr->text_length = data_len; + + ret=png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, text_ptr); + png_free(png_ptr, chunkdata); + if (ret) + png_error(png_ptr, "Insufficient memory to store zTXt chunk."); +} +#endif + +#if defined(PNG_READ_iTXt_SUPPORTED) +/* note: this does not correctly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_textp text_ptr; + png_charp chunkdata; + png_charp key, lang, text, lang_key; + int comp_flag; + int comp_type = 0; + int ret; + png_size_t slength, prefix_len, data_len; + + png_debug(1, "in png_handle_iTXt\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before iTXt"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + /* We will no doubt have problems with chunks even half this size, but + there is no hard and fast rule to tell us where to stop. */ + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr,"iTXt chunk too large to fit in memory"); + png_crc_finish(png_ptr, length); + return; + } +#endif + + chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (chunkdata == NULL) + { + png_warning(png_ptr, "No memory to process iTXt chunk."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)chunkdata, slength); + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, chunkdata); + return; + } + + chunkdata[slength] = 0x00; + + for (lang = chunkdata; *lang; lang++) + /* empty loop */ ; + lang++; /* skip NUL separator */ + + /* iTXt must have a language tag (possibly empty), two compression bytes, + translated keyword (possibly empty), and possibly some text after the + keyword */ + + if (lang >= chunkdata + slength) + { + comp_flag = PNG_TEXT_COMPRESSION_NONE; + png_warning(png_ptr, "Zero length iTXt chunk"); + } + else + { + comp_flag = *lang++; + comp_type = *lang++; + } + + for (lang_key = lang; *lang_key; lang_key++) + /* empty loop */ ; + lang_key++; /* skip NUL separator */ + + for (text = lang_key; *text; text++) + /* empty loop */ ; + text++; /* skip NUL separator */ + + prefix_len = text - chunkdata; + + key=chunkdata; + if (comp_flag) + chunkdata = png_decompress_chunk(png_ptr, comp_type, chunkdata, + (size_t)length, prefix_len, &data_len); + else + data_len=png_strlen(chunkdata + prefix_len); + text_ptr = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)png_sizeof(png_text)); + if (text_ptr == NULL) + { + png_warning(png_ptr,"Not enough memory to process iTXt chunk."); + png_free(png_ptr, chunkdata); + return; + } + text_ptr->compression = (int)comp_flag + 1; + text_ptr->lang_key = chunkdata+(lang_key-key); + text_ptr->lang = chunkdata+(lang-key); + text_ptr->itxt_length = data_len; + text_ptr->text_length = 0; + text_ptr->key = chunkdata; + text_ptr->text = chunkdata + prefix_len; + + ret=png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, text_ptr); + png_free(png_ptr, chunkdata); + if (ret) + png_error(png_ptr, "Insufficient memory to store iTXt chunk."); +} +#endif + +/* This function is called when we haven't found a handler for a + chunk. If there isn't a problem with the chunk itself (ie bad + chunk name, CRC, or a critical chunk), the chunk is silently ignored + -- unless the PNG_FLAG_UNKNOWN_CHUNKS_SUPPORTED flag is on in which + case it will be saved away to be written out later. */ +void /* PRIVATE */ +png_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_uint_32 skip = 0; + + png_debug(1, "in png_handle_unknown\n"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IDAT; +#endif + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) /* not an IDAT */ + png_ptr->mode |= PNG_AFTER_IDAT; + } + + png_check_chunk_name(png_ptr, png_ptr->chunk_name); + + if (!(png_ptr->chunk_name[0] & 0x20)) + { +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) + && png_ptr->read_user_chunk_fn == NULL +#endif + ) +#endif + png_chunk_error(png_ptr, "unknown critical chunk"); + } + +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + if (png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS) + { + png_unknown_chunk chunk; + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "unknown chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + png_strcpy((png_charp)chunk.name, (png_charp)png_ptr->chunk_name); + chunk.data = (png_bytep)png_malloc(png_ptr, length); + chunk.size = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)chunk.data, length); +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) + if(png_ptr->read_user_chunk_fn != NULL) + { + /* callback to user unknown chunk handler */ + if ((*(png_ptr->read_user_chunk_fn)) (png_ptr, &chunk) <= 0) + { + if (!(png_ptr->chunk_name[0] & 0x20)) + if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS) + { + png_free(png_ptr, chunk.data); + png_chunk_error(png_ptr, "unknown critical chunk"); + } + png_set_unknown_chunks(png_ptr, info_ptr, &chunk, 1); + } + } + else +#endif + png_set_unknown_chunks(png_ptr, info_ptr, &chunk, 1); + png_free(png_ptr, chunk.data); + } + else +#endif + skip = length; + + png_crc_finish(png_ptr, skip); + +#if !defined(PNG_READ_USER_CHUNKS_SUPPORTED) + if (&info_ptr == NULL) /* quiet compiler warnings about unused info_ptr */ + return; +#endif +} + +/* This function is called to verify that a chunk name is valid. + This function can't have the "critical chunk check" incorporated + into it, since in the future we will need to be able to call user + functions to handle unknown critical chunks after we check that + the chunk name itself is valid. */ + +#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) + +void /* PRIVATE */ +png_check_chunk_name(png_structp png_ptr, png_bytep chunk_name) +{ + png_debug(1, "in png_check_chunk_name\n"); + if (isnonalpha(chunk_name[0]) || isnonalpha(chunk_name[1]) || + isnonalpha(chunk_name[2]) || isnonalpha(chunk_name[3])) + { + png_chunk_error(png_ptr, "invalid chunk type"); + } +} + +/* Combines the row recently read in with the existing pixels in the + row. This routine takes care of alpha and transparency if requested. + This routine also handles the two methods of progressive display + of interlaced images, depending on the mask value. + The mask value describes which pixels are to be combined with + the row. The pattern always repeats every 8 pixels, so just 8 + bits are needed. A one indicates the pixel is to be combined, + a zero indicates the pixel is to be skipped. This is in addition + to any alpha or transparency value associated with the pixel. If + you want all pixels to be combined, pass 0xff (255) in mask. */ +#ifndef PNG_HAVE_ASSEMBLER_COMBINE_ROW +void /* PRIVATE */ +png_combine_row(png_structp png_ptr, png_bytep row, int mask) +{ + png_debug(1,"in png_combine_row\n"); + if (mask == 0xff) + { + png_memcpy(row, png_ptr->row_buf + 1, + PNG_ROWBYTES(png_ptr->row_info.pixel_depth, png_ptr->width)); + } + else + { + switch (png_ptr->row_info.pixel_depth) + { + case 1: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + int s_inc, s_start, s_end; + int m = 0x80; + int shift; + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 7; + s_inc = 1; + } + else +#endif + { + s_start = 7; + s_end = 0; + s_inc = -1; + } + + shift = s_start; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + int value; + + value = (*sp >> shift) & 0x01; + *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + case 2: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + int s_start, s_end, s_inc; + int m = 0x80; + int shift; + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + int value; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 6; + s_inc = 2; + } + else +#endif + { + s_start = 6; + s_end = 0; + s_inc = -2; + } + + shift = s_start; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + value = (*sp >> shift) & 0x03; + *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + case 4: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + int s_start, s_end, s_inc; + int m = 0x80; + int shift; + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + int value; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 4; + s_inc = 4; + } + else +#endif + { + s_start = 4; + s_end = 0; + s_inc = -4; + } + shift = s_start; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + value = (*sp >> shift) & 0xf; + *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + default: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + png_size_t pixel_bytes = (png_ptr->row_info.pixel_depth >> 3); + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + png_byte m = 0x80; + + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + png_memcpy(dp, sp, pixel_bytes); + } + + sp += pixel_bytes; + dp += pixel_bytes; + + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + } + } +} +#endif /* !PNG_HAVE_ASSEMBLER_COMBINE_ROW */ + +#ifdef PNG_READ_INTERLACING_SUPPORTED +#ifndef PNG_HAVE_ASSEMBLER_READ_INTERLACE /* else in pngvcrd.c, pnggccrd.c */ +/* OLD pre-1.0.9 interface: +void png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass, + png_uint_32 transformations) + */ +void /* PRIVATE */ +png_do_read_interlace(png_structp png_ptr) +{ + png_row_infop row_info = &(png_ptr->row_info); + png_bytep row = png_ptr->row_buf + 1; + int pass = png_ptr->pass; + png_uint_32 transformations = png_ptr->transformations; +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + /* offset to next interlace block */ + const int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; +#endif + + png_debug(1,"in png_do_read_interlace (stock C version)\n"); + if (row != NULL && row_info != NULL) + { + png_uint_32 final_width; + + final_width = row_info->width * png_pass_inc[pass]; + + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 3); + png_bytep dp = row + (png_size_t)((final_width - 1) >> 3); + int sshift, dshift; + int s_start, s_end, s_inc; + int jstop = png_pass_inc[pass]; + png_byte v; + png_uint_32 i; + int j; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (int)((row_info->width + 7) & 0x07); + dshift = (int)((final_width + 7) & 0x07); + s_start = 7; + s_end = 0; + s_inc = -1; + } + else +#endif + { + sshift = 7 - (int)((row_info->width + 7) & 0x07); + dshift = 7 - (int)((final_width + 7) & 0x07); + s_start = 0; + s_end = 7; + s_inc = 1; + } + + for (i = 0; i < row_info->width; i++) + { + v = (png_byte)((*sp >> sshift) & 0x01); + for (j = 0; j < jstop; j++) + { + *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + case 2: + { + png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2); + png_bytep dp = row + (png_uint_32)((final_width - 1) >> 2); + int sshift, dshift; + int s_start, s_end, s_inc; + int jstop = png_pass_inc[pass]; + png_uint_32 i; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (int)(((row_info->width + 3) & 0x03) << 1); + dshift = (int)(((final_width + 3) & 0x03) << 1); + s_start = 6; + s_end = 0; + s_inc = -2; + } + else +#endif + { + sshift = (int)((3 - ((row_info->width + 3) & 0x03)) << 1); + dshift = (int)((3 - ((final_width + 3) & 0x03)) << 1); + s_start = 0; + s_end = 6; + s_inc = 2; + } + + for (i = 0; i < row_info->width; i++) + { + png_byte v; + int j; + + v = (png_byte)((*sp >> sshift) & 0x03); + for (j = 0; j < jstop; j++) + { + *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + case 4: + { + png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1); + png_bytep dp = row + (png_size_t)((final_width - 1) >> 1); + int sshift, dshift; + int s_start, s_end, s_inc; + png_uint_32 i; + int jstop = png_pass_inc[pass]; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (int)(((row_info->width + 1) & 0x01) << 2); + dshift = (int)(((final_width + 1) & 0x01) << 2); + s_start = 4; + s_end = 0; + s_inc = -4; + } + else +#endif + { + sshift = (int)((1 - ((row_info->width + 1) & 0x01)) << 2); + dshift = (int)((1 - ((final_width + 1) & 0x01)) << 2); + s_start = 0; + s_end = 4; + s_inc = 4; + } + + for (i = 0; i < row_info->width; i++) + { + png_byte v = (png_byte)((*sp >> sshift) & 0xf); + int j; + + for (j = 0; j < jstop; j++) + { + *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + default: + { + png_size_t pixel_bytes = (row_info->pixel_depth >> 3); + png_bytep sp = row + (png_size_t)(row_info->width - 1) * pixel_bytes; + png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes; + + int jstop = png_pass_inc[pass]; + png_uint_32 i; + + for (i = 0; i < row_info->width; i++) + { + png_byte v[8]; + int j; + + png_memcpy(v, sp, pixel_bytes); + for (j = 0; j < jstop; j++) + { + png_memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + sp -= pixel_bytes; + } + break; + } + } + row_info->width = final_width; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,final_width); + } +#if !defined(PNG_READ_PACKSWAP_SUPPORTED) + if (&transformations == NULL) /* silence compiler warning */ + return; +#endif +} +#endif /* !PNG_HAVE_ASSEMBLER_READ_INTERLACE */ +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + +#ifndef PNG_HAVE_ASSEMBLER_READ_FILTER_ROW +void /* PRIVATE */ +png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep row, + png_bytep prev_row, int filter) +{ + png_debug(1, "in png_read_filter_row\n"); + png_debug2(2,"row = %lu, filter = %d\n", png_ptr->row_number, filter); + switch (filter) + { + case PNG_FILTER_VALUE_NONE: + break; + case PNG_FILTER_VALUE_SUB: + { + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_bytep rp = row + bpp; + png_bytep lp = row; + + for (i = bpp; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff); + rp++; + } + break; + } + case PNG_FILTER_VALUE_UP: + { + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_bytep rp = row; + png_bytep pp = prev_row; + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + break; + } + case PNG_FILTER_VALUE_AVG: + { + png_uint_32 i; + png_bytep rp = row; + png_bytep pp = prev_row; + png_bytep lp = row; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_uint_32 istop = row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + + ((int)(*pp++) / 2 )) & 0xff); + rp++; + } + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *lp++) / 2 ) & 0xff); + rp++; + } + break; + } + case PNG_FILTER_VALUE_PAETH: + { + png_uint_32 i; + png_bytep rp = row; + png_bytep pp = prev_row; + png_bytep lp = row; + png_bytep cp = prev_row; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_uint_32 istop=row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + + for (i = 0; i < istop; i++) /* use leftover rp,pp */ + { + int a, b, c, pa, pb, pc, p; + + a = *lp++; + b = *pp++; + c = *cp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + /* + if (pa <= pb && pa <= pc) + p = a; + else if (pb <= pc) + p = b; + else + p = c; + */ + + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; + + *rp = (png_byte)(((int)(*rp) + p) & 0xff); + rp++; + } + break; + } + default: + png_warning(png_ptr, "Ignoring bad adaptive filter type"); + *row=0; + break; + } +} +#endif /* !PNG_HAVE_ASSEMBLER_READ_FILTER_ROW */ + +void /* PRIVATE */ +png_read_finish_row(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + const int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + const int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + const int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + const int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + png_debug(1, "in png_read_finish_row\n"); + png_ptr->row_number++; + if (png_ptr->row_number < png_ptr->num_rows) + return; + + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + png_memset_check(png_ptr, png_ptr->prev_row, 0, png_ptr->rowbytes + 1); + do + { + png_ptr->pass++; + if (png_ptr->pass >= 7) + break; + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + png_ptr->irowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1; + + if (!(png_ptr->transformations & PNG_INTERLACE)) + { + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + if (!(png_ptr->num_rows)) + continue; + } + else /* if (png_ptr->transformations & PNG_INTERLACE) */ + break; + } while (png_ptr->iwidth == 0); + + if (png_ptr->pass < 7) + return; + } + + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IDAT; +#endif + char extra; + int ret; + + png_ptr->zstream.next_out = (Byte *)&extra; + png_ptr->zstream.avail_out = (uInt)1; + for(;;) + { + if (!(png_ptr->zstream.avail_in)) + { + while (!png_ptr->idat_size) + { + png_byte chunk_length[4]; + + png_crc_finish(png_ptr, 0); + + png_read_data(png_ptr, chunk_length, 4); + png_ptr->idat_size = png_get_uint_31(png_ptr, chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + if (png_memcmp(png_ptr->chunk_name, (png_bytep)png_IDAT, 4)) + png_error(png_ptr, "Not enough image data"); + + } + png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_in = png_ptr->zbuf; + if (png_ptr->zbuf_size > png_ptr->idat_size) + png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size; + png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zstream.avail_in); + png_ptr->idat_size -= png_ptr->zstream.avail_in; + } + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret == Z_STREAM_END) + { + if (!(png_ptr->zstream.avail_out) || png_ptr->zstream.avail_in || + png_ptr->idat_size) + png_warning(png_ptr, "Extra compressed data"); + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + if (ret != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg : + "Decompression Error"); + + if (!(png_ptr->zstream.avail_out)) + { + png_warning(png_ptr, "Extra compressed data."); + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + + } + png_ptr->zstream.avail_out = 0; + } + + if (png_ptr->idat_size || png_ptr->zstream.avail_in) + png_warning(png_ptr, "Extra compression data"); + + inflateReset(&png_ptr->zstream); + + png_ptr->mode |= PNG_AFTER_IDAT; +} + +void /* PRIVATE */ +png_read_start_row(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + const int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + const int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + const int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + const int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + int max_pixel_depth; + png_uint_32 row_bytes; + + png_debug(1, "in png_read_start_row\n"); + png_ptr->zstream.avail_in = 0; + png_init_read_transformations(png_ptr); + if (png_ptr->interlaced) + { + if (!(png_ptr->transformations & PNG_INTERLACE)) + png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - + png_pass_ystart[0]) / png_pass_yinc[0]; + else + png_ptr->num_rows = png_ptr->height; + + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + row_bytes = PNG_ROWBYTES(png_ptr->pixel_depth,png_ptr->iwidth) + 1; + + png_ptr->irowbytes = (png_size_t)row_bytes; + if((png_uint_32)png_ptr->irowbytes != row_bytes) + png_error(png_ptr, "Rowbytes overflow in png_read_start_row"); + } + else + { + png_ptr->num_rows = png_ptr->height; + png_ptr->iwidth = png_ptr->width; + png_ptr->irowbytes = png_ptr->rowbytes + 1; + } + max_pixel_depth = png_ptr->pixel_depth; + +#if defined(PNG_READ_PACK_SUPPORTED) + if ((png_ptr->transformations & PNG_PACK) && png_ptr->bit_depth < 8) + max_pixel_depth = 8; +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) + if (png_ptr->transformations & PNG_EXPAND) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (png_ptr->num_trans) + max_pixel_depth = 32; + else + max_pixel_depth = 24; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + if (max_pixel_depth < 8) + max_pixel_depth = 8; + if (png_ptr->num_trans) + max_pixel_depth *= 2; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + if (png_ptr->num_trans) + { + max_pixel_depth *= 4; + max_pixel_depth /= 3; + } + } + } +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & (PNG_FILLER)) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + max_pixel_depth = 32; + else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + if (max_pixel_depth <= 8) + max_pixel_depth = 16; + else + max_pixel_depth = 32; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + if (max_pixel_depth <= 32) + max_pixel_depth = 32; + else + max_pixel_depth = 64; + } + } +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + if (png_ptr->transformations & PNG_GRAY_TO_RGB) + { + if ( +#if defined(PNG_READ_EXPAND_SUPPORTED) + (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND)) || +#endif +#if defined(PNG_READ_FILLER_SUPPORTED) + (png_ptr->transformations & (PNG_FILLER)) || +#endif + png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (max_pixel_depth <= 16) + max_pixel_depth = 32; + else + max_pixel_depth = 64; + } + else + { + if (max_pixel_depth <= 8) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + max_pixel_depth = 32; + else + max_pixel_depth = 24; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + max_pixel_depth = 64; + else + max_pixel_depth = 48; + } + } +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) && \ +defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + if(png_ptr->transformations & PNG_USER_TRANSFORM) + { + int user_pixel_depth=png_ptr->user_transform_depth* + png_ptr->user_transform_channels; + if(user_pixel_depth > max_pixel_depth) + max_pixel_depth=user_pixel_depth; + } +#endif + + /* align the width on the next larger 8 pixels. Mainly used + for interlacing */ + row_bytes = ((png_ptr->width + 7) & ~((png_uint_32)7)); + /* calculate the maximum bytes needed, adding a byte and a pixel + for safety's sake */ + row_bytes = PNG_ROWBYTES(max_pixel_depth,row_bytes) + + 1 + ((max_pixel_depth + 7) >> 3); +#ifdef PNG_MAX_MALLOC_64K + if (row_bytes > (png_uint_32)65536L) + png_error(png_ptr, "This image requires a row greater than 64KB"); +#endif + png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, row_bytes+64); + png_ptr->row_buf = png_ptr->big_row_buf+32; +#if defined(PNG_DEBUG) && defined(PNG_USE_PNGGCCRD) + png_ptr->row_buf_size = row_bytes; +#endif + +#ifdef PNG_MAX_MALLOC_64K + if ((png_uint_32)png_ptr->rowbytes + 1 > (png_uint_32)65536L) + png_error(png_ptr, "This image requires a row greater than 64KB"); +#endif + if ((png_uint_32)png_ptr->rowbytes > PNG_SIZE_MAX - 1) + png_error(png_ptr, "Row has too many bytes to allocate in memory."); + png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, (png_uint_32)( + png_ptr->rowbytes + 1)); + + png_memset_check(png_ptr, png_ptr->prev_row, 0, png_ptr->rowbytes + 1); + + png_debug1(3, "width = %lu,\n", png_ptr->width); + png_debug1(3, "height = %lu,\n", png_ptr->height); + png_debug1(3, "iwidth = %lu,\n", png_ptr->iwidth); + png_debug1(3, "num_rows = %lu\n", png_ptr->num_rows); + png_debug1(3, "rowbytes = %lu,\n", png_ptr->rowbytes); + png_debug1(3, "irowbytes = %lu,\n", png_ptr->irowbytes); + + png_ptr->flags |= PNG_FLAG_ROW_INIT; +} diff --git a/com32/lib/libpng/pngset.c b/com32/lib/libpng/pngset.c new file mode 100644 index 0000000..13ecb28 --- /dev/null +++ b/com32/lib/libpng/pngset.c @@ -0,0 +1,1219 @@ + +/* pngset.c - storage of image information into info struct + * + * libpng 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * The functions here are used during reads to store data from the file + * into the info struct, and during writes to store application data + * into the info struct for writing into the file. This abstracts the + * info struct and allows us to change the structure in the future. + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_bKGD_SUPPORTED) +void PNGAPI +png_set_bKGD(png_structp png_ptr, png_infop info_ptr, png_color_16p background) +{ + png_debug1(1, "in %s storage function\n", "bKGD"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_memcpy(&(info_ptr->background), background, png_sizeof(png_color_16)); + info_ptr->valid |= PNG_INFO_bKGD; +} +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_cHRM(png_structp png_ptr, png_infop info_ptr, + double white_x, double white_y, double red_x, double red_y, + double green_x, double green_y, double blue_x, double blue_y) +{ + png_debug1(1, "in %s storage function\n", "cHRM"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (white_x < 0.0 || white_y < 0.0 || + red_x < 0.0 || red_y < 0.0 || + green_x < 0.0 || green_y < 0.0 || + blue_x < 0.0 || blue_y < 0.0) + { + png_warning(png_ptr, + "Ignoring attempt to set negative chromaticity value"); + return; + } + if (white_x > 21474.83 || white_y > 21474.83 || + red_x > 21474.83 || red_y > 21474.83 || + green_x > 21474.83 || green_y > 21474.83 || + blue_x > 21474.83 || blue_y > 21474.83) + { + png_warning(png_ptr, + "Ignoring attempt to set chromaticity value exceeding 21474.83"); + return; + } + + info_ptr->x_white = (float)white_x; + info_ptr->y_white = (float)white_y; + info_ptr->x_red = (float)red_x; + info_ptr->y_red = (float)red_y; + info_ptr->x_green = (float)green_x; + info_ptr->y_green = (float)green_y; + info_ptr->x_blue = (float)blue_x; + info_ptr->y_blue = (float)blue_y; +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_x_white = (png_fixed_point)(white_x*100000.+0.5); + info_ptr->int_y_white = (png_fixed_point)(white_y*100000.+0.5); + info_ptr->int_x_red = (png_fixed_point)( red_x*100000.+0.5); + info_ptr->int_y_red = (png_fixed_point)( red_y*100000.+0.5); + info_ptr->int_x_green = (png_fixed_point)(green_x*100000.+0.5); + info_ptr->int_y_green = (png_fixed_point)(green_y*100000.+0.5); + info_ptr->int_x_blue = (png_fixed_point)( blue_x*100000.+0.5); + info_ptr->int_y_blue = (png_fixed_point)( blue_y*100000.+0.5); +#endif + info_ptr->valid |= PNG_INFO_cHRM; +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_cHRM_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x, + png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, + png_fixed_point blue_x, png_fixed_point blue_y) +{ + png_debug1(1, "in %s storage function\n", "cHRM"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (white_x < 0 || white_y < 0 || + red_x < 0 || red_y < 0 || + green_x < 0 || green_y < 0 || + blue_x < 0 || blue_y < 0) + { + png_warning(png_ptr, + "Ignoring attempt to set negative chromaticity value"); + return; + } + if (white_x > (double) PNG_UINT_31_MAX || + white_y > (double) PNG_UINT_31_MAX || + red_x > (double) PNG_UINT_31_MAX || + red_y > (double) PNG_UINT_31_MAX || + green_x > (double) PNG_UINT_31_MAX || + green_y > (double) PNG_UINT_31_MAX || + blue_x > (double) PNG_UINT_31_MAX || + blue_y > (double) PNG_UINT_31_MAX) + { + png_warning(png_ptr, + "Ignoring attempt to set chromaticity value exceeding 21474.83"); + return; + } + info_ptr->int_x_white = white_x; + info_ptr->int_y_white = white_y; + info_ptr->int_x_red = red_x; + info_ptr->int_y_red = red_y; + info_ptr->int_x_green = green_x; + info_ptr->int_y_green = green_y; + info_ptr->int_x_blue = blue_x; + info_ptr->int_y_blue = blue_y; +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->x_white = (float)(white_x/100000.); + info_ptr->y_white = (float)(white_y/100000.); + info_ptr->x_red = (float)( red_x/100000.); + info_ptr->y_red = (float)( red_y/100000.); + info_ptr->x_green = (float)(green_x/100000.); + info_ptr->y_green = (float)(green_y/100000.); + info_ptr->x_blue = (float)( blue_x/100000.); + info_ptr->y_blue = (float)( blue_y/100000.); +#endif + info_ptr->valid |= PNG_INFO_cHRM; +} +#endif +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_gAMA(png_structp png_ptr, png_infop info_ptr, double file_gamma) +{ + double gamma; + png_debug1(1, "in %s storage function\n", "gAMA"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* Check for overflow */ + if (file_gamma > 21474.83) + { + png_warning(png_ptr, "Limiting gamma to 21474.83"); + gamma=21474.83; + } + else + gamma=file_gamma; + info_ptr->gamma = (float)gamma; +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = (int)(gamma*100000.+.5); +#endif + info_ptr->valid |= PNG_INFO_gAMA; + if(gamma == 0.0) + png_warning(png_ptr, "Setting gamma=0"); +} +#endif +void PNGAPI +png_set_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, png_fixed_point + int_gamma) +{ + png_fixed_point gamma; + + png_debug1(1, "in %s storage function\n", "gAMA"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (int_gamma > (png_fixed_point) PNG_UINT_31_MAX) + { + png_warning(png_ptr, "Limiting gamma to 21474.83"); + gamma=PNG_UINT_31_MAX; + } + else + { + if (int_gamma < 0) + { + png_warning(png_ptr, "Setting negative gamma to zero"); + gamma=0; + } + else + gamma=int_gamma; + } +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->gamma = (float)(gamma/100000.); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = gamma; +#endif + info_ptr->valid |= PNG_INFO_gAMA; + if(gamma == 0) + png_warning(png_ptr, "Setting gamma=0"); +} +#endif + +#if defined(PNG_hIST_SUPPORTED) +void PNGAPI +png_set_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p hist) +{ + int i; + + png_debug1(1, "in %s storage function\n", "hIST"); + if (png_ptr == NULL || info_ptr == NULL) + return; + if (info_ptr->num_palette == 0) + { + png_warning(png_ptr, + "Palette size 0, hIST allocation skipped."); + return; + } + +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0); +#endif + /* Changed from info->num_palette to 256 in version 1.2.1 */ + png_ptr->hist = (png_uint_16p)png_malloc_warn(png_ptr, + (png_uint_32)(256 * png_sizeof (png_uint_16))); + if (png_ptr->hist == NULL) + { + png_warning(png_ptr, "Insufficient memory for hIST chunk data."); + return; + } + + for (i = 0; i < info_ptr->num_palette; i++) + png_ptr->hist[i] = hist[i]; + info_ptr->hist = png_ptr->hist; + info_ptr->valid |= PNG_INFO_hIST; + +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_HIST; +#else + png_ptr->flags |= PNG_FLAG_FREE_HIST; +#endif +} +#endif + +void PNGAPI +png_set_IHDR(png_structp png_ptr, png_infop info_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type) +{ + png_debug1(1, "in %s storage function\n", "IHDR"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* check for width and height valid values */ + if (width == 0 || height == 0) + png_error(png_ptr, "Image width or height is zero in IHDR"); +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (width > png_ptr->user_width_max || height > png_ptr->user_height_max) + png_error(png_ptr, "image size exceeds user limits in IHDR"); +#else + if (width > PNG_USER_WIDTH_MAX || height > PNG_USER_HEIGHT_MAX) + png_error(png_ptr, "image size exceeds user limits in IHDR"); +#endif + if (width > PNG_UINT_31_MAX || height > PNG_UINT_31_MAX) + png_error(png_ptr, "Invalid image size in IHDR"); + if ( width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 64 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + png_warning(png_ptr, "Width is too large for libpng to process pixels"); + + /* check other values */ + if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 && + bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth in IHDR"); + + if (color_type < 0 || color_type == 1 || + color_type == 5 || color_type > 6) + png_error(png_ptr, "Invalid color type in IHDR"); + + if (((color_type == PNG_COLOR_TYPE_PALETTE) && bit_depth > 8) || + ((color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && bit_depth < 8)) + png_error(png_ptr, "Invalid color type/bit depth combination in IHDR"); + + if (interlace_type >= PNG_INTERLACE_LAST) + png_error(png_ptr, "Unknown interlace method in IHDR"); + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + png_error(png_ptr, "Unknown compression method in IHDR"); + +#if defined(PNG_MNG_FEATURES_SUPPORTED) + /* Accept filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not read a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE)&&png_ptr->mng_features_permitted) + png_warning(png_ptr,"MNG features are not allowed in a PNG datastream\n"); + if(filter_type != PNG_FILTER_TYPE_BASE) + { + if(!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING) && + ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA))) + png_error(png_ptr, "Unknown filter method in IHDR"); + if(png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) + png_warning(png_ptr, "Invalid filter method in IHDR"); + } +#else + if(filter_type != PNG_FILTER_TYPE_BASE) + png_error(png_ptr, "Unknown filter method in IHDR"); +#endif + + info_ptr->width = width; + info_ptr->height = height; + info_ptr->bit_depth = (png_byte)bit_depth; + info_ptr->color_type =(png_byte) color_type; + info_ptr->compression_type = (png_byte)compression_type; + info_ptr->filter_type = (png_byte)filter_type; + info_ptr->interlace_type = (png_byte)interlace_type; + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + info_ptr->channels = 1; + else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) + info_ptr->channels = 3; + else + info_ptr->channels = 1; + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + info_ptr->channels++; + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth); + + /* check for potential overflow */ + if ( width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 64 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + info_ptr->rowbytes = (png_size_t)0; + else + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth,width); +} + +#if defined(PNG_oFFs_SUPPORTED) +void PNGAPI +png_set_oFFs(png_structp png_ptr, png_infop info_ptr, + png_int_32 offset_x, png_int_32 offset_y, int unit_type) +{ + png_debug1(1, "in %s storage function\n", "oFFs"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_offset = offset_x; + info_ptr->y_offset = offset_y; + info_ptr->offset_unit_type = (png_byte)unit_type; + info_ptr->valid |= PNG_INFO_oFFs; +} +#endif + +#if defined(PNG_pCAL_SUPPORTED) +void PNGAPI +png_set_pCAL(png_structp png_ptr, png_infop info_ptr, + png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams, + png_charp units, png_charpp params) +{ + png_uint_32 length; + int i; + + png_debug1(1, "in %s storage function\n", "pCAL"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + length = png_strlen(purpose) + 1; + png_debug1(3, "allocating purpose for info (%lu bytes)\n", length); + info_ptr->pcal_purpose = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_purpose == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL purpose."); + return; + } + png_memcpy(info_ptr->pcal_purpose, purpose, (png_size_t)length); + + png_debug(3, "storing X0, X1, type, and nparams in info\n"); + info_ptr->pcal_X0 = X0; + info_ptr->pcal_X1 = X1; + info_ptr->pcal_type = (png_byte)type; + info_ptr->pcal_nparams = (png_byte)nparams; + + length = png_strlen(units) + 1; + png_debug1(3, "allocating units for info (%lu bytes)\n", length); + info_ptr->pcal_units = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_units == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL units."); + return; + } + png_memcpy(info_ptr->pcal_units, units, (png_size_t)length); + + info_ptr->pcal_params = (png_charpp)png_malloc_warn(png_ptr, + (png_uint_32)((nparams + 1) * png_sizeof(png_charp))); + if (info_ptr->pcal_params == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL params."); + return; + } + + info_ptr->pcal_params[nparams] = NULL; + + for (i = 0; i < nparams; i++) + { + length = png_strlen(params[i]) + 1; + png_debug2(3, "allocating parameter %d for info (%lu bytes)\n", i, length); + info_ptr->pcal_params[i] = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_params[i] == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL parameter."); + return; + } + png_memcpy(info_ptr->pcal_params[i], params[i], (png_size_t)length); + } + + info_ptr->valid |= PNG_INFO_pCAL; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_PCAL; +#endif +} +#endif + +#if defined(PNG_READ_sCAL_SUPPORTED) || defined(PNG_WRITE_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_sCAL(png_structp png_ptr, png_infop info_ptr, + int unit, double width, double height) +{ + png_debug1(1, "in %s storage function\n", "sCAL"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->scal_unit = (png_byte)unit; + info_ptr->scal_pixel_width = width; + info_ptr->scal_pixel_height = height; + + info_ptr->valid |= PNG_INFO_sCAL; +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_sCAL_s(png_structp png_ptr, png_infop info_ptr, + int unit, png_charp swidth, png_charp sheight) +{ + png_uint_32 length; + + png_debug1(1, "in %s storage function\n", "sCAL"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->scal_unit = (png_byte)unit; + + length = png_strlen(swidth) + 1; + png_debug1(3, "allocating unit for info (%d bytes)\n", length); + info_ptr->scal_s_width = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->scal_s_width == NULL) + { + png_warning(png_ptr, "Memory allocation failed while processing sCAL."); + } + png_memcpy(info_ptr->scal_s_width, swidth, (png_size_t)length); + + length = png_strlen(sheight) + 1; + png_debug1(3, "allocating unit for info (%d bytes)\n", length); + info_ptr->scal_s_height = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->scal_s_height == NULL) + { + png_free (png_ptr, info_ptr->scal_s_width); + png_warning(png_ptr, "Memory allocation failed while processing sCAL."); + } + png_memcpy(info_ptr->scal_s_height, sheight, (png_size_t)length); + + info_ptr->valid |= PNG_INFO_sCAL; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_SCAL; +#endif +} +#endif +#endif +#endif + +#if defined(PNG_pHYs_SUPPORTED) +void PNGAPI +png_set_pHYs(png_structp png_ptr, png_infop info_ptr, + png_uint_32 res_x, png_uint_32 res_y, int unit_type) +{ + png_debug1(1, "in %s storage function\n", "pHYs"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_pixels_per_unit = res_x; + info_ptr->y_pixels_per_unit = res_y; + info_ptr->phys_unit_type = (png_byte)unit_type; + info_ptr->valid |= PNG_INFO_pHYs; +} +#endif + +void PNGAPI +png_set_PLTE(png_structp png_ptr, png_infop info_ptr, + png_colorp palette, int num_palette) +{ + + png_debug1(1, "in %s storage function\n", "PLTE"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* + * It may not actually be necessary to set png_ptr->palette here; + * we do it for backward compatibility with the way the png_handle_tRNS + * function used to do the allocation. + */ +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0); +#endif + + /* Changed in libpng-1.2.1 to allocate 256 instead of num_palette entries, + in case of an invalid PNG file that has too-large sample values. */ + png_ptr->palette = (png_colorp)png_malloc(png_ptr, + 256 * png_sizeof(png_color)); + png_memset(png_ptr->palette, 0, 256 * png_sizeof(png_color)); + png_memcpy(png_ptr->palette, palette, num_palette * png_sizeof (png_color)); + info_ptr->palette = png_ptr->palette; + info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette; + +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_PLTE; +#else + png_ptr->flags |= PNG_FLAG_FREE_PLTE; +#endif + + info_ptr->valid |= PNG_INFO_PLTE; +} + +#if defined(PNG_sBIT_SUPPORTED) +void PNGAPI +png_set_sBIT(png_structp png_ptr, png_infop info_ptr, + png_color_8p sig_bit) +{ + png_debug1(1, "in %s storage function\n", "sBIT"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_memcpy(&(info_ptr->sig_bit), sig_bit, png_sizeof (png_color_8)); + info_ptr->valid |= PNG_INFO_sBIT; +} +#endif + +#if defined(PNG_sRGB_SUPPORTED) +void PNGAPI +png_set_sRGB(png_structp png_ptr, png_infop info_ptr, int intent) +{ + png_debug1(1, "in %s storage function\n", "sRGB"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->srgb_intent = (png_byte)intent; + info_ptr->valid |= PNG_INFO_sRGB; +} + +void PNGAPI +png_set_sRGB_gAMA_and_cHRM(png_structp png_ptr, png_infop info_ptr, + int intent) +{ +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED + float file_gamma; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_fixed_point int_file_gamma; +#endif +#endif +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED + float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_fixed_point int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, + int_green_y, int_blue_x, int_blue_y; +#endif +#endif + png_debug1(1, "in %s storage function\n", "sRGB_gAMA_and_cHRM"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_set_sRGB(png_ptr, info_ptr, intent); + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED + file_gamma = (float).45455; + png_set_gAMA(png_ptr, info_ptr, file_gamma); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + int_file_gamma = 45455L; + png_set_gAMA_fixed(png_ptr, info_ptr, int_file_gamma); +#endif +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FIXED_POINT_SUPPORTED + int_white_x = 31270L; + int_white_y = 32900L; + int_red_x = 64000L; + int_red_y = 33000L; + int_green_x = 30000L; + int_green_y = 60000L; + int_blue_x = 15000L; + int_blue_y = 6000L; + + png_set_cHRM_fixed(png_ptr, info_ptr, + int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, int_green_y, + int_blue_x, int_blue_y); +#endif +#ifdef PNG_FLOATING_POINT_SUPPORTED + white_x = (float).3127; + white_y = (float).3290; + red_x = (float).64; + red_y = (float).33; + green_x = (float).30; + green_y = (float).60; + blue_x = (float).15; + blue_y = (float).06; + + png_set_cHRM(png_ptr, info_ptr, + white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); +#endif +#endif +} +#endif + + +#if defined(PNG_iCCP_SUPPORTED) +void PNGAPI +png_set_iCCP(png_structp png_ptr, png_infop info_ptr, + png_charp name, int compression_type, + png_charp profile, png_uint_32 proflen) +{ + png_charp new_iccp_name; + png_charp new_iccp_profile; + + png_debug1(1, "in %s storage function\n", "iCCP"); + if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL) + return; + + new_iccp_name = (png_charp)png_malloc_warn(png_ptr, png_strlen(name)+1); + if (new_iccp_name == NULL) + { + png_warning(png_ptr, "Insufficient memory to process iCCP chunk."); + return; + } + png_strcpy(new_iccp_name, name); + new_iccp_profile = (png_charp)png_malloc_warn(png_ptr, proflen); + if (new_iccp_profile == NULL) + { + png_free (png_ptr, new_iccp_name); + png_warning(png_ptr, "Insufficient memory to process iCCP profile."); + return; + } + png_memcpy(new_iccp_profile, profile, (png_size_t)proflen); + + png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0); + + info_ptr->iccp_proflen = proflen; + info_ptr->iccp_name = new_iccp_name; + info_ptr->iccp_profile = new_iccp_profile; + /* Compression is always zero but is here so the API and info structure + * does not have to change if we introduce multiple compression types */ + info_ptr->iccp_compression = (png_byte)compression_type; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_ICCP; +#endif + info_ptr->valid |= PNG_INFO_iCCP; +} +#endif + +#if defined(PNG_TEXT_SUPPORTED) +void PNGAPI +png_set_text(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, + int num_text) +{ + int ret; + ret=png_set_text_2(png_ptr, info_ptr, text_ptr, num_text); + if (ret) + png_error(png_ptr, "Insufficient memory to store text"); +} + +int /* PRIVATE */ +png_set_text_2(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, + int num_text) +{ + int i; + + png_debug1(1, "in %s storage function\n", (png_ptr->chunk_name[0] == '\0' ? + "text" : (png_const_charp)png_ptr->chunk_name)); + + if (png_ptr == NULL || info_ptr == NULL || num_text == 0) + return(0); + + /* Make sure we have enough space in the "text" array in info_struct + * to hold all of the incoming text_ptr objects. + */ + if (info_ptr->num_text + num_text > info_ptr->max_text) + { + if (info_ptr->text != NULL) + { + png_textp old_text; + int old_max; + + old_max = info_ptr->max_text; + info_ptr->max_text = info_ptr->num_text + num_text + 8; + old_text = info_ptr->text; + info_ptr->text = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)(info_ptr->max_text * png_sizeof (png_text))); + if (info_ptr->text == NULL) + { + png_free(png_ptr, old_text); + return(1); + } + png_memcpy(info_ptr->text, old_text, (png_size_t)(old_max * + png_sizeof(png_text))); + png_free(png_ptr, old_text); + } + else + { + info_ptr->max_text = num_text + 8; + info_ptr->num_text = 0; + info_ptr->text = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)(info_ptr->max_text * png_sizeof (png_text))); + if (info_ptr->text == NULL) + return(1); +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_TEXT; +#endif + } + png_debug1(3, "allocated %d entries for info_ptr->text\n", + info_ptr->max_text); + } + for (i = 0; i < num_text; i++) + { + png_size_t text_length,key_len; + png_size_t lang_len,lang_key_len; + png_textp textp = &(info_ptr->text[info_ptr->num_text]); + + if (text_ptr[i].key == NULL) + continue; + + key_len = png_strlen(text_ptr[i].key); + + if(text_ptr[i].compression <= 0) + { + lang_len = 0; + lang_key_len = 0; + } + else +#ifdef PNG_iTXt_SUPPORTED + { + /* set iTXt data */ + if (text_ptr[i].lang != NULL) + lang_len = png_strlen(text_ptr[i].lang); + else + lang_len = 0; + if (text_ptr[i].lang_key != NULL) + lang_key_len = png_strlen(text_ptr[i].lang_key); + else + lang_key_len = 0; + } +#else + { + png_warning(png_ptr, "iTXt chunk not supported."); + continue; + } +#endif + + if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0') + { + text_length = 0; +#ifdef PNG_iTXt_SUPPORTED + if(text_ptr[i].compression > 0) + textp->compression = PNG_ITXT_COMPRESSION_NONE; + else +#endif + textp->compression = PNG_TEXT_COMPRESSION_NONE; + } + else + { + text_length = png_strlen(text_ptr[i].text); + textp->compression = text_ptr[i].compression; + } + + textp->key = (png_charp)png_malloc_warn(png_ptr, + (png_uint_32)(key_len + text_length + lang_len + lang_key_len + 4)); + if (textp->key == NULL) + return(1); + png_debug2(2, "Allocated %lu bytes at %x in png_set_text\n", + (png_uint_32)(key_len + lang_len + lang_key_len + text_length + 4), + (int)textp->key); + + png_memcpy(textp->key, text_ptr[i].key, + (png_size_t)(key_len)); + *(textp->key+key_len) = '\0'; +#ifdef PNG_iTXt_SUPPORTED + if (text_ptr[i].compression > 0) + { + textp->lang=textp->key + key_len + 1; + png_memcpy(textp->lang, text_ptr[i].lang, lang_len); + *(textp->lang+lang_len) = '\0'; + textp->lang_key=textp->lang + lang_len + 1; + png_memcpy(textp->lang_key, text_ptr[i].lang_key, lang_key_len); + *(textp->lang_key+lang_key_len) = '\0'; + textp->text=textp->lang_key + lang_key_len + 1; + } + else +#endif + { +#ifdef PNG_iTXt_SUPPORTED + textp->lang=NULL; + textp->lang_key=NULL; +#endif + textp->text=textp->key + key_len + 1; + } + if(text_length) + png_memcpy(textp->text, text_ptr[i].text, + (png_size_t)(text_length)); + *(textp->text+text_length) = '\0'; + +#ifdef PNG_iTXt_SUPPORTED + if(textp->compression > 0) + { + textp->text_length = 0; + textp->itxt_length = text_length; + } + else +#endif + { + textp->text_length = text_length; +#ifdef PNG_iTXt_SUPPORTED + textp->itxt_length = 0; +#endif + } + info_ptr->text[info_ptr->num_text]= *textp; + info_ptr->num_text++; + png_debug1(3, "transferred text chunk %d\n", info_ptr->num_text); + } + return(0); +} +#endif + +#if defined(PNG_tIME_SUPPORTED) +void PNGAPI +png_set_tIME(png_structp png_ptr, png_infop info_ptr, png_timep mod_time) +{ + png_debug1(1, "in %s storage function\n", "tIME"); + if (png_ptr == NULL || info_ptr == NULL || + (png_ptr->mode & PNG_WROTE_tIME)) + return; + + png_memcpy(&(info_ptr->mod_time), mod_time, png_sizeof (png_time)); + info_ptr->valid |= PNG_INFO_tIME; +} +#endif + +#if defined(PNG_tRNS_SUPPORTED) +void PNGAPI +png_set_tRNS(png_structp png_ptr, png_infop info_ptr, + png_bytep trans, int num_trans, png_color_16p trans_values) +{ + png_debug1(1, "in %s storage function\n", "tRNS"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (trans != NULL) + { + /* + * It may not actually be necessary to set png_ptr->trans here; + * we do it for backward compatibility with the way the png_handle_tRNS + * function used to do the allocation. + */ +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); +#endif + /* Changed from num_trans to 256 in version 1.2.1 */ + png_ptr->trans = info_ptr->trans = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + png_memcpy(info_ptr->trans, trans, (png_size_t)num_trans); +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_TRNS; +#else + png_ptr->flags |= PNG_FLAG_FREE_TRNS; +#endif + } + + if (trans_values != NULL) + { + png_memcpy(&(info_ptr->trans_values), trans_values, + png_sizeof(png_color_16)); + if (num_trans == 0) + num_trans = 1; + } + info_ptr->num_trans = (png_uint_16)num_trans; + info_ptr->valid |= PNG_INFO_tRNS; +} +#endif + +#if defined(PNG_sPLT_SUPPORTED) +void PNGAPI +png_set_sPLT(png_structp png_ptr, + png_infop info_ptr, png_sPLT_tp entries, int nentries) +{ + png_sPLT_tp np; + int i; + + np = (png_sPLT_tp)png_malloc_warn(png_ptr, + (info_ptr->splt_palettes_num + nentries) * png_sizeof(png_sPLT_t)); + if (np == NULL) + { + png_warning(png_ptr, "No memory for sPLT palettes."); + return; + } + + png_memcpy(np, info_ptr->splt_palettes, + info_ptr->splt_palettes_num * png_sizeof(png_sPLT_t)); + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes=NULL; + + for (i = 0; i < nentries; i++) + { + png_sPLT_tp to = np + info_ptr->splt_palettes_num + i; + png_sPLT_tp from = entries + i; + + to->name = (png_charp)png_malloc(png_ptr, + png_strlen(from->name) + 1); + /* TODO: use png_malloc_warn */ + png_strcpy(to->name, from->name); + to->entries = (png_sPLT_entryp)png_malloc(png_ptr, + from->nentries * png_sizeof(png_sPLT_t)); + /* TODO: use png_malloc_warn */ + png_memcpy(to->entries, from->entries, + from->nentries * png_sizeof(png_sPLT_t)); + to->nentries = from->nentries; + to->depth = from->depth; + } + + info_ptr->splt_palettes = np; + info_ptr->splt_palettes_num += nentries; + info_ptr->valid |= PNG_INFO_sPLT; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_SPLT; +#endif +} +#endif /* PNG_sPLT_SUPPORTED */ + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +void PNGAPI +png_set_unknown_chunks(png_structp png_ptr, + png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns) +{ + png_unknown_chunkp np; + int i; + + if (png_ptr == NULL || info_ptr == NULL || num_unknowns == 0) + return; + + np = (png_unknown_chunkp)png_malloc_warn(png_ptr, + (info_ptr->unknown_chunks_num + num_unknowns) * + png_sizeof(png_unknown_chunk)); + if (np == NULL) + { + png_warning(png_ptr, "Out of memory while processing unknown chunk."); + return; + } + + png_memcpy(np, info_ptr->unknown_chunks, + info_ptr->unknown_chunks_num * png_sizeof(png_unknown_chunk)); + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks=NULL; + + for (i = 0; i < num_unknowns; i++) + { + png_unknown_chunkp to = np + info_ptr->unknown_chunks_num + i; + png_unknown_chunkp from = unknowns + i; + + png_strncpy((png_charp)to->name, (png_charp)from->name, 5); + to->data = (png_bytep)png_malloc_warn(png_ptr, from->size); + if (to->data == NULL) + { + png_warning(png_ptr, "Out of memory processing unknown chunk."); + } + else + { + png_memcpy(to->data, from->data, from->size); + to->size = from->size; + + /* note our location in the read or write sequence */ + to->location = (png_byte)(png_ptr->mode & 0xff); + } + } + + info_ptr->unknown_chunks = np; + info_ptr->unknown_chunks_num += num_unknowns; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_UNKN; +#endif +} +void PNGAPI +png_set_unknown_chunk_location(png_structp png_ptr, png_infop info_ptr, + int chunk, int location) +{ + if(png_ptr != NULL && info_ptr != NULL && chunk >= 0 && chunk < + (int)info_ptr->unknown_chunks_num) + info_ptr->unknown_chunks[chunk].location = (png_byte)location; +} +#endif + +#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +void PNGAPI +png_permit_empty_plte (png_structp png_ptr, int empty_plte_permitted) +{ + /* This function is deprecated in favor of png_permit_mng_features() + and will be removed from libpng-2.0.0 */ + png_debug(1, "in png_permit_empty_plte, DEPRECATED.\n"); + if (png_ptr == NULL) + return; + png_ptr->mng_features_permitted = (png_byte) + ((png_ptr->mng_features_permitted & (~(PNG_FLAG_MNG_EMPTY_PLTE))) | + ((empty_plte_permitted & PNG_FLAG_MNG_EMPTY_PLTE))); +} +#endif + +#if defined(PNG_MNG_FEATURES_SUPPORTED) +png_uint_32 PNGAPI +png_permit_mng_features (png_structp png_ptr, png_uint_32 mng_features) +{ + png_debug(1, "in png_permit_mng_features\n"); + if (png_ptr == NULL) + return (png_uint_32)0; + png_ptr->mng_features_permitted = + (png_byte)(mng_features & PNG_ALL_MNG_FEATURES); + return (png_uint_32)png_ptr->mng_features_permitted; +} +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +void PNGAPI +png_set_keep_unknown_chunks(png_structp png_ptr, int keep, png_bytep + chunk_list, int num_chunks) +{ + png_bytep new_list, p; + int i, old_num_chunks; + if (num_chunks == 0) + { + if(keep == PNG_HANDLE_CHUNK_ALWAYS || keep == PNG_HANDLE_CHUNK_IF_SAFE) + png_ptr->flags |= PNG_FLAG_KEEP_UNKNOWN_CHUNKS; + else + png_ptr->flags &= ~PNG_FLAG_KEEP_UNKNOWN_CHUNKS; + + if(keep == PNG_HANDLE_CHUNK_ALWAYS) + png_ptr->flags |= PNG_FLAG_KEEP_UNSAFE_CHUNKS; + else + png_ptr->flags &= ~PNG_FLAG_KEEP_UNSAFE_CHUNKS; + return; + } + if (chunk_list == NULL) + return; + old_num_chunks=png_ptr->num_chunk_list; + new_list=(png_bytep)png_malloc(png_ptr, + (png_uint_32)(5*(num_chunks+old_num_chunks))); + if(png_ptr->chunk_list != NULL) + { + png_memcpy(new_list, png_ptr->chunk_list, + (png_size_t)(5*old_num_chunks)); + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list=NULL; + } + png_memcpy(new_list+5*old_num_chunks, chunk_list, + (png_size_t)(5*num_chunks)); + for (p=new_list+5*old_num_chunks+4, i=0; inum_chunk_list=old_num_chunks+num_chunks; + png_ptr->chunk_list=new_list; +#ifdef PNG_FREE_ME_SUPPORTED + png_ptr->free_me |= PNG_FREE_LIST; +#endif +} +#endif + +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) +void PNGAPI +png_set_read_user_chunk_fn(png_structp png_ptr, png_voidp user_chunk_ptr, + png_user_chunk_ptr read_user_chunk_fn) +{ + png_debug(1, "in png_set_read_user_chunk_fn\n"); + png_ptr->read_user_chunk_fn = read_user_chunk_fn; + png_ptr->user_chunk_ptr = user_chunk_ptr; +} +#endif + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +void PNGAPI +png_set_rows(png_structp png_ptr, png_infop info_ptr, png_bytepp row_pointers) +{ + png_debug1(1, "in %s storage function\n", "rows"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if(info_ptr->row_pointers && (info_ptr->row_pointers != row_pointers)) + png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); + info_ptr->row_pointers = row_pointers; + if(row_pointers) + info_ptr->valid |= PNG_INFO_IDAT; +} +#endif + +#ifdef PNG_WRITE_SUPPORTED +void PNGAPI +png_set_compression_buffer_size(png_structp png_ptr, png_uint_32 size) +{ + if(png_ptr->zbuf) + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf_size = (png_size_t)size; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; +} +#endif + +void PNGAPI +png_set_invalid(png_structp png_ptr, png_infop info_ptr, int mask) +{ + if (png_ptr && info_ptr) + info_ptr->valid &= ~(mask); +} + + +#ifndef PNG_1_0_X +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +/* this function was added to libpng 1.2.0 and should always exist by default */ +void PNGAPI +png_set_asm_flags (png_structp png_ptr, png_uint_32 asm_flags) +{ + png_uint_32 settable_asm_flags; + png_uint_32 settable_mmx_flags; + + settable_mmx_flags = +#ifdef PNG_HAVE_ASSEMBLER_COMBINE_ROW + PNG_ASM_FLAG_MMX_READ_COMBINE_ROW | +#endif +#ifdef PNG_HAVE_ASSEMBLER_READ_INTERLACE + PNG_ASM_FLAG_MMX_READ_INTERLACE | +#endif +#ifdef PNG_HAVE_ASSEMBLER_READ_FILTER_ROW + PNG_ASM_FLAG_MMX_READ_FILTER_SUB | + PNG_ASM_FLAG_MMX_READ_FILTER_UP | + PNG_ASM_FLAG_MMX_READ_FILTER_AVG | + PNG_ASM_FLAG_MMX_READ_FILTER_PAETH | +#endif + 0; + + /* could be some non-MMX ones in the future, but not currently: */ + settable_asm_flags = settable_mmx_flags; + + if (!(png_ptr->asm_flags & PNG_ASM_FLAG_MMX_SUPPORT_COMPILED) || + !(png_ptr->asm_flags & PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU)) + { + /* clear all MMX flags if MMX isn't supported */ + settable_asm_flags &= ~settable_mmx_flags; + png_ptr->asm_flags &= ~settable_mmx_flags; + } + + /* we're replacing the settable bits with those passed in by the user, + * so first zero them out of the master copy, then logical-OR in the + * allowed subset that was requested */ + + png_ptr->asm_flags &= ~settable_asm_flags; /* zero them */ + png_ptr->asm_flags |= (asm_flags & settable_asm_flags); /* set them */ +} +#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */ + +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +/* this function was added to libpng 1.2.0 */ +void PNGAPI +png_set_mmx_thresholds (png_structp png_ptr, + png_byte mmx_bitdepth_threshold, + png_uint_32 mmx_rowbytes_threshold) +{ + png_ptr->mmx_bitdepth_threshold = mmx_bitdepth_threshold; + png_ptr->mmx_rowbytes_threshold = mmx_rowbytes_threshold; +} +#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */ + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +/* this function was added to libpng 1.2.6 */ +void PNGAPI +png_set_user_limits (png_structp png_ptr, png_uint_32 user_width_max, + png_uint_32 user_height_max) +{ + /* Images with dimensions larger than these limits will be + * rejected by png_set_IHDR(). To accept any PNG datastream + * regardless of dimensions, set both limits to 0x7ffffffL. + */ + png_ptr->user_width_max = user_width_max; + png_ptr->user_height_max = user_height_max; +} +#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ + +#endif /* ?PNG_1_0_X */ diff --git a/com32/lib/libpng/pngtest.c b/com32/lib/libpng/pngtest.c new file mode 100644 index 0000000..163f9f7 --- /dev/null +++ b/com32/lib/libpng/pngtest.c @@ -0,0 +1,1554 @@ + +/* pngtest.c - a simple test program to test libpng + * + * libpng 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This program reads in a PNG image, writes it out again, and then + * compares the two files. If the files are identical, this shows that + * the basic chunk handling, filtering, and (de)compression code is working + * properly. It does not currently test all of the transforms, although + * it probably should. + * + * The program will report "FAIL" in certain legitimate cases: + * 1) when the compression level or filter selection method is changed. + * 2) when the maximum IDAT size (PNG_ZBUF_SIZE in pngconf.h) is not 8192. + * 3) unknown unsafe-to-copy ancillary chunks or unknown critical chunks + * exist in the input file. + * 4) others not listed here... + * In these cases, it is best to check with another tool such as "pngcheck" + * to see what the differences between the two files are. + * + * If a filename is given on the command-line, then this file is used + * for the input, rather than the default "pngtest.png". This allows + * testing a wide variety of files easily. You can also test a number + * of files at once by typing "pngtest -m file1.png file2.png ..." + */ + +#include "png.h" + +#if defined(_WIN32_WCE) +# if _WIN32_WCE < 211 + __error__ (f|w)printf functions are not supported on old WindowsCE.; +# endif +# include +# include +# define READFILE(file, data, length, check) \ + if (ReadFile(file, data, length, &check,NULL)) check = 0 +# define WRITEFILE(file, data, length, check)) \ + if (WriteFile(file, data, length, &check, NULL)) check = 0 +# define FCLOSE(file) CloseHandle(file) +#else +# include +# include +# include +# define READFILE(file, data, length, check) \ + check=(png_size_t)fread(data,(png_size_t)1,length,file) +# define WRITEFILE(file, data, length, check) \ + check=(png_size_t)fwrite(data,(png_size_t)1, length, file) +# define FCLOSE(file) fclose(file) +#endif + +#if defined(PNG_NO_STDIO) +# if defined(_WIN32_WCE) + typedef HANDLE png_FILE_p; +# else + typedef FILE * png_FILE_p; +# endif +#endif + +/* Makes pngtest verbose so we can find problems (needs to be before png.h) */ +#ifndef PNG_DEBUG +# define PNG_DEBUG 0 +#endif + +#if !PNG_DEBUG +# define SINGLE_ROWBUF_ALLOC /* makes buffer overruns easier to nail */ +#endif + +/* Turn on CPU timing +#define PNGTEST_TIMING +*/ + +#ifdef PNG_NO_FLOATING_POINT_SUPPORTED +#undef PNGTEST_TIMING +#endif + +#ifdef PNGTEST_TIMING +static float t_start, t_stop, t_decode, t_encode, t_misc; +#include +#endif + +/* Define png_jmpbuf() in case we are using a pre-1.0.6 version of libpng */ +#ifndef png_jmpbuf +# define png_jmpbuf(png_ptr) png_ptr->jmpbuf +#endif + +#ifdef PNGTEST_TIMING +static float t_start, t_stop, t_decode, t_encode, t_misc; +#if !defined(PNG_tIME_SUPPORTED) +#include +#endif +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) +static int tIME_chunk_present=0; +static char tIME_string[30] = "no tIME chunk present in file"; +#endif + +static int verbose = 0; + +int test_one_file PNGARG((PNG_CONST char *inname, PNG_CONST char *outname)); + +#ifdef __TURBOC__ +#include +#endif + +/* defined so I can write to a file on gui/windowing platforms */ +/* #define STDERR stderr */ +#define STDERR stdout /* for DOS */ + +/* example of using row callbacks to make a simple progress meter */ +static int status_pass=1; +static int status_dots_requested=0; +static int status_dots=1; + +void +#ifdef PNG_1_0_X +PNGAPI +#endif +read_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass); +void +#ifdef PNG_1_0_X +PNGAPI +#endif +read_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass) +{ + if(png_ptr == NULL || row_number > PNG_UINT_31_MAX) return; + if(status_pass != pass) + { + fprintf(stdout,"\n Pass %d: ",pass); + status_pass = pass; + status_dots = 31; + } + status_dots--; + if(status_dots == 0) + { + fprintf(stdout, "\n "); + status_dots=30; + } + fprintf(stdout, "r"); +} + +void +#ifdef PNG_1_0_X +PNGAPI +#endif +write_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass); +void +#ifdef PNG_1_0_X +PNGAPI +#endif +write_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass) +{ + if(png_ptr == NULL || row_number > PNG_UINT_31_MAX || pass > 7) return; + fprintf(stdout, "w"); +} + + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) +/* Example of using user transform callback (we don't transform anything, + but merely examine the row filters. We set this to 256 rather than + 5 in case illegal filter values are present.) */ +static png_uint_32 filters_used[256]; +void +#ifdef PNG_1_0_X +PNGAPI +#endif +count_filters(png_structp png_ptr, png_row_infop row_info, png_bytep data); +void +#ifdef PNG_1_0_X +PNGAPI +#endif +count_filters(png_structp png_ptr, png_row_infop row_info, png_bytep data) +{ + if(png_ptr != NULL && row_info != NULL) + ++filters_used[*(data-1)]; +} +#endif + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +/* example of using user transform callback (we don't transform anything, + but merely count the zero samples) */ + +static png_uint_32 zero_samples; + +void +#ifdef PNG_1_0_X +PNGAPI +#endif +count_zero_samples(png_structp png_ptr, png_row_infop row_info, png_bytep data); +void +#ifdef PNG_1_0_X +PNGAPI +#endif +count_zero_samples(png_structp png_ptr, png_row_infop row_info, png_bytep data) +{ + png_bytep dp = data; + if(png_ptr == NULL)return; + + /* contents of row_info: + * png_uint_32 width width of row + * png_uint_32 rowbytes number of bytes in row + * png_byte color_type color type of pixels + * png_byte bit_depth bit depth of samples + * png_byte channels number of channels (1-4) + * png_byte pixel_depth bits per pixel (depth*channels) + */ + + + /* counts the number of zero samples (or zero pixels if color_type is 3 */ + + if(row_info->color_type == 0 || row_info->color_type == 3) + { + int pos=0; + png_uint_32 n, nstop; + for (n=0, nstop=row_info->width; nbit_depth == 1) + { + if(((*dp << pos++ ) & 0x80) == 0) zero_samples++; + if(pos == 8) + { + pos = 0; + dp++; + } + } + if(row_info->bit_depth == 2) + { + if(((*dp << (pos+=2)) & 0xc0) == 0) zero_samples++; + if(pos == 8) + { + pos = 0; + dp++; + } + } + if(row_info->bit_depth == 4) + { + if(((*dp << (pos+=4)) & 0xf0) == 0) zero_samples++; + if(pos == 8) + { + pos = 0; + dp++; + } + } + if(row_info->bit_depth == 8) + if(*dp++ == 0) zero_samples++; + if(row_info->bit_depth == 16) + { + if((*dp | *(dp+1)) == 0) zero_samples++; + dp+=2; + } + } + } + else /* other color types */ + { + png_uint_32 n, nstop; + int channel; + int color_channels = row_info->channels; + if(row_info->color_type > 3)color_channels--; + + for (n=0, nstop=row_info->width; nbit_depth == 8) + if(*dp++ == 0) zero_samples++; + if(row_info->bit_depth == 16) + { + if((*dp | *(dp+1)) == 0) zero_samples++; + dp+=2; + } + } + if(row_info->color_type > 3) + { + dp++; + if(row_info->bit_depth == 16)dp++; + } + } + } +} +#endif /* PNG_WRITE_USER_TRANSFORM_SUPPORTED */ + +static int wrote_question = 0; + +#if defined(PNG_NO_STDIO) +/* START of code to validate stdio-free compilation */ +/* These copies of the default read/write functions come from pngrio.c and */ +/* pngwio.c. They allow "don't include stdio" testing of the library. */ +/* This is the function that does the actual reading of data. If you are + not reading from a standard C stream, you should create a replacement + read_data function and use it at run time with png_set_read_fn(), rather + than changing the library. */ + +#ifndef USE_FAR_KEYWORD +static void +pngtest_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check; + + /* fread() returns 0 on error, so it is OK to store this in a png_size_t + * instead of an int, which is what fread() actually returns. + */ + READFILE((png_FILE_p)png_ptr->io_ptr, data, length, check); + + if (check != length) + { + png_error(png_ptr, "Read Error!"); + } +} +#else +/* this is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +static void +pngtest_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + int check; + png_byte *n_data; + png_FILE_p io_ptr; + + /* Check if data really is near. If so, use usual code. */ + n_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)n_data == data) + { + READFILE(io_ptr, n_data, length, check); + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t read, remaining, err; + check = 0; + remaining = length; + do + { + read = MIN(NEAR_BUF_SIZE, remaining); + READFILE(io_ptr, buf, 1, err); + png_memcpy(data, buf, read); /* copy far buffer to near buffer */ + if(err != read) + break; + else + check += err; + data += read; + remaining -= read; + } + while (remaining != 0); + } + if (check != length) + { + png_error(png_ptr, "read Error"); + } +} +#endif /* USE_FAR_KEYWORD */ + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +static void +pngtest_flush(png_structp png_ptr) +{ +#if !defined(_WIN32_WCE) + png_FILE_p io_ptr; + io_ptr = (png_FILE_p)CVT_PTR((png_ptr->io_ptr)); + if (io_ptr != NULL) + fflush(io_ptr); +#endif +} +#endif + +/* This is the function that does the actual writing of data. If you are + not writing to a standard C stream, you should create a replacement + write_data function and use it at run time with png_set_write_fn(), rather + than changing the library. */ +#ifndef USE_FAR_KEYWORD +static void +pngtest_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + + WRITEFILE((png_FILE_p)png_ptr->io_ptr, data, length, check); + if (check != length) + { + png_error(png_ptr, "Write Error"); + } +} +#else +/* this is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +static void +pngtest_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + png_byte *near_data; /* Needs to be "png_byte *" instead of "png_bytep" */ + png_FILE_p io_ptr; + + /* Check if data really is near. If so, use usual code. */ + near_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)near_data == data) + { + WRITEFILE(io_ptr, near_data, length, check); + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t written, remaining, err; + check = 0; + remaining = length; + do + { + written = MIN(NEAR_BUF_SIZE, remaining); + png_memcpy(buf, data, written); /* copy far buffer to near buffer */ + WRITEFILE(io_ptr, buf, written, err); + if (err != written) + break; + else + check += err; + data += written; + remaining -= written; + } + while (remaining != 0); + } + if (check != length) + { + png_error(png_ptr, "Write Error"); + } +} + +#endif /* USE_FAR_KEYWORD */ + +/* This function is called when there is a warning, but the library thinks + * it can continue anyway. Replacement functions don't have to do anything + * here if you don't want to. In the default configuration, png_ptr is + * not used, but it is passed in case it may be useful. + */ +static void +pngtest_warning(png_structp png_ptr, png_const_charp message) +{ + PNG_CONST char *name = "UNKNOWN (ERROR!)"; + if (png_ptr != NULL && png_ptr->error_ptr != NULL) + name = png_ptr->error_ptr; + fprintf(STDERR, "%s: libpng warning: %s\n", name, message); +} + +/* This is the default error handling function. Note that replacements for + * this function MUST NOT RETURN, or the program will likely crash. This + * function is used by default, or if the program supplies NULL for the + * error function pointer in png_set_error_fn(). + */ +static void +pngtest_error(png_structp png_ptr, png_const_charp message) +{ + pngtest_warning(png_ptr, message); + /* We can return because png_error calls the default handler, which is + * actually OK in this case. */ +} +#endif /* PNG_NO_STDIO */ +/* END of code to validate stdio-free compilation */ + +/* START of code to validate memory allocation and deallocation */ +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + +/* Allocate memory. For reasonable files, size should never exceed + 64K. However, zlib may allocate more then 64K if you don't tell + it not to. See zconf.h and png.h for more information. zlib does + need to allocate exactly 64K, so whatever you call here must + have the ability to do that. + + This piece of code can be compiled to validate max 64K allocations + by setting MAXSEG_64K in zlib zconf.h *or* PNG_MAX_MALLOC_64K. */ +typedef struct memory_information +{ + png_uint_32 size; + png_voidp pointer; + struct memory_information FAR *next; +} memory_information; +typedef memory_information FAR *memory_infop; + +static memory_infop pinformation = NULL; +static int current_allocation = 0; +static int maximum_allocation = 0; +static int total_allocation = 0; +static int num_allocations = 0; + +png_voidp png_debug_malloc PNGARG((png_structp png_ptr, png_uint_32 size)); +void png_debug_free PNGARG((png_structp png_ptr, png_voidp ptr)); + +png_voidp +png_debug_malloc(png_structp png_ptr, png_uint_32 size) +{ + + /* png_malloc has already tested for NULL; png_create_struct calls + png_debug_malloc directly, with png_ptr == NULL which is OK */ + + if (size == 0) + return (NULL); + + /* This calls the library allocator twice, once to get the requested + buffer and once to get a new free list entry. */ + { + /* Disable malloc_fn and free_fn */ + memory_infop pinfo; + png_set_mem_fn(png_ptr, NULL, NULL, NULL); + pinfo = (memory_infop)png_malloc(png_ptr, + (png_uint_32)png_sizeof (*pinfo)); + pinfo->size = size; + current_allocation += size; + total_allocation += size; + num_allocations ++; + if (current_allocation > maximum_allocation) + maximum_allocation = current_allocation; + pinfo->pointer = (png_voidp)png_malloc(png_ptr, size); + /* Restore malloc_fn and free_fn */ + png_set_mem_fn(png_ptr, png_voidp_NULL, (png_malloc_ptr)png_debug_malloc, + (png_free_ptr)png_debug_free); + if (size != 0 && pinfo->pointer == NULL) + { + current_allocation -= size; + total_allocation -= size; + png_error(png_ptr, + "out of memory in pngtest->png_debug_malloc."); + } + pinfo->next = pinformation; + pinformation = pinfo; + /* Make sure the caller isn't assuming zeroed memory. */ + png_memset(pinfo->pointer, 0xdd, pinfo->size); + if(verbose) + printf("png_malloc %lu bytes at %x\n",size,pinfo->pointer); + assert(pinfo->size != 12345678); + return (png_voidp)(pinfo->pointer); + } +} + +/* Free a pointer. It is removed from the list at the same time. */ +void +png_debug_free(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL) + fprintf(STDERR, "NULL pointer to png_debug_free.\n"); + if (ptr == 0) + { +#if 0 /* This happens all the time. */ + fprintf(STDERR, "WARNING: freeing NULL pointer\n"); +#endif + return; + } + + /* Unlink the element from the list. */ + { + memory_infop FAR *ppinfo = &pinformation; + for (;;) + { + memory_infop pinfo = *ppinfo; + if (pinfo->pointer == ptr) + { + *ppinfo = pinfo->next; + current_allocation -= pinfo->size; + if (current_allocation < 0) + fprintf(STDERR, "Duplicate free of memory\n"); + /* We must free the list element too, but first kill + the memory that is to be freed. */ + png_memset(ptr, 0x55, pinfo->size); + png_free_default(png_ptr, pinfo); + pinfo=NULL; + break; + } + if (pinfo->next == NULL) + { + fprintf(STDERR, "Pointer %x not found\n", (unsigned int)ptr); + break; + } + ppinfo = &pinfo->next; + } + } + + /* Finally free the data. */ + if(verbose) + printf("Freeing %x\n",ptr); + png_free_default(png_ptr, ptr); + ptr=NULL; +} +#endif /* PNG_USER_MEM_SUPPORTED && PNG_DEBUG */ +/* END of code to test memory allocation/deallocation */ + +/* Test one file */ +int +test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) +{ + static png_FILE_p fpin; + static png_FILE_p fpout; /* "static" prevents setjmp corruption */ + png_structp read_ptr; + png_infop read_info_ptr, end_info_ptr; +#ifdef PNG_WRITE_SUPPORTED + png_structp write_ptr; + png_infop write_info_ptr; + png_infop write_end_info_ptr; +#else + png_structp write_ptr = NULL; + png_infop write_info_ptr = NULL; + png_infop write_end_info_ptr = NULL; +#endif + png_bytep row_buf; + png_uint_32 y; + png_uint_32 width, height; + int num_pass, pass; + int bit_depth, color_type; +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + +#if defined(_WIN32_WCE) + TCHAR path[MAX_PATH]; +#endif + char inbuf[256], outbuf[256]; + + row_buf = NULL; + +#if defined(_WIN32_WCE) + MultiByteToWideChar(CP_ACP, 0, inname, -1, path, MAX_PATH); + if ((fpin = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) +#else + if ((fpin = fopen(inname, "rb")) == NULL) +#endif + { + fprintf(STDERR, "Could not find input file %s\n", inname); + return (1); + } + +#if defined(_WIN32_WCE) + MultiByteToWideChar(CP_ACP, 0, outname, -1, path, MAX_PATH); + if ((fpout = CreateFile(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL)) == INVALID_HANDLE_VALUE) +#else + if ((fpout = fopen(outname, "wb")) == NULL) +#endif + { + fprintf(STDERR, "Could not open output file %s\n", outname); + FCLOSE(fpin); + return (1); + } + + png_debug(0, "Allocating read and write structures\n"); +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + read_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, png_voidp_NULL, + png_error_ptr_NULL, png_error_ptr_NULL, png_voidp_NULL, + (png_malloc_ptr)png_debug_malloc, (png_free_ptr)png_debug_free); +#else + read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, png_voidp_NULL, + png_error_ptr_NULL, png_error_ptr_NULL); +#endif +#if defined(PNG_NO_STDIO) + png_set_error_fn(read_ptr, (png_voidp)inname, pngtest_error, + pngtest_warning); +#endif +#ifdef PNG_WRITE_SUPPORTED +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + write_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, png_voidp_NULL, + png_error_ptr_NULL, png_error_ptr_NULL, png_voidp_NULL, + (png_malloc_ptr)png_debug_malloc, (png_free_ptr)png_debug_free); +#else + write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, png_voidp_NULL, + png_error_ptr_NULL, png_error_ptr_NULL); +#endif +#if defined(PNG_NO_STDIO) + png_set_error_fn(write_ptr, (png_voidp)inname, pngtest_error, + pngtest_warning); +#endif +#endif + png_debug(0, "Allocating read_info, write_info and end_info structures\n"); + read_info_ptr = png_create_info_struct(read_ptr); + end_info_ptr = png_create_info_struct(read_ptr); +#ifdef PNG_WRITE_SUPPORTED + write_info_ptr = png_create_info_struct(write_ptr); + write_end_info_ptr = png_create_info_struct(write_ptr); +#endif + +#ifdef PNG_SETJMP_SUPPORTED + png_debug(0, "Setting jmpbuf for read struct\n"); +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_jmpbuf(read_ptr))) +#endif + { + fprintf(STDERR, "%s -> %s: libpng read error\n", inname, outname); + if (row_buf) + png_free(read_ptr, row_buf); + png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); +#ifdef PNG_WRITE_SUPPORTED + png_destroy_info_struct(write_ptr, &write_end_info_ptr); + png_destroy_write_struct(&write_ptr, &write_info_ptr); +#endif + FCLOSE(fpin); + FCLOSE(fpout); + return (1); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_jmpbuf(read_ptr),jmpbuf,png_sizeof(jmp_buf)); +#endif + +#ifdef PNG_WRITE_SUPPORTED + png_debug(0, "Setting jmpbuf for write struct\n"); +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_jmpbuf(write_ptr))) +#endif + { + fprintf(STDERR, "%s -> %s: libpng write error\n", inname, outname); + png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); + png_destroy_info_struct(write_ptr, &write_end_info_ptr); +#ifdef PNG_WRITE_SUPPORTED + png_destroy_write_struct(&write_ptr, &write_info_ptr); +#endif + FCLOSE(fpin); + FCLOSE(fpout); + return (1); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_jmpbuf(write_ptr),jmpbuf,png_sizeof(jmp_buf)); +#endif +#endif +#endif + + png_debug(0, "Initializing input and output streams\n"); +#if !defined(PNG_NO_STDIO) + png_init_io(read_ptr, fpin); +# ifdef PNG_WRITE_SUPPORTED + png_init_io(write_ptr, fpout); +# endif +#else + png_set_read_fn(read_ptr, (png_voidp)fpin, pngtest_read_data); +# ifdef PNG_WRITE_SUPPORTED + png_set_write_fn(write_ptr, (png_voidp)fpout, pngtest_write_data, +# if defined(PNG_WRITE_FLUSH_SUPPORTED) + pngtest_flush); +# else + NULL); +# endif +# endif +#endif + if(status_dots_requested == 1) + { +#ifdef PNG_WRITE_SUPPORTED + png_set_write_status_fn(write_ptr, write_row_callback); +#endif + png_set_read_status_fn(read_ptr, read_row_callback); + } + else + { +#ifdef PNG_WRITE_SUPPORTED + png_set_write_status_fn(write_ptr, png_write_status_ptr_NULL); +#endif + png_set_read_status_fn(read_ptr, png_read_status_ptr_NULL); + } + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + { + int i; + for(i=0; i<256; i++) + filters_used[i]=0; + png_set_read_user_transform_fn(read_ptr, count_filters); + } +#endif +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + zero_samples=0; + png_set_write_user_transform_fn(write_ptr, count_zero_samples); +#endif + +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) +# ifndef PNG_HANDLE_CHUNK_ALWAYS +# define PNG_HANDLE_CHUNK_ALWAYS 3 +# endif + png_set_keep_unknown_chunks(read_ptr, PNG_HANDLE_CHUNK_ALWAYS, + png_bytep_NULL, 0); +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) +# ifndef PNG_HANDLE_CHUNK_IF_SAFE +# define PNG_HANDLE_CHUNK_IF_SAFE 2 +# endif + png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_IF_SAFE, + png_bytep_NULL, 0); +#endif + + png_debug(0, "Reading info struct\n"); + png_read_info(read_ptr, read_info_ptr); + + png_debug(0, "Transferring info struct\n"); + { + int interlace_type, compression_type, filter_type; + + if (png_get_IHDR(read_ptr, read_info_ptr, &width, &height, &bit_depth, + &color_type, &interlace_type, &compression_type, &filter_type)) + { + png_set_IHDR(write_ptr, write_info_ptr, width, height, bit_depth, +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + color_type, interlace_type, compression_type, filter_type); +#else + color_type, PNG_INTERLACE_NONE, compression_type, filter_type); +#endif + } + } +#if defined(PNG_FIXED_POINT_SUPPORTED) +#if defined(PNG_cHRM_SUPPORTED) + { + png_fixed_point white_x, white_y, red_x, red_y, green_x, green_y, blue_x, + blue_y; + if (png_get_cHRM_fixed(read_ptr, read_info_ptr, &white_x, &white_y, &red_x, + &red_y, &green_x, &green_y, &blue_x, &blue_y)) + { + png_set_cHRM_fixed(write_ptr, write_info_ptr, white_x, white_y, red_x, + red_y, green_x, green_y, blue_x, blue_y); + } + } +#endif +#if defined(PNG_gAMA_SUPPORTED) + { + png_fixed_point gamma; + + if (png_get_gAMA_fixed(read_ptr, read_info_ptr, &gamma)) + { + png_set_gAMA_fixed(write_ptr, write_info_ptr, gamma); + } + } +#endif +#else /* Use floating point versions */ +#if defined(PNG_FLOATING_POINT_SUPPORTED) +#if defined(PNG_cHRM_SUPPORTED) + { + double white_x, white_y, red_x, red_y, green_x, green_y, blue_x, + blue_y; + if (png_get_cHRM(read_ptr, read_info_ptr, &white_x, &white_y, &red_x, + &red_y, &green_x, &green_y, &blue_x, &blue_y)) + { + png_set_cHRM(write_ptr, write_info_ptr, white_x, white_y, red_x, + red_y, green_x, green_y, blue_x, blue_y); + } + } +#endif +#if defined(PNG_gAMA_SUPPORTED) + { + double gamma; + + if (png_get_gAMA(read_ptr, read_info_ptr, &gamma)) + { + png_set_gAMA(write_ptr, write_info_ptr, gamma); + } + } +#endif +#endif /* floating point */ +#endif /* fixed point */ +#if defined(PNG_iCCP_SUPPORTED) + { + png_charp name; + png_charp profile; + png_uint_32 proflen; + int compression_type; + + if (png_get_iCCP(read_ptr, read_info_ptr, &name, &compression_type, + &profile, &proflen)) + { + png_set_iCCP(write_ptr, write_info_ptr, name, compression_type, + profile, proflen); + } + } +#endif +#if defined(PNG_sRGB_SUPPORTED) + { + int intent; + + if (png_get_sRGB(read_ptr, read_info_ptr, &intent)) + { + png_set_sRGB(write_ptr, write_info_ptr, intent); + } + } +#endif + { + png_colorp palette; + int num_palette; + + if (png_get_PLTE(read_ptr, read_info_ptr, &palette, &num_palette)) + { + png_set_PLTE(write_ptr, write_info_ptr, palette, num_palette); + } + } +#if defined(PNG_bKGD_SUPPORTED) + { + png_color_16p background; + + if (png_get_bKGD(read_ptr, read_info_ptr, &background)) + { + png_set_bKGD(write_ptr, write_info_ptr, background); + } + } +#endif +#if defined(PNG_hIST_SUPPORTED) + { + png_uint_16p hist; + + if (png_get_hIST(read_ptr, read_info_ptr, &hist)) + { + png_set_hIST(write_ptr, write_info_ptr, hist); + } + } +#endif +#if defined(PNG_oFFs_SUPPORTED) + { + png_int_32 offset_x, offset_y; + int unit_type; + + if (png_get_oFFs(read_ptr, read_info_ptr,&offset_x,&offset_y,&unit_type)) + { + png_set_oFFs(write_ptr, write_info_ptr, offset_x, offset_y, unit_type); + } + } +#endif +#if defined(PNG_pCAL_SUPPORTED) + { + png_charp purpose, units; + png_charpp params; + png_int_32 X0, X1; + int type, nparams; + + if (png_get_pCAL(read_ptr, read_info_ptr, &purpose, &X0, &X1, &type, + &nparams, &units, ¶ms)) + { + png_set_pCAL(write_ptr, write_info_ptr, purpose, X0, X1, type, + nparams, units, params); + } + } +#endif +#if defined(PNG_pHYs_SUPPORTED) + { + png_uint_32 res_x, res_y; + int unit_type; + + if (png_get_pHYs(read_ptr, read_info_ptr, &res_x, &res_y, &unit_type)) + { + png_set_pHYs(write_ptr, write_info_ptr, res_x, res_y, unit_type); + } + } +#endif +#if defined(PNG_sBIT_SUPPORTED) + { + png_color_8p sig_bit; + + if (png_get_sBIT(read_ptr, read_info_ptr, &sig_bit)) + { + png_set_sBIT(write_ptr, write_info_ptr, sig_bit); + } + } +#endif +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED + { + int unit; + double scal_width, scal_height; + + if (png_get_sCAL(read_ptr, read_info_ptr, &unit, &scal_width, + &scal_height)) + { + png_set_sCAL(write_ptr, write_info_ptr, unit, scal_width, scal_height); + } + } +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + { + int unit; + png_charp scal_width, scal_height; + + if (png_get_sCAL_s(read_ptr, read_info_ptr, &unit, &scal_width, + &scal_height)) + { + png_set_sCAL_s(write_ptr, write_info_ptr, unit, scal_width, scal_height); + } + } +#endif +#endif +#endif +#if defined(PNG_TEXT_SUPPORTED) + { + png_textp text_ptr; + int num_text; + + if (png_get_text(read_ptr, read_info_ptr, &text_ptr, &num_text) > 0) + { + png_debug1(0, "Handling %d iTXt/tEXt/zTXt chunks\n", num_text); + png_set_text(write_ptr, write_info_ptr, text_ptr, num_text); + } + } +#endif +#if defined(PNG_tIME_SUPPORTED) + { + png_timep mod_time; + + if (png_get_tIME(read_ptr, read_info_ptr, &mod_time)) + { + png_set_tIME(write_ptr, write_info_ptr, mod_time); +#if defined(PNG_TIME_RFC1123_SUPPORTED) + /* we have to use png_strcpy instead of "=" because the string + pointed to by png_convert_to_rfc1123() gets free'ed before + we use it */ + png_strcpy(tIME_string,png_convert_to_rfc1123(read_ptr, mod_time)); + tIME_chunk_present++; +#endif /* PNG_TIME_RFC1123_SUPPORTED */ + } + } +#endif +#if defined(PNG_tRNS_SUPPORTED) + { + png_bytep trans; + int num_trans; + png_color_16p trans_values; + + if (png_get_tRNS(read_ptr, read_info_ptr, &trans, &num_trans, + &trans_values)) + { + png_set_tRNS(write_ptr, write_info_ptr, trans, num_trans, + trans_values); + } + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + { + png_unknown_chunkp unknowns; + int num_unknowns = (int)png_get_unknown_chunks(read_ptr, read_info_ptr, + &unknowns); + if (num_unknowns) + { + png_size_t i; + png_set_unknown_chunks(write_ptr, write_info_ptr, unknowns, + num_unknowns); + /* copy the locations from the read_info_ptr. The automatically + generated locations in write_info_ptr are wrong because we + haven't written anything yet */ + for (i = 0; i < (png_size_t)num_unknowns; i++) + png_set_unknown_chunk_location(write_ptr, write_info_ptr, i, + unknowns[i].location); + } + } +#endif + +#ifdef PNG_WRITE_SUPPORTED + png_debug(0, "\nWriting info struct\n"); + +/* If we wanted, we could write info in two steps: + png_write_info_before_PLTE(write_ptr, write_info_ptr); + */ + png_write_info(write_ptr, write_info_ptr); +#endif + +#ifdef SINGLE_ROWBUF_ALLOC + png_debug(0, "\nAllocating row buffer..."); + row_buf = (png_bytep)png_malloc(read_ptr, + png_get_rowbytes(read_ptr, read_info_ptr)); + png_debug1(0, "0x%08lx\n\n", (unsigned long)row_buf); +#endif /* SINGLE_ROWBUF_ALLOC */ + png_debug(0, "Writing row data\n"); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) + num_pass = png_set_interlace_handling(read_ptr); +# ifdef PNG_WRITE_SUPPORTED + png_set_interlace_handling(write_ptr); +# endif +#else + num_pass=1; +#endif + +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_misc += (t_stop - t_start); + t_start = t_stop; +#endif + for (pass = 0; pass < num_pass; pass++) + { + png_debug1(0, "Writing row data for pass %d\n",pass); + for (y = 0; y < height; y++) + { +#ifndef SINGLE_ROWBUF_ALLOC + png_debug2(0, "\nAllocating row buffer (pass %d, y = %ld)...", pass,y); + row_buf = (png_bytep)png_malloc(read_ptr, + png_get_rowbytes(read_ptr, read_info_ptr)); + png_debug2(0, "0x%08lx (%ld bytes)\n", (unsigned long)row_buf, + png_get_rowbytes(read_ptr, read_info_ptr)); +#endif /* !SINGLE_ROWBUF_ALLOC */ + png_read_rows(read_ptr, (png_bytepp)&row_buf, png_bytepp_NULL, 1); + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_decode += (t_stop - t_start); + t_start = t_stop; +#endif + png_write_rows(write_ptr, (png_bytepp)&row_buf, 1); +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_encode += (t_stop - t_start); + t_start = t_stop; +#endif +#endif /* PNG_WRITE_SUPPORTED */ + +#ifndef SINGLE_ROWBUF_ALLOC + png_debug2(0, "Freeing row buffer (pass %d, y = %ld)\n\n", pass, y); + png_free(read_ptr, row_buf); +#endif /* !SINGLE_ROWBUF_ALLOC */ + } + } + +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + png_free_data(read_ptr, read_info_ptr, PNG_FREE_UNKN, -1); +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + png_free_data(write_ptr, write_info_ptr, PNG_FREE_UNKN, -1); +#endif + + png_debug(0, "Reading and writing end_info data\n"); + + png_read_end(read_ptr, end_info_ptr); +#if defined(PNG_TEXT_SUPPORTED) + { + png_textp text_ptr; + int num_text; + + if (png_get_text(read_ptr, end_info_ptr, &text_ptr, &num_text) > 0) + { + png_debug1(0, "Handling %d iTXt/tEXt/zTXt chunks\n", num_text); + png_set_text(write_ptr, write_end_info_ptr, text_ptr, num_text); + } + } +#endif +#if defined(PNG_tIME_SUPPORTED) + { + png_timep mod_time; + + if (png_get_tIME(read_ptr, end_info_ptr, &mod_time)) + { + png_set_tIME(write_ptr, write_end_info_ptr, mod_time); +#if defined(PNG_TIME_RFC1123_SUPPORTED) + /* we have to use png_strcpy instead of "=" because the string + pointed to by png_convert_to_rfc1123() gets free'ed before + we use it */ + png_strcpy(tIME_string,png_convert_to_rfc1123(read_ptr, mod_time)); + tIME_chunk_present++; +#endif /* PNG_TIME_RFC1123_SUPPORTED */ + } + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + { + png_unknown_chunkp unknowns; + int num_unknowns; + num_unknowns = (int)png_get_unknown_chunks(read_ptr, end_info_ptr, + &unknowns); + if (num_unknowns) + { + png_size_t i; + png_set_unknown_chunks(write_ptr, write_end_info_ptr, unknowns, + num_unknowns); + /* copy the locations from the read_info_ptr. The automatically + generated locations in write_end_info_ptr are wrong because we + haven't written the end_info yet */ + for (i = 0; i < (png_size_t)num_unknowns; i++) + png_set_unknown_chunk_location(write_ptr, write_end_info_ptr, i, + unknowns[i].location); + } + } +#endif +#ifdef PNG_WRITE_SUPPORTED + png_write_end(write_ptr, write_end_info_ptr); +#endif + +#ifdef PNG_EASY_ACCESS_SUPPORTED + if(verbose) + { + png_uint_32 iwidth, iheight; + iwidth = png_get_image_width(write_ptr, write_info_ptr); + iheight = png_get_image_height(write_ptr, write_info_ptr); + fprintf(STDERR, "Image width = %lu, height = %lu\n", + iwidth, iheight); + } +#endif + + png_debug(0, "Destroying data structs\n"); +#ifdef SINGLE_ROWBUF_ALLOC + png_debug(1, "destroying row_buf for read_ptr\n"); + png_free(read_ptr, row_buf); + row_buf=NULL; +#endif /* SINGLE_ROWBUF_ALLOC */ + png_debug(1, "destroying read_ptr, read_info_ptr, end_info_ptr\n"); + png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); +#ifdef PNG_WRITE_SUPPORTED + png_debug(1, "destroying write_end_info_ptr\n"); + png_destroy_info_struct(write_ptr, &write_end_info_ptr); + png_debug(1, "destroying write_ptr, write_info_ptr\n"); + png_destroy_write_struct(&write_ptr, &write_info_ptr); +#endif + png_debug(0, "Destruction complete.\n"); + + FCLOSE(fpin); + FCLOSE(fpout); + + png_debug(0, "Opening files for comparison\n"); +#if defined(_WIN32_WCE) + MultiByteToWideChar(CP_ACP, 0, inname, -1, path, MAX_PATH); + if ((fpin = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) +#else + if ((fpin = fopen(inname, "rb")) == NULL) +#endif + { + fprintf(STDERR, "Could not find file %s\n", inname); + return (1); + } + +#if defined(_WIN32_WCE) + MultiByteToWideChar(CP_ACP, 0, outname, -1, path, MAX_PATH); + if ((fpout = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) +#else + if ((fpout = fopen(outname, "rb")) == NULL) +#endif + { + fprintf(STDERR, "Could not find file %s\n", outname); + FCLOSE(fpin); + return (1); + } + + for(;;) + { + png_size_t num_in, num_out; + + READFILE(fpin, inbuf, 1, num_in); + READFILE(fpout, outbuf, 1, num_out); + + if (num_in != num_out) + { + fprintf(STDERR, "\nFiles %s and %s are of a different size\n", + inname, outname); + if(wrote_question == 0) + { + fprintf(STDERR, + " Was %s written with the same maximum IDAT chunk size (%d bytes),", + inname,PNG_ZBUF_SIZE); + fprintf(STDERR, + "\n filtering heuristic (libpng default), compression"); + fprintf(STDERR, + " level (zlib default),\n and zlib version (%s)?\n\n", + ZLIB_VERSION); + wrote_question=1; + } + FCLOSE(fpin); + FCLOSE(fpout); + return (0); + } + + if (!num_in) + break; + + if (png_memcmp(inbuf, outbuf, num_in)) + { + fprintf(STDERR, "\nFiles %s and %s are different\n", inname, outname); + if(wrote_question == 0) + { + fprintf(STDERR, + " Was %s written with the same maximum IDAT chunk size (%d bytes),", + inname,PNG_ZBUF_SIZE); + fprintf(STDERR, + "\n filtering heuristic (libpng default), compression"); + fprintf(STDERR, + " level (zlib default),\n and zlib version (%s)?\n\n", + ZLIB_VERSION); + wrote_question=1; + } + FCLOSE(fpin); + FCLOSE(fpout); + return (0); + } + } + + FCLOSE(fpin); + FCLOSE(fpout); + + return (0); +} + +/* input and output filenames */ +#ifdef RISCOS +static PNG_CONST char *inname = "pngtest/png"; +static PNG_CONST char *outname = "pngout/png"; +#else +static PNG_CONST char *inname = "pngtest.png"; +static PNG_CONST char *outname = "pngout.png"; +#endif + +int +main(int argc, char *argv[]) +{ + int multiple = 0; + int ierror = 0; + + fprintf(STDERR, "Testing libpng version %s\n", PNG_LIBPNG_VER_STRING); + fprintf(STDERR, " with zlib version %s\n", ZLIB_VERSION); + fprintf(STDERR,"%s",png_get_copyright(NULL)); + /* Show the version of libpng used in building the library */ + fprintf(STDERR," library (%lu):%s", png_access_version_number(), + png_get_header_version(NULL)); + /* Show the version of libpng used in building the application */ + fprintf(STDERR," pngtest (%lu):%s", (unsigned long)PNG_LIBPNG_VER, + PNG_HEADER_VERSION_STRING); + fprintf(STDERR," png_sizeof(png_struct)=%ld, png_sizeof(png_info)=%ld\n", + (long)png_sizeof(png_struct), (long)png_sizeof(png_info)); + + /* Do some consistency checking on the memory allocation settings, I'm + not sure this matters, but it is nice to know, the first of these + tests should be impossible because of the way the macros are set + in pngconf.h */ +#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) + fprintf(STDERR, " NOTE: Zlib compiled for max 64k, libpng not\n"); +#endif + /* I think the following can happen. */ +#if !defined(MAXSEG_64K) && defined(PNG_MAX_MALLOC_64K) + fprintf(STDERR, " NOTE: libpng compiled for max 64k, zlib not\n"); +#endif + + if (strcmp(png_libpng_ver, PNG_LIBPNG_VER_STRING)) + { + fprintf(STDERR, + "Warning: versions are different between png.h and png.c\n"); + fprintf(STDERR, " png.h version: %s\n", PNG_LIBPNG_VER_STRING); + fprintf(STDERR, " png.c version: %s\n\n", png_libpng_ver); + ++ierror; + } + + if (argc > 1) + { + if (strcmp(argv[1], "-m") == 0) + { + multiple = 1; + status_dots_requested = 0; + } + else if (strcmp(argv[1], "-mv") == 0 || + strcmp(argv[1], "-vm") == 0 ) + { + multiple = 1; + verbose = 1; + status_dots_requested = 1; + } + else if (strcmp(argv[1], "-v") == 0) + { + verbose = 1; + status_dots_requested = 1; + inname = argv[2]; + } + else + { + inname = argv[1]; + status_dots_requested = 0; + } + } + + if (!multiple && argc == 3+verbose) + outname = argv[2+verbose]; + + if ((!multiple && argc > 3+verbose) || (multiple && argc < 2)) + { + fprintf(STDERR, + "usage: %s [infile.png] [outfile.png]\n\t%s -m {infile.png}\n", + argv[0], argv[0]); + fprintf(STDERR, + " reads/writes one PNG file (without -m) or multiple files (-m)\n"); + fprintf(STDERR, + " with -m %s is used as a temporary file\n", outname); + exit(1); + } + + if (multiple) + { + int i; +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + int allocation_now = current_allocation; +#endif + for (i=2; isize, + (unsigned int) pinfo->pointer); + pinfo = pinfo->next; + } + } +#endif + } +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + fprintf(STDERR, " Current memory allocation: %10d bytes\n", + current_allocation); + fprintf(STDERR, " Maximum memory allocation: %10d bytes\n", + maximum_allocation); + fprintf(STDERR, " Total memory allocation: %10d bytes\n", + total_allocation); + fprintf(STDERR, " Number of allocations: %10d\n", + num_allocations); +#endif + } + else + { + int i; + for (i=0; i<3; ++i) + { + int kerror; +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + int allocation_now = current_allocation; +#endif + if (i == 1) status_dots_requested = 1; + else if(verbose == 0)status_dots_requested = 0; + if (i == 0 || verbose == 1 || ierror != 0) + fprintf(STDERR, "Testing %s:",inname); + kerror = test_one_file(inname, outname); + if(kerror == 0) + { + if(verbose == 1 || i == 2) + { +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + int k; +#endif +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + fprintf(STDERR, "\n PASS (%lu zero samples)\n",zero_samples); +#else + fprintf(STDERR, " PASS\n"); +#endif +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + for (k=0; k<256; k++) + if(filters_used[k]) + fprintf(STDERR, " Filter %d was used %lu times\n", + k,filters_used[k]); +#endif +#if defined(PNG_TIME_RFC1123_SUPPORTED) + if(tIME_chunk_present != 0) + fprintf(STDERR, " tIME = %s\n",tIME_string); +#endif /* PNG_TIME_RFC1123_SUPPORTED */ + } + } + else + { + if(verbose == 0 && i != 2) + fprintf(STDERR, "Testing %s:",inname); + fprintf(STDERR, " FAIL\n"); + ierror += kerror; + } +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + if (allocation_now != current_allocation) + fprintf(STDERR, "MEMORY ERROR: %d bytes lost\n", + current_allocation-allocation_now); + if (current_allocation != 0) + { + memory_infop pinfo = pinformation; + + fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n", + current_allocation); + while (pinfo != NULL) + { + fprintf(STDERR," %lu bytes at %x\n", + pinfo->size, (unsigned int)pinfo->pointer); + pinfo = pinfo->next; + } + } +#endif + } +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + fprintf(STDERR, " Current memory allocation: %10d bytes\n", + current_allocation); + fprintf(STDERR, " Maximum memory allocation: %10d bytes\n", + maximum_allocation); + fprintf(STDERR, " Total memory allocation: %10d bytes\n", + total_allocation); + fprintf(STDERR, " Number of allocations: %10d\n", + num_allocations); +#endif + } + +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_misc += (t_stop - t_start); + t_start = t_stop; + fprintf(STDERR," CPU time used = %.3f seconds", + (t_misc+t_decode+t_encode)/(float)CLOCKS_PER_SEC); + fprintf(STDERR," (decoding %.3f,\n", + t_decode/(float)CLOCKS_PER_SEC); + fprintf(STDERR," encoding %.3f ,", + t_encode/(float)CLOCKS_PER_SEC); + fprintf(STDERR," other %.3f seconds)\n\n", + t_misc/(float)CLOCKS_PER_SEC); +#endif + + if (ierror == 0) + fprintf(STDERR, "libpng passes test\n"); + else + fprintf(STDERR, "libpng FAILS test\n"); + return (int)(ierror != 0); +} + +/* Generate a compiler error if there is an old png.h in the search path. */ +typedef version_1_2_8 your_png_h_is_not_version_1_2_8; diff --git a/com32/lib/libpng/pngtrans.c b/com32/lib/libpng/pngtrans.c new file mode 100644 index 0000000..9003a21 --- /dev/null +++ b/com32/lib/libpng/pngtrans.c @@ -0,0 +1,650 @@ + +/* pngtrans.c - transforms the data in a row (used by both readers and writers) + * + * libpng 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* turn on BGR-to-RGB mapping */ +void PNGAPI +png_set_bgr(png_structp png_ptr) +{ + png_debug(1, "in png_set_bgr\n"); + png_ptr->transformations |= PNG_BGR; +} +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* turn on 16 bit byte swapping */ +void PNGAPI +png_set_swap(png_structp png_ptr) +{ + png_debug(1, "in png_set_swap\n"); + if (png_ptr->bit_depth == 16) + png_ptr->transformations |= PNG_SWAP_BYTES; +} +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* turn on pixel packing */ +void PNGAPI +png_set_packing(png_structp png_ptr) +{ + png_debug(1, "in png_set_packing\n"); + if (png_ptr->bit_depth < 8) + { + png_ptr->transformations |= PNG_PACK; + png_ptr->usr_bit_depth = 8; + } +} +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* turn on packed pixel swapping */ +void PNGAPI +png_set_packswap(png_structp png_ptr) +{ + png_debug(1, "in png_set_packswap\n"); + if (png_ptr->bit_depth < 8) + png_ptr->transformations |= PNG_PACKSWAP; +} +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +void PNGAPI +png_set_shift(png_structp png_ptr, png_color_8p true_bits) +{ + png_debug(1, "in png_set_shift\n"); + png_ptr->transformations |= PNG_SHIFT; + png_ptr->shift = *true_bits; +} +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +int PNGAPI +png_set_interlace_handling(png_structp png_ptr) +{ + png_debug(1, "in png_set_interlace handling\n"); + if (png_ptr->interlaced) + { + png_ptr->transformations |= PNG_INTERLACE; + return (7); + } + + return (1); +} +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte on read, or remove a filler or alpha byte on write. + * The filler type has changed in v0.95 to allow future 2-byte fillers + * for 48-bit input data, as well as to avoid problems with some compilers + * that don't like bytes as parameters. + */ +void PNGAPI +png_set_filler(png_structp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_filler\n"); + png_ptr->transformations |= PNG_FILLER; + png_ptr->filler = (png_byte)filler; + if (filler_loc == PNG_FILLER_AFTER) + png_ptr->flags |= PNG_FLAG_FILLER_AFTER; + else + png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER; + + /* This should probably go in the "do_read_filler" routine. + * I attempted to do that in libpng-1.0.1a but that caused problems + * so I restored it in libpng-1.0.2a + */ + + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + png_ptr->usr_channels = 4; + } + + /* Also I added this in libpng-1.0.2a (what happens when we expand + * a less-than-8-bit grayscale to GA? */ + + if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY && png_ptr->bit_depth >= 8) + { + png_ptr->usr_channels = 2; + } +} + +#if !defined(PNG_1_0_X) +/* Added to libpng-1.2.7 */ +void PNGAPI +png_set_add_alpha(png_structp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_add_alpha\n"); + png_set_filler(png_ptr, filler, filler_loc); + png_ptr->transformations |= PNG_ADD_ALPHA; +} +#endif + +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +void PNGAPI +png_set_swap_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_swap_alpha\n"); + png_ptr->transformations |= PNG_SWAP_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +void PNGAPI +png_set_invert_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_invert_alpha\n"); + png_ptr->transformations |= PNG_INVERT_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +void PNGAPI +png_set_invert_mono(png_structp png_ptr) +{ + png_debug(1, "in png_set_invert_mono\n"); + png_ptr->transformations |= PNG_INVERT_MONO; +} + +/* invert monochrome grayscale data */ +void /* PRIVATE */ +png_do_invert(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_invert\n"); + /* This test removed from libpng version 1.0.13 and 1.2.0: + * if (row_info->bit_depth == 1 && + */ +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row == NULL || row_info == NULL) + return; +#endif + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(~(*rp)); + rp++; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 8) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i+=2) + { + *rp = (png_byte)(~(*rp)); + rp+=2; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 16) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i+=4) + { + *rp = (png_byte)(~(*rp)); + *(rp+1) = (png_byte)(~(*(rp+1))); + rp+=4; + } + } +} +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* swaps byte order on 16 bit depth images */ +void /* PRIVATE */ +png_do_swap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_swap\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->bit_depth == 16) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop= row_info->width * row_info->channels; + + for (i = 0; i < istop; i++, rp += 2) + { + png_byte t = *rp; + *rp = *(rp + 1); + *(rp + 1) = t; + } + } +} +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +static png_byte onebppswaptable[256] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF +}; + +static png_byte twobppswaptable[256] = { + 0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0, + 0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0, + 0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4, + 0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4, + 0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8, + 0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8, + 0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC, + 0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC, + 0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1, + 0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1, + 0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5, + 0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5, + 0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9, + 0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9, + 0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD, + 0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD, + 0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2, + 0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2, + 0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6, + 0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6, + 0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA, + 0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA, + 0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE, + 0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE, + 0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3, + 0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3, + 0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7, + 0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7, + 0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB, + 0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB, + 0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF, + 0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF +}; + +static png_byte fourbppswaptable[256] = { + 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, + 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, + 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, + 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, + 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, + 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2, + 0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, + 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3, + 0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74, + 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4, + 0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, + 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, + 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, + 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, + 0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, + 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, + 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, + 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, + 0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, + 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, + 0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, + 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA, + 0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, + 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB, + 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, + 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC, + 0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, + 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD, + 0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E, + 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE, + 0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, + 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF +}; + +/* swaps pixel packing order within bytes */ +void /* PRIVATE */ +png_do_packswap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_packswap\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->bit_depth < 8) + { + png_bytep rp, end, table; + + end = row + row_info->rowbytes; + + if (row_info->bit_depth == 1) + table = onebppswaptable; + else if (row_info->bit_depth == 2) + table = twobppswaptable; + else if (row_info->bit_depth == 4) + table = fourbppswaptable; + else + return; + + for (rp = row; rp < end; rp++) + *rp = table[*rp]; + } +} +#endif /* PNG_READ_PACKSWAP_SUPPORTED or PNG_WRITE_PACKSWAP_SUPPORTED */ + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +/* remove filler or alpha byte(s) */ +void /* PRIVATE */ +png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags) +{ + png_debug(1, "in png_do_strip_filler\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + png_bytep sp=row; + png_bytep dp=row; + png_uint_32 row_width=row_info->width; + png_uint_32 i; + + if ((row_info->color_type == PNG_COLOR_TYPE_RGB || + (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && + (flags & PNG_FLAG_STRIP_ALPHA))) && + row_info->channels == 4) + { + if (row_info->bit_depth == 8) + { + /* This converts from RGBX or RGBA to RGB */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + dp+=3; sp+=4; + for (i = 1; i < row_width; i++) + { + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + sp++; + } + } + /* This converts from XRGB or ARGB to RGB */ + else + { + for (i = 0; i < row_width; i++) + { + sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 24; + row_info->rowbytes = row_width * 3; + } + else /* if (row_info->bit_depth == 16) */ + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This converts from RRGGBBXX or RRGGBBAA to RRGGBB */ + sp += 8; dp += 6; + for (i = 1; i < row_width; i++) + { + /* This could be (although png_memcpy is probably slower): + png_memcpy(dp, sp, 6); + sp += 8; + dp += 6; + */ + + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + sp += 2; + } + } + else + { + /* This converts from XXRRGGBB or AARRGGBB to RRGGBB */ + for (i = 0; i < row_width; i++) + { + /* This could be (although png_memcpy is probably slower): + png_memcpy(dp, sp, 6); + sp += 8; + dp += 6; + */ + + sp+=2; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 48; + row_info->rowbytes = row_width * 6; + } + row_info->channels = 3; + } + else if ((row_info->color_type == PNG_COLOR_TYPE_GRAY || + (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + (flags & PNG_FLAG_STRIP_ALPHA))) && + row_info->channels == 2) + { + if (row_info->bit_depth == 8) + { + /* This converts from GX or GA to G */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + for (i = 0; i < row_width; i++) + { + *dp++ = *sp++; + sp++; + } + } + /* This converts from XG or AG to G */ + else + { + for (i = 0; i < row_width; i++) + { + sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + else /* if (row_info->bit_depth == 16) */ + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This converts from GGXX or GGAA to GG */ + sp += 4; dp += 2; + for (i = 1; i < row_width; i++) + { + *dp++ = *sp++; + *dp++ = *sp++; + sp += 2; + } + } + else + { + /* This converts from XXGG or AAGG to GG */ + for (i = 0; i < row_width; i++) + { + sp += 2; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + row_info->channels = 1; + } + if (flags & PNG_FLAG_STRIP_ALPHA) + row_info->color_type &= ~PNG_COLOR_MASK_ALPHA; + } +} +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* swaps red and blue bytes within a pixel */ +void /* PRIVATE */ +png_do_bgr(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_bgr\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 3) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 4) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + } + else if (row_info->bit_depth == 16) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 6) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 8) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + } + } +} +#endif /* PNG_READ_BGR_SUPPORTED or PNG_WRITE_BGR_SUPPORTED */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +void PNGAPI +png_set_user_transform_info(png_structp png_ptr, png_voidp + user_transform_ptr, int user_transform_depth, int user_transform_channels) +{ + png_debug(1, "in png_set_user_transform_info\n"); +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + png_ptr->user_transform_ptr = user_transform_ptr; + png_ptr->user_transform_depth = (png_byte)user_transform_depth; + png_ptr->user_transform_channels = (png_byte)user_transform_channels; +#else + if(user_transform_ptr || user_transform_depth || user_transform_channels) + png_warning(png_ptr, + "This version of libpng does not support user transform info"); +#endif +} +#endif + +/* This function returns a pointer to the user_transform_ptr associated with + * the user transform functions. The application should free any memory + * associated with this pointer before png_write_destroy and png_read_destroy + * are called. + */ +png_voidp PNGAPI +png_get_user_transform_ptr(png_structp png_ptr) +{ +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + return ((png_voidp)png_ptr->user_transform_ptr); +#else + if(png_ptr) + return (NULL); + return (NULL); +#endif +} diff --git a/com32/lib/libpng/pngvcrd.c b/com32/lib/libpng/pngvcrd.c new file mode 100644 index 0000000..940a7fc --- /dev/null +++ b/com32/lib/libpng/pngvcrd.c @@ -0,0 +1,3903 @@ +/* pngvcrd.c - mixed C/assembler version of utilities to read a PNG file + * + * For Intel x86 CPU and Microsoft Visual C++ compiler + * + * libpng version 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * Copyright (c) 1998, Intel Corporation + * + * Contributed by Nirav Chhatrapati, Intel Corporation, 1998 + * Interface to libpng contributed by Gilles Vollant, 1999 + * + * + * In png_do_read_interlace() in libpng versions 1.0.3a through 1.0.4d, + * a sign error in the post-MMX cleanup code for each pixel_depth resulted + * in bad pixels at the beginning of some rows of some images, and also + * (due to out-of-range memory reads and writes) caused heap corruption + * when compiled with MSVC 6.0. The error was fixed in version 1.0.4e. + * + * [png_read_filter_row_mmx_avg() bpp == 2 bugfix, GRR 20000916] + * + * [runtime MMX configuration, GRR 20010102] + * + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_USE_PNGVCRD) + +static int mmx_supported=2; + + +int PNGAPI +png_mmx_support(void) +{ + int mmx_supported_local = 0; + _asm { + push ebx //CPUID will trash these + push ecx + push edx + + pushfd //Save Eflag to stack + pop eax //Get Eflag from stack into eax + mov ecx, eax //Make another copy of Eflag in ecx + xor eax, 0x200000 //Toggle ID bit in Eflag [i.e. bit(21)] + push eax //Save modified Eflag back to stack + + popfd //Restored modified value back to Eflag reg + pushfd //Save Eflag to stack + pop eax //Get Eflag from stack + push ecx // save original Eflag to stack + popfd // restore original Eflag + xor eax, ecx //Compare the new Eflag with the original Eflag + jz NOT_SUPPORTED //If the same, CPUID instruction is not supported, + //skip following instructions and jump to + //NOT_SUPPORTED label + + xor eax, eax //Set eax to zero + + _asm _emit 0x0f //CPUID instruction (two bytes opcode) + _asm _emit 0xa2 + + cmp eax, 1 //make sure eax return non-zero value + jl NOT_SUPPORTED //If eax is zero, mmx not supported + + xor eax, eax //set eax to zero + inc eax //Now increment eax to 1. This instruction is + //faster than the instruction "mov eax, 1" + + _asm _emit 0x0f //CPUID instruction + _asm _emit 0xa2 + + and edx, 0x00800000 //mask out all bits but mmx bit(24) + cmp edx, 0 // 0 = mmx not supported + jz NOT_SUPPORTED // non-zero = Yes, mmx IS supported + + mov mmx_supported_local, 1 //set return value to 1 + +NOT_SUPPORTED: + mov eax, mmx_supported_local //move return value to eax + pop edx //CPUID trashed these + pop ecx + pop ebx + } + + //mmx_supported_local=0; // test code for force don't support MMX + //printf("MMX : %u (1=MMX supported)\n",mmx_supported_local); + + mmx_supported = mmx_supported_local; + return mmx_supported_local; +} + +/* Combines the row recently read in with the previous row. + This routine takes care of alpha and transparency if requested. + This routine also handles the two methods of progressive display + of interlaced images, depending on the mask value. + The mask value describes which pixels are to be combined with + the row. The pattern always repeats every 8 pixels, so just 8 + bits are needed. A one indicates the pixel is to be combined; a + zero indicates the pixel is to be skipped. This is in addition + to any alpha or transparency value associated with the pixel. If + you want all pixels to be combined, pass 0xff (255) in mask. */ + +/* Use this routine for x86 platform - uses faster MMX routine if machine + supports MMX */ + +void /* PRIVATE */ +png_combine_row(png_structp png_ptr, png_bytep row, int mask) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + const int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; +#endif + + png_debug(1,"in png_combine_row_asm\n"); + + if (mmx_supported == 2) { +#if !defined(PNG_1_0_X) + /* this should have happened in png_init_mmx_flags() already */ + png_warning(png_ptr, "asm_flags may not have been initialized"); +#endif + png_mmx_support(); + } + + if (mask == 0xff) + { + png_memcpy(row, png_ptr->row_buf + 1, + (png_size_t)PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->width)); + } + /* GRR: add "else if (mask == 0)" case? + * or does png_combine_row() not even get called in that case? */ + else + { + switch (png_ptr->row_info.pixel_depth) + { + case 1: + { + png_bytep sp; + png_bytep dp; + int s_inc, s_start, s_end; + int m; + int shift; + png_uint_32 i; + + sp = png_ptr->row_buf + 1; + dp = row; + m = 0x80; +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 7; + s_inc = 1; + } + else +#endif + { + s_start = 7; + s_end = 0; + s_inc = -1; + } + + shift = s_start; + + for (i = 0; i < png_ptr->width; i++) + { + if (m & mask) + { + int value; + + value = (*sp >> shift) & 0x1; + *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + + case 2: + { + png_bytep sp; + png_bytep dp; + int s_start, s_end, s_inc; + int m; + int shift; + png_uint_32 i; + int value; + + sp = png_ptr->row_buf + 1; + dp = row; + m = 0x80; +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 6; + s_inc = 2; + } + else +#endif + { + s_start = 6; + s_end = 0; + s_inc = -2; + } + + shift = s_start; + + for (i = 0; i < png_ptr->width; i++) + { + if (m & mask) + { + value = (*sp >> shift) & 0x3; + *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + + case 4: + { + png_bytep sp; + png_bytep dp; + int s_start, s_end, s_inc; + int m; + int shift; + png_uint_32 i; + int value; + + sp = png_ptr->row_buf + 1; + dp = row; + m = 0x80; +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 4; + s_inc = 4; + } + else +#endif + { + s_start = 4; + s_end = 0; + s_inc = -4; + } + shift = s_start; + + for (i = 0; i < png_ptr->width; i++) + { + if (m & mask) + { + value = (*sp >> shift) & 0xf; + *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + + case 8: + { + png_bytep srcptr; + png_bytep dstptr; + png_uint_32 len; + int m; + int diff, unmask; + + __int64 mask0=0x0102040810204080; + +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) + /* && mmx_supported */ ) +#else + if (mmx_supported) +#endif + { + srcptr = png_ptr->row_buf + 1; + dstptr = row; + m = 0x80; + unmask = ~mask; + len = png_ptr->width &~7; //reduce to multiple of 8 + diff = png_ptr->width & 7; //amount lost + + _asm + { + movd mm7, unmask //load bit pattern + psubb mm6,mm6 //zero mm6 + punpcklbw mm7,mm7 + punpcklwd mm7,mm7 + punpckldq mm7,mm7 //fill register with 8 masks + + movq mm0,mask0 + + pand mm0,mm7 //nonzero if keep byte + pcmpeqb mm0,mm6 //zeros->1s, v versa + + mov ecx,len //load length of line (pixels) + mov esi,srcptr //load source + mov ebx,dstptr //load dest + cmp ecx,0 //lcr + je mainloop8end + +mainloop8: + movq mm4,[esi] + pand mm4,mm0 + movq mm6,mm0 + pandn mm6,[ebx] + por mm4,mm6 + movq [ebx],mm4 + + add esi,8 //inc by 8 bytes processed + add ebx,8 + sub ecx,8 //dec by 8 pixels processed + + ja mainloop8 +mainloop8end: + + mov ecx,diff + cmp ecx,0 + jz end8 + + mov edx,mask + sal edx,24 //make low byte the high byte + +secondloop8: + sal edx,1 //move high bit to CF + jnc skip8 //if CF = 0 + mov al,[esi] + mov [ebx],al +skip8: + inc esi + inc ebx + + dec ecx + jnz secondloop8 +end8: + emms + } + } + else /* mmx not supported - use modified C routine */ + { + register unsigned int incr1, initial_val, final_val; + png_size_t pixel_bytes; + png_uint_32 i; + register int disp = png_pass_inc[png_ptr->pass]; + int offset_table[7] = {0, 4, 0, 2, 0, 1, 0}; + + pixel_bytes = (png_ptr->row_info.pixel_depth >> 3); + srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]* + pixel_bytes; + dstptr = row + offset_table[png_ptr->pass]*pixel_bytes; + initial_val = offset_table[png_ptr->pass]*pixel_bytes; + final_val = png_ptr->width*pixel_bytes; + incr1 = (disp)*pixel_bytes; + for (i = initial_val; i < final_val; i += incr1) + { + png_memcpy(dstptr, srcptr, pixel_bytes); + srcptr += incr1; + dstptr += incr1; + } + } /* end of else */ + + break; + } // end 8 bpp + + case 16: + { + png_bytep srcptr; + png_bytep dstptr; + png_uint_32 len; + int unmask, diff; + __int64 mask1=0x0101020204040808, + mask0=0x1010202040408080; + +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) + /* && mmx_supported */ ) +#else + if (mmx_supported) +#endif + { + srcptr = png_ptr->row_buf + 1; + dstptr = row; + + unmask = ~mask; + len = (png_ptr->width)&~7; + diff = (png_ptr->width)&7; + _asm + { + movd mm7, unmask //load bit pattern + psubb mm6,mm6 //zero mm6 + punpcklbw mm7,mm7 + punpcklwd mm7,mm7 + punpckldq mm7,mm7 //fill register with 8 masks + + movq mm0,mask0 + movq mm1,mask1 + + pand mm0,mm7 + pand mm1,mm7 + + pcmpeqb mm0,mm6 + pcmpeqb mm1,mm6 + + mov ecx,len //load length of line + mov esi,srcptr //load source + mov ebx,dstptr //load dest + cmp ecx,0 //lcr + jz mainloop16end + +mainloop16: + movq mm4,[esi] + pand mm4,mm0 + movq mm6,mm0 + movq mm7,[ebx] + pandn mm6,mm7 + por mm4,mm6 + movq [ebx],mm4 + + movq mm5,[esi+8] + pand mm5,mm1 + movq mm7,mm1 + movq mm6,[ebx+8] + pandn mm7,mm6 + por mm5,mm7 + movq [ebx+8],mm5 + + add esi,16 //inc by 16 bytes processed + add ebx,16 + sub ecx,8 //dec by 8 pixels processed + + ja mainloop16 + +mainloop16end: + mov ecx,diff + cmp ecx,0 + jz end16 + + mov edx,mask + sal edx,24 //make low byte the high byte +secondloop16: + sal edx,1 //move high bit to CF + jnc skip16 //if CF = 0 + mov ax,[esi] + mov [ebx],ax +skip16: + add esi,2 + add ebx,2 + + dec ecx + jnz secondloop16 +end16: + emms + } + } + else /* mmx not supported - use modified C routine */ + { + register unsigned int incr1, initial_val, final_val; + png_size_t pixel_bytes; + png_uint_32 i; + register int disp = png_pass_inc[png_ptr->pass]; + int offset_table[7] = {0, 4, 0, 2, 0, 1, 0}; + + pixel_bytes = (png_ptr->row_info.pixel_depth >> 3); + srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]* + pixel_bytes; + dstptr = row + offset_table[png_ptr->pass]*pixel_bytes; + initial_val = offset_table[png_ptr->pass]*pixel_bytes; + final_val = png_ptr->width*pixel_bytes; + incr1 = (disp)*pixel_bytes; + for (i = initial_val; i < final_val; i += incr1) + { + png_memcpy(dstptr, srcptr, pixel_bytes); + srcptr += incr1; + dstptr += incr1; + } + } /* end of else */ + + break; + } // end 16 bpp + + case 24: + { + png_bytep srcptr; + png_bytep dstptr; + png_uint_32 len; + int unmask, diff; + + __int64 mask2=0x0101010202020404, //24bpp + mask1=0x0408080810101020, + mask0=0x2020404040808080; + + srcptr = png_ptr->row_buf + 1; + dstptr = row; + + unmask = ~mask; + len = (png_ptr->width)&~7; + diff = (png_ptr->width)&7; + +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) + /* && mmx_supported */ ) +#else + if (mmx_supported) +#endif + { + _asm + { + movd mm7, unmask //load bit pattern + psubb mm6,mm6 //zero mm6 + punpcklbw mm7,mm7 + punpcklwd mm7,mm7 + punpckldq mm7,mm7 //fill register with 8 masks + + movq mm0,mask0 + movq mm1,mask1 + movq mm2,mask2 + + pand mm0,mm7 + pand mm1,mm7 + pand mm2,mm7 + + pcmpeqb mm0,mm6 + pcmpeqb mm1,mm6 + pcmpeqb mm2,mm6 + + mov ecx,len //load length of line + mov esi,srcptr //load source + mov ebx,dstptr //load dest + cmp ecx,0 + jz mainloop24end + +mainloop24: + movq mm4,[esi] + pand mm4,mm0 + movq mm6,mm0 + movq mm7,[ebx] + pandn mm6,mm7 + por mm4,mm6 + movq [ebx],mm4 + + + movq mm5,[esi+8] + pand mm5,mm1 + movq mm7,mm1 + movq mm6,[ebx+8] + pandn mm7,mm6 + por mm5,mm7 + movq [ebx+8],mm5 + + movq mm6,[esi+16] + pand mm6,mm2 + movq mm4,mm2 + movq mm7,[ebx+16] + pandn mm4,mm7 + por mm6,mm4 + movq [ebx+16],mm6 + + add esi,24 //inc by 24 bytes processed + add ebx,24 + sub ecx,8 //dec by 8 pixels processed + + ja mainloop24 + +mainloop24end: + mov ecx,diff + cmp ecx,0 + jz end24 + + mov edx,mask + sal edx,24 //make low byte the high byte +secondloop24: + sal edx,1 //move high bit to CF + jnc skip24 //if CF = 0 + mov ax,[esi] + mov [ebx],ax + xor eax,eax + mov al,[esi+2] + mov [ebx+2],al +skip24: + add esi,3 + add ebx,3 + + dec ecx + jnz secondloop24 + +end24: + emms + } + } + else /* mmx not supported - use modified C routine */ + { + register unsigned int incr1, initial_val, final_val; + png_size_t pixel_bytes; + png_uint_32 i; + register int disp = png_pass_inc[png_ptr->pass]; + int offset_table[7] = {0, 4, 0, 2, 0, 1, 0}; + + pixel_bytes = (png_ptr->row_info.pixel_depth >> 3); + srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]* + pixel_bytes; + dstptr = row + offset_table[png_ptr->pass]*pixel_bytes; + initial_val = offset_table[png_ptr->pass]*pixel_bytes; + final_val = png_ptr->width*pixel_bytes; + incr1 = (disp)*pixel_bytes; + for (i = initial_val; i < final_val; i += incr1) + { + png_memcpy(dstptr, srcptr, pixel_bytes); + srcptr += incr1; + dstptr += incr1; + } + } /* end of else */ + + break; + } // end 24 bpp + + case 32: + { + png_bytep srcptr; + png_bytep dstptr; + png_uint_32 len; + int unmask, diff; + + __int64 mask3=0x0101010102020202, //32bpp + mask2=0x0404040408080808, + mask1=0x1010101020202020, + mask0=0x4040404080808080; + + srcptr = png_ptr->row_buf + 1; + dstptr = row; + + unmask = ~mask; + len = (png_ptr->width)&~7; + diff = (png_ptr->width)&7; + +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) + /* && mmx_supported */ ) +#else + if (mmx_supported) +#endif + { + _asm + { + movd mm7, unmask //load bit pattern + psubb mm6,mm6 //zero mm6 + punpcklbw mm7,mm7 + punpcklwd mm7,mm7 + punpckldq mm7,mm7 //fill register with 8 masks + + movq mm0,mask0 + movq mm1,mask1 + movq mm2,mask2 + movq mm3,mask3 + + pand mm0,mm7 + pand mm1,mm7 + pand mm2,mm7 + pand mm3,mm7 + + pcmpeqb mm0,mm6 + pcmpeqb mm1,mm6 + pcmpeqb mm2,mm6 + pcmpeqb mm3,mm6 + + mov ecx,len //load length of line + mov esi,srcptr //load source + mov ebx,dstptr //load dest + + cmp ecx,0 //lcr + jz mainloop32end + +mainloop32: + movq mm4,[esi] + pand mm4,mm0 + movq mm6,mm0 + movq mm7,[ebx] + pandn mm6,mm7 + por mm4,mm6 + movq [ebx],mm4 + + movq mm5,[esi+8] + pand mm5,mm1 + movq mm7,mm1 + movq mm6,[ebx+8] + pandn mm7,mm6 + por mm5,mm7 + movq [ebx+8],mm5 + + movq mm6,[esi+16] + pand mm6,mm2 + movq mm4,mm2 + movq mm7,[ebx+16] + pandn mm4,mm7 + por mm6,mm4 + movq [ebx+16],mm6 + + movq mm7,[esi+24] + pand mm7,mm3 + movq mm5,mm3 + movq mm4,[ebx+24] + pandn mm5,mm4 + por mm7,mm5 + movq [ebx+24],mm7 + + add esi,32 //inc by 32 bytes processed + add ebx,32 + sub ecx,8 //dec by 8 pixels processed + + ja mainloop32 + +mainloop32end: + mov ecx,diff + cmp ecx,0 + jz end32 + + mov edx,mask + sal edx,24 //make low byte the high byte +secondloop32: + sal edx,1 //move high bit to CF + jnc skip32 //if CF = 0 + mov eax,[esi] + mov [ebx],eax +skip32: + add esi,4 + add ebx,4 + + dec ecx + jnz secondloop32 + +end32: + emms + } + } + else /* mmx _not supported - Use modified C routine */ + { + register unsigned int incr1, initial_val, final_val; + png_size_t pixel_bytes; + png_uint_32 i; + register int disp = png_pass_inc[png_ptr->pass]; + int offset_table[7] = {0, 4, 0, 2, 0, 1, 0}; + + pixel_bytes = (png_ptr->row_info.pixel_depth >> 3); + srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]* + pixel_bytes; + dstptr = row + offset_table[png_ptr->pass]*pixel_bytes; + initial_val = offset_table[png_ptr->pass]*pixel_bytes; + final_val = png_ptr->width*pixel_bytes; + incr1 = (disp)*pixel_bytes; + for (i = initial_val; i < final_val; i += incr1) + { + png_memcpy(dstptr, srcptr, pixel_bytes); + srcptr += incr1; + dstptr += incr1; + } + } /* end of else */ + + break; + } // end 32 bpp + + case 48: + { + png_bytep srcptr; + png_bytep dstptr; + png_uint_32 len; + int unmask, diff; + + __int64 mask5=0x0101010101010202, + mask4=0x0202020204040404, + mask3=0x0404080808080808, + mask2=0x1010101010102020, + mask1=0x2020202040404040, + mask0=0x4040808080808080; + +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) + /* && mmx_supported */ ) +#else + if (mmx_supported) +#endif + { + srcptr = png_ptr->row_buf + 1; + dstptr = row; + + unmask = ~mask; + len = (png_ptr->width)&~7; + diff = (png_ptr->width)&7; + _asm + { + movd mm7, unmask //load bit pattern + psubb mm6,mm6 //zero mm6 + punpcklbw mm7,mm7 + punpcklwd mm7,mm7 + punpckldq mm7,mm7 //fill register with 8 masks + + movq mm0,mask0 + movq mm1,mask1 + movq mm2,mask2 + movq mm3,mask3 + movq mm4,mask4 + movq mm5,mask5 + + pand mm0,mm7 + pand mm1,mm7 + pand mm2,mm7 + pand mm3,mm7 + pand mm4,mm7 + pand mm5,mm7 + + pcmpeqb mm0,mm6 + pcmpeqb mm1,mm6 + pcmpeqb mm2,mm6 + pcmpeqb mm3,mm6 + pcmpeqb mm4,mm6 + pcmpeqb mm5,mm6 + + mov ecx,len //load length of line + mov esi,srcptr //load source + mov ebx,dstptr //load dest + + cmp ecx,0 + jz mainloop48end + +mainloop48: + movq mm7,[esi] + pand mm7,mm0 + movq mm6,mm0 + pandn mm6,[ebx] + por mm7,mm6 + movq [ebx],mm7 + + movq mm6,[esi+8] + pand mm6,mm1 + movq mm7,mm1 + pandn mm7,[ebx+8] + por mm6,mm7 + movq [ebx+8],mm6 + + movq mm6,[esi+16] + pand mm6,mm2 + movq mm7,mm2 + pandn mm7,[ebx+16] + por mm6,mm7 + movq [ebx+16],mm6 + + movq mm7,[esi+24] + pand mm7,mm3 + movq mm6,mm3 + pandn mm6,[ebx+24] + por mm7,mm6 + movq [ebx+24],mm7 + + movq mm6,[esi+32] + pand mm6,mm4 + movq mm7,mm4 + pandn mm7,[ebx+32] + por mm6,mm7 + movq [ebx+32],mm6 + + movq mm7,[esi+40] + pand mm7,mm5 + movq mm6,mm5 + pandn mm6,[ebx+40] + por mm7,mm6 + movq [ebx+40],mm7 + + add esi,48 //inc by 32 bytes processed + add ebx,48 + sub ecx,8 //dec by 8 pixels processed + + ja mainloop48 +mainloop48end: + + mov ecx,diff + cmp ecx,0 + jz end48 + + mov edx,mask + sal edx,24 //make low byte the high byte + +secondloop48: + sal edx,1 //move high bit to CF + jnc skip48 //if CF = 0 + mov eax,[esi] + mov [ebx],eax +skip48: + add esi,4 + add ebx,4 + + dec ecx + jnz secondloop48 + +end48: + emms + } + } + else /* mmx _not supported - Use modified C routine */ + { + register unsigned int incr1, initial_val, final_val; + png_size_t pixel_bytes; + png_uint_32 i; + register int disp = png_pass_inc[png_ptr->pass]; + int offset_table[7] = {0, 4, 0, 2, 0, 1, 0}; + + pixel_bytes = (png_ptr->row_info.pixel_depth >> 3); + srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]* + pixel_bytes; + dstptr = row + offset_table[png_ptr->pass]*pixel_bytes; + initial_val = offset_table[png_ptr->pass]*pixel_bytes; + final_val = png_ptr->width*pixel_bytes; + incr1 = (disp)*pixel_bytes; + for (i = initial_val; i < final_val; i += incr1) + { + png_memcpy(dstptr, srcptr, pixel_bytes); + srcptr += incr1; + dstptr += incr1; + } + } /* end of else */ + + break; + } // end 48 bpp + + default: + { + png_bytep sptr; + png_bytep dp; + png_size_t pixel_bytes; + int offset_table[7] = {0, 4, 0, 2, 0, 1, 0}; + unsigned int i; + register int disp = png_pass_inc[png_ptr->pass]; // get the offset + register unsigned int incr1, initial_val, final_val; + + pixel_bytes = (png_ptr->row_info.pixel_depth >> 3); + sptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]* + pixel_bytes; + dp = row + offset_table[png_ptr->pass]*pixel_bytes; + initial_val = offset_table[png_ptr->pass]*pixel_bytes; + final_val = png_ptr->width*pixel_bytes; + incr1 = (disp)*pixel_bytes; + for (i = initial_val; i < final_val; i += incr1) + { + png_memcpy(dp, sptr, pixel_bytes); + sptr += incr1; + dp += incr1; + } + break; + } + } /* end switch (png_ptr->row_info.pixel_depth) */ + } /* end if (non-trivial mask) */ + +} /* end png_combine_row() */ + + +#if defined(PNG_READ_INTERLACING_SUPPORTED) + +void /* PRIVATE */ +png_do_read_interlace(png_structp png_ptr) +{ + png_row_infop row_info = &(png_ptr->row_info); + png_bytep row = png_ptr->row_buf + 1; + int pass = png_ptr->pass; + png_uint_32 transformations = png_ptr->transformations; +#ifdef PNG_USE_LOCAL_ARRAYS + const int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; +#endif + + png_debug(1,"in png_do_read_interlace\n"); + + if (mmx_supported == 2) { +#if !defined(PNG_1_0_X) + /* this should have happened in png_init_mmx_flags() already */ + png_warning(png_ptr, "asm_flags may not have been initialized"); +#endif + png_mmx_support(); + } + + if (row != NULL && row_info != NULL) + { + png_uint_32 final_width; + + final_width = row_info->width * png_pass_inc[pass]; + + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp, dp; + int sshift, dshift; + int s_start, s_end, s_inc; + png_byte v; + png_uint_32 i; + int j; + + sp = row + (png_size_t)((row_info->width - 1) >> 3); + dp = row + (png_size_t)((final_width - 1) >> 3); +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (int)((row_info->width + 7) & 7); + dshift = (int)((final_width + 7) & 7); + s_start = 7; + s_end = 0; + s_inc = -1; + } + else +#endif + { + sshift = 7 - (int)((row_info->width + 7) & 7); + dshift = 7 - (int)((final_width + 7) & 7); + s_start = 0; + s_end = 7; + s_inc = 1; + } + + for (i = row_info->width; i; i--) + { + v = (png_byte)((*sp >> sshift) & 0x1); + for (j = 0; j < png_pass_inc[pass]; j++) + { + *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + + case 2: + { + png_bytep sp, dp; + int sshift, dshift; + int s_start, s_end, s_inc; + png_uint_32 i; + + sp = row + (png_size_t)((row_info->width - 1) >> 2); + dp = row + (png_size_t)((final_width - 1) >> 2); +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (png_size_t)(((row_info->width + 3) & 3) << 1); + dshift = (png_size_t)(((final_width + 3) & 3) << 1); + s_start = 6; + s_end = 0; + s_inc = -2; + } + else +#endif + { + sshift = (png_size_t)((3 - ((row_info->width + 3) & 3)) << 1); + dshift = (png_size_t)((3 - ((final_width + 3) & 3)) << 1); + s_start = 0; + s_end = 6; + s_inc = 2; + } + + for (i = row_info->width; i; i--) + { + png_byte v; + int j; + + v = (png_byte)((*sp >> sshift) & 0x3); + for (j = 0; j < png_pass_inc[pass]; j++) + { + *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + + case 4: + { + png_bytep sp, dp; + int sshift, dshift; + int s_start, s_end, s_inc; + png_uint_32 i; + + sp = row + (png_size_t)((row_info->width - 1) >> 1); + dp = row + (png_size_t)((final_width - 1) >> 1); +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (png_size_t)(((row_info->width + 1) & 1) << 2); + dshift = (png_size_t)(((final_width + 1) & 1) << 2); + s_start = 4; + s_end = 0; + s_inc = -4; + } + else +#endif + { + sshift = (png_size_t)((1 - ((row_info->width + 1) & 1)) << 2); + dshift = (png_size_t)((1 - ((final_width + 1) & 1)) << 2); + s_start = 0; + s_end = 4; + s_inc = 4; + } + + for (i = row_info->width; i; i--) + { + png_byte v; + int j; + + v = (png_byte)((*sp >> sshift) & 0xf); + for (j = 0; j < png_pass_inc[pass]; j++) + { + *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + + default: // This is the place where the routine is modified + { + __int64 const4 = 0x0000000000FFFFFF; + // __int64 const5 = 0x000000FFFFFF0000; // unused... + __int64 const6 = 0x00000000000000FF; + png_bytep sptr, dp; + png_uint_32 i; + png_size_t pixel_bytes; + int width = row_info->width; + + pixel_bytes = (row_info->pixel_depth >> 3); + + sptr = row + (width - 1) * pixel_bytes; + dp = row + (final_width - 1) * pixel_bytes; + // New code by Nirav Chhatrapati - Intel Corporation + // sign fix by GRR + // NOTE: there is NO MMX code for 48-bit and 64-bit images + + // use MMX routine if machine supports it +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_INTERLACE) + /* && mmx_supported */ ) +#else + if (mmx_supported) +#endif + { + if (pixel_bytes == 3) + { + if (((pass == 0) || (pass == 1)) && width) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width + sub edi, 21 // (png_pass_inc[pass] - 1)*pixel_bytes +loop_pass0: + movd mm0, [esi] ; X X X X X v2 v1 v0 + pand mm0, const4 ; 0 0 0 0 0 v2 v1 v0 + movq mm1, mm0 ; 0 0 0 0 0 v2 v1 v0 + psllq mm0, 16 ; 0 0 0 v2 v1 v0 0 0 + movq mm2, mm0 ; 0 0 0 v2 v1 v0 0 0 + psllq mm0, 24 ; v2 v1 v0 0 0 0 0 0 + psrlq mm1, 8 ; 0 0 0 0 0 0 v2 v1 + por mm0, mm2 ; v2 v1 v0 v2 v1 v0 0 0 + por mm0, mm1 ; v2 v1 v0 v2 v1 v0 v2 v1 + movq mm3, mm0 ; v2 v1 v0 v2 v1 v0 v2 v1 + psllq mm0, 16 ; v0 v2 v1 v0 v2 v1 0 0 + movq mm4, mm3 ; v2 v1 v0 v2 v1 v0 v2 v1 + punpckhdq mm3, mm0 ; v0 v2 v1 v0 v2 v1 v0 v2 + movq [edi+16] , mm4 + psrlq mm0, 32 ; 0 0 0 0 v0 v2 v1 v0 + movq [edi+8] , mm3 + punpckldq mm0, mm4 ; v1 v0 v2 v1 v0 v2 v1 v0 + sub esi, 3 + movq [edi], mm0 + sub edi, 24 + //sub esi, 3 + dec ecx + jnz loop_pass0 + EMMS + } + } + else if (((pass == 2) || (pass == 3)) && width) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width + sub edi, 9 // (png_pass_inc[pass] - 1)*pixel_bytes +loop_pass2: + movd mm0, [esi] ; X X X X X v2 v1 v0 + pand mm0, const4 ; 0 0 0 0 0 v2 v1 v0 + movq mm1, mm0 ; 0 0 0 0 0 v2 v1 v0 + psllq mm0, 16 ; 0 0 0 v2 v1 v0 0 0 + movq mm2, mm0 ; 0 0 0 v2 v1 v0 0 0 + psllq mm0, 24 ; v2 v1 v0 0 0 0 0 0 + psrlq mm1, 8 ; 0 0 0 0 0 0 v2 v1 + por mm0, mm2 ; v2 v1 v0 v2 v1 v0 0 0 + por mm0, mm1 ; v2 v1 v0 v2 v1 v0 v2 v1 + movq [edi+4], mm0 ; move to memory + psrlq mm0, 16 ; 0 0 v2 v1 v0 v2 v1 v0 + movd [edi], mm0 ; move to memory + sub esi, 3 + sub edi, 12 + dec ecx + jnz loop_pass2 + EMMS + } + } + else if (width) /* && ((pass == 4) || (pass == 5)) */ + { + int width_mmx = ((width >> 1) << 1) - 8; + if (width_mmx < 0) + width_mmx = 0; + width -= width_mmx; // 8 or 9 pix, 24 or 27 bytes + if (width_mmx) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width_mmx + sub esi, 3 + sub edi, 9 +loop_pass4: + movq mm0, [esi] ; X X v2 v1 v0 v5 v4 v3 + movq mm7, mm0 ; X X v2 v1 v0 v5 v4 v3 + movq mm6, mm0 ; X X v2 v1 v0 v5 v4 v3 + psllq mm0, 24 ; v1 v0 v5 v4 v3 0 0 0 + pand mm7, const4 ; 0 0 0 0 0 v5 v4 v3 + psrlq mm6, 24 ; 0 0 0 X X v2 v1 v0 + por mm0, mm7 ; v1 v0 v5 v4 v3 v5 v4 v3 + movq mm5, mm6 ; 0 0 0 X X v2 v1 v0 + psllq mm6, 8 ; 0 0 X X v2 v1 v0 0 + movq [edi], mm0 ; move quad to memory + psrlq mm5, 16 ; 0 0 0 0 0 X X v2 + pand mm5, const6 ; 0 0 0 0 0 0 0 v2 + por mm6, mm5 ; 0 0 X X v2 v1 v0 v2 + movd [edi+8], mm6 ; move double to memory + sub esi, 6 + sub edi, 12 + sub ecx, 2 + jnz loop_pass4 + EMMS + } + } + + sptr -= width_mmx*3; + dp -= width_mmx*6; + for (i = width; i; i--) + { + png_byte v[8]; + int j; + + png_memcpy(v, sptr, 3); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, 3); + dp -= 3; + } + sptr -= 3; + } + } + } /* end of pixel_bytes == 3 */ + + else if (pixel_bytes == 1) + { + if (((pass == 0) || (pass == 1)) && width) + { + int width_mmx = ((width >> 2) << 2); + width -= width_mmx; + if (width_mmx) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width_mmx + sub edi, 31 + sub esi, 3 +loop1_pass0: + movd mm0, [esi] ; X X X X v0 v1 v2 v3 + movq mm1, mm0 ; X X X X v0 v1 v2 v3 + punpcklbw mm0, mm0 ; v0 v0 v1 v1 v2 v2 v3 v3 + movq mm2, mm0 ; v0 v0 v1 v1 v2 v2 v3 v3 + punpcklwd mm0, mm0 ; v2 v2 v2 v2 v3 v3 v3 v3 + movq mm3, mm0 ; v2 v2 v2 v2 v3 v3 v3 v3 + punpckldq mm0, mm0 ; v3 v3 v3 v3 v3 v3 v3 v3 + punpckhdq mm3, mm3 ; v2 v2 v2 v2 v2 v2 v2 v2 + movq [edi], mm0 ; move to memory v3 + punpckhwd mm2, mm2 ; v0 v0 v0 v0 v1 v1 v1 v1 + movq [edi+8], mm3 ; move to memory v2 + movq mm4, mm2 ; v0 v0 v0 v0 v1 v1 v1 v1 + punpckldq mm2, mm2 ; v1 v1 v1 v1 v1 v1 v1 v1 + punpckhdq mm4, mm4 ; v0 v0 v0 v0 v0 v0 v0 v0 + movq [edi+16], mm2 ; move to memory v1 + movq [edi+24], mm4 ; move to memory v0 + sub esi, 4 + sub edi, 32 + sub ecx, 4 + jnz loop1_pass0 + EMMS + } + } + + sptr -= width_mmx; + dp -= width_mmx*8; + for (i = width; i; i--) + { + int j; + + /* I simplified this part in version 1.0.4e + * here and in several other instances where + * pixel_bytes == 1 -- GR-P + * + * Original code: + * + * png_byte v[8]; + * png_memcpy(v, sptr, pixel_bytes); + * for (j = 0; j < png_pass_inc[pass]; j++) + * { + * png_memcpy(dp, v, pixel_bytes); + * dp -= pixel_bytes; + * } + * sptr -= pixel_bytes; + * + * Replacement code is in the next three lines: + */ + + for (j = 0; j < png_pass_inc[pass]; j++) + *dp-- = *sptr; + sptr--; + } + } + else if (((pass == 2) || (pass == 3)) && width) + { + int width_mmx = ((width >> 2) << 2); + width -= width_mmx; + if (width_mmx) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width_mmx + sub edi, 15 + sub esi, 3 +loop1_pass2: + movd mm0, [esi] ; X X X X v0 v1 v2 v3 + punpcklbw mm0, mm0 ; v0 v0 v1 v1 v2 v2 v3 v3 + movq mm1, mm0 ; v0 v0 v1 v1 v2 v2 v3 v3 + punpcklwd mm0, mm0 ; v2 v2 v2 v2 v3 v3 v3 v3 + punpckhwd mm1, mm1 ; v0 v0 v0 v0 v1 v1 v1 v1 + movq [edi], mm0 ; move to memory v2 and v3 + sub esi, 4 + movq [edi+8], mm1 ; move to memory v1 and v0 + sub edi, 16 + sub ecx, 4 + jnz loop1_pass2 + EMMS + } + } + + sptr -= width_mmx; + dp -= width_mmx*4; + for (i = width; i; i--) + { + int j; + + for (j = 0; j < png_pass_inc[pass]; j++) + { + *dp-- = *sptr; + } + sptr --; + } + } + else if (width) /* && ((pass == 4) || (pass == 5))) */ + { + int width_mmx = ((width >> 3) << 3); + width -= width_mmx; + if (width_mmx) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width_mmx + sub edi, 15 + sub esi, 7 +loop1_pass4: + movq mm0, [esi] ; v0 v1 v2 v3 v4 v5 v6 v7 + movq mm1, mm0 ; v0 v1 v2 v3 v4 v5 v6 v7 + punpcklbw mm0, mm0 ; v4 v4 v5 v5 v6 v6 v7 v7 + //movq mm1, mm0 ; v0 v0 v1 v1 v2 v2 v3 v3 + punpckhbw mm1, mm1 ;v0 v0 v1 v1 v2 v2 v3 v3 + movq [edi+8], mm1 ; move to memory v0 v1 v2 and v3 + sub esi, 8 + movq [edi], mm0 ; move to memory v4 v5 v6 and v7 + //sub esi, 4 + sub edi, 16 + sub ecx, 8 + jnz loop1_pass4 + EMMS + } + } + + sptr -= width_mmx; + dp -= width_mmx*2; + for (i = width; i; i--) + { + int j; + + for (j = 0; j < png_pass_inc[pass]; j++) + { + *dp-- = *sptr; + } + sptr --; + } + } + } /* end of pixel_bytes == 1 */ + + else if (pixel_bytes == 2) + { + if (((pass == 0) || (pass == 1)) && width) + { + int width_mmx = ((width >> 1) << 1); + width -= width_mmx; + if (width_mmx) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width_mmx + sub esi, 2 + sub edi, 30 +loop2_pass0: + movd mm0, [esi] ; X X X X v1 v0 v3 v2 + punpcklwd mm0, mm0 ; v1 v0 v1 v0 v3 v2 v3 v2 + movq mm1, mm0 ; v1 v0 v1 v0 v3 v2 v3 v2 + punpckldq mm0, mm0 ; v3 v2 v3 v2 v3 v2 v3 v2 + punpckhdq mm1, mm1 ; v1 v0 v1 v0 v1 v0 v1 v0 + movq [edi], mm0 + movq [edi + 8], mm0 + movq [edi + 16], mm1 + movq [edi + 24], mm1 + sub esi, 4 + sub edi, 32 + sub ecx, 2 + jnz loop2_pass0 + EMMS + } + } + + sptr -= (width_mmx*2 - 2); // sign fixed + dp -= (width_mmx*16 - 2); // sign fixed + for (i = width; i; i--) + { + png_byte v[8]; + int j; + sptr -= 2; + png_memcpy(v, sptr, 2); + for (j = 0; j < png_pass_inc[pass]; j++) + { + dp -= 2; + png_memcpy(dp, v, 2); + } + } + } + else if (((pass == 2) || (pass == 3)) && width) + { + int width_mmx = ((width >> 1) << 1) ; + width -= width_mmx; + if (width_mmx) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width_mmx + sub esi, 2 + sub edi, 14 +loop2_pass2: + movd mm0, [esi] ; X X X X v1 v0 v3 v2 + punpcklwd mm0, mm0 ; v1 v0 v1 v0 v3 v2 v3 v2 + movq mm1, mm0 ; v1 v0 v1 v0 v3 v2 v3 v2 + punpckldq mm0, mm0 ; v3 v2 v3 v2 v3 v2 v3 v2 + punpckhdq mm1, mm1 ; v1 v0 v1 v0 v1 v0 v1 v0 + movq [edi], mm0 + sub esi, 4 + movq [edi + 8], mm1 + //sub esi, 4 + sub edi, 16 + sub ecx, 2 + jnz loop2_pass2 + EMMS + } + } + + sptr -= (width_mmx*2 - 2); // sign fixed + dp -= (width_mmx*8 - 2); // sign fixed + for (i = width; i; i--) + { + png_byte v[8]; + int j; + sptr -= 2; + png_memcpy(v, sptr, 2); + for (j = 0; j < png_pass_inc[pass]; j++) + { + dp -= 2; + png_memcpy(dp, v, 2); + } + } + } + else if (width) // pass == 4 or 5 + { + int width_mmx = ((width >> 1) << 1) ; + width -= width_mmx; + if (width_mmx) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width_mmx + sub esi, 2 + sub edi, 6 +loop2_pass4: + movd mm0, [esi] ; X X X X v1 v0 v3 v2 + punpcklwd mm0, mm0 ; v1 v0 v1 v0 v3 v2 v3 v2 + sub esi, 4 + movq [edi], mm0 + sub edi, 8 + sub ecx, 2 + jnz loop2_pass4 + EMMS + } + } + + sptr -= (width_mmx*2 - 2); // sign fixed + dp -= (width_mmx*4 - 2); // sign fixed + for (i = width; i; i--) + { + png_byte v[8]; + int j; + sptr -= 2; + png_memcpy(v, sptr, 2); + for (j = 0; j < png_pass_inc[pass]; j++) + { + dp -= 2; + png_memcpy(dp, v, 2); + } + } + } + } /* end of pixel_bytes == 2 */ + + else if (pixel_bytes == 4) + { + if (((pass == 0) || (pass == 1)) && width) + { + int width_mmx = ((width >> 1) << 1) ; + width -= width_mmx; + if (width_mmx) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width_mmx + sub esi, 4 + sub edi, 60 +loop4_pass0: + movq mm0, [esi] ; v3 v2 v1 v0 v7 v6 v5 v4 + movq mm1, mm0 ; v3 v2 v1 v0 v7 v6 v5 v4 + punpckldq mm0, mm0 ; v7 v6 v5 v4 v7 v6 v5 v4 + punpckhdq mm1, mm1 ; v3 v2 v1 v0 v3 v2 v1 v0 + movq [edi], mm0 + movq [edi + 8], mm0 + movq [edi + 16], mm0 + movq [edi + 24], mm0 + movq [edi+32], mm1 + movq [edi + 40], mm1 + movq [edi+ 48], mm1 + sub esi, 8 + movq [edi + 56], mm1 + sub edi, 64 + sub ecx, 2 + jnz loop4_pass0 + EMMS + } + } + + sptr -= (width_mmx*4 - 4); // sign fixed + dp -= (width_mmx*32 - 4); // sign fixed + for (i = width; i; i--) + { + png_byte v[8]; + int j; + sptr -= 4; + png_memcpy(v, sptr, 4); + for (j = 0; j < png_pass_inc[pass]; j++) + { + dp -= 4; + png_memcpy(dp, v, 4); + } + } + } + else if (((pass == 2) || (pass == 3)) && width) + { + int width_mmx = ((width >> 1) << 1) ; + width -= width_mmx; + if (width_mmx) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width_mmx + sub esi, 4 + sub edi, 28 +loop4_pass2: + movq mm0, [esi] ; v3 v2 v1 v0 v7 v6 v5 v4 + movq mm1, mm0 ; v3 v2 v1 v0 v7 v6 v5 v4 + punpckldq mm0, mm0 ; v7 v6 v5 v4 v7 v6 v5 v4 + punpckhdq mm1, mm1 ; v3 v2 v1 v0 v3 v2 v1 v0 + movq [edi], mm0 + movq [edi + 8], mm0 + movq [edi+16], mm1 + movq [edi + 24], mm1 + sub esi, 8 + sub edi, 32 + sub ecx, 2 + jnz loop4_pass2 + EMMS + } + } + + sptr -= (width_mmx*4 - 4); // sign fixed + dp -= (width_mmx*16 - 4); // sign fixed + for (i = width; i; i--) + { + png_byte v[8]; + int j; + sptr -= 4; + png_memcpy(v, sptr, 4); + for (j = 0; j < png_pass_inc[pass]; j++) + { + dp -= 4; + png_memcpy(dp, v, 4); + } + } + } + else if (width) // pass == 4 or 5 + { + int width_mmx = ((width >> 1) << 1) ; + width -= width_mmx; + if (width_mmx) + { + _asm + { + mov esi, sptr + mov edi, dp + mov ecx, width_mmx + sub esi, 4 + sub edi, 12 +loop4_pass4: + movq mm0, [esi] ; v3 v2 v1 v0 v7 v6 v5 v4 + movq mm1, mm0 ; v3 v2 v1 v0 v7 v6 v5 v4 + punpckldq mm0, mm0 ; v7 v6 v5 v4 v7 v6 v5 v4 + punpckhdq mm1, mm1 ; v3 v2 v1 v0 v3 v2 v1 v0 + movq [edi], mm0 + sub esi, 8 + movq [edi + 8], mm1 + sub edi, 16 + sub ecx, 2 + jnz loop4_pass4 + EMMS + } + } + + sptr -= (width_mmx*4 - 4); // sign fixed + dp -= (width_mmx*8 - 4); // sign fixed + for (i = width; i; i--) + { + png_byte v[8]; + int j; + sptr -= 4; + png_memcpy(v, sptr, 4); + for (j = 0; j < png_pass_inc[pass]; j++) + { + dp -= 4; + png_memcpy(dp, v, 4); + } + } + } + + } /* end of pixel_bytes == 4 */ + + else if (pixel_bytes == 6) + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, 6); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, 6); + dp -= 6; + } + sptr -= 6; + } + } /* end of pixel_bytes == 6 */ + + else + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, pixel_bytes); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + sptr-= pixel_bytes; + } + } + } /* end of mmx_supported */ + + else /* MMX not supported: use modified C code - takes advantage + * of inlining of memcpy for a constant */ + { + if (pixel_bytes == 1) + { + for (i = width; i; i--) + { + int j; + for (j = 0; j < png_pass_inc[pass]; j++) + *dp-- = *sptr; + sptr--; + } + } + else if (pixel_bytes == 3) + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, pixel_bytes); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + sptr -= pixel_bytes; + } + } + else if (pixel_bytes == 2) + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, pixel_bytes); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + sptr -= pixel_bytes; + } + } + else if (pixel_bytes == 4) + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, pixel_bytes); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + sptr -= pixel_bytes; + } + } + else if (pixel_bytes == 6) + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, pixel_bytes); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + sptr -= pixel_bytes; + } + } + else + { + for (i = width; i; i--) + { + png_byte v[8]; + int j; + png_memcpy(v, sptr, pixel_bytes); + for (j = 0; j < png_pass_inc[pass]; j++) + { + png_memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + sptr -= pixel_bytes; + } + } + + } /* end of MMX not supported */ + break; + } + } /* end switch (row_info->pixel_depth) */ + + row_info->width = final_width; + + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,final_width); + } + +} + +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + + +// These variables are utilized in the functions below. They are declared +// globally here to ensure alignment on 8-byte boundaries. + +union uAll { + __int64 use; + double align; +} LBCarryMask = {0x0101010101010101}, + HBClearMask = {0x7f7f7f7f7f7f7f7f}, + ActiveMask, ActiveMask2, ActiveMaskEnd, ShiftBpp, ShiftRem; + + +// Optimized code for PNG Average filter decoder +void /* PRIVATE */ +png_read_filter_row_mmx_avg(png_row_infop row_info, png_bytep row + , png_bytep prev_row) +{ + int bpp; + png_uint_32 FullLength; + png_uint_32 MMXLength; + //png_uint_32 len; + int diff; + + bpp = (row_info->pixel_depth + 7) >> 3; // Get # bytes per pixel + FullLength = row_info->rowbytes; // # of bytes to filter + _asm { + // Init address pointers and offset + mov edi, row // edi ==> Avg(x) + xor ebx, ebx // ebx ==> x + mov edx, edi + mov esi, prev_row // esi ==> Prior(x) + sub edx, bpp // edx ==> Raw(x-bpp) + + xor eax, eax + // Compute the Raw value for the first bpp bytes + // Raw(x) = Avg(x) + (Prior(x)/2) +davgrlp: + mov al, [esi + ebx] // Load al with Prior(x) + inc ebx + shr al, 1 // divide by 2 + add al, [edi+ebx-1] // Add Avg(x); -1 to offset inc ebx + cmp ebx, bpp + mov [edi+ebx-1], al // Write back Raw(x); + // mov does not affect flags; -1 to offset inc ebx + jb davgrlp + // get # of bytes to alignment + mov diff, edi // take start of row + add diff, ebx // add bpp + add diff, 0xf // add 7 + 8 to incr past alignment boundary + and diff, 0xfffffff8 // mask to alignment boundary + sub diff, edi // subtract from start ==> value ebx at alignment + jz davggo + // fix alignment + // Compute the Raw value for the bytes upto the alignment boundary + // Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2) + xor ecx, ecx +davglp1: + xor eax, eax + mov cl, [esi + ebx] // load cl with Prior(x) + mov al, [edx + ebx] // load al with Raw(x-bpp) + add ax, cx + inc ebx + shr ax, 1 // divide by 2 + add al, [edi+ebx-1] // Add Avg(x); -1 to offset inc ebx + cmp ebx, diff // Check if at alignment boundary + mov [edi+ebx-1], al // Write back Raw(x); + // mov does not affect flags; -1 to offset inc ebx + jb davglp1 // Repeat until at alignment boundary +davggo: + mov eax, FullLength + mov ecx, eax + sub eax, ebx // subtract alignment fix + and eax, 0x00000007 // calc bytes over mult of 8 + sub ecx, eax // drop over bytes from original length + mov MMXLength, ecx + } // end _asm block + // Now do the math for the rest of the row + switch ( bpp ) + { + case 3: + { + ActiveMask.use = 0x0000000000ffffff; + ShiftBpp.use = 24; // == 3 * 8 + ShiftRem.use = 40; // == 64 - 24 + _asm { + // Re-init address pointers and offset + movq mm7, ActiveMask + mov ebx, diff // ebx ==> x = offset to alignment boundary + movq mm5, LBCarryMask + mov edi, row // edi ==> Avg(x) + movq mm4, HBClearMask + mov esi, prev_row // esi ==> Prior(x) + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm2, [edi + ebx - 8] // Load previous aligned 8 bytes + // (we correct position in loop below) +davg3lp: + movq mm0, [edi + ebx] // Load mm0 with Avg(x) + // Add (Prev_row/2) to Average + movq mm3, mm5 + psrlq mm2, ShiftRem // Correct position Raw(x-bpp) data + movq mm1, [esi + ebx] // Load mm1 with Prior(x) + movq mm6, mm7 + pand mm3, mm1 // get lsb for each prev_row byte + psrlq mm1, 1 // divide prev_row bytes by 2 + pand mm1, mm4 // clear invalid bit 7 of each byte + paddb mm0, mm1 // add (Prev_row/2) to Avg for each byte + // Add 1st active group (Raw(x-bpp)/2) to Average with LBCarry + movq mm1, mm3 // now use mm1 for getting LBCarrys + pand mm1, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 (Only valid for active group) + psrlq mm2, 1 // divide raw bytes by 2 + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm2, mm1 // add LBCarrys to (Raw(x-bpp)/2) for each byte + pand mm2, mm6 // Leave only Active Group 1 bytes to add to Avg + paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active + // byte + // Add 2nd active group (Raw(x-bpp)/2) to Average with LBCarry + psllq mm6, ShiftBpp // shift the mm6 mask to cover bytes 3-5 + movq mm2, mm0 // mov updated Raws to mm2 + psllq mm2, ShiftBpp // shift data to position correctly + movq mm1, mm3 // now use mm1 for getting LBCarrys + pand mm1, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 (Only valid for active group) + psrlq mm2, 1 // divide raw bytes by 2 + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm2, mm1 // add LBCarrys to (Raw(x-bpp)/2) for each byte + pand mm2, mm6 // Leave only Active Group 2 bytes to add to Avg + paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active + // byte + + // Add 3rd active group (Raw(x-bpp)/2) to Average with LBCarry + psllq mm6, ShiftBpp // shift the mm6 mask to cover the last two + // bytes + movq mm2, mm0 // mov updated Raws to mm2 + psllq mm2, ShiftBpp // shift data to position correctly + // Data only needs to be shifted once here to + // get the correct x-bpp offset. + movq mm1, mm3 // now use mm1 for getting LBCarrys + pand mm1, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 (Only valid for active group) + psrlq mm2, 1 // divide raw bytes by 2 + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm2, mm1 // add LBCarrys to (Raw(x-bpp)/2) for each byte + pand mm2, mm6 // Leave only Active Group 2 bytes to add to Avg + add ebx, 8 + paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active + // byte + + // Now ready to write back to memory + movq [edi + ebx - 8], mm0 + // Move updated Raw(x) to use as Raw(x-bpp) for next loop + cmp ebx, MMXLength + movq mm2, mm0 // mov updated Raw(x) to mm2 + jb davg3lp + } // end _asm block + } + break; + + case 6: + case 4: + case 7: + case 5: + { + ActiveMask.use = 0xffffffffffffffff; // use shift below to clear + // appropriate inactive bytes + ShiftBpp.use = bpp << 3; + ShiftRem.use = 64 - ShiftBpp.use; + _asm { + movq mm4, HBClearMask + // Re-init address pointers and offset + mov ebx, diff // ebx ==> x = offset to alignment boundary + // Load ActiveMask and clear all bytes except for 1st active group + movq mm7, ActiveMask + mov edi, row // edi ==> Avg(x) + psrlq mm7, ShiftRem + mov esi, prev_row // esi ==> Prior(x) + movq mm6, mm7 + movq mm5, LBCarryMask + psllq mm6, ShiftBpp // Create mask for 2nd active group + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm2, [edi + ebx - 8] // Load previous aligned 8 bytes + // (we correct position in loop below) +davg4lp: + movq mm0, [edi + ebx] + psrlq mm2, ShiftRem // shift data to position correctly + movq mm1, [esi + ebx] + // Add (Prev_row/2) to Average + movq mm3, mm5 + pand mm3, mm1 // get lsb for each prev_row byte + psrlq mm1, 1 // divide prev_row bytes by 2 + pand mm1, mm4 // clear invalid bit 7 of each byte + paddb mm0, mm1 // add (Prev_row/2) to Avg for each byte + // Add 1st active group (Raw(x-bpp)/2) to Average with LBCarry + movq mm1, mm3 // now use mm1 for getting LBCarrys + pand mm1, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 (Only valid for active group) + psrlq mm2, 1 // divide raw bytes by 2 + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm2, mm1 // add LBCarrys to (Raw(x-bpp)/2) for each byte + pand mm2, mm7 // Leave only Active Group 1 bytes to add to Avg + paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active + // byte + // Add 2nd active group (Raw(x-bpp)/2) to Average with LBCarry + movq mm2, mm0 // mov updated Raws to mm2 + psllq mm2, ShiftBpp // shift data to position correctly + add ebx, 8 + movq mm1, mm3 // now use mm1 for getting LBCarrys + pand mm1, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 (Only valid for active group) + psrlq mm2, 1 // divide raw bytes by 2 + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm2, mm1 // add LBCarrys to (Raw(x-bpp)/2) for each byte + pand mm2, mm6 // Leave only Active Group 2 bytes to add to Avg + paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active + // byte + cmp ebx, MMXLength + // Now ready to write back to memory + movq [edi + ebx - 8], mm0 + // Prep Raw(x-bpp) for next loop + movq mm2, mm0 // mov updated Raws to mm2 + jb davg4lp + } // end _asm block + } + break; + case 2: + { + ActiveMask.use = 0x000000000000ffff; + ShiftBpp.use = 16; // == 2 * 8 [BUGFIX] + ShiftRem.use = 48; // == 64 - 16 [BUGFIX] + _asm { + // Load ActiveMask + movq mm7, ActiveMask + // Re-init address pointers and offset + mov ebx, diff // ebx ==> x = offset to alignment boundary + movq mm5, LBCarryMask + mov edi, row // edi ==> Avg(x) + movq mm4, HBClearMask + mov esi, prev_row // esi ==> Prior(x) + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm2, [edi + ebx - 8] // Load previous aligned 8 bytes + // (we correct position in loop below) +davg2lp: + movq mm0, [edi + ebx] + psrlq mm2, ShiftRem // shift data to position correctly [BUGFIX] + movq mm1, [esi + ebx] + // Add (Prev_row/2) to Average + movq mm3, mm5 + pand mm3, mm1 // get lsb for each prev_row byte + psrlq mm1, 1 // divide prev_row bytes by 2 + pand mm1, mm4 // clear invalid bit 7 of each byte + movq mm6, mm7 + paddb mm0, mm1 // add (Prev_row/2) to Avg for each byte + // Add 1st active group (Raw(x-bpp)/2) to Average with LBCarry + movq mm1, mm3 // now use mm1 for getting LBCarrys + pand mm1, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 (Only valid for active group) + psrlq mm2, 1 // divide raw bytes by 2 + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm2, mm1 // add LBCarrys to (Raw(x-bpp)/2) for each byte + pand mm2, mm6 // Leave only Active Group 1 bytes to add to Avg + paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active byte + // Add 2nd active group (Raw(x-bpp)/2) to Average with LBCarry + psllq mm6, ShiftBpp // shift the mm6 mask to cover bytes 2 & 3 + movq mm2, mm0 // mov updated Raws to mm2 + psllq mm2, ShiftBpp // shift data to position correctly + movq mm1, mm3 // now use mm1 for getting LBCarrys + pand mm1, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 (Only valid for active group) + psrlq mm2, 1 // divide raw bytes by 2 + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm2, mm1 // add LBCarrys to (Raw(x-bpp)/2) for each byte + pand mm2, mm6 // Leave only Active Group 2 bytes to add to Avg + paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active byte + + // Add rdd active group (Raw(x-bpp)/2) to Average with LBCarry + psllq mm6, ShiftBpp // shift the mm6 mask to cover bytes 4 & 5 + movq mm2, mm0 // mov updated Raws to mm2 + psllq mm2, ShiftBpp // shift data to position correctly + // Data only needs to be shifted once here to + // get the correct x-bpp offset. + movq mm1, mm3 // now use mm1 for getting LBCarrys + pand mm1, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 (Only valid for active group) + psrlq mm2, 1 // divide raw bytes by 2 + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm2, mm1 // add LBCarrys to (Raw(x-bpp)/2) for each byte + pand mm2, mm6 // Leave only Active Group 2 bytes to add to Avg + paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active byte + + // Add 4th active group (Raw(x-bpp)/2) to Average with LBCarry + psllq mm6, ShiftBpp // shift the mm6 mask to cover bytes 6 & 7 + movq mm2, mm0 // mov updated Raws to mm2 + psllq mm2, ShiftBpp // shift data to position correctly + // Data only needs to be shifted once here to + // get the correct x-bpp offset. + add ebx, 8 + movq mm1, mm3 // now use mm1 for getting LBCarrys + pand mm1, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 (Only valid for active group) + psrlq mm2, 1 // divide raw bytes by 2 + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm2, mm1 // add LBCarrys to (Raw(x-bpp)/2) for each byte + pand mm2, mm6 // Leave only Active Group 2 bytes to add to Avg + paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active byte + + cmp ebx, MMXLength + // Now ready to write back to memory + movq [edi + ebx - 8], mm0 + // Prep Raw(x-bpp) for next loop + movq mm2, mm0 // mov updated Raws to mm2 + jb davg2lp + } // end _asm block + } + break; + + case 1: // bpp == 1 + { + _asm { + // Re-init address pointers and offset + mov ebx, diff // ebx ==> x = offset to alignment boundary + mov edi, row // edi ==> Avg(x) + cmp ebx, FullLength // Test if offset at end of array + jnb davg1end + // Do Paeth decode for remaining bytes + mov esi, prev_row // esi ==> Prior(x) + mov edx, edi + xor ecx, ecx // zero ecx before using cl & cx in loop below + sub edx, bpp // edx ==> Raw(x-bpp) +davg1lp: + // Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2) + xor eax, eax + mov cl, [esi + ebx] // load cl with Prior(x) + mov al, [edx + ebx] // load al with Raw(x-bpp) + add ax, cx + inc ebx + shr ax, 1 // divide by 2 + add al, [edi+ebx-1] // Add Avg(x); -1 to offset inc ebx + cmp ebx, FullLength // Check if at end of array + mov [edi+ebx-1], al // Write back Raw(x); + // mov does not affect flags; -1 to offset inc ebx + jb davg1lp +davg1end: + } // end _asm block + } + return; + + case 8: // bpp == 8 + { + _asm { + // Re-init address pointers and offset + mov ebx, diff // ebx ==> x = offset to alignment boundary + movq mm5, LBCarryMask + mov edi, row // edi ==> Avg(x) + movq mm4, HBClearMask + mov esi, prev_row // esi ==> Prior(x) + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm2, [edi + ebx - 8] // Load previous aligned 8 bytes + // (NO NEED to correct position in loop below) +davg8lp: + movq mm0, [edi + ebx] + movq mm3, mm5 + movq mm1, [esi + ebx] + add ebx, 8 + pand mm3, mm1 // get lsb for each prev_row byte + psrlq mm1, 1 // divide prev_row bytes by 2 + pand mm3, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 + psrlq mm2, 1 // divide raw bytes by 2 + pand mm1, mm4 // clear invalid bit 7 of each byte + paddb mm0, mm3 // add LBCarrys to Avg for each byte + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm0, mm1 // add (Prev_row/2) to Avg for each byte + paddb mm0, mm2 // add (Raw/2) to Avg for each byte + cmp ebx, MMXLength + movq [edi + ebx - 8], mm0 + movq mm2, mm0 // reuse as Raw(x-bpp) + jb davg8lp + } // end _asm block + } + break; + default: // bpp greater than 8 + { + _asm { + movq mm5, LBCarryMask + // Re-init address pointers and offset + mov ebx, diff // ebx ==> x = offset to alignment boundary + mov edi, row // edi ==> Avg(x) + movq mm4, HBClearMask + mov edx, edi + mov esi, prev_row // esi ==> Prior(x) + sub edx, bpp // edx ==> Raw(x-bpp) +davgAlp: + movq mm0, [edi + ebx] + movq mm3, mm5 + movq mm1, [esi + ebx] + pand mm3, mm1 // get lsb for each prev_row byte + movq mm2, [edx + ebx] + psrlq mm1, 1 // divide prev_row bytes by 2 + pand mm3, mm2 // get LBCarrys for each byte where both + // lsb's were == 1 + psrlq mm2, 1 // divide raw bytes by 2 + pand mm1, mm4 // clear invalid bit 7 of each byte + paddb mm0, mm3 // add LBCarrys to Avg for each byte + pand mm2, mm4 // clear invalid bit 7 of each byte + paddb mm0, mm1 // add (Prev_row/2) to Avg for each byte + add ebx, 8 + paddb mm0, mm2 // add (Raw/2) to Avg for each byte + cmp ebx, MMXLength + movq [edi + ebx - 8], mm0 + jb davgAlp + } // end _asm block + } + break; + } // end switch ( bpp ) + + _asm { + // MMX acceleration complete now do clean-up + // Check if any remaining bytes left to decode + mov ebx, MMXLength // ebx ==> x = offset bytes remaining after MMX + mov edi, row // edi ==> Avg(x) + cmp ebx, FullLength // Test if offset at end of array + jnb davgend + // Do Paeth decode for remaining bytes + mov esi, prev_row // esi ==> Prior(x) + mov edx, edi + xor ecx, ecx // zero ecx before using cl & cx in loop below + sub edx, bpp // edx ==> Raw(x-bpp) +davglp2: + // Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2) + xor eax, eax + mov cl, [esi + ebx] // load cl with Prior(x) + mov al, [edx + ebx] // load al with Raw(x-bpp) + add ax, cx + inc ebx + shr ax, 1 // divide by 2 + add al, [edi+ebx-1] // Add Avg(x); -1 to offset inc ebx + cmp ebx, FullLength // Check if at end of array + mov [edi+ebx-1], al // Write back Raw(x); + // mov does not affect flags; -1 to offset inc ebx + jb davglp2 +davgend: + emms // End MMX instructions; prep for possible FP instrs. + } // end _asm block +} + +// Optimized code for PNG Paeth filter decoder +void /* PRIVATE */ +png_read_filter_row_mmx_paeth(png_row_infop row_info, png_bytep row, + png_bytep prev_row) +{ + png_uint_32 FullLength; + png_uint_32 MMXLength; + //png_uint_32 len; + int bpp; + int diff; + //int ptemp; + int patemp, pbtemp, pctemp; + + bpp = (row_info->pixel_depth + 7) >> 3; // Get # bytes per pixel + FullLength = row_info->rowbytes; // # of bytes to filter + _asm + { + xor ebx, ebx // ebx ==> x offset + mov edi, row + xor edx, edx // edx ==> x-bpp offset + mov esi, prev_row + xor eax, eax + + // Compute the Raw value for the first bpp bytes + // Note: the formula works out to be always + // Paeth(x) = Raw(x) + Prior(x) where x < bpp +dpthrlp: + mov al, [edi + ebx] + add al, [esi + ebx] + inc ebx + cmp ebx, bpp + mov [edi + ebx - 1], al + jb dpthrlp + // get # of bytes to alignment + mov diff, edi // take start of row + add diff, ebx // add bpp + xor ecx, ecx + add diff, 0xf // add 7 + 8 to incr past alignment boundary + and diff, 0xfffffff8 // mask to alignment boundary + sub diff, edi // subtract from start ==> value ebx at alignment + jz dpthgo + // fix alignment +dpthlp1: + xor eax, eax + // pav = p - a = (a + b - c) - a = b - c + mov al, [esi + ebx] // load Prior(x) into al + mov cl, [esi + edx] // load Prior(x-bpp) into cl + sub eax, ecx // subtract Prior(x-bpp) + mov patemp, eax // Save pav for later use + xor eax, eax + // pbv = p - b = (a + b - c) - b = a - c + mov al, [edi + edx] // load Raw(x-bpp) into al + sub eax, ecx // subtract Prior(x-bpp) + mov ecx, eax + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + add eax, patemp // pcv = pav + pbv + // pc = abs(pcv) + test eax, 0x80000000 + jz dpthpca + neg eax // reverse sign of neg values +dpthpca: + mov pctemp, eax // save pc for later use + // pb = abs(pbv) + test ecx, 0x80000000 + jz dpthpba + neg ecx // reverse sign of neg values +dpthpba: + mov pbtemp, ecx // save pb for later use + // pa = abs(pav) + mov eax, patemp + test eax, 0x80000000 + jz dpthpaa + neg eax // reverse sign of neg values +dpthpaa: + mov patemp, eax // save pa for later use + // test if pa <= pb + cmp eax, ecx + jna dpthabb + // pa > pb; now test if pb <= pc + cmp ecx, pctemp + jna dpthbbc + // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp) + mov cl, [esi + edx] // load Prior(x-bpp) into cl + jmp dpthpaeth +dpthbbc: + // pb <= pc; Raw(x) = Paeth(x) + Prior(x) + mov cl, [esi + ebx] // load Prior(x) into cl + jmp dpthpaeth +dpthabb: + // pa <= pb; now test if pa <= pc + cmp eax, pctemp + jna dpthabc + // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp) + mov cl, [esi + edx] // load Prior(x-bpp) into cl + jmp dpthpaeth +dpthabc: + // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp) + mov cl, [edi + edx] // load Raw(x-bpp) into cl +dpthpaeth: + inc ebx + inc edx + // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256 + add [edi + ebx - 1], cl + cmp ebx, diff + jb dpthlp1 +dpthgo: + mov ecx, FullLength + mov eax, ecx + sub eax, ebx // subtract alignment fix + and eax, 0x00000007 // calc bytes over mult of 8 + sub ecx, eax // drop over bytes from original length + mov MMXLength, ecx + } // end _asm block + // Now do the math for the rest of the row + switch ( bpp ) + { + case 3: + { + ActiveMask.use = 0x0000000000ffffff; + ActiveMaskEnd.use = 0xffff000000000000; + ShiftBpp.use = 24; // == bpp(3) * 8 + ShiftRem.use = 40; // == 64 - 24 + _asm + { + mov ebx, diff + mov edi, row + mov esi, prev_row + pxor mm0, mm0 + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm1, [edi+ebx-8] +dpth3lp: + psrlq mm1, ShiftRem // shift last 3 bytes to 1st 3 bytes + movq mm2, [esi + ebx] // load b=Prior(x) + punpcklbw mm1, mm0 // Unpack High bytes of a + movq mm3, [esi+ebx-8] // Prep c=Prior(x-bpp) bytes + punpcklbw mm2, mm0 // Unpack High bytes of b + psrlq mm3, ShiftRem // shift last 3 bytes to 1st 3 bytes + // pav = p - a = (a + b - c) - a = b - c + movq mm4, mm2 + punpcklbw mm3, mm0 // Unpack High bytes of c + // pbv = p - b = (a + b - c) - b = a - c + movq mm5, mm1 + psubw mm4, mm3 + pxor mm7, mm7 + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + movq mm6, mm4 + psubw mm5, mm3 + + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + pcmpgtw mm0, mm4 // Create mask pav bytes < 0 + paddw mm6, mm5 + pand mm0, mm4 // Only pav bytes < 0 in mm7 + pcmpgtw mm7, mm5 // Create mask pbv bytes < 0 + psubw mm4, mm0 + pand mm7, mm5 // Only pbv bytes < 0 in mm0 + psubw mm4, mm0 + psubw mm5, mm7 + pxor mm0, mm0 + pcmpgtw mm0, mm6 // Create mask pcv bytes < 0 + pand mm0, mm6 // Only pav bytes < 0 in mm7 + psubw mm5, mm7 + psubw mm6, mm0 + // test pa <= pb + movq mm7, mm4 + psubw mm6, mm0 + pcmpgtw mm7, mm5 // pa > pb? + movq mm0, mm7 + // use mm7 mask to merge pa & pb + pand mm5, mm7 + // use mm0 mask copy to merge a & b + pand mm2, mm0 + pandn mm7, mm4 + pandn mm0, mm1 + paddw mm7, mm5 + paddw mm0, mm2 + // test ((pa <= pb)? pa:pb) <= pc + pcmpgtw mm7, mm6 // pab > pc? + pxor mm1, mm1 + pand mm3, mm7 + pandn mm7, mm0 + paddw mm7, mm3 + pxor mm0, mm0 + packuswb mm7, mm1 + movq mm3, [esi + ebx] // load c=Prior(x-bpp) + pand mm7, ActiveMask + movq mm2, mm3 // load b=Prior(x) step 1 + paddb mm7, [edi + ebx] // add Paeth predictor with Raw(x) + punpcklbw mm3, mm0 // Unpack High bytes of c + movq [edi + ebx], mm7 // write back updated value + movq mm1, mm7 // Now mm1 will be used as Raw(x-bpp) + // Now do Paeth for 2nd set of bytes (3-5) + psrlq mm2, ShiftBpp // load b=Prior(x) step 2 + punpcklbw mm1, mm0 // Unpack High bytes of a + pxor mm7, mm7 + punpcklbw mm2, mm0 // Unpack High bytes of b + // pbv = p - b = (a + b - c) - b = a - c + movq mm5, mm1 + // pav = p - a = (a + b - c) - a = b - c + movq mm4, mm2 + psubw mm5, mm3 + psubw mm4, mm3 + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = + // pav + pbv = pbv + pav + movq mm6, mm5 + paddw mm6, mm4 + + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + pcmpgtw mm0, mm5 // Create mask pbv bytes < 0 + pcmpgtw mm7, mm4 // Create mask pav bytes < 0 + pand mm0, mm5 // Only pbv bytes < 0 in mm0 + pand mm7, mm4 // Only pav bytes < 0 in mm7 + psubw mm5, mm0 + psubw mm4, mm7 + psubw mm5, mm0 + psubw mm4, mm7 + pxor mm0, mm0 + pcmpgtw mm0, mm6 // Create mask pcv bytes < 0 + pand mm0, mm6 // Only pav bytes < 0 in mm7 + psubw mm6, mm0 + // test pa <= pb + movq mm7, mm4 + psubw mm6, mm0 + pcmpgtw mm7, mm5 // pa > pb? + movq mm0, mm7 + // use mm7 mask to merge pa & pb + pand mm5, mm7 + // use mm0 mask copy to merge a & b + pand mm2, mm0 + pandn mm7, mm4 + pandn mm0, mm1 + paddw mm7, mm5 + paddw mm0, mm2 + // test ((pa <= pb)? pa:pb) <= pc + pcmpgtw mm7, mm6 // pab > pc? + movq mm2, [esi + ebx] // load b=Prior(x) + pand mm3, mm7 + pandn mm7, mm0 + pxor mm1, mm1 + paddw mm7, mm3 + pxor mm0, mm0 + packuswb mm7, mm1 + movq mm3, mm2 // load c=Prior(x-bpp) step 1 + pand mm7, ActiveMask + punpckhbw mm2, mm0 // Unpack High bytes of b + psllq mm7, ShiftBpp // Shift bytes to 2nd group of 3 bytes + // pav = p - a = (a + b - c) - a = b - c + movq mm4, mm2 + paddb mm7, [edi + ebx] // add Paeth predictor with Raw(x) + psllq mm3, ShiftBpp // load c=Prior(x-bpp) step 2 + movq [edi + ebx], mm7 // write back updated value + movq mm1, mm7 + punpckhbw mm3, mm0 // Unpack High bytes of c + psllq mm1, ShiftBpp // Shift bytes + // Now mm1 will be used as Raw(x-bpp) + // Now do Paeth for 3rd, and final, set of bytes (6-7) + pxor mm7, mm7 + punpckhbw mm1, mm0 // Unpack High bytes of a + psubw mm4, mm3 + // pbv = p - b = (a + b - c) - b = a - c + movq mm5, mm1 + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + movq mm6, mm4 + psubw mm5, mm3 + pxor mm0, mm0 + paddw mm6, mm5 + + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + pcmpgtw mm0, mm4 // Create mask pav bytes < 0 + pcmpgtw mm7, mm5 // Create mask pbv bytes < 0 + pand mm0, mm4 // Only pav bytes < 0 in mm7 + pand mm7, mm5 // Only pbv bytes < 0 in mm0 + psubw mm4, mm0 + psubw mm5, mm7 + psubw mm4, mm0 + psubw mm5, mm7 + pxor mm0, mm0 + pcmpgtw mm0, mm6 // Create mask pcv bytes < 0 + pand mm0, mm6 // Only pav bytes < 0 in mm7 + psubw mm6, mm0 + // test pa <= pb + movq mm7, mm4 + psubw mm6, mm0 + pcmpgtw mm7, mm5 // pa > pb? + movq mm0, mm7 + // use mm0 mask copy to merge a & b + pand mm2, mm0 + // use mm7 mask to merge pa & pb + pand mm5, mm7 + pandn mm0, mm1 + pandn mm7, mm4 + paddw mm0, mm2 + paddw mm7, mm5 + // test ((pa <= pb)? pa:pb) <= pc + pcmpgtw mm7, mm6 // pab > pc? + pand mm3, mm7 + pandn mm7, mm0 + paddw mm7, mm3 + pxor mm1, mm1 + packuswb mm1, mm7 + // Step ebx to next set of 8 bytes and repeat loop til done + add ebx, 8 + pand mm1, ActiveMaskEnd + paddb mm1, [edi + ebx - 8] // add Paeth predictor with Raw(x) + + cmp ebx, MMXLength + pxor mm0, mm0 // pxor does not affect flags + movq [edi + ebx - 8], mm1 // write back updated value + // mm1 will be used as Raw(x-bpp) next loop + // mm3 ready to be used as Prior(x-bpp) next loop + jb dpth3lp + } // end _asm block + } + break; + + case 6: + case 7: + case 5: + { + ActiveMask.use = 0x00000000ffffffff; + ActiveMask2.use = 0xffffffff00000000; + ShiftBpp.use = bpp << 3; // == bpp * 8 + ShiftRem.use = 64 - ShiftBpp.use; + _asm + { + mov ebx, diff + mov edi, row + mov esi, prev_row + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm1, [edi+ebx-8] + pxor mm0, mm0 +dpth6lp: + // Must shift to position Raw(x-bpp) data + psrlq mm1, ShiftRem + // Do first set of 4 bytes + movq mm3, [esi+ebx-8] // read c=Prior(x-bpp) bytes + punpcklbw mm1, mm0 // Unpack Low bytes of a + movq mm2, [esi + ebx] // load b=Prior(x) + punpcklbw mm2, mm0 // Unpack Low bytes of b + // Must shift to position Prior(x-bpp) data + psrlq mm3, ShiftRem + // pav = p - a = (a + b - c) - a = b - c + movq mm4, mm2 + punpcklbw mm3, mm0 // Unpack Low bytes of c + // pbv = p - b = (a + b - c) - b = a - c + movq mm5, mm1 + psubw mm4, mm3 + pxor mm7, mm7 + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + movq mm6, mm4 + psubw mm5, mm3 + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + pcmpgtw mm0, mm4 // Create mask pav bytes < 0 + paddw mm6, mm5 + pand mm0, mm4 // Only pav bytes < 0 in mm7 + pcmpgtw mm7, mm5 // Create mask pbv bytes < 0 + psubw mm4, mm0 + pand mm7, mm5 // Only pbv bytes < 0 in mm0 + psubw mm4, mm0 + psubw mm5, mm7 + pxor mm0, mm0 + pcmpgtw mm0, mm6 // Create mask pcv bytes < 0 + pand mm0, mm6 // Only pav bytes < 0 in mm7 + psubw mm5, mm7 + psubw mm6, mm0 + // test pa <= pb + movq mm7, mm4 + psubw mm6, mm0 + pcmpgtw mm7, mm5 // pa > pb? + movq mm0, mm7 + // use mm7 mask to merge pa & pb + pand mm5, mm7 + // use mm0 mask copy to merge a & b + pand mm2, mm0 + pandn mm7, mm4 + pandn mm0, mm1 + paddw mm7, mm5 + paddw mm0, mm2 + // test ((pa <= pb)? pa:pb) <= pc + pcmpgtw mm7, mm6 // pab > pc? + pxor mm1, mm1 + pand mm3, mm7 + pandn mm7, mm0 + paddw mm7, mm3 + pxor mm0, mm0 + packuswb mm7, mm1 + movq mm3, [esi + ebx - 8] // load c=Prior(x-bpp) + pand mm7, ActiveMask + psrlq mm3, ShiftRem + movq mm2, [esi + ebx] // load b=Prior(x) step 1 + paddb mm7, [edi + ebx] // add Paeth predictor with Raw(x) + movq mm6, mm2 + movq [edi + ebx], mm7 // write back updated value + movq mm1, [edi+ebx-8] + psllq mm6, ShiftBpp + movq mm5, mm7 + psrlq mm1, ShiftRem + por mm3, mm6 + psllq mm5, ShiftBpp + punpckhbw mm3, mm0 // Unpack High bytes of c + por mm1, mm5 + // Do second set of 4 bytes + punpckhbw mm2, mm0 // Unpack High bytes of b + punpckhbw mm1, mm0 // Unpack High bytes of a + // pav = p - a = (a + b - c) - a = b - c + movq mm4, mm2 + // pbv = p - b = (a + b - c) - b = a - c + movq mm5, mm1 + psubw mm4, mm3 + pxor mm7, mm7 + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + movq mm6, mm4 + psubw mm5, mm3 + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + pcmpgtw mm0, mm4 // Create mask pav bytes < 0 + paddw mm6, mm5 + pand mm0, mm4 // Only pav bytes < 0 in mm7 + pcmpgtw mm7, mm5 // Create mask pbv bytes < 0 + psubw mm4, mm0 + pand mm7, mm5 // Only pbv bytes < 0 in mm0 + psubw mm4, mm0 + psubw mm5, mm7 + pxor mm0, mm0 + pcmpgtw mm0, mm6 // Create mask pcv bytes < 0 + pand mm0, mm6 // Only pav bytes < 0 in mm7 + psubw mm5, mm7 + psubw mm6, mm0 + // test pa <= pb + movq mm7, mm4 + psubw mm6, mm0 + pcmpgtw mm7, mm5 // pa > pb? + movq mm0, mm7 + // use mm7 mask to merge pa & pb + pand mm5, mm7 + // use mm0 mask copy to merge a & b + pand mm2, mm0 + pandn mm7, mm4 + pandn mm0, mm1 + paddw mm7, mm5 + paddw mm0, mm2 + // test ((pa <= pb)? pa:pb) <= pc + pcmpgtw mm7, mm6 // pab > pc? + pxor mm1, mm1 + pand mm3, mm7 + pandn mm7, mm0 + pxor mm1, mm1 + paddw mm7, mm3 + pxor mm0, mm0 + // Step ex to next set of 8 bytes and repeat loop til done + add ebx, 8 + packuswb mm1, mm7 + paddb mm1, [edi + ebx - 8] // add Paeth predictor with Raw(x) + cmp ebx, MMXLength + movq [edi + ebx - 8], mm1 // write back updated value + // mm1 will be used as Raw(x-bpp) next loop + jb dpth6lp + } // end _asm block + } + break; + + case 4: + { + ActiveMask.use = 0x00000000ffffffff; + _asm { + mov ebx, diff + mov edi, row + mov esi, prev_row + pxor mm0, mm0 + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm1, [edi+ebx-8] // Only time should need to read + // a=Raw(x-bpp) bytes +dpth4lp: + // Do first set of 4 bytes + movq mm3, [esi+ebx-8] // read c=Prior(x-bpp) bytes + punpckhbw mm1, mm0 // Unpack Low bytes of a + movq mm2, [esi + ebx] // load b=Prior(x) + punpcklbw mm2, mm0 // Unpack High bytes of b + // pav = p - a = (a + b - c) - a = b - c + movq mm4, mm2 + punpckhbw mm3, mm0 // Unpack High bytes of c + // pbv = p - b = (a + b - c) - b = a - c + movq mm5, mm1 + psubw mm4, mm3 + pxor mm7, mm7 + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + movq mm6, mm4 + psubw mm5, mm3 + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + pcmpgtw mm0, mm4 // Create mask pav bytes < 0 + paddw mm6, mm5 + pand mm0, mm4 // Only pav bytes < 0 in mm7 + pcmpgtw mm7, mm5 // Create mask pbv bytes < 0 + psubw mm4, mm0 + pand mm7, mm5 // Only pbv bytes < 0 in mm0 + psubw mm4, mm0 + psubw mm5, mm7 + pxor mm0, mm0 + pcmpgtw mm0, mm6 // Create mask pcv bytes < 0 + pand mm0, mm6 // Only pav bytes < 0 in mm7 + psubw mm5, mm7 + psubw mm6, mm0 + // test pa <= pb + movq mm7, mm4 + psubw mm6, mm0 + pcmpgtw mm7, mm5 // pa > pb? + movq mm0, mm7 + // use mm7 mask to merge pa & pb + pand mm5, mm7 + // use mm0 mask copy to merge a & b + pand mm2, mm0 + pandn mm7, mm4 + pandn mm0, mm1 + paddw mm7, mm5 + paddw mm0, mm2 + // test ((pa <= pb)? pa:pb) <= pc + pcmpgtw mm7, mm6 // pab > pc? + pxor mm1, mm1 + pand mm3, mm7 + pandn mm7, mm0 + paddw mm7, mm3 + pxor mm0, mm0 + packuswb mm7, mm1 + movq mm3, [esi + ebx] // load c=Prior(x-bpp) + pand mm7, ActiveMask + movq mm2, mm3 // load b=Prior(x) step 1 + paddb mm7, [edi + ebx] // add Paeth predictor with Raw(x) + punpcklbw mm3, mm0 // Unpack High bytes of c + movq [edi + ebx], mm7 // write back updated value + movq mm1, mm7 // Now mm1 will be used as Raw(x-bpp) + // Do second set of 4 bytes + punpckhbw mm2, mm0 // Unpack Low bytes of b + punpcklbw mm1, mm0 // Unpack Low bytes of a + // pav = p - a = (a + b - c) - a = b - c + movq mm4, mm2 + // pbv = p - b = (a + b - c) - b = a - c + movq mm5, mm1 + psubw mm4, mm3 + pxor mm7, mm7 + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + movq mm6, mm4 + psubw mm5, mm3 + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + pcmpgtw mm0, mm4 // Create mask pav bytes < 0 + paddw mm6, mm5 + pand mm0, mm4 // Only pav bytes < 0 in mm7 + pcmpgtw mm7, mm5 // Create mask pbv bytes < 0 + psubw mm4, mm0 + pand mm7, mm5 // Only pbv bytes < 0 in mm0 + psubw mm4, mm0 + psubw mm5, mm7 + pxor mm0, mm0 + pcmpgtw mm0, mm6 // Create mask pcv bytes < 0 + pand mm0, mm6 // Only pav bytes < 0 in mm7 + psubw mm5, mm7 + psubw mm6, mm0 + // test pa <= pb + movq mm7, mm4 + psubw mm6, mm0 + pcmpgtw mm7, mm5 // pa > pb? + movq mm0, mm7 + // use mm7 mask to merge pa & pb + pand mm5, mm7 + // use mm0 mask copy to merge a & b + pand mm2, mm0 + pandn mm7, mm4 + pandn mm0, mm1 + paddw mm7, mm5 + paddw mm0, mm2 + // test ((pa <= pb)? pa:pb) <= pc + pcmpgtw mm7, mm6 // pab > pc? + pxor mm1, mm1 + pand mm3, mm7 + pandn mm7, mm0 + pxor mm1, mm1 + paddw mm7, mm3 + pxor mm0, mm0 + // Step ex to next set of 8 bytes and repeat loop til done + add ebx, 8 + packuswb mm1, mm7 + paddb mm1, [edi + ebx - 8] // add Paeth predictor with Raw(x) + cmp ebx, MMXLength + movq [edi + ebx - 8], mm1 // write back updated value + // mm1 will be used as Raw(x-bpp) next loop + jb dpth4lp + } // end _asm block + } + break; + case 8: // bpp == 8 + { + ActiveMask.use = 0x00000000ffffffff; + _asm { + mov ebx, diff + mov edi, row + mov esi, prev_row + pxor mm0, mm0 + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm1, [edi+ebx-8] // Only time should need to read + // a=Raw(x-bpp) bytes +dpth8lp: + // Do first set of 4 bytes + movq mm3, [esi+ebx-8] // read c=Prior(x-bpp) bytes + punpcklbw mm1, mm0 // Unpack Low bytes of a + movq mm2, [esi + ebx] // load b=Prior(x) + punpcklbw mm2, mm0 // Unpack Low bytes of b + // pav = p - a = (a + b - c) - a = b - c + movq mm4, mm2 + punpcklbw mm3, mm0 // Unpack Low bytes of c + // pbv = p - b = (a + b - c) - b = a - c + movq mm5, mm1 + psubw mm4, mm3 + pxor mm7, mm7 + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + movq mm6, mm4 + psubw mm5, mm3 + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + pcmpgtw mm0, mm4 // Create mask pav bytes < 0 + paddw mm6, mm5 + pand mm0, mm4 // Only pav bytes < 0 in mm7 + pcmpgtw mm7, mm5 // Create mask pbv bytes < 0 + psubw mm4, mm0 + pand mm7, mm5 // Only pbv bytes < 0 in mm0 + psubw mm4, mm0 + psubw mm5, mm7 + pxor mm0, mm0 + pcmpgtw mm0, mm6 // Create mask pcv bytes < 0 + pand mm0, mm6 // Only pav bytes < 0 in mm7 + psubw mm5, mm7 + psubw mm6, mm0 + // test pa <= pb + movq mm7, mm4 + psubw mm6, mm0 + pcmpgtw mm7, mm5 // pa > pb? + movq mm0, mm7 + // use mm7 mask to merge pa & pb + pand mm5, mm7 + // use mm0 mask copy to merge a & b + pand mm2, mm0 + pandn mm7, mm4 + pandn mm0, mm1 + paddw mm7, mm5 + paddw mm0, mm2 + // test ((pa <= pb)? pa:pb) <= pc + pcmpgtw mm7, mm6 // pab > pc? + pxor mm1, mm1 + pand mm3, mm7 + pandn mm7, mm0 + paddw mm7, mm3 + pxor mm0, mm0 + packuswb mm7, mm1 + movq mm3, [esi+ebx-8] // read c=Prior(x-bpp) bytes + pand mm7, ActiveMask + movq mm2, [esi + ebx] // load b=Prior(x) + paddb mm7, [edi + ebx] // add Paeth predictor with Raw(x) + punpckhbw mm3, mm0 // Unpack High bytes of c + movq [edi + ebx], mm7 // write back updated value + movq mm1, [edi+ebx-8] // read a=Raw(x-bpp) bytes + + // Do second set of 4 bytes + punpckhbw mm2, mm0 // Unpack High bytes of b + punpckhbw mm1, mm0 // Unpack High bytes of a + // pav = p - a = (a + b - c) - a = b - c + movq mm4, mm2 + // pbv = p - b = (a + b - c) - b = a - c + movq mm5, mm1 + psubw mm4, mm3 + pxor mm7, mm7 + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + movq mm6, mm4 + psubw mm5, mm3 + // pa = abs(p-a) = abs(pav) + // pb = abs(p-b) = abs(pbv) + // pc = abs(p-c) = abs(pcv) + pcmpgtw mm0, mm4 // Create mask pav bytes < 0 + paddw mm6, mm5 + pand mm0, mm4 // Only pav bytes < 0 in mm7 + pcmpgtw mm7, mm5 // Create mask pbv bytes < 0 + psubw mm4, mm0 + pand mm7, mm5 // Only pbv bytes < 0 in mm0 + psubw mm4, mm0 + psubw mm5, mm7 + pxor mm0, mm0 + pcmpgtw mm0, mm6 // Create mask pcv bytes < 0 + pand mm0, mm6 // Only pav bytes < 0 in mm7 + psubw mm5, mm7 + psubw mm6, mm0 + // test pa <= pb + movq mm7, mm4 + psubw mm6, mm0 + pcmpgtw mm7, mm5 // pa > pb? + movq mm0, mm7 + // use mm7 mask to merge pa & pb + pand mm5, mm7 + // use mm0 mask copy to merge a & b + pand mm2, mm0 + pandn mm7, mm4 + pandn mm0, mm1 + paddw mm7, mm5 + paddw mm0, mm2 + // test ((pa <= pb)? pa:pb) <= pc + pcmpgtw mm7, mm6 // pab > pc? + pxor mm1, mm1 + pand mm3, mm7 + pandn mm7, mm0 + pxor mm1, mm1 + paddw mm7, mm3 + pxor mm0, mm0 + // Step ex to next set of 8 bytes and repeat loop til done + add ebx, 8 + packuswb mm1, mm7 + paddb mm1, [edi + ebx - 8] // add Paeth predictor with Raw(x) + cmp ebx, MMXLength + movq [edi + ebx - 8], mm1 // write back updated value + // mm1 will be used as Raw(x-bpp) next loop + jb dpth8lp + } // end _asm block + } + break; + + case 1: // bpp = 1 + case 2: // bpp = 2 + default: // bpp > 8 + { + _asm { + mov ebx, diff + cmp ebx, FullLength + jnb dpthdend + mov edi, row + mov esi, prev_row + // Do Paeth decode for remaining bytes + mov edx, ebx + xor ecx, ecx // zero ecx before using cl & cx in loop below + sub edx, bpp // Set edx = ebx - bpp +dpthdlp: + xor eax, eax + // pav = p - a = (a + b - c) - a = b - c + mov al, [esi + ebx] // load Prior(x) into al + mov cl, [esi + edx] // load Prior(x-bpp) into cl + sub eax, ecx // subtract Prior(x-bpp) + mov patemp, eax // Save pav for later use + xor eax, eax + // pbv = p - b = (a + b - c) - b = a - c + mov al, [edi + edx] // load Raw(x-bpp) into al + sub eax, ecx // subtract Prior(x-bpp) + mov ecx, eax + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + add eax, patemp // pcv = pav + pbv + // pc = abs(pcv) + test eax, 0x80000000 + jz dpthdpca + neg eax // reverse sign of neg values +dpthdpca: + mov pctemp, eax // save pc for later use + // pb = abs(pbv) + test ecx, 0x80000000 + jz dpthdpba + neg ecx // reverse sign of neg values +dpthdpba: + mov pbtemp, ecx // save pb for later use + // pa = abs(pav) + mov eax, patemp + test eax, 0x80000000 + jz dpthdpaa + neg eax // reverse sign of neg values +dpthdpaa: + mov patemp, eax // save pa for later use + // test if pa <= pb + cmp eax, ecx + jna dpthdabb + // pa > pb; now test if pb <= pc + cmp ecx, pctemp + jna dpthdbbc + // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp) + mov cl, [esi + edx] // load Prior(x-bpp) into cl + jmp dpthdpaeth +dpthdbbc: + // pb <= pc; Raw(x) = Paeth(x) + Prior(x) + mov cl, [esi + ebx] // load Prior(x) into cl + jmp dpthdpaeth +dpthdabb: + // pa <= pb; now test if pa <= pc + cmp eax, pctemp + jna dpthdabc + // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp) + mov cl, [esi + edx] // load Prior(x-bpp) into cl + jmp dpthdpaeth +dpthdabc: + // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp) + mov cl, [edi + edx] // load Raw(x-bpp) into cl +dpthdpaeth: + inc ebx + inc edx + // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256 + add [edi + ebx - 1], cl + cmp ebx, FullLength + jb dpthdlp +dpthdend: + } // end _asm block + } + return; // No need to go further with this one + } // end switch ( bpp ) + _asm + { + // MMX acceleration complete now do clean-up + // Check if any remaining bytes left to decode + mov ebx, MMXLength + cmp ebx, FullLength + jnb dpthend + mov edi, row + mov esi, prev_row + // Do Paeth decode for remaining bytes + mov edx, ebx + xor ecx, ecx // zero ecx before using cl & cx in loop below + sub edx, bpp // Set edx = ebx - bpp +dpthlp2: + xor eax, eax + // pav = p - a = (a + b - c) - a = b - c + mov al, [esi + ebx] // load Prior(x) into al + mov cl, [esi + edx] // load Prior(x-bpp) into cl + sub eax, ecx // subtract Prior(x-bpp) + mov patemp, eax // Save pav for later use + xor eax, eax + // pbv = p - b = (a + b - c) - b = a - c + mov al, [edi + edx] // load Raw(x-bpp) into al + sub eax, ecx // subtract Prior(x-bpp) + mov ecx, eax + // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv + add eax, patemp // pcv = pav + pbv + // pc = abs(pcv) + test eax, 0x80000000 + jz dpthpca2 + neg eax // reverse sign of neg values +dpthpca2: + mov pctemp, eax // save pc for later use + // pb = abs(pbv) + test ecx, 0x80000000 + jz dpthpba2 + neg ecx // reverse sign of neg values +dpthpba2: + mov pbtemp, ecx // save pb for later use + // pa = abs(pav) + mov eax, patemp + test eax, 0x80000000 + jz dpthpaa2 + neg eax // reverse sign of neg values +dpthpaa2: + mov patemp, eax // save pa for later use + // test if pa <= pb + cmp eax, ecx + jna dpthabb2 + // pa > pb; now test if pb <= pc + cmp ecx, pctemp + jna dpthbbc2 + // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp) + mov cl, [esi + edx] // load Prior(x-bpp) into cl + jmp dpthpaeth2 +dpthbbc2: + // pb <= pc; Raw(x) = Paeth(x) + Prior(x) + mov cl, [esi + ebx] // load Prior(x) into cl + jmp dpthpaeth2 +dpthabb2: + // pa <= pb; now test if pa <= pc + cmp eax, pctemp + jna dpthabc2 + // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp) + mov cl, [esi + edx] // load Prior(x-bpp) into cl + jmp dpthpaeth2 +dpthabc2: + // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp) + mov cl, [edi + edx] // load Raw(x-bpp) into cl +dpthpaeth2: + inc ebx + inc edx + // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256 + add [edi + ebx - 1], cl + cmp ebx, FullLength + jb dpthlp2 +dpthend: + emms // End MMX instructions; prep for possible FP instrs. + } // end _asm block +} + +// Optimized code for PNG Sub filter decoder +void /* PRIVATE */ +png_read_filter_row_mmx_sub(png_row_infop row_info, png_bytep row) +{ + //int test; + int bpp; + png_uint_32 FullLength; + png_uint_32 MMXLength; + int diff; + + bpp = (row_info->pixel_depth + 7) >> 3; // Get # bytes per pixel + FullLength = row_info->rowbytes - bpp; // # of bytes to filter + _asm { + mov edi, row + mov esi, edi // lp = row + add edi, bpp // rp = row + bpp + xor eax, eax + // get # of bytes to alignment + mov diff, edi // take start of row + add diff, 0xf // add 7 + 8 to incr past + // alignment boundary + xor ebx, ebx + and diff, 0xfffffff8 // mask to alignment boundary + sub diff, edi // subtract from start ==> value + // ebx at alignment + jz dsubgo + // fix alignment +dsublp1: + mov al, [esi+ebx] + add [edi+ebx], al + inc ebx + cmp ebx, diff + jb dsublp1 +dsubgo: + mov ecx, FullLength + mov edx, ecx + sub edx, ebx // subtract alignment fix + and edx, 0x00000007 // calc bytes over mult of 8 + sub ecx, edx // drop over bytes from length + mov MMXLength, ecx + } // end _asm block + + // Now do the math for the rest of the row + switch ( bpp ) + { + case 3: + { + ActiveMask.use = 0x0000ffffff000000; + ShiftBpp.use = 24; // == 3 * 8 + ShiftRem.use = 40; // == 64 - 24 + _asm { + mov edi, row + movq mm7, ActiveMask // Load ActiveMask for 2nd active byte group + mov esi, edi // lp = row + add edi, bpp // rp = row + bpp + movq mm6, mm7 + mov ebx, diff + psllq mm6, ShiftBpp // Move mask in mm6 to cover 3rd active + // byte group + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm1, [edi+ebx-8] +dsub3lp: + psrlq mm1, ShiftRem // Shift data for adding 1st bpp bytes + // no need for mask; shift clears inactive bytes + // Add 1st active group + movq mm0, [edi+ebx] + paddb mm0, mm1 + // Add 2nd active group + movq mm1, mm0 // mov updated Raws to mm1 + psllq mm1, ShiftBpp // shift data to position correctly + pand mm1, mm7 // mask to use only 2nd active group + paddb mm0, mm1 + // Add 3rd active group + movq mm1, mm0 // mov updated Raws to mm1 + psllq mm1, ShiftBpp // shift data to position correctly + pand mm1, mm6 // mask to use only 3rd active group + add ebx, 8 + paddb mm0, mm1 + cmp ebx, MMXLength + movq [edi+ebx-8], mm0 // Write updated Raws back to array + // Prep for doing 1st add at top of loop + movq mm1, mm0 + jb dsub3lp + } // end _asm block + } + break; + + case 1: + { + // Placed here just in case this is a duplicate of the + // non-MMX code for the SUB filter in png_read_filter_row below + // + // png_bytep rp; + // png_bytep lp; + // png_uint_32 i; + // bpp = (row_info->pixel_depth + 7) >> 3; + // for (i = (png_uint_32)bpp, rp = row + bpp, lp = row; + // i < row_info->rowbytes; i++, rp++, lp++) + // { + // *rp = (png_byte)(((int)(*rp) + (int)(*lp)) & 0xff); + // } + _asm { + mov ebx, diff + mov edi, row + cmp ebx, FullLength + jnb dsub1end + mov esi, edi // lp = row + xor eax, eax + add edi, bpp // rp = row + bpp +dsub1lp: + mov al, [esi+ebx] + add [edi+ebx], al + inc ebx + cmp ebx, FullLength + jb dsub1lp +dsub1end: + } // end _asm block + } + return; + + case 6: + case 7: + case 4: + case 5: + { + ShiftBpp.use = bpp << 3; + ShiftRem.use = 64 - ShiftBpp.use; + _asm { + mov edi, row + mov ebx, diff + mov esi, edi // lp = row + add edi, bpp // rp = row + bpp + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm1, [edi+ebx-8] +dsub4lp: + psrlq mm1, ShiftRem // Shift data for adding 1st bpp bytes + // no need for mask; shift clears inactive bytes + movq mm0, [edi+ebx] + paddb mm0, mm1 + // Add 2nd active group + movq mm1, mm0 // mov updated Raws to mm1 + psllq mm1, ShiftBpp // shift data to position correctly + // there is no need for any mask + // since shift clears inactive bits/bytes + add ebx, 8 + paddb mm0, mm1 + cmp ebx, MMXLength + movq [edi+ebx-8], mm0 + movq mm1, mm0 // Prep for doing 1st add at top of loop + jb dsub4lp + } // end _asm block + } + break; + + case 2: + { + ActiveMask.use = 0x00000000ffff0000; + ShiftBpp.use = 16; // == 2 * 8 + ShiftRem.use = 48; // == 64 - 16 + _asm { + movq mm7, ActiveMask // Load ActiveMask for 2nd active byte group + mov ebx, diff + movq mm6, mm7 + mov edi, row + psllq mm6, ShiftBpp // Move mask in mm6 to cover 3rd active + // byte group + mov esi, edi // lp = row + movq mm5, mm6 + add edi, bpp // rp = row + bpp + psllq mm5, ShiftBpp // Move mask in mm5 to cover 4th active + // byte group + // PRIME the pump (load the first Raw(x-bpp) data set + movq mm1, [edi+ebx-8] +dsub2lp: + // Add 1st active group + psrlq mm1, ShiftRem // Shift data for adding 1st bpp bytes + // no need for mask; shift clears inactive + // bytes + movq mm0, [edi+ebx] + paddb mm0, mm1 + // Add 2nd active group + movq mm1, mm0 // mov updated Raws to mm1 + psllq mm1, ShiftBpp // shift data to position correctly + pand mm1, mm7 // mask to use only 2nd active group + paddb mm0, mm1 + // Add 3rd active group + movq mm1, mm0 // mov updated Raws to mm1 + psllq mm1, ShiftBpp // shift data to position correctly + pand mm1, mm6 // mask to use only 3rd active group + paddb mm0, mm1 + // Add 4th active group + movq mm1, mm0 // mov updated Raws to mm1 + psllq mm1, ShiftBpp // shift data to position correctly + pand mm1, mm5 // mask to use only 4th active group + add ebx, 8 + paddb mm0, mm1 + cmp ebx, MMXLength + movq [edi+ebx-8], mm0 // Write updated Raws back to array + movq mm1, mm0 // Prep for doing 1st add at top of loop + jb dsub2lp + } // end _asm block + } + break; + case 8: + { + _asm { + mov edi, row + mov ebx, diff + mov esi, edi // lp = row + add edi, bpp // rp = row + bpp + mov ecx, MMXLength + movq mm7, [edi+ebx-8] // PRIME the pump (load the first + // Raw(x-bpp) data set + and ecx, 0x0000003f // calc bytes over mult of 64 +dsub8lp: + movq mm0, [edi+ebx] // Load Sub(x) for 1st 8 bytes + paddb mm0, mm7 + movq mm1, [edi+ebx+8] // Load Sub(x) for 2nd 8 bytes + movq [edi+ebx], mm0 // Write Raw(x) for 1st 8 bytes + // Now mm0 will be used as Raw(x-bpp) for + // the 2nd group of 8 bytes. This will be + // repeated for each group of 8 bytes with + // the 8th group being used as the Raw(x-bpp) + // for the 1st group of the next loop. + paddb mm1, mm0 + movq mm2, [edi+ebx+16] // Load Sub(x) for 3rd 8 bytes + movq [edi+ebx+8], mm1 // Write Raw(x) for 2nd 8 bytes + paddb mm2, mm1 + movq mm3, [edi+ebx+24] // Load Sub(x) for 4th 8 bytes + movq [edi+ebx+16], mm2 // Write Raw(x) for 3rd 8 bytes + paddb mm3, mm2 + movq mm4, [edi+ebx+32] // Load Sub(x) for 5th 8 bytes + movq [edi+ebx+24], mm3 // Write Raw(x) for 4th 8 bytes + paddb mm4, mm3 + movq mm5, [edi+ebx+40] // Load Sub(x) for 6th 8 bytes + movq [edi+ebx+32], mm4 // Write Raw(x) for 5th 8 bytes + paddb mm5, mm4 + movq mm6, [edi+ebx+48] // Load Sub(x) for 7th 8 bytes + movq [edi+ebx+40], mm5 // Write Raw(x) for 6th 8 bytes + paddb mm6, mm5 + movq mm7, [edi+ebx+56] // Load Sub(x) for 8th 8 bytes + movq [edi+ebx+48], mm6 // Write Raw(x) for 7th 8 bytes + add ebx, 64 + paddb mm7, mm6 + cmp ebx, ecx + movq [edi+ebx-8], mm7 // Write Raw(x) for 8th 8 bytes + jb dsub8lp + cmp ebx, MMXLength + jnb dsub8lt8 +dsub8lpA: + movq mm0, [edi+ebx] + add ebx, 8 + paddb mm0, mm7 + cmp ebx, MMXLength + movq [edi+ebx-8], mm0 // use -8 to offset early add to ebx + movq mm7, mm0 // Move calculated Raw(x) data to mm1 to + // be the new Raw(x-bpp) for the next loop + jb dsub8lpA +dsub8lt8: + } // end _asm block + } + break; + + default: // bpp greater than 8 bytes + { + _asm { + mov ebx, diff + mov edi, row + mov esi, edi // lp = row + add edi, bpp // rp = row + bpp +dsubAlp: + movq mm0, [edi+ebx] + movq mm1, [esi+ebx] + add ebx, 8 + paddb mm0, mm1 + cmp ebx, MMXLength + movq [edi+ebx-8], mm0 // mov does not affect flags; -8 to offset + // add ebx + jb dsubAlp + } // end _asm block + } + break; + + } // end switch ( bpp ) + + _asm { + mov ebx, MMXLength + mov edi, row + cmp ebx, FullLength + jnb dsubend + mov esi, edi // lp = row + xor eax, eax + add edi, bpp // rp = row + bpp +dsublp2: + mov al, [esi+ebx] + add [edi+ebx], al + inc ebx + cmp ebx, FullLength + jb dsublp2 +dsubend: + emms // End MMX instructions; prep for possible FP instrs. + } // end _asm block +} + +// Optimized code for PNG Up filter decoder +void /* PRIVATE */ +png_read_filter_row_mmx_up(png_row_infop row_info, png_bytep row, + png_bytep prev_row) +{ + png_uint_32 len; + len = row_info->rowbytes; // # of bytes to filter + _asm { + mov edi, row + // get # of bytes to alignment + mov ecx, edi + xor ebx, ebx + add ecx, 0x7 + xor eax, eax + and ecx, 0xfffffff8 + mov esi, prev_row + sub ecx, edi + jz dupgo + // fix alignment +duplp1: + mov al, [edi+ebx] + add al, [esi+ebx] + inc ebx + cmp ebx, ecx + mov [edi + ebx-1], al // mov does not affect flags; -1 to offset inc ebx + jb duplp1 +dupgo: + mov ecx, len + mov edx, ecx + sub edx, ebx // subtract alignment fix + and edx, 0x0000003f // calc bytes over mult of 64 + sub ecx, edx // drop over bytes from length + // Unrolled loop - use all MMX registers and interleave to reduce + // number of branch instructions (loops) and reduce partial stalls +duploop: + movq mm1, [esi+ebx] + movq mm0, [edi+ebx] + movq mm3, [esi+ebx+8] + paddb mm0, mm1 + movq mm2, [edi+ebx+8] + movq [edi+ebx], mm0 + paddb mm2, mm3 + movq mm5, [esi+ebx+16] + movq [edi+ebx+8], mm2 + movq mm4, [edi+ebx+16] + movq mm7, [esi+ebx+24] + paddb mm4, mm5 + movq mm6, [edi+ebx+24] + movq [edi+ebx+16], mm4 + paddb mm6, mm7 + movq mm1, [esi+ebx+32] + movq [edi+ebx+24], mm6 + movq mm0, [edi+ebx+32] + movq mm3, [esi+ebx+40] + paddb mm0, mm1 + movq mm2, [edi+ebx+40] + movq [edi+ebx+32], mm0 + paddb mm2, mm3 + movq mm5, [esi+ebx+48] + movq [edi+ebx+40], mm2 + movq mm4, [edi+ebx+48] + movq mm7, [esi+ebx+56] + paddb mm4, mm5 + movq mm6, [edi+ebx+56] + movq [edi+ebx+48], mm4 + add ebx, 64 + paddb mm6, mm7 + cmp ebx, ecx + movq [edi+ebx-8], mm6 // (+56)movq does not affect flags; + // -8 to offset add ebx + jb duploop + + cmp edx, 0 // Test for bytes over mult of 64 + jz dupend + + + // 2 lines added by lcreeve at netins.net + // (mail 11 Jul 98 in png-implement list) + cmp edx, 8 //test for less than 8 bytes + jb duplt8 + + + add ecx, edx + and edx, 0x00000007 // calc bytes over mult of 8 + sub ecx, edx // drop over bytes from length + jz duplt8 + // Loop using MMX registers mm0 & mm1 to update 8 bytes simultaneously +duplpA: + movq mm1, [esi+ebx] + movq mm0, [edi+ebx] + add ebx, 8 + paddb mm0, mm1 + cmp ebx, ecx + movq [edi+ebx-8], mm0 // movq does not affect flags; -8 to offset add ebx + jb duplpA + cmp edx, 0 // Test for bytes over mult of 8 + jz dupend +duplt8: + xor eax, eax + add ecx, edx // move over byte count into counter + // Loop using x86 registers to update remaining bytes +duplp2: + mov al, [edi + ebx] + add al, [esi + ebx] + inc ebx + cmp ebx, ecx + mov [edi + ebx-1], al // mov does not affect flags; -1 to offset inc ebx + jb duplp2 +dupend: + // Conversion of filtered row completed + emms // End MMX instructions; prep for possible FP instrs. + } // end _asm block +} + + +// Optimized png_read_filter_row routines +void /* PRIVATE */ +png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep + row, png_bytep prev_row, int filter) +{ +#ifdef PNG_DEBUG + char filnm[10]; +#endif + + if (mmx_supported == 2) { +#if !defined(PNG_1_0_X) + /* this should have happened in png_init_mmx_flags() already */ + png_warning(png_ptr, "asm_flags may not have been initialized"); +#endif + png_mmx_support(); + } + +#ifdef PNG_DEBUG + png_debug(1, "in png_read_filter_row\n"); + switch (filter) + { + case 0: sprintf(filnm, "none"); + break; +#if !defined(PNG_1_0_X) + case 1: sprintf(filnm, "sub-%s", + (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_SUB)? "MMX" : "x86"); + break; + case 2: sprintf(filnm, "up-%s", + (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_UP)? "MMX" : "x86"); + break; + case 3: sprintf(filnm, "avg-%s", + (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_AVG)? "MMX" : "x86"); + break; + case 4: sprintf(filnm, "Paeth-%s", + (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH)? "MMX":"x86"); + break; +#else + case 1: sprintf(filnm, "sub"); + break; + case 2: sprintf(filnm, "up"); + break; + case 3: sprintf(filnm, "avg"); + break; + case 4: sprintf(filnm, "Paeth"); + break; +#endif + default: sprintf(filnm, "unknw"); + break; + } + png_debug2(0,"row=%5d, %s, ", png_ptr->row_number, filnm); + png_debug2(0, "pd=%2d, b=%d, ", (int)row_info->pixel_depth, + (int)((row_info->pixel_depth + 7) >> 3)); + png_debug1(0,"len=%8d, ", row_info->rowbytes); +#endif /* PNG_DEBUG */ + + switch (filter) + { + case PNG_FILTER_VALUE_NONE: + break; + + case PNG_FILTER_VALUE_SUB: + { +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_SUB) && + (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) && + (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold)) +#else + if (mmx_supported) +#endif + { + png_read_filter_row_mmx_sub(row_info, row); + } + else + { + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_bytep rp = row + bpp; + png_bytep lp = row; + + for (i = bpp; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff); + rp++; + } + } + break; + } + + case PNG_FILTER_VALUE_UP: + { +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_UP) && + (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) && + (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold)) +#else + if (mmx_supported) +#endif + { + png_read_filter_row_mmx_up(row_info, row, prev_row); + } + else + { + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_bytep rp = row; + png_bytep pp = prev_row; + + for (i = 0; i < istop; ++i) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + } + break; + } + + case PNG_FILTER_VALUE_AVG: + { +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_AVG) && + (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) && + (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold)) +#else + if (mmx_supported) +#endif + { + png_read_filter_row_mmx_avg(row_info, row, prev_row); + } + else + { + png_uint_32 i; + png_bytep rp = row; + png_bytep pp = prev_row; + png_bytep lp = row; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_uint_32 istop = row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + + ((int)(*pp++) >> 1)) & 0xff); + rp++; + } + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + + ((int)(*pp++ + *lp++) >> 1)) & 0xff); + rp++; + } + } + break; + } + + case PNG_FILTER_VALUE_PAETH: + { +#if !defined(PNG_1_0_X) + if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH) && + (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) && + (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold)) +#else + if (mmx_supported) +#endif + { + png_read_filter_row_mmx_paeth(row_info, row, prev_row); + } + else + { + png_uint_32 i; + png_bytep rp = row; + png_bytep pp = prev_row; + png_bytep lp = row; + png_bytep cp = prev_row; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_uint_32 istop=row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + + for (i = 0; i < istop; i++) // use leftover rp,pp + { + int a, b, c, pa, pb, pc, p; + + a = *lp++; + b = *pp++; + c = *cp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + /* + if (pa <= pb && pa <= pc) + p = a; + else if (pb <= pc) + p = b; + else + p = c; + */ + + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; + + *rp = (png_byte)(((int)(*rp) + p) & 0xff); + rp++; + } + } + break; + } + + default: + png_warning(png_ptr, "Ignoring bad row filter type"); + *row=0; + break; + } +} + +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED && PNG_USE_PNGVCRD */ diff --git a/com32/lib/libpng/pngwio.c b/com32/lib/libpng/pngwio.c new file mode 100644 index 0000000..d5d61f4 --- /dev/null +++ b/com32/lib/libpng/pngwio.c @@ -0,0 +1,228 @@ + +/* pngwio.c - functions for data output + * + * libpng 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file provides a location for all output. Users who need + * special handling are expected to write functions that have the same + * arguments as these and perform similar functions, but that possibly + * use different output methods. Note that you shouldn't change these + * functions, but rather write replacement functions and then change + * them at run time with png_set_write_fn(...). + */ + +#define PNG_INTERNAL +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Write the data to whatever output you are using. The default routine + writes to a file pointer. Note that this routine sometimes gets called + with very small lengths, so you should implement some kind of simple + buffering if you are using unbuffered writes. This should never be asked + to write more than 64K on a 16 bit machine. */ + +void /* PRIVATE */ +png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + if (png_ptr->write_data_fn != NULL ) + (*(png_ptr->write_data_fn))(png_ptr, data, length); + else + png_error(png_ptr, "Call to NULL write function"); +} + +#if !defined(PNG_NO_STDIO) +/* This is the function that does the actual writing of data. If you are + not writing to a standard C stream, you should create a replacement + write_data function and use it at run time with png_set_write_fn(), rather + than changing the library. */ +#ifndef USE_FAR_KEYWORD +void PNGAPI +png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + +#if defined(_WIN32_WCE) + if ( !WriteFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) + check = 0; +#else + check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr)); +#endif + if (check != length) + png_error(png_ptr, "Write Error"); +} +#else +/* this is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +void PNGAPI +png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + png_byte *near_data; /* Needs to be "png_byte *" instead of "png_bytep" */ + png_FILE_p io_ptr; + + /* Check if data really is near. If so, use usual code. */ + near_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)near_data == data) + { +#if defined(_WIN32_WCE) + if ( !WriteFile(io_ptr, near_data, length, &check, NULL) ) + check = 0; +#else + check = fwrite(near_data, 1, length, io_ptr); +#endif + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t written, remaining, err; + check = 0; + remaining = length; + do + { + written = MIN(NEAR_BUF_SIZE, remaining); + png_memcpy(buf, data, written); /* copy far buffer to near buffer */ +#if defined(_WIN32_WCE) + if ( !WriteFile(io_ptr, buf, written, &err, NULL) ) + err = 0; +#else + err = fwrite(buf, 1, written, io_ptr); +#endif + if (err != written) + break; + else + check += err; + data += written; + remaining -= written; + } + while (remaining != 0); + } + if (check != length) + png_error(png_ptr, "Write Error"); +} + +#endif +#endif + +/* This function is called to output any data pending writing (normally + to disk). After png_flush is called, there should be no data pending + writing in any buffers. */ +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +void /* PRIVATE */ +png_flush(png_structp png_ptr) +{ + if (png_ptr->output_flush_fn != NULL) + (*(png_ptr->output_flush_fn))(png_ptr); +} + +#if !defined(PNG_NO_STDIO) +void PNGAPI +png_default_flush(png_structp png_ptr) +{ +#if !defined(_WIN32_WCE) + png_FILE_p io_ptr; + io_ptr = (png_FILE_p)CVT_PTR((png_ptr->io_ptr)); + if (io_ptr != NULL) + fflush(io_ptr); +#endif +} +#endif +#endif + +/* This function allows the application to supply new output functions for + libpng if standard C streams aren't being used. + + This function takes as its arguments: + png_ptr - pointer to a png output data structure + io_ptr - pointer to user supplied structure containing info about + the output functions. May be NULL. + write_data_fn - pointer to a new output function that takes as its + arguments a pointer to a png_struct, a pointer to + data to be written, and a 32-bit unsigned int that is + the number of bytes to be written. The new write + function should call png_error(png_ptr, "Error msg") + to exit and output any fatal error messages. + flush_data_fn - pointer to a new flush function that takes as its + arguments a pointer to a png_struct. After a call to + the flush function, there should be no data in any buffers + or pending transmission. If the output method doesn't do + any buffering of ouput, a function prototype must still be + supplied although it doesn't have to do anything. If + PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile + time, output_flush_fn will be ignored, although it must be + supplied for compatibility. */ +void PNGAPI +png_set_write_fn(png_structp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn) +{ + png_ptr->io_ptr = io_ptr; + +#if !defined(PNG_NO_STDIO) + if (write_data_fn != NULL) + png_ptr->write_data_fn = write_data_fn; + else + png_ptr->write_data_fn = png_default_write_data; +#else + png_ptr->write_data_fn = write_data_fn; +#endif + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +#if !defined(PNG_NO_STDIO) + if (output_flush_fn != NULL) + png_ptr->output_flush_fn = output_flush_fn; + else + png_ptr->output_flush_fn = png_default_flush; +#else + png_ptr->output_flush_fn = output_flush_fn; +#endif +#endif /* PNG_WRITE_FLUSH_SUPPORTED */ + + /* It is an error to read while writing a png file */ + if (png_ptr->read_data_fn != NULL) + { + png_ptr->read_data_fn = NULL; + png_warning(png_ptr, + "Attempted to set both read_data_fn and write_data_fn in"); + png_warning(png_ptr, + "the same structure. Resetting read_data_fn to NULL."); + } +} + +#if defined(USE_FAR_KEYWORD) +#if defined(_MSC_VER) +void *png_far_to_near(png_structp png_ptr,png_voidp ptr, int check) +{ + void *near_ptr; + void FAR *far_ptr; + FP_OFF(near_ptr) = FP_OFF(ptr); + far_ptr = (void FAR *)near_ptr; + if(check != 0) + if(FP_SEG(ptr) != FP_SEG(far_ptr)) + png_error(png_ptr,"segment lost in conversion"); + return(near_ptr); +} +# else +void *png_far_to_near(png_structp png_ptr,png_voidp ptr, int check) +{ + void *near_ptr; + void FAR *far_ptr; + near_ptr = (void FAR *)ptr; + far_ptr = (void FAR *)near_ptr; + if(check != 0) + if(far_ptr != ptr) + png_error(png_ptr,"segment lost in conversion"); + return(near_ptr); +} +# endif +# endif +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/com32/lib/libpng/pngwrite.c b/com32/lib/libpng/pngwrite.c new file mode 100644 index 0000000..3246fda --- /dev/null +++ b/com32/lib/libpng/pngwrite.c @@ -0,0 +1,1464 @@ + +/* pngwrite.c - general routines to write a PNG file + * + * libpng 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +/* get internal access to png.h */ +#define PNG_INTERNAL +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Writes all the PNG information. This is the suggested way to use the + * library. If you have a new chunk to add, make a function to write it, + * and put it in the correct location here. If you want the chunk written + * after the image data, put it in png_write_end(). I strongly encourage + * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing + * the chunk, as that will keep the code from breaking if you want to just + * write a plain PNG file. If you have long comments, I suggest writing + * them in png_write_end(), and compressing them. + */ +void PNGAPI +png_write_info_before_PLTE(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_write_info_before_PLTE\n"); + if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) + { + png_write_sig(png_ptr); /* write PNG signature */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) + if((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE)&&(png_ptr->mng_features_permitted)) + { + png_warning(png_ptr,"MNG features are not allowed in a PNG datastream\n"); + png_ptr->mng_features_permitted=0; + } +#endif + /* write IHDR information. */ + png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type, + info_ptr->filter_type, +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + info_ptr->interlace_type); +#else + 0); +#endif + /* the rest of these check to see if the valid field has the appropriate + flag set, and if it does, writes the chunk. */ +#if defined(PNG_WRITE_gAMA_SUPPORTED) + if (info_ptr->valid & PNG_INFO_gAMA) + { +# ifdef PNG_FLOATING_POINT_SUPPORTED + png_write_gAMA(png_ptr, info_ptr->gamma); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_write_gAMA_fixed(png_ptr, info_ptr->int_gamma); +# endif +#endif + } +#endif +#if defined(PNG_WRITE_sRGB_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sRGB) + png_write_sRGB(png_ptr, (int)info_ptr->srgb_intent); +#endif +#if defined(PNG_WRITE_iCCP_SUPPORTED) + if (info_ptr->valid & PNG_INFO_iCCP) + png_write_iCCP(png_ptr, info_ptr->iccp_name, PNG_COMPRESSION_TYPE_BASE, + info_ptr->iccp_profile, (int)info_ptr->iccp_proflen); +#endif +#if defined(PNG_WRITE_sBIT_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sBIT) + png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type); +#endif +#if defined(PNG_WRITE_cHRM_SUPPORTED) + if (info_ptr->valid & PNG_INFO_cHRM) + { +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_write_cHRM(png_ptr, + info_ptr->x_white, info_ptr->y_white, + info_ptr->x_red, info_ptr->y_red, + info_ptr->x_green, info_ptr->y_green, + info_ptr->x_blue, info_ptr->y_blue); +#else +# ifdef PNG_FIXED_POINT_SUPPORTED + png_write_cHRM_fixed(png_ptr, + info_ptr->int_x_white, info_ptr->int_y_white, + info_ptr->int_x_red, info_ptr->int_y_red, + info_ptr->int_x_green, info_ptr->int_y_green, + info_ptr->int_x_blue, info_ptr->int_y_blue); +# endif +#endif + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks\n"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep=png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && !(up->location & PNG_HAVE_PLTE) && + !(up->location & PNG_HAVE_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif + png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE; + } +} + +void PNGAPI +png_write_info(png_structp png_ptr, png_infop info_ptr) +{ +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) + int i; +#endif + + png_debug(1, "in png_write_info\n"); + + png_write_info_before_PLTE(png_ptr, info_ptr); + + if (info_ptr->valid & PNG_INFO_PLTE) + png_write_PLTE(png_ptr, info_ptr->palette, + (png_uint_32)info_ptr->num_palette); + else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_error(png_ptr, "Valid palette required for paletted images\n"); + +#if defined(PNG_WRITE_tRNS_SUPPORTED) + if (info_ptr->valid & PNG_INFO_tRNS) + { +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) + /* invert the alpha channel (in tRNS) */ + if ((png_ptr->transformations & PNG_INVERT_ALPHA) && + info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + int j; + for (j=0; j<(int)info_ptr->num_trans; j++) + info_ptr->trans[j] = (png_byte)(255 - info_ptr->trans[j]); + } +#endif + png_write_tRNS(png_ptr, info_ptr->trans, &(info_ptr->trans_values), + info_ptr->num_trans, info_ptr->color_type); + } +#endif +#if defined(PNG_WRITE_bKGD_SUPPORTED) + if (info_ptr->valid & PNG_INFO_bKGD) + png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type); +#endif +#if defined(PNG_WRITE_hIST_SUPPORTED) + if (info_ptr->valid & PNG_INFO_hIST) + png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette); +#endif +#if defined(PNG_WRITE_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset, + info_ptr->offset_unit_type); +#endif +#if defined(PNG_WRITE_pCAL_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pCAL) + png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0, + info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams, + info_ptr->pcal_units, info_ptr->pcal_params); +#endif +#if defined(PNG_WRITE_sCAL_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sCAL) +#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) + png_write_sCAL(png_ptr, (int)info_ptr->scal_unit, + info_ptr->scal_pixel_width, info_ptr->scal_pixel_height); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit, + info_ptr->scal_s_width, info_ptr->scal_s_height); +#else + png_warning(png_ptr, + "png_write_sCAL not supported; sCAL chunk not written.\n"); +#endif +#endif +#endif +#if defined(PNG_WRITE_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit, + info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type); +#endif +#if defined(PNG_WRITE_tIME_SUPPORTED) + if (info_ptr->valid & PNG_INFO_tIME) + { + png_write_tIME(png_ptr, &(info_ptr->mod_time)); + png_ptr->mode |= PNG_WROTE_tIME; + } +#endif +#if defined(PNG_WRITE_sPLT_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sPLT) + for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) + png_write_sPLT(png_ptr, info_ptr->splt_palettes + i); +#endif +#if defined(PNG_WRITE_TEXT_SUPPORTED) + /* Check to see if we need to write text chunks */ + for (i = 0; i < info_ptr->num_text; i++) + { + png_debug2(2, "Writing header text chunk %d, type %d\n", i, + info_ptr->text[i].compression); + /* an internationalized chunk? */ + if (info_ptr->text[i].compression > 0) + { +#if defined(PNG_WRITE_iTXt_SUPPORTED) + /* write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); +#else + png_warning(png_ptr, "Unable to write international text\n"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + /* If we want a compressed text chunk */ + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt) + { +#if defined(PNG_WRITE_zTXt_SUPPORTED) + /* write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0, + info_ptr->text[i].compression); +#else + png_warning(png_ptr, "Unable to write compressed text\n"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; + } + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + { +#if defined(PNG_WRITE_tEXt_SUPPORTED) + /* write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, + 0); +#else + png_warning(png_ptr, "Unable to write uncompressed text\n"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks\n"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep=png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && (up->location & PNG_HAVE_PLTE) && + !(up->location & PNG_HAVE_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif +} + +/* Writes the end of the PNG file. If you don't want to write comments or + * time information, you can pass NULL for info. If you already wrote these + * in png_write_info(), do not write them again here. If you have long + * comments, I suggest writing them here, and compressing them. + */ +void PNGAPI +png_write_end(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_write_end\n"); + if (!(png_ptr->mode & PNG_HAVE_IDAT)) + png_error(png_ptr, "No IDATs written into file"); + + /* see if user wants us to write information chunks */ + if (info_ptr != NULL) + { +#if defined(PNG_WRITE_TEXT_SUPPORTED) + int i; /* local index variable */ +#endif +#if defined(PNG_WRITE_tIME_SUPPORTED) + /* check to see if user has supplied a time chunk */ + if ((info_ptr->valid & PNG_INFO_tIME) && + !(png_ptr->mode & PNG_WROTE_tIME)) + png_write_tIME(png_ptr, &(info_ptr->mod_time)); +#endif +#if defined(PNG_WRITE_TEXT_SUPPORTED) + /* loop through comment chunks */ + for (i = 0; i < info_ptr->num_text; i++) + { + png_debug2(2, "Writing trailer text chunk %d, type %d\n", i, + info_ptr->text[i].compression); + /* an internationalized chunk? */ + if (info_ptr->text[i].compression > 0) + { +#if defined(PNG_WRITE_iTXt_SUPPORTED) + /* write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); +#else + png_warning(png_ptr, "Unable to write international text\n"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt) + { +#if defined(PNG_WRITE_zTXt_SUPPORTED) + /* write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0, + info_ptr->text[i].compression); +#else + png_warning(png_ptr, "Unable to write compressed text\n"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; + } + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + { +#if defined(PNG_WRITE_tEXt_SUPPORTED) + /* write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0); +#else + png_warning(png_ptr, "Unable to write uncompressed text\n"); +#endif + + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks\n"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep=png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && (up->location & PNG_AFTER_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif + } + + png_ptr->mode |= PNG_AFTER_IDAT; + + /* write end of PNG file */ + png_write_IEND(png_ptr); +#if 0 +/* This flush, added in libpng-1.0.8, causes some applications to crash + because they do not set png_ptr->output_flush_fn */ + png_flush(png_ptr); +#endif +} + +#if defined(PNG_WRITE_tIME_SUPPORTED) +#if !defined(_WIN32_WCE) +/* "time.h" functions are not supported on WindowsCE */ +void PNGAPI +png_convert_from_struct_tm(png_timep ptime, struct tm FAR * ttime) +{ + png_debug(1, "in png_convert_from_struct_tm\n"); + ptime->year = (png_uint_16)(1900 + ttime->tm_year); + ptime->month = (png_byte)(ttime->tm_mon + 1); + ptime->day = (png_byte)ttime->tm_mday; + ptime->hour = (png_byte)ttime->tm_hour; + ptime->minute = (png_byte)ttime->tm_min; + ptime->second = (png_byte)ttime->tm_sec; +} + +void PNGAPI +png_convert_from_time_t(png_timep ptime, time_t ttime) +{ + struct tm *tbuf; + + png_debug(1, "in png_convert_from_time_t\n"); + tbuf = gmtime(&ttime); + png_convert_from_struct_tm(ptime, tbuf); +} +#endif +#endif + +/* Initialize png_ptr structure, and allocate any memory needed */ +png_structp PNGAPI +png_create_write_struct(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_write_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, png_voidp_NULL, png_malloc_ptr_NULL, png_free_ptr_NULL)); +} + +/* Alternate initialize png_ptr structure, and allocate any memory needed */ +png_structp PNGAPI +png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + png_structp png_ptr; +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + int i; + png_debug(1, "in png_create_write_struct\n"); +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG, + (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr); +#else + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); +#endif /* PNG_USER_MEM_SUPPORTED */ + if (png_ptr == NULL) + return (NULL); + +#if !defined(PNG_1_0_X) +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED + png_init_mmx_flags(png_ptr); /* 1.2.0 addition */ +#endif +#endif /* PNG_1_0_X */ + + /* added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max=PNG_USER_WIDTH_MAX; + png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; +#endif + +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_ptr->jmpbuf)) +#endif + { + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf=NULL; + png_destroy_struct(png_ptr); + return (NULL); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); +#endif +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn); +#endif /* PNG_USER_MEM_SUPPORTED */ + png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn); + + i=0; + do + { + if(user_png_ver[i] != png_libpng_ver[i]) + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + } while (png_libpng_ver[i++]); + + if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) + { + /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so + * we must recompile any applications that use any older library version. + * For versions after libpng 1.0, we will be compatible, so we need + * only check the first digit. + */ + if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] || + (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) || + (user_png_ver[0] == '0' && user_png_ver[2] < '9')) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[80]; + if (user_png_ver) + { + sprintf(msg, "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + sprintf(msg, "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); +#endif +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "Incompatible libpng version in application and library"); + } + } + + /* initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + + png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL, + png_flush_ptr_NULL); + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT, + 1, png_doublep_NULL, png_doublep_NULL); +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* Applications that neglect to set up their own setjmp() and then encounter + a png_error() will longjmp here. Since the jmpbuf is then meaningless we + abort instead of returning. */ +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) + PNG_ABORT(); + png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); +#else + if (setjmp(png_ptr->jmpbuf)) + PNG_ABORT(); +#endif +#endif + return (png_ptr); +} + +/* Initialize png_ptr structure, and allocate any memory needed */ +#undef png_write_init +void PNGAPI +png_write_init(png_structp png_ptr) +{ + /* We only come here via pre-1.0.7-compiled applications */ + png_write_init_2(png_ptr, "1.0.6 or earlier", 0, 0); +} + +void PNGAPI +png_write_init_2(png_structp png_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size, png_size_t png_info_size) +{ + /* We only come here via pre-1.0.12-compiled applications */ +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + if(png_sizeof(png_struct) > png_struct_size || + png_sizeof(png_info) > png_info_size) + { + char msg[80]; + png_ptr->warning_fn=NULL; + if (user_png_ver) + { + sprintf(msg, "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + sprintf(msg, "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); + } +#endif + if(png_sizeof(png_struct) > png_struct_size) + { + png_ptr->error_fn=NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "The png struct allocated by the application for writing is too small."); + } + if(png_sizeof(png_info) > png_info_size) + { + png_ptr->error_fn=NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "The info struct allocated by the application for writing is too small."); + } + png_write_init_3(&png_ptr, user_png_ver, png_struct_size); +} + + +void PNGAPI +png_write_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size) +{ + png_structp png_ptr=*ptr_ptr; +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; /* to save current jump buffer */ +#endif + int i = 0; + do + { + if (user_png_ver[i] != png_libpng_ver[i]) + { +#ifdef PNG_LEGACY_SUPPORTED + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; +#else + png_ptr->warning_fn=NULL; + png_warning(png_ptr, + "Application uses deprecated png_write_init() and should be recompiled."); + break; +#endif + } + } while (png_libpng_ver[i++]); + + png_debug(1, "in png_write_init_3\n"); + +#ifdef PNG_SETJMP_SUPPORTED + /* save jump buffer and error functions */ + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); +#endif + + if (png_sizeof(png_struct) > png_struct_size) + { + png_destroy_struct(png_ptr); + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); + *ptr_ptr = png_ptr; + } + + /* reset all variables to 0 */ + png_memset(png_ptr, 0, png_sizeof (png_struct)); + + /* added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max=PNG_USER_WIDTH_MAX; + png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; +#endif + +#if !defined(PNG_1_0_X) +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED + png_init_mmx_flags(png_ptr); /* 1.2.0 addition */ +#endif +#endif /* PNG_1_0_X */ + +#ifdef PNG_SETJMP_SUPPORTED + /* restore jump buffer */ + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); +#endif + + png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL, + png_flush_ptr_NULL); + + /* initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT, + 1, png_doublep_NULL, png_doublep_NULL); +#endif +} + +/* Write a few rows of image data. If the image is interlaced, + * either you will have to write the 7 sub images, or, if you + * have called png_set_interlace_handling(), you will have to + * "write" the image seven times. + */ +void PNGAPI +png_write_rows(png_structp png_ptr, png_bytepp row, + png_uint_32 num_rows) +{ + png_uint_32 i; /* row counter */ + png_bytepp rp; /* row pointer */ + + png_debug(1, "in png_write_rows\n"); + /* loop through the rows */ + for (i = 0, rp = row; i < num_rows; i++, rp++) + { + png_write_row(png_ptr, *rp); + } +} + +/* Write the image. You only need to call this function once, even + * if you are writing an interlaced image. + */ +void PNGAPI +png_write_image(png_structp png_ptr, png_bytepp image) +{ + png_uint_32 i; /* row index */ + int pass, num_pass; /* pass variables */ + png_bytepp rp; /* points to current row */ + + png_debug(1, "in png_write_image\n"); +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + /* intialize interlace handling. If image is not interlaced, + this will set pass to 1 */ + num_pass = png_set_interlace_handling(png_ptr); +#else + num_pass = 1; +#endif + /* loop through passes */ + for (pass = 0; pass < num_pass; pass++) + { + /* loop through image */ + for (i = 0, rp = image; i < png_ptr->height; i++, rp++) + { + png_write_row(png_ptr, *rp); + } + } +} + +/* called by user to write a row of image data */ +void PNGAPI +png_write_row(png_structp png_ptr, png_bytep row) +{ + png_debug2(1, "in png_write_row (row %ld, pass %d)\n", + png_ptr->row_number, png_ptr->pass); + /* initialize transformations and other stuff if first time */ + if (png_ptr->row_number == 0 && png_ptr->pass == 0) + { + /* make sure we wrote the header info */ + if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) + png_error(png_ptr, + "png_write_info was never called before png_write_row."); + + /* check for transforms that have been set but were defined out */ +#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_warning(png_ptr, "PNG_WRITE_PACKSWAP_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined."); +#endif + + png_write_start_row(png_ptr); + } + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + /* if interlaced and not interested in row, return */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + switch (png_ptr->pass) + { + case 0: + if (png_ptr->row_number & 0x07) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 1: + if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 2: + if ((png_ptr->row_number & 0x07) != 4) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 3: + if ((png_ptr->row_number & 0x03) || png_ptr->width < 3) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 4: + if ((png_ptr->row_number & 0x03) != 2) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 5: + if ((png_ptr->row_number & 0x01) || png_ptr->width < 2) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 6: + if (!(png_ptr->row_number & 0x01)) + { + png_write_finish_row(png_ptr); + return; + } + break; + } + } +#endif + + /* set up row info for transformations */ + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->usr_width; + png_ptr->row_info.channels = png_ptr->usr_channels; + png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth; + png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth * + png_ptr->row_info.channels); + + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + png_debug1(3, "row_info->color_type = %d\n", png_ptr->row_info.color_type); + png_debug1(3, "row_info->width = %lu\n", png_ptr->row_info.width); + png_debug1(3, "row_info->channels = %d\n", png_ptr->row_info.channels); + png_debug1(3, "row_info->bit_depth = %d\n", png_ptr->row_info.bit_depth); + png_debug1(3, "row_info->pixel_depth = %d\n", png_ptr->row_info.pixel_depth); + png_debug1(3, "row_info->rowbytes = %lu\n", png_ptr->row_info.rowbytes); + + /* Copy user's row into buffer, leaving room for filter byte. */ + png_memcpy_check(png_ptr, png_ptr->row_buf + 1, row, + png_ptr->row_info.rowbytes); + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + /* handle interlacing */ + if (png_ptr->interlaced && png_ptr->pass < 6 && + (png_ptr->transformations & PNG_INTERLACE)) + { + png_do_write_interlace(&(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->pass); + /* this should always get caught above, but still ... */ + if (!(png_ptr->row_info.width)) + { + png_write_finish_row(png_ptr); + return; + } + } +#endif + + /* handle other transformations */ + if (png_ptr->transformations) + png_do_write_transformations(png_ptr); + +#if defined(PNG_MNG_FEATURES_SUPPORTED) + /* Write filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) + { + /* Intrapixel differencing */ + png_do_write_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1); + } +#endif + + /* Find a filter if necessary, filter the row and write it out. */ + png_write_find_filter(png_ptr, &(png_ptr->row_info)); + + if (png_ptr->write_row_fn != NULL) + (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); +} + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +/* Set the automatic flush interval or 0 to turn flushing off */ +void PNGAPI +png_set_flush(png_structp png_ptr, int nrows) +{ + png_debug(1, "in png_set_flush\n"); + png_ptr->flush_dist = (nrows < 0 ? 0 : nrows); +} + +/* flush the current output buffers now */ +void PNGAPI +png_write_flush(png_structp png_ptr) +{ + int wrote_IDAT; + + png_debug(1, "in png_write_flush\n"); + /* We have already written out all of the data */ + if (png_ptr->row_number >= png_ptr->num_rows) + return; + + do + { + int ret; + + /* compress the data */ + ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH); + wrote_IDAT = 0; + + /* check for compression errors */ + if (ret != Z_OK) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + + if (!(png_ptr->zstream.avail_out)) + { + /* write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, + png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + wrote_IDAT = 1; + } + } while(wrote_IDAT == 1); + + /* If there is any data left to be output, write it into a new IDAT */ + if (png_ptr->zbuf_size != png_ptr->zstream.avail_out) + { + /* write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + png_ptr->flush_rows = 0; + png_flush(png_ptr); +} +#endif /* PNG_WRITE_FLUSH_SUPPORTED */ + +/* free all memory used by the write */ +void PNGAPI +png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn = NULL; + png_voidp mem_ptr = NULL; +#endif + + png_debug(1, "in png_destroy_write_struct\n"); + if (png_ptr_ptr != NULL) + { + png_ptr = *png_ptr_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; + mem_ptr = png_ptr->mem_ptr; +#endif + } + + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (info_ptr != NULL) + { + png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + if (png_ptr->num_chunk_list) + { + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list=NULL; + png_ptr->num_chunk_list=0; + } +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } + + if (png_ptr != NULL) + { + png_write_destroy(png_ptr); +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + *png_ptr_ptr = NULL; + } +} + + +/* Free any memory used in png_ptr struct (old method) */ +void /* PRIVATE */ +png_write_destroy(png_structp png_ptr) +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; /* save jump buffer */ +#endif + png_error_ptr error_fn; + png_error_ptr warning_fn; + png_voidp error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn; +#endif + + png_debug(1, "in png_write_destroy\n"); + /* free any memory zlib uses */ + deflateEnd(&png_ptr->zstream); + + /* free our memory. png_free checks NULL for us. */ + png_free(png_ptr, png_ptr->zbuf); + png_free(png_ptr, png_ptr->row_buf); + png_free(png_ptr, png_ptr->prev_row); + png_free(png_ptr, png_ptr->sub_row); + png_free(png_ptr, png_ptr->up_row); + png_free(png_ptr, png_ptr->avg_row); + png_free(png_ptr, png_ptr->paeth_row); + +#if defined(PNG_TIME_RFC1123_SUPPORTED) + png_free(png_ptr, png_ptr->time_buffer); +#endif + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_free(png_ptr, png_ptr->prev_filters); + png_free(png_ptr, png_ptr->filter_weights); + png_free(png_ptr, png_ptr->inv_filter_weights); + png_free(png_ptr, png_ptr->filter_costs); + png_free(png_ptr, png_ptr->inv_filter_costs); +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* reset structure */ + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); +#endif + + error_fn = png_ptr->error_fn; + warning_fn = png_ptr->warning_fn; + error_ptr = png_ptr->error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; +#endif + + png_memset(png_ptr, 0, png_sizeof (png_struct)); + + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; + png_ptr->error_ptr = error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr->free_fn = free_fn; +#endif + +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); +#endif +} + +/* Allow the application to select one or more row filters to use. */ +void PNGAPI +png_set_filter(png_structp png_ptr, int method, int filters) +{ + png_debug(1, "in png_set_filter\n"); +#if defined(PNG_MNG_FEATURES_SUPPORTED) + if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (method == PNG_INTRAPIXEL_DIFFERENCING)) + method = PNG_FILTER_TYPE_BASE; +#endif + if (method == PNG_FILTER_TYPE_BASE) + { + switch (filters & (PNG_ALL_FILTERS | 0x07)) + { + case 5: + case 6: + case 7: png_warning(png_ptr, "Unknown row filter for method 0"); + case PNG_FILTER_VALUE_NONE: png_ptr->do_filter=PNG_FILTER_NONE; break; + case PNG_FILTER_VALUE_SUB: png_ptr->do_filter=PNG_FILTER_SUB; break; + case PNG_FILTER_VALUE_UP: png_ptr->do_filter=PNG_FILTER_UP; break; + case PNG_FILTER_VALUE_AVG: png_ptr->do_filter=PNG_FILTER_AVG; break; + case PNG_FILTER_VALUE_PAETH: png_ptr->do_filter=PNG_FILTER_PAETH;break; + default: png_ptr->do_filter = (png_byte)filters; break; + } + + /* If we have allocated the row_buf, this means we have already started + * with the image and we should have allocated all of the filter buffers + * that have been selected. If prev_row isn't already allocated, then + * it is too late to start using the filters that need it, since we + * will be missing the data in the previous row. If an application + * wants to start and stop using particular filters during compression, + * it should start out with all of the filters, and then add and + * remove them after the start of compression. + */ + if (png_ptr->row_buf != NULL) + { + if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL) + { + png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; + } + + if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Up filter after starting"); + png_ptr->do_filter &= ~PNG_FILTER_UP; + } + else + { + png_ptr->up_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; + } + } + + if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Average filter after starting"); + png_ptr->do_filter &= ~PNG_FILTER_AVG; + } + else + { + png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; + } + } + + if ((png_ptr->do_filter & PNG_FILTER_PAETH) && + png_ptr->paeth_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Paeth filter after starting"); + png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH); + } + else + { + png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; + } + } + + if (png_ptr->do_filter == PNG_NO_FILTERS) + png_ptr->do_filter = PNG_FILTER_NONE; + } + } + else + png_error(png_ptr, "Unknown custom filter method"); +} + +/* This allows us to influence the way in which libpng chooses the "best" + * filter for the current scanline. While the "minimum-sum-of-absolute- + * differences metric is relatively fast and effective, there is some + * question as to whether it can be improved upon by trying to keep the + * filtered data going to zlib more consistent, hopefully resulting in + * better compression. + */ +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* GRR 970116 */ +void PNGAPI +png_set_filter_heuristics(png_structp png_ptr, int heuristic_method, + int num_weights, png_doublep filter_weights, + png_doublep filter_costs) +{ + int i; + + png_debug(1, "in png_set_filter_heuristics\n"); + if (heuristic_method >= PNG_FILTER_HEURISTIC_LAST) + { + png_warning(png_ptr, "Unknown filter heuristic method"); + return; + } + + if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT) + { + heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED; + } + + if (num_weights < 0 || filter_weights == NULL || + heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED) + { + num_weights = 0; + } + + png_ptr->num_prev_filters = (png_byte)num_weights; + png_ptr->heuristic_method = (png_byte)heuristic_method; + + if (num_weights > 0) + { + if (png_ptr->prev_filters == NULL) + { + png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_byte) * num_weights)); + + /* To make sure that the weighting starts out fairly */ + for (i = 0; i < num_weights; i++) + { + png_ptr->prev_filters[i] = 255; + } + } + + if (png_ptr->filter_weights == NULL) + { + png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * num_weights)); + + png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * num_weights)); + for (i = 0; i < num_weights; i++) + { + png_ptr->inv_filter_weights[i] = + png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; + } + } + + for (i = 0; i < num_weights; i++) + { + if (filter_weights[i] < 0.0) + { + png_ptr->inv_filter_weights[i] = + png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; + } + else + { + png_ptr->inv_filter_weights[i] = + (png_uint_16)((double)PNG_WEIGHT_FACTOR*filter_weights[i]+0.5); + png_ptr->filter_weights[i] = + (png_uint_16)((double)PNG_WEIGHT_FACTOR/filter_weights[i]+0.5); + } + } + } + + /* If, in the future, there are other filter methods, this would + * need to be based on png_ptr->filter. + */ + if (png_ptr->filter_costs == NULL) + { + png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST)); + + png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST)); + + for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) + { + png_ptr->inv_filter_costs[i] = + png_ptr->filter_costs[i] = PNG_COST_FACTOR; + } + } + + /* Here is where we set the relative costs of the different filters. We + * should take the desired compression level into account when setting + * the costs, so that Paeth, for instance, has a high relative cost at low + * compression levels, while it has a lower relative cost at higher + * compression settings. The filter types are in order of increasing + * relative cost, so it would be possible to do this with an algorithm. + */ + for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) + { + if (filter_costs == NULL || filter_costs[i] < 0.0) + { + png_ptr->inv_filter_costs[i] = + png_ptr->filter_costs[i] = PNG_COST_FACTOR; + } + else if (filter_costs[i] >= 1.0) + { + png_ptr->inv_filter_costs[i] = + (png_uint_16)((double)PNG_COST_FACTOR / filter_costs[i] + 0.5); + png_ptr->filter_costs[i] = + (png_uint_16)((double)PNG_COST_FACTOR * filter_costs[i] + 0.5); + } + } +} +#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ + +void PNGAPI +png_set_compression_level(png_structp png_ptr, int level) +{ + png_debug(1, "in png_set_compression_level\n"); + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL; + png_ptr->zlib_level = level; +} + +void PNGAPI +png_set_compression_mem_level(png_structp png_ptr, int mem_level) +{ + png_debug(1, "in png_set_compression_mem_level\n"); + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL; + png_ptr->zlib_mem_level = mem_level; +} + +void PNGAPI +png_set_compression_strategy(png_structp png_ptr, int strategy) +{ + png_debug(1, "in png_set_compression_strategy\n"); + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY; + png_ptr->zlib_strategy = strategy; +} + +void PNGAPI +png_set_compression_window_bits(png_structp png_ptr, int window_bits) +{ + if (window_bits > 15) + png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); + else if (window_bits < 8) + png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); +#ifndef WBITS_8_OK + /* avoid libpng bug with 256-byte windows */ + if (window_bits == 8) + { + png_warning(png_ptr, "Compression window is being reset to 512"); + window_bits=9; + } +#endif + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS; + png_ptr->zlib_window_bits = window_bits; +} + +void PNGAPI +png_set_compression_method(png_structp png_ptr, int method) +{ + png_debug(1, "in png_set_compression_method\n"); + if (method != 8) + png_warning(png_ptr, "Only compression method 8 is supported by PNG"); + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD; + png_ptr->zlib_method = method; +} + +void PNGAPI +png_set_write_status_fn(png_structp png_ptr, png_write_status_ptr write_row_fn) +{ + png_ptr->write_row_fn = write_row_fn; +} + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +void PNGAPI +png_set_write_user_transform_fn(png_structp png_ptr, png_user_transform_ptr + write_user_transform_fn) +{ + png_debug(1, "in png_set_write_user_transform_fn\n"); + png_ptr->transformations |= PNG_USER_TRANSFORM; + png_ptr->write_user_transform_fn = write_user_transform_fn; +} +#endif + + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +void PNGAPI +png_write_png(png_structp png_ptr, png_infop info_ptr, + int transforms, voidp params) +{ +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) + /* invert the alpha channel from opacity to transparency */ + if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + png_set_invert_alpha(png_ptr); +#endif + + /* Write the file header information. */ + png_write_info(png_ptr, info_ptr); + + /* ------ these transformations don't touch the info structure ------- */ + +#if defined(PNG_WRITE_INVERT_SUPPORTED) + /* invert monochrome pixels */ + if (transforms & PNG_TRANSFORM_INVERT_MONO) + png_set_invert_mono(png_ptr); +#endif + +#if defined(PNG_WRITE_SHIFT_SUPPORTED) + /* Shift the pixels up to a legal bit depth and fill in + * as appropriate to correctly scale the image. + */ + if ((transforms & PNG_TRANSFORM_SHIFT) + && (info_ptr->valid & PNG_INFO_sBIT)) + png_set_shift(png_ptr, &info_ptr->sig_bit); +#endif + +#if defined(PNG_WRITE_PACK_SUPPORTED) + /* pack pixels into bytes */ + if (transforms & PNG_TRANSFORM_PACKING) + png_set_packing(png_ptr); +#endif + +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) + /* swap location of alpha bytes from ARGB to RGBA */ + if (transforms & PNG_TRANSFORM_SWAP_ALPHA) + png_set_swap_alpha(png_ptr); +#endif + +#if defined(PNG_WRITE_FILLER_SUPPORTED) + /* Get rid of filler (OR ALPHA) bytes, pack XRGB/RGBX/ARGB/RGBA into + * RGB (4 channels -> 3 channels). The second parameter is not used. + */ + if (transforms & PNG_TRANSFORM_STRIP_FILLER) + png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); +#endif + +#if defined(PNG_WRITE_BGR_SUPPORTED) + /* flip BGR pixels to RGB */ + if (transforms & PNG_TRANSFORM_BGR) + png_set_bgr(png_ptr); +#endif + +#if defined(PNG_WRITE_SWAP_SUPPORTED) + /* swap bytes of 16-bit files to most significant byte first */ + if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) + png_set_swap(png_ptr); +#endif + +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) + /* swap bits of 1, 2, 4 bit packed pixel formats */ + if (transforms & PNG_TRANSFORM_PACKSWAP) + png_set_packswap(png_ptr); +#endif + + /* ----------------------- end of transformations ------------------- */ + + /* write the bits */ + if (info_ptr->valid & PNG_INFO_IDAT) + png_write_image(png_ptr, info_ptr->row_pointers); + + /* It is REQUIRED to call this to finish writing the rest of the file */ + png_write_end(png_ptr, info_ptr); + + if(transforms == 0 || params == NULL) + /* quiet compiler warnings */ return; +} +#endif +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/com32/lib/libpng/pngwtran.c b/com32/lib/libpng/pngwtran.c new file mode 100644 index 0000000..f1c8b3e --- /dev/null +++ b/com32/lib/libpng/pngwtran.c @@ -0,0 +1,563 @@ + +/* pngwtran.c - transforms the data in a row for PNG writers + * + * libpng version 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Transform the data according to the user's wishes. The order of + * transformations is significant. + */ +void /* PRIVATE */ +png_do_write_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_do_write_transformations\n"); + + if (png_ptr == NULL) + return; + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + if (png_ptr->transformations & PNG_USER_TRANSFORM) + if(png_ptr->write_user_transform_fn != NULL) + (*(png_ptr->write_user_transform_fn)) /* user write transform function */ + (png_ptr, /* png_ptr */ + &(png_ptr->row_info), /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_uint_32 rowbytes; number of bytes in row */ + /* png_byte color_type; color type of pixels */ + /* png_byte bit_depth; bit depth of samples */ + /* png_byte channels; number of channels (1-4) */ + /* png_byte pixel_depth; bits per pixel (depth*channels) */ + png_ptr->row_buf + 1); /* start of pixel data for row */ +#endif +#if defined(PNG_WRITE_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->flags); +#endif +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_do_pack(&(png_ptr->row_info), png_ptr->row_buf + 1, + (png_uint_32)png_ptr->bit_depth); +#endif +#if defined(PNG_WRITE_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_do_shift(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->shift)); +#endif +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_ALPHA) + png_do_write_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_ALPHA) + png_do_write_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +} + +#if defined(PNG_WRITE_PACK_SUPPORTED) +/* Pack pixels into bytes. Pass the true bit depth in bit_depth. The + * row_info bit depth should be 8 (one pixel per byte). The channels + * should be 1 (this only happens on grayscale and paletted images). + */ +void /* PRIVATE */ +png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) +{ + png_debug(1, "in png_do_pack\n"); + if (row_info->bit_depth == 8 && +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->channels == 1) + { + switch ((int)bit_depth) + { + case 1: + { + png_bytep sp, dp; + int mask, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + mask = 0x80; + v = 0; + + for (i = 0; i < row_width; i++) + { + if (*sp != 0) + v |= mask; + sp++; + if (mask > 1) + mask >>= 1; + else + { + mask = 0x80; + *dp = (png_byte)v; + dp++; + v = 0; + } + } + if (mask != 0x80) + *dp = (png_byte)v; + break; + } + case 2: + { + png_bytep sp, dp; + int shift, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 6; + v = 0; + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x03); + v |= (value << shift); + if (shift == 0) + { + shift = 6; + *dp = (png_byte)v; + dp++; + v = 0; + } + else + shift -= 2; + sp++; + } + if (shift != 6) + *dp = (png_byte)v; + break; + } + case 4: + { + png_bytep sp, dp; + int shift, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 4; + v = 0; + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x0f); + v |= (value << shift); + + if (shift == 0) + { + shift = 4; + *dp = (png_byte)v; + dp++; + v = 0; + } + else + shift -= 4; + + sp++; + } + if (shift != 4) + *dp = (png_byte)v; + break; + } + } + row_info->bit_depth = (png_byte)bit_depth; + row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); + } +} +#endif + +#if defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Shift pixel values to take advantage of whole range. Pass the + * true number of bits in bit_depth. The row should be packed + * according to row_info->bit_depth. Thus, if you had a row of + * bit depth 4, but the pixels only had values from 0 to 7, you + * would pass 3 as bit_depth, and this routine would translate the + * data to 0 to 15. + */ +void /* PRIVATE */ +png_do_shift(png_row_infop row_info, png_bytep row, png_color_8p bit_depth) +{ + png_debug(1, "in png_do_shift\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL && +#else + if ( +#endif + row_info->color_type != PNG_COLOR_TYPE_PALETTE) + { + int shift_start[4], shift_dec[4]; + int channels = 0; + + if (row_info->color_type & PNG_COLOR_MASK_COLOR) + { + shift_start[channels] = row_info->bit_depth - bit_depth->red; + shift_dec[channels] = bit_depth->red; + channels++; + shift_start[channels] = row_info->bit_depth - bit_depth->green; + shift_dec[channels] = bit_depth->green; + channels++; + shift_start[channels] = row_info->bit_depth - bit_depth->blue; + shift_dec[channels] = bit_depth->blue; + channels++; + } + else + { + shift_start[channels] = row_info->bit_depth - bit_depth->gray; + shift_dec[channels] = bit_depth->gray; + channels++; + } + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + shift_start[channels] = row_info->bit_depth - bit_depth->alpha; + shift_dec[channels] = bit_depth->alpha; + channels++; + } + + /* with low row depths, could only be grayscale, so one channel */ + if (row_info->bit_depth < 8) + { + png_bytep bp = row; + png_uint_32 i; + png_byte mask; + png_uint_32 row_bytes = row_info->rowbytes; + + if (bit_depth->gray == 1 && row_info->bit_depth == 2) + mask = 0x55; + else if (row_info->bit_depth == 4 && bit_depth->gray == 3) + mask = 0x11; + else + mask = 0xff; + + for (i = 0; i < row_bytes; i++, bp++) + { + png_uint_16 v; + int j; + + v = *bp; + *bp = 0; + for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0]) + { + if (j > 0) + *bp |= (png_byte)((v << j) & 0xff); + else + *bp |= (png_byte)((v >> (-j)) & mask); + } + } + } + else if (row_info->bit_depth == 8) + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (i = 0; i < istop; i++, bp++) + { + + png_uint_16 v; + int j; + int c = (int)(i%channels); + + v = *bp; + *bp = 0; + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + *bp |= (png_byte)((v << j) & 0xff); + else + *bp |= (png_byte)((v >> (-j)) & 0xff); + } + } + } + else + { + png_bytep bp; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (bp = row, i = 0; i < istop; i++) + { + int c = (int)(i%channels); + png_uint_16 value, v; + int j; + + v = (png_uint_16)(((png_uint_16)(*bp) << 8) + *(bp + 1)); + value = 0; + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + value |= (png_uint_16)((v << j) & (png_uint_16)0xffff); + else + value |= (png_uint_16)((v >> (-j)) & (png_uint_16)0xffff); + } + *bp++ = (png_byte)(value >> 8); + *bp++ = (png_byte)(value & 0xff); + } + } + } +} +#endif + +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_swap_alpha\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This converts from ARGB to RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + /* This converts from AARRGGBB to RRGGBBAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This converts from AG to GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + /* This converts from AAGG to GGAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } + } + } +} +#endif + +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_invert_alpha\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This inverts the alpha channel in RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + for (i = 0, sp = dp = row; i < row_width; i++) + { + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + /* This inverts the alpha channel in RRGGBBAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = (png_byte)(255 - *(sp++)); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This inverts the alpha channel in GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + *(dp++) = *(sp++); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + /* This inverts the alpha channel in GGAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = (png_byte)(255 - *(sp++)); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + } + } +} +#endif + +#if defined(PNG_MNG_FEATURES_SUPPORTED) +/* undoes intrapixel differencing */ +void /* PRIVATE */ +png_do_write_intrapixel(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_intrapixel\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + int bytes_per_pixel; + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 3; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 4; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + *(rp) = (png_byte)((*rp - *(rp+1))&0xff); + *(rp+2) = (png_byte)((*(rp+2) - *(rp+1))&0xff); + } + } + else if (row_info->bit_depth == 16) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 6; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 8; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + png_uint_32 s0 = (*(rp ) << 8) | *(rp+1); + png_uint_32 s1 = (*(rp+2) << 8) | *(rp+3); + png_uint_32 s2 = (*(rp+4) << 8) | *(rp+5); + png_uint_32 red = (png_uint_32)((s0-s1) & 0xffffL); + png_uint_32 blue = (png_uint_32)((s2-s1) & 0xffffL); + *(rp ) = (png_byte)((red >> 8) & 0xff); + *(rp+1) = (png_byte)(red & 0xff); + *(rp+4) = (png_byte)((blue >> 8) & 0xff); + *(rp+5) = (png_byte)(blue & 0xff); + } + } + } +} +#endif /* PNG_MNG_FEATURES_SUPPORTED */ +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/com32/lib/libpng/pngwutil.c b/com32/lib/libpng/pngwutil.c new file mode 100644 index 0000000..dd7b150 --- /dev/null +++ b/com32/lib/libpng/pngwutil.c @@ -0,0 +1,2730 @@ + +/* pngwutil.c - utilities to write a PNG file + * + * libpng version 1.2.8 - December 3, 2004 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2004 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Place a 32-bit number into a buffer in PNG byte order. We work + * with unsigned numbers for convenience, although one supported + * ancillary chunk uses signed (two's complement) numbers. + */ +void /* PRIVATE */ +png_save_uint_32(png_bytep buf, png_uint_32 i) +{ + buf[0] = (png_byte)((i >> 24) & 0xff); + buf[1] = (png_byte)((i >> 16) & 0xff); + buf[2] = (png_byte)((i >> 8) & 0xff); + buf[3] = (png_byte)(i & 0xff); +} + +#if defined(PNG_WRITE_pCAL_SUPPORTED) || defined(PNG_WRITE_oFFs_SUPPORTED) +/* The png_save_int_32 function assumes integers are stored in two's + * complement format. If this isn't the case, then this routine needs to + * be modified to write data in two's complement format. + */ +void /* PRIVATE */ +png_save_int_32(png_bytep buf, png_int_32 i) +{ + buf[0] = (png_byte)((i >> 24) & 0xff); + buf[1] = (png_byte)((i >> 16) & 0xff); + buf[2] = (png_byte)((i >> 8) & 0xff); + buf[3] = (png_byte)(i & 0xff); +} +#endif + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +void /* PRIVATE */ +png_save_uint_16(png_bytep buf, unsigned int i) +{ + buf[0] = (png_byte)((i >> 8) & 0xff); + buf[1] = (png_byte)(i & 0xff); +} + +/* Write a PNG chunk all at once. The type is an array of ASCII characters + * representing the chunk name. The array must be at least 4 bytes in + * length, and does not need to be null terminated. To be safe, pass the + * pre-defined chunk names here, and if you need a new one, define it + * where the others are defined. The length is the length of the data. + * All the data must be present. If that is not possible, use the + * png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end() + * functions instead. + */ +void PNGAPI +png_write_chunk(png_structp png_ptr, png_bytep chunk_name, + png_bytep data, png_size_t length) +{ + png_write_chunk_start(png_ptr, chunk_name, (png_uint_32)length); + png_write_chunk_data(png_ptr, data, length); + png_write_chunk_end(png_ptr); +} + +/* Write the start of a PNG chunk. The type is the chunk type. + * The total_length is the sum of the lengths of all the data you will be + * passing in png_write_chunk_data(). + */ +void PNGAPI +png_write_chunk_start(png_structp png_ptr, png_bytep chunk_name, + png_uint_32 length) +{ + png_byte buf[4]; + png_debug2(0, "Writing %s chunk (%lu bytes)\n", chunk_name, length); + + /* write the length */ + png_save_uint_32(buf, length); + png_write_data(png_ptr, buf, (png_size_t)4); + + /* write the chunk name */ + png_write_data(png_ptr, chunk_name, (png_size_t)4); + /* reset the crc and run it over the chunk name */ + png_reset_crc(png_ptr); + png_calculate_crc(png_ptr, chunk_name, (png_size_t)4); +} + +/* Write the data of a PNG chunk started with png_write_chunk_start(). + * Note that multiple calls to this function are allowed, and that the + * sum of the lengths from these calls *must* add up to the total_length + * given to png_write_chunk_start(). + */ +void PNGAPI +png_write_chunk_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + /* write the data, and run the CRC over it */ + if (data != NULL && length > 0) + { + png_calculate_crc(png_ptr, data, length); + png_write_data(png_ptr, data, length); + } +} + +/* Finish a chunk started with png_write_chunk_start(). */ +void PNGAPI +png_write_chunk_end(png_structp png_ptr) +{ + png_byte buf[4]; + + /* write the crc */ + png_save_uint_32(buf, png_ptr->crc); + + png_write_data(png_ptr, buf, (png_size_t)4); +} + +/* Simple function to write the signature. If we have already written + * the magic bytes of the signature, or more likely, the PNG stream is + * being embedded into another stream and doesn't need its own signature, + * we should call png_set_sig_bytes() to tell libpng how many of the + * bytes have already been written. + */ +void /* PRIVATE */ +png_write_sig(png_structp png_ptr) +{ + png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + /* write the rest of the 8 byte signature */ + png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes], + (png_size_t)8 - png_ptr->sig_bytes); + if(png_ptr->sig_bytes < 3) + png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; +} + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_iCCP_SUPPORTED) +/* + * This pair of functions encapsulates the operation of (a) compressing a + * text string, and (b) issuing it later as a series of chunk data writes. + * The compression_state structure is shared context for these functions + * set up by the caller in order to make the whole mess thread-safe. + */ + +typedef struct +{ + char *input; /* the uncompressed input data */ + int input_len; /* its length */ + int num_output_ptr; /* number of output pointers used */ + int max_output_ptr; /* size of output_ptr */ + png_charpp output_ptr; /* array of pointers to output */ +} compression_state; + +/* compress given text into storage in the png_ptr structure */ +static int /* PRIVATE */ +png_text_compress(png_structp png_ptr, + png_charp text, png_size_t text_len, int compression, + compression_state *comp) +{ + int ret; + + comp->num_output_ptr = comp->max_output_ptr = 0; + comp->output_ptr = NULL; + comp->input = NULL; + + /* we may just want to pass the text right through */ + if (compression == PNG_TEXT_COMPRESSION_NONE) + { + comp->input = text; + comp->input_len = text_len; + return((int)text_len); + } + + if (compression >= PNG_TEXT_COMPRESSION_LAST) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[50]; + sprintf(msg, "Unknown compression type %d", compression); + png_warning(png_ptr, msg); +#else + png_warning(png_ptr, "Unknown compression type"); +#endif + } + + /* We can't write the chunk until we find out how much data we have, + * which means we need to run the compressor first and save the + * output. This shouldn't be a problem, as the vast majority of + * comments should be reasonable, but we will set up an array of + * malloc'd pointers to be sure. + * + * If we knew the application was well behaved, we could simplify this + * greatly by assuming we can always malloc an output buffer large + * enough to hold the compressed text ((1001 * text_len / 1000) + 12) + * and malloc this directly. The only time this would be a bad idea is + * if we can't malloc more than 64K and we have 64K of random input + * data, or if the input string is incredibly large (although this + * wouldn't cause a failure, just a slowdown due to swapping). + */ + + /* set up the compression buffers */ + png_ptr->zstream.avail_in = (uInt)text_len; + png_ptr->zstream.next_in = (Bytef *)text; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = (Bytef *)png_ptr->zbuf; + + /* this is the same compression loop as in png_write_row() */ + do + { + /* compress the data */ + ret = deflate(&png_ptr->zstream, Z_NO_FLUSH); + if (ret != Z_OK) + { + /* error */ + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + /* check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + /* make sure the output array has room */ + if (comp->num_output_ptr >= comp->max_output_ptr) + { + int old_max; + + old_max = comp->max_output_ptr; + comp->max_output_ptr = comp->num_output_ptr + 4; + if (comp->output_ptr != NULL) + { + png_charpp old_ptr; + + old_ptr = comp->output_ptr; + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof (png_charpp))); + png_memcpy(comp->output_ptr, old_ptr, old_max + * png_sizeof (png_charp)); + png_free(png_ptr, old_ptr); + } + else + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof (png_charp))); + } + + /* save the data */ + comp->output_ptr[comp->num_output_ptr] = (png_charp)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf, + png_ptr->zbuf_size); + comp->num_output_ptr++; + + /* and reset the buffer */ + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; + } + /* continue until we don't have any more to compress */ + } while (png_ptr->zstream.avail_in); + + /* finish the compression */ + do + { + /* tell zlib we are finished */ + ret = deflate(&png_ptr->zstream, Z_FINISH); + + if (ret == Z_OK) + { + /* check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + /* check to make sure our output array has room */ + if (comp->num_output_ptr >= comp->max_output_ptr) + { + int old_max; + + old_max = comp->max_output_ptr; + comp->max_output_ptr = comp->num_output_ptr + 4; + if (comp->output_ptr != NULL) + { + png_charpp old_ptr; + + old_ptr = comp->output_ptr; + /* This could be optimized to realloc() */ + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof (png_charpp))); + png_memcpy(comp->output_ptr, old_ptr, + old_max * png_sizeof (png_charp)); + png_free(png_ptr, old_ptr); + } + else + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof (png_charp))); + } + + /* save off the data */ + comp->output_ptr[comp->num_output_ptr] = + (png_charp)png_malloc(png_ptr, (png_uint_32)png_ptr->zbuf_size); + png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf, + png_ptr->zbuf_size); + comp->num_output_ptr++; + + /* and reset the buffer pointers */ + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; + } + } + else if (ret != Z_STREAM_END) + { + /* we got an error */ + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + } while (ret != Z_STREAM_END); + + /* text length is number of buffers plus last buffer */ + text_len = png_ptr->zbuf_size * comp->num_output_ptr; + if (png_ptr->zstream.avail_out < png_ptr->zbuf_size) + text_len += png_ptr->zbuf_size - (png_size_t)png_ptr->zstream.avail_out; + + return((int)text_len); +} + +/* ship the compressed text out via chunk writes */ +static void /* PRIVATE */ +png_write_compressed_data_out(png_structp png_ptr, compression_state *comp) +{ + int i; + + /* handle the no-compression case */ + if (comp->input) + { + png_write_chunk_data(png_ptr, (png_bytep)comp->input, + (png_size_t)comp->input_len); + return; + } + + /* write saved output buffers, if any */ + for (i = 0; i < comp->num_output_ptr; i++) + { + png_write_chunk_data(png_ptr,(png_bytep)comp->output_ptr[i], + png_ptr->zbuf_size); + png_free(png_ptr, comp->output_ptr[i]); + comp->output_ptr[i]=NULL; + } + if (comp->max_output_ptr != 0) + png_free(png_ptr, comp->output_ptr); + comp->output_ptr=NULL; + /* write anything left in zbuf */ + if (png_ptr->zstream.avail_out < (png_uint_32)png_ptr->zbuf_size) + png_write_chunk_data(png_ptr, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + + /* reset zlib for another zTXt/iTXt or image data */ + deflateReset(&png_ptr->zstream); + png_ptr->zstream.data_type = Z_BINARY; +} +#endif + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. Note that the rest of this code depends upon this + * information being correct. + */ +void /* PRIVATE */ +png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height, + int bit_depth, int color_type, int compression_type, int filter_type, + int interlace_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IHDR; +#endif + png_byte buf[13]; /* buffer to store the IHDR info */ + + png_debug(1, "in png_write_IHDR\n"); + /* Check that we have valid input data from the application info */ + switch (color_type) + { + case PNG_COLOR_TYPE_GRAY: + switch (bit_depth) + { + case 1: + case 2: + case 4: + case 8: + case 16: png_ptr->channels = 1; break; + default: png_error(png_ptr,"Invalid bit depth for grayscale image"); + } + break; + case PNG_COLOR_TYPE_RGB: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for RGB image"); + png_ptr->channels = 3; + break; + case PNG_COLOR_TYPE_PALETTE: + switch (bit_depth) + { + case 1: + case 2: + case 4: + case 8: png_ptr->channels = 1; break; + default: png_error(png_ptr, "Invalid bit depth for paletted image"); + } + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for grayscale+alpha image"); + png_ptr->channels = 2; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for RGBA image"); + png_ptr->channels = 4; + break; + default: + png_error(png_ptr, "Invalid image color type specified"); + } + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + { + png_warning(png_ptr, "Invalid compression type specified"); + compression_type = PNG_COMPRESSION_TYPE_BASE; + } + + /* Write filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ( +#if defined(PNG_MNG_FEATURES_SUPPORTED) + !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) && +#endif + filter_type != PNG_FILTER_TYPE_BASE) + { + png_warning(png_ptr, "Invalid filter type specified"); + filter_type = PNG_FILTER_TYPE_BASE; + } + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + if (interlace_type != PNG_INTERLACE_NONE && + interlace_type != PNG_INTERLACE_ADAM7) + { + png_warning(png_ptr, "Invalid interlace type specified"); + interlace_type = PNG_INTERLACE_ADAM7; + } +#else + interlace_type=PNG_INTERLACE_NONE; +#endif + + /* save off the relevent information */ + png_ptr->bit_depth = (png_byte)bit_depth; + png_ptr->color_type = (png_byte)color_type; + png_ptr->interlaced = (png_byte)interlace_type; +#if defined(PNG_MNG_FEATURES_SUPPORTED) + png_ptr->filter_type = (png_byte)filter_type; +#endif + png_ptr->compression_type = (png_byte)compression_type; + png_ptr->width = width; + png_ptr->height = height; + + png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels); + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width); + /* set the usr info, so any transformations can modify it */ + png_ptr->usr_width = png_ptr->width; + png_ptr->usr_bit_depth = png_ptr->bit_depth; + png_ptr->usr_channels = png_ptr->channels; + + /* pack the header information into the buffer */ + png_save_uint_32(buf, width); + png_save_uint_32(buf + 4, height); + buf[8] = (png_byte)bit_depth; + buf[9] = (png_byte)color_type; + buf[10] = (png_byte)compression_type; + buf[11] = (png_byte)filter_type; + buf[12] = (png_byte)interlace_type; + + /* write the chunk */ + png_write_chunk(png_ptr, (png_bytep)png_IHDR, buf, (png_size_t)13); + + /* initialize zlib with PNG info */ + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = (voidpf)png_ptr; + if (!(png_ptr->do_filter)) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE || + png_ptr->bit_depth < 8) + png_ptr->do_filter = PNG_FILTER_NONE; + else + png_ptr->do_filter = PNG_ALL_FILTERS; + } + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY)) + { + if (png_ptr->do_filter != PNG_FILTER_NONE) + png_ptr->zlib_strategy = Z_FILTERED; + else + png_ptr->zlib_strategy = Z_DEFAULT_STRATEGY; + } + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_LEVEL)) + png_ptr->zlib_level = Z_DEFAULT_COMPRESSION; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL)) + png_ptr->zlib_mem_level = 8; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS)) + png_ptr->zlib_window_bits = 15; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_METHOD)) + png_ptr->zlib_method = 8; + deflateInit2(&png_ptr->zstream, png_ptr->zlib_level, + png_ptr->zlib_method, png_ptr->zlib_window_bits, + png_ptr->zlib_mem_level, png_ptr->zlib_strategy); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + /* libpng is not interested in zstream.data_type */ + /* set it to a predefined value, to avoid its evaluation inside zlib */ + png_ptr->zstream.data_type = Z_BINARY; + + png_ptr->mode = PNG_HAVE_IHDR; +} + +/* write the palette. We are careful not to trust png_color to be in the + * correct order for PNG, so people can redefine it to any convenient + * structure. + */ +void /* PRIVATE */ +png_write_PLTE(png_structp png_ptr, png_colorp palette, png_uint_32 num_pal) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_PLTE; +#endif + png_uint_32 i; + png_colorp pal_ptr; + png_byte buf[3]; + + png_debug(1, "in png_write_PLTE\n"); + if (( +#if defined(PNG_MNG_FEATURES_SUPPORTED) + !(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) && +#endif + num_pal == 0) || num_pal > 256) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_error(png_ptr, "Invalid number of colors in palette"); + } + else + { + png_warning(png_ptr, "Invalid number of colors in palette"); + return; + } + } + + if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) + { + png_warning(png_ptr, + "Ignoring request to write a PLTE chunk in grayscale PNG"); + return; + } + + png_ptr->num_palette = (png_uint_16)num_pal; + png_debug1(3, "num_palette = %d\n", png_ptr->num_palette); + + png_write_chunk_start(png_ptr, (png_bytep)png_PLTE, num_pal * 3); +#ifndef PNG_NO_POINTER_INDEXING + for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++) + { + buf[0] = pal_ptr->red; + buf[1] = pal_ptr->green; + buf[2] = pal_ptr->blue; + png_write_chunk_data(png_ptr, buf, (png_size_t)3); + } +#else + /* This is a little slower but some buggy compilers need to do this instead */ + pal_ptr=palette; + for (i = 0; i < num_pal; i++) + { + buf[0] = pal_ptr[i].red; + buf[1] = pal_ptr[i].green; + buf[2] = pal_ptr[i].blue; + png_write_chunk_data(png_ptr, buf, (png_size_t)3); + } +#endif + png_write_chunk_end(png_ptr); + png_ptr->mode |= PNG_HAVE_PLTE; +} + +/* write an IDAT chunk */ +void /* PRIVATE */ +png_write_IDAT(png_structp png_ptr, png_bytep data, png_size_t length) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IDAT; +#endif + png_debug(1, "in png_write_IDAT\n"); + + /* Optimize the CMF field in the zlib stream. */ + /* This hack of the zlib stream is compliant to the stream specification. */ + if (!(png_ptr->mode & PNG_HAVE_IDAT) && + png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE) + { + unsigned int z_cmf = data[0]; /* zlib compression method and flags */ + if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70) + { + /* Avoid memory underflows and multiplication overflows. */ + /* The conditions below are practically always satisfied; + however, they still must be checked. */ + if (length >= 2 && + png_ptr->height < 16384 && png_ptr->width < 16384) + { + png_uint_32 uncompressed_idat_size = png_ptr->height * + ((png_ptr->width * + png_ptr->channels * png_ptr->bit_depth + 15) >> 3); + unsigned int z_cinfo = z_cmf >> 4; + unsigned int half_z_window_size = 1 << (z_cinfo + 7); + while (uncompressed_idat_size <= half_z_window_size && + half_z_window_size >= 256) + { + z_cinfo--; + half_z_window_size >>= 1; + } + z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4); + if (data[0] != (png_byte)z_cmf) + { + data[0] = (png_byte)z_cmf; + data[1] &= 0xe0; + data[1] += (png_byte)(0x1f - ((z_cmf << 8) + data[1]) % 0x1f); + } + } + } + else + png_error(png_ptr, + "Invalid zlib compression method or flags in IDAT"); + } + + png_write_chunk(png_ptr, (png_bytep)png_IDAT, data, length); + png_ptr->mode |= PNG_HAVE_IDAT; +} + +/* write an IEND chunk */ +void /* PRIVATE */ +png_write_IEND(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IEND; +#endif + png_debug(1, "in png_write_IEND\n"); + png_write_chunk(png_ptr, (png_bytep)png_IEND, png_bytep_NULL, + (png_size_t)0); + png_ptr->mode |= PNG_HAVE_IEND; +} + +#if defined(PNG_WRITE_gAMA_SUPPORTED) +/* write a gAMA chunk */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +void /* PRIVATE */ +png_write_gAMA(png_structp png_ptr, double file_gamma) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_gAMA; +#endif + png_uint_32 igamma; + png_byte buf[4]; + + png_debug(1, "in png_write_gAMA\n"); + /* file_gamma is saved in 1/100,000ths */ + igamma = (png_uint_32)(file_gamma * 100000.0 + 0.5); + png_save_uint_32(buf, igamma); + png_write_chunk(png_ptr, (png_bytep)png_gAMA, buf, (png_size_t)4); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_gAMA_fixed(png_structp png_ptr, png_fixed_point file_gamma) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_gAMA; +#endif + png_byte buf[4]; + + png_debug(1, "in png_write_gAMA\n"); + /* file_gamma is saved in 1/100,000ths */ + png_save_uint_32(buf, (png_uint_32)file_gamma); + png_write_chunk(png_ptr, (png_bytep)png_gAMA, buf, (png_size_t)4); +} +#endif +#endif + +#if defined(PNG_WRITE_sRGB_SUPPORTED) +/* write a sRGB chunk */ +void /* PRIVATE */ +png_write_sRGB(png_structp png_ptr, int srgb_intent) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sRGB; +#endif + png_byte buf[1]; + + png_debug(1, "in png_write_sRGB\n"); + if(srgb_intent >= PNG_sRGB_INTENT_LAST) + png_warning(png_ptr, + "Invalid sRGB rendering intent specified"); + buf[0]=(png_byte)srgb_intent; + png_write_chunk(png_ptr, (png_bytep)png_sRGB, buf, (png_size_t)1); +} +#endif + +#if defined(PNG_WRITE_iCCP_SUPPORTED) +/* write an iCCP chunk */ +void /* PRIVATE */ +png_write_iCCP(png_structp png_ptr, png_charp name, int compression_type, + png_charp profile, int profile_len) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_iCCP; +#endif + png_size_t name_len; + png_charp new_name; + compression_state comp; + + png_debug(1, "in png_write_iCCP\n"); + if (name == NULL || (name_len = png_check_keyword(png_ptr, name, + &new_name)) == 0) + { + png_warning(png_ptr, "Empty keyword in iCCP chunk"); + return; + } + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + png_warning(png_ptr, "Unknown compression type in iCCP chunk"); + + if (profile == NULL) + profile_len = 0; + + if (profile_len) + profile_len = png_text_compress(png_ptr, profile, (png_size_t)profile_len, + PNG_COMPRESSION_TYPE_BASE, &comp); + + /* make sure we include the NULL after the name and the compression type */ + png_write_chunk_start(png_ptr, (png_bytep)png_iCCP, + (png_uint_32)name_len+profile_len+2); + new_name[name_len+1]=0x00; + png_write_chunk_data(png_ptr, (png_bytep)new_name, name_len + 2); + + if (profile_len) + png_write_compressed_data_out(png_ptr, &comp); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_name); +} +#endif + +#if defined(PNG_WRITE_sPLT_SUPPORTED) +/* write a sPLT chunk */ +void /* PRIVATE */ +png_write_sPLT(png_structp png_ptr, png_sPLT_tp spalette) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sPLT; +#endif + png_size_t name_len; + png_charp new_name; + png_byte entrybuf[10]; + int entry_size = (spalette->depth == 8 ? 6 : 10); + int palette_size = entry_size * spalette->nentries; + png_sPLT_entryp ep; +#ifdef PNG_NO_POINTER_INDEXING + int i; +#endif + + png_debug(1, "in png_write_sPLT\n"); + if (spalette->name == NULL || (name_len = png_check_keyword(png_ptr, + spalette->name, &new_name))==0) + { + png_warning(png_ptr, "Empty keyword in sPLT chunk"); + return; + } + + /* make sure we include the NULL after the name */ + png_write_chunk_start(png_ptr, (png_bytep)png_sPLT, + (png_uint_32)(name_len + 2 + palette_size)); + png_write_chunk_data(png_ptr, (png_bytep)new_name, name_len + 1); + png_write_chunk_data(png_ptr, (png_bytep)&spalette->depth, 1); + + /* loop through each palette entry, writing appropriately */ +#ifndef PNG_NO_POINTER_INDEXING + for (ep = spalette->entries; epentries+spalette->nentries; ep++) + { + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep->red; + entrybuf[1] = (png_byte)ep->green; + entrybuf[2] = (png_byte)ep->blue; + entrybuf[3] = (png_byte)ep->alpha; + png_save_uint_16(entrybuf + 4, ep->frequency); + } + else + { + png_save_uint_16(entrybuf + 0, ep->red); + png_save_uint_16(entrybuf + 2, ep->green); + png_save_uint_16(entrybuf + 4, ep->blue); + png_save_uint_16(entrybuf + 6, ep->alpha); + png_save_uint_16(entrybuf + 8, ep->frequency); + } + png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size); + } +#else + ep=spalette->entries; + for (i=0; i>spalette->nentries; i++) + { + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep[i].red; + entrybuf[1] = (png_byte)ep[i].green; + entrybuf[2] = (png_byte)ep[i].blue; + entrybuf[3] = (png_byte)ep[i].alpha; + png_save_uint_16(entrybuf + 4, ep[i].frequency); + } + else + { + png_save_uint_16(entrybuf + 0, ep[i].red); + png_save_uint_16(entrybuf + 2, ep[i].green); + png_save_uint_16(entrybuf + 4, ep[i].blue); + png_save_uint_16(entrybuf + 6, ep[i].alpha); + png_save_uint_16(entrybuf + 8, ep[i].frequency); + } + png_write_chunk_data(png_ptr, entrybuf, entry_size); + } +#endif + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_name); +} +#endif + +#if defined(PNG_WRITE_sBIT_SUPPORTED) +/* write the sBIT chunk */ +void /* PRIVATE */ +png_write_sBIT(png_structp png_ptr, png_color_8p sbit, int color_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sBIT; +#endif + png_byte buf[4]; + png_size_t size; + + png_debug(1, "in png_write_sBIT\n"); + /* make sure we don't depend upon the order of PNG_COLOR_8 */ + if (color_type & PNG_COLOR_MASK_COLOR) + { + png_byte maxbits; + + maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 : + png_ptr->usr_bit_depth); + if (sbit->red == 0 || sbit->red > maxbits || + sbit->green == 0 || sbit->green > maxbits || + sbit->blue == 0 || sbit->blue > maxbits) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[0] = sbit->red; + buf[1] = sbit->green; + buf[2] = sbit->blue; + size = 3; + } + else + { + if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[0] = sbit->gray; + size = 1; + } + + if (color_type & PNG_COLOR_MASK_ALPHA) + { + if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[size++] = sbit->alpha; + } + + png_write_chunk(png_ptr, (png_bytep)png_sBIT, buf, size); +} +#endif + +#if defined(PNG_WRITE_cHRM_SUPPORTED) +/* write the cHRM chunk */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +void /* PRIVATE */ +png_write_cHRM(png_structp png_ptr, double white_x, double white_y, + double red_x, double red_y, double green_x, double green_y, + double blue_x, double blue_y) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_cHRM; +#endif + png_byte buf[32]; + png_uint_32 itemp; + + png_debug(1, "in png_write_cHRM\n"); + /* each value is saved in 1/100,000ths */ + if (white_x < 0 || white_x > 0.8 || white_y < 0 || white_y > 0.8 || + white_x + white_y > 1.0) + { + png_warning(png_ptr, "Invalid cHRM white point specified"); +#if !defined(PNG_NO_CONSOLE_IO) + fprintf(stderr,"white_x=%f, white_y=%f\n",white_x, white_y); +#endif + return; + } + itemp = (png_uint_32)(white_x * 100000.0 + 0.5); + png_save_uint_32(buf, itemp); + itemp = (png_uint_32)(white_y * 100000.0 + 0.5); + png_save_uint_32(buf + 4, itemp); + + if (red_x < 0 || red_x > 0.8 || red_y < 0 || red_y > 0.8 || + red_x + red_y > 1.0) + { + png_warning(png_ptr, "Invalid cHRM red point specified"); + return; + } + itemp = (png_uint_32)(red_x * 100000.0 + 0.5); + png_save_uint_32(buf + 8, itemp); + itemp = (png_uint_32)(red_y * 100000.0 + 0.5); + png_save_uint_32(buf + 12, itemp); + + if (green_x < 0 || green_x > 0.8 || green_y < 0 || green_y > 0.8 || + green_x + green_y > 1.0) + { + png_warning(png_ptr, "Invalid cHRM green point specified"); + return; + } + itemp = (png_uint_32)(green_x * 100000.0 + 0.5); + png_save_uint_32(buf + 16, itemp); + itemp = (png_uint_32)(green_y * 100000.0 + 0.5); + png_save_uint_32(buf + 20, itemp); + + if (blue_x < 0 || blue_x > 0.8 || blue_y < 0 || blue_y > 0.8 || + blue_x + blue_y > 1.0) + { + png_warning(png_ptr, "Invalid cHRM blue point specified"); + return; + } + itemp = (png_uint_32)(blue_x * 100000.0 + 0.5); + png_save_uint_32(buf + 24, itemp); + itemp = (png_uint_32)(blue_y * 100000.0 + 0.5); + png_save_uint_32(buf + 28, itemp); + + png_write_chunk(png_ptr, (png_bytep)png_cHRM, buf, (png_size_t)32); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_cHRM_fixed(png_structp png_ptr, png_fixed_point white_x, + png_fixed_point white_y, png_fixed_point red_x, png_fixed_point red_y, + png_fixed_point green_x, png_fixed_point green_y, png_fixed_point blue_x, + png_fixed_point blue_y) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_cHRM; +#endif + png_byte buf[32]; + + png_debug(1, "in png_write_cHRM\n"); + /* each value is saved in 1/100,000ths */ + if (white_x > 80000L || white_y > 80000L || white_x + white_y > 100000L) + { + png_warning(png_ptr, "Invalid fixed cHRM white point specified"); +#if !defined(PNG_NO_CONSOLE_IO) + fprintf(stderr,"white_x=%ld, white_y=%ld\n",white_x, white_y); +#endif + return; + } + png_save_uint_32(buf, (png_uint_32)white_x); + png_save_uint_32(buf + 4, (png_uint_32)white_y); + + if (red_x > 80000L || red_y > 80000L || red_x + red_y > 100000L) + { + png_warning(png_ptr, "Invalid cHRM fixed red point specified"); + return; + } + png_save_uint_32(buf + 8, (png_uint_32)red_x); + png_save_uint_32(buf + 12, (png_uint_32)red_y); + + if (green_x > 80000L || green_y > 80000L || green_x + green_y > 100000L) + { + png_warning(png_ptr, "Invalid fixed cHRM green point specified"); + return; + } + png_save_uint_32(buf + 16, (png_uint_32)green_x); + png_save_uint_32(buf + 20, (png_uint_32)green_y); + + if (blue_x > 80000L || blue_y > 80000L || blue_x + blue_y > 100000L) + { + png_warning(png_ptr, "Invalid fixed cHRM blue point specified"); + return; + } + png_save_uint_32(buf + 24, (png_uint_32)blue_x); + png_save_uint_32(buf + 28, (png_uint_32)blue_y); + + png_write_chunk(png_ptr, (png_bytep)png_cHRM, buf, (png_size_t)32); +} +#endif +#endif + +#if defined(PNG_WRITE_tRNS_SUPPORTED) +/* write the tRNS chunk */ +void /* PRIVATE */ +png_write_tRNS(png_structp png_ptr, png_bytep trans, png_color_16p tran, + int num_trans, int color_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_tRNS; +#endif + png_byte buf[6]; + + png_debug(1, "in png_write_tRNS\n"); + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette) + { + png_warning(png_ptr,"Invalid number of transparent colors specified"); + return; + } + /* write the chunk out as it is */ + png_write_chunk(png_ptr, (png_bytep)png_tRNS, trans, (png_size_t)num_trans); + } + else if (color_type == PNG_COLOR_TYPE_GRAY) + { + /* one 16 bit value */ + if(tran->gray >= (1 << png_ptr->bit_depth)) + { + png_warning(png_ptr, + "Ignoring attempt to write tRNS chunk out-of-range for bit_depth"); + return; + } + png_save_uint_16(buf, tran->gray); + png_write_chunk(png_ptr, (png_bytep)png_tRNS, buf, (png_size_t)2); + } + else if (color_type == PNG_COLOR_TYPE_RGB) + { + /* three 16 bit values */ + png_save_uint_16(buf, tran->red); + png_save_uint_16(buf + 2, tran->green); + png_save_uint_16(buf + 4, tran->blue); + if(png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) + { + png_warning(png_ptr, + "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8"); + return; + } + png_write_chunk(png_ptr, (png_bytep)png_tRNS, buf, (png_size_t)6); + } + else + { + png_warning(png_ptr, "Can't write tRNS with an alpha channel"); + } +} +#endif + +#if defined(PNG_WRITE_bKGD_SUPPORTED) +/* write the background chunk */ +void /* PRIVATE */ +png_write_bKGD(png_structp png_ptr, png_color_16p back, int color_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_bKGD; +#endif + png_byte buf[6]; + + png_debug(1, "in png_write_bKGD\n"); + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + if ( +#if defined(PNG_MNG_FEATURES_SUPPORTED) + (png_ptr->num_palette || + (!(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE))) && +#endif + back->index > png_ptr->num_palette) + { + png_warning(png_ptr, "Invalid background palette index"); + return; + } + buf[0] = back->index; + png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)1); + } + else if (color_type & PNG_COLOR_MASK_COLOR) + { + png_save_uint_16(buf, back->red); + png_save_uint_16(buf + 2, back->green); + png_save_uint_16(buf + 4, back->blue); + if(png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) + { + png_warning(png_ptr, + "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8"); + return; + } + png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)6); + } + else + { + if(back->gray >= (1 << png_ptr->bit_depth)) + { + png_warning(png_ptr, + "Ignoring attempt to write bKGD chunk out-of-range for bit_depth"); + return; + } + png_save_uint_16(buf, back->gray); + png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)2); + } +} +#endif + +#if defined(PNG_WRITE_hIST_SUPPORTED) +/* write the histogram */ +void /* PRIVATE */ +png_write_hIST(png_structp png_ptr, png_uint_16p hist, int num_hist) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_hIST; +#endif + int i; + png_byte buf[3]; + + png_debug(1, "in png_write_hIST\n"); + if (num_hist > (int)png_ptr->num_palette) + { + png_debug2(3, "num_hist = %d, num_palette = %d\n", num_hist, + png_ptr->num_palette); + png_warning(png_ptr, "Invalid number of histogram entries specified"); + return; + } + + png_write_chunk_start(png_ptr, (png_bytep)png_hIST, (png_uint_32)(num_hist * 2)); + for (i = 0; i < num_hist; i++) + { + png_save_uint_16(buf, hist[i]); + png_write_chunk_data(png_ptr, buf, (png_size_t)2); + } + png_write_chunk_end(png_ptr); +} +#endif + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ + defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) +/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification, + * and if invalid, correct the keyword rather than discarding the entire + * chunk. The PNG 1.0 specification requires keywords 1-79 characters in + * length, forbids leading or trailing whitespace, multiple internal spaces, + * and the non-break space (0x80) from ISO 8859-1. Returns keyword length. + * + * The new_key is allocated to hold the corrected keyword and must be freed + * by the calling routine. This avoids problems with trying to write to + * static keywords without having to have duplicate copies of the strings. + */ +png_size_t /* PRIVATE */ +png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key) +{ + png_size_t key_len; + png_charp kp, dp; + int kflag; + int kwarn=0; + + png_debug(1, "in png_check_keyword\n"); + *new_key = NULL; + + if (key == NULL || (key_len = png_strlen(key)) == 0) + { + png_warning(png_ptr, "zero length keyword"); + return ((png_size_t)0); + } + + png_debug1(2, "Keyword to be checked is '%s'\n", key); + + *new_key = (png_charp)png_malloc_warn(png_ptr, (png_uint_32)(key_len + 2)); + if (*new_key == NULL) + { + png_warning(png_ptr, "Out of memory while procesing keyword"); + return ((png_size_t)0); + } + + /* Replace non-printing characters with a blank and print a warning */ + for (kp = key, dp = *new_key; *kp != '\0'; kp++, dp++) + { + if (*kp < 0x20 || (*kp > 0x7E && (png_byte)*kp < 0xA1)) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[40]; + + sprintf(msg, "invalid keyword character 0x%02X", *kp); + png_warning(png_ptr, msg); +#else + png_warning(png_ptr, "invalid character in keyword"); +#endif + *dp = ' '; + } + else + { + *dp = *kp; + } + } + *dp = '\0'; + + /* Remove any trailing white space. */ + kp = *new_key + key_len - 1; + if (*kp == ' ') + { + png_warning(png_ptr, "trailing spaces removed from keyword"); + + while (*kp == ' ') + { + *(kp--) = '\0'; + key_len--; + } + } + + /* Remove any leading white space. */ + kp = *new_key; + if (*kp == ' ') + { + png_warning(png_ptr, "leading spaces removed from keyword"); + + while (*kp == ' ') + { + kp++; + key_len--; + } + } + + png_debug1(2, "Checking for multiple internal spaces in '%s'\n", kp); + + /* Remove multiple internal spaces. */ + for (kflag = 0, dp = *new_key; *kp != '\0'; kp++) + { + if (*kp == ' ' && kflag == 0) + { + *(dp++) = *kp; + kflag = 1; + } + else if (*kp == ' ') + { + key_len--; + kwarn=1; + } + else + { + *(dp++) = *kp; + kflag = 0; + } + } + *dp = '\0'; + if(kwarn) + png_warning(png_ptr, "extra interior spaces removed from keyword"); + + if (key_len == 0) + { + png_free(png_ptr, *new_key); + *new_key=NULL; + png_warning(png_ptr, "Zero length keyword"); + } + + if (key_len > 79) + { + png_warning(png_ptr, "keyword length must be 1 - 79 characters"); + new_key[79] = '\0'; + key_len = 79; + } + + return (key_len); +} +#endif + +#if defined(PNG_WRITE_tEXt_SUPPORTED) +/* write a tEXt chunk */ +void /* PRIVATE */ +png_write_tEXt(png_structp png_ptr, png_charp key, png_charp text, + png_size_t text_len) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_tEXt; +#endif + png_size_t key_len; + png_charp new_key; + + png_debug(1, "in png_write_tEXt\n"); + if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0) + { + png_warning(png_ptr, "Empty keyword in tEXt chunk"); + return; + } + + if (text == NULL || *text == '\0') + text_len = 0; + else + text_len = png_strlen(text); + + /* make sure we include the 0 after the key */ + png_write_chunk_start(png_ptr, (png_bytep)png_tEXt, (png_uint_32)key_len+text_len+1); + /* + * We leave it to the application to meet PNG-1.0 requirements on the + * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of + * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. + * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. + */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, key_len + 1); + if (text_len) + png_write_chunk_data(png_ptr, (png_bytep)text, text_len); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_key); +} +#endif + +#if defined(PNG_WRITE_zTXt_SUPPORTED) +/* write a compressed text chunk */ +void /* PRIVATE */ +png_write_zTXt(png_structp png_ptr, png_charp key, png_charp text, + png_size_t text_len, int compression) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_zTXt; +#endif + png_size_t key_len; + char buf[1]; + png_charp new_key; + compression_state comp; + + png_debug(1, "in png_write_zTXt\n"); + + if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0) + { + png_warning(png_ptr, "Empty keyword in zTXt chunk"); + return; + } + + if (text == NULL || *text == '\0' || compression==PNG_TEXT_COMPRESSION_NONE) + { + png_write_tEXt(png_ptr, new_key, text, (png_size_t)0); + png_free(png_ptr, new_key); + return; + } + + text_len = png_strlen(text); + + png_free(png_ptr, new_key); + + /* compute the compressed data; do it now for the length */ + text_len = png_text_compress(png_ptr, text, text_len, compression, + &comp); + + /* write start of chunk */ + png_write_chunk_start(png_ptr, (png_bytep)png_zTXt, (png_uint_32) + (key_len+text_len+2)); + /* write key */ + png_write_chunk_data(png_ptr, (png_bytep)key, key_len + 1); + buf[0] = (png_byte)compression; + /* write compression */ + png_write_chunk_data(png_ptr, (png_bytep)buf, (png_size_t)1); + /* write the compressed data */ + png_write_compressed_data_out(png_ptr, &comp); + + /* close the chunk */ + png_write_chunk_end(png_ptr); +} +#endif + +#if defined(PNG_WRITE_iTXt_SUPPORTED) +/* write an iTXt chunk */ +void /* PRIVATE */ +png_write_iTXt(png_structp png_ptr, int compression, png_charp key, + png_charp lang, png_charp lang_key, png_charp text) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_iTXt; +#endif + png_size_t lang_len, key_len, lang_key_len, text_len; + png_charp new_lang, new_key; + png_byte cbuf[2]; + compression_state comp; + + png_debug(1, "in png_write_iTXt\n"); + + if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0) + { + png_warning(png_ptr, "Empty keyword in iTXt chunk"); + return; + } + if (lang == NULL || (lang_len = png_check_keyword(png_ptr, lang, &new_lang))==0) + { + png_warning(png_ptr, "Empty language field in iTXt chunk"); + new_lang = NULL; + lang_len = 0; + } + + if (lang_key == NULL) + lang_key_len = 0; + else + lang_key_len = png_strlen(lang_key); + + if (text == NULL) + text_len = 0; + else + text_len = png_strlen(text); + + /* compute the compressed data; do it now for the length */ + text_len = png_text_compress(png_ptr, text, text_len, compression-2, + &comp); + + + /* make sure we include the compression flag, the compression byte, + * and the NULs after the key, lang, and lang_key parts */ + + png_write_chunk_start(png_ptr, (png_bytep)png_iTXt, + (png_uint_32)( + 5 /* comp byte, comp flag, terminators for key, lang and lang_key */ + + key_len + + lang_len + + lang_key_len + + text_len)); + + /* + * We leave it to the application to meet PNG-1.0 requirements on the + * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of + * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. + * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. + */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, key_len + 1); + + /* set the compression flag */ + if (compression == PNG_ITXT_COMPRESSION_NONE || \ + compression == PNG_TEXT_COMPRESSION_NONE) + cbuf[0] = 0; + else /* compression == PNG_ITXT_COMPRESSION_zTXt */ + cbuf[0] = 1; + /* set the compression method */ + cbuf[1] = 0; + png_write_chunk_data(png_ptr, cbuf, 2); + + cbuf[0] = 0; + png_write_chunk_data(png_ptr, (new_lang ? (png_bytep)new_lang : cbuf), lang_len + 1); + png_write_chunk_data(png_ptr, (lang_key ? (png_bytep)lang_key : cbuf), lang_key_len + 1); + png_write_compressed_data_out(png_ptr, &comp); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_key); + if (new_lang) + png_free(png_ptr, new_lang); +} +#endif + +#if defined(PNG_WRITE_oFFs_SUPPORTED) +/* write the oFFs chunk */ +void /* PRIVATE */ +png_write_oFFs(png_structp png_ptr, png_int_32 x_offset, png_int_32 y_offset, + int unit_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_oFFs; +#endif + png_byte buf[9]; + + png_debug(1, "in png_write_oFFs\n"); + if (unit_type >= PNG_OFFSET_LAST) + png_warning(png_ptr, "Unrecognized unit type for oFFs chunk"); + + png_save_int_32(buf, x_offset); + png_save_int_32(buf + 4, y_offset); + buf[8] = (png_byte)unit_type; + + png_write_chunk(png_ptr, (png_bytep)png_oFFs, buf, (png_size_t)9); +} +#endif + +#if defined(PNG_WRITE_pCAL_SUPPORTED) +/* write the pCAL chunk (described in the PNG extensions document) */ +void /* PRIVATE */ +png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0, + png_int_32 X1, int type, int nparams, png_charp units, png_charpp params) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_pCAL; +#endif + png_size_t purpose_len, units_len, total_len; + png_uint_32p params_len; + png_byte buf[10]; + png_charp new_purpose; + int i; + + png_debug1(1, "in png_write_pCAL (%d parameters)\n", nparams); + if (type >= PNG_EQUATION_LAST) + png_warning(png_ptr, "Unrecognized equation type for pCAL chunk"); + + purpose_len = png_check_keyword(png_ptr, purpose, &new_purpose) + 1; + png_debug1(3, "pCAL purpose length = %d\n", (int)purpose_len); + units_len = png_strlen(units) + (nparams == 0 ? 0 : 1); + png_debug1(3, "pCAL units length = %d\n", (int)units_len); + total_len = purpose_len + units_len + 10; + + params_len = (png_uint_32p)png_malloc(png_ptr, (png_uint_32)(nparams + *png_sizeof(png_uint_32))); + + /* Find the length of each parameter, making sure we don't count the + null terminator for the last parameter. */ + for (i = 0; i < nparams; i++) + { + params_len[i] = png_strlen(params[i]) + (i == nparams - 1 ? 0 : 1); + png_debug2(3, "pCAL parameter %d length = %lu\n", i, params_len[i]); + total_len += (png_size_t)params_len[i]; + } + + png_debug1(3, "pCAL total length = %d\n", (int)total_len); + png_write_chunk_start(png_ptr, (png_bytep)png_pCAL, (png_uint_32)total_len); + png_write_chunk_data(png_ptr, (png_bytep)new_purpose, purpose_len); + png_save_int_32(buf, X0); + png_save_int_32(buf + 4, X1); + buf[8] = (png_byte)type; + buf[9] = (png_byte)nparams; + png_write_chunk_data(png_ptr, buf, (png_size_t)10); + png_write_chunk_data(png_ptr, (png_bytep)units, (png_size_t)units_len); + + png_free(png_ptr, new_purpose); + + for (i = 0; i < nparams; i++) + { + png_write_chunk_data(png_ptr, (png_bytep)params[i], + (png_size_t)params_len[i]); + } + + png_free(png_ptr, params_len); + png_write_chunk_end(png_ptr); +} +#endif + +#if defined(PNG_WRITE_sCAL_SUPPORTED) +/* write the sCAL chunk */ +#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) +void /* PRIVATE */ +png_write_sCAL(png_structp png_ptr, int unit, double width,double height) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sCAL; +#endif + png_size_t total_len; + char wbuf[32], hbuf[32]; + png_byte bunit = unit; + + png_debug(1, "in png_write_sCAL\n"); + +#if defined(_WIN32_WCE) +/* sprintf() function is not supported on WindowsCE */ + { + wchar_t wc_buf[32]; + swprintf(wc_buf, TEXT("%12.12e"), width); + WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, wbuf, 32, NULL, NULL); + swprintf(wc_buf, TEXT("%12.12e"), height); + WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, hbuf, 32, NULL, NULL); + } +#else + sprintf(wbuf, "%12.12e", width); + sprintf(hbuf, "%12.12e", height); +#endif + total_len = 1 + png_strlen(wbuf)+1 + png_strlen(hbuf); + + png_debug1(3, "sCAL total length = %d\n", (int)total_len); + png_write_chunk_start(png_ptr, (png_bytep)png_sCAL, (png_uint_32)total_len); + png_write_chunk_data(png_ptr, (png_bytep)&bunit, 1); + png_write_chunk_data(png_ptr, (png_bytep)wbuf, png_strlen(wbuf)+1); + png_write_chunk_data(png_ptr, (png_bytep)hbuf, png_strlen(hbuf)); + + png_write_chunk_end(png_ptr); +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_sCAL_s(png_structp png_ptr, int unit, png_charp width, + png_charp height) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sCAL; +#endif + png_size_t total_len; + char wbuf[32], hbuf[32]; + png_byte bunit = unit; + + png_debug(1, "in png_write_sCAL_s\n"); + + png_strcpy(wbuf,(const char *)width); + png_strcpy(hbuf,(const char *)height); + total_len = 1 + png_strlen(wbuf)+1 + png_strlen(hbuf); + + png_debug1(3, "sCAL total length = %d\n", total_len); + png_write_chunk_start(png_ptr, (png_bytep)png_sCAL, (png_uint_32)total_len); + png_write_chunk_data(png_ptr, (png_bytep)&bunit, 1); + png_write_chunk_data(png_ptr, (png_bytep)wbuf, png_strlen(wbuf)+1); + png_write_chunk_data(png_ptr, (png_bytep)hbuf, png_strlen(hbuf)); + + png_write_chunk_end(png_ptr); +} +#endif +#endif +#endif + +#if defined(PNG_WRITE_pHYs_SUPPORTED) +/* write the pHYs chunk */ +void /* PRIVATE */ +png_write_pHYs(png_structp png_ptr, png_uint_32 x_pixels_per_unit, + png_uint_32 y_pixels_per_unit, + int unit_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_pHYs; +#endif + png_byte buf[9]; + + png_debug(1, "in png_write_pHYs\n"); + if (unit_type >= PNG_RESOLUTION_LAST) + png_warning(png_ptr, "Unrecognized unit type for pHYs chunk"); + + png_save_uint_32(buf, x_pixels_per_unit); + png_save_uint_32(buf + 4, y_pixels_per_unit); + buf[8] = (png_byte)unit_type; + + png_write_chunk(png_ptr, (png_bytep)png_pHYs, buf, (png_size_t)9); +} +#endif + +#if defined(PNG_WRITE_tIME_SUPPORTED) +/* Write the tIME chunk. Use either png_convert_from_struct_tm() + * or png_convert_from_time_t(), or fill in the structure yourself. + */ +void /* PRIVATE */ +png_write_tIME(png_structp png_ptr, png_timep mod_time) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_tIME; +#endif + png_byte buf[7]; + + png_debug(1, "in png_write_tIME\n"); + if (mod_time->month > 12 || mod_time->month < 1 || + mod_time->day > 31 || mod_time->day < 1 || + mod_time->hour > 23 || mod_time->second > 60) + { + png_warning(png_ptr, "Invalid time specified for tIME chunk"); + return; + } + + png_save_uint_16(buf, mod_time->year); + buf[2] = mod_time->month; + buf[3] = mod_time->day; + buf[4] = mod_time->hour; + buf[5] = mod_time->minute; + buf[6] = mod_time->second; + + png_write_chunk(png_ptr, (png_bytep)png_tIME, buf, (png_size_t)7); +} +#endif + +/* initializes the row writing capability of libpng */ +void /* PRIVATE */ +png_write_start_row(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + png_size_t buf_size; + + png_debug(1, "in png_write_start_row\n"); + buf_size = (png_size_t)(PNG_ROWBYTES( + png_ptr->usr_channels*png_ptr->usr_bit_depth,png_ptr->width)+1); + + /* set up row buffer */ + png_ptr->row_buf = (png_bytep)png_malloc(png_ptr, (png_uint_32)buf_size); + png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE; + + /* set up filtering buffer, if using this filter */ + if (png_ptr->do_filter & PNG_FILTER_SUB) + { + png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; + } + + /* We only need to keep the previous row if we are using one of these. */ + if (png_ptr->do_filter & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH)) + { + /* set up previous row buffer */ + png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, (png_uint_32)buf_size); + png_memset(png_ptr->prev_row, 0, buf_size); + + if (png_ptr->do_filter & PNG_FILTER_UP) + { + png_ptr->up_row = (png_bytep )png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; + } + + if (png_ptr->do_filter & PNG_FILTER_AVG) + { + png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; + } + + if (png_ptr->do_filter & PNG_FILTER_PAETH) + { + png_ptr->paeth_row = (png_bytep )png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; + } + } + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* if interlaced, we need to set up width and height of pass */ + if (png_ptr->interlaced) + { + if (!(png_ptr->transformations & PNG_INTERLACE)) + { + png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - + png_pass_ystart[0]) / png_pass_yinc[0]; + png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 - + png_pass_start[0]) / png_pass_inc[0]; + } + else + { + png_ptr->num_rows = png_ptr->height; + png_ptr->usr_width = png_ptr->width; + } + } + else +#endif + { + png_ptr->num_rows = png_ptr->height; + png_ptr->usr_width = png_ptr->width; + } + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; +} + +/* Internal use only. Called when finished processing a row of data. */ +void /* PRIVATE */ +png_write_finish_row(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + int ret; + + png_debug(1, "in png_write_finish_row\n"); + /* next row */ + png_ptr->row_number++; + + /* see if we are done */ + if (png_ptr->row_number < png_ptr->num_rows) + return; + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* if interlaced, go to next pass */ + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + if (png_ptr->transformations & PNG_INTERLACE) + { + png_ptr->pass++; + } + else + { + /* loop until we find a non-zero width or height pass */ + do + { + png_ptr->pass++; + if (png_ptr->pass >= 7) + break; + png_ptr->usr_width = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + if (png_ptr->transformations & PNG_INTERLACE) + break; + } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0); + + } + + /* reset the row above the image for the next pass */ + if (png_ptr->pass < 7) + { + if (png_ptr->prev_row != NULL) + png_memset(png_ptr->prev_row, 0, + (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels* + png_ptr->usr_bit_depth,png_ptr->width))+1); + return; + } + } +#endif + + /* if we get here, we've just written the last row, so we need + to flush the compressor */ + do + { + /* tell the compressor we are done */ + ret = deflate(&png_ptr->zstream, Z_FINISH); + /* check for an error */ + if (ret == Z_OK) + { + /* check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + } + else if (ret != Z_STREAM_END) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + } while (ret != Z_STREAM_END); + + /* write any extra space */ + if (png_ptr->zstream.avail_out < png_ptr->zbuf_size) + { + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size - + png_ptr->zstream.avail_out); + } + + deflateReset(&png_ptr->zstream); + png_ptr->zstream.data_type = Z_BINARY; +} + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Pick out the correct pixels for the interlace pass. + * The basic idea here is to go through the row with a source + * pointer and a destination pointer (sp and dp), and copy the + * correct pixels for the pass. As the row gets compacted, + * sp will always be >= dp, so we should never overwrite anything. + * See the default: case for the easiest code to understand. + */ +void /* PRIVATE */ +png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; +#endif + + png_debug(1, "in png_do_write_interlace\n"); + /* we don't have to do anything on the last pass (6) */ +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL && pass < 6) +#else + if (pass < 6) +#endif + { + /* each pixel depth is handled separately */ + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + d = 0; + shift = 7; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 3); + value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01; + d |= (value << shift); + + if (shift == 0) + { + shift = 7; + *dp++ = (png_byte)d; + d = 0; + } + else + shift--; + + } + if (shift != 7) + *dp = (png_byte)d; + break; + } + case 2: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + shift = 6; + d = 0; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 2); + value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03; + d |= (value << shift); + + if (shift == 0) + { + shift = 6; + *dp++ = (png_byte)d; + d = 0; + } + else + shift -= 2; + } + if (shift != 6) + *dp = (png_byte)d; + break; + } + case 4: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + shift = 4; + d = 0; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 1); + value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f; + d |= (value << shift); + + if (shift == 0) + { + shift = 4; + *dp++ = (png_byte)d; + d = 0; + } + else + shift -= 4; + } + if (shift != 4) + *dp = (png_byte)d; + break; + } + default: + { + png_bytep sp; + png_bytep dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + png_size_t pixel_bytes; + + /* start at the beginning */ + dp = row; + /* find out how many bytes each pixel takes up */ + pixel_bytes = (row_info->pixel_depth >> 3); + /* loop through the row, only looking at the pixels that + matter */ + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + /* find out where the original pixel is */ + sp = row + (png_size_t)i * pixel_bytes; + /* move the pixel */ + if (dp != sp) + png_memcpy(dp, sp, pixel_bytes); + /* next pixel */ + dp += pixel_bytes; + } + break; + } + } + /* set new row width */ + row_info->width = (row_info->width + + png_pass_inc[pass] - 1 - + png_pass_start[pass]) / + png_pass_inc[pass]; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); + } +} +#endif + +/* This filters the row, chooses which filter to use, if it has not already + * been specified by the application, and then writes the row out with the + * chosen filter. + */ +#define PNG_MAXSUM (((png_uint_32)(-1)) >> 1) +#define PNG_HISHIFT 10 +#define PNG_LOMASK ((png_uint_32)0xffffL) +#define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT)) +void /* PRIVATE */ +png_write_find_filter(png_structp png_ptr, png_row_infop row_info) +{ + png_bytep prev_row, best_row, row_buf; + png_uint_32 mins, bpp; + png_byte filter_to_do = png_ptr->do_filter; + png_uint_32 row_bytes = row_info->rowbytes; +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + int num_p_filters = (int)png_ptr->num_prev_filters; +#endif + + png_debug(1, "in png_write_find_filter\n"); + /* find out how many bytes offset each pixel is */ + bpp = (row_info->pixel_depth + 7) >> 3; + + prev_row = png_ptr->prev_row; + best_row = row_buf = png_ptr->row_buf; + mins = PNG_MAXSUM; + + /* The prediction method we use is to find which method provides the + * smallest value when summing the absolute values of the distances + * from zero, using anything >= 128 as negative numbers. This is known + * as the "minimum sum of absolute differences" heuristic. Other + * heuristics are the "weighted minimum sum of absolute differences" + * (experimental and can in theory improve compression), and the "zlib + * predictive" method (not implemented yet), which does test compressions + * of lines using different filter methods, and then chooses the + * (series of) filter(s) that give minimum compressed data size (VERY + * computationally expensive). + * + * GRR 980525: consider also + * (1) minimum sum of absolute differences from running average (i.e., + * keep running sum of non-absolute differences & count of bytes) + * [track dispersion, too? restart average if dispersion too large?] + * (1b) minimum sum of absolute differences from sliding average, probably + * with window size <= deflate window (usually 32K) + * (2) minimum sum of squared differences from zero or running average + * (i.e., ~ root-mean-square approach) + */ + + + /* We don't need to test the 'no filter' case if this is the only filter + * that has been chosen, as it doesn't actually do anything to the data. + */ + if ((filter_to_do & PNG_FILTER_NONE) && + filter_to_do != PNG_FILTER_NONE) + { + png_bytep rp; + png_uint_32 sum = 0; + png_uint_32 i; + int v; + + for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++) + { + v = *rp; + sum += (v < 128) ? v : 256 - v; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + png_uint_32 sumhi, sumlo; + int j; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; /* Gives us some footroom */ + + /* Reduce the sum if we match any of the previous rows */ + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + /* Factor in the cost of this filter (this is here for completeness, + * but it makes no sense to have a "cost" for the NONE filter, as + * it has the minimum possible computational cost - none). + */ + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + mins = sum; + } + + /* sub filter */ + if (filter_to_do == PNG_FILTER_SUB) + /* it's the only filter so no testing is needed */ + { + png_bytep rp, lp, dp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; + i++, rp++, dp++) + { + *dp = *rp; + } + for (lp = row_buf + 1; i < row_bytes; + i++, rp++, lp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); + } + best_row = png_ptr->sub_row; + } + + else if (filter_to_do & PNG_FILTER_SUB) + { + png_bytep rp, dp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + /* We temporarily increase the "minimum sum" by the factor we + * would reduce the sum of this filter, so that we can do the + * early exit comparison without scaling the sum each time. + */ + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; + i++, rp++, dp++) + { + v = *dp = *rp; + + sum += (v < 128) ? v : 256 - v; + } + for (lp = row_buf + 1; i < row_bytes; + i++, rp++, lp++, dp++) + { + v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) + { + sumlo = (sumlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->sub_row; + } + } + + /* up filter */ + if (filter_to_do == PNG_FILTER_UP) + { + png_bytep rp, dp, pp; + png_uint_32 i; + + for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, + pp = prev_row + 1; i < row_bytes; + i++, rp++, pp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff); + } + best_row = png_ptr->up_row; + } + + else if (filter_to_do & PNG_FILTER_UP) + { + png_bytep rp, dp, pp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, + pp = prev_row + 1; i < row_bytes; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->up_row; + } + } + + /* avg filter */ + if (filter_to_do == PNG_FILTER_AVG) + { + png_bytep rp, dp, pp, lp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + } + for (lp = row_buf + 1; i < row_bytes; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) + & 0xff); + } + best_row = png_ptr->avg_row; + } + + else if (filter_to_do & PNG_FILTER_AVG) + { + png_bytep rp, dp, pp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_AVG) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + + sum += (v < 128) ? v : 256 - v; + } + for (lp = row_buf + 1; i < row_bytes; i++) + { + v = *dp++ = + (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->avg_row; + } + } + + /* Paeth filter */ + if (filter_to_do == PNG_FILTER_PAETH) + { + png_bytep rp, dp, pp, cp, lp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + } + + for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) + { + int a, b, c, pa, pb, pc, p; + + b = *pp++; + c = *cp++; + a = *lp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; + + *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + } + best_row = png_ptr->paeth_row; + } + + else if (filter_to_do & PNG_FILTER_PAETH) + { + png_bytep rp, dp, pp, cp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + + sum += (v < 128) ? v : 256 - v; + } + + for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) + { + int a, b, c, pa, pb, pc, p; + + b = *pp++; + c = *cp++; + a = *lp++; + +#ifndef PNG_SLOW_PAETH + p = b - c; + pc = a - c; +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; +#else /* PNG_SLOW_PAETH */ + p = a + b - c; + pa = abs(p - a); + pb = abs(p - b); + pc = abs(p - c); + if (pa <= pb && pa <= pc) + p = a; + else if (pb <= pc) + p = b; + else + p = c; +#endif /* PNG_SLOW_PAETH */ + + v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + best_row = png_ptr->paeth_row; + } + } + + /* Do the actual writing of the filtered row data from the chosen filter. */ + + png_write_filtered_row(png_ptr, best_row); + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + /* Save the type of filter we picked this time for future calculations */ + if (png_ptr->num_prev_filters > 0) + { + int j; + for (j = 1; j < num_p_filters; j++) + { + png_ptr->prev_filters[j] = png_ptr->prev_filters[j - 1]; + } + png_ptr->prev_filters[j] = best_row[0]; + } +#endif +} + + +/* Do the actual writing of a previously filtered row. */ +void /* PRIVATE */ +png_write_filtered_row(png_structp png_ptr, png_bytep filtered_row) +{ + png_debug(1, "in png_write_filtered_row\n"); + png_debug1(2, "filter = %d\n", filtered_row[0]); + /* set up the zlib input buffer */ + + png_ptr->zstream.next_in = filtered_row; + png_ptr->zstream.avail_in = (uInt)png_ptr->row_info.rowbytes + 1; + /* repeat until we have compressed all the data */ + do + { + int ret; /* return of zlib */ + + /* compress the data */ + ret = deflate(&png_ptr->zstream, Z_NO_FLUSH); + /* check for compression errors */ + if (ret != Z_OK) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + + /* see if it is time to write another IDAT */ + if (!(png_ptr->zstream.avail_out)) + { + /* write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + /* repeat until all data has been compressed */ + } while (png_ptr->zstream.avail_in); + + /* swap the current and previous rows */ + if (png_ptr->prev_row != NULL) + { + png_bytep tptr; + + tptr = png_ptr->prev_row; + png_ptr->prev_row = png_ptr->row_buf; + png_ptr->row_buf = tptr; + } + + /* finish row - updates counters and flushes zlib if last row */ + png_write_finish_row(png_ptr); + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) + png_ptr->flush_rows++; + + if (png_ptr->flush_dist > 0 && + png_ptr->flush_rows >= png_ptr->flush_dist) + { + png_write_flush(png_ptr); + } +#endif +} +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/com32/lib/lrand48.c b/com32/lib/lrand48.c new file mode 100644 index 0000000..03ad377 --- /dev/null +++ b/com32/lib/lrand48.c @@ -0,0 +1,41 @@ +/* + * lrand48.c + */ + +#include +#include + +unsigned short __rand48_seed[3]; + +long jrand48(unsigned short xsubi[3]) +{ + uint64_t x; + + /* The xsubi[] array is littleendian by spec */ + x = (uint64_t)xsubi[0] + + ((uint64_t)xsubi[1] << 16) + + ((uint64_t)xsubi[2] << 32); + + x = (0x5deece66dULL * x) + 0xb; + + xsubi[0] = (unsigned short)x; + xsubi[1] = (unsigned short)(x >> 16); + xsubi[2] = (unsigned short)(x >> 32); + + return (long)(int32_t)(x >> 16); +} + +long mrand48(void) +{ + return jrand48(__rand48_seed); +} + +long nrand48(unsigned short xsubi[3]) +{ + return (long)((uint32_t)jrand48(xsubi) >> 1); +} + +long lrand48(void) +{ + return (long)((uint32_t)mrand48() >> 1); +} diff --git a/com32/lib/malloc.c b/com32/lib/malloc.c new file mode 100644 index 0000000..40e88b4 --- /dev/null +++ b/com32/lib/malloc.c @@ -0,0 +1,157 @@ +/* + * malloc.c + * + * Very simple linked-list based malloc()/free(). + */ + +#include +#include +#include +#include +#include "init.h" +#include "malloc.h" + +struct free_arena_header __malloc_head = +{ + { + ARENA_TYPE_HEAD, + 0, + &__malloc_head, + &__malloc_head, + }, + &__malloc_head, + &__malloc_head +}; + +/* This is extern so it can be overridden by the user application */ +extern size_t __stack_size; +extern void *__mem_end; /* Produced after argv parsing */ + +static inline size_t sp(void) +{ + size_t sp; + asm volatile("movl %%esp,%0" : "=rm" (sp)); + return sp; +} + +#define E820_MEM_MAX 0xfff00000 /* 4 GB - 1 MB */ + +static int consider_memory_area(void *dummy, addr_t start, + addr_t len, bool valid) +{ + struct free_arena_header *fp; + addr_t end; + + (void)dummy; + + if (valid && start < E820_MEM_MAX) { + if (len > E820_MEM_MAX - start) + len = E820_MEM_MAX - start; + + end = start + len; + + if (end > __com32.cs_memsize) { + if (start <= __com32.cs_memsize) { + start = __com32.cs_memsize; + len = end - start; + } + + if (len >= 2*sizeof(struct arena_header)) { + fp = (struct free_arena_header *)start; + fp->a.size = len; + __inject_free_block(fp); + } + } + } + + return 0; +} + +static void __constructor init_memory_arena(void) +{ + struct free_arena_header *fp; + size_t start, total_space; + + start = (size_t)ARENA_ALIGN_UP(__mem_end); + total_space = sp() - start; + + if ( __stack_size == 0 || __stack_size > total_space >> 1 ) + __stack_size = total_space >> 1; /* Half for the stack, half for the heap... */ + + if ( total_space < __stack_size + 4*sizeof(struct arena_header) ) + __stack_size = total_space - 4*sizeof(struct arena_header); + + fp = (struct free_arena_header *)start; + fp->a.size = total_space - __stack_size; + + __inject_free_block(fp); + + /* Scan the memory map to look for other suitable regions */ + if (!__com32.cs_memsize) + return; /* Old Syslinux core, can't do this... */ + + syslinux_scan_memory(consider_memory_area, NULL); +} + +static void *__malloc_from_block(struct free_arena_header *fp, size_t size) +{ + size_t fsize; + struct free_arena_header *nfp, *na; + + fsize = fp->a.size; + + /* We need the 2* to account for the larger requirements of a free block */ + if ( fsize >= size+2*sizeof(struct arena_header) ) { + /* Bigger block than required -- split block */ + nfp = (struct free_arena_header *)((char *)fp + size); + na = fp->a.next; + + nfp->a.type = ARENA_TYPE_FREE; + nfp->a.size = fsize-size; + fp->a.type = ARENA_TYPE_USED; + fp->a.size = size; + + /* Insert into all-block chain */ + nfp->a.prev = fp; + nfp->a.next = na; + na->a.prev = nfp; + fp->a.next = nfp; + + /* Replace current block on free chain */ + nfp->next_free = fp->next_free; + nfp->prev_free = fp->prev_free; + fp->next_free->prev_free = nfp; + fp->prev_free->next_free = nfp; + } else { + /* Allocate the whole block */ + fp->a.type = ARENA_TYPE_USED; + + /* Remove from free chain */ + fp->next_free->prev_free = fp->prev_free; + fp->prev_free->next_free = fp->next_free; + } + + return (void *)(&fp->a + 1); +} + +void *malloc(size_t size) +{ + struct free_arena_header *fp; + + if ( size == 0 ) + return NULL; + + /* Add the obligatory arena header, and round up */ + size = (size+2*sizeof(struct arena_header)-1) & ARENA_SIZE_MASK; + + for ( fp = __malloc_head.next_free ; fp->a.type != ARENA_TYPE_HEAD ; + fp = fp->next_free ) { + if ( fp->a.size >= size ) { + /* Found fit -- allocate out of this block */ + return __malloc_from_block(fp, size); + } + } + + /* Nothing found... need to request a block from the kernel */ + return NULL; /* No kernel to get stuff from */ +} diff --git a/com32/lib/malloc.h b/com32/lib/malloc.h new file mode 100644 index 0000000..e70b8c9 --- /dev/null +++ b/com32/lib/malloc.h @@ -0,0 +1,55 @@ +/* + * malloc.h + * + * Internals for the memory allocator + */ + +#include +#include + +/* + * This is the minimum chunk size we will ask the kernel for; this should + * be a multiple of the page size on all architectures. + */ +#define MALLOC_CHUNK_SIZE 65536 +#define MALLOC_CHUNK_MASK (MALLOC_CHUNK_SIZE-1) + +/* + * This structure should be a power of two. This becomes the + * alignment unit. + */ +struct free_arena_header; + +struct arena_header { + size_t type; + size_t size; /* Also gives the location of the next entry */ + struct free_arena_header *next, *prev; +}; + +#ifdef DEBUG_MALLOC +#define ARENA_TYPE_USED 0x64e69c70 +#define ARENA_TYPE_FREE 0x012d610a +#define ARENA_TYPE_HEAD 0x971676b5 +#define ARENA_TYPE_DEAD 0xeeeeeeee +#else +#define ARENA_TYPE_USED 0 +#define ARENA_TYPE_FREE 1 +#define ARENA_TYPE_HEAD 2 +#endif + +#define ARENA_SIZE_MASK (~(uintptr_t)(sizeof(struct arena_header)-1)) + +#define ARENA_ALIGN_UP(p) ((char *)(((uintptr_t)(p) + ~ARENA_SIZE_MASK) & ARENA_SIZE_MASK)) +#define ARENA_ALIGN_DOWN(p) ((char *)((uintptr_t)(p) & ARENA_SIZE_MASK)) + +/* + * This structure should be no more than twice the size of the + * previous structure. + */ +struct free_arena_header { + struct arena_header a; + struct free_arena_header *next_free, *prev_free; +}; + +extern struct free_arena_header __malloc_head; +void __inject_free_block(struct free_arena_header *ah); diff --git a/com32/lib/math/pow.S b/com32/lib/math/pow.S new file mode 100644 index 0000000..56f504a --- /dev/null +++ b/com32/lib/math/pow.S @@ -0,0 +1,25 @@ +/* + * pow.S + * + * double pow(double base, double exponent) + */ + + .text + .globl pow + .type pow,@function +pow: + fldl 12(%esp) + fldl 4(%esp) + fyl2x + fld %st(0) + frndint + fsubr %st,%st(1) + fxch %st(1) + f2xm1 + fld1 + faddp %st,%st(1) + fscale + fstp %st(1) + ret + + .size pow,.-pow diff --git a/com32/lib/math/strtod.c b/com32/lib/math/strtod.c new file mode 100644 index 0000000..f4e35ac --- /dev/null +++ b/com32/lib/math/strtod.c @@ -0,0 +1,161 @@ +/* + * strtod.c + * + * Convert string to double + * + * Copyright (C) 2002 Michael Ringgaard. All rights reserved. + * Copyright 2006-2008 H. Peter Anvin - All Rights Reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + * COPYRIGHT OWNER 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. + */ + +#include +#include +#include +#include + +static inline int is_real(double x) +{ + const double Inf = 1.0/0.0; + return (x < Inf) && (x >= -Inf); +} + +double strtod(const char *str, char **endptr) +{ + double number; + int exponent; + int negative; + char *p = (char *) str; + double p10; + int n; + int num_digits; + int num_decimals; + const double Inf = 1.0/0.0; + + // Skip leading whitespace + while (isspace(*p)) p++; + + // Handle optional sign + negative = 0; + switch (*p) + { + case '-': negative = 1; // Fall through to increment position + case '+': p++; + } + + number = 0.; + exponent = 0; + num_digits = 0; + num_decimals = 0; + + // Process string of digits + while (isdigit(*p)) + { + number = number * 10. + (*p - '0'); + p++; + num_digits++; + } + + // Process decimal part + if (*p == '.') + { + p++; + + while (isdigit(*p)) + { + number = number * 10. + (*p - '0'); + p++; + num_digits++; + num_decimals++; + } + + exponent -= num_decimals; + } + + if (num_digits == 0) + { + errno = ERANGE; + return 0.0; + } + + // Correct for sign + if (negative) number = -number; + + // Process an exponent string + if (*p == 'e' || *p == 'E') + { + // Handle optional sign + negative = 0; + switch(*++p) + { + case '-': negative = 1; // Fall through to increment pos + case '+': p++; + } + + // Process string of digits + n = 0; + while (isdigit(*p)) + { + n = n * 10 + (*p - '0'); + p++; + } + + if (negative) + exponent -= n; + else + exponent += n; + } + + if (exponent < __DBL_MIN_EXP__ || + exponent > __DBL_MAX_EXP__) + { + errno = ERANGE; + return Inf; + } + + // Scale the result + p10 = 10.; + n = exponent; + if (n < 0) n = -n; + while (n) + { + if (n & 1) + { + if (exponent < 0) + number /= p10; + else + number *= p10; + } + n >>= 1; + p10 *= p10; + } + + if (!is_real(number)) errno = ERANGE; + if (endptr) *endptr = p; + + return number; +} diff --git a/com32/lib/memccpy.c b/com32/lib/memccpy.c new file mode 100644 index 0000000..22f68de --- /dev/null +++ b/com32/lib/memccpy.c @@ -0,0 +1,23 @@ +/* + * memccpy.c + * + * memccpy() + */ + +#include +#include + +void *memccpy(void *dst, const void *src, int c, size_t n) +{ + char *q = dst; + const char *p = src; + char ch; + + while ( n-- ) { + *q++ = ch = *p++; + if ( ch == (char)c ) + return q; + } + + return NULL; /* No instance of "c" found */ +} diff --git a/com32/lib/memchr.c b/com32/lib/memchr.c new file mode 100644 index 0000000..c5c5fa2 --- /dev/null +++ b/com32/lib/memchr.c @@ -0,0 +1,18 @@ +/* + * memchr.c + */ + +#include +#include + +void *memchr(const void *s, int c, size_t n) +{ + const unsigned char *sp = s; + + while ( n-- ) { + if ( *sp == (unsigned char)c ) + return (void *)sp; + } + + return NULL; +} diff --git a/com32/lib/memcmp.c b/com32/lib/memcmp.c new file mode 100644 index 0000000..f6bc172 --- /dev/null +++ b/com32/lib/memcmp.c @@ -0,0 +1,19 @@ +/* + * memcmp.c + */ + +#include + +int memcmp(const void *s1, const void *s2, size_t n) +{ + const unsigned char *c1 = s1, *c2 = s2; + int d = 0; + + while ( n-- ) { + d = (int)*c1++ - (int)*c2++; + if ( d ) + break; + } + + return d; +} diff --git a/com32/lib/memcpy.S b/com32/lib/memcpy.S new file mode 100644 index 0000000..6b986a0 --- /dev/null +++ b/com32/lib/memcpy.S @@ -0,0 +1,86 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * memcpy.S + * + * Reasonably efficient memcpy, using aligned transfers at least + * for the destination operand. + */ + + .text + .globl memcpy + .type memcpy, @function +memcpy: + jecxz 1f + + pushl %esi + pushl %edi + pushl %eax /* Return value */ + + movl %eax,%edi + movl %edx,%esi + + /* Initial alignment */ + movl %edi,%edx + shrl $1,%edx + jnc 11f + movsb + decl %ecx +11: + movb %cl,%al + cmpl $2,%ecx + jb 13f + + shrl $1,%edx + jnc 12f + movsw + subl $2,%ecx +12: + /* Bulk transfer */ + movb %cl,%al + shrl $2,%ecx + rep; movsl + + /* Final alignment */ + testb $2,%al + jz 14f + movsw +13: +14: + testb $1,%al + jz 15f + movsb +15: + + popl %eax /* Return value */ + popl %edi + popl %esi +1: + ret + + .size memcpy, .-memcpy diff --git a/com32/lib/memmem.c b/com32/lib/memmem.c new file mode 100644 index 0000000..0f59938 --- /dev/null +++ b/com32/lib/memmem.c @@ -0,0 +1,44 @@ +/* + * memmem.c + * + * Find a byte string inside a longer byte string + * + * This uses the "Not So Naive" algorithm, a very simple but + * usually effective algorithm, see: + * + * http://www-igm.univ-mlv.fr/~lecroq/string/ + */ + +#include + +void *memmem(const void *haystack, size_t n, const void *needle, size_t m) +{ + const unsigned char *y = (const unsigned char *)haystack; + const unsigned char *x = (const unsigned char *)needle; + + size_t j, k, l; + + if ( m > n ) + return NULL; + + if ( x[0] == x[1] ) { + k = 2; + l = 1; + } else { + k = 1; + l = 2; + } + + j = 0; + while ( j <= n-m ) { + if (x[1] != y[j+1]) { + j += k; + } else { + if ( !memcmp(x+2, y+j+2, m-2) && x[0] == y[j] ) + return (void *)&y[j]; + j += l; + } + } + + return NULL; +} diff --git a/com32/lib/memmove.S b/com32/lib/memmove.S new file mode 100644 index 0000000..b7ac676 --- /dev/null +++ b/com32/lib/memmove.S @@ -0,0 +1,139 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * memmove.S + * + * Reasonably efficient memmove, using aligned transfers at least + * for the destination operand. + */ + + .globl memmove + .type memmove,@function + .text +memmove: + jecxz 3f + + pushl %esi + pushl %edi + pushl %eax /* Return value */ + + movl %eax,%edi + movl %edx,%esi + + cmpl %edi,%esi + jb 1f + + /* source >= dest, forwards move */ + + /* Initial alignment */ + movl %edi,%edx + shrl $1,%edx + jnc 11f + movsb + decl %ecx +11: + movb %cl,%al + cmpl $2,%ecx + jb 13f + + shrl $1,%edx + jnc 12f + movsw + subl $2,%ecx +12: + /* Bulk transfer */ + movb %cl,%al + shrl $2,%ecx + rep; movsl + + /* Final alignment */ + testb $2,%al + jz 14f + movsw +13: +14: + testb $1,%al + jz 15f + movsb +15: + jmp 2f + + +1: + /* source < dest, backwards move */ + std + leal -1(%ecx,%esi),%esi + leal -1(%ecx,%edi),%edi + + /* Initial alignment */ + movl %edi,%edx + shrl $1,%edx + jc 21f + movsb + decl %ecx +21: + decl %esi + decl %edi + movb %cl,%al + cmpl $2,%ecx + jb 23f + shrl $1,%edx + jc 22f + movsw + subl $2,%ecx +22: + /* Bulk transfer */ + subl $2,%esi + subl $2,%edi + movb %cl,%al + shrl $2,%ecx + rep; movsl + + /* Final alignment */ + addl $2,%esi + addl $2,%edi + testb $2,%al + jz 24f + movsw +23: +24: + incl %esi + incl %edi + testb $1,%al + jz 25f + movsb +25: + cld +2: + popl %eax /* Return value */ + popl %edi + popl %esi +3: + ret + + .size memmove, .-memmove diff --git a/com32/lib/mempcpy.S b/com32/lib/mempcpy.S new file mode 100644 index 0000000..cad7b98 --- /dev/null +++ b/com32/lib/mempcpy.S @@ -0,0 +1,85 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * mempcpy.S + * + * Reasonably efficient mempcpy, using aligned transfers at least + * for the destination operand. + */ + + .text + .globl mempcpy + .type mempcpy, @function +mempcpy: + jecxz 1f + + pushl %esi + pushl %edi + + movl %eax,%edi + movl %edx,%esi + + /* Initial alignment */ + movl %edi,%edx + shrl $1,%edx + jnc 11f + movsb + decl %ecx +11: + movb %cl,%al + cmpl $2,%ecx + jb 13f + + shrl $1,%edx + jnc 12f + movsw + subl $2,%ecx +12: + /* Bulk transfer */ + movb %cl,%al + shrl $2,%ecx + rep; movsl + + /* Final alignment */ + testb $2,%al + jz 14f + movsw +13: +14: + testb $1,%al + jz 15f + movsb +15: + + movl %edi,%eax /* Return value */ + popl %edi + popl %esi +1: + ret + + .size mempcpy, .-mempcpy diff --git a/com32/lib/memset.S b/com32/lib/memset.S new file mode 100644 index 0000000..e641415 --- /dev/null +++ b/com32/lib/memset.S @@ -0,0 +1,86 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * memset.S + * + * Reasonably efficient memset, using aligned transfers at least + * for the destination operand. + */ + + .globl memset + .type memset,@function + .text +memset: + jecxz 6f + + pushl %edi + pushl %ebx + pushl %eax /* Return value */ + + movl %eax,%edi + movb %dl,%dh + movzwl %dx,%eax + shll $16,%edx + orl %edx,%eax + + /* Initial alignment */ + movl %edi,%edx + shrl $1,%edx + jnc 1f + stosb + decl %ecx +1: + movb %cl,%bl + cmpl $2,%ecx + jb 3f + shrl $1,%edx + jnc 2f + stosw + subl $2,%ecx +2: + /* Bulk transfer */ + movb %cl,%bl + shrl $2,%ecx + rep; stosl + + testb $2,%bl + jz 4f + stosw +3: +4: + testb $1,%bl + jz 5f + stosb +5: + popl %eax /* Return value */ + popl %ebx + popl %edi +6: + ret + + .size memset, .-memset diff --git a/com32/lib/memswap.c b/com32/lib/memswap.c new file mode 100644 index 0000000..97fa519 --- /dev/null +++ b/com32/lib/memswap.c @@ -0,0 +1,23 @@ +/* + * memswap() + * + * Swaps the contents of two nonoverlapping memory areas. + * This really could be done faster... + */ + +#include + +void memswap(void *m1, void *m2, size_t n) +{ + char *p = m1; + char *q = m2; + char tmp; + + while ( n-- ) { + tmp = *p; + *p = *q; + *q = tmp; + + p++; q++; + } +} diff --git a/com32/lib/onexit.c b/com32/lib/onexit.c new file mode 100644 index 0000000..c984792 --- /dev/null +++ b/com32/lib/onexit.c @@ -0,0 +1,39 @@ +/* + * onexit.c + */ + +#include +#include +#include "atexit.h" + +extern __noreturn (*__exit_handler)(int); +static struct atexit *__atexit_list; + +static __noreturn on_exit_exit(int rv) +{ + struct atexit *ap; + + for ( ap = __atexit_list ; ap ; ap = ap->next ) { + ap->fctn(rv, ap->arg); /* This assumes extra args are harmless */ + } + + _exit(rv); +} + +int on_exit(void (*fctn)(int, void *), void *arg) +{ + struct atexit *as = malloc(sizeof(struct atexit)); + + if ( !as ) + return -1; + + as->fctn = fctn; + as->arg = arg; + + as->next = __atexit_list; + __atexit_list = as; + + __exit_handler = on_exit_exit; + + return 0; +} diff --git a/com32/lib/opendir.c b/com32/lib/opendir.c new file mode 100644 index 0000000..aa2ba5b --- /dev/null +++ b/com32/lib/opendir.c @@ -0,0 +1,41 @@ +/* + * opendir.c + */ + +#include +#include +#include + +#include +#include +#include +#include + + +DIR *opendir(const char *pathname) +{ + DIR *newdir; + com32sys_t regs; + + newdir = NULL; + + strlcpy(__com32.cs_bounce, pathname, __com32.cs_bounce_size); + + regs.eax.w[0] = 0x0020; + regs.esi.w[0] = OFFS(__com32.cs_bounce); + regs.es = SEG(__com32.cs_bounce); + + __com32.cs_intcall(0x22, ®s, ®s); + + if (!(regs.eflags.l & EFLAGS_CF)) { + /* Initialization: malloc() then zero */ + newdir = calloc(1, sizeof(DIR)); + strcpy(newdir->dd_name, pathname); + newdir->dd_fd = regs.esi.w[0]; + newdir->dd_sect = regs.eax.l; + newdir->dd_stat = 0; + } + + /* We're done */ + return newdir; +} diff --git a/com32/lib/pci/cfgtype.c b/com32/lib/pci/cfgtype.c new file mode 100644 index 0000000..97215d4 --- /dev/null +++ b/com32/lib/pci/cfgtype.c @@ -0,0 +1,89 @@ +#include "pci/pci.h" +#include +#include + +enum pci_config_type __pci_cfg_type; + +static int type1_ok(void) +{ + uint32_t oldcf8, newcf8; + + /* Test for Configuration Method #1 */ + + /* Note: XFree86 writes ~0 and expects to read back 0x80fffffc. Linux + does this less severe test; go with Linux. */ + + cli(); + outb(1, 0xcfb); /* For old Intel chipsets */ + oldcf8 = inl(0xcf8); + outl(0x80000000, 0xcf8); + newcf8 = inl(0xcf8); + outl(oldcf8, 0xcf8); + sti(); + + return newcf8 == 0x80000000; +} + +static int type2_ok(void) +{ + uint8_t oldcf8, oldcfa; + uint8_t cf8, cfa; + + /* Test for Configuration Method #2 */ + + /* CM#2 is hard to probe for, but let's do our best... */ + + cli(); + outb(0, 0xcfb); /* For old Intel chipsets */ + oldcf8 = inb(0xcf8); + outb(0, 0xcf8); + oldcfa = inb(0xcfa); + outb(0, 0xcfa); + + cf8 = inb(0xcf8); + cfa = inb(0xcfa); + + outb(oldcf8, 0xcf8); + outb(oldcfa, 0xcfa); + sti(); + + return cf8 == 0 && cfa == 0; +} + +int pci_set_config_type(enum pci_config_type type) +{ + static const com32sys_t ireg = { + .eax.l = 0xb101, + .edi.l = 0, + .eflags.l = EFLAGS_CF, + }; + com32sys_t oreg; + + if ( type == PCI_CFG_AUTO ) { + type = PCI_CFG_NONE; + + /* Try to detect PCI BIOS */ + __intcall(0x1a, &ireg, &oreg); + + if ( !(oreg.eflags.l & EFLAGS_CF) && + oreg.eax.b[1] == 0 && oreg.edx.l == 0x20494250 ) { + /* PCI BIOS present. Use direct access if we know how to do it. */ + + if ( (oreg.eax.b[0] & 1) && type1_ok() ) + type = PCI_CFG_TYPE1; + else if ( (oreg.eax.b[0] & 2) && type2_ok() ) + type = PCI_CFG_TYPE2; + else + type = PCI_CFG_BIOS; /* Use BIOS calls as fallback */ + + } else if ( type1_ok() ) { + type = PCI_CFG_TYPE1; + } else if ( type2_ok() ) { + type = PCI_CFG_TYPE2; + } else { + type = PCI_CFG_NONE; /* Badness... */ + } + } + + return (__pci_cfg_type = type); +} diff --git a/com32/lib/pci/pci.h b/com32/lib/pci/pci.h new file mode 100644 index 0000000..66a1eb5 --- /dev/null +++ b/com32/lib/pci/pci.h @@ -0,0 +1,16 @@ +/* + * pci/pci.h + * + * Common internal header file + */ + +#ifndef PCI_PCI_H + +#include +#include + +extern enum pci_config_type __pci_cfg_type; +extern uint32_t __pci_read_bios(uint32_t call, pciaddr_t a); +extern void __pci_write_bios(uint32_t call, uint32_t v, pciaddr_t a); + +#endif /* PCI_PCI_H */ diff --git a/com32/lib/pci/readb.c b/com32/lib/pci/readb.c new file mode 100644 index 0000000..c0c4172 --- /dev/null +++ b/com32/lib/pci/readb.c @@ -0,0 +1,4 @@ +#define TYPE uint8_t +#define BWL(x) x ## b +#define BIOSCALL 0xb108 +#include "pci/readx.c" diff --git a/com32/lib/pci/readbios.c b/com32/lib/pci/readbios.c new file mode 100644 index 0000000..34f593c --- /dev/null +++ b/com32/lib/pci/readbios.c @@ -0,0 +1,15 @@ +#include +#include +#include "pci/pci.h" + +uint32_t __pci_read_bios(uint32_t call, pciaddr_t a) +{ + com32sys_t rs; + memset(&rs, 0, sizeof rs); + rs.eax.w[0] = call; + rs.ebx.w[0] = a >> 8; /* bus:device:function */ + rs.edi.b[0] = a; /* address:reg */ + __intcall(0x1a, &rs, &rs); + + return (rs.eflags.l & EFLAGS_CF) ? ~(uint32_t)0 : rs.ecx.l; +} diff --git a/com32/lib/pci/readl.c b/com32/lib/pci/readl.c new file mode 100644 index 0000000..fbef3a7 --- /dev/null +++ b/com32/lib/pci/readl.c @@ -0,0 +1,4 @@ +#define TYPE uint32_t +#define BWL(x) x ## l +#define BIOSCALL 0xb10a +#include "pci/readx.c" diff --git a/com32/lib/pci/readw.c b/com32/lib/pci/readw.c new file mode 100644 index 0000000..86604c4 --- /dev/null +++ b/com32/lib/pci/readw.c @@ -0,0 +1,4 @@ +#define TYPE uint16_t +#define BWL(x) x ## w +#define BIOSCALL 0xb109 +#include "pci/readx.c" diff --git a/com32/lib/pci/readx.c b/com32/lib/pci/readx.c new file mode 100644 index 0000000..3f19cad --- /dev/null +++ b/com32/lib/pci/readx.c @@ -0,0 +1,52 @@ +#include "pci/pci.h" +#include + +TYPE BWL(pci_read) (pciaddr_t a) +{ + TYPE r; + + for (;;) { + switch ( __pci_cfg_type ) { + case PCI_CFG_AUTO: + pci_set_config_type(PCI_CFG_AUTO); + break; /* Try again */ + + case PCI_CFG_TYPE1: + { + uint32_t oldcf8; + cli(); + oldcf8 = inl(0xcf8); + outl(a, 0xcf8); + r = BWL(in) (0xcfc + (a & 3)); + outl(oldcf8, 0xcf8); + sti(); + } + return r; + + case PCI_CFG_TYPE2: + { + uint8_t oldcf8, oldcfa; + + if ( a & (0x10 << 11) ) + return (TYPE)~0; /* Device 16-31 not supported */ + + cli(); + oldcf8 = inb(0xcf8); + oldcfa = inb(0xcfa); + outb(0xf0 + ((a >> (8-1)) & 0x0e), 0xcf8); + outb(a >> 16, 0xcfa); + r = BWL(in) (0xc000 + ((a >> (11-8)) & 0xf00) + (a & 0xff)); + outb(oldcf8, 0xcf8); + outb(oldcfa, 0xcfa); + sti(); + } + return r; + + case PCI_CFG_BIOS: + return (TYPE) __pci_read_bios(BIOSCALL, a); + + default: + return (TYPE)~0; + } + } +} diff --git a/com32/lib/pci/scan.c b/com32/lib/pci/scan.c new file mode 100644 index 0000000..0779410 --- /dev/null +++ b/com32/lib/pci/scan.c @@ -0,0 +1,572 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006-2007 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * pci.c + * + * A module to extract pci informations + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG +# define dprintf printf +#else +# define dprintf(...) ((void)0) +#endif + +#define MAX_LINE 512 + +/* searching the next char that is not a space */ +static char *skipspace(char *p) +{ + while (*p && *p <= ' ') + p++; + + return p; +} + +/* removing any \n found in a string */ +static void remove_eol(char *string) +{ + int j = strlen(string); + int i = 0; + for(i = 0; i < j; i++) if(string[i] == '\n') string[i] = 0; +} + +/* converting a hexa string into its numerical value */ +static int hex_to_int(char *hexa) +{ + return strtoul(hexa, NULL, 16); +} + +/* Try to match any pci device to the appropriate kernel module */ +/* it uses the modules.pcimap from the boot device */ +int get_module_name_from_pcimap(struct pci_domain *domain, char *modules_pcimap_path) +{ + char line[MAX_LINE]; + char module_name[21]; // the module name field is 21 char long + char delims[]=" "; // colums are separated by spaces + char vendor_id[16]; + char product_id[16]; + char sub_vendor_id[16]; + char sub_product_id[16]; + FILE *f; + struct pci_device *dev=NULL; + + /* Intializing the linux_kernel_module for each pci device to "unknown" */ + /* adding a dev_info member if needed */ + for_each_pci_func(dev, domain) { + /* initialize the dev_info structure if it doesn't exist yet. */ + if (! dev->dev_info) { + dev->dev_info = zalloc(sizeof *dev->dev_info); + if (!dev->dev_info) + return -1; + } + for (int i=0;idev_info->linux_kernel_module[i], "unknown",7); + } + } + + /* Opening the modules.pcimap (of a linux kernel) from the boot device */ + f=fopen(modules_pcimap_path, "r"); + if (!f) + return -ENOMODULESPCIMAP; + + strcpy(vendor_id,"0000"); + strcpy(product_id,"0000"); + strcpy(sub_product_id,"0000"); + strcpy(sub_vendor_id,"0000"); + dev->dev_info->linux_kernel_module_count=0; + + /* for each line we found in the modules.pcimap */ + while ( fgets(line, sizeof line, f) ) { + /* skipping unecessary lines */ + if ((line[0] == '#') || (line[0] == ' ') || (line[0] == 10)) + continue; + + char *result = NULL; + int field=0; + + /* looking for the next field */ + result = strtok(line, delims); + while( result != NULL ) { + /* if the column is larger than 1 char */ + /* multiple spaces generates some empty fields */ + if (strlen(result)>1) { + switch (field) { + case 0:strcpy(module_name,result); break; + case 1:strcpy(vendor_id,result); break; + case 2:strcpy(product_id,result); break; + case 3:strcpy(sub_vendor_id,result); break; + case 4:strcpy(sub_product_id,result); break; + } + field++; + } + /* Searching the next field */ + result = strtok( NULL, delims ); + } + int int_vendor_id=hex_to_int(vendor_id); + int int_sub_vendor_id=hex_to_int(sub_vendor_id); + int int_product_id=hex_to_int(product_id); + int int_sub_product_id=hex_to_int(sub_product_id); + /* if a pci_device matches an entry, fill the linux_kernel_module with + the appropriate kernel module */ + for_each_pci_func(dev, domain) { + if (int_vendor_id == dev->vendor && + int_product_id == dev->product && + (int_sub_product_id & dev->sub_product) + == dev->sub_product && + (int_sub_vendor_id & dev->sub_vendor) + == dev->sub_vendor) { + strcpy(dev->dev_info->linux_kernel_module[dev->dev_info->linux_kernel_module_count], module_name); + dev->dev_info->linux_kernel_module_count++; + } + } + } + fclose(f); + return 0; +} + +/* Try to match any pci device to the appropriate class name */ +/* it uses the pci.ids from the boot device */ +int get_class_name_from_pci_ids(struct pci_domain *domain, char *pciids_path) +{ + char line[MAX_LINE]; + char class_name[PCI_CLASS_NAME_SIZE]; + char sub_class_name[PCI_CLASS_NAME_SIZE]; + char class_id_str[5]; + char sub_class_id_str[5]; + FILE *f; + struct pci_device *dev; + bool class_mode=false; + + /* Intializing the vendor/product name for each pci device to "unknown" */ + /* adding a dev_info member if needed */ + for_each_pci_func(dev, domain) { + /* initialize the dev_info structure if it doesn't exist yet. */ + if (! dev->dev_info) { + dev->dev_info = zalloc(sizeof *dev->dev_info); + if (!dev->dev_info) + return -1; + } + strlcpy(dev->dev_info->class_name,"unknown",7); + } + + /* Opening the pci.ids from the boot device */ + f = fopen(pciids_path,"r"); + if (!f) + return -ENOPCIIDS; + + /* for each line we found in the pci.ids */ + while ( fgets(line, sizeof line, f) ) { + /* Skipping uncessary lines */ + if ((line[0] == '#') || (line[0] == ' ') || + (line[0] == 10)) + continue; + + /* Until we found a line starting with a 'C', we are not parsing classes */ + if (line[0] == 'C') + class_mode=true; + if (class_mode == false) + continue; + strlcpy(class_name,"unknown",7); + /* If the line doesn't start with a tab, it means that's a class name */ + if (line[0] != '\t') { + + /* ignore the two first char and then copy 2 chars (class id)*/ + strlcpy(class_id_str,&line[2],2); + class_id_str[2]=0; + + /* the class name is the next field */ + strlcpy(class_name,skipspace(strstr(line," ")),PCI_CLASS_NAME_SIZE-1); + remove_eol(class_name); + + int int_class_id_str=hex_to_int(class_id_str); + /* assign the class_name to any matching pci device */ + for_each_pci_func(dev, domain) { + if (int_class_id_str == dev->class[2]) { + strlcpy(dev->dev_info->class_name,class_name,PCI_CLASS_NAME_SIZE-1); + /* This value is usually the main category*/ + strlcpy(dev->dev_info->category_name,class_name+4,PCI_CLASS_NAME_SIZE-1); + } + } + /* if we have a tab + a char, it means this is a sub class name */ + } else if ((line[0] == '\t') && (line[1] != '\t')) { + + /* the sub class name the second field */ + strlcpy(sub_class_name,skipspace(strstr(line," ")),PCI_CLASS_NAME_SIZE-1); + remove_eol(sub_class_name); + + /* the sub class id is first field */ + strlcpy(sub_class_id_str,&line[1],2); + sub_class_id_str[2]=0; + + int int_class_id_str=hex_to_int(class_id_str); + int int_sub_class_id_str=hex_to_int(sub_class_id_str); + /* assign the product_name to any matching pci device */ + for_each_pci_func(dev, domain) { + if (int_class_id_str == dev->class[2] && + int_sub_class_id_str == dev->class[1]) + strlcpy(dev->dev_info->class_name,sub_class_name,PCI_CLASS_NAME_SIZE-1); + } + + } + } + fclose(f); + return 0; +} + + +/* Try to match any pci device to the appropriate vendor and product name */ +/* it uses the pci.ids from the boot device */ +int get_name_from_pci_ids(struct pci_domain *domain, char *pciids_path) +{ + char line[MAX_LINE]; + char vendor[PCI_VENDOR_NAME_SIZE]; + char vendor_id[5]; + char product[PCI_PRODUCT_NAME_SIZE]; + char product_id[5]; + char sub_product_id[5]; + char sub_vendor_id[5]; + FILE *f; + struct pci_device *dev; + bool skip_to_next_vendor=false; + uint16_t int_vendor_id; + uint16_t int_product_id; + uint16_t int_sub_product_id; + uint16_t int_sub_vendor_id; + + /* Intializing the vendor/product name for each pci device to "unknown" */ + /* adding a dev_info member if needed */ + for_each_pci_func(dev, domain) { + /* initialize the dev_info structure if it doesn't exist yet. */ + if (! dev->dev_info) { + dev->dev_info = zalloc(sizeof *dev->dev_info); + if (!dev->dev_info) + return -1; + } + strlcpy(dev->dev_info->vendor_name,"unknown",7); + strlcpy(dev->dev_info->product_name,"unknown",7); + } + + /* Opening the pci.ids from the boot device */ + f = fopen(pciids_path,"r"); + if (!f) + return -ENOPCIIDS; + + strlcpy(vendor_id,"0000",4); + strlcpy(product_id,"0000",4); + strlcpy(sub_product_id,"0000",4); + strlcpy(sub_vendor_id,"0000",4); + + /* for each line we found in the pci.ids */ + while ( fgets(line, sizeof line, f) ) { + /* Skipping uncessary lines */ + if ((line[0] == '#') || (line[0] == ' ') || (line[0] == 'C') || + (line[0] == 10)) + continue; + + /* If the line doesn't start with a tab, it means that's a vendor id */ + if (line[0] != '\t') { + + /* the 4 first chars are the vendor_id */ + strlcpy(vendor_id,line,4); + + /* the vendor name is the next field */ + vendor_id[4]=0; + strlcpy(vendor,skipspace(strstr(line," ")),PCI_VENDOR_NAME_SIZE-1); + + remove_eol(vendor); + /* init product_id, sub_product and sub_vendor */ + strlcpy(product_id,"0000",4); + strlcpy(sub_product_id,"0000",4); + strlcpy(sub_vendor_id,"0000",4); + + /* Unless we found a matching device, we have to skip to the next vendor */ + skip_to_next_vendor=true; + + int_vendor_id=hex_to_int(vendor_id); + /* Iterate in all pci devices to find a matching vendor */ + for_each_pci_func(dev, domain) { + /* if one device that match this vendor */ + if (int_vendor_id == dev->vendor) { + /* copy the vendor name for this device */ + strlcpy(dev->dev_info->vendor_name,vendor,PCI_VENDOR_NAME_SIZE-1); + /* Some pci devices match this vendor, so we have to found them */ + skip_to_next_vendor=false; + /* Let's loop on the other devices as some may have the same vendor */ + } + } + /* if we have a tab + a char, it means this is a product id + * but we only look at it if we own some pci devices of the current vendor*/ + } else if ((line[0] == '\t') && (line[1] != '\t') && (skip_to_next_vendor == false)) { + + /* the product name the second field */ + strlcpy(product,skipspace(strstr(line," ")),PCI_PRODUCT_NAME_SIZE-1); + remove_eol(product); + + /* the product id is first field */ + strlcpy(product_id,&line[1],4); + product_id[4]=0; + + /* init sub_product and sub_vendor */ + strlcpy(sub_product_id,"0000",4); + strlcpy(sub_vendor_id,"0000",4); + + int_vendor_id=hex_to_int(vendor_id); + int_product_id=hex_to_int(product_id); + /* assign the product_name to any matching pci device */ + for_each_pci_func(dev, domain) { + if (int_vendor_id == dev->vendor && + int_product_id == dev->product) { + strlcpy(dev->dev_info->vendor_name,vendor,PCI_VENDOR_NAME_SIZE-1); + strlcpy(dev->dev_info->product_name,product,PCI_PRODUCT_NAME_SIZE-1); + } + } + + /* if we have two tabs, it means this is a sub product + * but we only look at it if we own some pci devices of the current vendor*/ + } else if ((line[0] == '\t') && (line[1] == '\t') && (skip_to_next_vendor == false)) { + + /* the product name is last field */ + strlcpy(product,skipspace(strstr(line," ")),PCI_PRODUCT_NAME_SIZE-1); + strlcpy(product,skipspace(strstr(product," ")),PCI_PRODUCT_NAME_SIZE-1); + remove_eol(product); + + /* the sub_vendor id is first field */ + strlcpy(sub_vendor_id,&line[2],4); + sub_vendor_id[4]=0; + + /* the sub_vendor id is second field */ + strlcpy(sub_product_id,&line[7],4); + sub_product_id[4]=0; + + int_vendor_id=hex_to_int(vendor_id); + int_sub_vendor_id=hex_to_int(sub_vendor_id); + int_product_id=hex_to_int(product_id); + int_sub_product_id=hex_to_int(sub_product_id); + /* assign the product_name to any matching pci device */ + for_each_pci_func(dev, domain) { + if (int_vendor_id == dev->vendor && + int_product_id == dev->product && + int_sub_product_id == dev->sub_product && + int_sub_vendor_id == dev->sub_vendor) { + strlcpy(dev->dev_info->vendor_name,vendor,PCI_VENDOR_NAME_SIZE-1); + strlcpy(dev->dev_info->product_name,product,PCI_PRODUCT_NAME_SIZE-1); + } + } + } + } + fclose(f); + return 0; +} + +/* searching if any pcidevice match our query */ +struct match *find_pci_device(const struct pci_domain *domain, + struct match *list) +{ + uint32_t did, sid; + struct match *m; + const struct pci_device *dev; + + /* for all matches we have to search */ + for (m = list; m; m = m->next) { + /* for each pci device we know */ + for_each_pci_func(dev, domain) { + /* sid & did are the easiest way to compare devices */ + /* they are made of vendor/product subvendor/subproduct ids */ + sid = dev->svid_sdid; + did = dev->vid_did; + /* if the current device match */ + if (((did ^ m->did) & m->did_mask) == 0 && + ((sid ^ m->sid) & m->sid_mask) == 0 && + dev->revision >= m->rid_min + && dev->revision <= m->rid_max) { + dprintf("PCI Match: Vendor=%04x Product=%04x Sub_vendor=%04x Sub_Product=%04x Release=%02x\n", + dev->vendor, dev->product, + dev->sub_vendor, + dev->sub_product, + dev->revision); + /* returning the matched pci device */ + return m; + } + } + } + return NULL; +} + +/* scanning the pci bus to find pci devices */ +struct pci_domain *pci_scan(void) +{ + struct pci_domain *domain = NULL; + struct pci_bus *bus = NULL; + struct pci_slot *slot = NULL; + struct pci_device *func = NULL; + unsigned int nbus, ndev, nfunc, maxfunc; + uint32_t did, sid, rcid; + uint8_t hdrtype; + pciaddr_t a; + int cfgtype; + + cfgtype = pci_set_config_type(PCI_CFG_AUTO); + + dprintf("PCI configuration type %d\n", cfgtype); + + if (cfgtype == PCI_CFG_NONE) + return NULL; + + dprintf("Scanning PCI Buses\n"); + + for (nbus = 0; nbus < MAX_PCI_BUSES; nbus++) { + dprintf("Probing bus 0x%02x... \n", nbus); + bus = NULL; + + for (ndev = 0; ndev < MAX_PCI_DEVICES; ndev++) { + maxfunc = 1; /* Assume a single-function device */ + slot = NULL; + + for (nfunc = 0; nfunc < maxfunc; nfunc++) { + a = pci_mkaddr(nbus, ndev, nfunc, 0); + did = pci_readl(a); + + if (did == 0xffffffff || did == 0xffff0000 || + did == 0x0000ffff || did == 0x00000000) + continue; + + hdrtype = pci_readb(a + 0x0e); + + if (hdrtype & 0x80) + maxfunc = MAX_PCI_FUNC; /* Multifunction device */ + + rcid = pci_readl(a + 0x08); + sid = pci_readl(a + 0x2c); + + if (!domain) { + domain = zalloc(sizeof *domain); + if (!domain) + goto bail; + } + if (!bus) { + bus = zalloc(sizeof *bus); + if (!bus) + goto bail; + domain->bus[nbus] = bus; + } + if (!slot) { + slot = zalloc(sizeof *slot); + if (!slot) + goto bail; + bus->slot[ndev] = slot; + } + func = zalloc(sizeof *func); + if (!func) + goto bail; + + slot->func[nfunc] = func; + + func->vid_did = did; + func->svid_sdid = sid; + func->rid_class = rcid; + + dprintf + ("Scanning: BUS %02x DID %08x (%04x:%04x) SID %08x RID %02x\n", + nbus, did, did >> 16, (did << 16) >> 16, + sid, rcid & 0xff); + } + } + } + + return domain; + + bail: + free_pci_domain(domain); + return NULL; +} + +/* gathering additional configuration*/ +void gather_additional_pci_config(struct pci_domain *domain) +{ + struct pci_device *dev; + pciaddr_t pci_addr; + int cfgtype; + + cfgtype = pci_set_config_type(PCI_CFG_AUTO); + if (cfgtype == PCI_CFG_NONE) + return; + + for_each_pci_func3(dev, domain,pci_addr) { + if (! dev->dev_info) { + dev->dev_info = zalloc(sizeof *dev->dev_info); + if (!dev->dev_info) { + return; + } + } + dev->dev_info->irq = pci_readb(pci_addr + 0x3c); + dev->dev_info->latency = pci_readb(pci_addr + 0x0d); + } +} + + +void free_pci_domain(struct pci_domain *domain) +{ + struct pci_bus *bus; + struct pci_slot *slot; + struct pci_device *func; + unsigned int nbus, ndev, nfunc; + + if (domain) { + for (nbus = 0; nbus < MAX_PCI_BUSES; nbus++) { + bus = domain->bus[nbus]; + if (bus) { + for (ndev = 0; ndev < MAX_PCI_DEVICES; ndev++) { + slot = bus->slot[ndev]; + if (slot) { + for (nfunc = 0; nfunc < MAX_PCI_FUNC; nfunc++) { + func = slot->func[nfunc]; + if (func) { + if (func->dev_info) + free(func->dev_info); + free(func); + } + free(slot); + } + } + free(bus); + } + } + free(domain); + } + } +} diff --git a/com32/lib/pci/writeb.c b/com32/lib/pci/writeb.c new file mode 100644 index 0000000..5a9a24a --- /dev/null +++ b/com32/lib/pci/writeb.c @@ -0,0 +1,4 @@ +#define TYPE uint8_t +#define BWL(x) x ## b +#define BIOSCALL 0xb10b +#include "pci/writex.c" diff --git a/com32/lib/pci/writebios.c b/com32/lib/pci/writebios.c new file mode 100644 index 0000000..b890559 --- /dev/null +++ b/com32/lib/pci/writebios.c @@ -0,0 +1,14 @@ +#include +#include +#include "pci/pci.h" + +void __pci_write_bios(uint32_t call, uint32_t v, pciaddr_t a) +{ + com32sys_t rs; + memset(&rs, 0, sizeof rs); + rs.eax.w[0] = call; + rs.ebx.w[0] = a >> 8; /* bus:device:function */ + rs.edi.b[0] = a; /* address:reg */ + rs.ecx.l = v; + __intcall(0x1a, &rs, NULL); +} diff --git a/com32/lib/pci/writel.c b/com32/lib/pci/writel.c new file mode 100644 index 0000000..df9fc7b --- /dev/null +++ b/com32/lib/pci/writel.c @@ -0,0 +1,4 @@ +#define TYPE uint32_t +#define BWL(x) x ## l +#define BIOSCALL 0xb10d +#include "pci/writex.c" diff --git a/com32/lib/pci/writew.c b/com32/lib/pci/writew.c new file mode 100644 index 0000000..e5a948a --- /dev/null +++ b/com32/lib/pci/writew.c @@ -0,0 +1,4 @@ +#define TYPE uint16_t +#define BWL(x) x ## w +#define BIOSCALL 0xb10c +#include "pci/writex.c" diff --git a/com32/lib/pci/writex.c b/com32/lib/pci/writex.c new file mode 100644 index 0000000..d5a53c0 --- /dev/null +++ b/com32/lib/pci/writex.c @@ -0,0 +1,50 @@ +#include "pci/pci.h" + +void BWL(pci_write) (TYPE v, pciaddr_t a) +{ + for (;;) { + switch ( __pci_cfg_type ) { + case PCI_CFG_AUTO: + pci_set_config_type(PCI_CFG_AUTO); + break; /* Try again */ + + case PCI_CFG_TYPE1: + { + uint32_t oldcf8; + cli(); + oldcf8 = inl(0xcf8); + outl(a, 0xcf8); + BWL(out) (v, 0xcfc + (a & 3)); + outl(oldcf8, 0xcf8); + sti(); + } + return; + + case PCI_CFG_TYPE2: + { + uint8_t oldcf8, oldcfa; + + if ( a & (0x10 << 11) ) + return; /* Devices 16-31 not supported */ + + cli(); + oldcf8 = inb(0xcf8); + oldcfa = inb(0xcfa); + outb(0xf0 + ((a >> (8-1)) & 0x0e), 0xcf8); + outb(a >> 16, 0xcfa); + BWL(out) (v, 0xc000 + ((a >> (11-8)) & 0xf00) + (a & 0xff)); + outb(oldcf8, 0xcf8); + outb(oldcfa, 0xcfa); + sti(); + } + return; + + case PCI_CFG_BIOS: + __pci_write_bios(BIOSCALL, v, a); + return; + + default: + return; + } + } +} diff --git a/com32/lib/perror.c b/com32/lib/perror.c new file mode 100644 index 0000000..45585cd --- /dev/null +++ b/com32/lib/perror.c @@ -0,0 +1,12 @@ +/* + * perror.c + */ + +#include +#include +#include + +void perror(const char *s) +{ + fprintf(stderr, "%s: error %d\n", s, errno); +} diff --git a/com32/lib/printf.c b/com32/lib/printf.c new file mode 100644 index 0000000..3423759 --- /dev/null +++ b/com32/lib/printf.c @@ -0,0 +1,19 @@ +/* + * printf.c + */ + +#include +#include + +#define BUFFER_SIZE 16384 + +int printf(const char *format, ...) +{ + va_list ap; + int rv; + + va_start(ap, format); + rv = vfprintf(stdout, format, ap); + va_end(ap); + return rv; +} diff --git a/com32/lib/putchar.c b/com32/lib/putchar.c new file mode 100644 index 0000000..4b340d1 --- /dev/null +++ b/com32/lib/putchar.c @@ -0,0 +1,16 @@ +/* + * putchar.c + * + * gcc "printf decompilation" expects this to exist... + */ + +#include + +#undef putchar + +int putchar(int c) +{ + unsigned char ch = c; + + return _fwrite(&ch, 1, stdout) == 1 ? ch : EOF; +} diff --git a/com32/lib/puts.c b/com32/lib/puts.c new file mode 100644 index 0000000..ecebf27 --- /dev/null +++ b/com32/lib/puts.c @@ -0,0 +1,13 @@ +/* + * puts.c + */ + +#include + +int puts(const char *s) +{ + if ( fputs(s, stdout) < 0 ) + return -1; + + return _fwrite("\n", 1, stdout); +} diff --git a/com32/lib/qsort.c b/com32/lib/qsort.c new file mode 100644 index 0000000..312872d --- /dev/null +++ b/com32/lib/qsort.c @@ -0,0 +1,41 @@ +/* + * qsort.c + * + * This is actually combsort. It's an O(n log n) algorithm with + * simplicity/small code size being its main virtue. + */ + +#include +#include + +static inline size_t newgap(size_t gap) +{ + gap = (gap*10)/13; + if ( gap == 9 || gap == 10 ) + gap = 11; + + if ( gap < 1 ) + gap = 1; + return gap; +} + +void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)) +{ + size_t gap = nmemb; + size_t i, j; + char *p1, *p2; + int swapped; + + do { + gap = newgap(gap); + swapped = 0; + + for ( i = 0, p1 = base ; i < nmemb-gap ; i++, p1 += size ) { + j = i+gap; + if ( compar(p1, p2 = (char *)base+j*size) > 0 ) { + memswap(p1, p2, size); + swapped = 1; + } + } + } while ( gap > 1 || swapped ); +} diff --git a/com32/lib/readdir.c b/com32/lib/readdir.c new file mode 100644 index 0000000..bfa5252 --- /dev/null +++ b/com32/lib/readdir.c @@ -0,0 +1,54 @@ +/* + * readdir.c + */ + +#include +#include +#include + +#include +#include +#include +#include + +struct dirent *readdir(DIR *dir) +{ + struct dirent *newde; + com32sys_t regs; + + newde = NULL; + if ((dir != NULL) && (dir->dd_fd != 0) && (dir->dd_stat >= 0)) { + memset(__com32.cs_bounce, 0, 32); + memset(®s, 0, sizeof(regs)); + + regs.eax.w[0] = 0x0021; + regs.esi.w[0] = dir->dd_fd; + regs.edi.w[0] = OFFS(__com32.cs_bounce); + regs.es = SEG(__com32.cs_bounce); + + __com32.cs_intcall(0x22, ®s, ®s); + + /* Don't do this as we won't be able to rewind. + dir->dd_fd = regs.esi.w[0]; /* Shouldn't be needed? */ + if ((!(regs.eflags.l & EFLAGS_CF)) && (regs.esi.w[0] != 0)) { + newde = calloc(1, sizeof(newde)); + if (newde != NULL) { + strcpy(newde->d_name, __com32.cs_bounce); + newde->d_mode = regs.edx.b[0]; + newde->d_size = regs.eax.l; + newde->d_ino = regs.ebx.l; + dir->dd_stat = 1; + } else { + dir->dd_stat = -2; + errno = ENOMEM; + } + } else { + dir->dd_stat = -1; + errno = EIO; /* Is this the right nmber? */ + } + } else { + errno = EBADF; + } + + return newde; +} diff --git a/com32/lib/realloc.c b/com32/lib/realloc.c new file mode 100644 index 0000000..2161a75 --- /dev/null +++ b/com32/lib/realloc.c @@ -0,0 +1,98 @@ +/* + * realloc.c + */ + +#include +#include +#include + +#include "malloc.h" + +void *realloc(void *ptr, size_t size) +{ + struct free_arena_header *ah, *nah; + void *newptr; + size_t newsize, oldsize, xsize; + + if ( !ptr ) + return malloc(size); + + if ( size == 0 ) { + free(ptr); + return NULL; + } + + ah = (struct free_arena_header *) + ((struct arena_header *)ptr - 1); + + /* Actual size of the old block */ + oldsize = ah->a.size; + + /* Add the obligatory arena header, and round up */ + newsize = (size+2*sizeof(struct arena_header)-1) & ARENA_SIZE_MASK; + + if ( oldsize >= newsize && newsize >= (oldsize >> 2) && + oldsize-newsize < 4096 ) { + /* This allocation is close enough already. */ + return ptr; + } else { + xsize = oldsize; + + nah = ah->a.next; + if ((char *)nah == (char *)ah + ah->a.size && + nah->a.type == ARENA_TYPE_FREE && + oldsize + nah->a.size >= newsize) { + /* Merge in subsequent free block */ + ah->a.next = nah->a.next; + ah->a.next->a.prev = ah; + nah->next_free->prev_free = nah->prev_free; + nah->prev_free->next_free = nah->next_free; + xsize = (ah->a.size += nah->a.size); + } + + if (xsize >= newsize) { + /* We can reallocate in place */ + if (xsize >= newsize + 2*sizeof(struct arena_header)) { + /* Residual free block at end */ + nah = (struct free_arena_header *)((char *)ah + newsize); + nah->a.type = ARENA_TYPE_FREE; + nah->a.size = xsize - newsize; + ah->a.size = newsize; + + /* Insert into block list */ + nah->a.next = ah->a.next; + ah->a.next = nah; + nah->a.next->a.prev = nah; + nah->a.prev = ah; + + /* Insert into free list */ + if (newsize > oldsize) { + /* Hack: this free block is in the path of a memory object + which has already been grown at least once. As such, put + it at the *end* of the freelist instead of the beginning; + trying to save it for future realloc()s of the same block. */ + nah->prev_free = __malloc_head.prev_free; + nah->next_free = &__malloc_head; + __malloc_head.prev_free = nah; + nah->prev_free->next_free = nah; + } else { + nah->next_free = __malloc_head.next_free; + nah->prev_free = &__malloc_head; + __malloc_head.next_free = nah; + nah->next_free->prev_free = nah; + } + } + /* otherwise, use up the whole block */ + return ptr; + } else { + /* Last resort: need to allocate a new block and copy */ + oldsize -= sizeof(struct arena_header); + newptr = malloc(size); + if (newptr) { + memcpy(newptr, ptr, min(size,oldsize)); + free(ptr); + } + return newptr; + } + } +} diff --git a/com32/lib/seed48.c b/com32/lib/seed48.c new file mode 100644 index 0000000..c1d1730 --- /dev/null +++ b/com32/lib/seed48.c @@ -0,0 +1,18 @@ +/* + * seed48.c + */ + +#include +#include +#include + +extern unsigned short __rand48_seed[3]; + +unsigned short *seed48(const unsigned short xsubi[3]) +{ + static unsigned short oldseed[3]; + memcpy(oldseed, __rand48_seed, sizeof __rand48_seed); + memcpy(__rand48_seed, xsubi, sizeof __rand48_seed); + + return oldseed; +} diff --git a/com32/lib/setjmp.S b/com32/lib/setjmp.S new file mode 100644 index 0000000..658df48 --- /dev/null +++ b/com32/lib/setjmp.S @@ -0,0 +1,63 @@ +/* + * arch/i386/setjmp.S + * + * setjmp/longjmp for the i386 architecture + * + * + * + * The jmp_buf is assumed to contain the following, in order: + * %ebx + * %esp + * %ebp + * %esi + * %edi + * + */ + + .text + .align 4 + + .globl _setjmp + .type _setjmp, @function +_setjmp: # gcc 4.0.1 wants this as an alias? + + .globl setjmp + .type setjmp, @function +setjmp: +#ifdef REGPARM + movl %eax,%edx +#else + movl 4(%esp),%edx +#endif + popl %ecx # Return address, and adjust the stack + xorl %eax,%eax # Return value + movl %ebx,(%edx) + movl %esp,4(%edx) # Post-return %esp! + pushl %ecx # Make the call/return stack happy + movl %ebp,8(%edx) + movl %esi,12(%edx) + movl %edi,16(%edx) + movl %ecx,20(%edx) # Return address + ret + + .size setjmp,.-setjmp + + .text + .align 4 + .globl longjmp + .type longjmp, @function +longjmp: +#ifdef REGPARM + xchgl %eax,%edx +#else + movl 4(%esp),%edx # jmp_ptr address + movl 8(%esp),%eax # Return value +#endif + movl (%edx),%ebx + movl 4(%edx),%esp + movl 8(%edx),%ebp + movl 12(%edx),%esi + movl 16(%edx),%edi + jmp *20(%edx) + + .size longjmp,.-longjmp diff --git a/com32/lib/snprintf.c b/com32/lib/snprintf.c new file mode 100644 index 0000000..c642851 --- /dev/null +++ b/com32/lib/snprintf.c @@ -0,0 +1,16 @@ +/* + * snprintf.c + */ + +#include + +int snprintf(char *buffer, size_t n, const char *format, ...) +{ + va_list ap; + int rv; + + va_start(ap, format); + rv = vsnprintf(buffer, n, format, ap); + va_end(ap); + return rv; +} diff --git a/com32/lib/sprintf.c b/com32/lib/sprintf.c new file mode 100644 index 0000000..31f28af --- /dev/null +++ b/com32/lib/sprintf.c @@ -0,0 +1,18 @@ +/* + * sprintf.c + */ + +#include +#include + +int sprintf(char *buffer, const char *format, ...) +{ + va_list ap; + int rv; + + va_start(ap, format); + rv = vsnprintf(buffer, ~(size_t)0, format, ap); + va_end(ap); + + return rv; +} diff --git a/com32/lib/srand48.c b/com32/lib/srand48.c new file mode 100644 index 0000000..a3df16d --- /dev/null +++ b/com32/lib/srand48.c @@ -0,0 +1,16 @@ +/* + * srand48.c + */ + +#include +#include + +extern unsigned short __rand48_seed[3]; + + +void srand48(long seedval) +{ + __rand48_seed[0] = 0x330e; + __rand48_seed[1] = (unsigned short)seedval; + __rand48_seed[2] = (unsigned short)((uint32_t)seedval >> 16); +} diff --git a/com32/lib/sscanf.c b/com32/lib/sscanf.c new file mode 100644 index 0000000..81aab9e --- /dev/null +++ b/com32/lib/sscanf.c @@ -0,0 +1,17 @@ +/* + * sscanf() + */ + +#include + +int sscanf(const char *str, const char *format, ...) +{ + va_list ap; + int rv; + + va_start(ap, format); + rv = vsscanf(str, format, ap); + va_end(ap); + + return rv; +} diff --git a/com32/lib/stack.c b/com32/lib/stack.c new file mode 100644 index 0000000..dd7d9d4 --- /dev/null +++ b/com32/lib/stack.c @@ -0,0 +1,4 @@ +#include + +/* Default stack size 8 MB */ +size_t __stack_size = 8 << 20; diff --git a/com32/lib/stpcpy.c b/com32/lib/stpcpy.c new file mode 100644 index 0000000..85fc49d --- /dev/null +++ b/com32/lib/stpcpy.c @@ -0,0 +1,23 @@ +/* + * stpcpy.c + * + * stpcpy() + */ + +#include + +char *stpcpy(char *dst, const char *src) +{ + char *q = dst; + const char *p = src; + char ch; + + for (;;) { + *q = ch = *p++; + if ( !ch ) + break; + q++; + } + + return q; +} diff --git a/com32/lib/stpncpy.c b/com32/lib/stpncpy.c new file mode 100644 index 0000000..27f6003 --- /dev/null +++ b/com32/lib/stpncpy.c @@ -0,0 +1,23 @@ +/* + * stpncpy.c + * + * stpncpy() + */ + +#include + +char *stpncpy(char *dst, const char *src, size_t n) +{ + char *q = dst; + const char *p = src; + char ch; + + while ( n-- ) { + *q = ch = *p++; + if ( !ch ) + break; + q++; + } + + return q; +} diff --git a/com32/lib/strcasecmp.c b/com32/lib/strcasecmp.c new file mode 100644 index 0000000..12aef40 --- /dev/null +++ b/com32/lib/strcasecmp.c @@ -0,0 +1,23 @@ +/* + * strcasecmp.c + */ + +#include +#include + +int strcasecmp(const char *s1, const char *s2) +{ + const unsigned char *c1 = s1, *c2 = s2; + unsigned char ch; + int d = 0; + + while ( 1 ) { + /* toupper() expects an unsigned char (implicitly cast to int) + as input, and returns an int, which is exactly what we want. */ + d = toupper(ch = *c1++) - toupper(*c2++); + if ( d || !ch ) + break; + } + + return d; +} diff --git a/com32/lib/strcat.c b/com32/lib/strcat.c new file mode 100644 index 0000000..a5f9477 --- /dev/null +++ b/com32/lib/strcat.c @@ -0,0 +1,11 @@ +/* + * strcat.c + */ + +#include + +char *strcat(char *dst, const char *src) +{ + strcpy(strchr(dst, '\0'), src); + return dst; +} diff --git a/com32/lib/strchr.c b/com32/lib/strchr.c new file mode 100644 index 0000000..192f836 --- /dev/null +++ b/com32/lib/strchr.c @@ -0,0 +1,16 @@ +/* + * strchr.c + */ + +#include + +char *strchr(const char *s, int c) +{ + while ( *s != (char)c ) { + if ( ! *s ) + return NULL; + s++; + } + + return (char *)s; +} diff --git a/com32/lib/strcmp.c b/com32/lib/strcmp.c new file mode 100644 index 0000000..f44774f --- /dev/null +++ b/com32/lib/strcmp.c @@ -0,0 +1,20 @@ +/* + * strcmp.c + */ + +#include + +int strcmp(const char *s1, const char *s2) +{ + const unsigned char *c1 = s1, *c2 = s2; + unsigned char ch; + int d = 0; + + while ( 1 ) { + d = (int)(ch = *c1++) - (int)*c2++; + if ( d || !ch ) + break; + } + + return d; +} diff --git a/com32/lib/strcpy.c b/com32/lib/strcpy.c new file mode 100644 index 0000000..8372eba --- /dev/null +++ b/com32/lib/strcpy.c @@ -0,0 +1,20 @@ +/* + * strcpy.c + * + * strcpy() + */ + +#include + +char *strcpy(char *dst, const char *src) +{ + char *q = dst; + const char *p = src; + char ch; + + do { + *q++ = ch = *p++; + } while ( ch ); + + return dst; +} diff --git a/com32/lib/strdup.c b/com32/lib/strdup.c new file mode 100644 index 0000000..eb170c2 --- /dev/null +++ b/com32/lib/strdup.c @@ -0,0 +1,17 @@ +/* + * strdup.c + */ + +#include +#include + +char *strdup(const char *s) +{ + int l = strlen(s)+1; + char *d = malloc(l); + + if ( d ) + memcpy(d, s, l); + + return d; +} diff --git a/com32/lib/strerror.c b/com32/lib/strerror.c new file mode 100644 index 0000000..5d3fc9a --- /dev/null +++ b/com32/lib/strerror.c @@ -0,0 +1,23 @@ +/* + * strerror.c + */ + +#include + +char *strerror(int errnum) +{ + static char message[32] = "error "; /* enough for error 2^63-1 */ + + char numbuf[32]; + char *p; + + p = numbuf+sizeof numbuf; + *--p = '\0'; + + do { + *--p = (errnum % 10) + '0'; + errnum /= 10; + } while ( errnum ); + + return (char *)memcpy(message+6, p, (numbuf+sizeof numbuf)-p); +} diff --git a/com32/lib/strlcat.c b/com32/lib/strlcat.c new file mode 100644 index 0000000..f9db715 --- /dev/null +++ b/com32/lib/strlcat.c @@ -0,0 +1,29 @@ +/* + * strlcat.c + */ + +#include +#include + +size_t strlcat(char *dst, const char *src, size_t size) +{ + size_t bytes = 0; + char *q = dst; + const char *p = src; + char ch; + + while ( bytes < size && *q ) { + q++; + bytes++; + } + + while ( (ch = *p++) ) { + if ( bytes < size ) + *q++ = ch; + + bytes++; + } + + *q = '\0'; + return bytes; +} diff --git a/com32/lib/strlcpy.c b/com32/lib/strlcpy.c new file mode 100644 index 0000000..ba4272b --- /dev/null +++ b/com32/lib/strlcpy.c @@ -0,0 +1,24 @@ +/* + * strlcpy.c + */ + +#include +#include + +size_t strlcpy(char *dst, const char *src, size_t size) +{ + size_t bytes = 0; + char *q = dst; + const char *p = src; + char ch; + + while ( (ch = *p++) ) { + if ( bytes < size ) + *q++ = ch; + + bytes++; + } + + *q = '\0'; + return bytes; +} diff --git a/com32/lib/strlen.c b/com32/lib/strlen.c new file mode 100644 index 0000000..847c16e --- /dev/null +++ b/com32/lib/strlen.c @@ -0,0 +1,13 @@ +/* + * strlen() + */ + +#include + +size_t strlen(const char *s) +{ + const char *ss = s; + while ( *ss ) + ss++; + return ss-s; +} diff --git a/com32/lib/strncasecmp.c b/com32/lib/strncasecmp.c new file mode 100644 index 0000000..3309d1a --- /dev/null +++ b/com32/lib/strncasecmp.c @@ -0,0 +1,23 @@ +/* + * strncasecmp.c + */ + +#include +#include + +int strncasecmp(const char *s1, const char *s2, size_t n) +{ + const unsigned char *c1 = s1, *c2 = s2; + unsigned char ch; + int d = 0; + + while ( n-- ) { + /* toupper() expects an unsigned char (implicitly cast to int) + as input, and returns an int, which is exactly what we want. */ + d = toupper(ch = *c1++) - toupper(*c2++); + if ( d || !ch ) + break; + } + + return d; +} diff --git a/com32/lib/strncat.c b/com32/lib/strncat.c new file mode 100644 index 0000000..99d9575 --- /dev/null +++ b/com32/lib/strncat.c @@ -0,0 +1,11 @@ +/* + * strncat.c + */ + +#include + +char *strncat(char *dst, const char *src, size_t n) +{ + strncpy(strchr(dst, '\0'), src, n); + return dst; +} diff --git a/com32/lib/strncmp.c b/com32/lib/strncmp.c new file mode 100644 index 0000000..4dbde13 --- /dev/null +++ b/com32/lib/strncmp.c @@ -0,0 +1,20 @@ +/* + * strncmp.c + */ + +#include + +int strncmp(const char *s1, const char *s2, size_t n) +{ + const unsigned char *c1 = s1, *c2 = s2; + unsigned char ch; + int d = 0; + + while ( n-- ) { + d = (int)(ch = *c1++) - (int)*c2++; + if ( d || !ch ) + break; + } + + return d; +} diff --git a/com32/lib/strncpy.c b/com32/lib/strncpy.c new file mode 100644 index 0000000..a8fe45f --- /dev/null +++ b/com32/lib/strncpy.c @@ -0,0 +1,22 @@ +/* + * strncpy.c + * + * strncpy() + */ + +#include + +char *strncpy(char *dst, const char *src, size_t n) +{ + char *q = dst; + const char *p = src; + char ch; + + while ( n-- ) { + *q++ = ch = *p++; + if ( !ch ) + break; + } + + return dst; +} diff --git a/com32/lib/strndup.c b/com32/lib/strndup.c new file mode 100644 index 0000000..1b44e6f --- /dev/null +++ b/com32/lib/strndup.c @@ -0,0 +1,17 @@ +/* + * strndup.c + */ + +#include +#include + +char *strndup(const char *s, size_t n) +{ + int l = n > strlen(s) ? strlen(s)+1 : n+1; + char *d = malloc(l); + + if (d) + memcpy(d, s, l); + d[n] = '\0'; + return d; +} diff --git a/com32/lib/strnlen.c b/com32/lib/strnlen.c new file mode 100644 index 0000000..d073561 --- /dev/null +++ b/com32/lib/strnlen.c @@ -0,0 +1,13 @@ +/* + * strnlen() + */ + +#include + +size_t strnlen(const char *s, size_t n) +{ + const char *ss = s; + while ( n-- && *ss ) + ss++; + return ss-s; +} diff --git a/com32/lib/strntoimax.c b/com32/lib/strntoimax.c new file mode 100644 index 0000000..f53a266 --- /dev/null +++ b/com32/lib/strntoimax.c @@ -0,0 +1,13 @@ +/* + * strntoimax.c + * + * strntoimax() + */ + +#include +#include + +intmax_t strntoimax(const char *nptr, char **endptr, int base, size_t n) +{ + return (intmax_t) strntoumax(nptr, endptr, base, n); +} diff --git a/com32/lib/strntoumax.c b/com32/lib/strntoumax.c new file mode 100644 index 0000000..4e30637 --- /dev/null +++ b/com32/lib/strntoumax.c @@ -0,0 +1,75 @@ +/* + * strntoumax.c + * + * The strntoumax() function and associated + */ + +#include +#include +#include + +static inline int digitval(int ch) +{ + if ( ch >= '0' && ch <= '9' ) { + return ch-'0'; + } else if ( ch >= 'A' && ch <= 'Z' ) { + return ch-'A'+10; + } else if ( ch >= 'a' && ch <= 'z' ) { + return ch-'a'+10; + } else { + return -1; + } +} + +uintmax_t strntoumax(const char *nptr, char **endptr, int base, size_t n) +{ + int minus = 0; + uintmax_t v = 0; + int d; + + while ( n && isspace((unsigned char)*nptr) ) { + nptr++; + n--; + } + + /* Single optional + or - */ + if ( n && *nptr == '-' ) { + minus = 1; + nptr++; + n--; + } else if ( n && *nptr == '+' ) { + nptr++; + } + + if ( base == 0 ) { + if ( n >= 2 && nptr[0] == '0' && + (nptr[1] == 'x' || nptr[1] == 'X') ) { + n -= 2; + nptr += 2; + base = 16; + } else if ( n >= 1 && nptr[0] == '0' ) { + n--; + nptr++; + base = 8; + } else { + base = 10; + } + } else if ( base == 16 ) { + if ( n >= 2 && nptr[0] == '0' && + (nptr[1] == 'x' || nptr[1] == 'X') ) { + n -= 2; + nptr += 2; + } + } + + while ( n && (d = digitval(*nptr)) >= 0 && d < base ) { + v = v*base + d; + n--; + nptr++; + } + + if ( endptr ) + *endptr = (char *)nptr; + + return minus ? -v : v; +} diff --git a/com32/lib/strpcpy.c b/com32/lib/strpcpy.c new file mode 100644 index 0000000..e181e78 --- /dev/null +++ b/com32/lib/strpcpy.c @@ -0,0 +1,20 @@ +/* + * strpcpy.c + * + * strpcpy() - strcpy() which returns a pointer to the final null + */ + +#include + +char *strpcpy(char *dst, const char *src) +{ + char *q = dst; + const char *p = src; + char ch; + + do { + *q++ = ch = *p++; + } while ( ch ); + + return q-1; +} diff --git a/com32/lib/strrchr.c b/com32/lib/strrchr.c new file mode 100644 index 0000000..b9f2004 --- /dev/null +++ b/com32/lib/strrchr.c @@ -0,0 +1,18 @@ +/* + * strrchr.c + */ + +#include + +char *strrchr(const char *s, int c) +{ + const char *found = NULL; + + while ( *s ) { + if ( *s == (char) c ) + found = s; + s++; + } + + return (char *)found; +} diff --git a/com32/lib/strsep.c b/com32/lib/strsep.c new file mode 100644 index 0000000..58a7a07 --- /dev/null +++ b/com32/lib/strsep.c @@ -0,0 +1,21 @@ +/* + * strsep.c + */ + +#include + +char *strsep(char **stringp, const char *delim) +{ + char *s = *stringp; + char *e; + + if ( !s ) + return NULL; + + e = strpbrk(s, delim); + if (e) + *e++ = '\0'; + + *stringp = e; + return s; +} diff --git a/com32/lib/strspn.c b/com32/lib/strspn.c new file mode 100644 index 0000000..d569e02 --- /dev/null +++ b/com32/lib/strspn.c @@ -0,0 +1,66 @@ +/* + * strspn, strcspn + */ + +#include +#include +#include +#include + +#ifndef LONG_BIT +#define LONG_BIT (CHAR_BIT*sizeof(long)) +#endif + +static inline void +set_bit(unsigned long *bitmap, unsigned int bit) +{ + bitmap[bit/LONG_BIT] |= 1UL << (bit%LONG_BIT); +} + +static inline int +test_bit(unsigned long *bitmap, unsigned int bit) +{ + return (int)(bitmap[bit/LONG_BIT] >> (bit%LONG_BIT)) & 1; +} + +static size_t +strxspn(const char *s, const char *map, int parity) +{ + unsigned long matchmap[((1 << CHAR_BIT)+LONG_BIT-1)/LONG_BIT]; + size_t n = 0; + + /* Create bitmap */ + memset(matchmap, 0, sizeof matchmap); + while ( *map ) + set_bit(matchmap, (unsigned char) *map++); + + /* Make sure the null character never matches */ + if ( parity ) + set_bit(matchmap, 0); + + /* Calculate span length */ + while ( test_bit(matchmap, (unsigned char) *s++)^parity ) + n++; + + return n; +} + +size_t +strspn(const char *s, const char *accept) +{ + return strxspn(s, accept, 0); +} + +size_t +strcspn(const char *s, const char *reject) +{ + return strxspn(s, reject, 1); +} + +char * +strpbrk(const char *s, const char *accept) +{ + const char *ss = s+strxspn(s, accept, 1); + + return *ss ? (char *)ss : NULL; +} diff --git a/com32/lib/strstr.c b/com32/lib/strstr.c new file mode 100644 index 0000000..10222df --- /dev/null +++ b/com32/lib/strstr.c @@ -0,0 +1,10 @@ +/* + * strstr.c + */ + +#include + +char *strstr(const char *haystack, const char *needle) +{ + return (char *)memmem(haystack, strlen(haystack), needle, strlen(needle)); +} diff --git a/com32/lib/strtoimax.c b/com32/lib/strtoimax.c new file mode 100644 index 0000000..0cdd088 --- /dev/null +++ b/com32/lib/strtoimax.c @@ -0,0 +1,3 @@ +#define TYPE intmax_t +#define NAME strtoimax +#include "strtox.c" diff --git a/com32/lib/strtok.c b/com32/lib/strtok.c new file mode 100644 index 0000000..e89a9c5 --- /dev/null +++ b/com32/lib/strtok.c @@ -0,0 +1,15 @@ +/* + * strtok.c + */ + +#include + +char *strtok(char *s, const char *delim) +{ + static char *holder; + + if ( s ) + holder = s; + + return strsep(&holder, delim); +} diff --git a/com32/lib/strtol.c b/com32/lib/strtol.c new file mode 100644 index 0000000..9efc8b9 --- /dev/null +++ b/com32/lib/strtol.c @@ -0,0 +1,3 @@ +#define TYPE signed long +#define NAME strtol +#include "strtox.c" diff --git a/com32/lib/strtoll.c b/com32/lib/strtoll.c new file mode 100644 index 0000000..a9428c7 --- /dev/null +++ b/com32/lib/strtoll.c @@ -0,0 +1,3 @@ +#define TYPE signed long long +#define NAME strtoll +#include "strtox.c" diff --git a/com32/lib/strtoul.c b/com32/lib/strtoul.c new file mode 100644 index 0000000..3189aaa --- /dev/null +++ b/com32/lib/strtoul.c @@ -0,0 +1,3 @@ +#define TYPE unsigned long +#define NAME strtoul +#include "strtox.c" diff --git a/com32/lib/strtoull.c b/com32/lib/strtoull.c new file mode 100644 index 0000000..83c14e9 --- /dev/null +++ b/com32/lib/strtoull.c @@ -0,0 +1,3 @@ +#define TYPE unsigned long long +#define NAME strtoull +#include "strtox.c" diff --git a/com32/lib/strtoumax.c b/com32/lib/strtoumax.c new file mode 100644 index 0000000..a379710 --- /dev/null +++ b/com32/lib/strtoumax.c @@ -0,0 +1,3 @@ +#define TYPE uintmax_t +#define NAME strtoumax +#include "strtox.c" diff --git a/com32/lib/strtox.c b/com32/lib/strtox.c new file mode 100644 index 0000000..7c228b6 --- /dev/null +++ b/com32/lib/strtox.c @@ -0,0 +1,13 @@ +/* + * strtox.c + * + * strto...() functions, by macro definition + */ + +#include +#include + +TYPE NAME (const char *nptr, char **endptr, int base) +{ + return (TYPE) strntoumax(nptr, endptr, base, ~(size_t)0); +} diff --git a/com32/lib/sys/ansi.c b/com32/lib/sys/ansi.c new file mode 100644 index 0000000..590b1b4 --- /dev/null +++ b/com32/lib/sys/ansi.c @@ -0,0 +1,435 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * ansi.c + * + * ANSI character code engine + */ + +#include +#include +#include "ansi.h" + +static const struct term_state default_state = +{ + .xy = { 0, 0 }, + .cindex = 0, /* First color table entry */ + .vtgraphics = 0, + .intensity = 1, + .underline = 0, + .blink = 0, + .reverse = 0, + .fg = 7, + .bg = 0, + .autocr = 1, /* Mimic \n -> \r\n conversion by default */ + .saved_xy = { 0, 0 }, + .cursor = 1, + .state = st_init, + .pvt = 0, + .nparms = 0, +}; + +/* DEC VT graphics to codepage 437 table (characters 0x60-0x7F only) */ +static const char decvt_to_cp437[] = + { + 0004, 0261, 0007, 0007, 0007, 0007, 0370, 0361, + 0007, 0007, 0331, 0277, 0332, 0300, 0305, 0304, + 0304, 0304, 0137, 0137, 0303, 0264, 0301, 0302, + 0263, 0363, 0362, 0343, 0330, 0234, 0007, 00 + }; + +void __ansi_init(const struct term_info *ti) +{ + memcpy(ti->ts, &default_state, sizeof default_state); +} + +void __ansi_putchar(const struct term_info *ti, uint8_t ch) +{ + const struct ansi_ops *op = ti->op; + struct term_state *st = ti->ts; + const int rows = ti->rows; + const int cols = ti->cols; + struct curxy xy = st->xy; + + switch ( st->state ) { + case st_init: + switch ( ch ) { + case 1 ... 5: + st->state = st_tbl; + st->parms[0] = ch; + break; + case '\a': + op->beep(); + break; + case '\b': + if ( xy.x > 0 ) xy.x--; + break; + case '\t': + { + int nsp = 8 - (xy.x & 7); + while ( nsp-- ) + __ansi_putchar(ti, ' '); + } + return; /* Cursor already updated */ + case '\n': + case '\v': + case '\f': + xy.y++; + if ( st->autocr ) + xy.x = 0; + break; + case '\r': + xy.x = 0; + break; + case 127: + /* Ignore delete */ + break; + case 14: + st->vtgraphics = 1; + break; + case 15: + st->vtgraphics = 0; + break; + case 27: + st->state = st_esc; + break; + default: + /* Print character */ + if ( ch >= 32 ) { + if ( st->vtgraphics && (ch & 0xe0) == 0x60 ) + ch = decvt_to_cp437[ch - 0x60]; + + op->write_char(xy.x, xy.y, ch, st); + xy.x++; + } + break; + } + break; + + case st_esc: + switch ( ch ) { + case '%': + case '(': + case ')': + case '#': + /* Ignore this plus the subsequent character, allows + compatibility with Linux sequence to set charset */ + break; + case '[': + st->state = st_csi; + st->nparms = st->pvt = 0; + memset(st->parms, 0, sizeof st->parms); + break; + case 'c': + /* Reset terminal */ + memcpy(&st, &default_state, sizeof st); + op->erase(st, 0, 0, cols-1, rows-1); + xy.x = xy.y = 0; + st->state = st_init; + break; + default: + /* Ignore sequence */ + st->state = st_init; + break; + } + break; + + case st_csi: + { + int p0 = st->parms[0] ? st->parms[0] : 1; + + if ( ch >= '0' && ch <= '9' ) { + st->parms[st->nparms] = st->parms[st->nparms]*10 + (ch-'0'); + } else if ( ch == ';' ) { + st->nparms++; + if ( st->nparms >= ANSI_MAX_PARMS ) + st->nparms = ANSI_MAX_PARMS-1; + break; + } else if ( ch == '?' ) { + st->pvt = 1; + } else { + switch ( ch ) { + case 'A': + { + int y = xy.y - p0; + xy.y = (y < 0) ? 0 : y; + } + break; + case 'B': + { + int y = xy.y + p0; + xy.y = (y >= rows) ? rows-1 : y; + } + break; + case 'C': + { + int x = xy.x + p0; + xy.x = (x >= cols) ? cols-1 : x; + } + break; + case 'D': + { + int x = xy.x - p0; + xy.x = (x < 0) ? 0 : x; + } + break; + case 'E': + { + int y = xy.y + p0; + xy.y = (y >= rows) ? rows-1 : y; + xy.x = 0; + } + break; + case 'F': + { + int y = xy.y - p0; + xy.y = (y < 0) ? 0 : y; + xy.x = 0; + } + break; + case 'G': + case '\'': + { + int x = st->parms[0] - 1; + xy.x = (x >= cols) ? cols-1 : (x < 0) ? 0 : x; + } + break; + case 'H': + case 'f': + { + int y = st->parms[0] - 1; + int x = st->parms[1] - 1; + + xy.x = (x >= cols) ? cols-1 : (x < 0) ? 0 : x; + xy.y = (y >= rows) ? rows-1 : (y < 0) ? 0 : y; + } + break; + case 'J': + { + switch ( st->parms[0] ) { + case 0: + op->erase(st, xy.x, xy.y, cols-1, xy.y); + if ( xy.y < rows-1 ) + op->erase(st, 0, xy.y+1, cols-1, rows-1); + break; + + case 1: + if ( xy.y > 0 ) + op->erase(st, 0, 0, cols-1, xy.y-1); + if ( xy.y > 0 ) + op->erase(st, 0, xy.y, xy.x-1, xy.y); + break; + + case 2: + op->erase(st, 0, 0, cols-1, rows-1); + break; + + default: + /* Ignore */ + break; + } + } + break; + case 'K': + { + switch ( st->parms[0] ) { + case 0: + op->erase(st, xy.x, xy.y, cols-1, xy.y); + break; + + case 1: + if ( xy.x > 0 ) + op->erase(st, 0, xy.y, xy.x-1, xy.y); + break; + + case 2: + op->erase(st, 0, xy.y, cols-1, xy.y); + break; + + default: + /* Ignore */ + break; + } + } + break; + case 'h': + case 'l': + { + int set = (ch == 'h'); + switch ( st->parms[0] ) { + case 20: + st->autocr = set; + break; + case 25: + st->cursor = set; + op->showcursor(st); + break; + default: + /* Ignore */ + break; + } + } + break; + case 'm': + { + static const int ansi2pc[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + + int i; + for ( i = 0 ; i <= st->nparms ; i++ ) { + int a = st->parms[i]; + switch ( a ) { + case 0: + st->fg = 7; + st->bg = 0; + st->intensity = 1; + st->underline = 0; + st->blink = 0; + st->reverse = 0; + break; + case 1: + st->intensity = 2; + break; + case 2: + st->intensity = 0; + break; + case 4: + st->underline = 1; + break; + case 5: + st->blink = 1; + break; + case 7: + st->reverse = 1; + break; + case 21: + case 22: + st->intensity = 1; + break; + case 24: + st->underline = 0; + break; + case 25: + st->blink = 0; + break; + case 27: + st->reverse = 0; + break; + case 30 ... 37: + st->fg = ansi2pc[a-30]; + break; + case 38: + st->fg = 7; + st->underline = 1; + break; + case 39: + st->fg = 7; + st->underline = 0; + break; + case 40 ... 47: + st->bg = ansi2pc[a-40]; + break; + case 49: + st->bg = 7; + break; + default: + /* Do nothing */ + break; + } + } + } + break; + case 's': + st->saved_xy = xy; + break; + case 'u': + xy = st->saved_xy; + break; + default: /* Includes CAN and SUB */ + break; /* Drop unknown sequence */ + } + st->state = st_init; + } + } + break; + + case st_tbl: + st->parms[1] = 0; + if ( ch == '#' ) + st->state = st_tblc; + else + st->state = st_init; + break; + + case st_tblc: + { + unsigned int n = (unsigned char)ch - '0'; + const char *p; + + if (n < 10) { + st->parms[1] = st->parms[1]*10+n; + + if (! --st->parms[0]) { + if (st->parms[1] < console_color_table_size) { + /* Set the color table index */ + st->cindex = st->parms[1]; + + /* See if there are any other attributes we care about */ + p = console_color_table[st->parms[1]].ansi; + if (p) { + st->state = st_esc; + __ansi_putchar(ti, '['); + __ansi_putchar(ti, '0'); + __ansi_putchar(ti, ';'); + while (*p) + __ansi_putchar(ti, *p++); + __ansi_putchar(ti, 'm'); + } + } + st->state = st_init; + } + } else { + st->state = st_init; + } + } + break; + } + + /* If we fell off the end of the screen, adjust */ + if ( xy.x >= cols ) { + xy.x = 0; + xy.y++; + } + while ( xy.y >= rows ) { + xy.y--; + op->scroll_up(st); + } + + /* Update cursor position */ + op->set_cursor(xy.x, xy.y, st->cursor); + st->xy = xy; +} diff --git a/com32/lib/sys/ansi.h b/com32/lib/sys/ansi.h new file mode 100644 index 0000000..da06903 --- /dev/null +++ b/com32/lib/sys/ansi.h @@ -0,0 +1,64 @@ +/* + * ansi.h + */ + +#ifndef COM32_LIB_SYS_ANSI_H +#define COM32_LIB_SYS_ANSI_H + +#include + +#define ANSI_MAX_PARMS 16 + +enum ansi_state { + st_init, + st_esc, + st_csi, + st_tbl, + st_tblc, +}; + +struct curxy { + uint8_t x, y; +} __attribute__((packed)); + +struct term_state { + struct curxy xy; + int cindex; /* SOH color index */ + int vtgraphics; /* VT graphics on/off */ + int intensity; + int underline; + int blink; + int reverse; + int fg; + int bg; + int autocr; + struct curxy saved_xy; + int cursor; + enum ansi_state state; + int pvt; /* Private code? */ + int nparms; /* Number of parameters seen */ + int parms[ANSI_MAX_PARMS]; +}; + + +struct ansi_ops { + void (*erase)(const struct term_state *st, int x0, int y0, int x1, int y1); + void (*write_char)(int x, int y, uint8_t ch, const struct term_state *st); + void (*showcursor)(const struct term_state *st); + void (*scroll_up)(const struct term_state *st); + void (*set_cursor)(int x, int y, int visible); + void (*beep)(void); +}; + +struct term_info { + int rows, cols; /* Screen size */ + int disabled; + struct term_state *ts; + const struct ansi_ops *op; +}; + +void __ansi_init(const struct term_info *ti); +void __ansi_putchar(const struct term_info *ti, uint8_t ch); +void __ansicon_beep(void); + +#endif /* COM32_LIB_SYS_ANSI_H */ diff --git a/com32/lib/sys/ansicon_write.c b/com32/lib/sys/ansicon_write.c new file mode 100644 index 0000000..3322ab2 --- /dev/null +++ b/com32/lib/sys/ansicon_write.c @@ -0,0 +1,258 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * ansicon_write.c + * + * Write to the screen using ANSI control codes (about as capable as + * DOS' ANSI.SYS.) + */ + +#include +#include +#include +#include +#include +#include +#include +#include "file.h" +#include "ansi.h" + +static void ansicon_erase(const struct term_state *, int, int, int, int); +static void ansicon_write_char(int, int, uint8_t, const struct term_state *); +static void ansicon_showcursor(const struct term_state *); +static void ansicon_scroll_up(const struct term_state *); +static void ansicon_set_cursor(int, int, int); + +static struct term_state ts; +struct ansi_ops __ansicon_ops = { + .erase = ansicon_erase, + .write_char = ansicon_write_char, + .showcursor = ansicon_showcursor, + .set_cursor = ansicon_set_cursor, + .scroll_up = ansicon_scroll_up, + .beep = __ansicon_beep, +}; + +static struct term_info ti = + { + .disabled = 0, + .ts = &ts, + .op = &__ansicon_ops + }; + +#define BIOS_CURXY ((struct curxy *)0x450) /* Array for each page */ +#define BIOS_ROWS (*(uint8_t *)0x484) /* Minus one; if zero use 24 (= 25 lines) */ +#define BIOS_COLS (*(uint16_t *)0x44A) +#define BIOS_PAGE (*(uint8_t *)0x462) + +/* Reference counter to the screen, to keep track of if we need + reinitialization. */ +static int ansicon_counter = 0; + +static uint16_t cursor_type; /* Saved cursor pattern */ + +/* Common setup */ +int __ansicon_open(struct file_info *fp) +{ + static com32sys_t ireg; /* Auto-initalized to all zero */ + com32sys_t oreg; + + if (!ansicon_counter) { + /* Are we disabled? */ + if (syslinux_serial_console_info()->flowctl & 0x8000) { + ti.disabled = 1; + ti.rows = 25; + ti.cols = 80; + } else { + /* Force text mode */ + ireg.eax.w[0] = 0x0005; + __intcall(0x22, &ireg, NULL); + + /* Initial state */ + ti.rows = BIOS_ROWS ? BIOS_ROWS+1 : 25; + ti.cols = BIOS_COLS; + __ansi_init(&ti); + + /* Get cursor shape and position */ + ireg.eax.b[1] = 0x03; + ireg.ebx.b[1] = BIOS_PAGE; + __intcall(0x10, &ireg, &oreg); + cursor_type = oreg.ecx.w[0]; + ti.ts->xy.x = oreg.edx.b[0]; + ti.ts->xy.y = oreg.edx.b[1]; + } + } + + fp->o.rows = ti.rows; + fp->o.cols = ti.cols; + + ansicon_counter++; + return 0; +} + +int __ansicon_close(struct file_info *fp) +{ + (void)fp; + + ansicon_counter--; + return 0; +} + +/* Turn ANSI attributes into VGA attributes */ +static uint8_t ansicon_attribute(const struct term_state *st) +{ + int bg = st->bg; + int fg; + + if ( st->underline ) + fg = 0x01; + else if ( st->intensity == 0 ) + fg = 0x08; + else + fg = st->fg; + + if ( st->reverse ) { + bg = fg & 0x07; + fg &= 0x08; + fg |= st->bg; + } + + if ( st->blink ) + bg ^= 0x08; + + if ( st->intensity == 2 ) + fg ^= 0x08; + + return (bg << 4) | fg; +} + +/* Erase a region of the screen */ +static void ansicon_erase(const struct term_state *st, + int x0, int y0, int x1, int y1) +{ + static com32sys_t ireg; + + ireg.eax.w[0] = 0x0600; /* Clear window */ + ireg.ebx.b[1] = ansicon_attribute(st); + ireg.ecx.b[0] = x0; + ireg.ecx.b[1] = y0; + ireg.edx.b[0] = x1; + ireg.edx.b[1] = y1; + __intcall(0x10, &ireg, NULL); +} + +/* Show or hide the cursor */ +static void ansicon_showcursor(const struct term_state *st) +{ + static com32sys_t ireg; + + ireg.eax.b[1] = 0x01; + ireg.ecx.w[0] = st->cursor ? cursor_type : 0x2020; + __intcall(0x10, &ireg, NULL); +} + +static void ansicon_set_cursor(int x, int y, int visible) +{ + const int page = BIOS_PAGE; + struct curxy xy = BIOS_CURXY[page]; + static com32sys_t ireg; + + (void)visible; + + if (xy.x != x || xy.y != y) { + ireg.eax.b[1] = 0x02; + ireg.ebx.b[1] = page; + ireg.edx.b[1] = y; + ireg.edx.b[0] = x; + __intcall(0x10, &ireg, NULL); + } +} + +static void ansicon_write_char(int x, int y, uint8_t ch, + const struct term_state *st) +{ + static com32sys_t ireg; + + ansicon_set_cursor(x, y, 0); + + ireg.eax.b[1] = 0x09; + ireg.eax.b[0] = ch; + ireg.ebx.b[1] = BIOS_PAGE; + ireg.ebx.b[0] = ansicon_attribute(st); + ireg.ecx.w[0] = 1; + __intcall(0x10, &ireg, NULL); +} + +static void ansicon_scroll_up(const struct term_state *st) +{ + static com32sys_t ireg; + + ireg.eax.w[0] = 0x0601; + ireg.ebx.b[1] = ansicon_attribute(st); + ireg.ecx.w[0] = 0; + ireg.edx.b[1] = ti.rows-1; + ireg.edx.b[0] = ti.cols-1; + __intcall(0x10, &ireg, NULL); /* Scroll */ +} + +ssize_t __ansicon_write(struct file_info *fp, const void *buf, size_t count) +{ + const unsigned char *bufp = buf; + size_t n = 0; + + (void)fp; + + if ( ti.disabled ) + return count; /* Nothing to do */ + + while ( count-- ) { + __ansi_putchar(&ti, *bufp++); + n++; + } + + return n; +} + +void __ansicon_beep(void) +{ + static com32sys_t ireg; + + ireg.eax.w[0] = 0x0e07; + ireg.ebx.b[1] = BIOS_PAGE; + __intcall(0x10, &ireg, NULL); +} + +const struct output_dev dev_ansicon_w = { + .dev_magic = __DEV_MAGIC, + .flags = __DEV_TTY | __DEV_OUTPUT, + .fileflags = O_WRONLY | O_CREAT | O_TRUNC | O_APPEND, + .write = __ansicon_write, + .close = __ansicon_close, + .open = __ansicon_open, +}; diff --git a/com32/lib/sys/ansiserial_write.c b/com32/lib/sys/ansiserial_write.c new file mode 100644 index 0000000..097d0c6 --- /dev/null +++ b/com32/lib/sys/ansiserial_write.c @@ -0,0 +1,58 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * ansiserial_write.c + * + * Write to both to the ANSI console and the serial port + */ + +#include +#include +#include +#include +#include "file.h" + +extern int __ansicon_open(struct file_info *); +extern int __ansicon_close(struct file_info *); +extern ssize_t __ansicon_write(struct file_info *, const void *, size_t); +extern ssize_t __xserial_write(struct file_info *, const void *, size_t); + +static ssize_t __ansiserial_write(struct file_info *fp, const void *buf, size_t count) +{ + __ansicon_write(fp, buf, count); + return __xserial_write(fp, buf, count); +} + +const struct output_dev dev_ansiserial_w = { + .dev_magic = __DEV_MAGIC, + .flags = __DEV_TTY | __DEV_OUTPUT, + .fileflags = O_WRONLY | O_CREAT | O_TRUNC | O_APPEND, + .write = __ansiserial_write, + .close = __ansicon_close, + .open = __ansicon_open, +}; diff --git a/com32/lib/sys/argv.c b/com32/lib/sys/argv.c new file mode 100644 index 0000000..b405a8d --- /dev/null +++ b/com32/lib/sys/argv.c @@ -0,0 +1,96 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * argv.c + * + * Parse a single C string into argc and argv (argc is return value.) + * memptr points to available memory. + */ + +#include +#include +#include +#include + +extern char _end[]; /* Symbol created by linker */ +void *__mem_end = &_end; /* Global variable for use by malloc() */ + +int __parse_argv(char ***argv, const char *str) +{ + char argv0[] = ""; + char *mem = __mem_end; + const char *p = str; + char *q = mem; + char *r; + char **arg; + int wasspace = 1; + int argc = 1; + + /* First copy the string, turning whitespace runs into nulls */ + for ( p = str ; ; p++ ) { + if ( *p <= ' ' ) { + if ( !wasspace ) { + wasspace = 1; + *q++ = '\0'; + } + } else { + if ( wasspace ) { + argc++; + wasspace = 0; + } + *q++ = *p; + } + + /* This test is AFTER we have processed the null byte; + we treat it as a whitespace character so it terminates + the last argument */ + if ( ! *p ) + break; + } + + /* Now create argv */ + arg = ALIGN_UP_FOR(q, char *); + *argv = arg; + *arg++ = argv0; /* argv[0] */ + + q--; /* Point q to final null */ + if ( mem < q ) + *arg++ = mem; /* argv[1] */ + + for ( r = mem ; r < q ; r++ ) { + if ( *r == '\0' ) { + *arg++ = r+1; + } + } + + *arg++ = NULL; /* Null pointer at the end */ + __mem_end = arg; /* End of memory we used */ + + return argc; +} diff --git a/com32/lib/sys/cfarcall.c b/com32/lib/sys/cfarcall.c new file mode 100644 index 0000000..7c681fc --- /dev/null +++ b/com32/lib/sys/cfarcall.c @@ -0,0 +1,11 @@ +/* + * cfarcall.c + */ + +#include + +int __cfarcall(uint16_t cs, uint16_t ip, + const void *stack, uint32_t stack_size) +{ + return __com32.cs_cfarcall((cs << 16)+ip, stack, stack_size); +} diff --git a/com32/lib/sys/close.c b/com32/lib/sys/close.c new file mode 100644 index 0000000..e8421b9 --- /dev/null +++ b/com32/lib/sys/close.c @@ -0,0 +1,62 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * close.c + */ + +#include +#include +#include +#include "file.h" + +int close(int fd) +{ + struct file_info *fp = &__file_info[fd]; + int rv = 0; + + if ( fd >= NFILES || !fp->iop || !fp->oop ) { + errno = EBADF; + return -1; + } + + if ( fp->iop->close ) { + rv = fp->iop->close(fp); + if ( rv ) + return rv; + } + fp->iop = &dev_error_r; + + if ( fp->oop->close ) { + rv = fp->oop->close(fp); + if ( rv ) + return rv; + } + + memset(fp, 0, sizeof *fp); /* File structure unused */ + return 0; +} diff --git a/com32/lib/sys/colortable.c b/com32/lib/sys/colortable.c new file mode 100644 index 0000000..5f7e802 --- /dev/null +++ b/com32/lib/sys/colortable.c @@ -0,0 +1,9 @@ +#include + +static struct color_table default_color_table[] = { + {"default", "0", 0xffffffff, 0x00000000, SHADOW_NORMAL } +}; + +struct color_table *console_color_table = &default_color_table; +int console_color_table_size = + (sizeof default_color_table/sizeof(struct color_table)); diff --git a/com32/lib/sys/entry.S b/com32/lib/sys/entry.S new file mode 100644 index 0000000..53bf2ec --- /dev/null +++ b/com32/lib/sys/entry.S @@ -0,0 +1,94 @@ +/* ----------------------------------------------------------------------- + * + * Copyright 2003-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * COM32 start up code - must be linked first in the binary + */ + +/* Number of arguments in our version of the entry structure */ +#define COM32_ARGS 7 + + .section ".init","ax" + .globl _start + .type _start, @function +_start: + /* This first instruction acts as COM32 magic number */ + movl $0x21cd4cff,%eax + + /* Upwards string operations */ + cld + + /* Zero the .bss segment */ + xorl %eax,%eax + movl $__bss_start,%edi # Symbol provided by linker + movl $_end+3,%ecx # Symbol provided by linker + subl %edi,%ecx + shrl $2,%ecx + rep ; stosl + + /* Copy COM32 invocation parameters */ + leal 4(%esp),%esi # Argument list + movl $__com32,%edi + movl $(COM32_ARGS),%ecx + movl %esp,-4(%edi) # Save the initial stack ptr + cmpl (%esi),%ecx + jbe 1f + movl (%esi),%ecx +1: inc %ecx # Copy the argument count, too + rep ; movsl + + /* Parse the command line (assumes REGPARM) */ + movl __com32+4,%edx # Command line + pushl %edx # Make space for argv + movl %esp,%eax + call __parse_argv + pushl %eax # Save argc + + /* Look for library initialization functions */ + movl $__ctors_start, %esi +2: + cmpl $__ctors_end, %esi + jae 3f + call *(%esi) + addl $4,%esi + jmp 2b +/* + * Actually run main. This assumes REGPARM is used!!!! + */ +3: + popl %eax # argc + popl %edx # argv + call main + call *(__exit_handler) + hlt + .size _start, .-_start + + .bss + .globl __entry_esp +__entry_esp: .space 4 + .globl __com32 +__com32: .space 4*(COM32_ARGS+1) diff --git a/com32/lib/sys/err_read.c b/com32/lib/sys/err_read.c new file mode 100644 index 0000000..8b010ec --- /dev/null +++ b/com32/lib/sys/err_read.c @@ -0,0 +1,53 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * err_read.c + * + * Reading from a device which doesn't support reading + */ + +#include +#include +#include +#include +#include "file.h" + +static ssize_t __err_read(struct file_info *fp, void *buf, size_t count) +{ + (void)fp; (void)buf; (void)count; + errno = -EINVAL; + return -1; +} + +const struct input_dev dev_error_r = { + .dev_magic = __DEV_MAGIC, + .flags = __DEV_INPUT | __DEV_ERROR, + .fileflags = O_RDONLY, + .read = __err_read, + .close = NULL, +}; diff --git a/com32/lib/sys/err_write.c b/com32/lib/sys/err_write.c new file mode 100644 index 0000000..d7e3f62 --- /dev/null +++ b/com32/lib/sys/err_write.c @@ -0,0 +1,53 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * err_write.c + * + * Writing to a device which doesn't support writing + */ + +#include +#include +#include +#include +#include "file.h" + +static ssize_t __err_write(struct file_info *fp, const void *buf, size_t count) +{ + (void)fp; (void)buf; (void)count; + errno = -EINVAL; + return -1; +} + +const struct output_dev dev_error_w = { + .dev_magic = __DEV_MAGIC, + .flags = __DEV_OUTPUT | __DEV_ERROR, + .fileflags = O_WRONLY | O_CREAT | O_TRUNC | O_APPEND, + .write = __err_write, + .close = NULL, +}; diff --git a/com32/lib/sys/exit.S b/com32/lib/sys/exit.S new file mode 100644 index 0000000..2ab8012 --- /dev/null +++ b/com32/lib/sys/exit.S @@ -0,0 +1,41 @@ +/* + * Implementation of _exit() for com32 based on c32entry.S + */ + .text + + .globl _Exit + .type _Exit, @function +_Exit: + /* Just fall through to _exit */ + .size _Exit, .-_Exit + + .globl _exit + .type _exit, @function +_exit: +#ifdef REGPARM + pushl %eax +#endif + + /* Run any destructors */ + movl $__dtors_start, %esi +2: + cmpl $__dtors_end, %esi + jae 1f + call *(%esi) + addl $4,%esi + jmp 2b + +1: +#ifdef REGPARM + popl %eax +#else + movl 4(%esp),%eax # Exit code in %eax = return value +#endif + movl (__entry_esp),%esp # Return stack pointer to entry value + ret # Return to termination address + .size _exit, .-_exit + + .data +__exit_handler: + .globl __exit_handler + .long _exit diff --git a/com32/lib/sys/farcall.c b/com32/lib/sys/farcall.c new file mode 100644 index 0000000..75288d4 --- /dev/null +++ b/com32/lib/sys/farcall.c @@ -0,0 +1,11 @@ +/* + * farcall.c + */ + +#include + +void __farcall(uint16_t cs, uint16_t ip, + const com32sys_t *ireg, com32sys_t *oreg) +{ + __com32.cs_farcall((cs << 16)+ip, ireg, oreg); +} diff --git a/com32/lib/sys/file.h b/com32/lib/sys/file.h new file mode 100644 index 0000000..11dacd8 --- /dev/null +++ b/com32/lib/sys/file.h @@ -0,0 +1,108 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2003-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * file.h + * + * Internal implementation of file I/O for COM32 + */ + +#ifndef _COM32_SYS_FILE_H +#define _COM32_SYS_FILE_H + +#include +#include +#include +#include +#include + +/* Device structure; contains the relevant operations */ + +struct file_info; + +#define __DEV_MAGIC 0xf4e7 +#define __DEV_TTY 0x0001 /* TTY - must be bit 0 */ +#define __DEV_FILE 0x0002 /* Ordinary file */ +#define __DEV_OUTPUT 0x0004 /* This is an output device */ +#define __DEV_INPUT 0 /* Dummy */ +#define __DEV_ERROR 0x0008 /* This is the error device */ +#define __DEV_NULL 0x0010 /* This is the null device */ + +struct input_dev { + uint16_t dev_magic; /* Magic number */ + uint16_t flags; /* Flags */ + int fileflags; /* Permitted file flags */ + ssize_t (*read)(struct file_info *, void *, size_t); + int (*close)(struct file_info *); + int (*open)(struct file_info *); +}; + +struct output_dev { + uint16_t dev_magic; /* Magic number */ + uint16_t flags; /* Flags */ + int fileflags; + ssize_t (*write)(struct file_info *, const void *, size_t); + int (*close)(struct file_info *); + int (*open)(struct file_info *); + const struct output_dev *fallback; /* Fallback option for certain consoles */ +}; + +/* File structure */ + +#define NFILES 32 /* Number of files to support */ +#define MAXBLOCK 16384 /* Defined by ABI */ + +struct file_info { + const struct input_dev *iop; /* Input operations */ + const struct output_dev *oop; /* Output operations */ + + /* Output file data */ + struct { + int rows, cols; /* Rows and columns */ + } o; + + /* Structure used for input blocking */ + struct { + int blocklg2; /* Blocksize log 2 */ + size_t offset; /* Current file offset */ + size_t length; /* Total file length */ + uint16_t filedes; /* File descriptor */ + uint16_t _filler; /* Unused */ + size_t nbytes; /* Number of bytes available in buffer */ + char *datap; /* Current data pointer */ + void *pvt; /* Private pointer for driver */ + char buf[MAXBLOCK]; + } i; +}; + +extern struct file_info __file_info[NFILES]; + +/* Line input discipline */ +ssize_t __line_input(struct file_info *fp, char *buf, size_t bufsize, + ssize_t (*get_char)(struct file_info *, void *, size_t)); + +#endif /* _COM32_SYS_FILE_H */ diff --git a/com32/lib/sys/fileclose.c b/com32/lib/sys/fileclose.c new file mode 100644 index 0000000..bf93320 --- /dev/null +++ b/com32/lib/sys/fileclose.c @@ -0,0 +1,52 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * fileclose.c + * + * Close an ordinary file + */ + +#include +#include +#include +#include "file.h" + +int __file_close(struct file_info *fp) +{ + com32sys_t regs; + + if ( fp->i.filedes ) { + memset(®s, 0, sizeof regs); + regs.eax.w[0] = 0x0008; /* Close file */ + regs.esi.w[0] = fp->i.filedes; + + __com32.cs_intcall(0x22, ®s, NULL); + } + + return 0; +} diff --git a/com32/lib/sys/fileinfo.c b/com32/lib/sys/fileinfo.c new file mode 100644 index 0000000..a1fc7c9 --- /dev/null +++ b/com32/lib/sys/fileinfo.c @@ -0,0 +1,3 @@ +#include "file.h" + +struct file_info __file_info[NFILES]; diff --git a/com32/lib/sys/fileread.c b/com32/lib/sys/fileread.c new file mode 100644 index 0000000..79d912d --- /dev/null +++ b/com32/lib/sys/fileread.c @@ -0,0 +1,93 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * read.c + * + * Reading from a file + */ + +#include +#include +#include +#include +#include "file.h" + +int __file_get_block(struct file_info *fp) +{ + com32sys_t ireg, oreg; + + memset(&ireg, 0, sizeof ireg); + ireg.eax.w[0] = 0x0007; /* Read file */ + ireg.ebx.w[0] = OFFS(__com32.cs_bounce); + ireg.es = SEG(__com32.cs_bounce); + ireg.esi.w[0] = fp->i.filedes; + ireg.ecx.w[0] = MAXBLOCK >> fp->i.blocklg2; + + __intcall(0x22, &ireg, &oreg); + + if ( oreg.eflags.l & EFLAGS_CF ) { + errno = EIO; + return -1; + } + + fp->i.filedes = oreg.esi.w[0]; + fp->i.nbytes = oreg.ecx.l; + fp->i.datap = fp->i.buf; + memcpy(fp->i.buf, __com32.cs_bounce, fp->i.nbytes); + + return 0; +} + +ssize_t __file_read(struct file_info *fp, void *buf, size_t count) +{ + char *bufp = buf; + ssize_t n = 0; + size_t ncopy; + + while ( count ) { + if ( fp->i.nbytes == 0 ) { + if ( fp->i.offset >= fp->i.length || !fp->i.filedes ) + return n; /* As good as it gets... */ + + if ( __file_get_block(fp) ) + return n ? n : -1; + } + + ncopy = min(count, fp->i.nbytes); + memcpy(bufp, fp->i.datap, ncopy); + + n += ncopy; + bufp += ncopy; + count -= ncopy; + fp->i.datap += ncopy; + fp->i.offset += ncopy; + fp->i.nbytes -= ncopy; + } + + return n; +} diff --git a/com32/lib/sys/fstat.c b/com32/lib/sys/fstat.c new file mode 100644 index 0000000..ef5e20d --- /dev/null +++ b/com32/lib/sys/fstat.c @@ -0,0 +1,63 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2005-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * fstat.c + * + * Very trivial fstat emulation + */ + +#include +#include +#include "file.h" + +int fstat(int fd, struct stat *buf) +{ + struct file_info *fp = &__file_info[fd]; + + if ( fd >= NFILES || !fp->iop ) { + errno = EBADF; + return -1; + } + + if ( fp->iop->flags & __DEV_FILE ) { + if ( fp->i.length == (uint32_t)-1 ) { + /* File of unknown length, report it as a socket + (it probably really is, anyway!) */ + buf->st_mode = S_IFSOCK | 0444; + buf->st_size = 0; + } else { + buf->st_mode = S_IFREG | 0444; + buf->st_size = fp->i.length; + } + } else { + buf->st_mode = S_IFCHR | 0666; + buf->st_size = 0; + } + + return 0; +} diff --git a/com32/lib/sys/ftell.c b/com32/lib/sys/ftell.c new file mode 100644 index 0000000..3f10916 --- /dev/null +++ b/com32/lib/sys/ftell.c @@ -0,0 +1,16 @@ +/* + * sys/ftell.c + * + * We can't seek, but we can at least tell... + */ + +#include +#include "sys/file.h" + +long ftell(FILE *stream) +{ + int fd = fileno(stream); + struct file_info *fp = &__file_info[fd]; + + return fp->i.offset; +} diff --git a/com32/lib/sys/intcall.c b/com32/lib/sys/intcall.c new file mode 100644 index 0000000..e32fb8a --- /dev/null +++ b/com32/lib/sys/intcall.c @@ -0,0 +1,10 @@ +/* + * intcall.c + */ + +#include + +void __intcall(uint8_t vector, const com32sys_t *ireg, com32sys_t *oreg) +{ + __com32.cs_intcall(vector, ireg, oreg); +} diff --git a/com32/lib/sys/isatty.c b/com32/lib/sys/isatty.c new file mode 100644 index 0000000..22d73d3 --- /dev/null +++ b/com32/lib/sys/isatty.c @@ -0,0 +1,53 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * isatty.c + * + * Return if this is a tty or not + */ + +#include +#include +#include +#include +#include +#include +#include "file.h" + +int isatty(int fd) +{ + struct file_info *fp = &__file_info[fd]; + + if ( fd >= NFILES || !fp->iop ) { + errno = EBADF; + return -1; + } + + /* __DEV_TTY == 1 */ + return (fp->iop->flags & __DEV_TTY); +} diff --git a/com32/lib/sys/line_input.c b/com32/lib/sys/line_input.c new file mode 100644 index 0000000..53a7ae1 --- /dev/null +++ b/com32/lib/sys/line_input.c @@ -0,0 +1,88 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * line_input.c + * + * Line-oriented input discupline + */ + +#include "file.h" +#include +#include + +ssize_t __line_input(struct file_info *fp, char *buf, size_t bufsize, + ssize_t (*get_char)(struct file_info *, void *, size_t)) +{ + size_t n = 0; + char ch; + int rv; + ssize_t (* const Write)(struct file_info *, const void *, size_t) = + fp->oop->write; + + for(;;) { + rv = get_char(fp, &ch, 1); + + if ( rv != 1 ) { + syslinux_idle(); + continue; + } + + switch ( ch ) { + case '\n': /* Ignore incoming linefeed */ + break; + + case '\r': + *buf = '\n'; + Write(fp, "\n", 1); + return n+1; + + case '\b': + if ( n > 0 ) { + n--; buf--; + Write(fp, "\b \b", 3); + } + break; + + case '\x15': /* Ctrl-U */ + while ( n ) { + n--; buf--; + Write(fp, "\b \b", 3); + } + break; + + default: + if ( n < bufsize-1 ) { + *buf = ch; + Write(fp, buf, 1); + n++; + buf++; + } + break; + } + } +} diff --git a/com32/lib/sys/null_read.c b/com32/lib/sys/null_read.c new file mode 100644 index 0000000..9e42f21 --- /dev/null +++ b/com32/lib/sys/null_read.c @@ -0,0 +1,52 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * null_read.c + * + * Reading null device + */ + +#include +#include +#include +#include +#include "file.h" + +static ssize_t __null_read(struct file_info *fp, void *buf, size_t count) +{ + (void)fp; (void)buf; (void)count; + return 0; +} + +const struct input_dev dev_null_r = { + .dev_magic = __DEV_MAGIC, + .flags = __DEV_INPUT | __DEV_NULL, + .fileflags = O_RDONLY, + .read = __null_read, + .close = NULL, +}; diff --git a/com32/lib/sys/null_write.c b/com32/lib/sys/null_write.c new file mode 100644 index 0000000..cebd9ea --- /dev/null +++ b/com32/lib/sys/null_write.c @@ -0,0 +1,52 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * null_write.c + * + * Null writing device + */ + +#include +#include +#include +#include +#include "file.h" + +static ssize_t __null_write(struct file_info *fp, const void *buf, size_t count) +{ + (void)fp; (void)buf; (void)count; + return count; +} + +const struct output_dev dev_null_w = { + .dev_magic = __DEV_MAGIC, + .flags = __DEV_OUTPUT | __DEV_NULL, + .fileflags = O_WRONLY | O_CREAT | O_TRUNC | O_APPEND, + .write = __null_write, + .close = NULL, +}; diff --git a/com32/lib/sys/open.c b/com32/lib/sys/open.c new file mode 100644 index 0000000..756799e --- /dev/null +++ b/com32/lib/sys/open.c @@ -0,0 +1,90 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2003-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include +#include +#include +#include "file.h" + +/* + * open.c + * + * Open an ordinary file + */ + +extern ssize_t __file_read(struct file_info *, void *, size_t); +extern int __file_close(struct file_info *); + +static const struct input_dev file_dev = { + .dev_magic = __DEV_MAGIC, + .flags = __DEV_FILE | __DEV_INPUT, + .fileflags = O_RDONLY, + .read = __file_read, + .close = __file_close, + .open = NULL, +}; + +int open(const char *pathname, int flags, ...) +{ + com32sys_t regs; + int fd; + struct file_info *fp; + + fd = opendev(&file_dev, NULL, flags); + + if ( fd < 0 ) + return -1; + + fp = &__file_info[fd]; + + strlcpy(__com32.cs_bounce, pathname, __com32.cs_bounce_size); + + regs.eax.w[0] = 0x0006; + regs.esi.w[0] = OFFS(__com32.cs_bounce); + regs.es = SEG(__com32.cs_bounce); + + __com32.cs_intcall(0x22, ®s, ®s); + + if ( (regs.eflags.l & EFLAGS_CF) || regs.esi.w[0] == 0 ) { + errno = ENOENT; + return -1; + } + + { + uint16_t blklg2; + asm("bsrw %1,%0" : "=r" (blklg2) : "rm" (regs.ecx.w[0])); + fp->i.blocklg2 = blklg2; + } + fp->i.length = regs.eax.l; + fp->i.filedes = regs.esi.w[0]; + fp->i.offset = 0; + fp->i.nbytes = 0; + + return fd; +} diff --git a/com32/lib/sys/openconsole.c b/com32/lib/sys/openconsole.c new file mode 100644 index 0000000..c0416e2 --- /dev/null +++ b/com32/lib/sys/openconsole.c @@ -0,0 +1,51 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * openconsole.c + * + * Open the chosen console device + */ + +#include +#include +#include + +int openconsole(const struct input_dev *idev, const struct output_dev *odev) +{ + close(0); + if ( opendev(idev, odev, O_RDONLY) != 0 ) + return -1; + close(1); + if ( opendev(idev, odev, O_WRONLY) != 1 ) + return -1; + close(2); + if ( opendev(idev, odev, O_WRONLY) != 2 ) + return -1; + + return 0; +} diff --git a/com32/lib/sys/opendev.c b/com32/lib/sys/opendev.c new file mode 100644 index 0000000..831c5a4 --- /dev/null +++ b/com32/lib/sys/opendev.c @@ -0,0 +1,99 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2003-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include +#include +#include "file.h" + +/* + * opendev.c + * + * Open a special device + */ + +int opendev(const struct input_dev *idev, + const struct output_dev *odev, int flags) +{ + int fd; + struct file_info *fp; + int okflags; + int e; + + okflags = (idev ? idev->fileflags : 0) | (odev ? odev->fileflags : 0); + + if ( !(flags & 3) || (flags & ~okflags) ) { + errno = EINVAL; + return -1; + } + + for ( fd = 0, fp = __file_info ; fd < NFILES ; fd++, fp++ ) + if ( !fp->iop && !fp->oop ) + break; + + if ( fd >= NFILES ) { + errno = EMFILE; + return -1; + } + + /* The file structure is already zeroed */ + fp->iop = &dev_error_r; + fp->oop = &dev_error_w; + fp->i.datap = fp->i.buf; + + if (idev) { + if (idev->open && (e = idev->open(fp))) { + errno = e; + goto puke; + } + fp->iop = idev; + } + + while (odev) { + if (odev->open && (e = odev->open(fp))) { + if (e == EAGAIN) { + if (odev->fallback) { + odev = odev->fallback; + continue; + } else { + e = EIO; + } + } + errno = e; + goto puke; + } + fp->oop = odev; + break; + } + + return fd; + + puke: + close(fd); + return -1; +} diff --git a/com32/lib/sys/rawcon_read.c b/com32/lib/sys/rawcon_read.c new file mode 100644 index 0000000..76973a0 --- /dev/null +++ b/com32/lib/sys/rawcon_read.c @@ -0,0 +1,80 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * rawcon_read.c + * + * Character-oriented reading from the console without echo; + * this is a NONBLOCKING device. + */ + +#include +#include +#include +#include +#include +#include "file.h" + +/* Global, since it's used by stdcon_read */ +ssize_t __rawcon_read(struct file_info *fp, void *buf, size_t count) +{ + com32sys_t ireg, oreg; + char *bufp = buf; + size_t n = 0; + clock_t start; + + (void)fp; + + memset(&ireg, 0, sizeof ireg); + + start = times(NULL); + + while ( n < count ) { + /* Poll */ + ireg.eax.b[1] = 0x0B; + __intcall(0x21, &ireg, &oreg); + if ( !oreg.eax.b[0] ) + break; + + /* We have data, go get it */ + ireg.eax.b[1] = 0x08; + __intcall(0x21, &ireg, &oreg); + *bufp++ = oreg.eax.b[0]; + n++; + } + + return n; +} + +const struct input_dev dev_rawcon_r = { + .dev_magic = __DEV_MAGIC, + .flags = __DEV_TTY | __DEV_INPUT, + .fileflags = O_RDONLY, + .read = __rawcon_read, + .close = NULL, + .open = NULL, +}; diff --git a/com32/lib/sys/rawcon_write.c b/com32/lib/sys/rawcon_write.c new file mode 100644 index 0000000..2ea5e2f --- /dev/null +++ b/com32/lib/sys/rawcon_write.c @@ -0,0 +1,66 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * rawcon_write.c + * + * Raw writing to the console; no \n -> \r\n translation + */ + +#include +#include +#include +#include +#include "file.h" + +static ssize_t __rawcon_write(struct file_info *fp, const void *buf, size_t count) +{ + com32sys_t ireg; + const char *bufp = buf; + size_t n = 0; + + (void)fp; + + memset(&ireg, 0, sizeof ireg); + ireg.eax.b[1] = 0x02; + + while ( count-- ) { + ireg.edx.b[0] = *bufp++; + __intcall(0x21, &ireg, NULL); + n++; + } + + return n; +} + +const struct output_dev dev_rawcon_w = { + .dev_magic = __DEV_MAGIC, + .flags = __DEV_TTY | __DEV_OUTPUT, + .fileflags = O_WRONLY | O_CREAT | O_TRUNC | O_APPEND, + .write = __rawcon_write, + .close = NULL, +}; diff --git a/com32/lib/sys/read.c b/com32/lib/sys/read.c new file mode 100644 index 0000000..316ee00 --- /dev/null +++ b/com32/lib/sys/read.c @@ -0,0 +1,51 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * read.c + * + * Reading from a file descriptor + */ + +#include +#include +#include +#include +#include +#include "file.h" + +ssize_t read(int fd, void *buf, size_t count) +{ + struct file_info *fp = &__file_info[fd]; + + if ( fd >= NFILES || !fp->iop ) { + errno = EBADF; + return -1; + } + + return fp->iop->read(fp, buf, count); +} diff --git a/com32/lib/sys/screensize.c b/com32/lib/sys/screensize.c new file mode 100644 index 0000000..5a0d5ae --- /dev/null +++ b/com32/lib/sys/screensize.c @@ -0,0 +1,23 @@ +#include +#include +#include "file.h" + +int getscreensize(int fd, int *rows, int *cols) +{ + struct file_info *fp = &__file_info[fd]; + + if ( fd >= NFILES || !fp->iop ) { + errno = EBADF; + return -1; + } + + *rows = fp->o.rows; + *cols = fp->o.cols; + + if (!rows || !cols) { + errno = ENOTTY; + return -1; + } + + return 0; +} diff --git a/com32/lib/sys/serial_write.c b/com32/lib/sys/serial_write.c new file mode 100644 index 0000000..a637aae --- /dev/null +++ b/com32/lib/sys/serial_write.c @@ -0,0 +1,71 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * serial_write.c + * + * Raw writing to the serial port; no \n -> \r\n translation + */ + +#include +#include +#include +#include +#include +#include "file.h" + +ssize_t __serial_write(struct file_info *fp, const void *buf, size_t count) +{ + com32sys_t ireg; + const char *bufp = buf; + size_t n = 0; + + (void)fp; + + if (!syslinux_serial_console_info()->iobase) + return count; /* Nothing to do */ + + memset(&ireg, 0, sizeof ireg); + ireg.eax.b[1] = 0x04; + + while ( count-- ) { + ireg.edx.b[0] = *bufp++; + __intcall(0x21, &ireg, NULL); + n++; + } + + return n; +} + +const struct output_dev dev_serial_w = { + .dev_magic = __DEV_MAGIC, + .flags = __DEV_TTY | __DEV_OUTPUT, + .fileflags = O_WRONLY | O_CREAT | O_TRUNC | O_APPEND, + .write = __serial_write, + .close = NULL, + .open = NULL, +}; diff --git a/com32/lib/sys/stdcon_read.c b/com32/lib/sys/stdcon_read.c new file mode 100644 index 0000000..075c170 --- /dev/null +++ b/com32/lib/sys/stdcon_read.c @@ -0,0 +1,77 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * stdcon_read.c + * + * Line-oriented reading from the standard console + */ + +#include +#include +#include +#include +#include "file.h" + +extern ssize_t __rawcon_read(struct file_info *fp, void *buf, size_t count); + +static ssize_t __stdcon_read(struct file_info *fp, void *buf, size_t count) +{ + char *bufp = buf; + size_t n = 0; + char ch; + + (void)fp; + + while ( n < count ) { + if ( fp->i.nbytes ) { + ch = *bufp++ = *fp->i.datap++; + fp->i.nbytes--; + n++; + if ( ch == '\n' ) + return n; + } else { + fp->i.nbytes = __line_input(fp, fp->i.buf, MAXBLOCK, + __rawcon_read); + fp->i.datap = fp->i.buf; + + if ( fp->i.nbytes == 0 ) + return n; + } + } + + return n; +} + +const struct input_dev dev_stdcon_r = { + .dev_magic = __DEV_MAGIC, + .flags = __DEV_TTY | __DEV_INPUT, + .fileflags = O_RDONLY, + .read = __stdcon_read, + .close = NULL, + .open = NULL, +}; diff --git a/com32/lib/sys/stdcon_write.c b/com32/lib/sys/stdcon_write.c new file mode 100644 index 0000000..458d2c2 --- /dev/null +++ b/com32/lib/sys/stdcon_write.c @@ -0,0 +1,71 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * stdcon_write.c + * + * Writing to the console; \n -> \r\n conversion. + */ + +#include +#include +#include +#include +#include "file.h" + +static ssize_t __stdcon_write(struct file_info *fp, const void *buf, size_t count) +{ + com32sys_t ireg; + const char *bufp = buf; + size_t n = 0; + + (void)fp; + + memset(&ireg, 0, sizeof ireg); + ireg.eax.b[1] = 0x02; + + while ( count-- ) { + if ( *bufp == '\n' ) { + ireg.edx.b[0] = '\r'; + __intcall(0x21, &ireg, NULL); + } + ireg.edx.b[0] = *bufp++; + __intcall(0x21, &ireg, NULL); + n++; + } + + return n; +} + +const struct output_dev dev_stdcon_w = { + .dev_magic = __DEV_MAGIC, + .flags = __DEV_TTY | __DEV_OUTPUT, + .fileflags = O_WRONLY | O_CREAT | O_TRUNC | O_APPEND, + .write = __stdcon_write, + .close = NULL, + .open = NULL, +}; diff --git a/com32/lib/sys/times.c b/com32/lib/sys/times.c new file mode 100644 index 0000000..4dae0aa --- /dev/null +++ b/com32/lib/sys/times.c @@ -0,0 +1,44 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * sys/times.c + * + * Returns something like a clock. + */ + +#include +#include +#include + +clock_t times(struct tms *buf) +{ + (void)buf; /* Ignored */ + + /* Should we get this via INT 1Ah? */ + return *(uint16_t *)0x46c; +} diff --git a/com32/lib/sys/vesa/alphatbl.pl b/com32/lib/sys/vesa/alphatbl.pl new file mode 100644 index 0000000..d70c76e --- /dev/null +++ b/com32/lib/sys/vesa/alphatbl.pl @@ -0,0 +1,53 @@ +#!/usr/bin/perl +# +# Produce gamma-correction tables for alpha blending, assuming sRGB space. +# + +sub srgb_to_linear($) +{ + my($s) = @_; + + if ($s <= 10) { + return $s/(255*12.92); + } else { + return (($s+14.025)/269.025)**2.4; + } +} + +sub linear_to_srgb($) +{ + my($l) = @_; + my $s; + + if ($l <= 0.00304) { + $s = 12.92*$l; + } else { + $s = 1.055*$l**(1.0/2.4) - 0.055; + } + + return int($s*255+0.5); +} + +# Header +print "#include \n\n"; + +# +# Table 1: convert 8-bit sRGB values to 16-bit linear values +# + +print "const uint16_t __vesacon_srgb_to_linear[256] = {\n"; +for ($i = 0; $i <= 255; $i++) { + printf "\t%5d,\n", int(srgb_to_linear($i)*65535+0.5); +} +print "};\n\n"; + +# +# Table 2: convert linear values in the range [0, 65535*255], +# shifted right by 12 bits, to sRGB +# + +print "const uint8_t __vesacon_linear_to_srgb[4080] = {\n"; +for ($i = 0; $i <= 4079; $i++) { + printf "\t%3d,\n", linear_to_srgb(($i+0.5)/4079.937744); +} +print "};\n\n"; diff --git a/com32/lib/sys/vesa/background.c b/com32/lib/sys/vesa/background.c new file mode 100644 index 0000000..f6b7b77 --- /dev/null +++ b/com32/lib/sys/vesa/background.c @@ -0,0 +1,417 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vesa.h" +#include "video.h" + +/*** FIX: This really should be alpha-blended with color index 0 ***/ + +/* For best performance, "start" should be a multiple of 4, to assure + aligned dwords. */ +static void draw_background_line(int line, int start, int npixels) +{ + uint32_t *bgptr = &__vesacon_background[line][start]; + unsigned int bytes_per_pixel = __vesacon_bytes_per_pixel; + size_t fbptr = line*__vesa_info.mi.logical_scan + start*bytes_per_pixel; + + __vesacon_copy_to_screen(fbptr, bgptr, npixels); +} + +/* This draws the border, then redraws the text area */ +static void draw_background(void) +{ + int i; + const int bottom_border = VIDEO_BORDER + + (TEXT_PIXEL_ROWS % __vesacon_font_height); + const int right_border = VIDEO_BORDER + (TEXT_PIXEL_COLS % FONT_WIDTH); + + for (i = 0; i < VIDEO_BORDER; i++) + draw_background_line(i, 0, VIDEO_X_SIZE); + + for (i = VIDEO_BORDER; i < VIDEO_Y_SIZE-bottom_border; i++) { + draw_background_line(i, 0, VIDEO_BORDER); + draw_background_line(i, VIDEO_X_SIZE-right_border, right_border); + } + + for (i = VIDEO_Y_SIZE-bottom_border; i < VIDEO_Y_SIZE; i++) + draw_background_line(i, 0, VIDEO_X_SIZE); + + __vesacon_redraw_text(); +} + +static int read_png_file(FILE *fp) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + png_infop end_ptr = NULL; +#if 0 + png_color_16p image_background; + static const png_color_16 my_background = {0,0,0,0,0}; +#endif + png_bytep row_pointers[VIDEO_Y_SIZE]; + int passes; + int i; + int rv = -1; + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, + NULL, NULL, NULL); + + info_ptr = png_create_info_struct(png_ptr); + end_ptr = png_create_info_struct(png_ptr); + + if (!png_ptr || !info_ptr || !end_ptr || + setjmp(png_jmpbuf(png_ptr))) + goto err; + + png_init_io(png_ptr, fp); + png_set_sig_bytes(png_ptr, 8); + + png_set_user_limits(png_ptr, VIDEO_X_SIZE, VIDEO_Y_SIZE); + + png_read_info(png_ptr, info_ptr); + + /* Set the appropriate set of transformations. We need to end up + with 32-bit BGRA format, no more, no less. */ + + /* Expand to RGB first... */ + if (info_ptr->color_type & PNG_COLOR_MASK_PALETTE) + png_set_palette_to_rgb(png_ptr); + else if (!(info_ptr->color_type & PNG_COLOR_MASK_COLOR)) + png_set_gray_to_rgb(png_ptr); + + /* Add alpha channel, if need be */ + if (!(png_ptr->color_type & PNG_COLOR_MASK_ALPHA)) { + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(png_ptr); + else + png_set_add_alpha(png_ptr, ~0, PNG_FILLER_AFTER); + } + + /* Adjust the byte order, if necessary */ + png_set_bgr(png_ptr); + + /* Make sure we end up with 8-bit data */ + if (info_ptr->bit_depth == 16) + png_set_strip_16(png_ptr); + else if (info_ptr->bit_depth < 8) + png_set_packing(png_ptr); + +#if 0 + if (png_get_bKGD(png_ptr, info_ptr, &image_background)) + png_set_background(png_ptr, image_background, + PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); + else + png_set_background(png_ptr, &my_background, + PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0); +#endif + + /* Whew! Now we should get the stuff we want... */ + for (i = 0; i < (int)info_ptr->height; i++) + row_pointers[i] = (void *)__vesacon_background[i]; + + passes = png_set_interlace_handling(png_ptr); + + for (i = 0; i < passes; i++) + png_read_rows(png_ptr, row_pointers, NULL, info_ptr->height); + + rv = 0; + + err: + if (png_ptr) + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + return rv; +} + +static int jpeg_sig_cmp(uint8_t *bytes, int len) +{ + (void)len; + return (bytes[0] == 0xff && bytes[1] == 0xd8) ? 0 : -1; +} + +static int read_jpeg_file(FILE *fp, uint8_t *header, int len) +{ + struct jdec_private *jdec = NULL; + void *jpeg_file = NULL; + size_t length_of_file; + unsigned int width, height; + int rv = -1; + unsigned char *components[1]; + unsigned int bytes_per_row[1]; + + rv = floadfile(fp, &jpeg_file, &length_of_file, header, len); + fclose(fp); + if (rv) + goto err; + + jdec = tinyjpeg_init(); + if (!jdec) + goto err; + + if (tinyjpeg_parse_header(jdec, jpeg_file, length_of_file) < 0) + goto err; + + tinyjpeg_get_size(jdec, &width, &height); + if (width > VIDEO_X_SIZE || height > VIDEO_Y_SIZE) + goto err; + + components[0] = (void *)&__vesacon_background[0]; + tinyjpeg_set_components(jdec, components, 1); + bytes_per_row[0] = VIDEO_X_SIZE << 2; + tinyjpeg_set_bytes_per_row(jdec, bytes_per_row, 1); + + tinyjpeg_decode(jdec, TINYJPEG_FMT_BGRA32); + + rv = 0; + + err: + /* Don't use tinyjpeg_free() here, since we didn't allow tinyjpeg + to allocate the frame buffer */ + if (jdec) + free(jdec); + + if (jpeg_file) + free(jpeg_file); + + return rv; +} + +/* Simple grey Gaussian hole, enough to look interesting */ +int vesacon_default_background(void) +{ + int x, y, dx, dy, dy2; + uint8_t *bgptr = (uint8_t *)&__vesacon_background; + uint8_t k; + + if (__vesacon_pixel_format == PXF_NONE) + return 0; /* Not in graphics mode */ + + for (y = 0, dy = -VIDEO_Y_SIZE/2; y < VIDEO_Y_SIZE; y++, dy++) { + dy2 = dy*dy; + for (x = 0, dx = -VIDEO_X_SIZE/2; x < VIDEO_X_SIZE; x++, dx++) { + k = __vesacon_linear_to_srgb[500+((dx*dx+dy2) >> 6)]; + bgptr[0] = k; /* Blue */ + bgptr[1] = k; /* Green */ + bgptr[2] = k; /* Red */ + bgptr += 4; /* Dummy alpha */ + } + } + + draw_background(); + return 0; +} + +/* Set the background to a single flat color */ +int vesacon_set_background(unsigned int rgb) +{ + void *bgptr = __vesacon_background; + unsigned int count = VIDEO_X_SIZE*VIDEO_Y_SIZE; + + if (__vesacon_pixel_format == PXF_NONE) + return 0; /* Not in graphics mode */ + + asm volatile("rep; stosl" + : "+D" (bgptr), "+c" (count) + : "a" (rgb) + : "memory"); + + draw_background(); + return 0; +} + +struct lss16_header { + uint32_t magic; + uint16_t xsize; + uint16_t ysize; +}; + +#define LSS16_MAGIC 0x1413f33d + +static inline int lss16_sig_cmp(const void *header, int len) +{ + const struct lss16_header *h = header; + + if (len != 8) + return 1; + + return !(h->magic == LSS16_MAGIC && + h->xsize <= VIDEO_X_SIZE && h->ysize <= VIDEO_Y_SIZE); +} + +static int read_lss16_file(FILE *fp, const void *header, int header_len) +{ + const struct lss16_header *h = header; + uint32_t colors[16], color; + bool has_nybble; + uint8_t byte; + int count; + int nybble, prev; + enum state { + st_start, + st_c0, + st_c1, + st_c2, + } state; + int i, x, y; + uint32_t *bgptr = (void *)__vesacon_background; + + /* Assume the header, 8 bytes, has already been loaded. */ + if (header_len != 8) + return -1; + + for (i = 0; i < 16; i++) { + uint8_t rgb[3]; + if (fread(rgb, 1, 3, fp) != 3) + return -1; + + colors[i] = (((rgb[0] & 63)*255/63) << 16) + + (((rgb[1] & 63)*255/63) << 8) + + ((rgb[2] & 63)*255/63); + } + + /* By spec, the state machine is per row */ + for (y = 0; y < h->ysize; y++) { + state = st_start; + has_nybble = false; + color = colors[prev = 0]; /* By specification */ + count = 0; + + x = 0; + while (x < h->xsize) { + if (!has_nybble) { + if (fread(&byte, 1, 1, fp) != 1) + return -1; + nybble = byte & 0xf; + has_nybble = true; + } else { + nybble = byte >> 4; + has_nybble = false; + } + + switch (state) { + case st_start: + if (nybble != prev) { + *bgptr++ = color = colors[prev = nybble]; + x++; + } else { + state = st_c0; + } + break; + + case st_c0: + if (nybble == 0) { + state = st_c1; + } else { + count = nybble; + goto do_run; + } + break; + + case st_c1: + count = nybble + 16; + state = st_c2; + break; + + case st_c2: + count += nybble << 4; + goto do_run; + + do_run: + count = min(count, h->xsize-x); + x += count; + asm volatile("rep; stosl" + : "+D" (bgptr), "+c" (count) : "a" (color)); + state = st_start; + break; + } + } + + /* Zero-fill rest of row */ + i = VIDEO_X_SIZE-x; + asm volatile("rep; stosl" + : "+D" (bgptr), "+c" (i) : "a" (0) : "memory"); + } + + /* Zero-fill rest of screen */ + i = (VIDEO_Y_SIZE-y)*VIDEO_X_SIZE; + asm volatile("rep; stosl" + : "+D" (bgptr), "+c" (i) : "a" (0) : "memory"); + + return 0; +} + +int vesacon_load_background(const char *filename) +{ + FILE *fp = NULL; + uint8_t header[8]; + int rv = 1; + + if (__vesacon_pixel_format == PXF_NONE) + return 0; /* Not in graphics mode */ + + fp = fopen(filename, "r"); + + if (!fp) + goto err; + + if (fread(header, 1, 8, fp) != 8) + goto err; + + if (!png_sig_cmp(header, 0, 8)) { + rv = read_png_file(fp); + } else if (!jpeg_sig_cmp(header, 8)) { + rv = read_jpeg_file(fp, header, 8); + } else if (!lss16_sig_cmp(header, 8)) { + rv = read_lss16_file(fp, header, 8); + } + + /* This actually displays the stuff */ + draw_background(); + + err: + if (fp) + fclose(fp); + + return rv; +} + +int __vesacon_init_background(void) +{ + /* The BSS clearing has already cleared __vesacon_background */ + + /* The VESA BIOS has already cleared the screen */ + return 0; +} diff --git a/com32/lib/sys/vesa/debug.h b/com32/lib/sys/vesa/debug.h new file mode 100644 index 0000000..50fed46 --- /dev/null +++ b/com32/lib/sys/vesa/debug.h @@ -0,0 +1,36 @@ +#ifndef LIB_SYS_VESA_DEBUG_H +#define LIB_SYS_VESA_DEBUG_H + +#if 0 + +#include +#include + +ssize_t __serial_write(void *fp, const void *buf, size_t count); + +static void debug(const char *str, ...) +{ + va_list va; + char buf[65536]; + size_t len; + + va_start(va, str); + len = vsnprintf(buf, sizeof buf, str, va); + va_end(va); + + if (len >= sizeof buf) + len = sizeof buf - 1; + + __serial_write(NULL, buf, len); +} + +#else + +static inline void debug(const char *str, ...) +{ + (void)str; +} + +#endif + +#endif /* LIB_SYS_VESA_DEBUG_H */ diff --git a/com32/lib/sys/vesa/drawtxt.c b/com32/lib/sys/vesa/drawtxt.c new file mode 100644 index 0000000..662eebe --- /dev/null +++ b/com32/lib/sys/vesa/drawtxt.c @@ -0,0 +1,317 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include "vesa.h" +#include "video.h" +#include "fill.h" + +/* + * Visible cursor information + */ +static uint8_t cursor_pattern[FONT_MAX_HEIGHT]; +static struct vesa_char *cursor_pointer = NULL; +static int cursor_x, cursor_y; + +static inline void *copy_dword(void *dst, void *src, size_t dword_count) +{ + asm volatile("rep; movsl" + : "+D" (dst), "+S" (src), "+c" (dword_count)); + return dst; /* Updated destination pointer */ +} + +static inline __attribute__((always_inline)) + uint8_t alpha_val(uint8_t fg, uint8_t bg, uint8_t alpha) +{ + unsigned int tmp; + + tmp = __vesacon_srgb_to_linear[fg] * alpha; + tmp += __vesacon_srgb_to_linear[bg] * (255-alpha); + + return __vesacon_linear_to_srgb[tmp >> 12]; +} + +static uint32_t alpha_pixel(uint32_t fg, uint32_t bg) +{ + uint8_t alpha = fg >> 24; + uint8_t fg_r = fg >> 16; + uint8_t fg_g = fg >> 8; + uint8_t fg_b = fg; + uint8_t bg_r = bg >> 16; + uint8_t bg_g = bg >> 8; + uint8_t bg_b = bg; + + return + (alpha_val(fg_r, bg_r, alpha) << 16)| + (alpha_val(fg_g, bg_g, alpha) << 8)| + (alpha_val(fg_b, bg_b, alpha)); +} + +static void vesacon_update_characters(int row, int col, int nrows, int ncols) +{ + const int height = __vesacon_font_height; + const int width = FONT_WIDTH; + uint32_t *bgrowptr, *bgptr, bgval, fgval; + uint32_t fgcolor = 0, bgcolor = 0, color; + uint8_t chbits = 0, chxbits = 0, chsbits = 0; + int i, j, jx, pixrow, pixsrow; + struct vesa_char *rowptr, *rowsptr, *cptr, *csptr; + unsigned int bytes_per_pixel = __vesacon_bytes_per_pixel; + unsigned long pixel_offset; + uint32_t row_buffer[VIDEO_X_SIZE], *rowbufptr; + size_t fbrowptr; + uint8_t sha; + + bgrowptr = &__vesacon_background[row*height+VIDEO_BORDER][col*width+VIDEO_BORDER]; + + pixel_offset = ((row*height+VIDEO_BORDER)*VIDEO_X_SIZE)+ + (col*width+VIDEO_BORDER); + + fbrowptr = (row*height+VIDEO_BORDER) * __vesa_info.mi.logical_scan + + (col*width+VIDEO_BORDER) * bytes_per_pixel; + + /* Note that we keep a 1-character guard area around the real text area... */ + rowptr = &__vesacon_text_display[(row+1)*(TEXT_PIXEL_COLS/FONT_WIDTH+2)+(col+1)]; + rowsptr = rowptr - ((TEXT_PIXEL_COLS/FONT_WIDTH+2)+1); + pixrow = 0; + pixsrow = height-1; + + for (i = height*nrows; i >= 0; i--) { + bgptr = bgrowptr; + rowbufptr = row_buffer; + + cptr = rowptr; + csptr = rowsptr; + + chsbits = __vesacon_graphics_font[csptr->ch][pixsrow]; + if (__unlikely(csptr == cursor_pointer)) + chsbits |= cursor_pattern[pixsrow]; + sha = console_color_table[csptr->attr].shadow; + chsbits &= (sha & 0x02) ? 0xff : 0x00; + chsbits ^= (sha & 0x01) ? 0xff : 0x00; + chsbits <<= (width-2); + csptr++; + + /* Draw two pixels beyond the end of the line. One for the shadow, + and one to make sure we have a whole dword of data for the copy + operation at the end. Note that this code depends on the fact that + all characters begin on dword boundaries in the frame buffer. */ + + for (jx = 1, j = width*ncols+1; j >= 0; j--) { + chbits <<= 1; + chsbits <<= 1; + chxbits <<= 1; + + switch (jx) { + case 1: + chbits = __vesacon_graphics_font[cptr->ch][pixrow]; + if (__unlikely(cptr == cursor_pointer)) + chbits |= cursor_pattern[pixrow]; + sha = console_color_table[cptr->attr].shadow; + chxbits = chbits; + chxbits &= (sha & 0x02) ? 0xff : 0x00; + chxbits ^= (sha & 0x01) ? 0xff : 0x00; + fgcolor = console_color_table[cptr->attr].argb_fg; + bgcolor = console_color_table[cptr->attr].argb_bg; + cptr++; + jx--; + break; + case 0: + chsbits = __vesacon_graphics_font[csptr->ch][pixsrow]; + if (__unlikely(csptr == cursor_pointer)) + chsbits |= cursor_pattern[pixsrow]; + sha = console_color_table[csptr->attr].shadow; + chsbits &= (sha & 0x02) ? 0xff : 0x00; + chsbits ^= (sha & 0x01) ? 0xff : 0x00; + csptr++; + jx = width-1; + break; + default: + jx--; + break; + } + + /* If this pixel is raised, use the offsetted value */ + bgval = (chxbits & 0x80) ? bgptr[VIDEO_X_SIZE+1] : *bgptr; + bgptr++; + + /* If this pixel is set, use the fg color, else the bg color */ + fgval = (chbits & 0x80) ? fgcolor : bgcolor; + + /* Produce the combined color pixel value */ + color = alpha_pixel(fgval, bgval); + + /* Apply the shadow (75% shadow) */ + if ((chsbits & ~chxbits) & 0x80) { + color >>= 2; + color &= 0x3f3f3f; + } + + *rowbufptr++ = color; + } + + /* Copy to frame buffer */ + __vesacon_copy_to_screen(fbrowptr, row_buffer, rowbufptr-row_buffer); + + bgrowptr += VIDEO_X_SIZE; + fbrowptr += __vesa_info.mi.logical_scan; + + if (++pixrow == height) { + rowptr += TEXT_PIXEL_COLS/FONT_WIDTH+2; + pixrow = 0; + } + if (++pixsrow == height) { + rowsptr += TEXT_PIXEL_COLS/FONT_WIDTH+2; + pixsrow = 0; + } + } +} + +/* Bounding box for changed text. The (x1, y1) coordinates are +1! */ +static unsigned int upd_x0 = -1U, upd_x1, upd_y0 = -1U, upd_y1; + +/* Update the range already touched by various variables */ +void __vesacon_doit(void) +{ + if (upd_x1 > upd_x0 && upd_y1 > upd_y0) { + vesacon_update_characters(upd_y0, upd_x0, upd_y1-upd_y0, upd_x1-upd_x0); + upd_x0 = upd_y0 = -1U; + upd_x1 = upd_y1 = 0; + } +} + +/* Mark a range for update; note argument sequence is the same as + vesacon_update_characters() */ +static inline void vesacon_touch(int row, int col, int rows, int cols) +{ + unsigned int y0 = row; + unsigned int x0 = col; + unsigned int y1 = y0+rows; + unsigned int x1 = x0+cols; + + if (y0 < upd_y0) + upd_y0 = y0; + if (y1 > upd_y1) + upd_y1 = y1; + if (x0 < upd_x0) + upd_x0 = x0; + if (x1 > upd_x1) + upd_x1 = x1; +} + +/* Erase a region of the screen */ +void __vesacon_erase(int x0, int y0, int x1, int y1, attr_t attr) +{ + int y; + struct vesa_char *ptr = &__vesacon_text_display + [(y0+1)*(TEXT_PIXEL_COLS/FONT_WIDTH+2)+(x0+1)]; + struct vesa_char fill = { + .ch = ' ', + .attr = attr, + }; + int ncols = x1-x0+1; + + for (y = y0; y <= y1; y++) { + vesacon_fill(ptr, fill, ncols); + ptr += TEXT_PIXEL_COLS/FONT_WIDTH+2; + } + + vesacon_touch(y0, x0, y1-y0+1, ncols); +} + +/* Scroll the screen up */ +void __vesacon_scroll_up(int nrows, attr_t attr) +{ + struct vesa_char *fromptr = &__vesacon_text_display + [(nrows+1)*(TEXT_PIXEL_COLS/FONT_WIDTH+2)]; + struct vesa_char *toptr = &__vesacon_text_display + [(TEXT_PIXEL_COLS/FONT_WIDTH+2)]; + int dword_count = (__vesacon_text_rows-nrows)*(TEXT_PIXEL_COLS/FONT_WIDTH+2); + struct vesa_char fill = { + .ch = ' ', + .attr = attr, + }; + + toptr = copy_dword(toptr, fromptr, dword_count); + + dword_count = nrows*(TEXT_PIXEL_COLS/FONT_WIDTH+2); + + vesacon_fill(toptr, fill, dword_count); + + vesacon_touch(0, 0, __vesacon_text_rows, TEXT_PIXEL_COLS/FONT_WIDTH); +} + +/* Draw one character text at a specific area of the screen */ +void __vesacon_write_char(int x, int y, uint8_t ch, attr_t attr) +{ + struct vesa_char *ptr = &__vesacon_text_display + [(y+1)*(TEXT_PIXEL_COLS/FONT_WIDTH+2)+(x+1)]; + + ptr->ch = ch; + ptr->attr = attr; + + vesacon_touch(y, x, 1, 1); +} + +void __vesacon_set_cursor(int x, int y, int visible) +{ + struct vesa_char *ptr = &__vesacon_text_display + [(y+1)*(TEXT_PIXEL_COLS/FONT_WIDTH+2)+(x+1)]; + + if (cursor_pointer) + vesacon_touch(cursor_y, cursor_x, 1, 1); + + if (!visible) { + /* Invisible cursor */ + cursor_pointer = NULL; + } else { + cursor_pointer = ptr; + vesacon_touch(y, x, 1, 1); + } + + cursor_x = x; + cursor_y = y; +} + +void __vesacon_init_cursor(int font_height) +{ + int r0 = font_height - (font_height < 10 ? 2 : 3); + + if (r0 < 0) + r0 = 0; + + /* cursor_pattern is assumed to be all zero */ + cursor_pattern[r0] = 0xff; + cursor_pattern[r0+1] = 0xff; +} + +void __vesacon_redraw_text(void) +{ + vesacon_update_characters(0, 0, __vesacon_text_rows, + TEXT_PIXEL_COLS/FONT_WIDTH); +} diff --git a/com32/lib/sys/vesa/fill.h b/com32/lib/sys/vesa/fill.h new file mode 100644 index 0000000..63ca577 --- /dev/null +++ b/com32/lib/sys/vesa/fill.h @@ -0,0 +1,66 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#ifndef LIB_SYS_VESA_FILL_H +#define LIB_SYS_VESA_FILL_H + +#include "video.h" + +/* Fill a number of characters. */ +static inline struct vesa_char *vesacon_fill(struct vesa_char *ptr, + struct vesa_char fill, + unsigned int count) +{ + switch (sizeof(struct vesa_char)) { + case 1: + asm volatile("rep; stosb" + : "+D" (ptr), "+c" (count) + : "a" (fill) + : "memory"); + break; + case 2: + asm volatile("rep; stosw" + : "+D" (ptr), "+c" (count) + : "a" (fill) + : "memory"); + break; + case 4: + asm volatile("rep; stosl" + : "+D" (ptr), "+c" (count) + : "a" (fill) + : "memory"); + break; + default: + while (count--) + *ptr++ = fill; + break; + } + + return ptr; +} + +#endif /* LIB_SYS_VESA_FILL_H */ diff --git a/com32/lib/sys/vesa/fmtpixel.c b/com32/lib/sys/vesa/fmtpixel.c new file mode 100644 index 0000000..0b80197 --- /dev/null +++ b/com32/lib/sys/vesa/fmtpixel.c @@ -0,0 +1,102 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * fmtpixel.c + * + * Functions to format a single pixel + */ + +#include +#include "video.h" + +/* + * Format a sequence of pixels. The first argument is the line buffer; + * we can use it to write up to 4 bytes past the end of the last pixel. + * Return the place we should be copying from, this is usually the + * buffer address, but doesn't *have* to be. + */ + +static const void * +format_pxf_bgra32(void *ptr, const uint32_t *p, size_t n) +{ + (void)ptr; (void)n; + return p; /* No conversion needed! */ +} + +static const void * +format_pxf_bgr24(void *ptr, const uint32_t *p, size_t n) +{ + char *q = ptr; + + while (n--) { + *(uint32_t *)q = *p++; + q += 3; + } + return ptr; +} + +static const void * +format_pxf_le_rgb16_565(void *ptr, const uint32_t *p, size_t n) +{ + uint32_t bgra; + uint16_t *q = ptr; + + while (n--) { + bgra = *p++; + *q++ = + ((bgra >> 3) & 0x1f) + + ((bgra >> (2+8-5)) & (0x3f << 5)) + + ((bgra >> (3+16-11)) & (0x1f << 11)); + } + return ptr; +} + +static const void * +format_pxf_le_rgb15_555(void *ptr, const uint32_t *p, size_t n) +{ + uint32_t bgra; + uint16_t *q = ptr; + + while (n--) { + bgra = *p++; + *q++ = + ((bgra >> 3) & 0x1f) + + ((bgra >> (2+8-5)) & (0x1f << 5)) + + ((bgra >> (3+16-10)) & (0x1f << 10)); + } + return ptr; +} + +__vesacon_format_pixels_t __vesacon_format_pixels; + +const __vesacon_format_pixels_t __vesacon_format_pixels_list[PXF_NONE] = { + [PXF_BGRA32] = format_pxf_bgra32, + [PXF_BGR24] = format_pxf_bgr24, + [PXF_LE_RGB16_565] = format_pxf_le_rgb16_565, + [PXF_LE_RGB15_555] = format_pxf_le_rgb15_555, +}; diff --git a/com32/lib/sys/vesa/initvesa.c b/com32/lib/sys/vesa/initvesa.c new file mode 100644 index 0000000..0c2574d --- /dev/null +++ b/com32/lib/sys/vesa/initvesa.c @@ -0,0 +1,321 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1999-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * initvesa.c + * + * Query the VESA BIOS and select a 640x480x32 mode with local mapping + * support, if one exists. + */ + +#include +#include +#include +#include +#include +#include "vesa.h" +#include "video.h" +#include "fill.h" +#include "debug.h" + +struct vesa_info __vesa_info; + +struct vesa_char *__vesacon_text_display; + +int __vesacon_font_height, __vesacon_text_rows; +enum vesa_pixel_format __vesacon_pixel_format = PXF_NONE; +unsigned int __vesacon_bytes_per_pixel; +uint8_t __vesacon_graphics_font[FONT_MAX_CHARS][FONT_MAX_HEIGHT]; + +uint32_t __vesacon_background[VIDEO_Y_SIZE][VIDEO_X_SIZE]; + +static void unpack_font(uint8_t *dst, uint8_t *src, int height) +{ + int i; + + for (i = 0; i < FONT_MAX_CHARS; i++) { + memcpy(dst, src, height); + memset(dst+height, 0, FONT_MAX_HEIGHT-height); + + dst += FONT_MAX_HEIGHT; + src += height; + } +} + +static int __constfunc is_power_of_2(unsigned int x) +{ + return x && !(x & (x-1)); +} + +static int vesacon_paged_mode_ok(const struct vesa_mode_info *mi) +{ + int i; + + if (!is_power_of_2(mi->win_size) || + !is_power_of_2(mi->win_grain) || + mi->win_grain > mi->win_size) + return 0; /* Impossible... */ + + for (i = 0; i < 2; i++) { + if ((mi->win_attr[i] & 0x05) == 0x05 && mi->win_seg[i]) + return 1; /* Usable window */ + } + + return 0; /* Nope... */ +} + +static int vesacon_set_mode(void) +{ + com32sys_t rm; + uint8_t *rom_font; + uint16_t mode, bestmode, *mode_ptr; + struct vesa_general_info *gi; + struct vesa_mode_info *mi; + enum vesa_pixel_format pxf, bestpxf; + + /* Allocate space in the bounce buffer for these structures */ + gi = &((struct vesa_info *)__com32.cs_bounce)->gi; + mi = &((struct vesa_info *)__com32.cs_bounce)->mi; + + debug("Hello, World!\r\n"); + + memset(&rm, 0, sizeof rm); + memset(gi, 0, sizeof *gi); + + gi->signature = VBE2_MAGIC; /* Get VBE2 extended data */ + rm.eax.w[0] = 0x4F00; /* Get SVGA general information */ + rm.edi.w[0] = OFFS(gi); + rm.es = SEG(gi); + __intcall(0x10, &rm, &rm); + + if ( rm.eax.w[0] != 0x004F ) + return 1; /* Function call failed */ + if ( gi->signature != VESA_MAGIC ) + return 2; /* No magic */ + if ( gi->version < 0x0102 ) + return 3; /* VESA 1.2+ required */ + + /* Copy general info */ + memcpy(&__vesa_info.gi, gi, sizeof *gi); + + /* Search for a 640x480 mode with a suitable color and memory model... */ + + mode_ptr = GET_PTR(gi->video_mode_ptr); + bestmode = 0; + bestpxf = PXF_NONE; + + while ((mode = *mode_ptr++) != 0xFFFF) { + mode &= 0x1FF; /* The rest are attributes of sorts */ + + debug("Found mode: 0x%04x\r\n", mode); + + memset(mi, 0, sizeof *mi); + rm.eax.w[0] = 0x4F01; /* Get SVGA mode information */ + rm.ecx.w[0] = mode; + rm.edi.w[0] = OFFS(mi); + rm.es = SEG(mi); + __intcall(0x10, &rm, &rm); + + /* Must be a supported mode */ + if ( rm.eax.w[0] != 0x004f ) + continue; + + debug("mode_attr 0x%04x, h_res = %4d, v_res = %4d, bpp = %2d, layout = %d (%d,%d,%d)\r\n", + mi->mode_attr, mi->h_res, mi->v_res, mi->bpp, mi->memory_layout, + mi->rpos, mi->gpos, mi->bpos); + + /* Must be an LFB color graphics mode supported by the hardware. + + The bits tested are: + 4 - graphics mode + 3 - color mode + 1 - mode information available (mandatory in VBE 1.2+) + 0 - mode supported by hardware + */ + if ( (mi->mode_attr & 0x001b) != 0x001b ) + continue; + + /* Must be 640x480 */ + if ( mi->h_res != VIDEO_X_SIZE || mi->v_res != VIDEO_Y_SIZE ) + continue; + + /* We don't support multibank (interlaced memory) modes */ + /* + * Note: The Bochs VESA BIOS (vbe.c 1.58 2006/08/19) violates the + * specification which states that banks == 1 for unbanked modes; + * fortunately it does report bank_size == 0 for those. + */ + if ( mi->banks > 1 && mi->bank_size ) { + debug("bad: banks = %d, banksize = %d, pages = %d\r\n", + mi->banks, mi->bank_size, mi->image_pages); + continue; + } + + /* Must be either a flat-framebuffer mode, or be an acceptable + paged mode */ + if ( !(mi->mode_attr & 0x0080) && !vesacon_paged_mode_ok(mi) ) { + debug("bad: invalid paged mode\r\n"); + continue; + } + + /* Must either be a packed-pixel mode or a direct color mode + (depending on VESA version ); must be a supported pixel format */ + pxf = PXF_NONE; /* Not usable */ + + if (mi->bpp == 32 && + (mi->memory_layout == 4 || + (mi->memory_layout == 6 && mi->rpos == 16 && mi->gpos == 8 && + mi->bpos == 0))) + pxf = PXF_BGRA32; + else if (mi->bpp == 24 && + (mi->memory_layout == 4 || + (mi->memory_layout == 6 && mi->rpos == 16 && mi->gpos == 8 && + mi->bpos == 0))) + pxf = PXF_BGR24; + else if (mi->bpp == 16 && + (mi->memory_layout == 4 || + (mi->memory_layout == 6 && mi->rpos == 11 && mi->gpos == 5 && + mi->bpos == 0))) + pxf = PXF_LE_RGB16_565; + else if (mi->bpp == 15 && + (mi->memory_layout == 4 || + (mi->memory_layout == 6 && mi->rpos == 10 && mi->gpos == 5 && + mi->bpos == 0))) + pxf = PXF_LE_RGB15_555; + + if (pxf < bestpxf) { + debug("Best mode so far, pxf = %d\r\n", pxf); + + /* Best mode so far... */ + bestmode = mode; + bestpxf = pxf; + + /* Copy mode info */ + memcpy(&__vesa_info.mi, mi, sizeof *mi); + } + } + + if (bestpxf == PXF_NONE) + return 4; /* No mode found */ + + mi = &__vesa_info.mi; + mode = bestmode; + __vesacon_bytes_per_pixel = (mi->bpp+7) >> 3; + __vesacon_format_pixels = __vesacon_format_pixels_list[bestpxf]; + + /* Download the SYSLINUX- or BIOS-provided font */ + rm.eax.w[0] = 0x0018; /* Query custom font */ + __intcall(0x22, &rm, &rm); + if (!(rm.eflags.l & EFLAGS_CF) && rm.eax.b[0]) { + __vesacon_font_height = rm.eax.b[0]; + rom_font = MK_PTR(rm.es, rm.ebx.w[0]); + } else { + rm.eax.w[0] = 0x1130; /* Get Font Information */ + rm.ebx.w[0] = 0x0600; /* Get 8x16 ROM font */ + __intcall(0x10, &rm, &rm); + rom_font = MK_PTR(rm.es, rm.ebp.w[0]); + __vesacon_font_height = 16; + } + unpack_font((uint8_t *)__vesacon_graphics_font, rom_font, + __vesacon_font_height); + __vesacon_text_rows = (VIDEO_Y_SIZE-2*VIDEO_BORDER)/__vesacon_font_height; + __vesacon_init_cursor(__vesacon_font_height); + + /* Now set video mode */ + rm.eax.w[0] = 0x4F02; /* Set SVGA video mode */ + if (mi->mode_attr & 0x0080) + mode |= 0x4000; /* Request linear framebuffer if supported */ + rm.ebx.w[0] = mode; + __intcall(0x10, &rm, &rm); + if ( rm.eax.w[0] != 0x004F ) + return 9; /* Failed to set mode */ + + __vesacon_init_copy_to_screen(); + + /* Tell syslinux we changed video mode */ + rm.eax.w[0] = 0x0017; /* Report video mode change */ + /* In theory this should be: + + rm.ebx.w[0] = (mi->mode_attr & 4) ? 0x0007 : 0x000f; + + However, that would assume all systems that claim to handle text + output in VESA modes actually do that... */ + rm.ebx.w[0] = 0x000f; + rm.ecx.w[0] = VIDEO_X_SIZE; + rm.edx.w[0] = VIDEO_Y_SIZE; + __intcall(0x22, &rm, NULL); + + __vesacon_pixel_format = bestpxf; + + return 0; +} + + +static int init_text_display(void) +{ + size_t nchars; + struct vesa_char *ptr; + struct vesa_char def_char = { + .ch = ' ', + .attr = 0, + }; + + nchars = (TEXT_PIXEL_ROWS/__vesacon_font_height+2)* + (TEXT_PIXEL_COLS/FONT_WIDTH+2); + + __vesacon_text_display = ptr = malloc(nchars*sizeof(struct vesa_char)); + + if (!ptr) + return -1; + + + vesacon_fill(ptr, def_char, nchars); + + return 0; +} + +int __vesacon_init(void) +{ + int rv; + + /* We need the FPU for graphics, at least libpng et al will need it... */ + if (x86_init_fpu()) + return 10; + + rv = vesacon_set_mode(); + if (rv) + return rv; + + init_text_display(); + + debug("Mode set, now drawing at %#p\r\n", __vesa_info.mi.lfb_ptr); + + __vesacon_init_background(); + + debug("Ready!\r\n"); + return 0; +} diff --git a/com32/lib/sys/vesa/screencpy.c b/com32/lib/sys/vesa/screencpy.c new file mode 100644 index 0000000..a63a1cc --- /dev/null +++ b/com32/lib/sys/vesa/screencpy.c @@ -0,0 +1,122 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include +#include +#include +#include "vesa.h" +#include "video.h" + +static struct win_info { + char *win_base; + size_t win_pos; + size_t win_size; + int win_gshift; + int win_num; +} wi; + +static inline int __constfunc ilog2(unsigned int x) +{ + asm("bsrl %1,%0" : "=r" (x) : "rm" (x)); + return x; +} + +void __vesacon_init_copy_to_screen(void) +{ + struct vesa_mode_info * const mi = &__vesa_info.mi; + int winn; + + if (mi->mode_attr & 0x0080) { + /* Linear frame buffer */ + + wi.win_base = (char *)mi->lfb_ptr; + wi.win_size = 1 << 31; /* 2 GB, i.e. one huge window */ + wi.win_pos = 0; /* Already positioned (only one position...) */ + wi.win_num = -1; /* Not a window */ + } else { + /* Paged frame buffer */ + + /* We have already tested that *one* of these is usable */ + if ((mi->win_attr[0] & 0x05) == 0x05 && mi->win_seg[0]) + winn = 0; + else + winn = 1; + + wi.win_num = winn; + wi.win_base = (char *)(mi->win_seg[winn] << 4); + wi.win_size = mi->win_size << 10; + wi.win_gshift = ilog2(mi->win_grain) + 10; + wi.win_pos = -1; /* Undefined position */ + } +} + +static void set_window_pos(size_t win_pos) +{ + static com32sys_t ireg; + + wi.win_pos = win_pos; + + if (wi.win_num < 0) + return; /* This should never happen... */ + + ireg.eax.w[0] = 0x4F05; + ireg.ebx.b[0] = wi.win_num; + ireg.edx.w[0] = win_pos >> wi.win_gshift; + + __intcall(0x10, &ireg, NULL); +} + +void __vesacon_copy_to_screen(size_t dst, const uint32_t *src, size_t npixels) +{ + size_t win_pos, win_off; + size_t win_size = wi.win_size; + size_t omask = win_size - 1; + char *win_base = wi.win_base; + size_t l; + size_t bytes = npixels * __vesacon_bytes_per_pixel; + char rowbuf[bytes+4] __aligned(4); + const char *s; + + s = (const char *)__vesacon_format_pixels(rowbuf, src, npixels); + + while (bytes) { + win_off = dst & omask; + win_pos = dst & ~omask; + + if (__unlikely(win_pos != wi.win_pos)) + set_window_pos(win_pos); + + l = min(bytes, win_size-win_off); + memcpy(win_base+win_off, s, l); + + bytes -= l; + s += l; + dst += l; + } +} diff --git a/com32/lib/sys/vesa/vesa.h b/com32/lib/sys/vesa/vesa.h new file mode 100644 index 0000000..a7150b3 --- /dev/null +++ b/com32/lib/sys/vesa/vesa.h @@ -0,0 +1,113 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1999-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#ifndef LIB_SYS_VESA_H +#define LIB_SYS_VESA_H + +#include +#include + +/* VESA General Information table */ +struct vesa_general_info { + uint32_t signature; /* Magic number = "VESA" */ + uint16_t version; + far_ptr_t vendor_string; + uint8_t capabilities[4]; + far_ptr_t video_mode_ptr; + uint16_t total_memory; + + uint16_t oem_software_rev; + far_ptr_t oem_vendor_name_ptr; + far_ptr_t oem_product_name_ptr; + far_ptr_t oem_product_rev_ptr; + + uint8_t reserved[222]; + uint8_t oem_data[256]; +} __attribute__((packed)); + +#define VESA_MAGIC ('V' + ('E' << 8) + ('S' << 16) + ('A' << 24)) +#define VBE2_MAGIC ('V' + ('B' << 8) + ('E' << 16) + ('2' << 24)) + +struct vesa_mode_info { + uint16_t mode_attr; + uint8_t win_attr[2]; + uint16_t win_grain; + uint16_t win_size; + uint16_t win_seg[2]; + far_ptr_t win_scheme; + uint16_t logical_scan; + + uint16_t h_res; + uint16_t v_res; + uint8_t char_width; + uint8_t char_height; + uint8_t memory_planes; + uint8_t bpp; + uint8_t banks; + uint8_t memory_layout; + uint8_t bank_size; + uint8_t image_pages; + uint8_t page_function; + + uint8_t rmask; + uint8_t rpos; + uint8_t gmask; + uint8_t gpos; + uint8_t bmask; + uint8_t bpos; + uint8_t resv_mask; + uint8_t resv_pos; + uint8_t dcm_info; + + uint8_t *lfb_ptr; /* Linear frame buffer address */ + uint8_t *offscreen_ptr; /* Offscreen memory address */ + uint16_t offscreen_size; + + uint8_t reserved[206]; +} __attribute__((packed)); + +struct vesa_info { + struct vesa_general_info gi; + struct vesa_mode_info mi; +}; + +extern struct vesa_info __vesa_info; + +#if 0 +static inline void vesa_debug(uint32_t color, int pos) +{ + uint32_t *stp = (uint32_t *)__vesa_info.mi.lfb_ptr; + stp[pos*3] = color; +} +#else +static inline void vesa_debug(uint32_t color, int pos) +{ + (void)color; (void)pos; +} +#endif + +#endif /* LIB_SYS_VESA_H */ diff --git a/com32/lib/sys/vesa/video.h b/com32/lib/sys/vesa/video.h new file mode 100644 index 0000000..40537c1 --- /dev/null +++ b/com32/lib/sys/vesa/video.h @@ -0,0 +1,91 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#ifndef LIB_SYS_VESA_VIDEO_H +#define LIB_SYS_VESA_VIDEO_H + +#include + +#define FONT_MAX_CHARS 256 +#define FONT_MAX_HEIGHT 32 +#define FONT_WIDTH 8 + +#define VIDEO_X_SIZE 640 +#define VIDEO_Y_SIZE 480 + +#define VIDEO_BORDER 8 +#define TEXT_PIXEL_ROWS (VIDEO_Y_SIZE-2*VIDEO_BORDER) +#define TEXT_PIXEL_COLS (VIDEO_X_SIZE-2*VIDEO_BORDER) + +typedef uint16_t attr_t; + +struct vesa_char { + uint8_t ch; /* Character */ + uint8_t _filler; /* Currently unused */ + attr_t attr; /* Color table index */ +}; + +/* Pixel formats in order of decreasing preference; PXF_NONE should be last */ +/* BGR24 is preferred over BGRA32 since the I/O overhead is smaller. */ +enum vesa_pixel_format { + PXF_BGR24, /* 24-bit BGR */ + PXF_BGRA32, /* 32-bit BGRA */ + PXF_LE_RGB16_565, /* 16-bit littleendian 5:6:5 RGB */ + PXF_LE_RGB15_555, /* 15-bit littleendian 5:5:5 RGB */ + PXF_NONE +}; +extern enum vesa_pixel_format __vesacon_pixel_format; +extern unsigned int __vesacon_bytes_per_pixel; +typedef const void * (*__vesacon_format_pixels_t)(void *, const uint32_t *, + size_t); +extern __vesacon_format_pixels_t __vesacon_format_pixels; +extern const __vesacon_format_pixels_t __vesacon_format_pixels_list[PXF_NONE]; + +extern struct vesa_char *__vesacon_text_display; + +extern int __vesacon_font_height, __vesacon_text_rows; +extern uint8_t __vesacon_graphics_font[FONT_MAX_CHARS][FONT_MAX_HEIGHT]; +extern uint32_t __vesacon_background[VIDEO_Y_SIZE][VIDEO_X_SIZE]; +extern uint32_t __vesacon_shadowfb[VIDEO_Y_SIZE][VIDEO_X_SIZE]; + +extern const uint16_t __vesacon_srgb_to_linear[256]; +extern const uint8_t __vesacon_linear_to_srgb[4080]; + +int __vesacon_init_background(void); +int vesacon_load_background(const char *); +int __vesacon_init(void); +void __vesacon_init_cursor(int); +void __vesacon_erase(int, int, int, int, attr_t); +void __vesacon_scroll_up(int, attr_t); +void __vesacon_write_char(int, int, uint8_t, attr_t); +void __vesacon_redraw_text(void); +void __vesacon_doit(void); +void __vesacon_set_cursor(int, int, int); +void __vesacon_copy_to_screen(size_t, const uint32_t *, size_t); +void __vesacon_init_copy_to_screen(void); + +#endif /* LIB_SYS_VESA_VIDEO_H */ diff --git a/com32/lib/sys/vesacon_write.c b/com32/lib/sys/vesacon_write.c new file mode 100644 index 0000000..7ca25ac --- /dev/null +++ b/com32/lib/sys/vesacon_write.c @@ -0,0 +1,171 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * vesacon_write.c + * + * Write to the screen using ANSI control codes (about as capable as + * DOS' ANSI.SYS.) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ansi.h" +#include "file.h" +#include "vesa/video.h" + +static void vesacon_erase(const struct term_state *, int, int, int, int); +static void vesacon_write_char(int, int, uint8_t, const struct term_state *); +static void vesacon_showcursor(const struct term_state *); +static void vesacon_scroll_up(const struct term_state *); + +static struct term_state ts; +static struct ansi_ops op = { + .erase = vesacon_erase, + .write_char = vesacon_write_char, + .showcursor = vesacon_showcursor, + .set_cursor = __vesacon_set_cursor, /* in drawtxt.c */ + .scroll_up = vesacon_scroll_up, + .beep = __ansicon_beep, +}; + +static struct term_info ti = + { + .cols = TEXT_PIXEL_COLS/FONT_WIDTH, + .disabled = 0, + .ts = &ts, + .op = &op + }; + +/* Reference counter to the screen, to keep track of if we need + reinitialization. */ +static int vesacon_counter = 0; + +/* Common setup */ +int __vesacon_open(struct file_info *fp) +{ + (void)fp; + + if (!vesacon_counter) { + /* Are we disabled? */ + if (syslinux_serial_console_info()->flowctl & 0x8000) { + ti.disabled = 1; + ti.rows = 25; + ti.cols = 80; + } else { + /* Switch mode */ + if (__vesacon_init()) { + vesacon_counter = -1; + return EAGAIN; + } + + /* Initial state */ + __ansi_init(&ti); + ti.rows = __vesacon_text_rows; + } + } else if (vesacon_counter == -1) { + return EAGAIN; + } + + fp->o.rows = ti.rows; + fp->o.cols = ti.cols; + + vesacon_counter++; + return 0; +} + +int __vesacon_close(struct file_info *fp) +{ + (void)fp; + + vesacon_counter--; + return 0; +} + +/* Erase a region of the screen */ +static void vesacon_erase(const struct term_state *st, + int x0, int y0, int x1, int y1) +{ + __vesacon_erase(x0, y0, x1, y1, st->cindex); +} + +/* Draw text on the screen */ +static void vesacon_write_char(int x, int y, uint8_t ch, + const struct term_state *st) +{ + __vesacon_write_char(x, y, ch, st->cindex); +} + +/* Show or hide the cursor */ +static void vesacon_showcursor(const struct term_state *st) +{ + __vesacon_set_cursor(st->xy.x, st->xy.y, st->cursor); +} + +static void vesacon_scroll_up(const struct term_state *st) +{ + __vesacon_scroll_up(1, st->cindex); +} + +ssize_t __vesacon_write(struct file_info *fp, const void *buf, size_t count) +{ + const unsigned char *bufp = buf; + size_t n = 0; + + (void)fp; + + if ( ti.disabled ) + return count; /* Nothing to do */ + + /* This only updates the shadow text buffer... */ + while ( count-- ) { + __ansi_putchar(&ti, *bufp++); + n++; + } + + /* This actually draws it */ + __vesacon_doit(); + + return n; +} + +const struct output_dev dev_vesacon_w = { + .dev_magic = __DEV_MAGIC, + .flags = __DEV_TTY | __DEV_OUTPUT, + .fileflags = O_WRONLY | O_CREAT | O_TRUNC | O_APPEND, + .write = __vesacon_write, + .close = __vesacon_close, + .open = __vesacon_open, + .fallback = &dev_ansicon_w, +}; diff --git a/com32/lib/sys/vesaserial_write.c b/com32/lib/sys/vesaserial_write.c new file mode 100644 index 0000000..f492917 --- /dev/null +++ b/com32/lib/sys/vesaserial_write.c @@ -0,0 +1,60 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * vesaserial_write.c + * + * Write to both to the VESA console and the serial port + */ + +#include +#include +#include +#include +#include +#include "file.h" + +extern int __vesacon_open(void); +extern int __vesacon_close(struct file_info *); +extern ssize_t __vesacon_write(struct file_info *, const void *, size_t); +extern ssize_t __xserial_write(struct file_info *, const void *, size_t); + +static ssize_t __vesaserial_write(struct file_info *fp, const void *buf, size_t count) +{ + __vesacon_write(fp, buf, count); + return __xserial_write(fp, buf, count); +} + +const struct output_dev dev_vesaserial_w = { + .dev_magic = __DEV_MAGIC, + .flags = __DEV_TTY | __DEV_OUTPUT, + .fileflags = O_WRONLY | O_CREAT | O_TRUNC | O_APPEND, + .write = __vesaserial_write, + .close = __vesacon_close, + .open = __vesacon_open, + .fallback = &dev_ansiserial_w, +}; diff --git a/com32/lib/sys/write.c b/com32/lib/sys/write.c new file mode 100644 index 0000000..929ec3e --- /dev/null +++ b/com32/lib/sys/write.c @@ -0,0 +1,51 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * write.c + * + * Write to a file descriptor + */ + +#include +#include +#include +#include +#include +#include "file.h" + +ssize_t write(int fd, void *buf, size_t count) +{ + struct file_info *fp = &__file_info[fd]; + + if ( fd >= NFILES || !fp->oop ) { + errno = EBADF; + return -1; + } + + return fp->oop->write(fp, buf, count); +} diff --git a/com32/lib/sys/x86_init_fpu.c b/com32/lib/sys/x86_init_fpu.c new file mode 100644 index 0000000..9865155 --- /dev/null +++ b/com32/lib/sys/x86_init_fpu.c @@ -0,0 +1,58 @@ +/* + * x86_has_fpu.c + * + * Test for an x86 FPU, and do any necessary setup. + */ + +#include +#include + +static inline uint64_t get_cr0(void) +{ + uint32_t v; + asm("movl %%cr0,%0" : "=r" (v)); + return v; +} + +static inline void set_cr0(uint32_t v) +{ + asm volatile("movl %0,%%cr0" : : "r" (v)); +} + +#define CR0_PE 0x00000001 +#define CR0_MP 0x00000002 +#define CR0_EM 0x00000004 +#define CR0_TS 0x00000008 +#define CR0_ET 0x00000010 +#define CR0_NE 0x00000020 +#define CR0_WP 0x00010000 +#define CR0_AM 0x00040000 +#define CR0_NW 0x20000000 +#define CR0_CD 0x40000000 +#define CR0_PG 0x80000000 + +int x86_init_fpu(void) +{ + uint32_t cr0; + uint16_t fsw = 0xffff; + uint16_t fcw = 0xffff; + + cr0 = get_cr0(); + cr0 &= ~(CR0_EM|CR0_TS); + cr0 |= CR0_MP; + set_cr0(cr0); + + asm volatile("fninit"); + asm volatile("fnstsw %0" : "+m" (fsw)); + if (fsw != 0) + return -1; + + asm volatile("fnstcw %0" : "+m" (fcw)); + if ((fcw & 0x103f) != 0x3f) + return -1; + + /* Techically, this could be a 386 with a 287. We could add a check + for that here... */ + + return 0; +} diff --git a/com32/lib/sys/xserial_write.c b/com32/lib/sys/xserial_write.c new file mode 100644 index 0000000..6d0d0f7 --- /dev/null +++ b/com32/lib/sys/xserial_write.c @@ -0,0 +1,118 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * xserial_write.c + * + * Raw writing to the serial port; \n -> \r\n translation, and + * convert \1# sequences. + */ + +#include +#include +#include +#include +#include +#include +#include "file.h" + +static void emit(char ch) +{ + static com32sys_t ireg; /* Zeroed with the BSS */ + + ireg.eax.b[1] = 0x04; + ireg.edx.b[0] = ch; + + __intcall(0x21, &ireg, NULL); +} + +ssize_t __xserial_write(struct file_info *fp, const void *buf, size_t count) +{ + const char *bufp = buf; + size_t n = 0; + static enum { st_init, st_tbl, st_tblc } state = st_init; + static int ndigits; + static int ncolor = 0; + int num; + const char *p; + + (void)fp; + + if (!syslinux_serial_console_info()->iobase) + return count; /* Nothing to do */ + + while ( count-- ) { + unsigned char ch = *bufp++; + + switch (state) { + case st_init: + if (ch >= 1 && ch <= 5) { + state = st_tbl; + ndigits = ch; + } else if (ch == '\n') { + emit('\r'); + emit('\n'); + } else { + emit(ch); + } + break; + + case st_tbl: + if (ch == '#') { + state = st_tblc; + ncolor = 0; + } else { + state = st_init; + } + break; + + case st_tblc: + num = ch-'0'; + if (num < 10) { + ncolor = (ncolor*10)+num; + if (--ndigits == 0) { + if (ncolor < console_color_table_size) { + emit('\033'); + emit('['); + emit('0'); + emit(';'); + for (p = console_color_table[ncolor].ansi; *p; p++) + emit(*p); + emit('m'); + } + state = st_init; + } + } else { + state = st_init; + } + break; + } + n++; + } + + return n; +} diff --git a/com32/lib/sys/zeroregs.c b/com32/lib/sys/zeroregs.c new file mode 100644 index 0000000..6d52492 --- /dev/null +++ b/com32/lib/sys/zeroregs.c @@ -0,0 +1,5 @@ +#include + +/* When we don't need to pass any registers, it's convenient to just + be able to pass a prepared all-zero structure. */ +const com32sys_t __com32_zero_regs; diff --git a/com32/lib/sys/zfile.c b/com32/lib/sys/zfile.c new file mode 100644 index 0000000..9dbee9b --- /dev/null +++ b/com32/lib/sys/zfile.c @@ -0,0 +1,171 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2003-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include +#include +#include +#include + +#include "file.h" +#include "zlib.h" + +/* + * zopen.c + * + * Open an ordinary file, possibly compressed; if so, insert + * an appropriate decompressor. + */ + +int __file_get_block(struct file_info *fp); +int __file_close(struct file_info *fp); + +static ssize_t gzip_file_read(struct file_info *, void *, size_t); +static int gzip_file_close(struct file_info *); + +static const struct input_dev gzip_file_dev = { + .dev_magic = __DEV_MAGIC, + .flags = __DEV_FILE | __DEV_INPUT, + .fileflags = O_RDONLY, + .read = gzip_file_read, + .close = gzip_file_close, + .open = NULL, +}; + +static int gzip_file_init(struct file_info *fp) +{ + z_streamp zs = calloc(1, sizeof(z_stream)); + + if (!zs) + return -1; + + fp->i.pvt = zs; + + zs->next_in = (void *)fp->i.datap; + zs->avail_in = fp->i.nbytes; + + if (inflateInit2(zs, 15+32) != Z_OK) { + errno = EIO; + return -1; + } + + fp->iop = &gzip_file_dev; + fp->i.length = -1; /* Unknown */ + + return 0; +} + +static ssize_t gzip_file_read(struct file_info *fp, void *ptr, size_t n) +{ + z_streamp zs = fp->i.pvt; + int rv; + ssize_t bytes; + ssize_t nout = 0; + unsigned char *p = ptr; + + while ( n ) { + zs->next_out = p; + zs->avail_out = n; + + if (!zs->avail_in && fp->i.filedes) { + if (__file_get_block(fp)) + return nout ? nout : -1; + + zs->next_in = (void *)fp->i.datap; + zs->avail_in = fp->i.nbytes; + } + + rv = inflate(zs, Z_SYNC_FLUSH); + + bytes = n - zs->avail_out; + nout += bytes; + p += bytes; + n -= bytes; + + switch (rv) { + case Z_DATA_ERROR: + case Z_NEED_DICT: + case Z_BUF_ERROR: + case Z_STREAM_ERROR: + default: + errno = EIO; + return nout ? nout : -1; + case Z_MEM_ERROR: + errno = ENOMEM; + return nout ? nout : -1; + case Z_STREAM_END: + return nout; + case Z_OK: + break; + } + } + + return nout; +} + +static int gzip_file_close(struct file_info *fp) +{ + z_streamp zs = fp->i.pvt; + + inflateEnd(zs); + free(zs); + return __file_close(fp); +} + +int zopen(const char *pathname, int flags, ...) +{ + int fd, rv; + struct file_info *fp; + + /* We don't actually give a hoot about the creation bits... */ + fd = open(pathname, flags, 0); + + if ( fd < 0 ) + return -1; + + fp = &__file_info[fd]; + + /* Need to get the first block into the buffer, but not consumed */ + if ( __file_get_block(fp) ) + goto err; + + if (fp->i.nbytes >= 14 && + (uint8_t)fp->i.buf[0] == 037 && + (uint8_t)fp->i.buf[1] == 0213 && /* gzip */ + fp->i.buf[2] == 8) /* deflate */ + rv = gzip_file_init(fp); + else + rv = 0; /* Plain file */ + + if (!rv) + return fd; + + err: + close(fd); + return -1; +} diff --git a/com32/lib/sys/zfopen.c b/com32/lib/sys/zfopen.c new file mode 100644 index 0000000..dfd45de --- /dev/null +++ b/com32/lib/sys/zfopen.c @@ -0,0 +1,44 @@ +/* + * zfopen.c + */ + +#include +#include +#include +#include + +FILE *zfopen(const char *file, const char *mode) +{ + int flags = O_RDONLY; + int plus = 0; + int fd; + + while ( *mode ) { + switch ( *mode ) { + case 'r': + flags = O_RDONLY; + break; + case 'w': + flags = O_WRONLY|O_CREAT|O_TRUNC; + break; + case 'a': + flags = O_WRONLY|O_CREAT|O_APPEND; + break; + case '+': + plus = 1; + break; + } + mode++; + } + + if ( plus ) { + flags = (flags & ~(O_RDONLY|O_WRONLY)) | O_RDWR; + } + + fd = zopen(file, flags, 0666); + + if ( fd < 0 ) + return NULL; + else + return fdopen(fd, mode); +} diff --git a/com32/lib/syslinux/addlist.c b/com32/lib/syslinux/addlist.c new file mode 100644 index 0000000..0375511 --- /dev/null +++ b/com32/lib/syslinux/addlist.c @@ -0,0 +1,45 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#include +#include + +int syslinux_add_movelist(struct syslinux_movelist **list, + addr_t dst, addr_t src, addr_t len) +{ + struct syslinux_movelist *ml = malloc(sizeof(struct syslinux_movelist)); + if (!ml) + return -1; + + ml->dst = dst; + ml->src = src; + ml->len = len; + ml->next = *list; + + *list = ml; + return 0; +} diff --git a/com32/lib/syslinux/adv.c b/com32/lib/syslinux/adv.c new file mode 100644 index 0000000..c782cc6 --- /dev/null +++ b/com32/lib/syslinux/adv.c @@ -0,0 +1,49 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux/adv.c + * + * Access the syslinux auxilliary data vector + */ + +#include +#include +#include + +void *__syslinux_adv_ptr; +size_t __syslinux_adv_size; + +void __constructor __syslinux_get_adv(void) +{ + static com32sys_t reg; + + reg.eax.w[0] = 0x001c; + __intcall(0x22, ®, ®); + __syslinux_adv_ptr = MK_PTR(reg.es, reg.ebx.w[0]); + __syslinux_adv_size = reg.ecx.w[0]; +} diff --git a/com32/lib/syslinux/advwrite.c b/com32/lib/syslinux/advwrite.c new file mode 100644 index 0000000..7a65dc7 --- /dev/null +++ b/com32/lib/syslinux/advwrite.c @@ -0,0 +1,45 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux/advwrite.c + * + * Write back the ADV + */ + +#include +#include +#include + +int syslinux_adv_write(void) +{ + static com32sys_t reg; + + reg.eax.w[0] = 0x001d; + __intcall(0x22, ®, ®); + return (reg.eflags.l & EFLAGS_CF) ? -1 : 0; +} diff --git a/com32/lib/syslinux/cleanup.c b/com32/lib/syslinux/cleanup.c new file mode 100644 index 0000000..9400222 --- /dev/null +++ b/com32/lib/syslinux/cleanup.c @@ -0,0 +1,40 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include + +void syslinux_final_cleanup(uint16_t flags) +{ + static com32sys_t ireg; + + ireg.eax.w[0] = 0x000c; + ireg.edx.w[0] = flags; + + __intcall(0x22, &ireg, NULL); +} diff --git a/com32/lib/syslinux/config.c b/com32/lib/syslinux/config.c new file mode 100644 index 0000000..af90752 --- /dev/null +++ b/com32/lib/syslinux/config.c @@ -0,0 +1,41 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include + +const char *__syslinux_config_file; + +void __constructor __syslinux_get_config_file_name(void) +{ + static com32sys_t reg; + + reg.eax.w[0] = 0x000e; + __intcall(0x22, ®, ®); + __syslinux_config_file = MK_PTR(reg.es, reg.ebx.w[0]); +} diff --git a/com32/lib/syslinux/dsinfo.c b/com32/lib/syslinux/dsinfo.c new file mode 100644 index 0000000..60f7e99 --- /dev/null +++ b/com32/lib/syslinux/dsinfo.c @@ -0,0 +1,45 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include + +union syslinux_derivative_info __syslinux_derivative_info; + +void __constructor __syslinux_get_derivative_info(void) +{ + com32sys_t * const r = &__syslinux_derivative_info.rr.r; + + r->eax.w[0] = 0x000A; + __intcall(0x22, r, r); + + __syslinux_derivative_info.r.esbx = MK_PTR(r->es, r->ebx.w[0]); + __syslinux_derivative_info.r.fssi = MK_PTR(r->fs, r->esi.w[0]); + __syslinux_derivative_info.r.gsdi = MK_PTR(r->gs, r->edi.w[0]); +} diff --git a/com32/lib/syslinux/dump_mmap.c b/com32/lib/syslinux/dump_mmap.c new file mode 100644 index 0000000..90ab840 --- /dev/null +++ b/com32/lib/syslinux/dump_mmap.c @@ -0,0 +1,48 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * dump_mmap.c + * + * Writes a syslinux_memmap out to a specified file. This is + * intended for debugging. + */ + +#include +#include + +void syslinux_dump_memmap(FILE *file, struct syslinux_memmap *memmap) +{ + fprintf(file, "%10s %10s %10s\n" + "--------------------------------\n", + "Start", "Length", "Type"); + while (memmap->next) { + fprintf(file, "0x%08x 0x%08x %10d\n", memmap->start, + memmap->next->start - memmap->start, memmap->type); + memmap = memmap->next; + } +} diff --git a/com32/lib/syslinux/dump_movelist.c b/com32/lib/syslinux/dump_movelist.c new file mode 100644 index 0000000..d0b19b9 --- /dev/null +++ b/com32/lib/syslinux/dump_movelist.c @@ -0,0 +1,47 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * dump_movelist.c + * + * Writes a syslinux_movelist out to a specified file. This is + * intended for debugging. + */ + +#include +#include + +void syslinux_dump_movelist(FILE *file, struct syslinux_movelist *ml) +{ + fprintf(file, "%10s %10s %10s\n" + "--------------------------------\n", + "Dest", "Src", "Length"); + while (ml) { + fprintf(file, "0x%08x 0x%08x 0x%08x\n", ml->dst, ml->src, ml->len); + ml = ml->next; + } +} diff --git a/com32/lib/syslinux/features.c b/com32/lib/syslinux/features.c new file mode 100644 index 0000000..b4d79f7 --- /dev/null +++ b/com32/lib/syslinux/features.c @@ -0,0 +1,51 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux/features.c + * + * SYSLINUX feature flag query + */ + +#include +#include +#include +#include + +struct __syslinux_feature_flags __syslinux_feature_flags; + +void __constructor __syslinux_detect_features(void) +{ + static com32sys_t reg; + + memset(®, 0, sizeof reg); + reg.eax.w[0] = 0x0015; + __intcall(0x22, ®, ®); + + __syslinux_feature_flags.len = reg.ecx.w[0]; + __syslinux_feature_flags.ptr = MK_PTR(reg.es, reg.ebx.w[0]); +} diff --git a/com32/lib/syslinux/floadfile.c b/com32/lib/syslinux/floadfile.c new file mode 100644 index 0000000..ea10829 --- /dev/null +++ b/com32/lib/syslinux/floadfile.c @@ -0,0 +1,108 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2005-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * floadfile.c + * + * Read the contents of a data file into a malloc'd buffer + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define INCREMENTAL_CHUNK 1024*1024 + +int floadfile(FILE *f, void **ptr, size_t *len, const void *prefix, + size_t prefix_len) +{ + struct stat st; + void *data, *dp; + size_t alen, clen, rlen, xlen; + + clen = alen = 0; + data = NULL; + + if ( fstat(fileno(f), &st) ) + goto err; + + if (!S_ISREG(st.st_mode)) { + /* Not a regular file, we can't assume we know the file size */ + if (prefix_len) { + clen = alen = prefix_len; + data = malloc(prefix_len); + if (!data) + goto err; + + memcpy(data, prefix, prefix_len); + } + + do { + alen += INCREMENTAL_CHUNK; + dp = realloc(data, alen); + if (!dp) + goto err; + data = dp; + + rlen = fread((char *)data+clen, 1, alen-clen, f); + clen += rlen; + } while (clen == alen); + + *len = clen; + xlen = (clen + LOADFILE_ZERO_PAD-1) & ~(LOADFILE_ZERO_PAD-1); + dp = realloc(data, xlen); + if (dp) + data = dp; + *ptr = data; + } else { + *len = clen = st.st_size + prefix_len - ftell(f); + xlen = (clen + LOADFILE_ZERO_PAD-1) & ~(LOADFILE_ZERO_PAD-1); + + *ptr = data = malloc(xlen); + if ( !data ) + return -1; + + memcpy(data, prefix, prefix_len); + + if ( (off_t)fread((char *)data+prefix_len, 1, clen-prefix_len, f) + != clen-prefix_len ) + goto err; + } + + memset((char *)data + clen, 0, xlen-clen); + return 0; + + err: + if (data) + free(data); + return -1; +} diff --git a/com32/lib/syslinux/freelist.c b/com32/lib/syslinux/freelist.c new file mode 100644 index 0000000..304e12e --- /dev/null +++ b/com32/lib/syslinux/freelist.c @@ -0,0 +1,46 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * freelist.c + * + * Frees a syslinux_movelist + */ + +#include +#include + +void syslinux_free_movelist(struct syslinux_movelist *list) +{ + struct syslinux_movelist *m; + + while (list) { + m = list; + list = list->next; + free(m); + } +} diff --git a/com32/lib/syslinux/getadv.c b/com32/lib/syslinux/getadv.c new file mode 100644 index 0000000..3cb1785 --- /dev/null +++ b/com32/lib/syslinux/getadv.c @@ -0,0 +1,68 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux/getadv.c + * + * Get a data item from the auxilliary data vector. Returns a pointer + * and sets *size on success; NULL on failure. + */ + +#include +#include +#include + +const void *syslinux_getadv(int tag, size_t *size) +{ + const uint8_t *p; + size_t left, len; + + p = syslinux_adv_ptr(); + left = syslinux_adv_size(); + + while (left >= 2) { + uint8_t ptag = *p++; + size_t plen = *p++; + left -= 2; + + if (ptag == ADV_END) + return NULL; /* Not found */ + + if (left < plen) + return NULL; /* Item overrun */ + + if (ptag == tag) { + *size = plen; + return p; + } + + p += plen; + left -= plen; + } + + return NULL; +} diff --git a/com32/lib/syslinux/idle.c b/com32/lib/syslinux/idle.c new file mode 100644 index 0000000..93f6f6b --- /dev/null +++ b/com32/lib/syslinux/idle.c @@ -0,0 +1,57 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2005-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * idle.c + * + * What to do in a busy loop... + */ + +#include +#include +#include +#include + +void syslinux_idle(void) +{ + static int do_idle = 1; + static const com32sys_t sys_idle = { + .eax.l = 0x0013, + }; + com32sys_t idle_result; + + /* This call isn't supported on SYSLINUX < 3.08, but all it does + is return an error, so we don't care. */ + + if ( do_idle ) { + __intcall(0x22, &sys_idle, &idle_result); + + do_idle = ~idle_result.eflags.l & EFLAGS_CF; + } + + cpu_relax(); +} diff --git a/com32/lib/syslinux/initramfs.c b/com32/lib/syslinux/initramfs.c new file mode 100644 index 0000000..bf4cf0b --- /dev/null +++ b/com32/lib/syslinux/initramfs.c @@ -0,0 +1,76 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * initramfs.c + * + * Utility functions for initramfs manipulation + */ + +#include +#include + +struct initramfs *initramfs_init(void) +{ + struct initramfs *ir; + + ir = calloc(sizeof(*ir), 1); + if (!ir) + return NULL; + + ir->prev = ir->next = ir; + return ir; +} + +int initramfs_add_data(struct initramfs *ihead, const void *data, + size_t data_len, size_t len, size_t align) +{ + struct initramfs *in; + + if (!len) + return 0; /* Nothing to add... */ + + /* Alignment must be a power of 2, and <= INITRAMFS_MAX_ALIGN */ + if (!align || (align & (align-1)) || align > INITRAMFS_MAX_ALIGN) + return -1; + + in = malloc(sizeof(*in)); + if (!in) + return -1; + + in->len = len; + in->data = data; + in->data_len = data_len; + in->align = align; + + in->next = ihead; + in->prev = ihead->prev; + ihead->prev->next = in; + ihead->prev = in; + + return 0; +} diff --git a/com32/lib/syslinux/initramfs_archive.c b/com32/lib/syslinux/initramfs_archive.c new file mode 100644 index 0000000..4d4760c --- /dev/null +++ b/com32/lib/syslinux/initramfs_archive.c @@ -0,0 +1,47 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * initramfs_archive.c + * + * Utility function to load an initramfs archive. + */ + +#include +#include +#include + +int initramfs_load_archive(struct initramfs *ihead, const char *filename) +{ + void *data; + size_t len; + + if (loadfile(filename, &data, &len)) + return -1; + + return initramfs_add_data(ihead, data, len, len, 4); +} diff --git a/com32/lib/syslinux/initramfs_file.c b/com32/lib/syslinux/initramfs_file.c new file mode 100644 index 0000000..8f8eda6 --- /dev/null +++ b/com32/lib/syslinux/initramfs_file.c @@ -0,0 +1,163 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * initramfs_file.c + * + * Utility functions to add arbitrary files including cpio header + */ + +#include +#include +#include +#include +#include + +#define CPIO_MAGIC "070701" +struct cpio_header { + char c_magic[6]; /* 070701 */ + char c_ino[8]; /* Inode number */ + char c_mode[8]; /* File mode and permissions */ + char c_uid[8]; /* uid */ + char c_gid[8]; /* gid */ + char c_nlink[8]; /* Number of links */ + char c_mtime[8]; /* Modification time */ + char c_filesize[8]; /* Size of data field */ + char c_maj[8]; /* File device major number */ + char c_min[8]; /* File device minor number */ + char c_rmaj[8]; /* Device node reference major number */ + char c_rmin[8]; /* Device node reference minor number */ + char c_namesize[8]; /* Length of filename including final \0 */ + char c_chksum[8]; /* Checksum if c_magic ends in 2 */ +}; + +static uint32_t next_ino = 1; + +/* Create cpio headers for the directory entries leading up to a file. + Returns the number of bytes; doesn't touch the buffer if too small. */ +static size_t initramfs_mkdirs(const char *filename, void *buffer, + size_t buflen) +{ + const char *p = filename; + char *bp = buffer; + int len; + size_t bytes = 0; + int pad; + + while ((p = strchr(p, '/'))) { + if (p != filename && p[-1] != '/') { + len = p-filename; + bytes += ((sizeof(struct cpio_header)+len+1)+3) & ~3; + } + p++; + } + + if (buflen >= bytes) { + p = filename; + while ((p = strchr(p, '/'))) { + if (p != filename && p[-1] != '/') { + len = p-filename; + bp += sprintf(bp, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x" + "%08x%08x%08x%08x", next_ino++, S_IFDIR|0755, + 0, 0, 1, 0, 0, 0, 1, 0, 1, len+1, 0); + memcpy(bp, filename, len); + bp += len; + pad = (-(sizeof(struct cpio_header)+len) & 3) + 1; + memset(bp, 0, pad); + bp += pad; + } + } + } + + return bytes; +} + +/* + * Create a file header (with optional parent directory entries) + * and add it to an initramfs chain + */ +int initramfs_mknod(struct initramfs *ihead, const char *filename, + int do_mkdir, + uint16_t mode, size_t len, + uint32_t major, uint32_t minor) +{ + size_t bytes; + int namelen = strlen(filename); + int pad; + char *buffer, *bp; + + if (do_mkdir) + bytes = initramfs_mkdirs(filename, NULL, 0); + else + bytes = 0; + + bytes += ((sizeof(struct cpio_header)+namelen+1)+3) & ~3; + + bp = buffer = malloc(bytes); + if (!buffer) + return -1; + + if (do_mkdir) + bp += initramfs_mkdirs(filename, bp, bytes); + + bp += sprintf(bp, "070701%08x%08x%08x%08x%08x%08x%08x%08x%08x" + "%08x%08x%08x%08x", next_ino++, mode, + 0, 0, 1, 0, len, 0, 1, major, minor, namelen+1, 0); + memcpy(bp, filename, namelen); + bp += len; + pad = (-(sizeof(struct cpio_header)+namelen) & 3) + 1; + memset(bp, 0, pad); + + if (initramfs_add_data(ihead, buffer, bytes, bytes, 4)) { + free(buffer); + return -1; + } + + return 0; +} + +/* + * Add a file given data in memory to an initramfs chain. This + * can be used to create nonfiles like symlinks by specifying an + * appropriate mode. + */ +int initramfs_add_file(struct initramfs *ihead, const void *data, + size_t data_len, size_t len, + const char *filename, int do_mkdir, uint32_t mode) +{ + if (initramfs_mknod(ihead, filename, do_mkdir, + (mode & S_IFMT) ? mode : mode|S_IFREG, + len, 0, 1)) + return -1; + + return initramfs_add_data(ihead, data, data_len, len, 4); +} + +int initramfs_add_trailer(struct initramfs *ihead) +{ + return initramfs_mknod(ihead, "TRAILER!!!", 0, 0, 0, 0, 0); +} diff --git a/com32/lib/syslinux/initramfs_loadfile.c b/com32/lib/syslinux/initramfs_loadfile.c new file mode 100644 index 0000000..fee4d4f --- /dev/null +++ b/com32/lib/syslinux/initramfs_loadfile.c @@ -0,0 +1,48 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * initramfs_loadfile.c + * + * Load a single file into an initramfs image. + */ + +#include +#include + +int initramfs_load_file(struct initramfs *ihead, const char *src_filename, + const char *dst_filename, int do_mkdir, uint32_t mode) +{ + void *data; + size_t len; + + if (loadfile(src_filename, &data, &len)) + return -1; + + return initramfs_add_file(ihead, data, len, len, dst_filename, + do_mkdir, mode); +} diff --git a/com32/lib/syslinux/ipappend.c b/com32/lib/syslinux/ipappend.c new file mode 100644 index 0000000..1d57e00 --- /dev/null +++ b/com32/lib/syslinux/ipappend.c @@ -0,0 +1,58 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux/ipappend.c + * + * Get ipappend strings + */ + +#include +#include +#include + +struct syslinux_ipappend_strings __syslinux_ipappend_strings; +static const char *syslinux_ipappend_string_list[32]; + +void __constructor __syslinux_get_ipappend_strings(void) +{ + static com32sys_t reg; + int i; + + reg.eax.w[0] = 0x000f; + __intcall(0x22, ®, ®); + + if (!(reg.eflags.l & EFLAGS_CF)) { + __syslinux_ipappend_strings.count = reg.ecx.w[0]; + __syslinux_ipappend_strings.ptr = + syslinux_ipappend_string_list; + for (i = 0; i < reg.ecx.w[0]; i++) { + syslinux_ipappend_string_list[i] = + MK_PTR(reg.es, *(uint16_t *)MK_PTR(reg.es, reg.ebx.w[0]+i*2)); + } + } +} diff --git a/com32/lib/syslinux/keyboard.c b/com32/lib/syslinux/keyboard.c new file mode 100644 index 0000000..2b31b27 --- /dev/null +++ b/com32/lib/syslinux/keyboard.c @@ -0,0 +1,44 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#include +#include + +struct syslinux_keyboard_map __syslinux_keyboard_map; + +void __constructor __syslinux_get_keyboard_map(void) +{ + static com32sys_t reg; + + reg.eax.w[0] = 0x001e; + __intcall(0x22, ®, ®); + if (!(reg.eflags.l & EFLAGS_CF)) { + __syslinux_keyboard_map.version = reg.eax.w[0]; + __syslinux_keyboard_map.length = reg.ecx.w[0]; + __syslinux_keyboard_map.map = MK_PTR(reg.es, reg.ebx.w[0]); + } +} diff --git a/com32/lib/syslinux/load_linux.c b/com32/lib/syslinux/load_linux.c new file mode 100644 index 0000000..37d396a --- /dev/null +++ b/com32/lib/syslinux/load_linux.c @@ -0,0 +1,509 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * load_linux.c + * + * Load a Linux kernel (Image/zImage/bzImage). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef DEBUG +# define DEBUG 0 +#endif +#if DEBUG +# include +# define dprintf printf +#else +# define dprintf(f, ...) ((void)0) +#endif + +struct linux_header { + uint8_t boot_sector_1[0x0020]; + uint16_t old_cmd_line_magic; + uint16_t old_cmd_line_offset; + uint8_t boot_sector_2[0x01f1-0x0024]; + uint8_t setup_sects; + uint16_t root_flags; + uint32_t syssize; + uint16_t ram_size; + uint16_t vid_mode; + uint16_t root_dev; + uint16_t boot_flag; + uint16_t jump; + uint32_t header; + uint16_t version; + uint32_t realmode_swtch; + uint16_t start_sys; + uint16_t kernel_version; + uint8_t type_of_loader; + uint8_t loadflags; + uint16_t setup_move_size; + uint32_t code32_start; + uint32_t ramdisk_image; + uint32_t ramdisk_size; + uint32_t bootsect_kludge; + uint16_t heap_end_ptr; + uint16_t pad1; + uint32_t cmd_line_ptr; + uint32_t initrd_addr_max; + uint32_t kernel_alignment; + uint8_t relocatable_kernel; + uint8_t pad2[3]; + uint32_t cmdline_max_len; +} __packed; + +#define BOOT_MAGIC 0xAA55 +#define LINUX_MAGIC ('H' + ('d' << 8) + ('r' << 16) + ('S' << 24)) +#define OLD_CMDLINE_MAGIC 0xA33F + +/* loadflags */ +#define LOAD_HIGH 0x01 +#define CAN_USE_HEAP 0x80 + +/* Get a value with a potential suffix (k/m/g/t/p/e) */ +static unsigned long long suffix_number(const char *str) +{ + char *ep; + unsigned long long v; + int shift; + + v = strtoull(str, &ep, 0); + switch (*ep|0x20) { + case 'k': + shift = 10; + break; + case 'm': + shift = 20; + break; + case 'g': + shift = 30; + break; + case 't': + shift = 40; + break; + case 'p': + shift = 50; + break; + case 'e': + shift = 60; + break; + default: + shift = 0; + break; + } + v <<= shift; + + return v; +} + +/* + * Find the last instance of a particular command line argument + * (which should include the final =; do not use for boolean arguments) + * Note: the resulting string is typically not null-terminated. + */ +static const char *find_argument(const char *cmdline, const char *argument) +{ + const char *found = NULL; + const char *p = cmdline; + bool was_space = true; + size_t la = strlen(argument); + + while (*p) { + if (isspace(*p)) { + was_space = true; + } else if (was_space) { + if (!memcmp(p, argument, la)) + found = p+la; + was_space = false; + } + p++; + } + + return found; +} + +/* Truncate to 32 bits, with saturate */ +static inline uint32_t saturate32(unsigned long long v) +{ + return (v > 0xffffffff) ? 0xffffffff : (uint32_t)v; +} + +/* Get the combined size of the initramfs */ +static addr_t initramfs_size(struct initramfs *initramfs) +{ + struct initramfs *ip; + addr_t size = 0; + + if (!initramfs) + return 0; + + for (ip = initramfs->next; ip->len; ip = ip->next) { + size = (size+ip->align-1) & ~(ip->align-1); /* Alignment */ + size += ip->len; + } + + return size; +} + +/* Create the appropriate mappings for the initramfs */ +static int map_initramfs(struct syslinux_movelist **fraglist, + struct syslinux_memmap **mmap, + struct initramfs *initramfs, + addr_t addr) +{ + struct initramfs *ip; + addr_t next_addr, len, pad; + + for (ip = initramfs->next; ip->len; ip = ip->next) { + len = ip->len; + next_addr = addr+len; + + /* If this isn't the last entry, extend the zero-pad region + to enforce the alignment of the next chunk. */ + if (ip->next->len) { + pad = -next_addr & (ip->next->align-1); + len += pad; + next_addr += pad; + } + + if (ip->data_len) { + if (syslinux_add_movelist(fraglist, addr, (addr_t)ip->data, len)) + return -1; + } + if (len > ip->data_len) { + if (syslinux_add_memmap(mmap, addr+ip->data_len, + len-ip->data_len, SMT_ZERO)) + return -1; + } + addr = next_addr; + } + + return 0; +} + +int syslinux_boot_linux(void *kernel_buf, size_t kernel_size, + struct initramfs *initramfs, char *cmdline) +{ + struct linux_header hdr, *whdr; + size_t real_mode_size, prot_mode_size; + addr_t real_mode_base, prot_mode_base; + addr_t irf_size; + size_t cmdline_size, cmdline_offset; + struct syslinux_rm_regs regs; + struct syslinux_movelist *fraglist = NULL; + struct syslinux_memmap *mmap = NULL; + struct syslinux_memmap *amap = NULL; + bool ok; + uint32_t memlimit = 0; + uint16_t video_mode = 0; + const char *arg; + + cmdline_size = strlen(cmdline)+1; + + if (kernel_size < 2*512) + goto bail; + + /* Look for specific command-line arguments we care about */ + if ((arg = find_argument(cmdline, "mem="))) + memlimit = saturate32(suffix_number(arg)); + + if ((arg = find_argument(cmdline, "vga="))) { + switch (arg[0] | 0x20) { + case 'a': /* "ask" */ + video_mode = 0xfffd; + break; + case 'e': /* "ext" */ + video_mode = 0xfffe; + break; + case 'n': /* "normal" */ + video_mode = 0xffff; + break; + default: + video_mode = strtoul(arg, NULL, 0); + break; + } + } + + /* Copy the header into private storage */ + /* Use whdr to modify the actual kernel header */ + memcpy(&hdr, kernel_buf, sizeof hdr); + whdr = (struct linux_header *)kernel_buf; + + if (hdr.boot_flag != BOOT_MAGIC) + goto bail; + + if (hdr.header != LINUX_MAGIC) { + hdr.version = 0x0100; /* Very old kernel */ + hdr.loadflags = 0; + } + + whdr->vid_mode = video_mode; + + if (!hdr.setup_sects) + hdr.setup_sects = 4; + + if (hdr.version < 0x0203) + hdr.initrd_addr_max = 0x37ffffff; + + if (!memlimit && memlimit-1 > hdr.initrd_addr_max) + memlimit = hdr.initrd_addr_max+1; /* Zero for no limit */ + + if (hdr.version < 0x0205 || !(hdr.loadflags & LOAD_HIGH)) + hdr.relocatable_kernel = 0; + + if (hdr.version < 0x0206) + hdr.cmdline_max_len = 256; + + if (cmdline_size > hdr.cmdline_max_len) { + cmdline_size = hdr.cmdline_max_len; + cmdline[cmdline_size-1] = '\0'; + } + + if (hdr.version < 0x0202 || !(hdr.loadflags & 0x01)) + cmdline_offset = (0x9ff0 - cmdline_size) & ~15; + else + cmdline_offset = 0x10000; + + real_mode_size = (hdr.setup_sects+1) << 9; + real_mode_base = (hdr.loadflags & LOAD_HIGH) ? 0x10000 : 0x90000; + prot_mode_base = (hdr.loadflags & LOAD_HIGH) ? 0x100000 : 0x10000; + prot_mode_size = kernel_size - real_mode_size; + + if (!(hdr.loadflags & LOAD_HIGH) && prot_mode_size > 512*1024) + goto bail; /* Kernel cannot be loaded low */ + + if (initramfs && hdr.version < 0x0200) + goto bail; /* initrd/initramfs not supported */ + + if (hdr.version >= 0x0200) { + whdr->type_of_loader = 0x30; /* SYSLINUX unknown module */ + if (hdr.version >= 0x0201) { + whdr->heap_end_ptr = cmdline_offset - 0x0200; + whdr->loadflags |= CAN_USE_HEAP; + } + if (hdr.version >= 0x0202) { + whdr->cmd_line_ptr = real_mode_base+cmdline_offset; + } else { + whdr->old_cmd_line_magic = OLD_CMDLINE_MAGIC; + whdr->old_cmd_line_offset = cmdline_offset; + /* Be paranoid and round up to a multiple of 16 */ + whdr->setup_move_size = (cmdline_offset+cmdline_size+15) & ~15; + } + } + + /* Get the memory map */ + mmap = syslinux_memory_map(); /* Memory map for shuffle_boot */ + amap = syslinux_dup_memmap(mmap); /* Keep track of available memory */ + if (!mmap || !amap) + goto bail; + +#if DEBUG + dprintf("Initial memory map:\n"); + syslinux_dump_memmap(stdout, mmap); +#endif + + /* If the user has specified a memory limit, mark that as unavailable. + Question: should we mark this off-limit in the mmap as well (meaning + it's unavailable to the boot loader, which probably has already touched + some of it), or just in the amap? */ + if (memlimit) + if (syslinux_add_memmap(&amap, memlimit, -memlimit, SMT_RESERVED)) + goto bail; + + /* Place the kernel in memory */ + + /* First, find a suitable place for the protected-mode code */ + if (syslinux_memmap_type(amap, prot_mode_base, prot_mode_size) + != SMT_FREE) { + const struct syslinux_memmap *mp; + if (!hdr.relocatable_kernel) + goto bail; /* Can't relocate - no hope */ + + ok = false; + for (mp = amap; mp; mp = mp->next) { + addr_t start, end; + start = mp->start; + end = mp->next->start; + + if (mp->type != SMT_FREE) + continue; + + if (end <= prot_mode_base) + continue; /* Only relocate upwards */ + + if (start <= prot_mode_base) + start = prot_mode_base; + + start = ALIGN_UP(start, hdr.kernel_alignment); + if (start >= end) + continue; + + /* The 3* here is a total fudge factor... it's supposed to + account for the fact that the kernel needs to be decompressed, + and then followed by the BSS and BRK regions. This doesn't, + however, account for the fact that the kernel is decompressed + into a whole other place, either. */ + if (end - start >= 3*prot_mode_size) { + whdr->code32_start += start - prot_mode_base; + prot_mode_base = start; + ok = true; + break; + } + } + + if (!ok) + goto bail; + } + + /* Real mode code */ + if (syslinux_memmap_type(amap, real_mode_base, + cmdline_offset+cmdline_size) != SMT_FREE) { + const struct syslinux_memmap *mp; + + ok = false; + for (mp = amap; mp; mp = mp->next) { + addr_t start, end; + start = mp->start; + end = mp->next->start; + + if (mp->type != SMT_FREE) + continue; + + if (start < real_mode_base) + start = real_mode_base; /* Lowest address we'll use */ + if (end > 640*1024) + end = 640*1024; + + start = ALIGN_UP(start, 16); + if (start > 0x90000 || start >= end) + continue; + + if (end - start >= cmdline_offset+cmdline_size) { + real_mode_base = start; + ok = true; + break; + } + } + } + + if (syslinux_add_movelist(&fraglist, real_mode_base, (addr_t)kernel_buf, + real_mode_size)) + goto bail; + if (syslinux_add_memmap(&amap, real_mode_base, cmdline_offset+cmdline_size, + SMT_ALLOC)) + goto bail; + + /* Zero region between real mode code and cmdline */ + if (syslinux_add_memmap(&mmap, real_mode_base+real_mode_size, + cmdline_offset-real_mode_size, SMT_ZERO)) + goto bail; + + /* Command line */ + if (syslinux_add_movelist(&fraglist, real_mode_base+cmdline_offset, + (addr_t)cmdline, cmdline_size)) + goto bail; + + /* Protected-mode code */ + if (syslinux_add_movelist(&fraglist, prot_mode_base, + (addr_t)kernel_buf + real_mode_size, + prot_mode_size)) + goto bail; + if (syslinux_add_memmap(&amap, prot_mode_base, prot_mode_size, SMT_ALLOC)) + goto bail; + + /* Figure out the size of the initramfs, and where to put it. + We should put it at the highest possible address which is + <= hdr.initrd_addr_max, which fits the entire initramfs. */ + + irf_size = initramfs_size(initramfs); /* Handles initramfs == NULL */ + + if (irf_size) { + addr_t best_addr = 0; + struct syslinux_memmap *ml; + const addr_t align_mask = INITRAMFS_MAX_ALIGN-1; + + if (irf_size) { + for (ml = amap; ml->type != SMT_END; ml = ml->next) { + addr_t adj_start = (ml->start+align_mask) & ~align_mask; + addr_t adj_end = ml->next->start & ~align_mask; + if (ml->type == SMT_FREE && adj_end-adj_start >= irf_size) + best_addr = (adj_end - irf_size) & ~align_mask; + } + + if (!best_addr) + goto bail; /* Insufficient memory for initramfs */ + + whdr->ramdisk_image = best_addr; + whdr->ramdisk_size = irf_size; + + if (syslinux_add_memmap(&amap, best_addr, irf_size, SMT_ALLOC)) + goto bail; + + if (map_initramfs(&fraglist, &mmap, initramfs, best_addr)) + goto bail; + } + } + + /* Set up the registers on entry */ + memset(®s, 0, sizeof regs); + regs.es = regs.ds = regs.ss = regs.fs = regs.gs = real_mode_base >> 4; + regs.cs = (real_mode_base >> 4)+0x20; + /* regs.ip = 0; */ + /* Linux is OK with sp = 0 = 64K, but perhaps other things aren't... */ + regs.esp.w[0] = min(cmdline_offset, (size_t)0xfff0); + +#if DEBUG + dprintf("Final memory map:\n"); + syslinux_dump_memmap(stdout, mmap); + + dprintf("Final available map:\n"); + syslinux_dump_memmap(stdout, amap); + + dprintf("Initial movelist:\n"); + syslinux_dump_movelist(stdout, fraglist); +#endif + + syslinux_shuffle_boot_rm(fraglist, mmap, 0, ®s); + + bail: + syslinux_free_movelist(fraglist); + syslinux_free_memmap(mmap); + syslinux_free_memmap(amap); + return -1; +} diff --git a/com32/lib/syslinux/loadfile.c b/com32/lib/syslinux/loadfile.c new file mode 100644 index 0000000..7ad8e81 --- /dev/null +++ b/com32/lib/syslinux/loadfile.c @@ -0,0 +1,63 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2005-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * loadfile.c + * + * Read the contents of a data file into a malloc'd buffer + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define INCREMENTAL_CHUNK 1024*1024 + +int loadfile(const char *filename, void **ptr, size_t *len) +{ + FILE *f; + int rv, e; + + f = fopen(filename, "r"); + if ( !f ) + return -1; + + rv = floadfile(f, ptr, len, NULL, 0); + e = errno; + + fclose(f); + + if (rv) + errno = e; + return rv; +} diff --git a/com32/lib/syslinux/localboot.c b/com32/lib/syslinux/localboot.c new file mode 100644 index 0000000..069808d --- /dev/null +++ b/com32/lib/syslinux/localboot.c @@ -0,0 +1,42 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include + +/* This returns only on failure */ + +void syslinux_local_boot(uint16_t flags) +{ + static com32sys_t ireg; + + ireg.eax.w[0] = 0x0014; + ireg.edx.w[0] = flags; + + __intcall(0x22, &ireg, NULL); +} diff --git a/com32/lib/syslinux/memmap.c b/com32/lib/syslinux/memmap.c new file mode 100644 index 0000000..170168b --- /dev/null +++ b/com32/lib/syslinux/memmap.c @@ -0,0 +1,65 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * memmap.c + * + * Create initial memory map for "shuffle and boot" operation + */ + +#include +#include +#include +#include + +#include +#include + +static int syslinux_memory_map_callback(void *map, addr_t start, + addr_t len, bool valid) +{ + struct syslinux_memmap **mmap = map; + return syslinux_add_memmap(mmap, start, len, + valid ? SMT_FREE : SMT_RESERVED); +} + +struct syslinux_memmap *syslinux_memory_map(void) +{ + struct syslinux_memmap *mmap; + + mmap = syslinux_init_memmap(); + if (!mmap) + return NULL; + + if (syslinux_scan_memory(syslinux_memory_map_callback, &mmap)) { + syslinux_free_memmap(mmap); + return NULL; + } + + return mmap; +} diff --git a/com32/lib/syslinux/memscan.c b/com32/lib/syslinux/memscan.c new file mode 100644 index 0000000..0743920 --- /dev/null +++ b/com32/lib/syslinux/memscan.c @@ -0,0 +1,153 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * memscan.c + * + * Query the system for free memory + */ + +#include +#include +#include +#include +#include +#include + +#include + +struct e820_entry { + uint64_t start; + uint64_t len; + uint32_t type; +}; + +int syslinux_scan_memory(scan_memory_callback_t callback, void *data) +{ + static com32sys_t ireg; + com32sys_t oreg; + struct e820_entry *e820buf = __com32.cs_bounce; + uint64_t start, len, maxlen; + int memfound = 0; + int rv; + addr_t dosmem; + const addr_t bios_data = 0x510; /* Amount to reserve for BIOS data */ + + /* Use INT 12h to get DOS memory */ + __intcall(0x12, &__com32_zero_regs, &oreg); + dosmem = oreg.eax.w[0] << 10; + if (dosmem < 32*1024 || dosmem > 640*1024) { + /* INT 12h reports nonsense... now what? */ + uint16_t ebda_seg = *(uint16_t *)0x40e; + if (ebda_seg >= 0x8000 && ebda_seg < 0xa000) + dosmem = ebda_seg << 4; + else + dosmem = 640*1024; /* Hope for the best... */ + } + rv = callback(data, bios_data, dosmem-bios_data, true); + if (rv) + return rv; + + /* First try INT 15h AX=E820h */ + ireg.eax.l = 0xe820; + ireg.edx.l = 0x534d4150; + ireg.ebx.l = 0; + ireg.ecx.l = sizeof(*e820buf); + ireg.es = SEG(e820buf); + ireg.edi.w[0] = OFFS(e820buf); + memset(e820buf, 0, sizeof *e820buf); + + do { + __intcall(0x15, &ireg, &oreg); + + if ((oreg.eflags.l & EFLAGS_CF) || + (oreg.eax.l != 0x534d4150) || + (oreg.ecx.l < 20)) + break; + + start = e820buf->start; + len = e820buf->len; + + if (start < 0x100000000ULL) { + /* Don't rely on E820 being valid for low memory. Doing so + could mean stuff like overwriting the PXE stack even when + using "keeppxe", etc. */ + if (start < 0x100000ULL) { + if (len > 0x100000ULL-start) + len -= 0x100000ULL-start; + else + len = 0; + start = 0x100000ULL; + } + + maxlen = 0x100000000ULL-start; + if (len > maxlen) + len = maxlen; + + if (len) { + rv = callback(data, (addr_t)start, (addr_t)len, e820buf->type == 1); + if (rv) + return rv; + memfound = 1; + } + } + + ireg.ebx.l = oreg.ebx.l; + } while (oreg.ebx.l); + + if (memfound) + return 0; + + /* Next try INT 15h AX=E801h */ + ireg.eax.w[0] = 0xe801; + __intcall(0x15, &ireg, &oreg); + + if (!(oreg.eflags.l & EFLAGS_CF) && oreg.ecx.w[0]) { + rv = callback(data, (addr_t)1 << 20, oreg.ecx.w[0] << 10, true); + if (rv) + return rv; + + if (oreg.edx.w[0]) { + rv = callback(data, (addr_t)16 << 20, oreg.edx.w[0] << 16, true); + if (rv) + return rv; + } + + return 0; + } + + /* Finally try INT 15h AH=88h */ + ireg.eax.w[0] = 0x8800; + if (!(oreg.eflags.l & EFLAGS_CF) && oreg.eax.w[0]) { + rv = callback(data, (addr_t)1 << 20, oreg.ecx.w[0] << 10, true); + if (rv) + return rv; + } + + return 0; +} diff --git a/com32/lib/syslinux/movebits.c b/com32/lib/syslinux/movebits.c new file mode 100644 index 0000000..0da3146 --- /dev/null +++ b/com32/lib/syslinux/movebits.c @@ -0,0 +1,709 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * movebits.c + * + * Utility function to take a list of memory areas to shuffle and + * convert it to a set of shuffle operations. + * + * Note: a lot of the functions in this file deal with "parent pointers", + * which are pointers to a pointer to a list, or part of the list. + * They can be pointers to a variable holding the list root pointer, + * or pointers to a next field of a previous entry. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef DEBUG +# ifdef TEST +# define DEBUG 1 +# else +# define DEBUG 0 +# endif +#endif + +#if DEBUG +# include +# define dprintf printf +#else +# define dprintf(...) ((void)0) +#endif + +static jmp_buf new_movelist_bail; + +static struct syslinux_movelist * +new_movelist(addr_t dst, addr_t src, addr_t len) +{ + struct syslinux_movelist *ml = malloc(sizeof(struct syslinux_movelist)); + + if ( !ml ) + longjmp(new_movelist_bail, 1); + + ml->dst = dst; + ml->src = src; + ml->len = len; + ml->next = NULL; + + return ml; +} + +static struct syslinux_movelist * +dup_movelist(struct syslinux_movelist *src) +{ + struct syslinux_movelist *dst = NULL, **dstp = &dst, *ml; + + while (src) { + ml = new_movelist(src->dst, src->src, src->len); + *dstp = ml; + dstp = &ml->next; + src = src->next; + } + + return dst; +} + +static void +add_freelist(struct syslinux_memmap **mmap, addr_t start, + addr_t len, enum syslinux_memmap_types type) +{ + if (syslinux_add_memmap(mmap, start, len, type)) + longjmp(new_movelist_bail, 1); +} + +/* + * Take a chunk, entirely confined in **parentptr, and split it off so that + * it has its own structure. + */ +static struct syslinux_movelist ** +split_movelist(addr_t start, addr_t len, struct syslinux_movelist **parentptr) +{ + struct syslinux_movelist *m, *ml = *parentptr; + + assert(start >= ml->src); + assert(start < ml->src+ml->len); + + /* Split off the beginning */ + if ( start > ml->src ) { + addr_t l = start - ml->src; + + m = new_movelist(ml->dst+l, start, ml->len-l); + m->next = ml->next; + ml->len = l; + ml->next = m; + + parentptr = &ml->next; + ml = m; /* Continue processing the new node */ + } + + /* Split off the end */ + if ( ml->len > len ) { + addr_t l = ml->len - len; + + m = new_movelist(ml->dst+len, ml->src+len, l); + m->next = ml->next; + ml->len = len; + ml->next = m; + } + + return parentptr; +} + +static void +delete_movelist(struct syslinux_movelist **parentptr) +{ + struct syslinux_movelist *o = *parentptr; + *parentptr = o->next; + free(o); +} + +static void +free_movelist(struct syslinux_movelist **parentptr) +{ + while (*parentptr) + delete_movelist(parentptr); +} + +/* + * Scan the freelist looking for a particular chunk of memory + */ +static const struct syslinux_memmap * +is_free_zone(const struct syslinux_memmap *list, addr_t start, addr_t len) +{ + dprintf("f: 0x%08x bytes at 0x%08x\n", len, start); + + addr_t last, llast; + + last = start+len-1; + + while (list->type != SMT_END) { + llast = list->next->start-1; + if (list->start <= start) { + if (llast >= last) { + /* Chunk has a single, well-defined type */ + if (list->type == SMT_FREE) { + dprintf("F: 0x%08x bytes at 0x%08x\n", + list->next->start, list->start); + return list; /* It's free */ + } + return NULL; /* Not free */ + } else if (llast >= start) { + return NULL; /* Crosses region boundary */ + } + } + list = list->next; + } + + return NULL; /* Internal error? */ +} + +/* + * Scan the freelist looking for the smallest chunk of memory which + * can fit X bytes; returns the length of the block on success. + */ +static addr_t free_area(const struct syslinux_memmap *mmap, + addr_t len, addr_t *start) +{ + const struct syslinux_memmap *best = NULL; + const struct syslinux_memmap *s; + addr_t slen, best_len = -1; + + for (s = mmap; s->type != SMT_END; s = s->next) { + if (s->type != SMT_FREE) + continue; + slen = s->next->start - s->start; + if (slen >= len) { + if (!best || best_len > slen) { + best = s; + best_len = slen; + } + } + } + + if (best) { + *start = best->start; + return best_len; + } else { + return 0; + } +} + +/* + * Remove a chunk from the freelist + */ +static void +allocate_from(struct syslinux_memmap **mmap, addr_t start, addr_t len) +{ + syslinux_add_memmap(mmap, start, len, SMT_ALLOC); +} + +/* + * Find chunks of a movelist which are one-to-many (one source, multiple + * destinations.) Those chunks can get turned into post-shuffle copies, + * to avoid confusing the shuffler. + */ +static void shuffle_dealias(struct syslinux_movelist **fraglist, + struct syslinux_movelist **postcopy) +{ + struct syslinux_movelist *mp, **mpp, *mx, *np; + addr_t ps, pe, xs, xe, delta; + bool advance; + +#if DEBUG + dprintf("Before alias resolution:\n"); + syslinux_dump_movelist(stdout, *fraglist); +#endif + + *postcopy = NULL; + + /* + * Note: as written, this is an O(n^2) algorithm; by producing a list + * sorted by destination address we could reduce it to O(n log n). + */ + mpp = fraglist; + while ((mp = *mpp)) { + dprintf("mp -> (%#x,%#x,%#x)\n", mp->dst, mp->src, mp->len); + ps = mp->src; + pe = mp->src + mp->len - 1; + for (mx = *fraglist; mx != mp; mx = mx->next) { + dprintf("mx -> (%#x,%#x,%#x)\n", mx->dst, mx->src, mx->len); + /* + * If there is any overlap between mx and mp, mp should be + * modified and possibly split. + */ + xs = mx->src; + xe = mx->src + mx->len - 1; + + dprintf("?: %#x..%#x (inside %#x..%#x)\n", ps, pe, xs, xe); + + if (pe <= xs || ps >= xe) + continue; /* No overlap */ + + advance = false; + *mpp = mp->next; /* Remove from list */ + + if (pe > xe) { + delta = pe-xe; + np = new_movelist(mp->dst+mp->len-delta, mp->src+mp->len-delta, delta); + mp->len -= delta; pe = xe; + np->next = *mpp; + *mpp = np; + advance = true; + } + if (ps < xs) { + delta = xs-ps; + np = new_movelist(mp->dst, ps, delta); + mp->src += delta; ps = mp->src; + mp->dst += delta; + mp->len -= delta; + np->next = *mpp; + *mpp = np; + advance = true; + } + + assert(ps >= xs && pe <= xe); + + dprintf("Overlap: %#x..%#x (inside %#x..%#x)\n", ps, pe, xs, xe); + + mp->src = mx->dst + (ps-xs); + mp->next = *postcopy; + *postcopy = mp; + + mp = *mpp; + + if (!advance) + goto restart; + } + + mpp = &mp->next; + restart: + ; + } + +#if DEBUG + dprintf("After alias resolution:\n"); + syslinux_dump_movelist(stdout, *fraglist); + dprintf("Post-shuffle copies:\n"); + syslinux_dump_movelist(stdout, *postcopy); +#endif +} + +/* + * The code to actually emit moving of a chunk into its final place. + */ +static void +move_chunk(struct syslinux_movelist ***moves, + struct syslinux_memmap **mmap, + struct syslinux_movelist **fp, addr_t copylen) +{ + addr_t copydst, copysrc; + addr_t freebase, freelen; + addr_t needlen; + int reverse; + struct syslinux_movelist *f = *fp, *mv; + + if ( f->src < f->dst && (f->dst - f->src) < f->len ) { + /* "Shift up" type overlap */ + needlen = f->dst - f->src; + reverse = 1; + } else if ( f->src > f->dst && (f->src - f->dst) < f->len ) { + /* "Shift down" type overlap */ + needlen = f->src - f->dst; + reverse = 0; + } else { + needlen = f->len; + reverse = 0; + } + + copydst = f->dst; + copysrc = f->src; + + dprintf("Q: copylen = 0x%08x, needlen = 0x%08x\n", copylen, needlen); + + if ( copylen < needlen ) { + if (reverse) { + copydst += (f->len-copylen); + copysrc += (f->len-copylen); + } + + dprintf("X: 0x%08x bytes at 0x%08x -> 0x%08x\n", + copylen, copysrc, copydst); + + /* Didn't get all we wanted, so we have to split the chunk */ + fp = split_movelist(copysrc, copylen, fp); /* Is this right? */ + f = *fp; + } + + mv = new_movelist(f->dst, f->src, f->len); + dprintf("A: 0x%08x bytes at 0x%08x -> 0x%08x\n", + mv->len, mv->src, mv->dst); + **moves = mv; + *moves = &mv->next; + + /* Figure out what memory we just freed up */ + if ( f->dst > f->src ) { + freebase = f->src; + freelen = min(f->len, f->dst-f->src); + } else if ( f->src >= f->dst+f->len ) { + freebase = f->src; + freelen = f->len; + } else { + freelen = f->src-f->dst; + freebase = f->dst+f->len; + } + + dprintf("F: 0x%08x bytes at 0x%08x\n", freelen, freebase); + + add_freelist(mmap, freebase, freelen, SMT_FREE); + + delete_movelist(fp); +} + +/* + * moves is computed from "frags" and "freemem". "space" lists + * free memory areas at our disposal, and is (src, cnt) only. + */ +int +syslinux_compute_movelist(struct syslinux_movelist **moves, + struct syslinux_movelist *ifrags, + struct syslinux_memmap *memmap) +{ + struct syslinux_memmap *mmap = NULL; + const struct syslinux_memmap *mm, *ep; + struct syslinux_movelist *frags = NULL; + struct syslinux_movelist *postcopy = NULL; + struct syslinux_movelist *mv; + struct syslinux_movelist *f, **fp; + struct syslinux_movelist *o, **op; + addr_t needbase, needlen, copysrc, copydst, copylen; + addr_t avail; + addr_t fstart, flen; + addr_t cbyte; + addr_t ep_len; + int rv = -1; + int reverse; + + dprintf("entering syslinux_compute_movelist()...\n"); + + if (setjmp(new_movelist_bail)) { + nomem: + dprintf("Out of working memory!\n"); + goto bail; + } + + *moves = NULL; + + /* Create our memory map. Anything that is SMT_FREE or SMT_ZERO is + fair game, but mark anything used by source material as SMT_ALLOC. */ + mmap = syslinux_init_memmap(); + if (!mmap) + goto nomem; + + frags = dup_movelist(ifrags); + + /* Process one-to-many conditions */ + shuffle_dealias(&frags, &postcopy); + + for (mm = memmap; mm->type != SMT_END; mm = mm->next) + add_freelist(&mmap, mm->start, mm->next->start - mm->start, + mm->type == SMT_ZERO ? SMT_FREE : mm->type); + + for (f = frags; f; f = f->next) + add_freelist(&mmap, f->src, f->len, SMT_ALLOC); + + /* As long as there are unprocessed fragments in the chain... */ + while ( (fp = &frags, f = *fp) ) { + +#if DEBUG + dprintf("Current free list:\n"); + syslinux_dump_memmap(stdout, mmap); + dprintf("Current frag list:\n"); + syslinux_dump_movelist(stdout, frags); +#endif + + /* Scan for fragments which can be discarded without action. */ + if ( f->src == f->dst ) { + delete_movelist(fp); + continue; + } + op = &f->next; + while ( (o = *op) ) { + if ( o->src == o->dst ) + delete_movelist(op); + else + op = &o->next; + } + + /* Scan for fragments which can be immediately moved + to their final destination, if so handle them now */ + for ( op = fp; (o = *op); op = &o->next ) { + if ( o->src < o->dst && (o->dst - o->src) < o->len ) { + /* "Shift up" type overlap */ + needlen = o->dst - o->src; + needbase = o->dst + (o->len - needlen); + reverse = 1; + cbyte = o->dst + o->len - 1; + } else if ( o->src > o->dst && (o->src - o->dst) < o->len ) { + /* "Shift down" type overlap */ + needlen = o->src - o->dst; + needbase = o->dst; + reverse = 0; + cbyte = o->dst; /* "Critical byte" */ + } else { + needlen = o->len; + needbase = o->dst; + reverse = 0; + cbyte = o->dst; /* "Critical byte" */ + } + + if (is_free_zone(mmap, needbase, needlen)) { + fp = op, f = o; + dprintf("!: 0x%08x bytes at 0x%08x -> 0x%08x\n", + f->len, f->src, f->dst); + copysrc = f->src; + copylen = needlen; + allocate_from(&mmap, needbase, copylen); + goto move_chunk; + } + } + + /* Ok, bother. Need to do real work at least with one chunk. */ + + dprintf("@: 0x%08x bytes at 0x%08x -> 0x%08x\n", + f->len, f->src, f->dst); + + /* See if we can move this chunk into place by claiming + the destination, or in the case of partial overlap, the + missing portion. */ + + if ( f->src < f->dst && (f->dst - f->src) < f->len ) { + /* "Shift up" type overlap */ + needlen = f->dst - f->src; + needbase = f->dst + (f->len - needlen); + reverse = 1; + cbyte = f->dst + f->len - 1; + } else if ( f->src > f->dst && (f->src - f->dst) < f->len ) { + /* "Shift down" type overlap */ + needlen = f->src - f->dst; + needbase = f->dst; + reverse = 0; + cbyte = f->dst; /* "Critical byte" */ + } else { + needlen = f->len; + needbase = f->dst; + reverse = 0; + cbyte = f->dst; + } + + dprintf("need: base = 0x%08x, len = 0x%08x, " + "reverse = %d, cbyte = 0x%08x\n", + needbase, needlen, reverse, cbyte); + + ep = is_free_zone(mmap, cbyte, 1); + if (ep) { + ep_len = ep->next->start - ep->start; + if (reverse) + avail = needbase+needlen - ep->start; + else + avail = ep_len - (needbase - ep->start); + } else { + avail = 0; + } + + if (avail) { + /* We can move at least part of this chunk into place without + further ado */ + dprintf("space: start 0x%08x, len 0x%08x, free 0x%08x\n", + ep->start, ep_len, avail); + copylen = min(needlen, avail); + + if (reverse) + allocate_from(&mmap, needbase+needlen-copylen, copylen); + else + allocate_from(&mmap, needbase, copylen); + + goto move_chunk; + } + + /* At this point, we need to evict something out of our space. + Find the object occupying the critical byte of our target space, + and move it out (the whole object if we can, otherwise a subset.) + Then move a chunk of ourselves into place. */ + for ( op = &f->next, o = *op ; o ; op = &o->next, o = *op ) { + + dprintf("O: 0x%08x bytes at 0x%08x -> 0x%08x\n", + o->len, o->src, o->dst); + + if ( !(o->src <= cbyte && o->src+o->len > cbyte) ) + continue; /* Not what we're looking for... */ + + /* Find somewhere to put it... */ + + if ( is_free_zone(mmap, o->dst, o->len) ) { + /* Score! We can move it into place directly... */ + copydst = o->dst; + copysrc = o->src; + copylen = o->len; + } else if ( free_area(mmap, o->len, &fstart) ) { + /* We can move the whole chunk */ + copydst = fstart; + copysrc = o->src; + copylen = o->len; + } else { + /* Well, copy as much as we can... */ + if (syslinux_memmap_largest(mmap, SMT_FREE, &fstart, &flen)) { + dprintf("No free memory at all!\n"); + goto bail; /* Stuck! */ + } + + /* Make sure we include the critical byte */ + copydst = fstart; + if (reverse) { + copysrc = max(o->src, cbyte+1 - flen); + copylen = cbyte+1 - copysrc; + } else { + copysrc = cbyte; + copylen = min(flen, o->len - (cbyte-o->src)); + } + } + allocate_from(&mmap, copydst, copylen); + + if ( copylen < o->len ) { + op = split_movelist(copysrc, copylen, op); + o = *op; + } + + mv = new_movelist(copydst, copysrc, copylen); + dprintf("C: 0x%08x bytes at 0x%08x -> 0x%08x\n", + mv->len, mv->src, mv->dst); + *moves = mv; + moves = &mv->next; + + o->src = copydst; + + if ( copylen > needlen ) { + /* We don't need all the memory we freed up. Mark it free. */ + if ( copysrc < needbase ) { + add_freelist(&mmap, copysrc, needbase-copysrc, SMT_FREE); + copylen -= (needbase-copysrc); + } + if ( copylen > needlen ) { + add_freelist(&mmap, copysrc+needlen, copylen-needlen, SMT_FREE); + copylen = needlen; + } + } + reverse = 0; + goto move_chunk; + } + dprintf("Cannot find the chunk containing the critical byte\n"); + goto bail; /* Stuck! */ + + move_chunk: + move_chunk(&moves, &mmap, fp, copylen); + } + + /* Finally, append the postcopy chain to the end of the moves list */ + for (op = moves; (o = *op); op = &o->next) + ; /* Locate the end of the list */ + *op = postcopy; + postcopy = NULL; + + rv = 0; + bail: + if (mmap) + syslinux_free_memmap(mmap); + if (frags) + free_movelist(&frags); + if (postcopy) + free_movelist(&postcopy); + return rv; +} + +#ifdef TEST + +#include + +int main(int argc, char *argv[]) +{ + FILE *f; + unsigned long d, s, l; + struct syslinux_movelist *frags; + struct syslinux_movelist **fep = &frags; + struct syslinux_movelist *mv, *moves; + struct syslinux_memmap *memmap; + char line[BUFSIZ]; + + (void)argc; + + memmap = syslinux_init_memmap(); + + f = fopen(argv[1], "r"); + while ( fgets(line, sizeof line, f) != NULL ) { + if ( sscanf(line, "%lx %lx %lx", &s, &d, &l) == 3 ) { + if ( d ) { + if (s == -1UL) { + syslinux_add_memmap(&memmap, d, l, SMT_ZERO); + } else { + mv = new_movelist(d, s, l); + *fep = mv; + fep = &mv->next; + } + } else { + syslinux_add_memmap(&memmap, s, l, SMT_FREE); + } + } + } + fclose(f); + + *fep = NULL; + + printf("Input move list:\n"); + syslinux_dump_movelist(stdout, frags); + printf("Input free list:\n"); + syslinux_dump_memmap(stdout, memmap); + + if ( syslinux_compute_movelist(&moves, frags, memmap) ) { + printf("Failed to compute a move sequence\n"); + return 1; + } else { + printf("Final move list:\n"); + syslinux_dump_movelist(stdout, moves); + return 0; + } + } + +#endif /* TEST */ diff --git a/com32/lib/syslinux/pxe_get_cached.c b/com32/lib/syslinux/pxe_get_cached.c new file mode 100644 index 0000000..82c72a9 --- /dev/null +++ b/com32/lib/syslinux/pxe_get_cached.c @@ -0,0 +1,81 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * pxe_get_cached.c + * + * PXE call "get cached info" + */ + +#include +#include +#include +#include + +#include + +/* Returns the status code from PXE (0 on success), + or -1 on invocation failure */ +int pxe_get_cached_info(int level, void **buf, size_t *len) +{ + com32sys_t regs; + t_PXENV_GET_CACHED_INFO *gci = __com32.cs_bounce; + void *bbuf, *nbuf; + + memset(®s, 0, sizeof regs); + regs.eax.w[0] = 0x0009; + regs.ebx.w[0] = PXENV_GET_CACHED_INFO; + regs.es = SEG(gci); + regs.edi.w[0] = OFFS(gci); + + bbuf = &gci[1]; + + gci->Status = PXENV_STATUS_FAILURE; + gci->PacketType = level; + gci->BufferSize = gci->BufferLimit = 65536-sizeof(*gci); + gci->Buffer.seg = SEG(bbuf); + gci->Buffer.offs = OFFS(bbuf); + + __intcall(0x22, ®s, ®s); + + if (regs.eflags.l & EFLAGS_CF) + return -1; + + if (gci->Status) + return gci->Status; + + nbuf = malloc(gci->BufferSize); /* malloc() does not use the bounce buffer */ + if (!nbuf) + return -1; + + memcpy(nbuf, bbuf, gci->BufferSize); + + *buf = nbuf; + *len = gci->BufferSize; + + return 0; +} diff --git a/com32/lib/syslinux/pxe_get_nic.c b/com32/lib/syslinux/pxe_get_nic.c new file mode 100644 index 0000000..b779542 --- /dev/null +++ b/com32/lib/syslinux/pxe_get_nic.c @@ -0,0 +1,61 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * pxe_get_cached.c + * + * PXE call "get cached info" + */ + +#include +#include +#include +#include + +#include + +/* Returns the status code from PXE (0 on success), + or -1 on invocation failure */ +int pxe_get_nic_type(t_PXENV_UNDI_GET_NIC_TYPE *gnt) +{ + com32sys_t regs; + + memset(®s, 0, sizeof regs); + regs.eax.w[0] = 0x0009; + regs.ebx.w[0] = PXENV_UNDI_GET_NIC_TYPE; + regs.es = SEG( __com32.cs_bounce); + regs.edi.w[0] = OFFS( __com32.cs_bounce); + + __intcall(0x22, ®s, ®s); + + memcpy(gnt, __com32.cs_bounce, sizeof(t_PXENV_UNDI_GET_NIC_TYPE)); + + if (regs.eflags.l & EFLAGS_CF) + return -1; + + return gnt->Status; +} diff --git a/com32/lib/syslinux/reboot.c b/com32/lib/syslinux/reboot.c new file mode 100644 index 0000000..49b370e --- /dev/null +++ b/com32/lib/syslinux/reboot.c @@ -0,0 +1,47 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * reboot.c + * + * Provoke a system reboot + */ + +#include +#include +#include + +__noreturn syslinux_reboot(int warm) +{ + uint16_t * const reboot_flag = (uint16_t *)0x472; + + *reboot_flag = warm ? 0x1234 : 0; + __farcall(0xf000, 0xfff0, &__com32_zero_regs, NULL); + + while(1) + asm volatile("hlt"); +} diff --git a/com32/lib/syslinux/run_command.c b/com32/lib/syslinux/run_command.c new file mode 100644 index 0000000..eec6ca4 --- /dev/null +++ b/com32/lib/syslinux/run_command.c @@ -0,0 +1,47 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include +#include + +__noreturn syslinux_run_command(const char *command) +{ + static com32sys_t ireg; + + strcpy(__com32.cs_bounce, command); + + ireg.eax.w[0] = 0x0003; + ireg.es = SEG(__com32.cs_bounce); + ireg.ebx.w[0] = OFFS(__com32.cs_bounce); + + __intcall(0x22, &ireg, NULL); + + /* Should not return even on failure */ + for(;;); +} diff --git a/com32/lib/syslinux/run_default.c b/com32/lib/syslinux/run_default.c new file mode 100644 index 0000000..044fa1a --- /dev/null +++ b/com32/lib/syslinux/run_default.c @@ -0,0 +1,41 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include + +__noreturn syslinux_run_default(void) +{ + static com32sys_t ireg; + + ireg.eax.w[0] = 0x0004; + __intcall(0x22, &ireg, NULL); + + /* Should not return even on failure */ + for(;;); +} diff --git a/com32/lib/syslinux/runimage.c b/com32/lib/syslinux/runimage.c new file mode 100644 index 0000000..9055526 --- /dev/null +++ b/com32/lib/syslinux/runimage.c @@ -0,0 +1,65 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * runimage.c + * + * Load and run a syslinux image. + */ + +#include +#include +#include +#include + +void syslinux_run_kernel_image(const char *filename, const char *cmdline, + uint32_t ipappend_flags, uint32_t type) +{ + static com32sys_t ireg; + char *bbfilename, *bbcmdline, *bbptr; + int bytes; + + bbptr = __com32.cs_bounce; + + bytes = strlen(filename)+1; + memcpy(bbfilename = bbptr, filename, bytes); + bbptr += bytes; + + bytes = strlen(cmdline)+1; + memcpy(bbcmdline = bbptr, filename, bytes); + bbptr += bytes; + + ireg.eax.w[0] = 0x0016; + ireg.ds = SEG(bbfilename); + ireg.esi.w[0] = OFFS(bbfilename); + ireg.es = SEG(bbcmdline); + ireg.ebx.w[0] = OFFS(bbcmdline); + ireg.ecx.l = ipappend_flags; + ireg.edx.l = type; + + __intcall(0x22, &ireg, 0); +} diff --git a/com32/lib/syslinux/serial.c b/com32/lib/syslinux/serial.c new file mode 100644 index 0000000..82a8ffc --- /dev/null +++ b/com32/lib/syslinux/serial.c @@ -0,0 +1,52 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux/serial.c + * + * SYSLINUX serial console query + */ + +#include +#include +#include +#include + +struct syslinux_serial_console_info __syslinux_serial_console_info; + +void __constructor __syslinux_get_serial_console_info(void) +{ + static com32sys_t reg; + + memset(®, 0, sizeof reg); + reg.eax.w[0] = 0x000b; + __intcall(0x22, ®, ®); + + __syslinux_serial_console_info.iobase = reg.edx.w[0]; + __syslinux_serial_console_info.divisor = reg.ecx.w[0]; + __syslinux_serial_console_info.flowctl = reg.ebx.w[0]; +} diff --git a/com32/lib/syslinux/setadv.c b/com32/lib/syslinux/setadv.c new file mode 100644 index 0000000..5993df6 --- /dev/null +++ b/com32/lib/syslinux/setadv.c @@ -0,0 +1,116 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux/setadv.c + * + * (Over)write a data item in the auxilliary data vector. To + * delete an item, set its length to zero. + * + * Return 0 on success, -1 on error, and set errno. + * + * NOTE: Data is not written to disk unless + * syslinux_adv_write() is called. + */ + +#include +#include +#include +#include +#include +#include + +int syslinux_setadv(int tag, size_t size, const void *data) +{ + uint8_t *p, *advtmp; + size_t rleft, left; + + if ((unsigned)tag-1 > 254) { + errno = EINVAL; + return -1; /* Impossible tag value */ + } + + if (size > 255) { + errno = ENOSPC; /* Max 255 bytes for a data item */ + return -1; + } + + rleft = left = syslinux_adv_size(); + p = advtmp = alloca(left); + memcpy(p, syslinux_adv_ptr(), left); /* Make working copy */ + + while (rleft >= 2) { + uint8_t ptag = p[0]; + size_t plen = p[1]+2; + + if (ptag == ADV_END) + break; + + if (ptag == tag) { + /* Found our tag. Delete it. */ + + if (plen >= rleft) { + /* Entire remainder is our tag */ + break; + } + memmove(p, p+plen, rleft-plen); + rleft -= plen; /* Fewer bytes to read, but not to write */ + } else { + /* Not our tag */ + if (plen > rleft) + break; /* Corrupt tag (overrun) - overwrite it */ + + left -= plen; + rleft -= plen; + p += plen; + } + } + + /* Now (p, left) reflects the position to write in and how much space + we have for our data. */ + + if (size) { + if (left < size+2) { + errno = ENOSPC; /* Not enough space for data */ + return -1; + } + + *p++ = tag; + *p++ = size; + memcpy(p, data, size); + p += size; + left -= size+2; + } + + memset(p, 0, left); + + /* If we got here, everything went OK, commit the write to low memory */ + memcpy(syslinux_adv_ptr(), advtmp, syslinux_adv_size()); + + return 0; +} diff --git a/com32/lib/syslinux/shuffle.c b/com32/lib/syslinux/shuffle.c new file mode 100644 index 0000000..6c33d0a --- /dev/null +++ b/com32/lib/syslinux/shuffle.c @@ -0,0 +1,272 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * shuffle.c + * + * Common code for "shuffle and boot" operation; generates a shuffle list + * and puts it in the bounce buffer. Returns the number of shuffle + * descriptors. + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef DEBUG +# define DEBUG 0 +#endif + +#define dprintf(f, ...) ((void)0) +#define dprintf2(f, ...) ((void)0) + +#if DEBUG +# include +# undef dprintf +# define dprintf printf +# if DEBUG > 1 +# undef dprintf2 +# define dprintf2 printf +# endif +#endif + +struct shuffle_descriptor { + uint32_t dst, src, len; +}; + +static int shuffler_size; + +static void __constructor __syslinux_get_shuffer_size(void) +{ + static com32sys_t reg; + + reg.eax.w[0] = 0x0023; + __intcall(0x22, ®, ®); + + shuffler_size = (reg.eflags.l & EFLAGS_CF) ? 2048 : reg.ecx.w[0]; +} + +/* + * Allocate descriptor memory in these chunks; if this is large we may + * waste memory, if it is small we may get slow convergence. + */ +#define DESC_BLOCK_SIZE 256 + +int syslinux_do_shuffle(struct syslinux_movelist *fraglist, + struct syslinux_memmap *memmap, + addr_t entry_point, addr_t entry_type, + uint16_t bootflags) +{ + int rv = -1; + struct syslinux_movelist *moves = NULL, *mp; + struct syslinux_memmap *rxmap = NULL, *ml; + struct shuffle_descriptor *dp, *dbuf; + int np; + int desc_blocks, need_blocks; + int need_ptrs; + addr_t desczone, descfree, descaddr, descoffs; + int nmoves, nzero; + com32sys_t ireg; + + descaddr = 0; + dp = dbuf = NULL; + + /* Count the number of zero operations */ + nzero = 0; + for (ml = memmap; ml->type != SMT_END; ml = ml->next) { + if (ml->type == SMT_ZERO) + nzero++; + } + + /* Find the largest contiguous region unused by input *and* output; + this is where we put the move descriptor list and safe area */ + + rxmap = syslinux_dup_memmap(memmap); + if (!rxmap) + goto bail; + /* Avoid using the low 1 MB for the shuffle area -- this avoids + possible interference with the real mode code or stack */ + if (syslinux_add_memmap(&rxmap, 0, 1024*1024, SMT_RESERVED)) + goto bail; + for (mp = fraglist; mp; mp = mp->next) { + if (syslinux_add_memmap(&rxmap, mp->src, mp->len, SMT_ALLOC) || + syslinux_add_memmap(&rxmap, mp->dst, mp->len, SMT_ALLOC)) + goto bail; + } + if (syslinux_memmap_largest(rxmap, SMT_FREE, &desczone, &descfree)) + goto bail; + + syslinux_free_memmap(rxmap); + + dprintf("desczone = 0x%08x, descfree = 0x%08x\n", desczone, descfree); + + rxmap = syslinux_dup_memmap(memmap); + if (!rxmap) + goto bail; + + desc_blocks = (nzero+DESC_BLOCK_SIZE-1)/DESC_BLOCK_SIZE; + for (;;) { + /* We want (desc_blocks) allocation blocks, plus the terminating + descriptor, plus the shuffler safe area. */ + addr_t descmem = desc_blocks* + sizeof(struct shuffle_descriptor)*DESC_BLOCK_SIZE + + sizeof(struct shuffle_descriptor) + shuffler_size; + + descaddr = (desczone + descfree - descmem) & ~3; + + if (descaddr < desczone) + goto bail; /* No memory block large enough */ + + /* Mark memory used by shuffle descriptors as reserved */ + if (syslinux_add_memmap(&rxmap, descaddr, descmem, SMT_RESERVED)) + goto bail; + +#if DEBUG > 1 + syslinux_dump_movelist(stdout, fraglist); +#endif + + if (syslinux_compute_movelist(&moves, fraglist, rxmap)) + goto bail; + + nmoves = 0; + for (mp = moves; mp; mp = mp->next) + nmoves++; + + need_blocks = (nmoves+nzero+DESC_BLOCK_SIZE-1)/DESC_BLOCK_SIZE; + + if (desc_blocks >= need_blocks) + break; /* Sufficient memory, yay */ + + desc_blocks = need_blocks; /* Try again... */ + } + +#if DEBUG > 1 + dprintf("Final movelist:\n"); + syslinux_dump_movelist(stdout, moves); +#endif + + syslinux_free_memmap(rxmap); + rxmap = NULL; + + need_ptrs = nmoves+nzero+1; + dbuf = malloc(need_ptrs*sizeof(struct shuffle_descriptor)); + if (!dbuf) + goto bail; + + descoffs = descaddr - (addr_t)dbuf; + +#if DEBUG + dprintf("nmoves = %d, nzero = %d, dbuf = %p, offs = 0x%08x\n", + nmoves, nzero, dbuf, descoffs); +#endif + + /* Copy the move sequence into the descriptor buffer */ + np = 0; + dp = dbuf; + for (mp = moves; mp; mp = mp->next) { + dp->dst = mp->dst; + dp->src = mp->src; + dp->len = mp->len; + dprintf2("[ %08x %08x %08x ]\n", dp->dst, dp->src, dp->len); + dp++; np++; + } + + /* Copy bzero operations into the descriptor buffer */ + for (ml = memmap; ml->type != SMT_END; ml = ml->next) { + if (ml->type == SMT_ZERO) { + dp->dst = ml->start; + dp->src = (addr_t)-1; /* bzero region */ + dp->len = ml->next->start - ml->start; + dprintf2("[ %08x %08x %08x ]\n", dp->dst, dp->src, dp->len); + dp++; np++; + } + } + + /* Finally, record the termination entry */ + dp->dst = entry_point; + dp->src = entry_type; + dp->len = 0; + dp++; np++; + + if (np != need_ptrs) { + dprintf("!!! np = %d : nmoves = %d, nzero = %d, desc_blocks = %d\n", + np, nmoves, nzero, desc_blocks); + } + + rv = 0; + + bail: + /* This is safe only because free() doesn't use the bounce buffer!!!! */ + if (moves) + syslinux_free_movelist(moves); + if (rxmap) + syslinux_free_memmap(rxmap); + + if (rv) + return rv; + + /* Actually do it... */ + memset(&ireg, 0, sizeof ireg); + ireg.edi.l = descaddr; + ireg.esi.l = (addr_t)dbuf; + ireg.ecx.l = (addr_t)dp-(addr_t)dbuf; + ireg.edx.w[0] = bootflags; + ireg.eax.w[0] = 0x0024; + __intcall(0x22, &ireg, NULL); + + return -1; /* Shouldn't have returned! */ +} + +/* + * Common helper routine: takes a memory map and blots out the + * zones which are used in the destination of a fraglist + */ +struct syslinux_memmap * +syslinux_target_memmap(struct syslinux_movelist *fraglist, + struct syslinux_memmap *memmap) +{ + struct syslinux_memmap *tmap; + struct syslinux_movelist *mp; + + tmap = syslinux_dup_memmap(memmap); + if (!tmap) + return NULL; + + for (mp = fraglist; mp; mp = mp->next) { + if (syslinux_add_memmap(&tmap, mp->dst, mp->len, SMT_ALLOC)) { + syslinux_free_memmap(tmap); + return NULL; + } + } + + return tmap; +} diff --git a/com32/lib/syslinux/shuffle_pm.c b/com32/lib/syslinux/shuffle_pm.c new file mode 100644 index 0000000..cf250bb --- /dev/null +++ b/com32/lib/syslinux/shuffle_pm.c @@ -0,0 +1,78 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * shuffle_pm.c + * + * Shuffle and boot to protected mode code + */ + +#include +#include +#include + +int syslinux_shuffle_boot_pm(struct syslinux_movelist *fraglist, + struct syslinux_memmap *memmap, + uint16_t bootflags, + struct syslinux_pm_regs *regs) +{ + uint8_t handoff_code[9*5], *p; + const uint32_t *rp; + int i, rv; + struct syslinux_memmap *tmap; + addr_t regstub, stublen; + + tmap = syslinux_target_memmap(fraglist, memmap); + if (!tmap) + return -1; + + regstub = 0x800; /* Locate anywhere above this point */ + stublen = sizeof handoff_code; + rv = syslinux_memmap_find(tmap, SMT_FREE, ®stub, &stublen, 1); + syslinux_free_memmap(tmap); + if (rv) + return -1; + + /* Build register-setting stub */ + p = handoff_code; + rp = (const uint32_t *)regs; + for (i = 0; i < 8; i++) { + *p = 0xb8 + i; /* MOV gpr,imm32 */ + *(uint32_t *)(p+1) = *rp++; + p += 5; + } + *p = 0xe9; /* JMP */ + *(uint32_t *)(p+1) = regs->eip - regstub - sizeof handoff_code; + + /* Add register-setting stub to shuffle list */ + if (syslinux_add_movelist(&fraglist, regstub, (addr_t)handoff_code, + sizeof handoff_code)) + return -1; + + return syslinux_do_shuffle(fraglist, memmap, regstub, 1, bootflags); +} diff --git a/com32/lib/syslinux/shuffle_rm.c b/com32/lib/syslinux/shuffle_rm.c new file mode 100644 index 0000000..ccb4f95 --- /dev/null +++ b/com32/lib/syslinux/shuffle_rm.c @@ -0,0 +1,171 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * shuffle_rm.c + * + * Shuffle and boot to protected mode code + */ + +#include +#include +#include +#include +#include +#include + +enum gpr_index { R_AX, R_CX, R_DX, R_BX, R_SP, R_BP, R_SI, R_DI }; +enum seg_index { R_ES, R_CS, R_SS, R_DS, R_FS, R_GS }; + +#define ST8(P,V) \ + do { \ + uint8_t *_p = (void *)(P); \ + *_p++ = (V); \ + (P) = (void *)_p; \ + } while (0); +#define ST16(P,V) \ + do { \ + uint16_t *_p = (void *)(P); \ + *_p++ = (V); \ + (P) = (void *)_p; \ + } while (0) +#define ST32(P,V) \ + do { \ + uint32_t *_p = (void *)(P); \ + *_p++ = (V); \ + (P) = (void *)_p; \ + } while (0) + +#define MOV_TO_SEG(P,S,R) \ + ST16(P, 0xc08e + ((R) << 8) + ((S) << 11)) +#define MOV_TO_R16(P,R,V) \ + do { \ + ST8(P, 0xb8 + (R)); \ + ST16(P, V); \ + } while (0) +#define MOV_TO_R32(P,R,V) \ + do { \ + ST16(P, 0xb866 + ((R) << 8)); \ + ST32(P, V); \ + } while (0) + +int syslinux_shuffle_boot_rm(struct syslinux_movelist *fraglist, + struct syslinux_memmap *memmap, + uint16_t bootflags, + struct syslinux_rm_regs *regs) +{ + const struct syslinux_rm_regs_alt { + uint16_t seg[6]; + uint32_t gpr[8]; + uint32_t csip; + bool sti; + } *rp; + int i, rv; + uint8_t handoff_code[8+5*5+8*6+1+5], *p; + uint16_t off; + struct syslinux_memmap *tmap; + addr_t regstub, stublen; + /* Assign GPRs for each sreg, don't use AX and SP */ + static const uint8_t gpr_for_seg[6] = { R_CX, R_DX, R_BX, R_BP, R_SI, R_DI }; + + tmap = syslinux_target_memmap(fraglist, memmap); + if (!tmap) + return -1; + + /* + * Search for a good place to put the real-mode register stub. + * We prefer it as low as possible above 0x800. KVM barfs horribly + * if we're not aligned to a paragraph boundary, so set the alignment + * appropriately. + */ + regstub = 0x800; + stublen = sizeof handoff_code; + rv = syslinux_memmap_find(tmap, SMT_FREE, ®stub, &stublen, 16); + + if (rv || (regstub > 0x100000 - sizeof handoff_code)) { + /* + * Uh-oh. This isn't real-mode accessible memory. + * It might be possible to do something insane here like + * putting the stub in the IRQ vectors, or in the 0x5xx segment. + * This code tries the 0x510-0x7ff range and hopes for the best. + */ + regstub = 0x510; /* Try the 0x5xx segment... */ + stublen = sizeof handoff_code; + rv = syslinux_memmap_find(tmap, SMT_FREE, ®stub, &stublen, 16); + + if (!rv && (regstub > 0x100000 - sizeof handoff_code)) + rv = -1; /* No acceptable memory found */ + } + + syslinux_free_memmap(tmap); + if (rv) + return -1; + + /* Build register-setting stub */ + p = handoff_code; + rp = (const struct syslinux_rm_regs_alt *)regs; + + /* Set up GPRs with segment registers - don't use AX */ + for (i = 0; i < 6; i++) { + if (i != R_CS) + MOV_TO_R16(p, gpr_for_seg[i], rp->seg[i]); + } + + /* Actual transition to real mode */ + ST32(p, 0xeac0220f); /* MOV CR0,EAX; JMP FAR */ + off = (p-handoff_code)+4; + ST16(p, off); /* Offset */ + ST16(p,regstub >> 4); /* Segment */ + + /* Load SS and ESP immediately */ + MOV_TO_SEG(p, R_SS, R_BX); + MOV_TO_R32(p, R_SP, rp->gpr[R_SP]); + + /* Load the other segments */ + MOV_TO_SEG(p, R_ES, R_CX); + MOV_TO_SEG(p, R_DS, R_BP); + MOV_TO_SEG(p, R_FS, R_SI); + MOV_TO_SEG(p, R_GS, R_DI); + + for (i = 0; i < 8; i++) { + if (i != R_SP) + MOV_TO_R32(p, i, rp->gpr[i]); + } + + ST8(p, rp->sti ? 0xfb : 0xfa); /* STI/CLI */ + + ST8(p, 0xea); /* JMP FAR */ + ST32(p, rp->csip); + + /* Add register-setting stub to shuffle list */ + if (syslinux_add_movelist(&fraglist, regstub, (addr_t)handoff_code, + sizeof handoff_code)) + return -1; + + return syslinux_do_shuffle(fraglist, memmap, regstub, 0, bootflags); +} diff --git a/com32/lib/syslinux/version.c b/com32/lib/syslinux/version.c new file mode 100644 index 0000000..2131fa0 --- /dev/null +++ b/com32/lib/syslinux/version.c @@ -0,0 +1,46 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include + +struct syslinux_version __syslinux_version; + +void __constructor __syslinux_get_version(void) +{ + static com32sys_t reg; + + reg.eax.w[0] = 0x0001; + __intcall(0x22, ®, ®); + + __syslinux_version.version = reg.ecx.w[0]; + __syslinux_version.max_api = reg.eax.w[0]; + __syslinux_version.filesystem = reg.edx.b[0]; + __syslinux_version.version_string = MK_PTR(reg.es, reg.esi.w[0]); + __syslinux_version.copyright_string = MK_PTR(reg.es, reg.edi.w[0]); +} diff --git a/com32/lib/syslinux/zloadfile.c b/com32/lib/syslinux/zloadfile.c new file mode 100644 index 0000000..8dff193 --- /dev/null +++ b/com32/lib/syslinux/zloadfile.c @@ -0,0 +1,59 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2005-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * zloadfile.c + * + * Read the contents of a possibly compressed data file into a malloc'd buffer + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define INCREMENTAL_CHUNK 1024*1024 + +int zloadfile(const char *filename, void **ptr, size_t *len) +{ + FILE *f; + int rv; + + f = zfopen(filename, "r"); + if ( !f ) + return -1; + + rv = floadfile(f, ptr, len, NULL, 0); + fclose(f); + + return rv; +} diff --git a/com32/lib/syslinux/zonelist.c b/com32/lib/syslinux/zonelist.c new file mode 100644 index 0000000..0ceec9b --- /dev/null +++ b/com32/lib/syslinux/zonelist.c @@ -0,0 +1,301 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * zonelist.c + * + * Deal with syslinux_memmap's, which are data structures designed to + * hold memory maps. A zonelist is a sorted linked list of memory + * ranges, with the guarantee that no two adjacent blocks have the + * same range type. Additionally, all unspecified memory have a range + * type of zero. + */ + +#include +#include +#include + +#ifndef DEBUG +# ifdef TEST +# define DEBUG 1 +# else +# define DEBUG 0 +# endif +#endif + +#if DEBUG +# include +# define dprintf printf +#else +# define dprintf(...) ((void)0) +#endif + +/* + * Create an empty syslinux_memmap list. + */ +struct syslinux_memmap *syslinux_init_memmap(void) +{ + struct syslinux_memmap *sp, *ep; + + sp = malloc(sizeof(*sp)); + if (!sp) + return NULL; + + ep = malloc(sizeof(*ep)); + if (!ep) { + free(sp); + return NULL; + } + + sp->start = 0; + sp->type = SMT_UNDEFINED; + sp->next = ep; + + ep->start = 0; /* Wrap around... */ + ep->type = SMT_END; /* End of chain */ + ep->next = NULL; + + return sp; +} + +/* + * Add an item to a syslinux_memmap list, potentially overwriting + * what is already there. + */ +int syslinux_add_memmap(struct syslinux_memmap **list, + addr_t start, addr_t len, + enum syslinux_memmap_types type) +{ + addr_t last; + struct syslinux_memmap *mp, **mpp; + struct syslinux_memmap *range; + enum syslinux_memmap_types oldtype; + +#if DEBUG + dprintf("Input memmap:\n"); + syslinux_dump_memmap(stdout, *list); +#endif + + /* Remove this to make len == 0 mean all of memory */ + if (len == 0) + return 0; + + /* Last byte -- to avoid rollover */ + last = start+len-1; + + mpp = list; + oldtype = SMT_END; /* Impossible value */ + while (mp = *mpp, start > mp->start && mp->type != SMT_END) { + oldtype = mp->type; + mpp = &mp->next; + } + + if (start < mp->start || mp->type == SMT_END) { + if (type != oldtype) { + /* Splice in a new start token */ + range = malloc(sizeof(*range)); + if (!range) + return -1; + + range->start = start; + range->type = type; + *mpp = range; + range->next = mp; + mpp = &range->next; + } + } else { + /* mp is exactly aligned with the start of our region */ + if (type != oldtype) { + /* Reclaim this entry as our own boundary marker */ + oldtype = mp->type; + mp->type = type; + mpp = &mp->next; + } + } + + while (mp = *mpp, last > mp->start-1) { + oldtype = mp->type; + *mpp = mp->next; + free(mp); + } + + if (last < mp->start-1) { + if (oldtype != type) { + /* Need a new end token */ + range = malloc(sizeof(*range)); + if (!range) + return -1; + + range->start = last+1; + range->type = oldtype; + *mpp = range; + range->next = mp; + } + } else { + if (mp->type == type) { + /* Merge this region with the following one */ + *mpp = mp->next; + free(mp); + } + } + +#if DEBUG + dprintf("After adding (%#x,%#x,%d):\n", start, len, type); + syslinux_dump_memmap(stdout, *list); +#endif + + return 0; +} + +/* + * Verify what type a certain memory region is. This function returns + * SMT_ERROR if the memory region has multiple types. + */ +enum syslinux_memmap_types syslinux_memmap_type(struct syslinux_memmap *list, + addr_t start, addr_t len) +{ + addr_t last, llast; + + last = start+len-1; + + while (list->type != SMT_END) { + llast = list->next->start-1; + if (list->start <= start) { + if (llast >= last) + return list->type; /* Region has a well-defined type */ + else if (llast >= start) + return SMT_ERROR; /* Crosses region boundary */ + } + list = list->next; + } + + return SMT_ERROR; /* Internal error? */ +} + +/* + * Find the largest zone of a specific type. Returns -1 on failure. + */ +int syslinux_memmap_largest(struct syslinux_memmap *list, + enum syslinux_memmap_types type, + addr_t *start, addr_t *len) +{ + addr_t size, best_size = 0; + struct syslinux_memmap *best = NULL; + + while (list->type != SMT_END) { + size = list->next->start - list->start; + + if (list->type == type && size > best_size) { + best = list; + best_size = size; + } + + list = list->next; + } + + if (!best) + return -1; + + *start = best->start; + *len = best_size; + + return 0; +} + +/* + * Find the first (lowest address) zone of a specific type and of + * a certain minimum size, with an optional starting address. + * The input values of start and len are used as minima. + */ +int syslinux_memmap_find(struct syslinux_memmap *list, + enum syslinux_memmap_types type, + addr_t *start, addr_t *len, addr_t align) +{ + addr_t min_start = *start; + addr_t min_len = *len; + + while (list->type != SMT_END) { + if (list->type == type) { + addr_t xstart, xlen; + xstart = min_start > list->start ? min_start : list->start; + xstart = ALIGN_UP(xstart, align); + + if (xstart < list->next->start) { + xlen = list->next->start - xstart; + if (xlen >= min_len) { + *start = xstart; + *len = xlen; + return 0; + } + } + } + list = list->next; + } + + return -1; /* Not found */ +} + +/* + * Free a zonelist. + */ +void syslinux_free_memmap(struct syslinux_memmap *list) +{ + struct syslinux_memmap *ml; + + while (list) { + ml = list; + list = list->next; + free(ml); + } +} + +/* + * Duplicate a zonelist. Returns NULL on failure. + */ +struct syslinux_memmap *syslinux_dup_memmap(struct syslinux_memmap *list) +{ + struct syslinux_memmap *newlist = NULL, **nlp = &newlist; + struct syslinux_memmap *ml; + + while (list) { + ml = malloc(sizeof(*ml)); + if (!ml) { + syslinux_free_memmap(newlist); + return NULL; + } + ml->start = list->start; + ml->type = list->type; + ml->next = NULL; + *nlp = ml; + nlp = &ml->next; + + list = list->next; + } + + return newlist; +} diff --git a/com32/lib/vasprintf.c b/com32/lib/vasprintf.c new file mode 100644 index 0000000..910e7d9 --- /dev/null +++ b/com32/lib/vasprintf.c @@ -0,0 +1,25 @@ +/* + * vasprintf.c + */ + +#include +#include +#include + +int vasprintf(char **bufp, const char *format, va_list ap) +{ + va_list ap1; + int bytes; + char *p; + + va_copy(ap1, ap); + + bytes = vsnprintf(NULL, 0, format, ap1) + 1; + va_end(ap1); + + *bufp = p = malloc(bytes); + if ( !p ) + return -1; + + return vsnprintf(p, bytes, format, ap); +} diff --git a/com32/lib/vfprintf.c b/com32/lib/vfprintf.c new file mode 100644 index 0000000..39cf983 --- /dev/null +++ b/com32/lib/vfprintf.c @@ -0,0 +1,26 @@ +/* + * vfprintf.c + */ + +#include +#include +#include +#include + +#define BUFFER_SIZE 32768 + +int vfprintf(FILE *file, const char *format, va_list ap) +{ + int rv; + char buffer[BUFFER_SIZE]; + + rv = vsnprintf(buffer, BUFFER_SIZE, format, ap); + + if ( rv < 0 ) + return rv; + + if ( rv > BUFFER_SIZE-1 ) + rv = BUFFER_SIZE-1; + + return _fwrite(buffer, rv, file); +} diff --git a/com32/lib/vprintf.c b/com32/lib/vprintf.c new file mode 100644 index 0000000..7d60665 --- /dev/null +++ b/com32/lib/vprintf.c @@ -0,0 +1,11 @@ +/* + * vprintf.c + */ + +#include +#include + +int vprintf(const char *format, va_list ap) +{ + return vfprintf(stdout, format, ap); +} diff --git a/com32/lib/vsnprintf.c b/com32/lib/vsnprintf.c new file mode 100644 index 0000000..8028005 --- /dev/null +++ b/com32/lib/vsnprintf.c @@ -0,0 +1,433 @@ +/* + * vsnprintf.c + * + * vsnprintf(), from which the rest of the printf() + * family is built + */ + +#include +#include +#include +#include +#include +#include + +enum flags { + FL_ZERO = 0x01, /* Zero modifier */ + FL_MINUS = 0x02, /* Minus modifier */ + FL_PLUS = 0x04, /* Plus modifier */ + FL_TICK = 0x08, /* ' modifier */ + FL_SPACE = 0x10, /* Space modifier */ + FL_HASH = 0x20, /* # modifier */ + FL_SIGNED = 0x40, /* Number is signed */ + FL_UPPER = 0x80 /* Upper case digits */ +}; + +/* These may have to be adjusted on certain implementations */ +enum ranks { + rank_char = -2, + rank_short = -1, + rank_int = 0, + rank_long = 1, + rank_longlong = 2 +}; + +#define MIN_RANK rank_char +#define MAX_RANK rank_longlong + +#define INTMAX_RANK rank_longlong +#define SIZE_T_RANK rank_long +#define PTRDIFF_T_RANK rank_long + +#define EMIT(x) ({ if (o nchars ) { + while ( width > nchars ) { + EMIT(' '); + width--; + } + } + + /* Emit nondigits */ + if ( minus ) + EMIT('-'); + else if ( flags & FL_PLUS ) + EMIT('+'); + else if ( flags & FL_SPACE ) + EMIT(' '); + + if ( (flags & FL_HASH) && base == 16 ) { + EMIT('0'); + EMIT((flags & FL_UPPER) ? 'X' : 'x'); + } + + /* Emit zero padding */ + if ( (flags & (FL_MINUS|FL_ZERO)) == FL_ZERO && width > ndigits ) { + while ( width > nchars ) { + EMIT('0'); + width--; + } + } + + /* Generate the number. This is done from right to left. */ + q += ndigits; /* Advance the pointer to end of number */ + o += ndigits; + qq = q; oo = o; /* Temporary values */ + + b4tick = tickskip; + while ( ndigits > 0 ) { + if ( !b4tick-- ) { + qq--; oo--; ndigits--; + if ( oo < n ) *qq = '_'; + b4tick = tickskip-1; + } + qq--; oo--; ndigits--; + if ( oo < n ) *qq = digits[val%base]; + val /= base; + } + + /* Emit late space padding */ + while ( (flags & FL_MINUS) && width > nchars ) { + EMIT(' '); + width--; + } + + return o; +} + + +int vsnprintf(char *buffer, size_t n, const char *format, va_list ap) +{ + const char *p = format; + char ch; + char *q = buffer; + size_t o = 0; /* Number of characters output */ + uintmax_t val = 0; + int rank = rank_int; /* Default rank */ + int width = 0; + int prec = -1; + int base; + size_t sz; + enum flags flags = 0; + enum { + st_normal, /* Ground state */ + st_flags, /* Special flags */ + st_width, /* Field width */ + st_prec, /* Field precision */ + st_modifiers /* Length or conversion modifiers */ + } state = st_normal; + const char *sarg; /* %s string argument */ + char carg; /* %c char argument */ + int slen; /* String length */ + + while ( (ch = *p++) ) { + switch ( state ) { + case st_normal: + if ( ch == '%' ) { + state = st_flags; + flags = 0; rank = rank_int; width = 0; prec = -1; + } else { + EMIT(ch); + } + break; + + case st_flags: + switch ( ch ) { + case '-': + flags |= FL_MINUS; + break; + case '+': + flags |= FL_PLUS; + break; + case '\'': + flags |= FL_TICK; + break; + case ' ': + flags |= FL_SPACE; + break; + case '#': + flags |= FL_HASH; + break; + case '0': + flags |= FL_ZERO; + break; + default: + state = st_width; + p--; /* Process this character again */ + break; + } + break; + + case st_width: + if ( ch >= '0' && ch <= '9' ) { + width = width*10+(ch-'0'); + } else if ( ch == '*' ) { + width = va_arg(ap, int); + if ( width < 0 ) { + width = -width; + flags |= FL_MINUS; + } + } else if ( ch == '.' ) { + prec = 0; /* Precision given */ + state = st_prec; + } else { + state = st_modifiers; + p--; /* Process this character again */ + } + break; + + case st_prec: + if ( ch >= '0' && ch <= '9' ) { + prec = prec*10+(ch-'0'); + } else if ( ch == '*' ) { + prec = va_arg(ap, int); + if ( prec < 0 ) + prec = -1; + } else { + state = st_modifiers; + p--; /* Process this character again */ + } + break; + + case st_modifiers: + switch ( ch ) { + /* Length modifiers - nonterminal sequences */ + case 'h': + rank--; /* Shorter rank */ + break; + case 'l': + rank++; /* Longer rank */ + break; + case 'j': + rank = INTMAX_RANK; + break; + case 'z': + rank = SIZE_T_RANK; + break; + case 't': + rank = PTRDIFF_T_RANK; + break; + case 'L': + case 'q': + rank += 2; + break; + default: + /* Output modifiers - terminal sequences */ + state = st_normal; /* Next state will be normal */ + if ( rank < MIN_RANK ) /* Canonicalize rank */ + rank = MIN_RANK; + else if ( rank > MAX_RANK ) + rank = MAX_RANK; + + switch ( ch ) { + case 'P': /* Upper case pointer */ + flags |= FL_UPPER; + /* fall through */ + case 'p': /* Pointer */ + base = 16; + prec = (CHAR_BIT*sizeof(void *)+3)/4; + flags |= FL_HASH; + val = (uintmax_t)(uintptr_t)va_arg(ap, void *); + goto is_integer; + + case 'd': /* Signed decimal output */ + case 'i': + base = 10; + flags |= FL_SIGNED; + switch (rank) { + case rank_char: + /* Yes, all these casts are needed... */ + val = (uintmax_t)(intmax_t)(signed char)va_arg(ap, signed int); + break; + case rank_short: + val = (uintmax_t)(intmax_t)(signed short)va_arg(ap, signed int); + break; + case rank_int: + val = (uintmax_t)(intmax_t)va_arg(ap, signed int); + break; + case rank_long: + val = (uintmax_t)(intmax_t)va_arg(ap, signed long); + break; + case rank_longlong: + val = (uintmax_t)(intmax_t)va_arg(ap, signed long long); + break; + } + goto is_integer; + case 'o': /* Octal */ + base = 8; + goto is_unsigned; + case 'u': /* Unsigned decimal */ + base = 10; + goto is_unsigned; + case 'X': /* Upper case hexadecimal */ + flags |= FL_UPPER; + /* fall through */ + case 'x': /* Hexadecimal */ + base = 16; + goto is_unsigned; + + is_unsigned: + switch (rank) { + case rank_char: + val = (uintmax_t)(unsigned char)va_arg(ap, unsigned int); + break; + case rank_short: + val = (uintmax_t)(unsigned short)va_arg(ap, unsigned int); + break; + case rank_int: + val = (uintmax_t)va_arg(ap, unsigned int); + break; + case rank_long: + val = (uintmax_t)va_arg(ap, unsigned long); + break; + case rank_longlong: + val = (uintmax_t)va_arg(ap, unsigned long long); + break; + } + /* fall through */ + + is_integer: + sz = format_int(q, (o prec ) + slen = prec; + + if ( width > slen && !(flags & FL_MINUS) ) { + char pad = (flags & FL_ZERO) ? '0' : ' '; + while ( width > slen ) { + EMIT(pad); + width--; + } + } + for ( i = slen ; i ; i-- ) { + sch = *sarg++; + EMIT(sch); + } + if ( width > slen && (flags & FL_MINUS) ) { + while ( width > slen ) { + EMIT(' '); + width--; + } + } + } + break; + + case 'n': /* Output the number of characters written */ + { + switch (rank) { + case rank_char: + *va_arg(ap, signed char *) = o; + break; + case rank_short: + *va_arg(ap, signed short *) = o; + break; + case rank_int: + *va_arg(ap, signed int *) = o; + break; + case rank_long: + *va_arg(ap, signed long *) = o; + break; + case rank_longlong: + *va_arg(ap, signed long long *) = o; + break; + } + } + break; + + default: /* Anything else, including % */ + EMIT(ch); + break; + } + } + } + } + + /* Null-terminate the string */ + if ( o0 ) + buffer[n-1] = '\0'; /* Overflow - terminate at end of buffer */ + + return o; +} diff --git a/com32/lib/vsprintf.c b/com32/lib/vsprintf.c new file mode 100644 index 0000000..4a6100e --- /dev/null +++ b/com32/lib/vsprintf.c @@ -0,0 +1,11 @@ +/* + * vsprintf.c + */ + +#include +#include + +int vsprintf(char *buffer, const char *format, va_list ap) +{ + return vsnprintf(buffer, ~(size_t)0, format, ap); +} diff --git a/com32/lib/vsscanf.c b/com32/lib/vsscanf.c new file mode 100644 index 0000000..0615679 --- /dev/null +++ b/com32/lib/vsscanf.c @@ -0,0 +1,365 @@ +/* + * vsscanf.c + * + * vsscanf(), from which the rest of the scanf() + * family is built + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef LONG_BIT +#define LONG_BIT (CHAR_BIT*sizeof(long)) +#endif + +enum flags { + FL_SPLAT = 0x01, /* Drop the value, do not assign */ + FL_INV = 0x02, /* Character-set with inverse */ + FL_WIDTH = 0x04, /* Field width specified */ + FL_MINUS = 0x08, /* Negative number */ +}; + +enum ranks { + rank_char = -2, + rank_short = -1, + rank_int = 0, + rank_long = 1, + rank_longlong = 2, + rank_ptr = INT_MAX /* Special value used for pointers */ +}; + +#define MIN_RANK rank_char +#define MAX_RANK rank_longlong + +#define INTMAX_RANK rank_longlong +#define SIZE_T_RANK rank_long +#define PTRDIFF_T_RANK rank_long + +enum bail { + bail_none = 0, /* No error condition */ + bail_eof, /* Hit EOF */ + bail_err /* Conversion mismatch */ +}; + +static inline const char * +skipspace(const char *p) +{ + while ( isspace((unsigned char)*p) ) p++; + return p; +} + +#undef set_bit +static inline void +set_bit(unsigned long *bitmap, unsigned int bit) +{ + bitmap[bit/LONG_BIT] |= 1UL << (bit%LONG_BIT); +} + +#undef test_bit +static inline int +test_bit(unsigned long *bitmap, unsigned int bit) +{ + return (int)(bitmap[bit/LONG_BIT] >> (bit%LONG_BIT)) & 1; +} + +int vsscanf(const char *buffer, const char *format, va_list ap) +{ + const char *p = format; + char ch; + const char *q = buffer; + const char *qq; + uintmax_t val = 0; + int rank = rank_int; /* Default rank */ + unsigned int width = UINT_MAX; + int base; + enum flags flags = 0; + enum { + st_normal, /* Ground state */ + st_flags, /* Special flags */ + st_width, /* Field width */ + st_modifiers, /* Length or conversion modifiers */ + st_match_init, /* Initial state of %[ sequence */ + st_match, /* Main state of %[ sequence */ + st_match_range, /* After - in a %[ sequence */ + } state = st_normal; + char *sarg = NULL; /* %s %c or %[ string argument */ + enum bail bail = bail_none; + int sign; + int converted = 0; /* Successful conversions */ + unsigned long matchmap[((1 << CHAR_BIT)+(LONG_BIT-1))/LONG_BIT]; + int matchinv = 0; /* Is match map inverted? */ + unsigned char range_start = 0; + + while ( (ch = *p++) && !bail ) { + switch ( state ) { + case st_normal: + if ( ch == '%' ) { + state = st_flags; + flags = 0; rank = rank_int; width = UINT_MAX; + } else if ( isspace((unsigned char)ch) ) { + q = skipspace(q); + } else { + if ( *q == ch ) + q++; + else + bail = bail_err; /* Match failure */ + } + break; + + case st_flags: + switch ( ch ) { + case '*': + flags |= FL_SPLAT; + break; + case '0' ... '9': + width = (ch-'0'); + state = st_width; + flags |= FL_WIDTH; + break; + default: + state = st_modifiers; + p--; /* Process this character again */ + break; + } + break; + + case st_width: + if ( ch >= '0' && ch <= '9' ) { + width = width*10+(ch-'0'); + } else { + state = st_modifiers; + p--; /* Process this character again */ + } + break; + + case st_modifiers: + switch ( ch ) { + /* Length modifiers - nonterminal sequences */ + case 'h': + rank--; /* Shorter rank */ + break; + case 'l': + rank++; /* Longer rank */ + break; + case 'j': + rank = INTMAX_RANK; + break; + case 'z': + rank = SIZE_T_RANK; + break; + case 't': + rank = PTRDIFF_T_RANK; + break; + case 'L': + case 'q': + rank = rank_longlong; /* long double/long long */ + break; + + default: + /* Output modifiers - terminal sequences */ + state = st_normal; /* Next state will be normal */ + if ( rank < MIN_RANK ) /* Canonicalize rank */ + rank = MIN_RANK; + else if ( rank > MAX_RANK ) + rank = MAX_RANK; + + switch ( ch ) { + case 'P': /* Upper case pointer */ + case 'p': /* Pointer */ +#if 0 /* Enable this to allow null pointers by name */ + q = skipspace(q); + if ( !isdigit((unsigned char)*q) ) { + static const char * const nullnames[] = + { "null", "nul", "nil", "(null)", "(nul)", "(nil)", 0 }; + const char * const *np; + + /* Check to see if it's a null pointer by name */ + for ( np = nullnames ; *np ; np++ ) { + if ( !strncasecmp(q, *np, strlen(*np)) ) { + val = (uintmax_t)((void *)NULL); + goto set_integer; + } + } + /* Failure */ + bail = bail_err; + break; + } + /* else */ +#endif + rank = rank_ptr; + base = 0; sign = 0; + goto scan_int; + + case 'i': /* Base-independent integer */ + base = 0; sign = 1; + goto scan_int; + + case 'd': /* Decimal integer */ + base = 10; sign = 1; + goto scan_int; + + case 'o': /* Octal integer */ + base = 8; sign = 0; + goto scan_int; + + case 'u': /* Unsigned decimal integer */ + base = 10; sign = 0; + goto scan_int; + + case 'x': /* Hexadecimal integer */ + case 'X': + base = 16; sign = 0; + goto scan_int; + + case 'n': /* Number of characters consumed */ + val = (q-buffer); + goto set_integer; + + scan_int: + q = skipspace(q); + if ( !*q ) { + bail = bail_eof; + break; + } + val = strntoumax(q, (char **)&qq, base, width); + if ( qq == q ) { + bail = bail_err; + break; + } + q = qq; + converted++; + /* fall through */ + + set_integer: + if ( !(flags & FL_SPLAT) ) { + switch(rank) { + case rank_char: + *va_arg(ap, unsigned char *) = (unsigned char)val; + break; + case rank_short: + *va_arg(ap, unsigned short *) = (unsigned short)val; + break; + case rank_int: + *va_arg(ap, unsigned int *) = (unsigned int)val; + break; + case rank_long: + *va_arg(ap, unsigned long *) = (unsigned long)val; + break; + case rank_longlong: + *va_arg(ap, unsigned long long *) = (unsigned long long)val; + break; + case rank_ptr: + *va_arg(ap, void **) = (void *)(uintptr_t)val; + break; + } + } + break; + + case 'c': /* Character */ + width = (flags & FL_WIDTH) ? width : 1; /* Default width == 1 */ + sarg = va_arg(ap, char *); + while ( width-- ) { + if ( !*q ) { + bail = bail_eof; + break; + } + *sarg++ = *q++; + } + if ( !bail ) + converted++; + break; + + case 's': /* String */ + { + char *sp; + sp = sarg = va_arg(ap, char *); + while ( width-- && *q && !isspace((unsigned char)*q) ) { + *sp++ = *q++; + } + if ( sarg != sp ) { + *sp = '\0'; /* Terminate output */ + converted++; + } else { + bail = bail_eof; + } + } + break; + + case '[': /* Character range */ + sarg = va_arg(ap, char *); + state = st_match_init; + matchinv = 0; + memset(matchmap, 0, sizeof matchmap); + break; + + case '%': /* %% sequence */ + if ( *q == '%' ) + q++; + else + bail = bail_err; + break; + + default: /* Anything else */ + bail = bail_err; /* Unknown sequence */ + break; + } + } + break; + + case st_match_init: /* Initial state for %[ match */ + if ( ch == '^' && !(flags & FL_INV) ) { + matchinv = 1; + } else { + set_bit(matchmap, (unsigned char)ch); + state = st_match; + } + break; + + case st_match: /* Main state for %[ match */ + if ( ch == ']' ) { + goto match_run; + } else if ( ch == '-' ) { + range_start = (unsigned char)ch; + state = st_match_range; + } else { + set_bit(matchmap, (unsigned char)ch); + } + break; + + case st_match_range: /* %[ match after - */ + if ( ch == ']' ) { + set_bit(matchmap, (unsigned char)'-'); /* - was last character */ + goto match_run; + } else { + int i; + for ( i = range_start ; i < (unsigned char)ch ; i++ ) + set_bit(matchmap, i); + state = st_match; + } + break; + + match_run: /* Match expression finished */ + qq = q; + while ( width && *q && test_bit(matchmap, (unsigned char)*q)^matchinv ) { + *sarg++ = *q++; + } + if ( q != qq ) { + *sarg = '\0'; + converted++; + } else { + bail = *q ? bail_err : bail_eof; + } + break; + } + } + + if ( bail == bail_eof && !converted ) + converted = -1; /* Return EOF (-1) */ + + return converted; +} diff --git a/com32/lib/zalloc.c b/com32/lib/zalloc.c new file mode 100644 index 0000000..2a58a11 --- /dev/null +++ b/com32/lib/zalloc.c @@ -0,0 +1,17 @@ +/* + * zalloc.c + */ + +#include +#include + +void *zalloc(size_t size) +{ + void *ptr; + + ptr = malloc(size); + if ( ptr ) + memset(ptr, 0, size); + + return ptr; +} diff --git a/com32/lib/zlib/FAQ b/com32/lib/zlib/FAQ new file mode 100644 index 0000000..7115ec3 --- /dev/null +++ b/com32/lib/zlib/FAQ @@ -0,0 +1,315 @@ + + Frequently Asked Questions about zlib + + +If your question is not there, please check the zlib home page +http://www.zlib.org which may have more recent information. +The lastest zlib FAQ is at http://www.gzip.org/zlib/zlib_faq.html + + + 1. Is zlib Y2K-compliant? + + Yes. zlib doesn't handle dates. + + 2. Where can I get a Windows DLL version? + + The zlib sources can be compiled without change to produce a DLL. + See the file win32/DLL_FAQ.txt in the zlib distribution. + Pointers to the precompiled DLL are found in the zlib web site at + http://www.zlib.org. + + 3. Where can I get a Visual Basic interface to zlib? + + See + * http://www.winimage.com/zLibDll/ + * http://www.dogma.net/markn/articles/zlibtool/zlibtool.htm + * contrib/visual-basic.txt in the zlib distribution + + 4. compress() returns Z_BUF_ERROR + + Make sure that before the call of compress, the length of the compressed + buffer is equal to the total size of the compressed buffer and not + zero. For Visual Basic, check that this parameter is passed by reference + ("as any"), not by value ("as long"). + + 5. deflate() or inflate() returns Z_BUF_ERROR + + Before making the call, make sure that avail_in and avail_out are not + zero. When setting the parameter flush equal to Z_FINISH, also make sure + that avail_out is big enough to allow processing all pending input. + Note that a Z_BUF_ERROR is not fatal--another call to deflate() or + inflate() can be made with more input or output space. A Z_BUF_ERROR + may in fact be unavoidable depending on how the functions are used, since + it is not possible to tell whether or not there is more output pending + when strm.avail_out returns with zero. + + 6. Where's the zlib documentation (man pages, etc.)? + + It's in zlib.h for the moment, and Francis S. Lin has converted it to a + web page zlib.html. Volunteers to transform this to Unix-style man pages, + please contact Jean-loup Gailly (jloup@gzip.org). Examples of zlib usage + are in the files example.c and minigzip.c. + + 7. Why don't you use GNU autoconf or libtool or ...? + + Because we would like to keep zlib as a very small and simple + package. zlib is rather portable and doesn't need much configuration. + + 8. I found a bug in zlib. + + Most of the time, such problems are due to an incorrect usage of + zlib. Please try to reproduce the problem with a small program and send + the corresponding source to us at zlib@gzip.org . Do not send + multi-megabyte data files without prior agreement. + + 9. Why do I get "undefined reference to gzputc"? + + If "make test" produces something like + + example.o(.text+0x154): undefined reference to `gzputc' + + check that you don't have old files libz.* in /usr/lib, /usr/local/lib or + /usr/X11R6/lib. Remove any old versions, then do "make install". + +10. I need a Delphi interface to zlib. + + See the contrib/delphi directory in the zlib distribution. + +11. Can zlib handle .zip archives? + + See the directory contrib/minizip in the zlib distribution. + +12. Can zlib handle .Z files? + + No, sorry. You have to spawn an uncompress or gunzip subprocess, or adapt + the code of uncompress on your own. + +13. How can I make a Unix shared library? + + make clean + ./configure -s + make + +14. How do I install a shared zlib library on Unix? + + make install + + However, many flavors of Unix come with a shared zlib already installed. + Before going to the trouble of compiling a shared version of zlib and + trying to install it, you may want to check if it's already there! If you + can #include , it's there. The -lz option will probably link to it. + +15. I have a question about OttoPDF + + We are not the authors of OttoPDF. The real author is on the OttoPDF web + site Joel Hainley jhainley@myndkryme.com. + +16. Why does gzip give an error on a file I make with compress/deflate? + + The compress and deflate functions produce data in the zlib format, which + is different and incompatible with the gzip format. The gz* functions in + zlib on the other hand use the gzip format. Both the zlib and gzip + formats use the same compressed data format internally, but have different + headers and trailers around the compressed data. + +17. Ok, so why are there two different formats? + + The gzip format was designed to retain the directory information about + a single file, such as the name and last modification date. The zlib + format on the other hand was designed for in-memory and communication + channel applications, and has a much more compact header and trailer and + uses a faster integrity check than gzip. + +18. Well that's nice, but how do I make a gzip file in memory? + + You can request that deflate write the gzip format instead of the zlib + format using deflateInit2(). You can also request that inflate decode + the gzip format using inflateInit2(). Read zlib.h for more details. + + Note that you cannot specify special gzip header contents (e.g. a file + name or modification date), nor will inflate tell you what was in the + gzip header. If you need to customize the header or see what's in it, + you can use the raw deflate and inflate operations and the crc32() + function and roll your own gzip encoding and decoding. Read the gzip + RFC 1952 for details of the header and trailer format. + +19. Is zlib thread-safe? + + Yes. However any library routines that zlib uses and any application- + provided memory allocation routines must also be thread-safe. zlib's gz* + functions use stdio library routines, and most of zlib's functions use the + library memory allocation routines by default. zlib's Init functions allow + for the application to provide custom memory allocation routines. + + Of course, you should only operate on any given zlib or gzip stream from a + single thread at a time. + +20. Can I use zlib in my commercial application? + + Yes. Please read the license in zlib.h. + +21. Is zlib under the GNU license? + + No. Please read the license in zlib.h. + +22. The license says that altered source versions must be "plainly marked". So + what exactly do I need to do to meet that requirement? + + You need to change the ZLIB_VERSION and ZLIB_VERNUM #defines in zlib.h. In + particular, the final version number needs to be changed to "f", and an + identification string should be appended to ZLIB_VERSION. Version numbers + x.x.x.f are reserved for modifications to zlib by others than the zlib + maintainers. For example, if the version of the base zlib you are altering + is "1.2.3.4", then in zlib.h you should change ZLIB_VERNUM to 0x123f, and + ZLIB_VERSION to something like "1.2.3.f-zachary-mods-v3". You can also + update the version strings in deflate.c and inftrees.c. + + For altered source distributions, you should also note the origin and + nature of the changes in zlib.h, as well as in ChangeLog and README, along + with the dates of the alterations. The origin should include at least your + name (or your company's name), and an email address to contact for help or + issues with the library. + + Note that distributing a compiled zlib library along with zlib.h and + zconf.h is also a source distribution, and so you should change + ZLIB_VERSION and ZLIB_VERNUM and note the origin and nature of the changes + in zlib.h as you would for a full source distribution. + +23. Will zlib work on a big-endian or little-endian architecture, and can I + exchange compressed data between them? + + Yes and yes. + +24. Will zlib work on a 64-bit machine? + + It should. It has been tested on 64-bit machines, and has no dependence + on any data types being limited to 32-bits in length. If you have any + difficulties, please provide a complete problem report to zlib@gzip.org + +25. Will zlib decompress data from the PKWare Data Compression Library? + + No. The PKWare DCL uses a completely different compressed data format + than does PKZIP and zlib. However, you can look in zlib's contrib/blast + directory for a possible solution to your problem. + +26. Can I access data randomly in a compressed stream? + + No, not without some preparation. If when compressing you periodically + use Z_FULL_FLUSH, carefully write all the pending data at those points, + and keep an index of those locations, then you can start decompression + at those points. You have to be careful to not use Z_FULL_FLUSH too + often, since it can significantly degrade compression. + +27. Does zlib work on MVS, OS/390, CICS, etc.? + + We don't know for sure. We have heard occasional reports of success on + these systems. If you do use it on one of these, please provide us with + a report, instructions, and patches that we can reference when we get + these questions. Thanks. + +28. Is there some simpler, easier to read version of inflate I can look at + to understand the deflate format? + + First off, you should read RFC 1951. Second, yes. Look in zlib's + contrib/puff directory. + +29. Does zlib infringe on any patents? + + As far as we know, no. In fact, that was originally the whole point behind + zlib. Look here for some more information: + + http://www.gzip.org/#faq11 + +30. Can zlib work with greater than 4 GB of data? + + Yes. inflate() and deflate() will process any amount of data correctly. + Each call of inflate() or deflate() is limited to input and output chunks + of the maximum value that can be stored in the compiler's "unsigned int" + type, but there is no limit to the number of chunks. Note however that the + strm.total_in and strm_total_out counters may be limited to 4 GB. These + counters are provided as a convenience and are not used internally by + inflate() or deflate(). The application can easily set up its own counters + updated after each call of inflate() or deflate() to count beyond 4 GB. + compress() and uncompress() may be limited to 4 GB, since they operate in a + single call. gzseek() and gztell() may be limited to 4 GB depending on how + zlib is compiled. See the zlibCompileFlags() function in zlib.h. + + The word "may" appears several times above since there is a 4 GB limit + only if the compiler's "long" type is 32 bits. If the compiler's "long" + type is 64 bits, then the limit is 16 exabytes. + +31. Does zlib have any security vulnerabilities? + + The only one that we are aware of is potentially in gzprintf(). If zlib + is compiled to use sprintf() or vsprintf(), then there is no protection + against a buffer overflow of a 4K string space, other than the caller of + gzprintf() assuring that the output will not exceed 4K. On the other + hand, if zlib is compiled to use snprintf() or vsnprintf(), which should + normally be the case, then there is no vulnerability. The ./configure + script will display warnings if an insecure variation of sprintf() will + be used by gzprintf(). Also the zlibCompileFlags() function will return + information on what variant of sprintf() is used by gzprintf(). + + If you don't have snprintf() or vsnprintf() and would like one, you can + find a portable implementation here: + + http://www.ijs.si/software/snprintf/ + + Note that you should be using the most recent version of zlib. Versions + 1.1.3 and before were subject to a double-free vulnerability. + +32. Is there a Java version of zlib? + + Probably what you want is to use zlib in Java. zlib is already included + as part of the Java SDK in the java.util.zip package. If you really want + a version of zlib written in the Java language, look on the zlib home + page for links: http://www.zlib.org/ + +33. I get this or that compiler or source-code scanner warning when I crank it + up to maximally-pendantic. Can't you guys write proper code? + + Many years ago, we gave up attempting to avoid warnings on every compiler + in the universe. It just got to be a waste of time, and some compilers + were downright silly. So now, we simply make sure that the code always + works. + +34. Will zlib read the (insert any ancient or arcane format here) compressed + data format? + + Probably not. Look in the comp.compression FAQ for pointers to various + formats and associated software. + +35. How can I encrypt/decrypt zip files with zlib? + + zlib doesn't support encryption. The original PKZIP encryption is very weak + and can be broken with freely available programs. To get strong encryption, + use gpg ( http://www.gnupg.org/ ) which already includes zlib compression. + For PKZIP compatible "encryption", look at http://www.info-zip.org/ + +36. What's the difference between the "gzip" and "deflate" HTTP 1.1 encodings? + + "gzip" is the gzip format, and "deflate" is the zlib format. They should + probably have called the second one "zlib" instead to avoid confusion + with the raw deflate compressed data format. While the HTTP 1.1 RFC 2616 + correctly points to the zlib specification in RFC 1950 for the "deflate" + transfer encoding, there have been reports of servers and browsers that + incorrectly produce or expect raw deflate data per the deflate + specficiation in RFC 1951, most notably Microsoft. So even though the + "deflate" transfer encoding using the zlib format would be the more + efficient approach (and in fact exactly what the zlib format was designed + for), using the "gzip" transfer encoding is probably more reliable due to + an unfortunate choice of name on the part of the HTTP 1.1 authors. + + Bottom line: use the gzip format for HTTP 1.1 encoding. + +37. Does zlib support the new "Deflate64" format introduced by PKWare? + + No. PKWare has apparently decided to keep that format proprietary, since + they have not documented it as they have previous compression formats. + In any case, the compression improvements are so modest compared to other + more modern approaches, that it's not worth the effort to implement. + +38. Can you please sign these lengthy legal documents and fax them back to us + so that we can use your software in our product? + + No. Go away. Shoo. diff --git a/com32/lib/zlib/README b/com32/lib/zlib/README new file mode 100644 index 0000000..0f12054 --- /dev/null +++ b/com32/lib/zlib/README @@ -0,0 +1,126 @@ +ZLIB DATA COMPRESSION LIBRARY + +zlib 1.2.1 is a general purpose data compression library. All the code is +thread safe. The data format used by the zlib library is described by RFCs +(Request for Comments) 1950 to 1952 in the files +http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format) +and rfc1952.txt (gzip format). These documents are also available in other +formats from ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html + +All functions of the compression library are documented in the file zlib.h +(volunteer to write man pages welcome, contact zlib@gzip.org). A usage example +of the library is given in the file example.c which also tests that the library +is working correctly. Another example is given in the file minigzip.c. The +compression library itself is composed of all source files except example.c and +minigzip.c. + +To compile all files and run the test program, follow the instructions given at +the top of Makefile. In short "make test; make install" should work for most +machines. For Unix: "./configure; make test; make install" For MSDOS, use one +of the special makefiles such as Makefile.msc. For VMS, use Make_vms.com or +descrip.mms. + +Questions about zlib should be sent to , or to Gilles Vollant + for the Windows DLL version. The zlib home page is +http://www.zlib.org or http://www.gzip.org/zlib/ Before reporting a problem, +please check this site to verify that you have the latest version of zlib; +otherwise get the latest version and check whether the problem still exists or +not. + +PLEASE read the zlib FAQ http://www.gzip.org/zlib/zlib_faq.html before asking +for help. + +Mark Nelson wrote an article about zlib for the Jan. 1997 +issue of Dr. Dobb's Journal; a copy of the article is available in +http://dogma.net/markn/articles/zlibtool/zlibtool.htm + +The changes made in version 1.2.1 are documented in the file ChangeLog. + +Unsupported third party contributions are provided in directory "contrib". + +A Java implementation of zlib is available in the Java Development Kit +http://java.sun.com/j2se/1.4.2/docs/api/java/util/zip/package-summary.html +See the zlib home page http://www.zlib.org for details. + +A Perl interface to zlib written by Paul Marquess is in the +CPAN (Comprehensive Perl Archive Network) sites +http://www.cpan.org/modules/by-module/Compress/ + +A Python interface to zlib written by A.M. Kuchling is +available in Python 1.5 and later versions, see +http://www.python.org/doc/lib/module-zlib.html + +A zlib binding for TCL written by Andreas Kupries is +availlable at http://www.oche.de/~akupries/soft/trf/trf_zip.html + +An experimental package to read and write files in .zip format, written on top +of zlib by Gilles Vollant , is available in the +contrib/minizip directory of zlib. + + +Notes for some targets: + +- For Windows DLL versions, please see win32/DLL_FAQ.txt + +- For 64-bit Irix, deflate.c must be compiled without any optimization. With + -O, one libpng test fails. The test works in 32 bit mode (with the -n32 + compiler flag). The compiler bug has been reported to SGI. + +- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works + when compiled with cc. + +- On Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 is + necessary to get gzprintf working correctly. This is done by configure. + +- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with + other compilers. Use "make test" to check your compiler. + +- gzdopen is not supported on RISCOS, BEOS and by some Mac compilers. + +- For PalmOs, see http://palmzlib.sourceforge.net/ + +- When building a shared, i.e. dynamic library on Mac OS X, the library must be + installed before testing (do "make install" before "make test"), since the + library location is specified in the library. + + +Acknowledgments: + + The deflate format used by zlib was defined by Phil Katz. The deflate + and zlib specifications were written by L. Peter Deutsch. Thanks to all the + people who reported problems and suggested various improvements in zlib; + they are too numerous to cite here. + +Copyright notice: + + (C) 1995-2003 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + +If you use the zlib library in a product, we would appreciate *not* +receiving lengthy legal documents to sign. The sources are provided +for free but without warranty of any kind. The library has been +entirely written by Jean-loup Gailly and Mark Adler; it does not +include third-party code. + +If you redistribute modified sources, we would appreciate that you include +in the file ChangeLog history information documenting your changes. Please +read the FAQ for more information on the distribution of modified source +versions. diff --git a/com32/lib/zlib/adler32.c b/com32/lib/zlib/adler32.c new file mode 100644 index 0000000..296e45f --- /dev/null +++ b/com32/lib/zlib/adler32.c @@ -0,0 +1,73 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + + +#define ZLIB_INTERNAL +#include "zlib.h" + +#define BASE 65521UL /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +#ifdef NO_DIVIDE +# define MOD(a) \ + do { \ + if (a >= (BASE << 16)) a -= (BASE << 16); \ + if (a >= (BASE << 15)) a -= (BASE << 15); \ + if (a >= (BASE << 14)) a -= (BASE << 14); \ + if (a >= (BASE << 13)) a -= (BASE << 13); \ + if (a >= (BASE << 12)) a -= (BASE << 12); \ + if (a >= (BASE << 11)) a -= (BASE << 11); \ + if (a >= (BASE << 10)) a -= (BASE << 10); \ + if (a >= (BASE << 9)) a -= (BASE << 9); \ + if (a >= (BASE << 8)) a -= (BASE << 8); \ + if (a >= (BASE << 7)) a -= (BASE << 7); \ + if (a >= (BASE << 6)) a -= (BASE << 6); \ + if (a >= (BASE << 5)) a -= (BASE << 5); \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? (int)len : NMAX; + len -= k; + while (k >= 16) { + DO16(buf); + buf += 16; + k -= 16; + } + if (k != 0) do { + s1 += *buf++; + s2 += s1; + } while (--k); + MOD(s1); + MOD(s2); + } + return (s2 << 16) | s1; +} diff --git a/com32/lib/zlib/algorithm.txt b/com32/lib/zlib/algorithm.txt new file mode 100644 index 0000000..b022dde --- /dev/null +++ b/com32/lib/zlib/algorithm.txt @@ -0,0 +1,209 @@ +1. Compression algorithm (deflate) + +The deflation algorithm used by gzip (also zip and zlib) is a variation of +LZ77 (Lempel-Ziv 1977, see reference below). It finds duplicated strings in +the input data. The second occurrence of a string is replaced by a +pointer to the previous string, in the form of a pair (distance, +length). Distances are limited to 32K bytes, and lengths are limited +to 258 bytes. When a string does not occur anywhere in the previous +32K bytes, it is emitted as a sequence of literal bytes. (In this +description, `string' must be taken as an arbitrary sequence of bytes, +and is not restricted to printable characters.) + +Literals or match lengths are compressed with one Huffman tree, and +match distances are compressed with another tree. The trees are stored +in a compact form at the start of each block. The blocks can have any +size (except that the compressed data for one block must fit in +available memory). A block is terminated when deflate() determines that +it would be useful to start another block with fresh trees. (This is +somewhat similar to the behavior of LZW-based _compress_.) + +Duplicated strings are found using a hash table. All input strings of +length 3 are inserted in the hash table. A hash index is computed for +the next 3 bytes. If the hash chain for this index is not empty, all +strings in the chain are compared with the current input string, and +the longest match is selected. + +The hash chains are searched starting with the most recent strings, to +favor small distances and thus take advantage of the Huffman encoding. +The hash chains are singly linked. There are no deletions from the +hash chains, the algorithm simply discards matches that are too old. + +To avoid a worst-case situation, very long hash chains are arbitrarily +truncated at a certain length, determined by a runtime option (level +parameter of deflateInit). So deflate() does not always find the longest +possible match but generally finds a match which is long enough. + +deflate() also defers the selection of matches with a lazy evaluation +mechanism. After a match of length N has been found, deflate() searches for +a longer match at the next input byte. If a longer match is found, the +previous match is truncated to a length of one (thus producing a single +literal byte) and the process of lazy evaluation begins again. Otherwise, +the original match is kept, and the next match search is attempted only N +steps later. + +The lazy match evaluation is also subject to a runtime parameter. If +the current match is long enough, deflate() reduces the search for a longer +match, thus speeding up the whole process. If compression ratio is more +important than speed, deflate() attempts a complete second search even if +the first match is already long enough. + +The lazy match evaluation is not performed for the fastest compression +modes (level parameter 1 to 3). For these fast modes, new strings +are inserted in the hash table only when no match was found, or +when the match is not too long. This degrades the compression ratio +but saves time since there are both fewer insertions and fewer searches. + + +2. Decompression algorithm (inflate) + +2.1 Introduction + +The key question is how to represent a Huffman code (or any prefix code) so +that you can decode fast. The most important characteristic is that shorter +codes are much more common than longer codes, so pay attention to decoding the +short codes fast, and let the long codes take longer to decode. + +inflate() sets up a first level table that covers some number of bits of +input less than the length of longest code. It gets that many bits from the +stream, and looks it up in the table. The table will tell if the next +code is that many bits or less and how many, and if it is, it will tell +the value, else it will point to the next level table for which inflate() +grabs more bits and tries to decode a longer code. + +How many bits to make the first lookup is a tradeoff between the time it +takes to decode and the time it takes to build the table. If building the +table took no time (and if you had infinite memory), then there would only +be a first level table to cover all the way to the longest code. However, +building the table ends up taking a lot longer for more bits since short +codes are replicated many times in such a table. What inflate() does is +simply to make the number of bits in the first table a variable, and then +to set that variable for the maximum speed. + +For inflate, which has 286 possible codes for the literal/length tree, the size +of the first table is nine bits. Also the distance trees have 30 possible +values, and the size of the first table is six bits. Note that for each of +those cases, the table ended up one bit longer than the ``average'' code +length, i.e. the code length of an approximately flat code which would be a +little more than eight bits for 286 symbols and a little less than five bits +for 30 symbols. + + +2.2 More details on the inflate table lookup + +Ok, you want to know what this cleverly obfuscated inflate tree actually +looks like. You are correct that it's not a Huffman tree. It is simply a +lookup table for the first, let's say, nine bits of a Huffman symbol. The +symbol could be as short as one bit or as long as 15 bits. If a particular +symbol is shorter than nine bits, then that symbol's translation is duplicated +in all those entries that start with that symbol's bits. For example, if the +symbol is four bits, then it's duplicated 32 times in a nine-bit table. If a +symbol is nine bits long, it appears in the table once. + +If the symbol is longer than nine bits, then that entry in the table points +to another similar table for the remaining bits. Again, there are duplicated +entries as needed. The idea is that most of the time the symbol will be short +and there will only be one table look up. (That's whole idea behind data +compression in the first place.) For the less frequent long symbols, there +will be two lookups. If you had a compression method with really long +symbols, you could have as many levels of lookups as is efficient. For +inflate, two is enough. + +So a table entry either points to another table (in which case nine bits in +the above example are gobbled), or it contains the translation for the symbol +and the number of bits to gobble. Then you start again with the next +ungobbled bit. + +You may wonder: why not just have one lookup table for how ever many bits the +longest symbol is? The reason is that if you do that, you end up spending +more time filling in duplicate symbol entries than you do actually decoding. +At least for deflate's output that generates new trees every several 10's of +kbytes. You can imagine that filling in a 2^15 entry table for a 15-bit code +would take too long if you're only decoding several thousand symbols. At the +other extreme, you could make a new table for every bit in the code. In fact, +that's essentially a Huffman tree. But then you spend two much time +traversing the tree while decoding, even for short symbols. + +So the number of bits for the first lookup table is a trade of the time to +fill out the table vs. the time spent looking at the second level and above of +the table. + +Here is an example, scaled down: + +The code being decoded, with 10 symbols, from 1 to 6 bits long: + +A: 0 +B: 10 +C: 1100 +D: 11010 +E: 11011 +F: 11100 +G: 11101 +H: 11110 +I: 111110 +J: 111111 + +Let's make the first table three bits long (eight entries): + +000: A,1 +001: A,1 +010: A,1 +011: A,1 +100: B,2 +101: B,2 +110: -> table X (gobble 3 bits) +111: -> table Y (gobble 3 bits) + +Each entry is what the bits decode as and how many bits that is, i.e. how +many bits to gobble. Or the entry points to another table, with the number of +bits to gobble implicit in the size of the table. + +Table X is two bits long since the longest code starting with 110 is five bits +long: + +00: C,1 +01: C,1 +10: D,2 +11: E,2 + +Table Y is three bits long since the longest code starting with 111 is six +bits long: + +000: F,2 +001: F,2 +010: G,2 +011: G,2 +100: H,2 +101: H,2 +110: I,3 +111: J,3 + +So what we have here are three tables with a total of 20 entries that had to +be constructed. That's compared to 64 entries for a single table. Or +compared to 16 entries for a Huffman tree (six two entry tables and one four +entry table). Assuming that the code ideally represents the probability of +the symbols, it takes on the average 1.25 lookups per symbol. That's compared +to one lookup for the single table, or 1.66 lookups per symbol for the +Huffman tree. + +There, I think that gives you a picture of what's going on. For inflate, the +meaning of a particular symbol is often more than just a letter. It can be a +byte (a "literal"), or it can be either a length or a distance which +indicates a base value and a number of bits to fetch after the code that is +added to the base value. Or it might be the special end-of-block code. The +data structures created in inftrees.c try to encode all that information +compactly in the tables. + + +Jean-loup Gailly Mark Adler +jloup@gzip.org madler@alumni.caltech.edu + + +References: + +[LZ77] Ziv J., Lempel A., ``A Universal Algorithm for Sequential Data +Compression,'' IEEE Transactions on Information Theory, Vol. 23, No. 3, +pp. 337-343. + +``DEFLATE Compressed Data Format Specification'' available in +http://www.ietf.org/rfc/rfc1951.txt diff --git a/com32/lib/zlib/compress.c b/com32/lib/zlib/compress.c new file mode 100644 index 0000000..8cc4167 --- /dev/null +++ b/com32/lib/zlib/compress.c @@ -0,0 +1,78 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) + uLong sourceLen; +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11; +} diff --git a/com32/lib/zlib/crc32.c b/com32/lib/zlib/crc32.c new file mode 100644 index 0000000..447f138 --- /dev/null +++ b/com32/lib/zlib/crc32.c @@ -0,0 +1,310 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results about a factor + * of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Find a four-byte integer type for crc32_little() and crc32_big(). */ +#ifndef NOBYFOUR +# ifdef STDC /* need ANSI C limits.h to determine sizes */ +# include +# define BYFOUR +# if (UINT_MAX == 0xffffffffUL) + typedef unsigned int u4; +# else +# if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long u4; +# else +# if (USHRT_MAX == 0xffffffffUL) + typedef unsigned short u4; +# else +# undef BYFOUR /* can't find a four-byte integer type! */ +# endif +# endif +# endif +# endif /* STDC */ +#endif /* !NOBYFOUR */ + +/* Definitions for doing the crc four data bytes at a time. */ +#ifdef BYFOUR +# define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \ + (((w)&0xff00)<<8)+(((w)&0xff)<<24)) + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +#ifdef DYNAMIC_CRC_TABLE + +local int crc_table_empty = 1; +local unsigned long FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const unsigned long FAR *)); +#endif /* MAKECRCH */ + +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + unsigned long c; + int n, k; + unsigned long poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0UL; + for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) + poly |= 1UL << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (unsigned long)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, and + then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = REV(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = REV(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const unsigned long FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const unsigned long FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const unsigned long FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const unsigned long FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + u4 endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = (u4)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const u4 FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = REV((u4)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const u4 FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(REV(c)); +} + +#endif /* BYFOUR */ diff --git a/com32/lib/zlib/crc32.h b/com32/lib/zlib/crc32.h new file mode 100644 index 0000000..8053b61 --- /dev/null +++ b/com32/lib/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const unsigned long FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/com32/lib/zlib/deflate.c b/com32/lib/zlib/deflate.c new file mode 100644 index 0000000..feb1d16 --- /dev/null +++ b/com32/lib/zlib/deflate.c @@ -0,0 +1,1501 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://www.ietf.org/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.1 Copyright 1995-2003 Jean-loup Gailly "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifndef FASTEST +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif +#endif +local uInt longest_match_fast OF((deflate_state *s, IPos cur_match)); + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_RLE) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->wrap == 2 || + (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) + return Z_STREAM_ERROR; + + s = strm->state; + if (s->wrap) + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > MAX_DIST(s)) { + length = MAX_DIST(s); +#ifndef USE_DICT_HEAD + dictionary += dictLength - length; /* use the tail of the dictionary */ +#endif + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + strm->state->bi_valid = bits; + strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_RLE) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if (func != configuration_table[level].func && strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_PARTIAL_FLUSH); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds + * for every combination of windowBits and memLevel, as well as wrap. + * But even the conservative upper bound of about 14% expansion does not + * seem onerous for output buffer allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong destLen; + + /* conservative upper bound */ + destLen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11; + + /* if can't get parameters, return conservative bound */ + if (strm == Z_NULL || strm->state == Z_NULL) + return destLen; + + /* if not default parameters, return conservative bound */ + s = strm->state; + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return destLen; + + /* default settings: return tight bound for that case */ + return compressBound(sourceLen); +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_FINISH || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, 255); + s->status = BUSY_STATE; + strm->adler = crc32(0L, Z_NULL, 0); + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = (*(configuration_table[s->level].func))(s, flush); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + *dest = *source; + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + *ds = *ss; + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, strm->next_in, len); + } +#endif + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2: + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ +#endif /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for level == 1 or strategy == Z_RLE only + */ +local uInt longest_match_fast(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, eof) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (eof)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, eof) { \ + FLUSH_BLOCK_ONLY(s, eof); \ + if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ +#ifdef FASTEST + if ((s->strategy < Z_HUFFMAN_ONLY) || + (s->strategy == Z_RLE && s->strstart - hash_head == 1)) { + s->match_length = longest_match_fast (s, hash_head); + } +#else + if (s->strategy < Z_HUFFMAN_ONLY) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } +#endif + /* longest_match() or longest_match_fast() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy < Z_HUFFMAN_ONLY) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } + /* longest_match() or longest_match_fast() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif /* FASTEST */ diff --git a/com32/lib/zlib/deflate.h b/com32/lib/zlib/deflate.h new file mode 100644 index 0000000..e83ed28 --- /dev/null +++ b/com32/lib/zlib/deflate.h @@ -0,0 +1,325 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2002 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + int pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + Byte data_type; /* UNKNOWN, BINARY or ASCII */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + + /* in trees.c */ +void _tr_init OF((deflate_state *s)); +int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_align OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch _length_code[]; + extern uch _dist_code[]; +#else + extern const uch _length_code[]; + extern const uch _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/com32/lib/zlib/example.c b/com32/lib/zlib/example.c new file mode 100644 index 0000000..14614b7 --- /dev/null +++ b/com32/lib/zlib/example.c @@ -0,0 +1,566 @@ +/* example.c -- usage example of the zlib compression library + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + + +#include +#include "zlib.h" + +#ifdef STDC +# include +# include +#else + extern void exit OF((int)); +#endif + +#if defined(VMS) || defined(RISCOS) +# define TESTFILE "foo-gz" +#else +# define TESTFILE "foo.gz" +#endif + +#define CHECK_ERR(err, msg) { \ + if (err != Z_OK) { \ + fprintf(stderr, "%s error: %d\n", msg, err); \ + exit(1); \ + } \ +} + +const char hello[] = "hello, hello!"; +/* "hello world" would be more standard, but the repeated "hello" + * stresses the compression code better, sorry... + */ + +const char dictionary[] = "hello"; +uLong dictId; /* Adler32 value of the dictionary */ + +void test_compress OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_gzio OF((const char *fname, + Byte *uncompr, uLong uncomprLen)); +void test_deflate OF((Byte *compr, uLong comprLen)); +void test_inflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_large_deflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_large_inflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_flush OF((Byte *compr, uLong *comprLen)); +void test_sync OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_dict_deflate OF((Byte *compr, uLong comprLen)); +void test_dict_inflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +int main OF((int argc, char *argv[])); + +/* =========================================================================== + * Test compress() and uncompress() + */ +void test_compress(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + uLong len = (uLong)strlen(hello)+1; + + err = compress(compr, &comprLen, (const Bytef*)hello, len); + CHECK_ERR(err, "compress"); + + strcpy((char*)uncompr, "garbage"); + + err = uncompress(uncompr, &uncomprLen, compr, comprLen); + CHECK_ERR(err, "uncompress"); + + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad uncompress\n"); + exit(1); + } else { + printf("uncompress(): %s\n", (char *)uncompr); + } +} + +/* =========================================================================== + * Test read/write of .gz files + */ +void test_gzio(fname, uncompr, uncomprLen) + const char *fname; /* compressed file name */ + Byte *uncompr; + uLong uncomprLen; +{ +#ifdef NO_GZCOMPRESS + fprintf(stderr, "NO_GZCOMPRESS -- gz* functions cannot compress\n"); +#else + int err; + int len = (int)strlen(hello)+1; + gzFile file; + z_off_t pos; + + file = gzopen(fname, "wb"); + if (file == NULL) { + fprintf(stderr, "gzopen error\n"); + exit(1); + } + gzputc(file, 'h'); + if (gzputs(file, "ello") != 4) { + fprintf(stderr, "gzputs err: %s\n", gzerror(file, &err)); + exit(1); + } + if (gzprintf(file, ", %s!", "hello") != 8) { + fprintf(stderr, "gzprintf err: %s\n", gzerror(file, &err)); + exit(1); + } + gzseek(file, 1L, SEEK_CUR); /* add one zero byte */ + gzclose(file); + + file = gzopen(fname, "rb"); + if (file == NULL) { + fprintf(stderr, "gzopen error\n"); + exit(1); + } + strcpy((char*)uncompr, "garbage"); + + if (gzread(file, uncompr, (unsigned)uncomprLen) != len) { + fprintf(stderr, "gzread err: %s\n", gzerror(file, &err)); + exit(1); + } + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad gzread: %s\n", (char*)uncompr); + exit(1); + } else { + printf("gzread(): %s\n", (char*)uncompr); + } + + pos = gzseek(file, -8L, SEEK_CUR); + if (pos != 6 || gztell(file) != pos) { + fprintf(stderr, "gzseek error, pos=%ld, gztell=%ld\n", + (long)pos, (long)gztell(file)); + exit(1); + } + + if (gzgetc(file) != ' ') { + fprintf(stderr, "gzgetc error\n"); + exit(1); + } + + if (gzungetc(' ', file) != ' ') { + fprintf(stderr, "gzungetc error\n"); + exit(1); + } + + gzgets(file, (char*)uncompr, (int)uncomprLen); + if (strlen((char*)uncompr) != 7) { /* " hello!" */ + fprintf(stderr, "gzgets err after gzseek: %s\n", gzerror(file, &err)); + exit(1); + } + if (strcmp((char*)uncompr, hello + 6)) { + fprintf(stderr, "bad gzgets after gzseek\n"); + exit(1); + } else { + printf("gzgets() after gzseek: %s\n", (char*)uncompr); + } + + gzclose(file); +#endif +} + +/* =========================================================================== + * Test deflate() with small buffers + */ +void test_deflate(compr, comprLen) + Byte *compr; + uLong comprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + uLong len = (uLong)strlen(hello)+1; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION); + CHECK_ERR(err, "deflateInit"); + + c_stream.next_in = (Bytef*)hello; + c_stream.next_out = compr; + + while (c_stream.total_in != len && c_stream.total_out < comprLen) { + c_stream.avail_in = c_stream.avail_out = 1; /* force small buffers */ + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + } + /* Finish the stream, still forcing small buffers: */ + for (;;) { + c_stream.avail_out = 1; + err = deflate(&c_stream, Z_FINISH); + if (err == Z_STREAM_END) break; + CHECK_ERR(err, "deflate"); + } + + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); +} + +/* =========================================================================== + * Test inflate() with small buffers + */ +void test_inflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = 0; + d_stream.next_out = uncompr; + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + while (d_stream.total_out < uncomprLen && d_stream.total_in < comprLen) { + d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */ + err = inflate(&d_stream, Z_NO_FLUSH); + if (err == Z_STREAM_END) break; + CHECK_ERR(err, "inflate"); + } + + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad inflate\n"); + exit(1); + } else { + printf("inflate(): %s\n", (char *)uncompr); + } +} + +/* =========================================================================== + * Test deflate() with large buffers and dynamic change of compression level + */ +void test_large_deflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_BEST_SPEED); + CHECK_ERR(err, "deflateInit"); + + c_stream.next_out = compr; + c_stream.avail_out = (uInt)comprLen; + + /* At this point, uncompr is still mostly zeroes, so it should compress + * very well: + */ + c_stream.next_in = uncompr; + c_stream.avail_in = (uInt)uncomprLen; + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + if (c_stream.avail_in != 0) { + fprintf(stderr, "deflate not greedy\n"); + exit(1); + } + + /* Feed in already compressed data and switch to no compression: */ + deflateParams(&c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY); + c_stream.next_in = compr; + c_stream.avail_in = (uInt)comprLen/2; + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + + /* Switch back to compressing mode: */ + deflateParams(&c_stream, Z_BEST_COMPRESSION, Z_FILTERED); + c_stream.next_in = uncompr; + c_stream.avail_in = (uInt)uncomprLen; + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + + err = deflate(&c_stream, Z_FINISH); + if (err != Z_STREAM_END) { + fprintf(stderr, "deflate should report Z_STREAM_END\n"); + exit(1); + } + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); +} + +/* =========================================================================== + * Test inflate() with large buffers + */ +void test_large_inflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = (uInt)comprLen; + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + for (;;) { + d_stream.next_out = uncompr; /* discard the output */ + d_stream.avail_out = (uInt)uncomprLen; + err = inflate(&d_stream, Z_NO_FLUSH); + if (err == Z_STREAM_END) break; + CHECK_ERR(err, "large inflate"); + } + + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + if (d_stream.total_out != 2*uncomprLen + comprLen/2) { + fprintf(stderr, "bad large inflate: %ld\n", d_stream.total_out); + exit(1); + } else { + printf("large_inflate(): OK\n"); + } +} + +/* =========================================================================== + * Test deflate() with full flush + */ +void test_flush(compr, comprLen) + Byte *compr; + uLong *comprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + uInt len = (uInt)strlen(hello)+1; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION); + CHECK_ERR(err, "deflateInit"); + + c_stream.next_in = (Bytef*)hello; + c_stream.next_out = compr; + c_stream.avail_in = 3; + c_stream.avail_out = (uInt)*comprLen; + err = deflate(&c_stream, Z_FULL_FLUSH); + CHECK_ERR(err, "deflate"); + + compr[3]++; /* force an error in first compressed block */ + c_stream.avail_in = len - 3; + + err = deflate(&c_stream, Z_FINISH); + if (err != Z_STREAM_END) { + CHECK_ERR(err, "deflate"); + } + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); + + *comprLen = c_stream.total_out; +} + +/* =========================================================================== + * Test inflateSync() + */ +void test_sync(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = 2; /* just read the zlib header */ + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + d_stream.next_out = uncompr; + d_stream.avail_out = (uInt)uncomprLen; + + inflate(&d_stream, Z_NO_FLUSH); + CHECK_ERR(err, "inflate"); + + d_stream.avail_in = (uInt)comprLen-2; /* read all compressed data */ + err = inflateSync(&d_stream); /* but skip the damaged part */ + CHECK_ERR(err, "inflateSync"); + + err = inflate(&d_stream, Z_FINISH); + if (err != Z_DATA_ERROR) { + fprintf(stderr, "inflate should report DATA_ERROR\n"); + /* Because of incorrect adler32 */ + exit(1); + } + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + printf("after inflateSync(): hel%s\n", (char *)uncompr); +} + +/* =========================================================================== + * Test deflate() with preset dictionary + */ +void test_dict_deflate(compr, comprLen) + Byte *compr; + uLong comprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_BEST_COMPRESSION); + CHECK_ERR(err, "deflateInit"); + + err = deflateSetDictionary(&c_stream, + (const Bytef*)dictionary, sizeof(dictionary)); + CHECK_ERR(err, "deflateSetDictionary"); + + dictId = c_stream.adler; + c_stream.next_out = compr; + c_stream.avail_out = (uInt)comprLen; + + c_stream.next_in = (Bytef*)hello; + c_stream.avail_in = (uInt)strlen(hello)+1; + + err = deflate(&c_stream, Z_FINISH); + if (err != Z_STREAM_END) { + fprintf(stderr, "deflate should report Z_STREAM_END\n"); + exit(1); + } + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); +} + +/* =========================================================================== + * Test inflate() with a preset dictionary + */ +void test_dict_inflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = (uInt)comprLen; + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + d_stream.next_out = uncompr; + d_stream.avail_out = (uInt)uncomprLen; + + for (;;) { + err = inflate(&d_stream, Z_NO_FLUSH); + if (err == Z_STREAM_END) break; + if (err == Z_NEED_DICT) { + if (d_stream.adler != dictId) { + fprintf(stderr, "unexpected dictionary"); + exit(1); + } + err = inflateSetDictionary(&d_stream, (const Bytef*)dictionary, + sizeof(dictionary)); + } + CHECK_ERR(err, "inflate with dict"); + } + + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad inflate with dict\n"); + exit(1); + } else { + printf("inflate with dictionary: %s\n", (char *)uncompr); + } +} + +/* =========================================================================== + * Usage: example [output.gz [input.gz]] + */ + +int main(argc, argv) + int argc; + char *argv[]; +{ + Byte *compr, *uncompr; + uLong comprLen = 10000*sizeof(int); /* don't overflow on MSDOS */ + uLong uncomprLen = comprLen; + static const char* myVersion = ZLIB_VERSION; + + if (zlibVersion()[0] != myVersion[0]) { + fprintf(stderr, "incompatible zlib version\n"); + exit(1); + + } else if (strcmp(zlibVersion(), ZLIB_VERSION) != 0) { + fprintf(stderr, "warning: different zlib version\n"); + } + + printf("zlib version %s = 0x%04x, compile flags = 0x%lx\n", + ZLIB_VERSION, ZLIB_VERNUM, zlibCompileFlags()); + + compr = (Byte*)calloc((uInt)comprLen, 1); + uncompr = (Byte*)calloc((uInt)uncomprLen, 1); + /* compr and uncompr are cleared to avoid reading uninitialized + * data and to ensure that uncompr compresses well. + */ + if (compr == Z_NULL || uncompr == Z_NULL) { + printf("out of memory\n"); + exit(1); + } + test_compress(compr, comprLen, uncompr, uncomprLen); + + test_gzio((argc > 1 ? argv[1] : TESTFILE), + uncompr, uncomprLen); + + test_deflate(compr, comprLen); + test_inflate(compr, comprLen, uncompr, uncomprLen); + + test_large_deflate(compr, comprLen, uncompr, uncomprLen); + test_large_inflate(compr, comprLen, uncompr, uncomprLen); + + test_flush(compr, &comprLen); + test_sync(compr, comprLen, uncompr, uncomprLen); + comprLen = uncomprLen; + + test_dict_deflate(compr, comprLen); + test_dict_inflate(compr, comprLen, uncompr, uncomprLen); + + free(compr); + free(uncompr); + + return 0; +} diff --git a/com32/lib/zlib/gzio.c b/com32/lib/zlib/gzio.c new file mode 100644 index 0000000..9bc8d34 --- /dev/null +++ b/com32/lib/zlib/gzio.c @@ -0,0 +1,1017 @@ +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_GZCOMPRESS to avoid the compression code. + */ + + +#include + +#include "zutil.h" + +#ifdef NO_DEFLATE /* for compatiblity with old definition */ +# define NO_GZCOMPRESS +#endif + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +#ifndef Z_BUFSIZE +# ifdef MAXSEG_64K +# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ +# else +# define Z_BUFSIZE 16384 +# endif +#endif +#ifndef Z_PRINTF_BUFSIZE +# define Z_PRINTF_BUFSIZE 4096 +#endif + +#ifdef __MVS__ +# pragma map (fdopen , "\174\174FDOPEN") + FILE *fdopen(int, const char *); +#endif + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern void free OF((voidpf ptr)); +#endif + +#define ALLOC(size) malloc(size) +#define TRYFREE(p) {if (p) free(p);} + +static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +typedef struct gz_stream { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + FILE *file; /* .gz file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + char *path; /* path name for debugging only */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + z_off_t start; /* start of compressed data in file (header skipped) */ + z_off_t in; /* bytes into deflate or inflate */ + z_off_t out; /* bytes out of deflate or inflate */ + int back; /* one character push-back */ + int last; /* true if push-back is last character */ +} gz_stream; + + +local gzFile gz_open OF((const char *path, const char *mode, int fd)); +local int do_flush OF((gzFile file, int flush)); +local int get_byte OF((gz_stream *s)); +local void check_header OF((gz_stream *s)); +local int destroy OF((gz_stream *s)); +local void putLong OF((FILE *file, uLong x)); +local uLong getLong OF((gz_stream *s)); + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb"). The file is given either by file descriptor + or path name (if fd == -1). + gz_open returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ +local gzFile gz_open (path, mode, fd) + const char *path; + const char *mode; + int fd; +{ + int err; + int level = Z_DEFAULT_COMPRESSION; /* compression level */ + int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ + char *p = (char*)mode; + gz_stream *s; + char fmode[80]; /* copy of mode, without the compression level */ + char *m = fmode; + + if (!path || !mode) return Z_NULL; + + s = (gz_stream *)ALLOC(sizeof(gz_stream)); + if (!s) return Z_NULL; + + s->stream.zalloc = (alloc_func)0; + s->stream.zfree = (free_func)0; + s->stream.opaque = (voidpf)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->file = NULL; + s->z_err = Z_OK; + s->z_eof = 0; + s->in = 0; + s->out = 0; + s->back = EOF; + s->crc = crc32(0L, Z_NULL, 0); + s->msg = NULL; + s->transparent = 0; + + s->path = (char*)ALLOC(strlen(path)+1); + if (s->path == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + strcpy(s->path, path); /* do this early for debugging */ + + s->mode = '\0'; + do { + if (*p == 'r') s->mode = 'r'; + if (*p == 'w' || *p == 'a') s->mode = 'w'; + if (*p >= '0' && *p <= '9') { + level = *p - '0'; + } else if (*p == 'f') { + strategy = Z_FILTERED; + } else if (*p == 'h') { + strategy = Z_HUFFMAN_ONLY; + } else if (*p == 'R') { + strategy = Z_RLE; + } else { + *m++ = *p; /* copy the mode */ + } + } while (*p++ && m != fmode + sizeof(fmode)); + if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateInit2(&(s->stream), level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); +#endif + if (err != Z_OK || s->outbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } else { + s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); + + err = inflateInit2(&(s->stream), -MAX_WBITS); + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + if (err != Z_OK || s->inbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } + s->stream.avail_out = Z_BUFSIZE; + + errno = 0; + s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode); + + if (s->file == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + if (s->mode == 'w') { + /* Write a very simple .gz header: + */ + fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + s->start = 10L; + /* We use 10L instead of ftell(s->file) to because ftell causes an + * fflush on some systems. This version of the library doesn't use + * start anyway in write mode, so this initialization is not + * necessary. + */ + } else { + check_header(s); /* skip the .gz header */ + s->start = ftell(s->file) - s->stream.avail_in; + } + + return (gzFile)s; +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. +*/ +gzFile ZEXPORT gzopen (path, mode) + const char *path; + const char *mode; +{ + return gz_open (path, mode, -1); +} + +/* =========================================================================== + Associate a gzFile with the file descriptor fd. fd is not dup'ed here + to mimic the behavio(u)r of fdopen. +*/ +gzFile ZEXPORT gzdopen (fd, mode) + int fd; + const char *mode; +{ + char name[20]; + + if (fd < 0) return (gzFile)Z_NULL; + sprintf(name, "", fd); /* for debugging */ + + return gz_open (name, mode, fd); +} + +/* =========================================================================== + * Update the compression level and strategy + */ +int ZEXPORT gzsetparams (file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + /* Make room to allow flushing */ + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + } + s->stream.avail_out = Z_BUFSIZE; + } + + return deflateParams (&(s->stream), level, strategy); +} + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ +local int get_byte(s) + gz_stream *s; +{ + if (s->z_eof) return EOF; + if (s->stream.avail_in == 0) { + errno = 0; + s->stream.avail_in = fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + /* klibc hack */ + if (errno) s->z_err = Z_ERRNO; + return EOF; + } + s->stream.next_in = s->inbuf; + } + s->stream.avail_in--; + return *(s->stream.next_in)++; +} + +/* =========================================================================== + Check the gzip header of a gz_stream opened for reading. Set the stream + mode to transparent if the gzip magic header is not present; set s->err + to Z_DATA_ERROR if the magic header is present but the rest of the header + is incorrect. + IN assertion: the stream s has already been created sucessfully; + s->stream.avail_in is zero for the first time, but may be non-zero + for concatenated .gz files. +*/ +local void check_header(s) + gz_stream *s; +{ + int method; /* method byte */ + int flags; /* flags byte */ + uInt len; + int c; + + /* Assure two bytes in the buffer so we can peek ahead -- handle case + where first byte of header is at the end of the buffer after the last + gzip segment */ + len = s->stream.avail_in; + if (len < 2) { + if (len) s->inbuf[0] = s->stream.next_in[0]; + errno = 0; + len = fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file); + /* klibc hack */ + if (len == 0 && errno) s->z_err = Z_ERRNO; + s->stream.avail_in += len; + s->stream.next_in = s->inbuf; + if (s->stream.avail_in < 2) { + s->transparent = s->stream.avail_in; + return; + } + } + + /* Peek ahead to check the gzip magic header */ + if (s->stream.next_in[0] != gz_magic[0] || + s->stream.next_in[1] != gz_magic[1]) { + s->transparent = 1; + return; + } + s->stream.avail_in -= 2; + s->stream.next_in += 2; + + /* Check the rest of the gzip header */ + method = get_byte(s); + flags = get_byte(s); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) { + s->z_err = Z_DATA_ERROR; + return; + } + + /* Discard time, xflags and OS code: */ + for (len = 0; len < 6; len++) (void)get_byte(s); + + if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ + len = (uInt)get_byte(s); + len += ((uInt)get_byte(s))<<8; + /* len is garbage if EOF but the loop below will quit anyway */ + while (len-- != 0 && get_byte(s) != EOF) ; + } + if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ + for (len = 0; len < 2; len++) (void)get_byte(s); + } + s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; +} + + /* =========================================================================== + * Cleanup then free the given gz_stream. Return a zlib error code. + Try freeing in the reverse order of allocations. + */ +local int destroy (s) + gz_stream *s; +{ + int err = Z_OK; + + if (!s) return Z_STREAM_ERROR; + + TRYFREE(s->msg); + + if (s->stream.state != NULL) { + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateEnd(&(s->stream)); +#endif + } else if (s->mode == 'r') { + err = inflateEnd(&(s->stream)); + } + } + if (s->file != NULL && fclose(s->file)) { +#ifdef ESPIPE + if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */ +#endif + err = Z_ERRNO; + } + if (s->z_err < 0) err = s->z_err; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE(s->path); + TRYFREE(s); + return err; +} + +/* =========================================================================== + Reads the given number of uncompressed bytes from the compressed file. + gzread returns the number of bytes actually read (0 for end of file). +*/ +int ZEXPORT gzread (file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + Bytef *start = (Bytef*)buf; /* starting point for crc computation */ + Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ + + if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR; + + if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; + if (s->z_err == Z_STREAM_END) return 0; /* EOF */ + + next_out = (Byte*)buf; + s->stream.next_out = (Bytef*)buf; + s->stream.avail_out = len; + + if (s->stream.avail_out && s->back != EOF) { + *next_out++ = s->back; + s->stream.next_out++; + s->stream.avail_out--; + s->back = EOF; + s->out++; + if (s->last) { + s->z_err = Z_STREAM_END; + return 1; + } + } + + while (s->stream.avail_out != 0) { + + if (s->transparent) { + /* Copy first the lookahead bytes: */ + uInt n = s->stream.avail_in; + if (n > s->stream.avail_out) n = s->stream.avail_out; + if (n > 0) { + zmemcpy(s->stream.next_out, s->stream.next_in, n); + next_out += n; + s->stream.next_out = next_out; + s->stream.next_in += n; + s->stream.avail_out -= n; + s->stream.avail_in -= n; + } + if (s->stream.avail_out > 0) { + s->stream.avail_out -= fread(next_out, 1, s->stream.avail_out, + s->file); + } + len -= s->stream.avail_out; + s->in += len; + s->out += len; + if (len == 0) s->z_eof = 1; + return (int)len; + } + if (s->stream.avail_in == 0 && !s->z_eof) { + + errno = 0; + s->stream.avail_in = fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (errno) { + s->z_err = Z_ERRNO; + break; + } + } + s->stream.next_in = s->inbuf; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = inflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + + if (s->z_err == Z_STREAM_END) { + /* Check CRC and original size */ + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + start = s->stream.next_out; + + if (getLong(s) != s->crc) { + s->z_err = Z_DATA_ERROR; + } else { + (void)getLong(s); + /* The uncompressed length returned by above getlong() may be + * different from s->out in case of concatenated .gz files. + * Check for such files: + */ + check_header(s); + if (s->z_err == Z_OK) { + inflateReset(&(s->stream)); + s->crc = crc32(0L, Z_NULL, 0); + } + } + } + if (s->z_err != Z_OK || s->z_eof) break; + } + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + + return (int)(len - s->stream.avail_out); +} + + +/* =========================================================================== + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ +int ZEXPORT gzgetc(file) + gzFile file; +{ + unsigned char c; + + return gzread(file, &c, 1) == 1 ? c : -1; +} + + +/* =========================================================================== + Push one byte back onto the stream. +*/ +int ZEXPORT gzungetc(c, file) + int c; + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r' || c == EOF || s->back != EOF) return EOF; + s->back = c; + s->out--; + s->last = (s->z_err == Z_STREAM_END); + if (s->last) s->z_err = Z_OK; + s->z_eof = 0; + return c; +} + + +/* =========================================================================== + Reads bytes from the compressed file until len-1 characters are + read, or a newline character is read and transferred to buf, or an + end-of-file condition is encountered. The string is then terminated + with a null character. + gzgets returns buf, or Z_NULL in case of error. + + The current implementation is not optimized at all. +*/ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + char *b = buf; + if (buf == Z_NULL || len <= 0) return Z_NULL; + + while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ; + *buf = '\0'; + return b == buf && len > 0 ? Z_NULL : b; +} + + +#ifndef NO_GZCOMPRESS +/* =========================================================================== + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of bytes actually written (0 in case of error). +*/ +int ZEXPORT gzwrite (file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.next_in = (Bytef*)buf; + s->stream.avail_in = len; + + while (s->stream.avail_in != 0) { + + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + break; + } + s->stream.avail_out = Z_BUFSIZE; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + if (s->z_err != Z_OK) break; + } + s->crc = crc32(s->crc, (const Bytef *)buf, len); + + return (int)(len - s->stream.avail_in); +} + + +/* =========================================================================== + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ +#ifdef STDC +#include + +int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...) +{ + char buf[Z_PRINTF_BUFSIZE]; + va_list va; + int len; + + buf[sizeof(buf) - 1] = 0; + va_start(va, format); +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf(buf, format, va); + va_end(va); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = vsprintf(buf, format, va); + va_end(va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf(buf, sizeof(buf), format, va); + va_end(va); + len = strlen(buf); +# else + len = vsnprintf(buf, sizeof(buf), format, va); + va_end(va); +# endif +#endif + if (len <= 0 || len >= (int)sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, (unsigned)len); +} +#else /* not ANSI C */ + +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + char buf[Z_PRINTF_BUFSIZE]; + int len; + + buf[sizeof(buf) - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(buf); +# else + len = snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#endif + if (len <= 0 || len >= sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, len); +} +#endif + +/* =========================================================================== + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned char cc = (unsigned char) c; /* required for big endian systems */ + + return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1; +} + + +/* =========================================================================== + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ +int ZEXPORT gzputs(file, s) + gzFile file; + const char *s; +{ + return gzwrite(file, (char*)s, (unsigned)strlen(s)); +} + + +/* =========================================================================== + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. +*/ +local int do_flush (file, flush) + gzFile file; + int flush; +{ + uInt len; + int done = 0; + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) { + if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) { + s->z_err = Z_ERRNO; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + } + if (done) break; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), flush); + s->out -= s->stream.avail_out; + + /* Ignore the second of two consecutive flushes: */ + if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; + } + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +int ZEXPORT gzflush (file, flush) + gzFile file; + int flush; +{ + gz_stream *s = (gz_stream*)file; + int err = do_flush (file, flush); + + if (err) return err; + fflush(s->file); + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} +#endif /* NO_GZCOMPRESS */ + +/* =========================================================================== + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error. + SEEK_END is not implemented, returns error. + In this version of the library, gzseek can be extremely slow. +*/ +#if 0 /* COM32: seek not supported */ + +z_off_t ZEXPORT gzseek (file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || whence == SEEK_END || + s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) { + return -1L; + } + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return -1L; +#else + if (whence == SEEK_SET) { + offset -= s->in; + } + if (offset < 0) return -1L; + + /* At this point, offset is the number of zero bytes to write. */ + if (s->inbuf == Z_NULL) { + s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */ + if (s->inbuf == Z_NULL) return -1L; + zmemzero(s->inbuf, Z_BUFSIZE); + } + while (offset > 0) { + uInt size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (uInt)offset; + + size = gzwrite(file, s->inbuf, size); + if (size == 0) return -1L; + + offset -= size; + } + return s->in; +#endif + } + /* Rest of function is for reading only */ + + /* compute absolute position */ + if (whence == SEEK_CUR) { + offset += s->out; + } + if (offset < 0) return -1L; + + if (s->transparent) { + /* map to fseek */ + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + if (fseek(s->file, offset, SEEK_SET) < 0) return -1L; + + s->in = s->out = offset; + return offset; + } + + /* For a negative seek, rewind and use positive seek */ + if (offset >= s->out) { + offset -= s->out; + } else if (gzrewind(file) < 0) { + return -1L; + } + /* offset is now the number of bytes to skip. */ + + if (offset != 0 && s->outbuf == Z_NULL) { + s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); + if (s->outbuf == Z_NULL) return -1L; + } + if (offset && s->back != EOF) { + s->back = EOF; + s->out++; + offset--; + if (s->last) s->z_err = Z_STREAM_END; + } + while (offset > 0) { + int size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (int)offset; + + size = gzread(file, s->outbuf, (uInt)size); + if (size <= 0) return -1L; + offset -= size; + } + return s->out; +} + +#endif + +/* =========================================================================== + Rewinds input file. +*/ +#if 0 /* COM32: seek not supported */ + +int ZEXPORT gzrewind (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return -1; + + s->z_err = Z_OK; + s->z_eof = 0; + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + s->crc = crc32(0L, Z_NULL, 0); + if (!s->transparent) (void)inflateReset(&s->stream); + s->in = 0; + s->out = 0; + return fseek(s->file, s->start, SEEK_SET); +} +#endif + +/* =========================================================================== + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. +*/ +#if 0 /* COM32: seek not supported */ + +z_off_t ZEXPORT gztell (file) + gzFile file; +{ + return gzseek(file, 0L, SEEK_CUR); +} +#endif + +/* =========================================================================== + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ +int ZEXPORT gzeof (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + /* With concatenated compressed files that can have embedded + * crc trailers, z_eof is no longer the only/best indicator of EOF + * on a gz_stream. Handle end-of-stream error explicitly here. + */ + if (s == NULL || s->mode != 'r') return 0; + if (s->z_eof) return 1; + return s->z_err == Z_STREAM_END; +} + +/* =========================================================================== + Outputs a long in LSB order to the given file +*/ +local void putLong (file, x) + FILE *file; + uLong x; +{ + int n; + for (n = 0; n < 4; n++) { + fputc((int)(x & 0xff), file); + x >>= 8; + } +} + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets z_err in case + of error. +*/ +local uLong getLong (s) + gz_stream *s; +{ + uLong x = (uLong)get_byte(s); + int c; + + x += ((uLong)get_byte(s))<<8; + x += ((uLong)get_byte(s))<<16; + c = get_byte(s); + if (c == EOF) s->z_err = Z_DATA_ERROR; + x += ((uLong)c)<<24; + return x; +} + +/* =========================================================================== + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. +*/ +int ZEXPORT gzclose (file) + gzFile file; +{ + int err; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return Z_STREAM_ERROR; +#else + err = do_flush (file, Z_FINISH); + if (err != Z_OK) return destroy((gz_stream*)file); + + putLong (s->file, s->crc); + putLong (s->file, (uLong)(s->in & 0xffffffff)); +#endif + } + return destroy((gz_stream*)file); +} + +/* =========================================================================== + Returns the error message for the last error which occured on the + given compressed file. errnum is set to zlib error number. If an + error occured in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ +const char * ZEXPORT gzerror (file, errnum) + gzFile file; + int *errnum; +{ + char *m; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) { + *errnum = Z_STREAM_ERROR; + return (const char*)ERR_MSG(Z_STREAM_ERROR); + } + *errnum = s->z_err; + if (*errnum == Z_OK) return (const char*)""; + + m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg); + + if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err); + + TRYFREE(s->msg); + s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3); + if (s->msg == Z_NULL) return (const char*)ERR_MSG(Z_MEM_ERROR); + strcpy(s->msg, s->path); + strcat(s->msg, ": "); + strcat(s->msg, m); + return (const char*)s->msg; +} + +/* =========================================================================== + Clear the error and end-of-file flags, and do the same for the real file. +*/ +void ZEXPORT gzclearerr (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return; + if (s->z_err != Z_STREAM_END) s->z_err = Z_OK; + s->z_eof = 0; + /* klibc hack */ + /* clearerr(s->file); */ +} diff --git a/com32/lib/zlib/infback.c b/com32/lib/zlib/infback.c new file mode 100644 index 0000000..110b03b --- /dev/null +++ b/com32/lib/zlib/infback.c @@ -0,0 +1,619 @@ +/* infback.c -- inflate using a call-back interface + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + This code is largely copied from inflate.c. Normally either infback.o or + inflate.o would be linked into an application--not both. The interface + with inffast.c is retained so that optimized assembler-coded versions of + inflate_fast() can be used with either inflate.c or infback.c. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); + +/* + strm provides memory allocation functions in zalloc and zfree, or + Z_NULL to use the library memory allocation functions. + + windowBits is in the range 8..15, and window is a user-supplied + window and output buffer that is 2**windowBits bytes. + */ +int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) +z_stream FAR *strm; +int windowBits; +unsigned char FAR *window; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL || window == Z_NULL || + windowBits < 8 || windowBits > 15) + return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *)ZALLOC(strm, 1, + sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (voidpf)state; + state->wbits = windowBits; + state->wsize = 1U << windowBits; + state->window = window; + state->write = 0; + state->whave = 0; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +/* Macros for inflateBack(): */ + +/* Load returned state from inflate_fast() */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Set state from registers for inflate_fast() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Assure that some input is available. If input is requested, but denied, + then return a Z_BUF_ERROR from inflateBack(). */ +#define PULL() \ + do { \ + if (have == 0) { \ + have = in(in_desc, &next); \ + if (have == 0) { \ + next = Z_NULL; \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflateBack() + with an error if there is no input available. */ +#define PULLBYTE() \ + do { \ + PULL(); \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflateBack() with + an error. */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Assure that some output space is available, by writing out the window + if it's full. If the write fails, return from inflateBack() with a + Z_BUF_ERROR. */ +#define ROOM() \ + do { \ + if (left == 0) { \ + put = state->window; \ + left = state->wsize; \ + state->whave = left; \ + if (out(out_desc, put, left)) { \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* + strm provides the memory allocation functions and window buffer on input, + and provides information on the unused input on return. For Z_DATA_ERROR + returns, strm will also provide an error message. + + in() and out() are the call-back input and output functions. When + inflateBack() needs more input, it calls in(). When inflateBack() has + filled the window with output, or when it completes with data in the + window, it calls out() to write out the data. The application must not + change the provided input until in() is called again or inflateBack() + returns. The application must not change the window/output buffer until + inflateBack() returns. + + in() and out() are called with a descriptor parameter provided in the + inflateBack() call. This parameter can be a structure that provides the + information required to do the read or write, as well as accumulated + information on the input and output such as totals and check values. + + in() should return zero on failure. out() should return non-zero on + failure. If either in() or out() fails, than inflateBack() returns a + Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it + was in() or out() that caused in the error. Otherwise, inflateBack() + returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format + error, or Z_MEM_ERROR if it could not allocate memory for the state. + inflateBack() can also return Z_STREAM_ERROR if the input parameters + are not correct, i.e. strm is Z_NULL or the state was not initialized. + */ +int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) +z_stream FAR *strm; +in_func in; +void FAR *in_desc; +out_func out; +void FAR *out_desc; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* Check that the strm exists and that the state was initialized */ + if (strm == Z_NULL || strm->state == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* Reset the state */ + strm->msg = Z_NULL; + state->mode = TYPE; + state->last = 0; + state->whave = 0; + next = strm->next_in; + have = next != Z_NULL ? strm->avail_in : 0; + hold = 0; + bits = 0; + put = state->window; + left = state->wsize; + + /* Inflate until end of block marked as last */ + for (;;) + switch (state->mode) { + case TYPE: + /* determine and dispatch block type */ + if (state->last) { + BYTEBITS(); + state->mode = DONE; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + + case STORED: + /* get and verify stored block length */ + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + + /* copy stored block from input to output */ + while (state->length != 0) { + copy = state->length; + PULL(); + ROOM(); + if (copy > have) copy = have; + if (copy > left) copy = left; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + + case TABLE: + /* get dynamic table entries descriptor */ + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + + /* get code length code lengths (not a typo) */ + state->have = 0; + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + + /* get length and distance code code lengths */ + state->have = 0; + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = (unsigned)(state->lens[state->have - 1]); + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + + case LEN: + /* use inflate_fast() if we have enough input and output */ + if (have >= 6 && left >= 258) { + RESTORE(); + if (state->whave < state->wsize) + state->whave = state->wsize - left; + inflate_fast(strm, state->wsize); + LOAD(); + break; + } + + /* get a literal, length, or end-of-block code */ + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + + /* process literal */ + if (this.op == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + ROOM(); + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + } + + /* process end of block */ + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + + /* invalid code */ + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + + /* length code -- get extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + + /* get distance code */ + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + + /* get distance extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } + if (state->offset > state->wsize - (state->whave < state->wsize ? + left : 0)) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + + /* copy match from window to output */ + do { + ROOM(); + copy = state->wsize - state->offset; + if (copy < left) { + from = put + copy; + copy = left - copy; + } + else { + from = put - state->offset; + copy = left; + } + if (copy > state->length) copy = state->length; + state->length -= copy; + left -= copy; + do { + *put++ = *from++; + } while (--copy); + } while (state->length != 0); + break; + + case DONE: + /* inflate stream terminated properly -- write leftover output */ + ret = Z_STREAM_END; + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left)) + ret = Z_BUF_ERROR; + } + goto inf_leave; + + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + + default: /* can't happen, but makes compilers happy */ + ret = Z_STREAM_ERROR; + goto inf_leave; + } + + /* Return unused input */ + inf_leave: + strm->next_in = next; + strm->avail_in = have; + return ret; +} + +int ZEXPORT inflateBackEnd(strm) +z_stream FAR *strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} diff --git a/com32/lib/zlib/inffast.c b/com32/lib/zlib/inffast.c new file mode 100644 index 0000000..c716440 --- /dev/null +++ b/com32/lib/zlib/inffast.c @@ -0,0 +1,305 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - 68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + unsigned char FAR *in; /* local strm->next_in */ + unsigned char FAR *last; /* while in < last, enough input available */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code this; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); + wsize = state->wsize; + whave = state->whave; + write = state->write; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = lcode[hold & lmask]; + dolen: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op == 0) { /* literal */ + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + PUP(out) = (unsigned char)(this.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = dcode[hold & dmask]; + dodist: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + from = window - OFF; + if (write == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (write < op) { /* wrap around window */ + from += wsize + write - op; + op -= write; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (write < len) { /* some from start of window */ + op = write; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += write - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + this = dcode[this.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + this = lcode[this.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and write == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/com32/lib/zlib/inffast.h b/com32/lib/zlib/inffast.h new file mode 100644 index 0000000..1e88d2d --- /dev/null +++ b/com32/lib/zlib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/com32/lib/zlib/inffixed.h b/com32/lib/zlib/inffixed.h new file mode 100644 index 0000000..75ed4b5 --- /dev/null +++ b/com32/lib/zlib/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/com32/lib/zlib/inflate.c b/com32/lib/zlib/inflate.c new file mode 100644 index 0000000..a53b5c7 --- /dev/null +++ b/com32/lib/zlib/inflate.c @@ -0,0 +1,1270 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common write == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, unsigned out)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->wsize = 0; + state->whave = 0; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (voidpf)state; + if (windowBits < 0) { + state->wrap = 0; + windowBits = -windowBits; + } + else { + state->wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) windowBits &= 15; +#endif + } + if (windowBits < 8 || windowBits > 15) { + ZFREE(strm, state); + strm->state = Z_NULL; + return Z_STREAM_ERROR; + } + state->wbits = (unsigned)windowBits; + state->window = Z_NULL; + return inflateReset(strm); +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, + state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, out) +z_streamp strm; +unsigned out; +{ + struct inflate_state FAR *state; + unsigned copy, dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->write = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + copy = out - strm->avail_out; + if (copy >= state->wsize) { + zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); + state->write = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->write; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->write, strm->next_out - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, strm->next_out - copy, copy); + state->write = copy; + state->whave = state->wsize; + } + else { + state->write += dist; + if (state->write == state->wsize) state->write = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ + ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + if (BITS(4) + 8 > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + } while (len && copy < have); + if (state->flags & 0x02000) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + } while (len && copy < have); + if (state->flags & 0x02000) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = REVERSE(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + break; + } + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + if ((int)(this.op) == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + state->mode = LIT; + break; + } + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(this.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->mode = DIST; + case DIST: + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + state->extra = (unsigned)(this.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } + if (state->offset > state->whave + out - left) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->write) { + copy -= state->write; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->write - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + REVERSE(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) + if (updatewindow(strm, out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long id; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->mode != DICT) return Z_STREAM_ERROR; + + /* check for correct dictionary id */ + id = adler32(0L, Z_NULL, 0); + id = adler32(id, dictionary, dictLength); + if (id != state->check) return Z_DATA_ERROR; + + /* copy dictionary to window */ + if (updatewindow(strm, strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + if (dictLength > state->wsize) { + zmemcpy(state->window, dictionary + dictLength - state->wsize, + state->wsize); + state->whave = state->wsize; + } + else { + zmemcpy(state->window + state->wsize - dictLength, dictionary, + dictLength); + state->whave = dictLength; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + *dest = *source; + *copy = *state; + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) + zmemcpy(window, state->window, 1U << state->wbits); + copy->window = window; + dest->state = (voidpf)copy; + return Z_OK; +} diff --git a/com32/lib/zlib/inflate.h b/com32/lib/zlib/inflate.h new file mode 100644 index 0000000..9a12c8f --- /dev/null +++ b/com32/lib/zlib/inflate.h @@ -0,0 +1,117 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ +#ifdef GUNZIP + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ +#endif + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN, /* i: waiting for length/lit code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ +#ifdef GUNZIP + LENGTH, /* i: waiting for 32-bit length (gzip) */ +#endif + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to the BAD or MEM mode -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME + NAME -> COMMENT -> HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + Read deflate blocks: + TYPE -> STORED or TABLE or LEN or CHECK + STORED -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN + Read deflate codes: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 7K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ +}; diff --git a/com32/lib/zlib/inftrees.c b/com32/lib/zlib/inftrees.c new file mode 100644 index 0000000..3bb5639 --- /dev/null +++ b/com32/lib/zlib/inftrees.c @@ -0,0 +1,321 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.1 Copyright 1995-2003 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code this; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 76, 66}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) return -1; /* no codes! */ + for (min = 1; min <= MAXBITS; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || (codes - count[0] != 1))) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked when a LENS table is being made + against the space in *table, ENOUGH, minus the maximum space needed by + the worst case distance code, MAXD. This should never happen, but the + sufficiency of ENOUGH has not been proven exhaustively, hence the check. + This assumes that when type == LENS, bits == 9. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + this.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + this.op = (unsigned char)0; + this.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + this.op = (unsigned char)(extra[work[sym]]); + this.val = base[work[sym]]; + } + else { + this.op = (unsigned char)(32 + 64); /* end of block */ + this.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + do { + fill -= incr; + next[(huff >> drop) + fill] = this; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += 1U << curr; + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)(len - drop); + this.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + curr = root; + this.bits = (unsigned char)len; + } + + /* put invalid code marker in table */ + next[huff >> drop] = this; + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/com32/lib/zlib/inftrees.h b/com32/lib/zlib/inftrees.h new file mode 100644 index 0000000..82d365a --- /dev/null +++ b/com32/lib/zlib/inftrees.h @@ -0,0 +1,55 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of dynamic tree. The maximum found in a long but non- + exhaustive search was 1004 code structures (850 for length/literals + and 154 for distances, the latter actually the result of an + exhaustive search). The true maximum is not known, but the value + below is more than safe. */ +#define ENOUGH 1440 +#define MAXD 154 + +/* Type of code to build for inftable() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +extern int inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/com32/lib/zlib/minigzip.c b/com32/lib/zlib/minigzip.c new file mode 100644 index 0000000..077e2e1 --- /dev/null +++ b/com32/lib/zlib/minigzip.c @@ -0,0 +1,322 @@ +/* minigzip.c -- simulate gzip using the zlib compression library + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * minigzip is a minimal implementation of the gzip utility. This is + * only an example of using zlib and isn't meant to replace the + * full-featured gzip. No attempt is made to deal with file systems + * limiting names to 14 or 8+3 characters, etc... Error checking is + * very limited. So use minigzip only for testing; use gzip for the + * real thing. On MSDOS, use only on file names without extension + * or in pipe mode. + */ + + +#include +#include "zlib.h" + +#ifdef STDC +# include +# include +#else + extern void exit OF((int)); +#endif + +#ifdef USE_MMAP +# include +# include +# include +#endif + +#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__) +# include +# include +# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) +#else +# define SET_BINARY_MODE(file) +#endif + +#ifdef VMS +# define unlink delete +# define GZ_SUFFIX "-gz" +#endif +#ifdef RISCOS +# define unlink remove +# define GZ_SUFFIX "-gz" +# define fileno(file) file->__file +#endif +#if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fileno */ +#endif + +#ifndef WIN32 /* unlink already in stdio.h for WIN32 */ + extern int unlink OF((const char *)); +#endif + +#ifndef GZ_SUFFIX +# define GZ_SUFFIX ".gz" +#endif +#define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1) + +#define BUFLEN 16384 +#define MAX_NAME_LEN 1024 + +#ifdef MAXSEG_64K +# define local static + /* Needed for systems with limitation on stack size. */ +#else +# define local +#endif + +char *prog; + +void error OF((const char *msg)); +void gz_compress OF((FILE *in, gzFile out)); +#ifdef USE_MMAP +int gz_compress_mmap OF((FILE *in, gzFile out)); +#endif +void gz_uncompress OF((gzFile in, FILE *out)); +void file_compress OF((char *file, char *mode)); +void file_uncompress OF((char *file)); +int main OF((int argc, char *argv[])); + +/* =========================================================================== + * Display error message and exit + */ +void error(msg) + const char *msg; +{ + fprintf(stderr, "%s: %s\n", prog, msg); + exit(1); +} + +/* =========================================================================== + * Compress input to output then close both files. + */ + +void gz_compress(in, out) + FILE *in; + gzFile out; +{ + local char buf[BUFLEN]; + int len; + int err; + +#ifdef USE_MMAP + /* Try first compressing with mmap. If mmap fails (minigzip used in a + * pipe), use the normal fread loop. + */ + if (gz_compress_mmap(in, out) == Z_OK) return; +#endif + for (;;) { + errno = 0; + len = (int)fread(buf, 1, sizeof(buf), in); + if (!len && errno) { + perror("fread"); + exit(1); + } + if (len == 0) break; + + if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err)); + } + fclose(in); + if (gzclose(out) != Z_OK) error("failed gzclose"); +} + +#ifdef USE_MMAP /* MMAP version, Miguel Albrecht */ + +/* Try compressing the input file at once using mmap. Return Z_OK if + * if success, Z_ERRNO otherwise. + */ +int gz_compress_mmap(in, out) + FILE *in; + gzFile out; +{ + int len; + int err; + int ifd = fileno(in); + caddr_t buf; /* mmap'ed buffer for the entire input file */ + off_t buf_len; /* length of the input file */ + struct stat sb; + + /* Determine the size of the file, needed for mmap: */ + if (fstat(ifd, &sb) < 0) return Z_ERRNO; + buf_len = sb.st_size; + if (buf_len <= 0) return Z_ERRNO; + + /* Now do the actual mmap: */ + buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0); + if (buf == (caddr_t)(-1)) return Z_ERRNO; + + /* Compress the whole file at once: */ + len = gzwrite(out, (char *)buf, (unsigned)buf_len); + + if (len != (int)buf_len) error(gzerror(out, &err)); + + munmap(buf, buf_len); + fclose(in); + if (gzclose(out) != Z_OK) error("failed gzclose"); + return Z_OK; +} +#endif /* USE_MMAP */ + +/* =========================================================================== + * Uncompress input to output then close both files. + */ +void gz_uncompress(in, out) + gzFile in; + FILE *out; +{ + local char buf[BUFLEN]; + int len; + int err; + + for (;;) { + len = gzread(in, buf, sizeof(buf)); + if (len < 0) error (gzerror(in, &err)); + if (len == 0) break; + + if ((int)fwrite(buf, 1, (unsigned)len, out) != len) { + error("failed fwrite"); + } + } + if (fclose(out)) error("failed fclose"); + + if (gzclose(in) != Z_OK) error("failed gzclose"); +} + + +/* =========================================================================== + * Compress the given file: create a corresponding .gz file and remove the + * original. + */ +void file_compress(file, mode) + char *file; + char *mode; +{ + local char outfile[MAX_NAME_LEN]; + FILE *in; + gzFile out; + + strcpy(outfile, file); + strcat(outfile, GZ_SUFFIX); + + in = fopen(file, "rb"); + if (in == NULL) { + perror(file); + exit(1); + } + out = gzopen(outfile, mode); + if (out == NULL) { + fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile); + exit(1); + } + gz_compress(in, out); + + unlink(file); +} + + +/* =========================================================================== + * Uncompress the given file and remove the original. + */ +void file_uncompress(file) + char *file; +{ + local char buf[MAX_NAME_LEN]; + char *infile, *outfile; + FILE *out; + gzFile in; + uInt len = (uInt)strlen(file); + + strcpy(buf, file); + + if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) { + infile = file; + outfile = buf; + outfile[len-3] = '\0'; + } else { + outfile = file; + infile = buf; + strcat(infile, GZ_SUFFIX); + } + in = gzopen(infile, "rb"); + if (in == NULL) { + fprintf(stderr, "%s: can't gzopen %s\n", prog, infile); + exit(1); + } + out = fopen(outfile, "wb"); + if (out == NULL) { + perror(file); + exit(1); + } + + gz_uncompress(in, out); + + unlink(infile); +} + + +/* =========================================================================== + * Usage: minigzip [-d] [-f] [-h] [-r] [-1 to -9] [files...] + * -d : decompress + * -f : compress with Z_FILTERED + * -h : compress with Z_HUFFMAN_ONLY + * -r : compress with Z_RLE + * -1 to -9 : compression level + */ + +int main(argc, argv) + int argc; + char *argv[]; +{ + int uncompr = 0; + gzFile file; + char outmode[20]; + + strcpy(outmode, "wb6 "); + + prog = argv[0]; + argc--, argv++; + + while (argc > 0) { + if (strcmp(*argv, "-d") == 0) + uncompr = 1; + else if (strcmp(*argv, "-f") == 0) + outmode[3] = 'f'; + else if (strcmp(*argv, "-h") == 0) + outmode[3] = 'h'; + else if (strcmp(*argv, "-r") == 0) + outmode[3] = 'R'; + else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' && + (*argv)[2] == 0) + outmode[2] = (*argv)[1]; + else + break; + argc--, argv++; + } + if (argc == 0) { + SET_BINARY_MODE(stdin); + SET_BINARY_MODE(stdout); + if (uncompr) { + file = gzdopen(fileno(stdin), "rb"); + if (file == NULL) error("can't gzdopen stdin"); + gz_uncompress(file, stdout); + } else { + file = gzdopen(fileno(stdout), outmode); + if (file == NULL) error("can't gzdopen stdout"); + gz_compress(stdin, file); + } + } else { + do { + if (uncompr) { + file_uncompress(*argv); + } else { + file_compress(*argv, outmode); + } + } while (argv++, --argc); + } + return 0; +} diff --git a/com32/lib/zlib/trees.c b/com32/lib/zlib/trees.c new file mode 100644 index 0000000..668ed68 --- /dev/null +++ b/com32/lib/zlib/trees.c @@ -0,0 +1,1214 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2003 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local void set_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (value << s->bi_valid); + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (val << s->bi_valid);\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if (tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void _tr_stored_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void _tr_flush_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is ascii or binary */ + if (s->data_type == Z_UNKNOWN) set_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, eof); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+eof, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+eof, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (eof) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*eof)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Set the data type to ASCII or BINARY, using a crude approximation: + * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + * IN assertion: the fields freq of dyn_ltree are set and the total of all + * frequencies does not exceed 64K (to fit in an int on 16 bit machines). + */ +local void set_data_type(s) + deflate_state *s; +{ + int n = 0; + unsigned ascii_freq = 0; + unsigned bin_freq = 0; + while (n < 7) bin_freq += s->dyn_ltree[n++].Freq; + while (n < 128) ascii_freq += s->dyn_ltree[n++].Freq; + while (n < LITERALS) bin_freq += s->dyn_ltree[n++].Freq; + s->data_type = (Byte)(bin_freq > (ascii_freq >> 2) ? Z_BINARY : Z_ASCII); +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/com32/lib/zlib/trees.h b/com32/lib/zlib/trees.h new file mode 100644 index 0000000..aadfa16 --- /dev/null +++ b/com32/lib/zlib/trees.h @@ -0,0 +1,127 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; diff --git a/com32/lib/zlib/uncompr.c b/com32/lib/zlib/uncompr.c new file mode 100644 index 0000000..c3c9fa6 --- /dev/null +++ b/com32/lib/zlib/uncompr.c @@ -0,0 +1,60 @@ +/* uncompr.c -- decompress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ +int ZEXPORT uncompress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return Z_DATA_ERROR; + return err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} diff --git a/com32/lib/zlib/zconf.in.h b/com32/lib/zlib/zconf.in.h new file mode 100644 index 0000000..5d10eaa --- /dev/null +++ b/com32/lib/zlib/zconf.in.h @@ -0,0 +1,322 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflatePrime z_deflatePrime +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table + +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) +# define WIN32 +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +#define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/com32/lib/zlib/zlib.3 b/com32/lib/zlib/zlib.3 new file mode 100644 index 0000000..8900984 --- /dev/null +++ b/com32/lib/zlib/zlib.3 @@ -0,0 +1,159 @@ +.TH ZLIB 3 "17 November 2003" +.SH NAME +zlib \- compression/decompression library +.SH SYNOPSIS +[see +.I zlib.h +for full description] +.SH DESCRIPTION +The +.I zlib +library is a general purpose data compression library. +The code is thread safe. +It provides in-memory compression and decompression functions, +including integrity checks of the uncompressed data. +This version of the library supports only one compression method (deflation) +but other algorithms will be added later +and will have the same stream interface. +.LP +Compression can be done in a single step if the buffers are large enough +(for example if an input file is mmap'ed), +or can be done by repeated calls of the compression function. +In the latter case, +the application must provide more input and/or consume the output +(providing more output space) before each call. +.LP +The library also supports reading and writing files in +.IR gzip (1) +(.gz) format +with an interface similar to that of stdio. +.LP +The library does not install any signal handler. +The decoder checks the consistency of the compressed data, +so the library should never crash even in case of corrupted input. +.LP +All functions of the compression library are documented in the file +.IR zlib.h . +The distribution source includes examples of use of the library +in the files +.I example.c +and +.IR minigzip.c . +.LP +Changes to this version are documented in the file +.I ChangeLog +that accompanies the source, +and are concerned primarily with bug fixes and portability enhancements. +.LP +A Java implementation of +.I zlib +is available in the Java Development Kit 1.1: +.IP +http://www.javasoft.com/products/JDK/1.1/docs/api/Package-java.util.zip.html +.LP +A Perl interface to +.IR zlib , +written by Paul Marquess (pmqs@cpan.org), +is available at CPAN (Comprehensive Perl Archive Network) sites, +including: +.IP +http://www.cpan.org/modules/by-module/Compress/ +.LP +A Python interface to +.IR zlib , +written by A.M. Kuchling (amk@magnet.com), +is available in Python 1.5 and later versions: +.IP +http://www.python.org/doc/lib/module-zlib.html +.LP +A +.I zlib +binding for +.IR tcl (1), +written by Andreas Kupries (a.kupries@westend.com), +is availlable at: +.IP +http://www.westend.com/~kupries/doc/trf/man/man.html +.LP +An experimental package to read and write files in .zip format, +written on top of +.I zlib +by Gilles Vollant (info@winimage.com), +is available at: +.IP +http://www.winimage.com/zLibDll/unzip.html +and also in the +.I contrib/minizip +directory of the main +.I zlib +web site. +.SH "SEE ALSO" +The +.I zlib +web site can be found at either of these locations: +.IP +http://www.zlib.org +.br +http://www.gzip.org/zlib/ +.LP +The data format used by the zlib library is described by RFC +(Request for Comments) 1950 to 1952 in the files: +.IP +http://www.ietf.org/rfc/rfc1950.txt (concerning zlib format) +.br +http://www.ietf.org/rfc/rfc1951.txt (concerning deflate format) +.br +http://www.ietf.org/rfc/rfc1952.txt (concerning gzip format) +.LP +These documents are also available in other formats from: +.IP +ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html +.LP +Mark Nelson (markn@ieee.org) wrote an article about +.I zlib +for the Jan. 1997 issue of Dr. Dobb's Journal; +a copy of the article is available at: +.IP +http://dogma.net/markn/articles/zlibtool/zlibtool.htm +.SH "REPORTING PROBLEMS" +Before reporting a problem, +please check the +.I zlib +web site to verify that you have the latest version of +.IR zlib ; +otherwise, +obtain the latest version and see if the problem still exists. +Please read the +.I zlib +FAQ at: +.IP +http://www.gzip.org/zlib/zlib_faq.html +.LP +before asking for help. +Send questions and/or comments to zlib@gzip.org, +or (for the Windows DLL version) to Gilles Vollant (info@winimage.com). +.SH AUTHORS +Version 1.2.1 +Copyright (C) 1995-2003 Jean-loup Gailly (jloup@gzip.org) +and Mark Adler (madler@alumni.caltech.edu). +.LP +This software is provided "as-is," +without any express or implied warranty. +In no event will the authors be held liable for any damages +arising from the use of this software. +See the distribution directory with respect to requirements +governing redistribution. +The deflate format used by +.I zlib +was defined by Phil Katz. +The deflate and +.I zlib +specifications were written by L. Peter Deutsch. +Thanks to all the people who reported problems and suggested various +improvements in +.IR zlib ; +who are too numerous to cite here. +.LP +UNIX manual page by R. P. C. Rodgers, +U.S. National Library of Medicine (rodgers@nlm.nih.gov). +.\" end of man page diff --git a/com32/lib/zlib/zutil.c b/com32/lib/zlib/zutil.c new file mode 100644 index 0000000..1965406 --- /dev/null +++ b/com32/lib/zlib/zutil.c @@ -0,0 +1,318 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + + +#include "zutil.h" + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +#ifndef STDC +extern void exit OF((int)); +#endif + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch (sizeof(uInt)) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch (sizeof(uLong)) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch (sizeof(voidpf)) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch (sizeof(z_off_t)) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1 << 16; +#endif +#ifdef NO_GZIP + flags += 1 << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1 << 20; +#endif +#ifdef FASTEST + flags += 1 << 21; +#endif +#ifdef STDC +# ifdef NO_vsnprintf + flags += 1 << 25; +# ifdef HAS_vsprintf_void + flags += 1 << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1 << 26; +# endif +# endif +#else + flags += 1 << 24; +# ifdef NO_snprintf + flags += 1 << 25; +# ifdef HAS_sprintf_void + flags += 1 << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1 << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int z_verbose = verbose; + +void z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* does not exist on WCE */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/com32/lib/zlib/zutil.h b/com32/lib/zlib/zutil.h new file mode 100644 index 0000000..483dc26 --- /dev/null +++ b/com32/lib/zlib/zutil.h @@ -0,0 +1,257 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + + +#ifndef ZUTIL_H +#define ZUTIL_H + +#define ZLIB_INTERNAL +#include "zlib.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# define vsnprintf _vsnprintf +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif + +#ifdef HAVE_STRERROR + extern char *strerror OF((int)); +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int z_verbose; + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */ diff --git a/com32/libutil/Makefile b/com32/libutil/Makefile new file mode 100644 index 0000000..02789ca --- /dev/null +++ b/com32/libutil/Makefile @@ -0,0 +1,65 @@ +## ----------------------------------------------------------------------- +## +## Copyright 2001-2008 H. Peter Anvin - All Rights Reserved +## +## Permission is hereby granted, free of charge, to any person +## obtaining a copy of this software and associated documentation +## files (the "Software"), to deal in the Software without +## restriction, including without limitation the rights to use, +## copy, modify, merge, publish, distribute, sublicense, and/or +## sell copies of the Software, and to permit persons to whom +## the Software is furnished to do so, subject to the following +## conditions: +## +## The above copyright notice and this permission notice shall +## be included in all copies or substantial portions of the Software. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +## OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +## HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +## WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +## FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +## OTHER DEALINGS IN THE SOFTWARE. +## +## ----------------------------------------------------------------------- + +## +## Utility companion library for the COM32 library +## + +topdir = ../.. +include ../MCONFIG + +LIBOBJS = ansiline.o ansiraw.o get_key.o sha1hash.o unbase64.o \ + md5.o crypt-md5.o sha256crypt.o sha512crypt.o base64.o +LNXLIBOBJS = $(patsubst %.o,%.lo,$(LIBOBJS)) + +all: libutil_com.a libutil_lnx.a + +libutil_com.a: $(LIBOBJS) + rm -f $@ + $(AR) cq $@ $(LIBOBJS) + $(RANLIB) $@ + +libutil_lnx.a: $(LNXLIBOBJS) + rm -f $@ + $(AR) cq $@ $(LNXLIBOBJS) + $(RANLIB) $@ + +tidy dist: + rm -f *.o *.lo *.lst *.elf .*.d *.tmp + +clean: tidy + rm -f *.lnx libutil_lnx.a + +spotless: clean + rm -f *.lss *.a *.c32 *.com + rm -f *~ \#* + +install: all + mkdir -m 755 -p $(INSTALLROOT)$(COM32DIR) + install -m 644 libutil_com.a libutil_lnx.a $(INSTALLROOT)$(COM32DIR) + +-include .*.d diff --git a/com32/libutil/ansiline.c b/com32/libutil/ansiline.c new file mode 100644 index 0000000..fffb2ba --- /dev/null +++ b/com32/libutil/ansiline.c @@ -0,0 +1,84 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * ansiline.c + * + * Configures the console for ANSI output in line mode; versions + * for COM32 and Linux support. + */ + +#ifdef __COM32__ + +#include +#include +#include + +void console_ansi_std(void) +{ + openconsole(&dev_stdcon_r, &dev_ansiserial_w); +} + +#else + +#include +#include + +static struct termios original_termios_settings; + +static void __attribute__((constructor)) console_init(void) +{ + tcgetattr(0, &original_termios_settings); +} + +static void __attribute__((destructor)) console_cleanup(void) +{ + tcsetattr(0, TCSANOW, &original_termios_settings); +} + + +void console_ansi_std(void) +{ + struct termios tio; + + /* Disable stdio buffering */ + setbuf(stdin, NULL); + setbuf(stdout, NULL); + setbuf(stderr, NULL); + + /* Set the termios flag so we behave the same as libcom32 */ + tcgetattr(0, &tio); + tio.c_iflag &= ~ICRNL; + tio.c_iflag |= IGNCR; + tio.c_lflag |= ICANON|ECHO; + if (!tio.c_oflag & OPOST) + tio.c_oflag = 0; + tio.c_oflag |= OPOST|ONLCR; + tcsetattr(0, TCSANOW, &tio); +} + +#endif diff --git a/com32/libutil/ansiraw.c b/com32/libutil/ansiraw.c new file mode 100644 index 0000000..c55a82e --- /dev/null +++ b/com32/libutil/ansiraw.c @@ -0,0 +1,86 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * ansiraw.c + * + * Configures the console for ANSI output in raw mode; versions + * for COM32 and Linux support. + */ + +#ifdef __COM32__ + +#include +#include +#include + +void console_ansi_raw(void) +{ + openconsole(&dev_rawcon_r, &dev_ansiserial_w); +} + +#else + +#include +#include + +static struct termios original_termios_settings; + +static void __attribute__((constructor)) console_init(void) +{ + tcgetattr(0, &original_termios_settings); +} + +static void __attribute__((destructor)) console_cleanup(void) +{ + tcsetattr(0, TCSANOW, &original_termios_settings); +} + + +void console_ansi_raw(void) +{ + struct termios tio; + + /* Disable stdio buffering */ + setbuf(stdin, NULL); + setbuf(stdout, NULL); + setbuf(stderr, NULL); + + /* Set the termios flag so we behave the same as libcom32 */ + tcgetattr(0, &tio); + tio.c_iflag &= ~ICRNL; + tio.c_iflag |= IGNCR; + tio.c_lflag &= ~(ISIG|ICANON|ECHO); + if (!tio.c_oflag & OPOST) + tio.c_oflag = 0; + tio.c_oflag |= OPOST|ONLCR; + tio.c_cc[VMIN] = 0; + tio.c_cc[VTIME] = 1; /* Don't 100% busy-wait in Linux */ + tcsetattr(0, TCSAFLUSH, &tio); +} + +#endif diff --git a/com32/libutil/base64.c b/com32/libutil/base64.c new file mode 100644 index 0000000..7ad2159 --- /dev/null +++ b/com32/libutil/base64.c @@ -0,0 +1,94 @@ +/* + * Output a base64 string. + * + * Options include: + * - Character 62 and 63; + * - To pad or not to pad. + */ + +#include +#include + +size_t genbase64(char *output, const void *input, size_t size, int flags) +{ + static char charz[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_"; + uint8_t buf[3]; + int j; + const uint8_t *p; + char *q; + uint32_t bv; + int left = size; + + charz[62] = (char)flags; + charz[63] = (char)(flags >> 8); + + p = input; q = output; + + while (left > 0) { + if (left < 3) { + buf[0] = p[0]; + buf[1] = (left > 1) ? p[1] : 0; + buf[2] = 0; + p = buf; + } + + bv = (p[0] << 16) | (p[1] << 8) | p[2]; + p += 3; + left -= 3; + + for ( j = 0 ; j < 4 ; j++ ) { + *q++ = charz[(bv >> 18) & 0x3f]; + bv <<= 6; + } + } + + switch (left) { + case -1: + if (flags & BASE64_PAD) + q[-1] = '='; + else + q--; + break; + + case -2: + if (flags & BASE64_PAD) + q[-2] = q[-1] = '='; + else + q -= 2; + break; + + default: + break; + } + + *q = '\0'; + + return q-output; +} + +#ifdef TEST + +#include +#include + +int main(int argc, char *argv[]) +{ + int i; + char buf[4096]; + int len, bytes; + + for (i = 1; i < argc; i++) { + printf("Original: \"%s\"\n", argv[i]); + + len = strlen(argv[i]); + bytes = genbase64(buf, argv[i], len, BASE64_MIME|BASE64_PAD); + printf(" MIME: \"%s\" (%d)\n", buf, bytes); + bytes = genbase64(buf, argv[i], len, BASE64_SAFE); + printf(" Safe: \"%s\" (%d)\n", buf, bytes); + } + + return 0; +} + +#endif diff --git a/com32/libutil/crypt-md5.c b/com32/libutil/crypt-md5.c new file mode 100644 index 0000000..751c3d2 --- /dev/null +++ b/com32/libutil/crypt-md5.c @@ -0,0 +1,176 @@ +/*- + * Copyright (c) 2003 Poul-Henning Kamp + * 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. + */ + +#include +#include +#include + +/* + * UNIX password + */ + +static char * +_crypt_to64(char *s, uint32_t v, int n) +{ + static const char itoa64[64] = "./0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + while (--n >= 0) { + *s++ = itoa64[v&0x3f]; + v >>= 6; + } + return s; +} + +char * +crypt_md5(const char *pw, const char *salt) +{ + MD5_CTX ctx,ctx1; + unsigned long l; + int sl, pl; + uint32_t i; + uint8_t final[MD5_SIZE]; + const char *sp; + static char passwd[120]; /* Output buffer */ + static const char magic[] = "$1$"; + char *p; + const int magic_len = sizeof magic - 1; + int pwlen = strlen(pw); + + /* Refine the Salt first */ + sp = salt; + + /* If it starts with the magic string, then skip that */ + if (!strncmp(sp, magic, magic_len)) + sp += magic_len; + + /* Compute the salt length: + it stops at the first '$', max 8 chars */ + for (sl = 0; sl < 8 && sp[sl] && sp[sl] != '$'; sl++) + ; + + MD5Init(&ctx); + + /* The password first, since that is what is most unknown */ + MD5Update(&ctx, pw, pwlen); + + /* Then our magic string */ + MD5Update(&ctx, magic, magic_len); + + /* Then the raw salt */ + MD5Update(&ctx, sp, sl); + + /* Then just as many characters of the MD5(pw,salt,pw) */ + MD5Init(&ctx1); + MD5Update(&ctx1, pw, pwlen); + MD5Update(&ctx1, sp, sl); + MD5Update(&ctx1, pw, pwlen); + MD5Final(final, &ctx1); + for (pl = pwlen; pl > 0; pl -= MD5_SIZE) + MD5Update(&ctx, final, pl > MD5_SIZE ? MD5_SIZE : pl); + + /* Don't leave anything around in vm they could use. */ + memset(final, 0, sizeof final); + + /* Then something really weird... */ + for (i = pwlen; i; i >>= 1) + if (i & 1) + MD5Update(&ctx, final, 1); + else + MD5Update(&ctx, pw, 1); + + /* Now make the output string */ + p = passwd; + + memcpy(p, magic, magic_len); + p += magic_len; + + memcpy(p, sp, sl); + p += sl; + + *p++ = '$'; + + MD5Final(final, &ctx); + + /* + * and now, just to make sure things don't run too fast + * On a 60 Mhz Pentium this takes 34 msec, so you would + * need 30 seconds to build a 1000 entry dictionary... + */ + for (i = 0; i < 1000; i++) { + MD5Init(&ctx1); + if(i & 1) + MD5Update(&ctx1, pw, pwlen); + else + MD5Update(&ctx1, final, MD5_SIZE); + + if(i % 3) + MD5Update(&ctx1, sp, sl); + + if(i % 7) + MD5Update(&ctx1, pw, pwlen); + + if(i & 1) + MD5Update(&ctx1, final, MD5_SIZE); + else + MD5Update(&ctx1, pw, pwlen); + MD5Final(final, &ctx1); + } + + l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; + p = _crypt_to64(p, l, 4); + l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; + p = _crypt_to64(p, l, 4); + l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; + p = _crypt_to64(p, l, 4); + l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; + p = _crypt_to64(p, l, 4); + l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; + p = _crypt_to64(p, l, 4); + l = final[11]; + p = _crypt_to64(p, l, 2); + *p = '\0'; + + /* Don't leave anything around in vm they could use. */ + memset(final, 0, sizeof final); + + return passwd; +} + +#ifdef TEST +#include + +int main(int argc, char *argv[]) +{ + int i; + + for (i = 2; i < argc; i += 2) { + puts(crypt_md5(argv[i], argv[i-1])); + } + return 0; +} + +#endif diff --git a/com32/libutil/get_key.c b/com32/libutil/get_key.c new file mode 100644 index 0000000..81065f6 --- /dev/null +++ b/com32/libutil/get_key.c @@ -0,0 +1,171 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * get_key.c + * + * Get a single key, and try to pick apart function key codes. + * This doesn't decode anywhere close to all possiblities, but + * hopefully is enough to be useful. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct keycode { + int code; + int seqlen; + const unsigned char *seq; +}; + +#define MAXLEN 8 +#define CODE(x,y) { x, (sizeof y)-1, y } + +static const struct keycode keycodes[] = { + /* First, the BIOS combined codes */ + CODE(KEY_F1, "\0\x3B"), + CODE(KEY_F2, "\0\x3C"), + CODE(KEY_F3, "\0\x3D"), + CODE(KEY_F4, "\0\x3E"), + CODE(KEY_F5, "\0\x3F"), + CODE(KEY_F6, "\0\x40"), + CODE(KEY_F7, "\0\x41"), + CODE(KEY_F8, "\0\x42"), + CODE(KEY_F9, "\0\x43"), + CODE(KEY_F10, "\0\x44"), + CODE(KEY_F11, "\0\x85"), + CODE(KEY_F12, "\0\x86"), + + CODE(KEY_UP, "\0\x48"), + CODE(KEY_DOWN, "\0\x50"), + CODE(KEY_LEFT, "\0\x4B"), + CODE(KEY_RIGHT,"\0\x4D"), + CODE(KEY_PGUP, "\0\x49"), + CODE(KEY_PGDN, "\0\x51"), + CODE(KEY_HOME, "\0\x47"), + CODE(KEY_END, "\0\x4F"), + CODE(KEY_INSERT, "\0\x52"), + CODE(KEY_DELETE, "\0\x53"), + + /* Now, VT/xterm/Linux codes */ + CODE(KEY_F1, "\033[[A"), + CODE(KEY_F1, "\033OP"), + CODE(KEY_F2, "\033[[B"), + CODE(KEY_F2, "\033OQ"), + CODE(KEY_F3, "\033[[C"), + CODE(KEY_F3, "\033OR"), + CODE(KEY_F4, "\033[[D"), + CODE(KEY_F4, "\033OS"), + CODE(KEY_F5, "\033[[E"), + CODE(KEY_F5, "\033[15~"), + CODE(KEY_F6, "\033[17~"), + CODE(KEY_F7, "\033[18~"), + CODE(KEY_F8, "\033[19~"), + CODE(KEY_F9, "\033[20~"), + CODE(KEY_F10, "\033[21~"), + CODE(KEY_F11, "\033[23~"), + CODE(KEY_F12, "\033[24~"), + + CODE(KEY_UP, "\033[A"), + CODE(KEY_DOWN, "\033[B"), + CODE(KEY_LEFT, "\033[D"), + CODE(KEY_RIGHT,"\033[C"), + CODE(KEY_PGUP, "\033[5~"), + CODE(KEY_PGUP, "\033[V"), + CODE(KEY_PGDN, "\033[6~"), + CODE(KEY_PGDN, "\033[U"), + CODE(KEY_HOME, "\033[1~"), + CODE(KEY_HOME, "\033[H"), + CODE(KEY_END, "\033[4~"), + CODE(KEY_END, "\033[F"), + CODE(KEY_END, "\033OF"), + CODE(KEY_INSERT, "\033[2~"), + CODE(KEY_INSERT, "\033[@"), + CODE(KEY_DELETE, "\033[3~"), +}; +#define NCODES ((int)(sizeof keycodes/sizeof(struct keycode))) + +#define KEY_TIMEOUT ((CLK_TCK+9)/10) + +int get_key(FILE *f, clock_t timeout) +{ + unsigned char buffer[MAXLEN]; + int nc, i, rv; + const struct keycode *kc; + int another; + unsigned char ch; + clock_t start; + + /* We typically start in the middle of a clock tick */ + if ( timeout ) + timeout++; + + nc = 0; + start = times(NULL); + do { + rv = read(fileno(f), &ch, 1); + if ( rv == 0 || (rv == -1 && errno == EAGAIN) ) { + clock_t lateness = times(NULL)-start; + if ( nc && lateness > 1+KEY_TIMEOUT ) { + if ( nc == 1 ) + return buffer[0]; /* timeout in sequence */ + else if ( timeout && lateness > timeout ) + return KEY_NONE; + } else if ( !nc && timeout && lateness > timeout ) + return KEY_NONE; /* timeout before sequence */ + + do_idle(); + + another = 1; + continue; + } + + start = times(NULL); + + buffer[nc++] = ch; + + another = 0; + for ( i = 0, kc = keycodes ; i < NCODES ; i++, kc++ ) { + if ( nc == kc->seqlen && !memcmp(buffer, kc->seq, nc) ) + return kc->code; + else if ( nc < kc->seqlen && !memcmp(buffer, kc->seq, nc) ) { + another = 1; + break; + } + } + } while ( another ); + + /* We got an unrecognized sequence; return the first character */ + /* We really should remember this and return subsequent characters later */ + return buffer[0]; +} diff --git a/com32/libutil/include/base64.h b/com32/libutil/include/base64.h new file mode 100644 index 0000000..3242e58 --- /dev/null +++ b/com32/libutil/include/base64.h @@ -0,0 +1,51 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2005-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * base64.h + * + * Simple routines for handing base64 text + */ + +#ifndef LIBUTIL_BASE64_H +#define LIBUTIL_BASE64_H + +#include + +#define BASE64_PAD 0x10000 + +/* There is plenty of disagreement w.r.t. the last few characters... */ +#define BASE64_MIME ('+' + ('/' << 8)) +#define BASE64_SAFE ('-' + ('_' << 8)) +#define BASE64_CRYPT ('.' + ('/' << 8)) +#define BASE64_URL ('*' + ('-' << 8)) /* Haven't seen myself */ +#define BASE64_REGEX ('|' + ('-' << 8)) /* Ditto... */ + +size_t genbase64(char *output, const void *digest, size_t size, int flags); +size_t unbase64(unsigned char *, size_t, const char *); + +#endif diff --git a/com32/libutil/include/consoles.h b/com32/libutil/include/consoles.h new file mode 100644 index 0000000..07652ed --- /dev/null +++ b/com32/libutil/include/consoles.h @@ -0,0 +1,40 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * consoles.h + * + * Utility functions for common console configurations + */ + +#ifndef LIBUTIL_CONSOLES_H +#define LIBUTIL_CONSOLES_H + +void console_ansi_std(void); +void console_ansi_raw(void); + +#endif /* LIBUTIL_CONSOLES_H */ diff --git a/com32/libutil/include/getkey.h b/com32/libutil/include/getkey.h new file mode 100644 index 0000000..52312a2 --- /dev/null +++ b/com32/libutil/include/getkey.h @@ -0,0 +1,80 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * getkey.h + * + * Function to get a key symbol and parse it + */ + +#ifndef LIBUTIL_GETKEY_H +#define LIBUTIL_GETKEY_H + +#include +#include +#include + +#ifndef CLK_TCK +# define CLK_TCK sysconf(_SC_CLK_TCK) +#endif + +#define KEY_NONE (-1) + +#define KEY_CTRL(x) ((x) & 0x001f) +#define KEY_BACKSPACE 0x0008 +#define KEY_TAB 0x0009 +#define KEY_ENTER 0x000d +#define KEY_ESC 0x001b +#define KEY_DEL 0x007f + +#define KEY_F1 0x0100 +#define KEY_F2 0x0101 +#define KEY_F3 0x0102 +#define KEY_F4 0x0103 +#define KEY_F5 0x0104 +#define KEY_F6 0x0105 +#define KEY_F7 0x0106 +#define KEY_F8 0x0107 +#define KEY_F9 0x0108 +#define KEY_F10 0x0109 +#define KEY_F11 0x010A +#define KEY_F12 0x010B + +#define KEY_UP 0x0120 +#define KEY_DOWN 0x0121 +#define KEY_LEFT 0x0122 +#define KEY_RIGHT 0x0123 +#define KEY_PGUP 0x0124 +#define KEY_PGDN 0x0125 +#define KEY_HOME 0x0126 +#define KEY_END 0x0127 +#define KEY_INSERT 0x0128 +#define KEY_DELETE 0x0129 + +int get_key(FILE *, clock_t); + +#endif /* LIBUTIL_GETKEY_H */ diff --git a/com32/libutil/include/libutil.h b/com32/libutil/include/libutil.h new file mode 100644 index 0000000..af46a75 --- /dev/null +++ b/com32/libutil/include/libutil.h @@ -0,0 +1,51 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2005-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * libutil.h + * + * Misc libutil functions + */ + +#ifndef LIBUTIL_LIBUTIL_H +#define LIBUTIL_LIBUTIL_H + +#ifdef __COM32__ + +# include + +# define do_idle syslinux_idle + +#else /* not __COM32__ */ + +# include + +# define do_idle sched_yield + +#endif + +#endif diff --git a/com32/libutil/include/md5.h b/com32/libutil/include/md5.h new file mode 100644 index 0000000..3df01b5 --- /dev/null +++ b/com32/libutil/include/md5.h @@ -0,0 +1,46 @@ +/* MD5.H - header file for MD5C.C + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +#ifndef _LIBUTIL_MD5_H +#define _LIBUTIL_MD5_H + +#include +#include + +#define MD5_SIZE 16 /* 128 bits */ + +/* MD5 context. */ +typedef struct { + uint32_t state[4]; /* state (ABCD) */ + uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +void MD5Init(MD5_CTX *); +void MD5Update(MD5_CTX *, const void *, size_t); +void MD5Final(unsigned char [MD5_SIZE], MD5_CTX *); + +char *crypt_md5(const char *, const char *); + +#endif diff --git a/com32/libutil/include/minmax.h b/com32/libutil/include/minmax.h new file mode 100644 index 0000000..eb6e39a --- /dev/null +++ b/com32/libutil/include/minmax.h @@ -0,0 +1,42 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#ifndef _MINMAX_H +#define _MINMAX_H + +/* + * minmax.h: Type-independent safe min/max macros + */ + +#define min(x,y) ({ __typeof(x) xx = (x); \ + __typeof(y) yy = (y); \ + xx < yy ? xx : yy; }) +#define max(x,y) ({ __typeof(x) xx = (x); \ + __typeof(y) yy = (y); \ + xx > yy ? xx : yy; }) + +#endif /* _MINMAX_H */ diff --git a/com32/libutil/include/sha1.h b/com32/libutil/include/sha1.h new file mode 100644 index 0000000..ea324c7 --- /dev/null +++ b/com32/libutil/include/sha1.h @@ -0,0 +1,18 @@ +#ifndef LIBUTIL_SHA1_H +#define LIBUTIL_SHA1_H + +#include + +typedef struct { + uint32_t state[5]; + uint32_t count[2]; + unsigned char buffer[64]; +} SHA1_CTX; + +void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]); +void SHA1Init(SHA1_CTX* context); +void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len); /* +JHB */ +void SHA1Final(unsigned char digest[20], SHA1_CTX* context); + +#endif /* LIBUTIL_SHA1_H */ diff --git a/com32/libutil/include/xcrypt.h b/com32/libutil/include/xcrypt.h new file mode 100644 index 0000000..89bc8bc --- /dev/null +++ b/com32/libutil/include/xcrypt.h @@ -0,0 +1,10 @@ +#ifndef _LIBUTIL_XCRYPT_H +#define _LIBUTIL_XCRYPT_H + +/* Extended crypt() implementations */ + +char *crypt_md5(const char *, const char *); +char *sha256_crypt (const char *, const char *); +char *sha512_crypt (const char *, const char *); + +#endif diff --git a/com32/libutil/libutil_com.a b/com32/libutil/libutil_com.a new file mode 100644 index 0000000000000000000000000000000000000000..fb0896f243515f002400f7b9757ec2ca1da7b36c GIT binary patch literal 36518 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 b)h}gwl#lyt1A~>P6O^SL@#7%JGUNXbz)$1< literal 0 HcmV?d00001 diff --git a/com32/libutil/md5.c b/com32/libutil/md5.c new file mode 100644 index 0000000..0a2c3dc --- /dev/null +++ b/com32/libutil/md5.c @@ -0,0 +1,280 @@ +/* + * MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + * + * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + * rights reserved. + * + * License to copy and use this software is granted provided that it + * is identified as the "RSA Data Security, Inc. MD5 Message-Digest + * Algorithm" in all material mentioning or referencing this software + * or this function. + * + * License is also granted to make and use derivative works provided + * that such works are identified as "derived from the RSA Data + * Security, Inc. MD5 Message-Digest Algorithm" in all material + * mentioning or referencing the derived work. + * + * RSA Data Security, Inc. makes no representations concerning either + * the merchantability of this software or the suitability of this + * software for any particular purpose. It is provided "as is" + * without express or implied warranty of any kind. + * + * These notices must be retained in any copies of any part of this + * documentation and/or software. + * + * This code is the same as the code published by RSA Inc. It has been + * edited for clarity and style only. + */ + +#include +#include +#include + +static void MD5Transform(uint32_t [4], const unsigned char [64]); + +#define Encode memcpy +#define Decode memcpy + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* + * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. + * Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. */ + +void +MD5Init (MD5_CTX *context) +{ + context->count[0] = context->count[1] = 0; + + /* Load magic initialization constants. */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* + * MD5 block update operation. Continues an MD5 message-digest + * operation, processing another message block, and updating the + * context. + */ + +void +MD5Update (MD5_CTX *context, const void *in, size_t inputLen) +{ + unsigned int i, idx, partLen; + const unsigned char *input = in; + + /* Compute number of bytes mod 64 */ + idx = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((uint32_t)inputLen << 3)) + < ((uint32_t)inputLen << 3)) + context->count[1]++; + context->count[1] += ((uint32_t)inputLen >> 29); + + partLen = 64 - idx; + + /* Transform as many times as possible. */ + if (inputLen >= partLen) { + memcpy((void *)&context->buffer[idx], (const void *)input, + partLen); + MD5Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, &input[i]); + + idx = 0; + } + else + i = 0; + + /* Buffer remaining input */ + memcpy ((void *)&context->buffer[idx], (const void *)&input[i], + inputLen-i); +} + +/* + * MD5 padding. Adds padding followed by original length. + */ + +static void +MD5Pad (MD5_CTX *context) +{ + unsigned char bits[8]; + unsigned int idx, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. */ + idx = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (idx < 56) ? (56 - idx) : (120 - idx); + MD5Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update (context, bits, 8); +} + +/* + * MD5 finalization. Ends an MD5 message-digest operation, writing the + * the message digest and zeroizing the context. + */ + +void +MD5Final (unsigned char digest[16], MD5_CTX *context) +{ + /* Do padding. */ + MD5Pad (context); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. */ + memset ((void *)context, 0, sizeof (*context)); +} + +/* MD5 basic transformation. Transforms state based on block. */ + +static void +MD5Transform (state, block) + uint32_t state[4]; + const unsigned char block[64]; +{ + uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. */ + memset ((void *)x, 0, sizeof (x)); +} diff --git a/com32/libutil/sha1hash.c b/com32/libutil/sha1hash.c new file mode 100644 index 0000000..6bd3f4b --- /dev/null +++ b/com32/libutil/sha1hash.c @@ -0,0 +1,308 @@ +/* +SHA-1 in C +By Steve Reid +100% Public Domain + +----------------- +Modified 7/98 +By James H. Brown +Still 100% Public Domain + +Corrected a problem which generated improper hash values on 16 bit machines +Routine SHA1Update changed from + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int +len) +to + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned +long len) + +The 'len' parameter was declared an int which works fine on 32 bit machines. +However, on 16 bit machines an int is too small for the shifts being done +against +it. This caused the hash function to generate incorrect values if len was +greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). + +Since the file IO in main() reads 16K at a time, any file 8K or larger would +be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million +"a"s). + +I also changed the declaration of variables i & j in SHA1Update to +unsigned long from unsigned int for the same reason. + +These changes should make no difference to any 32 bit implementations since +an +int and a long are the same size in those environments. + +-- +I also corrected a few compiler warnings generated by Borland C. +1. Added #include for exit() prototype +2. Removed unused variable 'j' in SHA1Final +3. Changed exit(0) to return(0) at end of main. + +ALL changes I made can be located by searching for comments containing 'JHB' +----------------- +Modified 8/98 +By Steve Reid +Still 100% public domain + +1- Removed #include and used return() instead of exit() +2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) +3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net + +----------------- +Modified 4/01 +By Saul Kravitz +Still 100% PD +Modified to run on Compaq Alpha hardware. + +----------------- +Modified 2/03 +By H. Peter Anvin +Still 100% PD +Modified to run on any hardware with and +Changed the driver program + +*/ + +/* +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +/* #define SHA1HANDSOFF */ + +#include +#include +#include +#include /* For htonl/ntohl/htons/ntohs */ + +#include "sha1.h" + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#define blk0(i) (block->l[i] = ntohl(block->l[i])) +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +#ifdef VERBOSE /* SAK */ +void SHAPrintContext(SHA1_CTX *context, char *msg){ + printf("%s (%d,%d) %x %x %x %x %x\n", + msg, + context->count[0], context->count[1], + context->state[0], + context->state[1], + context->state[2], + context->state[3], + context->state[4]); +} +#endif + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) +{ +uint32_t a, b, c, d, e; +typedef union { + unsigned char c[64]; + uint32_t l[16]; +} CHAR64LONG16; +CHAR64LONG16* block; +#ifdef SHA1HANDSOFF +static unsigned char workspace[64]; + block = (CHAR64LONG16*)workspace; + memcpy(block, buffer, 64); +#else + block = (CHAR64LONG16*)buffer; +#endif + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +} + + +/* SHA1Init - Initialize new context */ + +void SHA1Init(SHA1_CTX* context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ + +void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len) /* +JHB */ +{ +uint32_t i, j; /* JHB */ + +#ifdef VERBOSE + SHAPrintContext(context, "before"); +#endif + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +#ifdef VERBOSE + SHAPrintContext(context, "after "); +#endif +} + + +/* Add padding and return the message digest. */ + +void SHA1Final(unsigned char digest[20], SHA1_CTX* context) +{ +uint32_t i; /* JHB */ +unsigned char finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + SHA1Update(context, (unsigned char *)"\200", 1); + while ((context->count[0] & 504) != 448) { + SHA1Update(context, (unsigned char *)"\0", 1); + } + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() +*/ + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + /* Wipe variables */ + i = 0; /* JHB */ + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(finalcount, 0, 8); /* SWR */ +#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite it's own static vars */ + SHA1Transform(context->state, context->buffer); +#endif +} + +/*************************************************************/ + +/* This is not quite the MIME base64 algorithm: it uses _ instead of /, + and instead of padding the output with = characters we just make the + output shorter. */ +char *mybase64(uint8_t digest[20]) +{ + static const char charz[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_"; + uint8_t input[21]; + static char output[28]; + int i, j; + uint8_t *p; + char *q; + uint32_t bv; + + memcpy(input, digest, 20); + input[20] = 0; /* Pad to multiple of 3 bytes */ + + p = input; q = output; + for ( i = 0 ; i < 7 ; i++ ) { + bv = (p[0] << 16) | (p[1] << 8) | p[2]; + p += 3; + for ( j = 0 ; j < 4 ; j++ ) { + *q++ = charz[(bv >> 18) & 0x3f]; + bv <<= 6; + } + } + *--q = '\0'; /* The last character is not significant */ + return output; +} + +#ifdef FOR_TESTING_ONLY + +int main(int argc, char** argv) +{ + int i; + SHA1_CTX context; + uint8_t digest[20], buffer[16384]; + FILE* file; + + if (argc < 2) { + file = stdin; + } + else { + if (!(file = fopen(argv[1], "rb"))) { + fputs("Unable to open file.", stderr); + return(-1); + } + } + SHA1Init(&context); + while (!feof(file)) { /* note: what if ferror(file) */ + i = fread(buffer, 1, 16384, file); + SHA1Update(&context, buffer, i); + } + SHA1Final(digest, &context); + fclose(file); + + puts(mybase64(digest)); + + return 0; +} + +#endif diff --git a/com32/libutil/sha256crypt.c b/com32/libutil/sha256crypt.c new file mode 100644 index 0000000..27f912e --- /dev/null +++ b/com32/libutil/sha256crypt.c @@ -0,0 +1,725 @@ +/* SHA256-based Unix crypt implementation. + Released into the Public Domain by Ulrich Drepper . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xcrypt.h" + +#define MIN(x,y) min(x,y) +#define MAX(x,y) max(x,y) + +/* Structure to save state of computation between the single steps. */ +struct sha256_ctx +{ + uint32_t H[8]; + + uint32_t total[2]; + uint32_t buflen; + char buffer[128]; /* NB: always correctly aligned for uint32_t. */ +}; + + +#if __BYTE_ORDER == __LITTLE_ENDIAN +# define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +#else +# define SWAP(n) (n) +#endif + + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (FIPS 180-2:5.1.1) */ +static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; + + +/* Constants for SHA256 from FIPS 180-2:4.2.2. */ +static const uint32_t K[64] = + { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + }; + + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. */ +static void +sha256_process_block (const void *buffer, size_t len, struct sha256_ctx *ctx) +{ + unsigned int t; + const uint32_t *words = buffer; + size_t nwords = len / sizeof (uint32_t); + uint32_t a = ctx->H[0]; + uint32_t b = ctx->H[1]; + uint32_t c = ctx->H[2]; + uint32_t d = ctx->H[3]; + uint32_t e = ctx->H[4]; + uint32_t f = ctx->H[5]; + uint32_t g = ctx->H[6]; + uint32_t h = ctx->H[7]; + + /* First increment the byte count. FIPS 180-2 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + ctx->total[0] += len; + if (ctx->total[0] < len) + ++ctx->total[1]; + + /* Process all bytes in the buffer with 64 bytes in each round of + the loop. */ + while (nwords > 0) + { + uint32_t W[64]; + uint32_t a_save = a; + uint32_t b_save = b; + uint32_t c_save = c; + uint32_t d_save = d; + uint32_t e_save = e; + uint32_t f_save = f; + uint32_t g_save = g; + uint32_t h_save = h; + + /* Operators defined in FIPS 180-2:4.1.2. */ +#define Ch(x, y, z) ((x & y) ^ (~x & z)) +#define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) +#define S0(x) (CYCLIC (x, 2) ^ CYCLIC (x, 13) ^ CYCLIC (x, 22)) +#define S1(x) (CYCLIC (x, 6) ^ CYCLIC (x, 11) ^ CYCLIC (x, 25)) +#define R0(x) (CYCLIC (x, 7) ^ CYCLIC (x, 18) ^ (x >> 3)) +#define R1(x) (CYCLIC (x, 17) ^ CYCLIC (x, 19) ^ (x >> 10)) + + /* It is unfortunate that C does not provide an operator for + cyclic rotation. Hope the C compiler is smart enough. */ +#define CYCLIC(w, s) ((w >> s) | (w << (32 - s))) + + /* Compute the message schedule according to FIPS 180-2:6.2.2 step 2. */ + for (t = 0; t < 16; ++t) + { + W[t] = SWAP (*words); + ++words; + } + for (t = 16; t < 64; ++t) + W[t] = R1 (W[t - 2]) + W[t - 7] + R0 (W[t - 15]) + W[t - 16]; + + /* The actual computation according to FIPS 180-2:6.2.2 step 3. */ + for (t = 0; t < 64; ++t) + { + uint32_t T1 = h + S1 (e) + Ch (e, f, g) + K[t] + W[t]; + uint32_t T2 = S0 (a) + Maj (a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + } + + /* Add the starting values of the context according to FIPS 180-2:6.2.2 + step 4. */ + a += a_save; + b += b_save; + c += c_save; + d += d_save; + e += e_save; + f += f_save; + g += g_save; + h += h_save; + + /* Prepare for the next round. */ + nwords -= 16; + } + + /* Put checksum in context given as argument. */ + ctx->H[0] = a; + ctx->H[1] = b; + ctx->H[2] = c; + ctx->H[3] = d; + ctx->H[4] = e; + ctx->H[5] = f; + ctx->H[6] = g; + ctx->H[7] = h; +} + + +/* Initialize structure containing state of computation. + (FIPS 180-2:5.3.2) */ +static void +sha256_init_ctx (struct sha256_ctx *ctx) +{ + ctx->H[0] = 0x6a09e667; + ctx->H[1] = 0xbb67ae85; + ctx->H[2] = 0x3c6ef372; + ctx->H[3] = 0xa54ff53a; + ctx->H[4] = 0x510e527f; + ctx->H[5] = 0x9b05688c; + ctx->H[6] = 0x1f83d9ab; + ctx->H[7] = 0x5be0cd19; + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} + + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +static void * +sha256_finish_ctx (struct sha256_ctx *ctx, void *resbuf) +{ + unsigned int i; + /* Take yet unprocessed bytes into account. */ + uint32_t bytes = ctx->buflen; + size_t pad; + + /* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; + + pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; + memcpy (&ctx->buffer[bytes], fillbuf, pad); + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + *(uint32_t *) &ctx->buffer[bytes + pad + 4] = SWAP (ctx->total[0] << 3); + *(uint32_t *) &ctx->buffer[bytes + pad] = SWAP ((ctx->total[1] << 3) | + (ctx->total[0] >> 29)); + + /* Process last bytes. */ + sha256_process_block (ctx->buffer, bytes + pad + 8, ctx); + + /* Put result from CTX in first 32 bytes following RESBUF. */ + for (i = 0; i < 8; ++i) + ((uint32_t *) resbuf)[i] = SWAP (ctx->H[i]); + + return resbuf; +} + + +static void +sha256_process_bytes (const void *buffer, size_t len, struct sha256_ctx *ctx) +{ + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (ctx->buflen != 0) + { + size_t left_over = ctx->buflen; + size_t add = 128 - left_over > len ? len : 128 - left_over; + + memcpy (&ctx->buffer[left_over], buffer, add); + ctx->buflen += add; + + if (ctx->buflen > 64) + { + sha256_process_block (ctx->buffer, ctx->buflen & ~63, ctx); + + ctx->buflen &= 63; + /* The regions in the following copy operation cannot overlap. */ + memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63], + ctx->buflen); + } + + buffer = (const char *) buffer + add; + len -= add; + } + + /* Process available complete blocks. */ + if (len >= 64) + { +/* To check alignment gcc has an appropriate operator. Other + compilers don't. */ +#if __GNUC__ >= 2 +# define UNALIGNED_P(p) (((uintptr_t) p) % __alignof__ (uint32_t) != 0) +#else +# define UNALIGNED_P(p) (((uintptr_t) p) % sizeof (uint32_t) != 0) +#endif + if (UNALIGNED_P (buffer)) + while (len > 64) + { + sha256_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); + buffer = (const char *) buffer + 64; + len -= 64; + } + else + { + sha256_process_block (buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } + } + + /* Move remaining bytes into internal buffer. */ + if (len > 0) + { + size_t left_over = ctx->buflen; + + memcpy (&ctx->buffer[left_over], buffer, len); + left_over += len; + if (left_over >= 64) + { + sha256_process_block (ctx->buffer, 64, ctx); + left_over -= 64; + memcpy (ctx->buffer, &ctx->buffer[64], left_over); + } + ctx->buflen = left_over; + } +} + + +/* Define our magic string to mark salt for SHA256 "encryption" + replacement. */ +static const char sha256_salt_prefix[] = "$5$"; + +/* Prefix for optional rounds specification. */ +static const char sha256_rounds_prefix[] = "rounds="; + +/* Maximum salt string length. */ +#define SALT_LEN_MAX 16 +/* Default number of rounds if not explicitly specified. */ +#define ROUNDS_DEFAULT 5000 +/* Minimum number of rounds. */ +#define ROUNDS_MIN 1000 +/* Maximum number of rounds. */ +#define ROUNDS_MAX 999999999 + +/* Table with characters for base64 transformation. */ +static const char b64t[64] = +"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + +static char * +sha256_crypt_r (const char *key, const char *salt, char *buffer, int buflen) +{ + unsigned char alt_result[32] + __attribute__ ((__aligned__ (__alignof__ (uint32_t)))); + unsigned char temp_result[32] + __attribute__ ((__aligned__ (__alignof__ (uint32_t)))); + struct sha256_ctx ctx; + struct sha256_ctx alt_ctx; + size_t salt_len; + size_t key_len; + size_t cnt; + char *cp; + char *copied_key = NULL; + char *copied_salt = NULL; + char *p_bytes; + char *s_bytes; + /* Default number of rounds. */ + size_t rounds = ROUNDS_DEFAULT; + bool rounds_custom = false; + + /* Find beginning of salt string. The prefix should normally always + be present. Just in case it is not. */ + if (strncmp (sha256_salt_prefix, salt, sizeof (sha256_salt_prefix) - 1) == 0) + /* Skip salt prefix. */ + salt += sizeof (sha256_salt_prefix) - 1; + + if (strncmp (salt, sha256_rounds_prefix, sizeof (sha256_rounds_prefix) - 1) + == 0) + { + const char *num = salt + sizeof (sha256_rounds_prefix) - 1; + char *endp; + unsigned long int srounds = strtoul (num, &endp, 10); + if (*endp == '$') + { + salt = endp + 1; + rounds = MAX (ROUNDS_MIN, MIN (srounds, ROUNDS_MAX)); + rounds_custom = true; + } + } + + salt_len = MIN (strcspn (salt, "$"), SALT_LEN_MAX); + key_len = strlen (key); + + if ((key - (char *) 0) % __alignof__ (uint32_t) != 0) + { + char *tmp = (char *) alloca (key_len + __alignof__ (uint32_t)); + key = copied_key = + memcpy (tmp + __alignof__ (uint32_t) + - (tmp - (char *) 0) % __alignof__ (uint32_t), + key, key_len); + } + + if ((salt - (char *) 0) % __alignof__ (uint32_t) != 0) + { + char *tmp = (char *) alloca (salt_len + __alignof__ (uint32_t)); + salt = copied_salt = + memcpy (tmp + __alignof__ (uint32_t) + - (tmp - (char *) 0) % __alignof__ (uint32_t), + salt, salt_len); + } + + /* Prepare for the real work. */ + sha256_init_ctx (&ctx); + + /* Add the key string. */ + sha256_process_bytes (key, key_len, &ctx); + + /* The last part is the salt string. This must be at most 8 + characters and it ends at the first `$' character (for + compatibility with existing implementations). */ + sha256_process_bytes (salt, salt_len, &ctx); + + + /* Compute alternate SHA256 sum with input KEY, SALT, and KEY. The + final result will be added to the first context. */ + sha256_init_ctx (&alt_ctx); + + /* Add key. */ + sha256_process_bytes (key, key_len, &alt_ctx); + + /* Add salt. */ + sha256_process_bytes (salt, salt_len, &alt_ctx); + + /* Add key again. */ + sha256_process_bytes (key, key_len, &alt_ctx); + + /* Now get result of this (32 bytes) and add it to the other + context. */ + sha256_finish_ctx (&alt_ctx, alt_result); + + /* Add for any character in the key one byte of the alternate sum. */ + for (cnt = key_len; cnt > 32; cnt -= 32) + sha256_process_bytes (alt_result, 32, &ctx); + sha256_process_bytes (alt_result, cnt, &ctx); + + /* Take the binary representation of the length of the key and for every + 1 add the alternate sum, for every 0 the key. */ + for (cnt = key_len; cnt > 0; cnt >>= 1) + if ((cnt & 1) != 0) + sha256_process_bytes (alt_result, 32, &ctx); + else + sha256_process_bytes (key, key_len, &ctx); + + /* Create intermediate result. */ + sha256_finish_ctx (&ctx, alt_result); + + /* Start computation of P byte sequence. */ + sha256_init_ctx (&alt_ctx); + + /* For every character in the password add the entire password. */ + for (cnt = 0; cnt < key_len; ++cnt) + sha256_process_bytes (key, key_len, &alt_ctx); + + /* Finish the digest. */ + sha256_finish_ctx (&alt_ctx, temp_result); + + /* Create byte sequence P. */ + cp = p_bytes = alloca (key_len); + for (cnt = key_len; cnt >= 32; cnt -= 32) + cp = mempcpy (cp, temp_result, 32); + memcpy (cp, temp_result, cnt); + + /* Start computation of S byte sequence. */ + sha256_init_ctx (&alt_ctx); + + /* For every character in the password add the entire password. */ + for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt) + sha256_process_bytes (salt, salt_len, &alt_ctx); + + /* Finish the digest. */ + sha256_finish_ctx (&alt_ctx, temp_result); + + /* Create byte sequence S. */ + cp = s_bytes = alloca (salt_len); + for (cnt = salt_len; cnt >= 32; cnt -= 32) + cp = mempcpy (cp, temp_result, 32); + memcpy (cp, temp_result, cnt); + + /* Repeatedly run the collected hash value through SHA256 to burn + CPU cycles. */ + for (cnt = 0; cnt < rounds; ++cnt) + { + /* New context. */ + sha256_init_ctx (&ctx); + + /* Add key or last result. */ + if ((cnt & 1) != 0) + sha256_process_bytes (p_bytes, key_len, &ctx); + else + sha256_process_bytes (alt_result, 32, &ctx); + + /* Add salt for numbers not divisible by 3. */ + if (cnt % 3 != 0) + sha256_process_bytes (s_bytes, salt_len, &ctx); + + /* Add key for numbers not divisible by 7. */ + if (cnt % 7 != 0) + sha256_process_bytes (p_bytes, key_len, &ctx); + + /* Add key or last result. */ + if ((cnt & 1) != 0) + sha256_process_bytes (alt_result, 32, &ctx); + else + sha256_process_bytes (p_bytes, key_len, &ctx); + + /* Create intermediate result. */ + sha256_finish_ctx (&ctx, alt_result); + } + + /* Now we can construct the result string. It consists of three + parts. */ + cp = stpncpy (buffer, sha256_salt_prefix, MAX (0, buflen)); + buflen -= sizeof (sha256_salt_prefix) - 1; + + if (rounds_custom) + { + int n = snprintf (cp, MAX (0, buflen), "%s%zu$", + sha256_rounds_prefix, rounds); + cp += n; + buflen -= n; + } + + cp = stpncpy (cp, salt, MIN ((size_t) MAX (0, buflen), salt_len)); + buflen -= MIN ((size_t) MAX (0, buflen), salt_len); + + if (buflen > 0) + { + *cp++ = '$'; + --buflen; + } + +#define b64_from_24bit(B2, B1, B0, N) \ + do { \ + unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \ + int n = (N); \ + while (n-- > 0 && buflen > 0) \ + { \ + *cp++ = b64t[w & 0x3f]; \ + --buflen; \ + w >>= 6; \ + } \ + } while (0) + + b64_from_24bit (alt_result[0], alt_result[10], alt_result[20], 4); + b64_from_24bit (alt_result[21], alt_result[1], alt_result[11], 4); + b64_from_24bit (alt_result[12], alt_result[22], alt_result[2], 4); + b64_from_24bit (alt_result[3], alt_result[13], alt_result[23], 4); + b64_from_24bit (alt_result[24], alt_result[4], alt_result[14], 4); + b64_from_24bit (alt_result[15], alt_result[25], alt_result[5], 4); + b64_from_24bit (alt_result[6], alt_result[16], alt_result[26], 4); + b64_from_24bit (alt_result[27], alt_result[7], alt_result[17], 4); + b64_from_24bit (alt_result[18], alt_result[28], alt_result[8], 4); + b64_from_24bit (alt_result[9], alt_result[19], alt_result[29], 4); + b64_from_24bit (0, alt_result[31], alt_result[30], 3); + if (buflen <= 0) + { + errno = ERANGE; + buffer = NULL; + } + else + *cp = '\0'; /* Terminate the string. */ + + /* Clear the buffer for the intermediate result so that people + attaching to processes or reading core dumps cannot get any + information. We do it in this way to clear correct_words[] + inside the SHA256 implementation as well. */ + sha256_init_ctx (&ctx); + sha256_finish_ctx (&ctx, alt_result); + memset (temp_result, '\0', sizeof (temp_result)); + memset (p_bytes, '\0', key_len); + memset (s_bytes, '\0', salt_len); + memset (&ctx, '\0', sizeof (ctx)); + memset (&alt_ctx, '\0', sizeof (alt_ctx)); + if (copied_key != NULL) + memset (copied_key, '\0', key_len); + if (copied_salt != NULL) + memset (copied_salt, '\0', salt_len); + + return buffer; +} + + +/* This entry point is equivalent to the `crypt' function in Unix + libcs. */ +char * +sha256_crypt (const char *key, const char *salt) +{ + /* We don't want to have an arbitrary limit in the size of the + password. We can compute an upper bound for the size of the + result in advance and so we can prepare the buffer we pass to + `sha256_crypt_r'. */ + static char *buffer; + static int buflen; + int needed = (sizeof (sha256_salt_prefix) - 1 + + sizeof (sha256_rounds_prefix) + 9 + 1 + + strlen (salt) + 1 + 43 + 1); + + if (buflen < needed) + { + char *new_buffer = (char *) realloc (buffer, needed); + if (new_buffer == NULL) + return NULL; + + buffer = new_buffer; + buflen = needed; + } + + return sha256_crypt_r (key, salt, buffer, buflen); +} + + +#ifdef TEST +static const struct +{ + const char *input; + const char result[32]; +} tests[] = + { + /* Test vectors from FIPS 180-2: appendix B.1. */ + { "abc", + "\xba\x78\x16\xbf\x8f\x01\xcf\xea\x41\x41\x40\xde\x5d\xae\x22\x23" + "\xb0\x03\x61\xa3\x96\x17\x7a\x9c\xb4\x10\xff\x61\xf2\x00\x15\xad" }, + /* Test vectors from FIPS 180-2: appendix B.2. */ + { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "\x24\x8d\x6a\x61\xd2\x06\x38\xb8\xe5\xc0\x26\x93\x0c\x3e\x60\x39" + "\xa3\x3c\xe4\x59\x64\xff\x21\x67\xf6\xec\xed\xd4\x19\xdb\x06\xc1" }, + /* Test vectors from the NESSIE project. */ + { "", + "\xe3\xb0\xc4\x42\x98\xfc\x1c\x14\x9a\xfb\xf4\xc8\x99\x6f\xb9\x24" + "\x27\xae\x41\xe4\x64\x9b\x93\x4c\xa4\x95\x99\x1b\x78\x52\xb8\x55" }, + { "a", + "\xca\x97\x81\x12\xca\x1b\xbd\xca\xfa\xc2\x31\xb3\x9a\x23\xdc\x4d" + "\xa7\x86\xef\xf8\x14\x7c\x4e\x72\xb9\x80\x77\x85\xaf\xee\x48\xbb" }, + { "message digest", + "\xf7\x84\x6f\x55\xcf\x23\xe1\x4e\xeb\xea\xb5\xb4\xe1\x55\x0c\xad" + "\x5b\x50\x9e\x33\x48\xfb\xc4\xef\xa3\xa1\x41\x3d\x39\x3c\xb6\x50" }, + { "abcdefghijklmnopqrstuvwxyz", + "\x71\xc4\x80\xdf\x93\xd6\xae\x2f\x1e\xfa\xd1\x44\x7c\x66\xc9\x52" + "\x5e\x31\x62\x18\xcf\x51\xfc\x8d\x9e\xd8\x32\xf2\xda\xf1\x8b\x73" }, + { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "\x24\x8d\x6a\x61\xd2\x06\x38\xb8\xe5\xc0\x26\x93\x0c\x3e\x60\x39" + "\xa3\x3c\xe4\x59\x64\xff\x21\x67\xf6\xec\xed\xd4\x19\xdb\x06\xc1" }, + { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + "\xdb\x4b\xfc\xbd\x4d\xa0\xcd\x85\xa6\x0c\x3c\x37\xd3\xfb\xd8\x80" + "\x5c\x77\xf1\x5f\xc6\xb1\xfd\xfe\x61\x4e\xe0\xa7\xc8\xfd\xb4\xc0" }, + { "123456789012345678901234567890123456789012345678901234567890" + "12345678901234567890", + "\xf3\x71\xbc\x4a\x31\x1f\x2b\x00\x9e\xef\x95\x2d\xd8\x3c\xa8\x0e" + "\x2b\x60\x02\x6c\x8e\x93\x55\x92\xd0\xf9\xc3\x08\x45\x3c\x81\x3e" } + }; +#define ntests (sizeof (tests) / sizeof (tests[0])) + + +static const struct +{ + const char *salt; + const char *input; + const char *expected; +} tests2[] = +{ + { "$5$saltstring", "Hello world!", + "$5$saltstring$5B8vYYiY.CVt1RlTTf8KbXBH3hsxY/GNooZaBBGWEc5" }, + { "$5$rounds=10000$saltstringsaltstring", "Hello world!", + "$5$rounds=10000$saltstringsaltst$3xv.VbSHBb41AL9AvLeujZkZRBAwqFMz2." + "opqey6IcA" }, + { "$5$rounds=5000$toolongsaltstring", "This is just a test", + "$5$rounds=5000$toolongsaltstrin$Un/5jzAHMgOGZ5.mWJpuVolil07guHPvOW8" + "mGRcvxa5" }, + { "$5$rounds=1400$anotherlongsaltstring", + "a very much longer text to encrypt. This one even stretches over more" + "than one line.", + "$5$rounds=1400$anotherlongsalts$Rx.j8H.h8HjEDGomFU8bDkXm3XIUnzyxf12" + "oP84Bnq1" }, + { "$5$rounds=77777$short", + "we have a short salt string but not a short password", + "$5$rounds=77777$short$JiO1O3ZpDAxGJeaDIuqCoEFysAe1mZNJRs3pw0KQRd/" }, + { "$5$rounds=123456$asaltof16chars..", "a short string", + "$5$rounds=123456$asaltof16chars..$gP3VQ/6X7UUEW3HkBn2w1/Ptq2jxPyzV/" + "cZKmF/wJvD" }, + { "$5$rounds=10$roundstoolow", "the minimum number is still observed", + "$5$rounds=1000$roundstoolow$yfvwcWrQ8l/K0DAWyuPMDNHpIVlTQebY9l/gL97" + "2bIC" }, +}; +#define ntests2 (sizeof (tests2) / sizeof (tests2[0])) + + +int +main (void) +{ + struct sha256_ctx ctx; + char sum[32]; + int result = 0; + int cnt; + + for (cnt = 0; cnt < (int) ntests; ++cnt) + { + sha256_init_ctx (&ctx); + sha256_process_bytes (tests[cnt].input, strlen (tests[cnt].input), &ctx); + sha256_finish_ctx (&ctx, sum); + if (memcmp (tests[cnt].result, sum, 32) != 0) + { + printf ("test %d run %d failed\n", cnt, 1); + result = 1; + } + + sha256_init_ctx (&ctx); + for (int i = 0; tests[cnt].input[i] != '\0'; ++i) + sha256_process_bytes (&tests[cnt].input[i], 1, &ctx); + sha256_finish_ctx (&ctx, sum); + if (memcmp (tests[cnt].result, sum, 32) != 0) + { + printf ("test %d run %d failed\n", cnt, 2); + result = 1; + } + } + + /* Test vector from FIPS 180-2: appendix B.3. */ + char buf[1000]; + memset (buf, 'a', sizeof (buf)); + sha256_init_ctx (&ctx); + for (int i = 0; i < 1000; ++i) + sha256_process_bytes (buf, sizeof (buf), &ctx); + sha256_finish_ctx (&ctx, sum); + static const char expected[32] = + "\xcd\xc7\x6e\x5c\x99\x14\xfb\x92\x81\xa1\xc7\xe2\x84\xd7\x3e\x67" + "\xf1\x80\x9a\x48\xa4\x97\x20\x0e\x04\x6d\x39\xcc\xc7\x11\x2c\xd0"; + if (memcmp (expected, sum, 32) != 0) + { + printf ("test %d failed\n", cnt); + result = 1; + } + + for (cnt = 0; cnt < ntests2; ++cnt) + { + char *cp = sha256_crypt (tests2[cnt].input, tests2[cnt].salt); + + if (strcmp (cp, tests2[cnt].expected) != 0) + { + printf ("test %d: expected \"%s\", got \"%s\"\n", + cnt, tests2[cnt].expected, cp); + result = 1; + } + } + + if (result == 0) + puts ("all tests OK"); + + return result; +} +#endif diff --git a/com32/libutil/sha512crypt.c b/com32/libutil/sha512crypt.c new file mode 100644 index 0000000..9553ec1 --- /dev/null +++ b/com32/libutil/sha512crypt.c @@ -0,0 +1,794 @@ +/* SHA512-based Unix crypt implementation. + Released into the Public Domain by Ulrich Drepper . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xcrypt.h" + +#define MIN(x,y) min(x,y) +#define MAX(x,y) max(x,y) + +/* Structure to save state of computation between the single steps. */ +struct sha512_ctx +{ + uint64_t H[8]; + + uint64_t total[2]; + uint64_t buflen; + char buffer[256]; /* NB: always correctly aligned for uint64_t. */ +}; + + +#if __BYTE_ORDER == __LITTLE_ENDIAN +# define SWAP(n) \ + (((n) << 56) \ + | (((n) & 0xff00) << 40) \ + | (((n) & 0xff0000) << 24) \ + | (((n) & 0xff000000) << 8) \ + | (((n) >> 8) & 0xff000000) \ + | (((n) >> 24) & 0xff0000) \ + | (((n) >> 40) & 0xff00) \ + | ((n) >> 56)) +#else +# define SWAP(n) (n) +#endif + + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (FIPS 180-2:5.1.2) */ +static const unsigned char fillbuf[128] = { 0x80, 0 /* , 0, 0, ... */ }; + + +/* Constants for SHA512 from FIPS 180-2:4.2.3. */ +static const uint64_t K[80] = + { + UINT64_C (0x428a2f98d728ae22), UINT64_C (0x7137449123ef65cd), + UINT64_C (0xb5c0fbcfec4d3b2f), UINT64_C (0xe9b5dba58189dbbc), + UINT64_C (0x3956c25bf348b538), UINT64_C (0x59f111f1b605d019), + UINT64_C (0x923f82a4af194f9b), UINT64_C (0xab1c5ed5da6d8118), + UINT64_C (0xd807aa98a3030242), UINT64_C (0x12835b0145706fbe), + UINT64_C (0x243185be4ee4b28c), UINT64_C (0x550c7dc3d5ffb4e2), + UINT64_C (0x72be5d74f27b896f), UINT64_C (0x80deb1fe3b1696b1), + UINT64_C (0x9bdc06a725c71235), UINT64_C (0xc19bf174cf692694), + UINT64_C (0xe49b69c19ef14ad2), UINT64_C (0xefbe4786384f25e3), + UINT64_C (0x0fc19dc68b8cd5b5), UINT64_C (0x240ca1cc77ac9c65), + UINT64_C (0x2de92c6f592b0275), UINT64_C (0x4a7484aa6ea6e483), + UINT64_C (0x5cb0a9dcbd41fbd4), UINT64_C (0x76f988da831153b5), + UINT64_C (0x983e5152ee66dfab), UINT64_C (0xa831c66d2db43210), + UINT64_C (0xb00327c898fb213f), UINT64_C (0xbf597fc7beef0ee4), + UINT64_C (0xc6e00bf33da88fc2), UINT64_C (0xd5a79147930aa725), + UINT64_C (0x06ca6351e003826f), UINT64_C (0x142929670a0e6e70), + UINT64_C (0x27b70a8546d22ffc), UINT64_C (0x2e1b21385c26c926), + UINT64_C (0x4d2c6dfc5ac42aed), UINT64_C (0x53380d139d95b3df), + UINT64_C (0x650a73548baf63de), UINT64_C (0x766a0abb3c77b2a8), + UINT64_C (0x81c2c92e47edaee6), UINT64_C (0x92722c851482353b), + UINT64_C (0xa2bfe8a14cf10364), UINT64_C (0xa81a664bbc423001), + UINT64_C (0xc24b8b70d0f89791), UINT64_C (0xc76c51a30654be30), + UINT64_C (0xd192e819d6ef5218), UINT64_C (0xd69906245565a910), + UINT64_C (0xf40e35855771202a), UINT64_C (0x106aa07032bbd1b8), + UINT64_C (0x19a4c116b8d2d0c8), UINT64_C (0x1e376c085141ab53), + UINT64_C (0x2748774cdf8eeb99), UINT64_C (0x34b0bcb5e19b48a8), + UINT64_C (0x391c0cb3c5c95a63), UINT64_C (0x4ed8aa4ae3418acb), + UINT64_C (0x5b9cca4f7763e373), UINT64_C (0x682e6ff3d6b2b8a3), + UINT64_C (0x748f82ee5defb2fc), UINT64_C (0x78a5636f43172f60), + UINT64_C (0x84c87814a1f0ab72), UINT64_C (0x8cc702081a6439ec), + UINT64_C (0x90befffa23631e28), UINT64_C (0xa4506cebde82bde9), + UINT64_C (0xbef9a3f7b2c67915), UINT64_C (0xc67178f2e372532b), + UINT64_C (0xca273eceea26619c), UINT64_C (0xd186b8c721c0c207), + UINT64_C (0xeada7dd6cde0eb1e), UINT64_C (0xf57d4f7fee6ed178), + UINT64_C (0x06f067aa72176fba), UINT64_C (0x0a637dc5a2c898a6), + UINT64_C (0x113f9804bef90dae), UINT64_C (0x1b710b35131c471b), + UINT64_C (0x28db77f523047d84), UINT64_C (0x32caab7b40c72493), + UINT64_C (0x3c9ebe0a15c9bebc), UINT64_C (0x431d67c49c100d4c), + UINT64_C (0x4cc5d4becb3e42b6), UINT64_C (0x597f299cfc657e2a), + UINT64_C (0x5fcb6fab3ad6faec), UINT64_C (0x6c44198c4a475817) + }; + + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 128 == 0. */ +static void +sha512_process_block (const void *buffer, size_t len, struct sha512_ctx *ctx) +{ + unsigned int t; + const uint64_t *words = buffer; + size_t nwords = len / sizeof (uint64_t); + uint64_t a = ctx->H[0]; + uint64_t b = ctx->H[1]; + uint64_t c = ctx->H[2]; + uint64_t d = ctx->H[3]; + uint64_t e = ctx->H[4]; + uint64_t f = ctx->H[5]; + uint64_t g = ctx->H[6]; + uint64_t h = ctx->H[7]; + + /* First increment the byte count. FIPS 180-2 specifies the possible + length of the file up to 2^128 bits. Here we only compute the + number of bytes. Do a double word increment. */ + ctx->total[0] += len; + if (ctx->total[0] < len) + ++ctx->total[1]; + + /* Process all bytes in the buffer with 128 bytes in each round of + the loop. */ + while (nwords > 0) + { + uint64_t W[80]; + uint64_t a_save = a; + uint64_t b_save = b; + uint64_t c_save = c; + uint64_t d_save = d; + uint64_t e_save = e; + uint64_t f_save = f; + uint64_t g_save = g; + uint64_t h_save = h; + + /* Operators defined in FIPS 180-2:4.1.2. */ +#define Ch(x, y, z) ((x & y) ^ (~x & z)) +#define Maj(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) +#define S0(x) (CYCLIC (x, 28) ^ CYCLIC (x, 34) ^ CYCLIC (x, 39)) +#define S1(x) (CYCLIC (x, 14) ^ CYCLIC (x, 18) ^ CYCLIC (x, 41)) +#define R0(x) (CYCLIC (x, 1) ^ CYCLIC (x, 8) ^ (x >> 7)) +#define R1(x) (CYCLIC (x, 19) ^ CYCLIC (x, 61) ^ (x >> 6)) + + /* It is unfortunate that C does not provide an operator for + cyclic rotation. Hope the C compiler is smart enough. */ +#define CYCLIC(w, s) ((w >> s) | (w << (64 - s))) + + /* Compute the message schedule according to FIPS 180-2:6.3.2 step 2. */ + for (t = 0; t < 16; ++t) + { + W[t] = SWAP (*words); + ++words; + } + for (t = 16; t < 80; ++t) + W[t] = R1 (W[t - 2]) + W[t - 7] + R0 (W[t - 15]) + W[t - 16]; + + /* The actual computation according to FIPS 180-2:6.3.2 step 3. */ + for (t = 0; t < 80; ++t) + { + uint64_t T1 = h + S1 (e) + Ch (e, f, g) + K[t] + W[t]; + uint64_t T2 = S0 (a) + Maj (a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + } + + /* Add the starting values of the context according to FIPS 180-2:6.3.2 + step 4. */ + a += a_save; + b += b_save; + c += c_save; + d += d_save; + e += e_save; + f += f_save; + g += g_save; + h += h_save; + + /* Prepare for the next round. */ + nwords -= 16; + } + + /* Put checksum in context given as argument. */ + ctx->H[0] = a; + ctx->H[1] = b; + ctx->H[2] = c; + ctx->H[3] = d; + ctx->H[4] = e; + ctx->H[5] = f; + ctx->H[6] = g; + ctx->H[7] = h; +} + + +/* Initialize structure containing state of computation. + (FIPS 180-2:5.3.3) */ +static void +sha512_init_ctx (struct sha512_ctx *ctx) +{ + ctx->H[0] = UINT64_C (0x6a09e667f3bcc908); + ctx->H[1] = UINT64_C (0xbb67ae8584caa73b); + ctx->H[2] = UINT64_C (0x3c6ef372fe94f82b); + ctx->H[3] = UINT64_C (0xa54ff53a5f1d36f1); + ctx->H[4] = UINT64_C (0x510e527fade682d1); + ctx->H[5] = UINT64_C (0x9b05688c2b3e6c1f); + ctx->H[6] = UINT64_C (0x1f83d9abfb41bd6b); + ctx->H[7] = UINT64_C (0x5be0cd19137e2179); + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} + + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +static void * +sha512_finish_ctx (struct sha512_ctx *ctx, void *resbuf) +{ + unsigned int i; + /* Take yet unprocessed bytes into account. */ + uint64_t bytes = ctx->buflen; + size_t pad; + + /* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; + + pad = bytes >= 112 ? 128 + 112 - bytes : 112 - bytes; + memcpy (&ctx->buffer[bytes], fillbuf, pad); + + /* Put the 128-bit file length in *bits* at the end of the buffer. */ + *(uint64_t *) &ctx->buffer[bytes + pad + 8] = SWAP (ctx->total[0] << 3); + *(uint64_t *) &ctx->buffer[bytes + pad] = SWAP ((ctx->total[1] << 3) | + (ctx->total[0] >> 61)); + + /* Process last bytes. */ + sha512_process_block (ctx->buffer, bytes + pad + 16, ctx); + + /* Put result from CTX in first 64 bytes following RESBUF. */ + for (i = 0; i < 8; ++i) + ((uint64_t *) resbuf)[i] = SWAP (ctx->H[i]); + + return resbuf; +} + + +static void +sha512_process_bytes (const void *buffer, size_t len, struct sha512_ctx *ctx) +{ + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (ctx->buflen != 0) + { + size_t left_over = ctx->buflen; + size_t add = 256 - left_over > len ? len : 256 - left_over; + + memcpy (&ctx->buffer[left_over], buffer, add); + ctx->buflen += add; + + if (ctx->buflen > 128) + { + sha512_process_block (ctx->buffer, ctx->buflen & ~127, ctx); + + ctx->buflen &= 127; + /* The regions in the following copy operation cannot overlap. */ + memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~127], + ctx->buflen); + } + + buffer = (const char *) buffer + add; + len -= add; + } + + /* Process available complete blocks. */ + if (len >= 128) + { +#if !_STRING_ARCH_unaligned +/* To check alignment gcc has an appropriate operator. Other + compilers don't. */ +# if __GNUC__ >= 2 +# define UNALIGNED_P(p) (((uintptr_t) p) % __alignof__ (uint64_t) != 0) +# else +# define UNALIGNED_P(p) (((uintptr_t) p) % sizeof (uint64_t) != 0) +# endif + if (UNALIGNED_P (buffer)) + while (len > 128) + { + sha512_process_block (memcpy (ctx->buffer, buffer, 128), 128, + ctx); + buffer = (const char *) buffer + 128; + len -= 128; + } + else +#endif + { + sha512_process_block (buffer, len & ~127, ctx); + buffer = (const char *) buffer + (len & ~127); + len &= 127; + } + } + + /* Move remaining bytes into internal buffer. */ + if (len > 0) + { + size_t left_over = ctx->buflen; + + memcpy (&ctx->buffer[left_over], buffer, len); + left_over += len; + if (left_over >= 128) + { + sha512_process_block (ctx->buffer, 128, ctx); + left_over -= 128; + memcpy (ctx->buffer, &ctx->buffer[128], left_over); + } + ctx->buflen = left_over; + } +} + + +/* Define our magic string to mark salt for SHA512 "encryption" + replacement. */ +static const char sha512_salt_prefix[] = "$6$"; + +/* Prefix for optional rounds specification. */ +static const char sha512_rounds_prefix[] = "rounds="; + +/* Maximum salt string length. */ +#define SALT_LEN_MAX 16 +/* Default number of rounds if not explicitly specified. */ +#define ROUNDS_DEFAULT 5000 +/* Minimum number of rounds. */ +#define ROUNDS_MIN 1000 +/* Maximum number of rounds. */ +#define ROUNDS_MAX 999999999 + +/* Table with characters for base64 transformation. */ +static const char b64t[64] = +"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + +static char * +sha512_crypt_r (const char *key, const char *salt, char *buffer, int buflen) +{ + unsigned char alt_result[64] + __attribute__ ((__aligned__ (__alignof__ (uint64_t)))); + unsigned char temp_result[64] + __attribute__ ((__aligned__ (__alignof__ (uint64_t)))); + struct sha512_ctx ctx; + struct sha512_ctx alt_ctx; + size_t salt_len; + size_t key_len; + size_t cnt; + char *cp; + char *copied_key = NULL; + char *copied_salt = NULL; + char *p_bytes; + char *s_bytes; + /* Default number of rounds. */ + size_t rounds = ROUNDS_DEFAULT; + bool rounds_custom = false; + + /* Find beginning of salt string. The prefix should normally always + be present. Just in case it is not. */ + if (strncmp (sha512_salt_prefix, salt, sizeof (sha512_salt_prefix) - 1) == 0) + /* Skip salt prefix. */ + salt += sizeof (sha512_salt_prefix) - 1; + + if (strncmp (salt, sha512_rounds_prefix, sizeof (sha512_rounds_prefix) - 1) + == 0) + { + const char *num = salt + sizeof (sha512_rounds_prefix) - 1; + char *endp; + unsigned long int srounds = strtoul (num, &endp, 10); + if (*endp == '$') + { + salt = endp + 1; + rounds = MAX (ROUNDS_MIN, MIN (srounds, ROUNDS_MAX)); + rounds_custom = true; + } + } + + salt_len = MIN (strcspn (salt, "$"), SALT_LEN_MAX); + key_len = strlen (key); + + if ((key - (char *) 0) % __alignof__ (uint64_t) != 0) + { + char *tmp = (char *) alloca (key_len + __alignof__ (uint64_t)); + key = copied_key = + memcpy (tmp + __alignof__ (uint64_t) + - (tmp - (char *) 0) % __alignof__ (uint64_t), + key, key_len); + } + + if ((salt - (char *) 0) % __alignof__ (uint64_t) != 0) + { + char *tmp = (char *) alloca (salt_len + __alignof__ (uint64_t)); + salt = copied_salt = + memcpy (tmp + __alignof__ (uint64_t) + - (tmp - (char *) 0) % __alignof__ (uint64_t), + salt, salt_len); + } + + /* Prepare for the real work. */ + sha512_init_ctx (&ctx); + + /* Add the key string. */ + sha512_process_bytes (key, key_len, &ctx); + + /* The last part is the salt string. This must be at most 8 + characters and it ends at the first `$' character (for + compatibility with existing implementations). */ + sha512_process_bytes (salt, salt_len, &ctx); + + + /* Compute alternate SHA512 sum with input KEY, SALT, and KEY. The + final result will be added to the first context. */ + sha512_init_ctx (&alt_ctx); + + /* Add key. */ + sha512_process_bytes (key, key_len, &alt_ctx); + + /* Add salt. */ + sha512_process_bytes (salt, salt_len, &alt_ctx); + + /* Add key again. */ + sha512_process_bytes (key, key_len, &alt_ctx); + + /* Now get result of this (64 bytes) and add it to the other + context. */ + sha512_finish_ctx (&alt_ctx, alt_result); + + /* Add for any character in the key one byte of the alternate sum. */ + for (cnt = key_len; cnt > 64; cnt -= 64) + sha512_process_bytes (alt_result, 64, &ctx); + sha512_process_bytes (alt_result, cnt, &ctx); + + /* Take the binary representation of the length of the key and for every + 1 add the alternate sum, for every 0 the key. */ + for (cnt = key_len; cnt > 0; cnt >>= 1) + if ((cnt & 1) != 0) + sha512_process_bytes (alt_result, 64, &ctx); + else + sha512_process_bytes (key, key_len, &ctx); + + /* Create intermediate result. */ + sha512_finish_ctx (&ctx, alt_result); + + /* Start computation of P byte sequence. */ + sha512_init_ctx (&alt_ctx); + + /* For every character in the password add the entire password. */ + for (cnt = 0; cnt < key_len; ++cnt) + sha512_process_bytes (key, key_len, &alt_ctx); + + /* Finish the digest. */ + sha512_finish_ctx (&alt_ctx, temp_result); + + /* Create byte sequence P. */ + cp = p_bytes = alloca (key_len); + for (cnt = key_len; cnt >= 64; cnt -= 64) + cp = mempcpy (cp, temp_result, 64); + memcpy (cp, temp_result, cnt); + + /* Start computation of S byte sequence. */ + sha512_init_ctx (&alt_ctx); + + /* For every character in the password add the entire password. */ + for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt) + sha512_process_bytes (salt, salt_len, &alt_ctx); + + /* Finish the digest. */ + sha512_finish_ctx (&alt_ctx, temp_result); + + /* Create byte sequence S. */ + cp = s_bytes = alloca (salt_len); + for (cnt = salt_len; cnt >= 64; cnt -= 64) + cp = mempcpy (cp, temp_result, 64); + memcpy (cp, temp_result, cnt); + + /* Repeatedly run the collected hash value through SHA512 to burn + CPU cycles. */ + for (cnt = 0; cnt < rounds; ++cnt) + { + /* New context. */ + sha512_init_ctx (&ctx); + + /* Add key or last result. */ + if ((cnt & 1) != 0) + sha512_process_bytes (p_bytes, key_len, &ctx); + else + sha512_process_bytes (alt_result, 64, &ctx); + + /* Add salt for numbers not divisible by 3. */ + if (cnt % 3 != 0) + sha512_process_bytes (s_bytes, salt_len, &ctx); + + /* Add key for numbers not divisible by 7. */ + if (cnt % 7 != 0) + sha512_process_bytes (p_bytes, key_len, &ctx); + + /* Add key or last result. */ + if ((cnt & 1) != 0) + sha512_process_bytes (alt_result, 64, &ctx); + else + sha512_process_bytes (p_bytes, key_len, &ctx); + + /* Create intermediate result. */ + sha512_finish_ctx (&ctx, alt_result); + } + + /* Now we can construct the result string. It consists of three + parts. */ + cp = stpncpy (buffer, sha512_salt_prefix, MAX (0, buflen)); + buflen -= sizeof (sha512_salt_prefix) - 1; + + if (rounds_custom) + { + int n = snprintf (cp, MAX (0, buflen), "%s%zu$", + sha512_rounds_prefix, rounds); + cp += n; + buflen -= n; + } + + cp = stpncpy (cp, salt, MIN ((size_t) MAX (0, buflen), salt_len)); + buflen -= MIN ((size_t) MAX (0, buflen), salt_len); + + if (buflen > 0) + { + *cp++ = '$'; + --buflen; + } + +#define b64_from_24bit(B2, B1, B0, N) \ + do { \ + unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \ + int n = (N); \ + while (n-- > 0 && buflen > 0) \ + { \ + *cp++ = b64t[w & 0x3f]; \ + --buflen; \ + w >>= 6; \ + } \ + } while (0) + + b64_from_24bit (alt_result[0], alt_result[21], alt_result[42], 4); + b64_from_24bit (alt_result[22], alt_result[43], alt_result[1], 4); + b64_from_24bit (alt_result[44], alt_result[2], alt_result[23], 4); + b64_from_24bit (alt_result[3], alt_result[24], alt_result[45], 4); + b64_from_24bit (alt_result[25], alt_result[46], alt_result[4], 4); + b64_from_24bit (alt_result[47], alt_result[5], alt_result[26], 4); + b64_from_24bit (alt_result[6], alt_result[27], alt_result[48], 4); + b64_from_24bit (alt_result[28], alt_result[49], alt_result[7], 4); + b64_from_24bit (alt_result[50], alt_result[8], alt_result[29], 4); + b64_from_24bit (alt_result[9], alt_result[30], alt_result[51], 4); + b64_from_24bit (alt_result[31], alt_result[52], alt_result[10], 4); + b64_from_24bit (alt_result[53], alt_result[11], alt_result[32], 4); + b64_from_24bit (alt_result[12], alt_result[33], alt_result[54], 4); + b64_from_24bit (alt_result[34], alt_result[55], alt_result[13], 4); + b64_from_24bit (alt_result[56], alt_result[14], alt_result[35], 4); + b64_from_24bit (alt_result[15], alt_result[36], alt_result[57], 4); + b64_from_24bit (alt_result[37], alt_result[58], alt_result[16], 4); + b64_from_24bit (alt_result[59], alt_result[17], alt_result[38], 4); + b64_from_24bit (alt_result[18], alt_result[39], alt_result[60], 4); + b64_from_24bit (alt_result[40], alt_result[61], alt_result[19], 4); + b64_from_24bit (alt_result[62], alt_result[20], alt_result[41], 4); + b64_from_24bit (0, 0, alt_result[63], 2); + + if (buflen <= 0) + { + errno = ERANGE; + buffer = NULL; + } + else + *cp = '\0'; /* Terminate the string. */ + + /* Clear the buffer for the intermediate result so that people + attaching to processes or reading core dumps cannot get any + information. We do it in this way to clear correct_words[] + inside the SHA512 implementation as well. */ + sha512_init_ctx (&ctx); + sha512_finish_ctx (&ctx, alt_result); + memset (temp_result, '\0', sizeof (temp_result)); + memset (p_bytes, '\0', key_len); + memset (s_bytes, '\0', salt_len); + memset (&ctx, '\0', sizeof (ctx)); + memset (&alt_ctx, '\0', sizeof (alt_ctx)); + if (copied_key != NULL) + memset (copied_key, '\0', key_len); + if (copied_salt != NULL) + memset (copied_salt, '\0', salt_len); + + return buffer; +} + + +/* This entry point is equivalent to the `crypt' function in Unix + libcs. */ +char * +sha512_crypt (const char *key, const char *salt) +{ + /* We don't want to have an arbitrary limit in the size of the + password. We can compute an upper bound for the size of the + result in advance and so we can prepare the buffer we pass to + `sha512_crypt_r'. */ + static char *buffer; + static int buflen; + int needed = (sizeof (sha512_salt_prefix) - 1 + + sizeof (sha512_rounds_prefix) + 9 + 1 + + strlen (salt) + 1 + 86 + 1); + + if (buflen < needed) + { + char *new_buffer = (char *) realloc (buffer, needed); + if (new_buffer == NULL) + return NULL; + + buffer = new_buffer; + buflen = needed; + } + + return sha512_crypt_r (key, salt, buffer, buflen); +} + + +#ifdef TEST +static const struct +{ + const char *input; + const char result[64]; +} tests[] = + { + /* Test vectors from FIPS 180-2: appendix C.1. */ + { "abc", + "\xdd\xaf\x35\xa1\x93\x61\x7a\xba\xcc\x41\x73\x49\xae\x20\x41\x31" + "\x12\xe6\xfa\x4e\x89\xa9\x7e\xa2\x0a\x9e\xee\xe6\x4b\x55\xd3\x9a" + "\x21\x92\x99\x2a\x27\x4f\xc1\xa8\x36\xba\x3c\x23\xa3\xfe\xeb\xbd" + "\x45\x4d\x44\x23\x64\x3c\xe8\x0e\x2a\x9a\xc9\x4f\xa5\x4c\xa4\x9f" }, + /* Test vectors from FIPS 180-2: appendix C.2. */ + { "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" + "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + "\x8e\x95\x9b\x75\xda\xe3\x13\xda\x8c\xf4\xf7\x28\x14\xfc\x14\x3f" + "\x8f\x77\x79\xc6\xeb\x9f\x7f\xa1\x72\x99\xae\xad\xb6\x88\x90\x18" + "\x50\x1d\x28\x9e\x49\x00\xf7\xe4\x33\x1b\x99\xde\xc4\xb5\x43\x3a" + "\xc7\xd3\x29\xee\xb6\xdd\x26\x54\x5e\x96\xe5\x5b\x87\x4b\xe9\x09" }, + /* Test vectors from the NESSIE project. */ + { "", + "\xcf\x83\xe1\x35\x7e\xef\xb8\xbd\xf1\x54\x28\x50\xd6\x6d\x80\x07" + "\xd6\x20\xe4\x05\x0b\x57\x15\xdc\x83\xf4\xa9\x21\xd3\x6c\xe9\xce" + "\x47\xd0\xd1\x3c\x5d\x85\xf2\xb0\xff\x83\x18\xd2\x87\x7e\xec\x2f" + "\x63\xb9\x31\xbd\x47\x41\x7a\x81\xa5\x38\x32\x7a\xf9\x27\xda\x3e" }, + { "a", + "\x1f\x40\xfc\x92\xda\x24\x16\x94\x75\x09\x79\xee\x6c\xf5\x82\xf2" + "\xd5\xd7\xd2\x8e\x18\x33\x5d\xe0\x5a\xbc\x54\xd0\x56\x0e\x0f\x53" + "\x02\x86\x0c\x65\x2b\xf0\x8d\x56\x02\x52\xaa\x5e\x74\x21\x05\x46" + "\xf3\x69\xfb\xbb\xce\x8c\x12\xcf\xc7\x95\x7b\x26\x52\xfe\x9a\x75" }, + { "message digest", + "\x10\x7d\xbf\x38\x9d\x9e\x9f\x71\xa3\xa9\x5f\x6c\x05\x5b\x92\x51" + "\xbc\x52\x68\xc2\xbe\x16\xd6\xc1\x34\x92\xea\x45\xb0\x19\x9f\x33" + "\x09\xe1\x64\x55\xab\x1e\x96\x11\x8e\x8a\x90\x5d\x55\x97\xb7\x20" + "\x38\xdd\xb3\x72\xa8\x98\x26\x04\x6d\xe6\x66\x87\xbb\x42\x0e\x7c" }, + { "abcdefghijklmnopqrstuvwxyz", + "\x4d\xbf\xf8\x6c\xc2\xca\x1b\xae\x1e\x16\x46\x8a\x05\xcb\x98\x81" + "\xc9\x7f\x17\x53\xbc\xe3\x61\x90\x34\x89\x8f\xaa\x1a\xab\xe4\x29" + "\x95\x5a\x1b\xf8\xec\x48\x3d\x74\x21\xfe\x3c\x16\x46\x61\x3a\x59" + "\xed\x54\x41\xfb\x0f\x32\x13\x89\xf7\x7f\x48\xa8\x79\xc7\xb1\xf1" }, + { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "\x20\x4a\x8f\xc6\xdd\xa8\x2f\x0a\x0c\xed\x7b\xeb\x8e\x08\xa4\x16" + "\x57\xc1\x6e\xf4\x68\xb2\x28\xa8\x27\x9b\xe3\x31\xa7\x03\xc3\x35" + "\x96\xfd\x15\xc1\x3b\x1b\x07\xf9\xaa\x1d\x3b\xea\x57\x78\x9c\xa0" + "\x31\xad\x85\xc7\xa7\x1d\xd7\x03\x54\xec\x63\x12\x38\xca\x34\x45" }, + { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + "\x1e\x07\xbe\x23\xc2\x6a\x86\xea\x37\xea\x81\x0c\x8e\xc7\x80\x93" + "\x52\x51\x5a\x97\x0e\x92\x53\xc2\x6f\x53\x6c\xfc\x7a\x99\x96\xc4" + "\x5c\x83\x70\x58\x3e\x0a\x78\xfa\x4a\x90\x04\x1d\x71\xa4\xce\xab" + "\x74\x23\xf1\x9c\x71\xb9\xd5\xa3\xe0\x12\x49\xf0\xbe\xbd\x58\x94" }, + { "123456789012345678901234567890123456789012345678901234567890" + "12345678901234567890", + "\x72\xec\x1e\xf1\x12\x4a\x45\xb0\x47\xe8\xb7\xc7\x5a\x93\x21\x95" + "\x13\x5b\xb6\x1d\xe2\x4e\xc0\xd1\x91\x40\x42\x24\x6e\x0a\xec\x3a" + "\x23\x54\xe0\x93\xd7\x6f\x30\x48\xb4\x56\x76\x43\x46\x90\x0c\xb1" + "\x30\xd2\xa4\xfd\x5d\xd1\x6a\xbb\x5e\x30\xbc\xb8\x50\xde\xe8\x43" } + }; +#define ntests (sizeof (tests) / sizeof (tests[0])) + + +static const struct +{ + const char *salt; + const char *input; + const char *expected; +} tests2[] = +{ + { "$6$saltstring", "Hello world!", + "$6$saltstring$svn8UoSVapNtMuq1ukKS4tPQd8iKwSMHWjl/O817G3uBnIFNjnQJu" + "esI68u4OTLiBFdcbYEdFCoEOfaS35inz1" }, + { "$6$rounds=10000$saltstringsaltstring", "Hello world!", + "$6$rounds=10000$saltstringsaltst$OW1/O6BYHV6BcXZu8QVeXbDWra3Oeqh0sb" + "HbbMCVNSnCM/UrjmM0Dp8vOuZeHBy/YTBmSK6H9qs/y3RnOaw5v." }, + { "$6$rounds=5000$toolongsaltstring", "This is just a test", + "$6$rounds=5000$toolongsaltstrin$lQ8jolhgVRVhY4b5pZKaysCLi0QBxGoNeKQ" + "zQ3glMhwllF7oGDZxUhx1yxdYcz/e1JSbq3y6JMxxl8audkUEm0" }, + { "$6$rounds=1400$anotherlongsaltstring", + "a very much longer text to encrypt. This one even stretches over more" + "than one line.", + "$6$rounds=1400$anotherlongsalts$POfYwTEok97VWcjxIiSOjiykti.o/pQs.wP" + "vMxQ6Fm7I6IoYN3CmLs66x9t0oSwbtEW7o7UmJEiDwGqd8p4ur1" }, + { "$6$rounds=77777$short", + "we have a short salt string but not a short password", + "$6$rounds=77777$short$WuQyW2YR.hBNpjjRhpYD/ifIw05xdfeEyQoMxIXbkvr0g" + "ge1a1x3yRULJ5CCaUeOxFmtlcGZelFl5CxtgfiAc0" }, + { "$6$rounds=123456$asaltof16chars..", "a short string", + "$6$rounds=123456$asaltof16chars..$BtCwjqMJGx5hrJhZywWvt0RLE8uZ4oPwc" + "elCjmw2kSYu.Ec6ycULevoBK25fs2xXgMNrCzIMVcgEJAstJeonj1" }, + { "$6$rounds=10$roundstoolow", "the minimum number is still observed", + "$6$rounds=1000$roundstoolow$kUMsbe306n21p9R.FRkW3IGn.S9NPN0x50YhH1x" + "hLsPuWGsUSklZt58jaTfF4ZEQpyUNGc0dqbpBYYBaHHrsX." }, +}; +#define ntests2 (sizeof (tests2) / sizeof (tests2[0])) + + +int +main (void) +{ + struct sha512_ctx ctx; + char sum[64]; + int result = 0; + int cnt; + + for (cnt = 0; cnt < (int) ntests; ++cnt) + { + sha512_init_ctx (&ctx); + sha512_process_bytes (tests[cnt].input, strlen (tests[cnt].input), &ctx); + sha512_finish_ctx (&ctx, sum); + if (memcmp (tests[cnt].result, sum, 64) != 0) + { + printf ("test %d run %d failed\n", cnt, 1); + result = 1; + } + + sha512_init_ctx (&ctx); + for (int i = 0; tests[cnt].input[i] != '\0'; ++i) + sha512_process_bytes (&tests[cnt].input[i], 1, &ctx); + sha512_finish_ctx (&ctx, sum); + if (memcmp (tests[cnt].result, sum, 64) != 0) + { + printf ("test %d run %d failed\n", cnt, 2); + result = 1; + } + } + + /* Test vector from FIPS 180-2: appendix C.3. */ + char buf[1000]; + memset (buf, 'a', sizeof (buf)); + sha512_init_ctx (&ctx); + for (int i = 0; i < 1000; ++i) + sha512_process_bytes (buf, sizeof (buf), &ctx); + sha512_finish_ctx (&ctx, sum); + static const char expected[64] = + "\xe7\x18\x48\x3d\x0c\xe7\x69\x64\x4e\x2e\x42\xc7\xbc\x15\xb4\x63" + "\x8e\x1f\x98\xb1\x3b\x20\x44\x28\x56\x32\xa8\x03\xaf\xa9\x73\xeb" + "\xde\x0f\xf2\x44\x87\x7e\xa6\x0a\x4c\xb0\x43\x2c\xe5\x77\xc3\x1b" + "\xeb\x00\x9c\x5c\x2c\x49\xaa\x2e\x4e\xad\xb2\x17\xad\x8c\xc0\x9b"; + if (memcmp (expected, sum, 64) != 0) + { + printf ("test %d failed\n", cnt); + result = 1; + } + + for (cnt = 0; cnt < ntests2; ++cnt) + { + char *cp = sha512_crypt (tests2[cnt].input, tests2[cnt].salt); + + if (strcmp (cp, tests2[cnt].expected) != 0) + { + printf ("test %d: expected \"%s\", got \"%s\"\n", + cnt, tests2[cnt].expected, cp); + result = 1; + } + } + + if (result == 0) + puts ("all tests OK"); + + return result; +} +#endif diff --git a/com32/libutil/unbase64.c b/com32/libutil/unbase64.c new file mode 100644 index 0000000..a1728d1 --- /dev/null +++ b/com32/libutil/unbase64.c @@ -0,0 +1,77 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2005-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * unbase64.c + * + * Convert a string in base64 format to a byte array. + */ + +#include +#include + +static const unsigned char _base64chars[] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +size_t unbase64(unsigned char *buffer, size_t bufsiz, const char *txt) +{ + unsigned int bits = 0; + int nbits = 0; + char base64tbl[256]; + int i; + char v; + size_t nbytes = 0; + + + memset(base64tbl, -1, sizeof base64tbl); + + for ( i = 0 ; _base64chars[i] ; i++ ) { + base64tbl[_base64chars[i]] = i; + } + + /* Also support filesystem safe alternate base64 encoding */ + base64tbl['.'] = 62; + base64tbl['-'] = 62; + base64tbl['_'] = 63; + + while ( *txt ) { + if ( (v = base64tbl[(unsigned char) *txt]) >= 0 ) { + bits <<= 6; + bits += v; + nbits += 6; + if ( nbits >= 8 ) { + if ( nbytes < bufsiz ) + *buffer++ = (bits >> (nbits-8)); + nbytes++; + nbits -= 8; + } + } + txt++; + } + + return nbytes; +} diff --git a/com32/mboot/Makefile b/com32/mboot/Makefile new file mode 100644 index 0000000..a690271 --- /dev/null +++ b/com32/mboot/Makefile @@ -0,0 +1,46 @@ +## ----------------------------------------------------------------------- +## +## Copyright 2001-2009 H. Peter Anvin - All Rights Reserved +## Copyright 2009 Intel Corporation; author: H. Peter Anvin +## +## 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, Inc., 51 Franklin St, Fifth Floor, +## Boston MA 02110-1301, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +## +## Multiboot module +## + +topdir = ../.. +include ../MCONFIG + +LIBS = ../libutil/libutil_com.a ../lib/libcom32.a $(LIBGCC) +LNXLIBS = ../libutil/libutil_lnx.a + +MODULES = mboot.c32 +TESTFILES = + +OBJS = mboot.o map.o mem.o apm.o solaris.o + +all: $(MODULES) $(TESTFILES) + +mboot.elf : $(OBJS) $(LIBS) $(C_LIBS) + $(LD) $(LDFLAGS) -o $@ $^ + +tidy dist: + rm -f *.o *.lo *.a *.lst *.elf .*.d *.tmp + +clean: tidy + rm -f *.lnx + +spotless: clean + rm -f *.lss *.c32 *.com + rm -f *~ \#* + +install: + +-include .*.d diff --git a/com32/mboot/apm.c b/com32/mboot/apm.c new file mode 100644 index 0000000..2cfb7de --- /dev/null +++ b/com32/mboot/apm.c @@ -0,0 +1,87 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * 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, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * Based on code from the Linux kernel: + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright 2007 rPath, Inc. - All Rights Reserved + * + * Original APM BIOS checking by Stephen Rothwell, May 1994 + * (sfr@canb.auug.org.au) + * + * This file is part of the Linux kernel, and is made available under + * the terms of the GNU General Public License version 2. + * + * ----------------------------------------------------------------------- */ + + +/* + * apm.c + * + * APM information for Multiboot + */ + +#include "mboot.h" +#include + +void mboot_apm(void) +{ + static struct apm_info apm; + com32sys_t ireg, oreg; + + memset(&ireg, 0, sizeof ireg); + + ireg.eax.w[0] = 0x5300; + __intcall(0x15, &ireg, &oreg); + + if (oreg.eflags.l & EFLAGS_CF) + return; /* No APM BIOS */ + + if (oreg.ebx.w[0] != 0x504d) + return; /* No "PM" signature */ + + if (!(oreg.ecx.w[0] & 0x02)) + return; /* 32 bits not supported */ + + /* Disconnect first, just in case */ + ireg.eax.b[0] = 0x04; + __intcall(0x15, &ireg, &oreg); + + /* 32-bit connect */ + ireg.eax.b[0] = 0x03; + __intcall(0x15, &ireg, &oreg); + + apm.cseg = oreg.eax.w[0]; + apm.offset = oreg.ebx.l; + apm.cseg_16 = oreg.ecx.w[0]; + apm.dseg_16 = oreg.edx.w[0]; + apm.cseg_len = oreg.esi.w[0]; + apm.cseg_16_len = oreg.esi.w[1]; + apm.dseg_16_len = oreg.edi.w[0]; + + /* Redo the installation check as the 32-bit connect; + some BIOSes return different flags this way... */ + + ireg.eax.b[0] = 0x00; + __intcall(0x15, &ireg, &oreg); + + if ((oreg.eflags.l & EFLAGS_CF) || (oreg.ebx.w[0] != 0x504d)) { + /* Failure with 32-bit connect, try to disconect and ignore */ + ireg.eax.b[0] = 0x04; + __intcall(0x15, &ireg, NULL); + return; + } + + apm.version = oreg.eax.w[0]; + + mbinfo.apm_table = map_data(&apm, sizeof apm, 4, false); + if (mbinfo.apm_table) + mbinfo.flags |= MB_INFO_APM_TABLE; +} diff --git a/com32/mboot/map.c b/com32/mboot/map.c new file mode 100644 index 0000000..15e3a28 --- /dev/null +++ b/com32/mboot/map.c @@ -0,0 +1,335 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * map.c + * + * Functions that deal with the memory map of various objects + */ + +#include "mboot.h" + +static struct syslinux_movelist *ml = NULL; +static struct syslinux_memmap *mmap = NULL, *amap = NULL; +static struct multiboot_header *mbh; +static addr_t mboot_high_water_mark = 0x100000; + +/* + * Note: although there is no such thing in the spec, at least Xen makes + * assumptions as to where in the memory space Grub would have loaded + * certain things. To support that, if "high" is set, then allocate this + * at an address strictly above any previous allocations. + * + * As a precaution, this also pads the data with zero up to the next + * alignment datum. + */ +addr_t map_data(const void *data, size_t len, size_t align, int flags) +{ + addr_t start = (flags & MAP_HIGH) ? mboot_high_water_mark : 0x2000; + addr_t pad = (flags & MAP_NOPAD) ? 0 : -len & (align-1); + addr_t xlen = len+pad; + + if (syslinux_memmap_find(amap, SMT_FREE, &start, &xlen, align) || + syslinux_add_memmap(&amap, start, len+pad, SMT_ALLOC) || + syslinux_add_movelist(&ml, start, (addr_t)data, len) || + (pad && syslinux_add_memmap(&mmap, start+len, pad, SMT_ZERO))) { + printf("Cannot map %zu bytes\n", len+pad); + return 0; + } + + dprintf("Mapping 0x%08x bytes (%#x pad) at 0x%08x\n", len, pad, start); + + if (start+len+pad > mboot_high_water_mark) + mboot_high_water_mark = start+len+pad; + + return start; +} + +addr_t map_string(const char *string) +{ + if (!string) + return 0; + else + return map_data(string, strlen(string)+1, 1, 0); +} + +int init_map(void) +{ + /* + * Note: mmap is the memory map (containing free and zeroed regions) + * needed by syslinux_shuffle_boot_pm(); amap is a map where we keep + * track ourselves which target memory ranges have already been + * allocated. + */ + mmap = syslinux_memory_map(); + amap = syslinux_dup_memmap(mmap); + if (!mmap || !amap) { + error("Failed to allocate initial memory map!\n"); + return -1; + } + +#if DEBUG + dprintf("Initial memory map:\n"); + syslinux_dump_memmap(stdout, mmap); +#endif + + return 0; +} + +int map_image(void *ptr, size_t len) +{ + int mbh_len; + char *cptr = ptr; + Elf32_Ehdr *eh = ptr; + Elf32_Phdr *ph; + Elf32_Shdr *sh; + unsigned int i; + uint32_t bad_flags; + + /* + * Search for the multiboot header... + */ + mbh_len = 0; + for (i = 0 ; i < MULTIBOOT_SEARCH ; i += 4) { + mbh = (struct multiboot_header *)((char *)ptr + i); + if (mbh->magic != MULTIBOOT_MAGIC) + continue; + if (mbh->magic + mbh->flags + mbh->checksum) + continue; + if (mbh->flags & MULTIBOOT_VIDEO_MODE) + mbh_len = 48; + else if (mbh->flags & MULTIBOOT_AOUT_KLUDGE) + mbh_len = 32; + else + mbh_len = 12; + + if (i + mbh_len < len) + mbh_len = 0; /* Invalid... */ + else + break; /* Found something... */ + } + + if (mbh_len) { + bad_flags = mbh->flags & (MULTIBOOT_UNSUPPORTED|MULTIBOOT_VIDEO_MODE); + if (bad_flags) { + printf("Unsupported Multiboot flags set: %#x\n", bad_flags); + return -1; + } + } + + if ( len < sizeof(Elf32_Ehdr) || + memcmp(eh->e_ident, "\x7f""ELF\1\1\1", 6) || + (eh->e_machine != EM_386 && eh->e_machine != EM_486 && + eh->e_machine != EM_X86_64) || + eh->e_version != EV_CURRENT || + eh->e_ehsize < sizeof(Elf32_Ehdr) || eh->e_ehsize >= len || + eh->e_phentsize < sizeof(Elf32_Phdr) || + !eh->e_phnum || + eh->e_phoff+eh->e_phentsize*eh->e_phnum > len ) + eh = NULL; /* No valid ELF header found */ + + /* + * Note: the Multiboot Specification implies that AOUT_KLUDGE should + * have precedence over the ELF header. However, Grub disagrees, and + * Grub is "the reference bootloader" for the Multiboot Specification. + * This is insane, since it makes the AOUT_KLUDGE bit functionally + * useless, but at least Solaris apparently depends on this behavior. + */ + if (eh && !(opt.aout && mbh_len && (mbh->flags & MULTIBOOT_AOUT_KLUDGE))) { + regs.eip = eh->e_entry; /* Can be overridden further down... */ + + ph = (Elf32_Phdr *)(cptr+eh->e_phoff); + + for (i = 0; i < eh->e_phnum; i++) { + if (ph->p_type == PT_LOAD || ph->p_type == PT_PHDR) { + /* + * This loads at p_paddr, which matches Grub. However, if + * e_entry falls within the p_vaddr range of this PHDR, then + * adjust it to match the p_paddr range... this is how Grub + * behaves, so it's by definition correct (it doesn't have to + * make sense...) + */ + addr_t addr = ph->p_paddr; + addr_t msize = ph->p_memsz; + addr_t dsize = min(msize, ph->p_filesz); + + if (eh->e_entry >= ph->p_vaddr && eh->e_entry < ph->p_vaddr + msize) + regs.eip = eh->e_entry + (ph->p_paddr - ph->p_vaddr); + + dprintf("Segment at 0x%08x data 0x%08x len 0x%08x\n", + addr, dsize, msize); + + if (syslinux_memmap_type(amap, addr, msize) != SMT_FREE) { + printf("Memory segment at 0x%08x (len 0x%08x) is unavailable\n", + addr, msize); + return -1; /* Memory region unavailable */ + } + + /* Mark this region as allocated in the available map */ + if (syslinux_add_memmap(&amap, addr, msize, SMT_ALLOC)) { + error("Overlapping segments found in ELF header\n"); + return -1; + } + + if (ph->p_filesz) { + /* Data present region. Create a move entry for it. */ + if (syslinux_add_movelist(&ml, addr, (addr_t)cptr+ph->p_offset, + dsize)) { + error("Failed to map PHDR data\n"); + return -1; + } + } + if (msize > dsize) { + /* Zero-filled region. Mark as a zero region in the memory map. */ + if (syslinux_add_memmap(&mmap, addr+dsize, msize-dsize, SMT_ZERO)) { + error("Failed to map PHDR zero region\n"); + return -1; + } + } + if (addr+msize > mboot_high_water_mark) + mboot_high_water_mark = addr+msize; + } else { + /* Ignore this program header */ + } + + ph = (Elf32_Phdr *)((char *)ph + eh->e_phentsize); + } + + /* Load the ELF symbol table */ + if (eh->e_shoff) { + addr_t addr, len; + + sh = (Elf32_Shdr *)((char *)eh + eh->e_shoff); + + len = eh->e_shentsize * eh->e_shnum; + /* + * Align this, but don't pad -- in general this means a bunch of + * smaller sections gets packed into a single page. + */ + addr = map_data(sh, len, 4096, MAP_HIGH|MAP_NOPAD); + if (!addr) { + error("Failed to map symbol table\n"); + return -1; + } + + mbinfo.flags |= MB_INFO_ELF_SHDR; + mbinfo.syms.e.addr = addr; + mbinfo.syms.e.num = eh->e_shnum; + mbinfo.syms.e.size = eh->e_shentsize; + mbinfo.syms.e.shndx = eh->e_shstrndx; + + for (i = 0; i < eh->e_shnum; i++) { + addr_t align; + + if (!sh[i].sh_size) + continue; /* Empty section */ + if (sh[i].sh_flags & SHF_ALLOC) + continue; /* SHF_ALLOC sections should have PHDRs */ + + align = sh[i].sh_addralign ? sh[i].sh_addralign : 0; + addr = map_data((char *)ptr + sh[i].sh_offset, sh[i].sh_size, + align, MAP_HIGH); + if (!addr) { + error("Failed to map symbol section\n"); + return -1; + } + sh[i].sh_addr = addr; + } + } + } else if (mbh_len && (mbh->flags & MULTIBOOT_AOUT_KLUDGE)) { + /* + * a.out kludge thing... + */ + char *data_ptr; + addr_t data_len, bss_len; + + regs.eip = mbh->entry_addr; + + data_ptr = (char *)mbh - (mbh->header_addr - mbh->load_addr); + data_len = mbh->load_end_addr - mbh->load_addr; + bss_len = mbh->bss_end_addr - mbh->load_end_addr; + + if (syslinux_memmap_type(amap, mbh->load_addr, data_len+bss_len) + != SMT_FREE) { + printf("Memory segment at 0x%08x (len 0x%08x) is unavailable\n", + mbh->load_addr, data_len+bss_len); + return -1; /* Memory region unavailable */ + } + if (syslinux_add_memmap(&amap, mbh->load_addr, + data_len+bss_len, SMT_ALLOC)) { + error("Failed to claim a.out address space!\n"); + return -1; + } + if (data_len) + if (syslinux_add_movelist(&ml, mbh->load_addr, (addr_t)data_ptr, + data_len)) { + error("Failed to map a.out data\n"); + return -1; + } + if (bss_len) + if (syslinux_add_memmap(&mmap, mbh->load_end_addr, bss_len, SMT_ZERO)) { + error("Failed to map a.out bss\n"); + return -1; + } + if (mbh->bss_end_addr > mboot_high_water_mark) + mboot_high_water_mark = mbh->bss_end_addr; + } else { + error("Invalid Multiboot image: neither ELF header nor a.out kludge found\n"); + return -1; + } + + return 0; +} + +/* + * Set up a stack. This isn't actually required by the spec, but it seems + * like a prudent thing to do. Also, put enough zeros at the top of the + * stack that something that looks for an ELF invocation record will know + * there isn't one. + */ +static void mboot_map_stack(void) +{ + addr_t start, len; + + if (syslinux_memmap_largest(amap, SMT_FREE, &start, &len) || len < 64) + return; /* Not much we can do, here... */ + + regs.esp = (start+len-32) & ~15; + dprintf("Mapping stack at 0x%08x\n", regs.esp); + syslinux_add_memmap(&mmap, regs.esp, 32, SMT_ZERO); +} + +void mboot_run(int bootflags) +{ + mboot_map_stack(); + + dprintf("Running, eip = 0x%08x, ebx = 0x%08x\n", regs.eip, regs.ebx); + + regs.eax = MULTIBOOT_VALID; + syslinux_shuffle_boot_pm(ml, mmap, bootflags, ®s); +} diff --git a/com32/mboot/mb_header.h b/com32/mboot/mb_header.h new file mode 100644 index 0000000..2a5be72 --- /dev/null +++ b/com32/mboot/mb_header.h @@ -0,0 +1,88 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000 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. + */ + +#ifndef MBOOT_MB_HEADER_H +#define MBOOT_MB_HEADER_H + +#include + +/* + * MultiBoot Header description + */ + +struct multiboot_header { + /* Must be MULTIBOOT_MAGIC - see below. */ + uint32_t magic; + + /* Feature flags - see below. */ + uint32_t flags; + + /* + * Checksum + * + * The above fields plus this one must equal 0 mod 2^32. + */ + uint32_t checksum; + + /* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */ + uint32_t header_addr; + uint32_t load_addr; + uint32_t load_end_addr; + uint32_t bss_end_addr; + uint32_t entry_addr; + + /* These are only valid if MULTIBOOT_VIDEO_MODE is set. */ + uint32_t mode_type; + uint32_t width; + uint32_t height; + uint32_t depth; +}; + +/* + * The entire multiboot_header must be contained + * within the first MULTIBOOT_SEARCH bytes of the kernel image. + */ +#define MULTIBOOT_SEARCH 8192 + +/* Magic value identifying the multiboot_header. */ +#define MULTIBOOT_MAGIC 0x1BADB002 + +/* + * Features flags for 'flags'. + * If a boot loader sees a flag in MULTIBOOT_MUSTKNOW set + * and it doesn't understand it, it must fail. + */ +#define MULTIBOOT_MUSTKNOW 0x0000FFFF + +/* currently unsupported flags... this is a kind of version number. */ +#define MULTIBOOT_UNSUPPORTED 0x0000FFF8 + +/* Align all boot modules on i386 page (4KB) boundaries. */ +#define MULTIBOOT_PAGE_ALIGN 0x00000001 + +/* Must pass memory information to OS. */ +#define MULTIBOOT_MEMORY_INFO 0x00000002 + +/* Must pass video information to OS. */ +#define MULTIBOOT_VIDEO_MODE 0x00000004 + +/* This flag indicates the use of the address fields in the header. */ +#define MULTIBOOT_AOUT_KLUDGE 0x00010000 + +#endif /* MBOOT_MB_HEADER_H */ diff --git a/com32/mboot/mb_info.h b/com32/mboot/mb_info.h new file mode 100644 index 0000000..149099f --- /dev/null +++ b/com32/mboot/mb_info.h @@ -0,0 +1,211 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000 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. + */ + +/* + * The structure type "mod_list" is used by the "multiboot_info" structure. + */ + +#ifndef MBOOT_MB_INFO_H +#define MBOOT_MB_INFO_H + +#include + +struct mod_list { + /* the memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */ + uint32_t mod_start; + uint32_t mod_end; + + /* Module command line */ + uint32_t cmdline; + + /* padding to take it to 16 bytes (must be zero) */ + uint32_t pad; +}; + + +/* + * INT-15, AX=E820 style "AddressRangeDescriptor" + * ...with a "size" parameter on the front which is the structure size - 4, + * pointing to the next one, up until the full buffer length of the memory + * map has been reached. + */ + +struct AddrRangeDesc { + uint32_t size; + uint64_t BaseAddr; + uint64_t Length; + uint32_t Type; + /* unspecified optional padding... */ +} __attribute__((packed)); + +/* usable memory "Type", all others are reserved. */ +#define MB_ARD_MEMORY 1 + + +/* Drive Info structure. */ +struct drive_info { + /* The size of this structure. */ + uint32_t size; + + /* The BIOS drive number. */ + uint8_t drive_number; + + /* The access mode (see below). */ + uint8_t drive_mode; + + /* The BIOS geometry. */ + uint16_t drive_cylinders; + uint8_t drive_heads; + uint8_t drive_sectors; + + /* The array of I/O ports used for the drive. */ + uint16_t drive_ports[0]; +}; + +/* Drive Mode. */ +#define MB_DI_CHS_MODE 0 +#define MB_DI_LBA_MODE 1 + + +/* APM BIOS info. */ +struct apm_info { + uint16_t version; + uint16_t cseg; + uint32_t offset; + uint16_t cseg_16; + uint16_t dseg_16; + uint16_t cseg_len; + uint16_t cseg_16_len; + uint16_t dseg_16_len; +}; + + +/* + * MultiBoot Info description + * + * This is the struct passed to the boot image. This is done by placing + * its address in the EAX register. + */ + +struct multiboot_info { + /* MultiBoot info version number */ + uint32_t flags; + + /* Available memory from BIOS */ + uint32_t mem_lower; + uint32_t mem_upper; + + /* "root" partition */ + uint32_t boot_device; + + /* Kernel command line */ + uint32_t cmdline; + + /* Boot-Module list */ + uint32_t mods_count; + uint32_t mods_addr; + + union { + struct { + /* (a.out) Kernel symbol table info */ + uint32_t tabsize; + uint32_t strsize; + uint32_t addr; + uint32_t pad; + } a; + struct { + /* (ELF) Kernel section header table */ + uint32_t num; + uint32_t size; + uint32_t addr; + uint32_t shndx; + } e; + } syms; + + /* Memory Mapping buffer */ + uint32_t mmap_length; + uint32_t mmap_addr; + + /* Drive Info buffer */ + uint32_t drives_length; + uint32_t drives_addr; + + /* ROM configuration table */ + uint32_t config_table; + + /* Boot Loader Name */ + uint32_t boot_loader_name; + + /* APM table */ + uint32_t apm_table; + + /* Video */ + uint32_t vbe_control_info; + uint32_t vbe_mode_info; + uint16_t vbe_mode; + uint16_t vbe_interface_seg; + uint16_t vbe_interface_off; + uint16_t vbe_interface_len; +}; + +/* + * Flags to be set in the 'flags' parameter above + */ + +/* is there basic lower/upper memory information? */ +#define MB_INFO_MEMORY 0x00000001 +/* is there a boot device set? */ +#define MB_INFO_BOOTDEV 0x00000002 +/* is the command-line defined? */ +#define MB_INFO_CMDLINE 0x00000004 +/* are there modules to do something with? */ +#define MB_INFO_MODS 0x00000008 + +/* These next two are mutually exclusive */ + +/* is there a symbol table loaded? */ +#define MB_INFO_AOUT_SYMS 0x00000010 +/* is there an ELF section header table? */ +#define MB_INFO_ELF_SHDR 0x00000020 + +/* is there a full memory map? */ +#define MB_INFO_MEM_MAP 0x00000040 + +/* Is there drive info? */ +#define MB_INFO_DRIVE_INFO 0x00000080 + +/* Is there a config table? */ +#define MB_INFO_CONFIG_TABLE 0x00000100 + +/* Is there a boot loader name? */ +#define MB_INFO_BOOT_LOADER_NAME 0x00000200 + +/* Is there a APM table? */ +#define MB_INFO_APM_TABLE 0x00000400 + +/* Is there video information? */ +#define MB_INFO_VIDEO_INFO 0x00000800 + +/* + * The following value must be present in the EAX register. + */ + +#define MULTIBOOT_VALID 0x2BADB002 + +#endif /* MBOOT_MB_INFO_H */ diff --git a/com32/mboot/mboot.c b/com32/mboot/mboot.c new file mode 100644 index 0000000..7103b3a --- /dev/null +++ b/com32/mboot/mboot.c @@ -0,0 +1,229 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * mboot.c + * + * Module to load a multiboot kernel + */ + +#include "mboot.h" + +struct multiboot_info mbinfo; +struct syslinux_pm_regs regs; +struct my_options opt; + +struct module_data { + void *data; + size_t len; + const char *cmdline; +}; + +static int map_modules(struct module_data *modules, int nmodules) +{ + struct mod_list *mod_list; + addr_t map_list = 0; + size_t list_size = nmodules * sizeof *mod_list; + int i; + + mod_list = malloc(list_size); + if (!mod_list) { + printf("Failed to allocate module list\n"); + return -1; + } + + map_list = map_data(mod_list, list_size, 16, 0); + if (!map_list) { + printf("Cannot map module list\n"); + return -1; + } + + for (i = 0; i < nmodules; i++) { + addr_t mod_map = 0; + addr_t cmd_map = 0; + + dprintf("Module %d cmdline: \"%s\"\n", i, modules[i].cmdline); + + cmd_map = map_string(modules[i].cmdline); + + mod_map = map_data(modules[i].data, modules[i].len, 4096, MAP_HIGH); + if (!mod_map) { + printf("Failed to map module (memory fragmentation issue?)\n"); + return -1; + } + mod_list[i].mod_start = mod_map; + mod_list[i].mod_end = mod_map + modules[i].len; + mod_list[i].cmdline = cmd_map; + mod_list[i].pad = 0; + } + + mbinfo.flags |= MB_INFO_MODS; + mbinfo.mods_count = nmodules; + mbinfo.mods_addr = map_list; + return 0; +} + +static int get_modules(char **argv, struct module_data **mdp) +{ + char **argp, **argx; + struct module_data *mp; + int rv; + int module_count = 1; + int arglen; + const char module_separator[] = "---"; + + for (argp = argv; *argp; argp++) { + if (!strcmp(*argp, module_separator)) + module_count++; + } + + *mdp = mp = malloc(module_count * sizeof(struct module_data)); + if (!mp) { + error("Out of memory!\n"); + return -1; + } + + argp = argv; + while (*argp) { + /* Note: it seems Grub transparently decompresses all compressed files, + not just the primary kernel. */ + printf("Loading %s... ", *argp); + rv = zloadfile(*argp, &mp->data, &mp->len); + + if (rv) { + printf("failed!\n"); + return -1; + } + printf("ok\n"); + + /* + * Note: Grub includes the kernel filename in the command line, so we + * want to match that behavior. + */ + arglen = 0; + for (argx = argp; *argx && strcmp(*argx, module_separator); argx++) + arglen += strlen(*argx)+1; + + if (arglen == 0) { + mp->cmdline = strdup(""); + } else { + char *p; + mp->cmdline = p = malloc(arglen); + for ( ; *argp && strcmp(*argp, module_separator); argp++) { + p = strpcpy(p, *argp); + *p++ = ' '; + } + *--p = '\0'; + } + mp++; + if (*argp) + argp++; /* Advance past module_separator */ + } + + return module_count; +} + +int main(int argc, char *argv[]) +{ + int nmodules; + struct module_data *modules; + bool keeppxe = false; + + openconsole(&dev_null_r, &dev_stdcon_w); + + (void)argc; /* Unused */ + argv++; + + while (*argv) { + if (!strcmp(*argv, "-solaris")) + opt.solaris = true; + else if (!strcmp(*argv, "-aout")) + opt.aout = true; + else + break; + argv++; + } + + if (!*argv) { + error("Usage: mboot.c32 [opts] mboot_file args... [--- module args...]...\n" + "Options:\n" + " -solaris Enable Solaris DHCP information passing\n" + " -aout Use the \"a.out kludge\" if enabled, even for ELF\n" + " This matches the Multiboot spec, but differs from Grub\n"); + return 1; + } + + /* Load the files */ + nmodules = get_modules(argv, &modules); + if (nmodules < 1) { + error("No files found!\n"); + return 1; /* Failure */ + } + + if (init_map()) + return 1; /* Failed to allocate intitial map */ + + /* + * Map the primary image. This should be done before mapping anything + * else, since it will have fixed address requirements. + */ + if (map_image(modules[0].data, modules[0].len)) + return 1; + + /* Map the mbinfo structure */ + regs.ebx = map_data(&mbinfo, sizeof mbinfo, 4, 0); + if (!regs.ebx) { + error("Failed to map Multiboot info structure!\n"); + return 1; + } + + /* Map the primary command line */ + if (modules[0].cmdline) { + mbinfo.cmdline = map_string(modules[0].cmdline); + dprintf("Main cmdline: \"%s\"\n", modules[0].cmdline); + if (mbinfo.cmdline) + mbinfo.flags |= MB_INFO_CMDLINE; + } + + /* Map auxilliary images */ + if (nmodules > 1) { + if (map_modules(modules+1, nmodules-1)) + return 1; + } + + /* Add auxilliary information */ + mboot_make_memmap(); + mboot_apm(); + if (opt.solaris) + mboot_solaris_dhcp_hack(); + + /* Run it */ + mboot_run(keeppxe ? 3 : 0); + error("mboot.c32: boot failed\n"); + return 1; +} diff --git a/com32/mboot/mboot.c32 b/com32/mboot/mboot.c32 new file mode 100755 index 0000000000000000000000000000000000000000..90dacfc5573faedd9042a13024000e76b49c443c GIT binary patch literal 31148 zcmb`Q4}4VBmH6NMN%9B-Gr)iWqYOG~B2g1byBR^7e}pJegNYag6@*042sD`qC@8^~ zD3|B7Zne8@x81F!yX&rQ>vmg+^p6RF3}RLOw2IIwqSYBjY>+PhROa_R?@j*TYP;Rf zk9_XDcmJPz&bjBFd+xc9u2jv-Q$8u`e&Jc=)1Lo_=W?|CQ#}div1g*;p!?R%(LL=T07NxGgTVQsci* z>Yt)DLAUX4f9k8B+89UP^i54~%Abl|j9;J3NA?Kk+sdx48J} zT1mI@_eN=2;uhM!>89Exk4nl+ht@vy@3p zp;p&FO7wd~)rUm~lHQ_jtU7wT-)Yvj!gV+BkcP;U+SrrFvQ_7qpI#2jPZ|YbZ)A%NQlq-NqVdQnpS1R zJVviC^x>LkY-n|LSvx?39pA9zA`lb?Ju{SLL0dmewh&iEjxdYLhSXk{?Bc8k<}>Zi=2 z_~nxa22dzq7$?8dc8Lp8Mr#imhx*m{-vEXn%l&+SI$biKoi5eZjnchwwWMg_%%bkjJ(00C&OZk>N^i7a znraUEtpTd~V9rf9$;fYEIs|UAh8|yk{=lf`2}zkJ_qbG|%fd*_AV$8D&YKGvbGob{ zwdM{~yZ|=3k^<@@msEGFH*DmL8mJ!j*Vf*=WW5aL4WZ#4P?nrPUpZ0t1joMV83K7* z#zuLlUB-g+`_yk$Pf<$M1od?xaC~m0$eiO=yIf`Z)n{(J>D|Z$KQDwJ$G5s8 zW7ZwgG>2xKc1%2Ayz_z48;H86hvKb1L=wVeT}qG@`C0NjHL8og%rf^ypQJ!~&fK}R zOa2k~5*bWNnW7sdzvzJR=``;nPcRRkhaI&o+bF^e`T&Z^%zc22CbMd6lX21&_VJq; z&gSixYxMGsyp}6Cgb#E?<0j)se&e2?Fib1 zyqouUf4tRIl}qVIeWbNtEkaofj}!Uxl)!hqG?N}afrZVgoQ?02tNz=yEEnNSImBa* z;1*(Vil&pQ~xr;gNN>q{6}6#BXRGvcoS@Bxv(Yj>YMgptgC~`2c9WUdBgXp^w{~ z>SNmHXS;E;_Eev)Duiik^t*C>5`RnTCG+`z`ip#jwjH?Fr|F5mc={c`2TCwVDK zC1;p*{?0w&qT?01Ga}-`oE=d8t_t5hj`(?>ZGWA#KZf>Ig=d!0nab(^G}NaFz?OD-yRK**VIvU+9s+K zq*q(~6rMC^Zfb>Z$**OmUZFEA5j(!buV5R2wAKf$kJ@xDgcU4YggrK${|Q)=OIUPr zwD2@aPvtG@nQk>qO*kfK3y!ODK2vkM)V#d&NVu44>GFQWNW!LY-yl=??VjGEeEhi# z3%uk-c7@WMG@Zv;QY4uM>ZCNi7nL9_Q*J?gx>YYS_CWYTOH)v9ee@%>FU(vmDx=2o7{7@TsqK{U z)2PUoj<3M@i0TEDaTo1*9?_|>5F`4IJrnB)b`TB3HH{i;YRj&#BdMJvsjd2Z;_fD3 zS*-evgI;-C@}C*%gw$j`$fHU?$`&1nt_d!X;i%JH<4K4&GNR*^!TF9z+qyNu25C3a z3{mUHwKYzk9}Khvjb`fJ%M8p>@%UWP2DXbXi6rqSz9y_%8O*!)rJQKZC@tm?V9ZQ& zu3MG4)g0Zd(#^RZRpwE1d}ftTmH8_Wwl!2)CGxZtWeLKJlD1G;@)Fn3UTmf{q}WW* zC^6WQHo>G|d!{Eg(~@kS<#gcVVRaz%td}0jJ*ubY)MRy?FIH}MLCspt+p(TH(VGinGYbc6YmQ;HYkLm* zq;{+)HCa6{P+Nv2Bo_5IdDj5A+Imn}Eig4`;nKezrh!)B7cJ=-hzUYou*QU@qiOvq zNFStg3t`Dc;3FuSXtl2Ps#jbMo^dODp;uOpY;lM7wq_VFrPQ09FGi-RlcCpJoh-{| zw3Y5qZ`%bTu8jx$j>zbu7b!3{bTTqpy_NjIbb4uZL(5!|uPDJt8%8QZcFhyh*93Fh z+;DGhPjfIQn>KRP_!Uf{=%WHzPds~Ln3yd`9rwZjIrfYcs-uO+M5{n6 z+t_Wtlr(MJX1^6TZS1q(f=wHD@YY1Ri<>s?v=akO8-LB4G^`CZ{5mSM1z}`F8oKZ~ z7C3NrWzmZ^KtB;ZJ7o9&3)&dM+ZN%>E=h}T{co`M+#vR{*I5ge26l~YE5I9yX)aU7 zPs2G~!bWk|IURb%XYG+FAUcV3FyYs}J>r_JZ3-uDhvzEN2TiCL%C>33rN=_A&zynV( zRO4hSGUh4&Gc-jQh&=;QTIf1jvhmQ)U^CJ)e3h^-ZXWqTUC2htuB+f zOjoOXYKh;R!`$YVeWNewO&475H_QCwN6P@nyw+!goRPmP>bBuv;nCaIF!#b&tz&KZ zV`}ACy6TD{bm{NmO-i zmb&8ze&cHCTMK)O_H^k1pLXlKd5Mcb_PhqVlli1#Y&c#jdNJwqZt{D*o7|(zX6J8J zyCRw3`vYr`y^ljP;UU2PCC`m(5aT@=&V4f$NTW((yxDd%xSUADRrG&t#+HVP_C;w*_*3ckGjnh zI?;CCQ{NJNm-}Poz7BaCFLAS6T2IzIB}@q?vBKDy5;x0zs@9*pg2|=FzYI!LFKBQV z9ZBX!tNPi8={Do2x73Hne#qoF#B5i;SMfpCAD~;&iyLww*$C_3VdL z&j#muNAy-lc}I5`mP_`l9T44;p*|*{m{ZgV=IKi(_QEaCqN6gOGGT}}j{Oi>Y&Y{b z3X;Da0D9S0`{$_LTQp+vOTmyOuUAb}Tdz*7)wG8)w#b80x_To%me$s7(=4 zp-i;w;0zy{-KOqTu(N?a)SE8|evH($b&ehVao zrzFjhI7tK1vRyMqtQ>zYVn5satDb13a~Bw?Ob9o(pSmgLJ!#v2;;Atc&X88rt`EULhsYeN;;ymynP~ zorUbgBof+BHHYigdfQJe3755>x+k39eyS!ps{K?r>}fysKsc-Y)K?>=$-fx{p_D;t ziG!j<4${HG*UKcOYJ#Qe&BU+d75XHPp;DAOlOI5WQsboap$tpLfCge`9@K?~2!>Sq zsb$^{7nRmt*?ww~x8o3N+}d&Nr)tAX+fOxkJIl#ROr+!XQ;WSF1^~35TI%hbN^){l z`>9rM$4;r?Yd^I-d};fs72ZytWEHocig-I-r#Kze_^fKHyq$kSX-^zTr@|jv;uU_@ zf%bNug8qOxz37Nq6D&ddnql<1sa`Dw2bxLzce-GyHLz}{WME*B<<0x(K3?FZ@4`#o zU%41;iQ3-t>11gu>G8f`rFJ^racAj$qB| zEZH*n<{N;10Kzm}z_YRjSe_GDHo1~6?`EqzidoYzImsMX^djw+V5$MS&x||i`<%WD z7@n|EFjUW+@*vfMQ)M?&6D*+F#rx2PxvNBF#P0AB_B#0<}HyjP*V3Z#%+10ISh4 z#Z@_`hlAA*y2-5$^*!iTeMv8!hDXwIB;!{Cg9-;%$#C@W7{o30eBLL!jc&p2s%TZ} z+b7lpS2_BL%_nn#78%Lw8mZt9kD#gfv%t)nfqH?Sa0WAFjnfPVYmN6@k!*&yCe$3P zTssaJDwVxW1nR<~?qs1nX`@p8BG?WnMct_J1+DHrXP~?^^_Z1r)qmD{UHi5LL3pH9 zDzDFZS@Hq_#GOu=(C3_*^ht^@onp7t@nZPmfrbmqqt%Y-P#Ciu6m?AOHVy`i?uPZw z@@SdkhREemYktV?daXY>d8pnXKnyoz!@Krvj6w6ELj$JT`HZcDL8$HObw)3^15>gK zu_)@Jm_NC)^LW^GLvq}W*H3Q-23KF!ok*Jbl+Q^g<==FZ*taB@X=FWVwHv<>X$j@Q z+2Rdu=lU$ESX=uAg>OQV37e!jlCjR$);^rfXLtn+PXLAE+4A*e1%!ePDI5AU){|Mn zPsTwftL>=aBzOU!q_Tr0kx-ma@Q}!6*ZPj&-;)u2)_PdR3?}8&&LfeGU(R64P{>}+ z|0w4p*8)*>Dx3`|svi>je08WMSozS6LOo}s+V0(f@o?fE?BB-LLJWAt!a>*pf~fK-`6t2&*nwedWF8P{+Is%CIYyZVqEMUAHPxDIqcK3*jR>4SN-%KqqlUZ9>E&#kQ zurakO1u4>VGD4M7W2@6R?Ou6zw8EVVesddWP<^Ir1C6csCF|s!p%*tPJy~T%7^KQc zUS+)ltSU)fZoN12o);@+?ju#srLWJ|1MGziL*FRYekq)kGpuHHY(sYR9tXfScf9EB zT)}2`u!#{ADC~1;Nk0?`YEXNAFb|-DeU&3qqHz?j+a^kpwUIDB@ZF;ga9o^~2_75R zTgjfu7D`kK&}z`)fuaNIwGn+0-^*IjaR94xbdTBNcyY{u&OP`4$w~=MM}(~$+mbQe z9b7pw-yNJt-0{kox4h5pa_m0refAB*&& z>7B+00oezL5ufxRE_&AAChsWvtKcWArTAZmYA$m?g1I?aoAkt%iU z;}4(O{(U*SgPpwQmt-4+O;W_DrKN3{)c$c`tyfg#K8IFjWh6(+(d1jH^%KjAy4$bf zl&(DadUTGCJx zq?7YU_SL&?n|H?$zGkSG)obkQeFMgj*N;8ZR~h`0Qzg~w$;;cX$&QR8UNDNq`*hb7 zdW+wQBDEnC1Q%2dC0-MwkLh#5~rtjD}8Z6pm0Gm?6!?cI!s z=sA>aTSILN>G@6OD9b{+PAsHb2F;_H-p#u)kII&ELq8kldmLMQ=vr`5ZVGb*edU3E zu^Ni4KbDcR!Dm}TGkm;R_RtIuZ}JZ!@FvC(6&J(k#*@M{aj}eU{Djx%X;@Q#r0pCm ztL$H-5*!@Z@+Wd_D@zSKFoaPx#-cuQu@zNHC*DqW@fx$Dbe#8*|KQb(XeNvO^}2bx z$4F`3M<12aGI(v}mnq93z@Gg6iKjz*SB}Hd*0XXH)^#}h+)9k zn;OXJw)V$%t(I)8a6Q-udXkxH--sRIOFG$ZN7uXc2558=OtqBtklAjt=4LSHJ{ra@)Nok+iGl%vuzO= zEW3oZ@$)`pW4x8QMQ`KPBxWzAvCZgame^~~Nn0kw{*W7gocXJ^$^mP53XGa#8N0fR zFpphj86~8na8~?na0NyYtI;;C4x^3EapH-+Mz51Cl|C==C1&I>p4U+}nu80PSb8$! zKVui9`owtueZT|p)f&n^&&tb)TgR`p_c#zawf507(^}4KR4*?}c@e4|h};eHol~?Y zTHdwZVOD-qQ)LdLC!^drtw)kf)vw4}TP#wKH~J=)pwsas07-t*W!YeC#0p;bM%QGgl74fAZjKJ+tQ>J|=v3>t&>OAU zsvkqKaXJ-wcMDA;V4y{BQyK@Hr(n|a1X)Vql3(uPd`64^XHs~Lf};eV@hxD2)~B$? za?-picc-oYYC^O{r%wpkCnb$-e&q4s_&kiH^F?n*KW#>9vgKggtzyQ@DRZ(pF60lk zd2L|BS<%(H+MbM~V(FyQW~XyFsOz}sB%)Q1dV!b-Aw)P(F9!;H6hiQF|HD`Hwm+H(5&ddg^#{)WttZmX3 z-Vf$^*6Jn*1Vi?kx(90*M{9D#P&oooik$r_Yj)3J4&l(k)PCa-hj8`5ylAVZDdutr zay?C|!r2t7$o^A`U-zHz*su=mFdy1yb`_bj1gV`}7XViL*T>WAPo${HWb3^PUx7%z zcXNe1J~3gL3!KSuBpx?NIQV}L7#E#jP=Y@Gcy%Hhgo+NLdQ`8g!EKIz zjD?QLTka0+ZykpQFXqiC{hBxF##PS9dvbUu_HU264(lkty7^Ud9Fa-pZElgzNHIB$sd^w`-F`>T56xUqN z2ysW?Xwb5~@{8@YO2_Ihzt!-JWtUVjNccdsw%@$XW47qVbVnoy2@`dX3~h~!c437D z53Rbo!vn|6>K_8)b1kfDuMI{W4U8KAqwe#-fR_QIL0ET)zu0OP?ExMa5|WiA#mg-B z@g7%LAF=DiuFf6dvc?pH*dx&03EI z#z_l|i(*!30hsZhoj2n7k;u}b?qko~W*w&w=)RV2&b0ng`>B@j__de_r-mj zHlM8Wt6Zq>X*~8{A_n45fY{e;2FC|27#BUVRKraVDW!CX(^4K_>XuQ0ClS5Vr5y^mtrJ6XLNiI8kY8n?}ItM2CXz&@$36{-NDLRla z({0k7AKx{nazGor8swCxOgDNwc2DntYELo%9g#`rs4=lJYi%-Tp`4(m*(KNN%n_mM zK1%CK&Td!GRIJQez@oQ$ijF7Gx4H!^Ycs;BE!sL}xYWyHUK31hXGfygC{r1N-W z2IL7?E0cO%_1i8Vl<`A}g8W`q{L&^m&TK-lm|?A1LCs!d?(Vmyhp}zGr6qxN^z}+e zbD{QpfXBdM^cBi=iDG*^qncz#B+I)wqm;LeLPx~PfP55ttaXqB-=nAni>u@BKu+Py zkj*T`7ilO$>;*ZT>-YgkW=~MBiElyC5k@bHmzKOlMi?(TWD&(9eE%`Y_~~Ne6#5_g zq5P_squ-Vpp!QST6W<~pMN;&7PCrT{MW=C+Znw_B|^UszX&n{>Wb*h zuNVir0obRuS`FM!1K`+Al82rw=SX#BavtWH5(?zSbs0i)u-@_e{OzA@BW}EHLksJn z>(b8b;qa=HDmisNoGTWT{gzK_XkTPOHeJQ~Jc8P{py ze{swj+=JVC^XHtGU%^2C%JXu|E+K2d1lyPqea^CGe9Kx3{)a&$L-mVEgN3=;E^f9L zuxKD(hc~g_YNFj!)@%;s~Bf98ysw&T{3Mv;Kf}$`Zhf1DrV3pecg>Gn{OxAR$EjFTna&W*fSrNu*1Y#Xr(3kG9#0KpQg2$bN{ z9;?VBM>AFjwMbU1B3MY8S&{er)90q$5mI#lwY3ZDhnN-`R9$Z8@%u-ncX%h>OpZZa zKgrIgTsO{KvDRh0=Z#jKG)`u<`DrCp7mQWpO8XWZ$%(2ZH&zZ$33+i+PxLXEE|iFr zrJWcb7bIU|&Suvb4w$tCz>^`J>^AFi&*}_GkzsW> zkB#i$e$#IY!4WX3xq?x6JI>INS?X5qAY9_@`dGSrs5r~}$YC;!Yt9QVk7eS?-MrU~L`40T3>X$q`*LwOI(aR)mr`nVojE?NxIZm{*u{7vNkH+%SI>-rIJ? zUc2If&r~d%?g`p8_fRw66Erb-&*G+odfByHSIhM-Y&j`R{*lS%oqi_WZQ?sY&Yk{P zrnpcJV#(T(}-?y7Ghne$SH;l%g*iLdvi0pf zgwMET5>CwxSPqsGNMI?^Q*(n>)x@JzDmwD~XlWi-+hhOS^!(vW-gNcmv44&}A=AgK zsLbEhbJEE=qDMQ{Su?7v(yXYqQU_D2&5Ffmrz~clF%DQPVYv68*;=6fsCrmsMdU0g z5N%48=MF>U*&h4bnVk)}GNQ;u!$wq^9?{JRqylT0f=6rG%FH}>s6$l|NY77ZCIjSZ z%G8^EcuI?xy1BYQy>jABSHyGdhv`yyRafNY_lRT+?ZoKBcwOw5Ub9MB*Lifd#k-~g zet3e^@3V6llB&ovD+0-&49!)MM|65cKqP`C9?Xhh(H;_7M{vA|v?VrlK_PAb%4PE} zmH|sI`KFMg!QzGuyc*N9y`AfLL7mxyrT;@(g&H1pIQ|Z=TZC1oSNJ1M?B8J3pCcAv z#!)es2#+lye3G-xx&Wu+p0-J5MM1up*Qp;3k7AuuyCDg{kDp4itF8cT??cJ82;~7< zZri}6Se%m?X~P|&#Zv7o*w=mV0l9EvDVUof#P}($%xUodjaOrp2lo6Yc`L!&%2?&b zTKu>~r;m(e##|n%gSb+&RQ$|W!51jpKJ{d{{{`n|1@_uQ5BUKGFxiv5w$+ zT9$vt^jz@S)7b)CVMCjIi#fN3LHz`eV2*Fs0A<*_$*}>n?LBB+ z4G433?$Jf#^0w`1KjaM0Uiax_)c;6Dnuxz;o1rTFeI3Ey(cxsCzvpR2pS0pQ+dnUh zUHnIOF){bBh!-76{E%m?f~_!mbA|2*pU-K;K^;?$oJ#19tUd(u< zEU961h{SGcSqB!zNu3-p8!O8(C1*1^JxAPjX16}QBDZb4(H!)+1?lJ>|00FHL7_D8 z%@qVuL_c+# zPHfi>4nn>~fclj6j#T;&7pORA*MiNSBf2`g{z(g?pGD~H%oy+a0VB2c}>`!Gmk{fh5p(`7+|O>sCRd1**)9)JW- z6z?YI0CS16*l_OJZGrN?8~lQSE(L(0!P9~}lApXU-SO{O4*n7f;A(Ym$zRd+A8eKx z>NMtJabaHhj`z94h~5&Goi(8tZmcnEQB&IAb7H09&WT(L3djG&uAWaB2YxU4t4oa4 zCCHINV2z2R7VDJn_%=OUmmb79WE7?BiRlTc$AYp%%tHCe#EX1Q%X`PS1%|g@b$0v4 zFRb!(@aib(AZwrl-(Uxu$e@F;5U2PW#L{zC2gkplgFjqkQF~wjbRgyFV42l{&bweU za%uAB0s50+k>M}UA0)8>;daw@riMLV_sY~bgkT*)cY5aDVS^zvi;Eu?s1{v*!=GBMw&!AgFdR7e~y)pfFZ@w8#v4UnLSB z&sOuXU^^vLwSXR0g=DRfV+X`L!c@&JPD^E(t%)WQQ#8+F}vB~`xv$UB8Hu9yG1TK*#D^b?l>F6_gzNH5uVV( z(X5kON-0-lRQ;62 ztXYDs(N~{O$(2~b=bzs;FbB@zeqiEFh!Cq-!05m*D~Oi7F%8KHa{eMD(B8Yb<13Ft zLg_V&s`>}>lPniEa+K?C<7o*)hDKzN_ev1^_B;b>&!VY-43~1Ew0ik_p ziOZQ9W?dz#LW4QG#&QMR`<^;_ExL|aS?5oQ({O|7epqeoIqXA6z#{|y`;&-4Pg{N( z8wupVpPT&2pK-<1+ji+7`nm=xSn@A(U~#Gv@>{A;h~``NknY=i1fl0a?fNO;g2C1$ zs=pM}B*GAlW_^(dlLK%p9BB3n6slwvOy!H-;uK}JW#>@y$Wv)vNh2-}n4aVd9Vty`bs-y4`6n^&dx~C6BR1;3 zl5_0^ZQ`CO+#=(*g-Y@=vqBsTDwYV=uZtXoC(*72Ddz0?W^04vD7>;DwA1_WPuWUP zzfz$%R|u!VZQ5LOcEfOH6+X2MTnPsA8CGRoLET&?$iGX(hcr};_>&cSj&xVo5UsCU zl5VeAnBTtT@xA}qmaU*}khUgJ-uk?Wkq~Xm0a;`a3y#N3iyqGZU?DY{ ztTn32T2J5@RaZ=b4q(BI_tr?@yTQZQSD9@-7Vvh`1$(CKX2J}UeTZDGTqB!}+2T*^ zNgO%`hFU8r-6f@G*Q9s1sz)YV0o4nba8KH~%()mB{}R3?^pD3WFH=zWZu*E>(o9iL zsITqPG-KS&0`X_%QF5a?*@0C49_@HHbwAzj?G)Q2!)$i+r1$%3w?MJPyE)w*vyX0; zOcDNU-WzB8>4RM?$lZ?sn@EHvcwrc9TPf5GCk@Glmr)k~>}2?^)%p=A(3~G;!?S58 zQyyd?5bc&LN(A)C$e3IQ(-Qyq5w?`m-c4Jd4unaA3|e9XX-}69>h|9kqEDe?+^bYY zY0PB_$knBo4=+ttrBPNq0N5AJC0hnr6jG=59;}`ccc5m@F15jnx%`5^)@rD+!kk@u z79|C5K1+12Ky;$Pf1R>}wCt9MP0Q1k937zIQ|R&*uD%GQcyIRo;eaYt)@}774Ql!1^Mw`W+g##PksH zHa%&L`S@0$iY-%rAyhG0ZwBG3n88}C>LAc~z&1ZaAo=FOpXKS#BhZXN0x@!#s;-LQ z)Q(^~Z=s4AgUkCKfhEQCkGbSj9QiLL*pr_x>!Cf#r`p26te6qn)pmgx46*R27&qfG z^0%6+1NpDJRu`b`gto36#Z@_RgI|>&BSG~{w|i{MN-IG5ww{_lyQFB(2KPB2WYv22 zg!0ht$Wg~DFwiP{JJS0{j4c-q0V9W^vb90s#WNjQvMTMEZ40x+6O=brb8b+Oco*o? zj6IK9e{RN6%!4WpXo8=qHfTa4#|?hR>>M^ZQw_QFP~i`clCJZ2#+xRh`^a@8iTno` z6CE81$0mS9k;0LSRbt)SmJ0tiyXT%m+|2HY=Qy^?L%+z<@!_j%3{Bx6LPu-|{m{T( zV~{HCfPz>Qh!VfRp$_Uc!oTrcUezUv!qINoW)fGFo zN=s@^!H6^VJr{T60{C-1xmqMA4XqgO2T>$PBoI$ZtlRbhzr$^PqLy-mgpE|aiod`r z9FZVquQO_EtfAh>5e$W+=@XljdrniTvT{{<=yc16wD=r#t+h2rPNZcE6t(Sr{w8Ba zEhm#$*nEjA-4^rvBkv_H;nk9jQjRh!@OG&(&iEq7qBX$>EO{Np^>njC?jw5FpA=Af zoS`=&2~1y;I*)ie{y>sEO z3(ADe3GK#0vKxt0kE6L5sp}B~&kAP6N?J_ypfEg3a{b=L+7dZ)oWeiQRrt3JW zKare-pQ7K}aS~u@vf8`3%sT(@Zk|yFnZv7C^y|HkB$+CRqPz>k!9pq&cst&(jh!4s zh=Wc&4F@AInbL-}0Wt z<5rb}4k(Emu~!QZqhU7av`)h)F> z;2kLpL{JUtb5-ZzrdTQ5)~A({I%|EkBRc!@h!f5^izM`%hkkzMB z;d_xN%E1v*HckK>TMBcn+a@wn}9hdTIu?jXs%i2 ziLOX-#hc`?iAoU>zd*4xSh|*qZURR5XN_Mr88og5Rol_dU zQ)klUIK7*EC}@Y^aA`1yqlWcw>^*4Tkt#7_Lc$V=QwcaTOt)d__3L|Cx4oNo>}?K~ zb`D64_8^b4W1AUhApjJLg@MGw8&dZh)HJ?>#(F$eWD$dHlCq{H&~ zEY85x(Ffp`X)EUpd-Q7TOs(=6Egtx9ZqbB#}xK?McWbI`;5|tqs&E zbXoPHZm|Z`P9-^XxCNj0t?c;KO5ZHb|*E#!pMg% zgw0fPlTS#yX;`A4fhDn=NwcAH+?n;26VC8V@l0)Sk3(lQ5-v;>#wq8@w^aOjc!86> z6G!1yK4(zs=mSJC?TJCHDq8EP4_r<@U0lS-bmX+U4Kf) ze@jZV)hS(9I1>i3<2|L3^DGLdsSCj!yXYRqdN}a|F=H^%QBh^f0{uAe5<LjxVgp zym>g$i`v%yb|5UrqyCsfcBaXxX?Bn;DPa0zn$?ml#<|cpr!^bVAM`$4MO}5WC+;F6 zCx2_tLHIGh2P>e|S1zvb;%7qxxkh&eTM=BVk!hBRGrwO|_=XRL7VupzRM!<7+v;se zW_@!|m%9d@_%(a!Y4EOr+_}J$MZId@WtsDHIsN6^Vj>H>YVo9tw_#LMIed#mteyp6DGO zE;HVshZ1eM`3ZGcT(0A;%y`RCxn)vL?K9FY@c5=+>B)_kzRGq-{HplQ+7NuNVfvwD z`Ywq5jVz`~4m{~c1o+ZQs=&>w=-~3t9^1pz#a7+-O zWT8{QWE%Io%Jpb%3RPc9S~vO48$~xOR6AU=bP?*snAaW=Yh$R-Yi#BThXq)2aI;6~ z6t9$v;>BprU$XQcSwR9OPNAZ_2V8U9`TJdt{C#}uVl;qCX8vuud9x-YnW_CmE{7Mk z7QtwR`N!ldH>}(NM)fxHz`9BrZ~}VOpR)7-w(Q)P!GkvskKR~l|H=u`nJJX1l9mxL z+YCpfFzKVgOL6K2fip@Yd-2}2zKeKN?FR%=YU0|j89i6P6XU>bl-(h#mU*oQ54O&G zWb!dkfvgBP>qGsmdHH?3Ron2Dumshst@$5Osa{wz4_+=18YN5G&Yfy&tFjAdm|mv0 zW{dJieQ%)8|tZU)uf_eHaB@5A3QSU3Qu0hv2rxmfLPDsz!*HC-v^FNZW z266}2MhVG_2auB72`By{_$2Va774$s*mB}=lb|PLNIhRYiJjPIf0NrII$)LD~5&a=k%>s$1>kyFVw*YjqXF&;;Ia;Vjw%wZye{C#6Q2 zlhk*E?KFvzvyE{^9}7N4jA05V+Bf~cazT9OjxQ9Q3k) z4no_xI#Bv2(oHznkjS=>HR??_8o+;mar#P5E|#`U1M!8hOAdH%W}2_z4z{^;TTXC#k#KbJRV$ z;^O23K6RbH_Z#LU^Deh}k8ZYj%m;j_yZn3y45w5y-}uu%0jpeu4C~^h&Uc{xU9Ozp zDL$*R$*JTcVoT)8-9CU2AJ$cOn4{k1f*GSPeM)Z6#V_TgR7^RAnVOX#$8XjRxt#Qe zD}A-m%Ak*HdKV0QA3(k;QtaSX6T68fHbm)rRm>udZxtpBms*#oth@J3-e0mCK(Cbh zV<_yZHm6Bd=MFk12dIN1uCc9TUa$3?s}-sb-D2=-fp4Y17MS%J;O3`+t2V20%Z(pUzeZjh~SLztJ0G2_Yvelgoi7+@-)VCrK(6rXMmWZn6{b<_nNb^iRi0N)Au0l=}M z+qW|yXWnd_M-LYGxF;t-A+G?S~&xOjmVZ@Sg6w7TddmJ_Bx#A;f>&miK zScmBLZvI%k+4G?K}6+(I%hy$$5@9=x1q@)`75h(@c#667Qy)9Z|pB zr^@BkiJ-~lJ;1w*ciHF%d3WJ%e{~oG;)#llKDN3wh7tT{ip0yz9Id^IpRH z2;NJ0#|(CJHt#cdFXeqCPg&SXNfY5!Nt!%$(ke+4U9j4_>Gn|$zP5J#=%{BT1-#K2 zV;udg;kkqWAxOw06cA*4Sxk^Meg>hGP)VqE>~rjh7MB>?eK;%l1RJNR-_=Myx6^ZG&G z2tHs@Gq6Wt9^j#R;DyOBnPs^3ZhCkPli0Gp_U}D7qL=IEqaz8v2eG4Qk9=@^cf4pH z_PMxqE&W5~jxJ%xQnv1tSV<%M@vrjyQc~1TIgr?4A4u>RIFL{~tdj{mAUUY;|8mK9 zxB!;>%Ozj)4Rp`xt3J)sX|*x(f7QwpT>jCxO{t+d8fV;*pU>ip-@<2|E0_tfjHeMj z@=xZMakUjg$~wvGl3vntE`jrvu&0qE_hVBQXueGS~fnYlE|XF5;p#;TXAc{>`B zLfm@x2RAs2c6c{+4?$i|Q#aximwDVBbVYuzJ~fMjiKnQpc21k%-Sj43M@abAy)Bz^ zPE~QuGTog^{?4aObAMAnnpWkW=KkIi{5Pk8UvT5y^gHV)bD+vD@M`+MX;txCxSBRs zpXRr#_|sMXaxb30z8BwL-;4jR?h6`?mH3Hd;ELcEH(Q_f`CXHO;`1Cwdp zJJ#{vJ9^YJ>BR12t~KAxT*TnGqFp_2Tl0tsR-tl-apQi|9W>&yXTd0-K22=2*>bM? zi};cjgfIU^_)-@v|D(;9Kloz4lq&G0RGI4$T1pl8(yH?Ld^x-q-(TO0|F7>Q=dbT2 z_s{hb<*r!Y&~zR)pv<#-`U2(=Y{8h+OEab*4aNj%1B_WeoH5y;%2Lip9%dLZH;kF} ze`xqazAZ7xm9*xF*<5)ZpN|sh?cG#A*?kr>!I^)MulLFqUUHO3B1exr(oET7yO;JH zV!t-V*cPx?nl8CKhrOul=71(o>tY&5`fk2*A{PfyOec2p{a5dgcd8wUYo6v+zEi-* zXXQ?IQfJfV;&7cC4o2JTzFIMUo0XkOYH)+!b*TShf$WO(vW ztzkKNHe7+GmW6lU*SIXaFuZj6vcS^T)=1-5^0oV<|H~ImyYi}l1&Ix8wsv#t!n+%< z3Jhik<}F_lZoS=3xU-271s1m4Z4GeVWtUwx07g3Pb_Sk(;|l4i^{VVZU;tr(z|3U} z@1kyb-_2Et1k0~aj3T$1i>jx4^r@q)n8 zra+_B+TwyhV_W00K-2P;z|5K|>JI&@U$V3nJcSo6X>7F`8A5vNipE6+fxBpN@zSQI z#+Fu4wEVuntd_`K+1d@u12Ra`mPeK?h9V5|^Qf|P+0yXRh0ST=3q>e9j64@T5DDCM zf4H#~lrC$HtXQ$UCEU1p80nju7v9|(Xl)E%6}afaRoU8FM&NKbwCgQyBEuqDU|~2= zxay+9l2w7cW`=6N=R?LoWZA+t*hMQY z945R{TU~imVDZB6!q3fkps{6npr!F{$obj)*85?R=0Mo){%i(J=e0I25=Izab5Zlc zrS}DFaxPrFxTUcb6s%ad=xo*(!0a-g2brC7S1Xvkc3IoP=B2~2y7WGaeU~*Zg>fvd zNDn-)3?>=QM>c`8wY+7K=H`4&`_6?v?FgX?{q^4nM+kX@Go(qla@f1X&;Lx^pN?k_ zOP|5}br<`z`Gorj4-vjjc#Uv?@B!g8VdNz~?TM*A?VkwG61oW62!%ygPAmS>m;a_D zR90S5IkO58G%Ri5U5gesHc8t1d?8joIlJa^TlbVS`S<>xxHr%9;IQ=f|D;=K>qA3j z-~W@xs{gHFX;Q!F@+&Vdvf6Q4bt+5w{ux33EnT)~c}oi<9zZZNZPC42WZAvTmakkE zShW1U6+&K-IrlY&mn>gQ4Vzjkmo8hpd}W|@=>v^}#cl2k3{DCyov^57QQ*F%t=2>y zY~U^=eTbN#U4in|>@RC+7yjN!!HspzMhZ75l#@6-{8{@6aL{wpLWAd@MxV+%e>jA%_IElT%UHqEk5nXgx?eP zH2AbH--_x-c!uE9#%OYtLD#bQEMX@98%c&Xk`0qbb7`Zvj5Qi=68|1whC@4tGp=zN zW3+QK9KJCb8QR1QhkJZRnRZ@=qtwU$l$4X6^8e}ON$|4)v$<9wro%zk~W*8qpUMg2bQZ zDdjr|mBi!Jm-glpa*2OS^JmrZzJ@S^_&&yiZ?Mf?Nm z$1r{)P5-YFN9v8(O8WxuLc$c{Kj4Wp)z=Zqh#w+f;JKB6&*F%0r0IV@@euJnY5FfE z&H^*yCp-mSMYxvuN7NU3-b@&;`E;klrhkO^m&o6lrhg;xi-`X_Pr+9wVJ7jz)ED~S zPB@SFchdBKkoeWa_owN9AMwkGZ{aEY6eV0w{5182{=zfk7=Le?{;kBJ!-&2#{RO8N z68{&TQr;j`5Py&QLjQRL@I2!0()3?Vd^+*h()7QVID9<boMVSjI|Ia*~gog>`#Q#8jp~r26NyNXI zrvC%PuOj~2H2v=(ekt*v@f3V+AY4cMzo{?u|DyT-a+>~4l)0Gl|G`u6{Rp9o_z~(0 z{qG>0PyBmn`aeYc8sfiC(|;N9%ZdMz=V-#$2saS_^e@c+UfLIU7ZRpWej`r@VI852 z_&ekaJhu`i5`W?|^M7xe{!7WvC;z{A3cQMNE%9U27kYlt{C_D;|3=DOMEPfU3cflC zGl?guFZ92ia31l0NYnp8;#U(tkf#5A#4jWMbDpCJQNs1aKc>FW|BL2-EKUDKl(~@d z|H@O!8-xnt?^9psKaX%O@xM>ge>L&x#9vR-|6bx#iErjPl3)^M6F)(H8P6BZ|5wxW zzne0bQ2xg}rN2iBvxxtZ`hw4a`TtL8`VY+ix6<@qPMIqx{{l}h;p>DOiSr*dvj1+D zJ+`Ij1}c`XxW8rT-AlrOqEP5dmt9#{I4yAf!j^jjWs95HOrUS1|HfGQ%~)+5e}4YP z^EZLNbNI{QZz6w__&b-sT>j4EuarLrx{nJzD8tf=M$yVdZHiW;m1zsLby|<+aIAAU zoDP@6?Z|ZK4v)j<$Z-T5g^p6k0*!wI{SQ0^jsaN4qHzg4127H1C9wIl8jTs7?mLs- zb@Z}qbh1(CVPmLs4vx(tZcF@?;vb<$5)rwdu ztew7;opMvt(nU)#SEP5=+Fhi8{DA_pufSp;wgk4kvb_L^4V)o-hag|<{%4-g5q?S7 vLHHdZL6E%q0C3Mp!)-(V|LK?im^Ix}2NtH`LDSl`g3El`xA +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mb_header.h" +#include "mb_info.h" + +#define DEBUG 0 +#if DEBUG +# define dprintf printf +#else +# define dprintf(f, ...) ((void)0) +#endif + +static inline void error(const char *msg) +{ + fputs(msg, stderr); +} + +/* mboot.c */ +extern struct multiboot_info mbinfo; +extern struct syslinux_pm_regs regs; +extern struct my_options { + bool solaris; + bool aout; +} opt; + +/* map.c */ +#define MAP_HIGH 1 +#define MAP_NOPAD 2 +addr_t map_data(const void *data, size_t len, size_t align, int flags); +addr_t map_string(const char *string); +int map_image(void *ptr, size_t len); +void mboot_run(int bootflags); +int init_map(void); + +/* mem.c */ +void mboot_make_memmap(void); + +/* apm.c */ +void mboot_apm(void); + +/* solaris.c */ +void mboot_solaris_dhcp_hack(void); + +#endif /* MBOOT_H */ diff --git a/com32/mboot/mem.c b/com32/mboot/mem.c new file mode 100644 index 0000000..07ccb62 --- /dev/null +++ b/com32/mboot/mem.c @@ -0,0 +1,206 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * mem.c + * + * Obtain a memory map for a Multiboot OS + * + * This differs from the libcom32 memory map functions in that it doesn't + * attempt to filter out memory regions... + */ + +#include "mboot.h" +#include + +struct e820_entry { + uint64_t start; + uint64_t len; + uint32_t type; +}; + +#define RANGE_ALLOC_BLOCK 128 + +static int mboot_scan_memory(struct AddrRangeDesc **ardp, uint32_t *dosmem) +{ + com32sys_t ireg, oreg; + struct e820_entry *e820buf = __com32.cs_bounce; + struct AddrRangeDesc *ard; + size_t ard_count, ard_space; + + /* Use INT 12h to get DOS memory */ + __intcall(0x12, &__com32_zero_regs, &oreg); + *dosmem = oreg.eax.w[0] << 10; + if (*dosmem < 32*1024 || *dosmem > 640*1024) { + /* INT 12h reports nonsense... now what? */ + uint16_t ebda_seg = *(uint16_t *)0x40e; + if (ebda_seg >= 0x8000 && ebda_seg < 0xa000) + *dosmem = ebda_seg << 4; + else + *dosmem = 640*1024; /* Hope for the best... */ + } + + /* Allocate initial space */ + *ardp = ard = malloc(RANGE_ALLOC_BLOCK * sizeof *ard); + if (!ard) + return 0; + + ard_count = 0; + ard_space = RANGE_ALLOC_BLOCK; + + /* First try INT 15h AX=E820h */ + memset(&ireg, 0, sizeof ireg); + ireg.eax.l = 0xe820; + ireg.edx.l = 0x534d4150; + /* ireg.ebx.l = 0; */ + ireg.ecx.l = sizeof(*e820buf); + ireg.es = SEG(e820buf); + ireg.edi.w[0] = OFFS(e820buf); + memset(e820buf, 0, sizeof *e820buf); + + do { + __intcall(0x15, &ireg, &oreg); + + if ((oreg.eflags.l & EFLAGS_CF) || + (oreg.eax.l != 0x534d4150) || + (oreg.ecx.l < 20)) + break; + + if (ard_count >= ard_space) { + ard_space += RANGE_ALLOC_BLOCK; + *ardp = ard = realloc(ard, ard_space*sizeof *ard); + if (!ard) + return ard_count; + } + + ard[ard_count].size = 20; + ard[ard_count].BaseAddr = e820buf->start; + ard[ard_count].Length = e820buf->len; + ard[ard_count].Type = e820buf->type; + ard_count++; + + ireg.ebx.l = oreg.ebx.l; + } while (oreg.ebx.l); + + if (ard_count) + return ard_count; + + ard[0].size = 20; + ard[0].BaseAddr = 0; + ard[0].Length = *dosmem << 10; + ard[0].Type = 1; + + /* Next try INT 15h AX=E801h */ + ireg.eax.w[0] = 0xe801; + __intcall(0x15, &ireg, &oreg); + + if (!(oreg.eflags.l & EFLAGS_CF) && oreg.ecx.w[0]) { + ard[1].size = 20; + ard[1].BaseAddr = 1 << 20; + ard[1].Length = oreg.ecx.w[0] << 10; + ard[1].Type = 1; + + if (oreg.edx.w[0]) { + ard[2].size = 20; + ard[2].BaseAddr = 16 << 20; + ard[2].Length = oreg.edx.w[0] << 16; + ard[2].Type = 1; + return 3; + } else { + return 2; + } + } + + /* Finally try INT 15h AH=88h */ + ireg.eax.w[0] = 0x8800; + if (!(oreg.eflags.l & EFLAGS_CF) && oreg.eax.w[0]) { + ard[1].size = 20; + ard[1].BaseAddr = 1 << 20; + ard[1].Length = oreg.ecx.w[0] << 10; + ard[1].Type = 1; + return 2; + } + + return 1; /* ... problematic ... */ +} + +void mboot_make_memmap(void) +{ + int i, nmap; + struct AddrRangeDesc *ard; + uint32_t lowmem, highmem; + uint32_t highrsvd; + + /* Always report DOS memory as "lowmem", this may be overly conservative + (e.g. if we're dropping PXE), but it should be *safe*... */ + + nmap = mboot_scan_memory(&ard, &lowmem); + + highmem = 0x100000; + highrsvd = 0xfff00000; + + again: + for (i = 0; i < nmap; i++) { + uint64_t start, end; + + start = ard[i].BaseAddr; + end = start + ard[i].Length; + + if (end < start) + end = ~0ULL; + + if (start & 0xffffffff00000000ULL) + continue; /* Not interested in 64-bit memory */ + + if (start < highmem) + start = highmem; + + if (end <= start) + continue; + + if (ard[i].Type == 1 && start == highmem) { + highmem = end; + goto again; + } else if (ard[i].Type != 1 && start < highrsvd) + highrsvd = start; + } + + if (highmem > highrsvd) + highmem = highrsvd; + + mbinfo.mem_lower = lowmem >> 10; + mbinfo.mem_upper = (highmem - 0x100000) >> 10; + mbinfo.flags |= MB_INFO_MEMORY; + + /* The spec says this address should be +4, but Grub disagrees */ + mbinfo.mmap_addr = map_data(ard, nmap*sizeof *ard, 4, false); + if (mbinfo.mmap_addr) { + mbinfo.mmap_length = nmap*sizeof *ard; + mbinfo.flags |= MB_INFO_MEM_MAP; + } +} diff --git a/com32/mboot/solaris.c b/com32/mboot/solaris.c new file mode 100644 index 0000000..1baf9c5 --- /dev/null +++ b/com32/mboot/solaris.c @@ -0,0 +1,52 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * solaris.c + * + * Solaris DHCP hack + * + * Solaris uses a nonstandard hack to pass DHCP information from a netboot. + */ + +#include "mboot.h" +#include + +void mboot_solaris_dhcp_hack(void) +{ + void *dhcpdata; + size_t dhcplen; + + if (!pxe_get_cached_info(PXENV_PACKET_TYPE_DHCP_ACK, &dhcpdata, &dhcplen)) { + mbinfo.drives_addr = map_data(dhcpdata, dhcplen, 4, 0); + if (mbinfo.drives_addr) { + mbinfo.drives_length = dhcplen; + mbinfo.boot_device = 0x20ffffff; + mbinfo.flags = (mbinfo.flags & ~MB_INFO_DRIVE_INFO) | MB_INFO_BOOTDEV; + } + } +} diff --git a/com32/menu/Makefile b/com32/menu/Makefile new file mode 100644 index 0000000..2a03272 --- /dev/null +++ b/com32/menu/Makefile @@ -0,0 +1,49 @@ +## ----------------------------------------------------------------------- +## +## Copyright 2001-2008 H. Peter Anvin - 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, Inc., 51 Franklin St, Fifth Floor, +## Boston MA 02110-1301, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +## +## Simple menu system +## + +topdir = ../.. +include ../MCONFIG + +LIBS = ../libutil/libutil_com.a ../lib/libcom32.a $(LIBGCC) +LNXLIBS = ../libutil/libutil_lnx.a + +MODULES = menu.c32 vesamenu.c32 +TESTFILES = + +COMMONOBJS = menumain.o readconfig.o passwd.o drain.o printmsg.o colors.o \ + background.o refstr.o execute.o + +all: $(MODULES) $(TESTFILES) + +menu.elf : menu.o $(COMMONOBJS) $(LIBS) $(C_LIBS) + $(LD) $(LDFLAGS) -o $@ $^ + +vesamenu.elf : vesamenu.o $(COMMONOBJS) $(LIBS) $(C_LIBS) + $(LD) $(LDFLAGS) -o $@ $^ + +tidy dist: + rm -f *.o *.lo *.a *.lst *.elf .*.d *.tmp + +clean: tidy + rm -f *.lnx + +spotless: clean + rm -f *.lss *.c32 *.com + rm -f *~ \#* + +install: + +-include .*.d diff --git a/com32/menu/background.c b/com32/menu/background.c new file mode 100644 index 0000000..fa03441 --- /dev/null +++ b/com32/menu/background.c @@ -0,0 +1,26 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008 H. Peter Anvin - 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, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include "menu.h" + +const char *current_background = NULL; + +void set_background(const char *new_background) +{ + if (!current_background || !new_background || + strcmp(current_background, new_background)) { + draw_background(new_background); + current_background = new_background; + } +} diff --git a/com32/menu/colors.c b/com32/menu/colors.c new file mode 100644 index 0000000..cfd91f1 --- /dev/null +++ b/com32/menu/colors.c @@ -0,0 +1,183 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - 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, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include +#include +#include "menu.h" + +/* + * The color/attribute indexes (\1#X, \2#XX, \3#XXX) are as follows + * + * 00 - screen Rest of the screen + * 01 - border Border area + * 02 - title Title bar + * 03 - unsel Unselected menu item + * 04 - hotkey Unselected hotkey + * 05 - sel Selection bar + * 06 - hotsel Selected hotkey + * 07 - scrollbar Scroll bar + * 08 - tabmsg Press [Tab] message + * 09 - cmdmark Command line marker + * 10 - cmdline Command line + * 11 - pwdborder Password box border + * 12 - pwdheader Password box header + * 13 - pwdentry Password box contents + * 14 - timeout_msg Timeout message + * 15 - timeout Timeout counter + * 16 - help Current entry help text + * 17 - disabled Disabled menu item + */ + +static const struct color_table default_colors[] = { + { "screen", "37;40", 0x80ffffff, 0x00000000, SHADOW_NORMAL }, + { "border", "30;44", 0x40000000, 0x00000000, SHADOW_NORMAL }, + { "title", "1;36;44", 0xc00090f0, 0x00000000, SHADOW_NORMAL }, + { "unsel", "37;44", 0x90ffffff, 0x00000000, SHADOW_NORMAL }, + { "hotkey", "1;37;44", 0xffffffff, 0x00000000, SHADOW_NORMAL }, + { "sel", "7;37;40", 0xe0000000, 0x20ff8000, SHADOW_ALL }, + { "hotsel", "1;7;37;40", 0xe0400000, 0x20ff8000, SHADOW_ALL }, + { "scrollbar", "30;44", 0x40000000, 0x00000000, SHADOW_NORMAL }, + { "tabmsg", "31;40", 0x90ffff00, 0x00000000, SHADOW_NORMAL }, + { "cmdmark", "1;36;40", 0xc000ffff, 0x00000000, SHADOW_NORMAL }, + { "cmdline", "37;40", 0xc0ffffff, 0x00000000, SHADOW_NORMAL }, + { "pwdborder", "30;47", 0x80ffffff, 0x20ffffff, SHADOW_NORMAL }, + { "pwdheader", "31;47", 0x80ff8080, 0x20ffffff, SHADOW_NORMAL }, + { "pwdentry", "30;47", 0x80ffffff, 0x20ffffff, SHADOW_NORMAL }, + { "timeout_msg", "37;40", 0x80ffffff, 0x00000000, SHADOW_NORMAL }, + { "timeout", "1;37;40", 0xc0ffffff, 0x00000000, SHADOW_NORMAL }, + { "help", "37;40", 0xc0ffffff, 0x00000000, SHADOW_NORMAL }, + { "disabled", "1;30;44", 0x60cccccc, 0x00000000, SHADOW_NORMAL }, +}; + +#define NCOLORS (sizeof default_colors/sizeof default_colors[0]) +const int message_base_color = NCOLORS; +const int menu_color_table_size = NCOLORS+256; + +/* Algorithmically generate the msgXX colors */ +void set_msg_colors_global(struct color_table *tbl, + unsigned int fg, unsigned int bg, + enum color_table_shadow shadow) +{ + struct color_table *cp = tbl+message_base_color; + unsigned int i; + unsigned int fga, bga; + unsigned int fgh, bgh; + unsigned int fg_idx, bg_idx; + unsigned int fg_rgb, bg_rgb; + + static const unsigned int pc2rgb[8] = + { 0x000000, 0x0000ff, 0x00ff00, 0x00ffff, 0xff0000, 0xff00ff, 0xffff00, + 0xffffff }; + + /* Converting PC RGBI to sensible RGBA values is an "interesting" + proposition. This algorithm may need plenty of tweaking. */ + + fga = fg & 0xff000000; + fgh = ((fg >> 1) & 0xff000000) | 0x80000000; + + bga = bg & 0xff000000; + bgh = ((bg >> 1) & 0xff000000) | 0x80000000; + + for (i = 0; i < 256; i++) { + fg_idx = i & 15; + bg_idx = i >> 4; + + fg_rgb = pc2rgb[fg_idx & 7] & fg; + bg_rgb = pc2rgb[bg_idx & 7] & bg; + + if (fg_idx & 8) { + /* High intensity foreground */ + fg_rgb |= fgh; + } else { + fg_rgb |= fga; + } + + if (bg_idx == 0) { + /* Default black background, assume transparent */ + bg_rgb = 0; + } else if (bg_idx & 8) { + bg_rgb |= bgh; + } else { + bg_rgb |= bga; + } + + cp->argb_fg = fg_rgb; + cp->argb_bg = bg_rgb; + cp->shadow = shadow; + cp++; + } +} + +struct color_table *default_color_table(void) +{ + unsigned int i; + const struct color_table *dp; + struct color_table *cp; + struct color_table *color_table; + static const int pc2ansi[8] = {0, 4, 2, 6, 1, 5, 3, 7}; + static char msg_names[6*256]; + char *mp; + + color_table = calloc(NCOLORS+256, sizeof(struct color_table)); + + dp = default_colors; + cp = color_table; + + for (i = 0; i < NCOLORS; i++) { + *cp = *dp; + cp->ansi = refstrdup(dp->ansi); + cp++; + dp++; + } + + mp = msg_names; + for (i = 0; i < 256; i++) { + cp->name = mp; + mp += sprintf(mp, "msg%02x", i)+1; + + rsprintf(&cp->ansi, "%s3%d;4%d", (i & 8) ? "1;" : "", + pc2ansi[i & 7], pc2ansi[(i >> 4) & 7]); + cp++; + } + + /*** XXX: This needs to move to run_menu() ***/ + console_color_table = color_table; + console_color_table_size = NCOLORS+256; + + set_msg_colors_global(color_table, MSG_COLORS_DEF_FG, + MSG_COLORS_DEF_BG, MSG_COLORS_DEF_SHADOW); + + return color_table; +} + +struct color_table *copy_color_table(const struct color_table *master) +{ + const struct color_table *dp; + struct color_table *color_table, *cp; + unsigned int i; + + color_table = calloc(NCOLORS+256, sizeof(struct color_table)); + + dp = master; + cp = color_table; + + for (i = 0; i < NCOLORS+256; i++) { + *cp = *dp; + cp->ansi = refstr_get(dp->ansi); + cp++; + dp++; + } + + return color_table; +} diff --git a/com32/menu/drain.c b/com32/menu/drain.c new file mode 100644 index 0000000..ace0451 --- /dev/null +++ b/com32/menu/drain.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include +#include + +void drain_keyboard(void) +{ + /* Prevent "ghost typing" and keyboard buffer snooping */ + volatile char junk; + int rv; + + do { + rv = read(0, (char *)&junk, 1); + } while (rv > 0); + + junk = 0; + + cli(); + *(volatile uint8_t *)0x419 = 0; /* Alt-XXX keyboard area */ + *(volatile uint16_t *)0x41a = 0x1e; /* Keyboard buffer empty */ + *(volatile uint16_t *)0x41c = 0x1e; + memset((void *)0x41e, 0, 32); /* Clear the actual keyboard buffer */ + sti(); +} diff --git a/com32/menu/execute.c b/com32/menu/execute.c new file mode 100644 index 0000000..d84958c --- /dev/null +++ b/com32/menu/execute.c @@ -0,0 +1,69 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - 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, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include +#include "menu.h" + +void execute(const char *cmdline, enum kernel_type type) +{ + com32sys_t ireg; + const char *p, * const *pp; + char *q = __com32.cs_bounce; + const char *kernel, *args; + + memset(&ireg, 0, sizeof ireg); + + kernel = q; + p = cmdline; + while ( *p && !my_isspace(*p) ) { + *q++ = *p++; + } + *q++ = '\0'; + + args = q; + while ( *p && my_isspace(*p) ) + p++; + + strcpy(q, p); + + if (kernel[0] == '.' && type == KT_NONE) { + /* It might be a type specifier */ + enum kernel_type type = KT_NONE; + for (pp = kernel_types; *pp; pp++, type++) { + if (!strcmp(kernel+1, *pp)) { + execute(p, type); /* Strip the type specifier and retry */ + } + } + } + + if (type == KT_LOCALBOOT) { + ireg.eax.w[0] = 0x0014; /* Local boot */ + ireg.edx.w[0] = strtoul(kernel, NULL, 0); + } else { + if (type < KT_KERNEL) + type = KT_KERNEL; + + ireg.eax.w[0] = 0x0016; /* Run kernel image */ + ireg.esi.w[0] = OFFS(kernel); + ireg.ds = SEG(kernel); + ireg.ebx.w[0] = OFFS(args); + ireg.es = SEG(args); + ireg.edx.l = type-KT_KERNEL; + /* ireg.ecx.l = 0; */ /* We do ipappend "manually" */ + } + + __intcall(0x22, &ireg, NULL); + + /* If this returns, something went bad; return to menu */ +} diff --git a/com32/menu/menu.c b/com32/menu/menu.c new file mode 100644 index 0000000..6d64bad --- /dev/null +++ b/com32/menu/menu.c @@ -0,0 +1,34 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - 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, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * menu.c + * + * Simple menu system which displays a list and allows the user to select + * a command line and/or edit it. + */ + +#include +#include "menu.h" + +int draw_background(const char *arg) +{ + /* Nothing to do... */ + (void)arg; + return 0; +} + +int main(int argc, char *argv[]) +{ + console_ansi_raw(); + return menu_main(argc, argv); +} diff --git a/com32/menu/menu.c32 b/com32/menu/menu.c32 new file mode 100755 index 0000000000000000000000000000000000000000..9413ec469f9f6e2acacf0e0d8c7b709bccdf4338 GIT binary patch literal 57140 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 8QSBnnT?{@D;F>_ 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 xY|VDTk7TzB^UE*J@OdaEhH}0MfBG|=j(`b +#include +#include +#include +#include +#include +#include +#include "refstr.h" + +#ifndef CLK_TCK +# define CLK_TCK sysconf(_SC_CLK_TCK) +#endif + +struct menu; + +/* Note: the _UNRES variants must always be immediately after their + "normal" versions. */ +enum menu_action { + MA_NONE, /* Undefined value */ + MA_CMD, /* Execute a command */ + MA_DISABLED, /* Disabled menu entry */ + MA_SUBMENU, /* This is a submenu entry */ + MA_GOTO, /* Go to another menu */ + MA_GOTO_UNRES, /* Unresolved go to */ + MA_QUIT, /* Quit to CLI */ + MA_EXIT, /* Exit to higher-level menu */ + MA_EXIT_UNRES, /* Unresolved exit */ +}; + +struct menu_entry { + struct menu *menu; /* Parent menu */ + const char *displayname; + const char *label; + const char *passwd; + char *helptext; + const char *cmdline; + struct menu *submenu; + struct menu_entry *next; /* Linked list of all labels across menus */ + int entry; /* Entry number inside menu */ + enum menu_action action; + unsigned char hotkey; + bool save; /* Save this entry if selected */ +}; + +static inline bool is_disabled(struct menu_entry *me) +{ + return me->action == MA_DISABLED; +} + +enum kernel_type { + /* Meta-types for internal use */ + KT_NONE, + KT_LOCALBOOT, + + /* The ones we can pass off to SYSLINUX, in order */ + KT_KERNEL, /* Undefined type */ + KT_LINUX, /* Linux kernel */ + KT_BOOT, /* Bootstrap program */ + KT_BSS, /* Boot sector with patch */ + KT_PXE, /* PXE NBP */ + KT_FDIMAGE, /* Floppy disk image */ + KT_COMBOOT, /* COMBOOT image */ + KT_COM32, /* COM32 image */ + KT_CONFIG, /* Configuration file */ +}; + +extern const char * const kernel_types[]; + +/* Configurable integer parameters */ +enum parameter_number { + P_WIDTH, + P_MARGIN, + P_PASSWD_MARGIN, + P_MENU_ROWS, + P_TABMSG_ROW, + P_CMDLINE_ROW, + P_END_ROW, + P_PASSWD_ROW, + P_TIMEOUT_ROW, + P_HELPMSG_ROW, + P_HELPMSGEND_ROW, + P_HSHIFT, + P_VSHIFT, + P_HIDDEN_ROW, + + NPARAMS +}; + +/* Configurable messages */ +enum message_number { + MSG_TITLE, + MSG_AUTOBOOT, + MSG_TAB, + MSG_NOTAB, + MSG_PASSPROMPT, + + MSG_COUNT +}; + +struct messages { + const char *name; /* Message configuration name */ + const char *defmsg; /* Default message text */ +}; + +struct menu_parameter { + const char *name; + int value; +}; + +extern const struct menu_parameter mparm[NPARAMS]; + +struct fkey_help { + const char *textname; + const char *background; +}; + +struct menu { + struct menu *next; /* Linked list of all menus */ + const char *label; /* Goto label for this menu */ + struct menu *parent; + struct menu_entry *parent_entry; /* Entry for self in parent */ + + struct menu_entry **menu_entries; + struct menu_entry *menu_hotkeys[256]; + + const char *messages[MSG_COUNT]; + int mparm[NPARAMS]; + + int nentries; + int nentries_space; + int defentry; + int timeout; + + bool allowedit; + bool save; /* MENU SAVE default for this menu */ + + int curentry; + int curtop; + + const char *title; + const char *ontimeout; + const char *onerror; + const char *menu_master_passwd; + const char *menu_background; + + struct color_table *color_table; + + struct fkey_help fkeyhelp[12]; +}; + +extern struct menu *root_menu, *start_menu, *hide_menu, *menu_list; + +/* 2048 is the current definition inside syslinux */ +#define MAX_CMDLINE_LEN 2048 + +/* These are global parameters regardless of which menu we're displaying */ +extern int shiftkey; +extern int hiddenmenu; +extern long long totaltimeout; + +void parse_configs(char **argv); +int draw_background(const char *filename); + +static inline int my_isspace(char c) +{ + return (unsigned char)c <= ' '; +} + +int my_isxdigit(char c); +unsigned int hexval(char c); +unsigned int hexval2(const char *p); +uint32_t parse_argb(char **p); + +int menu_main(int argc, char *argv[]); +void console_prepare(void); +void console_cleanup(void); + +extern const int message_base_color, menu_color_table_size; +int mygetkey(clock_t timeout); +int show_message_file(const char *filename, const char *background); + +/* passwd.c */ +int passwd_compare(const char *passwd, const char *entry); + +/* colors.c */ +#define MSG_COLORS_DEF_FG 0x90ffffff +#define MSG_COLORS_DEF_BG 0x80ffffff +#define MSG_COLORS_DEF_SHADOW SHADOW_NORMAL +void set_msg_colors_global(struct color_table *tbl, + unsigned int fg, unsigned int bg, + enum color_table_shadow shadow); +struct color_table *default_color_table(void); +struct color_table *copy_color_table(const struct color_table *master); +extern const int message_base_color; + +/* background.c */ +extern const char *current_background; +void set_background(const char *new_background); + +/* execute.c */ +void execute(const char *cmdline, enum kernel_type type); + +/* drain.c */ +void drain_keyboard(void); + +#endif /* MENU_H */ diff --git a/com32/menu/menumain.c b/com32/menu/menumain.c new file mode 100644 index 0000000..e5b6eb9 --- /dev/null +++ b/com32/menu/menumain.c @@ -0,0 +1,1096 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * 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, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * menumain.c + * + * Simple menu system which displays a list and allows the user to select + * a command line and/or edit it. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "menu.h" + +/* The symbol "cm" always refers to the current menu across this file... */ +static struct menu *cm; + +const struct menu_parameter mparm[NPARAMS] = { + [P_WIDTH] = { "width", 0 }, + [P_MARGIN] = { "margin", 10 }, + [P_PASSWD_MARGIN] = { "passwordmargin", 3 }, + [P_MENU_ROWS] = { "rows", 12 }, + [P_TABMSG_ROW] = { "tabmsgrow", 18 }, + [P_CMDLINE_ROW] = { "cmdlinerow", 18 }, + [P_END_ROW] = { "endrow", -1 }, + [P_PASSWD_ROW] = { "passwordrow", 11 }, + [P_TIMEOUT_ROW] = { "timeoutrow", 20 }, + [P_HELPMSG_ROW] = { "helpmsgrow", 22 }, + [P_HELPMSGEND_ROW] = { "helpmsgendrow", -1 }, + [P_HSHIFT] = { "hshift", 0 }, + [P_VSHIFT] = { "vshift", 0 }, + [P_HIDDEN_ROW] = { "hiddenrow", -2 }, +}; + +/* These macros assume "cm" is a pointer to the current menu */ +#define WIDTH (cm->mparm[P_WIDTH]) +#define MARGIN (cm->mparm[P_MARGIN]) +#define PASSWD_MARGIN (cm->mparm[P_PASSWD_MARGIN]) +#define MENU_ROWS (cm->mparm[P_MENU_ROWS]) +#define TABMSG_ROW (cm->mparm[P_TABMSG_ROW]+VSHIFT) +#define CMDLINE_ROW (cm->mparm[P_CMDLINE_ROW]+VSHIFT) +#define END_ROW (cm->mparm[P_END_ROW]) +#define PASSWD_ROW (cm->mparm[P_PASSWD_ROW]+VSHIFT) +#define TIMEOUT_ROW (cm->mparm[P_TIMEOUT_ROW]+VSHIFT) +#define HELPMSG_ROW (cm->mparm[P_HELPMSG_ROW]+VSHIFT) +#define HELPMSGEND_ROW (cm->mparm[P_HELPMSGEND_ROW]) +#define HSHIFT (cm->mparm[P_HSHIFT]) +#define VSHIFT (cm->mparm[P_VSHIFT]) +#define HIDDEN_ROW (cm->mparm[P_HIDDEN_ROW]) + +static char * +pad_line(const char *text, int align, int width) +{ + static char buffer[MAX_CMDLINE_LEN]; + int n, p; + + if ( width >= (int) sizeof buffer ) + return NULL; /* Can't do it */ + + n = strlen(text); + if ( n >= width ) + n = width; + + memset(buffer, ' ', width); + buffer[width] = 0; + p = ((width-n)*align)>>1; + memcpy(buffer+p, text, n); + + return buffer; +} + +/* Display an entry, with possible hotkey highlight. Assumes + that the current attribute is the non-hotkey one, and will + guarantee that as an exit condition as well. */ +static void +display_entry(const struct menu_entry *entry, const char *attrib, + const char *hotattrib, int width) +{ + const char *p = entry->displayname; + char marker; + + if (!p) + p = ""; + + switch (entry->action) { + case MA_SUBMENU: + marker = '>'; + break; + case MA_EXIT: + marker = '<'; + break; + default: + marker = 0; + break; + } + + if (marker) + width -= 2; + + while ( width ) { + if ( *p ) { + if ( *p == '^' ) { + p++; + if ( *p && ((unsigned char)*p & ~0x20) == entry->hotkey ) { + fputs(hotattrib, stdout); + putchar(*p++); + fputs(attrib, stdout); + width--; + } + } else { + putchar(*p++); + width--; + } + } else { + putchar(' '); + width--; + } + } + + if (marker) { + putchar(' '); + putchar(marker); + } +} + +static void +draw_row(int y, int sel, int top, int sbtop, int sbbot) +{ + int i = (y-4-VSHIFT)+top; + int dis = (i < cm->nentries) && is_disabled(cm->menu_entries[i]); + + printf("\033[%d;%dH\1#1\016x\017%s ", + y, MARGIN+1+HSHIFT, + (i == sel) ? "\1#5" : dis ? "\2#17" : "\1#3"); + + if ( i >= cm->nentries ) { + fputs(pad_line("", 0, WIDTH-2*MARGIN-4), stdout); + } else { + display_entry(cm->menu_entries[i], + (i == sel) ? "\1#5" : dis ? "\2#17" : "\1#3", + (i == sel) ? "\1#6" : dis ? "\2#17" : "\1#4", + WIDTH-2*MARGIN-4); + } + + if ( cm->nentries <= MENU_ROWS ) { + printf(" \1#1\016x\017"); + } else if ( sbtop > 0 ) { + if ( y >= sbtop && y <= sbbot ) + printf(" \1#7\016a\017"); + else + printf(" \1#1\016x\017"); + } else { + putchar(' '); /* Don't modify the scrollbar */ + } +} + +static jmp_buf timeout_jump; + +int mygetkey(clock_t timeout) +{ + clock_t t0, t; + clock_t tto, to; + int key; + + if ( !totaltimeout ) + return get_key(stdin, timeout); + + for (;;) { + tto = min(totaltimeout, INT_MAX); + to = timeout ? min(tto, timeout) : tto; + + t0 = times(NULL); + key = get_key(stdin, to); + t = times(NULL) - t0; + + if ( totaltimeout <= t ) + longjmp(timeout_jump, 1); + + totaltimeout -= t; + + if ( key != KEY_NONE ) + return key; + + if ( timeout ) { + if ( timeout <= t ) + return KEY_NONE; + + timeout -= t; + } + } +} + +static int +ask_passwd(const char *menu_entry) +{ + char user_passwd[WIDTH], *p; + int done; + int key; + int x; + int rv; + + printf("\033[%d;%dH\2#11\016l", PASSWD_ROW, PASSWD_MARGIN+1); + for ( x = 2 ; x <= WIDTH-2*PASSWD_MARGIN-1 ; x++ ) + putchar('q'); + + printf("k\033[%d;%dHx", PASSWD_ROW+1, PASSWD_MARGIN+1); + for ( x = 2 ; x <= WIDTH-2*PASSWD_MARGIN-1 ; x++ ) + putchar(' '); + + printf("x\033[%d;%dHm", PASSWD_ROW+2, PASSWD_MARGIN+1); + for ( x = 2 ; x <= WIDTH-2*PASSWD_MARGIN-1 ; x++ ) + putchar('q'); + + printf("j\017\033[%d;%dH\2#12 %s \033[%d;%dH\2#13", + PASSWD_ROW, (WIDTH-(strlen(cm->messages[MSG_PASSPROMPT])+2))/2, + cm->messages[MSG_PASSPROMPT], PASSWD_ROW+1, PASSWD_MARGIN+3); + + drain_keyboard(); + + /* Actually allow user to type a password, then compare to the SHA1 */ + done = 0; + p = user_passwd; + + while ( !done ) { + key = mygetkey(0); + + switch ( key ) { + case KEY_ENTER: + case KEY_CTRL('J'): + done = 1; + break; + + case KEY_ESC: + case KEY_CTRL('C'): + p = user_passwd; /* No password entered */ + done = 1; + break; + + case KEY_BACKSPACE: + case KEY_DEL: + case KEY_DELETE: + if ( p > user_passwd ) { + printf("\b \b"); + p--; + } + break; + + case KEY_CTRL('U'): + while ( p > user_passwd ) { + printf("\b \b"); + p--; + } + break; + + default: + if ( key >= ' ' && key <= 0xFF && + (p-user_passwd) < WIDTH-2*PASSWD_MARGIN-5 ) { + *p++ = key; + putchar('*'); + } + break; + } + } + + if ( p == user_passwd ) + return 0; /* No password entered */ + + *p = '\0'; + + rv = (cm->menu_master_passwd && + passwd_compare(cm->menu_master_passwd, user_passwd)) + || (menu_entry && passwd_compare(menu_entry, user_passwd)); + + /* Clean up */ + memset(user_passwd, 0, WIDTH); + drain_keyboard(); + + return rv; +} + + +static void +draw_menu(int sel, int top, int edit_line) +{ + int x, y; + int sbtop = 0, sbbot = 0; + const char *tabmsg; + int tabmsg_len; + + if ( cm->nentries > MENU_ROWS ) { + int sblen = max(MENU_ROWS*MENU_ROWS/cm->nentries, 1); + sbtop = (MENU_ROWS-sblen+1)*top/(cm->nentries-MENU_ROWS+1); + sbbot = sbtop+sblen-1; + sbtop += 4; sbbot += 4; /* Starting row of scrollbar */ + } + + printf("\033[%d;%dH\1#1\016l", VSHIFT+1, HSHIFT+MARGIN+1); + for ( x = 2+HSHIFT ; x <= (WIDTH-2*MARGIN-1)+HSHIFT ; x++ ) + putchar('q'); + + printf("k\033[%d;%dH\1#1x\017\1#2 %s \1#1\016x", + VSHIFT+2, + HSHIFT+MARGIN+1, + pad_line(cm->title, 1, WIDTH-2*MARGIN-4)); + + printf("\033[%d;%dH\1#1t", VSHIFT+3, HSHIFT+MARGIN+1); + for ( x = 2+HSHIFT ; x <= (WIDTH-2*MARGIN-1)+HSHIFT ; x++ ) + putchar('q'); + fputs("u\017", stdout); + + for ( y = 4+VSHIFT ; y < 4+VSHIFT+MENU_ROWS ; y++ ) + draw_row(y, sel, top, sbtop, sbbot); + + printf("\033[%d;%dH\1#1\016m", y, HSHIFT+MARGIN+1); + for ( x = 2+HSHIFT ; x <= (WIDTH-2*MARGIN-1)+HSHIFT ; x++ ) + putchar('q'); + fputs("j\017", stdout); + + if ( edit_line && cm->allowedit && !cm->menu_master_passwd ) + tabmsg = cm->messages[MSG_TAB]; + else + tabmsg = cm->messages[MSG_NOTAB]; + + tabmsg_len = strlen(tabmsg); + + printf("\1#8\033[%d;%dH%s", + TABMSG_ROW, 1+HSHIFT+((WIDTH-tabmsg_len)>>1), tabmsg); + printf("\1#0\033[%d;1H", END_ROW); +} + +static void +clear_screen(void) +{ + fputs("\033e\033%@\033)0\033(B\1#0\033[?25l\033[2J", stdout); +} + +static void +display_help(const char *text) +{ + int row; + const char *p; + + if (!text) { + text = ""; + printf("\1#0\033[%d;1H", HELPMSG_ROW); + } else { + printf("\2#16\033[%d;1H", HELPMSG_ROW); + } + + for (p = text, row = HELPMSG_ROW; *p && row <= HELPMSGEND_ROW; p++) { + switch (*p) { + case '\r': + case '\f': + case '\v': + case '\033': + break; + case '\n': + printf("\033[K\033[%d;1H", ++row); + break; + default: + putchar(*p); + } + } + + fputs("\033[K", stdout); + + while (row <= HELPMSGEND_ROW) { + printf("\033[K\033[%d;1H", ++row); + } +} + +static void show_fkey(int key) +{ + int fkey; + + while (1) { + switch (key) { + case KEY_F1: fkey = 0; break; + case KEY_F2: fkey = 1; break; + case KEY_F3: fkey = 2; break; + case KEY_F4: fkey = 3; break; + case KEY_F5: fkey = 4; break; + case KEY_F6: fkey = 5; break; + case KEY_F7: fkey = 6; break; + case KEY_F8: fkey = 7; break; + case KEY_F9: fkey = 8; break; + case KEY_F10: fkey = 9; break; + case KEY_F11: fkey = 10; break; + case KEY_F12: fkey = 11; break; + default: fkey = -1; break; + } + + if (fkey == -1) + break; + + if (cm->fkeyhelp[fkey].textname) + key = show_message_file(cm->fkeyhelp[fkey].textname, + cm->fkeyhelp[fkey].background); + else + break; + } +} + +static const char * +edit_cmdline(const char *input, int top) +{ + static char cmdline[MAX_CMDLINE_LEN]; + int key, len, prev_len, cursor; + int redraw = 1; /* We enter with the menu already drawn */ + + strncpy(cmdline, input, MAX_CMDLINE_LEN); + cmdline[MAX_CMDLINE_LEN-1] = '\0'; + + len = cursor = strlen(cmdline); + prev_len = 0; + + for (;;) { + if ( redraw > 1 ) { + /* Clear and redraw whole screen */ + /* Enable ASCII on G0 and DEC VT on G1; do it in this order + to avoid confusing the Linux console */ + clear_screen(); + draw_menu(-1, top, 1); + prev_len = 0; + } + + if ( redraw > 0 ) { + /* Redraw the command line */ + printf("\033[?25l\033[%d;1H\1#9> \2#10%s", + CMDLINE_ROW, pad_line(cmdline, 0, max(len, prev_len))); + printf("\2#10\033[%d;3H%s\033[?25h", + CMDLINE_ROW, pad_line(cmdline, 0, cursor)); + prev_len = len; + redraw = 0; + } + + key = mygetkey(0); + + switch( key ) { + case KEY_CTRL('L'): + redraw = 2; + break; + + case KEY_ENTER: + case KEY_CTRL('J'): + return cmdline; + + case KEY_ESC: + case KEY_CTRL('C'): + return NULL; + + case KEY_BACKSPACE: + case KEY_DEL: + if ( cursor ) { + memmove(cmdline+cursor-1, cmdline+cursor, len-cursor+1); + len--; + cursor--; + redraw = 1; + } + break; + + case KEY_CTRL('D'): + case KEY_DELETE: + if ( cursor < len ) { + memmove(cmdline+cursor, cmdline+cursor+1, len-cursor); + len--; + redraw = 1; + } + break; + + case KEY_CTRL('U'): + if ( len ) { + len = cursor = 0; + cmdline[len] = '\0'; + redraw = 1; + } + break; + + case KEY_CTRL('W'): + if ( cursor ) { + int prevcursor = cursor; + + while ( cursor && my_isspace(cmdline[cursor-1]) ) + cursor--; + + while ( cursor && !my_isspace(cmdline[cursor-1]) ) + cursor--; + + memmove(cmdline+cursor, cmdline+prevcursor, len-prevcursor+1); + len -= (cursor-prevcursor); + redraw = 1; + } + break; + + case KEY_LEFT: + case KEY_CTRL('B'): + if ( cursor ) { + cursor--; + redraw = 1; + } + break; + + case KEY_RIGHT: + case KEY_CTRL('F'): + if ( cursor < len ) { + putchar(cmdline[cursor++]); + } + break; + + case KEY_CTRL('K'): + if ( cursor < len ) { + cmdline[len = cursor] = '\0'; + redraw = 1; + } + break; + + case KEY_HOME: + case KEY_CTRL('A'): + if ( cursor ) { + cursor = 0; + redraw = 1; + } + break; + + case KEY_END: + case KEY_CTRL('E'): + if ( cursor != len ) { + cursor = len; + redraw = 1; + } + break; + + case KEY_F1: + case KEY_F2: + case KEY_F3: + case KEY_F4: + case KEY_F5: + case KEY_F6: + case KEY_F7: + case KEY_F8: + case KEY_F9: + case KEY_F10: + case KEY_F11: + case KEY_F12: + show_fkey(key); + redraw = 1; + break; + + default: + if ( key >= ' ' && key <= 0xFF && len < MAX_CMDLINE_LEN-1 ) { + if ( cursor == len ) { + cmdline[len] = key; + cmdline[++len] = '\0'; + cursor++; + putchar(key); + prev_len++; + } else { + memmove(cmdline+cursor+1, cmdline+cursor, len-cursor+1); + cmdline[cursor++] = key; + len++; + redraw = 1; + } + } + break; + } + } +} + +static inline int +shift_is_held(void) +{ + uint8_t shift_bits = *(uint8_t *)0x417; + + return !!(shift_bits & 0x5d); /* Caps/Scroll/Alt/Shift */ +} + +static void +print_timeout_message(int tol, int row, const char *msg) +{ + char buf[256]; + int nc = 0, nnc; + const char *tp = msg; + char tc; + char *tq = buf; + + while ((size_t)(tq-buf) < (sizeof buf-16) && (tc = *tp)) { + tp++; + if (tc == '#') { + nnc = sprintf(tq, "\2#15%d\2#14", tol); + tq += nnc; + nc += nnc-8; /* 8 formatting characters */ + } else if (tc == '{') { + /* Deal with {singular[,dual],plural} constructs */ + struct { + const char *s, *e; + } tx[3]; + const char *tpp; + int n = 0; + + memset(tx, 0, sizeof tx); + + tx[0].s = tp; + + while (*tp && *tp != '}') { + if (*tp == ',' && n < 2) { + tx[n].e = tp; + n++; + tx[n].s = tp+1; + } + tp++; + } + tx[n].e = tp; + + if (*tp) + tp++; /* Skip final bracket */ + + if (!tx[1].s) + tx[1] = tx[0]; + if (!tx[2].s) + tx[2] = tx[1]; + + /* Now [0] is singular, [1] is dual, and [2] is plural, + even if the user only specified some of them. */ + + switch (tol) { + case 1: n = 0; break; + case 2: n = 1; break; + default: n = 2; break; + } + + for (tpp = tx[n].s; tpp < tx[n].e; tpp++) { + if ((size_t)(tq-buf) < (sizeof buf)) { + *tq++ = *tpp; + nc++; + } + } + } else { + *tq++ = tc; + nc++; + } + } + *tq = '\0'; + + /* Let's hope 4 spaces on each side is enough... */ + printf("\033[%d;%dH\2#14 %s ", row, HSHIFT+1+((WIDTH-nc-8)>>1), buf); +} + +/* Set the background screen, etc. */ +static void +prepare_screen_for_menu(void) +{ + console_color_table = cm->color_table; + console_color_table_size = menu_color_table_size; + set_background(cm->menu_background); +} + +static const char * +do_hidden_menu(void) +{ + int key; + int timeout_left, this_timeout; + + clear_screen(); + + if ( !setjmp(timeout_jump) ) { + timeout_left = cm->timeout; + + while (!cm->timeout || timeout_left) { + int tol = timeout_left/CLK_TCK; + + print_timeout_message(tol, HIDDEN_ROW, cm->messages[MSG_AUTOBOOT]); + + this_timeout = min(timeout_left, CLK_TCK); + key = mygetkey(this_timeout); + + if (key != KEY_NONE) + return NULL; /* Key pressed */ + + timeout_left -= this_timeout; + } + } + + if (cm->ontimeout) + return cm->ontimeout; + return cm->menu_entries[cm->defentry]->cmdline; /* Default entry */ +} + +static const char * +run_menu(void) +{ + int key; + int done = 0; + volatile int entry = cm->curentry; + int prev_entry = -1; + volatile int top = cm->curtop; + int prev_top = -1; + int clear = 1, to_clear; + const char *cmdline = NULL; + volatile clock_t key_timeout, timeout_left, this_timeout; + const struct menu_entry *me; + + /* Note: for both key_timeout and timeout == 0 means no limit */ + timeout_left = key_timeout = cm->timeout; + + /* If we're in shiftkey mode, exit immediately unless a shift key + is pressed */ + if ( shiftkey && !shift_is_held() ) { + return cm->menu_entries[cm->defentry]->cmdline; + } else { + shiftkey = 0; + } + + /* Do this before hiddenmenu handling, so we show the background */ + prepare_screen_for_menu(); + + /* Handle hiddenmenu */ + if ( hiddenmenu ) { + cmdline = do_hidden_menu(); + if (cmdline) + return cmdline; + + /* Otherwise display the menu now; the timeout has already been + cancelled, since the user pressed a key. */ + hiddenmenu = 0; + key_timeout = 0; + } + + /* Handle both local and global timeout */ + if ( setjmp(timeout_jump) ) { + entry = cm->defentry; + + if ( top < 0 || top < entry-MENU_ROWS+1 ) + top = max(0, entry-MENU_ROWS+1); + else if ( top > entry || top > max(0, cm->nentries-MENU_ROWS) ) + top = min(entry, max(0, cm->nentries-MENU_ROWS)); + + draw_menu(cm->ontimeout ? -1 : entry, top, 1); + cmdline = cm->ontimeout ? cm->ontimeout : cm->menu_entries[entry]->cmdline; + done = 1; + } + + while ( !done ) { + if (entry <= 0) { + entry = 0; + while (entry < cm->nentries && is_disabled(cm->menu_entries[entry])) + entry++; + } + if (entry >= cm->nentries) { + entry = cm->nentries-1; + while (entry > 0 && is_disabled(cm->menu_entries[entry])) + entry--; + } + + me = cm->menu_entries[entry]; + + if ( top < 0 || top < entry-MENU_ROWS+1 ) + top = max(0, entry-MENU_ROWS+1); + else if ( top > entry || top > max(0, cm->nentries-MENU_ROWS) ) + top = min(entry, max(0, cm->nentries-MENU_ROWS)); + + /* Start with a clear screen */ + if ( clear ) { + /* Clear and redraw whole screen */ + /* Enable ASCII on G0 and DEC VT on G1; do it in this order + to avoid confusing the Linux console */ + if (clear >= 2) + prepare_screen_for_menu(); + clear_screen(); + clear = 0; + prev_entry = prev_top = -1; + } + + if ( top != prev_top ) { + draw_menu(entry, top, 1); + display_help(me->helptext); + } else if ( entry != prev_entry ) { + draw_row(prev_entry-top+4+VSHIFT, entry, top, 0, 0); + draw_row(entry-top+4+VSHIFT, entry, top, 0, 0); + display_help(me->helptext); + } + + prev_entry = entry; prev_top = top; + cm->curentry = entry; + cm->curtop = top; + + /* Cursor movement cancels timeout */ + if ( entry != cm->defentry ) + key_timeout = 0; + + if ( key_timeout ) { + int tol = timeout_left/CLK_TCK; + print_timeout_message(tol, TIMEOUT_ROW, cm->messages[MSG_AUTOBOOT]); + to_clear = 1; + } else { + to_clear = 0; + } + + this_timeout = min(min(key_timeout, timeout_left), (clock_t)CLK_TCK); + key = mygetkey(this_timeout); + + if ( key != KEY_NONE ) { + timeout_left = key_timeout; + if ( to_clear ) + printf("\033[%d;1H\1#0\033[K", TIMEOUT_ROW); + } + + switch ( key ) { + case KEY_NONE: /* Timeout */ + /* This is somewhat hacky, but this at least lets the user + know what's going on, and still deals with "phantom inputs" + e.g. on serial ports. + + Warning: a timeout will boot the default entry without any + password! */ + if ( key_timeout ) { + if ( timeout_left <= this_timeout ) + longjmp(timeout_jump, 1); + + timeout_left -= this_timeout; + } + break; + + case KEY_CTRL('L'): + clear = 1; + break; + + case KEY_ENTER: + case KEY_CTRL('J'): + key_timeout = 0; /* Cancels timeout */ + if ( me->passwd ) { + clear = 1; + done = ask_passwd(me->passwd); + } else { + done = 1; + } + cmdline = NULL; + if (done) { + switch (me->action) { + case MA_CMD: + cmdline = me->cmdline; + break; + case MA_SUBMENU: + case MA_GOTO: + case MA_EXIT: + done = 0; + clear = 2; + cm = me->submenu; + entry = cm->curentry; + top = cm->curtop; + break; + case MA_QUIT: + /* Quit menu system */ + done = 1; + clear = 1; + draw_row(entry-top+4+VSHIFT, -1, top, 0, 0); + break; + default: + done = 0; + break; + } + } + if (done && !me->passwd) { + /* Only save a new default if we don't have a password... */ + if (me->save && me->label) { + syslinux_setadv(ADV_MENUSAVE, strlen(me->label), me->label); + syslinux_adv_write(); + } + } + break; + + case KEY_UP: + case KEY_CTRL('P'): + while (entry > 0) { + entry--; + if (entry < top) + top -= MENU_ROWS; + if (!is_disabled(cm->menu_entries[entry])) + break; + } + break; + + case KEY_DOWN: + case KEY_CTRL('N'): + while (entry < cm->nentries-1) { + entry++; + if (entry >= top+MENU_ROWS) + top += MENU_ROWS; + if (!is_disabled(cm->menu_entries[entry])) + break; + } + break; + + case KEY_PGUP: + case KEY_LEFT: + case KEY_CTRL('B'): + case '<': + entry -= MENU_ROWS; + top -= MENU_ROWS; + while (entry > 0 && is_disabled(cm->menu_entries[entry])) { + entry--; + if (entry < top) + top -= MENU_ROWS; + } + break; + + case KEY_PGDN: + case KEY_RIGHT: + case KEY_CTRL('F'): + case '>': + case ' ': + entry += MENU_ROWS; + top += MENU_ROWS; + while (entry < cm->nentries-1 && is_disabled(cm->menu_entries[entry])) { + entry++; + if (entry >= top+MENU_ROWS) + top += MENU_ROWS; + } + break; + + case '-': + while (entry > 0) { + entry--; + top--; + if (!is_disabled(cm->menu_entries[entry])) + break; + } + break; + + case '+': + while (entry < cm->nentries-1) { + entry++; + top++; + if (!is_disabled(cm->menu_entries[entry])) + break; + } + break; + + case KEY_CTRL('A'): + case KEY_HOME: + top = entry = 0; + break; + + case KEY_CTRL('E'): + case KEY_END: + entry = cm->nentries - 1; + top = max(0, cm->nentries-MENU_ROWS); + break; + + case KEY_F1: + case KEY_F2: + case KEY_F3: + case KEY_F4: + case KEY_F5: + case KEY_F6: + case KEY_F7: + case KEY_F8: + case KEY_F9: + case KEY_F10: + case KEY_F11: + case KEY_F12: + show_fkey(key); + clear = 1; + break; + + case KEY_TAB: + if ( cm->allowedit && me->action == MA_CMD ) { + int ok = 1; + + key_timeout = 0; /* Cancels timeout */ + draw_row(entry-top+4+VSHIFT, -1, top, 0, 0); + + if ( cm->menu_master_passwd ) { + ok = ask_passwd(NULL); + clear_screen(); + draw_menu(-1, top, 0); + } else { + /* Erase [Tab] message and help text*/ + printf("\033[%d;1H\1#0\033[K", TABMSG_ROW); + display_help(NULL); + } + + if ( ok ) { + cmdline = edit_cmdline(me->cmdline, top); + done = !!cmdline; + clear = 1; /* In case we hit [Esc] and done is null */ + } else { + draw_row(entry-top+4+VSHIFT, entry, top, 0, 0); + } + } + break; + case KEY_CTRL('C'): /* Ctrl-C */ + case KEY_ESC: /* Esc */ + if ( cm->parent ) { + cm = cm->parent; + clear = 2; + entry = cm->curentry; + top = cm->curtop; + } else if ( cm->allowedit ) { + done = 1; + clear = 1; + key_timeout = 0; + + draw_row(entry-top+4+VSHIFT, -1, top, 0, 0); + + if ( cm->menu_master_passwd ) + done = ask_passwd(NULL); + } + break; + default: + if ( key > 0 && key < 0xFF ) { + key &= ~0x20; /* Upper case */ + if ( cm->menu_hotkeys[key] ) { + key_timeout = 0; + entry = cm->menu_hotkeys[key]->entry; + /* Should we commit at this point? */ + } + } + break; + } + } + + printf("\033[?25h"); /* Show cursor */ + + /* Return the label name so localboot and ipappend work */ + return cmdline; +} + +int menu_main(int argc, char *argv[]) +{ + const char *cmdline; + struct menu *m; + int rows, cols; + int i; + + (void)argc; + + if (getscreensize(1, &rows, &cols)) { + /* Unknown screen size? */ + rows = 24; + cols = 80; + } + + parse_configs(argv+1); + + /* Some postprocessing for all menus */ + for (m = menu_list; m; m = m->next) { + if (!m->mparm[P_WIDTH]) + m->mparm[P_WIDTH] = cols; + + /* If anyone has specified negative parameters, consider them + relative to the bottom row of the screen. */ + for (i = 0; i < NPARAMS; i++) + if (m->mparm[i] < 0) + m->mparm[i] = max(m->mparm[i]+rows, 0); + } + + if ( !cm->nentries ) { + fputs("Initial menu has no LABEL entries!\n", stdout); + return 1; /* Error! */ + } + + cm = start_menu; + for(;;) { + cmdline = run_menu(); + + printf("\033[?25h\033[%d;1H\033[0m", END_ROW); + + if ( cmdline ) { + execute(cmdline, KT_NONE); + if ( cm->onerror ) + execute(cm->onerror, KT_NONE); + } else { + return 0; /* Exit */ + } + } +} diff --git a/com32/menu/passwd.c b/com32/menu/passwd.c new file mode 100644 index 0000000..534fe8b --- /dev/null +++ b/com32/menu/passwd.c @@ -0,0 +1,96 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - 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, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include +#include + +#include "menu.h" + +static int passwd_compare_sha1(const char *passwd, const char *entry) +{ + struct { + SHA1_CTX ctx; + unsigned char sha1[20], pwdsha1[20]; + } d; + const char *p; + int rv; + + SHA1Init(&d.ctx); + + if ( (p = strchr(passwd+3, '$')) ) { + SHA1Update(&d.ctx, (void *)passwd+3, p-(passwd+3)); + p++; + } else { + p = passwd+3; /* Assume no salt */ + } + + SHA1Update(&d.ctx, (void *)entry, strlen(entry)); + SHA1Final(d.sha1, &d.ctx); + + memset(d.pwdsha1, 0, 20); + unbase64(d.pwdsha1, 20, p); + + rv = !memcmp(d.sha1, d.pwdsha1, 20); + + memset(&d, 0, sizeof d); + return rv; +} + +static int passwd_compare_md5(const char *passwd, const char *entry) +{ + const char *crypted = crypt_md5(entry, passwd+3); + int len = strlen(crypted); + + return !strncmp(crypted, passwd, len) && + (passwd[len] == '\0' || passwd[len] == '$'); +} + +static int passwd_compare_sha256(const char *passwd, const char *entry) +{ + const char *crypted = sha256_crypt(entry, passwd+3); + int len = strlen(crypted); + + return !strncmp(crypted, passwd, len) && + (passwd[len] == '\0' || passwd[len] == '$'); +} + +static int passwd_compare_sha512(const char *passwd, const char *entry) +{ + const char *crypted = sha512_crypt(entry, passwd+3); + int len = strlen(crypted); + + return !strncmp(crypted, passwd, len) && + (passwd[len] == '\0' || passwd[len] == '$'); +} + +int passwd_compare(const char *passwd, const char *entry) +{ + if ( passwd[0] != '$' || !passwd[1] || passwd[2] != '$' ) { + /* Plaintext passwd, yuck! */ + return !strcmp(entry, passwd); + } else { + switch (passwd[1]) { + case '1': + return passwd_compare_md5(passwd, entry); + case '4': + return passwd_compare_sha1(passwd, entry); + case '5': + return passwd_compare_sha256(passwd, entry); + case '6': + return passwd_compare_sha512(passwd, entry); + default: + return 0; /* Unknown encryption algorithm -> false */ + } + } +} diff --git a/com32/menu/printmsg.c b/com32/menu/printmsg.c new file mode 100644 index 0000000..cef5508 --- /dev/null +++ b/com32/menu/printmsg.c @@ -0,0 +1,119 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - 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, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __COM32__ +#include +#endif + +#include "menu.h" + +static int draw_message_file(const char *filename) +{ + FILE *f; + int ch; + enum msgname_state { + st_init, /* Base state */ + st_si_1, /* digit 1 */ + st_si_2, /* digit 2 */ + st_skipline, /* Skip until NL */ + } state = st_init; + int eof = 0; + int attr = 0; + + f = fopen(filename, "r"); + if (!f) + return -1; + + /* Clear screen, hide cursor, default attribute */ + printf("\033e\033%%@\033)0\033(B\3#%03d\033[?25l\033[2J\033[H", + message_base_color+0x07); + + while (!eof && (ch = getc(f)) != EOF) { + switch (state) { + case st_init: + switch (ch) { + case '\f': + fputs("\033[2J\033[H", stdout); + break; + case 15: /* SI */ + state = st_si_1; + break; + case 24: + state = st_skipline; + break; + case 26: + eof = 1; + break; + case '\a': + case '\n': + case '\r': + putchar(ch); + break; + default: + if (ch >= 32) + putchar(ch); + break; + } + break; + + case st_si_1: + attr = hexval(ch) << 4; + state = st_si_2; + break; + + case st_si_2: + attr |= hexval(ch); + printf("\3#%03d", attr+message_base_color); + state = st_init; + break; + + case st_skipline: + if (ch == '\n') + state = st_init; + break; + } + } + + fclose(f); + return 0; +} + +int show_message_file(const char *filename, const char *background) +{ + int rv = KEY_NONE; + const char *old_background = NULL; + + if (background) { + old_background = current_background; + set_background(background); + } + + if ( !(rv = draw_message_file(filename)) ) + rv = mygetkey(0); /* Wait for keypress */ + + if (old_background) + set_background(old_background); + + return rv; +} diff --git a/com32/menu/readconfig.c b/com32/menu/readconfig.c new file mode 100644 index 0000000..376d818 --- /dev/null +++ b/com32/menu/readconfig.c @@ -0,0 +1,1072 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * 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, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "menu.h" + +/* Empty refstring */ +const char *empty_string; + +/* Root menu, starting menu, hidden menu, and list of all menus */ +struct menu *root_menu, *start_menu, *hide_menu, *menu_list; + +/* These are global parameters regardless of which menu we're displaying */ +int shiftkey = 0; /* Only display menu if shift key pressed */ +int hiddenmenu = 0; +long long totaltimeout = 0; + +/* Keep track of global default */ +static int has_ui = 0; /* DEFAULT only counts if UI is found */ +static const char *globaldefault = NULL; +static bool menusave = false; /* True if there is any "menu save" */ + +/* Linked list of all entires, hidden or not; used by unlabel() */ +static struct menu_entry *all_entries; +static struct menu_entry **all_entries_end = &all_entries; + +static const struct messages messages[MSG_COUNT] = { + [MSG_AUTOBOOT] = { "autoboot", "Automatic boot in # second{,s}..." }, + [MSG_TAB] = { "tabmsg", "Press [Tab] to edit options" }, + [MSG_NOTAB] = { "notabmsg", "" }, + [MSG_PASSPROMPT] = { "passprompt", "Password required" }, +}; + +#define astrdup(x) ({ char *__x = (x); \ + size_t __n = strlen(__x) + 1; \ + char *__p = alloca(__n); \ + if ( __p ) memcpy(__p, __x, __n); \ + __p; }) + +/* Must match enum kernel_type */ +const char * const kernel_types[] = { + "none", + "localboot", + "kernel", + "linux", + "boot", + "bss", + "pxe", + "fdimage", + "comboot", + "com32", + "config", + NULL +}; + +/* + * Search the list of all menus for a specific label + */ +static struct menu * +find_menu(const char *label) +{ + struct menu *m; + + for (m = menu_list; m; m = m->next) { + if (!strcmp(label, m->label)) + return m; + } + + return NULL; +} + +#define MAX_LINE 4096 + +static char * +skipspace(char *p) +{ + while (*p && my_isspace(*p)) + p++; + + return p; +} + +/* Strip ^ from a string, returning a new reference to the same refstring + if none present */ +static const char *strip_caret(const char *str) +{ + const char *p, *r; + char *q; + int carets = 0; + + p = str; + for (;;) { + p = strchr(p, '^'); + if (!p) + break; + carets++; + p++; + } + + if (!carets) + return refstr_get(str); + + r = q = refstr_alloc(strlen(str)-carets); + for (p = str; *p; p++) + if (*p != '^') + *q++ = *p; + + *q = '\0'; /* refstr_alloc() already did this... */ + + return r; +} + +/* Check to see if we are at a certain keyword (case insensitive) */ +/* Returns a pointer to the first character past the keyword */ +static char * +looking_at(char *line, const char *kwd) +{ + char *p = line; + const char *q = kwd; + + while ( *p && *q && ((*p^*q) & ~0x20) == 0 ) { + p++; + q++; + } + + if ( *q ) + return NULL; /* Didn't see the keyword */ + + return my_isspace(*p) ? p : NULL; /* Must be EOL or whitespace */ +} + +static struct menu * new_menu(struct menu *parent, + struct menu_entry *parent_entry, + const char *label) +{ + struct menu *m = calloc(1, sizeof(struct menu)); + int i; + + m->label = label; + m->title = refstr_get(empty_string); + + if (parent) { + /* Submenu */ + m->parent = parent; + m->parent_entry = parent_entry; + parent_entry->action = MA_SUBMENU; + parent_entry->submenu = m; + + for (i = 0; i < MSG_COUNT; i++) + m->messages[i] = refstr_get(parent->messages[i]); + + memcpy(m->mparm, parent->mparm, sizeof m->mparm); + + m->allowedit = parent->allowedit; + m->timeout = parent->timeout; + m->save = parent->save; + + m->ontimeout = refstr_get(parent->ontimeout); + m->onerror = refstr_get(parent->onerror); + m->menu_master_passwd = refstr_get(parent->menu_master_passwd); + m->menu_background = refstr_get(parent->menu_background); + + m->color_table = copy_color_table(parent->color_table); + + for (i = 0; i < 12; i++) { + m->fkeyhelp[i].textname = refstr_get(parent->fkeyhelp[i].textname); + m->fkeyhelp[i].background = refstr_get(parent->fkeyhelp[i].background); + } + } else { + /* Root menu */ + for (i = 0; i < MSG_COUNT; i++) + m->messages[i] = refstrdup(messages[i].defmsg); + for (i = 0; i < NPARAMS; i++) + m->mparm[i] = mparm[i].value; + + m->allowedit = true; /* Allow edits of the command line */ + m->color_table = default_color_table(); + } + + m->next = menu_list; + menu_list = m; + + return m; +} + +struct labeldata { + const char *label; + const char *kernel; + enum kernel_type type; + const char *append; + const char *initrd; + const char *menulabel; + const char *passwd; + char *helptext; + unsigned int ipappend; + unsigned int menuhide; + unsigned int menudefault; + unsigned int menuseparator; + unsigned int menudisabled; + unsigned int menuindent; + enum menu_action action; + int save; + struct menu *submenu; +}; + +/* Menu currently being parsed */ +static struct menu *current_menu; + +static void +clear_label_data(struct labeldata *ld) +{ + refstr_put(ld->label); + refstr_put(ld->kernel); + refstr_put(ld->append); + refstr_put(ld->initrd); + refstr_put(ld->menulabel); + refstr_put(ld->passwd); + + memset(ld, 0, sizeof *ld); +} + +static struct menu_entry *new_entry(struct menu *m) +{ + struct menu_entry *me; + + if (m->nentries >= m->nentries_space) { + if (!m->nentries_space) + m->nentries_space = 1; + else + m->nentries_space <<= 1; + + m->menu_entries = realloc(m->menu_entries, m->nentries_space* + sizeof(struct menu_entry *)); + } + + me = calloc(1, sizeof(struct menu_entry)); + me->menu = m; + me->entry = m->nentries; + m->menu_entries[m->nentries++] = me; + *all_entries_end = me; + all_entries_end = &me->next; + + return me; +} + +static void consider_for_hotkey(struct menu *m, struct menu_entry *me) +{ + const char *p = strchr(me->displayname, '^'); + + if (me->action != MA_DISABLED) { + if ( p && p[1] ) { + unsigned char hotkey = p[1] & ~0x20; + if ( !m->menu_hotkeys[hotkey] ) { + me->hotkey = hotkey; + m->menu_hotkeys[hotkey] = me; + } + } + } +} + +static void +record(struct menu *m, struct labeldata *ld, const char *append) +{ + int i; + struct menu_entry *me; + const struct syslinux_ipappend_strings *ipappend; + + if (!ld->label) + return; /* Nothing defined */ + + /* Hidden entries are recorded on a special "hidden menu" */ + if (ld->menuhide) + m = hide_menu; + + if ( ld->label ) { + char ipoptions[4096], *ipp; + const char *a; + char *s; + + me = new_entry(m); + + me->displayname = ld->menulabel + ? refstr_get(ld->menulabel) : refstr_get(ld->label); + me->label = refstr_get(ld->label); + me->passwd = refstr_get(ld->passwd); + me->helptext = ld->helptext; + me->hotkey = 0; + me->action = ld->action ? ld->action : MA_CMD; + me->save = ld->save ? (ld->save > 0) : m->save; + + if ( ld->menuindent ) { + const char *dn; + + rsprintf(&dn, "%*s%s", ld->menuindent, "", me->displayname); + refstr_put(me->displayname); + me->displayname = dn; + } + + if ( ld->menuseparator ) { + refstr_put(me->displayname); + me->displayname = refstr_get(empty_string); + } + + if ( ld->menuseparator || ld->menudisabled ) { + me->action = MA_DISABLED; + refstr_put(me->label); + me->label = NULL; + refstr_put(me->passwd); + me->passwd = NULL; + } + + if (ld->menulabel) + consider_for_hotkey(m, me); + + switch (me->action) { + case MA_CMD: + ipp = ipoptions; + *ipp = '\0'; + + if (ld->initrd) + ipp += sprintf(ipp, " initrd=%s", ld->initrd); + + if (ld->ipappend) { + ipappend = syslinux_ipappend_strings(); + for (i = 0; i < ipappend->count; i++) { + if ( (ld->ipappend & (1U << i)) && ipappend->ptr[i] ) + ipp += sprintf(ipp, " %s", ipappend->ptr[i]); + } + } + + a = ld->append; + if ( !a ) + a = append; + if ( !a || (a[0] == '-' && !a[1]) ) + a = ""; + s = a[0] ? " " : ""; + if (ld->type == KT_KERNEL) { + rsprintf(&me->cmdline, "%s%s%s%s", + ld->kernel, s, a, ipoptions); + } else { + rsprintf(&me->cmdline, ".%s %s%s%s%s", + kernel_types[ld->type], ld->kernel, s, a, ipoptions); + } + break; + + case MA_GOTO_UNRES: + case MA_EXIT_UNRES: + me->cmdline = refstr_get(ld->kernel); + break; + + case MA_GOTO: + case MA_EXIT: + me->submenu = ld->submenu; + break; + + default: + break; + } + + if ( ld->menudefault && me->action == MA_CMD ) + m->defentry = m->nentries-1; + } + + clear_label_data(ld); +} + +static struct menu *begin_submenu(const char *tag) +{ + struct menu_entry *me; + + if (!tag[0]) + tag = NULL; + + me = new_entry(current_menu); + me->displayname = refstrdup(tag); + return new_menu(current_menu, me, refstr_get(me->displayname)); +} + +static struct menu *end_submenu(void) +{ + return current_menu->parent ? current_menu->parent : current_menu; +} + +static struct menu_entry *find_label(const char *str) +{ + const char *p; + struct menu_entry *me; + int pos; + + p = str; + while ( *p && !my_isspace(*p) ) + p++; + + /* p now points to the first byte beyond the kernel name */ + pos = p-str; + + for (me = all_entries; me; me = me->next) { + if (!strncmp(str, me->label, pos) && !me->label[pos]) + return me; + } + + return NULL; +} + +static const char *unlabel(const char *str) +{ + /* Convert a CLI-style command line to an executable command line */ + const char *p; + const char *q; + struct menu_entry *me; + int pos; + + p = str; + while ( *p && !my_isspace(*p) ) + p++; + + /* p now points to the first byte beyond the kernel name */ + pos = p-str; + + for (me = all_entries; me; me = me->next) { + if (!strncmp(str, me->label, pos) && !me->label[pos]) { + /* Found matching label */ + rsprintf(&q, "%s%s", me->cmdline, p); + refstr_put(str); + return q; + } + } + + return str; +} + +static const char * +refdup_word(char **p) +{ + char *sp = *p; + char *ep = sp; + + while (*ep && !my_isspace(*ep)) + ep++; + + *p = ep; + return refstrndup(sp, ep-sp); +} + +int my_isxdigit(char c) +{ + unsigned int uc = c; + + return (uc-'0') < 10 || + ((uc|0x20)-'a') < 6; +} + +unsigned int hexval(char c) +{ + unsigned char uc = c | 0x20; + unsigned int v; + + v = uc-'0'; + if (v < 10) + return v; + + return uc-'a'+10; +} + +unsigned int hexval2(const char *p) +{ + return (hexval(p[0]) << 4)+hexval(p[1]); +} + +uint32_t parse_argb(char **p) +{ + char *sp = *p; + char *ep; + uint32_t argb; + size_t len, dl; + + if (*sp == '#') + sp++; + + ep = sp; + + while (my_isxdigit(*ep)) + ep++; + + *p = ep; + len = ep-sp; + + switch(len) { + case 3: /* #rgb */ + argb = + 0xff000000 + + (hexval(sp[0])*0x11 << 16) + + (hexval(sp[1])*0x11 << 8) + + (hexval(sp[2])*0x11); + break; + case 4: /* #argb */ + argb = + (hexval(sp[0])*0x11 << 24) + + (hexval(sp[1])*0x11 << 16) + + (hexval(sp[2])*0x11 << 8) + + (hexval(sp[3])*0x11); + break; + case 6: /* #rrggbb */ + case 9: /* #rrrgggbbb */ + case 12: /* #rrrrggggbbbb */ + dl = len/3; + argb = + 0xff000000 + + (hexval2(sp+0) << 16) + + (hexval2(sp+dl) << 8) + + hexval2(sp+dl*2); + break; + case 8: /* #aarrggbb */ + /* #aaarrrgggbbb is indistinguishable from #rrrrggggbbbb, + assume the latter is a more common format */ + case 16: /* #aaaarrrrggggbbbb */ + dl = len/4; + argb = + (hexval2(sp+0) << 24) + + (hexval2(sp+dl) << 16) + + (hexval2(sp+dl*2) << 8) + + hexval2(sp+dl*3); + break; + default: + argb = 0xffff0000; /* Bright red (error indication) */ + break; + } + + return argb; +} + +/* + * Parser state. This is global so that including multiple + * files work as expected, which is that everything works the + * same way as if the files had been concatenated together. + */ +static const char *append = NULL; +static unsigned int ipappend = 0; +static struct labeldata ld; + +static int parse_one_config(const char *filename); + +static char *is_kernel_type(char *cmdstr, enum kernel_type *type) +{ + const char * const *p; + char *q; + enum kernel_type t = KT_NONE; + + for (p = kernel_types; *p; p++, t++) { + if ((q = looking_at(cmdstr, *p))) { + *type = t; + return q; + } + } + + return NULL; +} + +static char *is_message_name(char *cmdstr, enum message_number *msgnr) +{ + char *q; + enum message_number i; + + for (i = 0; i < MSG_COUNT; i++) { + if ((q = looking_at(cmdstr, messages[i].name))) { + *msgnr = i; + return q; + } + } + + return NULL; +} + +static char *is_fkey(char *cmdstr, int *fkeyno) +{ + char *q; + int no; + + if ((cmdstr[0]|0x20) != 'f') + return NULL; + + no = strtoul(cmdstr+1, &q, 10); + if (!my_isspace(*q)) + return NULL; + + if (no < 0 || no > 12) + return NULL; + + *fkeyno = (no == 0) ? 10 : no-1; + return q; +} + +static void parse_config_file(FILE *f) +{ + char line[MAX_LINE], *p, *ep, ch; + enum kernel_type type; + enum message_number msgnr; + int fkeyno; + struct menu *m = current_menu; + + while ( fgets(line, sizeof line, f) ) { + p = strchr(line, '\r'); + if ( p ) + *p = '\0'; + p = strchr(line, '\n'); + if ( p ) + *p = '\0'; + + p = skipspace(line); + + if ( looking_at(p, "menu") ) { + p = skipspace(p+4); + + if ( looking_at(p, "label") ) { + if ( ld.label ) { + refstr_put(ld.menulabel); + ld.menulabel = refstrdup(skipspace(p+5)); + } else if ( m->parent_entry ) { + refstr_put(m->parent_entry->displayname); + m->parent_entry->displayname = refstrdup(skipspace(p+5)); + consider_for_hotkey(m->parent, m->parent_entry); + if (!m->title[0]) { + /* MENU LABEL -> MENU TITLE on submenu */ + refstr_put(m->title); + m->title = strip_caret(m->parent_entry->displayname); + } + } + } else if ( looking_at(p, "title") ) { + refstr_put(m->title); + m->title = refstrdup(skipspace(p+5)); + if (m->parent_entry) { + /* MENU TITLE -> MENU LABEL on submenu */ + if (m->parent_entry->displayname == m->label) { + refstr_put(m->parent_entry->displayname); + m->parent_entry->displayname = refstr_get(m->title); + } + } + } else if ( looking_at(p, "default") ) { + if (ld.label) { + ld.menudefault = 1; + } else if (m->parent_entry) { + m->parent->defentry = m->parent_entry->entry; + } + } else if ( looking_at(p, "hide") ) { + ld.menuhide = 1; + } else if ( looking_at(p, "passwd") ) { + if ( ld.label ) { + refstr_put(ld.passwd); + ld.passwd = refstrdup(skipspace(p+6)); + } else if ( m->parent_entry ) { + refstr_put(m->parent_entry->passwd); + m->parent_entry->passwd = refstrdup(skipspace(p+6)); + } + } else if ( looking_at(p, "shiftkey") ) { + shiftkey = 1; + } else if ( looking_at(p, "save") ) { + menusave = true; + if (ld.label) + ld.save = 1; + else + m->save = true; + } else if ( looking_at(p, "nosave") ) { + if (ld.label) + ld.save = -1; + else + m->save = false; + } else if ( looking_at(p, "onerror") ) { + refstr_put(m->onerror); + m->onerror = refstrdup(skipspace(p+7)); + } else if ( looking_at(p, "master") ) { + p = skipspace(p+6); + if ( looking_at(p, "passwd") ) { + refstr_put(m->menu_master_passwd); + m->menu_master_passwd = refstrdup(skipspace(p+6)); + } + } else if ( (ep = looking_at(p, "include")) ) { + goto do_include; + } else if ( (ep = looking_at(p, "background")) ) { + p = skipspace(ep); + refstr_put(m->menu_background); + m->menu_background = refdup_word(&p); + } else if ( (ep = looking_at(p, "hidden")) ) { + hiddenmenu = 1; + } else if ( (ep = is_message_name(p, &msgnr)) ) { + refstr_put(m->messages[msgnr]); + m->messages[msgnr] = refstrdup(skipspace(ep)); + } else if ((ep = looking_at(p, "color")) || + (ep = looking_at(p, "colour"))) { + int i; + struct color_table *cptr; + p = skipspace(ep); + cptr = m->color_table; + for ( i = 0; i < menu_color_table_size; i++ ) { + if ( (ep = looking_at(p, cptr->name)) ) { + p = skipspace(ep); + if (*p) { + if (looking_at(p, "*")) { + p++; + } else { + refstr_put(cptr->ansi); + cptr->ansi = refdup_word(&p); + } + + p = skipspace(p); + if (*p) { + if (looking_at(p, "*")) + p++; + else + cptr->argb_fg = parse_argb(&p); + + p = skipspace(p); + if (*p) { + if (looking_at(p, "*")) + p++; + else + cptr->argb_bg = parse_argb(&p); + + /* Parse a shadow mode */ + p = skipspace(p); + ch = *p | 0x20; + if (ch == 'n') /* none */ + cptr->shadow = SHADOW_NONE; + else if (ch == 's') /* std, standard */ + cptr->shadow = SHADOW_NORMAL; + else if (ch == 'a') /* all */ + cptr->shadow = SHADOW_ALL; + else if (ch == 'r') /* rev, reverse */ + cptr->shadow = SHADOW_REVERSE; + } + } + } + break; + } + cptr++; + } + } else if ((ep = looking_at(p, "msgcolor")) || + (ep = looking_at(p, "msgcolour"))) { + unsigned int fg_mask = MSG_COLORS_DEF_FG; + unsigned int bg_mask = MSG_COLORS_DEF_BG; + enum color_table_shadow shadow = MSG_COLORS_DEF_SHADOW; + + p = skipspace(ep); + if (*p) { + if (!looking_at(p, "*")) + fg_mask = parse_argb(&p); + + p = skipspace(p); + if (*p) { + if (!looking_at(p, "*")) + bg_mask = parse_argb(&p); + + p = skipspace(p); + switch (*p | 0x20) { + case 'n': + shadow = SHADOW_NONE; + break; + case 's': + shadow = SHADOW_NORMAL; + break; + case 'a': + shadow = SHADOW_ALL; + break; + case 'r': + shadow = SHADOW_REVERSE; + break; + default: + /* go with default */ + break; + } + } + } + set_msg_colors_global(m->color_table, fg_mask, bg_mask, shadow); + } else if ( looking_at(p, "separator") ) { + record(m, &ld, append); + ld.label = refstr_get(empty_string); + ld.menuseparator = 1; + record(m, &ld, append); + } else if ( looking_at(p, "disable") || + looking_at(p, "disabled")) { + ld.menudisabled = 1; + } else if ( looking_at(p, "indent") ) { + ld.menuindent = atoi(skipspace(p+6)); + } else if ( looking_at(p, "begin") ) { + record(m, &ld, append); + m = current_menu = begin_submenu(skipspace(p+5)); + } else if ( looking_at(p, "end") ) { + record(m, &ld, append); + m = current_menu = end_submenu(); + } else if ( looking_at(p, "quit") ) { + if (ld.label) + ld.action = MA_QUIT; + } else if ( looking_at(p, "goto") ) { + if (ld.label) { + ld.action = MA_GOTO_UNRES; + refstr_put(ld.kernel); + ld.kernel = refstrdup(skipspace(p+4)); + } + } else if ( looking_at(p, "exit") ) { + p = skipspace(p+4); + if (ld.label && m->parent) { + if (*p) { + /* This is really just a goto, except for the marker */ + ld.action = MA_EXIT_UNRES; + refstr_put(ld.kernel); + ld.kernel = refstrdup(p); + } else { + ld.action = MA_EXIT; + ld.submenu = m->parent; + } + } + } else if ( looking_at(p, "start") ) { + start_menu = m; + } else { + /* Unknown, check for layout parameters */ + enum parameter_number mp; + for (mp = 0; mp < NPARAMS; mp++) { + if ( (ep = looking_at(p, mparm[mp].name)) ) { + m->mparm[mp] = atoi(skipspace(ep)); + break; + } + } + } + } else if ( looking_at(p, "text") ) { + enum text_cmd { + TEXT_UNKNOWN, + TEXT_HELP + } cmd = TEXT_UNKNOWN; + int len = ld.helptext ? strlen(ld.helptext) : 0; + int xlen; + + p = skipspace(p+4); + + if (looking_at(p, "help")) + cmd = TEXT_HELP; + + while ( fgets(line, sizeof line, f) ) { + p = skipspace(line); + if (looking_at(p, "endtext")) + break; + + xlen = strlen(line); + + switch (cmd) { + case TEXT_UNKNOWN: + break; + case TEXT_HELP: + ld.helptext = realloc(ld.helptext, len+xlen+1); + memcpy(ld.helptext+len, line, xlen+1); + len += xlen; + break; + } + } + } else if ( (ep = is_fkey(p, &fkeyno)) ) { + p = skipspace(ep); + if (m->fkeyhelp[fkeyno].textname) { + refstr_put(m->fkeyhelp[fkeyno].textname); + m->fkeyhelp[fkeyno].textname = NULL; + } + if (m->fkeyhelp[fkeyno].background) { + refstr_put(m->fkeyhelp[fkeyno].background); + m->fkeyhelp[fkeyno].background = NULL; + } + + refstr_put(m->fkeyhelp[fkeyno].textname); + m->fkeyhelp[fkeyno].textname = refdup_word(&p); + if (*p) { + p = skipspace(p); + m->fkeyhelp[fkeyno].background = refdup_word(&p); + } + } else if ( (ep = looking_at(p, "include")) ) { + do_include: + { + const char *file; + p = skipspace(ep); + file = refdup_word(&p); + p = skipspace(p); + if (*p) { + record(m, &ld, append); + m = current_menu = begin_submenu(p); + parse_one_config(file); + record(m, &ld, append); + m = current_menu = end_submenu(); + } else { + parse_one_config(file); + } + refstr_put(file); + } + } else if ( looking_at(p, "append") ) { + const char *a = refstrdup(skipspace(p+6)); + if ( ld.label ) { + refstr_put(ld.append); + ld.append = a; + } else { + refstr_put(append); + append = a; + } + } else if ( looking_at(p, "initrd") ) { + const char *a = refstrdup(skipspace(p+6)); + if ( ld.label ) { + refstr_put(ld.initrd); + ld.initrd = a; + } else { + /* Ignore */ + } + } else if ( looking_at(p, "label") ) { + p = skipspace(p+5); + record(m, &ld, append); + ld.label = refstrdup(p); + ld.kernel = refstrdup(p); + ld.type = KT_KERNEL; + ld.passwd = NULL; + ld.append = NULL; + ld.initrd = NULL; + ld.menulabel = NULL; + ld.helptext = NULL; + ld.ipappend = ipappend; + ld.menudefault = ld.menuhide = ld.menuseparator = + ld.menudisabled = ld.menuindent = 0; + } else if ( (ep = is_kernel_type(p, &type)) ) { + if ( ld.label ) { + refstr_put(ld.kernel); + ld.kernel = refstrdup(skipspace(ep)); + ld.type = type; + } + } else if ( looking_at(p, "timeout") ) { + m->timeout = (atoi(skipspace(p+7))*CLK_TCK+9)/10; + } else if ( looking_at(p, "totaltimeout") ) { + totaltimeout = (atoll(skipspace(p+13))*CLK_TCK+9)/10; + } else if ( looking_at(p, "ontimeout") ) { + m->ontimeout = refstrdup(skipspace(p+9)); + } else if ( looking_at(p, "allowoptions") ) { + m->allowedit = !!atoi(skipspace(p+12)); + } else if ( looking_at(p, "ipappend") ) { + if (ld.label) + ld.ipappend = atoi(skipspace(p+8)); + else + ipappend = atoi(skipspace(p+8)); + } else if ( looking_at(p, "default") ) { + refstr_put(globaldefault); + globaldefault = refstrdup(skipspace(p+7)); + } else if ( looking_at(p, "ui") ) { + has_ui = 1; + } + } +} + +static int parse_one_config(const char *filename) +{ + FILE *f; + + if (!strcmp(filename, "~")) + filename = syslinux_config_file(); + + f = fopen(filename, "r"); + if ( !f ) + return -1; + + parse_config_file(f); + fclose(f); + + return 0; +} + +static void resolve_gotos(void) +{ + struct menu_entry *me; + struct menu *m; + + for (me = all_entries; me; me = me->next) { + if (me->action == MA_GOTO_UNRES || + me->action == MA_EXIT_UNRES) { + m = find_menu(me->cmdline); + refstr_put(me->cmdline); + me->cmdline = NULL; + if (m) { + me->submenu = m; + me->action--; /* Drop the _UNRES */ + } else { + me->action = MA_DISABLED; + } + } + } +} + +void parse_configs(char **argv) +{ + const char *filename; + struct menu *m; + struct menu_entry *me; + + empty_string = refstrdup(""); + + /* Initialize defaults for the root and hidden menus */ + hide_menu = new_menu(NULL, NULL, refstrdup(".hidden")); + root_menu = new_menu(NULL, NULL, refstrdup(".top")); + start_menu = root_menu; + + /* Other initialization */ + memset(&ld, 0, sizeof(struct labeldata)); + + /* Actually process the files */ + current_menu = root_menu; + if ( !*argv ) { + parse_one_config("~"); + } else { + while ( (filename = *argv++) ) + parse_one_config(filename); + } + + /* On final EOF process the last label statement */ + record(current_menu, &ld, append); + + /* Common postprocessing */ + resolve_gotos(); + + /* Handle global default */ + if (has_ui && globaldefault) { + me = find_label(globaldefault); + if (me && me->menu != hide_menu) { + me->menu->defentry = me->entry; + start_menu = me->menu; + } + } + + /* If "menu save" is active, let the ADV override the global default */ + if (menusave) { + size_t len; + const char *lbl = syslinux_getadv(ADV_MENUSAVE, &len); + char *lstr; + if (lbl && len) { + lstr = refstr_alloc(len); + memcpy(lstr, lbl, len); /* refstr_alloc() adds the final null */ + me = find_label(lstr); + if (me && me->menu != hide_menu) { + me->menu->defentry = me->entry; + start_menu = me->menu; + } + refstr_put(lstr); + } + } + + /* Final per-menu initialization, with all labels known */ + for (m = menu_list; m; m = m->next) { + m->curentry = m->defentry; /* All menus start at their defaults */ + + if ( m->ontimeout ) + m->ontimeout = unlabel(m->ontimeout); + if ( m->onerror ) + m->onerror = unlabel(m->onerror); + } +} diff --git a/com32/menu/refstr.c b/com32/menu/refstr.c new file mode 100644 index 0000000..f727abc --- /dev/null +++ b/com32/menu/refstr.c @@ -0,0 +1,105 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008 H. Peter Anvin - 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, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * refstr.c + * + * Simple reference-counted strings + */ + +#include +#include +#include +#include "refstr.h" + +/* Allocate space for a refstring of len bytes, plus final null */ +/* The final null is inserted in the string; the rest is uninitialized. */ +char *refstr_alloc(size_t len) +{ + char *r = malloc(sizeof(unsigned int)+len+1); + if (!r) + return NULL; + *(unsigned int *)r = 1; + r += sizeof(unsigned int); + r[len] = '\0'; + return r; +} + +const char *refstrndup(const char *str, size_t len) +{ + char *r; + + if (!str) + return NULL; + + len = strnlen(str, len); + r = refstr_alloc(len); + if (r) + memcpy(r, str, len); + return r; +} + +const char *refstrdup(const char *str) +{ + char *r; + size_t len; + + if (!str) + return NULL; + + len = strlen(str); + r = refstr_alloc(len); + if (r) + memcpy(r, str, len); + return r; +} + +int vrsprintf(const char **bufp, const char *fmt, va_list ap) +{ + va_list ap1; + int len; + char *p; + + va_copy(ap1, ap); + len = vsnprintf(NULL, 0, fmt, ap1); + va_end(ap1); + + *bufp = p = refstr_alloc(len); + if ( !p ) + return -1; + + return vsnprintf(p, len+1, fmt, ap); +} + +int rsprintf(const char **bufp, const char *fmt, ...) +{ + int rv; + va_list ap; + + va_start(ap, fmt); + rv = vrsprintf(bufp, fmt, ap); + va_end(ap); + + return rv; +} + +void refstr_put(const char *r) +{ + unsigned int *ref; + + if (r) { + ref = (unsigned int *)r - 1; + + if (!--*ref) + free(ref); + } +} diff --git a/com32/menu/refstr.h b/com32/menu/refstr.h new file mode 100644 index 0000000..f51777d --- /dev/null +++ b/com32/menu/refstr.h @@ -0,0 +1,40 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008 H. Peter Anvin - 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, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * refstr.h + * + * Simple reference-counted strings + */ + +#ifndef REFSTR_H +#define REFSTR_H + +#include +#include + +static inline __attribute__((always_inline)) +const char *refstr_get(const char *r) +{ + if (r) + ((unsigned int *)r)[-1]++; + return r; +} + +void refstr_put(const char *); +char *refstr_alloc(size_t); +const char *refstrdup(const char *); +const char *refstrndup(const char *, size_t); +int rsprintf(const char **, const char *, ...); +int vrsprintf(const char **, const char *, va_list); + +#endif diff --git a/com32/menu/vesamenu.c b/com32/menu/vesamenu.c new file mode 100644 index 0000000..4dd4586 --- /dev/null +++ b/com32/menu/vesamenu.c @@ -0,0 +1,42 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - 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, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * vesamenu.c + * + * Simple menu system which displays a list and allows the user to select + * a command line and/or edit it. + * + * VESA graphics version. + */ + +#include +#include +#include + +#include "menu.h" + +int draw_background(const char *what) +{ + if (!what) + return vesacon_default_background(); + else if (what[0] == '#') + return vesacon_set_background(parse_argb((char **)&what)); + else + return vesacon_load_background(what); +} + +int main(int argc, char *argv[]) +{ + openconsole(&dev_rawcon_r, &dev_vesaserial_w); + return menu_main(argc, argv); +} diff --git a/com32/menu/vesamenu.c32 b/com32/menu/vesamenu.c32 new file mode 100755 index 0000000000000000000000000000000000000000..d9884770dc01b8db24022c0e25cd337d218b2616 GIT binary patch literal 147728 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 [] [options] + * chain fd [options] + * chain mbr: [] [options] + * chain boot [] [options] + * + * ... e.g. "chain hd0 1" will boot the first partition on the first hard + * disk. + * + * + * The mbr: syntax means search all the hard disks until one with a + * specific MBR serial number (bytes 440-443) is found. + * + * Partitions 1-4 are primary, 5+ logical, 0 = boot MBR (default.) + * + * Options: + * + * file=: + * loads the file **from the SYSLINUX filesystem** + * instead of loading the boot sector. + * + * seg=: + * loads at and jumps to :0000 instead of 0000:7C00. + * + * ntldr=: + * equivalent to -seg 0x2000 -file , used with WinNT's loaders + * + * freedos=: + * equivalent to -seg 0x60 -file , used with FreeDOS kernel.sys. + * + * msdos= + * pcdos= + * equivalent to -seg 0x70 -file , used with DOS' io.sys. + * + * swap: + * if the disk is not fd0/hd0, install a BIOS stub which swaps + * the drive numbers. + * + * hide: + * change type of primary partitions with IDs 01, 04, 06, 07, + * 0b, 0c, or 0e to 1x, except for the selected partition, which + * is converted the other way. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SECTOR 512 /* bytes/sector */ + +static struct options { + const char *loadfile; + uint16_t keeppxe; + uint16_t seg; + bool swap; + bool hide; +} opt; + +static inline void error(const char *msg) +{ + fputs(msg, stderr); +} + +/* + * Call int 13h, but with retry on failure. Especially floppies need this. + */ +static int int13_retry(const com32sys_t *inreg, com32sys_t *outreg) +{ + int retry = 6; /* Number of retries */ + com32sys_t tmpregs; + + if ( !outreg ) outreg = &tmpregs; + + while ( retry-- ) { + __intcall(0x13, inreg, outreg); + if ( !(outreg->eflags.l & EFLAGS_CF) ) + return 0; /* CF=0, OK */ + } + + return -1; /* Error */ +} + +/* + * Query disk parameters and EBIOS availability for a particular disk. + */ +struct diskinfo { + int disk; + int ebios; /* EBIOS supported on this disk */ + int cbios; /* CHS geometry is valid */ + int head; + int sect; +} disk_info; + +static int get_disk_params(int disk) +{ + static com32sys_t getparm, parm, getebios, ebios; + + disk_info.disk = disk; + disk_info.ebios = disk_info.cbios = 0; + + /* Get EBIOS support */ + getebios.eax.w[0] = 0x4100; + getebios.ebx.w[0] = 0x55aa; + getebios.edx.b[0] = disk; + getebios.eflags.b[0] = 0x3; /* CF set */ + + __intcall(0x13, &getebios, &ebios); + + if ( !(ebios.eflags.l & EFLAGS_CF) && + ebios.ebx.w[0] == 0xaa55 && + (ebios.ecx.b[0] & 1) ) { + disk_info.ebios = 1; + } + + /* Get disk parameters -- really only useful for + hard disks, but if we have a partitioned floppy + it's actually our best chance... */ + getparm.eax.b[1] = 0x08; + getparm.edx.b[0] = disk; + + __intcall(0x13, &getparm, &parm); + + if ( parm.eflags.l & EFLAGS_CF ) + return disk_info.ebios ? 0 : -1; + + disk_info.head = parm.edx.b[1]+1; + disk_info.sect = parm.ecx.b[0] & 0x3f; + if ( disk_info.sect == 0 ) { + disk_info.sect = 1; + } else { + disk_info.cbios = 1; /* Valid geometry */ + } + + return 0; +} + +/* + * Get a disk block and return a malloc'd buffer. + * Uses the disk number and information from disk_info. + */ +struct ebios_dapa { + uint16_t len; + uint16_t count; + uint16_t off; + uint16_t seg; + uint64_t lba; +}; + +static void *read_sector(unsigned int lba) +{ + com32sys_t inreg; + struct ebios_dapa *dapa = __com32.cs_bounce; + void *buf = (char *)__com32.cs_bounce + SECTOR; + void *data; + + memset(&inreg, 0, sizeof inreg); + + if ( disk_info.ebios ) { + dapa->len = sizeof(*dapa); + dapa->count = 1; /* 1 sector */ + dapa->off = OFFS(buf); + dapa->seg = SEG(buf); + dapa->lba = lba; + + inreg.esi.w[0] = OFFS(dapa); + inreg.ds = SEG(dapa); + inreg.edx.b[0] = disk_info.disk; + inreg.eax.b[1] = 0x42; /* Extended read */ + } else { + unsigned int c, h, s, t; + + if ( !disk_info.cbios ) { + /* We failed to get the geometry */ + + if ( lba ) + return NULL; /* Can only read MBR */ + + s = 1; h = 0; c = 0; + } else { + s = (lba % disk_info.sect) + 1; + t = lba / disk_info.sect; /* Track = head*cyl */ + h = t % disk_info.head; + c = t / disk_info.head; + } + + if ( s > 63 || h > 256 || c > 1023 ) + return NULL; + + inreg.eax.w[0] = 0x0201; /* Read one sector */ + inreg.ecx.b[1] = c & 0xff; + inreg.ecx.b[0] = s + (c >> 6); + inreg.edx.b[1] = h; + inreg.edx.b[0] = disk_info.disk; + inreg.ebx.w[0] = OFFS(buf); + inreg.es = SEG(buf); + } + + if (int13_retry(&inreg, NULL)) + return NULL; + + data = malloc(SECTOR); + if (data) + memcpy(data, buf, SECTOR); + return data; +} + +static int write_sector(unsigned int lba, const void *data) +{ + com32sys_t inreg; + struct ebios_dapa *dapa = __com32.cs_bounce; + void *buf = (char *)__com32.cs_bounce + SECTOR; + + memcpy(buf, data, SECTOR); + memset(&inreg, 0, sizeof inreg); + + if ( disk_info.ebios ) { + dapa->len = sizeof(*dapa); + dapa->count = 1; /* 1 sector */ + dapa->off = OFFS(buf); + dapa->seg = SEG(buf); + dapa->lba = lba; + + inreg.esi.w[0] = OFFS(dapa); + inreg.ds = SEG(dapa); + inreg.edx.b[0] = disk_info.disk; + inreg.eax.w[0] = 0x4300; /* Extended write */ + } else { + unsigned int c, h, s, t; + + if ( !disk_info.cbios ) { + /* We failed to get the geometry */ + + if ( lba ) + return -1; /* Can only write MBR */ + + s = 1; h = 0; c = 0; + } else { + s = (lba % disk_info.sect) + 1; + t = lba / disk_info.sect; /* Track = head*cyl */ + h = t % disk_info.head; + c = t / disk_info.head; + } + + if ( s > 63 || h > 256 || c > 1023 ) + return -1; + + inreg.eax.w[0] = 0x0301; /* Write one sector */ + inreg.ecx.b[1] = c & 0xff; + inreg.ecx.b[0] = s + (c >> 6); + inreg.edx.b[1] = h; + inreg.edx.b[0] = disk_info.disk; + inreg.ebx.w[0] = OFFS(buf); + inreg.es = SEG(buf); + } + + if (int13_retry(&inreg, NULL)) + return -1; + + return 0; /* ok */ +} + +static int write_verify_sector(unsigned int lba, const void *buf) +{ + char *rb; + int rv; + + rv = write_sector(lba, buf); + if (rv) + return rv; /* Write failure */ + rb = read_sector(lba); + if (!rb) + return -1; /* Readback failure */ + rv = memcmp(buf, rb, SECTOR); + free(rb); + return rv ? -1 : 0; +} + +/* Search for a specific drive, based on the MBR signature; bytes + 440-443. */ +static int find_disk(uint32_t mbr_sig) +{ + int drive; + bool is_me; + char *buf; + + for (drive = 0x80; drive <= 0xff; drive++) { + if (get_disk_params(drive)) + continue; /* Drive doesn't exist */ + if (!(buf = read_sector(0))) + continue; /* Cannot read sector */ + is_me = (*(uint32_t *)((char *)buf + 440) == mbr_sig); + free(buf); + if (is_me) + return drive; + } + return -1; +} + +/* A DOS partition table entry */ +struct part_entry { + uint8_t active_flag; /* 0x80 if "active" */ + uint8_t start_head; + uint8_t start_sect; + uint8_t start_cyl; + uint8_t ostype; + uint8_t end_head; + uint8_t end_sect; + uint8_t end_cyl; + uint32_t start_lba; + uint32_t length; +} __attribute__((packed)); + + +/* Search for a logical partition. Logical partitions are actually implemented + as recursive partition tables; theoretically they're supposed to form a + linked list, but other structures have been seen. + + To make things extra confusing: data partition offsets are relative to where + the data partition record is stored, whereas extended partition offsets + are relative to the beginning of the extended partition all the way back + at the MBR... but still not absolute! */ + +int nextpart; /* Number of the next logical partition */ + +static struct part_entry * +find_logical_partition(int whichpart, char *table, struct part_entry *self, + struct part_entry *root) +{ + static struct part_entry ltab_entry; + struct part_entry *ptab = (struct part_entry *)(table + 0x1be); + struct part_entry *found; + char *sector; + + int i; + + if ( *(uint16_t *)(table + 0x1fe) != 0xaa55 ) + return NULL; /* Signature missing */ + + /* We are assumed to already having enumerated all the data partitions + in this table if this is the MBR. For MBR, self == NULL. */ + + if ( self ) { + /* Scan the data partitions. */ + + for ( i = 0 ; i < 4 ; i++ ) { + if ( ptab[i].ostype == 0x00 || ptab[i].ostype == 0x05 || + ptab[i].ostype == 0x0f || ptab[i].ostype == 0x85 ) + continue; /* Skip empty or extended partitions */ + + if ( !ptab[i].length ) + continue; + + /* Adjust the offset to account for the extended partition itself */ + ptab[i].start_lba += self->start_lba; + + /* Sanity check entry: must not extend outside the extended partition. + This is necessary since some OSes put crap in some entries. */ + if ( ptab[i].start_lba + ptab[i].length <= self->start_lba || + ptab[i].start_lba >= self->start_lba + self->length ) + continue; + + /* OK, it's a data partition. Is it the one we're looking for? */ + if ( nextpart++ == whichpart ) { + memcpy(<ab_entry, &ptab[i], sizeof ltab_entry); + return <ab_entry; + } + } + } + + /* Scan the extended partitions. */ + for ( i = 0 ; i < 4 ; i++ ) { + if ( ptab[i].ostype != 0x05 && + ptab[i].ostype != 0x0f && ptab[i].ostype != 0x85 ) + continue; /* Skip empty or data partitions */ + + if ( !ptab[i].length ) + continue; + + /* Adjust the offset to account for the extended partition itself */ + if ( root ) + ptab[i].start_lba += root->start_lba; + + /* Sanity check entry: must not extend outside the extended partition. + This is necessary since some OSes put crap in some entries. */ + if ( root ) + if ( ptab[i].start_lba + ptab[i].length <= root->start_lba || + ptab[i].start_lba >= root->start_lba + root->length ) + continue; + + /* Process this partition */ + if ( !(sector = read_sector(ptab[i].start_lba)) ) + continue; /* Read error, must be invalid */ + + found = find_logical_partition(whichpart, sector, &ptab[i], + root ? root : &ptab[i]); + free(sector); + if (found) + return found; + } + + /* If we get here, there ain't nothing... */ + return NULL; +} + +static void do_boot(void *boot_sector, size_t boot_size, + struct syslinux_rm_regs *regs) +{ + uint16_t * const bios_fbm = (uint16_t *)0x413; + addr_t dosmem = *bios_fbm << 10; /* Technically a low bound */ + struct syslinux_memmap *mmap; + struct syslinux_movelist *mlist = NULL; + addr_t endimage; + uint8_t driveno = regs->edx.b[0]; + uint8_t swapdrive = driveno & 0x80; + int i; + addr_t loadbase = opt.seg ? (opt.seg << 4) : 0x7c00; + + mmap = syslinux_memory_map(); + + if (!mmap) { + error("Cannot read system memory map"); + return; + } + + /* Nothing below 0x7c00, much simpler... */ + + if (boot_size >= dosmem - loadbase) + goto too_big; + + endimage = loadbase + boot_size; + + if (syslinux_add_movelist(&mlist, loadbase, (addr_t)boot_sector, boot_size)) + goto enomem; + + if (opt.swap && driveno != swapdrive) { + static const uint8_t swapstub_master[] = { + /* The actual swap code */ + 0x53, /* 00: push bx */ + 0x0f,0xb6,0xda, /* 01: movzx bx,dl */ + 0x2e,0x8a,0x57,0x60, /* 04: mov dl,[cs:bx+0x60] */ + 0x5b, /* 08: pop bx */ + 0xea,0,0,0,0, /* 09: jmp far 0:0 */ + 0x90,0x90, /* 0E: nop; nop */ + /* Code to install this in the right location */ + /* Entry with DS = CS; ES = SI = 0; CX = 256 */ + 0x26,0x66,0x8b,0x7c,0x4c, /* 10: mov edi,[es:si+4*0x13] */ + 0x66,0x89,0x3e,0x0a,0x00, /* 15: mov [0x0A],edi */ + 0x26,0x8b,0x3e,0x13,0x04, /* 1A: mov di,[es:0x413] */ + 0x4f, /* 1F: dec di */ + 0x26,0x89,0x3e,0x13,0x04, /* 20: mov [es:0x413],di */ + 0x66,0xc1,0xe7,0x16, /* 25: shl edi,16+6 */ + 0x26,0x66,0x89,0x7c,0x4c, /* 29: mov [es:si+4*0x13],edi */ + 0x66,0xc1,0xef,0x10, /* 2E: shr edi,16 */ + 0x8e,0xc7, /* 32: mov es,di */ + 0x31,0xff, /* 34: xor di,di */ + 0xf3,0x66,0xa5, /* 36: rep movsd */ + 0xbe,0,0, /* 39: mov si,0 */ + 0xbf,0,0, /* 3C: mov di,0 */ + 0x8e,0xde, /* 3F: mov ds,si */ + 0x8e,0xc7, /* 41: mov es,di */ + 0x66,0xb9,0,0,0,0, /* 43: mov ecx,0 */ + 0x66,0xbe,0,0,0,0, /* 49: mov esi,0 */ + 0x66,0xbf,0,0,0,0, /* 4F: mov edi,0 */ + 0xea,0,0,0,0, /* 55: jmp 0:0 */ + /* pad out to segment boundary */ + 0x90,0x90, /* 5A: ... */ + 0x90,0x90,0x90,0x90, /* 5C: ... */ + }; + static uint8_t swapstub[1024]; + uint8_t *p; + + /* Note: we can't rely on either INT 13h nor the dosmem + vector to be correct at this stage, so we have to use an + installer stub to put things in the right place. + Round the installer location to a 1K boundary so the only + possible overlap is the identity mapping. */ + endimage = (endimage + 1023) & ~1023; + + /* Create swap stub */ + memcpy(swapstub, swapstub_master, sizeof swapstub_master); + *(uint16_t *)&swapstub[0x3a] = regs->ds; + *(uint16_t *)&swapstub[0x3d] = regs->es; + *(uint32_t *)&swapstub[0x45] = regs->ecx.l; + *(uint32_t *)&swapstub[0x4b] = regs->esi.l; + *(uint32_t *)&swapstub[0x51] = regs->edi.l; + *(uint16_t *)&swapstub[0x56] = regs->ip; + *(uint16_t *)&swapstub[0x58] = regs->cs; + p = &swapstub[sizeof swapstub_master]; + + /* Mapping table; start out with identity mapping everything */ + for (i = 0; i < 256; i++) + p[i] = i; + + /* And the actual swap */ + p[driveno] = swapdrive; + p[swapdrive] = driveno; + + /* Adjust registers */ + regs->ds = regs->cs = endimage >> 4; + regs->es = regs->esi.l = 0; + regs->ecx.l = sizeof swapstub >> 2; + regs->ip = 0x10; /* Installer offset */ + regs->ebx.b[0] = regs->edx.b[0] = swapdrive; + + if (syslinux_add_movelist(&mlist, endimage, (addr_t)swapstub, + sizeof swapstub)) + goto enomem; + + endimage += sizeof swapstub; + } + + /* Tell the shuffler not to muck with this area... */ + syslinux_add_memmap(&mmap, endimage, 0xa0000-endimage, SMT_RESERVED); + + fputs("Booting...\n", stdout); + syslinux_shuffle_boot_rm(mlist, mmap, opt.keeppxe, regs); + error("Chainboot failed!\n"); + return; + +too_big: + error("Loader file too large"); + return; + +enomem: + error("Out of memory"); + return; +} + +static int hide_unhide(char *mbr, int part) +{ + int i; + struct part_entry *pt; + const uint16_t mask = (1 << 0x01)|(1 << 0x04)|(1 << 0x06)|(1 << 0x07)| + (1 << 0x0b)|(1 << 0x0c)|(1 << 0x0e); + uint8_t t; + bool write_back = false; + + for (i = 1; i <= 4; i++) { + pt = (struct part_entry *)&mbr[0x1be + 16*(i-1)]; + t = pt->ostype; + if ((t <= 0x1f) && ((mask >> (t & ~0x10)) & 1)) { + /* It's a hideable partition type */ + if (i == part) + t &= ~0x10; /* unhide */ + else + t |= 0x10; /* hide */ + } + if (t != pt->ostype) { + write_back = true; + pt->ostype = t; + } + } + + if (write_back) + return write_verify_sector(0, mbr); + + return 0; /* ok */ +} + +int main(int argc, char *argv[]) +{ + char *mbr, *p; + void *boot_sector = NULL; + struct part_entry *partinfo; + struct syslinux_rm_regs regs; + char *drivename, *partition; + int hd, drive, whichpart; + int i; + size_t boot_size = SECTOR; + + openconsole(&dev_null_r, &dev_stdcon_w); + + drivename = "boot"; + partition = NULL; + + /* Prepare the register set */ + memset(®s, 0, sizeof regs); + + for (i = 1; i < argc; i++) { + if (!strncmp(argv[i], "file=", 5)) { + opt.loadfile = argv[i]+5; + } else if (!strncmp(argv[i], "seg=", 4)) { + uint32_t segval = strtoul(argv[i]+4, NULL, 0); + if (segval < 0x50 || segval > 0x9f000) { + error("Invalid segment"); + goto bail; + } + opt.seg = segval; + } else if (!strncmp(argv[i], "ntldr=", 6)) { + opt.seg = 0x2000; /* NTLDR wants this address */ + opt.loadfile = argv[i]+6; + } else if (!strncmp(argv[i], "freedos=", 8)) { + opt.seg = 0x60; /* FREEDOS wants this address */ + opt.loadfile = argv[i]+8; + } else if (!strncmp(argv[i], "msdos=", 6) || + !strncmp(argv[i], "pcdos=", 6)) { + opt.seg = 0x70; /* MS-DOS 2.0+ wants this address */ + opt.loadfile = argv[i]+6; + } else if (!strcmp(argv[i], "swap")) { + opt.swap = true; + } else if (!strcmp(argv[i], "hide")) { + opt.hide = true; + } else if (!strcmp(argv[i], "keeppxe")) { + opt.keeppxe = 3; + } else if (((argv[i][0] == 'h' || argv[i][0] == 'f') && argv[i][1] == 'd') + || !strncmp(argv[i], "mbr:", 4) + || !strncmp(argv[i], "mbr=", 4) + || !strcmp(argv[i], "boot") || !strncmp(argv[i], "boot,", 5)) { + drivename = argv[i]; + p = strchr(drivename, ','); + if (p) { + *p = '\0'; + partition = p+1; + } else if (argv[i+1] && argv[i+1][0] >= '0' && argv[i+1][0] <= '9') { + partition = argv[++i]; + } + } else { + error("Usage: chain.c32 (hd#|fd#|mbr:#|boot)[,partition] [options]\n"); + goto bail; + } + } + + if (opt.seg) { + regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.seg; + } else { + regs.ip = regs.esp.l = 0x7c00; + } + + hd = 0; + if ( !strncmp(drivename, "mbr", 3) ) { + drive = find_disk(strtoul(drivename+4, NULL, 0)); + if (drive == -1) { + error("Unable to find requested MBR signature\n"); + goto bail; + } + } else if ((drivename[0] == 'h' || drivename[0] == 'f') && + drivename[1] == 'd') { + hd = drivename[0] == 'h'; + drivename += 2; + drive = (hd ? 0x80 : 0) | strtoul(drivename, NULL, 0); + } else if (!strcmp(drivename, "boot")) { + const union syslinux_derivative_info *sdi; + sdi = syslinux_derivative_info(); + if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX || + sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) + drive = 0x80; /* Boot drive not available */ + else + drive = sdi->disk.drive_number; + } else { + error("Unparsable drive specification\n"); + goto bail; + } + + /* DOS kernels want the drive number in BL instead of DL. Indulge them. */ + regs.ebx.b[0] = regs.edx.b[0] = drive; + + whichpart = 0; /* Default */ + + if ( partition ) + whichpart = strtoul(partition, NULL, 0); + + if ( !(drive & 0x80) && whichpart ) { + error("Warning: Partitions of floppy devices may not work\n"); + } + + /* Get the disk geometry and disk access setup */ + if ( get_disk_params(drive) ) { + error("Cannot get disk parameters\n"); + goto bail; + } + + /* Get MBR */ + if ( !(mbr = read_sector(0)) ) { + error("Cannot read Master Boot Record\n"); + goto bail; + } + + if (opt.hide) { + if (whichpart < 1 || whichpart > 4) + error("WARNING: hide specified without a non-primary partition\n"); + if (hide_unhide(mbr, whichpart)) + error("WARNING: failed to write MBR for 'hide'\n"); + } + + if ( whichpart == 0 ) { + /* Boot the MBR */ + + partinfo = NULL; + boot_sector = mbr; + } else if ( whichpart <= 4 ) { + /* Boot a primary partition */ + + partinfo = &((struct part_entry *)(mbr + 0x1be))[whichpart-1]; + if ( partinfo->ostype == 0 ) { + error("Invalid primary partition\n"); + goto bail; + } + } else { + /* Boot a logical partition */ + + nextpart = 5; + partinfo = find_logical_partition(whichpart, mbr, NULL, NULL); + + if ( !partinfo || partinfo->ostype == 0 ) { + error("Requested logical partition not found\n"); + goto bail; + } + } + + /* Do the actual chainloading */ + if (opt.loadfile) { + fputs("Loading the boot file...\n", stdout); + if ( loadfile(opt.loadfile, &boot_sector, &boot_size) ) { + error("Failed to load the boot file\n"); + goto bail; + } + } else if (partinfo) { + /* Actually read the boot sector */ + /* Pick the first buffer that isn't already in use */ + if ( !(boot_sector = read_sector(partinfo->start_lba)) ) { + error("Cannot read boot sector\n"); + goto bail; + } + } + + if (!opt.loadfile) { + if (*(uint16_t *)((char *)boot_sector+boot_size-2) != 0xaa55) { + error("Boot sector signature not found (unbootable disk/partition?)\n"); + goto bail; + } + } + + if (partinfo) { + /* 0x7BE is the canonical place for the first partition entry. */ + regs.esi.w[0] = 0x7be; + memcpy((char *)0x7be, partinfo, sizeof(*partinfo)); + } + + do_boot(boot_sector, boot_size, ®s); + +bail: + return 255; +} diff --git a/com32/modules/chain.c32 b/com32/modules/chain.c32 new file mode 100755 index 0000000000000000000000000000000000000000..45b12c45c301f7ac57b0c5cfa9d2deef85850393 GIT binary patch literal 13604 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 Leoch`5%s?TI|-w) literal 0 HcmV?d00001 diff --git a/com32/modules/cmd.c b/com32/modules/cmd.c new file mode 100644 index 0000000..1a7f0ff --- /dev/null +++ b/com32/modules/cmd.c @@ -0,0 +1,25 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008 Michael Brown - 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, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * cmd.c + * + * Execute arbitrary commands + */ + +#include + +int main(void) +{ + syslinux_run_command(__com32.cs_cmdline); + return -1; +} diff --git a/com32/modules/cmd.c32 b/com32/modules/cmd.c32 new file mode 100755 index 0000000000000000000000000000000000000000..0abe142df880f21e4884f28d60c8c57cfcbff976 GIT binary patch literal 520 zcmdn--{-92AHxItIfMilc18mU&7TKfGJRg%TcX0Up9dt!&cMLXnf}L`uZ*di&++rp zZczcCa8T!i7wJHmfPEVT1sEFtfyiR^|6J2g z +#include +#include + +int main(int argc, char *argv[]) +{ + openconsole(&dev_null_r, &dev_stdcon_w); + + if (argc != 2) { + fprintf(stderr, "Usage: config \n"); + return 1; + } + + syslinux_run_kernel_image(argv[1], "", 0, IMAGE_TYPE_CONFIG); + + fprintf(stderr, "config: %s: failed to load (missing file?)\n", argv[1]); + return 1; +} diff --git a/com32/modules/config.c32 b/com32/modules/config.c32 new file mode 100755 index 0000000000000000000000000000000000000000..d153c2c17247992c1057e32cd53c04f68c77d4fd GIT binary patch literal 4360 zcmb7Ie{fXQ6@I(>LN>|5dm#%3#9`5kO(4=;jGBwy44KPiJwQ|L6w}k!!!oew)+vg-VpyzN*<6W2^K993-d0 zKEf;in9!fS@_2O>?Wd=XND%IM1wXMVs8GH!OW8*(SkSX$DR_QRTE248w$thwI`T)b z)Ji_~Ya1j7Z2EKjj+epOX^DxDQwGEy6Z$7MZ4TF6>F>rWn9Ehy5xeRkVyxAWvGuRt z@OU+i(vRq%YfL$=(~-_Im)@esr$-;AaXPVS^Oh&$OieeFgAV#3jk@%3qpq!_XXzyx zi`~M2≫c-`2nDkx|x~{hmSRuNpzuUmme(`mzp&pV) z*Ab$=P7bl*aWI^N9#V(_hrTG)m!^7ms`sRNFZAk=W6|YYdOT=_Lpm?~`~V!+$vZs= zs+?FJaMDi^y(UPyQ!8((p_hd#UIZnGGbsj+<}9ppc4n3XrBeqZUIm$O+4)kjB~y4i zqLa`Eut7iVHM6DPB104(@tfl-AjT*@?nqJA_`(z=#%(Fe5jUqOQ+x&r!ezsy87&K* zUPnBc!qL94$2^RPc;cU6PEM={EDJaD>aDbvctT?WRbg#DYyMec1UG% zVyBcZCw$smIUz|lIq`y&E+?K1xV8JPL2$!KE`EN_4XE5kyiS0n`-q!<8b8WN?2k67 z;Mc5O#1^=f8S}oh9wv$~V085*E8D}X*mXIvOX#p*QE;`KcuMH_j0c$`C;U>QoTw2( z<(P^uf?+vPFLWqSAtxGz&`PkiRyolubR1_ZL^-irS|KO)2qBS870QW#&~X~#Fyv#W zQBJf9p{X=z8%uGKA6)z-e%i6YGI|z%hg#zLg0_(&+C)l~ejdxcOmJ3B{68r{x8YdC z%qB7!ZwC2#1_W|R#PuN@wZPdT`d;6+OPMasCiEJd*N}ruv$k4IO7!(5mQo)iK2I?Q z`}C!NP^z6(EowDys4>>Cs2Rqzz1Jvl7X2*36fWRt@c|2ASl+g17U3_3Ifxm@P+P3# zxQ2kG$gpkup5K~y6WaxFkEIbQUWYSfQ+MBMA8W zVriXy@&%J50+`Pl$Ou=ON+jZaiN~zBIQkM#qnZGtQWgvc(vC6=R3fd+9jlb>im-K} z@M=Vfu;{jiEA`i=+eoWv0;5_?Zsh`5AlujiyOaS{wGSdPZL{kfeNq}uN}E}gh+p}a zC6EdC+KQWqCzt~a9@;@<<~QywS44BN9y&bqZwA{HDe;hkW(=L4yol zFc3&thE!hEe3y1R1Hz{`3!~x<(!QJ8{-rxnxI#P>$sIeeC@T~*B(f84NNo2C}>xhYou+O^p4C9J8bV7+SSjopFU z5<8EW)pzmc&UCiW@BbgdYjHiXG)ZM7e68hzFKhX5uK?~bpvd?(QWH&gupO+?lju`X z*V0)&Dd)|2a_1UbnA(C~fMSbHcoNYH;}ki>?q*A82l)*q!Uv7+FiR~Q%9Wun0%`BO zh1Uo|$gd%tetGGw>5+$k$g4}4*b47H9;m#{f=&6ioKs>>@E(2J#DUv;E8p<9EjXpnRa7YQRu$K9I z(@J>Kl9kVsMHrh-W}g!d4x2_k7Y?2^jcD}m87Bg#fH04fM3pB{&ypsOnrIbIC+OGL zN1KYGS00px(n{j(%m7 zz(z&ZwOIbf5fnm^03S7RWyXY-g^X3s>79$pToHK$Nt*}3fHhohq87ZP<;BPM{(F!WMjVo6zhM=Hu4u#`1+!I|SsI+* z7%6abTv{zKsWm(@R~SrV)p7>gl4H7N(W|W+pI~KeH>%Ig z4rdhb?>%IS-Nz=-IAh{TOu&)Al0)X$ZQq%xz>I4Y1(0N6wf*qMO`EUBtCXIgr^4%q zco-Et=ZetNlLw+6a*vs6^t850{!wNi2jhadc&NYbSq7QdIsA0ZL4XS?uQQK>`3QVE zNn^@DX|lra?si4`Q~N@2!fiNfR_tmKXoq@NE(=5(vEh?rNM|1$(<2bz>6f@!ZIf@( zQo?SY)A$g1fBHkUlrT0uJNahAV45FG7jiV#7vmfDuf_mY&kGIviW zOlb-G+R9La_K+U<=b^OyEU%C)cBVFszQ}79<)lt0W>4G(mR~F>mo%PXNWum}Rbe>c z+8iawbKg(TLe+0GV9XdWmJb@z3^4rv?p(&E4mA=TjPH&) zZhdpz_HAn%Pwn2-(74@E+R)gvZCBkh+kTYEC0T16%bV9Y8tTBWcSyS(O}p#r9eK|* zHa9o!+U{WDKhDqO@^%H9n)2Z?H*dbk)uA0hyDbMa+UubArrPgspc(I%-y3(P#`|vI z9|C>UE^@ +#include +#include +#include "cpuid.h" + +char display_line; + +int main(void) +{ + s_cpu cpu; + openconsole(&dev_stdcon_r, &dev_stdcon_w); + + for (;;) { + detect_cpu(&cpu); + printf("Vendor = %s\n",cpu.vendor); + printf("Model = %s\n",cpu.model); + printf("Vendor ID = %d\n",cpu.vendor_id); + printf("Family = %d\n",cpu.family); + printf("Model ID = %d\n",cpu.model_id); + printf("Stepping = %d\n",cpu.stepping); + printf("Flags = "); + if (cpu.flags.fpu) printf("fpu "); + if (cpu.flags.vme) printf("vme "); + if (cpu.flags.de) printf("de "); + if (cpu.flags.pse) printf("pse "); + if (cpu.flags.tsc) printf("tsc "); + if (cpu.flags.msr) printf("msr "); + if (cpu.flags.pae) printf("pae "); + if (cpu.flags.mce) printf("mce "); + if (cpu.flags.cx8) printf("cx8 "); + if (cpu.flags.apic) printf("apic "); + if (cpu.flags.sep) printf("sep "); + if (cpu.flags.mtrr) printf("mtrr "); + if (cpu.flags.pge) printf("pge "); + if (cpu.flags.mca) printf("mca "); + if (cpu.flags.cmov) printf("cmov "); + if (cpu.flags.pat) printf("pat "); + if (cpu.flags.pse_36) printf("pse_36 "); + if (cpu.flags.psn) printf("psn "); + if (cpu.flags.clflsh) printf("clflsh "); + if (cpu.flags.dts) printf("dts "); + if (cpu.flags.acpi) printf("acpi "); + if (cpu.flags.mmx) printf("mmx "); + if (cpu.flags.sse) printf("sse "); + if (cpu.flags.sse2) printf("sse2 "); + if (cpu.flags.ss) printf("ss "); + if (cpu.flags.htt) printf("ht "); + if (cpu.flags.acc) printf("acc "); + if (cpu.flags.syscall) printf("syscall "); + if (cpu.flags.mp) printf("mp "); + if (cpu.flags.nx) printf("nx "); + if (cpu.flags.mmxext) printf("mmxext "); + if (cpu.flags.lm) printf("lm "); + if (cpu.flags.nowext) printf("3dnowext "); + if (cpu.flags.now) printf("3dnow! "); + printf("\n"); + printf("SMP = "); + if (cpu.flags.smp) printf("yes\n"); + else printf("no\n"); + break; + } + + return 0; +} diff --git a/com32/modules/cpuidtest.c32 b/com32/modules/cpuidtest.c32 new file mode 100755 index 0000000000000000000000000000000000000000..01c5cc460206d3eeec7f6eb6790a22f1fd19d44d GIT binary patch literal 10316 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 Vl@uR%Jq{x3L;xY_^! literal 0 HcmV?d00001 diff --git a/com32/modules/dmi_utils.c b/com32/modules/dmi_utils.c new file mode 100644 index 0000000..e0baa87 --- /dev/null +++ b/com32/modules/dmi_utils.c @@ -0,0 +1,68 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- +*/ + +#include "stdio.h" +#include "dmi/dmi.h" + +void display_bios_characteristics(s_dmi *dmi) { +int i; + for (i=0;ibios.characteristics))[i] == true) { + moreprintf("\t\t%s\n", bios_charac_strings[i]); + } + } + for (i=0;ibios.characteristics_x1))[i] == true) { + moreprintf("\t\t%s\n", bios_charac_x1_strings[i]); + } + } + + for (i=0;ibios.characteristics_x2))[i] == true) { + moreprintf("\t\t%s\n", bios_charac_x2_strings[i]); + } + } +} + +void display_base_board_features(s_dmi *dmi) { +int i; + for (i=0;ibase_board.features))[i] == true) { + moreprintf("\t\t%s\n", base_board_features_strings[i]); + } + } +} + +void display_processor_flags(s_dmi *dmi) { +int i; + for (i=0;iprocessor.cpu_flags))[i] == true) { + moreprintf("\t\t%s\n", cpu_flags_strings[i]); + } + } +} diff --git a/com32/modules/dmitest.c b/com32/modules/dmitest.c new file mode 100644 index 0000000..07febc5 --- /dev/null +++ b/com32/modules/dmitest.c @@ -0,0 +1,211 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- +*/ + +/* + * dmitest.c + * + * DMI demo program using libcom32 + */ + +#include +#include +#include +#include "dmi/dmi.h" + +char display_line; + +void display_memory(s_dmi *dmi) { + int i; + for (i=0;imemory_count;i++) { + moreprintf("Memory Bank %d\n",i); + moreprintf("\tForm Factor : %s\n",dmi->memory[i].form_factor); + moreprintf("\tType : %s\n",dmi->memory[i].type); + moreprintf("\tType Detail : %s\n",dmi->memory[i].type_detail); + moreprintf("\tSpeed : %s\n",dmi->memory[i].speed); + moreprintf("\tSize : %s\n",dmi->memory[i].size); + moreprintf("\tDevice Set : %s\n",dmi->memory[i].device_set); + moreprintf("\tDevice Loc. : %s\n",dmi->memory[i].device_locator); + moreprintf("\tBank Locator : %s\n",dmi->memory[i].bank_locator); + moreprintf("\tTotal Width : %s\n",dmi->memory[i].total_width); + moreprintf("\tData Width : %s\n",dmi->memory[i].data_width); + moreprintf("\tError : %s\n",dmi->memory[i].error); + moreprintf("\tVendor : %s\n",dmi->memory[i].manufacturer); + moreprintf("\tSerial : %s\n",dmi->memory[i].serial); + moreprintf("\tAsset Tag : %s\n",dmi->memory[i].asset_tag); + moreprintf("\tPart Number : %s\n",dmi->memory[i].part_number); + } +} + +void display_battery(s_dmi *dmi) { + moreprintf("Battery\n"); + moreprintf("\tVendor : %s\n",dmi->battery.manufacturer); + moreprintf("\tManufacture Date : %s\n",dmi->battery.manufacture_date); + moreprintf("\tSerial : %s\n",dmi->battery.serial); + moreprintf("\tName : %s\n",dmi->battery.name); + moreprintf("\tChemistry : %s\n",dmi->battery.chemistry); + moreprintf("\tDesign Capacity : %s\n",dmi->battery.design_capacity); + moreprintf("\tDesign Voltage : %s\n",dmi->battery.design_voltage); + moreprintf("\tSBDS : %s\n",dmi->battery.sbds); + moreprintf("\tSBDS Manufact. Date : %s\n",dmi->battery.sbds_manufacture_date); + moreprintf("\tSBDS Chemistry : %s\n",dmi->battery.sbds_chemistry); + moreprintf("\tMaximum Error : %s\n",dmi->battery.maximum_error); + moreprintf("\tOEM Info : %s\n",dmi->battery.oem_info); +} + +void display_bios(s_dmi *dmi) { + moreprintf("BIOS\n"); + moreprintf("\tVendor: %s\n",dmi->bios.vendor); + moreprintf("\tVersion: %s\n",dmi->bios.version); + moreprintf("\tRelease: %s\n",dmi->bios.release_date); + moreprintf("\tBios Revision %s\n",dmi->bios.bios_revision); + moreprintf("\tFirmware Revision %s\n",dmi->bios.firmware_revision); + moreprintf("\tAddress: 0x%04X0\n",dmi->bios.address); + moreprintf("\tRuntime address: %u %s\n",dmi->bios.runtime_size,dmi->bios.runtime_size_unit); + moreprintf("\tRom size: %u %s\n",dmi->bios.rom_size,dmi->bios.rom_size_unit); + display_bios_characteristics(dmi); +} + +void display_system(s_dmi *dmi) { + moreprintf("\nSystem\n"); + moreprintf("\tManufacturer %s\n",dmi->system.manufacturer); + moreprintf("\tProduct Name %s\n",dmi->system.product_name); + moreprintf("\tVersion %s\n",dmi->system.version); + moreprintf("\tSerial %s\n",dmi->system.serial); + moreprintf("\tUUID %s\n",dmi->system.uuid); + moreprintf("\tWakeup Type %s\n",dmi->system.wakeup_type); + moreprintf("\tSKU Number %s\n",dmi->system.sku_number); + moreprintf("\tFamily %s\n",dmi->system.family); +} + +void display_base_board(s_dmi *dmi) { + moreprintf("Base board\n"); + moreprintf("\tManufacturer %s\n",dmi->base_board.manufacturer); + moreprintf("\tProduct Name %s\n",dmi->base_board.product_name); + moreprintf("\tVersion %s\n",dmi->base_board.version); + moreprintf("\tSerial %s\n",dmi->base_board.serial); + moreprintf("\tAsset Tag %s\n",dmi->base_board.asset_tag); + moreprintf("\tLocation %s\n",dmi->base_board.location); + moreprintf("\tType %s\n",dmi->base_board.type); + display_base_board_features(dmi); +} + +void display_chassis(s_dmi *dmi) { + moreprintf("\nChassis\n"); + moreprintf("\tManufacturer %s\n",dmi->chassis.manufacturer); + moreprintf("\tType %s\n",dmi->chassis.type); + moreprintf("\tLock %s\n",dmi->chassis.lock); + moreprintf("\tVersion %s\n",dmi->chassis.version); + moreprintf("\tSerial %s\n",dmi->chassis.serial); + moreprintf("\tAsset Tag %s\n",dmi->chassis.asset_tag); + moreprintf("\tBoot up state %s\n",dmi->chassis.boot_up_state); + moreprintf("\tPower supply state %s\n",dmi->chassis.power_supply_state); + moreprintf("\tThermal state %s\n",dmi->chassis.thermal_state); + moreprintf("\tSecurity Status %s\n",dmi->chassis.security_status); + moreprintf("\tOEM Information %s\n",dmi->chassis.oem_information); + moreprintf("\tHeight %u\n",dmi->chassis.height); + moreprintf("\tNB Power Cords %u\n",dmi->chassis.nb_power_cords); +} + +void display_cpu(s_dmi *dmi) { + moreprintf("\nCPU\n"); + moreprintf("\tSocket Designation %s\n",dmi->processor.socket_designation); + moreprintf("\tType %s\n",dmi->processor.type); + moreprintf("\tFamily %s\n",dmi->processor.family); + moreprintf("\tManufacturer %s\n",dmi->processor.manufacturer); + moreprintf("\tVersion %s\n",dmi->processor.version); + moreprintf("\tExternal Clock %u\n",dmi->processor.external_clock); + moreprintf("\tMax Speed %u\n",dmi->processor.max_speed); + moreprintf("\tCurrent Speed %u\n",dmi->processor.current_speed); + moreprintf("\tCpu Type %u\n",dmi->processor.signature.type); + moreprintf("\tCpu Family %u\n",dmi->processor.signature.family); + moreprintf("\tCpu Model %u\n",dmi->processor.signature.model); + moreprintf("\tCpu Stepping %u\n",dmi->processor.signature.stepping); + moreprintf("\tCpu Minor Stepping %u\n",dmi->processor.signature.minor_stepping); + moreprintf("\tVoltage %f\n",dmi->processor.voltage); + moreprintf("\tStatus %s\n",dmi->processor.status); + moreprintf("\tUpgrade %s\n",dmi->processor.upgrade); + moreprintf("\tCache L1 Handle %s\n",dmi->processor.cache1); + moreprintf("\tCache L2 Handle %s\n",dmi->processor.cache2); + moreprintf("\tCache L3 Handle %s\n",dmi->processor.cache3); + moreprintf("\tSerial %s\n",dmi->processor.serial); + moreprintf("\tPart Number %s\n",dmi->processor.part_number); + moreprintf("\tID %s\n",dmi->processor.id); + display_processor_flags(dmi); +} + +int main(void) +{ + char buffer[1024]; + s_dmi dmi; + openconsole(&dev_stdcon_r, &dev_stdcon_w); + + if (dmi_iterate(&dmi) == -ENODMITABLE) { + printf("No DMI Structure found\n"); + return -1; + } else { + printf("DMI %d.%d present.\n",dmi.dmitable.major_version,dmi.dmitable.minor_version); + printf("%d structures occupying %d bytes.\n",dmi.dmitable.num, dmi.dmitable.len); + printf("DMI table at 0x%08X.\n",dmi.dmitable.base); + } + + + parse_dmitable(&dmi); + + for (;;) { + printf("Available commands are system, chassis, base_board, cpu, bios, memory, battery, all, exit\n"); + printf("dmi: "); + fgets(buffer, sizeof buffer, stdin); + if ( !strncmp(buffer, "exit", 4) ) + break; + if ( !strncmp(buffer, "system", 6) ) + display_system(&dmi); + if ( !strncmp(buffer, "chassis", 6) ) + display_chassis(&dmi); + if ( !strncmp(buffer, "base_board", 10) ) + display_base_board(&dmi); + if ( !strncmp(buffer, "cpu", 3) ) + display_cpu(&dmi); + if ( !strncmp(buffer, "bios", 4) ) + display_bios(&dmi); + if ( !strncmp(buffer, "memory", 6) ) + display_memory(&dmi); + if ( !strncmp(buffer, "battery", 7) ) + display_battery(&dmi); + if ( !strncmp(buffer, "all", 3) ) { + display_bios(&dmi); + display_system(&dmi); + display_chassis(&dmi); + display_base_board(&dmi); + display_cpu(&dmi); + display_memory(&dmi); + display_battery(&dmi); + } + } + + return 0; +} diff --git a/com32/modules/dmitest.c32 b/com32/modules/dmitest.c32 new file mode 100755 index 0000000000000000000000000000000000000000..aaf0b5f3801c3a90685298aa2cb2457c83b00736 GIT binary patch literal 29000 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* If we don't have this much memory for the stack, signal failure */ +#define MIN_STACK 512 + +#define DEBUG 0 +#if DEBUG +# define dprintf printf +#else +# define dprintf(f, ...) ((void)0) +#endif + +static inline void error(const char *msg) +{ + fputs(msg, stderr); +} + +int boot_elf(void *ptr, size_t len, char **argv) +{ + char *cptr = ptr; + Elf32_Ehdr *eh = ptr; + Elf32_Phdr *ph; + unsigned int i; + struct syslinux_movelist *ml = NULL; + struct syslinux_memmap *mmap = NULL, *amap = NULL; + struct syslinux_pm_regs regs; + int argc; + addr_t argsize; + char **argp; + addr_t lstart, llen; + char *stack_frame = NULL; + addr_t stack_frame_size; + addr_t stack_pointer; + uint32_t *spp; + char *sfp; + addr_t sfa; + + memset(®s, 0, sizeof regs); + + /* + * Note: mmap is the memory map (containing free and zeroed regions) + * needed by syslinux_shuffle_boot_pm(); amap is a map where we keep + * track ourselves which target memory ranges have already been + * allocated. + */ + + if ( len < sizeof(Elf32_Ehdr) ) + goto bail; + + /* Must be ELF, 32-bit, littleendian, version 1 */ + if ( memcmp(eh->e_ident, "\x7f""ELF\1\1\1", 6) ) + goto bail; + + /* Is this a worthwhile test? In particular x86-64 normally + would imply ELF64 support, which we could do as long as + the addresses are 32-bit addresses, and entry is 32 bits. + 64-bit addresses would take a lot more work. */ + if ( eh->e_machine != EM_386 && eh->e_machine != EM_486 && + eh->e_machine != EM_X86_64 ) + goto bail; + + if ( eh->e_version != EV_CURRENT ) + goto bail; + + if ( eh->e_ehsize < sizeof(Elf32_Ehdr) || eh->e_ehsize >= len ) + goto bail; + + if ( eh->e_phentsize < sizeof(Elf32_Phdr) ) + goto bail; + + if ( !eh->e_phnum ) + goto bail; + + if ( eh->e_phoff+eh->e_phentsize*eh->e_phnum > len ) + goto bail; + + mmap = syslinux_memory_map(); + amap = syslinux_dup_memmap(mmap); + if (!mmap || !amap) + goto bail; + +#if DEBUG + dprintf("Initial memory map:\n"); + syslinux_dump_memmap(stdout, mmap); +#endif + + ph = (Elf32_Phdr *)(cptr+eh->e_phoff); + + for (i = 0; i < eh->e_phnum; i++) { + if (ph->p_type == PT_LOAD || ph->p_type == PT_PHDR) { + /* This loads at p_paddr, which is arguably the correct semantics. + The SysV spec says that SysV loads at p_vaddr (and thus Linux does, + too); that is, however, a major brainfuckage in the spec. */ + addr_t addr = ph->p_paddr; + addr_t msize = ph->p_memsz; + addr_t dsize = min(msize, ph->p_filesz); + + dprintf("Segment at 0x%08x data 0x%08x len 0x%08x\n", + addr, dsize, msize); + + if (syslinux_memmap_type(amap, addr, msize) != SMT_FREE) { + printf("Memory segment at 0x%08x (len 0x%08x) is unavailable\n", + addr, msize); + goto bail; /* Memory region unavailable */ + } + + /* Mark this region as allocated in the available map */ + if (syslinux_add_memmap(&amap, addr, dsize, SMT_ALLOC)) + goto bail; + + if (ph->p_filesz) { + /* Data present region. Create a move entry for it. */ + if (syslinux_add_movelist(&ml, addr, (addr_t)cptr+ph->p_offset, + dsize)) + goto bail; + } + if (msize > dsize) { + /* Zero-filled region. Mark as a zero region in the memory map. */ + if (syslinux_add_memmap(&mmap, addr+dsize, msize-dsize, SMT_ZERO)) + goto bail; + } + } else { + /* Ignore this program header */ + } + + ph = (Elf32_Phdr *)((char *)ph + eh->e_phentsize); + } + + /* Create the invocation record (initial stack frame) */ + + argsize = argc = 0; + for (argp = argv; *argp; argp++) { + dprintf("argv[%2d] = \"%s\"\n", argc, *argp); + argc++; + argsize += strlen(*argp)+1; + } + + /* We need the argument strings, argument pointers, + argc, plus four zero-word terminators. */ + stack_frame_size = argsize + argc*sizeof(char *) + 5*sizeof(long); + stack_frame_size = (stack_frame_size+15) & ~15; + stack_frame = calloc(stack_frame_size, 1); + if (!stack_frame) + goto bail; + +#if DEBUG + dprintf("Right before syslinux_memmap_largest()...\n"); + syslinux_dump_memmap(stdout, amap); +#endif + + if (syslinux_memmap_largest(amap, SMT_FREE, &lstart, &llen)) + goto bail; /* NO free memory?! */ + + if (llen < stack_frame_size+MIN_STACK+16) + goto bail; /* Insufficient memory */ + + /* Initial stack pointer address */ + stack_pointer = (lstart+llen-stack_frame_size) & ~15; + + dprintf("Stack frame at 0x%08x len 0x%08x\n", + stack_pointer, stack_frame_size); + + /* Create the stack frame. sfp is the pointer in current memory for + the next argument string, sfa is the address in its final resting place. + spp is the pointer into the argument array in current memory. */ + spp = (uint32_t *)stack_frame; + sfp = stack_frame + argc*sizeof(char *) + 5*sizeof(long); + sfa = stack_pointer + argc*sizeof(char *) + 5*sizeof(long); + + *spp++ = argc; + for (argp = argv; *argp; argp++) { + int bytes = strlen(*argp) + 1; /* Including final null */ + *spp++ = sfa; + memcpy(sfp, *argp, bytes); + sfp += bytes; + sfa += bytes; + } + /* Zero fields are aready taken care of by calloc() */ + + /* ... and we'll want to move it into the right place... */ +#if DEBUG + if (syslinux_memmap_type(amap, stack_pointer, stack_frame_size) + != SMT_FREE) { + dprintf("Stack frame area not free (how did that happen?)!\n"); + goto bail; /* Memory region unavailable */ + } +#endif + + if (syslinux_add_memmap(&amap, stack_pointer, stack_frame_size, SMT_ALLOC)) + goto bail; + + if (syslinux_add_movelist(&ml, stack_pointer, (addr_t)stack_frame, + stack_frame_size)) + goto bail; + + memset(®s, 0, sizeof regs); + regs.eip = eh->e_entry; + regs.esp = stack_pointer; + +#if DEBUG + dprintf("Final memory map:\n"); + syslinux_dump_memmap(stdout, mmap); + + dprintf("Final available map:\n"); + syslinux_dump_memmap(stdout, amap); + + dprintf("Movelist:\n"); + syslinux_dump_movelist(stdout, ml); +#endif + + /* This should not return... */ + fputs("Booting...\n", stdout); + syslinux_shuffle_boot_pm(ml, mmap, 0, ®s); + + bail: + if (stack_frame) + free(stack_frame); + syslinux_free_memmap(amap); + syslinux_free_memmap(mmap); + syslinux_free_movelist(ml); + + return -1; +} + +int main(int argc, char *argv[]) +{ + void *data; + size_t data_len; + + openconsole(&dev_null_r, &dev_stdcon_w); + + if (argc < 2) { + error("Usage: elf.c32 elf_file arguments...\n"); + return 1; + } + + if (zloadfile(argv[1], &data, &data_len)) { + error("Unable to load file\n"); + return 1; + } + + boot_elf(data, data_len, &argv[1]); + error("Invalid ELF file or insufficient memory\n"); + return 1; +} diff --git a/com32/modules/elf.c32 b/com32/modules/elf.c32 new file mode 100755 index 0000000000000000000000000000000000000000..4dde2db118d9f45fe787f5f9f47062f798204d7a GIT binary patch literal 27176 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Ν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 C^I0PR literal 0 HcmV?d00001 diff --git a/com32/modules/ethersel.c b/com32/modules/ethersel.c new file mode 100644 index 0000000..9bd445c --- /dev/null +++ b/com32/modules/ethersel.c @@ -0,0 +1,212 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2005-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * ethersel.c + * + * Search for an Ethernet card with a known PCI signature, and run + * the corresponding Ethernet module. + * + * To use this, set up a syslinux config file like this: + * + * PROMPT 0 + * DEFAULT ethersel.c32 + * # DEV [DID xxxx:yyyy[/mask]] [RID zz-zz] [SID uuuu:vvvv[/mask]] commandline + * # ... + * + * DID = PCI device ID + * RID = Revision ID (range) + * SID = Subsystem ID + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG +# define dprintf printf +#else +# define dprintf(...) ((void)0) +#endif + +static char * +skipspace(char *p) +{ + while ( *p && *p <= ' ' ) + p++; + + return p; +} + +#define MAX_LINE 512 + +/* Check to see if we are at a certain keyword (case insensitive) */ +static int looking_at(const char *line, const char *kwd) +{ + const char *p = line; + const char *q = kwd; + + while ( *p && *q && ((*p^*q) & ~0x20) == 0 ) { + p++; + q++; + } + + if ( *q ) + return 0; /* Didn't see the keyword */ + + return *p <= ' '; /* Must be EOL or whitespace */ +} + +static char * +get_did(char *p, uint32_t *idptr, uint32_t *maskptr) +{ + unsigned long vid, did, m1, m2; + + *idptr = -1; + *maskptr = 0xffffffff; + + vid = strtoul(p, &p, 16); + if ( *p != ':' ) + return p; /* Bogus ID */ + did = strtoul(p+1, &p, 16); + + *idptr = (did << 16) + vid; + + if ( *p == '/' ) { + m1 = strtoul(p+1, &p, 16); + if ( *p != ':' ) { + *maskptr = (m1 << 16) | 0xffff; + } else { + m2 = strtoul(p+1, &p, 16); + *maskptr = (m1 << 16) | m2; + } + } + + return p; +} + +static char * +get_rid_range(char *p, uint8_t *rid_min, uint8_t *rid_max) +{ + unsigned long r0, r1; + + p = skipspace(p+3); + + r0 = strtoul(p, &p, 16); + if ( *p == '-' ) { + r1 = strtoul(p+1, &p, 16); + } else { + r1 = r0; + } + + *rid_min = r0; + *rid_max = r1; + + return p; +} + +static struct match * +parse_config(const char *filename) +{ + char line[MAX_LINE], *p; + FILE *f; + struct match *list = NULL; + struct match **ep = &list; + struct match *m; + + if ( !filename ) + filename = syslinux_config_file(); + + f = fopen(filename, "r"); + if ( !f ) + return list; + + while ( fgets(line, sizeof line, f) ) { + p = skipspace(line); + + if ( !looking_at(p, "#") ) + continue; + p = skipspace(p+1); + + if ( !looking_at(p, "dev") ) + continue; + p = skipspace(p+3); + + m = malloc(sizeof(struct match)); + if ( !m ) + continue; + + memset(m, 0, sizeof *m); + m->rid_max = 0xff; + + for(;;) { + p = skipspace(p); + + if ( looking_at(p, "did") ) { + p = get_did(p+3, &m->did, &m->did_mask); + } else if ( looking_at(p, "sid") ) { + p = get_did(p+3, &m->sid, &m->sid_mask); + } else if ( looking_at(p, "rid") ) { + p = get_rid_range(p+3, &m->rid_min, &m->rid_max); + } else { + char *e; + + e = strchr(p, '\n'); + if ( *e ) *e = '\0'; + e = strchr(p, '\r'); + if ( *e ) *e = '\0'; + + m->filename = strdup(p); + if ( !m->filename ) + m->did = -1; + break; /* Done with this line */ + } + } + + dprintf("DEV DID %08x/%08x SID %08x/%08x RID %02x-%02x CMD %s\n", + m->did, m->did_mask, m->sid, m->sid_mask, + m->rid_min, m->rid_max, m->filename); + + *ep = m; + ep = &m->next; + } + + return list; +} + +int main(int argc, char *argv[]) +{ + struct match *list, *match; + struct pci_domain *pci_domain; + + openconsole(&dev_null_r, &dev_stdcon_w); + pci_domain = pci_scan(); + + if (pci_domain) { + list = parse_config(argc < 2 ? NULL : argv[1]); + + match = find_pci_device(pci_domain, list); + + if ( match ) + syslinux_run_command(match->filename); + } + + /* On error, return to the command line */ + fputs("Error: no recognized network card found!\n", stderr); + return 1; +} diff --git a/com32/modules/ethersel.c32 b/com32/modules/ethersel.c32 new file mode 100755 index 0000000000000000000000000000000000000000..c952cc819f9eb5cc800734291440d1dbdb4749b4 GIT binary patch literal 10532 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 +#include +#include +#include +#include + +static bool __constfunc cpu_has_cpuid(void) +{ + return cpu_has_eflag(X86_EFLAGS_ID); +} + +static bool __constfunc cpu_has_level(uint32_t level) +{ + uint32_t group; + uint32_t limit; + + if (!cpu_has_cpuid()) + return false; + + group = level & 0xffff0000; + limit = cpuid_eax(group); + + if ((limit & 0xffff0000) != group) + return false; + + if (level > limit) + return false; + + return true; +} + +/* This only supports feature groups 0 and 1, corresponding to the + Intel and AMD EDX bit vectors. We can add more later if need be. */ +static bool __constfunc cpu_has_feature(int x) +{ + uint32_t level = ((x & 1) << 31) | 1; + + return cpu_has_level(level) && ((cpuid_edx(level) >> (x & 31) & 1)); +} + +/* XXX: this really should be librarized */ +static void boot_args(char **args) +{ + int len = 0; + char **pp; + const char *p; + char c, *q, *str; + + for (pp = args; *pp; pp++) + len += strlen(*pp); + + q = str = alloca(len+1); + for (pp = args; *pp; pp++) { + p = *pp; + while ((c = *p++)) + *q++ = c; + } + *q = '\0'; + + if (!str[0]) + syslinux_run_default(); + else + syslinux_run_command(str); +} + +int main(int argc, char *argv[]) +{ + char **args[3]; + int i; + int n; + + args[0] = &argv[1]; + n = 1; + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "--")) { + argv[i] = NULL; + args[n++] = &argv[i+1]; + } + if (n >= 3) + break; + } + while (n < 3) { + args[n] = args[n-1]; + n++; + } + + boot_args(cpu_has_feature(X86_FEATURE_LM) ? args[0] : + cpu_has_feature(X86_FEATURE_PAE) ? args[1] : + args[2]); + return -1; +} diff --git a/com32/modules/ifcpu64.c32 b/com32/modules/ifcpu64.c32 new file mode 100755 index 0000000000000000000000000000000000000000..be4cd271e29aba8543d8f374f56c9be4668d26f3 GIT binary patch literal 904 zcmb7DO-vI(6rQ$&G*Y{bPy&WTG}VLyF?;Y(jFFZfH9}JU{3$`=rJjW@@j#@_(4@n< zNe>=8ltfEB7=L0s5X1zQwooxq>raqG6A5Bmod!!$gs|!Qwp{h#9%kNr@4fGR^XBb@ z*7D+j%FEN?QX6_wTxLTxTQlE`>-QwT15aNsfCwRaSv_VQFp5^!`W?|80l1wmDW44p zd8RyY3g-2-zgR0LbC|82_bOK*OS1<6{nJVp-5Q;F?3TYHX-k`lFTxxC+@e4i%kr{uo@4I-_Q`OLVK$*Z?ZQaV-~(dTBbCL#P_H zk4C+teEP0#`+KHXBtUXT9_W#Xiv^bJg~z~F#vZ}_X^JlJ z^CMLpH~y9z$5vNOoTddl{6Sz)ArXoE{RzcG`bdB~qBoIbY;^WkDJ|DqvM;b*J;Xr^F}#SY;9wKJ9$j zzl-)^$!n%LW`#h(EV&KT1aItiKJQyinc!APYQSTKs+%Nf(_52(iSnh2(x3z*etz;k z7!wB}Qt5?- +#include +#include +#include +#include + +static inline void error(const char *msg) +{ + fputs(msg, stderr); +} + +int main(int argc, char *argv[]) +{ + const struct syslinux_keyboard_map * const kmap = syslinux_keyboard_map(); + size_t map_size; + void *kbdmap; + + openconsole(&dev_null_r, &dev_stdcon_w); + + if (argc != 2) { + error("Usage: kbdmap mapfile\n"); + return 1; + } + + if (kmap->version != 1) { + error("Syslinux core version mismatch\n"); + return 1; + } + + if (loadfile(argv[1], &kbdmap, &map_size)) { + error("Keyboard map file load error\n"); + return 1; + } + + if (map_size != kmap->length) { + error("Keyboard map file format error\n"); + return 1; + } + + memcpy(kmap->map, kbdmap, map_size); + return 0; +} diff --git a/com32/modules/kbdmap.c32 b/com32/modules/kbdmap.c32 new file mode 100755 index 0000000000000000000000000000000000000000..50fbd2c0ae3931750d8ef140a0261fe17574b83a GIT binary patch literal 4644 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 +#include +#include +#include +#include +#include +#include +#include + +const char *progname = "linux.c32"; + +/* Find the last instance of a particular command line argument + (which should include the final =; do not use for boolean arguments) */ +static char *find_argument(char **argv, const char *argument) +{ + int la = strlen(argument); + char **arg; + char *ptr = NULL; + + for (arg = argv; *arg; arg++) { + if (!memcmp(*arg, argument, la)) + ptr = *arg + la; + } + + return ptr; +} + +/* Search for a boolean argument; return its position, or 0 if not present */ +static int find_boolean(char **argv, const char *argument) +{ + char **arg; + + for (arg = argv; *arg; arg++) { + if (!strcmp(*arg, argument)) + return (arg-argv)+1; + } + + return 0; +} + +/* Stitch together the command line from a set of argv's */ +static char *make_cmdline(char **argv) +{ + char **arg; + size_t bytes; + char *cmdline, *p; + + bytes = 1; /* Just in case we have a zero-entry cmdline */ + for (arg = argv; *arg; arg++) { + bytes += strlen(*arg)+1; + } + + p = cmdline = malloc(bytes); + if (!cmdline) + return NULL; + + for (arg = argv; *arg; arg++) { + int len = strlen(*arg); + memcpy(p, *arg, len); + p[len] = ' '; + p += len+1; + } + + if (p > cmdline) + p--; /* Remove the last space */ + *p = '\0'; + + return cmdline; +} + +int main(int argc, char *argv[]) +{ + const char *kernel_name; + struct initramfs *initramfs; + char *cmdline; + char *boot_image; + void *kernel_data; + size_t kernel_len; + bool opt_dhcpinfo = false; + bool opt_quiet = false; + void *dhcpdata; + size_t dhcplen; + char **argp, *arg, *p; + + openconsole(&dev_null_r, &dev_stdcon_w); + + (void)argc; + argp = argv+1; + + while ((arg = *argp) && arg[0] == '-') { + if (!strcmp("-dhcpinfo", arg)) { + opt_dhcpinfo = true; + } else { + fprintf(stderr, "%s: unknown option: %s\n", progname, arg); + return 1; + } + argp++; + } + + if (!arg) { + fprintf(stderr, "%s: missing kernel name\n", progname); + return 1; + } + + kernel_name = arg; + + boot_image = malloc(strlen(kernel_name)+12); + if (!boot_image) + goto bail; + strcpy(boot_image, "BOOT_IMAGE="); + strcpy(boot_image+11, kernel_name); + /* argp now points to the kernel name, and the command line follows. + Overwrite the kernel name with the BOOT_IMAGE= argument, and thus + we have the final argument. */ + *argp = boot_image; + + if (find_boolean(argp,"quiet")) + opt_quiet = true; + + if (!opt_quiet) + printf("Loading %s... ", kernel_name); + if (loadfile(kernel_name, &kernel_data, &kernel_len)) { + if (opt_quiet) + printf("Loading %s ", kernel_name); + printf("failed!\n"); + goto bail; + } + if (!opt_quiet) + printf("ok\n"); + + cmdline = make_cmdline(argp); + if (!cmdline) + goto bail; + + /* Initialize the initramfs chain */ + initramfs = initramfs_init(); + if (!initramfs) + goto bail; + + if ((arg = find_argument(argp, "initrd="))) { + do { + p = strchr(arg, ','); + if (p) + *p = '\0'; + + if (!opt_quiet) + printf("Loading %s... ", arg); + if (initramfs_load_archive(initramfs, arg)) { + if (opt_quiet) + printf("Loading %s ", kernel_name); + printf("failed!\n"); + goto bail; + } + if (!opt_quiet) + printf("ok\n"); + + if (p) + *p++ = ','; + } while ((arg = p)); + } + + /* Append the DHCP info */ + if (opt_dhcpinfo && + !pxe_get_cached_info(PXENV_PACKET_TYPE_DHCP_ACK, &dhcpdata, &dhcplen)) { + if (initramfs_add_file(initramfs, dhcpdata, dhcplen, dhcplen, + "/dhcpinfo.dat", 0, 0755)) + goto bail; + } + + /* This should not return... */ + syslinux_boot_linux(kernel_data, kernel_len, initramfs, cmdline); + + bail: + fprintf(stderr, "Kernel load failure (insufficient memory?)\n"); + return 1; +} diff --git a/com32/modules/linux.c32 b/com32/modules/linux.c32 new file mode 100755 index 0000000000000000000000000000000000000000..7c6554a87d6fd36059fec4fcfaafa293fccfd435 GIT binary patch literal 16556 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 literal 0 HcmV?d00001 diff --git a/com32/modules/meminfo.c b/com32/modules/meminfo.c new file mode 100644 index 0000000..e88c824 --- /dev/null +++ b/com32/modules/meminfo.c @@ -0,0 +1,125 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * 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, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * meminfo.c + * + * Dump the memory map of the system + */ +#include +#include +#include +#include +#include + +struct e820_data { + uint64_t base; + uint64_t len; + uint32_t type; + uint32_t extattr; +} __attribute__((packed)); + +static const char * const e820_types[] = { + "usable", + "reserved", + "ACPI reclaim", + "ACPI NVS", + "unusable", +}; + +static void dump_e820(void) +{ + com32sys_t ireg, oreg; + struct e820_data ed; + uint32_t type; + + memset(&ireg, 0, sizeof ireg); + + ireg.eax.w[0] = 0xe820; + ireg.edx.l = 0x534d4150; + ireg.ecx.l = sizeof(struct e820_data); + ireg.edi.w[0] = OFFS(__com32.cs_bounce); + ireg.es = SEG(__com32.cs_bounce); + + memset(&ed, 0, sizeof ed); + ed.extattr = 1; + + do { + memcpy(__com32.cs_bounce, &ed, sizeof ed); + + __intcall(0x15, &ireg, &oreg); + if (oreg.eflags.l & EFLAGS_CF || + oreg.eax.l != 0x534d4150 || + oreg.ecx.l < 20) + break; + + memcpy(&ed, __com32.cs_bounce, sizeof ed); + + if (oreg.ecx.l >= 24) { + /* ebx base length end type */ + printf("%8x %016llx %016llx %016llx %d [%x]", + ireg.ebx.l, ed.base, ed.len, ed.base+ed.len, ed.type, ed.extattr); + } else { + /* ebx base length end */ + printf("%8x %016llx %016llx %016llx %d [-]", + ireg.ebx.l, ed.base, ed.len, ed.base+ed.len, ed.type); + ed.extattr = 1; + } + + type = ed.type - 1; + if (type < sizeof(e820_types)/sizeof(e820_types[0])) + printf(" %s", e820_types[type]); + + putchar('\n'); + + ireg.ebx.l = oreg.ebx.l; + } while (ireg.ebx.l); +} + +static void dump_legacy(void) +{ + com32sys_t ireg, oreg; + uint16_t dosram = *(uint16_t *)0x413; + struct { uint16_t offs, seg; } * const ivt = (void *)0; + + memset(&ireg, 0, sizeof ireg); + + __intcall(0x12, &ireg, &oreg); + + printf("INT 15h = %04x:%04x DOS RAM: %dK (0x%05x) INT 12h: %dK (0x%05x)\n", + ivt[0x15].seg, ivt[0x15].offs, + dosram, dosram << 10, + oreg.eax.w[0], oreg.eax.w[0] << 10); + + ireg.eax.b[1] = 0x88; + __intcall(0x15, &ireg, &oreg); + + printf("INT 15 88: 0x%04x (%uK) ", + oreg.eax.w[0], oreg.eax.w[0]); + + ireg.eax.w[0] = 0xe801; + __intcall(0x15, &ireg, &oreg); + + printf("INT 15 E801: 0x%04x (%uK) 0x%04x (%uK)\n", + oreg.ecx.w[0], oreg.ecx.w[0], oreg.edx.w[0], oreg.edx.w[0] << 6); +} + +int main(void) +{ + openconsole(&dev_null_r, &dev_stdcon_w); + + dump_legacy(); + dump_e820(); + + return 0; +} diff --git a/com32/modules/meminfo.c32 b/com32/modules/meminfo.c32 new file mode 100755 index 0000000000000000000000000000000000000000..ced431625a6148506433f8db68e80e72f87fe056 GIT binary patch literal 5000 zcmc&&dvH|M8Na)GLpBe%8wkiFEsLDk1S0N2(1e6G2@5V|!Oa7R5J5Cxu&E-s3$aAA z>D|>FF5Af;IISJ0Q^z`E3**o!BabRFRlF~{ZMYzK_gOJ(yFtHAsrnctB5L2%E{l?sKH3lJ<&cMo%0CA5 z7p^Z}x}>_Ytv`yjy*uu=3A+#CC+Zj>2-2?ajS>YR6kxw2b1o)2FYd{pVGoZQXqx#q zp{>U1j*-#tGD&1BdjlMmytlJ2-CM;^QCBVPN~rQ2^6OYgJ4Q@6zkh582j6tfLZuUFs6 z`Y}v){2>3tG>~-JqaN=n0?bhhZQuktlh;XgwXaKwp&XOOt(BvUeqWH}vY@ z^U~a@$6Bqxp^MV1+Yl+8eQ+7E_s4U64t^HiG=j4%DS1x~`zgM$A}B#-STS%kWkAl+ zo?Zcz4jqJ8C3r&ReWkXI>C$^)odvBp8u;1$6sXvDI877}^O|E*z(yl6W>1c+u`$V! zEtZ)a*<)03#Ak{PLP4m!w=`|z=%3UuSGu&XGvqS&A|kFB6|z4*#WyL`B&rj59diZG zNL+=rt7mAIu(=6ye|(x0T#La?kKK?J_FM21TLF5)#7LNM-)6$CwLrfVe26%bHr!<6 z2OuOvX9PPo9t3~9QLb*4{P704+#i2d&iBVX+HikdmNWhFpUJ8I_zS)=?U9=h%HX6B z>%+#sMKa=b1SH+V%J|vXJ{lrF+9d_QX00D7C{qT_FQ*tah%jJw<0UKQ5IaTp$Dfk| z76`RY_s5@>0_Q{wNBQGkd96QQD+MdC6dMP_{&>9Qtf~+fyM>>~_ZX3gY|RnBsTNzNv14J%=aZgXg!{`%Dx)@&NUtPYD$3cd^s zTSy>VPJ^ntM-iEpAvt@eoPwRwVpe75RlcsM`ef9#RH`SX&y4GMxRJtS3ibmO37c>wq7}wC zYKYEeOM8I*5(}a2Mz@Db3!8Fk@S-o}gMQq2U_yNz()rilzI*l9d?0e`aynAsepv)6 zcd>`LUVesfZoFF*bQw=o>D)8PoVS)?+ zk$&+f;5WcXfp;*3{)BlP7floqM`W&GpNn1Vs2~962^+Syi-JOyH6KLElmlkvnz-?q z&>T@`ugN$$cto{qhO%~>R!w8LUbu!^wRy$_PV%wZEMp9?yhxj7j2kh|>nX$DM^q=k z*1g{WiHppsYr}E(6SF1)MI*noIX(2O31Axohos=TtGa$uMH(0!5woCWBO^>ew(D3P zK(UA`L#G!$n_~DBHNgeFS2o%n~r9EE^Y5M9lIcHKW#du z@z2tZ_)Y*}o*>&)kwHBbO&&M#N|6unYd4>XcqiKFok%BV0k!B#+{;6dlv{VAsA(x)BCB;K+%{^4)yQOzhPNu3$M8vkZD)w=OY+dSU?Z+oj0 z4Ew~0NhmiKv z@^7^fVDwo-6((-M-C9w6c>PyB^jH#5NN*Wd@i4X-Il%+lstitS9l17KP$mc|JAg^8 z6;aJnPYS)R(;%iC)HRD>6n&l+ z=s{#Gyhd6EO}?BR=IG)(RyJY9c^r=-*@6!M-n(r1id*q2;DUL#h(88#0em{jqe^#a;)|f8 z!x`>M<|UJkUFUHeR~FTagC?kB@ThpD(;{2bdaw2d56a1KjDd~VD}?#VpD9*KiQpTi zY!U@|?7X%v3ZZht?u#X2N1v5iU618c?A)F?;sCnPoB}?F1ux23>S9~)xNPO8blCMx z=2KyGhPsr9EJ2iW=&L48ZOsnqzJKmWd5iLaC})$}6uwE+EXq-x4-8fBQpyV@GiU87 zLJ~3%stVzRYr~XQQMfy;g{n8xfH7#m$n7z>2{8P>b1iL81|EIR@TG0bman87C7ir~ z4T1sz;m)}r%wJSvcg}3E&$j0}3Y%xqkKOKitlC~xzHpX3w|;>=&(WOgnAx0fw;R0E z8}1~hCso*sie}k~6$NP|=k1q!L1j(65kN;bL=P%oge~9>Rqo2QQHE3b{18C^){N+qTzV|lT(Axi_ M|LgbQJO1nTAM#9numAu6 literal 0 HcmV?d00001 diff --git a/com32/modules/pcitest.c b/com32/modules/pcitest.c new file mode 100644 index 0000000..8b97fbe --- /dev/null +++ b/com32/modules/pcitest.c @@ -0,0 +1,148 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2006 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- +*/ + +/* + * pcitest.c + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG +# define dprintf printf +#else +# define dprintf(...) ((void)0) +#endif + +char display_line=0; +#define moreprintf(...) \ + do { \ + display_line++; \ + if (display_line == 24) { \ + char tempbuf[10]; \ + display_line=0; \ + printf("Press Enter to continue\n"); \ + fgets(tempbuf, sizeof tempbuf, stdin); \ + } \ + printf ( __VA_ARGS__); \ + } while (0); + +void display_pci_devices(struct pci_domain *pci_domain) { + struct pci_device *pci_device; + char kernel_modules [LINUX_KERNEL_MODULE_SIZE*MAX_KERNEL_MODULES_PER_PCI_DEVICE]; + + for_each_pci_func(pci_device, pci_domain) { + + memset(kernel_modules,0,sizeof kernel_modules); + +/* printf("PCI: found %d kernel modules for %04x:%04x[%04x:%04x]\n", + pci_device->dev_info->linux_kernel_module_count, + pci_device->vendor, pci_device->product, + pci_device->sub_vendor, pci_device->sub_product); +*/ + for (int i=0; idev_info->linux_kernel_module_count;i++) { + if (i>0) { + strncat(kernel_modules," | ",3); + } + strncat(kernel_modules, pci_device->dev_info->linux_kernel_module[i],LINUX_KERNEL_MODULE_SIZE-1); + } + + moreprintf("%04x:%04x[%04x:%04x]: %s\n", + pci_device->vendor, pci_device->product, + pci_device->sub_vendor, pci_device->sub_product, + pci_device->dev_info->class_name); + + moreprintf(" Vendor Name : %s\n", pci_device->dev_info->vendor_name); + moreprintf(" Product Name : %s\n", pci_device->dev_info->product_name); + moreprintf(" PCI bus position : %02x:%02x.%01x\n", __pci_bus, __pci_slot, __pci_func); + moreprintf(" Kernel modules : %s\n\n",kernel_modules); + } +} + +int main(int argc, char *argv[]) +{ + struct pci_domain *pci_domain; + int return_code=0; + int nb_pci_devices=0; + + openconsole(&dev_stdcon_r, &dev_stdcon_w); + + /* Scanning to detect pci buses and devices */ + printf("PCI: Scanning PCI BUS\n"); + pci_domain = pci_scan(); + if (!pci_domain) { + printf("PCI: no devices found!\n"); + return 1; + } + + struct pci_device *pci_device; + for_each_pci_func(pci_device, pci_domain) { + nb_pci_devices++; + } + + printf("PCI: %d PCI devices found\n",nb_pci_devices); + + + printf("PCI: Looking for device name\n"); + /* Assigning product & vendor name for each device*/ + return_code=get_name_from_pci_ids(pci_domain,"pci.ids"); + if (return_code == -ENOPCIIDS) { + printf("PCI: ERROR !\n"); + printf("PCI: Unable to open pci.ids in the same directory as pcitest.c32.\n"); + printf("PCI: PCI Device names can't be computed.\n"); + } + + printf("PCI: Resolving class names\n"); + /* Assigning class name for each device*/ + return_code=get_class_name_from_pci_ids(pci_domain,"pci.ids"); + if (return_code == -ENOPCIIDS) { + printf("PCI: ERROR !\n"); + printf("PCI: Unable to open pci.ids in the same directory as pcitest.c32.\n"); + printf("PCI: PCI class names can't be computed.\n"); + } + + printf("PCI: Looking for Kernel modules\n"); + /* Detecting which kernel module should match each device */ + return_code=get_module_name_from_pcimap(pci_domain,"modules.pcimap"); + if (return_code == -ENOMODULESPCIMAP) { + printf("PCI: ERROR !\n"); + printf("PCI: Unable to open modules.pcimap in the same directory as pcitest.c32.\n"); + printf("PCI: Kernel Module names can't be computed.\n"); + } + + /* display the pci devices we found */ + display_pci_devices(pci_domain); + return 0; +} diff --git a/com32/modules/pcitest.c32 b/com32/modules/pcitest.c32 new file mode 100755 index 0000000000000000000000000000000000000000..5d7c5f4e8c7804b72d37834b2b4b3bf22f5c603f GIT binary patch literal 14504 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 JkPmZx{eO;(`b7W$ literal 0 HcmV?d00001 diff --git a/com32/modules/pmload.c b/com32/modules/pmload.c new file mode 100644 index 0000000..b45cf5f --- /dev/null +++ b/com32/modules/pmload.c @@ -0,0 +1,230 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * pmload.c + * + * Load a binary file and run it in protected mode. We give it + * an ELF-style invocation record, becase, why not? + * + * Usage: pmload.c32 filename address [arguments...] + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* If we don't have this much memory for the stack, signal failure */ +#define MIN_STACK 512 + +#define DEBUG 0 +#if DEBUG +# define dprintf printf +#else +# define dprintf(f, ...) ((void)0) +#endif + +static inline void error(const char *msg) +{ + fputs(msg, stderr); +} + +int boot_raw(void *ptr, size_t len, addr_t where, char **argv) +{ + struct syslinux_movelist *ml = NULL; + struct syslinux_memmap *mmap = NULL, *amap = NULL; + struct syslinux_pm_regs regs; + int argc; + addr_t argsize; + char **argp; + addr_t lstart, llen; + char *stack_frame = NULL; + addr_t stack_frame_size; + addr_t stack_pointer; + uint32_t *spp; + char *sfp; + addr_t sfa; + + memset(®s, 0, sizeof regs); + + mmap = syslinux_memory_map(); + amap = syslinux_dup_memmap(mmap); + if (!mmap || !amap) + goto bail; + +#if DEBUG + dprintf("Initial memory map:\n"); + syslinux_dump_memmap(stdout, mmap); +#endif + + dprintf("Segment at 0x%08x len 0x%08x\n", where, len); + + if (syslinux_memmap_type(amap, where, len) != SMT_FREE) { + printf("Memory segment at 0x%08x (len 0x%08x) is unavailable\n", + where, len); + goto bail; /* Memory region unavailable */ + } + + /* Mark this region as allocated in the available map */ + if (syslinux_add_memmap(&amap, where, len, SMT_ALLOC)) + goto bail; + + /* Data present region. Create a move entry for it. */ + if (syslinux_add_movelist(&ml, where, (addr_t)ptr, len)) + goto bail; + + /* Create the invocation record (initial stack frame) */ + + argsize = argc = 0; + for (argp = argv; *argp; argp++) { + dprintf("argv[%2d] = \"%s\"\n", argc, *argp); + argc++; + argsize += strlen(*argp)+1; + } + + /* We need the argument strings, argument pointers, + argc, plus four zero-word terminators. */ + stack_frame_size = argsize + argc*sizeof(char *) + 5*sizeof(long); + stack_frame_size = (stack_frame_size+15) & ~15; + stack_frame = calloc(stack_frame_size, 1); + if (!stack_frame) + goto bail; + +#if DEBUG + dprintf("Right before syslinux_memmap_largest()...\n"); + syslinux_dump_memmap(stdout, amap); +#endif + + if (syslinux_memmap_largest(amap, SMT_FREE, &lstart, &llen)) + goto bail; /* NO free memory?! */ + + if (llen < stack_frame_size+MIN_STACK+16) + goto bail; /* Insufficient memory */ + + /* Initial stack pointer address */ + stack_pointer = (lstart+llen-stack_frame_size) & ~15; + + dprintf("Stack frame at 0x%08x len 0x%08x\n", + stack_pointer, stack_frame_size); + + /* Create the stack frame. sfp is the pointer in current memory for + the next argument string, sfa is the address in its final resting place. + spp is the pointer into the argument array in current memory. */ + spp = (uint32_t *)stack_frame; + sfp = stack_frame + argc*sizeof(char *) + 5*sizeof(long); + sfa = stack_pointer + argc*sizeof(char *) + 5*sizeof(long); + + *spp++ = argc; + for (argp = argv; *argp; argp++) { + int bytes = strlen(*argp) + 1; /* Including final null */ + *spp++ = sfa; + memcpy(sfp, *argp, bytes); + sfp += bytes; + sfa += bytes; + } + /* Zero fields are aready taken care of by calloc() */ + + /* ... and we'll want to move it into the right place... */ +#if DEBUG + if (syslinux_memmap_type(amap, stack_pointer, stack_frame_size) + != SMT_FREE) { + dprintf("Stack frame area not free (how did that happen?)!\n"); + goto bail; /* Memory region unavailable */ + } +#endif + + if (syslinux_add_memmap(&amap, stack_pointer, stack_frame_size, SMT_ALLOC)) + goto bail; + + if (syslinux_add_movelist(&ml, stack_pointer, (addr_t)stack_frame, + stack_frame_size)) + goto bail; + + memset(®s, 0, sizeof regs); + regs.eip = where; + regs.esp = stack_pointer; + +#if DEBUG + dprintf("Final memory map:\n"); + syslinux_dump_memmap(stdout, mmap); + + dprintf("Final available map:\n"); + syslinux_dump_memmap(stdout, amap); + + dprintf("Movelist:\n"); + syslinux_dump_movelist(stdout, ml); +#endif + + /* This should not return... */ + fputs("Booting...\n", stdout); + syslinux_shuffle_boot_pm(ml, mmap, 0, ®s); + + bail: + if (stack_frame) + free(stack_frame); + syslinux_free_memmap(amap); + syslinux_free_memmap(mmap); + syslinux_free_movelist(ml); + + return -1; +} + +int main(int argc, char *argv[]) +{ + void *data; + size_t data_len; + addr_t where; + + openconsole(&dev_null_r, &dev_stdcon_w); + + if (argc < 3) { + error("Usage: pmload.c32 bin_file address arguments...\n"); + return 1; + } + + where = strtoul(argv[2], NULL, 0); + + if (loadfile(argv[1], &data, &data_len)) { + error("Unable to load file\n"); + return 1; + } + + boot_raw(data, data_len, where, &argv[1]); + error("Failed to boot, probably insufficient memory\n"); + return 1; +} diff --git a/com32/modules/pmload.c32 b/com32/modules/pmload.c32 new file mode 100755 index 0000000000000000000000000000000000000000..40196ed6961b2a8e41a12d242411b950f453f494 GIT binary patch literal 12836 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 z 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 literal 0 HcmV?d00001 diff --git a/com32/modules/reboot.c b/com32/modules/reboot.c new file mode 100644 index 0000000..f505675 --- /dev/null +++ b/com32/modules/reboot.c @@ -0,0 +1,16 @@ +#include +#include + +int main(int argc, char *argv[]) +{ + int warm = 0; + int i; + + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-w") || + !strcmp(argv[i], "--warm")) + warm = 1; + } + + syslinux_reboot(warm); +} diff --git a/com32/modules/reboot.c32 b/com32/modules/reboot.c32 new file mode 100755 index 0000000000000000000000000000000000000000..f318cbc4d12940f75530671c9ddd07588c206dc8 GIT binary patch literal 520 zcmdn--{-92AHxItIfMilc18mU&7TKfGJRg%TcX0Up9dt!&cMLXnf}L`uZ*di&++rp zZczcCa8T!i7yE%S0sFoP3NSSO1Chn-|HYb*vAq5m5%mJ3>%S-iQ013i9~GA7Cm;S7 z{s|2a3l404!_n!Y!qR!w@c(W`psLqe-6eAwcD(=^(D~toI#8tbK#9Pv*ATuqm|x1e zAEeaTI!1-1=0fL>7k7X>pkbZIJFgj@?2_T<-*%aQ8w;PK=Aq6Dr7SJCOJ7IF#UGw9 z0lV|MRYBebN>2c}sQHaR=g|cpf%bK{sPK22vH;~eKkfio2)4CLs^PanJ%8(gQkG+k z431!l*WArN7@P0^?~rsfJkVgpP{Qo=`g(65bMvD=ouLf9Kyx~odZSo8EzcFRbr!Mo zMsak0dd&w^Sjy4s#MsH)e3-?_@>J2g+^Z7i1k0cpxCa5CK#K0A5SIQUCw| literal 0 HcmV?d00001 diff --git a/com32/modules/sanboot.c b/com32/modules/sanboot.c new file mode 100644 index 0000000..b8bfe25 --- /dev/null +++ b/com32/modules/sanboot.c @@ -0,0 +1,140 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008 H. Peter Anvin - 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, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * sanboot.c + * + * Invoke the gPXE "sanboot" command, if available. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct segoff16 { + uint16_t offs, seg; +}; + +struct s_PXENV_FILE_CHECK_API { + uint16_t Status; + uint16_t Size; + uint32_t Magic; + uint32_t Provider; + uint32_t APIMask; + uint32_t Flags; +}; + +static bool is_gpxe(void) +{ + const struct syslinux_version *sv; + com32sys_t reg; + struct s_PXENV_FILE_CHECK_API *fca; + + sv = syslinux_version(); + if (sv->filesystem != SYSLINUX_FS_PXELINUX) + return false; /* Not PXELINUX */ + + fca = __com32.cs_bounce; + memset(fca, 0, sizeof *fca); + fca->Size = sizeof *fca; + fca->Magic = 0x91d447b2; + + memset(®, 0, sizeof reg); + reg.eax.w[0] = 0x0009; + reg.ebx.w[0] = 0x00e6; /* PXENV_FILE_API_CHECK */ + reg.edi.w[0] = OFFS(fca); + reg.es = SEG(fca); + + __intcall(0x22, ®, ®); + + if (reg.eflags.l & EFLAGS_CF) + return false; /* Cannot invoke PXE stack */ + + if (reg.eax.w[0] || fca->Status) + return false; /* PXE failure */ + + if (fca->Magic != 0xe9c17b20) + return false; /* Incorrect magic */ + + if (fca->Size < sizeof *fca) + return false; /* Short return */ + + if (!(fca->APIMask & (1 << 5))) + return false; /* No FILE EXEC */ + + return true; +} + +struct s_PXENV_FILE_EXEC { + uint16_t Status; + struct segoff16 Command; +}; + +static void sanboot(const char **args) +{ + char *q; + struct s_PXENV_FILE_EXEC *fx; + com32sys_t reg; + + memset(®, 0, sizeof reg); + + fx = __com32.cs_bounce; + q = (char *)(fx+1); + + fx->Status = 1; + fx->Command.offs = OFFS(q); + fx->Command.seg = SEG(q); + + q = stpcpy(q, "sanboot"); + + while (*args) { + *q++ = ' '; + q = stpcpy(q, *args); + args++; + } + + memset(®, 0, sizeof reg); + reg.eax.w[0] = 0x0009; + reg.ebx.w[0] = 0x00e5; /* PXENV_FILE_EXEC */ + reg.edi.w[0] = OFFS(fx); + reg.es = SEG(fx); + + __intcall(0x22, ®, ®); + + /* This should not return... */ +} + +int main(int argc, const char *argv[]) +{ + openconsole(&dev_null_r, &dev_stdcon_w); + + if (argc < 2) { + printf("Usage: sanboot rootpath\n"); + return 1; + } + + if (!is_gpxe()) { + printf("sanboot: gPXE API not detected\n"); + return 1; + } + + sanboot(argv+1); + + /* sanboot() should not return... */ + printf("SAN boot failed.\n"); + return 1; +} diff --git a/com32/modules/sanboot.c32 b/com32/modules/sanboot.c32 new file mode 100755 index 0000000000000000000000000000000000000000..22431b5382ddb234a15741e0048ad6fc76d03540 GIT binary patch literal 1800 zcmah~ZERCj7(Q)JyKXDAFgB-|vFIg%un?~fBOemFPCjHY9b+=O$(U|sO9)@PmuN;D zoqg}aiTcFCa>w9jQ4F1?3=i_<5 zp7-OP#?0!A3#UZwoclQ+iT_nrN|yYc$k-=NsFFa>c{rFy2!WkbtL7fCD{})AUn|8v zgg3)Yy~s{T=wC2|?CwEG z!nHmL4Pu|;qPL)%OAa41h0$e>YJJj37{w1_qF-0S%X`4HBBa9hLnCbfAgiO&D? z%f03*pw_w~X@`x#pp{wB4ue)=L7EP_I+}NXkglYA_DDnx&dKaBO|z$m-SzZgX^FlH z*QoY!9=l+&eor#a!9E@|wih`dlnEfz1BoN{Zd>K2n&b!=lW-x~!+rQ_HhWKNA+vY^ zwt@ARC3`|=d9$Bwxw43l5VxtrHlrP!Kei9`x~l}QvYes}?pv9jev-4Ikb*0srXR(G~ zoV4kT3wVJnfs6tqC6}GF>+MfT8c-q*v8-KkumKYW!wuV?R{6Ro;3o)fie8s5fzJ9@TS;JjMJ>sD z8%aAuSXCQ|m-FHdv66U=?&xRJ?-3pTQC2%yl0AE#Vv7*de1oD z#FI+xy*n&{vjR8Uk5Jr#J0BOTpIEl5kQT4=TrYQzjDHIQ4~O1?42*GB87u~Q+;

    4Stra_0{|9E@I&ylE$QT7n@mpWz}t_+b|hf?u6UJte!Lf6*>`Ptq+ci$a$@wYu z6f=HIJ|7M7+cX?ztvkG}fzQ!aKC9fQtbK$Vsa94B>VYtu4z)0j4@S=_Hjd{Oc$}4zqrv slYE*9IW-F(opf868?%`CjZS(j`_fq$m&In;&FKE$UNPTC?&81RzY6${!vFvP literal 0 HcmV?d00001 diff --git a/com32/modules/sdi.c b/com32/modules/sdi.c new file mode 100644 index 0000000..a5df4da --- /dev/null +++ b/com32/modules/sdi.c @@ -0,0 +1,194 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * 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, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * sdi.c + * + * Loader for the Microsoft System Deployment Image (SDI) format. + * Based on a historical patch by Remi Lefevre. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DEBUG 0 +#if DEBUG +# define dprintf printf +#else +# define dprintf(f, ...) ((void)0) +#endif + +typedef uint8_t guid_t[16]; + +struct SDIHeader { + uint32_t Signature; + char Version[4]; + uint64_t MDBtype; + uint64_t BootCodeOffset; + uint64_t BootCodeSize; + uint64_t VendorID; + uint64_t DeviceID; + guid_t DeviceModel; + uint64_t DeviceRole; + uint64_t Reserved1; + guid_t RuntimeGUID; + uint64_t RuntimeOEMrev; + uint64_t Reserved2; + uint64_t PageAlignment; /* BLOB alignment value in pages */ + uint64_t Reserved3[48]; + uint64_t Checksum; +}; + +#define SDI_LOAD_ADDR (16 << 20) /* 16 MB */ +#define SDI_SIGNATURE ('$' + ('S' << 8) + ('D' << 16) + ('I' << 24)) + +static inline void error(const char *msg) +{ + fputs(msg, stderr); +} + +static int boot_sdi(void *ptr, size_t len) +{ + const struct SDIHeader *hdr = ptr; + struct syslinux_memmap *mmap = NULL, *amap = NULL; + struct syslinux_rm_regs regs; + struct syslinux_movelist *ml = NULL; + char *boot_blob; + + /* **** Basic sanity checking **** */ + if (hdr->Signature != SDI_SIGNATURE) { + fputs("No $SDI signature in file\n", stdout); + goto bail; + } + if (memcmp(hdr->Version, "0001", 4)) { + int i; + fputs("Warning: unknown SDI version: ", stdout); + for (i = 0; i < 4; i++) + putchar(hdr->Version[i]); + putchar('\n'); + /* Then try anyway... */ + } + + /* **** Setup **** */ + mmap = syslinux_memory_map(); + amap = syslinux_dup_memmap(mmap); + if (!mmap || !amap) + goto bail; + + /* **** Map the BOOT BLOB to 0x7c00 **** */ + if (!hdr->BootCodeOffset) { + fputs("No BOOT BLOB in image\n", stdout); + goto bail; + } + if (!hdr->BootCodeSize) { + fputs("BOOT BLOB is empty\n", stdout); + goto bail; + } + if (len < hdr->BootCodeOffset + hdr->BootCodeSize) { + fputs("BOOT BLOB extends beyond file\n", stdout); + goto bail; + } + + if (syslinux_memmap_type(amap, 0x7c00, hdr->BootCodeSize) != SMT_FREE) { + fputs("BOOT BLOB too large for memory\n", stdout); + goto bail; + } + if (syslinux_add_memmap(&amap, 0x7c00, hdr->BootCodeSize, SMT_ALLOC)) + goto bail; + if (syslinux_add_movelist(&ml, 0x7c00, (addr_t)ptr + hdr->BootCodeOffset, + hdr->BootCodeSize)) + goto bail; + + /* **** Map the entire image to SDI_LOAD_ADDR **** */ + if (syslinux_memmap_type(amap, SDI_LOAD_ADDR, len) != SMT_FREE) { + fputs("Image too large for memory\n", stdout); + goto bail; + } + if (syslinux_add_memmap(&amap, SDI_LOAD_ADDR, len, SMT_ALLOC)) + goto bail; + if (syslinux_add_movelist(&ml, SDI_LOAD_ADDR, (addr_t)ptr, len)) + goto bail; + + /* **** Set up registers **** */ + memset(®s, 0, sizeof regs); + regs.ip = 0x7c00; + regs.esp.l = 0x7c00; + regs.edx.l = SDI_LOAD_ADDR | 0x41; + + fputs("Booting...\n", stdout); + syslinux_shuffle_boot_rm(ml, mmap, 0, ®s); + + bail: + syslinux_free_memmap(amap); + syslinux_free_memmap(mmap); + syslinux_free_movelist(ml); + return -1; +} + +/* + * Check that the sum of all bytes from first 512 bytes (SDI header) + * is 0 modulo 256. + */ +int has_valid_header(unsigned char *header) +{ + unsigned char checksum; + unsigned int i; + + checksum = 0; + for (i = 0; i < sizeof(struct SDIHeader); i++) + checksum += header[i]; + return (!checksum); +} + +int main(int argc, char *argv[]) +{ + void *data; + size_t data_len; + + openconsole(&dev_null_r, &dev_stdcon_w); + + if (argc != 2) { + error("Usage: sdi.c32 sdi_file\n"); + return 1; + } + + fputs("Loading ", stdout); + fputs(argv[1], stdout); + fputs("... ", stdout); + if (zloadfile(argv[1], &data, &data_len)) { + error("failed!\n"); + return 1; + } + fputs("ok\n", stdout); + + if (!has_valid_header(data)) { + error("SDI header is corrupted\n"); + return 1; + } + + boot_sdi(data, data_len); + error("Invalid SDI file or insufficient memory\n"); + return 1; +} diff --git a/com32/modules/sdi.c32 b/com32/modules/sdi.c32 new file mode 100755 index 0000000000000000000000000000000000000000..ea72effc030bb7d45db9c2ab2a7c6f14334fa317 GIT binary patch literal 24616 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 +#include +#include +#include +#include +#include "../lib/sys/vesa/vesa.h" + +/* Wait for a keypress */ +static void wait_key(void) +{ + char ch; + while (fread(&ch, 1, 1, stdin) == 0) + ; +} + +static void print_modes(void) +{ + static com32sys_t rm; + struct vesa_general_info *gi; + struct vesa_mode_info *mi; + uint16_t mode, *mode_ptr; + int lines; + + /* Allocate space in the bounce buffer for these structures */ + gi = &((struct vesa_info *)__com32.cs_bounce)->gi; + mi = &((struct vesa_info *)__com32.cs_bounce)->mi; + + gi->signature = VBE2_MAGIC; /* Get VBE2 extended data */ + rm.eax.w[0] = 0x4F00; /* Get SVGA general information */ + rm.edi.w[0] = OFFS(gi); + rm.es = SEG(gi); + __intcall(0x10, &rm, &rm); + + if ( rm.eax.w[0] != 0x004F ) { + printf("No VESA BIOS detected\n"); + return; + } else if ( gi->signature != VESA_MAGIC ) { + printf("VESA information structure has bad magic, trying anyway...\n"); + } + + printf("VBE version %d.%d\n" + "Mode attrib h_res v_res bpp layout rpos gpos bpos\n", + (gi->version >> 8) & 0xff, gi->version & 0xff); + + lines = 1; + + mode_ptr = GET_PTR(gi->video_mode_ptr); + + while ((mode = *mode_ptr++) != 0xFFFF) { + if (++lines >= 23) { + wait_key(); + lines = 0; + } + + rm.eax.w[0] = 0x4F01; /* Get SVGA mode information */ + rm.ecx.w[0] = mode; + rm.edi.w[0] = OFFS(mi); + rm.es = SEG(mi); + __intcall(0x10, &rm, &rm); + + /* Must be a supported mode */ + if ( rm.eax.w[0] != 0x004f ) + continue; + + printf("0x%04x 0x%04x %5u %5u %3u %6u %4u %4u %4u\n", + mode, mi->mode_attr, mi->h_res, mi->v_res, mi->bpp, + mi->memory_layout, mi->rpos, mi->gpos, mi->bpos); + } +} + +int main(void) +{ + openconsole(&dev_rawcon_r, &dev_stdcon_w); + + print_modes(); + return 0; +} diff --git a/com32/modules/vesainfo.c32 b/com32/modules/vesainfo.c32 new file mode 100755 index 0000000000000000000000000000000000000000..28a8b92be0e46dfb18bd868948ab140e06fbbe64 GIT binary patch literal 4872 zcmb7Ie^3S;x3U3E!8DY-LfAdt=hCz6K5Lrf$}V^BazE=piGK!S2~ zxt8blGInAo%}m;9#%3lnIvJ;(QM8R7f`{7bAZh#&(uAaGy>m@V)C!jxx1YDSAW7{X zy_vV~z3=<}{(j$k-*-KF`PoIcU7_RlB|LNF5-h=0tt25@xk8sy0L@l|F|;ZwAXnR{3mH zf8q8M>z}NwP_E@2eS0y_^n~zbE_>3o48il00JifbGfRRm;*FvVXZaXrE!aQ>NvVWM;K=`471Rb`Qs z+_RE`Ujn3FEn(MkhX&F5z1v=(;MD-Dl{3g-Z-nn1VY$OY!E9djh}jSbo%mx|XOwHK&wR5AmMG(_ zG*cN*k$Ei{wkBJ`mVvCkz_5Itj4S=Alw02(@IlBO>N=6{fi6tsK|_VqzCC;B+ED}c z8X^`5wIwR2bP)C(p+LizJ%^w*K+#_UV^3x0HHRNr(FYehD#-bx3}%s~217m&YE2_2 z9@aY;W>>;d+5yW>E=M+lw6ot1)Qu2IZR@=D251n-OtMZC)i=b0bb1B==YU7d#e_kh zXZD3=?>2jn*_Xj@I`F)7?$VWeB9SZ4UfWfXIA04OuihhM%rj(0b zYji2l=!lI9=E!d3K85koR1+pdt>(xQ9S4J8(O_Xx%be%diJoNcSZ~l{9fU_b(GRia zk1drK1)EuQ0jU-}fnkoQkTy0@rmy2%lQS=xcB0ky;1z)Rn4IywVs!gq=$7Eid zhI-8qrZr8D_zCSNG&J>sJ}u?YrHblu$fnu2!-hDZ8QV~sug-Rz2H!kGcIr3m*7$qK z&W3q3jl}$Q*i)%$xhO6x7jwZN8*FR+5T&+@-p(t2;K71zGS#?tFW|SZyWLWv0<)Ob z`Oq;BdJueWD8i|%>lQeb zzYaEbV5Vw|D=KtJbkK$c9?Tq(Hsl8`7{ZD`VM_sHQyilFJ`lXUk%aDE|0foShhpV+ zIVo6bDUignoh(+%^20k>64p2fl~N#BPB3mOB8jck9J@3Vhdt6i^2 zJfutXQ@e3)xk8$g_Tb?me=xwVPyw#eW;SdUirxB8nR*^>@b3 zKLU-)bSW93DBHzCtFruZ(X(p}jh^@J7-X%m*<-_U zo|a>beHdpBxUu(fsw{x4d)EQ+yUnF*({cAxWQ~OuK5}Dsa`0ITl=XF;<^qjlx<1Jw z2?)-xmC+oCh{zz?)nX=;62qPpomL#Vo&B^ZGKx0k8viUak#P~+Ovtfxvv#6&Vf;`6 zkpQlukpr$_av>=!^Jf{?EhtuL3C$d`oSr!x7;3v|K*RzZVJ)+o5(-$cB_)d$(=fN3 zoB1(!V9;{@DtF+r<-A7zl5|$S01Y!4iC0+>>}k?u5(}wd$td}9vaCeyDA2l+o}AgV zMKN44bQj{as z;0E@Q{BzCIND~{}U?^p6eS9$FlOG|w|-CaLMdS6Y0AqiBh4%A?4wzu+!5qxJXPiaPpclV{TZj#KnWW` z7c9vpC1T)nX{P!F9~hGC{z6)ou==sc={$7*;~c_ck0?`;yy*6YJ1jC0p^}W`3^fO*K>>H&o9UFtDkS@Y&bbl8&UH#&(5n?BB3)6O|Wt%vBsD*h2t( zeaI;n1pR9DLwpoZ=BJDQ%aks5Yv7n-Fed5a`B)d|~bnuX0Z zwZii?TN}1y3sO^C!}hI0&GxpInzo#r90vbv2rt$(HB+jL+MJBq$F&B9jt*^Eyz<7~}v=C%rEm$5 zEt$#OF8h30h|${FJo6UX9kdz;#%LoLA2Qq3DPsz|@QwMO%=w8a@S7N0=kkmLEdy;O z+G?~{&|XFR4cc$f`p_O-z%%R7YS9|e+R>b@H!Td^jBiYVU%6>FU<^+4iQko*sRlhi1xD9COXryn@vql^ E0KDs$^#A|> literal 0 HcmV?d00001 diff --git a/com32/modules/vpdtest.c b/com32/modules/vpdtest.c new file mode 100644 index 0000000..81a9fae --- /dev/null +++ b/com32/modules/vpdtest.c @@ -0,0 +1,68 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- +*/ + +/* + * vpdtest.c + * + * VPD demo program using libcom32 + */ + +#include +#include +#include +#include "vpd/vpd.h" + +int main(void) +{ + char buffer[1024]; + s_vpd vpd; + openconsole(&dev_stdcon_r, &dev_stdcon_w); + + if (vpd_decode(&vpd) == -ENOVPDTABLE) { + printf("No VPD Structure found\n"); + return -1; + } else { + printf("VPD present at address : 0x%s\n",vpd.base_address); + } + if (strlen(vpd.bios_build_id)>0) + printf("Bios Build ID : %s\n",vpd.bios_build_id); + if (strlen(vpd.bios_release_date)>0) + printf("Bios Release Date : %s\n",vpd.bios_release_date); + if (strlen(vpd.bios_version)>0) + printf("Bios Version : %s\n",vpd.bios_version); + if (strlen(vpd.default_flash_filename)>0) + printf("Default Flash Filename : %s\n",vpd.default_flash_filename); + if (strlen(vpd.box_serial_number)>0) + printf("Box Serial Number : %s\n",vpd.box_serial_number); + if (strlen(vpd.motherboard_serial_number)>0) + printf("Motherboard Serial Number : %s\n",vpd.motherboard_serial_number); + if (strlen(vpd.machine_type_model)>0) + printf("Machine Type/Model : %s\n",vpd.machine_type_model); + + return 0; +} diff --git a/com32/modules/vpdtest.c32 b/com32/modules/vpdtest.c32 new file mode 100755 index 0000000000000000000000000000000000000000..ff48e98c905687a9e4d57ed36af178dd28a5a355 GIT binary patch literal 5772 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 fxu%V0@1afe{@geJ&j~z$FFxYnO7XwW bpos) { + memcpy(dest, src + bpos, epos-bpos); + if (dest[epos - bpos] != 0) + dest[epos - bpos] = 0; + } else { + epos = strlen(src); + dest[0] = 0; + } + return epos; +} + +/* Handle most/all errors + * ierrno Input Error number + * cmdstr Command being executed to cause error + * filestr File/parameter causing error + */ +void rosh_error(const int ierrno, const char *cmdstr, const char *filestr) +{ + printf("--ERROR: %s '%s': ", cmdstr, filestr); + switch (ierrno) { + case EACCES: printf("Access DENIED\n"); + break; + case ENOENT: printf("not found\n"); + /* SYSLinux-3.72 COM32 API returns this for a + directory or empty file */ + ROSH_COM32(" (COM32) could be a directory or empty file\n"); + break; + case ENOTDIR: printf("not a directory\n"); + ROSH_COM32(" (COM32) could be directory\n"); + break; + case ENOSYS: printf("not implemented"); + break; + default: printf("returns error; errno=%d\n", ierrno); + } +} + +/* Concatenate command line arguments into one string + * cmdstr Output command string + * argc Argument Count + * argv Argument Values + * barg Beginning Argument + */ +int rosh_argcat(char *cmdstr, const int argc, char *argv[], const int barg) +{ + int i, arglen, curpos; /* index, argument length, current position + in cmdstr */ + curpos = 0; + cmdstr[0] = '\0'; /* Nullify string just to be sure */ + for (i = barg; i < argc; i++) { + arglen = strlen(argv[i]); + /* Theoretically, this should never be met in SYSLINUX */ + if ((curpos + arglen) > (ROSH_CMD_SZ - 1)) + arglen = (ROSH_CMD_SZ - 1) - curpos; + memcpy(cmdstr + curpos, argv[i], arglen); + curpos += arglen; + if (curpos >= (ROSH_CMD_SZ - 1)) { + /* Hopefully, curpos should not be greater than + (ROSH_CMD_SZ - 1) */ + /* Still need a '\0' at the last character */ + cmdstr[(ROSH_CMD_SZ - 1)] = 0; + break; /* Escape out of the for() loop; + We can no longer process anything more */ + } else { + cmdstr[curpos] = ' '; + curpos += 1; + cmdstr[curpos] = 0; + } + } + /* If there's a ' ' at the end, remove it. This is normal unless + the maximum length is met/exceeded. */ + if (cmdstr[curpos - 1] == ' ') + cmdstr[--curpos] = 0; + return curpos; +} /* rosh_argcat */ + +/* + * Prints a lot of the data in a struct termios + */ +/* +void rosh_print_tc(struct termios *tio) +{ + printf(" -- termios: "); + printf(".c_iflag=%04X ", tio->c_iflag); + printf(".c_oflag=%04X ", tio->c_oflag); + printf(".c_cflag=%04X ", tio->c_cflag); + printf(".c_lflag=%04X ", tio->c_lflag); + printf(".c_cc[VTIME]='%d' ", tio->c_cc[VTIME]); + printf(".c_cc[VMIN]='%d'", tio->c_cc[VMIN]); + printf("\n"); +} +*/ + +/* + * Switches console over to raw input mode. Allows get_key to get just + * 1 key sequence (without delay or display) + */ +void rosh_console_raw() +{ +// struct termios itio, ntio; +// tcgetattr(0, &itio); +// rosh_print_tc(&itio); +/* ntio = itio; + ntio.c_lflag &= ~(ICANON|ECHO); + tcsetattr(0, TCSAFLUSH, &ntio);*/ + console_ansi_raw(); /* Allows get_key to get just 1 key sequence + (w/o delay or display */ +// tcgetattr(0, &ntio); +// rosh_print_tc(&ntio); +} + +/* + * Switches back to standard getline mode. + */ +void rosh_console_std() +{ +// struct termios itio, ntio; + console_ansi_std(); +// tcsetattr(0, TCSANOW, &itio); +} + +/* + * Attempts to get a single key from the console + * returns key pressed + */ +int rosh_getkey() +{ + int inc; + + inc = KEY_NONE; +// rosh_console_raw(); + while (inc == KEY_NONE){ + inc = get_key(stdin, 6000); + } +// rosh_console_std(); + return inc; +} /* rosh_getkey */ + +/* Template for command functions + * cmdstr command string to process + * pwdstr Present Working Directory string + * ipwdstr Initial PWD + */ +void rosh_1(const char *cmdstr, const char *pwdstr, const char *ipwdstr) +{ + ROSH_DEBUG("CMD: '%s'\npwd: '%s'\npwd: '%s'\n", cmdstr, pwdstr, \ + ipwdstr); +} /* rosh_1 */ + +/* Concatenate multiple files to stdout + * cmdstr command string to process + * pwdstr Present Working Directory string + */ +void rosh_cat(const char *cmdstr, const char *pwdstr) +{ + FILE *f; + char filestr[ROSH_PATH_SZ + 1]; + char buf[ROSH_BUF_SZ]; + int numrd; + int cmdpos; + + ROSH_DEBUG("CMD: '%s'\npwd: '%s'\n", cmdstr, pwdstr); + /* Initialization */ + filestr[0] = 0; + cmdpos = 0; + /* skip the first word */ + cmdpos = rosh_parse_sp_1(filestr, cmdstr, cmdpos); + cmdpos = rosh_parse_sp_1(filestr, cmdstr, cmdpos); + while (strlen(filestr) > 0) { + printf("--File = '%s'\n", filestr); + f = fopen(filestr, "r"); + if (f != NULL) { + numrd = fread(buf, 1, ROSH_BUF_SZ, f); + while (numrd > 0) { + fwrite(buf, 1, numrd, stdout); + numrd = fread(buf, 1, ROSH_BUF_SZ, f); + } + fclose(f); + } else { + rosh_error(errno, "cat", filestr); + } + cmdpos = rosh_parse_sp_1(filestr, cmdstr, cmdpos); + } +} /* rosh_cat */ + +/* Change PWD (Present Working Directory) + * cmdstr command string to process + * pwdstr Present Working Directory string + * ipwdstr Initial PWD + */ +void rosh_cd(const char *cmdstr, char *pwdstr, const char *ipwdstr) +{ + int rv; + char filestr[ROSH_PATH_SZ + 1]; + int cmdpos; + ROSH_DEBUG("CMD: '%s'\npwd: '%s'\n", cmdstr, pwdstr); + /* Initialization */ + filestr[0] = 0; + cmdpos = 0; + rv = 0; + /* skip the first word */ + cmdpos = rosh_parse_sp_1(filestr, cmdstr, cmdpos); + cmdpos = rosh_parse_sp_1(filestr, cmdstr, cmdpos); + ROSH_COM32(" -- cd (Change Directory) not implemented for use with run and exit.\n"); + if (strlen(filestr) != 0) + rv = chdir(filestr); + else + rv = chdir(ipwdstr); + if (rv != 0) { + rosh_error(errno, "cd", filestr); + } else { + getcwd(pwdstr, ROSH_PATH_SZ + 1); + printf(" %s\n", pwdstr); + } +} /* rosh_cd */ + +/* Print the syslinux config file name + * cmdstr command string to process + * pwdstr Present Working Directory string + */ +void rosh_cfg(const char *cmdstr, const char *pwdstr) +{ + ROSH_DEBUG("CMD: '%s'\npwd: '%s'\n", cmdstr, pwdstr); + printf("CFG: '%s'\n", syslinux_config_file()); +} /* rosh_cfg */ + +/* Simple directory listing for one argument (file/directory) based on + * filestr and pwdstr + * ifilstr input filename/directory name to list + * pwdstr Present Working Directory string + */ +void rosh_dir_arg(const char *ifilstr, const char *pwdstr) +{ + struct stat fdstat; + int status; + int fd; + char filestr[ROSH_PATH_SZ + 1]; + int filepos; + DIR *d; + struct dirent *de; +#ifdef DO_DEBUG + char filestr2[ROSH_PATH_SZ + 1]; + int fd2, file2pos; +#ifdef __COM32__ +// int inchar; + char ty; +#endif /* __COM32__ */ +#endif /* DO_DEBUG */ + + /* Initialization; make filestr based on leading character of ifilstr + and pwdstr */ + if (ifilstr[0] == SEP) { + strcpy(filestr, ifilstr); + } else { + strcpy(filestr, pwdstr); + filepos = strlen(pwdstr); + if (filestr[filepos-1] != SEP) + filestr[filepos++] = SEP; + strcpy(filestr + filepos, ifilstr); +ROSH_DEBUG("--'%s'\n", filestr); + } + fd = open(filestr, O_RDONLY); + if (fd != -1) { + status = fstat(fd, &fdstat); + if (S_ISDIR(fdstat.st_mode)) { + ROSH_DEBUG("PATH '%s' is a directory\n", ifilstr); + d = fdopendir(fd); + de = readdir(d); + while (de != NULL) { +#ifdef DO_DEBUG + filestr2[0] = 0; + file2pos = strlen(filestr); + memcpy(filestr2, filestr, file2pos); + filestr2[file2pos] = '/'; + strcpy(filestr2+file2pos+1, de->d_name); + fd2 = open(filestr2, O_RDONLY); + status = fstat(fd2, &fdstat); + printf("@%8d:%8d:", (int)de->d_ino, (int)fdstat.st_size); + fd2 = close(fd2); +#endif /* DO_DEBUG */ + printf("%s\n", de->d_name); +#ifdef DO_DEBUG +// inchar = fgetc(stdin); +#endif /* DO_DEBUG */ + de = readdir(d); + } + closedir(d); + } else if (S_ISREG(fdstat.st_mode)) { + ROSH_DEBUG("PATH '%s' is a regular file\n", ifilstr); + printf("%8d:%s\n", (int)fdstat.st_size, ifilstr); + } else { + ROSH_DEBUG("PATH '%s' is some other file\n", ifilstr); + printf(" :%s\n", ifilstr); + } + } else { +#ifdef __COM32__ + if (filestr[strlen(filestr)-1] == SEP) { + /* Directory */ + filepos = 0; + d = opendir(filestr); + if (d != NULL) { +printf("DIR:'%s' %8d %8d\n", d->dd_name, d->dd_fd, d->dd_sect); + de = readdir(d); + while (de != NULL) { + filepos++; +#ifdef DO_DEBUG +// if (strlen(de->d_name) > 25) de->d_name[25] = 0; + switch (de->d_mode) { + case 16 : ty = 'D'; break; + case 32 : ty = 'F'; break; + default : ty = '*'; + } + printf("@%8d:%8d:%4d ", (int)de->d_ino, (int)de->d_size, de->d_mode); +#endif /* DO_DEBUG */ +// printf("%s\n", de->d_name); +printf("'%s'\n", de->d_name); +#ifdef DO_DEBUG +// inchar = fgetc(stdin); +// fgets(instr, ROSH_CMD_SZ, stdin); +#endif /* DO_DEBUG */ + free(de); + de = readdir(d); +// if(filepos>15){ de = NULL; printf("Force Break\n");} + } +printf("Dir.dd_fd: '%8d'\n", d->dd_fd); + closedir(d); + } else { + rosh_error(0, "dir:NULL", filestr); + } + } else { + rosh_error(errno, "dir_c32", filestr); + } +#else + rosh_error(errno, "dir", filestr); +#endif /* __COM32__ */ + } +} /* rosh_dir_arg */ + +/* Simple directory listing based on cmdstr and pwdstr + * cmdstr command string to process + * pwdstr Present Working Directory string + */ +void rosh_dir(const char *cmdstr, const char *pwdstr) +{ + char filestr[ROSH_PATH_SZ + 1]; + int cmdpos; /* Position within cmdstr */ + + ROSH_DEBUG("CMD: '%s'\npwd: '%s'\n", cmdstr, pwdstr); + /* Initialization */ + filestr[0] = 0; + cmdpos = 0; + /* skip the first word */ + cmdpos = rosh_parse_sp_1(filestr, cmdstr, cmdpos); + cmdpos = rosh_parse_sp_1(filestr, cmdstr, cmdpos); + /* If there are no real arguments, substitute PWD */ + if (strlen(filestr) == 0) + strcpy(filestr, pwdstr); + while (strlen(filestr) > 0) { + rosh_dir_arg(filestr, pwdstr); + cmdpos = rosh_parse_sp_1(filestr, cmdstr, cmdpos); + } +} /* rosh_dir */ + +/* List Directory; Calls rosh_dir() for now. + * cmdstr command string to process + * pwdstr Present Working Directory string + */ +void rosh_ls(const char *cmdstr, const char *pwdstr) +{ + printf(" ls implemented as dir (for now)\n"); + rosh_dir(cmdstr, pwdstr); +} /* rosh_ls */ + +/* Page through a buffer string + * buf Buffer to page through + */ +void rosh_more_buf(char *buf, int buflen, int rows, int cols) +{ + char *bufp, *bufeol; /* Pointer to current and next end-of-line + position in buffer */ + int bufpos, bufcnt; /* current position, count characters */ + char scrbuf[ROSH_SBUF_SZ]; + int inc; + int i, numln; /* Index, Number of lines */ + + bufpos = 0; + bufp = buf + bufpos; + bufeol = bufp; + numln = rows - 1; +printf("--(%d)\n", buflen); +// printf("--termIOS CONSTS: "); +// printf("ISIG=%08X ", ISIG); +// printf("ICANON=%08X ", ICANON); +// printf("ECHO=%08X ", ECHO); +// printf("=%08X", ); +// printf("\n"); + while (bufpos < buflen) { + for (i=0; i 0) { + bufpos += numrd; + numrd = fread(buf+bufpos, 1, \ + ((int)fdstat.st_size - bufpos), f); + } + fclose(f); + rosh_more_buf(buf, bufpos, rows, cols); + } + } else { + } + +} /* rosh_more_fd */ + +/* Page through a file like the more command + * cmdstr command string to process + * pwdstr Present Working Directory string + * ipwdstr Initial PWD + */ +void rosh_more(const char *cmdstr, const char *pwdstr) + /*, const char *ipwdstr)*/ +{ + int fd; + char filestr[ROSH_PATH_SZ + 1]; + int cmdpos; + int rows, cols; + + ROSH_DEBUG("CMD: '%s'\npwd: '%s'\n", cmdstr, pwdstr); + /* Initialization */ + filestr[0] = 0; + cmdpos = 0; + if (getscreensize(1, &rows, &cols)) { + ROSH_DEBUG("getscreensize() fail; fall back\n"); + ROSH_DEBUG("\tROWS='%d'\tCOLS='%d'\n", rows, cols); + /* If either fail, go under normal size, just in case */ + if (!rows) + rows = 20; + if (!cols) + cols = 75; + } + ROSH_DEBUG("\tROWS='%d'\tCOLS='%d'\n", rows, cols); + + /* skip the first word */ + cmdpos = rosh_parse_sp_1(filestr, cmdstr, cmdpos); + cmdpos = rosh_parse_sp_1(filestr, cmdstr, cmdpos); + if (strlen(filestr) > 0) { + /* There is no need to mess up the console if we don't have a + file */ + rosh_console_raw(); + while (strlen(filestr) > 0) { + printf("--File = '%s'\n", filestr); + fd = open(filestr, O_RDONLY); + if (fd != -1) { + rosh_more_fd(fd, rows, cols); + close(fd); + } else { + rosh_error(errno, "more", filestr); + } + cmdpos = rosh_parse_sp_1(filestr, cmdstr, cmdpos); + } + rosh_console_std(); + } +} /* rosh_more */ + +/* Page a file with rewind + * cmdstr command string to process + * pwdstr Present Working Directory string + * ipwdstr Initial PWD + */ +void rosh_less(const char *cmdstr, const char *pwdstr) +{ + printf(" less implemented as more (for now)\n"); + rosh_more(cmdstr, pwdstr); +} /* rosh_less */ + +/* Show PWD + * cmdstr command string to process + * pwdstr Present Working Directory string + */ +void rosh_pwd(const char *cmdstr, const char *pwdstr) +{ + int istr; + ROSH_DEBUG("CMD: '%s'\npwd: '%s'\n", cmdstr, pwdstr); + printf("%s\n", pwdstr); + istr = htonl(*(int*)pwdstr); + ROSH_DEBUG(" --%08X\n", istr); +} /* rosh_pwd */ + +/* Run a boot string, calling syslinux_run_command + * cmdstr command string to process + * pwdstr Present Working Directory string + * ipwdstr Initial PWD + */ +void rosh_run(const char *cmdstr, const char *pwdstr, const char *ipwdstr) +{ + int cmdpos; + char *cmdptr; + char istr[ROSH_CMD_SZ]; /* input command string */ + + cmdpos = 0; + ROSH_DEBUG("CMD: '%s'\npwd: '%s'\n", cmdstr, pwdstr); + /* skip the first word */ + cmdpos = rosh_search_sp(cmdstr, cmdpos); + /* skip spaces */ + cmdpos = rosh_search_nonsp(cmdstr, cmdpos); + cmdptr = (char *)(cmdstr + cmdpos); + printf("--run: '%s'\n", cmdptr); + /* //HERE--Reparse if pwdstr != ipwdstr; seems a little daunting as + detecting params vs filenames is difficult/impossible */ + if (strcmp(pwdstr, ipwdstr) != 0) { + /* For now, just prompt for verification */ + printf(" from directory '%s'? (y/N):", pwdstr); + fgets(istr, ROSH_CMD_SZ, stdin); + if ((istr[0] != 'y') && (istr[0] != 'Y')) { + printf("Aborting run\n"); + return; + } + printf("Run anyways\n"); + } + syslinux_run_command(cmdptr); +} /* rosh_run */ + +/* Process a single command string and call handling function + * cmdstr command string to process + * pwdstr Present Working Directory string + * ipwdstr Initial Present Working Directory string + * returns Whether to exit prompt + */ +char rosh_command(const char *cmdstr, char *pwdstr, const char *ipwdstr) +{ + char do_exit; + do_exit = false; + ROSH_DEBUG("--cmd:'%s'\n", cmdstr); + switch (cmdstr[0]) { + case 'e': case 'E': case 'q': case 'Q': + do_exit = true; break; + case 'c': case 'C': /* run 'cd' 'cat' 'cfg' */ + switch (cmdstr[1]) { + case 'a': case 'A': + rosh_cat(cmdstr, pwdstr); break; + case 'd': case 'D': + rosh_cd(cmdstr, pwdstr, ipwdstr); break; + case 'f': case 'F': + rosh_cfg(cmdstr, pwdstr); break; + default: rosh_help(1); + } + break; + case 'd': case 'D': /* run 'dir' */ + rosh_dir(cmdstr, pwdstr); break; + case 'h': case 'H': case '?': rosh_help(2); + break; + case 'l': case 'L': /* run 'ls' 'less' */ + switch (cmdstr[1]) { + case 0: case 's': case 'S': + rosh_ls(cmdstr, pwdstr); break; + case 'e': case 'E': + rosh_less(cmdstr, pwdstr); break; + default: rosh_help(1); + } + break; + case 'm': case 'M': + switch (cmdstr[1]) { + case 'a': case 'A': + rosh_help(2); + break; + case 'o': case 'O': + rosh_more(cmdstr, pwdstr); + break; + default: rosh_help(1); + } + break; + case 'p': case 'P': /* run 'pwd' */ + rosh_pwd(cmdstr, pwdstr); break; + case 'r': case 'R': /* run 'run' */ + rosh_run(cmdstr, pwdstr, ipwdstr); break; + case 'v': case 'V': + rosh_version(); break; + case 0: case '\n': break; + default : rosh_help(1); + } /* switch(cmdstr[0]) */ + return do_exit; +} /* rosh_command */ + +/* Process the prompt for commands as read from stdin and call rosh_command + * to process command line string + * icmdstr Initial command line string + * returns Exit status + */ +int rosh_prompt(const char *icmdstr) +{ + int rv; + char cmdstr[ROSH_CMD_SZ]; + char pwdstr[ROSH_PATH_SZ + 1], ipwdstr[ROSH_PATH_SZ + 1]; +/* int numchar; +*/ char do_exit; + char *c; + + rv = 0; + do_exit = false; + strcpy(pwdstr, "/"); + getcwd(pwdstr, ROSH_PATH_SZ + 1); + strcpy(ipwdstr, pwdstr); /* Retain the original PWD */ + if (icmdstr[0] != '\0') + do_exit = rosh_command(icmdstr, pwdstr, ipwdstr); + while (!(do_exit)) { + console_ansi_std(); + printf("\nrosh: "); + /* Read a line from console */ + fgets(cmdstr, ROSH_CMD_SZ, stdin); + /* remove newline from input string */ + c = strchr(cmdstr, '\n'); + *c = 0; + do_exit = rosh_command(cmdstr, pwdstr, ipwdstr); + } + if (strcmp(pwdstr, ipwdstr) != 0) { + /* Change directory to the original directory */ + strcpy(cmdstr, "cd "); + strcpy(cmdstr + 3, ipwdstr); + rosh_cd(cmdstr, pwdstr, ipwdstr); + } + return rv; +} + +int main(int argc, char *argv[]) +{ + int rv; + char cmdstr[ROSH_CMD_SZ]; + + /* Initialization */ + rv = 0; + console_ansi_std(); +// console_ansi_raw(); + if (argc != 1) { + rv = rosh_argcat(cmdstr, argc, argv, 1); + } else { + rosh_version(); + cmdstr[0] = '\0'; + } + rv = rosh_prompt(cmdstr); + printf("--Exiting '%s'\n", APP_NAME); + console_ansi_std(); + return rv; +} diff --git a/com32/rosh/rosh.c32 b/com32/rosh/rosh.c32 new file mode 100755 index 0000000000000000000000000000000000000000..980d3a6dae6b22193519a1565137ae5f18f15366 GIT binary patch literal 17264 zcmcJ04|G)3)$f_)hD?%yJIDkA#E**%nrP4rMonPQNivWC5hn=*BvgweF=!x|+#yhr z4BnZVlk2p6efDd+o?k1VZ}nBbmcF_$M5_sbMEo1TKm73z{%M`@f(D_GfOXz)pF4p- zZP)tNTW?nGJ^Svn&p!L?v(G;J{7FZw?9Hj)y1HIjS7~Rvr>w|e*+bpYgwxN4Rj2gI zQ-n-rjPd)wojqoKfFUh9>NQ_Ge| zj;ApeOV0%5nYKOd6gAn^6*#Z>lR7M5*JJ5#w+4n%RJ;^5k@xp_ij0$%e#25^;*)^) z3w&_D-!RVhL?_!A(@r>Ix71YGS$#u^okgr;f!dqdqn*g8e;A1R5WU*z^#r}1#w+rf zorrRJDzy_f-cw#tznidc3ankbFj`G-P1Y_X_)Cm&+69L?MZ0jVf3|T`#~UCsl#ZiB z{UWG4J|~3SF}4(l-w|RepyhX0NB@&}ov_v4j&H;2^HtU_BAN$86&ck%ORFlkoM2Wc zv4_9M-)^_N_8U@RyCW@dK1EG|QshvWRQxJXVVl5S3#>ZT(nHMdCuUcJ89&blBC6nO z4|VyiMrEO=eXQ~J(xp}PDiICJjD@{U`)*QkC)F7F5WUb2l(yII-l%4{?^YchKO@=* zXt(Son(BM*?dkXp;obp`AC7F9&RFoE9J)Iei$&}3BF*(jb|F{WYwKBp_Gl+Ex#Z-F zoetgW%+(#v-UJr5ZQ*6k48G5G&{(Ad(kE*BWBg-R*EhdEcVIs9q_C7aCkAucZ0qwn zC4#aSJ0;!iw7kzhLJ{fo(}_T6UT221<@|SoQkR^)0U~9_INj##mBuPXc(J*&8=Q_! zztHoXflcFMFsuc=(s;)EVGwB14@m3U2?;8HAGNmI`CFuIne>|4?zk;%yNwSRDV}-l zcH^(mB4s9#eacibVep{8 z+jxUivgJf3)XsbP`+@wM&JBkI~8!Oe->SCSeGoRs{a#p{)?lbVZW=3 zAK_n9D+8NsSKZ`Sc_gxJ0#y*|Qdh**N%9Mx1Yfw=>Z|XfQXwieVd>KFJe#lHPh+JS z7}#kJ3=h?xqO!ec48%E7UM{~-oZzc}mr5=qy2FuIAu6(sOdjq00(e)KVFNe-aN7?t z(j(q)W3erJNw=a~O;y`WRlnQob=p{Xh12HR&-Z{^@^`Tq@8;hC{Pynv`vRLl;Gf37 z1m*`MYlZ+965WuXdq>b25^*u9KismH_>1aHBkJXcItEGT{{tt)kMe=LtM7?6VPuSu zriZ`F-!|n@Pc*&H{{}>toiG`x!V>;KNjbeI;EEi-jPsWFI-Lrxe_I&Iy34YA)yn<(p0BY-{j14orGH8lI)fNYGCx=A#`Ud zUcfmbPEevXHhfr-?cf)TBo6__1s66Q)>k+k*{7F)v7@p+M4W$%k{HQ3ypJC_{dBK4 zGY9_OSN{a%J&rufho}v1$gbWRal;xQL54P{sFPZgw82D-$iZ?I&_jMn2hl6&NT!23 zm3TyQ9W(fry>FmZ7www7Y7bgyqFu$R5zlKIp6f&y}&g+P+SGLZx=#0LhK19o^GR#F3# zfE^NeH-Y;w5FAWh7g<0Z{E<=Z^HrJFm<>a#UiN^cD)P1PKqM<{B2za-(oK;fduS{l zj#PsN)_1D&7ZQV|bg?S`uOn|K(&Ts1r(7E3Pg2*uN!pDM@)KZ=dBnkc z{&|G41@zipr6Xs7ogtD?lR7>Jo9gA&Q4KjLZRI`f%v~i1{{(fku6LKo!CMIOy1jC6 z3PCrxi{)S^6y?4{4n7Es{L??+9qf{We?;m|l|#Ql8hx5R=E}h~A}x)&=+h?$=Ob+$ z?Y>hEZ6vDEgf3U>+!b=Do)9w$(I5w3L9x~;?ixAxzX?ipuaZM0$TN!Ewd!r|`si`u z`;eN`n(kgB2lvs}IyvMd9)F>aCOP<5O4rLFnUJ62W2YrbaC@=EJx|SV9pf%kC!t}X zuaIYCx@!=1)TxSlr5ySI81$46jxBoBgkOXSMBf7J^Noz`ZiMr}#JSfVecq%?BD((t zxP74|FjT2dZk2=k)%3v7Tsag1+87rYs#V7XhMN5wQN>)48$SXt75!&CBnsW7X0|2= zh6?@53|nAmr3$#wUuGnCkY_VWK^%jFGQ0rN&HBeo!rcMB4FtWY`ji-I>%ju*G{%Hq5`x4l40fr% z6~6guP-946)A>3Hcs$}F_FiWqqLXU@3NFVZqcMtHT?iY|CU~tL%vh54`9SQUZ>uWH zFU7c#Mrc&KU{US-_yZPdM5OO;vDh*r`(J9ZAPEwCoeB*mi10FvYec~HZ$qT|cc{@5 zKoNnupu#PC?HF8`hr=ts?b*6@Yv($jopt2l6)hOgSp2(`zO(6@92@ypa{ceuY3D6< z+Q(>1WGC1mWIYRqJDKK%QV~2ywgC-&*gXE5wK)`rr1`H0^`k)7Wk*71)wDXe`1P{( zokeY~3&qr=x`M%$dm^jJ3;hX{%LHG9-jmzBdE@52m{n$HvzsOVc)ilfPeHZgB2(e; zFdb#;iAL5VBk(B#F9%m+O4)K@=OQ~(?OKn`=+a(AADYpvybBzRQ(NQhvSEsPkH3j9 za60B>2O>gvO1~Xps;jHug8cKYbBCw)8R>0_#oA!1nz{&Lx4wr~B!YquVv-#h>ZXNhjIC9bm|%Jmmq~iM73)tUT`!Uf zjy8`AO9=(t@}pshsaIIFSqc8X@^Y(QWaEW4zQCrJ<89R+LXO2hNneiT%A|sBzdiA8 zi7%IUla*K5_yJ>TpM@H_%?{SQ(B4<*U<8e(Z@tjYOC8$n7XJ-;Im(YM==D!ctde-S zq*vMXc@AFWptldOl^0s+T?*y*Y=wD>8U06L-+;c{rZ-u&*%p6>z7Sl}3ikNNBrcHn zLWwt9`8_tn*;g`fNr)nQUy&(9x)7qs&V3Gk$P}Xz48{$MQTcsh6bmtykqz`*w5NaQ zhdx^V{rCET#vD3nLwMN~?Xv-0XvnD5ACd|_YN`$wD+Rs!M!UY+(KIV8P1MV+eH81m z?2q_5sgEGWq4`o9_gOjhk(F<-^LmF~ZYM34Lzi|#juSRI^g;*n!-X&meZE~QNbvuM zt4o;mR+@qlWndi5+JcGU>sZUo4#oyz+5H9G>l1i)ICJc_m(Y6!J?keH>{&1Iena9% zvb(jRDeKWcQsi0T(y`X>5xll3APz5+(J20D_ECLw2EIf4RV(j55CO{+N_P-)8@;qH zPr;sMYq+RO;BH7KB(IdhakT0dSNoj?_F8|;7;p!f&iyZ;KDLR}js@;!56 zt=JMs>ZAlzR(>Qx)76%}R*;eo%%Jp1l!5!sLV)blpLFh|&)0y+#a==lJ{Z#cc@yb~b?06EFF_97IST z%?A@q9nJ#C(VT{IxoxRMkTN$0M6?{7!-dD)_Lfw6S69p#+JHLvN3T$aME{OBM=Nvs z5~A0Gj36mWi*tgk(eZJ}9;Njt;go1X9I`|Q!601N?@nn+e{iwWn<~HD9rh;lLnGcO zMOuNO>-|&1&5}NuuW))p$7HUfwsXhm%dGQZd;TTG=WGhU(SAz|aF~c3`N?7#$c= zRZK2FqDbr^zsI=kq6i)|l0}bG;Bm1ZgudPcDpuz7@Q&(E=DmnNm=B!pkjXrgCU}%{fDneG2Lae|L z;$s>;fuYrM=mwyT4Tvk{;A>REj;K++J}`u^*iKn_fg!&fd>`bf2y;9Y+aQO|AX14Q zi(_FQOa!@G(1ihfh_2LUyH4^=&V1hBbZb2likA}MFq`P#;)EX2u&xBkK->i<40}Bc z0JiiJY)O7O5sl5~`?~)=g?Je@`4xfr6>N|=8#8s2qPu%&3f9hU_c1=SBxgc(zS%?Drixue34jRJa*VFxouQ4#Jv&>eVrH@F+EiLGMMB=@Mj7rrsRP0ZBnPkAO#s+GRRUL~J2Flsx zC%UvQ((Z=vyx6bKZE|i98yzc<`9rL@k+F#iIMfudT2TVcY#J`7bYm|9`3cy9@M(Wd z^rxbEn+h77-qvxD!OMfr0JR%_3wv<6l!Awsza-GEuG!cKX_k5ulp;^;ag&tFKPSM} zPlN<&nxM++O_*U^LxM~W0A32V4(hZ*MPZ>=aWt8R4|Q!bUeC=i-kV9MM22{w{fpsSW8dXWa5L9~tUCe>|N+1%u)aJ(Td`MIpQ&Z6urJE&4Ra#Ky^lrY3dN09WYSwPi1}zg0YM%~hdvdXH zmPZyedxg`9`EL{EzX>FGZgFYBtF2LJ#misK?nm^EHhV1?(b+VXU&lGYwe8+?*5-9C zMX9{7Gz)#|Y?iTVB1CRmvS72Y2yxDP7L*}D?Wtj` zksF*1!ToY*9X1{=vPc0zE)G@<2YSTG(Az7VSrE$E>rIPM#F4jUF9k&x*KT10gL<{_|wl~jQ(}|_FhmH*7;2{WD zr)@}Ja`06F<}+B}l4tm; zszyi+ncWuuvN~u=VXCi_G;GR#LF~U^uv{PZ*z|OvmrCqL&r zB^GAfh~&FiUlOhRp+RO`4Fuvwfxu8_Myd&8M+Z?Ptvo#x0WZZuBTt-^1@(9#sPdmR$~TE{hD z8OcS*$S)+RKy@F-iA;JMps)jLYdUayUmUj;9BGzmn*tk3wP&!AGGN$DnTZ3{WH)1W z6L~F=SQH@EzVwawU#uPM53pfR7QqY$8_A5v6j-ZM_!ieO{$5J2-~J|Z zorS?yTDry_wCo>yEOc<=8G&)m#?;tyRa4Sz$=Q;YEjcIR+w#`fkLBn3EeF1opF3hX zVDJx9-t@nR{>;Mmhb}q6o}5t@w_q{N2l#m{zH3428->h)S-*vre*Ba$EfM{Amscm8*4!vhFHL=KeKU|)ueflk5LQ5Q(Cxn+v^9$Z;9>tra z@N$eRV+;uWSR`6_B4W0pg4de9qG1KMT`cVowZhD((|*vitpVl@0mZ(xHaMkq{OYb8 z4=8s&0gKgtNpHAJ`I3>MZ;d2A5UaG(whOI?sn}I!u_RGyne>lE5~%>Kda?dPA&ClQ zu0Aa4dQp_pL|t9#D6LguSJs%P`)r|uSQZb)4njfG`vmn0=u*dkyDTqdcN$`6SJ&xh zm(vK?O6zP2&%9U^D<}}@$1;M3)`ojb7YB3jm`<(oFsDbq_UB=%r5n^I~U?{K7%nvw=V6zKl9vn%U=}vkunh zQ79qbmoN5vwG-=jFZN}r?cq$O&WA&~8RG{Yb1X)mmHBU@0>H$K8G5n8 zzbx2?O_M!(Go83e{J`m_F{N8E-%Hu=S}L8EN=J63g4kj^gtJ~8q^57&gQ);@%z%b2 zy2rSNa;!`*5~^_c9$=Nj?vZAsVZS)i)Z2vl1x)kvkGag$d(L_i5>%(QF(ZbqO>lOj zqiBl~O}6243nrh!3|@qwr>8Gr*VK!-6q}*1#dv>w&$AMHa~MhK@lm^#8liR2`yYmo zO)s+YMB1XXV50{E<5FMJvFiKb`W)9;qlh|E9j_NV+wE-=JiOQ$Td2$noKN*%XXMbH zdqy7a@sM?i({CTd;^@f-ozO0tlxQTEU@>BC>fi&z9cI?k0DGa+NEjcNoN=4h-Okg` zMp|%+<2r_0LXPkztilU=z0TS+Iw$CWLUBHdjW+DLLNYw59>duNCe^(_nUm^aK$iX; zblnI_A)jRGqzuE8mJBw128}aRtaR3cr~_NvEb@e4gOIoT)70>zED(#~;zbHu|8I5r z=rORt@D{>#G901Q&52EE8704Pp!-t^VujlWAoK&4PCJ?g*)wB{oeJv85}hQ6KD5A4 zlkAJ_Oo`Hdv$8Kq0z|*QBpV=dOguvFWyb)uFQOfn>vo(0xEqWa{~EkAPCtu5 zEnJw8{{(V7(vcf^2WO`6HKi6g)Qj&eSOQWdatJ%74F13~PX6(K0o46W$Kv%)tNsI< z7Gv^bj}bLCT4dI8D`*kg9n2n>c)s9J%eW?~pu1%>f<7Q)uKhS#a9B=_y|AU5Het0x zvEiI9u@`BUYD#TEDeT2|8>9H)lvi=ue8L7#@QB@lMX-;5GpwPwc6v}fH3>>0<>l`n zr62$eWs)h;5wjfnpU6Y!lL{@7E86{m8F`n&TKq*cxzu6iDnOm`3$Jy5l8SC2@h__s zhw<21AiDXW;|IDwu?sS0F|LbO!`d*suA`{b%W$=fxRp7}zLy`?1LzlRZCpSki_kuPB6cN&B}l1=l#(pw4CCkOl)@J zNJ{8A!v6}*ycR369r&Z*_!X{=e)@+Chr}IQuhnBAmn@mW)L0ErivV`^!wR#o&>2ER`_(xz37f zZ#Ydj%|8+|2^}-?2u5~pshS*_O;>zAqeDV>s(P$-l-ugR&$!+_8X?nZce=mInBSY6 zD#~@z2E8a~#m>?2DIfy;XTtZy?%GX(V(0T{qDf->`@$4%5soj2>WIS$+s#d_{zj5T!UThX@wv`^SpM%FlJk%J*f z(=-Oto*bgv3sJH8P!w}ZEfe~Zh>8ZOylc;nQozxl;ESfJn|hP{_?CK;8jy^JIs1^B zv&rpP!d#Y}YK)G5x1|&LWz=?1M{g5srr3*2oPN=CP(X^XuU|W#BLDPT z;2spYsg^xFoyfM4fTcoAawVUGVs`p6_)0lUcNQ*_XmVVHol_%QPhTNz^=0BcbY*t# zW!V*1X3xGX`(|W6D5P_2TMSY36$z5@Z*ft!|AQ_hkQcWn;yC7m%fwxC8Pkuj0yTAn zX0eh*p|f)6dOClP4BK~XbZ`mO+|?6a&oK6}Hf>M85AzumSf@YEd;;t)81N^{yY>|v zR%i4kZG-X>+R}fHr0wco;mrrO{}Ksm-h`xsx&im-?nB-17vPIyTll#*kY^dCFH?k< z(l*$ce@rI@tl9<}mQuPe6M-F5aV1qdo~(UpO*x8dY8!EdC{a6Y)i&A^{E6B(*6z=w zw*HjE+q=OHOkYKUGg}W7R=Ry3KDK!UxECPg`7IJtp6H=du^43OO&S8u(y1s^aSl_H z>-6;Jp#r$wGiIT8h6_c1W0o;v$JeoCh%3I4_n^Ptr0FP<(ss2I$y21S?Gpj%i{g#j ze5rApMdPY6wau3`?ls=6Z5E9?Wz{whH|`T^+^gF&NS?kV1&M{4!2p=efL>D}R9!q~ zp?k2ndW_96+V!NnyC~)ZH5;WbjLY)yC0QO8vQ%bjxJWue(Im^m!?HYlg)DQy4INxY z$>8=oDT!<^Mba)gx6elk`Erm5`R)-DN&SGB*fs&1JjT?{Lnh&}VZx{vG!z5At<5P# zk1_v5ns*GGL5`o-LERZyfML84?(HO}z);u`){d}=Ib^Yz_FqEJ!)s-b*V;I(FNye| z3|s+~$NgAx3V?ioqCX|#zNF_+G>JrRBT23SGSc~VEcR3oUD!!QaC2r9wiCiW3r{CD z2p#DH+YVx6%5@g&(%z*1Lit3qZQB4M>adwSAsUO+fhRg7r)~$C7B(NVwv~ZFn;l@b z3HmI;YI(6$m@F^0#gfdSXs$W2mVYG=b5lGxr{!kSGJL18-Gt_^?Yt%u`zjXO=EGOr z4)WdG77K7Zfn@?D-7H&93vbiUyLoTh0o_BJ)RMm0hQ(cuKAzSB`aKfP4{drf-&5E9 zrL_&m&3vEMnu{Z;KVWl6FUiG8&>2ibfgxMVjKEN;tUZT@28NQ=`&&li=Cm7>2Y@l= z1%|BZz4#vhIn+(xs{%u^9Q-xF7Dr%cv>f~a_yvZ>$f50&jpN42a!>~t7{YeTPXI*c zfTFJ?mnJ`XS2r(lN9nW?FkHJ zw;shb}P z9rT;md(6||$$CjXuA4u&_%Ry}wfBb?r}lHe(dR%P7@CbMO(ppr4w^w({>d+Zgj~2j z!lJq$xWjMjE6Ed@`;kDD%E3kWY$*y1dF9|Iplrc~qar!@3_$rYgx+9#og5qmBHZA^ zsVs^aO@X0#a_|E|asuz@9|c{#9K0JUAyqDvgP)5cb#m~e$Xp=Aw^5c!jz?nis-qoLd)R;e9l1gn##<;gGFmPuK zQ^?7vS8)EvDvi$;U<=i@$<_{cGM~R0oeaW@Lh*N_Y4{E=i0LJsurDS^Q`#|X7xE8{ zv=MYT;<0WTgZR^0N;O8`K@<6_X4|^ku%eFc0S1@l)C%oi0SnW$Es}TP z3Zl=)qcHc%Dy+^2rR}vB9FoJWAolsf3oWp*yoxH876qq=oo+RGAdXp>R3?0kKov-sj3P%d+9}L<8~7A8VIR+NWe6~6b-1X z6pAi4*%}jhgznUyK<~@DkX=zx4kwQKs5abDj*T-iM2627FLFr^G$%?Ic(J8BR8I%7 z=N)RAc7CE9`cGgDD=%?I{|6vmk|We0#g24f8o{!UGihUL#yE)tr&}aC7GBW zkSKT~p$i^JXwV`N3v=UEpwSNeR|WveFdC3WgvP4Ak_-VXj~6w{hf9?rOAz0oOQAC~ z>`O9@n=jU97saMs`WCd~K>LQERg7c^MY0vmUYalekgb2N)6U ze%-moq`3hY^nOm;GdDM%-B+uwoN-_7%`;g=?aG=Ncdc#Ms8rV1HZ-v5 z&B}UZdh={0YgIN<{N0$!X3UsdQE^wrEJ6^!Sqdv$wW_wcS@F(YG=Hv_t!-45x<>!n z8m1^&#dqC#^UQ2zRinS5M!Bz6S*g^lZmM0SHa2Zk8k>~bHS5%kO5N&)T2X97?)MeG zSX2{+bvzQ!hX_1gOtlwcM9waUu18(UUxY-ZE*Yi3PvPK89~PkeaiSInZ~ zgaQFQsm!~&>BgFxd+Tbb#{3%S8Y0hHw4|(zfO}U#$BNR>tc(y{S=meiDOq(;$J)l0 zY^KbZp{%Nb>grdny}wogPg7sn$W|{aN=5w5wMxrswVtGg#5GFohSlngY!zvrgq|^D z9<-~>iL0QAty-x9nKiv8n=+B!G`&Xgh|lTGY)xZREy+mzd4-geHA2jrSZY&ab3M8r z!ZHHEZZ0Y(6`F=t+_+{9L~5R;+@YXaYSpT``$cm}N(Dx?&a4VL0i`0USl3b`8uO4J zB;EeR&(Q;TE#V^p;lFEo0OIJHPxh`wLh(ANWAYS-csmZ+$Wu-CQCjSaP;AW^x=L~o}eHN-03 zH6TRS#jV*5kSGMr@Q2W&;#-M+Txl{l`>M8O_1YQ`quuzGt!{>L8ez$6)n=u!PFdw| zY65(TyWk7rVZQO!pi1Njt&OY%I8vg5^3CfSR&G>$O|{L$Z)s!GgVa42Z3>iRH;N+c zU0K)Ecz@H%H8(w2+qAZ}!L+N!bwa(M6n&;p7n6$Ck?I@$7;|gauWo8wOCu3PamlFj zuxWvR*`WUbWVbVR2V)+_3gO?;p^SNPsKMBe87svsE&f^OPR8alR*r1|#_nRQ0{_B7 z{t`ZzCZo?6V(}D*zW_}45efe#V4R%`_#|K{4i5lMio>S?QwT@&X8|Y2;UT~jU=#jZ zz$tM!1~@eiC*W>fS{zOSti)j};Hhyq4e&K_cnsiaad<3XXB>6_o*suM0KPU3PXc^h z9KISb1&Ab%0ysMkPXkPPK=8?4!nJ&Y5oob)z@!tx?~B8Pe+@9{hwyL3VZt8*Ou8cc z`*E1?9{?u35&n1_P6GTFz@$Th4Zx&Bf=>Y^9TI#FFzJxszXK*65}b&OEu=$&Qvj0= z36=qq4yoRBz@$ThCjurN5_~ma(jmc90h10%w&`)0@Ye$-eG>l0I86AP0h4YCe_I?T zyc;m-ned)CO!&DY;Q4?_|3q_V9449yz|5@^x@O*-cgwA}rnFOZ3C#{#lAk@#1GytdPql_+-qwy!;nuO#V-U?@0b=&PsoS%5uSBnFm{N zh3JbS=xZL@g{Nh%ojr-?89bBl{T0&R$Ke;^DdGP+PZ!G3LuI~0x<3vdjHiS@2KZk+ zUm*V+o@CJetA}`v!z2AM8Nb0($;n4vIoh>v@7}%l-rMth_ntqU{^qm8Pf}ce=|}xK z@;972j2})%()_=|Gdu{NX9KW{t~)O8Q)K;r`;jc-hix}D@?in? z?TvPpiJdXh!|^$GR)!}7&*QM$JUoFbY_|h@cv;}F0uPmmU>|G%50+3ds)OqKznd2G AkpKVy literal 0 HcmV?d00001 diff --git a/com32/rosh/rosh.h b/com32/rosh/rosh.h new file mode 100644 index 0000000..d7296e1 --- /dev/null +++ b/com32/rosh/rosh.h @@ -0,0 +1,148 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008 Gene Cumm - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * rosh.h + * + * Read-Only shell; Header + */ + +/* + * History + * b021 Move much PreProcessing stuff to rosh.h + * b018 Create rosh_debug() macro + * b012 Version of rosh.c at time of creating this file. + */ + +#ifndef ROSH_H +#define ROSH_H + +#include +#include +#include /* macro: true false */ +#include /* strcpy() strlen() memcpy() strchr() */ +#include +#include /* fstat() */ +#include /* open(); open mode macros */ +#include /* fdopendir() opendir() readdir() closedir() DIR */ +#include /* getcwd() */ +#include /* errno; error macros */ +#include /* For htonl/ntohl/htons/ntohs */ + +#include +#include + +/* A GNUC extension to void out unused functions are used */ +/* Plus, there seem to be other references for SYSLINUX to __GNUC__ */ +#ifndef __GNUC__ +#error SYSLINUX (I believe) requires __GNUC__ +#endif /* __GNUC__ */ + +#ifdef DO_DEBUG +#define ROSH_DEBUG(f, ...) printf (f, ## __VA_ARGS__) +#ifdef DO_DEBUG2 +#define ROSH_DEBUG2(f, ...) printf (f, ## __VA_ARGS__) +#else /* DO_DEBUG2 */ +#define ROSH_DEBUG2(f, ...) ((void)0) +#endif /* DO_DEBUG2 */ +#else /* DO_DEBUG */ +#define ROSH_DEBUG(f, ...) ((void)0) +#define ROSH_DEBUG2(f, ...) ((void)0) +#endif /* DO_DEBUG */ + +#ifdef __COM32__ +#define ROSH_IS_COM32 1 +#include /* openconsole() */ +#include /* Has info on the SYSLINUX variant */ +#include /* syslinux_run_command() */ +#define ROSH_COM32(f, ...) printf (f, ## __VA_ARGS__) +#else +#include +#define ROSH_IS_COM32 0 +static inline char *syslinux_config_file() +{ + return ""; +} +static inline int getscreensize(int fd, int *rows, int *cols) +{ + char *str; + int rv; + *rows = 0; + *cols = 0; + if (rows) { + str = getenv("LINES"); + if (str) { + *rows = atoi(str); + } + } + if (cols) { + str = getenv("COLUMNS"); + if (str) { + *cols = atoi(str); + } + } + if (!rows || !cols) + rv = -1; + else if (!*rows || !*cols) + rv = -2; + else + rv = 0; + return rv; +} +#define ROSH_COM32(f, ...) ((void)0) +#define syslinux_run_command(f) ((void)0) +#endif /* __COM32__ */ + +#define SEP '/' + +/* Size of buffer string */ +#define ROSH_BUF_SZ 16384 +/* Size of screen output buffer (80*40) */ +#define ROSH_SBUF_SZ 1200 + +/* Size of command buffer string */ +#ifdef MAX_CMDLINE_LEN +#define ROSH_CMD_SZ MAX_CMDLINE_LEN +#else +#ifdef COMMAND_LINE_SIZE +#define ROSH_CMD_SZ COMMAND_LINE_SIZE +#else +#define ROSH_CMD_SZ 2048 +#endif /* COMMAND_LINE_SIZE */ +#endif /* MAX_CMDLINE_LEN */ + +/* Size of path buffer string */ +#ifdef PATH_MAX +#define ROSH_PATH_SZ PATH_MAX +#elif NAME_MAX +#define ROSH_PATH_SZ NAME_MAX +#else +#define ROSH_PATH_SZ 255 +#endif /* NAME_MAX */ + +const char rosh_help_str1[] = +"Commands: ? cat cd cfg dir exit help less ls man more pwd run quit ver"; + +const char rosh_help_str2[] = +"Commands: (some 1-letter abreviations also allowed)\n\ + h HELP\n ALSO ? help man\n\ + cat Concatenate file to console\n cat \n\ + cd Change to directory
    \n cd \n\ + less Page a file with rewind\n\ + ls List contents of current directory\n ls \n\ + ALSO dir\n\ + more Page a file\n\ + pwd display Present Working Directory\n\ + run Run a program/kernel with options\n\ + exit Exit to previous environment\n ALSO quit"; + +#endif /* Not ROSH_H */ diff --git a/com32/samples/Makefile b/com32/samples/Makefile new file mode 100644 index 0000000..482769e --- /dev/null +++ b/com32/samples/Makefile @@ -0,0 +1,38 @@ +## ----------------------------------------------------------------------- +## +## Copyright 2001-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +## +## samples for syslinux users +## + +topdir = ../.. +include ../MCONFIG + +all: hello.c32 cat.c32 resolv.c32 serialinfo.c32 \ + localboot.c32 \ + fancyhello.c32 fancyhello.lnx \ + keytest.c32 keytest.lnx \ + advdump.c32 entrydump.c32 + +tidy dist: + rm -f *.o *.lo *.a *.lst *.elf .*.d *.tmp + +clean: tidy + rm -f *.lnx + +spotless: clean + rm -f *.lss *.c32 *.com + rm -f *~ \#* + +install: # Don't install samples + +-include .*.d diff --git a/com32/samples/advdump.c b/com32/samples/advdump.c new file mode 100644 index 0000000..2e2ef39 --- /dev/null +++ b/com32/samples/advdump.c @@ -0,0 +1,53 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * advdump.c + * + * Dump the contents of the syslinux ADV + */ + +#include +#include +#include +#include + +int main(void) +{ + uint8_t *p, *ep; + size_t s = syslinux_adv_size(); + char buf[256]; + + openconsole(&dev_stdcon_r, &dev_stdcon_w); + + p = syslinux_adv_ptr(); + + printf("ADV size: %zd bytes at %p\n", s, p); + + ep = p+s; /* Need at least opcode+len */ + while (p < ep-1 && *p) { + int t = *p++; + int l = *p++; + + if (p+l > ep) + break; + + memcpy(buf, p, l); + buf[l] = '\0'; + + printf("ADV %3d: \"%s\"\n", t, buf); + + p += l; + } + + return 0; +} diff --git a/com32/samples/advdump.c32 b/com32/samples/advdump.c32 new file mode 100755 index 0000000000000000000000000000000000000000..9d790777fc8899e09e64c72d2e215e34a2b0225d GIT binary patch literal 4876 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 literal 0 HcmV?d00001 diff --git a/com32/samples/cat.c b/com32/samples/cat.c new file mode 100644 index 0000000..277427f --- /dev/null +++ b/com32/samples/cat.c @@ -0,0 +1,31 @@ +#include +#include +#include + +int main(int argc, char *argv[]) +{ + FILE *f; + int ch; + int i; + + openconsole(&dev_stdcon_r, &dev_stdcon_w); + + printf("argv = %p\n", argv); + for ( i = 0 ; i <= argc ; i++ ) + printf("argv[%d] = %p = \"%s\"\n", i, argv[i], argv[i]); + + if ( argc < 2 ) { + fprintf(stderr, "Missing file name!\n"); + exit(1); + } + + printf("File = %s\n", argv[1]); + + f = fopen(argv[1], "r"); + while ( (ch = getc(f)) != EOF ) + putchar(ch); + + fclose(f); + + return 0; +} diff --git a/com32/samples/cat.c32 b/com32/samples/cat.c32 new file mode 100755 index 0000000000000000000000000000000000000000..8723a9d47736231efecf92437899b881a926adad GIT binary patch literal 5772 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 IXR`l)0pS(wCIA2c literal 0 HcmV?d00001 diff --git a/com32/samples/entrydump.c b/com32/samples/entrydump.c new file mode 100644 index 0000000..f0ddd5d --- /dev/null +++ b/com32/samples/entrydump.c @@ -0,0 +1,60 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * entry.c + * + * Dump the entry point registers + */ + +#include +#include +#include +#include +#include + +struct stack_frame { + uint16_t gs, fs, es, ds; + uint32_t edi, esi, ebp, esp; + uint32_t ebx, edx, ecx, eax; + uint32_t eflags; + uint16_t ret_ip, ret_cs; + uint16_t pxe_ip, pxe_cs; +}; + +int main(void) +{ + const union syslinux_derivative_info *di; + const struct stack_frame *sf; + + openconsole(&dev_null_r, &dev_stdcon_w); + + di = syslinux_derivative_info(); + + if (di->c.filesystem != SYSLINUX_FS_PXELINUX) { + printf("Not running under PXELINUX (fs = %02x)\n", di->c.filesystem); + return 1; + } + + sf = (const struct stack_frame *)di->pxe.stack; + + printf("EAX: %08x EBX: %08x ECX: %08x EDX: %08x\n" + "ESP: %08x EBP: %08x ESI: %08x EDI: %08x\n" + "SS: %04x DS: %04x ES: %04x FS: %04x GS: %04x\n" + "EFLAGS: %08x RET: %04x:%04x PXE: %04x:%04x\n", + sf->eax, sf->ebx, sf->ecx, sf->edx, + sf->esp+4, sf->ebp, sf->esi, sf->edi, + di->rr.r.fs, sf->ds, sf->es, sf->fs, sf->gs, + sf->eflags, sf->ret_cs, sf->ret_ip, sf->pxe_cs, sf->pxe_ip); + + return 0; +} diff --git a/com32/samples/entrydump.c32 b/com32/samples/entrydump.c32 new file mode 100755 index 0000000000000000000000000000000000000000..4af6a59845e8d18c514e5faf58c141b87b3f8911 GIT binary patch literal 4488 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 NvE_D=+m8Rb{1>vP*>V5? literal 0 HcmV?d00001 diff --git a/com32/samples/fancyhello.c b/com32/samples/fancyhello.c new file mode 100644 index 0000000..a6c5a37 --- /dev/null +++ b/com32/samples/fancyhello.c @@ -0,0 +1,42 @@ + +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * fancyhello.c + * + * Hello, World! using libcom32 and ANSI console; also possible to compile + * as a Linux application for testing. + */ + +#include +#include + +#include /* Provided by libutil */ + +int main(void) +{ + char buffer[1024]; + + console_ansi_std(); + + printf("\033[1;33;44m *** \033[37mHello, World!\033[33m *** \033[0m\n"); + + for (;;) { + printf("\033[1;36m>\033[0m "); + fgets(buffer, sizeof buffer, stdin); + if ( !strncmp(buffer, "exit", 4) ) + break; + printf("\033[1m:\033[0m %s", buffer); + } + return 0; +} diff --git a/com32/samples/fancyhello.c32 b/com32/samples/fancyhello.c32 new file mode 100755 index 0000000000000000000000000000000000000000..856a48b8b4ad7026d7e4ebd00edba59628408682 GIT binary patch literal 9044 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 +#include +#include + +int main(int argc, char *argv[]) +{ + int i; + + openconsole(&dev_stdcon_r, &dev_stdcon_w); + + printf("Hello, World!\n"); + + for (i = 1; i < argc; i++) + printf("%s%c", argv[i], (i == argc-1) ? '\n' : ' '); + + return 0; +} diff --git a/com32/samples/hello.c32 b/com32/samples/hello.c32 new file mode 100755 index 0000000000000000000000000000000000000000..721d87dfe1ef8884194606ccf04ae3a4ec989c35 GIT binary patch literal 4748 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 JzfR=ae*uC?UKRiV literal 0 HcmV?d00001 diff --git a/com32/samples/keytest.c b/com32/samples/keytest.c new file mode 100644 index 0000000..6978644 --- /dev/null +++ b/com32/samples/keytest.c @@ -0,0 +1,81 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * keytest.c + * + * Test the key parsing library + */ + +#include +#include +#include +#include +#include + +#include /* Provided by libutil */ +#include + +static void cooked_keys(void) +{ + int key; + + printf("[cooked]"); + + for(;;) { + key = get_key(stdin, 0); + + if ( key == 0x03 ) { + printf("[done]\n"); + exit(0); + } else if ( key == '?' ) + return; + + if ( key >= 0x20 && key < 0x100 ) { + putchar(key); + } else { + printf("[%04x]", key); + } + } +} + +static void raw_keys(void) +{ + int key; + + printf("[raw]"); + + for(;;) { + key = getc(stdin); + + if ( key == 0x03 ) { + printf("[done]\n"); + exit(0); + } else if ( key == '!' ) + return; + + printf("<%02x>", key); + } +} + +int main(void) +{ + console_ansi_raw(); + + printf("CLK_TCK = %d\n", (int)CLK_TCK); + printf("Press keys, end with Ctrl-C...\n"); + + for (;;) { + cooked_keys(); + raw_keys(); + } +} diff --git a/com32/samples/keytest.c32 b/com32/samples/keytest.c32 new file mode 100755 index 0000000000000000000000000000000000000000..84769a47737e63c1c0b69d051c18c5e654f86284 GIT binary patch literal 9812 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 literal 0 HcmV?d00001 diff --git a/com32/samples/keytest.lnx b/com32/samples/keytest.lnx new file mode 100755 index 0000000000000000000000000000000000000000..8e08edcf82efd1c5ccc85c07d097096f60745f55 GIT binary patch literal 22178 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 literal 0 HcmV?d00001 diff --git a/com32/samples/localboot.c b/com32/samples/localboot.c new file mode 100644 index 0000000..579c7b0 --- /dev/null +++ b/com32/samples/localboot.c @@ -0,0 +1,9 @@ +#include +#include + +int main(int argc, char *argv[]) +{ + syslinux_local_boot(argc > 1 ? atoi(argv[1]) : 0); + + return 0; +} diff --git a/com32/samples/localboot.c32 b/com32/samples/localboot.c32 new file mode 100755 index 0000000000000000000000000000000000000000..e7b11baacc1e716fb034b76fa77853b2ea723c92 GIT binary patch literal 1160 zcmcIiUr19?7(aKrx|M(KMK{Y>wB!g`3J!`;2k9EDXb);yy8an~f%uYc%Ll2&N$_l4 z*h4Qp6-W>L0pCn}a5Mi*gF*^S5GtyNjk_c@bkXqkoxABO)??qp_wV<8-|w9Bol(X4 zYJbua4hScxv-U)CV?K0;C3OfPdNo;B=+y)Y8`d5Mj6p~@(~r?&4ML6M z2Ozq&$#kzyDIqhwywc{1vM$BwgY0+7iFxwnn{qALMB;kO+=&}`0UIARLWS^bmd-}6 zz#L21)!r^$PEH1cB)+>#8QTf4<&8RrOqn1ZY(0DCG>KQz2yMY;>cl2F$DufJJ?Y2w z$Xzas=+=GZ#Uc_t(U^lnNChKyjKN28f%TAwbFdI11U)9?v9ngrTjo1f?ZJKrXYF(I z);3Qm>BjXM645}lNo38ZQXE!Rc$9InK_)$es@W*fQ}NrVy_r!(Tccz>>rgMXWLXb@ ze~;R+2v;%4wm=IOh2aGVcyJBE@LP_#fNy+;Dm?)C5b@j#=24k0Pi*efp2zxPH_lg& zlR96OFuqH&&C_X;*x@aaY$2&@jwDLGd6Mlu*y{yS*Q0^2eYthr*wpWb9y4<10;8GM zLV%H5x3NSP(8hbZNLR^J<++QOE=>A$)+%9Od{a?kk3j{4*;gJYXJ06@Cy`D+FvB(m z?N|sLxeXXy8D*OYG3DRm`)R%@41_)W42K$2K+Yoxj(kuCOB*aW)99 za$C~*3RU)JkyOD!Rm!pm!!tzx@c!mcvu>L#ye!p`&S#-jQyp7C>J|QrK3pWCMR@FA D=v-_p literal 0 HcmV?d00001 diff --git a/com32/samples/resolv.c b/com32/samples/resolv.c new file mode 100644 index 0000000..02cc0a8 --- /dev/null +++ b/com32/samples/resolv.c @@ -0,0 +1,66 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * resolv.c + * + * Resolve an IP address + */ + +#include +#include +#include +#include +#include + +uint32_t resolv(const char *name) +{ + com32sys_t reg; + + strcpy((char *)__com32.cs_bounce, name); + + memset(®, 0, sizeof reg); + reg.eax.w[0] = 0x0010; + reg.ebx.w[0] = OFFS(__com32.cs_bounce); + reg.es = SEG(__com32.cs_bounce); + + __intcall(0x22, ®, ®); + + if ( reg.eflags.l & EFLAGS_CF ) + return 0; + else + return reg.eax.l; +} + +int main(int argc, char *argv[]) +{ + uint32_t ip; + + openconsole(&dev_null_r, &dev_stdcon_w); + + if ( argc < 2 ) { + fputs("Usage: resolv hostname\n", stderr); + exit(1); + } + + ip = resolv(argv[1]); + + if ( ip ) { + printf("%s = %u.%u.%u.%u\n", argv[1], + (ip & 0xff), (ip >> 8) & 0xff, + (ip >> 16) & 0xff, (ip >> 24) & 0xff); + } else { + printf("%s not found\n", argv[1]); + } + + return 0; +} diff --git a/com32/samples/resolv.c32 b/com32/samples/resolv.c32 new file mode 100755 index 0000000000000000000000000000000000000000..42929a920d7e414532a59ab2e8b0e139037db726 GIT binary patch literal 4360 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 +#include +#include +#include + +int main(void) +{ + const struct syslinux_serial_console_info *si; + + openconsole(&dev_null_r, &dev_stdcon_w); + + si = syslinux_serial_console_info(); + + printf("Serial port base: %#06x\n", si->iobase); + printf("Serial port divisor: %5d", si->divisor); + if (si->divisor) + printf(" (%d baud)", 115200/si->divisor); + printf("\n" + "Flow control bits: %#05x\n", si->flowctl); + + return 0; +} diff --git a/com32/samples/serialinfo.c32 b/com32/samples/serialinfo.c32 new file mode 100755 index 0000000000000000000000000000000000000000..7ad8ee58b915fd7635ce444057733042573ca07d GIT binary patch literal 4232 zcmZ`-e^3(-1J0;`dlJ+{Q3LyxZ>n+!ajW{A`yP*RYpwbIF3Gl zv+|x7?bt8lC$C;F|gi)_W{DfCO zn=)RwS+8DozD+ZMPc?z|fk`W1v`wJxJje*@sJJs6 zm6kU&2uDvI<4)MP12f6Mu62xS1-P(pD*VtCu3_X%kUdd#H1Da%j+-Ou)oj4Y)ix9H zm>oMNPIWYUWME*RvFB>80$)cWUXoi+&%MoI6<-Z5s0~NE^68218zGL{pY}F;fXhiQ z#ZSYDo>3=Qw?fGiTb_iP*C9i_q=;@NM0=eaVawAXxW~Mt3=0m=a_d=TJ*%y!*LwO4 z{DCjP)*d4fasr3J3qL<(LuARjyWpIXTqV2dNAM;H&T32YTQv+pic6G3iRLAw0ysLd zAm{GOtOZK90Yai4Jdv8oDzP(Dcq?j>us-FbxiPd0ZGd|(6X3oS? zYbGYLt(hxfw`R7)EEGg)#;Y8wUS&TgxfH=?XeHC+>ir5iEW^#Oe}&4-#imOy&L+4@B@s)vf*Pk zz6*jCIy2abWgsZYpwt)=l;kd{MoI3Lij|~aU#KJ{DO*YYL`qkZFUZyU!?z(+!$~eN zxnLfuYBOGEKr;NKntqfRWK-lvpH}f}*RLW4)#|K$f0|i?2m@BPUU9M<-a^)u@egPO|dQ^d@T*yE?ZR82bVaDpB^Mw!IOpGrB!;qpxvaL z21%7V!b9B080XX^{$nv#n~p{8Y=fOUbr$t{5fju>6tyG_I^b+MeQ)^dLZ(a47KTj9 z0n{MVtUsuwIEIIlh13s@&n?2(uu%vIRr)2(p*8ZR8dHWv%`~RB-=2wc=;shxxPYhA z4=jIWSl)8z4&kV24s7Nz)R$?CJ*R-B+_as2m*2YhEw)SH9xEeKu^DG7Tk{iA;wNQL zPztrJH(^TaCV`$S3i#l`E}0>26>&pd$LX$?(p9*{bu9zNa_FgoYx@8}0@u?J;q*dK z%b~Yw<^%N{H**P_`=H)rVATip`?MgDXca+FdCzrd@*jX2Cpfe9mG$+8L~_}{h6rW3 z@t05^xYQ_5z{1yo#%JXc`EOtd49C;@hLuljk_cdar<@U~vsFsO_X>|)Uq$Q{p2l3 zbBf)y5udFR8SY>)+GWd+s*^4?+SF&Sjnvp4lh+_yEfp!#pwBwb%&`hC~f+2un!Zy4TG0GAYHN@^_M_(`b zH8vuL&Bt+8S~!#?;V%Ao)L>JKyL9swesA!Q;JzWqE<`6~RxyZ>>P zJ-h5nS+}XPw)_e8(<$|YTWcX@tb!_A2*LZW8}GmGFyQVDzKWBf1ft@lCyU1;yd6G! zZ8*`z>@5G;Gojz#?_I+6dC3+?l|}gV$SLU%`cnjVU%IL0(c183`MeD>42bZHH-X;- zBlP|rQ|M2b$92)cBI1s1Hf^fZX3V`drt}BeKUq}TSW#K+%Y?+=OQD7L$+H;5kQfcH_Koh zM_JD(Yl^mR+x{c;VB&6WuHxc&Goi)`tlkOz8BJYE5OP5)F>AncnV!#x$>JH#a~;4M zZPAK7vQ4)xQU^a<8>3e+BH<{zmz+p&PQ^O0sJ{Yt+FjIuLQYoiXl zM{6rixBqL5HAWnY<_**8EKaiN9YcjXoWAdg#)CNFW`yYnbSl0LxBz9{5nH^GZD4Tb z#zt%alw5Gc98A6KG~6hkDwe=(UfdNk$h=FH9ebdC6Kk z)#(L&jq-y`c`??d3-Qvuacl*6;$!&fUw~kh*4-ig5yVGvvd+@DI$C9=XuuO48Q6j^ zilM^@s%2sN(9W^6ajdhr#$;HLr)nabUGJU=NIJ9tF0`6NB3 zS%hPO8sfRT#mlrnwh4XKguZIb&oQj@3pqQ*w(SP9owtcwsNkbRjpu`TZ_5O zy5QbU*Ry-~NbP%ru4me$jyV-;p`vJy91IpC41+l$m$OLZc4M5u$XP6MemqxNn* zMytJRU2d({%%eYz`JP;S1!DXXDq!CXcmA$i6FHm;?wxt`E7zPR dZSvkbyjQMeoAOihXiQ5Ka#hPkt`+}v_#X?Xr~v=~ literal 0 HcmV?d00001 diff --git a/core/Makefile b/core/Makefile new file mode 100644 index 0000000..65418c4 --- /dev/null +++ b/core/Makefile @@ -0,0 +1,125 @@ +## ----------------------------------------------------------------------- +## +## Copyright 1998-2009 H. Peter Anvin - All Rights Reserved +## Copyright 2009 Intel Corporation; author: H. Peter Anvin +## +## 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +# +# Makefile for the SYSLINUX core +# + +# No builtin rules +MAKEFLAGS += -r +MAKE += -r + +topdir = .. +include $(topdir)/MCONFIG.embedded +-include $(topdir)/version.mk + +OPTFLAGS = +INCLUDES = + +# This is very similar to cp437; technically it's for Norway and Denmark, +# but it's unlikely the characters that are different will be used in +# filenames by other users. +CODEPAGE = cp865 + +# The targets to build in this directory... +BTARGET = kwdhash.gen \ + ldlinux.bss ldlinux.sys ldlinux.bin \ + pxelinux.0 isolinux.bin isolinux-debug.bin \ + extlinux.bin extlinux.bss extlinux.sys + +# All primary source files for the main syslinux files +NASMSRC = $(wildcard *.asm) +NASMHDR = $(wildcard *.inc) +CSRC = $(wildcard *.c) +CHDR = $(wildcard *.h) +OTHERSRC = keywords +ALLSRC = $(NASMSRC) $(NASMHDR) $(CSRC) $(CHDR) $(OTHERSRC) + +# The DATE is set on the make command line when building binaries for +# official release. Otherwise, substitute a hex string that is pretty much +# guaranteed to be unique to be unique from build to build. +ifndef HEXDATE +HEXDATE := $(shell $(PERL) ../now.pl $(SRCS)) +endif +ifndef DATE +DATE := $(shell sh ../gen-id.sh $(VERSION) $(HEXDATE)) +endif + +all: $(BTARGET) + +kwdhash.gen: keywords genhash.pl + $(PERL) genhash.pl < keywords > kwdhash.gen + +.PRECIOUS: %.elf + +# Standard rule for {isolinux,isolinux-debug}.bin +iso%.bin: iso%.elf checksumiso.pl + $(OBJCOPY) -O binary $< $@ + $(PERL) checksumiso.pl $@ + +# Standard rule for {ldlinux,pxelinux,extlinux}.bin +%.bin: %.elf + $(OBJCOPY) -O binary $< $@ + +%.o: %.asm kwdhash.gen ../version.gen + ( $(NASM) -M -DDEPEND $(NINCLUDE) -o $@ $< ; echo '' ) > .$@.d; true + $(NASM) $(NASMOPT) -f elf -g -F stabs -DDATE_STR="'$(DATE)'" \ + -DHEXDATE="$(HEXDATE)" \ + -l $(@:.o=.lsr) -o $@ $< + +%.elf: %.o syslinux.ld + $(LD) $(LDFLAGS) -T syslinux.ld -M -o $@ $< > $(@:.elf=.map) + $(OBJDUMP) -h $@ > $(@:.elf=.sec) + $(PERL) lstadjust.pl $(@:.elf=.lsr) $(@:.elf=.sec) $(@:.elf=.lst) + +pxelinux.0: pxelinux.bin + cp -f $< $@ + +ldlinux.bss: ldlinux.bin + dd if=$< of=$@ bs=512 count=1 + +ldlinux.sys: ldlinux.bin + dd if=$< of=$@ bs=512 skip=1 + +extlinux.bss: extlinux.bin + dd if=$< of=$@ bs=512 count=1 + +extlinux.sys: extlinux.bin + dd if=$< of=$@ bs=512 skip=1 + +# NASM prior to 2.03 wouldn't auto-generate this dependency... +ldlinux.o: codepage.cp + +codepage.cp: ../codepage/$(CODEPAGE).cp + cp -f $< $@ + +install: installer + +install-lib: installer + +install-all: install install-lib + +netinstall: installer + +tidy dist: + rm -f codepage.cp *.o *.elf stupid.* patch.offset .depend .*.d + rm -f *.lsr *.lst *.map *.sec + rm -f $(OBSOLETE) + +clean: tidy + +spotless: clean + rm -f $(BTARGET) *.bin *_bin.c + +# Include dependencies file +-include .*.d diff --git a/core/abort.inc b/core/abort.inc new file mode 100644 index 0000000..5b16b9d --- /dev/null +++ b/core/abort.inc @@ -0,0 +1,84 @@ +; ----------------------------------------------------------------------- +; +; Copyright 2005-2009 H. Peter Anvin - All Rights Reserved +; Copyright 2009 Intel Corporation; author: H. Peter Anvin +; +; 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, Inc., 53 Temple Place Ste 330, +; Boston MA 02111-1307, USA; either version 2 of the License, or +; (at your option) any later version; incorporated herein by reference. +; +; ----------------------------------------------------------------------- + +; +; abort.inc +; +; Code to terminate a kernel load +; + + section .text + +; +; dot_pause: same as abort_check, except prints a dot, too +; assumes CS == DS +; +dot_pause: + push si + mov si,dot_msg + call writestr_qchk + pop si + ; fall through + +; +; abort_check: let the user abort with or +; +abort_check: + call reset_idle ; Not idle despite pollchar + call pollchar + jz .ret1 + pusha + call getchar + cmp al,27 ; + je .kill + cmp al,3 ; + je .kill +.ret2: popa +.ret1: ret + +.kill: mov si,aborted_msg + mov bx,enter_command + jmp abort_load_chain + +; +; abort_load: Called by various routines which wants to print a fatal +; error message and return to the command prompt. Since this +; may happen at just about any stage of the boot process, assume +; our state is messed up, and just reset the segment registers +; and the stack forcibly. +; +; SI = offset (in _text) of error message to print +; BX = future entry point (abort_load_chain) +; +abort_load: + mov bx,error_or_command +abort_load_chain: + RESET_STACK_AND_SEGS AX + call writestr ; Expects SI -> error msg + + ; Return to the command prompt + jmp bx + +; +; error_or_command: Execute ONERROR if appropriate, otherwise enter_command +; +error_or_command: + mov cx,[OnerrorLen] + and cx,cx + jnz on_error + jmp enter_command + + section .data +aborted_msg db ' aborted.', CR, LF, 0 + + section .text diff --git a/core/adv.inc b/core/adv.inc new file mode 100644 index 0000000..d856a1a --- /dev/null +++ b/core/adv.inc @@ -0,0 +1,503 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 2007-2008 H. Peter Anvin - 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, Inc., 51 Franklin St, Fifth Floor, +;; Boston MA 02110-1301, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; adv.inc +;; +;; The auxillary data vector and its routines +;; +;; The auxillary data vector is a 512-byte aligned block that on the +;; disk-based derivatives can be part of the syslinux file itself. It +;; exists in two copies; when written, both copies are written (with a +;; sync in between, if from the operating system.) The first two +;; dwords are magic number and inverse checksum, then follows the data +;; area as a tagged array similar to BOOTP/DHCP, finally a tail +;; signature. +;; +;; Note that unlike BOOTP/DHCP, zero terminates the chain, and FF +;; has no special meaning. +;; + +;; +;; List of ADV tags... +;; +ADV_BOOTONCE equ 1 + +;; +;; Other ADV data... +;; +ADV_MAGIC1 equ 0x5a2d2fa5 ; Head signature +ADV_MAGIC2 equ 0xa3041767 ; Total checksum +ADV_MAGIC3 equ 0xdd28bf64 ; Tail signature + +ADV_LEN equ 500 ; Data bytes + +adv_retries equ 6 ; Disk retries + + section .adv + ; Introduce the ADVs to valid but blank +adv0: +.head resd 1 +.csum resd 1 +.data resb ADV_LEN +.tail resd 1 +.end equ $ +adv1: +.head resd 1 +.csum resd 1 +.data resb ADV_LEN +.tail resd 1 +.end equ $ + section .text + + ; + ; This is called after config file parsing, so we know + ; the intended location of the ADV + ; +adv_init: + cmp byte [ADVDrive],-1 + jne adv_read + +;%if IS_SYSLINUX || IS_MDSLINUX || IS_EXTLINUX +%if IS_EXTLINUX ; Not yet implemented for the other derivatives + ; + ; Update pointers to default ADVs... + ; + mov bx,[LDLSectors] + shl bx,2 + mov ecx,[bsHidden] + mov eax,[bx+SectorPtrs-8] + mov edx,[bx+SectorPtrs-4] + add eax,ecx + add edx,ecx + mov [ADVSec0],eax + mov [ADVSec1],edx + mov al,[DriveNumber] + mov [ADVDrive],al +%endif + ; ** fall through to adv_verify ** + + ; + ; Initialize the ADV data structure in memory + ; +adv_verify: + cmp byte [ADVDrive],-1 ; No ADV configured, still? + je .reset ; Then unconditionally reset + + mov si,adv0 + call .check_adv + jz .ok ; Primary ADV okay + mov si,adv1 + call .check_adv + jz .adv1ok + + ; Neither ADV is usable; initialize to blank +.reset: + mov di,adv0 + mov eax,ADV_MAGIC1 + stosd + mov eax,ADV_MAGIC2 + stosd + xor eax,eax + mov cx,ADV_LEN/4 + rep stosd + mov eax,ADV_MAGIC3 + stosd + +.ok: + ret + + ; The primary ADV is bad, but the backup is OK +.adv1ok: + mov di,adv0 + mov cx,512/4 + rep movsd + ret + + + ; SI points to the putative ADV; unchanged by routine + ; ZF=1 on return if good +.check_adv: + push si + lodsd + cmp eax,ADV_MAGIC1 + jne .done ; ZF=0, i.e. bad + xor edx,edx + mov cx,ADV_LEN/4+1 ; Remaining dwords +.csum: + lodsd + add edx,eax + loop .csum + cmp edx,ADV_MAGIC2 + jne .done + lodsd + cmp eax,ADV_MAGIC3 +.done: + pop si + ret + +; +; adv_get: find an ADV string if present +; +; Input: DL = ADV ID +; Output: CX = byte count (zero on not found) +; SI = pointer to data +; DL = unchanged +; +; Assumes CS == DS. +; + +adv_get: + push ax + mov si,adv0.data + xor ax,ax ; Keep AH=0 at all times +.loop: + lodsb ; Read ID + cmp al,dl + je .found + and al,al + jz .end + lodsb ; Read length + add si,ax + cmp si,adv0.tail + jb .loop + jmp .end + +.found: + lodsb + mov cx,ax + add ax,si ; Make sure it fits + cmp ax,adv0.tail + jbe .ok +.end: + xor cx,cx +.ok: + pop ax + ret + +; +; adv_set: insert a string into the ADV in memory +; +; Input: DL = ADV ID +; FS:BX = input buffer +; CX = byte count (max = 255!) +; Output: CF=1 on error +; CX clobbered +; +; Assumes CS == DS == ES. +; +adv_set: + push ax + push si + push di + and ch,ch + jnz .overflow + + push cx + mov si,adv0.data + xor ax,ax +.loop: + lodsb + cmp al,dl + je .found + and al,al + jz .endz + lodsb + add si,ax + cmp si,adv0.tail + jb .loop + jmp .end + +.found: ; Found, need to delete old copy + lodsb + lea di,[si-2] + push di + add si,ax + mov cx,adv0.tail + sub cx,si + jb .nukeit + rep movsb ; Remove the old one + mov [di],ah ; Termination zero + pop si + jmp .loop +.nukeit: + pop si + jmp .end +.endz: + dec si +.end: + ; Now SI points to where we want to put our data + pop cx + mov di,si + jcxz .empty + add si,cx + cmp si,adv0.tail-2 + jae .overflow ; CF=0 + + mov si,bx + mov al,dl + stosb + mov al,cl + stosb + fs rep movsb + +.empty: + mov cx,adv0.tail + sub cx,di + xor ax,ax + rep stosb ; Zero-fill remainder + + clc +.done: + pop di + pop si + pop ax + ret +.overflow: + stc + jmp .done + +; +; adv_cleanup: checksum adv0 and copy to adv1 +; Assumes CS == DS == ES. +; +adv_cleanup: + pushad + mov si,adv0.data + mov cx,ADV_LEN/4 + xor edx,edx +.loop: + lodsd + add edx,eax + loop .loop + mov eax,ADV_MAGIC2 + sub eax,edx + lea di,[si+4] ; adv1 + mov si,adv0 + mov [si+4],eax ; Store checksum + mov cx,(ADV_LEN+12)/4 + rep movsd + popad + ret + +; +; adv_write: write the ADV to disk. +; +; Location is in memory variables. +; Assumes CS == DS == ES. +; +; Returns CF=1 if the ADV cannot be written. +; +adv_write: + cmp dword [ADVSec0],0 + je .bad + cmp dword [ADVSec1],0 + je .bad + cmp byte [ADVDrive],-1 + je .bad + + push ax + call adv_cleanup + mov ah,3 ; Write + call adv_read_write + pop ax + + clc + ret +.bad: ; No location for ADV set + stc + ret + +; +; adv_read: read the ADV from disk +; +; Location is in memory variables. +; Assumes CS == DS == ES. +; +adv_read: + push ax + mov ah,2 ; Read + call adv_read_write + call adv_verify + pop ax + ret + +; +; adv_read_write: disk I/O for the ADV +; +; On input, AH=2 for read, AH=3 for write. +; Assumes CS == DS == ES. +; +adv_read_write: + mov [ADVOp],ah + pushad + + ; Check for EDD + mov bx,55AAh + mov ah,41h ; EDD existence query + mov dl,[ADVDrive] + int 13h + mov si,.cbios + jc .noedd + cmp bx,0AA55h + jne .noedd + test cl,1 + jz .noedd + mov si,.ebios +.noedd: + + mov eax,[ADVSec0] + mov bx,adv0 + call .doone + + mov eax,[ADVSec1] + mov bx,adv1 + call .doone + + popad + ret + +.doone: + xor edx,edx ; Zero-extend LBA + push si + jmp si + +.ebios: + mov cx,adv_retries +.eb_retry: + ; Form DAPA on stack + push edx + push eax + push es + push bx + push word 1 ; Sector count + push word 16 ; DAPA size + mov si,sp + pushad + mov dl,[ADVDrive] + mov ax,4000h + or ah,[ADVOp] + push ds + push ss + pop ds + int 13h + pop ds + popad + lea sp,[si+16] ; Remove DAPA + jc .eb_error + pop si + ret +.eb_error: + loop .eb_retry + stc + pop si + ret + +.cbios: + push edx + push eax + push bp + + mov dl,[ADVDrive] + and dl,dl + ; Floppies: can't trust INT 13h 08h, we better know + ; the geometry a priori, which means it better be our + ; boot device... + jns .noparm ; Floppy drive... urk + + mov ah,08h ; Get disk parameters + int 13h + jc .noparm + and ah,ah + jnz .noparm + shr dx,8 + inc dx + movzx edi,dx ; EDI = heads + and cx,3fh + movzx esi,cx ; ESI = sectors/track + jmp .parmok + +.noparm: + ; No CHS info... this better be our boot drive, then +%if IS_SYSLINUX || IS_EXTLINUX + cmp dl,[DriveNumber] + jne .cb_overflow ; Fatal error! + movzx esi,word [bsSecPerTrack] + movzx edi,word [bsHeads] +%else + ; Not a disk-based derivative... there is no hope + jmp .cb_overflow +%endif + +.parmok: + ; + ; Dividing by sectors to get (track,sector): we may have + ; up to 2^18 tracks, so we need to use 32-bit arithmetric. + ; + div esi + xor cx,cx + xchg cx,dx ; CX <- sector index (0-based) + ; EDX <- 0 + ; eax = track # + div edi ; Convert track to head/cyl + + ; Watch out for overflow, we might be writing! + cmp eax,1023 + ja .cb_overflow + + ; + ; Now we have AX = cyl, DX = head, CX = sector (0-based), + ; BP = sectors to transfer, SI = bsSecPerTrack, + ; ES:BX = data target + ; + + shl ah,6 ; Because IBM was STOOPID + ; and thought 8 bits were enough + ; then thought 10 bits were enough... + inc cx ; Sector numbers are 1-based, sigh + or cl,ah + mov ch,al + mov dh,dl + mov dl,[ADVDrive] + mov al,01h ; Transfer one sector + mov ah,[ADVOp] ; Operation + + mov bp,adv_retries +.cb_retry: + pushad + int 13h + popad + jc .cb_error + +.cb_done: + pop bp + pop eax + pop edx + pop si + ret + +.cb_error: + dec bp + jnz .cb_retry +.cb_overflow: + stc + jmp .cb_done + + section .data + alignz 4 +ADVSec0 dd 0 ; Not specified +ADVSec1 dd 0 ; Not specified +ADVDrive db -1 ; No ADV defined +ADVCHSInfo db -1 ; We have CHS info for this drive + + section .bss +ADVOp resb 1 diff --git a/core/bcopy32.inc b/core/bcopy32.inc new file mode 100644 index 0000000..c363373 --- /dev/null +++ b/core/bcopy32.inc @@ -0,0 +1,364 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved +;; Copyright 2009 Intel Corporation; author: H. Peter Anvin +;; +;; 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; bcopy32.inc +;; +;; 32-bit bcopy routine for real mode +;; + +; +; 32-bit bcopy routine for real mode +; +; We enter protected mode, set up a flat 32-bit environment, run rep movsd +; and then exit. IMPORTANT: This code assumes cs == 0. +; +; This code is probably excessively anal-retentive in its handling of +; segments, but this stuff is painful enough as it is without having to rely +; on everything happening "as it ought to." +; + + bits 16 + section .text + +; +; bcopy: +; 32-bit copy, overlap safe +; +; Inputs: +; ESI - source pointer (-1 means do bzero rather than bcopy) +; EDI - target pointer +; ECX - byte count +; DF - zero +; +; Outputs: +; ESI - first byte after source (garbage if ESI == -1 on entry) +; EDI - first byte after target +; +bcopy: jecxz .ret + pushad + push word pm_bcopy + call simple_pm_call + popad + add edi,ecx + add esi,ecx +.ret: ret + +; +; shuffle_and_boot_raw: +; The new version of shuffle and boot. +; Inputs: +; ESI -> Pointer to list of (dst, src, len) pairs(*) +; EDI -> Pointer to safe area for list + shuffler +; (must not overlap this code nor the RM stack) +; ECX -> Byte count of list area (for initial copy) +; +; If src == -1: then the memory pointed to by (dst, len) is bzeroed; +; this is handled inside the bcopy routine. +; +; If len == 0: this marks the end of the list; dst indicates +; the entry point and src the mode (0 = pm, 1 = rm) +; +shuffle_and_boot_raw: + push word pm_shuffle + call simple_pm_call + ; Never returns... + jmp kaboom + +; +; This routine is used to invoke a simple routine in 32-bit protected +; mode (with 32-bit zero-based CS, DS, ES, and SS, with ESP pointing to the +; real-mode stack even if the real-mode stack was in a nonzero SS.) +; +; No interrupt thunking services are provided; interrupts are disabled +; for the duration of the routine. Don't run for too long at a time +; unless you really mean it. +; +; Inputs: +; On stack - pm entrypoint (IP only) +; EAX, EBP preserved until real-mode exit +; EBX, ECX, EDX, ESI and EDI passed to the called routine +; +; Outputs: +; EAX, EBP restored from real-mode entry +; All other registers as returned from called function +; PM entrypoint cleaned off stack +; +simple_pm_call: + push eax + push ebp + movzx ebp,sp ; BP is used as frame pointer + pushfd ; Saves, among others, the IF flag + push ds + push es + push fs + push gs + + cli + call enable_a20 + + mov byte [cs:bcopy_gdt.TSS+5],89h ; Mark TSS unbusy + + ; Convert the stack segment to a base + xor eax,eax + mov ax,ss + shl eax,4 + add ebp,eax ; EBP is now an absolute frame ptr + + ; Save the old segmented stack pointer + mov [cs:.rm_esp],esp + mov [cs:.rm_ss],ss + + o32 lgdt [cs:bcopy_gdt] + mov eax,cr0 + or al,1 + mov cr0,eax ; Enter protected mode + jmp PM_CS32:.in_pm + + bits 32 +.in_pm: + mov ax,PM_DS32 + mov ss,eax + lea esp,[ebp-8*4-2*4] ; Flat mode stack + mov es,eax + mov ds,eax + + ; Set fs, gs, tr, and ldtr in case we're on a virtual + ; machine running on Intel VT hardware -- it can't + ; deal with a partial transition, for no good reason. + + mov al,PM_DS16 ; Real-mode-like segment + mov fs,eax + mov gs,eax + mov al,PM_TSS ; Intel VT really doesn't want + ltr ax ; an invalid TR and LDTR, so give + xor eax,eax ; it something that it can use... + lldt ax ; (sigh) + + movzx eax,word [ebp+2*4+2] + call eax ; Call actual routine + + jmp PM_CS16:.exit + bits 16 +.exit: + mov ax,PM_DS16 ; "Real-mode-like" data segment + mov es,eax + mov ds,eax + mov ss,eax + + mov eax,cr0 + and al,~1 + mov cr0,eax ; Disable protected mode + jmp 0:.in_rm + +.in_rm: ; Back in real mode + lss esp,[cs:.rm_esp] ; Restore the stack + pop gs + pop fs + pop es + pop ds + + popfd ; Re-enables interrupts + pop ebp + pop eax + ret 2 ; Drops the pm entry + + section .bss + alignb 4 +.rm_esp resd 1 +.rm_ss resw 1 + + + section .text +; +; Routines to enable and disable (yuck) A20. These routines are gathered +; from tips from a couple of sources, including the Linux kernel and +; http://www.x86.org/. The need for the delay to be as large as given here +; is indicated by Donnie Barnes of RedHat, the problematic system being an +; IBM ThinkPad 760EL. +; + + section .data + alignz 2 +A20Ptr dw a20_dunno + + section .bss + alignb 4 +A20Test resd 1 ; Counter for testing A20 status +A20Tries resb 1 ; Times until giving up on A20 + + section .text +enable_a20: + pushad + mov byte [cs:A20Tries],255 ; Times to try to make this work + +try_enable_a20: + +; +; First, see if we are on a system with no A20 gate, or the A20 gate +; is already enabled for us... +; +a20_none: + call a20_test + jnz a20_done + ; Otherwise, see if we had something memorized... + jmp word [cs:A20Ptr] + +; +; Next, try the BIOS (INT 15h AX=2401h) +; +a20_dunno: +a20_bios: + mov word [cs:A20Ptr], a20_bios + mov ax,2401h + pushf ; Some BIOSes muck with IF + int 15h + popf + + call a20_test + jnz a20_done + +; +; Enable the keyboard controller A20 gate +; +a20_kbc: + mov dl, 1 ; Allow early exit + call empty_8042 + jnz a20_done ; A20 live, no need to use KBC + + mov word [cs:A20Ptr], a20_kbc ; Starting KBC command sequence + + mov al,0D1h ; Write output port + out 064h, al + call empty_8042_uncond + + mov al,0DFh ; A20 on + out 060h, al + call empty_8042_uncond + + ; Apparently the UHCI spec assumes that A20 toggle + ; ends with a null command (assumed to be for sychronization?) + ; Put it here to see if it helps anything... + mov al,0FFh ; Null command + out 064h, al + call empty_8042_uncond + + ; Verify that A20 actually is enabled. Do that by + ; observing a word in low memory and the same word in + ; the HMA until they are no longer coherent. Note that + ; we don't do the same check in the disable case, because + ; we don't want to *require* A20 masking (SYSLINUX should + ; work fine without it, if the BIOS does.) +.kbc_wait: push cx + xor cx,cx +.kbc_wait_loop: + call a20_test + jnz a20_done_pop + loop .kbc_wait_loop + + pop cx +; +; Running out of options here. Final attempt: enable the "fast A20 gate" +; +a20_fast: + mov word [cs:A20Ptr], a20_fast + in al, 092h + or al,02h + and al,~01h ; Don't accidentally reset the machine! + out 092h, al + +.fast_wait: push cx + xor cx,cx +.fast_wait_loop: + call a20_test + jnz a20_done_pop + loop .fast_wait_loop + + pop cx + +; +; Oh bugger. A20 is not responding. Try frobbing it again; eventually give up +; and report failure to the user. +; + dec byte [cs:A20Tries] + jnz a20_dunno ; Did we get the wrong type? + + mov si, err_a20 + jmp abort_load + + section .data +err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0 + section .text + +; +; A20 unmasked, proceed... +; +a20_done_pop: pop cx +a20_done: popad + ret + +; +; This routine tests if A20 is enabled (ZF = 0). This routine +; must not destroy any register contents. +; +; The no-write early out avoids the io_delay in the (presumably common) +; case of A20 already enabled (e.g. from a previous call.) +; +a20_test: + push es + push cx + push eax + mov cx,0FFFFh ; HMA = segment 0FFFFh + mov es,cx + mov eax,[cs:A20Test] + mov cx,32 ; Loop count + jmp .test ; First iteration = early out +.wait: add eax,0x430aea41 ; A large prime number + mov [cs:A20Test],eax + io_delay ; Serialize, and fix delay +.test: cmp eax,[es:A20Test+10h] + loopz .wait +.done: pop eax + pop cx + pop es + ret + +; +; Routine to empty the 8042 KBC controller. If dl != 0 +; then we will test A20 in the loop and exit if A20 is +; suddenly enabled. +; +empty_8042_uncond: + xor dl,dl +empty_8042: + call a20_test + jz .a20_on + and dl,dl + jnz .done +.a20_on: io_delay + in al, 064h ; Status port + test al,1 + jz .no_output + io_delay + in al, 060h ; Read input + jmp short empty_8042 +.no_output: + test al,2 + jnz empty_8042 + io_delay +.done: ret + +; +; The 32-bit copy and shuffle code is "special", so it is in its own file +; +%include "bcopyxx.inc" diff --git a/core/bcopyxx.inc b/core/bcopyxx.inc new file mode 100644 index 0000000..89ae4f4 --- /dev/null +++ b/core/bcopyxx.inc @@ -0,0 +1,315 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved +;; Copyright 2009 Intel Corporation; author: H. Peter Anvin +;; +;; 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; bcopy32xx.inc +;; + + +; +; 32-bit bcopy routine +; +; This is the actual 32-bit portion of the bcopy and shuffle and boot +; routines. ALL THIS CODE NEEDS TO BE POSITION-INDEPENDENT, with the +; sole exception being the actual relocation code at the beginning of +; pm_shuffle_boot. +; +; It also really needs to live all in a single segment, for the +; address calculcations to actually work. +; + + bits 32 + section .bcopyxx + align 16 +bcopyxx_start equ $ +; +; pm_bcopy: +; +; This is the protected-mode core of the "bcopy" routine. +; Try to do aligned transfers; if the src and dst are relatively +; misaligned, align the dst. +; +; ECX is guaranteed to not be zero on entry. +; +; Clobbers ESI, EDI, ECX. +; + +pm_bcopy: + push ebx + push edx + push eax + + cmp esi,-1 + je .bzero + + cmp esi,edi ; If source < destination, we might + jb .reverse ; have to copy backwards + +.forward: + ; Initial alignment + mov edx,edi + shr edx,1 + jnc .faa1 + movsb + dec ecx +.faa1: + mov al,cl + cmp ecx,2 + jb .f_tiny + + shr edx,1 + jnc .faa2 + movsw + sub ecx,2 +.faa2: + + ; Bulk transfer + mov al,cl ; Save low bits + shr ecx,2 ; Convert to dwords + rep movsd ; Do our business + ; At this point ecx == 0 + + test al,2 + jz .fab2 + movsw +.fab2: +.f_tiny: + test al,1 + jz .fab1 + movsb +.fab1: +.done: + pop eax + pop edx + pop ebx + ret + +.reverse: + std ; Reverse copy + + lea esi,[esi+ecx-1] ; Point to final byte + lea edi,[edi+ecx-1] + + ; Initial alignment + mov edx,edi + shr edx,1 + jc .raa1 + movsb + dec ecx +.raa1: + + dec esi + dec edi + mov al,cl + cmp ecx,2 + jb .r_tiny + shr edx,1 + jc .raa2 + movsw + sub ecx,2 +.raa2: + + ; Bulk copy + sub esi,2 + sub edi,2 + mov al,cl ; Save low bits + shr ecx,2 + rep movsd + + ; Final alignment +.r_final: + add esi,2 + add edi,2 + test al,2 + jz .rab2 + movsw +.rab2: +.r_tiny: + inc esi + inc edi + test al,1 + jz .rab1 + movsb +.rab1: + cld + jmp short .done + +.bzero: + xor eax,eax + + ; Initial alignment + mov edx,edi + shr edx,1 + jnc .zaa1 + stosb + dec ecx +.zaa1: + + mov bl,cl + cmp ecx,2 + jb .z_tiny + shr edx,1 + jnc .zaa2 + stosw + sub ecx,2 +.zaa2: + + ; Bulk + mov bl,cl ; Save low bits + shr ecx,2 + rep stosd + + test bl,2 + jz .zab2 + stosw +.zab2: +.z_tiny: + test bl,1 + jz .zab1 + stosb +.zab1: + jmp short .done + +; +; shuffle_and_boot: +; +; This routine is used to shuffle memory around, followed by +; invoking an entry point somewhere in low memory. This routine +; can clobber any memory outside the bcopy special area. +; +; IMPORTANT: This routine does not set up any registers. +; It is the responsibility of the caller to generate an appropriate entry +; stub; *especially* when going to real mode. +; +; Inputs: +; ESI -> Pointer to list of (dst, src, len) pairs(*) +; EDI -> Pointer to safe area for list + shuffler +; (must not overlap this code nor the RM stack) +; ECX -> Byte count of list area (for initial copy) +; +; If src == -1: then the memory pointed to by (dst, len) is bzeroed; +; this is handled inside the bcopy routine. +; +; If len == 0: this marks the end of the list; dst indicates +; the entry point and src the mode (0 = pm, 1 = rm) +; +pm_shuffle: + mov ebx,edi ; EBX <- descriptor list + lea edx,[edi+ecx+15] ; EDX <- where to relocate our code to + and edx,~15 ; Align 16 to benefit the GDT + call pm_bcopy + mov edi,edx + mov esi,bcopyxx_start + mov ecx,bcopyxx_dwords + lea eax,[edx+.safe-bcopyxx_start] ; Resume point + ; Relocate this code + rep movsd + jmp eax ; Jump to safe location +.safe: + ; Give ourselves a safe stack + lea esp,[edx+bcopyxx_stack+bcopyxx_end-bcopyxx_start] + add edx,bcopy_gdt-bcopyxx_start + mov [edx+2],edx ; GDT self-pointer + lgdt [edx] ; Switch to local GDT + + ; Now for the actual shuffling... +.loop: + mov edi,[ebx] + mov esi,[ebx+4] + mov ecx,[ebx+8] + add ebx,12 + jecxz .done + call pm_bcopy + jmp .loop +.done: + push ecx ; == 0, for cleaning the flags register + and esi,esi + jz pm_shuffle_real_mode + popfd ; Clean the flags + jmp edi ; Protected mode entry + + ; We have a real-mode entry point, so we need to return + ; to real mode. Note: EDX already points to the GDT. +pm_shuffle_real_mode: + mov eax,edi + mov [edx+PM_CS16+2],ax + mov [edx+PM_DS16+2],ax + shr eax,16 + mov [edx+PM_CS16+4],al + mov [edx+PM_CS16+7],ah + mov [edx+PM_DS16+4],al + mov [edx+PM_DS16+7],ah + mov eax,cr0 + and al,~1 + popfd ; Clean the flags + ; No flag-changing instructions below... + mov dx,PM_DS16 + mov ds,edx + mov es,edx + mov fs,edx + mov gs,edx + mov ss,edx + jmp PM_CS16:0 + + align 16 +; GDT descriptor entry +%macro desc 1 +bcopy_gdt.%1: +PM_%1 equ bcopy_gdt.%1-bcopy_gdt +%endmacro + +bcopy_gdt: + dw bcopy_gdt_size-1 ; Null descriptor - contains GDT + dd bcopy_gdt ; pointer for LGDT instruction + dw 0 + + ; TSS segment to keep Intel VT happy. Intel VT is + ; unhappy about anything that doesn't smell like a + ; full-blown 32-bit OS. + desc TSS + dw 104-1, DummyTSS ; 08h 32-bit task state segment + dd 00008900h ; present, dpl 0, 104 bytes @DummyTSS + + desc CS16 + dd 0000ffffh ; 10h Code segment, use16, readable, + dd 00009b00h ; present, dpl 0, cover 64K + desc DS16 + dd 0000ffffh ; 18h Data segment, use16, read/write, + dd 00009300h ; present, dpl 0, cover 64K + desc CS32 + dd 0000ffffh ; 20h Code segment, use32, readable, + dd 00cf9b00h ; present, dpl 0, cover all 4G + desc DS32 + dd 0000ffffh ; 28h Data segment, use32, read/write, + dd 00cf9300h ; present, dpl 0, cover all 4G + +bcopy_gdt_size: equ $-bcopy_gdt + + alignz 4 +bcopyxx_end equ $ ; *Must* be dword-aligned! +bcopyxx_len equ $-bcopyxx_start +bcopyxx_dwords equ bcopyxx_len >> 2 + +bcopyxx_stack equ 128 ; We want this much stack + ; The +15 is for alignment +bcopyxx_safe equ bcopyxx_len + bcopyxx_stack + 15 + +; +; Space for a dummy task state segment. It should never be actually +; accessed, but just in case it is, point to a chunk of memory that +; has a chance to not be used for anything real... +; +DummyTSS equ 0x580 + + bits 16 + section .text diff --git a/core/bios.inc b/core/bios.inc new file mode 100644 index 0000000..987a216 --- /dev/null +++ b/core/bios.inc @@ -0,0 +1,39 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; bios.inc +;; +;; Header file for the BIOS data structures etc. +;; + +%ifndef _BIOS_INC +%define _BIOS_INC + + absolute 4*1Eh ; In the interrupt table +fdctab equ $ +fdctab1 resw 1 +fdctab2 resw 1 + absolute 0400h +serial_base resw 4 ; Base addresses for 4 serial ports + absolute 0413h +BIOS_fbm resw 1 ; Free Base Memory (kilobytes) + absolute 0462h +BIOS_page resb 1 ; Current video page + absolute 046Ch +BIOS_timer resw 1 ; Timer ticks + absolute 0472h +BIOS_magic resw 1 ; BIOS reset magic + absolute 0484h +BIOS_vidrows resb 1 ; Number of screen rows + +%endif ; _BIOS_INC diff --git a/core/bootsect.inc b/core/bootsect.inc new file mode 100644 index 0000000..b107eb5 --- /dev/null +++ b/core/bootsect.inc @@ -0,0 +1,214 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved +;; Copyright 2009 Intel Corporation; author: H. Peter Anvin +;; +;; 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; bootsect.inc +;; +;; Load a boot sector (or other bootstrap program.) +;; +;; Unlike previous versions of this software, this doesn't require that +;; the length is 512 bytes. This allows PXE bootstraps and WinNT +;; "CD boot sectors" to be invoked. +;; + +; +; Load a boot sector +; +is_bootsector: +%if IS_SYSLINUX + ; Transfer zero bytes + push word 0 + jmp short load_bootsec + +is_bss_sector: + ; Transfer the superblock +SuperSize equ $+1 + push word superblock_len_fat16 +%endif +load_bootsec: + mov edi,100000h + mov [trackbuf+4],edi ; Copy from this address + xor dx,dx ; No padding + mov bx,abort_check ; Don't print dots, but allow abort + call load_high + + sub edi,100000h + mov [trackbuf+8],edi ; Save length + + mov eax,7C00h ; Entry point + mov [trackbuf],eax ; Copy to this address + +%if IS_SYSLINUX + xor ecx,ecx + pop cx + + ; For a BSS boot sector we have to patch. + mov esi,superblock + mov edi,100000h+(superblock-bootsec) + call bcopy +%endif + push eax ; Save entry point + + xor edx,edx + xor esi,esi +%if IS_SYSLINUX || IS_EXTLINUX + ; Restore original FDC table + mov eax,[OrigFDCTabPtr] + mov [fdctab],eax + + mov dl,[DriveNumber] + mov si,PartInfo ; Partition info buffer + mov di,800h-18 ; Put partition info here + push di + mov cx,8 ; 16 bytes + xor ax,ax + rep movsw + pop si ; DS:SI points to partition info +%elif IS_ISOLINUX + mov dl,[DriveNumber] +%elif IS_PXELINUX + mov byte [KeepPXE],03h ; Chainloading + keep PXE + call reset_pxe +%endif + xor bx,bx + +; +; replace_bootstrap for the special case where we have exactly one +; descriptor. +; + +replace_bootstrap_one: + push word 1 ; Length of descriptor list + ; Fall through + +; +; Entrypoint for "shut down and replace bootstrap" -- also invoked by +; the COMBOOT API. This routine expects the entry point (CS, IP) and the +; count of the descriptor sequence on the stack; the shuffle +; descriptors start at the first byte of the trackbuf. +; +; The registers EDX and ESI are passed on to the called program, +; and BX is passed on as DS. +; +replace_bootstrap: + ; + ; Prepare for shutting down + ; + call vgaclearmode + call cleanup_hardware + + ; + ; Set up initial stack frame (not used by PXE if keeppxe is + ; set - we use the PXE stack then.) + ; + xor ax,ax + mov ds,ax + mov es,ax + +%if IS_PXELINUX + cmp byte [KeepPXE],0 + je .stdstack + les di,[InitStack] ; Reset stack to PXE original + jmp .stackok +%endif +.stdstack: + mov di,7C00h-44 + push di + mov cx,22 ; 44 bytes + rep stosw + pop di +.stackok: + + mov [es:di+28],edx ; New EDX + mov [es:di+12],esi ; New ESI + mov [es:di+6],bx ; New DS + +%if IS_PXELINUX == 0 + ; DON'T DO THIS FOR PXELINUX... + ; For PXE, ES:BX -> PXENV+, and this would corrupt + ; that use. + + ; Restore ES:DI -> $PnP (if we were ourselves called + ; that way...) + mov ax,[OrigESDI] + mov bx,[OrigESDI+2] + + mov [es:di+8],ax ; New DI + mov [es:di+4],bx ; New ES +%endif + pop ax ; List length + + push di + push es + + push ds + pop es + + mov ebx,trackbuf + imul di,ax,12 + add di,bx ; DI <- end of list + push di + + ; Terminating entry... + lea eax,[replace_stub] ; Entrypoint + push ax + stosd + xor ax,ax ; EAX[31:16] == 0 already + stosd ; 16-bit mode + stosd ; End of list + + ; Copy the stub + pop di + mov si,__replacestub_lma + mov cx,__replacestub_dwords + rep movsd + + xor ecx,ecx + pop cx ; ECX <- length of list + + pop word [replace_stub.ss] + pop word [replace_stub.esp] + pop dword [replace_stub.csip] + + cli + mov ss,[replace_stub.ss] + mov esp,[replace_stub.esp] + + mov edi,trackbuf + mov esi,edi + + jmp shuffle_and_boot_raw + + ; This stub gets run after the shuffle. It is copied + ; below 0x7c00 in order to properly handle the case + ; of bootstrap replacement. + section .replacestub +replace_stub: + mov cr0,eax + jmp 0:.next +.next: + mov ax,strict word 0 +.ss equ $-2 + mov ss,ax + mov esp,strict dword 0 +.esp equ $-4 + pop gs + pop fs + pop es + pop ds + popad + popfd + jmp 0:0 +.csip equ $-4 + + section .text diff --git a/core/cache.inc b/core/cache.inc new file mode 100644 index 0000000..5975557 --- /dev/null +++ b/core/cache.inc @@ -0,0 +1,114 @@ +; -*- fundamental -*- --------------------------------------------------- +; +; Copyright 2004-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +; Boston MA 02111-1307, USA; either version 2 of the License, or +; (at your option) any later version; incorporated herein by reference. +; +; ----------------------------------------------------------------------- + + section .text + + struc cptr +.sector: resd 1 ; Sector number +.prev: resw 1 ; LRU pointer to previous (less recent) +.next: resw 1 ; LRU pointer to next (more recent) + endstruc +cptr_size_lg2 equ 3 + +NCacheEntries equ 65536/SECTOR_SIZE + +; +; initcache: Initialize the cache data structures +; +initcache: + xor eax,eax ; We don't care about sector 0 + mov di,CachePtrs + mov cx,NCacheEntries+1 + mov bx,CachePtrs+NCacheEntries*cptr_size ; "prev" pointer +.loop: + mov [di+cptr.sector],eax ; Zero sector number + mov [di+cptr.prev],bx ; Previous pointer + mov [bx+cptr.next],di ; Previous entry's next pointer + mov bx,di + add di,cptr_size + loop .loop + ret + +; +; getcachesector: Check for a particular sector (EAX) in the sector cache, +; and if it is already there, return a pointer in GS:SI +; otherwise load it and return said pointer. +; +; Assumes CS == DS. +; +getcachesector: + push cx + push bx + push di + mov si,cache_seg + mov gs,si + mov si,CachePtrs+cptr_size ; Real sector cache pointers + mov cx,NCacheEntries +.search: + cmp eax,[si] + jz .hit + add si,cptr_size + loop .search + +.miss: + TRACER 'M' + ; Need to load it. + push es + push gs + pop es + mov bx,[CachePtrs+cptr.next] ; "Next most recent than head node" + mov [bx+cptr.sector],eax + mov si,bx + sub bx,CachePtrs+cptr_size + shl bx,SECTOR_SHIFT-cptr_size_lg2 ; Buffer address + pushad +%if IS_EXTLINUX + call getonesec_ext +%else + call getonesec +%endif + popad + pop es +.hit: + ; Update LRU, then compute buffer address + TRACER 'H' + + ; Remove from current position in the list + mov bx,[si+cptr.prev] + mov di,[si+cptr.next] + mov [bx+cptr.next],di + mov [di+cptr.prev],bx + + ; Add to just before head node + mov bx,[CachePtrs+cptr.prev] + mov [si+cptr.prev],bx + mov [bx+cptr.next],si + mov [CachePtrs+cptr.prev],si + mov word [si+cptr.next],CachePtrs + + sub si,CachePtrs+cptr_size + shl si,SECTOR_SHIFT-cptr_size_lg2 ; Buffer address + + pop di + pop bx + pop cx + ret + + section .bss + + ; Each CachePtr contains: + ; - Block pointer + ; - LRU previous pointer + ; - LRU next pointer + ; The first entry is the head node of the list + alignb 4 +CachePtrs resb (NCacheEntries+1)*cptr_size diff --git a/core/checksumiso.pl b/core/checksumiso.pl new file mode 100755 index 0000000..9b8c3ee --- /dev/null +++ b/core/checksumiso.pl @@ -0,0 +1,51 @@ +#!/usr/bin/perl +# +# Construct a checksum for isolinux*.bin, compatible +# with an mkisofs boot-info-table +# + +use bytes; +use integer; + +($file) = @ARGV; + +open(FILE, '+<', $file) or die "$0: cannot open $file: $!\n"; +binmode FILE; + +@fstat = stat(FILE) or die "$0: stat $file: $!\n"; +if (!$fstat[7]) { + die "$0: $file: cannot query length\n"; +} + +# Pad file to a multiple of 2048 bytes +$frac = $fstat[7] % 2048; +if ($frac) { + seek(FILE,$fstat[7],0) + or die "$0: $file: cannot seek to end\n"; + print FILE "\0" x (2048-$frac); +} + +# Checksum the file post header +if ( !seek(FILE,64,0) ) { + die "$0: $file: cannot seek past header\n"; +} + +$csum = 0; +$bytes = 64; +while ( ($n = read(FILE, $dw, 4)) > 0 ) { + $dw .= "\0\0\0\0"; # Pad to at least 32 bits + ($v) = unpack("V", $dw); + $csum = ($csum + $v) & 0xffffffff; + $bytes += $n; +} + +# Update header +if ( !seek(FILE,16,0) ) { + die "$0: $file: cannot seek to header\n"; +} + +print FILE pack("VV", $bytes, $csum); + +close(FILE); + +exit 0; diff --git a/core/cleanup.inc b/core/cleanup.inc new file mode 100644 index 0000000..063ed73 --- /dev/null +++ b/core/cleanup.inc @@ -0,0 +1,55 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 2007-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; cleanup.inc +;; +;; Some final tidying before jumping to a kernel or bootsector +;; + + section .text +; +; cleanup_hardware: +; +; Shut down anything transient. *No segment assumptions*. +; Preserves all registers. +; +cleanup_hardware: + pushad +; +; Linux wants the floppy motor shut off before starting the kernel, +; at least bootsect.S seems to imply so. If we don't load the floppy +; driver, this is *definitely* so! +; + xor ax,ax + xor dx,dx + int 13h + +%if 0 ; This bug report has not been substantiated! +; Vmware crashes if we scroll in the decompressor! Try to detect vmware +; and if it is Vmware, clear the screen... + mov eax,'VMXh' + xor ebx, ebx + mov ecx, 10 ; Get version + mov dx, 'VX' + in eax, dx + cmp ebx, 'VMXh' + jne .no_vmware + + mov ax,0x0003 ; Set mode (clear screen/home cursor) + int 10h +.no_vmware: +%endif + popad + + ; If we enabled serial port interrupts, clean them up now + jmp sirq_cleanup diff --git a/core/cmdline.inc b/core/cmdline.inc new file mode 100644 index 0000000..642e5e1 --- /dev/null +++ b/core/cmdline.inc @@ -0,0 +1,94 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 2003-2009 H. Peter Anvin - All Rights Reserved +;; Copyright 2009 Intel Corporation; author: H. Peter Anvin +;; +;; 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; cmdline.inc +;; +;; Common routine to assemble [null-terminated] command line into +;; real_mode_seg:cmd_line_here. +;; Not used by plain kernel due to BOOT_IMAGE= etc. +;; + +; +; Assumes DS == CS +; +make_plain_cmdline: + push es + ; ui.inc has already copied any APPEND options + mov ax,real_mode_seg + mov es,ax + + mov di,[CmdLinePtr] + call do_ip_append + + mov si,[CmdOptPtr] + + call strcpy + + dec di + mov [CmdLinePtr],di + + pop es + ret + +; +; Actual IPAppend strings... +; +%if IS_PXELINUX + section .data + alignz 2 +IPAppends dw IPOption + dw BOOTIFStr +numIPAppends equ ($-IPAppends)/2 +%else +IPAppends equ 0 +numIPAppends equ 0 +%endif + +; +; Handle "ipappend" strings, if applicable +; +; Assumes DS == CS; pushes output to ES:DI +; + section .text + +do_ip_append: +%ifndef DEPEND + %if numIPAppends > 0 + push cx + push bx + push si + + mov bx,IPAppends + mov cx,[IPAppend] + and cx,(1 << numIPAppends)-1 +.loop: + jcxz .done + mov si,[bx] + inc bx + inc bx + test cl,1 + jz .not_this + + call strcpy + mov byte [es:di-1],' ' ; Replace final null with space +.not_this: + shr cx,1 + jmp .loop +.done: + pop si + pop bx + pop cx + %endif +%endif + ret diff --git a/core/com32.inc b/core/com32.inc new file mode 100644 index 0000000..7232792 --- /dev/null +++ b/core/com32.inc @@ -0,0 +1,428 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; com32.inc +;; +;; Common code for running a COM32 image +;; + +; +; Load a COM32 image. A COM32 image is the 32-bit analogue to a DOS +; .com file. A COM32 image is loaded at address 0x101000, with %esp +; set to the high end of usable memory. +; +; A COM32 image should begin with the magic bytes: +; B8 FF 4C CD 21, which is "mov eax,0x21cd4cff" in 32-bit mode and +; "mov ax,0x4cff; int 0x21" in 16-bit mode. This will abort the +; program with an error if run in 16-bit mode. +; + + ; We need to make this a proper section rather + ; than using absolute numbers, in order to work + ; around a bug in GNU ld 2.17, which is still in + ; use as of this writing in the form of Debian + ; 4.0 (etch). + bits 32 + section .com32 exec write nobits align=16 +pm_idt equ 0x100000 ; Needs to be absolute... + resb 4096 +pm_entry: ; Needs to not be... + + bits 16 + section .data + alignz 2 +com32_pmidt: + dw 8*256 ; Limit + dd pm_idt ; Address + +com32_rmidt: + dw 0ffffh ; Limit + dd 0 ; Address + + section .text +is_com32_image: + push si ; Save file handle + push eax ; Save file length + + call make_plain_cmdline + ; Copy the command line into the low cmdline buffer + mov ax,real_mode_seg + mov fs,ax + mov si,cmd_line_here + mov di,command_line + mov cx,[CmdLinePtr] + inc cx ; Include final null + sub cx,si + fs rep movsb + + call comboot_setup_api ; Set up the COMBOOT-style API + + mov edi,pm_entry ; Load address + pop eax ; File length + pop si ; File handle + xor dx,dx ; No padding + mov bx,abort_check ; Don't print dots, but allow abort + call load_high + +com32_start: + mov ebx,com32_call_start ; Where to go in PM + +com32_enter_pm: + cli + mov ax,cs + mov ds,ax + mov [RealModeSSSP],sp + mov [RealModeSSSP+2],ss + cld + call a20_test + jnz .a20ok + call enable_a20 + +.a20ok: + mov byte [bcopy_gdt.TSS+5],89h ; Mark TSS unbusy + + lgdt [bcopy_gdt] ; We can use the same GDT just fine + lidt [com32_pmidt] ; Set up the IDT + mov eax,cr0 + or al,1 + mov cr0,eax ; Enter protected mode + jmp PM_CS32:.in_pm + + bits 32 +.in_pm: + xor eax,eax ; Available for future use... + mov fs,eax + mov gs,eax + lldt ax + + mov al,PM_DS32 ; Set up data segments + mov es,eax + mov ds,eax + mov ss,eax + + mov al,PM_TSS ; Be nice to Intel's VT by + ltr ax ; giving it a valid TR + + mov esp,[PMESP] ; Load protmode %esp if available + jmp ebx ; Go to where we need to go + +; +; This is invoked right before the actually starting the COM32 +; progam, in 32-bit mode... +; +com32_call_start: + ; + ; Point the stack to the end of (permitted) high memory + ; + mov esp,[word HighMemRsvd] + xor sp,sp ; Align to a 64K boundary + + ; + ; Set up the protmode IDT and the interrupt jump buffers + ; We set these up in the system area at 0x100000, + ; but we could also put them beyond the stack. + ; + mov edi,pm_idt + + ; Form an interrupt gate descriptor + mov eax,0x00200000+((pm_idt+8*256)&0x0000ffff) + mov ebx,0x0000ee00+((pm_idt+8*256)&0xffff0000) + xor ecx,ecx + inc ch ; ecx <- 256 + + push ecx +.make_idt: + stosd + add eax,8 + xchg eax,ebx + stosd + xchg eax,ebx + loop .make_idt + + pop ecx + + ; Each entry in the interrupt jump buffer contains + ; the following instructions: + ; + ; 00000000 60 pushad + ; 00000001 B0xx mov al, + ; 00000003 E9xxxxxxxx jmp com32_handle_interrupt + + mov eax,0e900b060h + mov ebx,com32_handle_interrupt-(pm_idt+8*256+8) + +.make_ijb: + stosd + sub [edi-2],cl ; Interrupt # + xchg eax,ebx + stosd + sub eax,8 + xchg eax,ebx + loop .make_ijb + + ; Now everything is set up for interrupts... + + push dword [HighMemSize] ; Memory managed by Syslinux + push dword com32_cfarcall ; Cfarcall entry point + push dword com32_farcall ; Farcall entry point + push dword (1 << 16) ; 64K bounce buffer + push dword (xfer_buf_seg << 4) ; Bounce buffer address + push dword com32_intcall ; Intcall entry point + push dword command_line ; Command line pointer + push dword 7 ; Argument count + sti ; Interrupts OK now + call pm_entry ; Run the program... + ; ... on return, fall through to com32_exit ... + +com32_exit: + mov bx,com32_done ; Return to command loop + +com32_enter_rm: + cli + cld + mov [PMESP],esp ; Save exit %esp + xor esp,esp ; Make sure the high bits are zero + jmp PM_CS16:.in_pm16 ; Return to 16-bit mode first + + bits 16 +.in_pm16: + mov ax,PM_DS16 ; Real-mode-like segment + mov es,ax + mov ds,ax + mov ss,ax + mov fs,ax + mov gs,ax + + lidt [com32_rmidt] ; Real-mode IDT (rm needs no GDT) + mov eax,cr0 + and al,~1 + mov cr0,eax + jmp 0:.in_rm + +.in_rm: ; Back in real mode + mov ax,cs ; Set up sane segments + mov ds,ax + mov es,ax + mov fs,ax + mov gs,ax + lss sp,[RealModeSSSP] ; Restore stack + jmp bx ; Go to whereever we need to go... + +com32_done: + sti + jmp enter_command + +; +; 16-bit support code +; + bits 16 + +; +; 16-bit interrupt-handling code +; +com32_int_rm: + pushf ; Flags on stack + push cs ; Return segment + push word .cont ; Return address + push dword edx ; Segment:offset of IVT entry + retf ; Invoke IVT routine +.cont: ; ... on resume ... + mov ebx,com32_int_resume + jmp com32_enter_pm ; Go back to PM + +; +; 16-bit intcall/farcall handling code +; +com32_sys_rm: + pop gs + pop fs + pop es + pop ds + popad + popfd + mov [cs:Com32SysSP],sp + retf ; Invoke routine +.return: + ; We clean up SP here because we don't know if the + ; routine returned with RET, RETF or IRET + mov sp,[cs:Com32SysSP] + pushfd + pushad + push ds + push es + push fs + push gs + mov ebx,com32_syscall.resume + jmp com32_enter_pm + +; +; 16-bit cfarcall handing code +; +com32_cfar_rm: + retf +.return: + mov sp,[cs:Com32SysSP] + mov [cs:RealModeEAX],eax + mov ebx,com32_cfarcall.resume + jmp com32_enter_pm + +; +; 32-bit support code +; + bits 32 + +; +; This is invoked on getting an interrupt in protected mode. At +; this point, we need to context-switch to real mode and invoke +; the interrupt routine. +; +; When this gets invoked, the registers are saved on the stack and +; AL contains the register number. +; +com32_handle_interrupt: + movzx eax,al + xor ebx,ebx ; Actually makes the code smaller + mov edx,[ebx+eax*4] ; Get the segment:offset of the routine + mov bx,com32_int_rm + jmp com32_enter_rm ; Go to real mode + +com32_int_resume: + popad + iret + +; +; Intcall/farcall invocation. We manifest a structure on the real-mode stack, +; containing the com32sys_t structure from as well as +; the following entries (from low to high address): +; - Target offset +; - Target segment +; - Return offset +; - Return segment (== real mode cs == 0) +; - Return flags +; +com32_farcall: + pushfd ; Save IF among other things... + pushad ; We only need to save some, but... + + mov eax,[esp+10*4] ; CS:IP + jmp com32_syscall + + +com32_intcall: + pushfd ; Save IF among other things... + pushad ; We only need to save some, but... + + movzx eax,byte [esp+10*4] ; INT number + mov eax,[eax*4] ; Get CS:IP from low memory + +com32_syscall: + cld + + movzx edi,word [word RealModeSSSP] + movzx ebx,word [word RealModeSSSP+2] + sub edi,54 ; Allocate 54 bytes + mov [word RealModeSSSP],di + shl ebx,4 + add edi,ebx ; Create linear address + + mov esi,[esp+11*4] ; Source regs + xor ecx,ecx + mov cl,11 ; 44 bytes to copy + rep movsd + + ; EAX is already set up to be CS:IP + stosd ; Save in stack frame + mov eax,com32_sys_rm.return ; Return seg:offs + stosd ; Save in stack frame + mov eax,[edi-12] ; Return flags + and eax,0x200cd7 ; Mask (potentially) unsafe flags + mov [edi-12],eax ; Primary flags entry + stosw ; Return flags + + mov bx,com32_sys_rm + jmp com32_enter_rm ; Go to real mode + + ; On return, the 44-byte return structure is on the + ; real-mode stack, plus the 10 additional bytes used + ; by the target address (see above.) +.resume: + movzx esi,word [word RealModeSSSP] + movzx eax,word [word RealModeSSSP+2] + mov edi,[esp+12*4] ; Dest regs + shl eax,4 + add esi,eax ; Create linear address + and edi,edi ; NULL pointer? + jnz .do_copy +.no_copy: mov edi,esi ; Do a dummy copy-to-self +.do_copy: xor ecx,ecx + mov cl,11 ; 44 bytes + rep movsd ; Copy register block + + add dword [word RealModeSSSP],54 ; Remove from stack + + popad + popfd + ret ; Return to 32-bit program + +; +; Cfarcall invocation. We copy the stack frame to the real-mode stack, +; followed by the return CS:IP and the CS:IP of the target function. +; +com32_cfarcall: + pushfd + pushad + + cld + mov ecx,[esp+12*4] ; Size of stack frame + + movzx edi,word [word RealModeSSSP] + movzx ebx,word [word RealModeSSSP+2] + mov [word Com32SysSP],di + sub edi,ecx ; Allocate space for stack frame + and edi,~3 ; Round + sub edi,4*2 ; Return pointer, return value + mov [word RealModeSSSP],di + shl ebx,4 + add edi,ebx ; Create linear address + + mov eax,[esp+10*4] ; CS:IP + stosd ; Save to stack frame + mov eax,com32_cfar_rm.return ; Return seg:off + stosd + mov esi,[esp+11*4] ; Stack frame + mov eax,ecx ; Copy the stack frame + shr ecx,2 + rep movsd + mov ecx,eax + and ecx,3 + rep movsb + + mov bx,com32_cfar_rm + jmp com32_enter_rm + +.resume: + popad + mov eax,[word RealModeEAX] + popfd + ret + + bits 16 + + section .bss1 + alignb 4 +RealModeSSSP resd 1 ; Real-mode SS:SP +RealModeEAX resd 1 ; Real mode EAX +PMESP resd 1 ; Protected-mode ESP +Com32SysSP resw 1 ; SP saved during COM32 syscall + + section .text diff --git a/core/comboot.inc b/core/comboot.inc new file mode 100644 index 0000000..cdba16d --- /dev/null +++ b/core/comboot.inc @@ -0,0 +1,1049 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved +;; Copyright 2009 Intel Corporation; author: H. Peter Anvin +;; +;; 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; comboot.inc +;; +;; Common code for running a COMBOOT image +;; + + section .text + +; Parameter registers definition; this is the definition +; of the stack frame used by INT 21h and INT 22h. +%define P_FLAGS word [bp+44] +%define P_FLAGSL byte [bp+44] +%define P_FLAGSH byte [bp+45] +%define P_CS word [bp+42] +%define P_IP word [bp+40] +%define P_CSIP dword [bp+40] +%define P_DS word [bp+38] +%define P_ES word [bp+36] +%define P_FS word [bp+34] +%define P_GS word [bp+32] +%define P_EAX dword [bp+28] +%define P_AX word [bp+28] +%define P_HAX word [bp+30] +%define P_AL byte [bp+28] +%define P_AH byte [bp+29] +%define P_ECX dword [bp+24] +%define P_CX word [bp+24] +%define P_HCX word [bp+26] +%define P_CL byte [bp+24] +%define P_CH byte [bp+25] +%define P_EDX dword [bp+20] +%define P_DX word [bp+20] +%define P_HDX word [bp+22] +%define P_DL byte [bp+20] +%define P_DH byte [bp+21] +%define P_EBX dword [bp+16] +%define P_BX word [bp+16] +%define P_HBX word [bp+18] +%define P_BL byte [bp+16] +%define P_BH byte [bp+17] +%define P_EBP dword [bp+8] +%define P_BP word [bp+8] +%define P_HBP word [bp+10] +%define P_ESI dword [bp+4] +%define P_SI word [bp+4] +%define P_HSI word [bp+6] +%define P_EDI dword [bp] +%define P_DI word [bp] +%define P_HDI word [bp+2] + +; Looks like a COMBOOT image but too large +comboot_too_large: + call close_file + mov si,err_comlarge + call writestr + jmp enter_command + +; +; Load a COMBOOT image. A COMBOOT image is basically a DOS .COM file, +; except that it may, of course, not contain any DOS system calls. We +; do, however, allow the execution of INT 20h to return to SYSLINUX. +; +is_comboot_image: + push si ; Save file handle + + call make_plain_cmdline + + call comboot_setup_api + + mov cx,comboot_seg + mov es,cx + + xor di,di + mov cx,64 ; 256 bytes (size of PSP) + xor eax,eax ; Clear PSP + rep stosd + + mov word [es:0], 020CDh ; INT 20h instruction + ; First non-free paragraph + ; This is valid because comboot_seg == real_mode_seg + ; == the highest segment used by all derivatives + int 12h ; Get DOS memory size + shl ax,6 ; Kilobytes -> paragraphs + mov word [es:02h],ax + +%ifndef DEPEND +%if real_mode_seg != comboot_seg +%error "This code assumes real_mode_seg == comboot_seg" +%endif +%endif + ; Copy the command line from high memory + mov si,cmd_line_here + mov cx,125 ; Max cmdline len (minus space and CR) + mov di,081h ; Offset in PSP for command line + mov al,' ' ; DOS command lines begin with a space + stosb + +.loop: es lodsb + and al,al + jz .done + stosb + loop .loop +.done: + + mov al,0Dh ; CR after last character + stosb + mov ax,di + sub al,82h ; Include space but not CR + mov [es:80h],al ; Store command line length + + ; Now actually load the file... + pop si ; File handle + mov bx,100h ; Load at :0100h + mov cx,10000h >> SECTOR_SHIFT + ; Absolute maximum # of sectors + call getfssec + cmp ecx,65536-256-2 ; Maximum size + ja comboot_too_large + + ; And invoke the program... + mov ax,es + mov ds,ax + mov ss,ax + xor sp,sp + push word 0 ; Return to address 0 -> exit + + jmp comboot_seg:100h ; Run it + +; Proper return vector +; Note: this gets invoked both via INT 21h and directly via INT 20h. +; We don't need to cld explicitly here, because comboot_exit does that +; when invoking RESET_STACK_AND_SEGS. +comboot_return: cli ; May not have a safe stack + push enter_command ; Normal return to command prompt + jmp comboot_exit + +; +; Set up the COMBOOT API interrupt vectors. This is also used +; by the COM32 code. +; +comboot_setup_api: + mov di,DOSErrTramp ; Error trampolines + mov cx,32 + push cx + mov eax,02EB206Ah ; push 20h; jmp $+4 +.loop1: stosd + inc ah + loop .loop1 + dec di + mov byte [di-1],0E9h + mov ax,comboot_bogus-2 + sub ax,di + stosw + + pop cx ; CX <- 32 + mov si,4*20h ; DOS interrupt vectors + mov bx,comboot_vectors + mov di,DOSSaveVectors +.loop2: + movsd + movzx eax,word [bx] + inc bx + inc bx + mov [si-4],eax + loop .loop2 + ret + + section .bss + alignb 4 +DOSSaveVectors resd 32 + + section .data +%define comboot_err(x) (DOSErrTramp+4*((x)-20h)) + +comboot_vectors: + dw comboot_return ; INT 20 = exit + dw comboot_int21 ; INT 21 = DOS-compatible system calls + dw comboot_int22 ; INT 22 = native system calls + dw comboot_err(23h) ; INT 23 = DOS Ctrl-C handler + dw comboot_err(24h) ; INT 24 = DOS critical error handler + dw comboot_err(25h) ; INT 25 = DOS absolute disk read + dw comboot_err(26h) ; INT 26 = DOS absolute disk write + dw comboot_err(27h) ; INT 27 = DOS TSR + dw comboot_int28 ; INT 28 = DOS idle interrupt + dw comboot_int29 ; INT 29 = DOS fast console output + dw comboot_err(2Ah) ; INT 2A = DOS network API (NetBIOS) + dw comboot_err(2Bh) ; INT 2B = DOS reserved + dw comboot_err(2Ch) ; INT 2C = DOS reserved + dw comboot_iret ; INT 2D = DOS reserved, AMIS + dw comboot_err(2Eh) ; INT 2E = DOS run command + dw comboot_iret ; INT 2F = DOS multiplex interrupt + dw comboot_err(30h) ; INT 30 = DOS CP/M system calls + dw comboot_err(31h) ; INT 31 = DPMI + dw comboot_err(32h) ; INT 32 = DOS reserved + dw comboot_iret ; INT 33 = DOS mouse API + dw comboot_err(34h) ; INT 34 = DOS FPU emulation + dw comboot_err(35h) ; INT 35 = DOS FPU emulation + dw comboot_err(36h) ; INT 36 = DOS FPU emulation + dw comboot_err(37h) ; INT 37 = DOS FPU emulation + dw comboot_err(38h) ; INT 38 = DOS FPU emulation + dw comboot_err(39h) ; INT 39 = DOS FPU emulation + dw comboot_err(3Ah) ; INT 3A = DOS FPU emulation + dw comboot_err(3Bh) ; INT 3B = DOS FPU emulation + dw comboot_err(3Ch) ; INT 3C = DOS FPU emulation + dw comboot_err(3Dh) ; INT 3D = DOS FPU emulation + dw comboot_err(3Eh) ; INT 3E = DOS FPU emulation + dw comboot_err(3Fh) ; INT 3F = DOS overlay manager + + section .text + +; INT 21h: generic DOS system call +comboot_int21: sti + push ds + push es + push fs + push gs + pushad + cld + mov bp,cs + mov ds,bp + mov es,bp + mov bp,sp ; Set up stack frame + + call adjust_screen ; The COMBOOT program might have changed the screen + + mov cx,int21_count + mov si,int21_table +.again: lodsb + cmp al,P_AH + lodsw + loopne .again + ; The last function in the list is the + ; "no such function" function + clc + call ax ; Call the invoked function +comboot_resume: + mov bp,sp ; In case the function clobbers BP + setc P_FLAGSL ; Propagate CF->error + popad + pop gs + pop fs + pop es + pop ds +comboot_iret: + iret + +comboot_bad_int21: + mov ax,P_AX + push P_CSIP + push 21h + ; Fall through + +; Attempted to execute invalid DOS system call +; The interrupt number is on the stack. +comboot_bogus: pop dx ; Interrupt number + pop edi ; CS:IP + mov cx,err_notdos + push comboot_bogus_tail + jmp comboot_exit_msg +comboot_bogus_tail: + xchg ax,dx + call writehex2 ; Interrupt number + mov al,' ' + call writechr + xchg ax,dx + call writehex4 ; Function number (AX) + mov al,' ' + call writechr + mov eax,edi + call writehex8 ; CS:IP of the origin + call crlf + jmp enter_command + +; +; Generic COMBOOT return to command line code +; stack -> where to go next +; CX -> message (for _msg version) +; +comboot_exit: + xor cx,cx +comboot_exit_msg: + pop bx ; Return address + RESET_STACK_AND_SEGS SI ; Contains sti, cld + call adjust_screen ; The COMBOOT program might have changed the screen + pusha + mov si,DOSSaveVectors + mov di,4*20h + mov cx,20h + rep movsd ; Restore DOS-range vectors + popa + jcxz .nomsg + mov si,KernelCName + call writestr + mov si,cx + call writestr +.nomsg: + jmp bx + +; +; INT 21h system calls +; +comboot_getkey: ; 01 = get key with echo + call vgashowcursor + call comboot_getchar + call vgahidecursor + call writechr + clc + ret + +comboot_writechr: ; 02 = writechr + mov al,P_DL + call writechr + clc + ret + +comboot_writeserial: ; 04 = write serial port + mov al,P_DL + call write_serial + clc + ret + +comboot_getkeynoecho: ; 08 = get key w/o echo + call comboot_getchar + clc + ret + +comboot_writestr: ; 09 = write DOS string + mov es,P_DS + mov si,P_DX +.loop: es lodsb + cmp al,'$' ; End string with $ - bizarre + je .done + call writechr + jmp short .loop +.done: clc + ret + +comboot_checkkey: ; 0B = check keyboard status + cmp byte [APIKeyFlag],00h + jnz .waiting + call pollchar +.waiting: setz al + dec al ; AL = 0FFh if present, 0 if not + mov P_AL,al + clc + ret + +comboot_checkver: ; 30 = check DOS version + ; We return 0 in all DOS-compatible version registers, + ; but the high part of eax-ebx-ecx-edx spell "SYSLINUX" + mov P_EAX,'SY' << 16 + mov P_EBX,'SL' << 16 + mov P_ECX,'IN' << 16 + mov P_EDX,'UX' << 16 + ret + +comboot_getchar: + cmp byte [APIKeyFlag],00h + jne .queued + call getchar ; If not queued get input + and al,al ; Function key? (CF <- 0) + jnz .done + mov [APIKeyWait],ah ; High part of key + inc byte [APIKeyFlag] ; Set flag +.done: mov P_AL,al + ret +.queued: mov al,[APIKeyWait] + dec byte [APIKeyFlag] + jmp .done + +; +; INT 28h - DOS idle +; +comboot_int28: + sti + cld + call do_idle + iret + +; +; INT 29h - DOS fast write character +; +comboot_int29: + sti + cld + call writechr ; Preserves registers! + iret + +; +; INT 22h - SYSLINUX-specific system calls +; System call number in ax +; +comboot_int22: + sti + push ds + push es + push fs + push gs + pushad + cld + mov bp,cs + mov ds,bp + mov es,bp + mov bp,sp ; Set up stack frame + + call adjust_screen ; The COMBOOT program might have changed the screen + + cmp ax,int22_count + jb .ok + xor ax,ax ; Function 0 -> unimplemented +.ok: + xchg ax,bx + add bx,bx ; CF <- 0 + call [bx+int22_table] + jmp comboot_resume ; On return + +; +; INT 22h AX=0000h Unimplemented call +; +comapi_err: + stc + ret + +; +; INT 22h AX=0001h Get SYSLINUX version +; +comapi_get_version: + ; Number of API functions supported + mov P_AX,int22_count + ; SYSLINUX version + mov P_CX,(VERSION_MAJOR << 8)+VERSION_MINOR + ; SYSLINUX derivative ID byte + mov P_DX,my_id + ; For future use + mov P_BX,cs ; cs == 0 + + mov P_ES,ds + ; ES:SI -> version banner + mov P_SI,syslinux_banner + ; ES:DI -> copyright string + mov P_DI,copyright_str + +comapi_nop: + clc + ret + +; +; INT 22h AX=0002h Write string +; +; Write null-terminated string in ES:BX +; +comapi_writestr: + mov ds,P_ES + mov si,P_BX + call writestr + clc + ret + +; +; INT 22h AX=0003h Run command +; +; Terminates the COMBOOT program and executes the command line in +; ES:BX as if it had been entered by the user. +; +comapi_run: + mov ds,P_ES + mov si,P_BX + mov di,command_line + call strcpy + push load_kernel ; Run a new kernel + jmp comboot_exit ; Terminate task, clean up + +; +; INT 22h AX=0004h Run default command +; +; Terminates the COMBOOT program and executes the default command line +; as if a timeout had happened or the user pressed . +; +comapi_run_default: + push auto_boot + jmp comboot_exit + +; +; INT 22h AX=0005h Force text mode +; +; Puts the video in standard text mode +; +comapi_textmode: + call vgaclearmode + clc + ret + +; +; INT 22h AX=0006h Open file +; +comapi_open: + push ds + mov ds,P_ES + mov si,P_SI + mov di,InitRD + call mangle_name + pop ds + call searchdir + jz comapi_err + mov P_EAX,eax + mov P_CX,SECTOR_SIZE + mov P_SI,si + clc + ret + +; +; INT 22h AX=0007h Read file +; +comapi_read: + mov es,P_ES + mov bx,P_BX + mov si,P_SI + mov cx,P_CX + call getfssec + jnc .noteof + xor si,si ; SI <- 0 on EOF, CF <- 0 +.noteof: mov P_SI,si + mov P_ECX,ecx + ret + +; +; INT 22h AX=0008h Close file +; +comapi_close: + mov si,P_SI + call close_file + clc + ret + +; +; INT 22h AX=0009h Call PXE stack +; +%if IS_PXELINUX +comapi_pxecall: + mov bx,P_BX + mov es,P_ES + mov di,P_DI + call pxenv + mov ax,[PXEStatus] + mov P_AX,ax + ret +%else +comapi_pxecall equ comapi_err ; Not available +%endif + +; +; INT 22h AX=000Ah Get Derivative-Specific Info +; +comapi_derinfo: + mov P_AL,my_id +%if IS_PXELINUX + mov ax,[APIVer] + mov P_DX,ax + mov ax,[StrucPtr] + mov P_BX,ax + mov ax,[StrucPtr+2] + mov P_ES,ax + mov ax,[InitStack] + mov P_SI,ax + mov ax,[InitStack+2] + mov P_FS,ax +%else + ; Physical medium... + + mov P_CL,SECTOR_SHIFT + mov al,[DriveNumber] + mov P_DL,al + mov P_FS,cs + mov P_SI,OrigESDI +%if IS_SYSLINUX || IS_EXTLINUX + mov P_ES,cs + mov P_BX,PartInfo +%elif IS_ISOLINUX + mov P_ES,cs + mov P_BX,spec_packet + mov ax,[BIOSType] + sub ax,bios_cdrom + shr ax,2 + mov P_CH,al ; Mode (el torito/cbios/ebios) +%endif +%endif + clc + ret + +; +; INT 22h AX=000Bh Get Serial Console Configuration +; +comapi_serialcfg: + mov ax,[SerialPort] + mov P_DX,ax + mov ax,[BaudDivisor] + mov P_CX,ax + mov ax,[FlowControl] + or al,ah + mov ah,[FlowIgnore] + shr ah,4 + test byte [DisplayCon],01h + jnz .normalconsole + or ah,80h +.normalconsole: + mov P_BX,ax + clc + ret + +; +; INT 22h AX=000Ch Perform final cleanup +; +comapi_cleanup: +%if IS_PXELINUX + ; Unload PXE if requested + test dl,3 + setnz [KeepPXE] + sub bp,sp ; unload_pxe may move the stack around + call unload_pxe + add bp,sp ; restore frame pointer... +%elif IS_SYSLINUX || IS_EXTLINUX + ; Restore original FDC table + mov eax,[OrigFDCTabPtr] + mov [fdctab],eax +%endif + call cleanup_hardware + clc + ret + +; +; INT 22h AX=000Dh Clean up then replace bootstrap +; +comapi_chainboot: + call comapi_cleanup + mov eax,P_EDI + mov [trackbuf+4],eax ; Copy from + mov eax,P_ECX + mov [trackbuf+8],eax ; Total bytes + mov eax,7C00h + mov [trackbuf],eax ; Copy to + push eax ; Entry point on stack + mov esi,P_ESI + mov edx,P_EBX + mov bx,P_DS + jmp replace_bootstrap_one + +; +; INT 22h AX=000Eh Get configuration file name +; +comapi_configfile: + mov P_ES,cs + mov P_BX,ConfigName + clc + ret + +; +; INT 22h AX=000Fh Get IPAPPEND strings +; +comapi_ipappend: + mov P_ES,cs + mov P_CX,numIPAppends + mov P_BX,IPAppends + clc + ret + +; +; INT 22h AX=0010h Resolve hostname +; +%if IS_PXELINUX +comapi_dnsresolv: + mov ds,P_ES + mov si,P_BX + call dns_resolv + mov P_EAX,eax + clc + ret +%else +comapi_dnsresolv equ comapi_err +%endif + + section .text + +; +; INT 22h AX=0011h Obsolete +; + +; +; INT 22h AX=0012h Obsolete +; + +; +; INT 22h AX=0013h Idle call +; +comapi_idle: + call do_idle + clc + ret + +; +; INT 22h AX=0014h Local boot +; +%if HAS_LOCALBOOT +comapi_localboot: + mov ax,P_DX + jmp local_boot +%else +comapi_localboot equ comapi_err +%endif ; HAS_LOCALBOOT + +; +; INT 22h AX=0015h Feature flags +; +comapi_features: + mov P_ES,cs + mov P_BX,feature_flags + mov P_CX,feature_flags_len + clc + ret + +; +; INT 22h AX=0016h Run kernel image +; +comapi_runkernel: + mov al,P_DL + cmp al,VK_TYPES-1 + ja .error + mov [KernelType],al + push ds + mov ds,P_DS + mov si,P_SI + mov di,KernelName + call mangle_name + pop ds + call searchdir + jz comapi_err + + ; The kernel image was found, so we can load it... + mov [Kernel_SI],si + mov [Kernel_EAX],eax + + ; It's not just possible, but quite likely, that ES:BX + ; points into real_mode_seg or xfer_buf_seg, so we + ; need to exercise some special care here... use + ; trackbuf as an intermediary + push ds + mov ds,P_ES + mov si,P_BX + mov di,trackbuf + call strcpy + pop ds + +%if IS_PXELINUX + mov al,P_CL + mov [IPAppend],al +%endif + + call comboot_exit + +.finish: + ; Copy the command line into its proper place + push es + mov dx,real_mode_seg + mov es,dx + mov si,trackbuf + mov di,cmd_line_here + call strcpy + mov byte [es:di-1],' ' ; Simulate APPEND + pop es + mov [CmdLinePtr],di + mov word [CmdOptPtr],zero_string + jmp kernel_good_saved + +.error equ comapi_usingvga.error + +; +; INT 22h AX=0017h Report video mode change +; +comapi_usingvga: + mov ax,P_BX + cmp ax,0Fh ; Unknown flags = failure + ja .error + mov [UsingVGA],al + mov cx,P_CX + mov dx,P_DX + mov [GXPixCols],cx + mov [GXPixRows],dx + test al,08h + jnz .notext + call adjust_screen +.notext: + clc + ret +.error: + stc + ret + +; +; INT 22h AX=0018h Query custom font +; +comapi_userfont: + mov al,[UserFont] + and al,al + jz .done + mov al,[VGAFontSize] + mov P_ES,aux_seg + mov P_BX,aux.fontbuf + +.done: ; CF=0 here + mov P_AL,al + ret + +; +; INT 22h AX=0019h Read disk +; +%if IS_SYSLINUX || IS_ISOLINUX || IS_EXTLINUX +comapi_readdisk: + mov esi,P_ESI ; Enforce ESI == EDI == 0, these + or esi,P_EDI ; are reserved for future expansion + jnz .err + mov eax,P_EDX + mov es,P_ES + mov bx,P_BX + mov bp,P_CX ; WE CANNOT use P_* after touching bp! + call getlinsec + clc + ret +.err: + stc + ret +%else +comapi_readdisk equ comapi_err +%endif + +; +; INT 22h AX=001Ah Obsolete +; + +; +; INT 22h AX=001Bh Obsolete +; + +; +; INT 22h AX=001Ch Get pointer to auxillary data vector +; +comapi_getadv: + mov P_ES,ds + mov P_BX,adv0.data + mov P_CX,ADV_LEN + ret + +; +; INT 22h AX=001Dh Write auxillary data vector +; +comapi_writeadv equ adv_write + +; +; INT 22h AX=001Eh Keyboard remapping table +comapi_kbdtable: + cmp P_DX,0 + jne .err + mov P_AX,1 ; Version + mov P_CX,256 ; Length + mov P_ES,cs + mov P_BX,KbdMap + ret +.err: + stc + ret + +; +; INT 22h AX=001Fh Get current working directory +; +comapi_getcwd: + mov P_ES,cs + mov P_BX,CurrentDirName + clc + ret + +; +; INT 22h AX=0020h Open directory +; +%if IS_SYSLINUX +comapi_opendir: + push ds + mov ds,P_ES + mov si,P_SI + mov di,InitRD + call mangle_name + pop ds + call searchdir + jnz comapi_err ; Didn't find a directory + cmp eax,0 + jz comapi_err ; Found nothing + ;ZF is unset + call alloc_fill_dir + mov P_EAX,eax + mov P_CX,SECTOR_SIZE + mov P_SI,si + clc + ret +%else +comapi_opendir equ comapi_err +%endif + +; +; INT 22h AX=0021h Read directory +; +%if IS_SYSLINUX +comapi_readdir: + mov es,P_ES + mov di,P_DI + mov si,P_SI + call readdir + mov P_EAX,eax + mov P_DL,dl + mov P_EBX,ebx + mov P_SI,si + ret +%else +comapi_readdir equ comapi_err +%endif + +; +; INT 22h AX=0022h Close directory +; +%if IS_SYSLINUX +comapi_closedir: + mov si,P_SI + call close_dir + clc + ret +%else +comapi_closedir equ comapi_err +%endif + +; +; INT 22h AX=0023h Query shuffler size +; +comapi_shufsize: + mov P_CX,bcopyxx_safe + ret + +; +; INT 22h AX=0024h Cleanup, shuffle and boot raw +; +comapi_shufraw: + call comapi_cleanup + mov edi,P_EDI + mov esi,P_ESI + mov ecx,P_ECX + jmp shuffle_and_boot_raw + + section .data + +%macro int21 2 + db %1 + dw %2 +%endmacro + +int21_table: + int21 00h, comboot_return + int21 01h, comboot_getkey + int21 02h, comboot_writechr + int21 04h, comboot_writeserial + int21 08h, comboot_getkeynoecho + int21 09h, comboot_writestr + int21 0Bh, comboot_checkkey + int21 30h, comboot_checkver + int21 4Ch, comboot_return + int21 -1, comboot_bad_int21 +int21_count equ ($-int21_table)/3 + + alignz 2 +int22_table: + dw comapi_err ; 0000 unimplemented syscall + dw comapi_get_version ; 0001 get SYSLINUX version + dw comapi_writestr ; 0002 write string + dw comapi_run ; 0003 run specified command + dw comapi_run_default ; 0004 run default command + dw comapi_textmode ; 0005 force text mode + dw comapi_open ; 0006 open file + dw comapi_read ; 0007 read file + dw comapi_close ; 0008 close file + dw comapi_pxecall ; 0009 call PXE stack + dw comapi_derinfo ; 000A derivative-specific info + dw comapi_serialcfg ; 000B get serial port config + dw comapi_cleanup ; 000C perform final cleanup + dw comapi_err ; 000D clean up then bootstrap + dw comapi_configfile ; 000E get name of config file + dw comapi_ipappend ; 000F get ipappend strings + dw comapi_dnsresolv ; 0010 resolve hostname + dw comapi_err ; 0011 maximum shuffle descriptors + dw comapi_err ; 0012 cleanup, shuffle and boot + dw comapi_idle ; 0013 idle call + dw comapi_localboot ; 0014 local boot + dw comapi_features ; 0015 feature flags + dw comapi_runkernel ; 0016 run kernel image + dw comapi_usingvga ; 0017 report video mode change + dw comapi_userfont ; 0018 query custom font + dw comapi_readdisk ; 0019 read disk + dw comapi_err ; 001A cleanup, shuffle and boot to pm + dw comapi_err ; 001B cleanup, shuffle and boot to rm + dw comapi_getadv ; 001C get pointer to ADV + dw comapi_writeadv ; 001D write ADV to disk + dw comapi_kbdtable ; 001E keyboard remapping table + dw comapi_getcwd ; 001F get current working directory + dw comapi_opendir ; 0020 open directory + dw comapi_readdir ; 0021 read directory + dw comapi_closedir ; 0022 close directory + dw comapi_shufsize ; 0023 query shuffler size + dw comapi_shufraw ; 0024 cleanup, shuffle and boot raw +int22_count equ ($-int22_table)/2 + +APIKeyWait db 0 +APIKeyFlag db 0 + +zero_string db 0 ; Empty, null-terminated string + +; +; This is the feature flag array for INT 22h AX=0015h +; +; Note: PXELINUX clears the idle is noop flag if appropriate +; in pxe_detect_nic_type +; +feature_flags: + db 1 ; Have local boot, idle is not noop +feature_flags_len equ ($-feature_flags) + +err_notdos db ': attempted DOS system call INT ',0 +err_comlarge db 'COMBOOT image too large.', CR, LF, 0 + + section .bss1 + alignb 4 +DOSErrTramp resd 33 ; Error trampolines +ConfigName resb FILENAME_MAX +CurrentDirName resb FILENAME_MAX diff --git a/core/config.inc b/core/config.inc new file mode 100644 index 0000000..5a3d1c5 --- /dev/null +++ b/core/config.inc @@ -0,0 +1,39 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 2002-2009 H. Peter Anvin - All Rights Reserved +;; Copyright 2009 Intel Corporation; author: H. Peter Anvin +;; +;; 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; config.inc +;; +;; Common configuration options. Some of these are imposed by the kernel. +;; + +%ifndef _CONFIG_INC +%define _CONFIG_INC + +max_cmd_len equ 2047 ; Must be &3; 2047 is the kernel limit +HIGHMEM_MAX equ 037FFFFFFh ; DEFAULT highest address for an initrd +DEFAULT_BAUD equ 9600 ; Default baud rate for serial port +BAUD_DIVISOR equ 115200 ; Serial port parameter +MAX_FKEYS equ 12 ; Number of F-key help files + +; +; Local boot supported +; +%assign HAS_LOCALBOOT 1 + +; +; Version number definitinons +; +%include "../version.gen" + +%endif ; _CONFIG_INC diff --git a/core/configinit.inc b/core/configinit.inc new file mode 100644 index 0000000..cf6a814 --- /dev/null +++ b/core/configinit.inc @@ -0,0 +1,59 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; configinit.inc +;; +;; Initialize the configuration section +;; + + section .text + +reset_config: + call highmemsize + + ; Initialize the .config section + xor eax,eax + mov si,__config_lma + mov di,__config_start + mov cx,__config_dwords + rep movsd + +%ifndef DEPEND +%if NULLFILE != 0 + mov al,NULLFILE + mov di,FKeyName + mov cx,MAX_FKEYS*(1 << FILENAME_MAX_LG2) + rep stosb +%endif +%endif + + mov si,linuxauto_cmd ; Default command: "linux auto" + mov di,default_cmd + mov cx,linuxauto_len + rep movsb + + mov di,KbdMap ; Default keymap 1:1 + xor al,al + inc ch ; CX <- 256 +mkkeymap: stosb + inc al + loop mkkeymap + + mov eax,[HighMemSize] + mov [VKernelEnd],eax + + ret + + section .data +linuxauto_cmd db 'linux auto',0 +linuxauto_len equ $-linuxauto_cmd diff --git a/core/conio.inc b/core/conio.inc new file mode 100644 index 0000000..16c39c6 --- /dev/null +++ b/core/conio.inc @@ -0,0 +1,431 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved +;; Copyright 2009 Intel Corporation; author: H. Peter Anvin +;; +;; 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; conio.inc +;; +;; Console I/O code, except: +;; writechr, writestr_early - module-dependent +;; writestr, crlf - writestr.inc +;; writehex* - writehex.inc +;; + +; +; loadkeys: Load a LILO-style keymap; file is open on the top of the +; getc stack. +; + section .text + +loadkeys: + mov cx,256 + mov di,trackbuf + call readc + jc .done ; EOF already? + + ; Make sure we are at EOF now... + call getc + jnc .done ; We should be at EOF now! + + ; It was okay, we can now move it into the KbdMap + mov si,trackbuf + mov di,KbdMap + mov cx,256 >> 2 + rep movsd + +.done: + call close + ret + +; +; get_msg_file: Load a text file and write its contents to the screen, +; interpreting color codes. Call with the file already +; on the top of the open/getc stack. +; +; Assumes CS == DS == ES. +; +get_msg_file: + mov byte [TextAttribute],07h ; Default grey on white + mov byte [DisplayMask],07h ; Display text in all modes + call msg_initvars + +print_msg_file: +.getc: + call getc + jc .done + cmp al,1Ah ; DOS EOF? + je .done + movzx cx,byte [UsingVGA] + and cl,01h + inc cx ; CL <- 01h = text mode, + ; 02h = graphics mode + call [NextCharJump] ; Do what shall be done + jmp .getc +.done: + jmp close ; Tailcall! + +msg_putchar: ; Normal character + cmp al,0Fh ; ^O = color code follows + je msg_ctrl_o + cmp al,0Dh ; Ignore + je msg_ignore + cmp al,0Ah ; = newline + je msg_newline + cmp al,0Ch ; = clear screen + je msg_formfeed + cmp al,07h ; = beep + je msg_beep + cmp al,19h ; = return to text mode + je msg_novga + cmp al,18h ; = VGA filename follows + je msg_vga + jnb .not_modectl + cmp al,10h ; 10h to 17h are mode controls + jae msg_modectl +.not_modectl: + +msg_normal: call write_serial_displaymask ; Write to serial port + test [DisplayMask],cl + jz msg_ignore ; Not screen + test byte [DisplayCon],01h + jz msg_ignore + mov bl,[TextAttribute] + mov bh,[BIOS_page] + mov ah,09h ; Write character/attribute + mov cx,1 ; One character only + int 10h ; Write to screen + mov al,[CursorCol] + inc ax + cmp al,[VidCols] + ja msg_line_wrap ; Screen wraparound + mov [CursorCol],al + +msg_gotoxy: mov bh,[BIOS_page] + mov dx,[CursorDX] + mov ah,02h ; Set cursor position + int 10h +msg_ignore: ret + +msg_beep: mov ax,0E07h ; Beep + xor bx,bx + int 10h + ret + +msg_ctrl_o: ; ^O = color code follows + mov word [NextCharJump],msg_setbg + ret +msg_newline: ; Newline char or end of line + mov si,crlf_msg + call write_serial_str_displaymask +msg_line_wrap: ; Screen wraparound + test [DisplayMask],cl + jz msg_ignore + mov byte [CursorCol],0 + mov al,[CursorRow] + inc ax + cmp al,[VidRows] + ja msg_scroll + mov [CursorRow],al + jmp short msg_gotoxy +msg_scroll: xor cx,cx ; Upper left hand corner + mov dx,[ScreenSize] + mov [CursorRow],dh ; New cursor at the bottom + mov bh,[ScrollAttribute] + mov ax,0601h ; Scroll up one line + int 10h + jmp short msg_gotoxy +msg_formfeed: ; Form feed character + mov si,crff_msg + call write_serial_str_displaymask + test [DisplayMask],cl + jz msg_ignore + xor cx,cx + mov [CursorDX],cx ; Upper lefthand corner + mov dx,[ScreenSize] + mov bh,[TextAttribute] + mov ax,0600h ; Clear screen region + int 10h + jmp msg_gotoxy +msg_setbg: ; Color background character + call unhexchar + jc msg_color_bad + shl al,4 + test [DisplayMask],cl + jz .dontset + mov [TextAttribute],al +.dontset: + mov word [NextCharJump],msg_setfg + ret +msg_setfg: ; Color foreground character + call unhexchar + jc msg_color_bad + test [DisplayMask],cl + jz .dontset + or [TextAttribute],al ; setbg set foreground to 0 +.dontset: + jmp short msg_putcharnext +msg_vga: + mov word [NextCharJump],msg_filename + mov di, VGAFileBuf + jmp short msg_setvgafileptr + +msg_color_bad: + mov byte [TextAttribute],07h ; Default attribute +msg_putcharnext: + mov word [NextCharJump],msg_putchar + ret + +msg_filename: ; Getting VGA filename + cmp al,0Ah ; = end of filename + je msg_viewimage + cmp al,' ' + jbe msg_ret ; Ignore space/control char + mov di,[VGAFilePtr] + cmp di,VGAFileBufEnd + jnb msg_ret + mov [di],al ; Can't use stosb (DS:) + inc di +msg_setvgafileptr: + mov [VGAFilePtr],di +msg_ret: ret + +msg_novga: + call vgaclearmode + jmp short msg_initvars + +msg_viewimage: + mov si,[VGAFilePtr] + mov byte [si],0 ; Zero-terminate filename + mov si,VGAFileBuf + mov di,VGAFileMBuf + call mangle_name + call open + jz msg_putcharnext ; Not there + call vgadisplayfile + ; Fall through + + ; Subroutine to initialize variables, also needed + ; after loading a graphics file +msg_initvars: + pusha + mov bh,[BIOS_page] + mov ah,03h ; Read cursor position + int 10h + mov [CursorDX],dx + popa + jmp short msg_putcharnext ; Initialize state machine + +msg_modectl: + and al,07h + mov [DisplayMask],al + jmp short msg_putcharnext + +; +; write_serial: If serial output is enabled, write character on serial port +; write_serial_displaymask: d:o, but ignore if DisplayMask & 04h == 0 +; +write_serial_displaymask: + test byte [DisplayMask], 04h + jz write_serial.end +write_serial: + pushfd + pushad + mov bx,[SerialPort] + and bx,bx + je .noserial + push ax + mov ah,[FlowInput] +.waitspace: + ; Wait for space in transmit register + lea dx,[bx+5] ; DX -> LSR + in al,dx + test al,20h + jz .waitspace + + ; Wait for input flow control + inc dx ; DX -> MSR + in al,dx + and al,ah + cmp al,ah + jne .waitspace +.no_flow: + + xchg dx,bx ; DX -> THR + pop ax + slow_out dx,al ; Send data +.noserial: popad + popfd +.end: ret + +; +; write_serial_str: write_serial for strings +; write_serial_str_displaymask: d:o, but ignore if DisplayMask & 04h == 0 +; +write_serial_str_displaymask: + test byte [DisplayMask], 04h + jz write_serial_str.end + +write_serial_str: +.loop lodsb + and al,al + jz .end + call write_serial + jmp short .loop +.end: ret + +; +; pollchar: check if we have an input character pending (ZF = 0) +; +pollchar: + call do_idle + pushad + mov ah,11h ; Poll keyboard + int 16h + jnz .done ; Keyboard response + mov dx,[SerialPort] + and dx,dx + jz .done ; No serial port -> no input + mov ax,[SerialTail] ; Already-queued input? + cli + cmp ax,[SerialHead] + jne .done_sti ; If so, return ZF = 0 + add dx,5 ; DX -> LSR + in al,dx + test al,1 ; ZF = 0 if data pending + jz .done_sti + inc dx ; DX -> MSR + mov ah,[FlowIgnore] ; Required status bits + in al,dx + and al,ah + cmp al,ah + setne al + dec al ; Set ZF = 0 if equal +.done_sti: sti +.done: popad + ret + +; +; getchar: Read a character from keyboard or serial port +; +getchar.sti_again: + sti +getchar: +.again: + call do_idle + mov ah,11h ; Poll keyboard + int 16h + jnz .kbd ; Keyboard input? + mov bx,[SerialPort] + and bx,bx + jz .again + mov ax,[SerialTail] + cli + cmp ax,[SerialHead] + jne .serial_queued + lea dx,[bx+5] ; DX -> LSR + in al,dx + test al,1 + jz .sti_again + inc dx ; DX -> MSR + mov ah,[FlowIgnore] + in al,dx + and al,ah + cmp al,ah + jne .sti_again +.serial: xor ah,ah ; Avoid confusion + mov dx,bx ; Data port + in al,dx ; Read data + sti + jmp .done +.serial_queued: + sti ; We already know we'll consume data + xchg bx,ax + push ds + mov ax,aux_seg + (aux.serial >> 4) + mov ds,ax + mov al,[bx] + pop ds + inc bx + and bx,serial_buf_size-1 + mov [SerialTail],bx + jmp .done + +.kbd: mov ah,10h ; Get keyboard input + int 16h + cmp al,0E0h + jnz .not_ext + xor al,al +.not_ext: + and al,al + jz .func_key + mov bx,KbdMap ; Convert character sets + xlatb +.func_key: +.done: + jmp reset_idle ; Character received + +%ifdef DEBUG_TRACERS +; +; debug hack to print a character with minimal code impact +; +debug_tracer: pushad + pushfd + mov bp,sp + mov bx,[bp+9*4] ; Get return address + mov al,[cs:bx] ; Get data byte + inc word [bp+9*4] ; Return to after data byte + call writechr + popfd + popad + ret +%endif ; DEBUG_TRACERS + + section .data +%if IS_ISOLINUX == 0 ; Defined elsewhere for ISOLINUX +crlf_msg db CR, LF +null_msg db 0 +%endif +crff_msg db CR, FF, 0 + + section .config + ; This is a word to pc_setint16 can set it +DisplayCon dw 01h ; Console display enabled + +ScrollAttribute db 07h ; Grey on white (normal text color) + + section .bss + alignb 2 +NextCharJump resw 1 ; Routine to interpret next print char +CursorDX equ $ +CursorCol resb 1 ; Cursor column for message file +CursorRow resb 1 ; Cursor row for message file +ScreenSize equ $ +VidCols resb 1 ; Columns on screen-1 +VidRows resb 1 ; Rows on screen-1 + +; Serial console stuff; don't put this in .config becasue we don't want +; loading a new config file to undo this setting. + section .data + alignz 4 +SerialPort dw 0 ; Serial port base (or 0 for no serial port) +BaudDivisor dw 115200/9600 ; Baud rate divisor +FlowControl equ $ +FlowOutput db 0 ; Outputs to assert for serial flow +FlowInput db 0 ; Input bits for serial flow +FlowIgnore db 0 ; Ignore input unless these bits set +FlowDummy db 0 ; Unused + + section .bss +TextAttribute resb 1 ; Text attribute for message file +DisplayMask resb 1 ; Display modes mask + +%include "serirq.inc" diff --git a/core/cpuinit.inc b/core/cpuinit.inc new file mode 100644 index 0000000..4d8cc2e --- /dev/null +++ b/core/cpuinit.inc @@ -0,0 +1,52 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; cpuinit.inc +;; +;; CPU-dependent initialization and related checks. +;; + +check_escapes: + mov ah,02h ; Check keyboard flags + int 16h + mov [KbdFlags],al ; Save for boot prompt check + test al,04h ; Ctrl->skip 386 check + jnz skip_checks + +; +; Now check that there is sufficient low (DOS) memory +; +; NOTE: Linux doesn't use all of real_mode_seg, but we use the same +; segment for COMBOOT images, which can use all 64K +; +dosram_k equ (real_mode_seg+0x1000) >> 6 ; Minimum DOS memory (K) + int 12h + cmp ax,dosram_k + jae enough_ram + mov si,err_noram + call writestr_early + jmp kaboom +enough_ram: +skip_checks: + + section .data +err_noram db 'It appears your computer has less than ' + asciidec dosram_k + db 'K of low ("DOS")' + db CR, LF + db 'RAM. Linux needs at least this amount to boot. If you get' + db CR, LF + db 'this message in error, hold down the Ctrl key while' + db CR, LF + db 'booting, and I will take your word for it.', CR, LF, 0 + section .text diff --git a/core/diskstart.inc b/core/diskstart.inc new file mode 100644 index 0000000..5322922 --- /dev/null +++ b/core/diskstart.inc @@ -0,0 +1,687 @@ +; ----------------------------------------------------------------------- +; +; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved +; Copyright 2009 Intel Corporation; author: H. Peter Anvin +; +; 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, Inc., 51 Franklin St, Fifth Floor, +; Boston MA 02110-1301, USA; either version 2 of the License, or +; (at your option) any later version; incorporated herein by reference. +; +; ----------------------------------------------------------------------- + +; +; diskstart.inc +; +; Common early-bootstrap code for harddisk-based Syslinux derivatives. +; + + ; Expanded superblock + section .bss + alignb 8 +SuperInfo resq 16 ; The first 16 bytes expanded 8 times + + section .text +; +; Some of the things that have to be saved very early are saved +; "close" to the initial stack pointer offset, in order to +; reduce the code size... +; +StackBuf equ $-44-32 ; Start the stack here (grow down - 4K) +PartInfo equ StackBuf ; Saved partition table entry +FloppyTable equ PartInfo+16 ; Floppy info table (must follow PartInfo) +OrigFDCTabPtr equ StackBuf-8 ; The 2nd high dword on the stack +OrigESDI equ StackBuf-4 ; The high dword on the stack + +; +; Primary entry point. Tempting as though it may be, we can't put the +; initial "cli" here; the jmp opcode in the first byte is part of the +; "magic number" (using the term very loosely) for the DOS superblock. +; +bootsec equ $ +_start: jmp short start ; 2 bytes + nop ; 1 byte +; +; "Superblock" follows -- it's in the boot sector, so it's already +; loaded and ready for us +; +bsOemName db MY_NAME ; The SYS command sets this, so... + zb 8-($-bsOemName) + +; +; These are the fields we actually care about. We end up expanding them +; all to dword size early in the code, so generate labels for both +; the expanded and unexpanded versions. +; +%macro superb 1 +bx %+ %1 equ SuperInfo+($-superblock)*8+4 +bs %+ %1 equ $ + zb 1 +%endmacro +%macro superw 1 +bx %+ %1 equ SuperInfo+($-superblock)*8 +bs %+ %1 equ $ + zw 1 +%endmacro +%macro superd 1 +bx %+ %1 equ $ ; no expansion for dwords +bs %+ %1 equ $ + zd 1 +%endmacro +superblock equ $ + superw BytesPerSec + superb SecPerClust + superw ResSectors + superb FATs + superw RootDirEnts + superw Sectors + superb Media + superw FATsecs + superw SecPerTrack + superw Heads +superinfo_size equ ($-superblock)-1 ; How much to expand + superd Hidden + superd HugeSectors + ; + ; This is as far as FAT12/16 and FAT32 are consistent + ; + ; FAT12/16 need 26 more bytes, + ; FAT32 need 54 more bytes + ; +superblock_len_fat16 equ $-superblock+26 +superblock_len_fat32 equ $-superblock+54 + zb 54 ; Maximum needed size +superblock_max equ $-superblock + +SecPerClust equ bxSecPerClust +; +; Note we don't check the constraints above now; we did that at install +; time (we hope!) +; +start: + cli ; No interrupts yet, please + cld ; Copy upwards +; +; Set up the stack +; + xor ax,ax + mov ss,ax + mov sp,StackBuf ; Just below BSS + push es ; Save initial ES:DI -> $PnP pointer + push di + mov es,ax +; +; DS:SI may contain a partition table entry. Preserve it for us. +; + mov cx,8 ; Save partition info + mov di,PartInfo + rep movsw + + mov ds,ax ; Now we can initialize DS... + +; +; Now sautee the BIOS floppy info block to that it will support decent- +; size transfers; the floppy block is 11 bytes and is stored in the +; INT 1Eh vector (brilliant waste of resources, eh?) +; +; Of course, if BIOSes had been properly programmed, we wouldn't have +; had to waste precious space with this code. +; + mov bx,fdctab + lfs si,[bx] ; FS:SI -> original fdctab + push fs ; Save on stack in case we need to bail + push si + + ; Save the old fdctab even if hard disk so the stack layout + ; is the same. The instructions above do not change the flags + mov [DriveNumber],dl ; Save drive number in DL + and dl,dl ; If floppy disk (00-7F), assume no + ; partition table + js harddisk + +floppy: + mov cl,6 ; 12 bytes (CX == 0) + ; es:di -> FloppyTable already + ; This should be safe to do now, interrupts are off... + mov [bx],di ; FloppyTable + mov [bx+2],ax ; Segment 0 + fs rep movsw ; Faster to move words + mov cl,[bsSecPerTrack] ; Patch the sector count + mov [di-8],cl + ; AX == 0 here + int 13h ; Some BIOSes need this + + jmp short not_harddisk +; +; The drive number and possibly partition information was passed to us +; by the BIOS or previous boot loader (MBR). Current "best practice" is to +; trust that rather than what the superblock contains. +; +; Would it be better to zero out bsHidden if we don't have a partition table? +; +; Note: di points to beyond the end of PartInfo +; +harddisk: + test byte [di-16],7Fh ; Sanity check: "active flag" should + jnz no_partition ; be 00 or 80 + mov eax,[di-8] ; Partition offset (dword) + mov [bsHidden],eax +no_partition: +; +; Get disk drive parameters (don't trust the superblock.) Don't do this for +; floppy drives -- INT 13:08 on floppy drives will (may?) return info about +; what the *drive* supports, not about the *media*. Fortunately floppy disks +; tend to have a fixed, well-defined geometry which is stored in the superblock. +; + ; DL == drive # still + mov ah,08h + int 13h + jc no_driveparm + and ah,ah + jnz no_driveparm + shr dx,8 + inc dx ; Contains # of heads - 1 + mov [bsHeads],dx + and cx,3fh + mov [bsSecPerTrack],cx +no_driveparm: +not_harddisk: +; +; Ready to enable interrupts, captain +; + sti + +; +; Do we have EBIOS (EDD)? +; +eddcheck: + mov bx,55AAh + mov ah,41h ; EDD existence query + mov dl,[DriveNumber] + int 13h + jc .noedd + cmp bx,0AA55h + jne .noedd + test cl,1 ; Extended disk access functionality set + jz .noedd + ; + ; We have EDD support... + ; + mov byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2)) +.noedd: + +; +; Load the first sector of LDLINUX.SYS; this used to be all proper +; with parsing the superblock and root directory; it doesn't fit +; together with EBIOS support, unfortunately. +; + mov eax,[FirstSector] ; Sector start + mov bx,ldlinux_sys ; Where to load it + call getonesec + + ; Some modicum of integrity checking + cmp dword [ldlinux_magic+4],LDLINUX_MAGIC^HEXDATE + jne kaboom + + ; Go for it... + jmp ldlinux_ent + +; +; getonesec: get one disk sector +; +getonesec: + mov bp,1 ; One sector + ; Fall through + +; +; getlinsec: load a sequence of BP floppy sector given by the linear sector +; number in EAX into the buffer at ES:BX. We try to optimize +; by loading up to a whole track at a time, but the user +; is responsible for not crossing a 64K boundary. +; (Yes, BP is weird for a count, but it was available...) +; +; On return, BX points to the first byte after the transferred +; block. +; +; This routine assumes CS == DS, and trashes most registers. +; +; Stylistic note: use "xchg" instead of "mov" when the source is a register +; that is dead from that point; this saves space. However, please keep +; the order to dst,src to keep things sane. +; +getlinsec: + add eax,[bsHidden] ; Add partition offset + xor edx,edx ; Zero-extend LBA (eventually allow 64 bits) + +.jmp: jmp strict short getlinsec_cbios + +; +; getlinsec_ebios: +; +; getlinsec implementation for EBIOS (EDD) +; +getlinsec_ebios: +.loop: + push bp ; Sectors left +.retry2: + call maxtrans ; Enforce maximum transfer size + movzx edi,bp ; Sectors we are about to read + mov cx,retry_count +.retry: + + ; Form DAPA on stack + push edx + push eax + push es + push bx + push di + push word 16 + mov si,sp + pushad + mov dl,[DriveNumber] + push ds + push ss + pop ds ; DS <- SS + mov ah,42h ; Extended Read + int 13h + pop ds + popad + lea sp,[si+16] ; Remove DAPA + jc .error + pop bp + add eax,edi ; Advance sector pointer + sub bp,di ; Sectors left + shl di,SECTOR_SHIFT ; 512-byte sectors + add bx,di ; Advance buffer pointer + and bp,bp + jnz .loop + + ret + +.error: + ; Some systems seem to get "stuck" in an error state when + ; using EBIOS. Doesn't happen when using CBIOS, which is + ; good, since some other systems get timeout failures + ; waiting for the floppy disk to spin up. + + pushad ; Try resetting the device + xor ax,ax + mov dl,[DriveNumber] + int 13h + popad + loop .retry ; CX-- and jump if not zero + + ;shr word [MaxTransfer],1 ; Reduce the transfer size + ;jnz .retry2 + + ; Total failure. Try falling back to CBIOS. + mov byte [getlinsec.jmp+1],(getlinsec_cbios-(getlinsec.jmp+2)) + ;mov byte [MaxTransfer],63 ; Max possibe CBIOS transfer + + pop bp + ; ... fall through ... + +; +; getlinsec_cbios: +; +; getlinsec implementation for legacy CBIOS +; +getlinsec_cbios: +.loop: + push edx + push eax + push bp + push bx + + movzx esi,word [bsSecPerTrack] + movzx edi,word [bsHeads] + ; + ; Dividing by sectors to get (track,sector): we may have + ; up to 2^18 tracks, so we need to use 32-bit arithmetric. + ; + div esi + xor cx,cx + xchg cx,dx ; CX <- sector index (0-based) + ; EDX <- 0 + ; eax = track # + div edi ; Convert track to head/cyl + + ; We should test this, but it doesn't fit... + ; cmp eax,1023 + ; ja .error + + ; + ; Now we have AX = cyl, DX = head, CX = sector (0-based), + ; BP = sectors to transfer, SI = bsSecPerTrack, + ; ES:BX = data target + ; + + call maxtrans ; Enforce maximum transfer size + + ; Must not cross track boundaries, so BP <= SI-CX + sub si,cx + cmp bp,si + jna .bp_ok + mov bp,si +.bp_ok: + + shl ah,6 ; Because IBM was STOOPID + ; and thought 8 bits were enough + ; then thought 10 bits were enough... + inc cx ; Sector numbers are 1-based, sigh + or cl,ah + mov ch,al + mov dh,dl + mov dl,[DriveNumber] + xchg ax,bp ; Sector to transfer count + mov ah,02h ; Read sectors + mov bp,retry_count +.retry: + pushad + int 13h + popad + jc .error +.resume: + movzx ecx,al ; ECX <- sectors transferred + shl ax,SECTOR_SHIFT ; Convert sectors in AL to bytes in AX + pop bx + add bx,ax + pop bp + pop eax + pop edx + add eax,ecx + sub bp,cx + jnz .loop + ret + +.error: + dec bp + jnz .retry + + xchg ax,bp ; Sectors transferred <- 0 + shr word [MaxTransfer],1 + jnz .resume + ; Fall through to disk_error + +; +; kaboom: write a message and bail out. +; +disk_error: +kaboom: + xor si,si + mov ss,si + mov sp,StackBuf-4 ; Reset stack + mov ds,si ; Reset data segment + pop dword [fdctab] ; Restore FDC table +.patch: ; When we have full code, intercept here + mov si,bailmsg + + ; Write error message, this assumes screen page 0 +.loop: lodsb + and al,al + jz .done + mov ah,0Eh ; Write to screen as TTY + mov bx,0007h ; Attribute + int 10h + jmp short .loop +.done: + cbw ; AH <- 0 +.again: int 16h ; Wait for keypress + ; NB: replaced by int 18h if + ; chosen at install time.. + int 19h ; And try once more to boot... +.norge: jmp short .norge ; If int 19h returned; this is the end + +; +; Truncate BP to MaxTransfer +; +maxtrans: + cmp bp,[MaxTransfer] + jna .ok + mov bp,[MaxTransfer] +.ok: ret + +; +; Error message on failure +; +bailmsg: db 'Boot error', 0Dh, 0Ah, 0 + + ; This fails if the boot sector overflows + zb 1F8h-($-$$) + +FirstSector dd 0xDEADBEEF ; Location of sector 1 +MaxTransfer dw 0x007F ; Max transfer size + +; This field will be filled in 0xAA55 by the installer, but we abuse it +; to house a pointer to the INT 16h instruction at +; kaboom.again, which gets patched to INT 18h in RAID mode. +bootsignature dw kaboom.again-bootsec + +; +; =========================================================================== +; End of boot sector +; =========================================================================== +; Start of LDLINUX.SYS +; =========================================================================== + +ldlinux_sys: + +syslinux_banner db 0Dh, 0Ah + db MY_NAME, ' ', VERSION_STR, ' ', DATE_STR, ' ', 0 + db 0Dh, 0Ah, 1Ah ; EOF if we "type" this in DOS + + alignz 8 +ldlinux_magic dd LDLINUX_MAGIC + dd LDLINUX_MAGIC^HEXDATE + +; +; This area is patched by the installer. It is found by looking for +; LDLINUX_MAGIC, plus 8 bytes. +; +patch_area: +LDLDwords dw 0 ; Total dwords starting at ldlinux_sys, + ; not including ADVs +LDLSectors dw 0 ; Number of sectors - (bootsec+this sec) + ; but including any ADVs +CheckSum dd 0 ; Checksum starting at ldlinux_sys + ; value = LDLINUX_MAGIC - [sum of dwords] +%if IS_EXTLINUX +CurrentDir dd 2 ; "Current" directory inode number +%endif + +; Space for up to 64 sectors, the theoretical maximum +SectorPtrs times 64 dd 0 + +ldlinux_ent: +; +; Note that some BIOSes are buggy and run the boot sector at 07C0:0000 +; instead of 0000:7C00 and the like. We don't want to add anything +; more to the boot sector, so it is written to not assume a fixed +; value in CS, but we don't want to deal with that anymore from now +; on. +; + jmp 0:.next +.next: + sti ; In case of broken INT 13h BIOSes + +; +; Tell the user we got this far +; + mov si,syslinux_banner + call writestr_early + +; +; Tell the user if we're using EBIOS or CBIOS +; +print_bios: + mov si,cbios_name + cmp byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2)) + jne .cbios + mov si,ebios_name +.cbios: + mov [BIOSName],si + call writestr_early + + section .bss +%define HAVE_BIOSNAME 1 +BIOSName resw 1 + + section .text +; +; Now we read the rest of LDLINUX.SYS. Don't bother loading the first +; sector again, though. +; +load_rest: + mov si,SectorPtrs + mov bx,7C00h+2*SECTOR_SIZE ; Where we start loading + mov cx,[LDLSectors] + +.get_chunk: + jcxz .done + xor bp,bp + lodsd ; First sector of this chunk + + mov edx,eax + +.make_chunk: + inc bp + dec cx + jz .chunk_ready + inc edx ; Next linear sector + cmp [si],edx ; Does it match + jnz .chunk_ready ; If not, this is it + add si,4 ; If so, add sector to chunk + jmp short .make_chunk + +.chunk_ready: + call getlinsecsr + shl bp,SECTOR_SHIFT + add bx,bp + jmp .get_chunk + +.done: + +; +; All loaded up, verify that we got what we needed. +; Note: the checksum field is embedded in the checksum region, so +; by the time we get to the end it should all cancel out. +; +verify_checksum: + mov si,ldlinux_sys + mov cx,[LDLDwords] + mov edx,-LDLINUX_MAGIC +.checksum: + lodsd + add edx,eax + loop .checksum + + and edx,edx ; Should be zero + jz all_read ; We're cool, go for it! + +; +; Uh-oh, something went bad... +; + mov si,checksumerr_msg + call writestr_early + jmp kaboom + +; +; ----------------------------------------------------------------------------- +; Subroutines that have to be in the first sector +; ----------------------------------------------------------------------------- + +; +; +; writestr_early: write a null-terminated string to the console +; This assumes we're on page 0. This is only used for early +; messages, so it should be OK. +; +writestr_early: + pushad +.loop: lodsb + and al,al + jz .return + mov ah,0Eh ; Write to screen as TTY + mov bx,0007h ; Attribute + int 10h + jmp short .loop +.return: popad + ret + + +; +; getlinsecsr: save registers, call getlinsec, restore registers +; Save/restore the flags, too, especially IF. +; +getlinsecsr: + pushfd + pushad + call getlinsec + popad + popfd + ret + +; +; Checksum error message +; +checksumerr_msg db ' Load error - ', 0 ; Boot failed appended + +; +; BIOS type string +; +cbios_name db 'CBIOS', 0 +ebios_name db 'EBIOS', 0 + +; +; Debug routine +; +%ifdef debug +safedumpregs: + cmp word [Debug_Magic],0D00Dh + jnz nc_return + jmp dumpregs +%endif + +rl_checkpt equ $ ; Must be <= 8000h + +rl_checkpt_off equ ($-$$) +%ifndef DEPEND +%if rl_checkpt_off > 400h +%error "Sector 1 overflow" +%endif +%endif + +; ---------------------------------------------------------------------------- +; End of code and data that have to be in the first sector +; ---------------------------------------------------------------------------- + +all_read: +; +; Let the user (and programmer!) know we got this far. This used to be +; in Sector 1, but makes a lot more sense here. +; + mov si,copyright_str + call writestr_early + + +; +; Insane hack to expand the DOS superblock to dwords +; +expand_super: + xor eax,eax + mov si,superblock + mov di,SuperInfo + mov cx,superinfo_size +.loop: + lodsw + dec si + stosd ; Store expanded word + xor ah,ah + stosd ; Store expanded byte + loop .loop + +; +; Fall through to the mainline code... +; + section .text diff --git a/core/dnsresolv.inc b/core/dnsresolv.inc new file mode 100644 index 0000000..c2c429c --- /dev/null +++ b/core/dnsresolv.inc @@ -0,0 +1,389 @@ +; -*- fundamental -*- +; ----------------------------------------------------------------------- +; +; Copyright 2004-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +; Bostom MA 02111-1307, USA; either version 2 of the License, or +; (at your option) any later version; incorporated herein by reference. +; +; ----------------------------------------------------------------------- + +; +; dnsresolv.inc +; +; Very simple DNS resolver (assumes recursion-enabled DNS server; +; this should be the normal thing for client-serving DNS servers.) +; + +DNS_PORT equ htons(53) ; Default DNS port +DNS_MAX_PACKET equ 512 ; Defined by protocol +; TFTP uses the range 49152-57343 +DNS_LOCAL_PORT equ htons(60053) ; All local DNS queries come from this port # +DNS_MAX_SERVERS equ 4 ; Max no of DNS servers + + section .text + +; +; Turn a string in DS:SI into a DNS "label set" in ES:DI. +; On return, DI points to the first byte after the label set, +; and SI to the terminating byte. +; +; On return, DX contains the number of dots encountered. +; +dns_mangle: + push ax + push bx + xor dx,dx +.isdot: + inc dx + xor al,al + mov bx,di + stosb +.getbyte: + lodsb + and al,al + jz .endstring + cmp al,':' + jz .endstring + cmp al,'.' + je .isdot + inc byte [es:bx] + stosb + jmp .getbyte +.endstring: + dec si + dec dx ; We always counted one high + cmp byte [es:bx],0 + jz .done + xor al,al + stosb +.done: + pop bx + pop ax + ret + +; +; Compare two sets of DNS labels, in DS:SI and ES:DI; the one in SI +; is allowed pointers relative to a packet in DNSRecvBuf. +; +; Assumes DS == ES. ZF = 1 if same; no registers changed. +; (Note: change reference to [di] to [es:di] to remove DS == ES assumption) +; +dns_compare: + pusha +%if 0 + +.label: + lodsb + cmp al,0C0h + jb .noptr + and al,03Fh ; Get pointer value + mov ah,al ; ... in network byte order! + lodsb + mov si,DNSRecvBuf + add si,ax + jmp .label +.noptr: + cmp al,[di] + jne .done ; Mismatch + inc di + movzx cx,al ; End label? + and cx,cx ; ZF = 1 if match + jz .done + + ; We have a string of bytes that need to match now + repe cmpsb + je .label + +.done: +%else + xor ax,ax +%endif + popa + ret + +; +; Skip past a DNS label set in DS:SI. +; +dns_skiplabel: + push ax + xor ax,ax ; AH == 0 +.loop: + lodsb + cmp al,0C0h ; Pointer? + jae .ptr + and al,al + jz .done + add si,ax + jmp .loop +.ptr: + inc si ; Pointer is two bytes +.done: + pop ax + ret + + ; DNS header format + struc dnshdr +.id: resw 1 +.flags: resw 1 +.qdcount: resw 1 +.ancount: resw 1 +.nscount: resw 1 +.arcount: resw 1 + endstruc + + ; DNS query + struc dnsquery +.qtype: resw 1 +.qclass: resw 1 + endstruc + + ; DNS RR + struc dnsrr +.type: resw 1 +.class: resw 1 +.ttl: resd 1 +.rdlength: resw 1 +.rdata: equ $ + endstruc + + section .bss2 + alignb 2 +DNSSendBuf resb DNS_MAX_PACKET +DNSRecvBuf resb DNS_MAX_PACKET +LocalDomain resb 256 ; Max possible length +DNSServers resd DNS_MAX_SERVERS + + section .data +pxe_udp_write_pkt_dns: +.status: dw 0 ; Status +.sip: dd 0 ; Server IP +.gip: dd 0 ; Gateway IP +.lport: dw DNS_LOCAL_PORT ; Local port +.rport: dw DNS_PORT ; Remote port +.buffersize: dw 0 ; Size of packet +.buffer: dw DNSSendBuf, 0 ; off, seg of buffer + +pxe_udp_read_pkt_dns: +.status: dw 0 ; Status +.sip: dd 0 ; Source IP +.dip: dd 0 ; Destination (our) IP +.rport: dw DNS_PORT ; Remote port +.lport: dw DNS_LOCAL_PORT ; Local port +.buffersize: dw DNS_MAX_PACKET ; Max packet size +.buffer: dw DNSRecvBuf, 0 ; off, seg of buffer + +LastDNSServer dw DNSServers + +; Actual resolver function +; Points to a null-terminated or :-terminated string in DS:SI +; and returns the name in EAX if it exists and can be found. +; If EAX = 0 on exit, the lookup failed. +; +; No segment assumptions permitted. +; + section .text +dns_resolv: + push ds + push es + push di + push bx + push cx + push dx + + push cs + pop es ; ES <- CS + + ; First, build query packet + mov di,DNSSendBuf+dnshdr.flags + inc word [es:di-2] ; New query ID + mov ax,htons(0100h) ; Recursion requested + stosw + mov ax,htons(1) ; One question + stosw + xor ax,ax ; No answers, NS or ARs + stosw + stosw + stosw + + call dns_mangle ; Convert name to DNS labels + + push cs ; DS <- CS + pop ds + + push si ; Save pointer to after DNS string + + ; Initialize... + mov eax,[MyIP] + mov [pxe_udp_read_pkt_dns.dip],eax + + and dx,dx + jnz .fqdn ; If we have dots, assume it's FQDN + dec di ; Remove final null + mov si,LocalDomain + call strcpy ; Uncompressed DNS label set so it ends in null +.fqdn: + + mov ax,htons(1) + stosw ; QTYPE = 1 = A + stosw ; QCLASS = 1 = IN + + sub di,DNSSendBuf + mov [pxe_udp_write_pkt_dns.buffersize],di + + ; Now, send it to the nameserver(s) + ; Outer loop: exponential backoff + ; Inner loop: scan the various DNS servers + + mov bx,TimeoutTable +.backoff: + movzx dx,byte [bx] + mov si,DNSServers +.servers: + cmp si,[LastDNSServer] + jb .moreservers + +.nomoreservers: + inc bx + cmp bx,TimeoutTableEnd + jb .backoff + + xor eax,eax ; Nothing... +.done: + pop si + pop dx + pop cx + pop bx + pop di + pop es + pop ds + ret + +.moreservers: + lodsd ; EAX <- next server + push si + push bx + push cx + push dx + + mov word [pxe_udp_write_pkt_dns.status],0 + + mov [pxe_udp_write_pkt_dns.sip],eax + mov [pxe_udp_read_pkt_dns.sip],eax + xor eax,[MyIP] + and eax,[Netmask] + jz .nogw + mov eax,[Gateway] +.nogw: + mov [pxe_udp_write_pkt_dns.gip],eax + + mov di,pxe_udp_write_pkt_dns + mov bx,PXENV_UDP_WRITE + call pxenv + jc .timeout ; Treat failed transmit as timeout + cmp word [pxe_udp_write_pkt_dns.status],0 + jne .timeout + + mov cx,[BIOS_timer] +.waitrecv: + mov ax,[BIOS_timer] + sub ax,cx + cmp ax,dx + jae .timeout + + mov word [pxe_udp_read_pkt_dns.status],0 + mov word [pxe_udp_read_pkt_dns.buffersize],DNS_MAX_PACKET + mov di,pxe_udp_read_pkt_dns + mov bx,PXENV_UDP_READ + call pxenv + and ax,ax + jnz .waitrecv + cmp [pxe_udp_read_pkt_dns.status],ax + jnz .waitrecv + + ; Got a packet, deal with it... + mov si,DNSRecvBuf + lodsw + cmp ax,[DNSSendBuf] ; ID + jne .waitrecv ; Not ours + + lodsw ; flags + xor al,80h ; Query#/Answer bit + test ax,htons(0F80Fh) + jnz .badness + + lodsw + xchg ah,al ; ntohs + mov cx,ax ; Questions echoed + lodsw + xchg ah,al ; ntohs + push ax ; Replies + lodsw ; NS records + lodsw ; Authority records + + jcxz .qskipped +.skipq: + call dns_skiplabel ; Skip name + add si,4 ; Skip question trailer + loop .skipq + +.qskipped: + pop cx ; Number of replies + jcxz .badness + +.parseanswer: + mov di,DNSSendBuf+dnshdr_size + call dns_compare + pushf + call dns_skiplabel + mov ax,[si+8] ; RDLENGTH + xchg ah,al ; ntohs + popf + jnz .notsame + cmp dword [si],htons(1)*0x10001 ; TYPE = A, CLASS = IN? + jne .notsame + cmp ax,4 ; RDLENGTH = 4? + jne .notsame + ; + ; We hit paydirt here... + ; + mov eax,[si+10] +.gotresult: + add sp,8 ; Drop timeout information + jmp .done + +.notsame: + add si,10 + add si,ax + loop .parseanswer + +.badness: + ; We got back no data from this server. + ; Unfortunately, for a recursive, non-authoritative + ; query there is no such thing as an NXDOMAIN reply, + ; which technically means we can't draw any + ; conclusions. However, in practice that means the + ; domain doesn't exist. If this turns out to be a + ; problem, we may want to add code to go through all + ; the servers before giving up. + + ; If the DNS server wasn't capable of recursion, and + ; isn't capable of giving us an authoritative reply + ; (i.e. neither AA or RA set), then at least try a + ; different setver... + + test word [DNSRecvBuf+dnshdr.flags],htons(0480h) + jz .timeout + + xor eax,eax + jmp .gotresult + +.timeout: + pop dx + pop cx + pop bx + pop si + jmp .servers diff --git a/core/ext2_fs.inc b/core/ext2_fs.inc new file mode 100644 index 0000000..e84efb1 --- /dev/null +++ b/core/ext2_fs.inc @@ -0,0 +1,183 @@ +; ----------------------------------------------------------------------- +; +; Copyright 1998-2008 H. Peter Anvin - 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, Inc., 675 Mass Ave, Cambridge MA 02139, +; USA; either version 2 of the License, or (at your option) any later +; version; incorporated herein by reference. +; +; ----------------------------------------------------------------------- + +; +; ext2_fs.inc +; +; NASM include file for ext2fs data structures +; + +%define EXT2_SUPER_MAGIC 0xEF53 + +%define EXT2_GOOD_OLD_REV 0 ; The good old (original) format +%define EXT2_DYNAMIC_REV 1 ; V2 format w/ dynamic inode sizes +%define EXT2_GOOD_OLD_INODE_SIZE 128 + +; Special inode numbers +%define EXT2_BAD_INO 1 ; Bad blocks inode +%define EXT2_ROOT_INO 2 ; Root inode +%define EXT2_BOOT_LOADER_INO 5 ; Boot loader inode +%define EXT2_UNDEL_DIR_INO 6 ; Undelete directory inode +%define EXT3_RESIZE_INO 7 ; Reserved group descriptors inode +%define EXT3_JOURNAL_INO 8 ; Journal inode + +; We're readonly, so we only care about incompat features. +%define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 +%define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 +%define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 +%define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 +%define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 +%define EXT2_FEATURE_INCOMPAT_ANY 0xffffffff + +%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) + +; +; File types and file modes +; +%define S_IFDIR 0040000o ; Directory +%define S_IFCHR 0020000o ; Character device +%define S_IFBLK 0060000o ; Block device +%define S_IFREG 0100000o ; Regular file +%define S_IFIFO 0010000o ; FIFO +%define S_IFLNK 0120000o ; Symbolic link +%define S_IFSOCK 0140000o ; Socket + +%define S_IFSHIFT 12 + +%define T_IFDIR (S_IFDIR >> S_IFSHIFT) +%define T_IFCHR (S_IFCHR >> S_IFSHIFT) +%define T_IFBLK (S_IFBLK >> S_IFSHIFT) +%define T_IFREG (S_IFREG >> S_IFSHIFT) +%define T_IFIFO (S_IFIFO >> S_IFSHIFT) +%define T_IFLNK (S_IFLNK >> S_IFSHIFT) +%define T_IFSOCK (S_IFSOCK >> S_IFSHIFT) + +; +; Structure definition for the ext2 superblock +; + struc ext2_super_block +s_inodes_count resd 1 ; Inodes count +s_blocks_count resd 1 ; Blocks count +s_r_blocks_count resd 1 ; Reserved blocks count +s_free_blocks_count resd 1 ; Free blocks count +s_free_inodes_count resd 1 ; Free inodes count +s_first_data_block resd 1 ; First Data Block +s_log_block_size resd 1 ; Block size +s_log_frag_size resd 1 ; Fragment size +s_blocks_per_group resd 1 ; # Blocks per group +s_frags_per_group resd 1 ; # Fragments per group +s_inodes_per_group resd 1 ; # Inodes per group +s_mtime resd 1 ; Mount time +s_wtime resd 1 ; Write time +s_mnt_count resw 1 ; Mount count +s_max_mnt_count resw 1 ; Maximal mount count +s_magic resw 1 ; Magic signature +s_state resw 1 ; File system state +s_errors resw 1 ; Behaviour when detecting errors +s_minor_rev_level resw 1 ; minor revision level +s_lastcheck resd 1 ; time of last check +s_checkinterval resd 1 ; max. time between checks +s_creator_os resd 1 ; OS +s_rev_level resd 1 ; Revision level +s_def_resuid resw 1 ; Default uid for reserved blocks +s_def_resgid resw 1 ; Default gid for reserved blocks +s_first_ino resd 1 ; First non-reserved inode +s_inode_size resw 1 ; size of inode structure +s_block_group_nr resw 1 ; block group # of this superblock +s_feature_compat resd 1 ; compatible feature set +s_feature_incompat resd 1 ; incompatible feature set +s_feature_ro_compat resd 1 ; readonly-compatible feature set +s_uuid resb 16 ; 128-bit uuid for volume +s_volume_name resb 16 ; volume name +s_last_mounted resb 64 ; directory where last mounted +s_algorithm_usage_bitmap resd 1 ; For compression +s_prealloc_blocks resb 1 ; Nr of blocks to try to preallocate +s_prealloc_dir_blocks resb 1 ; Nr to preallocate for dirs +s_padding1 resw 1 +s_reserved resd 204 ; Padding to the end of the block + endstruc + +%ifndef DEPEND +%if ext2_super_block_size != 1024 +%error "ext2_super_block definition bogus" +%endif +%endif + +; +; Structure definition for the ext2 inode +; + struc ext2_inode +i_mode resw 1 ; File mode +i_uid resw 1 ; Owner Uid +i_size resd 1 ; Size in bytes +i_atime resd 1 ; Access time +i_ctime resd 1 ; Creation time +i_mtime resd 1 ; Modification time +i_dtime resd 1 ; Deletion Time +i_gid resw 1 ; Group Id +i_links_count resw 1 ; Links count +i_blocks resd 1 ; Blocks count +i_flags resd 1 ; File flags +l_i_reserved1 resd 1 +i_block resd EXT2_N_BLOCKS ; Pointer to blocks +i_version resd 1 ; File version (for NFS) +i_file_acl resd 1 ; File ACL +i_dir_acl resd 1 ; Directory ACL +i_faddr resd 1 ; Fragment address +l_i_frag resb 1 ; Fragment number +l_i_fsize resb 1 ; Fragment size +i_pad1 resw 1 +l_i_reserved2 resd 2 + endstruc + +%ifndef DEPEND +%if ext2_inode_size != 128 +%error "ext2_inode definition bogus" +%endif +%endif + +; +; Structure definition for ext2 block group descriptor +; + struc ext2_group_desc +bg_block_bitmap resd 1 ; Block bitmap block +bg_inode_bitmap resd 1 ; Inode bitmap block +bg_inode_table resd 1 ; Inode table block +bg_free_blocks_count resw 1 ; Free blocks count +bg_free_inodes_count resw 1 ; Free inodes count +bg_used_dirs_count resw 1 ; Used inodes count +bg_pad resw 1 +bg_reserved resd 3 + endstruc + +%ifndef DEPEND +%if ext2_group_desc_size != 32 +%error "ext2_group_desc definition bogus" +%endif +%endif + +%define ext2_group_desc_lg2size 5 + +; +; Structure definition for ext2 directory entry +; + struc ext2_dir_entry +d_inode resd 1 ; Inode number +d_rec_len resw 1 ; Directory entry length +d_name_len resb 1 ; Name length +d_file_type resb 1 ; File type +d_name equ $ + endstruc diff --git a/core/extlinux.asm b/core/extlinux.asm new file mode 100644 index 0000000..ac5fb6f --- /dev/null +++ b/core/extlinux.asm @@ -0,0 +1,931 @@ +; -*- fundamental -*- (asm-mode sucks) +; **************************************************************************** +; +; extlinux.asm +; +; A program to boot Linux kernels off an ext2/ext3 filesystem. +; +; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved +; Copyright 2009 Intel Corporation; author: H. Peter Anvin +; +; 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, Inc., 53 Temple Place Ste 330, +; Boston MA 02111-1307, USA; either version 2 of the License, or +; (at your option) any later version; incorporated herein by reference. +; +; **************************************************************************** + +%define IS_EXTLINUX 1 +%include "head.inc" +%include "ext2_fs.inc" + +; +; Some semi-configurable constants... change on your own risk. +; +my_id equ extlinux_id +; NASM 0.98.38 croaks if these are equ's rather than macros... +FILENAME_MAX_LG2 equ 8 ; log2(Max filename size Including final null) +FILENAME_MAX equ (1 << FILENAME_MAX_LG2) ; Max mangled filename size +NULLFILE equ 0 ; Null character == empty filename +NULLOFFSET equ 0 ; Position in which to look +retry_count equ 16 ; How patient are we with the disk? +%assign HIGHMEM_SLOP 0 ; Avoid this much memory near the top +LDLINUX_MAGIC equ 0x3eb202fe ; A random number to identify ourselves with + +MAX_OPEN_LG2 equ 6 ; log2(Max number of open files) +MAX_OPEN equ (1 << MAX_OPEN_LG2) + +SECTOR_SHIFT equ 9 +SECTOR_SIZE equ (1 << SECTOR_SHIFT) + +MAX_SYMLINKS equ 64 ; Maximum number of symlinks per lookup +SYMLINK_SECTORS equ 2 ; Max number of sectors in a symlink + ; (should be >= FILENAME_MAX) + +ROOT_DIR_WORD equ 0x002F +CUR_DIR_DWORD equ 0x00002F2E + +; +; The following structure is used for "virtual kernels"; i.e. LILO-style +; option labels. The options we permit here are `kernel' and `append +; Since there is no room in the bottom 64K for all of these, we +; stick them in high memory and copy them down before we need them. +; + struc vkernel +vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!** +vk_rname: resb FILENAME_MAX ; Real name +vk_appendlen: resw 1 +vk_type: resb 1 ; Type of file + alignb 4 +vk_append: resb max_cmd_len+1 ; Command line + alignb 4 +vk_end: equ $ ; Should be <= vk_size + endstruc + +; +; File structure. This holds the information for each currently open file. +; + struc open_file_t +file_bytesleft resd 1 ; Number of bytes left (0 = free) +file_sector resd 1 ; Next linear sector to read +file_in_sec resd 1 ; Sector where inode lives +file_in_off resw 1 +file_mode resw 1 + endstruc + +%ifndef DEPEND +%if (open_file_t_size & (open_file_t_size-1)) +%error "open_file_t is not a power of 2" +%endif +%endif + +; --------------------------------------------------------------------------- +; BEGIN CODE +; --------------------------------------------------------------------------- + +; +; Memory below this point is reserved for the BIOS and the MBR +; + section .earlybss +trackbufsize equ 8192 +trackbuf resb trackbufsize ; Track buffer goes here + ; ends at 2800h + + section .bss +SuperBlock resb 1024 ; ext2 superblock +ClustSize resd 1 ; Bytes/cluster ("block") +ClustMask resd 1 ; Sectors/cluster - 1 +PtrsPerBlock1 resd 1 ; Pointers/cluster +PtrsPerBlock2 resd 1 ; (Pointers/cluster)^2 +DriveNumber resb 1 ; BIOS drive number +ClustShift resb 1 ; Shift count for sectors/cluster +ClustByteShift resb 1 ; Shift count for bytes/cluster + + alignb open_file_t_size +Files resb MAX_OPEN*open_file_t_size + +; +; Common bootstrap code for disk-based derivatives +; +%include "diskstart.inc" + +; +; Load the real (ext2) superblock; 1024 bytes long at offset 1024 +; + mov bx,SuperBlock + mov eax,1024 >> SECTOR_SHIFT + mov bp,ax + call getlinsecsr + +; +; Compute some values... +; + xor edx,edx + inc edx + + ; s_log_block_size = log2(blocksize) - 10 + mov cl,[SuperBlock+s_log_block_size] + add cl,10 + mov [ClustByteShift],cl + mov eax,edx + shl eax,cl + mov [ClustSize],eax + + sub cl,SECTOR_SHIFT + mov [ClustShift],cl + shr eax,SECTOR_SHIFT + mov [SecPerClust],eax + dec eax + mov [ClustMask],eax + + add cl,SECTOR_SHIFT-2 ; 4 bytes/pointer + shl edx,cl + mov [PtrsPerBlock1],edx + shl edx,cl + mov [PtrsPerBlock2],edx + +; +; Common initialization code +; +%include "init.inc" +%include "cpuinit.inc" + +; +; Initialize the metadata cache +; + call initcache + +; +; Now, everything is "up and running"... patch kaboom for more +; verbosity and using the full screen system +; + ; E9 = JMP NEAR + mov dword [kaboom.patch],0e9h+((kaboom2-(kaboom.patch+3)) << 8) + +; +; Now we're all set to start with our *real* business. First load the +; configuration file (if any) and parse it. +; +; In previous versions I avoided using 32-bit registers because of a +; rumour some BIOSes clobbered the upper half of 32-bit registers at +; random. I figure, though, that if there are any of those still left +; they probably won't be trying to install Linux on them... +; +; The code is still ripe with 16-bitisms, though. Not worth the hassle +; to take'm out. In fact, we may want to put them back if we're going +; to boot ELKS at some point. +; + +; +; Load configuration file +; +load_config: + mov si,config_name ; Save config file name + mov di,ConfigName + call strcpy + mov dword [CurrentDirName],CUR_DIR_DWORD ; Write './',0,0 to the CurrentDirName + call build_curdir_str + + mov di,ConfigName + call open + jz no_config_file + +; +; Now we have the config file open. Parse the config file and +; run the user interface. +; +%include "ui.inc" + +; +; getlinsec_ext: same as getlinsec, except load any sector from the zero +; block as all zeros; use to load any data derived +; from an ext2 block pointer, i.e. anything *except the +; superblock.* +; +getonesec_ext: + mov bp,1 + +getlinsec_ext: + cmp eax,[SecPerClust] + jae getlinsecsr ; Nothing fancy + + ; If we get here, at least part of what we want is in the + ; zero block. Zero one sector at a time and loop. + push eax + push cx + xchg di,bx + xor eax,eax + mov cx,SECTOR_SIZE >> 2 + rep stosd + xchg di,bx + pop cx + pop eax + inc eax + dec bp + jnz getlinsec_ext + ret + +; +; allocate_file: Allocate a file structure +; +; If successful: +; ZF set +; BX = file pointer +; In unsuccessful: +; ZF clear +; +allocate_file: + TRACER 'a' + push cx + mov bx,Files + mov cx,MAX_OPEN +.check: cmp dword [bx], byte 0 + je .found + add bx,open_file_t_size ; ZF = 0 + loop .check + ; ZF = 0 if we fell out of the loop +.found: pop cx + ret +; +; open_inode: +; Open a file indicated by an inode number in EAX +; +; NOTE: This file considers finding a zero-length file an +; error. This is so we don't have to deal with that special +; case elsewhere in the program (most loops have the test +; at the end). +; +; If successful: +; ZF clear +; SI = file pointer +; EAX = file length in bytes +; ThisInode = the first 128 bytes of the inode +; If unsuccessful +; ZF set +; +; Assumes CS == DS == ES. +; +open_inode.allocate_failure: + xor eax,eax + pop bx + pop di + ret + +open_inode: + push di + push bx + call allocate_file + jnz .allocate_failure + + push cx + push gs + ; First, get the appropriate inode group and index + dec eax ; There is no inode 0 + xor edx,edx + mov [bx+file_sector],edx + div dword [SuperBlock+s_inodes_per_group] + ; EAX = inode group; EDX = inode within group + push edx + + ; Now, we need the block group descriptor. + ; To get that, we first need the relevant descriptor block. + + shl eax, ext2_group_desc_lg2size ; Get byte offset in desc table + xor edx,edx + div dword [ClustSize] + ; eax = block #, edx = offset in block + add eax,dword [SuperBlock+s_first_data_block] + inc eax ; s_first_data_block+1 + mov cl,[ClustShift] + shl eax,cl + push edx + shr edx,SECTOR_SHIFT + add eax,edx + pop edx + and dx,SECTOR_SIZE-1 + call getcachesector ; Get the group descriptor + add si,dx + mov esi,[gs:si+bg_inode_table] ; Get inode table block # + pop eax ; Get inode within group + movzx edx, word [SuperBlock+s_inode_size] + mul edx + ; edx:eax = byte offset in inode table + div dword [ClustSize] + ; eax = block # versus inode table, edx = offset in block + add eax,esi + shl eax,cl ; Turn into sector + push dx + shr edx,SECTOR_SHIFT + add eax,edx + mov [bx+file_in_sec],eax + pop dx + and dx,SECTOR_SIZE-1 + mov [bx+file_in_off],dx + + call getcachesector + add si,dx + mov cx,EXT2_GOOD_OLD_INODE_SIZE >> 2 + mov di,ThisInode + gs rep movsd + + mov ax,[ThisInode+i_mode] + mov [bx+file_mode],ax + mov eax,[ThisInode+i_size] + mov [bx+file_bytesleft],eax + mov si,bx + and eax,eax ; ZF clear unless zero-length file + pop gs + pop cx + pop bx + pop di + ret + + section .bss + alignb 4 +ThisInode resb EXT2_GOOD_OLD_INODE_SIZE ; The most recently opened inode + + section .text +; +; close_file: +; Deallocates a file structure (pointer in SI) +; Assumes CS == DS. +; +close_file: + and si,si + jz .closed + mov dword [si],0 ; First dword == file_bytesleft + xor si,si +.closed: ret + +; +; searchdir: +; Search the root directory for a pre-mangled filename in DS:DI. +; +; NOTE: This file considers finding a zero-length file an +; error. This is so we don't have to deal with that special +; case elsewhere in the program (most loops have the test +; at the end). +; +; If successful: +; ZF clear +; SI = file pointer +; DX:AX = EAX = file length in bytes +; If unsuccessful +; ZF set +; +; Assumes CS == DS == ES; *** IS THIS CORRECT ***? +; +searchdir: + push bx + push cx + push bp + mov byte [SymlinkCtr],MAX_SYMLINKS + + mov eax,[CurrentDir] +.begin_path: +.leadingslash: + cmp byte [di],'/' ; Absolute filename? + jne .gotdir + mov eax,EXT2_ROOT_INO + inc di ; Skip slash + jmp .leadingslash +.gotdir: + + ; At this point, EAX contains the directory inode, + ; and DS:DI contains a pathname tail. +.open: + push eax ; Save directory inode + + call open_inode + jz .missing ; If error, done + + mov cx,[si+file_mode] + shr cx,S_IFSHIFT ; Get file type + + cmp cx,T_IFDIR + je .directory + + add sp,4 ; Drop directory inode + + cmp cx,T_IFREG + je .file + cmp cx,T_IFLNK + je .symlink + + ; Otherwise, something bad... +.err: + call close_file +.err_noclose: + xor eax,eax + xor si,si + cwd ; DX <- 0 + +.done: + and eax,eax ; Set/clear ZF + pop bp + pop cx + pop bx + ret + +.missing: + add sp,4 ; Drop directory inode + jmp .done + + ; + ; It's a file. + ; +.file: + cmp byte [di],0 ; End of path? + je .done ; If so, done + jmp .err ; Otherwise, error + + ; + ; It's a directory. + ; +.directory: + pop dword [ThisDir] ; Remember what directory we're searching + + cmp byte [di],0 ; More path? + je .err ; If not, bad + +.skipslash: ; Skip redundant slashes + cmp byte [di],'/' + jne .readdir + inc di + jmp .skipslash + +.readdir: + mov cx,[SecPerClust] + push cx + shl cx,SECTOR_SHIFT + mov bx,trackbuf + add cx,bx + mov [EndBlock],cx + pop cx + push bx + call getfssec + pop bx + pushf ; Save EOF flag + push si ; Save filesystem pointer +.getent: + cmp bx,[EndBlock] + jae .endblock + + push di + cmp dword [bx+d_inode],0 ; Zero inode = void entry + je .nope + + movzx cx,byte [bx+d_name_len] + lea si,[bx+d_name] + repe cmpsb + je .maybe +.nope: + pop di + add bx,[bx+d_rec_len] + jmp .getent + +.endblock: + pop si + popf + jnc .readdir ; There is more + jmp .err ; Otherwise badness... + +.maybe: + mov eax,[bx+d_inode] + + ; Does this match the end of the requested filename? + cmp byte [di],0 + je .finish + cmp byte [di],'/' + jne .nope + + ; We found something; now we need to open the file +.finish: + pop bx ; Adjust stack (di) + pop si + call close_file ; Close directory + pop bx ; Adjust stack (flags) + jmp .open + + ; + ; It's a symlink. We have to determine if it's a fast symlink + ; (data stored in the inode) or not (data stored as a regular + ; file.) Either which way, we start from the directory + ; which we just visited if relative, or from the root directory + ; if absolute, and append any remaining part of the path. + ; +.symlink: + dec byte [SymlinkCtr] + jz .err ; Too many symlink references + + cmp eax,SYMLINK_SECTORS*SECTOR_SIZE + jae .err ; Symlink too long + + ; Computation for fast symlink, as defined by ext2/3 spec + xor ecx,ecx + cmp [ThisInode+i_file_acl],ecx + setne cl ; ECX <- i_file_acl ? 1 : 0 + cmp [ThisInode+i_blocks],ecx + jne .slow_symlink + + ; It's a fast symlink +.fast_symlink: + call close_file ; We've got all we need + mov si,ThisInode+i_block + + push di + mov di,SymlinkTmpBuf + mov ecx,eax + rep movsb + pop si + +.symlink_finish: + cmp byte [si],0 + je .no_slash + mov al,'/' + stosb +.no_slash: + mov bp,SymlinkTmpBufEnd + call strecpy + jc .err_noclose ; Buffer overflow + + ; Now copy it to the "real" buffer; we need to have + ; two buffers so we avoid overwriting the tail on the + ; next copy + mov si,SymlinkTmpBuf + mov di,SymlinkBuf + push di + call strcpy + pop di + mov eax,[ThisDir] ; Resume searching previous directory + jmp .begin_path + +.slow_symlink: + mov bx,SymlinkTmpBuf + mov cx,SYMLINK_SECTORS + call getfssec + ; The EOF closed the file + + mov si,di ; SI = filename tail + mov di,SymlinkTmpBuf + add di,ax ; AX = file length + jmp .symlink_finish + + + section .bss + alignb 4 +SymlinkBuf resb SYMLINK_SECTORS*SECTOR_SIZE+64 +SymlinkTmpBuf equ trackbuf +SymlinkTmpBufEnd equ trackbuf+SYMLINK_SECTORS*SECTOR_SIZE+64 +ThisDir resd 1 +EndBlock resw 1 +SymlinkCtr resb 1 + + section .text +; +; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed +; to by ES:DI; ends on encountering any whitespace. +; DI is preserved. +; +; This verifies that a filename is < FILENAME_MAX characters, +; doesn't contain whitespace, zero-pads the output buffer, +; and removes redundant slashes, +; so "repe cmpsb" can do a compare, and the +; path-searching routine gets a bit of an easier job. +; +; FIX: we may want to support \-escapes here (and this would +; be the place.) +; +mangle_name: + push di + push bx + xor ax,ax + mov cx,FILENAME_MAX-1 + mov bx,di + +.mn_loop: + lodsb + cmp al,' ' ; If control or space, end + jna .mn_end + cmp al,ah ; Repeated slash? + je .mn_skip + xor ah,ah + cmp al,'/' + jne .mn_ok + mov ah,al +.mn_ok stosb +.mn_skip: loop .mn_loop +.mn_end: + cmp bx,di ; At the beginning of the buffer? + jbe .mn_zero + cmp byte [di-1],'/' ; Terminal slash? + jne .mn_zero +.mn_kill: dec di ; If so, remove it + inc cx + jmp short .mn_end +.mn_zero: + inc cx ; At least one null byte + xor ax,ax ; Zero-fill name + rep stosb + pop bx + pop di + ret ; Done + +; +; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled +; filename to the conventional representation. This is needed +; for the BOOT_IMAGE= parameter for the kernel. +; +; DS:SI -> input mangled file name +; ES:DI -> output buffer +; +; On return, DI points to the first byte after the output name, +; which is set to a null byte. +; +unmangle_name: call strcpy + dec di ; Point to final null byte + ret + +; +; +; kaboom2: once everything is loaded, replace the part of kaboom +; starting with "kaboom.patch" with this part + +kaboom2: + mov si,err_bootfailed + call writestr + cmp byte [kaboom.again+1],18h ; INT 18h version? + je .int18 + call getchar + call vgaclearmode + int 19h ; And try once more to boot... +.norge: jmp short .norge ; If int 19h returned; this is the end +.int18: + call vgaclearmode + int 18h +.noreg: jmp short .noreg ; Nynorsk + + +; +; linsector: Convert a linear sector index in a file to a linear sector number +; EAX -> linear sector number +; DS:SI -> open_file_t +; +; Returns next sector number in EAX; CF on EOF (not an error!) +; +linsector: + push gs + push ebx + push esi + push edi + push ecx + push edx + push ebp + + push eax ; Save sector index + mov cl,[ClustShift] + shr eax,cl ; Convert to block number + push eax + mov eax,[si+file_in_sec] + mov bx,si + call getcachesector ; Get inode + add si,[bx+file_in_off] ; Get *our* inode + pop eax + lea ebx,[i_block+4*eax] + cmp eax,EXT2_NDIR_BLOCKS + jb .direct + mov ebx,i_block+4*EXT2_IND_BLOCK + sub eax,EXT2_NDIR_BLOCKS + mov ebp,[PtrsPerBlock1] + cmp eax,ebp + jb .ind1 + mov ebx,i_block+4*EXT2_DIND_BLOCK + sub eax,ebp + mov ebp,[PtrsPerBlock2] + cmp eax,ebp + jb .ind2 + mov ebx,i_block+4*EXT2_TIND_BLOCK + sub eax,ebp + +.ind3: + ; Triple indirect; eax contains the block no + ; with respect to the start of the tind area; + ; ebx contains the pointer to the tind block. + xor edx,edx + div dword [PtrsPerBlock2] + ; EAX = which dind block, EDX = pointer within dind block + push ax + shr eax,SECTOR_SHIFT-2 + mov ebp,[gs:si+bx] + shl ebp,cl + add eax,ebp + call getcachesector + pop bx + and bx,(SECTOR_SIZE >> 2)-1 + shl bx,2 + mov eax,edx ; The ind2 code wants the remainder... + +.ind2: + ; Double indirect; eax contains the block no + ; with respect to the start of the dind area; + ; ebx contains the pointer to the dind block. + xor edx,edx + div dword [PtrsPerBlock1] + ; EAX = which ind block, EDX = pointer within ind block + push ax + shr eax,SECTOR_SHIFT-2 + mov ebp,[gs:si+bx] + shl ebp,cl + add eax,ebp + call getcachesector + pop bx + and bx,(SECTOR_SIZE >> 2)-1 + shl bx,2 + mov eax,edx ; The int1 code wants the remainder... + +.ind1: + ; Single indirect; eax contains the block no + ; with respect to the start of the ind area; + ; ebx contains the pointer to the ind block. + push ax + shr eax,SECTOR_SHIFT-2 + mov ebp,[gs:si+bx] + shl ebp,cl + add eax,ebp + call getcachesector + pop bx + and bx,(SECTOR_SIZE >> 2)-1 + shl bx,2 + +.direct: + mov ebx,[gs:bx+si] ; Get the pointer + + pop eax ; Get the sector index again + shl ebx,cl ; Convert block number to sector + and eax,[ClustMask] ; Add offset within block + add eax,ebx + + pop ebp + pop edx + pop ecx + pop edi + pop esi + pop ebx + pop gs + ret + +; +; getfssec: Get multiple sectors from a file +; +; Same as above, except SI is a pointer to a open_file_t +; +; ES:BX -> Buffer +; DS:SI -> Pointer to open_file_t +; CX -> Sector count (0FFFFh = until end of file) +; Must not exceed the ES segment +; Returns CF=1 on EOF (not necessarily error) +; On return ECX = number of bytes read +; All arguments are advanced to reflect data read. +; +getfssec: + push ebp + push eax + push edx + push edi + + movzx ecx,cx + push ecx ; Sectors requested read + mov eax,[si+file_bytesleft] + add eax,SECTOR_SIZE-1 + shr eax,SECTOR_SHIFT + cmp ecx,eax ; Number of sectors left + jbe .lenok + mov cx,ax +.lenok: +.getfragment: + mov eax,[si+file_sector] ; Current start index + mov edi,eax + call linsector + push eax ; Fragment start sector + mov edx,eax + xor ebp,ebp ; Fragment sector count +.getseccnt: + inc bp + dec cx + jz .do_read + xor eax,eax + mov ax,es + shl ax,4 + add ax,bx ; Now DI = how far into 64K block we are + not ax ; Bytes left in 64K block + inc eax + shr eax,SECTOR_SHIFT ; Sectors left in 64K block + cmp bp,ax + jnb .do_read ; Unless there is at least 1 more sector room... + inc edi ; Sector index + inc edx ; Linearly next sector + mov eax,edi + call linsector + ; jc .do_read + cmp edx,eax + je .getseccnt +.do_read: + pop eax ; Linear start sector + pushad + call getlinsec_ext + popad + push bp + shl bp,9 + add bx,bp ; Adjust buffer pointer + pop bp + add [si+file_sector],ebp ; Next sector index + jcxz .done + jnz .getfragment + ; Fall through +.done: + pop ecx ; Sectors requested read + shl ecx,SECTOR_SHIFT + sub [si+file_bytesleft],ecx + jnbe .noteof ; CF=0 in this case + add ecx,[si+file_bytesleft] ; Actual number of bytes read + call close_file + stc ; We hit EOF +.noteof: + pop edi + pop edx + pop eax + pop ebp + ret + +build_curdir_str: + ret + +; ----------------------------------------------------------------------------- +; Common modules +; ----------------------------------------------------------------------------- + +%include "getc.inc" ; getc et al +%include "conio.inc" ; Console I/O +%include "plaincon.inc" ; writechr +%include "writestr.inc" ; String output +%include "writehex.inc" ; Hexadecimal output +%include "configinit.inc" ; Initialize configuration +%include "parseconfig.inc" ; High-level config file handling +%include "parsecmd.inc" ; Low-level config file handling +%include "bcopy32.inc" ; 32-bit bcopy +%include "loadhigh.inc" ; Load a file into high memory +%include "font.inc" ; VGA font stuff +%include "graphics.inc" ; VGA graphics +%include "highmem.inc" ; High memory sizing +%include "strcpy.inc" ; strcpy() +%include "strecpy.inc" ; strcpy with end pointer check +%include "cache.inc" ; Metadata disk cache +%include "idle.inc" ; Idle handling +%include "adv.inc" ; Auxillary Data Vector +%include "localboot.inc" ; Disk-based local boot + +; ----------------------------------------------------------------------------- +; Begin data section +; ----------------------------------------------------------------------------- + + section .data +copyright_str db ' Copyright (C) 1994-' + asciidec YEAR + db ' H. Peter Anvin et al', CR, LF, 0 +err_bootfailed db CR, LF, 'Boot failed: please change disks and press ' + db 'a key to continue.', CR, LF, 0 +config_name db 'extlinux.conf',0 ; Unmangled form + +; +; Config file keyword table +; +%include "keywords.inc" + +; +; Extensions to search for (in *forward* order). +; + alignz 4 +exten_table: db '.cbt' ; COMBOOT (specific) + db '.img' ; Disk image + db '.bs', 0 ; Boot sector + db '.com' ; COMBOOT (same as DOS) + db '.c32' ; COM32 +exten_table_end: + dd 0, 0 ; Need 8 null bytes here + +; +; Misc initialized (data) variables +; +%ifdef debug ; This code for debugging only +debug_magic dw 0D00Dh ; Debug code sentinel +%endif + + alignz 4 +BufSafe dw trackbufsize/SECTOR_SIZE ; Clusters we can load into trackbuf +BufSafeBytes dw trackbufsize ; = how many bytes? +%ifndef DEPEND +%if ( trackbufsize % SECTOR_SIZE ) != 0 +%error trackbufsize must be a multiple of SECTOR_SIZE +%endif +%endif diff --git a/core/extlinux.bin b/core/extlinux.bin new file mode 100755 index 0000000000000000000000000000000000000000..cac1cd83bdaf779181271b31c92b06071a80d591 GIT binary patch literal 13414 zcmd^mdt4Mp)@b!G^bCkI;|tLkY!hT5i#TJl6U7018^1)w7p(#a$518W^&~a9s#y7;FViBobp16B9$gH8r+x1bI1qPxa`2+3$XT-v93S zqfb3fovJ!@>eQ)oN-6b?g{jXyvt-$-RP_J$H#ltalyw}f-^`|zc^b8-xqfrc!LsvB z8LNqt4KBh(PXo!vO@BUVTh7OQ%)50>h}wm6pdw~vv2)3@;EYc?%*F6_ruleW}r z+s9bPaF=ZQmMhwMLg>UTB|j$$fIZYyy{dk0DNq6gL-Ek6YFlt$3&RJtvZ_}RuIzuc z32h|?go_g*wvOCe&g*xG$eW=0ltDOvowNm;&PvNyk*^Wf|KnaG;600z*p(?S8-&X^ zladV&om@Y!eavK>wL9BjHN1eC{^)xxzXUNuQ?A)sMVx8!_hr z=CCdBO_;a^-|aJ<+W8s2dzZX|qQ98cyFn%NdM>eZwLd#fIKH6--l|u93f*V6kHJ~i z@mPQ4WJ_nzYDRnkr{d=^b27T!_BU~+DA4{^eB{ z=i!6ubxj)7Zjf%j+aB6JLGsN8+zo0ce8qVM1w5BywH8=M1}n>a!(DsvHS`TLGWfyr zbC1W)oWad78fQ&2nx`3OacE@lMC4N)PN0l?ARDR&iT{t`m07PLG~@=!dI`C|e9fK! zvStf#fBTwXt}v42$o<6DCS-T&9^2YI#dHnVV&T}rC42~;hi8qo1(vj`rJKZpTK*Zt zNYal$#;J?iia$}^TiOD+%(1QkJmoAu-+k;g5`es8d^q#pR~KiAxEyB^%RQWh_levy z1zFktwdJO9X#Tt<%U7a>%Ga&=ka%Ql^0-HAY37U|;I+$ejqwt$>FR4jQMiGwrf`D% z9dOR#d9eSY>@C3#-RKxBbl|T#aTSWOdxIRhQ8BoM1mUVDjQA;BrH-+;Xz*8ESP0d| z_+dy4$(Ta^Jh7RJX+#Y4*n?~8Rqdhs8;pn4HbeX9_zNSi*?oA&y?-?Z0qfoMED0VtRm# zsn!58BGUVJrjKnfuC|RXN$5;O`t3WIl7y>?jDF`9NOVETjzY%0x0-x7p_OekBA`$| zsy&ov>kaLp(j8oRE)iGN8F6I?K$npmP~nnDml1Iu{q_sU@)|$a!dV|KVvA*Tg#Wn@ zNHF>x<)~PeKP^04O{PqcPFqImw?E8Sg7}#hmY-%B$3JWd)o(wCEMfcvi`M2+n!dl> zy}z17PmuPJ@e{n+GC45Cn=aEKfe4f&TuMZ?ODV!Yu@@a7ePP&XK4iwT%^S@dVpia0 zbt|ysKPN?>NlPvnKzvBb3O6t!^8K_7TW}pm%EJU7`64V`Y9~!$(t6`1M`WYXVzj== zj}GB1vo;tkS=ODFmx@YaQlyBwV2O({Nt6C~+_IoZ8#7zK?G;(lS$62Ry(C-ysNea5 z9J4v4rBfTI^}b_|NBj(uG@74AUJN6P#!6#T6qyfmQZfI17?rN%Fv{Jeu#A+}Km@}g z3&S$J|M0ohZeVpIQuW}2QV4l8OyPpp=&1&r=8)cGGS!govT#L1ptk}cvElAA!FW@a{RAVl^{2CW=y6Rm>UV2TI(6!kJqLt@ zi-{WOCB?0718M9zkYTz|!JgdziP0igs@wG2r3fjqB;j%*;xpOglxFmJS({ti0VJHd z^NIfU8R;5_?h|7x8`6h>ls%r?@PyIRID~G0Xk(TO3H8P%RJRgp$nSKk^*atD?9@2T z^+qhHTNkKNiKs3f8e6yFN2l(G#3`q26Wt-b%^`g$BvbG1s3w!gr;~@qyWKADk0EYt zC-5pcWyOk=p7%3W^IzY+u={mc)9m}Sbs>Xxo9^`-7*5Mb?>XQ-=5t@K_SS@uKTjsl zO_9xO)WkN{7L_`iU8Cl2Vp-=h@)`hjMq5`J**w{;Jr9(fy6<65-c7zVVjoL#CW|0l zAn^x`^zf?cH16*<`Ugjj>xyrL^a%;tUR=4+jH`|saaKwa%n>Gz7-izvxpX)+Lw_XU z6C)$b2glan;!}v@I9xms`%=ML{q_ZFCHW$+-#*Kqe0+v}`&un6eX+2Rwi+rgkiJ{)xL+snMRisjuK`g$3`h|q^Mo@zuqb$o$MuOF%QazPzU8&zO#6(uIs;Y~| zc%;&AyT-&bD(gd$ZES?p*K0k)9GJ)?$9SEV=;fXFXF(03#1j~;08 zf)c37z?B{&p3NRL7O54%GI?gPokmdbGa1qpCr<=fTK4O{<#F8*o4w5kD>)SB9ye zuCfElAu|awb8+75I}uvp{f%~T*Lkp+@7yi(ly#PoKn^9nj6T25E7l!fw# zbTq|zcr?|1KLJx=C+N59MvLU4)-AY*dbD>ze=@0KNj_?X`L&p2*yVnU0we;VcCLMN zOD7vyrGf*ezwuSnT#R5>M^Ymxpt|dB-N&E$VU5AaJzV38Tl8>k=Rm*Q zA^cPSr$sS1OaFnsQZxCCbI~L$Z-{z%%JVp*G0&B^Ly)*Xktw6#BtL8T6PZdArwmx= zC%+j*t{A*e-ZhVfgrPdo$PJNh&K1HYoj$4^C8P=RkU7vy&)y1Zw610r-g6+vIm20%wopk2!h zyV4`t7OBzJwna$uA&fsg!nwgf&g&l};hZBMOvqcm&D-J2OWx-F%tz*i^^4&OqMAx+ zy=F2gW%>PBSTu=FLnUlbQ$|EM(+y6zB`pgh=d^_?Qy-LLVOf|6h2&|rUu<18>50}w zVL=s!7{{WqyOuoy1F74*YZ(m4P`|lftUT@#7LBcJg>P8pNtd>>H1ac-=c>AoJ)Owd z0_>;l!Xn^5u5@k2RT~i*83>DTQ^w9k2ux|X<1PxrRaz}*jV+*btB@8aVdxt>lMT-F zNbe}MzpFii{i2eB6;PG~sH~pQFFF^E^*%JokCW?ynK7`wu;>;XfKjF`3-g*sC*veiprYEbQ#ljB2D+FyH~JRncQR%ejxz)3=hSr5 z*`~Q~kga2I)APF#8l*~2h}<*0EA;K*VKRCQ%;)vdt?a%J1U(b_#s_*P{D4c(c|sWd zBc3ak-5dh{1}JGg>ZpqXeGO`*POC@fom0z)5t6%eosnQfnJy`ZX9Lo5s0m#+Ak!(| zi4`@0TDW<=S@ULBZ(2iU9r89k_s5%ZP1JJVn>Evvs;=Ae?+%#}%DYttp91=!r;Gnn zq-2jE^s;7%#BI|f+%%$Wa9B`!HeI@M0j6U>4PnSXEM=5Tcr_h;gpZDX5D@%2U8y?^ zvzEJuyr17L*X4~b!_R(>o7O@f-YY)8)}j~9SRt}v`14pNeChS`zUpggqj<-B{)$*! z_3kikn*TbKedyC4@t7_+$CltGeVO+Kuo$w=lxI2@L_|rt@=W~^NI2CyfDdRm7yKl9 z=r{cB;gTEXTsX@147YTvnV+0|M2zXwMq9Kw$3eK_Y9k}_KD@0!_H?SjKA)Yc2_YReWv6W z=v@2j&S8i(4NH`6VI0CqYRnuIT1tKn=sAF!o+|V1^uerhE8iX7C$ft9LXA$MLyCbJ zHV!Tda1{|nzXb)|sI`8f3mS#N-lJv$nA`up_u(dP>U*FqsBg$!zq$FkCIwt*aIHR= zZo2R?L$b$^6IA}-RBk@EIi)ca_0(iUVqw)J_}D@sg%{Y$y3Nk;!-R#Z8BwS*5EvZ+yedm{*SVU$w@U#{{2TsJ zf>ooOT~mYQZvUM~zeC;QO}j~NA*Qp`@8$hCi2CJbvricHC6jL8q!l}ds1<34QPu1m zN&{EeoTav(#1&28WB`+(1nzHvC87cLkO+9IGsKjYI9mNKxav=BOxe z$+dI_(vrJo6az-t*eE=wlzKJ5#{qR$f-uv8pD*r`8M1E-^GyzR@>=Ll|BukrD15ie z-;B8l=>NhN7HgFTLP`k9hc<8)H)orAa`3^*Y?H89hb!|;SkMR;@f45#gESZ&7|%ht zO-%*f)F-Z7v8t7Q%sdBI8n@cxr`v-3-k8PGZBSmnlljM-&*)mbAteK9{^P8CyXmvehz}A zXTB43QRcLjpVUk&4f;nX9K8>mN=#2DJcBp=AfN}P;S@CsP{RXE8n3WOfK3$#KHd2dueQ3y`W z#k(i%h=S$9@geZlZ+jCIZ7R)Aqj~I%1^3r1V$H@)zo+?V0!1N8(KOtYdA~@Jx{#P8 z@Stfxu%hr+|HKR-zZr2Dm&ZWVR4DLC5y3un}?aQ=JyUc>$NKr;+v^{a0DD65Xa z%z69*xQx?q22RJT^&g0If-ub#kmge`VW%1g$_$K_I`sV49+)+lJZ|M(hpx5`!v z{e#Hi=#U}}4hSIu_Xc1*&ja=gq6j%Ye5Et|sH;Q*pMCwXbrf8RUBZ(l55xf+i`hA= zMe-#?4_1pSlT%n{!cmw0$P4V03$};caHO}EtW`1A2~L|jvQ)iDu&Mhs8R@IVr%UH* z951laEu1L~6-)JSd}u-KIe#~$Pq3-@QJ_9ak55>t5`NdM##vG$&N6xiBr%D+CtKm( z(4Pvu(Lp(%B+m_lTA^bCx+yCk48Sp0Gaq`OB8Fw_NXtY%$Px^H+=d(COwWL*mc`}+ z!^QH)!-e`3GGETHYO5{N3a^tUnL5V~3sInw z%dDcM9|cR}U67p$zPpW3^kh-oM_Nf;XZ*-O<{+zX=E*|1sQ>*t%!n@O+AfTA=A{wR9ckISS?*!kx8s)p7HrtNju)P!72OxOwe^ z3*zLRcgbXV=iSL7c#5b!`8S_+6iM_IhR|KEC7`e2Xd&f3aDB`$Yg*aI%^LDNLmu{7 z#{gly{)o;~YyX*pGFNhXZUf~lneaB zNu`meT)R^OuO8y~{&`oq&Y7LIn>)Wsy?)t#*{(zw{EdR?1v`+4veYM%pF?ma^`zS# zbA|$$v-~5K9j?LI>=!QE_>yBli{bV2=tW4Or+*7>ltU1=7$S#4lx7R=_kQDF%_b>B zVE7;YtC_9CcuDyF>W~yQ^d|T^9w|Fts+l~$xLei>;TSX z%?U^#5YhjJX!0nC?wMwPKhU$EIwUj@78S{-GR_3s7bXU(4K4s*xEhh#B!kVSq1A8R z!{xDktfy8B&c0eLB;i`j>p^^UA8TT%l(ribDNCvwNjl`ZVDh<~ssy z$(Mu0H-gTCr!Wz+o#Yr~wVgu34~gIqxt@rM^v&81;-aM;;L)W>C&@mVJy{~FY(e$} z?a662l}U#Cz)6vu8c9@$bR-D`H*Q=w~@X+IH)QH(ye;jQ1e(Q z7_!}%`3eh@pjd!nm6F=VG>7z)&-zGZuZ>BCsy`?NjeVrFpBBjMl|+%G4d}O>Lo4Y) zRCUrtOaoRE2^%2u2CUI|C3$4PswF`KJ>sSie$s_k zODt}(PemvLmkd3v-?kH^4;=&3{XKlQr>fPJR3Zlyv0wh;{1mB$gySyq=X<+X1=N)& zl9Vb8DsY~oQ-O5>^X4gutCj#;!hhTs%&Qhpq2|8q&6`(o(xsprqB3-6*q*@DB7RgW z3xGzXa)xLqDq{!~`EeIQFz-z$gNVH%c?&@RX8=J6tx**S_F)alP%<*2_9Q*^TiNT0 zj9cAGnf#xO9t2(%+ni!bEA+qnTthNPs;7hRBa{ zAFgTCD4~i064wjos?a}8d$LM>vJq*cTc~l1VVu6(3@voP51-3s}hYU!fg+v#WrH0efK z^uXQkL{f-ILm$7!I$(K}zYRRA4&mU{!+8?YXH9{n8A0 zO4jwc9UnGMN5J@5A|HusyL#QKTG9^jcr9rgBL5hY_PZaeB}bKlBSYlzMLoYjD7e@UoihIc9 z0f&+P9}9!!jw>a9{Wm|&zuYtLK{?Po4D|2&U-p4fpL$FMFGOG<4zmpWv#pDjBhzbR z+t~ElmcKSI5#{aU_L0@dmJH$Y9muv^sX6!8x-Tzh1A5(0mov#4R3}3&2d#h@x)IqP z>l#Q?&{P$d{uH8$@Lm2BtqDao1FiAt9A?{J+?lmr^UX9+=Ijcb)F{hkEf6NyZiXAL zq{o4CuLRErwZGj=?S)9VzV9*r?8T{R8qs)`oR)btn28f<-KA`>3baZpQ~Kv2mF;Jm z7THE*C~FbIOU-aWeo+oBH3R?Ssk3VX3wi}0NZ^{O$gB1WtH{3UFM3u0!-mMa<<2Mik$BONpmE)YGL49Sqr%L zgzuCf*Fi9AKdglZ37*lvOiopKCR9=X?d%W>?f z{U&q6lu_-5%KCdPB;cw2Mze<6(WbKv1}N1SFEj)I%&ge11~|%Zo>|=@(?}r#KrfF8Eeb`pJ6Y0+QqjG_!-(R6C9>vGs_sSP@7o^12nmcaDyp>a^(Z zihh&`xJ?}*2}WQUYel$iNu7PI2A;KF);n&BguU%(^;}omzc57T#ZQh0)Tyq|t79Bu z=5;vdcYf|VOFF*Z`FU5bqrbZ3nzralmQU>Hv=6B_Tol7K6`ooP!_PUS=7Sql%bP~i zCsFzaG?C8DE@|vTOwrT^TKdV~Ya3LCc0(&&5!}Ys-?OVa84~YnWxq%S?;6}|aHii) z3N*pS3&@U8#Ix1>l#*6$S0AfCqycxQS#i0+_21mm4U0!=C0G>a@72yNEF}W|fbcrA z@O!mT#0us*bK3&eWiGxB*q7N1IMR}0E7z!FtLo!Yhs2}@VbAi^zT+y})Z`WJ6SX9# zSGZi#8VozhTuV0gF5xv0uba>--69FSY3W1I{I_g9Uoc!SDx3KlLog}Y`;Tk&t`kNy|>1lR5@z7(dlarootTUJfa0msyI>DzRPS(YZ; z?Hbn3boz?ncC=0v??_s8p>Yjnr%Eoxv1 zL}Atb8N$v2Iqn3j@F9Sg0FLodH?+= zscj$Qe)(@!o$`cKOP`Pixa0nYD?4kOfc0QzRbGNrJ=h1dE7RFz`EnzB|GDVP%!Bu$ zq_zRg2?oOOZ1i7`Mpfxz*Ea*{BKY8vR+Wa5TJG$ohm;qsDEgumjEzBf(F)*} zG&&Ak{DemqKQ>zGpL^HUg)|NW>&c{Q32??tZIchA?) z4^kHn+TwrBRrcjkwB-s7RknwDi@ruHpG_+9$^6DyK5MKn_~n&C)qy3BlO+T2+5$3x z_+W`wm`Vuj4GbOd~cO0U&R9G}x($1AnV6oz&`r`MNs z-6ZoMY$SZ^o^uGHM(V5G4VNN>dJf8+yZ0k_7+HKQ3EZT@vno9^+Bw@BzTnX4G|048bVcTeE3Sc6D7GiL0gYY>p3R+a@XL$?#PSynXl+2+Yy- zfQ2YKdj-ltH{N$!yfGcU@c}K#MfN}>KpYV?DB61;zWE@3#$W#C8MK7wvevK9$+8x5 zn+j}J4jy5xxA8evE-$N)Ta!~*$nkku>$n-_nNM>Cm|Ig>-_VEM{NqDKZlJ9lX; z$32t3&bEqOWcs>lMOEB$(%gy0O1}hkAp@Xd4 z94>#I{{dD9z44pY=O}ph$B><$ zrIby9M)M17Fo1RN+8O6VAFt$C*X6A7w*y)ugC9_WUS2A&T5aq3f5V~uqB+}-Y{#$H zDt~V~zTWlT_L0H#ku8jzL$R^Qnv<2iiDJ&rT1R_YLm%K~an`K0+4+U9z^wQ@m>@?v z@$3F+i;=35|UOXbpu&NoXRan5t*1FUk^!k7L#>TGegIlpllcRjaj%`RF}y6e>Y@9z3o zNxxb8=`L{>NH7xzvc>1DUC-xab0B_P;if{!axZ0p1T0zh9K2gzzLd`W!4ek~aBH%x zxjD*$K+w_Pmw3cwy$BEOaCBiFIcA_!pbboFI1WXnXI)rbc_9u zn6cc`enQX$wfuEt@Z1?jE;ozMQH0W(Q@Flh9hD?1tRsV$f%xRCE3oC}Js2hIHbtHa z*XO*HkMn_DifGXJ!W$gKr~=f1tVSCYJR)SIP7hB24kEPh*=LtO8^`5mugURC-;zb~ zMriP>pDfSw?>!D%3)VsxzOmI1iuBO|nXd+(S(GAICG&KU+`m~Zbb*Zyr;Ai!N8b%wD3pPj+78i0%6 zYV~LVo>t#t5&E&5j|=^!6X>UT;BY{b_dRncuM?r$d+(Xfhn4MP6E`XQKaSvYtct<$#y;$3__ZEn<)_bn} zHicKtmG@96oh!dX;nj2Hy%b(MSKdeA^>gKSDZFv6yr06G=gJ2tymhYJ@D~PYZ=Wl_ zN%H_l(R|Oj^3fFD0eTm2Lg;qe-q@F3?`Nn{{cg}eI5V+ literal 0 HcmV?d00001 diff --git a/core/extlinux.bss b/core/extlinux.bss new file mode 100644 index 0000000000000000000000000000000000000000..dc18d44bb41194ba3b7cbfa201cd8dfc35d7ff50 GIT binary patch literal 512 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 T*2_BZF{Ma(>_&}!<$U|U3v9q| literal 0 HcmV?d00001 diff --git a/core/font.inc b/core/font.inc new file mode 100644 index 0000000..4090e5a --- /dev/null +++ b/core/font.inc @@ -0,0 +1,152 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; font.inc +;; +;; VGA font handling code +;; + + section .text + +; +; loadfont: Load a .psf font file and install it onto the VGA console +; (if we're not on a VGA screen then ignore.) +; The font is on top of the getc stack. +; +loadfont.err: jmp close ; Tailcall the close routine + +loadfont: + mov di,trackbuf + mov cx,4 + call readc ; Read header + jc .err + + mov ax,[trackbuf] ; Magic number + cmp ax,0436h + jne .err + + mov al,[trackbuf+2] ; File mode + cmp al,5 ; Font modes 0-5 supported + ja .err + + xor bx,bx + mov bh,[trackbuf+3] ; Height of font + cmp bh,2 ; VGA minimum + jb .err + cmp bh,32 ; VGA maximum + ja .err + + ; Load the actual font + mov di,trackbuf + mov cx,bx ; Bytes = font height * 256 + call readc + jc .err + + call close + + ; Copy to font buffer + mov si,trackbuf ; Start of font data + mov [VGAFontSize],bh + push es + mov cx,aux_seg + mov es,cx + mov di,aux.fontbuf + mov cx,bx + shr cx,2 + rep movsd + pop es + + mov [UserFont], byte 1 ; Set font flag + +; +; use_font: +; This routine activates whatever font happens to be in the +; vgafontbuf, and updates the adjust_screen data. +; Must be called with CS = DS +; +use_font: + test byte [UsingVGA], ~03h ; Nonstandard mode? + jz .modeok + call vgaclearmode + +.modeok: + test [UserFont], byte 1 ; Are we using a user-specified font? + jz adjust_screen ; If not, just do the normal stuff + + push es + mov bp,aux_seg + mov es,bp + + mov bp,aux.fontbuf ; ES:BP -> font + mov bh,[VGAFontSize] + xor bl,bl ; Needed by both INT 10h calls + + test byte [UsingVGA], 01h ; Are we in graphics mode? + jz .text + +.graphics: + xor cx,cx + mov cl,bh ; CX = bytes/character + mov ax,[GXPixRows] + div cl ; Compute char rows per screen + mov dl,al + dec ax + mov [VidRows],al + mov ax,1121h ; Set user character table + int 10h + mov ax,[GXPixCols] + shr ax,3 ; 8 pixels/character + dec ax + mov [VidCols],al + pop es + ret ; No need to call adjust_screen + +.text: + mov cx,256 + xor dx,dx + mov ax,1110h + int 10h ; Load into VGA RAM + pop es + + xor bl,bl + mov ax,1103h ; Select page 0 + int 10h + +; +; adjust_screen: Set the internal variables associated with the screen size. +; This is a subroutine in case we're loading a custom font. +; +adjust_screen: + pusha + mov al,[BIOS_vidrows] + and al,al + jnz vidrows_ok + mov al,24 ; No vidrows in BIOS, assume 25 + ; (Remember: vidrows == rows-1) +vidrows_ok: mov [VidRows],al + mov ah,0fh + int 10h ; Read video state + dec ah ; Store count-1 (same as rows) + mov [VidCols],ah + popa + ret + + section .data + alignz 2 +VGAFontSize dw 16 ; Defaults to 16 byte font +UserFont db 0 ; Using a user-specified font + + section .bss1 + alignb 4 +GXPixCols resw 1 ; Graphics mode pixel columns +GXPixRows resw 1 ; Graphics mode pixel rows diff --git a/core/genhash.pl b/core/genhash.pl new file mode 100755 index 0000000..c79139f --- /dev/null +++ b/core/genhash.pl @@ -0,0 +1,26 @@ +#!/usr/bin/perl +# +# Generate hash values for keywords +# + +eval { use bytes; }; + +while ( defined($keywd = ) ) { + chomp $keywd; + + ($keywd,$keywdname) = split(/\s+/, $keywd); + $keywdname = $keywd unless ( $keywdname ); + + $l = length($keywd); + $h = 0; + for ( $i = 0 ; $i < $l ; $i++ ) { + $c = ord(substr($keywd,$i,1)) | 0x20; + $h = ((($h << 5)|($h >> 27)) ^ $c) & 0xFFFFFFFF; + } + if ( $seenhash{$h} ) { + printf STDERR "$0: hash collision (0x%08x) %s %s\n", + $h, $keywd, $seenhash{$h}; + } + $seenhash{$h} = $keywd; + printf("%-23s equ 0x%08x\n", "hash_${keywdname}", $h); +} diff --git a/core/getc.inc b/core/getc.inc new file mode 100644 index 0000000..bb12047 --- /dev/null +++ b/core/getc.inc @@ -0,0 +1,415 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved +;; Copyright 2009 Intel Corporation; author: H. Peter Anvin +;; +;; 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; getc.inc +;; +;; Simple file handling library (open, getc, ungetc) +;; +;; WARNING: This interface uses the real_mode_seg/comboot_seg. +;; + +MAX_GETC_LG2 equ 4 ; Max number of file nesting +MAX_GETC equ (1 << MAX_GETC_LG2) +bytes_per_getc_lg2 equ 16-MAX_GETC_LG2 +bytes_per_getc equ (1 << bytes_per_getc_lg2) +secs_per_getc equ bytes_per_getc/SECTOR_SIZE +MAX_UNGET equ 9 ; Max bytes that can be pushed back + + struc getc_file +gc_file resw 1 ; File pointer +gc_bufbytes resw 1 ; Bytes left in buffer +gc_bufdata resw 1 ; Pointer to data in buffer +gc_unget_cnt resb 1 ; Character pushed back count +gc_unget_buf resb MAX_UNGET ; Character pushed back buffer + endstruc +getc_file_lg2 equ 4 ; Size of getc_file as a power of 2 + +%ifndef DEPEND +%if (getc_file_size != (1 << getc_file_lg2)) +%error "getc_file_size != (1 << getc_file_lg2)" +%endif +%endif + +; +; open,getc: Load a file a character at a time for parsing in a manner +; similar to the C library getc routine. +; Up to MAX_GETC files can be open at the same time, +; they are accessed in a stack-like fashion. +; +; All routines assume CS == DS. +; +; open: Input: mangled filename in DS:DI +; Output: ZF set on file not found or zero length +; +; openfd: Input: file handle in SI, file size in EAX +; Output: ZF set on getc stack overflow +; +; getc: Output: CF set on end of file +; Character loaded in AL +; +; close: Output: CF set if nothing open +; +open: + call searchdir + jz openfd.ret +openfd: + push bx + + mov bx,[CurrentGetC] + sub bx,getc_file_size + cmp bx,GetCStack + jb .stack_full ; Excessive nesting + mov [CurrentGetC],bx + + mov [bx+gc_file],si ; File pointer + xor ax,ax + mov [bx+gc_bufbytes],ax ; Buffer empty + mov [bx+gc_unget_cnt],al ; ungetc buffer empty + + inc ax ; ZF <- 0 + pop bx +.ret: ret + +.stack_full: + call close_file + xor ax,ax ; ZF <- 1 + pop bx + ret + +getc: + push bx + push si + push di + push es + + mov di,[CurrentGetC] + movzx bx,byte [di+gc_unget_cnt] + and bx,bx + jnz .have_unget + + mov si,real_mode_seg ; Borrow the real_mode_seg + mov es,si + +.got_data: + sub word [di+gc_bufbytes],1 + jc .get_data ; Was it zero already? + mov si,[di+gc_bufdata] + mov al,[es:si] + inc si + mov [di+gc_bufdata],si +.done: + clc +.ret: + pop es + pop di + pop si + pop bx + ret +.have_unget: + dec bx + mov al,[di+bx+gc_unget_buf] + mov [di+gc_unget_cnt],bl + jmp .done + +.get_data: + pushad + ; Compute start of buffer + mov bx,di + sub bx,GetCStack + shl bx,bytes_per_getc_lg2-getc_file_lg2 + + mov [di+gc_bufdata],bx + mov si,[di+gc_file] + and si,si + mov [di+gc_bufbytes],si ; In case SI == 0 + jz .empty + mov cx,bytes_per_getc >> SECTOR_SHIFT + call getfssec + mov [di+gc_bufbytes],cx + mov [di+gc_file],si + jcxz .empty + popad + TRACER 'd' + jmp .got_data + +.empty: + TRACER 'e' + ; [di+gc_bufbytes] is zero already, thus we will continue + ; to get EOF on any further attempts to read the file. + popad + xor al,al ; Return a predictable zero + stc + jmp .ret + +; +; This is similar to getc, except that we read up to CX bytes and +; store them in ES:DI. Eventually this could get optimized... +; +; On return, CX and DI are adjusted by the number of bytes actually read. +; +readc: + push ax +.loop: + call getc + jc .out + stosb + loop .loop +.out: + pop ax + ret + +; +; close: close the top of the getc stack +; +close: + push bx + push si + mov bx,[CurrentGetC] + mov si,[bx+gc_file] + call close_file + add bx,getc_file_size + mov [CurrentGetC],bx + pop si + pop bx + ret + +; +; ungetc: Push a character (in AL) back into the getc buffer +; Note: if more than MAX_UNGET bytes are pushed back, all +; hell will break loose. +; +ungetc: + push di + push bx + mov di,[CurrentGetC] + movzx bx,[di+gc_unget_cnt] + mov [bx+di+gc_unget_buf],al + inc bx + mov [di+gc_unget_cnt],bl + pop bx + pop di + ret + +; +; skipspace: Skip leading whitespace using "getc". If we hit end-of-line +; or end-of-file, return with carry set; ZF = true of EOF +; ZF = false for EOLN; otherwise CF = ZF = 0. +; +; Otherwise AL = first character after whitespace +; +skipspace: +.loop: call getc + jc .eof + cmp al,1Ah ; DOS EOF + je .eof + cmp al,0Ah + je .eoln + cmp al,' ' + jbe .loop + ret ; CF = ZF = 0 +.eof: cmp al,al ; Set ZF + stc ; Set CF + ret +.eoln: add al,0FFh ; Set CF, clear ZF + ret + +; +; getint: Load an integer from the getc file. +; Return CF if error; otherwise return integer in EBX +; +getint: + mov di,NumBuf +.getnum: cmp di,NumBufEnd ; Last byte in NumBuf + jae .loaded + push di + call getc + pop di + jc .loaded + stosb + cmp al,'-' + jnb .getnum + call ungetc ; Unget non-numeric +.loaded: mov byte [di],0 + mov si,NumBuf + ; Fall through to parseint +; +; parseint: Convert an integer to a number in EBX +; Get characters from string in DS:SI +; Return CF on error +; DS:SI points to first character after number +; +; Syntaxes accepted: [-]dec, [-]0+oct, [-]0x+hex, val+[KMG] +; +parseint: + push eax + push ecx + push bp + xor eax,eax ; Current digit (keep eax == al) + mov ebx,eax ; Accumulator + mov ecx,ebx ; Base + xor bp,bp ; Used for negative flag +.begin: lodsb + cmp al,'-' + jne .not_minus + xor bp,1 ; Set unary minus flag + jmp short .begin +.not_minus: + cmp al,'0' + jb .err + je .octhex + cmp al,'9' + ja .err + mov cl,10 ; Base = decimal + jmp short .foundbase +.octhex: + lodsb + cmp al,'0' + jb .km ; Value is zero + or al,20h ; Downcase + cmp al,'x' + je .ishex + cmp al,'7' + ja .err + mov cl,8 ; Base = octal + jmp short .foundbase +.ishex: + mov al,'0' ; No numeric value accrued yet + mov cl,16 ; Base = hex +.foundbase: + call unhexchar + jc .km ; Not a (hex) digit + cmp al,cl + jae .km ; Invalid for base + imul ebx,ecx ; Multiply accumulated by base + add ebx,eax ; Add current digit + lodsb + jmp short .foundbase +.km: + dec si ; Back up to last non-numeric + lodsb + or al,20h + cmp al,'k' + je .isk + cmp al,'m' + je .ism + cmp al,'g' + je .isg + dec si ; Back up +.fini: and bp,bp + jz .ret ; CF=0! + neg ebx ; Value was negative +.done: clc +.ret: pop bp + pop ecx + pop eax + ret +.err: stc + jmp short .ret +.isg: shl ebx,10 ; * 2^30 +.ism: shl ebx,10 ; * 2^20 +.isk: shl ebx,10 ; * 2^10 + jmp .fini + + section .bss1 + alignb 4 +NumBuf resb 15 ; Buffer to load number +NumBufEnd resb 1 ; Last byte in NumBuf + +GetCStack resb getc_file_size*MAX_GETC +.end equ $ + + section .data +CurrentGetC dw GetCStack.end ; GetCStack empty + +; +; unhexchar: Convert a hexadecimal digit in AL to the equivalent number; +; return CF=1 if not a hex digit +; + section .text +unhexchar: + cmp al,'0' + jb .ret ; If failure, CF == 1 already + cmp al,'9' + ja .notdigit + sub al,'0' ; CF <- 0 + ret +.notdigit: or al,20h ; upper case -> lower case + cmp al,'a' + jb .ret ; If failure, CF == 1 already + cmp al,'f' + ja .err + sub al,'a'-10 ; CF <- 0 + ret +.err: stc +.ret: ret + +; +; +; getline: Get a command line, converting control characters to spaces +; and collapsing streches to one; a space is appended to the +; end of the string, unless the line is empty. +; The line is terminated by ^J, ^Z or EOF and is written +; to ES:DI. On return, DI points to first char after string. +; CF is set if we hit EOF. +; +getline: + call skipspace + mov dl,1 ; Empty line -> empty string. + jz .eof ; eof + jc .eoln ; eoln + call ungetc +.fillloop: push dx + push di + call getc + pop di + pop dx + jc .ret ; CF set! + cmp al,' ' + jna .ctrl + xor dx,dx +.store: stosb + jmp short .fillloop +.ctrl: cmp al,10 + je .ret ; CF clear! + cmp al,26 + je .eof + and dl,dl + jnz .fillloop ; Ignore multiple spaces + mov al,' ' ; Ctrl -> space + inc dx + jmp short .store +.eoln: clc ; End of line is not end of file + jmp short .ret +.eof: stc +.ret: pushf ; We want the last char to be space! + and dl,dl + jnz .xret + mov al,' ' + stosb +.xret: popf + ret + +; +; parseint_esdi: +; Same as parseint, but takes the input in ES:DI +; +parseint_esdi: + push ds + push es + pop ds + xchg si,di + call parseint + xchg si,di + pop ds + ret diff --git a/core/graphics.inc b/core/graphics.inc new file mode 100644 index 0000000..3ba5146 --- /dev/null +++ b/core/graphics.inc @@ -0,0 +1,330 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +; ---------------------------------------------------------------------------- +; VGA splash screen code +; ---------------------------------------------------------------------------- + +; +; vgadisplayfile: +; Display a graphical splash screen. +; The file is already opened on the top of the getc stack. +; +; Assumes CS == DS == ES. +; + section .text + +vgadisplayfile: + ; This is a cheap and easy way to make sure the screen is + ; cleared in case we were in graphics mode already + call vgaclearmode + call vgasetmode + jnz .error_nz + +.graphalready: + ; Load the header. + mov cx,4+2*2+16*3 + mov di,LSSHeader +.gethdr: + call getc + stosb + loop .gethdr + jc .error + + ; The header WILL be in the first chunk. + cmp dword [LSSMagic],0x1413f33d ; Magic number +.error_nz: jne .error + + mov dx,GraphColorMap ; Color map offset + mov ax,1012h ; Set RGB registers + xor bx,bx ; First register number + mov cx,16 ; 16 registers + int 10h + +.movecursor: + mov ax,[GraphYSize] ; Number of pixel rows + mov dx,[VGAFontSize] + add ax,dx + dec ax + div dl + xor dx,dx ; Set column to 0 + cmp al,[VidRows] + jb .rowsok + mov al,[VidRows] + dec al +.rowsok: + mov dh,al + mov ah,2 + xor bx,bx + int 10h ; Set cursor below image + + mov cx,[GraphYSize] ; Number of graphics rows + mov word [VGAPos],0 + +.drawpixelrow: + push cx + mov di,VGARowBuffer + ; Pre-clear the row buffer + push di + mov cx,640/4 + xor eax,eax + rep stosd + pop di + push di + mov cx,[GraphXSize] + call rledecode ; Decode one row + pop si + push es + mov di,0A000h ; VGA segment + mov es,di + mov di,[VGAPos] + mov bp,640 + call packedpixel2vga + add word [VGAPos],80 + pop es + pop cx + loop .drawpixelrow + +.error: + jmp close ; Tailcall! + +; +; rledecode: +; Decode a pixel row in RLE16 format. +; +; getc stack -> input +; CX -> pixel count +; ES:DI -> output (packed pixel) +; +rledecode: + xor dx,dx ; DL = last pixel, DH = nybble buffer +.loop: + call .getnybble + cmp al,dl + je .run ; Start of run sequence + stosb + mov dl,al + dec cx + jnz .loop +.done: + ret +.run: + xor bx,bx + call .getnybble + or bl,al + jz .longrun +.dorun: + push cx + mov cx,bx + mov al,dl + rep stosb + pop cx + sub cx,bx + ja .loop + jmp short .done +.longrun: + call .getnybble + mov bl,al + call .getnybble + shl al,4 + or bl,al + add bx,16 + jmp short .dorun + +.getnybble: + test dh,10h + jz .low + and dh,0Fh + mov al,dh + ret +.low: + call getc + mov dh,al + shr dh,4 + or dh,10h ; Nybble already read + and al,0Fh + ret + +; +; packedpixel2vga: +; Convert packed-pixel to VGA bitplanes +; +; DS:SI -> packed pixel string +; BP -> pixel count (multiple of 8) +; ES:DI -> output +; +packedpixel2vga: + mov dx,3C4h ; VGA Sequencer Register select port + mov al,2 ; Sequencer mask + out dx,al ; Select the sequencer mask + inc dx ; VGA Sequencer Register data port + mov al,1 + mov bl,al +.planeloop: + pusha + out dx,al +.loop1: + mov cx,8 +.loop2: + xchg cx,bx + lodsb + shr al,cl + rcl ch,1 ; VGA is bigendian. Sigh. + xchg cx,bx + loop .loop2 + mov al,bh + stosb + sub bp,byte 8 + ja .loop1 + popa + inc bl + shl al,1 + cmp bl,4 + jbe .planeloop + ret + +; +; vgasetmode: +; Enable VGA graphics, if possible; return ZF=1 on success +; DS must be set to the base segment; ES is set to DS. +; +vgasetmode: + push ds + pop es + mov al,[UsingVGA] + cmp al,01h + je .success ; Nothing to do... + test al,04h + jz .notvesa + ; We're in a VESA mode, which means VGA; use VESA call + ; to revert the mode, and then call the conventional + ; mode-setting for good measure... + mov ax,4F02h + mov bx,0012h + int 10h + jmp .setmode +.notvesa: + mov ax,1A00h ; Get video card and monitor + xor bx,bx + int 10h + sub bl, 7 ; BL=07h and BL=08h OK + cmp bl, 1 + ja .error ; ZF=0 +; mov bx,TextColorReg +; mov dx,1009h ; Read color registers +; int 10h +.setmode: + mov ax,0012h ; Set mode = 640x480 VGA 16 colors + int 10h + mov dx,linear_color + mov ax,1002h ; Write color registers + int 10h + mov [UsingVGA], byte 1 + + ; Set GXPixCols and GXPixRows + mov dword [GXPixCols],640+(480 << 16) + + call use_font ; Set graphics font/data + mov byte [ScrollAttribute], 00h + +.success: + xor ax,ax ; Set ZF +.error: + ret + +; +; vgaclearmode: +; Disable VGA graphics. It is not safe to assume any value +; for DS or ES. +; +vgaclearmode: + push ds + push es + pushad + mov ax,cs + mov ds,ax + mov es,ax + mov al,[UsingVGA] + and al,al ; Already in text mode? + jz .done + test al,04h + jz .notvesa + mov ax,4F02h ; VESA return to normal video mode + mov bx,0003h + int 10h +.notvesa: + mov ax,0003h ; Return to normal video mode + int 10h +; mov dx,TextColorReg ; Restore color registers +; mov ax,1002h +; int 10h + mov [UsingVGA], byte 0 + + mov byte [ScrollAttribute], 07h + call use_font ; Restore text font/data +.done: + popad + pop es + pop ds + ret + +; +; vgashowcursor/vgahidecursor: +; If VGA graphics is enabled, draw a cursor/clear a cursor +; +vgashowcursor: + pushad + mov al,'_' + jmp short vgacursorcommon +vgahidecursor: + pushad + mov al,' ' +vgacursorcommon: + cmp [UsingVGA], byte 1 + jne .done + mov ah,09h + mov bx,0007h + mov cx,1 + int 10h +.done: + popad + ret + + + section .data + ; Map colors to consecutive DAC registers +linear_color db 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0 + + ; See comboot.doc, INT 22h AX=0017h for the semantics + ; of this byte. +UsingVGA db 0 + + section .bss2 + alignb 4 +LSSHeader equ $ +LSSMagic resd 1 ; Magic number +GraphXSize resw 1 ; Width of splash screen file +GraphYSize resw 1 ; Height of splash screen file +GraphColorMap resb 3*16 +VGAPos resw 1 ; Pointer into VGA memory +VGAFilePtr resw 1 ; Pointer into VGAFileBuf +; TextColorReg resb 17 ; VGA color registers for text mode +%if IS_SYSLINUX +VGAFileBuf resb FILENAME_MAX+2 ; Unmangled VGA image name +%else +VGAFileBuf resb FILENAME_MAX ; Unmangled VGA image name +%endif +VGAFileBufEnd equ $ +VGAFileMBuf resb FILENAME_MAX ; Mangled VGA image name + + alignb 4 +VGARowBuffer resb 640+80 ; Decompression buffer diff --git a/core/head.inc b/core/head.inc new file mode 100644 index 0000000..7117b4e --- /dev/null +++ b/core/head.inc @@ -0,0 +1,32 @@ +; -*- fundamental -*- (asm-mode sucks) +; ----------------------------------------------------------------------- +; +; Copyright 2006-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +; Boston MA 02111-1307, USA; either version 2 of the License, or +; (at your option) any later version; incorporated herein by reference. +; +; ----------------------------------------------------------------------- + +; +; head.inc +; +; Common header includes +; + +%ifndef _HEAD_INC +%define _HEAD_INC + +%include "macros.inc" +%include "config.inc" +%include "layout.inc" +%include "kernel.inc" +%include "bios.inc" +%include "tracers.inc" +%include "stack.inc" +%include "io.inc" + +%endif ; _HEAD_INC diff --git a/core/highmem.inc b/core/highmem.inc new file mode 100644 index 0000000..1f8349b --- /dev/null +++ b/core/highmem.inc @@ -0,0 +1,158 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; highmem.inc +;; +;; Probe for the size of high memory. This can be overridden by a +;; mem= command on the command line while booting a new kernel. +;; + + section .text + +; +; This is set up as a subroutine; it will set up the global variable +; HighMemSize. All registers are preserved. +; +highmemsize: + push es + pushfd + pushad + + push cs + pop es + +; +; First, try INT 15:E820 (get BIOS memory map) +; +; Note: we may have to scan this multiple times, because some (daft) BIOSes +; report main memory as multiple contiguous ranges... +; +get_e820: + mov dword [E820Max],-(1 << 20) ; Max amount of high memory + mov dword [E820Mem],(1 << 20) ; End of detected high memory +.start_over: + mov di,E820Buf + xor ax,ax + mov cx,10 + rep stosw ; Clear buffer + xor ebx,ebx ; Start with first record + jmp short .do_e820 ; Skip "at end" check first time! +.int_loop: and ebx,ebx ; If we're back at beginning... + jz .e820_done ; ... we're done +.do_e820: mov eax,0000E820h + mov edx,534D4150h ; "SMAP" backwards + xor ecx,ecx + mov cl,20 ; ECX <- 20 (size of buffer) + mov di,E820Buf + int 15h + jnc .no_carry + ; If carry, ebx == 0 means error, ebx != 0 means we're done + and ebx,ebx + jnz .e820_done + jmp no_e820 +.no_carry: + cmp eax,534D4150h + jne no_e820 + cmp cx,20 + jb no_e820 + +; +; Look for a memory block starting at <= 1 MB and continuing upward +; + cmp dword [E820Buf+4], byte 0 + ja .int_loop ; Start >= 4 GB? + mov eax, [E820Buf] + cmp dword [E820Buf+16],1 + je .is_ram ; Is it memory? + ; + ; Non-memory range. Remember this as a limit; some BIOSes get the length + ; of primary RAM incorrect! + ; +.not_ram: + cmp eax, (1 << 20) + jb .int_loop ; Starts in lowmem region + cmp eax,[E820Max] + jae .int_loop ; Already above limit + mov [E820Max],eax ; Set limit + jmp .int_loop + +.is_ram: + cmp eax,[E820Mem] + ja .int_loop ; Not contiguous with our starting point + add eax,[E820Buf+8] + jc .overflow + cmp dword [E820Buf+12],0 + je .nooverflow +.overflow: + or eax,-1 +.nooverflow: + cmp eax,[E820Mem] + jbe .int_loop ; All is below our baseline + mov [E820Mem],eax + jmp .start_over ; Start over in case we find an adjacent range + +.e820_done: + mov eax,[E820Mem] + cmp eax,[E820Max] + jna .not_limited + mov eax,[E820Max] +.not_limited: + cmp eax,(1 << 20) + ja got_highmem ; Did we actually find memory? + ; otherwise fall through + +; +; INT 15:E820 failed. Try INT 15:E801. +; +no_e820: + mov ax,0e801h ; Query high memory (semi-recent) + int 15h + jc no_e801 + cmp ax,3c00h + ja no_e801 ; > 3C00h something's wrong with this call + jb e801_hole ; If memory hole we can only use low part + + mov ax,bx + shl eax,16 ; 64K chunks + add eax,(16 << 20) ; Add first 16M + jmp short got_highmem + +; +; INT 15:E801 failed. Try INT 15:88. +; +no_e801: + mov ah,88h ; Query high memory (oldest) + int 15h + cmp ax,14*1024 ; Don't trust memory >15M + jna e801_hole + mov ax,14*1024 +e801_hole: + and eax,0ffffh + shl eax,10 ; Convert from kilobytes + add eax,(1 << 20) ; First megabyte +got_highmem: +%if HIGHMEM_SLOP != 0 + sub eax,HIGHMEM_SLOP +%endif + mov [HighMemSize],eax + popad + popfd + pop es + ret ; Done! + + section .bss + alignb 4 +E820Buf resd 5 ; INT 15:E820 data buffer +E820Mem resd 1 ; Memory detected by E820 +E820Max resd 1 ; Is E820 memory capped? +HighMemSize resd 1 ; End of memory pointer (bytes) diff --git a/core/idle.inc b/core/idle.inc new file mode 100644 index 0000000..bd134ff --- /dev/null +++ b/core/idle.inc @@ -0,0 +1,78 @@ +;; -*- fundamental -*- --------------------------------------------------- +;; +;; Copyright 2008 H. Peter Anvin - All Rights Reserved +;; Copyright 2009 Intel Corporation; author: H. Peter Anvin +;; +;; 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, Inc., 51 Franklin St, Fifth Floor, +;; Boston MA 02110-1301, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + + section .text +TICKS_TO_IDLE equ 4 + +reset_idle: + push ax + mov ax,[cs:BIOS_timer] + mov [cs:IdleTimer],ax + pop ax + sti ; Guard against BIOS/PXE brokenness... + ret + +do_idle: + push ax + push ds + push es + mov ax,cs + mov ds,ax + mov es,ax + pushf + pop ax + test ah,2 + jnz .ok + push si + push cx + mov si,hlt_err + call writestr + mov si,sp + add si,10 + mov cx,16 +.errloop: + ss lodsw + call writehex4 + dec cx + jz .endloop + mov al,' ' + call writechr + jmp .errloop +.endloop: + call crlf + pop cx + pop si + sti +.ok: + mov ax,[BIOS_timer] + sub ax,[IdleTimer] + cmp ax,TICKS_TO_IDLE + jb .done + call [IdleHook] + cmp word [NoHalt],0 + jne .done + hlt +.done: + pop es + pop ds + pop ax +.ret: ret + + section .data +IdleHook dw do_idle.ret +NoHalt dw 0 + +hlt_err db 'ERROR: idle with IF=0', CR, LF, 0 + + section .bss +IdleTimer resw 1 diff --git a/core/init.inc b/core/init.inc new file mode 100644 index 0000000..0b213ac --- /dev/null +++ b/core/init.inc @@ -0,0 +1,47 @@ +; -*- fundamental -*- +; ----------------------------------------------------------------------- +; +; Copyright 2004-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +; Boston MA 02111-1307, USA; either version 2 of the License, or +; (at your option) any later version; incorporated herein by reference. +; +; ----------------------------------------------------------------------- + +; +; init.inc +; +; Common initialization code (inline) +; + + section .text +common_init: + ; Now set up screen parameters + call adjust_screen + +; +; Initialize configuration information +; + call reset_config + +; +; Clear Files structures +; + mov di,Files + mov cx,(MAX_OPEN*open_file_t_size)/4 + xor eax,eax + rep stosd + +%if IS_PXELINUX + mov di,Files+tftp_pktbuf + mov cx,MAX_OPEN +.setbufptr: + mov [di],ax + add di,open_file_t_size + add ax,PKTBUF_SIZE + loop .setbufptr +%endif + section .text ; This is an inline file... diff --git a/core/io.inc b/core/io.inc new file mode 100644 index 0000000..7161346 --- /dev/null +++ b/core/io.inc @@ -0,0 +1,35 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved +;; Copyright 2009 Intel Corporation; author: H. Peter Anvin +;; +;; 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; io.inc +;; +;; I/O related macros +;; + +%ifndef _IO_INC +%define _IO_INC + +%define IO_DELAY_PORT 80h ; Invalid port (we hope!) + +%macro io_delay 0.nolist + out IO_DELAY_PORT,al + out IO_DELAY_PORT,al +%endmacro + +%macro slow_out 2.nolist + out %1,%2 + io_delay +%endmacro + +%endif ; _IO_INC diff --git a/core/isolinux-debug.asm b/core/isolinux-debug.asm new file mode 100644 index 0000000..9c74b7c --- /dev/null +++ b/core/isolinux-debug.asm @@ -0,0 +1,2 @@ +%define DEBUG_MESSAGES 1 +%include "isolinux.asm" diff --git a/core/isolinux-debug.bin b/core/isolinux-debug.bin new file mode 100755 index 0000000000000000000000000000000000000000..45f0061aa8161b49264d04c2a9dbd4d6999779a7 GIT binary patch literal 14336 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 0x7c00. + ; Serious hack alert. +%ifndef DEBUG_MESSAGES +_hybrid_signature: + dd 0x7078c0fb ; An arbitrary number... + +_start_hybrid: + pop cx ; EBIOS flag + pop word [cs:bsSecPerTrack] + pop word [cs:bsHeads] + pop dx + pop di + pop es + xor eax,eax + xor ebx,ebx + cmp sp,7C00h + jae .nooffset + pop eax + pop ebx +.nooffset: + mov [cs:bsHidden],eax + mov [cs:bsHidden+4],ebx + + mov si,bios_cbios + jcxz _start_common + mov si,bios_ebios + jmp _start_common +%endif + +_start1: + mov si,bios_cdrom +_start_common: + mov [cs:InitStack],sp ; Save initial stack pointer + mov [cs:InitStack+2],ss + xor ax,ax + mov ss,ax + mov sp,StackBuf ; Set up stack + push es ; Save initial ES:DI -> $PnP pointer + push di + mov ds,ax + mov es,ax + mov fs,ax + mov gs,ax + sti + cld + + mov [BIOSType],si + mov eax,[si] + mov [GetlinsecPtr],eax + + ; Show signs of life + mov si,syslinux_banner + call writestr_early +%ifdef DEBUG_MESSAGES + mov si,copyright_str +%else + mov si,[BIOSName] +%endif + call writestr_early + + ; + ; Before modifying any memory, get the checksum of bytes + ; 64-2048 + ; +initial_csum: xor edi,edi + mov si,bi_end + mov cx,(SECTOR_SIZE-64) >> 2 +.loop: lodsd + add edi,eax + loop .loop + mov [FirstSecSum],edi + + mov [DriveNumber],dl +%ifdef DEBUG_MESSAGES + mov si,startup_msg + call writemsg + mov al,dl + call writehex2 + call crlf +%endif + ; + ; Initialize spec packet buffers + ; + mov di,_spec_start + mov cx,_spec_len >> 2 + xor eax,eax + rep stosd + + ; Initialize length field of the various packets + mov byte [spec_packet],13h + mov byte [drive_params],30 + mov byte [dapa],16 + mov byte [dspec_packet],13h + + ; Other nonzero fields + inc word [dsp_sectors] + + ; Are we just pretending to be a CD-ROM? + cmp word [BIOSType],bios_cdrom + jne found_drive ; If so, no spec packet... + + ; Now figure out what we're actually doing + ; Note: use passed-in DL value rather than 7Fh because + ; at least some BIOSes will get the wrong value otherwise + mov ax,4B01h ; Get disk emulation status + mov dl,[DriveNumber] + mov si,spec_packet + call int13 + jc award_hack ; changed for BrokenAwardHack + mov dl,[DriveNumber] + cmp [sp_drive],dl ; Should contain the drive number + jne spec_query_failed + +%ifdef DEBUG_MESSAGES + mov si,spec_ok_msg + call writemsg + mov al,byte [sp_drive] + call writehex2 + call crlf +%endif + +found_drive: + ; Alright, we have found the drive. Now, try to find the + ; boot file itself. If we have a boot info table, life is + ; good; if not, we have to make some assumptions, and try + ; to figure things out ourselves. In particular, the + ; assumptions we have to make are: + ; - single session only + ; - only one boot entry (no menu or other alternatives) + + cmp dword [bi_file],0 ; Address of code to load + jne found_file ; Boot info table present :) + +%ifdef DEBUG_MESSAGES + mov si,noinfotable_msg + call writemsg +%endif + + ; No such luck. See if the spec packet contained one. + mov eax,[sp_lba] + and eax,eax + jz set_file ; Good enough + +%ifdef DEBUG_MESSAGES + mov si,noinfoinspec_msg + call writemsg +%endif + + ; No such luck. Get the Boot Record Volume, assuming single + ; session disk, and that we're the first entry in the chain. + mov eax,17 ; Assumed address of BRV + mov bx,trackbuf + call getonesec + + mov eax,[trackbuf+47h] ; Get boot catalog address + mov bx,trackbuf + call getonesec ; Get boot catalog + + mov eax,[trackbuf+28h] ; First boot entry + ; And hope and pray this is us... + + ; Some BIOSes apparently have limitations on the size + ; that may be loaded (despite the El Torito spec being very + ; clear on the fact that it must all be loaded.) Therefore, + ; we load it ourselves, and *bleep* the BIOS. + +set_file: + mov [bi_file],eax + +found_file: + ; Set up boot file sizes + mov eax,[bi_length] + sub eax,SECTOR_SIZE-3 ; ... minus sector loaded + shr eax,2 ; bytes->dwords + mov [ImageDwords],eax ; boot file dwords + add eax,(2047 >> 2) + shr eax,9 ; dwords->sectors + mov [ImageSectors],ax ; boot file sectors + + mov eax,[bi_file] ; Address of code to load + inc eax ; Don't reload bootstrap code +%ifdef DEBUG_MESSAGES + mov si,offset_msg + call writemsg + call writehex8 + call crlf +%endif + + ; Just in case some BIOSes have problems with + ; segment wraparound, use the normalized address + mov bx,((7C00h+2048) >> 4) + mov es,bx + xor bx,bx + mov bp,[ImageSectors] +%ifdef DEBUG_MESSAGES + push ax + mov si,size_msg + call writemsg + mov ax,bp + call writehex4 + call crlf + pop ax +%endif + call getlinsec + + push ds + pop es + +%ifdef DEBUG_MESSAGES + mov si,loaded_msg + call writemsg +%endif + + ; Verify the checksum on the loaded image. +verify_image: + mov si,7C00h+2048 + mov bx,es + mov ecx,[ImageDwords] + mov edi,[FirstSecSum] ; First sector checksum +.loop es lodsd + add edi,eax + dec ecx + jz .done + and si,si + jnz .loop + ; SI wrapped around, advance ES + add bx,1000h + mov es,bx + jmp short .loop +.done: mov ax,ds + mov es,ax + cmp [bi_csum],edi + je integrity_ok + + mov si,checkerr_msg + call writemsg + jmp kaboom + +integrity_ok: +%ifdef DEBUG_MESSAGES + mov si,allread_msg + call writemsg +%endif + jmp all_read ; Jump to main code + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Start of BrokenAwardHack --- 10-nov-2002 Knut_Petersen@t-online.de +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; There is a problem with certain versions of the AWARD BIOS ... +;; the boot sector will be loaded and executed correctly, but, because the +;; int 13 vector points to the wrong code in the BIOS, every attempt to +;; load the spec packet will fail. We scan for the equivalent of +;; +;; mov ax,0201h +;; mov bx,7c00h +;; mov cx,0006h +;; mov dx,0180h +;; pushf +;; call +;; +;; and use as the new vector for int 13. The code above is +;; used to load the boot code into ram, and there should be no reason +;; for anybody to change it now or in the future. There are no opcodes +;; that use encodings relativ to IP, so scanning is easy. If we find the +;; code above in the BIOS code we can be pretty sure to run on a machine +;; with an broken AWARD BIOS ... +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; +%ifdef DEBUG_MESSAGES ;; + ;; +award_notice db "Trying BrokenAwardHack first ...",CR,LF,0 ;; +award_not_orig db "BAH: Original Int 13 vector : ",0 ;; +award_not_new db "BAH: Int 13 vector changed to : ",0 ;; +award_not_succ db "BAH: SUCCESS",CR,LF,0 ;; +award_not_fail db "BAH: FAILURE" ;; +award_not_crlf db CR,LF,0 ;; + ;; +%endif ;; + ;; +award_oldint13 dd 0 ;; +award_string db 0b8h,1,2,0bbh,0,7ch,0b9h,6,0,0bah,80h,1,09ch,09ah ;; + ;; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +award_hack: mov si,spec_err_msg ; Moved to this place from + call writemsg ; spec_query_faild + ; +%ifdef DEBUG_MESSAGES ; + ; + mov si,award_notice ; display our plan + call writemsg ; + mov si,award_not_orig ; display original int 13 + call writemsg ; vector +%endif ; + mov eax,[13h*4] ; + mov [award_oldint13],eax ; + ; +%ifdef DEBUG_MESSAGES ; + ; + call writehex8 ; + mov si,award_not_crlf ; + call writestr_early ; +%endif ; + push es ; save ES + mov ax,0f000h ; ES = BIOS Seg + mov es,ax ; + cld ; + xor di,di ; start at ES:DI = f000:0 +award_loop: push di ; save DI + mov si,award_string ; scan for award_string + mov cx,7 ; length of award_string = 7dw + repz cmpsw ; compare + pop di ; restore DI + jcxz award_found ; jmp if found + inc di ; not found, inc di + jno award_loop ; + ; +award_failed: pop es ; No, not this way :-(( +award_fail2: ; + ; +%ifdef DEBUG_MESSAGES ; + ; + mov si,award_not_fail ; display failure ... + call writemsg ; +%endif ; + mov eax,[award_oldint13] ; restore the original int + or eax,eax ; 13 vector if there is one + jz spec_query_failed ; and try other workarounds + mov [13h*4],eax ; + jmp spec_query_failed ; + ; +award_found: mov eax,[es:di+0eh] ; load possible int 13 addr + pop es ; restore ES + ; + cmp eax,[award_oldint13] ; give up if this is the + jz award_failed ; active int 13 vector, + mov [13h*4],eax ; otherwise change 0:13h*4 + ; + ; +%ifdef DEBUG_MESSAGES ; + ; + push eax ; display message and + mov si,award_not_new ; new vector address + call writemsg ; + pop eax ; + call writehex8 ; + mov si,award_not_crlf ; + call writestr_early ; +%endif ; + mov ax,4B01h ; try to read the spec packet + mov dl,[DriveNumber] ; now ... it should not fail + mov si,spec_packet ; any longer + int 13h ; + jc award_fail2 ; + ; +%ifdef DEBUG_MESSAGES ; + ; + mov si,award_not_succ ; display our SUCCESS + call writemsg ; +%endif ; + jmp found_drive ; and leave error recovery code + ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; End of BrokenAwardHack ---- 10-nov-2002 Knut_Petersen@t-online.de +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + + ; INT 13h, AX=4B01h, DL= failed. + ; Try to scan the entire 80h-FFh from the end. + +spec_query_failed: + + ; some code moved to BrokenAwardHack + + mov dl,0FFh +.test_loop: pusha + mov ax,4B01h + mov si,spec_packet + mov byte [si],13h ; Size of buffer + call int13 + popa + jc .still_broken + + mov si,maybe_msg + call writemsg + mov al,dl + call writehex2 + call crlf + + cmp byte [sp_drive],dl + jne .maybe_broken + + ; Okay, good enough... + mov si,alright_msg + call writemsg +.found_drive0: mov [DriveNumber],dl +.found_drive: jmp found_drive + + ; Award BIOS 4.51 apparently passes garbage in sp_drive, + ; but if this was the drive number originally passed in + ; DL then consider it "good enough" +.maybe_broken: + mov al,[DriveNumber] + cmp al,dl + je .found_drive + + ; Intel Classic R+ computer with Adaptec 1542CP BIOS 1.02 + ; passes garbage in sp_drive, and the drive number originally + ; passed in DL does not have 80h bit set. + or al,80h + cmp al,dl + je .found_drive0 + +.still_broken: dec dx + cmp dl, 80h + jnb .test_loop + + ; No spec packet anywhere. Some particularly pathetic + ; BIOSes apparently don't even implement function + ; 4B01h, so we can't query a spec packet no matter + ; what. If we got a drive number in DL, then try to + ; use it, and if it works, then well... + mov dl,[DriveNumber] + cmp dl,81h ; Should be 81-FF at least + jb fatal_error ; If not, it's hopeless + + ; Write a warning to indicate we're on *very* thin ice now + mov si,nospec_msg + call writemsg + mov al,dl + call writehex2 + call crlf + mov si,trysbm_msg + call writemsg + jmp .found_drive ; Pray that this works... + +fatal_error: + mov si,nothing_msg + call writemsg + +.norge: jmp short .norge + + ; Information message (DS:SI) output + ; Prefix with "isolinux: " + ; +writemsg: push ax + push si + mov si,isolinux_str + call writestr_early + pop si + call writestr_early + pop ax + ret + +; +; Write a character to the screen. There is a more "sophisticated" +; version of this in the subsequent code, so we patch the pointer +; when appropriate. +; + +writechr: + jmp near writechr_simple ; 3-byte jump + +writechr_simple: + pushfd + pushad + mov ah,0Eh + xor bx,bx + int 10h + popad + popfd + ret + +; +; int13: save all the segment registers and call INT 13h. +; Some CD-ROM BIOSes have been found to corrupt segment registers +; and/or disable interrupts. +; +int13: + pushf + push bp + push ds + push es + push fs + push gs + int 13h + mov bp,sp + setc [bp+10] ; Propagate CF to the caller + pop gs + pop fs + pop es + pop ds + pop bp + popf + ret + +; +; Get one sector. Convenience entry point. +; +getonesec: + mov bp,1 + ; Fall through to getlinsec + +; +; Get linear sectors - EBIOS LBA addressing, 2048-byte sectors. +; +; Input: +; EAX - Linear sector number +; ES:BX - Target buffer +; BP - Sector count +; +getlinsec: jmp word [cs:GetlinsecPtr] + +%ifndef DEBUG_MESSAGES + +; +; First, the variants that we use when actually loading off a disk +; (hybrid mode.) These are adapted versions of the equivalent routines +; in ldlinux.asm. +; + +; +; getlinsec_ebios: +; +; getlinsec implementation for floppy/HDD EBIOS (EDD) +; +getlinsec_ebios: + xor edx,edx + shld edx,eax,2 + shl eax,2 ; Convert to HDD sectors + add eax,[bsHidden] + adc edx,[bsHidden+4] + shl bp,2 + +.loop: + push bp ; Sectors left +.retry2: + call maxtrans ; Enforce maximum transfer size + movzx edi,bp ; Sectors we are about to read + mov cx,retry_count +.retry: + + ; Form DAPA on stack + push edx + push eax + push es + push bx + push di + push word 16 + mov si,sp + pushad + mov dl,[DriveNumber] + push ds + push ss + pop ds ; DS <- SS + mov ah,42h ; Extended Read + call int13 + pop ds + popad + lea sp,[si+16] ; Remove DAPA + jc .error + pop bp + add eax,edi ; Advance sector pointer + adc edx,0 + sub bp,di ; Sectors left + shl di,9 ; 512-byte sectors + add bx,di ; Advance buffer pointer + and bp,bp + jnz .loop + + ret + +.error: + ; Some systems seem to get "stuck" in an error state when + ; using EBIOS. Doesn't happen when using CBIOS, which is + ; good, since some other systems get timeout failures + ; waiting for the floppy disk to spin up. + + pushad ; Try resetting the device + xor ax,ax + mov dl,[DriveNumber] + call int13 + popad + loop .retry ; CX-- and jump if not zero + + ;shr word [MaxTransfer],1 ; Reduce the transfer size + ;jnz .retry2 + + ; Total failure. Try falling back to CBIOS. + mov word [GetlinsecPtr], getlinsec_cbios + ;mov byte [MaxTransfer],63 ; Max possibe CBIOS transfer + + pop bp + jmp getlinsec_cbios.loop + +; +; getlinsec_cbios: +; +; getlinsec implementation for legacy CBIOS +; +getlinsec_cbios: + xor edx,edx + shl eax,2 ; Convert to HDD sectors + add eax,[bsHidden] + shl bp,2 + +.loop: + push edx + push eax + push bp + push bx + + movzx esi,word [bsSecPerTrack] + movzx edi,word [bsHeads] + ; + ; Dividing by sectors to get (track,sector): we may have + ; up to 2^18 tracks, so we need to use 32-bit arithmetric. + ; + div esi + xor cx,cx + xchg cx,dx ; CX <- sector index (0-based) + ; EDX <- 0 + ; eax = track # + div edi ; Convert track to head/cyl + + ; We should test this, but it doesn't fit... + ; cmp eax,1023 + ; ja .error + + ; + ; Now we have AX = cyl, DX = head, CX = sector (0-based), + ; BP = sectors to transfer, SI = bsSecPerTrack, + ; ES:BX = data target + ; + + call maxtrans ; Enforce maximum transfer size + + ; Must not cross track boundaries, so BP <= SI-CX + sub si,cx + cmp bp,si + jna .bp_ok + mov bp,si +.bp_ok: + + shl ah,6 ; Because IBM was STOOPID + ; and thought 8 bits were enough + ; then thought 10 bits were enough... + inc cx ; Sector numbers are 1-based, sigh + or cl,ah + mov ch,al + mov dh,dl + mov dl,[DriveNumber] + xchg ax,bp ; Sector to transfer count + mov ah,02h ; Read sectors + mov bp,retry_count +.retry: + pushad + call int13 + popad + jc .error +.resume: + movzx ecx,al ; ECX <- sectors transferred + shl ax,9 ; Convert sectors in AL to bytes in AX + pop bx + add bx,ax + pop bp + pop eax + pop edx + add eax,ecx + sub bp,cx + jnz .loop + ret + +.error: + dec bp + jnz .retry + + xchg ax,bp ; Sectors transferred <- 0 + shr word [MaxTransfer],1 + jnz .resume + jmp disk_error + +; +; Truncate BP to MaxTransfer +; +maxtrans: + cmp bp,[MaxTransfer] + jna .ok + mov bp,[MaxTransfer] +.ok: ret + +%endif + +; +; This is the variant we use for real CD-ROMs: +; LBA, 2K sectors, some special error handling. +; +getlinsec_cdrom: + mov si,dapa ; Load up the DAPA + mov [si+4],bx + mov [si+6],es + mov [si+8],eax +.loop: + push bp ; Sectors left + cmp bp,[MaxTransferCD] + jbe .bp_ok + mov bp,[MaxTransferCD] +.bp_ok: + mov [si+2],bp + push si + mov dl,[DriveNumber] + mov ah,42h ; Extended Read + call xint13 + pop si + pop bp + movzx eax,word [si+2] ; Sectors we read + add [si+8],eax ; Advance sector pointer + sub bp,ax ; Sectors left + shl ax,SECTOR_SHIFT-4 ; 2048-byte sectors -> segment + add [si+6],ax ; Advance buffer pointer + and bp,bp + jnz .loop + mov eax,[si+8] ; Next sector + ret + + ; INT 13h with retry +xint13: mov byte [RetryCount],retry_count +.try: pushad + call int13 + jc .error + add sp,byte 8*4 ; Clean up stack + ret +.error: + mov [DiskError],ah ; Save error code + popad + mov [DiskSys],ax ; Save system call number + dec byte [RetryCount] + jz .real_error + push ax + mov al,[RetryCount] + mov ah,[dapa+2] ; Sector transfer count + cmp al,2 ; Only 2 attempts left + ja .nodanger + mov ah,1 ; Drop transfer size to 1 + jmp short .setsize +.nodanger: + cmp al,retry_count-2 + ja .again ; First time, just try again + shr ah,1 ; Otherwise, try to reduce + adc ah,0 ; the max transfer size, but not to 0 +.setsize: + mov [MaxTransferCD],ah + mov [dapa+2],ah +.again: + pop ax + jmp .try + +.real_error: mov si,diskerr_msg + call writemsg + mov al,[DiskError] + call writehex2 + mov si,oncall_str + call writestr_early + mov ax,[DiskSys] + call writehex4 + mov si,ondrive_str + call writestr_early + mov al,dl + call writehex2 + call crlf + ; Fall through to kaboom + +; +; kaboom: write a message and bail out. Wait for a user keypress, +; then do a hard reboot. +; +disk_error: +kaboom: + RESET_STACK_AND_SEGS AX + mov si,err_bootfailed + call writestr + call getchar + cli + mov word [BIOS_magic],0 ; Cold reboot + jmp 0F000h:0FFF0h ; Reset vector address + +; ----------------------------------------------------------------------------- +; Common modules needed in the first sector +; ----------------------------------------------------------------------------- + +%include "writestr.inc" ; String output +writestr_early equ writestr +%include "writehex.inc" ; Hexadecimal output + +; ----------------------------------------------------------------------------- +; Data that needs to be in the first sector +; ----------------------------------------------------------------------------- + +syslinux_banner db CR, LF, 'ISOLINUX ', VERSION_STR, ' ', DATE_STR, ' ', 0 +copyright_str db ' Copyright (C) 1994-' + asciidec YEAR + db ' H. Peter Anvin et al', CR, LF, 0 +isolinux_str db 'isolinux: ', 0 +%ifdef DEBUG_MESSAGES +startup_msg: db 'Starting up, DL = ', 0 +spec_ok_msg: db 'Loaded spec packet OK, drive = ', 0 +secsize_msg: db 'Sector size ', 0 +offset_msg: db 'Main image LBA = ', 0 +size_msg: db 'Sectors to load = ', 0 +loaded_msg: db 'Loaded boot image, verifying...', CR, LF, 0 +verify_msg: db 'Image checksum verified.', CR, LF, 0 +allread_msg db 'Main image read, jumping to main code...', CR, LF, 0 +%endif +noinfotable_msg db 'No boot info table, assuming single session disk...', CR, LF, 0 +noinfoinspec_msg db 'Spec packet missing LBA information, trying to wing it...', CR, LF, 0 +spec_err_msg: db 'Loading spec packet failed, trying to wing it...', CR, LF, 0 +maybe_msg: db 'Found something at drive = ', 0 +alright_msg: db 'Looks reasonable, continuing...', CR, LF, 0 +nospec_msg db 'Extremely broken BIOS detected, last attempt with drive = ', 0 +nothing_msg: db 'Failed to locate CD-ROM device; boot failed.', CR, LF +trysbm_msg db 'See http://syslinux.zytor.com/sbm for more information.', CR, LF, 0 +diskerr_msg: db 'Disk error ', 0 +oncall_str: db ', AX = ',0 +ondrive_str: db ', drive ', 0 +checkerr_msg: db 'Image checksum error, sorry...', CR, LF, 0 + +err_bootfailed db CR, LF, 'Boot failed: press a key to retry...' +bailmsg equ err_bootfailed +crlf_msg db CR, LF +null_msg db 0 + +bios_cdrom_str db 'ETCD', 0 +%ifndef DEBUG_MESSAGES +bios_cbios_str db 'CHDD', 0 +bios_ebios_str db 'EHDD' ,0 +%endif + + alignz 4 +bios_cdrom: dw getlinsec_cdrom, bios_cdrom_str +%ifndef DEBUG_MESSAGES +bios_cbios: dw getlinsec_cbios, bios_cbios_str +bios_ebios: dw getlinsec_ebios, bios_ebios_str +%endif + +; Maximum transfer size +MaxTransfer dw 127 ; Hard disk modes +MaxTransferCD dw 32 ; CD mode + +rl_checkpt equ $ ; Must be <= 800h + + ; This pads to the end of sector 0 and errors out on + ; overflow. + times 2048-($-$$) db 0 + +; ---------------------------------------------------------------------------- +; End of code and data that have to be in the first sector +; ---------------------------------------------------------------------------- + +all_read: + +; Test tracers + TRACER 'T' + TRACER '>' + +; +; Common initialization code +; +%include "init.inc" +%include "cpuinit.inc" + + ; Patch the writechr routine to point to the full code + mov word [writechr+1], writechr_full-(writechr+3) + +; Tell the user we got this far... +%ifndef DEBUG_MESSAGES ; Gets messy with debugging on + mov si,copyright_str + call writestr_early +%endif + +; +; Now we're all set to start with our *real* business. First load the +; configuration file (if any) and parse it. +; +; In previous versions I avoided using 32-bit registers because of a +; rumour some BIOSes clobbered the upper half of 32-bit registers at +; random. I figure, though, that if there are any of those still left +; they probably won't be trying to install Linux on them... +; +; The code is still ripe with 16-bitisms, though. Not worth the hassle +; to take'm out. In fact, we may want to put them back if we're going +; to boot ELKS at some point. +; + +; +; Now, we need to sniff out the actual filesystem data structures. +; mkisofs gave us a pointer to the primary volume descriptor +; (which will be at 16 only for a single-session disk!); from the PVD +; we should be able to find the rest of what we need to know. +; +get_fs_structures: + mov eax,[bi_pvd] + mov bx,trackbuf + call getonesec + + mov eax,[trackbuf+156+2] + mov [RootDir+dir_lba],eax + mov [CurrentDir+dir_lba],eax +%ifdef DEBUG_MESSAGES + mov si,dbg_rootdir_msg + call writemsg + call writehex8 + call crlf +%endif + mov eax,[trackbuf+156+10] + mov [RootDir+dir_len],eax + mov [CurrentDir+dir_len],eax + add eax,SECTOR_SIZE-1 + shr eax,SECTOR_SHIFT + mov [RootDir+dir_clust],eax + mov [CurrentDir+dir_clust],eax + + ; Look for an isolinux directory, and if found, + ; make it the current directory instead of the root + ; directory. + ; Also copy the name of the directory to CurrentDirName + mov word [CurrentDirName],ROOT_DIR_WORD ; Write '/',0 to the CurrentDirName + mov di,boot_dir ; Search for /boot/isolinux + mov al,02h + push di + call searchdir_iso + pop di + jnz .found_dir + mov di,isolinux_dir + mov al,02h ; Search for /isolinux + push di + call searchdir_iso + pop di + jz .no_isolinux_dir +.found_dir: + ; Copy current directory name to CurrentDirName + push si + push di + mov si,di + mov di,CurrentDirName + call strcpy + mov byte [di],0 ;done in case it's not word aligned + dec di + mov byte [di],'/' + pop di + pop si + + mov [CurrentDir+dir_len],eax + mov eax,[si+file_left] + mov [CurrentDir+dir_clust],eax + xor eax,eax ; Free this file pointer entry + xchg eax,[si+file_sector] + mov [CurrentDir+dir_lba],eax +%ifdef DEBUG_MESSAGES + push si + mov si,dbg_isodir_msg + call writemsg + pop si + call writehex8 + call crlf +%endif +.no_isolinux_dir: + +; +; Locate the configuration file +; +load_config: +%ifdef DEBUG_MESSAGES + mov si,dbg_config_msg + call writemsg +%endif + + mov si,config_name + mov di,ConfigName + call strcpy + + mov di,ConfigName + call open + jz no_config_file ; Not found or empty + +%ifdef DEBUG_MESSAGES + mov si,dbg_configok_msg + call writemsg +%endif + +; +; Now we have the config file open. Parse the config file and +; run the user interface. +; +%include "ui.inc" + +; +; Enable disk emulation. The kind of disk we emulate is dependent on the +; size of the file: 1200K, 1440K or 2880K floppy, otherwise harddisk. +; +is_disk_image: + TRACER CR + TRACER LF + TRACER 'D' + TRACER ':' + + mov edx,eax ; File size + mov di,img_table + mov cx,img_table_count + mov eax,[si+file_sector] ; Starting LBA of file + mov [dsp_lba],eax ; Location of file + mov byte [dsp_drive], 0 ; 00h floppy, 80h hard disk +.search_table: + TRACER 't' + mov eax,[di+4] + cmp edx,[di] + je .type_found + add di,8 + loop .search_table + + ; Hard disk image. Need to examine the partition table + ; in order to deduce the C/H/S geometry. Sigh. +.hard_disk_image: + TRACER 'h' + cmp edx,512 + jb .bad_image + + mov bx,trackbuf + mov cx,1 ; Load 1 sector + call getfssec + + cmp word [trackbuf+510],0aa55h ; Boot signature + jne .bad_image ; Image not bootable + + mov cx,4 ; 4 partition entries + mov di,trackbuf+446 ; Start of partition table + + xor ax,ax ; Highest sector(al) head(ah) + +.part_scan: + cmp byte [di+4], 0 + jz .part_loop + lea si,[di+1] + call .hs_check + add si,byte 4 + call .hs_check +.part_loop: + add di,byte 16 + loop .part_scan + + push eax ; H/S + push edx ; File size + mov bl,ah + xor bh,bh + inc bx ; # of heads in BX + xor ah,ah ; # of sectors in AX + cwde ; EAX[31:16] <- 0 + mul bx + shl eax,9 ; Convert to bytes + ; Now eax contains the number of bytes per cylinder + pop ebx ; File size + xor edx,edx + div ebx + and edx,edx + jz .no_remainder + inc eax ; Fractional cylinder... + ; Now (e)ax contains the number of cylinders +.no_remainder: cmp eax,1024 + jna .ok_cyl + mov ax,1024 ; Max possible # +.ok_cyl: dec ax ; Convert to max cylinder no + pop ebx ; S(bl) H(bh) + shl ah,6 + or bl,ah + xchg ax,bx + shl eax,16 + mov ah,bl + mov al,4 ; Hard disk boot + mov byte [dsp_drive], 80h ; Drive 80h = hard disk + +.type_found: + TRACER 'T' + mov bl,[sp_media] + and bl,0F0h ; Copy controller info bits + or al,bl + mov [dsp_media],al ; Emulation type + shr eax,8 + mov [dsp_chs],eax ; C/H/S geometry + mov ax,[sp_devspec] ; Copy device spec + mov [dsp_devspec],ax + mov al,[sp_controller] ; Copy controller index + mov [dsp_controller],al + + TRACER 'V' + call vgaclearmode ; Reset video + + mov ax,4C00h ; Enable emulation and boot + mov si,dspec_packet + mov dl,[DriveNumber] + lss sp,[InitStack] + TRACER 'X' + + call int13 + + ; If this returns, we have problems +.bad_image: + mov si,err_disk_image + call writestr + jmp enter_command + +; +; Look for the highest seen H/S geometry +; We compute cylinders separately +; +.hs_check: + mov bl,[si] ; Head # + cmp bl,ah + jna .done_track + mov ah,bl ; New highest head # +.done_track: mov bl,[si+1] + and bl,3Fh ; Sector # + cmp bl,al + jna .done_sector + mov al,bl +.done_sector: ret + +; +; close_file: +; Deallocates a file structure (pointer in SI) +; Assumes CS == DS. +; +close_file: + and si,si + jz .closed + mov dword [si],0 ; First dword == file_left + xor si,si +.closed: ret + +; +; searchdir: +; +; Open a file +; +; On entry: +; DS:DI = filename +; If successful: +; ZF clear +; SI = file pointer +; EAX = file length in bytes +; If unsuccessful +; ZF set +; +; Assumes CS == DS == ES, and trashes BX and CX. +; +; searchdir_iso is a special entry point for ISOLINUX only. In addition +; to the above, searchdir_iso passes a file flag mask in AL. This is useful +; for searching for directories. +; +alloc_failure: + xor ax,ax ; ZF <- 1 + ret + +searchdir: + xor al,al +searchdir_iso: + mov [ISOFlags],al + TRACER 'S' + call allocate_file ; Temporary file structure for directory + jnz alloc_failure + push es + push ds + pop es ; ES = DS + mov si,CurrentDir + cmp byte [di],'/' ; If filename begins with slash + jne .not_rooted + inc di ; Skip leading slash + mov si,RootDir ; Reference root directory instead +.not_rooted: + mov eax,[si+dir_clust] + mov [bx+file_left],eax + shl eax,SECTOR_SHIFT + mov [bx+file_bytesleft],eax + mov eax,[si+dir_lba] + mov [bx+file_sector],eax + mov edx,[si+dir_len] + +.look_for_slash: + mov ax,di +.scan: + mov cl,[di] + inc di + and cl,cl + jz .isfile + cmp cl,'/' + jne .scan + mov [di-1],byte 0 ; Terminate at directory name + mov cl,02h ; Search for directory + xchg cl,[ISOFlags] + + push di ; Save these... + push cx + + ; Create recursion stack frame... + push word .resume ; Where to "return" to + push es +.isfile: xchg ax,di + +.getsome: + ; Get a chunk of the directory + ; This relies on the fact that ISOLINUX doesn't change SI + mov si,trackbuf + TRACER 'g' + pushad + xchg bx,si + mov cx,[BufSafe] + call getfssec + popad + +.compare: + movzx eax,byte [si] ; Length of directory entry + cmp al,33 + jb .next_sector + TRACER 'c' + mov cl,[si+25] + xor cl,[ISOFlags] + test cl, byte 8Eh ; Unwanted file attributes! + jnz .not_file + pusha + movzx cx,byte [si+32] ; File identifier length + add si,byte 33 ; File identifier offset + TRACER 'i' + call iso_compare_names + popa + je .success +.not_file: + sub edx,eax ; Decrease bytes left + jbe .failure + add si,ax ; Advance pointer + +.check_overrun: + ; Did we finish the buffer? + cmp si,trackbuf+trackbufsize + jb .compare ; No, keep going + + jmp short .getsome ; Get some more directory + +.next_sector: + ; Advance to the beginning of next sector + lea ax,[si+SECTOR_SIZE-1] + and ax,~(SECTOR_SIZE-1) + sub ax,si + jmp short .not_file ; We still need to do length checks + +.failure: xor eax,eax ; ZF = 1 + mov [bx+file_sector],eax + pop es + ret + +.success: + mov eax,[si+2] ; Location of extent + mov [bx+file_sector],eax + mov eax,[si+10] ; Data length + mov [bx+file_bytesleft],eax + push eax + add eax,SECTOR_SIZE-1 + shr eax,SECTOR_SHIFT + mov [bx+file_left],eax + pop eax + jz .failure ; Empty file? + ; ZF = 0 + mov si,bx + pop es + ret + +.resume: ; We get here if we were only doing part of a lookup + ; This relies on the fact that .success returns bx == si + xchg edx,eax ; Directory length in edx + pop cx ; Old ISOFlags + pop di ; Next filename pointer + mov byte [di-1], '/' ; Restore slash + mov [ISOFlags],cl ; Restore the flags + jz .failure ; Did we fail? If so fail for real! + jmp .look_for_slash ; Otherwise, next level + +; +; allocate_file: Allocate a file structure +; +; If successful: +; ZF set +; BX = file pointer +; In unsuccessful: +; ZF clear +; +allocate_file: + TRACER 'a' + push cx + mov bx,Files + mov cx,MAX_OPEN +.check: cmp dword [bx], byte 0 + je .found + add bx,open_file_t_size ; ZF = 0 + loop .check + ; ZF = 0 if we fell out of the loop +.found: pop cx + ret + +; +; iso_compare_names: +; Compare the names DS:SI and DS:DI and report if they are +; equal from an ISO 9660 perspective. SI is the name from +; the filesystem; CX indicates its length, and ';' terminates. +; DI is expected to end with a null. +; +; Note: clobbers AX, CX, SI, DI; assumes DS == ES == base segment +; + +iso_compare_names: + ; First, terminate and canonicalize input filename + push di + mov di,ISOFileName +.canon_loop: jcxz .canon_end + lodsb + dec cx + cmp al,';' + je .canon_end + and al,al + je .canon_end + stosb + cmp di,ISOFileNameEnd-1 ; Guard against buffer overrun + jb .canon_loop +.canon_end: + cmp di,ISOFileName + jbe .canon_done + cmp byte [di-1],'.' ; Remove terminal dots + jne .canon_done + dec di + jmp short .canon_end +.canon_done: + mov [di],byte 0 ; Null-terminate string + pop di + mov si,ISOFileName +.compare: + lodsb + mov ah,[di] + inc di + and ax,ax + jz .success ; End of string for both + and al,al ; Is either one end of string? + jz .failure ; If so, failure + and ah,ah + jz .failure + or ax,2020h ; Convert to lower case + cmp al,ah + je .compare +.failure: and ax,ax ; ZF = 0 (at least one will be nonzero) +.success: ret + +; +; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed +; to by ES:DI; ends on encountering any whitespace. +; DI is preserved. +; +; This verifies that a filename is < FILENAME_MAX characters, +; doesn't contain whitespace, zero-pads the output buffer, +; and removes trailing dots and redundant slashes, +; so "repe cmpsb" can do a compare, and the +; path-searching routine gets a bit of an easier job. +; +mangle_name: + push di + push bx + xor ax,ax + mov cx,FILENAME_MAX-1 + mov bx,di + +.mn_loop: + lodsb + cmp al,' ' ; If control or space, end + jna .mn_end + cmp al,ah ; Repeated slash? + je .mn_skip + xor ah,ah + cmp al,'/' + jne .mn_ok + mov ah,al +.mn_ok stosb +.mn_skip: loop .mn_loop +.mn_end: + cmp bx,di ; At the beginning of the buffer? + jbe .mn_zero + cmp byte [es:di-1],'.' ; Terminal dot? + je .mn_kill + cmp byte [es:di-1],'/' ; Terminal slash? + jne .mn_zero +.mn_kill: dec di ; If so, remove it + inc cx + jmp short .mn_end +.mn_zero: + inc cx ; At least one null byte + xor ax,ax ; Zero-fill name + rep stosb + pop bx + pop di + ret ; Done + +; +; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled +; filename to the conventional representation. This is needed +; for the BOOT_IMAGE= parameter for the kernel. +; +; DS:SI -> input mangled file name +; ES:DI -> output buffer +; +; On return, DI points to the first byte after the output name, +; which is set to a null byte. +; +unmangle_name: call strcpy + dec di ; Point to final null byte + ret + +; +; getfssec: Get multiple clusters from a file, given the file pointer. +; +; On entry: +; ES:BX -> Buffer +; SI -> File pointer +; CX -> Cluster count +; On exit: +; SI -> File pointer (or 0 on EOF) +; CF = 1 -> Hit EOF +; ECX -> Bytes actually read +; +getfssec: + TRACER 'F' + push ds + push cs + pop ds ; DS <- CS + + movzx ecx,cx + cmp ecx,[si+file_left] + jna .ok_size + mov ecx,[si+file_left] +.ok_size: + + pushad + mov eax,[si+file_sector] + mov bp,cx + TRACER 'l' + call getlinsec + popad + + ; ECX[31:16] == 0 here... + add [si+file_sector],ecx + sub [si+file_left],ecx + shl ecx,SECTOR_SHIFT ; Convert to bytes + cmp ecx,[si+file_bytesleft] + jb .not_all + mov ecx,[si+file_bytesleft] +.not_all: sub [si+file_bytesleft],ecx + jnz .ret ; CF = 0 in this case... + push eax + xor eax,eax + mov [si+file_sector],eax ; Unused + mov si,ax + pop eax + stc +.ret: + pop ds + TRACER 'f' + ret + +; ----------------------------------------------------------------------------- +; Common modules +; ----------------------------------------------------------------------------- + +%include "getc.inc" ; getc et al +%include "conio.inc" ; Console I/O +%include "configinit.inc" ; Initialize configuration +%include "parseconfig.inc" ; High-level config file handling +%include "parsecmd.inc" ; Low-level config file handling +%include "bcopy32.inc" ; 32-bit bcopy +%include "loadhigh.inc" ; Load a file into high memory +%include "font.inc" ; VGA font stuff +%include "graphics.inc" ; VGA graphics +%include "highmem.inc" ; High memory sizing +%include "strcpy.inc" ; strcpy() +%include "rawcon.inc" ; Console I/O w/o using the console functions +%include "idle.inc" ; Idle handling +%include "adv.inc" ; Auxillary Data Vector +%include "localboot.inc" ; Disk-based local boot + +; ----------------------------------------------------------------------------- +; Begin data section +; ----------------------------------------------------------------------------- + + section .data + +default_str db 'default', 0 +default_len equ ($-default_str) +boot_dir db '/boot' ; /boot/isolinux +isolinux_dir db '/isolinux', 0 +config_name db 'isolinux.cfg', 0 +err_disk_image db 'Cannot load disk image (invalid file)?', CR, LF, 0 + +%ifdef DEBUG_MESSAGES +dbg_rootdir_msg db 'Root directory at LBA = ', 0 +dbg_isodir_msg db 'isolinux directory at LBA = ', 0 +dbg_config_msg db 'About to load config file...', CR, LF, 0 +dbg_configok_msg db 'Configuration file opened...', CR, LF, 0 +%endif + +; +; Config file keyword table +; +%include "keywords.inc" + +; +; Extensions to search for (in *forward* order). +; + alignz 4 +exten_table: db '.cbt' ; COMBOOT (specific) + db '.img' ; Disk image + db '.bin' ; CD boot sector + db '.com' ; COMBOOT (same as DOS) + db '.c32' ; COM32 +exten_table_end: + dd 0, 0 ; Need 8 null bytes here + +; +; Floppy image table +; + alignz 4 +img_table_count equ 3 +img_table: + dd 1200*1024 ; 1200K floppy + db 1 ; Emulation type + db 80-1 ; Max cylinder + db 15 ; Max sector + db 2-1 ; Max head + + dd 1440*1024 ; 1440K floppy + db 2 ; Emulation type + db 80-1 ; Max cylinder + db 18 ; Max sector + db 2-1 ; Max head + + dd 2880*1024 ; 2880K floppy + db 3 ; Emulation type + db 80-1 ; Max cylinder + db 36 ; Max sector + db 2-1 ; Max head + +; +; Misc initialized (data) variables +; + +; +; Variables that are uninitialized in SYSLINUX but initialized here +; +; **** ISOLINUX:: We may have to make this flexible, based on what the +; **** BIOS expects our "sector size" to be. +; + alignz 4 +BufSafe dw trackbufsize/SECTOR_SIZE ; Clusters we can load into trackbuf +BufSafeBytes dw trackbufsize ; = how many bytes? +%ifndef DEPEND +%if ( trackbufsize % SECTOR_SIZE ) != 0 +%error trackbufsize must be a multiple of SECTOR_SIZE +%endif +%endif diff --git a/core/isolinux.bin b/core/isolinux.bin new file mode 100755 index 0000000000000000000000000000000000000000..80f71c98ff5e0150267f6ad7597f295f3d4b3cdb GIT binary patch literal 14336 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 4) +bs_rootflags resw 1 ; 01F2 Root readonly flag +bs_syssize resw 1 ; 01F4 +bs_swapdev resw 1 ; 01F6 Swap device (obsolete) +bs_ramsize resw 1 ; 01F8 Ramdisk flags, formerly ramdisk size +bs_vidmode resw 1 ; 01FA Video mode +bs_rootdev resw 1 ; 01FC Root device +bs_bootsign resw 1 ; 01FE Boot sector signature (0AA55h) +su_jump resb 1 ; 0200 0EBh +su_jump2 resb 1 ; 0201 Size of following header +su_header resd 1 ; 0202 New setup code: header +su_version resw 1 ; 0206 See linux/arch/i386/boot/setup.S +su_switch resw 1 ; 0208 +su_setupseg resw 1 ; 020A +su_startsys resw 1 ; 020C +su_kver resw 1 ; 020E Kernel version pointer +su_loader resb 1 ; 0210 Loader ID +su_loadflags resb 1 ; 0211 Load high flag +su_movesize resw 1 ; 0212 +su_code32start resd 1 ; 0214 Start of code loaded high +su_ramdiskat resd 1 ; 0218 Start of initial ramdisk +su_ramdisklen resd 1 ; 021C Length of initial ramdisk +su_bsklugeoffs resw 1 ; 0220 +su_bsklugeseg resw 1 ; 0222 +su_heapend resw 1 ; 0224 +su_pad1 resw 1 ; 0226 +su_cmd_line_ptr resd 1 ; 0228 +su_ramdisk_max resd 1 ; 022C + resb (0f800h-12)-($-$$) +linux_stack equ $ ; F7F4 +linux_fdctab resb 12 +cmd_line_here equ $ ; F800 Should be out of the way + endstruc + +; +; Old kernel command line signature +; +CMD_MAGIC equ 0A33Fh ; Command line magic + +; +; If we're loading the command line old-style, we need a smaller +; heap. +; +old_cmd_line_here equ 9800h +old_max_cmd_len equ 2047 +old_linux_fdctab equ old_cmd_line_here-12 +old_linux_stack equ old_linux_fdctab + +; +; Magic number of su_header field +; +HEADER_ID equ 'HdrS' ; HdrS (in littleendian hex) + +; +; Flags for the su_loadflags field +; +LOAD_HIGH equ 01h ; Large kernel, load high +CAN_USE_HEAP equ 80h ; Boot loader reports heap size + +; +; ID codes for various modules +; +syslinux_id equ 031h ; 3 = SYSLINUX family; 1 = SYSLINUX +pxelinux_id equ 032h ; 3 = SYSLINUX family; 2 = PXELINUX +isolinux_id equ 033h ; 3 = SYSLINUX family; 3 = ISOLINUX +extlinux_id equ 034h ; 3 = SYSLINUX family; 4 = EXTLINUX + +; +; Types of vkernels +; +VK_LOCALBOOT equ -1 ; localboot (no actual kernel loaded) +VK_KERNEL equ 0 ; Choose by filename +VK_LINUX equ 1 ; Linux kernel image +VK_BOOT equ 2 ; Boot sector +VK_BSS equ 3 ; BSS boot sector +VK_PXE equ 4 ; PXE NBP +VK_FDIMAGE equ 5 ; Floppy disk image +VK_COMBOOT equ 6 ; COMBOOT image +VK_COM32 equ 7 ; COM32 image +VK_CONFIG equ 8 ; Configuration file +VK_TYPES equ 9 ; Number of VK types + +%endif ; _KERNEL_INC diff --git a/core/keywords b/core/keywords new file mode 100644 index 0000000..c289ae2 --- /dev/null +++ b/core/keywords @@ -0,0 +1,48 @@ +menu +text +include +append +initrd +config +default +ui +display +font +implicit +ipappend +kbdmap +kernel +linux +boot +bss +pxe +fdimage +comboot +com32 +label +localboot +prompt +say +serial +console +timeout +totaltimeout +allowoptions +ontimeout +onerror +noescape +nocomplete +nohalt +f0 +f1 +f2 +f3 +f4 +f5 +f6 +f7 +f8 +f9 +f10 +f11 +f12 diff --git a/core/keywords.inc b/core/keywords.inc new file mode 100644 index 0000000..d0f7db3 --- /dev/null +++ b/core/keywords.inc @@ -0,0 +1,101 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; keywords.inc +;; +;; Common header file for the handling of keyword hash and macros +;; + +%ifndef DEPEND ; Generated file +%include "kwdhash.gen" +%endif + +%macro keyword 2 + dd hash_%1 ; Hash value + dw 0 ; No argument + dw %2 ; Entrypoint +%endmacro + +%macro keyword 3 + dd hash_%1 ; Hash value + dw %3 ; 16-bit argument + dw %2 ; Entrypoint +%endmacro + +%macro keyword 4 + dd hash_%1 ; Hash value + db %3, %4 ; 2 8-bit arguments + dw %2 ; Entrypoint +%endmacro + +keywd_size equ 8 ; Bytes per keyword + + alignz 4 + +%define FKeyN(n) (FKeyName+(((n)-1) << FILENAME_MAX_LG2)) + +keywd_table: + keyword menu, pc_comment + keyword text, pc_text + keyword include, pc_opencmd, pc_include + keyword append, pc_append + keyword initrd, pc_filename, InitRD + keyword default, pc_default, 1 + keyword ui, pc_default, 2 + keyword display, pc_opencmd, get_msg_file + keyword font, pc_opencmd, loadfont + keyword implicit, pc_setint16, AllowImplicit + keyword kbdmap, pc_opencmd, loadkeys + keyword kernel, pc_kernel, VK_KERNEL + keyword linux, pc_kernel, VK_LINUX + keyword boot, pc_kernel, VK_BOOT + keyword bss, pc_kernel, VK_BSS + keyword pxe, pc_kernel, VK_PXE + keyword fdimage, pc_kernel, VK_FDIMAGE + keyword comboot, pc_kernel, VK_COMBOOT + keyword com32, pc_kernel, VK_COM32 + keyword config, pc_kernel, VK_CONFIG + keyword label, pc_label + keyword prompt, pc_setint16, ForcePrompt + keyword say, pc_say + keyword serial, pc_serial + keyword console, pc_setint16, DisplayCon + keyword timeout, pc_timeout, KbdTimeout + keyword totaltimeout, pc_timeout, TotalTimeout + keyword ontimeout, pc_ontimeout + keyword onerror, pc_onerror + keyword allowoptions, pc_setint16, AllowOptions + keyword noescape, pc_setint16, NoEscape + keyword nocomplete, pc_setint16, NoComplete + keyword nohalt, pc_setint16, NoHalt + keyword f1, pc_filename, FKeyN(1) + keyword f2, pc_filename, FKeyN(2) + keyword f3, pc_filename, FKeyN(3) + keyword f4, pc_filename, FKeyN(4) + keyword f5, pc_filename, FKeyN(5) + keyword f6, pc_filename, FKeyN(6) + keyword f7, pc_filename, FKeyN(7) + keyword f8, pc_filename, FKeyN(8) + keyword f9, pc_filename, FKeyN(9) + keyword f10, pc_filename, FKeyN(10) + keyword f0, pc_filename, FKeyN(10) + keyword f11, pc_filename, FKeyN(11) + keyword f12, pc_filename, FKeyN(12) +%if IS_PXELINUX + keyword ipappend, pc_ipappend +%endif +%if HAS_LOCALBOOT + keyword localboot, pc_localboot +%endif + +keywd_count equ ($-keywd_table)/keywd_size diff --git a/core/kwdhash.gen b/core/kwdhash.gen new file mode 100644 index 0000000..0604715 --- /dev/null +++ b/core/kwdhash.gen @@ -0,0 +1,48 @@ +hash_menu equ 0x003719b5 +hash_text equ 0x003b9b74 +hash_include equ 0x9a07d8ff +hash_append equ 0xc53999a4 +hash_initrd equ 0xd4d55e24 +hash_config equ 0xc0c69547 +hash_default equ 0xcc5159ed +hash_ui equ 0x00000ec9 +hash_display equ 0xd509bc40 +hash_font equ 0x0032b1b4 +hash_implicit equ 0xa6f50207 +hash_ipappend equ 0xc5399af0 +hash_kbdmap equ 0xd013b850 +hash_kernel equ 0xd068b4cc +hash_linux equ 0x06f536d8 +hash_boot equ 0x0030b194 +hash_bss equ 0x00018613 +hash_pxe equ 0x0001cf65 +hash_fdimage equ 0x4ea7089c +hash_comboot equ 0x18e0b18c +hash_com32 equ 0x06063252 +hash_label equ 0x06f104cc +hash_localboot equ 0x04f0def4 +hash_prompt equ 0xe7163a74 +hash_say equ 0x0001c059 +hash_serial equ 0xe068a84c +hash_console equ 0x18d831fd +hash_timeout equ 0xd4e332c9 +hash_totaltimeout equ 0xef51d0a9 +hash_allowoptions equ 0x1648dd10 +hash_ontimeout equ 0xd4e35eb9 +hash_onerror equ 0x1a68c589 +hash_noescape equ 0x0d00090e +hash_nocomplete equ 0x1d3a36e6 +hash_nohalt equ 0xdac589f4 +hash_f0 equ 0x00000cf0 +hash_f1 equ 0x00000cf1 +hash_f2 equ 0x00000cf2 +hash_f3 equ 0x00000cf3 +hash_f4 equ 0x00000cf4 +hash_f5 equ 0x00000cf5 +hash_f6 equ 0x00000cf6 +hash_f7 equ 0x00000cf7 +hash_f8 equ 0x00000cf8 +hash_f9 equ 0x00000cf9 +hash_f10 equ 0x00019e10 +hash_f11 equ 0x00019e11 +hash_f12 equ 0x00019e12 diff --git a/core/layout.inc b/core/layout.inc new file mode 100644 index 0000000..8c2e248 --- /dev/null +++ b/core/layout.inc @@ -0,0 +1,126 @@ +; ----------------------------------------------------------------------- +; +; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved +; Copyright 2009 Intel Corporation; author: H. Peter Anvin +; +; 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, Inc., 53 Temple Place Ste 330, +; Bostom MA 02111-1307, USA; either version 2 of the License, or +; (at your option) any later version; incorporated herein by reference. +; +; ----------------------------------------------------------------------- + +; +; layout.inc +; +; Memory layout of segments +; + + ; Default to 16-bit code + bits 16 + +; Memory below 0800h is reserved for the BIOS and the MBR. +BSS_START equ 0800h + +; Text starts at the load address of 07C00h. +TEXT_START equ 7C00h + +; The secondary BSS section, above the text; we really wish we could +; just make it follow .bcopy32 or hang off the end, +; but it doesn't seem to work that way. +LATEBSS_START equ 0B800h + +; +; The various sections and their relationship +; + ; Use .earlybss for things that MUST be in low memory. + section .earlybss nobits + section .config write progbits align=4 + section .replacestub exec write progbits align=16 + + ; Use .bss for things that doesn't have to be in low memory; + ; with .bss1 and .bss2 to offload. .earlybss should be used + ; for things that absolutely have to be below 0x7c00. + section .bss write nobits align=16 + +%if 0 ; IS_PXELINUX + ; Warning here: RBFG build 22 randomly overwrites + ; memory location [0x5680,0x576c), possibly more. It + ; seems that it gets confused and screws up the + ; pointer to its own internal packet buffer and starts + ; writing a received ARP packet into low memory. + section .rbfg write nobits +RBFG_brainfuck: resb 2048 ; Bigger than an Ethernet packet... +%endif + + section .bss2 write nobits align=16 + + section .text exec write progbits align=16 + section .bcopyxx exec write progbits align=16 + section .data write progbits align=16 + + section .adv write nobits align=512 + + ; .uibss contains bss data which is guaranteed to be + ; safe to clobber during the loading of the image. This + ; is because while loading the primary image we will clobber + ; the spillover from the last fractional sector load. + section .uibss write nobits align=16 + + ; Normal bss... + section .bss1 write nobits align=16 + + ; Symbols from linker script +%macro SECINFO 1 + extern __%1_start, __%1_lma, __%1_end + extern __%1_len, __%1_dwords +%endmacro + SECINFO config + SECINFO replacestub + + global _start + + section .text + +; +; Segment assignments in the bottom 640K +; Keep the low-memory footprint as small as possible... overrun is a hard +; failure! +; +; 0000h - main code/data segment (and BIOS segment) + +xfer_buf_seg equ 1000h +aux_seg equ 2000h + +serial_buf_size equ 4096 ; Should be a power of 2 + +; +; Contents of aux_seg +; + struc aux +.fontbuf resb 8192 +.serial resb serial_buf_size + + alignb 4096 ; Align the next segment to 4K + endstruc + +aux_seg_end equ aux_seg + (aux_size >> 4) + +; +; Bounce buffer for I/O to high mem +; Note: we keep all the segments page-aligned, even if that probably +; is somewhat excessive. Sector alignment is obligatory, however. +; + +%if IS_ISOLINUX +; ISOLINUX doesn't have a block cache yet +real_mode_seg equ aux_seg_end +%else +cache_seg equ aux_seg_end ; 64K area for metadata cache +real_mode_seg equ cache_seg + 1000h + +pktbuf_seg equ cache_seg ; PXELINUX packet buffers +%endif + +comboot_seg equ real_mode_seg ; COMBOOT image loading zone diff --git a/core/ldlinux.asm b/core/ldlinux.asm new file mode 100644 index 0000000..ba7e804 --- /dev/null +++ b/core/ldlinux.asm @@ -0,0 +1,1427 @@ +; -*- fundamental -*- (asm-mode sucks) +; **************************************************************************** +; +; ldlinux.asm +; +; A program to boot Linux kernels off an MS-DOS formatted floppy disk. This +; functionality is good to have for installation floppies, where it may +; be hard to find a functional Linux system to run LILO off. +; +; This program allows manipulation of the disk to take place entirely +; from MS-LOSS, and can be especially useful in conjunction with the +; umsdos filesystem. +; +; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved +; Copyright 2009 Intel Corporation; author: H. Peter Anvin +; +; 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, Inc., 53 Temple Place Ste 330, +; Boston MA 02111-1307, USA; either version 2 of the License, or +; (at your option) any later version; incorporated herein by reference. +; +; **************************************************************************** + +%ifndef IS_MDSLINUX +%define IS_SYSLINUX 1 +%endif +%include "head.inc" + +; +; Some semi-configurable constants... change on your own risk. +; +my_id equ syslinux_id +FILENAME_MAX_LG2 equ 6 ; log2(Max filename size Including final null) +FILENAME_MAX equ (1< 0x0000 +.vfat_end: + and ax,ax + jnz .not_us_pop +.ucs_ok: + pop bx + inc bx + cmp cx,3 + je .vfat_adj_add2 + cmp cx,9 + jne .vfat_adj_add0 +.vfat_adj_add3: inc si +.vfat_adj_add2: inc si +.vfat_adj_add1: inc si +.vfat_adj_add0: + loop .vfat_cmp + ; Okay, if we got here we had a match on this particular + ; entry... live to see another one. + popa + jmp .next_entry + +.not_us_pop: + pop bx +.not_us: + popa + jmp .nomatch + +.short_entry: + test byte [gs:si+11],8 ; Ignore volume labels + jnz .nomatch + + cmp byte [VFATNext],0 ; Do we have a longname match? + jne .no_long_match + + ; We already have a VFAT longname match, however, + ; the match is only valid if the checksum matches + push cx + push si + push ax + xor ax,ax + mov cx,11 +.csum_loop: + gs lodsb + ror ah,1 + add ah,al + loop .csum_loop + cmp ah,[VFATCsum] + pop ax + pop si + pop cx + je .found ; Got a match on longname + +.no_long_match: ; Look for a shortname match + push cx + push si + push di + mov di,MangledBuf + mov cx,11 + gs repe cmpsb + pop di + pop si + pop cx + je .found +.nomatch: + ; Reset the VFAT matching state machine + mov dh,[VFATInit] + mov [VFATNext],dh +.next_entry: + add si,32 + dec cx + jnz .scanentry + + call nextsector + jnc .scansector ; CF is set if we're at end + + ; If we get here, we failed +.failure: + pop es + pop gs + pop cx +.alloc_failure: + pop bx + xor eax,eax ; ZF <- 1 + ret +.found: + mov eax,[gs:si+28] ; File size + add eax,SECTOR_SIZE-1 + shr eax,SECTOR_SHIFT + mov [bx+4],eax ; Sector count + + mov cl,[ClustShift] + mov dx,[gs:si+20] ; High cluster word + shl edx,16 + mov dx,[gs:si+26] ; Low cluster word + sub edx,2 + shl edx,cl + add edx,[DataArea] + mov [bx],edx ; Starting sector + + mov eax,[gs:si+28] ; File length again + mov dl,[gs:si+11] ; File attribute + mov si,bx ; File pointer... + and si,si ; ZF <- 0 + + pop es + pop gs + pop cx + pop bx + ret + + section .data + alignz 4 + ; Note: we have no use of the first 32 bytes (header), + ; nor of the folloing 32 bytes (case mapping of control + ; characters), as long as we adjust the offsets appropriately. +codepage equ $-(32+32) +codepage_data: incbin "codepage.cp",32+32 +cp_uppercase equ codepage+cp.uppercase +cp_unicode equ codepage+cp.unicode +cp_unicode_alt equ codepage+cp.unicode_alt +codepage_end equ $ + + section .text +; +; Input: UCS-2 character in AX +; Output: Single byte character in AL, ZF = 1 +; On failure, returns ZF = 0 +; +ucs2_to_cp: + push es + push di + push cx + push cs + pop es + mov di,cp_unicode + mov cx,512 + repne scasw + xchg ax,cx + pop cx + pop di + pop es + not ax ; Doesn't change the flags! + ret + + section .bss +VFATInit resb 1 +VFATNext resb 1 +VFATCsum resb 1 + + section .text +; +; close_file: +; Deallocates a file structure (pointer in SI) +; Assumes CS == DS. +; +close_file: + and si,si + jz .closed + mov dword [si],0 ; First dword == file_sector + xor si,si +.closed: ret + +; +; close_dir: +; Deallocates a directory structure (pointer in SI) +; Assumes CS == DS. +; +close_dir: + and si,si + jz .closed + mov dword [si],0 ; First dword == file_sector + xor si,si +.closed: ret + +; +; searchdir: +; +; Open a file +; +; On entry: +; DS:DI = filename +; If successful: +; ZF clear +; SI = file pointer +; EAX = file length in bytes +; If unsuccessful +; ZF set +; +; Assumes CS == DS == ES, and trashes BX and CX. +; +searchdir: + mov eax,[CurrentDir] + cmp byte [di],'/' ; Root directory? + jne .notroot + mov eax,[RootDir] + inc di +.notroot: + +.pathwalk: + push eax ; Current directory sector + mov si,di +.findend: + lodsb + cmp al,' ' + jbe .endpath + cmp al,'/' + jne .findend +.endpath: + xchg si,di ; GRC: si begin; di end[ /]+1 + pop eax ; Current directory sector + + ; GRC Here I need to check if di-1 = si which signifies + ; we have the desired directory in EAX + ; What about where the file name = "."; later + mov dx,di + dec dx + cmp dx,si + jz .founddir + + mov [PrevDir],eax ; Remember last directory searched + + push di + call mangle_dos_name ; MangledBuf <- component + call search_dos_dir + pop di + jz .notfound ; Pathname component missing + + cmp byte [di-1],'/' ; Do we expect a directory + je .isdir + + ; Otherwise, it should be a file +.isfile: + test dl,18h ; Subdirectory|Volume Label + jnz .badfile ; If not a file, it's a bad thing + + ; SI and EAX are already set + mov [si+file_bytesleft],eax + push eax + add eax,SECTOR_SIZE-1 + shr eax,SECTOR_SHIFT + mov [si+file_left],eax ; Sectors left + pop eax + and eax,eax ; EAX != 0 + jz .badfile + ret ; Done! + + ; If we expected a directory, it better be one... +.isdir: + test dl,10h ; Subdirectory + jz .badfile + + xor eax,eax + xchg eax,[si+file_sector] ; Get sector number and free file structure + jmp .pathwalk ; Walk the next bit of the path + + ; Found the desired directory; ZF set but EAX not 0 +.founddir: + ret + +.badfile: + xor eax,eax + mov [si],eax ; Free file structure + +.notfound: + xor eax,eax ; Zero out EAX + ret + +; +; readdir: Read one file from a directory +; +; ES:DI -> String buffer (filename) +; DS:SI -> Pointer to open_file_t +; DS Must be the SYSLINUX Data Segment +; +; Returns the file's name in the filename string buffer +; EAX returns the file size +; EBX returns the beginning sector (currently without offsetting) +; DL returns the file type +; The directory handle's data is incremented to reflect a name read. +; +readdir: + push ecx + push bp ; Using bp to transfer between segment registers + push si + push es + push fs ; Using fs to store the current es (from COMBOOT) + push gs + mov bp,es + mov fs,bp + cmp si,0 + jz .fail +.load_handle: + mov eax,[ds:si+file_sector] ; Current sector + mov ebx,[ds:si+file_bytesleft] ; Current offset + cmp eax,0 + jz .fail +.fetch_cache: + call getcachesector +.move_current: + add si,bx ; Resume last position in sector + mov ecx,SECTOR_SIZE ; 0 out high part + sub cx,bx + shr cx,5 ; Number of entries left +.scanentry: + cmp byte [gs:si],0 + jz .fail + cmp word [gs:si+11],0Fh ; Long filename + jne .short_entry + +.vfat_entry: + push eax + push ecx + push si + push di +.vfat_ln_info: ; Get info about the line that we're on + mov al,[gs:si] + test al,40h + jz .vfat_tail_ln + and al,03Fh + mov ah,1 ; On beginning line + jmp .vfat_ck_ln + +.vfat_tail_ln: ; VFAT tail line processing (later in VFAT, head in name) + test al,80h ; Invalid data? + jnz .vfat_abort + mov ah,0 ; Not on beginning line + cmp dl,al + jne .vfat_abort ; Is this the entry we need? + mov bl,[gs:si+13] + cmp bl,[VFATCsum] + je .vfat_cp_ln + jmp .vfat_abort + +.vfat_ck_ln: ; Load this line's VFAT CheckSum + mov bl,[gs:si+13] + mov [VFATCsum],bl +.vfat_cp_ln: ; Copy VFAT line + dec al ; Store the next line we need + mov dx,ax ; Use DX to store the progress + mov cx,13 ; 13 characters per VFAT DIRENT + cbw ; AH <- 0 + mul cl ; Offset for DI + add di,ax ; Increment DI + inc si ; Align to the real characters +.vfat_cp_chr: + gs lodsw ; Unicode here!! + call ucs2_to_cp ; Convert to local codepage + jnz .vfat_abort ; Use short name if character not on codepage + stosb ; CAN NOT OVERRIDE es + cmp al,0 + jz .vfat_find_next ; Null-terminated string; don't process more + cmp cx,3 + je .vfat_adj_add2 + cmp cx,9 + jne .vfat_adj_add0 +.vfat_adj_add3: inc si +.vfat_adj_add2: inc si +.vfat_adj_add1: inc si +.vfat_adj_add0: + loop .vfat_cp_chr + cmp dh,1 ; Is this the first round? + jnz .vfat_find_next +.vfat_null_term: ; Need to null-terminate if first line as we rolled over the end + mov al,0 + stosb + +.vfat_find_next: ;Find the next part of the name + pop di + pop si + pop ecx + pop eax + cmp dl,0 + jz .vfat_find_info ; We're done with the name + add si,DIRENT_SIZE + dec cx + jnz .vfat_entry + call nextsector + jnc .vfat_entry ; CF is set if we're at end + jmp .fail +.vfat_find_info: ; Fetch next entry for the size/"INode" + add si,DIRENT_SIZE + dec cx + jnz .get_info + call nextsector + jnc .get_info ; CF is set if we're at end + jmp .fail +.vfat_abort: ; Something went wrong, skip + pop di + pop si + pop ecx + pop eax + jmp .skip_entry + +.short_entry: + test byte [gs:si+11],8 ; Ignore volume labels //HERE + jnz .skip_entry + mov edx,eax ;Save current sector + push cx + push si + push di + mov cx,8 +.short_file: + gs lodsb + cmp al,'.' + jz .short_dot +.short_file_loop: + cmp al,' ' + jz .short_skip_bs + stosb + loop .short_file_loop + jmp .short_period +.short_skip_bs: ; skip blank spaces in FILENAME (before EXT) + add si,cx + dec si +.short_period: + mov al,'.' + stosb + mov cx,3 +.short_ext: + gs lodsb + cmp al,' ' + jz .short_done + stosb + loop .short_ext + jmp .short_done +.short_dot: + stosb + gs lodsb + cmp al,' ' + jz .short_done + stosb +.short_done: + mov al,0 ; Null-terminate the short strings + stosb + pop di + pop si + pop cx + mov eax,edx +.get_info: + mov ebx,[gs:si+28] ; length + mov dl,[gs:si+11] ; type +.next_entry: + add si,DIRENT_SIZE + dec cx + jnz .store_offset + call nextsector + jnc .store_sect ; CF is set if we're at end + jmp .fail + +.skip_entry: + add si,DIRENT_SIZE + dec cx + jnz .scanentry + call nextsector + jnc .scanentry ; CF is set if we're at end + jmp .fail + +.store_sect: + pop gs + pop fs + pop es + pop si + mov [ds:si+file_sector],eax + mov eax,0 ; Now at beginning of new sector + jmp .success + +.store_offset: + pop gs + pop fs + pop es + pop si ; cx=num remain; SECTOR_SIZE-(cx*32)=cur pos + shl ecx,DIRENT_SHIFT + mov eax,SECTOR_SIZE + sub eax,ecx + and eax,0ffffh + +.success: + mov [ds:si+file_bytesleft],eax + ; "INode" number = ((CurSector-RootSector)*SECTOR_SIZE + Offset)/DIRENT_SIZE) + mov ecx,eax + mov eax,[ds:si+file_sector] + sub eax,[RootDir] + shl eax,SECTOR_SHIFT + add eax,ecx + shr eax,DIRENT_SHIFT + dec eax + xchg eax,ebx ; -> EBX=INode, EAX=FileSize + jmp .done + +.fail: + pop gs + pop fs + pop es + pop si + call close_dir + xor eax,eax + stc +.done: + pop bp + pop ecx +.end: + ret + + section .bss + alignb 4 +CurrentDir resd 1 ; Current directory +PrevDir resd 1 ; Last scanned directory + + section .text + +; +; +; kaboom2: once everything is loaded, replace the part of kaboom +; starting with "kaboom.patch" with this part + +kaboom2: + mov si,err_bootfailed + call writestr + cmp byte [kaboom.again+1],18h ; INT 18h version? + je .int18 + call getchar + call vgaclearmode + int 19h ; And try once more to boot... +.norge: jmp short .norge ; If int 19h returned; this is the end +.int18: + call vgaclearmode + int 18h +.noreg: jmp short .noreg ; Nynorsk + +; +; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed +; to by ES:DI; ends on encountering any whitespace. +; DI is preserved. +; +; This verifies that a filename is < FILENAME_MAX characters, +; doesn't contain whitespace, zero-pads the output buffer, +; and removes trailing dots and redundant slashes, plus changes +; backslashes to forward slashes, +; so "repe cmpsb" can do a compare, and the path-searching routine +; gets a bit of an easier job. +; +; +mangle_name: + push di + push bx + xor ax,ax + mov cx,FILENAME_MAX-1 + mov bx,di + +.mn_loop: + lodsb + cmp al,' ' ; If control or space, end + jna .mn_end + cmp al,'\' ; Backslash? + jne .mn_not_bs + mov al,'/' ; Change to forward slash +.mn_not_bs: + cmp al,ah ; Repeated slash? + je .mn_skip + xor ah,ah + cmp al,'/' + jne .mn_ok + mov ah,al +.mn_ok stosb +.mn_skip: loop .mn_loop +.mn_end: + cmp bx,di ; At the beginning of the buffer? + jbe .mn_zero + cmp byte [es:di-1],'.' ; Terminal dot? + je .mn_kill + cmp byte [es:di-1],'/' ; Terminal slash? + jne .mn_zero +.mn_kill: dec di ; If so, remove it + inc cx + jmp short .mn_end +.mn_zero: + inc cx ; At least one null byte + xor ax,ax ; Zero-fill name + rep stosb + pop bx + pop di + ret ; Done + +; +; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled +; filename to the conventional representation. This is needed +; for the BOOT_IMAGE= parameter for the kernel. +; NOTE: A 13-byte buffer is mandatory, even if the string is +; known to be shorter. +; +; DS:SI -> input mangled file name +; ES:DI -> output buffer +; +; On return, DI points to the first byte after the output name, +; which is set to a null byte. +; +unmangle_name: call strcpy + dec di ; Point to final null byte + ret + +; +; mangle_dos_name: +; Mangle a DOS filename component pointed to by DS:SI +; into [MangledBuf]; ends on encountering any whitespace or +; slash. +; +; WARNING: saves pointers into the buffer for longname +; matches! +; +; Assumes CS == DS == ES. +; + +mangle_dos_name: + pusha + mov di,MangledBuf + mov [NameStart],si + + mov cx,11 ; # of bytes to write + mov bx,cp_uppercase ; Case-conversion table +.loop: + lodsb + cmp al,' ' ; If control or space, end + jna .end + cmp al,'/' ; Slash, too + je .end + cmp al,'.' ; Period -> space-fill + je .is_period + xlatb ; Convert to upper case + mov ah,cl ; If the first byte (only!)... + cmp ax,0BE5h ; ... equals E5 hex ... + jne .charok + mov al,05h ; ... change it to 05 hex +.charok: stosb + loop .loop ; Don't continue if too long + ; Find the end for the benefit of longname search +.find_end: + lodsb + cmp al,' ' + jna .end + cmp al,'/' + jne .find_end +.end: + dec si + sub si,[NameStart] + mov [NameLen],si + mov al,' ' ; Space-fill name + rep stosb ; Doesn't do anything if CX=0 + popa + ret ; Done + +.is_period: + mov al,' ' ; We need to space-fill +.period_loop: cmp cx,3 ; If <= 3 characters left + jbe .loop ; Just ignore it + stosb ; Otherwise, write a space + loop .period_loop ; Dec CX and *always* jump + + section .bss + alignb 2 +NameStart resw 1 +NameLen resw 1 +MangledBuf resb 11 + + section .text +; +; getfssec_edx: Get multiple sectors from a file +; +; This routine makes sure the subtransfers do not cross a 64K boundary, +; and will correct the situation if it does, UNLESS *sectors* cross +; 64K boundaries. +; +; ES:BX -> Buffer +; EDX -> Current sector number +; CX -> Sector count (0FFFFh = until end of file) +; Must not exceed the ES segment +; Returns EDX=0, CF=1 on EOF (not necessarily error) +; All arguments are advanced to reflect data read. +; +getfssec_edx: + push ebp + push eax +.getfragment: + xor ebp,ebp ; Fragment sector count + push edx ; Starting sector pointer +.getseccnt: + inc bp + dec cx + jz .do_read + xor eax,eax + mov ax,es + shl ax,4 + add ax,bx ; Now AX = how far into 64K block we are + not ax ; Bytes left in 64K block + inc eax + shr eax,SECTOR_SHIFT ; Sectors left in 64K block + cmp bp,ax + jnb .do_read ; Unless there is at least 1 more sector room... + mov eax,edx ; Current sector + inc edx ; Predict it's the linearly next sector + call nextsector + jc .do_read + cmp edx,eax ; Did it match? + jz .getseccnt +.do_read: + pop eax ; Starting sector pointer + call getlinsecsr + lea eax,[eax+ebp-1] ; This is the last sector actually read + shl bp,9 + add bx,bp ; Adjust buffer pointer + call nextsector + jc .eof + mov edx,eax + and cx,cx + jnz .getfragment +.done: + pop eax + pop ebp + ret +.eof: + xor edx,edx + stc + jmp .done + +; +; getfssec: Get multiple sectors from a file +; +; Same as above, except SI is a pointer to a open_file_t +; +; ES:BX -> Buffer +; DS:SI -> Pointer to open_file_t +; CX -> Sector count (0FFFFh = until end of file) +; Must not exceed the ES segment +; Returns CF=1 on EOF (not necessarily error) +; ECX returns number of bytes read. +; All arguments are advanced to reflect data read. +; +getfssec: + push edx + movzx edx,cx + push edx ; Zero-extended CX + cmp edx,[si+file_left] + jbe .sizeok + mov edx,[si+file_left] + mov cx,dx +.sizeok: + sub [si+file_left],edx + mov edx,[si+file_sector] + call getfssec_edx + mov [si+file_sector],edx + pop ecx ; Sectors requested read + shl ecx,SECTOR_SHIFT + cmp ecx,[si+file_bytesleft] + ja .eof +.noteof: + sub [si+file_bytesleft],ecx ; CF <- 0 + pop edx + ret +.eof: + mov ecx,[si+file_bytesleft] + call close_file + pop edx + stc + ret + +; +; nextcluster: Advance a cluster pointer in EDI to the next cluster +; pointed at in the FAT tables. CF=0 on return if end of file. +; +nextcluster: + jmp strict short nextcluster_fat28 ; This gets patched + +nextcluster_fat12: + push eax + push edx + push bx + push cx + push si + mov edx,edi + shr edi,1 + pushf ; Save the shifted-out LSB (=CF) + add edx,edi + mov eax,edx + shr eax,9 + call getfatsector + mov bx,dx + and bx,1FFh + mov cl,[gs:si+bx] + inc edx + mov eax,edx + shr eax,9 + call getfatsector + mov bx,dx + and bx,1FFh + mov ch,[gs:si+bx] + popf + jnc .even + shr cx,4 +.even: and cx,0FFFh + movzx edi,cx + cmp di,0FF0h + pop si + pop cx + pop bx + pop edx + pop eax + ret + +; +; FAT16 decoding routine. +; +nextcluster_fat16: + push eax + push si + push bx + mov eax,edi + shr eax,SECTOR_SHIFT-1 + call getfatsector + mov bx,di + add bx,bx + and bx,1FEh + movzx edi,word [gs:si+bx] + cmp di,0FFF0h + pop bx + pop si + pop eax + ret +; +; FAT28 ("FAT32") decoding routine. +; +nextcluster_fat28: + push eax + push si + push bx + mov eax,edi + shr eax,SECTOR_SHIFT-2 + call getfatsector + mov bx,di + add bx,bx + add bx,bx + and bx,1FCh + mov edi,dword [gs:si+bx] + and edi,0FFFFFFFh ; 28 bits only + cmp edi,0FFFFFF0h + pop bx + pop si + pop eax + ret + +; +; nextsector: Given a sector in EAX on input, return the next sector +; of the same filesystem object, which may be the root +; directory or a cluster chain. Returns EOF. +; +; Assumes CS == DS. +; +nextsector: + push edi + push edx + mov edx,[DataArea] + mov edi,eax + sub edi,edx + jae .isdata + + ; Root directory + inc eax + cmp eax,edx + cmc + jmp .done + +.isdata: + not edi + test edi,[ClustMask] + jz .endcluster + + ; It's not the final sector in a cluster + inc eax + jmp .done + +.endcluster: + push gs ; nextcluster trashes gs + push cx + not edi + mov cl,[ClustShift] + shr edi,cl + add edi,2 + + ; Now EDI contains the cluster number + call nextcluster + cmc + jc .exit ; There isn't anything else... + + ; New cluster number now in EDI + sub edi,2 + shl edi,cl ; CF <- 0, unless something is very wrong + lea eax,[edi+edx] +.exit: + pop cx + pop gs +.done: + pop edx + pop edi + ret + +; +; getfatsector: Check for a particular sector (in EAX) in the FAT cache, +; and return a pointer in GS:SI, loading it if needed. +; +; Assumes CS == DS. +; +getfatsector: + add eax,[FAT] ; FAT starting address + jmp getcachesector + +; ----------------------------------------------------------------------------- +; Common modules +; ----------------------------------------------------------------------------- + +%include "getc.inc" ; getc et al +%include "conio.inc" ; Console I/O +%include "plaincon.inc" ; writechr +%include "writestr.inc" ; String output +%include "writehex.inc" ; Hexadecimal output +%include "configinit.inc" ; Initialize configuration +%include "parseconfig.inc" ; High-level config file handling +%include "parsecmd.inc" ; Low-level config file handling +%include "bcopy32.inc" ; 32-bit bcopy +%include "loadhigh.inc" ; Load a file into high memory +%include "font.inc" ; VGA font stuff +%include "graphics.inc" ; VGA graphics +%include "highmem.inc" ; High memory sizing +%include "strcpy.inc" ; strcpy() +%include "cache.inc" ; Metadata disk cache +%include "idle.inc" ; Idle handling +%include "adv.inc" ; Auxillary Data Vector +%include "localboot.inc" ; Disk-based local boot + +; ----------------------------------------------------------------------------- +; Begin data section +; ----------------------------------------------------------------------------- + + section .data +copyright_str db ' Copyright (C) 1994-' + asciidec YEAR + db ' H. Peter Anvin et al', CR, LF, 0 +err_bootfailed db CR, LF, 'Boot failed: please change disks and press ' + db 'a key to continue.', CR, LF, 0 +syslinux_cfg1 db '/boot' ; /boot/syslinux/syslinux.cfg +syslinux_cfg2 db '/syslinux' ; /syslinux/syslinux.cfg +syslinux_cfg3 db '/' ; /syslinux.cfg +config_name db 'syslinux.cfg', 0 ; syslinux.cfg + +; +; Config file keyword table +; +%include "keywords.inc" + +; +; Extensions to search for (in *forward* order). +; +exten_table: db '.cbt' ; COMBOOT (specific) + db '.bss' ; Boot Sector (add superblock) + db '.bs', 0 ; Boot Sector + db '.com' ; COMBOOT (same as DOS) + db '.c32' ; COM32 +exten_table_end: + dd 0, 0 ; Need 8 null bytes here + +; +; Misc initialized (data) variables +; +%ifdef debug ; This code for debugging only +debug_magic dw 0D00Dh ; Debug code sentinel +%endif + + alignz 4 +BufSafe dw trackbufsize/SECTOR_SIZE ; Clusters we can load into trackbuf +BufSafeBytes dw trackbufsize ; = how many bytes? +%ifndef DEPEND +%if ( trackbufsize % SECTOR_SIZE ) != 0 +%error trackbufsize must be a multiple of SECTOR_SIZE +%endif +%endif diff --git a/core/ldlinux.bin b/core/ldlinux.bin new file mode 100755 index 0000000000000000000000000000000000000000..10acec81ba12eee34c554fb151d87aa9ab3b6376 GIT binary patch literal 15430 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%0Dgzg~f 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 literal 0 HcmV?d00001 diff --git a/core/ldlinux.bss b/core/ldlinux.bss new file mode 100644 index 0000000000000000000000000000000000000000..29f76794ca267049dc991a7babe8b349d22d70dd GIT binary patch literal 512 zcmaDIF(EiI*vHc^G=hP;;MX6+1AQ0vY^i1o?>n%QgJJ)c>d#C2ZtSjL;NN1-zaXqb zOi@GOQibS7wody_cc#xNOS||aYC3#>oE3hp{>}A6eJMv;x9g9z#WFQpIL-LnDs;2p&eJ4=$@7=3Hw>WkIO#&(qX#5?zs+8;7LBdYzED9mS4-5I=>!x!sf{Fu;XaQHIM;Q zw=nGmTAy%MI4!YAJdJ;M1& literal 0 HcmV?d00001 diff --git a/core/ldlinux.sys b/core/ldlinux.sys new file mode 100644 index 0000000000000000000000000000000000000000..a341b0db60fb9b5e9a36de661fb39ac38a9bd1cf GIT binary patch literal 14918 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 ZCrK1S5L_&2#iw0GuGiu;IN|c={|~VEO_2Zq literal 0 HcmV?d00001 diff --git a/core/loadhigh.inc b/core/loadhigh.inc new file mode 100644 index 0000000..8ff9da1 --- /dev/null +++ b/core/loadhigh.inc @@ -0,0 +1,114 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved +;; Copyright 2009 Intel Corporation; author: H. Peter Anvin +;; +;; 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; loadhigh.inc +;; +;; Load a file into high memory +;; + + section .text + +; +; load_high: loads (the remainder of) a file into high memory. +; This routine prints dots for each 64K transferred, and +; calls abort_check periodically. +; +; The xfer_buf_seg is used as a bounce buffer. +; +; Assumes CS == DS. +; +; The input address (EDI) should be dword aligned, and the final +; stretch is padded with zeroes if necessary. +; +; Inputs: SI = file handle/cluster pointer +; EDI = target address in high memory +; EAX = maximum number of bytes to load +; DX = zero-padding mask (e.g. 0003h for pad to dword) +; BX = subroutine to call at the top of each loop +; (to print status and check for abort) +; MyHighMemSize = maximum load address +; +; Outputs: SI = file handle/cluster pointer +; EBX = first untouched address (not including padding) +; EDI = first untouched address (including padding) +; CF = reached EOF +; +load_high: + push es ; ES + + mov cx,xfer_buf_seg + mov es,cx + mov [PauseBird],bx + +.read_loop: + and si,si ; If SI == 0 then we have end of file + jz .eof + call [PauseBird] + + push eax ; Total bytes to transfer + cmp eax,(1 << 16) ; Max 64K in one transfer + jna .size_ok + mov eax,(1 << 16) +.size_ok: + push eax ; Bytes transferred this chunk + add eax,SECTOR_SIZE-1 + shr eax,SECTOR_SHIFT ; Convert to sectors + + ; Now (e)ax contains the number of sectors to get + push edi ; Target buffer + mov cx,ax + xor bx,bx ; ES:0 + call getfssec ; Load the data into xfer_buf_seg + pop edi ; Target buffer + pushf ; EOF status + lea ebx,[edi+ecx] ; End of data +.fix_slop: + test cx,dx + jz .noslop + ; The last dword fractional - pad with zeroes + ; Zero-padding is critical for multi-file initramfs. + mov byte [es:ecx],0 + inc cx + jmp short .fix_slop +.noslop: + lea eax,[edi+ecx] + cmp eax,[MyHighMemSize] + ja .overflow + + push esi ; File handle/cluster pointer + mov esi,(xfer_buf_seg << 4) ; Source address + call bcopy ; Copy to high memory + pop esi ; File handle/cluster pointer + popf ; EOF status + pop ecx ; Byte count this round + pop eax ; Total bytes to transfer + jc .eof + sub eax,ecx + jnz .read_loop ; More to read... (if ZF=1 then CF=0) +.eof: + pop es ; ES + ret + +.overflow: mov si,err_nohighmem + jmp abort_load + + section .data +err_nohighmem db CR, LF + db 'Not enough memory to load specified image.', CR, LF, 0 + + section .bss + alignb 2 +PauseBird resw 1 + + section .text diff --git a/core/localboot.inc b/core/localboot.inc new file mode 100644 index 0000000..ae54737 --- /dev/null +++ b/core/localboot.inc @@ -0,0 +1,75 @@ +; ----------------------------------------------------------------------- +; +; Copyright 1999-2008 H. Peter Anvin - 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, Inc., 51 Franklin St, Fifth Floor, +; Boston MA 02110-1301, USA; either version 2 of the License, or +; (at your option) any later version; incorporated herein by reference. +; +; ----------------------------------------------------------------------- + +; +; localboot.inc +; +; Boot from a local disk, or invoke INT 18h. +; + +%if HAS_LOCALBOOT + +; +; Boot a specified local disk. AX specifies the BIOS disk number; or +; -1 in case we should execute INT 18h ("next device.") +; + section .text + +local_boot: + call vgaclearmode + RESET_STACK_AND_SEGS dx ; dx <- 0 + mov fs,dx + mov gs,dx + mov si,localboot_msg + call writestr + cmp ax,-1 + je .int18 + + ; Load boot sector from the specified BIOS device and jump to it. + mov dl,al + xor dh,dh + push dx + xor ax,ax ; Reset drive + int 13h + mov ax,0201h ; Read one sector + mov cx,0001h ; C/H/S = 0/0/1 (first sector) + mov bx,trackbuf + mov bp,retry_count +.again: + pusha + int 13h + popa + jnc .ok + dec bp + jnz .again + jmp kaboom ; Failure... +.ok: + pop dx + cli ; Abandon hope, ye who enter here + mov si,trackbuf + mov di,07C00h + mov cx,512 ; Probably overkill, but should be safe + rep movsd + mov ss,cx ; SS <- 0 + mov sp,7C00h + jmp 0:07C00h ; Jump to new boot sector + +.int18: + int 18h ; Hope this does the right thing... + jmp kaboom ; If we returned, oh boy... + + section .data +localboot_msg db 'Booting from local disk...', CR, LF, 0 + + section .text + +%endif ; HAS_LOCALBOOT diff --git a/core/lstadjust.pl b/core/lstadjust.pl new file mode 100755 index 0000000..cec5402 --- /dev/null +++ b/core/lstadjust.pl @@ -0,0 +1,57 @@ +#!/usr/bin/perl +# +# Take a NASM list and map file and make the offsets in the list file +# absolute. This makes debugging a lot easier. +# +# Usage: +# +# lstadjust.pl listfile mapfile outfile +# + +($listfile, $mapfile, $outfile) = @ARGV; + +open(LST, "< $listfile\0") + or die "$0: cannot open: $listfile: $!\n"; +open(MAP, "< $mapfile\0") + or die "$0: cannot open: $mapfile: $!\n"; +open(OUT, "> $outfile\0") + or die "$0: cannot create: $outfile: $!\n"; + +%vstart = (); + +while (defined($line = )) { + if ($line =~ /^\s*([0-9]+)\s+(\S+)\s+([0-9a-f]+)\s+([0-9a-f]+)\s+([0-9a-f]+)\s+([0-9a-f]+)\s+2\*\*([0-9]+)/i) { + $vstart{$2} = hex $4; + } +} +close(MAP); + +$offset = 0; +@ostack = (); + +while (defined($line = )) { + chomp $line; + + $source = substr($line, 40); + if ($source =~ /^([^;]*);/) { + $source = $1; + } + + ($label, $op, $arg, $tail) = split(/\s+/, $source); + if ($op =~ /^(|\[)section$/i) { + $offset = $vstart{$arg}; + } elsif ($op =~ /^(absolute|\[absolute)$/i) { + $offset = 0; + } elsif ($op =~ /^struc$/i) { + push(@ostack, $offset); + $offset = 0; + } elsif ($op =~ /^endstruc$/i) { + $offset = pop(@ostack); + } + + if ($line =~ /^(\s*[0-9]+ )([0-9A-F]{8})(\s.*)$/) { + $line = sprintf("%s%08X%s", $1, (hex $2)+$offset, $3); + } + + print OUT $line, "\n"; +} diff --git a/core/macros.inc b/core/macros.inc new file mode 100644 index 0000000..1aa2b2c --- /dev/null +++ b/core/macros.inc @@ -0,0 +1,116 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved +;; Copyright 2009 Intel Corporation; author: H. Peter Anvin +;; +;; 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; macros.inc +;; +;; Convenient macros +;; + +%ifndef _MACROS_INC +%define _MACROS_INC + +; +; Identify the module we're compiling; the "correct" should be defined +; in the module itself to 1 +; +%ifdef IS_SYSLINUX + %define MY_NAME 'SYSLINUX' +%else + %define IS_SYSLINUX 0 +%endif +%ifdef IS_PXELINUX + %define MY_NAME 'PXELINUX' +%else + %define IS_PXELINUX 0 +%endif +%ifdef IS_ISOLINUX + %define MY_NAME 'ISOLINUX' +%else + %define IS_ISOLINUX 0 +%endif +%ifdef IS_EXTLINUX + %define MY_NAME 'EXTLINUX' +%else + %define IS_EXTLINUX 0 +%endif + +; +; Macros similar to res[bwd], but which works in the code segment (after +; section .text) or the data segment (section .data) +; +%macro zb 1.nolist + times %1 db 0 +%endmacro + +%macro zw 1.nolist + times %1 dw 0 +%endmacro + +%macro zd 1.nolist + times %1 dd 0 +%endmacro + +; +; Align with zero bytes in a progbits segment +; +%macro alignz 1.nolist + times (((%1) - (($-$$) % (%1))) % (%1)) db 0 +%endmacro + +; +; Macro to emit an unsigned decimal number as a string +; +%macro asciidec 1.nolist + %ifndef DEPEND ; Not safe for "depend" + %push asciidec + %assign %$v %1 + %if %$v == 0 + db '0' + %else + %assign %$dcount 0 + %assign %$n %$v + %assign %$d 1 + %rep 20 + %if %$n != 0 + %assign %$dcount %$dcount + 1 + %assign %$n %$n / 10 + %assign %$d %$d * 10 + %endif + %endrep + %rep %$dcount + %assign %$d %$d / 10 + db ((%$v / %$d) % 10) + '0' + %endrep + %endif + %pop + %endif +%endmacro + +; +; Macros for network byte order of constants +; +%define htons(x) ( ( ((x) & 0FFh) << 8 ) + ( ((x) & 0FF00h) >> 8 ) ) +%define ntohs(x) htons(x) +%define htonl(x) ( ( ((x) & 0FFh) << 24) + ( ((x) & 0FF00h) << 8 ) + ( ((x) & 0FF0000h) >> 8 ) + ( ((x) & 0FF000000h) >> 24) ) +%define ntohl(x) htonl(x) + +; +; ASCII +; +CR equ 13 ; Carriage Return +LF equ 10 ; Line Feed +FF equ 12 ; Form Feed +BS equ 8 ; Backspace + +%endif ; _MACROS_INC diff --git a/core/parsecmd.inc b/core/parsecmd.inc new file mode 100644 index 0000000..e63c205 --- /dev/null +++ b/core/parsecmd.inc @@ -0,0 +1,134 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved +;; Copyright 2009 Intel Corporation; author: H. Peter Anvin +;; +;; 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; parsecmd.inc +;; +;; Command line parser code +;; + + section .text + +; ------------------------------------------------------------------------- +; getcommand: Get a keyword from the current "getc" file and match it +; against a list of keywords (keywd_table). Each entry in +; that table should have the following form: +; <32 bit hash value> <16 bit handler offset> +; +; The handler is called, and upon return this function +; returns with CF = 0. On EOF, this function returns +; with CF = 1. +; ------------------------------------------------------------------------- + +getcommand.skipline: + call skipline + +getcommand: +.find: + call skipspace ; Skip leading whitespace + jz .eof ; End of file + jc .find ; End of line: try again + + ; Do this explicitly so #foo is treated as a comment + cmp al,'#' ; Leading hash mark -> comment + je .skipline + + ; Abuse the trackbuf by putting the keyword there for + ; possible error messaging... + mov di,trackbuf + stosb + or al,20h ; Convert to lower case + movzx ebx,al ; Hash for a one-char keyword +.read_loop: + call getc + jc .eof + cmp al,' ' ; Whitespace + jbe .done + stosb + or al,20h + rol ebx,5 + xor bl,al + jmp short .read_loop +.done: call ungetc + xor ax,ax + stosb ; Null-terminate the trackbuf + call skipspace + jz .eof + jc .noparm + call ungetc ; Return nonwhitespace char to buf + mov si,keywd_table + mov cx,keywd_count +.table_search: + lodsd + cmp ebx,eax + je .found_keywd + lodsd ; Skip entrypoint/argument + loop .table_search + + ; Otherwise unrecognized keyword + mov si,err_badcfg + call writestr + mov si,trackbuf + call writestr + call crlf + jmp .skipline + + ; No parameter +.noparm: + mov si,err_noparm + call writestr + mov si,trackbuf + call writestr + call crlf + jmp .find + +.found_keywd: lodsw ; Load argument into ax + call [si] + clc + ret + +.eof: stc + ret + +skipline: cmp al,10 ; Search for LF + je .end + call getc + jnc skipline +.end: ret + + section .data +err_badcfg db 'Unknown keyword in configuration file: ',0 +err_noparm db 'Missing parameter in configuration file. Keyword: ',0 + + section .uibss + alignb 4 +vk_size equ (vk_end + 3) & ~3 +VKernelBuf: resb vk_size ; "Current" vkernel +AppendBuf resb max_cmd_len+1 ; append= +Ontimeout resb max_cmd_len+1 ; ontimeout +Onerror resb max_cmd_len+1 ; onerror +KbdMap resb 256 ; Keyboard map +FKeyName resb MAX_FKEYS*FILENAME_MAX ; File names for F-key help +KernelCNameLen resw 1 ; Length of unmangled kernel name +InitRDCNameLen resw 1 ; Length of unmangled initrd name +%if IS_SYSLINUX +KernelName resb FILENAME_MAX+1 ; Mangled name for kernel +KernelCName resb FILENAME_MAX+2 ; Unmangled kernel name +InitRDCName resb FILENAME_MAX+2 ; Unmangled initrd name +%else +KernelName resb FILENAME_MAX ; Mangled name for kernel +KernelCName resb FILENAME_MAX ; Unmangled kernel name +InitRDCName resb FILENAME_MAX ; Unmangled initrd name +%endif +MNameBuf resb FILENAME_MAX +InitRD resb FILENAME_MAX diff --git a/core/parseconfig.inc b/core/parseconfig.inc new file mode 100644 index 0000000..65d71c9 --- /dev/null +++ b/core/parseconfig.inc @@ -0,0 +1,478 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved +;; Copyright 2009 Intel Corporation; author: H. Peter Anvin +;; +;; 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; parseconfig.inc +;; +;; Configuration file operations +;; + + section .text +; +; "default" or "ui" command, with level (1 = default, 2 = ui) +; +pc_default: cmp ax,[DefaultLevel] + jb skipline ; == call skipline + ret + mov [DefaultLevel],ax + mov di,default_cmd + call getline + mov byte [di-1],0 ; null-terminate + ret + +; +; "ontimeout" command +; +pc_ontimeout: mov di,Ontimeout + call getline + sub di,Ontimeout+1 ; Don't need final space + mov [OntimeoutLen],di + ret + +; +; "onerror" command +; +pc_onerror: mov di,Onerror + call getline + sub di,Onerror + mov [OnerrorLen],di + ret + +; +; "append" command +; +pc_append: cmp byte [VKernel],0 + ja .vk + mov di,AppendBuf + call getline + sub di,AppendBuf +.app1: mov [AppendLen],di + ret +.vk: mov di,VKernelBuf+vk_append ; "append" command (vkernel) + call getline + sub di,VKernelBuf+vk_append + cmp di,byte 2 + jne .app2 + cmp byte [VKernelBuf+vk_append],'-' + jne .app2 + xor di,di ; If "append -" -> null string +.app2: mov [VKernelBuf+vk_appendlen],di + ret + +; +; "ipappend" command (PXELINUX only) +; +%if IS_PXELINUX +pc_ipappend: call getint + jc .err + cmp byte [VKernel],0 + jne .vk + mov [IPAppend],bl +.err: ret +.vk: mov [VKernelBuf+vk_ipappend],bl + ret +%endif + +; +; "localboot" command +; +%if HAS_LOCALBOOT + +pc_localboot: call getint + cmp byte [VKernel],0 ; ("label" section only) + je .err + mov [VKernelBuf+vk_rname],bx + mov byte [VKernelBuf+vk_type],VK_LOCALBOOT +.err: ret + +%endif ; HAS_LOCALBOOT + +; +; "kernel", "config", ... command +; +pc_kernel: cmp byte [VKernel],0 + je .err ; ("label" section only) + mov [VKernelBuf+vk_type],al + call pc_getline + mov di,VKernelBuf+vk_rname + call mangle_name +.err: ret + +; +; "timeout", "totaltimeout" command +; +; N.B. 1/10 s ~ 1.D2162AABh clock ticks +; +pc_timeout: push ax + call getint + pop si + jc .err + mov eax,0D2162AABh + mul ebx ; clock ticks per 1/10 s + add ebx,edx + mov [si],ebx +.err: ret + + +; +; "totaltimeout" command +; +pc_totaltimeout: + +; +; Generic integer variable setting commands: +; "prompt", "implicit" +; +pc_setint16: + push ax + call getint + pop si + jc .err + mov [si],bx +.err: ret + +; +; Generic file-processing commands: +; "font", "kbdmap", +; +pc_filecmd: push ax ; Function to tailcall + call pc_getline + mov di,MNameBuf + call mangle_name + call searchdir + jnz .ok + pop ax ; Drop the successor function +.ok: ret ; Tailcall if OK, error return + +; +; Commands that expect the file to be opened on top of the getc stack. +; "display", "include" +; +pc_opencmd: push ax ; Function to tailcall + call pc_getline + mov di,MNameBuf + call mangle_name + call open + jnz .ok + pop ax ; Drop the successor function +.ok: ret ; Tailcall if OK, error return + +; +; "include" command (invoked from pc_opencmd) +; +pc_include: inc word [IncludeLevel] +.err: ret + +; +; "serial" command +; +pc_serial: call getint + jc .err + push bx ; Serial port # + xor eax,eax + mov [FlowControl],eax ; Default to no flow control + call skipspace + jc .nobaud + call ungetc + call getint + jc .nobaud +.valid_baud: + push ebx + call skipspace + jc .no_flow + call ungetc + call getint ; Hardware flow control? + jnc .valid_flow +.no_flow: + xor bx,bx ; Default -> no flow control +.valid_flow: + and bh,0Fh ; FlowIgnore + shl bh,4 + mov [FlowIgnore],bh + mov bh,bl + and bx,0F00Bh ; Valid bits + mov [FlowControl],bx + pop ebx ; Baud rate + jmp short .parse_baud +.nobaud: + mov ebx,DEFAULT_BAUD ; No baud rate given +.parse_baud: + pop di ; Serial port # + cmp ebx,byte 75 + jb .err ; < 75 baud == bogus + mov eax,BAUD_DIVISOR + cdq + div ebx + mov [BaudDivisor],ax + push ax ; Baud rate divisor + cmp di,3 + ja .port_is_io ; If port > 3 then port is I/O addr + shl di,1 + mov di,[di+serial_base] ; Get the I/O port from the BIOS +.port_is_io: + mov [SerialPort],di + + ; + ; Begin code to actually set up the serial port + ; + call sirq_cleanup_nowipe ; Cleanup existing IRQ handler + + lea dx,[di+3] ; DX -> LCR + mov al,83h ; Enable DLAB + slow_out dx,al + + pop ax ; Divisor + mov dx,di ; DX -> LS + slow_out dx,al + + inc dx ; DX -> MS + mov al,ah + slow_out dx,al + + mov al,03h ; Disable DLAB + inc dx ; DX -> LCR + inc dx + slow_out dx,al + + in al,dx ; Read back LCR (detect missing hw) + cmp al,03h ; If nothing here we'll read 00 or FF + jne .err ; Assume serial port busted + dec dx ; DX -> IIR/FCR + mov al,01h + slow_out dx,al ; Enable FIFOs if present + in al,dx + cmp al,0C0h ; FIFOs enabled and usable? + jae .fifo_ok + xor ax,ax ; Disable FIFO if unusable + slow_out dx,al +.fifo_ok: + + inc dx + inc dx ; DX -> MCR + mov al,[FlowOutput] ; Assert bits + slow_out dx,al + + ; Enable interrupts if requested + test al,8 + jz .noirq + call sirq_install +.noirq: + + ; Show some life + cmp byte [SerialNotice],0 + je .notfirst + mov byte [SerialNotice],0 + + mov si,syslinux_banner + call write_serial_str + mov si,copyright_str + call write_serial_str +.notfirst: + ret + +.err: + mov [SerialPort], word 0 + ret + +; +; Store mangled filename command (F-keys, "initrd") +; +pc_filename: push ax + call pc_getline + pop di + call mangle_name ; Mangle file name + ret + +; +; "label" command +; +pc_label: call commit_vk ; Commit any current vkernel + mov byte [InitRD+NULLOFFSET],NULLFILE ; No "initrd" statement + mov di,VKernelBuf ; Erase the vkernelbuf for better compression + mov cx,(vk_size >> 1) + xor ax,ax + rep stosw + call pc_getline + mov di,VKernelBuf+vk_vname + mov cx,FILENAME_MAX-1 +.loop: + lodsb + cmp al,' ' + jna .done + stosb + loop .loop +.done: + mov byte [VKernel],1 ; We've seen a "label" statement + mov si,VKernelBuf+vk_vname ; By default, rname == mangled vname + mov di,VKernelBuf+vk_rname + call mangle_name + mov si,AppendBuf ; Default append==global append + mov di,VKernelBuf+vk_append + mov cx,[AppendLen] + mov [VKernelBuf+vk_appendlen],cx + rep movsb +%if IS_PXELINUX ; PXELINUX only + mov al,[IPAppend] ; Default ipappend==global ipappend + mov [VKernelBuf+vk_ipappend],al +%endif + ret + +; +; "say" command +; +pc_say: call pc_getline ; "say" command + call writestr + jmp crlf ; tailcall + +; +; "text" command; ignore everything until we get an "endtext" line +; +pc_text: call skipline ; Ignore rest of line +.loop: + call pc_getline + jc .eof + + ; Leading spaces are already removed... + lodsd + and eax,0xdfdfdfdf ; Upper case + cmp eax,'ENDT' + jne .loop + lodsd + and eax,0x00dfdfdf ; Upper case and mask + cmp eax,'EXT' + jne .loop + ; If we get here we hit ENDTEXT +.eof: + ret + +; +; Comment line +; +pc_comment: ; Fall into pc_getline + +; +; Common subroutine: load line into trackbuf; returns with SI -> trackbuf +; CF is set on EOF. +; +pc_getline: mov di,trackbuf + push di + call getline + mov byte [di],0 ; Null-terminate + pop si + ret + +; +; Main loop for configuration file parsing +; +parse_config: + mov di,VKernelBuf ; Clear VKernelBuf at start + xor ax,ax + mov cx,vk_size + rep stosb + +.again: + call getcommand ; Parse one command + jnc .again ; If not EOF... + call close + dec word [IncludeLevel] ; Still parsing? + jnz .again + + ; + ; The fall through to commit_vk to commit any final + ; VKernel being read + ; +; +; commit_vk: Store the current VKernelBuf into buffer segment +; +commit_vk: + cmp byte [VKernel],0 + jz .nolabel ; Nothing to commit... + + mov di,VKernelBuf+vk_append + add di,[VKernelBuf+vk_appendlen] + + ; If we have an initrd statement, append it to the + ; append statement + cmp byte [InitRD+NULLOFFSET],NULLFILE + je .noinitrd + + mov si,str_initrd + mov cx,7 ; "initrd=" + rep movsb + mov si,InitRD + call unmangle_name + mov al,' ' + stosb + + ; For better compression, clean up the append field +.noinitrd: + mov ax,di + sub ax,VKernelBuf+vk_append + mov [VKernelBuf+vk_appendlen],ax + mov cx,max_cmd_len+1 + sub cx,ax + xor ax,ax + rep stosb + + ; Pack into high memory + mov esi,VKernelBuf + mov edi,[VKernelEnd] + mov ecx,vk_size + call rllpack + mov [VKernelEnd],edi +.nolabel: + ret +.overflow: + mov si,vk_overflow_msg + call writestr + ret + + section .data +vk_overflow_msg db 'Out of memory parsing config file', CR, LF, 0 +SerialNotice db 1 ; Only print this once + + section .bss + alignb 4 +VKernelEnd resd 1 ; Lowest high memory address used + + ; This symbol should be used by loaders to indicate + ; the highest address *they* are allowed to use. +HighMemRsvd equ VKernelEnd + ; by vkernels + section .config + alignz 4 +KbdTimeout dd 0 ; Keyboard timeout (if any) +TotalTimeout dd 0 ; Total timeout (if any) +AppendLen dw 0 ; Bytes in append= command +OntimeoutLen dw 0 ; Bytes in ontimeout command +OnerrorLen dw 0 ; Bytes in onerror command +CmdLinePtr dw cmd_line_here ; Command line advancing pointer +ForcePrompt dw 0 ; Force prompt +NoEscape dw 0 ; No escape +NoComplete dw 0 ; No label completion on TAB key +AllowImplicit dw 1 ; Allow implicit kernels +AllowOptions dw 1 ; User-specified options allowed +IncludeLevel dw 1 ; Nesting level +DefaultLevel dw 0 ; The current level of default +VKernel db 0 ; Have we seen any "label" statements? + +%if IS_PXELINUX +IPAppend db 0 ; Default IPAPPEND option +%endif + + section .uibss + alignb 4 ; For the good of REP MOVSD +command_line resb max_cmd_len+2 ; Command line buffer + alignb 4 +default_cmd resb max_cmd_len+1 ; "default" command line + +%include "rllpack.inc" diff --git a/core/plaincon.inc b/core/plaincon.inc new file mode 100644 index 0000000..59b2cbb --- /dev/null +++ b/core/plaincon.inc @@ -0,0 +1,24 @@ +; +; writechr: Write a single character in AL to the console without +; mangling any registers; handle video pages correctly. +; + section .text + +writechr: + call write_serial ; write to serial port if needed + pushfd + test byte [cs:UsingVGA], 08h + jz .videook + call vgaclearmode +.videook: + test byte [cs:DisplayCon], 01h + jz .nothing + pushad + mov ah,0Eh + mov bl,07h ; attribute + mov bh,[cs:BIOS_page] ; current page + int 10h + popad +.nothing: + popfd + ret diff --git a/core/pxe.inc b/core/pxe.inc new file mode 100644 index 0000000..7471c4f --- /dev/null +++ b/core/pxe.inc @@ -0,0 +1,154 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1999-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; pxe.inc +;; +;; PXE opcodes +;; + +%ifndef _PXE_INC +%define _PXE_INC 1 + +%define PXENV_TFTP_OPEN 0x0020 +%define PXENV_TFTP_CLOSE 0x0021 +%define PXENV_TFTP_READ 0x0022 +%define PXENV_TFTP_READ_FILE 0x0023 +%define PXENV_TFTP_READ_FILE_PMODE 0x0024 +%define PXENV_TFTP_GET_FSIZE 0x0025 + +%define PXENV_UDP_OPEN 0x0030 +%define PXENV_UDP_CLOSE 0x0031 +%define PXENV_UDP_READ 0x0032 +%define PXENV_UDP_WRITE 0x0033 + +%define PXENV_START_UNDI 0x0000 +%define PXENV_UNDI_STARTUP 0x0001 +%define PXENV_UNDI_CLEANUP 0x0002 +%define PXENV_UNDI_INITIALIZE 0x0003 +%define PXENV_UNDI_RESET_NIC 0x0004 +%define PXENV_UNDI_SHUTDOWN 0x0005 +%define PXENV_UNDI_OPEN 0x0006 +%define PXENV_UNDI_CLOSE 0x0007 +%define PXENV_UNDI_TRANSMIT 0x0008 +%define PXENV_UNDI_SET_MCAST_ADDR 0x0009 +%define PXENV_UNDI_SET_STATION_ADDR 0x000A +%define PXENV_UNDI_SET_PACKET_FILTER 0x000B +%define PXENV_UNDI_GET_INFORMATION 0x000C +%define PXENV_UNDI_GET_STATISTICS 0x000D +%define PXENV_UNDI_CLEAR_STATISTICS 0x000E +%define PXENV_UNDI_INITIATE_DIAGS 0x000F +%define PXENV_UNDI_FORCE_INTERRUPT 0x0010 +%define PXENV_UNDI_GET_MCAST_ADDR 0x0011 +%define PXENV_UNDI_GET_NIC_TYPE 0x0012 +%define PXENV_UNDI_GET_IFACE_INFO 0x0013 +%define PXENV_UNDI_ISR 0x0014 +%define PXENV_STOP_UNDI 0x0015 ; Overlap...? +%define PXENV_UNDI_GET_STATE 0x0015 ; Overlap...? + +%define PXENV_UNLOAD_STACK 0x0070 +%define PXENV_GET_CACHED_INFO 0x0071 +%define PXENV_RESTART_DHCP 0x0072 +%define PXENV_RESTART_TFTP 0x0073 +%define PXENV_MODE_SWITCH 0x0074 +%define PXENV_START_BASE 0x0075 +%define PXENV_STOP_BASE 0x0076 + +; gPXE extensions... +%define PXENV_FILE_OPEN 0x00e0 +%define PXENV_FILE_CLOSE 0x00e1 +%define PXENV_FILE_SELECT 0x00e2 +%define PXENV_FILE_READ 0x00e3 +%define PXENV_GET_FILE_SIZE 0x00e4 +%define PXENV_FILE_EXEC 0x00e5 +%define PXENV_FILE_API_CHECK 0x00e6 + +; Exit codes +%define PXENV_EXIT_SUCCESS 0x0000 +%define PXENV_EXIT_FAILURE 0x0001 + +; Status codes +%define PXENV_STATUS_SUCCESS 0x00 +%define PXENV_STATUS_FAILURE 0x01 +%define PXENV_STATUS_BAD_FUNC 0x02 +%define PXENV_STATUS_UNSUPPORTED 0x03 +%define PXENV_STATUS_KEEP_UNDI 0x04 +%define PXENV_STATUS_KEEP_ALL 0x05 +%define PXENV_STATUS_OUT_OF_RESOURCES 0x06 +%define PXENV_STATUS_ARP_TIMEOUT 0x11 +%define PXENV_STATUS_UDP_CLOSED 0x18 +%define PXENV_STATUS_UDP_OPEN 0x19 +%define PXENV_STATUS_TFTP_CLOSED 0x1a +%define PXENV_STATUS_TFTP_OPEN 0x1b +%define PXENV_STATUS_MCOPY_PROBLEM 0x20 +%define PXENV_STATUS_BIS_INTEGRITY_FAILURE 0x21 +%define PXENV_STATUS_BIS_VALIDATE_FAILURE 0x22 +%define PXENV_STATUS_BIS_INIT_FAILURE 0x23 +%define PXENV_STATUS_BIS_SHUTDOWN_FAILURE 0x24 +%define PXENV_STATUS_BIS_GBOA_FAILURE 0x25 +%define PXENV_STATUS_BIS_FREE_FAILURE 0x26 +%define PXENV_STATUS_BIS_GSI_FAILURE 0x27 +%define PXENV_STATUS_BIS_BAD_CKSUM 0x28 +%define PXENV_STATUS_TFTP_CANNOT_ARP_ADDRESS 0x30 +%define PXENV_STATUS_TFTP_OPEN_TIMEOUT 0x32 + +%define PXENV_STATUS_TFTP_UNKNOWN_OPCODE 0x33 +%define PXENV_STATUS_TFTP_READ_TIMEOUT 0x35 +%define PXENV_STATUS_TFTP_ERROR_OPCODE 0x36 +%define PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION 0x38 +%define PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION 0x39 +%define PXENV_STATUS_TFTP_TOO_MANY_PACKAGES 0x3a +%define PXENV_STATUS_TFTP_FILE_NOT_FOUND 0x3b +%define PXENV_STATUS_TFTP_ACCESS_VIOLATION 0x3c +%define PXENV_STATUS_TFTP_NO_MCAST_ADDRESS 0x3d +%define PXENV_STATUS_TFTP_NO_FILESIZE 0x3e +%define PXENV_STATUS_TFTP_INVALID_PACKET_SIZE 0x3f +%define PXENV_STATUS_DHCP_TIMEOUT 0x51 +%define PXENV_STATUS_DHCP_NO_IP_ADDRESS 0x52 +%define PXENV_STATUS_DHCP_NO_BOOTFILE_NAME 0x53 +%define PXENV_STATUS_DHCP_BAD_IP_ADDRESS 0x54 +%define PXENV_STATUS_UNDI_INVALID_FUNCTION 0x60 +%define PXENV_STATUS_UNDI_MEDIATEST_FAILED 0x61 +%define PXENV_STATUS_UNDI_CANNOT_INIT_NIC_FOR_MCAST 0x62 +%define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC 0x63 +%define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_PHY 0x64 +%define PXENV_STATUS_UNDI_CANNOT_READ_CONFIG_DATA 0x65 +%define PXENV_STATUS_UNDI_CANNOT_READ_INIT_DATA 0x66 +%define PXENV_STATUS_UNDI_BAD_MAC_ADDRESS 0x67 +%define PXENV_STATUS_UNDI_BAD_EEPROM_CHECKSUM 0x68 +%define PXENV_STATUS_UNDI_ERROR_SETTING_ISR 0x69 +%define PXENV_STATUS_UNDI_INVALID_STATE 0x6a +%define PXENV_STATUS_UNDI_TRANSMIT_ERROR 0x6b +%define PXENV_STATUS_UNDI_INVALID_PARAMETER 0x6c +%define PXENV_STATUS_BSTRAP_PROMPT_MENU 0x74 +%define PXENV_STATUS_BSTRAP_MCAST_ADDR 0x76 +%define PXENV_STATUS_BSTRAP_MISSING_LIST 0x77 +%define PXENV_STATUS_BSTRAP_NO_RESPONSE 0x78 +%define PXENV_STATUS_BSTRAP_FILE_TOO_BIG 0x79 +%define PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE 0xa0 +%define PXENV_STATUS_BINL_NO_PXE_SERVER 0xa1 +%define PXENV_STATUS_NOT_AVAILABLE_IN_PMODE 0xa2 +%define PXENV_STATUS_NOT_AVAILABLE_IN_RMODE 0xa3 +%define PXENV_STATUS_BUSD_DEVICE_NOT_SUPPORTED 0xb0 +%define PXENV_STATUS_LOADER_NO_FREE_BASE_MEMORY 0xc0 +%define PXENV_STATUS_LOADER_NO_BC_ROMID 0xc1 +%define PXENV_STATUS_LOADER_BAD_BC_ROMID 0xc2 +%define PXENV_STATUS_LOADER_BAD_BC_RUNTIME_IMAGE 0xc3 +%define PXENV_STATUS_LOADER_NO_UNDI_ROMID 0xc4 +%define PXENV_STATUS_LOADER_BAD_UNDI_ROMID 0xc5 +%define PXENV_STATUS_LOADER_BAD_UNDI_DRIVER_IMAGE 0xc6 +%define PXENV_STATUS_LOADER_NO_PXE_STRUCT 0xc8 +%define PXENV_STATUS_LOADER_NO_PXENV_STRUCT 0xc9 +%define PXENV_STATUS_LOADER_UNDI_START 0xca +%define PXENV_STATUS_LOADER_BC_START 0xcb + +%endif ; _PXE_INC diff --git a/core/pxeidle.inc b/core/pxeidle.inc new file mode 100644 index 0000000..230fb96 --- /dev/null +++ b/core/pxeidle.inc @@ -0,0 +1,122 @@ +;; -*- fundamental -*- --------------------------------------------------- +;; +;; Copyright 2008 H. Peter Anvin - All Rights Reserved +;; Copyright 2009 Intel Corporation; author: H. Peter Anvin +;; +;; 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, Inc., 51 Franklin St, Fifth Floor, +;; Boston MA 02110-1301, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; pxeidle.inc +;; +;; +;; Query for the NIC type, and detect certain special cases. +;; + + section .text + +;; +;; Initializes the idle mechanism based on the device type +;; +;; Assumes CS == DS == ES +;; +pxe_detect_nic_type: + pushad + + mov di,pxenv_get_nic_type + mov bx,PXENV_UNDI_GET_NIC_TYPE + call pxenv + jc .done + cmp word [di],0 + jne .done + cmp byte [di+2],2 ; PCI_NIC + jne .done ; No list for non-PCI nics + + mov cx,pxe_idle_pci_list.len + mov si,pxe_idle_pci_list +.look_for_id: + lodsd + cmp eax,[di+3] ; VID:DID + je .found_device + loop .look_for_id +.done: + popad + ret + +.found_device: + mov word [IdleHook],check_for_arp + jmp .done + +;; +;; List of devices for which we want to actually issue idle calls. +;; + section .data + alignz 4 +pxe_idle_pci_list: +; +; Older Broadcom NICs; these need idle calls to avoid FIFO stalls. +; + dw 0x14e4, 0x1659 ; BCM5721 + dw 0x14e4, 0x165a ; BCM5722 + dw 0x14e4, 0x165b ; BCM5723 + dw 0x14e4, 0x1668 ; BCM5714 + dw 0x14e4, 0x1669 ; BCM5714S + dw 0x14e4, 0x166a ; BCM5780 + dw 0x14e4, 0x166b ; BCM5780S + dw 0x14e4, 0x1673 ; BCM5755M + dw 0x14e4, 0x1674 ; BCM5756ME + dw 0x14e4, 0x1678 ; BCM5715 + dw 0x14e4, 0x1679 ; BCM5715S + dw 0x14e4, 0x167b ; BCM5755 +; +.len equ ($-pxe_idle_pci_list) >> 2 + + section .bss +pxenv_get_nic_type: +.status: resw 1 +.nic_type: resb 1 +.vid: resw 1 +.did: resw 1 +.base_class: resb 1 +.sub_class: resb 1 +.prog_intf: resb 1 +.rev: resb 1 +.busdevfunc: resw 1 +.svid: resw 1 +.sdid: resw 1 + + section .text +; +; Call the receive loop while idle. This is done mostly so we can respond to +; ARP messages, but perhaps in the future this can be used to do network +; console. +; +; hpa sez: people using automatic control on the serial port get very +; unhappy if we poll for ARP too often (the PXE stack is pretty slow, +; typically.) Therefore, only poll if at least 4 BIOS timer ticks have +; passed since the last poll, and reset this when a character is +; received (call reset_idle). +; +; Note: we only do this if pxe_detect_nic_type has set the IdleHook +; to point to this routine. +; +check_for_arp: + pushad + mov di,packet_buf + mov [pxe_udp_read_pkt.buffer],di + mov [pxe_udp_read_pkt.buffer+2],ds + mov word [pxe_udp_read_pkt.buffersize],packet_buf_size + mov eax,[MyIP] + mov [pxe_udp_read_pkt.dip],eax + mov word [pxe_udp_read_pkt.lport],htons(9) ; discard port + mov di,pxe_udp_read_pkt + mov bx,PXENV_UDP_READ + call pxenv + ; Ignore result... + popad + ret diff --git a/core/pxelinux.0 b/core/pxelinux.0 new file mode 100755 index 0000000000000000000000000000000000000000..743948e3d27b083f1c5457706ec422c166a51421 GIT binary patch literal 16138 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 literal 0 HcmV?d00001 diff --git a/core/pxelinux.asm b/core/pxelinux.asm new file mode 100644 index 0000000..58201a0 --- /dev/null +++ b/core/pxelinux.asm @@ -0,0 +1,2768 @@ +; -*- fundamental -*- (asm-mode sucks) +; **************************************************************************** +; +; pxelinux.asm +; +; A program to boot Linux kernels off a TFTP server using the Intel PXE +; network booting API. It is based on the SYSLINUX boot loader for +; MS-DOS floppies. +; +; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved +; Copyright 2009 Intel Corporation; author: H. Peter Anvin +; +; 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, Inc., 53 Temple Place Ste 330, +; Boston MA 02111-1307, USA; either version 2 of the License, or +; (at your option) any later version; incorporated herein by reference. +; +; **************************************************************************** + +%define IS_PXELINUX 1 +%include "head.inc" +%include "pxe.inc" + +; gPXE extensions support +%define GPXE 1 + +; +; Some semi-configurable constants... change on your own risk. +; +my_id equ pxelinux_id +FILENAME_MAX_LG2 equ 7 ; log2(Max filename size Including final null) +FILENAME_MAX equ (1 << FILENAME_MAX_LG2) +NULLFILE equ 0 ; Zero byte == null file name +NULLOFFSET equ 4 ; Position in which to look +REBOOT_TIME equ 5*60 ; If failure, time until full reset +%assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top +MAX_OPEN_LG2 equ 5 ; log2(Max number of open sockets) +MAX_OPEN equ (1 << MAX_OPEN_LG2) +PKTBUF_SIZE equ (65536/MAX_OPEN) ; Per-socket packet buffer size +TFTP_PORT equ htons(69) ; Default TFTP port +; Desired TFTP block size +; For Ethernet MTU is normally 1500. Unfortunately there seems to +; be a fair number of networks with "substandard" MTUs which break. +; The code assumes TFTP_LARGEBLK <= 2K. +TFTP_MTU equ 1440 +TFTP_LARGEBLK equ (TFTP_MTU-20-8-4) ; MTU - IP hdr - UDP hdr - TFTP hdr +; Standard TFTP block size +TFTP_BLOCKSIZE_LG2 equ 9 ; log2(bytes/block) +TFTP_BLOCKSIZE equ (1 << TFTP_BLOCKSIZE_LG2) +%assign USE_PXE_PROVIDED_STACK 1 ; Use stack provided by PXE? + +SECTOR_SHIFT equ TFTP_BLOCKSIZE_LG2 +SECTOR_SIZE equ TFTP_BLOCKSIZE + +; +; TFTP operation codes +; +TFTP_RRQ equ htons(1) ; Read request +TFTP_WRQ equ htons(2) ; Write request +TFTP_DATA equ htons(3) ; Data packet +TFTP_ACK equ htons(4) ; ACK packet +TFTP_ERROR equ htons(5) ; ERROR packet +TFTP_OACK equ htons(6) ; OACK packet + +; +; TFTP error codes +; +TFTP_EUNDEF equ htons(0) ; Unspecified error +TFTP_ENOTFOUND equ htons(1) ; File not found +TFTP_EACCESS equ htons(2) ; Access violation +TFTP_ENOSPACE equ htons(3) ; Disk full +TFTP_EBADOP equ htons(4) ; Invalid TFTP operation +TFTP_EBADID equ htons(5) ; Unknown transfer +TFTP_EEXISTS equ htons(6) ; File exists +TFTP_ENOUSER equ htons(7) ; No such user +TFTP_EOPTNEG equ htons(8) ; Option negotiation failure + +; +; The following structure is used for "virtual kernels"; i.e. LILO-style +; option labels. The options we permit here are `kernel' and `append +; Since there is no room in the bottom 64K for all of these, we +; stick them in high memory and copy them down before we need them. +; + struc vkernel +vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!** +vk_rname: resb FILENAME_MAX ; Real name +vk_ipappend: resb 1 ; "IPAPPEND" flag +vk_type: resb 1 ; Type of file +vk_appendlen: resw 1 + alignb 4 +vk_append: resb max_cmd_len+1 ; Command line + alignb 4 +vk_end: equ $ ; Should be <= vk_size + endstruc + +; +; BOOTP/DHCP packet pattern +; + struc bootp_t +bootp: +.opcode resb 1 ; BOOTP/DHCP "opcode" +.hardware resb 1 ; ARP hardware type +.hardlen resb 1 ; Hardware address length +.gatehops resb 1 ; Used by forwarders +.ident resd 1 ; Transaction ID +.seconds resw 1 ; Seconds elapsed +.flags resw 1 ; Broadcast flags +.cip resd 1 ; Client IP +.yip resd 1 ; "Your" IP +.sip resd 1 ; Next server IP +.gip resd 1 ; Relay agent IP +.macaddr resb 16 ; Client MAC address +.sname resb 64 ; Server name (optional) +.bootfile resb 128 ; Boot file name +.option_magic resd 1 ; Vendor option magic cookie +.options resb 1260 ; Vendor options + endstruc + +BOOTP_OPTION_MAGIC equ htonl(0x63825363) ; See RFC 2132 + +; +; TFTP connection data structure. Each one of these corresponds to a local +; UDP port. The size of this structure must be a power of 2. +; HBO = host byte order; NBO = network byte order +; (*) = written by options negotiation code, must be dword sized +; +; For a gPXE connection, we set the local port number to -1 and the +; remote port number contains the gPXE file handle. +; + struc open_file_t +tftp_localport resw 1 ; Local port number (0 = not in use) +tftp_remoteport resw 1 ; Remote port number +tftp_remoteip resd 1 ; Remote IP address +tftp_filepos resd 1 ; Bytes downloaded (including buffer) +tftp_filesize resd 1 ; Total file size(*) +tftp_blksize resd 1 ; Block size for this connection(*) +tftp_bytesleft resw 1 ; Unclaimed data bytes +tftp_lastpkt resw 1 ; Sequence number of last packet (NBO) +tftp_dataptr resw 1 ; Pointer to available data +tftp_goteof resb 1 ; 1 if the EOF packet received + resb 3 ; Currently unusued + ; At end since it should not be zeroed on socked close +tftp_pktbuf resw 1 ; Packet buffer offset + endstruc +%ifndef DEPEND +%if (open_file_t_size & (open_file_t_size-1)) +%error "open_file_t is not a power of 2" +%endif +%endif + +; --------------------------------------------------------------------------- +; BEGIN CODE +; --------------------------------------------------------------------------- + +; +; Memory below this point is reserved for the BIOS and the MBR +; + section .earlybss +trackbufsize equ 8192 +trackbuf resb trackbufsize ; Track buffer goes here + ; ends at 2800h + + alignb open_file_t_size +Files resb MAX_OPEN*open_file_t_size + + alignb FILENAME_MAX +BootFile resb 256 ; Boot file from DHCP packet +PathPrefix resb 256 ; Path prefix derived from boot file +DotQuadBuf resb 16 ; Buffer for dotted-quad IP address +IPOption resb 80 ; ip= option buffer +InitStack resd 1 ; Pointer to reset stack (SS:SP) +PXEStack resd 1 ; Saved stack during PXE call + + section .bss + alignb 4 +RebootTime resd 1 ; Reboot timeout, if set by option +StrucPtr resd 1 ; Pointer to PXENV+ or !PXE structure +APIVer resw 1 ; PXE API version found +LocalBootType resw 1 ; Local boot return code +RealBaseMem resw 1 ; Amount of DOS memory after freeing +OverLoad resb 1 ; Set if DHCP packet uses "overloading" +DHCPMagic resb 1 ; PXELINUX magic flags + +; The relative position of these fields matter! +MAC_MAX equ 32 ; Handle hardware addresses this long +MACLen resb 1 ; MAC address len +MACType resb 1 ; MAC address type +MAC resb MAC_MAX+1 ; Actual MAC address +BOOTIFStr resb 7 ; Space for "BOOTIF=" +MACStr resb 3*(MAC_MAX+1) ; MAC address as a string + +; The relative position of these fields matter! +UUIDType resb 1 ; Type byte from DHCP option +UUID resb 16 ; UUID, from the PXE stack +UUIDNull resb 1 ; dhcp_copyoption zero-terminates + +; +; PXE packets which don't need static initialization +; + alignb 4 +pxe_unload_stack_pkt: +.status: resw 1 ; Status +.reserved: resb 10 ; Reserved +pxe_unload_stack_pkt_len equ $-pxe_unload_stack_pkt + + alignb 16 + ; BOOTP/DHCP packet buffer + + section .bss2 + alignb 16 +packet_buf resb 2048 ; Transfer packet +packet_buf_size equ $-packet_buf + + section .text + ; + ; PXELINUX needs more BSS than the other derivatives; + ; therefore we relocate it from 7C00h on startup. + ; +StackBuf equ $ ; Base of stack if we use our own + +; +; Primary entry point. +; +bootsec equ $ +_start: + pushfd ; Paranoia... in case of return to PXE + pushad ; ... save as much state as possible + push ds + push es + push fs + push gs + + cld ; Copy upwards + xor ax,ax + mov ds,ax + mov es,ax + + jmp 0:_start1 ; Canonicalize address +_start1: + ; That is all pushed onto the PXE stack. Save the pointer + ; to it and switch to an internal stack. + mov [InitStack],sp + mov [InitStack+2],ss + +%if USE_PXE_PROVIDED_STACK + ; Apparently some platforms go bonkers if we + ; set up our own stack... + mov [BaseStack],sp + mov [BaseStack+4],ss +%endif + + lss esp,[BaseStack] + sti ; Stack set up and ready + +; +; Initialize screen (if we're using one) +; +%include "init.inc" + +; +; Tell the user we got this far +; + mov si,syslinux_banner + call writestr_early + + mov si,copyright_str + call writestr_early + +; +; Assume API version 2.1, in case we find the !PXE structure without +; finding the PXENV+ structure. This should really look at the Base +; Code ROM ID structure in have_pxe, but this is adequate for now -- +; if we have !PXE, we have to be 2.1 or higher, and we don't care +; about higher versions than that. +; + mov word [APIVer],0201h + +; +; Now we need to find the !PXE structure. +; We search for the following, in order: +; +; a. !PXE structure as SS:[SP+4] +; b. PXENV+ structure at [ES:BX] +; c. INT 1Ah AX=5650h -> PXENV+ +; d. Search memory for !PXE +; e. Search memory for PXENV+ +; +; If we find a PXENV+ structure, we try to find a !PXE structure from +; it if the API version is 2.1 or later. +; + ; Plan A: !PXE structure as SS:[SP+4] + lgs bp,[InitStack] ; GS:BP -> original stack + les bx,[gs:bp+48] + call is_pxe + je have_pxe + + ; Plan B: PXENV+ structure at [ES:BX] + inc byte [plan] + mov bx,[gs:bp+24] ; Original BX + mov es,[gs:bp+4] ; Original ES + call is_pxenv + je have_pxenv + + ; Plan C: PXENV+ structure via INT 1Ah AX=5650h + inc byte [plan] + mov ax, 5650h +%if USE_PXE_PROVIDED_STACK == 0 + lss sp,[InitStack] +%endif + int 1Ah ; May trash regs +%if USE_PXE_PROVIDED_STACK == 0 + lss esp,[BaseStack] +%endif + sti ; Work around Etherboot bug + + jc no_int1a + cmp ax,564Eh + jne no_int1a + + call is_pxenv + je have_pxenv + +no_int1a: + ; Plan D: !PXE memory scan + inc byte [plan] + call memory_scan_for_pxe_struct ; !PXE scan + je have_pxe + + ; Plan E: PXENV+ memory scan + inc byte [plan] + call memory_scan_for_pxenv_struct ; PXENV+ scan + je have_pxenv + + ; Found nothing at all!! +no_pxe: + mov si,err_nopxe + call writestr_early + jmp kaboom + +have_pxenv: + mov [StrucPtr],bx + mov [StrucPtr+2],es + + mov si,found_pxenv + call writestr_early + + mov si,apiver_str + call writestr_early + mov ax,[es:bx+6] + mov [APIVer],ax + call writehex4 + call crlf + + cmp ax,0201h ; API version 2.1 or higher + jb .old_api + cmp byte [es:bx+8],2Ch ; Space for !PXE pointer? + jb .pxescan + les bx,[es:bx+28h] ; !PXE structure pointer + call is_pxe + je have_pxe + + ; Nope, !PXE structure missing despite API 2.1+, or at least + ; the pointer is missing. Do a last-ditch attempt to find it. +.pxescan: + call memory_scan_for_pxe_struct + je have_pxe + + ; Otherwise, no dice, use PXENV+ structure +.old_api: + les bx,[StrucPtr] + push word [es:bx+22h] ; UNDI data len + push word [es:bx+20h] ; UNDI data seg + push word [es:bx+26h] ; UNDI code len + push word [es:bx+24h] ; UNDI code seg + push dword [es:bx+0Ah] ; PXENV+ entry point + + mov si,pxenventry_msg + jmp have_entrypoint + +have_pxe: + mov [StrucPtr],bx + mov [StrucPtr+2],es + + push word [es:bx+2Eh] ; UNDI data len + push word [es:bx+28h] ; UNDI data seg + push word [es:bx+36h] ; UNDI code len + push word [es:bx+30h] ; UNDI code seg + push dword [es:bx+10h] ; !PXE entry point + + mov si,pxeentry_msg + +have_entrypoint: + push cs + pop es ; Restore CS == DS == ES + + call writestr_early ; !PXE or PXENV+ entry found + + pop dword [PXEEntry] + mov ax,[PXEEntry+2] + call writehex4 + mov al,':' + call writechr + mov ax,[PXEEntry] + call writehex4 + + mov si,viaplan_msg + call writestr_early + + mov si,undi_code_msg + call writestr_early + pop ax ; UNDI code segment + call writehex4 + xchg dx,ax + mov si,len_msg + call writestr_early + pop ax ; UNDI code length + call writehex4 + call crlf + add ax,15 + shr ax,4 + add dx,ax ; DX = seg of end of UNDI code + + mov si,undi_data_msg + call writestr_early + pop ax ; UNDI data segment + call writehex4 + xchg bx,ax + mov si,len_msg + call writestr_early + pop ax ; UNDI data length + call writehex4 + call crlf + add ax,15 + shr ax,4 + add ax,bx ; AX = seg of end of UNDI data + + cmp ax,dx + ja .data_on_top + xchg ax,dx +.data_on_top: + ; Could we safely add 63 here before the shift? + shr ax,6 ; Convert to kilobytes + mov [RealBaseMem],ax + + +; +; Network-specific initialization +; + xor ax,ax + mov [LocalDomain],al ; No LocalDomain received + +; +; The DHCP client identifiers are best gotten from the DHCPREQUEST +; packet (query info 1). +; +query_bootp_1: + mov si,get_packet_msg + call writestr_early + + mov dl,1 + call pxe_get_cached_info + call parse_dhcp + + ; We don't use flags from the request packet, so + ; this is a good time to initialize DHCPMagic... + ; Initialize it to 1 meaning we will accept options found; + ; in earlier versions of PXELINUX bit 0 was used to indicate + ; we have found option 208 with the appropriate magic number; + ; we no longer require that, but MAY want to re-introduce + ; it in the future for vendor encapsulated options. + mov byte [DHCPMagic],1 + +; +; Now attempt to get the BOOTP/DHCP packet that brought us life (and an IP +; address). This lives in the DHCPACK packet (query info 2). +; +query_bootp_2: + mov dl,2 + call pxe_get_cached_info + call parse_dhcp ; Parse DHCP packet +; +; Save away MAC address (assume this is in query info 2. If this +; turns out to be problematic it might be better getting it from +; the query info 1 packet.) +; +.save_mac: + movzx cx,byte [trackbuf+bootp.hardlen] + cmp cx,16 + jna .mac_ok + xor cx,cx ; Bad hardware address length +.mac_ok: + mov [MACLen],cl + mov al,[trackbuf+bootp.hardware] + mov [MACType],al + mov si,trackbuf+bootp.macaddr + mov di,MAC + rep movsb + +; Enable this if we really need to zero-pad this field... +; mov cx,MAC+MAC_MAX+1 +; sub cx,di +; xor ax,ax +; rep stosb + +; +; Now, get the boot file and other info. This lives in the CACHED_REPLY +; packet (query info 3). +; +query_bootp_3: + mov dl,3 + call pxe_get_cached_info + call parse_dhcp ; Parse DHCP packet + call crlf + +; +; Generate the bootif string, and the hardware-based config string. +; +make_bootif_string: + mov si,bootif_str + mov di,BOOTIFStr + mov cx,bootif_str_len + rep movsb + + movzx cx,byte [MACLen] + mov si,MACType + inc cx +.hexify_mac: + push cx + mov cl,1 ; CH == 0 already + call lchexbytes + mov al,'-' + stosb + pop cx + loop .hexify_mac + mov [di-1],cl ; Null-terminate and strip final dash +; +; Generate ip= option +; + call genipopt + +; +; Print IP address +; + mov eax,[MyIP] + mov di,DotQuadBuf + push di + call gendotquad ; This takes network byte order input + + xchg ah,al ; Convert to host byte order + ror eax,16 ; (BSWAP doesn't work on 386) + xchg ah,al + + mov si,myipaddr_msg + call writestr_early + call writehex8 + mov al,' ' + call writechr + pop si ; DotQuadBuf + call writestr_early + call crlf + + mov si,IPOption + call writestr_early + call crlf + +; +; Check to see if we got any PXELINUX-specific DHCP options; in particular, +; if we didn't get the magic enable, do not recognize any other options. +; +check_dhcp_magic: + test byte [DHCPMagic], 1 ; If we didn't get the magic enable... + jnz .got_magic + mov byte [DHCPMagic], 0 ; If not, kill all other options +.got_magic: + + +; +; Initialize UDP stack +; +udp_init: + mov eax,[MyIP] + mov [pxe_udp_open_pkt.sip],eax + mov di,pxe_udp_open_pkt + mov bx,PXENV_UDP_OPEN + call pxenv + jc .failed + cmp word [pxe_udp_open_pkt.status], byte 0 + je .success +.failed: mov si,err_udpinit + call writestr_early + jmp kaboom +.success: + +; +; Common initialization code +; +%include "cpuinit.inc" + +; +; Detect NIC type and initialize the idle mechanism +; + call pxe_detect_nic_type + call reset_idle + +; +; Now we're all set to start with our *real* business. First load the +; configuration file (if any) and parse it. +; +; In previous versions I avoided using 32-bit registers because of a +; rumour some BIOSes clobbered the upper half of 32-bit registers at +; random. I figure, though, that if there are any of those still left +; they probably won't be trying to install Linux on them... +; +; The code is still ripe with 16-bitisms, though. Not worth the hassle +; to take'm out. In fact, we may want to put them back if we're going +; to boot ELKS at some point. +; + +; +; Store standard filename prefix +; +prefix: test byte [DHCPMagic], 04h ; Did we get a path prefix option + jnz .got_prefix + mov si,BootFile + mov di,PathPrefix + cld + call strcpy + mov cx,di + sub cx,PathPrefix+1 + std + lea si,[di-2] ; Skip final null! +.find_alnum: lodsb + or al,20h + cmp al,'.' ; Count . or - as alphanum + je .alnum + cmp al,'-' + je .alnum + cmp al,'0' + jb .notalnum + cmp al,'9' + jbe .alnum + cmp al,'a' + jb .notalnum + cmp al,'z' + ja .notalnum +.alnum: loop .find_alnum + dec si +.notalnum: mov byte [si+2],0 ; Zero-terminate after delimiter + cld +.got_prefix: + mov si,tftpprefix_msg + call writestr_early + mov si,PathPrefix + call writestr_early + call crlf + + ; Set CurrentDirName + push di + mov si,PathPrefix + mov di,CurrentDirName + call strcpy + pop di + +; +; Load configuration file +; +find_config: + +; +; Begin looking for configuration file +; +config_scan: + test byte [DHCPMagic], 02h + jz .no_option + + ; We got a DHCP option, try it first + call .try + jnz .success + +.no_option: + mov di,ConfigName + mov si,cfgprefix + mov cx,cfgprefix_len + rep movsb + + ; Have to guess config file name... + + ; Try loading by UUID. + cmp byte [HaveUUID],0 + je .no_uuid + + push di + mov bx,uuid_dashes + mov si,UUID +.gen_uuid: + movzx cx,byte [bx] + jcxz .done_uuid + inc bx + call lchexbytes + mov al,'-' + stosb + jmp .gen_uuid +.done_uuid: + mov [di-1],cl ; Remove last dash and zero-terminate + pop di + call .try + jnz .success +.no_uuid: + + ; Try loading by MAC address + push di + mov si,MACStr + call strcpy + pop di + call .try + jnz .success + + ; Nope, try hexadecimal IP prefixes... +.scan_ip: + mov cx,4 + mov si,MyIP + call uchexbytes ; Convert to hex string + + mov cx,8 ; Up to 8 attempts +.tryagain: + mov byte [di],0 ; Zero-terminate string + call .try + jnz .success + dec di ; Drop one character + loop .tryagain + + ; Final attempt: "default" string + mov si,default_str ; "default" string + call strcpy + call .try + jnz .success + + mov si,err_noconfig + call writestr_early + jmp kaboom + +.try: + pusha + mov si,trying_msg + call writestr_early + mov di,ConfigName + mov si,di + call writestr_early + call crlf + mov si,di + mov di,KernelName ; Borrow this buffer for mangled name + call mangle_name + call open + popa + ret + + +.success: + +; +; Linux kernel loading code is common. However, we need to define +; a couple of helper macros... +; + +; Unload PXE stack +%define HAVE_UNLOAD_PREP +%macro UNLOAD_PREP 0 + call unload_pxe +%endmacro + +; +; Now we have the config file open. Parse the config file and +; run the user interface. +; +%include "ui.inc" + +; +; Boot to the local disk by returning the appropriate PXE magic. +; AX contains the appropriate return code. +; +%if HAS_LOCALBOOT + +local_boot: + push cs + pop ds + mov [LocalBootType],ax + call vgaclearmode + mov si,localboot_msg + call writestr_early + ; Restore the environment we were called with + lss sp,[InitStack] + pop gs + pop fs + pop es + pop ds + popad + mov ax,[cs:LocalBootType] + popfd + retf ; Return to PXE + +%endif + +; +; kaboom: write a message and bail out. Wait for quite a while, +; or a user keypress, then do a hard reboot. +; +kaboom: + RESET_STACK_AND_SEGS AX +.patch: mov si,bailmsg + call writestr_early ; Returns with AL = 0 +.drain: call pollchar + jz .drained + call getchar + jmp short .drain +.drained: + mov edi,[RebootTime] + mov al,[DHCPMagic] + and al,09h ; Magic+Timeout + cmp al,09h + je .time_set + mov edi,REBOOT_TIME +.time_set: + mov cx,18 +.wait1: push cx + mov ecx,edi +.wait2: mov dx,[BIOS_timer] +.wait3: call pollchar + jnz .keypress + cmp dx,[BIOS_timer] + je .wait3 + loop .wait2,ecx + mov al,'.' + call writechr + pop cx + loop .wait1 +.keypress: + call crlf + mov word [BIOS_magic],0 ; Cold reboot + jmp 0F000h:0FFF0h ; Reset vector address + +; +; memory_scan_for_pxe_struct: +; memory_scan_for_pxenv_struct: +; +; If none of the standard methods find the !PXE/PXENV+ structure, +; look for it by scanning memory. +; +; On exit, if found: +; ZF = 1, ES:BX -> !PXE structure +; Otherwise: +; ZF = 0 +; +; Assumes DS == CS +; Clobbers AX, BX, CX, DX, SI, ES +; +memory_scan_for_pxe_struct: + mov dx,is_pxe + mov ax,[BIOS_fbm] ; Starting segment + shl ax,(10-4) ; Kilobytes -> paragraphs + jmp memory_scan_common + +memory_scan_for_pxenv_struct: + mov ax,1000h ; Starting segment + mov dx,is_pxenv + ; fall through + +memory_scan_common: + dec ax ; To skip inc ax +.mismatch: + inc ax + cmp ax,0A000h-1 ; End of memory + ja .not_found ; ZF = 0 on not found + mov es,ax + xor bx,bx + call dx + jne .mismatch +.not_found: + ret + +; +; is_pxe: +; Validity check on possible !PXE structure in ES:BX +; is_pxenv: +; Validity check on possible PXENV+ structure in ES:BX +; +; Return ZF = 1 on success +; +; Clobbers CX and SI +; +is_struc: +.pxe: + cmp dword [es:bx],'!PXE' + jne .bad + movzx cx,byte [es:bx+4] + cmp cx,58h + jae .checksum + ret +.pxenv: + cmp dword [es:bx],'PXEN' + jne .bad + cmp word [es:bx+4],'V+' + jne .bad + movzx cx,[es:bx+8] + cmp cx,28h + jb .bad +.checksum: + push ax + mov si,bx + xor ax,ax +.loop: + es lodsb + add ah,al + loop .loop + pop ax +.bad: + ret + +is_pxe equ is_struc.pxe +is_pxenv equ is_struc.pxenv + +; +; close_file: +; Deallocates a file structure (pointer in SI) +; Assumes CS == DS. +; +; XXX: We should check to see if this file is still open on the server +; side and send a courtesy ERROR packet to the server. +; +close_file: + and si,si + jz .closed + mov word [si],0 ; Not in use +.closed: ret + +; +; searchdir: +; +; Open a TFTP connection to the server +; +; On entry: +; DS:DI = mangled filename +; If successful: +; ZF clear +; SI = socket pointer +; EAX = file length in bytes, or -1 if unknown +; If unsuccessful +; ZF set +; + +searchdir: + push es + push bx + push cx + mov ax,ds + mov es,ax + mov si,di + push bp + mov bp,sp + + call allocate_socket + jz .ret + + mov ax,TimeoutTable ; Reset timeout + +.sendreq: push ax ; [bp-2] - Timeout pointer + push si ; [bp-4] - File name + + mov di,packet_buf + mov [pxe_udp_write_pkt.buffer],di + + mov ax,TFTP_RRQ ; TFTP opcode + stosw + + lodsd ; EAX <- server override (if any) + and eax,eax + jnz .noprefix ; No prefix, and we have the server + + push si ; Add common prefix + mov si,PathPrefix + call strcpy + dec di + pop si + + mov eax,[ServerIP] ; Get default server + +.noprefix: + call strcpy ; Filename +%if GPXE + mov si,packet_buf+2 + call is_gpxe + jnc .gpxe +%endif + + mov [bx+tftp_remoteip],eax + + push bx ; [bp-6] - TFTP block + mov bx,[bx] + push bx ; [bp-8] - TID (local port no) + + mov [pxe_udp_write_pkt.sip],eax + ; Now figure out the gateway + xor eax,[MyIP] + and eax,[Netmask] + jz .nogwneeded + mov eax,[Gateway] +.nogwneeded: + mov [pxe_udp_write_pkt.gip],eax + mov [pxe_udp_write_pkt.lport],bx + mov ax,[ServerPort] + mov [pxe_udp_write_pkt.rport],ax + mov si,tftp_tail + mov cx,tftp_tail_len + rep movsb + sub di,packet_buf ; Get packet size + mov [pxe_udp_write_pkt.buffersize],di + + mov di,pxe_udp_write_pkt + mov bx,PXENV_UDP_WRITE + call pxenv + jc .failure + cmp word [pxe_udp_write_pkt.status],byte 0 + jne .failure + + ; + ; Danger, Will Robinson! We need to support timeout + ; and retry lest we just lost a packet... + ; + + ; Packet transmitted OK, now we need to receive +.getpacket: mov bx,[bp-2] + movzx bx,byte [bx] + push bx ; [bp-10] - timeout in ticks + push word [BIOS_timer] ; [bp-12] + +.pkt_loop: mov bx,[bp-8] ; TID + mov di,packet_buf + mov [pxe_udp_read_pkt.buffer],di + mov [pxe_udp_read_pkt.buffer+2],ds + mov word [pxe_udp_read_pkt.buffersize],packet_buf_size + mov eax,[MyIP] + mov [pxe_udp_read_pkt.dip],eax + mov [pxe_udp_read_pkt.lport],bx + mov di,pxe_udp_read_pkt + mov bx,PXENV_UDP_READ + call pxenv + jnc .got_packet ; Wait for packet +.no_packet: + mov dx,[BIOS_timer] + cmp dx,[bp-12] + je .pkt_loop + mov [bp-12],dx + dec word [bp-10] + jnz .pkt_loop + pop ax ; Adjust stack + pop ax + jmp .failure + +.got_packet: + mov si,[bp-6] ; TFTP pointer + mov bx,[bp-8] ; TID + + ; Make sure the packet actually came from the server + ; This is technically not to the TFTP spec? + mov eax,[si+tftp_remoteip] + cmp [pxe_udp_read_pkt.sip],eax + jne .no_packet + + ; Got packet - reset timeout + mov word [bp-2],TimeoutTable + + pop ax ; Adjust stack + pop ax + + mov ax,[pxe_udp_read_pkt.rport] + mov [si+tftp_remoteport],ax + + ; filesize <- -1 == unknown + mov dword [si+tftp_filesize], -1 + ; Default blksize unless blksize option negotiated + mov word [si+tftp_blksize], TFTP_BLOCKSIZE + + movzx ecx,word [pxe_udp_read_pkt.buffersize] + sub cx,2 ; CX <- bytes after opcode + jb .failure ; Garbled reply + + mov si,packet_buf + lodsw + + cmp ax, TFTP_ERROR + je .bailnow ; ERROR reply: don't try again + + ; If the server doesn't support any options, we'll get + ; a DATA reply instead of OACK. Stash the data in + ; the file buffer and go with the default value for + ; all options... + cmp ax, TFTP_DATA + je .no_oack + + cmp ax, TFTP_OACK + jne .err_reply ; Unknown packet type + + ; Now we need to parse the OACK packet to get the transfer + ; and packet sizes. + ; SI -> first byte of options; [E]CX -> byte count +.parse_oack: + jcxz .done_pkt ; No options acked + +.get_opt_name: + ; If we find an option which starts with a NUL byte, + ; (a null option), we're either seeing garbage that some + ; TFTP servers add to the end of the packet, or we have + ; no clue how to parse the rest of the packet (what is + ; an option name and what is a value?) In either case, + ; discard the rest. + cmp byte [si],0 + je .done_pkt + + mov di,si + mov bx,si +.opt_name_loop: lodsb + and al,al + jz .got_opt_name + or al,20h ; Convert to lowercase + stosb + loop .opt_name_loop + ; We ran out, and no final null + jmp .done_pkt ; Ignore runt option +.got_opt_name: ; si -> option value + dec cx ; bytes left in pkt + jz .done_pkt ; Option w/o value, ignore + + ; Parse option pointed to by bx; guaranteed to be + ; null-terminated. + push cx + push si + mov si,bx ; -> option name + mov bx,tftp_opt_table + mov cx,tftp_opts +.opt_loop: + push cx + push si + mov di,[bx] ; Option pointer + mov cx,[bx+2] ; Option len + repe cmpsb + pop si + pop cx + je .get_value ; OK, known option + add bx,6 + loop .opt_loop + + pop si + pop cx + ; Non-negotiated option returned, no idea what it means... + jmp .err_reply + +.get_value: pop si ; si -> option value + pop cx ; cx -> bytes left in pkt + mov bx,[bx+4] ; Pointer to data target + add bx,[bp-6] ; TFTP socket pointer + xor eax,eax + xor edx,edx +.value_loop: lodsb + and al,al + jz .got_value + sub al,'0' + cmp al, 9 + ja .err_reply ; Not a decimal digit + imul edx,10 + add edx,eax + mov [bx],edx + loop .value_loop + ; Ran out before final null, accept anyway + jmp short .done_pkt + +.got_value: + dec cx + jnz .get_opt_name ; Not end of packet + + ; ZF == 1 + + ; Success, done! +.done_pkt: + pop si ; Junk + pop si ; We want the packet ptr in SI + + mov eax,[si+tftp_filesize] +.got_file: ; SI->socket structure, EAX = size + and eax,eax ; Set ZF depending on file size + jz .error_si ; ZF = 1 need to free the socket +.ret: + leave ; SP <- BP, POP BP + pop cx + pop bx + pop es + ret + + +.no_oack: ; We got a DATA packet, meaning no options are + ; suported. Save the data away and consider the length + ; undefined, *unless* this is the only data packet... + mov bx,[bp-6] ; File pointer + sub cx,2 ; Too short? + jb .failure + lodsw ; Block number + cmp ax,htons(1) + jne .failure + mov [bx+tftp_lastpkt],ax + cmp cx,TFTP_BLOCKSIZE + ja .err_reply ; Corrupt... + je .not_eof + ; This was the final EOF packet, already... + ; We know the filesize, but we also want to ack the + ; packet and set the EOF flag. + mov [bx+tftp_filesize],ecx + mov byte [bx+tftp_goteof],1 + push si + mov si,bx + ; AX = htons(1) already + call ack_packet + pop si +.not_eof: + mov [bx+tftp_bytesleft],cx + mov ax,pktbuf_seg + push es + mov es,ax + mov di,tftp_pktbuf + mov [bx+tftp_dataptr],di + add cx,3 + shr cx,2 + rep movsd + pop es + jmp .done_pkt + +.err_reply: ; TFTP protocol error. Send ERROR reply. + ; ServerIP and gateway are already programmed in + mov si,[bp-6] + mov ax,[si+tftp_remoteport] + mov word [pxe_udp_write_pkt.rport],ax + mov word [pxe_udp_write_pkt.buffer],tftp_proto_err + mov word [pxe_udp_write_pkt.buffersize],tftp_proto_err_len + mov di,pxe_udp_write_pkt + mov bx,PXENV_UDP_WRITE + call pxenv + + ; Write an error message and explode + mov si,err_damage + call writestr_early + jmp kaboom + +.bailnow: + ; Immediate error - no retry + mov word [bp-2],TimeoutTableEnd-1 + +.failure: pop bx ; Junk + pop bx + pop si + pop ax + inc ax + cmp ax,TimeoutTableEnd + jb .sendreq ; Try again + +.error: mov si,bx ; Socket pointer +.error_si: ; Socket pointer already in SI + call free_socket ; ZF <- 1, SI <- 0 + jmp .ret + + +%if GPXE +.gpxe: + push bx ; Socket pointer + mov di,gpxe_file_open + mov word [di],2 ; PXENV_STATUS_BAD_FUNC + mov word [di+4],packet_buf+2 ; Completed URL + mov [di+6],ds + mov bx,PXENV_FILE_OPEN + call pxenv + pop si ; Socket pointer in SI + jc .error_si + + mov ax,[di+2] + mov word [si+tftp_localport],-1 ; gPXE URL + mov [si+tftp_remoteport],ax + mov di,gpxe_get_file_size + mov [di+2],ax + +%if 0 + ; Disable this for now since gPXE doesn't always + ; return valid information in PXENV_GET_FILE_SIZE + mov bx,PXENV_GET_FILE_SIZE + call pxenv + mov eax,[di+4] ; File size + jnc .oksize +%endif + or eax,-1 ; Size unknown +.oksize: + mov [si+tftp_filesize],eax + jmp .got_file +%endif ; GPXE + +; +; allocate_socket: Allocate a local UDP port structure +; +; If successful: +; ZF set +; BX = socket pointer +; If unsuccessful: +; ZF clear +; +allocate_socket: + push cx + mov bx,Files + mov cx,MAX_OPEN +.check: cmp word [bx], byte 0 + je .found + add bx,open_file_t_size + loop .check + xor cx,cx ; ZF = 1 + pop cx + ret + ; Allocate a socket number. Socket numbers are made + ; guaranteed unique by including the socket slot number + ; (inverted, because we use the loop counter cx); add a + ; counter value to keep the numbers from being likely to + ; get immediately reused. + ; + ; The NextSocket variable also contains the top two bits + ; set. This generates a value in the range 49152 to + ; 57343. +.found: + dec cx + push ax + mov ax,[NextSocket] + inc ax + and ax,((1 << (13-MAX_OPEN_LG2))-1) | 0xC000 + mov [NextSocket],ax + shl cx,13-MAX_OPEN_LG2 + add cx,ax ; ZF = 0 + xchg ch,cl ; Convert to network byte order + mov [bx],cx ; Socket in use + pop ax + pop cx + ret + +; +; Free socket: socket in SI; return SI = 0, ZF = 1 for convenience +; +free_socket: + push es + pusha + xor ax,ax + mov es,ax + mov di,si + mov cx,tftp_pktbuf >> 1 ; tftp_pktbuf is not cleared + rep stosw + popa + pop es + xor si,si + ret + +; +; parse_dotquad: +; Read a dot-quad pathname in DS:SI and output an IP +; address in EAX, with SI pointing to the first +; nonmatching character. +; +; Return CF=1 on error. +; +; No segment assumptions permitted. +; +parse_dotquad: + push cx + mov cx,4 + xor eax,eax +.parseloop: + mov ch,ah + mov ah,al + lodsb + sub al,'0' + jb .notnumeric + cmp al,9 + ja .notnumeric + aad ; AL += 10 * AH; AH = 0; + xchg ah,ch + jmp .parseloop +.notnumeric: + cmp al,'.'-'0' + pushf + mov al,ah + mov ah,ch + xor ch,ch + ror eax,8 + popf + jne .error + loop .parseloop + jmp .done +.error: + loop .realerror ; If CX := 1 then we're done + clc + jmp .done +.realerror: + stc +.done: + dec si ; CF unchanged! + pop cx + ret + +; +; is_url: Return CF=0 if and only if the buffer pointed to by +; DS:SI is a URL (contains ://). No registers modified. +; +%if GPXE +is_url: + push si + push eax +.loop: + mov eax,[si] + inc si + and al,al + jz .not_url + and eax,0FFFFFFh + cmp eax,'://' + jne .loop +.done: + ; CF=0 here + pop eax + pop si + ret +.not_url: + stc + jmp .done + +; +; is_gpxe: Return CF=0 if and only if the buffer pointed to by +; DS:SI is a URL (contains ://) *and* the gPXE extensions +; API is available. No registers modified. +; +is_gpxe: + call is_url + jc .ret ; Not a URL, don't bother +.again: + cmp byte [HasGPXE],1 + ja .unknown + ; CF=1 if not available (0), + ; CF=0 if known available (1). +.ret: ret + +.unknown: + ; If we get here, the gPXE status is unknown. + push es + pushad + push ds + pop es + mov di,gpxe_file_api_check + mov bx,PXENV_FILE_API_CHECK ; BH = 0 + call pxenv + jc .nogood + cmp dword [di+4],0xe9c17b20 + jne .nogood + mov ax,[di+12] ; Don't care about the upper half... + not ax ; Set bits of *missing* functions... + and ax,01001011b ; The functions we care about + setz bh + jz .done +.nogood: + mov si,gpxe_warning_msg + call writestr_early +.done: + mov [HasGPXE],bh + popad + pop es + jmp .again + + section .data +gpxe_warning_msg: + db 'URL syntax, but gPXE extensions not detected, ' + db 'trying plain TFTP...', CR, LF, 0 +HasGPXE db -1 ; Unknown + section .text + +%endif + +; +; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed +; to by ES:DI; ends on encountering any whitespace. +; DI is preserved. +; +; This verifies that a filename is < FILENAME_MAX characters +; and doesn't contain whitespace, and zero-pads the output buffer, +; so "repe cmpsb" can do a compare. +; +; The first four bytes of the manged name is the IP address of +; the download host, 0 for no host, or -1 for a gPXE URL. +; +; No segment assumptions permitted. +; +mangle_name: + push di +%if GPXE + call is_url + jc .not_url + or eax,-1 ; It's a URL + jmp .prefix_done +.not_url: +%endif ; GPXE + push si + mov eax,[cs:ServerIP] + cmp byte [si],0 + je .noip ; Null filename?!?! + cmp word [si],'::' ; Leading ::? + je .gotprefix + +.more: + inc si + cmp byte [si],0 + je .noip + cmp word [si],'::' + jne .more + + ; We have a :: prefix of some sort, it could be either + ; a DNS name or a dot-quad IP address. Try the dot-quad + ; first... +.here: + pop si + push si + call parse_dotquad + jc .notdq + cmp word [si],'::' + je .gotprefix +.notdq: + pop si + push si + call dns_resolv + cmp word [si],'::' + jne .noip + and eax,eax + jnz .gotprefix + +.noip: + pop si + xor eax,eax + jmp .prefix_done + +.gotprefix: + pop cx ; Adjust stack + inc si ; Skip double colon + inc si + +.prefix_done: + stosd ; Save IP address prefix + mov cx,FILENAME_MAX-5 + +.mn_loop: + lodsb + cmp al,' ' ; If control or space, end + jna .mn_end + stosb + loop .mn_loop +.mn_end: + inc cx ; At least one null byte + xor ax,ax ; Zero-fill name + rep stosb ; Doesn't do anything if CX=0 + pop di + ret ; Done + +; +; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled +; filename to the conventional representation. This is needed +; for the BOOT_IMAGE= parameter for the kernel. +; +; NOTE: The output buffer needs to be able to hold an +; expanded IP address. +; +; DS:SI -> input mangled file name +; ES:DI -> output buffer +; +; On return, DI points to the first byte after the output name, +; which is set to a null byte. +; +unmangle_name: + push eax + lodsd + and eax,eax + jz .noip + cmp eax,-1 + jz .noip ; URL + call gendotquad + mov ax,'::' + stosw +.noip: + call strcpy + dec di ; Point to final null byte + pop eax + ret + +; +; pxenv +; +; This is the main PXENV+/!PXE entry point, using the PXENV+ +; calling convention. This is a separate local routine so +; we can hook special things from it if necessary. In particular, +; some PXE stacks seem to not like being invoked from anything but +; the initial stack, so humour it. +; +; While we're at it, save and restore all registers. +; +pxenv: + pushfd + pushad +%if USE_PXE_PROVIDED_STACK == 0 + mov [cs:PXEStack],sp + mov [cs:PXEStack+2],ss + lss sp,[cs:InitStack] +%endif + ; Pre-clear the Status field + mov word [es:di],cs + + ; This works either for the PXENV+ or the !PXE calling + ; convention, as long as we ignore CF (which is redundant + ; with AX anyway.) + push es + push di + push bx +.jump: call 0:0 + add sp,6 + mov [cs:PXEStatus],ax +%if USE_PXE_PROVIDED_STACK == 0 + lss sp,[cs:PXEStack] +%endif + mov bp,sp + and ax,ax + setnz [bp+32] ; If AX != 0 set CF on return + + ; This clobbers the AX return, but we already saved it into + ; the PXEStatus variable. + popad + popfd ; Restore flags (incl. IF, DF) + ret + +; Must be after function def due to NASM bug +PXEEntry equ pxenv.jump+1 + + section .bss + alignb 2 +PXEStatus resb 2 + + section .text + +; +; getfssec: Get multiple clusters from a file, given the starting cluster. +; +; In this case, get multiple blocks from a specific TCP connection. +; +; On entry: +; ES:BX -> Buffer +; SI -> TFTP socket pointer +; CX -> 512-byte block count; 0FFFFh = until end of file +; On exit: +; SI -> TFTP socket pointer (or 0 on EOF) +; CF = 1 -> Hit EOF +; ECX -> number of bytes actually read +; +getfssec: + push eax + push edi + push bx + push si + push fs + mov di,bx + mov ax,pktbuf_seg + mov fs,ax + + xor eax,eax + movzx ecx,cx + shl ecx,TFTP_BLOCKSIZE_LG2 ; Convert to bytes + push ecx ; Initial request size + jz .hit_eof ; Nothing to do? + +.need_more: + call fill_buffer + movzx eax,word [si+tftp_bytesleft] + and ax,ax + jz .hit_eof + + push ecx + cmp ecx,eax + jna .ok_size + mov ecx,eax +.ok_size: + mov ax,cx ; EAX<31:16> == ECX<31:16> == 0 + mov bx,[si+tftp_dataptr] + sub [si+tftp_bytesleft],cx + xchg si,bx + fs rep movsb ; Copy from packet buffer + xchg si,bx + mov [si+tftp_dataptr],bx + + pop ecx + sub ecx,eax + jnz .need_more + +.hit_eof: + call fill_buffer + + pop eax ; Initial request amount + xchg eax,ecx + sub ecx,eax ; ... minus anything not gotten + + pop fs + pop si + + ; Is there anything left of this? + mov eax,[si+tftp_filesize] + sub eax,[si+tftp_filepos] + jnz .bytes_left + + cmp [si+tftp_bytesleft],ax ; AX == 0 + jne .bytes_left + + cmp byte [si+tftp_goteof],0 + je .done + ; I'm 99% sure this can't happen, but... + call fill_buffer ; Receive/ACK the EOF packet +.done: + ; The socket is closed and the buffer drained + ; Close socket structure and re-init for next user + call free_socket + stc + jmp .ret +.bytes_left: + clc +.ret: + pop bx + pop edi + pop eax + ret + +; +; Get a fresh packet if the buffer is drained, and we haven't hit +; EOF yet. The buffer should be filled immediately after draining! +; +; expects fs -> pktbuf_seg and ds:si -> socket structure +; +fill_buffer: + cmp word [si+tftp_bytesleft],0 + je .empty + ret ; Otherwise, nothing to do + +.empty: + push es + pushad + mov ax,ds + mov es,ax + + ; Note: getting the EOF packet is not the same thing + ; as tftp_filepos == tftp_filesize; if the EOF packet + ; is empty the latter condition can be true without + ; having gotten the official EOF. + cmp byte [si+tftp_goteof],0 + jne .ret ; Already EOF + +%if GPXE + cmp word [si+tftp_localport], -1 + jne .get_packet_tftp + call get_packet_gpxe + jmp .ret +.get_packet_tftp: +%endif ; GPXE + + ; TFTP code... +.packet_loop: + ; Start by ACKing the previous packet; this should cause the + ; next packet to be sent. + mov bx,TimeoutTable + +.send_ack: push bx ; Retry pointer + movzx cx,byte [bx] ; Timeout + + mov ax,[si+tftp_lastpkt] + call ack_packet ; Send ACK + + ; We used to test the error code here, but sometimes + ; PXE would return negative status even though we really + ; did send the ACK. Now, just treat a failed send as + ; a normally lost packet, and let it time out in due + ; course of events. + +.send_ok: ; Now wait for packet. + mov dx,[BIOS_timer] ; Get current time + +.wait_data: push cx ; Timeout + push dx ; Old time + + mov bx,[si+tftp_pktbuf] + mov [pxe_udp_read_pkt.buffer],bx + mov [pxe_udp_read_pkt.buffer+2],fs + mov [pxe_udp_read_pkt.buffersize],word PKTBUF_SIZE + mov eax,[si+tftp_remoteip] + mov [pxe_udp_read_pkt.sip],eax + mov eax,[MyIP] + mov [pxe_udp_read_pkt.dip],eax + mov ax,[si+tftp_remoteport] + mov [pxe_udp_read_pkt.rport],ax + mov ax,[si+tftp_localport] + mov [pxe_udp_read_pkt.lport],ax + mov di,pxe_udp_read_pkt + mov bx,PXENV_UDP_READ + call pxenv + jnc .recv_ok + + ; No packet, or receive failure + mov dx,[BIOS_timer] + pop ax ; Old time + pop cx ; Timeout + cmp ax,dx ; Same time -> don't advance timeout + je .wait_data ; Same clock tick + loop .wait_data ; Decrease timeout + + pop bx ; Didn't get any, send another ACK + inc bx + cmp bx,TimeoutTableEnd + jb .send_ack + jmp kaboom ; Forget it... + +.recv_ok: pop dx ; + pop cx ; + + cmp word [pxe_udp_read_pkt.buffersize],byte 4 + jb .wait_data ; Bad size for a DATA packet + + mov bx,[si+tftp_pktbuf] + cmp word [fs:bx],TFTP_DATA ; Not a data packet? + jne .wait_data ; Then wait for something else + + mov ax,[si+tftp_lastpkt] + xchg ah,al ; Host byte order + inc ax ; Which packet are we waiting for? + xchg ah,al ; Network byte order + cmp [fs:bx+2],ax + je .right_packet + + ; Wrong packet, ACK the packet and then try again + ; This is presumably because the ACK got lost, + ; so the server just resent the previous packet + mov ax,[fs:bx+2] + call ack_packet + jmp .send_ok ; Reset timeout + +.right_packet: ; It's the packet we want. We're also EOF if the + ; size < blocksize + + pop cx ; Don't need the retry count anymore + + mov [si+tftp_lastpkt],ax ; Update last packet number + + movzx ecx,word [pxe_udp_read_pkt.buffersize] + sub cx,byte 4 ; Skip TFTP header + + ; Set pointer to data block + lea ax,[bx+4] ; Data past TFTP header + mov [si+tftp_dataptr],ax + + add [si+tftp_filepos],ecx + mov [si+tftp_bytesleft],cx + + cmp cx,[si+tftp_blksize] ; Is it a full block? + jb .last_block ; If not, it's EOF + +.ret: + popad + pop es + ret + + +.last_block: ; Last block - ACK packet immediately + mov ax,[fs:bx+2] + call ack_packet + + ; Make sure we know we are at end of file + mov eax,[si+tftp_filepos] + mov [si+tftp_filesize],eax + mov byte [si+tftp_goteof],1 + + jmp .ret + +; +; TimeoutTable: list of timeouts (in 18.2 Hz timer ticks) +; +; This is roughly an exponential backoff... +; + section .data +TimeoutTable: + db 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18 + db 21, 26, 31, 37, 44, 53, 64, 77, 92, 110, 132 + db 159, 191, 229, 255, 255, 255, 255 +TimeoutTableEnd equ $ + + section .text +; +; ack_packet: +; +; Send ACK packet. This is a common operation and so is worth canning. +; +; Entry: +; SI = TFTP block +; AX = Packet # to ack (network byte order) +; Exit: +; All registers preserved +; +; This function uses the pxe_udp_write_pkt but not the packet_buf. +; +ack_packet: + pushad + mov [ack_packet_buf+2],ax ; Packet number to ack + mov ax,[si] + mov [pxe_udp_write_pkt.lport],ax + mov ax,[si+tftp_remoteport] + mov [pxe_udp_write_pkt.rport],ax + mov eax,[si+tftp_remoteip] + mov [pxe_udp_write_pkt.sip],eax + xor eax,[MyIP] + and eax,[Netmask] + jz .nogw + mov eax,[Gateway] +.nogw: + mov [pxe_udp_write_pkt.gip],eax + mov [pxe_udp_write_pkt.buffer],word ack_packet_buf + mov [pxe_udp_write_pkt.buffersize], word 4 + mov di,pxe_udp_write_pkt + mov bx,PXENV_UDP_WRITE + call pxenv + popad + ret + +%if GPXE +; +; Get a fresh packet from a gPXE socket; expects fs -> pktbuf_seg +; and ds:si -> socket structure +; +; Assumes CS == DS == ES. +; +get_packet_gpxe: + mov di,gpxe_file_read + + mov ax,[si+tftp_remoteport] ; gPXE filehandle + mov [di+2],ax + mov ax,[si+tftp_pktbuf] + mov [di+6],ax + mov [si+tftp_dataptr],ax + mov [di+8],fs + +.again: + mov word [di+4],PKTBUF_SIZE + mov bx,PXENV_FILE_READ + call pxenv + jnc .ok ; Got data or EOF + cmp word [di],PXENV_STATUS_TFTP_OPEN ; == EWOULDBLOCK + je .again + jmp kaboom ; Otherwise error... + +.ok: + movzx eax,word [di+4] ; Bytes read + mov [si+tftp_bytesleft],ax ; Bytes in buffer + add [si+tftp_filepos],eax ; Position in file + + and ax,ax ; EOF? + mov eax,[si+tftp_filepos] + + jnz .got_stuff + + ; We got EOF here, make sure the upper layers know + mov [si+tftp_filesize],eax + +.got_stuff: + ; If we're done here, close the file + cmp [si+tftp_filesize],eax + ja .done ; Not EOF, there is still data... + + ; Reuse the previous [es:di] structure since the + ; relevant fields are all the same + mov byte [si+tftp_goteof],1 + + mov bx,PXENV_FILE_CLOSE + call pxenv + ; Ignore return... +.done: + ret +%endif ; GPXE + +; +; unload_pxe: +; +; This function unloads the PXE and UNDI stacks and unclaims +; the memory. +; +unload_pxe: + cmp byte [KeepPXE],0 ; Should we keep PXE around? + jne reset_pxe + + push ds + push es + + mov ax,cs + mov ds,ax + mov es,ax + + mov si,new_api_unload + cmp byte [APIVer+1],2 ; Major API version >= 2? + jae .new_api + mov si,old_api_unload +.new_api: + +.call_loop: xor ax,ax + lodsb + and ax,ax + jz .call_done + xchg bx,ax + mov di,pxe_unload_stack_pkt + push di + xor ax,ax + mov cx,pxe_unload_stack_pkt_len >> 1 + rep stosw + pop di + call pxenv + jc .cant_free + mov ax,word [pxe_unload_stack_pkt.status] + cmp ax,PXENV_STATUS_SUCCESS + jne .cant_free + jmp .call_loop + +.call_done: + mov bx,0FF00h + + mov dx,[RealBaseMem] + cmp dx,[BIOS_fbm] ; Sanity check + jna .cant_free + inc bx + + ; Check that PXE actually unhooked the INT 1Ah chain + movzx eax,word [4*0x1a] + movzx ecx,word [4*0x1a+2] + shl ecx,4 + add eax,ecx + shr eax,10 + cmp ax,dx ; Not in range + jae .ok + cmp ax,[BIOS_fbm] + jae .cant_free + ; inc bx + +.ok: + mov [BIOS_fbm],dx +.pop_ret: + pop es + pop ds + ret + +.cant_free: + mov si,cant_free_msg + call writestr_early + push ax + xchg bx,ax + call writehex4 + mov al,'-' + call writechr + pop ax + call writehex4 + mov al,'-' + call writechr + mov eax,[4*0x1a] + call writehex8 + call crlf + jmp .pop_ret + + ; We want to keep PXE around, but still we should reset + ; it to the standard bootup configuration +reset_pxe: + push es + push cs + pop es + mov bx,PXENV_UDP_CLOSE + mov di,pxe_udp_close_pkt + call pxenv + pop es + ret + +; +; gendotquad +; +; Take an IP address (in network byte order) in EAX and +; output a dotted quad string to ES:DI. +; DI points to terminal null at end of string on exit. +; +gendotquad: + push eax + push cx + mov cx,4 +.genchar: + push eax + cmp al,10 ; < 10? + jb .lt10 ; If so, skip first 2 digits + + cmp al,100 ; < 100 + jb .lt100 ; If so, skip first digit + + aam 100 + ; Now AH = 100-digit; AL = remainder + add ah,'0' + mov [es:di],ah + inc di + +.lt100: + aam 10 + ; Now AH = 10-digit; AL = remainder + add ah,'0' + mov [es:di],ah + inc di + +.lt10: + add al,'0' + stosb + mov al,'.' + stosb + pop eax + ror eax,8 ; Move next char into LSB + loop .genchar + dec di + mov [es:di], byte 0 + pop cx + pop eax + ret +; +; uchexbytes/lchexbytes +; +; Take a number of bytes in memory and convert to upper/lower-case +; hexadecimal +; +; Input: +; DS:SI = input bytes +; ES:DI = output buffer +; CX = number of bytes +; Output: +; DS:SI = first byte after +; ES:DI = first byte after +; CX = 0 +; +; Trashes AX, DX +; + +lchexbytes: + mov dl,'a'-'9'-1 + jmp xchexbytes +uchexbytes: + mov dl,'A'-'9'-1 +xchexbytes: +.loop: + lodsb + mov ah,al + shr al,4 + call .outchar + mov al,ah + call .outchar + loop .loop + ret +.outchar: + and al,0Fh + add al,'0' + cmp al,'9' + jna .done + add al,dl +.done: + stosb + ret + +; +; pxe_get_cached_info +; +; Get a DHCP packet from the PXE stack into the trackbuf. +; +; Input: +; DL = packet type +; Output: +; CX = buffer size +; +; Assumes CS == DS == ES. +; +pxe_get_cached_info: + pushad + mov al,' ' + call writechr + mov al,dl + call writehex2 + mov di,pxe_bootp_query_pkt + push di + xor ax,ax + stosw ; Status + movzx ax,dl + stosw ; Packet type + mov ax,trackbufsize + stosw ; Buffer size + mov ax,trackbuf + stosw ; Buffer offset + xor ax,ax + stosw ; Buffer segment + + pop di ; DI -> parameter set + mov bx,PXENV_GET_CACHED_INFO + call pxenv + jc .err + + popad + mov cx,[pxe_bootp_query_pkt.buffersize] + ret + +.err: + mov si,err_pxefailed + call writestr_early + call writehex4 + call crlf + jmp kaboom + + section .data +get_packet_msg db 'Getting cached packet', 0 + + section .text +; +; ip_ok +; +; Tests an IP address in EAX for validity; return with ZF=1 for bad. +; We used to refuse class E, but class E addresses are likely to become +; assignable unicast addresses in the near future. +; +ip_ok: + push ax + cmp eax,-1 ; Refuse the all-ones address + jz .out + and al,al ; Refuse network zero + jz .out + cmp al,127 ; Refuse loopback + jz .out + and al,0F0h + cmp al,224 ; Refuse class D +.out: + pop ax + ret + +; +; parse_dhcp +; +; Parse a DHCP packet. This includes dealing with "overloaded" +; option fields (see RFC 2132, section 9.3) +; +; This should fill in the following global variables, if the +; information is present: +; +; MyIP - client IP address +; ServerIP - boot server IP address +; Netmask - network mask +; Gateway - default gateway router IP +; BootFile - boot file name +; DNSServers - DNS server IPs +; LocalDomain - Local domain name +; MACLen, MAC - Client identifier, if MACLen == 0 +; +; This assumes the DHCP packet is in "trackbuf" and the length +; of the packet in in CX on entry. +; + +parse_dhcp: + mov byte [OverLoad],0 ; Assume no overload + mov eax, [trackbuf+bootp.yip] + call ip_ok + jz .noyip + mov [MyIP], eax +.noyip: + mov eax, [trackbuf+bootp.sip] + and eax, eax + call ip_ok + jz .nosip + mov [ServerIP], eax +.nosip: + sub cx, bootp.options + jbe .nooptions + mov si, trackbuf+bootp.option_magic + lodsd + cmp eax, BOOTP_OPTION_MAGIC + jne .nooptions + call parse_dhcp_options +.nooptions: + mov si, trackbuf+bootp.bootfile + test byte [OverLoad],1 + jz .nofileoverload + mov cx,128 + call parse_dhcp_options + jmp short .parsed_file +.nofileoverload: + cmp byte [si], 0 + jz .parsed_file ; No bootfile name + mov di,BootFile + mov cx,32 + rep movsd + xor al,al + stosb ; Null-terminate +.parsed_file: + mov si, trackbuf+bootp.sname + test byte [OverLoad],2 + jz .nosnameoverload + mov cx,64 + call parse_dhcp_options +.nosnameoverload: + ret + +; +; Parse a sequence of DHCP options, pointed to by DS:SI; the field +; size is CX -- some DHCP servers leave option fields unterminated +; in violation of the spec. +; +; For parse_some_dhcp_options, DH contains the minimum value for +; the option to recognize -- this is used to restrict parsing to +; PXELINUX-specific options only. +; +parse_dhcp_options: + xor dx,dx + +parse_some_dhcp_options: +.loop: + and cx,cx + jz .done + + lodsb + dec cx + jz .done ; Last byte; must be PAD, END or malformed + cmp al, 0 ; PAD option + je .loop + cmp al,255 ; END option + je .done + + ; Anything else will have a length field + mov dl,al ; DL <- option number + xor ax,ax + lodsb ; AX <- option length + dec cx + sub cx,ax ; Decrement bytes left counter + jb .done ; Malformed option: length > field size + + cmp dl,dh ; Is the option value valid? + jb .opt_done + + mov bx,dhcp_option_list +.find_option: + cmp bx,dhcp_option_list_end + jae .opt_done + cmp dl,[bx] + je .found_option + add bx,3 + jmp .find_option +.found_option: + pushad + call [bx+1] + popad + +; Fall through + ; Unknown option. Skip to the next one. +.opt_done: + add si,ax + jmp .loop +.done: + ret + + section .data +dhcp_option_list: + section .text + +%macro dopt 2 + section .data + db %1 + dw dopt_%2 + section .text +dopt_%2: +%endmacro + +; +; Parse individual DHCP options. SI points to the option data and +; AX to the option length. DL contains the option number. +; All registers are saved around the routine. +; + dopt 1, subnet_mask + mov ebx,[si] + mov [Netmask],ebx + ret + + dopt 3, router + mov ebx,[si] + mov [Gateway],ebx + ret + + dopt 6, dns_servers + mov cx,ax + shr cx,2 + cmp cl,DNS_MAX_SERVERS + jna .oklen + mov cl,DNS_MAX_SERVERS +.oklen: + mov di,DNSServers + rep movsd + mov [LastDNSServer],di + ret + + dopt 15, local_domain + mov bx,si + add bx,ax + xor ax,ax + xchg [bx],al ; Zero-terminate option + mov di,LocalDomain + call dns_mangle ; Convert to DNS label set + mov [bx],al ; Restore ending byte + ret + + dopt 43, vendor_encaps + mov dh,208 ; Only recognize PXELINUX options + mov cx,ax ; Length of option = max bytes to parse + call parse_some_dhcp_options ; Parse recursive structure + ret + + dopt 52, option_overload + mov bl,[si] + mov [OverLoad],bl + ret + + dopt 54, server + mov eax,[si] + cmp dword [ServerIP],0 + jne .skip ; Already have a next server IP + call ip_ok + jz .skip + mov [ServerIP],eax +.skip: ret + + dopt 61, client_identifier + cmp ax,MAC_MAX ; Too long? + ja .skip + cmp ax,2 ; Too short? + jb .skip + cmp [MACLen],ah ; Only do this if MACLen == 0 + jne .skip + push ax + lodsb ; Client identifier type + cmp al,[MACType] + pop ax + jne .skip ; Client identifier is not a MAC + dec ax + mov [MACLen],al + mov di,MAC + jmp dhcp_copyoption +.skip: ret + + dopt 67, bootfile_name + mov di,BootFile + jmp dhcp_copyoption + + dopt 97, uuid_client_identifier + cmp ax,17 ; type byte + 16 bytes UUID + jne .skip + mov dl,[si] ; Must have type 0 == UUID + or dl,[HaveUUID] ; Capture only the first instance + jnz .skip + mov byte [HaveUUID],1 ; Got UUID + mov di,UUIDType + jmp dhcp_copyoption +.skip: ret + + dopt 209, pxelinux_configfile + mov di,ConfigName + or byte [DHCPMagic],2 ; Got config file + jmp dhcp_copyoption + + dopt 210, pxelinux_pathprefix + mov di,PathPrefix + or byte [DHCPMagic],4 ; Got path prefix + jmp dhcp_copyoption + + dopt 211, pxelinux_reboottime + cmp al,4 + jne .done + mov ebx,[si] + xchg bl,bh ; Convert to host byte order + rol ebx,16 + xchg bl,bh + mov [RebootTime],ebx + or byte [DHCPMagic],8 ; Got RebootTime +.done: ret + + ; Common code for copying an option verbatim + ; Copies the option into ES:DI and null-terminates it. + ; Returns with AX=0 and SI past the option. +dhcp_copyoption: + xchg cx,ax ; CX <- option length + rep movsb + xchg cx,ax ; AX <- 0 + stosb ; Null-terminate + ret + + section .data +dhcp_option_list_end: + section .text + + section .data +HaveUUID db 0 +uuid_dashes db 4,2,2,2,6,0 ; Bytes per UUID dashed section + section .text + +; +; genipopt +; +; Generate an ip=::: +; option into IPOption based on a DHCP packet in trackbuf. +; Assumes CS == DS == ES. +; +genipopt: + pushad + mov di,IPOption + mov eax,'ip=' + stosd + dec di + mov eax,[MyIP] + call gendotquad + mov al,':' + stosb + mov eax,[ServerIP] + call gendotquad + mov al,':' + stosb + mov eax,[Gateway] + call gendotquad + mov al,':' + stosb + mov eax,[Netmask] + call gendotquad ; Zero-terminates its output + popad + ret + +; ----------------------------------------------------------------------------- +; Common modules +; ----------------------------------------------------------------------------- + +%include "getc.inc" ; getc et al +%include "conio.inc" ; Console I/O +%include "writestr.inc" ; String output +writestr_early equ writestr +%include "writehex.inc" ; Hexadecimal output +%include "configinit.inc" ; Initialize configuration +%include "parseconfig.inc" ; High-level config file handling +%include "parsecmd.inc" ; Low-level config file handling +%include "bcopy32.inc" ; 32-bit bcopy +%include "loadhigh.inc" ; Load a file into high memory +%include "font.inc" ; VGA font stuff +%include "graphics.inc" ; VGA graphics +%include "highmem.inc" ; High memory sizing +%include "strcpy.inc" ; strcpy() +%include "rawcon.inc" ; Console I/O w/o using the console functions +%include "dnsresolv.inc" ; DNS resolver +%include "idle.inc" ; Idle handling +%include "pxeidle.inc" ; PXE-specific idle mechanism +%include "adv.inc" ; Auxillary Data Vector + +; ----------------------------------------------------------------------------- +; Begin data section +; ----------------------------------------------------------------------------- + + section .data + +copyright_str db ' Copyright (C) 1994-' + asciidec YEAR + db ' H. Peter Anvin et al', CR, LF, 0 +err_bootfailed db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0 +bailmsg equ err_bootfailed +err_nopxe db "No !PXE or PXENV+ API found; we're dead...", CR, LF, 0 +err_pxefailed db 'PXE API call failed, error ', 0 +err_udpinit db 'Failed to initialize UDP stack', CR, LF, 0 +err_noconfig db 'Unable to locate configuration file', CR, LF, 0 +err_damage db 'TFTP server sent an incomprehesible reply', CR, LF, 0 +found_pxenv db 'Found PXENV+ structure', CR, LF, 0 +apiver_str db 'PXE API version is ',0 +pxeentry_msg db '!PXE entry point found (we hope) at ', 0 +pxenventry_msg db 'PXENV+ entry point found (we hope) at ', 0 +viaplan_msg db ' via plan ' +plan db 'A', CR, LF, 0 +trymempxe_msg db 'Scanning memory for !PXE structure... ', 0 +trymempxenv_msg db 'Scanning memory for PXENV+ structure... ', 0 +undi_data_msg db 'UNDI data segment at ',0 +undi_code_msg db 'UNDI code segment at ',0 +len_msg db ' len ', 0 +cant_free_msg db 'Failed to free base memory, error ', 0 +notfound_msg db 'not found', CR, LF, 0 +myipaddr_msg db 'My IP address seems to be ',0 +tftpprefix_msg db 'TFTP prefix: ', 0 +localboot_msg db 'Booting from local disk...', CR, LF, 0 +trying_msg db 'Trying to load: ', 0 +default_str db 'default', 0 +syslinux_banner db CR, LF, 'PXELINUX ', VERSION_STR, ' ', DATE_STR, ' ', 0 +cfgprefix db 'pxelinux.cfg/' ; No final null! +cfgprefix_len equ ($-cfgprefix) + +; This one we make ourselves +bootif_str db 'BOOTIF=' +bootif_str_len equ $-bootif_str +; +; Config file keyword table +; +%include "keywords.inc" + +; +; Extensions to search for (in *forward* order). +; (.bs and .bss are disabled for PXELINUX, since they are not supported) +; + alignz 4 +exten_table: db '.cbt' ; COMBOOT (specific) + db '.0', 0, 0 ; PXE bootstrap program + db '.com' ; COMBOOT (same as DOS) + db '.c32' ; COM32 +exten_table_end: + dd 0, 0 ; Need 8 null bytes here + +; +; PXE unload sequences +; +new_api_unload: + db PXENV_UDP_CLOSE + db PXENV_UNDI_SHUTDOWN + db PXENV_UNLOAD_STACK + db PXENV_STOP_UNDI + db 0 +old_api_unload: + db PXENV_UDP_CLOSE + db PXENV_UNDI_SHUTDOWN + db PXENV_UNLOAD_STACK + db PXENV_UNDI_CLEANUP + db 0 + +; +; PXE query packets partially filled in +; + section .bss +pxe_bootp_query_pkt: +.status: resw 1 ; Status +.packettype: resw 1 ; Boot server packet type +.buffersize: resw 1 ; Packet size +.buffer: resw 2 ; seg:off of buffer +.bufferlimit: resw 1 ; Unused + + section .data +pxe_udp_open_pkt: +.status: dw 0 ; Status +.sip: dd 0 ; Source (our) IP + +pxe_udp_close_pkt: +.status: dw 0 ; Status + +pxe_udp_write_pkt: +.status: dw 0 ; Status +.sip: dd 0 ; Server IP +.gip: dd 0 ; Gateway IP +.lport: dw 0 ; Local port +.rport: dw 0 ; Remote port +.buffersize: dw 0 ; Size of packet +.buffer: dw 0, 0 ; seg:off of buffer + +pxe_udp_read_pkt: +.status: dw 0 ; Status +.sip: dd 0 ; Source IP +.dip: dd 0 ; Destination (our) IP +.rport: dw 0 ; Remote port +.lport: dw 0 ; Local port +.buffersize: dw 0 ; Max packet size +.buffer: dw 0, 0 ; seg:off of buffer + +%if GPXE + +gpxe_file_api_check: +.status: dw 0 ; Status +.size: dw 20 ; Size in bytes +.magic: dd 0x91d447b2 ; Magic number +.provider: dd 0 +.apimask: dd 0 +.flags: dd 0 + +gpxe_file_open: +.status: dw 0 ; Status +.filehandle: dw 0 ; FileHandle +.filename: dd 0 ; seg:off of FileName +.reserved: dd 0 + +gpxe_get_file_size: +.status: dw 0 ; Status +.filehandle: dw 0 ; FileHandle +.filesize: dd 0 ; FileSize + +gpxe_file_read: +.status: dw 0 ; Status +.filehandle: dw 0 ; FileHandle +.buffersize: dw 0 ; BufferSize +.buffer: dd 0 ; seg:off of buffer + +%endif ; GPXE + +; +; Misc initialized (data) variables +; + alignz 4 +BaseStack dd StackBuf ; ESP of base stack + dw 0 ; SS of base stack +NextSocket dw 49152 ; Counter for allocating socket numbers +KeepPXE db 0 ; Should PXE be kept around? + +; +; TFTP commands +; +tftp_tail db 'octet', 0 ; Octet mode +tsize_str db 'tsize' ,0 ; Request size +tsize_len equ ($-tsize_str) + db '0', 0 +blksize_str db 'blksize', 0 ; Request large blocks +blksize_len equ ($-blksize_str) + asciidec TFTP_LARGEBLK + db 0 +tftp_tail_len equ ($-tftp_tail) + + alignz 2 +; +; Options negotiation parsing table (string pointer, string len, offset +; into socket structure) +; +tftp_opt_table: + dw tsize_str, tsize_len, tftp_filesize + dw blksize_str, blksize_len, tftp_blksize +tftp_opts equ ($-tftp_opt_table)/6 + +; +; Error packet to return on TFTP protocol error +; Most of our errors are OACK parsing errors, so use that error code +; +tftp_proto_err dw TFTP_ERROR ; ERROR packet + dw TFTP_EOPTNEG ; ERROR 8: OACK error + db 'TFTP protocol error', 0 ; Error message +tftp_proto_err_len equ ($-tftp_proto_err) + + alignz 4 +ack_packet_buf: dw TFTP_ACK, 0 ; TFTP ACK packet + +; +; IP information (initialized to "unknown" values) +MyIP dd 0 ; My IP address +ServerIP dd 0 ; IP address of boot server +Netmask dd 0 ; Netmask of this subnet +Gateway dd 0 ; Default router +ServerPort dw TFTP_PORT ; TFTP server port + +; +; Variables that are uninitialized in SYSLINUX but initialized here +; + alignz 4 +BufSafe dw trackbufsize/TFTP_BLOCKSIZE ; Clusters we can load into trackbuf +BufSafeBytes dw trackbufsize ; = how many bytes? +%ifndef DEPEND +%if ( trackbufsize % TFTP_BLOCKSIZE ) != 0 +%error trackbufsize must be a multiple of TFTP_BLOCKSIZE +%endif +%endif diff --git a/core/pxelinux.bin b/core/pxelinux.bin new file mode 100755 index 0000000000000000000000000000000000000000..743948e3d27b083f1c5457706ec422c166a51421 GIT binary patch literal 16138 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 literal 0 HcmV?d00001 diff --git a/core/rawcon.inc b/core/rawcon.inc new file mode 100644 index 0000000..f0d434c --- /dev/null +++ b/core/rawcon.inc @@ -0,0 +1,75 @@ +; +; writechr: Write a single character in AL to the console without +; mangling any registers. This does raw console writes, +; since some PXE BIOSes seem to interfere regular console I/O. +; +%if IS_ISOLINUX +writechr_full: +%else +writechr: +%endif + pushfd + push ds + push cs + pop ds + test byte [UsingVGA], 08h + jz .videook + call vgaclearmode +.videook: + call write_serial ; write to serial port if needed + test byte [DisplayCon],01h ; Write to screen? + jz .nothing + + pushad + mov bh,[BIOS_page] + push ax + mov ah,03h ; Read cursor position + int 10h + pop ax + cmp al,8 + je .bs + cmp al,13 + je .cr + cmp al,10 + je .lf + push dx + mov bh,[BIOS_page] + mov bl,07h ; White on black + mov cx,1 ; One only + mov ah,09h ; Write char and attribute + int 10h + pop dx + inc dl + cmp dl,[VidCols] + jna .curxyok + xor dl,dl +.lf: inc dh + cmp dh,[VidRows] + ja .scroll +.curxyok: mov bh,[BIOS_page] + mov ah,02h ; Set cursor position + int 10h +.ret: popad +.nothing: + pop ds + popfd + ret +.scroll: dec dh + mov bh,[BIOS_page] + mov ah,02h + int 10h + mov ax,0601h ; Scroll up one line + mov bh,[ScrollAttribute] + xor cx,cx + mov dx,[ScreenSize] ; The whole screen + int 10h + jmp short .ret +.cr: xor dl,dl + jmp short .curxyok +.bs: sub dl,1 + jnc .curxyok + mov dl,[VidCols] + sub dh,1 + jnc .curxyok + xor dh,dh + jmp short .curxyok diff --git a/core/regdump.inc b/core/regdump.inc new file mode 100644 index 0000000..bdb5172 --- /dev/null +++ b/core/regdump.inc @@ -0,0 +1,108 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 2003-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; regdump.inc +;; +;; Dump as much as possible of the register state; for debugging +;; + +disk_dumpregs: + mov ah,02h + call dumpregs + int 13h + ret + +dumpregs: + push gs + push fs + push es + push ds + push ss + push cs + pushad + pushfd + + push cs + pop ds + + mov bp,sp + mov di,regnames + + mov cx,9 ; 9 32-bit registers +.reg8: + mov si,[di] + inc di + inc di + call writestr + mov eax,[bp] + add bp,4 + call writehex8 + loop .reg8 + + mov cx,7 ; 6 16-bit registers +.reg4: + mov si,[di] + inc di + inc di + call writestr + mov eax,[bp] + inc bp + inc bp + call writehex4 + loop .reg4 + + call crlf + + popfd + popad + add sp,4 ; Skip CS, SS + pop ds + pop es + pop fs + pop gs + ret + +regnames: + dw .eflags + dw .edi + dw .esi + dw .ebp + dw .esp + dw .ebx + dw .edx + dw .ecx + dw .eax + dw .cs + dw .ss + dw .ds + dw .es + dw .fs + dw .gs + dw .ip + +.eflags db 'EFL: ', 0 +.edi db 13,10,'EDI: ', 0 +.esi db ' ESI: ', 0 +.ebp db ' EBP: ', 0 +.esp db ' ESP: ', 0 +.ebx db 13,10,'EBX: ', 0 +.edx db ' EDX: ', 0 +.ecx db ' ECX: ', 0 +.eax db ' EAX: ', 0 +.cs db 13,10,'CS: ',0 +.ss db ' SS: ',0 +.ds db ' DS: ',0 +.es db ' ES: ',0 +.fs db ' FS: ',0 +.gs db ' GS: ',0 +.ip db ' IP: ',0 diff --git a/core/rllpack.inc b/core/rllpack.inc new file mode 100644 index 0000000..c3bca69 --- /dev/null +++ b/core/rllpack.inc @@ -0,0 +1,164 @@ +; -*- fundamental -*- --------------------------------------------------- +; +; Copyright 2007-2009 H. Peter Anvin - All Rights Reserved +; Copyright 2009 Intel Corporation; author: H. Peter Anvin +; +; 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, Inc., 53 Temple Place Ste 330, +; Boston MA 02111-1307, USA; either version 2 of the License, or +; (at your option) any later version; incorporated herein by reference. +; +; ----------------------------------------------------------------------- + +; +; rllpack.inc +; +; Very simple RLL compressor/decompressor, used to pack binary structures +; together. +; +; Format of leading byte +; 1-128 = x verbatim bytes follow +; 129-223 = (x-126) times subsequent byte +; 224-255 = (x-224)*256+(next byte) times the following byte +; 0 = end of data +; +; These structures are stored *in reverse order* in high memory. +; High memory pointers point to one byte beyond the end. +; + + section .text + +; +; rllpack: +; Pack ECX bytes from ESI into EDI. +; Returns updated ESI and EDI. +; +rllpack: + push word .pmentry + call simple_pm_call + ret + + bits 32 +.pmentry: + push ecx + push ebx + push edx +.startseq: + xor eax,eax ; Zero byte + xor ebx,ebx ; Run length zero + dec edi + mov edx,edi ; Pointer to header byte + mov [edi],al ; Create header byte + jcxz .done ; If done, this was the terminator +.stdbyte: + lodsb + dec edi + mov [edi],al + dec ecx + cmp ah,al + je .same +.diff: + mov ah,al + xor ebx,ebx +.plainbyte: + inc ebx + inc byte [edx] + jcxz .startseq + jns .stdbyte + jmp .startseq +.same: + cmp bl,2 + jb .plainbyte + ; 3 bytes or more in a row, time to convert sequence + sub [edx],bl + jnz .normal + inc edi ; We killed a whole stretch, + ; drop start byte +.normal: + inc ebx + add edi,ebx ; Remove the stored run bytes +.getrun: + jcxz .nomatch + lodsb + cmp al,ah + jne .nomatch + cmp bx,(256-224)*256-1 ; Maximum run size + jae .nomatch + inc ebx + dec ecx + jmp .getrun +.nomatch: + cmp bx,224-126 + jae .twobyte +.onebyte: + add bl,126 + dec edi + mov [edi],bl + jmp .storebyte +.twobyte: + add bh,224 + sub edi,2 + mov [edi],bx +.storebyte: + dec edi + mov [edi],ah + dec esi ; Reload subsequent byte + jmp .startseq +.done: + pop edx + pop ebx + pop ecx + ret + + bits 16 +; +; rllunpack: +; Unpack bytes from ESI into EDI +; On return ESI, EDI are updated and +; ECX contains number of bytes output. +; +rllunpack: + push word .pmentry + call simple_pm_call + ret + + bits 32 +.pmentry: + push edi + xor ecx,ecx +.header: + dec esi + mov cl,[esi] + jcxz .done + cmp cl,129 + jae .isrun + ; Not a run +.copy: + dec esi + mov al,[esi] + stosb + loop .copy + jmp .header +.isrun: + cmp cl,224 + jae .longrun + sub cl,126 +.dorun: + dec esi + mov al,[esi] + rep stosb + jmp .header +.longrun: + sub cl,224 + mov ch,cl + dec esi + mov cl,[esi] + jmp .dorun +.done: + pop ecx + sub ecx,edi + neg ecx + ret + + bits 16 diff --git a/core/runkernel.inc b/core/runkernel.inc new file mode 100644 index 0000000..8bfc8b8 --- /dev/null +++ b/core/runkernel.inc @@ -0,0 +1,683 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved +;; Copyright 2009 Intel Corporation; author: H. Peter Anvin +;; +;; 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; runkernel.inc +;; +;; Common code for running a Linux kernel +;; + +; +; Hook macros, that may or may not be defined +; +%ifndef HAVE_UNLOAD_PREP +%macro UNLOAD_PREP 0 +%endmacro +%endif + +; +; A Linux kernel consists of three parts: boot sector, setup code, and +; kernel code. The boot sector is never executed when using an external +; booting utility, but it contains some status bytes that are necessary. +; +; First check that our kernel is at least 1K, or else it isn't long +; enough to have the appropriate headers. +; +; We used to require the kernel to be 64K or larger, but it has gotten +; popular to use the Linux kernel format for other things, which may +; not be so large. +; +; Additionally, we used to have a test for 8 MB or smaller. Equally +; obsolete. +; +is_linux_kernel: + push si ; file pointer + +; +; Now start transferring the kernel +; + push word real_mode_seg + pop es + +; +; Start by loading the bootsector/setup code, to see if we need to +; do something funky. It should fit in the first 32K (loading 64K won't +; work since we might have funny stuff up near the end of memory). +; + call abort_check ; Check for abort key + mov cx,8000h >> SECTOR_SHIFT ; Half a moby (32K) + xor bx,bx + pop si ; file pointer + call getfssec + cmp cx,1024 + jb kernel_corrupt + cmp word [es:bs_bootsign],0AA55h + jne kernel_corrupt ; Boot sec signature missing + +; +; Save the file pointer for later... +; + push si ; file pointer + +; +; Construct the command line (append options have already been copied) +; +construct_cmdline: + mov di,[CmdLinePtr] + mov si,boot_image ; BOOT_IMAGE= + mov cx,boot_image_len + rep movsb + mov si,KernelCName ; Unmangled kernel name + mov cx,[KernelCNameLen] + rep movsb + mov al,' ' ; Space + stosb + + call do_ip_append ; Handle IPAppend + + mov si,[CmdOptPtr] ; Options from user input + call strcpy + +; +; Scan through the command line for anything that looks like we might be +; interested in. The original version of this code automatically assumed +; the first option was BOOT_IMAGE=, but that is no longer certain. +; +parse_cmdline: + mov di,cmd_line_here +.skipspace: mov al,[es:di] + inc di +.skipspace_loaded: + and al,al + jz cmdline_end + cmp al,' ' + jbe .skipspace + dec di + + ; ES:DI now points to the beginning of an option + mov si,options_list +.next_opt: + movzx cx,byte [si] + jcxz .skip_opt + push di + inc si + repe cmpsb + jne .no_match + + ; This either needs to have been an option with parameter, + ; or be followed by EOL/whitespace + mov ax,[es:di-1] ; AL = last chr; AH = following + cmp al,'=' + je .is_match + cmp ah,' ' + ja .no_match +.is_match: + pop ax ; Drop option pointer on stack + call [si] +.skip_opt: + mov al,[es:di] + inc di + cmp al,' ' + ja .skip_opt + jmp .skipspace_loaded +.no_match: + pop di + add si,cx ; Skip remaining bytes + inc si ; Skip function pointer + inc si + jmp .next_opt + +opt_vga: + mov eax,[es:di-1] + mov bx,-1 + cmp eax,'=nor' ; vga=normal + je .vc0 + dec bx ; bx <- -2 + cmp eax,'=ext' ; vga=ext + je .vc0 + dec bx ; bx <- -3 + cmp eax,'=ask' ; vga=ask + je .vc0 + call parseint_esdi ; vga= + jc .skip ; Not an integer +.vc0: mov [es:bs_vidmode],bx ; Set video mode +.skip: + ret + +opt_mem: + call parseint_esdi + jc .skip +%if HIGHMEM_SLOP != 0 + sub ebx,HIGHMEM_SLOP +%endif + mov [MyHighMemSize],ebx +.skip: + ret + +opt_quiet: + mov byte [QuietBoot],1 + ret + +%if IS_PXELINUX +opt_keeppxe: + or byte [KeepPXE],1 ; KeepPXE set by command line + ret +%endif + +opt_initrd: + mov ax,di + cmp byte [es:di],' ' + ja .have_initrd + xor ax,ax +.have_initrd: + mov [InitRDPtr],ax + ret + +; +; After command line parsing... +; +cmdline_end: + sub di,cmd_line_here + mov [CmdLineLen],di ; Length including final null + +; +; Now check if we have a large kernel, which needs to be loaded high +; +prepare_header: + mov dword [RamdiskMax], HIGHMEM_MAX ; Default initrd limit + cmp dword [es:su_header],HEADER_ID ; New setup code ID + jne old_kernel ; Old kernel, load low + mov ax,[es:su_version] + mov [KernelVersion],ax + cmp ax,0200h ; Setup code version 2.0 + jb old_kernel ; Old kernel, load low + cmp ax,0201h ; Version 2.01+? + jb new_kernel ; If 2.00, skip this step + ; Set up the heap (assuming loading high for now) + mov word [es:su_heapend],linux_stack-512 + or byte [es:su_loadflags],80h ; Let the kernel know we care + cmp ax,0203h ; Version 2.03+? + jb new_kernel ; Not 2.03+ + mov eax,[es:su_ramdisk_max] + mov [RamdiskMax],eax ; Set the ramdisk limit + +; +; We definitely have a new-style kernel. Let the kernel know who we are, +; and that we are clueful +; +new_kernel: + mov byte [es:su_loader],my_id ; Show some ID + xor eax,eax + mov [es:su_ramdisklen],eax ; No initrd loaded yet + +; +; About to load the kernel. This is a modern kernel, so use the boot flags +; we were provided. +; + mov al,[es:su_loadflags] + mov [LoadFlags],al + +any_kernel: + mov si,loading_msg + call writestr_qchk + mov si,KernelCName ; Print kernel name part of + call writestr_qchk ; "Loading" message + +; +; Load the kernel. We always load it at 100000h even if we're supposed to +; load it "low"; for a "low" load we copy it down to low memory right before +; jumping to it. +; +read_kernel: + movzx ax,byte [es:bs_setupsecs] ; Setup sectors + and ax,ax + jnz .sects_ok + mov al,4 ; 0 = 4 setup sectors +.sects_ok: + inc ax ; Including the boot sector + mov [SetupSecs],ax + + call dot_pause + +; +; Move the stuff beyond the setup code to high memory at 100000h +; + movzx esi,word [SetupSecs] ; Setup sectors + shl si,9 ; Convert to bytes + mov ecx,8000h ; 32K + sub ecx,esi ; Number of bytes to copy + add esi,(real_mode_seg << 4) ; Pointer to source + mov edi,100000h ; Copy to address 100000h + + call bcopy ; Transfer to high memory + + pop si ; File pointer + and si,si ; EOF already? + jz high_load_done + + ; On exit EDI -> where to load the rest + + mov bx,dot_pause + or eax,-1 ; Load the whole file + mov dx,3 ; Pad to dword + call load_high + +high_load_done: + mov [KernelEnd],edi + mov ax,real_mode_seg ; Set to real mode seg + mov es,ax + + mov si,dot_msg + call writestr_qchk + +; +; Some older kernels (1.2 era) would have more than 4 setup sectors, but +; would not rely on the boot protocol to manage that. These kernels fail +; if they see protected-mode kernel data after the setup sectors, so +; clear that memory. +; + push di + mov di,[SetupSecs] + shl di,9 + xor eax,eax + mov cx,cmd_line_here + sub cx,di + shr cx,2 + rep stosd + pop di + +; +; Now see if we have an initial RAMdisk; if so, do requisite computation +; We know we have a new kernel; the old_kernel code already will have objected +; if we tried to load initrd using an old kernel +; +load_initrd: + ; Cap the ramdisk memory range if appropriate + mov eax,[RamdiskMax] + cmp eax,[MyHighMemSize] + ja .ok + mov [MyHighMemSize],eax +.ok: + xor eax,eax + cmp [InitRDPtr],ax + jz .noinitrd + call parse_load_initrd +.noinitrd: + +; +; Abandon hope, ye that enter here! We do no longer permit aborts. +; + call abort_check ; Last chance!! + + mov si,ready_msg + call writestr_qchk + + UNLOAD_PREP ; Module-specific hook + +; +; Now, if we were supposed to load "low", copy the kernel down to 10000h +; and the real mode stuff to 90000h. We assume that all bzImage kernels are +; capable of starting their setup from a different address. +; + mov ax,real_mode_seg + mov es,ax + mov fs,ax + +; +; If the default root device is set to FLOPPY (0000h), change to +; /dev/fd0 (0200h) +; + cmp word [es:bs_rootdev],byte 0 + jne root_not_floppy + mov word [es:bs_rootdev],0200h +root_not_floppy: + +; +; Copy command line. Unfortunately, the old kernel boot protocol requires +; the command line to exist in the 9xxxxh range even if the rest of the +; setup doesn't. +; +setup_command_line: + mov dx,[KernelVersion] + test byte [LoadFlags],LOAD_HIGH + jz .need_high_cmdline + cmp dx,0202h ; Support new cmdline protocol? + jb .need_high_cmdline + ; New cmdline protocol + ; Store 32-bit (flat) pointer to command line + ; This is the "high" location, since we have bzImage + mov dword [fs:su_cmd_line_ptr],(real_mode_seg << 4)+cmd_line_here + mov word [HeapEnd],linux_stack + mov word [fs:su_heapend],linux_stack-512 + jmp .setup_done + +.need_high_cmdline: +; +; Copy command line down to fit in high conventional memory +; -- this happens if we have a zImage kernel or the protocol +; is less than 2.02. +; + mov si,cmd_line_here + mov di,old_cmd_line_here + mov [fs:kern_cmd_magic],word CMD_MAGIC ; Store magic + mov [fs:kern_cmd_offset],di ; Store pointer + mov word [HeapEnd],old_linux_stack + mov ax,255 ; Max cmdline limit + cmp dx,0201h + jb .adjusted + ; Protocol 2.01+ + mov word [fs:su_heapend],old_linux_stack-512 + jbe .adjusted + ; Protocol 2.02+ + ; Note that the only reason we would end up here is + ; because we have a zImage, so we anticipate the move + ; to 90000h already... + mov dword [fs:su_cmd_line_ptr],0x90000+old_cmd_line_here + mov ax,old_max_cmd_len ; 2.02+ allow a higher limit +.adjusted: + + mov cx,[CmdLineLen] + cmp cx,ax + jna .len_ok + mov cx,ax ; Truncate the command line +.len_ok: + fs rep movsb + stosb ; Final null, note AL=0 already + mov [CmdLineEnd],di + cmp dx,0200h + jb .nomovesize + mov [es:su_movesize],di ; Tell the kernel what to move +.nomovesize: +.setup_done: + +; +; Time to start setting up move descriptors +; +setup_move: + mov di,trackbuf + xor cx,cx ; Number of descriptors + + mov bx,es ; real_mode_seg + mov fs,bx + push ds ; We need DS == ES == CS here + pop es + + test byte [LoadFlags],LOAD_HIGH + jnz .loading_high + +; Loading low: move real_mode stuff to 90000h, then move the kernel down + mov eax,90000h + stosd + mov eax,real_mode_seg << 4 + stosd + movzx eax,word [CmdLineEnd] + stosd + inc cx + + mov eax,10000h ; Target address of low kernel + stosd + mov eax,100000h ; Where currently loaded + stosd + neg eax + add eax,[KernelEnd] + stosd + inc cx + + mov bx,9000h ; Revised real mode segment + +.loading_high: + + cmp word [InitRDPtr],0 ; Did we have an initrd? + je .no_initrd + + mov eax,[fs:su_ramdiskat] + stosd + mov eax,[InitRDStart] + stosd + mov eax,[fs:su_ramdisklen] + stosd + inc cx + +.no_initrd: + push dword run_linux_kernel + push cx ; Length of descriptor list + + ; BX points to the final real mode segment, and will be loaded + ; into DS. + jmp replace_bootstrap + +run_linux_kernel: +; +; Set up segment registers and the Linux real-mode stack +; Note: ds == the real mode segment +; + cli + mov ax,ds + mov ss,ax + mov sp,strict word linux_stack + ; Point HeapEnd to the immediate of the instruction above +HeapEnd equ $-2 ; Self-modifying code! Fun! + mov es,ax + mov fs,ax + mov gs,ax + +; +; We're done... now RUN THAT KERNEL!!!! +; Setup segment == real mode segment + 020h; we need to jump to offset +; zero in the real mode segment. +; + add ax,020h + push ax + push word 0h + retf + +; +; Load an older kernel. Older kernels always have 4 setup sectors, can't have +; initrd, and are always loaded low. +; +old_kernel: + xor ax,ax + cmp word [InitRDPtr],ax ; Old kernel can't have initrd + je .load + mov si,err_oldkernel + jmp abort_load +.load: + mov byte [LoadFlags],al ; Always low + mov word [KernelVersion],ax ; Version 0.00 + jmp any_kernel + +; +; parse_load_initrd +; +; Parse an initrd= option and load the initrds. This sets +; InitRDStart and InitRDEnd with dword padding between; we then +; do a global memory shuffle to move it to the end of memory. +; +; On entry, EDI points to where to start loading. +; +parse_load_initrd: + push es + push ds + mov ax,real_mode_seg + mov ds,ax + push cs + pop es ; DS == real_mode_seg, ES == CS + + mov [cs:InitRDStart],edi + mov [cs:InitRDEnd],edi + + mov si,[cs:InitRDPtr] + +.get_chunk: + ; DS:SI points to the start of a name + + mov bx,si +.find_end: + lodsb + cmp al,',' + je .got_end + cmp al,' ' + jbe .got_end + jmp .find_end + +.got_end: + push ax ; Terminating character + push si ; Next filename (if any) + mov byte [si-1],0 ; Zero-terminate + mov si,bx ; Current filename + + push di + mov di,InitRD ; Target buffer for mangled name + call mangle_name + pop di + call loadinitrd + + pop si + pop ax + mov [si-1],al ; Restore ending byte + + cmp al,',' + je .get_chunk + + ; Compute the initrd target location + ; Note: we round to a page boundary twice here. The first + ; time it is to make sure we don't use any fractional page + ; which may be valid RAM but which will be ignored by the + ; kernel (and therefore is inaccessible.) The second time + ; it is to make sure we start out on page boundary. + mov edx,[cs:InitRDEnd] + sub edx,[cs:InitRDStart] + mov [su_ramdisklen],edx + mov eax,[cs:MyHighMemSize] + and ax,0F000h ; Round to a page boundary + sub eax,edx + and ax,0F000h ; Round to a page boundary + mov [su_ramdiskat],eax + + pop ds + pop es + ret + +; +; Load RAM disk into high memory +; +; Input: InitRD - set to the mangled name of the initrd +; EDI - location to load +; Output: EDI - location for next initrd +; InitRDEnd - updated +; +loadinitrd: + push ds + push es + mov ax,cs ; CS == DS == ES + mov ds,ax + mov es,ax + push edi + mov si,InitRD + mov di,InitRDCName + call unmangle_name ; Create human-readable name + sub di,InitRDCName + mov [InitRDCNameLen],di + mov di,InitRD + call searchdir ; Look for it in directory + pop edi + jz .notthere + + push si + mov si,crlfloading_msg ; Write "Loading " + call writestr_qchk + mov si,InitRDCName ; Write ramdisk name + call writestr_qchk + mov si,dotdot_msg ; Write dots + call writestr_qchk + pop si + +.li_skip_echo: + mov dx,3 + mov bx,dot_pause + call load_high + mov [InitRDEnd],ebx + + pop es + pop ds + ret + +.notthere: + mov si,err_noinitrd + call writestr + mov si,InitRDCName + call writestr + mov si,crlf_msg + jmp abort_load + +; +; writestr_qchk: writestr, except allows output to be suppressed +; assumes CS == DS +; +writestr_qchk: + test byte [QuietBoot],01h + jz writestr + ret + + section .data +crlfloading_msg db CR, LF +loading_msg db 'Loading ', 0 +dotdot_msg db '.' +dot_msg db '.', 0 +ready_msg db 'ready.', CR, LF, 0 +err_oldkernel db 'Cannot load a ramdisk with an old kernel image.' + db CR, LF, 0 +err_noinitrd db CR, LF, 'Could not find ramdisk image: ', 0 + +boot_image db 'BOOT_IMAGE=' +boot_image_len equ $-boot_image + +; +; Command line options we'd like to take a look at +; +%macro cmd_opt 2 +%strlen cmd_opt_len %1 + db cmd_opt_len + db %1 + dw %2 +%endmacro +options_list: + cmd_opt "vga=", opt_vga + cmd_opt "mem=", opt_mem + cmd_opt "quiet", opt_quiet +str_initrd equ $+1 ; Pointer to "initrd=" in memory + cmd_opt "initrd=", opt_initrd +%if IS_PXELINUX + cmd_opt "keeppxe", opt_keeppxe +%endif + db 0 + + section .bss + alignb 4 +MyHighMemSize resd 1 ; Possibly adjusted highmem size +RamdiskMax resd 1 ; Highest address for ramdisk +KernelSize resd 1 ; Size of kernel in bytes +KernelSects resd 1 ; Size of kernel in sectors +KernelEnd resd 1 ; Ending address of the kernel image +InitRDStart resd 1 ; Start of initrd (pre-relocation) +InitRDEnd resd 1 ; End of initrd (pre-relocation) +CmdLineLen resw 1 ; Length of command line including null +CmdLineEnd resw 1 ; End of the command line in real_mode_seg +SetupSecs resw 1 ; Number of setup sectors (+bootsect) +KernelVersion resw 1 ; Kernel protocol version +; +; These are derived from the command-line parser +; +InitRDPtr resw 1 ; Pointer to initrd= option in command line +LoadFlags resb 1 ; Loadflags from kernel +QuietBoot resb 1 ; Set if a quiet boot is requested diff --git a/core/serirq.inc b/core/serirq.inc new file mode 100644 index 0000000..579c42b --- /dev/null +++ b/core/serirq.inc @@ -0,0 +1,191 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 2009 Intel Corporation; author: H. Peter Anvin +;; +;; 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; serirq.inc +;; +;; Serial port IRQ code +;; +;; We don't know what IRQ, if any, we have, so map all of them... +;; + + section .text + bits 16 + align 8 + + section .bss + alignb 8 + +%assign n 0 +%rep 16 + section .text +serstub_irq %+ n : + push dword [cs:oldirq %+ n] + jmp short irq_common + + section .bss +oldirq %+ n resd 1 +%assign n n+1 +%endrep + + section .text +irq_common: + pushf + push ax + push dx + mov dx,[cs:SerialPort] + add dx,5 ; DX -> LSR + in al,dx + test al,1 ; Received data + jnz .data +.done: + pop dx + pop ax + popf + retf ; Chain to next handler +.data: + push es + push di + mov ax,aux_seg + (aux.serial >> 4) + mov es,ax + mov di,[cs:SerialHead] +.loop: + mov dx,[cs:SerialPort] ; DX -> RDR + in al,dx + stosb + mov ah,[cs:FlowIgnore] + add dx,5 ; DX -> LSR + in al,dx + push ax + and al,ah + cmp al,ah + jne .drop + and di,serial_buf_size-1 ; Wrap around if necessary + cmp di,[cs:SerialTail] ; Would this cause overflow? + je .drop ; If so, just drop the data + mov [cs:SerialHead],di +.drop: + pop ax + test al,1 ; More data? + jnz .loop +.full: + pop di + pop es + jmp .done + + section .data +; +; SerialIRQPort will generally track SerialPort, but will be 0 when an +; IRQ service is not installed. +; +SerialIRQPort dw 0 ; Serial port w IRQ service +SerialHead dw 0 ; Head of serial port rx buffer +SerialTail dw 0 ; Tail of serial port rx buffer + + section .text + +sirq_install: + pushad + + call sirq_cleanup + + ; Save the old interrupt vectors + mov si,4*08h + mov di,oldirq0 + mov cx,8 + rep movsd + mov si,4*70h + mov cx,8 + rep movsd + + ; Install new interrupt vectors + mov di,4*08h + mov cx,8 + mov eax,serstub_irq0 +.pic0: + stosd + add ax,serstub_irq1 - serstub_irq0 + loop .pic0 + mov di,4*70h + mov cx,8 +.pic1: + stosd + add ax,serstub_irq1 - serstub_irq0 + loop .pic1 + + mov bx,[SerialPort] + mov [SerialIRQPort],bx + + lea dx,[bx+5] ; DX -> LCR + mov al,03h ; Clear DLAB (should already be...) + slow_out dx,al + + lea dx,[bx+1] ; DX -> IER + mov al,1 ; Enable receive interrupt + slow_out dx,al + + popad + ret + +sirq_cleanup_nowipe: + pushad + push ds + push es + xor ax,ax + mov ds,ax + mov es,ax + + mov bx,[SerialIRQPort] + and bx,bx + jz .done + + lea dx,[bx+5] ; DX -> LCR + mov al,03h ; Clear DLAB (should already be...) + slow_out dx,al + + lea dx,[bx+1] ; DX -> IER + xor ax,ax + slow_out dx,al ; Clear IER + + ; Restore the original interrupt vectors + mov si,oldirq0 + mov di,4*08h + mov cx,8 + rep movsd + mov di,4*70h + mov cx,8 + rep movsd + +.done: + pop es + pop ds + popad + ret + +sirq_cleanup: + call sirq_cleanup_nowipe + pushad + push es + ; Just in case it might contain a password, erase the + ; serial port receive buffer... + mov ax,aux_seg + (aux.serial >> 4) + mov es,ax + xor eax,eax + mov [cs:SerialHead],eax + mov cx,serial_buf_size >> 2 + xor di,di + rep stosd + pop es + popad + ret + + section .text diff --git a/core/stack.inc b/core/stack.inc new file mode 100644 index 0000000..f670dec --- /dev/null +++ b/core/stack.inc @@ -0,0 +1,47 @@ +; ----------------------------------------------------------------------- +; +; Copyright 2005-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +; Boston MA 02111-1307, USA; either version 2 of the License, or +; (at your option) any later version; incorporated herein by reference. +; +; ----------------------------------------------------------------------- + +; +; stack.inc +; +; How to reset the stack pointer +; + +%ifndef _STACK_INC +%define _STACK_INC + +; +; This macro resets the stack pointer (including SS), and sets +; DS == ES == 0, interrupts on, DF = 0. +; +; It takes a 16-bit register that can be safely clobbered as parameter. +; +%macro RESET_STACK_AND_SEGS 1 + xor %1,%1 + mov ds,%1 + mov es,%1 +%if IS_SYSLINUX || IS_EXTLINUX + mov ss,%1 ; Just in case... + mov sp,StackBuf-2*5 ; Reset stack +%elif IS_PXELINUX + lss esp,[BaseStack] +%elif IS_ISOLINUX + mov ss,%1 + mov sp,StackBuf-2*2 +%else + NEED TO KNOW HOW TO RESET STACK +%endif + sti + cld +%endmacro + +%endif ; _STACK_INC diff --git a/core/strcpy.inc b/core/strcpy.inc new file mode 100644 index 0000000..2bafa7f --- /dev/null +++ b/core/strcpy.inc @@ -0,0 +1,13 @@ +; +; strcpy: Copy DS:SI -> ES:DI up to and including a null byte; +; on exit SI and DI point to the byte *after* the null byte +; + section .text + +strcpy: push ax +.loop: lodsb + stosb + and al,al + jnz .loop + pop ax + ret diff --git a/core/strecpy.inc b/core/strecpy.inc new file mode 100644 index 0000000..1fc53e9 --- /dev/null +++ b/core/strecpy.inc @@ -0,0 +1,28 @@ +; +; strecpy: Copy DS:SI -> ES:DI up to and including a null byte; +; on exit SI and DI point to the byte *after* the null byte. +; BP holds a pointer to the first byte beyond the end of the +; target buffer; return with CF=1 if target buffer overflows; +; the output is still zero-terminated. +; + section .text + +strecpy: + push ax + push bp + dec bp + dec bp +.loop: lodsb + stosb + and al,al ; CF=0 + jz .done + cmp bp,di ; CF set if BP < DI + jnc .loop + + ; Zero-terminate overflow string + mov al,0 ; Avoid changing flags + stosb +.done: + pop bp + pop ax + ret diff --git a/core/syslinux.ld b/core/syslinux.ld new file mode 100644 index 0000000..c1da230 --- /dev/null +++ b/core/syslinux.ld @@ -0,0 +1,150 @@ +/* ----------------------------------------------------------------------- + * + * Copyright 2008-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * 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, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * Linker script for the SYSLINUX core + */ + +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +EXTERN(_start) +ENTRY(_start) + +STACK_LEN = 4096; + +SECTIONS +{ + /* "Early" sections (before the load) */ + . = 0x0800; + + .earlybss : { + __earlybss_start = .; + *(.earlybss) + __earlybss_end = .; + } + __earlybss_len = __earlybss_end - __earlybss_start; + __earlybss_dwords = (__earlybss_len + 3) >> 2; + + .bss : { + __bss_start = .; + *(.bss) + *(.bss2) + __bss_end = .; + } + __bss_len = __bss_end - __bss_start; + __bss_dwords = (__bss_len + 3) >> 2; + + .config : AT (__config_lma) { + __config_start = .; + *(.config) + __config_end = .; + } + __config_len = __config_end - __config_start; + __config_dwords = (__config_len + 3) >> 2; + + . = ALIGN(16); + .replacestub : AT (__replacestub_lma) { + __replacestub_start = .; + *(.replacestub) + __replacestub_end = .; + } + __replacestub_len = __replacestub_end - __replacestub_start; + __replacestub_dwords = (__replacestub_len + 3) >> 2; + + /* Stack */ + + STACK_BASE = 0x7c00 - STACK_LEN; + . = STACK_BASE; + .stack : AT(STACK_BASE) { + __stack_start = .; + . += STACK_LEN; + __stack_end = .; + } + __stack_len = __stack_end - __stack_start; + __stack_dwords = (__stack_len + 3) >> 2; + + /* Initialized sections */ + + . = 0x7c00; + .text : { + FILL(0x90909090) + __text_start = .; + *(.text) + __text_end = .; + } + __text_len = __text_end - __text_start; + __text_dwords = (__text_len + 3) >> 2; + + . = ALIGN(16); + .bcopyxx : { + FILL(0x90909090) + __bcopyxx_start = .; + *(.bcopyxx) + __bcopyxx_end = .; + } + __bcopyxx_len = __bcopyxx_end - __bcopyxx_start; + __bcopyxx_dwords = (__bcopyxx_len + 3) >> 2; + + . = ALIGN(4); + .data : { + __data_start = .; + *(.data) + __data_end = .; + } + __data_len = __data_end - __data_start; + __data_dwords = (__data_len + 3) >> 2; + + . = ALIGN(4); + __config_lma = .; + . += SIZEOF(.config); + + . = ALIGN(4); + __replacestub_lma = .; + . += SIZEOF(.replacestub); + + /* ADV, must be the last intialized section */ + + . = ALIGN(512); + .adv : { + __adv_start = .; + *(.adv) + __adv_end = .; + } + __adv_len = __adv_end - __adv_start; + __adv_dwords = (__adv_len + 3) >> 2; + + /* Late uninitialized sections */ + + .uibss : { + __uibss_start = .; + *(.uibss) + __uibss_end = .; + } + __uibss_len = __uibss_end - __uibss_start; + __uibss_dwords = (__uibss_len + 3) >> 2; + + .bss1 : { + __bss1_start = .; + *(.bss1) + __bss1_end = .; + } + __bss1_len = __bss1_end - __bss1_start; + __bss1_dwords = (__bss1_len + 3) >> 2; + + . = ASSERT(__bss1_end <= 0x10000, "64K overflow"); + + . = 0x100000; + .com32 : { + *(.com32) + } +} diff --git a/core/tracers.inc b/core/tracers.inc new file mode 100644 index 0000000..a51209f --- /dev/null +++ b/core/tracers.inc @@ -0,0 +1,40 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; tracers.inc +;; +;; Debugging tracers +;; + +%ifndef _TRACERS_INC +%define _TRACERS_INC + +; Note: The Makefile builds one version with DEBUG_MESSAGES automatically. +; %define DEBUG_TRACERS 1 ; Uncomment to get debugging tracers +; %define DEBUG_MESSAGES ; Uncomment to get debugging messages + +%ifdef DEBUG_TRACERS + +%macro TRACER 1 + call debug_tracer + db %1 +%endmacro + +%else ; DEBUG_TRACERS + +%macro TRACER 1 +%endmacro + +%endif ; DEBUG_TRACERS + +%endif ; _TRACERS_INC diff --git a/core/ui.inc b/core/ui.inc new file mode 100644 index 0000000..1b40717 --- /dev/null +++ b/core/ui.inc @@ -0,0 +1,741 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved +;; Copyright 2009 Intel Corporation; author: H. Peter Anvin +;; +;; 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +; +; This file should be entered with the config file open (for getc) +; +load_config_file: + call parse_config ; Parse configuration file +no_config_file: + + call adv_init +; +; Check for an ADV boot-once entry +; + mov dl,ADV_BOOTONCE + call adv_get + jcxz .no_bootonce + +.have_bootone: + ; We apparently have a boot-once set; clear it and + ; then execute the boot-once... + + ; Save the boot-once data; SI = data, CX = length + mov di,command_line + rep movsb + xor ax,ax + stosb + + ; Clear the boot-once data from the ADV + xor cx,cx ; Set to zero = delete + call adv_set + jc .err + call adv_write +.err: jmp load_kernel + +.no_bootonce: + +; +; Check whether or not we are supposed to display the boot prompt. +; +check_for_key: + test byte [KbdFlags],5Bh ; Shift Alt Caps Scroll + jnz enter_command + cmp word [ForcePrompt],0 ; Force prompt? + jz auto_boot + cmp word [DefaultLevel],1 ; Active UI statement? + ja auto_boot + +enter_command: + cmp word [NoEscape],0 ; If NOESCAPE, no prompt, + jne auto_boot ; always run default cmd + + mov si,boot_prompt + call writestr + + mov byte [FuncFlag],0 ; not pressed + mov di,command_line + +; +; get the very first character -- we can either time +; out, or receive a character press at this time. Some dorky BIOSes stuff +; a return in the buffer on bootup, so wipe the keyboard buffer first. +; +clear_buffer: mov ah,11h ; Check for pending char + int 16h + jz get_char_time + mov ah,10h ; Get char + int 16h + jmp short clear_buffer + + ; For the first character, both KbdTimeout and + ; TotalTimeout apply; after that, only TotalTimeout. + +get_char_time: + mov eax,[TotalTimeout] + mov [ThisTotalTo],eax + mov eax,[KbdTimeout] + mov [ThisKbdTo],eax + +get_char: + call getchar_timeout + and dword [ThisKbdTo],0 ; For the next time... + + and al,al + jz func_key + +got_ascii: cmp al,7Fh ; == + je backspace + cmp al,' ' ; ASCII? + jb not_ascii + ja enter_char + cmp di,command_line ; Space must not be first + je short get_char +enter_char: test byte [FuncFlag],1 + jnz ctrl_f ; Keystroke after + cmp di,max_cmd_len+command_line ; Check there's space + jnb short get_char + stosb ; Save it + call writechr ; Echo to screen + jmp short get_char +not_ascii: + cmp al,0Dh ; Enter + je command_done + cmp al,09h ; Tab + je display_labels + cmp al,'F' & 1Fh ; + je set_func_flag +%if IS_PXELINUX + cmp al,'N' & 1Fh ; + je show_network_info +%endif + cmp al,'U' & 1Fh ; + je kill_command ; Kill input line + cmp al,'V' & 1Fh ; + je print_version + cmp al,'X' & 1Fh ; + je force_text_mode + cmp al,08h ; Backspace + jne get_char +backspace: cmp di,command_line ; Make sure there is anything + je get_char ; to erase + dec di ; Unstore one character + mov si,wipe_char ; and erase it from the screen + call writestr +get_char_2: + jmp short get_char + +kill_command: + call crlf + jmp enter_command + +force_text_mode: + call vgaclearmode + jmp enter_command + +set_func_flag: + mov byte [FuncFlag],1 + jmp short get_char_2 + +display_labels: + cmp word [NoComplete],0 ; Label completion enabled? + jne get_char_2 + push di ; Save pointer + mov cx,di + sub cx,command_line + call crlf + mov esi,[HighMemSize] ; Start from top of memory +.scan: + cmp esi,[VKernelEnd] + jbe .not_vk + + push cx ; save command line size + + mov edi,VKernelBuf + call rllunpack + ; ESI updated on return + + sub di,cx ; Return to beginning of buf + pop cx ; restore command line size + push si ; save SI + cmp cx,0 + jz .print + push di + push cx + mov si,command_line + es repe cmpsb + pop cx + pop di + jne .next +.print: + mov al,' ' + call writechr + + mov si,di + call writestr +.next: + pop si ; restore SI + jmp .scan +.not_vk: + call crlf + jmp fk_wrcmd + +ctrl_f: + xor ah,ah + mov [FuncFlag],ah + cmp al,'0' + jb get_char_2 + je .zero ; 0 = F10 + or al,20h ; Lower case + cmp al,'9' + jna .digit + cmp al,'a' ; F10-F12 = A, B, C + jb get_char_2 + cmp al,'c' + ja get_char_2 + sub al,'a'-10 + jmp show_help +.zero: + mov al,10 + jmp show_help +.digit: + sub al,'1' + jmp show_help + +func_key: + ; AL = 0 if we get here + xchg al,ah + cmp al,44h ; F10 + ja .f11_f12 + sub al,3Bh ; F1 + jb get_char_2 + jmp show_help +.f11_f12: + cmp al,85h ; F11 + jb get_char_2 + cmp al,86h ; F12 + ja get_char_2 + sub al,85h-10 + +show_help: ; AX = func key # (0 = F1, 9 = F10, 11 = F12) + push di ; Save end-of-cmdline pointer + shl ax,FILENAME_MAX_LG2 ; Convert to pointer + add ax,FKeyName + xchg di,ax + cmp byte [di+NULLOFFSET],NULLFILE + je short fk_nofile ; Undefined F-key + call open + jz short fk_nofile ; File not found + call crlf + call get_msg_file + jmp short fk_wrcmd + +print_version: + push di ; Command line write pointer + mov si,syslinux_banner + call writestr +%ifdef HAVE_BIOSNAME + mov si,[BIOSName] + call writestr +%endif + mov si,copyright_str + call writestr + + ; ... fall through ... + + ; Write the boot prompt and command line again and + ; wait for input. Note that this expects the cursor + ; to already have been CRLF'd, and that the old value + ; of DI (the command line write pointer) is on the stack. +fk_wrcmd: + mov si,boot_prompt + call writestr + pop di ; Command line write pointer + push di + mov byte [di],0 ; Null-terminate command line + mov si,command_line + call writestr ; Write command line so far +fk_nofile: pop di + jmp get_char + +; +; Show network info (in the form of the ipappend strings) +; +%if IS_PXELINUX +show_network_info: + push di ; Command line write pointer + call crlf + mov si,IPAppends ; See comboot.doc + mov cx,numIPAppends +.loop: + lodsw + push si + mov si,ax + call writestr + call crlf + pop si + loop .loop + jmp fk_wrcmd +%endif + +; +; Jump here to run the default command line +; +auto_boot: + mov si,default_cmd + mov di,command_line + mov cx,(max_cmd_len+4) >> 2 + rep movsd + jmp short load_kernel + +; +; Jump here when the command line is completed +; +command_done: + call crlf + cmp di,command_line ; Did we just hit return? + je auto_boot + xor al,al ; Store a final null + stosb + +load_kernel: ; Load the kernel now +; +; First we need to mangle the kernel name the way DOS would... +; + mov si,command_line + mov di,KernelName + push si + call mangle_name + pop si +; +; Fast-forward to first option (we start over from the beginning, since +; mangle_name doesn't necessarily return a consistent ending state.) +; +clin_non_wsp: lodsb + cmp al,' ' + ja clin_non_wsp +clin_is_wsp: and al,al + jz clin_opt_ptr + lodsb + cmp al,' ' + jbe clin_is_wsp +clin_opt_ptr: dec si ; Point to first nonblank + mov [CmdOptPtr],si ; Save ptr to first option +; +; If "allowoptions 0", put a null character here in order to ignore any +; user-specified options. +; + mov ax,[AllowOptions] + and ax,ax + jnz clin_opt_ok + mov [si],al +clin_opt_ok: + +; +; Now check if it is a "virtual kernel" +; +vk_check: + mov esi,[HighMemSize] ; Start from top of memory +.scan: + cmp esi,[VKernelEnd] + jbe .not_vk + + mov edi,VKernelBuf + call rllunpack + ; ESI updated on return + + sub di,cx ; Return to beginning of buf + push si + mov si,command_line +.loop: + lodsb + cmp al,' ' + jbe .done + scasb + je .loop +.nomatch: + pop si + jmp .scan +.done: + cmp byte [di],0 ; Must match end of string + jne .nomatch + pop si + +; +; We *are* using a "virtual kernel" +; +.found: + push es + push word real_mode_seg + pop es + mov di,cmd_line_here + mov si,VKernelBuf+vk_append + mov cx,[VKernelBuf+vk_appendlen] + rep movsb + mov [CmdLinePtr],di ; Where to add rest of cmd + pop es + mov di,KernelName + push di + mov si,VKernelBuf+vk_rname + mov cx,FILENAME_MAX ; We need ECX == CX later + rep movsb + pop di +%if IS_PXELINUX + mov al,[VKernelBuf+vk_ipappend] + mov [IPAppend],al +%endif + xor bx,bx ; Try only one version + + mov al,[VKernelBuf+vk_type] + mov [KernelType],al + +%if HAS_LOCALBOOT + ; Is this a "localboot" pseudo-kernel? + cmp al,VK_LOCALBOOT ; al == KernelType + mov ax,[VKernelBuf+vk_rname] ; Possible localboot type + je local_boot +%endif + jmp get_kernel + +.not_vk: +; +; Not a "virtual kernel" - check that's OK and construct the command line +; + cmp word [AllowImplicit],byte 0 + je bad_implicit + push es + push si + push di + mov di,real_mode_seg + mov es,di + mov si,AppendBuf + mov di,cmd_line_here + mov cx,[AppendLen] + rep movsb + mov [CmdLinePtr],di + pop di + pop si + pop es + + mov [KernelType], cl ; CL == 0 here + +; +; Find the kernel on disk +; +get_kernel: mov byte [KernelName+FILENAME_MAX],0 ; Zero-terminate filename/extension + mov di,KernelName+4*IS_PXELINUX + cmp byte [di],' ' + jbe bad_kernel ; Missing kernel name + xor al,al + mov cx,FILENAME_MAX-5 ; Need 4 chars + null + repne scasb ; Scan for final null + jne .no_skip + dec di ; Point to final null +.no_skip: mov [KernelExtPtr],di + mov bx,exten_table +.search_loop: push bx + mov di,KernelName ; Search on disk + call searchdir + pop bx + jnz kernel_good + mov eax,[bx] ; Try a different extension + mov si,[KernelExtPtr] + mov [si],eax + mov byte [si+4],0 + add bx,byte 4 + cmp bx,exten_table_end + jna .search_loop ; allow == case (final case) + ; Fall into bad_kernel +; +; bad_kernel: Kernel image not found +; bad_implicit: The user entered a nonvirtual kernel name, with "implicit 0" +; +bad_implicit: +bad_kernel: + mov cx,[OnerrorLen] + and cx,cx + jnz on_error +.really: + mov si,KernelName + mov di,KernelCName + push di + call unmangle_name ; Get human form + mov si,err_notfound ; Complain about missing kernel + call writestr + pop si ; KernelCName + call writestr + mov si,crlf_msg + jmp abort_load ; Ask user for clue + +; +; on_error: bad kernel, but we have onerror set; CX = OnerrorLen +; +on_error: + mov si,Onerror + mov di,command_line + push si ; + push di ; + push cx ; + push cx ; + push di ; + repe cmpsb + pop di ; di == command_line + pop bx ; bx == [OnerrorLen] + je bad_kernel.really ; Onerror matches command_line already + neg bx ; bx == -[OnerrorLen] + lea cx,[max_cmd_len+bx] + ; CX == max_cmd_len-[OnerrorLen] + mov di,command_line+max_cmd_len-1 + mov byte [di+1],0 ; Enforce null-termination + lea si,[di+bx] + std + rep movsb ; Make space in command_line + cld + pop cx ; cx == [OnerrorLen] + pop di ; di == command_line + pop si ; si == Onerror + rep movsb + jmp load_kernel + +; +; kernel_corrupt: Called if the kernel file does not seem healthy +; +kernel_corrupt: mov si,err_notkernel + jmp abort_load + +; +; Get a key, observing ThisKbdTO and ThisTotalTO -- those are timeouts +; which can be adjusted by the caller based on the corresponding +; master variables; on return they're updated. +; +; This cheats. If we say "no timeout" we actually get a timeout of +; 7.5 years. +; +getchar_timeout: + call vgashowcursor + call reset_idle + +.loop: + push word [BIOS_timer] + call pollchar + jnz .got_char + pop ax + cmp ax,[BIOS_timer] ; Has the timer advanced? + je .loop + + dec dword [ThisKbdTo] + jz .timeout + dec dword [ThisTotalTo] + jnz .loop + +.timeout: + ; Timeout!!!! + pop cx ; Discard return address + call vgahidecursor + mov si,Ontimeout ; Copy ontimeout command + mov di,command_line + mov cx,[OntimeoutLen] ; if we have one... + rep movsb + jmp command_done + +.got_char: + pop cx ; Discard + call getchar + call vgahidecursor + ret + +; +; This is it! We have a name (and location on the disk)... let's load +; that sucker!! First we have to decide what kind of file this is; base +; that decision on the file extension. The following extensions are +; recognized; case insensitive: +; +; .com - COMBOOT image +; .cbt - COMBOOT image +; .c32 - COM32 image +; .bs - Boot sector +; .0 - PXE bootstrap program (PXELINUX only) +; .bin - Boot sector +; .bss - Boot sector, but transfer over DOS superblock (SYSLINUX only) +; .img - Floppy image (ISOLINUX only) +; +; Anything else is assumed to be a Linux kernel. +; + section .bss + alignb 4 +Kernel_EAX resd 1 +Kernel_SI resw 1 + + section .text +kernel_good_saved: + ; Alternate entry point for which the return from + ; searchdir is stored in memory. This is used for + ; COMBOOT function INT 22h, AX=0016h. + mov si,[Kernel_SI] + mov eax,[Kernel_EAX] + +kernel_good: + pushad + ; + ; Common initialization for all kernel types + ; + xor ax,ax + mov [InitRDPtr],ax + mov [QuietBoot],al +%if IS_PXELINUX + mov [KeepPXE],al +%endif + + mov si,KernelName + mov di,KernelCName + call unmangle_name + sub di,KernelCName + mov [KernelCNameLen],di + + ; Default memory limit, can be overridden by image loaders + mov eax,[HighMemRsvd] + mov [MyHighMemSize],eax + + popad + + push di + push ax + mov di,KernelName+4*IS_PXELINUX + xor al,al + mov cx,FILENAME_MAX + repne scasb + jne .one_step + dec di +.one_step: mov ecx,[di-4] ; 4 bytes before end + pop ax + pop di + +; +; At this point, EAX contains the size of the kernel, SI contains +; the file handle/cluster pointer, and ECX contains the extension (if any.) +; + movzx di,byte [KernelType] + add di,di + jmp [kerneltype_table+di] + +is_unknown_filetype: + or ecx,20202000h ; Force lower case (except dot) + + cmp ecx,'.com' + je is_comboot_image + cmp ecx,'.cbt' + je is_comboot_image + cmp ecx,'.c32' + je is_com32_image +%if IS_ISOLINUX + cmp ecx,'.img' + je is_disk_image +%endif + cmp ecx,'.bss' + je is_bss_sector + cmp ecx,'.bin' + je is_bootsector + shr ecx,8 + cmp ecx,'.bs' + je is_bootsector + shr ecx,8 + cmp cx,'.0' + je is_bootsector + + ; Otherwise Linux kernel + jmp is_linux_kernel + +is_config_file: + pusha + mov si,KernelCName ; Save the config file name, for posterity + mov di,ConfigName + call strcpy + popa + call openfd + call reset_config + jmp load_config_file + +; This is an image type we can't deal with +is_bad_image: + mov si,err_badimage + call writestr + jmp enter_command + +%if IS_SYSLINUX + ; ok +%else +is_bss_sector equ is_bad_image +%endif +%if IS_ISOLINUX + ; ok +%else +is_disk_image equ is_bad_image +%endif + + section .data +boot_prompt db 'boot: ', 0 +wipe_char db BS, ' ', BS, 0 +err_badimage db 'Invalid image type for this media type!', CR, LF, 0 +err_notfound db 'Could not find kernel image: ',0 +err_notkernel db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0 + + + alignz 2 +kerneltype_table: + dw is_unknown_filetype ; VK_KERNEL + dw is_linux_kernel ; VK_LINUX + dw is_bootsector ; VK_BOOT + dw is_bss_sector ; VK_BSS + dw is_bootsector ; VK_PXE + dw is_disk_image ; VK_FDIMAGE + dw is_comboot_image ; VK_COMBOOT + dw is_com32_image ; VK_COM32 + dw is_config_file ; VK_CONFIG + + section .bss + alignb 4 +ThisKbdTo resd 1 ; Temporary holder for KbdTimeout +ThisTotalTo resd 1 ; Temporary holder for TotalTimeout +KernelExtPtr resw 1 ; During search, final null pointer +CmdOptPtr resw 1 ; Pointer to first option on cmd line +KbdFlags resb 1 ; Check for keyboard escapes +FuncFlag resb 1 ; Escape sequences received from keyboard +KernelType resb 1 ; Kernel type, from vkernel, if known + + section .text +; +; Linux kernel loading code is common. +; +%include "runkernel.inc" + +; +; COMBOOT-loading code +; +%include "comboot.inc" +%include "com32.inc" +%include "cmdline.inc" + +; +; Boot sector loading code +; +%include "bootsect.inc" + +; +; Abort loading code +; +%include "abort.inc" + +; +; Hardware cleanup common code +; +%include "cleanup.inc" diff --git a/core/writedec.inc b/core/writedec.inc new file mode 100644 index 0000000..10bf7ba --- /dev/null +++ b/core/writedec.inc @@ -0,0 +1,57 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; writedec.inc +;; +;; Write decimal numbers to the console +;; + + section .text +; +; writedec[bwl]: Write an unsigned decimal number in (AL, AX, EAX) +; to the console +; +writedecb: + pushad + movzx eax,al + jmp short writedec_common +writedecw: + pushad + movzx eax,ax + jmp short writedec_common +writedecl: + pushad +writedec_common: + pushfd + mov ebx,10 ; Conversion base + xor cx,cx ; Number of digits + +.cloop: + div ebx + inc cx + push dx + and eax,eax + jnz .cloop + +.dloop: + pop ax + add al,'0' + call writechr + loop .dloop + + popfd + popad + ret + +writechr: + ret diff --git a/core/writehex.inc b/core/writehex.inc new file mode 100644 index 0000000..1dbe4ab --- /dev/null +++ b/core/writehex.inc @@ -0,0 +1,53 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; writehex.inc +;; +;; Write hexadecimal numbers to the console +;; + + section .text +; +; writehex[248]: Write a hex number in (AL, AX, EAX) to the console +; +writehex2: + pushfd + pushad + rol eax,24 + mov cx,2 + jmp short writehex_common +writehex4: + pushfd + pushad + rol eax,16 + mov cx,4 + jmp short writehex_common +writehex8: + pushfd + pushad + mov cx,8 +writehex_common: +.loop: rol eax,4 + push eax + and al,0Fh + cmp al,10 + jae .high +.low: add al,'0' + jmp short .ischar +.high: add al,'A'-10 +.ischar: call writechr + pop eax + loop .loop + popad + popfd + ret diff --git a/core/writestr.inc b/core/writestr.inc new file mode 100644 index 0000000..4adc6bf --- /dev/null +++ b/core/writestr.inc @@ -0,0 +1,48 @@ +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; writestr.inc +;; +;; Code to write a simple string. +;; + + section .text +; +; crlf: Print a newline +; +crlf: push ax + mov al,CR + call writechr + mov al,LF + call writechr + pop ax + ret + +; +; writestr: write a null-terminated string to the console, saving +; registers on entry. +; +; Note: writestr_early and writestr are distinct in +; SYSLINUX and EXTLINUX, but not PXELINUX and ISOLINUX +; +writestr: + pushfd + pushad +.top: lodsb + and al,al + jz .end + call writechr + jmp short .top +.end: popad + popfd + ret diff --git a/doc/SubmittingPatches.txt b/doc/SubmittingPatches.txt new file mode 100644 index 0000000..dd764a7 --- /dev/null +++ b/doc/SubmittingPatches.txt @@ -0,0 +1,568 @@ +I don't have specific submission guidelines for Syslinux, but the ones +that appropriate to the Linux kernel are certainly good enough for +Syslinux. + +In particular, however, I appreciate if patches sent follow the +standard Linux submission format, as I can automatically import them +into git, retaining description and author information. Thus, this +file from the Linux kernel might be useful. + + + ----------------------------------------------------------------------- + + + + How to Get Your Change Into the Linux Kernel + or + Care And Operation Of Your Linus Torvalds + + + +For a person or company who wishes to submit a change to the Linux +kernel, the process can sometimes be daunting if you're not familiar +with "the system." This text is a collection of suggestions which +can greatly increase the chances of your change being accepted. + +Read Documentation/SubmitChecklist for a list of items to check +before submitting code. If you are submitting a driver, also read +Documentation/SubmittingDrivers. + + + +-------------------------------------------- +SECTION 1 - CREATING AND SENDING YOUR CHANGE +-------------------------------------------- + + + +1) "diff -up" +------------ + +Use "diff -up" or "diff -uprN" to create patches. + +All changes to the Linux kernel occur in the form of patches, as +generated by diff(1). When creating your patch, make sure to create it +in "unified diff" format, as supplied by the '-u' argument to diff(1). +Also, please use the '-p' argument which shows which C function each +change is in - that makes the resultant diff a lot easier to read. +Patches should be based in the root kernel source directory, +not in any lower subdirectory. + +To create a patch for a single file, it is often sufficient to do: + + SRCTREE= linux-2.6 + MYFILE= drivers/net/mydriver.c + + cd $SRCTREE + cp $MYFILE $MYFILE.orig + vi $MYFILE # make your change + cd .. + diff -up $SRCTREE/$MYFILE{.orig,} > /tmp/patch + +To create a patch for multiple files, you should unpack a "vanilla", +or unmodified kernel source tree, and generate a diff against your +own source tree. For example: + + MYSRC= /devel/linux-2.6 + + tar xvfz linux-2.6.12.tar.gz + mv linux-2.6.12 linux-2.6.12-vanilla + diff -uprN -X linux-2.6.12-vanilla/Documentation/dontdiff \ + linux-2.6.12-vanilla $MYSRC > /tmp/patch + +"dontdiff" is a list of files which are generated by the kernel during +the build process, and should be ignored in any diff(1)-generated +patch. The "dontdiff" file is included in the kernel tree in +2.6.12 and later. For earlier kernel versions, you can get it +from . + +Make sure your patch does not include any extra files which do not +belong in a patch submission. Make sure to review your patch -after- +generated it with diff(1), to ensure accuracy. + +If your changes produce a lot of deltas, you may want to look into +splitting them into individual patches which modify things in +logical stages. This will facilitate easier reviewing by other +kernel developers, very important if you want your patch accepted. +There are a number of scripts which can aid in this: + +Quilt: +http://savannah.nongnu.org/projects/quilt + +Andrew Morton's patch scripts: +http://www.zip.com.au/~akpm/linux/patches/ +Instead of these scripts, quilt is the recommended patch management +tool (see above). + + + +2) Describe your changes. + +Describe the technical detail of the change(s) your patch includes. + +Be as specific as possible. The WORST descriptions possible include +things like "update driver X", "bug fix for driver X", or "this patch +includes updates for subsystem X. Please apply." + +If your description starts to get long, that's a sign that you probably +need to split up your patch. See #3, next. + + + +3) Separate your changes. + +Separate _logical changes_ into a single patch file. + +For example, if your changes include both bug fixes and performance +enhancements for a single driver, separate those changes into two +or more patches. If your changes include an API update, and a new +driver which uses that new API, separate those into two patches. + +On the other hand, if you make a single change to numerous files, +group those changes into a single patch. Thus a single logical change +is contained within a single patch. + +If one patch depends on another patch in order for a change to be +complete, that is OK. Simply note "this patch depends on patch X" +in your patch description. + +If you cannot condense your patch set into a smaller set of patches, +then only post say 15 or so at a time and wait for review and integration. + + + +4) Style check your changes. + +Check your patch for basic style violations, details of which can be +found in Documentation/CodingStyle. Failure to do so simply wastes +the reviewers time and will get your patch rejected, probably +without even being read. + +At a minimum you should check your patches with the patch style +checker prior to submission (scripts/checkpatch.pl). You should +be able to justify all violations that remain in your patch. + + + +5) Select e-mail destination. + +Look through the MAINTAINERS file and the source code, and determine +if your change applies to a specific subsystem of the kernel, with +an assigned maintainer. If so, e-mail that person. + +If no maintainer is listed, or the maintainer does not respond, send +your patch to the primary Linux kernel developer's mailing list, +linux-kernel@vger.kernel.org. Most kernel developers monitor this +e-mail list, and can comment on your changes. + + +Do not send more than 15 patches at once to the vger mailing lists!!! + + +Linus Torvalds is the final arbiter of all changes accepted into the +Linux kernel. His e-mail address is . +He gets a lot of e-mail, so typically you should do your best to -avoid- +sending him e-mail. + +Patches which are bug fixes, are "obvious" changes, or similarly +require little discussion should be sent or CC'd to Linus. Patches +which require discussion or do not have a clear advantage should +usually be sent first to linux-kernel. Only after the patch is +discussed should the patch then be submitted to Linus. + + + +6) Select your CC (e-mail carbon copy) list. + +Unless you have a reason NOT to do so, CC linux-kernel@vger.kernel.org. + +Other kernel developers besides Linus need to be aware of your change, +so that they may comment on it and offer code review and suggestions. +linux-kernel is the primary Linux kernel developer mailing list. +Other mailing lists are available for specific subsystems, such as +USB, framebuffer devices, the VFS, the SCSI subsystem, etc. See the +MAINTAINERS file for a mailing list that relates specifically to +your change. + +Majordomo lists of VGER.KERNEL.ORG at: + + +If changes affect userland-kernel interfaces, please send +the MAN-PAGES maintainer (as listed in the MAINTAINERS file) +a man-pages patch, or at least a notification of the change, +so that some information makes its way into the manual pages. + +Even if the maintainer did not respond in step #4, make sure to ALWAYS +copy the maintainer when you change their code. + +For small patches you may want to CC the Trivial Patch Monkey +trivial@kernel.org managed by Adrian Bunk; which collects "trivial" +patches. Trivial patches must qualify for one of the following rules: + Spelling fixes in documentation + Spelling fixes which could break grep(1) + Warning fixes (cluttering with useless warnings is bad) + Compilation fixes (only if they are actually correct) + Runtime fixes (only if they actually fix things) + Removing use of deprecated functions/macros (eg. check_region) + Contact detail and documentation fixes + Non-portable code replaced by portable code (even in arch-specific, + since people copy, as long as it's trivial) + Any fix by the author/maintainer of the file (ie. patch monkey + in re-transmission mode) +URL: + + + +7) No MIME, no links, no compression, no attachments. Just plain text. + +Linus and other kernel developers need to be able to read and comment +on the changes you are submitting. It is important for a kernel +developer to be able to "quote" your changes, using standard e-mail +tools, so that they may comment on specific portions of your code. + +For this reason, all patches should be submitting e-mail "inline". +WARNING: Be wary of your editor's word-wrap corrupting your patch, +if you choose to cut-n-paste your patch. + +Do not attach the patch as a MIME attachment, compressed or not. +Many popular e-mail applications will not always transmit a MIME +attachment as plain text, making it impossible to comment on your +code. A MIME attachment also takes Linus a bit more time to process, +decreasing the likelihood of your MIME-attached change being accepted. + +Exception: If your mailer is mangling patches then someone may ask +you to re-send them using MIME. + +See Documentation/email-clients.txt for hints about configuring +your e-mail client so that it sends your patches untouched. + +8) E-mail size. + +When sending patches to Linus, always follow step #7. + +Large changes are not appropriate for mailing lists, and some +maintainers. If your patch, uncompressed, exceeds 40 kB in size, +it is preferred that you store your patch on an Internet-accessible +server, and provide instead a URL (link) pointing to your patch. + + + +9) Name your kernel version. + +It is important to note, either in the subject line or in the patch +description, the kernel version to which this patch applies. + +If the patch does not apply cleanly to the latest kernel version, +Linus will not apply it. + + + +10) Don't get discouraged. Re-submit. + +After you have submitted your change, be patient and wait. If Linus +likes your change and applies it, it will appear in the next version +of the kernel that he releases. + +However, if your change doesn't appear in the next version of the +kernel, there could be any number of reasons. It's YOUR job to +narrow down those reasons, correct what was wrong, and submit your +updated change. + +It is quite common for Linus to "drop" your patch without comment. +That's the nature of the system. If he drops your patch, it could be +due to +* Your patch did not apply cleanly to the latest kernel version. +* Your patch was not sufficiently discussed on linux-kernel. +* A style issue (see section 2). +* An e-mail formatting issue (re-read this section). +* A technical problem with your change. +* He gets tons of e-mail, and yours got lost in the shuffle. +* You are being annoying. + +When in doubt, solicit comments on linux-kernel mailing list. + + + +11) Include PATCH in the subject + +Due to high e-mail traffic to Linus, and to linux-kernel, it is common +convention to prefix your subject line with [PATCH]. This lets Linus +and other kernel developers more easily distinguish patches from other +e-mail discussions. + + + +12) Sign your work + +To improve tracking of who did what, especially with patches that can +percolate to their final resting place in the kernel through several +layers of maintainers, we've introduced a "sign-off" procedure on +patches that are being emailed around. + +The sign-off is a simple line at the end of the explanation for the +patch, which certifies that you wrote it or otherwise have the right to +pass it on as a open-source patch. The rules are pretty simple: if you +can certify the below: + + Developer's Certificate of Origin 1.1 + + By making a contribution to this project, I certify that: + + (a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + + (b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + + (c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + + (d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. + +then you just add a line saying + + Signed-off-by: Random J Developer + +using your real name (sorry, no pseudonyms or anonymous contributions.) + +Some people also put extra tags at the end. They'll just be ignored for +now, but you can do this to mark internal company procedures or just +point out some special detail about the sign-off. + + +13) When to use Acked-by: + +The Signed-off-by: tag indicates that the signer was involved in the +development of the patch, or that he/she was in the patch's delivery path. + +If a person was not directly involved in the preparation or handling of a +patch but wishes to signify and record their approval of it then they can +arrange to have an Acked-by: line added to the patch's changelog. + +Acked-by: is often used by the maintainer of the affected code when that +maintainer neither contributed to nor forwarded the patch. + +Acked-by: is not as formal as Signed-off-by:. It is a record that the acker +has at least reviewed the patch and has indicated acceptance. Hence patch +mergers will sometimes manually convert an acker's "yep, looks good to me" +into an Acked-by:. + +Acked-by: does not necessarily indicate acknowledgement of the entire patch. +For example, if a patch affects multiple subsystems and has an Acked-by: from +one subsystem maintainer then this usually indicates acknowledgement of just +the part which affects that maintainer's code. Judgement should be used here. + When in doubt people should refer to the original discussion in the mailing +list archives. + + +14) The canonical patch format + +The canonical patch subject line is: + + Subject: [PATCH 001/123] subsystem: summary phrase + +The canonical patch message body contains the following: + + - A "from" line specifying the patch author. + + - An empty line. + + - The body of the explanation, which will be copied to the + permanent changelog to describe this patch. + + - The "Signed-off-by:" lines, described above, which will + also go in the changelog. + + - A marker line containing simply "---". + + - Any additional comments not suitable for the changelog. + + - The actual patch (diff output). + +The Subject line format makes it very easy to sort the emails +alphabetically by subject line - pretty much any email reader will +support that - since because the sequence number is zero-padded, +the numerical and alphabetic sort is the same. + +The "subsystem" in the email's Subject should identify which +area or subsystem of the kernel is being patched. + +The "summary phrase" in the email's Subject should concisely +describe the patch which that email contains. The "summary +phrase" should not be a filename. Do not use the same "summary +phrase" for every patch in a whole patch series (where a "patch +series" is an ordered sequence of multiple, related patches). + +Bear in mind that the "summary phrase" of your email becomes +a globally-unique identifier for that patch. It propagates +all the way into the git changelog. The "summary phrase" may +later be used in developer discussions which refer to the patch. +People will want to google for the "summary phrase" to read +discussion regarding that patch. + +A couple of example Subjects: + + Subject: [patch 2/5] ext2: improve scalability of bitmap searching + Subject: [PATCHv2 001/207] x86: fix eflags tracking + +The "from" line must be the very first line in the message body, +and has the form: + + From: Original Author + +The "from" line specifies who will be credited as the author of the +patch in the permanent changelog. If the "from" line is missing, +then the "From:" line from the email header will be used to determine +the patch author in the changelog. + +The explanation body will be committed to the permanent source +changelog, so should make sense to a competent reader who has long +since forgotten the immediate details of the discussion that might +have led to this patch. + +The "---" marker line serves the essential purpose of marking for patch +handling tools where the changelog message ends. + +One good use for the additional comments after the "---" marker is for +a diffstat, to show what files have changed, and the number of inserted +and deleted lines per file. A diffstat is especially useful on bigger +patches. Other comments relevant only to the moment or the maintainer, +not suitable for the permanent changelog, should also go here. +Use diffstat options "-p 1 -w 70" so that filenames are listed from the +top of the kernel source tree and don't use too much horizontal space +(easily fit in 80 columns, maybe with some indentation). + +See more details on the proper patch format in the following +references. + + + + +----------------------------------- +SECTION 2 - HINTS, TIPS, AND TRICKS +----------------------------------- + +This section lists many of the common "rules" associated with code +submitted to the kernel. There are always exceptions... but you must +have a really good reason for doing so. You could probably call this +section Linus Computer Science 101. + + + +1) Read Documentation/CodingStyle + +Nuff said. If your code deviates too much from this, it is likely +to be rejected without further review, and without comment. + +One significant exception is when moving code from one file to +another -- in this case you should not modify the moved code at all in +the same patch which moves it. This clearly delineates the act of +moving the code and your changes. This greatly aids review of the +actual differences and allows tools to better track the history of +the code itself. + +Check your patches with the patch style checker prior to submission +(scripts/checkpatch.pl). The style checker should be viewed as +a guide not as the final word. If your code looks better with +a violation then its probably best left alone. + +The checker reports at three levels: + - ERROR: things that are very likely to be wrong + - WARNING: things requiring careful review + - CHECK: things requiring thought + +You should be able to justify all violations that remain in your +patch. + + + +2) #ifdefs are ugly + +Code cluttered with ifdefs is difficult to read and maintain. Don't do +it. Instead, put your ifdefs in a header, and conditionally define +'static inline' functions, or macros, which are used in the code. +Let the compiler optimize away the "no-op" case. + +Simple example, of poor code: + + dev = alloc_etherdev (sizeof(struct funky_private)); + if (!dev) + return -ENODEV; + #ifdef CONFIG_NET_FUNKINESS + init_funky_net(dev); + #endif + +Cleaned-up example: + +(in header) + #ifndef CONFIG_NET_FUNKINESS + static inline void init_funky_net (struct net_device *d) {} + #endif + +(in the code itself) + dev = alloc_etherdev (sizeof(struct funky_private)); + if (!dev) + return -ENODEV; + init_funky_net(dev); + + + +3) 'static inline' is better than a macro + +Static inline functions are greatly preferred over macros. +They provide type safety, have no length limitations, no formatting +limitations, and under gcc they are as cheap as macros. + +Macros should only be used for cases where a static inline is clearly +suboptimal [there a few, isolated cases of this in fast paths], +or where it is impossible to use a static inline function [such as +string-izing]. + +'static inline' is preferred over 'static __inline__', 'extern inline', +and 'extern __inline__'. + + + +4) Don't over-design. + +Don't try to anticipate nebulous future cases which may or may not +be useful: "Make it as simple as you can, and no simpler." + + + +---------------------- +SECTION 3 - REFERENCES +---------------------- + +Andrew Morton, "The perfect patch" (tpp). + + +Jeff Garzik, "Linux kernel patch submission format". + + +Greg Kroah-Hartman, "How to piss off a kernel subsystem maintainer". + + + + + +NO!!!! No more huge patch bombs to linux-kernel@vger.kernel.org people! + + +Kernel Documentation/CodingStyle: + + +Linus Torvalds's mail on the canonical patch format: + +-- diff --git a/doc/comboot.txt b/doc/comboot.txt new file mode 100644 index 0000000..f5fefda --- /dev/null +++ b/doc/comboot.txt @@ -0,0 +1,957 @@ + + COMBOOT and COM32 files + + +Syslinux supports simple standalone programs, using a file format +similar to DOS ".com" files. A 32-bit version, called COM32, is also +provided. A simple API provides access to a limited set of filesystem +and console functions. + + + ++++ COMBOOT file format ++++ + +A COMBOOT file is a raw binary file containing 16-bit code. It should +be linked to run at offset 0x100, and contain no absolute segment +references. It is run in 16-bit real mode. + +A COMBOOT image can be written to be compatible with MS-DOS. Such a +file will usually have extension ".com". A COMBOOT file which is not +compatible with MS-DOS will usually have extension ".cbt". + +Before running the program, Syslinux sets up the following fields in +the Program Segment Prefix (PSP), a structure at offset 0 in the +program segment: + + Offset Size Meaning + 0 word Contains an INT 20h instruction + 2 word Contains the paragraph (16-byte "segment" address) at + the end of memory available to the program. + 128 byte Length of the command line arguments, including the leading + space but not including the final CR character. + 129 127b Command line arguments, starting with a space and ending + with a CR character (ASCII 13). + +The program is allowed to use memory between the PSP paragraph (which +all the CS, DS, ES and SS registers point to at program start) and the +paragraph value given at offset 2. + +On startup, SP is set up to point to the end of the 64K segment, at +0xfffe. Under DOS it is possible for SP to contain a smaller +value if memory is very tight; this is never the case under Syslinux. + +The program should make no assumptions about what segment address it +will be loaded at; instead it should look at the segment registers on +program startup. Both DOS and Syslinux will guarantee CS == DS == ES +== SS on program start; the program should not assume anything about +the values of FS or GS. + +To exit, a program can either execute a near RET (which will jump to +offset 0 which contains an INT 20h instruction, terminating the +program), or execute INT 20h or INT 21h AH=00h or INT 21h AH=4Ch. +If compatiblity with Syslinux 1.xx is desired, use INT 20h. + + + ++++ COM32 file format ++++ + +A COM32 file is a raw binary file containing 32-bit code. It should +be linked to run at address 0x101000, and should not contain any +segment references. It will be run in flat-memory 32-bit protected +mode. Under Syslinux, it will be run in CPL 0, however, since it may +be possible to create a COM32 execution engine that would run under +something like Linux DOSEMU, it is recommended that the code does not +assume CPL 0 unless absolutely necessary. + +It is highly recommended that every COM32 program begins with the byte +sequence B8 FF 4C CD 21 (mov eax,21cd4cffh) as a magic number. + +A COM32 file should have extension ".c32". + +On startup, CS will be set up as a flat 32-bit code segment, and DS == +ES == SS will be set up as the equivalent flat 32-bit data segment. +FS and GS are reserved for future use and are currently initialized to +zero. A COM32 image should not assume any particular values of +segment selectors. + +ESP is set up at the end of available memory and also serves as +notification to the program how much memory is available. + +The following arguments are passed to the program on the stack: + + Address Size Meaning + [ESP] dword Return (termination) address + [ESP+4] dword Number of additional arguments (currently 7) + [ESP+8] dword Pointer to the command line arguments (null-terminated string) + [ESP+12] dword Pointer to INT call helper function + [ESP+16] dword Pointer to low memory bounce buffer + [ESP+20] dword Size of low memory bounce buffer + [ESP+24] dword Pointer to FAR call helper function (new in 2.05) + [ESP+28] dword Pointer to CDECL helper function (new in 3.54) + [ESP+32] dword Amount of memory controlled by the Syslinux core (new in 3.74) + +This corresponds to the following C prototype, available in the file +com32/include/com32.h: + +/* The standard prototype for _start() */ +int _start(unsigned int __nargs, + char *__cmdline, + void (*__intcall)(uint8_t, com32sys_t *, com32sys_t *), + void *__bounce_ptr, + unsigned int __bounce_len, + void (*__farcall)(uint32_t, com32sys_t *, com32sys_t *), + int (*__cfarcall)(uint32_t, void *, size_t) + ); + +The intcall helper function can be used to issue BIOS or Syslinux API +calls, and takes the interrupt number as first argument. The second +argument is a pointer to the input register definition, an instance of +the following structure (available in ): + +typedef union { + uint32_t l; + uint16_t w[2]; + uint8_t b[4]; +} reg32_t; + +typedef struct { + uint16_t gs; /* Offset 0 */ + uint16_t fs; /* Offset 2 */ + uint16_t es; /* Offset 4 */ + uint16_t ds; /* Offset 6 */ + + reg32_t edi; /* Offset 8 */ + reg32_t esi; /* Offset 12 */ + reg32_t ebp; /* Offset 16 */ + reg32_t _unused_esp; /* Offset 20 */ + reg32_t ebx; /* Offset 24 */ + reg32_t edx; /* Offset 28 */ + reg32_t ecx; /* Offset 32 */ + reg32_t eax; /* Offset 36 */ + + reg32_t eflags; /* Offset 40 */ +} com32sys_t; + +The third argument is a pointer to the output register definition, an +instance of the same structure. The third argument can also be zero +(NULL). + +Since BIOS or Syslinux API calls can generally only manipulate data +below address 0x100000, a "bounce buffer" in low memory, at least 64K +in size, is available, to copy data in and out. + +The farcall helper function behaves similarly, but takes as its first +argument the CS:IP (in the form (CS << 16) + IP) of procedure to be +invoked via a FAR CALL. + +The cfarcall helper function takes (CS << 16)+IP, a pointer to a stack +frame, a size of that stack frame, and returns the return value of EAX +(which may need to be appropriate truncated by the user.) + + + ++++ SYSLINUX API CALLS +++ + +Syslinux provides the following API calls. Syslinux 1.xx only +supported INT 20h - terminate program. [] indicates the first version +of Syslinux which supported this feature (correctly.) + +NOTE: Most of the API functionality is still experimental. Expect to +find bugs. + + + ++++ DOS-COMPATIBLE API CALLS ++++ + +INT 20h [1.48] Terminate program +INT 21h AH=00h [2.00] Terminate program +INT 21h AH=4Ch [2.00] Terminate program + + All of these terminate the program. + + +INT 21h AH=01h [2.01] Get Key with Echo + + Reads a key from the console input, with echo to the console + output. The read character is returned in AL. Extended + characters received from the keyboard are returned as NUL (00h) + + the extended character code. + + +INT 21h AH=02h [2.01] Write Character + + Writes a character in DL to the console (video and serial) + output. + + +INT 21h AH=04h [2.01] Write Character to Serial Port + + Writes a character in DL to the serial console output + (if enabled.) If no serial port is configured, this routine + does nothing. + + +INT 21h AH=08h [2.09] Get Key without Echo + + Reads a key fron the console input, without echoing it to the + console output. The read character is returned in AL. + + +INT 21h AH=09h [2.01] Write DOS String to Console + + Writes a DOS $-terminated string in DS:DX to the console. + + +INT 21h AH=0Bh [2.00] Check Keyboard + + Returns AL=FFh if there is a keystroke waiting (which can then + be read with INT 21h, AH=01h or AH=08h), otherwise AL=00h. + + +INT 21h AH=30h [2.00] Check DOS Version + + This function returns AX=BX=CX=DX=0, corresponding to a + hypothetical "DOS 0.0", but the high parts of EAX-EBX-ECX-EDX + spell "SYSLINUX": + + EAX=59530000h EBX=4C530000h ECX=4E490000h EDX=58550000h + + This function can thus be used to distinguish running on + Syslinux from running on DOS. + + + ++++ SYSLINUX-SPECIFIC API CALLS ++++ + +Syslinux-specific API calls are executed using INT 22h, with a +function number in AX. INT 22h is used by DOS for internal purposes; +do not execute INT 22h under DOS. + +DOS-compatible function INT 21h, AH=30h can be used to detect if the +Syslinux API calls are available. + +Any register not specifically listed as modified is preserved; +however, future versions of Syslinux may add additional output +registers to existing calls. + +All calls return CF=0 on success, CF=1 on failure. The noted outputs +apply if CF=0 only unless otherwise noted. All calls clobber the +arithmetric flags (CF, PF, AF, ZF, SF and OF) but leave all other +flags unchanged unless otherwise noted. + + +AX=0001h [2.00] Get Version + + Input: AX 0001h + Output: AX number of INT 22h API functions available + CH Syslinux major version number + CL Syslinux minor version number + DL Syslinux derivative ID (e.g. 32h = PXELINUX) + ES:SI Syslinux version string + ES:DI Syslinux copyright string + + This API call returns the Syslinux version and API + information. + + +AX=0002h [2.01] Write String + + Input: AX 0002h + ES:BX null-terminated string + Output: None + + Writes a null-terminated string on the console. + + +AX=0003h [2.01] Run command + + Input: AX 0003h + ES:BX null-terminated command string + Output: Does not return + + This API call terminates the program and executes the command + string as if the user had entered it at the Syslinux command + line. This API call does not return. + + +AX=0004h [2.01] Run default command + + Input: AX 0004h + Output: Does not return + + This API call terminates the program and executes the default + command string as if the user had pressed Enter alone on the + Syslinux command line. This API call does not return. + + +AX=0005h [2.00] Force text mode + + Input: AX 0005h + Output: None + + If the screen was in graphics mode (due to displaying a splash + screen using the command in a message file, or + similar), return to text mode. + + +AX=0006h [2.08] Open file + + Input: AX 0006h + ES:SI null-terminated filename + Output: SI file handle + EAX length of file in bytes, or -1 + CX file block size + + Open a file for reading. The exact syntax of the filenames + allowed depends on the particular Syslinux derivative. + + The Syslinux file system is block-oriented. The size of a + block will always be a power of two and no greater than 16K. + + Note: Syslinux considers a zero-length file to be nonexistent. + + In 3.70 or later, EAX can contain -1 indicating that the file + length is unknown. + + +AX=0007h [2.08] Read file + + Input: AX 0007h + SI file handle + ES:BX buffer + CX number of blocks to read + Output: SI file handle, or 0 if EOF was reached + ECX number of bytes read [3.70] + + Read blocks from a file. Note that the file handle that is + returned in SI may not be the same value that was passed in. + + If end of file was reached (SI=0), the file was automatically + closed. + + In 3.70 or later, ECX returns the number of bytes read. This + will always be a multiple of the block size unless EOF is + reached. + + The address of the buffer (ES:BX) should be at least 512-byte + aligned. Syslinux guarantees at least this alignment for the + COMBOOT load segment or the COM32 bounce buffer. + + Keep in mind that a "file" may be a TFTP connection, and that + leaving a file open for an extended period of time may result + in a timeout. + + WARNING: Calling this function with an invalid file handle + will probably crash the system. + + +AX=0008h [2.08] Close file + + Input: AX 0008h + SI file handle + Output: None + + Close a file before reaching the end of file. + + WARNING: Calling this function with an invalid file handle + will probably crash the system. + + +AX=0009h [2.00] Call PXE Stack [PXELINUX ONLY] + + Input: AX 0009h + BX PXE function number + ES:DI PXE parameter structure buffer + Output: AX PXE return status code + + Invoke an arbitrary PXE stack function. On SYSLINUX/ISOLINUX, + this function returns with an error (CF=1) and no action is + taken. On PXELINUX, this function always returns with CF=0 + indicating that the PXE stack was successfully invoked; check + the status code in AX and in the first word of the data buffer + to determine if the PXE call succeeded or not. + + The PXE stack will have the UDP stack OPEN; if you change that + you cannot call any of the file-related API functions, and + must restore UDP OPEN before returning to PXELINUX. + + PXELINUX reserves UDP port numbers from 49152 to 65535 for its + own use; port numbers below that range is available. + + +AX=000Ah [2.00] Get Derivative-Specific Information + + [SYSLINUX, EXTLINUX] + Input: AX 000Ah + CL 9 (to get a valid return in CL for all versions) + Output: AL 31h (SYSLINUX), 34h (EXTLINUX) + DL drive number + CL sector size as a power of 2 (9 = 512 bytes) [3.35] + ES:BX pointer to partition table entry (if DL >= 80h) + FS:SI pointer to initial ES:DI value [3.53] + + Note: This function was broken in EXTLINUX 3.00-3.02. + + On boot, ES:DI is supposed to point to the BIOS $PnP + structure, although in practice most operating systems + will search for it in memory. However, preserving + this while chainloading is probably a good idea. + + Note that FS:SI is a pointer to a memory location + containing the original ES:DI value, not the value + itself. + + + [PXELINUX] + Input: AX 000Ah + Output: AL 32h (PXELINUX) + DX PXE API version detected (DH=major, DL=minor) + ES:BX pointer to PXENV+ or !PXE structure + FS:SI pointer to original stack with invocation record + + Note: DX notes the API version detected by PXELINUX, + which may be more conservative than the actual version + available. For exact information examine the API + version entry in the PXENV+ structure, or the API + version entries in the ROMID structures pointed from + the !PXE structure. + + PXELINUX will use, and provide, the !PXE structure + over the PXENV+ structure. Examine the structure + signature to determine which particular structure was + provided. + + The FS:SI pointer points to the top of the original stack + provided by the PXE stack, with the following values + pushed at the time PXELINUX is started: + + [fs:si+0] GS <- top of stack + [fs:si+2] FS + [fs:si+4] ES + [fs:si+6] DS + [fs:si+8] EDI + [fs:si+12] ESI + [fs:si+16] EBP + [fs:si+20] - + [fs:si+24] EBX + [fs:si+28] EDX + [fs:si+32] ECX + [fs:si+36] EAX + [fs:si+40] EFLAGS + [fs:si+44] PXE return IP <- t.o.s. when PXELINUX invoked + [fs:si+46] PXE return CS + + + [ISOLINUX] + Input: AX 000Ah + Output: AL 33h (ISOLINUX) + DL drive number + CL 11 (sector size as a power of 2) [3.35] + CH mode [3.73] + 0 = El Torito + 1 = Hybrid (hard disk), CBIOS mode + 2 = Hybrid (hard disk), EBIOS mode + ES:BX pointer to El Torito spec packet + FS:SI pointer to initial ES:DI value [3.53] + + Note: Some very broken El Torito implementations do + not provide the spec packet information. If so, ES:BX + may point to all zeroes or to garbage. Call INT 13h, + AX=4B01h to obtain the spec packet directly from the + BIOS if necessary. + + This call gives information specific to a particular Syslinux + derivative. The value returned in AL is the same as is + returned in DL by INT 22h AX=0001h. + + +AX=000Bh [2.00] Get Serial Console Configuration + + Input: AX 000Bh + Output: DX serial port I/O base (e.g. 3F8h = COM1...) + CX baud rate divisor (1 = 115200 bps, 2 = 57600 bps...) + BX flow control configuration bits (see syslinux.txt) + -> bit 15 is set if the video console is disabled + + If no serial port is configured, DX will be set to 0 and the + other registers are undefined. + + +AX=000Ch [2.00] Perform final cleanup + Input: AX 000Ch + DX derivative-specific flags (0000h = clean up all) + Output: None + + This routine performs any "final cleanup" the boot loader + would normally perform before loading a kernel, such as + unloading the PXE stack in the case of PXELINUX. AFTER + INVOKING THIS CALL, NO OTHER API CALLS MAY BE INVOKED, NOR MAY + THE PROGRAM TERMINATE AND RETURN TO THE BOOT LOADER. This + call basically tells the boot loader "get out of the way, I'll + handle it from here." + + For COM32 images, the boot loader will continue to provide + interrupt and BIOS call thunking services as long its memory + areas (0x0800-0xffff, 0x100000-0x100fff) are not overwritten. + MAKE SURE TO DISABLE INTERRUPTS, AND INSTALL NEW GDT AND IDTS + BEFORE OVERWRITING THESE MEMORY AREAS. + + The permissible values for DX is an OR of these values: + + SYSLINUX: 0000h Normal cleanup + + PXELINUX: 0000h Normal cleanup + 0003h Keep UNDI and PXE stacks loaded + + ISOLINUX: 0000h Normal cleanup + + EXTLINUX: 0000h Normal cleanup + + All other values are undefined, and may have different + meanings in future versions of Syslinux. + + +AX=000Dh [2.08] Cleanup and replace bootstrap code + Input: AX 000Dh + DX derivative-specific flags (see previous function) + EDI bootstrap code (linear address, can be in high memory) + ECX bootstrap code length in bytes (must fit in low mem) + EBX(!) initial value of EDX after bootstrap + ESI initial value of ESI after bootstrap + DS initial value of DS after bootstrap + Output: Does not return + + This routine performs final cleanup, then takes a piece of + code, copies it over the primary bootstrap at address 7C00h, + and jumps to it. This can be used to chainload boot sectors, + MBRs, bootstraps, etc. + + Normal boot sectors expect DL to contain the drive number, + and, for hard drives (DL >= 80h) DS:SI to contain a pointer to + the 16-byte partition table entry. The memory between + 600h-7FFh is available to put the partition table entry in. + + For PXELINUX, if the PXE stack is not unloaded, all registers + (except DS, ESI and EDX) and the stack will be set up as they + were set up by the PXE ROM. + + +AX=000Eh [2.11] Get configuration file name + Input: AX 0000Eh + Output: ES:BX null-terminated file name string + + Returns the name of the configuration file. Note that it is + possible that the configuration file doesn't actually exist. + + +AX=000Fh [3.00] Get IPAPPEND strings [PXELINUX] + Input: AX 000Fh + Output: CX number of strings (currently 2) + ES:BX pointer to an array of NEAR pointers in + the same segment, one for each of the above + strings + + Returns the same strings that the "ipappend" option would have + added to the command line, one for each bit of the "ipappend" + flag value, so entry 0 is the "ip=" string and entry 1 is the + "BOOTIF=" string. + + +AX=0010h [3.00] Resolve hostname [PXELINUX] + Input: ES:BX pointer to null-terminated hostname + Output: EAX IP address of hostname (zero if not found) + + Queries the DNS server(s) for a specific hostname. If the + hostname does not contain a dot (.), the local domain name + is automatically appended. + + This function only return CF=1 if the function is not + supported. If the function is supported, but the hostname did + not resolve, it returns with CF=0, EAX=0. + + The IP address is returned in network byte order, i.e. if the + IP address is 1.2.3.4, EAX will contain 0x04030201. Note that + all uses of IP addresses in PXE are also in network byte order. + + +AX=0011h [3.05] Obsoleted in 3.80 + + +AX=0012h [3.50] Cleanup, shuffle and boot + Input: AX 0012h + DX derivative-specific flags (see function 000Ch) + ES:DI shuffle descriptor list (must be in low memory) + CX number of shuffle descriptors + EBX(!) initial value of EDX after bootstrap + ESI initial value of ESI after bootstrap + DS initial value of DS after bootstrap + EBP CS:IP of routine to jump to + Output: Does not return + (if CX is too large the routine returns with CF=1) + + This routine performs final cleanup, then performs a sequence + of copies, and jumps to a specified real mode entry point. + This is a more general version of function 000Dh, which can + also be used to load other types of programs. + + The copies must not touch memory below address 7C00h. + + ES:DI points to a list of CX descriptors each of the form: + + Offset Size Meaning + 0 dword destination address + 4 dword source address + 8 dword length in bytes + + The copies are overlap-safe, like memmove(). + + Starting in version 3.50, if the source address is -1 + (FFFFFFFFh) then the block specified by the destination + address and the length is set to all zero. + + Starting in version 3.50, if the destination address is -1 + (FFFFFFFFh) then the data block is loaded as a new set of + descriptors, and processing is continued (and unprocessed + descriptors are lost, this is thus typically only used as the + last descriptor in a block.) The block must still fit in the + internal descriptor buffer (see function 0011h), but can, of + course, itself chain another block. + + + Normal boot sectors expect DL to contain the drive number, + and, for hard drives (DL >= 80h) DS:SI to contain a pointer to + the 16-byte partition table entry. The memory between + 600h-7FFh is available to put the partition table entry in. + + For PXELINUX, if the PXE stack is not unloaded, all registers + (except DS, ESI and EDX) and the stack will be set up as they + were set up by the PXE ROM. + + This interface was probably broken before version 3.50. + + +AX=0013h [3.08] Idle loop call + Input: AX 0013h + Output: None + + Call this routine while sitting in an idle loop. It performs + any periodic activities required by the filesystem code. At + the moment, this is a no-op on all derivatives except + PXELINUX, where it executes PXE calls to answer ARP queries. + + Starting with version 3.10, this API call harmlessly returns + failure (CF=1) if invoked on a platform which does not need + idle calls. Additionally, it's safe to call this API call on + previous Syslinux versions (2.00 or later); it will just + harmlessly fail. Thus, if this call returns failure (CF=1), + it means that there is no technical reason to call this + function again, although doing so is of course safe. + + +AX=0014h [3.10] Local boot [PXELINUX, ISOLINUX] + Input: AX 0014h + DX Local boot parameter + Output: Does not return + + This function invokes the equivalent of the "localboot" + configuration file option. The parameter in DX is the same + parameter as would be entered after "localboot" in the + configuration file; this parameter is derivative-specific -- + see syslinux.txt for the definition. + + +AX=0015h [3.10] Get feature flags + Input: AX 0015h + Output: ES:BX pointer to flags in memory + CX number of flag bytes + + This function reports whether or not this Syslinux version and + derivative supports specific features. Keep in mind that + future versions might have more bits; remember to treat any + bits beyond the end of the array (as defined by the value in + CX) as zero. + + Currently the following feature flag is defined: + + Byte Bit Definition + ---------------------------------------------------- + 0 0 Local boot (AX=0014h) supported + 1 Idle loop call (AX=0013h) is a no-op + + All other flags are reserved. + + +AX=0016h [3.10] Run kernel image + Input: AX 0016h + DS:SI Filename of kernel image (zero-terminated string) + ES:BX Command line (zero-terminated string) + ECX IPAPPEND flags [PXELINUX] + EDX Type of file (since 3.50) + Output: Does not return if successful; returns with CF=1 if + the kernel image is not found. + + This function is similiar to AX=0003h Run command, except that + the filename and command line are treated as if specified in a + KERNEL and APPEND statement of a LABEL statement, which means: + + - The filename has to be exact; no variants are tried; + - No global APPEND statement is applied; + - ALLOWOPTIONS and IMPLICIT statements in the configuration + file do not apply. It is therefore important that the + COMBOOT module doesn't allow the end user to violate the + intent of the administrator. + + Additionally, this function returns with a failure if the file + doesn't exist, instead of returning to the command line. (It + may still return to the command line if the image is somehow + corrupt, however.) + + The file types are defined as follows: + + Equivalent + EDX Config Extensions Type of file + 0 KERNEL Determined by filename extension + 1 LINUX none Linux kernel image + 2 BOOT .bs .bin Bootstrap program + 3 BSS .bss Boot sector with patch [SYSLINUX] + 4 PXE .0 PXE Network Bootstrap Prog [PXELINUX] + 5 FDIMAGE .img Floppy disk image [ISOLINUX] + 6 COMBOOT .com .cbt 16-bit COMBOOT program + 7 COM32 .c32 COM32 program + 8 CONFIG Configuration file + + +AX=0017h [3.30] Report video mode change + Input: AX 0017h + BX Video mode flags + Bit 0: graphics mode + Bit 1: non-default mode + Bit 2: VESA mode + Bit 3: text functions not supported + CX For graphics modes, pixel columns + DX For graphics modes, pixel rows + Output: None + + This function is used to report video mode changes to + Syslinux. It does NOT actually change the video mode, but + rather, allows Syslinux to take appropriate action in response + to a video mode change. Modes that cannot be exited either + with the conventional BIOS mode set command (INT 10h, AH=00h) + or the VESA VBE mode set command (INT 10h, AX=4F02h) should + not be used. + + This function returns with a failure if BX contains any bits + which are undefined in the current version of Syslinux. + + The following bits in BX are currently defined: + + Bit 0: graphics mode + + Indicates that the mode is a graphics mode, as opposed + to a text mode. + + Bit 1: non-standard mode + + A non-standard mode is any mode except text mode and + graphics mode 0012h (VGA 640x480, 16 color.) + + Bit 2: VESA mode + + This mode is a VESA mode, and has to be exited with + the VESA VBE API (INT 10h, AX=4F02h) as opposed to the + conventional BIOS API (INT 10h, AH=00h). + + Bit 3: Text functions not supported + + This indicates that the BIOS text output functions + (INT 10h, AH=02h, 03h, 06h, 09h, 0Eh, 11h) don't work. + If this bit is set, Syslinux will reset the mode + before printing any characters on the screen. + + This is common for VESA modes. + + +AX=0018h [3.30] Query custom font + Input: AX 0018h + Output: AL Height of custom font in scan lines, or zero + ES:BX Pointer to custom font in memory + + This call queries if a custom display font has been loaded via + the "font" configuration file command. If no custom font has + been loaded, AL contains zero. + + +AX=0019h [3.50] Read disk [SYSLINUX, ISOLINUX, EXTLINUX] + Input: AX 0019h + EDX Sector number + ESI Reserved - MUST BE ZERO + EDI Reserved - MUST BE ZERO + CX Sector count + ES:BX Buffer address + Output: None + + Read disk blocks from the active filesystem (partition); for + disks, sector number zero is the boot sector. For ISOLINUX, + this call reads the CD-ROM. + + For compatiblity with all systems, the buffer should + *neither* cross 64K boundaries, *nor* wrap around the segment. + + This routine reports "boot failed" (and does not return) on + disk error. + + Note: for ISOLINUX in hybrid mode, this call uses simulated + 2048-byte CD-ROM sector numbers. + + +AX=001Ah [3.50] Obsoleted in 3.80 + + +AX=001Bh [3.50] Obsoleted in 3.80 + + +AX=001Ch [3.60] Get pointer to auxilliary data vector + Input: AX 001Ch + Output: ES:BX Auxilliary data vector + CX Size of the ADV (currently 500 bytes) + + The auxillary data vector is a tagged data structure used + to carry a small amount of information (up to 500 bytes) from + one boot to another. + + +AX=001Dh [3.60] Write auxilliary data vector + Input: AX 001Dh + Output: None + + Write the auxilliary data vector back to disk. Returns + failure for non-disk-based derivatives unless the "auxdata" + configuration command is used to specify a disk location + (not yet implemented.) + + In a future version, PXELINUX may end up attempting to save + the ADV on the server via TFTP write. + + +AX=001Eh [3.74] Keyboard remapping table + Input: AX 001Eh + DX 0000h - all other values reserved + Output: AX format version (1) + CX length in bytes (256) + ES:BX pointer to keyboard table + + This call queries the keyboard remapping table. For the current + version, the format code is always 1 and the length is always + 256. This version can be updated simply by overwriting the version + in memory; this may not be true in the future. + + +AX=001Fh [3.74] Get current working directory + Input: AX 0001Eh + Output: ES:BX null-terminated directory name string + + Returns the current working directory. For SYSLINUX, ISOLINUX, + and PXELINUX, this will be an absolute path. For EXTLINUX, it + currently returns "./". + + +AX=0020h [3.74] Open directory + Input: AX 001Fh + ES:SI /-null-terminated directory name + Output: SI directory handle + EAX clobbered + + Open a directory for reading. Directory name must have a trailing + "/" before the null (otherwise, you're looking for a file)(This + may change as this is a BETA call). + + +AX=0021h [3.74] Read directory + Input: AX 0020h + SI directory handle + ES:DI buffer for file name + Output: DL Type of file + SI directory handle, or 0 if end of directory was reached + EAX Size of file + EBX Inode of file + + Read one filename from the directory, incrementing the + directory structure at SI as appropriate, storing the filename + into the buffer at ES:DI, and returning the type of the file + in DL, the file length in EAX, the inode/file number in EBX + and the updated directory handle. + + +AX=0022h [3.74] Close directory + Input: AX 001Fh + SI directory handle + Output SI 0 + + Closes a directory. + + +AX=0023h [3.80] Get shuffler parameters + Input: AX 0023h + Output: CX size of shuffler "safe area" in bytes + Other registers reserved for future use + + This call gives the size of the required shuffler "safe area", + in bytes; for call 0024h. In the future, it may provide + additional parameters. + + +AX=0024h [3.80] Cleanup, shuffle and boot, raw version + Input: AX 0024h + DX derivative-specific flags (see function 000Ch) + EDI shuffle descriptor list safe area + ESI shuffle descriptor list source + ECX byte count of shuffle descriptor list + Output: Does not return + + This routine performs final cleanup, then performs a sequence + of copies, and jumps to a specified real mode entry point. + This is a more general version of function 000Dh, which can + also be used to load other types of programs. + + Unlike previous obsolete versions of this function, there are + no restrictions that copies must not touch memory below + address 7C00h. Either the shuffle descriptor list or the safe + area (or both) may be located in high memory. + + ESI points to a list of descriptors each of the form: + + Offset Size Meaning + 0 dword destination address + 4 dword source address (-1 = zero) + 8 dword length in bytes (0 = end of list) + + The copies are overlap-safe, like memmove(). + + Before actually executing the move list, the list is moved to + the address specified in EDI. The caller is responsibe to + ensure that the moved descriptor list plus a "safe area" + immediately afterwards (the size of which is specified by + function 0023h) is not disturbed by the copy sequence. It is, + however, safe to overwrite descriptors already consumed. + + If the source address is -1 (FFFFFFFFh) then the block + specified by the destination address and the length is set to + all zero. + + The list is terminated by an entry with length 0. For that + entry, the destination is used as an entry point, and the + source represents the type of entry point: + + 0 16-bit protected mode (dst is CS.base) + 1 Flat 32-bit protected mode (dst is EIP) + + This routine does not set up any GPR register state + whatsoever, including stack. It is the responsibility of the + caller to make sure the entry point provided sets up any + registers needed. + + For mode 0 (16-bit real mode), EAX will contain CR0 with bit 0 + masked out, suitable for loading into CR0 to immediately enter + real mode. Note: if real-mode entry is planned, + (CS.base & 0xfff0000f) should == 0 for compatibility with KVM, + and possibly other virtualization solutions. + + In both mode 0 and mode 1, the data segments will be loaded + with read/write data segments, matching the respective code + segment. For mode 0, B=0 and the limits will be 64K, for mode + 1, B=1 and the limits will be 4 GB. diff --git a/doc/distrib.txt b/doc/distrib.txt new file mode 100644 index 0000000..fa10a04 --- /dev/null +++ b/doc/distrib.txt @@ -0,0 +1,29 @@ +For creators of Linux distributions: + +Syslinux is a notoriously hard program to debug, since it runs outside +of any operating system, and has a tendency to expose BIOS and +hardware bugs on various systems. Therefore, I would appreciate if +you would resist the temptation of recompiling the Syslinux bootloader +itself (ldlinux.asm) if at all possible. If you do that, I will have +to refer any bug reports I receive back to the respective distributor. + +However, I have no such concerns about recompiling the installer +programs, and in fact, with both libc 5 and libc 6 in common use in +the Linux world today I understand if you wish to relink the +Linux-based installer against your system version of libc. Therefore +a special makefile targets "make installer" has been included with the +Syslinux distribution, starting with version 1.42. + +To rebuild the installer programs *only*, starting from a freshly +untarred distribution copy of Syslinux, do: + + make clean + make installer + +If you want to remove all intermediate files, including the ones +obtained from assembling ldlinux.asm and which are included in the +distribution, do "make spotless". + +I appreciate your assistance in this matter. + + H. Peter Anvin diff --git a/doc/extlinux.txt b/doc/extlinux.txt new file mode 100644 index 0000000..53f9f48 --- /dev/null +++ b/doc/extlinux.txt @@ -0,0 +1,132 @@ +EXTLINUX is a new Syslinux derivative, which boots from a Linux +ext2/ext3 filesystem. + +It works the same way as SYSLINUX (see doc/syslinux.txt), with a few +slight modifications. + +1. The installer is run on a *mounted* filesystem. Run the extlinux + installer on the directory in which you want extlinux installed: + + extlinux --install /boot + + Specify --install (-i) to install for the first time, or + --update (-U) to upgrade a previous installation. + + NOTE: this doesn't have to be the root directory of a filesystem. + If /boot is a filesystem, you can do: + + mkdir -p /boot/extlinux + extlinux --install /boot/extlinux + + ... to create a subdirectory and install extlinux in it. + /boot/extlinux is the recommended location for extlinux. + + +2. The configuration file is called "extlinux.conf", and is expected + to be found in the same directory as extlinux is installed in. + + +3. Pathnames can be absolute or relative; if absolute (with a leading + slash), they are relative to the root of the filesystem on which + extlinux is installed (/boot in the example above), if relative, + they are relative to the extlinux directory. + + extlinux supports subdirectories, but the total path length is + limited to 511 characters. + + +4. EXTLINUX now supports symbolic links. However, extremely long + symbolic links might hit the pathname limit. Also, please note + that absolute symbolic links are interpreted from the root *of the + filesystem*, which might be different from how the running system + would interpret it (e.g. in the case of a separate /boot + partition.) Therefore, use relative symbolic links if at all + possible. + + +5. EXTLINUX now has "boot-once" support. The boot-once information is + stored in an on-disk datastructure, part of extlinux.sys, called + the "Auxillary Data Vector". The Auxilliary Data Vector is also + available to COMBOOT/COM32 modules that want to store small amounts + of information. + + To set the boot-once information, do: + + extlinux --once 'command' /boot/extlinux + + where 'command' is any command you could enter at the Syslinux + command line. It will be executed on the next boot and then + erased. + + To clear the boot-once information, do: + + extlinux --clear-once /boot/extlinux + + If EXTLINUX is used on a RAID-1, this is recommended, since under + certain circumstances a RAID-1 rebuild can "resurrect" the + boot-once information otherwise. + + To clear the entire Auxillary Data Vector, do: + + extlinux --reset-adv /boot/extlinux + + This will erase all data stored in the ADV, including boot-once. + + The --once, --clear-once, and --reset-adv commands can be combined + with --install or --update, if desired. The ADV is preserved + across updates, unless --reset-adv is specified. + + +Note that EXTLINUX installs in the filesystem partition like a +well-behaved bootloader :) Thus, it needs a master boot record in the +partition table; the mbr.bin shipped with Syslinux should work well. +To install it just do: + + cat mbr.bin > /dev/XXX + +... where /dev/XXX is the appropriate master device, e.g. /dev/hda, +and make sure the correct partition in set active. + + +If you have multiple disks in a software RAID configuration, the +preferred way to boot is: + +- Create a separate RAID-1 partition for /boot. Note that the Linux + RAID-1 driver can span as many disks as you wish. + +- Install the MBR on *each disk*, and mark the RAID-1 partition + active. + +- Run "extlinux -i /boot" to install extlinux. This will install it on + all the drives in the RAID-1 set, which means you can boot any + combination of drives in any order. + + + +It is not required to re-run the extlinux installer after installing +new kernels. If you are using ext3 journalling, however, it might be +desirable to do so, since running the extlinux installer will flush +the log. Otherwise a dirty shutdown could cause some of the new +kernel image to still be in the log. This is a general problem for +boot loaders on journalling filesystems; it is not specific to +extlinux. The "sync" command does not flush the log on the ext3 +filesystem. + + +The Syslinux Project boot loaders support chain loading other +operating systems via a separate module, chain.c32 (located in +com32/modules/chain.c32). To use it, specify a LABEL in the +configuration file with KERNEL chain.c32 and APPEND [hd|fd] +[] + +For example: + +# Windows CE/ME/NT, a very dense operating system. +# Second partition (2) on the first hard disk (hd0); +# Linux would *typically* call this /dev/hda2 or /dev/sda2. +LABEL cement + KERNEL chain.c32 + APPEND hd0 2 + +See also doc/menu.txt. + diff --git a/doc/gpt.txt b/doc/gpt.txt new file mode 100644 index 0000000..14c1ee8 --- /dev/null +++ b/doc/gpt.txt @@ -0,0 +1,88 @@ + GPT boot protocol + +There is no official MBR-to-partition handover protocol defined for +booting from disks partitioned using GPT partition tables with +BIOS-style firmware. This is because the GPT partition format comes +from the EFI spec, which thinks the universe is all going to be EFI. +Sigh. + +There are thus two alternatives: hybrid booting, and defining a new +protocol. + + *** Hybrid booting *** + +Hybrid booting uses a standard MBR, and has bootable ("active") +partitions present, as partitions, in the GPT PMBR sector. This means +the PMBR, instead of containing only one "protective" partition (type +EE), may contain up to three partitions: a protective partition (EE) +*before* the active partition, the active partition, and a protective +partition (EE) *after* the active partition. The active partition is +limited to the first 2^32 sectors (2 TB) of the disk. + +All partitions, including the active partition, should have GPT +partition entries. Thus, changing which partition is active does NOT +change the GPT partition table. + +This is the only known way to boot Microsoft operating systems from a +GPT disk with BIOS firmware. + + + *** New protocol *** + +This defines an alternative (experimental) booting protocol for GPT +partitions with BIOS firmware. It maintains backwards compatibility +to the extent possible. It is implemented by the file mbr/gptmbr.bin. + + -> The PMBR + +The PMBR (the first 512-byte sector of the disk) is divided up as +follows: + + Offset Size Contents + --------------------------------------------------------- + 0 424 PMBR boot code + 424 16 GUID of the boot partition + 440 4 MBR-compatible disk ID + 444 2 Magic number: 1D 9A + 446 16 PMBR protective entry + 462 48 PMBR null entries + 510 2 Boot signature: 55 AA + +To change the bootable partition, verify that the magic number is +present (to avoid corrupting software not compatible with this +specification) and enter the GUID of the boot partition at offset +424. It might be wise to verify that the data already there is a +valid partition GUID already, or at least warn the user if that is not +the case. + + -> The handover protocol + +The PMBR boot code loads the first sector of the bootable partition, +and passes in DL=, ES:DI=, sets EAX to +0x54504721 ("!GPT") and points DS:SI to a structure of the following +form: + + Offset Size Contents + --------------------------------------------------------- + 0 1 0x80 (this is a bootable partition) + 1 3 CHS of partition (using INT 13h geometry) + 4 1 0xEE (partition type: EFI data partition) + 5 3 CHS of partition end + 8 4 Partition start LBA + 12 4 Partition end LBA + 16 varies GPT partition entry + +The CHS information is optional; gptmbr.bin currently does *NOT* +calculate them, and just leaves them as zero. + +Bytes 0-15 matches the standard MBR handover (DS:SI points to the +partition entry), except that the information is provided +synthetically. The MBR-compatible fields are directly usable if they +are < 2 TB, otherwise these fields should contain 0xFFFFFFFF and the +OS will need to understand the GPT partition entry which follows the +MBR one. The "!GPT" magic number in EAX and the 0xEE partition type +also informs the OS that the GPT partition information is present. + +Currently, this is compatible with Syslinux as long as the Syslinux +partition is < 2 TB; this probably will be improved in a future +version. diff --git a/doc/isolinux.txt b/doc/isolinux.txt new file mode 100644 index 0000000..eca2a97 --- /dev/null +++ b/doc/isolinux.txt @@ -0,0 +1,133 @@ + ISOLINUX + + A bootloader for Linux using ISO 9660/El Torito CD-ROMs + + Copyright 1994-2008 H. Peter Anvin - All Rights Reserved + +This program is provided under the terms of the GNU General Public +License, version 2 or, at your option, any later version. There is no +warranty, neither expressed nor implied, to the function of this +program. Please see the included file COPYING for details. + +---------------------------------------------------------------------- + +ISOLINUX is a boot loader for Linux/i386 that operates off ISO 9660/El +Torito CD-ROMs in "no emulation" mode. This avoids the need to create +an "emulation disk image" with limited space (for "floppy emulation") +or compatibility problems (for "hard disk emulation".) + +This documentation isn't here yet, but here is enough that you should +be able to test it out: + +Make sure you have a recent enough version of mkisofs. I recommend +mkisofs 1.13 (distributed with cdrecord 1.9), but 1.12 might work as +well (not tested.) + +To create an image, create a directory called "isolinux" (or, if you +prefer, "boot/isolinux") underneath the root directory of your ISO +image master file tree. Copy isolinux.bin, a config file called +"isolinux.cfg" (see syslinux.txt for details on the configuration +file), and all necessary files (kernels, initrd, display files, etc.) +into this directory, then use the following command to create your ISO +image (add additional options as appropriate, such as -J or -R): + + mkisofs -o \ + -b isolinux/isolinux.bin -c isolinux/boot.cat \ + -no-emul-boot -boot-load-size 4 -boot-info-table \ + + +(If you named the directory boot/isolinux that should of course be +-b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat.) + +ISOLINUX resolves pathnames the following way: + +- A pathname consists of names separated by slashes, Unix-style. +- A leading / means it searches from the root directory; otherwise the + search is from the isolinux directory (think of this as the "current + directory".) +- . and .. in pathname searches are not supported. +- The maximum length of any pathname is 255 characters. + +Note that ISOLINUX only uses the "plain" ISO 9660 filenames, i.e. it +does not support Rock Ridge or Joliet filenames. It can still be used +on a disk which uses Rock Ridge and/or Joliet extensions, of course. +Under Linux, you can verify the plain filenames by mounting with the +"-o norock,nojoliet" option to the mount command. Note, however, that +ISOLINUX does support "long" (level 2) ISO 9660 plain filenames, so if +compatibility with short-names-only operating systems like MS-DOS is +not an issue, you can use the "-l" or "-iso-level 2" option to mkisofs +to generate long (up to 31 characters) plain filenames. + +ISOLINUX does not support discontiguous files, interleaved mode, or +logical block and sector sizes other than 2048. This should normally +not be a problem. + +ISOLINUX is by default built in two versions, one version with extra +debugging messages enabled. If you are having problems with ISOLINUX, +I would greatly appreciate if you could try out the debugging version +(isolinux-debug.bin) and let me know what it reports. The debugging +version does not include hybrid mode support (see below.) + + + ++++ NOTE ON THE CONFIG FILE DIRECTORY ++++ + +ISOLINUX will search for the config file directory in the order +/boot/isolinux, /isolinux, /. The first directory that exists is +used, even if it contains no files. Therefore, please make sure that +these directories don't exist if you don't want ISOLINUX to use them. + + + ++++ HYBRID CD-ROM/HARD DISK MODE ++++ + +Starting in version 3.72, ISOLINUX supports a "hybrid mode" which can +be booted from either CD-ROM or from a device which BIOS considers a +hard disk or ZIP disk, e.g. a USB key or similar. + +To enable this mode, the .iso image should be postprocessed with the +"isohybrid" script from the utils directory: + + isohybrid filename.iso + +This script creates the necessary additional information to be able to +boot in hybrid mode. It also pads out the image to an even multiple +of 1 MB. + +This image can then be copied using any raw disk writing tool (on Unix +systems, typically "dd" or "cat") to a USB disk, or written to a +CD-ROM using standard CD burning tools. + +The ISO 9660 filesystem is encapsulated in a partition (which starts +at offset zero, which may confuse some systems.) This makes it +possible for the operating system, once booted, to use the remainder +of the device for persistent storage by creating a second partition. + + + ++++ BOOTING DOS (OR OTHER SIMILAR OPERATING SYSTEMS) ++++ + +WARNING: This feature depends on BIOS functionality which is +apparently broken in a very large number of BIOSes. Therefore, this +may not work on any particular system. No workaround is possible; if +you find that it doesn't work please complain to your vendor and +indicate that "BIOS INT 13h AX=4C00h fails." + +To boot DOS, or other real-mode operating systems (protected-mode +operating systems may or may not work correctly), using ISOLINUX, you +need to prepare a disk image (usually a floppy image, but a hard disk +image can be used on *most* systems) with the relevant operating +system. This file should be included on the CD-ROM in the /isolinux +directory, and have a .img extension. The ".img" extension does not +have to be specified on the command line, but has to be explicitly +specified if used in a "kernel" statement in isolinux.cfg. + +For a floppy image, the size of the image should be exactly one of the +following: + + 1,228,800 bytes - For a 1200K floppy image + 1,474,560 bytes - For a 1440K floppy image + 2,949,120 bytes - For a 2880K floppy image + +Any other size is assumed to be a hard disk image. In order to work +on as many systems as possible, a hard disk image should have exactly +one partition, marked active, that covers the entire size of the disk +image file. Even so, hard disk images are not supported on all +BIOSes. diff --git a/doc/keytab-lilo.txt b/doc/keytab-lilo.txt new file mode 100644 index 0000000..cdbea0f --- /dev/null +++ b/doc/keytab-lilo.txt @@ -0,0 +1,85 @@ +This is the documentation for the keytab-lilo.pl program. It was +taken verbatim from the LILO-20 README file; only this header was +added. + +LILO program code, documentation and auxiliary programs are +Copyright 1992-1997 Werner Almesberger. +All rights reserved. + +Redistribution and use in source and binary forms of parts of or the +whole original or derived work are permitted provided that the +original work is properly attributed to the author. The name of the +author may not be used to endorse or promote products derived from +this software without specific prior written permission. This work +is provided "as is" and without any express or implied warranties. + +To use a LILO keyboard table with Syslinux, specify the KBDMAP command +in syslinux.cfg, for example: + + kbdmap de.ktl + +============================================================================ + +Keyboard translation +-------------------- + +The PC keyboard emits so-called scan codes, which are basically key +numbers. The BIOS then translates those scan codes to the character codes +of the characters printed on the key-caps. By default, the BIOS normally +assumes that the keyboard has a US layout. Once an operating system is +loaded, this operating system can use a different mapping. + +At boot time, LILO only has access to the basic services provided by the +BIOS and therefore receives the character codes for an US keyboard. It +provides a simple mechanism to re-map the character codes to what is +appropriate for the actual layout.* + + * The current mechanism isn't perfect, because it sits on top of the + scan code to character code translation performed by the BIOS. This + means that key combinations that don't produce any useful character on + the US keyboard will be ignored by LILO. The advantage of this approach + is its simplicity. + + +Compiling keyboard translation tables +- - - - - - - - - - - - - - - - - - - + +LILO obtains layout information from the keyboard translation tables Linux +uses for the text console. They are usually stored in +/usr/lib/kbd/keytables. LILO comes with a program keytab-lilo.pl that reads +those tables and generates a table suitable for use by the map installer. +keytab-lilo.pl invokes the program loadkeys to print the tables in a format +that is easy to parse.* + + * On some systems, only root can execute loadkeys. It is then necessary + to run keytab-lilo.pl as root too. + +keytab-lilo.pl is used as follows: + + keytab-lilo.pl [ -p = ] ... + [][.] ] + [][.] ] + + -p = + Specifies corrections ("patches") to the mapping obtained from the + translation table files. E.g. if pressing the upper case "A" should + yield an at sign, -p 65=64 would be used. The -p option can be + repeated any number of times. The codes can also be given as + hexadecimal or as octal numbers if they are prefixed with 0x or 0, + respectively. + The directory in which the file resides. The default path is + /usr/lib/kbd/keytables. + Usually the trailing .map, which is automatically added if + the file name doesn't contain dots. + Is the layout which specifies the translation by the + BIOS. If none is specified, us is assumed. + Is the actual layout of the keyboard. + +keytab-lilo.pl writes the resulting translation table as a binary string to +standard output. Such tables can be stored anywhere with any name, but the +suggested naming convention is /boot/.ktl ("Keyboard Table for Lilo"), +where is the name of the keyboard layout. + +Example: + +keytab-lilo.pl de >/boot/de.ktl diff --git a/doc/mboot.txt b/doc/mboot.txt new file mode 100644 index 0000000..ef00ca5 --- /dev/null +++ b/doc/mboot.txt @@ -0,0 +1,26 @@ + +mboot.c32 +--------- + +mboot.c32 is a 32-bit comboot module that allows Syslinux and its +variants to load and boot kernels that use the Multiboot standard +(e.g. the Xen virtual machine monitor, and the Fiasco and GNU Mach +microkernels). + +To load a multiboot kernel and modules in Syslinux, put mboot.c32 (from +com32/modules) in the boot directory, and load it as the "kernel" in the +configuration file. The command-line to pass to mboot.c32 is the kernel +command-line, followed by all the module command lines, separated with +'---'. For example, to load a Xen VMM, xenlinux and an initrd: + +DEFAULT mboot.c32 xen.gz dom0_mem=15000 nosmp noacpi --- linux.gz console=tty0 root=/dev/hda1 --- initrd.img + +or, as a choice in a menu: + +LABEL Xen + KERNEL mboot.c32 + APPEND xen.gz dom0_mem=15000 nosmp noacpi --- linux.gz console=tty0 root=/dev/hda1 --- initrd.img + +mboot.c32 requires version 2.12 or later of Syslinux. + +Tim Deegan, May 2005 diff --git a/doc/memdisk.txt b/doc/memdisk.txt new file mode 100644 index 0000000..79d7640 --- /dev/null +++ b/doc/memdisk.txt @@ -0,0 +1,236 @@ +[This documentation is rather crufty at the moment.] + +MEMDISK is meant to allow booting legacy operating systems via PXE, +and as a workaround for BIOSes where ISOLINUX image support doesn't +work. + +MEMDISK simulates a disk by claiming a chunk of high memory for the +disk and a (very small - 2K typical) chunk of low (DOS) memory for the +driver itself, then hooking the INT 13h (disk driver) and INT 15h +(memory query) BIOS interrupts. + +To use it, type on the Syslinux command line: + +memdisk initrd=diskimg.img + +... where diskimg.img is the disk image you want to boot from. + +[Obviously, the memdisk binary as well as your disk image file need to +be present in the boot image directory.] + +... or add to your syslinux.cfg/pxelinux.cfg/isolinux.cfg something like: + +label dos + kernel memdisk + append initrd=dosboot.img + +Note the following: + +a) The disk image can be uncompressed or compressed with gzip or zip. + +b) If the disk image is less than 4,194,304 bytes (4096K, 4 MB) it is + assumed to be a floppy image and MEMDISK will try to guess its + geometry based on the size of the file. MEMDISK recognizes all the + standard floppy sizes as well as common extended formats: + + 163,840 bytes (160K) c=40 h=1 s=8 5.25" SSSD + 184,320 bytes (180K) c=40 h=1 s=9 5.25" SSSD + 327,680 bytes (320K) c=40 h=2 s=8 5.25" DSDD + 368,640 bytes (360K) c=40 h=2 s=9 5.25" DSDD + 655,360 bytes (640K) c=80 h=2 s=8 3.5" DSDD + 737,280 bytes (720K) c=80 h=2 s=9 3.5" DSDD + 1,222,800 bytes (1200K) c=80 h=2 s=15 5.25" DSHD + 1,474,560 bytes (1440K) c=80 h=2 s=18 3.5" DSHD + 1,638,400 bytes (1600K) c=80 h=2 s=20 3.5" DSHD (extended) + 1,720,320 bytes (1680K) c=80 h=2 s=21 3.5" DSHD (extended) + 1,763,328 bytes (1722K) c=82 h=2 s=21 3.5" DSHD (extended) + 1,784,832 bytes (1743K) c=83 h=2 s=21 3.5" DSHD (extended) + 1,802,240 bytes (1760K) c=80 h=2 s=22 3.5" DSHD (extended) + 1,884,160 bytes (1840K) c=80 h=2 s=23 3.5" DSHD (extended) + 1,966,080 bytes (1920K) c=80 h=2 s=24 3.5" DSHD (extended) + 2,949,120 bytes (2880K) c=80 h=2 s=36 3.5" DSED + 3,194,880 bytes (3120K) c=80 h=2 s=39 3.5" DSED (extended) + 3,276,800 bytes (3200K) c=80 h=2 s=40 3.5" DSED (extended) + 3,604,480 bytes (3520K) c=80 h=2 s=44 3.5" DSED (extended) + 3,932,160 bytes (3840K) c=80 h=2 s=48 3.5" DSED (extended) + + A small perl script is included in the MEMDISK directory which can + determine the geometry that MEMDISK would select for other sizes; + in general MEMDISK will correctly detect most physical extended + formats used, with 80 cylinders or slightly more. + + If the image is 4 MB or larger, it is assumed to be a hard disk + image, and should typically have an MBR and a partition table. It + may optionally have a DOSEMU geometry header; in which case the + header is used to determine the C/H/S geometry of the disk. + Otherwise, the geometry is determined by examining the partition + table, so the entire image should be partitioned for proper + operation (it may be divided between multiple partitions, however.) + + You can also specify the geometry manually with the following command + line options: + + c=# Specify number of cylinders (max 1024[*]) + h=# Specify number of heads (max 256[*]) + s=# Specify number of sectors (max 63) + floppy[=#] The image is a floppy image[**] + harddisk[=#] The image is a hard disk image[**] + + # represents a decimal number. + + [*] MS-DOS only allows max 255 heads, and only allows 255 cylinders + on floppy disks. + + [**] Normally MEMDISK emulates the first floppy or hard disk. This + can be overridden by specifying an index, e.g. floppy=1 will + simulate fd1 (B:). This may not work on all operating systems + or BIOSes. + +c) The disk is normally writable (although, of course, there is + nothing backing it up, so it only lasts until reset.) If you want, + you can mimic a write-protected disk by specifying the command line + option: + + ro Disk is readonly + +d) MEMDISK normally uses the BIOS "INT 15h mover" API to access high + memory. This is well-behaved with extended memory managers which load + later. Unfortunately it appears that the "DOS boot disk" from + WinME/XP *deliberately* crash the system when this API is invoked. + The following command-line options tells MEMDISK to enter protected + mode directly, whenever possible: + + raw Use raw access to protected mode memory. + + bigraw Use raw access to protected mode memory, and leave the + CPU in "big real" mode afterwards. + + int Use plain INT 15h access to protected memory. This assumes + that anything which hooks INT 15h knows what it is doing. + + safeint Use INT 15h access to protected memory, but invoke + INT 15h the way it was *before* MEMDISK was loaded. + This is the default since version 3.73. + +e) MEMDISK by default supports EDD/EBIOS on hard disks, but not on + floppy disks. This can be controlled with the options: + + edd Enable EDD/EBIOS + noedd Disable EDD/EBIOS + +f) The following option can be used to pause to view the messages: + + pause Wait for a keypress right before booting + + +Some interesting things to note: + +If you're using MEMDISK to boot DOS from a CD-ROM (using ISOLINUX), +you might find the generic El Torito CD-ROM driver by Gary Tong and +Bart Lagerweij useful: + + http://www.nu2.nu/eltorito/ + + +Similarly, if you're booting DOS over the network using PXELINUX, you +can use the "keeppxe" option and use the generic PXE (UNDI) NDIS +network driver, which is part of the PROBOOT.EXE distribution from +Intel: + + http://www.intel.com/support/network/adapter/1000/software.htm + + +Additional technical information: + +Starting with version 2.08, MEMDISK now supports an installation check +API. This works as follows: + + EAX = 454D08xxh ("ME") (08h = parameter query) + ECX = 444Dxxxxh ("MD") + EDX = 5349xxnnh ("IS") (nn = drive #) + EBX = 3F4Bxxxxh ("K?") + INT 13h + +If drive nn is a MEMDISK, the registers will contain: + + EAX = 4D21xxxxh ("!M") + ECX = 4D45xxxxh ("EM") + EDX = 4944xxxxh ("DI") + EBX = 4B53xxxxh ("SK") + + ES:DI -> MEMDISK info structures + +The low parts of EAX/ECX/EDX/EBX have the normal return values for INT +13h, AH=08h, i.e. information of the disk geometry etc. + +See Ralf Brown's interrupt list, +http://www.cs.cmu.edu/afs/cs.cmu.edu/user/ralf/pub/WWW/files.html or +http://www.ctyme.com/rbrown.htm, for a detailed description. + +The MEMDISK info structure currently contains: + + [ES:DI] word Total size of structure (currently 30 bytes) + [ES:DI+2] byte MEMDISK minor version + [ES:DI+3] byte MEMDISK major version + [ES:DI+4] dword Pointer to MEMDISK data in high memory + [ES:DI+8] dword Size of MEMDISK data in 512-byte sectors + [ES:DI+12] 16:16 Far pointer to command line + [ES:DI+16] 16:16 Old INT 13h pointer + [ES:DI+20] 16:16 Old INT 15h pointer + [ES:DI+24] word Amount of DOS memory before MEMDISK loaded + [ES:DI+26] byte Boot loader ID + [ES:DI+27] byte Currently unused + [ES:DI+28] word If nonzero, offset (vs ES) to installed DPT + This pointer+16 contains the original INT 1Eh + +Sizes of this structure: + +3.71+ 30 bytes Added DPT pointer +3.00-3.70 27 bytes Added boot loader ID +pre-3.00 26 bytes + +In addition, the following fields are available at [ES:0]: + + [ES:0] word Offset of INT 13h routine (segment == ES) + [ES:2] word Offset of INT 15h routine (segment == ES) + +The program mdiskchk.c in the sample directory is an example on how +this API can be used. + +The following code can be used to "disable" MEMDISK. Note that it +does not free the handler in DOS memory, and that running this from +DOS will probably crash your machine (DOS doesn't like drives suddenly +disappearing from underneath.) This is also not necessarily the best +method for this. + + mov eax, 454D0800h + mov ecx, 444D0000h + mov edx, 53490000h + mov dl,drive_number + mov ebx, 3F4B0000h + int 13h + + shr eax, 16 + cmp ax, 4D21h + jne not_memdisk + shr ecx, 16 + cmp cx, 4D45h + jne not_memdisk + shr edx, 16 + cmp dx, 4944h + jne not_memdisk + shr ebx, 16 + cmp bx, 4B53h + jne not_memdisk + + cli + mov bx,[es:0] ; INT 13h handler offset + mov eax,[es:di+16] ; Old INT 13h handler + mov byte [es:bx], 0EAh ; FAR JMP + mov [es:bx+1], eax + + mov bx,[es:2] ; INT 15h handler offset + mov eax,[es:di+20] ; Old INT 15h handler + mov byte [es:bx], 0EAh ; FAR JMP + mov [es:bx+1], eax + sti diff --git a/doc/menu.txt b/doc/menu.txt new file mode 100644 index 0000000..97e172f --- /dev/null +++ b/doc/menu.txt @@ -0,0 +1,534 @@ +There are two menu systems included with Syslinux, the advanced menu +system, and the simple menu system. + + ++++ THE ADVANCED MENU SYSTEM +++ + +The advanced menu system, written by Murali Krishnan Ganapathy, is +located in the menu/ subdirectly. It allows the user to create +hierarchial submenus, dynamic options, checkboxes, and just about +anything you want. It requires that the menu is compiled from a +simple C file, see menu/simple.c and menu/complex.c for examples. + +The advanced menu system doesn't support serial console at this time. + +See menu/README for more information. + + ++++ THE SIMPLE MENU SYSTEM +++ + +The simple menu system is a single module located at +com32/modules/vesamenu.c32 (graphical) or com32/modules/menu.c32 (text +mode only). It uses the same configuration file as the regular +Syslinux command line, and displays all the LABEL statements. + +To use the menu system, simply make sure [vesa]menu.c32 is in the +appropriate location for your boot medium (the same directory as the +configuration file for SYSLINUX, EXTLINUX and ISOLINUX, and the same +directory as pxelinux.0 for PXELINUX), and put the following options +in your configuration file: + +UI menu.c32 + + +There are a few menu additions to the configuration file, all starting +with the keywords MENU or TEXT; like the rest of the Syslinux config +file language, it is case insensitive: + + +MENU TITLE title + + Give the menu a title. The title is presented at the top of + the menu. + + +MENU HIDDEN + + Do not display the actual menu unless the user presses a key. + All that is displayed is a timeout message. + + +MENU SHIFTKEY + + Exit the menu system immediately unless either the Shift or Alt + key is pressed, or Caps Lock or Scroll Lock is set. + + +MENU SEPARATOR + + Insert an empty line in the menu. + + +MENU LABEL label + + (Only valid after a LABEL statement.) + Changes the label displayed for a specific entry. This allows + you to have a label that isn't suitable for the command line, + for example: + + # Soft Cap Linux + LABEL softcap + MENU LABEL Soft Cap ^Linux 9.6.36 + KERNEL softcap-9.6.36.bzi + APPEND whatever + + # A very dense operating system + LABEL brick + MENU LABEL ^Windows CE/ME/NT + KERNEL chain.c32 + APPEND hd0 2 + + The ^ symbol in a MENU LABEL statement defines a hotkey. + The hotkey will be highlighted in the menu and will move the + menu cursor immediately to that entry. + + Reusing hotkeys is disallowed, subsequent entries will not be + highlighted, and will not work. + + Keep in mind that the LABELs, not MENU LABELs, must be unique, + or odd things will happen to the command-line. + + +MENU INDENT count + + (Only valid after a LABEL statement.) + Will add "count" spaces in front of the displayed menu entry. + + +MENU DISABLE + + (Only valid after a LABEL statement.) + Makes the entry unselectable. This allows you to make a + section in your menu with different options below it. + for example: + + # Entries for network boots + LABEL - + MENU LABEL Network: + MENU DISABLE + + # Soft Cap Linux + LABEL softcap + MENU LABEL Soft Cap ^Linux 9.6.36 + MENU INDENT 1 + KERNEL softcap-9.6.36.bzi + APPEND whatever + + # Dos 6.22 + LABEL dos + MENU LABEL ^Dos 6.22 + MENU INDENT 1 + KERNEL memdisk + APPEND initrd=dos622.imz + + # Separator + MENU SEPARATOR + + # Entries for local boots + LABEL - + MENU LABEL Local: + MENU DISABLE + + # Windows 2000 + LABEL w2k + MENU LABEL ^Windows 2000 + MENU INDENT 1 + KERNEL chain.c32 + APPEND hd0 1 + + # Windows XP + LABEL xp + MENU LABEL Windows ^XP + MENU INDENT 1 + KERNEL chain.c32 + APPEND hd0 2 + +MENU HIDE + + (Only valid after a LABEL statement.) + Suppresses a particular LABEL entry from the menu. + + +MENU DEFAULT + + (Only valid after a LABEL statement.) + + Indicates that this entry should be the default for this + particular submenu. See also the DEFAULT directive below. + + +TEXT HELP +Help text ... +... which can span multiple lines +ENDTEXT + + (Only valid after a LABEL statement.) + + Specifies a help text that should be displayed when a particular + selection is highlighted. + + +MENU PASSWD passwd + + (Only valid after a LABEL statement.) + + Sets a password on this menu entry. "passwd" can be either a + cleartext password or a password encrypted with one of the + following algorithms: + + MD5 (Signature: $1$) + SHA-1 (Signature: $4$) + SHA-2-256 (Signature: $5$) + SHA-2-512 (Signature: $6$) + + Use the included Perl scripts "sha1pass" or "md5pass" to + encrypt passwords. MD5 passwords are compatible with most + Unix password file utilities; SHA-1 passwords are probably + unique to Syslinux; SHA-2 passwords are compatible with very + recent Linux distributions. Obviously, if you don't encrypt + your passwords they will not be very secure at all. + + If you are using passwords, you want to make sure you also use + the settings "NOESCAPE 1", "PROMPT 0", and either set + "ALLOWOPTIONS 0" or use a master password (see below.) + + If passwd is an empty string, this menu entry can only be + unlocked with the master password. + + +MENU MASTER PASSWD passwd + + Sets a master password. This password can be used to boot any + menu entry, and is required for the [Tab] and [Esc] keys to + work. + + +MENU BACKGROUND background + + For vesamenu.c32, sets the background image. The background + can either be a color (see MENU COLOR) or the name of an image + file, which should be 640x480 pixels and either in PNG or JPEG + format. + + +MENU BEGIN [tagname] +MENU END + + Begin/end a submenu. The entries between MENU BEGIN and MENU + END form a submenu, which is marked with a > mark on the right + hand of the screen. Submenus inherit the properties of their + parent menus, but can override them, and can thus have their + own backgrounds, master passwords, titles, timeouts, messages + and so forth. + + +MENU GOTO tagname + + (Only valid after a LABEL statement.) + + This label will transfer to the named submenu instead of + booting anything. To transfer to the top-level menu, specify + "menu goto .top". + + +MENU EXIT [tagname] + + (Only valid after a label statement inside MENU BEGIN ... + MENU END) + + Exit to the next higher menu, or, if tagname is specified, to + the named menu. + + +MENU QUIT + + (Only valid after a LABEL statement.) + + This label quits the menu system. + + WARNING: if MENU MASTER PASSWD or ALLOWOPTIONS 0 is set, this + will still allow exiting to the CLI; however, a separate MENU + PASSWD can of course be set for this label. + + +MENU START + + (Only valid inside MENU BEGIN ... MENU END) + + Indicates that the menu system should start at the menu being + defined instead of at the top-level menu. See also the + DEFAULT directive below. + + +DEFAULT label + + Set the global default. If "label" points into a submenu, + that menu becomes the start menu; in other words, this + directive has the same effect as both MENU DEFAULT and MENU + START. + + For backwards compatibility with earlier versions of Syslinux, + this directive is ignored unless the configuration file also + contains a UI directive. + + Note: the CLI accepts options after the label, or even a + non-label. The menu system does not support that. + + +MENU SAVE +MENU NOSAVE + + Remember the last entry selected and make that the default for + the next boot. A password-protected menu entry is *not* + saved. This requires the ADV data storage mechanism, which is + currently only implemented for EXTLINUX, although the other + Syslinux derivatives will accept the command (and ignore it.) + + NOTE: MENU SAVE stores the LABEL tag of the selected entry; + this mechanism therefore relies on LABEL tags being unique. + On the other hand, it handles changes in the configuration + file gracefully. + + The MENU SAVE information can be cleared with + "extlinux --reset-adv ". + + A MENU SAVE or MENU NOSAVE at the top of a (sub)menu affects + all entries underneath that (sub)menu except those that in + turn have MENU SAVE or MENU NOSAVE declared. This can be used + to only save certain entires when selected. + + +INCLUDE filename [tagname] +MENU INCLUDE filename [tagname] + + Include the contents of the configuration file filename at + this point. + + In the case of MENU INCLUDE, the included data is only seen by + the menu system; the core syslinux code does not parse this + command, so any labels defined in it are unavailable. + + If a tagname is included, the whole file is considered to have + been bracketed with a MENU BEGIN tagname ... MENU END pair, + and will therefore show up as a submenu. + + +MENU AUTOBOOT message + + Replaces the message "Automatic boot in # second{,s}...". The + symbol # is replaced with the number of seconds remaining. + The syntax "{singular,[dual,]plural}" can be used to conjugate + appropriately. + + +MENU TABMSG message + + Replaces the message "Press [Tab] to edit options". + + +MENU NOTABMSG message + + Takes the place of the TABMSG message if option editing is + disabled. Defaults to blank. + + +MENU PASSPROMPT message + + Replaces the message "Password required". + + +MENU COLOR element ansi foreground background shadow + + Sets the color of element "element" to the specified color + sequence: + + screen Rest of the screen + border Border area + title Title bar + unsel Unselected menu item + hotkey Unselected hotkey + sel Selection bar + hotsel Selected hotkey + disabled Disabled menu item + scrollbar Scroll bar + tabmsg Press [Tab] message + cmdmark Command line marker + cmdline Command line + pwdborder Password box border + pwdheader Password box header + pwdentry Password box contents + timeout_msg Timeout message + timeout Timeout counter + help Help text + msgXX Message (F-key) file attribute XX + + ... where XX is two hexadecimal digits (the "plain text" is 07). + + "ansi" is a sequence of semicolon-separated ECMA-48 Set + Graphics Rendition ([m) sequences: + + 0 reset all attributes to their defaults + 1 set bold + 4 set underscore (simulated with color on a color display) + 5 set blink + 7 set reverse video + 22 set normal intensity + 24 underline off + 25 blink off + 27 reverse video off + 30 set black foreground + 31 set red foreground + 32 set green foreground + 33 set brown foreground + 34 set blue foreground + 35 set magenta foreground + 36 set cyan foreground + 37 set white foreground + 38 set underscore on, set default foreground color + 39 set underscore off, set default foreground color + 40 set black background + 41 set red background + 42 set green background + 43 set brown background + 44 set blue background + 45 set magenta background + 46 set cyan background + 47 set white background + 49 set default background color + + These are used (a) in text mode, and (b) on the serial + console. + + "foreground" and "background" are color codes in #AARRGGBB + notation, where AA RR GG BB are hexadecimal digits for alpha + (opacity), red, green and blue, respectively. #00000000 + represents fully transparent, and #ffffffff represents opaque + white. + + "shadow" controls the handling of the graphical console text + shadow. Permitted values are "none" (no shadowing), "std" or + "standard" (standard shadowing - foreground pixels are + raised), "all" (both background and foreground raised), and + "rev" or "reverse" (background pixels are raised.) + + If any field is set to "*" or omitted (at the end of the line) + then that field is left unchanged. + + + The current defaults are: + + menu color screen 37;40 #80ffffff #00000000 std + menu color border 30;44 #40000000 #00000000 std + menu color title 1;36;44 #c00090f0 #00000000 std + menu color unsel 37;44 #90ffffff #00000000 std + menu color hotkey 1;37;44 #ffffffff #00000000 std + menu color sel 7;37;40 #e0000000 #20ff8000 all + menu color hotsel 1;7;37;40 #e0400000 #20ff8000 all + menu color disabled 1;30;44 #60cccccc #00000000 std + menu color scrollbar 30;44 #40000000 #00000000 std + menu color tabmsg 31;40 #90ffff00 #00000000 std + menu color cmdmark 1;36;40 #c000ffff #00000000 std + menu color cmdline 37;40 #c0ffffff #00000000 std + menu color pwdborder 30;47 #80ffffff #20ffffff std + menu color pwdheader 31;47 #80ff8080 #20ffffff std + menu color pwdentry 30;47 #80ffffff #20ffffff std + menu color timeout_msg 37;40 #80ffffff #00000000 std + menu color timeout 1;37;40 #c0ffffff #00000000 std + menu color help 37;40 #c0ffffff #00000000 std + menu color msg07 37;40 #90ffffff #00000000 std + + +MENU MSGCOLOR fg_filter bg_filter shadow + + Sets *all* the msgXX colors to a color scheme derived from the + fg_filter and bg_filter values. Background color zero is + always treated as transparent. The default corresponds to: + + menu msgcolor #90ffffff #80ffffff std + + This directive should come before any directive that + customizes individual msgXX colors. + + +MENU WIDTH 80 +MENU MARGIN 10 +MENU PASSWORDMARGIN 3 +MENU ROWS 12 +MENU TABMSGROW 18 +MENU CMDLINEROW 18 +MENU ENDROW -1 +MENU PASSWORDROW 11 +MENU TIMEOUTROW 20 +MENU HELPMSGROW 22 +MENU HELPMSGENDROW -1 +MENU HIDDENROW -2 +MENU HSHIFT 0 +MENU VSHIFT 0 + + These options control the layout of the menu on the screen. + The values above are the defaults. + + A negative value is relative to the calculated length of the + screen (25 for text mode, 28 for VESA graphics mode.) + + +F1 textfile background +... +F12 textfile background + + Displays full-screen help (also available at the command line.) + The same control code sequences as in the command line + interface are supported, although some are ignored. + + Additionally, a second argument allows a different background + image (see MENU BACKGROUND for supported formats) to be displayed. + + +The menu system honours the TIMEOUT command; if TIMEOUT is specified +it will execute the ONTIMEOUT command if one exists, otherwise it will +pick the default menu option. WARNING: the timeout action will bypass +password protection even if one is set for the specified or default +entry! + +Normally, the user can press [Tab] to edit the menu entry, and [Esc] +to return to the Syslinux command line. However, if the configuration +file specifies ALLOWOPTIONS 0, these keys will be disabled, and if +MENU MASTER PASSWD is set, they require the master password. + +The simple menu system supports serial console, using the normal +SERIAL directive. However, it can be quite slow over a slow serial +link; you probably want to set your baudrate to 38400 or higher if +possible. It requires a Linux/VT220/ANSI-compatible terminal on the +other end. + + + +++ USING AN ALTERNATE CONFIGURATION FILE +++ + + +It is also possible to load a secondary configuration file, to get to +another menu. To do that, invoke menu.c32 with the name of the +secondary configuration file. + +LABEL othermenu + MENU LABEL Another Menu + KERNEL menu.c32 + APPEND othermenu.conf + +If you specify more than one file, they will all be read, in the order +specified. The dummy filename ~ (tilde) is replaced with the filename +of the main configuration file. + +# The file graphics.conf contains common color and layout commands for +# all menus. +LABEL othermenu + MENU LABEL Another Menu + KERNEL vesamenu.c32 + APPEND graphics.conf othermenu.conf + +# Return to the main menu +LABEL mainmenu + MENU LABEL Return to Main Menu + KERNEL vesamenu.c32 + APPEND graphics.conf ~ + +See also the MENU INCLUDE directive above. diff --git a/doc/pxelinux.txt b/doc/pxelinux.txt new file mode 100644 index 0000000..47aea90 --- /dev/null +++ b/doc/pxelinux.txt @@ -0,0 +1,418 @@ + PXELINUX + + A bootloader for Linux using the PXE network booting protocol + + Copyright 1994-2008 H. Peter Anvin - All Rights Reserved + +This program is provided under the terms of the GNU General Public +License, version 2 or, at your option, any later version. There is no +warranty, neither expressed nor implied, to the function of this +program. Please see the included file COPYING for details. + +---------------------------------------------------------------------- + +PXELINUX is a Syslinux derivative, for booting Linux off a network +server, using a network ROM conforming to the Intel PXE (Pre-Execution +Environment) specification. PXELINUX is *not* a program that is +intended to be flashed or burned into a PROM on the network card; if +you want that, check out Etherboot (http://www.etherboot.org/). +Etherboot 5.4 or later can also be used to create a PXE-compliant boot +PROM for many network cards. + + + ++++ HOW TO CONFIGURE PXELINUX ++++ + +PXELINUX operates in many ways like SYSLINUX. If you are not familiar +with SYSLINUX, read syslinux.txt first, since this documentation only +explains the differences. + +On the TFTP server, create the directory "/tftpboot", and copy the +following files to it: + + pxelinux.0 - from the Syslinux distribution + + any kernel or initrd images you want to boot + +Finally, create the directory "/tftpboot/pxelinux.cfg". The +configuration file (equivalent of syslinux.cfg -- see syslinux.txt for +the options here) will live in this directory. Because more than one +system may be booted from the same server, the configuration file name +depends on the IP address of the booting machine. PXELINUX will +search for its config file on the boot server in the following way: + + First, it will search for the config file using the client UUID, if + one is provided by the PXE stack (note, some BIOSes don't have a + valid UUID, and you might end up with something like all 1's.) This is + in the standard UUID format using lower case hexadecimal digits, e.g. + b8945908-d6a6-41a9-611d-74a6ab80b83d. + + Next, it will search for the config file using the hardware type + (using its ARP type code) and address, all in lower case hexadecimal + with dash separators; for example, for an Ethernet (ARP type 1) + with address 88:99:AA:BB:CC:DD it would search for the filename + 01-88-99-aa-bb-cc-dd. + + Next, it will search for the config file using its own IP address + in upper case hexadecimal, e.g. 192.0.2.91 -> C000025B + (you can use the included progam "gethostip" to compute the + hexadecimal IP address for any host.) + + If that file is not found, it will remove one hex digit and try + again. Ultimately, it will try looking for a file named "default" + (in lower case). + + As an example, if the boot file name is /mybootdir/pxelinux.0, the + UUID is b8945908-d6a6-41a9-611d-74a6ab80b83d, the Ethernet MAC + address is 88:99:AA:BB:CC:DD and the IP address 192.0.2.91, it will + try: + + /mybootdir/pxelinux.cfg/b8945908-d6a6-41a9-611d-74a6ab80b83d + /mybootdir/pxelinux.cfg/01-88-99-aa-bb-cc-dd + /mybootdir/pxelinux.cfg/C000025B + /mybootdir/pxelinux.cfg/C000025 + /mybootdir/pxelinux.cfg/C00002 + /mybootdir/pxelinux.cfg/C0000 + /mybootdir/pxelinux.cfg/C000 + /mybootdir/pxelinux.cfg/C00 + /mybootdir/pxelinux.cfg/C0 + /mybootdir/pxelinux.cfg/C + /mybootdir/pxelinux.cfg/default + + ... in that order. + +Note that all filename references are relative to the directory +pxelinux.0 lives in. PXELINUX generally requires that filenames +(including any relative path) are 127 characters or shorter in length. + +Starting in release 3.20, PXELINUX will no longer apply a built-in +default if it cannot find any configuration file at all; instead it +will reboot after the timeout interval has expired. This keeps a +machine from getting stuck indefinitely due to a boot server failure. + +Starting in release 3.50, PXELINUX displays network information at +the boot prompt pressing . + +PXELINUX does not support MTFTP, and I have no plans of doing so, as +MTFTP is inherently broken for files more than 65535 packets (about +92 MB) in size. It is of course possible to use MTFTP for the initial +boot, if you have such a setup. MTFTP server setup is beyond the +scope of this document. + + + ++++ SETTING UP THE TFTP SERVER ++++ + +PXELINUX currently requires that the boot server has a TFTP server +which supports the "tsize" TFTP option (RFC 1784/RFC 2349). The +"tftp-hpa" TFTP server, which support options, is available at: + + http://www.kernel.org/pub/software/network/tftp/ + ftp://www.kernel.org/pub/software/network/tftp/ + +... and on any kernel.org mirror (see http://www.kernel.org/mirrors/). + +Another TFTP server which supports this is atftp by Jean-Pierre +Lefebvre: + + ftp://ftp.mamalinux.com/pub/atftp/ + +If your boot server is running Windows (and you can't fix that), try +tftpd32 by Philippe Jounin (you need version 2.11 or later; previous +versions had a bug which made it incompatible with PXELINUX): + + http://tftpd32.jounin.net/ + + + ++++ SETTING UP THE DHCP SERVER ++++ + +The PXE protocol uses a very complex set of extensions to DHCP or +BOOTP. However, most PXE implementations -- this includes all Intel +ones version 0.99n and later -- seem to be able to boot in a +"conventional" DHCP/TFTP configuration. Assuming you don't have to +support any very old or otherwise severely broken clients, this is +probably the best configuration unless you already have a PXE boot +server on your network. + +A sample DHCP setup, using the "conventional TFTP" configuration, +would look something like the following, using ISC dhcp 2.0 dhcpd.conf +syntax: + + allow booting; + allow bootp; + + # Standard configuration directives... + + option domain-name ""; + option subnet-mask ; + option broadcast-address ; + option domain-name-servers ; + option routers ; + + # Group the PXE bootable hosts together + group { + # PXE-specific configuration directives... + next-server ; + filename "/tftpboot/pxelinux.0"; + + # You need an entry like this for every host + # unless you're using dynamic addresses + host { + hardware ethernet ; + fixed-address ; + } + } + +Note that if your particular TFTP daemon runs under chroot (tftp-hpa +will do this if you specify the -s (secure) option; this is highly +recommended), you almost certainly should not include the /tftpboot +prefix in the filename statement. + +If this does not work for your configuration, you probably should set +up a "PXE boot server" on port 4011 of your TFTP server; a free PXE +boot server is available at: + + http://www.kano.org.uk/projects/pxe/ + +With such a boot server defined, your DHCP configuration should look +the same except for an "option dhcp-class-identifier" ("option +vendor-class-identifier" if you are using DHCP 3.0): + + allow booting; + allow bootp; + + # Standard configuration directives... + + option domain-name ""; + option subnet-mask ; + option broadcast-address ; + option domain-name-servers ; + option routers ; + + # Group the PXE bootable hosts together + group { + # PXE-specific configuration directives... + option dhcp-class-identifier "PXEClient"; + next-server ; + + # You need an entry like this for every host + # unless you're using dynamic addresses + host { + hardware ethernet ; + fixed-address ; + } + } + +Here, the boot file name is obtained from the PXE server. + +If the "conventional TFTP" configuration doesn't work on your clients, +and setting up a PXE boot server is not an option, you can attempt the +following configuration. It has been known to boot some +configurations correctly; however, there are no guarantees: + + allow booting; + allow bootp; + + # Standard configuration directives... + + option domain-name ""; + option subnet-mask ; + option broadcast-address ; + option domain-name-servers ; + option routers ; + + # Group the PXE bootable hosts together + group { + # PXE-specific configuration directives... + option dhcp-class-identifier "PXEClient"; + option vendor-encapsulated-options 09:0f:80:00:0c:4e:65:74:77:6f:72:6b:20:62:6f:6f:74:0a:07:00:50:72:6f:6d:70:74:06:01:02:08:03:80:00:00:47:04:80:00:00:00:ff; + next-server ; + filename "/tftpboot/pxelinux.0"; + + # You need an entry like this for every host + # unless you're using dynamic addresses + host { + hardware ethernet ; + fixed-address ; + } + } + +Note that this *will not* boot some clients that *will* boot with the +"conventional TFTP" configuration; Intel Boot Client 3.0 and later are +known to fall into this category. + + + ++++ SPECIAL DHCP OPTIONS ++++ + +PXELINUX (starting with version 1.62) supports the following +nonstandard DHCP options, which depending on your DHCP server you may +be able to use to customize the specific behaviour of PXELINUX. See +RFC 5071 for some additional information about these options. + +Option 208 pxelinux.magic + - Earlier versions of PXELINUX required this to be set to + F1:00:74:7E (241.0.116.126) for PXELINUX to + recognize any special DHCP options whatsoever. As of + PXELINUX 3.55, this option is deprecated and is no longer + required. + +Option 209 pxelinux.configfile + - Specifies the PXELINUX configuration file name. + +Option 210 pxelinux.pathprefix + - Specifies the PXELINUX common path prefix, instead of + deriving it from the boot file name. This almost certainly + needs to end in whatever character the TFTP server OS uses + as a pathname separator, e.g. slash (/) for Unix. + +Option 211 pxelinux.reboottime + - Specifies, in seconds, the time to wait before reboot in the + event of TFTP failure. 0 means wait "forever" (in reality, + it waits approximately 136 years.) + +ISC dhcp 3.0 supports a rather nice syntax for specifying custom +options; you can use the following syntax in dhcpd.conf if you are +running this version of dhcpd: + + option space pxelinux; + option pxelinux.magic code 208 = string; + option pxelinux.configfile code 209 = text; + option pxelinux.pathprefix code 210 = text; + option pxelinux.reboottime code 211 = unsigned integer 32; + + NOTE: In earlier versions of PXELINUX, this would only work as a + "site-option-space". Since PXELINUX 2.07, this will work both as a + "site-option-space" (unencapsulated) and as a "vendor-option-space" + (type 43 encapsulated.) This may avoid messing with the + dhcp-parameter-request-list, as detailed below. + +Then, inside your PXELINUX-booting group or class (whereever you have +the PXELINUX-related options, such as the filename option), you can +add, for example: + + # Always include the following lines for all PXELINUX clients + site-option-space "pxelinux"; + option pxelinux.magic f1:00:74:7e; + if exists dhcp-parameter-request-list { + # Always send the PXELINUX options (specified in hexadecimal) + option dhcp-parameter-request-list = concat(option dhcp-parameter-request-list,d0,d1,d2,d3); + } + # These lines should be customized to your setup + option pxelinux.configfile "configs/common"; + option pxelinux.pathprefix "/tftpboot/pxelinux/files/"; + option pxelinux.reboottime 30; + filename "/tftpboot/pxelinux/pxelinux.bin"; + +Note that the configfile is relative to the pathprefix: this will look +for a config file called /tftpboot/pxelinux/files/configs/common on +the TFTP server. + +The "option dhcp-parameter-request-list" statement forces the DHCP +server to send the PXELINUX-specific options, even though they are not +explicitly requested. Since the DHCP request is done before PXELINUX +is loaded, the PXE client won't know to request them. + +Using ISC dhcp 3.0 you can create a lot of these strings on the fly. +For example, to use the hexadecimal form of the hardware address as +the configuration file name, you could do something like: + + site-option-space "pxelinux"; + option pxelinux.magic f1:00:74:7e; + if exists dhcp-parameter-request-list { + # Always send the PXELINUX options (specified in hexadecimal) + option dhcp-parameter-request-list = concat(option dhcp-parameter-request-list,d0,d1,d2,d3); + } + option pxelinux.configfile = + concat("pxelinux.cfg/", binary-to-ascii(16, 8, ":", hardware)); + filename "/tftpboot/pxelinux.bin"; + +If you used this from a client whose Ethernet address was +58:FA:84:CF:55:0E, this would look for a configuration file named +"/tftpboot/pxelinux.cfg/1:58:fa:84:cf:55:e". + + + ++++ ALTERNATE TFTP SERVERS ++++ + +PXELINUX supports the following special pathname conventions: + +::filename + + Suppresses the common filename prefix, i.e. passes the string + "filename" unmodified to the server. + +IP address::filename (e.g. 192.0.2.1::filename) + + Suppresses the common filename prefix, *and* sends a request + to an alternate TFTP server. Instead of an IP address, a + DNS name can be used. It will be assumed to be fully + qualified if it contains dots; otherwise the local domain as + reported by the DHCP server (option 15) will be added. + +:: was chosen because it is unlikely to conflict with operating system +usage. However, if you happen to have an environment for which the +special treatment of :: is a problem, please contact the Syslinux +mailing list. + + + ++++ SOME NOTES ++++ + +If the boot fails, PXELINUX (unlike SYSLINUX) will not wait forever; +rather, if it has not received any input for approximately five +minutes after displaying an error message, it will reset the machine. +This allows an unattended machine to recover in case it had bad enough +luck of trying to boot at the same time the TFTP server goes down. + +Lots of PXE stacks, especially old ones, have various problems of +varying degrees of severity. Please see: + + http://syslinux.zytor.com/hardware.php + +... for a list of currently known hardware problems, with workarounds +if known. + + + ++++ KEEPING THE PXE STACK AROUND ++++ + +Normally, PXELINUX will unload the PXE and UNDI stacks before invoking +the kernel. In special circumstances (for example, when using MEMDISK +to boot an operating system with an UNDI network driver) it might be +desirable to keep the PXE stack in memory. If the option "keeppxe" +is given on the kernel command line, PXELINUX will keep the PXE and +UNDI stacks in memory. (If you don't know what this means, you +probably don't need it.) + + + ++++ PROBLEMS WITH YOUR PXE STACK ++++ + +There are a number of extremely broken PXE stacks in the field. The +gPXE project (formerly known as Etherboot) provides an open-source PXE +stack that works with a number of cards, and which can be loaded from +a CD-ROM, USB key, or floppy if desired. + +Information on gPXE is available from: + + http://www.etherboot.org/ + +... and ready-to-use ROM or disk images from: + + http://www.rom-o-matic.net/ + +Some cards, like may systems with the SiS 900, has a PXE stack which +works just barely well enough to load a single file, but doesn't +handle the more advanced items required by PXELINUX. If so, it is +possible to use the built-in PXE stack to load gPXE, which can then +load PXELINUX. See: + + http://www.etherboot.org/wiki/pxechaining + + + ++++ CURRENTLY KNOWN PROBLEMS ++++ + +The following problems are known with PXELINUX, so far: + ++ The error recovery routine doesn't work quite right. For right now, + it just does a hard reset - seems good enough. ++ We should probably call the UDP receive function in the keyboard + entry loop, so that we answer ARP requests. ++ Boot sectors/disk images are not supported yet. + +If you have additional problems, please contact the Syslinux mailing +list (see syslinux.txt for the address.) diff --git a/doc/rfc5071.txt b/doc/rfc5071.txt new file mode 100644 index 0000000..68f6f5a --- /dev/null +++ b/doc/rfc5071.txt @@ -0,0 +1,787 @@ + + + + + + +Network Working Group D. Hankins +Request for Comments: 5071 ISC +Category: Informational December 2007 + + + Dynamic Host Configuration Protocol Options Used by PXELINUX + +Status of This Memo + + This memo provides information for the Internet community. It does + not specify an Internet standard of any kind. Distribution of this + memo is unlimited. + +Abstract + + This document describes the use by PXELINUX of some DHCP Option Codes + numbering from 208-211. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Hankins Informational [Page 1] + +RFC 5071 PXELINUX Options December 2007 + + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3 + 2. Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 4 + 3. MAGIC Option . . . . . . . . . . . . . . . . . . . . . . . . . 4 + 3.1. Description . . . . . . . . . . . . . . . . . . . . . . . 4 + 3.2. Packet Format . . . . . . . . . . . . . . . . . . . . . . 5 + 3.3. Applicability . . . . . . . . . . . . . . . . . . . . . . 5 + 3.4. Response to RFC 3942 . . . . . . . . . . . . . . . . . . . 5 + 4. Configuration File Option . . . . . . . . . . . . . . . . . . 5 + 4.1. Description . . . . . . . . . . . . . . . . . . . . . . . 5 + 4.2. Packet Format . . . . . . . . . . . . . . . . . . . . . . 6 + 4.3. Applicability . . . . . . . . . . . . . . . . . . . . . . 6 + 4.4. Response to RFC 3942 . . . . . . . . . . . . . . . . . . . 6 + 4.5. Client and Server Behaviour . . . . . . . . . . . . . . . 6 + 5. Path Prefix Option . . . . . . . . . . . . . . . . . . . . . . 7 + 5.1. Description . . . . . . . . . . . . . . . . . . . . . . . 7 + 5.2. Packet Format . . . . . . . . . . . . . . . . . . . . . . 7 + 5.3. Applicability . . . . . . . . . . . . . . . . . . . . . . 7 + 5.4. Response to RFC 3942 . . . . . . . . . . . . . . . . . . . 8 + 5.5. Client and Server Behaviour . . . . . . . . . . . . . . . 8 + 6. Reboot Time Option . . . . . . . . . . . . . . . . . . . . . . 9 + 6.1. Description . . . . . . . . . . . . . . . . . . . . . . . 9 + 6.2. Packet Format . . . . . . . . . . . . . . . . . . . . . . 9 + 6.3. Applicability . . . . . . . . . . . . . . . . . . . . . . 10 + 6.4. Response to RFC 3942 . . . . . . . . . . . . . . . . . . . 10 + 6.5. Client and Server Behaviour . . . . . . . . . . . . . . . 10 + 7. Specification Conformance . . . . . . . . . . . . . . . . . . 11 + 8. Security Considerations . . . . . . . . . . . . . . . . . . . 11 + 9. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 11 + 10. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 12 + 11. References . . . . . . . . . . . . . . . . . . . . . . . . . . 12 + 11.1. Normative References . . . . . . . . . . . . . . . . . . . 12 + 11.2. Informative References . . . . . . . . . . . . . . . . . . 12 + + + + + + + + + + + + + + + + + +Hankins Informational [Page 2] + +RFC 5071 PXELINUX Options December 2007 + + +1. Introduction + + PXE, the Preboot eXecution Environment, is a first-stage network + bootstrap agent. PXE is loaded out of firmware on the client host, + and performs DHCP [3] queries to obtain an IP address. + + Once on the network, it loads a second-stage bootstrap agent as + configured by DHCP header and option contents. + + PXELINUX is one such second-stage bootstrap agent. Once PXE has + passed execution to it, PXELINUX seeks its configuration from a cache + of DHCP options supplied to the PXE first-stage agent, and then takes + action based upon those options. + + Most frequently, this implies loading via Trivial File Transfer + Protocol (TFTP) [6] one or more images that are decompressed into + memory, then executed to pass execution to the final Host Operating + System. + + PXELINUX uses DHCP options 208-211 to govern parts of this bootstrap + process, but these options are not requested by the PXE DHCP client + at the time it acquires its lease. At that time, the PXE bootloader + has no knowledge that PXELINUX is going to be in use, and even so, + would have no way to know what option(s) PXELINUX might digest. + Local installations that serve this PXELINUX image to its clients + must also configure their DHCP servers to provide these options even + though they are not on the DHCP Parameter Request List [4]. + + These options are: + + o "MAGIC" - 208 - An option whose presence and content verifies to + the PXELINUX bootloader that the options numbered 209-211 are for + the purpose as described herein. + + o "ConfigFile" - 209 - Configures the path/filename component of the + configuration file's location, which this bootloader should use to + configure itself. + + o "PathPrefix" - 210 - Configures a value to be prepended to the + ConfigFile to discern the directory location of the file. + + o "RebootTime" - 211 - Configures a timeout after which the + bootstrap program will reboot the system (most likely returning it + to PXE). + + Historically, these option codes numbering from 208-211 were + designated 'Site Local', but after publication of RFC3942 [8], they + were made available for allocation as new standard DHCP options. + + + +Hankins Informational [Page 3] + +RFC 5071 PXELINUX Options December 2007 + + + This document marks these codes as assigned. + + This direct assignment of option code values in the option + definitions below is unusual as it is not mentioned in DHCP Option + Code assignment guidelines [5]. This document's Option Code + assignments are done within RFC 3942's provisions for documenting + prior use of option codes within the new range (128-223 inclusive). + +2. Terminology + + o "first-stage bootloader" - Although a given bootloading order may + have many stages, such as where a BIOS boots a DOS Boot Disk, + which then loads a PXE executable, it is, in this example, only + the PXE executable that this document describes as the "first- + stage bootloader" -- in essence, this is the first stage of + booting at which DHCP is involved. + + o "second-stage bootloader" - This describes a program loaded by the + first-stage bootloader at the behest of the DHCP server. + + o "bootloader" and "network bootstrap agent" - These are synonyms, + excepting that "bootloader" is intentionally vague in that its + next form of bootstrapping may not in fact involve network + resources. + + The key words "MAY", "MUST", "MUST NOT", "SHOULD", and "SHOULD NOT" + in this document are to be interpreted as described in RFC 2119 [2]. + +3. MAGIC Option + +3.1. Description + + If this option is provided to the PXE bootloader, then the value is + checked by PXELINUX to match the octet string f1:00:74:7e. If this + matches, then PXELINUX bootloaders will also consume options 209-211, + as described below. Otherwise, they are ignored. + + This measure was intended to ensure that, as the 'Site Local' option + space is not allocated from a central authority, no conflict would + result in a PXELINUX bootloader improperly digesting options intended + for another purpose. + + + + + + + + + + +Hankins Informational [Page 4] + +RFC 5071 PXELINUX Options December 2007 + + +3.2. Packet Format + + The MAGIC Option format is as follows: + + Code Length m1 m2 m3 m4 + +--------+--------+--------+--------+--------+--------+ + | 208 | 4 | 0xF1 | 0x00 | 0x74 | 0x7E | + +--------+--------+--------+--------+--------+--------+ + + The code for this option is 208. The length is always four. + +3.3. Applicability + + This option is absolutely inapplicable to any other purpose. + +3.4. Response to RFC 3942 + + The option code 208 will be adopted for this purpose and immediately + deprecated. Future standards action may return this option to an + available status should it be necessary. + + A collision of the use of this option is harmless (at least from + PXELINUX' point of view) by design: if it does not match the + aforementioned magic value, the PXELINUX bootloader will take no + special action. + + The PXELINUX project will deprecate the use of this option; future + versions of the software will not evaluate its contents. + + It is reasonable to utilize this option code for another purpose, but + it is recommended to do this at a later time, given the desire to + avoid potential collisions in legacy user bases. + +4. Configuration File Option + +4.1. Description + + Once the PXELINUX executable has been entered from the PXE + bootloader, it evaluates this option and loads a file of that name + via TFTP. The contents of this file serve to configure PXELINUX in + its next stage of bootloading (specifying boot image names, + locations, boot-time flags, text to present the user in menu + selections, etc). + + In the absence of this option, the PXELINUX agent will search the + TFTP server (as determined by PXE prior to this stage) for a config + file of several default names. + + + + +Hankins Informational [Page 5] + +RFC 5071 PXELINUX Options December 2007 + + +4.2. Packet Format + + The Configuration File Option format is as follows: + + Code Length Config-file... + +--------+--------+--------+--------+--------+--------+ + | 209 | n | c1 | c2 | ... | c(n) | + +--------+--------+--------+--------+--------+--------+ + + The code for this option is 209. The Config-file (c1..c(n)) is an + NVT-ASCII [1] printable string; it is not terminated by a zero or any + other value. + +4.3. Applicability + + Any bootloader, PXE or otherwise, that makes use of a separate + configuration file rather than containing all configurations within + DHCP options (which may be impossible due to the limited space + available for DHCP options) may conceivably make use of this option. + +4.4. Response to RFC 3942 + + The code 209 will be adopted for this purpose. + +4.5. Client and Server Behaviour + + The Config File Option MUST be supplied by the DHCP server if it + appears on the Parameter Request List, but MUST also be supplied if + the server administrator believed it would later be useful to the + client (such as because the server is configured to offer a second- + stage boot image, which they know will make use of it). The option + MUST NOT be supplied if no value has been configured for it, or if a + value of zero length has been configured. + + The DHCP client MUST only cache this option in a location the second- + stage bootloader may access. + + The second-stage bootloader MUST, in concert with other DHCP options + and fields, use this option's value as a filename to be loaded via + TFTP and read for further second-stage-loader-specific configuration + parameters. The format and content of such a file is specific to the + second-stage bootloader, and as such, is out of scope of this + document. + + + + + + + + +Hankins Informational [Page 6] + +RFC 5071 PXELINUX Options December 2007 + + +5. Path Prefix Option + +5.1. Description + + In PXELINUX' case, it is often the case that several different + environments would have the same TFTP path prefix, but would have + different filenames (for example: hosts' bootloader images and config + files may be kept in a directory structure derived from their Media + Access Control (MAC) address). Consequently, it was deemed + worthwhile to deliver a TFTP path prefix configuration option, so + that these two things could be configured separately in a DHCP Server + configuration: the prefix and the possibly host-specific file + location. + + The actual filename that PXELINUX requests from its TFTP server is + derived by prepending this value to the Config File Option above. + Once this config file is loaded and during processing, any TFTP file + paths specified within it are similarly processed -- prepending the + contents of this option. + +5.2. Packet Format + + The Path Prefix Option format is as follows: + + Code Length Path-Prefix... + +--------+--------+--------+--------+--------+--------+ + | 210 | n | p1 | p2 | ... | p(n) | + +--------+--------+--------+--------+--------+--------+ + + The code for this option is 210. The Path Prefix is an NVT-ASCII + printable string; it is not terminated by zero or any other value. + +5.3. Applicability + + This option came into existence because server administrators found + it useful to configure the prefix and suffix of the config file path + separately. A group of different PXE booting clients may use the + same path prefix, but different filenames, or vice versa. + + The 'shortcut' this represents is worthwhile, but it is questionable + whether that needs to manifest itself on the protocol wire. + + + + + + + + + + +Hankins Informational [Page 7] + +RFC 5071 PXELINUX Options December 2007 + + + It only becomes interesting from a protocol standpoint if other + options are adopted that prefix this value as well -- performing a + kind of string compression is highly beneficial to the limited + available DHCP option space. + + But it's clearly inapplicable to any current use of, e.g., the + FILENAME header contents or the DHCP Boot File Name option (#67). + Use of these fields is encoded on firmware of thousands of devices + that can't or are not likely to be upgraded. Altering any behaviour + here is likely to cause severe compatibility problems. + + Although compression of the TFTP-loaded configuration file contents + is not a compelling factor, contrived configurations using these + values may also exist: where each of a large variety of different + clients load the same configuration file, with the same contents, but + due to a differently configured path prefix actually load different + images. Whether this sort of use is truly needed remains unproven. + +5.4. Response to RFC 3942 + + The code 210 will be adopted for this purpose. + +5.5. Client and Server Behaviour + + The Path Prefix option MUST be supplied by the DHCP server if it + appears on the Parameter Request List, but MUST also be supplied if + the server administrator believed it would later be useful to the + client (such as because the server is configured to offer a second- + stage boot image that they know will make use of it). The option + MUST NOT be supplied if no value has been configured for it, or if a + value of zero length has been configured. + + The DHCP client MUST only cache this option in a location where the + second-stage bootloader may access it. + + The second-stage bootloader MUST prepend this option's value, if any, + to the contents of the ConfigFile option prior to obtaining the + resulting value via TFTP, or the default 'Config File Search Path', + which the second-stage bootloader iterates in the absence of a Config + File Option. The client MAY prepend the value to other configuration + directives within that file once it has been loaded. The client MUST + NOT prepend this option's value to any other DHCP option contents or + field, unless explicitly stated in a document describing that option + or field. + + + + + + + +Hankins Informational [Page 8] + +RFC 5071 PXELINUX Options December 2007 + + +6. Reboot Time Option + +6.1. Description + + Should PXELINUX be executed, and then for some reason, be unable to + reach its TFTP server to continue bootstrapping, the client will, by + default, reboot itself after 300 seconds have passed. This may be + too long, too short, or inappropriate behaviour entirely, depending + on the environment. + + By configuring a non-zero value in this option, admins can inform + PXELINUX of which specific timeout is desired. The client will + reboot itself if it fails to achieve its configured network resources + within the specified number of seconds. + + This reboot will run through the system's normal boot-time execution + path, most likely leading it back to PXE and therefore PXELINUX. So, + in the general case, this is akin to returning the client to the DHCP + INIT state. + + By configuring zero, the feature is disabled, and instead the client + chooses to remove itself from the network and wait indefinitely for + operator intervention. + + It should be stressed that this is in no way related to configuring a + lease time. The perceived transition to INIT state is due to client + running state -- reinitializing itself -- not due to lease timer + activity. That is, it is not safe to assume that a PXELINUX client + will abandon its lease when this timer expires. + +6.2. Packet Format + + The Reboot Time Option format is as follows: + + Code Length + +--------+--------+--------+--------+--------+--------+ + | 211 | 4 | Reboot Time | + +--------+--------+--------+--------+--------+--------+ + + The code for this option is 211. The length is always four. The + Reboot Time is a 32-bit (4 byte) integer in network byte order. + + + + + + + + + + +Hankins Informational [Page 9] + +RFC 5071 PXELINUX Options December 2007 + + +6.3. Applicability + + Any network bootstrap program in any sufficiently complex networking + environment could conceivably enter into such a similar condition, + either due to having its IP address stolen out from under it by a + rogue client on the network, by being moved between networks where + its PXE-derived DHCP lease is no longer valid, or any similar means. + + It seems desirable for any network bootstrap agent to implement an + ultimate timeout for it to start over. + + The client may, for example, get different working configuration + parameters from a different DHCP server upon restarting. + +6.4. Response to RFC 3942 + + The code 211 will be adopted for this purpose. + +6.5. Client and Server Behaviour + + The Reboot Time Option MUST be supplied by the DHCP server if it + appears on the Parameter Request List, but MUST also be supplied if + the server administrator believed it would later be useful to the + client (such as because the server is configured to offer a second- + stage boot image that they know will make use of it). The option + MUST NOT be supplied if no value has been configured for it, or if it + contains a value of zero length. + + The DHCP client MUST only cache this option in a location the second- + stage bootloader may access. + + If the value of this option is nonzero, the second-stage bootloader + MUST schedule a timeout: after a number of seconds equal to this + option's value have passed, the second-stage bootloader MUST reboot + the system, ultimately returning the path of execution back to the + first-stage bootloader. It MUST NOT reboot the system once the + thread of execution has been passed to the host operating system (at + which point, this timeout is effectively obviated). + + If the value of this option is zero, the second-stage bootloader MUST + NOT schedule such a timeout at all. Any second-stage bootloader that + finds it has encountered excessive timeouts attempting to obtain its + host operating system SHOULD disconnect itself from the network to + wait for operator intervention, but MAY continue to attempt to + acquire the host operating system indefinitely. + + + + + + +Hankins Informational [Page 10] + +RFC 5071 PXELINUX Options December 2007 + + +7. Specification Conformance + + To conform to this specification, clients and servers MUST implement + the Configuration File, Path Prefix, and Reboot Time options as + directed. + + The MAGIC option MAY NOT be implemented, as it has been deprecated. + +8. Security Considerations + + PXE and PXELINUX allow any entity acting as a DHCP server to execute + arbitrary code upon a system. At present, no PXE implementation is + known to implement authentication mechanisms [7] so that PXE clients + can be sure they are receiving configuration information from the + correct, authoritative DHCP server. + + The use of TFTP by PXE and PXELINUX also lacks any form of + cryptographic signature -- so a 'Man in the Middle' attack may lead + to an attacker's code being executed on the client system. Since + this is not an encrypted channel, any of the TFTP loaded data may + also be exposed (such as in loading a "RAMDISK" image, which contains + /etc/passwd or similar information). + + The use of the Ethernet MAC Address as the client's unique identity + may allow an attacker who takes on that identity to gain + inappropriate access to a client system's network resources by being + given by the DHCP server whatever 'keys' are required, in fact, to be + the target system (to boot up as though it were the target). + + Great care should be taken to secure PXE and PXELINUX installations, + such as by using IP firewalls, to reduce or eliminate these concerns. + + A nearby attacker might feed a "Reboot Time" option value of 1 second + to a mass of unsuspecting clients, to effect a Denial Of Service + (DoS) upon the DHCP server, but then again it may just as easily + supply these clients with rogue second-stage bootloaders that simply + transmit a flood of packets. + + This document in and by itself provides no security, nor does it + impact existing DCHP security as described in RFC 2131 [3]. + +9. IANA Considerations + + IANA has done the following: + + 1. Moved DHCPv4 Option code 208 from 'Tentatively Assigned' to + 'Assigned', referencing this document. IANA has marked this same + option code, 208, as Deprecated. + + + +Hankins Informational [Page 11] + +RFC 5071 PXELINUX Options December 2007 + + + 2. Moved DHCPv4 Option code 209 from 'Tentatively Assigned' to + 'Assigned', referencing this document. + + 3. Moved DHCPv4 Option code 210 from 'Tentatively Assigned' to + 'Assigned', referencing this document. + + 4. Moved DHCPv4 Option code 211 from 'Tentatively Assigned' to + 'Assigned', referencing this document. + +10. Acknowledgements + + These options were designed and implemented for the PXELINUX project + by H. Peter Anvin, and he was instrumental in producing this + document. Shane Kerr has also provided feedback that has improved + this document. + +11. References + +11.1. Normative References + + [1] Postel, J. and J. Reynolds, "Telnet Protocol Specification", + STD 8, RFC 854, May 1983. + + [2] Bradner, S., "Key words for use in RFCs to Indicate Requirement + Levels", BCP 14, RFC 2119, March 1997. + + [3] Droms, R., "Dynamic Host Configuration Protocol", RFC 2131, + March 1997. + + [4] Alexander, S. and R. Droms, "DHCP Options and BOOTP Vendor + Extensions", RFC 2132, March 1997. + + [5] Droms, R., "Procedures and IANA Guidelines for Definition of New + DHCP Options and Message Types", BCP 43, RFC 2939, + September 2000. + +11.2. Informative References + + [6] Sollins, K., "The TFTP Protocol (Revision 2)", STD 33, RFC 1350, + July 1992. + + [7] Droms, R. and W. Arbaugh, "Authentication for DHCP Messages", + RFC 3118, June 2001. + + [8] Volz, B., "Reclassifying Dynamic Host Configuration Protocol + version 4 (DHCPv4) Options", RFC 3942, November 2004. + + + + + +Hankins Informational [Page 12] + +RFC 5071 PXELINUX Options December 2007 + + +Author's Address + + David W. Hankins + Internet Systems Consortium, Inc. + 950 Charter Street + Redwood City, CA 94063 + US + + Phone: +1 650 423 1307 + EMail: David_Hankins@isc.org + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Hankins Informational [Page 13] + +RFC 5071 PXELINUX Options December 2007 + + +Full Copyright Statement + + Copyright (C) The IETF Trust (2007). + + This document is subject to the rights, licenses and restrictions + contained in BCP 78, and except as set forth therein, the authors + retain all their rights. + + This document and the information contained herein are provided on an + "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS + OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND + THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF + THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED + WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +Intellectual Property + + The IETF takes no position regarding the validity or scope of any + Intellectual Property Rights or other rights that might be claimed to + pertain to the implementation or use of the technology described in + this document or the extent to which any license under such rights + might or might not be available; nor does it represent that it has + made any independent effort to identify any such rights. Information + on the procedures with respect to rights in RFC documents can be + found in BCP 78 and BCP 79. + + Copies of IPR disclosures made to the IETF Secretariat and any + assurances of licenses to be made available, or the result of an + attempt made to obtain a general license or permission for the use of + such proprietary rights by implementers or users of this + specification can be obtained from the IETF on-line IPR repository at + http://www.ietf.org/ipr. + + The IETF invites any interested party to bring to its attention any + copyrights, patents or patent applications, or other proprietary + rights that may cover technology that may be required to implement + this standard. Please address the information to the IETF at + ietf-ipr@ietf.org. + + + + + + + + + + + + +Hankins Informational [Page 14] + diff --git a/doc/sdi.txt b/doc/sdi.txt new file mode 100644 index 0000000..cf9b73f --- /dev/null +++ b/doc/sdi.txt @@ -0,0 +1,149 @@ + SDI files + + +Syslinux supports SDI files ( *.sdi ). + +Features: + * Support for gzipped SDI images + * When used with gpxelinux.0, images can be downloaded by HTTP or FTP, + leading to fastest boot times. + +"System Deployment Image" is a file format created by Microsoft and mostly used +in its products to provide in a single file a boot loader, an OS loader +(like NTLDR) and a disk or partition image to boot from it without any +other installed program. This is typically used in a PXE environment to boot +embedded Windows versions without boot disk support. + +The support of SDI images in Syslinux is based on a white +paper from Saad Syed. You can find the paper here: + +http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnxpesp1/html/ram_sdi.asp + +SDI support has been only been tested with SDI v1.0 with Windows XP Embedded +images and may not work with later versions or alternative uses. + + + ++++ Supported SDI images ++++ + +To make a SDI image supported by pxelinux/isolinux/syslinux, you need to +follow the steps below (detailed instructions are in the white paper +cited above): + +You need to install "Windows Embedded Studio" and to run the +"Remote Boot Service Setup". + +1) Create a new SDI file (eg: sdimgr /new xpe.sdi). + +2) Before importing your target partition, add the following files +in the root folder: + * ntdetect.com + * boot.ini + Its content should be: + [boot loader] + default=ramdisk(0)\WINDOWS + [operating systems] + ramdisk(0)\WINDOWS="Windows XPE From RAM" /fastdetect +(you can customize the name and add options like /debug) + +Note: Your partition may be compressed (using compressed NTFS), but these two +files need to be uncompressed. + +3) Import the partition in the SDI file (eg: sdimgr xpe.sdi /readpart:D:). +The size of the partition must be less than 500 MB. + +4) Import the boot program STARTROM.COM +(eg: sdimgr xpe.sdi /import:BOOT,0,C:\Program Files\Windows Embedded\Remote Boot Service\Downloads\startrom.com) + +5) Import the nt loader NTLDR in the SDI file +(eg: sdimgr xpe.sdi /import:LOAD,0,C:\Program Files\Windows Embedded\Remote Boot Service\Downloads\ntldr) + +Note: only the version of NTLDR provided by Remote Boot Service Setup +and located in this directory has been tested. According to +"http://skolk.livejournal.com/667.html", "osloader.exe" from retail XP +can also be used to replace this NTLDR version. + +6) Pack the SDI file (eg: sdimgr xpe.sdi /pack) + +7) Gzip your image +If you want to speed the download time, you can gzip the image as it will +be uncompressed by syslinux during the loading. You can use some programs +like ntfsclone ("http://www.linux-ntfs.org/doku.php?id=ntfsclone") to +remove unused blocks from the NTFS filesystem before deploying your image. + +8) You are now ready to boot your image. +Unlike the traditional way of using SDI images (startrom.n12), you don't need +other files than your SDI image in the tftpboot (for pxelinux), the CD +(for isolinux), or the hard disk for syslinux. + +* You can use the usual options of pxelinux/isolinux/syslinux (config file, +config path, reboot time...) + +For example, a simple configuration with pxelinux: +/tftpboot/xpe.sdi +/tftpboot/pxelinux.0 +/tftpboot/pxelinux.cfg/default with the following content: + + DEFAULT 0 + label 0 [WinXpe] + KERNEL sdi.c32 + APPEND xpe.sdi + + + ++++ Error messages ++++ + +* No $SDI signature in file + A SDI image should begin by a signature "$SDI", the signature has not +been found in your file. Perhaps your file is corrupted or has not been created +correctly. Run sdimgr on it to see if everything is correct. + +* No BOOT BLOB in image + You have to import a boot program (eg: startrom.com) when you make +your SDI image (see above). The offset of this program in the SDI file +is in the SDI header (begining of the file). However, the offset +found in your SDI file is null. +You probably forgot to include the boot program. Run the sdimgr program +and look if you see a line like: +BOOT 0x00000000.00001000 0x00000000.00005EC2... + -------- + This is the + offset and + should not + be null + +* BOOT BLOB is empty + See above. The size of your boot program included in the SDI +is null. You probably imported a corrupted version of startrom.com. +Run sdimgr and check the size in the following line: +BOOT 0x00000000.00001000 0x00000000.00005EC2... + -------- + this is the + size and + should not + be null + +* BOOT BLOB extends beyond file + You have a BOOT BLOB in your SDI file, but its size is invalid +because its goes beyond the total image size. Check the tools you used +to build the image file. + +* BOOT BLOB too large for memory + Your BOOT BLOB seems correct, however there is not enough memory +to load it. Increase your RAM or reduce the SDI size. This is a very +abnormal situation as the BOOT BLOB is usually very small. Your SDI +file might be corrupted. + +* Image too large for memory + Your SDI file seems correct, however there is not enough memory +to load it. Increase your RAM or reduce the SDI size. + +* SDI header is corrupted + Your SDI file seems correct, but its header contains a checksum +that is invalid. You most likely have a corrupted SDI file. + + + ++++ Warning messages ++++ + +* Warning: unknown SDI version +You are using a newer version of SDI than the one with which this program +has been tested. It may not work. Please give feedback and provide your +SDI version. diff --git a/doc/syslinux.txt b/doc/syslinux.txt new file mode 100644 index 0000000..f654bff --- /dev/null +++ b/doc/syslinux.txt @@ -0,0 +1,790 @@ + The Syslinux Project + + A suite of bootloaders for Linux + + Copyright 1994-2009 H. Peter Anvin and contributors + +This program is provided under the terms of the GNU General Public +License, version 2 or, at your option, any later version. There is no +warranty, neither expressed nor implied, to the function of this +program. Please see the included file COPYING for details. + +---------------------------------------------------------------------- + + Syslinux now has a home page at http://syslinux.zytor.com/ + +---------------------------------------------------------------------- + +The Syslinux suite contains the following boot loaders +("derivatives"), for their respective boot media: + + SYSLINUX - MS-DOS/Windows FAT filesystem + PXELINUX - PXE network booting + ISOLINUX - ISO9660 CD-ROM + EXTLINUX - Linux ext2/ext3 filesystem + +For historical reasons, some of the sections in this document applies +to the FAT loader (SYSLINUX) only; see pxelinux.txt, isolinux.txt and +extlinux.txt for what differs in these versions. The all-caps term +"SYSLINUX" generally refers to the FAT loader, whereas "Syslinux" +refers to the project as a whole. + +Help with cleaning up the docs would be greatly appreciated. + + + ++++ Options ++++ + +These are the options common to all versions of Syslinux: + + -s Safe, slow, stupid; uses simpler code that boots better + -f Force installing + -r Raid mode. If boot fails, tell the BIOS to boot the next + device in the boot sequence (usually the next hard disk) + instead of stopping with an error message. + This is useful for RAID-1 booting. + +These are only in the Windows version: + + -m Mbr; install a bootable MBR sector to the beginning of the + drive. + -a Active; marks the partition used active (=bootable) + + + ++++ CREATING A BOOTABLE LINUX FLOPPY +++ + +In order to create a bootable Linux floppy using SYSLINUX, prepare a +normal MS-DOS formatted floppy. Copy one or more Linux kernel files to +it, then execute the DOS command: + + syslinux [-sfrma][-d directory] a: [bootsecfile] + +(or whichever drive letter is appropriate; the [] meaning optional.) + +Use "syslinux.com" (in the dos subdirectory of the distribution) for +plain DOS (MS-DOS, DR-DOS, PC-DOS, FreeDOS...) or Win9x/ME. + +Use "syslinux.exe" (in the win32 subdirectory of the distribution) for +WinNT/2000/XP. + +Under Linux, execute the command: + + syslinux [-sfr][-d directory][-o offset] /dev/fd0 + +(or, again, whichever device is the correct one.) + +This will alter the boot sector on the disk and copy a file named +LDLINUX.SYS into its root directory (or a subdirectory, if the -d +option is specified.) + +The -s option, if given, will install a "safe, slow and stupid" +version of SYSLINUX. This version may work on some very buggy BIOSes +on which SYSLINUX would otherwise fail. If you find a machine on +which the -s option is required to make it boot reliably, please send +as much info about your machine as you can, and include the failure +mode. + +The -o option is used with a disk image file and specifies the byte +offset of the filesystem image in the file. + +For the DOS and Windows installers, the -m and -a options can be used +on hard drives to write a Master Boot Record (MBR), and to mark the +specific partition active. + +On boot time, by default, the kernel will be loaded from the image named +LINUX on the boot floppy. This default can be changed, see the section +on the Syslinux config file. + +If the Shift or Alt keys are held down during boot, or the Caps or Scroll +locks are set, Syslinux will display a LILO-style "boot:" prompt. The +user can then type a kernel file name followed by any kernel parameters. +The Syslinux loader does not need to know about the kernel file in +advance; all that is required is that it is a file located in the root +directory on the disk. + +There are two versions of the Linux installer; one in the "mtools" +directory which requires no special privilege (other than write +permission to the device where you are installing) but requires the +mtools program suite to be available, and one in the "unix" directory +which requires root privilege. + + + ++++ CONFIGURATION FILE ++++ + +All the configurable defaults in SYSLINUX can be changed by putting a +file called "syslinux.cfg" in the root directory of the boot disk. + +This is a text file in either UNIX or DOS format, containing one or +more of the following items (case is insensitive for keywords; upper +case is used here to indicate that a word should be typed verbatim): + +Starting with version 3.35, the configuration file can also be in +either the /boot/syslinux or /syslinux directories (searched in that +order.) If that is the case, then all filenames are assumed to be +relative to that same directory, unless preceded with a slash or +backslash. + +All options here applies to PXELINUX, ISOLINUX and EXTLINUX as well as +SYSLINUX unless otherwise noted. See the respective .txt files. + +# comment + A comment line. The whitespace after the hash mark is mandatory. + +INCLUDE filename + Inserts the contents of another file at this point in the + configuration file. Files can currently be nested up to 16 + levels deep, but it is not guaranteed that more than 8 levels + will be supported in the future. + +DEFAULT kernel options... + Sets the default command line. If Syslinux boots automatically, + it will act just as if the entries after DEFAULT had been typed + in at the "boot:" prompt. + + If no configuration file is present, or no DEFAULT entry is + present in the config file, the default is "linux auto". + + NOTE: Earlier versions of Syslinux used to automatically + append the string "auto" to whatever the user specified using + the DEFAULT command. As of version 1.54, this is no longer + true, as it caused problems when using a shell as a substitute + for "init." You may want to include this option manually. + +UI module options... + Selects a specific user interface module (typically menu.c32 + or vesamenu.c32). The command-line interface treats this as a + directive that overrides the DEFAULT and PROMPT directives. + +APPEND options... + Add one or more options to the kernel command line. These are + added both for automatic and manual boots. The options are + added at the very beginning of the kernel command line, + usually permitting explicitly entered kernel options to override + them. This is the equivalent of the LILO "append" option. + +IPAPPEND flag_val [PXELINUX only] + The IPAPPEND option is available only on PXELINUX. The + flag_val is an OR of the following options: + + 1: indicates that an option of the following format + should be generated and added to the kernel command line: + + ip=::: + + ... based on the input from the DHCP/BOOTP or PXE boot server. + + THE USE OF THIS OPTION IS NOT RECOMMENDED. If you have to use + it, it is probably an indication that your network configuration + is broken. Using just "ip=dhcp" on the kernel command line + is a preferrable option, or, better yet, run dhcpcd/dhclient, + from an initrd if necessary. + + 2: indicates that an option of the following format + should be generated and added to the kernel command line: + + BOOTIF= + + ... in dash-separated hexadecimal with leading hardware type + (same as for the configuration file; see pxelinux.txt.) + + This allows an initrd program to determine from which + interface the system booted. + +LABEL label + KERNEL image + APPEND options... + IPAPPEND flag_val [PXELINUX only] + Indicates that if "label" is entered as the kernel to boot, + Syslinux should instead boot "image", and the specified APPEND + and IPAPPEND options should be used instead of the ones + specified in the global section of the file (before the first + LABEL command.) The default for "image" is the same as + "label", and if no APPEND is given the default is to use the + global entry (if any). + + Starting with version 3.62, the number of LABEL statements is + virtually unlimited. + + Note that LILO uses the syntax: + image = mykernel + label = mylabel + append = "myoptions" + + ... whereas Syslinux uses the syntax: + label mylabel + kernel mykernel + append myoptions + + Note: The "kernel" doesn't have to be a Linux kernel; it can + be a boot sector or a COMBOOT file (see below.) + + Since version 3.32 label names are no longer mangled into DOS + format (for SYSLINUX.) + + The following commands are available after a LABEL statement: + + LINUX image - Linux kernel image (default) + BOOT image - Bootstrap program (.bs, .bin) + BSS image - BSS image (.bss) + PXE image - PXE Network Bootstrap Program (.0) + FDIMAGE image - Floppy disk image (.img) + COMBOOT image - COMBOOT program (.com, .cbt) + COM32 image - COM32 program (.c32) + CONFIG image - New configuration file + Using one of these keywords instead of KERNEL forces the + filetype, regardless of the filename. + + CONFIG means restart the boot loader using a different + configuration file. + + APPEND - + Append nothing. APPEND with a single hyphen as argument in a + LABEL section can be used to override a global APPEND. + + LOCALBOOT type [ISOLINUX, PXELINUX] + On PXELINUX, specifying "LOCALBOOT 0" instead of a "KERNEL" + option means invoking this particular label will cause a local + disk boot instead of booting a kernel. + + The argument 0 means perform a normal boot. The argument 4 + will perform a local boot with the Universal Network Driver + Interface (UNDI) driver still resident in memory. Finally, + the argument 5 will perform a local boot with the entire PXE + stack, including the UNDI driver, still resident in memory. + All other values are undefined. If you don't know what the + UNDI or PXE stacks are, don't worry -- you don't want them, + just specify 0. + + On ISOLINUX, the "type" specifies the local drive number to + boot from; 0x00 is the primary floppy drive and 0x80 is the + primary hard drive. The special value -1 causes ISOLINUX to + report failure to the BIOS, which, on recent BIOSes, should + mean that the next boot device in the boot sequence should be + activated. + + INITRD initrd_file + Starting with version 3.71, an initrd can be specified in a + separate statement (INITRD) instead of as part of the APPEND + statement; this functionally appends "initrd=initrd_file" to + the kernel command line. + + It supports multiple filenames separated by commas. + This is mostly useful for initramfs, which can be composed of + multiple separate cpio or cpio.gz archives. + Note: all files except the last one are zero-padded to a + 4K page boundary. This should not affect initramfs. + +IMPLICIT flag_val + If flag_val is 0, do not load a kernel image unless it has been + explicitly named in a LABEL statement. The default is 1. + +ALLOWOPTIONS flag_val + If flag_val is 0, the user is not allowed to specify any + arguments on the kernel command line. The only options + recognized are those specified in an APPEND statement. The + default is 1. + +TIMEOUT timeout + Indicates how long to wait at the boot: prompt until booting + automatically, in units of 1/10 s. The timeout is cancelled as + soon as the user types anything on the keyboard, the assumption + being that the user will complete the command line already + begun. A timeout of zero will disable the timeout completely, + this is also the default. + +TOTALTIMEOUT timeout + Indicates how long to wait until booting automatically, in + units of 1/10 s. This timeout is *not* cancelled by user + input, and can thus be used to deal with serial port glitches + or "the user walked away" type situations. A timeout of zero + will disable the timeout completely, this is also the default. + + Both TIMEOUT and TOTALTIMEOUT can be used together, for + example: + + # Wait 5 seconds unless the user types something, but + # always boot after 15 minutes. + TIMEOUT 50 + TOTALTIMEOUT 9000 + +ONTIMEOUT kernel options... + Sets the command line invoked on a timeout. Normally this is + the same thing as invoked by "DEFAULT". If this is specified, + then "DEFAULT" is used only if the user presses to + boot. + +ONERROR kernel options... + If a kernel image is not found (either due to it not existing, + or because IMPLICIT is set), run the specified command. The + faulty command line is appended to the specified options, so + if the ONERROR directive reads as: + + ONERROR xyzzy plugh + + ... and the command line as entered by the user is: + + foo bar baz + + ... Syslinux will execute the following as if entered by the + user: + + xyzzy plugh foo bar baz + +SERIAL port [[baudrate] flowcontrol] + Enables a serial port to act as the console. "port" is a + number (0 = /dev/ttyS0 = COM1, etc.) or an I/O port address + (e.g. 0x3F8); if "baudrate" is omitted, the baud rate defaults + to 9600 bps. The serial parameters are hardcoded to be 8 + bits, no parity, 1 stop bit. + + "flowcontrol" is a combination of the following bits: + 0x001 - Assert DTR + 0x002 - Assert RTS + 0x008 - Enable interrupts + 0x010 - Wait for CTS assertion + 0x020 - Wait for DSR assertion + 0x040 - Wait for RI assertion + 0x080 - Wait for DCD assertion + 0x100 - Ignore input unless CTS asserted + 0x200 - Ignore input unless DSR asserted + 0x400 - Ignore input unless RI asserted + 0x800 - Ignore input unless DCD asserted + + All other bits are reserved. + + Typical values are: + + 0 - No flow control (default) + 0x303 - Null modem cable detect + 0x013 - RTS/CTS flow control + 0x813 - RTS/CTS flow control, modem input + 0x023 - DTR/DSR flow control + 0x083 - DTR/DCD flow control + + For the SERIAL directive to be guaranteed to work properly, it + should be the first directive in the configuration file. + + NOTE: "port" values from 0 to 3 means the first four serial + ports detected by the BIOS. They may or may not correspond to + the legacy port values 0x3F8, 0x2F8, 0x3E8, 0x2E8. + + Enabling interrupts (setting the 0x008 bit) may give better + responsiveness without setting the NOHALT option, but could + potentially cause problems with buggy BIOSes. + +NOHALT flag_val + If flag_val is 1, don't halt the processor while idle. + Halting the processor while idle significantly reduces the + power consumption, but can cause poor responsiveness to the + serial console, especially when using scripts to drive the + serial console, as opposed to human interaction. + +CONSOLE flag_val + If flag_val is 0, disable output to the normal video console. + If flag_val is 1, enable output to the video console (this is + the default.) + + Some BIOSes try to forward this to the serial console and + sometimes make a total mess thereof, so this option lets you + disable the video console on these systems. + +FONT filename + Load a font in .psf format before displaying any output + (except the copyright line, which is output as ldlinux.sys + itself is loaded.) Syslinux only loads the font onto the + video card; if the .psf file contains a Unicode table it is + ignored. This only works on EGA and VGA cards; hopefully it + should do nothing on others. + +KBDMAP keymap + Install a simple keyboard map. The keyboard remapper used is + *very* simplistic (it simply remaps the keycodes received from + the BIOS, which means that only the key combinations relevant + in the default layout -- usually U.S. English -- can be + mapped) but should at least help people with AZERTY keyboard + layout and the locations of = and , (two special characters + used heavily on the Linux kernel command line.) + + The included program keytab-lilo.pl from the LILO distribution + can be used to create such keymaps. The file keytab-lilo.txt + contains the documentation for this program. + +DISPLAY filename + Displays the indicated file on the screen at boot time (before + the boot: prompt, if displayed). Please see the section below + on DISPLAY files. + + NOTE: If the file is missing, this option is simply ignored. + +SAY message + Prints the message on the screen. + +PROMPT flag_val + If flag_val is 0, display the boot: prompt only if the Shift or Alt + key is pressed, or Caps Lock or Scroll lock is set (this is the + default). If flag_val is 1, always display the boot: prompt. + +NOESCAPE flag_val + If flag_val is set to 1, ignore the Shift/Alt/Caps Lock/Scroll + Lock escapes. Use this (together with PROMPT 0) to force the + default boot alternative. + +NOCOMPLETE flag_val + If flag_val is set to 1, the Tab key does not display labels + at the boot: prompt. + +F1 filename +F2 filename + ...etc... +F9 filename +F10 filename +F11 filename +F12 filename + Displays the indicated file on the screen when a function key is + pressed at the boot: prompt. This can be used to implement + pre-boot online help (presumably for the kernel command line + options.) Please see the section below on DISPLAY files. + + When using the serial console, press to get to + the help screens, e.g. <2> to get to the F2 screen. + For F10-F12, hit , B, C. For + compatibility with earlier versions, F10 can also be entered as + 0. + +Blank lines are ignored. + +Note that the configuration file is not completely decoded. Syntax +different from the one described above may still work correctly in this +version of Syslinux, but may break in a future one. + + + ++++ DISPLAY FILE FORMAT ++++ + +DISPLAY and function-key help files are text files in either DOS or UNIX +format (with or without ). In addition, the following special codes +are interpreted: + + = = ASCII 12 + Clear the screen, home the cursor. Note that the screen is + filled with the current display color. + + = = ASCII 15 + Set the display colors to the specified background and + foreground colors, where and are hex digits, + corresponding to the standard PC display attributes: + + 0 = black 8 = dark grey + 1 = dark blue 9 = bright blue + 2 = dark green a = bright green + 3 = dark cyan b = bright cyan + 4 = dark red c = bright red + 5 = dark purple d = bright purple + 6 = brown e = yellow + 7 = light grey f = white + + Picking a bright color (8-f) for the background results in the + corresponding dark color (0-7), with the foreground flashing. + + Colors are not visible over the serial console. + +filename = = ASCII 24 + If a VGA display is present, enter graphics mode and display + the graphic included in the specified file. The file format + is an ad hoc format called LSS16; the included Perl program + "ppmtolss16" can be used to produce these images. This Perl + program also includes the file format specification. + + The image is displayed in 640x480 16-color mode. Once in + graphics mode, the display attributes (set by code + sequences) work slightly differently: the background color is + ignored, and the foreground colors are the 16 colors specified + in the image file. For that reason, ppmtolss16 allows you to + specify that certain colors should be assigned to specific + color indicies. + + Color indicies 0 and 7, in particular, should be chosen with + care: 0 is the background color, and 7 is the color used for + the text printed by Syslinux itself. + + = = ASCII 25 + If we are currently in graphics mode, return to text mode. + +.. .. = ASCII 16-23 + These codes can be used to select which modes to print a + certain part of the message file in. Each of these control + characters select a specific set of modes (text screen, + graphics screen, serial port) for which the output is actually + displayed: + + Character Text Graph Serial + ------------------------------------------------------ + = = ASCII 16 No No No + = = ASCII 17 Yes No No + = = ASCII 18 No Yes No + = = ASCII 19 Yes Yes No + = = ASCII 20 No No Yes + = = ASCII 21 Yes No Yes + = = ASCII 22 No Yes Yes + = = ASCII 23 Yes Yes Yes + + For example: + + Text modeGraphics modeSerial port + + ... will actually print out which mode the console is in! + + = = ASCII 26 + End of file (DOS convention). + + = = ASCII 7 + Beep the speaker. + + + ++++ COMMAND LINE KEYSTROKES ++++ + +The command line prompt supports the following keystrokes: + + boot specified command line + erase one character + erase the whole line + display the current Syslinux version + erase one word + force text mode + list matching labels +.. help screens (if configured) + equivalent to F1..F10 + interrupt boot in progress + interrupt boot in progress + display network information (PXELINUX only) + + + ++++ COMBOOT IMAGES AND OTHER OPERATING SYSTEMS ++++ + +This version of Syslinux supports chain loading of other operating +systems (such as MS-DOS and its derivatives, including Windows 95/98), +as well as COMBOOT-style standalone executables (a subset of DOS .COM +files; see separate section below.) + +Chain loading requires the boot sector of the foreign operating system +to be stored in a file in the root directory of the filesystem. +Because neither Linux kernels, boot sector images, nor COMBOOT files +have reliable magic numbers, Syslinux will look at the file extension. +The following extensions are recognized (case insensitive): + + none or other Linux kernel image + .0 PXE bootstrap program (NBP) [PXELINUX only] + .bin "CD boot sector" [ISOLINUX only] + .bs Boot sector [SYSLINUX only] + .bss Boot sector, DOS superblock will be patched in [SYSLINUX only] + .c32 COM32 image (32-bit COMBOOT) + .cbt COMBOOT image (not runnable from DOS) + .com COMBOOT image (runnable from DOS) + .img Disk image [ISOLINUX only] + +For filenames given on the command line, Syslinux will search for the +file by adding extensions in the order listed above if the plain +filename is not found. Filenames in KERNEL statements must be fully +qualified. + +If this is specified with one of the keywords LINUX, BOOT, BSS, +FDIMAGE, COMBOOT, COM32, or CONFIG instead of KERNEL, the filetype is +considered to be the one specified regardless of the filename. + + + ++++ BOOTING DOS (OR OTHER SIMILAR OPERATING SYSTEMS) ++++ + +This section applies to SYSLINUX only, not to PXELINUX or ISOLINUX. +See isolinux.txt for an equivalent procedure for ISOLINUX. + +This is the recommended procedure for creating a SYSLINUX disk that +can boot either DOS or Linux. This example assumes the drive is A: in +DOS and /dev/fd0 in Linux; for other drives, substitute the +appropriate drive designator. + + ---- Linux procedure ---- + +1. Make a DOS bootable disk. This can be done either by specifying + the /s option when formatting the disk in DOS, or by running the + DOS command SYS (this can be done under DOSEMU if DOSEMU has + direct device access to the relevant drive): + + format a: /s + or + sys a: + +2. Boot Linux. Copy the DOS boot sector from the disk into a file: + + dd if=/dev/fd0 of=dos.bss bs=512 count=1 + +3. Run SYSLINUX on the disk: + + syslinux /dev/fd0 + +4. Mount the disk and copy the DOS boot sector file to it. The file + *must* have extension .bss: + + mount -t msdos /dev/fd0 /mnt + cp dos.bss /mnt + +5. Copy the Linux kernel image(s), initrd(s), etc to the disk, and + create/edit syslinux.cfg and help files if desired: + + cp vmlinux /mnt + cp initrd.gz /mnt + +6. Unmount the disk (if applicable.) + + umount /mnt + + ---- DOS/Windows procedure ---- + +To make this installation in DOS only, you need the utility copybs.com +(included with Syslinux) as well as the syslinux.com installer. If +you are on an WinNT-based system (WinNT, Win2k, WinXP or later), use +syslinux.exe instead. + +1. Make a DOS bootable disk. This can be done either by specifying + the /s option when formatting the disk in DOS, or by running the + DOS command SYS: + + format a: /s + or + sys a: + +2. Copy the DOS boot sector from the disk into a file. The file + *must* have extension .bss: + + copybs a: a:dos.bss + +3. Run SYSLINUX on the disk: + + syslinux a: + +4. Copy the Linux kernel image(s), initrd(s), etc to the disk, and + create/edit syslinux.cfg and help files if desired: + + copy vmlinux a: + copy initrd.gz a: + + + ++++ COMBOOT EXECUTABLES ++++ + +Syslinux supports simple standalone programs, using a file format +similar to DOS ".com" files. A 32-bit version, called COM32, is also +provided. A simple API provides access to a limited set of filesystem +and console functions. + +See the file comboot.txt for more information on COMBOOT and COM32 +programs. + + + ++++ NOVICE PROTECTION ++++ + +Syslinux will attempt to detect booting on a machine with too little +memory, which means the Linux boot sequence cannot complete. If so, a +message is displayed and the boot sequence aborted. Holding down the +Ctrl key while booting disables this feature. + +Any file that SYSLINUX uses can be marked hidden, system or readonly +if so is convenient; SYSLINUX ignores all file attributes. The +SYSLINUX installed automatically sets the readonly/hidden/system +attributes on LDLINUX.SYS. + + + ++++ NOTES ON BOOTABLE CD-ROMS ++++ + +SYSLINUX can be used to create bootdisk images for El +Torito-compatible bootable CD-ROMs. However, it appears that many +BIOSes are very buggy when it comes to booting CD-ROMs. Some users +have reported that the following steps are helpful in making a CD-ROM +that is bootable on the largest possible number of machines: + + a) Use the -s (safe, slow and stupid) option to SYSLINUX; + b) Put the boot image as close to the beginning of the + ISO 9660 filesystem as possible. + +A CD-ROM is so much faster than a floppy that the -s option shouldn't +matter from a speed perspective. + +Of course, you probably want to use ISOLINUX instead. See isolinux.txt. + + + ++++ BOOTING FROM A FAT FILESYSTEM PARTITION ON A HARD DISK ++++ + +SYSLINUX can boot from a FAT filesystem partition on a hard disk +(including FAT32). The installation procedure is identical to the +procedure for installing it on a floppy, and should work under either +DOS or Linux. To boot from a partition, SYSLINUX needs to be launched +from a Master Boot Record or another boot loader, just like DOS itself +would. + +Under DOS, you can install a standard simple MBR on the primary hard +disk by running the command: + + FDISK /MBR + +Then use the FDISK command to mark the appropriate partition active. + +A simple MBR, roughly on par with the one installed by DOS (but +unencumbered), is included in the SYSLINUX distribution. To install +it under Linux, simply type: + + cat mbr.bin > /dev/XXX + +... where /dev/XXX is the device you wish to install it on. + +Under DOS or Win32, you can install the SYSLINUX MBR with the -m +option to the SYSLINUX installer, and use the -a option to mark the +current partition active: + + syslinux -ma c: + +Note that this will also install SYSLINUX on the specified partition. + + + ++++ HARDWARE INFORMATION +++ + +I have started to maintain a web page of hardware with known +problems. There are, unfortunately, lots of broken hardware out +there; especially early PXE stacks (for PXELINUX) have lots of +problems. + +A list of problems, and workarounds (if known), is maintained at: + + http://syslinux.zytor.com/hardware.php + + + ++++ BOOT LOADER IDS USED ++++ + +The Linux boot protocol supports a "boot loader ID", a single byte +where the upper nybble specifies a boot loader family (3 = Syslinux) +and the lower nybble is version or, in the case of Syslinux, media: + + 0x31 (49) = SYSLINUX + 0x32 (50) = PXELINUX + 0x33 (51) = ISOLINUX + 0x34 (52) = EXTLINUX + +In recent versions of Linux, this ID is available as +/proc/sys/kernel/bootloader_type. + + + ++++ BUG REPORTS ++++ + +I would appreciate hearing of any problems you have with Syslinux. I +would also like to hear from you if you have successfully used Syslinux, +*especially* if you are using it for a distribution. + +If you are reporting problems, please include all possible information +about your system and your BIOS; the vast majority of all problems +reported turn out to be BIOS or hardware bugs, and I need as much +information as possible in order to diagnose the problems. + +There is a mailing list for discussion among Syslinux users and for +announcements of new and test versions. To join, or to browse the +archive, go to: + + http://www.zytor.com/mailman/listinfo/syslinux + +Please DO NOT send HTML messages or attachments to the mailing list +(including multipart/alternative or similar.) All such messages will +be bounced. diff --git a/doc/usbkey.txt b/doc/usbkey.txt new file mode 100644 index 0000000..33613d6 --- /dev/null +++ b/doc/usbkey.txt @@ -0,0 +1,47 @@ +The proper mode to boot a USB key drive in is "USB-HDD". That is the +ONLY mode in which the C/H/S geometry encoded on the disk itself +doesn't have to match what the BIOS thinks it is. Since geometry on +USB drives is completely arbitrary, and can vary from BIOS to BIOS, +this is the only mode which will work in general. + +Some BIOSes have been reported (in particular, certain versions of the +Award BIOS) that cannot boot USB keys in "USB-HDD" mode. This is a +very serious BIOS bug, but it is unfortunately rather typical of the +kind of quality we're seeing out of major BIOS vendors these days. On +these BIOSes, you're generally stuck booting them in USB-ZIP mode. + +THIS MEANS THE FILESYSTEM IMAGE ON THE DISK HAS TO HAVE A CORRECT +ZIPDRIVE-COMPATIBLE GEOMETRY. + +A standard zipdrive (both the 100 MB and the 250 MB varieties) have a +"geometry" of 64 heads, 32 sectors, and are partitioned devices with a +single partition 4 (unlike most other media of this type which uses +partition 1.) The 100 MB variety has 96 cylinders, and the 250 MB +variety has 239 cylinders; but any number of cylinders will do as +appropriate for the size device you have. For example, if your device +reports when inserted into a Linux system: + +usb-storage: device found at 4 + Vendor: 32MB Model: HardDrive Rev: 1.88 + Type: Direct-Access ANSI SCSI revision: 02 +SCSI device sda: 64000 512-byte hdwr sectors (33 MB) + +... you would have 64000/(64*32) = 31.25 cylinders; round down to 31. + +The script "mkdiskimage" which is supplied with the syslinux +distribution can be used to initialize USB keys in a Zip-like fashion. +To do that, calculate the correct number of cylinders (31 in the +example above), and, if your USB key is /dev/sda (CHECK THE KERNEL +MESSAGES CAREFULLY - IF YOU ENTER THE WRONG DISK DRIVE IT CANNOT BE +RECOVERED), run: + + mkdiskimage -4 /dev/sda 0 64 32 + +(The 0 means automatically determine the size of the device, and -4 +means mimic a zipdisk by using partition 4.) + +Then you should be able to run + + syslinux /dev/sda4 + +... and mount /dev/sda4 and put your files on it as needed. diff --git a/dos/Makefile b/dos/Makefile new file mode 100644 index 0000000..fa2ed0a --- /dev/null +++ b/dos/Makefile @@ -0,0 +1,66 @@ +## ----------------------------------------------------------------------- +## +## Copyright 2001-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +## +## MS-DOS FAT installer +## + +topdir = .. +include $(topdir)/MCONFIG.embedded + +LDFLAGS = -T com16.ld +OPTFLAGS = -g +INCLUDES = -include code16.h -nostdinc -iwithprefix include \ + -I. -I.. -I../libfat -I ../libinstaller + +SRCS = syslinux.c \ + ../libinstaller/syslxmod.c \ + ../libinstaller/bootsect_bin.c \ + ../libinstaller/ldlinux_bin.c \ + ../libinstaller/mbr_bin.c \ + $(wildcard ../libfat/*.c) +OBJS = crt0.o $(patsubst %.c,%.o,$(notdir $(SRCS))) +LIBOBJS = conio.o memcpy.o memset.o skipatou.o atou.o malloc.o free.o \ + argv.o printf.o __divdi3.o __udivmoddi4.o + +VPATH = .:../libfat:../libinstaller + +TARGETS = syslinux.com copybs.com + +all: $(TARGETS) + +tidy dist: + -rm -f *.o *.i *.s *.a .*.d *.tmp *.elf *.lst + +clean: tidy + +spotless: clean + -rm -f *~ $(TARGETS) + +installer: + +syslinux.elf: $(OBJS) libcom.a + $(LD) $(LDFLAGS) -o $@ $^ + +libcom.a: $(LIBOBJS) + -rm -f $@ + $(AR) cq $@ $^ + $(RANLIB) $@ + +syslinux.com: syslinux.elf + $(OBJCOPY) -O binary $< $@ + +%.com: %.asm + ( $(NASM) -M -DDEPEND $(NASMOPT) -o $@ -M $< && echo '' ) > .$@.d; true + $(NASM) $(NASMOPT) -f bin -o $@ -l $*.lst $< + +-include .*.d *.tmp diff --git a/dos/__divdi3.c b/dos/__divdi3.c new file mode 100644 index 0000000..3641396 --- /dev/null +++ b/dos/__divdi3.c @@ -0,0 +1,29 @@ +/* + * arch/i386/libgcc/__divdi3.c + */ + +#include +#include + +extern uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t *rem); + +int64_t __divdi3(int64_t num, int64_t den) +{ + int minus = 0; + int64_t v; + + if ( num < 0 ) { + num = -num; + minus = 1; + } + if ( den < 0 ) { + den = -den; + minus ^= 1; + } + + v = __udivmoddi4(num, den, NULL); + if ( minus ) + v = -v; + + return v; +} diff --git a/dos/__udivmoddi4.c b/dos/__udivmoddi4.c new file mode 100644 index 0000000..8e7661f --- /dev/null +++ b/dos/__udivmoddi4.c @@ -0,0 +1,31 @@ +#include + +uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t *rem_p) +{ + uint64_t quot = 0, qbit = 1; + + if ( den == 0 ) { + asm volatile("int $0"); + return 0; /* If trap returns... */ + } + + /* Left-justify denominator and count shift */ + while ( (int64_t)den >= 0 ) { + den <<= 1; + qbit <<= 1; + } + + while ( qbit ) { + if ( den <= num ) { + num -= den; + quot += qbit; + } + den >>= 1; + qbit >>= 1; + } + + if ( rem_p ) + *rem_p = num; + + return quot; +} diff --git a/dos/argv.c b/dos/argv.c new file mode 100644 index 0000000..84888b0 --- /dev/null +++ b/dos/argv.c @@ -0,0 +1,92 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * argv.c + * + * Parse a single C string into argc and argv (argc is return value.) + * memptr points to available memory. + */ + +#include +#include +#include + +#define ALIGN_UP(p,t) ((t *)(((uintptr_t)(p) + (sizeof(t)-1)) & ~(sizeof(t)-1))) + +extern char _end[]; /* Symbol created by linker */ +void *__mem_end = &_end; /* Global variable for use by malloc() */ + +int __parse_argv(char ***argv, const char *str) +{ + char *mem = __mem_end; + const char *p = str; + char *q = mem; + char *r; + char **arg; + int wasspace = 0; + int argc = 1; + + /* First copy the string, turning whitespace runs into nulls */ + for ( p = str ; ; p++ ) { + if ( *p <= ' ' ) { + if ( !wasspace ) { + wasspace = 1; + *q++ = '\0'; + } + } else { + if ( wasspace ) { + argc++; + wasspace = 0; + } + *q++ = *p; + } + + /* This test is AFTER we have processed the null byte; + we treat it as a whitespace character so it terminates + the last argument */ + if ( ! *p ) + break; + } + + /* Now create argv */ + arg = ALIGN_UP(q,char *); + *argv = arg; + *arg++ = mem; /* argv[0] */ + + q--; /* Point q to final null */ + for ( r = mem ; r < q ; r++ ) { + if ( *r == '\0' ) { + *arg++ = r+1; + } + } + + *arg++ = NULL; /* Null pointer at the end */ + __mem_end = arg; /* End of memory we used */ + + return argc; +} diff --git a/dos/atou.c b/dos/atou.c new file mode 100644 index 0000000..2766370 --- /dev/null +++ b/dos/atou.c @@ -0,0 +1,9 @@ +#include "mystuff.h" + +unsigned int atou(const char *s) +{ + unsigned int i = 0; + while (isdigit(*s)) + i = i*10 + (*s++ - '0'); + return i; +} diff --git a/dos/code16.h b/dos/code16.h new file mode 100644 index 0000000..ca76565 --- /dev/null +++ b/dos/code16.h @@ -0,0 +1,6 @@ +/* Must be included first of all */ +#ifdef __ASSEMBLY__ + .code16 +#else +__asm__ (".code16gcc"); +#endif diff --git a/dos/com16.ld b/dos/com16.ld new file mode 100644 index 0000000..08a1e95 --- /dev/null +++ b/dos/com16.ld @@ -0,0 +1,127 @@ +/* + * Linker script for COM16 binaries + */ + +/* Script for -z combreloc: combine and sort reloc sections */ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", + "elf32-i386") +OUTPUT_ARCH(i386) +EXTERN(_start) +ENTRY(_start) +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = 0x100; + PROVIDE (__executable_start = .); + + .init : + { + KEEP (*(.init)) + } =0x90909090 + .text : + { + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + } =0x90909090 + .fini : + { + KEEP (*(.fini)) + } =0x90909090 + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + + /* Ensure the __preinit_array_start label is properly aligned. We + could instead move the label definition inside the section, but + the linker would then create the section even if it turns out to + be empty, which isn't pretty. */ + . = ALIGN(4); + PROVIDE (__preinit_array_start = .); + .preinit_array : { *(.preinit_array) } + PROVIDE (__preinit_array_end = .); + PROVIDE (__init_array_start = .); + .init_array : { *(.init_array) } + PROVIDE (__init_array_end = .); + PROVIDE (__fini_array_start = .); + .fini_array : { *(.fini_array) } + PROVIDE (__fini_array_end = .); + PROVIDE (__ctors_start = .); + .ctors : + { + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + PROVIDE (__ctors_end = .); + PROVIDE (__dtors_start = .); + .dtors : + { + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + PROVIDE (__dtors_end = .); + + /* Adjust the address for the data segment. Avoid mixing code and + data within same 128-byte chunk. */ + . = ALIGN(128); + + .data : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + _edata = .; + PROVIDE (edata = .); + __bss_start = .; + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. */ + . = ALIGN(32 / 8); + } + . = ALIGN(32 / 8); + _end = .; + PROVIDE (end = .); + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /DISCARD/ : { *(.note.GNU-stack) } +} diff --git a/dos/conio.c b/dos/conio.c new file mode 100644 index 0000000..bf54805 --- /dev/null +++ b/dos/conio.c @@ -0,0 +1,42 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2001-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * conio.c + * + * Output to the screen + */ + +#include +#include "mystuff.h" + +int putchar(int ch) +{ + if ( ch == '\n' ) + putchar('\r'); + asm("movb $0x02,%%ah ; int $0x21" : : "d" (ch)); + return ch; +} + +/* Note: doesn't put '\n' like the stdc version does */ +int puts(const char *s) +{ + int count = 0; + + while ( *s ) { + putchar(*s); + count++; + s++; + } + + return count; +} diff --git a/dos/copybs.asm b/dos/copybs.asm new file mode 100644 index 0000000..2640714 --- /dev/null +++ b/dos/copybs.asm @@ -0,0 +1,271 @@ +; -*- fundamental -*- (asm-mode sucks) +; ----------------------------------------------------------------------- +; +; Copyright 1998-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +; Boston MA 02111-1307, USA; either version 2 of the License, or +; (at your option) any later version; incorporated herein by reference. +; +; ----------------------------------------------------------------------- + +; +; copybs.asm +; +; Small DOS program to copy the boot sector from a drive +; to a file +; +; Usage: copybs : +; + + absolute 0 +pspInt20: resw 1 +pspNextParagraph: resw 1 + resb 1 ; reserved +pspDispatcher: resb 5 +pspTerminateVector: resd 1 +pspControlCVector: resd 1 +pspCritErrorVector: resd 1 + resw 11 ; reserved +pspEnvironment: resw 1 + resw 23 ; reserved +pspFCB_1: resb 16 +pspFCB_2: resb 16 + resd 1 ; reserved +pspCommandLen: resb 1 +pspCommandArg: resb 127 + + section .text + org 100h ; .COM format +_start: + mov ax,3000h ; Get DOS version + int 21h + xchg al,ah + mov [DOSVersion],ax + cmp ax,0200h ; DOS 2.00 minimum + jae dosver_ok + mov dx,msg_ancient_err + jmp die + + section .bss + alignb 2 +DOSVersion: resw 1 + + section .text +; +; Scan command line for a drive letter followed by a colon +; +dosver_ok: + xor cx,cx + mov si,pspCommandArg + mov cl,[pspCommandLen] + +cmdscan1: jcxz bad_usage ; End of command line? + lodsb ; Load character + dec cx + cmp al,' ' ; White space + jbe cmdscan1 + or al,020h ; -> lower case + cmp al,'a' ; Check for letter + jb bad_usage + cmp al,'z' + ja bad_usage + sub al,'a' ; Convert to zero-based index + mov [DriveNo],al ; Save away drive index + + section .bss +DriveNo: resb 1 + + section .text +; +; Got the leading letter, now the next character must be a colon +; +got_letter: jcxz bad_usage + lodsb + dec cx + cmp al,':' + jne bad_usage +; +; Got the colon; now we should have at least one whitespace +; followed by a filename +; +got_colon: jcxz bad_usage + lodsb + dec cx + cmp al,' ' + ja bad_usage + +skipspace: jcxz bad_usage + lodsb + dec cx + cmp al,' ' + jbe skipspace + + mov di,FileName +copyfile: stosb + jcxz got_cmdline + lodsb + dec cx + cmp al,' ' + ja copyfile + jmp short got_cmdline + +; +; We end up here if the command line doesn't parse +; +bad_usage: mov dx,msg_unfair + jmp die + + section .data +msg_unfair: db 'Usage: copybs : ', 0Dh, 0Ah, '$' + + section .bss + alignb 4 +FileName resb 256 + +; +; Parsed the command line OK. Get device parameter block to get the +; sector size. +; + struc DPB +dpbDrive: resb 1 +dpbUnit: resb 1 +dpbSectorSize: resw 1 +dpbClusterMask: resb 1 +dpbClusterShift: resb 1 +dpbFirstFAT: resw 1 +dpbFATCount: resb 1 +dpbRootEntries: resw 1 +dpbFirstSector: resw 1 +dpbMaxCluster: resw 1 +dpbFATSize: resw 1 +dpbDirSector: resw 1 +dpbDriverAddr: resd 1 +dpbMedia: resb 1 +dpbFirstAccess: resb 1 +dpbNextDPB: resd 1 +dpbNextFree: resw 1 +dpbFreeCnt: resw 1 + endstruc + + section .bss + alignb 2 +SectorSize resw 1 + + section .text +got_cmdline: + xor al,al ; Zero-terminate filename + stosb + + mov dl,[DriveNo] + inc dl ; 1-based + mov ah,32h + int 21h ; Get Drive Parameter Block + + and al,al + jnz filesystem_error + + mov dx,[bx+dpbSectorSize] ; Save sector size +; +; Read the boot sector. +; + section .data + align 4, db 0 +DISKIO equ $ +diStartSector: dd 0 ; Absolute sector 0 +diSectors: dw 1 ; One sector +diBuffer: dw SectorBuffer ; Buffer offset + dw 0 ; Buffer segment + + section .text +read_bootsect: + mov ax,cs ; Set DS <- CS + mov ds,ax + + mov [SectorSize],dx ; Saved sector size from above + + cmp word [DOSVersion],0400h ; DOS 4.00 has a new interface + jae .new +.old: + mov bx,SectorBuffer + mov cx,1 ; One sector + jmp short .common +.new: + mov [diBuffer+2],ax ; == DS + mov bx,DISKIO + mov cx,-1 +.common: + xor dx,dx ; Absolute sector 0 + mov al,[DriveNo] + int 25h ; DOS absolute disk read + pop ax ; Remove flags from stack + jc disk_read_error + +; +; Open the file and write the boot sector to the file. +; + mov dx,FileName + mov cx,0020h ; Attribute = ARCHIVE + mov ah,3Ch ; Create file + int 21h + jc file_write_error + + mov bx,ax + push ax ; Handle + + mov cx,[SectorSize] + mov dx,SectorBuffer + mov ah,40h ; Write file + int 21h + jc file_write_error + cmp ax,[SectorSize] + jne file_write_error + + pop bx ; Handle + mov ah,3Eh ; Close file + int 21h + jc file_write_error +; +; We're done! +; + mov ax,4C00h ; exit(0) + int 21h + +; +; Error routine jump +; +filesystem_error: + mov dx,msg_filesystem_err + jmp short die +disk_read_error: + mov dx,msg_read_err + jmp short die +file_write_error: + mov dx,msg_write_err +die: + push cs + pop ds + push dx + mov dx,msg_error + mov ah,09h + int 21h + pop dx + + mov ah,09h ; Write string + int 21h + + mov ax,4C01h ; Exit error status + int 21h + + section .data +msg_error: db 'ERROR: $' +msg_ancient_err: db 'DOS version 2.00 or later required', 0Dh, 0Ah, '$' +msg_filesystem_err: db 'Filesystem not found on disk', 0Dh, 0Ah, '$' +msg_read_err: db 'Boot sector read failed', 0Dh, 0Ah, '$' +msg_write_err: db 'File write failed', 0Dh, 0Ah, '$' + + section .bss + alignb 4 +SectorBuffer: resb 4096 diff --git a/dos/copybs.com b/dos/copybs.com new file mode 100644 index 0000000000000000000000000000000000000000..a1dabc21012af8da5d38e2c39b6f9b7136b6d30e GIT binary patch literal 408 zcmYL>JuCxZ7{}kfhp14CbZU4mDT{_;tJ8=e5fV}ng5~ty%27Su_pS|-CL-|>votp} zY>n}T_L?9*5{6q$_M7Au8oW@$ljD9FHK(n(Q9@_J{_auk+|Q zu#eo~t0@VxpRFLtv|rlG^+x220(Qe3$Ba%lRF88(x8t{VW8#wA_QO8BM*H38?L}`( z$H4;ry0Zoy5pIpulSO>`boP1_G=+Tp9HR!-TrCO}AM*T{Azbaim0E4!rC7jD_Yxhl zO +#include "malloc.h" + +static struct free_arena_header * +__free_block(struct free_arena_header *ah) +{ + struct free_arena_header *pah, *nah; + + pah = ah->a.prev; + nah = ah->a.next; + if ( pah->a.type == ARENA_TYPE_FREE && + (char *)pah+pah->a.size == (char *)ah ) { + /* Coalesce into the previous block */ + pah->a.size += ah->a.size; + pah->a.next = nah; + nah->a.prev = pah; + +#ifdef DEBUG_MALLOC + ah->a.type = ARENA_TYPE_DEAD; +#endif + + ah = pah; + pah = ah->a.prev; + } else { + /* Need to add this block to the free chain */ + ah->a.type = ARENA_TYPE_FREE; + + ah->next_free = __malloc_head.next_free; + ah->prev_free = &__malloc_head; + __malloc_head.next_free = ah; + ah->next_free->prev_free = ah; + } + + /* In either of the previous cases, we might be able to merge + with the subsequent block... */ + if ( nah->a.type == ARENA_TYPE_FREE && + (char *)ah+ah->a.size == (char *)nah ) { + ah->a.size += nah->a.size; + + /* Remove the old block from the chains */ + nah->next_free->prev_free = nah->prev_free; + nah->prev_free->next_free = nah->next_free; + ah->a.next = nah->a.next; + nah->a.next->a.prev = ah; + +#ifdef DEBUG_MALLOC + nah->a.type = ARENA_TYPE_DEAD; +#endif + } + + /* Return the block that contains the called block */ + return ah; +} + +void free(void *ptr) +{ + struct free_arena_header *ah; + + if ( !ptr ) + return; + + ah = (struct free_arena_header *) + ((struct arena_header *)ptr - 1); + +#ifdef DEBUG_MALLOC + assert( ah->a.type == ARENA_TYPE_USED ); +#endif + + __free_block(ah); + + /* Here we could insert code to return memory to the system. */ +} diff --git a/dos/inttypes.h b/dos/inttypes.h new file mode 100644 index 0000000..9a6118b --- /dev/null +++ b/dos/inttypes.h @@ -0,0 +1 @@ +#include diff --git a/dos/malloc.c b/dos/malloc.c new file mode 100644 index 0000000..052b0a5 --- /dev/null +++ b/dos/malloc.c @@ -0,0 +1,114 @@ +/* + * malloc.c + * + * Very simple linked-list based malloc()/free(). + */ + +#include +#include "malloc.h" + +struct free_arena_header __malloc_head = +{ + { + ARENA_TYPE_HEAD, + 0, + &__malloc_head, + &__malloc_head, + }, + &__malloc_head, + &__malloc_head +}; + +/* This is extern so it can be overridden by the user application */ +const size_t __stack_size = 4096; + +static inline size_t sp(void) +{ + uint32_t sp; + asm volatile("movl %%esp,%0" : "=rm" (sp)); + return sp; +} + +extern void *__mem_end; + +void __init_memory_arena(void) +{ + struct free_arena_header *fp; + size_t start, total_space; + + start = (size_t)ARENA_ALIGN_UP(__mem_end); + total_space = sp() - start; + + fp = (struct free_arena_header *)start; + fp->a.type = ARENA_TYPE_FREE; + fp->a.size = total_space - __stack_size; + + /* Insert into chains */ + fp->a.next = fp->a.prev = &__malloc_head; + fp->next_free = fp->prev_free = &__malloc_head; + __malloc_head.a.next = __malloc_head.a.prev = fp; + __malloc_head.next_free = __malloc_head.prev_free = fp; +} + +static void *__malloc_from_block(struct free_arena_header *fp, size_t size) +{ + size_t fsize; + struct free_arena_header *nfp, *na; + + fsize = fp->a.size; + + /* We need the 2* to account for the larger requirements of a free block */ + if ( fsize >= size+2*sizeof(struct arena_header) ) { + /* Bigger block than required -- split block */ + nfp = (struct free_arena_header *)((char *)fp + size); + na = fp->a.next; + + nfp->a.type = ARENA_TYPE_FREE; + nfp->a.size = fsize-size; + fp->a.type = ARENA_TYPE_USED; + fp->a.size = size; + + /* Insert into all-block chain */ + nfp->a.prev = fp; + nfp->a.next = na; + na->a.prev = nfp; + fp->a.next = nfp; + + /* Replace current block on free chain */ + nfp->next_free = fp->next_free; + nfp->prev_free = fp->prev_free; + fp->next_free->prev_free = nfp; + fp->prev_free->next_free = nfp; + } else { + /* Allocate the whole block */ + fp->a.type = ARENA_TYPE_USED; + + /* Remove from free chain */ + fp->next_free->prev_free = fp->prev_free; + fp->prev_free->next_free = fp->next_free; + } + + return (void *)(&fp->a + 1); +} + +void *malloc(size_t size) +{ + struct free_arena_header *fp; + + if ( size == 0 ) + return NULL; + + /* Add the obligatory arena header, and round up */ + size = (size+2*sizeof(struct arena_header)-1) & ~ARENA_SIZE_MASK; + + for ( fp = __malloc_head.next_free ; fp->a.type != ARENA_TYPE_HEAD ; + fp = fp->next_free ) { + if ( fp->a.size >= size ) { + /* Found fit -- allocate out of this block */ + return __malloc_from_block(fp, size); + } + } + + /* Nothing found... need to request a block from the kernel */ + return NULL; /* No kernel to get stuff from */ +} diff --git a/dos/malloc.h b/dos/malloc.h new file mode 100644 index 0000000..70d0e63 --- /dev/null +++ b/dos/malloc.h @@ -0,0 +1,54 @@ +/* + * malloc.h + * + * Internals for the memory allocator + */ + +#include +#include + +/* + * This is the minimum chunk size we will ask the kernel for; this should + * be a multiple of the page size on all architectures. + */ +#define MALLOC_CHUNK_SIZE 65536 +#define MALLOC_CHUNK_MASK (MALLOC_CHUNK_SIZE-1) + +/* + * This structure should be a power of two. This becomes the + * alignment unit. + */ +struct free_arena_header; + +struct arena_header { + size_t type; + size_t size; /* Also gives the location of the next entry */ + struct free_arena_header *next, *prev; +}; + +#ifdef DEBUG_MALLOC +#define ARENA_TYPE_USED 0x64e69c70 +#define ARENA_TYPE_FREE 0x012d610a +#define ARENA_TYPE_HEAD 0x971676b5 +#define ARENA_TYPE_DEAD 0xeeeeeeee +#else +#define ARENA_TYPE_USED 0 +#define ARENA_TYPE_FREE 1 +#define ARENA_TYPE_HEAD 2 +#endif + +#define ARENA_SIZE_MASK (sizeof(struct arena_header)-1) + +#define ARENA_ALIGN_UP(p) ((char *)(((uintptr_t)(p) + ARENA_SIZE_MASK) & ~ARENA_SIZE_MASK)) +#define ARENA_ALIGN_DOWN(p) ((char *)((uintptr_t)(p) & ~ARENA_SIZE_MASK)) + +/* + * This structure should be no more than twice the size of the + * previous structure. + */ +struct free_arena_header { + struct arena_header a; + struct free_arena_header *next_free, *prev_free; +}; + +extern struct free_arena_header __malloc_head; diff --git a/dos/memcpy.S b/dos/memcpy.S new file mode 100644 index 0000000..76eef73 --- /dev/null +++ b/dos/memcpy.S @@ -0,0 +1,23 @@ +# +# memcpy.S +# +# Simple 16-bit memcpy() implementation +# + + .text + .code16gcc + .globl memcpy + .type memcpy, @function +memcpy: + cld + pushw %di + pushw %si + movw %ax,%di + movw %dx,%si + # The third argument is already in cx + rep ; movsb + popw %si + popw %di + ret + + .size memcpy,.-memcpy diff --git a/dos/memset.S b/dos/memset.S new file mode 100644 index 0000000..86e12ab --- /dev/null +++ b/dos/memset.S @@ -0,0 +1,21 @@ +# +# memset.S +# +# Minimal 16-bit memset() implementation +# + + .text + .code16gcc + .globl memset + .type memset, @function +memset: + cld + pushw %di + movw %ax,%di + movb %dl,%al + # The third argument is already in %cx + rep ; stosb + popw %di + retl + + .size memset,.-memset diff --git a/dos/mystuff.h b/dos/mystuff.h new file mode 100644 index 0000000..83f072f --- /dev/null +++ b/dos/mystuff.h @@ -0,0 +1,15 @@ +#ifndef MYSTUFF_H +#define MYSTUFF_H + +#define NULL ((void *)0) + +unsigned int skip_atou(const char **s); +unsigned int atou(const char *s); + +static inline int +isdigit(int ch) +{ + return (ch >= '0') && (ch <= '9'); +} + +#endif /* MYSTUFF_H */ diff --git a/dos/perror.c b/dos/perror.c new file mode 100644 index 0000000..e6e8287 --- /dev/null +++ b/dos/perror.c @@ -0,0 +1,7 @@ +#include +#include + +void perror(const char *msg) +{ + printf("%s: error %s\n", msg, errno); +} diff --git a/dos/printf.c b/dos/printf.c new file mode 100644 index 0000000..99b389f --- /dev/null +++ b/dos/printf.c @@ -0,0 +1,297 @@ +/* + * Oh, it's a waste of space, but oh-so-yummy for debugging. It's just + * initialization code anyway, so it doesn't take up space when we're + * actually running. This version of printf() does not include 64-bit + * support. "Live with it." + * + * Most of this code was shamelessly snarfed from the Linux kernel, then + * modified. It's therefore GPL. + * + * printf() isn't actually needed to build syslinux.com, but during + * debugging it's handy. + */ + +#include +#include +#include "mystuff.h" + +static int strnlen(const char *s, int maxlen) +{ + const char *es = s; + while ( *es && maxlen ) { + es++; maxlen--; + } + + return (es-s); +} + +#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 char * number(char * str, long num, int base, int size, int precision + ,int type) +{ + char c,sign,tmp[66]; + const char *digits="0123456789abcdefghijklmnopqrstuvwxyz"; + int i; + + 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) + *str++ = ' '; + if (sign) + *str++ = sign; + if (type & SPECIAL) { + if (base==8) + *str++ = '0'; + else if (base==16) { + *str++ = '0'; + *str++ = digits[33]; + } + } + if (!(type & LEFT)) + while (size-- > 0) + *str++ = c; + while (i < precision--) + *str++ = '0'; + while (i-- > 0) + *str++ = tmp[i]; + while (size-- > 0) + *str++ = ' '; + return str; +} + +/* Forward decl. needed for IP address printing stuff... */ +int sprintf(char * buf, const char *fmt, ...); + +int vsprintf(char *buf, const char *fmt, va_list args) +{ + int len; + unsigned long num; + int i, base; + char * str; + 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 */ + + for (str=buf ; *fmt ; ++fmt) { + if (*fmt != '%') { + *str++ = *fmt; + 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 (isdigit(*fmt)) + field_width = skip_atou(&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 (isdigit(*fmt)) + precision = skip_atou(&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) + *str++ = ' '; + *str++ = (unsigned char) va_arg(args, int); + while (--field_width > 0) + *str++ = ' '; + continue; + + case 's': + s = va_arg(args, char *); + len = strnlen(s, precision); + + if (!(flags & LEFT)) + while (len < field_width--) + *str++ = ' '; + for (i = 0; i < len; ++i) + *str++ = *s++; + while (len < field_width--) + *str++ = ' '; + continue; + + case 'p': + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + str = number(str, + (unsigned long) va_arg(args, void *), 16, + field_width, precision, flags); + continue; + + + case 'n': + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = (str - buf); + } else { + int * ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + *str++ = '%'; + 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: + *str++ = '%'; + if (*fmt) + *str++ = *fmt; + else + --fmt; + continue; + } + 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); + str = number(str, num, base, field_width, precision, flags); + } + *str = '\0'; + return str-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; +} + +int printf(const char *fmt, ...) +{ + char printf_buf[1024]; + va_list args; + int printed; + + va_start(args, fmt); + printed = vsprintf(printf_buf, fmt, args); + va_end(args); + + puts(printf_buf); + + return printed; +} diff --git a/dos/skipatou.c b/dos/skipatou.c new file mode 100644 index 0000000..f71c534 --- /dev/null +++ b/dos/skipatou.c @@ -0,0 +1,10 @@ +#include "mystuff.h" + +unsigned int skip_atou(const char **s) +{ + int i=0; + + while (isdigit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} diff --git a/dos/stdint.h b/dos/stdint.h new file mode 100644 index 0000000..9a5553b --- /dev/null +++ b/dos/stdint.h @@ -0,0 +1,142 @@ +/* + * stdint.h + */ + +#ifndef _STDINT_H +#define _STDINT_H + +/* Exact types */ + +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long long int64_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +/* Small types */ + +typedef signed char int_least8_t; +typedef signed short int_least16_t; +typedef signed int int_least32_t; +typedef signed long long int_least64_t; + +typedef unsigned char uint_least8_t; +typedef unsigned short uint_least16_t; +typedef unsigned int uint_least32_t; +typedef unsigned long long uint_least64_t; + +/* Fast types */ + +typedef signed char int_fast8_t; +typedef signed short int_fast16_t; +typedef signed int int_fast32_t; +typedef signed long long int_fast64_t; + +typedef unsigned char uint_fast8_t; +typedef unsigned short uint_fast16_t; +typedef unsigned int uint_fast32_t; +typedef unsigned long long uint_fast64_t; + +/* Pointer types */ + +typedef int32_t intptr_t; +typedef uint32_t uintptr_t; + +/* Maximal types */ + +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + +/* + * To be strictly correct... + */ +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) + +# define INT8_MIN (-128) +# define INT16_MIN (-32767-1) +# define INT32_MIN (-2147483647-1) +# define INT64_MIN (-9223372036854775807LL-1) + +# define INT8_MAX (127) +# define INT16_MAX (32767) +# define INT32_MAX (2147483647) +# define INT64_MAX (9223372036854775807LL) + +# define UINT8_MAX (255U) +# define UINT16_MAX (65535U) +# define UINT32_MAX (4294967295U) +# define UINT64_MAX (18446744073709551615ULL) + +# define INT_LEAST8_MIN (-128) +# define INT_LEAST16_MIN (-32767-1) +# define INT_LEAST32_MIN (-2147483647-1) +# define INT_LEAST64_MIN (-9223372036854775807LL-1) + +# define INT_LEAST8_MAX (127) +# define INT_LEAST16_MAX (32767) +# define INT_LEAST32_MAX (2147483647) +# define INT_LEAST64_MAX (9223372036854775807LL) + +# define UINT_LEAST8_MAX (255U) +# define UINT_LEAST16_MAX (65535U) +# define UINT_LEAST32_MAX (4294967295U) +# define UINT_LEAST64_MAX (18446744073709551615ULL) + +# define INT_FAST8_MIN (-128) +# define INT_FAST16_MIN (-32767-1) +# define INT_FAST32_MIN (-2147483647-1) +# define INT_FAST64_MIN (-9223372036854775807LL-1) + +# define INT_FAST8_MAX (127) +# define INT_FAST16_MAX (32767) +# define INT_FAST32_MAX (2147483647) +# define INT_FAST64_MAX (9223372036854775807LL) + +# define UINT_FAST8_MAX (255U) +# define UINT_FAST16_MAX (65535U) +# define UINT_FAST32_MAX (4294967295U) +# define UINT_FAST64_MAX (18446744073709551615ULL) + +# define INTPTR_MIN (-2147483647-1) +# define INTPTR_MAX (2147483647) +# define UINTPTR_MAX (4294967295U) + +# define INTMAX_MIN (-9223372036854775807LL-1) +# define INTMAX_MAX (9223372036854775807LL) +# define UINTMAX_MAX (18446744073709551615ULL) + +/* ptrdiff_t limit */ +# define PTRDIFF_MIN (-2147483647-1) +# define PTRDIFF_MAX (2147483647) + +/* sig_atomic_t limit */ +# define SIG_ATOMIC_MIN (-2147483647-1) +# define SIG_ATOMIC_MAX (2147483647) + +/* size_t limit */ +# define SIZE_MAX (4294967295U) + +#endif /* STDC_LIMIT_MACROS */ + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) + +# define INT8_C(n) n +# define INT16_C(n) n +# define INT32_C(n) n +# define INT64_C(n) n ## LL + +# define UINT8_C(n) n ## U +# define UINT16_C(n) n ## U +# define UINT32_C(n) n ## U +# define UINT64_C(n) n ## ULL + +# define INTMAX_C(n) n ## LL +# define UINTMAX_C(n) n ## ULL + +#endif /* STDC_CONSTANT_MACROS */ + +#endif /* _STDINT_H */ diff --git a/dos/stdio.h b/dos/stdio.h new file mode 100644 index 0000000..97988c2 --- /dev/null +++ b/dos/stdio.h @@ -0,0 +1,21 @@ +#ifndef STDIO_H +#define STDIO_H + +#include +#include + +typedef unsigned int off_t; + +int putchar(int); +int puts(const char *); +int sprintf(char * buf, const char *fmt, ...); +int vsprintf(char *buf, const char *fmt, va_list args); +int printf(const char *fmt, ...); + +#define stdin 0 +#define stdout 1 +#define stderr 2 + +#define fprintf(x, y, ...) printf(y, ## __VA_ARGS__) + +#endif /* STDIO_H */ diff --git a/dos/stdlib.h b/dos/stdlib.h new file mode 100644 index 0000000..45b76a5 --- /dev/null +++ b/dos/stdlib.h @@ -0,0 +1,12 @@ +#ifndef STDLIB_H +#define STDLIB_H + +typedef int ssize_t; +typedef unsigned int size_t; + +void __attribute__((noreturn)) exit(int); + +void *malloc(size_t); +void free(void *); + +#endif diff --git a/dos/string.h b/dos/string.h new file mode 100644 index 0000000..44f77da --- /dev/null +++ b/dos/string.h @@ -0,0 +1,23 @@ +/* + * string.h + */ + +#ifndef _STRING_H +#define _STRING_H + +/* Standard routines */ +#define memcpy(a,b,c) __builtin_memcpy(a,b,c) +#define memset(a,b,c) __builtin_memset(a,b,c) +#define strcpy(a,b) __builtin_strcpy(a,b) +#define strlen(a) __builtin_strlen(a) + +/* This only returns true or false */ +static inline int memcmp(const void *__m1, const void *__m2, unsigned int __n) +{ + _Bool rv; + asm volatile("cld ; repe ; cmpsb ; setne %0" + : "=abd" (rv), "+D" (__m1), "+S" (__m2), "+c" (__n)); + return rv; +} + +#endif /* _STRING_H */ diff --git a/dos/syslinux.c b/dos/syslinux.c new file mode 100644 index 0000000..064859f --- /dev/null +++ b/dos/syslinux.c @@ -0,0 +1,680 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1998-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux.c - Linux installer program for SYSLINUX + * + * Hacked up for DOS. + */ + +#include +#include +#include +#include +#include "mystuff.h" + +#include "syslinux.h" +#include "libfat.h" + +const char *program = "syslinux"; /* Name of program */ +uint16_t dos_version; + +#ifdef DEBUG +# define dprintf printf +#else +# define dprintf(...) ((void)0) +#endif + +void __attribute__((noreturn)) usage(void) +{ + puts("Usage: syslinux [-sfmar][-d directory] : [bootsecfile]\n"); + exit(1); +} + +void unlock_device(int); + +void __attribute__((noreturn)) die(const char *msg) +{ + unlock_device(0); + puts("syslinux: "); + puts(msg); + putchar('\n'); + exit(1); +} + +void warning(const char *msg) +{ + puts("syslinux: warning: "); + puts(msg); + putchar('\n'); +} + +/* + * read/write wrapper functions + */ +int creat(const char *filename, int mode) +{ + uint16_t rv; + uint8_t err; + + dprintf("creat(\"%s\", 0x%x)\n", filename, mode); + + rv = 0x3C00; + asm volatile("int $0x21 ; setc %0" + : "=bcdm" (err), "+a" (rv) + : "c" (mode), "d" (filename)); + if ( err ) { + dprintf("rv = %d\n", rv); + die("cannot open ldlinux.sys"); + } + + return rv; +} + +void close(int fd) +{ + uint16_t rv = 0x3E00; + + dprintf("close(%d)\n", fd); + + asm volatile("int $0x21" + : "+a" (rv) + : "b" (fd)); + + /* The only error MS-DOS returns for close is EBADF, + and we really don't care... */ +} + +int rename(const char *oldname, const char *newname) +{ + uint16_t rv = 0x5600; /* Also support 43FFh? */ + uint8_t err; + + dprintf("rename(\"%s\", \"%s\")\n", oldname, newname); + + asm volatile("int $0x21 ; setc %0" + : "=bcdm" (err), "+a" (rv) + : "d" (oldname), "D" (newname)); + + if ( err ) { + dprintf("rv = %d\n", rv); + warning("cannot move ldlinux.sys"); + return rv; + } + + return 0; +} + +ssize_t write_file(int fd, const void *buf, size_t count) +{ + uint16_t rv; + ssize_t done = 0; + uint8_t err; + + dprintf("write_file(%d,%p,%u)\n", fd, buf, count); + + while ( count ) { + rv = 0x4000; + asm volatile("int $0x21 ; setc %0" + : "=abcdm" (err), "+a" (rv) + : "b" (fd), "c" (count), "d" (buf)); + if ( err || rv == 0 ) + die("file write error"); + + done += rv; + count -= rv; + } + + return done; +} + +static inline __attribute__((const)) uint16_t data_segment(void) +{ + uint16_t ds; + + asm("movw %%ds,%0" : "=rm" (ds)); + return ds; +} + +struct diskio { + uint32_t startsector; + uint16_t sectors; + uint16_t bufoffs, bufseg; +} __attribute__((packed)); + +void write_device(int drive, const void *buf, size_t nsecs, unsigned int sector) +{ + uint8_t err; + struct diskio dio; + + dprintf("write_device(%d,%p,%u,%u)\n", drive, buf, nsecs, sector); + + dio.startsector = sector; + dio.sectors = nsecs; + dio.bufoffs = (uintptr_t)buf; + dio.bufseg = data_segment(); + + asm volatile("int $0x26 ; setc %0 ; popfw" + : "=abcdm" (err) + : "a" (drive-1), "b" (&dio), "c" (-1), "d" (buf), "m" (dio)); + + if ( err ) + die("sector write error"); +} + +void read_device(int drive, const void *buf, size_t nsecs, unsigned int sector) +{ + uint8_t err; + struct diskio dio; + + dprintf("read_device(%d,%p,%u,%u)\n", drive, buf, nsecs, sector); + + dio.startsector = sector; + dio.sectors = nsecs; + dio.bufoffs = (uintptr_t)buf; + dio.bufseg = data_segment(); + + asm volatile("int $0x25 ; setc %0 ; popfw" + : "=abcdm" (err) + : "a" (drive-1), "b" (&dio), "c" (-1), "d" (buf), "m" (dio)); + + if ( err ) + die("sector read error"); +} + +/* Both traditional DOS and FAT32 DOS return this structure, but + FAT32 return a lot more data, so make sure we have plenty of space */ +struct deviceparams { + uint8_t specfunc; + uint8_t devtype; + uint16_t devattr; + uint16_t cylinders; + uint8_t mediatype; + uint16_t bytespersec; + uint8_t secperclust; + uint16_t ressectors; + uint8_t fats; + uint16_t rootdirents; + uint16_t sectors; + uint8_t media; + uint16_t fatsecs; + uint16_t secpertrack; + uint16_t heads; + uint32_t hiddensecs; + uint32_t hugesectors; + uint8_t lotsofpadding[224]; +} __attribute__((packed)); + +uint32_t get_partition_offset(int drive) +{ + uint8_t err; + uint16_t rv; + struct deviceparams dp; + + dp.specfunc = 1; /* Get current information */ + + rv = 0x440d; + asm volatile("int $0x21 ; setc %0" + : "=abcdm" (err), "+a" (rv), "=m" (dp) + : "b" (drive), "c" (0x0860), "d" (&dp)); + + if ( !err ) + return dp.hiddensecs; + + rv = 0x440d; + asm volatile("int $0x21 ; setc %0" + : "=abcdm" (err), "+a" (rv), "=m" (dp) + : "b" (drive), "c" (0x4860), "d" (&dp)); + + if ( !err ) + return dp.hiddensecs; + + die("could not find partition start offset"); +} + +struct rwblock { + uint8_t special; + uint16_t head; + uint16_t cylinder; + uint16_t firstsector; + uint16_t sectors; + uint16_t bufferoffset; + uint16_t bufferseg; +} __attribute__((packed)); + +static struct rwblock mbr = { + .special = 0, + .head = 0, + .cylinder = 0, + .firstsector = 0, /* MS-DOS, unlike the BIOS, zero-base sectors */ + .sectors = 1, + .bufferoffset = 0, + .bufferseg = 0 +}; + +void write_mbr(int drive, const void *buf) +{ + uint16_t rv; + uint8_t err; + + dprintf("write_mbr(%d,%p)\n", drive, buf); + + mbr.bufferoffset = (uintptr_t)buf; + mbr.bufferseg = data_segment(); + + rv = 0x440d; + asm volatile("int $0x21 ; setc %0" + : "=abcdm" (err), "+a" (rv) + : "c" (0x0841), "d" (&mbr), "b" (drive), "m" (mbr)); + + if ( !err ) + return; + + rv = 0x440d; + asm volatile("int $0x21 ; setc %0" + : "=abcdm" (err), "+a" (rv) + : "c" (0x4841), "d" (&mbr), "b" (drive), "m" (mbr)); + + if ( err ) + die("mbr write error"); +} + +void read_mbr(int drive, const void *buf) +{ + uint16_t rv; + uint8_t err; + + dprintf("read_mbr(%d,%p)\n", drive, buf); + + mbr.bufferoffset = (uintptr_t)buf; + mbr.bufferseg = data_segment(); + + rv = 0x440d; + asm volatile("int $0x21 ; setc %0" + : "=abcdm" (err), "+a" (rv) + : "c" (0x0861), "d" (&mbr), "b" (drive), "m" (mbr)); + + if ( !err ) + return; + + rv = 0x440d; + asm volatile("int $0x21 ; setc %0" + : "=abcdm" (err), "+a" (rv) + : "c" (0x4861), "d" (&mbr), "b" (drive), "m" (mbr)); + + if ( err ) + die("mbr read error"); +} + +/* This call can legitimately fail, and we don't care, so ignore error return */ +void set_attributes(const char *file, int attributes) +{ + uint16_t rv = 0x4301; + + dprintf("set_attributes(\"%s\", 0x%02x)\n", file, attributes); + + asm volatile("int $0x21" + : "+a" (rv) + : "c" (attributes), "d" (file)); +} + +/* + * Version of the read_device function suitable for libfat + */ +int libfat_xpread(intptr_t pp, void *buf, size_t secsize, libfat_sector_t sector) +{ + read_device(pp, buf, 1, sector); + return secsize; +} + +static inline void get_dos_version(void) +{ + uint16_t ver = 0x3001; + asm("int $0x21 ; xchgb %%ah,%%al" : "+a" (ver) : : "ebx", "ecx"); + dos_version = ver; + dprintf("DOS version %d.%d\n", (dos_version >> 8), dos_version & 0xff); +} + +/* The locking interface relies on static variables. A massive hack :( */ +static uint16_t lock_level; + +static inline void set_lock_device(uint8_t device) +{ + lock_level = device; +} + +void lock_device(int level) +{ + uint16_t rv; + uint8_t err; + uint16_t lock_call; + + if ( dos_version < 0x0700 ) + return; /* Win9x/NT only */ + +#if 0 + /* DOS 7.10 = Win95 OSR2 = first version with FAT32 */ + lock_call = (dos_version >= 0x0710) ? 0x484A : 0x084A; +#else + lock_call = 0x084A; /* MSDN says this is OK for all filesystems */ +#endif + + while ( (lock_level >> 8) < level ) { + uint16_t new_level = lock_level + 0x0100; + dprintf("Trying lock %04x...\n", new_level); + rv = 0x444d; + asm volatile("int $0x21 ; setc %0" + : "=abcdm" (err), "+a" (rv) + : "b" (new_level), "c" (lock_call), "d"(0x0001)); + if ( err ) { + /* rv == 0x0001 means this call is not supported, if so we + assume locking isn't needed (e.g. Win9x in DOS-only mode) */ + if ( rv == 0x0001 ) + return; + else + die("could not lock device"); + } + + lock_level = new_level; + } + return; +} + +void unlock_device(int level) +{ + uint16_t rv; + uint8_t err; + uint16_t unlock_call; + + if ( dos_version < 0x0700 ) + return; /* Win9x/NT only */ + +#if 0 + /* DOS 7.10 = Win95 OSR2 = first version with FAT32 */ + unlock_call = (dos_version >= 0x0710) ? 0x486A : 0x086A; +#else + unlock_call = 0x086A; /* MSDN says this is OK for all filesystems */ +#endif + + while ( (lock_level >> 8) > level ) { + uint16_t new_level = lock_level - 0x0100; + rv = 0x440d; + asm volatile("int $0x21 ; setc %0" + : "=abcdm" (err), "+a" (rv) + : "b" (new_level), "c" (unlock_call)); + lock_level = new_level; + } +} + + +/* + * This function does any desired MBR manipulation; called with the device lock held. + */ +struct mbr_entry { + uint8_t active; /* Active flag */ + uint8_t bhead; /* Begin head */ + uint8_t bsector; /* Begin sector */ + uint8_t bcylinder; /* Begin cylinder */ + uint8_t filesystem; /* Filesystem value */ + uint8_t ehead; /* End head */ + uint8_t esector; /* End sector */ + uint8_t ecylinder; /* End cylinder */ + uint32_t startlba; /* Start sector LBA */ + uint32_t sectors; /* Length in sectors */ +} __attribute__((packed)); + +static void adjust_mbr(int device, int writembr, int set_active) +{ + static unsigned char sectbuf[512]; + int i; + + if ( !writembr && !set_active ) + return; /* Nothing to do */ + + read_mbr(device, sectbuf); + + if ( writembr ) { + memcpy(sectbuf, syslinux_mbr, syslinux_mbr_len); + *(uint16_t *)(sectbuf+510) = 0xaa55; + } + + if ( set_active ) { + uint32_t offset = get_partition_offset(device); + struct mbr_entry *me = (struct mbr_entry *)(sectbuf+446); + int found = 0; + + for ( i = 0 ; i < 4 ; i++ ) { + if ( me->startlba == offset ) { + me->active = 0x80; + found++; + } else { + me->active = 0; + } + me++; + } + + if ( found < 1 ) { + die("partition not found"); + } else if ( found > 1 ) { + die("multiple aliased partitions found"); + } + } + + write_mbr(device, sectbuf); +} + +int main(int argc, char *argv[]) +{ + static unsigned char sectbuf[512]; + int dev_fd, fd; + static char ldlinux_name[] = "@:\\ldlinux.sys"; + char **argp, *opt; + int force = 0; /* -f (force) option */ + struct libfat_filesystem *fs; + libfat_sector_t s, *secp, sectors[65]; /* 65 is maximum possible */ + int32_t ldlinux_cluster; + int nsectors; + const char *device = NULL, *bootsecfile = NULL; + const char *errmsg; + int i; + int writembr = 0; /* -m (write MBR) option */ + int set_active = 0; /* -a (set partition active) option */ + const char *subdir = NULL; + int stupid = 0; + int raid_mode = 0; + + dprintf("argv = %p\n", argv); + for ( i = 0 ; i <= argc ; i++ ) + dprintf("argv[%d] = %p = \"%s\"\n", i, argv[i], argv[i]); + + (void)argc; /* Unused */ + + get_dos_version(); + + for ( argp = argv+1 ; *argp ; argp++ ) { + if ( **argp == '-' ) { + opt = *argp + 1; + if ( !*opt ) + usage(); + + while ( *opt ) { + switch ( *opt ) { + case 's': /* Use "safe, slow and stupid" code */ + stupid = 1; + break; + case 'r': /* RAID mode */ + raid_mode = 1; + break; + case 'f': /* Force install */ + force = 1; + break; + case 'm': /* Write MBR */ + writembr = 1; + break; + case 'a': /* Set partition active */ + set_active = 1; + break; + case 'd': + if ( argp[1] ) + subdir = *++argp; + break; + default: + usage(); + } + opt++; + } + } else { + if ( bootsecfile ) + usage(); + else if ( device ) + bootsecfile = *argp; + else + device = *argp; + } + } + + if ( !device ) + usage(); + + /* + * Figure out which drive we're talking to + */ + dev_fd = (device[0] & ~0x20) - 0x40; + if ( dev_fd < 1 || dev_fd > 26 || device[1] != ':' || device[2] ) + usage(); + + set_lock_device(dev_fd); + + lock_device(2); /* Make sure we can lock the device */ + read_device(dev_fd, sectbuf, 1, 0); + unlock_device(1); + + /* + * Check to see that what we got was indeed an MS-DOS boot sector/superblock + */ + if( (errmsg = syslinux_check_bootsect(sectbuf)) ) { + unlock_device(0); + puts(errmsg); + putchar('\n'); + exit(1); + } + + ldlinux_name[0] = dev_fd | 0x40; + + set_attributes(ldlinux_name, 0); + fd = creat(ldlinux_name, 0x07); /* SYSTEM HIDDEN READONLY */ + write_file(fd, syslinux_ldlinux, syslinux_ldlinux_len); + close(fd); + + /* + * Now, use libfat to create a block map. This probably + * should be changed to use ioctl(...,FIBMAP,...) since + * this is supposed to be a simple, privileged version + * of the installer. + */ + lock_device(2); + fs = libfat_open(libfat_xpread, dev_fd); + ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL); + secp = sectors; + nsectors = 0; + s = libfat_clustertosector(fs, ldlinux_cluster); + while ( s && nsectors < 65 ) { + *secp++ = s; + nsectors++; + s = libfat_nextsector(fs, s); + } + libfat_close(fs); + + /* + * If requested, move ldlinux.sys + */ + if (subdir) { + char new_ldlinux_name[160]; + char *cp = new_ldlinux_name+3; + const char *sd; + int slash = 1; + + new_ldlinux_name[0] = dev_fd | 0x40; + new_ldlinux_name[1] = ':'; + new_ldlinux_name[2] = '\\'; + + for (sd = subdir; *sd; sd++) { + char c = *sd; + + if (c == '/' || c == '\\') { + if (slash) + continue; + c = '\\'; + slash = 1; + } else { + slash = 0; + } + + *cp++ = c; + } + + /* Skip if subdirectory == root */ + if (cp > new_ldlinux_name+3) { + if (!slash) + *cp++ = '\\'; + + memcpy(cp, "ldlinux.sys", 12); + + set_attributes(ldlinux_name, 0); + if (rename(ldlinux_name, new_ldlinux_name)) + set_attributes(ldlinux_name, 0x07); + else + set_attributes(new_ldlinux_name, 0x07); + } + } + + /* + * Patch ldlinux.sys and the boot sector + */ + syslinux_patch(sectors, nsectors, stupid, raid_mode); + + /* + * Write the now-patched first sector of ldlinux.sys + */ + lock_device(3); + write_device(dev_fd, syslinux_ldlinux, 1, sectors[0]); + + /* + * Muck with the MBR, if desired, while we hold the lock + */ + adjust_mbr(dev_fd, writembr, set_active); + + /* + * To finish up, write the boot sector + */ + + /* Read the superblock again since it might have changed while mounted */ + read_device(dev_fd, sectbuf, 1, 0); + + /* Copy the syslinux code into the boot sector */ + syslinux_make_bootsect(sectbuf); + + /* Write new boot sector */ + if ( bootsecfile ) { + unlock_device(0); + fd = creat(bootsecfile, 0x20); /* ARCHIVE */ + write_file(fd, sectbuf, 512); + close(fd); + } else { + write_device(dev_fd, sectbuf, 1, 0); + unlock_device(0); + } + + /* Done! */ + + return 0; +} diff --git a/dos/syslinux.com b/dos/syslinux.com new file mode 100755 index 0000000000000000000000000000000000000000..df2b765a2e49e2253d02e6205dbe7d85635f5cfb GIT binary patch literal 23172 zcmdVC30PFewl-Y5X}TK(x)ns6ur)v+hej~LI3N)R)Hvb9poSqzp@~sXvs?5yfelG- zT8PmkC&_0#PRa40QIdlSVj8d&H4#HdPGaJT!69bjHclX;q5rpbgG27U-*f-_f6wzh zKRoQ3R!wWIT5Hv+DrnBNRtF&uXhda2?t-#yDIslD4T?s(pGMSakN9U3%U^TIXE`*E z3iVWcrVk;AibHxMOr-Ov_&`ZW$+VimP15g#&Y^;xmXc^fY6jB<)Ly_78qad8sqi%| z=fZ2#GdM%LbwYqk1})e|91aK79yv`%iW@HptWF~OO{OXIC2HS-p3OHk@|Y&{Lrdal zALjtXVJuSWPVk*iljwqmMsGG&=SYMqDtu3EB%T4I(=TcUYq#3jBqAzvFY-ECnkcvS zq1^e=>aHHq-=n#B0K981&p$So&%5U$J~5Xb59cCWs1h}%pXI5Pu9UlZ-P<`1aXpJT z2Q9|PagI$uT;J(%Jl=Oy1{Ed*cyx82xhpp%18E(tcu__tt0Yazpw?vpx|~ylqovb< zU@Az!txOsjy+bpy*98nEd|v?04bTa`xha$Rr)lY0c}%@9^$YBwhsHfde=D4;n}-d zgVqZJyfTDo0lhMWxdA>In->Q7(#aSCvZ?v3qb3l{`yp@%w0$SHF%NA3ZGJ#o*BbGm z!j~D;!3PYWWlo-YpYb-9Y8M#r`v0oWphta*a-WwUJl^LM{BW8I-(}F#69OPf7(0-$ z;LqWV@IuEUe(b^cu?I0J^D0f4Nr&93m9?rpTA4vPRHq?TH1DhloCp*upJa?Jk7Nv1 z^4D11QH)?%-9J9YRv!J4DMog+3j8qq14rhY=rTYqY}Ep!)QKcz2UY41}DKe%g<-(abe7%0pW~_@7N*d8D&sNW7E5`yJ#)2QlLLbIZy9@3)7Hc?2 zGO|Y)w0Qz57bJ2S*{eBX;v2pq)GVe&_0qm^ieNcbW;g=!irI*TujNjjV?&lEbtePl z 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 + +/* + * The second extended filesystem constants/structures + */ + +/* + * Define EXT2FS_DEBUG to produce debug messages + */ +#undef EXT2FS_DEBUG + +/* + * Define EXT2_PREALLOCATE to preallocate data blocks for expanding files + */ +#define EXT2_PREALLOCATE +#define EXT2_DEFAULT_PREALLOC_BLOCKS 8 + +/* + * The second extended file system version + */ +#define EXT2FS_DATE "95/08/09" +#define EXT2FS_VERSION "0.5b" + +/* + * Debug code + */ +#ifdef EXT2FS_DEBUG +# define ext2_debug(f, a...) { \ + printk ("EXT2-fs DEBUG (%s, %d): %s:", \ + __FILE__, __LINE__, __FUNCTION__); \ + printk (f, ## a); \ + } +#else +# define ext2_debug(f, a...) /**/ +#endif + +/* + * Special inode numbers + */ +#define EXT2_BAD_INO 1 /* Bad blocks inode */ +#define EXT2_ROOT_INO 2 /* Root inode */ +#define EXT2_ACL_IDX_INO 3 /* ACL inode */ +#define EXT2_ACL_DATA_INO 4 /* ACL inode */ +#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ +#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ + +/* First non-reserved inode for old ext2 filesystems */ +#define EXT2_GOOD_OLD_FIRST_INO 11 + +/* + * The second extended file system magic number + */ +#define EXT2_SUPER_MAGIC 0xEF53 + +/* + * Maximal count of links to a file + */ +#define EXT2_LINK_MAX 32000 + +/* + * Macro-instructions used to manage several block sizes + */ +#define EXT2_MIN_BLOCK_SIZE 1024 +#define EXT2_MAX_BLOCK_SIZE 4096 +#define EXT2_MIN_BLOCK_LOG_SIZE 10 +# define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size) +#define EXT2_ACLE_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_acl_entry)) +#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (__u32)) +# define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) +#define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_INODE_SIZE : \ + (s)->s_inode_size) +#define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ + EXT2_GOOD_OLD_FIRST_INO : \ + (s)->s_first_ino) + +/* + * Macro-instructions used to manage fragments + */ +#define EXT2_MIN_FRAG_SIZE 1024 +#define EXT2_MAX_FRAG_SIZE 4096 +#define EXT2_MIN_FRAG_LOG_SIZE 10 +# define EXT2_FRAG_SIZE(s) (EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size) +# define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s)) + +/* + * ACL structures + */ +struct ext2_acl_header /* Header of Access Control Lists */ +{ + __u32 aclh_size; + __u32 aclh_file_count; + __u32 aclh_acle_count; + __u32 aclh_first_acle; +}; + +struct ext2_acl_entry /* Access Control List Entry */ +{ + __u32 acle_size; + __u16 acle_perms; /* Access permissions */ + __u16 acle_type; /* Type of entry */ + __u16 acle_tag; /* User or group identity */ + __u16 acle_pad1; + __u32 acle_next; /* Pointer on next entry for the */ + /* same inode or on next free entry */ +}; + +/* + * Structure of a blocks group descriptor + */ +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]; +}; + +/* + * Macro-instructions used to manage group descriptors + */ +# define EXT2_BLOCKS_PER_GROUP(s) ((s)->s_blocks_per_group) +# define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc)) +# define EXT2_INODES_PER_GROUP(s) ((s)->s_inodes_per_group) + +/* + * Constants relative to the data blocks + */ +#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) + +/* + * Inode flags + */ +#define EXT2_SECRM_FL 0x00000001 /* Secure deletion */ +#define EXT2_UNRM_FL 0x00000002 /* Undelete */ +#define EXT2_COMPR_FL 0x00000004 /* Compress file */ +#define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */ +#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */ +#define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */ +#define EXT2_NODUMP_FL 0x00000040 /* do not dump file */ +#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */ +/* Reserved for compression usage... */ +#define EXT2_DIRTY_FL 0x00000100 +#define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */ +#define EXT2_NOCOMP_FL 0x00000400 /* Don't compress */ +#define EXT2_ECOMPR_FL 0x00000800 /* Compression error */ +/* End compression flags --- maybe not all used */ +#define EXT2_BTREE_FL 0x00001000 /* btree format dir */ +#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ + +#define EXT2_FL_USER_VISIBLE 0x00001FFF /* User visible flags */ +#define EXT2_FL_USER_MODIFIABLE 0x000000FF /* User modifiable flags */ + +/* + * ioctl commands + */ +#define EXT2_IOC_GETFLAGS _IOR('f', 1, long) +#define EXT2_IOC_SETFLAGS _IOW('f', 2, long) +#define EXT2_IOC_GETVERSION _IOR('v', 1, long) +#define EXT2_IOC_SETVERSION _IOW('v', 2, long) + +/* + * Structure of an inode on the disk + */ +struct ext2_inode { + __u16 i_mode; /* File mode */ + __u16 i_uid; /* Low 16 bits of Owner Uid */ + __u32 i_size; /* Size in bytes */ + __u32 i_atime; /* Access time */ + __u32 i_ctime; /* Creation time */ + __u32 i_mtime; /* Modification time */ + __u32 i_dtime; /* Deletion Time */ + __u16 i_gid; /* Low 16 bits of Group Id */ + __u16 i_links_count; /* Links count */ + __u32 i_blocks; /* Blocks count */ + __u32 i_flags; /* 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];/* Pointers to blocks */ + __u32 i_generation; /* 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; + __u16 l_i_uid_high; /* these 2 fields */ + __u16 l_i_gid_high; /* were reserved2[0] */ + __u32 l_i_reserved2; + } 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 */ +}; + +#define i_size_high i_dir_acl + +#if defined(__KERNEL__) || defined(__linux__) +#define i_reserved1 osd1.linux1.l_i_reserved1 +#define i_frag osd2.linux2.l_i_frag +#define i_fsize osd2.linux2.l_i_fsize +#define i_uid_low i_uid +#define i_gid_low i_gid +#define i_uid_high osd2.linux2.l_i_uid_high +#define i_gid_high osd2.linux2.l_i_gid_high +#define i_reserved2 osd2.linux2.l_i_reserved2 +#endif + +#ifdef __hurd__ +#define i_translator osd1.hurd1.h_i_translator +#define i_frag osd2.hurd2.h_i_frag; +#define i_fsize osd2.hurd2.h_i_fsize; +#define i_uid_high osd2.hurd2.h_i_uid_high +#define i_gid_high osd2.hurd2.h_i_gid_high +#define i_author osd2.hurd2.h_i_author +#endif + +#ifdef __masix__ +#define i_reserved1 osd1.masix1.m_i_reserved1 +#define i_frag osd2.masix2.m_i_frag +#define i_fsize osd2.masix2.m_i_fsize +#define i_reserved2 osd2.masix2.m_i_reserved2 +#endif + +/* + * File system states + */ +#define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */ +#define EXT2_ERROR_FS 0x0002 /* Errors detected */ + +/* + * Mount flags + */ +#define EXT2_MOUNT_CHECK 0x0001 /* Do mount-time checks */ +#define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */ +#define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */ +#define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */ +#define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */ +#define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */ +#define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */ +#define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */ + +#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt +#define set_opt(o, opt) o |= EXT2_MOUNT_##opt +#define test_opt(sb, opt) ((sb)->u.ext2_sb.s_mount_opt & \ + EXT2_MOUNT_##opt) +/* + * Maximal mount counts between two filesystem checks + */ +#define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */ +#define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */ + +/* + * Behaviour when detecting errors + */ +#define EXT2_ERRORS_CONTINUE 1 /* Continue execution */ +#define EXT2_ERRORS_RO 2 /* Remount fs read-only */ +#define EXT2_ERRORS_PANIC 3 /* Panic */ +#define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE + +/* + * Structure of the super block + */ +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_minor_rev_level; /* minor revision level */ + __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 */ + /* + * These fields are for EXT2_DYNAMIC_REV superblocks only. + * + * Note: the difference between the compatible feature set and + * the incompatible feature set is that if there is a bit set + * in the incompatible feature set that the kernel doesn't + * know about, it should refuse to mount the filesystem. + * + * e2fsck's requirements are more strict; if it doesn't know + * about a feature in either the compatible or incompatible + * feature set, it must abort and not try to meddle with + * things it doesn't understand... + */ + __u32 s_first_ino; /* First non-reserved inode */ + __u16 s_inode_size; /* size of inode structure */ + __u16 s_block_group_nr; /* block group # of this superblock */ + __u32 s_feature_compat; /* compatible feature set */ + __u32 s_feature_incompat; /* incompatible feature set */ + __u32 s_feature_ro_compat; /* readonly-compatible feature set */ + __u8 s_uuid[16]; /* 128-bit uuid for volume */ + char s_volume_name[16]; /* volume name */ + char s_last_mounted[64]; /* directory where last mounted */ + __u32 s_algorithm_usage_bitmap; /* For compression */ + /* + * Performance hints. Directory preallocation should only + * happen if the EXT2_COMPAT_PREALLOC flag is on. + */ + __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ + __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ + __u16 s_padding1; + __u32 s_reserved[204]; /* Padding to the end of the block */ +}; + +/* Assume that user mode programs are passing in an ext2fs superblock, not + * a kernel struct super_block. This will allow us to call the feature-test + * macros from user land. */ +#define EXT2_SB(sb) (sb) + +/* + * Codes for operating systems + */ +#define EXT2_OS_LINUX 0 +#define EXT2_OS_HURD 1 +#define EXT2_OS_MASIX 2 +#define EXT2_OS_FREEBSD 3 +#define EXT2_OS_LITES 4 + +/* + * Revision levels + */ +#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */ +#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */ + +#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV +#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV + +#define EXT2_GOOD_OLD_INODE_SIZE 128 + +/* + * Feature set definitions + */ + +#define EXT2_HAS_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_es->s_feature_compat & cpu_to_le32(mask) ) +#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_es->s_feature_ro_compat & cpu_to_le32(mask) ) +#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \ + ( EXT2_SB(sb)->s_es->s_feature_incompat & cpu_to_le32(mask) ) +#define EXT2_SET_COMPAT_FEATURE(sb,mask) \ + EXT2_SB(sb)->s_es->s_feature_compat |= cpu_to_le32(mask) +#define EXT2_SET_RO_COMPAT_FEATURE(sb,mask) \ + EXT2_SB(sb)->s_es->s_feature_ro_compat |= cpu_to_le32(mask) +#define EXT2_SET_INCOMPAT_FEATURE(sb,mask) \ + EXT2_SB(sb)->s_es->s_feature_incompat |= cpu_to_le32(mask) +#define EXT2_CLEAR_COMPAT_FEATURE(sb,mask) \ + EXT2_SB(sb)->s_es->s_feature_compat &= ~cpu_to_le32(mask) +#define EXT2_CLEAR_RO_COMPAT_FEATURE(sb,mask) \ + EXT2_SB(sb)->s_es->s_feature_ro_compat &= ~cpu_to_le32(mask) +#define EXT2_CLEAR_INCOMPAT_FEATURE(sb,mask) \ + EXT2_SB(sb)->s_es->s_feature_incompat &= ~cpu_to_le32(mask) + +#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001 +#define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002 +#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 +#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008 +#define EXT2_FEATURE_COMPAT_RESIZE_INO 0x0010 +#define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020 +#define EXT2_FEATURE_COMPAT_ANY 0xffffffff + +#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 +#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 +#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 +#define EXT2_FEATURE_RO_COMPAT_ANY 0xffffffff + +#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 +#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 +#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 +#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 +#define EXT2_FEATURE_INCOMPAT_ANY 0xffffffff + +#define EXT2_FEATURE_COMPAT_SUPP 0 +#define EXT2_FEATURE_INCOMPAT_SUPP EXT2_FEATURE_INCOMPAT_FILETYPE +#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT2_FEATURE_RO_COMPAT_BTREE_DIR) +#define EXT2_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT2_FEATURE_RO_COMPAT_SUPP +#define EXT2_FEATURE_INCOMPAT_UNSUPPORTED ~EXT2_FEATURE_INCOMPAT_SUPP + +/* + * Default values for user and/or group using reserved blocks + */ +#define EXT2_DEF_RESUID 0 +#define EXT2_DEF_RESGID 0 + +/* + * Structure of a directory entry + */ +#define EXT2_NAME_LEN 255 + +struct ext2_dir_entry { + __u32 inode; /* Inode number */ + __u16 rec_len; /* Directory entry length */ + __u16 name_len; /* Name length */ + char name[EXT2_NAME_LEN]; /* File name */ +}; + +/* + * The new version of the directory entry. Since EXT2 structures are + * stored in intel byte order, and the name_len field could never be + * bigger than 255 chars, it's safe to reclaim the extra byte for the + * file_type field. + */ +struct ext2_dir_entry_2 { + __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 */ +}; + +/* + * Ext2 directory file types. Only the low 3 bits are used. The + * other bits are reserved for now. + */ +enum { + EXT2_FT_UNKNOWN, + EXT2_FT_REG_FILE, + EXT2_FT_DIR, + EXT2_FT_CHRDEV, + EXT2_FT_BLKDEV, + EXT2_FT_FIFO, + EXT2_FT_SOCK, + EXT2_FT_SYMLINK, + EXT2_FT_MAX +}; + +/* + * 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) + + +#endif /* _LINUX_EXT2_FS_H */ diff --git a/extlinux/extlinux b/extlinux/extlinux new file mode 100755 index 0000000000000000000000000000000000000000..d97dd70c55f1005441aea1d9c5f74a97b72a721e GIT binary patch literal 52714 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%0Dl32N0jXoQ<2 zLmJgl2*FL3`<L!uzg10k;R1Sm(vGo^8%T7qkY;dcU?G1-aGHf&Kz+YvAupn^9v{~C zU4VM-QSKA4*RBV5ZYD=wJyaXIl5w-RJfvY3VEumrZ-tTbxdP0v)l4yJ6>`GXATk~J ztp8M^8qfIzVyQ^^F}?XQw02;x7!(1)7z9tE}hKp$%CL96p@j2q;06$FDaev7kv|A{)F zPP70yhzGdR%671I4eCv#QIs@p*$|a zhoc0-d#XgKrsY~17RL9@Kq5C>5^gurhT+4M1iuWS{s}l7|7s}X?)M?wQ-Fs{f<{V0 z`}}Fsvk@f_j~n5WiF`NmBSdPP2PA&vfkSJS zB)pOUDk=$thf2;#0!EZKgh&t}BuKzVH+h^q^yI;Lgoh|X&}vJoN5p!qf)?wwRw~|W zD+M3d_V!=(?^nI{f3;Zet+m#AtG1W=Z0)_?-*3&HeRhu6_PgJI=bNlqv(~Ixvu0-P zy=TvyDY9Y!p>%(FYy3)sZUA}e>F8b_`OCn|yOmWe%=>_$y6hR#tgX~|M@ST+GS49V z7z~_uC}(T#46!-iW1eYNA;yE%rqw6sd%N+=PUtTmfLj}_bq8mBO8b%}Ru`HJS z5#{#kL7HhldpXT= zo7jG;)Z=SXk2~pQ-bdqD8h?cNNY>+aDf&6!<*3KsSsb$;u%Jn7294Y4Yqw89Gu$D( zRzrFFCWG)1dios{{B`L)R^tphk}Y2eJG&69hpiIK8dto5vWONPfb4gXD=w)AjiwQlBT~pDY_~np@rf2hzWh`~z#!P;!LDp3M5Z zV9g^bP*sv!ZYW7?cqKYfAA)tv+B8(CkV#6C{EBtkP;!)lw}Esc$-lB@5WfZS z1H|95el)Z&VSgB;M@jyjr7EpTI?+E^52D%Gl^5R>b|#=7aN_O^`hw#ec^4q%jMj7q z&@UmHM5y`#JIZmslQot#8ed87|6+JZ1oat^%bOOTfNkPTv+z&+3F?Q>KIY6)l0M$?^@bcKhqj(TOFfu z){-W+dL?VCb6LFtecNRDmm(YiBd1Nz5&x1ecg;{nO@EI*!LeV1tWYKnLw`!gf2%e$ zGk%2dCek>&D!yxvSgHtJ;+*s8eD;XlLz(cDWUI3FhWH|4XWrigS<7j;8Cn+Q+`c#L z%tOW_kTppf?QF^1jkM`srK76|k!EM^9ng&w@fs;5jTDulZw0rsLZ#mdQ*d0ivJ8Ij zHRU|N+o}@w$H6{T+OS&iJ^n0XDx<=!qEyJTuIqubmg_;C5buVsZ*7rrfpAwha9}zlF2FEbv7%Ip> z2=+|tw~yhfdIz&z`7BcMY#yXD>RX7)JIXH%*GN@;D!Puf^*@D+C+&=So?N`&=V9hs z-eiX1ae=L~#u|^LJex<>j`{#>=YI*Ahwb5%l4YHL9e4_5J_pEtq!rfFF90dQfGn!u zD6p(K|H`@^reCx2?E8NccINUcgjLHIC8XSn6zRF!QPwk;aBt5lde-hU?! zl0s?xjHUJ5Lp>iw(m2{Z5A+HIYwvazvj9n_n6#2*z-&Awm#jvl3=ve0nGPXh-7d_< zPa`gj%MD|fFhZ3MsxnJdp&IO**Ms&$^xacaEqG6xN&Ei;lE!ep&zd;}=!-xn9AM6w zWDI4T)6it+Jd1eQXr)fyM^X(rlR(dqyU@8WfNE#nxoTp;UGS_3^+-7VezGoW0_)#1 z=dX#@GiL#wpma06hKQBY&4if+-Ar>pE`6JnMrou{5Ugo8KxDw!(nznOsMis!iu;CA zrIFqNAing6iSyF6SliNk{*8FiYirqIh+6W-weJevnI zsU2Vmsp>r64}6H~(GP@SW(H0wgw6w34T8;o5FQQ}}S&*$7FH0}q*@zlVJ@05|n!|jn1MvlRoDIZ8fP`#Zj1cn3e)hH42j9KL{wM zuoyEcSwSN_L3#DX6g1M5h)3C}5QWF(si?pha5-8LauxY9@>poGpn0GRS`r>o!r19h z^)%cp$9Y1$qQ*zSMw!zJOQlY@51B&HDp_iMv8JEuCFM;kG)Z_(5E&4OnVDHSzaXb) z^V7x)^QkC4lb8}X12-{ZEhy9p(+aa(r2#Hfwk%j|mJ5?{rfAB-)QLz!;Y7=`x)7Zb zKp}D}P*&Yzo+f%o0;`rS15+D5AF`b}6)06|UL;SqszlVf;p?SYD)Lkn z@7OX*+@N}Rd>d|EjEv{af;JR*epdd1!nqhpbAk!vn42wfK_L_7m7$}m6OAp?r)fGf zx-ihf*@2dq?JcHj<$Db(gQmcY9!C#~VOf6NH&4o%2V*U(8pc~zaXbeMx7ZIy>ZTYq z9G-e~(Bj#)9FrADix&BgRwvynDdaB`HTRHR+*?}Qm<0exMfq(PRbmy}Z%Z zZk~1&kQT)PprHvOV`x%KvZtam)*DN7x9L?%5kS3mXfu}4M7pSSGKJ;EjN<_eaU6h! zIPUaZHJ?NjwKY+UbFK(7ye-THG9~osMAqJmV)`1#+394g^un7+HeZJ)xT76u=wwi! zA|9`q1q@GOUG0fjrzT+SP_--Ep5EMi01^$Z@69W~ck{2BeO&@O$+jqo1X|5A}=J979J32432HTdfB<-{}>_pN%c84TI5Wm$vg7`bz$a<@Nb7r1XHgR5a=h0`7`iE^tkh({> z-`zG>;t{03E8Mr)>z-=^_8&qyVB<%%<}LvKroj<^aU09{j=eJtxj_T4d>14-upRo-|AfI9CW^O>H5r^ z==y>6cI!U7=8cceU4Hq%C85nF`}be+-kkK&SMhJZ)AhuDdst-C{!0E~LeO5h&aMef zvUXj#V!c!Loo^fs6~g6gzz7Jb3`W7<=7j!teg|V)mK?%w&{JUH;t;=~eTDWL+K0ez za9qk{z#o-0X-4H*bg9}>bg;SWmd{6gF?)B<&N+yd>_ap$7qO8KGPckDiHg|cI&QW% z&UFsVu?zPN+I?@>#ry1@4rf);9<^`KDXYv(viEM^`Q*XpGB02592=a!?8@s_I>%PV z?0CdJZC&TjGZoQW=2fhp)4AXtNWQQGlB?HMG0Kw9S;yFN#CrC{2AhGDa9AWb3P*}Q z`|Ka9bs-0KU4XiT6yIU~s*SUY9Py*oF75!@V(Nb-0(K6Fz#|1Tz!oyRKjHMPz}Z_! zmZg%@0L{@_iw5nr5oF*;177?>Sqf!cdd!+R8h-!$8p5^iJgTUqJwWi)4UteT&s4=200oWNS z-4q$UK2q8il+OwJza+oZlg~+X_8KE!Y*q>dkY;q{T=|^=o(l%;=16HWGWwE8DKt=( zo-EFT-xJVv*(3&k+ajY^h=9)@17Ua^dYjddErQU0 zR_WvCq>qx$iKt*d_u4zOHxl-FI74)4z-C_kVCK=vb<^&~G}eZv@TlXq;SoqB-8i;A zwspnYu0&c(0u(Gp$A=>cVD=d$@ODA|ESWFZaO zR@ERArFzV6r%>>ddXxmgb&J+uK{!OGTrS6Ep4rHPBZaYQdmt7U`Fe4DD>iB=e@VJN zJX5)H#Hc<@)=j1oX)G>>&ytFHh4O4qR2Jv5CSqB2MVeP3SPEy(8Xd7046Ep&2yEoy zEM%m|(>=Ykml(1&*%(X0EOO4I>p~IKwJn)8ZQ;u1(^xz7V9_zaAqA{zSmQP>ZCbj< zT~xakCRn^B6-ze660|h-zPi{vDE6;ws>e~gD%F>2iNi)x9H{ZmKAe@>)#IkQffepq zHZ`yrdRh|LwW&7&=yNgMzTWs2oOy{8TGikk%_iUt3vo_=KQNTQRq^`>fOA$J-_2zTb|3Qjj(&PbV5)p;dUna@IBYX0mtCOW?sC=c>PS1+}?!pT0pD3baSdMj@+tei>nVP zW==WLD5>P2YM|UK5{JSr?Bw-YwiI4hR7X4ev%-hz4m2v;Fv*HwOG=XW_-a><*wvX- zv~+UO@#@RTGT!mD4;utk(T6Nf{?%7Nj=eqfriHCa_7J^&5c zlER7CD&B_;D1L3wSTL)9oGy2o;KW<}xEAY;=FV2n^E7C88A5C(F}sXHEsx5yYz{h$ zbT5u`ZpR=rKCjgucAvXHM_4eJ*6<{7)hn8uU?UDs>eYL8k{G}p+yKL#4i#jh35H;s za*!)I3i>Gx80_EL--#8m zkri8V@vNHNtAoJ+ci6NQBZUJ)_xTkqt?+`<)p@RMYjRbrXC=1r4&Ae4d_Io?_qqi4 z0^!+D%*?Ghw7GVq^win?M(AMu5WB1!FWkNGLAv)2ZFc4bpDWE}_8qY+2A($IOgJ88 zvyIEKR|R7Lu>AmLYA{QKb71N;E$$|{@`BBTln#t}^fEWm2cO%6Hilka3&2C^w$wck zZjIegwc;dpWEB6ZyO!wyOX4_{n2x3w{T;VP)6DF#xZ&yBBmEL?QPODQyg&iYW(zi| zxZqa0X$qv{GQ-H#dyd3I!T>DP>;Pj&qMzb@LvD5*ZZF!6dz{*~Kqu_kN(Op@?>5;b zPy~5J5#RNtL8+=i-*RaaKwk-NCDXpp^C3Zx<)SMo?2as5^4GUV(jNL@!EX!9`{<@f zx&z{br4~w#x`wTN&!C+r{iQd=T^ayd(wM=nEPV-sGu^MdlPkxMvlqC%sFALnGzP20 z4ST#{u3WWpslVfm_0`+)D0~UecNQVOXKGDe6vJ;T_&fDX|7Fi-FCGYVo1lAdO)gL4 zj)vzCV@Unn0&8<{%RMwHErXdRF4fRSr?bh}I-+%Iz~yKaW^_^f+!^oFVg_>)tpb+4 zrDB{6z#U<$q33LPo0MM3?F=jv&ICD@r8{Fy<~ab5ORP4YQsjZkj7e`VAnPjwUn-2g z#!lOf8!xq-S&deqZBbGD#|=tvh8(iNP@E$lAl*&k5j`F{5>$$hD7Y8lR|&RitMs=d zlk&LPlES(QZIvNC+U%}$rKXrz%kHYTBXd<={o-+8@43pq_2ByZ?#_uNJG0tp@L_@H` zIdiE|oebX!A{-T-cW@{?xrlaqqw3XOoN@);BUgJ-FlXbLBx9?{%LqNO%Xqq(dr9h<6vpxmQH- zB|oIn_==dlCL(I71_F{T)nsTyrz21j;aNKF5MChhtR&*gG(AkXT*qZm^M?{2%G-uC zl#2gyF*}p;RpXxU>OGhb)>kjUa|qDtCjfuYQT&&y3;(Y`2nbpQl$nr$`Tku;UjxX= zOQk+SVC4w^sN)VH_m>6DMBZ_dmJ!&8I0_)hWyR4U>I`8`cg3@GYAP3Bsh4PaDnL&e zI^}WM2Gt@=>9|99T;f?fl6SA9Wd!~OdYF8_N3c{gfyY`HEY=g{5Ml`?Sl1GGtekg{ z>n@}{i2wt|3I7=Z4M5@4XnL5C zyalRh500DM6DD#c1m-hg?Vd2>afAYuE|df!o zA#@=F-HgCv_504FAahx`v5=|8>qs9Z=?#0r@Bk*AGGYi2NAP8dHd2DDH&xAs@hFnU zp}v6*^iw3wmZY_Yi|Wi3tE9q&^L5-Id_v;c!A0HyNy`X)$fFZ#Ue25Ai@cX<-Y}sj z%ePQ~?vVZ%0;)vdvHBJwYXbrrsgRKDx`Um(M`RyCf-xWCM};9>A{~r~C(-Xfl)(M7 z`3im&w&fz<6aogzbpTv9jp$tf-;n5Pf&)60fECVHx?N1=jVxmTmiZ(CyCLCII_?lo zaE5k6@;)#0jKDg4x`Q(%I1%d?3d9&;!lxykE+l-{OBbk%D8z)jCnJN#*B6WPc%1<6 z=m0Nr>8lnxXm$J>QyG#bi-vv@wLDEqOoa*0&~b-wpTx7hh`d}c%`r#F4N)AEgqk|obF1no0u1*)mTiy|jc82p12DZPj7|H&^tdoK6@u|=2vGV? zfa5x9^2ESIDs<~7nPM3?^iI$>il7F9t2H@J$N)tMJPGlDgt(DFCC5tbQ(?mKI_?mz zz#^`oRmfW|^o+m;#9{*6CrCUKCiJv`arE_FEOX)d zSTt_7H2*$W1xYLg)jbA4!H9kc5P~d3DSm{G8i`GicCnV20iXylQpyi>l&Vr1QYnzq z5T!s$Lo}<1-(FO^9D#-dSTn&Igpk@O z@XI=x;65*z@H9vRH-Q$s(bQK=>In`L4inb31Rkq@k0JRO0<1j(LKzTc@vS=A2@u!O zB*0xdN(G+M(H{f6sH2|(gr%$oqszY{>F1jIZvg+LqX9-LRWvjY_q?4dNxroy4o}Zw zZ9?1`8Rzyod7=!O6-n}$k)xv=@s$X8Az(7EX$UJ2P)(w|P@cL7A@B&c8fYT|o0o8n zj>~skY7#7vyfL9?1pWu&=rjbslV~%+pLHt1f9q6&Tv-E1dQg%Zegp81j^e*uoALiC z1Sm@C`#SnD0ObVKe-bHPUY4yH{rXbG=jRnsQboLl1KK+75b_7Gf>kH)c%f$mo`_zd z>0v^?Ln416;X*H+@C`4WkQ>ABo01A}!T#0C}W-lM2%vCJ8Rx?+#ME%Z4EvgZqBz1^Mw1Jx!HtAG?ojP?t0DdMI zybT2UrBUkku}&rUlSJ8#^ zG>L$JOL}>L|4zj3LtyuR@oz{+M-nTH1!OC+nP~?9LY!9mTj&$P_nG{WIz7WYOaK3QoOgtg|;AX-#*>+bb~VA0dX%t6eJ6zNHuPG{lUf zPzEp?-`&?c1aU#bS2XhLNPj$~BK#Fw6-jZbI)Y#7?FOk&CjBBY{9+t`Vb#U5LFHrk zaZ34g8)?_ziZ&1-vZV)~5F?#^5QBdak*(!m;g0;GWe+D6Bki%)G=B6g(bC(A8Bh=H zPq%U!+Q+;3rP#2xw&Hu9!7yJMGjY+*ApE%K<-i9>6ZMMmTkfw%CV&N_fPFyC+7WhU z0Vxh-BaPQ&1iWqu-!kxVPfaOsbD92Kg#^Rb1wQ`tPOx(tzFP2o`01zo-7U2ne7nI{ zi8yaWyvWC}5`hDNe2;rEd?ny}+DiivKI-ub0_#h@SHbt{3FJ}cF9Lk8f$uf&U5hkx zk%!?e1S4+)b~SClz$&RRTv`2oha{HIa<9c+qEsigcu;_MjrQPsfRtx0^8Fx?V50iz z%5dRDt6TSblVbR|MZv^L8vx&8b_(w@vgrZ2+Dip$7s6~ z*ad8#x#;(qXnR(sf7iG!nSNa3x&-=bjcY5?FKb*k82zutwF30B8ZRJF^rspZpy)R> z&RU>;K90ExT=av4w%iFYHhf$+6#bf};}seg{g=k^goTTKO5^%zi~dOCdUVk5XxwX1 zj7yCd5h(f*jq5uj{e{L$2*fXF9Q)>Rar|q1oZuYi8rP1O<5}Y;3!URu+hWYLt9k+{Zyz(t;)e$ zf%Cq^GOW;8#UJW3{i_tKw+G1YUF^+Y%)t-j;D>>87cHI6806=hlDU}w-+`O;(M7Pw zn>lp8f-T&S-LBvz{iJ~NV*b;BoAFBeWa8esVdCfJ@Ye%3Yqm*m1>t!`Ykocyza%SLH1jKO$V3f!#u zX8va2+XD6L$jLtdoc_hg=k{;YKSslTdx4vE^^|uvaI=P)_#xojqpb6vtbPRiP@ulg z0q5>!L;nSEb5Z{fbMOK%?(+FhkzLm9f%c42kascvV&MF}33^)AzYe%rLr(oJ&f)I{ zeyumY$Am_y0pNw+_!tu^R)fIJI&bE`4R{0Ua|z@V=a*1(vHb7n;Lid#dlkt4Vh)`z z`YGs38KW`2kLS>_ebm1we*$o`AAs_(eb~Q9$39tQ_D+yKANYfBXXP)+;cw2t`Pq*f z()sYs@v;s04FUUq6?jnJy*c^+J_r9kaI-Ig`utzuW?ek-U*_;12X6L`kp2O1vrmIK zw}zUF^(_JZ0NR6T96x6RH*5XLUkThi4;1lvpe>O~rm)Hu3wm64$(mJ-?uz=RHLeS+ zb_lk7Nr2Se=XS>XT3h0-yw|%e>CKAEi{dz7r9E=axwC2%32t}$W@59Io*HOR_w?Y5 zS6vPcVwGa*aGudp*I+Eh#IvQyB!ZOUE$r0G%6L|gS=@!y=AT(HHFUvlS6>1vu(3)P zi@>@Yk!eYcE^yPlwA_!Ua(@-;zcXmRdsb-yS`@8;uTA2BhH6ldLq%XMYaG8 zNzfY-+}iak7hP1pMAk-eO*}R!bl|L~2Ca#i^&05RJkORuvu7Ky!RZ7!DXW3?M5a&V4KTTlNG4z28o z7RdGW$9jhr^|@=3KclI)%Gh-~d-{5{LD&zL54er7PA(=(EWxIZB&K)Vj()dm6IO}! zwgvUduHJQB{DM>qRvu=_Sa2)V@mK!hmy*bV`lj2v2im*26jy34wZp3G?%r(fN`IB5 ztnW1k31A6)qAw9-^H;8MiJDn|m|dC4ReiFd7yA@qSapf*9>Lt~guU&-6zrtn;^iS0 zr)a+%-Cwi4HMpeBtD$dcP2)m3z2O(#s<$&(T6bStDjrPG3p#@-zA)3lWW6Eh9dS}u zkP&t94d5-nY)SSfFf-W^WJ3j$eJ&0f!eK{f0r4$*ocmSGE&>enO_^|%Qze3}T!kfq zeaRs<Q!H?5bF4lzBVmZYkVwl0kD eAPbyW&Fq>8x%%7OR9Cw9%1G;Gy|YFfp86jX8o`AC literal 0 HcmV?d00001 diff --git a/extlinux/main.c b/extlinux/main.c new file mode 100644 index 0000000..e9226fc --- /dev/null +++ b/extlinux/main.c @@ -0,0 +1,1026 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1998-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * extlinux.c + * + * Install the extlinux boot block on an ext2/3 filesystem + */ + +#define _GNU_SOURCE /* Enable everything */ +#include +/* This is needed to deal with the kernel headers imported into glibc 3.3.3. */ +typedef uint64_t u64; +#include +#include +#include +#include +#include +#ifndef __KLIBC__ +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* Floppy geometry */ +#include /* Hard disk geometry */ +#define statfs _kernel_statfs /* HACK to deal with broken 2.4 distros */ +#include /* FIGETBSZ, FIBMAP */ +#undef statfs + +#include "ext2_fs.h" +#include "../version.h" +#include "syslxint.h" + +#ifdef DEBUG +# define dprintf printf +#else +# define dprintf(...) ((void)0) +#endif + +/* Global option handling */ + +const char *program; + +/* These are the options we can set and their values */ +struct my_options { + unsigned int sectors; + unsigned int heads; + int raid_mode; + int reset_adv; + const char *set_once; +} opt = { + .sectors = 0, + .heads = 0, + .raid_mode = 0, + .reset_adv = 0, + .set_once = NULL, +}; + +static void __attribute__((noreturn)) usage(int rv) +{ + fprintf(stderr, + "Usage: %s [options] directory\n" + " --install -i Install over the current bootsector\n" + " --update -U Update a previous EXTLINUX installation\n" + " --zip -z Force zipdrive geometry (-H 64 -S 32)\n" + " --sectors=# -S Force the number of sectors per track\n" + " --heads=# -H Force number of heads\n" + " --raid -r Fall back to the next device on boot failure\n" + " --once=... -o Execute a command once upon boot\n" + " --clear-once -O Clear the boot-once command\n" + " --reset-adv Reset auxilliary data\n" + "\n" + " Note: geometry is determined at boot time for devices which\n" + " are considered hard disks by the BIOS. Unfortunately, this is\n" + " not possible for devices which are considered floppy disks,\n" + " which includes zipdisks and LS-120 superfloppies.\n" + "\n" + " The -z option is useful for USB devices which are considered\n" + " hard disks by some BIOSes and zipdrives by other BIOSes.\n", + program); + + exit(rv); +} + +enum long_only_opt { + OPT_NONE, + OPT_RESET_ADV, +}; + +static const struct option long_options[] = { + { "install", 0, NULL, 'i' }, + { "update", 0, NULL, 'U' }, + { "zipdrive", 0, NULL, 'z' }, + { "sectors", 1, NULL, 'S' }, + { "heads", 1, NULL, 'H' }, + { "raid-mode", 0, NULL, 'r' }, + { "version", 0, NULL, 'v' }, + { "help", 0, NULL, 'h' }, + { "once", 1, NULL, 'o' }, + { "clear-once", 0, NULL, 'O' }, + { "reset-adv", 0, NULL, OPT_RESET_ADV }, + { 0, 0, 0, 0 } +}; + +static const char short_options[] = "iUuzS:H:rvho:O"; + +#if defined(__linux__) && !defined(BLKGETSIZE64) +/* This takes a u64, but the size field says size_t. Someone screwed big. */ +# define BLKGETSIZE64 _IOR(0x12,114,size_t) +#endif + +#define LDLINUX_MAGIC 0x3eb202fe + +enum bs_offsets { + bsJump = 0x00, + bsOemName = 0x03, + bsBytesPerSec = 0x0b, + bsSecPerClust = 0x0d, + bsResSectors = 0x0e, + bsFATs = 0x10, + bsRootDirEnts = 0x11, + bsSectors = 0x13, + bsMedia = 0x15, + bsFATsecs = 0x16, + bsSecPerTrack = 0x18, + bsHeads = 0x1a, + bsHiddenSecs = 0x1c, + bsHugeSectors = 0x20, + + /* FAT12/16 only */ + bs16DriveNumber = 0x24, + bs16Reserved1 = 0x25, + bs16BootSignature = 0x26, + bs16VolumeID = 0x27, + bs16VolumeLabel = 0x2b, + bs16FileSysType = 0x36, + bs16Code = 0x3e, + + /* FAT32 only */ + bs32FATSz32 = 36, + bs32ExtFlags = 40, + bs32FSVer = 42, + bs32RootClus = 44, + bs32FSInfo = 48, + bs32BkBootSec = 50, + bs32Reserved = 52, + bs32DriveNumber = 64, + bs32Reserved1 = 65, + bs32BootSignature = 66, + bs32VolumeID = 67, + bs32VolumeLabel = 71, + bs32FileSysType = 82, + bs32Code = 90, + + bsSignature = 0x1fe +}; + +#define bsHead bsJump +#define bsHeadLen (bsOemName-bsHead) +#define bsCode bs32Code /* The common safe choice */ +#define bsCodeLen (bsSignature-bs32Code) + +#ifndef EXT2_SUPER_OFFSET +#define EXT2_SUPER_OFFSET 1024 +#endif + +const char *program; + +/* + * Boot block + */ +extern unsigned char extlinux_bootsect[]; +extern unsigned int extlinux_bootsect_len; +#define boot_block extlinux_bootsect +#define boot_block_len extlinux_bootsect_len + +/* + * Image file + */ +extern unsigned char extlinux_image[]; +extern unsigned int extlinux_image_len; +#define boot_image extlinux_image +#define boot_image_len extlinux_image_len + +/* + * Common abort function + */ +void __attribute__((noreturn)) die(const char *msg) +{ + fputs(msg, stderr); + exit(1); +} + +/* + * read/write wrapper functions + */ +ssize_t xpread(int fd, void *buf, size_t count, off_t offset) +{ + char *bufp = (char *)buf; + ssize_t rv; + ssize_t done = 0; + + while ( count ) { + rv = pread(fd, bufp, count, offset); + if ( rv == 0 ) { + die("short read"); + } else if ( rv == -1 ) { + if ( errno == EINTR ) { + continue; + } else { + die(strerror(errno)); + } + } else { + bufp += rv; + offset += rv; + done += rv; + count -= rv; + } + } + + return done; +} + +ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset) +{ + const char *bufp = (const char *)buf; + ssize_t rv; + ssize_t done = 0; + + while ( count ) { + rv = pwrite(fd, bufp, count, offset); + if ( rv == 0 ) { + die("short write"); + } else if ( rv == -1 ) { + if ( errno == EINTR ) { + continue; + } else { + die(strerror(errno)); + } + } else { + bufp += rv; + offset += rv; + done += rv; + count -= rv; + } + } + + return done; +} + +/* + * Produce file map + */ +int +sectmap(int fd, uint32_t *sectors, int nsectors) +{ + unsigned int blksize, blk, nblk; + unsigned int i; + + /* Get block size */ + if ( ioctl(fd, FIGETBSZ, &blksize) ) + return -1; + + /* Number of sectors per block */ + blksize >>= SECTOR_SHIFT; + + nblk = 0; + while ( nsectors ) { + + blk = nblk++; + dprintf("querying block %u\n", blk); + if ( ioctl(fd, FIBMAP, &blk) ) + return -1; + + blk *= blksize; + for ( i = 0 ; i < blksize ; i++ ) { + if ( !nsectors ) + return 0; + + dprintf("Sector: %10u\n", blk); + *sectors++ = blk++; + nsectors--; + } + } + + return 0; +} + +/* + * Get the size of a block device + */ +uint64_t get_size(int devfd) +{ + uint64_t bytes; + uint32_t sects; + struct stat st; + +#ifdef BLKGETSIZE64 + if ( !ioctl(devfd, BLKGETSIZE64, &bytes) ) + return bytes; +#endif + if ( !ioctl(devfd, BLKGETSIZE, §s) ) + return (uint64_t)sects << 9; + else if ( !fstat(devfd, &st) && st.st_size ) + return st.st_size; + else + return 0; +} + + +/* + * Get device geometry and partition offset + */ +struct geometry_table { + uint64_t bytes; + struct hd_geometry g; +}; + +/* Standard floppy disk geometries, plus LS-120. Zipdisk geometry + (x/64/32) is the final fallback. I don't know what LS-240 has + as its geometry, since I don't have one and don't know anyone that does, + and Google wasn't helpful... */ +static const struct geometry_table standard_geometries[] = { + { 360*1024, { 2, 9, 40, 0 } }, + { 720*1024, { 2, 9, 80, 0 } }, + { 1200*1024, { 2, 15, 80, 0 } }, + { 1440*1024, { 2, 18, 80, 0 } }, + { 1680*1024, { 2, 21, 80, 0 } }, + { 1722*1024, { 2, 21, 80, 0 } }, + { 2880*1024, { 2, 36, 80, 0 } }, + { 3840*1024, { 2, 48, 80, 0 } }, + { 123264*1024, { 8, 32, 963, 0 } }, /* LS120 */ + { 0, {0,0,0,0} } +}; + +int +get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo) +{ + struct floppy_struct fd_str; + const struct geometry_table *gp; + + memset(geo, 0, sizeof *geo); + + if ( !ioctl(devfd, HDIO_GETGEO, &geo) ) { + return 0; + } else if ( !ioctl(devfd, FDGETPRM, &fd_str) ) { + geo->heads = fd_str.head; + geo->sectors = fd_str.sect; + geo->cylinders = fd_str.track; + geo->start = 0; + return 0; + } + + /* Didn't work. Let's see if this is one of the standard geometries */ + for ( gp = standard_geometries ; gp->bytes ; gp++ ) { + if ( gp->bytes == totalbytes ) { + memcpy(geo, &gp->g, sizeof *geo); + return 0; + } + } + + /* Didn't work either... assign a geometry of 64 heads, 32 sectors; this is + what zipdisks use, so this would help if someone has a USB key that + they're booting in USB-ZIP mode. */ + + geo->heads = opt.heads ?: 64; + geo->sectors = opt.sectors ?: 32; + geo->cylinders = totalbytes/(geo->heads*geo->sectors << SECTOR_SHIFT); + geo->start = 0; + + if ( !opt.sectors && !opt.heads ) + fprintf(stderr, "Warning: unable to obtain device geometry (defaulting to %d heads, %d sectors)\n" + " (on hard disks, this is usually harmless.)\n", + geo->heads, geo->sectors); + + return 1; +} + +/* + * Query the device geometry and put it into the boot sector. + * Map the file and put the map in the boot sector and file. + * Stick the "current directory" inode number into the file. + */ +void +patch_file_and_bootblock(int fd, int dirfd, int devfd) +{ + struct stat dirst; + struct hd_geometry geo; + uint32_t *sectp; + uint64_t totalbytes, totalsectors; + int nsect; + unsigned char *p, *patcharea; + int i, dw; + uint32_t csum; + + if ( fstat(dirfd, &dirst) ) { + perror("fstat dirfd"); + exit(255); /* This should never happen */ + } + + totalbytes = get_size(devfd); + get_geometry(devfd, totalbytes, &geo); + + if ( opt.heads ) + geo.heads = opt.heads; + if ( opt.sectors ) + geo.sectors = opt.sectors; + + /* Patch this into a fake FAT superblock. This isn't because + FAT is a good format in any way, it's because it lets the + early bootstrap share code with the FAT version. */ + dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors); + + totalsectors = totalbytes >> SECTOR_SHIFT; + if ( totalsectors >= 65536 ) { + set_16(boot_block+bsSectors, 0); + } else { + set_16(boot_block+bsSectors, totalsectors); + } + set_32(boot_block+bsHugeSectors, totalsectors); + + set_16(boot_block+bsBytesPerSec, SECTOR_SIZE); + set_16(boot_block+bsSecPerTrack, geo.sectors); + set_16(boot_block+bsHeads, geo.heads); + set_32(boot_block+bsHiddenSecs, geo.start); + + /* If we're in RAID mode then patch the appropriate instruction; + either way write the proper boot signature */ + i = get_16(boot_block+0x1FE); + if (opt.raid_mode) + set_16(boot_block+i, 0x18CD); /* INT 18h */ + + set_16(boot_block+0x1FE, 0xAA55); + + /* Construct the boot file */ + + dprintf("directory inode = %lu\n", (unsigned long) dirst.st_ino); + nsect = (boot_image_len+SECTOR_SIZE-1) >> SECTOR_SHIFT; + nsect += 2; /* Two sectors for the ADV */ + sectp = alloca(sizeof(uint32_t)*nsect); + if ( sectmap(fd, sectp, nsect) ) { + perror("bmap"); + exit(1); + } + + /* First sector need pointer in boot sector */ + set_32(boot_block+0x1F8, *sectp++); + nsect--; + + /* Search for LDLINUX_MAGIC to find the patch area */ + for ( p = boot_image ; get_32(p) != LDLINUX_MAGIC ; p += 4 ); + patcharea = p+8; + + /* Set up the totals */ + dw = boot_image_len >> 2; /* COMPLETE dwords, excluding ADV */ + set_16(patcharea, dw); + set_16(patcharea+2, nsect); /* Not including the first sector, but + including the ADV */ + set_32(patcharea+8, dirst.st_ino); /* "Current" directory */ + + /* Set the sector pointers */ + p = patcharea+12; + + memset(p, 0, 64*4); + while ( nsect-- ) { + set_32(p, *sectp++); + p += 4; + } + + /* Now produce a checksum */ + set_32(patcharea+4, 0); + + csum = LDLINUX_MAGIC; + for ( i = 0, p = boot_image ; i < dw ; i++, p += 4 ) + csum -= get_32(p); /* Negative checksum */ + + set_32(patcharea+4, csum); +} + +/* + * Read the ADV from an existing instance, or initialize if invalid. + * Returns -1 on fatal errors, 0 if ADV is okay, and 1 if no valid + * ADV was found. + */ +int +read_adv(const char *path) +{ + char *file; + int fd = -1; + struct stat st; + int err = 0; + + asprintf(&file, "%s%sextlinux.sys", + path, + path[0] && path[strlen(path)-1] == '/' ? "" : "/"); + + if ( !file ) { + perror(program); + return -1; + } + + fd = open(file, O_RDONLY); + if ( fd < 0 ) { + if ( errno != ENOENT ) { + err = -1; + } else { + syslinux_reset_adv(syslinux_adv); + } + } else if (fstat(fd, &st)) { + err = -1; + } else if (st.st_size < 2*ADV_SIZE) { + /* Too small to be useful */ + syslinux_reset_adv(syslinux_adv); + err = 0; /* Nothing to read... */ + } else if (xpread(fd, syslinux_adv, 2*ADV_SIZE, + st.st_size-2*ADV_SIZE) != 2*ADV_SIZE) { + err = -1; + } else { + /* We got it... maybe? */ + err = syslinux_validate_adv(syslinux_adv) ? 1 : 0; + } + + if (err < 0) + perror(file); + + if (fd >= 0) + close(fd); + if (file) + free(file); + + return err; +} + +/* + * Update the ADV in an existing installation. + */ +int +write_adv(const char *path) +{ + unsigned char advtmp[2*ADV_SIZE]; + char *file; + int fd = -1; + struct stat st, xst; + int err = 0; + int flags, nflags; + + asprintf(&file, "%s%sextlinux.sys", + path, + path[0] && path[strlen(path)-1] == '/' ? "" : "/"); + + if ( !file ) { + perror(program); + return -1; + } + + fd = open(file, O_RDONLY); + if ( fd < 0 ) { + err = -1; + } else if (fstat(fd, &st)) { + err = -1; + } else if (st.st_size < 2*ADV_SIZE) { + /* Too small to be useful */ + err = -2; + } else if (xpread(fd, advtmp, 2*ADV_SIZE, + st.st_size-2*ADV_SIZE) != 2*ADV_SIZE) { + err = -1; + } else { + /* We got it... maybe? */ + err = syslinux_validate_adv(advtmp) ? -2 : 0; + if (!err) { + /* Got a good one, write our own ADV here */ + if (!ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) { + nflags = flags & ~EXT2_IMMUTABLE_FL; + if (nflags != flags) + ioctl(fd, EXT2_IOC_SETFLAGS, &nflags); + } + if (!(st.st_mode & S_IWUSR)) + fchmod(fd, st.st_mode | S_IWUSR); + + /* Need to re-open read-write */ + close(fd); + fd = open(file, O_RDWR|O_SYNC); + if (fd < 0) { + err = -1; + } else if (fstat(fd, &xst) || xst.st_ino != st.st_ino || + xst.st_dev != st.st_dev || xst.st_size != st.st_size) { + fprintf(stderr, "%s: race condition on write\n", file); + err = -2; + } + /* Write our own version ... */ + if (xpwrite(fd, syslinux_adv, 2*ADV_SIZE, + st.st_size-2*ADV_SIZE) != 2*ADV_SIZE) { + err = -1; + } + + sync(); + + if (!(st.st_mode & S_IWUSR)) + fchmod(fd, st.st_mode); + + if (nflags != flags) + ioctl(fd, EXT2_IOC_SETFLAGS, &flags); + } + } + + if (err == -2) + fprintf(stderr, "%s: cannot write auxilliary data (need --update)?\n", + file); + else if (err == -1) + perror(file); + + if (fd >= 0) + close(fd); + if (file) + free(file); + + return err; +} + +/* + * Make any user-specified ADV modifications + */ +int modify_adv(void) +{ + int rv = 0; + + if (opt.set_once) { + if (syslinux_setadv(ADV_BOOTONCE, strlen(opt.set_once), opt.set_once)) { + fprintf(stderr, "%s: not enough space for boot-once command\n", program); + rv = -1; + } + } + + return rv; +} + +/* + * Install the boot block on the specified device. + * Must be run AFTER install_file()! + */ +int +install_bootblock(int fd, const char *device) +{ + struct ext2_super_block sb; + + if ( xpread(fd, &sb, sizeof sb, EXT2_SUPER_OFFSET) != sizeof sb ) { + perror("reading superblock"); + return 1; + } + + if ( sb.s_magic != EXT2_SUPER_MAGIC ) { + fprintf(stderr, "no ext2/ext3 superblock found on %s\n", device); + return 1; + } + + if ( xpwrite(fd, boot_block, boot_block_len, 0) != boot_block_len ) { + perror("writing bootblock"); + return 1; + } + + return 0; +} + +int +install_file(const char *path, int devfd, struct stat *rst) +{ + char *file; + int fd = -1, dirfd = -1, flags; + struct stat st; + + asprintf(&file, "%s%sextlinux.sys", + path, + path[0] && path[strlen(path)-1] == '/' ? "" : "/"); + if ( !file ) { + perror(program); + return 1; + } + + dirfd = open(path, O_RDONLY|O_DIRECTORY); + if ( dirfd < 0 ) { + perror(path); + goto bail; + } + + fd = open(file, O_RDONLY); + if ( fd < 0 ) { + if ( errno != ENOENT ) { + perror(file); + goto bail; + } + } else { + /* If file exist, remove the immutable flag and set u+w mode */ + if ( !ioctl(fd, EXT2_IOC_GETFLAGS, &flags) ) { + flags &= ~EXT2_IMMUTABLE_FL; + ioctl(fd, EXT2_IOC_SETFLAGS, &flags); + } + if ( !fstat(fd, &st) ) { + fchmod(fd, st.st_mode | S_IWUSR); + } + } + close(fd); + + fd = open(file, O_WRONLY|O_TRUNC|O_CREAT|O_SYNC, S_IRUSR|S_IRGRP|S_IROTH); + if ( fd < 0 ) { + perror(file); + goto bail; + } + + /* Write it the first time */ + if ( xpwrite(fd, boot_image, boot_image_len, 0) != boot_image_len || + xpwrite(fd, syslinux_adv, 2*ADV_SIZE, boot_image_len) != 2*ADV_SIZE ) { + fprintf(stderr, "%s: write failure on %s\n", program, file); + goto bail; + } + + /* Map the file, and patch the initial sector accordingly */ + patch_file_and_bootblock(fd, dirfd, devfd); + + /* Write the first sector again - this relies on the file being + overwritten in place! */ + if ( xpwrite(fd, boot_image, SECTOR_SIZE, 0) != SECTOR_SIZE ) { + fprintf(stderr, "%s: write failure on %s\n", program, file); + goto bail; + } + + /* Attempt to set immutable flag and remove all write access */ + /* Only set immutable flag if file is owned by root */ + if ( !fstat(fd, &st) ) { + fchmod(fd, st.st_mode & (S_IRUSR|S_IRGRP|S_IROTH)); + if ( st.st_uid == 0 && !ioctl(fd, EXT2_IOC_GETFLAGS, &flags) ) { + flags |= EXT2_IMMUTABLE_FL; + ioctl(fd, EXT2_IOC_SETFLAGS, &flags); + } + } + + if ( fstat(fd, rst) ) { + perror(file); + goto bail; + } + + close(dirfd); + close(fd); + return 0; + + bail: + if ( dirfd >= 0 ) + close(dirfd); + if ( fd >= 0 ) + close(fd); + + return 1; +} + +/* EXTLINUX installs the string 'EXTLINUX' at offset 3 in the boot + sector; this is consistent with FAT filesystems. */ +int +already_installed(int devfd) +{ + char buffer[8]; + + xpread(devfd, buffer, 8, 3); + return !memcmp(buffer, "EXTLINUX", 8); +} + + +#ifdef __KLIBC__ +static char devname_buf[64]; + +static void device_cleanup(void) +{ + unlink(devname_buf); +} +#endif + +/* Verify that a device fd and a pathname agree. + Return 0 on valid, -1 on error. */ +static int validate_device(const char *path, int devfd) +{ + struct stat pst, dst; + + if (stat(path, &pst) || fstat(devfd, &dst)) + return -1; + + return (pst.st_dev == dst.st_rdev) ? 0 : -1; +} + +#ifndef __KLIBC__ +static const char *find_device(const char *mtab_file, dev_t dev) +{ + struct mntent *mnt; + struct stat dst; + FILE *mtab; + const char *devname = NULL; + + mtab = setmntent(mtab_file, "r"); + if (!mtab) + return NULL; + + while ( (mnt = getmntent(mtab)) ) { + if ( (!strcmp(mnt->mnt_type, "ext2") || + !strcmp(mnt->mnt_type, "ext3")) && + !stat(mnt->mnt_fsname, &dst) && dst.st_rdev == dev ) { + devname = strdup(mnt->mnt_fsname); + break; + } + } + endmntent(mtab); + + return devname; +} +#endif + +int +install_loader(const char *path, int update_only) +{ + struct stat st, fst; + int devfd, rv; + const char *devname = NULL; + struct statfs sfs; + + if ( stat(path, &st) || !S_ISDIR(st.st_mode) ) { + fprintf(stderr, "%s: Not a directory: %s\n", program, path); + return 1; + } + + if ( statfs(path, &sfs) ) { + fprintf(stderr, "%s: statfs %s: %s\n", program, path, strerror(errno)); + return 1; + } + + if ( sfs.f_type != EXT2_SUPER_MAGIC ) { + fprintf(stderr, "%s: not an ext2/ext3 filesystem: %s\n", program, path); + return 1; + } + + devfd = -1; + +#ifdef __KLIBC__ + + /* klibc doesn't have getmntent and friends; instead, just create + a new device with the appropriate device type */ + snprintf(devname_buf, sizeof devname_buf, "/tmp/dev-%u:%u", + major(st.st_dev), minor(st.st_dev)); + + if (mknod(devname_buf, S_IFBLK|0600, st.st_dev)) { + fprintf(stderr, "%s: cannot create device %s\n", program, devname); + return 1; + } + + atexit(device_cleanup); /* unlink the device node on exit */ + devname = devname_buf; + +#else + + devname = find_device("/proc/mounts", st.st_dev); + if (!devname) { + /* Didn't find it in /proc/mounts, try /etc/mtab */ + devname = find_device("/etc/mtab", st.st_dev); + } + if (!devname) { + fprintf(stderr, "%s: cannot find device for path %s\n", program, path); + return 1; + } + + fprintf(stderr, "%s is device %s\n", path, devname); +#endif + + if ( (devfd = open(devname, O_RDWR|O_SYNC)) < 0 ) { + fprintf(stderr, "%s: cannot open device %s\n", program, devname); + return 1; + } + + /* Verify that the device we opened is the device intended */ + if (validate_device(path, devfd)) { + fprintf(stderr, "%s: path %s doesn't match device %s\n", + program, path, devname); + return 1; + } + + if ( update_only && !already_installed(devfd) ) { + fprintf(stderr, "%s: no previous extlinux boot sector found\n", program); + return 1; + } + + /* Read a pre-existing ADV, if already installed */ + if (opt.reset_adv) + syslinux_reset_adv(syslinux_adv); + else if (read_adv(path) < 0) + return 1; + + if (modify_adv() < 0) + return 1; + + /* Install extlinux.sys */ + if (install_file(path, devfd, &fst)) + return 1; + + if ( fst.st_dev != st.st_dev ) { + fprintf(stderr, "%s: file system changed under us - aborting!\n", + program); + return 1; + } + + sync(); + rv = install_bootblock(devfd, devname); + close(devfd); + sync(); + + return rv; +} + +/* + * Modify the ADV of an existing installation + */ +int +modify_existing_adv(const char *path) +{ + if (opt.reset_adv) + syslinux_reset_adv(syslinux_adv); + else if (read_adv(path) < 0) + return 1; + + if (modify_adv() < 0) + return 1; + + if (write_adv(path) < 0) + return 1; + + return 0; +} + +int +main(int argc, char *argv[]) +{ + int o; + const char *directory; + int update_only = -1; + + program = argv[0]; + + while ( (o = getopt_long(argc, argv, short_options, + long_options, NULL)) != EOF ) { + switch ( o ) { + case 'z': + opt.heads = 64; + opt.sectors = 32; + break; + case 'S': + opt.sectors = strtoul(optarg, NULL, 0); + if ( opt.sectors < 1 || opt.sectors > 63 ) { + fprintf(stderr, "%s: invalid number of sectors: %u (must be 1-63)\n", + program, opt.sectors); + exit(EX_USAGE); + } + break; + case 'H': + opt.heads = strtoul(optarg, NULL, 0); + if ( opt.heads < 1 || opt.heads > 256 ) { + fprintf(stderr, "%s: invalid number of heads: %u (must be 1-256)\n", + program, opt.heads); + exit(EX_USAGE); + } + break; + case 'r': + opt.raid_mode = 1; + break; + case 'i': + update_only = 0; + break; + case 'u': + case 'U': + update_only = 1; + break; + case 'h': + usage(0); + break; + case 'o': + opt.set_once = optarg; + break; + case 'O': + opt.set_once = ""; + break; + case OPT_RESET_ADV: + opt.reset_adv = 1; + break; + case 'v': + fputs("extlinux " VERSION_STR + " Copyright 1994-" YEAR_STR " H. Peter Anvin \n", stderr); + exit(0); + default: + usage(EX_USAGE); + } + } + + directory = argv[optind]; + + if ( !directory ) + usage(EX_USAGE); + + if ( update_only == -1 ) { + if (opt.reset_adv || opt.set_once) { + return modify_existing_adv(directory); + } else { + usage(EX_USAGE); + } + } + + return install_loader(directory, update_only); +} diff --git a/gen-id.sh b/gen-id.sh new file mode 100755 index 0000000..1b3e108 --- /dev/null +++ b/gen-id.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# +# Create a build ID for this build. If we're using a git tree, +# generate an ID from "git describe", otherwise use the passed-in +# timestamp. +# +# Usage: gen-id.sh version timestamp +# + +ver="$1" +tim="$1" + +if test -n "$GIT_DIR" -o -d ../.git -o -f ../.git; then + id="$(git describe)" + if test -n "$id"; then + if test x"$(echo "$id" | cut -d- -f1)" = xsyslinux; then + id="$(echo "$id" | cut -d- -f2-)" + if test x"$(echo "$id" | cut -d- -f1)" = x"$ver"; then + id="$(echo "$id" | cut -d- -f2-)" + fi + fi + fi + if test -n "$id"; then + if test -n "$(git diff-index --name-only HEAD)"; then + id="${id}"\* + fi + fi +fi +if test -z "$id"; then + id="$tim" +fi +echo "$id" diff --git a/gpxe/.gitignore b/gpxe/.gitignore new file mode 100644 index 0000000..0517ab7 --- /dev/null +++ b/gpxe/.gitignore @@ -0,0 +1 @@ +/contrib diff --git a/gpxe/COPYING b/gpxe/COPYING new file mode 100644 index 0000000..a43ea21 --- /dev/null +++ b/gpxe/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/gpxe/COPYRIGHTS b/gpxe/COPYRIGHTS new file mode 100644 index 0000000..d686b43 --- /dev/null +++ b/gpxe/COPYRIGHTS @@ -0,0 +1,26 @@ + +In general gPXE files are licensed under the GPL. GPLed files are in +general either from Linux or have been explicitly put under GPL by the +authors. The license for a file is usually documented at the top of +the file. + +A few files are inherited from FreeBSD netboot and therefore can be +used under BSD or GPL. Documented in this file are some of the +non-GPL'ed files. If the internal documentation for a file disagrees +with what is documented in this file, the internal documentation for +the file shall be override this file. + +File Copyright status + +src/core/misc.c BSD +src/drivers/net/3c509.c BSD +src/drivers/net/3c509.h BSD +src/drivers/net/3c595.c BSD +src/drivers/net/3c595.h BSD +src/drivers/net/3c90x.c Open Source +src/drivers/net/epic100.c None +src/drivers/net/epic100.h None +src/drivers/net/ns8390.c BSD +src/drivers/net/ns8390.h BSD +src/arch/i386/include/bits/string.h None + diff --git a/gpxe/LOG b/gpxe/LOG new file mode 100644 index 0000000..d29c8eb --- /dev/null +++ b/gpxe/LOG @@ -0,0 +1,12 @@ +gPXE LOG file + ++ Development for gPXE was moved from SourceForge CVS to a git + repository on git.etherboot.org in the first half of the year 2007. + ++ The gitweb interface for gPXE is available at: + + http://git.etherboot.org/?p=gpxe.git + ++ The gitweb interface largely obsoletes the LOG file that used to be + distributed with Etherboot. + diff --git a/gpxe/Makefile b/gpxe/Makefile new file mode 100644 index 0000000..b079d53 --- /dev/null +++ b/gpxe/Makefile @@ -0,0 +1,41 @@ +## ----------------------------------------------------------------------- +## +## Copyright 2008-2009 H. Peter Anvin - All Rights Reserved +## Copyright 2009 Intel Corporation; author: H. Peter Anvin +## +## 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, Inc., 51 Franklin St, Fifth Floor, +## Boston MA 02110-1301, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +# +# Makefile for gpxe/gpxelinux.0 +# +# Very simple, really... +# + +TARGETS = gpxelinux.0 + +all: $(TARGETS) + +tidy: + +clean: tidy + +dist: + $(MAKE) -C src veryclean > /dev/null 2>&1 + +spotless: clean dist + rm -f $(TARGETS) + +installer: + +src/bin/undionly.kkpxe: pxelinux.gpxe ../core/pxelinux.0 + $(MAKE) -C src bin/undionly.kkpxe NO_WERROR=1 \ + EMBEDDED_IMAGE=../pxelinux.gpxe,../../core/pxelinux.0 + +gpxelinux.0: src/bin/undionly.kkpxe + cp -f $< $@ diff --git a/gpxe/README b/gpxe/README new file mode 100644 index 0000000..b3ae215 --- /dev/null +++ b/gpxe/README @@ -0,0 +1,53 @@ +gPXE README File + +gPXE is an implementation of the PXE specification for network +booting, with extensions to allow additional features such as booting +via HTTP, iSCSI, and AoE. + +In generally, gPXE is compatible with the industry-standard PXE +specification, and also supports Etherboot .nbi file loading and some +additional protocols and features. + +For more detailed information about gPXE, please visit our project +website at: http://etherboot.org/ + +BUILDING gPXE IMAGE FROM SOURCE + +If you don't want to install development tools, and have access to the +Web, you can get gPXE and Etherboot ROM images made on demand from +http://rom-o-matic.net/ + +If you would like to compile gPXE images from source, here are some tips. + +We normally compile gPXE images on x86, 32-bit Linux machines. It is +possible to also use x86-64 machines. We use gcc compiler options to +create 32-bit output. + +It is important to have the necessary software packages installed. A gcc-based +toolchain is required. + +The following packages (at least) are required: + + - a gcc tool chain (gcc 3.x or gcc 4.x) + - binutils + - perl + - syslinux + - mtools + +To test your environment, cd to the "src" directory and type: + + make + +You should see a lot of output, and when it stops, the "bin" directory +should be populated with gPXE images and object files. + +To learn more about what to build and how to use gPXE, please visit our +project website at http://etherboot.org/ , particularly the "howto" section. + +CONTACTING US + +Pointers to our project mailing lists are on http://etherboot.org/ + +Real-time help is often available on IRC on the #etherboot channel of +irc.freenode.net. + diff --git a/gpxe/VERSION b/gpxe/VERSION new file mode 100644 index 0000000..69b5110 --- /dev/null +++ b/gpxe/VERSION @@ -0,0 +1 @@ +0.9.6+ 2008-11-23 diff --git a/gpxe/gpxelinux.0 b/gpxe/gpxelinux.0 new file mode 100644 index 0000000000000000000000000000000000000000..abaab0a0688d9976e514cf64a03433e4c048e570 GIT binary patch literal 76161 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 P5dikt@8;jpf#m%UKloIX literal 0 HcmV?d00001 diff --git a/gpxe/pxelinux.gpxe b/gpxe/pxelinux.gpxe new file mode 100644 index 0000000..51fe222 --- /dev/null +++ b/gpxe/pxelinux.gpxe @@ -0,0 +1,4 @@ +#!gpxe +dhcp net0 +imgload pxelinux.0 +boot pxelinux.0 diff --git a/gpxe/src/.gitignore b/gpxe/src/.gitignore new file mode 100644 index 0000000..413f814 --- /dev/null +++ b/gpxe/src/.gitignore @@ -0,0 +1,5 @@ +.toolcheck +.echocheck +TAGS* +bin* +config-local.h diff --git a/gpxe/src/Makefile b/gpxe/src/Makefile new file mode 100644 index 0000000..a627d96 --- /dev/null +++ b/gpxe/src/Makefile @@ -0,0 +1,138 @@ +############################################################################### +# +# Initialise various variables +# + +CLEANUP := +CFLAGS := +ASFLAGS := +LDFLAGS := +MAKEDEPS := Makefile + +############################################################################### +# +# Locations of tools +# +HOST_CC := gcc +RM := rm -f +TOUCH := touch +MKDIR := mkdir +CP := cp +ECHO := echo +PRINTF := printf +PERL := /usr/bin/perl +CC := $(CROSS_COMPILE)gcc +CPP := $(CROSS_COMPILE)gcc -E -Wp,-Wall +AS := $(CROSS_COMPILE)as +LD := $(CROSS_COMPILE)ld +SIZE := $(CROSS_COMPILE)size +AR := $(CROSS_COMPILE)ar +RANLIB := $(CROSS_COMPILE)ranlib +OBJCOPY := $(CROSS_COMPILE)objcopy +NM := $(CROSS_COMPILE)nm +OBJDUMP := $(CROSS_COMPILE)objdump +PARSEROM := $(PERL) ./util/parserom.pl +MAKEROM := $(PERL) ./util/makerom.pl +SYMCHECK := $(PERL) ./util/symcheck.pl +SORTOBJDUMP := $(PERL) ./util/sortobjdump.pl +NRV2B := ./util/nrv2b +ZBIN := ./util/zbin +ELF2EFI32 := ./util/elf2efi32 +ELF2EFI64 := ./util/elf2efi64 +EFIROM := ./util/efirom +DOXYGEN := doxygen + +############################################################################### +# +# SRCDIRS lists all directories containing source files. +# +SRCDIRS := +SRCDIRS += libgcc +SRCDIRS += core +SRCDIRS += proto +SRCDIRS += net net/tcp net/udp +SRCDIRS += image +SRCDIRS += drivers/bus +SRCDIRS += drivers/net +SRCDIRS += drivers/net/e1000 +SRCDIRS += drivers/net/phantom +SRCDIRS += drivers/block +SRCDIRS += drivers/nvs +SRCDIRS += drivers/bitbash +SRCDIRS += drivers/infiniband +SRCDIRS += interface/pxe interface/efi interface/smbios +SRCDIRS += tests +SRCDIRS += crypto crypto/axtls crypto/matrixssl +SRCDIRS += hci hci/commands hci/tui +SRCDIRS += hci/mucurses hci/mucurses/widgets +SRCDIRS += usr + +# NON_AUTO_SRCS lists files that are excluded from the normal +# automatic build system. +# +NON_AUTO_SRCS := +NON_AUTO_SRCS += drivers/net/prism2.c + +############################################################################### +# +# Default build target: build the most common targets and print out a +# helpfully suggestive message +# +all : bin/blib.a bin/gpxe.dsk bin/gpxe.iso bin/gpxe.usb bin/undionly.kpxe + @$(ECHO) '===========================================================' + @$(ECHO) + @$(ECHO) 'To create a bootable floppy, type' + @$(ECHO) ' cat bin/gpxe.dsk > /dev/fd0' + @$(ECHO) 'where /dev/fd0 is your floppy drive. This will erase any' + @$(ECHO) 'data already on the disk.' + @$(ECHO) + @$(ECHO) 'To create a bootable USB key, type' + @$(ECHO) ' cat bin/gpxe.usb > /dev/sdX' + @$(ECHO) 'where /dev/sdX is your USB key, and is *not* a real hard' + @$(ECHO) 'disk on your system. This will erase any data already on' + @$(ECHO) 'the USB key.' + @$(ECHO) + @$(ECHO) 'To create a bootable CD-ROM, burn the ISO image ' + @$(ECHO) 'bin/gpxe.iso to a blank CD-ROM.' + @$(ECHO) + @$(ECHO) 'These images contain drivers for all supported cards. You' + @$(ECHO) 'can build more customised images, and ROM images, using' + @$(ECHO) ' make bin/.' + @$(ECHO) + @$(ECHO) '===========================================================' + +############################################################################### +# +# Build targets that do nothing but might be tried by users +# +configure : + @$(ECHO) "No configuration needed." + +install : + @$(ECHO) "No installation required." + +############################################################################### +# +# Version number calculations +# +VERSION_MAJOR = 0 +VERSION_MINOR = 9 +VERSION_PATCH = 7 +EXTRAVERSION = +MM_VERSION = $(VERSION_MAJOR).$(VERSION_MINOR) +VERSION = $(MM_VERSION).$(VERSION_PATCH)$(EXTRAVERSION) +CFLAGS += -DVERSION_MAJOR=$(VERSION_MAJOR) \ + -DVERSION_MINOR=$(VERSION_MINOR) \ + -DVERSION_PATCH=$(VERSION_PATCH) \ + -DVERSION=\"$(VERSION)\" +IDENT = '$(@F) $(VERSION) (GPL) etherboot.org' +version : + @$(ECHO) $(VERSION) + +############################################################################### +# +# Drag in the bulk of the build system +# + +MAKEDEPS += Makefile.housekeeping +include Makefile.housekeeping diff --git a/gpxe/src/Makefile.housekeeping b/gpxe/src/Makefile.housekeeping new file mode 100644 index 0000000..2ab842e --- /dev/null +++ b/gpxe/src/Makefile.housekeeping @@ -0,0 +1,858 @@ +# -*- makefile -*- : Force emacs to use Makefile mode +# +# This file contains various boring housekeeping functions that would +# otherwise seriously clutter up the main Makefile. + +############################################################################### +# +# Find a usable "echo -e" substitute. +# +TAB := $(shell $(PRINTF) '\t') +ECHO_E_ECHO := $(ECHO) +ECHO_E_ECHO_E := $(ECHO) -e +ECHO_E_BIN_ECHO := /bin/echo +ECHO_E_BIN_ECHO_E := /bin/echo -e +ECHO_E_ECHO_TAB := $(shell $(ECHO_E_ECHO) '\t' | cat) +ECHO_E_ECHO_E_TAB := $(shell $(ECHO_E_ECHO_E) '\t' | cat) +ECHO_E_BIN_ECHO_TAB := $(shell $(ECHO_E_BIN_ECHO) '\t') +ECHO_E_BIN_ECHO_E_TAB := $(shell $(ECHO_E_BIN_ECHO_E) '\t') + +ifeq ($(ECHO_E_ECHO_TAB),$(TAB)) +ECHO_E := $(ECHO_E_ECHO) +endif +ifeq ($(ECHO_E_ECHO_E_TAB),$(TAB)) +ECHO_E := $(ECHO_E_ECHO_E) +endif +ifeq ($(ECHO_E_BIN_ECHO_TAB),$(TAB)) +ECHO_E := $(ECHO_E_BIN_ECHO) +endif +ifeq ($(ECHO_E_BIN_ECHO_E_TAB),$(TAB)) +ECHO_E := $(ECHO_E_BIN_ECHO_E) +endif + +.echocheck : +ifdef ECHO_E + @$(TOUCH) $@ +else + @$(PRINTF) '%24s : x%sx\n' 'tab' '$(TAB)' + @$(PRINTF) '%24s : x%sx\n' '"$(ECHO_E_ECHO) \t"' \ + '$(ECHO_E_ECHO_TAB)' + @$(PRINTF) '%24s : x%sx\n' '"$(ECHO_E_ECHO_E) \t"' \ + '$(ECHO_E_ECHO_E_TAB)' + @$(PRINTF) '%24s : x%sx\n' '"$(ECHO_E_BIN_ECHO) \t"' \ + '$(ECHO_E_BIN_ECHO_TAB)' + @$(PRINTF) '%24s : x%sx\n' '"$(ECHO_E_BIN_ECHO_E) \t"' \ + '$(ECHO_E_BIN_ECHO_E_TAB)' + @$(ECHO) "No usable \"echo -e\" substitute found" + @exit 1 +endif +MAKEDEPS += .echocheck +VERYCLEANUP += .echocheck + +echo : + @$(ECHO) "Using \"$(ECHO_E)\" for \"echo -e\"" + +############################################################################### +# +# Determine host OS +# +HOST_OS := $(shell uname -s) +hostos : + @$(ECHO) $(HOST_OS) + +############################################################################### +# +# Check for tools that can cause failed builds +# +.toolcheck : + @if $(CC) -v 2>&1 | grep -is 'gcc version 2\.96' > /dev/null; then \ + $(ECHO) 'gcc 2.96 is unsuitable for compiling Etherboot'; \ + $(ECHO) 'Use gcc 2.95 or gcc 3.x instead'; \ + exit 1; \ + fi + @if [ `perl -e 'use bytes; print chr(255)' | wc -c` = 2 ]; then \ + $(ECHO) 'Your Perl version has a Unicode handling bug'; \ + $(ECHO) 'Execute this command before compiling Etherboot:'; \ + $(ECHO) 'export LANG=$${LANG%.UTF-8}'; \ + exit 1; \ + fi + @$(TOUCH) $@ +MAKEDEPS += .toolcheck +VERYCLEANUP += .toolcheck + +############################################################################### +# +# Check for various tool workarounds +# + +# Make syntax does not allow use of comma or space in certain places. +# This ugly workaround is suggested in the manual. +# +COMMA := , +EMPTY := +SPACE := $(EMPTY) $(EMPTY) + +# Check for an old version of gas (binutils 2.9.1) +# +OLDGAS := $(shell $(AS) --version | grep -q '2\.9\.1' && $(ECHO) -DGAS291) +CFLAGS += $(OLDGAS) +oldgas : + @$(ECHO) $(oldgas) + +# Some widespread patched versions of gcc include -fstack-protector by +# default, even when -ffreestanding is specified. We therefore need +# to disable -fstack-protector if the compiler supports it. +# +SP_TEST = $(CC) -fno-stack-protector -x c -c /dev/null \ + -o /dev/null >/dev/null 2>&1 +SP_FLAGS := $(shell $(SP_TEST) && $(ECHO) '-fno-stack-protector') +CFLAGS += $(SP_FLAGS) + +############################################################################### +# +# Build verbosity +# +ifeq ($(V),1) +Q := +QM := @\# +else +Q := @ +QM := @ +endif + +############################################################################### +# +# Set BIN according to whatever was specified on the command line as +# the build target. +# + +# Determine how many different BIN directories are mentioned in the +# make goals. +# +BIN_GOALS := $(filter bin/% bin-%,$(MAKECMDGOALS)) +BIN_GOAL_BINS := $(foreach BG,$(BIN_GOALS),$(firstword $(subst /, ,$(BG)))) +NUM_BINS := $(words $(sort $(BIN_GOAL_BINS))) + +ifeq ($(NUM_BINS),0) + +# No BIN directory was specified. Set BIN to "bin" as a sensible +# default. + +BIN := bin + +else # NUM_BINS == 0 + +ifeq ($(NUM_BINS),1) + +# If exactly one BIN directory was specified, set BIN to match this +# directory. +# +BIN := $(firstword $(BIN_GOAL_BINS)) + +else # NUM_BINS == 1 + +# More than one BIN directory was specified. We cannot handle the +# latter case within a single make invocation, so set up recursive +# targets for each BIN directory. +# +# Leave $(BIN) undefined. This has implications for any target that +# depends on $(BIN); such targets should be made conditional upon the +# existence of $(BIN). +# +$(BIN_GOALS) : % : BIN_RECURSE + $(Q)$(MAKE) --no-print-directory BIN=$(firstword $(subst /, ,$@)) $@ +.PHONY : BIN_RECURSE + +endif # NUM_BINS == 1 +endif # NUM_BINS == 0 + +ifdef BIN + +# Create $(BIN) directory if it doesn't exist yet +# +ifeq ($(wildcard $(BIN)),) +$(shell $(MKDIR) -p $(BIN)) +endif + +# Target to allow e.g. "make bin-efi arch" +# +$(BIN) : + @# Do nothing, silently +.PHONY : $(BIN) + +# Remove everything in $(BIN) for a "make clean" +# +CLEANUP += $(BIN)/*.* # Avoid picking up directories + +endif # defined(BIN) + +# Determine whether or not we need to include the dependency files +# +NO_DEP_TARGETS := $(BIN) clean veryclean +ifeq ($(MAKECMDGOALS),) +NEED_DEPS := 1 +endif +ifneq ($(strip $(filter-out $(NO_DEP_TARGETS),$(MAKECMDGOALS))),) +NEED_DEPS := 1 +endif + +############################################################################### +# +# Select build architecture and platform based on $(BIN) +# +# BIN has the form bin[-[arch-]platform] + +ARCHS := $(patsubst arch/%,%,$(wildcard arch/*)) +PLATFORMS := $(patsubst config/defaults/%.h,%,\ + $(wildcard config/defaults/*.h)) +archs : + @$(ECHO) $(ARCHS) + +platforms : + @$(ECHO) $(PLATFORMS) + +ifdef BIN + +# Determine architecture portion of $(BIN), if present +BIN_ARCH := $(strip $(foreach A,$(ARCHS),\ + $(patsubst bin-$(A)-%,$(A),\ + $(filter bin-$(A)-%,$(BIN))))) + +# Determine platform portion of $(BIN), if present +ifeq ($(BIN_ARCH),) +BIN_PLATFORM := $(patsubst bin-%,%,$(filter bin-%,$(BIN))) +else +BIN_PLATFORM := $(patsubst bin-$(BIN_ARCH)-%,%,$(BIN)) +endif + +# Determine build architecture +DEFAULT_ARCH := i386 +ARCH := $(firstword $(BIN_ARCH) $(DEFAULT_ARCH)) +CFLAGS += -DARCH=$(ARCH) +arch : + @$(ECHO) $(ARCH) +.PHONY : arch + +# Determine build platform +DEFAULT_PLATFORM := pcbios +PLATFORM := $(firstword $(BIN_PLATFORM) $(DEFAULT_PLATFORM)) +CFLAGS += -DPLATFORM=$(PLATFORM) +platform : + @$(ECHO) $(PLATFORM) + +endif # defined(BIN) + +# Include architecture-specific Makefile +ifdef ARCH +MAKEDEPS += arch/$(ARCH)/Makefile +include arch/$(ARCH)/Makefile +endif + +############################################################################### +# +# Source file handling + +# SRCDIRS lists all directories containing source files. +srcdirs : + @$(ECHO) $(SRCDIRS) + +# SRCS lists all .c or .S files found in any SRCDIR +# +SRCS += $(wildcard $(patsubst %,%/*.c,$(SRCDIRS))) +SRCS += $(wildcard $(patsubst %,%/*.S,$(SRCDIRS))) +srcs : + @$(ECHO) $(SRCS) + +# AUTO_SRCS lists all files in SRCS that are not mentioned in +# NON_AUTO_SRCS. Files should be added to NON_AUTO_SRCS if they +# cannot be built using the standard build template. +# +AUTO_SRCS = $(filter-out $(NON_AUTO_SRCS),$(SRCS)) +autosrcs : + @$(ECHO) $(AUTO_SRCS) + +# Just about everything else in this section depends upon having +# $(BIN) set + +ifdef BIN + +# Common flags +# +CFLAGS += -I include -I arch/$(ARCH)/include -I . +CFLAGS += -Os -ffreestanding +CFLAGS += -Wall -W -Wformat-nonliteral +CFLAGS += -g +CFLAGS += $(EXTRA_CFLAGS) +ASFLAGS += $(EXTRA_ASFLAGS) +LDFLAGS += $(EXTRA_LDFLAGS) + +# Inhibit -Werror if NO_WERROR is specified on make command line +# +ifneq ($(NO_WERROR),1) +CFLAGS += -Werror +ASFLAGS += --fatal-warnings +endif + +# compiler.h is needed for our linking and debugging system +# +CFLAGS += -include compiler.h + +# CFLAGS for specific object types +# +CFLAGS_c += +CFLAGS_S += -DASSEMBLY + +# Base object name of the current target +# +OBJECT = $(firstword $(subst ., ,$(@F))) + +# CFLAGS for specific object files. You can define +# e.g. CFLAGS_rtl8139, and have those flags automatically used when +# compiling bin/rtl8139.o. +# +OBJ_CFLAGS = $(CFLAGS_$(OBJECT)) -DOBJECT=$(subst -,_,$(OBJECT)) +$(BIN)/%.flags : + @$(ECHO) $(OBJ_CFLAGS) + +# Rules for specific object types. +# +COMPILE_c = $(CC) $(CFLAGS) $(CFLAGS_c) $(OBJ_CFLAGS) +RULE_c = $(Q)$(COMPILE_c) -c $< -o $@ +RULE_c_to_dbg%.o = $(Q)$(COMPILE_c) -Ddebug_$(OBJECT)=$* -c $< -o $@ +RULE_c_to_c = $(Q)$(COMPILE_c) -E -c $< > $@ +RULE_c_to_s = $(Q)$(COMPILE_c) -S -g0 -c $< -o $@ + +PREPROCESS_S = $(CPP) $(CFLAGS) $(CFLAGS_S) $(OBJ_CFLAGS) +ASSEMBLE_S = $(AS) $(ASFLAGS) +RULE_S = $(Q)$(PREPROCESS_S) $< | $(ASSEMBLE_S) -o $@ +RULE_S_to_s = $(Q)$(PREPROCESS_S) $< > $@ + +DEBUG_TARGETS += dbg%.o c s + +# We automatically generate rules for any file mentioned in AUTO_SRCS +# using the following set of templates. It would be cleaner to use +# $(eval ...), but this function exists only in GNU make >= 3.80. + +# src_template : generate Makefile rules for a given source file +# +# $(1) is the full path to the source file (e.g. "drivers/net/rtl8139.c") +# $(2) is the full path to the .d file (e.g. "bin/deps/drivers/net/rtl8139.d") +# $(3) is the source type (e.g. "c") +# $(4) is the source base name (e.g. "rtl8139") +# +define src_template + + @$(ECHO) " [DEPS] $(1)" + @$(MKDIR) -p $(dir $(2)) + @$(RM) $(2) + @$(TOUCH) $(2) + $(foreach OBJ,$(if $(OBJS_$(4)),$(OBJS_$(4)),$(4)), \ + $(call obj_template,$(1),$(2),$(3),$(OBJ))) + @$(PARSEROM) $(1) >> $(2) + +endef + +# obj_template : generate Makefile rules for a given resultant object +# of a particular source file. (We can have multiple objects per +# source file via the OBJS_xxx list.) +# +# $(1) is the full path to the source file (e.g. "drivers/net/rtl8139.c") +# $(2) is the full path to the .d file (e.g. "bin/deps/drivers/net/rtl8139.d") +# $(3) is the source type (e.g. "c") +# $(4) is the object name (e.g. "rtl8139") +# +define obj_template + + @$(CPP) $(CFLAGS) $(CFLAGS_$(3)) $(CFLAGS_$(4)) -DOBJECT=$(4) \ + -Wno-error -MM $(1) -MT "$(4)_DEPS" -MG -MP | \ + sed 's/_DEPS\s*:/_DEPS =/' >> $(2) + @$(ECHO_E) '\n$$(BIN)/$(4).o : $(1) $$(MAKEDEPS) $$($(4)_DEPS)' \ + '\n\t$$(QM)$(ECHO) " [BUILD] $$@"' \ + '\n\t$$(RULE_$(3))\n' \ + '\nBOBJS += $$(BIN)/$(4).o\n' \ + $(foreach TGT,$(DEBUG_TARGETS), \ + $(if $(RULE_$(3)_to_$(TGT)), \ + '\n$$(BIN)/$(4).$(TGT) : $(1) $$(MAKEDEPS) $$($(4)_DEPS)' \ + '\n\t$$(QM)$(ECHO) " [BUILD] $$@"' \ + '\n\t$$(RULE_$(3)_to_$(TGT))\n' \ + '\n$(TGT)_OBJS += $$(BIN)/$(4).$(TGT)\n' ) ) \ + '\n$(2) : $$($(4)_DEPS)\n' \ + '\nTAGS : $$($(4)_DEPS)\n' \ + >> $(2) + +endef + +# Rule to generate the Makefile rules files to be included +# +$(BIN)/deps/%.d : % $(MAKEDEPS) $(PARSEROM) + $(if $(filter $(AUTO_SRCS),$<),$(call src_template,$<,$@,$(subst .,,$(suffix $<)),$(basename $(notdir $<))),@$(ECHO) 'ERROR: $< is not an AUTO_SRC' ; exit 1) + +# Calculate and include the list of Makefile rules files +# +AUTO_DEPS = $(patsubst %,$(BIN)/deps/%.d,$(AUTO_SRCS)) +ifdef NEED_DEPS +ifneq ($(AUTO_DEPS),) +-include $(AUTO_DEPS) +endif +endif +autodeps : + @$(ECHO) $(AUTO_DEPS) +VERYCLEANUP += $(BIN)/deps + +# The following variables are created by the Makefile rules files +# +bobjs : + @$(ECHO) $(BOBJS) +drivers : + @$(ECHO) $(DRIVERS) +.PHONY : drivers +roms : + @$(ECHO) $(ROMS) + +# List of embedded images included in the last build of embedded.o. +# This is needed in order to correctly rebuild embedded.o whenever the +# list of objects changes. +# +EMBEDDED_LIST := $(BIN)/.embedded.list +ifeq ($(wildcard $(EMBEDDED_LIST)),) +EMBEDDED_LIST_IMAGE := +else +EMBEDDED_LIST_IMAGE := $(shell cat $(EMBEDDED_LIST)) +endif +ifneq ($(EMBEDDED_LIST_IMAGE),$(EMBEDDED_IMAGE)) +$(shell $(ECHO) "$(EMBEDDED_IMAGE)" > $(EMBEDDED_LIST)) +endif + +$(EMBEDDED_LIST) : + +VERYCLEANUP += $(EMBEDDED_LIST) + +EMBEDDED_FILES := $(subst $(COMMA), ,$(EMBEDDED_IMAGE)) +EMBED_ALL := $(foreach i,$(shell seq 1 $(words $(EMBEDDED_FILES))),\ + EMBED ( $(i), \"$(word $(i), $(EMBEDDED_FILES))\",\ + \"$(notdir $(word $(i),$(EMBEDDED_FILES)))\" )) + +$(BIN)/embedded.o : $(EMBEDDED_FILES) $(EMBEDDED_LIST) +CFLAGS_embedded = -DEMBED_ALL="$(EMBED_ALL)" + +# Generate the NIC file from the parsed source files. The NIC file is +# only for rom-o-matic. +# +$(BIN)/NIC : $(AUTO_DEPS) + @$(ECHO) '# This is an automatically generated file, do not edit' > $@ + @$(ECHO) '# It does not affect anything in the build, ' \ + 'it is only for rom-o-matic' >> $@ + @$(ECHO) >> $@ + @perl -ne 'chomp; print "$$1\n" if /\# NIC\t(.*)$$/' $^ >> $@ +CLEANUP += $(BIN)/NIC # Doesn't match the $(BIN)/*.* pattern + +# Analyse a target name (e.g. "bin/dfe538--prism2_pci.zrom.tmp") and +# derive the variables: +# +# TGT_ELEMENTS : the elements of the target (e.g. "dfe538 prism2_pci") +# TGT_PREFIX : the prefix type (e.g. "zrom") +# TGT_DRIVERS : the driver for each element (e.g. "rtl8139 prism2_pci") +# TGT_ROM_NAME : the ROM name (e.g. "dfe538") +# TGT_MEDIA : the media type (e.g. "rom") +# +DRIVERS_gpxe = $(DRIVERS) +CARD_DRIVER = $(firstword $(DRIVER_$(1)) $(1)) +TGT_ELEMENTS = $(subst --, ,$(firstword $(subst ., ,$(notdir $@)))) +TGT_PREFIX = $(word 2,$(subst ., ,$(notdir $@))) +TGT_ROM_NAME = $(firstword $(TGT_ELEMENTS)) +TGT_DRIVERS = $(strip $(if $(DRIVERS_$(TGT_ROM_NAME)), \ + $(DRIVERS_$(TGT_ROM_NAME)), \ + $(foreach TGT_ELEMENT,$(TGT_ELEMENTS), \ + $(call CARD_DRIVER,$(TGT_ELEMENT))) )) +TGT_MEDIA = $(subst z,,$(TGT_PREFIX)) + +# Look up ROM IDs for the current target +# (e.g. "bin/dfe538--prism2_pci.zrom.tmp") and derive the variables: +# +# TGT_PCI_VENDOR : the PCI vendor ID (e.g. "0x1186") +# TGT_PCI_DEVICE : the PCI device ID (e.g. "0x1300") +# +TGT_PCI_VENDOR = $(PCI_VENDOR_$(TGT_ROM_NAME)) +TGT_PCI_DEVICE = $(PCI_DEVICE_$(TGT_ROM_NAME)) + +# Calculate link-time options for the current target +# (e.g. "bin/dfe538--prism2_pci.zrom.tmp") and derive the variables: +# +# TGT_LD_DRIVERS : symbols to require in order to drag in the relevant drivers +# (e.g. "obj_rtl8139 obj_prism2_pci") +# TGT_LD_IDS : symbols to define in order to fill in ID structures in the +# ROM header (e.g."pci_vendor_id=0x1186 pci_device_id=0x1300") +# +TGT_LD_DRIVERS = $(subst -,_,$(patsubst %,obj_%,$(TGT_DRIVERS))) +TGT_LD_PREFIX = obj_$(TGT_PREFIX)prefix +TGT_LD_IDS = pci_vendor_id=$(firstword $(TGT_PCI_VENDOR) 0) \ + pci_device_id=$(firstword $(TGT_PCI_DEVICE) 0) + +# Calculate linker flags based on link-time options for the current +# target type (e.g. "bin/dfe538--prism2_pci.zrom.tmp") and derive the +# variables: +# +# TGT_LD_FLAGS : target-specific flags to pass to linker (e.g. +# "-u obj_zpciprefix -u obj_rtl8139 -u obj_prism2_pci +# --defsym pci_vendor=0x1186 --defsym pci_device=0x1300") +# +TGT_LD_FLAGS = $(foreach SYM,$(TGT_LD_PREFIX) $(TGT_LD_DRIVERS) obj_config,\ + -u $(SYM) --defsym check_$(SYM)=$(SYM) ) \ + $(patsubst %,--defsym %,$(TGT_LD_IDS)) + +# Calculate makerom flags for the specific target +# (e.g. "bin/dfe538--prism2_pci.zrom.tmp") and derive the variables: +# +# TGT_MAKEROM_FLAGS : target-specific flags for makerom (e.g. +# "-p 0x1186,0x1300") +# +TGT_MAKEROM_FLAGS = $(strip $(MAKEROM_FLAGS_$(TGT_ROM_NAME)) \ + $(if $(TGT_PCI_VENDOR),$(strip -p $(TGT_PCI_VENDOR),$(TGT_PCI_DEVICE)))) + +# Calculate list of debugging versions of objects to be included in +# the target. +# +DEBUG_LIST = $(subst $(COMMA), ,$(DEBUG)) +DEBUG_OBJ_LEVEL = $(firstword $(word 2,$(subst :, ,$(1))) 1) +DEBUG_OBJ_BASE = $(word 1,$(subst :, ,$(1))).dbg$(call DEBUG_OBJ_LEVEL,$(1)) +DEBUG_OBJ = $(BIN)/$(call DEBUG_OBJ_BASE,$(1)).o +DEBUG_ORIG_OBJ = $(BIN)/$(word 1,$(subst :, ,$(1))).o +DEBUG_OBJS = $(foreach D,$(DEBUG_LIST),$(call DEBUG_OBJ,$(D))) +DEBUG_ORIG_OBJS = $(foreach D,$(DEBUG_LIST),$(call DEBUG_ORIG_OBJ,$(D))) +BLIB_OBJS = $(DEBUG_OBJS) $(filter-out $(DEBUG_ORIG_OBJS),$(BOBJS)) + +# Print out all derived information for a given target. +# +$(BIN)/%.info : + @$(ECHO) 'Elements : $(TGT_ELEMENTS)' + @$(ECHO) 'Prefix : $(TGT_PREFIX)' + @$(ECHO) 'Drivers : $(TGT_DRIVERS)' + @$(ECHO) 'ROM name : $(TGT_ROM_NAME)' + @$(ECHO) 'Media : $(TGT_MEDIA)' + @$(ECHO) + @$(ECHO) 'PCI vendor : $(TGT_PCI_VENDOR)' + @$(ECHO) 'PCI device : $(TGT_PCI_DEVICE)' + @$(ECHO) + @$(ECHO) 'LD driver symbols : $(TGT_LD_DRIVERS)' + @$(ECHO) 'LD prefix symbols : $(TGT_LD_PREFIX)' + @$(ECHO) 'LD ID symbols : $(TGT_LD_IDS)' + @$(ECHO) + @$(ECHO) 'LD target flags : $(TGT_LD_FLAGS)' + @$(ECHO) + @$(ECHO) 'makerom target flags : $(TGT_MAKEROM_FLAGS)' + @$(ECHO) + @$(ECHO) 'Debugging objects : $(DEBUG_OBJS)' + @$(ECHO) 'Replaced objects : $(DEBUG_ORIG_OBJS)' + +# List of objects included in the last build of blib. This is needed +# in order to correctly rebuild blib whenever the list of objects +# changes. +# +BLIB_LIST := $(BIN)/.blib.list +ifeq ($(wildcard $(BLIB_LIST)),) +BLIB_LIST_OBJS := +else +BLIB_LIST_OBJS := $(shell cat $(BLIB_LIST)) +endif +ifneq ($(BLIB_LIST_OBJS),$(BLIB_OBJS)) +$(shell $(ECHO) "$(BLIB_OBJS)" > $(BLIB_LIST)) +endif + +$(BLIB_LIST) : + +VERYCLEANUP += $(BLIB_LIST) + +# Library of all objects +# +BLIB = $(BIN)/blib.a +$(BLIB) : $(BLIB_OBJS) $(BLIB_LIST) $(MAKEDEPS) + $(Q)$(RM) $(BLIB) + $(QM)$(ECHO) " [AR] $@" + $(Q)$(AR) r $@ $(BLIB_OBJS) + $(Q)$(RANLIB) $@ +blib : $(BLIB) + +# Build an intermediate object file from the objects required for the +# specified target. +# +$(BIN)/%.tmp : $(BLIB) $(MAKEDEPS) $(LDSCRIPT) + $(QM)$(ECHO) " [LD] $@" + $(Q)$(LD) $(LDFLAGS) -T $(LDSCRIPT) $(TGT_LD_FLAGS) $(BLIB) -o $@ \ + -Map $(BIN)/$*.tmp.map + $(Q)$(OBJDUMP) -ht $@ | $(SORTOBJDUMP) >> $(BIN)/$*.tmp.map + +# Keep intermediate object file (useful for debugging) +.PRECIOUS : $(BIN)/%.tmp + +# Show a linker map for the specified target +# +$(BIN)/%.map : $(BIN)/%.tmp + @less $(BIN)/$*.tmp.map + +# Extract compression information from intermediate object file +# +$(BIN)/%.zinfo : $(BIN)/%.tmp + $(QM)$(ECHO) " [ZINFO] $@" + $(Q)$(OBJCOPY) -O binary -j .zinfo $< $@ + +# Build raw binary file from intermediate object file +# +$(BIN)/%.bin : $(BIN)/%.tmp + $(QM)$(ECHO) " [BIN] $@" + $(Q)$(OBJCOPY) -O binary -R .zinfo $< $@ + +# Compress raw binary file +# +$(BIN)/%.zbin : $(BIN)/%.bin $(BIN)/%.zinfo $(ZBIN) + $(QM)$(ECHO) " [ZBIN] $@" + $(Q)$(ZBIN) $(BIN)/$*.bin $(BIN)/$*.zinfo > $@ + +# Rules for each media format. These are generated and placed in an +# external Makefile fragment. We could do this via $(eval ...), but +# that would require make >= 3.80. +# +# Note that there's an alternative way to generate most .rom images: +# they can be copied from their 'master' ROM image using cp and +# reprocessed with makerom to add the PCI IDs and ident string. The +# relevant rule would look something like: +# +# $(BIN)/dfe538%rom : $(BIN)/rtl8139%rom +# cat $< $@ +# $(FINALISE_rom) +# +# You can derive the ROM/driver relationships using the variables +# DRIVER_ and/or ROMS_. +# +# We don't currently do this, because (a) it would require generating +# yet more Makefile fragments (since you need a rule for each ROM in +# ROMS), and (b) the linker is so fast that it probably wouldn't make +# much difference to the overall build time. + +media : + @$(ECHO) $(MEDIA) + +AUTO_MEDIA = $(filter-out $(NON_AUTO_MEDIA),$(MEDIA)) +automedia : + @$(ECHO) $(AUTO_MEDIA) + +# media_template : create Makefile rules for specified media +# +# $(1) is the media name (e.g. "rom") +# $(2) is the full path to the .d file (e.g. "bin/deps/rom.media.d") +# +define media_template + + @$(ECHO) " [MEDIADEPS] $(1)" + @$(MKDIR) -p $(dir $(2)) + @$(RM) $(2) + @$(TOUCH) $(2) + @$(ECHO_E) '$$(BIN)/%.$(1) : $$(BIN)/%.$(1).zbin' \ + '\n\t$$(QM)$(ECHO) " [FINISH] $$@"' \ + '\n\t$$(Q)$$(CP) $$< $$@' \ + '\n\t$$(Q)$$(FINALISE_$(1))' \ + > $(2) + +endef + +# Rule to generate the Makefile rules to be included +# +$(BIN)/deps/%.media.d : $(MAKEDEPS) + $(if $(filter $(AUTO_MEDIA),$*), \ + $(call media_template,$*,$@), \ + @$(ECHO) 'ERROR: $* is not an AUTO_MEDIA' ; exit 1) + +# Calculate and include the list of Makefile rules files +# +MEDIA_DEPS = $(patsubst %,$(BIN)/deps/%.media.d,$(AUTO_MEDIA)) +mediadeps : + @$(ECHO) $(MEDIA_DEPS) +ifdef NEED_DEPS +ifneq ($(MEDIA_DEPS),) +-include $(MEDIA_DEPS) +endif +endif + +# Wrap up binary blobs (for embedded images) +# +$(BIN)/%.o : payload/%.img + $(QM)echo " [WRAP] $@" + $(Q)$(LD) -b binary -r -o $@ $< --undefined obj_payload \ + --defsym obj_$*=0 + +BOBJS += $(patsubst payload/%.img,$(BIN)/%.o,$(wildcard payload/*.img)) + +# The "allXXXs" targets for each suffix +# +allall: allroms allzroms allpxes allisos alldsks +allroms allzroms : all%s : $(foreach ROM,$(ROMS),$(BIN)/$(ROM).%) +allpxes allisos alldsks : all%s : $(foreach DRIVER,$(DRIVERS),$(BIN)/$(DRIVER).%) + +# Alias for gpxe.% +# +$(BIN)/etherboot.% : $(BIN)/gpxe.% + ln -sf $(notdir $<) $@ + +endif # defined(BIN) + +############################################################################### +# +# Rules for finalising files. TGT_MAKEROM_FLAGS is defined as part of +# the automatic build system and varies by target; it includes the +# "-p 0x1234,0x5678" string to set the PCI IDs. +# +FINALISE_rom = $(MAKEROM) $(MAKEROM_FLAGS) $(TGT_MAKEROM_FLAGS) \ + -i$(IDENT) -s 0 $@ + +# Some ROMs require specific flags to be passed to makerom.pl +# +MAKEROM_FLAGS_3c503 = -3 + +############################################################################### +# +# The compression utilities +# +$(NRV2B) : util/nrv2b.c $(MAKEDEPS) + $(QM)$(ECHO) " [HOSTCC] $@" + $(Q)$(HOST_CC) -O2 -DENCODE -DDECODE -DMAIN -DVERBOSE -DNDEBUG \ + -DBITSIZE=32 -DENDIAN=0 -o $@ $< +CLEANUP += $(NRV2B) + +$(ZBIN) : util/zbin.c util/nrv2b.c $(MAKEDEPS) + $(QM)$(ECHO) " [HOSTCC] $@" + $(Q)$(HOST_CC) -O2 -o $@ $< +CLEANUP += $(ZBIN) + +############################################################################### +# +# The EFI image converter +# +$(ELF2EFI32) : util/elf2efi.c $(MAKEDEPS) + $(QM)$(ECHO) " [HOSTCC] $@" + $(Q)$(HOST_CC) -DMDE_CPU_IA32 -idirafter include -O2 \ + -o $@ $< -lbfd -liberty +CLEANUP += $(ELF2EFI32) + +$(ELF2EFI64) : util/elf2efi.c $(MAKEDEPS) + $(QM)$(ECHO) " [HOSTCC] $@" + $(Q)$(HOST_CC) -DMDE_CPU_X64 -idirafter include -O2 \ + -o $@ $< -lbfd -liberty +CLEANUP += $(ELF2EFI64) + +$(EFIROM) : util/efirom.c $(MAKEDEPS) + $(QM)$(ECHO) " [HOSTCC] $@" + $(Q)$(HOST_CC) -idirafter include -O2 -o $@ $< +CLEANUP += $(EFIROM) + +############################################################################### +# +# Auto-incrementing build serial number. Append "bs" to your list of +# build targets to get a serial number printed at the end of the +# build. Enable -DBUILD_SERIAL in order to see it when the code runs. +# +BUILDSERIAL_H = config/.buildserial.h +BUILDSERIAL_NOW = config/.buildserial.now +BUILDSERIAL_NEXT = config/.buildserial.next + +$(BUILDSERIAL_NOW) $(BUILDSERIAL_NEXT) : + $(ECHO) 1 > $@ + +$(BUILDSERIAL_H) : $(BUILDSERIAL_NOW) $(BUILDSERIAL_NEXT) + $(ECHO) '#define BUILD_SERIAL_NUM $(shell cat $<)' > $@ + +ifeq ($(filter bs,$(MAKECMDGOALS)),bs) +$(shell diff -q $(BUILDSERIAL_NOW) $(BUILDSERIAL_NEXT) > /dev/null || \ + cp -f $(BUILDSERIAL_NEXT) $(BUILDSERIAL_NOW)) +endif + +bs : $(BUILDSERIAL_NOW) + @$(ECHO) $$(( $(shell cat $<) + 1 )) > $(BUILDSERIAL_NEXT) + @$(ECHO) "Build serial number is $(shell cat $<)" + +############################################################################### +# +# Build the TAGS file(s) for emacs +# +TAGS : + ctags -e -R -f $@ --exclude=bin + +CLEANUP += TAGS + +############################################################################### +# +# Force rebuild for any given target +# +%.rebuild : + rm -f $* + $(Q)$(MAKE) $* + +############################################################################### +# +# Symbol table checks +# + +ifdef BIN + +SYMTAB = $(BIN)/symtab +$(SYMTAB) : $(BLIB) + $(OBJDUMP) -w -t $< > $@ + +CLEANUP += $(BIN)/symtab + +symcheck : $(SYMTAB) + $(SYMCHECK) $< + +endif # defined(BIN) + +############################################################################### +# +# Build bochs symbol table +# + +ifdef BIN + +$(BIN)/%.bxs : $(BIN)/%.tmp + $(NM) $< | cut -d" " -f1,3 > $@ + +endif # defined(BIN) + +############################################################################### +# +# Documentation +# + +ifdef BIN + +$(BIN)/doxygen.cfg : doxygen.cfg $(MAKEDEPS) + $(PERL) -pe 's{\@SRCDIRS\@}{$(SRCDIRS)}; ' \ + -e 's{\@BIN\@}{$(BIN)}; ' \ + -e 's{\@ARCH\@}{$(ARCH)}; ' \ + $< > $@ + +$(BIN)/doc : $(BIN)/doxygen.cfg + $(DOXYGEN) $< + +.PHONY : $(BIN)/doc + +VERYCLEANUP += $(BIN)/doc + +doc : $(BIN)/doc + +docview : + @[ -f $(BIN)/doc/html/index.html ] || $(MAKE) $(BIN)/doc + @if [ -n "$$BROWSER" ] ; then \ + ( $$BROWSER $(BIN)/doc/html/index.html & ) ; \ + else \ + $(ECHO) "Documentation index in $(BIN)/doc/html/index.html" ; \ + fi + +endif # defined(BIN) + +############################################################################### +# +# Clean-up +# +clean : + $(RM) $(CLEANUP) + +veryclean : clean + $(RM) -r $(VERYCLEANUP) diff --git a/gpxe/src/README.pixify b/gpxe/src/README.pixify new file mode 100644 index 0000000..9aef25d --- /dev/null +++ b/gpxe/src/README.pixify @@ -0,0 +1,90 @@ +This file documents the driver changes needed to support use as part +of a PXE stack. + +PROPER WAY +========== + +1. The probe() routine. + +There are three additional fields that need to be filled in the nic +structure: ioaddr, irqno and irq. + + ioaddr is the base I/O address and seems to be for information only; + no use will be made of this value other than displaying it on the + screen. + + irqno must be the IRQ number for the NIC. For PCI NICs this can + simply be copied from pci->irq. + + irq is a function pointer, like poll and transmit. It must point to + the driver's irq() function. + +2. The poll() routine. + +This must take an additional parameter: "int retrieve". Calling +poll() with retrieve!=0 should function exactly as before. Calling +poll() with retrieve==0 indicates that poll() should check for the +presence of a packet to read, but must *not* read the packet. The +packet will be read by a subsequent call to poll() with retrieve!=0. + +The easiest way to implement this is to insert the line + if ( ! retrieve ) return 1; +between the "is there a packet ready" and the "fetch packet" parts of +the existing poll() routine. + +Care must be taken that a call to poll() with retrieve==0 does not +clear the NIC's "packet ready" status indicator, otherwise the +subsequent call to poll() with retrieve!=0 will fail because it will +think that there is no packet to read. + +poll() should also acknowledge and clear the NIC's "packet received" +interrupt. It does not need to worry about enabling/disabling +interrupts; this is taken care of by calls to the driver's irq() +routine. + +Etherboot will forcibly regenerate an interrupt if a packet remains +pending after all interrupts have been acknowledged. You can +therefore get away with having poll() just acknolwedge and clear all +NIC interrupts, without particularly worrying about exactly when this +should be done. + +3. The irq() routine. + +This is a new routine, with prototype + void DRIVER_irq ( struct nic *nic, irq_action_t action ); +"action" takes one of three possible values: ENABLE, DISABLE or FORCE. +ENABLE and DISABLE mean to enable/disable the NIC's "packet received" +interrupt. FORCE means that the NIC should be forced to generate a +fake "packet received" interrupt. + +If you are unable to implement FORCE, your NIC will not work when +being driven via the UNDI interface under heavy network traffic +conditions. Since Etherboot's UNDI driver (make bin/undi.zpxe) is the +only program known to use this interface, it probably doesn't really +matter. + + +QUICK AND DIRTY WAY +=================== + +It is possible to use the system timer interrupt (IRQ 0) rather than a +genuine NIC interrupt. Since there is a constant stream of timer +interrupts, the net upshot is a whole load of spurious "NIC" +interrupts that have no effect other than to cause unnecessary PXE API +calls. It's inefficient but it works. + +To achieve this, simply set nic->irqno=0 in probe() and point nic->irq +to a dummy routine that does nothing. Add the line + if ( ! retrieve ) return 1; +at the beginning of poll(), to prevent the packet being read (and +discarded) when poll() is called with retrieve==0; + + +UNCONVERTED DRIVERS +=================== + +Drivers that have not yet been converted should continue to function +when not used as part of a PXE stack, although there will be a +harmless compile-time warning about assignment from an incompatible +pointer type in the probe() function, since the prototype for the +poll() function is missing the "int retrieve" parameter. diff --git a/gpxe/src/arch/i386/Makefile b/gpxe/src/arch/i386/Makefile new file mode 100644 index 0000000..1392bba --- /dev/null +++ b/gpxe/src/arch/i386/Makefile @@ -0,0 +1,114 @@ +# Force i386-only instructions +# +CFLAGS += -march=i386 + +# Code size reduction. +# +CFLAGS += -fstrength-reduce -fomit-frame-pointer + +# Code size reduction. gcc3 needs a different syntax to gcc2 if you +# want to avoid spurious warnings. +# +GCC_VERSION := $(subst ., ,$(shell $(CC) -dumpversion)) +GCC_MAJOR := $(firstword $(GCC_VERSION)) +ifeq ($(GCC_MAJOR),2) +CFLAGS += -malign-jumps=1 -malign-loops=1 -malign-functions=1 +else +CFLAGS += -falign-jumps=1 -falign-loops=1 -falign-functions=1 +endif + +# Code size reduction. This is almost always a win. The kernel uses it, too. +# +CFLAGS += -mpreferred-stack-boundary=2 + +# Code size reduction. Use regparm for all functions - C functions +# called from assembly (or vice versa) need __asmcall now +# +CFLAGS += -mregparm=3 + +# Code size reduction. Use -mrtd (same __asmcall requirements as above) +CFLAGS += -mrtd + +# Code size reduction. This is the logical complement to -mregparm=3. +# It doesn't currently buy us anything, but if anything ever tries to +# return small structures, let's be prepared +# +CFLAGS += -freg-struct-return + +# Force 32-bit code even on an x86-64 machine +# +CFLAGS += -m32 +ASFLAGS += --32 +ifeq ($(HOST_OS),FreeBSD) +LDFLAGS += -m elf_i386_fbsd +else +LDFLAGS += -m elf_i386 +endif + +# EFI requires -fshort-wchar, and nothing else currently uses wchar_t +# +CFLAGS += -fshort-wchar + +# We need to undefine the default macro "i386" when compiling .S +# files, otherwise ".arch i386" translates to ".arch 1"... +# +CFLAGS += -Ui386 + +# Locations of utilities +# +ISOLINUX_BIN = /usr/lib/syslinux/isolinux.bin + +# i386-specific directories containing source files +# +SRCDIRS += arch/i386/core arch/i386/transitions arch/i386/prefix +SRCDIRS += arch/i386/firmware/pcbios +SRCDIRS += arch/i386/image +SRCDIRS += arch/i386/drivers +SRCDIRS += arch/i386/drivers/net +SRCDIRS += arch/i386/interface/pcbios +SRCDIRS += arch/i386/interface/pxe +SRCDIRS += arch/i386/interface/syslinux + +# The various xxx_loader.c files are #included into core/loader.c and +# should not be compiled directly. +# +NON_AUTO_SRCS += arch/i386/core/aout_loader.c +NON_AUTO_SRCS += arch/i386/core/freebsd_loader.c +NON_AUTO_SRCS += arch/i386/core/wince_loader.c + +# unnrv2b.S is used to generate a 16-bit as well as a 32-bit object. +# +OBJS_unnrv2b = unnrv2b unnrv2b16 +CFLAGS_unnrv2b16 = -DCODE16 + +# Include common x86 Makefile +# +MAKEDEPS += arch/x86/Makefile +include arch/x86/Makefile + +# Include platform-specific Makefile +# +MAKEDEPS += arch/i386/Makefile.$(PLATFORM) +include arch/i386/Makefile.$(PLATFORM) + +# Some suffixes (e.g. %.fd0) are generated directly from other +# finished files (e.g. %.dsk), rather than having their own prefix. + +# rule to write disk images to /dev/fd0 +NON_AUTO_MEDIA += fd0 +%fd0 : %dsk + $(QM)$(ECHO) " [DD] $@" + $(Q)dd if=$< bs=512 conv=sync of=/dev/fd0 + $(Q)sync + +# rule to create padded disk images +NON_AUTO_MEDIA += pdsk +%pdsk : %dsk + $(QM)$(ECHO) " [DSKPAD] $@" + $(Q)cp $< $@ + $(Q)$(PERL) ./util/dskpad.pl $@ + +# Add NON_AUTO_MEDIA to the media list, so that they show up in the +# output of "make" +# +MEDIA += $(NON_AUTO_MEDIA) diff --git a/gpxe/src/arch/i386/Makefile.efi b/gpxe/src/arch/i386/Makefile.efi new file mode 100644 index 0000000..8d651b0 --- /dev/null +++ b/gpxe/src/arch/i386/Makefile.efi @@ -0,0 +1,10 @@ +# -*- makefile -*- : Force emacs to use Makefile mode + +# Specify EFI image builder +# +ELF2EFI = $(ELF2EFI32) + +# Include generic EFI Makefile +# +MAKEDEPS += arch/x86/Makefile.efi +include arch/x86/Makefile.efi diff --git a/gpxe/src/arch/i386/Makefile.pcbios b/gpxe/src/arch/i386/Makefile.pcbios new file mode 100644 index 0000000..64b3dac --- /dev/null +++ b/gpxe/src/arch/i386/Makefile.pcbios @@ -0,0 +1,55 @@ +# -*- makefile -*- : Force emacs to use Makefile mode + +# The i386 linker script +# +LDSCRIPT = arch/i386/scripts/i386.lds + +# Stop ld from complaining about our customised linker script +# +LDFLAGS += -N --no-check-sections + +# Media types. +# +MEDIA += rom +MEDIA += pxe +MEDIA += kpxe +MEDIA += kkpxe +MEDIA += elf +MEDIA += elfd +MEDIA += lmelf +MEDIA += lmelfd +MEDIA += lkrn +MEDIA += bImage +MEDIA += dsk +MEDIA += nbi +MEDIA += hd +MEDIA += raw +MEDIA += com +MEDIA += exe + +# rule to make a non-emulation ISO boot image +NON_AUTO_MEDIA += iso +%iso: %lkrn util/geniso + $(QM)$(ECHO) " [GENISO] $@" + $(Q)ISOLINUX_BIN=$(ISOLINUX_BIN) bash util/geniso $@ $< + +# rule to make a floppy emulation ISO boot image +NON_AUTO_MEDIA += liso +%liso: %lkrn util/genliso + $(QM)$(ECHO) " [GENLISO] $@" + $(Q)bash util/genliso $@ $< + +# Special target for building Master Boot Record binary +$(BIN)/mbr.bin : $(BIN)/mbr.o + $(QM)$(ECHO) " [OBJCOPY] $@" + $(Q)$(OBJCOPY) -O binary $< $@ + +# rule to make a USB disk image +$(BIN)/usbdisk.bin : $(BIN)/usbdisk.o + $(QM)$(ECHO) " [OBJCOPY] $@" + $(Q)$(OBJCOPY) -O binary $< $@ + +NON_AUTO_MEDIA += usb +%usb: $(BIN)/usbdisk.bin %hd + $(QM)$(ECHO) " [FINISH] $@" + $(Q)cat $^ > $@ diff --git a/gpxe/src/arch/i386/README.i386 b/gpxe/src/arch/i386/README.i386 new file mode 100644 index 0000000..b9b79cc --- /dev/null +++ b/gpxe/src/arch/i386/README.i386 @@ -0,0 +1,197 @@ +Etherboot/NILO i386 initialisation path and external call interface +=================================================================== + +1. Background + +GCC compiles 32-bit code. It is capable of producing +position-independent code, but the resulting binary is about 25% +bigger than the corresponding fixed-position code. Since one main use +of Etherboot is as firmware to be burned into an EPROM, code size must +be kept as small as possible. + +This means that we want to compile fixed-position code with GCC, and +link it to have a predetermined start address. The problem then is +that we must know the address that the code will be loaded to when it +runs. There are several ways to solve this: + +1. Pick an address, link the code with this start address, then make + sure that the code gets loaded at that location. This is + problematic, because we may pick an address that we later end up + wanting to use to load the operating system that we're booting. + +2. Pick an address, link the code with this start address, then set up + virtual addressing so that the virtual addresses match the + link-time addresses regardless of the real physical address that + the code is loaded to. This enables us to relocate Etherboot to + the top of high memory, where it will be out of the way of any + loading operating system. + +3. Link the code with a text start address of zero and a data start + address also of zero. Use 16-bit real mode and the + quasi-position-independence it gives you via segment addressing. + Doing this requires that we generate 16-bit code, rather than + 32-bit code, and restricts us to a maximum of 64kB in each segment. + +There are other possible approaches (e.g. including a relocation table +and code that performs standard dynamic relocation), but the three +options listed above are probably the best available. + +Etherboot can be invoked in a variety of ways (ROM, floppy, as a PXE +NBP, etc). Several of these ways involve control being passed to +Etherboot with the CPU in 16-bit real mode. Some will involve the CPU +being in 32-bit protected mode, and there's an outside chance that +some may involve the CPU being in 16-bit protected mode. We will +almost certainly have to effect a CPU mode change in order to reach +the mode we want to be in to execute the C code. + +Additionally, Etherboot may wish to call external routines, such as +BIOS interrupts, which must be called in 16-bit real mode. When +providing a PXE API, Etherboot must provide a mechanism for external +code to call it from 16-bit real mode. + +Not all i386 builds of Etherboot will want to make real-mode calls. +For example, when built for LinuxBIOS rather than the standard PCBIOS, +no real-mode calls are necessary. + +For the ultimate in PXE compatibility, we may want to build Etherboot +to run permanently in real mode. + +There is a wide variety of potential combinations of mode switches +that we may wish to implement. There are additional complications, +such as the inability to access a high-memory stack when running in +real mode. + +2. Transition libraries + +To handle all these various combinations of mode switches, we have +several "transition" libraries in Etherboot. We also have the concept +of an "internal" and an "external" environment. The internal +environment is the environment within which we can execute C code. +The external environment is the environment of whatever external code +we're trying to interface to, such as the system BIOS or a PXE NBP. + +As well as having a separate addressing scheme, the internal +environment also has a separate stack. + +The transition libraries are: + +a) librm + +librm handles transitions between an external 16-bit real-mode +environment and an internal 32-bit protected-mode environment with +virtual addresses. + +b) libkir + +libkir handles transitions between an external 16-bit real-mode (or +16:16 or 16:32 protected-mode) environment and an internal 16-bit +real-mode (or 16:16 protected-mode) environment. + +c) libpm + +libpm handles transitions between an external 32-bit protected-mode +environment with flat physical addresses and an internal 32-bit +protected-mode environment with virtual addresses. + +The transition libraries handle the transitions required when +Etherboot is started up for the first time, the transitions required +to execute any external code, and the transitions required when +Etherboot exits (if it exits). When Etherboot provides a PXE API, +they also handle the transitions required when a PXE client makes a +PXE API call to Etherboot. + +Etherboot may use multiple transition libraries. For example, an +Etherboot ELF image does not require librm for its initial transitions +from prefix to runtime, but may require librm for calling external +real-mode functions. + +3. Setup and initialisation + +Etherboot is conceptually divided into the prefix, the decompressor, +and the runtime image. (For non-compressed images, the decompressor +is a no-op.) The complete image comprises all three parts and is +distinct from the runtime image, which exclude the prefix and the +decompressor. + +The prefix does several tasks: + + Load the complete image into memory. (For example, the floppy + prefix issues BIOS calls to load the remainder of the complete image + from the floppy disk into RAM, and the ISA ROM prefix copies the ROM + contents into RAM for faster access.) + + Call the decompressor, if the runtime image is compressed. This + decompresses the runtime image. + + Call the runtime image's setup() routine. This is a routine + implemented in assembly code which sets up the internal environment + so that C code can execute. + + Call the runtime image's arch_initialise() routine. This is a + routine implemented in C which does some basic startup tasks, such + as initialising the console device, obtaining a memory map and + relocating the runtime image to high memory. + + Call the runtime image's arch_main() routine. This records the exit + mechanism requested by the prefix and calls main(). (The prefix + needs to register an exit mechanism because by the time main() + returns, the memory occupied by the prefix has most likely been + overwritten.) + +When acting as a PXE ROM, the ROM prefix contains an UNDI loader +routine in addition to its usual code. The UNDI loader performs a +similar sequence of steps: + + Load the complete image into memory. + + Call the decompressor. + + Call the runtime image's setup() routine. + + Call the runtime image's arch_initialise() routine. + + Call the runtime image's install_pxe_stack() routine. + + Return to caller. + +The runtime image's setup() routine will perform the following steps: + + Switch to the internal environment using an appropriate transition + library. This will record the parameters of the external + environment. + + Set up the internal environment: load a stack, and set up a GDT for + virtual addressing if virtual addressing is to be used. + + Switch back to the external environment using the transition + library. This will record the parameters of the internal + environment. + +Once the setup() routine has returned, the internal environment has been +set up ready for C code to run. The prefix can call C routines using +a function from the transition library. + +The runtime image's arch_initialise() routine will perform the +following steps: + + Zero the bss + + Initialise the console device(s) and print a welcome message. + + Obtain a memory map via the INT 15,E820 BIOS call or suitable + fallback mechanism. [not done if libkir is being used] + + Relocate the runtime image to the top of high memory. [not done if + libkir is being used] + + Install librm to base memory. [done only if librm is being used] + + Call initialise(). + + Return to the prefix, setting registers to indicate to the prefix + the new location of the transition library, if applicable. Which + registers these are is specific to the transition library being + used. + +Once the arch_initialise() routine has returned, the prefix will +probably call arch_main(). diff --git a/gpxe/src/arch/i386/core/aout_loader.c b/gpxe/src/arch/i386/core/aout_loader.c new file mode 100644 index 0000000..f85620e --- /dev/null +++ b/gpxe/src/arch/i386/core/aout_loader.c @@ -0,0 +1,144 @@ +/* a.out */ +struct exec { + unsigned long a_midmag; /* flags<<26 | mid<<16 | magic */ + unsigned long a_text; /* text segment size */ + unsigned long a_data; /* initialized data size */ + unsigned long a_bss; /* uninitialized data size */ + unsigned long a_syms; /* symbol table size */ + unsigned long a_entry; /* entry point */ + unsigned long a_trsize; /* text relocation size */ + unsigned long a_drsize; /* data relocation size */ +}; + +struct aout_state { + struct exec head; + unsigned long curaddr; + int segment; /* current segment number, -1 for none */ + unsigned long loc; /* start offset of current block */ + unsigned long skip; /* padding to be skipped to current segment */ + unsigned long toread; /* remaining data to be read in the segment */ +}; + +static struct aout_state astate; + +static sector_t aout_download(unsigned char *data, unsigned int len, int eof); +static inline os_download_t aout_probe(unsigned char *data, unsigned int len) +{ + unsigned long start, mid, end, istart, iend; + if (len < sizeof(astate.head)) { + return 0; + } + memcpy(&astate.head, data, sizeof(astate.head)); + if ((astate.head.a_midmag & 0xffff) != 0x010BL) { + return 0; + } + + printf("(a.out"); + aout_freebsd_probe(); + printf(")... "); + /* Check the aout image */ + start = astate.head.a_entry; + mid = (((start + astate.head.a_text) + 4095) & ~4095) + astate.head.a_data; + end = ((mid + 4095) & ~4095) + astate.head.a_bss; + istart = 4096; + iend = istart + (mid - start); + if (!prep_segment(start, mid, end, istart, iend)) + return dead_download; + astate.segment = -1; + astate.loc = 0; + astate.skip = 0; + astate.toread = 0; + return aout_download; +} + +static sector_t aout_download(unsigned char *data, unsigned int len, int eof) +{ + unsigned int offset; /* working offset in the current data block */ + + offset = 0; + +#ifdef AOUT_LYNX_KDI + astate.segment++; + if (astate.segment == 0) { + astate.curaddr = 0x100000; + astate.head.a_entry = astate.curaddr + 0x20; + } + memcpy(phys_to_virt(astate.curaddr), data, len); + astate.curaddr += len; + return 0; +#endif + + do { + if (astate.segment != -1) { + if (astate.skip) { + if (astate.skip >= len - offset) { + astate.skip -= len - offset; + break; + } + offset += astate.skip; + astate.skip = 0; + } + + if (astate.toread) { + if (astate.toread >= len - offset) { + memcpy(phys_to_virt(astate.curaddr), data+offset, + len - offset); + astate.curaddr += len - offset; + astate.toread -= len - offset; + break; + } + memcpy(phys_to_virt(astate.curaddr), data+offset, astate.toread); + offset += astate.toread; + astate.toread = 0; + } + } + + /* Data left, but current segment finished - look for the next + * segment. This is quite simple for a.out files. */ + astate.segment++; + switch (astate.segment) { + case 0: + /* read text */ + astate.curaddr = astate.head.a_entry; + astate.skip = 4096; + astate.toread = astate.head.a_text; + break; + case 1: + /* read data */ + /* skip and curaddr may be wrong, but I couldn't find + * examples where this failed. There is no reasonable + * documentation for a.out available. */ + astate.skip = ((astate.curaddr + 4095) & ~4095) - astate.curaddr; + astate.curaddr = (astate.curaddr + 4095) & ~4095; + astate.toread = astate.head.a_data; + break; + case 2: + /* initialize bss and start kernel */ + astate.curaddr = (astate.curaddr + 4095) & ~4095; + astate.skip = 0; + astate.toread = 0; + memset(phys_to_virt(astate.curaddr), '\0', astate.head.a_bss); + goto aout_startkernel; + default: + break; + } + } while (offset < len); + + astate.loc += len; + + if (eof) { + unsigned long entry; + +aout_startkernel: + entry = astate.head.a_entry; + done(1); + + aout_freebsd_boot(); +#ifdef AOUT_LYNX_KDI + xstart32(entry); +#endif + printf("unexpected a.out variant\n"); + longjmp(restart_etherboot, -2); + } + return 0; +} diff --git a/gpxe/src/arch/i386/core/basemem_packet.c b/gpxe/src/arch/i386/core/basemem_packet.c new file mode 100644 index 0000000..64e0bcc --- /dev/null +++ b/gpxe/src/arch/i386/core/basemem_packet.c @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2007 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. + */ + +/** + * @file + * + * Packet buffer in base memory. Used by various components which + * need to pass packets to and from external real-mode code. + * + */ + +#include + +#undef basemem_packet +char __bss16_array ( basemem_packet, [BASEMEM_PACKET_LEN] ); diff --git a/gpxe/src/arch/i386/core/cpu.c b/gpxe/src/arch/i386/core/cpu.c new file mode 100644 index 0000000..c24fa4e --- /dev/null +++ b/gpxe/src/arch/i386/core/cpu.c @@ -0,0 +1,73 @@ +#include +#include +#include + +/** @file + * + * CPU identification + * + */ + +/** + * Test to see if CPU flag is changeable + * + * @v flag Flag to test + * @ret can_change Flag is changeable + */ +static inline int flag_is_changeable ( unsigned int flag ) { + uint32_t f1, f2; + + __asm__ ( "pushfl\n\t" + "pushfl\n\t" + "popl %0\n\t" + "movl %0,%1\n\t" + "xorl %2,%0\n\t" + "pushl %0\n\t" + "popfl\n\t" + "pushfl\n\t" + "popl %0\n\t" + "popfl\n\t" + : "=&r" ( f1 ), "=&r" ( f2 ) + : "ir" ( flag ) ); + + return ( ( ( f1 ^ f2 ) & flag ) != 0 ); +} + +/** + * Get CPU information + * + * @v cpu CPU information structure to fill in + */ +void get_cpuinfo ( struct cpuinfo_x86 *cpu ) { + unsigned int cpuid_level; + unsigned int cpuid_extlevel; + unsigned int discard_1, discard_2, discard_3; + + memset ( cpu, 0, sizeof ( *cpu ) ); + + /* Check for CPUID instruction */ + if ( ! flag_is_changeable ( X86_EFLAGS_ID ) ) { + DBG ( "CPUID not supported\n" ); + return; + } + + /* Get features, if present */ + cpuid ( 0x00000000, &cpuid_level, &discard_1, + &discard_2, &discard_3 ); + if ( cpuid_level >= 0x00000001 ) { + cpuid ( 0x00000001, &discard_1, &discard_2, + &discard_3, &cpu->features ); + } else { + DBG ( "CPUID cannot return capabilities\n" ); + } + + /* Get 64-bit features, if present */ + cpuid ( 0x80000000, &cpuid_extlevel, &discard_1, + &discard_2, &discard_3 ); + if ( ( cpuid_extlevel & 0xffff0000 ) == 0x80000000 ) { + if ( cpuid_extlevel >= 0x80000001 ) { + cpuid ( 0x80000001, &discard_1, &discard_2, + &discard_3, &cpu->amd_features ); + } + } +} diff --git a/gpxe/src/arch/i386/core/dumpregs.c b/gpxe/src/arch/i386/core/dumpregs.c new file mode 100644 index 0000000..82dc218 --- /dev/null +++ b/gpxe/src/arch/i386/core/dumpregs.c @@ -0,0 +1,23 @@ +#include +#include + +void __asmcall _dump_regs ( struct i386_all_regs *ix86 ) { + + __asm__ __volatile__ ( + TEXT16_CODE ( ".globl dump_regs\n\t" + "\ndump_regs:\n\t" + "pushl $_dump_regs\n\t" + "pushw %%cs\n\t" + "call prot_call\n\t" + "addr32 leal 4(%%esp), %%esp\n\t" + "ret\n\t" ) : : ); + + printf ( "EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n" + "ESI=%08x EDI=%08x EBP=%08x ESP=%08x\n" + "CS=%04x SS=%04x DS=%04x ES=%04x FS=%04x GS=%04x\n", + ix86->regs.eax, ix86->regs.ebx, ix86->regs.ecx, + ix86->regs.edx, ix86->regs.esi, ix86->regs.edi, + ix86->regs.ebp, ix86->regs.esp, + ix86->segs.cs, ix86->segs.ss, ix86->segs.ds, + ix86->segs.es, ix86->segs.fs, ix86->segs.gs ); +} diff --git a/gpxe/src/arch/i386/core/etherboot.prefix.lds b/gpxe/src/arch/i386/core/etherboot.prefix.lds new file mode 100644 index 0000000..3550a2a --- /dev/null +++ b/gpxe/src/arch/i386/core/etherboot.prefix.lds @@ -0,0 +1,100 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) + +ENTRY(_prefix_start) +SECTIONS { + /* Prefix */ + .prefix : { + _verbatim_start = . ; + _prefix_start = . ; + *(.prefix) + . = ALIGN(16); + _prefix_end = . ; + } = 0x9090 + _prefix_size = _prefix_end - _prefix_start; + + .text.nocompress : { + *(.prefix.udata) + } = 0x9090 + + decompress_to = . ; + .prefix.zdata : { + _compressed = . ; + *(.prefix.zdata) + _compressed_end = . ; + } + _compressed_size = _compressed_end - _compressed; + + . = ALIGN(16); + _verbatim_end = . ; + + + /* Size of the core of etherboot in memory */ + _base_size = _end - _text; + + /* _prefix_size is the length of the non-core etherboot prefix */ + _prefix_size = _prefix_end - _prefix_start; + + /* _verbatim_size is the actual amount that has to be copied to base memory */ + _verbatim_size = _verbatim_end - _verbatim_start; + + /* _image_size is the amount of base memory needed to run */ + _image_size = _base_size + _prefix_size; + + /* Standard sizes rounded up to paragraphs */ + _prefix_size_pgh = (_prefix_size + 15) / 16; + _verbatim_size_pgh = (_verbatim_size + 15) / 16; + _image_size_pgh = (_image_size + 15) / 16 ; + + /* Standard sizes in sectors */ + _prefix_size_sct = (_prefix_size + 511) / 512; + _verbatim_size_sct = (_verbatim_size + 511) / 512; + _image_size_sct = (_image_size + 511) / 512; + + /* Symbol offsets and sizes for the exe prefix */ + _exe_hdr_size = 32; + _exe_size = _verbatim_size; /* Should this be - 32 to exclude the header? */ + _exe_size_tail = (_exe_size) % 512; + _exe_size_pages = ((_exe_size) + 511) / 512; + _exe_bss_size = ((_image_size - _verbatim_size) + 15) / 16; + _exe_ss_offset = (_stack_offset + _prefix_size - _exe_hdr_size + 15) / 16 ; + + /* This is where we copy the compressed image before decompression. + * Prepare to decompress in place. The end mark is about 8.25 bytes long, + * and the worst case symbol is about 16.5 bytes long. Therefore + * We need to reserve at least 25 bytes of slack here. + * Currently I reserve 2048 bytes of just slack to be safe :) + * 2048 bytes easily falls within the BSS (the defualt stack is 4096 bytes) + * so we really are decompressing in place. + * + * Hmm. I missed a trick. In the very worst case (no compression) + * the encoded data is 9/8 the size as it started out so to be completely + * safe I need to be 1/8 of the uncompressed code size past the end. + * This will still fit compfortably into our bss in any conceivable scenario. + */ + _compressed_copy = _edata + _prefix_size - _compressed_size + + /* The amount to overflow _edata */ + MAX( ((_edata - _text + 7) / 8) , 2016 ) + 32; + _assert = ASSERT( ( _compressed_copy - _prefix_size ) < _ebss , "Cannot decompress in place" ) ; + + decompress = DEFINED(decompress) ? decompress : 0; + /DISCARD/ : { + *(.comment) + *(.note) + } + + /* Symbols used by the prefixes whose addresses are inconvinient + * to compute, at runtime in the code. + */ + image_basemem_size = DEFINED(image_basemem_size)? image_basemem_size : 65536; + image_basemem = DEFINED(image_basemem)? image_basemem : 65536; + _prefix_real_to_prot = _real_to_prot + _prefix_size ; + _prefix_prot_to_real = _prot_to_real + _prefix_size ; + _prefix_image_basemem_size = image_basemem_size + _prefix_size ; + _prefix_image_basemem = image_basemem + _prefix_size ; + _prefix_rm_in_call = _rm_in_call + _prefix_size ; + _prefix_in_call = _in_call + _prefix_size ; + _prefix_rom = rom + _prefix_size ; + _prefix_rm_etherboot_location = rm_etherboot_location + _prefix_size ; + _prefix_stack_end = _stack_end + _prefix_size ; +} diff --git a/gpxe/src/arch/i386/core/freebsd_loader.c b/gpxe/src/arch/i386/core/freebsd_loader.c new file mode 100644 index 0000000..464f6d9 --- /dev/null +++ b/gpxe/src/arch/i386/core/freebsd_loader.c @@ -0,0 +1,377 @@ +/* bootinfo */ +#define BOOTINFO_VERSION 1 +#define NODEV (-1) /* non-existent device */ +#define PAGE_SHIFT 12 /* LOG2(PAGE_SIZE) */ +#define PAGE_SIZE (1<= estate.p.phdr32[j].p_offset) && + ((shdr[i].sh_offset + shdr[i].sh_size) <= + (estate.p.phdr32[j].p_offset + estate.p.phdr32[j].p_filesz))) + { + shdr[i].sh_offset=0; + shdr[i].sh_size=0; + break; + } + } + } + if ((shdr[i].sh_offset != 0) && (shdr[i].sh_size != 0)) + { + symtabindex = i; + symstrindex = shdr[i].sh_link; + } + } + } + } + + /* Check if we have a symbol table index and have not loaded it */ + if ((symtab_load == 0) && (symtabindex >= 0)) + { + /* No symbol table yet? Load it first... */ + + /* This happens to work out in a strange way. + * If we are past the point in the file already, + * we will skip a *large* number of bytes which + * ends up bringing us to the end of the file and + * an old (default) boot. Less code and lets + * the state machine work in a cleaner way but this + * is a nasty side-effect trick... */ + estate.skip = shdr[symtabindex].sh_offset - (estate.loc + offset); + + /* And we need to read this many bytes... */ + estate.toread = shdr[symtabindex].sh_size; + + if (estate.toread) + { +#if ELF_DEBUG + printf("db sym, size %lX, curaddr %lX\n", + estate.toread, estate.curaddr); +#endif + /* Save where we are loading this... */ + symtab_load = estate.curaddr; + + *((long *)phys_to_virt(estate.curaddr)) = estate.toread; + estate.curaddr += sizeof(long); + + /* Start to read... */ + return 1; + } + } + else if ((symstr_load == 0) && (symstrindex >= 0)) + { + /* We have already loaded the symbol table, so + * now on to the symbol strings... */ + + + /* Same nasty trick as above... */ + estate.skip = shdr[symstrindex].sh_offset - (estate.loc + offset); + + /* And we need to read this many bytes... */ + estate.toread = shdr[symstrindex].sh_size; + + if (estate.toread) + { +#if ELF_DEBUG + printf("db str, size %lX, curaddr %lX\n", + estate.toread, estate.curaddr); +#endif + /* Save where we are loading this... */ + symstr_load = estate.curaddr; + + *((long *)phys_to_virt(estate.curaddr)) = estate.toread; + estate.curaddr += sizeof(long); + + /* Start to read... */ + return 1; + } + } + } + /* all done */ + return 0; +} + +static void elf_freebsd_boot(unsigned long entry) +{ + if (image_type != Elf_FreeBSD) + return; + + memset(&bsdinfo, 0, sizeof(bsdinfo)); + bsdinfo.bi_basemem = meminfo.basememsize; + bsdinfo.bi_extmem = meminfo.memsize; + bsdinfo.bi_memsizes_valid = 1; + bsdinfo.bi_version = BOOTINFO_VERSION; + bsdinfo.bi_kernelname = virt_to_phys(KERNEL_BUF); + bsdinfo.bi_nfs_diskless = NULL; + bsdinfo.bi_size = sizeof(bsdinfo); +#define RB_BOOTINFO 0x80000000 /* have `struct bootinfo *' arg */ + if(freebsd_kernel_env[0] != '\0'){ + freebsd_howto |= RB_BOOTINFO; + bsdinfo.bi_envp = (unsigned long)freebsd_kernel_env; + } + + /* Check if we have symbols loaded, and if so, + * made the meta_data needed to pass those to + * the kernel. */ + if ((symtab_load !=0) && (symstr_load != 0)) + { + unsigned long *t; + + bsdinfo.bi_symtab = symtab_load; + + /* End of symbols (long aligned...) */ + /* Assumes size of long is a power of 2... */ + bsdinfo.bi_esymtab = (symstr_load + + sizeof(long) + + *((long *)phys_to_virt(symstr_load)) + + sizeof(long) - 1) & ~(sizeof(long) - 1); + + /* Where we will build the meta data... */ + t = phys_to_virt(bsdinfo.bi_esymtab); + +#if ELF_DEBUG + printf("Metadata at %lX\n",t); +#endif + + /* Set up the pointer to the memory... */ + bsdinfo.bi_modulep = virt_to_phys(t); + + /* The metadata structure is an array of 32-bit + * words where we store some information about the + * system. This is critical, as FreeBSD now looks + * only for the metadata for the extended symbol + * information rather than in the bootinfo. + */ + /* First, do the kernel name and the kernel type */ + /* Note that this assumed x86 byte order... */ + + /* 'kernel\0\0' */ + *t++=MODINFO_NAME; *t++= 7; *t++=0x6E72656B; *t++=0x00006C65; + + /* 'elf kernel\0\0' */ + *t++=MODINFO_TYPE; *t++=11; *t++=0x20666C65; *t++=0x6E72656B; *t++ = 0x00006C65; + + /* Now the symbol start/end - note that they are + * here in local/physical address - the Kernel + * boot process will relocate the addresses. */ + *t++=MODINFOMD_SSYM | MODINFO_METADATA; *t++=sizeof(*t); *t++=bsdinfo.bi_symtab; + *t++=MODINFOMD_ESYM | MODINFO_METADATA; *t++=sizeof(*t); *t++=bsdinfo.bi_esymtab; + + *t++=MODINFO_END; *t++=0; /* end of metadata */ + + /* Since we have symbols we need to make + * sure that the kernel knows its own end + * of memory... It is not _end but after + * the symbols and the metadata... */ + bsdinfo.bi_kernend = virt_to_phys(t); + + /* Signal locore.s that we have a valid bootinfo + * structure that was completely filled in. */ + freebsd_howto |= 0x80000000; + } + + xstart32(entry, freebsd_howto, NODEV, 0, 0, 0, + virt_to_phys(&bsdinfo), 0, 0, 0); + longjmp(restart_etherboot, -2); +} +#endif + +#ifdef AOUT_IMAGE +static void aout_freebsd_probe(void) +{ + image_type = Aout; + if (((astate.head.a_midmag >> 16) & 0xffff) == 0) { + /* Some other a.out variants have a different + * value, and use other alignments (e.g. 1K), + * not the 4K used by FreeBSD. */ + image_type = Aout_FreeBSD; + printf("/FreeBSD"); + off = -(astate.head.a_entry & 0xff000000); + astate.head.a_entry += off; + } +} + +static void aout_freebsd_boot(void) +{ + if (image_type == Aout_FreeBSD) { + memset(&bsdinfo, 0, sizeof(bsdinfo)); + bsdinfo.bi_basemem = meminfo.basememsize; + bsdinfo.bi_extmem = meminfo.memsize; + bsdinfo.bi_memsizes_valid = 1; + bsdinfo.bi_version = BOOTINFO_VERSION; + bsdinfo.bi_kernelname = virt_to_phys(KERNEL_BUF); + bsdinfo.bi_nfs_diskless = NULL; + bsdinfo.bi_size = sizeof(bsdinfo); + xstart32(astate.head.a_entry, freebsd_howto, NODEV, 0, 0, 0, + virt_to_phys(&bsdinfo), 0, 0, 0); + longjmp(restart_etherboot, -2); + } +} +#endif diff --git a/gpxe/src/arch/i386/core/gdbidt.S b/gpxe/src/arch/i386/core/gdbidt.S new file mode 100644 index 0000000..cd8b38a --- /dev/null +++ b/gpxe/src/arch/i386/core/gdbidt.S @@ -0,0 +1,215 @@ +/* + * Interrupt Descriptor Table (IDT) setup and interrupt handlers for GDB stub. + */ + +#include + +#define SIZEOF_I386_REGS 32 +#define SIZEOF_I386_FLAGS 4 + +/**************************************************************************** + * Interrupt Descriptor Table + **************************************************************************** + */ + .section ".data16", "aw", @progbits + .globl idtr +idtr: +idt_limit: + .word idt_length - 1 +idt_base: + .long 0 + +/* IDT entries have the following format: + * offset_lo, segment selector, flags, offset_hi + * + * Since it is not possible to specify relocations in arbitrary + * expressions like (int_overflow & 0xffff), we initialise the + * IDT with entries in an incorrect format. + * + * The entries are shuffled into the correct format in init_librm(). + */ +#define IDT_ENTRY_EMPTY(name) .word 0, 0, 0, 0 +#define IDT_ENTRY_PRESENT(name) \ + .long int_##name; \ + .word 0x8e00, VIRTUAL_CS + +.align 16 +idt: + IDT_ENTRY_PRESENT(divide_error) + IDT_ENTRY_PRESENT(debug_trap) + IDT_ENTRY_EMPTY(non_maskable_interrupt) + IDT_ENTRY_PRESENT(breakpoint) + IDT_ENTRY_PRESENT(overflow) + IDT_ENTRY_PRESENT(bound_range_exceeded) + IDT_ENTRY_PRESENT(invalid_opcode) + IDT_ENTRY_EMPTY(device_not_available) + IDT_ENTRY_PRESENT(double_fault) + IDT_ENTRY_EMPTY(coprocessor_segment_overrun) + IDT_ENTRY_PRESENT(invalid_tss) + IDT_ENTRY_PRESENT(segment_not_present) + IDT_ENTRY_PRESENT(stack_segment_fault) + IDT_ENTRY_PRESENT(general_protection) + IDT_ENTRY_PRESENT(page_fault) +idt_end: + .equ idt_length, idt_end - idt + +/* The IDT entries are fixed up (once) in init_librm() */ +idt_fixed: + .byte 0 + +/**************************************************************************** + * idt_init (real-mode near call, 16-bit real-mode near return address) + * + * Initialise the IDT, called from init_librm. + * + * Parameters: + * %eax : IDT base address + * + * Destroys %ax, %bx, and %di. + **************************************************************************** + */ + .section ".text16", "ax", @progbits + .code16 + .globl idt_init +idt_init: + movl %eax, idt_base + addl $idt, idt_base + + /* IDT entries are only fixed up once */ + movb idt_fixed, %al + orb %al, %al + jnz 2f + movb $1, idt_fixed + + /* Shuffle IDT entries into the correct format */ + movb $(idt_length / 8), %al + movw $idt, %bx + or %al, %al + jz 2f +1: + movw 2(%bx), %di + xchg %di, 6(%bx) + movw %di, 2(%bx) + addw $8, %bx + dec %al + jnz 1b +2: + ret + +/**************************************************************************** + * Interrupt handlers + **************************************************************************** + */ + .section ".text", "ax", @progbits + .code32 + +/* POSIX signal numbers for reporting traps to GDB */ +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGBUS 7 +#define SIGFPE 8 +#define SIGSEGV 11 +#define SIGSTKFLT 16 + +int_divide_error: + pushl $SIGFPE + jmp do_interrupt + +int_debug_trap: +int_breakpoint: + pushl $SIGTRAP + jmp do_interrupt + +int_overflow: +int_bound_range_exceeded: + pushl $SIGSTKFLT + jmp do_interrupt + +int_invalid_opcode: + pushl $SIGILL + jmp do_interrupt + +int_double_fault: + movl $SIGBUS, (%esp) + jmp do_interrupt + +int_invalid_tss: +int_segment_not_present: +int_stack_segment_fault: +int_general_protection: +int_page_fault: + movl $SIGSEGV, (%esp) + jmp do_interrupt + +/* When invoked, the stack contains: eflags, cs, eip, signo. */ +#define IH_OFFSET_GDB_REGS ( 0 ) +#define IH_OFFSET_GDB_EIP ( IH_OFFSET_GDB_REGS + SIZEOF_I386_REGS ) +#define IH_OFFSET_GDB_EFLAGS ( IH_OFFSET_GDB_EIP + 4 ) +#define IH_OFFSET_GDB_SEG_REGS ( IH_OFFSET_GDB_EFLAGS + SIZEOF_I386_FLAGS ) +#define IH_OFFSET_GDB_END ( IH_OFFSET_GDB_SEG_REGS + 6 * 4 ) +#define IH_OFFSET_SIGNO ( IH_OFFSET_GDB_END ) +#define IH_OFFSET_OLD_EIP ( IH_OFFSET_SIGNO + 4 ) +#define IH_OFFSET_OLD_CS ( IH_OFFSET_OLD_EIP + 4 ) +#define IH_OFFSET_OLD_EFLAGS ( IH_OFFSET_OLD_CS + 4 ) +#define IH_OFFSET_END ( IH_OFFSET_OLD_EFLAGS + 4 ) + +/* We also access the stack whilst still storing or restoring + * the register snapshot. Since ESP is in flux, we need + * special offsets. + */ +#define IH_OFFSET_FLUX_OLD_CS ( IH_OFFSET_OLD_CS - 44 ) +#define IH_OFFSET_FLUX_OLD_EFLAGS ( IH_OFFSET_OLD_EFLAGS - 40 ) +#define IH_OFFSET_FLUX_OLD_EIP ( IH_OFFSET_OLD_EIP - 36 ) +#define IH_OFFSET_FLUX_END ( IH_OFFSET_END - 20 ) +do_interrupt: + /* Store CPU state in GDB register snapshot */ + pushw $0 + pushw %gs + pushw $0 + pushw %fs + pushw $0 + pushw %es + pushw $0 + pushw %ds + pushw $0 + pushw %ss + pushw $0 + pushw IH_OFFSET_FLUX_OLD_CS + 2(%esp) + pushl IH_OFFSET_FLUX_OLD_EFLAGS(%esp) + pushl IH_OFFSET_FLUX_OLD_EIP(%esp) + pushl %edi + pushl %esi + pushl %ebp + leal IH_OFFSET_FLUX_END(%esp), %edi + pushl %edi /* old ESP */ + pushl %ebx + pushl %edx + pushl %ecx + pushl %eax + + /* Call GDB stub exception handler */ + pushl %esp + pushl (IH_OFFSET_SIGNO + 4)(%esp) + call gdbmach_handler + addl $8, %esp + + /* Restore CPU state from GDB register snapshot */ + popl %eax + popl %ecx + popl %edx + popl %ebx + addl $4, %esp /* Changing ESP currently not supported */ + popl %ebp + popl %esi + popl %edi + popl IH_OFFSET_FLUX_OLD_EIP(%esp) + popl IH_OFFSET_FLUX_OLD_EFLAGS(%esp) + popl IH_OFFSET_FLUX_OLD_CS(%esp) + popl %ss + popl %ds + popl %es + popl %fs + popl %gs + + addl $4, %esp /* drop signo */ + iret diff --git a/gpxe/src/arch/i386/core/gdbmach.c b/gpxe/src/arch/i386/core/gdbmach.c new file mode 100644 index 0000000..97827ec --- /dev/null +++ b/gpxe/src/arch/i386/core/gdbmach.c @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2008 Stefan Hajnoczi . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +/** @file + * + * GDB architecture-specific bits for i386 + * + */ + +enum { + DR7_CLEAR = 0x00000400, /* disable hardware breakpoints */ + DR6_CLEAR = 0xffff0ff0, /* clear breakpoint status */ +}; + +/** Hardware breakpoint, fields stored in x86 bit pattern form */ +struct hwbp { + int type; /* type (1=write watchpoint, 3=access watchpoint) */ + unsigned long addr; /* linear address */ + size_t len; /* length (0=1-byte, 1=2-byte, 3=4-byte) */ + int enabled; +}; + +static struct hwbp hwbps [ 4 ]; +static gdbreg_t dr7 = DR7_CLEAR; + +static struct hwbp *gdbmach_find_hwbp ( int type, unsigned long addr, size_t len ) { + struct hwbp *available = NULL; + unsigned int i; + for ( i = 0; i < sizeof hwbps / sizeof hwbps [ 0 ]; i++ ) { + if ( hwbps [ i ].type == type && hwbps [ i ].addr == addr && hwbps [ i ].len == len ) { + return &hwbps [ i ]; + } + if ( !hwbps [ i ].enabled ) { + available = &hwbps [ i ]; + } + } + return available; +} + +static void gdbmach_commit_hwbp ( struct hwbp *bp ) { + unsigned int regnum = bp - hwbps; + + /* Set breakpoint address */ + assert ( regnum < ( sizeof hwbps / sizeof hwbps [ 0 ] ) ); + switch ( regnum ) { + case 0: + __asm__ __volatile__ ( "movl %0, %%dr0\n" : : "r" ( bp->addr ) ); + break; + case 1: + __asm__ __volatile__ ( "movl %0, %%dr1\n" : : "r" ( bp->addr ) ); + break; + case 2: + __asm__ __volatile__ ( "movl %0, %%dr2\n" : : "r" ( bp->addr ) ); + break; + case 3: + __asm__ __volatile__ ( "movl %0, %%dr3\n" : : "r" ( bp->addr ) ); + break; + } + + /* Set type */ + dr7 &= ~( 0x3 << ( 16 + 4 * regnum ) ); + dr7 |= bp->type << ( 16 + 4 * regnum ); + + /* Set length */ + dr7 &= ~( 0x3 << ( 18 + 4 * regnum ) ); + dr7 |= bp->len << ( 18 + 4 * regnum ); + + /* Set/clear local enable bit */ + dr7 &= ~( 0x3 << 2 * regnum ); + dr7 |= bp->enabled << 2 * regnum; +} + +int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, int enable ) { + struct hwbp *bp; + + /* Check and convert breakpoint type to x86 type */ + switch ( type ) { + case GDBMACH_WATCH: + type = 0x1; + break; + case GDBMACH_AWATCH: + type = 0x3; + break; + default: + return 0; /* unsupported breakpoint type */ + } + + /* Only lengths 1, 2, and 4 are supported */ + if ( len != 2 && len != 4 ) { + len = 1; + } + len--; /* convert to x86 breakpoint length bit pattern */ + + /* Calculate linear address by adding segment base */ + addr += virt_offset; + + /* Set up the breakpoint */ + bp = gdbmach_find_hwbp ( type, addr, len ); + if ( !bp ) { + return 0; /* ran out of hardware breakpoints */ + } + bp->type = type; + bp->addr = addr; + bp->len = len; + bp->enabled = enable; + gdbmach_commit_hwbp ( bp ); + return 1; +} + +static void gdbmach_disable_hwbps ( void ) { + /* Store and clear hardware breakpoints */ + __asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( DR7_CLEAR ) ); +} + +static void gdbmach_enable_hwbps ( void ) { + /* Clear breakpoint status register */ + __asm__ __volatile__ ( "movl %0, %%dr6\n" : : "r" ( DR6_CLEAR ) ); + + /* Restore hardware breakpoints */ + __asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( dr7 ) ); +} + +__asmcall void gdbmach_handler ( int signo, gdbreg_t *regs ) { + gdbmach_disable_hwbps(); + gdbstub_handler ( signo, regs ); + gdbmach_enable_hwbps(); +} diff --git a/gpxe/src/arch/i386/core/nulltrap.c b/gpxe/src/arch/i386/core/nulltrap.c new file mode 100644 index 0000000..3046fbe --- /dev/null +++ b/gpxe/src/arch/i386/core/nulltrap.c @@ -0,0 +1,51 @@ +#include +#include + +__attribute__ (( noreturn, section ( ".text.null_trap" ) )) +void null_function_trap ( void ) { + void *stack; + + /* 128 bytes of NOPs; the idea of this is that if something + * dereferences a NULL pointer and overwrites us, we at least + * have some chance of still getting to execute the printf() + * statement. + */ + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + __asm__ __volatile__ ( "nop ; nop ; nop ; nop" ); + + __asm__ __volatile__ ( "movl %%esp, %0" : "=r" ( stack ) ); + printf ( "NULL method called from %p (stack %p)\n", + __builtin_return_address ( 0 ), stack ); + DBG_HD ( stack, 256 ); + while ( 1 ) {} +} diff --git a/gpxe/src/arch/i386/core/pic8259.c b/gpxe/src/arch/i386/core/pic8259.c new file mode 100644 index 0000000..8a0433d --- /dev/null +++ b/gpxe/src/arch/i386/core/pic8259.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include + +/** @file + * + * Minimal support for the 8259 Programmable Interrupt Controller + * + */ + +/** + * Send non-specific EOI(s) + * + * @v irq IRQ number + * + * This seems to be inherently unsafe. + */ +static inline void send_nonspecific_eoi ( unsigned int irq ) { + DBG ( "Sending non-specific EOI for IRQ %d\n", irq ); + if ( irq >= IRQ_PIC_CUTOFF ) { + outb ( ICR_EOI_NON_SPECIFIC, PIC2_ICR ); + } + outb ( ICR_EOI_NON_SPECIFIC, PIC1_ICR ); +} + +/** + * Send specific EOI(s) + * + * @v irq IRQ number + */ +static inline void send_specific_eoi ( unsigned int irq ) { + DBG ( "Sending specific EOI for IRQ %d\n", irq ); + if ( irq >= IRQ_PIC_CUTOFF ) { + outb ( ( ICR_EOI_SPECIFIC | ICR_VALUE ( CHAINED_IRQ ) ), + ICR_REG ( CHAINED_IRQ ) ); + } + outb ( ( ICR_EOI_SPECIFIC | ICR_VALUE ( irq ) ), ICR_REG ( irq ) ); +} + +/** + * Send End-Of-Interrupt to the PIC + * + * @v irq IRQ number + */ +void send_eoi ( unsigned int irq ) { + send_specific_eoi ( irq ); +} diff --git a/gpxe/src/arch/i386/core/prefixudata.lds b/gpxe/src/arch/i386/core/prefixudata.lds new file mode 100644 index 0000000..1c76128 --- /dev/null +++ b/gpxe/src/arch/i386/core/prefixudata.lds @@ -0,0 +1,8 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) + +SECTIONS { + .prefix.udata : { + *(*) + } +} diff --git a/gpxe/src/arch/i386/core/prefixzdata.lds b/gpxe/src/arch/i386/core/prefixzdata.lds new file mode 100644 index 0000000..bf6ea97 --- /dev/null +++ b/gpxe/src/arch/i386/core/prefixzdata.lds @@ -0,0 +1,8 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) + +SECTIONS { + .prefix.zdata : { + *(*) + } +} diff --git a/gpxe/src/arch/i386/core/rdtsc_timer.c b/gpxe/src/arch/i386/core/rdtsc_timer.c new file mode 100644 index 0000000..443c8ad --- /dev/null +++ b/gpxe/src/arch/i386/core/rdtsc_timer.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2008 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. + */ + +/** @file + * + * RDTSC timer + * + */ + +#include +#include +#include + +/** + * Number of TSC ticks per microsecond + * + * This is calibrated on the first use of the timer. + */ +static unsigned long rdtsc_ticks_per_usec; + +/** + * Delay for a fixed number of microseconds + * + * @v usecs Number of microseconds for which to delay + */ +static void rdtsc_udelay ( unsigned long usecs ) { + unsigned long start; + unsigned long elapsed; + + /* Sanity guard, since we may divide by this */ + if ( ! usecs ) + usecs = 1; + + start = currticks(); + if ( rdtsc_ticks_per_usec ) { + /* Already calibrated; busy-wait until done */ + do { + elapsed = ( currticks() - start ); + } while ( elapsed < ( usecs * rdtsc_ticks_per_usec ) ); + } else { + /* Not yet calibrated; use timer2 and calibrate + * based on result. + */ + timer2_udelay ( usecs ); + elapsed = ( currticks() - start ); + rdtsc_ticks_per_usec = ( elapsed / usecs ); + DBG ( "RDTSC timer calibrated: %ld ticks in %ld usecs " + "(%ld MHz)\n", elapsed, usecs, + ( rdtsc_ticks_per_usec << TSC_SHIFT ) ); + } +} + +/** + * Get number of ticks per second + * + * @ret ticks_per_sec Number of ticks per second + */ +static unsigned long rdtsc_ticks_per_sec ( void ) { + + /* Calibrate timer, if not already done */ + if ( ! rdtsc_ticks_per_usec ) + udelay ( 1 ); + + /* Sanity check */ + assert ( rdtsc_ticks_per_usec != 0 ); + + return ( rdtsc_ticks_per_usec * 1000 * 1000 ); +} + +PROVIDE_TIMER ( rdtsc, udelay, rdtsc_udelay ); +PROVIDE_TIMER_INLINE ( rdtsc, currticks ); +PROVIDE_TIMER ( rdtsc, ticks_per_sec, rdtsc_ticks_per_sec ); diff --git a/gpxe/src/arch/i386/core/realmode.c b/gpxe/src/arch/i386/core/realmode.c new file mode 100644 index 0000000..9a77bd8 --- /dev/null +++ b/gpxe/src/arch/i386/core/realmode.c @@ -0,0 +1,23 @@ +/* Real-mode interface: C portions. + * + * Initial version by Michael Brown , January 2004. + */ + +#include "realmode.h" + +/* + * Copy data to/from base memory. + * + */ + +#ifdef KEEP_IT_REAL + +void memcpy_to_real ( segoff_t dest, void *src, size_t n ) { + +} + +void memcpy_from_real ( void *dest, segoff_t src, size_t n ) { + +} + +#endif /* KEEP_IT_REAL */ diff --git a/gpxe/src/arch/i386/core/relocate.c b/gpxe/src/arch/i386/core/relocate.c new file mode 100644 index 0000000..bdc8498 --- /dev/null +++ b/gpxe/src/arch/i386/core/relocate.c @@ -0,0 +1,168 @@ +#include +#include +#include + +/* + * Originally by Eric Biederman + * + * Heavily modified by Michael Brown + * + */ + +/* + * The linker passes in the symbol _max_align, which is the alignment + * that we must preserve, in bytes. + * + */ +extern char _max_align[]; +#define max_align ( ( unsigned int ) _max_align ) + +/* Linker symbols */ +extern char _textdata[]; +extern char _etextdata[]; + +/* within 1MB of 4GB is too close. + * MAX_ADDR is the maximum address we can easily do DMA to. + * + * Not sure where this constraint comes from, but kept it from Eric's + * old code - mcb30 + */ +#define MAX_ADDR (0xfff00000UL) + +/** + * Relocate Etherboot + * + * @v ix86 x86 register dump from prefix + * @ret ix86 x86 registers to return to prefix + * + * This finds a suitable location for Etherboot near the top of 32-bit + * address space, and returns the physical address of the new location + * to the prefix in %edi. + */ +__asmcall void relocate ( struct i386_all_regs *ix86 ) { + struct memory_map memmap; + unsigned long start, end, size, padded_size; + unsigned long new_start, new_end; + unsigned i; + + /* Get memory map and current location */ + get_memmap ( &memmap ); + start = virt_to_phys ( _textdata ); + end = virt_to_phys ( _etextdata ); + size = ( end - start ); + padded_size = ( size + max_align - 1 ); + + DBG ( "Relocate: currently at [%lx,%lx)\n" + "...need %lx bytes for %d-byte alignment\n", + start, end, padded_size, max_align ); + + /* Walk through the memory map and find the highest address + * below 4GB that etherboot will fit into. Ensure etherboot + * lies entirely within a range with A20=0. This means that + * even if something screws up the state of the A20 line, the + * etherboot code is still visible and we have a chance to + * diagnose the problem. + */ + new_end = end; + for ( i = 0 ; i < memmap.count ; i++ ) { + struct memory_region *region = &memmap.regions[i]; + unsigned long r_start, r_end; + + DBG ( "Considering [%llx,%llx)\n", region->start, region->end); + + /* Truncate block to MAX_ADDR. This will be less than + * 4GB, which means that we can get away with using + * just 32-bit arithmetic after this stage. + */ + if ( region->start > MAX_ADDR ) { + DBG ( "...starts after MAX_ADDR=%lx\n", MAX_ADDR ); + continue; + } + r_start = region->start; + if ( region->end > MAX_ADDR ) { + DBG ( "...end truncated to MAX_ADDR=%lx\n", MAX_ADDR ); + r_end = MAX_ADDR; + } else { + r_end = region->end; + } + + /* Shrink the range down to use only even megabytes + * (i.e. A20=0). + */ + if ( ( r_end - 1 ) & 0x100000 ) { + /* If last byte that might be used (r_end-1) + * is in an odd megabyte, round down r_end to + * the top of the next even megabyte. + * + * Make sure that we don't accidentally wrap + * r_end below 0. + */ + if ( r_end >= 1 ) { + r_end = ( r_end - 1 ) & ~0xfffff; + DBG ( "...end truncated to %lx " + "(avoid ending in odd megabyte)\n", + r_end ); + } + } else if ( ( r_end - size ) & 0x100000 ) { + /* If the last byte that might be used + * (r_end-1) is in an even megabyte, but the + * first byte that might be used (r_end-size) + * is an odd megabyte, round down to the top + * of the next even megabyte. + * + * Make sure that we don't accidentally wrap + * r_end below 0. + */ + if ( r_end >= 0x100000 ) { + r_end = ( r_end - 0x100000 ) & ~0xfffff; + DBG ( "...end truncated to %lx " + "(avoid starting in odd megabyte)\n", + r_end ); + } + } + + DBG ( "...usable portion is [%lx,%lx)\n", r_start, r_end ); + + /* If we have rounded down r_end below r_ start, skip + * this block. + */ + if ( r_end < r_start ) { + DBG ( "...truncated to negative size\n" ); + continue; + } + + /* Check that there is enough space to fit in Etherboot */ + if ( ( r_end - r_start ) < size ) { + DBG ( "...too small (need %lx bytes)\n", size ); + continue; + } + + /* If the start address of the Etherboot we would + * place in this block is higher than the end address + * of the current highest block, use this block. + * + * Note that this avoids overlaps with the current + * Etherboot, as well as choosing the highest of all + * viable blocks. + */ + if ( ( r_end - size ) > new_end ) { + new_end = r_end; + DBG ( "...new best block found.\n" ); + } + } + + /* Calculate new location of Etherboot, and align it to the + * required alignemnt. + */ + new_start = new_end - padded_size; + new_start += ( start - new_start ) & ( max_align - 1 ); + new_end = new_start + size; + + DBG ( "Relocating from [%lx,%lx) to [%lx,%lx)\n", + start, end, new_start, new_end ); + + /* Let prefix know what to copy */ + ix86->regs.esi = start; + ix86->regs.edi = new_start; + ix86->regs.ecx = size; +} diff --git a/gpxe/src/arch/i386/core/setjmp.S b/gpxe/src/arch/i386/core/setjmp.S new file mode 100644 index 0000000..59a1b7c --- /dev/null +++ b/gpxe/src/arch/i386/core/setjmp.S @@ -0,0 +1,40 @@ +/* setjmp and longjmp. Use of these functions is deprecated. */ + + .text + .arch i386 + .code32 + +/************************************************************************** +SETJMP - Save stack context for non-local goto +**************************************************************************/ + .globl setjmp +setjmp: + movl 4(%esp),%ecx /* jmpbuf */ + movl 0(%esp),%edx /* return address */ + movl %edx,0(%ecx) + movl %ebx,4(%ecx) + movl %esp,8(%ecx) + movl %ebp,12(%ecx) + movl %esi,16(%ecx) + movl %edi,20(%ecx) + movl $0,%eax + ret + +/************************************************************************** +LONGJMP - Non-local jump to a saved stack context +**************************************************************************/ + .globl longjmp +longjmp: + movl 4(%esp),%edx /* jumpbuf */ + movl 8(%esp),%eax /* result */ + movl 0(%edx),%ecx + movl 4(%edx),%ebx + movl 8(%edx),%esp + movl 12(%edx),%ebp + movl 16(%edx),%esi + movl 20(%edx),%edi + cmpl $0,%eax + jne 1f + movl $1,%eax +1: movl %ecx,0(%esp) + ret diff --git a/gpxe/src/arch/i386/core/stack.S b/gpxe/src/arch/i386/core/stack.S new file mode 100644 index 0000000..da66d23 --- /dev/null +++ b/gpxe/src/arch/i386/core/stack.S @@ -0,0 +1,13 @@ + .arch i386 + +/**************************************************************************** + * Internal stack + **************************************************************************** + */ + .section ".stack", "aw", @nobits + .align 8 + .globl _stack +_stack: + .space 4096 + .globl _estack +_estack: diff --git a/gpxe/src/arch/i386/core/stack16.S b/gpxe/src/arch/i386/core/stack16.S new file mode 100644 index 0000000..d1251f0 --- /dev/null +++ b/gpxe/src/arch/i386/core/stack16.S @@ -0,0 +1,13 @@ + .arch i386 + +/**************************************************************************** + * Internal stack + **************************************************************************** + */ + .section ".stack16", "aw", @nobits + .align 8 + .globl _stack16 +_stack16: + .space 4096 + .globl _estack16 +_estack16: diff --git a/gpxe/src/arch/i386/core/start16.lds b/gpxe/src/arch/i386/core/start16.lds new file mode 100644 index 0000000..544fc78 --- /dev/null +++ b/gpxe/src/arch/i386/core/start16.lds @@ -0,0 +1,8 @@ +/* When linking with an uncompressed image, these symbols are not + * defined so we provide them here. + */ + +__decompressor_uncompressed = 0 ; +__decompressor__start = 0 ; + +INCLUDE arch/i386/core/start16z.lds diff --git a/gpxe/src/arch/i386/core/start16z.lds b/gpxe/src/arch/i386/core/start16z.lds new file mode 100644 index 0000000..711bcf7 --- /dev/null +++ b/gpxe/src/arch/i386/core/start16z.lds @@ -0,0 +1,65 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) + +/* Linker-generated symbols are prefixed with a double underscore. + * Decompressor symbols are prefixed with __decompressor_. All other + * symbols are the same as in the original object file, i.e. the + * runtime addresses. + */ + +ENTRY(_start16) + +SECTIONS { + .text : { + *(.text) + } + .payload : { + __payload_start = .; + *(.data) + __payload_end = .; + } + + /* _payload_size is the size of the binary image appended to + * start16, in bytes. + */ + __payload_size = __payload_end - __payload_start ; + + /* _size is the size of the runtime image + * (start32 + the C code), in bytes. + */ + __size = _end - _start ; + + /* _decompressor_size is the size of the decompressor, in + * bytes. For a non-compressed image, start16.lds sets + * _decompressor_uncompressed = _decompressor__start = 0. + */ + __decompressor_size = __decompressor_uncompressed - __decompressor__start ; + + /* image__size is the total size of the image, after + * decompression and including the decompressor if applicable. + * It is therefore the amount of memory that start16's payload + * needs in order to execute, in bytes. + */ + __image_size = __size + __decompressor_size ; + + /* Amount to add to runtime symbols to obtain the offset of + * that symbol within the image. + */ + __offset_adjust = __decompressor_size - _start ; + + /* Calculations for the stack + */ + __stack_size = _estack - _stack ; + __offset_stack = _stack + __offset_adjust ; + + /* Some symbols will be larger than 16 bits but guaranteed to + * be multiples of 16. We calculate them in paragraphs and + * export these symbols which can be used in 16-bit code + * without risk of overflow. + */ + __image_size_pgh = ( __image_size / 16 ); + __start_pgh = ( _start / 16 ); + __decompressor_size_pgh = ( __decompressor_size / 16 ); + __offset_stack_pgh = ( __offset_stack / 16 ); +} + diff --git a/gpxe/src/arch/i386/core/timer2.c b/gpxe/src/arch/i386/core/timer2.c new file mode 100644 index 0000000..bb589ec --- /dev/null +++ b/gpxe/src/arch/i386/core/timer2.c @@ -0,0 +1,85 @@ +/* + * arch/i386/core/i386_timer.c + * + * Use the "System Timer 2" to implement the udelay callback in + * the BIOS timer driver. Also used to calibrate the clock rate + * in the RTDSC timer driver. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, or (at + * your option) any later version. + */ + +#include +#include +#include + +/* Timers tick over at this rate */ +#define TIMER2_TICKS_PER_SEC 1193180U + +/* Parallel Peripheral Controller Port B */ +#define PPC_PORTB 0x61 + +/* Meaning of the port bits */ +#define PPCB_T2OUT 0x20 /* Bit 5 */ +#define PPCB_SPKR 0x02 /* Bit 1 */ +#define PPCB_T2GATE 0x01 /* Bit 0 */ + +/* Ports for the 8254 timer chip */ +#define TIMER2_PORT 0x42 +#define TIMER_MODE_PORT 0x43 + +/* Meaning of the mode bits */ +#define TIMER0_SEL 0x00 +#define TIMER1_SEL 0x40 +#define TIMER2_SEL 0x80 +#define READBACK_SEL 0xC0 + +#define LATCH_COUNT 0x00 +#define LOBYTE_ACCESS 0x10 +#define HIBYTE_ACCESS 0x20 +#define WORD_ACCESS 0x30 + +#define MODE0 0x00 +#define MODE1 0x02 +#define MODE2 0x04 +#define MODE3 0x06 +#define MODE4 0x08 +#define MODE5 0x0A + +#define BINARY_COUNT 0x00 +#define BCD_COUNT 0x01 + +static void load_timer2 ( unsigned int ticks ) { + /* + * Now let's take care of PPC channel 2 + * + * Set the Gate high, program PPC channel 2 for mode 0, + * (interrupt on terminal count mode), binary count, + * load 5 * LATCH count, (LSB and MSB) to begin countdown. + * + * Note some implementations have a bug where the high bits byte + * of channel 2 is ignored. + */ + /* Set up the timer gate, turn off the speaker */ + /* Set the Gate high, disable speaker */ + outb((inb(PPC_PORTB) & ~PPCB_SPKR) | PPCB_T2GATE, PPC_PORTB); + /* binary, mode 0, LSB/MSB, Ch 2 */ + outb(TIMER2_SEL|WORD_ACCESS|MODE0|BINARY_COUNT, TIMER_MODE_PORT); + /* LSB of ticks */ + outb(ticks & 0xFF, TIMER2_PORT); + /* MSB of ticks */ + outb(ticks >> 8, TIMER2_PORT); +} + +static int timer2_running ( void ) { + return ((inb(PPC_PORTB) & PPCB_T2OUT) == 0); +} + +void timer2_udelay ( unsigned long usecs ) { + load_timer2 ( ( usecs * TIMER2_TICKS_PER_SEC ) / ( 1000 * 1000 ) ); + while (timer2_running()) { + /* Do nothing */ + } +} diff --git a/gpxe/src/arch/i386/core/video_subr.c b/gpxe/src/arch/i386/core/video_subr.c new file mode 100644 index 0000000..c821cd0 --- /dev/null +++ b/gpxe/src/arch/i386/core/video_subr.c @@ -0,0 +1,104 @@ +/* + * + * modified from linuxbios code + * by Cai Qiang + * + */ + +#include "stddef.h" +#include "string.h" +#include +#include "console.h" +#include +#include "vga.h" + +struct console_driver vga_console; + +static char *vidmem; /* The video buffer */ +static int video_line, video_col; + +#define VIDBUFFER 0xB8000 + +static void memsetw(void *s, int c, unsigned int n) +{ + unsigned int i; + u16 *ss = (u16 *) s; + + for (i = 0; i < n; i++) { + ss[i] = ( u16 ) c; + } +} + +static void video_init(void) +{ + static int inited=0; + + vidmem = (char *)phys_to_virt(VIDBUFFER); + + if (!inited) { + video_line = 0; + video_col = 0; + + memsetw(vidmem, VGA_ATTR_CLR_WHT, 2*1024); // + + inited=1; + } +} + +static void video_scroll(void) +{ + int i; + + memcpy(vidmem, vidmem + COLS * 2, (LINES - 1) * COLS * 2); + for (i = (LINES - 1) * COLS * 2; i < LINES * COLS * 2; i += 2) + vidmem[i] = ' '; +} + +static void vga_putc(int byte) +{ + if (byte == '\n') { + video_line++; + video_col = 0; + + } else if (byte == '\r') { + video_col = 0; + + } else if (byte == '\b') { + video_col--; + + } else if (byte == '\t') { + video_col += 4; + + } else if (byte == '\a') { + //beep + //beep(500); + + } else { + vidmem[((video_col + (video_line *COLS)) * 2)] = byte; + vidmem[((video_col + (video_line *COLS)) * 2) +1] = VGA_ATTR_CLR_WHT; + video_col++; + } + if (video_col < 0) { + video_col = 0; + } + if (video_col >= COLS) { + video_line++; + video_col = 0; + } + if (video_line >= LINES) { + video_scroll(); + video_line--; + } + // move the cursor + write_crtc((video_col + (video_line *COLS)) >> 8, CRTC_CURSOR_HI); + write_crtc((video_col + (video_line *COLS)) & 0x0ff, CRTC_CURSOR_LO); +} + +struct console_driver vga_console __console_driver = { + .putchar = vga_putc, + .disabled = 1, +}; + +struct init_fn video_init_fn __init_fn ( INIT_EARLY ) = { + .initialise = video_init, +}; diff --git a/gpxe/src/arch/i386/core/virtaddr.S b/gpxe/src/arch/i386/core/virtaddr.S new file mode 100644 index 0000000..cf6da4f --- /dev/null +++ b/gpxe/src/arch/i386/core/virtaddr.S @@ -0,0 +1,101 @@ +/* + * Functions to support the virtual addressing method of relocation + * that Etherboot uses. + * + */ + +#include "librm.h" + + .arch i386 + .text + .code32 + +/**************************************************************************** + * _virt_to_phys (virtual addressing) + * + * Switch from virtual to flat physical addresses. %esp is adjusted + * to a physical value. Segment registers are set to flat physical + * selectors. All other registers are preserved. Flags are + * preserved. + * + * Parameters: none + * Returns: none + **************************************************************************** + */ + .globl _virt_to_phys +_virt_to_phys: + /* Preserve registers and flags */ + pushfl + pushl %eax + pushl %ebp + + /* Change return address to a physical address */ + movl virt_offset, %ebp + addl %ebp, 12(%esp) + + /* Switch to physical code segment */ + pushl $PHYSICAL_CS + leal 1f(%ebp), %eax + pushl %eax + lret +1: + /* Reload other segment registers and adjust %esp */ + movl $PHYSICAL_DS, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %fs + movl %eax, %gs + movl %eax, %ss + addl %ebp, %esp + + /* Restore registers and flags, and return */ + popl %ebp + popl %eax + popfl + ret + +/**************************************************************************** + * _phys_to_virt (flat physical addressing) + * + * Switch from flat physical to virtual addresses. %esp is adjusted + * to a virtual value. Segment registers are set to virtual + * selectors. All other registers are preserved. Flags are + * preserved. + * + * Note that this depends on the GDT already being correctly set up + * (e.g. by a call to run_here()). + * + * Parameters: none + * Returns: none + **************************************************************************** + */ + .globl _phys_to_virt +_phys_to_virt: + /* Preserve registers and flags */ + pushfl + pushl %eax + pushl %ebp + + /* Switch to virtual code segment */ + ljmp $VIRTUAL_CS, $1f +1: + /* Reload data segment registers */ + movl $VIRTUAL_DS, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %fs + movl %eax, %gs + + /* Reload stack segment and adjust %esp */ + movl virt_offset, %ebp + movl %eax, %ss + subl %ebp, %esp + + /* Change the return address to a virtual address */ + subl %ebp, 12(%esp) + + /* Restore registers and flags, and return */ + popl %ebp + popl %eax + popfl + ret diff --git a/gpxe/src/arch/i386/core/wince_loader.c b/gpxe/src/arch/i386/core/wince_loader.c new file mode 100644 index 0000000..f452b65 --- /dev/null +++ b/gpxe/src/arch/i386/core/wince_loader.c @@ -0,0 +1,273 @@ +#define LOAD_DEBUG 0 + +static int get_x_header(unsigned char *data, unsigned long now); +static void jump_2ep(); +static unsigned char ce_signature[] = {'B', '0', '0', '0', 'F', 'F', '\n',}; +static char ** ep; + +#define BOOT_ARG_PTR_LOCATION 0x001FFFFC + +typedef struct _BOOT_ARGS{ + unsigned char ucVideoMode; + unsigned char ucComPort; + unsigned char ucBaudDivisor; + unsigned char ucPCIConfigType; + + unsigned long dwSig; + #define BOOTARG_SIG 0x544F4F42 + unsigned long dwLen; + + unsigned char ucLoaderFlags; + unsigned char ucEshellFlags; + unsigned char ucEdbgAdapterType; + unsigned char ucEdbgIRQ; + + unsigned long dwEdbgBaseAddr; + unsigned long dwEdbgDebugZone; + unsigned long dwDHCPLeaseTime; + unsigned long dwEdbgFlags; + + unsigned long dwEBootFlag; + unsigned long dwEBootAddr; + unsigned long dwLaunchAddr; + + unsigned long pvFlatFrameBuffer; + unsigned short vesaMode; + unsigned short cxDisplayScreen; + unsigned short cyDisplayScreen; + unsigned short cxPhysicalScreen; + unsigned short cyPhysicalScreen; + unsigned short cbScanLineLength; + unsigned short bppScreen; + + unsigned char RedMaskSize; + unsigned char REdMaskPosition; + unsigned char GreenMaskSize; + unsigned char GreenMaskPosition; + unsigned char BlueMaskSize; + unsigned char BlueMaskPosition; +} BOOT_ARGS; + +BOOT_ARGS BootArgs; + +static struct segment_info{ + unsigned long addr; // Section Address + unsigned long size; // Section Size + unsigned long checksum; // Section CheckSum +} X; + +#define PSIZE (1500) //Max Packet Size +#define DSIZE (PSIZE+12) +static unsigned long dbuffer_available =0; +static unsigned long not_loadin =0; +static unsigned long d_now =0; + +unsigned long entry; +static unsigned long ce_curaddr; + + +static sector_t ce_loader(unsigned char *data, unsigned int len, int eof); +static os_download_t wince_probe(unsigned char *data, unsigned int len) +{ + if (strncmp(ce_signature, data, sizeof(ce_signature)) != 0) { + return 0; + } + printf("(WINCE)"); + return ce_loader; +} + +static sector_t ce_loader(unsigned char *data, unsigned int len, int eof) +{ + static unsigned char dbuffer[DSIZE]; + int this_write = 0; + static int firsttime = 1; + + /* + * new packet in, we have to + * [1] copy data to dbuffer, + * + * update... + * [2] dbuffer_available + */ + memcpy( (dbuffer+dbuffer_available), data, len); //[1] + dbuffer_available += len; // [2] + len = 0; + + d_now = 0; + +#if 0 + printf("dbuffer_available =%ld \n", dbuffer_available); +#endif + + if (firsttime) + { + d_now = sizeof(ce_signature); + printf("String Physical Address = %lx \n", + *(unsigned long *)(dbuffer+d_now)); + + d_now += sizeof(unsigned long); + printf("Image Size = %ld [%lx]\n", + *(unsigned long *)(dbuffer+d_now), + *(unsigned long *)(dbuffer+d_now)); + + d_now += sizeof(unsigned long); + dbuffer_available -= d_now; + + d_now = (unsigned long)get_x_header(dbuffer, d_now); + firsttime = 0; + } + + if (not_loadin == 0) + { + d_now = get_x_header(dbuffer, d_now); + } + + while ( not_loadin > 0 ) + { + /* dbuffer do not have enough data to loading, copy all */ +#if LOAD_DEBUG + printf("[0] not_loadin = [%ld], dbuffer_available = [%ld] \n", + not_loadin, dbuffer_available); + printf("[0] d_now = [%ld] \n", d_now); +#endif + + if( dbuffer_available <= not_loadin) + { + this_write = dbuffer_available ; + memcpy(phys_to_virt(ce_curaddr), (dbuffer+d_now), this_write ); + ce_curaddr += this_write; + not_loadin -= this_write; + + /* reset index and available in the dbuffer */ + dbuffer_available = 0; + d_now = 0; +#if LOAD_DEBUG + printf("[1] not_loadin = [%ld], dbuffer_available = [%ld] \n", + not_loadin, dbuffer_available); + printf("[1] d_now = [%ld], this_write = [%d] \n", + d_now, this_write); +#endif + + // get the next packet... + return (0); + } + + /* dbuffer have more data then loading ... , copy partital.... */ + else + { + this_write = not_loadin; + memcpy(phys_to_virt(ce_curaddr), (dbuffer+d_now), this_write); + ce_curaddr += this_write; + not_loadin = 0; + + /* reset index and available in the dbuffer */ + dbuffer_available -= this_write; + d_now += this_write; +#if LOAD_DEBUG + printf("[2] not_loadin = [%ld], dbuffer_available = [%ld] \n", + not_loadin, dbuffer_available); + printf("[2] d_now = [%ld], this_write = [%d] \n\n", + d_now, this_write); +#endif + + /* dbuffer not empty, proceed processing... */ + + // don't have enough data to get_x_header.. + if ( dbuffer_available < (sizeof(unsigned long) * 3) ) + { +// printf("we don't have enough data remaining to call get_x. \n"); + memcpy( (dbuffer+0), (dbuffer+d_now), dbuffer_available); + return (0); + } + else + { +#if LOAD_DEBUG + printf("with remaining data to call get_x \n"); + printf("dbuffer available = %ld , d_now = %ld\n", + dbuffer_available, d_now); +#endif + d_now = get_x_header(dbuffer, d_now); + } + } + } + return (0); +} + +static int get_x_header(unsigned char *dbuffer, unsigned long now) +{ + X.addr = *(unsigned long *)(dbuffer + now); + X.size = *(unsigned long *)(dbuffer + now + sizeof(unsigned long)); + X.checksum = *(unsigned long *)(dbuffer + now + sizeof(unsigned long)*2); + + if (X.addr == 0) + { + entry = X.size; + done(1); + printf("Entry Point Address = [%lx] \n", entry); + jump_2ep(); + } + + if (!prep_segment(X.addr, X.addr + X.size, X.addr + X.size, 0, 0)) { + longjmp(restart_etherboot, -2); + } + + ce_curaddr = X.addr; + now += sizeof(unsigned long)*3; + + /* re-calculate dbuffer available... */ + dbuffer_available -= sizeof(unsigned long)*3; + + /* reset index of this section */ + not_loadin = X.size; + +#if 1 + printf("\n"); + printf("\t Section Address = [%lx] \n", X.addr); + printf("\t Size = %d [%lx]\n", X.size, X.size); + printf("\t Checksum = %ld [%lx]\n", X.checksum, X.checksum); +#endif +#if LOAD_DEBUG + printf("____________________________________________\n"); + printf("\t dbuffer_now = %ld \n", now); + printf("\t dbuffer available = %ld \n", dbuffer_available); + printf("\t not_loadin = %ld \n", not_loadin); +#endif + + return now; +} + +static void jump_2ep() +{ + BootArgs.ucVideoMode = 1; + BootArgs.ucComPort = 1; + BootArgs.ucBaudDivisor = 1; + BootArgs.ucPCIConfigType = 1; // do not fill with 0 + + BootArgs.dwSig = BOOTARG_SIG; + BootArgs.dwLen = sizeof(BootArgs); + + if(BootArgs.ucVideoMode == 0) + { + BootArgs.cxDisplayScreen = 640; + BootArgs.cyDisplayScreen = 480; + BootArgs.cxPhysicalScreen = 640; + BootArgs.cyPhysicalScreen = 480; + BootArgs.bppScreen = 16; + BootArgs.cbScanLineLength = 1024; + BootArgs.pvFlatFrameBuffer = 0x800a0000; // ollie say 0x98000000 + } + else if(BootArgs.ucVideoMode != 0xFF) + { + BootArgs.cxDisplayScreen = 0; + BootArgs.cyDisplayScreen = 0; + BootArgs.cxPhysicalScreen = 0; + BootArgs.cyPhysicalScreen = 0; + BootArgs.bppScreen = 0; + BootArgs.cbScanLineLength = 0; + BootArgs.pvFlatFrameBuffer = 0; + } + + ep = phys_to_virt(BOOT_ARG_PTR_LOCATION); + *ep= virt_to_phys(&BootArgs); + xstart32(entry); +} diff --git a/gpxe/src/arch/i386/core/x86_io.c b/gpxe/src/arch/i386/core/x86_io.c new file mode 100644 index 0000000..424a96c --- /dev/null +++ b/gpxe/src/arch/i386/core/x86_io.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include + +/** @file + * + * gPXE I/O API for x86 + * + */ + +/** + * Read 64-bit qword from memory-mapped device + * + * @v io_addr I/O address + * @ret data Value read + * + * This routine uses MMX instructions. + */ +static uint64_t x86_readq ( volatile uint64_t *io_addr ) { + uint64_t data; + __asm__ __volatile__ ( "pushl %%edx\n\t" + "pushl %%eax\n\t" + "movq (%1), %%mm0\n\t" + "movq %%mm0, (%%esp)\n\t" + "popl %%eax\n\t" + "popl %%edx\n\t" + "emms\n\t" + : "=A" ( data ) : "r" ( io_addr ) ); + return data; +} + +/** + * Write 64-bit qword to memory-mapped device + * + * @v data Value to write + * @v io_addr I/O address + * + * This routine uses MMX instructions. + */ +static void x86_writeq ( uint64_t data, volatile uint64_t *io_addr ) { + __asm__ __volatile__ ( "pushl %%edx\n\t" + "pushl %%eax\n\t" + "movq (%%esp), %%mm0\n\t" + "movq %%mm0, (%1)\n\t" + "popl %%eax\n\t" + "popl %%edx\n\t" + "emms\n\t" + : : "A" ( data ), "r" ( io_addr ) ); +} + +PROVIDE_IOAPI_INLINE ( x86, phys_to_bus ); +PROVIDE_IOAPI_INLINE ( x86, bus_to_phys ); +PROVIDE_IOAPI_INLINE ( x86, ioremap ); +PROVIDE_IOAPI_INLINE ( x86, iounmap ); +PROVIDE_IOAPI_INLINE ( x86, io_to_bus ); +PROVIDE_IOAPI_INLINE ( x86, readb ); +PROVIDE_IOAPI_INLINE ( x86, readw ); +PROVIDE_IOAPI_INLINE ( x86, readl ); +PROVIDE_IOAPI ( x86, readq, x86_readq ); +PROVIDE_IOAPI_INLINE ( x86, writeb ); +PROVIDE_IOAPI_INLINE ( x86, writew ); +PROVIDE_IOAPI_INLINE ( x86, writel ); +PROVIDE_IOAPI ( x86, writeq, x86_writeq ); +PROVIDE_IOAPI_INLINE ( x86, inb ); +PROVIDE_IOAPI_INLINE ( x86, inw ); +PROVIDE_IOAPI_INLINE ( x86, inl ); +PROVIDE_IOAPI_INLINE ( x86, outb ); +PROVIDE_IOAPI_INLINE ( x86, outw ); +PROVIDE_IOAPI_INLINE ( x86, outl ); +PROVIDE_IOAPI_INLINE ( x86, insb ); +PROVIDE_IOAPI_INLINE ( x86, insw ); +PROVIDE_IOAPI_INLINE ( x86, insl ); +PROVIDE_IOAPI_INLINE ( x86, outsb ); +PROVIDE_IOAPI_INLINE ( x86, outsw ); +PROVIDE_IOAPI_INLINE ( x86, outsl ); +PROVIDE_IOAPI_INLINE ( x86, iodelay ); +PROVIDE_IOAPI_INLINE ( x86, mb ); diff --git a/gpxe/src/arch/i386/drivers/net/undi.c b/gpxe/src/arch/i386/drivers/net/undi.c new file mode 100644 index 0000000..1090cc9 --- /dev/null +++ b/gpxe/src/arch/i386/drivers/net/undi.c @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * UNDI PCI driver + * + */ + +/** + * Find UNDI ROM for PCI device + * + * @v pci PCI device + * @ret undirom UNDI ROM, or NULL + * + * Try to find a driver for this device. Try an exact match on the + * ROM address first, then fall back to a vendor/device ID match only + */ +static struct undi_rom * undipci_find_rom ( struct pci_device *pci ) { + struct undi_rom *undirom; + unsigned long rombase; + + rombase = pci_bar_start ( pci, PCI_ROM_ADDRESS ); + undirom = undirom_find_pci ( pci->vendor, pci->device, rombase ); + if ( ! undirom ) + undirom = undirom_find_pci ( pci->vendor, pci->device, 0 ); + return undirom; +} + +/** + * Probe PCI device + * + * @v pci PCI device + * @v id PCI ID + * @ret rc Return status code + */ +static int undipci_probe ( struct pci_device *pci, + const struct pci_device_id *id __unused ) { + struct undi_device *undi; + struct undi_rom *undirom; + unsigned int busdevfn = PCI_BUSDEVFN ( pci->bus, pci->devfn ); + int rc; + + /* Ignore non-network devices */ + if ( PCI_BASE_CLASS ( pci->class ) != PCI_BASE_CLASS_NETWORK ) + return -ENOTTY; + + /* Allocate UNDI device structure */ + undi = zalloc ( sizeof ( *undi ) ); + if ( ! undi ) + return -ENOMEM; + pci_set_drvdata ( pci, undi ); + + /* Find/create our pixie */ + if ( preloaded_undi.pci_busdevfn == busdevfn ) { + /* Claim preloaded UNDI device */ + DBGC ( undi, "UNDI %p using preloaded UNDI device\n", undi ); + memcpy ( undi, &preloaded_undi, sizeof ( *undi ) ); + memset ( &preloaded_undi, 0, sizeof ( preloaded_undi ) ); + } else { + /* Find UNDI ROM for PCI device */ + if ( ! ( undirom = undipci_find_rom ( pci ) ) ) { + rc = -ENODEV; + goto err_find_rom; + } + + /* Call UNDI ROM loader to create pixie */ + if ( ( rc = undi_load_pci ( undi, undirom, busdevfn ) ) != 0 ) + goto err_load_pci; + } + + /* Add to device hierarchy */ + snprintf ( undi->dev.name, sizeof ( undi->dev.name ), + "UNDI-%s", pci->dev.name ); + memcpy ( &undi->dev.desc, &pci->dev.desc, sizeof ( undi->dev.desc ) ); + undi->dev.parent = &pci->dev; + INIT_LIST_HEAD ( &undi->dev.children ); + list_add ( &undi->dev.siblings, &pci->dev.children ); + + /* Create network device */ + if ( ( rc = undinet_probe ( undi ) ) != 0 ) + goto err_undinet_probe; + + return 0; + + err_undinet_probe: + undi_unload ( undi ); + list_del ( &undi->dev.siblings ); + err_find_rom: + err_load_pci: + free ( undi ); + pci_set_drvdata ( pci, NULL ); + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void undipci_remove ( struct pci_device *pci ) { + struct undi_device *undi = pci_get_drvdata ( pci ); + + undinet_remove ( undi ); + undi_unload ( undi ); + list_del ( &undi->dev.siblings ); + free ( undi ); + pci_set_drvdata ( pci, NULL ); +} + +static struct pci_device_id undipci_nics[] = { +PCI_ROM ( 0xffff, 0xffff, "undipci", "UNDI (PCI)" ), +}; + +struct pci_driver undipci_driver __pci_driver = { + .ids = undipci_nics, + .id_count = ( sizeof ( undipci_nics ) / sizeof ( undipci_nics[0] ) ), + .probe = undipci_probe, + .remove = undipci_remove, +}; diff --git a/gpxe/src/arch/i386/drivers/net/undiisr.S b/gpxe/src/arch/i386/drivers/net/undiisr.S new file mode 100644 index 0000000..2b31b41 --- /dev/null +++ b/gpxe/src/arch/i386/drivers/net/undiisr.S @@ -0,0 +1,85 @@ +#define PXENV_UNDI_ISR 0x0014 +#define PXENV_UNDI_ISR_IN_START 1 +#define PXENV_UNDI_ISR_OUT_OURS 0 +#define PXENV_UNDI_ISR_OUT_NOT_OURS 1 + +#define IRQ_PIC_CUTOFF 8 +#define ICR_EOI_NON_SPECIFIC 0x20 +#define PIC1_ICR 0x20 +#define PIC2_ICR 0xa0 + + .text + .arch i386 + .code16 + + .section ".text16", "ax", @progbits + .globl undiisr +undiisr: + + /* Preserve registers */ + pushw %ds + pushw %es + pushw %fs + pushw %gs + pushfl + pushal + + /* Set up our segment registers */ + movw %cs:rm_ds, %ax + movw %ax, %ds + + /* Check that we have an UNDI entry point */ + cmpw $0, undinet_entry_point + je chain + + /* Issue UNDI API call */ + movw %ax, %es + movw $undinet_params, %di + movw $PXENV_UNDI_ISR, %bx + movw $PXENV_UNDI_ISR_IN_START, funcflag + pushw %es + pushw %di + pushw %bx + lcall *undinet_entry_point + cli /* Just in case */ + addw $6, %sp + cmpw $PXENV_UNDI_ISR_OUT_OURS, funcflag + jne eoi + +trig: /* Record interrupt occurence */ + incb undiisr_trigger_count + +eoi: /* Send EOI */ + movb $ICR_EOI_NON_SPECIFIC, %al + cmpb $IRQ_PIC_CUTOFF, undiisr_irq + jb 1f + outb %al, $PIC2_ICR +1: outb %al, $PIC1_ICR + jmp exit + +chain: /* Chain to next handler */ + pushfw + lcall *undiisr_next_handler + +exit: /* Restore registers and return */ + cli + popal + movzwl %sp, %esp + addr32 movl -20(%esp), %esp /* %esp isn't restored by popal */ + popfl + popw %gs + popw %fs + popw %es + popw %ds + iret + + .section ".data16", "aw", @progbits +undinet_params: +status: .word 0 +funcflag: .word 0 +bufferlength: .word 0 +framelength: .word 0 +frameheaderlength: .word 0 +frame: .word 0, 0 +prottype: .byte 0 +pkttype: .byte 0 diff --git a/gpxe/src/arch/i386/drivers/net/undiload.c b/gpxe/src/arch/i386/drivers/net/undiload.c new file mode 100644 index 0000000..dbd9e7c --- /dev/null +++ b/gpxe/src/arch/i386/drivers/net/undiload.c @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * UNDI load/unload + * + */ + +/** Parameter block for calling UNDI loader */ +static struct s_UNDI_LOADER __bss16 ( undi_loader ); +#define undi_loader __use_data16 ( undi_loader ) + +/** UNDI loader entry point */ +static SEGOFF16_t __bss16 ( undi_loader_entry ); +#define undi_loader_entry __use_data16 ( undi_loader_entry ) + +/** + * Call UNDI loader to create a pixie + * + * @v undi UNDI device + * @v undirom UNDI ROM + * @ret rc Return status code + */ +int undi_load ( struct undi_device *undi, struct undi_rom *undirom ) { + struct s_PXE ppxe; + unsigned int fbms_seg; + uint16_t exit; + int rc; + + /* Set up START_UNDI parameters */ + memset ( &undi_loader, 0, sizeof ( undi_loader ) ); + undi_loader.AX = undi->pci_busdevfn; + undi_loader.BX = undi->isapnp_csn; + undi_loader.DX = undi->isapnp_read_port; + undi_loader.ES = BIOS_SEG; + undi_loader.DI = find_pnp_bios(); + + /* Allocate base memory for PXE stack */ + undi->restore_fbms = get_fbms(); + fbms_seg = ( undi->restore_fbms << 6 ); + fbms_seg -= ( ( undirom->code_size + 0x0f ) >> 4 ); + undi_loader.UNDI_CS = fbms_seg; + fbms_seg -= ( ( undirom->data_size + 0x0f ) >> 4 ); + undi_loader.UNDI_DS = fbms_seg; + + /* Debug info */ + DBGC ( undi, "UNDI %p loading UNDI ROM %p to CS %04x DS %04x for ", + undi, undirom, undi_loader.UNDI_CS, undi_loader.UNDI_DS ); + if ( undi->pci_busdevfn != UNDI_NO_PCI_BUSDEVFN ) { + unsigned int bus = ( undi->pci_busdevfn >> 8 ); + unsigned int devfn = ( undi->pci_busdevfn & 0xff ); + DBGC ( undi, "PCI %02x:%02x.%x\n", + bus, PCI_SLOT ( devfn ), PCI_FUNC ( devfn ) ); + } + if ( undi->isapnp_csn != UNDI_NO_ISAPNP_CSN ) { + DBGC ( undi, "ISAPnP(%04x) CSN %04x\n", + undi->isapnp_read_port, undi->isapnp_csn ); + } + + /* Call loader */ + undi_loader_entry = undirom->loader_entry; + __asm__ __volatile__ ( REAL_CODE ( "pushw %%ds\n\t" + "pushw %%ax\n\t" + "lcall *%c2\n\t" + "addw $4, %%sp\n\t" ) + : "=a" ( exit ) + : "a" ( __from_data16 ( &undi_loader ) ), + "p" ( __from_data16 ( &undi_loader_entry ) ) + : "ebx", "ecx", "edx", "esi", "edi", "ebp" ); + + /* UNDI API calls may rudely change the status of A20 and not + * bother to restore it afterwards. Intel is known to be + * guilty of this. + * + * Note that we will return to this point even if A20 gets + * screwed up by the UNDI driver, because Etherboot always + * resides in an even megabyte of RAM. + */ + gateA20_set(); + + if ( exit != PXENV_EXIT_SUCCESS ) { + rc = -undi_loader.Status; + if ( rc == 0 ) /* Paranoia */ + rc = -EIO; + DBGC ( undi, "UNDI %p loader failed: %s\n", + undi, strerror ( rc ) ); + return rc; + } + + /* Populate PXE device structure */ + undi->pxenv = undi_loader.PXENVptr; + undi->ppxe = undi_loader.PXEptr; + copy_from_real ( &ppxe, undi->ppxe.segment, undi->ppxe.offset, + sizeof ( ppxe ) ); + undi->entry = ppxe.EntryPointSP; + DBGC ( undi, "UNDI %p loaded PXENV+ %04x:%04x !PXE %04x:%04x " + "entry %04x:%04x\n", undi, undi->pxenv.segment, + undi->pxenv.offset, undi->ppxe.segment, undi->ppxe.offset, + undi->entry.segment, undi->entry.offset ); + + /* Update free base memory counter */ + undi->fbms = ( fbms_seg >> 6 ); + set_fbms ( undi->fbms ); + DBGC ( undi, "UNDI %p using [%d,%d) kB of base memory\n", + undi, undi->fbms, undi->restore_fbms ); + + return 0; +} + +/** + * Unload a pixie + * + * @v undi UNDI device + * @ret rc Return status code + * + * Erases the PXENV+ and !PXE signatures, and frees the used base + * memory (if possible). + */ +int undi_unload ( struct undi_device *undi ) { + static uint32_t dead = 0xdeaddead; + + DBGC ( undi, "UNDI %p unloading\n", undi ); + + /* Erase signatures */ + if ( undi->pxenv.segment ) + put_real ( dead, undi->pxenv.segment, undi->pxenv.offset ); + if ( undi->ppxe.segment ) + put_real ( dead, undi->ppxe.segment, undi->ppxe.offset ); + + /* Free base memory, if possible */ + if ( undi->fbms == get_fbms() ) { + DBGC ( undi, "UNDI %p freeing [%d,%d) kB of base memory\n", + undi, undi->fbms, undi->restore_fbms ); + set_fbms ( undi->restore_fbms ); + return 0; + } else { + DBGC ( undi, "UNDI %p leaking [%d,%d) kB of base memory\n", + undi, undi->fbms, undi->restore_fbms ); + return -EBUSY; + } +} diff --git a/gpxe/src/arch/i386/drivers/net/undinet.c b/gpxe/src/arch/i386/drivers/net/undinet.c new file mode 100644 index 0000000..d6db6f7 --- /dev/null +++ b/gpxe/src/arch/i386/drivers/net/undinet.c @@ -0,0 +1,796 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/** @file + * + * UNDI network device driver + * + */ + +/** An UNDI NIC */ +struct undi_nic { + /** Assigned IRQ number */ + unsigned int irq; + /** Currently processing ISR */ + int isr_processing; + /** Bug workarounds */ + int hacks; +}; + +/** + * @defgroup undi_hacks UNDI workarounds + * @{ + */ + +/** Work around Etherboot 5.4 bugs */ +#define UNDI_HACK_EB54 0x0001 + +/** @} */ + +static void undinet_close ( struct net_device *netdev ); + +/***************************************************************************** + * + * UNDI API call + * + ***************************************************************************** + */ + +/** + * Name UNDI API call + * + * @v function API call number + * @ret name API call name + */ +static inline __attribute__ (( always_inline )) const char * +undinet_function_name ( unsigned int function ) { + switch ( function ) { + case PXENV_START_UNDI: + return "PXENV_START_UNDI"; + case PXENV_STOP_UNDI: + return "PXENV_STOP_UNDI"; + case PXENV_UNDI_STARTUP: + return "PXENV_UNDI_STARTUP"; + case PXENV_UNDI_CLEANUP: + return "PXENV_UNDI_CLEANUP"; + case PXENV_UNDI_INITIALIZE: + return "PXENV_UNDI_INITIALIZE"; + case PXENV_UNDI_RESET_ADAPTER: + return "PXENV_UNDI_RESET_ADAPTER"; + case PXENV_UNDI_SHUTDOWN: + return "PXENV_UNDI_SHUTDOWN"; + case PXENV_UNDI_OPEN: + return "PXENV_UNDI_OPEN"; + case PXENV_UNDI_CLOSE: + return "PXENV_UNDI_CLOSE"; + case PXENV_UNDI_TRANSMIT: + return "PXENV_UNDI_TRANSMIT"; + case PXENV_UNDI_SET_MCAST_ADDRESS: + return "PXENV_UNDI_SET_MCAST_ADDRESS"; + case PXENV_UNDI_SET_STATION_ADDRESS: + return "PXENV_UNDI_SET_STATION_ADDRESS"; + case PXENV_UNDI_SET_PACKET_FILTER: + return "PXENV_UNDI_SET_PACKET_FILTER"; + case PXENV_UNDI_GET_INFORMATION: + return "PXENV_UNDI_GET_INFORMATION"; + case PXENV_UNDI_GET_STATISTICS: + return "PXENV_UNDI_GET_STATISTICS"; + case PXENV_UNDI_CLEAR_STATISTICS: + return "PXENV_UNDI_CLEAR_STATISTICS"; + case PXENV_UNDI_INITIATE_DIAGS: + return "PXENV_UNDI_INITIATE_DIAGS"; + case PXENV_UNDI_FORCE_INTERRUPT: + return "PXENV_UNDI_FORCE_INTERRUPT"; + case PXENV_UNDI_GET_MCAST_ADDRESS: + return "PXENV_UNDI_GET_MCAST_ADDRESS"; + case PXENV_UNDI_GET_NIC_TYPE: + return "PXENV_UNDI_GET_NIC_TYPE"; + case PXENV_UNDI_GET_IFACE_INFO: + return "PXENV_UNDI_GET_IFACE_INFO"; + /* + * Duplicate case value; this is a bug in the PXE specification. + * + * case PXENV_UNDI_GET_STATE: + * return "PXENV_UNDI_GET_STATE"; + */ + case PXENV_UNDI_ISR: + return "PXENV_UNDI_ISR"; + default: + return "UNKNOWN API CALL"; + } +} + +/** + * UNDI parameter block + * + * Used as the paramter block for all UNDI API calls. Resides in base + * memory. + */ +static union u_PXENV_ANY __bss16 ( undinet_params ); +#define undinet_params __use_data16 ( undinet_params ) + +/** UNDI entry point + * + * Used as the indirection vector for all UNDI API calls. Resides in + * base memory. + */ +SEGOFF16_t __bss16 ( undinet_entry_point ); +#define undinet_entry_point __use_data16 ( undinet_entry_point ) + +/** + * Issue UNDI API call + * + * @v undinic UNDI NIC + * @v function API call number + * @v params UNDI parameter block + * @v params_len Length of UNDI parameter block + * @ret rc Return status code + */ +static int undinet_call ( struct undi_nic *undinic, unsigned int function, + void *params, size_t params_len ) { + PXENV_EXIT_t exit; + int discard_b, discard_D; + int rc; + + /* Copy parameter block and entry point */ + assert ( params_len <= sizeof ( undinet_params ) ); + memcpy ( &undinet_params, params, params_len ); + + /* Call real-mode entry point. This calling convention will + * work with both the !PXE and the PXENV+ entry points. + */ + __asm__ __volatile__ ( REAL_CODE ( "pushw %%es\n\t" + "pushw %%di\n\t" + "pushw %%bx\n\t" + "lcall *%c3\n\t" + "addw $6, %%sp\n\t" ) + : "=a" ( exit ), "=b" ( discard_b ), + "=D" ( discard_D ) + : "p" ( __from_data16 ( &undinet_entry_point )), + "b" ( function ), + "D" ( __from_data16 ( &undinet_params ) ) + : "ecx", "edx", "esi", "ebp" ); + + /* UNDI API calls may rudely change the status of A20 and not + * bother to restore it afterwards. Intel is known to be + * guilty of this. + * + * Note that we will return to this point even if A20 gets + * screwed up by the UNDI driver, because Etherboot always + * resides in an even megabyte of RAM. + */ + gateA20_set(); + + /* Determine return status code based on PXENV_EXIT and + * PXENV_STATUS + */ + if ( exit == PXENV_EXIT_SUCCESS ) { + rc = 0; + } else { + rc = -undinet_params.Status; + /* Paranoia; don't return success for the combination + * of PXENV_EXIT_FAILURE but PXENV_STATUS_SUCCESS + */ + if ( rc == 0 ) + rc = -EIO; + } + + /* If anything goes wrong, print as much debug information as + * it's possible to give. + */ + if ( rc != 0 ) { + SEGOFF16_t rm_params = { + .segment = rm_ds, + .offset = __from_data16 ( &undinet_params ), + }; + + DBGC ( undinic, "UNDINIC %p %s failed: %s\n", undinic, + undinet_function_name ( function ), strerror ( rc ) ); + DBGC ( undinic, "UNDINIC %p parameters at %04x:%04x length " + "%#02zx, entry point at %04x:%04x\n", undinic, + rm_params.segment, rm_params.offset, params_len, + undinet_entry_point.segment, + undinet_entry_point.offset ); + DBGC ( undinic, "UNDINIC %p parameters provided:\n", undinic ); + DBGC_HDA ( undinic, rm_params, params, params_len ); + DBGC ( undinic, "UNDINIC %p parameters returned:\n", undinic ); + DBGC_HDA ( undinic, rm_params, &undinet_params, params_len ); + } + + /* Copy parameter block back */ + memcpy ( params, &undinet_params, params_len ); + + return rc; +} + +/***************************************************************************** + * + * UNDI interrupt service routine + * + ***************************************************************************** + */ + +/** + * UNDI interrupt service routine + * + * The UNDI ISR increments a counter (@c trigger_count) and exits. + */ +extern void undiisr ( void ); + +/** IRQ number */ +uint8_t __data16 ( undiisr_irq ); +#define undiisr_irq __use_data16 ( undiisr_irq ) + +/** IRQ chain vector */ +struct segoff __data16 ( undiisr_next_handler ); +#define undiisr_next_handler __use_data16 ( undiisr_next_handler ) + +/** IRQ trigger count */ +volatile uint8_t __data16 ( undiisr_trigger_count ) = 0; +#define undiisr_trigger_count __use_data16 ( undiisr_trigger_count ) + +/** Last observed trigger count */ +static unsigned int last_trigger_count = 0; + +/** + * Hook UNDI interrupt service routine + * + * @v irq IRQ number + */ +static void undinet_hook_isr ( unsigned int irq ) { + + assert ( irq <= IRQ_MAX ); + assert ( undiisr_irq == 0 ); + + undiisr_irq = irq; + hook_bios_interrupt ( IRQ_INT ( irq ), + ( ( unsigned int ) undiisr ), + &undiisr_next_handler ); +} + +/** + * Unhook UNDI interrupt service routine + * + * @v irq IRQ number + */ +static void undinet_unhook_isr ( unsigned int irq ) { + + assert ( irq <= IRQ_MAX ); + + unhook_bios_interrupt ( IRQ_INT ( irq ), + ( ( unsigned int ) undiisr ), + &undiisr_next_handler ); + undiisr_irq = 0; +} + +/** + * Test to see if UNDI ISR has been triggered + * + * @ret triggered ISR has been triggered since last check + */ +static int undinet_isr_triggered ( void ) { + unsigned int this_trigger_count; + + /* Read trigger_count. Do this only once; it is volatile */ + this_trigger_count = undiisr_trigger_count; + + if ( this_trigger_count == last_trigger_count ) { + /* Not triggered */ + return 0; + } else { + /* Triggered */ + last_trigger_count = this_trigger_count; + return 1; + } +} + +/***************************************************************************** + * + * UNDI network device interface + * + ***************************************************************************** + */ + +/** UNDI transmit buffer descriptor */ +static struct s_PXENV_UNDI_TBD __data16 ( undinet_tbd ); +#define undinet_tbd __use_data16 ( undinet_tbd ) + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int undinet_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct undi_nic *undinic = netdev->priv; + struct s_PXENV_UNDI_TRANSMIT undi_transmit; + size_t len = iob_len ( iobuf ); + int rc; + + /* Technically, we ought to make sure that the previous + * transmission has completed before we re-use the buffer. + * However, many PXE stacks (including at least some Intel PXE + * stacks and Etherboot 5.4) fail to generate TX completions. + * In practice this won't be a problem, since our TX datapath + * has a very low packet volume and we can get away with + * assuming that a TX will be complete by the time we want to + * transmit the next packet. + */ + + /* Copy packet to UNDI I/O buffer */ + if ( len > sizeof ( basemem_packet ) ) + len = sizeof ( basemem_packet ); + memcpy ( &basemem_packet, iobuf->data, len ); + + /* Create PXENV_UNDI_TRANSMIT data structure */ + memset ( &undi_transmit, 0, sizeof ( undi_transmit ) ); + undi_transmit.DestAddr.segment = rm_ds; + undi_transmit.DestAddr.offset = __from_data16 ( &undinet_tbd ); + undi_transmit.TBD.segment = rm_ds; + undi_transmit.TBD.offset = __from_data16 ( &undinet_tbd ); + + /* Create PXENV_UNDI_TBD data structure */ + undinet_tbd.ImmedLength = len; + undinet_tbd.Xmit.segment = rm_ds; + undinet_tbd.Xmit.offset = __from_data16 ( basemem_packet ); + + /* Issue PXE API call */ + if ( ( rc = undinet_call ( undinic, PXENV_UNDI_TRANSMIT, + &undi_transmit, + sizeof ( undi_transmit ) ) ) != 0 ) + goto done; + + /* Free I/O buffer */ + netdev_tx_complete ( netdev, iobuf ); + + done: + return rc; +} + +/** + * Poll for received packets + * + * @v netdev Network device + * + * Fun, fun, fun. UNDI drivers don't use polling; they use + * interrupts. We therefore cheat and pretend that an interrupt has + * occurred every time undinet_poll() is called. This isn't too much + * of a hack; PCI devices share IRQs and so the first thing that a + * proper ISR should do is call PXENV_UNDI_ISR to determine whether or + * not the UNDI NIC generated the interrupt; there is no harm done by + * spurious calls to PXENV_UNDI_ISR. Similarly, we wouldn't be + * handling them any more rapidly than the usual rate of + * undinet_poll() being called even if we did implement a full ISR. + * So it should work. Ha! + * + * Addendum (21/10/03). Some cards don't play nicely with this trick, + * so instead of doing it the easy way we have to go to all the hassle + * of installing a genuine interrupt service routine and dealing with + * the wonderful 8259 Programmable Interrupt Controller. Joy. + * + * Addendum (10/07/07). When doing things such as iSCSI boot, in + * which we have to co-operate with a running OS, we can't get away + * with the "ISR-just-increments-a-counter-and-returns" trick at all, + * because it involves tying up the PIC for far too long, and other + * interrupt-dependent components (e.g. local disks) start breaking. + * We therefore implement a "proper" ISR which calls PXENV_UNDI_ISR + * from within interrupt context in order to deassert the device + * interrupt, and sends EOI if applicable. + */ +static void undinet_poll ( struct net_device *netdev ) { + struct undi_nic *undinic = netdev->priv; + struct s_PXENV_UNDI_ISR undi_isr; + struct io_buffer *iobuf = NULL; + size_t len; + size_t frag_len; + size_t max_frag_len; + int rc; + + if ( ! undinic->isr_processing ) { + /* Do nothing unless ISR has been triggered */ + if ( ! undinet_isr_triggered() ) { + /* Allow interrupt to occur */ + __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" + "nop\n\t" + "nop\n\t" + "cli\n\t" ) : : ); + return; + } + + /* Start ISR processing */ + undinic->isr_processing = 1; + undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS; + } else { + /* Continue ISR processing */ + undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; + } + + /* Run through the ISR loop */ + while ( 1 ) { + if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr, + sizeof ( undi_isr ) ) ) != 0 ) + break; + switch ( undi_isr.FuncFlag ) { + case PXENV_UNDI_ISR_OUT_TRANSMIT: + /* We don't care about transmit completions */ + break; + case PXENV_UNDI_ISR_OUT_RECEIVE: + /* Packet fragment received */ + len = undi_isr.FrameLength; + frag_len = undi_isr.BufferLength; + if ( ( len == 0 ) || ( len < frag_len ) ) { + /* Don't laugh. VMWare does it. */ + DBGC ( undinic, "UNDINIC %p reported insane " + "fragment (%zd of %zd bytes)\n", + undinic, frag_len, len ); + netdev_rx_err ( netdev, NULL, -EINVAL ); + break; + } + if ( ! iobuf ) + iobuf = alloc_iob ( len ); + if ( ! iobuf ) { + DBGC ( undinic, "UNDINIC %p could not " + "allocate %zd bytes for RX buffer\n", + undinic, len ); + /* Fragment will be dropped */ + netdev_rx_err ( netdev, NULL, -ENOMEM ); + goto done; + } + max_frag_len = iob_tailroom ( iobuf ); + if ( frag_len > max_frag_len ) { + DBGC ( undinic, "UNDINIC %p fragment too big " + "(%zd+%zd does not fit into %zd)\n", + undinic, iob_len ( iobuf ), frag_len, + ( iob_len ( iobuf ) + max_frag_len ) ); + frag_len = max_frag_len; + } + copy_from_real ( iob_put ( iobuf, frag_len ), + undi_isr.Frame.segment, + undi_isr.Frame.offset, frag_len ); + if ( iob_len ( iobuf ) == len ) { + /* Whole packet received; deliver it */ + netdev_rx ( netdev, iob_disown ( iobuf ) ); + /* Etherboot 5.4 fails to return all packets + * under mild load; pretend it retriggered. + */ + if ( undinic->hacks & UNDI_HACK_EB54 ) + --last_trigger_count; + } + break; + case PXENV_UNDI_ISR_OUT_DONE: + /* Processing complete */ + undinic->isr_processing = 0; + goto done; + default: + /* Should never happen. VMWare does it routinely. */ + DBGC ( undinic, "UNDINIC %p ISR returned invalid " + "FuncFlag %04x\n", undinic, undi_isr.FuncFlag ); + undinic->isr_processing = 0; + goto done; + } + undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; + } + + done: + if ( iobuf ) { + DBGC ( undinic, "UNDINIC %p returned incomplete packet " + "(%zd of %zd)\n", undinic, iob_len ( iobuf ), + ( iob_len ( iobuf ) + iob_tailroom ( iobuf ) ) ); + netdev_rx_err ( netdev, iobuf, -EINVAL ); + } +} + +/** + * Open NIC + * + * @v netdev Net device + * @ret rc Return status code + */ +static int undinet_open ( struct net_device *netdev ) { + struct undi_nic *undinic = netdev->priv; + struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_address; + struct s_PXENV_UNDI_OPEN undi_open; + int rc; + + /* Hook interrupt service routine and enable interrupt */ + undinet_hook_isr ( undinic->irq ); + enable_irq ( undinic->irq ); + send_eoi ( undinic->irq ); + + /* Set station address. Required for some PXE stacks; will + * spuriously fail on others. Ignore failures. We only ever + * use it to set the MAC address to the card's permanent value + * anyway. + */ + memcpy ( undi_set_address.StationAddress, netdev->ll_addr, + sizeof ( undi_set_address.StationAddress ) ); + undinet_call ( undinic, PXENV_UNDI_SET_STATION_ADDRESS, + &undi_set_address, sizeof ( undi_set_address ) ); + + /* Open NIC. We ask for promiscuous operation, since it's the + * only way to ask for all multicast addresses. On any + * switched network, it shouldn't really make a difference to + * performance. + */ + memset ( &undi_open, 0, sizeof ( undi_open ) ); + undi_open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST | FLTR_PRMSCS ); + if ( ( rc = undinet_call ( undinic, PXENV_UNDI_OPEN, &undi_open, + sizeof ( undi_open ) ) ) != 0 ) + goto err; + + DBGC ( undinic, "UNDINIC %p opened\n", undinic ); + return 0; + + err: + undinet_close ( netdev ); + return rc; +} + +/** + * Close NIC + * + * @v netdev Net device + */ +static void undinet_close ( struct net_device *netdev ) { + struct undi_nic *undinic = netdev->priv; + struct s_PXENV_UNDI_ISR undi_isr; + struct s_PXENV_UNDI_CLOSE undi_close; + int rc; + + /* Ensure ISR has exited cleanly */ + while ( undinic->isr_processing ) { + undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT; + if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr, + sizeof ( undi_isr ) ) ) != 0 ) + break; + switch ( undi_isr.FuncFlag ) { + case PXENV_UNDI_ISR_OUT_TRANSMIT: + case PXENV_UNDI_ISR_OUT_RECEIVE: + /* Continue draining */ + break; + default: + /* Stop processing */ + undinic->isr_processing = 0; + break; + } + } + + /* Close NIC */ + undinet_call ( undinic, PXENV_UNDI_CLOSE, &undi_close, + sizeof ( undi_close ) ); + + /* Disable interrupt and unhook ISR */ + disable_irq ( undinic->irq ); + undinet_unhook_isr ( undinic->irq ); + + DBGC ( undinic, "UNDINIC %p closed\n", undinic ); +} + +/** + * Enable/disable interrupts + * + * @v netdev Net device + * @v enable Interrupts should be enabled + */ +static void undinet_irq ( struct net_device *netdev, int enable ) { + struct undi_nic *undinic = netdev->priv; + + /* Cannot support interrupts yet */ + DBGC ( undinic, "UNDINIC %p cannot %s interrupts\n", + undinic, ( enable ? "enable" : "disable" ) ); +} + +/** UNDI network device operations */ +static struct net_device_operations undinet_operations = { + .open = undinet_open, + .close = undinet_close, + .transmit = undinet_transmit, + .poll = undinet_poll, + .irq = undinet_irq, +}; + +/** + * Probe UNDI device + * + * @v undi UNDI device + * @ret rc Return status code + */ +int undinet_probe ( struct undi_device *undi ) { + struct net_device *netdev; + struct undi_nic *undinic; + struct s_PXENV_START_UNDI start_undi; + struct s_PXENV_UNDI_STARTUP undi_startup; + struct s_PXENV_UNDI_INITIALIZE undi_initialize; + struct s_PXENV_UNDI_GET_INFORMATION undi_info; + struct s_PXENV_UNDI_GET_IFACE_INFO undi_iface; + struct s_PXENV_UNDI_SHUTDOWN undi_shutdown; + struct s_PXENV_UNDI_CLEANUP undi_cleanup; + struct s_PXENV_STOP_UNDI stop_undi; + int rc; + + /* Allocate net device */ + netdev = alloc_etherdev ( sizeof ( *undinic ) ); + if ( ! netdev ) + return -ENOMEM; + netdev_init ( netdev, &undinet_operations ); + undinic = netdev->priv; + undi_set_drvdata ( undi, netdev ); + netdev->dev = &undi->dev; + memset ( undinic, 0, sizeof ( *undinic ) ); + undinet_entry_point = undi->entry; + DBGC ( undinic, "UNDINIC %p using UNDI %p\n", undinic, undi ); + + /* Hook in UNDI stack */ + if ( ! ( undi->flags & UNDI_FL_STARTED ) ) { + memset ( &start_undi, 0, sizeof ( start_undi ) ); + start_undi.AX = undi->pci_busdevfn; + start_undi.BX = undi->isapnp_csn; + start_undi.DX = undi->isapnp_read_port; + start_undi.ES = BIOS_SEG; + start_undi.DI = find_pnp_bios(); + if ( ( rc = undinet_call ( undinic, PXENV_START_UNDI, + &start_undi, + sizeof ( start_undi ) ) ) != 0 ) + goto err_start_undi; + } + undi->flags |= UNDI_FL_STARTED; + + /* Bring up UNDI stack */ + if ( ! ( undi->flags & UNDI_FL_INITIALIZED ) ) { + memset ( &undi_startup, 0, sizeof ( undi_startup ) ); + if ( ( rc = undinet_call ( undinic, PXENV_UNDI_STARTUP, + &undi_startup, + sizeof ( undi_startup ) ) ) != 0 ) + goto err_undi_startup; + memset ( &undi_initialize, 0, sizeof ( undi_initialize ) ); + if ( ( rc = undinet_call ( undinic, PXENV_UNDI_INITIALIZE, + &undi_initialize, + sizeof ( undi_initialize ))) != 0 ) + goto err_undi_initialize; + } + undi->flags |= UNDI_FL_INITIALIZED; + + /* Get device information */ + memset ( &undi_info, 0, sizeof ( undi_info ) ); + if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_INFORMATION, + &undi_info, sizeof ( undi_info ) ) ) != 0 ) + goto err_undi_get_information; + memcpy ( netdev->ll_addr, undi_info.PermNodeAddress, ETH_ALEN ); + undinic->irq = undi_info.IntNumber; + if ( undinic->irq > IRQ_MAX ) { + DBGC ( undinic, "UNDINIC %p invalid IRQ %d\n", + undinic, undinic->irq ); + goto err_bad_irq; + } + DBGC ( undinic, "UNDINIC %p is %s on IRQ %d\n", + undinic, eth_ntoa ( netdev->ll_addr ), undinic->irq ); + + /* Get interface information */ + memset ( &undi_iface, 0, sizeof ( undi_iface ) ); + if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_IFACE_INFO, + &undi_iface, + sizeof ( undi_iface ) ) ) != 0 ) + goto err_undi_get_iface_info; + DBGC ( undinic, "UNDINIC %p has type %s and link speed %d\n", + undinic, undi_iface.IfaceType, undi_iface.LinkSpeed ); + if ( strncmp ( ( ( char * ) undi_iface.IfaceType ), "Etherboot", + sizeof ( undi_iface.IfaceType ) ) == 0 ) { + DBGC ( undinic, "UNDINIC %p Etherboot 5.4 workaround enabled\n", + undinic ); + undinic->hacks |= UNDI_HACK_EB54; + } + + /* Mark as link up; we don't handle link state */ + netdev_link_up ( netdev ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + DBGC ( undinic, "UNDINIC %p added\n", undinic ); + return 0; + + err_register: + err_undi_get_iface_info: + err_bad_irq: + err_undi_get_information: + err_undi_initialize: + /* Shut down UNDI stack */ + memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) ); + undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown, + sizeof ( undi_shutdown ) ); + memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) ); + undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup, + sizeof ( undi_cleanup ) ); + undi->flags &= ~UNDI_FL_INITIALIZED; + err_undi_startup: + /* Unhook UNDI stack */ + memset ( &stop_undi, 0, sizeof ( stop_undi ) ); + undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi, + sizeof ( stop_undi ) ); + undi->flags &= ~UNDI_FL_STARTED; + err_start_undi: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + undi_set_drvdata ( undi, NULL ); + return rc; +} + +/** + * Remove UNDI device + * + * @v undi UNDI device + */ +void undinet_remove ( struct undi_device *undi ) { + struct net_device *netdev = undi_get_drvdata ( undi ); + struct undi_nic *undinic = netdev->priv; + struct s_PXENV_UNDI_SHUTDOWN undi_shutdown; + struct s_PXENV_UNDI_CLEANUP undi_cleanup; + struct s_PXENV_STOP_UNDI stop_undi; + + /* Unregister net device */ + unregister_netdev ( netdev ); + + /* If we are preparing for an OS boot, or if we cannot exit + * via the PXE stack, then shut down the PXE stack. + */ + if ( ! ( undi->flags & UNDI_FL_KEEP_ALL ) ) { + + /* Shut down UNDI stack */ + memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) ); + undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown, + sizeof ( undi_shutdown ) ); + memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) ); + undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup, + sizeof ( undi_cleanup ) ); + undi->flags &= ~UNDI_FL_INITIALIZED; + + /* Unhook UNDI stack */ + memset ( &stop_undi, 0, sizeof ( stop_undi ) ); + undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi, + sizeof ( stop_undi ) ); + undi->flags &= ~UNDI_FL_STARTED; + } + + /* Clear entry point */ + memset ( &undinet_entry_point, 0, sizeof ( undinet_entry_point ) ); + + /* Free network device */ + netdev_nullify ( netdev ); + netdev_put ( netdev ); + + DBGC ( undinic, "UNDINIC %p removed\n", undinic ); +} diff --git a/gpxe/src/arch/i386/drivers/net/undionly.c b/gpxe/src/arch/i386/drivers/net/undionly.c new file mode 100644 index 0000000..4cdce67 --- /dev/null +++ b/gpxe/src/arch/i386/drivers/net/undionly.c @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * "Pure" UNDI driver + * + * This is the UNDI driver without explicit support for PCI or any + * other bus type. It is capable only of using the preloaded UNDI + * device. It must not be combined in an image with any other + * drivers. + * + * If you want a PXE-loadable image that contains only the UNDI + * driver, build "bin/undionly.kpxe". + * + * If you want any other image format, or any other drivers in + * addition to the UNDI driver, build e.g. "bin/undi.dsk". + */ + +/** + * Probe UNDI root bus + * + * @v rootdev UNDI bus root device + * + * Scans the UNDI bus for devices and registers all devices it can + * find. + */ +static int undibus_probe ( struct root_device *rootdev ) { + struct undi_device *undi = &preloaded_undi; + int rc; + + /* Check for a valie preloaded UNDI device */ + if ( ! undi->entry.segment ) { + DBG ( "No preloaded UNDI device found!\n" ); + return -ENODEV; + } + + /* Add to device hierarchy */ + strncpy ( undi->dev.name, "UNDI", + ( sizeof ( undi->dev.name ) - 1 ) ); + if ( undi->pci_busdevfn != UNDI_NO_PCI_BUSDEVFN ) { + undi->dev.desc.bus_type = BUS_TYPE_PCI; + undi->dev.desc.location = undi->pci_busdevfn; + undi->dev.desc.vendor = undi->pci_vendor; + undi->dev.desc.device = undi->pci_device; + } else if ( undi->isapnp_csn != UNDI_NO_ISAPNP_CSN ) { + undi->dev.desc.bus_type = BUS_TYPE_ISAPNP; + } + undi->dev.parent = &rootdev->dev; + list_add ( &undi->dev.siblings, &rootdev->dev.children); + INIT_LIST_HEAD ( &undi->dev.children ); + + /* Create network device */ + if ( ( rc = undinet_probe ( undi ) ) != 0 ) + goto err; + + return 0; + + err: + list_del ( &undi->dev.siblings ); + return rc; +} + +/** + * Remove UNDI root bus + * + * @v rootdev UNDI bus root device + */ +static void undibus_remove ( struct root_device *rootdev __unused ) { + struct undi_device *undi = &preloaded_undi; + + undinet_remove ( undi ); + list_del ( &undi->dev.siblings ); +} + +/** UNDI bus root device driver */ +static struct root_driver undi_root_driver = { + .probe = undibus_probe, + .remove = undibus_remove, +}; + +/** UNDI bus root device */ +struct root_device undi_root_device __root_device = { + .dev = { .name = "UNDI" }, + .driver = &undi_root_driver, +}; + +/** + * Prepare for exit + * + * @v flags Shutdown flags + */ +static void undionly_shutdown ( int flags ) { + /* If we are shutting down to boot an OS, clear the "keep PXE + * stack" flag. + */ + if ( flags & SHUTDOWN_BOOT ) + preloaded_undi.flags &= ~UNDI_FL_KEEP_ALL; +} + +struct startup_fn startup_undionly __startup_fn ( STARTUP_LATE ) = { + .shutdown = undionly_shutdown, +}; diff --git a/gpxe/src/arch/i386/drivers/net/undipreload.c b/gpxe/src/arch/i386/drivers/net/undipreload.c new file mode 100644 index 0000000..e29d150 --- /dev/null +++ b/gpxe/src/arch/i386/drivers/net/undipreload.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include + +/** @file + * + * Preloaded UNDI stack + * + */ + +/** + * Preloaded UNDI device + * + * This is the UNDI device that was present when Etherboot started + * execution (i.e. when loading a .kpxe image). The first driver to + * claim this device must zero out this data structure. + */ +struct undi_device __data16 ( preloaded_undi ); diff --git a/gpxe/src/arch/i386/drivers/net/undirom.c b/gpxe/src/arch/i386/drivers/net/undirom.c new file mode 100644 index 0000000..e578278 --- /dev/null +++ b/gpxe/src/arch/i386/drivers/net/undirom.c @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include +#include + +/** @file + * + * UNDI expansion ROMs + * + */ + +/** List of all UNDI ROMs */ +static LIST_HEAD ( undiroms ); + +/** + * Parse PXE ROM ID structure + * + * @v undirom UNDI ROM + * @v pxeromid Offset within ROM to PXE ROM ID structure + * @ret rc Return status code + */ +static int undirom_parse_pxeromid ( struct undi_rom *undirom, + unsigned int pxeromid ) { + struct undi_rom_id undi_rom_id; + unsigned int undiloader; + + DBGC ( undirom, "UNDIROM %p has PXE ROM ID at %04x:%04x\n", undirom, + undirom->rom_segment, pxeromid ); + + /* Read PXE ROM ID structure and verify */ + copy_from_real ( &undi_rom_id, undirom->rom_segment, pxeromid, + sizeof ( undi_rom_id ) ); + if ( undi_rom_id.Signature != UNDI_ROM_ID_SIGNATURE ) { + DBGC ( undirom, "UNDIROM %p has bad PXE ROM ID signature " + "%08x\n", undirom, undi_rom_id.Signature ); + return -EINVAL; + } + + /* Check for UNDI loader */ + undiloader = undi_rom_id.UNDILoader; + if ( ! undiloader ) { + DBGC ( undirom, "UNDIROM %p has no UNDI loader\n", undirom ); + return -EINVAL; + } + + /* Fill in UNDI ROM loader fields */ + undirom->loader_entry.segment = undirom->rom_segment; + undirom->loader_entry.offset = undiloader; + undirom->code_size = undi_rom_id.CodeSize; + undirom->data_size = undi_rom_id.DataSize; + + DBGC ( undirom, "UNDIROM %p has UNDI loader at %04x:%04x " + "(code %04zx data %04zx)\n", undirom, + undirom->loader_entry.segment, undirom->loader_entry.offset, + undirom->code_size, undirom->data_size ); + return 0; +} + +/** + * Parse PCI expansion header + * + * @v undirom UNDI ROM + * @v pcirheader Offset within ROM to PCI expansion header + */ +static int undirom_parse_pcirheader ( struct undi_rom *undirom, + unsigned int pcirheader ) { + struct pcir_header pcir_header; + + DBGC ( undirom, "UNDIROM %p has PCI expansion header at %04x:%04x\n", + undirom, undirom->rom_segment, pcirheader ); + + /* Read PCI expansion header and verify */ + copy_from_real ( &pcir_header, undirom->rom_segment, pcirheader, + sizeof ( pcir_header ) ); + if ( pcir_header.signature != PCIR_SIGNATURE ) { + DBGC ( undirom, "UNDIROM %p has bad PCI expansion header " + "signature %08x\n", undirom, pcir_header.signature ); + return -EINVAL; + } + + /* Fill in UNDI ROM PCI device fields */ + undirom->bus_type = PCI_NIC; + undirom->bus_id.pci.vendor_id = pcir_header.vendor_id; + undirom->bus_id.pci.device_id = pcir_header.device_id; + + DBGC ( undirom, "UNDIROM %p is for PCI devices %04x:%04x\n", undirom, + undirom->bus_id.pci.vendor_id, undirom->bus_id.pci.device_id ); + return 0; + +} + +/** + * Probe UNDI ROM + * + * @v rom_segment ROM segment address + * @ret rc Return status code + */ +static int undirom_probe ( unsigned int rom_segment ) { + struct undi_rom *undirom = NULL; + struct undi_rom_header romheader; + size_t rom_len; + unsigned int pxeromid; + unsigned int pcirheader; + int rc; + + /* Read expansion ROM header and verify */ + copy_from_real ( &romheader, rom_segment, 0, sizeof ( romheader ) ); + if ( romheader.Signature != ROM_SIGNATURE ) { + rc = -EINVAL; + goto err; + } + rom_len = ( romheader.ROMLength * 512 ); + + /* Allocate memory for UNDI ROM */ + undirom = zalloc ( sizeof ( *undirom ) ); + if ( ! undirom ) { + DBG ( "Could not allocate UNDI ROM structure\n" ); + rc = -ENOMEM; + goto err; + } + DBGC ( undirom, "UNDIROM %p trying expansion ROM at %04x:0000 " + "(%zdkB)\n", undirom, rom_segment, ( rom_len / 1024 ) ); + undirom->rom_segment = rom_segment; + + /* Check for and parse PXE ROM ID */ + pxeromid = romheader.PXEROMID; + if ( ! pxeromid ) { + DBGC ( undirom, "UNDIROM %p has no PXE ROM ID\n", undirom ); + rc = -EINVAL; + goto err; + } + if ( pxeromid > rom_len ) { + DBGC ( undirom, "UNDIROM %p PXE ROM ID outside ROM\n", + undirom ); + rc = -EINVAL; + goto err; + } + if ( ( rc = undirom_parse_pxeromid ( undirom, pxeromid ) ) != 0 ) + goto err; + + /* Parse PCIR header, if present */ + pcirheader = romheader.PCIRHeader; + if ( pcirheader ) + undirom_parse_pcirheader ( undirom, pcirheader ); + + /* Add to UNDI ROM list and return */ + DBGC ( undirom, "UNDIROM %p registered\n", undirom ); + list_add ( &undirom->list, &undiroms ); + return 0; + + err: + free ( undirom ); + return rc; +} + +/** + * Create UNDI ROMs for all possible expansion ROMs + * + * @ret + */ +static void undirom_probe_all_roms ( void ) { + static int probed = 0; + unsigned int rom_segment; + + /* Perform probe only once */ + if ( probed ) + return; + + DBG ( "Scanning for PXE expansion ROMs\n" ); + + /* Scan through expansion ROM region at 512 byte intervals */ + for ( rom_segment = 0xc000 ; rom_segment < 0x10000 ; + rom_segment += 0x20 ) { + undirom_probe ( rom_segment ); + } + + probed = 1; +} + +/** + * Find UNDI ROM for PCI device + * + * @v vendor_id PCI vendor ID + * @v device_id PCI device ID + * @v rombase ROM base address, or 0 for any + * @ret undirom UNDI ROM, or NULL + */ +struct undi_rom * undirom_find_pci ( unsigned int vendor_id, + unsigned int device_id, + unsigned int rombase ) { + struct undi_rom *undirom; + + undirom_probe_all_roms(); + + list_for_each_entry ( undirom, &undiroms, list ) { + if ( undirom->bus_type != PCI_NIC ) + continue; + if ( undirom->bus_id.pci.vendor_id != vendor_id ) + continue; + if ( undirom->bus_id.pci.device_id != device_id ) + continue; + if ( rombase && ( ( undirom->rom_segment << 4 ) != rombase ) ) + continue; + DBGC ( undirom, "UNDIROM %p matched PCI %04x:%04x (%08x)\n", + undirom, vendor_id, device_id, rombase ); + return undirom; + } + + DBG ( "No UNDI ROM matched PCI %04x:%04x (%08x)\n", + vendor_id, device_id, rombase ); + return NULL; +} diff --git a/gpxe/src/arch/i386/firmware/pcbios/basemem.c b/gpxe/src/arch/i386/firmware/pcbios/basemem.c new file mode 100644 index 0000000..b126d2a --- /dev/null +++ b/gpxe/src/arch/i386/firmware/pcbios/basemem.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include + +/** @file + * + * Base memory allocation + * + */ + +/** + * Set the BIOS free base memory counter + * + * @v new_fbms New free base memory counter (in kB) + */ +void set_fbms ( unsigned int new_fbms ) { + uint16_t fbms = new_fbms; + + /* Update the BIOS memory counter */ + put_real ( fbms, BDA_SEG, BDA_FBMS ); + + /* Update our hidden memory region map */ + hide_basemem(); +} diff --git a/gpxe/src/arch/i386/firmware/pcbios/bios_console.c b/gpxe/src/arch/i386/firmware/pcbios/bios_console.c new file mode 100644 index 0000000..9136377 --- /dev/null +++ b/gpxe/src/arch/i386/firmware/pcbios/bios_console.c @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include + +#define ATTR_BOLD 0x08 + +#define ATTR_FCOL_MASK 0x07 +#define ATTR_FCOL_BLACK 0x00 +#define ATTR_FCOL_BLUE 0x01 +#define ATTR_FCOL_GREEN 0x02 +#define ATTR_FCOL_CYAN 0x03 +#define ATTR_FCOL_RED 0x04 +#define ATTR_FCOL_MAGENTA 0x05 +#define ATTR_FCOL_YELLOW 0x06 +#define ATTR_FCOL_WHITE 0x07 + +#define ATTR_BCOL_MASK 0x70 +#define ATTR_BCOL_BLACK 0x00 +#define ATTR_BCOL_BLUE 0x10 +#define ATTR_BCOL_GREEN 0x20 +#define ATTR_BCOL_CYAN 0x30 +#define ATTR_BCOL_RED 0x40 +#define ATTR_BCOL_MAGENTA 0x50 +#define ATTR_BCOL_YELLOW 0x60 +#define ATTR_BCOL_WHITE 0x70 + +#define ATTR_DEFAULT ATTR_FCOL_WHITE + +/** Current character attribute */ +static unsigned int bios_attr = ATTR_DEFAULT; + +/** + * Handle ANSI CUP (cursor position) + * + * @v count Parameter count + * @v params[0] Row (1 is top) + * @v params[1] Column (1 is left) + */ +static void bios_handle_cup ( unsigned int count __unused, int params[] ) { + int cx = ( params[1] - 1 ); + int cy = ( params[0] - 1 ); + + if ( cx < 0 ) + cx = 0; + if ( cy < 0 ) + cy = 0; + + __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" + "int $0x10\n\t" + "cli\n\t" ) + : : "a" ( 0x0200 ), "b" ( 1 ), + "d" ( ( cy << 8 ) | cx ) ); +} + +/** + * Handle ANSI ED (erase in page) + * + * @v count Parameter count + * @v params[0] Region to erase + */ +static void bios_handle_ed ( unsigned int count __unused, + int params[] __unused ) { + /* We assume that we always clear the whole screen */ + assert ( params[0] == ANSIESC_ED_ALL ); + + __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" + "int $0x10\n\t" + "cli\n\t" ) + : : "a" ( 0x0600 ), "b" ( bios_attr << 8 ), + "c" ( 0 ), "d" ( 0xffff ) ); +} + +/** + * Handle ANSI SGR (set graphics rendition) + * + * @v count Parameter count + * @v params List of graphic rendition aspects + */ +static void bios_handle_sgr ( unsigned int count, int params[] ) { + static const uint8_t bios_attr_fcols[10] = { + ATTR_FCOL_BLACK, ATTR_FCOL_RED, ATTR_FCOL_GREEN, + ATTR_FCOL_YELLOW, ATTR_FCOL_BLUE, ATTR_FCOL_MAGENTA, + ATTR_FCOL_CYAN, ATTR_FCOL_WHITE, + ATTR_FCOL_WHITE, ATTR_FCOL_WHITE /* defaults */ + }; + static const uint8_t bios_attr_bcols[10] = { + ATTR_BCOL_BLACK, ATTR_BCOL_RED, ATTR_BCOL_GREEN, + ATTR_BCOL_YELLOW, ATTR_BCOL_BLUE, ATTR_BCOL_MAGENTA, + ATTR_BCOL_CYAN, ATTR_BCOL_WHITE, + ATTR_BCOL_BLACK, ATTR_BCOL_BLACK /* defaults */ + }; + unsigned int i; + int aspect; + + for ( i = 0 ; i < count ; i++ ) { + aspect = params[i]; + if ( aspect == 0 ) { + bios_attr = ATTR_DEFAULT; + } else if ( aspect == 1 ) { + bios_attr |= ATTR_BOLD; + } else if ( aspect == 22 ) { + bios_attr &= ~ATTR_BOLD; + } else if ( ( aspect >= 30 ) && ( aspect <= 39 ) ) { + bios_attr &= ~ATTR_FCOL_MASK; + bios_attr |= bios_attr_fcols[ aspect - 30 ]; + } else if ( ( aspect >= 40 ) && ( aspect <= 49 ) ) { + bios_attr &= ~ATTR_BCOL_MASK; + bios_attr |= bios_attr_bcols[ aspect - 40 ]; + } + } +} + +/** BIOS console ANSI escape sequence handlers */ +static struct ansiesc_handler bios_ansiesc_handlers[] = { + { ANSIESC_CUP, bios_handle_cup }, + { ANSIESC_ED, bios_handle_ed }, + { ANSIESC_SGR, bios_handle_sgr }, + { 0, NULL } +}; + +/** BIOS console ANSI escape sequence context */ +static struct ansiesc_context bios_ansiesc_ctx = { + .handlers = bios_ansiesc_handlers, +}; + +/** + * Print a character to BIOS console + * + * @v character Character to be printed + */ +static void bios_putchar ( int character ) { + int discard_a, discard_b, discard_c; + + /* Intercept ANSI escape sequences */ + character = ansiesc_process ( &bios_ansiesc_ctx, character ); + if ( character < 0 ) + return; + + /* Print character with attribute */ + __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" + /* Skip non-printable characters */ + "cmpb $0x20, %%al\n\t" + "jb 1f\n\t" + /* Set attribute */ + "movw $0x0001, %%cx\n\t" + "movb $0x09, %%ah\n\t" + "int $0x10\n\t" + "\n1:\n\t" + /* Print character */ + "xorw %%bx, %%bx\n\t" + "movb $0x0e, %%ah\n\t" + "int $0x10\n\t" + "cli\n\t" ) + : "=a" ( discard_a ), "=b" ( discard_b ), + "=c" ( discard_c ) + : "a" ( character ), "b" ( bios_attr ) + : "ebp" ); +} + +/** + * Pointer to current ANSI output sequence + * + * While we are in the middle of returning an ANSI sequence for a + * special key, this will point to the next character to return. When + * not in the middle of such a sequence, this will point to a NUL + * (note: not "will be NULL"). + */ +static const char *ansi_input = ""; + +/** + * Lowest BIOS scancode of interest + * + * Most of the BIOS key scancodes that we are interested in are in a + * dense range, so subtracting a constant and treating them as offsets + * into an array works efficiently. + */ +#define BIOS_KEY_MIN 0x42 + +/** Offset into list of interesting BIOS scancodes */ +#define BIOS_KEY(scancode) ( (scancode) - BIOS_KEY_MIN ) + +/** Mapping from BIOS scan codes to ANSI escape sequences */ +static const char *ansi_sequences[] = { + [ BIOS_KEY ( 0x42 ) ] = "[19~", /* F8 (required for PXE) */ + [ BIOS_KEY ( 0x47 ) ] = "[H", /* Home */ + [ BIOS_KEY ( 0x48 ) ] = "[A", /* Up arrow */ + [ BIOS_KEY ( 0x4b ) ] = "[D", /* Left arrow */ + [ BIOS_KEY ( 0x4d ) ] = "[C", /* Right arrow */ + [ BIOS_KEY ( 0x4f ) ] = "[F", /* End */ + [ BIOS_KEY ( 0x50 ) ] = "[B", /* Down arrow */ + [ BIOS_KEY ( 0x53 ) ] = "[3~", /* Delete */ +}; + +/** + * Get ANSI escape sequence corresponding to BIOS scancode + * + * @v scancode BIOS scancode + * @ret ansi_seq ANSI escape sequence, if any, otherwise NULL + */ +static const char * scancode_to_ansi_seq ( unsigned int scancode ) { + unsigned int bios_key = BIOS_KEY ( scancode ); + + if ( bios_key < ( sizeof ( ansi_sequences ) / + sizeof ( ansi_sequences[0] ) ) ) { + return ansi_sequences[bios_key]; + } + DBG ( "Unrecognised BIOS scancode %02x\n", scancode ); + return NULL; +} + +/** + * Get character from BIOS console + * + * @ret character Character read from console + */ +static int bios_getchar ( void ) { + uint16_t keypress; + unsigned int character; + const char *ansi_seq; + + /* If we are mid-sequence, pass out the next byte */ + if ( ( character = *ansi_input ) ) { + ansi_input++; + return character; + } + + /* Read character from real BIOS console */ + __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" + "int $0x16\n\t" + "cli\n\t" ) + : "=a" ( keypress ) : "a" ( 0x1000 ) ); + character = ( keypress & 0xff ); + + /* If it's a normal character, just return it */ + if ( character && ( character < 0x80 ) ) + return character; + + /* Otherwise, check for a special key that we know about */ + if ( ( ansi_seq = scancode_to_ansi_seq ( keypress >> 8 ) ) ) { + /* Start of escape sequence: return ESC (0x1b) */ + ansi_input = ansi_seq; + return 0x1b; + } + + return 0; +} + +/** + * Check for character ready to read from BIOS console + * + * @ret True Character available to read + * @ret False No character available to read + */ +static int bios_iskey ( void ) { + unsigned int discard_a; + unsigned int flags; + + /* If we are mid-sequence, we are always ready */ + if ( *ansi_input ) + return 1; + + /* Otherwise check the real BIOS console */ + __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" + "int $0x16\n\t" + "pushfw\n\t" + "popw %w0\n\t" + "cli\n\t" ) + : "=r" ( flags ), "=a" ( discard_a ) + : "a" ( 0x0100 ) ); + return ( ! ( flags & ZF ) ); +} + +struct console_driver bios_console __console_driver = { + .putchar = bios_putchar, + .getchar = bios_getchar, + .iskey = bios_iskey, +}; diff --git a/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S b/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S new file mode 100644 index 0000000..decb083 --- /dev/null +++ b/gpxe/src/arch/i386/firmware/pcbios/e820mangler.S @@ -0,0 +1,593 @@ +/* + * Copyright (C) 2006 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. + */ + + .text + .arch i386 + .code16 + +#define SMAP 0x534d4150 + +/* Most documentation refers to the E820 buffer as being 20 bytes, and + * the API makes it perfectly legitimate to pass only a 20-byte buffer + * and expect to get valid data. However, some morons at ACPI decided + * to extend the data structure by adding an extra "extended + * attributes" field and by including critical information within this + * field, such as whether or not the region is enabled. A caller who + * passes in only a 20-byte buffer therefore risks getting very, very + * misleading information. + * + * I have personally witnessed an HP BIOS that returns a value of + * 0x0009 in the extended attributes field. If we don't pass this + * value through to the caller, 32-bit WinPE will die, usually with a + * PAGE_FAULT_IN_NONPAGED_AREA blue screen of death. + * + * Allow a ridiculously large maximum value (64 bytes) for the E820 + * buffer as a guard against insufficiently creative idiots in the + * future. + */ +#define E820MAXSIZE 64 + +/**************************************************************************** + * + * Allowed memory windows + * + * There are two ways to view this list. The first is as a list of + * (non-overlapping) allowed memory regions, sorted by increasing + * address. The second is as a list of (non-overlapping) hidden + * memory regions, again sorted by increasing address. The second + * view is offset by half an entry from the first: think about this + * for a moment and it should make sense. + * + * xxx_memory_window is used to indicate an "allowed region" + * structure, hidden_xxx_memory is used to indicate a "hidden region" + * structure. Each structure is 16 bytes in length. + * + **************************************************************************** + */ + .section ".data16", "aw", @progbits + .align 16 + .globl hidemem_base + .globl hidemem_umalloc + .globl hidemem_textdata +memory_windows: +base_memory_window: .long 0x00000000, 0x00000000 /* Start of memory */ + +hidemem_base: .long 0x000a0000, 0x00000000 /* Changes at runtime */ +ext_memory_window: .long 0x000a0000, 0x00000000 /* 640kB mark */ + +hidemem_umalloc: .long 0xffffffff, 0xffffffff /* Changes at runtime */ + .long 0xffffffff, 0xffffffff /* Changes at runtime */ + +hidemem_textdata: .long 0xffffffff, 0xffffffff /* Changes at runtime */ + .long 0xffffffff, 0xffffffff /* Changes at runtime */ + + .long 0xffffffff, 0xffffffff /* End of memory */ +memory_windows_end: + +/**************************************************************************** + * Truncate region to memory window + * + * Parameters: + * %edx:%eax Start of region + * %ecx:%ebx Length of region + * %si Memory window + * Returns: + * %edx:%eax Start of windowed region + * %ecx:%ebx Length of windowed region + **************************************************************************** + */ + .section ".text16", "ax", @progbits +window_region: + /* Convert (start,len) to (start, end) */ + addl %eax, %ebx + adcl %edx, %ecx + /* Truncate to window start */ + cmpl 4(%si), %edx + jne 1f + cmpl 0(%si), %eax +1: jae 2f + movl 4(%si), %edx + movl 0(%si), %eax +2: /* Truncate to window end */ + cmpl 12(%si), %ecx + jne 1f + cmpl 8(%si), %ebx +1: jbe 2f + movl 12(%si), %ecx + movl 8(%si), %ebx +2: /* Convert (start, end) back to (start, len) */ + subl %eax, %ebx + sbbl %edx, %ecx + /* If length is <0, set length to 0 */ + jae 1f + xorl %ebx, %ebx + xorl %ecx, %ecx + ret + .size window_region, . - window_region + +/**************************************************************************** + * Patch "memory above 1MB" figure + * + * Parameters: + * %ax Memory above 1MB, in 1kB blocks + * Returns: + * %ax Modified memory above 1M in 1kB blocks + **************************************************************************** + */ + .section ".text16", "ax", @progbits +patch_1m: + pushal + /* Convert to (start,len) format and call truncate */ + xorl %ecx, %ecx + movzwl %ax, %ebx + shll $10, %ebx + xorl %edx, %edx + movl $0x100000, %eax + movw $ext_memory_window, %si + call window_region + /* Convert back to "memory above 1MB" format and return via %ax */ + pushfw + shrl $10, %ebx + popfw + movw %sp, %bp + movw %bx, 28(%bp) + popal + ret + .size patch_1m, . - patch_1m + +/**************************************************************************** + * Patch "memory above 16MB" figure + * + * Parameters: + * %bx Memory above 16MB, in 64kB blocks + * Returns: + * %bx Modified memory above 16M in 64kB blocks + **************************************************************************** + */ + .section ".text16", "ax", @progbits +patch_16m: + pushal + /* Convert to (start,len) format and call truncate */ + xorl %ecx, %ecx + shll $16, %ebx + xorl %edx, %edx + movl $0x1000000, %eax + movw $ext_memory_window, %si + call window_region + /* Convert back to "memory above 16MB" format and return via %bx */ + pushfw + shrl $16, %ebx + popfw + movw %sp, %bp + movw %bx, 16(%bp) + popal + ret + .size patch_16m, . - patch_16m + +/**************************************************************************** + * Patch "memory between 1MB and 16MB" and "memory above 16MB" figures + * + * Parameters: + * %ax Memory between 1MB and 16MB, in 1kB blocks + * %bx Memory above 16MB, in 64kB blocks + * Returns: + * %ax Modified memory between 1MB and 16MB, in 1kB blocks + * %bx Modified memory above 16MB, in 64kB blocks + **************************************************************************** + */ + .section ".text16", "ax", @progbits +patch_1m_16m: + call patch_1m + call patch_16m + /* If 1M region is no longer full-length, kill off the 16M region */ + cmpw $( 15 * 1024 ), %ax + je 1f + xorw %bx, %bx +1: ret + .size patch_1m_16m, . - patch_1m_16m + +/**************************************************************************** + * Get underlying e820 memory region to underlying_e820 buffer + * + * Parameters: + * As for INT 15,e820 + * Returns: + * As for INT 15,e820 + * + * Wraps the underlying INT 15,e820 call so that the continuation + * value (%ebx) is a 16-bit simple sequence counter (with the high 16 + * bits ignored), and termination is always via CF=1 rather than + * %ebx=0. + * + **************************************************************************** + */ + .section ".text16", "ax", @progbits +get_underlying_e820: + + /* If the requested region is in the cache, return it */ + cmpw %bx, underlying_e820_index + jne 2f + pushw %di + pushw %si + movw $underlying_e820_cache, %si + cmpl underlying_e820_cache_size, %ecx + jbe 1f + movl underlying_e820_cache_size, %ecx +1: pushl %ecx + rep movsb + popl %ecx + popw %si + popw %di + incw %bx + movl %edx, %eax + ret +2: + /* If the requested region is earlier than the cached region, + * invalidate the cache. + */ + cmpw %bx, underlying_e820_index + jbe 1f + movw $0xffff, underlying_e820_index +1: + /* If the cache is invalid, reset the underlying %ebx */ + cmpw $0xffff, underlying_e820_index + jne 1f + andl $0, underlying_e820_ebx +1: + /* If the cache is valid but the continuation value is zero, + * this means that the previous underlying call returned with + * %ebx=0. Return with CF=1 in this case. + */ + cmpw $0xffff, underlying_e820_index + je 1f + cmpl $0, underlying_e820_ebx + jne 1f + stc + ret +1: + /* Get the next region into the cache */ + pushl %eax + pushl %ebx + pushl %ecx + pushl %edx + pushl %esi /* Some implementations corrupt %esi, so we */ + pushl %edi /* preserve %esi, %edi and %ebp to be paranoid */ + pushl %ebp + pushw %es + pushw %ds + popw %es + movw $underlying_e820_cache, %di + cmpl $E820MAXSIZE, %ecx + jbe 1f + movl $E820MAXSIZE, %ecx +1: movl underlying_e820_ebx, %ebx + stc + pushfw + lcall *%cs:int15_vector + popw %es + popl %ebp + popl %edi + popl %esi + /* Check for error return from underlying e820 call */ + jc 2f /* CF set: error */ + cmpl $SMAP, %eax + je 3f /* 'SMAP' missing: error */ +2: /* An error occurred: return values returned by underlying e820 call */ + stc /* Force CF set if SMAP was missing */ + addr32 leal 16(%esp), %esp /* avoid changing other flags */ + ret +3: /* No error occurred */ + movl %ebx, underlying_e820_ebx + movl %ecx, underlying_e820_cache_size + popl %edx + popl %ecx + popl %ebx + popl %eax + /* Mark cache as containing this result */ + incw underlying_e820_index + + /* Loop until found */ + jmp get_underlying_e820 + .size get_underlying_e820, . - get_underlying_e820 + + .section ".data16", "aw", @progbits +underlying_e820_index: + .word 0xffff /* Initialise to an invalid value */ + .size underlying_e820_index, . - underlying_e820_index + + .section ".bss16", "aw", @nobits +underlying_e820_ebx: + .long 0 + .size underlying_e820_ebx, . - underlying_e820_ebx + + .section ".bss16", "aw", @nobits +underlying_e820_cache: + .space E820MAXSIZE + .size underlying_e820_cache, . - underlying_e820_cache + + .section ".bss16", "aw", @nobits +underlying_e820_cache_size: + .long 0 + .size underlying_e820_cache_size, . - underlying_e820_cache_size + +/**************************************************************************** + * Get windowed e820 region, without empty region stripping + * + * Parameters: + * As for INT 15,e820 + * Returns: + * As for INT 15,e820 + * + * Wraps the underlying INT 15,e820 call so that each underlying + * region is returned N times, windowed to fit within N visible-memory + * windows. Termination is always via CF=1. + * + **************************************************************************** + */ + .section ".text16", "ax", @progbits +get_windowed_e820: + + /* Preserve registers */ + pushl %esi + pushw %bp + + /* Split %ebx into %si:%bx, store original %bx in %bp */ + pushl %ebx + popw %bp + popw %si + + /* %si == 0 => start of memory_windows list */ + testw %si, %si + jne 1f + movw $memory_windows, %si +1: + /* Get (cached) underlying e820 region to buffer */ + call get_underlying_e820 + jc 99f /* Abort on error */ + + /* Preserve registers */ + pushal + /* start => %edx:%eax, len => %ecx:%ebx */ + movl %es:0(%di), %eax + movl %es:4(%di), %edx + movl %es:8(%di), %ebx + movl %es:12(%di), %ecx + /* Truncate region to current window */ + call window_region +1: /* Store modified values in e820 map entry */ + movl %eax, %es:0(%di) + movl %edx, %es:4(%di) + movl %ebx, %es:8(%di) + movl %ecx, %es:12(%di) + /* Restore registers */ + popal + + /* Derive continuation value for next call */ + addw $16, %si + cmpw $memory_windows_end, %si + jne 1f + /* End of memory windows: reset %si and allow %bx to continue */ + xorw %si, %si + jmp 2f +1: /* More memory windows to go: restore original %bx */ + movw %bp, %bx +2: /* Construct %ebx from %si:%bx */ + pushw %si + pushw %bx + popl %ebx + +98: /* Clear CF */ + clc +99: /* Restore registers and return */ + popw %bp + popl %esi + ret + .size get_windowed_e820, . - get_windowed_e820 + +/**************************************************************************** + * Get windowed e820 region, with empty region stripping + * + * Parameters: + * As for INT 15,e820 + * Returns: + * As for INT 15,e820 + * + * Wraps the underlying INT 15,e820 call so that each underlying + * region is returned up to N times, windowed to fit within N + * visible-memory windows. Empty windows are never returned. + * Termination is always via CF=1. + * + **************************************************************************** + */ + .section ".text16", "ax", @progbits +get_nonempty_e820: + + /* Record entry parameters */ + pushl %eax + pushl %ecx + pushl %edx + + /* Get next windowed region */ + call get_windowed_e820 + jc 99f /* abort on error */ + + /* If region is non-empty, finish here */ + cmpl $0, %es:8(%di) + jne 98f + cmpl $0, %es:12(%di) + jne 98f + + /* Region was empty: restore entry parameters and go to next region */ + popl %edx + popl %ecx + popl %eax + jmp get_nonempty_e820 + +98: /* Clear CF */ + clc +99: /* Return values from underlying call */ + addr32 leal 12(%esp), %esp /* avoid changing flags */ + ret + .size get_nonempty_e820, . - get_nonempty_e820 + +/**************************************************************************** + * Get mangled e820 region, with empty region stripping + * + * Parameters: + * As for INT 15,e820 + * Returns: + * As for INT 15,e820 + * + * Wraps the underlying INT 15,e820 call so that underlying regions + * are windowed to the allowed memory regions. Empty regions are + * stripped from the map. Termination is always via %ebx=0. + * + **************************************************************************** + */ + .section ".text16", "ax", @progbits +get_mangled_e820: + + /* Get a nonempty region */ + call get_nonempty_e820 + jc 99f /* Abort on error */ + + /* Peek ahead to see if there are any further nonempty regions */ + pushal + pushw %es + movw %sp, %bp + subw %cx, %sp + movl $0xe820, %eax + movl $SMAP, %edx + pushw %ss + popw %es + movw %sp, %di + call get_nonempty_e820 + movw %bp, %sp + popw %es + popal + jnc 99f /* There are further nonempty regions */ + + /* No futher nonempty regions: zero %ebx and clear CF */ + xorl %ebx, %ebx + +99: /* Return */ + ret + .size get_mangled_e820, . - get_mangled_e820 + +/**************************************************************************** + * Set/clear CF on the stack as appropriate, assumes stack is as it should + * be immediately before IRET + **************************************************************************** + */ +patch_cf: + pushw %bp + movw %sp, %bp + setc 8(%bp) /* Set/reset CF; clears PF, AF, ZF, SF */ + popw %bp + ret + +/**************************************************************************** + * INT 15,e820 handler + **************************************************************************** + */ + .section ".text16", "ax", @progbits +int15_e820: + pushw %ds + pushw %cs:rm_ds + popw %ds + call get_mangled_e820 + popw %ds + call patch_cf + iret + .size int15_e820, . - int15_e820 + +/**************************************************************************** + * INT 15,e801 handler + **************************************************************************** + */ + .section ".text16", "ax", @progbits +int15_e801: + /* Call previous handler */ + pushfw + lcall *%cs:int15_vector + call patch_cf + /* Edit result */ + pushw %ds + pushw %cs:rm_ds + popw %ds + call patch_1m_16m + xchgw %ax, %cx + xchgw %bx, %dx + call patch_1m_16m + xchgw %ax, %cx + xchgw %bx, %dx + popw %ds + iret + .size int15_e801, . - int15_e801 + +/**************************************************************************** + * INT 15,88 handler + **************************************************************************** + */ + .section ".text16", "ax", @progbits +int15_88: + /* Call previous handler */ + pushfw + lcall *%cs:int15_vector + call patch_cf + /* Edit result */ + pushw %ds + pushw %cs:rm_ds + popw %ds + call patch_1m + popw %ds + iret + .size int15_88, . - int15_88 + +/**************************************************************************** + * INT 15 handler + **************************************************************************** + */ + .section ".text16", "ax", @progbits + .globl int15 +int15: + /* See if we want to intercept this call */ + pushfw + cmpw $0xe820, %ax + jne 1f + cmpl $SMAP, %edx + jne 1f + popfw + jmp int15_e820 +1: cmpw $0xe801, %ax + jne 2f + popfw + jmp int15_e801 +2: cmpb $0x88, %ah + jne 3f + popfw + jmp int15_88 +3: popfw + ljmp *%cs:int15_vector + .size int15, . - int15 + + .section ".text16.data", "aw", @progbits + .globl int15_vector +int15_vector: + .long 0 + .size int15_vector, . - int15_vector diff --git a/gpxe/src/arch/i386/firmware/pcbios/fakee820.c b/gpxe/src/arch/i386/firmware/pcbios/fakee820.c new file mode 100644 index 0000000..552bf41 --- /dev/null +++ b/gpxe/src/arch/i386/firmware/pcbios/fakee820.c @@ -0,0 +1,91 @@ +/* Copyright (C) 2008 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. + */ + +#include +#include + +/** Assembly routine in inline asm */ +extern void int15_fakee820(); + +/** Original INT 15 handler */ +static struct segoff __text16 ( real_int15_vector ); +#define real_int15_vector __use_text16 ( real_int15_vector ) + +/** An INT 15,e820 memory map entry */ +struct e820_entry { + /** Start of region */ + uint64_t start; + /** Length of region */ + uint64_t len; + /** Type of region */ + uint32_t type; +} __attribute__ (( packed )); + +#define E820_TYPE_RAM 1 /**< Normal memory */ +#define E820_TYPE_RSVD 2 /**< Reserved and unavailable */ +#define E820_TYPE_ACPI 3 /**< ACPI reclaim memory */ +#define E820_TYPE_NVS 4 /**< ACPI NVS memory */ + +/** Fake e820 map */ +static struct e820_entry __text16_array ( e820map, [] ) __used = { + { 0x00000000ULL, ( 0x000a0000ULL - 0x00000000ULL ), E820_TYPE_RAM }, + { 0x00100000ULL, ( 0xcfb50000ULL - 0x00100000ULL ), E820_TYPE_RAM }, + { 0xcfb50000ULL, ( 0xcfb64000ULL - 0xcfb50000ULL ), E820_TYPE_RSVD }, + { 0xcfb64000ULL, ( 0xcfb66000ULL - 0xcfb64000ULL ), E820_TYPE_RSVD }, + { 0xcfb66000ULL, ( 0xcfb85c00ULL - 0xcfb66000ULL ), E820_TYPE_ACPI }, + { 0xcfb85c00ULL, ( 0xd0000000ULL - 0xcfb85c00ULL ), E820_TYPE_RSVD }, + { 0xe0000000ULL, ( 0xf0000000ULL - 0xe0000000ULL ), E820_TYPE_RSVD }, + { 0xfe000000ULL, (0x100000000ULL - 0xfe000000ULL ), E820_TYPE_RSVD }, + {0x100000000ULL, (0x230000000ULL -0x100000000ULL ), E820_TYPE_RAM }, +}; +#define e820map __use_text16 ( e820map ) + +void fake_e820 ( void ) { + __asm__ __volatile__ ( + TEXT16_CODE ( "\nint15_fakee820:\n\t" + "pushfw\n\t" + "cmpl $0xe820, %%eax\n\t" + "jne 99f\n\t" + "cmpl $0x534d4150, %%edx\n\t" + "jne 99f\n\t" + "pushaw\n\t" + "movw %%sp, %%bp\n\t" + "andb $~0x01, 22(%%bp)\n\t" /* Clear return CF */ + "leaw e820map(%%bx), %%si\n\t" + "cs rep movsb\n\t" + "popaw\n\t" + "movl %%edx, %%eax\n\t" + "addl $20, %%ebx\n\t" + "cmpl %0, %%ebx\n\t" + "jne 1f\n\t" + "xorl %%ebx,%%ebx\n\t" + "\n1:\n\t" + "popfw\n\t" + "iret\n\t" + "\n99:\n\t" + "popfw\n\t" + "ljmp *%%cs:real_int15_vector\n\t" ) + : : "i" ( sizeof ( e820map ) ) ); + + hook_bios_interrupt ( 0x15, ( unsigned int ) int15_fakee820, + &real_int15_vector ); +} + +void unfake_e820 ( void ) { + unhook_bios_interrupt ( 0x15, ( unsigned int ) int15_fakee820, + &real_int15_vector ); +} diff --git a/gpxe/src/arch/i386/firmware/pcbios/gateA20.c b/gpxe/src/arch/i386/firmware/pcbios/gateA20.c new file mode 100644 index 0000000..34e3ac5 --- /dev/null +++ b/gpxe/src/arch/i386/firmware/pcbios/gateA20.c @@ -0,0 +1,174 @@ +#include +#include +#include +#include +#include + +#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 KC_CMD_NULL 0xff /* null command ("pulse nothing") */ +#define KB_SET_A20 0xdf /* enable A20, + enable output buffer full interrupt + enable data line + disable clock line */ +#define KB_UNSET_A20 0xdd /* enable A20, + enable output buffer full interrupt + enable data line + disable clock line */ + +#define SCP_A 0x92 /* System Control Port A */ + +enum { Disable_A20 = 0x2400, Enable_A20 = 0x2401, Query_A20_Status = 0x2402, + Query_A20_Support = 0x2403 }; + +enum a20_methods { + A20_UNKNOWN = 0, + A20_INT15, + A20_KBC, + A20_SCPA, +}; + +#define A20_MAX_RETRIES 32 +#define A20_INT15_RETRIES 32 +#define A20_KBC_RETRIES (2^21) +#define A20_SCPA_RETRIES (2^21) + +/** + * Drain keyboard controller + */ +static void empty_8042 ( void ) { + unsigned long time; + + time = currticks() + TICKS_PER_SEC; /* max wait of 1 second */ + while ( ( inb ( K_CMD ) & ( K_IBUF_FUL | K_OBUF_FUL ) ) && + currticks() < time ) { + iodelay(); + ( void ) inb_p ( K_RDWR ); + iodelay(); + } +} + +/** + * Fast test to see if gate A20 is already set + * + * @v retries Number of times to retry before giving up + * @ret set Gate A20 is set + */ +static int gateA20_is_set ( int retries ) { + static uint32_t test_pattern = 0xdeadbeef; + physaddr_t test_pattern_phys = virt_to_phys ( &test_pattern ); + physaddr_t verify_pattern_phys = ( test_pattern_phys ^ 0x100000 ); + userptr_t verify_pattern_user = phys_to_user ( verify_pattern_phys ); + uint32_t verify_pattern; + + do { + /* Check for difference */ + copy_from_user ( &verify_pattern, verify_pattern_user, 0, + sizeof ( verify_pattern ) ); + if ( verify_pattern != test_pattern ) + return 1; + + /* Avoid false negatives */ + test_pattern++; + + iodelay(); + + /* Always retry at least once, to avoid false negatives */ + } while ( retries-- >= 0 ); + + /* Pattern matched every time; gate A20 is not set */ + return 0; +} + +/* + * Gate A20 for high memory + * + * Note that this function gets called as part of the return path from + * librm's real_call, which is used to make the int15 call if librm is + * being used. To avoid an infinite recursion, we make gateA20_set + * return immediately if it is already part of the call stack. + */ +void gateA20_set ( void ) { + static char reentry_guard = 0; + static int a20_method = A20_UNKNOWN; + unsigned int discard_a; + unsigned int scp_a; + int retries = 0; + + /* Avoid potential infinite recursion */ + if ( reentry_guard ) + return; + reentry_guard = 1; + + /* Fast check to see if gate A20 is already enabled */ + if ( gateA20_is_set ( 0 ) ) + goto out; + + for ( ; retries < A20_MAX_RETRIES ; retries++ ) { + switch ( a20_method ) { + case A20_UNKNOWN: + case A20_INT15: + /* Try INT 15 method */ + __asm__ __volatile__ ( REAL_CODE ( "int $0x15" ) + : "=a" ( discard_a ) + : "a" ( Enable_A20 ) ); + if ( gateA20_is_set ( A20_INT15_RETRIES ) ) { + DBG ( "Enabled gate A20 using BIOS\n" ); + a20_method = A20_INT15; + goto out; + } + /* fall through */ + case A20_KBC: + /* Try keyboard controller method */ + empty_8042(); + outb ( KC_CMD_WOUT, K_CMD ); + empty_8042(); + outb ( KB_SET_A20, K_RDWR ); + empty_8042(); + outb ( KC_CMD_NULL, K_CMD ); + empty_8042(); + if ( gateA20_is_set ( A20_KBC_RETRIES ) ) { + DBG ( "Enabled gate A20 using " + "keyboard controller\n" ); + a20_method = A20_KBC; + goto out; + } + /* fall through */ + case A20_SCPA: + /* Try "Fast gate A20" method */ + scp_a = inb ( SCP_A ); + scp_a &= ~0x01; /* Avoid triggering a reset */ + scp_a |= 0x02; /* Enable A20 */ + iodelay(); + outb ( scp_a, SCP_A ); + iodelay(); + if ( gateA20_is_set ( A20_SCPA_RETRIES ) ) { + DBG ( "Enabled gate A20 using " + "Fast Gate A20\n" ); + a20_method = A20_SCPA; + goto out; + } + } + } + + /* Better to die now than corrupt memory later */ + printf ( "FATAL: Gate A20 stuck\n" ); + while ( 1 ) {} + + out: + if ( retries ) + DBG ( "%d attempts were required to enable A20\n", + ( retries + 1 ) ); + reentry_guard = 0; +} + +void gateA20_unset ( void ) { + /* Not currently implemented */ +} diff --git a/gpxe/src/arch/i386/firmware/pcbios/hidemem.c b/gpxe/src/arch/i386/firmware/pcbios/hidemem.c new file mode 100644 index 0000000..620b62e --- /dev/null +++ b/gpxe/src/arch/i386/firmware/pcbios/hidemem.c @@ -0,0 +1,218 @@ +/* Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** Set to true if you want to test a fake E820 map */ +#define FAKE_E820 0 + +/** Alignment for hidden memory regions */ +#define ALIGN_HIDDEN 4096 /* 4kB page alignment should be enough */ + +/** + * A hidden region of gPXE + * + * This represents a region that will be edited out of the system's + * memory map. + * + * This structure is accessed by assembly code, so must not be + * changed. + */ +struct hidden_region { + /** Physical start address */ + uint64_t start; + /** Physical end address */ + uint64_t end; +}; + +/** Hidden base memory */ +extern struct hidden_region __data16 ( hidemem_base ); +#define hidemem_base __use_data16 ( hidemem_base ) + +/** Hidden umalloc memory */ +extern struct hidden_region __data16 ( hidemem_umalloc ); +#define hidemem_umalloc __use_data16 ( hidemem_umalloc ) + +/** Hidden text memory */ +extern struct hidden_region __data16 ( hidemem_textdata ); +#define hidemem_textdata __use_data16 ( hidemem_textdata ) + +/** Assembly routine in e820mangler.S */ +extern void int15(); + +/** Vector for storing original INT 15 handler */ +extern struct segoff __text16 ( int15_vector ); +#define int15_vector __use_text16 ( int15_vector ) + +/* The linker defines these symbols for us */ +extern char _textdata[]; +extern char _etextdata[]; +extern char _text16_memsz[]; +#define _text16_memsz ( ( unsigned int ) _text16_memsz ) +extern char _data16_memsz[]; +#define _data16_memsz ( ( unsigned int ) _data16_memsz ) + +/** + * Hide region of memory from system memory map + * + * @v region Hidden memory region + * @v start Start of region + * @v end End of region + */ +static void hide_region ( struct hidden_region *region, + physaddr_t start, physaddr_t end ) { + + /* Some operating systems get a nasty shock if a region of the + * E820 map seems to start on a non-page boundary. Make life + * safer by rounding out our edited region. + */ + region->start = ( start & ~( ALIGN_HIDDEN - 1 ) ); + region->end = ( ( end + ALIGN_HIDDEN - 1 ) & ~( ALIGN_HIDDEN - 1 ) ); + + DBG ( "Hiding region [%llx,%llx)\n", region->start, region->end ); +} + +/** + * Hide used base memory + * + */ +void hide_basemem ( void ) { + /* Hide from the top of free base memory to 640kB. Don't use + * hide_region(), because we don't want this rounded to the + * nearest page boundary. + */ + hidemem_base.start = ( get_fbms() * 1024 ); +} + +/** + * Hide umalloc() region + * + */ +void hide_umalloc ( physaddr_t start, physaddr_t end ) { + assert ( end <= virt_to_phys ( _textdata ) ); + hide_region ( &hidemem_umalloc, start, end ); +} + +/** + * Hide .text and .data + * + */ +void hide_textdata ( void ) { + hide_region ( &hidemem_textdata, virt_to_phys ( _textdata ), + virt_to_phys ( _etextdata ) ); +} + +/** + * Hide Etherboot + * + * Installs an INT 15 handler to edit Etherboot out of the memory map + * returned by the BIOS. + */ +static void hide_etherboot ( void ) { + struct memory_map memmap; + unsigned int rm_ds_top; + unsigned int rm_cs_top; + unsigned int fbms; + + /* Dump memory map before mangling */ + DBG ( "Hiding gPXE from system memory map\n" ); + get_memmap ( &memmap ); + + /* Hook in fake E820 map, if we're testing one */ + if ( FAKE_E820 ) { + DBG ( "Hooking in fake E820 map\n" ); + fake_e820(); + get_memmap ( &memmap ); + } + + /* Initialise the hidden regions */ + hide_basemem(); + hide_umalloc ( virt_to_phys ( _textdata ), virt_to_phys ( _textdata ) ); + hide_textdata(); + + /* Some really moronic BIOSes bring up the PXE stack via the + * UNDI loader entry point and then don't bother to unload it + * before overwriting the code and data segments. If this + * happens, we really don't want to leave INT 15 hooked, + * because that will cause any loaded OS to die horribly as + * soon as it attempts to fetch the system memory map. + * + * We use a heuristic to guess whether or not we are being + * loaded sensibly. + */ + rm_cs_top = ( ( ( rm_cs << 4 ) + _text16_memsz + 1024 - 1 ) >> 10 ); + rm_ds_top = ( ( ( rm_ds << 4 ) + _data16_memsz + 1024 - 1 ) >> 10 ); + fbms = get_fbms(); + if ( ( rm_cs_top < fbms ) && ( rm_ds_top < fbms ) ) { + DBG ( "Detected potentially unsafe UNDI load at CS=%04x " + "DS=%04x FBMS=%dkB\n", rm_cs, rm_ds, fbms ); + DBG ( "Disabling INT 15 memory hiding\n" ); + return; + } + + /* Hook INT 15 */ + hook_bios_interrupt ( 0x15, ( unsigned int ) int15, + &int15_vector ); + + /* Dump memory map after mangling */ + DBG ( "Hidden gPXE from system memory map\n" ); + get_memmap ( &memmap ); +} + +/** + * Unhide Etherboot + * + * Uninstalls the INT 15 handler installed by hide_etherboot(), if + * possible. + */ +static void unhide_etherboot ( int flags __unused ) { + + /* If we have more than one hooked interrupt at this point, it + * means that some other vector is still hooked, in which case + * we can't safely unhook INT 15 because we need to keep our + * memory protected. (We expect there to be at least one + * hooked interrupt, because INT 15 itself is still hooked). + */ + if ( hooked_bios_interrupts > 1 ) { + DBG ( "Cannot unhide: %d interrupt vectors still hooked\n", + hooked_bios_interrupts ); + return; + } + + /* Try to unhook INT 15. If it fails, then just leave it + * hooked; it takes care of protecting itself. :) + */ + unhook_bios_interrupt ( 0x15, ( unsigned int ) int15, + &int15_vector ); + + /* Unhook fake E820 map, if used */ + if ( FAKE_E820 ) + unfake_e820(); +} + +/** Hide Etherboot startup function */ +struct startup_fn hide_etherboot_startup_fn __startup_fn ( STARTUP_EARLY ) = { + .startup = hide_etherboot, + .shutdown = unhide_etherboot, +}; diff --git a/gpxe/src/arch/i386/firmware/pcbios/memmap.c b/gpxe/src/arch/i386/firmware/pcbios/memmap.c new file mode 100644 index 0000000..ff387d9 --- /dev/null +++ b/gpxe/src/arch/i386/firmware/pcbios/memmap.c @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include + +/** + * @file + * + * Memory mapping + * + */ + +/** Magic value for INT 15,e820 calls */ +#define SMAP ( 0x534d4150 ) + +/** An INT 15,e820 memory map entry */ +struct e820_entry { + /** Start of region */ + uint64_t start; + /** Length of region */ + uint64_t len; + /** Type of region */ + uint32_t type; + /** Extended attributes (optional) */ + uint32_t attrs; +} __attribute__ (( packed )); + +#define E820_TYPE_RAM 1 /**< Normal memory */ +#define E820_TYPE_RESERVED 2 /**< Reserved and unavailable */ +#define E820_TYPE_ACPI 3 /**< ACPI reclaim memory */ +#define E820_TYPE_NVS 4 /**< ACPI NVS memory */ + +#define E820_ATTR_ENABLED 0x00000001UL +#define E820_ATTR_NONVOLATILE 0x00000002UL +#define E820_ATTR_UNKNOWN 0xfffffffcUL + +#define E820_MIN_SIZE 20 + +/** Buffer for INT 15,e820 calls */ +static struct e820_entry __bss16 ( e820buf ); +#define e820buf __use_data16 ( e820buf ) + +/** + * Get size of extended memory via INT 15,e801 + * + * @ret extmem Extended memory size, in kB, or 0 + */ +static unsigned int extmemsize_e801 ( void ) { + uint16_t extmem_1m_to_16m_k, extmem_16m_plus_64k; + uint16_t confmem_1m_to_16m_k, confmem_16m_plus_64k; + unsigned int flags; + unsigned int extmem; + + __asm__ __volatile__ ( REAL_CODE ( "stc\n\t" + "int $0x15\n\t" + "pushfw\n\t" + "popw %w0\n\t" ) + : "=r" ( flags ), + "=a" ( extmem_1m_to_16m_k ), + "=b" ( extmem_16m_plus_64k ), + "=c" ( confmem_1m_to_16m_k ), + "=d" ( confmem_16m_plus_64k ) + : "a" ( 0xe801 ) ); + + if ( flags & CF ) { + DBG ( "INT 15,e801 failed with CF set\n" ); + return 0; + } + + if ( ! ( extmem_1m_to_16m_k | extmem_16m_plus_64k ) ) { + DBG ( "INT 15,e801 extmem=0, using confmem\n" ); + extmem_1m_to_16m_k = confmem_1m_to_16m_k; + extmem_16m_plus_64k = confmem_16m_plus_64k; + } + + extmem = ( extmem_1m_to_16m_k + ( extmem_16m_plus_64k * 64 ) ); + DBG ( "INT 15,e801 extended memory size %d+64*%d=%d kB " + "[100000,%llx)\n", extmem_1m_to_16m_k, extmem_16m_plus_64k, + extmem, ( 0x100000 + ( ( ( uint64_t ) extmem ) * 1024 ) ) ); + + /* Sanity check. Some BIOSes report the entire 4GB address + * space as available, which cannot be correct (since that + * would leave no address space available for 32-bit PCI + * BARs). + */ + if ( extmem == ( 0x400000 - 0x400 ) ) { + DBG ( "INT 15,e801 reported whole 4GB; assuming insane\n" ); + return 0; + } + + return extmem; +} + +/** + * Get size of extended memory via INT 15,88 + * + * @ret extmem Extended memory size, in kB + */ +static unsigned int extmemsize_88 ( void ) { + uint16_t extmem; + + /* Ignore CF; it is not reliable for this call */ + __asm__ __volatile__ ( REAL_CODE ( "int $0x15" ) + : "=a" ( extmem ) : "a" ( 0x8800 ) ); + + DBG ( "INT 15,88 extended memory size %d kB [100000, %x)\n", + extmem, ( 0x100000 + ( extmem * 1024 ) ) ); + return extmem; +} + +/** + * Get size of extended memory + * + * @ret extmem Extended memory size, in kB + * + * Note that this is only an approximation; for an accurate picture, + * use the E820 memory map obtained via get_memmap(); + */ +unsigned int extmemsize ( void ) { + unsigned int extmem; + + /* Try INT 15,e801 first, then fall back to INT 15,88 */ + extmem = extmemsize_e801(); + if ( ! extmem ) + extmem = extmemsize_88(); + return extmem; +} + +/** + * Get e820 memory map + * + * @v memmap Memory map to fill in + * @ret rc Return status code + */ +static int meme820 ( struct memory_map *memmap ) { + struct memory_region *region = memmap->regions; + uint32_t next = 0; + uint32_t smap; + size_t size; + unsigned int flags; + unsigned int discard_D; + + /* Clear the E820 buffer. Do this once before starting, + * rather than on each call; some BIOSes rely on the contents + * being preserved between calls. + */ + memset ( &e820buf, 0, sizeof ( e820buf ) ); + + do { + /* Some BIOSes corrupt %esi for fun. Guard against + * this by telling gcc that all non-output registers + * may be corrupted. + */ + __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" + "stc\n\t" + "int $0x15\n\t" + "pushfw\n\t" + "popw %%dx\n\t" + "popl %%ebp\n\t" ) + : "=a" ( smap ), "=b" ( next ), + "=c" ( size ), "=d" ( flags ), + "=D" ( discard_D ) + : "a" ( 0xe820 ), "b" ( next ), + "D" ( __from_data16 ( &e820buf ) ), + "c" ( sizeof ( e820buf ) ), + "d" ( SMAP ) + : "esi", "memory" ); + + if ( smap != SMAP ) { + DBG ( "INT 15,e820 failed SMAP signature check\n" ); + return -ENOTSUP; + } + + if ( size < E820_MIN_SIZE ) { + DBG ( "INT 15,e820 returned only %zd bytes\n", size ); + return -EINVAL; + } + + if ( flags & CF ) { + DBG ( "INT 15,e820 terminated on CF set\n" ); + break; + } + + /* If first region is not RAM, assume map is invalid */ + if ( ( memmap->count == 0 ) && + ( e820buf.type != E820_TYPE_RAM ) ) { + DBG ( "INT 15,e820 failed, first entry not RAM\n" ); + return -EINVAL; + } + + DBG ( "INT 15,e820 region [%llx,%llx) type %d", + e820buf.start, ( e820buf.start + e820buf.len ), + ( int ) e820buf.type ); + if ( size > offsetof ( typeof ( e820buf ), attrs ) ) { + DBG ( " (%s", ( ( e820buf.attrs & E820_ATTR_ENABLED ) + ? "enabled" : "disabled" ) ); + if ( e820buf.attrs & E820_ATTR_NONVOLATILE ) + DBG ( ", non-volatile" ); + if ( e820buf.attrs & E820_ATTR_UNKNOWN ) + DBG ( ", other [%08x]", e820buf.attrs ); + DBG ( ")" ); + } + DBG ( "\n" ); + + /* Discard non-RAM regions */ + if ( e820buf.type != E820_TYPE_RAM ) + continue; + + /* Check extended attributes, if present */ + if ( size > offsetof ( typeof ( e820buf ), attrs ) ) { + if ( ! ( e820buf.attrs & E820_ATTR_ENABLED ) ) + continue; + if ( e820buf.attrs & E820_ATTR_NONVOLATILE ) + continue; + } + + region->start = e820buf.start; + region->end = e820buf.start + e820buf.len; + region++; + memmap->count++; + + if ( memmap->count >= ( sizeof ( memmap->regions ) / + sizeof ( memmap->regions[0] ) ) ) { + DBG ( "INT 15,e820 too many regions returned\n" ); + /* Not a fatal error; what we've got so far at + * least represents valid regions of memory, + * even if we couldn't get them all. + */ + break; + } + } while ( next != 0 ); + + /* Sanity checks. Some BIOSes report complete garbage via INT + * 15,e820 (especially at POST time), despite passing the + * signature checks. We currently check for a base memory + * region (starting at 0) and at least one high memory region + * (starting at 0x100000). + */ + if ( memmap->count < 2 ) { + DBG ( "INT 15,e820 returned only %d regions; assuming " + "insane\n", memmap->count ); + return -EINVAL; + } + if ( memmap->regions[0].start != 0 ) { + DBG ( "INT 15,e820 region 0 starts at %llx (expected 0); " + "assuming insane\n", memmap->regions[0].start ); + return -EINVAL; + } + if ( memmap->regions[1].start != 0x100000 ) { + DBG ( "INT 15,e820 region 1 starts at %llx (expected 100000); " + "assuming insane\n", memmap->regions[0].start ); + return -EINVAL; + } + + return 0; +} + +/** + * Get memory map + * + * @v memmap Memory map to fill in + */ +void get_memmap ( struct memory_map *memmap ) { + unsigned int basemem, extmem; + int rc; + + DBG ( "Fetching system memory map\n" ); + + /* Clear memory map */ + memset ( memmap, 0, sizeof ( *memmap ) ); + + /* Get base and extended memory sizes */ + basemem = basememsize(); + DBG ( "FBMS base memory size %d kB [0,%x)\n", + basemem, ( basemem * 1024 ) ); + extmem = extmemsize(); + + /* Try INT 15,e820 first */ + if ( ( rc = meme820 ( memmap ) ) == 0 ) { + DBG ( "Obtained system memory map via INT 15,e820\n" ); + return; + } + + /* Fall back to constructing a map from basemem and extmem sizes */ + DBG ( "INT 15,e820 failed; constructing map\n" ); + memmap->regions[0].end = ( basemem * 1024 ); + memmap->regions[1].start = 0x100000; + memmap->regions[1].end = 0x100000 + ( extmem * 1024 ); + memmap->count = 2; +} diff --git a/gpxe/src/arch/i386/firmware/pcbios/pnpbios.c b/gpxe/src/arch/i386/firmware/pcbios/pnpbios.c new file mode 100644 index 0000000..420d2ae --- /dev/null +++ b/gpxe/src/arch/i386/firmware/pcbios/pnpbios.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include + +/** @file + * + * PnP BIOS + * + */ + +/** PnP BIOS structure */ +struct pnp_bios { + /** Signature + * + * Must be equal to @c PNP_BIOS_SIGNATURE + */ + uint32_t signature; + /** Version as BCD (e.g. 1.0 is 0x10) */ + uint8_t version; + /** Length of this structure */ + uint8_t length; + /** System capabilities */ + uint16_t control; + /** Checksum */ + uint8_t checksum; +} __attribute__ (( packed )); + +/** Signature for a PnP BIOS structure */ +#define PNP_BIOS_SIGNATURE \ + ( ( '$' << 0 ) + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) ) + +/** + * Test address for PnP BIOS structure + * + * @v offset Offset within BIOS segment to test + * @ret rc Return status code + */ +static int is_pnp_bios ( unsigned int offset ) { + union { + struct pnp_bios pnp_bios; + uint8_t bytes[256]; /* 256 is maximum length possible */ + } u; + size_t len; + unsigned int i; + uint8_t sum = 0; + + /* Read start of header and verify signature */ + copy_from_real ( &u.pnp_bios, BIOS_SEG, offset, sizeof ( u.pnp_bios )); + if ( u.pnp_bios.signature != PNP_BIOS_SIGNATURE ) + return -EINVAL; + + /* Read whole header and verify checksum */ + len = u.pnp_bios.length; + copy_from_real ( &u.bytes, BIOS_SEG, offset, len ); + for ( i = 0 ; i < len ; i++ ) { + sum += u.bytes[i]; + } + if ( sum != 0 ) + return -EINVAL; + + DBG ( "Found PnP BIOS at %04x:%04x\n", BIOS_SEG, offset ); + + return 0; +} + +/** + * Locate Plug-and-Play BIOS + * + * @ret pnp_offset Offset of PnP BIOS structure within BIOS segment + * + * The PnP BIOS structure will be at BIOS_SEG:pnp_offset. If no PnP + * BIOS is found, -1 is returned. + */ +int find_pnp_bios ( void ) { + static int pnp_offset = 0; + + if ( pnp_offset ) + return pnp_offset; + + for ( pnp_offset = 0 ; pnp_offset < 0x10000 ; pnp_offset += 0x10 ) { + if ( is_pnp_bios ( pnp_offset ) == 0 ) + return pnp_offset; + } + + pnp_offset = -1; + return pnp_offset; +} diff --git a/gpxe/src/arch/i386/image/bootsector.c b/gpxe/src/arch/i386/image/bootsector.c new file mode 100644 index 0000000..0f297a2 --- /dev/null +++ b/gpxe/src/arch/i386/image/bootsector.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2007 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. + */ + +/** + * @file + * + * x86 bootsector image format + * + */ + +#include +#include +#include +#include + +/** Vector for storing original INT 18 handler + * + * We do not chain to this vector, so there is no need to place it in + * .text16. + */ +static struct segoff int18_vector; + +/** Vector for storing original INT 19 handler + * + * We do not chain to this vector, so there is no need to place it in + * .text16. + */ +static struct segoff int19_vector; + +/** Restart point for INT 18 or 19 */ +extern void bootsector_exec_fail ( void ); + +/** + * Jump to preloaded bootsector + * + * @v segment Real-mode segment + * @v offset Real-mode offset + * @v drive Drive number to pass to boot sector + * @ret rc Return status code + */ +int call_bootsector ( unsigned int segment, unsigned int offset, + unsigned int drive ) { + int discard_b, discard_D, discard_d; + + DBG ( "Booting from boot sector at %04x:%04x\n", segment, offset ); + + /* Hook INTs 18 and 19 to capture failure paths */ + hook_bios_interrupt ( 0x18, ( unsigned int ) bootsector_exec_fail, + &int18_vector ); + hook_bios_interrupt ( 0x19, ( unsigned int ) bootsector_exec_fail, + &int19_vector ); + + /* Boot the loaded sector + * + * We assume that the boot sector may completely destroy our + * real-mode stack, so we preserve everything we need in + * static storage. + */ + __asm__ __volatile__ ( REAL_CODE ( /* Save return address off-stack */ + "popw %%cs:saved_retaddr\n\t" + /* Save stack pointer */ + "movw %%ss, %%ax\n\t" + "movw %%ax, %%cs:saved_ss\n\t" + "movw %%sp, %%cs:saved_sp\n\t" + /* Jump to boot sector */ + "pushw %%bx\n\t" + "pushw %%di\n\t" + "sti\n\t" + "lret\n\t" + /* Preserved variables */ + "\nsaved_ss: .word 0\n\t" + "\nsaved_sp: .word 0\n\t" + "\nsaved_retaddr: .word 0\n\t" + /* Boot failure return point */ + "\nbootsector_exec_fail:\n\t" + /* Restore stack pointer */ + "movw %%cs:saved_ss, %%ax\n\t" + "movw %%ax, %%ss\n\t" + "movw %%cs:saved_sp, %%sp\n\t" + /* Return via saved address */ + "jmp *%%cs:saved_retaddr\n\t" ) + : "=b" ( discard_b ), "=D" ( discard_D ), + "=d" ( discard_d ) + : "b" ( segment ), "D" ( offset ), + "d" ( drive ) + : "eax", "ecx", "esi", "ebp" ); + + DBG ( "Booted disk returned via INT 18 or 19\n" ); + + /* Unhook INTs 18 and 19 */ + unhook_bios_interrupt ( 0x18, ( unsigned int ) bootsector_exec_fail, + &int18_vector ); + unhook_bios_interrupt ( 0x19, ( unsigned int ) bootsector_exec_fail, + &int19_vector ); + + return -ECANCELED; +} diff --git a/gpxe/src/arch/i386/image/bzimage.c b/gpxe/src/arch/i386/image/bzimage.c new file mode 100644 index 0000000..47d46ca --- /dev/null +++ b/gpxe/src/arch/i386/image/bzimage.c @@ -0,0 +1,576 @@ +/* + * Copyright (C) 2007 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. + */ + +/** + * @file + * + * Linux bzImage image format + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +FEATURE ( FEATURE_IMAGE, "bzImage", DHCP_EB_FEATURE_BZIMAGE, 1 ); + +struct image_type bzimage_image_type __image_type ( PROBE_NORMAL ); + +/** + * bzImage load context + */ +struct bzimage_load_context { + /** Real-mode kernel portion load segment address */ + unsigned int rm_kernel_seg; + /** Real-mode kernel portion load address */ + userptr_t rm_kernel; + /** Real-mode kernel portion file size */ + size_t rm_filesz; + /** Real-mode heap top (offset from rm_kernel) */ + size_t rm_heap; + /** Command line (offset from rm_kernel) */ + size_t rm_cmdline; + /** Real-mode kernel portion total memory size */ + size_t rm_memsz; + /** Non-real-mode kernel portion load address */ + userptr_t pm_kernel; + /** Non-real-mode kernel portion file and memory size */ + size_t pm_sz; +}; + +/** + * bzImage execution context + */ +struct bzimage_exec_context { + /** Real-mode kernel portion load segment address */ + unsigned int rm_kernel_seg; + /** Real-mode kernel portion load address */ + userptr_t rm_kernel; + /** Real-mode heap top (offset from rm_kernel) */ + size_t rm_heap; + /** Command line (offset from rm_kernel) */ + size_t rm_cmdline; + /** Command line maximum length */ + size_t cmdline_size; + /** Video mode */ + unsigned int vid_mode; + /** Memory limit */ + uint64_t mem_limit; + /** Initrd address */ + physaddr_t ramdisk_image; + /** Initrd size */ + physaddr_t ramdisk_size; +}; + +/** + * Parse kernel command line for bootloader parameters + * + * @v image bzImage file + * @v exec_ctx Execution context + * @v cmdline Kernel command line + * @ret rc Return status code + */ +static int bzimage_parse_cmdline ( struct image *image, + struct bzimage_exec_context *exec_ctx, + const char *cmdline ) { + char *vga; + char *mem; + + /* Look for "vga=" */ + if ( ( vga = strstr ( cmdline, "vga=" ) ) ) { + vga += 4; + if ( strcmp ( vga, "normal" ) == 0 ) { + exec_ctx->vid_mode = BZI_VID_MODE_NORMAL; + } else if ( strcmp ( vga, "ext" ) == 0 ) { + exec_ctx->vid_mode = BZI_VID_MODE_EXT; + } else if ( strcmp ( vga, "ask" ) == 0 ) { + exec_ctx->vid_mode = BZI_VID_MODE_ASK; + } else { + exec_ctx->vid_mode = strtoul ( vga, &vga, 0 ); + if ( *vga && ( *vga != ' ' ) ) { + DBGC ( image, "bzImage %p strange \"vga=\"" + "terminator '%c'\n", image, *vga ); + } + } + } + + /* Look for "mem=" */ + if ( ( mem = strstr ( cmdline, "mem=" ) ) ) { + mem += 4; + exec_ctx->mem_limit = strtoul ( mem, &mem, 0 ); + switch ( *mem ) { + case 'G': + case 'g': + exec_ctx->mem_limit <<= 10; + case 'M': + case 'm': + exec_ctx->mem_limit <<= 10; + case 'K': + case 'k': + exec_ctx->mem_limit <<= 10; + break; + case '\0': + case ' ': + break; + default: + DBGC ( image, "bzImage %p strange \"mem=\" " + "terminator '%c'\n", image, *mem ); + break; + } + exec_ctx->mem_limit -= 1; + } + + return 0; +} + +/** + * Set command line + * + * @v image bzImage image + * @v exec_ctx Execution context + * @v cmdline Kernel command line + * @ret rc Return status code + */ +static int bzimage_set_cmdline ( struct image *image, + struct bzimage_exec_context *exec_ctx, + const char *cmdline ) { + size_t cmdline_len; + + /* Copy command line down to real-mode portion */ + cmdline_len = ( strlen ( cmdline ) + 1 ); + if ( cmdline_len > exec_ctx->cmdline_size ) + cmdline_len = exec_ctx->cmdline_size; + copy_to_user ( exec_ctx->rm_kernel, exec_ctx->rm_cmdline, + cmdline, cmdline_len ); + DBGC ( image, "bzImage %p command line \"%s\"\n", image, cmdline ); + + return 0; +} + +/** + * Load initrd + * + * @v image bzImage image + * @v initrd initrd image + * @v address Address at which to load, or UNULL + * @ret len Length of loaded image, rounded up to 4 bytes + */ +static size_t bzimage_load_initrd ( struct image *image, + struct image *initrd, + userptr_t address ) { + char *filename = initrd->cmdline; + struct cpio_header cpio; + size_t offset = 0; + + /* Do not include kernel image itself as an initrd */ + if ( initrd == image ) + return 0; + + /* Create cpio header before non-prebuilt images */ + if ( filename && filename[0] ) { + size_t name_len = ( strlen ( filename ) + 1 ); + + DBGC ( image, "bzImage %p inserting initrd %p as %s\n", + image, initrd, filename ); + memset ( &cpio, '0', sizeof ( cpio ) ); + memcpy ( cpio.c_magic, CPIO_MAGIC, sizeof ( cpio.c_magic ) ); + cpio_set_field ( cpio.c_mode, 0100644 ); + cpio_set_field ( cpio.c_nlink, 1 ); + cpio_set_field ( cpio.c_filesize, initrd->len ); + cpio_set_field ( cpio.c_namesize, name_len ); + if ( address ) { + copy_to_user ( address, offset, &cpio, + sizeof ( cpio ) ); + } + offset += sizeof ( cpio ); + if ( address ) { + copy_to_user ( address, offset, filename, + name_len ); + } + offset += name_len; + offset = ( ( offset + 0x03 ) & ~0x03 ); + } + + /* Copy in initrd image body */ + if ( address ) { + DBGC ( image, "bzImage %p has initrd %p at [%lx,%lx)\n", + image, initrd, address, ( address + offset ) ); + memcpy_user ( address, offset, initrd->data, 0, + initrd->len ); + } + offset += initrd->len; + + offset = ( ( offset + 0x03 ) & ~0x03 ); + return offset; +} + +/** + * Load initrds, if any + * + * @v image bzImage image + * @v exec_ctx Execution context + * @ret rc Return status code + */ +static int bzimage_load_initrds ( struct image *image, + struct bzimage_exec_context *exec_ctx ) { + struct image *initrd; + size_t total_len = 0; + physaddr_t address; + int rc; + + /* Add up length of all initrd images */ + for_each_image ( initrd ) { + total_len += bzimage_load_initrd ( image, initrd, UNULL ); + } + + /* Give up if no initrd images found */ + if ( ! total_len ) + return 0; + + /* Find a suitable start address. Try 1MB boundaries, + * starting from the downloaded kernel image itself and + * working downwards until we hit an available region. + */ + for ( address = ( user_to_phys ( image->data, 0 ) & ~0xfffff ) ; ; + address -= 0x100000 ) { + /* Check that we're not going to overwrite the + * kernel itself. This check isn't totally + * accurate, but errs on the side of caution. + */ + if ( address <= ( BZI_LOAD_HIGH_ADDR + image->len ) ) { + DBGC ( image, "bzImage %p could not find a location " + "for initrd\n", image ); + return -ENOBUFS; + } + /* Check that we are within the kernel's range */ + if ( ( address + total_len - 1 ) > exec_ctx->mem_limit ) + continue; + /* Prepare and verify segment */ + if ( ( rc = prep_segment ( phys_to_user ( address ), 0, + total_len ) ) != 0 ) + continue; + /* Use this address */ + break; + } + + /* Record initrd location */ + exec_ctx->ramdisk_image = address; + exec_ctx->ramdisk_size = total_len; + + /* Construct initrd */ + DBGC ( image, "bzImage %p constructing initrd at [%lx,%lx)\n", + image, address, ( address + total_len ) ); + for_each_image ( initrd ) { + address += bzimage_load_initrd ( image, initrd, + phys_to_user ( address ) ); + } + + return 0; +} + +/** + * Execute bzImage image + * + * @v image bzImage image + * @ret rc Return status code + */ +static int bzimage_exec ( struct image *image ) { + struct bzimage_exec_context exec_ctx; + struct bzimage_header bzhdr; + const char *cmdline = ( image->cmdline ? image->cmdline : "" ); + int rc; + + /* Initialise context */ + memset ( &exec_ctx, 0, sizeof ( exec_ctx ) ); + + /* Retrieve kernel header */ + exec_ctx.rm_kernel_seg = image->priv.ul; + exec_ctx.rm_kernel = real_to_user ( exec_ctx.rm_kernel_seg, 0 ); + copy_from_user ( &bzhdr, exec_ctx.rm_kernel, BZI_HDR_OFFSET, + sizeof ( bzhdr ) ); + exec_ctx.rm_cmdline = exec_ctx.rm_heap = + ( bzhdr.heap_end_ptr + 0x200 ); + exec_ctx.vid_mode = bzhdr.vid_mode; + if ( bzhdr.version >= 0x0203 ) { + exec_ctx.mem_limit = bzhdr.initrd_addr_max; + } else { + exec_ctx.mem_limit = BZI_INITRD_MAX; + } + if ( bzhdr.version >= 0x0206 ) { + exec_ctx.cmdline_size = bzhdr.cmdline_size; + } else { + exec_ctx.cmdline_size = BZI_CMDLINE_SIZE; + } + DBG ( "cmdline_size = %zd\n", exec_ctx.cmdline_size ); + + /* Parse command line for bootloader parameters */ + if ( ( rc = bzimage_parse_cmdline ( image, &exec_ctx, cmdline ) ) != 0) + return rc; + + /* Store command line */ + if ( ( rc = bzimage_set_cmdline ( image, &exec_ctx, cmdline ) ) != 0 ) + return rc; + + /* Load any initrds */ + if ( ( rc = bzimage_load_initrds ( image, &exec_ctx ) ) != 0 ) + return rc; + + /* Update and store kernel header */ + bzhdr.vid_mode = exec_ctx.vid_mode; + bzhdr.ramdisk_image = exec_ctx.ramdisk_image; + bzhdr.ramdisk_size = exec_ctx.ramdisk_size; + copy_to_user ( exec_ctx.rm_kernel, BZI_HDR_OFFSET, &bzhdr, + sizeof ( bzhdr ) ); + + /* Prepare for exiting */ + shutdown ( SHUTDOWN_BOOT ); + + DBGC ( image, "bzImage %p jumping to RM kernel at %04x:0000 " + "(stack %04x:%04zx)\n", image, + ( exec_ctx.rm_kernel_seg + 0x20 ), + exec_ctx.rm_kernel_seg, exec_ctx.rm_heap ); + + /* Jump to the kernel */ + __asm__ __volatile__ ( REAL_CODE ( "movw %w0, %%ds\n\t" + "movw %w0, %%es\n\t" + "movw %w0, %%fs\n\t" + "movw %w0, %%gs\n\t" + "movw %w0, %%ss\n\t" + "movw %w1, %%sp\n\t" + "pushw %w2\n\t" + "pushw $0\n\t" + "lret\n\t" ) + : : "r" ( exec_ctx.rm_kernel_seg ), + "r" ( exec_ctx.rm_heap ), + "r" ( exec_ctx.rm_kernel_seg + 0x20 ) ); + + /* There is no way for the image to return, since we provide + * no return address. + */ + assert ( 0 ); + + return -ECANCELED; /* -EIMPOSSIBLE */ +} + +/** + * Load and parse bzImage header + * + * @v image bzImage file + * @v load_ctx Load context + * @v bzhdr Buffer for bzImage header + * @ret rc Return status code + */ +static int bzimage_load_header ( struct image *image, + struct bzimage_load_context *load_ctx, + struct bzimage_header *bzhdr ) { + + /* Sanity check */ + if ( image->len < ( BZI_HDR_OFFSET + sizeof ( *bzhdr ) ) ) { + DBGC ( image, "bzImage %p too short for kernel header\n", + image ); + return -ENOEXEC; + } + + /* Read and verify header */ + copy_from_user ( bzhdr, image->data, BZI_HDR_OFFSET, + sizeof ( *bzhdr ) ); + if ( bzhdr->header != BZI_SIGNATURE ) { + DBGC ( image, "bzImage %p bad signature %08x\n", + image, bzhdr->header ); + return -ENOEXEC; + } + + /* We don't support ancient kernels */ + if ( bzhdr->version < 0x0200 ) { + DBGC ( image, "bzImage %p version %04x not supported\n", + image, bzhdr->version ); + return -ENOTSUP; + } + + /* Calculate load address and size of real-mode portion */ + load_ctx->rm_kernel_seg = ( ( bzhdr->loadflags & BZI_LOAD_HIGH ) ? + 0x1000 : /* 1000:0000 (bzImage) */ + 0x9000 ); /* 9000:0000 (zImage) */ + load_ctx->rm_kernel = real_to_user ( load_ctx->rm_kernel_seg, 0 ); + load_ctx->rm_filesz = + ( ( bzhdr->setup_sects ? bzhdr->setup_sects : 4 ) + 1 ) << 9; + load_ctx->rm_memsz = BZI_ASSUMED_RM_SIZE; + if ( load_ctx->rm_filesz > image->len ) { + DBGC ( image, "bzImage %p too short for %zd byte of setup\n", + image, load_ctx->rm_filesz ); + return -ENOEXEC; + } + + /* Calculate load address and size of non-real-mode portion */ + load_ctx->pm_kernel = ( ( bzhdr->loadflags & BZI_LOAD_HIGH ) ? + phys_to_user ( BZI_LOAD_HIGH_ADDR ) : + phys_to_user ( BZI_LOAD_LOW_ADDR ) ); + load_ctx->pm_sz = ( image->len - load_ctx->rm_filesz ); + + DBGC ( image, "bzImage %p version %04x RM %#zx bytes PM %#zx bytes\n", + image, bzhdr->version, load_ctx->rm_filesz, load_ctx->pm_sz ); + return 0; +} + +/** + * Load real-mode portion of bzImage + * + * @v image bzImage file + * @v load_ctx Load context + * @ret rc Return status code + */ +static int bzimage_load_real ( struct image *image, + struct bzimage_load_context *load_ctx ) { + int rc; + + /* Allow space for the stack and heap */ + load_ctx->rm_memsz += BZI_STACK_SIZE; + load_ctx->rm_heap = load_ctx->rm_memsz; + + /* Allow space for the command line */ + load_ctx->rm_cmdline = load_ctx->rm_memsz; + load_ctx->rm_memsz += BZI_CMDLINE_SIZE; + + /* Prepare, verify, and load the real-mode segment */ + if ( ( rc = prep_segment ( load_ctx->rm_kernel, load_ctx->rm_filesz, + load_ctx->rm_memsz ) ) != 0 ) { + DBGC ( image, "bzImage %p could not prepare RM segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + memcpy_user ( load_ctx->rm_kernel, 0, image->data, 0, + load_ctx->rm_filesz ); + + return 0; +} + +/** + * Load non-real-mode portion of bzImage + * + * @v image bzImage file + * @v load_ctx Load context + * @ret rc Return status code + */ +static int bzimage_load_non_real ( struct image *image, + struct bzimage_load_context *load_ctx ) { + int rc; + + /* Prepare, verify and load the non-real-mode segment */ + if ( ( rc = prep_segment ( load_ctx->pm_kernel, load_ctx->pm_sz, + load_ctx->pm_sz ) ) != 0 ) { + DBGC ( image, "bzImage %p could not prepare PM segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + memcpy_user ( load_ctx->pm_kernel, 0, image->data, load_ctx->rm_filesz, + load_ctx->pm_sz ); + + return 0; +} + +/** + * Update and store bzImage header + * + * @v image bzImage file + * @v load_ctx Load context + * @v bzhdr Original bzImage header + * @ret rc Return status code + */ +static int bzimage_write_header ( struct image *image __unused, + struct bzimage_load_context *load_ctx, + struct bzimage_header *bzhdr ) { + struct bzimage_cmdline cmdline; + + /* Update the header and copy it into the loaded kernel */ + bzhdr->type_of_loader = BZI_LOADER_TYPE_GPXE; + if ( bzhdr->version >= 0x0201 ) { + bzhdr->heap_end_ptr = ( load_ctx->rm_heap - 0x200 ); + bzhdr->loadflags |= BZI_CAN_USE_HEAP; + } + if ( bzhdr->version >= 0x0202 ) { + bzhdr->cmd_line_ptr = user_to_phys ( load_ctx->rm_kernel, + load_ctx->rm_cmdline ); + } else { + cmdline.magic = BZI_CMDLINE_MAGIC; + cmdline.offset = load_ctx->rm_cmdline; + copy_to_user ( load_ctx->rm_kernel, BZI_CMDLINE_OFFSET, + &cmdline, sizeof ( cmdline ) ); + bzhdr->setup_move_size = load_ctx->rm_memsz; + } + copy_to_user ( load_ctx->rm_kernel, BZI_HDR_OFFSET, + bzhdr, sizeof ( *bzhdr ) ); + + return 0; +} + +/** + * Load bzImage image into memory + * + * @v image bzImage file + * @ret rc Return status code + */ +int bzimage_load ( struct image *image ) { + struct bzimage_load_context load_ctx; + struct bzimage_header bzhdr; + int rc; + + /* Initialise context */ + memset ( &load_ctx, 0, sizeof ( load_ctx ) ); + + /* Load and verify header */ + if ( ( rc = bzimage_load_header ( image, &load_ctx, &bzhdr ) ) != 0 ) + return rc; + + /* This is a bzImage image, valid or otherwise */ + if ( ! image->type ) + image->type = &bzimage_image_type; + + /* Load real-mode portion */ + if ( ( rc = bzimage_load_real ( image, &load_ctx ) ) != 0 ) + return rc; + + /* Load non-real-mode portion */ + if ( ( rc = bzimage_load_non_real ( image, &load_ctx ) ) != 0 ) + return rc; + + /* Update and write out header */ + if ( ( rc = bzimage_write_header ( image, &load_ctx, &bzhdr ) ) != 0 ) + return rc; + + /* Record real-mode segment in image private data field */ + image->priv.ul = load_ctx.rm_kernel_seg; + + return 0; +} + +/** Linux bzImage image type */ +struct image_type bzimage_image_type __image_type ( PROBE_NORMAL ) = { + .name = "bzImage", + .load = bzimage_load, + .exec = bzimage_exec, +}; diff --git a/gpxe/src/arch/i386/image/com32.c b/gpxe/src/arch/i386/image/com32.c new file mode 100644 index 0000000..d1b9a59 --- /dev/null +++ b/gpxe/src/arch/i386/image/com32.c @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2008 Daniel Verkamp . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * @file + * + * SYSLINUX COM32 image format + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct image_type com32_image_type __image_type ( PROBE_NORMAL ); + +/** + * Execute COMBOOT image + * + * @v image COM32 image + * @ret rc Return status code + */ +static int com32_exec ( struct image *image ) { + struct memory_map memmap; + unsigned int i; + int state; + uint32_t avail_mem_top; + + state = rmsetjmp ( comboot_return ); + + switch ( state ) { + case 0: /* First time through; invoke COM32 program */ + + /* Get memory map */ + get_memmap ( &memmap ); + + /* Find end of block covering COM32 image loading area */ + for ( i = 0, avail_mem_top = 0 ; i < memmap.count ; i++ ) { + if ( (memmap.regions[i].start <= COM32_START_PHYS) && + (memmap.regions[i].end > COM32_START_PHYS + image->len) ) { + avail_mem_top = memmap.regions[i].end; + break; + } + } + + DBGC ( image, "COM32 %p: available memory top = 0x%x\n", + image, avail_mem_top ); + + assert ( avail_mem_top != 0 ); + + com32_external_esp = phys_to_virt ( avail_mem_top ); + + /* Hook COMBOOT API interrupts */ + hook_comboot_interrupts(); + + /* Unregister image, so that a "boot" command doesn't + * throw us into an execution loop. We never + * reregister ourselves; COMBOOT images expect to be + * removed on exit. + */ + unregister_image ( image ); + + __asm__ __volatile__ ( + "movl %%esp, (com32_internal_esp)\n\t" /* Save internal virtual address space ESP */ + "movl (com32_external_esp), %%esp\n\t" /* Switch to COM32 ESP (top of available memory) */ + "call _virt_to_phys\n\t" /* Switch to flat physical address space */ + "pushl %0\n\t" /* Pointer to CDECL helper function */ + "pushl %1\n\t" /* Pointer to FAR call helper function */ + "pushl %2\n\t" /* Size of low memory bounce buffer */ + "pushl %3\n\t" /* Pointer to low memory bounce buffer */ + "pushl %4\n\t" /* Pointer to INT call helper function */ + "pushl %5\n\t" /* Pointer to the command line arguments */ + "pushl $6\n\t" /* Number of additional arguments */ + "call *%6\n\t" /* Execute image */ + "call _phys_to_virt\n\t" /* Switch back to internal virtual address space */ + "movl (com32_internal_esp), %%esp\n\t" /* Switch back to internal stack */ + : + : + /* %0 */ "r" ( virt_to_phys ( com32_cfarcall_wrapper ) ), + /* %1 */ "r" ( virt_to_phys ( com32_farcall_wrapper ) ), + /* %2 */ "r" ( get_fbms() * 1024 - (COM32_BOUNCE_SEG << 4) ), + /* %3 */ "i" ( COM32_BOUNCE_SEG << 4 ), + /* %4 */ "r" ( virt_to_phys ( com32_intcall_wrapper ) ), + /* %5 */ "r" ( virt_to_phys ( image->cmdline ) ), + /* %6 */ "r" ( COM32_START_PHYS ) + : + "memory" ); + DBGC ( image, "COM32 %p: returned\n", image ); + break; + + case COMBOOT_EXIT: + DBGC ( image, "COM32 %p: exited\n", image ); + break; + + case COMBOOT_EXIT_RUN_KERNEL: + DBGC ( image, "COM32 %p: exited to run kernel %p\n", + image, comboot_replacement_image ); + image->replacement = comboot_replacement_image; + comboot_replacement_image = NULL; + image_autoload ( image->replacement ); + break; + + case COMBOOT_EXIT_COMMAND: + DBGC ( image, "COM32 %p: exited after executing command\n", + image ); + break; + + default: + assert ( 0 ); + break; + } + + unhook_comboot_interrupts(); + comboot_force_text_mode(); + + return 0; +} + +/** + * Check image name extension + * + * @v image COM32 image + * @ret rc Return status code + */ +static int com32_identify ( struct image *image ) { + const char *ext; + static const uint8_t magic[] = { 0xB8, 0xFF, 0x4C, 0xCD, 0x21 }; + uint8_t buf[5]; + + if ( image->len >= 5 ) { + /* Check for magic number + * mov eax,21cd4cffh + * B8 FF 4C CD 21 + */ + copy_from_user ( buf, image->data, 0, sizeof(buf) ); + if ( ! memcmp ( buf, magic, sizeof(buf) ) ) { + DBGC ( image, "COM32 %p: found magic number\n", + image ); + return 0; + } + } + + /* Magic number not found; check filename extension */ + + ext = strrchr( image->name, '.' ); + + if ( ! ext ) { + DBGC ( image, "COM32 %p: no extension\n", + image ); + return -ENOEXEC; + } + + ++ext; + + if ( strcasecmp( ext, "c32" ) ) { + DBGC ( image, "COM32 %p: unrecognized extension %s\n", + image, ext ); + return -ENOEXEC; + } + + return 0; +} + + +/** + * Load COM32 image into memory + * @v image COM32 image + * @ret rc Return status code + */ +static int comboot_load_image ( struct image *image ) { + size_t filesz, memsz; + userptr_t buffer; + int rc; + + filesz = image->len; + memsz = filesz; + buffer = phys_to_user ( COM32_START_PHYS ); + if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) { + DBGC ( image, "COM32 %p: could not prepare segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + /* Copy image to segment */ + memcpy_user ( buffer, 0, image->data, 0, filesz ); + + return 0; +} + +/** + * Prepare COM32 low memory bounce buffer + * @v image COM32 image + * @ret rc Return status code + */ +static int comboot_prepare_bounce_buffer ( struct image * image ) { + unsigned int seg; + userptr_t seg_userptr; + size_t filesz, memsz; + int rc; + + seg = COM32_BOUNCE_SEG; + seg_userptr = real_to_user ( seg, 0 ); + + /* Ensure the entire 64k segment is free */ + memsz = 0xFFFF; + filesz = 0; + + /* Prepare, verify, and load the real-mode segment */ + if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) { + DBGC ( image, "COM32 %p: could not prepare bounce buffer segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Load COM32 image into memory + * + * @v image COM32 image + * @ret rc Return status code + */ +static int com32_load ( struct image *image ) { + int rc; + + DBGC ( image, "COM32 %p: name '%s', cmdline '%s'\n", + image, image->name, image->cmdline ); + + /* Check if this is a COMBOOT image */ + if ( ( rc = com32_identify ( image ) ) != 0 ) { + return rc; + } + + /* This is a COM32 image, valid or otherwise */ + if ( ! image->type ) + image->type = &com32_image_type; + + /* Load image */ + if ( ( rc = comboot_load_image ( image ) ) != 0 ) { + return rc; + } + + /* Prepare bounce buffer segment */ + if ( ( rc = comboot_prepare_bounce_buffer ( image ) ) != 0 ) { + return rc; + } + + return 0; +} + +/** SYSLINUX COM32 image type */ +struct image_type com32_image_type __image_type ( PROBE_NORMAL ) = { + .name = "COM32", + .load = com32_load, + .exec = com32_exec, +}; diff --git a/gpxe/src/arch/i386/image/comboot.c b/gpxe/src/arch/i386/image/comboot.c new file mode 100644 index 0000000..40e3218 --- /dev/null +++ b/gpxe/src/arch/i386/image/comboot.c @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2008 Daniel Verkamp . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * @file + * + * SYSLINUX COMBOOT (16-bit) image format + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +FEATURE ( FEATURE_IMAGE, "COMBOOT", DHCP_EB_FEATURE_COMBOOT, 1 ); + +struct image_type comboot_image_type __image_type ( PROBE_NORMAL ); + +/** + * COMBOOT PSP, copied to offset 0 of code segment + */ +struct comboot_psp { + /** INT 20 instruction, executed if COMBOOT image returns with RET */ + uint16_t int20; + /** Segment of first non-free paragraph of memory */ + uint16_t first_non_free_para; +}; + +/** Offset in PSP of command line */ +#define COMBOOT_PSP_CMDLINE_OFFSET 0x81 + +/** Maximum length of command line in PSP + * (127 bytes minus space and CR) */ +#define COMBOOT_MAX_CMDLINE_LEN 125 + + +/** + * Copy command line to PSP + * + * @v image COMBOOT image + */ +static void comboot_copy_cmdline ( struct image * image, userptr_t seg_userptr ) { + const char *cmdline = ( image->cmdline ? image->cmdline : "" ); + int cmdline_len = strlen ( cmdline ); + if( cmdline_len > COMBOOT_MAX_CMDLINE_LEN ) + cmdline_len = COMBOOT_MAX_CMDLINE_LEN; + uint8_t len_byte = cmdline_len; + char spc = ' ', cr = '\r'; + + /* Copy length to byte before command line */ + copy_to_user ( seg_userptr, COMBOOT_PSP_CMDLINE_OFFSET - 1, + &len_byte, 1 ); + + /* Command line starts with space */ + copy_to_user ( seg_userptr, + COMBOOT_PSP_CMDLINE_OFFSET, + &spc, 1 ); + + /* Copy command line */ + copy_to_user ( seg_userptr, + COMBOOT_PSP_CMDLINE_OFFSET + 1, + cmdline, cmdline_len ); + + /* Command line ends with CR */ + copy_to_user ( seg_userptr, + COMBOOT_PSP_CMDLINE_OFFSET + cmdline_len + 1, + &cr, 1 ); +} + +/** + * Initialize PSP + * + * @v image COMBOOT image + * @v seg_userptr segment to initialize + */ +static void comboot_init_psp ( struct image * image, userptr_t seg_userptr ) { + struct comboot_psp psp; + + /* Fill PSP */ + + /* INT 20h instruction, byte order reversed */ + psp.int20 = 0x20CD; + + /* get_fbms() returns BIOS free base memory counter, which is in + * kilobytes; x * 1024 / 16 == x * 64 == x << 6 */ + psp.first_non_free_para = get_fbms() << 6; + + DBGC ( image, "COMBOOT %p: first non-free paragraph = 0x%x\n", + image, psp.first_non_free_para ); + + /* Copy the PSP to offset 0 of segment. + * The rest of the PSP was already zeroed by + * comboot_prepare_segment. */ + copy_to_user ( seg_userptr, 0, &psp, sizeof( psp ) ); + + /* Copy the command line to the PSP */ + comboot_copy_cmdline ( image, seg_userptr ); +} + +/** + * Execute COMBOOT image + * + * @v image COMBOOT image + * @ret rc Return status code + */ +static int comboot_exec ( struct image *image ) { + userptr_t seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 ); + int state; + + state = rmsetjmp ( comboot_return ); + + switch ( state ) { + case 0: /* First time through; invoke COMBOOT program */ + + /* Initialize PSP */ + comboot_init_psp ( image, seg_userptr ); + + /* Hook COMBOOT API interrupts */ + hook_comboot_interrupts(); + + DBGC ( image, "executing 16-bit COMBOOT image at %4x:0100\n", + COMBOOT_PSP_SEG ); + + /* Unregister image, so that a "boot" command doesn't + * throw us into an execution loop. We never + * reregister ourselves; COMBOOT images expect to be + * removed on exit. + */ + unregister_image ( image ); + + /* Store stack segment at 0x38 and stack pointer at 0x3A + * in the PSP and jump to the image */ + __asm__ __volatile__ ( + REAL_CODE ( /* Save return address with segment on old stack */ + "popw %%ax\n\t" + "pushw %%cs\n\t" + "pushw %%ax\n\t" + /* Set DS=ES=segment with image */ + "movw %w0, %%ds\n\t" + "movw %w0, %%es\n\t" + /* Set SS:SP to new stack (end of image segment) */ + "movw %w0, %%ss\n\t" + "xor %%sp, %%sp\n\t" + "pushw $0\n\t" + "pushw %w0\n\t" + "pushw $0x100\n\t" + /* Zero registers (some COM files assume GP regs are 0) */ + "xorw %%ax, %%ax\n\t" + "xorw %%bx, %%bx\n\t" + "xorw %%cx, %%cx\n\t" + "xorw %%dx, %%dx\n\t" + "xorw %%si, %%si\n\t" + "xorw %%di, %%di\n\t" + "xorw %%bp, %%bp\n\t" + "lret\n\t" ) + : : "r" ( COMBOOT_PSP_SEG ) : "eax" ); + DBGC ( image, "COMBOOT %p: returned\n", image ); + break; + + case COMBOOT_EXIT: + DBGC ( image, "COMBOOT %p: exited\n", image ); + break; + + case COMBOOT_EXIT_RUN_KERNEL: + DBGC ( image, "COMBOOT %p: exited to run kernel %p\n", + image, comboot_replacement_image ); + image->replacement = comboot_replacement_image; + comboot_replacement_image = NULL; + image_autoload ( image->replacement ); + break; + + case COMBOOT_EXIT_COMMAND: + DBGC ( image, "COMBOOT %p: exited after executing command\n", + image ); + break; + + default: + assert ( 0 ); + break; + } + + unhook_comboot_interrupts(); + comboot_force_text_mode(); + + return 0; +} + +/** + * Check image name extension + * + * @v image COMBOOT image + * @ret rc Return status code + */ +static int comboot_identify ( struct image *image ) { + const char *ext; + + ext = strrchr( image->name, '.' ); + + if ( ! ext ) { + DBGC ( image, "COMBOOT %p: no extension\n", + image ); + return -ENOEXEC; + } + + ++ext; + + if ( strcasecmp( ext, "com" ) && strcasecmp( ext, "cbt" ) ) { + DBGC ( image, "COMBOOT %p: unrecognized extension %s\n", + image, ext ); + return -ENOEXEC; + } + + return 0; +} + +/** + * Load COMBOOT image into memory, preparing a segment and returning it + * @v image COMBOOT image + * @ret rc Return status code + */ +static int comboot_prepare_segment ( struct image *image ) +{ + userptr_t seg_userptr; + size_t filesz, memsz; + int rc; + + /* Load image in segment */ + seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 ); + + /* Allow etra 0x100 bytes before image for PSP */ + filesz = image->len + 0x100; + + /* Ensure the entire 64k segment is free */ + memsz = 0xFFFF; + + /* Prepare, verify, and load the real-mode segment */ + if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) { + DBGC ( image, "COMBOOT %p: could not prepare segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + /* Zero PSP */ + memset_user ( seg_userptr, 0, 0, 0x100 ); + + /* Copy image to segment:0100 */ + memcpy_user ( seg_userptr, 0x100, image->data, 0, image->len ); + + return 0; +} + +/** + * Load COMBOOT image into memory + * + * @v image COMBOOT image + * @ret rc Return status code + */ +static int comboot_load ( struct image *image ) { + int rc; + + DBGC ( image, "COMBOOT %p: name '%s'\n", + image, image->name ); + + /* Check if this is a COMBOOT image */ + if ( ( rc = comboot_identify ( image ) ) != 0 ) { + + return rc; + } + + /* This is a 16-bit COMBOOT image, valid or otherwise */ + if ( ! image->type ) + image->type = &comboot_image_type; + + /* Sanity check for filesize */ + if( image->len >= 0xFF00 ) { + DBGC( image, "COMBOOT %p: image too large\n", + image ); + return -ENOEXEC; + } + + /* Prepare segment and load image */ + if ( ( rc = comboot_prepare_segment ( image ) ) != 0 ) { + return rc; + } + + return 0; +} + +/** SYSLINUX COMBOOT (16-bit) image type */ +struct image_type comboot_image_type __image_type ( PROBE_NORMAL ) = { + .name = "COMBOOT", + .load = comboot_load, + .exec = comboot_exec, +}; diff --git a/gpxe/src/arch/i386/image/elfboot.c b/gpxe/src/arch/i386/image/elfboot.c new file mode 100644 index 0000000..c8daf72 --- /dev/null +++ b/gpxe/src/arch/i386/image/elfboot.c @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include +#include +#include +#include + +/** + * @file + * + * ELF bootable image + * + */ + +FEATURE ( FEATURE_IMAGE, "ELF", DHCP_EB_FEATURE_ELF, 1 ); + +struct image_type elfboot_image_type __image_type ( PROBE_NORMAL ); + +/** + * Execute ELF image + * + * @v image ELF image + * @ret rc Return status code + */ +static int elfboot_exec ( struct image *image ) { + physaddr_t entry = image->priv.phys; + + /* An ELF image has no callback interface, so we need to shut + * down before invoking it. + */ + shutdown ( SHUTDOWN_BOOT ); + + /* Jump to OS with flat physical addressing */ + DBGC ( image, "ELF %p starting execution at %lx\n", image, entry ); + __asm__ __volatile__ ( PHYS_CODE ( "call *%%edi\n\t" ) + : : "D" ( entry ) + : "eax", "ebx", "ecx", "edx", "esi", "ebp", + "memory" ); + + DBGC ( image, "ELF %p returned\n", image ); + + /* It isn't safe to continue after calling shutdown() */ + while ( 1 ) {} + + return -ECANCELED; /* -EIMPOSSIBLE, anyone? */ +} + +/** + * Load ELF image into memory + * + * @v image ELF file + * @ret rc Return status code + */ +static int elfboot_load ( struct image *image ) { + Elf32_Ehdr ehdr; + static const uint8_t e_ident[] = { + [EI_MAG0] = ELFMAG0, + [EI_MAG1] = ELFMAG1, + [EI_MAG2] = ELFMAG2, + [EI_MAG3] = ELFMAG3, + [EI_CLASS] = ELFCLASS32, + [EI_DATA] = ELFDATA2LSB, + [EI_VERSION] = EV_CURRENT, + }; + int rc; + + /* Read ELF header */ + copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) ); + if ( memcmp ( ehdr.e_ident, e_ident, sizeof ( e_ident ) ) != 0 ) { + DBG ( "Invalid ELF identifier\n" ); + return -ENOEXEC; + } + + /* This is an ELF image, valid or otherwise */ + if ( ! image->type ) + image->type = &elfboot_image_type; + + /* Load the image using core ELF support */ + if ( ( rc = elf_load ( image ) ) != 0 ) { + DBGC ( image, "ELF %p could not load: %s\n", + image, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** ELF image type */ +struct image_type elfboot_image_type __image_type ( PROBE_NORMAL ) = { + .name = "ELF", + .load = elfboot_load, + .exec = elfboot_exec, +}; diff --git a/gpxe/src/arch/i386/image/eltorito.c b/gpxe/src/arch/i386/image/eltorito.c new file mode 100644 index 0000000..9d57310 --- /dev/null +++ b/gpxe/src/arch/i386/image/eltorito.c @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2007 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. + */ + +/** + * @file + * + * El Torito bootable ISO image format + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ISO9660_BLKSIZE 2048 +#define ELTORITO_VOL_DESC_OFFSET ( 17 * ISO9660_BLKSIZE ) + +/** An El Torito Boot Record Volume Descriptor */ +struct eltorito_vol_desc { + /** Boot record indicator; must be 0 */ + uint8_t record_indicator; + /** ISO-9660 identifier; must be "CD001" */ + uint8_t iso9660_id[5]; + /** Version, must be 1 */ + uint8_t version; + /** Boot system indicator; must be "EL TORITO SPECIFICATION" */ + uint8_t system_indicator[32]; + /** Unused */ + uint8_t unused[32]; + /** Boot catalog sector */ + uint32_t sector; +} __attribute__ (( packed )); + +/** An El Torito Boot Catalog Validation Entry */ +struct eltorito_validation_entry { + /** Header ID; must be 1 */ + uint8_t header_id; + /** Platform ID + * + * 0 = 80x86 + * 1 = PowerPC + * 2 = Mac + */ + uint8_t platform_id; + /** Reserved */ + uint16_t reserved; + /** ID string */ + uint8_t id_string[24]; + /** Checksum word */ + uint16_t checksum; + /** Signature; must be 0xaa55 */ + uint16_t signature; +} __attribute__ (( packed )); + +/** A bootable entry in the El Torito Boot Catalog */ +struct eltorito_boot_entry { + /** Boot indicator + * + * Must be @c ELTORITO_BOOTABLE for a bootable ISO image + */ + uint8_t indicator; + /** Media type + * + */ + uint8_t media_type; + /** Load segment */ + uint16_t load_segment; + /** System type */ + uint8_t filesystem; + /** Unused */ + uint8_t reserved_a; + /** Sector count */ + uint16_t length; + /** Starting sector */ + uint32_t start; + /** Unused */ + uint8_t reserved_b[20]; +} __attribute__ (( packed )); + +/** Boot indicator for a bootable ISO image */ +#define ELTORITO_BOOTABLE 0x88 + +/** El Torito media types */ +enum eltorito_media_type { + /** No emulation */ + ELTORITO_NO_EMULATION = 0, +}; + +struct image_type eltorito_image_type __image_type ( PROBE_NORMAL ); + +/** + * Calculate 16-bit word checksum + * + * @v data Data to checksum + * @v len Length (in bytes, must be even) + * @ret sum Checksum + */ +static unsigned int word_checksum ( void *data, size_t len ) { + uint16_t *words; + uint16_t sum = 0; + + for ( words = data ; len ; words++, len -= 2 ) { + sum += *words; + } + return sum; +} + +/** + * Execute El Torito image + * + * @v image El Torito image + * @ret rc Return status code + */ +static int eltorito_exec ( struct image *image ) { + struct ramdisk ramdisk; + struct int13_drive int13_drive; + unsigned int load_segment = image->priv.ul; + unsigned int load_offset = ( load_segment ? 0 : 0x7c00 ); + int rc; + + memset ( &ramdisk, 0, sizeof ( ramdisk ) ); + init_ramdisk ( &ramdisk, image->data, image->len, ISO9660_BLKSIZE ); + + memset ( &int13_drive, 0, sizeof ( int13_drive ) ); + int13_drive.blockdev = &ramdisk.blockdev; + register_int13_drive ( &int13_drive ); + + if ( ( rc = call_bootsector ( load_segment, load_offset, + int13_drive.drive ) ) != 0 ) { + DBGC ( image, "ElTorito %p boot failed: %s\n", + image, strerror ( rc ) ); + goto err; + } + + rc = -ECANCELED; /* -EIMPOSSIBLE */ + err: + unregister_int13_drive ( &int13_drive ); + return rc; +} + +/** + * Read and verify El Torito Boot Record Volume Descriptor + * + * @v image El Torito file + * @ret catalog_offset Offset of Boot Catalog + * @ret rc Return status code + */ +static int eltorito_read_voldesc ( struct image *image, + unsigned long *catalog_offset ) { + static const struct eltorito_vol_desc vol_desc_signature = { + .record_indicator = 0, + .iso9660_id = "CD001", + .version = 1, + .system_indicator = "EL TORITO SPECIFICATION", + }; + struct eltorito_vol_desc vol_desc; + + /* Sanity check */ + if ( image->len < ( ELTORITO_VOL_DESC_OFFSET + ISO9660_BLKSIZE ) ) { + DBGC ( image, "ElTorito %p too short\n", image ); + return -ENOEXEC; + } + + /* Read and verify Boot Record Volume Descriptor */ + copy_from_user ( &vol_desc, image->data, ELTORITO_VOL_DESC_OFFSET, + sizeof ( vol_desc ) ); + if ( memcmp ( &vol_desc, &vol_desc_signature, + offsetof ( typeof ( vol_desc ), sector ) ) != 0 ) { + DBGC ( image, "ElTorito %p invalid Boot Record Volume " + "Descriptor\n", image ); + return -ENOEXEC; + } + *catalog_offset = ( vol_desc.sector * ISO9660_BLKSIZE ); + + DBGC ( image, "ElTorito %p boot catalog at offset %#lx\n", + image, *catalog_offset ); + + return 0; +} + +/** + * Read and verify El Torito Boot Catalog + * + * @v image El Torito file + * @v catalog_offset Offset of Boot Catalog + * @ret boot_entry El Torito boot entry + * @ret rc Return status code + */ +static int eltorito_read_catalog ( struct image *image, + unsigned long catalog_offset, + struct eltorito_boot_entry *boot_entry ) { + struct eltorito_validation_entry validation_entry; + + /* Sanity check */ + if ( image->len < ( catalog_offset + ISO9660_BLKSIZE ) ) { + DBGC ( image, "ElTorito %p bad boot catalog offset %#lx\n", + image, catalog_offset ); + return -ENOEXEC; + } + + /* Read and verify the Validation Entry of the Boot Catalog */ + copy_from_user ( &validation_entry, image->data, catalog_offset, + sizeof ( validation_entry ) ); + if ( word_checksum ( &validation_entry, + sizeof ( validation_entry ) ) != 0 ) { + DBGC ( image, "ElTorito %p bad Validation Entry checksum\n", + image ); + return -ENOEXEC; + } + + /* Read and verify the Initial/Default entry */ + copy_from_user ( boot_entry, image->data, + ( catalog_offset + sizeof ( validation_entry ) ), + sizeof ( *boot_entry ) ); + if ( boot_entry->indicator != ELTORITO_BOOTABLE ) { + DBGC ( image, "ElTorito %p not bootable\n", image ); + return -ENOEXEC; + } + if ( boot_entry->media_type != ELTORITO_NO_EMULATION ) { + DBGC ( image, "ElTorito %p cannot support media type %d\n", + image, boot_entry->media_type ); + return -ENOTSUP; + } + + DBGC ( image, "ElTorito %p media type %d segment %04x\n", + image, boot_entry->media_type, boot_entry->load_segment ); + + return 0; +} + +/** + * Load El Torito virtual disk image into memory + * + * @v image El Torito file + * @v boot_entry El Torito boot entry + * @ret rc Return status code + */ +static int eltorito_load_disk ( struct image *image, + struct eltorito_boot_entry *boot_entry ) { + unsigned long start = ( boot_entry->start * ISO9660_BLKSIZE ); + unsigned long length = ( boot_entry->length * ISO9660_BLKSIZE ); + unsigned int load_segment; + userptr_t buffer; + int rc; + + /* Sanity check */ + if ( image->len < ( start + length ) ) { + DBGC ( image, "ElTorito %p virtual disk lies outside image\n", + image ); + return -ENOEXEC; + } + DBGC ( image, "ElTorito %p virtual disk at %#lx+%#lx\n", + image, start, length ); + + /* Calculate load address */ + load_segment = boot_entry->load_segment; + buffer = real_to_user ( load_segment, ( load_segment ? 0 : 0x7c00 ) ); + + /* Verify and prepare segment */ + if ( ( rc = prep_segment ( buffer, length, length ) ) != 0 ) { + DBGC ( image, "ElTorito %p could not prepare segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + /* Copy image to segment */ + memcpy_user ( buffer, 0, image->data, start, length ); + + return 0; +} + +/** + * Load El Torito image into memory + * + * @v image El Torito file + * @ret rc Return status code + */ +static int eltorito_load ( struct image *image ) { + struct eltorito_boot_entry boot_entry; + unsigned long bootcat_offset; + int rc; + + /* Read Boot Record Volume Descriptor, if present */ + if ( ( rc = eltorito_read_voldesc ( image, &bootcat_offset ) ) != 0 ) + return rc; + + /* This is an El Torito image, valid or otherwise */ + if ( ! image->type ) + image->type = &eltorito_image_type; + + /* Read Boot Catalog */ + if ( ( rc = eltorito_read_catalog ( image, bootcat_offset, + &boot_entry ) ) != 0 ) + return rc; + + /* Load Virtual Disk image */ + if ( ( rc = eltorito_load_disk ( image, &boot_entry ) ) != 0 ) + return rc; + + /* Record load segment in image private data field */ + image->priv.ul = boot_entry.load_segment; + + return 0; +} + +/** El Torito image type */ +struct image_type eltorito_image_type __image_type ( PROBE_NORMAL ) = { + .name = "El Torito", + .load = eltorito_load, + .exec = eltorito_exec, +}; diff --git a/gpxe/src/arch/i386/image/multiboot.c b/gpxe/src/arch/i386/image/multiboot.c new file mode 100644 index 0000000..52bb10f --- /dev/null +++ b/gpxe/src/arch/i386/image/multiboot.c @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2007 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. + */ + +/** + * @file + * + * Multiboot image format + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +FEATURE ( FEATURE_IMAGE, "Multiboot", DHCP_EB_FEATURE_MULTIBOOT, 1 ); + +struct image_type multiboot_image_type __image_type ( PROBE_MULTIBOOT ); + +/** + * Maximum number of modules we will allow for + * + * If this has bitten you: sorry. I did have a perfect scheme with a + * dynamically allocated list of modules on the protected-mode stack, + * but it was incompatible with some broken OSes that can only access + * low memory at boot time (even though we kindly set up 4GB flat + * physical addressing as per the multiboot specification. + * + */ +#define MAX_MODULES 8 + +/** + * Maximum combined length of command lines + * + * Again; sorry. Some broken OSes zero out any non-base memory that + * isn't part of the loaded module set, so we can't just use + * virt_to_phys(cmdline) to point to the command lines, even though + * this would comply with the Multiboot spec. + */ +#define MB_MAX_CMDLINE 512 + +/** Multiboot flags that we support */ +#define MB_SUPPORTED_FLAGS ( MB_FLAG_PGALIGN | MB_FLAG_MEMMAP | \ + MB_FLAG_VIDMODE | MB_FLAG_RAW ) + +/** Compulsory feature multiboot flags */ +#define MB_COMPULSORY_FLAGS 0x0000ffff + +/** Optional feature multiboot flags */ +#define MB_OPTIONAL_FLAGS 0xffff0000 + +/** + * Multiboot flags that we don't support + * + * We only care about the compulsory feature flags (bits 0-15); we are + * allowed to ignore the optional feature flags. + */ +#define MB_UNSUPPORTED_FLAGS ( MB_COMPULSORY_FLAGS & ~MB_SUPPORTED_FLAGS ) + +/** A multiboot header descriptor */ +struct multiboot_header_info { + /** The actual multiboot header */ + struct multiboot_header mb; + /** Offset of header within the multiboot image */ + size_t offset; +}; + +/** Multiboot module command lines */ +static char __bss16_array ( mb_cmdlines, [MB_MAX_CMDLINE] ); +#define mb_cmdlines __use_data16 ( mb_cmdlines ) + +/** Offset within module command lines */ +static unsigned int mb_cmdline_offset; + +/** + * Build multiboot memory map + * + * @v image Multiboot image + * @v mbinfo Multiboot information structure + * @v mbmemmap Multiboot memory map + * @v limit Maxmimum number of memory map entries + */ +static void multiboot_build_memmap ( struct image *image, + struct multiboot_info *mbinfo, + struct multiboot_memory_map *mbmemmap, + unsigned int limit ) { + struct memory_map memmap; + unsigned int i; + + /* Get memory map */ + get_memmap ( &memmap ); + + /* Translate into multiboot format */ + memset ( mbmemmap, 0, sizeof ( *mbmemmap ) ); + for ( i = 0 ; i < memmap.count ; i++ ) { + if ( i >= limit ) { + DBGC ( image, "MULTIBOOT %p limit of %d memmap " + "entries reached\n", image, limit ); + break; + } + mbmemmap[i].size = ( sizeof ( mbmemmap[i] ) - + sizeof ( mbmemmap[i].size ) ); + mbmemmap[i].base_addr = memmap.regions[i].start; + mbmemmap[i].length = ( memmap.regions[i].end - + memmap.regions[i].start ); + mbmemmap[i].type = MBMEM_RAM; + mbinfo->mmap_length += sizeof ( mbmemmap[i] ); + if ( memmap.regions[i].start == 0 ) + mbinfo->mem_lower = ( memmap.regions[i].end / 1024 ); + if ( memmap.regions[i].start == 0x100000 ) + mbinfo->mem_upper = ( ( memmap.regions[i].end - + 0x100000 ) / 1024 ); + } +} + +/** + * Add command line in base memory + * + * @v cmdline Command line + * @ret physaddr Physical address of command line + */ +physaddr_t multiboot_add_cmdline ( const char *cmdline ) { + char *mb_cmdline; + + if ( ! cmdline ) + cmdline = ""; + + /* Copy command line to base memory buffer */ + mb_cmdline = ( mb_cmdlines + mb_cmdline_offset ); + mb_cmdline_offset += + ( snprintf ( mb_cmdline, + ( sizeof ( mb_cmdlines ) - mb_cmdline_offset ), + "%s", cmdline ) + 1 ); + + /* Truncate to terminating NUL in buffer if necessary */ + if ( mb_cmdline_offset > sizeof ( mb_cmdlines ) ) + mb_cmdline_offset = ( sizeof ( mb_cmdlines ) - 1 ); + + return virt_to_phys ( mb_cmdline ); +} + +/** + * Build multiboot module list + * + * @v image Multiboot image + * @v modules Module list to fill, or NULL + * @ret count Number of modules + */ +static unsigned int +multiboot_build_module_list ( struct image *image, + struct multiboot_module *modules, + unsigned int limit ) { + struct image *module_image; + struct multiboot_module *module; + unsigned int count = 0; + unsigned int insert; + physaddr_t start; + physaddr_t end; + unsigned int i; + + /* Add each image as a multiboot module */ + for_each_image ( module_image ) { + + if ( count >= limit ) { + DBGC ( image, "MULTIBOOT %p limit of %d modules " + "reached\n", image, limit ); + break; + } + + /* Do not include kernel image itself as a module */ + if ( module_image == image ) + continue; + + /* At least some OSes expect the multiboot modules to + * be in ascending order, so we have to support it. + */ + start = user_to_phys ( module_image->data, 0 ); + end = user_to_phys ( module_image->data, module_image->len ); + for ( insert = 0 ; insert < count ; insert++ ) { + if ( start < modules[insert].mod_start ) + break; + } + module = &modules[insert]; + memmove ( ( module + 1 ), module, + ( ( count - insert ) * sizeof ( *module ) ) ); + module->mod_start = start; + module->mod_end = end; + module->string = + multiboot_add_cmdline ( module_image->cmdline ); + module->reserved = 0; + + /* We promise to page-align modules */ + assert ( ( module->mod_start & 0xfff ) == 0 ); + + count++; + } + + /* Dump module configuration */ + for ( i = 0 ; i < count ; i++ ) { + DBGC ( image, "MULTIBOOT %p module %d is [%x,%x)\n", + image, i, modules[i].mod_start, + modules[i].mod_end ); + } + + return count; +} + +/** + * The multiboot information structure + * + * Kept in base memory because some OSes won't find it elsewhere, + * along with the other structures belonging to the Multiboot + * information table. + */ +static struct multiboot_info __bss16 ( mbinfo ); +#define mbinfo __use_data16 ( mbinfo ) + +/** The multiboot bootloader name */ +static char __data16_array ( mb_bootloader_name, [] ) = "gPXE " VERSION; +#define mb_bootloader_name __use_data16 ( mb_bootloader_name ) + +/** The multiboot memory map */ +static struct multiboot_memory_map + __bss16_array ( mbmemmap, [MAX_MEMORY_REGIONS] ); +#define mbmemmap __use_data16 ( mbmemmap ) + +/** The multiboot module list */ +static struct multiboot_module __bss16_array ( mbmodules, [MAX_MODULES] ); +#define mbmodules __use_data16 ( mbmodules ) + +/** + * Execute multiboot image + * + * @v image Multiboot image + * @ret rc Return status code + */ +static int multiboot_exec ( struct image *image ) { + physaddr_t entry = image->priv.phys; + + /* Populate multiboot information structure */ + memset ( &mbinfo, 0, sizeof ( mbinfo ) ); + mbinfo.flags = ( MBI_FLAG_LOADER | MBI_FLAG_MEM | MBI_FLAG_MMAP | + MBI_FLAG_CMDLINE | MBI_FLAG_MODS ); + multiboot_build_memmap ( image, &mbinfo, mbmemmap, + ( sizeof(mbmemmap) / sizeof(mbmemmap[0]) ) ); + mb_cmdline_offset = 0; + mbinfo.cmdline = multiboot_add_cmdline ( image->cmdline ); + mbinfo.mods_count = multiboot_build_module_list ( image, mbmodules, + ( sizeof(mbmodules) / sizeof(mbmodules[0]) ) ); + mbinfo.mods_addr = virt_to_phys ( mbmodules ); + mbinfo.mmap_addr = virt_to_phys ( mbmemmap ); + mbinfo.boot_loader_name = virt_to_phys ( mb_bootloader_name ); + + /* Multiboot images may not return and have no callback + * interface, so shut everything down prior to booting the OS. + */ + shutdown ( SHUTDOWN_BOOT ); + + /* Jump to OS with flat physical addressing */ + DBGC ( image, "MULTIBOOT %p starting execution at %lx\n", + image, entry ); + __asm__ __volatile__ ( PHYS_CODE ( "pushl %%ebp\n\t" + "call *%%edi\n\t" + "popl %%ebp\n\t" ) + : : "a" ( MULTIBOOT_BOOTLOADER_MAGIC ), + "b" ( virt_to_phys ( &mbinfo ) ), + "D" ( entry ) + : "ecx", "edx", "esi", "memory" ); + + DBGC ( image, "MULTIBOOT %p returned\n", image ); + + /* It isn't safe to continue after calling shutdown() */ + while ( 1 ) {} + + return -ECANCELED; /* -EIMPOSSIBLE, anyone? */ +} + +/** + * Find multiboot header + * + * @v image Multiboot file + * @v hdr Multiboot header descriptor to fill in + * @ret rc Return status code + */ +static int multiboot_find_header ( struct image *image, + struct multiboot_header_info *hdr ) { + uint32_t buf[64]; + size_t offset; + unsigned int buf_idx; + uint32_t checksum; + + /* Scan through first 8kB of image file 256 bytes at a time. + * (Use the buffering to avoid the overhead of a + * copy_from_user() for every dword.) + */ + for ( offset = 0 ; offset < 8192 ; offset += sizeof ( buf[0] ) ) { + /* Check for end of image */ + if ( offset > image->len ) + break; + /* Refill buffer if applicable */ + buf_idx = ( ( offset % sizeof ( buf ) ) / sizeof ( buf[0] ) ); + if ( buf_idx == 0 ) { + copy_from_user ( buf, image->data, offset, + sizeof ( buf ) ); + } + /* Check signature */ + if ( buf[buf_idx] != MULTIBOOT_HEADER_MAGIC ) + continue; + /* Copy header and verify checksum */ + copy_from_user ( &hdr->mb, image->data, offset, + sizeof ( hdr->mb ) ); + checksum = ( hdr->mb.magic + hdr->mb.flags + + hdr->mb.checksum ); + if ( checksum != 0 ) + continue; + /* Record offset of multiboot header and return */ + hdr->offset = offset; + return 0; + } + + /* No multiboot header found */ + return -ENOEXEC; +} + +/** + * Load raw multiboot image into memory + * + * @v image Multiboot file + * @v hdr Multiboot header descriptor + * @ret rc Return status code + */ +static int multiboot_load_raw ( struct image *image, + struct multiboot_header_info *hdr ) { + size_t offset; + size_t filesz; + size_t memsz; + userptr_t buffer; + int rc; + + /* Verify and prepare segment */ + offset = ( hdr->offset - hdr->mb.header_addr + hdr->mb.load_addr ); + filesz = ( hdr->mb.load_end_addr ? + ( hdr->mb.load_end_addr - hdr->mb.load_addr ) : + ( image->len - offset ) ); + memsz = ( hdr->mb.bss_end_addr ? + ( hdr->mb.bss_end_addr - hdr->mb.load_addr ) : filesz ); + buffer = phys_to_user ( hdr->mb.load_addr ); + if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) { + DBGC ( image, "MULTIBOOT %p could not prepare segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + /* Copy image to segment */ + memcpy_user ( buffer, 0, image->data, offset, filesz ); + + /* Record execution entry point in image private data field */ + image->priv.phys = hdr->mb.entry_addr; + + return 0; +} + +/** + * Load ELF multiboot image into memory + * + * @v image Multiboot file + * @ret rc Return status code + */ +static int multiboot_load_elf ( struct image *image ) { + int rc; + + /* Load ELF image*/ + if ( ( rc = elf_load ( image ) ) != 0 ) { + DBGC ( image, "MULTIBOOT %p ELF image failed to load: %s\n", + image, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Load multiboot image into memory + * + * @v image Multiboot file + * @ret rc Return status code + */ +static int multiboot_load ( struct image *image ) { + struct multiboot_header_info hdr; + int rc; + + /* Locate multiboot header, if present */ + if ( ( rc = multiboot_find_header ( image, &hdr ) ) != 0 ) { + DBGC ( image, "MULTIBOOT %p has no multiboot header\n", + image ); + return rc; + } + DBGC ( image, "MULTIBOOT %p found header with flags %08x\n", + image, hdr.mb.flags ); + + /* This is a multiboot image, valid or otherwise */ + if ( ! image->type ) + image->type = &multiboot_image_type; + + /* Abort if we detect flags that we cannot support */ + if ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) { + DBGC ( image, "MULTIBOOT %p flags %08x not supported\n", + image, ( hdr.mb.flags & MB_UNSUPPORTED_FLAGS ) ); + return -ENOTSUP; + } + + /* Load the actual image */ + if ( hdr.mb.flags & MB_FLAG_RAW ) { + if ( ( rc = multiboot_load_raw ( image, &hdr ) ) != 0 ) + return rc; + } else { + if ( ( rc = multiboot_load_elf ( image ) ) != 0 ) + return rc; + } + + return 0; +} + +/** Multiboot image type */ +struct image_type multiboot_image_type __image_type ( PROBE_MULTIBOOT ) = { + .name = "Multiboot", + .load = multiboot_load, + .exec = multiboot_exec, +}; diff --git a/gpxe/src/arch/i386/image/nbi.c b/gpxe/src/arch/i386/image/nbi.c new file mode 100644 index 0000000..a4ee442 --- /dev/null +++ b/gpxe/src/arch/i386/image/nbi.c @@ -0,0 +1,442 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * NBI image format. + * + * The Net Boot Image format is defined by the "Draft Net Boot Image + * Proposal 0.3" by Jamie Honan, Gero Kuhlmann and Ken Yap. It is now + * considered to be a legacy format, but it still included because a + * large amount of software (e.g. nymph, LTSP) makes use of NBI files. + * + * Etherboot does not implement the INT 78 callback interface + * described by the NBI specification. For a callback interface on + * x86 architecture, use PXE. + * + */ + +FEATURE ( FEATURE_IMAGE, "NBI", DHCP_EB_FEATURE_NBI, 1 ); + +struct image_type nbi_image_type __image_type ( PROBE_NORMAL ); + +/** + * An NBI image header + * + * Note that the length field uses a peculiar encoding; use the + * NBI_LENGTH() macro to decode the actual header length. + * + */ +struct imgheader { + unsigned long magic; /**< Magic number (NBI_MAGIC) */ + union { + unsigned char length; /**< Nibble-coded header length */ + unsigned long flags; /**< Image flags */ + }; + segoff_t location; /**< 16-bit seg:off header location */ + union { + segoff_t segoff; /**< 16-bit seg:off entry point */ + unsigned long linear; /**< 32-bit entry point */ + } execaddr; +} __attribute__ (( packed )); + +/** NBI magic number */ +#define NBI_MAGIC 0x1B031336UL + +/* Interpretation of the "length" fields */ +#define NBI_NONVENDOR_LENGTH(len) ( ( (len) & 0x0f ) << 2 ) +#define NBI_VENDOR_LENGTH(len) ( ( (len) & 0xf0 ) >> 2 ) +#define NBI_LENGTH(len) ( NBI_NONVENDOR_LENGTH(len) + NBI_VENDOR_LENGTH(len) ) + +/* Interpretation of the "flags" fields */ +#define NBI_PROGRAM_RETURNS(flags) ( (flags) & ( 1 << 8 ) ) +#define NBI_LINEAR_EXEC_ADDR(flags) ( (flags) & ( 1 << 31 ) ) + +/** NBI header length */ +#define NBI_HEADER_LENGTH 512 + +/** + * An NBI segment header + * + * Note that the length field uses a peculiar encoding; use the + * NBI_LENGTH() macro to decode the actual header length. + * + */ +struct segheader { + unsigned char length; /**< Nibble-coded header length */ + unsigned char vendortag; /**< Vendor-defined private tag */ + unsigned char reserved; + unsigned char flags; /**< Segment flags */ + unsigned long loadaddr; /**< Load address */ + unsigned long imglength; /**< Segment length in NBI file */ + unsigned long memlength; /**< Segment length in memory */ +}; + +/* Interpretation of the "flags" fields */ +#define NBI_LOADADDR_FLAGS(flags) ( (flags) & 0x03 ) +#define NBI_LOADADDR_ABS 0x00 +#define NBI_LOADADDR_AFTER 0x01 +#define NBI_LOADADDR_END 0x02 +#define NBI_LOADADDR_BEFORE 0x03 +#define NBI_LAST_SEGHEADER(flags) ( (flags) & ( 1 << 2 ) ) + +/* Define a type for passing info to a loaded program */ +struct ebinfo { + uint8_t major, minor; /* Version */ + uint16_t flags; /* Bit flags */ +}; + +/** Info passed to NBI image */ +static struct ebinfo loaderinfo = { + VERSION_MAJOR, VERSION_MINOR, + 0 +}; + +/** + * Prepare a segment for an NBI image + * + * @v image NBI image + * @v offset Offset within NBI image + * @v filesz Length of initialised-data portion of the segment + * @v memsz Total length of the segment + * @v src Source for initialised data + * @ret rc Return status code + */ +static int nbi_prepare_segment ( struct image *image, size_t offset __unused, + userptr_t dest, size_t filesz, size_t memsz ){ + int rc; + + if ( ( rc = prep_segment ( dest, filesz, memsz ) ) != 0 ) { + DBGC ( image, "NBI %p could not prepare segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Load a segment for an NBI image + * + * @v image NBI image + * @v offset Offset within NBI image + * @v filesz Length of initialised-data portion of the segment + * @v memsz Total length of the segment + * @v src Source for initialised data + * @ret rc Return status code + */ +static int nbi_load_segment ( struct image *image, size_t offset, + userptr_t dest, size_t filesz, + size_t memsz __unused ) { + memcpy_user ( dest, 0, image->data, offset, filesz ); + return 0; +} + +/** + * Process segments of an NBI image + * + * @v image NBI image + * @v imgheader Image header information + * @v process Function to call for each segment + * @ret rc Return status code + */ +static int nbi_process_segments ( struct image *image, + struct imgheader *imgheader, + int ( * process ) ( struct image *image, + size_t offset, + userptr_t dest, + size_t filesz, + size_t memsz ) ) { + struct segheader sh; + size_t offset = 0; + size_t sh_off; + userptr_t dest; + size_t filesz; + size_t memsz; + int rc; + + /* Copy image header to target location */ + dest = real_to_user ( imgheader->location.segment, + imgheader->location.offset ); + filesz = memsz = NBI_HEADER_LENGTH; + if ( ( rc = process ( image, offset, dest, filesz, memsz ) ) != 0 ) + return rc; + offset += filesz; + + /* Process segments in turn */ + sh_off = NBI_LENGTH ( imgheader->length ); + do { + /* Read segment header */ + copy_from_user ( &sh, image->data, sh_off, sizeof ( sh ) ); + if ( sh.length == 0 ) { + /* Avoid infinite loop? */ + DBGC ( image, "NBI %p invalid segheader length 0\n", + image ); + return -ENOEXEC; + } + + /* Calculate segment load address */ + switch ( NBI_LOADADDR_FLAGS ( sh.flags ) ) { + case NBI_LOADADDR_ABS: + dest = phys_to_user ( sh.loadaddr ); + break; + case NBI_LOADADDR_AFTER: + dest = userptr_add ( dest, memsz + sh.loadaddr ); + break; + case NBI_LOADADDR_BEFORE: + dest = userptr_add ( dest, -sh.loadaddr ); + break; + case NBI_LOADADDR_END: + /* Not correct according to the spec, but + * maintains backwards compatibility with + * previous versions of Etherboot. + */ + dest = phys_to_user ( ( extmemsize() + 1024 ) * 1024 + - sh.loadaddr ); + break; + default: + /* Cannot be reached */ + assert ( 0 ); + } + + /* Process this segment */ + filesz = sh.imglength; + memsz = sh.memlength; + if ( ( offset + filesz ) > image->len ) { + DBGC ( image, "NBI %p segment outside file\n", image ); + return -ENOEXEC; + } + if ( ( rc = process ( image, offset, dest, + filesz, memsz ) ) != 0 ) { + return rc; + } + offset += filesz; + + /* Next segheader */ + sh_off += NBI_LENGTH ( sh.length ); + if ( sh_off >= NBI_HEADER_LENGTH ) { + DBGC ( image, "NBI %p header overflow\n", image ); + return -ENOEXEC; + } + + } while ( ! NBI_LAST_SEGHEADER ( sh.flags ) ); + + if ( offset != image->len ) { + DBGC ( image, "NBI %p length wrong (file %zd, metadata %zd)\n", + image, image->len, offset ); + return -ENOEXEC; + } + + return 0; +} + +/** + * Load an NBI image into memory + * + * @v image NBI image + * @ret rc Return status code + */ +static int nbi_load ( struct image *image ) { + struct imgheader imgheader; + int rc; + + /* If we don't have enough data give up */ + if ( image->len < NBI_HEADER_LENGTH ) { + DBGC ( image, "NBI %p too short for an NBI image\n", image ); + return -ENOEXEC; + } + + /* Check image header */ + copy_from_user ( &imgheader, image->data, 0, sizeof ( imgheader ) ); + if ( imgheader.magic != NBI_MAGIC ) { + DBGC ( image, "NBI %p has no NBI signature\n", image ); + return -ENOEXEC; + } + + /* This is an NBI image, valid or otherwise */ + if ( ! image->type ) + image->type = &nbi_image_type; + + DBGC ( image, "NBI %p placing header at %hx:%hx\n", image, + imgheader.location.segment, imgheader.location.offset ); + + /* NBI files can have overlaps between segments; the bss of + * one segment may overlap the initialised data of another. I + * assume this is a design flaw, but there are images out + * there that we need to work with. We therefore do two + * passes: first to initialise the segments, then to copy the + * data. This avoids zeroing out already-copied data. + */ + if ( ( rc = nbi_process_segments ( image, &imgheader, + nbi_prepare_segment ) ) != 0 ) + return rc; + if ( ( rc = nbi_process_segments ( image, &imgheader, + nbi_load_segment ) ) != 0 ) + return rc; + + /* Record header address in image private data field */ + image->priv.user = real_to_user ( imgheader.location.segment, + imgheader.location.offset ); + + return 0; +} + +/** + * Boot a 16-bit NBI image + * + * @v imgheader Image header information + * @ret rc Return status code, if image returns + */ +static int nbi_boot16 ( struct image *image, struct imgheader *imgheader ) { + int discard_D, discard_S, discard_b; + int rc; + + DBGC ( image, "NBI %p executing 16-bit image at %04x:%04x\n", image, + imgheader->execaddr.segoff.segment, + imgheader->execaddr.segoff.offset ); + + gateA20_unset(); + + __asm__ __volatile__ ( + REAL_CODE ( "pushw %%ds\n\t" /* far pointer to bootp data */ + "pushw %%bx\n\t" + "pushl %%esi\n\t" /* location */ + "pushw %%cs\n\t" /* lcall execaddr */ + "call 1f\n\t" + "jmp 2f\n\t" + "\n1:\n\t" + "pushl %%edi\n\t" + "lret\n\t" + "\n2:\n\t" + "addw $8,%%sp\n\t" /* clean up stack */ ) + : "=a" ( rc ), "=D" ( discard_D ), "=S" ( discard_S ), + "=b" ( discard_b ) + : "D" ( imgheader->execaddr.segoff ), + "S" ( imgheader->location ), + "b" ( __from_data16 ( basemem_packet ) ) + : "ecx", "edx", "ebp" ); + + gateA20_set(); + + return rc; +} + +/** + * Boot a 32-bit NBI image + * + * @v imgheader Image header information + * @ret rc Return status code, if image returns + */ +static int nbi_boot32 ( struct image *image, struct imgheader *imgheader ) { + int discard_D, discard_S, discard_b; + int rc; + + DBGC ( image, "NBI %p executing 32-bit image at %lx\n", + image, imgheader->execaddr.linear ); + + /* no gateA20_unset for PM call */ + + /* Jump to OS with flat physical addressing */ + __asm__ __volatile__ ( + PHYS_CODE ( "pushl %%ebx\n\t" /* bootp data */ + "pushl %%esi\n\t" /* imgheader */ + "pushl %%eax\n\t" /* loaderinfo */ + "call *%%edi\n\t" + "addl $12, %%esp\n\t" /* clean up stack */ ) + : "=a" ( rc ), "=D" ( discard_D ), "=S" ( discard_S ), + "=b" ( discard_b ) + : "D" ( imgheader->execaddr.linear ), + "S" ( ( imgheader->location.segment << 4 ) + + imgheader->location.offset ), + "b" ( virt_to_phys ( basemem_packet ) ), + "a" ( virt_to_phys ( &loaderinfo ) ) + : "ecx", "edx", "ebp", "memory" ); + + return rc; +} + +/** + * Prepare DHCP parameter block for NBI image + * + * @v image NBI image + * @ret rc Return status code + */ +static int nbi_prepare_dhcp ( struct image *image ) { + struct net_device *boot_netdev; + int rc; + + boot_netdev = last_opened_netdev(); + if ( ! boot_netdev ) { + DBGC ( image, "NBI %p could not identify a network device\n", + image ); + return -ENODEV; + } + + if ( ( rc = create_fakedhcpack ( boot_netdev, basemem_packet, + sizeof ( basemem_packet ) ) ) != 0 ) { + DBGC ( image, "NBI %p failed to build DHCP packet\n", image ); + return rc; + } + + return 0; +} + +/** + * Execute a loaded NBI image + * + * @v image NBI image + * @ret rc Return status code + */ +static int nbi_exec ( struct image *image ) { + struct imgheader imgheader; + int may_return; + int rc; + + copy_from_user ( &imgheader, image->priv.user, 0, + sizeof ( imgheader ) ); + + /* Prepare DHCP option block */ + if ( ( rc = nbi_prepare_dhcp ( image ) ) != 0 ) + return rc; + + /* Shut down now if NBI image will not return */ + may_return = NBI_PROGRAM_RETURNS ( imgheader.flags ); + if ( ! may_return ) + shutdown ( SHUTDOWN_BOOT ); + + /* Execute NBI image */ + if ( NBI_LINEAR_EXEC_ADDR ( imgheader.flags ) ) { + rc = nbi_boot32 ( image, &imgheader ); + } else { + rc = nbi_boot16 ( image, &imgheader ); + } + + if ( ! may_return ) { + /* Cannot continue after shutdown() called */ + DBGC ( image, "NBI %p returned %d from non-returnable image\n", + image, rc ); + while ( 1 ) {} + } + + DBGC ( image, "NBI %p returned %d\n", image, rc ); + + return rc; +} + +/** NBI image type */ +struct image_type nbi_image_type __image_type ( PROBE_NORMAL ) = { + .name = "NBI", + .load = nbi_load, + .exec = nbi_exec, +}; diff --git a/gpxe/src/arch/i386/image/pxe_image.c b/gpxe/src/arch/i386/image/pxe_image.c new file mode 100644 index 0000000..90550d8 --- /dev/null +++ b/gpxe/src/arch/i386/image/pxe_image.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2007 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. + */ + +/** + * @file + * + * PXE image format + * + */ + +#include +#include +#include +#include +#include +#include +#include + +FEATURE ( FEATURE_IMAGE, "PXE", DHCP_EB_FEATURE_PXE, 1 ); + +struct image_type pxe_image_type __image_type ( PROBE_PXE ); + +/** + * Execute PXE image + * + * @v image PXE image + * @ret rc Return status code + */ +static int pxe_exec ( struct image *image ) { + int rc; + + /* Ensure that PXE stack is ready to use */ + pxe_init_structures(); + pxe_hook_int1a(); + + /* Arbitrarily pick the most recently opened network device */ + pxe_set_netdev ( last_opened_netdev() ); + + /* Many things will break if pxe_netdev is NULL */ + if ( ! pxe_netdev ) { + DBGC ( image, "IMAGE %p could not locate PXE net device\n", + image ); + return -ENODEV; + } + + /* Start PXE NBP */ + rc = pxe_start_nbp(); + + /* Deactivate PXE */ + pxe_set_netdev ( NULL ); + pxe_unhook_int1a(); + + return rc; +} + +/** + * Load PXE image into memory + * + * @v image PXE file + * @ret rc Return status code + */ +int pxe_load ( struct image *image ) { + userptr_t buffer = real_to_user ( 0, 0x7c00 ); + size_t filesz = image->len; + size_t memsz = image->len; + int rc; + + /* Images too large to fit in base memory cannot be PXE + * images. We include this check to help prevent unrecognised + * images from being marked as PXE images, since PXE images + * have no signature we can check against. + */ + if ( filesz > ( 0xa0000 - 0x7c00 ) ) + return -ENOEXEC; + + /* Rejecting zero-length images is also useful, since these + * end up looking to the user like bugs in gPXE. + */ + if ( ! filesz ) + return -ENOEXEC; + + /* There are no signature checks for PXE; we will accept anything */ + if ( ! image->type ) + image->type = &pxe_image_type; + + /* Verify and prepare segment */ + if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) { + DBGC ( image, "IMAGE %p could not prepare segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + /* Copy image to segment */ + memcpy_user ( buffer, 0, image->data, 0, filesz ); + + return 0; +} + +/** PXE image type */ +struct image_type pxe_image_type __image_type ( PROBE_PXE ) = { + .name = "PXE", + .load = pxe_load, + .exec = pxe_exec, +}; diff --git a/gpxe/src/arch/i386/include/basemem.h b/gpxe/src/arch/i386/include/basemem.h new file mode 100644 index 0000000..cd5668e --- /dev/null +++ b/gpxe/src/arch/i386/include/basemem.h @@ -0,0 +1,33 @@ +#ifndef _BASEMEM_H +#define _BASEMEM_H + +/** @file + * + * Base memory allocation + * + */ + +#include +#include +#include + +/** + * Read the BIOS free base memory counter + * + * @ret fbms Free base memory counter (in kB) + */ +static inline unsigned int get_fbms ( void ) { + uint16_t fbms; + + get_real ( fbms, BDA_SEG, BDA_FBMS ); + return fbms; +} + +extern void set_fbms ( unsigned int new_fbms ); + +/* Actually in hidemem.c, but putting it here avoids polluting the + * architecture-independent include/hidemem.h. + */ +extern void hide_basemem ( void ); + +#endif /* _BASEMEM_H */ diff --git a/gpxe/src/arch/i386/include/basemem_packet.h b/gpxe/src/arch/i386/include/basemem_packet.h new file mode 100644 index 0000000..e4d4f49 --- /dev/null +++ b/gpxe/src/arch/i386/include/basemem_packet.h @@ -0,0 +1,13 @@ +#ifndef BASEMEM_PACKET_H +#define BASEMEM_PACKET_H + +#include + +/** Maximum length of base memory packet buffer */ +#define BASEMEM_PACKET_LEN 1514 + +/** Base memory packet buffer */ +extern char __bss16_array ( basemem_packet, [BASEMEM_PACKET_LEN] ); +#define basemem_packet __use_data16 ( basemem_packet ) + +#endif /* BASEMEM_PACKET_H */ diff --git a/gpxe/src/arch/i386/include/bios.h b/gpxe/src/arch/i386/include/bios.h new file mode 100644 index 0000000..979a092 --- /dev/null +++ b/gpxe/src/arch/i386/include/bios.h @@ -0,0 +1,8 @@ +#ifndef BIOS_H +#define BIOS_H + +#define BDA_SEG 0x0040 +#define BDA_FBMS 0x0013 +#define BDA_NUM_DRIVES 0x0075 + +#endif /* BIOS_H */ diff --git a/gpxe/src/arch/i386/include/bios_disks.h b/gpxe/src/arch/i386/include/bios_disks.h new file mode 100644 index 0000000..0dd7c4e --- /dev/null +++ b/gpxe/src/arch/i386/include/bios_disks.h @@ -0,0 +1,69 @@ +#ifndef BIOS_DISKS_H +#define BIOS_DISKS_H + +#include "dev.h" + +/* + * Constants + * + */ + +#define BIOS_DISK_MAX_NAME_LEN 6 + +struct bios_disk_sector { + char data[512]; +}; + +/* + * The location of a BIOS disk + * + */ +struct bios_disk_loc { + uint8_t drive; +}; + +/* + * A physical BIOS disk device + * + */ +struct bios_disk_device { + char name[BIOS_DISK_MAX_NAME_LEN]; + uint8_t drive; + uint8_t type; +}; + +/* + * A BIOS disk driver, with a valid device ID range and naming + * function. + * + */ +struct bios_disk_driver { + void ( *fill_drive_name ) ( char *buf, uint8_t drive ); + uint8_t min_drive; + uint8_t max_drive; +}; + +/* + * Define a BIOS disk driver + * + */ +#define BIOS_DISK_DRIVER( _name, _fill_drive_name, _min_drive, _max_drive ) \ + static struct bios_disk_driver _name = { \ + .fill_drive_name = _fill_drive_name, \ + .min_drive = _min_drive, \ + .max_drive = _max_drive, \ + } + +/* + * Functions in bios_disks.c + * + */ + + +/* + * bios_disk bus global definition + * + */ +extern struct bus_driver bios_disk_driver; + +#endif /* BIOS_DISKS_H */ diff --git a/gpxe/src/arch/i386/include/biosint.h b/gpxe/src/arch/i386/include/biosint.h new file mode 100644 index 0000000..d365cf0 --- /dev/null +++ b/gpxe/src/arch/i386/include/biosint.h @@ -0,0 +1,31 @@ +#ifndef BIOSINT_H +#define BIOSINT_H + +/** + * @file BIOS interrupts + * + */ + +#include + +struct segoff; + +/** + * Hooked interrupt count + * + * At exit, after unhooking all possible interrupts, this counter + * should be examined. If it is non-zero, it means that we failed to + * unhook at least one interrupt vector, and so must not free up the + * memory we are using. (Note that this also implies that we should + * re-hook INT 15 in order to hide ourselves from the memory map). + */ +extern uint16_t __text16 ( hooked_bios_interrupts ); +#define hooked_bios_interrupts __use_text16 ( hooked_bios_interrupts ) + +extern void hook_bios_interrupt ( unsigned int interrupt, unsigned int handler, + struct segoff *chain_vector ); +extern int unhook_bios_interrupt ( unsigned int interrupt, + unsigned int handler, + struct segoff *chain_vector ); + +#endif /* BIOSINT_H */ diff --git a/gpxe/src/arch/i386/include/bits/byteswap.h b/gpxe/src/arch/i386/include/bits/byteswap.h new file mode 100644 index 0000000..98418c2 --- /dev/null +++ b/gpxe/src/arch/i386/include/bits/byteswap.h @@ -0,0 +1,41 @@ +#ifndef ETHERBOOT_BITS_BYTESWAP_H +#define ETHERBOOT_BITS_BYTESWAP_H + +static inline __attribute__ ((always_inline, const)) uint16_t +__bswap_variable_16(uint16_t x) +{ + __asm__("xchgb %b0,%h0\n\t" + : "=q" (x) + : "0" (x)); + return x; +} + +static inline __attribute__ ((always_inline, const)) uint32_t +__bswap_variable_32(uint32_t x) +{ + __asm__("xchgb %b0,%h0\n\t" + "rorl $16,%0\n\t" + "xchgb %b0,%h0" + : "=q" (x) + : "0" (x)); + return x; +} + +static inline __attribute__ ((always_inline, const)) uint64_t +__bswap_variable_64(uint64_t x) +{ + union { + uint64_t qword; + uint32_t dword[2]; + } u; + + u.qword = x; + u.dword[0] = __bswap_variable_32(u.dword[0]); + u.dword[1] = __bswap_variable_32(u.dword[1]); + __asm__("xchgl %0,%1" + : "=r" ( u.dword[0] ), "=r" ( u.dword[1] ) + : "0" ( u.dword[0] ), "1" ( u.dword[1] ) ); + return u.qword; +} + +#endif /* ETHERBOOT_BITS_BYTESWAP_H */ diff --git a/gpxe/src/arch/i386/include/bits/compiler.h b/gpxe/src/arch/i386/include/bits/compiler.h new file mode 100644 index 0000000..119a9a2 --- /dev/null +++ b/gpxe/src/arch/i386/include/bits/compiler.h @@ -0,0 +1,25 @@ +#ifndef _BITS_COMPILER_H +#define _BITS_COMPILER_H + +#ifndef ASSEMBLY + +/** Declare a function with standard calling conventions */ +#define __asmcall __attribute__ (( cdecl, regparm(0) )) + +/** + * Declare a function with libgcc implicit linkage + * + * It seems as though gcc expects its implicit arithmetic functions to + * be cdecl, even if -mrtd is specified. This is somewhat + * inconsistent; for example, if -mregparm=3 is used then the implicit + * functions do become regparm(3). + * + * The implicit calls to memcpy() and memset() which gcc can generate + * do not seem to have this inconsistency; -mregparm and -mrtd affect + * them in the same way as any other function. + */ +#define __libgcc __attribute__ (( cdecl )) + +#endif /* ASSEMBLY */ + +#endif /* _BITS_COMPILER_H */ diff --git a/gpxe/src/arch/i386/include/bits/cpu.h b/gpxe/src/arch/i386/include/bits/cpu.h new file mode 100644 index 0000000..83339dd --- /dev/null +++ b/gpxe/src/arch/i386/include/bits/cpu.h @@ -0,0 +1,86 @@ +#ifndef I386_BITS_CPU_H +#define I386_BITS_CPU_H + +/* Intel-defined CPU features, CPUID level 0x00000001, word 0 */ +#define X86_FEATURE_FPU 0 /* Onboard FPU */ +#define X86_FEATURE_VME 1 /* Virtual Mode Extensions */ +#define X86_FEATURE_DE 2 /* Debugging Extensions */ +#define X86_FEATURE_PSE 3 /* Page Size Extensions */ +#define X86_FEATURE_TSC 4 /* Time Stamp Counter */ +#define X86_FEATURE_MSR 5 /* Model-Specific Registers, RDMSR, WRMSR */ +#define X86_FEATURE_PAE 6 /* Physical Address Extensions */ +#define X86_FEATURE_MCE 7 /* Machine Check Architecture */ +#define X86_FEATURE_CX8 8 /* CMPXCHG8 instruction */ +#define X86_FEATURE_APIC 9 /* Onboard APIC */ +#define X86_FEATURE_SEP 11 /* SYSENTER/SYSEXIT */ +#define X86_FEATURE_MTRR 12 /* Memory Type Range Registers */ +#define X86_FEATURE_PGE 13 /* Page Global Enable */ +#define X86_FEATURE_MCA 14 /* Machine Check Architecture */ +#define X86_FEATURE_CMOV 15 /* CMOV instruction (FCMOVCC and FCOMI too if FPU present) */ +#define X86_FEATURE_PAT 16 /* Page Attribute Table */ +#define X86_FEATURE_PSE36 17 /* 36-bit PSEs */ +#define X86_FEATURE_PN 18 /* Processor serial number */ +#define X86_FEATURE_CLFLSH 19 /* Supports the CLFLUSH instruction */ +#define X86_FEATURE_DTES 21 /* Debug Trace Store */ +#define X86_FEATURE_ACPI 22 /* ACPI via MSR */ +#define X86_FEATURE_MMX 23 /* Multimedia Extensions */ +#define X86_FEATURE_FXSR 24 /* FXSAVE and FXRSTOR instructions (fast save and restore */ + /* of FPU context), and CR4.OSFXSR available */ +#define X86_FEATURE_XMM 25 /* Streaming SIMD Extensions */ +#define X86_FEATURE_XMM2 26 /* Streaming SIMD Extensions-2 */ +#define X86_FEATURE_SELFSNOOP 27 /* CPU self snoop */ +#define X86_FEATURE_HT 28 /* Hyper-Threading */ +#define X86_FEATURE_ACC 29 /* Automatic clock control */ +#define X86_FEATURE_IA64 30 /* IA-64 processor */ + +/* AMD-defined CPU features, CPUID level 0x80000001, word 1 */ +/* Don't duplicate feature flags which are redundant with Intel! */ +#define X86_FEATURE_SYSCALL 11 /* SYSCALL/SYSRET */ +#define X86_FEATURE_MMXEXT 22 /* AMD MMX extensions */ +#define X86_FEATURE_LM 29 /* Long Mode (x86-64) */ +#define X86_FEATURE_3DNOWEXT 30 /* AMD 3DNow! extensions */ +#define X86_FEATURE_3DNOW 31 /* 3DNow! */ + +/** x86 CPU information */ +struct cpuinfo_x86 { + /** CPU features */ + unsigned int features; + /** 64-bit CPU features */ + unsigned int amd_features; +}; + +/* + * EFLAGS bits + */ +#define X86_EFLAGS_CF 0x00000001 /* Carry Flag */ +#define X86_EFLAGS_PF 0x00000004 /* Parity Flag */ +#define X86_EFLAGS_AF 0x00000010 /* Auxillary carry Flag */ +#define X86_EFLAGS_ZF 0x00000040 /* Zero Flag */ +#define X86_EFLAGS_SF 0x00000080 /* Sign Flag */ +#define X86_EFLAGS_TF 0x00000100 /* Trap Flag */ +#define X86_EFLAGS_IF 0x00000200 /* Interrupt Flag */ +#define X86_EFLAGS_DF 0x00000400 /* Direction Flag */ +#define X86_EFLAGS_OF 0x00000800 /* Overflow Flag */ +#define X86_EFLAGS_IOPL 0x00003000 /* IOPL mask */ +#define X86_EFLAGS_NT 0x00004000 /* Nested Task */ +#define X86_EFLAGS_RF 0x00010000 /* Resume Flag */ +#define X86_EFLAGS_VM 0x00020000 /* Virtual Mode */ +#define X86_EFLAGS_AC 0x00040000 /* Alignment Check */ +#define X86_EFLAGS_VIF 0x00080000 /* Virtual Interrupt Flag */ +#define X86_EFLAGS_VIP 0x00100000 /* Virtual Interrupt Pending */ +#define X86_EFLAGS_ID 0x00200000 /* CPUID detection flag */ + +/* + * Generic CPUID function + */ +static inline __attribute__ (( always_inline )) void +cpuid ( int op, unsigned int *eax, unsigned int *ebx, + unsigned int *ecx, unsigned int *edx ) { + __asm__ ( "cpuid" : + "=a" ( *eax ), "=b" ( *ebx ), "=c" ( *ecx ), "=d" ( *edx ) + : "0" ( op ) ); +} + +extern void get_cpuinfo ( struct cpuinfo_x86 *cpu ); + +#endif /* I386_BITS_CPU_H */ diff --git a/gpxe/src/arch/i386/include/bits/eltorito.h b/gpxe/src/arch/i386/include/bits/eltorito.h new file mode 100644 index 0000000..d43e9aa --- /dev/null +++ b/gpxe/src/arch/i386/include/bits/eltorito.h @@ -0,0 +1,3 @@ +#ifndef ELTORITO_PLATFORM +#define ELTORITO_PLATFORM ELTORITO_PLATFORM_X86 +#endif /* ELTORITO_PLATFORM */ diff --git a/gpxe/src/arch/i386/include/bits/endian.h b/gpxe/src/arch/i386/include/bits/endian.h new file mode 100644 index 0000000..413e702 --- /dev/null +++ b/gpxe/src/arch/i386/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/gpxe/src/arch/i386/include/bits/errfile.h b/gpxe/src/arch/i386/include/bits/errfile.h new file mode 100644 index 0000000..5ea8a31 --- /dev/null +++ b/gpxe/src/arch/i386/include/bits/errfile.h @@ -0,0 +1,39 @@ +#ifndef _BITS_ERRFILE_H +#define _BITS_ERRFILE_H + +/** + * @addtogroup errfile Error file identifiers + * @{ + */ + +#define ERRFILE_memtop_umalloc ( ERRFILE_ARCH | ERRFILE_CORE | 0x00000000 ) +#define ERRFILE_memmap ( ERRFILE_ARCH | ERRFILE_CORE | 0x00010000 ) +#define ERRFILE_pnpbios ( ERRFILE_ARCH | ERRFILE_CORE | 0x00020000 ) +#define ERRFILE_bios_smbios ( ERRFILE_ARCH | ERRFILE_CORE | 0x00030000 ) +#define ERRFILE_biosint ( ERRFILE_ARCH | ERRFILE_CORE | 0x00040000 ) +#define ERRFILE_int13 ( ERRFILE_ARCH | ERRFILE_CORE | 0x00050000 ) + +#define ERRFILE_bootsector ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 ) +#define ERRFILE_bzimage ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 ) +#define ERRFILE_eltorito ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00020000 ) +#define ERRFILE_multiboot ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00030000 ) +#define ERRFILE_nbi ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00040000 ) +#define ERRFILE_pxe_image ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00050000 ) +#define ERRFILE_elfboot ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00060000 ) +#define ERRFILE_comboot ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00070000 ) +#define ERRFILE_com32 ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00080000 ) +#define ERRFILE_comboot_resolv ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00090000 ) +#define ERRFILE_comboot_call ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x000a0000 ) + +#define ERRFILE_undi ( ERRFILE_ARCH | ERRFILE_NET | 0x00000000 ) +#define ERRFILE_undiload ( ERRFILE_ARCH | ERRFILE_NET | 0x00010000 ) +#define ERRFILE_undinet ( ERRFILE_ARCH | ERRFILE_NET | 0x00020000 ) +#define ERRFILE_undionly ( ERRFILE_ARCH | ERRFILE_NET | 0x00030000 ) +#define ERRFILE_undirom ( ERRFILE_ARCH | ERRFILE_NET | 0x00040000 ) + +#define ERRFILE_timer_rdtsc ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00000000 ) +#define ERRFILE_timer_bios ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00010000 ) + +/** @} */ + +#endif /* _BITS_ERRFILE_H */ diff --git a/gpxe/src/arch/i386/include/bits/io.h b/gpxe/src/arch/i386/include/bits/io.h new file mode 100644 index 0000000..dd0ee44 --- /dev/null +++ b/gpxe/src/arch/i386/include/bits/io.h @@ -0,0 +1,12 @@ +#ifndef _BITS_IO_H +#define _BITS_IO_H + +/** @file + * + * i386-specific I/O API implementations + * + */ + +#include + +#endif /* _BITS_IO_H */ diff --git a/gpxe/src/arch/i386/include/bits/nap.h b/gpxe/src/arch/i386/include/bits/nap.h new file mode 100644 index 0000000..f8ba7a7 --- /dev/null +++ b/gpxe/src/arch/i386/include/bits/nap.h @@ -0,0 +1,13 @@ +#ifndef _BITS_NAP_H +#define _BITS_NAP_H + +/** @file + * + * i386-specific CPU sleeping API implementations + * + */ + +#include +#include + +#endif /* _BITS_MAP_H */ diff --git a/gpxe/src/arch/i386/include/bits/smbios.h b/gpxe/src/arch/i386/include/bits/smbios.h new file mode 100644 index 0000000..647ea19 --- /dev/null +++ b/gpxe/src/arch/i386/include/bits/smbios.h @@ -0,0 +1,12 @@ +#ifndef _BITS_SMBIOS_H +#define _BITS_SMBIOS_H + +/** @file + * + * i386-specific SMBIOS API implementations + * + */ + +#include + +#endif /* _BITS_SMBIOS_H */ diff --git a/gpxe/src/arch/i386/include/bits/stdint.h b/gpxe/src/arch/i386/include/bits/stdint.h new file mode 100644 index 0000000..6ccf097 --- /dev/null +++ b/gpxe/src/arch/i386/include/bits/stdint.h @@ -0,0 +1,21 @@ +#ifndef _BITS_STDINT_H +#define _BITS_STDINT_H + +typedef unsigned int size_t; +typedef signed int ssize_t; +typedef signed long off_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int 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; + +typedef unsigned long physaddr_t; +typedef unsigned long intptr_t; + +#endif /* _BITS_STDINT_H */ diff --git a/gpxe/src/arch/i386/include/bits/timer.h b/gpxe/src/arch/i386/include/bits/timer.h new file mode 100644 index 0000000..99666d8 --- /dev/null +++ b/gpxe/src/arch/i386/include/bits/timer.h @@ -0,0 +1,13 @@ +#ifndef _BITS_TIMER_H +#define _BITS_TIMER_H + +/** @file + * + * i386-specific timer API implementations + * + */ + +#include +#include + +#endif /* _BITS_TIMER_H */ diff --git a/gpxe/src/arch/i386/include/bits/uaccess.h b/gpxe/src/arch/i386/include/bits/uaccess.h new file mode 100644 index 0000000..0ecc502 --- /dev/null +++ b/gpxe/src/arch/i386/include/bits/uaccess.h @@ -0,0 +1,12 @@ +#ifndef _BITS_UACCESS_H +#define _BITS_UACCESS_H + +/** @file + * + * i386-specific user access API implementations + * + */ + +#include + +#endif /* _BITS_UACCESS_H */ diff --git a/gpxe/src/arch/i386/include/bits/umalloc.h b/gpxe/src/arch/i386/include/bits/umalloc.h new file mode 100644 index 0000000..dcbd0a6 --- /dev/null +++ b/gpxe/src/arch/i386/include/bits/umalloc.h @@ -0,0 +1,12 @@ +#ifndef _BITS_UMALLOC_H +#define _BITS_UMALLOC_H + +/** @file + * + * i386-specific user memory allocation API implementations + * + */ + +#include + +#endif /* _BITS_UMALLOC_H */ diff --git a/gpxe/src/arch/i386/include/bochs.h b/gpxe/src/arch/i386/include/bochs.h new file mode 100644 index 0000000..9d090fc --- /dev/null +++ b/gpxe/src/arch/i386/include/bochs.h @@ -0,0 +1,34 @@ +#ifndef BOCHS_H +#define BOCHS_H + +/** @file + * + * bochs breakpoints + * + * This file defines @c bochsbp, the magic breakpoint instruction that + * is incredibly useful when debugging under bochs. This file should + * never be included in production code. + * + * Use the pseudo-instruction @c bochsbp in assembly code, or the + * bochsbp() function in C code. + * + */ + +#ifdef ASSEMBLY + +/* Breakpoint for when debugging under bochs */ +#define bochsbp xchgw %bx, %bx +#define BOCHSBP bochsbp + +#else /* ASSEMBLY */ + +/** Breakpoint for when debugging under bochs */ +static inline void bochsbp ( void ) { + __asm__ __volatile__ ( "xchgw %bx, %bx" ); +} + +#endif /* ASSEMBLY */ + +#warning "bochs.h should not be included into production code" + +#endif /* BOCHS_H */ diff --git a/gpxe/src/arch/i386/include/bootsector.h b/gpxe/src/arch/i386/include/bootsector.h new file mode 100644 index 0000000..e907105 --- /dev/null +++ b/gpxe/src/arch/i386/include/bootsector.h @@ -0,0 +1,12 @@ +#ifndef _BOOTSECTOR_H +#define _BOOTSECTOR_H + +/** @file + * + * x86 bootsector image format + */ + +extern int call_bootsector ( unsigned int segment, unsigned int offset, + unsigned int drive ); + +#endif /* _BOOTSECTOR_H */ diff --git a/gpxe/src/arch/i386/include/bzimage.h b/gpxe/src/arch/i386/include/bzimage.h new file mode 100644 index 0000000..aee058a --- /dev/null +++ b/gpxe/src/arch/i386/include/bzimage.h @@ -0,0 +1,137 @@ +#ifndef _BZIMAGE_H +#define _BZIMAGE_H + +#include + +/** + * A bzImage header + * + * As documented in Documentation/i386/boot.txt + */ +struct bzimage_header { + /** The size of the setup in sectors + * + * If this field contains 0, assume it contains 4. + */ + uint8_t setup_sects; + /** If set, the root is mounted readonly */ + uint16_t root_flags; + /** DO NOT USE - for bootsect.S use only */ + uint16_t syssize; + /** DO NOT USE - obsolete */ + uint16_t swap_dev; + /** DO NOT USE - for bootsect.S use only */ + uint16_t ram_size; + /** Video mode control */ + uint16_t vid_mode; + /** Default root device number */ + uint16_t root_dev; + /** 0xAA55 magic number */ + uint16_t boot_flag; + /** Jump instruction */ + uint16_t jump; + /** Magic signature "HdrS" */ + uint32_t header; + /** Boot protocol version supported */ + uint16_t version; + /** Boot loader hook (see below) */ + uint32_t realmode_swtch; + /** The load-low segment (0x1000) (obsolete) */ + uint16_t start_sys; + /** Pointer to kernel version string */ + uint16_t kernel_version; + /** Boot loader identifier */ + uint8_t type_of_loader; + /** Boot protocol option flags */ + uint8_t loadflags; + /** Move to high memory size (used with hooks) */ + uint16_t setup_move_size; + /** Boot loader hook (see below) */ + uint32_t code32_start; + /** initrd load address (set by boot loader) */ + uint32_t ramdisk_image; + /** initrd size (set by boot loader) */ + uint32_t ramdisk_size; + /** DO NOT USE - for bootsect.S use only */ + uint32_t bootsect_kludge; + /** Free memory after setup end */ + uint16_t heap_end_ptr; + /** Unused */ + uint16_t pad1; + /** 32-bit pointer to the kernel command line */ + uint32_t cmd_line_ptr; + /** Highest legal initrd address */ + uint32_t initrd_addr_max; + /** Physical addr alignment required for kernel */ + uint32_t kernel_alignment; + /** Whether kernel is relocatable or not */ + uint8_t relocatable_kernel; + /** Unused */ + uint8_t pad2[3]; + /** Maximum size of the kernel command line */ + uint32_t cmdline_size; +} __attribute__ (( packed )); + +/** Offset of bzImage header within kernel image */ +#define BZI_HDR_OFFSET 0x1f1 + +/** bzImage magic signature value */ +#define BZI_SIGNATURE 0x53726448 + +/** bzImage boot loader identifier for Etherboot */ +#define BZI_LOADER_TYPE_ETHERBOOT 0x40 + +/** bzImage boot loader identifier for gPXE + * + * We advertise ourselves as Etherboot version 6. + */ +#define BZI_LOADER_TYPE_GPXE ( BZI_LOADER_TYPE_ETHERBOOT | 0x06 ) + +/** bzImage "load high" flag */ +#define BZI_LOAD_HIGH 0x01 + +/** Load address for high-loaded kernels */ +#define BZI_LOAD_HIGH_ADDR 0x100000 + +/** Load address for low-loaded kernels */ +#define BZI_LOAD_LOW_ADDR 0x10000 + +/** bzImage "kernel can use heap" flag */ +#define BZI_CAN_USE_HEAP 0x80 + +/** bzImage special video mode "normal" */ +#define BZI_VID_MODE_NORMAL 0xffff + +/** bzImage special video mode "ext" */ +#define BZI_VID_MODE_EXT 0xfffe + +/** bzImage special video mode "ask" */ +#define BZI_VID_MODE_ASK 0xfffd + +/** bzImage maximum initrd address for versions < 2.03 */ +#define BZI_INITRD_MAX 0x37ffffff + +/** bzImage command-line structure used by older kernels */ +struct bzimage_cmdline { + /** Magic signature */ + uint16_t magic; + /** Offset to command line */ + uint16_t offset; +} __attribute__ (( packed )); + +/** Offset of bzImage command-line structure within kernel image */ +#define BZI_CMDLINE_OFFSET 0x20 + +/** bzImage command line present magic marker value */ +#define BZI_CMDLINE_MAGIC 0xa33f + +/** Assumed size of real-mode portion (including .bss) */ +#define BZI_ASSUMED_RM_SIZE 0x8000 + +/** Amount of stack space to provide */ +#define BZI_STACK_SIZE 0x1000 + +/** Maximum size of command line */ +#define BZI_CMDLINE_SIZE 0x100 + +#endif /* _BZIMAGE_H */ diff --git a/gpxe/src/arch/i386/include/callbacks_arch.h b/gpxe/src/arch/i386/include/callbacks_arch.h new file mode 100644 index 0000000..f9cba48 --- /dev/null +++ b/gpxe/src/arch/i386/include/callbacks_arch.h @@ -0,0 +1,243 @@ +/* Callout/callback interface for Etherboot + * + * This file provides the mechanisms for making calls from Etherboot + * to external programs and vice-versa. + * + * Initial version by Michael Brown , January 2004. + * + * $Id$ + */ + +#ifndef CALLBACKS_ARCH_H +#define CALLBACKS_ARCH_H + +/* Skip the definitions that won't make sense to the assembler */ +#ifndef ASSEMBLY + +/* Struct to hold general-purpose register values. PUSHAL and POPAL + * can work directly with this structure; do not change the order of + * registers. + */ +typedef struct { + union { + uint16_t di; + uint32_t edi; + }; + union { + uint16_t si; + uint32_t esi; + }; + union { + uint16_t bp; + uint32_t ebp; + }; + union { + uint16_t sp; + uint32_t esp; + }; + union { + struct { + uint8_t bl; + uint8_t bh; + } PACKED; + uint16_t bx; + uint32_t ebx; + }; + union { + struct { + uint8_t dl; + uint8_t dh; + } PACKED; + uint16_t dx; + uint32_t edx; + }; + union { + struct { + uint8_t cl; + uint8_t ch; + } PACKED; + uint16_t cx; + uint32_t ecx; + }; + union { + struct { + uint8_t al; + uint8_t ah; + } PACKED; + uint16_t ax; + uint32_t eax; + }; +} regs_t; + +/* Struct to hold segment register values. Don't change the order; + * many bits of assembly code rely on it. + */ +typedef struct { + uint16_t cs; + uint16_t ss; + uint16_t ds; + uint16_t es; + uint16_t fs; + uint16_t gs; +} PACKED seg_regs_t; + +/* Struct for a GDT descriptor */ +typedef struct { + uint16_t limit; + uint32_t address; + uint16_t padding; +} PACKED gdt_descriptor_t; + +/* Struct for a GDT entry. Use GDT_SEGMENT() to fill it in. + */ +typedef struct { + uint16_t limit_0_15; + uint16_t base_0_15; + uint8_t base_16_23; + uint8_t accessed__type__sflag__dpl__present; + uint8_t limit_16_19__avl__size__granularity; + uint8_t base_24_31; +} PACKED gdt_segment_t; + +#define GDT_SEGMENT(base,limit,type,sflag,dpl,avl,size,granularity) \ + ( (gdt_segment_t) { \ + ( (limit) & 0xffff ), \ + ( (base) & 0xffff ), \ + ( ( (base) >> 16 ) & 0xff ), \ + ( ( 1 << 0 ) | ( (type) << 1 ) | \ + ( (sflag) << 4 ) | ( (dpl) << 5 ) | ( 1 << 7 ) ), \ + ( ( (limit) >> 16 ) | \ + ( (avl) << 4 ) | ( (size) << 5 ) | ( (granularity) << 7 ) ),\ + ( (base) >> 24 ) \ + } ) +#define GDT_SEGMENT_BASE(gdt_segment) \ + ( (gdt_segment)->base_0_15 | \ + (gdt_segment)->base_16_23 << 16 | \ + (gdt_segment)->base_24_31 << 24 ) +#define GDT_SEGMENT_LIMIT(gdt_segment) \ + ( (gdt_segment)->limit_0_15 | \ + ( ( (gdt_segment)->limit_16_19__avl__size__granularity \ + & 0xf ) << 16 ) ) +#define GDT_SEGMENT_GRANULARITY(gdt_segment) \ + ( ( (gdt_segment)->limit_16_19__avl__size__granularity \ + & 0x80 ) >> 7 ) +#define GDT_SEGMENT_TYPE(gdt_segment) \ + ( ( (gdt_segment)->accessed__type__sflag__dpl__present & 0x0e ) >> 1 ) +#define GDT_SEGMENT_SIZE(gdt_segment) \ + ( ( (gdt_segment)->limit_16_19__avl__size__granularity \ + & 0x60 ) >> 5 ) + +#define GDT_TYPE_DATA (0x0) +#define GDT_TYPE_STACK (0x2) +#define GDT_TYPE_WRITEABLE (0x1) +#define GDT_TYPE_CODE (0x6) +#define GDT_TYPE_EXEC_ONLY_CODE (0x4) +#define GDT_TYPE_CONFORMING (0x1) +#define GDT_SFLAG_SYSTEM (0) +#define GDT_SFLAG_NORMAL (1) +#define GDT_AVL_NORMAL (0) +#define GDT_SIZE_16BIT (0x0) +#define GDT_SIZE_32BIT (0x2) +#define GDT_SIZE_64BIT (0x1) +#define GDT_SIZE_UNKNOWN (0x3) +#define GDT_GRANULARITY_SMALL (0) +#define GDT_GRANULARITY_LARGE (1) +#define GDT_SEGMENT_NORMAL(base,limit,type,size,granularity) \ + GDT_SEGMENT ( base, limit, type, \ + GDT_SFLAG_NORMAL, 0, GDT_AVL_NORMAL, \ + size, granularity ) + +/* Protected mode code segment */ +#define GDT_SEGMENT_PMCS(base) GDT_SEGMENT_NORMAL ( \ + base, 0xfffff, GDT_TYPE_CODE | GDT_TYPE_CONFORMING, \ + GDT_SIZE_32BIT, GDT_GRANULARITY_LARGE ) +#define GDT_SEGMENT_PMCS_PHYS GDT_SEGMENT_PMCS(0) +/* Protected mode data segment */ +#define GDT_SEGMENT_PMDS(base) GDT_SEGMENT_NORMAL ( \ + base, 0xfffff, GDT_TYPE_DATA | GDT_TYPE_WRITEABLE, \ + GDT_SIZE_32BIT, GDT_GRANULARITY_LARGE ) +#define GDT_SEGMENT_PMDS_PHYS GDT_SEGMENT_PMDS(0) +/* Real mode code segment */ +/* Not sure if there's any reason to use GDT_TYPE_EXEC_ONLY_CODE + * instead of just GDT_TYPE_CODE, but that's what our old GDT did and + * it worked, so I'm not changing it. + */ +#define GDT_SEGMENT_RMCS(base) GDT_SEGMENT_NORMAL ( \ + base, 0xffff, GDT_TYPE_EXEC_ONLY_CODE | GDT_TYPE_CONFORMING, \ + GDT_SIZE_16BIT, GDT_GRANULARITY_SMALL ) +/* Real mode data segment */ +#define GDT_SEGMENT_RMDS(base) GDT_SEGMENT_NORMAL ( \ + base, 0xffff, GDT_TYPE_DATA | GDT_TYPE_WRITEABLE, \ + GDT_SIZE_16BIT, GDT_GRANULARITY_SMALL ) +/* Long mode code segment */ +#define GDT_SEGMENT_LMCS(base) GDT_SEGMENT_NORMAL ( \ + base, 0xfffff, GDT_TYPE_CODE | GDT_TYPE_CONFORMING, \ + GDT_SIZE_64BIT, GDT_GRANULARITY_LARGE ) +#define GDT_SEGMENT_LMCS_PHYS GDT_SEGMENT_LMCS(0) +/* Long mode data segment */ +/* AFIACT, GDT_SIZE_64BIT applies only to code segments */ +#define GDT_SEGMENT_LMDS(base) GDT_SEGMENT_NORMAL ( \ + base, 0xfffff, GDT_TYPE_DATA | GDT_TYPE_WRITEABLE, \ + GDT_SIZE_32BIT, GDT_GRANULARITY_LARGE ) +#define GDT_SEGMENT_LMDS_PHYS GDT_SEGMENT_LMDS(0) + +/* Template for creating GDT structures (including segment register + * lists), suitable for passing as parameters to external_call(). + */ +#define GDT_STRUCT_t(num_segments) \ + struct { \ + gdt_descriptor_t descriptor; \ + gdt_segment_t segments[num_segments]; \ + } PACKED +/* And utility function for filling it in */ +#define GDT_ADJUST(structure) { \ + (structure)->descriptor.address = \ + virt_to_phys(&((structure)->descriptor.limit)); \ + (structure)->descriptor.limit = \ + sizeof((structure)->segments) + 8 - 1; \ + (structure)->descriptor.padding = 0; \ +} + +/* Data passed in to in_call() by assembly wrapper. + */ +typedef struct { + regs_t regs; + seg_regs_t seg_regs; + gdt_descriptor_t gdt_desc; + uint32_t flags; + struct { + uint32_t offset; + uint32_t segment; + } ret_addr; +} PACKED i386_pm_in_call_data_t; + +typedef struct { + seg_regs_t seg_regs; + union { + uint16_t pad; + uint16_t prefix_sp; + }; + uint16_t flags; + struct { + uint16_t offset; + uint16_t segment; + } ret_addr; + uint32_t orig_opcode; +} PACKED i386_rm_in_call_data_t; + +typedef struct { + i386_pm_in_call_data_t *pm; + i386_rm_in_call_data_t *rm; +} i386_in_call_data_t; +#define in_call_data_t i386_in_call_data_t + +/* Function prototypes + */ +extern int install_rm_callback_interface ( void *address, size_t available ); + +#endif /* ASSEMBLY */ + +#define RM_IN_CALL (0) +#define RM_IN_CALL_FAR (2) + +#endif /* CALLBACKS_ARCH_H */ diff --git a/gpxe/src/arch/i386/include/comboot.h b/gpxe/src/arch/i386/include/comboot.h new file mode 100644 index 0000000..56661a8 --- /dev/null +++ b/gpxe/src/arch/i386/include/comboot.h @@ -0,0 +1,133 @@ +#ifndef COMBOOT_H +#define COMBOOT_H + +/** + * @file + * + * SYSLINUX COMBOOT + */ + +#include +#include +#include + +/** Segment used for COMBOOT PSP and image */ +#define COMBOOT_PSP_SEG 0x07C0 + +/** Entry point address of COM32 images */ +#define COM32_START_PHYS 0x101000 + +/** COM32 bounce buffer segment */ +#define COM32_BOUNCE_SEG 0x07C0 + +/** Size of SYSLINUX file block in bytes */ +#define COMBOOT_FILE_BLOCKSZ 512 + +/** COMBOOT feature flags (INT 22h AX=15h) */ +#define COMBOOT_FEATURE_LOCAL_BOOT (1 << 0) +#define COMBOOT_FEATURE_IDLE_LOOP (1 << 1) + +/** Maximum number of shuffle descriptors for + * shuffle and boot functions + * (INT 22h AX=0012h, 001Ah, 001Bh) + */ +#define COMBOOT_MAX_SHUFFLE_DESCRIPTORS 682 + +typedef union { + uint32_t l; + uint16_t w[2]; + uint8_t b[4]; +} com32_reg32_t; + +typedef struct { + uint16_t gs; /* Offset 0 */ + uint16_t fs; /* Offset 2 */ + uint16_t es; /* Offset 4 */ + uint16_t ds; /* Offset 6 */ + + com32_reg32_t edi; /* Offset 8 */ + com32_reg32_t esi; /* Offset 12 */ + com32_reg32_t ebp; /* Offset 16 */ + com32_reg32_t _unused_esp; /* Offset 20 */ + com32_reg32_t ebx; /* Offset 24 */ + com32_reg32_t edx; /* Offset 28 */ + com32_reg32_t ecx; /* Offset 32 */ + com32_reg32_t eax; /* Offset 36 */ + + com32_reg32_t eflags; /* Offset 40 */ +} com32sys_t; + +typedef struct { + uint32_t eax; /* Offset 0 */ + uint32_t ecx; /* Offset 4 */ + uint32_t edx; /* Offset 8 */ + uint32_t ebx; /* Offset 12 */ + uint32_t esp; /* Offset 16 */ + uint32_t ebp; /* Offset 20 */ + uint32_t esi; /* Offset 24 */ + uint32_t edi; /* Offset 28 */ + + uint32_t eip; /* Offset 32 */ +} syslinux_pm_regs; + +typedef struct { + uint16_t es; /* Offset 0 */ + uint16_t _unused_cs; /* Offset 2 */ + uint16_t ds; /* Offset 4 */ + uint16_t ss; /* Offset 6 */ + uint16_t fs; /* Offset 8 */ + uint16_t gs; /* Offset 10 */ + + uint32_t eax; /* Offset 12 */ + uint32_t ecx; /* Offset 16 */ + uint32_t edx; /* Offset 20 */ + uint32_t ebx; /* Offset 24 */ + uint32_t esp; /* Offset 28 */ + uint32_t ebp; /* Offset 32 */ + uint32_t esi; /* Offset 36 */ + uint32_t edi; /* Offset 40 */ + + uint16_t ip; /* Offset 44 */ + uint16_t cs; /* Offset 46 */ +} syslinux_rm_regs; + +typedef struct { + uint32_t dest; + uint32_t src; + uint32_t len; +} comboot_shuffle_descriptor; + +extern void hook_comboot_interrupts ( ); +extern void unhook_comboot_interrupts ( ); + +/* These are not the correct prototypes, but it doens't matter, + * as we only ever get the address of these functions; + * they are only called from COM32 code running in PHYS_CODE + */ +extern void com32_intcall_wrapper ( ); +extern void com32_farcall_wrapper ( ); +extern void com32_cfarcall_wrapper ( ); + +/* Resolve a hostname to an (IPv4) address */ +extern int comboot_resolv ( const char *name, struct in_addr *address ); + +/* setjmp/longjmp context buffer used to return after loading an image */ +extern rmjmp_buf comboot_return; + +/* Replacement image when exiting with COMBOOT_EXIT_RUN_KERNEL */ +extern struct image *comboot_replacement_image; + +extern void *com32_external_esp; + +#define COMBOOT_EXIT 1 +#define COMBOOT_EXIT_RUN_KERNEL 2 +#define COMBOOT_EXIT_COMMAND 3 + +extern void comboot_force_text_mode ( void ); + +#define COMBOOT_VIDEO_GRAPHICS 0x01 +#define COMBOOT_VIDEO_NONSTANDARD 0x02 +#define COMBOOT_VIDEO_VESA 0x04 +#define COMBOOT_VIDEO_NOTEXT 0x08 + +#endif diff --git a/gpxe/src/arch/i386/include/fakee820.h b/gpxe/src/arch/i386/include/fakee820.h new file mode 100644 index 0000000..f1fe8af --- /dev/null +++ b/gpxe/src/arch/i386/include/fakee820.h @@ -0,0 +1,7 @@ +#ifndef _FAKEE820_H +#define _FAKEE820_H + +extern void fake_e820 ( void ); +extern void unfake_e820 ( void ); + +#endif /* _FAKEE820_H */ diff --git a/gpxe/src/arch/i386/include/gateA20.h b/gpxe/src/arch/i386/include/gateA20.h new file mode 100644 index 0000000..297ad6f --- /dev/null +++ b/gpxe/src/arch/i386/include/gateA20.h @@ -0,0 +1,7 @@ +#ifndef GATEA20_H +#define GATEA20_H + +extern void gateA20_set ( void ); +extern void gateA20_unset ( void ); + +#endif /* GATEA20_H */ diff --git a/gpxe/src/arch/i386/include/gdbmach.h b/gpxe/src/arch/i386/include/gdbmach.h new file mode 100644 index 0000000..794dab1 --- /dev/null +++ b/gpxe/src/arch/i386/include/gdbmach.h @@ -0,0 +1,64 @@ +#ifndef GDBMACH_H +#define GDBMACH_H + +/** @file + * + * GDB architecture specifics + * + * This file declares functions for manipulating the machine state and + * debugging context. + * + */ + +#include + +typedef unsigned long gdbreg_t; + +/* The register snapshot, this must be in sync with interrupt handler and the + * GDB protocol. */ +enum { + GDBMACH_EAX, + GDBMACH_ECX, + GDBMACH_EDX, + GDBMACH_EBX, + GDBMACH_ESP, + GDBMACH_EBP, + GDBMACH_ESI, + GDBMACH_EDI, + GDBMACH_EIP, + GDBMACH_EFLAGS, + GDBMACH_CS, + GDBMACH_SS, + GDBMACH_DS, + GDBMACH_ES, + GDBMACH_FS, + GDBMACH_GS, + GDBMACH_NREGS, + GDBMACH_SIZEOF_REGS = GDBMACH_NREGS * sizeof ( gdbreg_t ) +}; + +/* Breakpoint types */ +enum { + GDBMACH_BPMEM, + GDBMACH_BPHW, + GDBMACH_WATCH, + GDBMACH_RWATCH, + GDBMACH_AWATCH, +}; + +static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) { + regs [ GDBMACH_EIP ] = pc; +} + +static inline void gdbmach_set_single_step ( gdbreg_t *regs, int step ) { + regs [ GDBMACH_EFLAGS ] &= ~( 1 << 8 ); /* Trace Flag (TF) */ + regs [ GDBMACH_EFLAGS ] |= ( step << 8 ); +} + +static inline void gdbmach_breakpoint ( void ) { + __asm__ __volatile__ ( "int $3\n" ); +} + +extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, int enable ); + +#endif /* GDBMACH_H */ diff --git a/gpxe/src/arch/i386/include/gpxe/abft.h b/gpxe/src/arch/i386/include/gpxe/abft.h new file mode 100644 index 0000000..1c651ef --- /dev/null +++ b/gpxe/src/arch/i386/include/gpxe/abft.h @@ -0,0 +1,35 @@ +#ifndef _GPXE_ABFT_H +#define _GPXE_ABFT_H + +/** @file + * + * AoE boot firmware table + * + */ + +#include +#include +#include + +/** AoE boot firmware table signature */ +#define ABFT_SIG "aBFT" + +/** + * AoE Boot Firmware Table (aBFT) + */ +struct abft_table { + /** ACPI header */ + struct acpi_description_header acpi; + /** AoE shelf */ + uint16_t shelf; + /** AoE slot */ + uint8_t slot; + /** Reserved */ + uint8_t reserved_a; + /** MAC address */ + uint8_t mac[ETH_ALEN]; +} __attribute__ (( packed )); + +extern void abft_fill_data ( struct aoe_session *aoe ); + +#endif /* _GPXE_ABFT_H */ diff --git a/gpxe/src/arch/i386/include/gpxe/bios_nap.h b/gpxe/src/arch/i386/include/gpxe/bios_nap.h new file mode 100644 index 0000000..f1c721e --- /dev/null +++ b/gpxe/src/arch/i386/include/gpxe/bios_nap.h @@ -0,0 +1,16 @@ +#ifndef _GPXE_BIOS_NAP_H +#define _GPXE_BIOS_NAP_H + +/** @file + * + * BIOS CPU sleeping + * + */ + +#ifdef NAP_PCBIOS +#define NAP_PREFIX_pcbios +#else +#define NAP_PREFIX_pcbios __pcbios_ +#endif + +#endif /* _GPXE_BIOS_NAP_H */ diff --git a/gpxe/src/arch/i386/include/gpxe/bios_smbios.h b/gpxe/src/arch/i386/include/gpxe/bios_smbios.h new file mode 100644 index 0000000..0a6f277 --- /dev/null +++ b/gpxe/src/arch/i386/include/gpxe/bios_smbios.h @@ -0,0 +1,16 @@ +#ifndef _GPXE_BIOS_SMBIOS_H +#define _GPXE_BIOS_SMBIOS_H + +/** @file + * + * Standard PC-BIOS SMBIOS interface + * + */ + +#ifdef SMBIOS_PCBIOS +#define SMBIOS_PREFIX_pcbios +#else +#define SMBIOS_PREFIX_pcbios __pcbios_ +#endif + +#endif /* _GPXE_BIOS_SMBIOS_H */ diff --git a/gpxe/src/arch/i386/include/gpxe/bios_timer.h b/gpxe/src/arch/i386/include/gpxe/bios_timer.h new file mode 100644 index 0000000..7e3caa3 --- /dev/null +++ b/gpxe/src/arch/i386/include/gpxe/bios_timer.h @@ -0,0 +1,42 @@ +#ifndef _GPXE_BIOS_TIMER_H +#define _GPXE_BIOS_TIMER_H + +/** @file + * + * BIOS timer + * + */ + +#ifdef TIMER_PCBIOS +#define TIMER_PREFIX_pcbios +#else +#define TIMER_PREFIX_pcbios __pcbios_ +#endif + +#include + +/** + * Delay for a fixed number of microseconds + * + * @v usecs Number of microseconds for which to delay + */ +static inline __always_inline void +TIMER_INLINE ( pcbios, udelay ) ( unsigned long usecs ) { + /* BIOS timer is not high-resolution enough for udelay(), so + * we use timer2 + */ + timer2_udelay ( usecs ); +} + +/** + * Get number of ticks per second + * + * @ret ticks_per_sec Number of ticks per second + */ +static inline __always_inline unsigned long +TIMER_INLINE ( pcbios, ticks_per_sec ) ( void ) { + /* BIOS timer ticks over at 18.2 ticks per second */ + return 18; +} + +#endif /* _GPXE_BIOS_TIMER_H */ diff --git a/gpxe/src/arch/i386/include/gpxe/ibft.h b/gpxe/src/arch/i386/include/gpxe/ibft.h new file mode 100644 index 0000000..5eef547 --- /dev/null +++ b/gpxe/src/arch/i386/include/gpxe/ibft.h @@ -0,0 +1,300 @@ +#ifndef _GPXE_IBFT_H +#define _GPXE_IBFT_H + +/* + * Copyright Fen Systems Ltd. 2007. Portions of this code are derived + * from IBM Corporation Sample Programs. Copyright IBM Corporation + * 2004, 2007. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +/** @file + * + * iSCSI boot firmware table + * + * The information in this file is derived from the document "iSCSI + * Boot Firmware Table (iBFT)" as published by IBM at + * + * ftp://ftp.software.ibm.com/systems/support/system_x_pdf/ibm_iscsi_boot_firmware_table_v1.02.pdf + * + */ + +#include +#include +#include + +/** iSCSI Boot Firmware Table signature */ +#define IBFT_SIG "iBFT" + +/** An offset from the start of the iBFT */ +typedef uint16_t ibft_off_t; + +/** Length of a string within the iBFT (excluding terminating NUL) */ +typedef uint16_t ibft_size_t; + +/** A string within the iBFT */ +struct ibft_string { + /** Length of string */ + ibft_size_t length; + /** Offset to string */ + ibft_off_t offset; +} __attribute__ (( packed )); + +/** An IP address within the iBFT */ +struct ibft_ipaddr { + /** Reserved; must be zero */ + uint16_t zeroes[5]; + /** Must be 0xffff if IPv4 address is present, otherwise zero */ + uint16_t ones; + /** The IPv4 address, or zero if not present */ + struct in_addr in; +} __attribute__ (( packed )); + +/** + * iBFT structure header + * + * This structure is common to several sections within the iBFT. + */ +struct ibft_header { + /** Structure ID + * + * This is an IBFT_STRUCTURE_ID_XXX constant + */ + uint8_t structure_id; + /** Version (always 1) */ + uint8_t version; + /** Length, including this header */ + uint16_t length; + /** Index + * + * This is the number of the NIC or Target, when applicable. + */ + uint8_t index; + /** Flags */ + uint8_t flags; +} __attribute__ (( packed )); + +/** + * iBFT Control structure + * + */ +struct ibft_control { + /** Common header */ + struct ibft_header header; + /** Extensions */ + uint16_t extensions; + /** Offset to Initiator structure */ + ibft_off_t initiator; + /** Offset to NIC structure for NIC 0 */ + ibft_off_t nic_0; + /** Offset to Target structure for target 0 */ + ibft_off_t target_0; + /** Offset to NIC structure for NIC 1 */ + ibft_off_t nic_1; + /** Offset to Target structure for target 1 */ + ibft_off_t target_1; +} __attribute__ (( packed )); + +/** Structure ID for Control section */ +#define IBFT_STRUCTURE_ID_CONTROL 0x01 + +/** Attempt login only to specified target + * + * If this flag is not set, all targets will be logged in to. + */ +#define IBFT_FL_CONTROL_SINGLE_LOGIN_ONLY 0x01 + +/** + * iBFT Initiator structure + * + */ +struct ibft_initiator { + /** Common header */ + struct ibft_header header; + /** iSNS server */ + struct ibft_ipaddr isns_server; + /** SLP server */ + struct ibft_ipaddr slp_server; + /** Primary and secondary Radius servers */ + struct ibft_ipaddr radius[2]; + /** Initiator name */ + struct ibft_string initiator_name; +} __attribute__ (( packed )); + +/** Structure ID for Initiator section */ +#define IBFT_STRUCTURE_ID_INITIATOR 0x02 + +/** Initiator block valid */ +#define IBFT_FL_INITIATOR_BLOCK_VALID 0x01 + +/** Initiator firmware boot selected */ +#define IBFT_FL_INITIATOR_FIRMWARE_BOOT_SELECTED 0x02 + +/** + * iBFT NIC structure + * + */ +struct ibft_nic { + /** Common header */ + struct ibft_header header; + /** IP address */ + struct ibft_ipaddr ip_address; + /** Subnet mask + * + * This is the length of the subnet mask in bits (e.g. /24). + */ + uint8_t subnet_mask_prefix; + /** Origin */ + uint8_t origin; + /** Default gateway */ + struct ibft_ipaddr gateway; + /** Primary and secondary DNS servers */ + struct ibft_ipaddr dns[2]; + /** DHCP server */ + struct ibft_ipaddr dhcp; + /** VLAN tag */ + uint16_t vlan; + /** MAC address */ + uint8_t mac_address[6]; + /** PCI bus:dev:fn */ + uint16_t pci_bus_dev_func; + /** Hostname */ + struct ibft_string hostname; +} __attribute__ (( packed )); + +/** Structure ID for NIC section */ +#define IBFT_STRUCTURE_ID_NIC 0x03 + +/** NIC block valid */ +#define IBFT_FL_NIC_BLOCK_VALID 0x01 + +/** NIC firmware boot selected */ +#define IBFT_FL_NIC_FIRMWARE_BOOT_SELECTED 0x02 + +/** NIC global / link local */ +#define IBFT_FL_NIC_GLOBAL 0x04 + +/** + * iBFT Target structure + * + */ +struct ibft_target { + /** Common header */ + struct ibft_header header; + /** IP address */ + struct ibft_ipaddr ip_address; + /** TCP port */ + uint16_t socket; + /** Boot LUN */ + uint64_t boot_lun; + /** CHAP type + * + * This is an IBFT_CHAP_XXX constant. + */ + uint8_t chap_type; + /** NIC association */ + uint8_t nic_association; + /** Target name */ + struct ibft_string target_name; + /** CHAP name */ + struct ibft_string chap_name; + /** CHAP secret */ + struct ibft_string chap_secret; + /** Reverse CHAP name */ + struct ibft_string reverse_chap_name; + /** Reverse CHAP secret */ + struct ibft_string reverse_chap_secret; +} __attribute__ (( packed )); + +/** Structure ID for Target section */ +#define IBFT_STRUCTURE_ID_TARGET 0x04 + +/** Target block valid */ +#define IBFT_FL_TARGET_BLOCK_VALID 0x01 + +/** Target firmware boot selected */ +#define IBFT_FL_TARGET_FIRMWARE_BOOT_SELECTED 0x02 + +/** Target use Radius CHAP */ +#define IBFT_FL_TARGET_USE_CHAP 0x04 + +/** Target use Radius rCHAP */ +#define IBFT_FL_TARGET_USE_RCHAP 0x08 + +/* Values for chap_type */ +#define IBFT_CHAP_NONE 0 /**< No CHAP authentication */ +#define IBFT_CHAP_ONE_WAY 1 /**< One-way CHAP */ +#define IBFT_CHAP_MUTUAL 2 /**< Mutual CHAP */ + +/** + * iSCSI Boot Firmware Table (iBFT) + */ +struct ibft_table { + /** ACPI header */ + struct acpi_description_header acpi; + /** Reserved */ + uint8_t reserved[12]; + /** Control structure */ + struct ibft_control control; +} __attribute__ (( packed )); + +/** + * iSCSI string block descriptor + * + * This is an internal structure that we use to keep track of the + * allocation of string data. + */ +struct ibft_string_block { + /** The iBFT containing these strings */ + struct ibft_table *table; + /** Offset of first free byte within iBFT */ + unsigned int offset; +}; + +/** Amount of space reserved for strings in a gPXE iBFT */ +#define IBFT_STRINGS_SIZE 384 + +/** + * An iBFT created by gPXE + * + */ +struct gpxe_ibft { + /** The fixed section */ + struct ibft_table table; + /** The Initiator section */ + struct ibft_initiator initiator __attribute__ (( aligned ( 16 ) )); + /** The NIC section */ + struct ibft_nic nic __attribute__ (( aligned ( 16 ) )); + /** The Target section */ + struct ibft_target target __attribute__ (( aligned ( 16 ) )); + /** Strings block */ + char strings[IBFT_STRINGS_SIZE]; +} __attribute__ (( packed, aligned ( 16 ) )); + +struct net_device; +struct iscsi_session; + +extern int ibft_fill_data ( struct net_device *netdev, + struct iscsi_session *iscsi ); + +#endif /* _GPXE_IBFT_H */ diff --git a/gpxe/src/arch/i386/include/gpxe/memtop_umalloc.h b/gpxe/src/arch/i386/include/gpxe/memtop_umalloc.h new file mode 100644 index 0000000..a3cd2c0 --- /dev/null +++ b/gpxe/src/arch/i386/include/gpxe/memtop_umalloc.h @@ -0,0 +1,16 @@ +#ifndef _GPXE_MEMTOP_UMALLOC_H +#define _GPXE_MEMTOP_UMALLOC_H + +/** @file + * + * External memory allocation + * + */ + +#ifdef UMALLOC_MEMTOP +#define UMALLOC_PREFIX_memtop +#else +#define UMALLOC_PREFIX_memtop __memtop_ +#endif + +#endif /* _GPXE_MEMTOP_UMALLOC_H */ diff --git a/gpxe/src/arch/i386/include/gpxe/rdtsc_timer.h b/gpxe/src/arch/i386/include/gpxe/rdtsc_timer.h new file mode 100644 index 0000000..0e03d70 --- /dev/null +++ b/gpxe/src/arch/i386/include/gpxe/rdtsc_timer.h @@ -0,0 +1,37 @@ +#ifndef _GPXE_RDTSC_TIMER_H +#define _GPXE_RDTSC_TIMER_H + +/** @file + * + * RDTSC timer + * + */ + +#ifdef TIMER_RDTSC +#define TIMER_PREFIX_rdtsc +#else +#define TIMER_PREFIX_rdtsc __rdtsc_ +#endif + +/** + * RDTSC values can easily overflow an unsigned long. We discard the + * low-order bits in order to obtain sensibly-scaled values. + */ +#define TSC_SHIFT 8 + +/** + * Get current system time in ticks + * + * @ret ticks Current time, in ticks + */ +static inline __always_inline unsigned long +TIMER_INLINE ( rdtsc, currticks ) ( void ) { + unsigned long ticks; + + __asm__ __volatile__ ( "rdtsc\n\t" + "shrdl %1, %%edx, %%eax\n\t" + : "=a" ( ticks ) : "i" ( TSC_SHIFT ) : "edx" ); + return ticks; +} + +#endif /* _GPXE_RDTSC_TIMER_H */ diff --git a/gpxe/src/arch/i386/include/gpxe/timer2.h b/gpxe/src/arch/i386/include/gpxe/timer2.h new file mode 100644 index 0000000..59705fa --- /dev/null +++ b/gpxe/src/arch/i386/include/gpxe/timer2.h @@ -0,0 +1,12 @@ +#ifndef _GPXE_TIMER2_H +#define _GPXE_TIMER2_H + +/** @file + * + * Timer chip control + * + */ + +extern void timer2_udelay ( unsigned long usecs ); + +#endif /* _GPXE_TIMER2_H */ diff --git a/gpxe/src/arch/i386/include/gpxe/x86_io.h b/gpxe/src/arch/i386/include/gpxe/x86_io.h new file mode 100644 index 0000000..b1ae3ba --- /dev/null +++ b/gpxe/src/arch/i386/include/gpxe/x86_io.h @@ -0,0 +1,151 @@ +#ifndef _GPXE_X86_IO_H +#define _GPXE_X86_IO_H + +/** @file + * + * gPXE I/O API for x86 + * + * i386 uses direct pointer dereferences for accesses to memory-mapped + * I/O space, and the inX/outX instructions for accesses to + * port-mapped I/O space. + * + * 64-bit atomic accesses (readq() and writeq()) use MMX instructions, + * and will crash original Pentium and earlier CPUs. Fortunately, no + * hardware that requires atomic 64-bit accesses will physically fit + * into a machine with such an old CPU anyway. + */ + +#ifdef IOAPI_X86 +#define IOAPI_PREFIX_x86 +#else +#define IOAPI_PREFIX_x86 __x86_ +#endif + +/* + * Memory space mappings + * + */ + +/* + * Physical<->Bus and Bus<->I/O address mappings + * + */ + +static inline __always_inline unsigned long +IOAPI_INLINE ( x86, phys_to_bus ) ( unsigned long phys_addr ) { + return phys_addr; +} + +static inline __always_inline unsigned long +IOAPI_INLINE ( x86, bus_to_phys ) ( unsigned long bus_addr ) { + return bus_addr; +} + +static inline __always_inline void * +IOAPI_INLINE ( x86, ioremap ) ( unsigned long bus_addr, size_t len __unused ) { + return phys_to_virt ( bus_addr ); +} + +static inline __always_inline void +IOAPI_INLINE ( x86, iounmap ) ( volatile const void *io_addr __unused ) { + /* Nothing to do */ +} + +static inline __always_inline unsigned long +IOAPI_INLINE ( x86, io_to_bus ) ( volatile const void *io_addr ) { + return virt_to_phys ( io_addr ); +} + +/* + * MMIO reads and writes up to 32 bits + * + */ + +#define X86_READX( _api_func, _type ) \ +static inline __always_inline _type \ +IOAPI_INLINE ( x86, _api_func ) ( volatile _type *io_addr ) { \ + return *io_addr; \ +} +X86_READX ( readb, uint8_t ); +X86_READX ( readw, uint16_t ); +X86_READX ( readl, uint32_t ); + +#define X86_WRITEX( _api_func, _type ) \ +static inline __always_inline void \ +IOAPI_INLINE ( x86, _api_func ) ( _type data, \ + volatile _type *io_addr ) { \ + *io_addr = data; \ +} +X86_WRITEX ( writeb, uint8_t ); +X86_WRITEX ( writew, uint16_t ); +X86_WRITEX ( writel, uint32_t ); + +/* + * PIO reads and writes up to 32 bits + * + */ + +#define X86_INX( _insn_suffix, _type, _reg_prefix ) \ +static inline __always_inline _type \ +IOAPI_INLINE ( x86, in ## _insn_suffix ) ( volatile _type *io_addr ) { \ + _type data; \ + __asm__ __volatile__ ( "in" #_insn_suffix " %w1, %" _reg_prefix "0" \ + : "=a" ( data ) : "Nd" ( io_addr ) ); \ + return data; \ +} \ +static inline __always_inline void \ +IOAPI_INLINE ( x86, ins ## _insn_suffix ) ( volatile _type *io_addr, \ + _type *data, \ + unsigned int count ) { \ + unsigned int discard_D; \ + __asm__ __volatile__ ( "rep ins" #_insn_suffix \ + : "=D" ( discard_D ) \ + : "d" ( io_addr ), "c" ( count ), \ + "0" ( data ) ); \ +} +X86_INX ( b, uint8_t, "b" ); +X86_INX ( w, uint16_t, "w" ); +X86_INX ( l, uint32_t, "k" ); + +#define X86_OUTX( _insn_suffix, _type, _reg_prefix ) \ +static inline __always_inline void \ +IOAPI_INLINE ( x86, out ## _insn_suffix ) ( _type data, \ + volatile _type *io_addr ) { \ + __asm__ __volatile__ ( "out" #_insn_suffix " %" _reg_prefix "0, %w1" \ + : : "a" ( data ), "Nd" ( io_addr ) ); \ +} \ +static inline __always_inline void \ +IOAPI_INLINE ( x86, outs ## _insn_suffix ) ( volatile _type *io_addr, \ + const _type *data, \ + unsigned int count ) { \ + unsigned int discard_S; \ + __asm__ __volatile__ ( "rep outs" #_insn_suffix \ + : "=S" ( discard_S ) \ + : "d" ( io_addr ), "c" ( count ), \ + "0" ( data ) ); \ +} +X86_OUTX ( b, uint8_t, "b" ); +X86_OUTX ( w, uint16_t, "w" ); +X86_OUTX ( l, uint32_t, "k" ); + +/* + * Slow down I/O + * + */ + +static inline __always_inline void +IOAPI_INLINE ( x86, iodelay ) ( void ) { + __asm__ __volatile__ ( "outb %al, $0x80" ); +} + +/* + * Memory barrier + * + */ + +static inline __always_inline void +IOAPI_INLINE ( x86, mb ) ( void ) { + __asm__ __volatile__ ( "lock; addl $0, 0(%%esp)" : : : "memory" ); +} + +#endif /* _GPXE_X86_IO_H */ diff --git a/gpxe/src/arch/i386/include/int13.h b/gpxe/src/arch/i386/include/int13.h new file mode 100644 index 0000000..bf6d031 --- /dev/null +++ b/gpxe/src/arch/i386/include/int13.h @@ -0,0 +1,290 @@ +#ifndef INT13_H +#define INT13_H + +/** @file + * + * INT 13 emulation + * + */ + +#include +#include +#include + +struct block_device; + +/** + * @defgroup int13ops INT 13 operation codes + * @{ + */ + +/** Reset disk system */ +#define INT13_RESET 0x00 +/** Get status of last operation */ +#define INT13_GET_LAST_STATUS 0x01 +/** Read sectors */ +#define INT13_READ_SECTORS 0x02 +/** Write sectors */ +#define INT13_WRITE_SECTORS 0x03 +/** Get drive parameters */ +#define INT13_GET_PARAMETERS 0x08 +/** Get disk type */ +#define INT13_GET_DISK_TYPE 0x15 +/** Extensions installation check */ +#define INT13_EXTENSION_CHECK 0x41 +/** Extended read */ +#define INT13_EXTENDED_READ 0x42 +/** Extended write */ +#define INT13_EXTENDED_WRITE 0x43 +/** Get extended drive parameters */ +#define INT13_GET_EXTENDED_PARAMETERS 0x48 +/** Get CD-ROM status / terminate emulation */ +#define INT13_CDROM_STATUS_TERMINATE 0x4b + +/** @} */ + +/** + * @defgroup int13status INT 13 status codes + * @{ + */ + +/** Operation completed successfully */ +#define INT13_STATUS_SUCCESS 0x00 +/** Invalid function or parameter */ +#define INT13_STATUS_INVALID 0x01 +/** Read error */ +#define INT13_STATUS_READ_ERROR 0x04 +/** Write error */ +#define INT13_STATUS_WRITE_ERROR 0xcc + +/** @} */ + +/** Block size for non-extended INT 13 calls */ +#define INT13_BLKSIZE 512 + +/** An INT 13 emulated drive */ +struct int13_drive { + /** List of all registered drives */ + struct list_head list; + + /** Underlying block device */ + struct block_device *blockdev; + + /** BIOS in-use drive number (0x80-0xff) */ + unsigned int drive; + /** BIOS natural drive number (0x80-0xff) + * + * This is the drive number that would have been assigned by + * 'naturally' appending the drive to the end of the BIOS + * drive list. + * + * If the emulated drive replaces a preexisting drive, this is + * the drive number that the preexisting drive gets remapped + * to. + */ + unsigned int natural_drive; + + /** Number of cylinders + * + * The cylinder number field in an INT 13 call is ten bits + * wide, giving a maximum of 1024 cylinders. Conventionally, + * when the 7.8GB limit of a CHS address is exceeded, it is + * the number of cylinders that is increased beyond the + * addressable limit. + */ + unsigned int cylinders; + /** Number of heads + * + * The head number field in an INT 13 call is eight bits wide, + * giving a maximum of 256 heads. However, apparently all + * versions of MS-DOS up to and including Win95 fail with 256 + * heads, so the maximum encountered in practice is 255. + */ + unsigned int heads; + /** Number of sectors per track + * + * The sector number field in an INT 13 call is six bits wide, + * giving a maximum of 63 sectors, since sector numbering + * (unlike head and cylinder numbering) starts at 1, not 0. + */ + unsigned int sectors_per_track; + + /** Status of last operation */ + int last_status; +}; + +/** An INT 13 disk address packet */ +struct int13_disk_address { + /** Size of the packet, in bytes */ + uint8_t bufsize; + /** Reserved, must be zero */ + uint8_t reserved; + /** Block count */ + uint16_t count; + /** Data buffer */ + struct segoff buffer; + /** Starting block number */ + uint64_t lba; + /** Data buffer (EDD-3.0 only) */ + uint64_t buffer_phys; +} __attribute__ (( packed )); + +/** INT 13 disk parameters */ +struct int13_disk_parameters { + /** Size of this structure */ + uint16_t bufsize; + /** Flags */ + uint16_t flags; + /** Number of cylinders */ + uint32_t cylinders; + /** Number of heads */ + uint32_t heads; + /** Number of sectors per track */ + uint32_t sectors_per_track; + /** Total number of sectors on drive */ + uint64_t sectors; + /** Bytes per sector */ + uint16_t sector_size; + +} __attribute__ (( packed )); + +/** + * @defgroup int13types INT 13 disk types + * @{ + */ + +/** No such drive */ +#define INT13_DISK_TYPE_NONE 0x00 +/** Floppy without change-line support */ +#define INT13_DISK_TYPE_FDD 0x01 +/** Floppy with change-line support */ +#define INT13_DISK_TYPE_FDD_CL 0x02 +/** Hard disk */ +#define INT13_DISK_TYPE_HDD 0x03 + +/** @} */ + +/** + * @defgroup int13flags INT 13 disk parameter flags + * @{ + */ + +/** DMA boundary errors handled transparently */ +#define INT13_FL_DMA_TRANSPARENT 0x01 +/** CHS information is valid */ +#define INT13_FL_CHS_VALID 0x02 +/** Removable drive */ +#define INT13_FL_REMOVABLE 0x04 +/** Write with verify supported */ +#define INT13_FL_VERIFIABLE 0x08 +/** Has change-line supported (valid only for removable drives) */ +#define INT13_FL_CHANGE_LINE 0x10 +/** Drive can be locked (valid only for removable drives) */ +#define INT13_FL_LOCKABLE 0x20 +/** CHS is max possible, not current media (valid only for removable drives) */ +#define INT13_FL_CHS_MAX 0x40 + +/** @} */ + +/** + * @defgroup int13exts INT 13 extension flags + * @{ + */ + +/** Extended disk access functions supported */ +#define INT13_EXTENSION_LINEAR 0x01 +/** Removable drive functions supported */ +#define INT13_EXTENSION_REMOVABLE 0x02 +/** EDD functions supported */ +#define INT13_EXTENSION_EDD 0x04 + +/** @} */ + +/** + * @defgroup int13vers INT 13 extension versions + * @{ + */ + +/** INT13 extensions version 1.x */ +#define INT13_EXTENSION_VER_1_X 0x01 +/** INT13 extensions version 2.0 (EDD-1.0) */ +#define INT13_EXTENSION_VER_2_0 0x20 +/** INT13 extensions version 2.1 (EDD-1.1) */ +#define INT13_EXTENSION_VER_2_1 0x21 +/** INT13 extensions version 3.0 (EDD-3.0) */ +#define INT13_EXTENSION_VER_3_0 0x30 + +/** @} */ + +/** Bootable CD-ROM specification packet */ +struct int13_cdrom_specification { + /** Size of packet in bytes */ + uint8_t size; + /** Boot media type */ + uint8_t media_type; + /** Drive number */ + uint8_t drive; + /** CD-ROM controller number */ + uint8_t controller; + /** LBA of disk image to emulate */ + uint32_t lba; + /** Device specification */ + uint16_t device; + /** Segment of 3K buffer for caching CD-ROM reads */ + uint16_t cache_segment; + /** Load segment for initial boot image */ + uint16_t load_segment; + /** Number of 512-byte sectors to load */ + uint16_t load_sectors; + /** Low 8 bits of cylinder number */ + uint8_t cyl; + /** Sector number, plus high 2 bits of cylinder number */ + uint8_t cyl_sector; + /** Head number */ + uint8_t head; +} __attribute__ (( packed )); + +/** A C/H/S address within a partition table entry */ +struct partition_chs { + /** Head number */ + uint8_t head; + /** Sector number, plus high 2 bits of cylinder number */ + uint8_t cyl_sector; + /** Low 8 bits of cylinder number */ + uint8_t cyl; +} __attribute__ (( packed )); + +#define PART_HEAD(chs) ( (chs).head ) +#define PART_SECTOR(chs) ( (chs).cyl_sector & 0x3f ) +#define PART_CYLINDER(chs) ( (chs).cyl | ( ( (chs).cyl_sector & 0xc0 ) << 2 ) ) + +/** A partition table entry within the MBR */ +struct partition_table_entry { + /** Bootable flag */ + uint8_t bootable; + /** C/H/S start address */ + struct partition_chs chs_start; + /** System indicator (partition type) */ + uint8_t type; + /** C/H/S end address */ + struct partition_chs chs_end; + /** Linear start address */ + uint32_t start; + /** Linear length */ + uint32_t length; +} __attribute__ (( packed )); + +/** A Master Boot Record */ +struct master_boot_record { + uint8_t pad[446]; + /** Partition table */ + struct partition_table_entry partitions[4]; + /** 0x55aa MBR signature */ + uint16_t signature; +} __attribute__ (( packed )); + +extern void register_int13_drive ( struct int13_drive *drive ); +extern void unregister_int13_drive ( struct int13_drive *drive ); +extern int int13_boot ( unsigned int drive ); + +#endif /* INT13_H */ diff --git a/gpxe/src/arch/i386/include/kir.h b/gpxe/src/arch/i386/include/kir.h new file mode 100644 index 0000000..84633d2 --- /dev/null +++ b/gpxe/src/arch/i386/include/kir.h @@ -0,0 +1,18 @@ +#ifndef KIR_H +#define KIR_H + +#ifndef KEEP_IT_REAL +#error "kir.h can be used only with -DKEEP_IT_REAL" +#endif + +#ifdef ASSEMBLY + +#define code32 code16gcc + +#else /* ASSEMBLY */ + +__asm__ ( ".code16gcc" ); + +#endif /* ASSEMBLY */ + +#endif /* KIR_H */ diff --git a/gpxe/src/arch/i386/include/libkir.h b/gpxe/src/arch/i386/include/libkir.h new file mode 100644 index 0000000..1f5b135 --- /dev/null +++ b/gpxe/src/arch/i386/include/libkir.h @@ -0,0 +1,233 @@ +#ifndef LIBKIR_H +#define LIBKIR_H + +#include "realmode.h" + +#ifndef ASSEMBLY + +/* + * Full API documentation for these functions is in realmode.h. + * + */ + +/* Access to variables in .data16 and .text16 in a way compatible with librm */ +#define __data16( variable ) variable +#define __data16_array( variable, array ) variable array +#define __bss16( variable ) variable +#define __bss16_array( variable, array ) variable array +#define __text16( variable ) variable +#define __text16_array( variable,array ) variable array +#define __use_data16( variable ) variable +#define __use_text16( variable ) variable +#define __from_data16( pointer ) pointer +#define __from_text16( pointer ) pointer + +/* Real-mode data and code segments */ +static inline __attribute__ (( always_inline )) unsigned int _rm_cs ( void ) { + uint16_t cs; + __asm__ __volatile__ ( "movw %%cs, %w0" : "=r" ( cs ) ); + return cs; +} + +static inline __attribute__ (( always_inline )) unsigned int _rm_ds ( void ) { + uint16_t ds; + __asm__ __volatile__ ( "movw %%ds, %w0" : "=r" ( ds ) ); + return ds; +} + +#define rm_cs ( _rm_cs() ) +#define rm_ds ( _rm_ds() ) + +/* Copy to/from base memory */ + +static inline void copy_to_real_libkir ( unsigned int dest_seg, + unsigned int dest_off, + const void *src, size_t n ) { + unsigned int discard_D, discard_S, discard_c; + + __asm__ __volatile__ ( "pushw %%es\n\t" + "movw %3, %%es\n\t" + "rep movsb\n\t" + "popw %%es\n\t" + : "=D" ( discard_D ), "=S" ( discard_S ), + "=c" ( discard_c ) + : "r" ( dest_seg ), "D" ( dest_off ), + "S" ( src ), + "c" ( n ) + : "memory" ); +} + +static inline void copy_from_real_libkir ( void *dest, + unsigned int src_seg, + unsigned int src_off, + size_t n ) { + unsigned int discard_D, discard_S, discard_c; + + __asm__ __volatile__ ( "pushw %%ds\n\t" + "movw %4, %%ds\n\t" + "rep movsb\n\t" + "popw %%ds\n\t" + : "=D" ( discard_D ), "=S" ( discard_S ), + "=c" ( discard_c ) + : "D" ( dest ), + "r" ( src_seg ), "S" ( src_off ), + "c" ( n ) + : "memory" ); +} + +#define copy_to_real copy_to_real_libkir +#define copy_from_real copy_from_real_libkir + +/* + * Transfer individual values to/from base memory. There may well be + * a neater way to do this. We have two versions: one for constant + * offsets (where the mov instruction must be of the form "mov + * %es:123, %xx") and one for non-constant offsets (where the mov + * instruction must be of the form "mov %es:(%xx), %yx". If it's + * possible to incorporate both forms into one __asm__ instruction, I + * don't know how to do it. + * + * Ideally, the mov instruction should be "mov%z0"; the "%z0" is meant + * to expand to either "b", "w" or "l" depending on the size of + * operand 0. This would remove the (minor) ambiguity in the mov + * instruction. However, gcc on at least my system barfs with an + * "internal compiler error" when confronted with %z0. + * + */ + +#define put_real_kir_const_off( var, seg, off ) \ + __asm__ ( "movw %w1, %%es\n\t" \ + "mov %0, %%es:%c2\n\t" \ + "pushw %%ds\n\t" /* restore %es */ \ + "popw %%es\n\t" \ + : \ + : "r,r" ( var ), "rm,rm" ( seg ), "i,!r" ( off ) \ + ) + +#define put_real_kir_nonconst_off( var, seg, off ) \ + __asm__ ( "movw %w1, %%es\n\t" \ + "mov %0, %%es:(%2)\n\t" \ + "pushw %%ds\n\t" /* restore %es */ \ + "popw %%es\n\t" \ + : \ + : "r" ( var ), "rm" ( seg ), "r" ( off ) \ + ) + +#define put_real_kir( var, seg, off ) \ + do { \ + if ( __builtin_constant_p ( off ) ) \ + put_real_kir_const_off ( var, seg, off ); \ + else \ + put_real_kir_nonconst_off ( var, seg, off ); \ + } while ( 0 ) + +#define get_real_kir_const_off( var, seg, off ) \ + __asm__ ( "movw %w1, %%es\n\t" \ + "mov %%es:%c2, %0\n\t" \ + "pushw %%ds\n\t" /* restore %es */ \ + "popw %%es\n\t" \ + : "=r,r" ( var ) \ + : "rm,rm" ( seg ), "i,!r" ( off ) \ + ) + +#define get_real_kir_nonconst_off( var, seg, off ) \ + __asm__ ( "movw %w1, %%es\n\t" \ + "mov %%es:(%2), %0\n\t" \ + "pushw %%ds\n\t" /* restore %es */ \ + "popw %%es\n\t" \ + : "=r" ( var ) \ + : "rm" ( seg ), "r" ( off ) \ + ) + +#define get_real_kir( var, seg, off ) \ + do { \ + if ( __builtin_constant_p ( off ) ) \ + get_real_kir_const_off ( var, seg, off ); \ + else \ + get_real_kir_nonconst_off ( var, seg, off ); \ + } while ( 0 ) + +#define put_real put_real_kir +#define get_real get_real_kir + +/** + * A pointer to a user buffer + * + * This is actually a struct segoff, but encoded as a uint32_t to + * ensure that gcc passes it around efficiently. + */ +typedef uint32_t userptr_t; + +/** + * Copy data to user buffer + * + * @v buffer User buffer + * @v offset Offset within user buffer + * @v src Source + * @v len Length + */ +static inline __attribute__ (( always_inline )) void +copy_to_user ( userptr_t buffer, off_t offset, const void *src, size_t len ) { + copy_to_real ( ( buffer >> 16 ), ( ( buffer & 0xffff ) + offset ), + src, len ); +} + +/** + * Copy data from user buffer + * + * @v dest Destination + * @v buffer User buffer + * @v offset Offset within user buffer + * @v len Length + */ +static inline __attribute__ (( always_inline )) void +copy_from_user ( void *dest, userptr_t buffer, off_t offset, size_t len ) { + copy_from_real ( dest, ( buffer >> 16 ), + ( ( buffer & 0xffff ) + offset ), len ); +} + +/** + * Convert segment:offset address to user buffer + * + * @v segment Real-mode segment + * @v offset Real-mode offset + * @ret buffer User buffer + */ +static inline __attribute__ (( always_inline )) userptr_t +real_to_user ( unsigned int segment, unsigned int offset ) { + return ( ( segment << 16 ) | offset ); +} + +/** + * Convert virtual address to user buffer + * + * @v virtual Virtual address + * @ret buffer User buffer + * + * This constructs a user buffer from an ordinary pointer. Use it + * when you need to pass a pointer to an internal buffer to a function + * that expects a @c userptr_t. + */ +static inline __attribute__ (( always_inline )) userptr_t +virt_to_user ( void * virtual ) { + return real_to_user ( rm_ds, ( intptr_t ) virtual ); +} + +/* TEXT16_CODE: declare a fragment of code that resides in .text16 */ +#define TEXT16_CODE( asm_code_str ) \ + ".section \".text16\", \"ax\", @progbits\n\t" \ + ".code16\n\t" \ + ".arch i386\n\t" \ + asm_code_str "\n\t" \ + ".code16gcc\n\t" \ + ".previous\n\t" + +/* REAL_CODE: declare a fragment of code that executes in real mode */ +#define REAL_CODE( asm_code_str ) \ + ".code16\n\t" \ + asm_code_str "\n\t" \ + ".code16gcc\n\t" + +#endif /* ASSEMBLY */ + +#endif /* LIBKIR_H */ diff --git a/gpxe/src/arch/i386/include/librm.h b/gpxe/src/arch/i386/include/librm.h new file mode 100755 index 0000000..9eb2767 --- /dev/null +++ b/gpxe/src/arch/i386/include/librm.h @@ -0,0 +1,204 @@ +#ifndef LIBRM_H +#define LIBRM_H + +/* Segment selectors as used in our protected-mode GDTs. + * + * Don't change these unless you really know what you're doing. + */ + +#define VIRTUAL_CS 0x08 +#define VIRTUAL_DS 0x10 +#define PHYSICAL_CS 0x18 +#define PHYSICAL_DS 0x20 +#define REAL_CS 0x28 +#define REAL_DS 0x30 +#if 0 +#define LONG_CS 0x38 +#define LONG_DS 0x40 +#endif + +#ifndef ASSEMBLY + +#ifdef UACCESS_LIBRM +#define UACCESS_PREFIX_librm +#else +#define UACCESS_PREFIX_librm __librm_ +#endif + +/* Variables in librm.S */ +extern unsigned long virt_offset; + +/** + * Convert physical address to user pointer + * + * @v phys_addr Physical address + * @ret userptr User pointer + */ +static inline __always_inline userptr_t +UACCESS_INLINE ( librm, phys_to_user ) ( unsigned long phys_addr ) { + return ( phys_addr - virt_offset ); +} + +/** + * Convert user buffer to physical address + * + * @v userptr User pointer + * @v offset Offset from user pointer + * @ret phys_addr Physical address + */ +static inline __always_inline unsigned long +UACCESS_INLINE ( librm, user_to_phys ) ( userptr_t userptr, off_t offset ) { + return ( userptr + offset + virt_offset ); +} + +static inline __always_inline userptr_t +UACCESS_INLINE ( librm, virt_to_user ) ( volatile const void *addr ) { + return trivial_virt_to_user ( addr ); +} + +static inline __always_inline void * +UACCESS_INLINE ( librm, user_to_virt ) ( userptr_t userptr, off_t offset ) { + return trivial_user_to_virt ( userptr, offset ); +} + +static inline __always_inline userptr_t +UACCESS_INLINE ( librm, userptr_add ) ( userptr_t userptr, off_t offset ) { + return trivial_userptr_add ( userptr, offset ); +} + +static inline __always_inline void +UACCESS_INLINE ( librm, memcpy_user ) ( userptr_t dest, off_t dest_off, + userptr_t src, off_t src_off, + size_t len ) { + trivial_memcpy_user ( dest, dest_off, src, src_off, len ); +} + +static inline __always_inline void +UACCESS_INLINE ( librm, memmove_user ) ( userptr_t dest, off_t dest_off, + userptr_t src, off_t src_off, + size_t len ) { + trivial_memmove_user ( dest, dest_off, src, src_off, len ); +} + +static inline __always_inline void +UACCESS_INLINE ( librm, memset_user ) ( userptr_t buffer, off_t offset, + int c, size_t len ) { + trivial_memset_user ( buffer, offset, c, len ); +} + +static inline __always_inline size_t +UACCESS_INLINE ( librm, strlen_user ) ( userptr_t buffer, off_t offset ) { + return trivial_strlen_user ( buffer, offset ); +} + +static inline __always_inline off_t +UACCESS_INLINE ( librm, memchr_user ) ( userptr_t buffer, off_t offset, + int c, size_t len ) { + return trivial_memchr_user ( buffer, offset, c, len ); +} + + +/****************************************************************************** + * + * Access to variables in .data16 and .text16 + * + */ + +extern char *data16; +extern char *text16; + +#define __data16( variable ) \ + __attribute__ (( section ( ".data16" ) )) \ + _data16_ ## variable __asm__ ( #variable ) + +#define __data16_array( variable, array ) \ + __attribute__ (( section ( ".data16" ) )) \ + _data16_ ## variable array __asm__ ( #variable ) + +#define __bss16( variable ) \ + __attribute__ (( section ( ".bss16" ) )) \ + _data16_ ## variable __asm__ ( #variable ) + +#define __bss16_array( variable, array ) \ + __attribute__ (( section ( ".bss16" ) )) \ + _data16_ ## variable array __asm__ ( #variable ) + +#define __text16( variable ) \ + __attribute__ (( section ( ".text16.data" ) )) \ + _text16_ ## variable __asm__ ( #variable ) + +#define __text16_array( variable, array ) \ + __attribute__ (( section ( ".text16.data" ) )) \ + _text16_ ## variable array __asm__ ( #variable ) + +#define __use_data16( variable ) \ + ( * ( ( typeof ( _data16_ ## variable ) * ) \ + & ( data16 [ ( size_t ) & ( _data16_ ## variable ) ] ) ) ) + +#define __use_text16( variable ) \ + ( * ( ( typeof ( _text16_ ## variable ) * ) \ + & ( text16 [ ( size_t ) & ( _text16_ ## variable ) ] ) ) ) + +#define __from_data16( pointer ) \ + ( ( unsigned int ) \ + ( ( ( void * ) (pointer) ) - ( ( void * ) data16 ) ) ) + +#define __from_text16( pointer ) \ + ( ( unsigned int ) \ + ( ( ( void * ) (pointer) ) - ( ( void * ) text16 ) ) ) + +/* Variables in librm.S, present in the normal data segment */ +extern uint16_t rm_sp; +extern uint16_t rm_ss; +extern uint16_t __data16 ( rm_cs ); +#define rm_cs __use_data16 ( rm_cs ) +extern uint16_t __text16 ( rm_ds ); +#define rm_ds __use_text16 ( rm_ds ) + +/* Functions that librm expects to be able to link to. Included here + * so that the compiler will catch prototype mismatches. + */ +extern void gateA20_set ( void ); + +/** + * Convert segment:offset address to user buffer + * + * @v segment Real-mode segment + * @v offset Real-mode offset + * @ret buffer User buffer + */ +static inline __always_inline userptr_t +real_to_user ( unsigned int segment, unsigned int offset ) { + return ( phys_to_user ( ( segment << 4 ) + offset ) ); +} + +extern uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ); +extern void remove_user_from_rm_stack ( userptr_t data, size_t size ); + +/* TEXT16_CODE: declare a fragment of code that resides in .text16 */ +#define TEXT16_CODE( asm_code_str ) \ + ".section \".text16\", \"ax\", @progbits\n\t" \ + ".code16\n\t" \ + asm_code_str "\n\t" \ + ".code32\n\t" \ + ".previous\n\t" + +/* REAL_CODE: declare a fragment of code that executes in real mode */ +#define REAL_CODE( asm_code_str ) \ + "pushl $1f\n\t" \ + "call real_call\n\t" \ + "addl $4, %%esp\n\t" \ + TEXT16_CODE ( "\n1:\n\t" \ + asm_code_str \ + "\n\t" \ + "ret\n\t" ) + +/* PHYS_CODE: declare a fragment of code that executes in flat physical mode */ +#define PHYS_CODE( asm_code_str ) \ + "call _virt_to_phys\n\t" \ + asm_code_str \ + "call _phys_to_virt\n\t" + +#endif /* ASSEMBLY */ + +#endif /* LIBRM_H */ diff --git a/gpxe/src/arch/i386/include/limits.h b/gpxe/src/arch/i386/include/limits.h new file mode 100644 index 0000000..f13db26 --- /dev/null +++ b/gpxe/src/arch/i386/include/limits.h @@ -0,0 +1,59 @@ +#ifndef LIMITS_H +#define LIMITS_H 1 + +/* Number of bits in a `char' */ +#define CHAR_BIT 8 + +/* Minimum and maximum values a `signed char' can hold */ +#define SCHAR_MIN (-128) +#define SCHAR_MAX 127 + +/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */ +#define UCHAR_MAX 255 + +/* Minimum and maximum values a `char' can hold */ +#define CHAR_MIN SCHAR_MIN +#define CHAR_MAX SCHAR_MAX + +/* Minimum and maximum values a `signed short int' can hold */ +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 + +/* Maximum value an `unsigned short' can hold. (Minimum is 0.) */ +#define USHRT_MAX 65535 + + +/* Minimum and maximum values a `signed int' can hold */ +#define INT_MIN (-INT_MAX - 1) +#define INT_MAX 2147483647 + +/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */ +#define UINT_MAX 4294967295U + + +/* Minimum and maximum values a `signed int' can hold */ +#define INT_MAX 2147483647 +#define INT_MIN (-INT_MAX - 1) + + +/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */ +#define UINT_MAX 4294967295U + + +/* Minimum and maximum values a `signed long' can hold */ +#define LONG_MAX 2147483647 +#define LONG_MIN (-LONG_MAX - 1L) + +/* Maximum value an `unsigned long' can hold. (Minimum is 0.) */ +#define ULONG_MAX 4294967295UL + +/* Minimum and maximum values a `signed long long' can hold */ +#define LLONG_MAX 9223372036854775807LL +#define LLONG_MIN (-LONG_MAX - 1LL) + + +/* Maximum value an `unsigned long long' can hold. (Minimum is 0.) */ +#define ULLONG_MAX 18446744073709551615ULL + + +#endif /* LIMITS_H */ diff --git a/gpxe/src/arch/i386/include/memsizes.h b/gpxe/src/arch/i386/include/memsizes.h new file mode 100644 index 0000000..6222fd6 --- /dev/null +++ b/gpxe/src/arch/i386/include/memsizes.h @@ -0,0 +1,17 @@ +#ifndef _MEMSIZES_H +#define _MEMSIZES_H + +#include + +/** + * Get size of base memory from BIOS free base memory counter + * + * @ret basemem Base memory size, in kB + */ +static inline unsigned int basememsize ( void ) { + return get_fbms(); +} + +extern unsigned int extmemsize ( void ); + +#endif /* _MEMSIZES_H */ diff --git a/gpxe/src/arch/i386/include/multiboot.h b/gpxe/src/arch/i386/include/multiboot.h new file mode 100644 index 0000000..4ca7089 --- /dev/null +++ b/gpxe/src/arch/i386/include/multiboot.h @@ -0,0 +1,147 @@ +#ifndef _MULTIBOOT_H +#define _MULTIBOOT_H + +/** + * @file + * + * Multiboot operating systems + * + */ + +#include + +/** The magic number for the Multiboot header */ +#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 + +/** Boot modules must be page aligned */ +#define MB_FLAG_PGALIGN 0x00000001 + +/** Memory map must be provided */ +#define MB_FLAG_MEMMAP 0x00000002 + +/** Video mode information must be provided */ +#define MB_FLAG_VIDMODE 0x00000004 + +/** Image is a raw multiboot image (not ELF) */ +#define MB_FLAG_RAW 0x00010000 + +/** + * The magic number passed by a Multiboot-compliant boot loader + * + * Must be passed in register %eax when jumping to the Multiboot OS + * image. + */ +#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 + +/** Multiboot information structure mem_* fields are valid */ +#define MBI_FLAG_MEM 0x00000001 + +/** Multiboot information structure boot_device field is valid */ +#define MBI_FLAG_BOOTDEV 0x00000002 + +/** Multiboot information structure cmdline field is valid */ +#define MBI_FLAG_CMDLINE 0x00000004 + +/** Multiboot information structure module fields are valid */ +#define MBI_FLAG_MODS 0x00000008 + +/** Multiboot information structure a.out symbol table is valid */ +#define MBI_FLAG_AOUT 0x00000010 + +/** Multiboot information struture ELF section header table is valid */ +#define MBI_FLAG_ELF 0x00000020 + +/** Multiboot information structure memory map is valid */ +#define MBI_FLAG_MMAP 0x00000040 + +/** Multiboot information structure drive list is valid */ +#define MBI_FLAG_DRIVES 0x00000080 + +/** Multiboot information structure ROM configuration field is valid */ +#define MBI_FLAG_CFGTBL 0x00000100 + +/** Multiboot information structure boot loader name field is valid */ +#define MBI_FLAG_LOADER 0x00000200 + +/** Multiboot information structure APM table is valid */ +#define MBI_FLAG_APM 0x00000400 + +/** Multiboot information structure video information is valid */ +#define MBI_FLAG_VBE 0x00000800 + +/** A multiboot header */ +struct multiboot_header { + uint32_t magic; + uint32_t flags; + uint32_t checksum; + uint32_t header_addr; + uint32_t load_addr; + uint32_t load_end_addr; + uint32_t bss_end_addr; + uint32_t entry_addr; +} __attribute__ (( packed, may_alias )); + +/** A multiboot a.out symbol table */ +struct multiboot_aout_symbol_table { + uint32_t tabsize; + uint32_t strsize; + uint32_t addr; + uint32_t reserved; +} __attribute__ (( packed, may_alias )); + +/** A multiboot ELF section header table */ +struct multiboot_elf_section_header_table { + uint32_t num; + uint32_t size; + uint32_t addr; + uint32_t shndx; +} __attribute__ (( packed, may_alias )); + +/** A multiboot information structure */ +struct multiboot_info { + uint32_t flags; + uint32_t mem_lower; + uint32_t mem_upper; + uint32_t boot_device; + uint32_t cmdline; + uint32_t mods_count; + uint32_t mods_addr; + union { + struct multiboot_aout_symbol_table aout_syms; + struct multiboot_elf_section_header_table elf_sections; + } syms; + uint32_t mmap_length; + uint32_t mmap_addr; + uint32_t drives_length; + uint32_t drives_addr; + uint32_t config_table; + uint32_t boot_loader_name; + uint32_t apm_table; + uint32_t vbe_control_info; + uint32_t vbe_mode_info; + uint16_t vbe_mode; + uint16_t vbe_interface_seg; + uint16_t vbe_interface_off; + uint16_t vbe_interface_len; +} __attribute__ (( packed, may_alias )); + +/** A multiboot module structure */ +struct multiboot_module { + uint32_t mod_start; + uint32_t mod_end; + uint32_t string; + uint32_t reserved; +} __attribute__ (( packed, may_alias )); + +/** A multiboot memory map entry */ +struct multiboot_memory_map { + uint32_t size; + uint64_t base_addr; + uint64_t length; + uint32_t type; +} __attribute__ (( packed, may_alias )); + +/** Usable RAM */ +#define MBMEM_RAM 1 + +#endif /* _MULTIBOOT_H */ diff --git a/gpxe/src/arch/i386/include/pic8259.h b/gpxe/src/arch/i386/include/pic8259.h new file mode 100644 index 0000000..0c501a9 --- /dev/null +++ b/gpxe/src/arch/i386/include/pic8259.h @@ -0,0 +1,69 @@ +/* + * Basic support for controlling the 8259 Programmable Interrupt Controllers. + * + * Initially written by Michael Brown (mcb30). + */ + +#ifndef PIC8259_H +#define PIC8259_H + +/* For segoff_t */ +#include "realmode.h" + +#define IRQ_PIC_CUTOFF 8 + +/* 8259 register locations */ +#define PIC1_ICW1 0x20 +#define PIC1_OCW2 0x20 +#define PIC1_OCW3 0x20 +#define PIC1_ICR 0x20 +#define PIC1_IRR 0x20 +#define PIC1_ISR 0x20 +#define PIC1_ICW2 0x21 +#define PIC1_ICW3 0x21 +#define PIC1_ICW4 0x21 +#define PIC1_IMR 0x21 +#define PIC2_ICW1 0xa0 +#define PIC2_OCW2 0xa0 +#define PIC2_OCW3 0xa0 +#define PIC2_ICR 0xa0 +#define PIC2_IRR 0xa0 +#define PIC2_ISR 0xa0 +#define PIC2_ICW2 0xa1 +#define PIC2_ICW3 0xa1 +#define PIC2_ICW4 0xa1 +#define PIC2_IMR 0xa1 + +/* Register command values */ +#define OCW3_ID 0x08 +#define OCW3_READ_IRR 0x03 +#define OCW3_READ_ISR 0x02 +#define ICR_EOI_NON_SPECIFIC 0x20 +#define ICR_EOI_NOP 0x40 +#define ICR_EOI_SPECIFIC 0x60 +#define ICR_EOI_SET_PRIORITY 0xc0 + +/* Macros to enable/disable IRQs */ +#define IMR_REG(x) ( (x) < IRQ_PIC_CUTOFF ? PIC1_IMR : PIC2_IMR ) +#define IMR_BIT(x) ( 1 << ( (x) % IRQ_PIC_CUTOFF ) ) +#define irq_enabled(x) ( ( inb ( IMR_REG(x) ) & IMR_BIT(x) ) == 0 ) +#define enable_irq(x) outb ( inb( IMR_REG(x) ) & ~IMR_BIT(x), IMR_REG(x) ) +#define disable_irq(x) outb ( inb( IMR_REG(x) ) | IMR_BIT(x), IMR_REG(x) ) + +/* Macros for acknowledging IRQs */ +#define ICR_REG( irq ) ( (irq) < IRQ_PIC_CUTOFF ? PIC1_ICR : PIC2_ICR ) +#define ICR_VALUE( irq ) ( (irq) % IRQ_PIC_CUTOFF ) +#define CHAINED_IRQ 2 + +/* Utility macros to convert IRQ numbers to INT numbers and INT vectors */ +#define IRQ_INT( irq ) ( ( ( (irq) - IRQ_PIC_CUTOFF ) ^ 0x70 ) & 0x7f ) + +/* Other constants */ +#define IRQ_MAX 15 +#define IRQ_NONE -1U + +/* Function prototypes + */ +void send_eoi ( unsigned int irq ); + +#endif /* PIC8259_H */ diff --git a/gpxe/src/arch/i386/include/pnpbios.h b/gpxe/src/arch/i386/include/pnpbios.h new file mode 100644 index 0000000..ab31c69 --- /dev/null +++ b/gpxe/src/arch/i386/include/pnpbios.h @@ -0,0 +1,15 @@ +#ifndef _PNPBIOS_H +#define _PNPBIOS_H + +/** @file + * + * PnP BIOS + * + */ + +/* BIOS segment address */ +#define BIOS_SEG 0xf000 + +extern int find_pnp_bios ( void ); + +#endif /* _PNPBIOS_H */ diff --git a/gpxe/src/arch/i386/include/pxe.h b/gpxe/src/arch/i386/include/pxe.h new file mode 100644 index 0000000..6d332ac --- /dev/null +++ b/gpxe/src/arch/i386/include/pxe.h @@ -0,0 +1,151 @@ +#ifndef PXE_H +#define PXE_H + +#include "pxe_types.h" +#include "pxe_api.h" +#include + +/* Parameter block for pxenv_unknown() */ +struct s_PXENV_UNKNOWN { + PXENV_STATUS_t Status; /**< PXE status code */ +} PACKED; + +typedef struct s_PXENV_UNKNOWN PXENV_UNKNOWN_t; + +/* Union used for PXE API calls; we don't know the type of the + * structure until we interpret the opcode. Also, Status is available + * in the same location for any opcode, and it's convenient to have + * non-specific access to it. + */ +union u_PXENV_ANY { + /* Make it easy to read status for any operation */ + PXENV_STATUS_t Status; + struct s_PXENV_UNKNOWN unknown; + struct s_PXENV_UNLOAD_STACK unload_stack; + struct s_PXENV_GET_CACHED_INFO get_cached_info; + struct s_PXENV_TFTP_READ_FILE restart_tftp; + struct s_PXENV_START_UNDI start_undi; + struct s_PXENV_STOP_UNDI stop_undi; + struct s_PXENV_START_BASE start_base; + struct s_PXENV_STOP_BASE stop_base; + struct s_PXENV_TFTP_OPEN tftp_open; + struct s_PXENV_TFTP_CLOSE tftp_close; + struct s_PXENV_TFTP_READ tftp_read; + struct s_PXENV_TFTP_READ_FILE tftp_read_file; + struct s_PXENV_TFTP_GET_FSIZE tftp_get_fsize; + struct s_PXENV_UDP_OPEN udp_open; + struct s_PXENV_UDP_CLOSE udp_close; + struct s_PXENV_UDP_WRITE udp_write; + struct s_PXENV_UDP_READ udp_read; + struct s_PXENV_UNDI_STARTUP undi_startup; + struct s_PXENV_UNDI_CLEANUP undi_cleanup; + struct s_PXENV_UNDI_INITIALIZE undi_initialize; + struct s_PXENV_UNDI_RESET undi_reset_adapter; + struct s_PXENV_UNDI_SHUTDOWN undi_shutdown; + struct s_PXENV_UNDI_OPEN undi_open; + struct s_PXENV_UNDI_CLOSE undi_close; + struct s_PXENV_UNDI_TRANSMIT undi_transmit; + struct s_PXENV_UNDI_SET_MCAST_ADDRESS undi_set_mcast_address; + struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_station_address; + struct s_PXENV_UNDI_SET_PACKET_FILTER undi_set_packet_filter; + struct s_PXENV_UNDI_GET_INFORMATION undi_get_information; + struct s_PXENV_UNDI_GET_STATISTICS undi_get_statistics; + struct s_PXENV_UNDI_CLEAR_STATISTICS undi_clear_statistics; + struct s_PXENV_UNDI_INITIATE_DIAGS undi_initiate_diags; + struct s_PXENV_UNDI_FORCE_INTERRUPT undi_force_interrupt; + struct s_PXENV_UNDI_GET_MCAST_ADDRESS undi_get_mcast_address; + struct s_PXENV_UNDI_GET_NIC_TYPE undi_get_nic_type; + struct s_PXENV_UNDI_GET_IFACE_INFO undi_get_iface_info; + struct s_PXENV_UNDI_GET_STATE undi_get_state; + struct s_PXENV_UNDI_ISR undi_isr; + struct s_PXENV_FILE_OPEN file_open; + struct s_PXENV_FILE_CLOSE file_close; + struct s_PXENV_FILE_SELECT file_select; + struct s_PXENV_FILE_READ file_read; + struct s_PXENV_GET_FILE_SIZE get_file_size; + struct s_PXENV_FILE_EXEC file_exec; + struct s_PXENV_FILE_API_CHECK file_api_check; +}; + +typedef union u_PXENV_ANY PXENV_ANY_t; + +/** An UNDI expansion ROM header */ +struct undi_rom_header { + /** Signature + * + * Must be equal to @c ROM_SIGNATURE + */ + UINT16_t Signature; + /** ROM length in 512-byte blocks */ + UINT8_t ROMLength; + /** Unused */ + UINT8_t unused[0x13]; + /** Offset of the PXE ROM ID structure */ + UINT16_t PXEROMID; + /** Offset of the PCI ROM structure */ + UINT16_t PCIRHeader; +} PACKED; + +/** Signature for an expansion ROM */ +#define ROM_SIGNATURE 0xaa55 + +/** An UNDI ROM ID structure */ +struct undi_rom_id { + /** Signature + * + * Must be equal to @c UNDI_ROM_ID_SIGNATURE + */ + UINT32_t Signature; + /** Length of structure */ + UINT8_t StructLength; + /** Checksum */ + UINT8_t StructCksum; + /** Structure revision + * + * Must be zero. + */ + UINT8_t StructRev; + /** UNDI revision + * + * Version 2.1.0 is encoded as the byte sequence 0x00, 0x01, 0x02. + */ + UINT8_t UNDIRev[3]; + /** Offset to UNDI loader */ + UINT16_t UNDILoader; + /** Minimum required stack segment size */ + UINT16_t StackSize; + /** Minimum required data segment size */ + UINT16_t DataSize; + /** Minimum required code segment size */ + UINT16_t CodeSize; +} PACKED; + +/** Signature for an UNDI ROM ID structure */ +#define UNDI_ROM_ID_SIGNATURE \ + ( ( 'U' << 0 ) + ( 'N' << 8 ) + ( 'D' << 16 ) + ( 'I' << 24 ) ) + +/** A PCI expansion header */ +struct pcir_header { + /** Signature + * + * Must be equal to @c PCIR_SIGNATURE + */ + uint32_t signature; + /** PCI vendor ID */ + uint16_t vendor_id; + /** PCI device ID */ + uint16_t device_id; +} PACKED; + +/** Signature for an UNDI ROM ID structure */ +#define PCIR_SIGNATURE \ + ( ( 'P' << 0 ) + ( 'C' << 8 ) + ( 'I' << 16 ) + ( 'R' << 24 ) ) + + +extern struct net_device *pxe_netdev; + +extern void pxe_set_netdev ( struct net_device *netdev ); + +extern void pxe_set_cached_filename ( const unsigned char *filename ); + +#endif /* PXE_H */ diff --git a/gpxe/src/arch/i386/include/pxe_api.h b/gpxe/src/arch/i386/include/pxe_api.h new file mode 100644 index 0000000..b3d4bca --- /dev/null +++ b/gpxe/src/arch/i386/include/pxe_api.h @@ -0,0 +1,1841 @@ +#ifndef PXE_API_H +#define PXE_API_H + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** @file + * + * Preboot eXecution Environment (PXE) API + * + */ + +#include "pxe_types.h" + +/** @addtogroup pxe Preboot eXecution Environment (PXE) API + * @{ + */ + +/** @defgroup pxe_api_call PXE entry points + * + * PXE entry points and calling conventions + * + * @{ + */ + +/** The PXENV+ structure */ +struct s_PXENV { + /** Signature + * + * Contains the bytes 'P', 'X', 'E', 'N', 'V', '+'. + */ + UINT8_t Signature[6]; + /** PXE API version + * + * MSB is major version number, LSB is minor version number. + * If the API version number is 0x0201 or greater, the !PXE + * structure pointed to by #PXEPtr should be used instead of + * this data structure. + */ + UINT16_t Version; + UINT8_t Length; /**< Length of this structure */ + /** Checksum + * + * The byte checksum of this structure (using the length in + * #Length) must be zero. + */ + UINT8_t Checksum; + SEGOFF16_t RMEntry; /**< Real-mode PXENV+ entry point */ + /** Protected-mode PXENV+ entry point offset + * + * PXE 2.1 deprecates this entry point. For protected-mode + * API calls, use the !PXE structure pointed to by #PXEPtr + * instead. + */ + UINT32_t PMOffset; + /** Protected-mode PXENV+ entry point segment selector + * + * PXE 2.1 deprecates this entry point. For protected-mode + * API calls, use the !PXE structure pointed to by #PXEPtr + * instead. + */ + SEGSEL_t PMSelector; + SEGSEL_t StackSeg; /**< Stack segment selector */ + UINT16_t StackSize; /**< Stack segment size */ + SEGSEL_t BC_CodeSeg; /**< Base-code code segment selector */ + UINT16_t BC_CodeSize; /**< Base-code code segment size */ + SEGSEL_t BC_DataSeg; /**< Base-code data segment selector */ + UINT16_t BC_DataSize; /**< Base-code data segment size */ + SEGSEL_t UNDIDataSeg; /**< UNDI data segment selector */ + UINT16_t UNDIDataSize; /**< UNDI data segment size */ + SEGSEL_t UNDICodeSeg; /**< UNDI code segment selector */ + UINT16_t UNDICodeSize; /**< UNDI code segment size */ + /** Address of the !PXE structure + * + * This field is present only if #Version is 0x0201 or + * greater. If present, it points to a struct s_PXE. + */ + SEGOFF16_t PXEPtr; +} PACKED; + +typedef struct s_PXENV PXENV_t; + +/** The !PXE structure */ +struct s_PXE { + /** Signature + * + * Contains the bytes '!', 'P', 'X', 'E'. + */ + UINT8_t Signature[4]; + UINT8_t StructLength; /**< Length of this structure */ + /** Checksum + * + * The byte checksum of this structure (using the length in + * #StructLength) must be zero. + */ + UINT8_t StructCksum; + /** Revision of this structure + * + * For PXE version 2.1, this field must be zero. + */ + UINT8_t StructRev; + UINT8_t reserved_1; /**< Must be zero */ + /** Address of the UNDI ROM ID structure + * + * This is a pointer to a struct s_UNDI_ROM_ID. + */ + SEGOFF16_t UNDIROMID; + /** Address of the Base Code ROM ID structure + * + * This is a pointer to a struct s_BC_ROM_ID. + */ + SEGOFF16_t BaseROMID; + /** 16-bit !PXE entry point + * + * This is the entry point for either real mode, or protected + * mode with a 16-bit stack segment. + */ + SEGOFF16_t EntryPointSP; + /** 32-bit !PXE entry point + * + * This is the entry point for protected mode with a 32-bit + * stack segment. + */ + SEGOFF16_t EntryPointESP; + /** Status call-out function + * + * @v 0 (if in a time-out loop) + * @v n Number of a received TFTP packet + * @ret 0 Continue operation + * @ret 1 Cancel operation + * + * This function will be called whenever the PXE stack is in + * protected mode, is waiting for an event (e.g. a DHCP reply) + * and wishes to allow the user to cancel the operation. + * Parameters are passed in register %ax; the return value + * must also be placed in register %ax. All other registers + * and flags @b must be preserved. + * + * In real mode, an internal function (that checks for a + * keypress) will be used. + * + * If this field is set to -1, no status call-out function + * will be used and consequently the user will not be allowed + * to interrupt operations. + * + * @note The PXE specification version 2.1 defines the + * StatusCallout field, mentions it 11 times, but nowhere + * defines what it actually does or how it gets called. + * Fortunately, the WfM specification version 1.1a deigns to + * inform us of such petty details. + */ + SEGOFF16_t StatusCallout; + UINT8_t reserved_2; /**< Must be zero */ + /** Number of segment descriptors + * + * If this number is greater than 7, the remaining descriptors + * follow immediately after #BC_CodeWrite. + */ + UINT8_t SegDescCnt; + /** First protected-mode selector + * + * This is the segment selector value for the first segment + * assigned to PXE. Protected-mode selectors must be + * consecutive, according to the PXE 2.1 specification, though + * no reason is given. Each #SEGDESC_t includes a field for + * the segment selector, so this information is entirely + * redundant. + */ + SEGSEL_t FirstSelector; + /** Stack segment descriptor */ + SEGDESC_t Stack; + /** UNDI data segment descriptor */ + SEGDESC_t UNDIData; + /** UNDI code segment descriptor */ + SEGDESC_t UNDICode; + /** UNDI writable code segment descriptor */ + SEGDESC_t UNDICodeWrite; + /** Base-code data segment descriptor */ + SEGDESC_t BC_Data; + /** Base-code code segment descriptor */ + SEGDESC_t BC_Code; + /** Base-code writable code segment descriptor */ + SEGDESC_t BC_CodeWrite; +} PACKED; + +typedef struct s_PXE PXE_t; + +/** @} */ /* pxe_api_call */ + +/** @defgroup pxe_preboot_api PXE Preboot API + * + * General high-level functions: #PXENV_UNLOAD_STACK, #PXENV_START_UNDI etc. + * + * @{ + */ + +/** @defgroup pxenv_unload_stack PXENV_UNLOAD_STACK + * + * UNLOAD BASE CODE STACK + * + * @{ + */ + +/** PXE API function code for pxenv_unload_stack() */ +#define PXENV_UNLOAD_STACK 0x0070 + +/** Parameter block for pxenv_unload_stack() */ +struct s_PXENV_UNLOAD_STACK { + PXENV_STATUS_t Status; /**< PXE status code */ + UINT8_t reserved[10]; /**< Must be zero */ +} PACKED; + +typedef struct s_PXENV_UNLOAD_STACK PXENV_UNLOAD_STACK_t; + +extern PXENV_EXIT_t pxenv_unload_stack ( struct s_PXENV_UNLOAD_STACK + *unload_stack ); + +/** @} */ /* pxenv_unload_stack */ + +/** @defgroup pxenv_get_cached_info PXENV_GET_CACHED_INFO + * + * GET CACHED INFO + * + * @{ + */ + +/** PXE API function code for pxenv_get_cached_info() */ +#define PXENV_GET_CACHED_INFO 0x0071 + +/** The client's DHCPDISCOVER packet */ +#define PXENV_PACKET_TYPE_DHCP_DISCOVER 1 + +/** The DHCP server's DHCPACK packet */ +#define PXENV_PACKET_TYPE_DHCP_ACK 2 + +/** The Boot Server's Discover Reply packet + * + * This packet contains DHCP option 60 set to "PXEClient", a valid + * boot file name, and may or may not contain MTFTP options. + */ +#define PXENV_PACKET_TYPE_CACHED_REPLY 3 + +/** Parameter block for pxenv_get_cached_info() */ +struct s_PXENV_GET_CACHED_INFO { + PXENV_STATUS_t Status; /**< PXE status code */ + /** Packet type. + * + * Valid values are #PXENV_PACKET_TYPE_DHCP_DISCOVER, + * #PXENV_PACKET_TYPE_DHCP_ACK or #PXENV_PACKET_TYPE_CACHED_REPLY + */ + UINT16_t PacketType; + UINT16_t BufferSize; /**< Buffer size */ + SEGOFF16_t Buffer; /**< Buffer address */ + UINT16_t BufferLimit; /**< Maximum buffer size */ +} PACKED; + +typedef struct s_PXENV_GET_CACHED_INFO PXENV_GET_CACHED_INFO_t; + +#define BOOTP_REQ 1 /**< A BOOTP request packet */ +#define BOOTP_REP 2 /**< A BOOTP reply packet */ + +/** DHCP broadcast flag + * + * Request a broadcast response (DHCPOFFER or DHCPACK) from the DHCP + * server. + */ +#define BOOTP_BCAST 0x8000 + +#define VM_RFC1048 0x63825363L /**< DHCP magic cookie */ + +/** Maximum length of DHCP options */ +#define BOOTP_DHCPVEND 1024 + +/** Format of buffer filled in by pxenv_get_cached_info() + * + * This somewhat convoluted data structure simply describes the layout + * of a DHCP packet. Refer to RFC2131 section 2 for a full + * description. + */ +struct bootph { + /** Message opcode. + * + * Valid values are #BOOTP_REQ and #BOOTP_REP. + */ + UINT8_t opcode; + /** NIC hardware type. + * + * Valid values are as for s_PXENV_UNDI_GET_INFORMATION::HwType. + */ + UINT8_t Hardware; + UINT8_t Hardlen; /**< MAC address length */ + /** Gateway hops + * + * Zero in packets sent by the client. May be non-zero in + * replies from the DHCP server, if the reply comes via a DHCP + * relay agent. + */ + UINT8_t Gatehops; + UINT32_t ident; /**< DHCP transaction id (xid) */ + /** Elapsed time + * + * Number of seconds since the client began the DHCP + * transaction. + */ + UINT16_t seconds; + /** Flags + * + * This is the bitwise-OR of any of the following values: + * #BOOTP_BCAST. + */ + UINT16_t Flags; + /** Client IP address + * + * Set only if the client already has an IP address. + */ + IP4_t cip; + /** Your IP address + * + * This is the IP address that the server assigns to the + * client. + */ + IP4_t yip; + /** Server IP address + * + * This is the IP address of the BOOTP/DHCP server. + */ + IP4_t sip; + /** Gateway IP address + * + * This is the IP address of the BOOTP/DHCP relay agent, if + * any. It is @b not (necessarily) the address of the default + * gateway for routing purposes. + */ + IP4_t gip; + MAC_ADDR_t CAddr; /**< Client MAC address */ + UINT8_t Sname[64]; /**< Server host name */ + UINT8_t bootfile[128]; /**< Boot file name */ + /** DHCP options + * + * Don't ask. Just laugh. Then burn a copy of the PXE + * specification and send Intel an e-mail asking them if + * they've figured out what a "union" does in C yet. + */ + union bootph_vendor { + UINT8_t d[BOOTP_DHCPVEND]; /**< DHCP options */ + /** DHCP options */ + struct bootph_vendor_v { + /** DHCP magic cookie + * + * Should have the value #VM_RFC1048. + */ + UINT8_t magic[4]; + UINT32_t flags; /**< BOOTP flags/opcodes */ + /** "End of BOOTP vendor extensions" + * + * Abandon hope, all ye who consider the + * purpose of this field. + */ + UINT8_t pad[56]; + } v; + } vendor; +} PACKED; + +typedef struct bootph BOOTPLAYER_t; + +extern PXENV_EXIT_t pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO + *get_cached_info ); + +/** @} */ /* pxenv_get_cached_info */ + +/** @defgroup pxenv_restart_tftp PXENV_RESTART_TFTP + * + * RESTART TFTP + * + * @{ + */ + +/** PXE API function code for pxenv_restart_tftp() */ +#define PXENV_RESTART_TFTP 0x0073 + +/** Parameter block for pxenv_restart_tftp() */ +struct s_PXENV_TFTP_READ_FILE; + +typedef struct s_PXENV_RESTART_TFTP PXENV_RESTART_TFTP_t; + +extern PXENV_EXIT_t pxenv_restart_tftp ( struct s_PXENV_TFTP_READ_FILE + *restart_tftp ); + +/** @} */ /* pxenv_restart_tftp */ + +/** @defgroup pxenv_start_undi PXENV_START_UNDI + * + * START UNDI + * + * @{ + */ + +/** PXE API function code for pxenv_start_undi() */ +#define PXENV_START_UNDI 0x0000 + +/** Parameter block for pxenv_start_undi() */ +struct s_PXENV_START_UNDI { + PXENV_STATUS_t Status; /**< PXE status code */ + /** %ax register as passed to the Option ROM initialisation routine. + * + * For a PCI device, this should contain the bus:dev:fn value + * that uniquely identifies the PCI device in the system. For + * a non-PCI device, this field is not defined. + */ + UINT16_t AX; + /** %bx register as passed to the Option ROM initialisation routine. + * + * For an ISAPnP device, this should contain the Card Select + * Number assigned to the ISAPnP card. For non-ISAPnP + * devices, this should contain 0xffff. + */ + UINT16_t BX; + /** %dx register as passed to the Option ROM initialisation routine. + * + * For an ISAPnP device, this should contain the ISAPnP Read + * Port address as currently set in all ISAPnP cards. If + * there are no ISAPnP cards, this should contain 0xffff. (If + * this is a non-ISAPnP device, but there are ISAPnP cards in + * the system, this value is not well defined.) + */ + UINT16_t DX; + /** %di register as passed to the Option ROM initialisation routine. + * + * This contains the #OFF16_t portion of a struct #s_SEGOFF16 + * that points to the System BIOS Plug and Play Installation + * Check Structure. (Refer to section 4.4 of the Plug and + * Play BIOS specification for a description of this + * structure.) + * + * @note The PXE specification defines the type of this field + * as #UINT16_t. For x86, #OFF16_t and #UINT16_t are + * equivalent anyway; for other architectures #OFF16_t makes + * more sense. + */ + OFF16_t DI; + /** %es register as passed to the Option ROM initialisation routine. + * + * This contains the #SEGSEL_t portion of a struct #s_SEGOFF16 + * that points to the System BIOS Plug and Play Installation + * Check Structure. (Refer to section 4.4 of the Plug and + * Play BIOS specification for a description of this + * structure.) + * + * @note The PXE specification defines the type of this field + * as #UINT16_t. For x86, #SEGSEL_t and #UINT16_t are + * equivalent anyway; for other architectures #SEGSEL_t makes + * more sense. + */ + SEGSEL_t ES; +} PACKED; + +typedef struct s_PXENV_START_UNDI PXENV_START_UNDI_t; + +extern PXENV_EXIT_t pxenv_start_undi ( struct s_PXENV_START_UNDI *start_undi ); + +/** @} */ /* pxenv_start_undi */ + +/** @defgroup pxenv_stop_undi PXENV_STOP_UNDI + * + * STOP UNDI + * + * @{ + */ + +/** PXE API function code for pxenv_stop_undi() */ +#define PXENV_STOP_UNDI 0x0015 + +/** Parameter block for pxenv_stop_undi() */ +struct s_PXENV_STOP_UNDI { + PXENV_STATUS_t Status; /**< PXE status code */ +} PACKED; + +typedef struct s_PXENV_STOP_UNDI PXENV_STOP_UNDI_t; + +extern PXENV_EXIT_t pxenv_stop_undi ( struct s_PXENV_STOP_UNDI *stop_undi ); + +/** @} */ /* pxenv_stop_undi */ + +/** @defgroup pxenv_start_base PXENV_START_BASE + * + * START BASE + * + * @{ + */ + +/** PXE API function code for pxenv_start_base() */ +#define PXENV_START_BASE 0x0075 + +/** Parameter block for pxenv_start_base() */ +struct s_PXENV_START_BASE { + PXENV_STATUS_t Status; /**< PXE status code */ +} PACKED; + +typedef struct s_PXENV_START_BASE PXENV_START_BASE_t; + +extern PXENV_EXIT_t pxenv_start_base ( struct s_PXENV_START_BASE *start_base ); + +/** @} */ /* pxenv_start_base */ + +/** @defgroup pxenv_stop_base PXENV_STOP_BASE + * + * STOP BASE + * + * @{ + */ + +/** PXE API function code for pxenv_stop_base() */ +#define PXENV_STOP_BASE 0x0076 + +/** Parameter block for pxenv_stop_base() */ +struct s_PXENV_STOP_BASE { + PXENV_STATUS_t Status; /**< PXE status code */ +} PACKED; + +typedef struct s_PXENV_STOP_BASE PXENV_STOP_BASE_t; + +extern PXENV_EXIT_t pxenv_stop_base ( struct s_PXENV_STOP_BASE *stop_base ); + +/** @} */ /* pxenv_stop_base */ + +/** @} */ /* pxe_preboot_api */ + +/** @defgroup pxe_tftp_api PXE TFTP API + * + * Download files via TFTP or MTFTP + * + * @{ + */ + +/** @defgroup pxenv_tftp_open PXENV_TFTP_OPEN + * + * TFTP OPEN + * + * @{ + */ + +/** PXE API function code for pxenv_tftp_open() */ +#define PXENV_TFTP_OPEN 0x0020 + +/** Parameter block for pxenv_tftp_open() */ +struct s_PXENV_TFTP_OPEN { + PXENV_STATUS_t Status; /**< PXE status code */ + IP4_t ServerIPAddress; /**< TFTP server IP address */ + IP4_t GatewayIPAddress; /**< Relay agent IP address */ + UINT8_t FileName[128]; /**< File name */ + UDP_PORT_t TFTPPort; /**< TFTP server UDP port */ + /** Requested size of TFTP packets + * + * This is the TFTP "blksize" option. This must be at least + * 512, since servers that do not support TFTP options cannot + * negotiate blocksizes smaller than this. + */ + UINT16_t PacketSize; +} PACKED; + +typedef struct s_PXENV_TFTP_OPEN PXENV_TFTP_OPEN_t; + +extern PXENV_EXIT_t pxenv_tftp_open ( struct s_PXENV_TFTP_OPEN *tftp_open ); + +/** @} */ /* pxenv_tftp_open */ + +/** @defgroup pxenv_tftp_close PXENV_TFTP_CLOSE + * + * TFTP CLOSE + * + * @{ + */ + +/** PXE API function code for pxenv_tftp_close() */ +#define PXENV_TFTP_CLOSE 0x0021 + +/** Parameter block for pxenv_tftp_close() */ +struct s_PXENV_TFTP_CLOSE { + PXENV_STATUS_t Status; /**< PXE status code */ +} PACKED; + +typedef struct s_PXENV_TFTP_CLOSE PXENV_TFTP_CLOSE_t; + +extern PXENV_EXIT_t pxenv_tftp_close ( struct s_PXENV_TFTP_CLOSE *tftp_close ); + +/** @} */ /* pxenv_tftp_close */ + +/** @defgroup pxenv_tftp_read PXENV_TFTP_READ + * + * TFTP READ + * + * @{ + */ + +/** PXE API function code for pxenv_tftp_read() */ +#define PXENV_TFTP_READ 0x0022 + +/** Parameter block for pxenv_tftp_read() */ +struct s_PXENV_TFTP_READ { + PXENV_STATUS_t Status; /**< PXE status code */ + UINT16_t PacketNumber; /**< TFTP packet number */ + UINT16_t BufferSize; /**< Size of data buffer */ + SEGOFF16_t Buffer; /**< Address of data buffer */ +} PACKED; + +typedef struct s_PXENV_TFTP_READ PXENV_TFTP_READ_t; + +extern PXENV_EXIT_t pxenv_tftp_read ( struct s_PXENV_TFTP_READ *tftp_read ); + +/** @} */ /* pxenv_tftp_read */ + +/** @defgroup pxenv_tftp_read_file PXENV_TFTP_READ_FILE + * + * TFTP/MTFTP READ FILE + * + * @{ + */ + +/** PXE API function code for pxenv_tftp_read_file() */ +#define PXENV_TFTP_READ_FILE 0x0023 + +/** Parameter block for pxenv_tftp_read_file() */ +struct s_PXENV_TFTP_READ_FILE { + PXENV_STATUS_t Status; /**< PXE status code */ + UINT8_t FileName[128]; /**< File name */ + UINT32_t BufferSize; /**< Size of data buffer */ + ADDR32_t Buffer; /**< Address of data buffer */ + IP4_t ServerIPAddress; /**< TFTP server IP address */ + IP4_t GatewayIPAddress; /**< Relay agent IP address */ + /** File multicast IP address */ + IP4_t McastIPAddress; + /** Client multicast listening port */ + UDP_PORT_t TFTPClntPort; + /** Server multicast listening port */ + UDP_PORT_t TFTPSrvPort; + /** TFTP open timeout. + * + * This is the timeout for receiving the first DATA or ACK + * packets during the MTFTP Listen phase. + */ + UINT16_t TFTPOpenTimeOut; + /** TFTP reopen timeout. + * + * This is the timeout for receiving an ACK packet while in + * the MTFTP Listen phase (when at least one ACK packet has + * already been seen). + */ + UINT16_t TFTPReopenDelay; +} PACKED; + +typedef struct s_PXENV_TFTP_READ_FILE PXENV_TFTP_READ_FILE_t; + +extern PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE + *tftp_read_file ); + +/** @} */ /* pxenv_tftp_read_file */ + +/** @defgroup pxenv_tftp_get_fsize PXENV_TFTP_GET_FSIZE + * + * TFTP GET FILE SIZE + * + * @{ + */ + +/** PXE API function code for pxenv_tftp_get_fsize() */ +#define PXENV_TFTP_GET_FSIZE 0x0025 + +/** Parameter block for pxenv_tftp_get_fsize() */ +struct s_PXENV_TFTP_GET_FSIZE { + PXENV_STATUS_t Status; /**< PXE status code */ + IP4_t ServerIPAddress; /**< TFTP server IP address */ + IP4_t GatewayIPAddress; /**< Relay agent IP address */ + UINT8_t FileName[128]; /**< File name */ + UINT32_t FileSize; /**< Size of the file */ +} PACKED; + +typedef struct s_PXENV_TFTP_GET_FSIZE PXENV_TFTP_GET_FSIZE_t; + +extern PXENV_EXIT_t pxenv_tftp_get_fsize ( struct s_PXENV_TFTP_GET_FSIZE + *get_fsize ); + +/** @} */ /* pxenv_tftp_get_fsize */ + +/** @} */ /* pxe_tftp_api */ + +/** @defgroup pxe_udp_api PXE UDP API + * + * Transmit and receive UDP packets + * + * @{ + */ + +/** @defgroup pxenv_udp_open PXENV_UDP_OPEN + * + * UDP OPEN + * + * @{ + */ + +/** PXE API function code for pxenv_udp_open() */ +#define PXENV_UDP_OPEN 0x0030 + +/** Parameter block for pxenv_udp_open() */ +struct s_PXENV_UDP_OPEN { + PXENV_STATUS_t Status; /**< PXE status code */ + IP4_t src_ip; /**< IP address of this station */ +} PACKED; + +typedef struct s_PXENV_UDP_OPEN PXENV_UDP_OPEN_t; + +extern PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *udp_open ); + +/** @} */ /* pxenv_udp_open */ + +/** @defgroup pxenv_udp_close PXENV_UDP_CLOSE + * + * UDP CLOSE + * + * @{ + */ + +/** PXE API function code for pxenv_udp_close() */ +#define PXENV_UDP_CLOSE 0x0031 + +/** Parameter block for pxenv_udp_close() */ +struct s_PXENV_UDP_CLOSE { + PXENV_STATUS_t Status; /**< PXE status code */ +} PACKED; + +typedef struct s_PXENV_UDP_CLOSE PXENV_UDP_CLOSE_t; + +extern PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *udp_close ); + +/** @} */ /* pxenv_udp_close */ + +/** @defgroup pxenv_udp_write PXENV_UDP_WRITE + * + * UDP WRITE + * + * @{ + */ + +/** PXE API function code for pxenv_udp_write() */ +#define PXENV_UDP_WRITE 0x0033 + +/** Parameter block for pxenv_udp_write() */ +struct s_PXENV_UDP_WRITE { + PXENV_STATUS_t Status; /**< PXE status code */ + IP4_t ip; /**< Destination IP address */ + IP4_t gw; /**< Relay agent IP address */ + UDP_PORT_t src_port; /**< Source UDP port */ + UDP_PORT_t dst_port; /**< Destination UDP port */ + UINT16_t buffer_size; /**< UDP payload buffer size */ + SEGOFF16_t buffer; /**< UDP payload buffer address */ +} PACKED; + +typedef struct s_PXENV_UDP_WRITE PXENV_UDP_WRITE_t; + +extern PXENV_EXIT_t pxenv_udp_write ( struct s_PXENV_UDP_WRITE *udp_write ); + +/** @} */ /* pxenv_udp_write */ + +/** @defgroup pxenv_udp_read PXENV_UDP_READ + * + * UDP READ + * + * @{ + */ + +/** PXE API function code for pxenv_udp_read() */ +#define PXENV_UDP_READ 0x0032 + +/** Parameter block for pxenv_udp_read() */ +struct s_PXENV_UDP_READ { + PXENV_STATUS_t Status; /**< PXE status code */ + IP4_t src_ip; /**< Source IP address */ + IP4_t dest_ip; /**< Destination IP address */ + UDP_PORT_t s_port; /**< Source UDP port */ + UDP_PORT_t d_port; /**< Destination UDP port */ + UINT16_t buffer_size; /**< UDP payload buffer size */ + SEGOFF16_t buffer; /**< UDP payload buffer address */ +} PACKED; + +typedef struct s_PXENV_UDP_READ PXENV_UDP_READ_t; + +extern PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *udp_read ); + +/** @} */ /* pxenv_udp_read */ + +/** @} */ /* pxe_udp_api */ + +/** @defgroup pxe_undi_api PXE UNDI API + * + * Direct control of the network interface card + * + * @{ + */ + +/** @defgroup pxenv_undi_startup PXENV_UNDI_STARTUP + * + * UNDI STARTUP + * + * @{ + */ + +/** PXE API function code for pxenv_undi_startup() */ +#define PXENV_UNDI_STARTUP 0x0001 + +#define PXENV_BUS_ISA 0 /**< ISA bus type */ +#define PXENV_BUS_EISA 1 /**< EISA bus type */ +#define PXENV_BUS_MCA 2 /**< MCA bus type */ +#define PXENV_BUS_PCI 3 /**< PCI bus type */ +#define PXENV_BUS_VESA 4 /**< VESA bus type */ +#define PXENV_BUS_PCMCIA 5 /**< PCMCIA bus type */ + +/** Parameter block for pxenv_undi_startup() */ +struct s_PXENV_UNDI_STARTUP { + PXENV_STATUS_t Status; /**< PXE status code */ +} PACKED; + +typedef struct s_PXENV_UNDI_STARTUP PXENV_UNDI_STARTUP_t; + +extern PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP + *undi_startup ); + +/** @} */ /* pxenv_undi_startup */ + +/** @defgroup pxenv_undi_cleanup PXENV_UNDI_CLEANUP + * + * UNDI CLEANUP + * + * @{ + */ + +/** PXE API function code for pxenv_undi_cleanup() */ +#define PXENV_UNDI_CLEANUP 0x0002 + +/** Parameter block for pxenv_undi_cleanup() */ +struct s_PXENV_UNDI_CLEANUP { + PXENV_STATUS_t Status; /**< PXE status code */ +} PACKED; + +typedef struct s_PXENV_UNDI_CLEANUP PXENV_UNDI_CLEANUP_t; + +extern PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP + *undi_cleanup ); + +/** @} */ /* pxenv_undi_cleanup */ + +/** @defgroup pxenv_undi_initialize PXENV_UNDI_INITIALIZE + * + * UNDI INITIALIZE + * + * @{ + */ + +/** PXE API function code for pxenv_undi_initialize() */ +#define PXENV_UNDI_INITIALIZE 0x0003 + +/** Parameter block for pxenv_undi_initialize() */ +struct s_PXENV_UNDI_INITIALIZE { + PXENV_STATUS_t Status; /**< PXE status code */ + /** NDIS 2.0 configuration information, or NULL + * + * This is a pointer to the data structure returned by the + * NDIS 2.0 GetProtocolManagerInfo() API call. The data + * structure is documented, in a rather haphazard way, in + * section 4-17 of the NDIS 2.0 specification. + */ + ADDR32_t ProtocolIni; + UINT8_t reserved[8]; /**< Must be zero */ +} PACKED; + +typedef struct s_PXENV_UNDI_INITIALIZE PXENV_UNDI_INITIALIZE_t; + +extern PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE + *undi_initialize ); + +/** @} */ /* pxenv_undi_initialize */ + +/** @defgroup pxenv_undi_reset_adapter PXENV_UNDI_RESET_ADAPTER + * + * UNDI RESET ADAPTER + * + * @{ + */ + +/** PXE API function code for pxenv_undi_reset_adapter() */ +#define PXENV_UNDI_RESET_ADAPTER 0x0004 + +/** Maximum number of multicast MAC addresses */ +#define MAXNUM_MCADDR 8 + +/** List of multicast MAC addresses */ +struct s_PXENV_UNDI_MCAST_ADDRESS { + /** Number of multicast MAC addresses */ + UINT16_t MCastAddrCount; + /** List of up to #MAXNUM_MCADDR multicast MAC addresses */ + MAC_ADDR_t McastAddr[MAXNUM_MCADDR]; +} PACKED; + +typedef struct s_PXENV_UNDI_MCAST_ADDRESS PXENV_UNDI_MCAST_ADDRESS_t; + +/** Parameter block for pxenv_undi_reset_adapter() */ +struct s_PXENV_UNDI_RESET { + PXENV_STATUS_t Status; /**< PXE status code */ + /** Multicast MAC addresses */ + struct s_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf; +} PACKED; + +typedef struct s_PXENV_UNDI_RESET PXENV_UNDI_RESET_t; + +extern PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET + *undi_reset_adapter ); + +/** @} */ /* pxenv_undi_reset_adapter */ + +/** @defgroup pxenv_undi_shutdown PXENV_UNDI_SHUTDOWN + * + * UNDI SHUTDOWN + * + * @{ + */ + +/** PXE API function code for pxenv_undi_shutdown() */ +#define PXENV_UNDI_SHUTDOWN 0x0005 + +/** Parameter block for pxenv_undi_shutdown() */ +struct s_PXENV_UNDI_SHUTDOWN { + PXENV_STATUS_t Status; /**< PXE status code */ +} PACKED; + +typedef struct s_PXENV_UNDI_SHUTDOWN PXENV_UNDI_SHUTDOWN_t; + +extern PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN + *undi_shutdown ); + +/** @} */ /* pxenv_undi_shutdown */ + +/** @defgroup pxenv_undi_open PXENV_UNDI_OPEN + * + * UNDI OPEN + * + * @{ + */ + +/** PXE API function code for pxenv_undi_open() */ +#define PXENV_UNDI_OPEN 0x0006 + +/** Accept "directed" packets + * + * These are packets addresses to either this adapter's MAC address or + * to any of the configured multicast MAC addresses (see + * #s_PXENV_UNDI_MCAST_ADDRESS). + */ +#define FLTR_DIRECTED 0x0001 +/** Accept broadcast packets */ +#define FLTR_BRDCST 0x0002 +/** Accept all packets; listen in promiscuous mode */ +#define FLTR_PRMSCS 0x0004 +/** Accept source-routed packets */ +#define FLTR_SRC_RTG 0x0008 + +/** Parameter block for pxenv_undi_open() */ +struct s_PXENV_UNDI_OPEN { + PXENV_STATUS_t Status; /**< PXE status code */ + /** Open flags as defined in NDIS 2.0 + * + * This is the OpenOptions field as passed to the NDIS 2.0 + * OpenAdapter() API call. It is defined to be "adapter + * specific", though 0 is guaranteed to be a valid value. + */ + UINT16_t OpenFlag; + /** Receive packet filter + * + * This is the bitwise-OR of any of the following flags: + * #FLTR_DIRECTED, #FLTR_BRDCST, #FLTR_PRMSCS and + * #FLTR_SRC_RTG. + */ + UINT16_t PktFilter; + /** Multicast MAC addresses */ + struct s_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf; +} PACKED; + +typedef struct s_PXENV_UNDI_OPEN PXENV_UNDI_OPEN_t; + +extern PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ); + +/** @} */ /* pxenv_undi_open */ + +/** @defgroup pxenv_undi_close PXENV_UNDI_CLOSE + * + * UNDI CLOSE + * + * @{ + */ + +/** PXE API function code for pxenv_undi_close() */ +#define PXENV_UNDI_CLOSE 0x0007 + +/** Parameter block for pxenv_undi_close() */ +struct s_PXENV_UNDI_CLOSE { + PXENV_STATUS_t Status; /**< PXE status code */ +} PACKED; + +typedef struct s_PXENV_UNDI_CLOSE PXENV_UNDI_CLOSE_t; + +extern PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ); + +/** @} */ /* pxenv_undi_close */ + +/** @defgroup pxenv_undi_transmit PXENV_UNDI_TRANSMIT + * + * UNDI TRANSMIT PACKET + * + * @{ + */ + +/** PXE API function code for pxenv_undi_transmit() */ +#define PXENV_UNDI_TRANSMIT 0x0008 + +#define P_UNKNOWN 0 /**< Media header already filled in */ +#define P_IP 1 /**< IP protocol */ +#define P_ARP 2 /**< ARP protocol */ +#define P_RARP 3 /**< RARP protocol */ +#define P_OTHER 4 /**< Other protocol */ + +#define XMT_DESTADDR 0x0000 /**< Unicast packet */ +#define XMT_BROADCAST 0x0001 /**< Broadcast packet */ + +/** Maximum number of data blocks in a transmit buffer descriptor */ +#define MAX_DATA_BLKS 8 + +/** A transmit buffer descriptor, as pointed to by s_PXENV_UNDI_TRANSMIT::TBD + */ +struct s_PXENV_UNDI_TBD { + UINT16_t ImmedLength; /**< Length of the transmit buffer */ + SEGOFF16_t Xmit; /**< Address of the transmit buffer */ + UINT16_t DataBlkCount; + /** Array of up to #MAX_DATA_BLKS additional transmit buffers */ + struct DataBlk { + /** Always 1 + * + * A value of 0 would indicate that #TDDataPtr were an + * #ADDR32_t rather than a #SEGOFF16_t. The PXE + * specification version 2.1 explicitly states that + * this is not supported; #TDDataPtr will always be a + * #SEGOFF16_t. + */ + UINT8_t TDPtrType; + UINT8_t TDRsvdByte; /**< Must be zero */ + UINT16_t TDDataLen; /**< Length of this transmit buffer */ + SEGOFF16_t TDDataPtr; /**< Address of this transmit buffer */ + } DataBlock[MAX_DATA_BLKS]; +} PACKED; + +typedef struct s_PXENV_UNDI_TBD PXENV_UNDI_TBD_t; + +/** Parameter block for pxenv_undi_transmit() */ +struct s_PXENV_UNDI_TRANSMIT { + PXENV_STATUS_t Status; /**< PXE status code */ + /** Protocol + * + * Valid values are #P_UNKNOWN, #P_IP, #P_ARP or #P_RARP. If + * the caller has already filled in the media header, this + * field must be set to #P_UNKNOWN. + */ + UINT8_t Protocol; + /** Unicast/broadcast flag + * + * Valid values are #XMT_DESTADDR or #XMT_BROADCAST. + */ + UINT8_t XmitFlag; + SEGOFF16_t DestAddr; /**< Destination MAC address */ + /** Address of the Transmit Buffer Descriptor + * + * This is a pointer to a struct s_PXENV_UNDI_TBD. + */ + SEGOFF16_t TBD; + UINT32_t Reserved[2]; /**< Must be zero */ +} PACKED; + +typedef struct s_PXENV_UNDI_TRANSMIT PXENV_UNDI_TRANSMIT_t; + +extern PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT + *undi_transmit ); + +/** @} */ /* pxenv_undi_transmit */ + +/** @defgroup pxenv_undi_set_mcast_address PXENV_UNDI_SET_MCAST_ADDRESS + * + * UNDI SET MULTICAST ADDRESS + * + * @{ + */ + +/** PXE API function code for pxenv_undi_set_mcast_address() */ +#define PXENV_UNDI_SET_MCAST_ADDRESS 0x0009 + +/** Parameter block for pxenv_undi_set_mcast_address() */ +struct s_PXENV_UNDI_SET_MCAST_ADDRESS { + PXENV_STATUS_t Status; /**< PXE status code */ + /** List of multicast addresses */ + struct s_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf; +} PACKED; + +typedef struct s_PXENV_UNDI_SET_MCAST_ADDRESS PXENV_UNDI_SET_MCAST_ADDRESS_t; + +extern PXENV_EXIT_t pxenv_undi_set_mcast_address ( + struct s_PXENV_UNDI_SET_MCAST_ADDRESS *undi_set_mcast_address ); + +/** @} */ /* pxenv_undi_set_mcast_address */ + +/** @defgroup pxenv_undi_set_station_address PXENV_UNDI_SET_STATION_ADDRESS + * + * UNDI SET STATION ADDRESS + * + * @{ + */ + +/** PXE API function code for pxenv_undi_set_station_address() */ +#define PXENV_UNDI_SET_STATION_ADDRESS 0x000a + +/** Parameter block for pxenv_undi_set_station_address() */ +struct s_PXENV_UNDI_SET_STATION_ADDRESS { + PXENV_STATUS_t Status; /**< PXE status code */ + MAC_ADDR_t StationAddress; /**< Station MAC address */ +} PACKED; + +typedef struct s_PXENV_UNDI_SET_STATION_ADDRESS PXENV_UNDI_SET_STATION_ADDRESS_t; + +extern PXENV_EXIT_t pxenv_undi_set_station_address ( + struct s_PXENV_UNDI_SET_STATION_ADDRESS *undi_set_station_address ); + +/** @} */ /* pxenv_undi_set_station_address */ + +/** @defgroup pxenv_undi_set_packet_filter PXENV_UNDI_SET_PACKET_FILTER + * + * UNDI SET PACKET FILTER + * + * @{ + */ + +/** PXE API function code for pxenv_undi_set_packet_filter() */ +#define PXENV_UNDI_SET_PACKET_FILTER 0x000b + +/** Parameter block for pxenv_undi_set_packet_filter() */ +struct s_PXENV_UNDI_SET_PACKET_FILTER { + PXENV_STATUS_t Status; /**< PXE status code */ + /** Receive packet filter + * + * This field takes the same values as + * s_PXENV_UNDI_OPEN::PktFilter. + * + * @note Yes, this field is a different size to + * s_PXENV_UNDI_OPEN::PktFilter. Blame "the managers at Intel + * who apparently let a consultant come up with the spec + * without any kind of adult supervision" (quote from hpa). + */ + UINT8_t filter; +} PACKED; + +typedef struct s_PXENV_UNDI_SET_PACKET_FILTER PXENV_UNDI_SET_PACKET_FILTER_t; + +extern PXENV_EXIT_t pxenv_undi_set_packet_filter ( + struct s_PXENV_UNDI_SET_PACKET_FILTER *undi_set_packet_filter ); + +/** @} */ /* pxenv_undi_set_packet_filter */ + +/** @defgroup pxenv_undi_get_information PXENV_UNDI_GET_INFORMATION + * + * UNDI GET INFORMATION + * + * @{ + */ + +/** PXE API function code for pxenv_undi_get_information() */ +#define PXENV_UNDI_GET_INFORMATION 0x000c + +#define ETHER_TYPE 1 /**< Ethernet (10Mb) */ +#define EXP_ETHER_TYPE 2 /**< Experimental Ethernet (3Mb) */ +#define AX25_TYPE 3 /**< Amateur Radio AX.25 */ +#define TOKEN_RING_TYPE 4 /**< Proteon ProNET Token Ring */ +#define CHAOS_TYPE 5 /**< Chaos */ +#define IEEE_TYPE 6 /**< IEEE 802 Networks */ +#define ARCNET_TYPE 7 /**< ARCNET */ + +/** Parameter block for pxenv_undi_get_information() */ +struct s_PXENV_UNDI_GET_INFORMATION { + PXENV_STATUS_t Status; /**< PXE status code */ + UINT16_t BaseIo; /**< I/O base address */ + UINT16_t IntNumber; /**< IRQ number */ + UINT16_t MaxTranUnit; /**< Adapter MTU */ + /** Hardware type + * + * Valid values are defined in RFC1010 ("Assigned numbers"), + * and are #ETHER_TYPE, #EXP_ETHER_TYPE, #AX25_TYPE, + * #TOKEN_RING_TYPE, #CHAOS_TYPE, #IEEE_TYPE or #ARCNET_TYPE. + */ + UINT16_t HwType; + UINT16_t HwAddrLen; /**< MAC address length */ + MAC_ADDR_t CurrentNodeAddress; /**< Current MAC address */ + MAC_ADDR_t PermNodeAddress; /**< Permanent (EEPROM) MAC address */ + SEGSEL_t ROMAddress; /**< Real-mode ROM segment address */ + UINT16_t RxBufCt; /**< Receive queue length */ + UINT16_t TxBufCt; /**< Transmit queue length */ +} PACKED; + +typedef struct s_PXENV_UNDI_GET_INFORMATION PXENV_UNDI_GET_INFORMATION_t; + +extern PXENV_EXIT_t pxenv_undi_get_information ( + struct s_PXENV_UNDI_GET_INFORMATION *undi_get_information ); + +/** @} */ /* pxenv_undi_get_information */ + +/** @defgroup pxenv_undi_get_statistics PXENV_UNDI_GET_STATISTICS + * + * UNDI GET STATISTICS + * + * @{ + */ + +/** PXE API function code for pxenv_undi_get_statistics() */ +#define PXENV_UNDI_GET_STATISTICS 0x000d + +/** Parameter block for pxenv_undi_get_statistics() */ +struct s_PXENV_UNDI_GET_STATISTICS { + PXENV_STATUS_t Status; /**< PXE status code */ + UINT32_t XmtGoodFrames; /**< Successful transmission count */ + UINT32_t RcvGoodFrames; /**< Successful reception count */ + UINT32_t RcvCRCErrors; /**< Receive CRC error count */ + UINT32_t RcvResourceErrors; /**< Receive queue overflow count */ +} PACKED; + +typedef struct s_PXENV_UNDI_GET_STATISTICS PXENV_UNDI_GET_STATISTICS_t; + +extern PXENV_EXIT_t pxenv_undi_get_statistics ( + struct s_PXENV_UNDI_GET_STATISTICS *undi_get_statistics ); + +/** @} */ /* pxenv_undi_get_statistics */ + +/** @defgroup pxenv_undi_clear_statistics PXENV_UNDI_CLEAR_STATISTICS + * + * UNDI CLEAR STATISTICS + * + * @{ + */ + +/** PXE API function code for pxenv_undi_clear_statistics() */ +#define PXENV_UNDI_CLEAR_STATISTICS 0x000e + +/** Parameter block for pxenv_undi_clear_statistics() */ +struct s_PXENV_UNDI_CLEAR_STATISTICS { + PXENV_STATUS_t Status; /**< PXE status code */ +} PACKED; + +typedef struct s_PXENV_UNDI_CLEAR_STATISTICS PXENV_UNDI_CLEAR_STATISTICS_t; + +extern PXENV_EXIT_t pxenv_undi_clear_statistics ( + struct s_PXENV_UNDI_CLEAR_STATISTICS *undi_clear_statistics ); + +/** @} */ /* pxenv_undi_clear_statistics */ + +/** @defgroup pxenv_undi_initiate_diags PXENV_UNDI_INITIATE_DIAGS + * + * UNDI INITIATE DIAGS + * + * @{ + */ + +/** PXE API function code for pxenv_undi_initiate_diags() */ +#define PXENV_UNDI_INITIATE_DIAGS 0x000f + +/** Parameter block for pxenv_undi_initiate_diags() */ +struct s_PXENV_UNDI_INITIATE_DIAGS { + PXENV_STATUS_t Status; /**< PXE status code */ +} PACKED; + +typedef struct s_PXENV_UNDI_INITIATE_DIAGS PXENV_UNDI_INITIATE_DIAGS_t; + +extern PXENV_EXIT_t pxenv_undi_initiate_diags ( + struct s_PXENV_UNDI_INITIATE_DIAGS *undi_initiate_diags ); + +/** @} */ /* pxenv_undi_initiate_diags */ + +/** @defgroup pxenv_undi_force_interrupt PXENV_UNDI_FORCE_INTERRUPT + * + * UNDI FORCE INTERRUPT + * + * @{ + */ + +/** PXE API function code for pxenv_undi_force_interrupt() */ +#define PXENV_UNDI_FORCE_INTERRUPT 0x0010 + +/** Parameter block for pxenv_undi_force_interrupt() */ +struct s_PXENV_UNDI_FORCE_INTERRUPT { + PXENV_STATUS_t Status; /**< PXE status code */ +} PACKED; + +typedef struct s_PXENV_UNDI_FORCE_INTERRUPT PXENV_UNDI_FORCE_INTERRUPT_t; + +extern PXENV_EXIT_t pxenv_undi_force_interrupt ( + struct s_PXENV_UNDI_FORCE_INTERRUPT *undi_force_interrupt ); + +/** @} */ /* pxenv_undi_force_interrupt */ + +/** @defgroup pxenv_undi_get_mcast_address PXENV_UNDI_GET_MCAST_ADDRESS + * + * UNDI GET MULTICAST ADDRESS + * + * @{ + */ + +/** PXE API function code for pxenv_undi_get_mcast_address() */ +#define PXENV_UNDI_GET_MCAST_ADDRESS 0x0011 + +/** Parameter block for pxenv_undi_get_mcast_address() */ +struct s_PXENV_UNDI_GET_MCAST_ADDRESS { + PXENV_STATUS_t Status; /**< PXE status code */ + IP4_t InetAddr; /**< Multicast IP address */ + MAC_ADDR_t MediaAddr; /**< Multicast MAC address */ +} PACKED; + +typedef struct s_PXENV_UNDI_GET_MCAST_ADDRESS PXENV_UNDI_GET_MCAST_ADDRESS_t; + +extern PXENV_EXIT_t pxenv_undi_get_mcast_address ( + struct s_PXENV_UNDI_GET_MCAST_ADDRESS *undi_get_mcast_address ); + +/** @} */ /* pxenv_undi_get_mcast_address */ + +/** @defgroup pxenv_undi_get_nic_type PXENV_UNDI_GET_NIC_TYPE + * + * UNDI GET NIC TYPE + * + * @{ + */ + +/** PXE API function code for pxenv_undi_get_nic_type() */ +#define PXENV_UNDI_GET_NIC_TYPE 0x0012 + +#define PCI_NIC 2 /**< PCI network card */ +#define PnP_NIC 3 /**< ISAPnP network card */ +#define CardBus_NIC 4 /**< CardBus network card */ + +/** Information for a PCI or equivalent NIC */ +struct pci_nic_info { + UINT16_t Vendor_ID; /**< PCI vendor ID */ + UINT16_t Dev_ID; /**< PCI device ID */ + UINT8_t Base_Class; /**< PCI base class */ + UINT8_t Sub_Class; /**< PCI sub class */ + UINT8_t Prog_Intf; /**< PCI programming interface */ + UINT8_t Rev; /**< PCI revision */ + UINT16_t BusDevFunc; /**< PCI bus:dev:fn address */ + UINT16_t SubVendor_ID; /**< PCI subvendor ID */ + UINT16_t SubDevice_ID; /**< PCI subdevice ID */ +} PACKED; + +/** Information for an ISAPnP or equivalent NIC */ +struct pnp_nic_info { + UINT32_t EISA_Dev_ID; /**< EISA device ID */ + UINT8_t Base_Class; /**< Base class */ + UINT8_t Sub_Class; /**< Sub class */ + UINT8_t Prog_Intf; /**< Programming interface */ + /** Card Select Number assigned to card */ + UINT16_t CardSelNum; +} PACKED; + +/** Parameter block for pxenv_undi_get_nic_type() */ +struct s_PXENV_UNDI_GET_NIC_TYPE { + PXENV_STATUS_t Status; /**< PXE status code */ + /** NIC type + * + * Valid values are #PCI_NIC, #PnP_NIC or #CardBus_NIC. + */ + UINT8_t NicType; + /** NIC information */ + union nic_type_info { + /** NIC information (if #NicType==#PCI_NIC) */ + struct pci_nic_info pci; + /** NIC information (if #NicType==#CardBus_NIC) */ + struct pci_nic_info cardbus; + /** NIC information (if #NicType==#PnP_NIC) */ + struct pnp_nic_info pnp; + } info; +} PACKED; + +typedef struct s_PXENV_UNDI_GET_NIC_TYPE PXENV_UNDI_GET_NIC_TYPE_t; + +extern PXENV_EXIT_t pxenv_undi_get_nic_type ( + struct s_PXENV_UNDI_GET_NIC_TYPE *undi_get_nic_type ); + +/** @} */ /* pxenv_undi_get_nic_type */ + +/** @defgroup pxenv_undi_get_iface_info PXENV_UNDI_GET_IFACE_INFO + * + * UNDI GET IFACE INFO + * + * @{ + */ + +/** PXE API function code for pxenv_undi_get_iface_info() */ +#define PXENV_UNDI_GET_IFACE_INFO 0x0013 + +/** Parameter block for pxenv_undi_get_iface_info() */ +struct s_PXENV_UNDI_GET_IFACE_INFO { + PXENV_STATUS_t Status; /**< PXE status code */ + /** Interface type + * + * This is defined in the NDIS 2.0 specification to be one of + * the strings "802.3", "802.4", "802.5", "802.6", "DIX", + * "DIX+802.3", "APPLETALK", "ARCNET", "FDDI", "SDLC", "BSC", + * "HDLC", or "ISDN". + * + * "Normal" Ethernet, for various historical reasons, is + * "DIX+802.3". + */ + UINT8_t IfaceType[16]; + UINT32_t LinkSpeed; /**< Link speed, in bits per second */ + /** Service flags + * + * These are the "service flags" defined in the "MAC + * Service-Specific Characteristics" table in the NDIS 2.0 + * specification. Almost all of them are irrelevant to PXE. + */ + UINT32_t ServiceFlags; + UINT32_t Reserved[4]; /**< Must be zero */ +} PACKED; + +typedef struct s_PXENV_UNDI_GET_IFACE_INFO PXENV_UNDI_GET_IFACE_INFO_t; + +extern PXENV_EXIT_t pxenv_undi_get_iface_info ( + struct s_PXENV_UNDI_GET_IFACE_INFO *undi_get_iface_info ); + +/** @} */ /* pxenv_undi_get_iface_info */ + +/** @defgroup pxenv_undi_get_state PXENV_UNDI_GET_STATE + * + * UNDI GET STATE + * + * @{ + */ + +/** PXE API function code for pxenv_undi_get_state() */ +#define PXENV_UNDI_GET_STATE 0x0015 + +/** pxenv_start_undi() has been called */ +#define PXE_UNDI_GET_STATE_STARTED 1 +/** pxenv_undi_initialize() has been called */ +#define PXE_UNDI_GET_STATE_INITIALIZED 2 +/** pxenv_undi_open() has been called */ +#define PXE_UNDI_GET_STATE_OPENED 3 + +/** Parameter block for pxenv_undi_get_state() */ +struct s_PXENV_UNDI_GET_STATE { + PXENV_STATUS_t Status; /**< PXE status code */ + /** Current state of the UNDI driver + * + * Valid values are #PXE_UNDI_GET_STATE_STARTED, + * #PXE_UNDI_GET_STATE_INITIALIZED or + * #PXE_UNDI_GET_STATE_OPENED. + */ + UINT8_t UNDIstate; +} PACKED; + +typedef struct s_PXENV_UNDI_GET_STATE PXENV_UNDI_GET_STATE_t; + +extern PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE + *undi_get_state ); + +/** @} */ /* pxenv_undi_get_state */ + +/** @defgroup pxenv_undi_isr PXENV_UNDI_ISR + * + * UNDI ISR + * + * @{ + */ + +/** PXE API function code for pxenv_undi_isr() */ +#define PXENV_UNDI_ISR 0x0014 + +/** Determine whether or not this is our interrupt */ +#define PXENV_UNDI_ISR_IN_START 1 +/** Start processing interrupt */ +#define PXENV_UNDI_ISR_IN_PROCESS 2 +/** Continue processing interrupt */ +#define PXENV_UNDI_ISR_IN_GET_NEXT 3 +/** This interrupt was ours */ +#define PXENV_UNDI_ISR_OUT_OURS 0 +/** This interrupt was not ours */ +#define PXENV_UNDI_ISR_OUT_NOT_OURS 1 +/** Finished processing interrupt */ +#define PXENV_UNDI_ISR_OUT_DONE 0 +/** A packet transmission has completed */ +#define PXENV_UNDI_ISR_OUT_TRANSMIT 2 +/** A packet has been received */ +#define PXENV_UNDI_ISR_OUT_RECEIVE 3 +/** We are already in the middle of processing an interrupt */ +#define PXENV_UNDI_ISR_OUT_BUSY 4 + +/** Unicast packet (or packet captured in promiscuous mode) */ +#define P_DIRECTED 0 +/** Broadcast packet */ +#define P_BROADCAST 1 +/** Multicast packet */ +#define P_MULTICAST 2 + +/** Parameter block for pxenv_undi_isr() */ +struct s_PXENV_UNDI_ISR { + PXENV_STATUS_t Status; /**< PXE status code */ + /** Function flag + * + * Valid values are #PXENV_UNDI_ISR_IN_START, + * #PXENV_UNDI_ISR_IN_PROCESS, #PXENV_UNDI_ISR_IN_GET_NEXT, + * #PXENV_UNDI_ISR_OUT_OURS, #PXENV_UNDI_ISR_OUT_NOT_OURS, + * #PXENV_UNDI_ISR_OUT_DONE, #PXENV_UNDI_ISR_OUT_TRANSMIT, + * #PXENV_UNDI_ISR_OUT_RECEIVE or #PXENV_UNDI_ISR_OUT_BUSY. + */ + UINT16_t FuncFlag; + UINT16_t BufferLength; /**< Data buffer length */ + UINT16_t FrameLength; /**< Total frame length */ + UINT16_t FrameHeaderLength; /**< Frame header length */ + SEGOFF16_t Frame; /**< Data buffer address */ + /** Protocol type + * + * Valid values are #P_IP, #P_ARP, #P_RARP or #P_OTHER. + */ + UINT8_t ProtType; + /** Packet type + * + * Valid values are #P_DIRECTED, #P_BROADCAST or #P_MULTICAST. + */ + UINT8_t PktType; +} PACKED; + +typedef struct s_PXENV_UNDI_ISR PXENV_UNDI_ISR_t; + +extern PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ); + +/** @} */ /* pxenv_undi_isr */ + +/** @} */ /* pxe_undi_api */ + +/** @defgroup pxe_file_api PXE FILE API + * + * POSIX-like file operations + * + * @{ + */ + +/** @defgroup pxenv_file_open PXENV_FILE_OPEN + * + * FILE OPEN + * + * @{ + */ + +/** PXE API function code for pxenv_file_open() */ +#define PXENV_FILE_OPEN 0x00e0 + +/** Parameter block for pxenv_file_open() */ +struct s_PXENV_FILE_OPEN { + PXENV_STATUS_t Status; /**< PXE status code */ + UINT16_t FileHandle; /**< File handle */ + SEGOFF16_t FileName; /**< File URL */ + UINT32_t Reserved; /**< Reserved */ +} PACKED; + +typedef struct s_PXENV_FILE_OPEN PXENV_FILE_OPEN_t; + +extern PXENV_EXIT_t pxenv_file_open ( struct s_PXENV_FILE_OPEN *file_open ); + +/** @} */ /* pxenv_file_open */ + +/** @defgroup pxenv_file_close PXENV_FILE_CLOSE + * + * FILE CLOSE + * + * @{ + */ + +/** PXE API function code for pxenv_file_close() */ +#define PXENV_FILE_CLOSE 0x00e1 + +/** Parameter block for pxenv_file_close() */ +struct s_PXENV_FILE_CLOSE { + PXENV_STATUS_t Status; /**< PXE status code */ + UINT16_t FileHandle; /**< File handle */ +} PACKED; + +typedef struct s_PXENV_FILE_CLOSE PXENV_FILE_CLOSE_t; + +extern PXENV_EXIT_t pxenv_file_close ( struct s_PXENV_FILE_CLOSE + *file_close ); + +/** @} */ /* pxenv_file_close */ + +/** @defgroup pxenv_file_select PXENV_FILE_SELECT + * + * FILE SELECT + * + * @{ + */ + +/** PXE API function code for pxenv_file_select() */ +#define PXENV_FILE_SELECT 0x00e2 + +/** File is ready for reading */ +#define RDY_READ 0x0001 + +/** Parameter block for pxenv_file_select() */ +struct s_PXENV_FILE_SELECT { + PXENV_STATUS_t Status; /**< PXE status code */ + UINT16_t FileHandle; /**< File handle */ + UINT16_t Ready; /**< Indication of readiness */ +} PACKED; + +typedef struct s_PXENV_FILE_SELECT PXENV_FILE_SELECT_t; + +extern PXENV_EXIT_t pxenv_file_select ( struct s_PXENV_FILE_SELECT + *file_select ); + +/** @} */ /* pxenv_file_select */ + +/** @defgroup pxenv_file_read PXENV_FILE_READ + * + * FILE READ + * + * @{ + */ + +/** PXE API function code for pxenv_file_read() */ +#define PXENV_FILE_READ 0x00e3 + +/** Parameter block for pxenv_file_read() */ +struct s_PXENV_FILE_READ { + PXENV_STATUS_t Status; /**< PXE status code */ + UINT16_t FileHandle; /**< File handle */ + UINT16_t BufferSize; /**< Data buffer size */ + SEGOFF16_t Buffer; /**< Data buffer */ +} PACKED; + +typedef struct s_PXENV_FILE_READ PXENV_FILE_READ_t; + +extern PXENV_EXIT_t pxenv_file_read ( struct s_PXENV_FILE_READ *file_read ); + +/** @} */ /* pxenv_file_read */ + +/** @defgroup pxenv_get_file_size PXENV_GET_FILE_SIZE + * + * GET FILE SIZE + * + * @{ + */ + +/** PXE API function code for pxenv_get_file_size() */ +#define PXENV_GET_FILE_SIZE 0x00e4 + +/** Parameter block for pxenv_get_file_size() */ +struct s_PXENV_GET_FILE_SIZE { + PXENV_STATUS_t Status; /**< PXE status code */ + UINT16_t FileHandle; /**< File handle */ + UINT32_t FileSize; /**< File size */ +} PACKED; + +typedef struct s_PXENV_GET_FILE_SIZE PXENV_GET_FILE_SIZE_t; + +extern PXENV_EXIT_t pxenv_get_file_size ( struct s_PXENV_GET_FILE_SIZE + *get_file_size ); + +/** @} */ /* pxenv_get_file_size */ + +/** @defgroup pxenv_file_exec PXENV_FILE_EXEC + * + * FILE EXEC + * + * @{ + */ + +/** PXE API function code for pxenv_file_exec() */ +#define PXENV_FILE_EXEC 0x00e5 + +/** Parameter block for pxenv_file_exec() */ +struct s_PXENV_FILE_EXEC { + PXENV_STATUS_t Status; /**< PXE status code */ + SEGOFF16_t Command; /**< Command to execute */ +} PACKED; + +typedef struct s_PXENV_FILE_EXEC PXENV_FILE_EXEC_t; + +extern PXENV_EXIT_t pxenv_file_exec ( struct s_PXENV_FILE_EXEC *file_exec ); + +/** @} */ /* pxenv_file_exec */ + +/** @defgroup pxenv_file_api_check PXENV_FILE_API_CHECK + * + * FILE API CHECK + * + * @{ + */ + +/** PXE API function code for pxenv_file_api_check() */ +#define PXENV_FILE_API_CHECK 0x00e6 + +/** Parameter block for pxenv_file_api_check() */ +struct s_PXENV_FILE_API_CHECK { + PXENV_STATUS_t Status; /**< PXE status code */ + UINT16_t Size; /**< Size of structure */ + UINT32_t Magic; /**< Magic number */ + UINT32_t Provider; /**< Implementation identifier */ + UINT32_t APIMask; /**< Supported API functions */ + UINT32_t Flags; /**< Reserved for the future */ +} PACKED; + +typedef struct s_PXENV_FILE_API_CHECK PXENV_FILE_API_CHECK_t; + +extern PXENV_EXIT_t pxenv_file_api_check ( struct s_PXENV_FILE_API_CHECK *file_api_check ); + +/** @} */ /* pxenv_file_api_check */ + +/** @} */ /* pxe_file_api */ + +/** @defgroup pxe_loader_api PXE Loader API + * + * The UNDI ROM loader API + * + * @{ + */ + +/** Parameter block for undi_loader() */ +struct s_UNDI_LOADER { + /** PXE status code */ + PXENV_STATUS_t Status; + /** %ax register as for PXENV_START_UNDI */ + UINT16_t AX; + /** %bx register as for PXENV_START_UNDI */ + UINT16_t BX; + /** %dx register as for PXENV_START_UNDI */ + UINT16_t DX; + /** %di register as for PXENV_START_UNDI */ + OFF16_t DI; + /** %es register as for PXENV_START_UNDI */ + SEGSEL_t ES; + /** UNDI data segment + * + * @note The PXE specification defines the type of this field + * as #UINT16_t. For x86, #SEGSEL_t and #UINT16_t are + * equivalent anyway; for other architectures #SEGSEL_t makes + * more sense. + */ + SEGSEL_t UNDI_DS; + /** UNDI code segment + * + * @note The PXE specification defines the type of this field + * as #UINT16_t. For x86, #SEGSEL_t and #UINT16_t are + * equivalent anyway; for other architectures #SEGSEL_t makes + * more sense. + */ + SEGSEL_t UNDI_CS; + /** Address of the !PXE structure (a struct s_PXE) */ + SEGOFF16_t PXEptr; + /** Address of the PXENV+ structure (a struct s_PXENV) */ + SEGOFF16_t PXENVptr; +} PACKED; + +typedef struct s_UNDI_LOADER UNDI_LOADER_t; + +extern PXENV_EXIT_t undi_loader ( struct s_UNDI_LOADER *undi_loader ); + +/** @} */ /* pxe_loader_api */ + +/** @} */ /* pxe */ + +/** @page pxe_notes Etherboot PXE implementation notes + +@section pxe_routing IP routing + +Several PXE API calls (e.g. pxenv_tftp_open() and pxenv_udp_write()) +allow for the caller to specify a "relay agent IP address", often in a +field called "gateway" or similar. The PXE specification states that +"The IP layer should provide space for a minimum of four routing +entries obtained from the default router and static route DHCP option +tags in the DHCPACK message, plus any non-zero giaddr field from the +DHCPOFFER message(s) accepted by the client". + +The DHCP static route option ("option static-routes" in dhcpd.conf) +works only for classed IP routing (i.e. it provides no way to specify +a subnet mask). Since virtually everything now uses classless IP +routing, the DHCP static route option is almost totally useless, and +is (according to the dhcp-options man page) not implemented by any of +the popular DHCP clients. + +This leaves the caller-specified "relay agent IP address", the giaddr +field from the DHCPOFFER message(s) and the default gateway(s) +provided via the routers option ("option routers" in dhcpd.conf) in +the DHCPACK message. Each of these is a default gateway address. +It's a fair bet that the routers option should take priority over the +giaddr field, since the routers option has to be explicitly specified +by the DHCP server operator. Similarly, it's fair to assume that the +caller-specified "relay agent IP address", if present, should take +priority over any other routing table entries. + +@bug Etherboot currently ignores all potential sources of routing +information other than the first router provided to it by a DHCP +routers option. + +@section pxe_x86_modes x86 processor mode restrictions + +On the x86 platform, different PXE API calls have different +restrictions on the processor modes (real or protected) that can be +used. See the individual API call descriptions for the restrictions +that apply to any particular call. + +@subsection pxe_x86_pmode16 Real mode, or protected-mode with 16-bit stack + +The PXE specification states that the API function can be called in +protected mode only if the s_PXE::StatusCallout field is set to a +non-zero value, and that the API function cannot be called with a +32-bit stack segment. + +Etherboot does not enforce either of these restrictions; they seem (as +with so much of the PXE specification) to be artifacts of the Intel +implementation. + +*/ + +#endif /* PXE_API_H */ diff --git a/gpxe/src/arch/i386/include/pxe_call.h b/gpxe/src/arch/i386/include/pxe_call.h new file mode 100644 index 0000000..2f3ea15 --- /dev/null +++ b/gpxe/src/arch/i386/include/pxe_call.h @@ -0,0 +1,35 @@ +#ifndef _PXE_CALL_H +#define _PXE_CALL_H + +/** @file + * + * PXE API entry point + */ + +#include +#include + +/** PXE load address segment */ +#define PXE_LOAD_SEGMENT 0 + +/** PXE load address offset */ +#define PXE_LOAD_OFFSET 0x7c00 + +/** PXE physical load address */ +#define PXE_LOAD_PHYS ( ( PXE_LOAD_SEGMENT << 4 ) + PXE_LOAD_OFFSET ) + +/** !PXE structure */ +extern struct s_PXE __text16 ( ppxe ); +#define ppxe __use_text16 ( ppxe ) + +/** PXENV+ structure */ +extern struct s_PXENV __text16 ( pxenv ); +#define pxenv __use_text16 ( pxenv ) + +extern void pxe_hook_int1a ( void ); +extern int pxe_unhook_int1a ( void ); +extern void pxe_init_structures ( void ); +extern int pxe_start_nbp ( void ); +extern __asmcall void pxe_api_call ( struct i386_all_regs *ix86 ); + +#endif /* _PXE_CALL_H */ diff --git a/gpxe/src/arch/i386/include/pxe_types.h b/gpxe/src/arch/i386/include/pxe_types.h new file mode 100644 index 0000000..dd9092e --- /dev/null +++ b/gpxe/src/arch/i386/include/pxe_types.h @@ -0,0 +1,125 @@ +#ifndef PXE_TYPES_H +#define PXE_TYPES_H + +/** @file + * + * PXE data types + * + */ + +#include +#include /* PXE status codes */ + +/** @addtogroup pxe Preboot eXecution Environment (PXE) API + * @{ + */ + +/** @defgroup pxe_types PXE data types + * + * Basic PXE data types such as #UINT16_t, #ADDR32_t, #SEGSEL_t etc. + * + * These definitions are based on Table 1-1 ("Data Type Definitions") + * in the Intel PXE specification version 2.1. They have been + * generalised to non-x86 architectures where possible. + * + * @{ + */ + +/** An 8-bit unsigned integer */ +typedef uint8_t UINT8_t; + +/** A 16-bit unsigned integer */ +typedef uint16_t UINT16_t; + +/** A 32-bit unsigned integer */ +typedef uint32_t UINT32_t; + +/** A PXE exit code. + * + * Permitted values are #PXENV_EXIT_SUCCESS and #PXENV_EXIT_FAILURE. + * + */ +typedef UINT16_t PXENV_EXIT_t; +#define PXENV_EXIT_SUCCESS 0x0000 /**< No error occurred */ +#define PXENV_EXIT_FAILURE 0x0001 /**< An error occurred */ + +/** A PXE status code. + * + * Status codes are defined in errno.h. + * + */ +typedef UINT16_t PXENV_STATUS_t; + +/** An IPv4 address. + * + * @note This data type is in network (big-endian) byte order. + * + */ +typedef UINT32_t IP4_t; + +/** A UDP port. + * + * @note This data type is in network (big-endian) byte order. + * + */ +typedef UINT16_t UDP_PORT_t; + +/** Maximum length of a MAC address */ +#define MAC_ADDR_LEN 16 + +/** A MAC address */ +typedef UINT8_t MAC_ADDR_t[MAC_ADDR_LEN]; + +#ifndef HAVE_ARCH_ADDR32 +/** A physical address. + * + * For x86, this is a 32-bit physical address, and is therefore + * limited to the low 4GB. + * + */ +typedef UINT32_t ADDR32_t; +#endif + +#ifndef HAVE_ARCH_SEGSEL +/** A segment selector. + * + * For x86, this is a real mode segment (0x0000-0xffff), or a + * protected-mode segment selector, such as could be loaded into a + * segment register. + * + */ +typedef UINT16_t SEGSEL_t; +#endif + +#ifndef HAVE_ARCH_OFF16 +/** An offset within a segment identified by #SEGSEL + * + * For x86, this is a 16-bit offset. + * + */ +typedef UINT16_t OFF16_t; +#endif + +/** A segment:offset address + * + * For x86, this is a 16-bit real-mode or protected-mode + * segment:offset address. + * + */ +typedef struct s_SEGOFF16 { + OFF16_t offset; /**< Offset within the segment */ + SEGSEL_t segment; /**< Segment selector */ +} PACKED SEGOFF16_t; + +/** A segment descriptor */ +typedef struct s_SEGDESC { + SEGSEL_t segment_address; /**< Segment selector */ + ADDR32_t Physical_address; /**< Segment base address */ + OFF16_t Seg_size; /**< Size of the segment */ +} PACKED SEGDESC_t; + +/** @} */ /* pxe_types */ + +/** @} */ /* pxe */ + +#endif /* PXE_TYPES_H */ diff --git a/gpxe/src/arch/i386/include/realmode.h b/gpxe/src/arch/i386/include/realmode.h new file mode 100644 index 0000000..26e6dd7 --- /dev/null +++ b/gpxe/src/arch/i386/include/realmode.h @@ -0,0 +1,125 @@ +#ifndef REALMODE_H +#define REALMODE_H + +#include +#include +#include + +/* + * Data structures and type definitions + * + */ + +/* + * Declaration of variables in .data16 + * + * To place a variable in the .data16 segment, declare it using the + * pattern: + * + * int __data16 ( foo ); + * #define foo __use_data16 ( foo ); + * + * extern uint32_t __data16 ( bar ); + * #define bar __use_data16 ( bar ); + * + * static long __data16 ( baz ) = 0xff000000UL; + * #define baz __use_data16 ( baz ); + * + * i.e. take a normal declaration, add __data16() around the variable + * name, and add a line saying "#define __use_data16 ( ) + * + * You can then access them just like any other variable, for example + * + * int x = foo + bar; + * + * This magic is achieved at a cost of only around 7 extra bytes per + * group of accesses to .data16 variables. When using KEEP_IT_REAL, + * there is no extra cost. + * + * You should place variables in .data16 when they need to be accessed + * by real-mode code. Real-mode assembly (e.g. as created by + * REAL_CODE()) can access these variables via the usual data segment. + * You can therefore write something like + * + * static uint16_t __data16 ( foo ); + * #define foo __use_data16 ( foo ) + * + * int bar ( void ) { + * __asm__ __volatile__ ( REAL_CODE ( "int $0xff\n\t" + * "movw %ax, foo" ) + * : : ); + * return foo; + * } + * + * Variables may also be placed in .text16 using __text16 and + * __use_text16. Some variables (e.g. chained interrupt vectors) fit + * most naturally in .text16; most should be in .data16. + * + * If you have only a pointer to a magic symbol within .data16 or + * .text16, rather than the symbol itself, you can attempt to extract + * the underlying symbol name using __from_data16() or + * __from_text16(). This is not for the faint-hearted; check the + * assembler output to make sure that it's doing the right thing. + */ + +/** + * Copy data to base memory + * + * @v dest_seg Destination segment + * @v dest_off Destination offset + * @v src Source + * @v len Length + */ +static inline __always_inline void +copy_to_real ( unsigned int dest_seg, unsigned int dest_off, + void *src, size_t n ) { + copy_to_user ( real_to_user ( dest_seg, dest_off ), 0, src, n ); +} + +/** + * Copy data to base memory + * + * @v dest Destination + * @v src_seg Source segment + * @v src_off Source offset + * @v len Length + */ +static inline __always_inline void +copy_from_real ( void *dest, unsigned int src_seg, + unsigned int src_off, size_t n ) { + copy_from_user ( dest, real_to_user ( src_seg, src_off ), 0, n ); +} + +/** + * Write a single variable to base memory + * + * @v var Variable to write + * @v dest_seg Destination segment + * @v dest_off Destination offset + */ +#define put_real( var, dest_seg, dest_off ) \ + copy_to_real ( (dest_seg), (dest_off), &(var), sizeof (var) ) + +/** + * Read a single variable from base memory + * + * @v var Variable to read + * @v src_seg Source segment + * @v src_off Source offset + */ +#define get_real( var, src_seg, src_off ) \ + copy_from_real ( &(var), (src_seg), (src_off), sizeof (var) ) + +/* + * REAL_CODE ( asm_code_str ) + * + * This can be used in inline assembly to create a fragment of code + * that will execute in real mode. For example: to write a character + * to the BIOS console using INT 10, you would do something like: + * + * __asm__ __volatile__ ( REAL_CODE ( "int $0x16" ) + * : "=a" ( character ) : "a" ( 0x0000 ) ); + * + */ + +#endif /* REALMODE_H */ diff --git a/gpxe/src/arch/i386/include/registers.h b/gpxe/src/arch/i386/include/registers.h new file mode 100644 index 0000000..e68fa85 --- /dev/null +++ b/gpxe/src/arch/i386/include/registers.h @@ -0,0 +1,196 @@ +#ifndef REGISTERS_H +#define REGISTERS_H + +/** @file + * + * i386 registers. + * + * This file defines data structures that allow easy access to i386 + * register dumps. + * + */ + +#include + +/** + * A 16-bit general register. + * + * This type encapsulates a 16-bit register such as %ax, %bx, %cx, + * %dx, %si, %di, %bp or %sp. + * + */ +typedef union { + struct { + union { + uint8_t l; + uint8_t byte; + }; + uint8_t h; + } PACKED; + uint16_t word; +} PACKED reg16_t; + +/** + * A 32-bit general register. + * + * This type encapsulates a 32-bit register such as %eax, %ebx, %ecx, + * %edx, %esi, %edi, %ebp or %esp. + * + */ +typedef union { + struct { + union { + uint8_t l; + uint8_t byte; + }; + uint8_t h; + } PACKED; + uint16_t word; + uint32_t dword; +} PACKED reg32_t; + +/** + * A 32-bit general register dump. + * + * This is the data structure that is created on the stack by the @c + * pushal instruction, and can be read back using the @c popal + * instruction. + * + */ +struct i386_regs { + union { + uint16_t di; + uint32_t edi; + }; + union { + uint16_t si; + uint32_t esi; + }; + union { + uint16_t bp; + uint32_t ebp; + }; + union { + uint16_t sp; + uint32_t esp; + }; + union { + struct { + uint8_t bl; + uint8_t bh; + } PACKED; + uint16_t bx; + uint32_t ebx; + }; + union { + struct { + uint8_t dl; + uint8_t dh; + } PACKED; + uint16_t dx; + uint32_t edx; + }; + union { + struct { + uint8_t cl; + uint8_t ch; + } PACKED; + uint16_t cx; + uint32_t ecx; + }; + union { + struct { + uint8_t al; + uint8_t ah; + } PACKED; + uint16_t ax; + uint32_t eax; + }; +} PACKED; + +/** + * A segment register dump. + * + * The i386 has no equivalent of the @c pushal or @c popal + * instructions for the segment registers. We adopt the convention of + * always using the sequences + * + * @code + * + * pushw %gs ; pushw %fs ; pushw %es ; pushw %ds ; pushw %ss ; pushw %cs + * + * @endcode + * + * and + * + * @code + * + * addw $4, %sp ; popw %ds ; popw %es ; popw %fs ; popw %gs + * + * @endcode + * + * This is the data structure that is created and read back by these + * instruction sequences. + * + */ +struct i386_seg_regs { + uint16_t cs; + uint16_t ss; + uint16_t ds; + uint16_t es; + uint16_t fs; + uint16_t gs; +} PACKED; + +/** + * A full register dump. + * + * This data structure is created by the instructions + * + * @code + * + * pushfl + * pushal + * pushw %gs ; pushw %fs ; pushw %es ; pushw %ds ; pushw %ss ; pushw %cs + * + * @endcode + * + * and can be read back using the instructions + * + * @code + * + * addw $4, %sp ; popw %ds ; popw %es ; popw %fs ; popw %gs + * popal + * popfl + * + * @endcode + * + * prot_call() and kir_call() create this data structure on the stack + * and pass in a pointer to this structure. + * + */ +struct i386_all_regs { + struct i386_seg_regs segs; + struct i386_regs regs; + uint32_t flags; +} PACKED; + +/* Flags */ +#define CF ( 1 << 0 ) +#define PF ( 1 << 2 ) +#define AF ( 1 << 4 ) +#define ZF ( 1 << 6 ) +#define SF ( 1 << 7 ) +#define OF ( 1 << 11 ) + +/* Segment:offset structure. Note that the order within the structure + * is offset:segment. + */ +struct segoff { + uint16_t offset; + uint16_t segment; +} PACKED; + +typedef struct segoff segoff_t; + +#endif /* REGISTERS_H */ diff --git a/gpxe/src/arch/i386/include/setjmp.h b/gpxe/src/arch/i386/include/setjmp.h new file mode 100644 index 0000000..c18d03e --- /dev/null +++ b/gpxe/src/arch/i386/include/setjmp.h @@ -0,0 +1,38 @@ +#ifndef ETHERBOOT_SETJMP_H +#define ETHERBOOT_SETJMP_H + +#include +#include + +/** A jump buffer */ +typedef struct { + uint32_t retaddr; + uint32_t ebx; + uint32_t esp; + uint32_t ebp; + uint32_t esi; + uint32_t edi; +} jmp_buf[1]; + +/** A real-mode-extended jump buffer */ +typedef struct { + jmp_buf env; + uint16_t rm_ss; + uint16_t rm_sp; +} rmjmp_buf[1]; + +extern int __asmcall setjmp ( jmp_buf env ); +extern void __asmcall longjmp ( jmp_buf env, int val ); + +#define rmsetjmp( _env ) ( { \ + (_env)->rm_ss = rm_ss; \ + (_env)->rm_sp = rm_sp; \ + setjmp ( (_env)->env ); } ) \ + +#define rmlongjmp( _env, _val ) do { \ + rm_ss = (_env)->rm_ss; \ + rm_sp = (_env)->rm_sp; \ + longjmp ( (_env)->env, (_val) ); \ + } while ( 0 ) + +#endif /* ETHERBOOT_SETJMP_H */ diff --git a/gpxe/src/arch/i386/include/undi.h b/gpxe/src/arch/i386/include/undi.h new file mode 100644 index 0000000..c6253d0 --- /dev/null +++ b/gpxe/src/arch/i386/include/undi.h @@ -0,0 +1,104 @@ +#ifndef _UNDI_H +#define _UNDI_H + +/** @file + * + * UNDI driver + * + */ + +#ifndef ASSEMBLY + +#include +#include + +/** An UNDI device + * + * This structure is used by assembly code as well as C; do not alter + * this structure without editing pxeprefix.S to match. + */ +struct undi_device { + /** PXENV+ structure address */ + SEGOFF16_t pxenv; + /** !PXE structure address */ + SEGOFF16_t ppxe; + /** Entry point */ + SEGOFF16_t entry; + /** Free base memory after load */ + UINT16_t fbms; + /** Free base memory prior to load */ + UINT16_t restore_fbms; + /** PCI bus:dev.fn, or @c UNDI_NO_PCI_BUSDEVFN */ + UINT16_t pci_busdevfn; + /** ISAPnP card select number, or @c UNDI_NO_ISAPNP_CSN */ + UINT16_t isapnp_csn; + /** ISAPnP read port, or @c UNDI_NO_ISAPNP_READ_PORT */ + UINT16_t isapnp_read_port; + /** PCI vendor ID + * + * Filled in only for the preloaded UNDI device by pxeprefix.S + */ + UINT16_t pci_vendor; + /** PCI device ID + * + * Filled in only for the preloaded UNDI device by pxeprefix.S + */ + UINT16_t pci_device; + /** Flags + * + * This is the bitwise OR of zero or more UNDI_FL_XXX + * constants. + */ + UINT16_t flags; + + /** Generic device */ + struct device dev; + /** Driver-private data + * + * Use undi_set_drvdata() and undi_get_drvdata() to access this + * field. + */ + void *priv; +} __attribute__ (( packed )); + +/** + * Set UNDI driver-private data + * + * @v undi UNDI device + * @v priv Private data + */ +static inline void undi_set_drvdata ( struct undi_device *undi, void *priv ) { + undi->priv = priv; +} + +/** + * Get UNDI driver-private data + * + * @v undi UNDI device + * @ret priv Private data + */ +static inline void * undi_get_drvdata ( struct undi_device *undi ) { + return undi->priv; +} + +#endif /* ASSEMBLY */ + +/** PCI bus:dev.fn field is invalid */ +#define UNDI_NO_PCI_BUSDEVFN 0xffff + +/** ISAPnP card select number field is invalid */ +#define UNDI_NO_ISAPNP_CSN 0xffff + +/** ISAPnP read port field is invalid */ +#define UNDI_NO_ISAPNP_READ_PORT 0xffff + +/** UNDI flag: START_UNDI has been called */ +#define UNDI_FL_STARTED 0x0001 + +/** UNDI flag: UNDI_STARTUP and UNDI_INITIALIZE have been called */ +#define UNDI_FL_INITIALIZED 0x0002 + +/** UNDI flag: keep stack resident */ +#define UNDI_FL_KEEP_ALL 0x0004 + +#endif /* _UNDI_H */ diff --git a/gpxe/src/arch/i386/include/undiload.h b/gpxe/src/arch/i386/include/undiload.h new file mode 100644 index 0000000..bfc1187 --- /dev/null +++ b/gpxe/src/arch/i386/include/undiload.h @@ -0,0 +1,33 @@ +#ifndef _UNDILOAD_H +#define _UNDILOAD_H + +/** @file + * + * UNDI load/unload + * + */ + +struct undi_device; +struct undi_rom; + +extern int undi_load ( struct undi_device *undi, struct undi_rom *undirom ); +extern int undi_unload ( struct undi_device *undi ); + +/** + * Call UNDI loader to create a pixie + * + * @v undi UNDI device + * @v undirom UNDI ROM + * @v pci_busdevfn PCI bus:dev.fn + * @ret rc Return status code + */ +static inline int undi_load_pci ( struct undi_device *undi, + struct undi_rom *undirom, + unsigned int pci_busdevfn ) { + undi->pci_busdevfn = pci_busdevfn; + undi->isapnp_csn = UNDI_NO_ISAPNP_CSN; + undi->isapnp_read_port = UNDI_NO_ISAPNP_READ_PORT; + return undi_load ( undi, undirom ); +} + +#endif /* _UNDILOAD_H */ diff --git a/gpxe/src/arch/i386/include/undinet.h b/gpxe/src/arch/i386/include/undinet.h new file mode 100644 index 0000000..1a4a385 --- /dev/null +++ b/gpxe/src/arch/i386/include/undinet.h @@ -0,0 +1,15 @@ +#ifndef _UNDINET_H +#define _UNDINET_H + +/** @file + * + * UNDI network device driver + * + */ + +struct undi_device; + +extern int undinet_probe ( struct undi_device *undi ); +extern void undinet_remove ( struct undi_device *undi ); + +#endif /* _UNDINET_H */ diff --git a/gpxe/src/arch/i386/include/undipreload.h b/gpxe/src/arch/i386/include/undipreload.h new file mode 100644 index 0000000..d9bc8cb --- /dev/null +++ b/gpxe/src/arch/i386/include/undipreload.h @@ -0,0 +1,16 @@ +#ifndef _UNDIPRELOAD_H +#define _UNDIPRELOAD_H + +/** @file + * + * Preloaded UNDI stack + * + */ + +#include +#include + +extern struct undi_device __data16 ( preloaded_undi ); +#define preloaded_undi __use_data16 ( preloaded_undi ) + +#endif /* _UNDIPRELOAD_H */ diff --git a/gpxe/src/arch/i386/include/undirom.h b/gpxe/src/arch/i386/include/undirom.h new file mode 100644 index 0000000..a263600 --- /dev/null +++ b/gpxe/src/arch/i386/include/undirom.h @@ -0,0 +1,51 @@ +#ifndef _UNDIROM_H +#define _UNDIROM_H + +/** @file + * + * UNDI expansion ROMs + * + */ + +#include + +/** An UNDI PCI device ID */ +struct undi_pci_device_id { + /** PCI vendor ID */ + unsigned int vendor_id; + /** PCI device ID */ + unsigned int device_id; +}; + +/** An UNDI device ID */ +union undi_device_id { + /** PCI device ID */ + struct undi_pci_device_id pci; +}; + +/** An UNDI ROM */ +struct undi_rom { + /** List of UNDI ROMs */ + struct list_head list; + /** ROM segment address */ + unsigned int rom_segment; + /** UNDI loader entry point */ + SEGOFF16_t loader_entry; + /** Code segment size */ + size_t code_size; + /** Data segment size */ + size_t data_size; + /** Bus type + * + * Values are as used by @c PXENV_UNDI_GET_NIC_TYPE + */ + unsigned int bus_type; + /** Device ID */ + union undi_device_id bus_id; +}; + +extern struct undi_rom * undirom_find_pci ( unsigned int vendor_id, + unsigned int device_id, + unsigned int rombase ); + +#endif /* _UNDIROM_H */ diff --git a/gpxe/src/arch/i386/include/vga.h b/gpxe/src/arch/i386/include/vga.h new file mode 100644 index 0000000..01fc39d --- /dev/null +++ b/gpxe/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/gpxe/src/arch/i386/interface/pcbios/abft.c b/gpxe/src/arch/i386/interface/pcbios/abft.c new file mode 100644 index 0000000..af28bbc --- /dev/null +++ b/gpxe/src/arch/i386/interface/pcbios/abft.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include + +/** @file + * + * AoE Boot Firmware Table + * + */ + +#define abftab __use_data16 ( abftab ) +/** The aBFT used by gPXE */ +struct abft_table __data16 ( abftab ) __attribute__ (( aligned ( 16 ) )) = { + /* ACPI header */ + .acpi = { + .signature = ABFT_SIG, + .length = sizeof ( abftab ), + .revision = 1, + .oem_id = "FENSYS", + .oem_table_id = "gPXE", + }, +}; + +/** + * Fill in all variable portions of aBFT + * + * @v aoe AoE session + */ +void abft_fill_data ( struct aoe_session *aoe ) { + + /* Fill in boot parameters */ + abftab.shelf = aoe->major; + abftab.slot = aoe->minor; + memcpy ( abftab.mac, aoe->netdev->ll_addr, sizeof ( abftab.mac ) ); + + /* Update checksum */ + acpi_fix_checksum ( &abftab.acpi ); + + DBG ( "AoE boot firmware table:\n" ); + DBG_HD ( &abftab, sizeof ( abftab ) ); +} diff --git a/gpxe/src/arch/i386/interface/pcbios/aoeboot.c b/gpxe/src/arch/i386/interface/pcbios/aoeboot.c new file mode 100644 index 0000000..6e1e51c --- /dev/null +++ b/gpxe/src/arch/i386/interface/pcbios/aoeboot.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int aoeboot ( const char *root_path ) { + struct ata_device ata; + struct int13_drive drive; + int rc; + + memset ( &ata, 0, sizeof ( ata ) ); + memset ( &drive, 0, sizeof ( drive ) ); + + printf ( "AoE booting from %s\n", root_path ); + + /* FIXME: ugly, ugly hack */ + struct net_device *netdev = last_opened_netdev(); + + if ( ( rc = aoe_attach ( &ata, netdev, root_path ) ) != 0 ) { + printf ( "Could not attach AoE device: %s\n", + strerror ( rc ) ); + goto error_attach; + } + if ( ( rc = init_atadev ( &ata ) ) != 0 ) { + printf ( "Could not initialise AoE device: %s\n", + strerror ( rc ) ); + goto error_init; + } + + /* FIXME: ugly, ugly hack */ + struct aoe_session *aoe = + container_of ( ata.backend, struct aoe_session, refcnt ); + abft_fill_data ( aoe ); + + drive.blockdev = &ata.blockdev; + + register_int13_drive ( &drive ); + printf ( "Registered as BIOS drive %#02x\n", drive.drive ); + printf ( "Booting from BIOS drive %#02x\n", drive.drive ); + rc = int13_boot ( drive.drive ); + printf ( "Boot failed\n" ); + + printf ( "Unregistering BIOS drive %#02x\n", drive.drive ); + unregister_int13_drive ( &drive ); + + error_init: + aoe_detach ( &ata ); + error_attach: + return rc; +} + +struct sanboot_protocol aoe_sanboot_protocol __sanboot_protocol = { + .prefix = "aoe:", + .boot = aoeboot, +}; diff --git a/gpxe/src/arch/i386/interface/pcbios/bios_nap.c b/gpxe/src/arch/i386/interface/pcbios/bios_nap.c new file mode 100644 index 0000000..2f4a051 --- /dev/null +++ b/gpxe/src/arch/i386/interface/pcbios/bios_nap.c @@ -0,0 +1,14 @@ +#include +#include + +/** + * Save power by halting the CPU until the next interrupt + * + */ +static void bios_cpu_nap ( void ) { + __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" + "hlt\n\t" + "cli\n\t" ) : : ); +} + +PROVIDE_NAP ( pcbios, cpu_nap, bios_cpu_nap ); diff --git a/gpxe/src/arch/i386/interface/pcbios/bios_smbios.c b/gpxe/src/arch/i386/interface/pcbios/bios_smbios.c new file mode 100644 index 0000000..efaaef0 --- /dev/null +++ b/gpxe/src/arch/i386/interface/pcbios/bios_smbios.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * System Management BIOS + * + */ + +/** + * Find SMBIOS + * + * @v smbios SMBIOS entry point descriptor structure to fill in + * @ret rc Return status code + */ +static int bios_find_smbios ( struct smbios *smbios ) { + union { + struct smbios_entry entry; + uint8_t bytes[256]; /* 256 is maximum length possible */ + } u; + static unsigned int offset = 0; + size_t len; + unsigned int i; + uint8_t sum; + + /* Try to find SMBIOS */ + for ( ; offset < 0x10000 ; offset += 0x10 ) { + + /* Read start of header and verify signature */ + copy_from_real ( &u.entry, BIOS_SEG, offset, + sizeof ( u.entry )); + if ( u.entry.signature != SMBIOS_SIGNATURE ) + continue; + + /* Read whole header and verify checksum */ + len = u.entry.len; + copy_from_real ( &u.bytes, BIOS_SEG, offset, len ); + for ( i = 0 , sum = 0 ; i < len ; i++ ) { + sum += u.bytes[i]; + } + if ( sum != 0 ) { + DBG ( "SMBIOS at %04x:%04x has bad checksum %02x\n", + BIOS_SEG, offset, sum ); + continue; + } + + /* Fill result structure */ + DBG ( "Found SMBIOS v%d.%d entry point at %04x:%04x\n", + u.entry.major, u.entry.minor, BIOS_SEG, offset ); + smbios->address = phys_to_user ( u.entry.smbios_address ); + smbios->len = u.entry.smbios_len; + smbios->count = u.entry.smbios_count; + return 0; + } + + DBG ( "No SMBIOS found\n" ); + return -ENODEV; +} + +PROVIDE_SMBIOS ( pcbios, find_smbios, bios_find_smbios ); diff --git a/gpxe/src/arch/i386/interface/pcbios/bios_timer.c b/gpxe/src/arch/i386/interface/pcbios/bios_timer.c new file mode 100644 index 0000000..0b475ea --- /dev/null +++ b/gpxe/src/arch/i386/interface/pcbios/bios_timer.c @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2008 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. + */ + +/** @file + * + * BIOS timer + * + */ + +#include +#include +#include + +/** + * Get current system time in ticks + * + * @ret ticks Current time, in ticks + * + * Use direct memory access to BIOS variables, longword 0040:006C + * (ticks today) and byte 0040:0070 (midnight crossover flag) instead + * of calling timeofday BIOS interrupt. + */ +static unsigned long bios_currticks ( void ) { + static int days = 0; + uint32_t ticks; + uint8_t midnight; + + /* Re-enable interrupts so that the timer interrupt can occur */ + __asm__ __volatile__ ( REAL_CODE ( "sti\n\t" + "nop\n\t" + "nop\n\t" + "cli\n\t" ) : : ); + + get_real ( ticks, BDA_SEG, 0x006c ); + get_real ( midnight, BDA_SEG, 0x0070 ); + + if ( midnight ) { + midnight = 0; + put_real ( midnight, BDA_SEG, 0x0070 ); + days += 0x1800b0; + } + + return ( days + ticks ); +} + +PROVIDE_TIMER_INLINE ( pcbios, udelay ); +PROVIDE_TIMER ( pcbios, currticks, bios_currticks ); +PROVIDE_TIMER_INLINE ( pcbios, ticks_per_sec ); diff --git a/gpxe/src/arch/i386/interface/pcbios/biosint.c b/gpxe/src/arch/i386/interface/pcbios/biosint.c new file mode 100644 index 0000000..1306f91 --- /dev/null +++ b/gpxe/src/arch/i386/interface/pcbios/biosint.c @@ -0,0 +1,90 @@ +#include +#include +#include + +/** + * @file BIOS interrupts + * + */ + +/** + * Hook INT vector + * + * @v interrupt INT number + * @v handler Offset within .text16 to interrupt handler + * @v chain_vector Vector for chaining to previous handler + * + * Hooks in an i386 INT handler. The handler itself must reside + * within the .text16 segment. @c chain_vector will be filled in with + * the address of the previously-installed handler for this interrupt; + * the handler should probably exit by ljmping via this vector. + */ +void hook_bios_interrupt ( unsigned int interrupt, unsigned int handler, + struct segoff *chain_vector ) { + struct segoff vector = { + .segment = rm_cs, + .offset = handler, + }; + + DBG ( "Hooking INT %#02x to %04x:%04x\n", + interrupt, rm_cs, handler ); + + if ( ( chain_vector->segment != 0 ) || + ( chain_vector->offset != 0 ) ) { + /* Already hooked; do nothing */ + DBG ( "...already hooked\n" ); + return; + } + + copy_from_real ( chain_vector, 0, ( interrupt * 4 ), + sizeof ( *chain_vector ) ); + DBG ( "...chaining to %04x:%04x\n", + chain_vector->segment, chain_vector->offset ); + if ( DBG_LOG ) { + char code[64]; + copy_from_real ( code, chain_vector->segment, + chain_vector->offset, sizeof ( code ) ); + DBG_HDA ( *chain_vector, code, sizeof ( code ) ); + } + + copy_to_real ( 0, ( interrupt * 4 ), &vector, sizeof ( vector ) ); + hooked_bios_interrupts++; +} + +/** + * Unhook INT vector + * + * @v interrupt INT number + * @v handler Offset within .text16 to interrupt handler + * @v chain_vector Vector containing address of previous handler + * + * Unhooks an i386 interrupt handler hooked by hook_i386_vector(). + * Note that this operation may fail, if some external code has hooked + * the vector since we hooked in our handler. If it fails, it means + * that it is not possible to unhook our handler, and we must leave it + * (and its chaining vector) resident in memory. + */ +int unhook_bios_interrupt ( unsigned int interrupt, unsigned int handler, + struct segoff *chain_vector ) { + struct segoff vector; + + DBG ( "Unhooking INT %#02x from %04x:%04x\n", + interrupt, rm_cs, handler ); + + copy_from_real ( &vector, 0, ( interrupt * 4 ), sizeof ( vector ) ); + if ( ( vector.segment != rm_cs ) || ( vector.offset != handler ) ) { + DBG ( "...cannot unhook; vector points to %04x:%04x\n", + vector.segment, vector.offset ); + return -EBUSY; + } + + DBG ( "...restoring to %04x:%04x\n", + chain_vector->segment, chain_vector->offset ); + copy_to_real ( 0, ( interrupt * 4 ), chain_vector, + sizeof ( *chain_vector ) ); + + chain_vector->segment = 0; + chain_vector->offset = 0; + hooked_bios_interrupts--; + return 0; +} diff --git a/gpxe/src/arch/i386/interface/pcbios/ibft.c b/gpxe/src/arch/i386/interface/pcbios/ibft.c new file mode 100644 index 0000000..43d1f85 --- /dev/null +++ b/gpxe/src/arch/i386/interface/pcbios/ibft.c @@ -0,0 +1,444 @@ +/* + * Copyright Fen Systems Ltd. 2007. Portions of this code are derived + * from IBM Corporation Sample Programs. Copyright IBM Corporation + * 2004, 2007. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * iSCSI boot firmware table + * + * The information in this file is derived from the document "iSCSI + * Boot Firmware Table (iBFT)" as published by IBM at + * + * ftp://ftp.software.ibm.com/systems/support/system_x_pdf/ibm_iscsi_boot_firmware_table_v1.02.pdf + * + */ + +#define ibftab __use_data16 ( ibftab ) +/** The iBFT used by gPXE */ +struct gpxe_ibft __data16 ( ibftab ) = { + /* Table header */ + .table = { + /* ACPI header */ + .acpi = { + .signature = IBFT_SIG, + .length = sizeof ( ibftab ), + .revision = 1, + .oem_id = "FENSYS", + .oem_table_id = "gPXE", + }, + /* Control block */ + .control = { + .header = { + .structure_id = IBFT_STRUCTURE_ID_CONTROL, + .version = 1, + .length = sizeof ( ibftab.table.control ), + .flags = 0, + }, + .initiator = offsetof ( typeof ( ibftab ), initiator ), + .nic_0 = offsetof ( typeof ( ibftab ), nic ), + .target_0 = offsetof ( typeof ( ibftab ), target ), + }, + }, + /* iSCSI initiator information */ + .initiator = { + .header = { + .structure_id = IBFT_STRUCTURE_ID_INITIATOR, + .version = 1, + .length = sizeof ( ibftab.initiator ), + .flags = ( IBFT_FL_INITIATOR_BLOCK_VALID | + IBFT_FL_INITIATOR_FIRMWARE_BOOT_SELECTED ), + }, + }, + /* NIC information */ + .nic = { + .header = { + .structure_id = IBFT_STRUCTURE_ID_NIC, + .version = 1, + .length = sizeof ( ibftab.nic ), + .flags = ( IBFT_FL_NIC_BLOCK_VALID | + IBFT_FL_NIC_FIRMWARE_BOOT_SELECTED ), + }, + }, + /* iSCSI target information */ + .target = { + .header = { + .structure_id = IBFT_STRUCTURE_ID_TARGET, + .version = 1, + .length = sizeof ( ibftab.target ), + .flags = ( IBFT_FL_TARGET_BLOCK_VALID | + IBFT_FL_TARGET_FIRMWARE_BOOT_SELECTED ), + }, + }, +}; + +/** + * Fill in an IP address field within iBFT + * + * @v ipaddr IP address field + * @v in IPv4 address + */ +static void ibft_set_ipaddr ( struct ibft_ipaddr *ipaddr, struct in_addr in ) { + memset ( ipaddr, 0, sizeof ( ipaddr ) ); + if ( in.s_addr ) { + ipaddr->in = in; + ipaddr->ones = 0xffff; + } +} + +/** + * Fill in an IP address within iBFT from configuration setting + * + * @v ipaddr IP address field + * @v setting Configuration setting + * @v tag DHCP option tag + */ +static void ibft_set_ipaddr_option ( struct ibft_ipaddr *ipaddr, + struct setting *setting ) { + struct in_addr in = { 0 }; + fetch_ipv4_setting ( NULL, setting, &in ); + ibft_set_ipaddr ( ipaddr, in ); +} + +/** + * Read IP address from iBFT (for debugging) + * + * @v strings iBFT string block descriptor + * @v string String field + * @ret ipaddr IP address string + */ +static const char * ibft_ipaddr ( struct ibft_ipaddr *ipaddr ) { + return inet_ntoa ( ipaddr->in ); +} + +/** + * Allocate a string within iBFT + * + * @v strings iBFT string block descriptor + * @v string String field to fill in + * @v len Length of string to allocate (excluding NUL) + * @ret rc Return status code + */ +static int ibft_alloc_string ( struct ibft_string_block *strings, + struct ibft_string *string, size_t len ) { + char *dest; + unsigned int remaining; + + dest = ( ( ( char * ) strings->table ) + strings->offset ); + remaining = ( strings->table->acpi.length - strings->offset ); + if ( len >= remaining ) + return -ENOMEM; + + string->offset = strings->offset; + string->length = len; + strings->offset += ( len + 1 ); + return 0; +} + +/** + * Fill in a string field within iBFT + * + * @v strings iBFT string block descriptor + * @v string String field + * @v data String to fill in, or NULL + * @ret rc Return status code + */ +static int ibft_set_string ( struct ibft_string_block *strings, + struct ibft_string *string, const char *data ) { + char *dest; + int rc; + + if ( ! data ) + return 0; + + if ( ( rc = ibft_alloc_string ( strings, string, + strlen ( data ) ) ) != 0 ) + return rc; + dest = ( ( ( char * ) strings->table ) + string->offset ); + strcpy ( dest, data ); + + return 0; +} + +/** + * Fill in a string field within iBFT from configuration setting + * + * @v strings iBFT string block descriptor + * @v string String field + * @v setting Configuration setting + * @ret rc Return status code + */ +static int ibft_set_string_option ( struct ibft_string_block *strings, + struct ibft_string *string, + struct setting *setting ) { + int len; + char *dest; + int rc; + + len = fetch_setting_len ( NULL, setting ); + if ( len < 0 ) { + string->offset = 0; + string->length = 0; + return 0; + } + + if ( ( rc = ibft_alloc_string ( strings, string, len ) ) != 0 ) + return rc; + dest = ( ( ( char * ) strings->table ) + string->offset ); + fetch_string_setting ( NULL, setting, dest, ( len + 1 ) ); + return 0; +} + +/** + * Read string from iBFT (for debugging) + * + * @v strings iBFT string block descriptor + * @v string String field + * @ret data String content (or "") + */ +static const char * ibft_string ( struct ibft_string_block *strings, + struct ibft_string *string ) { + return ( ( ( char * ) strings->table ) + string->offset ); +} + +/** + * Fill in NIC portion of iBFT + * + * @v nic NIC portion of iBFT + * @v strings iBFT string block descriptor + * @v netdev Network device + * @ret rc Return status code + */ +static int ibft_fill_nic ( struct ibft_nic *nic, + struct ibft_string_block *strings, + struct net_device *netdev ) { + struct in_addr netmask_addr = { 0 }; + unsigned int netmask_count = 0; + int rc; + + /* Extract values from DHCP configuration */ + ibft_set_ipaddr_option ( &nic->ip_address, &ip_setting ); + DBG ( "iBFT NIC IP = %s\n", ibft_ipaddr ( &nic->ip_address ) ); + ibft_set_ipaddr_option ( &nic->gateway, &gateway_setting ); + DBG ( "iBFT NIC gateway = %s\n", ibft_ipaddr ( &nic->gateway ) ); + ibft_set_ipaddr_option ( &nic->dns[0], &dns_setting ); + DBG ( "iBFT NIC DNS = %s\n", ibft_ipaddr ( &nic->dns[0] ) ); + if ( ( rc = ibft_set_string_option ( strings, &nic->hostname, + &hostname_setting ) ) != 0 ) + return rc; + DBG ( "iBFT NIC hostname = %s\n", + ibft_string ( strings, &nic->hostname ) ); + + /* Derive subnet mask prefix from subnet mask */ + fetch_ipv4_setting ( NULL, &netmask_setting, &netmask_addr ); + while ( netmask_addr.s_addr ) { + if ( netmask_addr.s_addr & 0x1 ) + netmask_count++; + netmask_addr.s_addr >>= 1; + } + nic->subnet_mask_prefix = netmask_count; + DBG ( "iBFT NIC subnet = /%d\n", nic->subnet_mask_prefix ); + + /* Extract values from net-device configuration */ + memcpy ( nic->mac_address, netdev->ll_addr, + sizeof ( nic->mac_address ) ); + DBG ( "iBFT NIC MAC = %s\n", + netdev->ll_protocol->ntoa ( nic->mac_address ) ); + nic->pci_bus_dev_func = netdev->dev->desc.location; + DBG ( "iBFT NIC PCI = %04x\n", nic->pci_bus_dev_func ); + + return 0; +} + +/** + * Fill in Initiator portion of iBFT + * + * @v initiator Initiator portion of iBFT + * @v strings iBFT string block descriptor + * @ret rc Return status code + */ +static int ibft_fill_initiator ( struct ibft_initiator *initiator, + struct ibft_string_block *strings ) { + const char *initiator_iqn = iscsi_initiator_iqn(); + int rc; + + if ( ( rc = ibft_set_string ( strings, &initiator->initiator_name, + initiator_iqn ) ) != 0 ) + return rc; + DBG ( "iBFT initiator hostname = %s\n", + ibft_string ( strings, &initiator->initiator_name ) ); + + return 0; +} + +/** + * Fill in Target CHAP portion of iBFT + * + * @v target Target portion of iBFT + * @v strings iBFT string block descriptor + * @v iscsi iSCSI session + * @ret rc Return status code + */ +static int ibft_fill_target_chap ( struct ibft_target *target, + struct ibft_string_block *strings, + struct iscsi_session *iscsi ) { + int rc; + + if ( ! ( iscsi->status & ISCSI_STATUS_AUTH_FORWARD_REQUIRED ) ) + return 0; + + assert ( iscsi->initiator_username ); + assert ( iscsi->initiator_password ); + + target->chap_type = IBFT_CHAP_ONE_WAY; + if ( ( rc = ibft_set_string ( strings, &target->chap_name, + iscsi->initiator_username ) ) != 0 ) + return rc; + DBG ( "iBFT target username = %s\n", + ibft_string ( strings, &target->chap_name ) ); + if ( ( rc = ibft_set_string ( strings, &target->chap_secret, + iscsi->initiator_password ) ) != 0 ) + return rc; + DBG ( "iBFT target password = \n" ); + + return 0; +} + +/** + * Fill in Target Reverse CHAP portion of iBFT + * + * @v target Target portion of iBFT + * @v strings iBFT string block descriptor + * @v iscsi iSCSI session + * @ret rc Return status code + */ +static int ibft_fill_target_reverse_chap ( struct ibft_target *target, + struct ibft_string_block *strings, + struct iscsi_session *iscsi ) { + int rc; + + if ( ! ( iscsi->status & ISCSI_STATUS_AUTH_REVERSE_REQUIRED ) ) + return 0; + + assert ( iscsi->initiator_username ); + assert ( iscsi->initiator_password ); + assert ( iscsi->target_username ); + assert ( iscsi->target_password ); + + target->chap_type = IBFT_CHAP_MUTUAL; + if ( ( rc = ibft_set_string ( strings, &target->reverse_chap_name, + iscsi->target_username ) ) != 0 ) + return rc; + DBG ( "iBFT target reverse username = %s\n", + ibft_string ( strings, &target->chap_name ) ); + if ( ( rc = ibft_set_string ( strings, &target->reverse_chap_secret, + iscsi->target_password ) ) != 0 ) + return rc; + DBG ( "iBFT target reverse password = \n" ); + + return 0; +} + +/** + * Fill in Target portion of iBFT + * + * @v target Target portion of iBFT + * @v strings iBFT string block descriptor + * @v iscsi iSCSI session + * @ret rc Return status code + */ +static int ibft_fill_target ( struct ibft_target *target, + struct ibft_string_block *strings, + struct iscsi_session *iscsi ) { + struct sockaddr_in *sin_target = + ( struct sockaddr_in * ) &iscsi->target_sockaddr; + int rc; + + /* Fill in Target values */ + ibft_set_ipaddr ( &target->ip_address, sin_target->sin_addr ); + DBG ( "iBFT target IP = %s\n", ibft_ipaddr ( &target->ip_address ) ); + target->socket = ntohs ( sin_target->sin_port ); + DBG ( "iBFT target port = %d\n", target->socket ); + if ( ( rc = ibft_set_string ( strings, &target->target_name, + iscsi->target_iqn ) ) != 0 ) + return rc; + DBG ( "iBFT target name = %s\n", + ibft_string ( strings, &target->target_name ) ); + if ( ( rc = ibft_fill_target_chap ( target, strings, iscsi ) ) != 0 ) + return rc; + if ( ( rc = ibft_fill_target_reverse_chap ( target, strings, + iscsi ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Fill in all variable portions of iBFT + * + * @v netdev Network device + * @v initiator_iqn Initiator IQN + * @v st_target Target socket address + * @v target_iqn Target IQN + * @ret rc Return status code + * + */ +int ibft_fill_data ( struct net_device *netdev, + struct iscsi_session *iscsi ) { + struct ibft_string_block strings = { + .table = &ibftab.table, + .offset = offsetof ( typeof ( ibftab ), strings ), + }; + int rc; + + /* Fill in NIC, Initiator and Target portions */ + if ( ( rc = ibft_fill_nic ( &ibftab.nic, &strings, netdev ) ) != 0 ) + return rc; + if ( ( rc = ibft_fill_initiator ( &ibftab.initiator, + &strings ) ) != 0 ) + return rc; + if ( ( rc = ibft_fill_target ( &ibftab.target, &strings, + iscsi ) ) != 0 ) + return rc; + + /* Update checksum */ + acpi_fix_checksum ( &ibftab.table.acpi ); + + return 0; +} diff --git a/gpxe/src/arch/i386/interface/pcbios/int13.c b/gpxe/src/arch/i386/interface/pcbios/int13.c new file mode 100644 index 0000000..2e9de5c --- /dev/null +++ b/gpxe/src/arch/i386/interface/pcbios/int13.c @@ -0,0 +1,660 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * INT 13 emulation + * + * This module provides a mechanism for exporting block devices via + * the BIOS INT 13 disk interrupt interface. + * + */ + +/** Vector for chaining to other INT 13 handlers */ +static struct segoff __text16 ( int13_vector ); +#define int13_vector __use_text16 ( int13_vector ) + +/** Assembly wrapper */ +extern void int13_wrapper ( void ); + +/** List of registered emulated drives */ +static LIST_HEAD ( drives ); + +/** + * INT 13, 00 - Reset disk system + * + * @v drive Emulated drive + * @ret status Status code + */ +static int int13_reset ( struct int13_drive *drive __unused, + struct i386_all_regs *ix86 __unused ) { + DBG ( "Reset drive\n" ); + return 0; +} + +/** + * INT 13, 01 - Get status of last operation + * + * @v drive Emulated drive + * @ret status Status code + */ +static int int13_get_last_status ( struct int13_drive *drive, + struct i386_all_regs *ix86 __unused ) { + DBG ( "Get status of last operation\n" ); + return drive->last_status; +} + +/** + * Read / write sectors + * + * @v drive Emulated drive + * @v al Number of sectors to read or write (must be nonzero) + * @v ch Low bits of cylinder number + * @v cl (bits 7:6) High bits of cylinder number + * @v cl (bits 5:0) Sector number + * @v dh Head number + * @v es:bx Data buffer + * @v io Read / write method + * @ret status Status code + * @ret al Number of sectors read or written + */ +static int int13_rw_sectors ( struct int13_drive *drive, + struct i386_all_regs *ix86, + int ( * io ) ( struct block_device *blockdev, + uint64_t block, + unsigned long count, + userptr_t buffer ) ) { + struct block_device *blockdev = drive->blockdev; + unsigned int cylinder, head, sector; + unsigned long lba; + unsigned int count; + userptr_t buffer; + + /* Validate blocksize */ + if ( blockdev->blksize != INT13_BLKSIZE ) { + DBG ( "Invalid blocksize (%zd) for non-extended read/write\n", + blockdev->blksize ); + return -INT13_STATUS_INVALID; + } + + /* Calculate parameters */ + cylinder = ( ( ( ix86->regs.cl & 0xc0 ) << 2 ) | ix86->regs.ch ); + assert ( cylinder < drive->cylinders ); + head = ix86->regs.dh; + assert ( head < drive->heads ); + sector = ( ix86->regs.cl & 0x3f ); + assert ( ( sector >= 1 ) && ( sector <= drive->sectors_per_track ) ); + lba = ( ( ( ( cylinder * drive->heads ) + head ) + * drive->sectors_per_track ) + sector - 1 ); + count = ix86->regs.al; + buffer = real_to_user ( ix86->segs.es, ix86->regs.bx ); + + DBG ( "C/H/S %d/%d/%d = LBA %#lx <-> %04x:%04x (count %d)\n", cylinder, + head, sector, lba, ix86->segs.es, ix86->regs.bx, count ); + + /* Read from / write to block device */ + if ( io ( blockdev, lba, count, buffer ) != 0 ) + return -INT13_STATUS_READ_ERROR; + + return 0; +} + +/** + * INT 13, 02 - Read sectors + * + * @v drive Emulated drive + * @v al Number of sectors to read (must be nonzero) + * @v ch Low bits of cylinder number + * @v cl (bits 7:6) High bits of cylinder number + * @v cl (bits 5:0) Sector number + * @v dh Head number + * @v es:bx Data buffer + * @ret status Status code + * @ret al Number of sectors read + */ +static int int13_read_sectors ( struct int13_drive *drive, + struct i386_all_regs *ix86 ) { + DBG ( "Read: " ); + return int13_rw_sectors ( drive, ix86, drive->blockdev->op->read ); +} + +/** + * INT 13, 03 - Write sectors + * + * @v drive Emulated drive + * @v al Number of sectors to write (must be nonzero) + * @v ch Low bits of cylinder number + * @v cl (bits 7:6) High bits of cylinder number + * @v cl (bits 5:0) Sector number + * @v dh Head number + * @v es:bx Data buffer + * @ret status Status code + * @ret al Number of sectors written + */ +static int int13_write_sectors ( struct int13_drive *drive, + struct i386_all_regs *ix86 ) { + DBG ( "Write: " ); + return int13_rw_sectors ( drive, ix86, drive->blockdev->op->write ); +} + +/** + * INT 13, 08 - Get drive parameters + * + * @v drive Emulated drive + * @ret status Status code + * @ret ch Low bits of maximum cylinder number + * @ret cl (bits 7:6) High bits of maximum cylinder number + * @ret cl (bits 5:0) Maximum sector number + * @ret dh Maximum head number + * @ret dl Number of drives + */ +static int int13_get_parameters ( struct int13_drive *drive, + struct i386_all_regs *ix86 ) { + unsigned int max_cylinder = drive->cylinders - 1; + unsigned int max_head = drive->heads - 1; + unsigned int max_sector = drive->sectors_per_track; /* sic */ + + DBG ( "Get drive parameters\n" ); + + ix86->regs.ch = ( max_cylinder & 0xff ); + ix86->regs.cl = ( ( ( max_cylinder >> 8 ) << 6 ) | max_sector ); + ix86->regs.dh = max_head; + get_real ( ix86->regs.dl, BDA_SEG, BDA_NUM_DRIVES ); + return 0; +} + +/** + * INT 13, 15 - Get disk type + * + * @v drive Emulated drive + * @ret ah Type code + * @ret cx:dx Sector count + * @ret status Status code / disk type + */ +static int int13_get_disk_type ( struct int13_drive *drive, + struct i386_all_regs *ix86 ) { + DBG ( "Get disk type\n" ); + ix86->regs.cx = ( drive->cylinders >> 16 ); + ix86->regs.dx = ( drive->cylinders & 0xffff ); + return INT13_DISK_TYPE_HDD; +} + +/** + * INT 13, 41 - Extensions installation check + * + * @v drive Emulated drive + * @v bx 0x55aa + * @ret bx 0xaa55 + * @ret cx Extensions API support bitmap + * @ret status Status code / API version + */ +static int int13_extension_check ( struct int13_drive *drive __unused, + struct i386_all_regs *ix86 ) { + if ( ix86->regs.bx == 0x55aa ) { + DBG ( "INT 13 extensions installation check\n" ); + ix86->regs.bx = 0xaa55; + ix86->regs.cx = INT13_EXTENSION_LINEAR; + return INT13_EXTENSION_VER_1_X; + } else { + return -INT13_STATUS_INVALID; + } +} + +/** + * Extended read / write + * + * @v drive Emulated drive + * @v ds:si Disk address packet + * @v io Read / write method + * @ret status Status code + */ +static int int13_extended_rw ( struct int13_drive *drive, + struct i386_all_regs *ix86, + int ( * io ) ( struct block_device *blockdev, + uint64_t block, + unsigned long count, + userptr_t buffer ) ) { + struct block_device *blockdev = drive->blockdev; + struct int13_disk_address addr; + uint64_t lba; + unsigned long count; + userptr_t buffer; + + /* Read parameters from disk address structure */ + copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si, sizeof ( addr )); + lba = addr.lba; + count = addr.count; + buffer = real_to_user ( addr.buffer.segment, addr.buffer.offset ); + + DBG ( "LBA %#llx <-> %04x:%04x (count %ld)\n", (unsigned long long)lba, + addr.buffer.segment, addr.buffer.offset, count ); + + /* Read from / write to block device */ + if ( io ( blockdev, lba, count, buffer ) != 0 ) + return -INT13_STATUS_READ_ERROR; + + return 0; +} + +/** + * INT 13, 42 - Extended read + * + * @v drive Emulated drive + * @v ds:si Disk address packet + * @ret status Status code + */ +static int int13_extended_read ( struct int13_drive *drive, + struct i386_all_regs *ix86 ) { + DBG ( "Extended read: " ); + return int13_extended_rw ( drive, ix86, drive->blockdev->op->read ); +} + +/** + * INT 13, 43 - Extended write + * + * @v drive Emulated drive + * @v ds:si Disk address packet + * @ret status Status code + */ +static int int13_extended_write ( struct int13_drive *drive, + struct i386_all_regs *ix86 ) { + DBG ( "Extended write: " ); + return int13_extended_rw ( drive, ix86, drive->blockdev->op->write ); +} + +/** + * INT 13, 48 - Get extended parameters + * + * @v drive Emulated drive + * @v ds:si Drive parameter table + * @ret status Status code + */ +static int int13_get_extended_parameters ( struct int13_drive *drive, + struct i386_all_regs *ix86 ) { + struct int13_disk_parameters params = { + .bufsize = sizeof ( params ), + .flags = INT13_FL_DMA_TRANSPARENT, + .cylinders = drive->cylinders, + .heads = drive->heads, + .sectors_per_track = drive->sectors_per_track, + .sectors = drive->blockdev->blocks, + .sector_size = drive->blockdev->blksize, + }; + + DBG ( "Get extended drive parameters to %04x:%04x\n", + ix86->segs.ds, ix86->regs.si ); + + copy_to_real ( ix86->segs.ds, ix86->regs.si, ¶ms, + sizeof ( params ) ); + return 0; +} + +/** + * INT 13 handler + * + */ +static __asmcall void int13 ( struct i386_all_regs *ix86 ) { + int command = ix86->regs.ah; + unsigned int bios_drive = ix86->regs.dl; + struct int13_drive *drive; + int status; + + list_for_each_entry ( drive, &drives, list ) { + + if ( bios_drive != drive->drive ) { + /* Remap any accesses to this drive's natural number */ + if ( bios_drive == drive->natural_drive ) { + DBG ( "INT 13,%04x (%02x) remapped to " + "(%02x)\n", ix86->regs.ax, + bios_drive, drive->drive ); + ix86->regs.dl = drive->drive; + return; + } + continue; + } + + DBG ( "INT 13,%04x (%02x): ", ix86->regs.ax, drive->drive ); + + switch ( command ) { + case INT13_RESET: + status = int13_reset ( drive, ix86 ); + break; + case INT13_GET_LAST_STATUS: + status = int13_get_last_status ( drive, ix86 ); + break; + case INT13_READ_SECTORS: + status = int13_read_sectors ( drive, ix86 ); + break; + case INT13_WRITE_SECTORS: + status = int13_write_sectors ( drive, ix86 ); + break; + case INT13_GET_PARAMETERS: + status = int13_get_parameters ( drive, ix86 ); + break; + case INT13_GET_DISK_TYPE: + status = int13_get_disk_type ( drive, ix86 ); + break; + case INT13_EXTENSION_CHECK: + status = int13_extension_check ( drive, ix86 ); + break; + case INT13_EXTENDED_READ: + status = int13_extended_read ( drive, ix86 ); + break; + case INT13_EXTENDED_WRITE: + status = int13_extended_write ( drive, ix86 ); + break; + case INT13_GET_EXTENDED_PARAMETERS: + status = int13_get_extended_parameters ( drive, ix86 ); + break; + default: + DBG ( "*** Unrecognised INT 13 ***\n" ); + status = -INT13_STATUS_INVALID; + break; + } + + /* Store status for INT 13,01 */ + drive->last_status = status; + + /* Negative status indicates an error */ + if ( status < 0 ) { + status = -status; + DBG ( "INT13 failed with status %x\n", status ); + } else { + ix86->flags &= ~CF; + } + ix86->regs.ah = status; + + /* Set OF to indicate to wrapper not to chain this call */ + ix86->flags |= OF; + + return; + } +} + +/** + * Hook INT 13 handler + * + */ +static void hook_int13 ( void ) { + /* Assembly wrapper to call int13(). int13() sets OF if we + * should not chain to the previous handler. (The wrapper + * clears CF and OF before calling int13()). + */ + __asm__ __volatile__ ( + TEXT16_CODE ( "\nint13_wrapper:\n\t" + /* Preserve %ax and %dx for future reference */ + "pushw %%bp\n\t" + "movw %%sp, %%bp\n\t" + "pushw %%ax\n\t" + "pushw %%dx\n\t" + /* Clear OF, set CF, call int13() */ + "orb $0, %%al\n\t" + "stc\n\t" + "pushl %0\n\t" + "pushw %%cs\n\t" + "call prot_call\n\t" + /* Chain if OF not set */ + "jo 1f\n\t" + "pushfw\n\t" + "lcall *%%cs:int13_vector\n\t" + "\n1:\n\t" + /* Overwrite flags for iret */ + "pushfw\n\t" + "popw 6(%%bp)\n\t" + /* Fix up %dl: + * + * INT 13,15 : do nothing + * INT 13,08 : load with number of drives + * all others: restore original value + */ + "cmpb $0x15, -1(%%bp)\n\t" + "je 2f\n\t" + "movb -4(%%bp), %%dl\n\t" + "cmpb $0x08, -1(%%bp)\n\t" + "jne 2f\n\t" + "pushw %%ds\n\t" + "pushw %1\n\t" + "popw %%ds\n\t" + "movb %c2, %%dl\n\t" + "popw %%ds\n\t" + /* Return */ + "\n2:\n\t" + "movw %%bp, %%sp\n\t" + "popw %%bp\n\t" + "iret\n\t" ) + : : "i" ( int13 ), "i" ( BDA_SEG ), "i" ( BDA_NUM_DRIVES ) ); + + hook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper, + &int13_vector ); +} + +/** + * Unhook INT 13 handler + */ +static void unhook_int13 ( void ) { + unhook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper, + &int13_vector ); +} + +/** + * Guess INT 13 drive geometry + * + * @v drive Emulated drive + * + * Guesses the drive geometry by inspecting the partition table. + */ +static void guess_int13_geometry ( struct int13_drive *drive ) { + struct master_boot_record mbr; + struct partition_table_entry *partition; + unsigned int guessed_heads = 255; + unsigned int guessed_sectors_per_track = 63; + unsigned long blocks; + unsigned long blocks_per_cyl; + unsigned int i; + + /* Don't even try when the blksize is invalid for C/H/S access */ + if ( drive->blockdev->blksize != INT13_BLKSIZE ) + return; + + /* Scan through partition table and modify guesses for heads + * and sectors_per_track if we find any used partitions. + */ + if ( drive->blockdev->op->read ( drive->blockdev, 0, 1, + virt_to_user ( &mbr ) ) == 0 ) { + for ( i = 0 ; i < 4 ; i++ ) { + partition = &mbr.partitions[i]; + if ( ! partition->type ) + continue; + guessed_heads = + ( PART_HEAD ( partition->chs_end ) + 1 ); + guessed_sectors_per_track = + PART_SECTOR ( partition->chs_end ); + DBG ( "Guessing C/H/S xx/%d/%d based on partition " + "%d\n", guessed_heads, + guessed_sectors_per_track, ( i + 1 ) ); + } + } else { + DBG ( "Could not read partition table to guess geometry\n" ); + } + + /* Apply guesses if no geometry already specified */ + if ( ! drive->heads ) + drive->heads = guessed_heads; + if ( ! drive->sectors_per_track ) + drive->sectors_per_track = guessed_sectors_per_track; + if ( ! drive->cylinders ) { + /* Avoid attempting a 64-bit divide on a 32-bit system */ + blocks = ( ( drive->blockdev->blocks <= ULONG_MAX ) ? + drive->blockdev->blocks : ULONG_MAX ); + blocks_per_cyl = ( drive->heads * drive->sectors_per_track ); + assert ( blocks_per_cyl != 0 ); + drive->cylinders = ( blocks / blocks_per_cyl ); + if ( drive->cylinders > 1024 ) + drive->cylinders = 1024; + } +} + +/** + * Register INT 13 emulated drive + * + * @v drive Emulated drive + * + * Registers the drive with the INT 13 emulation subsystem, and hooks + * the INT 13 interrupt vector (if not already hooked). + * + * The underlying block device must be valid. A drive number and + * geometry will be assigned if left blank. + */ +void register_int13_drive ( struct int13_drive *drive ) { + uint8_t num_drives; + + /* Give drive a default geometry if none specified */ + guess_int13_geometry ( drive ); + + /* Assign natural drive number */ + get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES ); + drive->natural_drive = ( num_drives | 0x80 ); + num_drives++; + + /* Assign drive number */ + if ( ( drive->drive & 0xff ) == 0xff ) { + /* Drive number == -1 => use natural drive number */ + drive->drive = drive->natural_drive; + } else { + /* Use specified drive number (+0x80 if necessary) */ + drive->drive |= 0x80; + if ( num_drives <= ( drive->drive & 0x7f ) ) + num_drives = ( ( drive->drive & 0x7f ) + 1 ); + } + + /* Update BIOS drive count */ + put_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES ); + + DBG ( "Registered INT13 drive %02x (naturally %02x) with C/H/S " + "geometry %d/%d/%d\n", drive->drive, drive->natural_drive, + drive->cylinders, drive->heads, drive->sectors_per_track ); + + /* Hook INT 13 vector if not already hooked */ + if ( list_empty ( &drives ) ) + hook_int13(); + + /* Add to list of emulated drives */ + list_add ( &drive->list, &drives ); +} + +/** + * Unregister INT 13 emulated drive + * + * @v drive Emulated drive + * + * Unregisters the drive from the INT 13 emulation subsystem. If this + * is the last emulated drive, the INT 13 vector is unhooked (if + * possible). + */ +void unregister_int13_drive ( struct int13_drive *drive ) { + /* Remove from list of emulated drives */ + list_del ( &drive->list ); + + /* Should adjust BIOS drive count, but it's difficult to do so + * reliably. + */ + + DBG ( "Unregistered INT13 drive %02x\n", drive->drive ); + + /* Unhook INT 13 vector if no more drives */ + if ( list_empty ( &drives ) ) + unhook_int13(); +} + +/** + * Attempt to boot from an INT 13 drive + * + * @v drive Drive number + * @ret rc Return status code + * + * This boots from the specified INT 13 drive by loading the Master + * Boot Record to 0000:7c00 and jumping to it. INT 18 is hooked to + * capture an attempt by the MBR to boot the next device. (This is + * the closest thing to a return path from an MBR). + * + * Note that this function can never return success, by definition. + */ +int int13_boot ( unsigned int drive ) { + struct memory_map memmap; + int status, signature; + int discard_c, discard_d; + int rc; + + DBG ( "Booting from INT 13 drive %02x\n", drive ); + + /* Use INT 13 to read the boot sector */ + __asm__ __volatile__ ( REAL_CODE ( "pushw %%es\n\t" + "pushw $0\n\t" + "popw %%es\n\t" + "stc\n\t" + "sti\n\t" + "int $0x13\n\t" + "sti\n\t" /* BIOS bugs */ + "jc 1f\n\t" + "xorl %%eax, %%eax\n\t" + "\n1:\n\t" + "movzwl %%es:0x7dfe, %%ebx\n\t" + "popw %%es\n\t" ) + : "=a" ( status ), "=b" ( signature ), + "=c" ( discard_c ), "=d" ( discard_d ) + : "a" ( 0x0201 ), "b" ( 0x7c00 ), + "c" ( 1 ), "d" ( drive ) ); + if ( status ) + return -EIO; + + /* Check signature is correct */ + if ( signature != be16_to_cpu ( 0x55aa ) ) { + DBG ( "Invalid disk signature %#04x (should be 0x55aa)\n", + cpu_to_be16 ( signature ) ); + return -ENOEXEC; + } + + /* Dump out memory map prior to boot, if memmap debugging is + * enabled. Not required for program flow, but we have so + * many problems that turn out to be memory-map related that + * it's worth doing. + */ + get_memmap ( &memmap ); + + /* Jump to boot sector */ + if ( ( rc = call_bootsector ( 0x0, 0x7c00, drive ) ) != 0 ) { + DBG ( "INT 13 drive %02x boot returned\n", drive ); + return rc; + } + + return -ECANCELED; /* -EIMPOSSIBLE */ +} diff --git a/gpxe/src/arch/i386/interface/pcbios/iscsiboot.c b/gpxe/src/arch/i386/interface/pcbios/iscsiboot.c new file mode 100644 index 0000000..02aec4b --- /dev/null +++ b/gpxe/src/arch/i386/interface/pcbios/iscsiboot.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct setting keep_san_setting __setting = { + .name = "keep-san", + .description = "Preserve SAN connection", + .tag = DHCP_EB_KEEP_SAN, + .type = &setting_type_int8, +}; + +static int iscsiboot ( const char *root_path ) { + struct scsi_device *scsi; + struct int13_drive *drive; + int keep_san; + int rc; + + scsi = zalloc ( sizeof ( *scsi ) ); + if ( ! scsi ) { + rc = -ENOMEM; + goto err_alloc_scsi; + } + drive = zalloc ( sizeof ( *drive ) ); + if ( ! drive ) { + rc = -ENOMEM; + goto err_alloc_drive; + } + + printf ( "iSCSI booting from %s\n", root_path ); + + if ( ( rc = iscsi_attach ( scsi, root_path ) ) != 0 ) { + printf ( "Could not attach iSCSI device: %s\n", + strerror ( rc ) ); + goto err_attach; + } + if ( ( rc = init_scsidev ( scsi ) ) != 0 ) { + printf ( "Could not initialise iSCSI device: %s\n", + strerror ( rc ) ); + goto err_init; + } + + drive->blockdev = &scsi->blockdev; + + /* FIXME: ugly, ugly hack */ + struct net_device *netdev = last_opened_netdev(); + struct iscsi_session *iscsi = + container_of ( scsi->backend, struct iscsi_session, refcnt ); + ibft_fill_data ( netdev, iscsi ); + + register_int13_drive ( drive ); + printf ( "Registered as BIOS drive %#02x\n", drive->drive ); + printf ( "Booting from BIOS drive %#02x\n", drive->drive ); + rc = int13_boot ( drive->drive ); + printf ( "Boot failed\n" ); + + /* Leave drive registered, if instructed to do so */ + keep_san = fetch_intz_setting ( NULL, &keep_san_setting ); + if ( keep_san ) { + printf ( "Preserving connection to SAN disk\n" ); + shutdown_exit_flags |= SHUTDOWN_KEEP_DEVICES; + return rc; + } + + printf ( "Unregistering BIOS drive %#02x\n", drive->drive ); + unregister_int13_drive ( drive ); + + err_init: + iscsi_detach ( scsi ); + err_attach: + free ( drive ); + err_alloc_drive: + free ( scsi ); + err_alloc_scsi: + return rc; +} + +struct sanboot_protocol iscsi_sanboot_protocol __sanboot_protocol = { + .prefix = "iscsi:", + .boot = iscsiboot, +}; diff --git a/gpxe/src/arch/i386/interface/pcbios/memtop_umalloc.c b/gpxe/src/arch/i386/interface/pcbios/memtop_umalloc.c new file mode 100644 index 0000000..744d8e3 --- /dev/null +++ b/gpxe/src/arch/i386/interface/pcbios/memtop_umalloc.c @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2007 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. + */ + +/** + * @file + * + * External memory allocation + * + */ + +#include +#include +#include +#include +#include +#include + +/** Alignment of external allocated memory */ +#define EM_ALIGN ( 4 * 1024 ) + +/** Equivalent of NOWHERE for user pointers */ +#define UNOWHERE ( ~UNULL ) + +/** An external memory block */ +struct external_memory { + /** Size of this memory block (excluding this header) */ + size_t size; + /** Block is currently in use */ + int used; +}; + +/** Top of heap */ +static userptr_t top = UNULL; + +/** Bottom of heap (current lowest allocated block) */ +static userptr_t bottom = UNULL; + +/** + * Initialise external heap + * + * @ret rc Return status code + */ +static int init_eheap ( void ) { + struct memory_map memmap; + unsigned long heap_size = 0; + unsigned int i; + + DBG ( "Allocating external heap\n" ); + + get_memmap ( &memmap ); + for ( i = 0 ; i < memmap.count ; i++ ) { + struct memory_region *region = &memmap.regions[i]; + unsigned long r_start, r_end; + unsigned long r_size; + + DBG ( "Considering [%llx,%llx)\n", region->start, region->end); + + /* Truncate block to 4GB */ + if ( region->start > UINT_MAX ) { + DBG ( "...starts after 4GB\n" ); + continue; + } + r_start = region->start; + if ( region->end > UINT_MAX ) { + DBG ( "...end truncated to 4GB\n" ); + r_end = 0; /* =4GB, given the wraparound */ + } else { + r_end = region->end; + } + + /* Use largest block */ + r_size = ( r_end - r_start ); + if ( r_size > heap_size ) { + DBG ( "...new best block found\n" ); + top = bottom = phys_to_user ( r_end ); + heap_size = r_size; + } + } + + if ( ! heap_size ) { + DBG ( "No external heap available\n" ); + return -ENOMEM; + } + + DBG ( "External heap grows downwards from %lx\n", + user_to_phys ( top, 0 ) ); + return 0; +} + +/** + * Collect free blocks + * + */ +static void ecollect_free ( void ) { + struct external_memory extmem; + + /* Walk the free list and collect empty blocks */ + while ( bottom != top ) { + copy_from_user ( &extmem, bottom, -sizeof ( extmem ), + sizeof ( extmem ) ); + if ( extmem.used ) + break; + DBG ( "EXTMEM freeing [%lx,%lx)\n", user_to_phys ( bottom, 0 ), + user_to_phys ( bottom, extmem.size ) ); + bottom = userptr_add ( bottom, + ( extmem.size + sizeof ( extmem ) ) ); + } +} + +/** + * Reallocate external memory + * + * @v old_ptr Memory previously allocated by umalloc(), or UNULL + * @v new_size Requested size + * @ret new_ptr Allocated memory, or UNULL + * + * Calling realloc() with a new size of zero is a valid way to free a + * memory block. + */ +static userptr_t memtop_urealloc ( userptr_t ptr, size_t new_size ) { + struct external_memory extmem; + userptr_t new = ptr; + size_t align; + int rc; + + /* Initialise external memory allocator if necessary */ + if ( bottom == top ) { + if ( ( rc = init_eheap() ) != 0 ) + return UNULL; + } + + /* Get block properties into extmem */ + if ( ptr && ( ptr != UNOWHERE ) ) { + /* Determine old size */ + copy_from_user ( &extmem, ptr, -sizeof ( extmem ), + sizeof ( extmem ) ); + } else { + /* Create a zero-length block */ + ptr = bottom = userptr_add ( bottom, -sizeof ( extmem ) ); + DBG ( "EXTMEM allocating [%lx,%lx)\n", + user_to_phys ( ptr, 0 ), user_to_phys ( ptr, 0 ) ); + extmem.size = 0; + } + extmem.used = ( new_size > 0 ); + + /* Expand/shrink block if possible */ + if ( ptr == bottom ) { + /* Update block */ + new = userptr_add ( ptr, - ( new_size - extmem.size ) ); + align = ( user_to_phys ( new, 0 ) & ( EM_ALIGN - 1 ) ); + new_size += align; + new = userptr_add ( new, -align ); + DBG ( "EXTMEM expanding [%lx,%lx) to [%lx,%lx)\n", + user_to_phys ( ptr, 0 ), + user_to_phys ( ptr, extmem.size ), + user_to_phys ( new, 0 ), + user_to_phys ( new, new_size )); + memmove_user ( new, 0, ptr, 0, ( ( extmem.size < new_size ) ? + extmem.size : new_size ) ); + extmem.size = new_size; + bottom = new; + } else { + /* Cannot expand; can only pretend to shrink */ + if ( new_size > extmem.size ) { + /* Refuse to expand */ + DBG ( "EXTMEM cannot expand [%lx,%lx)\n", + user_to_phys ( ptr, 0 ), + user_to_phys ( ptr, extmem.size ) ); + return UNULL; + } + } + + /* Write back block properties */ + copy_to_user ( new, -sizeof ( extmem ), &extmem, + sizeof ( extmem ) ); + + /* Collect any free blocks and update hidden memory region */ + ecollect_free(); + hide_umalloc ( user_to_phys ( bottom, -sizeof ( extmem ) ), + user_to_phys ( top, 0 ) ); + + return ( new_size ? new : UNOWHERE ); +} + +PROVIDE_UMALLOC ( memtop, urealloc, memtop_urealloc ); diff --git a/gpxe/src/arch/i386/interface/pcbios/pcibios.c b/gpxe/src/arch/i386/interface/pcbios/pcibios.c new file mode 100644 index 0000000..81b4fd3 --- /dev/null +++ b/gpxe/src/arch/i386/interface/pcbios/pcibios.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include + +/** @file + * + * PCI configuration space access via PCI BIOS + * + */ + +/** + * Determine maximum PCI bus number within system + * + * @ret max_bus Maximum bus number + */ +static int pcibios_max_bus ( void ) { + int discard_a, discard_D; + uint8_t max_bus; + + __asm__ __volatile__ ( REAL_CODE ( "stc\n\t" + "int $0x1a\n\t" + "jnc 1f\n\t" + "xorw %%cx, %%cx\n\t" + "\n1:\n\t" ) + : "=c" ( max_bus ), "=a" ( discard_a ), + "=D" ( discard_D ) + : "a" ( PCIBIOS_INSTALLATION_CHECK >> 16 ), + "D" ( 0 ) + : "ebx", "edx" ); + + return max_bus; +} + +/** + * Read configuration space via PCI BIOS + * + * @v pci PCI device + * @v command PCI BIOS command + * @v value Value read + * @ret rc Return status code + */ +int pcibios_read ( struct pci_device *pci, uint32_t command, uint32_t *value ){ + int discard_b, discard_D; + int status; + + __asm__ __volatile__ ( REAL_CODE ( "stc\n\t" + "int $0x1a\n\t" + "jnc 1f\n\t" + "xorl %%eax, %%eax\n\t" + "decl %%eax\n\t" + "movl %%eax, %%ecx\n\t" + "\n1:\n\t" ) + : "=a" ( status ), "=b" ( discard_b ), + "=c" ( *value ), "=D" ( discard_D ) + : "a" ( command >> 16 ), "D" ( command ), + "b" ( PCI_BUSDEVFN ( pci->bus, pci->devfn ) ) + : "edx" ); + + return ( ( status >> 8 ) & 0xff ); +} + +/** + * Write configuration space via PCI BIOS + * + * @v pci PCI device + * @v command PCI BIOS command + * @v value Value to be written + * @ret rc Return status code + */ +int pcibios_write ( struct pci_device *pci, uint32_t command, uint32_t value ){ + int discard_b, discard_c, discard_D; + int status; + + __asm__ __volatile__ ( REAL_CODE ( "stc\n\t" + "int $0x1a\n\t" + "jnc 1f\n\t" + "movb $0xff, %%ah\n\t" + "\n1:\n\t" ) + : "=a" ( status ), "=b" ( discard_b ), + "=c" ( discard_c ), "=D" ( discard_D ) + : "a" ( command >> 16 ), "D" ( command ), + "b" ( PCI_BUSDEVFN ( pci->bus, pci->devfn ) ), + "c" ( value ) + : "edx" ); + + return ( ( status >> 8 ) & 0xff ); +} + +PROVIDE_PCIAPI ( pcbios, pci_max_bus, pcibios_max_bus ); +PROVIDE_PCIAPI_INLINE ( pcbios, pci_read_config_byte ); +PROVIDE_PCIAPI_INLINE ( pcbios, pci_read_config_word ); +PROVIDE_PCIAPI_INLINE ( pcbios, pci_read_config_dword ); +PROVIDE_PCIAPI_INLINE ( pcbios, pci_write_config_byte ); +PROVIDE_PCIAPI_INLINE ( pcbios, pci_write_config_word ); +PROVIDE_PCIAPI_INLINE ( pcbios, pci_write_config_dword ); diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_call.c b/gpxe/src/arch/i386/interface/pxe/pxe_call.c new file mode 100644 index 0000000..06dee25 --- /dev/null +++ b/gpxe/src/arch/i386/interface/pxe/pxe_call.c @@ -0,0 +1,456 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include + +/** @file + * + * PXE API entry point + */ + +/** Vector for chaining INT 1A */ +extern struct segoff __text16 ( pxe_int_1a_vector ); +#define pxe_int_1a_vector __use_text16 ( pxe_int_1a_vector ) + +/** INT 1A handler */ +extern void pxe_int_1a ( void ); + +/** A function pointer to hold any PXE API call + * + * Used by pxe_api_call() to avoid large swathes of duplicated code. + */ +union pxenv_call { + PXENV_EXIT_t ( * any ) ( union u_PXENV_ANY * ); + PXENV_EXIT_t ( * unknown ) ( struct s_PXENV_UNKNOWN * ); + PXENV_EXIT_t ( * unload_stack ) ( struct s_PXENV_UNLOAD_STACK * ); + PXENV_EXIT_t ( * get_cached_info ) + ( struct s_PXENV_GET_CACHED_INFO * ); + PXENV_EXIT_t ( * restart_tftp ) ( struct s_PXENV_TFTP_READ_FILE * ); + PXENV_EXIT_t ( * start_undi ) ( struct s_PXENV_START_UNDI * ); + PXENV_EXIT_t ( * stop_undi ) ( struct s_PXENV_STOP_UNDI * ); + PXENV_EXIT_t ( * start_base ) ( struct s_PXENV_START_BASE * ); + PXENV_EXIT_t ( * stop_base ) ( struct s_PXENV_STOP_BASE * ); + PXENV_EXIT_t ( * tftp_open ) ( struct s_PXENV_TFTP_OPEN * ); + PXENV_EXIT_t ( * tftp_close ) ( struct s_PXENV_TFTP_CLOSE * ); + PXENV_EXIT_t ( * tftp_read ) ( struct s_PXENV_TFTP_READ * ); + PXENV_EXIT_t ( * tftp_read_file ) ( struct s_PXENV_TFTP_READ_FILE * ); + PXENV_EXIT_t ( * tftp_get_fsize ) ( struct s_PXENV_TFTP_GET_FSIZE * ); + PXENV_EXIT_t ( * udp_open ) ( struct s_PXENV_UDP_OPEN * ); + PXENV_EXIT_t ( * udp_close ) ( struct s_PXENV_UDP_CLOSE * ); + PXENV_EXIT_t ( * udp_write ) ( struct s_PXENV_UDP_WRITE * ); + PXENV_EXIT_t ( * udp_read ) ( struct s_PXENV_UDP_READ * ); + PXENV_EXIT_t ( * undi_startup ) ( struct s_PXENV_UNDI_STARTUP * ); + PXENV_EXIT_t ( * undi_cleanup ) ( struct s_PXENV_UNDI_CLEANUP * ); + PXENV_EXIT_t ( * undi_initialize ) + ( struct s_PXENV_UNDI_INITIALIZE * ); + PXENV_EXIT_t ( * undi_reset_adapter ) ( struct s_PXENV_UNDI_RESET * ); + PXENV_EXIT_t ( * undi_shutdown ) ( struct s_PXENV_UNDI_SHUTDOWN * ); + PXENV_EXIT_t ( * undi_open ) ( struct s_PXENV_UNDI_OPEN * ); + PXENV_EXIT_t ( * undi_close ) ( struct s_PXENV_UNDI_CLOSE * ); + PXENV_EXIT_t ( * undi_transmit ) ( struct s_PXENV_UNDI_TRANSMIT * ); + PXENV_EXIT_t ( * undi_set_mcast_address ) + ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS * ); + PXENV_EXIT_t ( * undi_set_station_address ) + ( struct s_PXENV_UNDI_SET_STATION_ADDRESS * ); + PXENV_EXIT_t ( * undi_set_packet_filter ) + ( struct s_PXENV_UNDI_SET_PACKET_FILTER * ); + PXENV_EXIT_t ( * undi_get_information ) + ( struct s_PXENV_UNDI_GET_INFORMATION * ); + PXENV_EXIT_t ( * undi_get_statistics ) + ( struct s_PXENV_UNDI_GET_STATISTICS * ); + PXENV_EXIT_t ( * undi_clear_statistics ) + ( struct s_PXENV_UNDI_CLEAR_STATISTICS * ); + PXENV_EXIT_t ( * undi_initiate_diags ) + ( struct s_PXENV_UNDI_INITIATE_DIAGS * ); + PXENV_EXIT_t ( * undi_force_interrupt ) + ( struct s_PXENV_UNDI_FORCE_INTERRUPT * ); + PXENV_EXIT_t ( * undi_get_mcast_address ) + ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS * ); + PXENV_EXIT_t ( * undi_get_nic_type ) + ( struct s_PXENV_UNDI_GET_NIC_TYPE * ); + PXENV_EXIT_t ( * undi_get_iface_info ) + ( struct s_PXENV_UNDI_GET_IFACE_INFO * ); + PXENV_EXIT_t ( * undi_get_state ) ( struct s_PXENV_UNDI_GET_STATE * ); + PXENV_EXIT_t ( * undi_isr ) ( struct s_PXENV_UNDI_ISR * ); + PXENV_EXIT_t ( * file_open ) ( struct s_PXENV_FILE_OPEN * ); + PXENV_EXIT_t ( * file_close ) ( struct s_PXENV_FILE_CLOSE * ); + PXENV_EXIT_t ( * file_select ) ( struct s_PXENV_FILE_SELECT * ); + PXENV_EXIT_t ( * file_read ) ( struct s_PXENV_FILE_READ * ); + PXENV_EXIT_t ( * get_file_size ) ( struct s_PXENV_GET_FILE_SIZE * ); + PXENV_EXIT_t ( * file_exec ) ( struct s_PXENV_FILE_EXEC * ); + PXENV_EXIT_t ( * file_api_check ) ( struct s_PXENV_FILE_API_CHECK * ); +}; + +/** + * Handle an unknown PXE API call + * + * @v pxenv_unknown Pointer to a struct s_PXENV_UNKNOWN + * @ret #PXENV_EXIT_FAILURE Always + * @err #PXENV_STATUS_UNSUPPORTED Always + */ +static PXENV_EXIT_t pxenv_unknown ( struct s_PXENV_UNKNOWN *pxenv_unknown ) { + pxenv_unknown->Status = PXENV_STATUS_UNSUPPORTED; + return PXENV_EXIT_FAILURE; +} + +/** + * Dispatch PXE API call + * + * @v bx PXE opcode + * @v es:di Address of PXE parameter block + * @ret ax PXE exit code + */ +__asmcall void pxe_api_call ( struct i386_all_regs *ix86 ) { + int opcode = ix86->regs.bx; + userptr_t parameters = real_to_user ( ix86->segs.es, ix86->regs.di ); + size_t param_len; + union u_PXENV_ANY pxenv_any; + union pxenv_call pxenv_call; + PXENV_EXIT_t ret; + + switch ( opcode ) { + case PXENV_UNLOAD_STACK: + pxenv_call.unload_stack = pxenv_unload_stack; + param_len = sizeof ( pxenv_any.unload_stack ); + break; + case PXENV_GET_CACHED_INFO: + pxenv_call.get_cached_info = pxenv_get_cached_info; + param_len = sizeof ( pxenv_any.get_cached_info ); + break; + case PXENV_RESTART_TFTP: + pxenv_call.restart_tftp = pxenv_restart_tftp; + param_len = sizeof ( pxenv_any.restart_tftp ); + break; + case PXENV_START_UNDI: + pxenv_call.start_undi = pxenv_start_undi; + param_len = sizeof ( pxenv_any.start_undi ); + break; + case PXENV_STOP_UNDI: + pxenv_call.stop_undi = pxenv_stop_undi; + param_len = sizeof ( pxenv_any.stop_undi ); + break; + case PXENV_START_BASE: + pxenv_call.start_base = pxenv_start_base; + param_len = sizeof ( pxenv_any.start_base ); + break; + case PXENV_STOP_BASE: + pxenv_call.stop_base = pxenv_stop_base; + param_len = sizeof ( pxenv_any.stop_base ); + break; + case PXENV_TFTP_OPEN: + pxenv_call.tftp_open = pxenv_tftp_open; + param_len = sizeof ( pxenv_any.tftp_open ); + break; + case PXENV_TFTP_CLOSE: + pxenv_call.tftp_close = pxenv_tftp_close; + param_len = sizeof ( pxenv_any.tftp_close ); + break; + case PXENV_TFTP_READ: + pxenv_call.tftp_read = pxenv_tftp_read; + param_len = sizeof ( pxenv_any.tftp_read ); + break; + case PXENV_TFTP_READ_FILE: + pxenv_call.tftp_read_file = pxenv_tftp_read_file; + param_len = sizeof ( pxenv_any.tftp_read_file ); + break; + case PXENV_TFTP_GET_FSIZE: + pxenv_call.tftp_get_fsize = pxenv_tftp_get_fsize; + param_len = sizeof ( pxenv_any.tftp_get_fsize ); + break; + case PXENV_UDP_OPEN: + pxenv_call.udp_open = pxenv_udp_open; + param_len = sizeof ( pxenv_any.udp_open ); + break; + case PXENV_UDP_CLOSE: + pxenv_call.udp_close = pxenv_udp_close; + param_len = sizeof ( pxenv_any.udp_close ); + break; + case PXENV_UDP_WRITE: + pxenv_call.udp_write = pxenv_udp_write; + param_len = sizeof ( pxenv_any.udp_write ); + break; + case PXENV_UDP_READ: + pxenv_call.udp_read = pxenv_udp_read; + param_len = sizeof ( pxenv_any.udp_read ); + break; + case PXENV_UNDI_STARTUP: + pxenv_call.undi_startup = pxenv_undi_startup; + param_len = sizeof ( pxenv_any.undi_startup ); + break; + case PXENV_UNDI_CLEANUP: + pxenv_call.undi_cleanup = pxenv_undi_cleanup; + param_len = sizeof ( pxenv_any.undi_cleanup ); + break; + case PXENV_UNDI_INITIALIZE: + pxenv_call.undi_initialize = pxenv_undi_initialize; + param_len = sizeof ( pxenv_any.undi_initialize ); + break; + case PXENV_UNDI_RESET_ADAPTER: + pxenv_call.undi_reset_adapter = pxenv_undi_reset_adapter; + param_len = sizeof ( pxenv_any.undi_reset_adapter ); + break; + case PXENV_UNDI_SHUTDOWN: + pxenv_call.undi_shutdown = pxenv_undi_shutdown; + param_len = sizeof ( pxenv_any.undi_shutdown ); + break; + case PXENV_UNDI_OPEN: + pxenv_call.undi_open = pxenv_undi_open; + param_len = sizeof ( pxenv_any.undi_open ); + break; + case PXENV_UNDI_CLOSE: + pxenv_call.undi_close = pxenv_undi_close; + param_len = sizeof ( pxenv_any.undi_close ); + break; + case PXENV_UNDI_TRANSMIT: + pxenv_call.undi_transmit = pxenv_undi_transmit; + param_len = sizeof ( pxenv_any.undi_transmit ); + break; + case PXENV_UNDI_SET_MCAST_ADDRESS: + pxenv_call.undi_set_mcast_address = + pxenv_undi_set_mcast_address; + param_len = sizeof ( pxenv_any.undi_set_mcast_address ); + break; + case PXENV_UNDI_SET_STATION_ADDRESS: + pxenv_call.undi_set_station_address = + pxenv_undi_set_station_address; + param_len = sizeof ( pxenv_any.undi_set_station_address ); + break; + case PXENV_UNDI_SET_PACKET_FILTER: + pxenv_call.undi_set_packet_filter = + pxenv_undi_set_packet_filter; + param_len = sizeof ( pxenv_any.undi_set_packet_filter ); + break; + case PXENV_UNDI_GET_INFORMATION: + pxenv_call.undi_get_information = pxenv_undi_get_information; + param_len = sizeof ( pxenv_any.undi_get_information ); + break; + case PXENV_UNDI_GET_STATISTICS: + pxenv_call.undi_get_statistics = pxenv_undi_get_statistics; + param_len = sizeof ( pxenv_any.undi_get_statistics ); + break; + case PXENV_UNDI_CLEAR_STATISTICS: + pxenv_call.undi_clear_statistics = pxenv_undi_clear_statistics; + param_len = sizeof ( pxenv_any.undi_clear_statistics ); + break; + case PXENV_UNDI_INITIATE_DIAGS: + pxenv_call.undi_initiate_diags = pxenv_undi_initiate_diags; + param_len = sizeof ( pxenv_any.undi_initiate_diags ); + break; + case PXENV_UNDI_FORCE_INTERRUPT: + pxenv_call.undi_force_interrupt = pxenv_undi_force_interrupt; + param_len = sizeof ( pxenv_any.undi_force_interrupt ); + break; + case PXENV_UNDI_GET_MCAST_ADDRESS: + pxenv_call.undi_get_mcast_address = + pxenv_undi_get_mcast_address; + param_len = sizeof ( pxenv_any.undi_get_mcast_address ); + break; + case PXENV_UNDI_GET_NIC_TYPE: + pxenv_call.undi_get_nic_type = pxenv_undi_get_nic_type; + param_len = sizeof ( pxenv_any.undi_get_nic_type ); + break; + case PXENV_UNDI_GET_IFACE_INFO: + pxenv_call.undi_get_iface_info = pxenv_undi_get_iface_info; + param_len = sizeof ( pxenv_any.undi_get_iface_info ); + break; + case PXENV_UNDI_ISR: + pxenv_call.undi_isr = pxenv_undi_isr; + param_len = sizeof ( pxenv_any.undi_isr ); + break; + case PXENV_FILE_OPEN: + pxenv_call.file_open = pxenv_file_open; + param_len = sizeof ( pxenv_any.file_open ); + break; + case PXENV_FILE_CLOSE: + pxenv_call.file_close = pxenv_file_close; + param_len = sizeof ( pxenv_any.file_close ); + break; + case PXENV_FILE_SELECT: + pxenv_call.file_select = pxenv_file_select; + param_len = sizeof ( pxenv_any.file_select ); + break; + case PXENV_FILE_READ: + pxenv_call.file_read = pxenv_file_read; + param_len = sizeof ( pxenv_any.file_read ); + break; + case PXENV_GET_FILE_SIZE: + pxenv_call.get_file_size = pxenv_get_file_size; + param_len = sizeof ( pxenv_any.get_file_size ); + break; + case PXENV_FILE_EXEC: + pxenv_call.file_exec = pxenv_file_exec; + param_len = sizeof ( pxenv_any.file_exec ); + break; + case PXENV_FILE_API_CHECK: + pxenv_call.file_api_check = pxenv_file_api_check; + param_len = sizeof ( pxenv_any.file_api_check ); + break; + default: + DBG ( "PXENV_UNKNOWN_%hx", opcode ); + pxenv_call.unknown = pxenv_unknown; + param_len = sizeof ( pxenv_any.unknown ); + break; + } + + /* Copy parameter block from caller */ + copy_from_user ( &pxenv_any, parameters, 0, param_len ); + + /* Set default status in case child routine fails to do so */ + pxenv_any.Status = PXENV_STATUS_FAILURE; + + /* Hand off to relevant API routine */ + DBG ( "[" ); + ret = pxenv_call.any ( &pxenv_any ); + if ( pxenv_any.Status != PXENV_STATUS_SUCCESS ) { + DBG ( " %02x", pxenv_any.Status ); + } + if ( ret != PXENV_EXIT_SUCCESS ) { + DBG ( ret == PXENV_EXIT_FAILURE ? " err" : " ??" ); + } + DBG ( "]" ); + + /* Copy modified parameter block back to caller and return */ + copy_to_user ( parameters, 0, &pxenv_any, param_len ); + ix86->regs.ax = ret; +} + +/** + * Dispatch PXE loader call + * + * @v es:di Address of PXE parameter block + * @ret ax PXE exit code + */ +__asmcall void pxe_loader_call ( struct i386_all_regs *ix86 ) { + userptr_t uparams = real_to_user ( ix86->segs.es, ix86->regs.di ); + struct s_UNDI_LOADER params; + PXENV_EXIT_t ret; + + /* Copy parameter block from caller */ + copy_from_user ( ¶ms, uparams, 0, sizeof ( params ) ); + + /* Fill in ROM segment address */ + ppxe.UNDIROMID.segment = ix86->segs.ds; + + /* Set default status in case child routine fails to do so */ + params.Status = PXENV_STATUS_FAILURE; + + /* Call UNDI loader */ + ret = undi_loader ( ¶ms ); + + /* Copy modified parameter block back to caller and return */ + copy_to_user ( uparams, 0, ¶ms, sizeof ( params ) ); + ix86->regs.ax = ret; +} + +/** + * Hook INT 1A for PXE + * + */ +void pxe_hook_int1a ( void ) { + hook_bios_interrupt ( 0x1a, ( unsigned int ) pxe_int_1a, + &pxe_int_1a_vector ); +} + +/** + * Unhook INT 1A for PXE + * + * @ret rc Return status code + */ +int pxe_unhook_int1a ( void ) { + return unhook_bios_interrupt ( 0x1a, ( unsigned int ) pxe_int_1a, + &pxe_int_1a_vector ); +} + +/** + * Calculate byte checksum as used by PXE + * + * @v data Data + * @v size Length of data + * @ret sum Checksum + */ +static uint8_t pxe_checksum ( void *data, size_t size ) { + uint8_t *bytes = data; + uint8_t sum = 0; + + while ( size-- ) { + sum += *bytes++; + } + return sum; +} + +/** + * Initialise !PXE and PXENV+ structures + * + */ +void pxe_init_structures ( void ) { + uint32_t rm_cs_phys = ( rm_cs << 4 ); + uint32_t rm_ds_phys = ( rm_ds << 4 ); + + /* Fill in missing segment fields */ + ppxe.EntryPointSP.segment = rm_cs; + ppxe.EntryPointESP.segment = rm_cs; + ppxe.Stack.segment_address = rm_ds; + ppxe.Stack.Physical_address = rm_ds_phys; + ppxe.UNDIData.segment_address = rm_ds; + ppxe.UNDIData.Physical_address = rm_ds_phys; + ppxe.UNDICode.segment_address = rm_cs; + ppxe.UNDICode.Physical_address = rm_cs_phys; + ppxe.UNDICodeWrite.segment_address = rm_cs; + ppxe.UNDICodeWrite.Physical_address = rm_cs_phys; + pxenv.RMEntry.segment = rm_cs; + pxenv.StackSeg = rm_ds; + pxenv.UNDIDataSeg = rm_ds; + pxenv.UNDICodeSeg = rm_cs; + pxenv.PXEPtr.segment = rm_cs; + + /* Update checksums */ + ppxe.StructCksum -= pxe_checksum ( &ppxe, sizeof ( ppxe ) ); + pxenv.Checksum -= pxe_checksum ( &pxenv, sizeof ( pxenv ) ); +} + +/** + * Start PXE NBP at 0000:7c00 + * + * @ret rc Return status code + */ +int pxe_start_nbp ( void ) { + int discard_b, discard_c, discard_d, discard_D; + uint16_t rc; + + /* Far call to PXE NBP */ + __asm__ __volatile__ ( REAL_CODE ( "movw %%cx, %%es\n\t" + "pushw %%es\n\t" + "pushw %%di\n\t" + "sti\n\t" + "lcall $0, $0x7c00\n\t" + "addw $4, %%sp\n\t" ) + : "=a" ( rc ), "=b" ( discard_b ), + "=c" ( discard_c ), "=d" ( discard_d ), + "=D" ( discard_D ) + : "a" ( 0 ), "b" ( __from_text16 ( &pxenv ) ), + "c" ( rm_cs ), + "d" ( virt_to_phys ( &pxenv ) ), + "D" ( __from_text16 ( &ppxe ) ) + : "esi", "ebp", "memory" ); + + return rc; +} diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_entry.S b/gpxe/src/arch/i386/interface/pxe/pxe_entry.S new file mode 100644 index 0000000..0e8c8e2 --- /dev/null +++ b/gpxe/src/arch/i386/interface/pxe/pxe_entry.S @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2006 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. + * + */ + + .arch i386 + +/**************************************************************************** + * !PXE structure + **************************************************************************** + */ + .section ".text16.data", "aw", @progbits + .globl ppxe + .align 16 +ppxe: + .ascii "!PXE" /* Signature */ + .byte pxe_length /* StructLength */ + .byte 0 /* StructCksum */ + .byte 0 /* StructRev */ + .byte 0 /* reserved_1 */ + .word undiheader, 0 /* UNDIROMID */ + .word 0, 0 /* BaseROMID */ + .word pxe_entry_sp, 0 /* EntryPointSP */ + .word pxe_entry_esp, 0 /* EntryPointESP */ + .word -1, -1 /* StatusCallout */ + .byte 0 /* reserved_2 */ + .byte SegDescCnt /* SegDescCnt */ + .word 0 /* FirstSelector */ +pxe_segments: + .word 0, 0, 0, _data16_memsz /* Stack */ + .word 0, 0, 0, _data16_memsz /* UNDIData */ + .word 0, 0, 0, _text16_memsz /* UNDICode */ + .word 0, 0, 0, _text16_memsz /* UNDICodeWrite */ + .word 0, 0, 0, 0 /* BC_Data */ + .word 0, 0, 0, 0 /* BC_Code */ + .word 0, 0, 0, 0 /* BC_CodeWrite */ + .equ SegDescCnt, ( ( . - pxe_segments ) / 8 ) + .equ pxe_length, . - ppxe + .size ppxe, . - ppxe + + /* Define undiheader=0 as a weak symbol for non-ROM builds */ + .section ".weak", "a", @nobits + .weak undiheader +undiheader: + +/**************************************************************************** + * PXENV+ structure + **************************************************************************** + */ + .section ".text16.data", "aw", @progbits + .globl pxenv + .align 16 +pxenv: + .ascii "PXENV+" /* Signature */ + .word 0x0201 /* Version */ + .byte pxenv_length /* Length */ + .byte 0 /* Checksum */ + .word pxenv_entry, 0 /* RMEntry */ + .long 0 /* PMEntry */ + .word 0 /* PMSelector */ + .word 0 /* StackSeg */ + .word _data16_memsz /* StackSize */ + .word 0 /* BC_CodeSeg */ + .word 0 /* BC_CodeSize */ + .word 0 /* BC_DataSeg */ + .word 0 /* BC_DataSize */ + .word 0 /* UNDIDataSeg */ + .word _data16_memsz /* UNDIDataSize */ + .word 0 /* UNDICodeSeg */ + .word _text16_memsz /* UNDICodeSize */ + .word ppxe, 0 /* PXEPtr */ + .equ pxenv_length, . - pxenv + .size pxenv, . - pxenv + +/**************************************************************************** + * pxenv_entry (16-bit far call) + * + * PXE API call PXENV+ entry point + * + * Parameters: + * %es:di : Far pointer to PXE parameter structure + * %bx : PXE API call + * Returns: + * %ax : PXE exit status + * Corrupts: + * none + **************************************************************************** + */ + /* Wyse Streaming Manager server (WLDRM13.BIN) assumes that + * the PXENV+ entry point is at UNDI_CS:0000; apparently, + * somebody at Wyse has difficulty distinguishing between the + * words "may" and "must"... + */ + .section ".text16.null", "ax", @progbits + .code16 +pxenv_null_entry: + jmp pxenv_entry + + .section ".text16", "ax", @progbits + .code16 +pxenv_entry: + pushl $pxe_api_call + pushw %cs + call prot_call + addl $4, %esp + lret + .size pxenv_entry, . - pxenv_entry + +/**************************************************************************** + * pxe_entry + * + * PXE API call !PXE entry point + * + * Parameters: + * stack : Far pointer to PXE parameter structure + * stack : PXE API call + * Returns: + * %ax : PXE exit status + * Corrupts: + * none + **************************************************************************** + */ + .section ".text16", "ax", @progbits + .code16 +pxe_entry: +pxe_entry_sp: + /* Preserve original %esp */ + pushl %esp + /* Zero high word of %esp to allow use of common code */ + movzwl %sp, %esp + jmp pxe_entry_common +pxe_entry_esp: + /* Preserve %esp to match behaviour of pxe_entry_sp */ + pushl %esp +pxe_entry_common: + /* Save PXENV+ API call registers */ + pushw %es + pushw %di + pushw %bx + /* Load !PXE parameters from stack into PXENV+ registers */ + addr32 movw 18(%esp), %bx + movw %bx, %es + addr32 movw 16(%esp), %di + addr32 movw 14(%esp), %bx + /* Make call as for PXENV+ */ + pushw %cs + call pxenv_entry + /* Restore PXENV+ registers */ + popw %bx + popw %di + popw %es + /* Restore original %esp and return */ + popl %esp + lret + .size pxe_entry, . - pxe_entry + +/**************************************************************************** + * pxe_int_1a + * + * PXE INT 1A handler + * + * Parameters: + * %ax : 0x5650 + * Returns: + * %ax : 0x564e + * %es:bx : Far pointer to the PXENV+ structure + * %edx : Physical address of the PXENV+ structure + * CF cleared + * Corrupts: + * none + **************************************************************************** + */ + .section ".text16", "ax", @progbits + .code16 + .globl pxe_int_1a +pxe_int_1a: + pushfw + cmpw $0x5650, %ax + jne 1f + /* INT 1A,5650 - PXE installation check */ + xorl %edx, %edx + movw %cs, %dx + movw %dx, %es + movw $pxenv, %bx + shll $4, %edx + addl $pxenv, %edx + movw $0x564e, %ax + pushw %bp + movw %sp, %bp + andb $~0x01, 8(%bp) /* Clear CF on return */ + popw %bp + popfw + iret +1: /* INT 1A,other - pass through */ + popfw + ljmp *%cs:pxe_int_1a_vector + + .section ".text16.data", "aw", @progbits + .globl pxe_int_1a_vector +pxe_int_1a_vector: .long 0 diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_errors.c b/gpxe/src/arch/i386/interface/pxe/pxe_errors.c new file mode 100644 index 0000000..f884ef8 --- /dev/null +++ b/gpxe/src/arch/i386/interface/pxe/pxe_errors.c @@ -0,0 +1,103 @@ +#include +#include + +/* + * This table was generated from the relevant section of errno.h using + * + * perl -ne 'if ( /(PXENV_STATUS_(\S+))/ ) { + * $code = $1; $msg = $2; + * $msg =~ s/_/ /g; $msg = ucfirst lc $msg; + * $msg =~ s/(tftp|udp|arp|undi|bis|binl|pxenv|pxe|dhcp)/uc $1/ieg; + * print "\t{ $code, \"$msg\" },\n"; + * }' + * + * followed by a little manual tweaking. + * + */ +struct errortab pxe_errortab[] __errortab = { + { PXENV_STATUS_SUCCESS, "Success" }, + { PXENV_STATUS_FAILURE, "Failure" }, + { PXENV_STATUS_BAD_FUNC, "Bad function" }, + { PXENV_STATUS_UNSUPPORTED, "Unsupported function" }, + { PXENV_STATUS_KEEP_UNDI, "Keep UNDI" }, + { PXENV_STATUS_KEEP_ALL, "Keep all" }, + { PXENV_STATUS_OUT_OF_RESOURCES, "Out of resources" }, + { PXENV_STATUS_ARP_TIMEOUT, "ARP timeout" }, + { PXENV_STATUS_UDP_CLOSED, "UDP closed" }, + { PXENV_STATUS_UDP_OPEN, "UDP open" }, + { PXENV_STATUS_TFTP_CLOSED, "TFTP closed" }, + { PXENV_STATUS_TFTP_OPEN, "TFTP open" }, + { PXENV_STATUS_MCOPY_PROBLEM, "Memory copy problem" }, + { PXENV_STATUS_BIS_INTEGRITY_FAILURE, "BIS integrity failure" }, + { PXENV_STATUS_BIS_VALIDATE_FAILURE, "BIS validation failure" }, + { PXENV_STATUS_BIS_INIT_FAILURE, "BIS init failure" }, + { PXENV_STATUS_BIS_SHUTDOWN_FAILURE, "BIS shutdown failure" }, + { PXENV_STATUS_BIS_GBOA_FAILURE, "BIS GBOA failure" }, + { PXENV_STATUS_BIS_FREE_FAILURE, "BIS free failure" }, + { PXENV_STATUS_BIS_GSI_FAILURE, "BIS GSI failure" }, + { PXENV_STATUS_BIS_BAD_CKSUM, "BIS bad checksum" }, + { PXENV_STATUS_TFTP_CANNOT_ARP_ADDRESS, "TFTP cannot ARP address" }, + { PXENV_STATUS_TFTP_OPEN_TIMEOUT, "TFTP open timeout" }, + { PXENV_STATUS_TFTP_UNKNOWN_OPCODE, "TFTP unknown opcode" }, + { PXENV_STATUS_TFTP_READ_TIMEOUT, "TFTP read timeout" }, + { PXENV_STATUS_TFTP_ERROR_OPCODE, "TFTP error opcode" }, + { PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION, + "TFTP cannot open connection" }, + { PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION, + "TFTP cannot read from connection" }, + { PXENV_STATUS_TFTP_TOO_MANY_PACKAGES, "TFTP too many packages" }, + { PXENV_STATUS_TFTP_FILE_NOT_FOUND, "TFTP file not found" }, + { PXENV_STATUS_TFTP_ACCESS_VIOLATION, "TFTP access violation" }, + { PXENV_STATUS_TFTP_NO_MCAST_ADDRESS, "TFTP no mcast address" }, + { PXENV_STATUS_TFTP_NO_FILESIZE, "TFTP no filesize" }, + { PXENV_STATUS_TFTP_INVALID_PACKET_SIZE, "TFTP invalid packet size" }, + { PXENV_STATUS_DHCP_TIMEOUT, "DHCP timeout" }, + { PXENV_STATUS_DHCP_NO_IP_ADDRESS, "DHCP no ip address" }, + { PXENV_STATUS_DHCP_NO_BOOTFILE_NAME, "DHCP no bootfile name" }, + { PXENV_STATUS_DHCP_BAD_IP_ADDRESS, "DHCP bad ip address" }, + { PXENV_STATUS_UNDI_INVALID_FUNCTION, "UNDI invalid function" }, + { PXENV_STATUS_UNDI_MEDIATEST_FAILED, "UNDI mediatest failed" }, + { PXENV_STATUS_UNDI_CANNOT_INIT_NIC_FOR_MCAST, + "UNDI cannot initialise NIC for multicast" }, + { PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC, + "UNDI cannot initialise NIC" }, + { PXENV_STATUS_UNDI_CANNOT_INITIALIZE_PHY, + "UNDI cannot initialise PHY" }, + { PXENV_STATUS_UNDI_CANNOT_READ_CONFIG_DATA, + "UNDI cannot read config data" }, + { PXENV_STATUS_UNDI_CANNOT_READ_INIT_DATA, + "UNDI cannot read init data" }, + { PXENV_STATUS_UNDI_BAD_MAC_ADDRESS, "UNDI bad MAC address" }, + { PXENV_STATUS_UNDI_BAD_EEPROM_CHECKSUM, "UNDI bad EEPROM checksum" }, + { PXENV_STATUS_UNDI_ERROR_SETTING_ISR, "UNDI error setting ISR" }, + { PXENV_STATUS_UNDI_INVALID_STATE, "UNDI invalid state" }, + { PXENV_STATUS_UNDI_TRANSMIT_ERROR, "UNDI transmit error" }, + { PXENV_STATUS_UNDI_INVALID_PARAMETER, "UNDI invalid parameter" }, + { PXENV_STATUS_BSTRAP_PROMPT_MENU, "Bootstrap prompt menu" }, + { PXENV_STATUS_BSTRAP_MCAST_ADDR, "Bootstrap mcast addr" }, + { PXENV_STATUS_BSTRAP_MISSING_LIST, "Bootstrap missing list" }, + { PXENV_STATUS_BSTRAP_NO_RESPONSE, "Bootstrap no response" }, + { PXENV_STATUS_BSTRAP_FILE_TOO_BIG, "Bootstrap file too big" }, + { PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE, + "BINL canceled by keystroke" }, + { PXENV_STATUS_BINL_NO_PXE_SERVER, "BINL no PXE server" }, + { PXENV_STATUS_NOT_AVAILABLE_IN_PMODE, + "Not available in protected mode" }, + { PXENV_STATUS_NOT_AVAILABLE_IN_RMODE, "Not available in real mode" }, + { PXENV_STATUS_BUSD_DEVICE_NOT_SUPPORTED, + "BUSD device not supported" }, + { PXENV_STATUS_LOADER_NO_FREE_BASE_MEMORY, + "Loader no free base memory" }, + { PXENV_STATUS_LOADER_NO_BC_ROMID, "Loader no Base Code ROM ID" }, + { PXENV_STATUS_LOADER_BAD_BC_ROMID, "Loader bad Base Code ROM ID" }, + { PXENV_STATUS_LOADER_BAD_BC_RUNTIME_IMAGE, + "Loader bad Base Code runtime image" }, + { PXENV_STATUS_LOADER_NO_UNDI_ROMID, "Loader no UNDI ROM ID" }, + { PXENV_STATUS_LOADER_BAD_UNDI_ROMID, "Loader bad UNDI ROM ID" }, + { PXENV_STATUS_LOADER_BAD_UNDI_DRIVER_IMAGE, + "Loader bad UNDI driver image" }, + { PXENV_STATUS_LOADER_NO_PXE_STRUCT, "Loader no !PXE struct" }, + { PXENV_STATUS_LOADER_NO_PXENV_STRUCT, "Loader no PXENV+ struct" }, + { PXENV_STATUS_LOADER_UNDI_START, "Loader UNDI start" }, + { PXENV_STATUS_LOADER_BC_START, "Loader Base Code start" }, +}; diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_file.c b/gpxe/src/arch/i386/interface/pxe/pxe_file.c new file mode 100644 index 0000000..4167458 --- /dev/null +++ b/gpxe/src/arch/i386/interface/pxe/pxe_file.c @@ -0,0 +1,264 @@ +/** @file + * + * PXE FILE API + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Copyright (C) 2007 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. + */ + +FEATURE ( FEATURE_MISC, "PXEXT", DHCP_EB_FEATURE_PXE_EXT, 2 ); + +/** + * FILE OPEN + * + * @v file_open Pointer to a struct s_PXENV_FILE_OPEN + * @v s_PXENV_FILE_OPEN::FileName URL of file to open + * @ret #PXENV_EXIT_SUCCESS File was opened + * @ret #PXENV_EXIT_FAILURE File was not opened + * @ret s_PXENV_FILE_OPEN::Status PXE status code + * @ret s_PXENV_FILE_OPEN::FileHandle Handle of opened file + * + */ +PXENV_EXIT_t pxenv_file_open ( struct s_PXENV_FILE_OPEN *file_open ) { + userptr_t filename; + size_t filename_len; + int fd; + + DBG ( "PXENV_FILE_OPEN" ); + + /* Copy name from external program, and open it */ + filename = real_to_user ( file_open->FileName.segment, + file_open->FileName.offset ); + filename_len = strlen_user ( filename, 0 ); + { + char uri_string[ filename_len + 1 ]; + + copy_from_user ( uri_string, filename, 0, + sizeof ( uri_string ) ); + DBG ( " %s", uri_string ); + fd = open ( uri_string ); + } + + if ( fd < 0 ) { + file_open->Status = PXENV_STATUS ( fd ); + return PXENV_EXIT_FAILURE; + } + + DBG ( " as file %d", fd ); + + file_open->FileHandle = fd; + file_open->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/** + * FILE CLOSE + * + * @v file_close Pointer to a struct s_PXENV_FILE_CLOSE + * @v s_PXENV_FILE_CLOSE::FileHandle File handle + * @ret #PXENV_EXIT_SUCCESS File was closed + * @ret #PXENV_EXIT_FAILURE File was not closed + * @ret s_PXENV_FILE_CLOSE::Status PXE status code + * + */ +PXENV_EXIT_t pxenv_file_close ( struct s_PXENV_FILE_CLOSE *file_close ) { + + DBG ( "PXENV_FILE_CLOSE %d", file_close->FileHandle ); + + close ( file_close->FileHandle ); + file_close->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/** + * FILE SELECT + * + * @v file_select Pointer to a struct s_PXENV_FILE_SELECT + * @v s_PXENV_FILE_SELECT::FileHandle File handle + * @ret #PXENV_EXIT_SUCCESS File has been checked for readiness + * @ret #PXENV_EXIT_FAILURE File has not been checked for readiness + * @ret s_PXENV_FILE_SELECT::Status PXE status code + * @ret s_PXENV_FILE_SELECT::Ready Indication of readiness + * + */ +PXENV_EXIT_t pxenv_file_select ( struct s_PXENV_FILE_SELECT *file_select ) { + fd_set fdset; + int ready; + + DBG ( "PXENV_FILE_SELECT %d", file_select->FileHandle ); + + FD_ZERO ( &fdset ); + FD_SET ( file_select->FileHandle, &fdset ); + if ( ( ready = select ( &fdset, 0 ) ) < 0 ) { + file_select->Status = PXENV_STATUS ( ready ); + return PXENV_EXIT_FAILURE; + } + + file_select->Ready = ( ready ? RDY_READ : 0 ); + file_select->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/** + * FILE READ + * + * @v file_read Pointer to a struct s_PXENV_FILE_READ + * @v s_PXENV_FILE_READ::FileHandle File handle + * @v s_PXENV_FILE_READ::BufferSize Size of data buffer + * @v s_PXENV_FILE_READ::Buffer Data buffer + * @ret #PXENV_EXIT_SUCCESS Data has been read from file + * @ret #PXENV_EXIT_FAILURE Data has not been read from file + * @ret s_PXENV_FILE_READ::Status PXE status code + * @ret s_PXENV_FILE_READ::Ready Indication of readiness + * @ret s_PXENV_FILE_READ::BufferSize Length of data read + * + */ +PXENV_EXIT_t pxenv_file_read ( struct s_PXENV_FILE_READ *file_read ) { + userptr_t buffer; + ssize_t len; + + DBG ( "PXENV_FILE_READ %d to %04x:%04x+%04x", file_read->FileHandle, + file_read->Buffer.segment, file_read->Buffer.offset, + file_read->BufferSize ); + + buffer = real_to_user ( file_read->Buffer.segment, + file_read->Buffer.offset ); + if ( ( len = read_user ( file_read->FileHandle, buffer, 0, + file_read->BufferSize ) ) < 0 ) { + file_read->Status = PXENV_STATUS ( len ); + return PXENV_EXIT_FAILURE; + } + + DBG ( " read %04zx", ( ( size_t ) len ) ); + + file_read->BufferSize = len; + file_read->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/** + * GET FILE SIZE + * + * @v get_file_size Pointer to a struct s_PXENV_GET_FILE_SIZE + * @v s_PXENV_GET_FILE_SIZE::FileHandle File handle + * @ret #PXENV_EXIT_SUCCESS File size has been determined + * @ret #PXENV_EXIT_FAILURE File size has not been determined + * @ret s_PXENV_GET_FILE_SIZE::Status PXE status code + * @ret s_PXENV_GET_FILE_SIZE::FileSize Size of file + */ +PXENV_EXIT_t pxenv_get_file_size ( struct s_PXENV_GET_FILE_SIZE + *get_file_size ) { + ssize_t filesize; + + DBG ( "PXENV_GET_FILE_SIZE %d", get_file_size->FileHandle ); + + filesize = fsize ( get_file_size->FileHandle ); + if ( filesize < 0 ) { + get_file_size->Status = PXENV_STATUS ( filesize ); + return PXENV_EXIT_FAILURE; + } + + DBG ( " is %zd", ( ( size_t ) filesize ) ); + + get_file_size->FileSize = filesize; + get_file_size->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/** + * FILE EXEC + * + * @v file_exec Pointer to a struct s_PXENV_FILE_EXEC + * @v s_PXENV_FILE_EXEC::Command Command to execute + * @ret #PXENV_EXIT_SUCCESS Command was executed successfully + * @ret #PXENV_EXIT_FAILURE Command was not executed successfully + * @ret s_PXENV_FILE_EXEC::Status PXE status code + * + */ +PXENV_EXIT_t pxenv_file_exec ( struct s_PXENV_FILE_EXEC *file_exec ) { + userptr_t command; + size_t command_len; + int rc; + + DBG ( "PXENV_FILE_EXEC" ); + + /* Copy name from external program, and exec it */ + command = real_to_user ( file_exec->Command.segment, + file_exec->Command.offset ); + command_len = strlen_user ( command, 0 ); + { + char command_string[ command_len + 1 ]; + + copy_from_user ( command_string, command, 0, + sizeof ( command_string ) ); + DBG ( " %s", command_string ); + + if ( ( rc = system ( command_string ) ) != 0 ) { + file_exec->Status = PXENV_STATUS ( rc ); + return PXENV_EXIT_FAILURE; + } + } + + file_exec->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/** + * FILE API CHECK + * + * @v file_exec Pointer to a struct s_PXENV_FILE_API_CHECK + * @v s_PXENV_FILE_API_CHECK::Magic Inbound magic number (0x91d447b2) + * @ret #PXENV_EXIT_SUCCESS Command was executed successfully + * @ret #PXENV_EXIT_FAILURE Command was not executed successfully + * @ret s_PXENV_FILE_API_CHECK::Status PXE status code + * @ret s_PXENV_FILE_API_CHECK::Magic Outbound magic number (0xe9c17b20) + * @ret s_PXENV_FILE_API_CHECK::Provider "gPXE" (0x45585067) + * @ret s_PXENV_FILE_API_CHECK::APIMask API function bitmask + * @ret s_PXENV_FILE_API_CHECK::Flags Reserved + * + */ +PXENV_EXIT_t pxenv_file_api_check ( struct s_PXENV_FILE_API_CHECK *file_api_check ) { + DBG ( "PXENV_FILE_API_CHECK" ); + + if ( file_api_check->Magic != 0x91d447b2 ) { + file_api_check->Status = PXENV_STATUS_BAD_FUNC; + return PXENV_EXIT_FAILURE; + } else if ( file_api_check->Size < + sizeof(struct s_PXENV_FILE_API_CHECK) ) { + file_api_check->Status = PXENV_STATUS_OUT_OF_RESOURCES; + return PXENV_EXIT_FAILURE; + } else { + file_api_check->Status = PXENV_STATUS_SUCCESS; + file_api_check->Size = sizeof(struct s_PXENV_FILE_API_CHECK); + file_api_check->Magic = 0xe9c17b20; + file_api_check->Provider = 0x45585067; /* "gPXE" */ + file_api_check->APIMask = 0x0000007f; /* Functions e0-e6 */ + file_api_check->Flags = 0; /* None defined */ + return PXENV_EXIT_SUCCESS; + } +} diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_loader.c b/gpxe/src/arch/i386/interface/pxe/pxe_loader.c new file mode 100644 index 0000000..d228a36 --- /dev/null +++ b/gpxe/src/arch/i386/interface/pxe/pxe_loader.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include "pxe.h" +#include "pxe_call.h" + +/** @file + * + * PXE UNDI loader + * + */ + +/* PXENV_UNDI_LOADER + * + */ +PXENV_EXIT_t undi_loader ( struct s_UNDI_LOADER *undi_loader ) { + + /* Perform one-time initialisation (e.g. heap) */ + initialise(); + + DBG ( "[PXENV_UNDI_LOADER to CS %04x DS %04x]", + undi_loader->UNDI_CS, undi_loader->UNDI_DS ); + + /* Set up PXE data structures */ + pxe_init_structures(); + + /* Fill in UNDI loader structure */ + undi_loader->PXEptr.segment = rm_cs; + undi_loader->PXEptr.offset = __from_text16 ( &ppxe ); + undi_loader->PXENVptr.segment = rm_cs; + undi_loader->PXENVptr.offset = __from_text16 ( &pxenv ); + + undi_loader->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_preboot.c b/gpxe/src/arch/i386/interface/pxe/pxe_preboot.c new file mode 100644 index 0000000..193abc3 --- /dev/null +++ b/gpxe/src/arch/i386/interface/pxe/pxe_preboot.c @@ -0,0 +1,352 @@ +/** @file + * + * PXE Preboot API + * + */ + +/* 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pxe.h" +#include "pxe_call.h" + +/* Avoid dragging in isapnp.o unnecessarily */ +uint16_t isapnp_read_port; + +/** Zero-based versions of PXENV_GET_CACHED_INFO::PacketType */ +enum pxe_cached_info_indices { + CACHED_INFO_DHCPDISCOVER = ( PXENV_PACKET_TYPE_DHCP_DISCOVER - 1 ), + CACHED_INFO_DHCPACK = ( PXENV_PACKET_TYPE_DHCP_ACK - 1 ), + CACHED_INFO_BINL = ( PXENV_PACKET_TYPE_CACHED_REPLY - 1 ), + NUM_CACHED_INFOS +}; + +/** A cached DHCP packet */ +union pxe_cached_info { + struct dhcphdr dhcphdr; + /* This buffer must be *exactly* the size of a BOOTPLAYER_t + * structure, otherwise WinPE will die horribly. It takes the + * size of *our* buffer and feeds it in to us as the size of + * one of *its* buffers. If our buffer is larger than it + * expects, we therefore end up overwriting part of its data + * segment, since it tells us to do so. (D'oh!) + * + * Note that a BOOTPLAYER_t is not necessarily large enough to + * hold a DHCP packet; this is a flaw in the PXE spec. + */ + BOOTPLAYER_t packet; +} __attribute__ (( packed )); + +/** A PXE DHCP packet creator */ +struct pxe_dhcp_packet_creator { + /** Create DHCP packet + * + * @v netdev Network device + * @v data Buffer for DHCP packet + * @v max_len Size of DHCP packet buffer + * @ret rc Return status code + */ + int ( * create ) ( struct net_device *netdev, void *data, + size_t max_len ); +}; + +/** PXE DHCP packet creators */ +static struct pxe_dhcp_packet_creator pxe_dhcp_packet_creators[] = { + [CACHED_INFO_DHCPDISCOVER] = { create_fakedhcpdiscover }, + [CACHED_INFO_DHCPACK] = { create_fakedhcpack }, + [CACHED_INFO_BINL] = { create_fakepxebsack }, +}; + +/* The case in which the caller doesn't supply a buffer is really + * awkward to support given that we have multiple sources of options, + * and that we don't actually store the DHCP packets. (We may not + * even have performed DHCP; we may have obtained all configuration + * from non-volatile stored options or from the command line.) + * + * Some NBPs rely on the buffers we provide being persistent, so we + * can't just use the temporary packet buffer. 4.5kB of base memory + * always wasted just because some clients are too lazy to provide + * their own buffers... + */ +static union pxe_cached_info __bss16_array ( cached_info, [NUM_CACHED_INFOS] ); +#define cached_info __use_data16 ( cached_info ) + +/** + * Set PXE cached TFTP filename + * + * @v filename TFTP filename + * + * This is a bug-for-bug compatibility hack needed in order to work + * with Microsoft Remote Installation Services (RIS). The filename + * used in a call to PXENV_RESTART_TFTP or PXENV_TFTP_READ_FILE must + * be returned as the DHCP filename in subsequent calls to + * PXENV_GET_CACHED_INFO. + */ +void pxe_set_cached_filename ( const unsigned char *filename ) { + memcpy ( cached_info[CACHED_INFO_DHCPACK].dhcphdr.file, filename, + sizeof ( cached_info[CACHED_INFO_DHCPACK].dhcphdr.file ) ); + memcpy ( cached_info[CACHED_INFO_BINL].dhcphdr.file, filename, + sizeof ( cached_info[CACHED_INFO_BINL].dhcphdr.file ) ); +} + +/** + * UNLOAD BASE CODE STACK + * + * @v None - + * @ret ... + * + */ +PXENV_EXIT_t pxenv_unload_stack ( struct s_PXENV_UNLOAD_STACK *unload_stack ) { + DBG ( "PXENV_UNLOAD_STACK" ); + + unload_stack->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_GET_CACHED_INFO + * + * Status: working + */ +PXENV_EXIT_t pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO + *get_cached_info ) { + struct pxe_dhcp_packet_creator *creator; + union pxe_cached_info *info; + unsigned int idx; + size_t len; + userptr_t buffer; + int rc; + + DBG ( "PXENV_GET_CACHED_INFO %d", get_cached_info->PacketType ); + + DBG ( " to %04x:%04x+%x", get_cached_info->Buffer.segment, + get_cached_info->Buffer.offset, get_cached_info->BufferSize ); + + /* Sanity check */ + idx = ( get_cached_info->PacketType - 1 ); + if ( idx >= NUM_CACHED_INFOS ) { + DBG ( " bad PacketType" ); + goto err; + } + info = &cached_info[idx]; + + /* Construct cached version of packet, if not already constructed. */ + if ( ! info->dhcphdr.op ) { + /* Construct DHCP packet */ + creator = &pxe_dhcp_packet_creators[idx]; + if ( ( rc = creator->create ( pxe_netdev, info, + sizeof ( *info ) ) ) != 0 ) { + DBG ( " failed to build packet" ); + goto err; + } + } + + len = get_cached_info->BufferSize; + if ( len == 0 ) { + /* Point client at our cached buffer. + * + * To add to the fun, Intel decided at some point in + * the evolution of the PXE specification to add the + * BufferLimit field, which we are meant to fill in + * with the length of our packet buffer, so that the + * caller can safely modify the boot server reply + * packet stored therein. However, this field was not + * present in earlier versions of the PXE spec, and + * there is at least one PXE NBP (Altiris) which + * allocates only exactly enough space for this + * earlier, shorter version of the structure. If we + * actually fill in the BufferLimit field, we + * therefore risk trashing random areas of the + * caller's memory. If we *don't* fill it in, then + * the caller is at liberty to assume that whatever + * random value happened to be in that location + * represents the length of the buffer we've just + * passed back to it. + * + * Since older PXE stacks won't fill this field in + * anyway, it's probably safe to assume that no + * callers actually rely on it, so we choose to not + * fill it in. + */ + get_cached_info->Buffer.segment = rm_ds; + get_cached_info->Buffer.offset = __from_data16 ( info ); + get_cached_info->BufferSize = sizeof ( *info ); + DBG ( " returning %04x:%04x+%04x['%x']", + get_cached_info->Buffer.segment, + get_cached_info->Buffer.offset, + get_cached_info->BufferSize, + get_cached_info->BufferLimit ); + } else { + /* Copy packet to client buffer */ + if ( len > sizeof ( *info ) ) + len = sizeof ( *info ); + if ( len < sizeof ( *info ) ) + DBG ( " buffer may be too short" ); + buffer = real_to_user ( get_cached_info->Buffer.segment, + get_cached_info->Buffer.offset ); + copy_to_user ( buffer, 0, info, len ); + get_cached_info->BufferSize = len; + } + + get_cached_info->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; + + err: + get_cached_info->Status = PXENV_STATUS_OUT_OF_RESOURCES; + return PXENV_EXIT_FAILURE; +} + +/* PXENV_RESTART_TFTP + * + * Status: working + */ +PXENV_EXIT_t pxenv_restart_tftp ( struct s_PXENV_TFTP_READ_FILE + *restart_tftp ) { + PXENV_EXIT_t tftp_exit; + + DBG ( "PXENV_RESTART_TFTP " ); + + /* Intel bug-for-bug hack */ + pxe_set_cached_filename ( restart_tftp->FileName ); + + /* Words cannot describe the complete mismatch between the PXE + * specification and any possible version of reality... + */ + restart_tftp->Buffer = PXE_LOAD_PHYS; /* Fixed by spec, apparently */ + restart_tftp->BufferSize = ( 0xa0000 - PXE_LOAD_PHYS ); /* Near enough */ + tftp_exit = pxenv_tftp_read_file ( restart_tftp ); + if ( tftp_exit != PXENV_EXIT_SUCCESS ) + return tftp_exit; + + /* Fire up the new NBP */ + restart_tftp->Status = pxe_start_nbp(); + + /* Not sure what "SUCCESS" actually means, since we can only + * return if the new NBP failed to boot... + */ + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_START_UNDI + * + * Status: working + */ +PXENV_EXIT_t pxenv_start_undi ( struct s_PXENV_START_UNDI *start_undi ) { + unsigned int bus_type; + unsigned int location; + struct net_device *netdev; + + DBG ( "PXENV_START_UNDI %04x:%04x:%04x", + start_undi->AX, start_undi->BX, start_undi->DX ); + + /* Determine bus type and location. Use a heuristic to decide + * whether we are PCI or ISAPnP + */ + if ( ( start_undi->DX >= ISAPNP_READ_PORT_MIN ) && + ( start_undi->DX <= ISAPNP_READ_PORT_MAX ) && + ( start_undi->BX >= ISAPNP_CSN_MIN ) && + ( start_undi->BX <= ISAPNP_CSN_MAX ) ) { + bus_type = BUS_TYPE_ISAPNP; + location = start_undi->BX; + /* Record ISAPnP read port for use by isapnp.c */ + isapnp_read_port = start_undi->DX; + } else { + bus_type = BUS_TYPE_PCI; + location = start_undi->AX; + } + + /* Probe for devices, etc. */ + startup(); + + /* Look for a matching net device */ + netdev = find_netdev_by_location ( bus_type, location ); + if ( ! netdev ) { + DBG ( " no net device found" ); + start_undi->Status = PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC; + return PXENV_EXIT_FAILURE; + } + DBG ( " using netdev %s", netdev->name ); + + /* Save as PXE net device */ + pxe_set_netdev ( netdev ); + + /* Hook INT 1A */ + pxe_hook_int1a(); + + start_undi->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_STOP_UNDI + * + * Status: working + */ +PXENV_EXIT_t pxenv_stop_undi ( struct s_PXENV_STOP_UNDI *stop_undi ) { + DBG ( "PXENV_STOP_UNDI" ); + + /* Unhook INT 1A */ + pxe_unhook_int1a(); + + /* Clear PXE net device */ + pxe_set_netdev ( NULL ); + + /* Prepare for unload */ + shutdown ( SHUTDOWN_BOOT ); + + stop_undi->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_START_BASE + * + * Status: won't implement (requires major structural changes) + */ +PXENV_EXIT_t pxenv_start_base ( struct s_PXENV_START_BASE *start_base ) { + DBG ( "PXENV_START_BASE" ); + + start_base->Status = PXENV_STATUS_UNSUPPORTED; + return PXENV_EXIT_FAILURE; +} + +/* PXENV_STOP_BASE + * + * Status: working + */ +PXENV_EXIT_t pxenv_stop_base ( struct s_PXENV_STOP_BASE *stop_base ) { + DBG ( "PXENV_STOP_BASE" ); + + /* The only time we will be called is when the NBP is trying + * to shut down the PXE stack. There's nothing we need to do + * in this call. + */ + + stop_base->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_tftp.c b/gpxe/src/arch/i386/interface/pxe/pxe_tftp.c new file mode 100644 index 0000000..715a0b6 --- /dev/null +++ b/gpxe/src/arch/i386/interface/pxe/pxe_tftp.c @@ -0,0 +1,581 @@ +/** @file + * + * PXE TFTP API + * + */ + +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** A PXE TFTP connection */ +struct pxe_tftp_connection { + /** Data transfer interface */ + struct xfer_interface xfer; + /** Data buffer */ + userptr_t buffer; + /** Size of data buffer */ + size_t size; + /** Starting offset of data buffer */ + size_t start; + /** File position */ + size_t offset; + /** Maximum file position */ + size_t max_offset; + /** Block size */ + size_t blksize; + /** Block index */ + unsigned int blkidx; + /** Overall return status code */ + int rc; +}; + +/** The PXE TFTP connection */ +static struct pxe_tftp_connection pxe_tftp = { + .xfer = XFER_INIT ( &null_xfer_ops ), +}; + +/** + * Close PXE TFTP connection + * + * @v rc Final status code + */ +static void pxe_tftp_close ( int rc ) { + xfer_nullify ( &pxe_tftp.xfer ); + xfer_close ( &pxe_tftp.xfer, rc ); + pxe_tftp.rc = rc; +} + +/** + * Receive new data + * + * @v xfer Data transfer interface + * @v iobuf I/O buffer + * @v meta Transfer metadata + * @ret rc Return status code + */ +static int pxe_tftp_xfer_deliver_iob ( struct xfer_interface *xfer __unused, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + size_t len = iob_len ( iobuf ); + int rc = 0; + + /* Calculate new buffer position */ + if ( meta->whence != SEEK_CUR ) + pxe_tftp.offset = 0; + pxe_tftp.offset += meta->offset; + + /* Copy data block to buffer */ + if ( len == 0 ) { + /* No data (pure seek); treat as success */ + } else if ( pxe_tftp.offset < pxe_tftp.start ) { + DBG ( " buffer underrun at %zx (min %zx)", + pxe_tftp.offset, pxe_tftp.start ); + rc = -ENOBUFS; + } else if ( ( pxe_tftp.offset + len ) > + ( pxe_tftp.start + pxe_tftp.size ) ) { + DBG ( " buffer overrun at %zx (max %zx)", + ( pxe_tftp.offset + len ), + ( pxe_tftp.start + pxe_tftp.size ) ); + rc = -ENOBUFS; + } else { + copy_to_user ( pxe_tftp.buffer, + ( pxe_tftp.offset - pxe_tftp.start ), + iobuf->data, len ); + } + + /* Calculate new buffer position */ + pxe_tftp.offset += len; + + /* Record maximum offset as the file size */ + if ( pxe_tftp.max_offset < pxe_tftp.offset ) + pxe_tftp.max_offset = pxe_tftp.offset; + + /* Terminate transfer on error */ + if ( rc != 0 ) + pxe_tftp_close ( rc ); + + free_iob ( iobuf ); + return rc; +} + +/** + * Handle close() event + * + * @v xfer Data transfer interface + * @v rc Reason for close + */ +static void pxe_tftp_xfer_close ( struct xfer_interface *xfer __unused, + int rc ) { + pxe_tftp_close ( rc ); +} + +static struct xfer_interface_operations pxe_tftp_xfer_ops = { + .close = pxe_tftp_xfer_close, + .vredirect = xfer_vopen, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = pxe_tftp_xfer_deliver_iob, + .deliver_raw = xfer_deliver_as_iob, +}; + +/** + * Maximum length of a PXE TFTP URI + * + * The PXE TFTP API provides 128 characters for the filename; the + * extra 128 bytes allow for the remainder of the URI. + */ +#define PXE_TFTP_URI_LEN 256 + +/** + * Open PXE TFTP connection + * + * @v ipaddress IP address + * @v port TFTP server port + * @v filename File name + * @v blksize Requested block size + * @ret rc Return status code + */ +static int pxe_tftp_open ( uint32_t ipaddress, unsigned int port, + const unsigned char *filename, size_t blksize ) { + char uri_string[PXE_TFTP_URI_LEN]; + struct in_addr address; + int rc; + + /* Intel bug-for-bug hack */ + pxe_set_cached_filename ( filename ); + + /* Reset PXE TFTP connection structure */ + memset ( &pxe_tftp, 0, sizeof ( pxe_tftp ) ); + xfer_init ( &pxe_tftp.xfer, &pxe_tftp_xfer_ops, NULL ); + pxe_tftp.rc = -EINPROGRESS; + + /* Construct URI string */ + address.s_addr = ipaddress; + if ( ! port ) + port = htons ( TFTP_PORT ); + if ( blksize < TFTP_DEFAULT_BLKSIZE ) + blksize = TFTP_DEFAULT_BLKSIZE; + snprintf ( uri_string, sizeof ( uri_string ), + "tftp://%s:%d%s%s?blksize=%zd", + inet_ntoa ( address ), ntohs ( port ), + ( ( filename[0] == '/' ) ? "" : "/" ), filename, blksize ); + DBG ( " %s", uri_string ); + + /* Open PXE TFTP connection */ + if ( ( rc = xfer_open_uri_string ( &pxe_tftp.xfer, + uri_string ) ) != 0 ) { + DBG ( " could not open (%s)\n", strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * TFTP OPEN + * + * @v tftp_open Pointer to a struct s_PXENV_TFTP_OPEN + * @v s_PXENV_TFTP_OPEN::ServerIPAddress TFTP server IP address + * @v s_PXENV_TFTP_OPEN::GatewayIPAddress Relay agent IP address, or 0.0.0.0 + * @v s_PXENV_TFTP_OPEN::FileName Name of file to open + * @v s_PXENV_TFTP_OPEN::TFTPPort TFTP server UDP port + * @v s_PXENV_TFTP_OPEN::PacketSize TFTP blksize option to request + * @ret #PXENV_EXIT_SUCCESS File was opened + * @ret #PXENV_EXIT_FAILURE File was not opened + * @ret s_PXENV_TFTP_OPEN::Status PXE status code + * @ret s_PXENV_TFTP_OPEN::PacketSize Negotiated blksize + * @err #PXENV_STATUS_TFTP_INVALID_PACKET_SIZE Requested blksize too small + * + * Opens a TFTP connection for downloading a file a block at a time + * using pxenv_tftp_read(). + * + * If s_PXENV_TFTP_OPEN::GatewayIPAddress is 0.0.0.0, normal IP + * routing will take place. See the relevant + * @ref pxe_routing "implementation note" for more details. + * + * On x86, you must set the s_PXE::StatusCallout field to a nonzero + * value before calling this function in protected mode. You cannot + * call this function with a 32-bit stack segment. (See the relevant + * @ref pxe_x86_pmode16 "implementation note" for more details.) + * + * @note According to the PXE specification version 2.1, this call + * "opens a file for reading/writing", though how writing is to be + * achieved without the existence of an API call %pxenv_tftp_write() + * is not made clear. + * + * @note Despite the existence of the numerous statements within the + * PXE specification of the form "...if a TFTP/MTFTP or UDP connection + * is active...", you cannot use pxenv_tftp_open() and + * pxenv_tftp_read() to read a file via MTFTP; only via plain old + * TFTP. If you want to use MTFTP, use pxenv_tftp_read_file() + * instead. Astute readers will note that, since + * pxenv_tftp_read_file() is an atomic operation from the point of + * view of the PXE API, it is conceptually impossible to issue any + * other PXE API call "if an MTFTP connection is active". + */ +PXENV_EXIT_t pxenv_tftp_open ( struct s_PXENV_TFTP_OPEN *tftp_open ) { + int rc; + + DBG ( "PXENV_TFTP_OPEN" ); + + /* Guard against callers that fail to close before re-opening */ + pxe_tftp_close ( 0 ); + + /* Open connection */ + if ( ( rc = pxe_tftp_open ( tftp_open->ServerIPAddress, + tftp_open->TFTPPort, + tftp_open->FileName, + tftp_open->PacketSize ) ) != 0 ) { + tftp_open->Status = PXENV_STATUS ( rc ); + return PXENV_EXIT_FAILURE; + } + + /* Wait for OACK to arrive so that we have the block size */ + while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) && + ( pxe_tftp.max_offset == 0 ) ) { + step(); + } + pxe_tftp.blksize = xfer_window ( &pxe_tftp.xfer ); + tftp_open->PacketSize = pxe_tftp.blksize; + DBG ( " blksize=%d", tftp_open->PacketSize ); + + /* EINPROGRESS is normal; we don't wait for the whole transfer */ + if ( rc == -EINPROGRESS ) + rc = 0; + + tftp_open->Status = PXENV_STATUS ( rc ); + return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS ); +} + +/** + * TFTP CLOSE + * + * @v tftp_close Pointer to a struct s_PXENV_TFTP_CLOSE + * @ret #PXENV_EXIT_SUCCESS File was closed successfully + * @ret #PXENV_EXIT_FAILURE File was not closed + * @ret s_PXENV_TFTP_CLOSE::Status PXE status code + * @err None - + * + * Close a connection previously opened with pxenv_tftp_open(). You + * must have previously opened a connection with pxenv_tftp_open(). + * + * On x86, you must set the s_PXE::StatusCallout field to a nonzero + * value before calling this function in protected mode. You cannot + * call this function with a 32-bit stack segment. (See the relevant + * @ref pxe_x86_pmode16 "implementation note" for more details.) + */ +PXENV_EXIT_t pxenv_tftp_close ( struct s_PXENV_TFTP_CLOSE *tftp_close ) { + DBG ( "PXENV_TFTP_CLOSE" ); + + pxe_tftp_close ( 0 ); + tftp_close->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/** + * TFTP READ + * + * @v tftp_read Pointer to a struct s_PXENV_TFTP_READ + * @v s_PXENV_TFTP_READ::Buffer Address of data buffer + * @ret #PXENV_EXIT_SUCCESS Data was read successfully + * @ret #PXENV_EXIT_FAILURE Data was not read + * @ret s_PXENV_TFTP_READ::Status PXE status code + * @ret s_PXENV_TFTP_READ::PacketNumber TFTP packet number + * @ret s_PXENV_TFTP_READ::BufferSize Length of data written into buffer + * + * Reads a single packet from a connection previously opened with + * pxenv_tftp_open() into the data buffer pointed to by + * s_PXENV_TFTP_READ::Buffer. You must have previously opened a + * connection with pxenv_tftp_open(). The data written into + * s_PXENV_TFTP_READ::Buffer is just the file data; the various + * network headers have already been removed. + * + * The buffer must be large enough to contain a packet of the size + * negotiated via the s_PXENV_TFTP_OPEN::PacketSize field in the + * pxenv_tftp_open() call. It is worth noting that the PXE + * specification does @b not require the caller to fill in + * s_PXENV_TFTP_READ::BufferSize before calling pxenv_tftp_read(), so + * the PXE stack is free to ignore whatever value the caller might + * place there and just assume that the buffer is large enough. That + * said, it may be worth the caller always filling in + * s_PXENV_TFTP_READ::BufferSize to guard against PXE stacks that + * mistake it for an input parameter. + * + * The length of the TFTP data packet will be returned via + * s_PXENV_TFTP_READ::BufferSize. If this length is less than the + * blksize negotiated via s_PXENV_TFTP_OPEN::PacketSize in the call to + * pxenv_tftp_open(), this indicates that the block is the last block + * in the file. Note that zero is a valid length for + * s_PXENV_TFTP_READ::BufferSize, and will occur when the length of + * the file is a multiple of the blksize. + * + * The PXE specification doesn't actually state that calls to + * pxenv_tftp_read() will return the data packets in strict sequential + * order, though most PXE stacks will probably do so. The sequence + * number of the packet will be returned in + * s_PXENV_TFTP_READ::PacketNumber. The first packet in the file has + * a sequence number of one, not zero. + * + * To guard against flawed PXE stacks, the caller should probably set + * s_PXENV_TFTP_READ::PacketNumber to one less than the expected + * returned value (i.e. set it to zero for the first call to + * pxenv_tftp_read() and then re-use the returned s_PXENV_TFTP_READ + * parameter block for subsequent calls without modifying + * s_PXENV_TFTP_READ::PacketNumber between calls). The caller should + * also guard against potential problems caused by flawed + * implementations returning the occasional duplicate packet, by + * checking that the value returned in s_PXENV_TFTP_READ::PacketNumber + * is as expected (i.e. one greater than that returned from the + * previous call to pxenv_tftp_read()). + * + * On x86, you must set the s_PXE::StatusCallout field to a nonzero + * value before calling this function in protected mode. You cannot + * call this function with a 32-bit stack segment. (See the relevant + * @ref pxe_x86_pmode16 "implementation note" for more details.) + */ +PXENV_EXIT_t pxenv_tftp_read ( struct s_PXENV_TFTP_READ *tftp_read ) { + int rc; + + DBG ( "PXENV_TFTP_READ to %04x:%04x", + tftp_read->Buffer.segment, tftp_read->Buffer.offset ); + + /* Read single block into buffer */ + pxe_tftp.buffer = real_to_user ( tftp_read->Buffer.segment, + tftp_read->Buffer.offset ); + pxe_tftp.size = pxe_tftp.blksize; + pxe_tftp.start = pxe_tftp.offset; + while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) && + ( pxe_tftp.offset == pxe_tftp.start ) ) + step(); + pxe_tftp.buffer = UNULL; + tftp_read->BufferSize = ( pxe_tftp.offset - pxe_tftp.start ); + tftp_read->PacketNumber = ++pxe_tftp.blkidx; + + /* EINPROGRESS is normal if we haven't reached EOF yet */ + if ( rc == -EINPROGRESS ) + rc = 0; + + tftp_read->Status = PXENV_STATUS ( rc ); + return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS ); +} + +/** + * TFTP/MTFTP read file + * + * @v tftp_read_file Pointer to a struct s_PXENV_TFTP_READ_FILE + * @v s_PXENV_TFTP_READ_FILE::FileName File name + * @v s_PXENV_TFTP_READ_FILE::BufferSize Size of the receive buffer + * @v s_PXENV_TFTP_READ_FILE::Buffer Address of the receive buffer + * @v s_PXENV_TFTP_READ_FILE::ServerIPAddress TFTP server IP address + * @v s_PXENV_TFTP_READ_FILE::GatewayIPAddress Relay agent IP address + * @v s_PXENV_TFTP_READ_FILE::McastIPAddress File's multicast IP address + * @v s_PXENV_TFTP_READ_FILE::TFTPClntPort Client multicast UDP port + * @v s_PXENV_TFTP_READ_FILE::TFTPSrvPort Server multicast UDP port + * @v s_PXENV_TFTP_READ_FILE::TFTPOpenTimeOut Time to wait for first packet + * @v s_PXENV_TFTP_READ_FILE::TFTPReopenDelay MTFTP inactivity timeout + * @ret #PXENV_EXIT_SUCCESS File downloaded successfully + * @ret #PXENV_EXIT_FAILURE File not downloaded + * @ret s_PXENV_TFTP_READ_FILE::Status PXE status code + * @ret s_PXENV_TFTP_READ_FILE::BufferSize Length of downloaded file + * + * Downloads an entire file via either TFTP or MTFTP into the buffer + * pointed to by s_PXENV_TFTP_READ_FILE::Buffer. + * + * The PXE specification does not make it clear how the caller + * requests that MTFTP be used rather than TFTP (or vice versa). One + * reasonable guess is that setting + * s_PXENV_TFTP_READ_FILE::McastIPAddress to 0.0.0.0 would cause TFTP + * to be used instead of MTFTP, though it is conceivable that some PXE + * stacks would interpret that as "use the DHCP-provided multicast IP + * address" instead. Some PXE stacks will not implement MTFTP at all, + * and will always use TFTP. + * + * It is not specified whether or not + * s_PXENV_TFTP_READ_FILE::TFTPSrvPort will be used as the TFTP server + * port for TFTP (rather than MTFTP) downloads. Callers should assume + * that the only way to access a TFTP server on a non-standard port is + * to use pxenv_tftp_open() and pxenv_tftp_read(). + * + * If s_PXENV_TFTP_READ_FILE::GatewayIPAddress is 0.0.0.0, normal IP + * routing will take place. See the relevant + * @ref pxe_routing "implementation note" for more details. + * + * It is interesting to note that s_PXENV_TFTP_READ_FILE::Buffer is an + * #ADDR32_t type, i.e. nominally a flat physical address. Some PXE + * NBPs (e.g. NTLDR) are known to call pxenv_tftp_read_file() in real + * mode with s_PXENV_TFTP_READ_FILE::Buffer set to an address above + * 1MB. This means that PXE stacks must be prepared to write to areas + * outside base memory. Exactly how this is to be achieved is not + * specified, though using INT 15,87 is as close to a standard method + * as any, and should probably be used. Switching to protected-mode + * in order to access high memory will fail if pxenv_tftp_read_file() + * is called in V86 mode; it is reasonably to expect that a V86 + * monitor would intercept the relatively well-defined INT 15,87 if it + * wants the PXE stack to be able to write to high memory. + * + * Things get even more interesting if pxenv_tftp_read_file() is + * called in protected mode, because there is then absolutely no way + * for the PXE stack to write to an absolute physical address. You + * can't even get around the problem by creating a special "access + * everything" segment in the s_PXE data structure, because the + * #SEGDESC_t descriptors are limited to 64kB in size. + * + * Previous versions of the PXE specification (e.g. WfM 1.1a) provide + * a separate API call, %pxenv_tftp_read_file_pmode(), specifically to + * work around this problem. The s_PXENV_TFTP_READ_FILE_PMODE + * parameter block splits s_PXENV_TFTP_READ_FILE::Buffer into + * s_PXENV_TFTP_READ_FILE_PMODE::BufferSelector and + * s_PXENV_TFTP_READ_FILE_PMODE::BufferOffset, i.e. it provides a + * protected-mode segment:offset address for the data buffer. This + * API call is no longer present in version 2.1 of the PXE + * specification. + * + * Etherboot makes the assumption that s_PXENV_TFTP_READ_FILE::Buffer + * is an offset relative to the caller's data segment, when + * pxenv_tftp_read_file() is called in protected mode. + * + * On x86, you must set the s_PXE::StatusCallout field to a nonzero + * value before calling this function in protected mode. You cannot + * call this function with a 32-bit stack segment. (See the relevant + * @ref pxe_x86_pmode16 "implementation note" for more details.) + * + * @note Microsoft's NTLDR assumes that the filename passed in via + * s_PXENV_TFTP_READ_FILE::FileName will be stored in the "file" field + * of the stored DHCPACK packet, whence it will be returned via any + * subsequent calls to pxenv_get_cached_info(). Though this is + * essentially a bug in the Intel PXE implementation (not, for once, + * in the specification!), it is a bug that Microsoft relies upon, and + * so we implement this bug-for-bug compatibility by overwriting the + * filename stored DHCPACK packet with the filename passed in + * s_PXENV_TFTP_READ_FILE::FileName. + * + */ +PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE + *tftp_read_file ) { + int rc; + + DBG ( "PXENV_TFTP_READ_FILE to %08x+%x", tftp_read_file->Buffer, + tftp_read_file->BufferSize ); + + /* Open TFTP file */ + if ( ( rc = pxe_tftp_open ( tftp_read_file->ServerIPAddress, 0, + tftp_read_file->FileName, 0 ) ) != 0 ) { + tftp_read_file->Status = PXENV_STATUS ( rc ); + return PXENV_EXIT_FAILURE; + } + + /* Read entire file */ + pxe_tftp.buffer = phys_to_user ( tftp_read_file->Buffer ); + pxe_tftp.size = tftp_read_file->BufferSize; + while ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) + step(); + pxe_tftp.buffer = UNULL; + tftp_read_file->BufferSize = pxe_tftp.max_offset; + + /* Close TFTP file */ + pxe_tftp_close ( rc ); + + tftp_read_file->Status = PXENV_STATUS ( rc ); + return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS ); +} + +/** + * TFTP GET FILE SIZE + * + * @v tftp_get_fsize Pointer to a struct s_PXENV_TFTP_GET_FSIZE + * @v s_PXENV_TFTP_GET_FSIZE::ServerIPAddress TFTP server IP address + * @v s_PXENV_TFTP_GET_FSIZE::GatewayIPAddress Relay agent IP address + * @v s_PXENV_TFTP_GET_FSIZE::FileName File name + * @ret #PXENV_EXIT_SUCCESS File size was determined successfully + * @ret #PXENV_EXIT_FAILURE File size was not determined + * @ret s_PXENV_TFTP_GET_FSIZE::Status PXE status code + * @ret s_PXENV_TFTP_GET_FSIZE::FileSize File size + * + * Determine the size of a file on a TFTP server. This uses the + * "tsize" TFTP option, and so will not work with a TFTP server that + * does not support TFTP options, or that does not support the "tsize" + * option. + * + * The PXE specification states that this API call will @b not open a + * TFTP connection for subsequent use with pxenv_tftp_read(). (This + * is somewhat daft, since the only way to obtain the file size via + * the "tsize" option involves issuing a TFTP open request, but that's + * life.) + * + * You cannot call pxenv_tftp_get_fsize() while a TFTP or UDP + * connection is open. + * + * If s_PXENV_TFTP_GET_FSIZE::GatewayIPAddress is 0.0.0.0, normal IP + * routing will take place. See the relevant + * @ref pxe_routing "implementation note" for more details. + * + * On x86, you must set the s_PXE::StatusCallout field to a nonzero + * value before calling this function in protected mode. You cannot + * call this function with a 32-bit stack segment. (See the relevant + * @ref pxe_x86_pmode16 "implementation note" for more details.) + * + * @note There is no way to specify the TFTP server port with this API + * call. Though you can open a file using a non-standard TFTP server + * port (via s_PXENV_TFTP_OPEN::TFTPPort or, potentially, + * s_PXENV_TFTP_READ_FILE::TFTPSrvPort), you can only get the size of + * a file from a TFTP server listening on the standard TFTP port. + * "Consistency" is not a word in Intel's vocabulary. + */ +PXENV_EXIT_t pxenv_tftp_get_fsize ( struct s_PXENV_TFTP_GET_FSIZE + *tftp_get_fsize ) { + int rc; + + DBG ( "PXENV_TFTP_GET_FSIZE" ); + + /* Open TFTP file */ + if ( ( rc = pxe_tftp_open ( tftp_get_fsize->ServerIPAddress, 0, + tftp_get_fsize->FileName, 0 ) ) != 0 ) { + tftp_get_fsize->Status = PXENV_STATUS ( rc ); + return PXENV_EXIT_FAILURE; + } + + /* Wait for initial seek to arrive, and record size */ + while ( ( ( rc = pxe_tftp.rc ) == -EINPROGRESS ) && + ( pxe_tftp.max_offset == 0 ) ) { + step(); + } + tftp_get_fsize->FileSize = pxe_tftp.max_offset; + DBG ( " fsize=%d", tftp_get_fsize->FileSize ); + + /* EINPROGRESS is normal; we don't wait for the whole transfer */ + if ( rc == -EINPROGRESS ) + rc = 0; + + /* Close TFTP file */ + pxe_tftp_close ( rc ); + + tftp_get_fsize->Status = PXENV_STATUS ( rc ); + return ( rc ? PXENV_EXIT_FAILURE : PXENV_EXIT_SUCCESS ); +} diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_udp.c b/gpxe/src/arch/i386/interface/pxe/pxe_udp.c new file mode 100644 index 0000000..033b1ad --- /dev/null +++ b/gpxe/src/arch/i386/interface/pxe/pxe_udp.c @@ -0,0 +1,403 @@ +/** @file + * + * PXE UDP API + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * 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. + */ + +/** A PXE UDP connection */ +struct pxe_udp_connection { + /** Data transfer interface to UDP stack */ + struct xfer_interface xfer; + /** Local address */ + struct sockaddr_in local; + /** Current PXENV_UDP_READ parameter block */ + struct s_PXENV_UDP_READ *pxenv_udp_read; +}; + +/** + * Receive PXE UDP data + * + * @v xfer Data transfer interface + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + * + * Receives a packet as part of the current pxenv_udp_read() + * operation. + */ +static int pxe_udp_deliver_iob ( struct xfer_interface *xfer, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct pxe_udp_connection *pxe_udp = + container_of ( xfer, struct pxe_udp_connection, xfer ); + struct s_PXENV_UDP_READ *pxenv_udp_read = pxe_udp->pxenv_udp_read; + struct sockaddr_in *sin_src; + struct sockaddr_in *sin_dest; + userptr_t buffer; + size_t len; + int rc = 0; + + if ( ! pxenv_udp_read ) { + DBG ( "PXE discarded UDP packet\n" ); + rc = -ENOBUFS; + goto done; + } + + /* Copy packet to buffer and record length */ + buffer = real_to_user ( pxenv_udp_read->buffer.segment, + pxenv_udp_read->buffer.offset ); + len = iob_len ( iobuf ); + if ( len > pxenv_udp_read->buffer_size ) + len = pxenv_udp_read->buffer_size; + copy_to_user ( buffer, 0, iobuf->data, len ); + pxenv_udp_read->buffer_size = len; + + /* Fill in source/dest information */ + assert ( meta ); + sin_src = ( struct sockaddr_in * ) meta->src; + assert ( sin_src ); + assert ( sin_src->sin_family == AF_INET ); + pxenv_udp_read->src_ip = sin_src->sin_addr.s_addr; + pxenv_udp_read->s_port = sin_src->sin_port; + sin_dest = ( struct sockaddr_in * ) meta->dest; + assert ( sin_dest ); + assert ( sin_dest->sin_family == AF_INET ); + pxenv_udp_read->dest_ip = sin_dest->sin_addr.s_addr; + pxenv_udp_read->d_port = sin_dest->sin_port; + + /* Mark as received */ + pxe_udp->pxenv_udp_read = NULL; + + done: + free_iob ( iobuf ); + return rc; +} + +/** PXE UDP data transfer interface operations */ +static struct xfer_interface_operations pxe_udp_xfer_operations = { + .close = ignore_xfer_close, + .vredirect = ignore_xfer_vredirect, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = pxe_udp_deliver_iob, + .deliver_raw = xfer_deliver_as_iob, +}; + +/** The PXE UDP connection */ +static struct pxe_udp_connection pxe_udp = { + .xfer = XFER_INIT ( &pxe_udp_xfer_operations ), + .local = { + .sin_family = AF_INET, + }, +}; + +/** + * UDP OPEN + * + * @v pxenv_udp_open Pointer to a struct s_PXENV_UDP_OPEN + * @v s_PXENV_UDP_OPEN::src_ip IP address of this station, or 0.0.0.0 + * @ret #PXENV_EXIT_SUCCESS Always + * @ret s_PXENV_UDP_OPEN::Status PXE status code + * @err #PXENV_STATUS_UDP_OPEN UDP connection already open + * @err #PXENV_STATUS_OUT_OF_RESOURCES Could not open connection + * + * Prepares the PXE stack for communication using pxenv_udp_write() + * and pxenv_udp_read(). + * + * The IP address supplied in s_PXENV_UDP_OPEN::src_ip will be + * recorded and used as the local station's IP address for all further + * communication, including communication by means other than + * pxenv_udp_write() and pxenv_udp_read(). (If + * s_PXENV_UDP_OPEN::src_ip is 0.0.0.0, the local station's IP address + * will remain unchanged.) + * + * You can only have one open UDP connection at a time. This is not a + * meaningful restriction, since pxenv_udp_write() and + * pxenv_udp_read() allow you to specify arbitrary local and remote + * ports and an arbitrary remote address for each packet. According + * to the PXE specifiation, you cannot have a UDP connection open at + * the same time as a TFTP connection; this restriction does not apply + * to Etherboot. + * + * On x86, you must set the s_PXE::StatusCallout field to a nonzero + * value before calling this function in protected mode. You cannot + * call this function with a 32-bit stack segment. (See the relevant + * @ref pxe_x86_pmode16 "implementation note" for more details.) + * + * @note The PXE specification does not make it clear whether the IP + * address supplied in s_PXENV_UDP_OPEN::src_ip should be used only + * for this UDP connection, or retained for all future communication. + * The latter seems more consistent with typical PXE stack behaviour. + * + * @note Etherboot currently ignores the s_PXENV_UDP_OPEN::src_ip + * parameter. + * + */ +PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *pxenv_udp_open ) { + int rc; + + DBG ( "PXENV_UDP_OPEN" ); + + /* Record source IP address */ + pxe_udp.local.sin_addr.s_addr = pxenv_udp_open->src_ip; + DBG ( " %s", inet_ntoa ( pxe_udp.local.sin_addr ) ); + + /* Open promiscuous UDP connection */ + xfer_close ( &pxe_udp.xfer, 0 ); + if ( ( rc = udp_open_promisc ( &pxe_udp.xfer ) ) != 0 ) { + pxenv_udp_open->Status = PXENV_STATUS ( rc ); + return PXENV_EXIT_FAILURE; + } + + pxenv_udp_open->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/** + * UDP CLOSE + * + * @v pxenv_udp_close Pointer to a struct s_PXENV_UDP_CLOSE + * @ret #PXENV_EXIT_SUCCESS Always + * @ret s_PXENV_UDP_CLOSE::Status PXE status code + * @err None - + * + * Closes a UDP connection opened with pxenv_udp_open(). + * + * You can only have one open UDP connection at a time. You cannot + * have a UDP connection open at the same time as a TFTP connection. + * You cannot use pxenv_udp_close() to close a TFTP connection; use + * pxenv_tftp_close() instead. + * + * On x86, you must set the s_PXE::StatusCallout field to a nonzero + * value before calling this function in protected mode. You cannot + * call this function with a 32-bit stack segment. (See the relevant + * @ref pxe_x86_pmode16 "implementation note" for more details.) + * + */ +PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *pxenv_udp_close ) { + DBG ( "PXENV_UDP_CLOSE" ); + + /* Close UDP connection */ + xfer_close ( &pxe_udp.xfer, 0 ); + + pxenv_udp_close->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/** + * UDP WRITE + * + * @v pxenv_udp_write Pointer to a struct s_PXENV_UDP_WRITE + * @v s_PXENV_UDP_WRITE::ip Destination IP address + * @v s_PXENV_UDP_WRITE::gw Relay agent IP address, or 0.0.0.0 + * @v s_PXENV_UDP_WRITE::src_port Source UDP port, or 0 + * @v s_PXENV_UDP_WRITE::dst_port Destination UDP port + * @v s_PXENV_UDP_WRITE::buffer_size Length of the UDP payload + * @v s_PXENV_UDP_WRITE::buffer Address of the UDP payload + * @ret #PXENV_EXIT_SUCCESS Packet was transmitted successfully + * @ret #PXENV_EXIT_FAILURE Packet could not be transmitted + * @ret s_PXENV_UDP_WRITE::Status PXE status code + * @err #PXENV_STATUS_UDP_CLOSED UDP connection is not open + * @err #PXENV_STATUS_UNDI_TRANSMIT_ERROR Could not transmit packet + * + * Transmits a single UDP packet. A valid IP and UDP header will be + * prepended to the payload in s_PXENV_UDP_WRITE::buffer; the buffer + * should not contain precomputed IP and UDP headers, nor should it + * contain space allocated for these headers. The first byte of the + * buffer will be transmitted as the first byte following the UDP + * header. + * + * If s_PXENV_UDP_WRITE::gw is 0.0.0.0, normal IP routing will take + * place. See the relevant @ref pxe_routing "implementation note" for + * more details. + * + * If s_PXENV_UDP_WRITE::src_port is 0, port 2069 will be used. + * + * You must have opened a UDP connection with pxenv_udp_open() before + * calling pxenv_udp_write(). + * + * On x86, you must set the s_PXE::StatusCallout field to a nonzero + * value before calling this function in protected mode. You cannot + * call this function with a 32-bit stack segment. (See the relevant + * @ref pxe_x86_pmode16 "implementation note" for more details.) + * + * @note Etherboot currently ignores the s_PXENV_UDP_WRITE::gw + * parameter. + * + */ +PXENV_EXIT_t pxenv_udp_write ( struct s_PXENV_UDP_WRITE *pxenv_udp_write ) { + struct sockaddr_in dest; + struct xfer_metadata meta = { + .src = ( struct sockaddr * ) &pxe_udp.local, + .dest = ( struct sockaddr * ) &dest, + .netdev = pxe_netdev, + }; + size_t len; + struct io_buffer *iobuf; + userptr_t buffer; + int rc; + + DBG ( "PXENV_UDP_WRITE" ); + + /* Construct destination socket address */ + memset ( &dest, 0, sizeof ( dest ) ); + dest.sin_family = AF_INET; + dest.sin_addr.s_addr = pxenv_udp_write->ip; + dest.sin_port = pxenv_udp_write->dst_port; + + /* Set local (source) port. PXE spec says source port is 2069 + * if not specified. Really, this ought to be set at UDP open + * time but hey, we didn't design this API. + */ + pxe_udp.local.sin_port = pxenv_udp_write->src_port; + if ( ! pxe_udp.local.sin_port ) + pxe_udp.local.sin_port = htons ( 2069 ); + + /* FIXME: we ignore the gateway specified, since we're + * confident of being able to do our own routing. We should + * probably allow for multiple gateways. + */ + + /* Allocate and fill data buffer */ + len = pxenv_udp_write->buffer_size; + iobuf = xfer_alloc_iob ( &pxe_udp.xfer, len ); + if ( ! iobuf ) { + pxenv_udp_write->Status = PXENV_STATUS_OUT_OF_RESOURCES; + return PXENV_EXIT_FAILURE; + } + buffer = real_to_user ( pxenv_udp_write->buffer.segment, + pxenv_udp_write->buffer.offset ); + copy_from_user ( iob_put ( iobuf, len ), buffer, 0, len ); + + DBG ( " %04x:%04x+%x %d->%s:%d", pxenv_udp_write->buffer.segment, + pxenv_udp_write->buffer.offset, pxenv_udp_write->buffer_size, + ntohs ( pxenv_udp_write->src_port ), + inet_ntoa ( dest.sin_addr ), + ntohs ( pxenv_udp_write->dst_port ) ); + + /* Transmit packet */ + if ( ( rc = xfer_deliver_iob_meta ( &pxe_udp.xfer, iobuf, + &meta ) ) != 0 ) { + pxenv_udp_write->Status = PXENV_STATUS ( rc ); + return PXENV_EXIT_FAILURE; + } + + pxenv_udp_write->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/** + * UDP READ + * + * @v pxenv_udp_read Pointer to a struct s_PXENV_UDP_READ + * @v s_PXENV_UDP_READ::dest_ip Destination IP address, or 0.0.0.0 + * @v s_PXENV_UDP_READ::d_port Destination UDP port, or 0 + * @v s_PXENV_UDP_READ::buffer_size Size of the UDP payload buffer + * @v s_PXENV_UDP_READ::buffer Address of the UDP payload buffer + * @ret #PXENV_EXIT_SUCCESS A packet has been received + * @ret #PXENV_EXIT_FAILURE No packet has been received + * @ret s_PXENV_UDP_READ::Status PXE status code + * @ret s_PXENV_UDP_READ::src_ip Source IP address + * @ret s_PXENV_UDP_READ::dest_ip Destination IP address + * @ret s_PXENV_UDP_READ::s_port Source UDP port + * @ret s_PXENV_UDP_READ::d_port Destination UDP port + * @ret s_PXENV_UDP_READ::buffer_size Length of UDP payload + * @err #PXENV_STATUS_UDP_CLOSED UDP connection is not open + * @err #PXENV_STATUS_FAILURE No packet was ready to read + * + * Receive a single UDP packet. This is a non-blocking call; if no + * packet is ready to read, the call will return instantly with + * s_PXENV_UDP_READ::Status==PXENV_STATUS_FAILURE. + * + * If s_PXENV_UDP_READ::dest_ip is 0.0.0.0, UDP packets addressed to + * any IP address will be accepted and may be returned to the caller. + * + * If s_PXENV_UDP_READ::d_port is 0, UDP packets addressed to any UDP + * port will be accepted and may be returned to the caller. + * + * You must have opened a UDP connection with pxenv_udp_open() before + * calling pxenv_udp_read(). + * + * On x86, you must set the s_PXE::StatusCallout field to a nonzero + * value before calling this function in protected mode. You cannot + * call this function with a 32-bit stack segment. (See the relevant + * @ref pxe_x86_pmode16 "implementation note" for more details.) + * + * @note The PXE specification (version 2.1) does not state that we + * should fill in s_PXENV_UDP_READ::dest_ip and + * s_PXENV_UDP_READ::d_port, but Microsoft Windows' NTLDR program + * expects us to do so, and will fail if we don't. + * + */ +PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) { + struct in_addr dest_ip_wanted = { .s_addr = pxenv_udp_read->dest_ip }; + struct in_addr dest_ip; + uint16_t d_port_wanted = pxenv_udp_read->d_port; + uint16_t d_port; + + DBG ( "PXENV_UDP_READ" ); + + /* Try receiving a packet */ + pxe_udp.pxenv_udp_read = pxenv_udp_read; + step(); + if ( pxe_udp.pxenv_udp_read ) { + /* No packet received */ + pxe_udp.pxenv_udp_read = NULL; + goto no_packet; + } + dest_ip.s_addr = pxenv_udp_read->dest_ip; + d_port = pxenv_udp_read->d_port; + + /* Filter on destination address and/or port */ + if ( dest_ip_wanted.s_addr && + ( dest_ip_wanted.s_addr != dest_ip.s_addr ) ) { + DBG ( " wrong IP %s", inet_ntoa ( dest_ip ) ); + DBG ( " (wanted %s)", inet_ntoa ( dest_ip_wanted ) ); + goto no_packet; + } + if ( d_port_wanted && ( d_port_wanted != d_port ) ) { + DBG ( " wrong port %d ", htons ( d_port ) ); + DBG ( " (wanted %d)", htons ( d_port_wanted ) ); + goto no_packet; + } + + DBG ( " %04x:%04x+%x %s:", pxenv_udp_read->buffer.segment, + pxenv_udp_read->buffer.offset, pxenv_udp_read->buffer_size, + inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->src_ip ) )); + DBG ( "%d<-%s:%d", ntohs ( pxenv_udp_read->s_port ), + inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->dest_ip ) ), + ntohs ( pxenv_udp_read->d_port ) ); + + pxenv_udp_read->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; + + no_packet: + pxenv_udp_read->Status = PXENV_STATUS_FAILURE; + return PXENV_EXIT_FAILURE; +} diff --git a/gpxe/src/arch/i386/interface/pxe/pxe_undi.c b/gpxe/src/arch/i386/interface/pxe/pxe_undi.c new file mode 100644 index 0000000..4e4a3da --- /dev/null +++ b/gpxe/src/arch/i386/interface/pxe/pxe_undi.c @@ -0,0 +1,684 @@ +/** @file + * + * PXE UNDI API + * + */ + +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pxe.h" + +/** + * Count of outstanding transmitted packets + * + * This is incremented each time PXENV_UNDI_TRANSMIT is called, and + * decremented each time that PXENV_UNDI_ISR is called with the TX + * queue empty, stopping when the count reaches zero. This allows us + * to provide a pessimistic approximation of TX completion events to + * the PXE NBP simply by monitoring the netdev's TX queue. + */ +static int undi_tx_count = 0; + +struct net_device *pxe_netdev = NULL; + +/** + * Set network device as current PXE network device + * + * @v netdev Network device, or NULL + */ +void pxe_set_netdev ( struct net_device *netdev ) { + if ( pxe_netdev ) + netdev_put ( pxe_netdev ); + pxe_netdev = NULL; + if ( netdev ) + pxe_netdev = netdev_get ( netdev ); +} + +/** + * Open PXE network device + * + * @ret rc Return status code + */ +static int pxe_netdev_open ( void ) { + int rc; + + if ( ( rc = netdev_open ( pxe_netdev ) ) != 0 ) + return rc; + + netdev_irq ( pxe_netdev, 1 ); + return 0; +} + +/** + * Close PXE network device + * + */ +static void pxe_netdev_close ( void ) { + netdev_irq ( pxe_netdev, 0 ); + netdev_close ( pxe_netdev ); + undi_tx_count = 0; +} + +/* PXENV_UNDI_STARTUP + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) { + DBG ( "PXENV_UNDI_STARTUP" ); + + undi_startup->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_CLEANUP + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) { + DBG ( "PXENV_UNDI_CLEANUP" ); + + pxe_netdev_close(); + + undi_cleanup->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_INITIALIZE + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE + *undi_initialize ) { + DBG ( "PXENV_UNDI_INITIALIZE" ); + + undi_initialize->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_RESET_ADAPTER + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET + *undi_reset_adapter ) { + int rc; + + DBG ( "PXENV_UNDI_RESET_ADAPTER" ); + + pxe_netdev_close(); + if ( ( rc = pxe_netdev_open() ) != 0 ) { + undi_reset_adapter->Status = PXENV_STATUS ( rc ); + return PXENV_EXIT_FAILURE; + } + + undi_reset_adapter->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_SHUTDOWN + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN + *undi_shutdown ) { + DBG ( "PXENV_UNDI_SHUTDOWN" ); + + pxe_netdev_close(); + + undi_shutdown->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_OPEN + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) { + int rc; + + DBG ( "PXENV_UNDI_OPEN" ); + + if ( ( rc = pxe_netdev_open() ) != 0 ) { + undi_open->Status = PXENV_STATUS ( rc ); + return PXENV_EXIT_FAILURE; + } + + undi_open->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_CLOSE + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) { + DBG ( "PXENV_UNDI_CLOSE" ); + + pxe_netdev_close(); + + undi_close->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_TRANSMIT + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT + *undi_transmit ) { + struct s_PXENV_UNDI_TBD tbd; + struct DataBlk *datablk; + struct io_buffer *iobuf; + struct net_protocol *net_protocol; + struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol; + char destaddr[MAX_LL_ADDR_LEN]; + const void *ll_dest; + size_t ll_hlen = ll_protocol->ll_header_len; + size_t len; + unsigned int i; + int rc; + + DBG ( "PXENV_UNDI_TRANSMIT" ); + + /* Identify network-layer protocol */ + switch ( undi_transmit->Protocol ) { + case P_IP: net_protocol = &ipv4_protocol; break; + case P_ARP: net_protocol = &arp_protocol; break; + case P_RARP: net_protocol = &rarp_protocol; break; + case P_UNKNOWN: + net_protocol = NULL; + ll_hlen = 0; + break; + default: + undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER; + return PXENV_EXIT_FAILURE; + } + DBG ( " %s", ( net_protocol ? net_protocol->name : "RAW" ) ); + + /* Calculate total packet length */ + copy_from_real ( &tbd, undi_transmit->TBD.segment, + undi_transmit->TBD.offset, sizeof ( tbd ) ); + len = tbd.ImmedLength; + DBG ( " %d", tbd.ImmedLength ); + for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) { + datablk = &tbd.DataBlock[i]; + len += datablk->TDDataLen; + DBG ( "+%d", datablk->TDDataLen ); + } + + /* Allocate and fill I/O buffer */ + iobuf = alloc_iob ( ll_hlen + len ); + if ( ! iobuf ) { + undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES; + return PXENV_EXIT_FAILURE; + } + iob_reserve ( iobuf, ll_hlen ); + copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment, + tbd.Xmit.offset, tbd.ImmedLength ); + for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) { + datablk = &tbd.DataBlock[i]; + copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ), + datablk->TDDataPtr.segment, + datablk->TDDataPtr.offset, + datablk->TDDataLen ); + } + + /* Add link-layer header, if required to do so */ + if ( net_protocol != NULL ) { + + /* Calculate destination address */ + if ( undi_transmit->XmitFlag == XMT_DESTADDR ) { + copy_from_real ( destaddr, + undi_transmit->DestAddr.segment, + undi_transmit->DestAddr.offset, + ll_protocol->ll_addr_len ); + ll_dest = destaddr; + } else { + DBG ( " BCAST" ); + ll_dest = ll_protocol->ll_broadcast; + } + + /* Add link-layer header */ + if ( ( rc = ll_protocol->push ( iobuf, ll_dest, + pxe_netdev->ll_addr, + net_protocol->net_proto ))!=0){ + free_iob ( iobuf ); + undi_transmit->Status = PXENV_STATUS ( rc ); + return PXENV_EXIT_FAILURE; + } + } + + /* Transmit packet */ + if ( ( rc = netdev_tx ( pxe_netdev, iobuf ) ) != 0 ) { + undi_transmit->Status = PXENV_STATUS ( rc ); + return PXENV_EXIT_FAILURE; + } + + /* Flag transmission as in-progress */ + undi_tx_count++; + + 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 ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS + *undi_set_mcast_address ) { + DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" ); + + undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED; + return PXENV_EXIT_FAILURE; +} + +/* PXENV_UNDI_SET_STATION_ADDRESS + * + * Status: working + */ +PXENV_EXIT_t +pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS + *undi_set_station_address ) { + + DBG ( "PXENV_UNDI_SET_STATION_ADDRESS" ); + + /* If adapter is open, the change will have no effect; return + * an error + */ + if ( pxe_netdev->state & NETDEV_OPEN ) { + undi_set_station_address->Status = + PXENV_STATUS_UNDI_INVALID_STATE; + return PXENV_EXIT_FAILURE; + } + + /* Update MAC address */ + memcpy ( pxe_netdev->ll_addr, + &undi_set_station_address->StationAddress, + pxe_netdev->ll_protocol->ll_addr_len ); + + undi_set_station_address->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_SET_PACKET_FILTER + * + * Status: won't implement (would require driver API changes for no + * real benefit) + */ +PXENV_EXIT_t +pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER + *undi_set_packet_filter ) { + DBG ( "PXENV_UNDI_SET_PACKET_FILTER" ); + + undi_set_packet_filter->Status = PXENV_STATUS_UNSUPPORTED; + return PXENV_EXIT_FAILURE; +} + +/* PXENV_UNDI_GET_INFORMATION + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION + *undi_get_information ) { + struct device *dev = pxe_netdev->dev; + struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol; + + DBG ( "PXENV_UNDI_GET_INFORMATION" ); + + undi_get_information->BaseIo = dev->desc.ioaddr; + undi_get_information->IntNumber = dev->desc.irq; + /* Cheat: assume all cards can cope with this */ + undi_get_information->MaxTranUnit = ETH_MAX_MTU; + undi_get_information->HwType = ntohs ( ll_protocol->ll_proto ); + undi_get_information->HwAddrLen = ll_protocol->ll_addr_len; + /* Cheat: assume card is always configured with its permanent + * node address. This is a valid assumption within Etherboot + * at the time of writing. + */ + memcpy ( &undi_get_information->CurrentNodeAddress, + pxe_netdev->ll_addr, + sizeof ( undi_get_information->CurrentNodeAddress ) ); + memcpy ( &undi_get_information->PermNodeAddress, + pxe_netdev->ll_addr, + sizeof ( undi_get_information->PermNodeAddress ) ); + undi_get_information->ROMAddress = 0; + /* nic.rom_info->rom_segment; */ + /* We only provide the ability to receive or transmit a single + * packet at a time. This is a bootloader, not an OS. + */ + undi_get_information->RxBufCt = 1; + undi_get_information->TxBufCt = 1; + + undi_get_information->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_GET_STATISTICS + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS + *undi_get_statistics ) { + DBG ( "PXENV_UNDI_GET_STATISTICS" ); + + undi_get_statistics->XmtGoodFrames = pxe_netdev->tx_stats.good; + undi_get_statistics->RcvGoodFrames = pxe_netdev->rx_stats.good; + undi_get_statistics->RcvCRCErrors = pxe_netdev->rx_stats.bad; + undi_get_statistics->RcvResourceErrors = pxe_netdev->rx_stats.bad; + + undi_get_statistics->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_CLEAR_STATISTICS + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS + *undi_clear_statistics ) { + DBG ( "PXENV_UNDI_CLEAR_STATISTICS" ); + + memset ( &pxe_netdev->tx_stats, 0, sizeof ( pxe_netdev->tx_stats ) ); + memset ( &pxe_netdev->rx_stats, 0, sizeof ( pxe_netdev->rx_stats ) ); + + undi_clear_statistics->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_INITIATE_DIAGS + * + * Status: won't implement (would require driver API changes for no + * real benefit) + */ +PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS + *undi_initiate_diags ) { + DBG ( "PXENV_UNDI_INITIATE_DIAGS" ); + + undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED; + return PXENV_EXIT_FAILURE; +} + +/* PXENV_UNDI_FORCE_INTERRUPT + * + * Status: won't implement (would require driver API changes for no + * perceptible benefit) + */ +PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT + *undi_force_interrupt ) { + DBG ( "PXENV_UNDI_FORCE_INTERRUPT" ); + + undi_force_interrupt->Status = PXENV_STATUS_UNSUPPORTED; + return PXENV_EXIT_FAILURE; +} + +/* PXENV_UNDI_GET_MCAST_ADDRESS + * + * Status: stub (no PXE multicast support) + */ +PXENV_EXIT_t +pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS + *undi_get_mcast_address ) { + DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS" ); + + undi_get_mcast_address->Status = PXENV_STATUS_UNSUPPORTED; + return PXENV_EXIT_FAILURE; +} + +/* PXENV_UNDI_GET_NIC_TYPE + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE + *undi_get_nic_type ) { + struct device *dev = pxe_netdev->dev; + + DBG ( "PXENV_UNDI_GET_NIC_TYPE" ); + + memset ( &undi_get_nic_type->info, 0, + sizeof ( undi_get_nic_type->info ) ); + + switch ( dev->desc.bus_type ) { + case BUS_TYPE_PCI: { + struct pci_nic_info *info = &undi_get_nic_type->info.pci; + + undi_get_nic_type->NicType = PCI_NIC; + info->Vendor_ID = dev->desc.vendor; + info->Dev_ID = dev->desc.device; + info->Base_Class = PCI_BASE_CLASS ( dev->desc.class ); + info->Sub_Class = PCI_SUB_CLASS ( dev->desc.class ); + info->Prog_Intf = PCI_PROG_INTF ( dev->desc.class ); + info->BusDevFunc = dev->desc.location; + /* Cheat: remaining fields are probably unnecessary, + * and would require adding extra code to pci.c. + */ + undi_get_nic_type->info.pci.SubVendor_ID = 0xffff; + undi_get_nic_type->info.pci.SubDevice_ID = 0xffff; + break; } + case BUS_TYPE_ISAPNP: { + struct pnp_nic_info *info = &undi_get_nic_type->info.pnp; + + undi_get_nic_type->NicType = PnP_NIC; + info->EISA_Dev_ID = ( ( dev->desc.vendor << 16 ) | + dev->desc.device ); + info->CardSelNum = dev->desc.location; + /* Cheat: remaining fields are probably unnecessary, + * and would require adding extra code to isapnp.c. + */ + break; } + default: + undi_get_nic_type->Status = PXENV_STATUS_FAILURE; + return PXENV_EXIT_FAILURE; + } + + undi_get_nic_type->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_GET_IFACE_INFO + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO + *undi_get_iface_info ) { + DBG ( "PXENV_UNDI_GET_IFACE_INFO" ); + + /* Just hand back some info, doesn't really matter what it is. + * Most PXE stacks seem to take this approach. + */ + snprintf ( ( char * ) undi_get_iface_info->IfaceType, + sizeof ( undi_get_iface_info->IfaceType ), "gPXE" ); + undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */ + undi_get_iface_info->ServiceFlags = 0; + memset ( undi_get_iface_info->Reserved, 0, + sizeof(undi_get_iface_info->Reserved) ); + + undi_get_iface_info->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} + +/* PXENV_UNDI_GET_STATE + * + * Status: impossible + */ +PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE + *undi_get_state ) { + DBG ( "PXENV_UNDI_GET_STATE" ); + + undi_get_state->Status = PXENV_STATUS_UNSUPPORTED; + return PXENV_EXIT_FAILURE; +}; + +/* PXENV_UNDI_ISR + * + * Status: working + */ +PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) { + struct io_buffer *iobuf; + size_t len; + struct ll_protocol *ll_protocol; + const void *ll_dest; + const void *ll_source; + uint16_t net_proto; + size_t ll_hlen; + struct net_protocol *net_protocol; + unsigned int prottype; + int rc; + + DBG ( "PXENV_UNDI_ISR" ); + + /* Just in case some idiot actually looks at these fields when + * we weren't meant to fill them in... + */ + undi_isr->BufferLength = 0; + undi_isr->FrameLength = 0; + undi_isr->FrameHeaderLength = 0; + undi_isr->ProtType = 0; + undi_isr->PktType = 0; + + switch ( undi_isr->FuncFlag ) { + case PXENV_UNDI_ISR_IN_START : + DBG ( " START" ); + + /* Call poll(). This should acknowledge the device + * interrupt and queue up any received packet. + */ + netdev_poll ( pxe_netdev ); + + /* Disable interrupts to avoid interrupt storm */ + netdev_irq ( pxe_netdev, 0 ); + + /* Always say it was ours for the sake of simplicity */ + undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS; + break; + case PXENV_UNDI_ISR_IN_PROCESS : + DBG ( " PROCESS" ); + /* Fall through */ + case PXENV_UNDI_ISR_IN_GET_NEXT : + DBG ( " GET_NEXT" ); + + /* Some dumb NBPs (e.g. emBoot's winBoot/i) never call + * PXENV_UNDI_ISR with FuncFlag=PXENV_UNDI_ISR_START; + * they just sit in a tight polling loop merrily + * violating the PXE spec with repeated calls to + * PXENV_UNDI_ISR_IN_PROCESS. Force extra polls to + * cope with these out-of-spec clients. + */ + netdev_poll ( pxe_netdev ); + + /* If we have not yet marked a TX as complete, and the + * netdev TX queue is empty, report the TX completion. + */ + if ( undi_tx_count && list_empty ( &pxe_netdev->tx_queue ) ) { + DBG ( " TXC" ); + undi_tx_count--; + undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_TRANSMIT; + break; + } + + /* Remove first packet from netdev RX queue */ + iobuf = netdev_rx_dequeue ( pxe_netdev ); + if ( ! iobuf ) { + DBG ( " DONE" ); + /* No more packets remaining */ + undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE; + /* Re-enable interrupts */ + netdev_irq ( pxe_netdev, 1 ); + break; + } + + /* Copy packet to base memory buffer */ + len = iob_len ( iobuf ); + DBG ( " RX %zd", len ); + if ( len > sizeof ( basemem_packet ) ) { + /* Should never happen */ + len = sizeof ( basemem_packet ); + } + memcpy ( basemem_packet, iobuf->data, len ); + + /* Strip link-layer header */ + ll_protocol = pxe_netdev->ll_protocol; + if ( ( rc = ll_protocol->pull ( iobuf, &ll_dest, &ll_source, + &net_proto ) ) != 0 ) { + /* Assume unknown net_proto and no ll_source */ + net_proto = 0; + ll_source = NULL; + } + ll_hlen = ( len - iob_len ( iobuf ) ); + + /* Determine network-layer protocol */ + switch ( net_proto ) { + case htons ( ETH_P_IP ): + net_protocol = &ipv4_protocol; + prottype = P_IP; + break; + case htons ( ETH_P_ARP ): + net_protocol = &arp_protocol; + prottype = P_ARP; + break; + case htons ( ETH_P_RARP ): + net_protocol = &rarp_protocol; + prottype = P_RARP; + break; + default: + net_protocol = NULL; + prottype = P_UNKNOWN; + break; + } + DBG ( " %s", ( net_protocol ? net_protocol->name : "RAW" ) ); + + /* Fill in UNDI_ISR structure */ + undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE; + undi_isr->BufferLength = len; + undi_isr->FrameLength = len; + undi_isr->FrameHeaderLength = ll_hlen; + undi_isr->Frame.segment = rm_ds; + undi_isr->Frame.offset = __from_data16 ( basemem_packet ); + undi_isr->ProtType = prottype; + undi_isr->PktType = XMT_DESTADDR; + + /* Free packet */ + free_iob ( iobuf ); + break; + default : + DBG ( " INVALID(%04x)", undi_isr->FuncFlag ); + + /* Should never happen */ + undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE; + undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER; + return PXENV_EXIT_FAILURE; + } + + undi_isr->Status = PXENV_STATUS_SUCCESS; + return PXENV_EXIT_SUCCESS; +} diff --git a/gpxe/src/arch/i386/interface/syslinux/com32_call.c b/gpxe/src/arch/i386/interface/syslinux/com32_call.c new file mode 100644 index 0000000..4a782dc --- /dev/null +++ b/gpxe/src/arch/i386/interface/syslinux/com32_call.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2008 Daniel Verkamp . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * @file SYSLINUX COM32 helpers + * + */ + +#include +#include +#include +#include +#include + +static com32sys_t __bss16 ( com32_regs ); +#define com32_regs __use_data16 ( com32_regs ) + +static uint8_t __bss16 ( com32_int_vector ); +#define com32_int_vector __use_data16 ( com32_int_vector ) + +static uint32_t __bss16 ( com32_farcall_proc ); +#define com32_farcall_proc __use_data16 ( com32_farcall_proc ) + +uint16_t __bss16 ( com32_saved_sp ); + +/** + * Interrupt call helper + */ +void __asmcall com32_intcall ( uint8_t interrupt, physaddr_t inregs_phys, physaddr_t outregs_phys ) { + + memcpy_user ( virt_to_user( &com32_regs ), 0, + phys_to_user ( inregs_phys ), 0, + sizeof(com32sys_t) ); + + com32_int_vector = interrupt; + + __asm__ __volatile__ ( + REAL_CODE ( /* Save all registers */ + "pushal\n\t" + "pushw %%ds\n\t" + "pushw %%es\n\t" + "pushw %%fs\n\t" + "pushw %%gs\n\t" + /* Mask off unsafe flags */ + "movl (com32_regs + 40), %%eax\n\t" + "andl $0x200cd7, %%eax\n\t" + "movl %%eax, (com32_regs + 40)\n\t" + /* Load com32_regs into the actual registers */ + "movw %%sp, %%ss:(com32_saved_sp)\n\t" + "movw $com32_regs, %%sp\n\t" + "popw %%gs\n\t" + "popw %%fs\n\t" + "popw %%es\n\t" + "popw %%ds\n\t" + "popal\n\t" + "popfl\n\t" + "movw %%ss:(com32_saved_sp), %%sp\n\t" + /* patch INT instruction */ + "pushw %%ax\n\t" + "movb %%ss:(com32_int_vector), %%al\n\t" + "movb %%al, %%cs:(com32_intcall_instr + 1)\n\t" + /* perform a jump to avoid problems with cache + * consistency in self-modifying code on some CPUs (486) + */ + "jmp 1f\n" + "1:\n\t" + "popw %%ax\n\t" + "com32_intcall_instr:\n\t" + /* INT instruction to be patched */ + "int $0xFF\n\t" + /* Copy regs back to com32_regs */ + "movw %%sp, %%ss:(com32_saved_sp)\n\t" + "movw $(com32_regs + 44), %%sp\n\t" + "pushfl\n\t" + "pushal\n\t" + "pushw %%ds\n\t" + "pushw %%es\n\t" + "pushw %%fs\n\t" + "pushw %%gs\n\t" + "movw %%ss:(com32_saved_sp), %%sp\n\t" + /* Restore registers */ + "popw %%gs\n\t" + "popw %%fs\n\t" + "popw %%es\n\t" + "popw %%ds\n\t" + "popal\n\t") + : : ); + + if ( outregs_phys ) { + memcpy_user ( phys_to_user ( outregs_phys ), 0, + virt_to_user( &com32_regs ), 0, + sizeof(com32sys_t) ); + } +} + +/** + * Farcall helper + */ +void __asmcall com32_farcall ( uint32_t proc, physaddr_t inregs_phys, physaddr_t outregs_phys ) { + + memcpy_user ( virt_to_user( &com32_regs ), 0, + phys_to_user ( inregs_phys ), 0, + sizeof(com32sys_t) ); + + com32_farcall_proc = proc; + + __asm__ __volatile__ ( + REAL_CODE ( /* Save all registers */ + "pushal\n\t" + "pushw %%ds\n\t" + "pushw %%es\n\t" + "pushw %%fs\n\t" + "pushw %%gs\n\t" + /* Mask off unsafe flags */ + "movl (com32_regs + 40), %%eax\n\t" + "andl $0x200cd7, %%eax\n\t" + "movl %%eax, (com32_regs + 40)\n\t" + /* Load com32_regs into the actual registers */ + "movw %%sp, %%ss:(com32_saved_sp)\n\t" + "movw $com32_regs, %%sp\n\t" + "popw %%gs\n\t" + "popw %%fs\n\t" + "popw %%es\n\t" + "popw %%ds\n\t" + "popal\n\t" + "popfl\n\t" + "movw %%ss:(com32_saved_sp), %%sp\n\t" + /* Call procedure */ + "lcall *%%ss:(com32_farcall_proc)\n\t" + /* Copy regs back to com32_regs */ + "movw %%sp, %%ss:(com32_saved_sp)\n\t" + "movw $(com32_regs + 44), %%sp\n\t" + "pushfl\n\t" + "pushal\n\t" + "pushw %%ds\n\t" + "pushw %%es\n\t" + "pushw %%fs\n\t" + "pushw %%gs\n\t" + "movw %%ss:(com32_saved_sp), %%sp\n\t" + /* Restore registers */ + "popw %%gs\n\t" + "popw %%fs\n\t" + "popw %%es\n\t" + "popw %%ds\n\t" + "popal\n\t") + : : ); + + if ( outregs_phys ) { + memcpy_user ( phys_to_user ( outregs_phys ), 0, + virt_to_user( &com32_regs ), 0, + sizeof(com32sys_t) ); + } +} + +/** + * CDECL farcall helper + */ +int __asmcall com32_cfarcall ( uint32_t proc, physaddr_t stack, size_t stacksz ) { + int32_t eax; + + copy_user_to_rm_stack ( phys_to_user ( stack ), stacksz ); + com32_farcall_proc = proc; + + __asm__ __volatile__ ( + REAL_CODE ( "lcall *%%ss:(com32_farcall_proc)\n\t" ) + : "=a" (eax) + : + : "ecx", "edx" ); + + remove_user_from_rm_stack ( 0, stacksz ); + + return eax; +} diff --git a/gpxe/src/arch/i386/interface/syslinux/com32_wrapper.S b/gpxe/src/arch/i386/interface/syslinux/com32_wrapper.S new file mode 100644 index 0000000..08d7398 --- /dev/null +++ b/gpxe/src/arch/i386/interface/syslinux/com32_wrapper.S @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2008 Daniel Verkamp . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + .text + .arch i386 + .code32 + + .globl com32_farcall_wrapper +com32_farcall_wrapper: + + movl $com32_farcall, %eax + jmp com32_wrapper + + + .globl com32_cfarcall_wrapper +com32_cfarcall_wrapper: + + movl $com32_cfarcall, %eax + jmp com32_wrapper + + + .globl com32_intcall_wrapper +com32_intcall_wrapper: + + movl $com32_intcall, %eax + /*jmp com32_wrapper*/ /* fall through */ + +com32_wrapper: + + /* Switch to internal virtual address space */ + call _phys_to_virt + + mov %eax, (com32_helper_function) + + /* Save external COM32 stack pointer */ + movl %esp, (com32_external_esp) + + /* Copy arguments to caller-save registers */ + movl 12(%esp), %eax + movl 8(%esp), %ecx + movl 4(%esp), %edx + + /* Switch to internal stack */ + movl (com32_internal_esp), %esp + + /* Copy arguments to internal stack */ + pushl %eax + pushl %ecx + pushl %edx + + call *(com32_helper_function) + + /* Clean up stack */ + addl $12, %esp + + /* Save internal stack pointer and restore external stack pointer */ + movl %esp, (com32_internal_esp) + movl (com32_external_esp), %esp + + /* Switch to external flat physical address space */ + call _virt_to_phys + + ret + + + .data + +/* Internal gPXE virtual address space %esp */ +.globl com32_internal_esp +.lcomm com32_internal_esp, 4 + +/* External flat physical address space %esp */ +.globl com32_external_esp +.lcomm com32_external_esp, 4 + +/* Function pointer of helper to call */ +.lcomm com32_helper_function, 4 diff --git a/gpxe/src/arch/i386/interface/syslinux/comboot_call.c b/gpxe/src/arch/i386/interface/syslinux/comboot_call.c new file mode 100644 index 0000000..bf6c4c6 --- /dev/null +++ b/gpxe/src/arch/i386/interface/syslinux/comboot_call.c @@ -0,0 +1,693 @@ +/* + * Copyright (C) 2008 Daniel Verkamp . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * @file SYSLINUX COMBOOT API + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** The "SYSLINUX" version string */ +static char __data16_array ( syslinux_version, [] ) = "gPXE " VERSION; +#define syslinux_version __use_data16 ( syslinux_version ) + +/** The "SYSLINUX" copyright string */ +static char __data16_array ( syslinux_copyright, [] ) = "http://etherboot.org"; +#define syslinux_copyright __use_data16 ( syslinux_copyright ) + +static char __data16_array ( syslinux_configuration_file, [] ) = ""; +#define syslinux_configuration_file __use_data16 ( syslinux_configuration_file ) + +/** Feature flags */ +static uint8_t __data16 ( comboot_feature_flags ) = COMBOOT_FEATURE_IDLE_LOOP; +#define comboot_feature_flags __use_data16 ( comboot_feature_flags ) + +typedef union { + syslinux_pm_regs pm; syslinux_rm_regs rm; +} syslinux_regs; + +/** Initial register values for INT 22h AX=1Ah and 1Bh */ +static syslinux_regs __text16 ( comboot_initial_regs ); +#define comboot_initial_regs __use_text16 ( comboot_initial_regs ) + +static struct segoff __text16 ( int20_vector ); +#define int20_vector __use_text16 ( int20_vector ) + +static struct segoff __text16 ( int21_vector ); +#define int21_vector __use_text16 ( int21_vector ) + +static struct segoff __text16 ( int22_vector ); +#define int22_vector __use_text16 ( int22_vector ) + +extern void int20_wrapper ( void ); +extern void int21_wrapper ( void ); +extern void int22_wrapper ( void ); + +/* setjmp/longjmp context buffer used to return after loading an image */ +rmjmp_buf comboot_return; + +/* Replacement image when exiting with COMBOOT_EXIT_RUN_KERNEL */ +struct image *comboot_replacement_image; + +/* Mode flags set by INT 22h AX=0017h */ +static uint16_t comboot_graphics_mode = 0; + + +/** + * Print a string with a particular terminator + */ +static void print_user_string ( unsigned int segment, unsigned int offset, char terminator ) { + int i = 0; + char c; + userptr_t str = real_to_user ( segment, offset ); + for ( ; ; ) { + copy_from_user ( &c, str, i, 1 ); + if ( c == terminator ) break; + putchar ( c ); + i++; + } +} + + +/** + * Perform a series of memory copies from a list in low memory + */ +static void shuffle ( unsigned int list_segment, unsigned int list_offset, unsigned int count ) +{ + comboot_shuffle_descriptor shuf[COMBOOT_MAX_SHUFFLE_DESCRIPTORS]; + unsigned int i; + + /* Copy shuffle descriptor list so it doesn't get overwritten */ + copy_from_user ( shuf, real_to_user ( list_segment, list_offset ), 0, + count * sizeof( comboot_shuffle_descriptor ) ); + + /* Do the copies */ + for ( i = 0; i < count; i++ ) { + userptr_t src_u = phys_to_user ( shuf[ i ].src ); + userptr_t dest_u = phys_to_user ( shuf[ i ].dest ); + + if ( shuf[ i ].src == 0xFFFFFFFF ) { + /* Fill with 0 instead of copying */ + memset_user ( dest_u, 0, 0, shuf[ i ].len ); + } else if ( shuf[ i ].dest == 0xFFFFFFFF ) { + /* Copy new list of descriptors */ + count = shuf[ i ].len / sizeof( comboot_shuffle_descriptor ); + assert ( count <= COMBOOT_MAX_SHUFFLE_DESCRIPTORS ); + copy_from_user ( shuf, src_u, 0, shuf[ i ].len ); + i = -1; + } else { + /* Regular copy */ + memmove_user ( dest_u, 0, src_u, 0, shuf[ i ].len ); + } + } +} + + +/** + * Set default text mode + */ +void comboot_force_text_mode ( void ) { + if ( comboot_graphics_mode & COMBOOT_VIDEO_VESA ) { + /* Set VGA mode 3 via VESA VBE mode set */ + __asm__ __volatile__ ( + REAL_CODE ( + "mov $0x4F02, %%ax\n\t" + "mov $0x03, %%bx\n\t" + "int $0x10\n\t" + ) + : : ); + } else if ( comboot_graphics_mode & COMBOOT_VIDEO_GRAPHICS ) { + /* Set VGA mode 3 via standard VGA mode set */ + __asm__ __volatile__ ( + REAL_CODE ( + "mov $0x03, %%ax\n\t" + "int $0x10\n\t" + ) + : : ); + } + + comboot_graphics_mode = 0; +} + + +/** + * Fetch kernel and optional initrd + */ +static int comboot_fetch_kernel ( char *kernel_file, char *cmdline ) { + struct image *kernel = NULL; + struct image *initrd = NULL; + char *initrd_file; + int rc; + + /* Find initrd= parameter, if any */ + if ( ( initrd_file = strstr ( cmdline, "initrd=" ) ) != NULL ) { + char *initrd_end; + + /* skip "initrd=" */ + initrd_file += 7; + + /* Find terminating space, if any, and replace with NUL */ + initrd_end = strchr ( initrd_file, ' ' ); + if ( initrd_end ) + *initrd_end = '\0'; + + DBG ( "COMBOOT: fetching initrd '%s'\n", initrd_file ); + + /* Allocate and fetch initrd */ + initrd = alloc_image(); + if ( ! initrd ) { + DBG ( "COMBOOT: could not allocate initrd\n" ); + rc = -ENOMEM; + goto out; + } + if ( ( rc = imgfetch ( initrd, initrd_file, + register_image ) ) != 0 ) { + DBG ( "COMBOOT: could not fetch initrd: %s\n", + strerror ( rc ) ); + goto out; + } + + /* Restore space after initrd name, if applicable */ + if ( initrd_end ) + *initrd_end = ' '; + } + + DBG ( "COMBOOT: fetching kernel '%s'\n", kernel_file ); + + /* Allocate and fetch kernel */ + kernel = alloc_image(); + if ( ! kernel ) { + DBG ( "COMBOOT: could not allocate kernel\n" ); + rc = -ENOMEM; + goto out; + } + if ( ( rc = imgfetch ( kernel, kernel_file, + register_image ) ) != 0 ) { + DBG ( "COMBOOT: could not fetch kernel: %s\n", + strerror ( rc ) ); + goto out; + } + if ( ( rc = image_set_cmdline ( kernel, cmdline ) ) != 0 ) { + DBG ( "COMBOOT: could not set kernel command line: %s\n", + strerror ( rc ) ); + goto out; + } + + /* Store kernel as replacement image */ + assert ( comboot_replacement_image == NULL ); + comboot_replacement_image = image_get ( kernel ); + + out: + /* Drop image references unconditionally; either we want to + * discard them, or they have been registered and we should + * drop out local reference. + */ + image_put ( kernel ); + image_put ( initrd ); + return rc; +} + + +/** + * Terminate program interrupt handler + */ +static __asmcall void int20 ( struct i386_all_regs *ix86 __unused ) { + rmlongjmp ( comboot_return, COMBOOT_EXIT ); +} + + +/** + * DOS-compatible API + */ +static __asmcall void int21 ( struct i386_all_regs *ix86 ) { + ix86->flags |= CF; + + switch ( ix86->regs.ah ) { + case 0x00: + case 0x4C: /* Terminate program */ + rmlongjmp ( comboot_return, COMBOOT_EXIT ); + break; + + case 0x01: /* Get Key with Echo */ + case 0x08: /* Get Key without Echo */ + /* TODO: handle extended characters? */ + ix86->regs.al = getchar( ); + + /* Enter */ + if ( ix86->regs.al == 0x0A ) + ix86->regs.al = 0x0D; + + if ( ix86->regs.ah == 0x01 ) + putchar ( ix86->regs.al ); + + ix86->flags &= ~CF; + break; + + case 0x02: /* Write Character */ + putchar ( ix86->regs.dl ); + ix86->flags &= ~CF; + break; + + case 0x04: /* Write Character to Serial Port */ + serial_putc ( ix86->regs.dl ); + ix86->flags &= ~CF; + break; + + case 0x09: /* Write DOS String to Console */ + print_user_string ( ix86->segs.ds, ix86->regs.dx, '$' ); + ix86->flags &= ~CF; + break; + + case 0x0B: /* Check Keyboard */ + if ( iskey() ) + ix86->regs.al = 0xFF; + else + ix86->regs.al = 0x00; + + ix86->flags &= ~CF; + break; + + case 0x30: /* Check DOS Version */ + /* Bottom halves all 0; top halves spell "SYSLINUX" */ + ix86->regs.eax = 0x59530000; + ix86->regs.ebx = 0x4C530000; + ix86->regs.ecx = 0x4E490000; + ix86->regs.edx = 0x58550000; + ix86->flags &= ~CF; + break; + + default: + DBG ( "COMBOOT unknown int21 function %02x\n", ix86->regs.ah ); + break; + } +} + + +/** + * SYSLINUX API + */ +static __asmcall void int22 ( struct i386_all_regs *ix86 ) { + ix86->flags |= CF; + + switch ( ix86->regs.ax ) { + case 0x0001: /* Get Version */ + + /* Number of INT 22h API functions available */ + ix86->regs.ax = 0x001B; + + /* SYSLINUX version number */ + ix86->regs.ch = 0; /* major */ + ix86->regs.cl = 0; /* minor */ + + /* SYSLINUX derivative ID */ + ix86->regs.dl = BZI_LOADER_TYPE_GPXE; + + /* SYSLINUX version and copyright strings */ + ix86->segs.es = rm_ds; + ix86->regs.si = ( ( unsigned ) __from_data16 ( syslinux_version ) ); + ix86->regs.di = ( ( unsigned ) __from_data16 ( syslinux_copyright ) ); + + ix86->flags &= ~CF; + break; + + case 0x0002: /* Write String */ + print_user_string ( ix86->segs.es, ix86->regs.bx, '\0' ); + ix86->flags &= ~CF; + break; + + case 0x0003: /* Run command */ + { + userptr_t cmd_u = real_to_user ( ix86->segs.es, ix86->regs.bx ); + int len = strlen_user ( cmd_u, 0 ); + char cmd[len + 1]; + copy_from_user ( cmd, cmd_u, 0, len + 1 ); + DBG ( "COMBOOT: executing command '%s'\n", cmd ); + system ( cmd ); + DBG ( "COMBOOT: exiting after executing command...\n" ); + rmlongjmp ( comboot_return, COMBOOT_EXIT_COMMAND ); + } + break; + + case 0x0004: /* Run default command */ + /* FIXME: just exit for now */ + rmlongjmp ( comboot_return, COMBOOT_EXIT_COMMAND ); + break; + + case 0x0005: /* Force text mode */ + comboot_force_text_mode ( ); + ix86->flags &= ~CF; + break; + + case 0x0006: /* Open file */ + { + int fd; + userptr_t file_u = real_to_user ( ix86->segs.es, ix86->regs.si ); + int len = strlen_user ( file_u, 0 ); + char file[len + 1]; + + copy_from_user ( file, file_u, 0, len + 1 ); + + if ( file[0] == '\0' ) { + DBG ( "COMBOOT: attempted open with empty file name\n" ); + break; + } + + DBG ( "COMBOOT: opening file '%s'\n", file ); + + fd = open ( file ); + + if ( fd < 0 ) { + DBG ( "COMBOOT: error opening file %s\n", file ); + break; + } + + /* This relies on the fact that a gPXE POSIX fd will + * always fit in 16 bits. + */ +#if (POSIX_FD_MAX > 65535) +#error POSIX_FD_MAX too large +#endif + ix86->regs.si = (uint16_t) fd; + + ix86->regs.cx = COMBOOT_FILE_BLOCKSZ; + ix86->regs.eax = fsize ( fd ); + ix86->flags &= ~CF; + } + break; + + case 0x0007: /* Read file */ + { + int fd = ix86->regs.si; + int len = ix86->regs.cx * COMBOOT_FILE_BLOCKSZ; + int rc; + fd_set fds; + userptr_t buf = real_to_user ( ix86->segs.es, ix86->regs.bx ); + + /* Wait for data ready to read */ + FD_ZERO ( &fds ); + FD_SET ( fd, &fds ); + + select ( &fds, 1 ); + + rc = read_user ( fd, buf, 0, len ); + if ( rc < 0 ) { + DBG ( "COMBOOT: read failed\n" ); + ix86->regs.si = 0; + break; + } + + ix86->regs.ecx = rc; + ix86->flags &= ~CF; + } + break; + + case 0x0008: /* Close file */ + { + int fd = ix86->regs.si; + close ( fd ); + ix86->flags &= ~CF; + } + break; + + case 0x0009: /* Call PXE Stack */ + pxe_api_call ( ix86 ); + ix86->flags &= ~CF; + break; + + case 0x000A: /* Get Derivative-Specific Information */ + + /* gPXE has its own derivative ID, so there is no defined + * output here; just return AL for now */ + ix86->regs.al = BZI_LOADER_TYPE_GPXE; + ix86->flags &= ~CF; + break; + + case 0x000B: /* Get Serial Console Configuration */ + /* FIXME: stub */ + ix86->regs.dx = 0; + ix86->flags &= ~CF; + break; + + case 0x000E: /* Get configuration file name */ + /* FIXME: stub */ + ix86->segs.es = rm_ds; + ix86->regs.bx = ( ( unsigned ) __from_data16 ( syslinux_configuration_file ) ); + ix86->flags &= ~CF; + break; + + case 0x000F: /* Get IPAPPEND strings */ + /* FIXME: stub */ + ix86->regs.cx = 0; + ix86->segs.es = 0; + ix86->regs.bx = 0; + ix86->flags &= ~CF; + break; + + case 0x0010: /* Resolve hostname */ + { + userptr_t hostname_u = real_to_user ( ix86->segs.es, ix86->regs.bx ); + int len = strlen_user ( hostname_u, 0 ); + char hostname[len]; + struct in_addr addr; + + copy_from_user ( hostname, hostname_u, 0, len + 1 ); + + /* TODO: + * "If the hostname does not contain a dot (.), the + * local domain name is automatically appended." + */ + + comboot_resolv ( hostname, &addr ); + + ix86->regs.eax = addr.s_addr; + ix86->flags &= ~CF; + } + break; + + case 0x0011: /* Maximum number of shuffle descriptors */ + ix86->regs.cx = COMBOOT_MAX_SHUFFLE_DESCRIPTORS; + ix86->flags &= ~CF; + break; + + case 0x0012: /* Cleanup, shuffle and boot */ + if ( ix86->regs.cx > COMBOOT_MAX_SHUFFLE_DESCRIPTORS ) + break; + + /* Perform final cleanup */ + shutdown ( SHUTDOWN_BOOT ); + + /* Perform sequence of copies */ + shuffle ( ix86->segs.es, ix86->regs.di, ix86->regs.cx ); + + /* Jump to real-mode entry point */ + __asm__ __volatile__ ( + REAL_CODE ( + "pushw %0\n\t" + "popw %%ds\n\t" + "pushl %1\n\t" + "lret\n\t" + ) + : + : "r" ( ix86->segs.ds ), + "r" ( ix86->regs.ebp ), + "d" ( ix86->regs.ebx ), + "S" ( ix86->regs.esi ) ); + + assert ( 0 ); /* Execution should never reach this point */ + + break; + + case 0x0013: /* Idle loop call */ + step ( ); + ix86->flags &= ~CF; + break; + + case 0x0015: /* Get feature flags */ + ix86->segs.es = rm_ds; + ix86->regs.bx = ( ( unsigned ) __from_data16 ( &comboot_feature_flags ) ); + ix86->regs.cx = 1; /* Number of feature flag bytes */ + ix86->flags &= ~CF; + break; + + case 0x0016: /* Run kernel image */ + { + userptr_t file_u = real_to_user ( ix86->segs.ds, ix86->regs.si ); + userptr_t cmd_u = real_to_user ( ix86->segs.es, ix86->regs.bx ); + int file_len = strlen_user ( file_u, 0 ); + int cmd_len = strlen_user ( cmd_u, 0 ); + char file[file_len + 1]; + char cmd[cmd_len + 1]; + + copy_from_user ( file, file_u, 0, file_len + 1 ); + copy_from_user ( cmd, cmd_u, 0, cmd_len + 1 ); + + DBG ( "COMBOOT: run kernel %s %s\n", file, cmd ); + comboot_fetch_kernel ( file, cmd ); + /* Technically, we should return if we + * couldn't load the kernel, but it's not safe + * to do that since we have just overwritten + * part of the COMBOOT program's memory space. + */ + DBG ( "COMBOOT: exiting to run kernel...\n" ); + rmlongjmp ( comboot_return, COMBOOT_EXIT_RUN_KERNEL ); + } + break; + + case 0x0017: /* Report video mode change */ + comboot_graphics_mode = ix86->regs.bx; + ix86->flags &= ~CF; + break; + + case 0x0018: /* Query custom font */ + /* FIXME: stub */ + ix86->regs.al = 0; + ix86->segs.es = 0; + ix86->regs.bx = 0; + ix86->flags &= ~CF; + break; + + case 0x001B: /* Cleanup, shuffle and boot to real mode */ + if ( ix86->regs.cx > COMBOOT_MAX_SHUFFLE_DESCRIPTORS ) + break; + + /* Perform final cleanup */ + shutdown ( SHUTDOWN_BOOT ); + + /* Perform sequence of copies */ + shuffle ( ix86->segs.es, ix86->regs.di, ix86->regs.cx ); + + /* Copy initial register values to .text16 */ + memcpy_user ( real_to_user ( rm_cs, (unsigned) __from_text16 ( &comboot_initial_regs ) ), 0, + real_to_user ( ix86->segs.ds, ix86->regs.si ), 0, + sizeof(syslinux_rm_regs) ); + + /* Load initial register values */ + __asm__ __volatile__ ( + REAL_CODE ( + /* Point SS:SP at the register value structure */ + "pushw %%cs\n\t" + "popw %%ss\n\t" + "movw $comboot_initial_regs, %%sp\n\t" + + /* Segment registers */ + "popw %%es\n\t" + "popw %%ax\n\t" /* Skip CS */ + "popw %%ds\n\t" + "popw %%ax\n\t" /* Skip SS for now */ + "popw %%fs\n\t" + "popw %%gs\n\t" + + /* GP registers */ + "popl %%eax\n\t" + "popl %%ecx\n\t" + "popl %%edx\n\t" + "popl %%ebx\n\t" + "popl %%ebp\n\t" /* Skip ESP for now */ + "popl %%ebp\n\t" + "popl %%esi\n\t" + "popl %%edi\n\t" + + /* Load correct SS:ESP */ + "movw $(comboot_initial_regs + 6), %%sp\n\t" + "popw %%ss\n\t" + "movl %%cs:(comboot_initial_regs + 28), %%esp\n\t" + + "ljmp *%%cs:(comboot_initial_regs + 44)\n\t" + ) + : : ); + + break; + + default: + DBG ( "COMBOOT unknown int22 function %04x\n", ix86->regs.ax ); + break; + } +} + +/** + * Hook BIOS interrupts related to COMBOOT API (INT 20h, 21h, 22h) + */ +void hook_comboot_interrupts ( ) { + + __asm__ __volatile__ ( + TEXT16_CODE ( "\nint20_wrapper:\n\t" + "pushl %0\n\t" + "pushw %%cs\n\t" + "call prot_call\n\t" + "addw $4, %%sp\n\t" + "iret\n\t" ) + : : "i" ( int20 ) ); + + hook_bios_interrupt ( 0x20, ( unsigned int ) int20_wrapper, + &int20_vector ); + + __asm__ __volatile__ ( + TEXT16_CODE ( "\nint21_wrapper:\n\t" + "pushl %0\n\t" + "pushw %%cs\n\t" + "call prot_call\n\t" + "addw $4, %%sp\n\t" + "iret\n\t" ) + : : "i" ( int21 ) ); + + hook_bios_interrupt ( 0x21, ( unsigned int ) int21_wrapper, + &int21_vector ); + + __asm__ __volatile__ ( + TEXT16_CODE ( "\nint22_wrapper:\n\t" + "pushl %0\n\t" + "pushw %%cs\n\t" + "call prot_call\n\t" + "addw $4, %%sp\n\t" + "iret\n\t" ) + : : "i" ( int22) ); + + hook_bios_interrupt ( 0x22, ( unsigned int ) int22_wrapper, + &int22_vector ); +} + +/** + * Unhook BIOS interrupts related to COMBOOT API (INT 20h, 21h, 22h) + */ +void unhook_comboot_interrupts ( ) { + + unhook_bios_interrupt ( 0x20, ( unsigned int ) int20_wrapper, + &int20_vector ); + + unhook_bios_interrupt ( 0x21, ( unsigned int ) int21_wrapper, + &int21_vector ); + + unhook_bios_interrupt ( 0x22, ( unsigned int ) int22_wrapper, + &int22_vector ); +} diff --git a/gpxe/src/arch/i386/interface/syslinux/comboot_resolv.c b/gpxe/src/arch/i386/interface/syslinux/comboot_resolv.c new file mode 100644 index 0000000..41c3af7 --- /dev/null +++ b/gpxe/src/arch/i386/interface/syslinux/comboot_resolv.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include +#include + +static int comboot_resolv_rc; +static struct in_addr comboot_resolv_addr; + +static void comboot_resolv_done ( struct resolv_interface *resolv, + struct sockaddr *sa, int rc ) { + struct sockaddr_in *sin; + + resolv_unplug ( resolv ); + + if ( rc != 0 ) { + comboot_resolv_rc = rc; + return; + } + + if ( sa->sa_family != AF_INET ) { + comboot_resolv_rc = -EAFNOSUPPORT; + return; + } + + sin = ( ( struct sockaddr_in * ) sa ); + comboot_resolv_addr = sin->sin_addr; + + comboot_resolv_rc = 0; +} + +static struct resolv_interface_operations comboot_resolv_ops = { + .done = comboot_resolv_done, +}; + +static struct resolv_interface comboot_resolver = { + .intf = { + .dest = &null_resolv.intf, + .refcnt = NULL, + }, + .op = &comboot_resolv_ops, +}; + +int comboot_resolv ( const char *name, struct in_addr *address ) { + int rc; + + comboot_resolv_rc = -EINPROGRESS; + + if ( ( rc = resolv ( &comboot_resolver, name, NULL ) ) != 0 ) + return rc; + + while ( comboot_resolv_rc == -EINPROGRESS ) + step(); + + *address = comboot_resolv_addr; + return comboot_resolv_rc; +} diff --git a/gpxe/src/arch/i386/kir-Makefile b/gpxe/src/arch/i386/kir-Makefile new file mode 100644 index 0000000..bbfc1a3 --- /dev/null +++ b/gpxe/src/arch/i386/kir-Makefile @@ -0,0 +1,26 @@ +# Makefile to build a KEEP_IT_REAL flavour +# +# KEEP_IT_REAL, by its nature, requires a different build of every +# single object file, since the inclusion of ".code16gcc" will +# generate different machine code from the assembly. Unlike the other +# config options, there is no way that this global dependency can ever +# be reduced, so it makes sense to be able to build both the normal +# and the KIR versions without having to force a full rebuild each +# time. + +# Add this Makefile to MAKEDEPS +# +MAKEDEPS += arch/i386/kir-Makefile + +# Place binaries in bin-kir +# +BIN = bin-kir + +# Compile with -DKEEP_IT_REAL, forcibly include kir.h at the start of +# each file to drag in ".code16gcc" +# +CFLAGS += -DKEEP_IT_REAL -include kir.h + +include Makefile + +LDSCRIPT = arch/i386/scripts/i386-kir.lds diff --git a/gpxe/src/arch/i386/prefix/bImageprefix.S b/gpxe/src/arch/i386/prefix/bImageprefix.S new file mode 100644 index 0000000..30020f7 --- /dev/null +++ b/gpxe/src/arch/i386/prefix/bImageprefix.S @@ -0,0 +1,611 @@ +/* + Copyright (C) 2000, Entity Cyber, Inc. + + Authors: Gary Byers (gb@thinguin.org) + Marty Connor (mdc@thinguin.org) + Eric Biederman (ebiederman@lnxi.com) + + This code also derives a lot from arch/i386/boot/setup.S in + the linux kernel. + + This software may be used and distributed according to the terms + of the GNU Public License (GPL), incorporated herein by reference. + + Description: + + This is just a little bit of code and data that can get prepended + to an Etherboot ROM image in order to allow LILO to load the + result as if it were a Linux kernel image. + + A real Linux kernel image consists of a one-sector boot loader + (to load the image from a floppy disk), followed a few sectors + of setup code, followed by the kernel code itself. There's + a table in the first sector (starting at offset 497) that indicates + how many sectors of setup code follow the first sector and which + contains some other parameters that aren't interesting in this + case. + + When LILO loads the sectors that comprise a kernel image, it doesn't + execute the code in the first sector (since that code would try to + load the image from a floppy disk.) The code in the first sector + below doesn't expect to get executed (and prints an error message + if it ever -is- executed.) LILO's only interested in knowing the + number of setup sectors advertised in the table (at offset 497 in + the first sector.) + + Etherboot doesn't require much in the way of setup code. + Historically, the Linux kernel required at least 4 sectors of + setup code. Current versions of LILO look at the byte at + offset 497 in the first sector to indicate how many sectors + of setup code are contained in the image. + + The setup code that is present here does a lot of things + exactly the way the linux kernel does them instead of in + ways more typical of etherboot. Generally this is so + the code can be strongly compatible with the linux kernel. + In addition the general etherboot technique of enabling the a20 + after we switch into protected mode does not work if etherboot + is being loaded at 1MB. +*/ + + .equ CR0_PE,1 + +#ifdef GAS291 +#define DATA32 data32; +#define ADDR32 addr32; +#define LJMPI(x) ljmp x +#else +#define DATA32 data32 +#define ADDR32 addr32 +/* newer GAS295 require #define LJMPI(x) ljmp *x */ +#define LJMPI(x) ljmp x +#endif + +/* Simple and small GDT entries for booting only */ +#define GDT_ENTRY_BOOT_CS 2 +#define GDT_ENTRY_BOOT_DS (GDT_ENTRY_BOOT_CS + 1) +#define __BOOT_CS (GDT_ENTRY_BOOT_CS * 8) +#define __BOOT_DS (GDT_ENTRY_BOOT_DS * 8) + + +#define SETUPSECS 4 /* Minimal nr of setup-sectors */ +#define PREFIXSIZE ((SETUPSECS+1)*512) +#define PREFIXPGH (PREFIXSIZE / 16 ) +#define BOOTSEG 0x07C0 /* original address of boot-sector */ +#define INITSEG 0x9000 /* we move boot here - out of the way */ +#define SETUPSEG 0x9020 /* setup starts here */ +#define SYSSEG 0x1000 /* system loaded at 0x10000 (65536). */ + +#define DELTA_INITSEG (SETUPSEG - INITSEG) /* 0x0020 */ + +/* Signature words to ensure LILO loaded us right */ +#define SIG1 0xAA55 +#define SIG2 0x5A5A + + .text + .code16 + .arch i386 + .org 0 + .section ".prefix", "ax", @progbits +_prefix: + +/* + This is a minimal boot sector. If anyone tries to execute it (e.g., if + a .lkrn file is dd'ed to a floppy), print an error message. +*/ + +bootsector: + jmp $BOOTSEG, $go - _prefix /* reload cs:ip to match relocation addr */ +go: + movw $0x2000, %di /* 0x2000 is arbitrary value >= length + of bootsect + room for stack */ + + movw $BOOTSEG, %ax + movw %ax,%ds + movw %ax,%es + + cli + movw %ax, %ss /* put stack at BOOTSEG:0x2000. */ + movw %di,%sp + sti + + movw $why_end-why, %cx + movw $why - _prefix, %si + + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + movb $0x0e, %ah /* write char, tty mode */ +prloop: + lodsb + int $0x10 + loop prloop +freeze: jmp freeze + +why: .ascii "This image cannot be loaded from a floppy disk.\r\n" +why_end: + + + .org 497 +setup_sects: + .byte SETUPSECS +root_flags: + .word 0 +syssize: + .word _verbatim_size_pgh - PREFIXPGH +swap_dev: + .word 0 +ram_size: + .word 0 +vid_mode: + .word 0 +root_dev: + .word 0 +boot_flag: + .word 0xAA55 + +/* + We're now at the beginning of the second sector of the image - + where the setup code goes. + + We don't need to do too much setup for Etherboot. + + This code gets loaded at SETUPSEG:0. It wants to start + executing the Etherboot image that's loaded at SYSSEG:0 and + whose entry point is SYSSEG:0. +*/ +setup_code: + jmp trampoline +# This is the setup header, and it must start at %cs:2 (old 0x9020:2) + + .ascii "HdrS" # header signature + .word 0x0203 # header version number (>= 0x0105) + # or else old loadlin-1.5 will fail) +realmode_swtch: .word 0, 0 # default_switch, SETUPSEG +start_sys_seg: .word SYSSEG # low load segment (obsolete) + .word kernel_version - setup_code + # pointing to kernel version string + # above section of header is compatible + # with loadlin-1.5 (header v1.5). Don't + # change it. + +type_of_loader: .byte 0 # = 0, old one (LILO, Loadlin, + # Bootlin, SYSLX, bootsect...) + # See Documentation/i386/boot.txt for + # assigned ids + +# flags, unused bits must be zero (RFU) bit within loadflags +loadflags: +LOADED_HIGH = 1 # If set, the kernel is loaded high +CAN_USE_HEAP = 0x80 # If set, the loader also has set + # heap_end_ptr to tell how much + # space behind setup.S can be used for + # heap purposes. + # Only the loader knows what is free + .byte LOADED_HIGH + +setup_move_size: .word 0x8000 # size to move, when setup is not + # loaded at 0x90000. We will move setup + # to 0x90000 then just before jumping + # into the kernel. However, only the + # loader knows how much data behind + # us also needs to be loaded. + +code32_start: # here loaders can put a different + # start address for 32-bit code. + .long 0x100000 # 0x100000 = default for big kernel + +ramdisk_image: .long 0 # address of loaded ramdisk image + # Here the loader puts the 32-bit + # address where it loaded the image. + # This only will be read by the kernel. + +ramdisk_size: .long 0 # its size in bytes + +bootsect_kludge: + .long 0 # obsolete + +heap_end_ptr: .word 0 # (Header version 0x0201 or later) + # space from here (exclusive) down to + # end of setup code can be used by setup + # for local heap purposes. + +pad1: .word 0 +cmd_line_ptr: .long 0 # (Header version 0x0202 or later) + # If nonzero, a 32-bit pointer + # to the kernel command line. + # The command line should be + # located between the start of + # setup and the end of low + # memory (0xa0000), or it may + # get overwritten before it + # gets read. If this field is + # used, there is no longer + # anything magical about the + # 0x90000 segment; the setup + # can be located anywhere in + # low memory 0x10000 or higher. + +ramdisk_max: .long 0 # (Header version 0x0203 or later) + # The highest safe address for + # the contents of an initrd + +trampoline: call start_of_setup +trampoline_end: + .space 1024 +# End of setup header ##################################################### + +start_of_setup: +# Set %ds = %cs, we know that SETUPSEG = %cs at this point + movw %cs, %ax # aka SETUPSEG + movw %ax, %ds +# Check signature at end of setup + cmpw $SIG1, (setup_sig1 - setup_code) + jne bad_sig + + cmpw $SIG2, (setup_sig2 - setup_code) + jne bad_sig + + jmp good_sig1 + +# Routine to print asciiz string at ds:si +prtstr: + lodsb + andb %al, %al + jz fin + + call prtchr + jmp prtstr + +fin: ret + +# Part of above routine, this one just prints ascii al +prtchr: pushw %ax + pushw %cx + movw $7,%bx + movw $0x01, %cx + movb $0x0e, %ah + int $0x10 + popw %cx + popw %ax + ret + +no_sig_mess: .string "No setup signature found ..." + +good_sig1: + jmp good_sig + +# We now have to find the rest of the setup code/data +bad_sig: + movw %cs, %ax # SETUPSEG + subw $DELTA_INITSEG, %ax # INITSEG + movw %ax, %ds + xorb %bh, %bh + movb (497), %bl # get setup sect from bootsect + subw $4, %bx # LILO loads 4 sectors of setup + shlw $8, %bx # convert to words (1sect=2^8 words) + movw %bx, %cx + shrw $3, %bx # convert to segment + addw $SYSSEG, %bx + movw %bx, %cs:(start_sys_seg - setup_code) +# Move rest of setup code/data to here + movw $2048, %di # four sectors loaded by LILO + subw %si, %si + pushw %cs + popw %es + movw $SYSSEG, %ax + movw %ax, %ds + rep + movsw + movw %cs, %ax # aka SETUPSEG + movw %ax, %ds + cmpw $SIG1, (setup_sig1 - setup_code) + jne no_sig + + cmpw $SIG2, (setup_sig2 - setup_code) + jne no_sig + + jmp good_sig + +no_sig: + lea (no_sig_mess - setup_code), %si + call prtstr + +no_sig_loop: + hlt + jmp no_sig_loop + +good_sig: + cmpw $0, %cs:(realmode_swtch - setup_code) + jz rmodeswtch_normal + + lcall *%cs:(realmode_swtch - setup_code) + jmp rmodeswtch_end + +rmodeswtch_normal: + pushw %cs + call default_switch + +rmodeswtch_end: +# we get the code32 start address and modify the below 'jmpi' +# (loader may have changed it) + movl %cs:(code32_start - setup_code), %eax + movl %eax, %cs:(code32 - setup_code) + +# then we load the segment descriptors + movw %cs, %ax # aka SETUPSEG + movw %ax, %ds + +# +# Enable A20. This is at the very best an annoying procedure. +# A20 code ported from SYSLINUX 1.52-1.63 by H. Peter Anvin. +# + +A20_TEST_LOOPS = 32 # Iterations per wait +A20_ENABLE_LOOPS = 255 # Total loops to try + +a20_try_loop: + + # First, see if we are on a system with no A20 gate. +a20_none: + call a20_test + jnz a20_done + + # Next, try the BIOS (INT 0x15, AX=0x2401) +a20_bios: + movw $0x2401, %ax + pushfl # Be paranoid about flags + int $0x15 + popfl + + call a20_test + jnz a20_done + + # Try enabling A20 through the keyboard controller +a20_kbc: + call empty_8042 + + call a20_test # Just in case the BIOS worked + jnz a20_done # but had a delayed reaction. + + movb $0xD1, %al # command write + outb %al, $0x64 + call empty_8042 + + movb $0xDF, %al # A20 on + outb %al, $0x60 + call empty_8042 + + # Wait until a20 really *is* enabled; it can take a fair amount of + # time on certain systems; Toshiba Tecras are known to have this + # problem. +a20_kbc_wait: + xorw %cx, %cx +a20_kbc_wait_loop: + call a20_test + jnz a20_done + loop a20_kbc_wait_loop + + # Final attempt: use "configuration port A" +a20_fast: + inb $0x92, %al # Configuration Port A + orb $0x02, %al # "fast A20" version + andb $0xFE, %al # don't accidentally reset + outb %al, $0x92 + + # Wait for configuration port A to take effect +a20_fast_wait: + xorw %cx, %cx +a20_fast_wait_loop: + call a20_test + jnz a20_done + loop a20_fast_wait_loop + + # A20 is still not responding. Try frobbing it again. + # + decb (a20_tries - setup_code) + jnz a20_try_loop + + movw $(a20_err_msg - setup_code), %si + call prtstr + +a20_die: + hlt + jmp a20_die + +a20_tries: + .byte A20_ENABLE_LOOPS + +a20_err_msg: + .ascii "linux: fatal error: A20 gate not responding!" + .byte 13, 10, 0 + + # If we get here, all is good +a20_done: + # Leave the idt alone + + # set up gdt + xorl %eax, %eax # Compute gdt_base + movw %ds, %ax # (Convert %ds:gdt to a linear ptr) + shll $4, %eax + addl $(bImage_gdt - setup_code), %eax + movl %eax, (bImage_gdt_48+2 - setup_code) + DATA32 lgdt %ds:(bImage_gdt_48 - setup_code) # load gdt with whatever is + # appropriate + + # Switch to protected mode + movl %cr0, %eax + orb $CR0_PE, %al + movl %eax, %cr0 + + DATA32 ljmp *%ds:(code32 - setup_code) +code32: + .long 0x100000 + .word __BOOT_CS, 0 + +# Here's a bunch of information about your current kernel.. +kernel_version: .ascii "Etherboot " + .ascii VERSION + .byte 0 + +# This is the default real mode switch routine. +# to be called just before protected mode transition +default_switch: + cli # no interrupts allowed ! + movb $0x80, %al # disable NMI for bootup + # sequence + outb %al, $0x70 + lret + +# This routine tests whether or not A20 is enabled. If so, it +# exits with zf = 0. +# +# The memory address used, 0x200, is the int $0x80 vector, which +# should be safe. + +A20_TEST_ADDR = 4*0x80 + +a20_test: + pushw %cx + pushw %ax + xorw %cx, %cx + movw %cx, %fs # Low memory + decw %cx + movw %cx, %gs # High memory area + movw $A20_TEST_LOOPS, %cx + movw %fs:(A20_TEST_ADDR), %ax + pushw %ax +a20_test_wait: + incw %ax + movw %ax, %fs:(A20_TEST_ADDR) + call delay # Serialize and make delay constant + cmpw %gs:(A20_TEST_ADDR+0x10), %ax + loope a20_test_wait + + popw %fs:(A20_TEST_ADDR) + popw %ax + popw %cx + ret + + +# This routine checks that the keyboard command queue is empty +# (after emptying the output buffers) +# +# Some machines have delusions that the keyboard buffer is always full +# with no keyboard attached... +# +# If there is no keyboard controller, we will usually get 0xff +# to all the reads. With each IO taking a microsecond and +# a timeout of 100,000 iterations, this can take about half a +# second ("delay" == outb to port 0x80). That should be ok, +# and should also be plenty of time for a real keyboard controller +# to empty. +# + +empty_8042: + pushl %ecx + movl $100000, %ecx + +empty_8042_loop: + decl %ecx + jz empty_8042_end_loop + + call delay + + inb $0x64, %al # 8042 status port + testb $1, %al # output buffer? + jz no_output + + call delay + inb $0x60, %al # read it + jmp empty_8042_loop + +no_output: + testb $2, %al # is input buffer full? + jnz empty_8042_loop # yes - loop +empty_8042_end_loop: + popl %ecx + + +# Delay is needed after doing I/O +delay: + outb %al,$0x80 + ret + +# Descriptor tables +# +# NOTE: The intel manual says gdt should be sixteen bytes aligned for +# efficiency reasons. However, there are machines which are known not +# to boot with misaligned GDTs, so alter this at your peril! If you alter +# GDT_ENTRY_BOOT_CS (in asm/segment.h) remember to leave at least two +# empty GDT entries (one for NULL and one reserved). +# +# NOTE: On some CPUs, the GDT must be 8 byte aligned. This is +# true for the Voyager Quad CPU card which will not boot without +# This directive. 16 byte aligment is recommended by intel. +# + .balign 16 +bImage_gdt: + .fill GDT_ENTRY_BOOT_CS,8,0 + + .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb) + .word 0 # base address = 0 + .word 0x9A00 # code read/exec + .word 0x00CF # granularity = 4096, 386 + # (+5th nibble of limit) + + .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb) + .word 0 # base address = 0 + .word 0x9200 # data read/write + .word 0x00CF # granularity = 4096, 386 + # (+5th nibble of limit) +bImage_gdt_end: + .balign 4 + + .word 0 # alignment byte +bImage_idt_48: + .word 0 # idt limit = 0 + .long 0 # idt base = 0L + + .word 0 # alignment byte +bImage_gdt_48: + .word bImage_gdt_end - bImage_gdt - 1 # gdt limit + .long bImage_gdt_48 - setup_code # gdt base (filled in later) + + .section ".text16", "ax", @progbits +prefix_exit: + int $0x19 /* should try to boot machine */ +prefix_exit_end: + .previous + + + .org (PREFIXSIZE - 4) +# Setup signature -- must be last +setup_sig1: .word SIG1 +setup_sig2: .word SIG2 + /* Etherboot expects to be contiguous in memory once loaded. + * The linux bImage protocol does not do this, but since we + * don't need any information that's left in the prefix, it + * doesn't matter: we just have to ensure that we make it to _start + * + * protected_start will live at 0x100000 and it will be the + * the first code called as we enter protected mode. + */ + .code32 +protected_start: + /* Load segment registers */ + movw $__BOOT_DS, %ax + movw %ax, %ss + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + + /* Use the internal etherboot stack */ + movl $(_prefix_stack_end - protected_start + 0x100000), %esp + + pushl $0 /* No parameters to preserve for exit path */ + pushl $0 /* Use prefix exit path mechanism */ + + jmp _start +/* + That's about it. +*/ diff --git a/gpxe/src/arch/i386/prefix/bootpart.S b/gpxe/src/arch/i386/prefix/bootpart.S new file mode 100644 index 0000000..d60fe9b --- /dev/null +++ b/gpxe/src/arch/i386/prefix/bootpart.S @@ -0,0 +1,216 @@ +#define BOOT_SEG 0x07c0 +#define EXEC_SEG 0x0100 +#define STACK_SEG 0x0200 +#define STACK_SIZE 0x2000 + + .text + .arch i386 + .section ".prefix", "awx", @progbits + .code16 + +/* + * Find active partition + * + * Parameters: + * %dl : BIOS drive number + * %bp : Active partition handler routine + */ +find_active_partition: + /* Set up stack at STACK_SEG:STACK_SIZE */ + movw $STACK_SEG, %ax + movw %ax, %ss + movw $STACK_SIZE, %sp + + /* Relocate self to EXEC_SEG */ + pushw $BOOT_SEG + popw %ds + pushw $EXEC_SEG + popw %es + xorw %si, %si + xorw %di, %di + movw $0x200, %cx + rep movsb + ljmp $EXEC_SEG, $1f +1: pushw %ds + popw %es + pushw %cs + popw %ds + + /* Check for LBA extensions */ + movb $0x41, %ah + movw $0x55aa, %bx + stc + int $0x13 + jc 1f + cmpw $0xaa55, %bx + jne 1f + movw $read_lba, read_sectors +1: + /* Read and process root partition table */ + xorb %dh, %dh + movw $0x0001, %cx + xorl %esi, %esi + xorl %edi, %edi + call process_table + + /* Print failure message */ + movw $10f, %si + jmp boot_error +10: .asciz "Could not locate active partition\r\n" + +/* + * Print failure message and boot next device + * + * Parameters: + * %si : Failure string + */ +boot_error: + cld + movw $0x0007, %bx + movb $0x0e, %ah +1: lodsb + testb %al, %al + je 99f + int $0x10 + jmp 1b +99: /* Boot next device */ + int $0x18 + +/* + * Process partition table + * + * Parameters: + * %dl : BIOS drive number + * %dh : Head + * %cl : Sector (bits 0-5), high two bits of cylinder (bits 6-7) + * %ch : Low eight bits of cylinder + * %esi:%edi : LBA address + * %bp : Active partition handler routine + * + * Returns: + * CF set on error + */ +process_table: + pushal + call read_boot_sector + jc 99f + movw $446, %bx +1: call process_partition + addw $16, %bx + cmpw $510, %bx + jne 1b +99: popal + ret + +/* + * Process partition + * + * Parameters: + * %dl : BIOS drive number + * %dh : Head + * %cl : Sector (bits 0-5), high two bits of cylinder (bits 6-7) + * %ch : Low eight bits of cylinder + * %esi:%edi : LBA address + * %bx : Offset within partition table + * %bp : Active partition handler routine + */ +process_partition: + pushal + /* Load C/H/S values from partition entry */ + movb %es:1(%bx), %dh + movw %es:2(%bx), %cx + /* Update LBA address from partition entry */ + addl %es:8(%bx), %edi + adcl $0, %esi + /* Check active flag */ + testb $0x80, %es:(%bx) + jz 1f + call read_boot_sector + jc 99f + jmp *%bp +1: /* Check for extended partition */ + movb %es:4(%bx), %al + cmpb $0x05, %al + je 2f + cmpb $0x0f, %al + je 2f + cmpb $0x85, %al + jne 99f +2: call process_table +99: popal + /* Reload original partition table */ + call read_boot_sector + ret + +/* + * Read single sector to %es:0000 and verify 0x55aa signature + * + * Parameters: + * %dl : BIOS drive number + * %dh : Head + * %cl : Sector (bits 0-5), high two bits of cylinder (bits 6-7) + * %ch : Low eight bits of cylinder + * %esi:%edi : LBA address + * + * Returns: + * CF set on error + */ +read_boot_sector: + pushw %ax + movw $1, %ax + call *read_sectors + jc 99f + cmpw $0xaa55, %es:(510) + je 99f + stc +99: popw %ax + ret + +/* + * Read sectors to %es:0000 + * + * Parameters: + * %dl : BIOS drive number + * %dh : Head + * %cl : Sector (bits 0-5), high two bits of cylinder (bits 6-7) + * %ch : Low eight bits of cylinder + * %esi:%edi : LBA address + * %ax : Number of sectors (max 127) + * + * Returns: + * CF set on error + */ +read_sectors: .word read_chs + +read_chs: + /* Read sectors using C/H/S address */ + pushal + xorw %bx, %bx + movb $0x02, %ah + stc + int $0x13 + sti + popal + ret + +read_lba: + /* Read sectors using LBA address */ + pushal + movw %ax, (lba_desc + 2) + pushw %es + popw (lba_desc + 6) + movl %edi, (lba_desc + 8) + movl %esi, (lba_desc + 12) + movw $lba_desc, %si + movb $0x42, %ah + int $0x13 + popal + ret + +lba_desc: + .byte 0x10 + .byte 0 + .word 1 + .word 0x0000 + .word 0x0000 + .long 0, 0 diff --git a/gpxe/src/arch/i386/prefix/comprefix.S b/gpxe/src/arch/i386/prefix/comprefix.S new file mode 100644 index 0000000..2cbbca5 --- /dev/null +++ b/gpxe/src/arch/i386/prefix/comprefix.S @@ -0,0 +1,46 @@ +/* We need a real mode stack that won't be stomped on by Etherboot + which starts at 0x20000. Choose something that's sufficiently high, + but not in DOC territory. Note that we couldn't do this in a real + .com program since stack variables are in the same segment as the + code and data, but this isn't really a .com program, it just looks + like one to make DOS load it into memory. It still has the 64kB + limitation of .com files though. */ +#define STACK_SEG 0x7000 +#define STACK_SIZE 0x4000 + + .text + .code16 + .arch i386 + .section ".prefix", "ax", @progbits + +/* Cheat a little with the relocations: .COM files are loaded at 0x100 */ +_prefix: + /* Set up temporary stack */ + movw $STACK_SEG, %ax + movw %ax, %ss + movw $STACK_SIZE, %sp + + pushl $0 /* No parameters to preserve for exit path */ + pushw $0 /* Dummy return address - use prefix_exit */ + + /* Calculate segment address of image start */ + pushw %cs + popw %ax + addw $(0x100/16), %ax + pushw %ax + pushw $_start + /* Calculated lcall to _start with %cs:0000 = image start */ + lret + + .section ".text16", "ax", @progbits +prefix_exit: + movw $0x4c00,%ax /* return to DOS */ + int $0x21 /* reach this on Quit */ +prefix_exit_end: + .previous + +/* The body of etherboot is attached here at build time. + * Force 16 byte alignment + */ + .align 16,0 +_body: diff --git a/gpxe/src/arch/i386/prefix/dskprefix.S b/gpxe/src/arch/i386/prefix/dskprefix.S new file mode 100644 index 0000000..0156812 --- /dev/null +++ b/gpxe/src/arch/i386/prefix/dskprefix.S @@ -0,0 +1,379 @@ +/* NOTE: this boot sector contains instructions that need at least an 80186. + * Yes, as86 has a bug somewhere in the valid instruction set checks. + * + */ + +/* floppyload.S Copyright (C) 1991, 1992 Linus Torvalds + * modified by Drew Eckhardt + * modified by Bruce Evans (bde) + * + * floppyprefix.S is loaded at 0x0000:0x7c00 by the bios-startup routines. + * + * It then loads the system at SYSSEG<<4, using BIOS interrupts. + * + * The loader has been made as simple as possible, and continuous read errors + * will result in a unbreakable loop. Reboot by hand. It loads pretty fast by + * getting whole tracks at a time whenever possible. + */ + +.equ BOOTSEG, 0x07C0 /* original address of boot-sector */ + +.equ SYSSEG, 0x1000 /* system loaded at SYSSEG<<4 */ + + .org 0 + .arch i386 + .text + .section ".prefix", "ax", @progbits + .code16 + + jmp $BOOTSEG, $go /* reload cs:ip to match relocation addr */ +go: + movw $0x2000-12, %di /* 0x2000 is arbitrary value >= length */ + /* of bootsect + room for stack + 12 for */ + /* saved disk parm block */ + + movw $BOOTSEG, %ax + movw %ax,%ds + movw %ax,%es + movw %ax,%ss /* put stack at BOOTSEG:0x4000-12. */ + movw %di,%sp + +/* Many BIOS's default disk parameter tables will not recognize multi-sector + * reads beyond the maximum sector number specified in the default diskette + * parameter tables - this may mean 7 sectors in some cases. + * + * Since single sector reads are slow and out of the question, we must take care + * of this by creating new parameter tables (for the first disk) in RAM. We + * will set the maximum sector count to 36 - the most we will encounter on an + * ED 2.88. High doesn't hurt. Low does. + * + * Segments are as follows: ds=es=ss=cs - BOOTSEG + */ + + xorw %cx,%cx + movw %cx,%es /* access segment 0 */ + movw $0x78, %bx /* 0:bx is parameter table address */ + pushw %ds /* save ds */ +/* 0:bx is parameter table address */ + ldsw %es:(%bx),%si /* loads ds and si */ + + movw %ax,%es /* ax is BOOTSECT (loaded above) */ + movb $6, %cl /* copy 12 bytes */ + cld + pushw %di /* keep a copy for later */ + rep + movsw /* ds:si is source, es:di is dest */ + popw %di + + movb $36,%es:4(%di) + + movw %cx,%ds /* access segment 0 */ + xchgw %di,(%bx) + movw %es,%si + xchgw %si,2(%bx) + popw %ds /* restore ds */ + movw %di, dpoff /* save old parameters */ + movw %si, dpseg /* to restore just before finishing */ + pushw %ds + popw %es /* reload es */ + +/* Note that es is already set up. Also cx is 0 from rep movsw above. */ + + xorb %ah,%ah /* reset FDC */ + xorb %dl,%dl + int $0x13 + +/* Get disk drive parameters, specifically number of sectors/track. + * + * It seems that there is no BIOS call to get the number of sectors. Guess + * 36 sectors if sector 36 can be read, 18 sectors if sector 18 can be read, + * 15 if sector 15 can be read. Otherwise guess 9. + */ + + movw $disksizes, %si /* table of sizes to try */ + +probe_loop: + lodsb + cbtw /* extend to word */ + movw %ax, sectors + cmpw $disksizes+4, %si + jae got_sectors /* if all else fails, try 9 */ + xchgw %cx,%ax /* cx = track and sector */ + xorw %dx,%dx /* drive 0, head 0 */ + movw $0x0200, %bx /* address after boot sector */ + /* (512 bytes from origin, es = cs) */ + movw $0x0201, %ax /* service 2, 1 sector */ + int $0x13 + jc probe_loop /* try next value */ + +got_sectors: + movw $msg1end-msg1, %cx + movw $msg1, %si + call print_str + +/* ok, we've written the Loading... message, now we want to load the system */ + + movw $SYSSEG, %ax + movw %ax,%es /* segment of SYSSEG<<4 */ + pushw %es + call read_it + +/* This turns off the floppy drive motor, so that we enter the kernel in a + * known state, and don't have to worry about it later. + */ + movw $0x3f2, %dx + xorb %al,%al + outb %al,%dx + + call print_nl + pop %es /* = SYSSEG */ + +/* Restore original disk parameters */ + movw $0x78, %bx + movw dpoff, %di + movw dpseg, %si + xorw %ax,%ax + movw %ax,%ds + movw %di,(%bx) + movw %si,2(%bx) + + /* Everything now loaded. %es = SYSSEG, so %es:0000 points to + * start of loaded image. + */ + + /* Jump to loaded copy */ + ljmp $SYSSEG, $start_runtime + +endseg: .word SYSSEG + _filesz_pgh + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii "SUBW" + .long endseg + .long 16 + .long 0 + .previous + +/* This routine loads the system at address SYSSEG<<4, making sure no 64kB + * boundaries are crossed. We try to load it as fast as possible, loading whole + * tracks whenever we can. + * + * in: es - starting address segment (normally SYSSEG) + */ +read_it: + movw $0,sread /* load whole image including prefix */ + movw %es,%ax + testw $0x0fff, %ax +die: jne die /* es must be at 64kB boundary */ + xorw %bx,%bx /* bx is starting address within segment */ +rp_read: + movw %es,%ax + movw %bx,%dx + movb $4, %cl + shrw %cl,%dx /* bx is always divisible by 16 */ + addw %dx,%ax + cmpw endseg, %ax /* have we loaded all yet? */ + jb ok1_read + ret +ok1_read: + movw sectors, %ax + subw sread, %ax + movw %ax,%cx + shlw $9, %cx + addw %bx,%cx + jnc ok2_read + je ok2_read + xorw %ax,%ax + subw %bx,%ax + shrw $9, %ax +ok2_read: + call read_track + movw %ax,%cx + addw sread, %ax + cmpw sectors, %ax + jne ok3_read + movw $1, %ax + subw head, %ax + jne ok4_read + incw track +ok4_read: + movw %ax, head + xorw %ax,%ax +ok3_read: + movw %ax, sread + shlw $9, %cx + addw %cx,%bx + jnc rp_read + movw %es,%ax + addb $0x10, %ah + movw %ax,%es + xorw %bx,%bx + jmp rp_read + +read_track: + pusha + pushw %ax + pushw %bx + pushw %bp /* just in case the BIOS is buggy */ + movw $0x0e2e, %ax /* 0x2e = . */ + movw $0x0007, %bx + int $0x10 + popw %bp + popw %bx + popw %ax + + movw track, %dx + movw sread, %cx + incw %cx + movb %dl,%ch + movw head, %dx + movb %dl,%dh + andw $0x0100, %dx + movb $2, %ah + + pushw %dx /* save for error dump */ + pushw %cx + pushw %bx + pushw %ax + + int $0x13 + jc bad_rt + addw $8, %sp + popa + ret + +bad_rt: pushw %ax /* save error code */ + call print_all /* ah = error, al = read */ + + xorb %ah,%ah + xorb %dl,%dl + int $0x13 + + addw $10, %sp + popa + jmp read_track + +/* print_all is for debugging purposes. It will print out all of the registers. + * The assumption is that this is called from a routine, with a stack frame like + * dx + * cx + * bx + * ax + * error + * ret <- sp + */ + +print_all: + call print_nl /* nl for readability */ + movw $5, %cx /* error code + 4 registers */ + movw %sp,%bp + +print_loop: + pushw %cx /* save count left */ + + cmpb $5, %cl + jae no_reg /* see if register name is needed */ + + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + movw $0xe05+0x41-1, %ax + subb %cl,%al + int $0x10 + + movb $0x58, %al /* 'X' */ + int $0x10 + + movb $0x3A, %al /* ':' */ + int $0x10 + +no_reg: + addw $2, %bp /* next register */ + call print_hex /* print it */ + movb $0x20, %al /* print a space */ + int $0x10 + popw %cx + loop print_loop + call print_nl /* nl for readability */ + ret + +print_str: + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + movb $0x0e, %ah /* write char, tty mode */ +prloop: + lodsb + int $0x10 + loop prloop + ret + +print_nl: + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + movw $0xe0d, %ax /* CR */ + int $0x10 + movb $0xa, %al /* LF */ + int $0x10 + ret + +/* print_hex prints the word pointed to by ss:bp in hexadecimal. */ + +print_hex: + movw (%bp),%dx /* load word into dx */ + movb $4, %cl + movb $0x0e, %ah /* write char, tty mode */ + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + call print_digit + call print_digit + call print_digit +/* fall through */ +print_digit: + rol %cl,%dx /* rotate so that lowest 4 bits are used */ + movb $0x0f, %al /* mask for nybble */ + andb %dl,%al + addb $0x90, %al /* convert al to ascii hex (four instructions) */ + daa + adcb $0x40, %al + daa + int $0x10 + ret + +sread: .word 0 /* sectors read of current track */ +head: .word 0 /* current head */ +track: .word 0 /* current track */ + +sectors: + .word 0 + +dpseg: .word 0 +dpoff: .word 0 + +disksizes: + .byte 36,18,15,9 + +msg1: + .ascii "Loading ROM image" +msg1end: + + .org 510, 0 + .word 0xAA55 + +start_runtime: + /* Install gPXE */ + call install + + /* Set up real-mode stack */ + movw %bx, %ss + movw $_estack16, %sp + + /* Jump to .text16 segment */ + pushw %ax + pushw $1f + lret + .section ".text16", "awx", @progbits +1: + pushl $main + pushw %cs + call prot_call + popl %ecx /* discard */ + + /* Uninstall gPXE */ + call uninstall + + /* Boot next device */ + int $0x18 + diff --git a/gpxe/src/arch/i386/prefix/elf_dprefix.S b/gpxe/src/arch/i386/prefix/elf_dprefix.S new file mode 100644 index 0000000..0eac77e --- /dev/null +++ b/gpxe/src/arch/i386/prefix/elf_dprefix.S @@ -0,0 +1,94 @@ +#include "elf.h" + + .arch i386 + .section ".prefix", "a", @progbits + +#define LOAD_ADDR 0x10000 + + /* ELF Header */ + .globl elf_header +elf_header: +e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 +e_type: .short ET_DYN +e_machine: .short EM_386 +e_version: .long 1 +e_entry: .long LOAD_ADDR + _start - elf_header +e_phoff: .long elf_program_header - elf_header +e_shoff: .long 0 +e_flags: .long 0 +e_ehsize: .short elf_header_end - elf_header +e_phentsize: .short ELF32_PHDR_SIZE +e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE +e_shentsize: .short 0 +e_shnum: .short 0 +e_shstrndx: .short 0 +elf_header_end: + +elf_program_header: +phdr1_p_type: .long PT_NOTE +phdr1_p_offset: .long elf_note - elf_header +phdr1_p_vaddr: .long elf_note +phdr1_p_paddr: .long elf_note +phdr1_p_filesz: .long elf_note_end - elf_note +phdr1_p_memsz: .long elf_note_end - elf_note +phdr1_p_flags: .long PF_R | PF_W | PF_X +phdr1_p_align: .long 0 + +/* The decompressor */ +phdr2_p_type: .long PT_LOAD +phdr2_p_offset: .long 0 +phdr2_p_vaddr: .long elf_header +phdr2_p_paddr: .long LOAD_ADDR +phdr2_p_filesz: .long _verbatim_size +phdr2_p_memsz: .long _image_size +phdr2_p_flags: .long PF_R | PF_W | PF_X +phdr2_p_align: .long 16 + +elf_program_header_end: + + .globl elf_note +elf_note: + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_NAME +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .asciz "Etherboot" +4: + + + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_VERSION +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .asciz VERSION +4: + +#if 0 + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_CHECKSUM +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .word 0 +4: +#endif + .balign 4 +elf_note_end: + + /* Dummy routines to satisfy the build */ + .section ".text16", "ax", @progbits +prefix_exit: + +prefix_exit_end: + .previous diff --git a/gpxe/src/arch/i386/prefix/elfprefix.S b/gpxe/src/arch/i386/prefix/elfprefix.S new file mode 100644 index 0000000..d712753 --- /dev/null +++ b/gpxe/src/arch/i386/prefix/elfprefix.S @@ -0,0 +1,94 @@ +#include "elf.h" + + .arch i386 + .section ".prefix", "a", @progbits + +#define LOAD_ADDR 0x10000 + + /* ELF Header */ + .globl elf_header +elf_header: +e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 +e_type: .short ET_EXEC +e_machine: .short EM_386 +e_version: .long 1 +e_entry: .long LOAD_ADDR + _start - elf_header +e_phoff: .long elf_program_header - elf_header +e_shoff: .long 0 +e_flags: .long 0 +e_ehsize: .short elf_header_end - elf_header +e_phentsize: .short ELF32_PHDR_SIZE +e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE +e_shentsize: .short 0 +e_shnum: .short 0 +e_shstrndx: .short 0 +elf_header_end: + +elf_program_header: +phdr1_p_type: .long PT_NOTE +phdr1_p_offset: .long elf_note - elf_header +phdr1_p_vaddr: .long elf_note +phdr1_p_paddr: .long elf_note +phdr1_p_filesz: .long elf_note_end - elf_note +phdr1_p_memsz: .long elf_note_end - elf_note +phdr1_p_flags: .long PF_R | PF_W | PF_X +phdr1_p_align: .long 0 + +/* The decompressor */ +phdr2_p_type: .long PT_LOAD +phdr2_p_offset: .long 0 +phdr2_p_vaddr: .long elf_header +phdr2_p_paddr: .long LOAD_ADDR +phdr2_p_filesz: .long _verbatim_size +phdr2_p_memsz: .long _image_size +phdr2_p_flags: .long PF_R | PF_W | PF_X +phdr2_p_align: .long 16 + +elf_program_header_end: + + .globl elf_note +elf_note: + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_NAME +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .asciz "Etherboot" +4: + + + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_VERSION +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .asciz VERSION +4: + +#if 0 + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_CHECKSUM +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .word 0 +4: +#endif + .balign 4 +elf_note_end: + + /* Dummy routines to satisfy the build */ + .section ".text16", "ax", @progbits +prefix_exit: + +prefix_exit_end: + .previous diff --git a/gpxe/src/arch/i386/prefix/exeprefix.S b/gpxe/src/arch/i386/prefix/exeprefix.S new file mode 100755 index 0000000..f1b402b --- /dev/null +++ b/gpxe/src/arch/i386/prefix/exeprefix.S @@ -0,0 +1,41 @@ +/* + Prefix for .exe images + Doesn't work yet, even though it starts off the same as a .com + image as shown by DOS debug. +*/ + + .text + .code16 + .arch i386 + .section ".prefix", "ax", @progbits + +_prefix: + .byte 'M', 'Z' + .short _exe_size_tail /* tail */ + .short _exe_size_pages /* pages */ + .short 0 /* relocations */ + .short 2 /* header paras */ + .short _exe_bss_size /* min */ + .short 0xFFFF /* max paras */ + .short _exe_ss_offset /* SS */ + .short _stack_size /* SP */ + .short 0 /* checksum */ + .short 0 /* IP */ + .short 0 /* CS */ + .short 0x1C /* reloc offset */ + .short 0 /* overlay number */ + .short 0 /* fill */ + .short 0 /* fill */ + + .section ".text16", "ax", @progbits +prefix_exit: + movw $0x4c00,%ax /* return to DOS */ + int $0x21 /* reach this on Quit */ +prefix_exit_end: + .previous + +/* The body of etherboot is attached here at build time. + * Force 16 byte alignment + */ + .align 16,0 +_body: diff --git a/gpxe/src/arch/i386/prefix/hdprefix.S b/gpxe/src/arch/i386/prefix/hdprefix.S new file mode 100644 index 0000000..086d7f4 --- /dev/null +++ b/gpxe/src/arch/i386/prefix/hdprefix.S @@ -0,0 +1,107 @@ + .text + .arch i386 + .section ".prefix", "awx", @progbits + .code16 + .org 0 + + movw $load_image, %bp + jmp find_active_partition + +#include "bootpart.S" + +load_image: + /* Get disk geometry */ + pushal + pushw %es + movb $0x08, %ah + int $0x13 + jc load_failed + movb %cl, max_sector + movb %dh, max_head + popw %es + popal + +1: /* Read to end of current track */ + movb %cl, %al + negb %al + addb max_sector, %al + incb %al + andb $0x3f, %al + movzbl %al, %eax + call *read_sectors + jc load_failed + + /* Update %es */ + movw %es, %bx + shll $5, %eax + addw %ax, %bx + movw %bx, %es + shrl $5, %eax + + /* Update LBA address */ + addl %eax, %edi + adcl $0, %esi + + /* Update CHS address */ + andb $0xc0, %cl + orb $0x01, %cl + incb %dh + cmpb max_head, %dh + jbe 2f + xorb %dh, %dh + incb %ch + jnc 2f + addb $0xc0, %cl +2: + /* Loop until whole image is read */ + subl %eax, load_length + ja 1b + ljmp $BOOT_SEG, $start_image + +max_sector: + .byte 0 +max_head: + .byte 0 +load_length: + .long _filesz_sect + + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii "SUBL" + .long load_length + .long 512 + .long 0 + .previous + + +load_failed: + movw $10f, %si + jmp boot_error +10: .asciz "Could not load gPXE\r\n" + + .org 510 + .byte 0x55, 0xaa + +start_image: + /* Install gPXE */ + call install + + /* Set up real-mode stack */ + movw %bx, %ss + movw $_estack16, %sp + + /* Jump to .text16 segment */ + pushw %ax + pushw $1f + lret + .section ".text16", "awx", @progbits +1: + pushl $main + pushw %cs + call prot_call + popl %ecx /* discard */ + + /* Uninstall gPXE */ + call uninstall + + /* Boot next device */ + int $0x18 diff --git a/gpxe/src/arch/i386/prefix/kkpxeprefix.S b/gpxe/src/arch/i386/prefix/kkpxeprefix.S new file mode 100644 index 0000000..e0bea0c --- /dev/null +++ b/gpxe/src/arch/i386/prefix/kkpxeprefix.S @@ -0,0 +1,8 @@ +/***************************************************************************** + * PXE prefix that keeps the whole PXE stack present + ***************************************************************************** + */ + +#define PXELOADER_KEEP_UNDI +#define PXELOADER_KEEP_PXE +#include "pxeprefix.S" diff --git a/gpxe/src/arch/i386/prefix/kpxeprefix.S b/gpxe/src/arch/i386/prefix/kpxeprefix.S new file mode 100644 index 0000000..d708604 --- /dev/null +++ b/gpxe/src/arch/i386/prefix/kpxeprefix.S @@ -0,0 +1,7 @@ +/***************************************************************************** + * PXE prefix that keep the UNDI portion of the PXE stack present + ***************************************************************************** + */ + +#define PXELOADER_KEEP_UNDI +#include "pxeprefix.S" diff --git a/gpxe/src/arch/i386/prefix/libprefix.S b/gpxe/src/arch/i386/prefix/libprefix.S new file mode 100644 index 0000000..4218913 --- /dev/null +++ b/gpxe/src/arch/i386/prefix/libprefix.S @@ -0,0 +1,817 @@ +/* + * Copyright (C) 2006 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. + * + */ + + .arch i386 + +/** + * High memory temporary load address + * + * Temporary buffer into which to copy (or decompress) our runtime + * image, prior to calling get_memmap() and relocate(). We don't + * actually leave anything here once install() has returned. + * + * We use the start of an even megabyte so that we don't have to worry + * about the current state of the A20 line. + * + * We use 4MB rather than 2MB because some PXE stack / PMM BIOS + * combinations are known to place data required by other UNDI ROMs + * loader around the 2MB mark. + */ + .globl HIGHMEM_LOADPOINT + .equ HIGHMEM_LOADPOINT, ( 4 << 20 ) + +/* Image compression enabled */ +#define COMPRESS 1 + +#define CR0_PE 1 + +/***************************************************************************** + * Utility function: print character (with LF -> LF,CR translation) + * + * Parameters: + * %al : character to print + * %ds:di : output buffer (or %di=0 to print to console) + * Returns: + * %ds:di : next character in output buffer (if applicable) + ***************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 + .globl print_character +print_character: + /* Preserve registers */ + pushw %ax + pushw %bx + pushw %bp + /* If %di is non-zero, write character to buffer and exit */ + testw %di, %di + jz 1f + movb %al, %ds:(%di) + incw %di + jmp 3f +1: /* Print character */ + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + movb $0x0e, %ah /* write char, tty mode */ + cmpb $0x0a, %al /* '\n'? */ + jne 2f + int $0x10 + movb $0x0d, %al +2: int $0x10 + /* Restore registers and return */ +3: popw %bp + popw %bx + popw %ax + ret + .size print_character, . - print_character + +/***************************************************************************** + * Utility function: print a NUL-terminated string + * + * Parameters: + * %ds:si : string to print + * %ds:di : output buffer (or %di=0 to print to console) + * Returns: + * %ds:si : character after terminating NUL + * %ds:di : next character in output buffer (if applicable) + ***************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 + .globl print_message +print_message: + /* Preserve registers */ + pushw %ax + /* Print string */ +1: lodsb + testb %al, %al + je 2f + call print_character + jmp 1b +2: /* Restore registers and return */ + popw %ax + ret + .size print_message, . - print_message + +/***************************************************************************** + * Utility functions: print hex digit/byte/word/dword + * + * Parameters: + * %al (low nibble) : digit to print + * %al : byte to print + * %ax : word to print + * %eax : dword to print + * %ds:di : output buffer (or %di=0 to print to console) + * Returns: + * %ds:di : next character in output buffer (if applicable) + ***************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 + .globl print_hex_dword +print_hex_dword: + rorl $16, %eax + call print_hex_word + rorl $16, %eax + /* Fall through */ + .size print_hex_dword, . - print_hex_dword + .globl print_hex_word +print_hex_word: + xchgb %al, %ah + call print_hex_byte + xchgb %al, %ah + /* Fall through */ + .size print_hex_word, . - print_hex_word + .globl print_hex_byte +print_hex_byte: + rorb $4, %al + call print_hex_nibble + rorb $4, %al + /* Fall through */ + .size print_hex_byte, . - print_hex_byte + .globl print_hex_nibble +print_hex_nibble: + /* Preserve registers */ + pushw %ax + /* Print digit (technique by Norbert Juffa */ + andb $0x0f, %al + cmpb $10, %al + sbbb $0x69, %al + das + call print_character + /* Restore registers and return */ + popw %ax + ret + .size print_hex_nibble, . - print_hex_nibble + +/***************************************************************************** + * Utility function: print PCI bus:dev.fn + * + * Parameters: + * %ax : PCI bus:dev.fn to print + * %ds:di : output buffer (or %di=0 to print to console) + * Returns: + * %ds:di : next character in output buffer (if applicable) + ***************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 + .globl print_pci_busdevfn +print_pci_busdevfn: + /* Preserve registers */ + pushw %ax + /* Print bus */ + xchgb %al, %ah + call print_hex_byte + /* Print ":" */ + movb $( ':' ), %al + call print_character + /* Print device */ + movb %ah, %al + shrb $3, %al + call print_hex_byte + /* Print "." */ + movb $( '.' ), %al + call print_character + /* Print function */ + movb %ah, %al + andb $0x07, %al + call print_hex_nibble + /* Restore registers and return */ + popw %ax + ret + .size print_pci_busdevfn, . - print_pci_busdevfn + +/***************************************************************************** + * Utility function: clear current line + * + * Parameters: + * %ds:di : output buffer (or %di=0 to print to console) + * Returns: + * %ds:di : next character in output buffer (if applicable) + ***************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 + .globl print_kill_line +print_kill_line: + /* Preserve registers */ + pushw %ax + pushw %cx + /* Print CR */ + movb $( '\r' ), %al + call print_character + /* Print 79 spaces */ + movb $( ' ' ), %al + movw $79, %cx +1: call print_character + loop 1b + /* Print CR */ + movb $( '\r' ), %al + call print_character + /* Restore registers and return */ + popw %cx + popw %ax + ret + .size print_kill_line, . - print_kill_line + +/**************************************************************************** + * pm_call (real-mode near call) + * + * Call routine in 16-bit protected mode for access to extended memory + * + * Parameters: + * %ax : address of routine to call in 16-bit protected mode + * Returns: + * none + * Corrupts: + * %ax + * + * The specified routine is called in 16-bit protected mode, with: + * + * %cs : 16-bit code segment with base matching real-mode %cs + * %ss : 16-bit data segment with base matching real-mode %ss + * %ds,%es,%fs,%gs : 32-bit data segment with zero base and 4GB limit + * + **************************************************************************** + */ + +#ifndef KEEP_IT_REAL + + /* GDT for protected-mode calls */ + .section ".prefix.lib", "awx", @progbits + .align 16 +pm_call_vars: +gdt: +gdt_limit: .word gdt_length - 1 +gdt_base: .long 0 + .word 0 /* padding */ +pm_cs: /* 16-bit protected-mode code segment */ + .equ PM_CS, pm_cs - gdt + .word 0xffff, 0 + .byte 0, 0x9b, 0x00, 0 +pm_ss: /* 16-bit protected-mode stack segment */ + .equ PM_SS, pm_ss - gdt + .word 0xffff, 0 + .byte 0, 0x93, 0x00, 0 +pm_ds: /* 32-bit protected-mode flat data segment */ + .equ PM_DS, pm_ds - gdt + .word 0xffff, 0 + .byte 0, 0x93, 0xcf, 0 +gdt_end: + .equ gdt_length, . - gdt + .size gdt, . - gdt + + .section ".prefix.lib", "awx", @progbits + .align 16 +pm_saved_gdt: + .long 0, 0 + .size pm_saved_gdt, . - pm_saved_gdt + + .equ pm_call_vars_size, . - pm_call_vars +#define PM_CALL_VAR(x) ( -pm_call_vars_size + ( (x) - pm_call_vars ) ) + + .section ".prefix.lib", "awx", @progbits + .code16 +pm_call: + /* Preserve registers, flags, and RM return point */ + pushw %bp + movw %sp, %bp + subw $pm_call_vars_size, %sp + andw $0xfff0, %sp + pushfl + pushw %gs + pushw %fs + pushw %es + pushw %ds + pushw %ss + pushw %cs + pushw $99f + + /* Set up local variable block, and preserve GDT */ + pushw %cx + pushw %si + pushw %di + pushw %ss + popw %es + movw $pm_call_vars, %si + leaw PM_CALL_VAR(pm_call_vars)(%bp), %di + movw $pm_call_vars_size, %cx + cs rep movsb + popw %di + popw %si + popw %cx + sgdt PM_CALL_VAR(pm_saved_gdt)(%bp) + + /* Set up GDT bases */ + pushl %eax + pushl %edi + xorl %eax, %eax + movw %ss, %ax + shll $4, %eax + movzwl %bp, %edi + addr32 leal PM_CALL_VAR(gdt)(%eax, %edi), %eax + movl %eax, PM_CALL_VAR(gdt_base)(%bp) + movw %cs, %ax + movw $PM_CALL_VAR(pm_cs), %di + call set_seg_base + movw %ss, %ax + movw $PM_CALL_VAR(pm_ss), %di + call set_seg_base + popl %edi + popl %eax + + /* Switch CPU to protected mode and load up segment registers */ + pushl %eax + cli + data32 lgdt PM_CALL_VAR(gdt)(%bp) + movl %cr0, %eax + orb $CR0_PE, %al + movl %eax, %cr0 + ljmp $PM_CS, $1f +1: movw $PM_SS, %ax + movw %ax, %ss + movw $PM_DS, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + popl %eax + + /* Call PM routine */ + call *%ax + + /* Set real-mode segment limits on %ds, %es, %fs and %gs */ + movw %ss, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + + /* Return CPU to real mode */ + movl %cr0, %eax + andb $0!CR0_PE, %al + movl %eax, %cr0 + + /* Restore registers and flags */ + lret /* will ljmp to 99f */ +99: popw %ss + popw %ds + popw %es + popw %fs + popw %gs + data32 lgdt PM_CALL_VAR(pm_saved_gdt)(%bp) + popfl + movw %bp, %sp + popw %bp + ret + .size pm_call, . - pm_call + +set_seg_base: + rolw $4, %ax + movw %ax, 2(%bp,%di) + andw $0xfff0, 2(%bp,%di) + movb %al, 4(%bp,%di) + andb $0x0f, 4(%bp,%di) + ret + .size set_seg_base, . - set_seg_base + +#endif /* KEEP_IT_REAL */ + +/**************************************************************************** + * copy_bytes (real-mode or 16-bit protected-mode near call) + * + * Copy bytes + * + * Parameters: + * %ds:esi : source address + * %es:edi : destination address + * %ecx : length + * Returns: + * %ds:esi : next source address + * %es:edi : next destination address + * Corrupts: + * None + **************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 +copy_bytes: + pushl %ecx + rep addr32 movsb + popl %ecx + ret + .size copy_bytes, . - copy_bytes + +/**************************************************************************** + * install_block (real-mode near call) + * + * Install block to specified address + * + * Parameters: + * %esi : source physical address (must be a multiple of 16) + * %edi : destination physical address (must be a multiple of 16) + * %ecx : length of (decompressed) data + * %edx : total length of block (including any uninitialised data portion) + * Returns: + * %esi : next source physical address (will be a multiple of 16) + * Corrupts: + * none + **************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 +install_block: + +#ifdef KEEP_IT_REAL + + /* Preserve registers */ + pushw %ds + pushw %es + pushl %ecx + pushl %edi + + /* Convert %esi and %edi to segment registers */ + shrl $4, %esi + movw %si, %ds + xorw %si, %si + shrl $4, %edi + movw %di, %es + xorw %di, %di + +#else /* KEEP_IT_REAL */ + + /* Call self in protected mode */ + pushw %ax + movw $1f, %ax + call pm_call + popw %ax + ret +1: + /* Preserve registers */ + pushl %ecx + pushl %edi + +#endif /* KEEP_IT_REAL */ + + +#if COMPRESS + /* Decompress source to destination */ + call decompress16 +#else + /* Copy source to destination */ + call copy_bytes +#endif + + /* Zero .bss portion */ + negl %ecx + addl %edx, %ecx + pushw %ax + xorw %ax, %ax + rep addr32 stosb + popw %ax + + /* Round up %esi to start of next source block */ + addl $0xf, %esi + andl $~0xf, %esi + + +#ifdef KEEP_IT_REAL + + /* Convert %ds:esi back to a physical address */ + movzwl %ds, %cx + shll $4, %ecx + addl %ecx, %esi + + /* Restore registers */ + popl %edi + popl %ecx + popw %es + popw %ds + +#else /* KEEP_IT_REAL */ + + /* Restore registers */ + popl %edi + popl %ecx + +#endif + + ret + .size install_block, . - install_block + +/**************************************************************************** + * alloc_basemem (real-mode near call) + * + * Allocate space for .text16 and .data16 from top of base memory. + * Memory is allocated using the BIOS free base memory counter at + * 0x40:13. + * + * Parameters: + * none + * Returns: + * %ax : .text16 segment address + * %bx : .data16 segment address + * Corrupts: + * none + **************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 + .globl alloc_basemem +alloc_basemem: + /* Preserve registers */ + pushw %fs + + /* FBMS => %ax as segment address */ + pushw $0x40 + popw %fs + movw %fs:0x13, %ax + shlw $6, %ax + + /* Calculate .data16 segment address */ + subw $_data16_memsz_pgh, %ax + pushw %ax + + /* Calculate .text16 segment address */ + subw $_text16_memsz_pgh, %ax + pushw %ax + + /* Update FBMS */ + shrw $6, %ax + movw %ax, %fs:0x13 + + /* Retrieve .text16 and .data16 segment addresses */ + popw %ax + popw %bx + + /* Restore registers and return */ + popw %fs + ret + .size alloc_basemem, . - alloc_basemem + +/**************************************************************************** + * free_basemem (real-mode near call) + * + * Free space allocated with alloc_basemem. + * + * Parameters: + * %ax : .text16 segment address + * %bx : .data16 segment address + * Returns: + * %ax : 0 if successfully freed + * Corrupts: + * none + **************************************************************************** + */ + .section ".text16", "ax", @progbits + .code16 + .globl free_basemem +free_basemem: + /* Preserve registers */ + pushw %fs + + /* Check FBMS counter */ + pushw %ax + shrw $6, %ax + pushw $0x40 + popw %fs + cmpw %ax, %fs:0x13 + popw %ax + jne 1f + + /* Check hooked interrupt count */ + cmpw $0, %cs:hooked_bios_interrupts + jne 1f + + /* OK to free memory */ + addw $_text16_memsz_pgh, %ax + addw $_data16_memsz_pgh, %ax + shrw $6, %ax + movw %ax, %fs:0x13 + xorw %ax, %ax + +1: /* Restore registers and return */ + popw %fs + ret + .size free_basemem, . - free_basemem + + .section ".text16.data", "aw", @progbits + .globl hooked_bios_interrupts +hooked_bios_interrupts: + .word 0 + .size hooked_bios_interrupts, . - hooked_bios_interrupts + +/**************************************************************************** + * install (real-mode near call) + * + * Install all text and data segments. + * + * Parameters: + * none + * Returns: + * %ax : .text16 segment address + * %bx : .data16 segment address + * Corrupts: + * none + **************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 + .globl install +install: + /* Preserve registers */ + pushl %esi + pushl %edi + /* Allocate space for .text16 and .data16 */ + call alloc_basemem + /* Image source = %cs:0000 */ + xorl %esi, %esi + /* Image destination = HIGHMEM_LOADPOINT */ + movl $HIGHMEM_LOADPOINT, %edi + /* Install text and data segments */ + call install_prealloc + /* Restore registers and return */ + popl %edi + popl %esi + ret + .size install, . - install + +/**************************************************************************** + * install_prealloc (real-mode near call) + * + * Install all text and data segments. + * + * Parameters: + * %ax : .text16 segment address + * %bx : .data16 segment address + * %esi : Image source physical address (or zero for %cs:0000) + * %edi : Decompression temporary area physical address + * Corrupts: + * none + **************************************************************************** + */ + .section ".prefix.lib", "awx", @progbits + .code16 + .globl install_prealloc +install_prealloc: + /* Save registers */ + pushal + pushw %ds + pushw %es + + /* Sanity: clear the direction flag asap */ + cld + + /* Calculate physical address of payload (i.e. first source) */ + testl %esi, %esi + jnz 1f + movw %cs, %si + shll $4, %esi +1: addl $_payload_lma, %esi + + /* Install .text16 and .data16 */ + pushl %edi + movzwl %ax, %edi + shll $4, %edi + movl $_text16_memsz, %ecx + movl %ecx, %edx + call install_block /* .text16 */ + movzwl %bx, %edi + shll $4, %edi + movl $_data16_filesz, %ecx + movl $_data16_memsz, %edx + call install_block /* .data16 */ + popl %edi + + /* Set up %ds for access to .data16 */ + movw %bx, %ds + +#ifdef KEEP_IT_REAL + /* Initialise libkir */ + movw %ax, (init_libkir_vector+2) + lcall *init_libkir_vector +#else + /* Install .text and .data to temporary area in high memory, + * prior to reading the E820 memory map and relocating + * properly. + */ + movl $_textdata_filesz, %ecx + movl $_textdata_memsz, %edx + call install_block + + /* Initialise librm at current location */ + movw %ax, (init_librm_vector+2) + lcall *init_librm_vector + + /* Call relocate() to determine target address for relocation. + * relocate() will return with %esi, %edi and %ecx set up + * ready for the copy to the new location. + */ + movw %ax, (prot_call_vector+2) + pushl $relocate + lcall *prot_call_vector + popl %edx /* discard */ + + /* Copy code to new location */ + pushl %edi + pushw %ax + movw $copy_bytes, %ax + call pm_call + popw %ax + popl %edi + + /* Initialise librm at new location */ + lcall *init_librm_vector + +#endif + /* Restore registers */ + popw %es + popw %ds + popal + ret + .size install_prealloc, . - install_prealloc + + /* Vectors for far calls to .text16 functions */ + .section ".data16", "aw", @progbits +#ifdef KEEP_IT_REAL +init_libkir_vector: + .word init_libkir + .word 0 + .size init_libkir_vector, . - init_libkir_vector +#else +init_librm_vector: + .word init_librm + .word 0 + .size init_librm_vector, . - init_librm_vector +prot_call_vector: + .word prot_call + .word 0 + .size prot_call_vector, . - prot_call_vector +#endif + +/**************************************************************************** + * uninstall (real-mode near call) + * + * Uninstall all text and data segments. + * + * Parameters: + * %ax : .text16 segment address + * %bx : .data16 segment address + * Returns: + * none + * Corrupts: + * none + **************************************************************************** + */ + .section ".text16", "ax", @progbits + .code16 + .globl uninstall +uninstall: + call free_basemem + ret + .size uninstall, . - uninstall + + + + /* File split information for the compressor */ +#if COMPRESS + .section ".zinfo", "a", @progbits + .ascii "COPY" + .long _prefix_lma + .long _prefix_filesz + .long _max_align + .ascii "PACK" + .long _text16_lma + .long _text16_filesz + .long _max_align + .ascii "PACK" + .long _data16_lma + .long _data16_filesz + .long _max_align + .ascii "PACK" + .long _textdata_lma + .long _textdata_filesz + .long _max_align +#else /* COMPRESS */ + .section ".zinfo", "a", @progbits + .ascii "COPY" + .long _prefix_lma + .long _filesz + .long _max_align +#endif /* COMPRESS */ diff --git a/gpxe/src/arch/i386/prefix/lkrnprefix.S b/gpxe/src/arch/i386/prefix/lkrnprefix.S new file mode 100644 index 0000000..094263d --- /dev/null +++ b/gpxe/src/arch/i386/prefix/lkrnprefix.S @@ -0,0 +1,214 @@ +/* + 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 a ROM image in order to allow bootloaders 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 a bootloader 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.) + + We don't require much in the way of setup code. Historically, the + Linux kernel required at least 4 sectors of setup code. + Therefore, at least 4 sectors must be present even though we don't + use them. + +*/ + +#define SETUPSECS 4 /* Minimal nr of setup-sectors */ +#define PREFIXSIZE ((SETUPSECS+1)*512) +#define PREFIXPGH (PREFIXSIZE / 16 ) +#define BOOTSEG 0x07C0 /* original address of boot-sector */ +#define INITSEG 0x9000 /* we move boot here - out of the way */ +#define SETUPSEG 0x9020 /* setup starts here */ +#define SYSSEG 0x1000 /* system loaded at 0x10000 (65536). */ + + .text + .code16 + .arch i386 + .org 0 + .section ".prefix", "ax", @progbits +/* + This is a minimal boot sector. If anyone tries to execute it (e.g., if + a .lilo file is dd'ed to a floppy), print an error message. +*/ + +bootsector: + jmp $BOOTSEG, $1f /* reload cs:ip to match relocation addr */ +1: + movw $0x2000, %di /* 0x2000 is arbitrary value >= length + of bootsect + room for stack */ + + movw $BOOTSEG, %ax + movw %ax,%ds + movw %ax,%es + + cli + movw %ax, %ss /* put stack at BOOTSEG:0x2000. */ + movw %di,%sp + sti + + movw $why_end-why, %cx + movw $why, %si + + movw $0x0007, %bx /* page 0, attribute 7 (normal) */ + movb $0x0e, %ah /* write char, tty mode */ +prloop: + lodsb + int $0x10 + loop prloop +freeze: jmp freeze + +why: .ascii "This image cannot be loaded from a floppy disk.\r\n" +why_end: + + +/* + The following header is documented in the Linux source code at + Documentation/i386/boot.txt +*/ + .org 497 +setup_sects: + .byte SETUPSECS +root_flags: + .word 0 +syssize: + .long _filesz_pgh - PREFIXPGH + + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii "SUBL" + .long syssize + .long 16 + .long 0 + .previous + +ram_size: + .word 0 +vid_mode: + .word 0 +root_dev: + .word 0 +boot_flag: + .word 0xAA55 +jump: + /* Manually specify a two-byte jmp instruction here rather + * than leaving it up to the assembler. */ + .byte 0xeb + .byte setup_code - header +header: + .byte 'H', 'd', 'r', 'S' +version: + .word 0x0207 /* 2.07 */ +realmode_swtch: + .long 0 +start_sys: + .word 0 +kernel_version: + .word 0 +type_of_loader: + .byte 0 +loadflags: + .byte 0 +setup_move_size: + .word 0 +code32_start: + .long 0 +ramdisk_image: + .long 0 +ramdisk_size: + .long 0 +bootsect_kludge: + .long 0 +heap_end_ptr: + .word 0 +pad1: + .word 0 +cmd_line_ptr: + .long 0 +initrd_addr_max: + /* We don't use an initrd but some bootloaders (e.g. SYSLINUX) have + * been known to require this field. Set the value to 2 GB. This + * value is also used by the Linux kernel. */ + .long 0x7fffffff +kernel_alignment: + .long 0 +relocatable_kernel: + .byte 0 +pad2: + .byte 0, 0, 0 +cmdline_size: + .long 0 +hardware_subarch: + .long 0 +hardware_subarch_data: + .byte 0, 0, 0, 0, 0, 0, 0, 0 + +/* + We don't need to do too much setup. + + This code gets loaded at SETUPSEG:0. It wants to start + executing the image that's loaded at SYSSEG:0 and + whose entry point is SYSSEG:0. +*/ +setup_code: + /* We expect to be contiguous in memory once loaded. The Linux image + * boot process requires that setup code is loaded separately from + * "non-real code". 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 image *would* be. + */ + ljmp $(SYSSEG-(PREFIXSIZE/16)), $run_gpxe + + + .org PREFIXSIZE +/* + We're now at the beginning of the kernel proper. + */ +run_gpxe: + /* Set up stack just below 0x7c00 */ + xorw %ax, %ax + movw %ax, %ss + movw $0x7c00, %sp + + /* Install gPXE */ + call install + + /* Set up real-mode stack */ + movw %bx, %ss + movw $_estack16, %sp + + /* Jump to .text16 segment */ + pushw %ax + pushw $1f + lret + .section ".text16", "awx", @progbits +1: + pushl $main + pushw %cs + call prot_call + popl %ecx /* discard */ + + /* Uninstall gPXE */ + call uninstall + + /* Boot next device */ + int $0x18 diff --git a/gpxe/src/arch/i386/prefix/lmelf_dprefix.S b/gpxe/src/arch/i386/prefix/lmelf_dprefix.S new file mode 100644 index 0000000..93534df --- /dev/null +++ b/gpxe/src/arch/i386/prefix/lmelf_dprefix.S @@ -0,0 +1,161 @@ +#include "elf.h" + .arch sledgehammer + .code32 + .equ FLAT_CODE_SEG,_pmcs-_gdt + .equ FLAT_DATA_SEG,_pmds-_gdt + .equ MSR_K6_EFER, 0xC0000080 + .equ EFER_LME, 0x00000100 + .equ X86_CR4_PAE, 0x00000020 + .equ CR0_PG, 0x80000000 + + .section ".prefix", "ax", @progbits + +#define LOAD_ADDR 0x10000 + + /* ELF Header */ + .globl elf_header +elf_header: +e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 +e_type: .short ET_DYN +e_machine: .short EM_X86_64 +e_version: .long 1 +e_entry: .long LOAD_ADDR + elf_start - elf_header +e_phoff: .long elf_program_header - elf_header +e_shoff: .long 0 +e_flags: .long 0 +e_ehsize: .short elf_header_end - elf_header +e_phentsize: .short ELF32_PHDR_SIZE +e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE +e_shentsize: .short 0 +e_shnum: .short 0 +e_shstrndx: .short 0 +elf_header_end: + +elf_program_header: +phdr1_p_type: .long PT_NOTE +phdr1_p_offset: .long elf_note - elf_header +phdr1_p_vaddr: .long elf_note +phdr1_p_paddr: .long elf_note +phdr1_p_filesz: .long elf_note_end - elf_note +phdr1_p_memsz: .long elf_note_end - elf_note +phdr1_p_flags: .long PF_R | PF_W | PF_X +phdr1_p_align: .long 0 + +/* The decompressor */ +phdr2_p_type: .long PT_LOAD +phdr2_p_offset: .long 0 +phdr2_p_vaddr: .long elf_header +phdr2_p_paddr: .long LOAD_ADDR +phdr2_p_filesz: .long _verbatim_size +phdr2_p_memsz: .long _image_size +phdr2_p_flags: .long PF_R | PF_W | PF_X +phdr2_p_align: .long 16 + +elf_program_header_end: + + .globl elf_note +elf_note: + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_NAME +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .asciz "Etherboot" +4: + + + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_VERSION +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .asciz VERSION +4: + +#if 0 + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_CHECKSUM +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .word 0 +4: +#endif + .balign 4 +elf_note_end: + +elf_start: + .code64 + /* Reload the gdt to something I know */ + leaq _gdt(%rip), %rax + movq %rax, 0x02 + gdtptr(%rip) + lgdt gdtptr(%rip) + + /* Enter 32bit compatibility mode */ + leaq elf_start32(%rip), %rax + movl %eax, 0x00 + elf_start32_addr(%rip) + ljmp *elf_start32_addr(%rip) + +elf_start32: + .code32 + /* Reload the data segments */ + movl $FLAT_DATA_SEG, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + + /* Disable paging */ + movl %cr0, %eax + andl $~CR0_PG, %eax + movl %eax, %cr0 + + /* Disable long mode */ + movl $MSR_K6_EFER, %ecx + rdmsr + andl $~EFER_LME, %eax + wrmsr + + /* Disable PAE */ + movl %cr4, %eax + andl $~X86_CR4_PAE, %eax + movl %eax, %cr4 + + /* Save the first argument */ + pushl %ebx + jmp _start + +gdtptr: + .word _gdt_end - _gdt -1 + .long _gdt + .long 0 +_gdt: +elf_start32_addr: + .long elf_start32 + .long FLAT_CODE_SEG +_pmcs: + /* 32 bit protected mode code segment, base 0 */ + .word 0xffff,0 + .byte 0,0x9f,0xcf,0 + +_pmds: + /* 32 bit protected mode data segment, base 0 */ + .word 0xffff,0 + .byte 0,0x93,0xcf,0 +_gdt_end: + + + /* Dummy routines to satisfy the build */ + .section ".text16", "ax", @progbits +prefix_exit: + +prefix_exit_end: + .previous diff --git a/gpxe/src/arch/i386/prefix/lmelf_prefix.S b/gpxe/src/arch/i386/prefix/lmelf_prefix.S new file mode 100644 index 0000000..3c1e725 --- /dev/null +++ b/gpxe/src/arch/i386/prefix/lmelf_prefix.S @@ -0,0 +1,161 @@ +#include "elf.h" + .arch sledgehammer + .code32 + .equ FLAT_CODE_SEG,_pmcs-_gdt + .equ FLAT_DATA_SEG,_pmds-_gdt + .equ MSR_K6_EFER, 0xC0000080 + .equ EFER_LME, 0x00000100 + .equ X86_CR4_PAE, 0x00000020 + .equ CR0_PG, 0x80000000 + + .section ".prefix", "ax", @progbits + +#define LOAD_ADDR 0x10000 + + /* ELF Header */ + .globl elf_header +elf_header: +e_ident: .byte 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 +e_type: .short ET_EXEC +e_machine: .short EM_X86_64 +e_version: .long 1 +e_entry: .long LOAD_ADDR + elf_start - elf_header +e_phoff: .long elf_program_header - elf_header +e_shoff: .long 0 +e_flags: .long 0 +e_ehsize: .short elf_header_end - elf_header +e_phentsize: .short ELF32_PHDR_SIZE +e_phnum: .short (elf_program_header_end - elf_program_header)/ELF32_PHDR_SIZE +e_shentsize: .short 0 +e_shnum: .short 0 +e_shstrndx: .short 0 +elf_header_end: + +elf_program_header: +phdr1_p_type: .long PT_NOTE +phdr1_p_offset: .long elf_note - elf_header +phdr1_p_vaddr: .long elf_note +phdr1_p_paddr: .long elf_note +phdr1_p_filesz: .long elf_note_end - elf_note +phdr1_p_memsz: .long elf_note_end - elf_note +phdr1_p_flags: .long PF_R | PF_W | PF_X +phdr1_p_align: .long 0 + +/* The decompressor */ +phdr2_p_type: .long PT_LOAD +phdr2_p_offset: .long 0 +phdr2_p_vaddr: .long elf_header +phdr2_p_paddr: .long LOAD_ADDR +phdr2_p_filesz: .long _verbatim_size +phdr2_p_memsz: .long _image_size +phdr2_p_flags: .long PF_R | PF_W | PF_X +phdr2_p_align: .long 16 + +elf_program_header_end: + + .globl elf_note +elf_note: + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_NAME +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .asciz "Etherboot" +4: + + + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_VERSION +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .asciz VERSION +4: + +#if 0 + .balign 4 + .int 2f - 1f + .int 4f - 3f + .int EIN_PROGRAM_CHECKSUM +1: .asciz "ELFBoot" +2: + .balign 4 +3: + .word 0 +4: +#endif + .balign 4 +elf_note_end: + +elf_start: + .code64 + /* Reload the gdt to something I know */ + leaq _gdt(%rip), %rax + movq %rax, 0x02 + gdtptr(%rip) + lgdt gdtptr(%rip) + + /* Enter 32bit compatibility mode */ + leaq elf_start32(%rip), %rax + movl %eax, 0x00 + elf_start32_addr(%rip) + ljmp *elf_start32_addr(%rip) + +elf_start32: + .code32 + /* Reload the data segments */ + movl $FLAT_DATA_SEG, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + + /* Disable paging */ + movl %cr0, %eax + andl $~CR0_PG, %eax + movl %eax, %cr0 + + /* Disable long mode */ + movl $MSR_K6_EFER, %ecx + rdmsr + andl $~EFER_LME, %eax + wrmsr + + /* Disable PAE */ + movl %cr4, %eax + andl $~X86_CR4_PAE, %eax + movl %eax, %cr4 + + /* Save the first argument */ + pushl %ebx + jmp _start + +gdtptr: + .word _gdt_end - _gdt -1 + .long _gdt + .long 0 +_gdt: +elf_start32_addr: + .long elf_start32 + .long FLAT_CODE_SEG +_pmcs: + /* 32 bit protected mode code segment, base 0 */ + .word 0xffff,0 + .byte 0,0x9f,0xcf,0 + +_pmds: + /* 32 bit protected mode data segment, base 0 */ + .word 0xffff,0 + .byte 0,0x93,0xcf,0 +_gdt_end: + + + /* Dummy routines to satisfy the build */ + .section ".text16", "ax", @progbits +prefix_exit: + +prefix_exit_end: + .previous diff --git a/gpxe/src/arch/i386/prefix/mbr.S b/gpxe/src/arch/i386/prefix/mbr.S new file mode 100644 index 0000000..adfe204 --- /dev/null +++ b/gpxe/src/arch/i386/prefix/mbr.S @@ -0,0 +1,13 @@ + .text + .arch i386 + .section ".prefix", "awx", @progbits + .code16 + .org 0 + +mbr: + movw $exec_sector, %bp + jmp find_active_partition +exec_sector: + ljmp $0x0000, $0x7c00 + +#include "bootpart.S" diff --git a/gpxe/src/arch/i386/prefix/nbiprefix.S b/gpxe/src/arch/i386/prefix/nbiprefix.S new file mode 100644 index 0000000..4fb4acb --- /dev/null +++ b/gpxe/src/arch/i386/prefix/nbiprefix.S @@ -0,0 +1,77 @@ + .text + .arch i386 + .code16 + .section ".prefix", "ax", @progbits + .org 0 + +nbi_header: + +/***************************************************************************** + * NBI file header + ***************************************************************************** + */ +file_header: + .long 0x1b031336 /* Signature */ + .byte 0x04 /* 16 bytes header, no vendor info */ + .byte 0 + .byte 0 + .byte 0 /* No flags */ + .word 0x0000, 0x07c0 /* Load header to 0x07c0:0x0000 */ + .word entry, 0x07c0 /* Start execution at 0x07c0:entry */ + .size file_header, . - file_header + +/***************************************************************************** + * NBI segment header + ***************************************************************************** + */ +segment_header: + .byte 0x04 /* 16 bytes header, no vendor info */ + .byte 0 + .byte 0 + .byte 0x04 /* Last segment */ + .long 0x00007e00 +imglen: .long _filesz - 512 +memlen: .long _filesz - 512 + .size segment_header, . - segment_header + + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii "SUBL" + .long imglen + .long 1 + .long 0 + .ascii "SUBL" + .long memlen + .long 1 + .long 0 + .previous + +/***************************************************************************** + * NBI entry point + ***************************************************************************** + */ +entry: + /* Install gPXE */ + call install + + /* Jump to .text16 segment */ + pushw %ax + pushw $1f + lret + .section ".text16", "awx", @progbits +1: + pushl $main + pushw %cs + call prot_call + popl %ecx /* discard */ + + /* Uninstall gPXE */ + call uninstall + + /* Reboot system */ + int $0x19 + + .previous + .size entry, . - entry + +nbi_header_end: + .org 512 diff --git a/gpxe/src/arch/i386/prefix/nullprefix.S b/gpxe/src/arch/i386/prefix/nullprefix.S new file mode 100644 index 0000000..032d41e --- /dev/null +++ b/gpxe/src/arch/i386/prefix/nullprefix.S @@ -0,0 +1,13 @@ + .org 0 + .text + .arch i386 + + .section ".prefix", "ax", @progbits + .code16 +_prefix: + + .section ".text16", "ax", @progbits +prefix_exit: + +prefix_exit_end: + .previous diff --git a/gpxe/src/arch/i386/prefix/pxeprefix.S b/gpxe/src/arch/i386/prefix/pxeprefix.S new file mode 100644 index 0000000..b3b7947 --- /dev/null +++ b/gpxe/src/arch/i386/prefix/pxeprefix.S @@ -0,0 +1,701 @@ +#define PXENV_UNDI_SHUTDOWN 0x0005 +#define PXENV_UNDI_GET_NIC_TYPE 0x0012 +#define PXENV_STOP_UNDI 0x0015 +#define PXENV_UNLOAD_STACK 0x0070 + + .text + .arch i386 + .org 0 + .code16 + +#include + +#define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) ) + +/***************************************************************************** + * Entry point: set operating context, print welcome message + ***************************************************************************** + */ + .section ".prefix", "ax", @progbits + jmp $0x7c0, $1f +1: + /* Preserve registers for possible return to PXE */ + pushfl + pushal + pushw %gs + pushw %fs + pushw %es + pushw %ds + + /* Store magic word on PXE stack and remember PXE %ss:esp */ + pushl $STACK_MAGIC + movw %ss, %cs:pxe_ss + movl %esp, %cs:pxe_esp + + /* Set up segments */ + movw %cs, %ax + movw %ax, %ds + movw $0x40, %ax /* BIOS data segment access */ + movw %ax, %fs + /* Set up stack just below 0x7c00 */ + xorw %ax, %ax + movw %ax, %ss + movl $0x7c00, %esp + /* Clear direction flag, for the sake of sanity */ + cld + /* Print welcome message */ + movw $10f, %si + xorw %di, %di + call print_message + .section ".prefix.data", "aw", @progbits +10: .asciz "PXE->EB:" + .previous + +/***************************************************************************** + * Find us a usable !PXE or PXENV+ entry point + ***************************************************************************** + */ +detect_pxe: + /* Plan A: !PXE pointer from the stack */ + lgsl pxe_esp, %ebp /* %gs:%bp -> original stack */ + lesw %gs:52(%bp), %bx + call is_valid_ppxe + je have_ppxe + + /* Plan B: PXENV+ pointer from initial ES:BX */ + movw %gs:32(%bp),%bx + movw %gs:8(%bp),%es + call is_valid_pxenv + je have_pxenv + + /* Plan C: PXENV+ structure via INT 1Ah */ + movw $0x5650, %ax + int $0x1a + jc 1f + cmpw $0x564e, %ax + jne 1f + call is_valid_pxenv + je have_pxenv +1: + /* Plan D: scan base memory for !PXE */ + call memory_scan_ppxe + je have_ppxe + + /* Plan E: scan base memory for PXENV+ */ + call memory_scan_pxenv + jne stack_not_found + +have_pxenv: + movw %bx, pxenv_offset + movw %es, pxenv_segment + + cmpw $0x201, %es:6(%bx) /* API version >= 2.01 */ + jb 1f + cmpb $0x2c, %es:8(%bx) /* ... and structure long enough */ + jb 2f + + lesw %es:0x28(%bx), %bx /* Find !PXE from PXENV+ */ + call is_valid_ppxe + je have_ppxe +2: + call memory_scan_ppxe /* We are *supposed* to have !PXE... */ + je have_ppxe +1: + lesw pxenv_segoff, %bx /* Nope, we're stuck with PXENV+ */ + + /* Record entry point and UNDI segments */ + pushl %es:0x0a(%bx) /* Entry point */ + popl entry_segoff + pushw %es:0x24(%bx) /* UNDI code segment */ + pushw %es:0x26(%bx) /* UNDI code size */ + popl undi_code_segoff + pushw %es:0x20(%bx) /* UNDI data segment */ + pushw %es:0x22(%bx) /* UNDI data size */ + popl undi_data_segoff + + /* Print "PXENV+ at
    " */ + movw $10f, %si + call print_message + call print_segoff + movb $( ',' ), %al + call print_character + jmp check_have_stack + .section ".prefix.data", "aw", @progbits +10: .asciz " PXENV+ at " + .previous + +have_ppxe: + movw %bx, ppxe_offset + movw %es, ppxe_segment + + pushl %es:0x10(%bx) /* Entry point */ + popl entry_segoff + pushw %es:0x30(%bx) /* UNDI code segment */ + pushw %es:0x36(%bx) /* UNDI code size */ + popl undi_code_segoff + pushw %es:0x28(%bx) /* UNDI data segment */ + pushw %es:0x2e(%bx) /* UNDI data size */ + popl undi_data_segoff + /* Print "!PXE at
    " */ + movw $10f, %si + call print_message + call print_segoff + movb $( ',' ), %al + call print_character + jmp check_have_stack + .section ".prefix.data", "aw", @progbits +10: .asciz " !PXE at " + .previous + +is_valid_ppxe: + cmpl $0x45585021, %es:(%bx) + jne 1f + movzbw %es:4(%bx), %cx + cmpw $0x58, %cx + jae is_valid_checksum +1: + ret + +is_valid_pxenv: + cmpl $0x4e455850, %es:(%bx) + jne 1b + cmpw $0x2b56, %es:4(%bx) + jne 1b + movzbw %es:8(%bx), %cx + cmpw $0x28, %cx + jb 1b + +is_valid_checksum: + pushw %ax + movw %bx, %si + xorw %ax, %ax +2: + es lodsb + addb %al, %ah + loopw 2b + popw %ax + ret + +memory_scan_ppxe: + movw $is_valid_ppxe, %dx + jmp memory_scan_common + +memory_scan_pxenv: + movw $is_valid_pxenv, %dx + +memory_scan_common: + movw %fs:(0x13), %ax + shlw $6, %ax + decw %ax +1: incw %ax + cmpw $( 0xa000 - 1 ), %ax + ja 2f + movw %ax, %es + xorw %bx, %bx + call *%dx + jne 1b +2: ret + +/***************************************************************************** + * Sanity check: we must have an entry point + ***************************************************************************** + */ +check_have_stack: + /* Check for entry point */ + movl entry_segoff, %eax + testl %eax, %eax + jnz 99f + /* No entry point: print message and skip everything else */ +stack_not_found: + movw $10f, %si + call print_message + jmp finished + .section ".prefix.data", "aw", @progbits +10: .asciz " No PXE stack found!\n" + .previous +99: + +/***************************************************************************** + * Calculate base memory usage by UNDI + ***************************************************************************** + */ +find_undi_basemem_usage: + movw undi_code_segment, %ax + movw undi_code_size, %bx + movw undi_data_segment, %cx + movw undi_data_size, %dx + cmpw %ax, %cx + ja 1f + xchgw %ax, %cx + xchgw %bx, %dx +1: /* %ax:%bx now describes the lower region, %cx:%dx the higher */ + shrw $6, %ax /* Round down to nearest kB */ + movw %ax, undi_fbms_start + addw $0x0f, %dx /* Round up to next segment */ + shrw $4, %dx + addw %dx, %cx + addw $((1024 / 16) - 1), %cx /* Round up to next kB */ + shrw $6, %cx + movw %cx, undi_fbms_end + +/***************************************************************************** + * Print information about detected PXE stack + ***************************************************************************** + */ +print_structure_information: + /* Print entry point */ + movw $10f, %si + call print_message + les entry_segoff, %bx + call print_segoff + .section ".prefix.data", "aw", @progbits +10: .asciz " entry point at " + .previous + /* Print UNDI code segment */ + movw $10f, %si + call print_message + les undi_code_segoff, %bx + call print_segoff + .section ".prefix.data", "aw", @progbits +10: .asciz "\n UNDI code segment " + .previous + /* Print UNDI data segment */ + movw $10f, %si + call print_message + les undi_data_segoff, %bx + call print_segoff + .section ".prefix.data", "aw", @progbits +10: .asciz ", data segment " + .previous + /* Print UNDI memory usage */ + movw $10f, %si + call print_message + movw undi_fbms_start, %ax + call print_word + movb $( '-' ), %al + call print_character + movw undi_fbms_end, %ax + call print_word + movw $20f, %si + call print_message + .section ".prefix.data", "aw", @progbits +10: .asciz " (" +20: .asciz "kB)\n" + .previous + +/***************************************************************************** + * Determine physical device + ***************************************************************************** + */ +get_physical_device: + /* Issue PXENV_UNDI_GET_NIC_TYPE */ + movw $PXENV_UNDI_GET_NIC_TYPE, %bx + call pxe_call + jnc 1f + call print_pxe_error + jmp no_physical_device +1: /* Determine physical device type */ + movb ( pxe_parameter_structure + 0x02 ), %al + cmpb $2, %al + je pci_physical_device + jmp no_physical_device + +pci_physical_device: + /* Record PCI bus:dev.fn and vendor/device IDs */ + movl ( pxe_parameter_structure + 0x03 ), %eax + movl %eax, pci_vendor + movw ( pxe_parameter_structure + 0x0b ), %ax + movw %ax, pci_busdevfn + movw $10f, %si + call print_message + call print_pci_busdevfn + movb $0x0a, %al + call print_character + jmp 99f + .section ".prefix.data", "aw", @progbits +10: .asciz " UNDI device is PCI " + .previous + +no_physical_device: + /* No device found, or device type not understood */ + movw $10f, %si + call print_message + .section ".prefix.data", "aw", @progbits +10: .asciz " Unable to determine UNDI physical device\n" + .previous + +99: + +/***************************************************************************** + * Leave NIC in a safe state + ***************************************************************************** + */ +#ifndef PXELOADER_KEEP_PXE +shutdown_nic: + /* Issue PXENV_UNDI_SHUTDOWN */ + movw $PXENV_UNDI_SHUTDOWN, %bx + call pxe_call + jnc 1f + call print_pxe_error +1: +unload_base_code: + /* Issue PXENV_UNLOAD_STACK */ + movw $PXENV_UNLOAD_STACK, %bx + call pxe_call + jnc 1f + call print_pxe_error + jmp 99f +1: /* Free base memory used by PXE base code */ + movw undi_fbms_start, %ax + movw %fs:(0x13), %bx + call free_basemem +99: + andw $~( UNDI_FL_INITIALIZED | UNDI_FL_KEEP_ALL ), flags +#endif /* PXELOADER_KEEP_PXE */ + +/***************************************************************************** + * Unload UNDI driver + ***************************************************************************** + */ +#ifndef PXELOADER_KEEP_UNDI +unload_undi: + /* Issue PXENV_STOP_UNDI */ + movw $PXENV_STOP_UNDI, %bx + call pxe_call + jnc 1f + call print_pxe_error + jmp 99f +1: /* Free base memory used by UNDI */ + movw undi_fbms_end, %ax + movw undi_fbms_start, %bx + call free_basemem + /* Clear UNDI_FL_STARTED */ + andw $~UNDI_FL_STARTED, flags +99: +#endif /* PXELOADER_KEEP_UNDI */ + +/***************************************************************************** + * Print remaining free base memory + ***************************************************************************** + */ +print_free_basemem: + movw $10f, %si + call print_message + movw %fs:(0x13), %ax + call print_word + movw $20f, %si + call print_message + .section ".prefix.data", "aw", @progbits +10: .asciz " " +20: .asciz "kB free base memory after PXE unload\n" + .previous + +/***************************************************************************** + * Exit point + ***************************************************************************** + */ +finished: + jmp run_gpxe + +/***************************************************************************** + * Subroutine: print segment:offset address + * + * Parameters: + * %es:%bx : segment:offset address to print + * %ds:di : output buffer (or %di=0 to print to console) + * Returns: + * %ds:di : next character in output buffer (if applicable) + ***************************************************************************** + */ +print_segoff: + /* Preserve registers */ + pushw %ax + /* Print ":offset" */ + movw %es, %ax + call print_hex_word + movb $( ':' ), %al + call print_character + movw %bx, %ax + call print_hex_word + /* Restore registers and return */ + popw %ax + ret + +/***************************************************************************** + * Subroutine: print decimal word + * + * Parameters: + * %ax : word to print + * %ds:di : output buffer (or %di=0 to print to console) + * Returns: + * %ds:di : next character in output buffer (if applicable) + ***************************************************************************** + */ +print_word: + /* Preserve registers */ + pushw %ax + pushw %bx + pushw %cx + pushw %dx + /* Build up digit sequence on stack */ + movw $10, %bx + xorw %cx, %cx +1: xorw %dx, %dx + divw %bx, %ax + pushw %dx + incw %cx + testw %ax, %ax + jnz 1b + /* Print digit sequence */ +1: popw %ax + call print_hex_nibble + loop 1b + /* Restore registers and return */ + popw %dx + popw %cx + popw %bx + popw %ax + ret + +/***************************************************************************** + * Subroutine: zero 1kB block of base memory + * + * Parameters: + * %bx : block to zero (in kB) + * Returns: + * Nothing + ***************************************************************************** + */ +zero_kb: + /* Preserve registers */ + pushw %ax + pushw %cx + pushw %di + pushw %es + /* Zero block */ + movw %bx, %ax + shlw $6, %ax + movw %ax, %es + movw $0x400, %cx + xorw %di, %di + xorw %ax, %ax + rep stosb + /* Restore registers and return */ + popw %es + popw %di + popw %cx + popw %ax + ret + +/***************************************************************************** + * Subroutine: free and zero base memory + * + * Parameters: + * %ax : Desired new free base memory counter (in kB) + * %bx : Expected current free base memory counter (in kB) + * %fs : BIOS data segment (0x40) + * Returns: + * None + * + * The base memory from %bx kB to %ax kB is unconditionally zeroed. + * It will be freed if and only if the expected current free base + * memory counter (%bx) matches the actual current free base memory + * counter in 0x40:0x13; if this does not match then the memory will + * be leaked. + ***************************************************************************** + */ +free_basemem: + /* Zero base memory */ + pushw %bx +1: cmpw %bx, %ax + je 2f + call zero_kb + incw %bx + jmp 1b +2: popw %bx + /* Free base memory */ + cmpw %fs:(0x13), %bx /* Update FBMS only if "old" value */ + jne 1f /* is correct */ +1: movw %ax, %fs:(0x13) + ret + +/***************************************************************************** + * Subroutine: make a PXE API call. Works with either !PXE or PXENV+ API. + * + * Parameters: + * %bx : PXE API call number + * %ds:pxe_parameter_structure : Parameters for PXE API call + * Returns: + * %ax : PXE status code (not exit code) + * CF set if %ax is non-zero + ***************************************************************************** + */ +pxe_call: + /* Preserve registers */ + pushw %di + pushw %es + /* Set up registers for PXENV+ API. %bx already set up */ + pushw %ds + popw %es + movw $pxe_parameter_structure, %di + /* Set up stack for !PXE API */ + pushw %es + pushw %di + pushw %bx + /* Make the API call */ + lcall *entry_segoff + /* Reset the stack */ + addw $6, %sp + movw pxe_parameter_structure, %ax + clc + testw %ax, %ax + jz 1f + stc +1: /* Restore registers and return */ + popw %es + popw %di + ret + +/***************************************************************************** + * Subroutine: print PXE API call error message + * + * Parameters: + * %ax : PXE status code + * %bx : PXE API call number + * Returns: + * Nothing + ***************************************************************************** + */ +print_pxe_error: + pushw %si + movw $10f, %si + call print_message + xchgw %ax, %bx + call print_hex_word + movw $20f, %si + call print_message + xchgw %ax, %bx + call print_hex_word + movw $30f, %si + call print_message + popw %si + ret + .section ".prefix.data", "aw", @progbits +10: .asciz " UNDI API call " +20: .asciz " failed: status code " +30: .asciz "\n" + .previous + +/***************************************************************************** + * PXE data structures + ***************************************************************************** + */ + .section ".prefix.data" + +pxe_esp: .long 0 +pxe_ss: .word 0 + +pxe_parameter_structure: .fill 20 + +undi_code_segoff: +undi_code_size: .word 0 +undi_code_segment: .word 0 + +undi_data_segoff: +undi_data_size: .word 0 +undi_data_segment: .word 0 + +/* The following fields are part of a struct undi_device */ + +undi_device: + +pxenv_segoff: +pxenv_offset: .word 0 +pxenv_segment: .word 0 + +ppxe_segoff: +ppxe_offset: .word 0 +ppxe_segment: .word 0 + +entry_segoff: +entry_offset: .word 0 +entry_segment: .word 0 + +undi_fbms_start: .word 0 +undi_fbms_end: .word 0 + +pci_busdevfn: .word UNDI_NO_PCI_BUSDEVFN +isapnp_csn: .word UNDI_NO_ISAPNP_CSN +isapnp_read_port: .word UNDI_NO_ISAPNP_READ_PORT + +pci_vendor: .word 0 +pci_device: .word 0 +flags: + .word ( UNDI_FL_INITIALIZED | UNDI_FL_STARTED | UNDI_FL_KEEP_ALL ) + + .equ undi_device_size, ( . - undi_device ) + +/***************************************************************************** + * Run gPXE main code + ***************************************************************************** + */ + .section ".prefix" +run_gpxe: + /* Install gPXE */ + call install + + /* Set up real-mode stack */ + movw %bx, %ss + movw $_estack16, %sp + +#ifdef PXELOADER_KEEP_UNDI + /* Copy our undi_device structure to the preloaded_undi variable */ + movw %bx, %es + movw $preloaded_undi, %di + movw $undi_device, %si + movw $undi_device_size, %cx + rep movsb +#endif + + /* Retrieve PXE %ss:esp */ + movw pxe_ss, %di + movl pxe_esp, %ebp + + /* Jump to .text16 segment with %ds pointing to .data16 */ + movw %bx, %ds + pushw %ax + pushw $1f + lret + .section ".text16", "ax", @progbits +1: + /* Run main program */ + pushl $main + pushw %cs + call prot_call + popl %ecx /* discard */ + + /* Uninstall gPXE */ + call uninstall + + /* Restore PXE stack */ + movw %di, %ss + movl %ebp, %esp + + /* Check PXE stack magic */ + popl %eax + cmpl $STACK_MAGIC, %eax + jne 1f + + /* PXE stack OK: return to caller */ + popw %ds + popw %es + popw %fs + popw %gs + popal + popfl + xorw %ax, %ax /* Return success */ + lret + +1: /* PXE stack corrupt or removed: use INT 18 */ + int $0x18 + .previous diff --git a/gpxe/src/arch/i386/prefix/romprefix.S b/gpxe/src/arch/i386/prefix/romprefix.S new file mode 100644 index 0000000..7d53237 --- /dev/null +++ b/gpxe/src/arch/i386/prefix/romprefix.S @@ -0,0 +1,725 @@ +/* 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. + */ + +#include + +#define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) ) +#define PMM_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'M' << 16 ) + ( 'M' << 24 ) ) +#define PCI_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( ' ' << 24 ) ) +#define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) ) +#define PNP_GET_BBS_VERSION 0x60 +#define PMM_ALLOCATE 0x0000 +#define PMM_DEALLOCATE 0x0002 + +/* ROM banner timeout. Based on the configurable BANNER_TIMEOUT in + * config.h, but converted to a number of (18Hz) timer ticks, and + * doubled to allow for BIOSes that switch video modes immediately + * beforehand, so rendering the message almost invisible to the user. + */ +#define ROM_BANNER_TIMEOUT ( 2 * ( 18 * BANNER_TIMEOUT ) / 10 ) + + .text + .code16 + .arch i386 + .section ".prefix", "ax", @progbits + + .org 0x00 +romheader: + .word 0xAA55 /* BIOS extension signature */ +romheader_size: .byte _filesz_sect /* Size in 512-byte blocks */ + jmp init /* Initialisation vector */ +checksum: + .byte 0 + .org 0x16 + .word undiheader + .org 0x18 + .word pciheader + .org 0x1a + .word pnpheader + .size romheader, . - romheader + + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii "SUBB" + .long romheader_size + .long 512 + .long 0 + .previous + +pciheader: + .ascii "PCIR" /* Signature */ + .word pci_vendor_id /* Vendor identification */ + .word pci_device_id /* Device identification */ + .word 0x0000 /* Device list pointer */ + .word pciheader_len /* PCI data structure length */ + .byte 0x03 /* PCI data structure revision */ + .byte 0x02, 0x00, 0x00 /* Class code */ +pciheader_image_length: + .word _filesz_sect /* Image length */ + .word 0x0001 /* Revision level */ + .byte 0x00 /* Code type */ + .byte 0x80 /* Last image indicator */ +pciheader_runtime_length: + .word _filesz_sect /* Maximum run-time image length */ + .word 0x0000 /* Configuration utility code header */ + .word 0x0000 /* DMTF CLP entry point */ + .equ pciheader_len, . - pciheader + .size pciheader, . - pciheader + + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ + .ascii "SUBW" + .long pciheader_image_length + .long 512 + .long 0 + .ascii "SUBW" + .long pciheader_runtime_length + .long 512 + .long 0 + .previous + +pnpheader: + .ascii "$PnP" /* Signature */ + .byte 0x01 /* Structure revision */ + .byte ( pnpheader_len / 16 ) /* Length (in 16 byte increments) */ + .word 0x0000 /* Offset of next header */ + .byte 0x00 /* Reserved */ + .byte 0x00 /* Checksum */ + .long 0x00000000 /* Device identifier */ + .word mfgstr /* Manufacturer string */ + .word prodstr /* Product name */ + .byte 0x02 /* Device base type code */ + .byte 0x00 /* Device sub-type code */ + .byte 0x00 /* Device interface type code */ + .byte 0xf4 /* Device indicator */ + .word 0x0000 /* Boot connection vector */ + .word 0x0000 /* Disconnect vector */ + .word bev_entry /* Boot execution vector */ + .word 0x0000 /* Reserved */ + .word 0x0000 /* Static resource information vector*/ + .equ pnpheader_len, . - pnpheader + .size pnpheader, . - pnpheader + +/* Manufacturer string */ +mfgstr: + .asciz "http://etherboot.org" + .size mfgstr, . - mfgstr + +/* Product string + * + * Defaults to PRODUCT_SHORT_NAME. If the ROM image is writable at + * initialisation time, it will be filled in to include the PCI + * bus:dev.fn number of the card as well. + */ +prodstr: + .ascii PRODUCT_SHORT_NAME +prodstr_separator: + .byte 0 + .ascii "(PCI " +prodstr_pci_id: + .asciz "xx:xx.x)" /* Filled in by init code */ + .size prodstr, . - prodstr + + .globl undiheader +undiheader: + .ascii "UNDI" /* Signature */ + .byte undiheader_len /* Length of structure */ + .byte 0 /* Checksum */ + .byte 0 /* Structure revision */ + .byte 0,1,2 /* PXE version: 2.1.0 */ + .word undiloader /* Offset to loader routine */ + .word _data16_memsz /* Stack segment size */ + .word _data16_memsz /* Data segment size */ + .word _text16_memsz /* Code segment size */ + .ascii "PCIR" /* Bus type */ + .equ undiheader_len, . - undiheader + .size undiheader, . - undiheader + +/* Initialisation (called once during POST) + * + * Determine whether or not this is a PnP system via a signature + * check. If it is PnP, return to the PnP BIOS indicating that we are + * a boot-capable device; the BIOS will call our boot execution vector + * if it wants to boot us. If it is not PnP, hook INT 19. + */ +init: + /* Preserve registers, clear direction flag, set %ds=%cs */ + pushaw + pushw %ds + pushw %es + pushw %fs + pushw %gs + cld + pushw %cs + popw %ds + + /* Shuffle some registers around. We need %di available for + * the print_xxx functions, and in a register that's + * addressable from %es, so shuffle as follows: + * + * %di (pointer to PnP structure) => %bx + * %bx (runtime segment address, for PCI 3.0) => %gs + */ + movw %bx, %gs + movw %di, %bx + + /* Print message as early as possible */ + movw $init_message, %si + xorw %di, %di + call print_message + call print_pci_busdevfn + + /* Fill in product name string, if possible */ + movw $prodstr_pci_id, %di + call print_pci_busdevfn + movb $( ' ' ), prodstr_separator + + /* Print segment address */ + movb $( ' ' ), %al + xorw %di, %di + call print_character + movw %cs, %ax + call print_hex_word + + /* Check for PCI BIOS version */ + pushl %ebx + pushl %edx + pushl %edi + stc + movw $0xb101, %ax + int $0x1a + jc no_pci3 + cmpl $PCI_SIGNATURE, %edx + jne no_pci3 + testb %ah, %ah + jnz no_pci3 + movw $init_message_pci, %si + xorw %di, %di + call print_message + movb %bh, %al + call print_hex_nibble + movb $( '.' ), %al + call print_character + movb %bl, %al + call print_hex_byte + cmpb $3, %bh + jb no_pci3 + /* PCI >=3.0: leave %gs as-is if sane */ + movw %gs, %ax + cmpw $0xa000, %ax /* Insane if %gs < 0xa000 */ + jb pci3_insane + movw %cs, %bx /* Sane if %cs == %gs */ + cmpw %bx, %ax + je 1f + movzbw romheader_size, %cx /* Sane if %cs+len <= %gs */ + shlw $5, %cx + addw %cx, %bx + cmpw %bx, %ax + jae 1f + movw %cs, %bx /* Sane if %gs+len <= %cs */ + addw %cx, %ax + cmpw %bx, %ax + jbe 1f +pci3_insane: /* PCI 3.0 with insane %gs value: print error and ignore %gs */ + movb $( '!' ), %al + call print_character + movw %gs, %ax + call print_hex_word +no_pci3: + /* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */ + pushw %cs + popw %gs +1: popl %edi + popl %edx + popl %ebx + + /* Check for PnP BIOS */ + testw $0x0f, %bx /* PnP signature must be aligned - bochs */ + jnz no_bbs /* uses unalignment to indicate 'fake' PnP. */ + cmpl $PNP_SIGNATURE, %es:0(%bx) + jne no_bbs + /* Is PnP: print PnP message */ + movw $init_message_pnp, %si + xorw %di, %di + call print_message + /* Check for BBS */ + pushw %es:0x1b(%bx) /* Real-mode data segment */ + pushw %ds /* &(bbs_version) */ + pushw $bbs_version + pushw $PNP_GET_BBS_VERSION + lcall *%es:0xd(%bx) + addw $8, %sp + testw %ax, %ax + je got_bbs +no_bbs: /* Not BBS-compliant - must hook INT 19 */ + movw $init_message_int19, %si + xorw %di, %di + call print_message + xorw %ax, %ax + movw %ax, %es + pushl %es:( 0x19 * 4 ) + popl orig_int19 + pushw %gs /* %gs contains runtime %cs */ + pushw $int19_entry + popl %es:( 0x19 * 4 ) + jmp bbs_done +got_bbs: /* BBS compliant - no need to hook INT 19 */ + movw $init_message_bbs, %si + xorw %di, %di + call print_message +bbs_done: + + /* Check for PMM */ + movw $( 0xe000 - 1 ), %bx +pmm_scan: + incw %bx + jz no_pmm + movw %bx, %es + cmpl $PMM_SIGNATURE, %es:0 + jne pmm_scan + xorw %dx, %dx + xorw %si, %si + movzbw %es:5, %cx +1: es lodsb + addb %al, %dl + loop 1b + jnz pmm_scan + /* PMM found: print PMM message */ + movw $init_message_pmm, %si + xorw %di, %di + call print_message + /* We have PMM and so a 1kB stack: preserve upper register halves */ + pushal + /* Calculate required allocation size in %esi */ + movzbl romheader_size, %eax + shll $9, %eax + addl $_textdata_memsz, %eax + orw $0xffff, %ax /* Ensure allocation size is at least 64kB */ + bsrl %eax, %ecx + subw $15, %cx /* Round up and convert to 64kB count */ + movw $1, %si + shlw %cl, %si +pmm_loop: + /* Try to allocate block via PMM */ + pushw $0x0006 /* Aligned, extended memory */ + pushl $0xffffffff /* No handle */ + movzwl %si, %eax + shll $12, %eax + pushl %eax /* Allocation size in paragraphs */ + pushw $PMM_ALLOCATE + lcall *%es:7 + addw $12, %sp + /* Abort if allocation fails */ + testw %dx, %dx /* %ax==0 even on success, since align>=64kB */ + jz pmm_fail + /* If block has A20==1, free block and try again with twice + * the allocation size (and hence alignment). + */ + testw $0x0010, %dx + jz got_pmm + pushw %dx + pushw $0 + pushw $PMM_DEALLOCATE + lcall *%es:7 + addw $6, %sp + addw %si, %si + jmp pmm_loop +got_pmm: /* PMM allocation succeeded */ + movw %dx, ( image_source + 2 ) + movw %dx, %ax + xorw %di, %di + call print_hex_word + movb $( '@' ), %al + call print_character + movw %si, %ax + call print_hex_byte + /* Copy ROM to PMM block */ + xorw %ax, %ax + movw %ax, %es + movl image_source, %edi + xorl %esi, %esi + movzbl romheader_size, %ecx + shll $9, %ecx + addr32 rep movsb /* PMM presence implies flat real mode */ + movl %edi, decompress_to + /* Shrink ROM */ + movb $_prefix_memsz_sect, romheader_size +pmm_fail: + /* Restore upper register halves */ + popal +no_pmm: + + /* Update checksum */ + xorw %bx, %bx + xorw %si, %si + movzbw romheader_size, %cx + shlw $9, %cx +1: lodsb + addb %al, %bl + loop 1b + subb %bl, checksum + + /* Copy self to option ROM space. Required for PCI3.0, which + * loads us to a temporary location in low memory. Will be a + * no-op for lower PCI versions. + */ + movb $( ' ' ), %al + xorw %di, %di + call print_character + movw %gs, %ax + call print_hex_word + movzbw romheader_size, %cx + shlw $9, %cx + movw %ax, %es + xorw %si, %si + xorw %di, %di + cs rep movsb + + /* Prompt for POST-time shell */ + movw $init_message_prompt, %si + xorw %di, %di + call print_message + movw $prodstr, %si + call print_message + movw $init_message_dots, %si + call print_message + /* Wait for Ctrl-B */ + movw $0xff02, %bx + call wait_for_key + /* Clear prompt */ + pushf + xorw %di, %di + call print_kill_line + movw $init_message_done, %si + call print_message + popf + jnz 2f + /* Ctrl-B was pressed: invoke gPXE. The keypress will be + * picked up by the initial shell prompt, and we will drop + * into a shell. + */ + pushw %cs + call exec +2: + /* Restore registers */ + popw %gs + popw %fs + popw %es + popw %ds + popaw + + /* Indicate boot capability to PnP BIOS, if present */ + movw $0x20, %ax + lret + .size init, . - init + +/* + * Note to hardware vendors: + * + * If you wish to brand this boot ROM, please do so by defining the + * strings PRODUCT_NAME and PRODUCT_SHORT_NAME in config/general.h. + * + * While nothing in the GPL prevents you from removing all references + * to gPXE or http://etherboot.org, we prefer you not to do so. + * + * If you have an OEM-mandated branding requirement that cannot be + * satisfied simply by defining PRODUCT_NAME and PRODUCT_SHORT_NAME, + * please contact us. + * + * [ Including an ASCII NUL in PRODUCT_NAME is considered to be + * bypassing the spirit of this request! ] + */ +init_message: + .ascii "\n" + .ascii PRODUCT_NAME + .ascii "\n" + .asciz "gPXE (http://etherboot.org) - " + .size init_message, . - init_message +init_message_pci: + .asciz " PCI" + .size init_message_pci, . - init_message_pci +init_message_pnp: + .asciz " PnP" + .size init_message_pnp, . - init_message_pnp +init_message_bbs: + .asciz " BBS" + .size init_message_bbs, . - init_message_bbs +init_message_pmm: + .asciz " PMM" + .size init_message_pmm, . - init_message_pmm +init_message_int19: + .asciz " INT19" + .size init_message_int19, . - init_message_int19 +init_message_prompt: + .asciz "\nPress Ctrl-B to configure " + .size init_message_prompt, . - init_message_prompt +init_message_dots: + .asciz "..." + .size init_message_dots, . - init_message_dots +init_message_done: + .asciz "\n\n" + .size init_message_done, . - init_message_done + +/* ROM image location + * + * May be either within option ROM space, or within PMM-allocated block. + */ +image_source: + .long 0 + .size image_source, . - image_source + +/* Temporary decompression area + * + * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block. + */ +decompress_to: + .long HIGHMEM_LOADPOINT + .size decompress_to, . - decompress_to + +/* BBS version + * + * Filled in by BBS BIOS. We ignore the value. + */ +bbs_version: + .word 0 + .size bbs_version, . - bbs_version + +/* Boot Execution Vector entry point + * + * Called by the PnP BIOS when it wants to boot us. + */ +bev_entry: + pushw %cs + call exec + lret + .size bev_entry, . - bev_entry + +/* INT19 entry point + * + * Called via the hooked INT 19 if we detected a non-PnP BIOS. We + * attempt to return via the original INT 19 vector (if we were able + * to store it). + */ +int19_entry: + pushw %cs + popw %ds + /* Prompt user to press B to boot */ + movw $int19_message_prompt, %si + xorw %di, %di + call print_message + movw $prodstr, %si + call print_message + movw $int19_message_dots, %si + call print_message + movw $0xdf4e, %bx + call wait_for_key + pushf + xorw %di, %di + call print_kill_line + movw $int19_message_done, %si + call print_message + popf + jz 1f + /* Leave keypress in buffer and start gPXE. The keypress will + * cause the usual initial Ctrl-B prompt to be skipped. + */ + pushw %cs + call exec +1: /* Try to call original INT 19 vector */ + movl %cs:orig_int19, %eax + testl %eax, %eax + je 2f + ljmp *%cs:orig_int19 +2: /* No chained vector: issue INT 18 as a last resort */ + int $0x18 + .size int19_entry, . - int19_entry +orig_int19: + .long 0 + .size orig_int19, . - orig_int19 + +int19_message_prompt: + .asciz "Press N to skip booting from " + .size int19_message_prompt, . - int19_message_prompt +int19_message_dots: + .asciz "..." + .size int19_message_dots, . - int19_message_dots +int19_message_done: + .asciz "\n\n" + .size int19_message_done, . - int19_message_done + +/* Execute as a boot device + * + */ +exec: /* Set %ds = %cs */ + pushw %cs + popw %ds + + /* Print message as soon as possible */ + movw $prodstr, %si + xorw %di, %di + call print_message + movw $exec_message, %si + call print_message + + /* Store magic word on BIOS stack and remember BIOS %ss:sp */ + pushl $STACK_MAGIC + movw %ss, %dx + movw %sp, %bp + + /* Obtain a reasonably-sized temporary stack */ + xorw %ax, %ax + movw %ax, %ss + movw $0x7c00, %sp + + /* Install gPXE */ + movl image_source, %esi + movl decompress_to, %edi + call alloc_basemem + call install_prealloc + + /* Set up real-mode stack */ + movw %bx, %ss + movw $_estack16, %sp + + /* Jump to .text16 segment */ + pushw %ax + pushw $1f + lret + .section ".text16", "awx", @progbits +1: /* Call main() */ + pushl $main + pushw %cs + call prot_call + popl %ecx /* discard */ + + /* Uninstall gPXE */ + call uninstall + + /* Restore BIOS stack */ + movw %dx, %ss + movw %bp, %sp + + /* Check magic word on BIOS stack */ + popl %eax + cmpl $STACK_MAGIC, %eax + jne 1f + /* BIOS stack OK: return to caller */ + lret +1: /* BIOS stack corrupt: use INT 18 */ + int $0x18 + .previous + +exec_message: + .asciz " starting execution\n" + .size exec_message, . - exec_message + +/* UNDI loader + * + * Called by an external program to load our PXE stack. + */ +undiloader: + /* Save registers */ + pushl %esi + pushl %edi + pushw %ds + pushw %es + pushw %bx + /* ROM segment address to %ds */ + pushw %cs + popw %ds + /* UNDI loader parameter structure address into %es:%di */ + movw %sp, %bx + movw %ss:18(%bx), %di + movw %ss:20(%bx), %es + /* Install to specified real-mode addresses */ + pushw %di + movw %es:12(%di), %bx + movw %es:14(%di), %ax + movl image_source, %esi + movl decompress_to, %edi + call install_prealloc + popw %di + /* Call UNDI loader C code */ + pushl $pxe_loader_call + pushw %cs + pushw $1f + pushw %ax + pushw $prot_call + lret +1: popw %bx /* discard */ + popw %bx /* discard */ + /* Restore registers and return */ + popw %bx + popw %es + popw %ds + popl %edi + popl %esi + lret + .size undiloader, . - undiloader + +/* Wait for key press specified by %bl (masked by %bh) + * + * Used by init and INT19 code when prompting user. If the specified + * key is pressed, it is left in the keyboard buffer. + * + * Returns with ZF set iff specified key is pressed. + */ +wait_for_key: + /* Preserve registers */ + pushw %cx + pushw %ax +1: /* Empty the keyboard buffer before waiting for input */ + movb $0x01, %ah + int $0x16 + jz 2f + xorw %ax, %ax + int $0x16 + jmp 1b +2: /* Wait for a key press */ + movw $ROM_BANNER_TIMEOUT, %cx +3: decw %cx + js 99f /* Exit with ZF clear */ + /* Wait for timer tick to be updated */ + call wait_for_tick + /* Check to see if a key was pressed */ + movb $0x01, %ah + int $0x16 + jz 3b + /* Check to see if key was the specified key */ + andb %bh, %al + cmpb %al, %bl + je 99f /* Exit with ZF set */ + /* Not the specified key: remove from buffer and stop waiting */ + pushfw + xorw %ax, %ax + int $0x16 + popfw /* Exit with ZF clear */ +99: /* Restore registers and return */ + popw %ax + popw %cx + ret + .size wait_for_key, . - wait_for_key + +/* Wait for timer tick + * + * Used by wait_for_key + */ +wait_for_tick: + pushl %eax + pushw %fs + movw $0x40, %ax + movw %ax, %fs + movl %fs:(0x6c), %eax +1: pushf + sti + hlt + popf + cmpl %fs:(0x6c), %eax + je 1b + popw %fs + popl %eax + ret + .size wait_for_tick, . - wait_for_tick diff --git a/gpxe/src/arch/i386/prefix/unnrv2b.S b/gpxe/src/arch/i386/prefix/unnrv2b.S new file mode 100644 index 0000000..70167a1 --- /dev/null +++ b/gpxe/src/arch/i386/prefix/unnrv2b.S @@ -0,0 +1,182 @@ +/* + * Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer + * + * This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Originally this code was part of ucl the data compression library + * for upx the ``Ultimate Packer of eXecutables''. + * + * - Converted to gas assembly, and refitted to work with etherboot. + * Eric Biederman 20 Aug 2002 + * + * - Structure modified to be a subroutine call rather than an + * executable prefix. + * Michael Brown 30 Mar 2004 + * + * - Modified to be compilable as either 16-bit or 32-bit code. + * Michael Brown 9 Mar 2005 + */ + +/**************************************************************************** + * This file provides the decompress() and decompress16() functions + * which can be called in order to decompress an image compressed with + * the nrv2b utility in src/util. + * + * These functions are designed to be called by the prefix. They are + * position-independent code. + * + * The same basic assembly code is used to compile both + * decompress() and decompress16(). + **************************************************************************** + */ + + .text + .arch i386 + .section ".prefix.lib", "ax", @progbits + +#ifdef CODE16 +/**************************************************************************** + * decompress16 (real-mode near call, position independent) + * + * Decompress data in 16-bit mode + * + * Parameters (passed via registers): + * %ds:%esi - Start of compressed input data + * %es:%edi - Start of output buffer + * Returns: + * %ds:%esi - End of compressed input data + * %es:%edi - End of decompressed output data + * All other registers are preserved + * + * NOTE: It would be possible to build a smaller version of the + * decompression code for -DKEEP_IT_REAL by using + * #define REG(x) x + * to use 16-bit registers where possible. This would impose limits + * that the compressed data size must be in the range [1,65533-%si] + * and the uncompressed data size must be in the range [1,65536-%di] + * (where %si and %di are the input values for those registers). Note + * particularly that the lower limit is 1, not 0, and that the upper + * limit on the input (compressed) data really is 65533, since the + * algorithm may read up to three bytes beyond the end of the input + * data, since it reads dwords. + **************************************************************************** + */ + +#define REG(x) e ## x +#define ADDR32 addr32 + + .code16 + .globl decompress16 +decompress16: + +#else /* CODE16 */ + +/**************************************************************************** + * decompress (32-bit protected-mode near call, position independent) + * + * Parameters (passed via registers): + * %ds:%esi - Start of compressed input data + * %es:%edi - Start of output buffer + * Returns: + * %ds:%esi - End of compressed input data + * %es:%edi - End of decompressed output data + * All other registers are preserved + **************************************************************************** + */ + +#define REG(x) e ## x +#define ADDR32 + + .code32 + .globl decompress +decompress: + +#endif /* CODE16 */ + +#define xAX REG(ax) +#define xCX REG(cx) +#define xBP REG(bp) +#define xSI REG(si) +#define xDI REG(di) + + /* Save registers */ + push %xAX + pushl %ebx + push %xCX + push %xBP + /* Do the decompression */ + cld + xor %xBP, %xBP + dec %xBP /* last_m_off = -1 */ + jmp dcl1_n2b + +decompr_literals_n2b: + ADDR32 movsb +decompr_loop_n2b: + addl %ebx, %ebx + jnz dcl2_n2b +dcl1_n2b: + call getbit32 +dcl2_n2b: + jc decompr_literals_n2b + xor %xAX, %xAX + inc %xAX /* m_off = 1 */ +loop1_n2b: + call getbit1 + adc %xAX, %xAX /* m_off = m_off*2 + getbit() */ + call getbit1 + jnc loop1_n2b /* while(!getbit()) */ + sub $3, %xAX + jb decompr_ebpeax_n2b /* if (m_off == 2) goto decompr_ebpeax_n2b ? */ + shl $8, %xAX + ADDR32 movb (%xSI), %al /* m_off = (m_off - 3)*256 + src[ilen++] */ + inc %xSI + xor $-1, %xAX + jz decompr_end_n2b /* if (m_off == 0xffffffff) goto decomp_end_n2b */ + mov %xAX, %xBP /* last_m_off = m_off ?*/ +decompr_ebpeax_n2b: + xor %xCX, %xCX + call getbit1 + adc %xCX, %xCX /* m_len = getbit() */ + call getbit1 + adc %xCX, %xCX /* m_len = m_len*2 + getbit()) */ + jnz decompr_got_mlen_n2b /* if (m_len == 0) goto decompr_got_mlen_n2b */ + inc %xCX /* m_len++ */ +loop2_n2b: + call getbit1 + adc %xCX, %xCX /* m_len = m_len*2 + getbit() */ + call getbit1 + jnc loop2_n2b /* while(!getbit()) */ + inc %xCX + inc %xCX /* m_len += 2 */ +decompr_got_mlen_n2b: + cmp $-0xd00, %xBP + adc $1, %xCX /* m_len = m_len + 1 + (last_m_off > 0xd00) */ + push %xSI + ADDR32 lea (%xBP,%xDI), %xSI /* m_pos = dst + olen + -m_off */ + rep + es ADDR32 movsb /* dst[olen++] = *m_pos++ while(m_len > 0) */ + pop %xSI + jmp decompr_loop_n2b + + +getbit1: + addl %ebx, %ebx + jnz 1f +getbit32: + ADDR32 movl (%xSI), %ebx + sub $-4, %xSI /* sets carry flag */ + adcl %ebx, %ebx +1: + ret + +decompr_end_n2b: + /* Restore registers and return */ + pop %xBP + pop %xCX + popl %ebx + pop %xAX + ret diff --git a/gpxe/src/arch/i386/prefix/usbdisk.S b/gpxe/src/arch/i386/prefix/usbdisk.S new file mode 100644 index 0000000..fa7d195 --- /dev/null +++ b/gpxe/src/arch/i386/prefix/usbdisk.S @@ -0,0 +1,23 @@ + .text + .arch i386 + .section ".prefix", "awx", @progbits + .code16 + .org 0 + +#include "mbr.S" + +/* Partition table: ZIP-compatible partition 4, 64 heads, 32 sectors/track */ + .org 446 + .space 16 + .space 16 + .space 16 + .byte 0x80, 0x01, 0x01, 0x00 + .byte 0xeb, 0x3f, 0x20, 0x01 + .long 0x00000020 + .long 0x00000fe0 + + .org 510 + .byte 0x55, 0xaa + +/* Skip to start of partition */ + .org 32 * 512 diff --git a/gpxe/src/arch/i386/scripts/i386-kir.lds b/gpxe/src/arch/i386/scripts/i386-kir.lds new file mode 100644 index 0000000..b213c60 --- /dev/null +++ b/gpxe/src/arch/i386/scripts/i386-kir.lds @@ -0,0 +1,196 @@ +/* -*- sh -*- */ + +/* + * Linker script for i386 images + * + */ + +OUTPUT_FORMAT ( "elf32-i386", "elf32-i386", "elf32-i386" ) +OUTPUT_ARCH ( i386 ) +ENTRY ( _entry ) + +SECTIONS { + + /* All sections in the resulting file have consecutive load + * addresses, but may have individual link addresses depending on + * the memory model being used. + * + * The linker symbols _prefix_link_addr, load_addr, and + * _max_align may be specified explicitly. If not specified, they + * will default to: + * + * _prefix_link_addr = 0 + * _load_addr = 0 + * _max_align = 16 + * + * We guarantee alignment of virtual addresses to any alignment + * specified by the constituent object files (e.g. via + * __attribute__((aligned(x)))). Load addresses are guaranteed + * only up to _max_align. Provided that all loader and relocation + * code honours _max_align, this means that physical addresses are + * also guaranteed up to _max_align. + * + * Note that when using -DKEEP_IT_REAL, the UNDI segments are only + * guaranteed to be loaded on a paragraph boundary (i.e. 16-byte + * alignment). Using _max_align>16 will therefore not guarantee + * >16-byte alignment of physical addresses when -DKEEP_IT_REAL is + * used (though virtual addresses will still be fully aligned). + * + */ + + /* + * The prefix + */ + + _prefix_link_addr = DEFINED ( _prefix_link_addr ) ? _prefix_link_addr : 0; + . = _prefix_link_addr; + _prefix = .; + + .prefix : AT ( _prefix_load_offset + __prefix ) { + __prefix = .; + _entry = .; + *(.prefix) + *(.prefix.*) + _eprefix_progbits = .; + } + + _eprefix = .; + + /* + * The 16-bit sections + */ + + _text16_link_addr = 0; + . = _text16_link_addr; + _text16 = .; + + . += 1; /* Prevent NULL being valid */ + + .text16 : AT ( _text16_load_offset + __text16 ) { + __text16 = .; + *(.text.null_trap) + *(.text16) + *(.text16.*) + *(.text) + *(.text.*) + _etext16_progbits = .; + } = 0x9090 + + _etext16 = .; + + _data16_link_addr = 0; + . = _data16_link_addr; + _data16 = .; + + . += 1; /* Prevent NULL being valid */ + + .rodata16 : AT ( _data16_load_offset + __rodata16 ) { + __rodata16 = .; + *(.rodata16) + *(.rodata16.*) + *(.rodata) + *(.rodata.*) + } + .data16 : AT ( _data16_load_offset + __data16 ) { + __data16 = .; + *(.data16) + *(.data16.*) + *(.data) + *(.data.*) + *(SORT(.tbl.*)) /* Various tables. See include/tables.h */ + _edata16_progbits = .; + } + .bss16 : AT ( _data16_load_offset + __bss16 ) { + __bss16 = .; + _bss16 = .; + *(.bss16) + *(.bss16.*) + *(.bss) + *(.bss.*) + *(COMMON) + _ebss16 = .; + } + .stack16 : AT ( _data16_load_offset + __stack16 ) { + __stack16 = .; + *(.stack16) + *(.stack16.*) + *(.stack) + *(.stack.*) + } + + _edata16 = .; + + _end = .; + + /* + * Dispose of the comment and note sections to make the link map + * easier to read + */ + + /DISCARD/ : { + *(.comment) + *(.note) + } + + /* + * Load address calculations. The slightly obscure nature of the + * calculations is because ALIGN(x) can only operate on the + * location counter. + */ + + _max_align = DEFINED ( _max_align ) ? _max_align : 16; + _load_addr = DEFINED ( _load_addr ) ? _load_addr : 0; + + . = _load_addr; + + . -= _prefix_link_addr; + _prefix_load_offset = ALIGN ( _max_align ); + _prefix_load_addr = _prefix_link_addr + _prefix_load_offset; + _prefix_size = _eprefix - _prefix; + _prefix_progbits_size = _eprefix_progbits - _prefix; + . = _prefix_load_addr + _prefix_progbits_size; + + . -= _text16_link_addr; + _text16_load_offset = ALIGN ( _max_align ); + _text16_load_addr = _text16_link_addr + _text16_load_offset; + _text16_size = _etext16 - _text16; + _text16_progbits_size = _etext16_progbits - _text16; + . = _text16_load_addr + _text16_progbits_size; + + . -= _data16_link_addr; + _data16_load_offset = ALIGN ( _max_align ); + _data16_load_addr = _data16_link_addr + _data16_load_offset; + _data16_size = _edata16 - _data16; + _data16_progbits_size = _edata16_progbits - _data16; + . = _data16_load_addr + _data16_progbits_size; + + . = ALIGN ( _max_align ); + + _load_size = . - _load_addr; + + /* + * Alignment checks. ALIGN() can only operate on the location + * counter, so we set the location counter to each value we want + * to check. + */ + + . = _prefix_load_addr - _prefix_link_addr; + _assert = ASSERT ( ( . == ALIGN ( _max_align ) ), + "_prefix is badly aligned" ); + + . = _text16_load_addr - _text16_link_addr; + _assert = ASSERT ( ( . == ALIGN ( _max_align ) ), + "_text16 is badly aligned" ); + + . = _data16_load_addr - _data16_link_addr; + _assert = ASSERT ( ( . == ALIGN ( _max_align ) ), + "_data16 is badly aligned" ); + + /* + * Values calculated to save code from doing it + */ + _text16_size_pgh = ( ( _text16_size + 15 ) / 16 ); + _data16_size_pgh = ( ( _data16_size + 15 ) / 16 ); + _load_size_pgh = ( ( _load_size + 15 ) / 16 ); + _load_size_sect = ( ( _load_size + 511 ) / 512 ); +} diff --git a/gpxe/src/arch/i386/scripts/i386.lds b/gpxe/src/arch/i386/scripts/i386.lds new file mode 100644 index 0000000..8a0c673 --- /dev/null +++ b/gpxe/src/arch/i386/scripts/i386.lds @@ -0,0 +1,209 @@ +/* -*- sh -*- */ + +/* + * Linker script for i386 images + * + */ + +SECTIONS { + + /* Each section starts at a virtual address of zero. + * + * We guarantee alignment of virtual addresses to any alignment + * specified by the constituent object files (e.g. via + * __attribute__((aligned(x)))). Load addresses are guaranteed + * only up to _max_align. Provided that all loader and relocation + * code honours _max_align, this means that physical addresses are + * also guaranteed up to _max_align. + * + * Note that when using -DKEEP_IT_REAL, the UNDI segments are only + * guaranteed to be loaded on a paragraph boundary (i.e. 16-byte + * alignment). Using _max_align>16 will therefore not guarantee + * >16-byte alignment of physical addresses when -DKEEP_IT_REAL is + * used (though virtual addresses will still be fully aligned). + * + */ + + /* + * The prefix + * + */ + + .prefix 0x0 : AT ( _prefix_lma ) { + _prefix = .; + *(.prefix) + *(.prefix.*) + _mprefix = .; + } .bss.prefix (NOLOAD) : AT ( _end_lma ) { + _eprefix = .; + } + _prefix_filesz = ABSOLUTE ( _mprefix - _prefix ); + _prefix_memsz = ABSOLUTE ( _eprefix - _prefix ); + + /* + * The 16-bit (real-mode) code section + * + */ + + .text16 0x0 : AT ( _text16_lma ) { + _text16 = .; + *(.text16.null) + . += 1; /* Prevent NULL being valid */ + *(.text16) + *(.text16.*) + _mtext16 = .; + } .bss.text16 (NOLOAD) : AT ( _end_lma ) { + _etext16 = .; + } + _text16_filesz = ABSOLUTE ( _mtext16 - _text16 ); + _text16_memsz = ABSOLUTE ( _etext16 - _text16 ); + + /* + * The 16-bit (real-mode) data section + * + */ + + .data16 0x0 : AT ( _data16_lma ) { + _data16 = .; + . += 1; /* Prevent NULL being valid */ + *(.rodata16) + *(.rodata16.*) + *(.data16) + *(.data16.*) + _mdata16 = .; + } .bss.data16 (NOLOAD) : AT ( _end_lma ) { + *(.bss16) + *(.bss16.*) + *(.stack16) + *(.stack16.*) + _edata16 = .; + } + _data16_filesz = ABSOLUTE ( _mdata16 - _data16 ); + _data16_memsz = ABSOLUTE ( _edata16 - _data16 ); + + /* + * The 32-bit sections + * + */ + + .textdata 0x0 : AT ( _textdata_lma ) { + _textdata = .; + *(.text.null_trap) + . += 1; /* Prevent NULL being valid */ + *(.text) + *(.text.*) + *(.rodata) + *(.rodata.*) + *(.data) + *(.data.*) + *(SORT(.tbl.*)) /* Various tables. See include/tables.h */ + _mtextdata = .; + } .bss.textdata (NOLOAD) : AT ( _end_lma ) { + *(.bss) + *(.bss.*) + *(COMMON) + *(.stack) + *(.stack.*) + _etextdata = .; + } + _textdata_filesz = ABSOLUTE ( _mtextdata - _textdata ); + _textdata_memsz = ABSOLUTE ( _etextdata - _textdata ); + + /* + * Compressor information block + * + */ + + .zinfo 0x0 : AT ( _zinfo_lma ) { + _zinfo = .; + *(.zinfo) + *(.zinfo.*) + _mzinfo = .; + } .bss.zinfo (NOLOAD) : AT ( _end_lma ) { + _ezinfo = .; + } + _zinfo_filesz = ABSOLUTE ( _mzinfo - _zinfo ); + _zinfo_memsz = ABSOLUTE ( _ezinfo - _zinfo ); + + /* + * Weak symbols that need zero values if not otherwise defined + * + */ + + .weak 0x0 : AT ( _end_lma ) { + _weak = .; + *(.weak) + _eweak = .; + } + _assert = ASSERT ( ( _weak == _eweak ), ".weak is non-zero length" ); + + /* + * Dispose of the comment and note sections to make the link map + * easier to read + * + */ + + /DISCARD/ : { + *(.comment) + *(.comment.*) + *(.note) + *(.note.*) + *(.eh_frame) + *(.eh_frame.*) + *(.rel) + *(.rel.*) + } + + /* + * Load address calculations. In older versions of ld, ALIGN() + * can operate only on the location counter, so we use that. + * + */ + + PROVIDE ( _max_align = 16 ); + . = 0; + + . = ALIGN ( _max_align ); + _prefix_lma = .; + . += _prefix_filesz; + + . = ALIGN ( _max_align ); + _payload_lma = .; + _text16_lma = .; + . += _text16_filesz; + + . = ALIGN ( _max_align ); + _data16_lma = .; + . += _data16_filesz; + + . = ALIGN ( _max_align ); + _textdata_lma = .; + . += _textdata_filesz; + + _filesz = .; /* Do not include zinfo block in file size */ + + . = ALIGN ( _max_align ); + _zinfo_lma = .; + . += _zinfo_filesz; + + . = ALIGN ( _max_align ); + _end_lma = .; + + /* + * Values calculated to save code from doing it + * + */ + _prefix_memsz_pgh = ( ( _prefix_memsz + 15 ) / 16 ); + _prefix_memsz_sect = ( ( _prefix_memsz + 511 ) / 512 ); + _text16_memsz_pgh = ( ( _text16_memsz + 15 ) / 16 ); + _data16_memsz_pgh = ( ( _data16_memsz + 15 ) / 16 ); + + /* + * File size in paragraphs and sectors. Note that wherever the + * _filesz variables are used, there must be a corresponding + * .zinfo.fixup section. + * + */ + _filesz_pgh = ( ( _filesz + 15 ) / 16 ); + _filesz_sect = ( ( _filesz + 511 ) / 512 ); +} diff --git a/gpxe/src/arch/i386/transitions/libkir.S b/gpxe/src/arch/i386/transitions/libkir.S new file mode 100644 index 0000000..1023ddd --- /dev/null +++ b/gpxe/src/arch/i386/transitions/libkir.S @@ -0,0 +1,254 @@ +/* + * libkir: a transition library for -DKEEP_IT_REAL + * + * Michael Brown + * + */ + +/**************************************************************************** + * This file defines libkir: an interface between external and + * internal environments when -DKEEP_IT_REAL is used, so that both + * internal and external environments are in real mode. It deals with + * switching data segments and the stack. It provides the following + * functions: + * + * ext_to_kir & switch between external and internal (kir) + * kir_to_ext environments, preserving all non-segment + * registers + * + * kir_call issue a call to an internal routine from external + * code + * + * libkir is written to avoid assuming that segments are anything + * other than opaque data types, and also avoids assuming that the + * stack pointer is 16-bit. This should enable it to run just as well + * in 16:16 or 16:32 protected mode as in real mode. + **************************************************************************** + */ + +/* Breakpoint for when debugging under bochs */ +#define BOCHSBP xchgw %bx, %bx + + .text + .arch i386 + .section ".text16", "awx", @progbits + .code16 + +/**************************************************************************** + * init_libkir (real-mode or 16:xx protected-mode far call) + * + * Initialise libkir ready for transitions to the kir environment + * + * Parameters: + * %cs : .text16 segment + * %ds : .data16 segment + **************************************************************************** + */ + .globl init_libkir +init_libkir: + /* Record segment registers */ + pushw %ds + popw %cs:kir_ds + lret + +/**************************************************************************** + * ext_to_kir (real-mode or 16:xx protected-mode near call) + * + * Switch from external stack and segment registers to internal stack + * and segment registers. %ss:sp is restored from the saved kir_ds + * and kir_sp. %ds, %es, %fs and %gs are all restored from the saved + * kir_ds. All other registers are preserved. + * + * %cs:0000 must point to the start of the runtime image code segment + * on entry. + * + * Parameters: none + **************************************************************************** + */ + + .globl ext_to_kir +ext_to_kir: + /* Record external segment registers */ + movw %ds, %cs:ext_ds + pushw %cs + popw %ds /* Set %ds = %cs for easier access to variables */ + movw %es, %ds:ext_es + movw %fs, %ds:ext_fs + movw %gs, %ds:ext_fs + + /* Preserve registers */ + movw %ax, %ds:save_ax + + /* Extract near return address from stack */ + popw %ds:save_retaddr + + /* Record external %ss:esp */ + movw %ss, %ds:ext_ss + movl %esp, %ds:ext_esp + + /* Load internal segment registers and stack pointer */ + movw %ds:kir_ds, %ax + movw %ax, %ss + movzwl %ds:kir_sp, %esp + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs +1: + + /* Place return address on new stack */ + pushw %cs:save_retaddr + + /* Restore registers and return */ + movw %cs:save_ax, %ax + ret + +/**************************************************************************** + * kir_to_ext (real-mode or 16:xx protected-mode near call) + * + * Switch from internal stack and segment registers to external stack + * and segment registers. %ss:%esp is restored from the saved ext_ss + * and ext_esp. Other segment registers are restored from the + * corresponding locations. All other registers are preserved. + * + * Note that it is actually %ss that is recorded as kir_ds, on the + * assumption that %ss == %ds when kir_to_ext is called. + * + * Parameters: none + **************************************************************************** + */ + + .globl kir_to_ext +kir_to_ext: + /* Record near return address */ + pushw %cs + popw %ds /* Set %ds = %cs for easier access to variables */ + popw %ds:save_retaddr + + /* Record internal segment registers and %sp */ + movw %ss, %ds:kir_ds + movw %sp, %ds:kir_sp + + /* Load external segment registers and stack pointer */ + movw %ds:ext_ss, %ss + movl %ds:ext_esp, %esp + movw %ds:ext_gs, %gs + movw %ds:ext_fs, %fs + movw %ds:ext_es, %es + movw %ds:ext_ds, %ds + + /* Return */ + pushw %cs:save_retaddr + ret + +/**************************************************************************** + * kir_call (real-mode or 16:xx protected-mode far call) + * + * Call a specific C function in the internal code. The prototype of + * the C function must be + * void function ( struct i386_all_resg *ix86 ); + * ix86 will point to a struct containing the real-mode registers + * at entry to kir_call. + * + * All registers will be preserved across kir_call(), unless the C + * function explicitly overwrites values in ix86. Interrupt status + * will also be preserved. + * + * Parameters: + * function : (32-bit) virtual address of C function to call + * + * Example usage: + * pushl $pxe_api_call + * lcall $UNDI_CS, $kir_call + * addw $4, %sp + * to call in to the C function + * void pxe_api_call ( struct i386_all_regs *ix86 ); + **************************************************************************** + */ + + .globl kir_call +kir_call: + /* Preserve flags. Must do this before any operation that may + * affect flags. + */ + pushfl + popl %cs:save_flags + + /* Disable interrupts. We do funny things with the stack, and + * we're not re-entrant. + */ + cli + + /* Extract address of internal routine from stack. We must do + * this without using (%bp), because we may be called with + * either a 16-bit or a 32-bit stack segment. + */ + popl %cs:save_retaddr /* Scratch location */ + popl %cs:save_function + subl $8, %esp /* Restore %esp */ + + /* Switch to internal stack. Note that the external stack is + * inaccessible once we're running internally (since we have + * no concept of 48-bit far pointers) + */ + call ext_to_kir + + /* Store external registers on internal stack */ + pushl %cs:save_flags + pushal + pushl %cs:ext_fs_and_gs + pushl %cs:ext_ds_and_es + pushl %cs:ext_cs_and_ss + + /* Push &ix86 on stack and call function */ + sti + pushl %esp + data32 call *%cs:save_function + popl %eax /* discard */ + + /* Restore external registers from internal stack */ + popl %cs:ext_cs_and_ss + popl %cs:ext_ds_and_es + popl %cs:ext_fs_and_gs + popal + popl %cs:save_flags + + /* Switch to external stack */ + call kir_to_ext + + /* Restore flags */ + pushl %cs:save_flags + popfl + + /* Return */ + lret + +/**************************************************************************** + * Stored internal and external stack and segment registers + **************************************************************************** + */ + +ext_cs_and_ss: +ext_cs: .word 0 +ext_ss: .word 0 +ext_ds_and_es: +ext_ds: .word 0 +ext_es: .word 0 +ext_fs_and_gs: +ext_fs: .word 0 +ext_gs: .word 0 +ext_esp: .long 0 + + .globl kir_ds +kir_ds: .word 0 + .globl kir_sp +kir_sp: .word _estack + +/**************************************************************************** + * Temporary variables + **************************************************************************** + */ +save_ax: .word 0 +save_retaddr: .long 0 +save_flags: .long 0 +save_function: .long 0 diff --git a/gpxe/src/arch/i386/transitions/libpm.S b/gpxe/src/arch/i386/transitions/libpm.S new file mode 100644 index 0000000..e69de29 diff --git a/gpxe/src/arch/i386/transitions/librm.S b/gpxe/src/arch/i386/transitions/librm.S new file mode 100755 index 0000000..8cf1f7f --- /dev/null +++ b/gpxe/src/arch/i386/transitions/librm.S @@ -0,0 +1,579 @@ +/* + * librm: a library for interfacing to real-mode code + * + * Michael Brown + * + */ + +/* Drag in local definitions */ +#include "librm.h" + +/* For switches to/from protected mode */ +#define CR0_PE 1 + +/* Size of various C data structures */ +#define SIZEOF_I386_SEG_REGS 12 +#define SIZEOF_I386_REGS 32 +#define SIZEOF_REAL_MODE_REGS ( SIZEOF_I386_SEG_REGS + SIZEOF_I386_REGS ) +#define SIZEOF_I386_FLAGS 4 +#define SIZEOF_I386_ALL_REGS ( SIZEOF_REAL_MODE_REGS + SIZEOF_I386_FLAGS ) + + .arch i386 + +/**************************************************************************** + * Global descriptor table + * + * Call init_librm to set up the GDT before attempting to use any + * protected-mode code. + * + * Define FLATTEN_REAL_MODE if you want to use so-called "flat real + * mode" with 4GB limits instead. + * + * NOTE: This must be located before prot_to_real, otherwise gas + * throws a "can't handle non absolute segment in `ljmp'" error due to + * not knowing the value of REAL_CS when the ljmp is encountered. + * + * Note also that putting ".word gdt_end - gdt - 1" directly into + * gdt_limit, rather than going via gdt_length, will also produce the + * "non absolute segment" error. This is most probably a bug in gas. + **************************************************************************** + */ + +#ifdef FLATTEN_REAL_MODE +#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x8f +#else +#define RM_LIMIT_16_19__AVL__SIZE__GRANULARITY 0x00 +#endif + .section ".data16", "aw", @progbits + .align 16 +gdt: +gdtr: /* The first GDT entry is unused, the GDTR can fit here. */ +gdt_limit: .word gdt_length - 1 +gdt_base: .long 0 + .word 0 /* padding */ + + .org gdt + VIRTUAL_CS, 0 +virtual_cs: /* 32 bit protected mode code segment, virtual addresses */ + .word 0xffff, 0 + .byte 0, 0x9f, 0xcf, 0 + + .org gdt + VIRTUAL_DS, 0 +virtual_ds: /* 32 bit protected mode data segment, virtual addresses */ + .word 0xffff, 0 + .byte 0, 0x93, 0xcf, 0 + + .org gdt + PHYSICAL_CS, 0 +physical_cs: /* 32 bit protected mode code segment, physical addresses */ + .word 0xffff, 0 + .byte 0, 0x9f, 0xcf, 0 + + .org gdt + PHYSICAL_DS, 0 +physical_ds: /* 32 bit protected mode data segment, physical addresses */ + .word 0xffff, 0 + .byte 0, 0x93, 0xcf, 0 + + .org gdt + REAL_CS, 0 +real_cs: /* 16 bit real mode code segment */ + .word 0xffff, 0 + .byte 0, 0x9b, RM_LIMIT_16_19__AVL__SIZE__GRANULARITY, 0 + + .org gdt + REAL_DS +real_ds: /* 16 bit real mode data segment */ + .word 0xffff, 0 + .byte 0, 0x93, RM_LIMIT_16_19__AVL__SIZE__GRANULARITY, 0 + +gdt_end: + .equ gdt_length, gdt_end - gdt + +/**************************************************************************** + * init_librm (real-mode far call, 16-bit real-mode far return address) + * + * Initialise the GDT ready for transitions to protected mode. + * + * Parameters: + * %cs : .text16 segment + * %ds : .data16 segment + * %edi : Physical base of protected-mode code (virt_offset) + **************************************************************************** + */ + .section ".text16", "ax", @progbits + .code16 + .globl init_librm +init_librm: + /* Preserve registers */ + pushl %eax + pushl %ebx + + /* Store _virt_offset and set up virtual_cs and virtual_ds segments */ + movl %edi, %eax + movw $virtual_cs, %bx + call set_seg_base + movw $virtual_ds, %bx + call set_seg_base + movl %edi, _virt_offset + + /* Negate virt_offset */ + negl %edi + + /* Store rm_cs and _text16, set up real_cs segment */ + xorl %eax, %eax + movw %cs, %ax + movw %ax, rm_cs + shll $4, %eax + movw $real_cs, %bx + call set_seg_base + addr32 leal (%eax, %edi), %ebx + movl %ebx, _text16 + + /* Store rm_ds and _data16, set up real_ds segment */ + xorl %eax, %eax + movw %ds, %ax + movw %ax, %cs:rm_ds + shll $4, %eax + movw $real_ds, %bx + call set_seg_base + addr32 leal (%eax, %edi), %ebx + movl %ebx, _data16 + + /* Set GDT and IDT base */ + movl %eax, gdt_base + addl $gdt, gdt_base + call idt_init + + /* Restore registers */ + negl %edi + popl %ebx + popl %eax + lret + + .section ".text16", "ax", @progbits + .code16 + .weak idt_init +set_seg_base: +1: movw %ax, 2(%bx) + rorl $16, %eax + movb %al, 4(%bx) + movb %ah, 7(%bx) + roll $16, %eax +idt_init: /* Reuse the return opcode here */ + ret + +/**************************************************************************** + * real_to_prot (real-mode near call, 32-bit virtual return address) + * + * Switch from 16-bit real-mode to 32-bit protected mode with virtual + * addresses. The real-mode %ss:sp is stored in rm_ss and rm_sp, and + * the protected-mode %esp is restored from the saved pm_esp. + * Interrupts are disabled. All other registers may be destroyed. + * + * The return address for this function should be a 32-bit virtual + * address. + * + * Parameters: + * %ecx : number of bytes to move from RM stack to PM stack + * + **************************************************************************** + */ + .section ".text16", "ax", @progbits + .code16 +real_to_prot: + /* Make sure we have our data segment available */ + movw %cs:rm_ds, %ax + movw %ax, %ds + + /* Add _virt_offset, _text16 and _data16 to stack to be + * copied, and also copy the return address. + */ + pushl _virt_offset + pushl _text16 + pushl _data16 + addw $16, %cx /* %ecx must be less than 64kB anyway */ + + /* Real-mode %ss:%sp => %ebp:%edx and virtual address => %esi */ + xorl %ebp, %ebp + movw %ss, %bp + movzwl %sp, %edx + movl %ebp, %eax + shll $4, %eax + addr32 leal (%eax,%edx), %esi + subl _virt_offset, %esi + + /* Switch to protected mode */ + cli + data32 lgdt gdtr + data32 lidt idtr + movl %cr0, %eax + orb $CR0_PE, %al + movl %eax, %cr0 + data32 ljmp $VIRTUAL_CS, $1f + .section ".text", "ax", @progbits + .code32 +1: + /* Set up protected-mode data segments and stack pointer */ + movw $VIRTUAL_DS, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + movl pm_esp, %esp + + /* Record real-mode %ss:sp (after removal of data) */ + movw %bp, rm_ss + addl %ecx, %edx + movw %dx, rm_sp + + /* Move data from RM stack to PM stack */ + subl %ecx, %esp + movl %esp, %edi + rep movsb + + /* Publish virt_offset, text16 and data16 for PM code to use */ + popl data16 + popl text16 + popl virt_offset + + /* Return to virtual address */ + ret + + /* Default IDTR with no interrupts */ + .section ".data16", "aw", @progbits + .weak idtr +idtr: +rm_idtr: + .word 0xffff /* limit */ + .long 0 /* base */ + +/**************************************************************************** + * prot_to_real (protected-mode near call, 32-bit real-mode return address) + * + * Switch from 32-bit protected mode with virtual addresses to 16-bit + * real mode. The protected-mode %esp is stored in pm_esp and the + * real-mode %ss:sp is restored from the saved rm_ss and rm_sp. The + * high word of the real-mode %esp is set to zero. All real-mode data + * segment registers are loaded from the saved rm_ds. Interrupts are + * *not* enabled, since we want to be able to use prot_to_real in an + * ISR. All other registers may be destroyed. + * + * The return address for this function should be a 32-bit (sic) + * real-mode offset within .code16. + * + * Parameters: + * %ecx : number of bytes to move from PM stack to RM stack + * + **************************************************************************** + */ + .section ".text", "ax", @progbits + .code32 +prot_to_real: + /* Add return address to data to be moved to RM stack */ + addl $4, %ecx + + /* Real-mode %ss:sp => %ebp:edx and virtual address => %edi */ + movzwl rm_ss, %ebp + movzwl rm_sp, %edx + subl %ecx, %edx + movl %ebp, %eax + shll $4, %eax + leal (%eax,%edx), %edi + subl virt_offset, %edi + + /* Move data from PM stack to RM stack */ + movl %esp, %esi + rep movsb + + /* Record protected-mode %esp (after removal of data) */ + movl %esi, pm_esp + + /* Load real-mode segment limits */ + movw $REAL_DS, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + ljmp $REAL_CS, $1f + .section ".text16", "ax", @progbits + .code16 +1: + /* Switch to real mode */ + movl %cr0, %eax + andb $0!CR0_PE, %al + movl %eax, %cr0 + ljmp *p2r_jump_vector +p2r_jump_target: + + /* Set up real-mode data segments and stack pointer */ + movw %cs:rm_ds, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %bp, %ss + movl %edx, %esp + + /* Reset IDTR to the real-mode defaults */ + data32 lidt rm_idtr + + /* Return to real-mode address */ + data32 ret + + + /* Real-mode code and data segments. Assigned by the call to + * init_librm. rm_cs doubles as the segment part of the jump + * vector used by prot_to_real. rm_ds is located in .text16 + * rather than .data16 because code needs to be able to locate + * the data segment. + */ + .section ".data16", "aw", @progbits +p2r_jump_vector: + .word p2r_jump_target + .globl rm_cs +rm_cs: .word 0 + .globl rm_ds + .section ".text16.data", "aw", @progbits +rm_ds: .word 0 + +/**************************************************************************** + * prot_call (real-mode far call, 16-bit real-mode far return address) + * + * Call a specific C function in the protected-mode code. The + * prototype of the C function must be + * void function ( struct i386_all_regs *ix86 ); + * ix86 will point to a struct containing the real-mode registers + * at entry to prot_call. + * + * All registers will be preserved across prot_call(), unless the C + * function explicitly overwrites values in ix86. Interrupt status + * and GDT will also be preserved. Gate A20 will be enabled. + * + * Note that prot_call() does not rely on the real-mode stack + * remaining intact in order to return, since everything relevant is + * copied to the protected-mode stack for the duration of the call. + * In particular, this means that a real-mode prefix can make a call + * to main() which will return correctly even if the prefix's stack + * gets vapourised during the Etherboot run. (The prefix cannot rely + * on anything else on the stack being preserved, so should move any + * critical data to registers before calling main()). + * + * Parameters: + * function : virtual address of protected-mode function to call + * + * Example usage: + * pushl $pxe_api_call + * call prot_call + * addw $4, %sp + * to call in to the C function + * void pxe_api_call ( struct i386_all_regs *ix86 ); + **************************************************************************** + */ + +#define PC_OFFSET_GDT ( 0 ) +#define PC_OFFSET_IDT ( PC_OFFSET_GDT + 8 /* pad to 8 to keep alignment */ ) +#define PC_OFFSET_IX86 ( PC_OFFSET_IDT + 8 /* pad to 8 to keep alignment */ ) +#define PC_OFFSET_RETADDR ( PC_OFFSET_IX86 + SIZEOF_I386_ALL_REGS ) +#define PC_OFFSET_FUNCTION ( PC_OFFSET_RETADDR + 4 ) +#define PC_OFFSET_END ( PC_OFFSET_FUNCTION + 4 ) + + .section ".text16", "ax", @progbits + .code16 + .globl prot_call +prot_call: + /* Preserve registers, flags and GDT on external RM stack */ + pushfl + pushal + pushw %gs + pushw %fs + pushw %es + pushw %ds + pushw %ss + pushw %cs + subw $16, %sp + movw %sp, %bp + sidt 8(%bp) + sgdt (%bp) + + /* For sanity's sake, clear the direction flag as soon as possible */ + cld + + /* Switch to protected mode and move register dump to PM stack */ + movl $PC_OFFSET_END, %ecx + pushl $1f + jmp real_to_prot + .section ".text", "ax", @progbits + .code32 +1: + /* Set up environment expected by C code */ + call gateA20_set + + /* Call function */ + leal PC_OFFSET_IX86(%esp), %eax + pushl %eax + call *(PC_OFFSET_FUNCTION+4)(%esp) + popl %eax /* discard */ + + /* Switch to real mode and move register dump back to RM stack */ + movl $PC_OFFSET_END, %ecx + pushl $1f + jmp prot_to_real + .section ".text16", "ax", @progbits + .code16 +1: + /* Reload GDT and IDT, restore registers and flags and return */ + movw %sp, %bp + data32 lgdt (%bp) + data32 lidt 8(%bp) + addw $20, %sp /* also skip %cs and %ss */ + popw %ds + popw %es + popw %fs + popw %gs + popal + /* popal skips %esp. We therefore want to do "movl -20(%sp), + * %esp", but -20(%sp) is not a valid 80386 expression. + * Fortunately, prot_to_real() zeroes the high word of %esp, so + * we can just use -20(%esp) instead. + */ + addr32 movl -20(%esp), %esp + popfl + lret + +/**************************************************************************** + * real_call (protected-mode near call, 32-bit virtual return address) + * + * Call a real-mode function from protected-mode code. + * + * The non-segment register values will be passed directly to the + * real-mode code. The segment registers will be set as per + * prot_to_real. The non-segment register values set by the real-mode + * function will be passed back to the protected-mode caller. A + * result of this is that this routine cannot be called directly from + * C code, since it clobbers registers that the C ABI expects the + * callee to preserve. Gate A20 will *not* be automatically + * re-enabled. Since we always run from an even megabyte of memory, + * we are guaranteed to return successfully to the protected-mode + * code, which should then call gateA20_set() if it suspects that gate + * A20 may have been disabled. Note that enabling gate A20 is a + * potentially slow operation that may also cause keyboard input to be + * lost; this is why it is not done automatically. + * + * librm.h defines a convenient macro REAL_CODE() for using real_call. + * See librm.h and realmode.h for details and examples. + * + * Parameters: + * (32-bit) near pointer to real-mode function to call + * + * Returns: none + **************************************************************************** + */ + +#define RC_OFFSET_PRESERVE_REGS ( 0 ) +#define RC_OFFSET_RETADDR ( RC_OFFSET_PRESERVE_REGS + SIZEOF_I386_REGS ) +#define RC_OFFSET_FUNCTION ( RC_OFFSET_RETADDR + 4 ) +#define RC_OFFSET_END ( RC_OFFSET_FUNCTION + 4 ) + + .section ".text", "ax", @progbits + .code32 + .globl real_call +real_call: + /* Create register dump and function pointer copy on PM stack */ + pushal + pushl RC_OFFSET_FUNCTION(%esp) + + /* Switch to real mode and move register dump to RM stack */ + movl $( RC_OFFSET_RETADDR + 4 /* function pointer copy */ ), %ecx + pushl $1f + jmp prot_to_real + .section ".text16", "ax", @progbits + .code16 +1: + /* Call real-mode function */ + popl rc_function + popal + call *rc_function + pushal + + /* For sanity's sake, clear the direction flag as soon as possible */ + cld + + /* Switch to protected mode and move register dump back to PM stack */ + movl $RC_OFFSET_RETADDR, %ecx + pushl $1f + jmp real_to_prot + .section ".text", "ax", @progbits + .code32 +1: + /* Restore registers and return */ + popal + ret + + + /* Function vector, used because "call xx(%sp)" is not a valid + * 16-bit expression. + */ + .section ".data16", "aw", @progbits +rc_function: .word 0, 0 + +/**************************************************************************** + * Stored real-mode and protected-mode stack pointers + * + * The real-mode stack pointer is stored here whenever real_to_prot + * is called and restored whenever prot_to_real is called. The + * converse happens for the protected-mode stack pointer. + * + * Despite initial appearances this scheme is, in fact re-entrant, + * because program flow dictates that we always return via the point + * we left by. For example: + * PXE API call entry + * 1 real => prot + * ... + * Print a text string + * ... + * 2 prot => real + * INT 10 + * 3 real => prot + * ... + * ... + * 4 prot => real + * PXE API call exit + * + * At point 1, the RM mode stack value, say RPXE, is stored in + * rm_ss,sp. We want this value to still be present in rm_ss,sp when + * we reach point 4. + * + * At point 2, the RM stack value is restored from RPXE. At point 3, + * the RM stack value is again stored in rm_ss,sp. This *does* + * overwrite the RPXE that we have stored there, but it's the same + * value, since the code between points 2 and 3 has managed to return + * to us. + **************************************************************************** + */ + .section ".data", "aw", @progbits + .globl rm_sp +rm_sp: .word 0 + .globl rm_ss +rm_ss: .word 0 +pm_esp: .long _estack + +/**************************************************************************** + * Virtual address offsets + * + * These are used by the protected-mode code to map between virtual + * and physical addresses, and to access variables in the .text16 or + * .data16 segments. + **************************************************************************** + */ + /* Internal copies, created by init_librm (which runs in real mode) */ + .section ".data16", "aw", @progbits +_virt_offset: .long 0 +_text16: .long 0 +_data16: .long 0 + + /* Externally-visible copies, created by real_to_prot */ + .section ".data", "aw", @progbits + .globl virt_offset +virt_offset: .long 0 + .globl text16 +text16: .long 0 + .globl data16 +data16: .long 0 diff --git a/gpxe/src/arch/i386/transitions/librm_mgmt.c b/gpxe/src/arch/i386/transitions/librm_mgmt.c new file mode 100755 index 0000000..50569f8 --- /dev/null +++ b/gpxe/src/arch/i386/transitions/librm_mgmt.c @@ -0,0 +1,56 @@ +/* + * librm: a library for interfacing to real-mode code + * + * Michael Brown + * + */ + +#include +#include + +/* + * This file provides functions for managing librm. + * + */ + +/** + * Allocate space on the real-mode stack and copy data there from a + * user buffer + * + * @v data User buffer + * @v size Size of stack data + * @ret sp New value of real-mode stack pointer + */ +uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ) { + userptr_t rm_stack; + rm_sp -= size; + rm_stack = real_to_user ( rm_ss, rm_sp ); + memcpy_user ( rm_stack, 0, data, 0, size ); + return rm_sp; +}; + +/** + * Deallocate space on the real-mode stack, optionally copying back + * data to a user buffer. + * + * @v data User buffer + * @v size Size of stack data + */ +void remove_user_from_rm_stack ( userptr_t data, size_t size ) { + if ( data ) { + userptr_t rm_stack = real_to_user ( rm_ss, rm_sp ); + memcpy_user ( rm_stack, 0, data, 0, size ); + } + rm_sp += size; +}; + +PROVIDE_UACCESS_INLINE ( librm, phys_to_user ); +PROVIDE_UACCESS_INLINE ( librm, user_to_phys ); +PROVIDE_UACCESS_INLINE ( librm, virt_to_user ); +PROVIDE_UACCESS_INLINE ( librm, user_to_virt ); +PROVIDE_UACCESS_INLINE ( librm, userptr_add ); +PROVIDE_UACCESS_INLINE ( librm, memcpy_user ); +PROVIDE_UACCESS_INLINE ( librm, memmove_user ); +PROVIDE_UACCESS_INLINE ( librm, memset_user ); +PROVIDE_UACCESS_INLINE ( librm, strlen_user ); +PROVIDE_UACCESS_INLINE ( librm, memchr_user ); diff --git a/gpxe/src/arch/x86/Makefile b/gpxe/src/arch/x86/Makefile new file mode 100644 index 0000000..a7c4bc0 --- /dev/null +++ b/gpxe/src/arch/x86/Makefile @@ -0,0 +1,9 @@ +# Include common x86 headers +# +CFLAGS += -Iarch/x86/include + +# x86-specific directories containing source files +# +SRCDIRS += arch/x86/core +SRCDIRS += arch/x86/interface/efi +SRCDIRS += arch/x86/prefix diff --git a/gpxe/src/arch/x86/Makefile.efi b/gpxe/src/arch/x86/Makefile.efi new file mode 100644 index 0000000..bef8d59 --- /dev/null +++ b/gpxe/src/arch/x86/Makefile.efi @@ -0,0 +1,28 @@ +# -*- makefile -*- : Force emacs to use Makefile mode + +# The EFI linker script +# +LDSCRIPT = arch/x86/scripts/efi.lds + +# Retain relocation information for elf2efi +# +LDFLAGS += -q -S + +# Media types. +# +NON_AUTO_MEDIA += efi +NON_AUTO_MEDIA += efidrv + +# Rules for building EFI files +# +$(BIN)/%.efi : $(BIN)/%.efi.tmp $(ELF2EFI) + $(QM)$(ECHO) " [FINISH] $@" + $(Q)$(ELF2EFI) --subsystem=10 $< $@ + +$(BIN)/%.efidrv : $(BIN)/%.efidrv.tmp $(ELF2EFI) + $(QM)$(ECHO) " [FINISH] $@" + $(Q)$(ELF2EFI) --subsystem=11 $< $@ + +$(BIN)/%.efirom : $(BIN)/%.efidrv $(EFIROM) + $(QM)$(ECHO) " [FINISH] $@" + $(Q)$(EFIROM) -v $(TGT_PCI_VENDOR) -d $(TGT_PCI_DEVICE) $< $@ diff --git a/gpxe/src/arch/x86/core/pcidirect.c b/gpxe/src/arch/x86/core/pcidirect.c new file mode 100644 index 0000000..db17215 --- /dev/null +++ b/gpxe/src/arch/x86/core/pcidirect.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include + +/** @file + * + * PCI configuration space access via Type 1 accesses + * + */ + +/** + * Prepare for Type 1 PCI configuration space access + * + * @v pci PCI device + * @v where Location within PCI configuration space + */ +void pcidirect_prepare ( struct pci_device *pci, int where ) { + outl ( ( 0x80000000 | ( pci->bus << 16 ) | ( pci->devfn << 8 ) | + ( where & ~3 ) ), PCIDIRECT_CONFIG_ADDRESS ); +} + +PROVIDE_PCIAPI_INLINE ( direct, pci_max_bus ); +PROVIDE_PCIAPI_INLINE ( direct, pci_read_config_byte ); +PROVIDE_PCIAPI_INLINE ( direct, pci_read_config_word ); +PROVIDE_PCIAPI_INLINE ( direct, pci_read_config_dword ); +PROVIDE_PCIAPI_INLINE ( direct, pci_write_config_byte ); +PROVIDE_PCIAPI_INLINE ( direct, pci_write_config_word ); +PROVIDE_PCIAPI_INLINE ( direct, pci_write_config_dword ); diff --git a/gpxe/src/arch/x86/core/x86_string.c b/gpxe/src/arch/x86/core/x86_string.c new file mode 100644 index 0000000..c0224c7 --- /dev/null +++ b/gpxe/src/arch/x86/core/x86_string.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2007 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. + */ + +/** @file + * + * Optimised string operations + * + */ + +#include + +/** + * Copy memory area + * + * @v dest Destination address + * @v src Source address + * @v len Length + * @ret dest Destination address + */ +void * __memcpy ( void *dest, const void *src, size_t len ) { + void *edi = dest; + const void *esi = src; + int discard_ecx; + + /* We often do large dword-aligned and dword-length block + * moves. Using movsl rather than movsb speeds these up by + * around 32%. + */ + if ( len >> 2 ) { + __asm__ __volatile__ ( "rep movsl" + : "=&D" ( edi ), "=&S" ( esi ), + "=&c" ( discard_ecx ) + : "0" ( edi ), "1" ( esi ), + "2" ( len >> 2 ) + : "memory" ); + } + if ( len & 0x02 ) { + __asm__ __volatile__ ( "movsw" : "=&D" ( edi ), "=&S" ( esi ) + : "0" ( edi ), "1" ( esi ) : "memory" ); + } + if ( len & 0x01 ) { + __asm__ __volatile__ ( "movsb" : "=&D" ( edi ), "=&S" ( esi ) + : "0" ( edi ), "1" ( esi ) : "memory" ); + } + return dest; +} diff --git a/gpxe/src/arch/x86/include/bits/pci_io.h b/gpxe/src/arch/x86/include/bits/pci_io.h new file mode 100644 index 0000000..0fbb439 --- /dev/null +++ b/gpxe/src/arch/x86/include/bits/pci_io.h @@ -0,0 +1,13 @@ +#ifndef _BITS_PCI_IO_H +#define _BITS_PCI_IO_H + +/** @file + * + * i386-specific PCI I/O API implementations + * + */ + +#include +#include + +#endif /* _BITS_PCI_IO_H */ diff --git a/gpxe/src/arch/x86/include/bits/string.h b/gpxe/src/arch/x86/include/bits/string.h new file mode 100644 index 0000000..42ddedd --- /dev/null +++ b/gpxe/src/arch/x86/include/bits/string.h @@ -0,0 +1,250 @@ +#ifndef ETHERBOOT_BITS_STRING_H +#define ETHERBOOT_BITS_STRING_H +/* + * Taken from Linux /usr/include/asm/string.h + * All except memcpy, memmove, memset and memcmp removed. + * + * Non-standard memswap() function added because it saves quite a bit + * of code (mbrown@fensystems.co.uk). + */ + +/* + * This string-include defines all string functions as inline + * functions. Use gcc. It also assumes ds=es=data space, this should be + * normal. Most of the string-functions are rather heavily hand-optimized, + * see especially strtok,strstr,str[c]spn. They should work, but are not + * very easy to understand. Everything is done entirely within the register + * set, making the functions fast and clean. String instructions have been + * used through-out, making for "slightly" unclear code :-) + * + * NO Copyright (C) 1991, 1992 Linus Torvalds, + * consider these trivial functions to be PD. + */ + +#define __HAVE_ARCH_MEMCPY + +extern void * __memcpy ( void *dest, const void *src, size_t len ); + +#if 0 +static inline __attribute__ (( always_inline )) void * +__memcpy ( void *dest, const void *src, size_t len ) { + int d0, d1, d2; + __asm__ __volatile__ ( "rep ; movsb" + : "=&c" ( d0 ), "=&S" ( d1 ), "=&D" ( d2 ) + : "0" ( len ), "1" ( src ), "2" ( dest ) + : "memory" ); + return dest; +} +#endif + +static inline __attribute__ (( always_inline )) void * +__constant_memcpy ( void *dest, const void *src, size_t len ) { + union { + uint32_t u32[2]; + uint16_t u16[4]; + uint8_t u8[8]; + } __attribute__ (( __may_alias__ )) *dest_u = dest; + const union { + uint32_t u32[2]; + uint16_t u16[4]; + uint8_t u8[8]; + } __attribute__ (( __may_alias__ )) *src_u = src; + const void *esi; + void *edi; + + switch ( len ) { + case 0 : /* 0 bytes */ + return dest; + /* + * Single-register moves; these are always better than a + * string operation. We can clobber an arbitrary two + * registers (data, source, dest can re-use source register) + * instead of being restricted to esi and edi. There's also a + * much greater potential for optimising with nearby code. + * + */ + case 1 : /* 4 bytes */ + dest_u->u8[0] = src_u->u8[0]; + return dest; + case 2 : /* 6 bytes */ + dest_u->u16[0] = src_u->u16[0]; + return dest; + case 4 : /* 4 bytes */ + dest_u->u32[0] = src_u->u32[0]; + return dest; + /* + * Double-register moves; these are probably still a win. + * + */ + case 3 : /* 12 bytes */ + dest_u->u16[0] = src_u->u16[0]; + dest_u->u8[2] = src_u->u8[2]; + return dest; + case 5 : /* 10 bytes */ + dest_u->u32[0] = src_u->u32[0]; + dest_u->u8[4] = src_u->u8[4]; + return dest; + case 6 : /* 12 bytes */ + dest_u->u32[0] = src_u->u32[0]; + dest_u->u16[2] = src_u->u16[2]; + return dest; + case 8 : /* 10 bytes */ + dest_u->u32[0] = src_u->u32[0]; + dest_u->u32[1] = src_u->u32[1]; + return dest; + } + + /* Even if we have to load up esi and edi ready for a string + * operation, we can sometimes save space by using multiple + * single-byte "movs" operations instead of loading up ecx and + * using "rep movsb". + * + * "load ecx, rep movsb" is 7 bytes, plus an average of 1 byte + * to allow for saving/restoring ecx 50% of the time. + * + * "movsl" and "movsb" are 1 byte each, "movsw" is two bytes. + * (In 16-bit mode, "movsl" is 2 bytes and "movsw" is 1 byte, + * but "movsl" moves twice as much data, so it balances out). + * + * The cutoff point therefore occurs around 26 bytes; the byte + * requirements for each method are: + * + * len 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 + * #bytes (ecx) 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 + * #bytes (no ecx) 4 5 6 7 5 6 7 8 6 7 8 9 7 8 9 10 + */ + + esi = src; + edi = dest; + + if ( len >= 26 ) + return __memcpy ( dest, src, len ); + + if ( len >= 6*4 ) + __asm__ __volatile__ ( "movsl" : "=&D" ( edi ), "=&S" ( esi ) + : "0" ( edi ), "1" ( esi ) : "memory" ); + if ( len >= 5*4 ) + __asm__ __volatile__ ( "movsl" : "=&D" ( edi ), "=&S" ( esi ) + : "0" ( edi ), "1" ( esi ) : "memory" ); + if ( len >= 4*4 ) + __asm__ __volatile__ ( "movsl" : "=&D" ( edi ), "=&S" ( esi ) + : "0" ( edi ), "1" ( esi ) : "memory" ); + if ( len >= 3*4 ) + __asm__ __volatile__ ( "movsl" : "=&D" ( edi ), "=&S" ( esi ) + : "0" ( edi ), "1" ( esi ) : "memory" ); + if ( len >= 2*4 ) + __asm__ __volatile__ ( "movsl" : "=&D" ( edi ), "=&S" ( esi ) + : "0" ( edi ), "1" ( esi ) : "memory" ); + if ( len >= 1*4 ) + __asm__ __volatile__ ( "movsl" : "=&D" ( edi ), "=&S" ( esi ) + : "0" ( edi ), "1" ( esi ) : "memory" ); + if ( ( len % 4 ) >= 2 ) + __asm__ __volatile__ ( "movsw" : "=&D" ( edi ), "=&S" ( esi ) + : "0" ( edi ), "1" ( esi ) : "memory" ); + if ( ( len % 2 ) >= 1 ) + __asm__ __volatile__ ( "movsb" : "=&D" ( edi ), "=&S" ( esi ) + : "0" ( edi ), "1" ( esi ) : "memory" ); + + return dest; +} + +#define memcpy( dest, src, len ) \ + ( __builtin_constant_p ( (len) ) ? \ + __constant_memcpy ( (dest), (src), (len) ) : \ + __memcpy ( (dest), (src), (len) ) ) + +#define __HAVE_ARCH_MEMMOVE +static inline void * memmove(void * dest,const void * src, size_t n) +{ +int d0, d1, d2; +if (dest + +/** @file + * + * PCI configuration space access via PCI BIOS + * + */ + +#ifdef PCIAPI_PCBIOS +#define PCIAPI_PREFIX_pcbios +#else +#define PCIAPI_PREFIX_pcbios __pcbios_ +#endif + +struct pci_device; + +#define PCIBIOS_INSTALLATION_CHECK 0xb1010000 +#define PCIBIOS_READ_CONFIG_BYTE 0xb1080000 +#define PCIBIOS_READ_CONFIG_WORD 0xb1090000 +#define PCIBIOS_READ_CONFIG_DWORD 0xb10a0000 +#define PCIBIOS_WRITE_CONFIG_BYTE 0xb10b0000 +#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c0000 +#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d0000 + +extern int pcibios_read ( struct pci_device *pci, uint32_t command, + uint32_t *value ); +extern int pcibios_write ( struct pci_device *pci, uint32_t command, + uint32_t value ); + +/** + * Read byte from PCI configuration space via PCI BIOS + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( pcbios, pci_read_config_byte ) ( struct pci_device *pci, + unsigned int where, + uint8_t *value ) { + uint32_t tmp; + int rc; + + rc = pcibios_read ( pci, PCIBIOS_READ_CONFIG_BYTE | where, &tmp ); + *value = tmp; + return rc; +} + +/** + * Read word from PCI configuration space via PCI BIOS + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( pcbios, pci_read_config_word ) ( struct pci_device *pci, + unsigned int where, + uint16_t *value ) { + uint32_t tmp; + int rc; + + rc = pcibios_read ( pci, PCIBIOS_READ_CONFIG_WORD | where, &tmp ); + *value = tmp; + return rc; +} + +/** + * Read dword from PCI configuration space via PCI BIOS + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( pcbios, pci_read_config_dword ) ( struct pci_device *pci, + unsigned int where, + uint32_t *value ) { + return pcibios_read ( pci, PCIBIOS_READ_CONFIG_DWORD | where, value ); +} + +/** + * Write byte to PCI configuration space via PCI BIOS + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( pcbios, pci_write_config_byte ) ( struct pci_device *pci, + unsigned int where, + uint8_t value ) { + return pcibios_write ( pci, PCIBIOS_WRITE_CONFIG_BYTE | where, value ); +} + +/** + * Write word to PCI configuration space via PCI BIOS + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( pcbios, pci_write_config_word ) ( struct pci_device *pci, + unsigned int where, + uint16_t value ) { + return pcibios_write ( pci, PCIBIOS_WRITE_CONFIG_WORD | where, value ); +} + +/** + * Write dword to PCI configuration space via PCI BIOS + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( pcbios, pci_write_config_dword ) ( struct pci_device *pci, + unsigned int where, + uint32_t value ) { + return pcibios_write ( pci, PCIBIOS_WRITE_CONFIG_DWORD | where, value); +} + +#endif /* _GPXE_PCIBIOS_H */ diff --git a/gpxe/src/arch/x86/include/gpxe/pcidirect.h b/gpxe/src/arch/x86/include/gpxe/pcidirect.h new file mode 100644 index 0000000..fe433c6 --- /dev/null +++ b/gpxe/src/arch/x86/include/gpxe/pcidirect.h @@ -0,0 +1,139 @@ +#ifndef _PCIDIRECT_H +#define _PCIDIRECT_H + +#include +#include + +#ifdef PCIAPI_DIRECT +#define PCIAPI_PREFIX_direct +#else +#define PCIAPI_PREFIX_direct __direct_ +#endif + +/** @file + * + * PCI configuration space access via Type 1 accesses + * + */ + +#define PCIDIRECT_CONFIG_ADDRESS 0xcf8 +#define PCIDIRECT_CONFIG_DATA 0xcfc + +struct pci_device; + +extern void pcidirect_prepare ( struct pci_device *pci, int where ); + +/** + * Determine maximum PCI bus number within system + * + * @ret max_bus Maximum bus number + */ +static inline __always_inline int +PCIAPI_INLINE ( direct, pci_max_bus ) ( void ) { + /* No way to work this out via Type 1 accesses */ + return 0xff; +} + +/** + * Read byte from PCI configuration space via Type 1 access + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( direct, pci_read_config_byte ) ( struct pci_device *pci, + unsigned int where, + uint8_t *value ) { + pcidirect_prepare ( pci, where ); + *value = inb ( PCIDIRECT_CONFIG_DATA + ( where & 3 ) ); + return 0; +} + +/** + * Read word from PCI configuration space via Type 1 access + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( direct, pci_read_config_word ) ( struct pci_device *pci, + unsigned int where, + uint16_t *value ) { + pcidirect_prepare ( pci, where ); + *value = inw ( PCIDIRECT_CONFIG_DATA + ( where & 2 ) ); + return 0; +} + +/** + * Read dword from PCI configuration space via Type 1 access + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( direct, pci_read_config_dword ) ( struct pci_device *pci, + unsigned int where, + uint32_t *value ) { + pcidirect_prepare ( pci, where ); + *value = inl ( PCIDIRECT_CONFIG_DATA ); + return 0; +} + +/** + * Write byte to PCI configuration space via Type 1 access + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( direct, pci_write_config_byte ) ( struct pci_device *pci, + unsigned int where, + uint8_t value ) { + pcidirect_prepare ( pci, where ); + outb ( value, PCIDIRECT_CONFIG_DATA + ( where & 3 ) ); + return 0; +} + +/** + * Write word to PCI configuration space via Type 1 access + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( direct, pci_write_config_word ) ( struct pci_device *pci, + unsigned int where, + uint16_t value ) { + pcidirect_prepare ( pci, where ); + outw ( value, PCIDIRECT_CONFIG_DATA + ( where & 2 ) ); + return 0; +} + +/** + * Write dword to PCI configuration space via Type 1 access + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( direct, pci_write_config_dword ) ( struct pci_device *pci, + unsigned int where, + uint32_t value ) { + pcidirect_prepare ( pci, where ); + outl ( value, PCIDIRECT_CONFIG_DATA ); + return 0; +} + +#endif /* _PCIDIRECT_H */ diff --git a/gpxe/src/arch/x86/interface/efi/efix86_nap.c b/gpxe/src/arch/x86/interface/efi/efix86_nap.c new file mode 100644 index 0000000..45e99a6 --- /dev/null +++ b/gpxe/src/arch/x86/interface/efi/efix86_nap.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include + +/** @file + * + * gPXE CPU sleeping API for EFI + * + */ + +/** + * Sleep until next interrupt + * + */ +static void efix86_cpu_nap ( void ) { + /* + * I can't find any EFI API that allows us to put the CPU to + * sleep. The CpuSleep() function is defined in CpuLib.h, but + * isn't part of any exposed protocol so we have no way to + * call it. + * + * The EFI shell doesn't seem to bother sleeping the CPU; it + * just sits there idly burning power. + * + */ + __asm__ __volatile__ ( "hlt" ); +} + +PROVIDE_NAP ( efix86, cpu_nap, efix86_cpu_nap ); diff --git a/gpxe/src/arch/x86/prefix/efidrvprefix.c b/gpxe/src/arch/x86/prefix/efidrvprefix.c new file mode 100644 index 0000000..5f63158 --- /dev/null +++ b/gpxe/src/arch/x86/prefix/efidrvprefix.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2009 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. + */ + +#include +#include +#include + +/** + * EFI entry point + * + * @v image_handle Image handle + * @v systab System table + * @ret efirc EFI return status code + */ +EFI_STATUS EFIAPI _start ( EFI_HANDLE image_handle, + EFI_SYSTEM_TABLE *systab ) { + EFI_STATUS efirc; + + /* Initialise EFI environment */ + if ( ( efirc = efi_init ( image_handle, systab ) ) != 0 ) + return efirc; + + /* Initialise gPXE environment */ + initialise(); + startup(); + + /* Install SNP driver and return */ + return RC_TO_EFIRC ( efi_snp_install () ); +} diff --git a/gpxe/src/arch/x86/prefix/efiprefix.c b/gpxe/src/arch/x86/prefix/efiprefix.c new file mode 100644 index 0000000..b05b744 --- /dev/null +++ b/gpxe/src/arch/x86/prefix/efiprefix.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2009 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. + */ + +#include +#include + +/** + * EFI entry point + * + * @v image_handle Image handle + * @v systab System table + * @ret efirc EFI return status code + */ +EFI_STATUS EFIAPI _start ( EFI_HANDLE image_handle, + EFI_SYSTEM_TABLE *systab ) { + EFI_STATUS efirc; + + /* Initialise EFI environment */ + if ( ( efirc = efi_init ( image_handle, systab ) ) != 0 ) + return efirc; + + /* Call to main() */ + return RC_TO_EFIRC ( main () ); +} diff --git a/gpxe/src/arch/x86/scripts/efi.lds b/gpxe/src/arch/x86/scripts/efi.lds new file mode 100644 index 0000000..aac3105 --- /dev/null +++ b/gpxe/src/arch/x86/scripts/efi.lds @@ -0,0 +1,105 @@ +/* -*- sh -*- */ + +/* + * Linker script for EFI images + * + */ + +ENTRY ( _start ) + +SECTIONS { + + /* The file starts at a virtual address of zero, and sections are + * contiguous. Each section is aligned to at least _max_align, + * which defaults to 32. Load addresses are equal to virtual + * addresses. + */ + + _max_align = 32; + + /* Allow plenty of space for file headers */ + . = 0x1000; + + /* + * The text section + * + */ + + . = ALIGN ( _max_align ); + .text : { + _text = .; + *(.text) + *(.text.*) + _etext = .; + } + + /* + * The rodata section + * + */ + + . = ALIGN ( _max_align ); + .rodata : { + _rodata = .; + *(.rodata) + *(.rodata.*) + _erodata = .; + } + + /* + * The data section + * + */ + + . = ALIGN ( _max_align ); + .data : { + _data = .; + *(.data) + *(.data.*) + *(SORT(.tbl.*)) /* Various tables. See include/tables.h */ + _edata = .; + } + + /* + * The bss section + * + */ + + . = ALIGN ( _max_align ); + .bss : { + _bss = .; + *(.bss) + *(.bss.*) + *(COMMON) + _ebss = .; + } + + /* + * Weak symbols that need zero values if not otherwise defined + * + */ + + .weak 0x0 : { + _weak = .; + *(.weak) + _eweak = .; + } + _assert = ASSERT ( ( _weak == _eweak ), ".weak is non-zero length" ); + + /* + * Dispose of the comment and note sections to make the link map + * easier to read + * + */ + + /DISCARD/ : { + *(.comment) + *(.comment.*) + *(.note) + *(.note.*) + *(.eh_frame) + *(.eh_frame.*) + *(.rel) + *(.rel.*) + } +} diff --git a/gpxe/src/arch/x86_64/Makefile b/gpxe/src/arch/x86_64/Makefile new file mode 100644 index 0000000..d2c2ff5 --- /dev/null +++ b/gpxe/src/arch/x86_64/Makefile @@ -0,0 +1,41 @@ +# Code size reduction. +# +CFLAGS += -fstrength-reduce -fomit-frame-pointer + +# Code size reduction. gcc3 needs a different syntax to gcc2 if you +# want to avoid spurious warnings. +# +CFLAGS += -falign-jumps=1 -falign-loops=1 -falign-functions=1 + +# Use %rip-relative addressing wherever possible. +# +CFLAGS += -fpie + +# Force 64-bit code +# +CFLAGS += -m64 +ASFLAGS += --64 +LDFLAGS += -m elf_x86_64 + +# EFI requires -fshort-wchar, and nothing else currently uses wchar_t +# +CFLAGS += -fshort-wchar + +# We need to undefine the default macro "i386" when compiling .S +# files, otherwise ".arch i386" translates to ".arch 1"... +# +CFLAGS += -Ui386 + +# x86_64-specific directories containing source files +# +SRCDIRS += arch/x86_64/prefix + +# Include common x86 Makefile +# +MAKEDEPS += arch/x86/Makefile +include arch/x86/Makefile + +# Include platform-specific Makefile +# +MAKEDEPS += arch/x86_64/Makefile.$(PLATFORM) +include arch/x86_64/Makefile.$(PLATFORM) diff --git a/gpxe/src/arch/x86_64/Makefile.efi b/gpxe/src/arch/x86_64/Makefile.efi new file mode 100644 index 0000000..26b7127 --- /dev/null +++ b/gpxe/src/arch/x86_64/Makefile.efi @@ -0,0 +1,14 @@ +# -*- makefile -*- : Force emacs to use Makefile mode + +# EFI probably doesn't guarantee us a red zone, so let's not rely on it. +# +CFLAGS += -mno-red-zone + +# Specify EFI image builder +# +ELF2EFI = $(ELF2EFI64) + +# Include generic EFI Makefile +# +MAKEDEPS += arch/x86/Makefile.efi +include arch/x86/Makefile.efi diff --git a/gpxe/src/arch/x86_64/include/bits/byteswap.h b/gpxe/src/arch/x86_64/include/bits/byteswap.h new file mode 100644 index 0000000..9ed85e8 --- /dev/null +++ b/gpxe/src/arch/x86_64/include/bits/byteswap.h @@ -0,0 +1,22 @@ +#ifndef _BITS_BYTESWAP_H +#define _BITS_BYTESWAP_H + +static inline __attribute__ (( always_inline, const )) uint16_t +__bswap_variable_16 ( uint16_t x ) { + __asm__ ( "xchgb %b0,%h0" : "=Q" ( x ) : "0" ( x ) ); + return x; +} + +static inline __attribute__ (( always_inline, const )) uint32_t +__bswap_variable_32 ( uint32_t x ) { + __asm__ ( "bswapl %k0" : "=r" ( x ) : "0" ( x ) ); + return x; +} + +static inline __attribute__ (( always_inline, const )) uint64_t +__bswap_variable_64 ( uint64_t x ) { + __asm__ ( "bswapq %q0" : "=r" ( x ) : "0" ( x ) ); + return x; +} + +#endif /* _BITS_BYTESWAP_H */ diff --git a/gpxe/src/arch/x86_64/include/bits/compiler.h b/gpxe/src/arch/x86_64/include/bits/compiler.h new file mode 100644 index 0000000..51a7eaa --- /dev/null +++ b/gpxe/src/arch/x86_64/include/bits/compiler.h @@ -0,0 +1,14 @@ +#ifndef _BITS_COMPILER_H +#define _BITS_COMPILER_H + +#ifndef ASSEMBLY + +/** Declare a function with standard calling conventions */ +#define __asmcall __attribute__ (( regparm(0) )) + +/** Declare a function with libgcc implicit linkage */ +#define __libgcc + +#endif /* ASSEMBLY */ + +#endif /* _BITS_COMPILER_H */ diff --git a/gpxe/src/arch/x86_64/include/bits/endian.h b/gpxe/src/arch/x86_64/include/bits/endian.h new file mode 100644 index 0000000..413e702 --- /dev/null +++ b/gpxe/src/arch/x86_64/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/gpxe/src/arch/x86_64/include/bits/errfile.h b/gpxe/src/arch/x86_64/include/bits/errfile.h new file mode 100644 index 0000000..dcda26b --- /dev/null +++ b/gpxe/src/arch/x86_64/include/bits/errfile.h @@ -0,0 +1,11 @@ +#ifndef _BITS_ERRFILE_H +#define _BITS_ERRFILE_H + +/** + * @addtogroup errfile Error file identifiers + * @{ + */ + +/** @} */ + +#endif /* _BITS_ERRFILE_H */ diff --git a/gpxe/src/arch/x86_64/include/bits/io.h b/gpxe/src/arch/x86_64/include/bits/io.h new file mode 100644 index 0000000..921fdcc --- /dev/null +++ b/gpxe/src/arch/x86_64/include/bits/io.h @@ -0,0 +1,10 @@ +#ifndef _BITS_IO_H +#define _BITS_IO_H + +/** @file + * + * x86_64-specific I/O API implementations + * + */ + +#endif /* _BITS_IO_H */ diff --git a/gpxe/src/arch/x86_64/include/bits/nap.h b/gpxe/src/arch/x86_64/include/bits/nap.h new file mode 100644 index 0000000..0a0c8a2 --- /dev/null +++ b/gpxe/src/arch/x86_64/include/bits/nap.h @@ -0,0 +1,12 @@ +#ifndef _BITS_NAP_H +#define _BITS_NAP_H + +/** @file + * + * x86_64-specific CPU sleeping API implementations + * + */ + +#include + +#endif /* _BITS_MAP_H */ diff --git a/gpxe/src/arch/x86_64/include/bits/smbios.h b/gpxe/src/arch/x86_64/include/bits/smbios.h new file mode 100644 index 0000000..2f0118d --- /dev/null +++ b/gpxe/src/arch/x86_64/include/bits/smbios.h @@ -0,0 +1,10 @@ +#ifndef _BITS_SMBIOS_H +#define _BITS_SMBIOS_H + +/** @file + * + * i386-specific SMBIOS API implementations + * + */ + +#endif /* _BITS_SMBIOS_H */ diff --git a/gpxe/src/arch/x86_64/include/bits/stdint.h b/gpxe/src/arch/x86_64/include/bits/stdint.h new file mode 100644 index 0000000..23bae9c --- /dev/null +++ b/gpxe/src/arch/x86_64/include/bits/stdint.h @@ -0,0 +1,21 @@ +#ifndef _BITS_STDINT_H +#define _BITS_STDINT_H + +typedef unsigned long size_t; +typedef signed long ssize_t; +typedef signed long off_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int 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; + +typedef unsigned long physaddr_t; +typedef unsigned long intptr_t; + +#endif /* _BITS_STDINT_H */ diff --git a/gpxe/src/arch/x86_64/include/bits/timer.h b/gpxe/src/arch/x86_64/include/bits/timer.h new file mode 100644 index 0000000..dfa6c27 --- /dev/null +++ b/gpxe/src/arch/x86_64/include/bits/timer.h @@ -0,0 +1,10 @@ +#ifndef _BITS_TIMER_H +#define _BITS_TIMER_H + +/** @file + * + * x86_64-specific timer API implementations + * + */ + +#endif /* _BITS_TIMER_H */ diff --git a/gpxe/src/arch/x86_64/include/bits/uaccess.h b/gpxe/src/arch/x86_64/include/bits/uaccess.h new file mode 100644 index 0000000..4558292 --- /dev/null +++ b/gpxe/src/arch/x86_64/include/bits/uaccess.h @@ -0,0 +1,10 @@ +#ifndef _BITS_UACCESS_H +#define _BITS_UACCESS_H + +/** @file + * + * x86_64-specific user access API implementations + * + */ + +#endif /* _BITS_UACCESS_H */ diff --git a/gpxe/src/arch/x86_64/include/bits/umalloc.h b/gpxe/src/arch/x86_64/include/bits/umalloc.h new file mode 100644 index 0000000..12bf949 --- /dev/null +++ b/gpxe/src/arch/x86_64/include/bits/umalloc.h @@ -0,0 +1,10 @@ +#ifndef _BITS_UMALLOC_H +#define _BITS_UMALLOC_H + +/** @file + * + * x86_64-specific user memory allocation API implementations + * + */ + +#endif /* _BITS_UMALLOC_H */ diff --git a/gpxe/src/arch/x86_64/include/gdbmach.h b/gpxe/src/arch/x86_64/include/gdbmach.h new file mode 100644 index 0000000..fcf8e94 --- /dev/null +++ b/gpxe/src/arch/x86_64/include/gdbmach.h @@ -0,0 +1,51 @@ +#ifndef GDBMACH_H +#define GDBMACH_H + +/** @file + * + * GDB architecture specifics + * + * This file declares functions for manipulating the machine state and + * debugging context. + * + */ + +#include + +typedef unsigned long gdbreg_t; + +/* The register snapshot, this must be in sync with interrupt handler and the + * GDB protocol. */ +enum { + // STUB: don't expect this to work! + GDBMACH_EIP, + GDBMACH_EFLAGS, + GDBMACH_NREGS, + GDBMACH_SIZEOF_REGS = GDBMACH_NREGS * sizeof ( gdbreg_t ) +}; + +/* Breakpoint types */ +enum { + GDBMACH_BPMEM, + GDBMACH_BPHW, + GDBMACH_WATCH, + GDBMACH_RWATCH, + GDBMACH_AWATCH, +}; + +static inline void gdbmach_set_pc ( gdbreg_t *regs, gdbreg_t pc ) { + regs [ GDBMACH_EIP ] = pc; +} + +static inline void gdbmach_set_single_step ( gdbreg_t *regs, int step ) { + regs [ GDBMACH_EFLAGS ] &= ~( 1 << 8 ); /* Trace Flag (TF) */ + regs [ GDBMACH_EFLAGS ] |= ( step << 8 ); +} + +static inline void gdbmach_breakpoint ( void ) { + __asm__ __volatile__ ( "int $3\n" ); +} + +extern int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, int enable ); + +#endif /* GDBMACH_H */ diff --git a/gpxe/src/arch/x86_64/include/limits.h b/gpxe/src/arch/x86_64/include/limits.h new file mode 100644 index 0000000..8cf87b4 --- /dev/null +++ b/gpxe/src/arch/x86_64/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 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/gpxe/src/config/.gitignore b/gpxe/src/config/.gitignore new file mode 100644 index 0000000..8e94f32 --- /dev/null +++ b/gpxe/src/config/.gitignore @@ -0,0 +1 @@ +.buildserial.* diff --git a/gpxe/src/config/console.h b/gpxe/src/config/console.h new file mode 100644 index 0000000..b4ea1dd --- /dev/null +++ b/gpxe/src/config/console.h @@ -0,0 +1,21 @@ +#ifndef CONFIG_CONSOLE_H +#define CONFIG_CONSOLE_H + +/** @file + * + * Console configuration + * + * These options specify the console types that Etherboot will use for + * interaction with the user. + * + */ + +#include + +//#define CONSOLE_PCBIOS /* Default BIOS console */ +//#define CONSOLE_SERIAL /* Serial port */ +//#define CONSOLE_DIRECT_VGA /* Direct access to VGA card */ +//#define CONSOLE_BTEXT /* Who knows what this does? */ +//#define CONSOLE_PC_KBD /* Direct access to PC keyboard */ + +#endif /* CONFIG_CONSOLE_H */ diff --git a/gpxe/src/config/defaults.h b/gpxe/src/config/defaults.h new file mode 100644 index 0000000..1f55ef3 --- /dev/null +++ b/gpxe/src/config/defaults.h @@ -0,0 +1,8 @@ +#ifndef CONFIG_DEFAULTS_H +#define CONFIG_DEFAULTS_H + +#define CONFIG_DEFAULTS(_platform) + +#include CONFIG_DEFAULTS(PLATFORM) + +#endif /* CONFIG_DEFAULTS_H */ diff --git a/gpxe/src/config/defaults/efi.h b/gpxe/src/config/defaults/efi.h new file mode 100644 index 0000000..fe38fd0 --- /dev/null +++ b/gpxe/src/config/defaults/efi.h @@ -0,0 +1,21 @@ +#ifndef CONFIG_DEFAULTS_EFI_H +#define CONFIG_DEFAULTS_EFI_H + +/** @file + * + * Configuration defaults for EFI + * + */ + +#define UACCESS_EFI +#define IOAPI_EFI +#define PCIAPI_EFI +#define CONSOLE_EFI +#define TIMER_EFI +#define NAP_EFIX86 +#define UMALLOC_EFI +#define SMBIOS_EFI + +#define IMAGE_EFI /* EFI image support */ + +#endif /* CONFIG_DEFAULTS_EFI_H */ diff --git a/gpxe/src/config/defaults/pcbios.h b/gpxe/src/config/defaults/pcbios.h new file mode 100644 index 0000000..4359e1a --- /dev/null +++ b/gpxe/src/config/defaults/pcbios.h @@ -0,0 +1,29 @@ +#ifndef CONFIG_DEFAULTS_PCBIOS_H +#define CONFIG_DEFAULTS_PCBIOS_H + +/** @file + * + * Configuration defaults for PCBIOS + * + */ + +#define UACCESS_LIBRM +#define IOAPI_X86 +#define PCIAPI_PCBIOS +#define TIMER_PCBIOS +#define CONSOLE_PCBIOS +#define NAP_PCBIOS +#define UMALLOC_MEMTOP +#define SMBIOS_PCBIOS + +#define IMAGE_ELF /* ELF image support */ +#define IMAGE_MULTIBOOT /* MultiBoot image support */ +#define IMAGE_PXE /* PXE image support */ +#define IMAGE_SCRIPT /* gPXE script image support */ +#define IMAGE_BZIMAGE /* Linux bzImage image support */ +#define IMAGE_COMBOOT /* SYSLINUX COMBOOT image support */ + +#define SANBOOT_PROTO_ISCSI /* iSCSI protocol */ +#define SANBOOT_PROTO_AOE /* AoE protocol */ + +#endif /* CONFIG_DEFAULTS_PCBIOS_H */ diff --git a/gpxe/src/config/general.h b/gpxe/src/config/general.h new file mode 100644 index 0000000..a3d563c --- /dev/null +++ b/gpxe/src/config/general.h @@ -0,0 +1,123 @@ +#ifndef CONFIG_GENERAL_H +#define CONFIG_GENERAL_H + +/** @file + * + * General configuration + * + */ + +#include + +/* + * Branding + * + * Vendors may use these strings to add their own branding to gPXE. + * PRODUCT_NAME is displayed prior to any gPXE branding in startup + * messages, and PRODUCT_SHORT_NAME is used where a brief product + * label is required (e.g. in BIOS boot selection menus). + * + * To minimise end-user confusion, it's probably a good idea to either + * make PRODUCT_SHORT_NAME a substring of PRODUCT_NAME or leave it as + * "gPXE". + * + */ +#define PRODUCT_NAME "" +#define PRODUCT_SHORT_NAME "gPXE" + +/* + * Timer configuration + * + */ +#define BANNER_TIMEOUT 20 /* Tenths of a second for which the shell + banner should appear */ + +/* + * Network protocols + * + */ + +#define NET_PROTO_IPV4 /* IPv4 protocol */ + +/* + * Download protocols + * + */ + +#define DOWNLOAD_PROTO_TFTP /* Trivial File Transfer Protocol */ +#define DOWNLOAD_PROTO_NFS /* Network File System */ +#define DOWNLOAD_PROTO_HTTP /* Hypertext Transfer Protocol */ +#define DOWNLOAD_PROTO_HTTPS /* Secure Hypertext Transfer Protocol */ +#define DOWNLOAD_PROTO_FTP /* File Transfer Protocol */ +#undef DOWNLOAD_PROTO_TFTM /* Multicast Trivial File Transfer Protocol */ +#undef DOWNLOAD_PROTO_SLAM /* Scalable Local Area Multicast */ +#undef DOWNLOAD_PROTO_FSP /* FSP? */ + +/* + * SAN boot protocols + * + */ + +//#undef SANBOOT_PROTO_ISCSI /* iSCSI protocol */ +//#undef SANBOOT_PROTO_AOE /* AoE protocol */ + +/* + * Name resolution modules + * + */ + +#define DNS_RESOLVER /* DNS resolver */ +#undef NMB_RESOLVER /* NMB resolver */ + +/* + * Image types + * + * Etherboot supports various image formats. Select whichever ones + * you want to use. + * + */ +//#define IMAGE_NBI /* NBI image support */ +//#define IMAGE_ELF /* ELF image support */ +//#define IMAGE_FREEBSD /* FreeBSD kernel image support */ +//#define IMAGE_MULTIBOOT /* MultiBoot image support */ +//#define IMAGE_AOUT /* a.out image support */ +//#define IMAGE_WINCE /* WinCE image support */ +//#define IMAGE_PXE /* PXE image support */ +//#define IMAGE_SCRIPT /* gPXE script image support */ +//#define IMAGE_BZIMAGE /* Linux bzImage image support */ +//#define IMAGE_COMBOOT /* SYSLINUX COMBOOT image support */ +//#define IMAGE_EFI /* EFI image support */ + +/* + * Command-line commands to include + * + */ +#define AUTOBOOT_CMD /* Automatic booting */ +#define NVO_CMD /* Non-volatile option storage commands */ +#define CONFIG_CMD /* Option configuration console */ +#define IFMGMT_CMD /* Interface management commands */ +#define ROUTE_CMD /* Routing table management commands */ +#define IMAGE_CMD /* Image management commands */ +#define DHCP_CMD /* DHCP management commands */ +#define SANBOOT_CMD /* SAN boot commands */ +#define LOGIN_CMD /* Login command */ + +/* + * Obscure configuration options + * + * You probably don't need to touch these. + * + */ + +#undef BUILD_SERIAL /* Include an automatic build serial + * number. Add "bs" to the list of + * make targets. For example: + * "make bin/rtl8139.dsk bs" */ +#undef BUILD_ID /* Include a custom build ID string, + * e.g "test-foo" */ +#undef NULL_TRAP /* Attempt to catch NULL function calls */ +#undef GDBSERIAL /* Remote GDB debugging over serial */ +#undef GDBUDP /* Remote GDB debugging over UDP + * (both may be set) */ + +#endif /* CONFIG_GENERAL_H */ diff --git a/gpxe/src/config/ioapi.h b/gpxe/src/config/ioapi.h new file mode 100644 index 0000000..7726a0f --- /dev/null +++ b/gpxe/src/config/ioapi.h @@ -0,0 +1,15 @@ +#ifndef CONFIG_IOAPI_H +#define CONFIG_IOAPI_H + +/** @file + * + * I/O API configuration + * + */ + +#include + +//#undef PCIAPI_PCBIOS /* Access via PCI BIOS */ +//#define PCIAPI_DIRECT /* Direct access via Type 1 accesses */ + +#endif /* CONFIG_IOAPI_H */ diff --git a/gpxe/src/config/isa.h b/gpxe/src/config/isa.h new file mode 100644 index 0000000..523be1c --- /dev/null +++ b/gpxe/src/config/isa.h @@ -0,0 +1,15 @@ +#ifndef CONFIG_ISA_H +#define CONFIG_ISA_H + +/** @file + * + * ISA probe address configuration + * + * You can override the list of addresses that will be probed by any + * ISA drivers. + * + */ +#undef ISA_PROBE_ADDRS /* e.g. 0x200, 0x300 */ +#undef ISA_PROBE_ONLY /* Do not probe any other addresses */ + +#endif /* CONFIG_ISA_H */ diff --git a/gpxe/src/config/nap.h b/gpxe/src/config/nap.h new file mode 100644 index 0000000..8648d92 --- /dev/null +++ b/gpxe/src/config/nap.h @@ -0,0 +1,15 @@ +#ifndef CONFIG_NAP_H +#define CONFIG_NAP_H + +/** @file + * + * CPU sleeping + * + */ + +#include + +//#undef NAP_PCBIOS +//#define NAP_NULL + +#endif /* CONFIG_NAP_H */ diff --git a/gpxe/src/config/serial.h b/gpxe/src/config/serial.h new file mode 100644 index 0000000..984a7a9 --- /dev/null +++ b/gpxe/src/config/serial.h @@ -0,0 +1,28 @@ +#ifndef CONFIG_SERIAL_H +#define CONFIG_SERIAL_H + +/** @file + * + * Serial port configuration + * + * These options affect the operation of the serial console. They + * take effect only if the serial console is included using the + * CONSOLE_SERIAL option. + * + */ + +#define COMCONSOLE 0x3f8 /* I/O port address */ + +/* Keep settings from a previous user of the serial port (e.g. lilo or + * LinuxBIOS), ignoring COMSPEED, COMDATA, COMPARITY and COMSTOP. + */ +#undef COMPRESERVE + +#ifndef COMPRESERVE +#define COMSPEED 115200 /* Baud rate */ +#define COMDATA 8 /* Data bits */ +#define COMPARITY 0 /* Parity: 0=None, 1=Odd, 2=Even */ +#define COMSTOP 1 /* Stop bits */ +#endif + +#endif /* CONFIG_SERIAL_H */ diff --git a/gpxe/src/config/timer.h b/gpxe/src/config/timer.h new file mode 100644 index 0000000..7c3f352 --- /dev/null +++ b/gpxe/src/config/timer.h @@ -0,0 +1,15 @@ +#ifndef CONFIG_TIMER_H +#define CONFIG_TIMER_H + +/** @file + * + * Timer configuration. + * + */ + +#include + +//#undef TIMER_PCBIOS +//#define TIMER_RDTSC + +#endif /* CONFIG_TIMER_H */ diff --git a/gpxe/src/config/umalloc.h b/gpxe/src/config/umalloc.h new file mode 100644 index 0000000..de4019e --- /dev/null +++ b/gpxe/src/config/umalloc.h @@ -0,0 +1,12 @@ +#ifndef CONFIG_UMALLOC_H +#define CONFIG_UMALLOC_H + +/** @file + * + * User memory allocation API configuration + * + */ + +#include + +#endif /* CONFIG_UMALLOC_H */ diff --git a/gpxe/src/core/acpi.c b/gpxe/src/core/acpi.c new file mode 100644 index 0000000..94b7b2a --- /dev/null +++ b/gpxe/src/core/acpi.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2006 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. + */ + +#include + +/** @file + * + * ACPI support functions + * + */ + +/** + * Fix up ACPI table checksum + * + * @v acpi ACPI table header + */ +void acpi_fix_checksum ( struct acpi_description_header *acpi ) { + unsigned int i = 0; + uint8_t sum = 0; + + for ( i = 0 ; i < acpi->length ; i++ ) { + sum += *( ( ( uint8_t * ) acpi ) + i ); + } + acpi->checksum -= sum; +} diff --git a/gpxe/src/core/ansiesc.c b/gpxe/src/core/ansiesc.c new file mode 100644 index 0000000..6b820ad --- /dev/null +++ b/gpxe/src/core/ansiesc.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include + +/** @file + * + * ANSI escape sequences + * + */ + +/** + * Call ANSI escape sequence handler + * + * @v handlers List of escape sequence handlers + * @v function Control function identifier + * @v count Parameter count + * @v params Parameter list + */ +static void ansiesc_call_handler ( struct ansiesc_handler *handlers, + unsigned int function, int count, + int params[] ) { + struct ansiesc_handler *handler; + + for ( handler = handlers ; handler->function ; handler++ ) { + if ( handler->function == function ) { + handler->handle ( count, params ); + break; + } + } +} + +/** + * Process character that may be part of ANSI escape sequence + * + * @v ctx ANSI escape sequence context + * @v c Character + * @ret c Original character if not part of escape sequence + * @ret <0 Character was part of escape sequence + * + * ANSI escape sequences will be plucked out of the character stream + * and interpreted; once complete they will be passed to the + * appropriate handler if one exists in this ANSI escape sequence + * context. + * + * In the interests of code size, we are rather liberal about the + * sequences we are prepared to accept as valid. + */ +int ansiesc_process ( struct ansiesc_context *ctx, int c ) { + if ( ctx->count == 0 ) { + if ( c == ESC ) { + /* First byte of CSI : begin escape sequence */ + ctx->count = 1; + memset ( ctx->params, 0xff, sizeof ( ctx->params ) ); + ctx->function = 0; + return -1; + } else { + /* Normal character */ + return c; + } + } else { + if ( c == '[' ) { + /* Second byte of CSI : do nothing */ + } else if ( ( c >= '0' ) && ( c <= '9' ) ) { + /* Parameter Byte : part of a parameter value */ + int *param = &ctx->params[ctx->count - 1]; + if ( *param < 0 ) + *param = 0; + *param = ( ( *param * 10 ) + ( c - '0' ) ); + } else if ( c == ';' ) { + /* Parameter Byte : parameter delimiter */ + ctx->count++; + if ( ctx->count > ( sizeof ( ctx->params ) / + sizeof ( ctx->params[0] ) ) ) { + /* Excessive parameters : abort sequence */ + ctx->count = 0; + DBG ( "Too many parameters in ANSI escape " + "sequence\n" ); + } + } else if ( ( c >= 0x20 ) && ( c <= 0x2f ) ) { + /* Intermediate Byte */ + ctx->function <<= 8; + ctx->function |= c; + } else { + /* Treat as Final Byte. Zero ctx->count before + * calling handler to avoid potential infinite loops. + */ + int count = ctx->count; + ctx->count = 0; + ctx->function <<= 8; + ctx->function |= c; + ansiesc_call_handler ( ctx->handlers, ctx->function, + count, ctx->params ); + } + return -1; + } +} diff --git a/gpxe/src/core/asprintf.c b/gpxe/src/core/asprintf.c new file mode 100644 index 0000000..94d7e7c --- /dev/null +++ b/gpxe/src/core/asprintf.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include + +/** + * Write a formatted string to newly allocated memory. + * + * @v strp Pointer to hold allocated string + * @v fmt Format string + * @v args Arguments corresponding to the format string + * @ret len Length of formatted string + */ +int vasprintf ( char **strp, const char *fmt, va_list args ) { + size_t len; + va_list args_tmp; + + /* Calculate length needed for string */ + va_copy ( args_tmp, args ); + len = ( vsnprintf ( NULL, 0, fmt, args_tmp ) + 1 ); + va_end ( args_tmp ); + + /* Allocate and fill string */ + *strp = malloc ( len ); + if ( ! *strp ) + return -ENOMEM; + return vsnprintf ( *strp, len, fmt, args ); +} + +/** + * Write a formatted string to newly allocated memory. + * + * @v strp Pointer to hold allocated string + * @v fmt Format string + * @v ... Arguments corresponding to the format string + * @ret len Length of formatted string + */ +int asprintf ( char **strp, const char *fmt, ... ) { + va_list args; + int len; + + va_start ( args, fmt ); + len = vasprintf ( strp, fmt, args ); + va_end ( args ); + return len; +} diff --git a/gpxe/src/core/base64.c b/gpxe/src/core/base64.c new file mode 100644 index 0000000..e54821e --- /dev/null +++ b/gpxe/src/core/base64.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2009 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. + */ + +#include +#include +#include +#include + +/** @file + * + * Base64 encoding + * + */ + +static const char base64[64] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/** + * Base64-encode a string + * + * @v raw Raw string + * @v encoded Buffer for encoded string + * + * The buffer must be the correct length for the encoded string. Use + * something like + * + * char buf[ base64_encoded_len ( strlen ( raw ) ) + 1 ]; + * + * (the +1 is for the terminating NUL) to provide a buffer of the + * correct size. + */ +void base64_encode ( const char *raw, char *encoded ) { + const uint8_t *raw_bytes = ( ( const uint8_t * ) raw ); + uint8_t *encoded_bytes = ( ( uint8_t * ) encoded ); + size_t raw_bit_len = ( 8 * strlen ( raw ) ); + unsigned int bit; + unsigned int tmp; + + for ( bit = 0 ; bit < raw_bit_len ; bit += 6 ) { + tmp = ( ( raw_bytes[ bit / 8 ] << ( bit % 8 ) ) | + ( raw_bytes[ bit / 8 + 1 ] >> ( 8 - ( bit % 8 ) ) ) ); + tmp = ( ( tmp >> 2 ) & 0x3f ); + *(encoded_bytes++) = base64[tmp]; + } + for ( ; ( bit % 8 ) != 0 ; bit += 6 ) + *(encoded_bytes++) = '='; + *(encoded_bytes++) = '\0'; + + DBG ( "Base64-encoded \"%s\" as \"%s\"\n", raw, encoded ); + assert ( strlen ( encoded ) == base64_encoded_len ( strlen ( raw ) ) ); +} diff --git a/gpxe/src/core/basename.c b/gpxe/src/core/basename.c new file mode 100644 index 0000000..7340c0d --- /dev/null +++ b/gpxe/src/core/basename.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2007 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. + */ + +/** + * @file + * + * Get base name of path + * + */ + +#include +#include + +/** + * Return base name from path + * + * @v path Full path + * @ret basename Base name + */ +char * basename ( char *path ) { + char *basename; + + basename = strrchr ( path, '/' ); + return ( basename ? ( basename + 1 ) : path ); +} + +/** + * Return directory name from path + * + * @v path Full path + * @ret dirname Directory name + * + * Note that this function may modify its argument. + */ +char * dirname ( char *path ) { + char *separator; + + separator = strrchr ( path, '/' ); + if ( separator == path ) { + return "/"; + } else if ( separator ) { + *separator = 0; + return path; + } else { + return "."; + } +} diff --git a/gpxe/src/core/bitmap.c b/gpxe/src/core/bitmap.c new file mode 100644 index 0000000..d026647 --- /dev/null +++ b/gpxe/src/core/bitmap.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include + +/** @file + * + * Bitmaps for multicast downloads + * + */ + +/** + * Resize bitmap + * + * @v bitmap Bitmap + * @v new_length New length of bitmap, in bits + * @ret rc Return status code + */ +int bitmap_resize ( struct bitmap *bitmap, unsigned int new_length ) { + unsigned int old_num_blocks; + unsigned int new_num_blocks; + size_t new_size; + bitmap_block_t *new_blocks; + + old_num_blocks = BITMAP_INDEX ( bitmap->length + BITMAP_BLKSIZE - 1 ); + new_num_blocks = BITMAP_INDEX ( new_length + BITMAP_BLKSIZE - 1 ); + + if ( old_num_blocks != new_num_blocks ) { + new_size = ( new_num_blocks * sizeof ( bitmap->blocks[0] ) ); + new_blocks = realloc ( bitmap->blocks, new_size ); + if ( ! new_blocks ) { + DBGC ( bitmap, "Bitmap %p could not resize to %d " + "bits\n", bitmap, new_length ); + return -ENOMEM; + } + bitmap->blocks = new_blocks; + } + bitmap->length = new_length; + + while ( old_num_blocks < new_num_blocks ) { + bitmap->blocks[old_num_blocks++] = 0; + } + + DBGC ( bitmap, "Bitmap %p resized to %d bits\n", bitmap, new_length ); + return 0; +} + +/** + * Test bit in bitmap + * + * @v bitmap Bitmap + * @v bit Bit index + * @ret is_set Bit is set + */ +int bitmap_test ( struct bitmap *bitmap, unsigned int bit ) { + unsigned int index = BITMAP_INDEX ( bit ); + bitmap_block_t mask = BITMAP_MASK ( bit ); + + if ( bit >= bitmap->length ) + return 0; + return ( bitmap->blocks[index] & mask ); +} + +/** + * Set bit in bitmap + * + * @v bitmap Bitmap + * @v bit Bit index + */ +void bitmap_set ( struct bitmap *bitmap, unsigned int bit ) { + unsigned int index = BITMAP_INDEX ( bit ); + bitmap_block_t mask = BITMAP_MASK ( bit ); + + DBGC ( bitmap, "Bitmap %p setting bit %d\n", bitmap, bit ); + + /* Update bitmap */ + bitmap->blocks[index] |= mask; + + /* Update first gap counter */ + while ( bitmap_test ( bitmap, bitmap->first_gap ) ) { + bitmap->first_gap++; + } +} diff --git a/gpxe/src/core/bitops.c b/gpxe/src/core/bitops.c new file mode 100644 index 0000000..53abaae --- /dev/null +++ b/gpxe/src/core/bitops.c @@ -0,0 +1,11 @@ +#include + +int __flsl ( long x ) { + unsigned long value = x; + int ls = 0; + + for ( ls = 0 ; value ; ls++ ) { + value >>= 1; + } + return ls; +} diff --git a/gpxe/src/core/btext.c b/gpxe/src/core/btext.c new file mode 100644 index 0000000..122d89f --- /dev/null +++ b/gpxe/src/core/btext.c @@ -0,0 +1,5039 @@ +#if 0 + +/* + * Procedures for drawing on the screen early on in the boot process. + * + * Benjamin Herrenschmidt + * + * move to LinuxBIOS by LYH yhlu@tyan.com + * move to Etherboot by LYH + */ + +#include "console.h" +#include +#include + +#undef __BIG_ENDIAN +#if 0 +#define __LITTLE_ENDIAN +#endif + +#include "btext.h" + +//#define NO_SCROLL + +#ifndef NO_SCROLL +static void scrollscreen(void); +#endif + +static void draw_byte(const unsigned char c, u32 locX, u32 locY); +#if 0 +static void draw_byte_32(const unsigned char *bits, u32 *base, u32 rb); +static void draw_byte_16(const unsigned char *bits, u32 *base, u32 rb); +#endif +static void draw_byte_8(const unsigned char *bits, u32 *base, u32 rb); + +static u32 g_loc_X; +static u32 g_loc_Y; +static u32 g_max_loc_X; +static u32 g_max_loc_Y; + +#define CHAR_256 0 + +#if CHAR_256==1 +#define cmapsz (16*256) +#else +#define cmapsz (16*96) +#endif + +static const unsigned char vga_font[cmapsz]; + +u32 boot_text_mapped; + +boot_infos_t disp_bi; + +#define BTEXT +#define BTDATA + + +/* This function will enable the early boot text when doing OF booting. This + * way, xmon output should work too + */ +static void +btext_setup_display(u32 width, u32 height, u32 depth, u32 pitch, + unsigned long address) +{ + boot_infos_t* bi = &disp_bi; + + g_loc_X = 0; + g_loc_Y = 0; + g_max_loc_X = width / 8; + g_max_loc_Y = height / 16; +// bi->logicalDisplayBase = (unsigned char *)address; + bi->dispDeviceBase = address; + bi->dispDeviceRowBytes = pitch; + bi->dispDeviceDepth = depth; + bi->dispDeviceRect[0] = bi->dispDeviceRect[1] = 0; + bi->dispDeviceRect[2] = width; + bi->dispDeviceRect[3] = height; + boot_text_mapped = 0; +} + +/* Here's a small text engine to use during early boot + * or for debugging purposes + * + * todo: + * + * - build some kind of vgacon with it to enable early printk + * - move to a separate file + * - add a few video driver hooks to keep in sync with display + * changes. + */ + +static void +map_boot_text(void) +{ + boot_infos_t *bi = &disp_bi; + + if (bi->dispDeviceBase == 0) + return; + + boot_text_mapped = 0; + + bi->logicalDisplayBase = phys_to_virt(bi->dispDeviceBase); + + boot_text_mapped = 1; +} + +/* Calc the base address of a given point (x,y) */ +static unsigned char * BTEXT +calc_base(boot_infos_t *bi, u32 x, u32 y) +{ + unsigned char *base; + base = bi->logicalDisplayBase; +#if 0 + /* Ummm... which moron wrote this? */ + if (base == 0) + base = bi->dispDeviceBase; +#endif + base += (x + bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3); + base += (y + bi->dispDeviceRect[1]) * bi->dispDeviceRowBytes; + return base; +} + + +static void BTEXT btext_clearscreen(void) +{ + boot_infos_t* bi = &disp_bi; + u32 *base = (u32 *)calc_base(bi, 0, 0); + u32 width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * + (bi->dispDeviceDepth >> 3)) >> 2; + u32 i,j; + + for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++) + { + u32 *ptr = base; + for(j=width; j; --j) + *(ptr++) = 0; + base += (bi->dispDeviceRowBytes >> 2); + } +} + +#if 0 +__inline__ void dcbst(const void* addr) +{ + __asm__ __volatile__ ("dcbst 0,%0" :: "r" (addr)); +} + +static void BTEXT btext_flushscreen(void) +{ + boot_infos_t* bi = &disp_bi; + u32 *base = (unsigned long *)calc_base(bi, 0, 0); + u32 width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * + (bi->dispDeviceDepth >> 3)) >> 2; + u32 i,j; + + for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++) + { + u32 *ptr = base; + for(j=width; j>0; j-=8) { + dcbst(ptr); + ptr += 8; + } + base += (bi->dispDeviceRowBytes >> 2); + } +} +#endif + + +#ifndef NO_SCROLL +static BTEXT void +scrollscreen(void) +{ + boot_infos_t* bi = &disp_bi; + u32 *src = (u32 *)calc_base(bi,0,16); + u32 *dst = (u32 *)calc_base(bi,0,0); + u32 width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * + (bi->dispDeviceDepth >> 3)) >> 2; + u32 i,j; + + for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1] - 16); i++) + { + u32 *src_ptr = src; + u32 *dst_ptr = dst; + for(j=width; j; --j) + *(dst_ptr++) = *(src_ptr++); + src += (bi->dispDeviceRowBytes >> 2); + dst += (bi->dispDeviceRowBytes >> 2); + } + for (i=0; i<16; i++) + { + u32 *dst_ptr = dst; + for(j=width; j; --j) + *(dst_ptr++) = 0; + dst += (bi->dispDeviceRowBytes >> 2); + } +} +#endif /* ndef NO_SCROLL */ + +static void BTEXT btext_drawchar(char c) +{ + u32 cline = 0; + + if (!boot_text_mapped) + return; + + switch (c) { + case '\b': + if (g_loc_X > 0) + --g_loc_X; + break; + case '\t': + g_loc_X = (g_loc_X & -8) + 8; + break; + case '\r': + g_loc_X = 0; + break; + case '\n': + g_loc_X = 0; + g_loc_Y++; + cline = 1; + break; + default: + draw_byte(c, g_loc_X++, g_loc_Y); + } + if (g_loc_X >= g_max_loc_X) { + g_loc_X = 0; + g_loc_Y++; + cline = 1; + } +#ifndef NO_SCROLL + while (g_loc_Y >= g_max_loc_Y) { + scrollscreen(); + g_loc_Y--; + } +#else + /* wrap around from bottom to top of screen so we don't + waste time scrolling each line. -- paulus. */ + if (g_loc_Y >= g_max_loc_Y) + g_loc_Y = 0; + if (cline) { + for (x = 0; x < g_max_loc_X; ++x) + draw_byte(' ', x, g_loc_Y); + } +#endif +} +#if 0 +static void BTEXT +btext_drawstring(const char *c) +{ + if (!boot_text_mapped) + return; + while (*c) + btext_drawchar(*c++); +} +static void BTEXT +btext_drawhex(u32 v) +{ + static char hex_table[] = "0123456789abcdef"; + + if (!boot_text_mapped) + return; + btext_drawchar(hex_table[(v >> 28) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 24) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 20) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 16) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 12) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 8) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 4) & 0x0000000FUL]); + btext_drawchar(hex_table[(v >> 0) & 0x0000000FUL]); + btext_drawchar(' '); +} +#endif + +static void BTEXT +draw_byte(const unsigned char c, u32 locX, u32 locY) +{ + boot_infos_t* bi = &disp_bi; + unsigned char *base = calc_base(bi, locX << 3, locY << 4); +#if CHAR_256==1 + unsigned const char *font = &vga_font[(((u32)c)) * 16]; +#else + unsigned const char *font = &vga_font[(((u32)c-0x20)) * 16]; +#endif + + u32 rb = bi->dispDeviceRowBytes; + + switch(bi->dispDeviceDepth) { +#if 0 + case 24: + case 32: + draw_byte_32(font, (u32 *)base, rb); + break; + case 15: + case 16: + draw_byte_16(font, (u32 *)base, rb); + break; +#endif + case 8: + draw_byte_8(font, (u32 *)base, rb); + break; + } +} +static u32 expand_bits_8[16] BTDATA = { +#if defined(__BIG_ENDIAN) + 0x00000000,0x000000ff,0x0000ff00,0x0000ffff, + 0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff, + 0xff000000,0xff0000ff,0xff00ff00,0xff00ffff, + 0xffff0000,0xffff00ff,0xffffff00,0xffffffff +#elif defined(__LITTLE_ENDIAN) + 0x00000000,0xff000000,0x00ff0000,0xffff0000, + 0x0000ff00,0xff00ff00,0x00ffff00,0xffffff00, + 0x000000ff,0xff0000ff,0x00ff00ff,0xffff00ff, + 0x0000ffff,0xff00ffff,0x00ffffff,0xffffffff +#else +#error FIXME: No endianness?? +#endif +}; +#if 0 +static const u32 expand_bits_16[4] BTDATA = { +#if defined(__BIG_ENDIAN) + 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff +#elif defined(__LITTLE_ENDIAN) + 0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff +#else +#error FIXME: No endianness?? +#endif +}; +#endif +#if 0 +static void BTEXT +draw_byte_32(const unsigned char *font, u32 *base, u32 rb) +{ + u32 l, bits; + u32 fg = 0xFFFFFFFF; + u32 bg = 0x00000000; + + for (l = 0; l < 16; ++l) + { + bits = *font++; + base[0] = (-(bits >> 7) & fg) ^ bg; + base[1] = (-((bits >> 6) & 1) & fg) ^ bg; + base[2] = (-((bits >> 5) & 1) & fg) ^ bg; + base[3] = (-((bits >> 4) & 1) & fg) ^ bg; + base[4] = (-((bits >> 3) & 1) & fg) ^ bg; + base[5] = (-((bits >> 2) & 1) & fg) ^ bg; + base[6] = (-((bits >> 1) & 1) & fg) ^ bg; + base[7] = (-(bits & 1) & fg) ^ bg; + base = (u32 *) ((char *)base + rb); + } +} + +static void BTEXT +draw_byte_16(const unsigned char *font, u32 *base, u32 rb) +{ + u32 l, bits; + u32 fg = 0xFFFFFFFF; + u32 bg = 0x00000000; + u32 *eb = expand_bits_16; + + for (l = 0; l < 16; ++l) + { + bits = *font++; + base[0] = (eb[bits >> 6] & fg) ^ bg; + base[1] = (eb[(bits >> 4) & 3] & fg) ^ bg; + base[2] = (eb[(bits >> 2) & 3] & fg) ^ bg; + base[3] = (eb[bits & 3] & fg) ^ bg; + base = (u32 *) ((char *)base + rb); + } +} +#endif +static void BTEXT +draw_byte_8(const unsigned char *font, u32 *base, u32 rb) +{ + u32 l, bits; + u32 fg = 0x0F0F0F0F; + u32 bg = 0x00000000; + u32 *eb = expand_bits_8; + + for (l = 0; l < 16; ++l) + { + bits = *font++; + base[0] = (eb[bits >> 4] & fg) ^ bg; + base[1] = (eb[bits & 0xf] & fg) ^ bg; + base = (u32 *) ((char *)base + rb); + } +} + +static void btext_init(void) +{ +#if 0 +// for debug +#define frame_buffer 0xfc000000 +#else + uint32_t frame_buffer;// 0xfc000000 + + struct pci_device dev; + + #warning "pci_find_device_x no longer exists; use find_pci_device instead" + /* pci_find_device_x(0x1002, 0x4752, 0, &dev); */ + if(dev.vendor==0) return; // no fb + + frame_buffer = (uint32_t)dev.membase; +#endif + + btext_setup_display(640, 480, 8, 640,frame_buffer); + btext_clearscreen(); + map_boot_text(); +} +static void btext_putc(int c) +{ + btext_drawchar((unsigned char)c); +} + +struct console_driver btext_console __console_driver = { + .putchar = btext_putc, + .disabled = 1, +}; + +//come from linux/drivers/video/font-8x16.c +/**********************************************/ +/* */ +/* Font file generated by cpi2fnt */ +/* */ +/**********************************************/ + + +static const unsigned char vga_font[cmapsz] BTDATA = { +#if CHAR_256==1 + /* 0 0x00 '^@' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 1 0x01 '^A' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x81, /* 10000001 */ + 0xa5, /* 10100101 */ + 0x81, /* 10000001 */ + 0x81, /* 10000001 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0x81, /* 10000001 */ + 0x81, /* 10000001 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 2 0x02 '^B' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xdb, /* 11011011 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 3 0x03 '^C' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 4 0x04 '^D' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 5 0x05 '^E' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0xe7, /* 11100111 */ + 0xe7, /* 11100111 */ + 0xe7, /* 11100111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 6 0x06 '^F' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 7 0x07 '^G' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 8 0x08 '^H' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xe7, /* 11100111 */ + 0xc3, /* 11000011 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 9 0x09 '^I' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x42, /* 01000010 */ + 0x42, /* 01000010 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 10 0x0a '^J' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0x99, /* 10011001 */ + 0xbd, /* 10111101 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0xc3, /* 11000011 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 11 0x0b '^K' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1e, /* 00011110 */ + 0x0e, /* 00001110 */ + 0x1a, /* 00011010 */ + 0x32, /* 00110010 */ + 0x78, /* 01111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 12 0x0c '^L' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 13 0x0d '^M' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x33, /* 00110011 */ + 0x3f, /* 00111111 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x70, /* 01110000 */ + 0xf0, /* 11110000 */ + 0xe0, /* 11100000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 14 0x0e '^N' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x63, /* 01100011 */ + 0x63, /* 01100011 */ + 0x63, /* 01100011 */ + 0x67, /* 01100111 */ + 0xe7, /* 11100111 */ + 0xe6, /* 11100110 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 15 0x0f '^O' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xdb, /* 11011011 */ + 0x3c, /* 00111100 */ + 0xe7, /* 11100111 */ + 0x3c, /* 00111100 */ + 0xdb, /* 11011011 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 16 0x10 '^P' */ + 0x00, /* 00000000 */ + 0x80, /* 10000000 */ + 0xc0, /* 11000000 */ + 0xe0, /* 11100000 */ + 0xf0, /* 11110000 */ + 0xf8, /* 11111000 */ + 0xfe, /* 11111110 */ + 0xf8, /* 11111000 */ + 0xf0, /* 11110000 */ + 0xe0, /* 11100000 */ + 0xc0, /* 11000000 */ + 0x80, /* 10000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 17 0x11 '^Q' */ + 0x00, /* 00000000 */ + 0x02, /* 00000010 */ + 0x06, /* 00000110 */ + 0x0e, /* 00001110 */ + 0x1e, /* 00011110 */ + 0x3e, /* 00111110 */ + 0xfe, /* 11111110 */ + 0x3e, /* 00111110 */ + 0x1e, /* 00011110 */ + 0x0e, /* 00001110 */ + 0x06, /* 00000110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 18 0x12 '^R' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 19 0x13 '^S' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 20 0x14 '^T' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7f, /* 01111111 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7b, /* 01111011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 21 0x15 '^U' */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x0c, /* 00001100 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 22 0x16 '^V' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 23 0x17 '^W' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 24 0x18 '^X' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 25 0x19 '^Y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 26 0x1a '^Z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 27 0x1b '^[' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xfe, /* 11111110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 28 0x1c '^\' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 29 0x1d '^]' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x28, /* 00101000 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x28, /* 00101000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 30 0x1e '^^' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 31 0x1f '^_' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ +#endif + /* 32 0x20 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 33 0x21 '!' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 34 0x22 '"' */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x24, /* 00100100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 35 0x23 '#' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 36 0x24 '$' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc2, /* 11000010 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x86, /* 10000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 37 0x25 '%' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc2, /* 11000010 */ + 0xc6, /* 11000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc6, /* 11000110 */ + 0x86, /* 10000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 38 0x26 '&' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 39 0x27 ''' */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 40 0x28 '(' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 41 0x29 ')' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 42 0x2a '*' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0xff, /* 11111111 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 43 0x2b '+' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 44 0x2c ',' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 45 0x2d '-' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 46 0x2e '.' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 47 0x2f '/' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x02, /* 00000010 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0x80, /* 10000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 48 0x30 '0' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 49 0x31 '1' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x38, /* 00111000 */ + 0x78, /* 01111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 50 0x32 '2' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 51 0x33 '3' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x3c, /* 00111100 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 52 0x34 '4' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x1c, /* 00011100 */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x1e, /* 00011110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 53 0x35 '5' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 54 0x36 '6' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 55 0x37 '7' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 56 0x38 '8' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 57 0x39 '9' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 58 0x3a ':' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 59 0x3b ';' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 60 0x3c '<' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 61 0x3d '=' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 62 0x3e '>' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 63 0x3f '?' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 64 0x40 '@' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xdc, /* 11011100 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 65 0x41 'A' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 66 0x42 'B' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 67 0x43 'C' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc2, /* 11000010 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc2, /* 11000010 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 68 0x44 'D' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 69 0x45 'E' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x66, /* 01100110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x60, /* 01100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 70 0x46 'F' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x66, /* 01100110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 71 0x47 'G' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc2, /* 11000010 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xde, /* 11011110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x66, /* 01100110 */ + 0x3a, /* 00111010 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 72 0x48 'H' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 73 0x49 'I' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 74 0x4a 'J' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1e, /* 00011110 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 75 0x4b 'K' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xe6, /* 11100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x78, /* 01111000 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 76 0x4c 'L' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf0, /* 11110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 77 0x4d 'M' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xee, /* 11101110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 78 0x4e 'N' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xe6, /* 11100110 */ + 0xf6, /* 11110110 */ + 0xfe, /* 11111110 */ + 0xde, /* 11011110 */ + 0xce, /* 11001110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 79 0x4f 'O' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 80 0x50 'P' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 81 0x51 'Q' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xde, /* 11011110 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0x0e, /* 00001110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 82 0x52 'R' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 83 0x53 'S' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x38, /* 00111000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 84 0x54 'T' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x5a, /* 01011010 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 85 0x55 'U' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 86 0x56 'V' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 87 0x57 'W' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0xee, /* 11101110 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 88 0x58 'X' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 89 0x59 'Y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 90 0x5a 'Z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x86, /* 10000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc2, /* 11000010 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 91 0x5b '[' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 92 0x5c '\' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x80, /* 10000000 */ + 0xc0, /* 11000000 */ + 0xe0, /* 11100000 */ + 0x70, /* 01110000 */ + 0x38, /* 00111000 */ + 0x1c, /* 00011100 */ + 0x0e, /* 00001110 */ + 0x06, /* 00000110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 93 0x5d ']' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 94 0x5e '^' */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 95 0x5f '_' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 96 0x60 '`' */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 97 0x61 'a' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 98 0x62 'b' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 99 0x63 'c' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 100 0x64 'd' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1c, /* 00011100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 101 0x65 'e' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 102 0x66 'f' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1c, /* 00011100 */ + 0x36, /* 00110110 */ + 0x32, /* 00110010 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 103 0x67 'g' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* 104 0x68 'h' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x6c, /* 01101100 */ + 0x76, /* 01110110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 105 0x69 'i' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 106 0x6a 'j' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x0e, /* 00001110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 107 0x6b 'k' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x78, /* 01111000 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 108 0x6c 'l' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 109 0x6d 'm' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xec, /* 11101100 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 110 0x6e 'n' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 111 0x6f 'o' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 112 0x70 'p' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* 113 0x71 'q' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x1e, /* 00011110 */ + 0x00, /* 00000000 */ + + /* 114 0x72 'r' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x76, /* 01110110 */ + 0x66, /* 01100110 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 115 0x73 's' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x38, /* 00111000 */ + 0x0c, /* 00001100 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 116 0x74 't' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0xfc, /* 11111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x36, /* 00110110 */ + 0x1c, /* 00011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 117 0x75 'u' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 118 0x76 'v' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 119 0x77 'w' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 120 0x78 'x' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 121 0x79 'y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + + /* 122 0x7a 'z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xcc, /* 11001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 123 0x7b '{' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 124 0x7c '|' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 125 0x7d '}' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 126 0x7e '~' */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 127 0x7f '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ +#if CHAR_256256==1 + /* 128 0x80 '€' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc2, /* 11000010 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc2, /* 11000010 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 129 0x81 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 130 0x82 '‚' */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 131 0x83 'ƒ' */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 132 0x84 '„' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 133 0x85 '…' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 134 0x86 '†' */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 135 0x87 '‡' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 136 0x88 'ˆ' */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 137 0x89 '‰' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 138 0x8a 'Š' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 139 0x8b '‹' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 140 0x8c 'Œ' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 141 0x8d '' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 142 0x8e 'Ž' */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 143 0x8f '' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 144 0x90 '' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x66, /* 01100110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 145 0x91 '‘' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xec, /* 11101100 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x7e, /* 01111110 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x6e, /* 01101110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 146 0x92 '’' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3e, /* 00111110 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xfe, /* 11111110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xce, /* 11001110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 147 0x93 '“' */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 148 0x94 '”' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 149 0x95 '•' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 150 0x96 '–' */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 151 0x97 '—' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 152 0x98 '˜' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* 153 0x99 '™' */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 154 0x9a 'š' */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 155 0x9b '›' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 156 0x9c 'œ' */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x64, /* 01100100 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xe6, /* 11100110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 157 0x9d '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 158 0x9e 'ž' */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xf8, /* 11111000 */ + 0xc4, /* 11000100 */ + 0xcc, /* 11001100 */ + 0xde, /* 11011110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 159 0x9f 'Ÿ' */ + 0x00, /* 00000000 */ + 0x0e, /* 00001110 */ + 0x1b, /* 00011011 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 160 0xa0 ' ' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 161 0xa1 '¡' */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 162 0xa2 '¢' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 163 0xa3 '£' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 164 0xa4 '¤' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 165 0xa5 '¥' */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xe6, /* 11100110 */ + 0xf6, /* 11110110 */ + 0xfe, /* 11111110 */ + 0xde, /* 11011110 */ + 0xce, /* 11001110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 166 0xa6 '¦' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x3e, /* 00111110 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 167 0xa7 '§' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 168 0xa8 '¨' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 169 0xa9 '©' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 170 0xaa 'ª' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 171 0xab '«' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0xe0, /* 11100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xdc, /* 11011100 */ + 0x86, /* 10000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x3e, /* 00111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 172 0xac '¬' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0xe0, /* 11100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x66, /* 01100110 */ + 0xce, /* 11001110 */ + 0x9a, /* 10011010 */ + 0x3f, /* 00111111 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 173 0xad '­' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 174 0xae '®' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x36, /* 00110110 */ + 0x6c, /* 01101100 */ + 0xd8, /* 11011000 */ + 0x6c, /* 01101100 */ + 0x36, /* 00110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 175 0xaf '¯' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xd8, /* 11011000 */ + 0x6c, /* 01101100 */ + 0x36, /* 00110110 */ + 0x6c, /* 01101100 */ + 0xd8, /* 11011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 176 0xb0 '°' */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + + /* 177 0xb1 '±' */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + + /* 178 0xb2 '²' */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + + /* 179 0xb3 '³' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 180 0xb4 '´' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 181 0xb5 'µ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 182 0xb6 '¶' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 183 0xb7 '·' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 184 0xb8 '¸' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 185 0xb9 '¹' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x06, /* 00000110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 186 0xba 'º' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 187 0xbb '»' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 188 0xbc '¼' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x06, /* 00000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 189 0xbd '½' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 190 0xbe '¾' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 191 0xbf '¿' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 192 0xc0 'À' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 193 0xc1 'Á' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 194 0xc2 'Â' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 195 0xc3 'Ã' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 196 0xc4 'Ä' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 197 0xc5 'Å' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 198 0xc6 'Æ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 199 0xc7 'Ç' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 200 0xc8 'È' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x30, /* 00110000 */ + 0x3f, /* 00111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 201 0xc9 'É' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x30, /* 00110000 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 202 0xca 'Ê' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf7, /* 11110111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 203 0xcb 'Ë' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xf7, /* 11110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 204 0xcc 'Ì' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x30, /* 00110000 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 205 0xcd 'Í' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 206 0xce 'Î' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf7, /* 11110111 */ + 0x00, /* 00000000 */ + 0xf7, /* 11110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 207 0xcf 'Ï' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 208 0xd0 'Ð' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 209 0xd1 'Ñ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 210 0xd2 'Ò' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 211 0xd3 'Ó' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x3f, /* 00111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 212 0xd4 'Ô' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 213 0xd5 'Õ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 214 0xd6 'Ö' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 215 0xd7 '×' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xff, /* 11111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 216 0xd8 'Ø' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 217 0xd9 'Ù' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 218 0xda 'Ú' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 219 0xdb 'Û' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 220 0xdc 'Ü' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 221 0xdd 'Ý' */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + + /* 222 0xde 'Þ' */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + + /* 223 0xdf 'ß' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 224 0xe0 'à' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xdc, /* 11011100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 225 0xe1 'á' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xd8, /* 11011000 */ + 0xcc, /* 11001100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 226 0xe2 'â' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 227 0xe3 'ã' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 228 0xe4 'ä' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 229 0xe5 'å' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 230 0xe6 'æ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + + /* 231 0xe7 'ç' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 232 0xe8 'è' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 233 0xe9 'é' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 234 0xea 'ê' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xee, /* 11101110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 235 0xeb 'ë' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1e, /* 00011110 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x3e, /* 00111110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 236 0xec 'ì' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 237 0xed 'í' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x03, /* 00000011 */ + 0x06, /* 00000110 */ + 0x7e, /* 01111110 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0xf3, /* 11110011 */ + 0x7e, /* 01111110 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 238 0xee 'î' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1c, /* 00011100 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x1c, /* 00011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 239 0xef 'ï' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 240 0xf0 'ð' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 241 0xf1 'ñ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 242 0xf2 'ò' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 243 0xf3 'ó' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 244 0xf4 'ô' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0e, /* 00001110 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 245 0xf5 'õ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 246 0xf6 'ö' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 247 0xf7 '÷' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 248 0xf8 'ø' */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 249 0xf9 'ù' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 250 0xfa 'ú' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 251 0xfb 'û' */ + 0x00, /* 00000000 */ + 0x0f, /* 00001111 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0xec, /* 11101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x3c, /* 00111100 */ + 0x1c, /* 00011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 252 0xfc 'ü' */ + 0x00, /* 00000000 */ + 0x6c, /* 01101100 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 253 0xfd 'ý' */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x32, /* 00110010 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 254 0xfe 'þ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 255 0xff 'ÿ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ +#endif +}; + +#endif diff --git a/gpxe/src/core/config.c b/gpxe/src/core/config.c new file mode 100644 index 0000000..bd0d66e --- /dev/null +++ b/gpxe/src/core/config.c @@ -0,0 +1,224 @@ +/* + * 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 + +/* + * Build ID string calculations + * + */ +#undef XSTR +#undef STR +#define XSTR(s) STR(s) +#define STR(s) #s + +#ifdef BUILD_SERIAL +#include "config/.buildserial.h" +#define BUILD_SERIAL_STR " #" XSTR(BUILD_SERIAL_NUM) +#else +#define BUILD_SERIAL_STR "" +#endif + +#ifdef BUILD_ID +#define BUILD_ID_STR " " BUILD_ID +#else +#define BUILD_ID_STR "" +#endif + +#if defined(BUILD_ID) || defined(BUILD_SERIAL) +#define BUILD_STRING " [build" BUILD_ID_STR BUILD_SERIAL_STR "]" +#else +#define BUILD_STRING "" +#endif + +/* + * Drag in all requested console types + * + */ + +#ifdef CONSOLE_PCBIOS +REQUIRE_OBJECT ( bios_console ); +#endif +#ifdef CONSOLE_SERIAL +REQUIRE_OBJECT ( serial_console ); +#endif +#ifdef CONSOLE_DIRECT_VGA +REQUIRE_OBJECT ( video_subr ); +#endif +#ifdef CONSOLE_BTEXT +REQUIRE_OBJECT ( btext ); +#endif +#ifdef CONSOLE_PC_KBD +REQUIRE_OBJECT ( pc_kbd ); +#endif +#ifdef CONSOLE_SYSLOG +REQUIRE_OBJECT ( syslog ); +#endif +#ifdef CONSOLE_EFI +REQUIRE_OBJECT ( efi_console ); +#endif + +/* + * Drag in all requested network protocols + * + */ +#ifdef NET_PROTO_IPV4 +REQUIRE_OBJECT ( ipv4 ); +#endif + +/* + * Drag in all requested download protocols + * + */ +#ifdef DOWNLOAD_PROTO_TFTP +REQUIRE_OBJECT ( tftp ); +#endif +#ifdef DOWNLOAD_PROTO_NFS +REQUIRE_OBJECT ( nfs ); +#endif +#ifdef DOWNLOAD_PROTO_HTTP +REQUIRE_OBJECT ( http ); +#endif +#ifdef DOWNLOAD_PROTO_HTTPS +REQUIRE_OBJECT ( https ); +#endif +#ifdef DOWNLOAD_PROTO_FTP +REQUIRE_OBJECT ( ftp ); +#endif +#ifdef DOWNLOAD_PROTO_TFTM +REQUIRE_OBJECT ( tftm ); +#endif +#ifdef DOWNLOAD_PROTO_SLAM +REQUIRE_OBJECT ( slam ); +#endif + +/* + * Drag in all requested SAN boot protocols + * + */ +#ifdef SANBOOT_PROTO_ISCSI +REQUIRE_OBJECT ( iscsiboot ); +#endif +#ifdef SANBOOT_PROTO_AOE +REQUIRE_OBJECT ( aoeboot ); +#endif + +/* + * Drag in all requested resolvers + * + */ +#ifdef DNS_RESOLVER +REQUIRE_OBJECT ( dns ); +#endif +#ifdef NMB_RESOLVER +REQUIRE_OBJECT ( nmb ); +#endif + +/* + * Drag in all requested image formats + * + */ +#ifdef IMAGE_NBI +REQUIRE_OBJECT ( nbi ); +#endif +#ifdef IMAGE_ELF +REQUIRE_OBJECT ( elfboot ); +#endif +#ifdef IMAGE_FREEBSD +REQUIRE_OBJECT ( freebsd ); +#endif +#ifdef IMAGE_MULTIBOOT +REQUIRE_OBJECT ( multiboot ); +#endif +#ifdef IMAGE_AOUT +REQUIRE_OBJECT ( aout ); +#endif +#ifdef IMAGE_WINCE +REQUIRE_OBJECT ( wince ); +#endif +#ifdef IMAGE_PXE +REQUIRE_OBJECT ( pxe_image ); +#endif +#ifdef IMAGE_SCRIPT +REQUIRE_OBJECT ( script ); +#endif +#ifdef IMAGE_BZIMAGE +REQUIRE_OBJECT ( bzimage ); +#endif +#ifdef IMAGE_ELTORITO +REQUIRE_OBJECT ( eltorito ); +#endif +#ifdef IMAGE_COMBOOT +REQUIRE_OBJECT ( comboot ); +REQUIRE_OBJECT ( com32 ); +REQUIRE_OBJECT ( comboot_call ); +REQUIRE_OBJECT ( com32_call ); +REQUIRE_OBJECT ( com32_wrapper ); +REQUIRE_OBJECT ( comboot_resolv ); +#endif +#ifdef IMAGE_EFI +REQUIRE_OBJECT ( efi_image ); +#endif + +/* + * Drag in all requested commands + * + */ +#ifdef AUTOBOOT_CMD +REQUIRE_OBJECT ( autoboot_cmd ); +#endif +#ifdef NVO_CMD +REQUIRE_OBJECT ( nvo_cmd ); +#endif +#ifdef CONFIG_CMD +REQUIRE_OBJECT ( config_cmd ); +#endif +#ifdef IFMGMT_CMD +REQUIRE_OBJECT ( ifmgmt_cmd ); +#endif +#ifdef ROUTE_CMD +REQUIRE_OBJECT ( route_cmd ); +#endif +#ifdef IMAGE_CMD +REQUIRE_OBJECT ( image_cmd ); +#endif +#ifdef DHCP_CMD +REQUIRE_OBJECT ( dhcp_cmd ); +#endif +#ifdef SANBOOT_CMD +REQUIRE_OBJECT ( sanboot_cmd ); +#endif +#ifdef LOGIN_CMD +REQUIRE_OBJECT ( login_cmd ); +#endif + +/* + * Drag in miscellaneous objects + * + */ +#ifdef NULL_TRAP +REQUIRE_OBJECT ( nulltrap ); +#endif +#ifdef GDBSERIAL +REQUIRE_OBJECT ( gdbidt ); +REQUIRE_OBJECT ( gdbserial ); +REQUIRE_OBJECT ( gdbstub_cmd ); +#endif +#ifdef GDBUDP +REQUIRE_OBJECT ( gdbidt ); +REQUIRE_OBJECT ( gdbudp ); +REQUIRE_OBJECT ( gdbstub_cmd ); +#endif + +/* + * Drag in objects that are always required, but not dragged in via + * symbol dependencies. + * + */ +REQUIRE_OBJECT ( device ); +REQUIRE_OBJECT ( embedded ); diff --git a/gpxe/src/core/console.c b/gpxe/src/core/console.c new file mode 100644 index 0000000..c9773f7 --- /dev/null +++ b/gpxe/src/core/console.c @@ -0,0 +1,135 @@ +#include "stddef.h" +#include "console.h" +#include +#include + +/** @file */ + +static struct console_driver console_drivers[0] + __table_start ( struct console_driver, console ); +static struct console_driver console_drivers_end[0] + __table_end ( struct console_driver, console ); + +/** + * Write a single character to each console device. + * + * @v character Character to be written + * @ret None - + * @err None - + * + * The character is written out to all enabled console devices, using + * each device's console_driver::putchar() method. + * + */ +void putchar ( int character ) { + struct console_driver *console; + + /* Automatic LF -> CR,LF translation */ + if ( character == '\n' ) + putchar ( '\r' ); + + for ( console = console_drivers; console < console_drivers_end ; + console++ ) { + if ( ( ! console->disabled ) && console->putchar ) + console->putchar ( character ); + } +} + +/** + * Check to see if any input is available on any console. + * + * @v None - + * @ret console Console device that has input available, if any. + * @ret NULL No console device has input available. + * @err None - + * + * All enabled console devices are checked once for available input + * using each device's console_driver::iskey() method. The first + * console device that has available input will be returned, if any. + * + */ +static struct console_driver * has_input ( void ) { + struct console_driver *console; + + for ( console = console_drivers; console < console_drivers_end ; + console++ ) { + if ( ( ! console->disabled ) && console->iskey ) { + if ( console->iskey () ) + return console; + } + } + return NULL; +} + +/** + * Read a single character from any console. + * + * @v None - + * @ret character Character read from a console. + * @err None - + * + * A character will be read from the first enabled console device that + * has input available using that console's console_driver::getchar() + * method. If no console has input available to be read, this method + * will block. To perform a non-blocking read, use something like + * + * @code + * + * int key = iskey() ? getchar() : -1; + * + * @endcode + * + * The character read will not be echoed back to any console. + * + */ +int getchar ( void ) { + struct console_driver *console; + int character; + + while ( 1 ) { + console = has_input(); + if ( console && console->getchar ) { + character = console->getchar (); + break; + } + + /* Doze for a while (until the next interrupt). This works + * fine, because the keyboard is interrupt-driven, and the + * timer interrupt (approx. every 50msec) takes care of the + * serial port, which is read by polling. This reduces the + * power dissipation of a modern CPU considerably, and also + * makes Etherboot waiting for user interaction waste a lot + * less CPU time in a VMware session. + */ + cpu_nap(); + + /* Keep processing background tasks while we wait for + * input. + */ + step(); + } + + /* CR -> LF translation */ + if ( character == '\r' ) + character = '\n'; + + return character; +} + +/** Check for available input on any console. + * + * @v None - + * @ret True Input is available on a console + * @ret False Input is not available on any console + * @err None - + * + * All enabled console devices are checked once for available input + * using each device's console_driver::iskey() method. If any console + * device has input available, this call will return True. If this + * call returns True, you can then safely call getchar() without + * blocking. + * + */ +int iskey ( void ) { + return has_input() ? 1 : 0; +} diff --git a/gpxe/src/core/cpio.c b/gpxe/src/core/cpio.c new file mode 100644 index 0000000..7d2e882 --- /dev/null +++ b/gpxe/src/core/cpio.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2007 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. + */ + +/** @file + * + * CPIO archives + * + */ + +#include +#include +#include + +/** + * Set field within a CPIO header + * + * @v field Field within CPIO header + * @v value Value to set + */ +void cpio_set_field ( char *field, unsigned long value ) { + char buf[9]; + + snprintf ( buf, sizeof ( buf ), "%08lx", value ); + memcpy ( field, buf, 8 ); +} diff --git a/gpxe/src/core/cwuri.c b/gpxe/src/core/cwuri.c new file mode 100644 index 0000000..81fd900 --- /dev/null +++ b/gpxe/src/core/cwuri.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include + +/** @file + * + * Current working URI + * + * Somewhat analogous to the current working directory in a POSIX + * system. + */ + +/** Current working URI */ +struct uri *cwuri = NULL; + +/** + * Change working URI + * + * @v uri New working URI, or NULL + */ +void churi ( struct uri *uri ) { + struct uri *new_uri; + + new_uri = resolve_uri ( cwuri, uri ); + uri_put ( cwuri ); + cwuri = new_uri; +} diff --git a/gpxe/src/core/debug.c b/gpxe/src/core/debug.c new file mode 100644 index 0000000..500a7ac --- /dev/null +++ b/gpxe/src/core/debug.c @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include + +void pause ( void ) { + printf ( "\nPress a key" ); + getchar(); + printf ( "\r \r" ); +} + +void more ( void ) { + printf ( "---more---" ); + getchar(); + printf ( "\r \r" ); +} + +/** + * Print row of a hex dump with specified display address + * + * @v dispaddr Display address + * @v data Data to print + * @v len Length of data + * @v offset Starting offset within data + */ +static void dbg_hex_dump_da_row ( unsigned long dispaddr, const void *data, + unsigned long len, unsigned int offset ) { + const uint8_t *bytes = data; + unsigned int i; + uint8_t byte; + + printf ( "%08lx :", ( dispaddr + offset ) ); + for ( i = offset ; i < ( offset + 16 ) ; i++ ) { + if ( i >= len ) { + printf ( " " ); + continue; + } + printf ( " %02x", bytes[i] ); + } + printf ( " : " ); + for ( i = offset ; i < ( offset + 16 ) ; i++ ) { + if ( i >= len ) { + printf ( " " ); + continue; + } + byte = bytes[i]; + if ( ( byte < 0x20 ) || ( byte >= 0x7f ) ) + byte = '.'; + printf ( "%c", byte ); + } + printf ( "\n" ); +} + +/** + * Print hex dump with specified display address + * + * @v dispaddr Display address + * @v data Data to print + * @v len Length of data + */ +void dbg_hex_dump_da ( unsigned long dispaddr, const void *data, + unsigned long len ) { + unsigned int offset; + + for ( offset = 0 ; offset < len ; offset += 16 ) { + dbg_hex_dump_da_row ( dispaddr, data, len, offset ); + } +} + +#define GUARD_SYMBOL ( ( 'M' << 24 ) | ( 'I' << 16 ) | ( 'N' << 8 ) | 'E' ) +/* Fill a region with guard markers. We use a 4-byte pattern to make + * it less likely that check_region will find spurious 1-byte regions + * of non-corruption. + */ +void guard_region ( void *region, size_t len ) { + uint32_t offset = 0; + + len &= ~0x03; + for ( offset = 0; offset < len ; offset += 4 ) { + *((uint32_t *)(region + offset)) = GUARD_SYMBOL; + } +} + +/* Check a region that has been guarded with guard_region() for + * corruption. + */ +int check_region ( void *region, size_t len ) { + uint8_t corrupted = 0; + uint8_t in_corruption = 0; + uint32_t offset = 0; + uint32_t test = 0; + + len &= ~0x03; + for ( offset = 0; offset < len ; offset += 4 ) { + test = *((uint32_t *)(region + offset)) = GUARD_SYMBOL; + if ( ( in_corruption == 0 ) && + ( test != GUARD_SYMBOL ) ) { + /* Start of corruption */ + if ( corrupted == 0 ) { + corrupted = 1; + printf ( "Region %p-%p (physical %#lx-%#lx) " + "corrupted\n", + region, region + len, + virt_to_phys ( region ), + virt_to_phys ( region + len ) ); + } + in_corruption = 1; + printf ( "--- offset %#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 %#zx (end of region)\n", len-1 ); + } + return corrupted; +} + +/** + * Maximum number of separately coloured message streams + * + * Six is the realistic maximum; there are 8 basic ANSI colours, one + * of which will be the terminal default and one of which will be + * invisible on the terminal because it matches the background colour. + */ +#define NUM_AUTO_COLOURS 6 + +/** A colour assigned to an autocolourised debug message stream */ +struct autocolour { + /** Message stream ID */ + unsigned long stream; + /** Last recorded usage */ + unsigned long last_used; +}; + +/** + * Choose colour index for debug autocolourisation + * + * @v stream Message stream ID + * @ret colour Colour ID + */ +static int dbg_autocolour ( unsigned long stream ) { + static struct autocolour acs[NUM_AUTO_COLOURS]; + static unsigned long use; + unsigned int i; + unsigned int oldest; + unsigned int oldest_last_used; + + /* Increment usage iteration counter */ + use++; + + /* Scan through list for a currently assigned colour */ + for ( i = 0 ; i < ( sizeof ( acs ) / sizeof ( acs[0] ) ) ; i++ ) { + if ( acs[i].stream == stream ) { + acs[i].last_used = use; + return i; + } + } + + /* No colour found; evict the oldest from the list */ + oldest = 0; + oldest_last_used = use; + for ( i = 0 ; i < ( sizeof ( acs ) / sizeof ( acs[0] ) ) ; i++ ) { + if ( acs[i].last_used < oldest_last_used ) { + oldest_last_used = acs[i].last_used; + oldest = i; + } + } + acs[oldest].stream = stream; + acs[oldest].last_used = use; + return oldest; +} + +/** + * Select automatic colour for debug messages + * + * @v stream Message stream ID + */ +void dbg_autocolourise ( unsigned long stream ) { + printf ( "\033[%dm", + ( stream ? ( 31 + dbg_autocolour ( stream ) ) : 0 ) ); +} + +/** + * Revert to normal colour + * + */ +void dbg_decolourise ( void ) { + printf ( "\033[0m" ); +} diff --git a/gpxe/src/core/device.c b/gpxe/src/core/device.c new file mode 100644 index 0000000..84915c2 --- /dev/null +++ b/gpxe/src/core/device.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include + +/** + * @file + * + * Device model + * + */ + +static struct root_device root_devices[0] + __table_start ( struct root_device, root_devices ); +static struct root_device root_devices_end[0] + __table_end ( struct root_device, root_devices ); + +/** Registered root devices */ +static LIST_HEAD ( devices ); + +/** + * Probe a root device + * + * @v rootdev Root device + * @ret rc Return status code + */ +static int rootdev_probe ( struct root_device *rootdev ) { + int rc; + + DBG ( "Adding %s root bus\n", rootdev->dev.name ); + if ( ( rc = rootdev->driver->probe ( rootdev ) ) != 0 ) { + DBG ( "Failed to add %s root bus: %s\n", + rootdev->dev.name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Remove a root device + * + * @v rootdev Root device + */ +static void rootdev_remove ( struct root_device *rootdev ) { + rootdev->driver->remove ( rootdev ); + DBG ( "Removed %s root bus\n", rootdev->dev.name ); +} + +/** + * Probe all devices + * + * This initiates probing for all devices in the system. After this + * call, the device hierarchy will be populated, and all hardware + * should be ready to use. + */ +static void probe_devices ( void ) { + struct root_device *rootdev; + int rc; + + for ( rootdev = root_devices; rootdev < root_devices_end; rootdev++ ) { + list_add ( &rootdev->dev.siblings, &devices ); + INIT_LIST_HEAD ( &rootdev->dev.children ); + if ( ( rc = rootdev_probe ( rootdev ) ) != 0 ) + list_del ( &rootdev->dev.siblings ); + } +} + +/** + * Remove all devices + * + */ +static void remove_devices ( int flags ) { + struct root_device *rootdev; + struct root_device *tmp; + + if ( flags & SHUTDOWN_KEEP_DEVICES ) { + DBG ( "Refusing to remove devices on shutdown\n" ); + return; + } + + list_for_each_entry_safe ( rootdev, tmp, &devices, dev.siblings ) { + rootdev_remove ( rootdev ); + list_del ( &rootdev->dev.siblings ); + } +} + +struct startup_fn startup_devices __startup_fn ( STARTUP_NORMAL ) = { + .startup = probe_devices, + .shutdown = remove_devices, +}; diff --git a/gpxe/src/core/downloader.c b/gpxe/src/core/downloader.c new file mode 100644 index 0000000..0a443db --- /dev/null +++ b/gpxe/src/core/downloader.c @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Image downloader + * + */ + +/** A downloader */ +struct downloader { + /** Reference count for this object */ + struct refcnt refcnt; + + /** Job control interface */ + struct job_interface job; + /** Data transfer interface */ + struct xfer_interface xfer; + + /** Image to contain downloaded file */ + struct image *image; + /** Current position within image buffer */ + size_t pos; + /** Image registration routine */ + int ( * register_image ) ( struct image *image ); +}; + +/** + * Free downloader object + * + * @v refcnt Downloader reference counter + */ +static void downloader_free ( struct refcnt *refcnt ) { + struct downloader *downloader = + container_of ( refcnt, struct downloader, refcnt ); + + image_put ( downloader->image ); + free ( downloader ); +} + +/** + * Terminate download + * + * @v downloader Downloader + * @v rc Reason for termination + */ +static void downloader_finished ( struct downloader *downloader, int rc ) { + + /* Block further incoming messages */ + job_nullify ( &downloader->job ); + xfer_nullify ( &downloader->xfer ); + + /* Free resources and close interfaces */ + xfer_close ( &downloader->xfer, rc ); + job_done ( &downloader->job, rc ); +} + +/** + * Ensure that download buffer is large enough for the specified size + * + * @v downloader Downloader + * @v len Required minimum size + * @ret rc Return status code + */ +static int downloader_ensure_size ( struct downloader *downloader, + size_t len ) { + userptr_t new_buffer; + + /* If buffer is already large enough, do nothing */ + if ( len <= downloader->image->len ) + return 0; + + DBGC ( downloader, "Downloader %p extending to %zd bytes\n", + downloader, len ); + + /* Extend buffer */ + new_buffer = urealloc ( downloader->image->data, len ); + if ( ! new_buffer ) { + DBGC ( downloader, "Downloader %p could not extend buffer to " + "%zd bytes\n", downloader, len ); + return -ENOBUFS; + } + downloader->image->data = new_buffer; + downloader->image->len = len; + + return 0; +} + +/**************************************************************************** + * + * Job control interface + * + */ + +/** + * Handle kill() event received via job control interface + * + * @v job Downloader job control interface + */ +static void downloader_job_kill ( struct job_interface *job ) { + struct downloader *downloader = + container_of ( job, struct downloader, job ); + + /* Terminate download */ + downloader_finished ( downloader, -ECANCELED ); +} + +/** Downloader job control interface operations */ +static struct job_interface_operations downloader_job_operations = { + .done = ignore_job_done, + .kill = downloader_job_kill, + .progress = ignore_job_progress, +}; + +/**************************************************************************** + * + * Data transfer interface + * + */ + +/** + * Handle deliver_raw() event received via data transfer interface + * + * @v xfer Downloader data transfer interface + * @v iobuf Datagram I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int downloader_xfer_deliver_iob ( struct xfer_interface *xfer, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct downloader *downloader = + container_of ( xfer, struct downloader, xfer ); + size_t len; + size_t max; + int rc; + + /* Calculate new buffer position */ + if ( meta->whence != SEEK_CUR ) + downloader->pos = 0; + downloader->pos += meta->offset; + + /* Ensure that we have enough buffer space for this data */ + len = iob_len ( iobuf ); + max = ( downloader->pos + len ); + if ( ( rc = downloader_ensure_size ( downloader, max ) ) != 0 ) + goto done; + + /* Copy data to buffer */ + copy_to_user ( downloader->image->data, downloader->pos, + iobuf->data, len ); + + /* Update current buffer position */ + downloader->pos += len; + + done: + free_iob ( iobuf ); + return rc; +} + +/** + * Handle close() event received via data transfer interface + * + * @v xfer Downloader data transfer interface + * @v rc Reason for close + */ +static void downloader_xfer_close ( struct xfer_interface *xfer, int rc ) { + struct downloader *downloader = + container_of ( xfer, struct downloader, xfer ); + + /* Register image if download was successful */ + if ( rc == 0 ) + rc = downloader->register_image ( downloader->image ); + + /* Terminate download */ + downloader_finished ( downloader, rc ); +} + +/** Downloader data transfer interface operations */ +static struct xfer_interface_operations downloader_xfer_operations = { + .close = downloader_xfer_close, + .vredirect = xfer_vopen, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = downloader_xfer_deliver_iob, + .deliver_raw = xfer_deliver_as_iob, +}; + +/**************************************************************************** + * + * Instantiator + * + */ + +/** + * Instantiate a downloader + * + * @v job Job control interface + * @v image Image to fill with downloaded file + * @v register_image Image registration routine + * @v type Location type to pass to xfer_open() + * @v ... Remaining arguments to pass to xfer_open() + * @ret rc Return status code + * + * Instantiates a downloader object to download the specified URI into + * the specified image object. If the download is successful, the + * image registration routine @c register_image() will be called. + */ +int create_downloader ( struct job_interface *job, struct image *image, + int ( * register_image ) ( struct image *image ), + int type, ... ) { + struct downloader *downloader; + va_list args; + int rc; + + /* Allocate and initialise structure */ + downloader = zalloc ( sizeof ( *downloader ) ); + if ( ! downloader ) + return -ENOMEM; + downloader->refcnt.free = downloader_free; + job_init ( &downloader->job, &downloader_job_operations, + &downloader->refcnt ); + xfer_init ( &downloader->xfer, &downloader_xfer_operations, + &downloader->refcnt ); + downloader->image = image_get ( image ); + downloader->register_image = register_image; + va_start ( args, type ); + + /* Instantiate child objects and attach to our interfaces */ + if ( ( rc = xfer_vopen ( &downloader->xfer, type, args ) ) != 0 ) + goto err; + + /* Attach parent interface, mortalise self, and return */ + job_plug_plug ( &downloader->job, job ); + ref_put ( &downloader->refcnt ); + va_end ( args ); + return 0; + + err: + downloader_finished ( downloader, rc ); + ref_put ( &downloader->refcnt ); + va_end ( args ); + return rc; +} diff --git a/gpxe/src/core/errno.c b/gpxe/src/core/errno.c new file mode 100644 index 0000000..b4f44ce --- /dev/null +++ b/gpxe/src/core/errno.c @@ -0,0 +1,18 @@ +#include + +/** @file + * + * Error codes + * + * This file provides the global variable #errno. + * + */ + +/** + * Global "last error" number. + * + * This is valid only when a function has just returned indicating a + * failure. + * + */ +int errno; diff --git a/gpxe/src/core/exec.c b/gpxe/src/core/exec.c new file mode 100644 index 0000000..a9861b6 --- /dev/null +++ b/gpxe/src/core/exec.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Command execution + * + */ + +static struct command commands[0] + __table_start ( struct command, commands ); +static struct command commands_end[0] + __table_end ( struct command, commands ); + +/* Avoid dragging in getopt.o unless a command really uses it */ +int optind; +int nextchar; + +/** + * Execute command + * + * @v command Command name + * @v argv Argument list + * @ret rc Command exit status + * + * Execute the named command. Unlike a traditional POSIX execv(), + * this function returns the exit status of the command. + */ +int execv ( const char *command, char * const argv[] ) { + struct command *cmd; + int argc; + + /* Count number of arguments */ + for ( argc = 0 ; argv[argc] ; argc++ ) {} + + /* Sanity checks */ + if ( ! command ) { + DBG ( "No command\n" ); + return -EINVAL; + } + if ( ! argc ) { + DBG ( "%s: empty argument list\n", command ); + return -EINVAL; + } + + /* Reset getopt() library ready for use by the command. This + * is an artefact of the POSIX getopt() API within the context + * of Etherboot; see the documentation for reset_getopt() for + * details. + */ + reset_getopt(); + + /* Hand off to command implementation */ + for ( cmd = commands ; cmd < commands_end ; cmd++ ) { + if ( strcmp ( command, cmd->name ) == 0 ) + return cmd->exec ( argc, ( char ** ) argv ); + } + + printf ( "%s: command not found\n", command ); + return -ENOEXEC; +} + +/** + * Expand variables within command line + * + * @v command Command line + * @ret expcmd Expanded command line + * + * The expanded command line is allocated with malloc() and the caller + * must eventually free() it. + */ +static char * expand_command ( const char *command ) { + char *expcmd; + char *start; + char *end; + char *head; + char *name; + char *tail; + int setting_len; + int new_len; + char *tmp; + + /* Obtain temporary modifiable copy of command line */ + expcmd = strdup ( command ); + if ( ! expcmd ) + return NULL; + + /* Expand while expansions remain */ + while ( 1 ) { + + head = expcmd; + + /* Locate opener */ + start = strstr ( expcmd, "${" ); + if ( ! start ) + break; + *start = '\0'; + name = ( start + 2 ); + + /* Locate closer */ + end = strstr ( name, "}" ); + if ( ! end ) + break; + *end = '\0'; + tail = ( end + 1 ); + + /* Determine setting length */ + setting_len = fetchf_named_setting ( name, NULL, 0 ); + if ( setting_len < 0 ) + setting_len = 0; /* Treat error as empty setting */ + + /* Read setting into temporary buffer */ + { + char setting_buf[ setting_len + 1 ]; + + setting_buf[0] = '\0'; + fetchf_named_setting ( name, setting_buf, + sizeof ( setting_buf ) ); + + /* Construct expanded string and discard old string */ + tmp = expcmd; + new_len = asprintf ( &expcmd, "%s%s%s", + head, setting_buf, tail ); + free ( tmp ); + if ( new_len < 0 ) + return NULL; + } + } + + return expcmd; +} + +/** + * Split command line into argv array + * + * @v args Command line + * @v argv Argument array to populate, or NULL + * @ret argc Argument count + * + * Splits the command line into whitespace-delimited arguments. If @c + * argv is non-NULL, any whitespace in the command line will be + * replaced with NULs. + */ +static int split_args ( char *args, char * argv[] ) { + int argc = 0; + + while ( 1 ) { + /* Skip over any whitespace / convert to NUL */ + while ( *args == ' ' ) { + if ( argv ) + *args = '\0'; + args++; + } + /* Check for end of line */ + if ( ! *args ) + break; + /* We have found the start of the next argument */ + if ( argv ) + argv[argc] = args; + argc++; + /* Skip to start of next whitespace, if any */ + while ( *args && ( *args != ' ' ) ) { + args++; + } + } + return argc; +} + +/** + * Execute command line + * + * @v command Command line + * @ret rc Command exit status + * + * Execute the named command and arguments. + */ +int system ( const char *command ) { + char *args; + int argc; + int rc = 0; + + /* Perform variable expansion */ + args = expand_command ( command ); + if ( ! args ) + return -ENOMEM; + + /* Count arguments */ + argc = split_args ( args, NULL ); + + /* Create argv array and execute command */ + if ( argc ) { + char * argv[argc + 1]; + + split_args ( args, argv ); + argv[argc] = NULL; + + if ( argv[0][0] != '#' ) + rc = execv ( argv[0], argv ); + } + + free ( args ); + return rc; +} + +/** + * The "echo" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Exit code + */ +static int echo_exec ( int argc, char **argv ) { + int i; + + for ( i = 1 ; i < argc ; i++ ) { + printf ( "%s%s", ( ( i == 1 ) ? "" : " " ), argv[i] ); + } + printf ( "\n" ); + return 0; +} + +/** "echo" command */ +struct command echo_command __command = { + .name = "echo", + .exec = echo_exec, +}; diff --git a/gpxe/src/core/filter.c b/gpxe/src/core/filter.c new file mode 100644 index 0000000..f9ebe7c --- /dev/null +++ b/gpxe/src/core/filter.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include + +/** @file + * + * Data transfer filters + * + */ + +/* + * Pass-through methods to be used by filters which don't want to + * intercept all events. + * + */ + +void filter_close ( struct xfer_interface *xfer, int rc ) { + struct xfer_interface *other = filter_other_half ( xfer ); + + xfer_close ( other, rc ); +} + +int filter_vredirect ( struct xfer_interface *xfer, int type, + va_list args ) { + struct xfer_interface *other = filter_other_half ( xfer ); + + return xfer_vredirect ( other, type, args ); +} + +size_t filter_window ( struct xfer_interface *xfer ) { + struct xfer_interface *other = filter_other_half ( xfer ); + + return xfer_window ( other ); +} + +struct io_buffer * filter_alloc_iob ( struct xfer_interface *xfer, + size_t len ) { + struct xfer_interface *other = filter_other_half ( xfer ); + + return xfer_alloc_iob ( other, len ); +} + +int filter_deliver_iob ( struct xfer_interface *xfer, struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct xfer_interface *other = filter_other_half ( xfer ); + + return xfer_deliver_iob_meta ( other, iobuf, meta ); +} + +int filter_deliver_raw ( struct xfer_interface *xfer, const void *data, + size_t len ) { + struct xfer_interface *other = filter_other_half ( xfer ); + + return xfer_deliver_raw ( other, data, len ); +} diff --git a/gpxe/src/core/gdbserial.c b/gpxe/src/core/gdbserial.c new file mode 100644 index 0000000..b0d5047 --- /dev/null +++ b/gpxe/src/core/gdbserial.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 Stefan Hajnoczi . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +struct gdb_transport serial_gdb_transport __gdb_transport; + +static size_t gdbserial_recv ( char *buf, size_t len ) { + assert ( len > 0 ); + buf [ 0 ] = serial_getc(); + return 1; +} + +static void gdbserial_send ( const char *buf, size_t len ) { + while ( len-- > 0 ) { + serial_putc ( *buf++ ); + } +} + +struct gdb_transport serial_gdb_transport __gdb_transport = { + .name = "serial", + .recv = gdbserial_recv, + .send = gdbserial_send, +}; + +struct gdb_transport *gdbserial_configure ( void ) { + return &serial_gdb_transport; +} diff --git a/gpxe/src/core/gdbstub.c b/gpxe/src/core/gdbstub.c new file mode 100644 index 0000000..bbed344 --- /dev/null +++ b/gpxe/src/core/gdbstub.c @@ -0,0 +1,402 @@ +/* + * Copyright (C) 2008 Stefan Hajnoczi . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * @file + * + * GDB stub for remote debugging + * + */ + +#include +#include +#include +#include +#include +#include +#include "gdbmach.h" + +enum { + POSIX_EINVAL = 0x1c, /* used to report bad arguments to GDB */ + SIZEOF_PAYLOAD = 256, /* buffer size of GDB payload data */ +}; + +struct gdbstub { + struct gdb_transport *trans; + int exit_handler; /* leave interrupt handler */ + + int signo; + gdbreg_t *regs; + + void ( * parse ) ( struct gdbstub *stub, char ch ); + uint8_t cksum1; + + /* Buffer for payload data when parsing a packet. Once the + * packet has been received, this buffer is used to hold + * the reply payload. */ + char buf [ SIZEOF_PAYLOAD + 4 ]; /* $...PAYLOAD...#XX */ + char *payload; /* start of payload */ + int len; /* length of payload */ +}; + +/* Transports */ +static struct gdb_transport gdb_transport_start[0] __table_start ( struct gdb_transport, gdb_transports ); +static struct gdb_transport gdb_transport_end[0] __table_end ( struct gdb_transport, gdb_transports ); + +/* Packet parser states */ +static void gdbstub_state_new ( struct gdbstub *stub, char ch ); +static void gdbstub_state_data ( struct gdbstub *stub, char ch ); +static void gdbstub_state_cksum1 ( struct gdbstub *stub, char ch ); +static void gdbstub_state_cksum2 ( struct gdbstub *stub, char ch ); +static void gdbstub_state_wait_ack ( struct gdbstub *stub, char ch ); + +static uint8_t gdbstub_from_hex_digit ( char ch ) { + return ( isdigit ( ch ) ? ch - '0' : tolower ( ch ) - 'a' + 0xa ) & 0xf; +} + +static uint8_t gdbstub_to_hex_digit ( uint8_t b ) { + b &= 0xf; + return ( b < 0xa ? '0' : 'a' - 0xa ) + b; +} + +/* + * To make reading/writing device memory atomic, we check for + * 2- or 4-byte aligned operations and handle them specially. + */ + +static void gdbstub_from_hex_buf ( char *dst, char *src, int lenbytes ) { + if ( lenbytes == 2 && ( ( unsigned long ) dst & 0x1 ) == 0 ) { + uint16_t i = gdbstub_from_hex_digit ( src [ 2 ] ) << 12 | + gdbstub_from_hex_digit ( src [ 3 ] ) << 8 | + gdbstub_from_hex_digit ( src [ 0 ] ) << 4 | + gdbstub_from_hex_digit ( src [ 1 ] ); + * ( uint16_t * ) dst = cpu_to_le16 ( i ); + } else if ( lenbytes == 4 && ( ( unsigned long ) dst & 0x3 ) == 0 ) { + uint32_t i = gdbstub_from_hex_digit ( src [ 6 ] ) << 28 | + gdbstub_from_hex_digit ( src [ 7 ] ) << 24 | + gdbstub_from_hex_digit ( src [ 4 ] ) << 20 | + gdbstub_from_hex_digit ( src [ 5 ] ) << 16 | + gdbstub_from_hex_digit ( src [ 2 ] ) << 12 | + gdbstub_from_hex_digit ( src [ 3 ] ) << 8 | + gdbstub_from_hex_digit ( src [ 0 ] ) << 4 | + gdbstub_from_hex_digit ( src [ 1 ] ); + * ( uint32_t * ) dst = cpu_to_le32 ( i ); + } else { + while ( lenbytes-- > 0 ) { + *dst++ = gdbstub_from_hex_digit ( src [ 0 ] ) << 4 | + gdbstub_from_hex_digit ( src [ 1 ] ); + src += 2; + } + } +} + +static void gdbstub_to_hex_buf ( char *dst, char *src, int lenbytes ) { + if ( lenbytes == 2 && ( ( unsigned long ) src & 0x1 ) == 0 ) { + uint16_t i = cpu_to_le16 ( * ( uint16_t * ) src ); + dst [ 0 ] = gdbstub_to_hex_digit ( i >> 4 ); + dst [ 1 ] = gdbstub_to_hex_digit ( i ); + dst [ 2 ] = gdbstub_to_hex_digit ( i >> 12 ); + dst [ 3 ] = gdbstub_to_hex_digit ( i >> 8 ); + } else if ( lenbytes == 4 && ( ( unsigned long ) src & 0x3 ) == 0 ) { + uint32_t i = cpu_to_le32 ( * ( uint32_t * ) src ); + dst [ 0 ] = gdbstub_to_hex_digit ( i >> 4 ); + dst [ 1 ] = gdbstub_to_hex_digit ( i ); + dst [ 2 ] = gdbstub_to_hex_digit ( i >> 12 ); + dst [ 3 ] = gdbstub_to_hex_digit ( i >> 8 ); + dst [ 4 ] = gdbstub_to_hex_digit ( i >> 20 ); + dst [ 5 ] = gdbstub_to_hex_digit ( i >> 16); + dst [ 6 ] = gdbstub_to_hex_digit ( i >> 28 ); + dst [ 7 ] = gdbstub_to_hex_digit ( i >> 24 ); + } else { + while ( lenbytes-- > 0 ) { + *dst++ = gdbstub_to_hex_digit ( *src >> 4 ); + *dst++ = gdbstub_to_hex_digit ( *src ); + src++; + } + } +} + +static uint8_t gdbstub_cksum ( char *data, int len ) { + uint8_t cksum = 0; + while ( len-- > 0 ) { + cksum += ( uint8_t ) *data++; + } + return cksum; +} + +static void gdbstub_tx_packet ( struct gdbstub *stub ) { + uint8_t cksum = gdbstub_cksum ( stub->payload, stub->len ); + stub->buf [ 0 ] = '$'; + stub->buf [ stub->len + 1 ] = '#'; + stub->buf [ stub->len + 2 ] = gdbstub_to_hex_digit ( cksum >> 4 ); + stub->buf [ stub->len + 3 ] = gdbstub_to_hex_digit ( cksum ); + stub->trans->send ( stub->buf, stub->len + 4 ); + stub->parse = gdbstub_state_wait_ack; +} + +/* GDB commands */ +static void gdbstub_send_ok ( struct gdbstub *stub ) { + stub->payload [ 0 ] = 'O'; + stub->payload [ 1 ] = 'K'; + stub->len = 2; + gdbstub_tx_packet ( stub ); +} + +static void gdbstub_send_num_packet ( struct gdbstub *stub, char reply, int num ) { + stub->payload [ 0 ] = reply; + stub->payload [ 1 ] = gdbstub_to_hex_digit ( ( char ) num >> 4 ); + stub->payload [ 2 ] = gdbstub_to_hex_digit ( ( char ) num ); + stub->len = 3; + gdbstub_tx_packet ( stub ); +} + +/* Format is arg1,arg2,...,argn:data where argn are hex integers and data is not an argument */ +static int gdbstub_get_packet_args ( struct gdbstub *stub, unsigned long *args, int nargs, int *stop_idx ) { + int i; + char ch = 0; + int argc = 0; + unsigned long val = 0; + for ( i = 1; i < stub->len && argc < nargs; i++ ) { + ch = stub->payload [ i ]; + if ( ch == ':' ) { + break; + } else if ( ch == ',' ) { + args [ argc++ ] = val; + val = 0; + } else { + val = ( val << 4 ) | gdbstub_from_hex_digit ( ch ); + } + } + if ( stop_idx ) { + *stop_idx = i; + } + if ( argc < nargs ) { + args [ argc++ ] = val; + } + return ( ( i == stub->len || ch == ':' ) && argc == nargs ); +} + +static void gdbstub_send_errno ( struct gdbstub *stub, int errno ) { + gdbstub_send_num_packet ( stub, 'E', errno ); +} + +static void gdbstub_report_signal ( struct gdbstub *stub ) { + gdbstub_send_num_packet ( stub, 'S', stub->signo ); +} + +static void gdbstub_read_regs ( struct gdbstub *stub ) { + gdbstub_to_hex_buf ( stub->payload, ( char * ) stub->regs, GDBMACH_SIZEOF_REGS ); + stub->len = GDBMACH_SIZEOF_REGS * 2; + gdbstub_tx_packet ( stub ); +} + +static void gdbstub_write_regs ( struct gdbstub *stub ) { + if ( stub->len != 1 + GDBMACH_SIZEOF_REGS * 2 ) { + gdbstub_send_errno ( stub, POSIX_EINVAL ); + return; + } + gdbstub_from_hex_buf ( ( char * ) stub->regs, &stub->payload [ 1 ], GDBMACH_SIZEOF_REGS ); + gdbstub_send_ok ( stub ); +} + +static void gdbstub_read_mem ( struct gdbstub *stub ) { + unsigned long args [ 2 ]; + if ( !gdbstub_get_packet_args ( stub, args, sizeof args / sizeof args [ 0 ], NULL ) ) { + gdbstub_send_errno ( stub, POSIX_EINVAL ); + return; + } + args [ 1 ] = ( args [ 1 ] < SIZEOF_PAYLOAD / 2 ) ? args [ 1 ] : SIZEOF_PAYLOAD / 2; + gdbstub_to_hex_buf ( stub->payload, ( char * ) args [ 0 ], args [ 1 ] ); + stub->len = args [ 1 ] * 2; + gdbstub_tx_packet ( stub ); +} + +static void gdbstub_write_mem ( struct gdbstub *stub ) { + unsigned long args [ 2 ]; + int colon; + if ( !gdbstub_get_packet_args ( stub, args, sizeof args / sizeof args [ 0 ], &colon ) || + colon >= stub->len || stub->payload [ colon ] != ':' || + ( stub->len - colon - 1 ) % 2 != 0 ) { + gdbstub_send_errno ( stub, POSIX_EINVAL ); + return; + } + gdbstub_from_hex_buf ( ( char * ) args [ 0 ], &stub->payload [ colon + 1 ], ( stub->len - colon - 1 ) / 2 ); + gdbstub_send_ok ( stub ); +} + +static void gdbstub_continue ( struct gdbstub *stub, int single_step ) { + gdbreg_t pc; + if ( stub->len > 1 && gdbstub_get_packet_args ( stub, &pc, 1, NULL ) ) { + gdbmach_set_pc ( stub->regs, pc ); + } + gdbmach_set_single_step ( stub->regs, single_step ); + stub->exit_handler = 1; + /* Reply will be sent when we hit the next breakpoint or interrupt */ +} + +static void gdbstub_breakpoint ( struct gdbstub *stub ) { + unsigned long args [ 3 ]; + int enable = stub->payload [ 0 ] == 'Z' ? 1 : 0; + if ( !gdbstub_get_packet_args ( stub, args, sizeof args / sizeof args [ 0 ], NULL ) ) { + gdbstub_send_errno ( stub, POSIX_EINVAL ); + return; + } + if ( gdbmach_set_breakpoint ( args [ 0 ], args [ 1 ], args [ 2 ], enable ) ) { + gdbstub_send_ok ( stub ); + } else { + /* Not supported */ + stub->len = 0; + gdbstub_tx_packet ( stub ); + } +} + +static void gdbstub_rx_packet ( struct gdbstub *stub ) { + switch ( stub->payload [ 0 ] ) { + case '?': + gdbstub_report_signal ( stub ); + break; + case 'g': + gdbstub_read_regs ( stub ); + break; + case 'G': + gdbstub_write_regs ( stub ); + break; + case 'm': + gdbstub_read_mem ( stub ); + break; + case 'M': + gdbstub_write_mem ( stub ); + break; + case 'c': /* Continue */ + case 'k': /* Kill */ + case 's': /* Step */ + case 'D': /* Detach */ + gdbstub_continue ( stub, stub->payload [ 0 ] == 's' ); + if ( stub->payload [ 0 ] == 'D' ) { + gdbstub_send_ok ( stub ); + } + break; + case 'Z': /* Insert breakpoint */ + case 'z': /* Remove breakpoint */ + gdbstub_breakpoint ( stub ); + break; + default: + stub->len = 0; + gdbstub_tx_packet ( stub ); + break; + } +} + +/* GDB packet parser */ +static void gdbstub_state_new ( struct gdbstub *stub, char ch ) { + if ( ch == '$' ) { + stub->len = 0; + stub->parse = gdbstub_state_data; + } +} + +static void gdbstub_state_data ( struct gdbstub *stub, char ch ) { + if ( ch == '#' ) { + stub->parse = gdbstub_state_cksum1; + } else if ( ch == '$' ) { + stub->len = 0; /* retry new packet */ + } else { + /* If the length exceeds our buffer, let the checksum fail */ + if ( stub->len < SIZEOF_PAYLOAD ) { + stub->payload [ stub->len++ ] = ch; + } + } +} + +static void gdbstub_state_cksum1 ( struct gdbstub *stub, char ch ) { + stub->cksum1 = gdbstub_from_hex_digit ( ch ) << 4; + stub->parse = gdbstub_state_cksum2; +} + +static void gdbstub_state_cksum2 ( struct gdbstub *stub, char ch ) { + uint8_t their_cksum; + uint8_t our_cksum; + + stub->parse = gdbstub_state_new; + their_cksum = stub->cksum1 + gdbstub_from_hex_digit ( ch ); + our_cksum = gdbstub_cksum ( stub->payload, stub->len ); + if ( their_cksum == our_cksum ) { + stub->trans->send ( "+", 1 ); + if ( stub->len > 0 ) { + gdbstub_rx_packet ( stub ); + } + } else { + stub->trans->send ( "-", 1 ); + } +} + +static void gdbstub_state_wait_ack ( struct gdbstub *stub, char ch ) { + if ( ch == '+' ) { + stub->parse = gdbstub_state_new; + } else if ( ch == '-' ) { + gdbstub_tx_packet ( stub ); /* retransmit */ + } else if ( ch == '$' ) { + /* GDB is reconnecting, drop our packet and listen to GDB */ + stub->trans->send ( "-", 1 ); + stub->parse = gdbstub_state_new; + } +} + +static void gdbstub_parse ( struct gdbstub *stub, char ch ) { + stub->parse ( stub, ch ); +} + +static struct gdbstub stub = { + .parse = gdbstub_state_new +}; + +void gdbstub_handler ( int signo, gdbreg_t *regs ) { + char packet [ SIZEOF_PAYLOAD + 4 ]; + size_t len, i; + + /* A transport must be set up */ + if ( !stub.trans ) { + return; + } + + stub.signo = signo; + stub.regs = regs; + stub.exit_handler = 0; + gdbstub_report_signal ( &stub ); + while ( !stub.exit_handler && ( len = stub.trans->recv ( packet, sizeof ( packet ) ) ) > 0 ) { + for ( i = 0; i < len; i++ ) { + gdbstub_parse ( &stub, packet [ i ] ); + } + } +} + +struct gdb_transport *find_gdb_transport ( const char *name ) { + struct gdb_transport *trans; + for ( trans = gdb_transport_start; trans < gdb_transport_end; trans++ ) { + if ( strcmp ( trans->name, name ) == 0 ) { + return trans; + } + } + return NULL; +} + +void gdbstub_start ( struct gdb_transport *trans ) { + stub.trans = trans; + stub.payload = &stub.buf [ 1 ]; + gdbmach_breakpoint(); +} diff --git a/gpxe/src/core/gdbudp.c b/gpxe/src/core/gdbudp.c new file mode 100644 index 0000000..26feb38 --- /dev/null +++ b/gpxe/src/core/gdbudp.c @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2008 Stefan Hajnoczi . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * GDB over UDP transport + * + */ + +enum { + DEFAULT_PORT = 43770, /* UDP listen port */ +}; + +struct gdb_transport udp_gdb_transport __gdb_transport; + +static struct net_device *netdev; +static uint8_t dest_eth[ETH_ALEN]; +static struct sockaddr_in dest_addr; +static struct sockaddr_in source_addr; + +static void gdbudp_ensure_netdev_open ( struct net_device *netdev ) { + /* The device may have been closed between breakpoints */ + assert ( netdev ); + netdev_open ( netdev ); + + /* Strictly speaking, we may need to close the device when leaving the interrupt handler */ +} + +static size_t gdbudp_recv ( char *buf, size_t len ) { + struct io_buffer *iob; + struct ethhdr *ethhdr; + struct arphdr *arphdr; + struct iphdr *iphdr; + struct udp_header *udphdr; + size_t payload_len; + + gdbudp_ensure_netdev_open ( netdev ); + + for ( ; ; ) { + netdev_poll ( netdev ); + while ( ( iob = netdev_rx_dequeue ( netdev ) ) != NULL ) { + /* Ethernet header */ + if ( iob_len ( iob ) < sizeof ( *ethhdr ) ) { + goto bad_packet; + } + ethhdr = iob->data; + iob_pull ( iob, sizeof ( *ethhdr ) ); + + /* Handle ARP requests so the client can find our MAC */ + if ( ethhdr->h_protocol == htons ( ETH_P_ARP ) ) { + arphdr = iob->data; + if ( iob_len ( iob ) < sizeof ( *arphdr ) + 2 * ( ETH_ALEN + sizeof ( struct in_addr ) ) || + arphdr->ar_hrd != htons ( ARPHRD_ETHER ) || + arphdr->ar_pro != htons ( ETH_P_IP ) || + arphdr->ar_hln != ETH_ALEN || + arphdr->ar_pln != sizeof ( struct in_addr ) || + arphdr->ar_op != htons ( ARPOP_REQUEST ) || + * ( uint32_t * ) arp_target_pa ( arphdr ) != source_addr.sin_addr.s_addr ) { + goto bad_packet; + } + + /* Generate an ARP reply */ + arphdr->ar_op = htons ( ARPOP_REPLY ); + memswap ( arp_sender_pa ( arphdr ), arp_target_pa ( arphdr ), sizeof ( struct in_addr ) ); + memcpy ( arp_target_ha ( arphdr ), arp_sender_ha ( arphdr ), ETH_ALEN ); + memcpy ( arp_sender_ha ( arphdr ), netdev->ll_addr, ETH_ALEN ); + + /* Fix up ethernet header */ + ethhdr = iob_push ( iob, sizeof ( *ethhdr ) ); + memcpy ( ethhdr->h_dest, ethhdr->h_source, ETH_ALEN ); + memcpy ( ethhdr->h_source, netdev->ll_addr, ETH_ALEN ); + + netdev_tx ( netdev, iob ); + continue; /* no need to free iob */ + } + + if ( ethhdr->h_protocol != htons ( ETH_P_IP ) ) { + goto bad_packet; + } + + /* IP header */ + if ( iob_len ( iob ) < sizeof ( *iphdr ) ) { + goto bad_packet; + } + iphdr = iob->data; + iob_pull ( iob, sizeof ( *iphdr ) ); + if ( iphdr->protocol != IP_UDP || iphdr->dest.s_addr != source_addr.sin_addr.s_addr ) { + goto bad_packet; + } + + /* UDP header */ + if ( iob_len ( iob ) < sizeof ( *udphdr ) ) { + goto bad_packet; + } + udphdr = iob->data; + if ( udphdr->dest != source_addr.sin_port ) { + goto bad_packet; + } + + /* Learn the remote connection details */ + memcpy ( dest_eth, ethhdr->h_source, ETH_ALEN ); + dest_addr.sin_addr.s_addr = iphdr->src.s_addr; + dest_addr.sin_port = udphdr->src; + + /* Payload */ + payload_len = ntohs ( udphdr->len ); + if ( payload_len < sizeof ( *udphdr ) || payload_len > iob_len ( iob ) ) { + goto bad_packet; + } + payload_len -= sizeof ( *udphdr ); + iob_pull ( iob, sizeof ( *udphdr ) ); + if ( payload_len > len ) { + goto bad_packet; + } + memcpy ( buf, iob->data, payload_len ); + + free_iob ( iob ); + return payload_len; + +bad_packet: + free_iob ( iob ); + } + cpu_nap(); + } +} + +static void gdbudp_send ( const char *buf, size_t len ) { + struct io_buffer *iob; + struct ethhdr *ethhdr; + struct iphdr *iphdr; + struct udp_header *udphdr; + + /* Check that we are connected */ + if ( dest_addr.sin_port == 0 ) { + return; + } + + gdbudp_ensure_netdev_open ( netdev ); + + iob = alloc_iob ( sizeof ( *ethhdr ) + sizeof ( *iphdr ) + sizeof ( *udphdr ) + len ); + if ( !iob ) { + return; + } + + /* Payload */ + iob_reserve ( iob, sizeof ( *ethhdr ) + sizeof ( *iphdr ) + sizeof ( *udphdr ) ); + memcpy ( iob_put ( iob, len ), buf, len ); + + /* UDP header */ + udphdr = iob_push ( iob, sizeof ( *udphdr ) ); + udphdr->src = source_addr.sin_port; + udphdr->dest = dest_addr.sin_port; + udphdr->len = htons ( iob_len ( iob ) ); + udphdr->chksum = 0; /* optional and we are not using it */ + + /* IP header */ + iphdr = iob_push ( iob, sizeof ( *iphdr ) ); + memset ( iphdr, 0, sizeof ( *iphdr ) ); + iphdr->verhdrlen = ( IP_VER | ( sizeof ( *iphdr ) / 4 ) ); + iphdr->service = IP_TOS; + iphdr->len = htons ( iob_len ( iob ) ); + iphdr->ttl = IP_TTL; + iphdr->protocol = IP_UDP; + iphdr->dest.s_addr = dest_addr.sin_addr.s_addr; + iphdr->src.s_addr = source_addr.sin_addr.s_addr; + iphdr->chksum = tcpip_chksum ( iphdr, sizeof ( *iphdr ) ); + + /* Ethernet header */ + ethhdr = iob_push ( iob, sizeof ( *ethhdr ) ); + memcpy ( ethhdr->h_dest, dest_eth, ETH_ALEN ); + memcpy ( ethhdr->h_source, netdev->ll_addr, ETH_ALEN ); + ethhdr->h_protocol = htons ( ETH_P_IP ); + + netdev_tx ( netdev, iob ); +} + +struct gdb_transport *gdbudp_configure ( const char *name, struct sockaddr_in *addr ) { + struct settings *settings; + + /* Release old network device */ + netdev_put ( netdev ); + + netdev = find_netdev ( name ); + if ( !netdev ) { + return NULL; + } + + /* Hold network device */ + netdev_get ( netdev ); + + /* Source UDP port */ + source_addr.sin_port = ( addr && addr->sin_port ) ? addr->sin_port : htons ( DEFAULT_PORT ); + + /* Source IP address */ + if ( addr && addr->sin_addr.s_addr ) { + source_addr.sin_addr.s_addr = addr->sin_addr.s_addr; + } else { + settings = netdev_settings ( netdev ); + fetch_ipv4_setting ( settings, &ip_setting, &source_addr.sin_addr ); + if ( source_addr.sin_addr.s_addr == 0 ) { + netdev_put ( netdev ); + netdev = NULL; + return NULL; + } + } + + return &udp_gdb_transport; +} + +static int gdbudp_init ( int argc, char **argv ) { + if ( argc != 1 ) { + printf ( "udp: missing argument\n" ); + return 1; + } + + if ( !gdbudp_configure ( argv[0], NULL ) ) { + printf ( "%s: device does not exist or has no IP address\n", argv[0] ); + return 1; + } + return 0; +} + +struct gdb_transport udp_gdb_transport __gdb_transport = { + .name = "udp", + .init = gdbudp_init, + .send = gdbudp_send, + .recv = gdbudp_recv, +}; diff --git a/gpxe/src/core/getkey.c b/gpxe/src/core/getkey.c new file mode 100644 index 0000000..787c902 --- /dev/null +++ b/gpxe/src/core/getkey.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include + +/** @file + * + * Special key interpretation + * + */ + +#define GETKEY_TIMEOUT ( TICKS_PER_SEC / 4 ) + +/** + * Read character from console if available within timeout period + * + * @v timeout Timeout period, in ticks + * @ret character Character read from console + */ +static int getchar_timeout ( unsigned long timeout ) { + unsigned long expiry = ( currticks() + timeout ); + + while ( currticks() < expiry ) { + step(); + if ( iskey() ) + return getchar(); + } + + return -1; +} + +/** + * Get single keypress + * + * @ret key Key pressed + * + * The returned key will be an ASCII value or a KEY_XXX special + * constant. This function differs from getchar() in that getchar() + * will return "special" keys (e.g. cursor keys) as a series of + * characters forming an ANSI escape sequence. + */ +int getkey ( void ) { + int character; + unsigned int n = 0; + + character = getchar(); + if ( character != ESC ) + return character; + + while ( ( character = getchar_timeout ( GETKEY_TIMEOUT ) ) >= 0 ) { + if ( character == '[' ) + continue; + if ( isdigit ( character ) ) { + n = ( ( n * 10 ) + ( character - '0' ) ); + continue; + } + if ( character >= 0x40 ) + return KEY_ANSI ( n, character ); + } + + return ESC; +} diff --git a/gpxe/src/core/getopt.c b/gpxe/src/core/getopt.c new file mode 100644 index 0000000..6de412b --- /dev/null +++ b/gpxe/src/core/getopt.c @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include + +/** @file + * + * Parse command-line options + * + */ + +/** + * Option argument + * + * This will point to the argument for the most recently returned + * option, if applicable. + */ +char *optarg; + +/** + * Current option index + * + * This is an index into the argv[] array. When getopt() returns -1, + * @c optind is the index to the first element that is not an option. + */ +int optind; + +/** + * Current option character index + * + * This is an index into the current element of argv[]. + */ +int nextchar; + +/** + * Unrecognised option + * + * When an unrecognised option is encountered, the actual option + * character is stored in @c optopt. + */ +int optopt; + +/** + * Get option argument from argv[] array + * + * @v argc Argument count + * @v argv Argument list + * @ret argument Option argument, or NULL + * + * Grab the next element of argv[], if it exists and is not an option. + */ +static const char * get_argv_argument ( int argc, char * const argv[] ) { + char *arg; + + /* Don't overrun argv[] */ + if ( optind >= argc ) + return NULL; + arg = argv[optind]; + + /* If next argv element is an option, then it's not usable as + * an argument. + */ + if ( *arg == '-' ) + return NULL; + + /** Consume this argv element, and return it */ + optind++; + return arg; +} + +/** + * Match long option + * + * @v argc Argument count + * @v argv Argument list + * @v opttext Option text within current argv[] element + * @v longopt Long option specification + * @ret option Option to return from getopt() + * @ret matched Found a match for this long option + */ +static int match_long_option ( int argc, char * const argv[], + const char *opttext, + const struct option *longopt, int *option ) { + size_t optlen; + const char *argument = NULL; + + /* Compare option name */ + optlen = strlen ( longopt->name ); + if ( strncmp ( opttext, longopt->name, optlen ) != 0 ) + return 0; + + /* Check for inline argument */ + if ( opttext[optlen] == '=' ) { + argument = &opttext[ optlen + 1 ]; + } else if ( opttext[optlen] ) { + /* Long option with trailing garbage - no match */ + return 0; + } + + /* Consume this argv element */ + optind++; + + /* If we want an argument but don't have one yet, try to grab + * the next argv element + */ + if ( ( longopt->has_arg != no_argument ) && ( ! argument ) ) + argument = get_argv_argument ( argc, argv ); + + /* If we need an argument but don't have one, sulk */ + if ( ( longopt->has_arg == required_argument ) && ( ! argument ) ) { + printf ( "Option \"%s\" requires an argument\n", + longopt->name ); + *option = ':'; + return 1; + } + + /* If we have an argument where we shouldn't have one, sulk */ + if ( ( longopt->has_arg == no_argument ) && argument ) { + printf ( "Option \"%s\" takes no argument\n", longopt->name ); + *option = ':'; + return 1; + } + + /* Store values and return success */ + optarg = ( char * ) argument; + if ( longopt->flag ) { + *(longopt->flag) = longopt->val; + *option = 0; + } else { + *option = longopt->val; + } + return 1; +} + +/** + * Match short option + * + * @v argc Argument count + * @v argv Argument list + * @v opttext Option text within current argv[] element + * @v shortopt Option character from option specification + * @ret option Option to return from getopt() + * @ret matched Found a match for this short option + */ +static int match_short_option ( int argc, char * const argv[], + const char *opttext, int shortopt, + enum getopt_argument_requirement has_arg, + int *option ) { + const char *argument = NULL; + + /* Compare option character */ + if ( *opttext != shortopt ) + return 0; + + /* Consume option character */ + opttext++; + nextchar++; + if ( *opttext ) { + if ( has_arg != no_argument ) { + /* Consume remainder of element as inline argument */ + argument = opttext; + optind++; + nextchar = 0; + } + } else { + /* Reached end of argv element */ + optind++; + nextchar = 0; + } + + /* If we want an argument but don't have one yet, try to grab + * the next argv element + */ + if ( ( has_arg != no_argument ) && ( ! argument ) ) + argument = get_argv_argument ( argc, argv ); + + /* If we need an argument but don't have one, sulk */ + if ( ( has_arg == required_argument ) && ( ! argument ) ) { + printf ( "Option \"%c\" requires an argument\n", shortopt ); + *option = ':'; + return 1; + } + + /* Store values and return success */ + optarg = ( char * ) argument; + *option = shortopt; + return 1; +} + +/** + * Parse command-line options + * + * @v argc Argument count + * @v argv Argument list + * @v optstring Option specification string + * @v longopts Long option specification table + * @ret longindex Index of long option (or NULL) + * @ret option Option found, or -1 for no more options + * + * Note that the caller must arrange for reset_getopt() to be called + * before each set of calls to getopt_long(). In Etherboot, this is + * done automatically by execv(). + */ +int getopt_long ( int argc, char * const argv[], const char *optstring, + const struct option *longopts, int *longindex ) { + const char *opttext = argv[optind]; + const struct option *longopt; + int shortopt; + enum getopt_argument_requirement has_arg; + int option; + + /* Check for end of argv array */ + if ( optind >= argc ) + return -1; + + /* Check for end of options */ + if ( *(opttext++) != '-' ) + return -1; + + /* Check for long options */ + if ( *(opttext++) == '-' ) { + for ( longopt = longopts ; longopt->name ; longopt++ ) { + if ( ! match_long_option ( argc, argv, opttext, + longopt, &option ) ) + continue; + if ( longindex ) + *longindex = ( longopt - longopts ); + return option; + } + optopt = '?'; + printf ( "Unrecognised option \"--%s\"\n", opttext ); + return '?'; + } + + /* Check for short options */ + if ( nextchar < 1 ) + nextchar = 1; + opttext = ( argv[optind] + nextchar ); + while ( ( shortopt = *(optstring++) ) ) { + has_arg = no_argument; + while ( *optstring == ':' ) { + has_arg++; + optstring++; + } + if ( match_short_option ( argc, argv, opttext, shortopt, + has_arg, &option ) ) { + return option; + } + } + optopt = *opttext; + printf ( "Unrecognised option \"-%c\"\n", optopt ); + return '?'; +} diff --git a/gpxe/src/core/hw.c b/gpxe/src/core/hw.c new file mode 100644 index 0000000..65604ee --- /dev/null +++ b/gpxe/src/core/hw.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * "Hello World" data source + * + */ + +struct hw { + struct refcnt refcnt; + struct xfer_interface xfer; + struct process process; +}; + +static const char hw_msg[] = "Hello world!\n"; + +static void hw_finished ( struct hw *hw, int rc ) { + xfer_nullify ( &hw->xfer ); + xfer_close ( &hw->xfer, rc ); + process_del ( &hw->process ); +} + +static void hw_xfer_close ( struct xfer_interface *xfer, int rc ) { + struct hw *hw = container_of ( xfer, struct hw, xfer ); + + hw_finished ( hw, rc ); +} + +static struct xfer_interface_operations hw_xfer_operations = { + .close = hw_xfer_close, + .vredirect = ignore_xfer_vredirect, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = xfer_deliver_as_raw, + .deliver_raw = ignore_xfer_deliver_raw, +}; + +static void hw_step ( struct process *process ) { + struct hw *hw = container_of ( process, struct hw, process ); + int rc; + + if ( xfer_window ( &hw->xfer ) ) { + rc = xfer_deliver_raw ( &hw->xfer, hw_msg, sizeof ( hw_msg ) ); + hw_finished ( hw, rc ); + } +} + +static int hw_open ( struct xfer_interface *xfer, struct uri *uri __unused ) { + struct hw *hw; + + /* Allocate and initialise structure */ + hw = zalloc ( sizeof ( *hw ) ); + if ( ! hw ) + return -ENOMEM; + xfer_init ( &hw->xfer, &hw_xfer_operations, &hw->refcnt ); + process_init ( &hw->process, hw_step, &hw->refcnt ); + + /* Attach parent interface, mortalise self, and return */ + xfer_plug_plug ( &hw->xfer, xfer ); + ref_put ( &hw->refcnt ); + return 0; +} + +struct uri_opener hw_uri_opener __uri_opener = { + .scheme = "hw", + .open = hw_open, +}; diff --git a/gpxe/src/core/i82365.c b/gpxe/src/core/i82365.c new file mode 100644 index 0000000..c26639e --- /dev/null +++ b/gpxe/src/core/i82365.c @@ -0,0 +1,656 @@ +#ifdef CONFIG_PCMCIA + +/* + * i82365.c + * Support for i82365 and similar ISA-to-PCMCIA bridges + * + * Taken from Linux kernel sources, distributed under GPL2 + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The initial developer of the original code is David A. Hinds + * . Portions created by David A. Hinds + * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + * + * Ported by: Anselm Martin Hoffmeister, Stockholm Projekt Computer-Service, Sankt Augustin/Bonn, GERMANY + */ + +/* + * + * + * ****************************** + * PLEASE DO NOT YET WORK ON THIS + * ****************************** + * + * I'm still fixing it up on every end, so we most probably would interfere + * at some point. If there's anything obvious or better, not-so-obvious, + * please contact me by e-mail: anselm (AT) hoffmeister (DOT) be *THANKS* + */ +#include "../include/pcmcia.h" +#include "../include/pcmcia-opts.h" +#include "../include/i82365.h" + +#ifndef CONFIG_ISA +#error PCMCIA_I82365 only works with ISA defined - set CONFIG_ISA +#endif + +typedef enum pcic_id { + IS_I82365A, IS_I82365B, IS_I82365DF, + IS_IBM, IS_RF5Cx96, IS_VLSI, IS_VG468, IS_VG469, + IS_PD6710, IS_PD672X, IS_VT83C469, +} pcic_id; + +/* Flags for classifying groups of controllers */ +#define IS_VADEM 0x0001 +#define IS_CIRRUS 0x0002 +#define IS_TI 0x0004 +#define IS_O2MICRO 0x0008 +#define IS_VIA 0x0010 +#define IS_TOPIC 0x0020 +#define IS_RICOH 0x0040 +#define IS_UNKNOWN 0x0400 +#define IS_VG_PWR 0x0800 +#define IS_DF_PWR 0x1000 +#define IS_PCI 0x2000 +#define IS_ALIVE 0x8000 + +typedef struct pcic_t { + char *name; + u_short flags; +} pcic_t; + +static pcic_t pcic[] = { + { "Intel i82365sl A step", 0 }, + { "Intel i82365sl B step", 0 }, + { "Intel i82365sl DF", IS_DF_PWR }, + { "IBM Clone", 0 }, + { "Ricoh RF5C296/396", 0 }, + { "VLSI 82C146", 0 }, + { "Vadem VG-468", IS_VADEM }, + { "Vadem VG-469", IS_VADEM|IS_VG_PWR }, + { "Cirrus PD6710", IS_CIRRUS }, + { "Cirrus PD672x", IS_CIRRUS }, + { "VIA VT83C469", IS_CIRRUS|IS_VIA }, +}; + +typedef struct cirrus_state_t { + u_char misc1, misc2; + u_char timer[6]; +} cirrus_state_t; + +typedef struct vg46x_state_t { + u_char ctl, ema; +} vg46x_state_t; + +typedef struct socket_info_t { + u_short type, flags; + socket_cap_t cap; + ioaddr_t ioaddr; + u_short psock; + u_char cs_irq, intr; + void (*handler)(void *info, u_int events); + void *info; + union { + cirrus_state_t cirrus; + vg46x_state_t vg46x; + } state; +} socket_info_t; + +//static socket_info_t socket[8]; + +int i365_base = 0x3e0; // Default in Linux kernel +int cycle_time = 120; // External clock time in ns, 120ns =~ 8.33 MHz +int mydriverid = 0; + +void phex ( unsigned char c ); +/*static int to_cycles(int ns) +{ + return ns/cycle_time; +} +*/ +/*static int to_ns(int cycles) +{ + return cycle_time*cycles; +} +*/ + +static u_char i365_get(u_short sock, u_short reg) +{ + //unsigned long flags; + //spin_lock_irqsave(&bus_lock,flags); + { + ioaddr_t port = pccsock[sock].ioaddr; + u_char val; + reg = I365_REG(pccsock[sock].internalid, reg); + outb(reg, port); val = inb(port+1); + //spin_unlock_irqrestore(&bus_lock,flags); + return val; + } +} + +static void i365_set(u_short sock, u_short reg, u_char data) +{ + //unsigned long flags; + //spin_lock_irqsave(&bus_lock,flags); + { + ioaddr_t port = pccsock[sock].ioaddr; + u_char val = I365_REG(pccsock[sock].internalid, reg); + outb(val, port); outb(data, port+1); + //spin_unlock_irqrestore(&bus_lock,flags); + } +} + +void add_socket_i365(u_short port, int psock, int type) { + pccsock[pccsocks].ioaddr = port; + pccsock[pccsocks].internalid = psock; + pccsock[pccsocks].type = type; + pccsock[pccsocks].flags = pcic[type].flags; + pccsock[pccsocks].drivernum = mydriverid; + pccsock[pccsocks].configoffset = -1; + // Find out if a card in inside that socket + pccsock[pccsocks].status = (( 12 == (i365_get(pccsocks,I365_STATUS)&12) ) ? HASCARD : EMPTY ); + // *TODO* check if that's all + if ( 0 == (psock & 1) ) { + printf ( "Found a PCMCIA controller (i82365) at io %x, type '%s'\n", port, pcic[type].name ); + // pccsock[pccsocks].status == HASCARD? "holds card":"empty" ); + } + pccsocks++; + return; +} + +void i365_bset(u_short sock, u_short reg, u_char mask) { + u_char d = i365_get(sock, reg); + d |= mask; + i365_set(sock, reg, d); +} + +void i365_bclr(u_short sock, u_short reg, u_char mask) { + u_char d = i365_get(sock, reg); + d &= ~mask; + i365_set(sock, reg, d); +} + + +/*static void i365_bflip(u_short sock, u_short reg, u_char mask, int b) +{ + u_char d = i365_get(sock, reg); + if (b) + d |= mask; + else + d &= ~mask; + i365_set(sock, reg, d); +} +*/ + +/* +static u_short i365_get_pair(u_short sock, u_short reg) +{ + u_short a, b; + a = i365_get(sock, reg); + b = i365_get(sock, reg+1); + return (a + (b<<8)); +} +*/ + +/* +static void i365_set_pair(u_short sock, u_short reg, u_short data) +{ + i365_set(sock, reg, data & 0xff); + i365_set(sock, reg+1, data >> 8); +} +*/ +int identify_i365 ( u_short port, u_short sock ) { + u_char val; + int type = -1; + /* Use the next free entry in the socket table */ + pccsock[pccsocks].ioaddr = port; + pccsock[pccsocks].internalid = sock; + // *TODO* wakeup a sleepy cirrus controller? + + if ((val = i365_get(pccsocks, I365_IDENT)) & 0x70) + return -1; + switch (val) { + case 0x82: + type = IS_I82365A; break; + case 0x83: + type = IS_I82365B; break; + case 0x84: + type = IS_I82365DF; break; + case 0x88: case 0x89: case 0x8a: + type = IS_IBM; break; + } + /* Check for Vadem VG-468 chips */ + outb(0x0e, port); + outb(0x37, port); + i365_bset(pccsocks, VG468_MISC, VG468_MISC_VADEMREV); + val = i365_get(pccsocks, I365_IDENT); + if (val & I365_IDENT_VADEM) { + i365_bclr(pccsocks, VG468_MISC, VG468_MISC_VADEMREV); + type = ((val & 7) >= 4) ? IS_VG469 : IS_VG468; + } + + /* Check for Ricoh chips */ + val = i365_get(pccsocks, RF5C_CHIP_ID); + if ((val == RF5C_CHIP_RF5C296) || (val == RF5C_CHIP_RF5C396)) type = IS_RF5Cx96; + + /* Check for Cirrus CL-PD67xx chips */ + i365_set(pccsocks, PD67_CHIP_INFO, 0); + val = i365_get(pccsocks, PD67_CHIP_INFO); + if ((val & PD67_INFO_CHIP_ID) == PD67_INFO_CHIP_ID) { + val = i365_get(pccsocks, PD67_CHIP_INFO); + if ((val & PD67_INFO_CHIP_ID) == 0) { + type = (val & PD67_INFO_SLOTS) ? IS_PD672X : IS_PD6710; + i365_set(pccsocks, PD67_EXT_INDEX, 0xe5); + if (i365_get(pccsocks, PD67_EXT_INDEX) != 0xe5) type = IS_VT83C469; + } + } + return type; +} + +int init_i82365(void) { + int i, j, sock, k, ns, id; + //unsigned int ui,uj; + //unsigned char * upc; + ioaddr_t port; + int i82365s = 0; + // Change from kernel: No irq init, no check_region, no isapnp support + // No ignore socket, no extra sockets to check (so it's easier here :-/) + // Probably we don't need any of them; in case YOU do, SHOUT AT ME! + id = identify_i365(i365_base, 0); + if ((id == IS_I82365DF) && (identify_i365(i365_base, 1) != id)) { + for (i = 0; i < 4; i++) { + port = i365_base + ((i & 1) << 2) + ((i & 2) << 1); + sock = (i & 1) << 1; + if (identify_i365(port, sock) == IS_I82365DF) { + add_socket_i365(port, sock, IS_VLSI); + } + } + } else { + for (i = 0; i < 4; i += 2) { + port = i365_base + 2*(i>>2); + sock = (i & 3); + id = identify_i365(port, sock); + if (id < 0) continue; + + for (j = ns = 0; j < 2; j++) { + /* Does the socket exist? */ + if (identify_i365(port, sock+j) < 0) continue; + /* Check for bad socket decode */ + for (k = 0; k <= i82365s; k++) + i365_set(k, I365_MEM(0)+I365_W_OFF, k); + for (k = 0; k <= i82365s; k++) + if (i365_get(k, I365_MEM(0)+I365_W_OFF) != k) + break; + if (k <= i82365s) break; + add_socket_i365(port, sock+j, id); ns++; + } + } + } + return 0; + + + + + + + +/* printf ( "Selecting config 1: io 0x300 @byte 87*2.." ); + upc[(2*87)] = 2; + i365_bclr(1, I365_ADDRWIN, 1 ); + i365_set(1,I365_INTCTL, 0x65 ); //no-reset, memory-card + i365_set(1, I365_IO(0)+0, 0x20 ); + i365_set(1, I365_IO(0)+1, 0x03 ); + i365_set(1, I365_IO(0)+2, 0x3f ); + i365_set(1, I365_IO(0)+3, 0x03 ); + i365_set(1, 0x3a, 0x05 ); + i365_set(1, 0x3b, 0x05 ); + i365_set(1, 0x3c, 0x05 ); + i365_set(1, 0x3d, 0x05 ); + i365_set(1, 0x3e, 0x05 ); + i365_set(1, 0x3f, 0x05 ); + i365_set(1, 0x07, 0x0a ); + i365_set(1, I365_ADDRWIN, 0x40 ); // 0x40 + printf ( "!\n" ); getchar(); + printf ( "\n" ); + return 0; */ +} + +void phex ( unsigned char c ) { + unsigned char a = 0, b = 0; + b = ( c & 0xf ); + if ( b > 9 ) b += ('a'-'9'-1); + b += '0'; + a = ( c & 0xf0 ) >> 4; + if ( a > 9 ) a += ('a'-'9'-1); + a += '0'; + printf ( "%c%c ", a, b ); + return; +} + +int deinit_i82365(void) { + printf("Deinitializing i82365\n" ); + return 0; +} + +/*static int i365_get_status(u_short sock, u_int *value) +{ + u_int status; + + status = i365_get(sock, I365_STATUS); + *value = ((status & I365_CS_DETECT) == I365_CS_DETECT) + ? SS_DETECT : 0; + + if (i365_get(sock, I365_INTCTL) & I365_PC_IOCARD) + *value |= (status & I365_CS_STSCHG) ? 0 : SS_STSCHG; + else { + *value |= (status & I365_CS_BVD1) ? 0 : SS_BATDEAD; + *value |= (status & I365_CS_BVD2) ? 0 : SS_BATWARN; + } + *value |= (status & I365_CS_WRPROT) ? SS_WRPROT : 0; + *value |= (status & I365_CS_READY) ? SS_READY : 0; + *value |= (status & I365_CS_POWERON) ? SS_POWERON : 0; + +#ifdef CONFIG_ISA + if (pccsock[sock].type == IS_VG469) { + status = i365_get(sock, VG469_VSENSE); + if (pccsock[sock].internalid & 1) { + *value |= (status & VG469_VSENSE_B_VS1) ? 0 : SS_3VCARD; + *value |= (status & VG469_VSENSE_B_VS2) ? 0 : SS_XVCARD; + } else { + *value |= (status & VG469_VSENSE_A_VS1) ? 0 : SS_3VCARD; + *value |= (status & VG469_VSENSE_A_VS2) ? 0 : SS_XVCARD; + } + } +#endif + + printf("i82365: GetStatus(%d) = %#4.4x\n", sock, *value); + return 0; +} //i365_get_status +*/ + +/*static int i365_set_socket(u_short sock, socket_state_t *state) +{ + socket_info_t *t = &socket[sock]; + u_char reg; + + printf("i82365: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, " + "io_irq %d, csc_mask %#2.2x)\n", sock, state->flags, + state->Vcc, state->Vpp, state->io_irq, state->csc_mask); +printf ("\nERROR:UNIMPLEMENTED\n" ); +return 0; + // First set global controller options + // set_bridge_state(sock); *TODO* check: need this here? + + // IO card, RESET flag, IO interrupt + reg = t->intr; + if (state->io_irq != t->cap.pci_irq) reg |= state->io_irq; + reg |= (state->flags & SS_RESET) ? 0 : I365_PC_RESET; + reg |= (state->flags & SS_IOCARD) ? I365_PC_IOCARD : 0; + i365_set(sock, I365_INTCTL, reg); + + reg = I365_PWR_NORESET; + if (state->flags & SS_PWR_AUTO) reg |= I365_PWR_AUTO; + if (state->flags & SS_OUTPUT_ENA) reg |= I365_PWR_OUT; + + if (t->flags & IS_CIRRUS) { + if (state->Vpp != 0) { + if (state->Vpp == 120) + reg |= I365_VPP1_12V; + else if (state->Vpp == state->Vcc) + reg |= I365_VPP1_5V; + else return -EINVAL; + } + if (state->Vcc != 0) { + reg |= I365_VCC_5V; + if (state->Vcc == 33) + i365_bset(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V); + else if (state->Vcc == 50) + i365_bclr(sock, PD67_MISC_CTL_1, PD67_MC1_VCC_3V); + else return -EINVAL; + } + } else if (t->flags & IS_VG_PWR) { + if (state->Vpp != 0) { + if (state->Vpp == 120) + reg |= I365_VPP1_12V; + else if (state->Vpp == state->Vcc) + reg |= I365_VPP1_5V; + else return -EINVAL; + } + if (state->Vcc != 0) { + reg |= I365_VCC_5V; + if (state->Vcc == 33) + i365_bset(sock, VG469_VSELECT, VG469_VSEL_VCC); + else if (state->Vcc == 50) + i365_bclr(sock, VG469_VSELECT, VG469_VSEL_VCC); + else return -EINVAL; + } + } else if (t->flags & IS_DF_PWR) { + switch (state->Vcc) { + case 0: break; + case 33: reg |= I365_VCC_3V; break; + case 50: reg |= I365_VCC_5V; break; + default: return -EINVAL; + } + switch (state->Vpp) { + case 0: break; + case 50: reg |= I365_VPP1_5V; break; + case 120: reg |= I365_VPP1_12V; break; + default: return -EINVAL; + } + } else { + switch (state->Vcc) { + case 0: break; + case 50: reg |= I365_VCC_5V; break; + default: return -EINVAL; + } + switch (state->Vpp) { + case 0: break; + case 50: reg |= I365_VPP1_5V | I365_VPP2_5V; break; + case 120: reg |= I365_VPP1_12V | I365_VPP2_12V; break; + default: return -EINVAL; + } + } + + if (reg != i365_get(sock, I365_POWER)) + i365_set(sock, I365_POWER, reg); + + // Chipset-specific functions + if (t->flags & IS_CIRRUS) { + // Speaker control + i365_bflip(sock, PD67_MISC_CTL_1, PD67_MC1_SPKR_ENA, + state->flags & SS_SPKR_ENA); + } + + // Card status change interrupt mask + reg = t->cs_irq << 4; + if (state->csc_mask & SS_DETECT) reg |= I365_CSC_DETECT; + if (state->flags & SS_IOCARD) { + if (state->csc_mask & SS_STSCHG) reg |= I365_CSC_STSCHG; + } else { + if (state->csc_mask & SS_BATDEAD) reg |= I365_CSC_BVD1; + if (state->csc_mask & SS_BATWARN) reg |= I365_CSC_BVD2; + if (state->csc_mask & SS_READY) reg |= I365_CSC_READY; + } + i365_set(sock, I365_CSCINT, reg); + i365_get(sock, I365_CSC); + + return 0; +} // i365_set_socket +*/ + +/*static int i365_get_io_map(u_short sock, struct pccard_io_map *io) +{ + u_char map, ioctl, addr; + printf ( "GETIOMAP unimplemented\n" ); return 0; + map = io->map; + if (map > 1) return -EINVAL; + io->start = i365_get_pair(sock, I365_IO(map)+I365_W_START); + io->stop = i365_get_pair(sock, I365_IO(map)+I365_W_STOP); + ioctl = i365_get(sock, I365_IOCTL); + addr = i365_get(sock, I365_ADDRWIN); + io->speed = to_ns(ioctl & I365_IOCTL_WAIT(map)) ? 1 : 0; + io->flags = (addr & I365_ENA_IO(map)) ? MAP_ACTIVE : 0; + io->flags |= (ioctl & I365_IOCTL_0WS(map)) ? MAP_0WS : 0; + io->flags |= (ioctl & I365_IOCTL_16BIT(map)) ? MAP_16BIT : 0; + io->flags |= (ioctl & I365_IOCTL_IOCS16(map)) ? MAP_AUTOSZ : 0; + printf("i82365: GetIOMap(%d, %d) = %#2.2x, %d ns, " + "%#4.4x-%#4.4x\n", sock, map, io->flags, io->speed, + io->start, io->stop); + return 0; +} // i365_get_io_map +*/ + +/*====================================================================*/ + +/*static int i365_set_io_map(u_short sock, struct pccard_io_map *io) +{ + u_char map, ioctl; + + printf("i82365: SetIOMap(%d, %d, %#2.2x, %d ns, " + "%#4.4x-%#4.4x)\n", sock, io->map, io->flags, + io->speed, io->start, io->stop); +printf ( "UNIMPLEMENTED\n" ); + return 0; + map = io->map; + //if ((map > 1) || (io->start > 0xffff) || (io->stop > 0xffff) || + if ((map > 1) || + (io->stop < io->start)) return -EINVAL; + // Turn off the window before changing anything + if (i365_get(sock, I365_ADDRWIN) & I365_ENA_IO(map)) + i365_bclr(sock, I365_ADDRWIN, I365_ENA_IO(map)); + i365_set_pair(sock, I365_IO(map)+I365_W_START, io->start); + i365_set_pair(sock, I365_IO(map)+I365_W_STOP, io->stop); + ioctl = i365_get(sock, I365_IOCTL) & ~I365_IOCTL_MASK(map); + if (io->speed) ioctl |= I365_IOCTL_WAIT(map); + if (io->flags & MAP_0WS) ioctl |= I365_IOCTL_0WS(map); + if (io->flags & MAP_16BIT) ioctl |= I365_IOCTL_16BIT(map); + if (io->flags & MAP_AUTOSZ) ioctl |= I365_IOCTL_IOCS16(map); + i365_set(sock, I365_IOCTL, ioctl); + // Turn on the window if necessary + if (io->flags & MAP_ACTIVE) + i365_bset(sock, I365_ADDRWIN, I365_ENA_IO(map)); + return 0; +} // i365_set_io_map +*/ + +/* +static int i365_set_mem_map(u_short sock, struct pccard_mem_map *mem) +{ + u_short base, i; + u_char map; + + printf("i82365: SetMemMap(%d, %d, %#2.2x, %d ns, %#5.5lx-%#5.5" + "lx, %#5.5x)\n", sock, mem->map, mem->flags, mem->speed, + mem->sys_start, mem->sys_stop, mem->card_start); + +printf ( "UNIMPLEMENTED\n" ); + return 0; + map = mem->map; + if ((map > 4) || (mem->card_start > 0x3ffffff) || + (mem->sys_start > mem->sys_stop) || (mem->speed > 1000)) + return -EINVAL; + if (!(socket[sock].flags & IS_PCI) && + ((mem->sys_start > 0xffffff) || (mem->sys_stop > 0xffffff))) + return -EINVAL; + + // Turn off the window before changing anything + if (i365_get(sock, I365_ADDRWIN) & I365_ENA_MEM(map)) + i365_bclr(sock, I365_ADDRWIN, I365_ENA_MEM(map)); + + base = I365_MEM(map); + i = (mem->sys_start >> 12) & 0x0fff; + if (mem->flags & MAP_16BIT) i |= I365_MEM_16BIT; + if (mem->flags & MAP_0WS) i |= I365_MEM_0WS; + i365_set_pair(sock, base+I365_W_START, i); + + i = (mem->sys_stop >> 12) & 0x0fff; + switch (to_cycles(mem->speed)) { + case 0: break; + case 1: i |= I365_MEM_WS0; break; + case 2: i |= I365_MEM_WS1; break; + default: i |= I365_MEM_WS1 | I365_MEM_WS0; break; + } + i365_set_pair(sock, base+I365_W_STOP, i); + + i = ((mem->card_start - mem->sys_start) >> 12) & 0x3fff; + if (mem->flags & MAP_WRPROT) i |= I365_MEM_WRPROT; + if (mem->flags & MAP_ATTRIB) i |= I365_MEM_REG; + i365_set_pair(sock, base+I365_W_OFF, i); + + // Turn on the window if necessary + if (mem->flags & MAP_ACTIVE) + i365_bset(sock, I365_ADDRWIN, I365_ENA_MEM(map)); + return 0; +} // i365_set_mem_map +*/ + + +int i82365_interfacer ( interface_func_t func, int sockno, int par1, int par2, void* par3 ) { + //int i, j, k; + //u_int ui; + u_char *upc; + struct pcc_config_t * pccc; + switch ( func ) { + case INIT: + mydriverid = par1; + return init_i82365(); + case SHUTDOWN: + i365_set(sockno, I365_ADDRWIN, i365_get(sockno, I365_ADDRWIN) & 0x20 ); + i365_set(sockno, I365_INTCTL, 0x05 ); + sleepticks(2); + i365_set(sockno,I365_INTCTL, 0x45 ); //no-reset, memory-card + break; + case MAPATTRMEM: + i365_set(sockno,I365_POWER, 0xb1 ); + i365_set(sockno, I365_INTCTL, 0x05 ); + sleepticks(2); + i365_set(sockno,I365_INTCTL, 0x45 ); //no-reset, memory-card + i365_set(sockno, I365_ADDRWIN, i365_get(sockno, I365_ADDRWIN) & 0x20 ); + //i365_bclr(sockno, I365_ADDRWIN, 1 ); + i365_set(sockno, I365_MEM(0)+0, ( par1 >> 12 )& 0xff ); //start + i365_set(sockno, I365_MEM(0)+1, ( par1 >> 20 ) & 0x0f ); + i365_set(sockno, I365_MEM(0)+2, ((par1 + par2 - 1 ) >> 12 ) & 0xff ); //end + i365_set(sockno, I365_MEM(0)+3, (( par1 + par2 - 1 ) >> 20 ) & 0x0f ); + i365_set(sockno, I365_MEM(0)+4, ((0x4000000 - par1) >> 12) & 0xff ); //offset low + i365_set(sockno, I365_MEM(0)+5, 0x40 | (((0x40000000 - par1) >> 12) & 0x3f)); + i365_bset(sockno, I365_ADDRWIN, 1 ); + if ( ! ( 1 & i365_get ( sockno, I365_ADDRWIN ) ) ) return 1; + break; + case UNMAPATTRMEM: + i365_set(sockno, I365_ADDRWIN, i365_get(sockno, I365_ADDRWIN) & 0x20 ); + i365_set(sockno,I365_INTCTL, 0x45 ); //no-reset, memory-card + break; + case SELECTCONFIG: // Params: par1: config number; par3 config pointer pointer + if ( 0 > pccsock[sockno].configoffset ) return 1; + if ( NULL == (pccc = par3 ) ) return 2; + // write config number to + upc = ioremap ( MAP_ATTRMEM_TO, MAP_ATTRMEM_LEN ); + if ( pccsock[sockno].configoffset > MAP_ATTRMEM_LEN ) return 3; + if ( ( par1 & 0x7fffffc0 ) ) return 4; + if ( pccc->index != par1 ) return 5; + upc[pccsock[sockno].configoffset] = ( upc[pccsock[sockno].configoffset] & 0xc0 ) | ( par1 & 0x3f ); + i365_set(sockno, I365_IOCTL, (i365_get(sockno, I365_IOCTL) & 0xfe) | 0x20 ); // 16bit autosize + i365_set(sockno, I365_IO(0)+0, pccc->iowin & 0xff); + i365_set(sockno, I365_IO(0)+1, (pccc->iowin >> 8) & 0xff); + i365_set(sockno, I365_IO(0)+2, (pccc->iowin+pccc->iolen - 1) & 0xff); + i365_set(sockno, I365_IO(0)+3, ((pccc->iowin+pccc->iolen- 1) >> 8) & 0xff); + // Disable mem mapping + i365_bclr(sockno, I365_ADDRWIN, 1); + i365_set(sockno, I365_INTCTL, 0x65); + i365_bset(sockno, I365_ADDRWIN,0x40); + break; + default: + return -1; // ERROR: Unknown function called + } + return 0; +} + +// get_mem_map[1320] +// cirrus_get_state/set/opts... +// vg46x_get_state/... +// get_bridge_state/... + +#endif /* CONFIG_PCMCIA */ diff --git a/gpxe/src/core/image.c b/gpxe/src/core/image.c new file mode 100644 index 0000000..277d09a --- /dev/null +++ b/gpxe/src/core/image.c @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Executable/loadable images + * + */ + +/** List of registered images */ +struct list_head images = LIST_HEAD_INIT ( images ); + +/** List of image types */ +static struct image_type image_types[0] + __table_start ( struct image_type, image_types ); +static struct image_type image_types_end[0] + __table_end ( struct image_type, image_types ); + +/** + * Free executable/loadable image + * + * @v refcnt Reference counter + */ +static void free_image ( struct refcnt *refcnt ) { + struct image *image = container_of ( refcnt, struct image, refcnt ); + + uri_put ( image->uri ); + ufree ( image->data ); + image_put ( image->replacement ); + free ( image ); + DBGC ( image, "IMAGE %p freed\n", image ); +} + +/** + * Allocate executable/loadable image + * + * @ret image Executable/loadable image + */ +struct image * alloc_image ( void ) { + struct image *image; + + image = zalloc ( sizeof ( *image ) ); + if ( image ) { + image->refcnt.free = free_image; + } + return image; +} + +/** + * Set image URI + * + * @v image Image + * @v URI New image URI + * @ret rc Return status code + * + * If no name is set, the name will be updated to the base name of the + * URI path (if any). + */ +int image_set_uri ( struct image *image, struct uri *uri ) { + const char *path = uri->path; + + /* Replace URI reference */ + uri_put ( image->uri ); + image->uri = uri_get ( uri ); + + /* Set name if none already specified */ + if ( path && ( ! image->name[0] ) ) + image_set_name ( image, basename ( ( char * ) path ) ); + + return 0; +} + +/** + * Set image command line + * + * @v image Image + * @v cmdline New image command line + * @ret rc Return status code + */ +int image_set_cmdline ( struct image *image, const char *cmdline ) { + free ( image->cmdline ); + image->cmdline = strdup ( cmdline ); + if ( ! image->cmdline ) + return -ENOMEM; + return 0; +} + +/** + * Register executable/loadable image + * + * @v image Executable/loadable image + * @ret rc Return status code + */ +int register_image ( struct image *image ) { + static unsigned int imgindex = 0; + + /* Create image name if it doesn't already have one */ + if ( ! image->name[0] ) { + snprintf ( image->name, sizeof ( image->name ), "img%d", + imgindex++ ); + } + + /* Add to image list */ + image_get ( image ); + list_add_tail ( &image->list, &images ); + DBGC ( image, "IMAGE %p at [%lx,%lx) registered as %s\n", + image, user_to_phys ( image->data, 0 ), + user_to_phys ( image->data, image->len ), image->name ); + + return 0; +} + +/** + * Unregister executable/loadable image + * + * @v image Executable/loadable image + */ +void unregister_image ( struct image *image ) { + DBGC ( image, "IMAGE %p unregistered\n", image ); + list_del ( &image->list ); + image_put ( image ); +} + +/** + * Find image by name + * + * @v name Image name + * @ret image Executable/loadable image, or NULL + */ +struct image * find_image ( const char *name ) { + struct image *image; + + list_for_each_entry ( image, &images, list ) { + if ( strcmp ( image->name, name ) == 0 ) + return image; + } + + return NULL; +} + +/** + * Load executable/loadable image into memory + * + * @v image Executable/loadable image + * @v type Executable/loadable image type + * @ret rc Return status code + */ +static int image_load_type ( struct image *image, struct image_type *type ) { + int rc; + + /* Check image is actually loadable */ + if ( ! type->load ) + return -ENOEXEC; + + /* Try the image loader */ + if ( ( rc = type->load ( image ) ) != 0 ) { + DBGC ( image, "IMAGE %p could not load as %s: %s\n", + image, type->name, strerror ( rc ) ); + return rc; + } + + /* Flag as loaded */ + image->flags |= IMAGE_LOADED; + return 0; +} + +/** + * Load executable/loadable image into memory + * + * @v image Executable/loadable image + * @ret rc Return status code + */ +int image_load ( struct image *image ) { + + assert ( image->type != NULL ); + + return image_load_type ( image, image->type ); +} + +/** + * Autodetect image type and load executable/loadable image into memory + * + * @v image Executable/loadable image + * @ret rc Return status code + */ +int image_autoload ( struct image *image ) { + struct image_type *type; + int rc; + + /* If image already has a type, use it */ + if ( image->type ) + return image_load ( image ); + + /* Otherwise probe for a suitable type */ + for ( type = image_types ; type < image_types_end ; type++ ) { + DBGC ( image, "IMAGE %p trying type %s\n", image, type->name ); + rc = image_load_type ( image, type ); + if ( image->type == NULL ) + continue; + return rc; + } + + DBGC ( image, "IMAGE %p format not recognised\n", image ); + return -ENOEXEC; +} + +/** + * Execute loaded image + * + * @v image Loaded image + * @ret rc Return status code + */ +int image_exec ( struct image *image ) { + struct image *replacement; + struct uri *old_cwuri; + int rc; + + /* Image must be loaded first */ + if ( ! ( image->flags & IMAGE_LOADED ) ) { + DBGC ( image, "IMAGE %p could not execute: not loaded\n", + image ); + return -ENOTTY; + } + + assert ( image->type != NULL ); + + /* Check that image is actually executable */ + if ( ! image->type->exec ) + return -ENOEXEC; + + /* Switch current working directory to be that of the image itself */ + old_cwuri = uri_get ( cwuri ); + churi ( image->uri ); + + /* Take out a temporary reference to the image. This allows + * the image to unregister itself if necessary, without + * automatically freeing itself. + */ + image_get ( image ); + + /* Try executing the image */ + if ( ( rc = image->type->exec ( image ) ) != 0 ) { + DBGC ( image, "IMAGE %p could not execute: %s\n", + image, strerror ( rc ) ); + /* Do not return yet; we still have clean-up to do */ + } + + /* Pick up replacement image before we drop the original + * image's temporary reference. + */ + replacement = image->replacement; + + /* Drop temporary reference to the original image */ + image_put ( image ); + + /* Reset current working directory */ + churi ( old_cwuri ); + uri_put ( old_cwuri ); + + /* Tail-recurse into replacement image, if one exists */ + if ( replacement ) { + DBGC ( image, "IMAGE %p replacing self with IMAGE %p\n", + image, replacement ); + if ( ( rc = image_exec ( replacement ) ) != 0 ) + return rc; + } + + return rc; +} + +/** + * Register and autoload an image + * + * @v image Image + * @ret rc Return status code + */ +int register_and_autoload_image ( struct image *image ) { + int rc; + + if ( ( rc = register_image ( image ) ) != 0 ) + return rc; + + if ( ( rc = image_autoload ( image ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Register and autoexec an image + * + * @v image Image + * @ret rc Return status code + */ +int register_and_autoexec_image ( struct image *image ) { + int rc; + + if ( ( rc = register_and_autoload_image ( image ) ) != 0 ) + return rc; + + if ( ( rc = image_exec ( image ) ) != 0 ) + return rc; + + return 0; +} diff --git a/gpxe/src/core/init.c b/gpxe/src/core/init.c new file mode 100644 index 0000000..50e199c --- /dev/null +++ b/gpxe/src/core/init.c @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include + +/** @file + * + * Initialisation, startup and shutdown routines + * + */ + +/** Registered initialisation functions */ +static struct init_fn init_fns[0] + __table_start ( struct init_fn, init_fns ); +static struct init_fn init_fns_end[0] + __table_end ( struct init_fn, init_fns ); + +/** Registered startup/shutdown functions */ +static struct startup_fn startup_fns[0] + __table_start ( struct startup_fn, startup_fns ); +static struct startup_fn startup_fns_end[0] + __table_end ( struct startup_fn, startup_fns ); + +/** "startup() has been called" flag */ +static int started = 0; + +/** + * Initialise gPXE + * + * This function performs the one-time-only and irreversible + * initialisation steps, such as initialising the heap. It must be + * called before (almost) any other function. + * + * There is, by definition, no counterpart to this function on the + * shutdown path. + */ +void initialise ( void ) { + struct init_fn *init_fn; + + /* Call registered initialisation functions */ + for ( init_fn = init_fns ; init_fn < init_fns_end ; init_fn++ ) { + init_fn->initialise (); + } +} + +/** + * Start up gPXE + * + * This function performs the repeatable initialisation steps, such as + * probing devices. You may call startup() and shutdown() multiple + * times (as is done via the PXE API when PXENV_START_UNDI is used). + */ +void startup ( void ) { + struct startup_fn *startup_fn; + + if ( started ) + return; + + /* Call registered startup functions */ + for ( startup_fn = startup_fns ; startup_fn < startup_fns_end ; + startup_fn++ ) { + if ( startup_fn->startup ) + startup_fn->startup(); + } + + started = 1; +} + +/** + * Shut down gPXE + * + * @v flags Shutdown behaviour flags + * + * This function reverses the actions of startup(), and leaves gPXE in + * a state ready to be removed from memory. You may call startup() + * again after calling shutdown(). + + * Call this function only once, before either exiting main() or + * starting up a non-returnable image. + */ +void shutdown ( int flags ) { + struct startup_fn *startup_fn; + + if ( ! started ) + return; + + /* Call registered shutdown functions (in reverse order) */ + for ( startup_fn = startup_fns_end - 1 ; startup_fn >= startup_fns ; + startup_fn-- ) { + if ( startup_fn->shutdown ) + startup_fn->shutdown ( flags ); + } + + started = 0; +} diff --git a/gpxe/src/core/interface.c b/gpxe/src/core/interface.c new file mode 100644 index 0000000..37aabfe --- /dev/null +++ b/gpxe/src/core/interface.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2007 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. + */ + +#include + +/** @file + * + * Object communication interfaces + * + */ + +/** + * Plug an interface into a new destination interface + * + * @v intf Interface + * @v dest New destination interface + * + * The reference to the existing destination interface is dropped, a + * reference to the new destination interface is obtained, and the + * interface is updated to point to the new destination interface. + * + * Note that there is no "unplug" call; instead you must plug the + * interface into a null interface. + */ +void plug ( struct interface *intf, struct interface *dest ) { + DBGC ( intf, "INTF %p moving from INTF %p to INTF %p\n", + intf, intf->dest, dest ); + intf_put ( intf->dest ); + intf->dest = intf_get ( dest ); +} + +/** + * Plug two interfaces together + * + * @v a Interface A + * @v b Interface B + * + * Plugs interface A into interface B, and interface B into interface + * A. (The basic plug() function is unidirectional; this function is + * merely a shorthand for two calls to plug(), hence the name.) + */ +void plug_plug ( struct interface *a, struct interface *b ) { + plug ( a, b ); + plug ( b, a ); +} diff --git a/gpxe/src/core/iobuf.c b/gpxe/src/core/iobuf.c new file mode 100644 index 0000000..cc4aede --- /dev/null +++ b/gpxe/src/core/iobuf.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include + +/** @file + * + * I/O buffers + * + */ + +/** + * Allocate I/O buffer + * + * @v len Required length of buffer + * @ret iobuf I/O buffer, or NULL if none available + * + * The I/O buffer will be physically aligned to a multiple of + * @c IOBUF_SIZE. + */ +struct io_buffer * alloc_iob ( size_t len ) { + struct io_buffer *iobuf = NULL; + void *data; + + /* Pad to minimum length */ + if ( len < IOB_ZLEN ) + len = IOB_ZLEN; + + /* Align buffer length */ + len = ( len + __alignof__( *iobuf ) - 1 ) & + ~( __alignof__( *iobuf ) - 1 ); + + /* Allocate memory for buffer plus descriptor */ + data = malloc_dma ( len + sizeof ( *iobuf ), IOB_ALIGN ); + if ( ! data ) + return NULL; + + iobuf = ( struct io_buffer * ) ( data + len ); + iobuf->head = iobuf->data = iobuf->tail = data; + iobuf->end = iobuf; + return iobuf; +} + +/** + * Free I/O buffer + * + * @v iobuf I/O buffer + */ +void free_iob ( struct io_buffer *iobuf ) { + if ( iobuf ) { + assert ( iobuf->head <= iobuf->data ); + assert ( iobuf->data <= iobuf->tail ); + assert ( iobuf->tail <= iobuf->end ); + free_dma ( iobuf->head, + ( iobuf->end - iobuf->head ) + sizeof ( *iobuf ) ); + } +} + +/** + * Ensure I/O buffer has sufficient headroom + * + * @v iobuf I/O buffer + * @v len Required headroom + * + * This function currently only checks for the required headroom; it + * does not reallocate the I/O buffer if required. If we ever have a + * code path that requires this functionality, it's a fairly trivial + * change to make. + */ +int iob_ensure_headroom ( struct io_buffer *iobuf, size_t len ) { + + if ( iob_headroom ( iobuf ) >= len ) + return 0; + return -ENOBUFS; +} + diff --git a/gpxe/src/core/job.c b/gpxe/src/core/job.c new file mode 100644 index 0000000..6c2faf3 --- /dev/null +++ b/gpxe/src/core/job.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include + +/** @file + * + * Job control interfaces + * + */ + +void job_done ( struct job_interface *job, int rc ) { + struct job_interface *dest = job_get_dest ( job ); + + job_unplug ( job ); + dest->op->done ( dest, rc ); + job_put ( dest ); +} + +void job_kill ( struct job_interface *job ) { + struct job_interface *dest = job_get_dest ( job ); + + job_unplug ( job ); + dest->op->kill ( dest ); + job_put ( dest ); +} + +/**************************************************************************** + * + * Helper methods + * + * These functions are designed to be used as methods in the + * job_interface_operations table. + * + */ + +void ignore_job_done ( struct job_interface *job __unused, int rc __unused ) { + /* Nothing to do */ +} + +void ignore_job_kill ( struct job_interface *job __unused ) { + /* Nothing to do */ +} + +void ignore_job_progress ( struct job_interface *job __unused, + struct job_progress *progress ) { + memset ( progress, 0, sizeof ( *progress ) ); +} + +/** Null job control interface operations */ +struct job_interface_operations null_job_ops = { + .done = ignore_job_done, + .kill = ignore_job_kill, + .progress = ignore_job_progress, +}; + +/** + * Null job control interface + * + * This is the interface to which job control interfaces are connected + * when unplugged. It will never generate messages, and will silently + * absorb all received messages. + */ +struct job_interface null_job = { + .intf = { + .dest = &null_job.intf, + .refcnt = NULL, + }, + .op = &null_job_ops, +}; diff --git a/gpxe/src/core/linebuf.c b/gpxe/src/core/linebuf.c new file mode 100644 index 0000000..d02f37c --- /dev/null +++ b/gpxe/src/core/linebuf.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2007 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. + */ + +/** + * @file + * + * Line buffering + * + */ + +#include +#include +#include +#include +#include + +/** + * Retrieve buffered-up line + * + * @v linebuf Line buffer + * @ret line Buffered line, or NULL if no line ready to read + */ +char * buffered_line ( struct line_buffer *linebuf ) { + return ( linebuf->ready ? linebuf->data : NULL ); +} + +/** + * Discard line buffer contents + * + * @v linebuf Line buffer + */ +void empty_line_buffer ( struct line_buffer *linebuf ) { + free ( linebuf->data ); + linebuf->data = NULL; + linebuf->len = 0; + linebuf->ready = 0; +} + +/** + * Buffer up received data by lines + * + * @v linebuf Line buffer + * @v data New data to add + * @v len Length of new data to add + * @ret len Consumed length, or negative error number + * + * After calling line_buffer(), use buffered_line() to determine + * whether or not a complete line is available. Carriage returns and + * newlines will have been stripped, and the line will be + * NUL-terminated. This buffered line is valid only until the next + * call to line_buffer() (or to empty_line_buffer()). + * + * Note that line buffers use dynamically allocated storage; you + * should call empty_line_buffer() before freeing a @c struct @c + * line_buffer. + */ +ssize_t line_buffer ( struct line_buffer *linebuf, + const char *data, size_t len ) { + const char *eol; + size_t consume; + size_t new_len; + char *new_data; + + /* Free any completed line from previous iteration */ + if ( linebuf->ready ) + empty_line_buffer ( linebuf ); + + /* Search for line terminator */ + if ( ( eol = memchr ( data, '\n', len ) ) ) { + consume = ( eol - data + 1 ); + } else { + consume = len; + } + + /* Reallocate data buffer and copy in new data */ + new_len = ( linebuf->len + consume ); + new_data = realloc ( linebuf->data, ( new_len + 1 ) ); + if ( ! new_data ) + return -ENOMEM; + memcpy ( ( new_data + linebuf->len ), data, consume ); + new_data[new_len] = '\0'; + linebuf->data = new_data; + linebuf->len = new_len; + + /* If we have reached end of line, trim the line and mark as ready */ + if ( eol ) { + linebuf->data[--linebuf->len] = '\0'; /* trim NL */ + if ( linebuf->data[linebuf->len - 1] == '\r' ) + linebuf->data[--linebuf->len] = '\0'; /* trim CR */ + linebuf->ready = 1; + } + + return consume; +} diff --git a/gpxe/src/core/main.c b/gpxe/src/core/main.c new file mode 100644 index 0000000..bd2428f --- /dev/null +++ b/gpxe/src/core/main.c @@ -0,0 +1,94 @@ +/************************************************************************** +gPXE - Network Bootstrap Program + +Literature dealing with the network protocols: + ARP - RFC826 + RARP - RFC903 + UDP - RFC768 + BOOTP - RFC951, RFC2132 (vendor extensions) + DHCP - RFC2131, RFC2132 (options) + TFTP - RFC1350, RFC2347 (options), RFC2348 (blocksize), RFC2349 (tsize) + RPC - RFC1831, RFC1832 (XDR), RFC1833 (rpcbind/portmapper) + NFS - RFC1094, RFC1813 (v3, useful for clarifications, not implemented) + IGMP - RFC1112 + +**************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define NORMAL "\033[0m" +#define BOLD "\033[1m" +#define CYAN "\033[36m" + +static struct feature features[0] __table_start ( struct feature, features ); +static struct feature features_end[0] __table_end ( struct feature, features ); + +/** + * Main entry point + * + * @ret rc Return status code + */ +__asmcall int main ( void ) { + struct feature *feature; + struct image *image; + + /* Some devices take an unreasonably long time to initialise */ + printf ( PRODUCT_SHORT_NAME " initialising devices...\n" ); + + initialise(); + startup(); + + /* + * Print welcome banner + * + * + * If you wish to brand this build of gPXE, please do so by + * defining the string PRODUCT_NAME in config/general.h. + * + * While nothing in the GPL prevents you from removing all + * references to gPXE or http://etherboot.org, we prefer you + * not to do so. + * + */ + printf ( NORMAL "\n\n" PRODUCT_NAME "\n" BOLD "gPXE " VERSION + NORMAL " -- Open Source Boot Firmware -- " + CYAN "http://etherboot.org" NORMAL "\n" + "Features:" ); + for ( feature = features ; feature < features_end ; feature++ ) + printf ( " %s", feature->name ); + printf ( "\n" ); + + /* Prompt for shell */ + if ( shell_banner() ) { + /* User wants shell; just give them a shell */ + shell(); + } else { + /* User doesn't want shell; load and execute the first + * image, or autoboot() if we have no images. If + * booting fails for any reason, offer a second chance + * to enter the shell for diagnostics. + */ + if ( have_images() ) { + for_each_image ( image ) { + image_exec ( image ); + break; + } + } else { + autoboot(); + } + + if ( shell_banner() ) + shell(); + } + + shutdown ( SHUTDOWN_EXIT | shutdown_exit_flags ); + + return 0; +} diff --git a/gpxe/src/core/malloc.c b/gpxe/src/core/malloc.c new file mode 100644 index 0000000..db7f1be --- /dev/null +++ b/gpxe/src/core/malloc.c @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Dynamic memory allocation + * + */ + +/** A free block of memory */ +struct memory_block { + /** List of free blocks */ + struct list_head list; + /** Size of this block */ + size_t size; +}; + +#define MIN_MEMBLOCK_SIZE \ + ( ( size_t ) ( 1 << ( fls ( sizeof ( struct memory_block ) - 1 ) ) ) ) + +/** A block of allocated memory complete with size information */ +struct autosized_block { + /** Size of this block */ + size_t size; + /** Remaining data */ + char data[0]; +}; + +/** + * Address for zero-length memory blocks + * + * @c malloc(0) or @c realloc(ptr,0) will return the special value @c + * NOWHERE. Calling @c free(NOWHERE) will have no effect. + * + * This is consistent with the ANSI C standards, which state that + * "either NULL or a pointer suitable to be passed to free()" must be + * returned in these cases. Using a special non-NULL value means that + * the caller can take a NULL return value to indicate failure, + * without first having to check for a requested size of zero. + * + * Code outside of malloc.c do not ever need to refer to the actual + * value of @c NOWHERE; this is an internal definition. + */ +#define NOWHERE ( ( void * ) ~( ( intptr_t ) 0 ) ) + +/** List of free memory blocks */ +static LIST_HEAD ( free_blocks ); + +/** Total amount of free memory */ +size_t freemem; + +/** + * Heap size + * + * Currently fixed at 128kB. + */ +#define HEAP_SIZE ( 128 * 1024 ) + +/** The heap itself */ +static char heap[HEAP_SIZE] __attribute__ (( aligned ( __alignof__(void *) ))); + +/** + * Allocate a memory block + * + * @v size Requested size + * @v align Physical alignment + * @ret ptr Memory block, or NULL + * + * Allocates a memory block @b physically aligned as requested. No + * guarantees are provided for the alignment of the virtual address. + * + * @c align must be a power of two. @c size may not be zero. + */ +void * alloc_memblock ( size_t size, size_t align ) { + struct memory_block *block; + size_t align_mask; + size_t pre_size; + ssize_t post_size; + struct memory_block *pre; + struct memory_block *post; + + /* Round up size to multiple of MIN_MEMBLOCK_SIZE and + * calculate alignment mask. + */ + size = ( size + MIN_MEMBLOCK_SIZE - 1 ) & ~( MIN_MEMBLOCK_SIZE - 1 ); + align_mask = ( align - 1 ) | ( MIN_MEMBLOCK_SIZE - 1 ); + + DBG ( "Allocating %#zx (aligned %#zx)\n", size, align ); + + /* Search through blocks for the first one with enough space */ + list_for_each_entry ( block, &free_blocks, list ) { + pre_size = ( - virt_to_phys ( block ) ) & align_mask; + post_size = block->size - pre_size - size; + if ( post_size >= 0 ) { + /* Split block into pre-block, block, and + * post-block. After this split, the "pre" + * block is the one currently linked into the + * free list. + */ + pre = block; + block = ( ( ( void * ) pre ) + pre_size ); + post = ( ( ( void * ) block ) + size ); + DBG ( "[%p,%p) -> [%p,%p) + [%p,%p)\n", pre, + ( ( ( void * ) pre ) + pre->size ), pre, block, + post, ( ( ( void * ) pre ) + pre->size ) ); + /* If there is a "post" block, add it in to + * the free list. Leak it if it is too small + * (which can happen only at the very end of + * the heap). + */ + if ( ( size_t ) post_size >= MIN_MEMBLOCK_SIZE ) { + post->size = post_size; + list_add ( &post->list, &pre->list ); + } + /* Shrink "pre" block, leaving the main block + * isolated and no longer part of the free + * list. + */ + pre->size = pre_size; + /* If there is no "pre" block, remove it from + * the list. Also remove it (i.e. leak it) if + * it is too small, which can happen only at + * the very start of the heap. + */ + if ( pre_size < MIN_MEMBLOCK_SIZE ) + list_del ( &pre->list ); + /* Update total free memory */ + freemem -= size; + /* Return allocated block */ + DBG ( "Allocated [%p,%p)\n", block, + ( ( ( void * ) block ) + size ) ); + return block; + } + } + + DBG ( "Failed to allocate %#zx (aligned %#zx)\n", size, align ); + return NULL; +} + +/** + * Free a memory block + * + * @v ptr Memory allocated by alloc_memblock(), or NULL + * @v size Size of the memory + * + * If @c ptr is NULL, no action is taken. + */ +void free_memblock ( void *ptr, size_t size ) { + struct memory_block *freeing; + struct memory_block *block; + ssize_t gap_before; + ssize_t gap_after = -1; + + /* Allow for ptr==NULL */ + if ( ! ptr ) + return; + + /* Round up size to match actual size that alloc_memblock() + * would have used. + */ + size = ( size + MIN_MEMBLOCK_SIZE - 1 ) & ~( MIN_MEMBLOCK_SIZE - 1 ); + freeing = ptr; + freeing->size = size; + DBG ( "Freeing [%p,%p)\n", freeing, ( ( ( void * ) freeing ) + size )); + + /* Insert/merge into free list */ + list_for_each_entry ( block, &free_blocks, list ) { + /* Calculate gaps before and after the "freeing" block */ + gap_before = ( ( ( void * ) freeing ) - + ( ( ( void * ) block ) + block->size ) ); + gap_after = ( ( ( void * ) block ) - + ( ( ( void * ) freeing ) + freeing->size ) ); + /* Merge with immediately preceding block, if possible */ + if ( gap_before == 0 ) { + DBG ( "[%p,%p) + [%p,%p) -> [%p,%p)\n", block, + ( ( ( void * ) block ) + block->size ), freeing, + ( ( ( void * ) freeing ) + freeing->size ),block, + ( ( ( void * ) freeing ) + freeing->size ) ); + block->size += size; + list_del ( &block->list ); + freeing = block; + } + /* Stop processing as soon as we reach a following block */ + if ( gap_after >= 0 ) + break; + } + + /* Insert before the immediately following block. If + * possible, merge the following block into the "freeing" + * block. + */ + DBG ( "[%p,%p)\n", freeing, ( ( ( void * ) freeing ) + freeing->size)); + list_add_tail ( &freeing->list, &block->list ); + if ( gap_after == 0 ) { + DBG ( "[%p,%p) + [%p,%p) -> [%p,%p)\n", freeing, + ( ( ( void * ) freeing ) + freeing->size ), block, + ( ( ( void * ) block ) + block->size ), freeing, + ( ( ( void * ) block ) + block->size ) ); + freeing->size += block->size; + list_del ( &block->list ); + } + + /* Update free memory counter */ + freemem += size; +} + +/** + * Reallocate memory + * + * @v old_ptr Memory previously allocated by malloc(), or NULL + * @v new_size Requested size + * @ret new_ptr Allocated memory, or NULL + * + * Allocates memory with no particular alignment requirement. @c + * new_ptr will be aligned to at least a multiple of sizeof(void*). + * If @c old_ptr is non-NULL, then the contents of the newly allocated + * memory will be the same as the contents of the previously allocated + * memory, up to the minimum of the old and new sizes. The old memory + * will be freed. + * + * If allocation fails the previously allocated block is left + * untouched and NULL is returned. + * + * Calling realloc() with a new size of zero is a valid way to free a + * memory block. + */ +void * realloc ( void *old_ptr, size_t new_size ) { + struct autosized_block *old_block; + struct autosized_block *new_block; + size_t old_total_size; + size_t new_total_size; + size_t old_size; + void *new_ptr = NOWHERE; + + /* Allocate new memory if necessary. If allocation fails, + * return without touching the old block. + */ + if ( new_size ) { + new_total_size = ( new_size + + offsetof ( struct autosized_block, data ) ); + new_block = alloc_memblock ( new_total_size, 1 ); + if ( ! new_block ) + return NULL; + new_block->size = new_total_size; + new_ptr = &new_block->data; + } + + /* Copy across relevant part of the old data region (if any), + * then free it. Note that at this point either (a) new_ptr + * is valid, or (b) new_size is 0; either way, the memcpy() is + * valid. + */ + if ( old_ptr && ( old_ptr != NOWHERE ) ) { + old_block = container_of ( old_ptr, struct autosized_block, + data ); + old_total_size = old_block->size; + old_size = ( old_total_size - + offsetof ( struct autosized_block, data ) ); + memcpy ( new_ptr, old_ptr, + ( ( old_size < new_size ) ? old_size : new_size ) ); + free_memblock ( old_block, old_total_size ); + } + + return new_ptr; +} + +/** + * Allocate memory + * + * @v size Requested size + * @ret ptr Memory, or NULL + * + * Allocates memory with no particular alignment requirement. @c ptr + * will be aligned to at least a multiple of sizeof(void*). + */ +void * malloc ( size_t size ) { + return realloc ( NULL, size ); +} + +/** + * Free memory + * + * @v ptr Memory allocated by malloc(), or NULL + * + * Memory allocated with malloc_dma() cannot be freed with free(); it + * must be freed with free_dma() instead. + * + * If @c ptr is NULL, no action is taken. + */ +void free ( void *ptr ) { + realloc ( ptr, 0 ); +} + +/** + * Allocate cleared memory + * + * @v size Requested size + * @ret ptr Allocated memory + * + * Allocate memory as per malloc(), and zero it. + * + * This function name is non-standard, but pretty intuitive. + * zalloc(size) is always equivalent to calloc(1,size) + */ +void * zalloc ( size_t size ) { + void *data; + + data = malloc ( size ); + if ( data ) + memset ( data, 0, size ); + return data; +} + +/** + * Add memory to allocation pool + * + * @v start Start address + * @v end End address + * + * Adds a block of memory [start,end) to the allocation pool. This is + * a one-way operation; there is no way to reclaim this memory. + * + * @c start must be aligned to at least a multiple of sizeof(void*). + */ +void mpopulate ( void *start, size_t len ) { + /* Prevent free_memblock() from rounding up len beyond the end + * of what we were actually given... + */ + free_memblock ( start, ( len & ~( MIN_MEMBLOCK_SIZE - 1 ) ) ); +} + +/** + * Initialise the heap + * + */ +static void init_heap ( void ) { + mpopulate ( heap, sizeof ( heap ) ); +} + +/** Memory allocator initialisation function */ +struct init_fn heap_init_fn __init_fn ( INIT_EARLY ) = { + .initialise = init_heap, +}; + +#if 0 +#include +/** + * Dump free block list + * + */ +void mdumpfree ( void ) { + struct memory_block *block; + + printf ( "Free block list:\n" ); + list_for_each_entry ( block, &free_blocks, list ) { + printf ( "[%p,%p] (size %#zx)\n", block, + ( ( ( void * ) block ) + block->size ), block->size ); + } +} +#endif diff --git a/gpxe/src/core/misc.c b/gpxe/src/core/misc.c new file mode 100644 index 0000000..1f51272 --- /dev/null +++ b/gpxe/src/core/misc.c @@ -0,0 +1,91 @@ +/************************************************************************** +MISC Support Routines +**************************************************************************/ + +#include +#include +#include +#include + +/************************************************************************** +INET_ATON - Convert an ascii x.x.x.x to binary form +**************************************************************************/ +int inet_aton ( const char *cp, struct in_addr *inp ) { + const char *p = cp; + const char *digits_start; + unsigned long ip = 0; + unsigned long val; + int j; + for(j = 0; j <= 3; j++) { + digits_start = p; + val = strtoul(p, ( char ** ) &p, 10); + if ((p == digits_start) || (val > 255)) return 0; + if ( ( j < 3 ) && ( *(p++) != '.' ) ) return 0; + ip = (ip << 8) | val; + } + if ( *p == '\0' ) { + inp->s_addr = htonl(ip); + return 1; + } + return 0; +} + +int isspace ( int c ) { + switch ( c ) { + case ' ': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + return 1; + default: + return 0; + } +} + +unsigned long strtoul ( const char *p, char **endp, int base ) { + unsigned long ret = 0; + unsigned int charval; + + while ( isspace ( *p ) ) + p++; + + if ( base == 0 ) { + base = 10; + if ( *p == '0' ) { + p++; + base = 8; + if ( ( *p | 0x20 ) == 'x' ) { + p++; + base = 16; + } + } + } + + while ( 1 ) { + charval = *p; + if ( charval >= 'a' ) { + charval = ( charval - 'a' + 10 ); + } else if ( charval >= 'A' ) { + charval = ( charval - 'A' + 10 ); + } else if ( charval <= '9' ) { + charval = ( charval - '0' ); + } + if ( charval >= ( unsigned int ) base ) + break; + ret = ( ( ret * base ) + charval ); + p++; + } + + if ( endp ) + *endp = ( char * ) p; + + return ( ret ); +} + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/gpxe/src/core/monojob.c b/gpxe/src/core/monojob.c new file mode 100644 index 0000000..657bfd7 --- /dev/null +++ b/gpxe/src/core/monojob.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Single foreground job + * + */ + +static int monojob_rc; + +static void monojob_done ( struct job_interface *job __unused, int rc ) { + monojob_rc = rc; +} + +/** Single foreground job operations */ +static struct job_interface_operations monojob_operations = { + .done = monojob_done, + .kill = ignore_job_kill, + .progress = ignore_job_progress, +}; + +/** Single foreground job */ +struct job_interface monojob = { + .intf = { + .dest = &null_job.intf, + .refcnt = NULL, + }, + .op = &monojob_operations, +}; + +/** + * Wait for single foreground job to complete + * + * @v string Job description to display + * @ret rc Job final status code + */ +int monojob_wait ( const char *string ) { + int key; + int rc; + unsigned long last_progress_dot; + unsigned long elapsed; + + printf ( "%s.", string ); + monojob_rc = -EINPROGRESS; + last_progress_dot = currticks(); + while ( monojob_rc == -EINPROGRESS ) { + step(); + if ( iskey() ) { + key = getchar(); + switch ( key ) { + case CTRL_C: + job_kill ( &monojob ); + rc = -ECANCELED; + goto done; + default: + break; + } + } + elapsed = ( currticks() - last_progress_dot ); + if ( elapsed >= TICKS_PER_SEC ) { + printf ( "." ); + last_progress_dot = currticks(); + } + } + rc = monojob_rc; + +done: + job_done ( &monojob, rc ); + if ( rc ) { + printf ( " %s\n", strerror ( rc ) ); + } else { + printf ( " ok\n" ); + } + return rc; +} diff --git a/gpxe/src/core/null_nap.c b/gpxe/src/core/null_nap.c new file mode 100644 index 0000000..a3b01eb --- /dev/null +++ b/gpxe/src/core/null_nap.c @@ -0,0 +1,3 @@ +#include + +PROVIDE_NAP_INLINE ( null, cpu_nap ); diff --git a/gpxe/src/core/nvo.c b/gpxe/src/core/nvo.c new file mode 100644 index 0000000..e5c07d9 --- /dev/null +++ b/gpxe/src/core/nvo.c @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Non-volatile stored options + * + */ + +/** + * Calculate checksum over non-volatile stored options + * + * @v nvo Non-volatile options block + * @ret sum Checksum + */ +static unsigned int nvo_checksum ( struct nvo_block *nvo ) { + uint8_t *data = nvo->data; + uint8_t sum = 0; + unsigned int i; + + for ( i = 0 ; i < nvo->total_len ; i++ ) { + sum += *(data++); + } + return sum; +} + +/** + * Load non-volatile stored options from non-volatile storage device + * + * @v nvo Non-volatile options block + * @ret rc Return status code + */ +static int nvo_load ( struct nvo_block *nvo ) { + void *data = nvo->data; + struct nvo_fragment *frag; + int rc; + + /* Read data a fragment at a time */ + for ( frag = nvo->fragments ; frag->len ; frag++ ) { + if ( ( rc = nvs_read ( nvo->nvs, frag->address, data, + frag->len ) ) != 0 ) { + DBGC ( nvo, "NVO %p could not read %zd bytes at " + "%#04x\n", nvo, frag->len, frag->address ); + return rc; + } + data += frag->len; + } + + DBGC ( nvo, "NVO %p loaded from non-volatile storage\n", nvo ); + return 0; +} + +/** + * Save non-volatile stored options back to non-volatile storage device + * + * @v nvo Non-volatile options block + * @ret rc Return status code + */ +static int nvo_save ( struct nvo_block *nvo ) { + void *data = nvo->data; + uint8_t *checksum = data; + struct nvo_fragment *frag; + int rc; + + /* Recalculate checksum */ + *checksum -= nvo_checksum ( nvo ); + + /* Write data a fragment at a time */ + for ( frag = nvo->fragments ; frag->len ; frag++ ) { + if ( ( rc = nvs_write ( nvo->nvs, frag->address, data, + frag->len ) ) != 0 ) { + DBGC ( nvo, "NVO %p could not write %zd bytes at " + "%#04x\n", nvo, frag->len, frag->address ); + return rc; + } + data += frag->len; + } + + DBGC ( nvo, "NVO %p saved to non-volatile storage\n", nvo ); + return 0; +} + +/** + * Parse stored options + * + * @v nvo Non-volatile options block + * + * Verifies that the options data is valid, and configures the DHCP + * options block. If the data is not valid, it is replaced with an + * empty options block. + */ +static void nvo_init_dhcpopts ( struct nvo_block *nvo ) { + uint8_t *options_data; + size_t options_len; + + /* Steal one byte for the checksum */ + options_data = ( nvo->data + 1 ); + options_len = ( nvo->total_len - 1 ); + + /* If checksum fails, or options data starts with a zero, + * assume the whole block is invalid. This should capture the + * case of random initial contents. + */ + if ( ( nvo_checksum ( nvo ) != 0 ) || ( options_data[0] == 0 ) ) { + DBGC ( nvo, "NVO %p has checksum %02x and initial byte %02x; " + "assuming empty\n", nvo, nvo_checksum ( nvo ), + options_data[0] ); + memset ( nvo->data, 0, nvo->total_len ); + } + + dhcpopt_init ( &nvo->dhcpopts, options_data, options_len ); +} + +/** + * Store value of NVO setting + * + * @v settings Settings block + * @v setting Setting to store + * @v data Setting data, or NULL to clear setting + * @v len Length of setting data + * @ret rc Return status code + */ +static int nvo_store ( struct settings *settings, struct setting *setting, + const void *data, size_t len ) { + struct nvo_block *nvo = + container_of ( settings, struct nvo_block, settings ); + int rc; + + /* Update stored options */ + if ( ( rc = dhcpopt_store ( &nvo->dhcpopts, setting->tag, + data, len ) ) != 0 ) { + DBGC ( nvo, "NVO %p could not store %zd bytes: %s\n", + nvo, len, strerror ( rc ) ); + return rc; + } + + /* Save updated options to NVS */ + if ( ( rc = nvo_save ( nvo ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Fetch value of NVO setting + * + * @v settings Settings block + * @v setting Setting to fetch + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + * + * The actual length of the setting will be returned even if + * the buffer was too small. + */ +static int nvo_fetch ( struct settings *settings, struct setting *setting, + void *data, size_t len ) { + struct nvo_block *nvo = + container_of ( settings, struct nvo_block, settings ); + + return dhcpopt_fetch ( &nvo->dhcpopts, setting->tag, data, len ); +} + +/** NVO settings operations */ +static struct settings_operations nvo_settings_operations = { + .store = nvo_store, + .fetch = nvo_fetch, +}; + +/** + * Initialise non-volatile stored options + * + * @v nvo Non-volatile options block + * @v nvs Underlying non-volatile storage device + * @v fragments List of option-containing fragments + * @v refcnt Containing object reference counter, or NULL + */ +void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs, + struct nvo_fragment *fragments, struct refcnt *refcnt ) { + nvo->nvs = nvs; + nvo->fragments = fragments; + settings_init ( &nvo->settings, &nvo_settings_operations, refcnt, + "nvo", 0 ); +} + +/** + * Register non-volatile stored options + * + * @v nvo Non-volatile options block + * @v parent Parent settings block, or NULL + * @ret rc Return status code + */ +int register_nvo ( struct nvo_block *nvo, struct settings *parent ) { + struct nvo_fragment *fragment = nvo->fragments; + int rc; + + /* Calculate total length of all fragments */ + for ( fragment = nvo->fragments ; fragment->len ; fragment++ ) + nvo->total_len += fragment->len; + + /* Allocate memory for options and read in from NVS */ + nvo->data = malloc ( nvo->total_len ); + if ( ! nvo->data ) { + DBGC ( nvo, "NVO %p could not allocate %zd bytes\n", + nvo, nvo->total_len ); + rc = -ENOMEM; + goto err_malloc; + } + if ( ( rc = nvo_load ( nvo ) ) != 0 ) + goto err_load; + + /* Verify and register options */ + nvo_init_dhcpopts ( nvo ); + if ( ( rc = register_settings ( &nvo->settings, parent ) ) != 0 ) + goto err_register; + + DBGC ( nvo, "NVO %p registered\n", nvo ); + return 0; + + err_register: + err_load: + free ( nvo->data ); + nvo->data = NULL; + err_malloc: + return rc; +} + +/** + * Unregister non-volatile stored options + * + * @v nvo Non-volatile options block + */ +void unregister_nvo ( struct nvo_block *nvo ) { + unregister_settings ( &nvo->settings ); + free ( nvo->data ); + nvo->data = NULL; + DBGC ( nvo, "NVO %p unregistered\n", nvo ); +} diff --git a/gpxe/src/core/open.c b/gpxe/src/core/open.c new file mode 100644 index 0000000..db8d92e --- /dev/null +++ b/gpxe/src/core/open.c @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Data transfer interface opening + * + */ + +/** Registered URI openers */ +static struct uri_opener uri_openers[0] + __table_start ( struct uri_opener, uri_openers ); +static struct uri_opener uri_openers_end[0] + __table_end ( struct uri_opener, uri_openers ); + +/** Registered socket openers */ +static struct socket_opener socket_openers[0] + __table_start ( struct socket_opener, socket_openers ); +static struct socket_opener socket_openers_end[0] + __table_end ( struct socket_opener, socket_openers ); + +/** + * Open URI + * + * @v xfer Data transfer interface + * @v uri URI + * @ret rc Return status code + * + * The URI will be regarded as being relative to the current working + * URI (see churi()). + */ +int xfer_open_uri ( struct xfer_interface *xfer, struct uri *uri ) { + struct uri_opener *opener; + struct uri *resolved_uri; + int rc = -ENOTSUP; + + /* Resolve URI */ + resolved_uri = resolve_uri ( cwuri, uri ); + if ( ! resolved_uri ) + return -ENOMEM; + + /* Find opener which supports this URI scheme */ + for ( opener = uri_openers ; opener < uri_openers_end ; opener++ ) { + if ( strcmp ( resolved_uri->scheme, opener->scheme ) == 0 ) { + rc = opener->open ( xfer, resolved_uri ); + goto done; + } + } + DBGC ( xfer, "XFER %p attempted to open unsupported URI scheme " + "\"%s\"\n", xfer, resolved_uri->scheme ); + + done: + uri_put ( resolved_uri ); + return rc; +} + +/** + * Open URI string + * + * @v xfer Data transfer interface + * @v uri_string URI string (e.g. "http://etherboot.org/kernel") + * @ret rc Return status code + * + * The URI will be regarded as being relative to the current working + * URI (see churi()). + */ +int xfer_open_uri_string ( struct xfer_interface *xfer, + const char *uri_string ) { + struct uri *uri; + int rc; + + DBGC ( xfer, "XFER %p opening URI %s\n", xfer, uri_string ); + + uri = parse_uri ( uri_string ); + if ( ! uri ) + return -ENOMEM; + + rc = xfer_open_uri ( xfer, uri ); + + uri_put ( uri ); + return rc; +} + +/** + * Open socket + * + * @v xfer Data transfer interface + * @v semantics Communication semantics (e.g. SOCK_STREAM) + * @v peer Peer socket address + * @v local Local socket address, or NULL + * @ret rc Return status code + */ +int xfer_open_socket ( struct xfer_interface *xfer, int semantics, + struct sockaddr *peer, struct sockaddr *local ) { + struct socket_opener *opener; + + DBGC ( xfer, "XFER %p opening (%s,%s) socket\n", xfer, + socket_semantics_name ( semantics ), + socket_family_name ( peer->sa_family ) ); + + for ( opener = socket_openers; opener < socket_openers_end; opener++ ){ + if ( ( opener->semantics == semantics ) && + ( opener->family == peer->sa_family ) ) { + return opener->open ( xfer, peer, local ); + } + } + + DBGC ( xfer, "XFER %p attempted to open unsupported socket type " + "(%s,%s)\n", xfer, socket_semantics_name ( semantics ), + socket_family_name ( peer->sa_family ) ); + return -ENOTSUP; +} + +/** + * Open location + * + * @v xfer Data transfer interface + * @v type Location type + * @v args Remaining arguments depend upon location type + * @ret rc Return status code + */ +int xfer_vopen ( struct xfer_interface *xfer, int type, va_list args ) { + switch ( type ) { + case LOCATION_URI_STRING: { + const char *uri_string = va_arg ( args, const char * ); + + return xfer_open_uri_string ( xfer, uri_string ); } + case LOCATION_URI: { + struct uri *uri = va_arg ( args, struct uri * ); + + return xfer_open_uri ( xfer, uri ); } + case LOCATION_SOCKET: { + int semantics = va_arg ( args, int ); + struct sockaddr *peer = va_arg ( args, struct sockaddr * ); + struct sockaddr *local = va_arg ( args, struct sockaddr * ); + + return xfer_open_socket ( xfer, semantics, peer, local ); } + default: + DBGC ( xfer, "XFER %p attempted to open unsupported location " + "type %d\n", xfer, type ); + return -ENOTSUP; + } +} + +/** + * Open location + * + * @v xfer Data transfer interface + * @v type Location type + * @v ... Remaining arguments depend upon location type + * @ret rc Return status code + */ +int xfer_open ( struct xfer_interface *xfer, int type, ... ) { + va_list args; + int rc; + + va_start ( args, type ); + rc = xfer_vopen ( xfer, type, args ); + va_end ( args ); + return rc; +} diff --git a/gpxe/src/core/pc_kbd.c b/gpxe/src/core/pc_kbd.c new file mode 100644 index 0000000..799c8be --- /dev/null +++ b/gpxe/src/core/pc_kbd.c @@ -0,0 +1,112 @@ +/* Minimal polling PC keyboard driver + * - No interrupt + * - No LED + * - No special keys + * + * still Enough For Me to type a filename. + * + * 2003-07 by SONE Takesh + * 2004-04 moved by LYH From filo to Etherboot + * yhlu@tyan.com + */ + +#include +#include "console.h" + +static char key_map[][128] = { + { + "\0\x1b""1234567890-=\b\t" + "qwertyuiop[]\r\0as" + "dfghjkl;'`\0\\zxcv" + "bnm,./\0*\0 \0\0\0\0\0\0" + "\0\0\0\0\0\0\0""789-456+1" + "230." + },{ + "\0\x1b""!@#$%^&*()_+\b\t" + "QWERTYUIOP{}\r\0AS" + "DFGHJKL:\"~\0|ZXCV" + "BNM<>?\0\0\0 \0\0\0\0\0\0" + "\0\0\0\0\0\0\0""789-456+1" + "230." + } +}; + +static int cur_scan; +static unsigned int shift_state; +#define SHIFT 1 +#define CONTROL 2 +#define CAPS 4 + +static int get_scancode(void) +{ + int scan; + + if ((inb(0x64) & 1) == 0) + return 0; + scan = inb(0x60); + + switch (scan) { + case 0x2a: + case 0x36: + shift_state |= SHIFT; + break; + case 0xaa: + case 0xb6: + shift_state &= ~SHIFT; + break; + case 0x1d: + shift_state |= CONTROL; + break; + case 0x9d: + shift_state &= ~CONTROL; + break; + case 0x3a: + shift_state ^= CAPS; + break; + } + + if (scan & 0x80) + return 0; /* ignore break code or 0xe0 etc! */ + return scan; +} + +static int kbd_havekey(void) +{ + if (!cur_scan) + cur_scan = get_scancode(); + return cur_scan != 0; +} + +static int kbd_ischar(void) +{ + if (!kbd_havekey()) + return 0; + if (!key_map[shift_state & SHIFT][cur_scan]) { + cur_scan = 0; + return 0; + } + return 1; +} + +static int kbd_getc(void) +{ + int c; + + while (!kbd_ischar()) + ; + c = key_map[shift_state & SHIFT][cur_scan]; + if (shift_state & (CONTROL | CAPS)) { + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) { + if (shift_state & CONTROL) + c &= 0x1f; + else if (shift_state & CAPS) + c ^= ('A' ^ 'a'); + } + } + cur_scan = 0; + return c; +} + +struct console_driver pc_kbd_console __console_driver = { + .getchar = kbd_getc, +}; diff --git a/gpxe/src/core/pcmcia.c b/gpxe/src/core/pcmcia.c new file mode 100644 index 0000000..631971e --- /dev/null +++ b/gpxe/src/core/pcmcia.c @@ -0,0 +1,267 @@ +#if 0 + +/* + * pcmcia.c + * + * PCMCIA support routines for etherboot - generic stuff + * + * This code has partly be taken from the linux kernel sources, .../drivers/pcmcia/ + * Started & put together by + * Anselm Martin Hoffmeister + * Stockholm Projekt Computer-Service + * Sankt Augustin / Bonn, Germany + * + * Distributed under GPL2 + */ + +/* + * + * + * ****************************** + * PLEASE DO NOT YET WORK ON THIS + * ****************************** + * + * I'm still fixing it up on every end, so we most probably would interfere + * at some point. If there's anything obvious or better, not-so-obvious, + * please contact me by e-mail: anselm (AT) hoffmeister (DOT) be *THANKS* + */ +#include +#include +#include +#define CODE_STATUS "alpha" +#define CODE_VERSION "0.1.3" +#include +#include +#include + +int sockets; /* AHTODO: Phase this out! */ +u_int pccsocks; +struct pccsock_t pccsock[MAXPCCSOCKS]; +int inited = -1; +struct pcc_config_t pccconfig[MAXPCCCONFIGS]; + +struct driver_interact_t driver[] = { +#ifdef SUPPORT_I82365 + { I82365, i82365_interfacer, "Intel_82365" }, +#endif +}; + +#define NUM_DRIVERS (sizeof(driver)/(sizeof(struct driver_interact_t))) + +void sleepticks(int numticks ) { + u_int tmo; + for (tmo = currticks()+numticks; currticks() < tmo; ) { + } + return; +} + +static void pcmcia_init_all(void) { + u_int i, j, k, l, m, n, ui, configs = 0; + u_int multicard[8]; + u_char *uc, upc; + if ( PDEBUG > 0 ) printf("Initializing PCMCIA subsystem (code-status: " CODE_STATUS ", Version " CODE_VERSION ")\n"); + if ( PDEBUG > 2 ) { + printf ( "Supporting %d driver(s): ", NUM_DRIVERS ); + for ( i = 0; i < NUM_DRIVERS; ++i ) { + printf ( "[%s] ", driver[i].name ); + } + printf ( "\n" ); + } + pccsocks = 0; + sockets = 0; + // Init all drivers in the driver[] array: + for ( i = 0; i < NUM_DRIVERS; ++i ) { + driver[i].f(INIT,0,i,0,0); // init needs no params. It uses pccsocks and pccsock[]. + // Only i tells it which driver_id itself is. + } + for ( i = 0; i < pccsocks; ++i ) { + printf ( "Socket %d: ", i ); + if ( pccsock[i].status != HASCARD ) { + printf ( "is %s: skipping\n", pccsock[i].status == EMPTY? "empty":"[status unknown]" ); + continue; + } + if ( 0 != driver[pccsock[i].drivernum].f(MAPATTRMEM,pccsock[i].internalid,MAP_ATTRMEM_TO, MAP_ATTRMEM_LEN,0 ) ) { + printf ("PCMCIA controller failed to map attribute memory.\n**** SEVERE ERROR CONDITION. Skipping controller.\n" ); + if ( PDEBUG > 2 ) { + printf ( "\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(); + } + +} + +static void 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"); +} + +#endif diff --git a/gpxe/src/core/posix_io.c b/gpxe/src/core/posix_io.c new file mode 100644 index 0000000..e0459bd --- /dev/null +++ b/gpxe/src/core/posix_io.c @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * POSIX-like I/O + * + * These functions provide traditional blocking I/O semantics. They + * are designed to be used by the PXE TFTP API. Because they block, + * they may not be used by most other portions of the gPXE codebase. + */ + +/** An open file */ +struct posix_file { + /** Reference count for this object */ + struct refcnt refcnt; + /** List of open files */ + struct list_head list; + /** File descriptor */ + int fd; + /** Overall status + * + * Set to -EINPROGRESS while data transfer is in progress. + */ + int rc; + /** Data transfer interface */ + struct xfer_interface xfer; + /** Current seek position */ + size_t pos; + /** File size */ + size_t filesize; + /** Received data queue */ + struct list_head data; +}; + +/** List of open files */ +static LIST_HEAD ( posix_files ); + +/** + * Free open file + * + * @v refcnt Reference counter + */ +static void posix_file_free ( struct refcnt *refcnt ) { + struct posix_file *file = + container_of ( refcnt, struct posix_file, refcnt ); + struct io_buffer *iobuf; + struct io_buffer *tmp; + + list_for_each_entry_safe ( iobuf, tmp, &file->data, list ) { + list_del ( &iobuf->list ); + free_iob ( iobuf ); + } + free ( file ); +} + +/** + * Terminate file data transfer + * + * @v file POSIX file + * @v rc Reason for termination + */ +static void posix_file_finished ( struct posix_file *file, int rc ) { + xfer_nullify ( &file->xfer ); + xfer_close ( &file->xfer, rc ); + file->rc = rc; +} + +/** + * Handle close() event + * + * @v xfer POSIX file data transfer interface + * @v rc Reason for close + */ +static void posix_file_xfer_close ( struct xfer_interface *xfer, int rc ) { + struct posix_file *file = + container_of ( xfer, struct posix_file, xfer ); + + posix_file_finished ( file, rc ); +} + +/** + * Handle deliver_iob() event + * + * @v xfer POSIX file data transfer interface + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int +posix_file_xfer_deliver_iob ( struct xfer_interface *xfer, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct posix_file *file = + container_of ( xfer, struct posix_file, xfer ); + + /* Keep track of file position solely for the filesize */ + if ( meta->whence != SEEK_CUR ) + file->pos = 0; + file->pos += meta->offset; + if ( file->filesize < file->pos ) + file->filesize = file->pos; + + if ( iob_len ( iobuf ) ) { + list_add_tail ( &iobuf->list, &file->data ); + } else { + free_iob ( iobuf ); + } + + return 0; +} + +/** POSIX file data transfer interface operations */ +static struct xfer_interface_operations posix_file_xfer_operations = { + .close = posix_file_xfer_close, + .vredirect = xfer_vopen, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = posix_file_xfer_deliver_iob, + .deliver_raw = xfer_deliver_as_iob, +}; + +/** + * Identify file by file descriptor + * + * @v fd File descriptor + * @ret file Corresponding file, or NULL + */ +static struct posix_file * posix_fd_to_file ( int fd ) { + struct posix_file *file; + + list_for_each_entry ( file, &posix_files, list ) { + if ( file->fd == fd ) + return file; + } + return NULL; +} + +/** + * Find an available file descriptor + * + * @ret fd File descriptor, or negative error number + */ +static int posix_find_free_fd ( void ) { + int fd; + + for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) { + if ( ! posix_fd_to_file ( fd ) ) + return fd; + } + DBG ( "POSIX could not find free file descriptor\n" ); + return -ENFILE; +} + +/** + * Open file + * + * @v uri_string URI string + * @ret fd File descriptor, or negative error number + */ +int open ( const char *uri_string ) { + struct posix_file *file; + int fd; + int rc; + + /* Find a free file descriptor to use */ + fd = posix_find_free_fd(); + if ( fd < 0 ) + return fd; + + /* Allocate and initialise structure */ + file = zalloc ( sizeof ( *file ) ); + if ( ! file ) + return -ENOMEM; + file->refcnt.free = posix_file_free; + file->fd = fd; + file->rc = -EINPROGRESS; + xfer_init ( &file->xfer, &posix_file_xfer_operations, + &file->refcnt ); + INIT_LIST_HEAD ( &file->data ); + + /* Open URI on data transfer interface */ + if ( ( rc = xfer_open_uri_string ( &file->xfer, uri_string ) ) != 0 ) + goto err; + + /* Wait for open to succeed or fail */ + while ( list_empty ( &file->data ) ) { + step(); + if ( file->rc == 0 ) + break; + if ( file->rc != -EINPROGRESS ) { + rc = file->rc; + goto err; + } + } + + /* Add to list of open files. List takes reference ownership. */ + list_add ( &file->list, &posix_files ); + DBG ( "POSIX opened %s as file %d\n", uri_string, fd ); + return fd; + + err: + posix_file_finished ( file, rc ); + ref_put ( &file->refcnt ); + return rc; +} + +/** + * Check file descriptors for readiness + * + * @v readfds File descriptors to check + * @v wait Wait until data is ready + * @ret nready Number of ready file descriptors + */ +int select ( fd_set *readfds, int wait ) { + struct posix_file *file; + int fd; + + do { + for ( fd = POSIX_FD_MIN ; fd <= POSIX_FD_MAX ; fd++ ) { + if ( ! FD_ISSET ( fd, readfds ) ) + continue; + file = posix_fd_to_file ( fd ); + if ( ! file ) + return -EBADF; + if ( ( list_empty ( &file->data ) ) && + ( file->rc == -EINPROGRESS ) ) + continue; + /* Data is available or status has changed */ + FD_ZERO ( readfds ); + FD_SET ( fd, readfds ); + return 1; + } + step(); + } while ( wait ); + + return 0; +} + +/** + * Read data from file + * + * @v buffer Data buffer + * @v offset Starting offset within data buffer + * @v len Maximum length to read + * @ret len Actual length read, or negative error number + * + * This call is non-blocking; if no data is available to read then + * -EWOULDBLOCK will be returned. + */ +ssize_t read_user ( int fd, userptr_t buffer, off_t offset, size_t max_len ) { + struct posix_file *file; + struct io_buffer *iobuf; + size_t len; + + /* Identify file */ + file = posix_fd_to_file ( fd ); + if ( ! file ) + return -EBADF; + + /* Try to fetch more data if none available */ + if ( list_empty ( &file->data ) ) + step(); + + /* Dequeue at most one received I/O buffer into user buffer */ + list_for_each_entry ( iobuf, &file->data, list ) { + len = iob_len ( iobuf ); + if ( len > max_len ) + len = max_len; + copy_to_user ( buffer, offset, iobuf->data, len ); + iob_pull ( iobuf, len ); + if ( ! iob_len ( iobuf ) ) { + list_del ( &iobuf->list ); + free_iob ( iobuf ); + } + file->pos += len; + assert ( len != 0 ); + return len; + } + + /* If file has completed, return (after returning all data) */ + if ( file->rc != -EINPROGRESS ) { + assert ( list_empty ( &file->data ) ); + return file->rc; + } + + /* No data ready and file still in progress; return -WOULDBLOCK */ + return -EWOULDBLOCK; +} + +/** + * Determine file size + * + * @v fd File descriptor + * @ret size File size, or negative error number + */ +ssize_t fsize ( int fd ) { + struct posix_file *file; + + /* Identify file */ + file = posix_fd_to_file ( fd ); + if ( ! file ) + return -EBADF; + + return file->filesize; +} + +/** + * Close file + * + * @v fd File descriptor + * @ret rc Return status code + */ +int close ( int fd ) { + struct posix_file *file; + + /* Identify file */ + file = posix_fd_to_file ( fd ); + if ( ! file ) + return -EBADF; + + /* Terminate data transfer */ + posix_file_finished ( file, 0 ); + + /* Remove from list of open files and drop reference */ + list_del ( &file->list ); + ref_put ( &file->refcnt ); + return 0; +} diff --git a/gpxe/src/core/process.c b/gpxe/src/core/process.c new file mode 100644 index 0000000..6a68714 --- /dev/null +++ b/gpxe/src/core/process.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include + +/** @file + * + * Processes + * + * We implement a trivial form of cooperative multitasking, in which + * all processes share a single stack and address space. + */ + +/** Process run queue */ +static LIST_HEAD ( run_queue ); + +/** Registered permanent processes */ +static struct process processes[0] + __table_start ( struct process, processes ); +static struct process processes_end[0] + __table_end ( struct process, processes ); + +/** + * Add process to process list + * + * @v process Process + */ +void process_add ( struct process *process ) { + DBGC ( process, "PROCESS %p starting\n", process ); + ref_get ( process->refcnt ); + list_add_tail ( &process->list, &run_queue ); +} + +/** + * Remove process from process list + * + * @v process Process + * + * It is safe to call process_del() multiple times; further calls will + * have no effect. + */ +void process_del ( struct process *process ) { + if ( ! list_empty ( &process->list ) ) { + DBGC ( process, "PROCESS %p stopping\n", process ); + list_del ( &process->list ); + INIT_LIST_HEAD ( &process->list ); + ref_put ( process->refcnt ); + } else { + DBGC ( process, "PROCESS %p already stopped\n", process ); + } +} + +/** + * Single-step a single process + * + * This executes a single step of the first process in the run queue, + * and moves the process to the end of the run queue. + */ +void step ( void ) { + struct process *process; + + list_for_each_entry ( process, &run_queue, list ) { + list_del ( &process->list ); + list_add_tail ( &process->list, &run_queue ); + DBGC2 ( process, "PROCESS %p executing\n", process ); + process->step ( process ); + DBGC2 ( process, "PROCESS %p finished executing\n", process ); + break; + } +} + +/** + * Initialise processes + * + */ +static void init_processes ( void ) { + struct process *process; + + for ( process = processes ; process < processes_end ; process++ ) { + process_add ( process ); + } +} + +/** Process initialiser */ +struct init_fn process_init_fn __init_fn ( INIT_NORMAL ) = { + .initialise = init_processes, +}; diff --git a/gpxe/src/core/proto_eth_slow.c b/gpxe/src/core/proto_eth_slow.c new file mode 100644 index 0000000..b759a71 --- /dev/null +++ b/gpxe/src/core/proto_eth_slow.c @@ -0,0 +1,406 @@ +/* Copyright 2004 Linux Networx */ +#ifdef PROTO_LACP +#if 0 +#include "nic.h" +#include "timer.h" +#endif + +#define LACP_DEBUG 0 + +/* Structure definitions originally taken from the linux bond_3ad driver */ + +#define SLOW_DST_MAC "\x01\x80\xc2\x00\x00\x02" +static const char slow_dest[] = SLOW_DST_MAC; + + +#define SLOW_SUBTYPE_LACP 1 +#define SLOW_SUBTYPE_MARKER 2 + +struct slow_header { + uint8_t subtype; +}; + +struct lacp_info { + uint16_t system_priority; + uint8_t system[ETH_ALEN]; + uint16_t key; + uint16_t port_priority; + uint16_t port; + uint8_t state; + uint8_t reserved[3]; +} PACKED; + +#define LACP_CMP_LEN (2 + 6 + 2 + 2 + 2) +#define LACP_CP_LEN (2 + 6 + 2 + 2 + 2 + 1) + +/* Link Aggregation Control Protocol(LACP) data unit structure(43.4.2.2 in the 802.3ad standard) */ +struct slow_lacp { + uint8_t subtype; /* = LACP(= 0x01) */ + uint8_t version_number; + uint8_t tlv_type_actor_info; /* = actor information(type/length/value) */ +#define LACP_TLV_TERMINATOR 0 +#define LACP_TLV_ACTOR 1 +#define LACP_TLV_PARTNER 2 +#define LACP_TLV_COLLECTOR 3 + uint8_t actor_information_length; /* = 20 */ + struct lacp_info actor; + uint8_t tlv_type_partner_info; /* = partner information */ + uint8_t partner_information_length; /* = 20 */ + struct lacp_info partner; + uint8_t tlv_type_collector_info; /* = collector information */ + uint8_t collector_information_length; /* = 16 */ + uint16_t collector_max_delay; + uint8_t reserved_12[12]; + uint8_t tlv_type_terminator; /* = terminator */ + uint8_t terminator_length; /* = 0 */ + uint8_t reserved_50[50]; /* = 0 */ +} PACKED; + +/* Marker Protocol Data Unit(PDU) structure(43.5.3.2 in the 802.3ad standard) */ +struct slow_marker { + uint8_t subtype; /* = 0x02 (marker PDU) */ + uint8_t version_number; /* = 0x01 */ + uint8_t tlv_type; +#define MARKER_TLV_TERMINATOR 0 /* marker terminator */ +#define MARKER_TLV_INFO 1 /* marker information */ +#define MARKER_TLV_RESPONSE 2 /* marker response information */ + uint8_t marker_length; /* = 0x16 */ + uint16_t requester_port; /* The number assigned to the port by the requester */ + uint8_t requester_system[ETH_ALEN]; /* The requester's system id */ + uint32_t requester_transaction_id; /* The transaction id allocated by the requester, */ + uint16_t pad; /* = 0 */ + uint8_t tlv_type_terminator; /* = 0x00 */ + uint8_t terminator_length; /* = 0x00 */ + uint8_t reserved_90[90]; /* = 0 */ +} PACKED; + +union slow_union { + struct slow_header header; + struct slow_lacp lacp; + struct slow_marker marker; +}; + +#define FAST_PERIODIC_TIME (1*TICKS_PER_SEC) +#define SLOW_PERIODIC_TIME (30*TICKS_PER_SEC) +#define SHORT_TIMEOUT_TIME (3*FAST_PERIODIC_TIME) +#define LONG_TIMEOUT_TIME (3*SLOW_PERIODIC_TIME) +#define CHURN_DETECTION_TIME (60*TICKS_PER_SEC) +#define AGGREGATE_WAIT_TIME (2*TICKS_PER_SEC) + +#define LACP_ACTIVITY (1 << 0) +#define LACP_TIMEOUT (1 << 1) +#define LACP_AGGREGATION (1 << 2) +#define LACP_SYNCHRONIZATION (1 << 3) +#define LACP_COLLECTING (1 << 4) +#define LACP_DISTRIBUTING (1 << 5) +#define LACP_DEFAULTED (1 << 6) +#define LACP_EXPIRED (1 << 7) + +#define UNSELECTED 0 +#define STANDBY 1 +#define SELECTED 2 + + +struct lacp_state { + struct slow_lacp pkt; + unsigned long current_while_timer; /* Time when the LACP information expires */ + unsigned long periodic_timer; /* Time when I need to send my partner an update */ +}; + +static struct lacp_state lacp; + + +#if LACP_DEBUG > 0 +static void print_lacp_state(uint8_t state) +{ + printf("%hhx", state); + if (state & LACP_ACTIVITY) { + printf(" Activity"); + } + if (state & LACP_TIMEOUT) { + printf(" Timeout"); + } + if (state & LACP_AGGREGATION) { + printf(" Aggregation"); + } + if (state & LACP_SYNCHRONIZATION) { + printf(" Syncronization"); + } + if (state & LACP_COLLECTING) { + printf(" Collecting"); + } + if (state & LACP_DISTRIBUTING) { + printf(" Distributing"); + } + if (state & LACP_DEFAULTED) { + printf(" Defaulted"); + } + if (state & LACP_EXPIRED) { + printf(" Expired"); + } + printf("\n"); +} + +static inline void print_lacpdu(struct slow_lacp *pkt) +{ + printf("subtype version: %hhx %hhx\n", + pkt->subtype, pkt->version_number); + printf("actor_tlv %hhx", pkt->tlv_type_actor_info); + printf(" len: %hhx (\n", pkt->actor_information_length); + printf(" sys_pri: %hx", ntohs(pkt->actor.system_priority)); + printf(" mac: %!", pkt->actor.system); + printf(" key: %hx", ntohs(pkt->actor.key)); + printf(" port_pri: %hx", ntohs(pkt->actor.port_priority)); + printf(" port: %hx\n", ntohs(pkt->actor.port)); + printf(" state: "); + print_lacp_state(pkt->actor.state); +#if LACP_DEBUG > 1 + printf(" reserved: %hhx %hhx %hhx\n", + pkt->actor.reserved[0], pkt->actor.reserved[1], pkt->actor.reserved[2]); +#endif + printf(")\n"); + printf("partner_tlv: %hhx", pkt->tlv_type_partner_info); + printf(" len: %hhx (\n", pkt->partner_information_length); + printf(" sys_pri: %hx", ntohs(pkt->partner.system_priority)); + printf(" mac: %!", pkt->partner.system); + printf(" key: %hx", ntohs(pkt->partner.key)); + printf(" port_pri: %hx", ntohs(pkt->partner.port_priority)); + printf(" port: %hx\n", ntohs(pkt->partner.port)); + printf(" state: "); + print_lacp_state(pkt->partner.state); +#if LACP_DEBUG > 1 + printf(" reserved: %hhx %hhx %hhx\n", + pkt->partner.reserved[0], pkt->partner.reserved[1], pkt->partner.reserved[2]); +#endif + printf(")\n"); + printf("collector_tlv: %hhx ", pkt->tlv_type_collector_info); + printf(" len: %hhx (", pkt->collector_information_length); + printf(" max_delay: %hx", ntohs(pkt->collector_max_delay)); +#if LACP_DEBUG > 1 + printf("reserved_12: %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx\n", + pkt->reserved_12[0], pkt->reserved_12[1], pkt->reserved_12[2], + pkt->reserved_12[3], pkt->reserved_12[4], pkt->reserved_12[5], + pkt->reserved_12[6], pkt->reserved_12[7], pkt->reserved_12[8], + pkt->reserved_12[9], pkt->reserved_12[10], pkt->reserved_12[11]); +#endif + printf(" )\n"); + printf("terminator_tlv: %hhx", pkt->tlv_type_terminator); + printf(" len: %hhx ()\n", pkt->terminator_length); +} + +static inline unsigned long lacp_timer_val(unsigned long now, unsigned long when) +{ + return when?(when - now)/TICKS_PER_SEC : 0; +} +static void print_lacp(const char *which, struct slow_lacp *pkt, unsigned long now) +{ + printf("%s\n", which); + print_lacpdu(pkt); + printf("timers: c %ds p %ds\n", + lacp_timer_val(now, lacp.current_while_timer), + lacp_timer_val(now, lacp.periodic_timer) + ); + printf("\n"); +} +#else /* LACP_DEBUG */ +#define print_lacp(which, pkt, now) do {} while(0) +#endif /* LACP_DEBUG */ + +static void lacp_init_state(const uint8_t *mac) +{ + memset(&lacp, 0, sizeof(lacp)); + + /* Initialize the packet constants */ + lacp.pkt.subtype = 1; + lacp.pkt.version_number = 1; + + + /* The default state of my interface */ + lacp.pkt.tlv_type_actor_info = LACP_TLV_ACTOR; + lacp.pkt.actor_information_length = 0x14; + lacp.pkt.actor.system_priority = htons(1); + memcpy(lacp.pkt.actor.system, mac, ETH_ALEN); + lacp.pkt.actor.key = htons(1); + lacp.pkt.actor.port = htons(1); + lacp.pkt.actor.port_priority = htons(1); + lacp.pkt.actor.state = + LACP_SYNCHRONIZATION | + LACP_COLLECTING | + LACP_DISTRIBUTING | + LACP_DEFAULTED; + + /* Set my partner defaults */ + lacp.pkt.tlv_type_partner_info = LACP_TLV_PARTNER; + lacp.pkt.partner_information_length = 0x14; + lacp.pkt.partner.system_priority = htons(1); + /* memset(lacp.pkt.parnter_system, 0, ETH_ALEN); */ + lacp.pkt.partner.key = htons(1); + lacp.pkt.partner.port = htons(1); + lacp.pkt.partner.port_priority = htons(1); + lacp.pkt.partner.state = + LACP_ACTIVITY | + LACP_SYNCHRONIZATION | + LACP_COLLECTING | + LACP_DISTRIBUTING | + LACP_DEFAULTED; + + lacp.pkt.tlv_type_collector_info = LACP_TLV_COLLECTOR; + lacp.pkt.collector_information_length = 0x10; + lacp.pkt.collector_max_delay = htons(0x8000); /* ???? */ + + lacp.pkt.tlv_type_terminator = LACP_TLV_TERMINATOR; + lacp.pkt.terminator_length = 0; +} + +#define LACP_NTT_MASK (LACP_ACTIVITY | LACP_TIMEOUT | \ + LACP_SYNCHRONIZATION | LACP_AGGREGATION) + +static inline int lacp_update_ntt(struct slow_lacp *pkt) +{ + int ntt = 0; + if ((memcmp(&pkt->partner, &lacp.pkt.actor, LACP_CMP_LEN) != 0) || + ((pkt->partner.state & LACP_NTT_MASK) != + (lacp.pkt.actor.state & LACP_NTT_MASK))) + { + ntt = 1; + } + return ntt; +} + +static inline void lacp_record_pdu(struct slow_lacp *pkt) +{ + memcpy(&lacp.pkt.partner, &pkt->actor, LACP_CP_LEN); + + lacp.pkt.actor.state &= ~LACP_DEFAULTED; + lacp.pkt.partner.state &= ~LACP_SYNCHRONIZATION; + if ((memcmp(&pkt->partner, &lacp.pkt.actor, LACP_CMP_LEN) == 0) && + ((pkt->partner.state & LACP_AGGREGATION) == + (lacp.pkt.actor.state & LACP_AGGREGATION))) + { + lacp.pkt.partner.state |= LACP_SYNCHRONIZATION; + } + if (!(pkt->actor.state & LACP_AGGREGATION)) { + lacp.pkt.partner.state |= LACP_SYNCHRONIZATION; + } + + /* ACTIVITY? */ +} + +static inline int lacp_timer_expired(unsigned long now, unsigned long when) +{ + return when && (now > when); +} + +static inline void lacp_start_periodic_timer(unsigned long now) +{ + if ((lacp.pkt.partner.state & LACP_ACTIVITY) || + (lacp.pkt.actor.state & LACP_ACTIVITY)) { + lacp.periodic_timer = now + + (((lacp.pkt.partner.state & LACP_TIMEOUT)? + FAST_PERIODIC_TIME : SLOW_PERIODIC_TIME)); + } +} + +static inline void lacp_start_current_while_timer(unsigned long now) +{ + lacp.current_while_timer = now + + ((lacp.pkt.actor.state & LACP_TIMEOUT) ? + SHORT_TIMEOUT_TIME : LONG_TIMEOUT_TIME); + + lacp.pkt.actor.state &= ~LACP_EXPIRED; +} + +static void send_lacp_reports(unsigned long now, int ntt) +{ + if (memcmp(nic.node_addr, lacp.pkt.actor.system, ETH_ALEN) != 0) { + lacp_init_state(nic.node_addr); + } + /* If the remote information has expired I need to take action */ + if (lacp_timer_expired(now, lacp.current_while_timer)) { + if (!(lacp.pkt.actor.state & LACP_EXPIRED)) { + lacp.pkt.partner.state &= ~LACP_SYNCHRONIZATION; + lacp.pkt.partner.state |= LACP_TIMEOUT; + lacp.pkt.actor.state |= LACP_EXPIRED; + lacp.current_while_timer = now + SHORT_TIMEOUT_TIME; + ntt = 1; + } + else { + lacp_init_state(nic.node_addr); + } + } + /* If the periodic timer has expired I need to transmit */ + if (lacp_timer_expired(now, lacp.periodic_timer)) { + ntt = 1; + /* Reset by lacp_start_periodic_timer */ + } + if (ntt) { + eth_transmit(slow_dest, ETH_P_SLOW, sizeof(lacp.pkt), &lacp.pkt); + + /* Restart the periodic timer */ + lacp_start_periodic_timer(now); + + print_lacp("Trasmitted", &lacp.pkt, now); + } +} + +static inline void send_eth_slow_reports(unsigned long now) +{ + send_lacp_reports(now, 0); +} + +static inline void process_eth_slow(unsigned short ptype, unsigned long now) +{ + union slow_union *pkt; + if ((ptype != ETH_P_SLOW) || + (nic.packetlen < (ETH_HLEN + sizeof(pkt->header)))) { + return; + } + pkt = (union slow_union *)&nic.packet[ETH_HLEN]; + if ((pkt->header.subtype == SLOW_SUBTYPE_LACP) && + (nic.packetlen >= ETH_HLEN + sizeof(pkt->lacp))) { + int ntt; + if (memcmp(nic.node_addr, lacp.pkt.actor.system, ETH_ALEN) != 0) { + lacp_init_state(nic.node_addr); + } + /* As long as nic.packet is 2 byte aligned all is good */ + print_lacp("Received", &pkt->lacp, now); + /* I don't actually implement the MUX or SELECT + * machines. + * + * What logically happens when the client and I + * disagree about an aggregator is the current + * aggregtator is unselected. The MUX machine places + * me in DETACHED. The SELECT machine runs and + * reslects the same aggregator. If I go through + * these steps fast enough an outside observer can not + * notice this. + * + * Since the process will not generate any noticeable + * effect it does not need an implmenetation. This + * keeps the code simple and the code and binary + * size down. + */ + /* lacp_update_selected(&pkt->lacp); */ + ntt = lacp_update_ntt(&pkt->lacp); + lacp_record_pdu(&pkt->lacp); + lacp_start_current_while_timer(now); + send_lacp_reports(now, ntt); + } + /* If we receive a marker information packet return it */ + else if ((pkt->header.subtype == SLOW_SUBTYPE_MARKER) && + (nic.packetlen >= ETH_HLEN + sizeof(pkt->marker)) && + (pkt->marker.tlv_type == MARKER_TLV_INFO) && + (pkt->marker.marker_length == 0x16)) + { + pkt->marker.tlv_type = MARKER_TLV_RESPONSE; + eth_transmit(slow_dest, ETH_P_SLOW, + sizeof(pkt->marker), &pkt->marker); + } + + } +#else + +#define send_eth_slow_reports(now) do {} while(0) +#define process_eth_slow(ptype, now) do {} while(0) + +#endif diff --git a/gpxe/src/core/random.c b/gpxe/src/core/random.c new file mode 100644 index 0000000..d34e763 --- /dev/null +++ b/gpxe/src/core/random.c @@ -0,0 +1,39 @@ +/** @file + * + * Random number generation + * + */ + +#include +#include + +static int32_t rnd_seed = 0; + +/** + * Seed the pseudo-random number generator + * + * @v seed Seed value + */ +void srandom ( unsigned int seed ) { + rnd_seed = seed; +} + +/** + * Generate a pseudo-random number between 0 and 2147483647L or 2147483562? + * + * @ret rand Pseudo-random number + */ +long int random ( void ) { + int32_t q; + + if ( ! rnd_seed ) /* Initialize linear congruential generator */ + srandom ( currticks() ); + + /* simplified version of the LCG given in Bruce Schneier's + "Applied Cryptography" */ + q = ( rnd_seed / 53668 ); + rnd_seed = ( 40014 * ( rnd_seed - 53668 * q ) - 12211 * q ); + if ( rnd_seed < 0 ) + rnd_seed += 2147483563L; + return rnd_seed; +} diff --git a/gpxe/src/core/refcnt.c b/gpxe/src/core/refcnt.c new file mode 100644 index 0000000..30bb6de --- /dev/null +++ b/gpxe/src/core/refcnt.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include + +/** @file + * + * Reference counting + * + */ + +/** + * Increment reference count + * + * @v refcnt Reference counter, or NULL + * @ret refcnt Reference counter + * + * If @c refcnt is NULL, no action is taken. + */ +struct refcnt * ref_get ( struct refcnt *refcnt ) { + + if ( refcnt ) { + refcnt->refcnt++; + DBGC2 ( refcnt, "REFCNT %p incremented to %d\n", + refcnt, refcnt->refcnt ); + } + return refcnt; +} + +/** + * Decrement reference count + * + * @v refcnt Reference counter, or NULL + * + * If the reference count decreases below zero, the object's free() + * method will be called. + * + * If @c refcnt is NULL, no action is taken. + */ +void ref_put ( struct refcnt *refcnt ) { + + if ( ! refcnt ) + return; + + refcnt->refcnt--; + DBGC2 ( refcnt, "REFCNT %p decremented to %d\n", + refcnt, refcnt->refcnt ); + + if ( refcnt->refcnt >= 0 ) + return; + + if ( refcnt->free ) { + DBGC ( refcnt, "REFCNT %p being freed via method %p\n", + refcnt, refcnt->free ); + refcnt->free ( refcnt ); + } else { + DBGC ( refcnt, "REFCNT %p being freed\n", refcnt ); + free ( refcnt ); + } +} diff --git a/gpxe/src/core/resolv.c b/gpxe/src/core/resolv.c new file mode 100644 index 0000000..f4a587f --- /dev/null +++ b/gpxe/src/core/resolv.c @@ -0,0 +1,400 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Name resolution + * + */ + +/*************************************************************************** + * + * Name resolution interfaces + * + *************************************************************************** + */ + +/** + * Name resolution completed + * + * @v resolv Name resolution interface + * @v sa Completed socket address (if successful) + * @v rc Final status code + */ +void resolv_done ( struct resolv_interface *resolv, struct sockaddr *sa, + int rc ) { + struct resolv_interface *dest = resolv_get_dest ( resolv ); + + resolv_unplug ( resolv ); + dest->op->done ( dest, sa, rc ); + resolv_put ( dest ); +} + +/** + * Ignore name resolution done() event + * + * @v resolv Name resolution interface + * @v sa Completed socket address (if successful) + * @v rc Final status code + */ +void ignore_resolv_done ( struct resolv_interface *resolv __unused, + struct sockaddr *sa __unused, int rc __unused ) { + /* Do nothing */ +} + +/** Null name resolution interface operations */ +struct resolv_interface_operations null_resolv_ops = { + .done = ignore_resolv_done, +}; + +/** Null name resolution interface */ +struct resolv_interface null_resolv = { + .intf = { + .dest = &null_resolv.intf, + .refcnt = NULL, + }, + .op = &null_resolv_ops, +}; + +/*************************************************************************** + * + * Numeric name resolver + * + *************************************************************************** + */ + +/** A numeric name resolver */ +struct numeric_resolv { + /** Reference counter */ + struct refcnt refcnt; + /** Name resolution interface */ + struct resolv_interface resolv; + /** Process */ + struct process process; + /** Completed socket address */ + struct sockaddr sa; + /** Overall status code */ + int rc; +}; + +static void numeric_step ( struct process *process ) { + struct numeric_resolv *numeric = + container_of ( process, struct numeric_resolv, process ); + + resolv_done ( &numeric->resolv, &numeric->sa, numeric->rc ); + process_del ( process ); +} + +static int numeric_resolv ( struct resolv_interface *resolv, + const char *name, struct sockaddr *sa ) { + struct numeric_resolv *numeric; + struct sockaddr_in *sin; + + /* Allocate and initialise structure */ + numeric = zalloc ( sizeof ( *numeric ) ); + if ( ! numeric ) + return -ENOMEM; + resolv_init ( &numeric->resolv, &null_resolv_ops, &numeric->refcnt ); + process_init ( &numeric->process, numeric_step, &numeric->refcnt ); + memcpy ( &numeric->sa, sa, sizeof ( numeric->sa ) ); + + DBGC ( numeric, "NUMERIC %p attempting to resolve \"%s\"\n", + numeric, name ); + + /* Attempt to resolve name */ + sin = ( ( struct sockaddr_in * ) &numeric->sa ); + sin->sin_family = AF_INET; + if ( inet_aton ( name, &sin->sin_addr ) == 0 ) + numeric->rc = -EINVAL; + + /* Attach to parent interface, mortalise self, and return */ + resolv_plug_plug ( &numeric->resolv, resolv ); + ref_put ( &numeric->refcnt ); + return 0; +} + +struct resolver numeric_resolver __resolver ( RESOLV_NUMERIC ) = { + .name = "NUMERIC", + .resolv = numeric_resolv, +}; + +/*************************************************************************** + * + * Name resolution multiplexer + * + *************************************************************************** + */ + +/** Registered name resolvers */ +static struct resolver resolvers[0] + __table_start ( struct resolver, resolvers ); +static struct resolver resolvers_end[0] + __table_end ( struct resolver, resolvers ); + +/** A name resolution multiplexer */ +struct resolv_mux { + /** Reference counter */ + struct refcnt refcnt; + /** Parent name resolution interface */ + struct resolv_interface parent; + + /** Child name resolution interface */ + struct resolv_interface child; + /** Current child resolver */ + struct resolver *resolver; + + /** Socket address to complete */ + struct sockaddr sa; + /** Name to be resolved + * + * Must be at end of structure + */ + char name[0]; +}; + +/** + * Try current child name resolver + * + * @v mux Name resolution multiplexer + * @ret rc Return status code + */ +static int resolv_mux_try ( struct resolv_mux *mux ) { + struct resolver *resolver = mux->resolver; + int rc; + + DBGC ( mux, "RESOLV %p trying method %s\n", mux, resolver->name ); + + if ( ( rc = resolver->resolv ( &mux->child, mux->name, + &mux->sa ) ) != 0 ) { + DBGC ( mux, "RESOLV %p could not use method %s: %s\n", + mux, resolver->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Handle done() event from child name resolver + * + * @v resolv Child name resolution interface + * @v sa Completed socket address (if successful) + * @v rc Final status code + */ +static void resolv_mux_done ( struct resolv_interface *resolv, + struct sockaddr *sa, int rc ) { + struct resolv_mux *mux = + container_of ( resolv, struct resolv_mux, child ); + + /* Unplug child */ + resolv_unplug ( &mux->child ); + + /* If this resolution succeeded, stop now */ + if ( rc == 0 ) { + DBGC ( mux, "RESOLV %p succeeded using method %s\n", + mux, mux->resolver->name ); + goto finished; + } + + /* Attempt next child resolver, if possible */ + mux->resolver++; + if ( mux->resolver >= resolvers_end ) { + DBGC ( mux, "RESOLV %p failed to resolve name\n", mux ); + goto finished; + } + if ( ( rc = resolv_mux_try ( mux ) ) != 0 ) + goto finished; + + /* Next resolver is now running */ + return; + + finished: + resolv_done ( &mux->parent, sa, rc ); +} + +/** Name resolution multiplexer operations */ +static struct resolv_interface_operations resolv_mux_child_ops = { + .done = resolv_mux_done, +}; + +/** + * Start name resolution + * + * @v resolv Name resolution interface + * @v name Name to resolve + * @v sa Socket address to complete + * @ret rc Return status code + */ +int resolv ( struct resolv_interface *resolv, const char *name, + struct sockaddr *sa ) { + struct resolv_mux *mux; + size_t name_len = ( strlen ( name ) + 1 ); + int rc; + + /* Allocate and initialise structure */ + mux = zalloc ( sizeof ( *mux ) + name_len ); + if ( ! mux ) + return -ENOMEM; + resolv_init ( &mux->parent, &null_resolv_ops, &mux->refcnt ); + resolv_init ( &mux->child, &resolv_mux_child_ops, &mux->refcnt ); + mux->resolver = resolvers; + memcpy ( &mux->sa, sa, sizeof ( mux->sa ) ); + memcpy ( mux->name, name, name_len ); + + DBGC ( mux, "RESOLV %p attempting to resolve \"%s\"\n", mux, name ); + + /* Start first resolver in chain. There will always be at + * least one resolver (the numeric resolver), so no need to + * check for the zero-resolvers-available case. + */ + if ( ( rc = resolv_mux_try ( mux ) ) != 0 ) + goto err; + + /* Attach parent interface, mortalise self, and return */ + resolv_plug_plug ( &mux->parent, resolv ); + ref_put ( &mux->refcnt ); + return 0; + + err: + ref_put ( &mux->refcnt ); + return rc; +} + +/*************************************************************************** + * + * Named socket opening + * + *************************************************************************** + */ + +/** A named socket */ +struct named_socket { + /** Reference counter */ + struct refcnt refcnt; + /** Data transfer interface */ + struct xfer_interface xfer; + /** Name resolution interface */ + struct resolv_interface resolv; + /** Communication semantics (e.g. SOCK_STREAM) */ + int semantics; + /** Stored local socket address, if applicable */ + struct sockaddr local; + /** Stored local socket address exists */ + int have_local; +}; + +/** Named socket opener data transfer interface operations */ +static struct xfer_interface_operations named_xfer_ops = { + .close = ignore_xfer_close, + .vredirect = ignore_xfer_vredirect, + .window = no_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = xfer_deliver_as_raw, + .deliver_raw = ignore_xfer_deliver_raw, +}; + +/** + * Handle done() event + * + * @v resolv Name resolution interface + * @v sa Completed socket address (if successful) + * @v rc Final status code + */ +static void named_resolv_done ( struct resolv_interface *resolv, + struct sockaddr *sa, int rc ) { + struct named_socket *named = + container_of ( resolv, struct named_socket, resolv ); + + /* Unplug resolver and nullify data transfer interface */ + resolv_unplug ( &named->resolv ); + xfer_nullify ( &named->xfer ); + + /* Redirect if name resolution was successful */ + if ( rc == 0 ) { + rc = xfer_redirect ( &named->xfer, LOCATION_SOCKET, + named->semantics, sa, + ( named->have_local ? + &named->local : NULL ) ); + } + + /* Close data transfer interface if redirection failed */ + if ( rc != 0 ) + xfer_close ( &named->xfer, rc ); + + /* Unplug data transfer interface */ + xfer_unplug ( &named->xfer ); +} + +/** Named socket opener name resolution interface operations */ +static struct resolv_interface_operations named_resolv_ops = { + .done = named_resolv_done, +}; + +/** + * Open named socket + * + * @v semantics Communication semantics (e.g. SOCK_STREAM) + * @v peer Peer socket address to complete + * @v name Name to resolve + * @v local Local socket address, or NULL + * @ret rc Return status code + */ +int xfer_open_named_socket ( struct xfer_interface *xfer, int semantics, + struct sockaddr *peer, const char *name, + struct sockaddr *local ) { + struct named_socket *named; + int rc; + + /* Allocate and initialise structure */ + named = zalloc ( sizeof ( *named ) ); + if ( ! named ) + return -ENOMEM; + xfer_init ( &named->xfer, &named_xfer_ops, &named->refcnt ); + resolv_init ( &named->resolv, &named_resolv_ops, &named->refcnt ); + named->semantics = semantics; + if ( local ) { + memcpy ( &named->local, local, sizeof ( named->local ) ); + named->have_local = 1; + } + + DBGC ( named, "RESOLV %p opening named socket \"%s\"\n", + named, name ); + + /* Start name resolution */ + if ( ( rc = resolv ( &named->resolv, name, peer ) ) != 0 ) + goto err; + + /* Attach parent interface, mortalise self, and return */ + xfer_plug_plug ( &named->xfer, xfer ); + ref_put ( &named->refcnt ); + return 0; + + err: + ref_put ( &named->refcnt ); + return rc; +} diff --git a/gpxe/src/core/serial.c b/gpxe/src/core/serial.c new file mode 100644 index 0000000..5b3be39 --- /dev/null +++ b/gpxe/src/core/serial.c @@ -0,0 +1,252 @@ +/* + * The serial port interface routines implement a simple polled i/o + * interface to a standard serial port. Due to the space restrictions + * for the boot blocks, no BIOS support is used (since BIOS requires + * expensive real/protected mode switches), instead the rudimentary + * BIOS support is duplicated here. + * + * The base address and speed for the i/o port are passed from the + * Makefile in the COMCONSOLE and CONSPEED preprocessor macros. The + * line control parameters are currently hard-coded to 8 bits, no + * parity, 1 stop bit (8N1). This can be changed in init_serial(). + */ + +#include "stddef.h" +#include +#include +#include +#include +#include "config/serial.h" + +/* Set default values if none specified */ + +#ifndef COMCONSOLE +#define COMCONSOLE 0x3f8 +#endif + +#ifndef COMSPEED +#define COMSPEED 9600 +#endif + +#ifndef COMDATA +#define COMDATA 8 +#endif + +#ifndef COMPARITY +#define COMPARITY 0 +#endif + +#ifndef COMSTOP +#define COMSTOP 1 +#endif + +#undef UART_BASE +#define UART_BASE ( COMCONSOLE ) + +#undef UART_BAUD +#define UART_BAUD ( COMSPEED ) + +#if ((115200%UART_BAUD) != 0) +#error Bad ttys0 baud rate +#endif + +#define COMBRD (115200/UART_BAUD) + +/* Line Control Settings */ +#define UART_LCS ( ( ( (COMDATA) - 5 ) << 0 ) | \ + ( ( (COMPARITY) ) << 3 ) | \ + ( ( (COMSTOP) - 1 ) << 2 ) ) + +/* Data */ +#define UART_RBR 0x00 +#define UART_TBR 0x00 + +/* Control */ +#define UART_IER 0x01 +#define UART_IIR 0x02 +#define UART_FCR 0x02 +#define UART_LCR 0x03 +#define UART_MCR 0x04 +#define UART_DLL 0x00 +#define UART_DLM 0x01 + +/* Status */ +#define UART_LSR 0x05 +#define UART_LSR_TEMPT 0x40 /* Transmitter empty */ +#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ +#define UART_LSR_BI 0x10 /* Break interrupt indicator */ +#define UART_LSR_FE 0x08 /* Frame error indicator */ +#define UART_LSR_PE 0x04 /* Parity error indicator */ +#define UART_LSR_OE 0x02 /* Overrun error indicator */ +#define UART_LSR_DR 0x01 /* Receiver data ready */ + +#define UART_MSR 0x06 +#define UART_SCR 0x07 + +#if defined(UART_MEM) +#define uart_readb(addr) readb((addr)) +#define uart_writeb(val,addr) writeb((val),(addr)) +#else +#define uart_readb(addr) inb((addr)) +#define uart_writeb(val,addr) outb((val),(addr)) +#endif + +/* + * void serial_putc(int ch); + * Write character `ch' to port UART_BASE. + */ +void serial_putc ( int ch ) { + int i; + int status; + i = 1000; /* timeout */ + while(--i > 0) { + status = uart_readb(UART_BASE + UART_LSR); + if (status & UART_LSR_THRE) { + /* TX buffer emtpy */ + uart_writeb(ch, UART_BASE + UART_TBR); + break; + } + mdelay(2); + } +} + +/* + * int serial_getc(void); + * Read a character from port UART_BASE. + */ +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; + status = uart_readb(UART_BASE + UART_LSR); /* line status reg; */ + return status & 1; /* rx char available */ +} + +/* + * int serial_init(void); + * Initialize port UART_BASE to speed COMSPEED, line settings 8N1. + */ +static void serial_init ( void ) { + int status; + int divisor, lcs; + + DBG ( "Serial port %#x initialising\n", UART_BASE ); + + divisor = COMBRD; + lcs = UART_LCS; + + +#ifdef COMPRESERVE + lcs = uart_readb(UART_BASE + UART_LCR) & 0x7f; + uart_writeb(0x80 | lcs, UART_BASE + UART_LCR); + divisor = (uart_readb(UART_BASE + UART_DLM) << 8) | uart_readb(UART_BASE + UART_DLL); + uart_writeb(lcs, UART_BASE + UART_LCR); +#endif + + /* Set Baud Rate Divisor to COMSPEED, and test to see if the + * serial port appears to be present. + */ + uart_writeb(0x80 | lcs, UART_BASE + UART_LCR); + uart_writeb(0xaa, UART_BASE + UART_DLL); + if (uart_readb(UART_BASE + UART_DLL) != 0xaa) { + DBG ( "Serial port %#x UART_DLL failed\n", UART_BASE ); + goto out; + } + uart_writeb(0x55, UART_BASE + UART_DLL); + if (uart_readb(UART_BASE + UART_DLL) != 0x55) { + DBG ( "Serial port %#x UART_DLL failed\n", UART_BASE ); + goto out; + } + uart_writeb(divisor & 0xff, UART_BASE + UART_DLL); + if (uart_readb(UART_BASE + UART_DLL) != (divisor & 0xff)) { + DBG ( "Serial port %#x UART_DLL failed\n", UART_BASE ); + goto out; + } + uart_writeb(0xaa, UART_BASE + UART_DLM); + if (uart_readb(UART_BASE + UART_DLM) != 0xaa) { + DBG ( "Serial port %#x UART_DLM failed\n", UART_BASE ); + goto out; + } + uart_writeb(0x55, UART_BASE + UART_DLM); + if (uart_readb(UART_BASE + UART_DLM) != 0x55) { + DBG ( "Serial port %#x UART_DLM failed\n", UART_BASE ); + goto out; + } + uart_writeb((divisor >> 8) & 0xff, UART_BASE + UART_DLM); + if (uart_readb(UART_BASE + UART_DLM) != ((divisor >> 8) & 0xff)) { + DBG ( "Serial port %#x UART_DLM failed\n", UART_BASE ); + goto out; + } + uart_writeb(lcs, UART_BASE + UART_LCR); + + /* disable interrupts */ + uart_writeb(0x0, UART_BASE + UART_IER); + + /* disable fifo's */ + uart_writeb(0x00, UART_BASE + UART_FCR); + + /* Set clear to send, so flow control works... */ + uart_writeb((1<<1), UART_BASE + UART_MCR); + + + /* Flush the input buffer. */ + do { + /* rx buffer reg + * throw away (unconditionally the first time) + */ + (void) uart_readb(UART_BASE + UART_RBR); + /* line status reg */ + status = uart_readb(UART_BASE + UART_LSR); + } while(status & UART_LSR_DR); + out: + return; +} + +/* + * void serial_fini(void); + * Cleanup our use of the serial port, in particular flush the + * output buffer so we don't accidentially lose characters. + */ +static void serial_fini ( int flags __unused ) { + int i, status; + /* Flush the output buffer to avoid dropping characters, + * if we are reinitializing the serial port. + */ + i = 10000; /* timeout */ + do { + status = uart_readb(UART_BASE + UART_LSR); + } while((--i > 0) && !(status & UART_LSR_TEMPT)); + /* Don't mark it as disabled; it's still usable */ +} + +/** + * Serial driver initialisation function + * + * Initialise serial port early on so that it is available to capture + * early debug messages. + */ +struct init_fn serial_init_fn __init_fn ( INIT_SERIAL ) = { + .initialise = serial_init, +}; + +/** Serial driver startup function */ +struct startup_fn serial_startup_fn __startup_fn ( STARTUP_EARLY ) = { + .shutdown = serial_fini, +}; diff --git a/gpxe/src/core/serial_console.c b/gpxe/src/core/serial_console.c new file mode 100644 index 0000000..0300482 --- /dev/null +++ b/gpxe/src/core/serial_console.c @@ -0,0 +1,31 @@ +#include +#include +#include "console.h" + +/** @file + * + * Serial console + * + */ + +struct console_driver serial_console __console_driver; + +static void serial_console_init ( void ) { + /* Serial driver initialization should already be done, + * time to enable the serial console. */ + serial_console.disabled = 0; +} + +struct console_driver serial_console __console_driver = { + .putchar = serial_putc, + .getchar = serial_getc, + .iskey = serial_ischar, + .disabled = 1, +}; + +/** + * Serial console initialisation function + */ +struct init_fn serial_console_init_fn __init_fn ( INIT_CONSOLE ) = { + .initialise = serial_console_init, +}; diff --git a/gpxe/src/core/settings.c b/gpxe/src/core/settings.c new file mode 100644 index 0000000..f34eb66 --- /dev/null +++ b/gpxe/src/core/settings.c @@ -0,0 +1,1210 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Configuration settings + * + */ + +/** Registered settings */ +static struct setting settings[0] + __table_start ( struct setting, settings ); +static struct setting settings_end[0] + __table_end ( struct setting, settings ); + +/** Registered setting types */ +static struct setting_type setting_types[0] + __table_start ( struct setting_type, setting_types ); +static struct setting_type setting_types_end[0] + __table_end ( struct setting_type, setting_types ); + +/** Registered settings applicators */ +static struct settings_applicator settings_applicators[0] + __table_start ( struct settings_applicator, settings_applicators ); +static struct settings_applicator settings_applicators_end[0] + __table_end ( struct settings_applicator, settings_applicators ); + +/****************************************************************************** + * + * Registered settings blocks + * + ****************************************************************************** + */ + +/** + * Store value of simple setting + * + * @v options DHCP option block + * @v setting Setting to store + * @v data Setting data, or NULL to clear setting + * @v len Length of setting data + * @ret rc Return status code + */ +int simple_settings_store ( struct settings *settings, struct setting *setting, + const void *data, size_t len ) { + struct simple_settings *simple = + container_of ( settings, struct simple_settings, settings ); + return dhcpopt_extensible_store ( &simple->dhcpopts, setting->tag, + data, len ); +} + +/** + * Fetch value of simple setting + * + * @v options DHCP option block + * @v setting Setting to fetch + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +int simple_settings_fetch ( struct settings *settings, struct setting *setting, + void *data, size_t len ) { + struct simple_settings *simple = + container_of ( settings, struct simple_settings, settings ); + return dhcpopt_fetch ( &simple->dhcpopts, setting->tag, data, len ); +} + +/** Simple settings operations */ +struct settings_operations simple_settings_operations = { + .store = simple_settings_store, + .fetch = simple_settings_fetch, +}; + +/** Root simple settings block */ +struct simple_settings simple_settings_root = { + .settings = { + .refcnt = NULL, + .name = "", + .siblings = + LIST_HEAD_INIT ( simple_settings_root.settings.siblings ), + .children = + LIST_HEAD_INIT ( simple_settings_root.settings.children ), + .op = &simple_settings_operations, + }, +}; + +/** Root settings block */ +#define settings_root simple_settings_root.settings + +/** + * Apply all settings + * + * @ret rc Return status code + */ +static int apply_settings ( void ) { + struct settings_applicator *applicator; + int rc; + + /* Call all settings applicators */ + for ( applicator = settings_applicators ; + applicator < settings_applicators_end ; applicator++ ) { + if ( ( rc = applicator->apply() ) != 0 ) { + DBG ( "Could not apply settings using applicator " + "%p: %s\n", applicator, strerror ( rc ) ); + return rc; + } + } + + return 0; +} + +/** + * Reprioritise settings + * + * @v settings Settings block + * + * Reorders the settings block amongst its siblings according to its + * priority. + */ +static void reprioritise_settings ( struct settings *settings ) { + struct settings *parent = settings->parent; + long priority; + struct settings *tmp; + long tmp_priority; + + /* Stop when we reach the top of the tree */ + if ( ! parent ) + return; + + /* Read priority, if present */ + priority = fetch_intz_setting ( settings, &priority_setting ); + + /* Remove from siblings list */ + list_del ( &settings->siblings ); + + /* Reinsert after any existing blocks which have a higher priority */ + list_for_each_entry ( tmp, &parent->children, siblings ) { + tmp_priority = fetch_intz_setting ( tmp, &priority_setting ); + if ( priority > tmp_priority ) + break; + } + list_add_tail ( &settings->siblings, &tmp->siblings ); + + /* Recurse up the tree */ + reprioritise_settings ( parent ); +} + +/** + * Register settings block + * + * @v settings Settings block + * @v parent Parent settings block, or NULL + * @ret rc Return status code + */ +int register_settings ( struct settings *settings, struct settings *parent ) { + struct settings *old_settings; + + /* NULL parent => add to settings root */ + assert ( settings != NULL ); + if ( parent == NULL ) + parent = &settings_root; + + /* Remove any existing settings with the same name */ + if ( ( old_settings = find_child_settings ( parent, settings->name ) )) + unregister_settings ( old_settings ); + + /* Add to list of settings */ + ref_get ( settings->refcnt ); + ref_get ( parent->refcnt ); + settings->parent = parent; + list_add_tail ( &settings->siblings, &parent->children ); + DBGC ( settings, "Settings %p registered\n", settings ); + + /* Fix up settings priority */ + reprioritise_settings ( settings ); + + /* Apply potentially-updated settings */ + apply_settings(); + + return 0; +} + +/** + * Unregister settings block + * + * @v settings Settings block + */ +void unregister_settings ( struct settings *settings ) { + + /* Remove from list of settings */ + ref_put ( settings->refcnt ); + ref_put ( settings->parent->refcnt ); + settings->parent = NULL; + list_del ( &settings->siblings ); + DBGC ( settings, "Settings %p unregistered\n", settings ); + + /* Apply potentially-updated settings */ + apply_settings(); +} + +/** + * Find child named settings block + * + * @v parent Parent settings block + * @v name Name within this parent + * @ret settings Settings block, or NULL + */ +struct settings * find_child_settings ( struct settings *parent, + const char *name ) { + struct settings *settings; + size_t len; + + /* NULL parent => add to settings root */ + if ( parent == NULL ) + parent = &settings_root; + + /* Look for a child whose name matches the initial component */ + list_for_each_entry ( settings, &parent->children, siblings ) { + len = strlen ( settings->name ); + if ( strncmp ( name, settings->name, len ) != 0 ) + continue; + if ( name[len] == 0 ) + return settings; + if ( name[len] == '.' ) + return find_child_settings ( settings, + ( name + len + 1 ) ); + } + + return NULL; +} + +/** + * Find named settings block + * + * @v name Name + * @ret settings Settings block, or NULL + */ +struct settings * find_settings ( const char *name ) { + + /* If name is empty, use the root */ + if ( ! *name ) + return &settings_root; + + return find_child_settings ( &settings_root, name ); +} + +/****************************************************************************** + * + * Core settings routines + * + ****************************************************************************** + */ + +/** + * Store value of setting + * + * @v settings Settings block, or NULL + * @v setting Setting to store + * @v data Setting data, or NULL to clear setting + * @v len Length of setting data + * @ret rc Return status code + */ +int store_setting ( struct settings *settings, struct setting *setting, + const void *data, size_t len ) { + int rc; + + /* NULL settings implies storing into the global settings root */ + if ( ! settings ) + settings = &settings_root; + + /* Store setting */ + if ( ( rc = settings->op->store ( settings, setting, + data, len ) ) != 0 ) + return rc; + + /* Reprioritise settings if necessary */ + if ( setting_cmp ( setting, &priority_setting ) == 0 ) + reprioritise_settings ( settings ); + + /* If these settings are registered, apply potentially-updated + * settings + */ + for ( ; settings ; settings = settings->parent ) { + if ( settings == &settings_root ) { + if ( ( rc = apply_settings() ) != 0 ) + return rc; + break; + } + } + + return 0; +} + +/** + * Fetch value of setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + * + * The actual length of the setting will be returned even if + * the buffer was too small. + */ +int fetch_setting ( struct settings *settings, struct setting *setting, + void *data, size_t len ) { + struct settings *child; + int ret; + + /* Avoid returning uninitialised data on error */ + memset ( data, 0, len ); + + /* NULL settings implies starting at the global settings root */ + if ( ! settings ) + settings = &settings_root; + + /* Try this block first */ + if ( ( ret = settings->op->fetch ( settings, setting, + data, len ) ) >= 0 ) + return ret; + + /* Recurse into each child block in turn */ + list_for_each_entry ( child, &settings->children, siblings ) { + if ( ( ret = fetch_setting ( child, setting, + data, len ) ) >= 0 ) + return ret; + } + + return -ENOENT; +} + +/** + * Fetch length of setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @ret len Length of setting data, or negative error + * + * This function can also be used as an existence check for the + * setting. + */ +int fetch_setting_len ( struct settings *settings, struct setting *setting ) { + return fetch_setting ( settings, setting, NULL, 0 ); +} + +/** + * Fetch value of string setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v data Buffer to fill with setting string data + * @v len Length of buffer + * @ret len Length of string setting, or negative error + * + * The resulting string is guaranteed to be correctly NUL-terminated. + * The returned length will be the length of the underlying setting + * data. + */ +int fetch_string_setting ( struct settings *settings, struct setting *setting, + char *data, size_t len ) { + memset ( data, 0, len ); + return fetch_setting ( settings, setting, data, + ( ( len > 0 ) ? ( len - 1 ) : 0 ) ); +} + +/** + * Fetch value of string setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v data Buffer to allocate and fill with setting string data + * @ret len Length of string setting, or negative error + * + * The resulting string is guaranteed to be correctly NUL-terminated. + * The returned length will be the length of the underlying setting + * data. The caller is responsible for eventually freeing the + * allocated buffer. + */ +int fetch_string_setting_copy ( struct settings *settings, + struct setting *setting, + char **data ) { + int len; + int check_len; + + len = fetch_setting_len ( settings, setting ); + if ( len < 0 ) + return len; + + *data = malloc ( len + 1 ); + if ( ! *data ) + return -ENOMEM; + + fetch_string_setting ( settings, setting, *data, ( len + 1 ) ); + assert ( check_len == len ); + return len; +} + +/** + * Fetch value of IPv4 address setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v inp IPv4 address to fill in + * @ret len Length of setting, or negative error + */ +int fetch_ipv4_setting ( struct settings *settings, struct setting *setting, + struct in_addr *inp ) { + int len; + + len = fetch_setting ( settings, setting, inp, sizeof ( *inp ) ); + if ( len < 0 ) + return len; + if ( len < ( int ) sizeof ( *inp ) ) + return -ERANGE; + return len; +} + +/** + * Fetch value of signed integer setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v value Integer value to fill in + * @ret len Length of setting, or negative error + */ +int fetch_int_setting ( struct settings *settings, struct setting *setting, + long *value ) { + union { + uint8_t u8[ sizeof ( long ) ]; + int8_t s8[ sizeof ( long ) ]; + } buf; + int len; + int i; + + /* Avoid returning uninitialised data on error */ + *value = 0; + + /* Fetch raw (network-ordered, variable-length) setting */ + len = fetch_setting ( settings, setting, &buf, sizeof ( buf ) ); + if ( len < 0 ) + return len; + if ( len > ( int ) sizeof ( buf ) ) + return -ERANGE; + + /* Convert to host-ordered signed long */ + *value = ( ( buf.s8[0] >= 0 ) ? 0 : -1L ); + for ( i = 0 ; i < len ; i++ ) { + *value = ( ( *value << 8 ) | buf.u8[i] ); + } + + return len; +} + +/** + * Fetch value of unsigned integer setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v value Integer value to fill in + * @ret len Length of setting, or negative error + */ +int fetch_uint_setting ( struct settings *settings, struct setting *setting, + unsigned long *value ) { + long svalue; + int len; + + /* Avoid returning uninitialised data on error */ + *value = 0; + + /* Fetch as a signed long */ + len = fetch_int_setting ( settings, setting, &svalue ); + if ( len < 0 ) + return len; + + /* Mask off sign-extended bits */ + *value = ( svalue & ( -1UL >> ( sizeof ( long ) - len ) ) ); + + return len; +} + +/** + * Fetch value of signed integer setting, or zero + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @ret value Setting value, or zero + */ +long fetch_intz_setting ( struct settings *settings, struct setting *setting ){ + long value; + + fetch_int_setting ( settings, setting, &value ); + return value; +} + +/** + * Fetch value of unsigned integer setting, or zero + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @ret value Setting value, or zero + */ +unsigned long fetch_uintz_setting ( struct settings *settings, + struct setting *setting ) { + unsigned long value; + + fetch_uint_setting ( settings, setting, &value ); + return value; +} + +/** + * Fetch value of UUID setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v uuid UUID to fill in + * @ret len Length of setting, or negative error + */ +int fetch_uuid_setting ( struct settings *settings, struct setting *setting, + union uuid *uuid ) { + int len; + + len = fetch_setting ( settings, setting, uuid, sizeof ( *uuid ) ); + if ( len < 0 ) + return len; + if ( len != sizeof ( *uuid ) ) + return -ERANGE; + return len; +} + +/** + * Compare two settings + * + * @v a Setting to compare + * @v b Setting to compare + * @ret 0 Settings are the same + * @ret non-zero Settings are not the same + */ +int setting_cmp ( struct setting *a, struct setting *b ) { + + /* If the settings have tags, compare them */ + if ( a->tag && ( a->tag == b->tag ) ) + return 0; + + /* Otherwise, compare the names */ + return strcmp ( a->name, b->name ); +} + +/****************************************************************************** + * + * Formatted setting routines + * + ****************************************************************************** + */ + +/** + * Store value of typed setting + * + * @v settings Settings block + * @v setting Setting to store + * @v type Settings type + * @v value Formatted setting data, or NULL + * @ret rc Return status code + */ +int storef_setting ( struct settings *settings, struct setting *setting, + const char *value ) { + + /* NULL value implies deletion. Avoid imposing the burden of + * checking for NULL values on each typed setting's storef() + * method. + */ + if ( ! value ) + return delete_setting ( settings, setting ); + + return setting->type->storef ( settings, setting, value ); +} + +/** + * Find named setting + * + * @v name Name + * @ret setting Named setting, or NULL + */ +static struct setting * find_setting ( const char *name ) { + struct setting *setting; + + for ( setting = settings ; setting < settings_end ; setting++ ) { + if ( strcmp ( name, setting->name ) == 0 ) + return setting; + } + return NULL; +} + +/** + * Find setting type + * + * @v name Name + * @ret type Setting type, or NULL + */ +static struct setting_type * find_setting_type ( const char *name ) { + struct setting_type *type; + + for ( type = setting_types ; type < setting_types_end ; type++ ) { + if ( strcmp ( name, type->name ) == 0 ) + return type; + } + return NULL; +} + +/** + * Parse setting name + * + * @v name Name of setting + * @v settings Settings block to fill in + * @v setting Setting to fill in + * @ret rc Return status code + * + * Interprets a name of the form + * "[settings_name/]tag_name[:type_name]" and fills in the appropriate + * fields. + */ +static int parse_setting_name ( const char *name, struct settings **settings, + struct setting *setting ) { + char tmp_name[ strlen ( name ) + 1 ]; + char *settings_name; + char *setting_name; + char *type_name; + struct setting *named_setting; + char *tmp; + + /* Set defaults */ + *settings = &settings_root; + memset ( setting, 0, sizeof ( *setting ) ); + setting->type = &setting_type_hex; + + /* Split name into "[settings_name/]setting_name[:type_name]" */ + memcpy ( tmp_name, name, sizeof ( tmp_name ) ); + if ( ( setting_name = strchr ( tmp_name, '/' ) ) != NULL ) { + *(setting_name++) = 0; + settings_name = tmp_name; + } else { + setting_name = tmp_name; + settings_name = NULL; + } + if ( ( type_name = strchr ( setting_name, ':' ) ) != NULL ) + *(type_name++) = 0; + + /* Identify settings block, if specified */ + if ( settings_name ) { + *settings = find_settings ( settings_name ); + if ( *settings == NULL ) { + DBG ( "Unrecognised settings block \"%s\" in \"%s\"\n", + settings_name, name ); + return -ENODEV; + } + } + + /* Identify tag number */ + if ( ( named_setting = find_setting ( setting_name ) ) != NULL ) { + memcpy ( setting, named_setting, sizeof ( *setting ) ); + } else { + /* Unrecognised name: try to interpret as a tag number */ + tmp = setting_name; + while ( 1 ) { + setting->tag = ( ( setting->tag << 8 ) | + strtoul ( tmp, &tmp, 0 ) ); + if ( *tmp == 0 ) + break; + if ( *tmp != '.' ) { + DBG ( "Invalid setting \"%s\" in \"%s\"\n", + setting_name, name ); + return -ENOENT; + } + tmp++; + } + setting->tag |= (*settings)->tag_magic; + } + + /* Identify setting type, if specified */ + if ( type_name ) { + setting->type = find_setting_type ( type_name ); + if ( setting->type == NULL ) { + DBG ( "Invalid setting type \"%s\" in \"%s\"\n", + type_name, name ); + return -ENOTSUP; + } + } + + return 0; +} + +/** + * Parse and store value of named setting + * + * @v name Name of setting + * @v value Formatted setting data, or NULL + * @ret rc Return status code + */ +int storef_named_setting ( const char *name, const char *value ) { + struct settings *settings; + struct setting setting; + int rc; + + if ( ( rc = parse_setting_name ( name, &settings, &setting ) ) != 0 ) + return rc; + return storef_setting ( settings, &setting, value ); +} + +/** + * Fetch and format value of named setting + * + * @v name Name of setting + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +int fetchf_named_setting ( const char *name, char *buf, size_t len ) { + struct settings *settings; + struct setting setting; + int rc; + + if ( ( rc = parse_setting_name ( name, &settings, &setting ) ) != 0 ) + return rc; + return fetchf_setting ( settings, &setting, buf, len ); +} + +/****************************************************************************** + * + * Setting types + * + ****************************************************************************** + */ + +/** + * Parse and store value of string setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @ret rc Return status code + */ +static int storef_string ( struct settings *settings, struct setting *setting, + const char *value ) { + return store_setting ( settings, setting, value, strlen ( value ) ); +} + +/** + * Fetch and format value of string setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int fetchf_string ( struct settings *settings, struct setting *setting, + char *buf, size_t len ) { + return fetch_string_setting ( settings, setting, buf, len ); +} + +/** A string setting type */ +struct setting_type setting_type_string __setting_type = { + .name = "string", + .storef = storef_string, + .fetchf = fetchf_string, +}; + +/** + * Parse and store value of URI-encoded string setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @ret rc Return status code + */ +static int storef_uristring ( struct settings *settings, + struct setting *setting, + const char *value ) { + char buf[ strlen ( value ) + 1 ]; /* Decoding never expands string */ + size_t len; + + len = uri_decode ( value, buf, sizeof ( buf ) ); + return store_setting ( settings, setting, buf, len ); +} + +/** + * Fetch and format value of URI-encoded string setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int fetchf_uristring ( struct settings *settings, + struct setting *setting, + char *buf, size_t len ) { + ssize_t raw_len; + + /* We need to always retrieve the full raw string to know the + * length of the encoded string. + */ + raw_len = fetch_setting ( settings, setting, NULL, 0 ); + if ( raw_len < 0 ) + return raw_len; + + { + char raw_buf[ raw_len + 1 ]; + + fetch_string_setting ( settings, setting, raw_buf, + sizeof ( raw_buf ) ); + return uri_encode ( raw_buf, buf, len ); + } +} + +/** A URI-encoded string setting type */ +struct setting_type setting_type_uristring __setting_type = { + .name = "uristring", + .storef = storef_uristring, + .fetchf = fetchf_uristring, +}; + +/** + * Parse and store value of IPv4 address setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @ret rc Return status code + */ +static int storef_ipv4 ( struct settings *settings, struct setting *setting, + const char *value ) { + struct in_addr ipv4; + + if ( inet_aton ( value, &ipv4 ) == 0 ) + return -EINVAL; + return store_setting ( settings, setting, &ipv4, sizeof ( ipv4 ) ); +} + +/** + * Fetch and format value of IPv4 address setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int fetchf_ipv4 ( struct settings *settings, struct setting *setting, + char *buf, size_t len ) { + struct in_addr ipv4; + int raw_len; + + if ( ( raw_len = fetch_ipv4_setting ( settings, setting, &ipv4 ) ) < 0) + return raw_len; + return snprintf ( buf, len, "%s", inet_ntoa ( ipv4 ) ); +} + +/** An IPv4 address setting type */ +struct setting_type setting_type_ipv4 __setting_type = { + .name = "ipv4", + .storef = storef_ipv4, + .fetchf = fetchf_ipv4, +}; + +/** + * Parse and store value of integer setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @v size Integer size, in bytes + * @ret rc Return status code + */ +static int storef_int ( struct settings *settings, struct setting *setting, + const char *value, unsigned int size ) { + union { + uint32_t num; + uint8_t bytes[4]; + } u; + char *endp; + + u.num = htonl ( strtoul ( value, &endp, 0 ) ); + if ( *endp ) + return -EINVAL; + return store_setting ( settings, setting, + &u.bytes[ sizeof ( u ) - size ], size ); +} + +/** + * Parse and store value of 8-bit integer setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @v size Integer size, in bytes + * @ret rc Return status code + */ +static int storef_int8 ( struct settings *settings, struct setting *setting, + const char *value ) { + return storef_int ( settings, setting, value, 1 ); +} + +/** + * Parse and store value of 16-bit integer setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @v size Integer size, in bytes + * @ret rc Return status code + */ +static int storef_int16 ( struct settings *settings, struct setting *setting, + const char *value ) { + return storef_int ( settings, setting, value, 2 ); +} + +/** + * Parse and store value of 32-bit integer setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @v size Integer size, in bytes + * @ret rc Return status code + */ +static int storef_int32 ( struct settings *settings, struct setting *setting, + const char *value ) { + return storef_int ( settings, setting, value, 4 ); +} + +/** + * Fetch and format value of signed integer setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int fetchf_int ( struct settings *settings, struct setting *setting, + char *buf, size_t len ) { + long value; + int rc; + + if ( ( rc = fetch_int_setting ( settings, setting, &value ) ) < 0 ) + return rc; + return snprintf ( buf, len, "%ld", value ); +} + +/** + * Fetch and format value of unsigned integer setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int fetchf_uint ( struct settings *settings, struct setting *setting, + char *buf, size_t len ) { + unsigned long value; + int rc; + + if ( ( rc = fetch_uint_setting ( settings, setting, &value ) ) < 0 ) + return rc; + return snprintf ( buf, len, "%#lx", value ); +} + +/** A signed 8-bit integer setting type */ +struct setting_type setting_type_int8 __setting_type = { + .name = "int8", + .storef = storef_int8, + .fetchf = fetchf_int, +}; + +/** A signed 16-bit integer setting type */ +struct setting_type setting_type_int16 __setting_type = { + .name = "int16", + .storef = storef_int16, + .fetchf = fetchf_int, +}; + +/** A signed 32-bit integer setting type */ +struct setting_type setting_type_int32 __setting_type = { + .name = "int32", + .storef = storef_int32, + .fetchf = fetchf_int, +}; + +/** An unsigned 8-bit integer setting type */ +struct setting_type setting_type_uint8 __setting_type = { + .name = "uint8", + .storef = storef_int8, + .fetchf = fetchf_uint, +}; + +/** An unsigned 16-bit integer setting type */ +struct setting_type setting_type_uint16 __setting_type = { + .name = "uint16", + .storef = storef_int16, + .fetchf = fetchf_uint, +}; + +/** An unsigned 32-bit integer setting type */ +struct setting_type setting_type_uint32 __setting_type = { + .name = "uint32", + .storef = storef_int32, + .fetchf = fetchf_uint, +}; + +/** + * Parse and store value of hex string setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @ret rc Return status code + */ +static int storef_hex ( struct settings *settings, struct setting *setting, + const char *value ) { + char *ptr = ( char * ) value; + uint8_t bytes[ strlen ( value ) ]; /* cannot exceed strlen(value) */ + unsigned int len = 0; + + while ( 1 ) { + bytes[len++] = strtoul ( ptr, &ptr, 16 ); + switch ( *ptr ) { + case '\0' : + return store_setting ( settings, setting, bytes, len ); + case ':' : + ptr++; + break; + default : + return -EINVAL; + } + } +} + +/** + * Fetch and format value of hex string setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int fetchf_hex ( struct settings *settings, struct setting *setting, + char *buf, size_t len ) { + int raw_len; + int check_len; + int used = 0; + int i; + + raw_len = fetch_setting_len ( settings, setting ); + if ( raw_len < 0 ) + return raw_len; + + { + uint8_t raw[raw_len]; + + check_len = fetch_setting ( settings, setting, raw, + sizeof ( raw ) ); + if ( check_len < 0 ) + return check_len; + assert ( check_len == raw_len ); + + if ( len ) + buf[0] = 0; /* Ensure that a terminating NUL exists */ + for ( i = 0 ; i < raw_len ; i++ ) { + used += ssnprintf ( ( buf + used ), ( len - used ), + "%s%02x", ( used ? ":" : "" ), + raw[i] ); + } + return used; + } +} + +/** A hex-string setting */ +struct setting_type setting_type_hex __setting_type = { + .name = "hex", + .storef = storef_hex, + .fetchf = fetchf_hex, +}; + +/** + * Parse and store value of UUID setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @ret rc Return status code + */ +static int storef_uuid ( struct settings *settings __unused, + struct setting *setting __unused, + const char *value __unused ) { + return -ENOTSUP; +} + +/** + * Fetch and format value of UUID setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int fetchf_uuid ( struct settings *settings, struct setting *setting, + char *buf, size_t len ) { + union uuid uuid; + int raw_len; + + if ( ( raw_len = fetch_uuid_setting ( settings, setting, &uuid ) ) < 0) + return raw_len; + return snprintf ( buf, len, "%s", uuid_ntoa ( &uuid ) ); +} + +/** UUID setting type */ +struct setting_type setting_type_uuid __setting_type = { + .name = "uuid", + .storef = storef_uuid, + .fetchf = fetchf_uuid, +}; + +/****************************************************************************** + * + * Settings + * + ****************************************************************************** + */ + +/** Hostname setting */ +struct setting hostname_setting __setting = { + .name = "hostname", + .description = "Host name", + .tag = DHCP_HOST_NAME, + .type = &setting_type_string, +}; + +/** Filename setting */ +struct setting filename_setting __setting = { + .name = "filename", + .description = "Boot filename", + .tag = DHCP_BOOTFILE_NAME, + .type = &setting_type_string, +}; + +/** Root path setting */ +struct setting root_path_setting __setting = { + .name = "root-path", + .description = "NFS/iSCSI root path", + .tag = DHCP_ROOT_PATH, + .type = &setting_type_string, +}; + +/** Username setting */ +struct setting username_setting __setting = { + .name = "username", + .description = "User name", + .tag = DHCP_EB_USERNAME, + .type = &setting_type_string, +}; + +/** Password setting */ +struct setting password_setting __setting = { + .name = "password", + .description = "Password", + .tag = DHCP_EB_PASSWORD, + .type = &setting_type_string, +}; + +/** Priority setting */ +struct setting priority_setting __setting = { + .name = "priority", + .description = "Priority of these settings", + .tag = DHCP_EB_PRIORITY, + .type = &setting_type_int8, +}; diff --git a/gpxe/src/core/string.c b/gpxe/src/core/string.c new file mode 100644 index 0000000..2e17bdc --- /dev/null +++ b/gpxe/src/core/string.c @@ -0,0 +1,353 @@ +/* + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 2004 Tobias Lorenz + * + * string handling functions + * based on linux/lib/string.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * stupid library routines.. The optimized versions should generally be found + * as inline code in + * + * 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 +#include +#include +#include + +/* *** FROM string.c *** */ + +#ifndef __HAVE_ARCH_STRCPY +/** + * strcpy - Copy a %NUL terminated string + * @dest: Where to copy the string to + * @src: Where to copy the string from + */ +char * strcpy(char * dest,const char *src) +{ + char *tmp = dest; + + while ((*dest++ = *src++) != '\0') + /* nothing */; + return tmp; +} +#endif + +#ifndef __HAVE_ARCH_STRNCPY +/** + * strncpy - Copy a length-limited, %NUL-terminated string + * @dest: Where to copy the string to + * @src: Where to copy the string from + * @count: The maximum number of bytes to copy + * + * Note that unlike userspace strncpy, this does not %NUL-pad the buffer. + * However, the result is not %NUL-terminated if the source exceeds + * @count bytes. + */ +char * strncpy(char * dest,const char *src,size_t count) +{ + char *tmp = dest; + + while (count-- && (*dest++ = *src++) != '\0') + /* nothing */; + + return tmp; +} +#endif + +#ifndef __HAVE_ARCH_STRCAT +/** + * strcat - Append one %NUL-terminated string to another + * @dest: The string to be appended to + * @src: The string to append to it + */ +char * strcat(char * dest, const char * src) +{ + char *tmp = dest; + + while (*dest) + dest++; + while ((*dest++ = *src++) != '\0') + ; + + return tmp; +} +#endif + +#ifndef __HAVE_ARCH_STRCMP +/** + * strcmp - Compare two strings + * @cs: One string + * @ct: Another string + */ +int strcmp(const char * cs,const char * ct) +{ + register signed char __res; + + while (1) { + if ((__res = *cs - *ct++) != 0 || !*cs++) + break; + } + + return __res; +} +#endif + +#ifndef __HAVE_ARCH_STRNCMP +/** + * strncmp - Compare two length-limited strings + * @cs: One string + * @ct: Another string + * @count: The maximum number of bytes to compare + */ +int strncmp(const char * cs,const char * ct,size_t count) +{ + register signed char __res = 0; + + while (count) { + if ((__res = *cs - *ct++) != 0 || !*cs++) + break; + count--; + } + + return __res; +} +#endif + +#ifndef __HAVE_ARCH_STRCASECMP +int strcasecmp(const char *a, const char *b) +{ + while (*a && *b && (*a & ~0x20) == (*b & ~0x20)) {a++; b++; } + return((*a & ~0x20) - (*b & ~0x20)); +} +#endif + +#ifndef __HAVE_ARCH_STRCHR +/** + * strchr - Find the first occurrence of a character in a string + * @s: The string to be searched + * @c: The character to search for + */ +char * strchr(const char * s, int c) +{ + for(; *s != (char) c; ++s) + if (*s == '\0') + return NULL; + return (char *) s; +} +#endif + +#ifndef __HAVE_ARCH_STRRCHR +/** + * strrchr - Find the last occurrence of a character in a string + * @s: The string to be searched + * @c: The character to search for + */ +char * strrchr(const char * s, int c) +{ + const char *p = s + strlen(s); + do { + if (*p == (char)c) + return (char *)p; + } while (--p >= s); + return NULL; +} +#endif + +#ifndef __HAVE_ARCH_STRLEN +/** + * strlen - Find the length of a string + * @s: The string to be sized + */ +size_t strlen(const char * s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} +#endif + +#ifndef __HAVE_ARCH_STRNLEN +/** + * strnlen - Find the length of a length-limited string + * @s: The string to be sized + * @count: The maximum number of bytes to search + */ +size_t strnlen(const char * s, size_t count) +{ + const char *sc; + + for (sc = s; count-- && *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} +#endif + +#ifndef __HAVE_ARCH_MEMSET +/** + * memset - Fill a region of memory with the given value + * @s: Pointer to the start of the area. + * @c: The byte to fill the area with + * @count: The size of the area. + * + * Do not use memset() to access IO space, use memset_io() instead. + */ +void * memset(void * s,int c,size_t count) +{ + char *xs = (char *) s; + + while (count--) + *xs++ = c; + + return s; +} +#endif + +#ifndef __HAVE_ARCH_MEMCPY +/** + * memcpy - Copy one area of memory to another + * @dest: Where to copy to + * @src: Where to copy from + * @count: The size of the area. + * + * You should not use this function to access IO space, use memcpy_toio() + * or memcpy_fromio() instead. + */ +void * memcpy(void * dest,const void *src,size_t count) +{ + char *tmp = (char *) dest, *s = (char *) src; + + while (count--) + *tmp++ = *s++; + + return dest; +} +#endif + +#ifndef __HAVE_ARCH_MEMMOVE +/** + * memmove - Copy one area of memory to another + * @dest: Where to copy to + * @src: Where to copy from + * @count: The size of the area. + * + * Unlike memcpy(), memmove() copes with overlapping areas. + */ +void * memmove(void * dest,const void *src,size_t count) +{ + char *tmp, *s; + + if (dest <= src) { + tmp = (char *) dest; + s = (char *) src; + while (count--) + *tmp++ = *s++; + } + else { + tmp = (char *) dest + count; + s = (char *) src + count; + while (count--) + *--tmp = *--s; + } + + return dest; +} +#endif + +#ifndef __HAVE_ARCH_MEMCMP +/** + * memcmp - Compare two areas of memory + * @cs: One area of memory + * @ct: Another area of memory + * @count: The size of the area. + */ +int memcmp(const void * cs,const void * ct,size_t count) +{ + const unsigned char *su1, *su2; + int res = 0; + + for( su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--) + if ((res = *su1 - *su2) != 0) + break; + return res; +} +#endif + +#ifndef __HAVE_ARCH_STRSTR +/** + * strstr - Find the first substring in a %NUL terminated string + * @s1: The string to be searched + * @s2: The string to search for + */ +char * strstr(const char * s1,const char * s2) +{ + int l1, l2; + + l2 = strlen(s2); + if (!l2) + return (char *) s1; + l1 = strlen(s1); + while (l1 >= l2) { + l1--; + if (!memcmp(s1,s2,l2)) + return (char *) s1; + s1++; + } + return NULL; +} +#endif + +#ifndef __HAVE_ARCH_MEMCHR +/** + * memchr - Find a character in an area of memory. + * @s: The memory area + * @c: The byte to search for + * @n: The size of the area. + * + * returns the address of the first occurrence of @c, or %NULL + * if @c is not found + */ +void * memchr(const void *s, int c, size_t n) +{ + const unsigned char *p = s; + while (n-- != 0) { + if ((unsigned char)c == *p++) { + return (void *)(p-1); + } + } + return NULL; +} + +#endif + +char * strndup(const char *s, size_t n) +{ + size_t len = strlen(s); + char *new; + + if (len>n) + len = n; + new = malloc(len+1); + if (new) { + new[len] = '\0'; + memcpy(new,s,len); + } + return new; +} + +char * strdup(const char *s) { + return strndup(s, ~((size_t)0)); +} diff --git a/gpxe/src/core/stringextra.c b/gpxe/src/core/stringextra.c new file mode 100644 index 0000000..c2be4fc --- /dev/null +++ b/gpxe/src/core/stringextra.c @@ -0,0 +1,273 @@ +/* + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 2004 Tobias Lorenz + * + * string handling functions + * based on linux/lib/string.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * stupid library routines.. The optimized versions should generally be found + * as inline code in + * + * 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. + */ + +/* + * these are the standard string functions that are currently not used by + * any code in etherboot. put into a separate file to avoid linking them in + * with the rest of string.o + * if anything ever does want to use a function of these, consider moving + * the function in question back into string.c + */ + +#include +#include +#include +#include + +/* *** FROM string.c *** */ + +#ifndef __HAVE_ARCH_STRNICMP +/** + * strnicmp - Case insensitive, length-limited string comparison + * @s1: One string + * @s2: The other string + * @len: the maximum number of characters to compare + */ +int strnicmp(const char *s1, const char *s2, size_t len) +{ + /* Yes, Virginia, it had better be unsigned */ + unsigned char c1, c2; + + c1 = 0; c2 = 0; + if (len) { + do { + c1 = *s1; c2 = *s2; + s1++; s2++; + if (!c1) + break; + if (!c2) + break; + if (c1 == c2) + continue; + c1 = tolower(c1); + c2 = tolower(c2); + if (c1 != c2) + break; + } while (--len); + } + return (int)c1 - (int)c2; +} +#endif + +char * ___strtok; + +#ifndef __HAVE_ARCH_STRNCAT +/** + * strncat - Append a length-limited, %NUL-terminated string to another + * @dest: The string to be appended to + * @src: The string to append to it + * @count: The maximum numbers of bytes to copy + * + * Note that in contrast to strncpy, strncat ensures the result is + * terminated. + */ +char * strncat(char *dest, const char *src, size_t count) +{ + char *tmp = dest; + + if (count) { + while (*dest) + dest++; + while ((*dest++ = *src++)) { + if (--count == 0) { + *dest = '\0'; + break; + } + } + } + + return tmp; +} +#endif + +#ifndef __HAVE_ARCH_STRSPN +/** + * strspn - Calculate the length of the initial substring of @s which only + * contain letters in @accept + * @s: The string to be searched + * @accept: The string to search for + */ +size_t strspn(const char *s, const char *accept) +{ + const char *p; + const char *a; + size_t count = 0; + + for (p = s; *p != '\0'; ++p) { + for (a = accept; *a != '\0'; ++a) { + if (*p == *a) + break; + } + if (*a == '\0') + return count; + ++count; + } + + return count; +} +#endif + +#ifndef __HAVE_ARCH_STRCSPN +/** + * strcspn - Calculate the length of the initial substring of @s which only + * contain letters not in @reject + * @s: The string to be searched + * @accept: The string to search for + */ +size_t strcspn(const char *s, const char *reject) +{ + const char *p; + const char *r; + size_t count = 0; + + for (p = s; *p != '\0'; ++p) { + for (r = reject; *r != '\0'; ++r) { + if (*p == *r) + return count; + } + ++count; + } + + return count; +} +#endif + +#ifndef __HAVE_ARCH_STRPBRK +/** + * strpbrk - Find the first occurrence of a set of characters + * @cs: The string to be searched + * @ct: The characters to search for + */ +char * strpbrk(const char * cs,const char * ct) +{ + const char *sc1,*sc2; + + for( sc1 = cs; *sc1 != '\0'; ++sc1) { + for( sc2 = ct; *sc2 != '\0'; ++sc2) { + if (*sc1 == *sc2) + return (char *) sc1; + } + } + return NULL; +} +#endif + +#ifndef __HAVE_ARCH_STRTOK +/** + * strtok - Split a string into tokens + * @s: The string to be searched + * @ct: The characters to search for + * + * WARNING: strtok is deprecated, use strsep instead. + */ +char * strtok(char * s,const char * ct) +{ + char *sbegin, *send; + + sbegin = s ? s : ___strtok; + if (!sbegin) { + return NULL; + } + sbegin += strspn(sbegin,ct); + if (*sbegin == '\0') { + ___strtok = NULL; + return( NULL ); + } + send = strpbrk( sbegin, ct); + if (send && *send != '\0') + *send++ = '\0'; + ___strtok = send; + return (sbegin); +} +#endif + +#ifndef __HAVE_ARCH_STRSEP +/** + * strsep - Split a string into tokens + * @s: The string to be searched + * @ct: The characters to search for + * + * strsep() updates @s to point after the token, ready for the next call. + * + * It returns empty tokens, too, behaving exactly like the libc function + * of that name. In fact, it was stolen from glibc2 and de-fancy-fied. + * Same semantics, slimmer shape. ;) + */ +char * strsep(char **s, const char *ct) +{ + char *sbegin = *s, *end; + + if (sbegin == NULL) + return NULL; + + end = strpbrk(sbegin, ct); + if (end) + *end++ = '\0'; + *s = end; + + return sbegin; +} +#endif + +#ifndef __HAVE_ARCH_BCOPY +/** + * bcopy - Copy one area of memory to another + * @src: Where to copy from + * @dest: Where to copy to + * @count: The size of the area. + * + * Note that this is the same as memcpy(), with the arguments reversed. + * memcpy() is the standard, bcopy() is a legacy BSD function. + * + * You should not use this function to access IO space, use memcpy_toio() + * or memcpy_fromio() instead. + */ +char * bcopy(const char * src, char * dest, int count) +{ + return memmove(dest,src,count); +} +#endif + +#ifndef __HAVE_ARCH_MEMSCAN +/** + * memscan - Find a character in an area of memory. + * @addr: The memory area + * @c: The byte to search for + * @size: The size of the area. + * + * returns the address of the first occurrence of @c, or 1 byte past + * the area if @c is not found + */ +void * memscan(const void * addr, int c, size_t size) +{ + unsigned char * p = (unsigned char *) addr; + + while (size) { + if (*p == c) + return (void *) p; + p++; + size--; + } + return (void *) p; +} +#endif diff --git a/gpxe/src/core/timer.c b/gpxe/src/core/timer.c new file mode 100644 index 0000000..d71e3da --- /dev/null +++ b/gpxe/src/core/timer.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008 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. + */ + +#include + +/** + * Delay for a fixed number of milliseconds + * + * @v msecs Number of milliseconds for which to delay + */ +void mdelay ( unsigned long msecs ) { + while ( msecs-- ) + udelay ( 1000 ); +} + +/** + * Delay for a fixed number of seconds + * + * @v secs Number of seconds for which to delay + */ +unsigned int sleep ( unsigned int secs ) { + while ( secs-- ) + mdelay ( 1000 ); + return 0; +} diff --git a/gpxe/src/core/uri.c b/gpxe/src/core/uri.c new file mode 100644 index 0000000..7bb46da --- /dev/null +++ b/gpxe/src/core/uri.c @@ -0,0 +1,465 @@ +/* + * Copyright (C) 2007 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. + */ + +/** @file + * + * Uniform Resource Identifiers + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/** + * Dump URI for debugging + * + * @v uri URI + */ +static void dump_uri ( struct uri *uri ) { + if ( ! uri ) + return; + if ( uri->scheme ) + DBG ( " scheme \"%s\"", uri->scheme ); + if ( uri->opaque ) + DBG ( " opaque \"%s\"", uri->opaque ); + if ( uri->user ) + DBG ( " user \"%s\"", uri->user ); + if ( uri->password ) + DBG ( " password \"%s\"", uri->password ); + if ( uri->host ) + DBG ( " host \"%s\"", uri->host ); + if ( uri->port ) + DBG ( " port \"%s\"", uri->port ); + if ( uri->path ) + DBG ( " path \"%s\"", uri->path ); + if ( uri->query ) + DBG ( " query \"%s\"", uri->query ); + if ( uri->fragment ) + DBG ( " fragment \"%s\"", uri->fragment ); +} + +/** + * Parse URI + * + * @v uri_string URI as a string + * @ret uri URI + * + * Splits a URI into its component parts. The return URI structure is + * dynamically allocated and must eventually be freed by calling + * uri_put(). + */ +struct uri * parse_uri ( const char *uri_string ) { + struct uri *uri; + char *raw; + char *tmp; + char *path = NULL; + char *authority = NULL; + size_t raw_len; + + /* Allocate space for URI struct and a copy of the string */ + raw_len = ( strlen ( uri_string ) + 1 /* NUL */ ); + uri = zalloc ( sizeof ( *uri ) + raw_len ); + if ( ! uri ) + return NULL; + raw = ( ( ( char * ) uri ) + sizeof ( *uri ) ); + + /* Zero URI struct and copy in the raw string */ + memcpy ( raw, uri_string, raw_len ); + + /* Start by chopping off the fragment, if it exists */ + if ( ( tmp = strchr ( raw, '#' ) ) ) { + *(tmp++) = '\0'; + uri->fragment = tmp; + } + + /* Identify absolute/relative URI. We ignore schemes that are + * apparently only a single character long, since otherwise we + * misinterpret a DOS-style path name ("C:\path\to\file") as a + * URI with scheme="C",opaque="\path\to\file". + */ + if ( ( tmp = strchr ( raw, ':' ) ) && ( tmp > ( raw + 1 ) ) ) { + /* Absolute URI: identify hierarchical/opaque */ + uri->scheme = raw; + *(tmp++) = '\0'; + if ( *tmp == '/' ) { + /* Absolute URI with hierarchical part */ + path = tmp; + } else { + /* Absolute URI with opaque part */ + uri->opaque = tmp; + } + } else { + /* Relative URI */ + path = raw; + } + + /* If we don't have a path (i.e. we have an absolute URI with + * an opaque portion, we're already finished processing + */ + if ( ! path ) + goto done; + + /* Chop off the query, if it exists */ + if ( ( tmp = strchr ( path, '?' ) ) ) { + *(tmp++) = '\0'; + uri->query = tmp; + } + + /* Identify net/absolute/relative path */ + if ( strncmp ( path, "//", 2 ) == 0 ) { + /* Net path. If this is terminated by the first '/' + * of an absolute path, then we have no space for a + * terminator after the authority field, so shuffle + * the authority down by one byte, overwriting one of + * the two slashes. + */ + authority = ( path + 2 ); + if ( ( tmp = strchr ( authority, '/' ) ) ) { + /* Shuffle down */ + uri->path = tmp; + memmove ( ( authority - 1 ), authority, + ( tmp - authority ) ); + authority--; + *(--tmp) = '\0'; + } + } else { + /* Absolute/relative path */ + uri->path = path; + } + + /* Split authority into user[:password] and host[:port] portions */ + if ( ( tmp = strchr ( authority, '@' ) ) ) { + /* Has user[:password] */ + *(tmp++) = '\0'; + uri->host = tmp; + uri->user = authority; + if ( ( tmp = strchr ( authority, ':' ) ) ) { + /* Has password */ + *(tmp++) = '\0'; + uri->password = tmp; + } + } else { + /* No user:password */ + uri->host = authority; + } + + /* Split host into host[:port] */ + if ( ( tmp = strchr ( uri->host, ':' ) ) ) { + *(tmp++) = '\0'; + uri->port = tmp; + } + + done: + DBG ( "URI \"%s\" split into", uri_string ); + dump_uri ( uri ); + DBG ( "\n" ); + + return uri; +} + +/** + * Get port from URI + * + * @v uri URI, or NULL + * @v default_port Default port to use if none specified in URI + * @ret port Port + */ +unsigned int uri_port ( struct uri *uri, unsigned int default_port ) { + if ( ( ! uri ) || ( ! uri->port ) ) + return default_port; + return ( strtoul ( uri->port, NULL, 0 ) ); +} + +/** + * Unparse URI + * + * @v buf Buffer to fill with URI string + * @v size Size of buffer + * @v uri URI to write into buffer, or NULL + * @ret len Length of URI string + */ +int unparse_uri ( char *buf, size_t size, struct uri *uri ) { + int used = 0; + + DBG ( "URI unparsing" ); + dump_uri ( uri ); + DBG ( "\n" ); + + /* Special-case NULL URI */ + if ( ! uri ) { + if ( size ) + buf[0] = '\0'; + return 0; + } + + /* Special-case opaque URIs */ + if ( uri->opaque ) { + return ssnprintf ( ( buf + used ), ( size - used ), + "%s:%s", uri->scheme, uri->opaque ); + } + + /* scheme:// */ + if ( uri->scheme ) { + used += ssnprintf ( ( buf + used ), ( size - used ), + "%s://", uri->scheme ); + } + + /* [user[:password]@]host[:port] */ + if ( uri->host ) { + if ( uri->user ) { + used += ssnprintf ( ( buf + used ), ( size - used ), + "%s", uri->user ); + if ( uri->password ) { + used += ssnprintf ( ( buf + used ), + ( size - used ), + ":%s", uri->password ); + } + used += ssnprintf ( ( buf + used ), ( size - used ), + "@" ); + } + used += ssnprintf ( ( buf + used ), ( size - used ), "%s", + uri->host ); + if ( uri->port ) { + used += ssnprintf ( ( buf + used ), ( size - used ), + ":%s", uri->port ); + } + } + + /* /path */ + if ( uri->path ) { + used += ssnprintf ( ( buf + used ), ( size - used ), + "%s", uri->path ); + } + + /* ?query */ + if ( uri->query ) { + used += ssnprintf ( ( buf + used ), ( size - used ), + "?%s", uri->query ); + } + + /* #fragment */ + if ( uri->fragment ) { + used += ssnprintf ( ( buf + used ), ( size - used ), + "#%s", uri->fragment ); + } + + return used; +} + +/** + * Duplicate URI + * + * @v uri URI + * @ret uri Duplicate URI + * + * Creates a modifiable copy of a URI. + */ +struct uri * uri_dup ( struct uri *uri ) { + size_t len = ( unparse_uri ( NULL, 0, uri ) + 1 ); + char buf[len]; + + unparse_uri ( buf, len, uri ); + return parse_uri ( buf ); +} + +/** + * Resolve base+relative path + * + * @v base_uri Base path + * @v relative_uri Relative path + * @ret resolved_uri Resolved path + * + * Takes a base path (e.g. "/var/lib/tftpboot/vmlinuz" and a relative + * path (e.g. "initrd.gz") and produces a new path + * (e.g. "/var/lib/tftpboot/initrd.gz"). Note that any non-directory + * portion of the base path will automatically be stripped; this + * matches the semantics used when resolving the path component of + * URIs. + */ +char * resolve_path ( const char *base_path, + const char *relative_path ) { + size_t base_len = ( strlen ( base_path ) + 1 ); + char base_path_copy[base_len]; + char *base_tmp = base_path_copy; + char *resolved; + + /* If relative path is absolute, just re-use it */ + if ( relative_path[0] == '/' ) + return strdup ( relative_path ); + + /* Create modifiable copy of path for dirname() */ + memcpy ( base_tmp, base_path, base_len ); + base_tmp = dirname ( base_tmp ); + + /* Process "./" and "../" elements */ + while ( *relative_path == '.' ) { + relative_path++; + if ( *relative_path == 0 ) { + /* Do nothing */ + } else if ( *relative_path == '/' ) { + relative_path++; + } else if ( *relative_path == '.' ) { + relative_path++; + if ( *relative_path == 0 ) { + base_tmp = dirname ( base_tmp ); + } else if ( *relative_path == '/' ) { + base_tmp = dirname ( base_tmp ); + relative_path++; + } else { + relative_path -= 2; + break; + } + } else { + relative_path--; + break; + } + } + + /* Create and return new path */ + if ( asprintf ( &resolved, "%s%s%s", base_tmp, + ( ( base_tmp[ strlen ( base_tmp ) - 1 ] == '/' ) ? + "" : "/" ), relative_path ) < 0 ) + return NULL; + + return resolved; +} + +/** + * Resolve base+relative URI + * + * @v base_uri Base URI, or NULL + * @v relative_uri Relative URI + * @ret resolved_uri Resolved URI + * + * Takes a base URI (e.g. "http://etherboot.org/kernels/vmlinuz" and a + * relative URI (e.g. "../initrds/initrd.gz") and produces a new URI + * (e.g. "http://etherboot.org/initrds/initrd.gz"). + */ +struct uri * resolve_uri ( struct uri *base_uri, + struct uri *relative_uri ) { + struct uri tmp_uri; + char *tmp_path = NULL; + struct uri *new_uri; + + /* If relative URI is absolute, just re-use it */ + if ( uri_is_absolute ( relative_uri ) || ( ! base_uri ) ) + return uri_get ( relative_uri ); + + /* Mangle URI */ + memcpy ( &tmp_uri, base_uri, sizeof ( tmp_uri ) ); + if ( relative_uri->path ) { + tmp_path = resolve_path ( ( base_uri->path ? + base_uri->path : "/" ), + relative_uri->path ); + tmp_uri.path = tmp_path; + tmp_uri.query = relative_uri->query; + tmp_uri.fragment = relative_uri->fragment; + } else if ( relative_uri->query ) { + tmp_uri.query = relative_uri->query; + tmp_uri.fragment = relative_uri->fragment; + } else if ( relative_uri->fragment ) { + tmp_uri.fragment = relative_uri->fragment; + } + + /* Create demangled URI */ + new_uri = uri_dup ( &tmp_uri ); + free ( tmp_path ); + return new_uri; +} + +/** + * Test for unreserved URI characters + * + * @v c Character to test + * @ret is_unreserved Character is an unreserved character + */ +static int is_unreserved_uri_char ( int c ) { + /* According to RFC3986, the unreserved character set is + * + * A-Z a-z 0-9 - _ . ~ + */ + return ( isupper ( c ) || islower ( c ) || isdigit ( c ) || + ( c == '-' ) || ( c == '_' ) || + ( c == '.' ) || ( c == '~' ) ); +} + +/** + * URI-encode string + * + * @v raw_string String to be URI-encoded + * @v buf Buffer to contain encoded string + * @v len Length of buffer + * @ret len Length of encoded string (excluding NUL) + */ +size_t uri_encode ( const char *raw_string, char *buf, size_t len ) { + ssize_t remaining = len; + size_t used; + unsigned char c; + + if ( len ) + buf[0] = '\0'; + + while ( ( c = *(raw_string++) ) ) { + if ( is_unreserved_uri_char ( c ) ) { + used = ssnprintf ( buf, remaining, "%c", c ); + } else { + used = ssnprintf ( buf, remaining, "%%%02X", c ); + } + buf += used; + remaining -= used; + } + + return ( len - remaining ); +} + +/** + * Decode URI-encoded string + * + * @v encoded_string URI-encoded string + * @v buf Buffer to contain decoded string + * @v len Length of buffer + * @ret len Length of decoded string (excluding NUL) + */ +size_t uri_decode ( const char *encoded_string, char *buf, size_t len ) { + ssize_t remaining = len; + char hexbuf[3]; + char *hexbuf_end; + unsigned char c; + + if ( len ) + buf[0] = '\0'; + + while ( *encoded_string ) { + if ( *encoded_string == '%' ) { + encoded_string++; + snprintf ( hexbuf, sizeof ( hexbuf ), "%s", + encoded_string ); + c = strtoul ( hexbuf, &hexbuf_end, 16 ); + encoded_string += ( hexbuf_end - hexbuf ); + } else { + c = *(encoded_string++); + } + ssnprintf ( buf++, remaining--, "%c", c ); + } + return ( len - remaining ); +} diff --git a/gpxe/src/core/uuid.c b/gpxe/src/core/uuid.c new file mode 100644 index 0000000..a3a82c6 --- /dev/null +++ b/gpxe/src/core/uuid.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include + +/** @file + * + * Universally unique IDs + * + */ + +/** + * Convert UUID to printable string + * + * @v uuid UUID + * @ret string UUID in canonical form + */ +char * uuid_ntoa ( union uuid *uuid ) { + static char buf[37]; /* "00000000-0000-0000-0000-000000000000" */ + + sprintf ( buf, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", + be32_to_cpu ( uuid->canonical.a ), + be16_to_cpu ( uuid->canonical.b ), + be16_to_cpu ( uuid->canonical.c ), + be16_to_cpu ( uuid->canonical.d ), + uuid->canonical.e[0], uuid->canonical.e[1], + uuid->canonical.e[2], uuid->canonical.e[3], + uuid->canonical.e[4], uuid->canonical.e[5] ); + return buf; +} diff --git a/gpxe/src/core/vsprintf.c b/gpxe/src/core/vsprintf.c new file mode 100644 index 0000000..4457fe4 --- /dev/null +++ b/gpxe/src/core/vsprintf.c @@ -0,0 +1,421 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include + +/** @file */ + +#define CHAR_LEN 0 /**< "hh" length modifier */ +#define SHORT_LEN 1 /**< "h" length modifier */ +#define INT_LEN 2 /**< no length modifier */ +#define LONG_LEN 3 /**< "l" length modifier */ +#define LONGLONG_LEN 4 /**< "ll" length modifier */ +#define SIZE_T_LEN 5 /**< "z" length modifier */ + +static uint8_t type_sizes[] = { + [CHAR_LEN] = sizeof ( char ), + [SHORT_LEN] = sizeof ( short ), + [INT_LEN] = sizeof ( int ), + [LONG_LEN] = sizeof ( long ), + [LONGLONG_LEN] = sizeof ( long long ), + [SIZE_T_LEN] = sizeof ( size_t ), +}; + +/** + * Use lower-case for hexadecimal digits + * + * Note that this value is set to 0x20 since that makes for very + * efficient calculations. (Bitwise-ORing with @c LCASE converts to a + * lower-case character, for example.) + */ +#define LCASE 0x20 + +/** + * Use "alternate form" + * + * For hexadecimal numbers, this means to add a "0x" or "0X" prefix to + * the number. + */ +#define ALT_FORM 0x02 + +/** + * Format a hexadecimal number + * + * @v end End of buffer to contain number + * @v num Number to format + * @v width Minimum field width + * @ret ptr End of buffer + * + * Fills a buffer in reverse order with a formatted hexadecimal + * number. The number will be zero-padded to the specified width. + * Lower-case and "alternate form" (i.e. "0x" prefix) flags may be + * set. + * + * There must be enough space in the buffer to contain the largest + * number that this function can format. + */ +static char * format_hex ( char *end, unsigned long long num, int width, + int flags ) { + char *ptr = end; + int case_mod; + + /* Generate the number */ + case_mod = flags & LCASE; + do { + *(--ptr) = "0123456789ABCDEF"[ num & 0xf ] | case_mod; + num >>= 4; + } while ( num ); + + /* Zero-pad to width */ + while ( ( end - ptr ) < width ) + *(--ptr) = '0'; + + /* Add "0x" or "0X" if alternate form specified */ + if ( flags & ALT_FORM ) { + *(--ptr) = 'X' | case_mod; + *(--ptr) = '0'; + } + + return ptr; +} + +/** + * Format a decimal number + * + * @v end End of buffer to contain number + * @v num Number to format + * @v width Minimum field width + * @ret ptr End of buffer + * + * Fills a buffer in reverse order with a formatted decimal number. + * The number will be space-padded to the specified width. + * + * There must be enough space in the buffer to contain the largest + * number that this function can format. + */ +static char * format_decimal ( char *end, signed long num, int width ) { + char *ptr = end; + int negative = 0; + + /* Generate the number */ + if ( num < 0 ) { + negative = 1; + num = -num; + } + do { + *(--ptr) = '0' + ( num % 10 ); + num /= 10; + } while ( num ); + + /* Add "-" if necessary */ + if ( negative ) + *(--ptr) = '-'; + + /* Space-pad to width */ + while ( ( end - ptr ) < width ) + *(--ptr) = ' '; + + return ptr; +} + +/** + * Print character via a printf context + * + * @v ctx Context + * @v c Character + * + * Call's the printf_context::handler() method and increments + * printf_context::len. + */ +static inline void cputchar ( struct printf_context *ctx, unsigned int c ) { + ctx->handler ( ctx, c ); + ++ctx->len; +} + +/** + * Write a formatted string to a printf context + * + * @v ctx Context + * @v fmt Format string + * @v args Arguments corresponding to the format string + * @ret len Length of formatted string + */ +size_t vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) { + int flags; + int width; + uint8_t *length; + char *ptr; + char tmp_buf[32]; /* 32 is enough for all numerical formats. + * Insane width fields could overflow this buffer. */ + + /* Initialise context */ + ctx->len = 0; + + for ( ; *fmt ; fmt++ ) { + /* Pass through ordinary characters */ + if ( *fmt != '%' ) { + cputchar ( ctx, *fmt ); + continue; + } + fmt++; + /* Process flag characters */ + flags = 0; + for ( ; ; fmt++ ) { + if ( *fmt == '#' ) { + flags |= ALT_FORM; + } else if ( *fmt == '0' ) { + /* We always 0-pad hex and space-pad decimal */ + } else { + /* End of flag characters */ + break; + } + } + /* Process field width */ + width = 0; + for ( ; ; fmt++ ) { + if ( ( ( unsigned ) ( *fmt - '0' ) ) < 10 ) { + width = ( width * 10 ) + ( *fmt - '0' ); + } else { + break; + } + } + /* We don't do floating point */ + /* Process length modifier */ + length = &type_sizes[INT_LEN]; + for ( ; ; fmt++ ) { + if ( *fmt == 'h' ) { + length--; + } else if ( *fmt == 'l' ) { + length++; + } else if ( *fmt == 'z' ) { + length = &type_sizes[SIZE_T_LEN]; + } else { + break; + } + } + /* Process conversion specifier */ + ptr = tmp_buf + sizeof ( tmp_buf ) - 1; + *ptr = '\0'; + if ( *fmt == 'c' ) { + cputchar ( ctx, va_arg ( args, unsigned int ) ); + } else if ( *fmt == 's' ) { + ptr = va_arg ( args, char * ); + if ( ! ptr ) + ptr = ""; + } else if ( *fmt == 'p' ) { + intptr_t ptrval; + + ptrval = ( intptr_t ) va_arg ( args, void * ); + ptr = format_hex ( ptr, ptrval, width, + ( ALT_FORM | LCASE ) ); + } else if ( ( *fmt & ~0x20 ) == 'X' ) { + unsigned long long hex; + + flags |= ( *fmt & 0x20 ); /* LCASE */ + if ( *length >= sizeof ( unsigned long long ) ) { + hex = va_arg ( args, unsigned long long ); + } else if ( *length >= sizeof ( unsigned long ) ) { + hex = va_arg ( args, unsigned long ); + } else { + hex = va_arg ( args, unsigned int ); + } + ptr = format_hex ( ptr, hex, width, flags ); + } else if ( ( *fmt == 'd' ) || ( *fmt == 'i' ) ){ + signed long decimal; + + if ( *length >= sizeof ( signed long ) ) { + decimal = va_arg ( args, signed long ); + } else { + decimal = va_arg ( args, signed int ); + } + ptr = format_decimal ( ptr, decimal, width ); + } else { + *(--ptr) = *fmt; + } + /* Write out conversion result */ + for ( ; *ptr ; ptr++ ) { + cputchar ( ctx, *ptr ); + } + } + + return ctx->len; +} + +/** Context used by vsnprintf() and friends */ +struct sputc_context { + struct printf_context ctx; + /** Buffer for formatted string (used by printf_sputc()) */ + char *buf; + /** Buffer length (used by printf_sputc()) */ + size_t max_len; +}; + +/** + * Write character to buffer + * + * @v ctx Context + * @v c Character + */ +static void printf_sputc ( struct printf_context *ctx, unsigned int c ) { + struct sputc_context * sctx = + container_of ( ctx, struct sputc_context, ctx ); + + if ( ctx->len < sctx->max_len ) + sctx->buf[ctx->len] = c; +} + +/** + * Write a formatted string to a buffer + * + * @v buf Buffer into which to write the string + * @v size Size of buffer + * @v fmt Format string + * @v args Arguments corresponding to the format string + * @ret len Length of formatted string + * + * If the buffer is too small to contain the string, the returned + * length is the length that would have been written had enough space + * been available. + */ +int vsnprintf ( char *buf, size_t size, const char *fmt, va_list args ) { + struct sputc_context sctx; + size_t len; + size_t end; + + /* Hand off to vcprintf */ + sctx.ctx.handler = printf_sputc; + sctx.buf = buf; + sctx.max_len = size; + len = vcprintf ( &sctx.ctx, fmt, args ); + + /* Add trailing NUL */ + if ( size ) { + end = size - 1; + if ( len < end ) + end = len; + buf[end] = '\0'; + } + + return len; +} + +/** + * Write a formatted string to a buffer + * + * @v buf Buffer into which to write the string + * @v size Size of buffer + * @v fmt Format string + * @v ... Arguments corresponding to the format string + * @ret len Length of formatted string + */ +int snprintf ( char *buf, size_t size, const char *fmt, ... ) { + va_list args; + int i; + + va_start ( args, fmt ); + i = vsnprintf ( buf, size, fmt, args ); + va_end ( args ); + return i; +} + +/** + * Version of vsnprintf() that accepts a signed buffer size + * + * @v buf Buffer into which to write the string + * @v size Size of buffer + * @v fmt Format string + * @v args Arguments corresponding to the format string + * @ret len Length of formatted string + */ +int vssnprintf ( char *buf, ssize_t ssize, const char *fmt, va_list args ) { + + /* Treat negative buffer size as zero buffer size */ + if ( ssize < 0 ) + ssize = 0; + + /* Hand off to vsnprintf */ + return vsnprintf ( buf, ssize, fmt, args ); +} + +/** + * Version of vsnprintf() that accepts a signed buffer size + * + * @v buf Buffer into which to write the string + * @v size Size of buffer + * @v fmt Format string + * @v ... Arguments corresponding to the format string + * @ret len Length of formatted string + */ +int ssnprintf ( char *buf, ssize_t ssize, const char *fmt, ... ) { + va_list args; + int len; + + /* Hand off to vssnprintf */ + va_start ( args, fmt ); + len = vssnprintf ( buf, ssize, fmt, args ); + va_end ( args ); + return len; +} + +/** + * Write character to console + * + * @v ctx Context + * @v c Character + */ +static void printf_putchar ( struct printf_context *ctx __unused, + unsigned int c ) { + putchar ( c ); +} + +/** + * Write a formatted string to the console + * + * @v fmt Format string + * @v args Arguments corresponding to the format string + * @ret len Length of formatted string + */ +int vprintf ( const char *fmt, va_list args ) { + struct printf_context ctx; + + /* Hand off to vcprintf */ + ctx.handler = printf_putchar; + return vcprintf ( &ctx, fmt, args ); +} + +/** + * Write a formatted string to the console. + * + * @v fmt Format string + * @v ... Arguments corresponding to the format string + * @ret len Length of formatted string + */ +int printf ( const char *fmt, ... ) { + va_list args; + int i; + + va_start ( args, fmt ); + i = vprintf ( fmt, args ); + va_end ( args ); + return i; +} diff --git a/gpxe/src/core/xfer.c b/gpxe/src/core/xfer.c new file mode 100644 index 0000000..9ed19da --- /dev/null +++ b/gpxe/src/core/xfer.c @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include + +/** @file + * + * Data transfer interfaces + * + */ + +/** + * Dummy transfer metadata + * + * This gets passed to xfer_interface::deliver_iob() and equivalents + * when no metadata is available. + */ +static struct xfer_metadata dummy_metadata; + +/** + * Close data transfer interface + * + * @v xfer Data transfer interface + * @v rc Reason for close + */ +void xfer_close ( struct xfer_interface *xfer, int rc ) { + struct xfer_interface *dest = xfer_get_dest ( xfer ); + + DBGC ( xfer, "XFER %p->%p close\n", xfer, dest ); + + xfer_unplug ( xfer ); + dest->op->close ( dest, rc ); + xfer_put ( dest ); +} + +/** + * Send redirection event + * + * @v xfer Data transfer interface + * @v type New location type + * @v args Remaining arguments depend upon location type + * @ret rc Return status code + */ +int xfer_vredirect ( struct xfer_interface *xfer, int type, va_list args ) { + struct xfer_interface *dest = xfer_get_dest ( xfer ); + int rc; + + DBGC ( xfer, "XFER %p->%p redirect\n", xfer, dest ); + + rc = dest->op->vredirect ( dest, type, args ); + + if ( rc != 0 ) { + DBGC ( xfer, "XFER %p<-%p redirect: %s\n", xfer, dest, + strerror ( rc ) ); + } + xfer_put ( dest ); + return rc; +} + +/** + * Send redirection event + * + * @v xfer Data transfer interface + * @v type New location type + * @v ... Remaining arguments depend upon location type + * @ret rc Return status code + */ +int xfer_redirect ( struct xfer_interface *xfer, int type, ... ) { + va_list args; + int rc; + + va_start ( args, type ); + rc = xfer_vredirect ( xfer, type, args ); + va_end ( args ); + return rc; +} + +/** + * Check flow control window + * + * @v xfer Data transfer interface + * @ret len Length of window + */ +size_t xfer_window ( struct xfer_interface *xfer ) { + struct xfer_interface *dest = xfer_get_dest ( xfer ); + size_t len; + + len = dest->op->window ( dest ); + + xfer_put ( dest ); + return len; +} + +/** + * Allocate I/O buffer + * + * @v xfer Data transfer interface + * @v len I/O buffer payload length + * @ret iobuf I/O buffer + */ +struct io_buffer * xfer_alloc_iob ( struct xfer_interface *xfer, size_t len ) { + struct xfer_interface *dest = xfer_get_dest ( xfer ); + struct io_buffer *iobuf; + + DBGC ( xfer, "XFER %p->%p alloc_iob %zd\n", xfer, dest, len ); + + iobuf = dest->op->alloc_iob ( dest, len ); + + if ( ! iobuf ) { + DBGC ( xfer, "XFER %p<-%p alloc_iob failed\n", xfer, dest ); + } + xfer_put ( dest ); + return iobuf; +} + +/** + * Deliver datagram as I/O buffer with metadata + * + * @v xfer Data transfer interface + * @v iobuf Datagram I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +int xfer_deliver_iob_meta ( struct xfer_interface *xfer, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct xfer_interface *dest = xfer_get_dest ( xfer ); + int rc; + + DBGC ( xfer, "XFER %p->%p deliver_iob %zd\n", xfer, dest, + iob_len ( iobuf ) ); + + rc = dest->op->deliver_iob ( dest, iobuf, meta ); + + if ( rc != 0 ) { + DBGC ( xfer, "XFER %p<-%p deliver_iob: %s\n", xfer, dest, + strerror ( rc ) ); + } + xfer_put ( dest ); + return rc; +} + +/** + * Deliver datagram as I/O buffer with metadata + * + * @v xfer Data transfer interface + * @v iobuf Datagram I/O buffer + * @ret rc Return status code + */ +int xfer_deliver_iob ( struct xfer_interface *xfer, + struct io_buffer *iobuf ) { + return xfer_deliver_iob_meta ( xfer, iobuf, &dummy_metadata ); +} + +/** + * Deliver datagram as raw data + * + * @v xfer Data transfer interface + * @v iobuf Datagram I/O buffer + * @ret rc Return status code + */ +int xfer_deliver_raw ( struct xfer_interface *xfer, + const void *data, size_t len ) { + struct xfer_interface *dest = xfer_get_dest ( xfer ); + int rc; + + DBGC ( xfer, "XFER %p->%p deliver_raw %p+%zd\n", xfer, dest, + data, len ); + + rc = dest->op->deliver_raw ( dest, data, len ); + + if ( rc != 0 ) { + DBGC ( xfer, "XFER %p<-%p deliver_raw: %s\n", xfer, dest, + strerror ( rc ) ); + } + xfer_put ( dest ); + return rc; +} + +/** + * Deliver formatted string + * + * @v xfer Data transfer interface + * @v format Format string + * @v args Arguments corresponding to the format string + * @ret rc Return status code + */ +int xfer_vprintf ( struct xfer_interface *xfer, const char *format, + va_list args ) { + size_t len; + va_list args_tmp; + + va_copy ( args_tmp, args ); + len = vsnprintf ( NULL, 0, format, args ); + { + char buf[len + 1]; + vsnprintf ( buf, sizeof ( buf ), format, args_tmp ); + va_end ( args_tmp ); + return xfer_deliver_raw ( xfer, buf, len ); + } +} + +/** + * Deliver formatted string + * + * @v xfer Data transfer interface + * @v format Format string + * @v ... Arguments corresponding to the format string + * @ret rc Return status code + */ +int xfer_printf ( struct xfer_interface *xfer, const char *format, ... ) { + va_list args; + int rc; + + va_start ( args, format ); + rc = xfer_vprintf ( xfer, format, args ); + va_end ( args ); + return rc; +} + +/** + * Seek to position + * + * @v xfer Data transfer interface + * @v offset Offset to new position + * @v whence Basis for new position + * @ret rc Return status code + */ +int xfer_seek ( struct xfer_interface *xfer, off_t offset, int whence ) { + struct io_buffer *iobuf; + struct xfer_metadata meta = { + .offset = offset, + .whence = whence, + }; + + DBGC ( xfer, "XFER %p seek %s+%ld\n", xfer, + whence_text ( whence ), offset ); + + /* Allocate and send a zero-length data buffer */ + iobuf = xfer_alloc_iob ( xfer, 0 ); + if ( ! iobuf ) + return -ENOMEM; + return xfer_deliver_iob_meta ( xfer, iobuf, &meta ); +} + +/**************************************************************************** + * + * Helper methods + * + * These functions are designed to be used as methods in the + * xfer_interface_operations table. + * + */ + +/** + * Ignore close() event + * + * @v xfer Data transfer interface + * @v rc Reason for close + */ +void ignore_xfer_close ( struct xfer_interface *xfer __unused, + int rc __unused ) { + /* Nothing to do */ +} + +/** + * Ignore vredirect() event + * + * @v xfer Data transfer interface + * @v type New location type + * @v args Remaining arguments depend upon location type + * @ret rc Return status code + */ +int ignore_xfer_vredirect ( struct xfer_interface *xfer __unused, + int type __unused, va_list args __unused ) { + return 0; +} + +/** + * Unlimited flow control window + * + * @v xfer Data transfer interface + * @ret len Length of window + * + * This handler indicates that the interface is always ready to accept + * data. + */ +size_t unlimited_xfer_window ( struct xfer_interface *xfer __unused ) { + return ~( ( size_t ) 0 ); +} + +/** + * No flow control window + * + * @v xfer Data transfer interface + * @ret len Length of window + * + * This handler indicates that the interface is never ready to accept + * data. + */ +size_t no_xfer_window ( struct xfer_interface *xfer __unused ) { + return 0; +} + +/** + * Allocate I/O buffer + * + * @v xfer Data transfer interface + * @v len I/O buffer payload length + * @ret iobuf I/O buffer + */ +struct io_buffer * +default_xfer_alloc_iob ( struct xfer_interface *xfer __unused, size_t len ) { + return alloc_iob ( len ); +} + +/** + * Deliver datagram as raw data + * + * @v xfer Data transfer interface + * @v iobuf Datagram I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + * + * This function is intended to be used as the deliver() method for + * data transfer interfaces that prefer to handle raw data. + */ +int xfer_deliver_as_raw ( struct xfer_interface *xfer, + struct io_buffer *iobuf, + struct xfer_metadata *meta __unused ) { + int rc; + + rc = xfer->op->deliver_raw ( xfer, iobuf->data, iob_len ( iobuf ) ); + free_iob ( iobuf ); + return rc; +} + +/** + * Deliver datagram as I/O buffer + * + * @v xfer Data transfer interface + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + * + * This function is intended to be used as the deliver_raw() method + * for data transfer interfaces that prefer to handle I/O buffers. + */ +int xfer_deliver_as_iob ( struct xfer_interface *xfer, + const void *data, size_t len ) { + struct io_buffer *iobuf; + + iobuf = xfer->op->alloc_iob ( xfer, len ); + if ( ! iobuf ) + return -ENOMEM; + + memcpy ( iob_put ( iobuf, len ), data, len ); + return xfer->op->deliver_iob ( xfer, iobuf, &dummy_metadata ); +} + +/** + * Ignore datagram as raw data event + * + * @v xfer Data transfer interface + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +int ignore_xfer_deliver_raw ( struct xfer_interface *xfer, + const void *data __unused, size_t len ) { + DBGC ( xfer, "XFER %p %zd bytes delivered %s\n", xfer, len, + ( ( xfer == &null_xfer ) ? + "before connection" : "after termination" ) ); + return 0; +} + +/** Null data transfer interface operations */ +struct xfer_interface_operations null_xfer_ops = { + .close = ignore_xfer_close, + .vredirect = ignore_xfer_vredirect, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = xfer_deliver_as_raw, + .deliver_raw = ignore_xfer_deliver_raw, +}; + +/** + * Null data transfer interface + * + * This is the interface to which data transfer interfaces are + * connected when unplugged. It will never generate messages, and + * will silently absorb all received messages. + */ +struct xfer_interface null_xfer = XFER_INIT ( &null_xfer_ops ); diff --git a/gpxe/src/crypto/asn1.c b/gpxe/src/crypto/asn1.c new file mode 100644 index 0000000..25e7495 --- /dev/null +++ b/gpxe/src/crypto/asn1.c @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include + +/** @file + * + * ASN.1 encoding + * + */ + +/** + * Start parsing ASN.1 object + * + * @v cursor ASN.1 object cursor + * @v type Expected type + * @ret len Length of object body, or negative error + * + * The object cursor will be updated to point to the start of the + * object body (i.e. the first byte following the length byte(s)), and + * the length of the object body (i.e. the number of bytes until the + * following object tag, if any) is returned. + * + * If any error occurs (i.e. if the object is not of the expected + * type, or if we overflow beyond the end of the ASN.1 object), then + * the cursor will be invalidated and a negative value will be + * returned. + */ +static int asn1_start ( struct asn1_cursor *cursor, + unsigned int type ) { + unsigned int len_len; + unsigned int len; + int rc; + + /* Sanity check */ + if ( cursor->len < 2 /* Tag byte and first length byte */ ) { + if ( cursor->len ) + DBGC ( cursor, "ASN1 %p too short\n", cursor ); + rc = -EINVAL; + goto notfound; + } + + /* Check the tag byte */ + if ( *( ( uint8_t * ) cursor->data ) != type ) { + DBGC ( cursor, "ASN1 %p type mismatch (expected %d, got %d)\n", + cursor, type, *( ( uint8_t * ) cursor->data ) ); + rc = -ENXIO; + goto notfound; + } + cursor->data++; + cursor->len--; + + /* Extract length of the length field and sanity check */ + len_len = *( ( uint8_t * ) cursor->data ); + if ( len_len & 0x80 ) { + len_len = ( len_len & 0x7f ); + cursor->data++; + cursor->len--; + } else { + len_len = 1; + } + if ( cursor->len < len_len ) { + DBGC ( cursor, "ASN1 %p bad length field length %d (max " + "%zd)\n", cursor, len_len, cursor->len ); + rc = -EINVAL; + goto notfound; + } + + /* Extract the length and sanity check */ + for ( len = 0 ; len_len ; len_len-- ) { + len <<= 8; + len |= *( ( uint8_t * ) cursor->data ); + cursor->data++; + cursor->len--; + } + if ( cursor->len < len ) { + DBGC ( cursor, "ASN1 %p bad length %d (max %zd)\n", + cursor, len, cursor->len ); + rc = -EINVAL; + goto notfound; + } + + return len; + + notfound: + cursor->data = NULL; + cursor->len = 0; + return rc; +} + +/** + * Enter ASN.1 object + * + * @v cursor ASN.1 object cursor + * @v type Expected type + * @ret rc Return status code + * + * The object cursor will be updated to point to the body of the + * current ASN.1 object. If any error occurs, the object cursor will + * be invalidated. + */ +int asn1_enter ( struct asn1_cursor *cursor, unsigned int type ) { + int len; + + len = asn1_start ( cursor, type ); + if ( len < 0 ) + return len; + + cursor->len = len; + DBGC ( cursor, "ASN1 %p entered object type %02x (len %x)\n", + cursor, type, len ); + + return 0; +} + +/** + * Skip ASN.1 object + * + * @v cursor ASN.1 object cursor + * @v type Expected type + * @ret rc Return status code + * + * The object cursor will be updated to point to the next ASN.1 + * object. If any error occurs, the object cursor will be + * invalidated. + */ +int asn1_skip ( struct asn1_cursor *cursor, unsigned int type ) { + int len; + + len = asn1_start ( cursor, type ); + if ( len < 0 ) + return len; + + cursor->data += len; + cursor->len -= len; + DBGC ( cursor, "ASN1 %p skipped object type %02x (len %x)\n", + cursor, type, len ); + + if ( ! cursor->len ) { + DBGC ( cursor, "ASN1 %p reached end of object\n", cursor ); + cursor->data = NULL; + return -ENOENT; + } + + return 0; +} diff --git a/gpxe/src/crypto/axtls/aes.c b/gpxe/src/crypto/axtls/aes.c new file mode 100644 index 0000000..0c0d724 --- /dev/null +++ b/gpxe/src/crypto/axtls/aes.c @@ -0,0 +1,476 @@ +/* + * Copyright(C) 2006 Cameron Rich + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * AES implementation - this is a small code version. There are much faster + * versions around but they are much larger in size (i.e. they use large + * submix tables). + */ + +#include +#include "crypto.h" + +/* all commented out in skeleton mode */ +#ifndef CONFIG_SSL_SKELETON_MODE + +#define rot1(x) (((x) << 24) | ((x) >> 8)) +#define rot2(x) (((x) << 16) | ((x) >> 16)) +#define rot3(x) (((x) << 8) | ((x) >> 24)) + +/* + * This cute trick does 4 'mul by two' at once. Stolen from + * Dr B. R. Gladman but I'm sure the u-(u>>7) is + * a standard graphics trick + * The key to this is that we need to xor with 0x1b if the top bit is set. + * a 1xxx xxxx 0xxx 0xxx First we mask the 7bit, + * b 1000 0000 0000 0000 then we shift right by 7 putting the 7bit in 0bit, + * c 0000 0001 0000 0000 we then subtract (c) from (b) + * d 0111 1111 0000 0000 and now we and with our mask + * e 0001 1011 0000 0000 + */ +#define mt 0x80808080 +#define ml 0x7f7f7f7f +#define mh 0xfefefefe +#define mm 0x1b1b1b1b +#define mul2(x,t) ((t)=((x)&mt), \ + ((((x)+(x))&mh)^(((t)-((t)>>7))&mm))) + +#define inv_mix_col(x,f2,f4,f8,f9) (\ + (f2)=mul2(x,f2), \ + (f4)=mul2(f2,f4), \ + (f8)=mul2(f4,f8), \ + (f9)=(x)^(f8), \ + (f8)=((f2)^(f4)^(f8)), \ + (f2)^=(f9), \ + (f4)^=(f9), \ + (f8)^=rot3(f2), \ + (f8)^=rot2(f4), \ + (f8)^rot1(f9)) + +/* some macros to do endian independent byte extraction */ +#define n2l(c,l) l=ntohl(*c); c++ +#define l2n(l,c) *c++=htonl(l) + +/* + * AES S-box + */ +static const uint8_t aes_sbox[256] = +{ + 0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5, + 0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76, + 0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0, + 0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0, + 0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC, + 0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15, + 0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A, + 0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75, + 0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0, + 0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84, + 0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B, + 0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF, + 0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85, + 0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8, + 0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5, + 0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2, + 0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17, + 0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73, + 0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88, + 0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB, + 0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C, + 0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79, + 0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9, + 0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08, + 0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6, + 0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A, + 0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E, + 0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E, + 0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94, + 0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF, + 0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68, + 0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16, +}; + +/* + * AES is-box + */ +static const uint8_t aes_isbox[256] = +{ + 0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38, + 0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb, + 0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87, + 0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb, + 0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d, + 0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e, + 0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2, + 0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25, + 0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16, + 0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92, + 0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda, + 0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84, + 0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a, + 0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06, + 0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02, + 0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b, + 0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea, + 0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73, + 0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85, + 0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e, + 0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89, + 0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b, + 0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20, + 0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4, + 0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31, + 0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f, + 0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d, + 0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef, + 0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0, + 0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61, + 0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26, + 0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d +}; + +static const unsigned char Rcon[30]= +{ + 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80, + 0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f, + 0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4, + 0xb3,0x7d,0xfa,0xef,0xc5,0x91, +}; + +/* Perform doubling in Galois Field GF(2^8) using the irreducible polynomial + x^8+x^4+x^3+x+1 */ +static unsigned char AES_xtime(uint32_t x) +{ + return x = (x&0x80) ? (x<<1)^0x1b : x<<1; +} + +/** + * Set up AES with the key/iv and cipher size. + */ +void AES_set_key(AES_CTX *ctx, const uint8_t *key, + const uint8_t *iv, AES_MODE mode) +{ + int i, ii; + uint32_t *W, tmp, tmp2; + const unsigned char *ip; + int words; + + switch (mode) + { + case AES_MODE_128: + i = 10; + words = 4; + break; + + case AES_MODE_256: + i = 14; + words = 8; + break; + + default: /* fail silently */ + return; + } + + ctx->rounds = i; + ctx->key_size = words; + W = ctx->ks; + for (i = 0; i < words; i+=2) + { + W[i+0]= ((uint32_t)key[ 0]<<24)| + ((uint32_t)key[ 1]<<16)| + ((uint32_t)key[ 2]<< 8)| + ((uint32_t)key[ 3] ); + W[i+1]= ((uint32_t)key[ 4]<<24)| + ((uint32_t)key[ 5]<<16)| + ((uint32_t)key[ 6]<< 8)| + ((uint32_t)key[ 7] ); + key += 8; + } + + ip = Rcon; + ii = 4 * (ctx->rounds+1); + for (i = words; i> 8)&0xff]<<16; + tmp2|=(uint32_t)aes_sbox[(tmp>>16)&0xff]<<24; + tmp2|=(uint32_t)aes_sbox[(tmp>>24) ]; + tmp=tmp2^(((unsigned int)*ip)<<24); + ip++; + } + + if ((words == 8) && ((i % words) == 4)) + { + tmp2 =(uint32_t)aes_sbox[(tmp )&0xff] ; + tmp2|=(uint32_t)aes_sbox[(tmp>> 8)&0xff]<< 8; + tmp2|=(uint32_t)aes_sbox[(tmp>>16)&0xff]<<16; + tmp2|=(uint32_t)aes_sbox[(tmp>>24) ]<<24; + tmp=tmp2; + } + + W[i]=W[i-words]^tmp; + } + + /* copy the iv across */ + memcpy(ctx->iv, iv, 16); +} + +/** + * Change a key for decryption. + */ +void AES_convert_key(AES_CTX *ctx) +{ + int i; + uint32_t *k,w,t1,t2,t3,t4; + + k = ctx->ks; + k += 4; + + for (i=ctx->rounds*4; i>4; i--) + { + w= *k; + w = inv_mix_col(w,t1,t2,t3,t4); + *k++ =w; + } +} + +#if 0 +/** + * Encrypt a byte sequence (with a block size 16) using the AES cipher. + */ +void AES_cbc_encrypt(AES_CTX *ctx, const uint8_t *msg, uint8_t *out, int length) +{ + uint32_t tin0, tin1, tin2, tin3; + uint32_t tout0, tout1, tout2, tout3; + uint32_t tin[4]; + uint32_t *iv = (uint32_t *)ctx->iv; + uint32_t *msg_32 = (uint32_t *)msg; + uint32_t *out_32 = (uint32_t *)out; + + n2l(iv, tout0); + n2l(iv, tout1); + n2l(iv, tout2); + n2l(iv, tout3); + iv -= 4; + + for (length -= 16; length >= 0; length -= 16) + { + n2l(msg_32, tin0); + n2l(msg_32, tin1); + n2l(msg_32, tin2); + n2l(msg_32, tin3); + tin[0] = tin0^tout0; + tin[1] = tin1^tout1; + tin[2] = tin2^tout2; + tin[3] = tin3^tout3; + + AES_encrypt(ctx, tin); + + tout0 = tin[0]; + l2n(tout0, out_32); + tout1 = tin[1]; + l2n(tout1, out_32); + tout2 = tin[2]; + l2n(tout2, out_32); + tout3 = tin[3]; + l2n(tout3, out_32); + } + + l2n(tout0, iv); + l2n(tout1, iv); + l2n(tout2, iv); + l2n(tout3, iv); +} + +/** + * Decrypt a byte sequence (with a block size 16) using the AES cipher. + */ +void AES_cbc_decrypt(AES_CTX *ctx, const uint8_t *msg, uint8_t *out, int length) +{ + uint32_t tin0, tin1, tin2, tin3; + uint32_t xor0,xor1,xor2,xor3; + uint32_t tout0,tout1,tout2,tout3; + uint32_t data[4]; + uint32_t *iv = (uint32_t *)ctx->iv; + uint32_t *msg_32 = (uint32_t *)msg; + uint32_t *out_32 = (uint32_t *)out; + + n2l(iv ,xor0); + n2l(iv, xor1); + n2l(iv, xor2); + n2l(iv, xor3); + iv -= 4; + + for (length-=16; length >= 0; length -= 16) + { + n2l(msg_32, tin0); + n2l(msg_32, tin1); + n2l(msg_32, tin2); + n2l(msg_32, tin3); + + data[0] = tin0; + data[1] = tin1; + data[2] = tin2; + data[3] = tin3; + + AES_decrypt(ctx, data); + + tout0 = data[0]^xor0; + tout1 = data[1]^xor1; + tout2 = data[2]^xor2; + tout3 = data[3]^xor3; + + xor0 = tin0; + xor1 = tin1; + xor2 = tin2; + xor3 = tin3; + + l2n(tout0, out_32); + l2n(tout1, out_32); + l2n(tout2, out_32); + l2n(tout3, out_32); + } + + l2n(xor0, iv); + l2n(xor1, iv); + l2n(xor2, iv); + l2n(xor3, iv); +} +#endif + +/** + * Encrypt a single block (16 bytes) of data + */ +void AES_encrypt(const AES_CTX *ctx, uint32_t *data) +{ + /* To make this code smaller, generate the sbox entries on the fly. + * This will have a really heavy effect upon performance. + */ + uint32_t tmp[4]; + uint32_t tmp1, old_a0, a0, a1, a2, a3, row; + int curr_rnd; + int rounds = ctx->rounds; + const uint32_t *k = ctx->ks; + + /* Pre-round key addition */ + for (row = 0; row < 4; row++) + { + data[row] ^= *(k++); + } + + /* Encrypt one block. */ + for (curr_rnd = 0; curr_rnd < rounds; curr_rnd++) + { + /* Perform ByteSub and ShiftRow operations together */ + for (row = 0; row < 4; row++) + { + a0 = (uint32_t)aes_sbox[(data[row%4]>>24)&0xFF]; + a1 = (uint32_t)aes_sbox[(data[(row+1)%4]>>16)&0xFF]; + a2 = (uint32_t)aes_sbox[(data[(row+2)%4]>>8)&0xFF]; + a3 = (uint32_t)aes_sbox[(data[(row+3)%4])&0xFF]; + + /* Perform MixColumn iff not last round */ + if (curr_rnd < (rounds - 1)) + { + tmp1 = a0 ^ a1 ^ a2 ^ a3; + old_a0 = a0; + + a0 ^= tmp1 ^ AES_xtime(a0 ^ a1); + a1 ^= tmp1 ^ AES_xtime(a1 ^ a2); + a2 ^= tmp1 ^ AES_xtime(a2 ^ a3); + a3 ^= tmp1 ^ AES_xtime(a3 ^ old_a0); + + } + + tmp[row] = ((a0 << 24) | (a1 << 16) | (a2 << 8) | a3); + } + + /* KeyAddition - note that it is vital that this loop is separate from + the MixColumn operation, which must be atomic...*/ + for (row = 0; row < 4; row++) + { + data[row] = tmp[row] ^ *(k++); + } + } +} + +/** + * Decrypt a single block (16 bytes) of data + */ +void AES_decrypt(const AES_CTX *ctx, uint32_t *data) +{ + uint32_t tmp[4]; + uint32_t xt0,xt1,xt2,xt3,xt4,xt5,xt6; + uint32_t a0, a1, a2, a3, row; + int curr_rnd; + int rounds = ctx->rounds; + uint32_t *k = (uint32_t*)ctx->ks + ((rounds+1)*4); + + /* pre-round key addition */ + for (row=4; row > 0;row--) + { + data[row-1] ^= *(--k); + } + + /* Decrypt one block */ + for (curr_rnd=0; curr_rnd < rounds; curr_rnd++) + { + /* Perform ByteSub and ShiftRow operations together */ + for (row = 4; row > 0; row--) + { + a0 = aes_isbox[(data[(row+3)%4]>>24)&0xFF]; + a1 = aes_isbox[(data[(row+2)%4]>>16)&0xFF]; + a2 = aes_isbox[(data[(row+1)%4]>>8)&0xFF]; + a3 = aes_isbox[(data[row%4])&0xFF]; + + /* Perform MixColumn iff not last round */ + if (curr_rnd<(rounds-1)) + { + /* The MDS cofefficients (0x09, 0x0B, 0x0D, 0x0E) + are quite large compared to encryption; this + operation slows decryption down noticeably. */ + xt0 = AES_xtime(a0^a1); + xt1 = AES_xtime(a1^a2); + xt2 = AES_xtime(a2^a3); + xt3 = AES_xtime(a3^a0); + xt4 = AES_xtime(xt0^xt1); + xt5 = AES_xtime(xt1^xt2); + xt6 = AES_xtime(xt4^xt5); + + xt0 ^= a1^a2^a3^xt4^xt6; + xt1 ^= a0^a2^a3^xt5^xt6; + xt2 ^= a0^a1^a3^xt4^xt6; + xt3 ^= a0^a1^a2^xt5^xt6; + tmp[row-1] = ((xt0<<24)|(xt1<<16)|(xt2<<8)|xt3); + } + else + tmp[row-1] = ((a0<<24)|(a1<<16)|(a2<<8)|a3); + } + + for (row = 4; row > 0; row--) + { + data[row-1] = tmp[row-1] ^ *(--k); + } + } +} + +#endif diff --git a/gpxe/src/crypto/axtls/bigint.c b/gpxe/src/crypto/axtls/bigint.c new file mode 100644 index 0000000..49cad97 --- /dev/null +++ b/gpxe/src/crypto/axtls/bigint.c @@ -0,0 +1,1496 @@ +/* + * Copyright(C) 2006 Cameron Rich + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * @defgroup bigint_api Big Integer API + * @brief The bigint implementation as used by the axTLS project. + * + * The bigint library is for RSA encryption/decryption as well as signing. + * This code tries to minimise use of malloc/free by maintaining a small + * cache. A bigint context may maintain state by being made "permanent". + * It be be later released with a bi_depermanent() and bi_free() call. + * + * It supports the following reduction techniques: + * - Classical + * - Barrett + * - Montgomery + * + * It also implements the following: + * - Karatsuba multiplication + * - Squaring + * - Sliding window exponentiation + * - Chinese Remainder Theorem (implemented in rsa.c). + * + * All the algorithms used are pretty standard, and designed for different + * data bus sizes. Negative numbers are not dealt with at all, so a subtraction + * may need to be tested for negativity. + * + * This library steals some ideas from Jef Poskanzer + * + * and GMP . It gets most of its implementation + * detail from "The Handbook of Applied Cryptography" + * + * @{ + */ + +#include +#include +#include +#include +#include +#include "bigint.h" +#include "crypto.h" + +static bigint *bi_int_multiply(BI_CTX *ctx, bigint *bi, comp i); +static bigint *bi_int_divide(BI_CTX *ctx, bigint *biR, comp denom); +static bigint __malloc *alloc(BI_CTX *ctx, int size); +static bigint *trim(bigint *bi); +static void more_comps(bigint *bi, int n); +#if defined(CONFIG_BIGINT_KARATSUBA) || defined(CONFIG_BIGINT_BARRETT) || \ + defined(CONFIG_BIGINT_MONTGOMERY) +static bigint *comp_right_shift(bigint *biR, int num_shifts); +static bigint *comp_left_shift(bigint *biR, int num_shifts); +#endif + +#ifdef CONFIG_BIGINT_CHECK_ON +static void check(const bigint *bi); +#endif + +/** + * @brief Start a new bigint context. + * @return A bigint context. + */ +BI_CTX *bi_initialize(void) +{ + /* calloc() sets everything to zero */ + BI_CTX *ctx = (BI_CTX *)calloc(1, sizeof(BI_CTX)); + + /* the radix */ + ctx->bi_radix = alloc(ctx, 2); + ctx->bi_radix->comps[0] = 0; + ctx->bi_radix->comps[1] = 1; + bi_permanent(ctx->bi_radix); + return ctx; +} + +/** + * @brief Close the bigint context and free any resources. + * + * Free up any used memory - a check is done if all objects were not + * properly freed. + * @param ctx [in] The bigint session context. + */ +void bi_terminate(BI_CTX *ctx) +{ + bigint *p, *pn; + + bi_depermanent(ctx->bi_radix); + bi_free(ctx, ctx->bi_radix); + + if (ctx->active_count != 0) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("bi_terminate: there were %d un-freed bigints\n", + ctx->active_count); +#endif + abort(); + } + + for (p = ctx->free_list; p != NULL; p = pn) + { + pn = p->next; + free(p->comps); + free(p); + } + + free(ctx); +} + +/** + * @brief Increment the number of references to this object. + * It does not do a full copy. + * @param bi [in] The bigint to copy. + * @return A reference to the same bigint. + */ +bigint *bi_copy(bigint *bi) +{ + check(bi); + if (bi->refs != PERMANENT) + bi->refs++; + return bi; +} + +/** + * @brief Simply make a bigint object "unfreeable" if bi_free() is called on it. + * + * For this object to be freed, bi_depermanent() must be called. + * @param bi [in] The bigint to be made permanent. + */ +void bi_permanent(bigint *bi) +{ + check(bi); + if (bi->refs != 1) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("bi_permanent: refs was not 1\n"); +#endif + abort(); + } + + bi->refs = PERMANENT; +} + +/** + * @brief Take a permanent object and make it eligible for freedom. + * @param bi [in] The bigint to be made back to temporary. + */ +void bi_depermanent(bigint *bi) +{ + check(bi); + if (bi->refs != PERMANENT) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("bi_depermanent: bigint was not permanent\n"); +#endif + abort(); + } + + bi->refs = 1; +} + +/** + * @brief Free a bigint object so it can be used again. + * + * The memory itself it not actually freed, just tagged as being available + * @param ctx [in] The bigint session context. + * @param bi [in] The bigint to be freed. + */ +void bi_free(BI_CTX *ctx, bigint *bi) +{ + check(bi); + if (bi->refs == PERMANENT) + { + return; + } + + if (--bi->refs > 0) + { + return; + } + + bi->next = ctx->free_list; + ctx->free_list = bi; + ctx->free_count++; + + if (--ctx->active_count < 0) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("bi_free: active_count went negative " + "- double-freed bigint?\n"); +#endif + abort(); + } +} + +/** + * @brief Convert an (unsigned) integer into a bigint. + * @param ctx [in] The bigint session context. + * @param i [in] The (unsigned) integer to be converted. + * + */ +bigint *int_to_bi(BI_CTX *ctx, comp i) +{ + bigint *biR = alloc(ctx, 1); + biR->comps[0] = i; + return biR; +} + +/** + * @brief Do a full copy of the bigint object. + * @param ctx [in] The bigint session context. + * @param bi [in] The bigint object to be copied. + */ +bigint *bi_clone(BI_CTX *ctx, const bigint *bi) +{ + bigint *biR = alloc(ctx, bi->size); + check(bi); + memcpy(biR->comps, bi->comps, bi->size*COMP_BYTE_SIZE); + return biR; +} + +/** + * @brief Perform an addition operation between two bigints. + * @param ctx [in] The bigint session context. + * @param bia [in] A bigint. + * @param bib [in] Another bigint. + * @return The result of the addition. + */ +bigint *bi_add(BI_CTX *ctx, bigint *bia, bigint *bib) +{ + int n; + comp carry = 0; + comp *pa, *pb; + + check(bia); + check(bib); + + n = max(bia->size, bib->size); + more_comps(bia, n+1); + more_comps(bib, n); + pa = bia->comps; + pb = bib->comps; + + do + { + comp sl, rl, cy1; + sl = *pa + *pb++; + rl = sl + carry; + cy1 = sl < *pa; + carry = cy1 | (rl < sl); + *pa++ = rl; + } while (--n != 0); + + *pa = carry; /* do overflow */ + bi_free(ctx, bib); + return trim(bia); +} + +/** + * @brief Perform a subtraction operation between two bigints. + * @param ctx [in] The bigint session context. + * @param bia [in] A bigint. + * @param bib [in] Another bigint. + * @param is_negative [out] If defined, indicates that the result was negative. + * is_negative may be null. + * @return The result of the subtraction. The result is always positive. + */ +bigint *bi_subtract(BI_CTX *ctx, + bigint *bia, bigint *bib, int *is_negative) +{ + int n = bia->size; + comp *pa, *pb, carry = 0; + + check(bia); + check(bib); + + more_comps(bib, n); + pa = bia->comps; + pb = bib->comps; + + do + { + comp sl, rl, cy1; + sl = *pa - *pb++; + rl = sl - carry; + cy1 = sl > *pa; + carry = cy1 | (rl > sl); + *pa++ = rl; + } while (--n != 0); + + if (is_negative) /* indicate a negative result */ + { + *is_negative = carry; + } + + bi_free(ctx, trim(bib)); /* put bib back to the way it was */ + return trim(bia); +} + +/** + * Perform a multiply between a bigint an an (unsigned) integer + */ +static bigint *bi_int_multiply(BI_CTX *ctx, bigint *bia, comp b) +{ + int j = 0, n = bia->size; + bigint *biR = alloc(ctx, n + 1); + comp carry = 0; + comp *r = biR->comps; + comp *a = bia->comps; + + check(bia); + + /* clear things to start with */ + memset(r, 0, ((n+1)*COMP_BYTE_SIZE)); + + do + { + long_comp tmp = *r + (long_comp)a[j]*b + carry; + *r++ = (comp)tmp; /* downsize */ + carry = (comp)(tmp >> COMP_BIT_SIZE); + } while (++j < n); + + *r = carry; + bi_free(ctx, bia); + return trim(biR); +} + +/** + * @brief Does both division and modulo calculations. + * + * Used extensively when doing classical reduction. + * @param ctx [in] The bigint session context. + * @param u [in] A bigint which is the numerator. + * @param v [in] Either the denominator or the modulus depending on the mode. + * @param is_mod [n] Determines if this is a normal division (0) or a reduction + * (1). + * @return The result of the division/reduction. + */ +bigint *bi_divide(BI_CTX *ctx, bigint *u, bigint *v, int is_mod) +{ + int n = v->size, m = u->size-n; + int j = 0, orig_u_size = u->size; + uint8_t mod_offset = ctx->mod_offset; + comp d; + bigint *quotient, *tmp_u; + comp q_dash; + + check(u); + check(v); + + /* if doing reduction and we are < mod, then return mod */ + if (is_mod && bi_compare(v, u) > 0) + { + bi_free(ctx, v); + return u; + } + + quotient = alloc(ctx, m+1); + tmp_u = alloc(ctx, n+1); + v = trim(v); /* make sure we have no leading 0's */ + d = (comp)((long_comp)COMP_RADIX/(V1+1)); + + /* clear things to start with */ + memset(quotient->comps, 0, ((quotient->size)*COMP_BYTE_SIZE)); + + /* normalise */ + if (d > 1) + { + u = bi_int_multiply(ctx, u, d); + + if (is_mod) + { + v = ctx->bi_normalised_mod[mod_offset]; + } + else + { + v = bi_int_multiply(ctx, v, d); + } + } + + if (orig_u_size == u->size) /* new digit position u0 */ + { + more_comps(u, orig_u_size + 1); + } + + do + { + /* get a temporary short version of u */ + memcpy(tmp_u->comps, &u->comps[u->size-n-1-j], (n+1)*COMP_BYTE_SIZE); + + /* calculate q' */ + if (U(0) == V1) + { + q_dash = COMP_RADIX-1; + } + else + { + q_dash = (comp)(((long_comp)U(0)*COMP_RADIX + U(1))/V1); + } + + if (v->size > 1 && V2) + { + /* we are implementing the following: + if (V2*q_dash > (((U(0)*COMP_RADIX + U(1) - + q_dash*V1)*COMP_RADIX) + U(2))) ... */ + comp inner = (comp)((long_comp)COMP_RADIX*U(0) + U(1) - + (long_comp)q_dash*V1); + if ((long_comp)V2*q_dash > (long_comp)inner*COMP_RADIX + U(2)) + { + q_dash--; + } + } + + /* multiply and subtract */ + if (q_dash) + { + int is_negative; + tmp_u = bi_subtract(ctx, tmp_u, + bi_int_multiply(ctx, bi_copy(v), q_dash), &is_negative); + more_comps(tmp_u, n+1); + + Q(j) = q_dash; + + /* add back */ + if (is_negative) + { + Q(j)--; + tmp_u = bi_add(ctx, tmp_u, bi_copy(v)); + + /* lop off the carry */ + tmp_u->size--; + v->size--; + } + } + else + { + Q(j) = 0; + } + + /* copy back to u */ + memcpy(&u->comps[u->size-n-1-j], tmp_u->comps, (n+1)*COMP_BYTE_SIZE); + } while (++j <= m); + + bi_free(ctx, tmp_u); + bi_free(ctx, v); + + if (is_mod) /* get the remainder */ + { + bi_free(ctx, quotient); + return bi_int_divide(ctx, trim(u), d); + } + else /* get the quotient */ + { + bi_free(ctx, u); + return trim(quotient); + } +} + +/* + * Perform an integer divide on a bigint. + */ +static bigint *bi_int_divide(BI_CTX *ctx __unused, bigint *biR, comp denom) +{ + int i = biR->size - 1; + long_comp r = 0; + + check(biR); + + do + { + r = (r<comps[i]; + biR->comps[i] = (comp)(r / denom); + r %= denom; + } while (--i != 0); + + return trim(biR); +} + +#ifdef CONFIG_BIGINT_MONTGOMERY +/** + * There is a need for the value of integer N' such that B^-1(B-1)-N^-1N'=1, + * where B^-1(B-1) mod N=1. Actually, only the least significant part of + * N' is needed, hence the definition N0'=N' mod b. We reproduce below the + * simple algorithm from an article by Dusse and Kaliski to efficiently + * find N0' from N0 and b */ +static comp modular_inverse(bigint *bim) +{ + int i; + comp t = 1; + comp two_2_i_minus_1 = 2; /* 2^(i-1) */ + long_comp two_2_i = 4; /* 2^i */ + comp N = bim->comps[0]; + + for (i = 2; i <= COMP_BIT_SIZE; i++) + { + if ((long_comp)N*t%two_2_i >= two_2_i_minus_1) + { + t += two_2_i_minus_1; + } + + two_2_i_minus_1 <<= 1; + two_2_i <<= 1; + } + + return (comp)(COMP_RADIX-t); +} +#endif + +#if defined(CONFIG_BIGINT_KARATSUBA) || defined(CONFIG_BIGINT_BARRETT) || \ + defined(CONFIG_BIGINT_MONTGOMERY) +/** + * Take each component and shift down (in terms of components) + */ +static bigint *comp_right_shift(bigint *biR, int num_shifts) +{ + int i = biR->size-num_shifts; + comp *x = biR->comps; + comp *y = &biR->comps[num_shifts]; + + check(biR); + + if (i <= 0) /* have we completely right shifted? */ + { + biR->comps[0] = 0; /* return 0 */ + biR->size = 1; + return biR; + } + + do + { + *x++ = *y++; + } while (--i > 0); + + biR->size -= num_shifts; + return biR; +} + +/** + * Take each component and shift it up (in terms of components) + */ +static bigint *comp_left_shift(bigint *biR, int num_shifts) +{ + int i = biR->size-1; + comp *x, *y; + + check(biR); + + if (num_shifts <= 0) + { + return biR; + } + + more_comps(biR, biR->size + num_shifts); + + x = &biR->comps[i+num_shifts]; + y = &biR->comps[i]; + + do + { + *x-- = *y--; + } while (i--); + + memset(biR->comps, 0, num_shifts*COMP_BYTE_SIZE); /* zero LS comps */ + return biR; +} +#endif + +/** + * @brief Allow a binary sequence to be imported as a bigint. + * @param ctx [in] The bigint session context. + * @param data [in] The data to be converted. + * @param size [in] The number of bytes of data. + * @return A bigint representing this data. + */ +bigint *bi_import(BI_CTX *ctx, const uint8_t *data, int size) +{ + bigint *biR = alloc(ctx, (size+COMP_BYTE_SIZE-1)/COMP_BYTE_SIZE); + int i, j = 0, offset = 0; + + memset(biR->comps, 0, biR->size*COMP_BYTE_SIZE); + + for (i = size-1; i >= 0; i--) + { + biR->comps[offset] += data[i] << (j*8); + + if (++j == COMP_BYTE_SIZE) + { + j = 0; + offset ++; + } + } + + return trim(biR); +} + +#ifdef CONFIG_SSL_FULL_MODE +/** + * @brief The testharness uses this code to import text hex-streams and + * convert them into bigints. + * @param ctx [in] The bigint session context. + * @param data [in] A string consisting of hex characters. The characters must + * be in upper case. + * @return A bigint representing this data. + */ +bigint *bi_str_import(BI_CTX *ctx, const char *data) +{ + int size = strlen(data); + bigint *biR = alloc(ctx, (size+COMP_NUM_NIBBLES-1)/COMP_NUM_NIBBLES); + int i, j = 0, offset = 0; + memset(biR->comps, 0, biR->size*COMP_BYTE_SIZE); + + for (i = size-1; i >= 0; i--) + { + int num = (data[i] <= '9') ? (data[i] - '0') : (data[i] - 'A' + 10); + biR->comps[offset] += num << (j*4); + + if (++j == COMP_NUM_NIBBLES) + { + j = 0; + offset ++; + } + } + + return biR; +} + +void bi_print(const char *label, bigint *x) +{ + int i, j; + + if (x == NULL) + { + printf("%s: (null)\n", label); + return; + } + + printf("%s: (size %d)\n", label, x->size); + for (i = x->size-1; i >= 0; i--) + { + for (j = COMP_NUM_NIBBLES-1; j >= 0; j--) + { + comp mask = 0x0f << (j*4); + comp num = (x->comps[i] & mask) >> (j*4); + putc((num <= 9) ? (num + '0') : (num + 'A' - 10), stdout); + } + } + + printf("\n"); +} +#endif + +/** + * @brief Take a bigint and convert it into a byte sequence. + * + * This is useful after a decrypt operation. + * @param ctx [in] The bigint session context. + * @param x [in] The bigint to be converted. + * @param data [out] The converted data as a byte stream. + * @param size [in] The maximum size of the byte stream. Unused bytes will be + * zeroed. + */ +void bi_export(BI_CTX *ctx, bigint *x, uint8_t *data, int size) +{ + int i, j, k = size-1; + + check(x); + memset(data, 0, size); /* ensure all leading 0's are cleared */ + + for (i = 0; i < x->size; i++) + { + for (j = 0; j < COMP_BYTE_SIZE; j++) + { + comp mask = 0xff << (j*8); + int num = (x->comps[i] & mask) >> (j*8); + data[k--] = num; + + if (k < 0) + { + break; + } + } + } + + bi_free(ctx, x); +} + +/** + * @brief Pre-calculate some of the expensive steps in reduction. + * + * This function should only be called once (normally when a session starts). + * When the session is over, bi_free_mod() should be called. bi_mod_power() + * relies on this function being called. + * @param ctx [in] The bigint session context. + * @param bim [in] The bigint modulus that will be used. + * @param mod_offset [in] There are three moduluii that can be stored - the + * standard modulus, and its two primes p and q. This offset refers to which + * modulus we are referring to. + * @see bi_free_mod(), bi_mod_power(). + */ +void bi_set_mod(BI_CTX *ctx, bigint *bim, int mod_offset) +{ + int k = bim->size; + comp d = (comp)((long_comp)COMP_RADIX/(bim->comps[k-1]+1)); +#ifdef CONFIG_BIGINT_MONTGOMERY + bigint *R, *R2; +#endif + + ctx->bi_mod[mod_offset] = bim; + bi_permanent(ctx->bi_mod[mod_offset]); + ctx->bi_normalised_mod[mod_offset] = bi_int_multiply(ctx, bim, d); + bi_permanent(ctx->bi_normalised_mod[mod_offset]); + +#if defined(CONFIG_BIGINT_MONTGOMERY) + /* set montgomery variables */ + R = comp_left_shift(bi_clone(ctx, ctx->bi_radix), k-1); /* R */ + R2 = comp_left_shift(bi_clone(ctx, ctx->bi_radix), k*2-1); /* R^2 */ + ctx->bi_RR_mod_m[mod_offset] = bi_mod(ctx, R2); /* R^2 mod m */ + ctx->bi_R_mod_m[mod_offset] = bi_mod(ctx, R); /* R mod m */ + + bi_permanent(ctx->bi_RR_mod_m[mod_offset]); + bi_permanent(ctx->bi_R_mod_m[mod_offset]); + + ctx->N0_dash[mod_offset] = modular_inverse(ctx->bi_mod[mod_offset]); + +#elif defined (CONFIG_BIGINT_BARRETT) + ctx->bi_mu[mod_offset] = + bi_divide(ctx, comp_left_shift( + bi_clone(ctx, ctx->bi_radix), k*2-1), ctx->bi_mod[mod_offset], 0); + bi_permanent(ctx->bi_mu[mod_offset]); +#endif +} + +/** + * @brief Used when cleaning various bigints at the end of a session. + * @param ctx [in] The bigint session context. + * @param mod_offset [in] The offset to use. + * @see bi_set_mod(). + */ +void bi_free_mod(BI_CTX *ctx, int mod_offset) +{ + bi_depermanent(ctx->bi_mod[mod_offset]); + bi_free(ctx, ctx->bi_mod[mod_offset]); +#if defined (CONFIG_BIGINT_MONTGOMERY) + bi_depermanent(ctx->bi_RR_mod_m[mod_offset]); + bi_depermanent(ctx->bi_R_mod_m[mod_offset]); + bi_free(ctx, ctx->bi_RR_mod_m[mod_offset]); + bi_free(ctx, ctx->bi_R_mod_m[mod_offset]); +#elif defined(CONFIG_BIGINT_BARRETT) + bi_depermanent(ctx->bi_mu[mod_offset]); + bi_free(ctx, ctx->bi_mu[mod_offset]); +#endif + bi_depermanent(ctx->bi_normalised_mod[mod_offset]); + bi_free(ctx, ctx->bi_normalised_mod[mod_offset]); +} + +/** + * Perform a standard multiplication between two bigints. + */ +static bigint *regular_multiply(BI_CTX *ctx, bigint *bia, bigint *bib) +{ + int i, j, i_plus_j; + int n = bia->size; + int t = bib->size; + bigint *biR = alloc(ctx, n + t); + comp *sr = biR->comps; + comp *sa = bia->comps; + comp *sb = bib->comps; + + check(bia); + check(bib); + + /* clear things to start with */ + memset(biR->comps, 0, ((n+t)*COMP_BYTE_SIZE)); + i = 0; + + do + { + comp carry = 0; + comp b = *sb++; + i_plus_j = i; + j = 0; + + do + { + long_comp tmp = sr[i_plus_j] + (long_comp)sa[j]*b + carry; + sr[i_plus_j++] = (comp)tmp; /* downsize */ + carry = (comp)(tmp >> COMP_BIT_SIZE); + } while (++j < n); + + sr[i_plus_j] = carry; + } while (++i < t); + + bi_free(ctx, bia); + bi_free(ctx, bib); + return trim(biR); +} + +#ifdef CONFIG_BIGINT_KARATSUBA +/* + * Karatsuba improves on regular multiplication due to only 3 multiplications + * being done instead of 4. The additional additions/subtractions are O(N) + * rather than O(N^2) and so for big numbers it saves on a few operations + */ +static bigint *karatsuba(BI_CTX *ctx, bigint *bia, bigint *bib, int is_square) +{ + bigint *x0, *x1; + bigint *p0, *p1, *p2; + int m; + + if (is_square) + { + m = (bia->size + 1)/2; + } + else + { + m = (max(bia->size, bib->size) + 1)/2; + } + + x0 = bi_clone(ctx, bia); + x0->size = m; + x1 = bi_clone(ctx, bia); + comp_right_shift(x1, m); + bi_free(ctx, bia); + + /* work out the 3 partial products */ + if (is_square) + { + p0 = bi_square(ctx, bi_copy(x0)); + p2 = bi_square(ctx, bi_copy(x1)); + p1 = bi_square(ctx, bi_add(ctx, x0, x1)); + } + else /* normal multiply */ + { + bigint *y0, *y1; + y0 = bi_clone(ctx, bib); + y0->size = m; + y1 = bi_clone(ctx, bib); + comp_right_shift(y1, m); + bi_free(ctx, bib); + + p0 = bi_multiply(ctx, bi_copy(x0), bi_copy(y0)); + p2 = bi_multiply(ctx, bi_copy(x1), bi_copy(y1)); + p1 = bi_multiply(ctx, bi_add(ctx, x0, x1), bi_add(ctx, y0, y1)); + } + + p1 = bi_subtract(ctx, + bi_subtract(ctx, p1, bi_copy(p2), NULL), bi_copy(p0), NULL); + + comp_left_shift(p1, m); + comp_left_shift(p2, 2*m); + return bi_add(ctx, p1, bi_add(ctx, p0, p2)); +} +#endif + +/** + * @brief Perform a multiplication operation between two bigints. + * @param ctx [in] The bigint session context. + * @param bia [in] A bigint. + * @param bib [in] Another bigint. + * @return The result of the multiplication. + */ +bigint *bi_multiply(BI_CTX *ctx, bigint *bia, bigint *bib) +{ + check(bia); + check(bib); + +#ifdef CONFIG_BIGINT_KARATSUBA + if (min(bia->size, bib->size) < MUL_KARATSUBA_THRESH) + { + return regular_multiply(ctx, bia, bib); + } + + return karatsuba(ctx, bia, bib, 0); +#else + return regular_multiply(ctx, bia, bib); +#endif +} + +#ifdef CONFIG_BIGINT_SQUARE +/* + * Perform the actual square operion. It takes into account overflow. + */ +static bigint *regular_square(BI_CTX *ctx, bigint *bi) +{ + int t = bi->size; + int i = 0, j; + bigint *biR = alloc(ctx, t*2); + comp *w = biR->comps; + comp *x = bi->comps; + comp carry; + + memset(w, 0, biR->size*COMP_BYTE_SIZE); + + do + { + long_comp tmp = w[2*i] + (long_comp)x[i]*x[i]; + comp u = 0; + w[2*i] = (comp)tmp; + carry = (comp)(tmp >> COMP_BIT_SIZE); + + for (j = i+1; j < t; j++) + { + long_comp xx = (long_comp)x[i]*x[j]; + long_comp blob = (long_comp)w[i+j]+carry; + + if (u) /* previous overflow */ + { + blob += COMP_RADIX; + } + + u = 0; + if (xx & COMP_BIG_MSB) /* check for overflow */ + { + u = 1; + } + + tmp = 2*xx + blob; + w[i+j] = (comp)tmp; + carry = (comp)(tmp >> COMP_BIT_SIZE); + } + + w[i+t] += carry; + + if (u) + { + w[i+t+1] = 1; /* add carry */ + } + } while (++i < t); + + bi_free(ctx, bi); + return trim(biR); +} + +/** + * @brief Perform a square operation on a bigint. + * @param ctx [in] The bigint session context. + * @param bia [in] A bigint. + * @return The result of the multiplication. + */ +bigint *bi_square(BI_CTX *ctx, bigint *bia) +{ + check(bia); + +#ifdef CONFIG_BIGINT_KARATSUBA + if (bia->size < SQU_KARATSUBA_THRESH) + { + return regular_square(ctx, bia); + } + + return karatsuba(ctx, bia, NULL, 1); +#else + return regular_square(ctx, bia); +#endif +} +#endif + +/** + * @brief Compare two bigints. + * @param bia [in] A bigint. + * @param bib [in] Another bigint. + * @return -1 if smaller, 1 if larger and 0 if equal. + */ +int bi_compare(bigint *bia, bigint *bib) +{ + int r, i; + + check(bia); + check(bib); + + if (bia->size > bib->size) + r = 1; + else if (bia->size < bib->size) + r = -1; + else + { + comp *a = bia->comps; + comp *b = bib->comps; + + /* Same number of components. Compare starting from the high end + * and working down. */ + r = 0; + i = bia->size - 1; + + do + { + if (a[i] > b[i]) + { + r = 1; + break; + } + else if (a[i] < b[i]) + { + r = -1; + break; + } + } while (--i >= 0); + } + + return r; +} + +/* + * Allocate and zero more components. Does not consume bi. + */ +static void more_comps(bigint *bi, int n) +{ + if (n > bi->max_comps) + { + bi->max_comps = max(bi->max_comps * 2, n); + bi->comps = (comp*)realloc(bi->comps, bi->max_comps * COMP_BYTE_SIZE); + } + + if (n > bi->size) + { + memset(&bi->comps[bi->size], 0, (n-bi->size)*COMP_BYTE_SIZE); + } + + bi->size = n; +} + +/* + * Make a new empty bigint. It may just use an old one if one is available. + * Otherwise get one off the heap. + */ +static bigint *alloc(BI_CTX *ctx, int size) +{ + bigint *biR; + + /* Can we recycle an old bigint? */ + if (ctx->free_list != NULL) + { + biR = ctx->free_list; + ctx->free_list = biR->next; + ctx->free_count--; + + if (biR->refs != 0) + { +#ifdef CONFIG_SSL_FULL_MODE + printf("alloc: refs was not 0\n"); +#endif + abort(); /* create a stack trace from a core dump */ + } + + more_comps(biR, size); + } + else + { + /* No free bigints available - create a new one. */ + biR = (bigint *)malloc(sizeof(bigint)); + biR->comps = (comp*)malloc(size * COMP_BYTE_SIZE); + biR->max_comps = size; /* give some space to spare */ + } + + biR->size = size; + biR->refs = 1; + biR->next = NULL; + ctx->active_count++; + return biR; +} + +/* + * Work out the highest '1' bit in an exponent. Used when doing sliding-window + * exponentiation. + */ +static int find_max_exp_index(bigint *biexp) +{ + int i = COMP_BIT_SIZE-1; + comp shift = COMP_RADIX/2; + comp test = biexp->comps[biexp->size-1]; /* assume no leading zeroes */ + + check(biexp); + + do + { + if (test & shift) + { + return i+(biexp->size-1)*COMP_BIT_SIZE; + } + + shift >>= 1; + } while (--i != 0); + + return -1; /* error - must have been a leading 0 */ +} + +/* + * Is a particular bit is an exponent 1 or 0? Used when doing sliding-window + * exponentiation. + */ +static int exp_bit_is_one(bigint *biexp, int offset) +{ + comp test = biexp->comps[offset / COMP_BIT_SIZE]; + int num_shifts = offset % COMP_BIT_SIZE; + comp shift = 1; + int i; + + check(biexp); + + for (i = 0; i < num_shifts; i++) + { + shift <<= 1; + } + + return test & shift; +} + +#ifdef CONFIG_BIGINT_CHECK_ON +/* + * Perform a sanity check on bi. + */ +static void check(const bigint *bi) +{ + if (bi->refs <= 0) + { + printf("check: zero or negative refs in bigint\n"); + abort(); + } + + if (bi->next != NULL) + { + printf("check: attempt to use a bigint from " + "the free list\n"); + abort(); + } +} +#endif + +/* + * Delete any leading 0's (and allow for 0). + */ +static bigint *trim(bigint *bi) +{ + check(bi); + + while (bi->comps[bi->size-1] == 0 && bi->size > 1) + { + bi->size--; + } + + return bi; +} + +#if defined(CONFIG_BIGINT_MONTGOMERY) +/** + * @brief Perform a single montgomery reduction. + * @param ctx [in] The bigint session context. + * @param bixy [in] A bigint. + * @return The result of the montgomery reduction. + */ +bigint *bi_mont(BI_CTX *ctx, bigint *bixy) +{ + int i = 0, n; + uint8_t mod_offset = ctx->mod_offset; + bigint *bim = ctx->bi_mod[mod_offset]; + comp mod_inv = ctx->N0_dash[mod_offset]; + + check(bixy); + + if (ctx->use_classical) /* just use classical instead */ + { + return bi_mod(ctx, bixy); + } + + n = bim->size; + + do + { + bixy = bi_add(ctx, bixy, comp_left_shift( + bi_int_multiply(ctx, bim, bixy->comps[i]*mod_inv), i)); + } while (++i < n); + + comp_right_shift(bixy, n); + + if (bi_compare(bixy, bim) >= 0) + { + bixy = bi_subtract(ctx, bixy, bim, NULL); + } + + return bixy; +} + +#elif defined(CONFIG_BIGINT_BARRETT) +/* + * Stomp on the most significant components to give the illusion of a "mod base + * radix" operation + */ +static bigint *comp_mod(bigint *bi, int mod) +{ + check(bi); + + if (bi->size > mod) + { + bi->size = mod; + } + + return bi; +} + +/* + * Barrett reduction has no need for some parts of the product, so ignore bits + * of the multiply. This routine gives Barrett its big performance + * improvements over Classical/Montgomery reduction methods. + */ +static bigint *partial_multiply(BI_CTX *ctx, bigint *bia, bigint *bib, + int inner_partial, int outer_partial) +{ + int i = 0, j, n = bia->size, t = bib->size; + bigint *biR; + comp carry; + comp *sr, *sa, *sb; + + check(bia); + check(bib); + + biR = alloc(ctx, n + t); + sa = bia->comps; + sb = bib->comps; + sr = biR->comps; + + if (inner_partial) + { + memset(sr, 0, inner_partial*COMP_BYTE_SIZE); + } + else /* outer partial */ + { + if (n < outer_partial || t < outer_partial) /* should we bother? */ + { + bi_free(ctx, bia); + bi_free(ctx, bib); + biR->comps[0] = 0; /* return 0 */ + biR->size = 1; + return biR; + } + + memset(&sr[outer_partial], 0, (n+t-outer_partial)*COMP_BYTE_SIZE); + } + + do + { + comp *a = sa; + comp b = *sb++; + long_comp tmp; + int i_plus_j = i; + carry = 0; + j = n; + + if (outer_partial && i_plus_j < outer_partial) + { + i_plus_j = outer_partial; + a = &sa[outer_partial-i]; + j = n-(outer_partial-i); + } + + do + { + if (inner_partial && i_plus_j >= inner_partial) + { + break; + } + + tmp = sr[i_plus_j] + ((long_comp)*a++)*b + carry; + sr[i_plus_j++] = (comp)tmp; /* downsize */ + carry = (comp)(tmp >> COMP_BIT_SIZE); + } while (--j != 0); + + sr[i_plus_j] = carry; + } while (++i < t); + + bi_free(ctx, bia); + bi_free(ctx, bib); + return trim(biR); +} + +/** + * @brief Perform a single Barrett reduction. + * @param ctx [in] The bigint session context. + * @param bi [in] A bigint. + * @return The result of the Barrett reduction. + */ +bigint *bi_barrett(BI_CTX *ctx, bigint *bi) +{ + bigint *q1, *q2, *q3, *r1, *r2, *r; + uint8_t mod_offset = ctx->mod_offset; + bigint *bim = ctx->bi_mod[mod_offset]; + int k = bim->size; + + check(bi); + check(bim); + + /* use Classical method instead - Barrett cannot help here */ + if (bi->size > k*2) + { + return bi_mod(ctx, bi); + } + + q1 = comp_right_shift(bi_clone(ctx, bi), k-1); + + /* do outer partial multiply */ + q2 = partial_multiply(ctx, q1, ctx->bi_mu[mod_offset], 0, k-1); + q3 = comp_right_shift(q2, k+1); + r1 = comp_mod(bi, k+1); + + /* do inner partial multiply */ + r2 = comp_mod(partial_multiply(ctx, q3, bim, k+1, 0), k+1); + r = bi_subtract(ctx, r1, r2, NULL); + + /* if (r >= m) r = r - m; */ + if (bi_compare(r, bim) >= 0) + { + r = bi_subtract(ctx, r, bim, NULL); + } + + return r; +} +#endif /* CONFIG_BIGINT_BARRETT */ + +#ifdef CONFIG_BIGINT_SLIDING_WINDOW +/* + * Work out g1, g3, g5, g7... etc for the sliding-window algorithm + */ +static void precompute_slide_window(BI_CTX *ctx, int window, bigint *g1) +{ + int k = 1, i; + bigint *g2; + + for (i = 0; i < window-1; i++) /* compute 2^(window-1) */ + { + k <<= 1; + } + + ctx->g = (bigint **)malloc(k*sizeof(bigint *)); + ctx->g[0] = bi_clone(ctx, g1); + bi_permanent(ctx->g[0]); + g2 = bi_residue(ctx, bi_square(ctx, ctx->g[0])); /* g^2 */ + + for (i = 1; i < k; i++) + { + ctx->g[i] = bi_residue(ctx, bi_multiply(ctx, ctx->g[i-1], bi_copy(g2))); + bi_permanent(ctx->g[i]); + } + + bi_free(ctx, g2); + ctx->window = k; +} +#endif + +/** + * @brief Perform a modular exponentiation. + * + * This function requires bi_set_mod() to have been called previously. This is + * one of the optimisations used for performance. + * @param ctx [in] The bigint session context. + * @param bi [in] The bigint on which to perform the mod power operation. + * @param biexp [in] The bigint exponent. + * @see bi_set_mod(). + */ +bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp) +{ + int i = find_max_exp_index(biexp), j, window_size = 1; + bigint *biR = int_to_bi(ctx, 1); + +#if defined(CONFIG_BIGINT_MONTGOMERY) + uint8_t mod_offset = ctx->mod_offset; + if (!ctx->use_classical) + { + /* preconvert */ + bi = bi_mont(ctx, + bi_multiply(ctx, bi, ctx->bi_RR_mod_m[mod_offset])); /* x' */ + bi_free(ctx, biR); + biR = ctx->bi_R_mod_m[mod_offset]; /* A */ + } +#endif + + check(bi); + check(biexp); + +#ifdef CONFIG_BIGINT_SLIDING_WINDOW + for (j = i; j > 32; j /= 5) /* work out an optimum size */ + window_size++; + + /* work out the slide constants */ + precompute_slide_window(ctx, window_size, bi); +#else /* just one constant */ + ctx->g = (bigint **)malloc(sizeof(bigint *)); + ctx->g[0] = bi_clone(ctx, bi); + ctx->window = 1; + bi_permanent(ctx->g[0]); +#endif + + /* if sliding-window is off, then only one bit will be done at a time and + * will reduce to standard left-to-right exponentiation */ + do + { + if (exp_bit_is_one(biexp, i)) + { + int l = i-window_size+1; + int part_exp = 0; + + if (l < 0) /* LSB of exponent will always be 1 */ + l = 0; + else + { + while (exp_bit_is_one(biexp, l) == 0) + l++; /* go back up */ + } + + /* build up the section of the exponent */ + for (j = i; j >= l; j--) + { + biR = bi_residue(ctx, bi_square(ctx, biR)); + if (exp_bit_is_one(biexp, j)) + part_exp++; + + if (j != l) + part_exp <<= 1; + } + + part_exp = (part_exp-1)/2; /* adjust for array */ + biR = bi_residue(ctx, bi_multiply(ctx, biR, ctx->g[part_exp])); + i = l-1; + } + else /* square it */ + { + biR = bi_residue(ctx, bi_square(ctx, biR)); + i--; + } + } while (i >= 0); + + /* cleanup */ + for (i = 0; i < ctx->window; i++) + { + bi_depermanent(ctx->g[i]); + bi_free(ctx, ctx->g[i]); + } + + free(ctx->g); + bi_free(ctx, bi); + bi_free(ctx, biexp); +#if defined CONFIG_BIGINT_MONTGOMERY + return ctx->use_classical ? biR : bi_mont(ctx, biR); /* convert back */ +#else /* CONFIG_BIGINT_CLASSICAL or CONFIG_BIGINT_BARRETT */ + return biR; +#endif +} + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * @brief Perform a modular exponentiation using a temporary modulus. + * + * We need this function to check the signatures of certificates. The modulus + * of this function is temporary as it's just used for authentication. + * @param ctx [in] The bigint session context. + * @param bi [in] The bigint to perform the exp/mod. + * @param bim [in] The temporary modulus. + * @param biexp [in] The bigint exponent. + * @see bi_set_mod(). + */ +bigint *bi_mod_power2(BI_CTX *ctx, bigint *bi, bigint *bim, bigint *biexp) +{ + bigint *biR, *tmp_biR; + + /* Set up a temporary bigint context and transfer what we need between + * them. We need to do this since we want to keep the original modulus + * which is already in this context. This operation is only called when + * doing peer verification, and so is not expensive :-) */ + BI_CTX *tmp_ctx = bi_initialize(); + bi_set_mod(tmp_ctx, bi_clone(tmp_ctx, bim), BIGINT_M_OFFSET); + tmp_biR = bi_mod_power(tmp_ctx, + bi_clone(tmp_ctx, bi), + bi_clone(tmp_ctx, biexp)); + biR = bi_clone(ctx, tmp_biR); + bi_free(tmp_ctx, tmp_biR); + bi_free_mod(tmp_ctx, BIGINT_M_OFFSET); + bi_terminate(tmp_ctx); + + bi_free(ctx, bi); + bi_free(ctx, bim); + bi_free(ctx, biexp); + return biR; +} +#endif +/** @} */ diff --git a/gpxe/src/crypto/axtls/bigint.h b/gpxe/src/crypto/axtls/bigint.h new file mode 100644 index 0000000..f9a3c70 --- /dev/null +++ b/gpxe/src/crypto/axtls/bigint.h @@ -0,0 +1,91 @@ +/* + * Copyright(C) 2006 Cameron Rich + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef BIGINT_HEADER +#define BIGINT_HEADER + +/* enable features based on a 'super-set' capbaility. */ +#if defined(CONFIG_SSL_FULL_MODE) +#define CONFIG_SSL_ENABLE_CLIENT +#define CONFIG_SSL_CERT_VERIFICATION +#elif defined(CONFIG_SSL_ENABLE_CLIENT) +#define CONFIG_SSL_CERT_VERIFICATION +#endif + +#include "os_port.h" +#include "bigint_impl.h" + +#ifndef CONFIG_BIGINT_CHECK_ON +#define check(A) /**< disappears in normal production mode */ +#endif +BI_CTX *bi_initialize(void); +void bi_terminate(BI_CTX *ctx); +void bi_permanent(bigint *bi); +void bi_depermanent(bigint *bi); +void bi_free(BI_CTX *ctx, bigint *bi); +bigint *bi_copy(bigint *bi); +bigint *bi_clone(BI_CTX *ctx, const bigint *bi); +void bi_export(BI_CTX *ctx, bigint *bi, uint8_t *data, int size); +bigint *bi_import(BI_CTX *ctx, const uint8_t *data, int len); +bigint *int_to_bi(BI_CTX *ctx, comp i); + +/* the functions that actually do something interesting */ +bigint *bi_add(BI_CTX *ctx, bigint *bia, bigint *bib); +bigint *bi_subtract(BI_CTX *ctx, bigint *bia, + bigint *bib, int *is_negative); +bigint *bi_divide(BI_CTX *ctx, bigint *bia, bigint *bim, int is_mod); +bigint *bi_multiply(BI_CTX *ctx, bigint *bia, bigint *bib); +bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp); +bigint *bi_mod_power2(BI_CTX *ctx, bigint *bi, bigint *bim, bigint *biexp); +int bi_compare(bigint *bia, bigint *bib); +void bi_set_mod(BI_CTX *ctx, bigint *bim, int mod_offset); +void bi_free_mod(BI_CTX *ctx, int mod_offset); + +#ifdef CONFIG_SSL_FULL_MODE +void bi_print(const char *label, bigint *bi); +bigint *bi_str_import(BI_CTX *ctx, const char *data); +#endif + +/** + * @def bi_mod + * Find the residue of B. bi_set_mod() must be called before hand. + */ +#define bi_mod(A, B) bi_divide(A, B, ctx->bi_mod[ctx->mod_offset], 1) + +/** + * bi_residue() is technically the same as bi_mod(), but it uses the + * appropriate reduction technique (which is bi_mod() when doing classical + * reduction). + */ +#if defined(CONFIG_BIGINT_MONTGOMERY) +#define bi_residue(A, B) bi_mont(A, B) +bigint *bi_mont(BI_CTX *ctx, bigint *bixy); +#elif defined(CONFIG_BIGINT_BARRETT) +#define bi_residue(A, B) bi_barrett(A, B) +bigint *bi_barrett(BI_CTX *ctx, bigint *bi); +#else /* if defined(CONFIG_BIGINT_CLASSICAL) */ +#define bi_residue(A, B) bi_mod(A, B) +#endif + +#ifdef CONFIG_BIGINT_SQUARE +bigint *bi_square(BI_CTX *ctx, bigint *bi); +#else +#define bi_square(A, B) bi_multiply(A, bi_copy(B), B) +#endif + +#endif diff --git a/gpxe/src/crypto/axtls/bigint_impl.h b/gpxe/src/crypto/axtls/bigint_impl.h new file mode 100644 index 0000000..762a7cc --- /dev/null +++ b/gpxe/src/crypto/axtls/bigint_impl.h @@ -0,0 +1,105 @@ +/* + * Copyright(C) 2006 Cameron Rich + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef BIGINT_IMPL_HEADER +#define BIGINT_IMPL_HEADER + +/* Maintain a number of precomputed variables when doing reduction */ +#define BIGINT_M_OFFSET 0 /**< Normal modulo offset. */ +#ifdef CONFIG_BIGINT_CRT +#define BIGINT_P_OFFSET 1 /**< p modulo offset. */ +#define BIGINT_Q_OFFSET 2 /**< q module offset. */ +#define BIGINT_NUM_MODS 3 /**< The number of modulus constants used. */ +#else +#define BIGINT_NUM_MODS 1 +#endif + +/* Architecture specific functions for big ints */ +#ifdef WIN32 +#define COMP_RADIX 4294967296i64 +#define COMP_BIG_MSB 0x8000000000000000i64 +#else +#define COMP_RADIX 4294967296ULL /**< Max component + 1 */ +#define COMP_BIG_MSB 0x8000000000000000ULL /**< (Max dbl comp + 1)/ 2 */ +#endif +#define COMP_BIT_SIZE 32 /**< Number of bits in a component. */ +#define COMP_BYTE_SIZE 4 /**< Number of bytes in a component. */ +#define COMP_NUM_NIBBLES 8 /**< Used For diagnostics only. */ + +typedef uint32_t comp; /**< A single precision component. */ +typedef uint64_t long_comp; /**< A double precision component. */ +typedef int64_t slong_comp; /**< A signed double precision component. */ + +/** + * @struct _bigint + * @brief A big integer basic object + */ +struct _bigint +{ + struct _bigint* next; /**< The next bigint in the cache. */ + short size; /**< The number of components in this bigint. */ + short max_comps; /**< The heapsize allocated for this bigint */ + int refs; /**< An internal reference count. */ + comp* comps; /**< A ptr to the actual component data */ +}; + +typedef struct _bigint bigint; /**< An alias for _bigint */ + +/** + * Maintains the state of the cache, and a number of variables used in + * reduction. + */ +typedef struct /**< A big integer "session" context. */ +{ + bigint *active_list; /**< Bigints currently used. */ + bigint *free_list; /**< Bigints not used. */ + bigint *bi_radix; /**< The radix used. */ + bigint *bi_mod[BIGINT_NUM_MODS]; /**< modulus */ + +#if defined(CONFIG_BIGINT_MONTGOMERY) + bigint *bi_RR_mod_m[BIGINT_NUM_MODS]; /**< R^2 mod m */ + bigint *bi_R_mod_m[BIGINT_NUM_MODS]; /**< R mod m */ + comp N0_dash[BIGINT_NUM_MODS]; +#elif defined(CONFIG_BIGINT_BARRETT) + bigint *bi_mu[BIGINT_NUM_MODS]; /**< Storage for mu */ +#endif + bigint *bi_normalised_mod[BIGINT_NUM_MODS]; /**< Normalised mod storage. */ + bigint **g; /**< Used by sliding-window. */ + int window; /**< The size of the sliding window */ + int active_count; /**< Number of active bigints. */ + int free_count; /**< Number of free bigints. */ + +#ifdef CONFIG_BIGINT_MONTGOMERY + uint8_t use_classical; /**< Use classical reduction. */ +#endif + uint8_t mod_offset; /**< The mod offset we are using */ +} BI_CTX; + +#ifndef WIN32 +#define max(a,b) ((a)>(b)?(a):(b)) /**< Find the maximum of 2 numbers. */ +#define min(a,b) ((a)<(b)?(a):(b)) /**< Find the minimum of 2 numbers. */ +#endif + +#define PERMANENT 0x7FFF55AA /**< A magic number for permanents. */ + +#define V1 v->comps[v->size-1] /**< v1 for division */ +#define V2 v->comps[v->size-2] /**< v2 for division */ +#define U(j) tmp_u->comps[tmp_u->size-j-1] /**< uj for division */ +#define Q(j) quotient->comps[quotient->size-j-1] /**< qj for division */ + +#endif diff --git a/gpxe/src/crypto/axtls/crypto.h b/gpxe/src/crypto/axtls/crypto.h new file mode 100644 index 0000000..12acb27 --- /dev/null +++ b/gpxe/src/crypto/axtls/crypto.h @@ -0,0 +1,300 @@ +/* + * Copyright(C) 2006 Cameron Rich + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * @file crypto.h + */ + +#ifndef HEADER_CRYPTO_H +#define HEADER_CRYPTO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bigint.h" + +/************************************************************************** + * AES declarations + **************************************************************************/ + +#define AES_MAXROUNDS 14 + +typedef struct aes_key_st +{ + uint16_t rounds; + uint16_t key_size; + uint32_t ks[(AES_MAXROUNDS+1)*8]; + uint8_t iv[16]; +} AES_CTX; + +typedef enum +{ + AES_MODE_128, + AES_MODE_256 +} AES_MODE; + +void AES_set_key(AES_CTX *ctx, const uint8_t *key, + const uint8_t *iv, AES_MODE mode); +void AES_cbc_encrypt(AES_CTX *ctx, const uint8_t *msg, + uint8_t *out, int length); +void AES_cbc_decrypt(AES_CTX *ks, const uint8_t *in, uint8_t *out, int length); +void AES_convert_key(AES_CTX *ctx); +void AES_encrypt(const AES_CTX *ctx, uint32_t *data); +void AES_decrypt(const AES_CTX *ctx, uint32_t *data); + +/************************************************************************** + * RC4 declarations + **************************************************************************/ + +typedef struct +{ + int x, y, m[256]; +} RC4_CTX; + +void RC4_setup(RC4_CTX *s, const uint8_t *key, int length); +void RC4_crypt(RC4_CTX *s, const uint8_t *msg, uint8_t *data, int length); + +/************************************************************************** + * SHA1 declarations + **************************************************************************/ + +#define SHA1_SIZE 20 + +/* + * This structure will hold context information for the SHA-1 + * hashing operation + */ +typedef struct +{ + uint32_t Intermediate_Hash[SHA1_SIZE/4]; /* Message Digest */ + uint32_t Length_Low; /* Message length in bits */ + uint32_t Length_High; /* Message length in bits */ + uint16_t Message_Block_Index; /* Index into message block array */ + uint8_t Message_Block[64]; /* 512-bit message blocks */ +} SHA1_CTX; + +void SHA1Init(SHA1_CTX *); +void SHA1Update(SHA1_CTX *, const uint8_t * msg, int len); +void SHA1Final(SHA1_CTX *, uint8_t *digest); + +/************************************************************************** + * MD5 declarations + **************************************************************************/ + +/* MD5 context. */ + +#define MD5_SIZE 16 + +typedef struct +{ + uint32_t state[4]; /* state (ABCD) */ + uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ + uint8_t buffer[64]; /* input buffer */ +} MD5_CTX; + +void MD5Init(MD5_CTX *); +void MD5Update(MD5_CTX *, const uint8_t *msg, int len); +void MD5Final(MD5_CTX *, uint8_t *digest); + +/************************************************************************** + * HMAC declarations + **************************************************************************/ +void hmac_md5(const uint8_t *msg, int length, const uint8_t *key, + int key_len, uint8_t *digest); +void hmac_sha1(const uint8_t *msg, int length, const uint8_t *key, + int key_len, uint8_t *digest); + +/************************************************************************** + * RNG declarations + **************************************************************************/ +void RNG_initialize(const uint8_t *seed_buf, int size); +void RNG_terminate(void); +void get_random(int num_rand_bytes, uint8_t *rand_data); +//void get_random_NZ(int num_rand_bytes, uint8_t *rand_data); + +#include +static inline void get_random_NZ(int num_rand_bytes, uint8_t *rand_data) { + memset ( rand_data, 0x01, num_rand_bytes ); +} + +/************************************************************************** + * RSA declarations + **************************************************************************/ + +typedef struct +{ + bigint *m; /* modulus */ + bigint *e; /* public exponent */ + bigint *d; /* private exponent */ +#ifdef CONFIG_BIGINT_CRT + bigint *p; /* p as in m = pq */ + bigint *q; /* q as in m = pq */ + bigint *dP; /* d mod (p-1) */ + bigint *dQ; /* d mod (q-1) */ + bigint *qInv; /* q^-1 mod p */ +#endif + int num_octets; + bigint *sig_m; /* signature modulus */ + BI_CTX *bi_ctx; +} RSA_CTX; + +void RSA_priv_key_new(RSA_CTX **rsa_ctx, + const uint8_t *modulus, int mod_len, + const uint8_t *pub_exp, int pub_len, + const uint8_t *priv_exp, int priv_len +#ifdef CONFIG_BIGINT_CRT + , const uint8_t *p, int p_len, + const uint8_t *q, int q_len, + const uint8_t *dP, int dP_len, + const uint8_t *dQ, int dQ_len, + const uint8_t *qInv, int qInv_len +#endif + ); +void RSA_pub_key_new(RSA_CTX **rsa_ctx, + const uint8_t *modulus, int mod_len, + const uint8_t *pub_exp, int pub_len); +void RSA_free(RSA_CTX *ctx); +int RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data, + int is_decryption); +bigint *RSA_private(const RSA_CTX *c, bigint *bi_msg); +#ifdef CONFIG_SSL_CERT_VERIFICATION +bigint *RSA_raw_sign_verify(RSA_CTX *c, bigint *bi_msg); +bigint *RSA_sign_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len, + bigint *modulus, bigint *pub_exp); +bigint *RSA_public(const RSA_CTX *c, bigint *bi_msg); +int RSA_encrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len, + uint8_t *out_data, int is_signing); +void RSA_print(const RSA_CTX *ctx); +#endif + +/************************************************************************** + * ASN1 declarations + **************************************************************************/ +#define X509_OK 0 +#define X509_NOT_OK -1 +#define X509_VFY_ERROR_NO_TRUSTED_CERT -2 +#define X509_VFY_ERROR_BAD_SIGNATURE -3 +#define X509_VFY_ERROR_NOT_YET_VALID -4 +#define X509_VFY_ERROR_EXPIRED -5 +#define X509_VFY_ERROR_SELF_SIGNED -6 +#define X509_VFY_ERROR_INVALID_CHAIN -7 +#define X509_VFY_ERROR_UNSUPPORTED_DIGEST -8 +#define X509_INVALID_PRIV_KEY -9 + +/* + * The Distinguished Name + */ +#define X509_NUM_DN_TYPES 3 +#define X509_COMMON_NAME 0 +#define X509_ORGANIZATION 1 +#define X509_ORGANIZATIONAL_TYPE 2 + +#define ASN1_INTEGER 0x02 +#define ASN1_BIT_STRING 0x03 +#define ASN1_OCTET_STRING 0x04 +#define ASN1_NULL 0x05 +#define ASN1_OID 0x06 +#define ASN1_PRINTABLE_STR 0x13 +#define ASN1_TELETEX_STR 0x14 +#define ASN1_IA5_STR 0x16 +#define ASN1_UTC_TIME 0x17 +#define ASN1_SEQUENCE 0x30 +#define ASN1_SET 0x31 +#define ASN1_IMPLICIT_TAG 0x80 +#define ASN1_EXPLICIT_TAG 0xa0 + +#define SALT_SIZE 8 + +struct _x509_ctx +{ + char *ca_cert_dn[X509_NUM_DN_TYPES]; + char *cert_dn[X509_NUM_DN_TYPES]; +#if defined(_WIN32_WCE) + long not_before; + long not_after; +#else + time_t not_before; + time_t not_after; +#endif + uint8_t *signature; + uint16_t sig_len; + uint8_t sig_type; + RSA_CTX *rsa_ctx; + bigint *digest; + struct _x509_ctx *next; +}; + +typedef struct _x509_ctx X509_CTX; + +#ifdef CONFIG_SSL_CERT_VERIFICATION +typedef struct +{ + X509_CTX *cert[CONFIG_X509_MAX_CA_CERTS]; +} CA_CERT_CTX; +#endif + +int asn1_get_private_key(const uint8_t *buf, int len, RSA_CTX **rsa_ctx); +int asn1_next_obj(const uint8_t *buf, int *offset, int obj_type); +int asn1_skip_obj(const uint8_t *buf, int *offset, int obj_type); +int asn1_get_int(const uint8_t *buf, int *offset, uint8_t **object); +int x509_new(const uint8_t *cert, int *len, X509_CTX **ctx); +void x509_free(X509_CTX *x509_ctx); +#ifdef CONFIG_SSL_CERT_VERIFICATION +int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert); +const uint8_t *x509_get_signature(const uint8_t *asn1_signature, int *len); +#endif +#ifdef CONFIG_SSL_FULL_MODE +void x509_print(CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert); +void x509_display_error(int error); +#endif + +/************************************************************************** + * MISC declarations + **************************************************************************/ + +extern const char * const unsupported_str; + +typedef void (*crypt_func)(void *, const uint8_t *, uint8_t *, int); +typedef void (*hmac_func)(const uint8_t *msg, int length, const uint8_t *key, + int key_len, uint8_t *digest); + +typedef struct +{ + uint8_t *pre_data; /* include the ssl record bytes */ + uint8_t *data; /* the regular ssl data */ + int max_len; + int index; +} BUF_MEM; + +BUF_MEM buf_new(void); +void buf_grow(BUF_MEM *bm, int len); +void buf_free(BUF_MEM *bm); +int get_file(const char *filename, uint8_t **buf); + +#if defined(CONFIG_SSL_FULL_MODE) || defined(WIN32) || defined(CONFIG_DEBUG) +void print_blob(const char *format, const uint8_t *data, int size, ...); +#else + #define print_blob(...) +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/gpxe/src/crypto/axtls/os_port.h b/gpxe/src/crypto/axtls/os_port.h new file mode 100644 index 0000000..babdbfa --- /dev/null +++ b/gpxe/src/crypto/axtls/os_port.h @@ -0,0 +1,61 @@ +/** + * @file os_port.h + * + * Trick the axtls code into building within our build environment. + */ + +#ifndef HEADER_OS_PORT_H +#define HEADER_OS_PORT_H + +#include +#include +#include +#include +#include +#include + +#define STDCALL +#define EXP_FUNC +#define TTY_FLUSH() + +/** We can't actually abort, since we are effectively a kernel... */ +#define abort() assert ( 0 ) + +/** crypto_misc.c has a bad #ifdef */ +static inline void close ( int fd __unused ) { + /* Do nothing */ +} + +typedef void FILE; + +static inline FILE * fopen ( const char *filename __unused, + const char *mode __unused ) { + return NULL; +} + +static inline int fseek ( FILE *stream __unused, long offset __unused, + int whence __unused ) { + return -1; +} + +static inline long ftell ( FILE *stream __unused ) { + return -1; +} + +static inline size_t fread ( void *ptr __unused, size_t size __unused, + size_t nmemb __unused, FILE *stream __unused ) { + return -1; +} + +static inline int fclose ( FILE *stream __unused ) { + return -1; +} + +#define CONFIG_SSL_CERT_VERIFICATION 1 +#define CONFIG_SSL_MAX_CERTS 1 +#define CONFIG_X509_MAX_CA_CERTS 1 +#define CONFIG_SSL_EXPIRY_TIME 24 +#define CONFIG_SSL_ENABLE_CLIENT 1 +#define CONFIG_BIGINT_CLASSICAL 1 + +#endif diff --git a/gpxe/src/crypto/axtls/rsa.c b/gpxe/src/crypto/axtls/rsa.c new file mode 100644 index 0000000..389eda5 --- /dev/null +++ b/gpxe/src/crypto/axtls/rsa.c @@ -0,0 +1,332 @@ +/* + * Copyright(C) 2006 Cameron Rich + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * Implements the RSA public encryption algorithm. Uses the bigint library to + * perform its calculations. + */ + +#include +#include +#include +#include +#include "crypto.h" + +#ifdef CONFIG_BIGINT_CRT +static bigint *bi_crt(const RSA_CTX *rsa, bigint *bi); +#endif + +void RSA_priv_key_new(RSA_CTX **ctx, + const uint8_t *modulus, int mod_len, + const uint8_t *pub_exp, int pub_len, + const uint8_t *priv_exp, int priv_len +#if CONFIG_BIGINT_CRT + , const uint8_t *p, int p_len, + const uint8_t *q, int q_len, + const uint8_t *dP, int dP_len, + const uint8_t *dQ, int dQ_len, + const uint8_t *qInv, int qInv_len +#endif + ) +{ + RSA_CTX *rsa_ctx; + BI_CTX *bi_ctx; + RSA_pub_key_new(ctx, modulus, mod_len, pub_exp, pub_len); + rsa_ctx = *ctx; + bi_ctx = rsa_ctx->bi_ctx; + rsa_ctx->d = bi_import(bi_ctx, priv_exp, priv_len); + bi_permanent(rsa_ctx->d); + +#ifdef CONFIG_BIGINT_CRT + rsa_ctx->p = bi_import(bi_ctx, p, p_len); + rsa_ctx->q = bi_import(bi_ctx, q, q_len); + rsa_ctx->dP = bi_import(bi_ctx, dP, dP_len); + rsa_ctx->dQ = bi_import(bi_ctx, dQ, dQ_len); + rsa_ctx->qInv = bi_import(bi_ctx, qInv, qInv_len); + bi_permanent(rsa_ctx->dP); + bi_permanent(rsa_ctx->dQ); + bi_permanent(rsa_ctx->qInv); + bi_set_mod(bi_ctx, rsa_ctx->p, BIGINT_P_OFFSET); + bi_set_mod(bi_ctx, rsa_ctx->q, BIGINT_Q_OFFSET); +#endif +} + +void RSA_pub_key_new(RSA_CTX **ctx, + const uint8_t *modulus, int mod_len, + const uint8_t *pub_exp, int pub_len) +{ + RSA_CTX *rsa_ctx; + BI_CTX *bi_ctx = bi_initialize(); + *ctx = (RSA_CTX *)calloc(1, sizeof(RSA_CTX)); + rsa_ctx = *ctx; + rsa_ctx->bi_ctx = bi_ctx; + rsa_ctx->num_octets = (mod_len & 0xFFF0); + rsa_ctx->m = bi_import(bi_ctx, modulus, mod_len); + bi_set_mod(bi_ctx, rsa_ctx->m, BIGINT_M_OFFSET); + rsa_ctx->e = bi_import(bi_ctx, pub_exp, pub_len); + bi_permanent(rsa_ctx->e); +} + +/** + * Free up any RSA context resources. + */ +void RSA_free(RSA_CTX *rsa_ctx) +{ + BI_CTX *bi_ctx; + if (rsa_ctx == NULL) /* deal with ptrs that are null */ + return; + + bi_ctx = rsa_ctx->bi_ctx; + + bi_depermanent(rsa_ctx->e); + bi_free(bi_ctx, rsa_ctx->e); + bi_free_mod(rsa_ctx->bi_ctx, BIGINT_M_OFFSET); + + if (rsa_ctx->d) + { + bi_depermanent(rsa_ctx->d); + bi_free(bi_ctx, rsa_ctx->d); +#ifdef CONFIG_BIGINT_CRT + bi_depermanent(rsa_ctx->dP); + bi_depermanent(rsa_ctx->dQ); + bi_depermanent(rsa_ctx->qInv); + bi_free(bi_ctx, rsa_ctx->dP); + bi_free(bi_ctx, rsa_ctx->dQ); + bi_free(bi_ctx, rsa_ctx->qInv); + bi_free_mod(rsa_ctx->bi_ctx, BIGINT_P_OFFSET); + bi_free_mod(rsa_ctx->bi_ctx, BIGINT_Q_OFFSET); +#endif + } + + bi_terminate(bi_ctx); + free(rsa_ctx); +} + +/** + * @brief Use PKCS1.5 for decryption/verification. + * @param ctx [in] The context + * @param in_data [in] The data to encrypt (must be < modulus size-11) + * @param out_data [out] The encrypted data. + * @param is_decryption [in] Decryption or verify operation. + * @return The number of bytes that were originally encrypted. -1 on error. + * @see http://www.rsasecurity.com/rsalabs/node.asp?id=2125 + */ +int RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data, + uint8_t *out_data, int is_decryption) +{ + int byte_size = ctx->num_octets; + uint8_t *block; + int i, size; + bigint *decrypted_bi, *dat_bi; + + memset(out_data, 0, byte_size); /* initialise */ + + /* decrypt */ + dat_bi = bi_import(ctx->bi_ctx, in_data, byte_size); +#ifdef CONFIG_SSL_CERT_VERIFICATION + decrypted_bi = is_decryption ? /* decrypt or verify? */ + RSA_private(ctx, dat_bi) : RSA_public(ctx, dat_bi); +#else /* always a decryption */ + decrypted_bi = RSA_private(ctx, dat_bi); +#endif + + /* convert to a normal block */ + block = (uint8_t *)malloc(byte_size); + bi_export(ctx->bi_ctx, decrypted_bi, block, byte_size); + + i = 10; /* start at the first possible non-padded byte */ + +#ifdef CONFIG_SSL_CERT_VERIFICATION + if (is_decryption == 0) /* PKCS1.5 signing pads with "0xff"s */ + { + while (block[i++] == 0xff && i < byte_size); + + if (block[i-2] != 0xff) + i = byte_size; /*ensure size is 0 */ + } + else /* PKCS1.5 encryption padding is random */ +#endif + { + while (block[i++] && i < byte_size); + } + size = byte_size - i; + + /* get only the bit we want */ + if (size > 0) + memcpy(out_data, &block[i], size); + + free(block); + return size ? size : -1; +} + +/** + * Performs m = c^d mod n + */ +bigint *RSA_private(const RSA_CTX *c, bigint *bi_msg) +{ +#ifdef CONFIG_BIGINT_CRT + return bi_crt(c, bi_msg); +#else + BI_CTX *ctx = c->bi_ctx; + ctx->mod_offset = BIGINT_M_OFFSET; + return bi_mod_power(ctx, bi_msg, c->d); +#endif +} + +#ifdef CONFIG_BIGINT_CRT +/** + * Use the Chinese Remainder Theorem to quickly perform RSA decrypts. + * This should really be in bigint.c (and was at one stage), but needs + * access to the RSA_CTX context... + */ +static bigint *bi_crt(const RSA_CTX *rsa, bigint *bi) +{ + BI_CTX *ctx = rsa->bi_ctx; + bigint *m1, *m2, *h; + + /* Montgomery has a condition the 0 < x, y < m and these products violate + * that condition. So disable Montgomery when using CRT */ +#if defined(CONFIG_BIGINT_MONTGOMERY) + ctx->use_classical = 1; +#endif + ctx->mod_offset = BIGINT_P_OFFSET; + m1 = bi_mod_power(ctx, bi_copy(bi), rsa->dP); + + ctx->mod_offset = BIGINT_Q_OFFSET; + m2 = bi_mod_power(ctx, bi, rsa->dQ); + + h = bi_subtract(ctx, bi_add(ctx, m1, rsa->p), bi_copy(m2), NULL); + h = bi_multiply(ctx, h, rsa->qInv); + ctx->mod_offset = BIGINT_P_OFFSET; + h = bi_residue(ctx, h); +#if defined(CONFIG_BIGINT_MONTGOMERY) + ctx->use_classical = 0; /* reset for any further operation */ +#endif + return bi_add(ctx, m2, bi_multiply(ctx, rsa->q, h)); +} +#endif + +#ifdef CONFIG_SSL_FULL_MODE +/** + * Used for diagnostics. + */ +void RSA_print(const RSA_CTX *rsa_ctx) +{ + if (rsa_ctx == NULL) + return; + + printf("----------------- RSA DEBUG ----------------\n"); + printf("Size:\t%d\n", rsa_ctx->num_octets); + bi_print("Modulus", rsa_ctx->m); + bi_print("Public Key", rsa_ctx->e); + bi_print("Private Key", rsa_ctx->d); +} +#endif + +#ifdef CONFIG_SSL_CERT_VERIFICATION +/** + * Performs c = m^e mod n + */ +bigint *RSA_public(const RSA_CTX * c, bigint *bi_msg) +{ + c->bi_ctx->mod_offset = BIGINT_M_OFFSET; + return bi_mod_power(c->bi_ctx, bi_msg, c->e); +} + +/** + * Use PKCS1.5 for encryption/signing. + * see http://www.rsasecurity.com/rsalabs/node.asp?id=2125 + */ +int RSA_encrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len, + uint8_t *out_data, int is_signing) +{ + int byte_size = ctx->num_octets; + int num_pads_needed = byte_size-in_len-3; + bigint *dat_bi, *encrypt_bi; + + /* note: in_len+11 must be > byte_size */ + out_data[0] = 0; /* ensure encryption block is < modulus */ + + if (is_signing) + { + out_data[1] = 1; /* PKCS1.5 signing pads with "0xff"'s */ + memset(&out_data[2], 0xff, num_pads_needed); + } + else /* randomize the encryption padding with non-zero bytes */ + { + out_data[1] = 2; + get_random_NZ(num_pads_needed, &out_data[2]); + } + + out_data[2+num_pads_needed] = 0; + memcpy(&out_data[3+num_pads_needed], in_data, in_len); + + /* now encrypt it */ + dat_bi = bi_import(ctx->bi_ctx, out_data, byte_size); + encrypt_bi = is_signing ? RSA_private(ctx, dat_bi) : + RSA_public(ctx, dat_bi); + bi_export(ctx->bi_ctx, encrypt_bi, out_data, byte_size); + return byte_size; +} + +#if 0 +/** + * Take a signature and decrypt it. + */ +bigint *RSA_sign_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len, + bigint *modulus, bigint *pub_exp) +{ + uint8_t *block; + int i, size; + bigint *decrypted_bi, *dat_bi; + bigint *bir = NULL; + + block = (uint8_t *)malloc(sig_len); + + /* decrypt */ + dat_bi = bi_import(ctx, sig, sig_len); + ctx->mod_offset = BIGINT_M_OFFSET; + + /* convert to a normal block */ + decrypted_bi = bi_mod_power2(ctx, dat_bi, modulus, pub_exp); + + bi_export(ctx, decrypted_bi, block, sig_len); + ctx->mod_offset = BIGINT_M_OFFSET; + + i = 10; /* start at the first possible non-padded byte */ + while (block[i++] && i < sig_len); + size = sig_len - i; + + /* get only the bit we want */ + if (size > 0) + { + int len; + const uint8_t *sig_ptr = x509_get_signature(&block[i], &len); + + if (sig_ptr) + { + bir = bi_import(ctx, sig_ptr, len); + } + } + + free(block); + return bir; +} +#endif + +#endif /* CONFIG_SSL_CERT_VERIFICATION */ diff --git a/gpxe/src/crypto/axtls/sha1.c b/gpxe/src/crypto/axtls/sha1.c new file mode 100644 index 0000000..9a42801 --- /dev/null +++ b/gpxe/src/crypto/axtls/sha1.c @@ -0,0 +1,240 @@ +/* + * Copyright(C) 2006 Cameron Rich + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** + * SHA1 implementation - as defined in FIPS PUB 180-1 published April 17, 1995. + * This code was originally taken from RFC3174 + */ + +#include +#include "crypto.h" + +/* + * Define the SHA1 circular left shift macro + */ +#define SHA1CircularShift(bits,word) \ + (((word) << (bits)) | ((word) >> (32-(bits)))) + +/* ----- static functions ----- */ +static void SHA1PadMessage(SHA1_CTX *ctx); +static void SHA1ProcessMessageBlock(SHA1_CTX *ctx); + +/** + * Initialize the SHA1 context + */ +void SHA1Init(SHA1_CTX *ctx) +{ + ctx->Length_Low = 0; + ctx->Length_High = 0; + ctx->Message_Block_Index = 0; + ctx->Intermediate_Hash[0] = 0x67452301; + ctx->Intermediate_Hash[1] = 0xEFCDAB89; + ctx->Intermediate_Hash[2] = 0x98BADCFE; + ctx->Intermediate_Hash[3] = 0x10325476; + ctx->Intermediate_Hash[4] = 0xC3D2E1F0; +} + +/** + * Accepts an array of octets as the next portion of the message. + */ +void SHA1Update(SHA1_CTX *ctx, const uint8_t *msg, int len) +{ + while (len--) + { + ctx->Message_Block[ctx->Message_Block_Index++] = (*msg & 0xFF); + + ctx->Length_Low += 8; + if (ctx->Length_Low == 0) + { + ctx->Length_High++; + } + + if (ctx->Message_Block_Index == 64) + { + SHA1ProcessMessageBlock(ctx); + } + + msg++; + } +} + +/** + * Return the 160-bit message digest into the user's array + */ +void SHA1Final(SHA1_CTX *ctx, uint8_t *digest) +{ + int i; + + SHA1PadMessage(ctx); + memset(ctx->Message_Block, 0, 64); + ctx->Length_Low = 0; /* and clear length */ + ctx->Length_High = 0; + + for (i = 0; i < SHA1_SIZE; i++) + { + digest[i] = ctx->Intermediate_Hash[i>>2] >> 8 * ( 3 - ( i & 0x03 ) ); + } +} + +/** + * Process the next 512 bits of the message stored in the array. + */ +static void SHA1ProcessMessageBlock(SHA1_CTX *ctx) +{ + const uint32_t K[] = { /* Constants defined in SHA-1 */ + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + uint32_t temp; /* Temporary word value */ + uint32_t W[80]; /* Word sequence */ + uint32_t A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for (t = 0; t < 16; t++) + { + W[t] = ctx->Message_Block[t * 4] << 24; + W[t] |= ctx->Message_Block[t * 4 + 1] << 16; + W[t] |= ctx->Message_Block[t * 4 + 2] << 8; + W[t] |= ctx->Message_Block[t * 4 + 3]; + } + + for (t = 16; t < 80; t++) + { + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = ctx->Intermediate_Hash[0]; + B = ctx->Intermediate_Hash[1]; + C = ctx->Intermediate_Hash[2]; + D = ctx->Intermediate_Hash[3]; + E = ctx->Intermediate_Hash[4]; + + for (t = 0; t < 20; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + + B = A; + A = temp; + } + + for (t = 20; t < 40; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for (t = 40; t < 60; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for (t = 60; t < 80; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + ctx->Intermediate_Hash[0] += A; + ctx->Intermediate_Hash[1] += B; + ctx->Intermediate_Hash[2] += C; + ctx->Intermediate_Hash[3] += D; + ctx->Intermediate_Hash[4] += E; + ctx->Message_Block_Index = 0; +} + +/* + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call the ProcessMessageBlock function + * provided appropriately. When it returns, it can be assumed that + * the message digest has been computed. + * + * @param ctx [in, out] The SHA1 context + */ +static void SHA1PadMessage(SHA1_CTX *ctx) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (ctx->Message_Block_Index > 55) + { + ctx->Message_Block[ctx->Message_Block_Index++] = 0x80; + while(ctx->Message_Block_Index < 64) + { + ctx->Message_Block[ctx->Message_Block_Index++] = 0; + } + + SHA1ProcessMessageBlock(ctx); + + while (ctx->Message_Block_Index < 56) + { + ctx->Message_Block[ctx->Message_Block_Index++] = 0; + } + } + else + { + ctx->Message_Block[ctx->Message_Block_Index++] = 0x80; + while(ctx->Message_Block_Index < 56) + { + + ctx->Message_Block[ctx->Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + ctx->Message_Block[56] = ctx->Length_High >> 24; + ctx->Message_Block[57] = ctx->Length_High >> 16; + ctx->Message_Block[58] = ctx->Length_High >> 8; + ctx->Message_Block[59] = ctx->Length_High; + ctx->Message_Block[60] = ctx->Length_Low >> 24; + ctx->Message_Block[61] = ctx->Length_Low >> 16; + ctx->Message_Block[62] = ctx->Length_Low >> 8; + ctx->Message_Block[63] = ctx->Length_Low; + SHA1ProcessMessageBlock(ctx); +} diff --git a/gpxe/src/crypto/axtls_aes.c b/gpxe/src/crypto/axtls_aes.c new file mode 100644 index 0000000..51e1924 --- /dev/null +++ b/gpxe/src/crypto/axtls_aes.c @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include +#include +#include "crypto/axtls/crypto.h" + +/** @file + * + * AES algorithm + * + */ + +/** Basic AES blocksize */ +#define AES_BLOCKSIZE 16 + +/** AES context */ +struct aes_context { + /** AES context for AXTLS */ + AES_CTX axtls_ctx; + /** Cipher is being used for decrypting */ + int decrypting; +}; + +/** + * Set key + * + * @v ctx Context + * @v key Key + * @v keylen Key length + * @ret rc Return status code + */ +static int aes_setkey ( void *ctx, const void *key, size_t keylen ) { + struct aes_context *aes_ctx = ctx; + AES_MODE mode; + void *iv; + + switch ( keylen ) { + case ( 128 / 8 ): + mode = AES_MODE_128; + break; + case ( 256 / 8 ): + mode = AES_MODE_256; + break; + default: + return -EINVAL; + } + + /* IV is not a relevant concept at this stage; use a dummy + * value that will have no side-effects. + */ + iv = &aes_ctx->axtls_ctx.iv; + + AES_set_key ( &aes_ctx->axtls_ctx, key, iv, mode ); + + aes_ctx->decrypting = 0; + + return 0; +} + +/** + * Set initialisation vector + * + * @v ctx Context + * @v iv Initialisation vector + */ +static void aes_setiv ( void *ctx __unused, const void *iv __unused ) { + /* Nothing to do */ +} + +/** + * Call AXTLS' AES_encrypt() or AES_decrypt() functions + * + * @v axtls_ctx AXTLS AES context + * @v src Data to process + * @v dst Buffer for output + * @v func AXTLS AES function to call + */ +static void aes_call_axtls ( AES_CTX *axtls_ctx, const void *src, void *dst, + void ( * func ) ( const AES_CTX *axtls_ctx, + uint32_t *data ) ){ + const uint32_t *srcl = src; + uint32_t *dstl = dst; + unsigned int i; + + /* AXTLS' AES_encrypt() and AES_decrypt() functions both + * expect to deal with an array of four dwords in host-endian + * order. + */ + for ( i = 0 ; i < 4 ; i++ ) + dstl[i] = ntohl ( srcl[i] ); + func ( axtls_ctx, dstl ); + for ( i = 0 ; i < 4 ; i++ ) + dstl[i] = htonl ( dstl[i] ); +} + +/** + * Encrypt data + * + * @v ctx Context + * @v src Data to encrypt + * @v dst Buffer for encrypted data + * @v len Length of data + */ +static void aes_encrypt ( void *ctx, const void *src, void *dst, + size_t len ) { + struct aes_context *aes_ctx = ctx; + + assert ( len == AES_BLOCKSIZE ); + if ( aes_ctx->decrypting ) + assert ( 0 ); + aes_call_axtls ( &aes_ctx->axtls_ctx, src, dst, AES_encrypt ); +} + +/** + * Decrypt data + * + * @v ctx Context + * @v src Data to decrypt + * @v dst Buffer for decrypted data + * @v len Length of data + */ +static void aes_decrypt ( void *ctx, const void *src, void *dst, + size_t len ) { + struct aes_context *aes_ctx = ctx; + + assert ( len == AES_BLOCKSIZE ); + if ( ! aes_ctx->decrypting ) { + AES_convert_key ( &aes_ctx->axtls_ctx ); + aes_ctx->decrypting = 1; + } + aes_call_axtls ( &aes_ctx->axtls_ctx, src, dst, AES_decrypt ); +} + +/** Basic AES algorithm */ +static struct cipher_algorithm aes_algorithm = { + .name = "aes", + .ctxsize = sizeof ( struct aes_context ), + .blocksize = AES_BLOCKSIZE, + .setkey = aes_setkey, + .setiv = aes_setiv, + .encrypt = aes_encrypt, + .decrypt = aes_decrypt, +}; + +/* AES with cipher-block chaining */ +CBC_CIPHER ( aes_cbc, aes_cbc_algorithm, + aes_algorithm, struct aes_context, AES_BLOCKSIZE ); diff --git a/gpxe/src/crypto/axtls_sha1.c b/gpxe/src/crypto/axtls_sha1.c new file mode 100644 index 0000000..841e193 --- /dev/null +++ b/gpxe/src/crypto/axtls_sha1.c @@ -0,0 +1,25 @@ +#include "crypto/axtls/crypto.h" +#include +#include + +static void sha1_init ( void *ctx ) { + SHA1Init ( ctx ); +} + +static void sha1_update ( void *ctx, const void *data, size_t len ) { + SHA1Update ( ctx, data, len ); +} + +static void sha1_final ( void *ctx, void *out ) { + SHA1Final ( ctx, out ); +} + +struct digest_algorithm sha1_algorithm = { + .name = "sha1", + .ctxsize = SHA1_CTX_SIZE, + .blocksize = 64, + .digestsize = SHA1_DIGEST_SIZE, + .init = sha1_init, + .update = sha1_update, + .final = sha1_final, +}; diff --git a/gpxe/src/crypto/cbc.c b/gpxe/src/crypto/cbc.c new file mode 100644 index 0000000..c7116ea --- /dev/null +++ b/gpxe/src/crypto/cbc.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2009 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. + */ + +#include +#include +#include +#include + +/** @file + * + * Cipher-block chaining + * + */ + +/** + * XOR data blocks + * + * @v src Input data + * @v dst Second input data and output data buffer + * @v len Length of data + */ +static void cbc_xor ( const void *src, void *dst, size_t len ) { + const uint32_t *srcl = src; + uint32_t *dstl = dst; + unsigned int i; + + /* Assume that block sizes will always be dword-aligned, for speed */ + assert ( ( len % sizeof ( *srcl ) ) == 0 ); + + for ( i = 0 ; i < ( len / sizeof ( *srcl ) ) ; i++ ) + dstl[i] ^= srcl[i]; +} + +/** + * Encrypt data + * + * @v ctx Context + * @v src Data to encrypt + * @v dst Buffer for encrypted data + * @v len Length of data + * @v raw_cipher Underlying cipher algorithm + * @v cbc_ctx CBC context + */ +void cbc_encrypt ( void *ctx, const void *src, void *dst, size_t len, + struct cipher_algorithm *raw_cipher, void *cbc_ctx ) { + size_t blocksize = raw_cipher->blocksize; + + assert ( ( len % blocksize ) == 0 ); + + while ( len ) { + cbc_xor ( src, cbc_ctx, blocksize ); + cipher_encrypt ( raw_cipher, ctx, cbc_ctx, dst, blocksize ); + memcpy ( cbc_ctx, dst, blocksize ); + dst += blocksize; + src += blocksize; + len -= blocksize; + } +} + +/** + * Decrypt data + * + * @v ctx Context + * @v src Data to decrypt + * @v dst Buffer for decrypted data + * @v len Length of data + * @v raw_cipher Underlying cipher algorithm + * @v cbc_ctx CBC context + */ +void cbc_decrypt ( void *ctx, const void *src, void *dst, size_t len, + struct cipher_algorithm *raw_cipher, void *cbc_ctx ) { + size_t blocksize = raw_cipher->blocksize; + + assert ( ( len % blocksize ) == 0 ); + + while ( len ) { + cipher_decrypt ( raw_cipher, ctx, src, dst, blocksize ); + cbc_xor ( cbc_ctx, dst, blocksize ); + memcpy ( cbc_ctx, src, blocksize ); + dst += blocksize; + src += blocksize; + len -= blocksize; + } +} diff --git a/gpxe/src/crypto/chap.c b/gpxe/src/crypto/chap.c new file mode 100644 index 0000000..d0784d2 --- /dev/null +++ b/gpxe/src/crypto/chap.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * CHAP protocol + * + */ + +/** + * Initialise CHAP challenge/response + * + * @v chap CHAP challenge/response + * @v digest Digest algorithm to use + * @ret rc Return status code + * + * Initialises a CHAP challenge/response structure. This routine + * allocates memory, and so may fail. The allocated memory must + * eventually be freed by a call to chap_finish(). + */ +int chap_init ( struct chap_response *chap, + struct digest_algorithm *digest ) { + size_t state_len; + void *state; + + assert ( chap->digest == NULL ); + assert ( chap->digest_context == NULL ); + assert ( chap->response == NULL ); + + DBG ( "CHAP %p initialising with %s digest\n", chap, digest->name ); + + state_len = ( digest->ctxsize + digest->digestsize ); + state = malloc ( state_len ); + if ( ! state ) { + DBG ( "CHAP %p could not allocate %zd bytes for state\n", + chap, state_len ); + return -ENOMEM; + } + + chap->digest = digest; + chap->digest_context = state; + chap->response = ( state + digest->ctxsize ); + chap->response_len = digest->digestsize; + digest_init ( chap->digest, chap->digest_context ); + return 0; +} + +/** + * Add data to the CHAP challenge + * + * @v chap CHAP response + * @v data Data to add + * @v len Length of data to add + */ +void chap_update ( struct chap_response *chap, const void *data, + size_t len ) { + assert ( chap->digest != NULL ); + assert ( chap->digest_context != NULL ); + + if ( ! chap->digest ) + return; + + digest_update ( chap->digest, chap->digest_context, data, len ); +} + +/** + * Respond to the CHAP challenge + * + * @v chap CHAP response + * + * Calculates the final CHAP response value, and places it in @c + * chap->response, with a length of @c chap->response_len. + */ +void chap_respond ( struct chap_response *chap ) { + assert ( chap->digest != NULL ); + assert ( chap->digest_context != NULL ); + assert ( chap->response != NULL ); + + DBG ( "CHAP %p responding to challenge\n", chap ); + + if ( ! chap->digest ) + return; + + digest_final ( chap->digest, chap->digest_context, chap->response ); +} + +/** + * Free resources used by a CHAP response + * + * @v chap CHAP response + */ +void chap_finish ( struct chap_response *chap ) { + void *state = chap->digest_context; + + DBG ( "CHAP %p finished\n", chap ); + + free ( state ); + memset ( chap, 0, sizeof ( *chap ) ); +} diff --git a/gpxe/src/crypto/crypto_null.c b/gpxe/src/crypto/crypto_null.c new file mode 100644 index 0000000..8cc9217 --- /dev/null +++ b/gpxe/src/crypto/crypto_null.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2007 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. + */ + +/** + * @file + * + * Null crypto algorithm + */ + +#include +#include + +static void digest_null_init ( void *ctx __unused ) { + /* Do nothing */ +} + +static void digest_null_update ( void *ctx __unused, const void *src __unused, + size_t len __unused ) { + /* Do nothing */ +} + +static void digest_null_final ( void *ctx __unused, void *out __unused ) { + /* Do nothing */ +} + +struct digest_algorithm digest_null = { + .name = "null", + .ctxsize = 0, + .blocksize = 1, + .digestsize = 0, + .init = digest_null_init, + .update = digest_null_update, + .final = digest_null_final, +}; + +static int cipher_null_setkey ( void *ctx __unused, const void *key __unused, + size_t keylen __unused ) { + /* Do nothing */ + return 0; +} + +static void cipher_null_setiv ( void *ctx __unused, + const void *iv __unused ) { + /* Do nothing */ +} + +static void cipher_null_encrypt ( void *ctx __unused, const void *src, + void *dst, size_t len ) { + memcpy ( dst, src, len ); +} + +static void cipher_null_decrypt ( void *ctx __unused, const void *src, + void *dst, size_t len ) { + memcpy ( dst, src, len ); +} + +struct cipher_algorithm cipher_null = { + .name = "null", + .ctxsize = 0, + .blocksize = 1, + .setkey = cipher_null_setkey, + .setiv = cipher_null_setiv, + .encrypt = cipher_null_encrypt, + .decrypt = cipher_null_decrypt, +}; + +struct pubkey_algorithm pubkey_null = { + .name = "null", + .ctxsize = 0, +}; diff --git a/gpxe/src/crypto/hmac.c b/gpxe/src/crypto/hmac.c new file mode 100644 index 0000000..be0298a --- /dev/null +++ b/gpxe/src/crypto/hmac.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2007 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. + */ + +/** + * @file + * + * Keyed-Hashing for Message Authentication + */ + +#include +#include +#include +#include + +/** + * Reduce HMAC key length + * + * @v digest Digest algorithm to use + * @v digest_ctx Digest context + * @v key Key + * @v key_len Length of key + */ +static void hmac_reduce_key ( struct digest_algorithm *digest, + void *key, size_t *key_len ) { + uint8_t digest_ctx[digest->ctxsize]; + + digest_init ( digest, digest_ctx ); + digest_update ( digest, digest_ctx, key, *key_len ); + digest_final ( digest, digest_ctx, key ); + *key_len = digest->digestsize; +} + +/** + * Initialise HMAC + * + * @v digest Digest algorithm to use + * @v digest_ctx Digest context + * @v key Key + * @v key_len Length of key + * + * The length of the key should be less than the block size of the + * digest algorithm being used. (If the key length is greater, it + * will be replaced with its own digest, and key_len will be updated + * accordingly). + */ +void hmac_init ( struct digest_algorithm *digest, void *digest_ctx, + void *key, size_t *key_len ) { + unsigned char k_ipad[digest->blocksize]; + unsigned int i; + + /* Reduce key if necessary */ + if ( *key_len > sizeof ( k_ipad ) ) + hmac_reduce_key ( digest, key, key_len ); + + /* Construct input pad */ + memset ( k_ipad, 0, sizeof ( k_ipad ) ); + memcpy ( k_ipad, key, *key_len ); + for ( i = 0 ; i < sizeof ( k_ipad ) ; i++ ) { + k_ipad[i] ^= 0x36; + } + + /* Start inner hash */ + digest_init ( digest, digest_ctx ); + digest_update ( digest, digest_ctx, k_ipad, sizeof ( k_ipad ) ); +} + +/** + * Finalise HMAC + * + * @v digest Digest algorithm to use + * @v digest_ctx Digest context + * @v key Key + * @v key_len Length of key + * @v hmac HMAC digest to fill in + * + * The length of the key should be less than the block size of the + * digest algorithm being used. (If the key length is greater, it + * will be replaced with its own digest, and key_len will be updated + * accordingly). + */ +void hmac_final ( struct digest_algorithm *digest, void *digest_ctx, + void *key, size_t *key_len, void *hmac ) { + unsigned char k_opad[digest->blocksize]; + unsigned int i; + + /* Reduce key if necessary */ + if ( *key_len > sizeof ( k_opad ) ) + hmac_reduce_key ( digest, key, key_len ); + + /* Construct output pad */ + memset ( k_opad, 0, sizeof ( k_opad ) ); + memcpy ( k_opad, key, *key_len ); + for ( i = 0 ; i < sizeof ( k_opad ) ; i++ ) { + k_opad[i] ^= 0x5c; + } + + /* Finish inner hash */ + digest_final ( digest, digest_ctx, hmac ); + + /* Perform outer hash */ + digest_init ( digest, digest_ctx ); + digest_update ( digest, digest_ctx, k_opad, sizeof ( k_opad ) ); + digest_update ( digest, digest_ctx, hmac, digest->digestsize ); + digest_final ( digest, digest_ctx, hmac ); +} diff --git a/gpxe/src/crypto/md5.c b/gpxe/src/crypto/md5.c new file mode 100644 index 0000000..76fb8a6 --- /dev/null +++ b/gpxe/src/crypto/md5.c @@ -0,0 +1,234 @@ +/* + * Cryptographic API. + * + * MD5 Message Digest Algorithm (RFC1321). + * + * Derived from cryptoapi implementation, originally based on the + * public domain implementation written by Colin Plumb in 1993. + * + * Reduced object size by around 50% compared to the original Linux + * version for use in Etherboot by Michael Brown. + * + * Copyright (c) Cryptoapi developers. + * Copyright (c) 2002 James Morris + * Copyright (c) 2006 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 (at your option) + * any later version. + * + */ + +#include +#include +#include +#include +#include + +#define __md5step __attribute__ (( regparm ( 3 ) )) + +struct md5_step { + u32 __md5step ( * f ) ( u32 b, u32 c, u32 d ); + u8 coefficient; + u8 constant; +}; + +static u32 __md5step f1(u32 b, u32 c, u32 d) +{ + return ( d ^ ( b & ( c ^ d ) ) ); +} + +static u32 __md5step f2(u32 b, u32 c, u32 d) +{ + return ( c ^ ( d & ( b ^ c ) ) ); +} + +static u32 __md5step f3(u32 b, u32 c, u32 d) +{ + return ( b ^ c ^ d ); +} + +static u32 __md5step f4(u32 b, u32 c, u32 d) +{ + return ( c ^ ( b | ~d ) ); +} + +static struct md5_step md5_steps[4] = { + { + .f = f1, + .coefficient = 1, + .constant = 0, + }, + { + .f = f2, + .coefficient = 5, + .constant = 1, + }, + { + .f = f3, + .coefficient = 3, + .constant = 5, + }, + { + .f = f4, + .coefficient = 7, + .constant = 0, + } +}; + +static const u8 r[64] = { + 7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22, + 5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20, + 4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23, + 6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21 +}; + +static const u32 k[64] = { + 0xd76aa478UL, 0xe8c7b756UL, 0x242070dbUL, 0xc1bdceeeUL, + 0xf57c0fafUL, 0x4787c62aUL, 0xa8304613UL, 0xfd469501UL, + 0x698098d8UL, 0x8b44f7afUL, 0xffff5bb1UL, 0x895cd7beUL, + 0x6b901122UL, 0xfd987193UL, 0xa679438eUL, 0x49b40821UL, + 0xf61e2562UL, 0xc040b340UL, 0x265e5a51UL, 0xe9b6c7aaUL, + 0xd62f105dUL, 0x02441453UL, 0xd8a1e681UL, 0xe7d3fbc8UL, + 0x21e1cde6UL, 0xc33707d6UL, 0xf4d50d87UL, 0x455a14edUL, + 0xa9e3e905UL, 0xfcefa3f8UL, 0x676f02d9UL, 0x8d2a4c8aUL, + 0xfffa3942UL, 0x8771f681UL, 0x6d9d6122UL, 0xfde5380cUL, + 0xa4beea44UL, 0x4bdecfa9UL, 0xf6bb4b60UL, 0xbebfbc70UL, + 0x289b7ec6UL, 0xeaa127faUL, 0xd4ef3085UL, 0x04881d05UL, + 0xd9d4d039UL, 0xe6db99e5UL, 0x1fa27cf8UL, 0xc4ac5665UL, + 0xf4292244UL, 0x432aff97UL, 0xab9423a7UL, 0xfc93a039UL, + 0x655b59c3UL, 0x8f0ccc92UL, 0xffeff47dUL, 0x85845dd1UL, + 0x6fa87e4fUL, 0xfe2ce6e0UL, 0xa3014314UL, 0x4e0811a1UL, + 0xf7537e82UL, 0xbd3af235UL, 0x2ad7d2bbUL, 0xeb86d391UL, +}; + +static void md5_transform(u32 *hash, const u32 *in) +{ + u32 a, b, c, d, f, g, temp; + int i; + struct md5_step *step; + + a = hash[0]; + b = hash[1]; + c = hash[2]; + d = hash[3]; + + for ( i = 0 ; i < 64 ; i++ ) { + step = &md5_steps[i >> 4]; + f = step->f ( b, c, d ); + g = ( ( i * step->coefficient + step->constant ) & 0xf ); + temp = d; + d = c; + c = b; + a += ( f + k[i] + in[g] ); + a = ( ( a << r[i] ) | ( a >> ( 32-r[i] ) ) ); + b += a; + a = temp; + } + + hash[0] += a; + hash[1] += b; + hash[2] += c; + hash[3] += d; +} + +/* XXX: this stuff can be optimized */ +static inline void le32_to_cpu_array(u32 *buf, unsigned int words) +{ + while (words--) { + le32_to_cpus(buf); + buf++; + } +} + +static inline void cpu_to_le32_array(u32 *buf, unsigned int words) +{ + while (words--) { + cpu_to_le32s(buf); + buf++; + } +} + +static inline void md5_transform_helper(struct md5_ctx *ctx) +{ + le32_to_cpu_array(ctx->block, sizeof(ctx->block) / sizeof(u32)); + md5_transform(ctx->hash, ctx->block); +} + +static void md5_init(void *context) +{ + struct md5_ctx *mctx = context; + + mctx->hash[0] = 0x67452301; + mctx->hash[1] = 0xefcdab89; + mctx->hash[2] = 0x98badcfe; + mctx->hash[3] = 0x10325476; + mctx->byte_count = 0; +} + +static void md5_update(void *context, const void *data, size_t len) +{ + struct md5_ctx *mctx = context; + const u32 avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f); + + mctx->byte_count += len; + + if (avail > len) { + memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), + data, len); + return; + } + + memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), + data, avail); + + md5_transform_helper(mctx); + data += avail; + len -= avail; + + while (len >= sizeof(mctx->block)) { + memcpy(mctx->block, data, sizeof(mctx->block)); + md5_transform_helper(mctx); + data += sizeof(mctx->block); + len -= sizeof(mctx->block); + } + + memcpy(mctx->block, data, len); +} + +static void md5_final(void *context, void *out) +{ + struct md5_ctx *mctx = context; + const unsigned int offset = mctx->byte_count & 0x3f; + char *p = (char *)mctx->block + offset; + int padding = 56 - (offset + 1); + + *p++ = 0x80; + if (padding < 0) { + memset(p, 0x00, padding + sizeof (u64)); + md5_transform_helper(mctx); + p = (char *)mctx->block; + padding = 56; + } + + memset(p, 0, padding); + mctx->block[14] = mctx->byte_count << 3; + mctx->block[15] = mctx->byte_count >> 29; + le32_to_cpu_array(mctx->block, (sizeof(mctx->block) - + sizeof(u64)) / sizeof(u32)); + md5_transform(mctx->hash, mctx->block); + cpu_to_le32_array(mctx->hash, sizeof(mctx->hash) / sizeof(u32)); + memcpy(out, mctx->hash, sizeof(mctx->hash)); + memset(mctx, 0, sizeof(*mctx)); +} + +struct digest_algorithm md5_algorithm = { + .name = "md5", + .ctxsize = MD5_CTX_SIZE, + .blocksize = ( MD5_BLOCK_WORDS * 4 ), + .digestsize = MD5_DIGEST_SIZE, + .init = md5_init, + .update = md5_update, + .final = md5_final, +}; diff --git a/gpxe/src/crypto/x509.c b/gpxe/src/crypto/x509.c new file mode 100644 index 0000000..35adfa3 --- /dev/null +++ b/gpxe/src/crypto/x509.c @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include + +/** @file + * + * X.509 certificates + * + * The structure of X.509v3 certificates is concisely documented in + * RFC5280 section 4.1. The structure of RSA public keys is + * documented in RFC2313. + */ + +/** Object Identifier for "rsaEncryption" (1.2.840.113549.1.1.1) */ +static const uint8_t oid_rsa_encryption[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01 }; + +/** + * Identify X.509 certificate public key + * + * @v certificate Certificate + * @v algorithm Public key algorithm to fill in + * @v pubkey Public key value to fill in + * @ret rc Return status code + */ +static int x509_public_key ( const struct asn1_cursor *certificate, + struct asn1_cursor *algorithm, + struct asn1_cursor *pubkey ) { + struct asn1_cursor cursor; + int rc; + + /* Locate subjectPublicKeyInfo */ + memcpy ( &cursor, certificate, sizeof ( cursor ) ); + rc = ( asn1_enter ( &cursor, ASN1_SEQUENCE ), /* Certificate */ + asn1_enter ( &cursor, ASN1_SEQUENCE ), /* tbsCertificate */ + asn1_skip ( &cursor, ASN1_EXPLICIT_TAG ), /* version */ + asn1_skip ( &cursor, ASN1_INTEGER ), /* serialNumber */ + asn1_skip ( &cursor, ASN1_SEQUENCE ), /* signature */ + asn1_skip ( &cursor, ASN1_SEQUENCE ), /* issuer */ + asn1_skip ( &cursor, ASN1_SEQUENCE ), /* validity */ + asn1_skip ( &cursor, ASN1_SEQUENCE ), /* name */ + asn1_enter ( &cursor, ASN1_SEQUENCE )/* subjectPublicKeyInfo*/); + if ( rc != 0 ) { + DBG ( "Cannot locate subjectPublicKeyInfo in:\n" ); + DBG_HDA ( 0, certificate->data, certificate->len ); + return rc; + } + + /* Locate algorithm */ + memcpy ( algorithm, &cursor, sizeof ( *algorithm ) ); + rc = ( asn1_enter ( algorithm, ASN1_SEQUENCE ) /* algorithm */ ); + if ( rc != 0 ) { + DBG ( "Cannot locate algorithm in:\n" ); + DBG_HDA ( 0, certificate->data, certificate->len ); + return rc; + } + + /* Locate subjectPublicKey */ + memcpy ( pubkey, &cursor, sizeof ( *pubkey ) ); + rc = ( asn1_skip ( pubkey, ASN1_SEQUENCE ), /* algorithm */ + asn1_enter ( pubkey, ASN1_BIT_STRING ) /* subjectPublicKey*/ ); + if ( rc != 0 ) { + DBG ( "Cannot locate subjectPublicKey in:\n" ); + DBG_HDA ( 0, certificate->data, certificate->len ); + return rc; + } + + return 0; +} + +/** + * Identify X.509 certificate RSA modulus and public exponent + * + * @v certificate Certificate + * @v rsa RSA public key to fill in + * @ret rc Return status code + * + * The caller is responsible for eventually calling + * x509_free_rsa_public_key() to free the storage allocated to hold + * the RSA modulus and exponent. + */ +int x509_rsa_public_key ( const struct asn1_cursor *certificate, + struct x509_rsa_public_key *rsa_pubkey ) { + struct asn1_cursor algorithm; + struct asn1_cursor pubkey; + struct asn1_cursor modulus; + struct asn1_cursor exponent; + int rc; + + /* First, extract the public key algorithm and key data */ + if ( ( rc = x509_public_key ( certificate, &algorithm, + &pubkey ) ) != 0 ) + return rc; + + /* Check that algorithm is RSA */ + rc = ( asn1_enter ( &algorithm, ASN1_OID ) /* algorithm */ ); + if ( rc != 0 ) { + DBG ( "Cannot locate algorithm:\n" ); + DBG_HDA ( 0, certificate->data, certificate->len ); + return rc; + } + if ( ( algorithm.len != sizeof ( oid_rsa_encryption ) ) || + ( memcmp ( algorithm.data, &oid_rsa_encryption, + sizeof ( oid_rsa_encryption ) ) != 0 ) ) { + DBG ( "algorithm is not rsaEncryption in:\n" ); + DBG_HDA ( 0, certificate->data, certificate->len ); + return -ENOTSUP; + } + + /* Check that public key is a byte string, i.e. that the + * "unused bits" byte contains zero. + */ + if ( ( pubkey.len < 1 ) || + ( ( *( uint8_t * ) pubkey.data ) != 0 ) ) { + DBG ( "subjectPublicKey is not a byte string in:\n" ); + DBG_HDA ( 0, certificate->data, certificate->len ); + return -ENOTSUP; + } + pubkey.data++; + pubkey.len--; + + /* Pick out the modulus and exponent */ + rc = ( asn1_enter ( &pubkey, ASN1_SEQUENCE ) /* RSAPublicKey */ ); + if ( rc != 0 ) { + DBG ( "Cannot locate RSAPublicKey in:\n" ); + DBG_HDA ( 0, certificate->data, certificate->len ); + return -ENOTSUP; + } + memcpy ( &modulus, &pubkey, sizeof ( modulus ) ); + rc = ( asn1_enter ( &modulus, ASN1_INTEGER ) /* modulus */ ); + if ( rc != 0 ) { + DBG ( "Cannot locate modulus in:\n" ); + DBG_HDA ( 0, certificate->data, certificate->len ); + return -ENOTSUP; + } + memcpy ( &exponent, &pubkey, sizeof ( exponent ) ); + rc = ( asn1_skip ( &exponent, ASN1_INTEGER ), /* modulus */ + asn1_enter ( &exponent, ASN1_INTEGER ) /* publicExponent */ ); + if ( rc != 0 ) { + DBG ( "Cannot locate publicExponent in:\n" ); + DBG_HDA ( 0, certificate->data, certificate->len ); + return -ENOTSUP; + } + + /* Allocate space and copy out modulus and exponent */ + rsa_pubkey->modulus = malloc ( modulus.len + exponent.len ); + if ( ! rsa_pubkey->modulus ) + return -ENOMEM; + rsa_pubkey->exponent = ( rsa_pubkey->modulus + modulus.len ); + memcpy ( rsa_pubkey->modulus, modulus.data, modulus.len ); + rsa_pubkey->modulus_len = modulus.len; + memcpy ( rsa_pubkey->exponent, exponent.data, exponent.len ); + rsa_pubkey->exponent_len = exponent.len; + + DBG2 ( "RSA modulus:\n" ); + DBG2_HDA ( 0, rsa_pubkey->modulus, rsa_pubkey->modulus_len ); + DBG2 ( "RSA exponent:\n" ); + DBG2_HDA ( 0, rsa_pubkey->exponent, rsa_pubkey->exponent_len ); + + return 0; +} diff --git a/gpxe/src/doc/build_sys.dox b/gpxe/src/doc/build_sys.dox new file mode 100644 index 0000000..9466f66 --- /dev/null +++ b/gpxe/src/doc/build_sys.dox @@ -0,0 +1,419 @@ +/** @page build_sys Build system + +@section overview Overview + +Building an Etherboot image consists of three stages: + + -# @ref compilation : Compiling all the source files into object files + -# @ref linking : Linking a particular image from selected object files + -# @ref finalisation : Producing the final output binary + +Though this is a remarkably complex process, it is important to note +that it all happens automatically. Whatever state your build tree is +in, you can always type, for example + +@code + + make bin/rtl8139.dsk + +@endcode + +and know that you will get a floppy disk image with an RTL8139 driver +built from the current sources. + +@section compilation Compilation + +@subsection comp_overview Overview + +Each source file (a @c .c or a @c .S file) is compiled into a @c .o +file in the @c bin/ directory. Etherboot makes minimal use of +conditional compilation (see @ref ifdef_harmful), and so you will find +that all objects get built, even the objects that correspond to +features that you are not intending to include in your image. For +example, all network card drivers will be compiled even if you are +just building a ROM for a 3c509 card. This is a deliberate design +decision; please do @b not attempt to "fix" the build system to avoid +doing this. + +Source files are defined to be any @c .c or @c .S files found in a +directory listed in the Makefile variable #SRCDIRS. You therefore do +@b not need to edit the Makefile just because you have added a new +source file (although you will need to edit the Makefile if you have +added a new source directory). To see a list of all source +directories and source files that the build system currently knows +about, you can use the commands + +@code + + make srcdirs + make srcs + +@endcode + +Rules for compiling @c .c and @c .S files are defined in the Makefile +variables #RULE_c and #RULE_S. Makefile rules are automatically +generated for each source file using these rules. The generated rules +can be found in the @c .d file corresponding to each source file; +these are located in bin/deps/. For example, the rules +generated for drivers/net/rtl8139.c can be found in +bin/deps/drivers/net/rtl8139.c.d. These rules allow you to +type, for example + +@code + + make bin/rtl8139.o + +@endcode + +and have rtl8139.o be built from +drivers/net/rtl8139.c using the generic rule #RULE_c for +compiling @c .c files. + +You can see the full list of object files that will be built using + +@code + + make bobjs + +@endcode + +@subsection comp_ar After compilation + +Once all objects have been compiled, they will be collected into a +build library ("blib") in bin/blib.a. + +@subsection comp_custom Customising compilation + +The Makefile rules for a particular object can be customised to a +certain extent by defining the Makefile variable CFLAGS_@. +For example, if you were to set + +@code + + CFLAGS_rtl8139 = -DFOO + +@endcode + +then bin/rtl8139.o would be compiled with the additional +flags -DFOO. To see the flags that will be used when +compiling a particular object, you can use e.g. + +@code + + make bin/rtl8139.flags + +@endcode + +If you need more flexibility than the CFLAGS_@ mechanism +provides, then you can exclude source files from the automatic rule +generation process by listing them in the Makefile variable +#NON_AUTO_SRCS. The command + +@code + + make autosrcs + +@endcode + +will show you which files are currently part of the automatic rule +generation process. + +@subsection comp_multiobj Multiple objects + +A single source file can be used to generate multiple object files. +This is used, for example, to generate the decompressing and the +non-decompressing prefixes from the same source files. + +By default, a single object will be built from each source file. To +override the list of objects for a source file, you can define the +Makefile variable OBJS_@. For example, the +arch/i386/prefix/dskprefix.S source file is built into two +objects, bin/dskprefix.o and zdskprefix.o by +defining the Makefile variable + +@code + + OBJS_dskprefix = dskprefix zdskprefix + +@endcode + +Since there would be little point in building two identical objects, +customised compilation flags (see @ref comp_custom) are defined as + +@code + + CFLAGS_zdskprefix = -DCOMPRESS + +@endcode + +Thus, arch/i386/prefix/dskprefix.S is built into @c +dskprefix.o using the normal set of flags, and into @c zdskprefix.o +using the normal set of flags plus -DCOMPRESS. + +@subsection comp_debug Special debugging targets + +In addition to the basic rules #RULE_c and #RULE_S for compiling +source files into object files, there are various other rules that can +be useful for debugging. + +@subsubsection comp_debug_c_to_c Preprocessed C + +You can see the results of preprocessing a @c .c file (including the +per-object flags defined via CFLAGS_@ if applicable) using +e.g. + +@code + + make bin/rtl8139.c + +@endcode + +and examining the resulting file (bin/rtl8139.c in this +case). + +@subsubsection comp_debug_x_to_s Assembler + +You can see the results of assembling a @c .c file, or of +preprocessing a @c .S file, using e.g. + +@code + + make bin/rtl8139.s + make bin/zdskprefix.s + +@endcode + +@subsubsection comp_debug_dbg Debugging-enabled targets + +You can build targets with debug messages (DBG()) enabled using e.g. + +@code + + make bin/rtl8139.dbg.o + make bin/rtl8139.dbg2.o + +@endcode + +You will probably not need to use these targets directly, since a +mechanism exists to select debugging levels at build time; see @ref +debug. + +@section linking Linking + +@subsection link_overview Overview + +Etherboot is designed to be small and extremely customisable. This is +achieved by linking in only the features that are really wanted in any +particular build. + +There are two places from which the list of desired features is +obtained: + + -# @ref link_config_h + -# @ref link_cmdline + +@subsection link_config_h config.h + +The config.h file is used to define global build options that are +likely to apply to all images that you build, such as the console +types, supported download protocols etc. See the documentation for +config.h for more details. + +@subsection link_cmdline The make command line + +When you type a command such as + +@code + + make bin/dfe538.zrom + +@endcode + +it is used to derive the following information: + + - We are building a compressed ROM image + - The DFE538 is a PCI NIC, so we need the decompressing PCI ROM prefix + - The PCI IDs for the DFE538 are 1186:1300 + - The DFE538 is an rtl8139-based card, therefore we need the rtl8139 driver + +You can see this process in action using the command + +@code + + make bin/dfe538.zrom.info + +@endcode + +which will print + +@code + + Elements : dfe538 + Prefix : zrom + Drivers : rtl8139 + ROM name : dfe538 + Media : rom + + ROM type : pci + PCI vendor : 0x1186 + PCI device : 0x1300 + + LD driver symbols : obj_rtl8139 + LD prefix symbols : obj_zpciprefix + LD ID symbols : pci_vendor_id=0x1186 pci_device_id=0x1300 + + LD target flags : -u obj_zpciprefix --defsym check_obj_zpciprefix=obj_zpciprefix -u obj_rtl8139 --defsym check_obj_rtl8139=obj_rtl8139 -u obj_config --defsym check_obj_config=obj_config --defsym pci_vendor_id=0x1186 --defsym pci_device_id=0x1300 + +@endcode + +This should be interpreted as follows: + +@code + + Elements : dfe538 + Prefix : zrom + +@endcode + +"Elements" is the list of components preceding the first dot in the +target name. "Prefix" is the component following the first dot in the +target name. (It's called a "prefix" because the code that makes it a +@c .zrom (rather than a @c .dsk, @c .zpxe or any other type of target) +usually ends up at the start of the resulting binary image.) + +@code + + Drivers : rtl8139 + +@endcode + +"Drivers" is the list of drivers corresponding to the "Elements". +Most drivers support several network cards. The PCI_ROM() and +ISA_ROM() macros are used in the driver source files to list the cards +that a particular driver can support. + +@code + + ROM name : dfe538 + +@endcode + +"ROM name" is the first element in the "Elements" list. It is used to +select the PCI IDs for a PCI ROM. + +@code + + Media : rom + +@endcode + +"Media" is the "Prefix" minus the leading @c z, if any. + +@code + + ROM type : pci + PCI vendor : 0x1186 + PCI device : 0x1300 + +@endcode + +These are derived from the "ROM name" and the PCI_ROM() or ISA_ROM() +macros in the driver source files. + +@code + + LD driver symbols : obj_rtl8139 + LD prefix symbols : obj_zpciprefix + +@endcode + +This is the interesting part. At this point, we have established that +we need the rtl8139 driver (i.e. @c rtl8139.o) and the decompressing +PCI prefix (i.e. @c zpciprefix.o). Our build system (via the +compiler.h header file) arranges that every object exports a symbol +obj_@; this can be seen by e.g. + +@code + + objdump -t bin/rtl8139.o + +@endcode + +which will show the line + +@code + + 00000000 g *ABS* 00000000 obj_rtl8139 + +@endcode + +By instructing the linker that we need the symbols @c obj_rtl8139 and +@c obj_zpciprefix, we can therefore ensure that these two objects are +included in our build. (The linker will also include any objects that +these two objects require, since that's the whole purpose of the +linker.) + +In a similar way, we always instruct the linker that we need the +symbol @c obj_config, in order to include the object @c config.o. @c +config.o is used to drag in the objects that were specified via +config.h; see @ref link_config_h. + +@code + + LD target flags : -u obj_zpciprefix --defsym check_obj_zpciprefix=obj_zpciprefix -u obj_rtl8139 --defsym check_obj_rtl8139=obj_rtl8139 -u obj_config --defsym check_obj_config=obj_config --defsym pci_vendor_id=0x1186 --defsym pci_device_id=0x1300 + +@endcode + +These are the flags that we pass to the linker in order to include the +objects that we want in our build, and to check that they really get +included. (This latter check is needed to work around what seems to +be a bug in @c ld). + +The linker does its job of linking all the required objects together +into a coherent build. The best way to see what is happening is to +look at one of the resulting linker maps; try, for example + +@code + + make bin/dfe538.dsk.map + +@endcode + +The linker map includes, amongst others: + + - A list of which objects are included in the build, and why. + - The results of processing the linker script, line-by-line. + - A complete symbol table of the resulting build. + +It is worth spending time examining the linker map to see how an +Etherboot image is assembled. + +Whatever format is selected, the Etherboot image is built into an ELF +file, simply because this is the default format used by @c ld. + +@section finalisation Finalisation + +@subsection final_overview Overview + +The ELF file resulting from @ref linking "linking" needs to be +converted into the final binary image. Usually, this is just a case +of running + +@code + + objcopy -O binary + +@endcode + +to convert the ELF file into a raw binary image. Certain image +formats require special additional treatment. + +@subsection final_rom ROM images + +ROM images must be rounded up to a suitable ROM size (e.g. 16kB or +32kB), and certain header information such as checksums needs to be +filled in. This is done by the @c makerom.pl program. + +@section debug Debugging-enabled builds + +*/ diff --git a/gpxe/src/doc/pxe_extensions b/gpxe/src/doc/pxe_extensions new file mode 100644 index 0000000..92269cf --- /dev/null +++ b/gpxe/src/doc/pxe_extensions @@ -0,0 +1,279 @@ +FILE OPEN + +Op-Code: PXENV_FILE_OPEN (00e0h) + +Input: Far pointer to a t_PXENV_FILE_OPEN parameter structure + that has been initialised by the caller. + +Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be + returned in AX. The status field in the parameter + structure must be set to one of the values represented + by the PXENV_STATUS_xxx constants. + +Description: Opens a file specified by a URL for reading. Multiple + files may be opened and used concurrently. + + +typedef struct s_PXENV_FILE_OPEN { + PXENV_STATUS Status; + UINT16 FileHandle; + SEGOFF16 FileName; + UINT32 Reserved; +} t_PXENV_FILE_OPEN; + + +Set before calling API service: + +FileName: URL of file to be opened. Null terminated. + +Reserved: Must be zero. + + +Returned from API service: + +FileHandle: Handle for use in subsequent PXE FILE API calls. + +Status: See PXENV_STATUS_xxx constants. + + + + +FILE CLOSE + +Op-Code: PXENV_FILE_CLOSE (00e1h) + +Input: Far pointer to a t_PXENV_FILE_CLOSE parameter structure + that has been initialised by the caller. + +Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be + returned in AX. The status field in the parameter + structure must be set to one of the values represented + by the PXENV_STATUS_xxx constants. + +Description: Closes a previously opened file. + + +typedef struct s_PXENV_FILE_CLOSE { + PXENV_STATUS Status; + UINT16 FileHandle; +} t_PXENV_FILE_CLOSE; + + +Set before calling API service: + +FileHandle: Handle obtained when file was opened. + + +Returned from API service: + +Status: See PXENV_STATUS_xxx constants. + + + + +FILE SELECT + +Op-Code: PXENV_FILE_SELECT (00e2h) + +Input: Far pointer to a t_PXENV_FILE_SELECT parameter structure + that has been initialised by the caller. + +Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be + returned in AX. The status field in the parameter + structure must be set to one of the values represented + by the PXENV_STATUS_xxx constants. + +Description: Check a previously opened file's readiness for I/O. + + +typedef struct s_PXENV_FILE_SELECT { + PXENV_STATUS Status; + UINT16 FileHandle; + UINT16 Ready; +#define RDY_READ 0x0001 +} t_PXENV_FILE_SELECT; + + +Set before calling API service: + +FileHandle: Handle obtained when file was opened. + + +Returned from API service: + +Ready: Indication of readiness. This can be zero, or more, + of the RDY_xxx constants. Multiple values are + arithmetically or-ed together. + +Status: See PXENV_STATUS_xxx constants. + + + + +FILE READ + +Op-Code: PXENV_FILE_READ (00e3h) + +Input: Far pointer to a t_PXENV_FILE_READ parameter structure + that has been initialised by the caller. + +Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be + returned in AX. The status field in the parameter + structure must be set to one of the values represented + by the PXENV_STATUS_xxx constants. + + This API function is non-blocking. PXENV_EXIT_SUCCESS + and PXENV_STATUS_SUCCESS is returned if a data block + has been transferred into the caller's buffer. + PXENV_EXIT_FAILURE and PXENV_STATUS_TFTP_OPEN is + returned if no data is available to transfer; any + other status code reflects an error. + +Description: Read from a previously opened file. + + +typedef struct s_PXENV_FILE_READ { + PXENV_STATUS Status; + UINT16 FileHandle; + UINT16 BufferSize; + SEGOFF16 Buffer; +} t_PXENV_FILE_READ; + + +Set before calling API service: + +FileHandle: Handle obtained when file was opened. + +BufferSize: Maximum number of data bytes that can be copied into + Buffer. + +Buffer: Segment:Offset address of data buffer. + + +Returned from API service: + +BufferSize: Number of bytes written to the data buffer. End of + file if this is zero. + +Status: See PXENV_STATUS_xxx constants. + + + + +GET FILE SIZE + +Op-Code: PXENV_GET_FILE_SIZE (00e4h) + +Input: Far pointer to a t_PXENV_GET_FILE_SIZE parameter + structure that has been initialised by the caller. + +Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be + returned in AX. The status field in the parameter + structure must be set to one of the values represented + by the PXENV_STATUS_xxx constants. + +Description: Determine size of a previously opened file. + + +typedef struct s_PXENV_GET_FILE_SIZE { + PXENV_STATUS Status; + UINT16 FileHandle; + UINT32 FileSize; +} t_PXENV_GET_FILE_SIZE; + + +Set before calling API service: + +FileHandle: Handle obtained when file was opened. + + +Returned from API service: + +FileSize: Size of the file in bytes. + +Status: See PXENV_STATUS_xxx constants. + + + + +FILE EXEC + +Op-Code: PXENV_FILE_EXEC (00e5h) + +Input: Far pointer to a t_PXENV_FILE_EXEC parameter + structure that has been initialized by the caller. + +Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be + returned in AX. The Status field in the parameter + structure must be set to one of the values represented + by the PXENV_STATUS_xxx constants. + +Description: Execute a gPXE command. + +typedef struct s_PXENV_FILE_EXEC { + PXENV_STATUS_t Status; + SEGOFF16_t Command; +} t_PXENV_FILE_EXEC; + + +Set before calling API service: + +Command: Command to execute. Null terminated. + + +Returned from API service: + +Status: See PXENV_STATUS_xxx constants. + + + + +FILE API CHECK + +Op-Code: PXENV_FILE_API_CHECK (00e6h) + +Input: Far pointer to a t_PXENV_FILE_CHECK_API parameter + structure that has been initialized by the caller. + + On entry, the Magic field should contain the number + 0x91d447b2 or the call will fail. + +Output: PXENV_EXIT_SUCCESS or PXENV_EXIT_FAILURE must be + returned in AX. The Status field in the parameter + structure must be set to one of the values represented + by the PXENV_STATUS_xxx constants. + + If this API is present and the Magic field contains the + proper value on entry, AX will contain PXENV_EXIT_SUCCESS, + the Status field PXENV_STATUS_SUCCESS, and the Magic field + the number 0xe9c17b20. Any other combination should be + considered a failure. + +Description: Detect presence of this API. + + +typedef struct s_PXENV_FILE_CHECK_API { + PXENV_STATUS Status; + UINT16 Size; + UINT32 Magic; + UINT32 Provider; + UINT32 APIMask; + UINT32 Flags; +} t_PXENV_FILE_CHECK_API; + +Set before calling API service: + +Size: Set to sizeof(t_PXENV_FILE_CHECK_API) (20). +Magic: Set to 0x91d447b2. + + +Returned from API service: + +Size: Set to the number of bytes filled in (20). +Magic: Set to 0xe9c17b20. +Provider: Set to 0x45585067 ("gPXE"). Another implementation of this + API can use another value, e.g. to indicate a different + command set supported by FILE EXEC. +APIMask: Bitmask of supported API functions (one bit for each function + in the range 00e0h to 00ffh). +Flags: Set to zero, reserved for future use. diff --git a/gpxe/src/doxygen.cfg b/gpxe/src/doxygen.cfg new file mode 100644 index 0000000..5c42909 --- /dev/null +++ b/gpxe/src/doxygen.cfg @@ -0,0 +1,1245 @@ +# Doxyfile 1.4.4 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = Etherboot + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = @BIN@/doc + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, +# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, +# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, +# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, +# Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = YES + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = v=@param \ + ret=@retval \ + err=@exception + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources +# only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = YES + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is YES. + +SHOW_DIRECTORIES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the progam writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = @SRCDIRS@ \ + include \ + include/gpxe \ + arch/@ARCH@/include \ + doc + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm + +FILE_PATTERNS = *.c \ + *.h \ + *.S \ + *.dox + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = YES + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = YES + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = YES + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = YES + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = YES + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = include \ + arch/@ARCH@/include + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = DOXYGEN=1 + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = __attribute__ \ + PACKED \ + __unused \ + __used \ + __aligned \ + __table \ + __table_start \ + __table_end + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that a graph may be further truncated if the graph's +# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH +# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), +# the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/gpxe/src/drivers/bitbash/bitbash.c b/gpxe/src/drivers/bitbash/bitbash.c new file mode 100644 index 0000000..c6f9352 --- /dev/null +++ b/gpxe/src/drivers/bitbash/bitbash.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2006 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. + */ + +#include + +/** @file + * + * Bit-bashing interfaces + * + */ + +/** + * Set/clear output bit + * + * @v basher Bit-bashing interface + * @v bit_id Bit number + * @v data Value to write + * + * If @c data is 0, a logic 0 will be written. If @c data is + * non-zero, a logic 1 will be written. + */ +void write_bit ( struct bit_basher *basher, unsigned int bit_id, + unsigned long data ) { + basher->op->write ( basher, bit_id, ( data ? -1UL : 0 ) ); +} + +/** + * Read input bit + * + * @v basher Bit-bashing interface + * @v bit_id Bit number + * @ret data Value read + * + * @c data will always be either 0 or -1UL. The idea is that the + * caller can simply binary-AND the returned value with whatever mask + * it needs to apply. + */ +int read_bit ( struct bit_basher *basher, unsigned int bit_id ) { + return ( basher->op->read ( basher, bit_id ) ? -1UL : 0 ); +} diff --git a/gpxe/src/drivers/bitbash/i2c_bit.c b/gpxe/src/drivers/bitbash/i2c_bit.c new file mode 100644 index 0000000..b85057a --- /dev/null +++ b/gpxe/src/drivers/bitbash/i2c_bit.c @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * I2C bit-bashing interface + * + * This implements a simple I2C master via a bit-bashing interface + * that provides two lines: SCL (clock) and SDA (data). + */ + +/** + * Delay between output state changes + * + * Max rated i2c speed (for the basic i2c protocol) is 100kbps, + * i.e. 200k clock transitions per second. + */ +static void i2c_delay ( void ) { + udelay ( I2C_UDELAY ); +} + +/** + * Set state of I2C SCL line + * + * @v basher Bit-bashing interface + * @v state New state of SCL + */ +static void setscl ( struct bit_basher *basher, int state ) { + DBG2 ( "%c", ( state ? '/' : '\\' ) ); + write_bit ( basher, I2C_BIT_SCL, state ); + i2c_delay(); +} + +/** + * Set state of I2C SDA line + * + * @v basher Bit-bashing interface + * @v state New state of SDA + */ +static void setsda ( struct bit_basher *basher, int state ) { + DBG2 ( "%c", ( state ? '1' : '0' ) ); + write_bit ( basher, I2C_BIT_SDA, state ); + i2c_delay(); +} + +/** + * Get state of I2C SDA line + * + * @v basher Bit-bashing interface + * @ret state State of SDA + */ +static int getsda ( struct bit_basher *basher ) { + int state; + state = read_bit ( basher, I2C_BIT_SDA ); + DBG2 ( "%c", ( state ? '+' : '-' ) ); + return state; +} + +/** + * Send an I2C start condition + * + * @v basher Bit-bashing interface + */ +static void i2c_start ( struct bit_basher *basher ) { + setscl ( basher, 1 ); + setsda ( basher, 0 ); + setscl ( basher, 0 ); + setsda ( basher, 1 ); +} + +/** + * Send an I2C data bit + * + * @v basher Bit-bashing interface + * @v bit Bit to send + */ +static void i2c_send_bit ( struct bit_basher *basher, int bit ) { + setsda ( basher, bit ); + setscl ( basher, 1 ); + setscl ( basher, 0 ); + setsda ( basher, 1 ); +} + +/** + * Receive an I2C data bit + * + * @v basher Bit-bashing interface + * @ret bit Received bit + */ +static int i2c_recv_bit ( struct bit_basher *basher ) { + int bit; + + setscl ( basher, 1 ); + bit = getsda ( basher ); + setscl ( basher, 0 ); + return bit; +} + +/** + * Send an I2C stop condition + * + * @v basher Bit-bashing interface + */ +static void i2c_stop ( struct bit_basher *basher ) { + setsda ( basher, 0 ); + setscl ( basher, 1 ); + setsda ( basher, 1 ); +} + +/** + * Send byte via I2C bus and check for acknowledgement + * + * @v basher Bit-bashing interface + * @v byte Byte to send + * @ret rc Return status code + * + * Sends a byte via the I2C bus and checks for an acknowledgement from + * the slave device. + */ +static int i2c_send_byte ( struct bit_basher *basher, uint8_t byte ) { + int i; + int ack; + + /* Send byte */ + DBG2 ( "[send %02x]", byte ); + for ( i = 8 ; i ; i-- ) { + i2c_send_bit ( basher, byte & 0x80 ); + byte <<= 1; + } + + /* Check for acknowledgement from slave */ + ack = ( i2c_recv_bit ( basher ) == 0 ); + DBG2 ( "%s", ( ack ? "[acked]" : "[not acked]" ) ); + + return ( ack ? 0 : -EIO ); +} + +/** + * Receive byte via I2C bus + * + * @v basher Bit-bashing interface + * @ret byte Received byte + * + * Receives a byte via the I2C bus and sends NACK to the slave device. + */ +static uint8_t i2c_recv_byte ( struct bit_basher *basher ) { + uint8_t byte = 0; + int i; + + /* Receive byte */ + for ( i = 8 ; i ; i-- ) { + byte <<= 1; + byte |= ( i2c_recv_bit ( basher ) & 0x1 ); + } + + /* Send NACK */ + i2c_send_bit ( basher, 1 ); + + DBG2 ( "[rcvd %02x]", byte ); + return byte; +} + +/** + * Select I2C device for reading or writing + * + * @v basher Bit-bashing interface + * @v i2cdev I2C device + * @v offset Starting offset within the device + * @v direction I2C_READ or I2C_WRITE + * @ret rc Return status code + */ +static int i2c_select ( struct bit_basher *basher, struct i2c_device *i2cdev, + unsigned int offset, unsigned int direction ) { + unsigned int address; + int shift; + unsigned int byte; + int rc; + + i2c_start ( basher ); + + /* Calculate address to appear on bus */ + address = ( ( ( i2cdev->dev_addr | + ( offset >> ( 8 * i2cdev->word_addr_len ) ) ) << 1 ) + | direction ); + + /* Send address a byte at a time */ + for ( shift = ( 8 * ( i2cdev->dev_addr_len - 1 ) ) ; + shift >= 0 ; shift -= 8 ) { + byte = ( ( address >> shift ) & 0xff ); + if ( ( rc = i2c_send_byte ( basher, byte ) ) != 0 ) + return rc; + } + + return 0; +} + +/** + * Reset I2C bus + * + * @v basher Bit-bashing interface + * @ret rc Return status code + * + * i2c devices often don't have a reset line, so even a reboot or + * system power cycle is sometimes not enough to bring them back to a + * known state. + */ +static int i2c_reset ( struct bit_basher *basher ) { + unsigned int i; + int sda; + + /* Clock through several cycles, waiting for an opportunity to + * pull SDA low while SCL is high (which creates a start + * condition). + */ + setscl ( basher, 0 ); + setsda ( basher, 1 ); + for ( i = 0 ; i < I2C_RESET_MAX_CYCLES ; i++ ) { + setscl ( basher, 1 ); + sda = getsda ( basher ); + if ( sda ) { + /* Now that the device will see a start, issue it */ + i2c_start ( basher ); + /* Stop the bus to leave it in a known good state */ + i2c_stop ( basher ); + DBGC ( basher, "I2CBIT %p reset after %d attempts\n", + basher, ( i + 1 ) ); + return 0; + } + setscl ( basher, 0 ); + } + + DBGC ( basher, "I2CBIT %p could not reset after %d attempts\n", + basher, i ); + return -ETIMEDOUT; +} + +/** + * Read data from I2C device via bit-bashing interface + * + * @v i2c I2C interface + * @v i2cdev I2C device + * @v offset Starting offset within the device + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + * + * Note that attempting to read zero bytes of data is a valid way to + * check for I2C device presence. + */ +static int i2c_bit_read ( struct i2c_interface *i2c, + struct i2c_device *i2cdev, unsigned int offset, + uint8_t *data, unsigned int len ) { + struct i2c_bit_basher *i2cbit + = container_of ( i2c, struct i2c_bit_basher, i2c ); + struct bit_basher *basher = &i2cbit->basher; + int rc = 0; + + DBGC ( basher, "I2CBIT %p reading from device %x: ", + basher, i2cdev->dev_addr ); + + for ( ; ; data++, offset++ ) { + + /* Select device for writing */ + if ( ( rc = i2c_select ( basher, i2cdev, offset, + I2C_WRITE ) ) != 0 ) + break; + + /* Abort at end of data */ + if ( ! ( len-- ) ) + break; + + /* Select offset */ + if ( ( rc = i2c_send_byte ( basher, offset ) ) != 0 ) + break; + + /* Select device for reading */ + if ( ( rc = i2c_select ( basher, i2cdev, offset, + I2C_READ ) ) != 0 ) + break; + + /* Read byte */ + *data = i2c_recv_byte ( basher ); + DBGC ( basher, "%02x ", *data ); + } + + DBGC ( basher, "%s\n", ( rc ? "failed" : "" ) ); + i2c_stop ( basher ); + return rc; +} + +/** + * Write data to I2C device via bit-bashing interface + * + * @v i2c I2C interface + * @v i2cdev I2C device + * @v offset Starting offset within the device + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + * + * Note that attempting to write zero bytes of data is a valid way to + * check for I2C device presence. + */ +static int i2c_bit_write ( struct i2c_interface *i2c, + struct i2c_device *i2cdev, unsigned int offset, + const uint8_t *data, unsigned int len ) { + struct i2c_bit_basher *i2cbit + = container_of ( i2c, struct i2c_bit_basher, i2c ); + struct bit_basher *basher = &i2cbit->basher; + int rc = 0; + + DBGC ( basher, "I2CBIT %p writing to device %x: ", + basher, i2cdev->dev_addr ); + + for ( ; ; data++, offset++ ) { + + /* Select device for writing */ + if ( ( rc = i2c_select ( basher, i2cdev, offset, + I2C_WRITE ) ) != 0 ) + break; + + /* Abort at end of data */ + if ( ! ( len-- ) ) + break; + + /* Select offset */ + if ( ( rc = i2c_send_byte ( basher, offset ) ) != 0 ) + break; + + /* Write data to device */ + DBGC ( basher, "%02x ", *data ); + if ( ( rc = i2c_send_byte ( basher, *data ) ) != 0 ) + break; + } + + DBGC ( basher, "%s\n", ( rc ? "failed" : "" ) ); + i2c_stop ( basher ); + return rc; +} + +/** + * Initialise I2C bit-bashing interface + * + * @v i2cbit I2C bit-bashing interface + * @v bash_op Bit-basher operations + */ +int init_i2c_bit_basher ( struct i2c_bit_basher *i2cbit, + struct bit_basher_operations *bash_op ) { + struct bit_basher *basher = &i2cbit->basher; + int rc; + + /* Initialise data structures */ + basher->op = bash_op; + assert ( basher->op->read != NULL ); + assert ( basher->op->write != NULL ); + i2cbit->i2c.read = i2c_bit_read; + i2cbit->i2c.write = i2c_bit_write; + + /* Reset I2C bus */ + if ( ( rc = i2c_reset ( basher ) ) != 0 ) { + DBGC ( basher, "I2CBIT %p could not reset I2C bus: %s\n", + basher, strerror ( rc ) ); + return rc; + } + + return 0; +} diff --git a/gpxe/src/drivers/bitbash/spi_bit.c b/gpxe/src/drivers/bitbash/spi_bit.c new file mode 100644 index 0000000..ef87b5a --- /dev/null +++ b/gpxe/src/drivers/bitbash/spi_bit.c @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * SPI bit-bashing interface + * + */ + +/** Delay between SCLK changes and around SS changes */ +static void spi_bit_delay ( void ) { + udelay ( SPI_BIT_UDELAY ); +} + +/** Chip select line will be asserted */ +#define SELECT_SLAVE 0 + +/** Chip select line will be deasserted */ +#define DESELECT_SLAVE SPI_MODE_SSPOL + +/** + * Select/deselect slave + * + * @v spibit SPI bit-bashing interface + * @v slave Slave number + * @v state Slave select state + * + * @c state must be @c SELECT_SLAVE or @c DESELECT_SLAVE. + */ +static void spi_bit_set_slave_select ( struct spi_bit_basher *spibit, + unsigned int slave, + unsigned int state ) { + struct bit_basher *basher = &spibit->basher; + + state ^= ( spibit->bus.mode & SPI_MODE_SSPOL ); + DBG ( "Setting slave %d select %s\n", slave, + ( state ? "high" : "low" ) ); + + spi_bit_delay(); + write_bit ( basher, SPI_BIT_SS ( slave ), state ); + spi_bit_delay(); +} + +/** + * Transfer bits over SPI bit-bashing bus + * + * @v bus SPI bus + * @v data_out TX data buffer (or NULL) + * @v data_in RX data buffer (or NULL) + * @v len Length of transfer (in @b bits) + * @v endianness Endianness of this data transfer + * + * This issues @c len clock cycles on the SPI bus, shifting out data + * from the @c data_out buffer to the MOSI line and shifting in data + * from the MISO line to the @c data_in buffer. If @c data_out is + * NULL, then the data sent will be all zeroes. If @c data_in is + * NULL, then the incoming data will be discarded. + */ +static void spi_bit_transfer ( struct spi_bit_basher *spibit, + const void *data_out, void *data_in, + unsigned int len, int endianness ) { + struct spi_bus *bus = &spibit->bus; + struct bit_basher *basher = &spibit->basher; + unsigned int sclk = ( ( bus->mode & SPI_MODE_CPOL ) ? 1 : 0 ); + unsigned int cpha = ( ( bus->mode & SPI_MODE_CPHA ) ? 1 : 0 ); + unsigned int bit_offset; + unsigned int byte_offset; + unsigned int byte_mask; + unsigned int bit; + unsigned int step; + + DBG ( "Transferring %d bits in mode %x\n", len, bus->mode ); + + for ( step = 0 ; step < ( len * 2 ) ; step++ ) { + /* Calculate byte offset and byte mask */ + bit_offset = ( ( endianness == SPI_BIT_BIG_ENDIAN ) ? + ( len - ( step / 2 ) - 1 ) : ( step / 2 ) ); + byte_offset = ( bit_offset / 8 ); + byte_mask = ( 1 << ( bit_offset % 8 ) ); + + /* Shift data in or out */ + if ( sclk == cpha ) { + const uint8_t *byte; + + /* Shift data out */ + if ( data_out ) { + byte = ( data_out + byte_offset ); + bit = ( *byte & byte_mask ); + } else { + bit = 0; + } + write_bit ( basher, SPI_BIT_MOSI, bit ); + } else { + uint8_t *byte; + + /* Shift data in */ + bit = read_bit ( basher, SPI_BIT_MISO ); + if ( data_in ) { + byte = ( data_in + byte_offset ); + *byte &= ~byte_mask; + *byte |= ( bit & byte_mask ); + } + } + + /* Toggle clock line */ + spi_bit_delay(); + sclk = ~sclk; + write_bit ( basher, SPI_BIT_SCLK, sclk ); + } +} + +/** + * Read/write data via SPI bit-bashing bus + * + * @v bus SPI bus + * @v device SPI device + * @v command Command + * @v address Address to read/write (<0 for no address) + * @v data_out TX data buffer (or NULL) + * @v data_in RX data buffer (or NULL) + * @v len Length of transfer + * @ret rc Return status code + */ +static int spi_bit_rw ( struct spi_bus *bus, struct spi_device *device, + unsigned int command, int address, + const void *data_out, void *data_in, size_t len ) { + struct spi_bit_basher *spibit + = container_of ( bus, struct spi_bit_basher, bus ); + uint32_t tmp; + + /* Set clock line to idle state */ + write_bit ( &spibit->basher, SPI_BIT_SCLK, + ( bus->mode & SPI_MODE_CPOL ) ); + + /* Assert chip select on specified slave */ + spi_bit_set_slave_select ( spibit, device->slave, SELECT_SLAVE ); + + /* Transmit command */ + assert ( device->command_len <= ( 8 * sizeof ( tmp ) ) ); + tmp = cpu_to_le32 ( command ); + spi_bit_transfer ( spibit, &tmp, NULL, device->command_len, + SPI_BIT_BIG_ENDIAN ); + + /* Transmit address, if present */ + if ( address >= 0 ) { + assert ( device->address_len <= ( 8 * sizeof ( tmp ) ) ); + tmp = cpu_to_le32 ( address ); + spi_bit_transfer ( spibit, &tmp, NULL, device->address_len, + SPI_BIT_BIG_ENDIAN ); + } + + /* Transmit/receive data */ + spi_bit_transfer ( spibit, data_out, data_in, ( len * 8 ), + spibit->endianness ); + + /* Deassert chip select on specified slave */ + spi_bit_set_slave_select ( spibit, device->slave, DESELECT_SLAVE ); + + return 0; +} + +/** + * Initialise SPI bit-bashing interface + * + * @v spibit SPI bit-bashing interface + */ +void init_spi_bit_basher ( struct spi_bit_basher *spibit ) { + assert ( &spibit->basher.op->read != NULL ); + assert ( &spibit->basher.op->write != NULL ); + spibit->bus.rw = spi_bit_rw; +} diff --git a/gpxe/src/drivers/block/ata.c b/gpxe/src/drivers/block/ata.c new file mode 100644 index 0000000..c21d2f6 --- /dev/null +++ b/gpxe/src/drivers/block/ata.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include + +/** @file + * + * ATA block device + * + */ + +static inline __attribute__ (( always_inline )) struct ata_device * +block_to_ata ( struct block_device *blockdev ) { + return container_of ( blockdev, struct ata_device, blockdev ); +} + +/** + * Issue ATA command + * + * @v ata ATA device + * @v command ATA command + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +ata_command ( struct ata_device *ata, struct ata_command *command ) { + DBG ( "ATA cmd %02x dev %02x LBA%s %llx count %04x\n", + command->cb.cmd_stat, command->cb.device, + ( command->cb.lba48 ? "48" : "" ), + ( unsigned long long ) command->cb.lba.native, + command->cb.count.native ); + + return ata->command ( ata, command ); +} + +/** + * Read block from ATA device + * + * @v blockdev Block device + * @v block LBA block number + * @v count Block count + * @v buffer Data buffer + * @ret rc Return status code + */ +static int ata_read ( struct block_device *blockdev, uint64_t block, + unsigned long count, userptr_t buffer ) { + struct ata_device *ata = block_to_ata ( blockdev ); + struct ata_command command; + + memset ( &command, 0, sizeof ( command ) ); + command.cb.lba.native = block; + command.cb.count.native = count; + command.cb.device = ( ata->device | ATA_DEV_OBSOLETE | ATA_DEV_LBA ); + command.cb.lba48 = ata->lba48; + if ( ! ata->lba48 ) + command.cb.device |= command.cb.lba.bytes.low_prev; + command.cb.cmd_stat = ( ata->lba48 ? ATA_CMD_READ_EXT : ATA_CMD_READ ); + command.data_in = buffer; + return ata_command ( ata, &command ); +} + +/** + * Write block to ATA device + * + * @v blockdev Block device + * @v block LBA block number + * @v count Block count + * @v buffer Data buffer + * @ret rc Return status code + */ +static int ata_write ( struct block_device *blockdev, uint64_t block, + unsigned long count, userptr_t buffer ) { + struct ata_device *ata = block_to_ata ( blockdev ); + struct ata_command command; + + memset ( &command, 0, sizeof ( command ) ); + command.cb.lba.native = block; + command.cb.count.native = count; + command.cb.device = ( ata->device | ATA_DEV_OBSOLETE | ATA_DEV_LBA ); + command.cb.lba48 = ata->lba48; + if ( ! ata->lba48 ) + command.cb.device |= command.cb.lba.bytes.low_prev; + command.cb.cmd_stat = ( ata->lba48 ? + ATA_CMD_WRITE_EXT : ATA_CMD_WRITE ); + command.data_out = buffer; + return ata_command ( ata, &command ); +} + +/** + * Identify ATA device + * + * @v blockdev Block device + * @ret rc Return status code + */ +static int ata_identify ( struct block_device *blockdev ) { + struct ata_device *ata = block_to_ata ( blockdev ); + struct ata_command command; + struct ata_identity identity; + int rc; + + /* Issue IDENTIFY */ + memset ( &command, 0, sizeof ( command ) ); + command.cb.count.native = 1; + command.cb.device = ( ata->device | ATA_DEV_OBSOLETE | ATA_DEV_LBA ); + command.cb.cmd_stat = ATA_CMD_IDENTIFY; + command.data_in = virt_to_user ( &identity ); + linker_assert ( sizeof ( identity ) == ATA_SECTOR_SIZE, + __ata_identity_bad_size__ ); + if ( ( rc = ata_command ( ata, &command ) ) != 0 ) + return rc; + + /* Fill in block device parameters */ + blockdev->blksize = ATA_SECTOR_SIZE; + if ( identity.supports_lba48 & cpu_to_le16 ( ATA_SUPPORTS_LBA48 ) ) { + ata->lba48 = 1; + blockdev->blocks = le64_to_cpu ( identity.lba48_sectors ); + } else { + blockdev->blocks = le32_to_cpu ( identity.lba_sectors ); + } + return 0; +} + +static struct block_device_operations ata_operations = { + .read = ata_read, + .write = ata_write +}; + +/** + * Initialise ATA device + * + * @v ata ATA device + * @ret rc Return status code + * + * Initialises an ATA device. The ata_device::command field and the + * @c ATA_FL_SLAVE portion of the ata_device::flags field must already + * be filled in. This function will configure ata_device::blockdev, + * including issuing an IDENTIFY DEVICE call to determine the block + * size and total device size. + */ +int init_atadev ( struct ata_device *ata ) { + /** Fill in read and write methods, and get device capacity */ + ata->blockdev.op = &ata_operations; + return ata_identify ( &ata->blockdev ); +} diff --git a/gpxe/src/drivers/block/ramdisk.c b/gpxe/src/drivers/block/ramdisk.c new file mode 100644 index 0000000..5091199 --- /dev/null +++ b/gpxe/src/drivers/block/ramdisk.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include + +/** + * @file + * + * RAM disks + * + */ + +static inline __attribute__ (( always_inline )) struct ramdisk * +block_to_ramdisk ( struct block_device *blockdev ) { + return container_of ( blockdev, struct ramdisk, blockdev ); +} + +/** + * Read block + * + * @v blockdev Block device + * @v block Block number + * @v count Block count + * @v buffer Data buffer + * @ret rc Return status code + */ +static int ramdisk_read ( struct block_device *blockdev, uint64_t block, + unsigned long count, userptr_t buffer ) { + struct ramdisk *ramdisk = block_to_ramdisk ( blockdev ); + unsigned long offset = ( block * blockdev->blksize ); + unsigned long length = ( count * blockdev->blksize ); + + DBGC ( ramdisk, "RAMDISK %p reading [%lx,%lx)\n", + ramdisk, offset, length ); + + memcpy_user ( buffer, 0, ramdisk->data, offset, length ); + return 0; +} + +/** + * Write block + * + * @v blockdev Block device + * @v block Block number + * @v count Block count + * @v buffer Data buffer + * @ret rc Return status code + */ +static int ramdisk_write ( struct block_device *blockdev, uint64_t block, + unsigned long count, userptr_t buffer ) { + struct ramdisk *ramdisk = block_to_ramdisk ( blockdev ); + unsigned long offset = ( block * blockdev->blksize ); + unsigned long length = ( count * blockdev->blksize ); + + DBGC ( ramdisk, "RAMDISK %p writing [%lx,%lx)\n", + ramdisk, offset, length ); + + memcpy_user ( ramdisk->data, offset, buffer, 0, length ); + return 0; +} + +static struct block_device_operations ramdisk_operations = { + .read = ramdisk_read, + .write = ramdisk_write +}; + +int init_ramdisk ( struct ramdisk *ramdisk, userptr_t data, size_t len, + unsigned int blksize ) { + + if ( ! blksize ) + blksize = 512; + + ramdisk->data = data; + ramdisk->blockdev.op = &ramdisk_operations; + ramdisk->blockdev.blksize = blksize; + ramdisk->blockdev.blocks = ( len / blksize ); + + return 0; +} diff --git a/gpxe/src/drivers/block/scsi.c b/gpxe/src/drivers/block/scsi.c new file mode 100644 index 0000000..b22bd20 --- /dev/null +++ b/gpxe/src/drivers/block/scsi.c @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include + +/** @file + * + * SCSI block device + * + */ + +/** Maximum number of dummy "read capacity (10)" operations + * + * These are issued at connection setup to draw out various useless + * power-on messages. + */ +#define SCSI_MAX_DUMMY_READ_CAP 10 + +static inline __attribute__ (( always_inline )) struct scsi_device * +block_to_scsi ( struct block_device *blockdev ) { + return container_of ( blockdev, struct scsi_device, blockdev ); +} + +/** + * Issue SCSI command + * + * @v scsi SCSI device + * @v command SCSI command + * @ret rc Return status code + */ +static int scsi_command ( struct scsi_device *scsi, + struct scsi_command *command ) { + int rc; + + /* Clear sense response code before issuing command */ + command->sense_response = 0; + + /* Issue SCSI command */ + if ( ( rc = scsi->command ( scsi, command ) ) != 0 ) { + /* Something went wrong with the issuing mechanism, + * (rather than with the command itself) + */ + DBG ( "SCSI %p " SCSI_CDB_FORMAT " err %s\n", + scsi, SCSI_CDB_DATA ( command->cdb ), strerror ( rc ) ); + return rc; + } + + /* Check for SCSI errors */ + if ( command->status != 0 ) { + DBG ( "SCSI %p " SCSI_CDB_FORMAT " status %02x sense %02x\n", + scsi, SCSI_CDB_DATA ( command->cdb ), + command->status, command->sense_response ); + return -EIO; + } + + return 0; +} + +/** + * Read block from SCSI device using READ (10) + * + * @v blockdev Block device + * @v block LBA block number + * @v count Block count + * @v buffer Data buffer + * @ret rc Return status code + */ +static int scsi_read_10 ( struct block_device *blockdev, uint64_t block, + unsigned long count, userptr_t buffer ) { + struct scsi_device *scsi = block_to_scsi ( blockdev ); + struct scsi_command command; + struct scsi_cdb_read_10 *cdb = &command.cdb.read10; + + /* Issue READ (10) */ + memset ( &command, 0, sizeof ( command ) ); + cdb->opcode = SCSI_OPCODE_READ_10; + cdb->lba = cpu_to_be32 ( block ); + cdb->len = cpu_to_be16 ( count ); + command.data_in = buffer; + command.data_in_len = ( count * blockdev->blksize ); + return scsi_command ( scsi, &command ); +} + +/** + * Read block from SCSI device using READ (16) + * + * @v blockdev Block device + * @v block LBA block number + * @v count Block count + * @v buffer Data buffer + * @ret rc Return status code + */ +static int scsi_read_16 ( struct block_device *blockdev, uint64_t block, + unsigned long count, userptr_t buffer ) { + struct scsi_device *scsi = block_to_scsi ( blockdev ); + struct scsi_command command; + struct scsi_cdb_read_16 *cdb = &command.cdb.read16; + + /* Issue READ (16) */ + memset ( &command, 0, sizeof ( command ) ); + cdb->opcode = SCSI_OPCODE_READ_16; + cdb->lba = cpu_to_be64 ( block ); + cdb->len = cpu_to_be32 ( count ); + command.data_in = buffer; + command.data_in_len = ( count * blockdev->blksize ); + return scsi_command ( scsi, &command ); +} + +/** + * Write block to SCSI device using WRITE (10) + * + * @v blockdev Block device + * @v block LBA block number + * @v count Block count + * @v buffer Data buffer + * @ret rc Return status code + */ +static int scsi_write_10 ( struct block_device *blockdev, uint64_t block, + unsigned long count, userptr_t buffer ) { + struct scsi_device *scsi = block_to_scsi ( blockdev ); + struct scsi_command command; + struct scsi_cdb_write_10 *cdb = &command.cdb.write10; + + /* Issue WRITE (10) */ + memset ( &command, 0, sizeof ( command ) ); + cdb->opcode = SCSI_OPCODE_WRITE_10; + cdb->lba = cpu_to_be32 ( block ); + cdb->len = cpu_to_be16 ( count ); + command.data_out = buffer; + command.data_out_len = ( count * blockdev->blksize ); + return scsi_command ( scsi, &command ); +} + +/** + * Write block to SCSI device using WRITE (16) + * + * @v blockdev Block device + * @v block LBA block number + * @v count Block count + * @v buffer Data buffer + * @ret rc Return status code + */ +static int scsi_write_16 ( struct block_device *blockdev, uint64_t block, + unsigned long count, userptr_t buffer ) { + struct scsi_device *scsi = block_to_scsi ( blockdev ); + struct scsi_command command; + struct scsi_cdb_write_16 *cdb = &command.cdb.write16; + + /* Issue WRITE (16) */ + memset ( &command, 0, sizeof ( command ) ); + cdb->opcode = SCSI_OPCODE_WRITE_16; + cdb->lba = cpu_to_be64 ( block ); + cdb->len = cpu_to_be32 ( count ); + command.data_out = buffer; + command.data_out_len = ( count * blockdev->blksize ); + return scsi_command ( scsi, &command ); +} + +/** + * Read capacity of SCSI device via READ CAPACITY (10) + * + * @v blockdev Block device + * @ret rc Return status code + */ +static int scsi_read_capacity_10 ( struct block_device *blockdev ) { + struct scsi_device *scsi = block_to_scsi ( blockdev ); + struct scsi_command command; + struct scsi_cdb_read_capacity_10 *cdb = &command.cdb.readcap10; + struct scsi_capacity_10 capacity; + int rc; + + /* Issue READ CAPACITY (10) */ + memset ( &command, 0, sizeof ( command ) ); + cdb->opcode = SCSI_OPCODE_READ_CAPACITY_10; + command.data_in = virt_to_user ( &capacity ); + command.data_in_len = sizeof ( capacity ); + + if ( ( rc = scsi_command ( scsi, &command ) ) != 0 ) + return rc; + + /* Fill in block device fields */ + blockdev->blksize = be32_to_cpu ( capacity.blksize ); + blockdev->blocks = ( be32_to_cpu ( capacity.lba ) + 1 ); + + return 0; +} + +/** + * Read capacity of SCSI device via READ CAPACITY (16) + * + * @v blockdev Block device + * @ret rc Return status code + */ +static int scsi_read_capacity_16 ( struct block_device *blockdev ) { + struct scsi_device *scsi = block_to_scsi ( blockdev ); + struct scsi_command command; + struct scsi_cdb_read_capacity_16 *cdb = &command.cdb.readcap16; + struct scsi_capacity_16 capacity; + int rc; + + /* Issue READ CAPACITY (16) */ + memset ( &command, 0, sizeof ( command ) ); + cdb->opcode = SCSI_OPCODE_SERVICE_ACTION_IN; + cdb->service_action = SCSI_SERVICE_ACTION_READ_CAPACITY_16; + cdb->len = cpu_to_be32 ( sizeof ( capacity ) ); + command.data_in = virt_to_user ( &capacity ); + command.data_in_len = sizeof ( capacity ); + + if ( ( rc = scsi_command ( scsi, &command ) ) != 0 ) + return rc; + + /* Fill in block device fields */ + blockdev->blksize = be32_to_cpu ( capacity.blksize ); + blockdev->blocks = ( be64_to_cpu ( capacity.lba ) + 1 ); + return 0; +} + +static struct block_device_operations scsi_operations_16 = { + .read = scsi_read_16, + .write = scsi_write_16, +}; + +static struct block_device_operations scsi_operations_10 = { + .read = scsi_read_10, + .write = scsi_write_10, +}; + +/** + * Initialise SCSI device + * + * @v scsi SCSI device + * @ret rc Return status code + * + * Initialises a SCSI device. The scsi_device::command and + * scsi_device::lun fields must already be filled in. This function + * will configure scsi_device::blockdev, including issuing a READ + * CAPACITY call to determine the block size and total device size. + */ +int init_scsidev ( struct scsi_device *scsi ) { + unsigned int i; + int rc; + + /* Issue some theoretically extraneous READ CAPACITY (10) + * commands, solely in order to draw out the "CHECK CONDITION + * (power-on occurred)", "CHECK CONDITION (reported LUNs data + * has changed)" etc. that some dumb targets insist on sending + * as an error at start of day. The precise command that we + * use is unimportant; we just need to provide the target with + * an opportunity to send its responses. + */ + for ( i = 0 ; i < SCSI_MAX_DUMMY_READ_CAP ; i++ ) { + if ( ( rc = scsi_read_capacity_10 ( &scsi->blockdev ) ) == 0 ) + break; + } + + /* Try READ CAPACITY (10), which is a mandatory command, first. */ + scsi->blockdev.op = &scsi_operations_10; + if ( ( rc = scsi_read_capacity_10 ( &scsi->blockdev ) ) != 0 ) + return rc; + + /* If capacity range was exceeded (i.e. capacity.lba was + * 0xffffffff, meaning that blockdev->blocks is now zero), use + * READ CAPACITY (16) instead. READ CAPACITY (16) is not + * mandatory, so we can't just use it straight off. + */ + if ( scsi->blockdev.blocks == 0 ) { + scsi->blockdev.op = &scsi_operations_16; + if ( ( rc = scsi_read_capacity_16 ( &scsi->blockdev ) ) != 0 ) + return rc; + } + + return 0; +} diff --git a/gpxe/src/drivers/bus/eisa.c b/gpxe/src/drivers/bus/eisa.c new file mode 100644 index 0000000..d9e4235 --- /dev/null +++ b/gpxe/src/drivers/bus/eisa.c @@ -0,0 +1,185 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static struct eisa_driver eisa_drivers[0] + __table_start ( struct eisa_driver, eisa_drivers ); +static struct eisa_driver eisa_drivers_end[0] + __table_end ( struct eisa_driver, eisa_drivers ); + +static void eisabus_remove ( struct root_device *rootdev ); + +/** + * Reset and enable/disable an EISA device + * + * @v eisa EISA device + * @v enabled 1=enable, 0=disable + */ +void eisa_device_enabled ( struct eisa_device *eisa, int enabled ) { + /* Set reset line high for 1000 µs. Spec says 500 µs, but + * this doesn't work for all cards, so we are conservative. + */ + outb ( EISA_CMD_RESET, eisa->ioaddr + EISA_GLOBAL_CONFIG ); + udelay ( 1000 ); /* Must wait 800 */ + + /* Set reset low and write a 1 to ENABLE. Delay again, in + * case the card takes a while to wake up. + */ + outb ( enabled ? EISA_CMD_ENABLE : 0, + eisa->ioaddr + EISA_GLOBAL_CONFIG ); + udelay ( 1000 ); /* Must wait 800 */ + + DBG ( "EISA %s device %02x\n", ( enabled ? "enabled" : "disabled" ), + eisa->slot ); +} + +/** + * Probe an EISA device + * + * @v eisa EISA device + * @ret rc Return status code + * + * Searches for a driver for the EISA device. If a driver is found, + * its probe() routine is called. + */ +static int eisa_probe ( struct eisa_device *eisa ) { + struct eisa_driver *driver; + struct eisa_device_id *id; + unsigned int i; + int rc; + + DBG ( "Adding EISA device %02x (%04x:%04x (\"%s\") io %x)\n", + eisa->slot, eisa->vendor_id, eisa->prod_id, + isa_id_string ( eisa->vendor_id, eisa->prod_id ), eisa->ioaddr ); + + for ( driver = eisa_drivers; driver < eisa_drivers_end; driver++ ) { + for ( i = 0 ; i < driver->id_count ; i++ ) { + id = &driver->ids[i]; + if ( id->vendor_id != eisa->vendor_id ) + continue; + if ( ISA_PROD_ID ( id->prod_id ) != + ISA_PROD_ID ( eisa->prod_id ) ) + continue; + eisa->driver = driver; + eisa->driver_name = id->name; + DBG ( "...using driver %s\n", eisa->driver_name ); + if ( ( rc = driver->probe ( eisa, id ) ) != 0 ) { + DBG ( "......probe failed\n" ); + continue; + } + return 0; + } + } + + DBG ( "...no driver found\n" ); + return -ENOTTY; +} + +/** + * Remove an EISA device + * + * @v eisa EISA device + */ +static void eisa_remove ( struct eisa_device *eisa ) { + eisa->driver->remove ( eisa ); + DBG ( "Removed EISA device %02x\n", eisa->slot ); +} + +/** + * Probe EISA root bus + * + * @v rootdev EISA bus root device + * + * Scans the EISA bus for devices and registers all devices it can + * find. + */ +static int eisabus_probe ( struct root_device *rootdev ) { + struct eisa_device *eisa = NULL; + unsigned int slot; + int rc; + + for ( slot = EISA_MIN_SLOT ; slot <= EISA_MAX_SLOT ; slot++ ) { + /* Allocate struct eisa_device */ + if ( ! eisa ) + eisa = malloc ( sizeof ( *eisa ) ); + if ( ! eisa ) { + rc = -ENOMEM; + goto err; + } + memset ( eisa, 0, sizeof ( *eisa ) ); + eisa->slot = slot; + eisa->ioaddr = EISA_SLOT_BASE ( eisa->slot ); + + /* Test for board present */ + outb ( 0xff, eisa->ioaddr + EISA_VENDOR_ID ); + eisa->vendor_id = + le16_to_cpu ( inw ( eisa->ioaddr + EISA_VENDOR_ID ) ); + eisa->prod_id = + le16_to_cpu ( inw ( eisa->ioaddr + EISA_PROD_ID ) ); + if ( eisa->vendor_id & 0x80 ) { + /* No board present */ + continue; + } + + /* Add to device hierarchy */ + snprintf ( eisa->dev.name, sizeof ( eisa->dev.name ), + "EISA%02x", slot ); + eisa->dev.desc.bus_type = BUS_TYPE_EISA; + eisa->dev.desc.vendor = eisa->vendor_id; + eisa->dev.desc.device = eisa->prod_id; + eisa->dev.parent = &rootdev->dev; + list_add ( &eisa->dev.siblings, &rootdev->dev.children ); + INIT_LIST_HEAD ( &eisa->dev.children ); + + /* Look for a driver */ + if ( eisa_probe ( eisa ) == 0 ) { + /* eisadev registered, we can drop our ref */ + eisa = NULL; + } else { + /* Not registered; re-use struct */ + list_del ( &eisa->dev.siblings ); + } + } + + free ( eisa ); + return 0; + + err: + free ( eisa ); + eisabus_remove ( rootdev ); + return rc; +} + +/** + * Remove EISA root bus + * + * @v rootdev EISA bus root device + */ +static void eisabus_remove ( struct root_device *rootdev ) { + struct eisa_device *eisa; + struct eisa_device *tmp; + + list_for_each_entry_safe ( eisa, tmp, &rootdev->dev.children, + dev.siblings ) { + eisa_remove ( eisa ); + list_del ( &eisa->dev.siblings ); + free ( eisa ); + } +} + +/** EISA bus root device driver */ +static struct root_driver eisa_root_driver = { + .probe = eisabus_probe, + .remove = eisabus_remove, +}; + +/** EISA bus root device */ +struct root_device eisa_root_device __root_device = { + .dev = { .name = "EISA" }, + .driver = &eisa_root_driver, +}; diff --git a/gpxe/src/drivers/bus/isa.c b/gpxe/src/drivers/bus/isa.c new file mode 100644 index 0000000..fa5def5 --- /dev/null +++ b/gpxe/src/drivers/bus/isa.c @@ -0,0 +1,175 @@ +#include +#include +#include +#include +#include +#include +#include + +/* + * isa.c implements a "classical" port-scanning method of ISA device + * detection. The driver must provide a list of probe addresses + * (probe_addrs), together with a function (probe_addr) that can be + * used to test for the physical presence of a device at any given + * address. + * + * Note that this should probably be considered the "last resort" for + * device probing. If the card supports ISAPnP or EISA, use that + * instead. Some cards (e.g. the 3c509) implement a proprietary + * ISAPnP-like mechanism. + * + * The ISA probe address list can be overridden by config.h; if the + * user specifies ISA_PROBE_ADDRS then that list will be used first. + * (If ISA_PROBE_ONLY is defined, the driver's own list will never be + * used). + */ + +/* + * User-supplied probe address list + * + */ +static isa_probe_addr_t isa_extra_probe_addrs[] = { +#ifdef ISA_PROBE_ADDRS + ISA_PROBE_ADDRS +#endif +}; +#define ISA_EXTRA_PROBE_ADDR_COUNT \ + ( sizeof ( isa_extra_probe_addrs ) / sizeof ( isa_extra_probe_addrs[0] ) ) + +#define ISA_IOIDX_MIN( driver ) ( -ISA_EXTRA_PROBE_ADDR_COUNT ) +#ifdef ISA_PROBE_ONLY +#define ISA_IOIDX_MAX( driver ) ( -1 ) +#else +#define ISA_IOIDX_MAX( driver ) ( (int) (driver)->addr_count - 1 ) +#endif + +#define ISA_IOADDR( driver, ioidx ) \ + ( ( (ioidx) < 0 ) ? \ + isa_extra_probe_addrs[ (ioidx) + ISA_EXTRA_PROBE_ADDR_COUNT ] : \ + (driver)->probe_addrs[(ioidx)] ) + +static struct isa_driver isa_drivers[0] + __table_start ( struct isa_driver, isa_drivers ); +static struct isa_driver isa_drivers_end[0] + __table_end ( struct isa_driver, isa_drivers ); + +static void isabus_remove ( struct root_device *rootdev ); + +/** + * Probe an ISA device + * + * @v isa ISA device + * @ret rc Return status code + */ +static int isa_probe ( struct isa_device *isa ) { + int rc; + + DBG ( "Trying ISA driver %s at I/O %04x\n", + isa->driver->name, isa->ioaddr ); + + if ( ( rc = isa->driver->probe ( isa ) ) != 0 ) { + DBG ( "...probe failed\n" ); + return rc; + } + + DBG ( "...device found\n" ); + return 0; +} + +/** + * Remove an ISA device + * + * @v isa ISA device + */ +static void isa_remove ( struct isa_device *isa ) { + isa->driver->remove ( isa ); + DBG ( "Removed ISA%04x\n", isa->ioaddr ); +} + +/** + * Probe ISA root bus + * + * @v rootdev ISA bus root device + * + * Scans the ISA bus for devices and registers all devices it can + * find. + */ +static int isabus_probe ( struct root_device *rootdev ) { + struct isa_device *isa = NULL; + struct isa_driver *driver; + int ioidx; + int rc; + + for ( driver = isa_drivers ; driver < isa_drivers_end ; driver++ ) { + for ( ioidx = ISA_IOIDX_MIN ( driver ) ; + ioidx <= ISA_IOIDX_MAX ( driver ) ; ioidx++ ) { + /* Allocate struct isa_device */ + if ( ! isa ) + isa = malloc ( sizeof ( *isa ) ); + if ( ! isa ) { + rc = -ENOMEM; + goto err; + } + memset ( isa, 0, sizeof ( *isa ) ); + isa->driver = driver; + isa->ioaddr = ISA_IOADDR ( driver, ioidx ); + + /* Add to device hierarchy */ + snprintf ( isa->dev.name, sizeof ( isa->dev.name ), + "ISA%04x", isa->ioaddr ); + isa->dev.desc.bus_type = BUS_TYPE_ISA; + isa->dev.desc.vendor = driver->vendor_id; + isa->dev.desc.device = driver->prod_id; + isa->dev.parent = &rootdev->dev; + list_add ( &isa->dev.siblings, + &rootdev->dev.children ); + INIT_LIST_HEAD ( &isa->dev.children ); + + /* Try probing at this I/O address */ + if ( isa_probe ( isa ) == 0 ) { + /* isadev registered, we can drop our ref */ + isa = NULL; + } else { + /* Not registered; re-use struct */ + list_del ( &isa->dev.siblings ); + } + } + } + + free ( isa ); + return 0; + + err: + free ( isa ); + isabus_remove ( rootdev ); + return rc; +} + +/** + * Remove ISA root bus + * + * @v rootdev ISA bus root device + */ +static void isabus_remove ( struct root_device *rootdev ) { + struct isa_device *isa; + struct isa_device *tmp; + + list_for_each_entry_safe ( isa, tmp, &rootdev->dev.children, + dev.siblings ) { + isa_remove ( isa ); + list_del ( &isa->dev.siblings ); + free ( isa ); + } +} + +/** ISA bus root device driver */ +static struct root_driver isa_root_driver = { + .probe = isabus_probe, + .remove = isabus_remove, +}; + +/** ISA bus root device */ +struct root_device isa_root_device __root_device = { + .dev = { .name = "ISA" }, + .driver = &isa_root_driver, +}; diff --git a/gpxe/src/drivers/bus/isa_ids.c b/gpxe/src/drivers/bus/isa_ids.c new file mode 100644 index 0000000..7310158 --- /dev/null +++ b/gpxe/src/drivers/bus/isa_ids.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +/* + * EISA and ISAPnP IDs are actually mildly human readable, though in a + * somewhat brain-damaged way. + * + */ +char * isa_id_string ( unsigned int vendor, unsigned int product ) { + static char buf[7]; + int i; + + /* Vendor ID is a compressed ASCII string */ + vendor = bswap_16 ( vendor ); + for ( i = 2 ; i >= 0 ; i-- ) { + buf[i] = ( 'A' - 1 + ( vendor & 0x1f ) ); + vendor >>= 5; + } + + /* Product ID is a 4-digit hex string */ + sprintf ( &buf[3], "%04x", bswap_16 ( product ) ); + + return buf; +} diff --git a/gpxe/src/drivers/bus/isapnp.c b/gpxe/src/drivers/bus/isapnp.c new file mode 100644 index 0000000..8f812df --- /dev/null +++ b/gpxe/src/drivers/bus/isapnp.c @@ -0,0 +1,758 @@ +/************************************************************************** +* +* isapnp.c -- Etherboot isapnp support for the 3Com 3c515 +* Written 2002-2003 by Timothy Legge +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Portions of this code: +* Copyright (C) 2001 P.J.H.Fox (fox@roestock.demon.co.uk) +* +* +* REVISION HISTORY: +* ================ +* Version 0.1 April 26, 2002 TJL +* Version 0.2 01/08/2003 TJL Moved outside the 3c515.c driver file +* Version 0.3 Sept 23, 2003 timlegge Change delay to currticks +* +* +* Generalised into an ISAPnP bus that can be used by more than just +* the 3c515 by Michael Brown +* +***************************************************************************/ + +/** @file + * + * ISAPnP bus support + * + * Etherboot orignally gained ISAPnP support in a very limited way for + * the 3c515 NIC. The current implementation is almost a complete + * rewrite based on the ISAPnP specification, with passing reference + * to the Linux ISAPnP code. + * + * There can be only one ISAPnP bus in a system. Once the read port + * is known and all cards have been allocated CSNs, there's nothing to + * be gained by re-scanning for cards. + * + * External code (e.g. the ISAPnP ROM prefix) may already know the + * read port address, in which case it can store it in + * #isapnp_read_port. Note that setting the read port address in this + * way will prevent further isolation from taking place; you should + * set the read port address only if you know that devices have + * already been allocated CSNs. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * ISAPnP Read Port address. + * + * ROM prefix may be able to set this address, which is why this is + * non-static. + */ +uint16_t isapnp_read_port; + +static struct isapnp_driver isapnp_drivers[0] + __table_start ( struct isapnp_driver, isapnp_drivers ); +static struct isapnp_driver isapnp_drivers_end[0] + __table_end ( struct isapnp_driver, isapnp_drivers ); + +static void isapnpbus_remove ( struct root_device *rootdev ); + +/* + * ISAPnP utility functions + * + */ + +#define ISAPNP_CARD_ID_FMT "ID %04x:%04x (\"%s\") serial %x" +#define ISAPNP_CARD_ID_DATA(identifier) \ + (identifier)->vendor_id, (identifier)->prod_id, \ + isa_id_string ( (identifier)->vendor_id, (identifier)->prod_id ), \ + (identifier)->serial +#define ISAPNP_DEV_ID_FMT "ID %04x:%04x (\"%s\")" +#define ISAPNP_DEV_ID_DATA(isapnp) \ + (isapnp)->vendor_id, (isapnp)->prod_id, \ + isa_id_string ( (isapnp)->vendor_id, (isapnp)->prod_id ) + +static inline void isapnp_write_address ( unsigned int address ) { + outb ( address, ISAPNP_ADDRESS ); +} + +static inline void isapnp_write_data ( unsigned int data ) { + outb ( data, ISAPNP_WRITE_DATA ); +} + +static inline unsigned int isapnp_read_data ( void ) { + return inb ( isapnp_read_port ); +} + +static inline void isapnp_write_byte ( unsigned int address, + unsigned int value ) { + isapnp_write_address ( address ); + isapnp_write_data ( value ); +} + +static inline unsigned int isapnp_read_byte ( unsigned int address ) { + isapnp_write_address ( address ); + return isapnp_read_data (); +} + +static inline unsigned int isapnp_read_word ( unsigned int address ) { + /* Yes, they're in big-endian order */ + return ( ( isapnp_read_byte ( address ) << 8 ) + | isapnp_read_byte ( address + 1 ) ); +} + +/** Inform cards of a new read port address */ +static inline void isapnp_set_read_port ( void ) { + isapnp_write_byte ( ISAPNP_READPORT, ( isapnp_read_port >> 2 ) ); +} + +/** + * Enter the Isolation state. + * + * Only cards currently in the Sleep state will respond to this + * command. + */ +static inline void isapnp_serialisolation ( void ) { + isapnp_write_address ( ISAPNP_SERIALISOLATION ); +} + +/** + * Enter the Wait for Key state. + * + * All cards will respond to this command, regardless of their current + * state. + */ +static inline void isapnp_wait_for_key ( void ) { + isapnp_write_byte ( ISAPNP_CONFIGCONTROL, ISAPNP_CONFIG_WAIT_FOR_KEY ); +} + +/** + * Reset (i.e. remove) Card Select Number. + * + * Only cards currently in the Sleep state will respond to this + * command. + */ +static inline void isapnp_reset_csn ( void ) { + isapnp_write_byte ( ISAPNP_CONFIGCONTROL, ISAPNP_CONFIG_RESET_CSN ); +} + +/** + * Place a specified card into the Config state. + * + * @v csn Card Select Number + * @ret None - + * @err None - + * + * Only cards currently in the Sleep, Isolation, or Config states will + * respond to this command. The card that has the specified CSN will + * enter the Config state, all other cards will enter the Sleep state. + */ +static inline void isapnp_wake ( uint8_t csn ) { + isapnp_write_byte ( ISAPNP_WAKE, csn ); +} + +static inline unsigned int isapnp_read_resourcedata ( void ) { + return isapnp_read_byte ( ISAPNP_RESOURCEDATA ); +} + +static inline unsigned int isapnp_read_status ( void ) { + return isapnp_read_byte ( ISAPNP_STATUS ); +} + +/** + * Assign a Card Select Number to a card, and enter the Config state. + * + * @v csn Card Select Number + * + * Only cards in the Isolation state will respond to this command. + * The isolation protocol is designed so that only one card will + * remain in the Isolation state by the time the isolation protocol + * completes. + */ +static inline void isapnp_write_csn ( unsigned int csn ) { + isapnp_write_byte ( ISAPNP_CARDSELECTNUMBER, csn ); +} + +static inline void isapnp_logicaldevice ( unsigned int logdev ) { + isapnp_write_byte ( ISAPNP_LOGICALDEVICENUMBER, logdev ); +} + +static inline void isapnp_activate ( unsigned int logdev ) { + isapnp_logicaldevice ( logdev ); + isapnp_write_byte ( ISAPNP_ACTIVATE, 1 ); +} + +static inline void isapnp_deactivate ( unsigned int logdev ) { + isapnp_logicaldevice ( logdev ); + isapnp_write_byte ( ISAPNP_ACTIVATE, 0 ); +} + +static inline unsigned int isapnp_read_iobase ( unsigned int index ) { + return isapnp_read_word ( ISAPNP_IOBASE ( index ) ); +} + +static inline unsigned int isapnp_read_irqno ( unsigned int index ) { + return isapnp_read_byte ( ISAPNP_IRQNO ( index ) ); +} + +static void isapnp_delay ( void ) { + udelay ( 1000 ); +} + +/** + * Linear feedback shift register. + * + * @v lfsr Current value of the LFSR + * @v input_bit Current input bit to the LFSR + * @ret lfsr Next value of the LFSR + * + * This routine implements the linear feedback shift register as + * described in Appendix B of the PnP ISA spec. The hardware + * implementation uses eight D-type latches and two XOR gates. I + * think this is probably the smallest possible implementation in + * software. Six instructions when input_bit is a constant 0 (for + * isapnp_send_key). :) + */ +static inline unsigned int isapnp_lfsr_next ( unsigned int lfsr, + unsigned int input_bit ) { + register uint8_t lfsr_next; + + lfsr_next = lfsr >> 1; + lfsr_next |= ( ( ( lfsr ^ lfsr_next ) ^ input_bit ) ) << 7; + return lfsr_next; +} + +/** + * Send the ISAPnP initiation key. + * + * Sending the key causes all ISAPnP cards that are currently in the + * Wait for Key state to transition into the Sleep state. + */ +static void isapnp_send_key ( void ) { + unsigned int i; + unsigned int lfsr; + + isapnp_delay(); + isapnp_write_address ( 0x00 ); + isapnp_write_address ( 0x00 ); + + lfsr = ISAPNP_LFSR_SEED; + for ( i = 0 ; i < 32 ; i++ ) { + isapnp_write_address ( lfsr ); + lfsr = isapnp_lfsr_next ( lfsr, 0 ); + } +} + +/** + * Compute ISAPnP identifier checksum + * + * @v identifier ISAPnP identifier + * @ret checksum Expected checksum value + */ +static unsigned int isapnp_checksum ( struct isapnp_identifier *identifier ) { + unsigned int i, j; + unsigned int lfsr; + unsigned int byte; + + lfsr = ISAPNP_LFSR_SEED; + for ( i = 0 ; i < 8 ; i++ ) { + byte = * ( ( ( uint8_t * ) identifier ) + i ); + for ( j = 0 ; j < 8 ; j++ ) { + lfsr = isapnp_lfsr_next ( lfsr, byte ); + byte >>= 1; + } + } + return lfsr; +} + +/* + * Read a byte of resource data from the current location + * + * @ret byte Byte of resource data + */ +static inline unsigned int isapnp_peek_byte ( void ) { + unsigned int i; + + /* Wait for data to be ready */ + for ( i = 0 ; i < 20 ; i++ ) { + if ( isapnp_read_status() & 0x01 ) { + /* Byte ready - read it */ + return isapnp_read_resourcedata(); + } + isapnp_delay(); + } + /* Data never became ready - return 0xff */ + return 0xff; +} + +/** + * Read resource data. + * + * @v buf Buffer in which to store data, or NULL + * @v bytes Number of bytes to read + * + * Resource data is read from the current location. If #buf is NULL, + * the data is discarded. + */ +static void isapnp_peek ( void *buf, size_t len ) { + unsigned int i; + unsigned int byte; + + for ( i = 0 ; i < len ; i++) { + byte = isapnp_peek_byte(); + if ( buf ) + * ( ( uint8_t * ) buf + i ) = byte; + } +} + +/** + * Find a tag within the resource data. + * + * @v wanted_tag The tag that we're looking for + * @v buf Buffer in which to store the tag's contents + * @v len Length of buffer + * @ret rc Return status code + * + * Scan through the resource data until we find a particular tag, and + * read its contents into a buffer. + */ +static int isapnp_find_tag ( unsigned int wanted_tag, void *buf, size_t len ) { + unsigned int tag; + unsigned int tag_len; + + DBG2 ( "ISAPnP read tag" ); + do { + tag = isapnp_peek_byte(); + if ( ISAPNP_IS_SMALL_TAG ( tag ) ) { + tag_len = ISAPNP_SMALL_TAG_LEN ( tag ); + tag = ISAPNP_SMALL_TAG_NAME ( tag ); + } else { + tag_len = ( isapnp_peek_byte() + + ( isapnp_peek_byte() << 8 ) ); + tag = ISAPNP_LARGE_TAG_NAME ( tag ); + } + DBG2 ( " %02x (%02x)", tag, tag_len ); + if ( tag == wanted_tag ) { + if ( len > tag_len ) + len = tag_len; + isapnp_peek ( buf, len ); + DBG2 ( "\n" ); + return 0; + } else { + isapnp_peek ( NULL, tag_len ); + } + } while ( tag != ISAPNP_TAG_END ); + DBG2 ( "\n" ); + return -ENOENT; +} + +/** + * Find specified Logical Device ID tag + * + * @v logdev Logical device ID + * @v logdevid Logical device ID structure to fill in + * @ret rc Return status code + */ +static int isapnp_find_logdevid ( unsigned int logdev, + struct isapnp_logdevid *logdevid ) { + unsigned int i; + int rc; + + for ( i = 0 ; i <= logdev ; i++ ) { + if ( ( rc = isapnp_find_tag ( ISAPNP_TAG_LOGDEVID, logdevid, + sizeof ( *logdevid ) ) ) != 0 ) + return rc; + } + return 0; +} + +/** + * Try isolating ISAPnP cards at the current read port. + * + * @ret \>0 Number of ISAPnP cards found + * @ret 0 There are no ISAPnP cards in the system + * @ret \<0 A conflict was detected; try a new read port + * @err None - + * + * The state diagram on page 18 (PDF page 24) of the PnP ISA spec + * gives the best overview of what happens here. + */ +static int isapnp_try_isolate ( void ) { + struct isapnp_identifier identifier; + unsigned int i, j; + unsigned int seen_55aa, seen_life; + unsigned int csn = 0; + unsigned int data; + unsigned int byte; + + DBG ( "ISAPnP attempting isolation at read port %04x\n", + isapnp_read_port ); + + /* Place all cards into the Sleep state, whatever state + * they're currently in. + */ + isapnp_wait_for_key(); + isapnp_send_key(); + + /* Reset all assigned CSNs */ + isapnp_reset_csn(); + isapnp_delay(); + isapnp_delay(); + + /* Place all cards into the Isolation state */ + isapnp_wait_for_key (); + isapnp_send_key(); + isapnp_wake ( 0x00 ); + + /* Set the read port */ + isapnp_set_read_port(); + isapnp_delay(); + + while ( 1 ) { + + /* All cards that do not have assigned CSNs are + * currently in the Isolation state, each time we go + * through this loop. + */ + + /* Initiate serial isolation */ + isapnp_serialisolation(); + isapnp_delay(); + + /* Read identifier serially via the ISAPnP read port. */ + memset ( &identifier, 0, sizeof ( identifier ) ); + seen_55aa = seen_life = 0; + for ( i = 0 ; i < 9 ; i++ ) { + byte = 0; + for ( j = 0 ; j < 8 ; j++ ) { + data = isapnp_read_data(); + isapnp_delay(); + data = ( ( data << 8 ) | isapnp_read_data() ); + isapnp_delay(); + byte >>= 1; + if ( data != 0xffff ) { + seen_life++; + if ( data == 0x55aa ) { + byte |= 0x80; + seen_55aa++; + } + } + } + *( ( ( uint8_t * ) &identifier ) + i ) = byte; + } + + /* If we didn't see any 55aa patterns, stop here */ + if ( ! seen_55aa ) { + if ( csn ) { + DBG ( "ISAPnP found no more cards\n" ); + } else { + if ( seen_life ) { + DBG ( "ISAPnP saw life but no cards, " + "trying new read port\n" ); + csn = -1; + } else { + DBG ( "ISAPnP saw no signs of life, " + "abandoning isolation\n" ); + } + } + break; + } + + /* If the checksum was invalid stop here */ + if ( identifier.checksum != isapnp_checksum ( &identifier) ) { + DBG ( "ISAPnP found malformed card " + ISAPNP_CARD_ID_FMT "\n with checksum %02x " + "(should be %02x), trying new read port\n", + ISAPNP_CARD_ID_DATA ( &identifier ), + identifier.checksum, + isapnp_checksum ( &identifier) ); + csn = -1; + break; + } + + /* Give the device a CSN */ + csn++; + DBG ( "ISAPnP found card " ISAPNP_CARD_ID_FMT + ", assigning CSN %02x\n", + ISAPNP_CARD_ID_DATA ( &identifier ), csn ); + + isapnp_write_csn ( csn ); + isapnp_delay(); + + /* Send this card back to Sleep and force all cards + * without a CSN into Isolation state + */ + isapnp_wake ( 0x00 ); + isapnp_delay(); + } + + /* Place all cards in Wait for Key state */ + isapnp_wait_for_key(); + + /* Return number of cards found */ + if ( csn > 0 ) { + DBG ( "ISAPnP found %d cards at read port %04x\n", + csn, isapnp_read_port ); + } + return csn; +} + +/** + * Find a valid read port and isolate all ISAPnP cards. + * + */ +static void isapnp_isolate ( void ) { + for ( isapnp_read_port = ISAPNP_READ_PORT_START ; + isapnp_read_port <= ISAPNP_READ_PORT_MAX ; + isapnp_read_port += ISAPNP_READ_PORT_STEP ) { + /* Avoid problematic locations such as the NE2000 + * probe space + */ + if ( ( isapnp_read_port >= 0x280 ) && + ( isapnp_read_port <= 0x380 ) ) + continue; + + /* If we detect any ISAPnP cards at this location, stop */ + if ( isapnp_try_isolate() >= 0 ) + return; + } +} + +/** + * Activate or deactivate an ISAPnP device. + * + * @v isapnp ISAPnP device + * @v activation True to enable, False to disable the device + * @ret None - + * @err None - + * + * This routine simply activates the device in its current + * configuration, or deactivates the device. It does not attempt any + * kind of resource arbitration. + * + */ +void isapnp_device_activation ( struct isapnp_device *isapnp, + int activation ) { + /* Wake the card and select the logical device */ + isapnp_wait_for_key (); + isapnp_send_key (); + isapnp_wake ( isapnp->csn ); + isapnp_logicaldevice ( isapnp->logdev ); + + /* Activate/deactivate the logical device */ + isapnp_activate ( activation ); + isapnp_delay(); + + /* Return all cards to Wait for Key state */ + isapnp_wait_for_key (); + + DBG ( "ISAPnP %s device %02x:%02x\n", + ( activation ? "activated" : "deactivated" ), + isapnp->csn, isapnp->logdev ); +} + +/** + * Probe an ISAPnP device + * + * @v isapnp ISAPnP device + * @ret rc Return status code + * + * Searches for a driver for the ISAPnP device. If a driver is found, + * its probe() routine is called. + */ +static int isapnp_probe ( struct isapnp_device *isapnp ) { + struct isapnp_driver *driver; + struct isapnp_device_id *id; + unsigned int i; + int rc; + + DBG ( "Adding ISAPnP device %02x:%02x (%04x:%04x (\"%s\") " + "io %x irq %d)\n", isapnp->csn, isapnp->logdev, + isapnp->vendor_id, isapnp->prod_id, + isa_id_string ( isapnp->vendor_id, isapnp->prod_id ), + isapnp->ioaddr, isapnp->irqno ); + + for ( driver = isapnp_drivers; driver < isapnp_drivers_end; driver++ ){ + for ( i = 0 ; i < driver->id_count ; i++ ) { + id = &driver->ids[i]; + if ( id->vendor_id != isapnp->vendor_id ) + continue; + if ( ISA_PROD_ID ( id->prod_id ) != + ISA_PROD_ID ( isapnp->prod_id ) ) + continue; + isapnp->driver = driver; + isapnp->driver_name = id->name; + DBG ( "...using driver %s\n", isapnp->driver_name ); + if ( ( rc = driver->probe ( isapnp, id ) ) != 0 ) { + DBG ( "......probe failed\n" ); + continue; + } + return 0; + } + } + + DBG ( "...no driver found\n" ); + return -ENOTTY; +} + +/** + * Remove an ISAPnP device + * + * @v isapnp ISAPnP device + */ +static void isapnp_remove ( struct isapnp_device *isapnp ) { + isapnp->driver->remove ( isapnp ); + DBG ( "Removed ISAPnP device %02x:%02x\n", + isapnp->csn, isapnp->logdev ); +} + +/** + * Probe ISAPnP root bus + * + * @v rootdev ISAPnP bus root device + * + * Scans the ISAPnP bus for devices and registers all devices it can + * find. + */ +static int isapnpbus_probe ( struct root_device *rootdev ) { + struct isapnp_device *isapnp = NULL; + struct isapnp_identifier identifier; + struct isapnp_logdevid logdevid; + unsigned int csn; + unsigned int logdev; + int rc; + + /* Perform isolation if it hasn't yet been done */ + if ( ! isapnp_read_port ) + isapnp_isolate(); + + for ( csn = 1 ; csn <= 0xff ; csn++ ) { + for ( logdev = 0 ; logdev <= 0xff ; logdev++ ) { + + /* Allocate struct isapnp_device */ + if ( ! isapnp ) + isapnp = malloc ( sizeof ( *isapnp ) ); + if ( ! isapnp ) { + rc = -ENOMEM; + goto err; + } + memset ( isapnp, 0, sizeof ( *isapnp ) ); + isapnp->csn = csn; + isapnp->logdev = logdev; + + /* Wake the card */ + isapnp_wait_for_key(); + isapnp_send_key(); + isapnp_wake ( csn ); + + /* Read the card identifier */ + isapnp_peek ( &identifier, sizeof ( identifier ) ); + + /* No card with this CSN; stop here */ + if ( identifier.vendor_id & 0x80 ) + goto done; + + /* Find the Logical Device ID tag */ + if ( ( rc = isapnp_find_logdevid ( logdev, + &logdevid ) ) != 0){ + /* No more logical devices; go to next CSN */ + break; + } + + /* Select the logical device */ + isapnp_logicaldevice ( logdev ); + + /* Populate struct isapnp_device */ + isapnp->vendor_id = logdevid.vendor_id; + isapnp->prod_id = logdevid.prod_id; + isapnp->ioaddr = isapnp_read_iobase ( 0 ); + isapnp->irqno = isapnp_read_irqno ( 0 ); + + /* Return all cards to Wait for Key state */ + isapnp_wait_for_key(); + + /* Add to device hierarchy */ + snprintf ( isapnp->dev.name, + sizeof ( isapnp->dev.name ), + "ISAPnP%02x:%02x", csn, logdev ); + isapnp->dev.desc.bus_type = BUS_TYPE_ISAPNP; + isapnp->dev.desc.vendor = isapnp->vendor_id; + isapnp->dev.desc.device = isapnp->prod_id; + isapnp->dev.desc.ioaddr = isapnp->ioaddr; + isapnp->dev.desc.irq = isapnp->irqno; + isapnp->dev.parent = &rootdev->dev; + list_add ( &isapnp->dev.siblings, + &rootdev->dev.children ); + INIT_LIST_HEAD ( &isapnp->dev.children ); + + /* Look for a driver */ + if ( isapnp_probe ( isapnp ) == 0 ) { + /* isapnpdev registered, we can drop our ref */ + isapnp = NULL; + } else { + /* Not registered; re-use struct */ + list_del ( &isapnp->dev.siblings ); + } + } + } + + done: + free ( isapnp ); + return 0; + + err: + free ( isapnp ); + isapnpbus_remove ( rootdev ); + return rc; +} + +/** + * Remove ISAPnP root bus + * + * @v rootdev ISAPnP bus root device + */ +static void isapnpbus_remove ( struct root_device *rootdev ) { + struct isapnp_device *isapnp; + struct isapnp_device *tmp; + + list_for_each_entry_safe ( isapnp, tmp, &rootdev->dev.children, + dev.siblings ) { + isapnp_remove ( isapnp ); + list_del ( &isapnp->dev.siblings ); + free ( isapnp ); + } +} + +/** ISAPnP bus root device driver */ +static struct root_driver isapnp_root_driver = { + .probe = isapnpbus_probe, + .remove = isapnpbus_remove, +}; + +/** ISAPnP bus root device */ +struct root_device isapnp_root_device __root_device = { + .dev = { .name = "ISAPnP" }, + .driver = &isapnp_root_driver, +}; diff --git a/gpxe/src/drivers/bus/mca.c b/gpxe/src/drivers/bus/mca.c new file mode 100644 index 0000000..e923381 --- /dev/null +++ b/gpxe/src/drivers/bus/mca.c @@ -0,0 +1,180 @@ +/* + * MCA bus driver code + * + * Abstracted from 3c509.c. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct mca_driver mca_drivers[0] + __table_start ( struct mca_driver, mca_drivers ); +static struct mca_driver mca_drivers_end[0] + __table_end ( struct mca_driver, mca_drivers ); + +static void mcabus_remove ( struct root_device *rootdev ); + +/** + * Probe an MCA device + * + * @v mca MCA device + * @ret rc Return status code + * + * Searches for a driver for the MCA device. If a driver is found, + * its probe() routine is called. + */ +static int mca_probe ( struct mca_device *mca ) { + struct mca_driver *driver; + struct mca_device_id *id; + unsigned int i; + int rc; + + DBG ( "Adding MCA slot %02x (ID %04x POS " + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x)\n", + mca->slot, MCA_ID ( mca ), + mca->pos[0], mca->pos[1], mca->pos[2], mca->pos[3], + mca->pos[4], mca->pos[5], mca->pos[6], mca->pos[7] ); + + for ( driver = mca_drivers; driver < mca_drivers_end; driver++ ){ + for ( i = 0 ; i < driver->id_count ; i++ ) { + id = &driver->ids[i]; + if ( id->id != MCA_ID ( mca ) ) + continue; + mca->driver = driver; + mca->driver_name = id->name; + DBG ( "...using driver %s\n", mca->driver_name ); + if ( ( rc = driver->probe ( mca, id ) ) != 0 ) { + DBG ( "......probe failed\n" ); + continue; + } + return 0; + } + } + + DBG ( "...no driver found\n" ); + return -ENOTTY; +} + +/** + * Remove an MCA device + * + * @v mca MCA device + */ +static void mca_remove ( struct mca_device *mca ) { + mca->driver->remove ( mca ); + DBG ( "Removed MCA device %02x\n", mca->slot ); +} + +/** + * Probe MCA root bus + * + * @v rootdev MCA bus root device + * + * Scans the MCA bus for devices and registers all devices it can + * find. + */ +static int mcabus_probe ( struct root_device *rootdev ) { + struct mca_device *mca = NULL; + unsigned int slot; + int seen_non_ff; + unsigned int i; + int rc; + + for ( slot = 0 ; slot <= MCA_MAX_SLOT_NR ; slot++ ) { + /* Allocate struct mca_device */ + if ( ! mca ) + mca = malloc ( sizeof ( *mca ) ); + if ( ! mca ) { + rc = -ENOMEM; + goto err; + } + memset ( mca, 0, sizeof ( *mca ) ); + mca->slot = slot; + + /* Make sure motherboard setup is off */ + outb_p ( 0xff, MCA_MOTHERBOARD_SETUP_REG ); + + /* Select the slot */ + outb_p ( 0x8 | ( mca->slot & 0xf ), MCA_ADAPTER_SETUP_REG ); + + /* Read the POS registers */ + seen_non_ff = 0; + for ( i = 0 ; i < ( sizeof ( mca->pos ) / + sizeof ( mca->pos[0] ) ) ; i++ ) { + mca->pos[i] = inb_p ( MCA_POS_REG ( i ) ); + if ( mca->pos[i] != 0xff ) + seen_non_ff = 1; + } + + /* Kill all setup modes */ + outb_p ( 0, MCA_ADAPTER_SETUP_REG ); + + /* If all POS registers are 0xff, this means there's no device + * present + */ + if ( ! seen_non_ff ) + continue; + + /* Add to device hierarchy */ + snprintf ( mca->dev.name, sizeof ( mca->dev.name ), + "MCA%02x", slot ); + mca->dev.desc.bus_type = BUS_TYPE_MCA; + mca->dev.desc.vendor = GENERIC_MCA_VENDOR; + mca->dev.desc.device = MCA_ID ( mca ); + mca->dev.parent = &rootdev->dev; + list_add ( &mca->dev.siblings, &rootdev->dev.children ); + INIT_LIST_HEAD ( &mca->dev.children ); + + /* Look for a driver */ + if ( mca_probe ( mca ) == 0 ) { + /* mcadev registered, we can drop our ref */ + mca = NULL; + } else { + /* Not registered; re-use struct */ + list_del ( &mca->dev.siblings ); + } + } + + free ( mca ); + return 0; + + err: + free ( mca ); + mcabus_remove ( rootdev ); + return rc; +} + +/** + * Remove MCA root bus + * + * @v rootdev MCA bus root device + */ +static void mcabus_remove ( struct root_device *rootdev ) { + struct mca_device *mca; + struct mca_device *tmp; + + list_for_each_entry_safe ( mca, tmp, &rootdev->dev.children, + dev.siblings ) { + mca_remove ( mca ); + list_del ( &mca->dev.siblings ); + free ( mca ); + } +} + +/** MCA bus root device driver */ +static struct root_driver mca_root_driver = { + .probe = mcabus_probe, + .remove = mcabus_remove, +}; + +/** MCA bus root device */ +struct root_device mca_root_device __root_device = { + .dev = { .name = "MCA" }, + .driver = &mca_root_driver, +}; diff --git a/gpxe/src/drivers/bus/pci.c b/gpxe/src/drivers/bus/pci.c new file mode 100644 index 0000000..2dc9d43 --- /dev/null +++ b/gpxe/src/drivers/bus/pci.c @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2006 Michael Brown . + * + * Based in part on pci.c from Etherboot 5.4, by Ken Yap and David + * Munro, in turn based on the Linux kernel's PCI implementation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * PCI bus + * + */ + +static struct pci_driver pci_drivers[0] + __table_start ( struct pci_driver, pci_drivers ); +static struct pci_driver pci_drivers_end[0] + __table_end ( struct pci_driver, pci_drivers ); + +static void pcibus_remove ( struct root_device *rootdev ); + +/** + * Read PCI BAR + * + * @v pci PCI device + * @v reg PCI register number + * @ret bar Base address register + * + * Reads the specified PCI base address register, including the flags + * portion. 64-bit BARs will be handled automatically. If the value + * of the 64-bit BAR exceeds the size of an unsigned long (i.e. if the + * high dword is non-zero on a 32-bit platform), then the value + * returned will be zero plus the flags for a 64-bit BAR. Unreachable + * 64-bit BARs are therefore returned as uninitialised 64-bit BARs. + */ +static unsigned long pci_bar ( struct pci_device *pci, unsigned int reg ) { + uint32_t low; + uint32_t high; + + pci_read_config_dword ( pci, reg, &low ); + if ( ( low & (PCI_BASE_ADDRESS_SPACE|PCI_BASE_ADDRESS_MEM_TYPE_MASK) ) + == (PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64) ){ + pci_read_config_dword ( pci, reg + 4, &high ); + if ( high ) { + if ( sizeof ( unsigned long ) > sizeof ( uint32_t ) ) { + return ( ( ( uint64_t ) high << 32 ) | low ); + } else { + DBG ( "Unhandled 64-bit BAR %08x%08x\n", + high, low ); + return PCI_BASE_ADDRESS_MEM_TYPE_64; + } + } + } + return low; +} + +/** + * Find the start of a PCI BAR + * + * @v pci PCI device + * @v reg PCI register number + * @ret start BAR start address + * + * Reads the specified PCI base address register, and returns the + * address portion of the BAR (i.e. without the flags). + * + * If the address exceeds the size of an unsigned long (i.e. if a + * 64-bit BAR has a non-zero high dword on a 32-bit machine), the + * return value will be zero. + */ +unsigned long pci_bar_start ( struct pci_device *pci, unsigned int reg ) { + unsigned long bar; + + bar = pci_bar ( pci, reg ); + if ( (bar & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY ){ + return ( bar & PCI_BASE_ADDRESS_MEM_MASK ); + } else { + return ( bar & PCI_BASE_ADDRESS_IO_MASK ); + } +} + +/** + * Read membase and ioaddr for a PCI device + * + * @v pci PCI device + * + * This scans through all PCI BARs on the specified device. The first + * valid memory BAR is recorded as pci_device::membase, and the first + * valid IO BAR is recorded as pci_device::ioaddr. + * + * 64-bit BARs are handled automatically. On a 32-bit platform, if a + * 64-bit BAR has a non-zero high dword, it will be regarded as + * invalid. + */ +static void pci_read_bases ( struct pci_device *pci ) { + unsigned long bar; + int reg; + + for ( reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4 ) { + bar = pci_bar ( pci, reg ); + if ( bar & PCI_BASE_ADDRESS_SPACE_IO ) { + if ( ! pci->ioaddr ) + pci->ioaddr = + ( bar & PCI_BASE_ADDRESS_IO_MASK ); + } else { + if ( ! pci->membase ) + pci->membase = + ( bar & PCI_BASE_ADDRESS_MEM_MASK ); + /* Skip next BAR if 64-bit */ + if ( bar & PCI_BASE_ADDRESS_MEM_TYPE_64 ) + reg += 4; + } + } +} + +/** + * Enable PCI device + * + * @v pci PCI device + * + * Set device to be a busmaster in case BIOS neglected to do so. Also + * adjust PCI latency timer to a reasonable value, 32. + */ +void adjust_pci_device ( struct pci_device *pci ) { + unsigned short new_command, pci_command; + unsigned char pci_latency; + + pci_read_config_word ( pci, PCI_COMMAND, &pci_command ); + new_command = ( pci_command | PCI_COMMAND_MASTER | + PCI_COMMAND_MEM | PCI_COMMAND_IO ); + if ( pci_command != new_command ) { + DBG ( "PCI BIOS has not enabled device %02x:%02x.%x! " + "Updating PCI command %04x->%04x\n", pci->bus, + PCI_SLOT ( pci->devfn ), PCI_FUNC ( pci->devfn ), + pci_command, new_command ); + pci_write_config_word ( pci, PCI_COMMAND, new_command ); + } + + pci_read_config_byte ( pci, PCI_LATENCY_TIMER, &pci_latency); + if ( pci_latency < 32 ) { + DBG ( "PCI device %02x:%02x.%x latency timer is unreasonably " + "low at %d. Setting to 32.\n", pci->bus, + PCI_SLOT ( pci->devfn ), PCI_FUNC ( pci->devfn ), + pci_latency ); + pci_write_config_byte ( pci, PCI_LATENCY_TIMER, 32); + } +} + +/** + * Probe a PCI device + * + * @v pci PCI device + * @ret rc Return status code + * + * Searches for a driver for the PCI device. If a driver is found, + * its probe() routine is called. + */ +static int pci_probe ( struct pci_device *pci ) { + struct pci_driver *driver; + struct pci_device_id *id; + unsigned int i; + int rc; + + DBG ( "Adding PCI device %02x:%02x.%x (%04x:%04x mem %lx io %lx " + "irq %d)\n", pci->bus, PCI_SLOT ( pci->devfn ), + PCI_FUNC ( pci->devfn ), pci->vendor, pci->device, + pci->membase, pci->ioaddr, pci->irq ); + + for ( driver = pci_drivers ; driver < pci_drivers_end ; driver++ ) { + for ( i = 0 ; i < driver->id_count ; i++ ) { + id = &driver->ids[i]; + if ( ( id->vendor != PCI_ANY_ID ) && + ( id->vendor != pci->vendor ) ) + continue; + if ( ( id->device != PCI_ANY_ID ) && + ( id->device != pci->device ) ) + continue; + pci->driver = driver; + pci->driver_name = id->name; + DBG ( "...using driver %s\n", pci->driver_name ); + if ( ( rc = driver->probe ( pci, id ) ) != 0 ) { + DBG ( "......probe failed\n" ); + continue; + } + return 0; + } + } + + DBG ( "...no driver found\n" ); + return -ENOTTY; +} + +/** + * Remove a PCI device + * + * @v pci PCI device + */ +static void pci_remove ( struct pci_device *pci ) { + pci->driver->remove ( pci ); + DBG ( "Removed PCI device %02x:%02x.%x\n", pci->bus, + PCI_SLOT ( pci->devfn ), PCI_FUNC ( pci->devfn ) ); +} + +/** + * Probe PCI root bus + * + * @v rootdev PCI bus root device + * + * Scans the PCI bus for devices and registers all devices it can + * find. + */ +static int pcibus_probe ( struct root_device *rootdev ) { + struct pci_device *pci = NULL; + unsigned int max_bus; + unsigned int bus; + unsigned int devfn; + uint8_t hdrtype = 0; + uint32_t tmp; + int rc; + + max_bus = pci_max_bus(); + for ( bus = 0 ; bus <= max_bus ; bus++ ) { + for ( devfn = 0 ; devfn <= 0xff ; devfn++ ) { + + /* Allocate struct pci_device */ + if ( ! pci ) + pci = malloc ( sizeof ( *pci ) ); + if ( ! pci ) { + rc = -ENOMEM; + goto err; + } + memset ( pci, 0, sizeof ( *pci ) ); + pci->bus = bus; + pci->devfn = devfn; + + /* Skip all but the first function on + * non-multifunction cards + */ + if ( PCI_FUNC ( devfn ) == 0 ) { + pci_read_config_byte ( pci, PCI_HEADER_TYPE, + &hdrtype ); + } else if ( ! ( hdrtype & 0x80 ) ) { + continue; + } + + /* Check for physical device presence */ + pci_read_config_dword ( pci, PCI_VENDOR_ID, &tmp ); + if ( ( tmp == 0xffffffff ) || ( tmp == 0 ) ) + continue; + + /* Populate struct pci_device */ + pci->vendor = ( tmp & 0xffff ); + pci->device = ( tmp >> 16 ); + pci_read_config_dword ( pci, PCI_REVISION, &tmp ); + pci->class = ( tmp >> 8 ); + pci_read_config_byte ( pci, PCI_INTERRUPT_LINE, + &pci->irq ); + pci_read_bases ( pci ); + + /* Add to device hierarchy */ + snprintf ( pci->dev.name, sizeof ( pci->dev.name ), + "PCI%02x:%02x.%x", bus, + PCI_SLOT ( devfn ), PCI_FUNC ( devfn ) ); + pci->dev.desc.bus_type = BUS_TYPE_PCI; + pci->dev.desc.location = PCI_BUSDEVFN (bus, devfn); + pci->dev.desc.vendor = pci->vendor; + pci->dev.desc.device = pci->device; + pci->dev.desc.class = pci->class; + pci->dev.desc.ioaddr = pci->ioaddr; + pci->dev.desc.irq = pci->irq; + pci->dev.parent = &rootdev->dev; + list_add ( &pci->dev.siblings, &rootdev->dev.children); + INIT_LIST_HEAD ( &pci->dev.children ); + + /* Look for a driver */ + if ( pci_probe ( pci ) == 0 ) { + /* pcidev registered, we can drop our ref */ + pci = NULL; + } else { + /* Not registered; re-use struct pci_device */ + list_del ( &pci->dev.siblings ); + } + } + } + + free ( pci ); + return 0; + + err: + free ( pci ); + pcibus_remove ( rootdev ); + return rc; +} + +/** + * Remove PCI root bus + * + * @v rootdev PCI bus root device + */ +static void pcibus_remove ( struct root_device *rootdev ) { + struct pci_device *pci; + struct pci_device *tmp; + + list_for_each_entry_safe ( pci, tmp, &rootdev->dev.children, + dev.siblings ) { + pci_remove ( pci ); + list_del ( &pci->dev.siblings ); + free ( pci ); + } +} + +/** PCI bus root device driver */ +static struct root_driver pci_root_driver = { + .probe = pcibus_probe, + .remove = pcibus_remove, +}; + +/** PCI bus root device */ +struct root_device pci_root_device __root_device = { + .dev = { .name = "PCI" }, + .driver = &pci_root_driver, +}; diff --git a/gpxe/src/drivers/bus/pciextra.c b/gpxe/src/drivers/bus/pciextra.c new file mode 100644 index 0000000..4603bcb --- /dev/null +++ b/gpxe/src/drivers/bus/pciextra.c @@ -0,0 +1,79 @@ +#include +#include + +/** + * Look for a PCI capability + * + * @v pci PCI device to query + * @v cap Capability code + * @ret address Address of capability, or 0 if not found + * + * Determine whether or not a device supports a given PCI capability. + * Returns the address of the requested capability structure within + * the device's PCI configuration space, or 0 if the device does not + * support it. + */ +int pci_find_capability ( struct pci_device *pci, int cap ) { + uint16_t status; + uint8_t pos, id; + uint8_t hdr_type; + int ttl = 48; + + pci_read_config_word ( pci, PCI_STATUS, &status ); + if ( ! ( status & PCI_STATUS_CAP_LIST ) ) + return 0; + + pci_read_config_byte ( pci, PCI_HEADER_TYPE, &hdr_type ); + switch ( hdr_type & 0x7F ) { + case PCI_HEADER_TYPE_NORMAL: + case PCI_HEADER_TYPE_BRIDGE: + default: + pci_read_config_byte ( pci, PCI_CAPABILITY_LIST, &pos ); + break; + case PCI_HEADER_TYPE_CARDBUS: + pci_read_config_byte ( pci, PCI_CB_CAPABILITY_LIST, &pos ); + break; + } + while ( ttl-- && pos >= 0x40 ) { + pos &= ~3; + pci_read_config_byte ( pci, pos + PCI_CAP_LIST_ID, &id ); + DBG ( "PCI Capability: %d\n", id ); + if ( id == 0xff ) + break; + if ( id == cap ) + return pos; + pci_read_config_byte ( pci, pos + PCI_CAP_LIST_NEXT, &pos ); + } + return 0; +} + +/** + * Find the size of a PCI BAR + * + * @v pci PCI device + * @v reg PCI register number + * @ret size BAR size + * + * It should not be necessary for any Etherboot code to call this + * function. + */ +unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ) { + uint32_t start, size; + + /* Save the original bar */ + pci_read_config_dword ( pci, reg, &start ); + /* Compute which bits can be set */ + pci_write_config_dword ( pci, reg, ~0 ); + pci_read_config_dword ( pci, reg, &size ); + /* Restore the original size */ + pci_write_config_dword ( pci, reg, start ); + /* Find the significant bits */ + if ( start & PCI_BASE_ADDRESS_SPACE_IO ) { + size &= PCI_BASE_ADDRESS_IO_MASK; + } else { + size &= PCI_BASE_ADDRESS_MEM_MASK; + } + /* Find the lowest bit set */ + size = size & ~( size - 1 ); + return size; +} diff --git a/gpxe/src/drivers/bus/virtio-pci.c b/gpxe/src/drivers/bus/virtio-pci.c new file mode 100644 index 0000000..34acce2 --- /dev/null +++ b/gpxe/src/drivers/bus/virtio-pci.c @@ -0,0 +1,64 @@ +/* virtio-pci.c - pci interface for virtio interface + * + * (c) Copyright 2008 Bull S.A.S. + * + * Author: Laurent Vivier + * + * some parts from Linux Virtio PCI driver + * + * Copyright IBM Corp. 2007 + * Authors: Anthony Liguori + * + */ + +#include "etherboot.h" +#include "gpxe/io.h" +#include "gpxe/virtio-ring.h" +#include "gpxe/virtio-pci.h" + +int vp_find_vq(unsigned int ioaddr, int queue_index, + struct vring_virtqueue *vq) +{ + struct vring * vr = &vq->vring; + u16 num; + + /* select the queue */ + + outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_SEL); + + /* check if the queue is available */ + + num = inw(ioaddr + VIRTIO_PCI_QUEUE_NUM); + if (!num) { + printf("ERROR: queue size is 0\n"); + return -1; + } + + if (num > MAX_QUEUE_NUM) { + printf("ERROR: queue size %d > %d\n", num, MAX_QUEUE_NUM); + return -1; + } + + /* check if the queue is already active */ + + if (inl(ioaddr + VIRTIO_PCI_QUEUE_PFN)) { + printf("ERROR: queue already active\n"); + return -1; + } + + vq->queue_index = queue_index; + + /* initialize the queue */ + + vring_init(vr, num, (unsigned char*)&vq->queue); + + /* activate the queue + * + * NOTE: vr->desc is initialized by vring_init() + */ + + outl((unsigned long)virt_to_phys(vr->desc) >> PAGE_SHIFT, + ioaddr + VIRTIO_PCI_QUEUE_PFN); + + return num; +} diff --git a/gpxe/src/drivers/bus/virtio-ring.c b/gpxe/src/drivers/bus/virtio-ring.c new file mode 100644 index 0000000..6415f62 --- /dev/null +++ b/gpxe/src/drivers/bus/virtio-ring.c @@ -0,0 +1,134 @@ +/* virtio-pci.c - virtio ring management + * + * (c) Copyright 2008 Bull S.A.S. + * + * Author: Laurent Vivier + * + * some parts from Linux Virtio Ring + * + * Copyright Rusty Russell IBM Corporation 2007 + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * + */ + +#include "etherboot.h" +#include "gpxe/io.h" +#include "gpxe/virtio-ring.h" +#include "gpxe/virtio-pci.h" + +#define BUG() do { \ + printf("BUG: failure at %s:%d/%s()!\n", \ + __FILE__, __LINE__, __FUNCTION__); \ + while(1); \ +} while (0) +#define BUG_ON(condition) do { if (condition) BUG(); } while (0) + +/* + * vring_free + * + * put at the begin of the free list the current desc[head] + */ + +void vring_detach(struct vring_virtqueue *vq, unsigned int head) +{ + struct vring *vr = &vq->vring; + unsigned int i; + + /* find end of given descriptor */ + + i = head; + while (vr->desc[i].flags & VRING_DESC_F_NEXT) + i = vr->desc[i].next; + + /* link it with free list and point to it */ + + vr->desc[i].next = vq->free_head; + wmb(); + vq->free_head = head; +} + +/* + * vring_get_buf + * + * get a buffer from the used list + * + */ + +int vring_get_buf(struct vring_virtqueue *vq, unsigned int *len) +{ + struct vring *vr = &vq->vring; + struct vring_used_elem *elem; + u32 id; + int ret; + + BUG_ON(!vring_more_used(vq)); + + elem = &vr->used->ring[vq->last_used_idx % vr->num]; + wmb(); + id = elem->id; + if (len != NULL) + *len = elem->len; + + ret = vq->vdata[id]; + + vring_detach(vq, id); + + vq->last_used_idx++; + + return ret; +} + +void vring_add_buf(struct vring_virtqueue *vq, + struct vring_list list[], + unsigned int out, unsigned int in, + int index, int num_added) +{ + struct vring *vr = &vq->vring; + int i, avail, head, prev; + + BUG_ON(out + in == 0); + + prev = 0; + head = vq->free_head; + for (i = head; out; i = vr->desc[i].next, out--) { + + vr->desc[i].flags = VRING_DESC_F_NEXT; + vr->desc[i].addr = (u64)virt_to_phys(list->addr); + vr->desc[i].len = list->length; + prev = i; + list++; + } + for ( ; in; i = vr->desc[i].next, in--) { + + vr->desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE; + vr->desc[i].addr = (u64)virt_to_phys(list->addr); + vr->desc[i].len = list->length; + prev = i; + list++; + } + vr->desc[prev].flags &= ~VRING_DESC_F_NEXT; + + vq->free_head = i; + + vq->vdata[head] = index; + + avail = (vr->avail->idx + num_added) % vr->num; + vr->avail->ring[avail] = head; + wmb(); +} + +void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int num_added) +{ + struct vring *vr = &vq->vring; + + wmb(); + vr->avail->idx += num_added; + + mb(); + if (!(vr->used->flags & VRING_USED_F_NO_NOTIFY)) + vp_notify(ioaddr, vq->queue_index); +} + diff --git a/gpxe/src/drivers/infiniband/MT25218_PRM.h b/gpxe/src/drivers/infiniband/MT25218_PRM.h new file mode 100644 index 0000000..19ca92c --- /dev/null +++ b/gpxe/src/drivers/infiniband/MT25218_PRM.h @@ -0,0 +1,3460 @@ +/* + This software is available to you under a choice of one of two + licenses. You may choose to be licensed under the terms of the GNU + General Public License (GPL) Version 2, available at + , or the OpenIB.org BSD + license, available in the LICENSE.TXT file accompanying this + software. These details are also available at + . + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved. +*/ + +/*** + *** This file was generated at "Tue Nov 22 15:21:23 2005" + *** by: + *** % csp_bf -copyright=/mswg/misc/license-header.txt -prefix arbelprm_ -bits -fixnames MT25218_PRM.csp + ***/ + +#ifndef H_prefix_arbelprm_bits_fixnames_MT25218_PRM_csp_H +#define H_prefix_arbelprm_bits_fixnames_MT25218_PRM_csp_H + +/* UD Address Vector */ + +struct arbelprm_ud_address_vector_st { /* Little Endian */ + pseudo_bit_t pd[0x00018]; /* Protection Domain */ + pseudo_bit_t port_number[0x00002]; /* Port number + 1 - Port 1 + 2 - Port 2 + other - reserved */ + pseudo_bit_t reserved0[0x00006]; +/* -------------- */ + pseudo_bit_t rlid[0x00010]; /* Remote (Destination) LID */ + pseudo_bit_t my_lid_path_bits[0x00007];/* Source LID - the lower 7 bits (upper bits are taken from PortInfo) */ + pseudo_bit_t g[0x00001]; /* Global address enable - if set, GRH will be formed for packet header */ + pseudo_bit_t reserved1[0x00008]; +/* -------------- */ + pseudo_bit_t hop_limit[0x00008]; /* IPv6 hop limit */ + pseudo_bit_t max_stat_rate[0x00003];/* Maximum static rate control. + 0 - 4X injection rate + 1 - 1X injection rate + other - reserved + */ + pseudo_bit_t reserved2[0x00001]; + pseudo_bit_t msg[0x00002]; /* Max Message size, size is 256*2^MSG bytes */ + pseudo_bit_t reserved3[0x00002]; + pseudo_bit_t mgid_index[0x00006]; /* Index to port GID table + mgid_index = (port_number-1) * 2^log_max_gid + gid_index + Where: + 1. log_max_gid is taken from QUERY_DEV_LIM command + 2. gid_index is the index to the GID table */ + pseudo_bit_t reserved4[0x0000a]; +/* -------------- */ + pseudo_bit_t flow_label[0x00014]; /* IPv6 flow label */ + pseudo_bit_t tclass[0x00008]; /* IPv6 TClass */ + pseudo_bit_t sl[0x00004]; /* InfiniBand Service Level (SL) */ +/* -------------- */ + pseudo_bit_t rgid_127_96[0x00020]; /* Remote GID[127:96] */ +/* -------------- */ + pseudo_bit_t rgid_95_64[0x00020]; /* Remote GID[95:64] */ +/* -------------- */ + pseudo_bit_t rgid_63_32[0x00020]; /* Remote GID[63:32] */ +/* -------------- */ + pseudo_bit_t rgid_31_0[0x00020]; /* Remote GID[31:0] if G bit is set. Must be set to 0x2 if G bit is cleared. */ +/* -------------- */ +}; + +/* Send doorbell */ + +struct arbelprm_send_doorbell_st { /* Little Endian */ + pseudo_bit_t nopcode[0x00005]; /* Opcode of descriptor to be executed */ + pseudo_bit_t f[0x00001]; /* Fence bit. If set, descriptor is fenced */ + pseudo_bit_t reserved0[0x00002]; + pseudo_bit_t wqe_counter[0x00010]; /* Modulo-64K counter of WQEs posted to the QP since its creation excluding the newly posted WQEs in this doorbell. Should be zero for the first doorbell on the QP */ + pseudo_bit_t wqe_cnt[0x00008]; /* Number of WQEs posted with this doorbell. Must be grater then zero. */ +/* -------------- */ + pseudo_bit_t nds[0x00006]; /* Next descriptor size (in 16-byte chunks) */ + pseudo_bit_t reserved1[0x00002]; + pseudo_bit_t qpn[0x00018]; /* QP number this doorbell is rung on */ +/* -------------- */ +}; + +/* ACCESS_LAM_inject_errors_input_modifier */ + +struct arbelprm_access_lam_inject_errors_input_modifier_st { /* Little Endian */ + pseudo_bit_t index3[0x00007]; + pseudo_bit_t q3[0x00001]; + pseudo_bit_t index2[0x00007]; + pseudo_bit_t q2[0x00001]; + pseudo_bit_t index1[0x00007]; + pseudo_bit_t q1[0x00001]; + pseudo_bit_t index0[0x00007]; + pseudo_bit_t q0[0x00001]; +/* -------------- */ +}; + +/* ACCESS_LAM_inject_errors_input_parameter */ + +struct arbelprm_access_lam_inject_errors_input_parameter_st { /* Little Endian */ + pseudo_bit_t ba[0x00002]; /* Bank Address */ + pseudo_bit_t da[0x00002]; /* Dimm Address */ + pseudo_bit_t reserved0[0x0001c]; +/* -------------- */ + pseudo_bit_t ra[0x00010]; /* Row Address */ + pseudo_bit_t ca[0x00010]; /* Column Address */ +/* -------------- */ +}; + +/* */ + +struct arbelprm_recv_wqe_segment_next_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00006]; + pseudo_bit_t nda_31_6[0x0001a]; /* Next WQE address, low 32 bit. WQE address must be aligned to 64-byte boundary (6 LSB are forced ZERO). */ +/* -------------- */ + pseudo_bit_t nds[0x00006]; /* Next WQE size in OctoWords (16 bytes). + Zero value in NDS field signals end of WQEs? chain. + */ + pseudo_bit_t reserved1[0x0001a]; +/* -------------- */ +}; + +/* Send wqe segment data inline */ + +struct arbelprm_wqe_segment_data_inline_st { /* Little Endian */ + pseudo_bit_t byte_count[0x0000a]; /* Not including padding for 16Byte chunks */ + pseudo_bit_t reserved0[0x00015]; + pseudo_bit_t always1[0x00001]; +/* -------------- */ + pseudo_bit_t data[0x00018]; /* Data may be more this segment size - in 16Byte chunks */ + pseudo_bit_t reserved1[0x00008]; +/* -------------- */ + pseudo_bit_t reserved2[0x00040]; +/* -------------- */ +}; + +/* Send wqe segment data ptr */ + +struct arbelprm_wqe_segment_data_ptr_st { /* Little Endian */ + pseudo_bit_t byte_count[0x0001f]; + pseudo_bit_t always0[0x00001]; +/* -------------- */ + pseudo_bit_t l_key[0x00020]; +/* -------------- */ + pseudo_bit_t local_address_h[0x00020]; +/* -------------- */ + pseudo_bit_t local_address_l[0x00020]; +/* -------------- */ +}; + +/* Send wqe segment rd */ + +struct arbelprm_local_invalidate_segment_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00040]; +/* -------------- */ + pseudo_bit_t mem_key[0x00018]; + pseudo_bit_t reserved1[0x00008]; +/* -------------- */ + pseudo_bit_t reserved2[0x000a0]; +/* -------------- */ +}; + +/* Fast_Registration_Segment */ + +struct arbelprm_fast_registration_segment_st { /* Little Endian */ + pseudo_bit_t reserved0[0x0001b]; + pseudo_bit_t lr[0x00001]; /* If set - Local Read access will be enabled */ + pseudo_bit_t lw[0x00001]; /* If set - Local Write access will be enabled */ + pseudo_bit_t rr[0x00001]; /* If set - Remote Read access will be enabled */ + pseudo_bit_t rw[0x00001]; /* If set - Remote Write access will be enabled */ + pseudo_bit_t a[0x00001]; /* If set - Remote Atomic access will be enabled */ +/* -------------- */ + pseudo_bit_t pbl_ptr_63_32[0x00020];/* Physical address pointer [63:32] to the physical buffer list */ +/* -------------- */ + pseudo_bit_t mem_key[0x00020]; /* Memory Key on which the fast registration is executed on. */ +/* -------------- */ + pseudo_bit_t page_size[0x00005]; /* Page size used for the region. Actual size is [4K]*2^Page_size bytes. + page_size should be less than 20. */ + pseudo_bit_t reserved1[0x00002]; + pseudo_bit_t zb[0x00001]; /* Zero Based Region */ + pseudo_bit_t pbl_ptr_31_8[0x00018]; /* Physical address pointer [31:8] to the physical buffer list */ +/* -------------- */ + pseudo_bit_t start_address_h[0x00020];/* Start Address[63:32] - Virtual Address where this region starts */ +/* -------------- */ + pseudo_bit_t start_address_l[0x00020];/* Start Address[31:0] - Virtual Address where this region starts */ +/* -------------- */ + pseudo_bit_t reg_len_h[0x00020]; /* Region Length[63:32] */ +/* -------------- */ + pseudo_bit_t reg_len_l[0x00020]; /* Region Length[31:0] */ +/* -------------- */ +}; + +/* Send wqe segment atomic */ + +struct arbelprm_wqe_segment_atomic_st { /* Little Endian */ + pseudo_bit_t swap_add_h[0x00020]; +/* -------------- */ + pseudo_bit_t swap_add_l[0x00020]; +/* -------------- */ + pseudo_bit_t compare_h[0x00020]; +/* -------------- */ + pseudo_bit_t compare_l[0x00020]; +/* -------------- */ +}; + +/* Send wqe segment remote address */ + +struct arbelprm_wqe_segment_remote_address_st { /* Little Endian */ + pseudo_bit_t remote_virt_addr_h[0x00020]; +/* -------------- */ + pseudo_bit_t remote_virt_addr_l[0x00020]; +/* -------------- */ + pseudo_bit_t rkey[0x00020]; +/* -------------- */ + pseudo_bit_t reserved0[0x00020]; +/* -------------- */ +}; + +/* end wqe segment bind */ + +struct arbelprm_wqe_segment_bind_st { /* Little Endian */ + pseudo_bit_t reserved0[0x0001d]; + pseudo_bit_t rr[0x00001]; /* If set, Remote Read Enable for bound window. */ + pseudo_bit_t rw[0x00001]; /* If set, Remote Write Enable for bound window. + */ + pseudo_bit_t a[0x00001]; /* If set, Atomic Enable for bound window. */ +/* -------------- */ + pseudo_bit_t reserved1[0x0001e]; + pseudo_bit_t zb[0x00001]; /* If set, Window is Zero Based. */ + pseudo_bit_t type[0x00001]; /* Window type. + 0 - Type one window + 1 - Type two window + */ +/* -------------- */ + pseudo_bit_t new_rkey[0x00020]; /* The new RKey of window to bind */ +/* -------------- */ + pseudo_bit_t region_lkey[0x00020]; /* Local key of region, which window will be bound to */ +/* -------------- */ + pseudo_bit_t start_address_h[0x00020]; +/* -------------- */ + pseudo_bit_t start_address_l[0x00020]; +/* -------------- */ + pseudo_bit_t length_h[0x00020]; +/* -------------- */ + pseudo_bit_t length_l[0x00020]; +/* -------------- */ +}; + +/* Send wqe segment ud */ + +struct arbelprm_wqe_segment_ud_st { /* Little Endian */ + struct arbelprm_ud_address_vector_st ud_address_vector;/* UD Address Vector */ +/* -------------- */ + pseudo_bit_t destination_qp[0x00018]; + pseudo_bit_t reserved0[0x00008]; +/* -------------- */ + pseudo_bit_t q_key[0x00020]; +/* -------------- */ + pseudo_bit_t reserved1[0x00040]; +/* -------------- */ +}; + +/* Send wqe segment rd */ + +struct arbelprm_wqe_segment_rd_st { /* Little Endian */ + pseudo_bit_t destination_qp[0x00018]; + pseudo_bit_t reserved0[0x00008]; +/* -------------- */ + pseudo_bit_t q_key[0x00020]; +/* -------------- */ + pseudo_bit_t reserved1[0x00040]; +/* -------------- */ +}; + +/* Send wqe segment ctrl */ + +struct arbelprm_wqe_segment_ctrl_send_st { /* Little Endian */ + pseudo_bit_t always1[0x00001]; + pseudo_bit_t s[0x00001]; /* Solicited Event bit. If set, SE (Solicited Event) bit is set in the (last packet of) message. */ + pseudo_bit_t e[0x00001]; /* Event bit. If set, event is generated upon WQE?s completion, if QP is allowed to generate an event. Every WQE with E-bit set generates an event. The C bit must be set on unsignalled QPs if the E bit is set. */ + pseudo_bit_t c[0x00001]; /* Completion Queue bit. Valid for unsignalled QPs only. If set, the CQ is updated upon WQE?s completion */ + pseudo_bit_t ip[0x00001]; /* When set, InfiniHost III Ex will calculate the IP checksum of the IP header that is present immediately after the IPoverIB encapsulation header. In the case of multiple headers (encapsulation), InfiniHost III Ex will calculate the checksum only for the first IP header following the IPoverIB encapsulation header. Not Valid for IPv6 packets */ + pseudo_bit_t tcp_udp[0x00001]; /* When set, InfiniHost III Ex will calculate the TCP/UDP checksum of the packet that is present immediately after the IP header. In the case of multiple headers (encapsulation), InfiniHost III Ex will calculate the checksum only for the first TCP header following the IP header. This bit may be set only if the entire TCP/UDP segment is present in one IB packet */ + pseudo_bit_t reserved0[0x00001]; + pseudo_bit_t so[0x00001]; /* Strong Ordering - when set, the WQE will be executed only after all previous WQEs have been executed. Can be set for RC WQEs only. This bit must be set in type two BIND, Fast Registration and Local invalidate operations. */ + pseudo_bit_t reserved1[0x00018]; +/* -------------- */ + pseudo_bit_t immediate[0x00020]; /* If the OpCode encodes an operation with Immediate (RDMA-write/SEND), This field will hold the Immediate data to be sent. If the OpCode encodes send and invalidate operations, this field holds the Invalidation key to be inserted into the packet; otherwise, this field is reserved. */ +/* -------------- */ +}; + +/* Send wqe segment next */ + +struct arbelprm_wqe_segment_next_st { /* Little Endian */ + pseudo_bit_t nopcode[0x00005]; /* Next Opcode: OpCode to be used in the next WQE. Encodes the type of operation to be executed on the QP: + ?00000? - NOP. WQE with this opcode creates a completion, but does nothing else + ?01000? - RDMA-write + ?01001? - RDMA-Write with Immediate + ?10000? - RDMA-read + ?10001? - Atomic Compare & swap + ?10010? - Atomic Fetch & Add + ?11000? - Bind memory window + + The encoding for the following operations depends on the QP type: + For RC, UC and RD QP: + ?01010? - SEND + ?01011? - SEND with Immediate + + For UD QP: + the encoding depends on the values of bit[31] of the Q_key field in the Datagram Segment (see Table 39, ?Unreliable Datagram Segment Format - Pointers,? on page 101) of + both the current WQE and the next WQE, as follows: + + If the last WQE Q_Key bit[31] is clear and the next WQE Q_key bit[31] is set : + ?01000? - SEND + ?01001? - SEND with Immediate + + otherwise (if the next WQE Q_key bit[31] is cleared, or the last WQE Q_Key bit[31] is set): + ?01010? - SEND + ?01011? - SEND with Immediate + + All other opcode values are RESERVED, and will result in invalid operation execution. */ + pseudo_bit_t reserved0[0x00001]; + pseudo_bit_t nda_31_6[0x0001a]; /* Next WQE address, low 32 bit. WQE address must be aligned to 64-byte boundary (6 LSB are forced ZERO). */ +/* -------------- */ + pseudo_bit_t nds[0x00006]; /* Next WQE size in OctoWords (16 bytes). + Zero value in NDS field signals end of WQEs? chain. + */ + pseudo_bit_t f[0x00001]; /* Fence bit. If set, next WQE will start execution only after all previous Read/Atomic WQEs complete. */ + pseudo_bit_t always1[0x00001]; + pseudo_bit_t reserved1[0x00018]; +/* -------------- */ +}; + +/* Address Path */ + +struct arbelprm_address_path_st { /* Little Endian */ + pseudo_bit_t pkey_index[0x00007]; /* PKey table index */ + pseudo_bit_t reserved0[0x00011]; + pseudo_bit_t port_number[0x00002]; /* Specific port associated with this QP/EE. + 1 - Port 1 + 2 - Port 2 + other - reserved */ + pseudo_bit_t reserved1[0x00006]; +/* -------------- */ + pseudo_bit_t rlid[0x00010]; /* Remote (Destination) LID */ + pseudo_bit_t my_lid_path_bits[0x00007];/* Source LID - the lower 7 bits (upper bits are taken from PortInfo) */ + pseudo_bit_t g[0x00001]; /* Global address enable - if set, GRH will be formed for packet header */ + pseudo_bit_t reserved2[0x00005]; + pseudo_bit_t rnr_retry[0x00003]; /* RNR retry count (see C9-132 in IB spec Vol 1) + 0-6 - number of retries + 7 - infinite */ +/* -------------- */ + pseudo_bit_t hop_limit[0x00008]; /* IPv6 hop limit */ + pseudo_bit_t max_stat_rate[0x00003];/* Maximum static rate control. + 0 - 100% injection rate + 1 - 25% injection rate + 2 - 12.5% injection rate + 3 - 50% injection rate + other - reserved */ + pseudo_bit_t reserved3[0x00005]; + pseudo_bit_t mgid_index[0x00006]; /* Index to port GID table */ + pseudo_bit_t reserved4[0x00005]; + pseudo_bit_t ack_timeout[0x00005]; /* Local ACK timeout - Transport timer for activation of retransmission mechanism. Refer to IB spec Vol1 9.7.6.1.3 for further details. + The transport timer is set to 4.096us*2^ack_timeout, if ack_timeout is 0 then transport timer is disabled. */ +/* -------------- */ + pseudo_bit_t flow_label[0x00014]; /* IPv6 flow label */ + pseudo_bit_t tclass[0x00008]; /* IPv6 TClass */ + pseudo_bit_t sl[0x00004]; /* InfiniBand Service Level (SL) */ +/* -------------- */ + pseudo_bit_t rgid_127_96[0x00020]; /* Remote GID[127:96] */ +/* -------------- */ + pseudo_bit_t rgid_95_64[0x00020]; /* Remote GID[95:64] */ +/* -------------- */ + pseudo_bit_t rgid_63_32[0x00020]; /* Remote GID[63:32] */ +/* -------------- */ + pseudo_bit_t rgid_31_0[0x00020]; /* Remote GID[31:0] */ +/* -------------- */ +}; + +/* HCA Command Register (HCR) */ + +struct arbelprm_hca_command_register_st { /* Little Endian */ + pseudo_bit_t in_param_h[0x00020]; /* Input Parameter: parameter[63:32] or pointer[63:32] to input mailbox (see command description) */ +/* -------------- */ + pseudo_bit_t in_param_l[0x00020]; /* Input Parameter: parameter[31:0] or pointer[31:0] to input mailbox (see command description) */ +/* -------------- */ + pseudo_bit_t input_modifier[0x00020];/* Input Parameter Modifier */ +/* -------------- */ + pseudo_bit_t out_param_h[0x00020]; /* Output Parameter: parameter[63:32] or pointer[63:32] to output mailbox (see command description) */ +/* -------------- */ + pseudo_bit_t out_param_l[0x00020]; /* Output Parameter: parameter[31:0] or pointer[31:0] to output mailbox (see command description) */ +/* -------------- */ + pseudo_bit_t reserved0[0x00010]; + pseudo_bit_t token[0x00010]; /* Software assigned token to the command, to uniquely identify it. The token is returned to the software in the EQE reported. */ +/* -------------- */ + pseudo_bit_t opcode[0x0000c]; /* Command opcode */ + pseudo_bit_t opcode_modifier[0x00004];/* Opcode Modifier, see specific description for each command. */ + pseudo_bit_t reserved1[0x00006]; + pseudo_bit_t e[0x00001]; /* Event Request + 0 - Don't report event (software will poll the GO bit) + 1 - Report event to EQ when the command completes */ + pseudo_bit_t go[0x00001]; /* Go (0=Software ownership for the HCR, 1=Hardware ownership for the HCR) + Software can write to the HCR only if Go bit is cleared. + Software must set the Go bit to trigger the HW to execute the command. Software must not write to this register value other than 1 for the Go bit. */ + pseudo_bit_t status[0x00008]; /* Command execution status report. Valid only if command interface in under SW ownership (Go bit is cleared) + 0 - command completed without error. If different than zero, command execution completed with error. Syndrom encoding is depended on command executed and is defined for each command */ +/* -------------- */ +}; + +/* CQ Doorbell */ + +struct arbelprm_cq_cmd_doorbell_st { /* Little Endian */ + pseudo_bit_t cqn[0x00018]; /* CQ number accessed */ + pseudo_bit_t cmd[0x00003]; /* Command to be executed on CQ + 0x0 - Reserved + 0x1 - Request notification for next Solicited completion event. CQ_param specifies the current CQ Consumer Counter. + 0x2 - Request notification for next Solicited or Unsolicited completion event. CQ_param specifies the current CQ Consumer Counter. + 0x3 - Request notification for multiple completions (Arm-N). CQ_param specifies the value of the CQ Counter that when reached by HW (i.e. HW generates a CQE into this Counter) Event will be generated + Other - Reserved */ + pseudo_bit_t reserved0[0x00001]; + pseudo_bit_t cmd_sn[0x00002]; /* Command Sequence Number - This field should be incremented upon receiving completion notification of the respective CQ. + This transition is done by ringing Request notification for next Solicited, Request notification for next Solicited or Unsolicited + completion or Request notification for multiple completions doorbells after receiving completion notification. + This field is initialized to Zero */ + pseudo_bit_t reserved1[0x00002]; +/* -------------- */ + pseudo_bit_t cq_param[0x00020]; /* parameter to be used by CQ command */ +/* -------------- */ +}; + +/* RD-send doorbell */ + +struct arbelprm_rd_send_doorbell_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00008]; + pseudo_bit_t een[0x00018]; /* End-to-end context number (reliable datagram) + Must be zero for Nop and Bind operations */ +/* -------------- */ + pseudo_bit_t reserved1[0x00008]; + pseudo_bit_t qpn[0x00018]; /* QP number this doorbell is rung on */ +/* -------------- */ + struct arbelprm_send_doorbell_st send_doorbell;/* Send Parameters */ +/* -------------- */ +}; + +/* Multicast Group Member QP */ + +struct arbelprm_mgmqp_st { /* Little Endian */ + pseudo_bit_t qpn_i[0x00018]; /* QPN_i: QP number which is a member in this multicast group. Valid only if Qi bit is set. Length of the QPN_i list is set in INIT_HCA */ + pseudo_bit_t reserved0[0x00007]; + pseudo_bit_t qi[0x00001]; /* Qi: QPN_i is valid */ +/* -------------- */ +}; + +/* vsd */ + +struct arbelprm_vsd_st { /* Little Endian */ + pseudo_bit_t vsd_dw0[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw1[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw2[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw3[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw4[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw5[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw6[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw7[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw8[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw9[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw10[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw11[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw12[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw13[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw14[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw15[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw16[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw17[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw18[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw19[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw20[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw21[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw22[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw23[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw24[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw25[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw26[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw27[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw28[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw29[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw30[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw31[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw32[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw33[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw34[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw35[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw36[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw37[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw38[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw39[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw40[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw41[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw42[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw43[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw44[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw45[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw46[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw47[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw48[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw49[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw50[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw51[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw52[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw53[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw54[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw55[0x00020]; +/* -------------- */ +}; + +/* ACCESS_LAM_inject_errors */ + +struct arbelprm_access_lam_inject_errors_st { /* Little Endian */ + struct arbelprm_access_lam_inject_errors_input_parameter_st access_lam_inject_errors_input_parameter; +/* -------------- */ + struct arbelprm_access_lam_inject_errors_input_modifier_st access_lam_inject_errors_input_modifier; +/* -------------- */ + pseudo_bit_t reserved0[0x00020]; +/* -------------- */ +}; + +/* Logical DIMM Information */ + +struct arbelprm_dimminfo_st { /* Little Endian */ + pseudo_bit_t dimmsize[0x00010]; /* Size of DIMM in units of 2^20 Bytes. This value is valid only when DIMMStatus is 0. */ + pseudo_bit_t reserved0[0x00008]; + pseudo_bit_t dimmstatus[0x00001]; /* DIMM Status + 0 - Enabled + 1 - Disabled + */ + pseudo_bit_t dh[0x00001]; /* When set, the DIMM is Hidden and can not be accessed from the PCI bus. */ + pseudo_bit_t wo[0x00001]; /* When set, the DIMM is write only. + If data integrity is configured (other than none), the DIMM must be + only targeted by write transactions where the address and size are multiples of 16 bytes. */ + pseudo_bit_t reserved1[0x00005]; +/* -------------- */ + pseudo_bit_t spd[0x00001]; /* 0 - DIMM SPD was read from DIMM + 1 - DIMM SPD was read from InfiniHost-III-EX NVMEM */ + pseudo_bit_t sladr[0x00003]; /* SPD Slave Address 3 LSBits. + Valid only if spd bit is 0. */ + pseudo_bit_t sock_num[0x00002]; /* DIMM socket number (for double sided DIMM one of the two numbers will be reported) */ + pseudo_bit_t syn[0x00004]; /* Error syndrome (valid regardless of status value) + 0 - DIMM has no error + 1 - SPD error (e.g. checksum error, no response, error while reading) + 2 - DIMM out of bounds (e.g. DIMM rows number is not between 7 and 14, DIMM type is not 2) + 3 - DIMM conflict (e.g. mix of registered and unbuffered DIMMs, CAS latency conflict) + 5 - DIMM size trimmed due to configuration (size exceeds) + other - Error, reserved + */ + pseudo_bit_t reserved2[0x00016]; +/* -------------- */ + pseudo_bit_t reserved3[0x00040]; +/* -------------- */ + pseudo_bit_t dimm_start_adr_h[0x00020];/* DIMM memory start address [63:32]. This value is valid only when DIMMStatus is 0. */ +/* -------------- */ + pseudo_bit_t dimm_start_adr_l[0x00020];/* DIMM memory start address [31:0]. This value is valid only when DIMMStatus is 0. */ +/* -------------- */ + pseudo_bit_t reserved4[0x00040]; +/* -------------- */ +}; + +/* UAR Parameters */ + +struct arbelprm_uar_params_st { /* Little Endian */ + pseudo_bit_t uar_base_addr_h[0x00020];/* UAR Base (pyhsical) Address [63:32] (QUERY_HCA only) */ +/* -------------- */ + pseudo_bit_t reserved0[0x00014]; + pseudo_bit_t uar_base_addr_l[0x0000c];/* UAR Base (pyhsical) Address [31:20] (QUERY_HCA only) */ +/* -------------- */ + pseudo_bit_t uar_page_sz[0x00008]; /* This field defines the size of each UAR page. + Size of UAR Page is 4KB*2^UAR_Page_Size */ + pseudo_bit_t log_max_uars[0x00004]; /* Number of UARs supported is 2^log_max_UARs */ + pseudo_bit_t reserved1[0x00004]; + pseudo_bit_t log_uar_entry_sz[0x00006];/* Size of UAR Context entry is 2^log_uar_sz in 4KByte pages */ + pseudo_bit_t reserved2[0x0000a]; +/* -------------- */ + pseudo_bit_t reserved3[0x00020]; +/* -------------- */ + pseudo_bit_t uar_scratch_base_addr_h[0x00020];/* Base address of UAR scratchpad [63:32]. + Number of entries in table is 2^log_max_uars. + Table must be aligned to its size */ +/* -------------- */ + pseudo_bit_t uar_scratch_base_addr_l[0x00020];/* Base address of UAR scratchpad [31:0]. + Number of entries in table is 2^log_max_uars. + Table must be aligned to its size. */ +/* -------------- */ + pseudo_bit_t uar_context_base_addr_h[0x00020];/* Base address of UAR Context [63:32]. + Number of entries in table is 2^log_max_uars. + Table must be aligned to its size. */ +/* -------------- */ + pseudo_bit_t uar_context_base_addr_l[0x00020];/* Base address of UAR Context [31:0]. + Number of entries in table is 2^log_max_uars. + Table must be aligned to its size. */ +/* -------------- */ +}; + +/* Translation and Protection Tables Parameters */ + +struct arbelprm_tptparams_st { /* Little Endian */ + pseudo_bit_t mpt_base_adr_h[0x00020];/* MPT - Memory Protection Table base physical address [63:32]. + Entry size is 64 bytes. + Table must be aligned to its size. + Address may be set to 0xFFFFFFFF if address translation and protection is not supported. */ +/* -------------- */ + pseudo_bit_t mpt_base_adr_l[0x00020];/* MPT - Memory Protection Table base physical address [31:0]. + Entry size is 64 bytes. + Table must be aligned to its size. + Address may be set to 0xFFFFFFFF if address translation and protection is not supported. */ +/* -------------- */ + pseudo_bit_t log_mpt_sz[0x00006]; /* Log (base 2) of the number of region/windows entries in the MPT table. */ + pseudo_bit_t reserved0[0x00002]; + pseudo_bit_t pfto[0x00005]; /* Page Fault RNR Timeout - + The field returned in RNR Naks generated when a page fault is detected. + It has no effect when on-demand-paging is not used. */ + pseudo_bit_t reserved1[0x00013]; +/* -------------- */ + pseudo_bit_t reserved2[0x00020]; +/* -------------- */ + pseudo_bit_t mtt_base_addr_h[0x00020];/* MTT - Memory Translation table base physical address [63:32]. + Table must be aligned to its size. + Address may be set to 0xFFFFFFFF if address translation and protection is not supported. */ +/* -------------- */ + pseudo_bit_t mtt_base_addr_l[0x00020];/* MTT - Memory Translation table base physical address [31:0]. + Table must be aligned to its size. + Address may be set to 0xFFFFFFFF if address translation and protection is not supported. */ +/* -------------- */ + pseudo_bit_t reserved3[0x00040]; +/* -------------- */ +}; + +/* Multicast Support Parameters */ + +struct arbelprm_multicastparam_st { /* Little Endian */ + pseudo_bit_t mc_base_addr_h[0x00020];/* Base Address of the Multicast Table [63:32]. + The base address must be aligned to the entry size. + Address may be set to 0xFFFFFFFF if multicast is not supported. */ +/* -------------- */ + pseudo_bit_t mc_base_addr_l[0x00020];/* Base Address of the Multicast Table [31:0]. + The base address must be aligned to the entry size. + Address may be set to 0xFFFFFFFF if multicast is not supported. */ +/* -------------- */ + pseudo_bit_t reserved0[0x00040]; +/* -------------- */ + pseudo_bit_t log_mc_table_entry_sz[0x00010];/* Log2 of the Size of multicast group member (MGM) entry. + Must be greater than 5 (to allow CTRL and GID sections). + That implies the number of QPs per MC table entry. */ + pseudo_bit_t reserved1[0x00010]; +/* -------------- */ + pseudo_bit_t mc_table_hash_sz[0x00011];/* Number of entries in multicast DGID hash table (must be power of 2) + INIT_HCA - the required number of entries + QUERY_HCA - the actual number of entries assigned by firmware (will be less than or equal to the amount required in INIT_HCA) */ + pseudo_bit_t reserved2[0x0000f]; +/* -------------- */ + pseudo_bit_t log_mc_table_sz[0x00005];/* Log2 of the overall number of MC entries in the MCG table (includes both hash and auxiliary tables) */ + pseudo_bit_t reserved3[0x00013]; + pseudo_bit_t mc_hash_fn[0x00003]; /* Multicast hash function + 0 - Default hash function + other - reserved */ + pseudo_bit_t reserved4[0x00005]; +/* -------------- */ + pseudo_bit_t reserved5[0x00020]; +/* -------------- */ +}; + +/* QPC/EEC/CQC/EQC/RDB Parameters */ + +struct arbelprm_qpcbaseaddr_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00080]; +/* -------------- */ + pseudo_bit_t qpc_base_addr_h[0x00020];/* QPC Base Address [63:32] + Table must be aligned on its size */ +/* -------------- */ + pseudo_bit_t log_num_of_qp[0x00005];/* Log base 2 of number of supported QPs */ + pseudo_bit_t reserved1[0x00002]; + pseudo_bit_t qpc_base_addr_l[0x00019];/* QPC Base Address [31:7] + Table must be aligned on its size */ +/* -------------- */ + pseudo_bit_t reserved2[0x00040]; +/* -------------- */ + pseudo_bit_t eec_base_addr_h[0x00020];/* EEC Base Address [63:32] + Table must be aligned on its size. + Address may be set to 0xFFFFFFFF if RD is not supported. */ +/* -------------- */ + pseudo_bit_t log_num_of_ee[0x00005];/* Log base 2 of number of supported EEs. */ + pseudo_bit_t reserved3[0x00002]; + pseudo_bit_t eec_base_addr_l[0x00019];/* EEC Base Address [31:7] + Table must be aligned on its size + Address may be set to 0xFFFFFFFF if RD is not supported. */ +/* -------------- */ + pseudo_bit_t srqc_base_addr_h[0x00020];/* SRQ Context Base Address [63:32] + Table must be aligned on its size + Address may be set to 0xFFFFFFFF if SRQ is not supported. */ +/* -------------- */ + pseudo_bit_t log_num_of_srq[0x00005];/* Log base 2 of number of supported SRQs. */ + pseudo_bit_t srqc_base_addr_l[0x0001b];/* SRQ Context Base Address [31:5] + Table must be aligned on its size + Address may be set to 0xFFFFFFFF if SRQ is not supported. */ +/* -------------- */ + pseudo_bit_t cqc_base_addr_h[0x00020];/* CQC Base Address [63:32] + Table must be aligned on its size */ +/* -------------- */ + pseudo_bit_t log_num_of_cq[0x00005];/* Log base 2 of number of supported CQs. */ + pseudo_bit_t reserved4[0x00001]; + pseudo_bit_t cqc_base_addr_l[0x0001a];/* CQC Base Address [31:6] + Table must be aligned on its size */ +/* -------------- */ + pseudo_bit_t reserved5[0x00040]; +/* -------------- */ + pseudo_bit_t eqpc_base_addr_h[0x00020];/* Extended QPC Base Address [63:32] + Table has same number of entries as QPC table. + Table must be aligned to entry size. */ +/* -------------- */ + pseudo_bit_t eqpc_base_addr_l[0x00020];/* Extended QPC Base Address [31:0] + Table has same number of entries as QPC table. + Table must be aligned to entry size. */ +/* -------------- */ + pseudo_bit_t reserved6[0x00040]; +/* -------------- */ + pseudo_bit_t eeec_base_addr_h[0x00020];/* Extended EEC Base Address [63:32] + Table has same number of entries as EEC table. + Table must be aligned to entry size. + Address may be set to 0xFFFFFFFF if RD is not supported. */ +/* -------------- */ + pseudo_bit_t eeec_base_addr_l[0x00020];/* Extended EEC Base Address [31:0] + Table has same number of entries as EEC table. + Table must be aligned to entry size. + Address may be set to 0xFFFFFFFF if RD is not supported. */ +/* -------------- */ + pseudo_bit_t reserved7[0x00040]; +/* -------------- */ + pseudo_bit_t eqc_base_addr_h[0x00020];/* EQC Base Address [63:32] + Address may be set to 0xFFFFFFFF if EQs are not supported. + Table must be aligned to entry size. */ +/* -------------- */ + pseudo_bit_t log_num_eq[0x00004]; /* Log base 2 of number of supported EQs. + Must be 6 or less in InfiniHost-III-EX. */ + pseudo_bit_t reserved8[0x00002]; + pseudo_bit_t eqc_base_addr_l[0x0001a];/* EQC Base Address [31:6] + Address may be set to 0xFFFFFFFF if EQs are not supported. + Table must be aligned to entry size. */ +/* -------------- */ + pseudo_bit_t reserved9[0x00040]; +/* -------------- */ + pseudo_bit_t rdb_base_addr_h[0x00020];/* Base address of table that holds remote read and remote atomic requests [63:32]. + Address may be set to 0xFFFFFFFF if remote RDMA reads are not supported. + Please refer to QP and EE chapter for further explanation on RDB allocation. */ +/* -------------- */ + pseudo_bit_t rdb_base_addr_l[0x00020];/* Base address of table that holds remote read and remote atomic requests [31:0]. + Table must be aligned to RDB entry size (32 bytes). + Address may be set to zero if remote RDMA reads are not supported. + Please refer to QP and EE chapter for further explanation on RDB allocation. */ +/* -------------- */ + pseudo_bit_t reserved10[0x00040]; +/* -------------- */ +}; + +/* Header_Log_Register */ + +struct arbelprm_header_log_register_st { /* Little Endian */ + pseudo_bit_t place_holder[0x00020]; +/* -------------- */ + pseudo_bit_t reserved0[0x00060]; +/* -------------- */ +}; + +/* Performance Monitors */ + +struct arbelprm_performance_monitors_st { /* Little Endian */ + pseudo_bit_t e0[0x00001]; /* Enables counting of respective performance counter */ + pseudo_bit_t e1[0x00001]; /* Enables counting of respective performance counter */ + pseudo_bit_t e2[0x00001]; /* Enables counting of respective performance counter */ + pseudo_bit_t reserved0[0x00001]; + pseudo_bit_t r0[0x00001]; /* If written to as '1 - resets respective performance counter, if written to az '0 - no change to matter */ + pseudo_bit_t r1[0x00001]; /* If written to as '1 - resets respective performance counter, if written to az '0 - no change to matter */ + pseudo_bit_t r2[0x00001]; /* If written to as '1 - resets respective performance counter, if written to az '0 - no change to matter */ + pseudo_bit_t reserved1[0x00001]; + pseudo_bit_t i0[0x00001]; /* Interrupt enable on respective counter overflow. '1 - interrupt enabled, '0 - interrupt disabled. */ + pseudo_bit_t i1[0x00001]; /* Interrupt enable on respective counter overflow. '1 - interrupt enabled, '0 - interrupt disabled. */ + pseudo_bit_t i2[0x00001]; /* Interrupt enable on respective counter overflow. '1 - interrupt enabled, '0 - interrupt disabled. */ + pseudo_bit_t reserved2[0x00001]; + pseudo_bit_t f0[0x00001]; /* Overflow flag. If set, overflow occurred on respective counter. Cleared if written to as '1 */ + pseudo_bit_t f1[0x00001]; /* Overflow flag. If set, overflow occurred on respective counter. Cleared if written to as '1 */ + pseudo_bit_t f2[0x00001]; /* Overflow flag. If set, overflow occurred on respective counter. Cleared if written to as '1 */ + pseudo_bit_t reserved3[0x00001]; + pseudo_bit_t ev_cnt1[0x00005]; /* Specifies event to be counted by Event_counter1 See XXX for events' definition. */ + pseudo_bit_t reserved4[0x00003]; + pseudo_bit_t ev_cnt2[0x00005]; /* Specifies event to be counted by Event_counter2 See XXX for events' definition. */ + pseudo_bit_t reserved5[0x00003]; +/* -------------- */ + pseudo_bit_t clock_counter[0x00020]; +/* -------------- */ + pseudo_bit_t event_counter1[0x00020]; +/* -------------- */ + pseudo_bit_t event_counter2[0x00020];/* Read/write event counter, counting events specified by EvCntl and EvCnt2 fields repsectively. When the event counter reaches is maximum value of 0xFFFFFF, the next event will cause it to roll over to zero, set F1 or F2 bit respectively and generate interrupt by I1 I2 bit respectively. */ +/* -------------- */ +}; + +/* Receive segment format */ + +struct arbelprm_wqe_segment_ctrl_recv_st { /* Little Endian */ + struct arbelprm_recv_wqe_segment_next_st wqe_segment_next; +/* -------------- */ + pseudo_bit_t reserved0[0x00002]; + pseudo_bit_t reserved1[0x00001]; + pseudo_bit_t reserved2[0x00001]; + pseudo_bit_t reserved3[0x0001c]; +/* -------------- */ + pseudo_bit_t reserved4[0x00020]; +/* -------------- */ +}; + +/* MLX WQE segment format */ + +struct arbelprm_wqe_segment_ctrl_mlx_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00002]; + pseudo_bit_t e[0x00001]; /* WQE event */ + pseudo_bit_t c[0x00001]; /* Create CQE (for "requested signalling" QP) */ + pseudo_bit_t icrc[0x00002]; /* icrc field detemines what to do with the last dword of the packet: 0 - Calculate ICRC and put it instead of last dword. Last dword must be 0x0. 1,2 - reserved. 3 - Leave last dword as is. Last dword must not be 0x0. */ + pseudo_bit_t reserved1[0x00002]; + pseudo_bit_t sl[0x00004]; + pseudo_bit_t max_statrate[0x00004]; + pseudo_bit_t slr[0x00001]; /* 0= take slid from port. 1= take slid from given headers */ + pseudo_bit_t v15[0x00001]; /* Send packet over VL15 */ + pseudo_bit_t reserved2[0x0000e]; +/* -------------- */ + pseudo_bit_t vcrc[0x00010]; /* Packet's VCRC (if not 0 - otherwise computed by HW) */ + pseudo_bit_t rlid[0x00010]; /* Destination LID (must match given headers) */ +/* -------------- */ + pseudo_bit_t reserved3[0x00040]; +/* -------------- */ +}; + +/* Send WQE segment format */ + +struct arbelprm_send_wqe_segment_st { /* Little Endian */ + struct arbelprm_wqe_segment_next_st wqe_segment_next;/* Send wqe segment next */ +/* -------------- */ + struct arbelprm_wqe_segment_ctrl_send_st wqe_segment_ctrl_send;/* Send wqe segment ctrl */ +/* -------------- */ + struct arbelprm_wqe_segment_rd_st wqe_segment_rd;/* Send wqe segment rd */ +/* -------------- */ + struct arbelprm_wqe_segment_ud_st wqe_segment_ud;/* Send wqe segment ud */ +/* -------------- */ + struct arbelprm_wqe_segment_bind_st wqe_segment_bind;/* Send wqe segment bind */ +/* -------------- */ + pseudo_bit_t reserved0[0x00180]; +/* -------------- */ + struct arbelprm_wqe_segment_remote_address_st wqe_segment_remote_address;/* Send wqe segment remote address */ +/* -------------- */ + struct arbelprm_wqe_segment_atomic_st wqe_segment_atomic;/* Send wqe segment atomic */ +/* -------------- */ + struct arbelprm_fast_registration_segment_st fast_registration_segment;/* Fast Registration Segment */ +/* -------------- */ + struct arbelprm_local_invalidate_segment_st local_invalidate_segment;/* local invalidate segment */ +/* -------------- */ + struct arbelprm_wqe_segment_data_ptr_st wqe_segment_data_ptr;/* Send wqe segment data ptr */ +/* -------------- */ + struct arbelprm_wqe_segment_data_inline_st wqe_segment_data_inline;/* Send wqe segment data inline */ +/* -------------- */ + pseudo_bit_t reserved1[0x00200]; +/* -------------- */ +}; + +/* QP and EE Context Entry */ + +struct arbelprm_queue_pair_ee_context_entry_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00008]; + pseudo_bit_t de[0x00001]; /* Send/Receive Descriptor Event enable - if set, events can be generated upon descriptors' completion on send/receive queue (controlled by E bit in WQE). Invalid in EE context */ + pseudo_bit_t reserved1[0x00002]; + pseudo_bit_t pm_state[0x00002]; /* Path migration state (Migrated, Armed or Rearm) + 11-Migrated + 00-Armed + 01-Rearm + 10-Reserved + Should be set to 11 for UD QPs and for QPs which do not support APM */ + pseudo_bit_t reserved2[0x00003]; + pseudo_bit_t st[0x00003]; /* Service type (invalid in EE context): + 000-Reliable Connection + 001-Unreliable Connection + 010-Reliable Datagram + 011-Unreliable Datagram + 111-MLX transport (raw bits injection). Used for management QPs and RAW */ + pseudo_bit_t reserved3[0x00009]; + pseudo_bit_t state[0x00004]; /* QP/EE state: + 0 - RST + 1 - INIT + 2 - RTR + 3 - RTS + 4 - SQEr + 5 - SQD (Send Queue Drained) + 6 - ERR + 7 - Send Queue Draining + 8 - Reserved + 9 - Suspended + A- F - Reserved + (Valid for QUERY_QPEE and ERR2RST_QPEE commands only) */ +/* -------------- */ + pseudo_bit_t reserved4[0x00020]; +/* -------------- */ + pseudo_bit_t sched_queue[0x00004]; /* Schedule queue to be used for WQE scheduling to execution. Determines QOS for this QP. */ + pseudo_bit_t rlky[0x00001]; /* When set this QP can use the Reserved L_Key */ + pseudo_bit_t reserved5[0x00003]; + pseudo_bit_t log_sq_stride[0x00003];/* Stride on the send queue. WQ entry is 16*(2^log_SQ_stride) bytes. + Stride must be equal or bigger then 64 bytes (minimum log_RQ_stride value allowed is 2). */ + pseudo_bit_t log_sq_size[0x00004]; /* Log2 of the Number of WQEs in the Send Queue. */ + pseudo_bit_t reserved6[0x00001]; + pseudo_bit_t log_rq_stride[0x00003];/* Stride on the receive queue. WQ entry is 16*(2^log_RQ_stride) bytes. + Stride must be equal or bigger then 64 bytes (minimum log_RQ_stride value allowed is 2). */ + pseudo_bit_t log_rq_size[0x00004]; /* Log2 of the Number of WQEs in the Receive Queue. */ + pseudo_bit_t reserved7[0x00001]; + pseudo_bit_t msg_max[0x00005]; /* Max message size allowed on the QP. Maximum message size is 2^msg_Max. + Must be equal to MTU for UD and MLX QPs. */ + pseudo_bit_t mtu[0x00003]; /* MTU of the QP (Must be the same for both paths: primary and alternative): + 0x1 - 256 bytes + 0x2 - 512 + 0x3 - 1024 + 0x4 - 2048 + other - reserved + + Should be configured to 0x4 for UD and MLX QPs. */ +/* -------------- */ + pseudo_bit_t usr_page[0x00018]; /* QP (see "non_privileged Access to the HCA Hardware"). Not valid (reserved) in EE context. */ + pseudo_bit_t reserved8[0x00008]; +/* -------------- */ + pseudo_bit_t local_qpn_een[0x00018];/* Local QP/EE number Lower bits determine position of this record in QPC table, and - thus - constrained + This field is valid for QUERY and ERR2RST commands only. */ + pseudo_bit_t reserved9[0x00008]; +/* -------------- */ + pseudo_bit_t remote_qpn_een[0x00018];/* Remote QP/EE number */ + pseudo_bit_t reserved10[0x00008]; +/* -------------- */ + pseudo_bit_t reserved11[0x00040]; +/* -------------- */ + struct arbelprm_address_path_st primary_address_path;/* Primary address path for the QP/EE */ +/* -------------- */ + struct arbelprm_address_path_st alternative_address_path;/* Alternate address path for the QP/EE */ +/* -------------- */ + pseudo_bit_t rdd[0x00018]; /* Reliable Datagram Domain */ + pseudo_bit_t reserved12[0x00008]; +/* -------------- */ + pseudo_bit_t pd[0x00018]; /* QP protection domain. Not valid (reserved) in EE context. */ + pseudo_bit_t reserved13[0x00008]; +/* -------------- */ + pseudo_bit_t wqe_base_adr_h[0x00020];/* Bits 63:32 of WQE address for both SQ and RQ. + Reserved for EE context. */ +/* -------------- */ + pseudo_bit_t wqe_lkey[0x00020]; /* memory key (L-Key) to be used to access WQEs. Not valid (reserved) in EE context. */ +/* -------------- */ + pseudo_bit_t reserved14[0x00003]; + pseudo_bit_t ssc[0x00001]; /* Send Signaled Completion + 1 - all send WQEs generate CQEs. + 0 - only send WQEs with C bit set generate completion. + Not valid (reserved) in EE context. */ + pseudo_bit_t sic[0x00001]; /* If set - Ignore end to end credits on send queue. Not valid (reserved) in EE context. */ + pseudo_bit_t cur_retry_cnt[0x00003];/* Current transport retry counter (QUERY_QPEE only). + The current transport retry counter can vary from retry_count down to 1, where 1 means that the last retry attempt is currently executing. */ + pseudo_bit_t cur_rnr_retry[0x00003];/* Current RNR retry counter (QUERY_QPEE only). + The current RNR retry counter can vary from rnr_retry to 1, where 1 means that the last retry attempt is currently executing. */ + pseudo_bit_t fre[0x00001]; /* Fast Registration Work Request Enabled. (Reserved for EE) */ + pseudo_bit_t reserved15[0x00001]; + pseudo_bit_t sae[0x00001]; /* If set - Atomic operations enabled on send queue. Not valid (reserved) in EE context. */ + pseudo_bit_t swe[0x00001]; /* If set - RDMA - write enabled on send queue. Not valid (reserved) in EE context. */ + pseudo_bit_t sre[0x00001]; /* If set - RDMA - read enabled on send queue. Not valid (reserved) in EE context. */ + pseudo_bit_t retry_count[0x00003]; /* Transport timeout Retry count */ + pseudo_bit_t reserved16[0x00002]; + pseudo_bit_t sra_max[0x00003]; /* Maximum number of outstanding RDMA-read/Atomic operations allowed in the send queue. Maximum number is 2^SRA_Max. Must be zero in EE context. */ + pseudo_bit_t flight_lim[0x00004]; /* Number of outstanding (in-flight) messages on the wire allowed for this send queue. + Number of outstanding messages is 2^Flight_Lim. + Use 0xF for unlimited number of outstanding messages. */ + pseudo_bit_t ack_req_freq[0x00004]; /* ACK required frequency. ACK required bit will be set in every 2^AckReqFreq packets at least. Not valid for RD QP. */ +/* -------------- */ + pseudo_bit_t reserved17[0x00020]; +/* -------------- */ + pseudo_bit_t next_send_psn[0x00018];/* Next PSN to be sent */ + pseudo_bit_t reserved18[0x00008]; +/* -------------- */ + pseudo_bit_t cqn_snd[0x00018]; /* CQ number completions from the send queue to be reported to. Not valid (reserved) in EE context. */ + pseudo_bit_t reserved19[0x00008]; +/* -------------- */ + pseudo_bit_t reserved20[0x00006]; + pseudo_bit_t snd_wqe_base_adr_l[0x0001a];/* While opening (creating) the WQ, this field should contain the address of first descriptor to be posted. Not valid (reserved) in EE context. */ +/* -------------- */ + pseudo_bit_t snd_db_record_index[0x00020];/* Index in the UAR Context Table Entry. + HW uses this index as an offset from the UAR Context Table Entry in order to read this SQ doorbell record. + The entry is obtained via the usr_page field. + Not valid for EE. */ +/* -------------- */ + pseudo_bit_t last_acked_psn[0x00018];/* The last acknowledged PSN for the requester (QUERY_QPEE only) */ + pseudo_bit_t reserved21[0x00008]; +/* -------------- */ + pseudo_bit_t ssn[0x00018]; /* Requester Send Sequence Number (QUERY_QPEE only) */ + pseudo_bit_t reserved22[0x00008]; +/* -------------- */ + pseudo_bit_t reserved23[0x00003]; + pseudo_bit_t rsc[0x00001]; /* 1 - all receive WQEs generate CQEs. + 0 - only receive WQEs with C bit set generate completion. + Not valid (reserved) in EE context. + */ + pseudo_bit_t ric[0x00001]; /* Invalid Credits. + 1 - place "Invalid Credits" to ACKs sent from this queue. + 0 - ACKs report the actual number of end to end credits on the connection. + Not valid (reserved) in EE context. + Must be set to 1 on QPs which are attached to SRQ. */ + pseudo_bit_t reserved24[0x00008]; + pseudo_bit_t rae[0x00001]; /* If set - Atomic operations enabled. on receive queue. Not valid (reserved) in EE context. */ + pseudo_bit_t rwe[0x00001]; /* If set - RDMA - write enabled on receive queue. Not valid (reserved) in EE context. */ + pseudo_bit_t rre[0x00001]; /* If set - RDMA - read enabled on receive queue. Not valid (reserved) in EE context. */ + pseudo_bit_t reserved25[0x00005]; + pseudo_bit_t rra_max[0x00003]; /* Maximum number of outstanding RDMA-read/Atomic operations allowed on receive queue is 2^RRA_Max. + Must be 0 for EE context. */ + pseudo_bit_t reserved26[0x00008]; +/* -------------- */ + pseudo_bit_t next_rcv_psn[0x00018]; /* Next (expected) PSN on receive */ + pseudo_bit_t min_rnr_nak[0x00005]; /* Minimum RNR NAK timer value (TTTTT field encoding according to the IB spec Vol1 9.7.5.2.8). + Not valid (reserved) in EE context. */ + pseudo_bit_t reserved27[0x00003]; +/* -------------- */ + pseudo_bit_t reserved28[0x00005]; + pseudo_bit_t ra_buff_indx[0x0001b]; /* Index to outstanding read/atomic buffer. + This field constructs the address to the RDB for maintaining the incoming RDMA read and atomic requests. */ +/* -------------- */ + pseudo_bit_t cqn_rcv[0x00018]; /* CQ number completions from receive queue to be reported to. Not valid (reserved) in EE context. */ + pseudo_bit_t reserved29[0x00008]; +/* -------------- */ + pseudo_bit_t reserved30[0x00006]; + pseudo_bit_t rcv_wqe_base_adr_l[0x0001a];/* While opening (creating) the WQ, this field should contain the address of first descriptor to be posted. Not valid (reserved) in EE context. */ +/* -------------- */ + pseudo_bit_t rcv_db_record_index[0x00020];/* Index in the UAR Context Table Entry containing the doorbell record for the receive queue. + HW uses this index as an offset from the UAR Context Table Entry in order to read this RQ doorbell record. + The entry is obtained via the usr_page field. + Not valid for EE. */ +/* -------------- */ + pseudo_bit_t q_key[0x00020]; /* Q_Key to be validated against received datagrams. + On send datagrams, if Q_Key[31] specified in the WQE is set, then this Q_Key will be transmitted in the outgoing message. + Not valid (reserved) in EE context. */ +/* -------------- */ + pseudo_bit_t srqn[0x00018]; /* SRQN - Shared Receive Queue Number - specifies the SRQ number from which the QP dequeues receive descriptors. + SRQN is valid only if SRQ bit is set. Not valid (reserved) in EE context. */ + pseudo_bit_t srq[0x00001]; /* SRQ - Shared Receive Queue. If this bit is set, then the QP is associated with a SRQ. Not valid (reserved) in EE context. */ + pseudo_bit_t reserved31[0x00007]; +/* -------------- */ + pseudo_bit_t rmsn[0x00018]; /* Responder current message sequence number (QUERY_QPEE only) */ + pseudo_bit_t reserved32[0x00008]; +/* -------------- */ + pseudo_bit_t sq_wqe_counter[0x00010];/* A 16bits counter that is incremented for each WQE posted to the SQ. + Must be 0x0 in SQ initialization. + (QUERY_QPEE only). */ + pseudo_bit_t rq_wqe_counter[0x00010];/* A 16bits counter that is incremented for each WQE posted to the RQ. + Must be 0x0 in RQ initialization. + (QUERY_QPEE only). */ +/* -------------- */ + pseudo_bit_t reserved33[0x00040]; +/* -------------- */ +}; + +/* Clear Interrupt [63:0] */ + +struct arbelprm_clr_int_st { /* Little Endian */ + pseudo_bit_t clr_int_h[0x00020]; /* Clear Interrupt [63:32] + Write transactions to this register will clear (de-assert) the virtual interrupt output pins of InfiniHost-III-EX. The value to be written in this register is obtained by executing QUERY_ADAPTER command on command interface after system boot. + This register is write-only. Reading from this register will cause undefined result + */ +/* -------------- */ + pseudo_bit_t clr_int_l[0x00020]; /* Clear Interrupt [31:0] + Write transactions to this register will clear (de-assert) the virtual interrupt output pins of InfiniHost-III-EX. The value to be written in this register is obtained by executing QUERY_ADAPTER command on command interface after system boot. + This register is write-only. Reading from this register will cause undefined result */ +/* -------------- */ +}; + +/* EQ_Arm_DB_Region */ + +struct arbelprm_eq_arm_db_region_st { /* Little Endian */ + pseudo_bit_t eq_x_arm_h[0x00020]; /* EQ[63:32] X state. + This register is used to Arm EQs when setting the appropriate bits. */ +/* -------------- */ + pseudo_bit_t eq_x_arm_l[0x00020]; /* EQ[31:0] X state. + This register is used to Arm EQs when setting the appropriate bits. */ +/* -------------- */ +}; + +/* EQ Set CI DBs Table */ + +struct arbelprm_eq_set_ci_table_st { /* Little Endian */ + pseudo_bit_t eq0_set_ci[0x00020]; /* EQ0_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved0[0x00020]; +/* -------------- */ + pseudo_bit_t eq1_set_ci[0x00020]; /* EQ1_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved1[0x00020]; +/* -------------- */ + pseudo_bit_t eq2_set_ci[0x00020]; /* EQ2_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved2[0x00020]; +/* -------------- */ + pseudo_bit_t eq3_set_ci[0x00020]; /* EQ3_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved3[0x00020]; +/* -------------- */ + pseudo_bit_t eq4_set_ci[0x00020]; /* EQ4_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved4[0x00020]; +/* -------------- */ + pseudo_bit_t eq5_set_ci[0x00020]; /* EQ5_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved5[0x00020]; +/* -------------- */ + pseudo_bit_t eq6_set_ci[0x00020]; /* EQ6_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved6[0x00020]; +/* -------------- */ + pseudo_bit_t eq7_set_ci[0x00020]; /* EQ7_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved7[0x00020]; +/* -------------- */ + pseudo_bit_t eq8_set_ci[0x00020]; /* EQ8_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved8[0x00020]; +/* -------------- */ + pseudo_bit_t eq9_set_ci[0x00020]; /* EQ9_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved9[0x00020]; +/* -------------- */ + pseudo_bit_t eq10_set_ci[0x00020]; /* EQ10_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved10[0x00020]; +/* -------------- */ + pseudo_bit_t eq11_set_ci[0x00020]; /* EQ11_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved11[0x00020]; +/* -------------- */ + pseudo_bit_t eq12_set_ci[0x00020]; /* EQ12_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved12[0x00020]; +/* -------------- */ + pseudo_bit_t eq13_set_ci[0x00020]; /* EQ13_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved13[0x00020]; +/* -------------- */ + pseudo_bit_t eq14_set_ci[0x00020]; /* EQ14_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved14[0x00020]; +/* -------------- */ + pseudo_bit_t eq15_set_ci[0x00020]; /* EQ15_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved15[0x00020]; +/* -------------- */ + pseudo_bit_t eq16_set_ci[0x00020]; /* EQ16_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved16[0x00020]; +/* -------------- */ + pseudo_bit_t eq17_set_ci[0x00020]; /* EQ17_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved17[0x00020]; +/* -------------- */ + pseudo_bit_t eq18_set_ci[0x00020]; /* EQ18_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved18[0x00020]; +/* -------------- */ + pseudo_bit_t eq19_set_ci[0x00020]; /* EQ19_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved19[0x00020]; +/* -------------- */ + pseudo_bit_t eq20_set_ci[0x00020]; /* EQ20_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved20[0x00020]; +/* -------------- */ + pseudo_bit_t eq21_set_ci[0x00020]; /* EQ21_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved21[0x00020]; +/* -------------- */ + pseudo_bit_t eq22_set_ci[0x00020]; /* EQ22_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved22[0x00020]; +/* -------------- */ + pseudo_bit_t eq23_set_ci[0x00020]; /* EQ23_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved23[0x00020]; +/* -------------- */ + pseudo_bit_t eq24_set_ci[0x00020]; /* EQ24_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved24[0x00020]; +/* -------------- */ + pseudo_bit_t eq25_set_ci[0x00020]; /* EQ25_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved25[0x00020]; +/* -------------- */ + pseudo_bit_t eq26_set_ci[0x00020]; /* EQ26_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved26[0x00020]; +/* -------------- */ + pseudo_bit_t eq27_set_ci[0x00020]; /* EQ27_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved27[0x00020]; +/* -------------- */ + pseudo_bit_t eq28_set_ci[0x00020]; /* EQ28_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved28[0x00020]; +/* -------------- */ + pseudo_bit_t eq29_set_ci[0x00020]; /* EQ29_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved29[0x00020]; +/* -------------- */ + pseudo_bit_t eq30_set_ci[0x00020]; /* EQ30_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved30[0x00020]; +/* -------------- */ + pseudo_bit_t eq31_set_ci[0x00020]; /* EQ31_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved31[0x00020]; +/* -------------- */ + pseudo_bit_t eq32_set_ci[0x00020]; /* EQ32_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved32[0x00020]; +/* -------------- */ + pseudo_bit_t eq33_set_ci[0x00020]; /* EQ33_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved33[0x00020]; +/* -------------- */ + pseudo_bit_t eq34_set_ci[0x00020]; /* EQ34_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved34[0x00020]; +/* -------------- */ + pseudo_bit_t eq35_set_ci[0x00020]; /* EQ35_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved35[0x00020]; +/* -------------- */ + pseudo_bit_t eq36_set_ci[0x00020]; /* EQ36_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved36[0x00020]; +/* -------------- */ + pseudo_bit_t eq37_set_ci[0x00020]; /* EQ37_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved37[0x00020]; +/* -------------- */ + pseudo_bit_t eq38_set_ci[0x00020]; /* EQ38_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved38[0x00020]; +/* -------------- */ + pseudo_bit_t eq39_set_ci[0x00020]; /* EQ39_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved39[0x00020]; +/* -------------- */ + pseudo_bit_t eq40_set_ci[0x00020]; /* EQ40_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved40[0x00020]; +/* -------------- */ + pseudo_bit_t eq41_set_ci[0x00020]; /* EQ41_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved41[0x00020]; +/* -------------- */ + pseudo_bit_t eq42_set_ci[0x00020]; /* EQ42_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved42[0x00020]; +/* -------------- */ + pseudo_bit_t eq43_set_ci[0x00020]; /* EQ43_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved43[0x00020]; +/* -------------- */ + pseudo_bit_t eq44_set_ci[0x00020]; /* EQ44_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved44[0x00020]; +/* -------------- */ + pseudo_bit_t eq45_set_ci[0x00020]; /* EQ45_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved45[0x00020]; +/* -------------- */ + pseudo_bit_t eq46_set_ci[0x00020]; /* EQ46_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved46[0x00020]; +/* -------------- */ + pseudo_bit_t eq47_set_ci[0x00020]; /* EQ47_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved47[0x00020]; +/* -------------- */ + pseudo_bit_t eq48_set_ci[0x00020]; /* EQ48_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved48[0x00020]; +/* -------------- */ + pseudo_bit_t eq49_set_ci[0x00020]; /* EQ49_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved49[0x00020]; +/* -------------- */ + pseudo_bit_t eq50_set_ci[0x00020]; /* EQ50_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved50[0x00020]; +/* -------------- */ + pseudo_bit_t eq51_set_ci[0x00020]; /* EQ51_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved51[0x00020]; +/* -------------- */ + pseudo_bit_t eq52_set_ci[0x00020]; /* EQ52_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved52[0x00020]; +/* -------------- */ + pseudo_bit_t eq53_set_ci[0x00020]; /* EQ53_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved53[0x00020]; +/* -------------- */ + pseudo_bit_t eq54_set_ci[0x00020]; /* EQ54_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved54[0x00020]; +/* -------------- */ + pseudo_bit_t eq55_set_ci[0x00020]; /* EQ55_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved55[0x00020]; +/* -------------- */ + pseudo_bit_t eq56_set_ci[0x00020]; /* EQ56_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved56[0x00020]; +/* -------------- */ + pseudo_bit_t eq57_set_ci[0x00020]; /* EQ57_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved57[0x00020]; +/* -------------- */ + pseudo_bit_t eq58_set_ci[0x00020]; /* EQ58_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved58[0x00020]; +/* -------------- */ + pseudo_bit_t eq59_set_ci[0x00020]; /* EQ59_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved59[0x00020]; +/* -------------- */ + pseudo_bit_t eq60_set_ci[0x00020]; /* EQ60_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved60[0x00020]; +/* -------------- */ + pseudo_bit_t eq61_set_ci[0x00020]; /* EQ61_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved61[0x00020]; +/* -------------- */ + pseudo_bit_t eq62_set_ci[0x00020]; /* EQ62_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved62[0x00020]; +/* -------------- */ + pseudo_bit_t eq63_set_ci[0x00020]; /* EQ63_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved63[0x00020]; +/* -------------- */ +}; + +/* InfiniHost-III-EX Configuration Registers */ + +struct arbelprm_configuration_registers_st { /* Little Endian */ + pseudo_bit_t reserved0[0x403400]; +/* -------------- */ + struct arbelprm_hca_command_register_st hca_command_interface_register;/* HCA Command Register */ +/* -------------- */ + pseudo_bit_t reserved1[0x3fcb20]; +/* -------------- */ +}; + +/* QP_DB_Record */ + +struct arbelprm_qp_db_record_st { /* Little Endian */ + pseudo_bit_t counter[0x00010]; /* Modulo-64K counter of WQEs posted to the QP since its creation. Should be initialized to zero. */ + pseudo_bit_t reserved0[0x00010]; +/* -------------- */ + pseudo_bit_t reserved1[0x00005]; + pseudo_bit_t res[0x00003]; /* 0x3 for SQ + 0x4 for RQ + 0x5 for SRQ */ + pseudo_bit_t qp_number[0x00018]; /* QP number */ +/* -------------- */ +}; + +/* CQ_ARM_DB_Record */ + +struct arbelprm_cq_arm_db_record_st { /* Little Endian */ + pseudo_bit_t counter[0x00020]; /* CQ counter for the arming request */ +/* -------------- */ + pseudo_bit_t cmd[0x00003]; /* 0x0 - No command + 0x1 - Request notification for next Solicited completion event. Counter filed specifies the current CQ Consumer Counter. + 0x2 - Request notification for next Solicited or Unsolicited completion event. Counter filed specifies the current CQ Consumer counter. + 0x3 - Request notification for multiple completions (Arm-N). Counter filed specifies the value of the CQ Index that when reached by HW (i.e. HW generates a CQE into this Index) Event will be generated + Other - Reserved */ + pseudo_bit_t cmd_sn[0x00002]; /* Command Sequence Number - See Table 35, "CQ Doorbell Layout" for definition of this filed */ + pseudo_bit_t res[0x00003]; /* Must be 0x2 */ + pseudo_bit_t cq_number[0x00018]; /* CQ number */ +/* -------------- */ +}; + +/* CQ_CI_DB_Record */ + +struct arbelprm_cq_ci_db_record_st { /* Little Endian */ + pseudo_bit_t counter[0x00020]; /* CQ counter */ +/* -------------- */ + pseudo_bit_t reserved0[0x00005]; + pseudo_bit_t res[0x00003]; /* Must be 0x1 */ + pseudo_bit_t cq_number[0x00018]; /* CQ number */ +/* -------------- */ +}; + +/* Virtual_Physical_Mapping */ + +struct arbelprm_virtual_physical_mapping_st { /* Little Endian */ + pseudo_bit_t va_h[0x00020]; /* Virtual Address[63:32]. Valid only for MAP_ICM command. */ +/* -------------- */ + pseudo_bit_t reserved0[0x0000c]; + pseudo_bit_t va_l[0x00014]; /* Virtual Address[31:12]. Valid only for MAP_ICM command. */ +/* -------------- */ + pseudo_bit_t pa_h[0x00020]; /* Physical Address[63:32] */ +/* -------------- */ + pseudo_bit_t log2size[0x00006]; /* Log2 of the size in 4KB pages of the physical and virtual contiguous memory that starts at PA_L/H and VA_L/H */ + pseudo_bit_t reserved1[0x00006]; + pseudo_bit_t pa_l[0x00014]; /* Physical Address[31:12] */ +/* -------------- */ +}; + +/* MOD_STAT_CFG */ + +struct arbelprm_mod_stat_cfg_st { /* Little Endian */ + pseudo_bit_t log_max_srqs[0x00005]; /* Log (base 2) of the number of SRQs to allocate (0 if no SRQs are required), valid only if srq bit is set. */ + pseudo_bit_t reserved0[0x00001]; + pseudo_bit_t srq[0x00001]; /* When set SRQs are supported */ + pseudo_bit_t srq_m[0x00001]; /* Modify SRQ parameters */ + pseudo_bit_t reserved1[0x00018]; +/* -------------- */ + pseudo_bit_t reserved2[0x007e0]; +/* -------------- */ +}; + +/* SRQ Context */ + +struct arbelprm_srq_context_st { /* Little Endian */ + pseudo_bit_t srqn[0x00018]; /* SRQ number */ + pseudo_bit_t log_srq_size[0x00004]; /* Log2 of the Number of WQEs in the Receive Queue. + Maximum value is 0x10, i.e. 16M WQEs. */ + pseudo_bit_t state[0x00004]; /* SRQ State: + 1111 - SW Ownership + 0000 - HW Ownership + 0001 - Error + Valid only on QUERY_SRQ and HW2SW_SRQ commands. */ +/* -------------- */ + pseudo_bit_t l_key[0x00020]; /* memory key (L-Key) to be used to access WQEs. */ +/* -------------- */ + pseudo_bit_t srq_db_record_index[0x00020];/* Index in the UAR Context Table Entry containing the doorbell record for the receive queue. + HW uses this index as an offset from the UAR Context Table Entry in order to read this SRQ doorbell record. + The entry is obtained via the usr_page field. */ +/* -------------- */ + pseudo_bit_t usr_page[0x00018]; /* Index (offset) of user page allocated for this SRQ (see "non_privileged Access to the HCA Hardware"). Not valid (reserved) in EE context. */ + pseudo_bit_t reserved0[0x00005]; + pseudo_bit_t log_rq_stride[0x00003];/* Stride (max WQE size) on the receive queue. WQ entry is 16*(2^log_RQ_stride) bytes. */ +/* -------------- */ + pseudo_bit_t wqe_addr_h[0x00020]; /* Bits 63:32 of WQE address (WQE base address) */ +/* -------------- */ + pseudo_bit_t reserved1[0x00006]; + pseudo_bit_t srq_wqe_base_adr_l[0x0001a];/* While opening (creating) the SRQ, this field should contain the address of first descriptor to be posted. */ +/* -------------- */ + pseudo_bit_t pd[0x00018]; /* SRQ protection domain. */ + pseudo_bit_t reserved2[0x00008]; +/* -------------- */ + pseudo_bit_t wqe_cnt[0x00010]; /* WQE count on the SRQ. + Valid only on QUERY_SRQ and HW2SW_SRQ commands. */ + pseudo_bit_t lwm[0x00010]; /* Limit Water Mark - if the LWM is not zero, and the wqe_cnt drops below LWM when a WQE is dequeued from the SRQ, then a SRQ limit event is fired and the LWM is set to zero. */ +/* -------------- */ + pseudo_bit_t srq_wqe_counter[0x00010];/* A 16bits counter that is incremented for each WQE posted to the SQ. + Must be 0x0 in SRQ initialization. + (QUERY_SRQ only). */ + pseudo_bit_t reserved3[0x00010]; +/* -------------- */ + pseudo_bit_t reserved4[0x00060]; +/* -------------- */ +}; + +/* PBL */ + +struct arbelprm_pbl_st { /* Little Endian */ + pseudo_bit_t mtt_0_h[0x00020]; /* First MTT[63:32] */ +/* -------------- */ + pseudo_bit_t mtt_0_l[0x00020]; /* First MTT[31:0] */ +/* -------------- */ + pseudo_bit_t mtt_1_h[0x00020]; /* Second MTT[63:32] */ +/* -------------- */ + pseudo_bit_t mtt_1_l[0x00020]; /* Second MTT[31:0] */ +/* -------------- */ + pseudo_bit_t mtt_2_h[0x00020]; /* Third MTT[63:32] */ +/* -------------- */ + pseudo_bit_t mtt_2_l[0x00020]; /* Third MTT[31:0] */ +/* -------------- */ + pseudo_bit_t mtt_3_h[0x00020]; /* Fourth MTT[63:32] */ +/* -------------- */ + pseudo_bit_t mtt_3_l[0x00020]; /* Fourth MTT[31:0] */ +/* -------------- */ +}; + +/* Performance Counters */ + +struct arbelprm_performance_counters_st { /* Little Endian */ + pseudo_bit_t sqpc_access_cnt[0x00020];/* SQPC cache access count */ +/* -------------- */ + pseudo_bit_t sqpc_miss_cnt[0x00020];/* SQPC cache miss count */ +/* -------------- */ + pseudo_bit_t reserved0[0x00040]; +/* -------------- */ + pseudo_bit_t rqpc_access_cnt[0x00020];/* RQPC cache access count */ +/* -------------- */ + pseudo_bit_t rqpc_miss_cnt[0x00020];/* RQPC cache miss count */ +/* -------------- */ + pseudo_bit_t reserved1[0x00040]; +/* -------------- */ + pseudo_bit_t cqc_access_cnt[0x00020];/* CQC cache access count */ +/* -------------- */ + pseudo_bit_t cqc_miss_cnt[0x00020]; /* CQC cache miss count */ +/* -------------- */ + pseudo_bit_t reserved2[0x00040]; +/* -------------- */ + pseudo_bit_t tpt_access_cnt[0x00020];/* TPT cache access count */ +/* -------------- */ + pseudo_bit_t mpt_miss_cnt[0x00020]; /* MPT cache miss count */ +/* -------------- */ + pseudo_bit_t mtt_miss_cnt[0x00020]; /* MTT cache miss count */ +/* -------------- */ + pseudo_bit_t reserved3[0x00620]; +/* -------------- */ +}; + +/* Transport and CI Error Counters */ + +struct arbelprm_transport_and_ci_error_counters_st { /* Little Endian */ + pseudo_bit_t rq_num_lle[0x00020]; /* Responder - number of local length errors */ +/* -------------- */ + pseudo_bit_t sq_num_lle[0x00020]; /* Requester - number of local length errors */ +/* -------------- */ + pseudo_bit_t rq_num_lqpoe[0x00020]; /* Responder - number local QP operation error */ +/* -------------- */ + pseudo_bit_t sq_num_lqpoe[0x00020]; /* Requester - number local QP operation error */ +/* -------------- */ + pseudo_bit_t rq_num_leeoe[0x00020]; /* Responder - number local EE operation error */ +/* -------------- */ + pseudo_bit_t sq_num_leeoe[0x00020]; /* Requester - number local EE operation error */ +/* -------------- */ + pseudo_bit_t rq_num_lpe[0x00020]; /* Responder - number of local protection errors */ +/* -------------- */ + pseudo_bit_t sq_num_lpe[0x00020]; /* Requester - number of local protection errors */ +/* -------------- */ + pseudo_bit_t rq_num_wrfe[0x00020]; /* Responder - number of CQEs with error. + Incremented each time a CQE with error is generated */ +/* -------------- */ + pseudo_bit_t sq_num_wrfe[0x00020]; /* Requester - number of CQEs with error. + Incremented each time a CQE with error is generated */ +/* -------------- */ + pseudo_bit_t reserved0[0x00020]; +/* -------------- */ + pseudo_bit_t sq_num_mwbe[0x00020]; /* Requester - number of memory window bind errors */ +/* -------------- */ + pseudo_bit_t reserved1[0x00020]; +/* -------------- */ + pseudo_bit_t sq_num_bre[0x00020]; /* Requester - number of bad response errors */ +/* -------------- */ + pseudo_bit_t rq_num_lae[0x00020]; /* Responder - number of local access errors */ +/* -------------- */ + pseudo_bit_t reserved2[0x00040]; +/* -------------- */ + pseudo_bit_t sq_num_rire[0x00020]; /* Requester - number of remote invalid request errors + NAK-Invalid Request on: + 1. Unsupported OpCode: Responder detected an unsupported OpCode. + 2. Unexpected OpCode: Responder detected an error in the sequence of OpCodes, such + as a missing "Last" packet. + Note: there is no PSN error, thus this does not indicate a dropped packet. */ +/* -------------- */ + pseudo_bit_t rq_num_rire[0x00020]; /* Responder - number of remote invalid request errors. + NAK may or may not be sent. + 1. QP Async Affiliated Error: Unsupported or Reserved OpCode (RC,RD only): + Inbound request OpCode was either reserved, or was for a function not supported by this + QP. (E.g. RDMA or ATOMIC on QP not set up for this). + 2. Misaligned ATOMIC: VA does not point to an aligned address on an atomic opera-tion. + 3. Too many RDMA READ or ATOMIC Requests: There were more requests received + and not ACKed than allowed for the connection. + 4. Out of Sequence OpCode, current packet is "First" or "Only": The Responder + detected an error in the sequence of OpCodes; a missing "Last" packet + 5. Out of Sequence OpCode, current packet is not "First" or "Only": The Responder + detected an error in the sequence of OpCodes; a missing "First" packet + 6. Local Length Error: Inbound "Send" request message exceeded the responder.s avail-able + buffer space. + 7. Length error: RDMA WRITE request message contained too much or too little pay-load + data compared to the DMA length advertised in the first or only packet. + 8. Length error: Payload length was not consistent with the opcode: + a: 0 byte <= "only" <= PMTU bytes + b: ("first" or "middle") == PMTU bytes + c: 1byte <= "last" <= PMTU bytes + 9. Length error: Inbound message exceeded the size supported by the CA port. */ +/* -------------- */ + pseudo_bit_t sq_num_rae[0x00020]; /* Requester - number of remote access errors. + NAK-Remote Access Error on: + R_Key Violation: Responder detected an invalid R_Key while executing an RDMA + Request. */ +/* -------------- */ + pseudo_bit_t rq_num_rae[0x00020]; /* Responder - number of remote access errors. + R_Key Violation Responder detected an R_Key violation while executing an RDMA + request. + NAK may or may not be sent. */ +/* -------------- */ + pseudo_bit_t sq_num_roe[0x00020]; /* Requester - number of remote operation errors. + NAK-Remote Operation Error on: + Remote Operation Error: Responder encountered an error, (local to the responder), + which prevented it from completing the request. */ +/* -------------- */ + pseudo_bit_t rq_num_roe[0x00020]; /* Responder - number of remote operation errors. + NAK-Remote Operation Error on: + 1. Malformed WQE: Responder detected a malformed Receive Queue WQE while pro-cessing + the packet. + 2. Remote Operation Error: Responder encountered an error, (local to the responder), + which prevented it from completing the request. */ +/* -------------- */ + pseudo_bit_t sq_num_tree[0x00020]; /* Requester - number of transport retries exceeded errors */ +/* -------------- */ + pseudo_bit_t reserved3[0x00020]; +/* -------------- */ + pseudo_bit_t sq_num_rree[0x00020]; /* Requester - number of RNR nak retries exceeded errors */ +/* -------------- */ + pseudo_bit_t reserved4[0x00020]; +/* -------------- */ + pseudo_bit_t sq_num_lrdve[0x00020]; /* Requester - number of local RDD violation errors */ +/* -------------- */ + pseudo_bit_t rq_num_rirdre[0x00020];/* Responder - number of remote invalid RD request errors */ +/* -------------- */ + pseudo_bit_t reserved5[0x00040]; +/* -------------- */ + pseudo_bit_t sq_num_rabrte[0x00020];/* Requester - number of remote aborted errors */ +/* -------------- */ + pseudo_bit_t reserved6[0x00020]; +/* -------------- */ + pseudo_bit_t sq_num_ieecne[0x00020];/* Requester - number of invalid EE context number errors */ +/* -------------- */ + pseudo_bit_t reserved7[0x00020]; +/* -------------- */ + pseudo_bit_t sq_num_ieecse[0x00020];/* Requester - invalid EE context state errors */ +/* -------------- */ + pseudo_bit_t reserved8[0x00380]; +/* -------------- */ + pseudo_bit_t rq_num_oos[0x00020]; /* Responder - number of out of sequence requests received */ +/* -------------- */ + pseudo_bit_t sq_num_oos[0x00020]; /* Requester - number of out of sequence Naks received */ +/* -------------- */ + pseudo_bit_t rq_num_mce[0x00020]; /* Responder - number of bad multicast packets received */ +/* -------------- */ + pseudo_bit_t reserved9[0x00020]; +/* -------------- */ + pseudo_bit_t rq_num_rsync[0x00020]; /* Responder - number of RESYNC operations */ +/* -------------- */ + pseudo_bit_t sq_num_rsync[0x00020]; /* Requester - number of RESYNC operations */ +/* -------------- */ + pseudo_bit_t rq_num_udsdprd[0x00020];/* The number of UD packets silently discarded on the receive queue due to lack of receive descriptor. */ +/* -------------- */ + pseudo_bit_t reserved10[0x00020]; +/* -------------- */ + pseudo_bit_t rq_num_ucsdprd[0x00020];/* The number of UC packets silently discarded on the receive queue due to lack of receive descriptor. */ +/* -------------- */ + pseudo_bit_t reserved11[0x003e0]; +/* -------------- */ + pseudo_bit_t num_cqovf[0x00020]; /* Number of CQ overflows */ +/* -------------- */ + pseudo_bit_t num_eqovf[0x00020]; /* Number of EQ overflows */ +/* -------------- */ + pseudo_bit_t num_baddb[0x00020]; /* Number of bad doorbells */ +/* -------------- */ + pseudo_bit_t reserved12[0x002a0]; +/* -------------- */ +}; + +/* Event_data Field - HCR Completion Event */ + +struct arbelprm_hcr_completion_event_st { /* Little Endian */ + pseudo_bit_t token[0x00010]; /* HCR Token */ + pseudo_bit_t reserved0[0x00010]; +/* -------------- */ + pseudo_bit_t reserved1[0x00020]; +/* -------------- */ + pseudo_bit_t status[0x00008]; /* HCR Status */ + pseudo_bit_t reserved2[0x00018]; +/* -------------- */ + pseudo_bit_t out_param_h[0x00020]; /* HCR Output Parameter [63:32] */ +/* -------------- */ + pseudo_bit_t out_param_l[0x00020]; /* HCR Output Parameter [31:0] */ +/* -------------- */ + pseudo_bit_t reserved3[0x00020]; +/* -------------- */ +}; + +/* Completion with Error CQE */ + +struct arbelprm_completion_with_error_st { /* Little Endian */ + pseudo_bit_t myqpn[0x00018]; /* Indicates the QP for which completion is being reported */ + pseudo_bit_t reserved0[0x00008]; +/* -------------- */ + pseudo_bit_t reserved1[0x00060]; +/* -------------- */ + pseudo_bit_t reserved2[0x00010]; + pseudo_bit_t vendor_code[0x00008]; + pseudo_bit_t syndrome[0x00008]; /* Completion with error syndrome: + 0x01 - Local Length Error + 0x02 - Local QP Operation Error + 0x03 - Local EE Context Operation Error + 0x04 - Local Protection Error + 0x05 - Work Request Flushed Error + 0x06 - Memory Window Bind Error + 0x10 - Bad Response Error + 0x11 - Local Access Error + 0x12 - Remote Invalid Request Error + 0x13 - Remote Access Error + 0x14 - Remote Operation Error + 0x15 - Transport Retry Counter Exceeded + 0x16 - RNR Retry Counter Exceeded + 0x20 - Local RDD Violation Error + 0x21 - Remote Invalid RD Request + 0x22 - Remote Aborted Error + 0x23 - Invalid EE Context Number + 0x24 - Invalid EE Context State + other - Reserved + Syndrome is defined according to the IB specification volume 1. For detailed explanation of the syndromes, refer to chapters 10-11 of the IB specification rev 1.1. */ +/* -------------- */ + pseudo_bit_t reserved3[0x00020]; +/* -------------- */ + pseudo_bit_t reserved4[0x00006]; + pseudo_bit_t wqe_addr[0x0001a]; /* Bits 31:6 of WQE virtual address completion is reported for. The 6 least significant bits are zero. */ +/* -------------- */ + pseudo_bit_t reserved5[0x00007]; + pseudo_bit_t owner[0x00001]; /* Owner field. Zero value of this field means SW ownership of CQE. */ + pseudo_bit_t reserved6[0x00010]; + pseudo_bit_t opcode[0x00008]; /* The opcode of WQE completion is reported for. + + The following values are reported in case of completion with error: + 0xFE - For completion with error on Receive Queues + 0xFF - For completion with error on Send Queues */ +/* -------------- */ +}; + +/* Resize CQ Input Mailbox */ + +struct arbelprm_resize_cq_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00020]; +/* -------------- */ + pseudo_bit_t start_addr_h[0x00020]; /* Start address of CQ[63:32]. + Must be aligned on CQE size (32 bytes) */ +/* -------------- */ + pseudo_bit_t start_addr_l[0x00020]; /* Start address of CQ[31:0]. + Must be aligned on CQE size (32 bytes) */ +/* -------------- */ + pseudo_bit_t reserved1[0x00018]; + pseudo_bit_t log_cq_size[0x00005]; /* Log (base 2) of the CQ size (in entries) */ + pseudo_bit_t reserved2[0x00003]; +/* -------------- */ + pseudo_bit_t reserved3[0x00060]; +/* -------------- */ + pseudo_bit_t l_key[0x00020]; /* Memory key (L_Key) to be used to access CQ */ +/* -------------- */ + pseudo_bit_t reserved4[0x00100]; +/* -------------- */ +}; + +/* MAD_IFC Input Modifier */ + +struct arbelprm_mad_ifc_input_modifier_st { /* Little Endian */ + pseudo_bit_t port_number[0x00008]; /* The packet reception port number (1 or 2). */ + pseudo_bit_t mad_extended_info[0x00001];/* Mad_Extended_Info valid bit (MAD_IFC Input Mailbox data from offset 00100h and down). MAD_Extended_Info is read only if this bit is set. + Required for trap generation when BKey check is enabled and for global routed packets. */ + pseudo_bit_t reserved0[0x00007]; + pseudo_bit_t rlid[0x00010]; /* Remote (source) LID from the received MAD. + This field is required for trap generation upon MKey/BKey validation. */ +/* -------------- */ +}; + +/* MAD_IFC Input Mailbox */ + +struct arbelprm_mad_ifc_st { /* Little Endian */ + pseudo_bit_t request_mad_packet[64][0x00020];/* Request MAD Packet (256bytes) */ +/* -------------- */ + pseudo_bit_t my_qpn[0x00018]; /* Destination QP number from the received MAD. + This field is reserved if Mad_extended_info indication in the input modifier is clear. */ + pseudo_bit_t reserved0[0x00008]; +/* -------------- */ + pseudo_bit_t rqpn[0x00018]; /* Remote (source) QP number from the received MAD. + This field is reserved if Mad_extended_info indication in the input modifier is clear. */ + pseudo_bit_t reserved1[0x00008]; +/* -------------- */ + pseudo_bit_t rlid[0x00010]; /* Remote (source) LID from the received MAD. + This field is reserved if Mad_extended_info indication in the input modifier is clear. */ + pseudo_bit_t ml_path[0x00007]; /* My (destination) LID path bits from the received MAD. + This field is reserved if Mad_extended_info indication in the input modifier is clear. */ + pseudo_bit_t g[0x00001]; /* If set, the GRH field in valid. + This field is reserved if Mad_extended_info indication in the input modifier is clear. */ + pseudo_bit_t reserved2[0x00004]; + pseudo_bit_t sl[0x00004]; /* Service Level of the received MAD. + This field is reserved if Mad_extended_info indication in the input modifier is clear. */ +/* -------------- */ + pseudo_bit_t pkey_indx[0x00010]; /* Index in PKey table that matches PKey of the received MAD. + This field is reserved if Mad_extended_info indication in the input modifier is clear. */ + pseudo_bit_t reserved3[0x00010]; +/* -------------- */ + pseudo_bit_t reserved4[0x00180]; +/* -------------- */ + pseudo_bit_t grh[10][0x00020]; /* The GRH field of the MAD packet that was scattered to the first 40 bytes pointed to by the scatter list. + Valid if Mad_extended_info bit (in the input modifier) and g bit are set. + Otherwise this field is reserved. */ +/* -------------- */ + pseudo_bit_t reserved5[0x004c0]; +/* -------------- */ +}; + +/* Query Debug Message */ + +struct arbelprm_query_debug_msg_st { /* Little Endian */ + pseudo_bit_t phy_addr_h[0x00020]; /* Translation of the address in firmware area. High 32 bits. */ +/* -------------- */ + pseudo_bit_t v[0x00001]; /* Physical translation is valid */ + pseudo_bit_t reserved0[0x0000b]; + pseudo_bit_t phy_addr_l[0x00014]; /* Translation of the address in firmware area. Low 32 bits. */ +/* -------------- */ + pseudo_bit_t fw_area_base[0x00020]; /* Firmware area base address. The format strings and the trace buffers may be located starting from this address. */ +/* -------------- */ + pseudo_bit_t fw_area_size[0x00020]; /* Firmware area size */ +/* -------------- */ + pseudo_bit_t trc_hdr_sz[0x00020]; /* Trace message header size in dwords. */ +/* -------------- */ + pseudo_bit_t trc_arg_num[0x00020]; /* The number of arguments per trace message. */ +/* -------------- */ + pseudo_bit_t reserved1[0x000c0]; +/* -------------- */ + pseudo_bit_t dbg_msk_h[0x00020]; /* Debug messages mask [63:32] */ +/* -------------- */ + pseudo_bit_t dbg_msk_l[0x00020]; /* Debug messages mask [31:0] */ +/* -------------- */ + pseudo_bit_t reserved2[0x00040]; +/* -------------- */ + pseudo_bit_t buff0_addr[0x00020]; /* Address in firmware area of Trace Buffer 0 */ +/* -------------- */ + pseudo_bit_t buff0_size[0x00020]; /* Size of Trace Buffer 0 */ +/* -------------- */ + pseudo_bit_t buff1_addr[0x00020]; /* Address in firmware area of Trace Buffer 1 */ +/* -------------- */ + pseudo_bit_t buff1_size[0x00020]; /* Size of Trace Buffer 1 */ +/* -------------- */ + pseudo_bit_t buff2_addr[0x00020]; /* Address in firmware area of Trace Buffer 2 */ +/* -------------- */ + pseudo_bit_t buff2_size[0x00020]; /* Size of Trace Buffer 2 */ +/* -------------- */ + pseudo_bit_t buff3_addr[0x00020]; /* Address in firmware area of Trace Buffer 3 */ +/* -------------- */ + pseudo_bit_t buff3_size[0x00020]; /* Size of Trace Buffer 3 */ +/* -------------- */ + pseudo_bit_t buff4_addr[0x00020]; /* Address in firmware area of Trace Buffer 4 */ +/* -------------- */ + pseudo_bit_t buff4_size[0x00020]; /* Size of Trace Buffer 4 */ +/* -------------- */ + pseudo_bit_t buff5_addr[0x00020]; /* Address in firmware area of Trace Buffer 5 */ +/* -------------- */ + pseudo_bit_t buff5_size[0x00020]; /* Size of Trace Buffer 5 */ +/* -------------- */ + pseudo_bit_t buff6_addr[0x00020]; /* Address in firmware area of Trace Buffer 6 */ +/* -------------- */ + pseudo_bit_t buff6_size[0x00020]; /* Size of Trace Buffer 6 */ +/* -------------- */ + pseudo_bit_t buff7_addr[0x00020]; /* Address in firmware area of Trace Buffer 7 */ +/* -------------- */ + pseudo_bit_t buff7_size[0x00020]; /* Size of Trace Buffer 7 */ +/* -------------- */ + pseudo_bit_t reserved3[0x00400]; +/* -------------- */ +}; + +/* User Access Region */ + +struct arbelprm_uar_st { /* Little Endian */ + struct arbelprm_rd_send_doorbell_st rd_send_doorbell;/* Reliable Datagram send doorbell */ +/* -------------- */ + struct arbelprm_send_doorbell_st send_doorbell;/* Send doorbell */ +/* -------------- */ + pseudo_bit_t reserved0[0x00040]; +/* -------------- */ + struct arbelprm_cq_cmd_doorbell_st cq_command_doorbell;/* CQ Doorbell */ +/* -------------- */ + pseudo_bit_t reserved1[0x03ec0]; +/* -------------- */ +}; + +/* Receive doorbell */ + +struct arbelprm_receive_doorbell_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00008]; + pseudo_bit_t wqe_counter[0x00010]; /* Modulo-64K counter of WQEs posted on this queue since its creation. Should be zero for the first doorbell on the QP */ + pseudo_bit_t reserved1[0x00008]; +/* -------------- */ + pseudo_bit_t reserved2[0x00005]; + pseudo_bit_t srq[0x00001]; /* If set, this is a Shared Receive Queue */ + pseudo_bit_t reserved3[0x00002]; + pseudo_bit_t qpn[0x00018]; /* QP number or SRQ number this doorbell is rung on */ +/* -------------- */ +}; + +/* SET_IB Parameters */ + +struct arbelprm_set_ib_st { /* Little Endian */ + pseudo_bit_t rqk[0x00001]; /* Reset QKey Violation Counter */ + pseudo_bit_t reserved0[0x00011]; + pseudo_bit_t sig[0x00001]; /* Set System Image GUID to system_image_guid specified. + system_image_guid and sig must be the same for all ports. */ + pseudo_bit_t reserved1[0x0000d]; +/* -------------- */ + pseudo_bit_t capability_mask[0x00020];/* PortInfo Capability Mask */ +/* -------------- */ + pseudo_bit_t system_image_guid_h[0x00020];/* System Image GUID[63:32], takes effect only if the SIG bit is set + Must be the same for both ports. */ +/* -------------- */ + pseudo_bit_t system_image_guid_l[0x00020];/* System Image GUID[31:0], takes effect only if the SIG bit is set + Must be the same for both ports. */ +/* -------------- */ + pseudo_bit_t reserved2[0x00180]; +/* -------------- */ +}; + +/* Multicast Group Member */ + +struct arbelprm_mgm_entry_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00006]; + pseudo_bit_t next_gid_index[0x0001a];/* Index of next Multicast Group Member whose GID maps to same MGID_HASH number. + The index is into the Multicast Group Table, which is the comprised the MGHT and AMGM tables. + next_gid_index=0 means end of the chain. */ +/* -------------- */ + pseudo_bit_t reserved1[0x00060]; +/* -------------- */ + pseudo_bit_t mgid_128_96[0x00020]; /* Multicast group GID[128:96] in big endian format. + Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */ +/* -------------- */ + pseudo_bit_t mgid_95_64[0x00020]; /* Multicast group GID[95:64] in big endian format. + Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */ +/* -------------- */ + pseudo_bit_t mgid_63_32[0x00020]; /* Multicast group GID[63:32] in big endian format. + Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */ +/* -------------- */ + pseudo_bit_t mgid_31_0[0x00020]; /* Multicast group GID[31:0] in big endian format. + Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */ +/* -------------- */ + struct arbelprm_mgmqp_st mgmqp_0; /* Multicast Group Member QP */ +/* -------------- */ + struct arbelprm_mgmqp_st mgmqp_1; /* Multicast Group Member QP */ +/* -------------- */ + struct arbelprm_mgmqp_st mgmqp_2; /* Multicast Group Member QP */ +/* -------------- */ + struct arbelprm_mgmqp_st mgmqp_3; /* Multicast Group Member QP */ +/* -------------- */ + struct arbelprm_mgmqp_st mgmqp_4; /* Multicast Group Member QP */ +/* -------------- */ + struct arbelprm_mgmqp_st mgmqp_5; /* Multicast Group Member QP */ +/* -------------- */ + struct arbelprm_mgmqp_st mgmqp_6; /* Multicast Group Member QP */ +/* -------------- */ + struct arbelprm_mgmqp_st mgmqp_7; /* Multicast Group Member QP */ +/* -------------- */ +}; + +/* INIT_IB Parameters */ + +struct arbelprm_init_ib_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00004]; + pseudo_bit_t vl_cap[0x00004]; /* Maximum VLs supported on the port, excluding VL15. + Legal values are 1,2,4 and 8. */ + pseudo_bit_t port_width_cap[0x00004];/* IB Port Width + 1 - 1x + 3 - 1x, 4x + 11 - 1x, 4x or 12x (must not be used in InfiniHost-III-EX MT25208) + else - Reserved */ + pseudo_bit_t mtu_cap[0x00004]; /* Maximum MTU Supported + 0x0 - Reserved + 0x1 - 256 + 0x2 - 512 + 0x3 - 1024 + 0x4 - 2048 + 0x5 - 0xF Reserved */ + pseudo_bit_t g0[0x00001]; /* Set port GUID0 to GUID0 specified */ + pseudo_bit_t ng[0x00001]; /* Set node GUID to node_guid specified. + node_guid and ng must be the same for all ports. */ + pseudo_bit_t sig[0x00001]; /* Set System Image GUID to system_image_guid specified. + system_image_guid and sig must be the same for all ports. */ + pseudo_bit_t reserved1[0x0000d]; +/* -------------- */ + pseudo_bit_t max_gid[0x00010]; /* Maximum number of GIDs for the port */ + pseudo_bit_t reserved2[0x00010]; +/* -------------- */ + pseudo_bit_t max_pkey[0x00010]; /* Maximum pkeys for the port. + Must be the same for both ports. */ + pseudo_bit_t reserved3[0x00010]; +/* -------------- */ + pseudo_bit_t reserved4[0x00020]; +/* -------------- */ + pseudo_bit_t guid0_h[0x00020]; /* EUI-64 GUID assigned by the manufacturer, takes effect only if the G0 bit is set (bits 63:32) */ +/* -------------- */ + pseudo_bit_t guid0_l[0x00020]; /* EUI-64 GUID assigned by the manufacturer, takes effect only if the G0 bit is set (bits 31:0) */ +/* -------------- */ + pseudo_bit_t node_guid_h[0x00020]; /* Node GUID[63:32], takes effect only if the NG bit is set + Must be the same for both ports. */ +/* -------------- */ + pseudo_bit_t node_guid_l[0x00020]; /* Node GUID[31:0], takes effect only if the NG bit is set + Must be the same for both ports. */ +/* -------------- */ + pseudo_bit_t system_image_guid_h[0x00020];/* System Image GUID[63:32], takes effect only if the SIG bit is set + Must be the same for both ports. */ +/* -------------- */ + pseudo_bit_t system_image_guid_l[0x00020];/* System Image GUID[31:0], takes effect only if the SIG bit is set + Must be the same for both ports. */ +/* -------------- */ + pseudo_bit_t reserved5[0x006c0]; +/* -------------- */ +}; + +/* Query Device Limitations */ + +struct arbelprm_query_dev_lim_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00080]; +/* -------------- */ + pseudo_bit_t log_max_qp[0x00005]; /* Log2 of the Maximum number of QPs supported */ + pseudo_bit_t reserved1[0x00003]; + pseudo_bit_t log2_rsvd_qps[0x00004];/* Log (base 2) of the number of QPs reserved for firmware use + The reserved resources are numbered from 0 to 2^log2_rsvd_qps-1 */ + pseudo_bit_t reserved2[0x00004]; + pseudo_bit_t log_max_qp_sz[0x00008];/* The maximum number of WQEs allowed on the RQ or the SQ is 2^log_max_qp_sz-1 */ + pseudo_bit_t log_max_srq_sz[0x00008];/* The maximum number of WQEs allowed on the SRQ is 2^log_max_srq_sz-1 */ +/* -------------- */ + pseudo_bit_t log_max_ee[0x00005]; /* Log2 of the Maximum number of EE contexts supported */ + pseudo_bit_t reserved3[0x00003]; + pseudo_bit_t log2_rsvd_ees[0x00004];/* Log (base 2) of the number of EECs reserved for firmware use + The reserved resources are numbered from 0 to 2^log2_rsvd_ees-1 */ + pseudo_bit_t reserved4[0x00004]; + pseudo_bit_t log_max_srqs[0x00005]; /* Log base 2 of the maximum number of SRQs supported, valid only if SRQ bit is set. + */ + pseudo_bit_t reserved5[0x00007]; + pseudo_bit_t log2_rsvd_srqs[0x00004];/* Log (base 2) of the number of reserved SRQs for firmware use + The reserved resources are numbered from 0 to 2^log2_rsvd_srqs-1 + This parameter is valid only if the SRQ bit is set. */ +/* -------------- */ + pseudo_bit_t log_max_cq[0x00005]; /* Log2 of the Maximum number of CQs supported */ + pseudo_bit_t reserved6[0x00003]; + pseudo_bit_t log2_rsvd_cqs[0x00004];/* Log (base 2) of the number of CQs reserved for firmware use + The reserved resources are numbered from 0 to 2^log2_rsrvd_cqs-1 */ + pseudo_bit_t reserved7[0x00004]; + pseudo_bit_t log_max_cq_sz[0x00008];/* Log2 of the Maximum CQEs allowed in a CQ */ + pseudo_bit_t reserved8[0x00008]; +/* -------------- */ + pseudo_bit_t log_max_eq[0x00003]; /* Log2 of the Maximum number of EQs */ + pseudo_bit_t reserved9[0x00005]; + pseudo_bit_t num_rsvd_eqs[0x00004]; /* The number of EQs reserved for firmware use + The reserved resources are numbered from 0 to num_rsvd_eqs-1 + If 0 - no resources are reserved. */ + pseudo_bit_t reserved10[0x00004]; + pseudo_bit_t log_max_mpts[0x00006]; /* Log (base 2) of the maximum number of MPT entries (the number of Regions/Windows) */ + pseudo_bit_t reserved11[0x00002]; + pseudo_bit_t log_max_eq_sz[0x00008];/* Log2 of the Maximum EQEs allowed in a EQ */ +/* -------------- */ + pseudo_bit_t log_max_mtts[0x00006]; /* Log2 of the Maximum number of MTT entries */ + pseudo_bit_t reserved12[0x00002]; + pseudo_bit_t log2_rsvd_mrws[0x00004];/* Log (base 2) of the number of MPTs reserved for firmware use + The reserved resources are numbered from 0 to 2^log2_rsvd_mrws-1 */ + pseudo_bit_t reserved13[0x00004]; + pseudo_bit_t log_max_mrw_sz[0x00008];/* Log2 of the Maximum Size of Memory Region/Window */ + pseudo_bit_t reserved14[0x00004]; + pseudo_bit_t log2_rsvd_mtts[0x00004];/* Log (base 2) of the number of MTT entries reserved for firmware use + The reserved resources are numbered from 0 to 2^log2_rsvd_mtts-1 + */ +/* -------------- */ + pseudo_bit_t reserved15[0x00020]; +/* -------------- */ + pseudo_bit_t log_max_ra_res_qp[0x00006];/* Log2 of the Maximum number of outstanding RDMA read/Atomic per QP as a responder */ + pseudo_bit_t reserved16[0x0000a]; + pseudo_bit_t log_max_ra_req_qp[0x00006];/* Log2 of the maximum number of outstanding RDMA read/Atomic per QP as a requester */ + pseudo_bit_t reserved17[0x0000a]; +/* -------------- */ + pseudo_bit_t log_max_ra_res_global[0x00006];/* Log2 of the maximum number of RDMA read/atomic operations the HCA responder can support globally. That implies the RDB table size. */ + pseudo_bit_t reserved18[0x00016]; + pseudo_bit_t log2_rsvd_rdbs[0x00004];/* Log (base 2) of the number of RDB entries reserved for firmware use + The reserved resources are numbered from 0 to 2^log2_rsvd_rdbs-1 */ +/* -------------- */ + pseudo_bit_t rsz_srq[0x00001]; /* Ability to modify the maximum number of WRs per SRQ. */ + pseudo_bit_t reserved19[0x0001f]; +/* -------------- */ + pseudo_bit_t num_ports[0x00004]; /* Number of IB ports. */ + pseudo_bit_t max_vl[0x00004]; /* Maximum VLs supported on each port, excluding VL15 */ + pseudo_bit_t max_port_width[0x00004];/* IB Port Width + 1 - 1x + 3 - 1x, 4x + 11 - 1x, 4x or 12x + else - Reserved */ + pseudo_bit_t max_mtu[0x00004]; /* Maximum MTU Supported + 0x0 - Reserved + 0x1 - 256 + 0x2 - 512 + 0x3 - 1024 + 0x4 - 2048 + 0x5 - 0xF Reserved */ + pseudo_bit_t local_ca_ack_delay[0x00005];/* The Local CA ACK Delay. This is the value recommended to be returned in Query HCA verb. + The delay value in microseconds is computed using 4.096us * 2^(local_ca_ack_delay). */ + pseudo_bit_t reserved20[0x0000b]; +/* -------------- */ + pseudo_bit_t log_max_gid[0x00004]; /* Log2 of the maximum number of GIDs per port */ + pseudo_bit_t reserved21[0x0001c]; +/* -------------- */ + pseudo_bit_t log_max_pkey[0x00004]; /* Log2 of the max PKey Table Size (per IB port) */ + pseudo_bit_t reserved22[0x0000c]; + pseudo_bit_t stat_rate_support[0x00010];/* bit mask of stat rate supported + bit 0 - full bw + bit 1 - 1/4 bw + bit 2 - 1/8 bw + bit 3 - 1/2 bw; */ +/* -------------- */ + pseudo_bit_t reserved23[0x00020]; +/* -------------- */ + pseudo_bit_t rc[0x00001]; /* RC Transport supported */ + pseudo_bit_t uc[0x00001]; /* UC Transport Supported */ + pseudo_bit_t ud[0x00001]; /* UD Transport Supported */ + pseudo_bit_t rd[0x00001]; /* RD Transport Supported */ + pseudo_bit_t raw_ipv6[0x00001]; /* Raw IPv6 Transport Supported */ + pseudo_bit_t raw_ether[0x00001]; /* Raw Ethertype Transport Supported */ + pseudo_bit_t srq[0x00001]; /* SRQ is supported + */ + pseudo_bit_t ipo_ib_checksum[0x00001];/* IP over IB checksum is supported */ + pseudo_bit_t pkv[0x00001]; /* PKey Violation Counter Supported */ + pseudo_bit_t qkv[0x00001]; /* QKey Violation Coutner Supported */ + pseudo_bit_t reserved24[0x00006]; + pseudo_bit_t mw[0x00001]; /* Memory windows supported */ + pseudo_bit_t apm[0x00001]; /* Automatic Path Migration Supported */ + pseudo_bit_t atm[0x00001]; /* Atomic operations supported (atomicity is guaranteed between QPs on this HCA) */ + pseudo_bit_t rm[0x00001]; /* Raw Multicast Supported */ + pseudo_bit_t avp[0x00001]; /* Address Vector Port checking supported */ + pseudo_bit_t udm[0x00001]; /* UD Multicast Supported */ + pseudo_bit_t reserved25[0x00002]; + pseudo_bit_t pg[0x00001]; /* Paging on demand supported */ + pseudo_bit_t r[0x00001]; /* Router mode supported */ + pseudo_bit_t reserved26[0x00006]; +/* -------------- */ + pseudo_bit_t log_pg_sz[0x00008]; /* Minimum system page size supported (log2). + For proper operation it must be less than or equal the hosting platform (CPU) minimum page size. */ + pseudo_bit_t reserved27[0x00008]; + pseudo_bit_t uar_sz[0x00006]; /* UAR Area Size = 1MB * 2^uar_sz */ + pseudo_bit_t reserved28[0x00006]; + pseudo_bit_t num_rsvd_uars[0x00004];/* The number of UARs reserved for firmware use + The reserved resources are numbered from 0 to num_reserved_uars-1 + Note that UAR number num_reserved_uars is always for the kernel. */ +/* -------------- */ + pseudo_bit_t reserved29[0x00020]; +/* -------------- */ + pseudo_bit_t max_desc_sz_sq[0x00010];/* Max descriptor size in bytes for the send queue */ + pseudo_bit_t max_sg_sq[0x00008]; /* The maximum S/G list elements in a SQ WQE (max_desc_sz/16 - 3) */ + pseudo_bit_t reserved30[0x00008]; +/* -------------- */ + pseudo_bit_t max_desc_sz_rq[0x00010];/* Max descriptor size in bytes for the receive queue */ + pseudo_bit_t max_sg_rq[0x00008]; /* The maximum S/G list elements in a RQ WQE (max_desc_sz/16 - 3) */ + pseudo_bit_t reserved31[0x00008]; +/* -------------- */ + pseudo_bit_t reserved32[0x00040]; +/* -------------- */ + pseudo_bit_t log_max_mcg[0x00008]; /* Log2 of the maximum number of multicast groups */ + pseudo_bit_t num_rsvd_mcgs[0x00004];/* The number of MGMs reserved for firmware use in the MGHT. + The reserved resources are numbered from 0 to num_reserved_mcgs-1 + If 0 - no resources are reserved. */ + pseudo_bit_t reserved33[0x00004]; + pseudo_bit_t log_max_qp_mcg[0x00008];/* Log2 of the maximum number of QPs per multicast group */ + pseudo_bit_t reserved34[0x00008]; +/* -------------- */ + pseudo_bit_t log_max_rdds[0x00006]; /* Log2 of the maximum number of RDDs */ + pseudo_bit_t reserved35[0x00006]; + pseudo_bit_t num_rsvd_rdds[0x00004];/* The number of RDDs reserved for firmware use + The reserved resources are numbered from 0 to num_reserved_rdds-1. + If 0 - no resources are reserved. */ + pseudo_bit_t log_max_pd[0x00006]; /* Log2 of the maximum number of PDs */ + pseudo_bit_t reserved36[0x00006]; + pseudo_bit_t num_rsvd_pds[0x00004]; /* The number of PDs reserved for firmware use + The reserved resources are numbered from 0 to num_reserved_pds-1 + If 0 - no resources are reserved. */ +/* -------------- */ + pseudo_bit_t reserved37[0x000c0]; +/* -------------- */ + pseudo_bit_t qpc_entry_sz[0x00010]; /* QPC Entry Size for the device + For the InfiniHost-III-EX MT25208 entry size is 256 bytes */ + pseudo_bit_t eec_entry_sz[0x00010]; /* EEC Entry Size for the device + For the InfiniHost-III-EX MT25208 entry size is 256 bytes */ +/* -------------- */ + pseudo_bit_t eqpc_entry_sz[0x00010];/* Extended QPC entry size for the device + For the InfiniHost-III-EX MT25208 entry size is 32 bytes */ + pseudo_bit_t eeec_entry_sz[0x00010];/* Extended EEC entry size for the device + For the InfiniHost-III-EX MT25208 entry size is 32 bytes */ +/* -------------- */ + pseudo_bit_t cqc_entry_sz[0x00010]; /* CQC entry size for the device + For the InfiniHost-III-EX MT25208 entry size is 64 bytes */ + pseudo_bit_t eqc_entry_sz[0x00010]; /* EQ context entry size for the device + For the InfiniHost-III-EX MT25208 entry size is 64 bytes */ +/* -------------- */ + pseudo_bit_t uar_scratch_entry_sz[0x00010];/* UAR Scratchpad Entry Size + For the InfiniHost-III-EX MT25208 entry size is 32 bytes */ + pseudo_bit_t srq_entry_sz[0x00010]; /* SRQ context entry size for the device + For the InfiniHost-III-EX MT25208 entry size is 32 bytes */ +/* -------------- */ + pseudo_bit_t mpt_entry_sz[0x00010]; /* MPT entry size in Bytes for the device. + For the InfiniHost-III-EX MT25208 entry size is 64 bytes */ + pseudo_bit_t mtt_entry_sz[0x00010]; /* MTT entry size in Bytes for the device. + For the InfiniHost-III-EX MT25208 entry size is 8 bytes */ +/* -------------- */ + pseudo_bit_t bmme[0x00001]; /* Base Memory Management Extension Support */ + pseudo_bit_t win_type[0x00001]; /* Bound Type 2 Memory Window Association mechanism: + 0 - Type 2A - QP Number Association; or + 1 - Type 2B - QP Number and PD Association. */ + pseudo_bit_t mps[0x00001]; /* Ability of this HCA to support multiple page sizes per Memory Region. */ + pseudo_bit_t bl[0x00001]; /* Ability of this HCA to support Block List Physical Buffer Lists. (The device does not supports Block List) */ + pseudo_bit_t zb[0x00001]; /* Zero Based region/windows supported */ + pseudo_bit_t lif[0x00001]; /* Ability of this HCA to support Local Invalidate Fencing. */ + pseudo_bit_t reserved38[0x00002]; + pseudo_bit_t log_pbl_sz[0x00006]; /* Log2 of the Maximum Physical Buffer List size in Bytes supported by this HCA when invoking the Allocate L_Key verb. + */ + pseudo_bit_t reserved39[0x00012]; +/* -------------- */ + pseudo_bit_t resd_lkey[0x00020]; /* The value of the reserved Lkey for Base Memory Management Extension */ +/* -------------- */ + pseudo_bit_t lamr[0x00001]; /* When set the device requires local attached memory in order to operate. + When set, ICM pages, Firmware Area and ICM auxiliary pages must be allocated in the local attached memory. */ + pseudo_bit_t reserved40[0x0001f]; +/* -------------- */ + pseudo_bit_t max_icm_size_h[0x00020];/* Bits [63:32] of maximum ICM size InfiniHost III Ex support in bytes. */ +/* -------------- */ + pseudo_bit_t max_icm_size_l[0x00020];/* Bits [31:0] of maximum ICM size InfiniHost III Ex support in bytes. */ +/* -------------- */ + pseudo_bit_t reserved41[0x002c0]; +/* -------------- */ +}; + +/* QUERY_ADAPTER Parameters Block */ + +struct arbelprm_query_adapter_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00080]; +/* -------------- */ + pseudo_bit_t reserved1[0x00018]; + pseudo_bit_t intapin[0x00008]; /* Driver should set this field to INTR value in the event queue in order to get Express interrupt messages. */ +/* -------------- */ + pseudo_bit_t reserved2[0x00060]; +/* -------------- */ + struct arbelprm_vsd_st vsd; +/* -------------- */ +}; + +/* QUERY_FW Parameters Block */ + +struct arbelprm_query_fw_st { /* Little Endian */ + pseudo_bit_t fw_rev_major[0x00010]; /* Firmware Revision - Major */ + pseudo_bit_t fw_pages[0x00010]; /* Amount of physical memory to be allocated for FW usage is in 4KByte pages. */ +/* -------------- */ + pseudo_bit_t fw_rev_minor[0x00010]; /* Firmware Revision - Minor */ + pseudo_bit_t fw_rev_subminor[0x00010];/* Firmware Sub-minor version (Patch level). */ +/* -------------- */ + pseudo_bit_t cmd_interface_rev[0x00010];/* Command Interface Interpreter Revision ID */ + pseudo_bit_t reserved0[0x0000e]; + pseudo_bit_t wqe_h_mode[0x00001]; /* Hermon mode. If '1', then WQE and AV format is the advanced format */ + pseudo_bit_t zb_wq_cq[0x00001]; /* If '1', then ZB mode of WQ and CQ are enabled (i.e. real Memfree PRM is supported) */ +/* -------------- */ + pseudo_bit_t log_max_outstanding_cmd[0x00008];/* Log2 of the maximum number of commands the HCR can support simultaneously */ + pseudo_bit_t reserved1[0x00017]; + pseudo_bit_t dt[0x00001]; /* Debug Trace Support + 0 - Debug trace is not supported + 1 - Debug trace is supported */ +/* -------------- */ + pseudo_bit_t cmd_interface_db[0x00001];/* Set if the device accepts commands by means of special doorbells */ + pseudo_bit_t reserved2[0x0001f]; +/* -------------- */ + pseudo_bit_t reserved3[0x00060]; +/* -------------- */ + pseudo_bit_t clr_int_base_addr_h[0x00020];/* Bits [63:32] of Clear interrupt register physical address. + Points to 64 bit register. */ +/* -------------- */ + pseudo_bit_t clr_int_base_addr_l[0x00020];/* Bits [31:0] of Clear interrupt register physical address. + Points to 64 bit register. */ +/* -------------- */ + pseudo_bit_t reserved4[0x00040]; +/* -------------- */ + pseudo_bit_t error_buf_start_h[0x00020];/* Read Only buffer for catastrophic error reports (physical address) */ +/* -------------- */ + pseudo_bit_t error_buf_start_l[0x00020];/* Read Only buffer for catastrophic error reports (physical address) */ +/* -------------- */ + pseudo_bit_t error_buf_size[0x00020];/* Size in words */ +/* -------------- */ + pseudo_bit_t reserved5[0x00020]; +/* -------------- */ + pseudo_bit_t eq_arm_base_addr_h[0x00020];/* Bits [63:32] of EQ Arm DBs physical address. + Points to 64 bit register. + Setting bit x in the offset, arms EQ number x. + */ +/* -------------- */ + pseudo_bit_t eq_arm_base_addr_l[0x00020];/* Bits [31:0] of EQ Arm DBs physical address. + Points to 64 bit register. + Setting bit x in the offset, arms EQ number x. */ +/* -------------- */ + pseudo_bit_t eq_set_ci_base_addr_h[0x00020];/* Bits [63:32] of EQ Set CI DBs Table physical address. + Points to a the EQ Set CI DBs Table base address. */ +/* -------------- */ + pseudo_bit_t eq_set_ci_base_addr_l[0x00020];/* Bits [31:0] of EQ Set CI DBs Table physical address. + Points to a the EQ Set CI DBs Table base address. */ +/* -------------- */ + pseudo_bit_t cmd_db_dw1[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 1 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */ + pseudo_bit_t cmd_db_dw0[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 0 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */ +/* -------------- */ + pseudo_bit_t cmd_db_dw3[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 3 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */ + pseudo_bit_t cmd_db_dw2[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 2 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */ +/* -------------- */ + pseudo_bit_t cmd_db_dw5[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 5 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */ + pseudo_bit_t cmd_db_dw4[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 4 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */ +/* -------------- */ + pseudo_bit_t cmd_db_dw7[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 7 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */ + pseudo_bit_t cmd_db_dw6[0x00010]; /* offset in bytes from cmd_db_addr_base where DWord 6 of a Command Interface Doorbell should be written. Valid only if CmdInterfaceDb bit is '1' */ +/* -------------- */ + pseudo_bit_t cmd_db_addr_base_h[0x00020];/* High bits of cmd_db_addr_base, which cmd_db_dw offsets refer to. Valid only if CmdInterfaceDb bit is '1' */ +/* -------------- */ + pseudo_bit_t cmd_db_addr_base_l[0x00020];/* Low bits of cmd_db_addr_base, which cmd_db_dw offsets refer to. Valid only if CmdInterfaceDb bit is '1' */ +/* -------------- */ + pseudo_bit_t reserved6[0x004c0]; +/* -------------- */ +}; + +/* ACCESS_LAM */ + +struct arbelprm_access_lam_st { /* Little Endian */ + struct arbelprm_access_lam_inject_errors_st access_lam_inject_errors; +/* -------------- */ + pseudo_bit_t reserved0[0x00080]; +/* -------------- */ +}; + +/* ENABLE_LAM Parameters Block */ + +struct arbelprm_enable_lam_st { /* Little Endian */ + pseudo_bit_t lam_start_adr_h[0x00020];/* LAM start address [63:32] */ +/* -------------- */ + pseudo_bit_t lam_start_adr_l[0x00020];/* LAM start address [31:0] */ +/* -------------- */ + pseudo_bit_t lam_end_adr_h[0x00020];/* LAM end address [63:32] */ +/* -------------- */ + pseudo_bit_t lam_end_adr_l[0x00020];/* LAM end address [31:0] */ +/* -------------- */ + pseudo_bit_t di[0x00002]; /* Data Integrity Configuration: + 00 - none + 01 - Parity + 10 - ECC Detection Only + 11 - ECC With Correction */ + pseudo_bit_t ap[0x00002]; /* Auto Precharge Mode + 00 - No auto precharge + 01 - Auto precharge per transaction + 10 - Auto precharge per 64 bytes + 11 - reserved */ + pseudo_bit_t dh[0x00001]; /* When set, LAM is Hidden and can not be accessed directly from the PCI bus. */ + pseudo_bit_t reserved0[0x0001b]; +/* -------------- */ + pseudo_bit_t reserved1[0x00160]; +/* -------------- */ + struct arbelprm_dimminfo_st dimm0; /* Logical DIMM 0 Parameters */ +/* -------------- */ + struct arbelprm_dimminfo_st dimm1; /* Logical DIMM 1 Parameters */ +/* -------------- */ + pseudo_bit_t reserved2[0x00400]; +/* -------------- */ +}; + +/* Memory Access Parameters for UD Address Vector Table */ + +struct arbelprm_udavtable_memory_parameters_st { /* Little Endian */ + pseudo_bit_t l_key[0x00020]; /* L_Key used to access TPT */ +/* -------------- */ + pseudo_bit_t pd[0x00018]; /* PD used by TPT for matching against PD of region entry being accessed. */ + pseudo_bit_t reserved0[0x00005]; + pseudo_bit_t xlation_en[0x00001]; /* When cleared, address is physical address and no translation will be done. When set, address is virtual. */ + pseudo_bit_t reserved1[0x00002]; +/* -------------- */ +}; + +/* INIT_HCA & QUERY_HCA Parameters Block */ + +struct arbelprm_init_hca_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00060]; +/* -------------- */ + pseudo_bit_t reserved1[0x00010]; + pseudo_bit_t time_stamp_granularity[0x00008];/* This field controls the granularity in which CQE Timestamp counter is incremented. + The TimeStampGranularity units is 1/4 of a microseconds. (e.g is TimeStampGranularity is configured to 0x2, CQE Timestamp will be incremented every one microsecond) + When sets to Zero, timestamp reporting in the CQE is disabled. + This feature is currently not supported. + */ + pseudo_bit_t hca_core_clock[0x00008];/* Internal Clock Period (in units of 1/16 ns) (QUERY_HCA only) */ +/* -------------- */ + pseudo_bit_t reserved2[0x00008]; + pseudo_bit_t router_qp[0x00010]; /* Upper 16 bit to be used as a QP number for router mode. Low order 8 bits are taken from the TClass field of the incoming packet. + Valid only if RE bit is set */ + pseudo_bit_t reserved3[0x00007]; + pseudo_bit_t re[0x00001]; /* Router Mode Enable + If this bit is set, entire packet (including all headers and ICRC) will be considered as a data payload and will be scattered to memory as specified in the descriptor that is posted on the QP matching the TClass field of packet. */ +/* -------------- */ + pseudo_bit_t udp[0x00001]; /* UD Port Check Enable + 0 - Port field in Address Vector is ignored + 1 - HCA will check the port field in AV entry (fetched for UD descriptor) against the Port of the UD QP executing the descriptor. */ + pseudo_bit_t he[0x00001]; /* Host Endianess - Used for Atomic Operations + 0 - Host is Little Endian + 1 - Host is Big endian + */ + pseudo_bit_t reserved4[0x00001]; + pseudo_bit_t ce[0x00001]; /* Checksum Enabled - when Set IPoverIB checksum generation & checking is enabled */ + pseudo_bit_t sph[0x00001]; /* 0 - SW calculates TCP/UDP Pseudo-Header checksum and inserts it into the TCP/UDP checksum field when sending a packet + 1 - HW calculates TCP/UDP Pseudo-Header checksum when sending a packet + */ + pseudo_bit_t rph[0x00001]; /* 0 - Not HW calculation of TCP/UDP Pseudo-Header checksum are done when receiving a packet + 1 - HW calculates TCP/UDP Pseudo-Header checksum when receiving a packet + */ + pseudo_bit_t reserved5[0x00002]; + pseudo_bit_t responder_exu[0x00004];/* Indicate the relation between the execution enegines allocation dedicated for responder versus the engines dedicated for reqvester . + responder_exu/16 = (number of responder exu engines)/(total number of engines) + Legal values are 0x0-0xF. 0 is "auto". + + */ + pseudo_bit_t reserved6[0x00004]; + pseudo_bit_t wqe_quota[0x0000f]; /* Maximum number of WQEs that are executed prior to preemption of execution unit. 0 - reserved. */ + pseudo_bit_t wqe_quota_en[0x00001]; /* If set - wqe_quota field is used. If cleared - WQE quota is set to "auto" value */ +/* -------------- */ + pseudo_bit_t reserved7[0x00040]; +/* -------------- */ + struct arbelprm_qpcbaseaddr_st qpc_eec_cqc_eqc_rdb_parameters; +/* -------------- */ + pseudo_bit_t reserved8[0x00100]; +/* -------------- */ + struct arbelprm_multicastparam_st multicast_parameters; +/* -------------- */ + pseudo_bit_t reserved9[0x00080]; +/* -------------- */ + struct arbelprm_tptparams_st tpt_parameters; +/* -------------- */ + pseudo_bit_t reserved10[0x00080]; +/* -------------- */ + struct arbelprm_uar_params_st uar_parameters;/* UAR Parameters */ +/* -------------- */ + pseudo_bit_t reserved11[0x00600]; +/* -------------- */ +}; + +/* Event Queue Context Table Entry */ + +struct arbelprm_eqc_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00008]; + pseudo_bit_t st[0x00004]; /* Event delivery state machine + 0x9 - Armed + 0xA - Fired + 0xB - Always_Armed (auto-rearm) + other - reserved */ + pseudo_bit_t reserved1[0x00005]; + pseudo_bit_t oi[0x00001]; /* Oerrun ignore. + If set, HW will not check EQ full condition when writing new EQEs. */ + pseudo_bit_t tr[0x00001]; /* Translation Required. If set - EQ access undergo address translation. */ + pseudo_bit_t reserved2[0x00005]; + pseudo_bit_t owner[0x00004]; /* 0 - SW ownership + 1 - HW ownership + Valid for the QUERY_EQ and HW2SW_EQ commands only */ + pseudo_bit_t status[0x00004]; /* EQ status: + 0000 - OK + 1010 - EQ write failure + Valid for the QUERY_EQ and HW2SW_EQ commands only */ +/* -------------- */ + pseudo_bit_t start_address_h[0x00020];/* Start Address of Event Queue[63:32]. */ +/* -------------- */ + pseudo_bit_t start_address_l[0x00020];/* Start Address of Event Queue[31:0]. + Must be aligned on 32-byte boundary */ +/* -------------- */ + pseudo_bit_t reserved3[0x00018]; + pseudo_bit_t log_eq_size[0x00005]; /* Amount of entries in this EQ is 2^log_eq_size. + Log_eq_size must be bigger than 1. + Maximum EQ size is 2^17 EQEs (max Log_eq_size is 17). */ + pseudo_bit_t reserved4[0x00003]; +/* -------------- */ + pseudo_bit_t reserved5[0x00020]; +/* -------------- */ + pseudo_bit_t intr[0x00008]; /* Interrupt (message) to be generated to report event to INT layer. + 00iiiiii - set to INTA given in QUERY_ADAPTER in order to generate INTA messages on Express. + 10jjjjjj - specificies type of interrupt message to be generated (total 64 different messages supported). + All other values are reserved and should not be used. + + If interrupt generation is not required, ST field must be set upon creation to Fired state. No EQ arming doorbell should be performed. In this case hardware will not generate any interrupt. */ + pseudo_bit_t reserved6[0x00018]; +/* -------------- */ + pseudo_bit_t pd[0x00018]; /* PD to be used to access EQ */ + pseudo_bit_t reserved7[0x00008]; +/* -------------- */ + pseudo_bit_t lkey[0x00020]; /* Memory key (L-Key) to be used to access EQ */ +/* -------------- */ + pseudo_bit_t reserved8[0x00040]; +/* -------------- */ + pseudo_bit_t consumer_indx[0x00020];/* Contains next entry to be read upon polling the event queue. + Must be initalized to zero while opening EQ */ +/* -------------- */ + pseudo_bit_t producer_indx[0x00020];/* Contains next entry in EQ to be written by the HCA. + Must be initalized to zero while opening EQ. */ +/* -------------- */ + pseudo_bit_t reserved9[0x00080]; +/* -------------- */ +}; + +/* Memory Translation Table (MTT) Entry */ + +struct arbelprm_mtt_st { /* Little Endian */ + pseudo_bit_t ptag_h[0x00020]; /* High-order bits of physical tag. The size of the field depends on the page size of the region. Maximum PTAG size is 52 bits. */ +/* -------------- */ + pseudo_bit_t p[0x00001]; /* Present bit. If set, page entry is valid. If cleared, access to this page will generate non-present page access fault. */ + pseudo_bit_t reserved0[0x0000b]; + pseudo_bit_t ptag_l[0x00014]; /* Low-order bits of Physical tag. The size of the field depends on the page size of the region. Maximum PTAG size is 52 bits. */ +/* -------------- */ +}; + +/* Memory Protection Table (MPT) Entry */ + +struct arbelprm_mpt_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00008]; + pseudo_bit_t r_w[0x00001]; /* Defines whether this entry is Region (1) or Window (0) */ + pseudo_bit_t pa[0x00001]; /* Physical address. If set, no virtual-to-physical address translation will be performed for this region */ + pseudo_bit_t lr[0x00001]; /* If set - local read access enabled */ + pseudo_bit_t lw[0x00001]; /* If set - local write access enabled */ + pseudo_bit_t rr[0x00001]; /* If set - remote read access enabled. */ + pseudo_bit_t rw[0x00001]; /* If set - remote write access enabled */ + pseudo_bit_t a[0x00001]; /* If set - remote Atomic access is enabled */ + pseudo_bit_t eb[0x00001]; /* If set - Bind is enabled. Valid for region entry only. */ + pseudo_bit_t reserved1[0x0000c]; + pseudo_bit_t status[0x00004]; /* Region/Window Status + 0xF - not valid (SW ownership) + 0x3 - FREE state + else - HW ownership + Unbound Type I windows are doneted reg_wnd_len field equals zero. + Unbound Type II windows are donated by Status=FREE. */ +/* -------------- */ + pseudo_bit_t page_size[0x00005]; /* Page size used for the region. Actual size is [4K]*2^Page_size bytes. + page_size should be less than 20. */ + pseudo_bit_t reserved2[0x00002]; + pseudo_bit_t type[0x00001]; /* Applicable for windows only, must be zero for regions + 0 - Type one window + 1 - Type two window */ + pseudo_bit_t qpn[0x00018]; /* QP number this MW is attached to. Valid for type2 memory windows and on QUERY_MPT only */ +/* -------------- */ + pseudo_bit_t mem_key[0x00020]; /* The memory Key. The field holds the mem_key field in the following semantics: {key[7:0],key[31:8]}. + */ +/* -------------- */ + pseudo_bit_t pd[0x00018]; /* Protection Domain */ + pseudo_bit_t reserved3[0x00001]; + pseudo_bit_t ei[0x00001]; /* Enable Invalidation - When set, Local/Remote invalidation can be executed on this window/region. + Must be set for type2 windows and non-shared physical memory regions. + Must be clear for regions that are used to access Work Queues, Completion Queues and Event Queues */ + pseudo_bit_t zb[0x00001]; /* When set, this region is Zero Based Region */ + pseudo_bit_t fre[0x00001]; /* When set, Fast Registration Operations can be executed on this region */ + pseudo_bit_t rae[0x00001]; /* When set, remote access can be enabled on this region. + Used when executing Fast Registration Work Request to validate that remote access rights can be granted to this MPT. + If the bit is cleared, Fast Registration Work Request requesting remote access rights will fail. + */ + pseudo_bit_t reserved4[0x00003]; +/* -------------- */ + pseudo_bit_t start_address_h[0x00020];/* Start Address[63:32] - Virtual Address where this region/window starts */ +/* -------------- */ + pseudo_bit_t start_address_l[0x00020];/* Start Address[31:0] - Virtual Address where this region/window starts */ +/* -------------- */ + pseudo_bit_t reg_wnd_len_h[0x00020];/* Region/Window Length[63:32] */ +/* -------------- */ + pseudo_bit_t reg_wnd_len_l[0x00020];/* Region/Window Length[31:0] */ +/* -------------- */ + pseudo_bit_t lkey[0x00020]; /* Must be 0 for SW2HW_MPT. + On QUERY_MPT and HW2SW_MPT commands for Memory Window it reflects the LKey of the Region that the Window is bound to. + The field holds the lkey field in the following semantics: {key[7:0],key[31:8]}. */ +/* -------------- */ + pseudo_bit_t win_cnt[0x00020]; /* Number of windows bound to this region. Valid for regions only. + The field is valid only for the QUERY_MPT and HW2SW_MPT commands. */ +/* -------------- */ + pseudo_bit_t reserved5[0x00020]; +/* -------------- */ + pseudo_bit_t mtt_adr_h[0x00006]; /* Base (first) address of the MTT relative to MTT base in the ICM */ + pseudo_bit_t reserved6[0x0001a]; +/* -------------- */ + pseudo_bit_t reserved7[0x00003]; + pseudo_bit_t mtt_adr_l[0x0001d]; /* Base (first) address of the MTT relative to MTT base address in the ICM. Must be aligned on 8 bytes. */ +/* -------------- */ + pseudo_bit_t mtt_sz[0x00020]; /* Number of MTT entries allocated for this MR. + When Fast Registration Operations can not be executed on this region (FRE bit is zero) this field is reserved. + When Fast Registration Operation is enabled (FRE bit is set) this field indicates the number of MTTs allocated for this MR. If mtt_sz value is zero, there is no limit for the numbers of MTTs and the HCA does not check this field when executing fast register WQE. */ +/* -------------- */ + pseudo_bit_t reserved8[0x00040]; +/* -------------- */ +}; + +/* Completion Queue Context Table Entry */ + +struct arbelprm_completion_queue_context_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00008]; + pseudo_bit_t st[0x00004]; /* Event delivery state machine + 0x0 - reserved + 0x9 - ARMED (Request for Notification) + 0x6 - ARMED SOLICITED (Request Solicited Notification) + 0xA - FIRED + other - reserved + + Must be 0x0 in CQ initialization. + Valid for the QUERY_CQ and HW2SW_CQ commands only. */ + pseudo_bit_t reserved1[0x00005]; + pseudo_bit_t oi[0x00001]; /* When set, overrun ignore is enabled. + When set, Updates of CQ consumer counter (poll for completion) or Request completion notifications (Arm CQ) doorbells should not be rang on that CQ. */ + pseudo_bit_t reserved2[0x0000a]; + pseudo_bit_t status[0x00004]; /* CQ status + 0000 - OK + 1001 - CQ overflow + 1010 - CQ write failure + Valid for the QUERY_CQ and HW2SW_CQ commands only */ +/* -------------- */ + pseudo_bit_t start_address_h[0x00020];/* Start address of CQ[63:32]. + Must be aligned on CQE size (32 bytes) */ +/* -------------- */ + pseudo_bit_t start_address_l[0x00020];/* Start address of CQ[31:0]. + Must be aligned on CQE size (32 bytes) */ +/* -------------- */ + pseudo_bit_t usr_page[0x00018]; /* UAR page this CQ can be accessed through (ringinig CQ doorbells) */ + pseudo_bit_t log_cq_size[0x00005]; /* Log (base 2) of the CQ size (in entries). + Maximum CQ size is 2^17 CQEs (max log_cq_size is 17) */ + pseudo_bit_t reserved3[0x00003]; +/* -------------- */ + pseudo_bit_t reserved4[0x00020]; +/* -------------- */ + pseudo_bit_t c_eqn[0x00008]; /* Event Queue this CQ reports completion events to. + Valid values are 0 to 63 + If configured to value other than 0-63, completion events will not be reported on the CQ. */ + pseudo_bit_t reserved5[0x00018]; +/* -------------- */ + pseudo_bit_t pd[0x00018]; /* Protection Domain to be used to access CQ. + Must be the same PD of the CQ L_Key. */ + pseudo_bit_t reserved6[0x00008]; +/* -------------- */ + pseudo_bit_t l_key[0x00020]; /* Memory key (L_Key) to be used to access CQ */ +/* -------------- */ + pseudo_bit_t last_notified_indx[0x00020];/* Maintained by HW. + Valid for QUERY_CQ and HW2SW_CQ commands only. */ +/* -------------- */ + pseudo_bit_t solicit_producer_indx[0x00020];/* Maintained by HW. + Valid for QUERY_CQ and HW2SW_CQ commands only. + */ +/* -------------- */ + pseudo_bit_t consumer_counter[0x00020];/* Consumer counter is a 32bits counter that is incremented for each CQE pooled from the CQ. + Must be 0x0 in CQ initialization. + Valid for the QUERY_CQ and HW2SW_CQ commands only. */ +/* -------------- */ + pseudo_bit_t producer_counter[0x00020];/* Producer counter is a 32bits counter that is incremented for each CQE that is written by the HW to the CQ. + CQ overrun is reported if Producer_counter + 1 equals to Consumer_counter and a CQE needs to be added.. + Maintained by HW (valid for the QUERY_CQ and HW2SW_CQ commands only) */ +/* -------------- */ + pseudo_bit_t cqn[0x00018]; /* CQ number. Least significant bits are constrained by the position of this CQ in CQC table + Valid for the QUERY_CQ and HW2SW_CQ commands only */ + pseudo_bit_t reserved7[0x00008]; +/* -------------- */ + pseudo_bit_t cq_ci_db_record[0x00020];/* Index in the UAR Context Table Entry. + HW uses this index as an offset from the UAR Context Table Entry in order to read this CQ Consumer Counter doorbell record. + This value can be retrieved from the HW in the QUERY_CQ command. */ +/* -------------- */ + pseudo_bit_t cq_state_db_record[0x00020];/* Index in the UAR Context Table Entry. + HW uses this index as an offset from the UAR Context Table Entry in order to read this CQ state doorbell record. + This value can be retrieved from the HW in the QUERY_CQ command. */ +/* -------------- */ + pseudo_bit_t reserved8[0x00020]; +/* -------------- */ +}; + +/* GPIO_event_data */ + +struct arbelprm_gpio_event_data_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00060]; +/* -------------- */ + pseudo_bit_t gpio_event_hi[0x00020];/* If any bit is set to 1, then a rising/falling event has occurred on the corrsponding GPIO pin. */ +/* -------------- */ + pseudo_bit_t gpio_event_lo[0x00020];/* If any bit is set to 1, then a rising/falling event has occurred on the corrsponding GPIO pin. */ +/* -------------- */ + pseudo_bit_t reserved1[0x00020]; +/* -------------- */ +}; + +/* Event_data Field - QP/EE Events */ + +struct arbelprm_qp_ee_event_st { /* Little Endian */ + pseudo_bit_t qpn_een[0x00018]; /* QP/EE/SRQ number event is reported for */ + pseudo_bit_t reserved0[0x00008]; +/* -------------- */ + pseudo_bit_t reserved1[0x00020]; +/* -------------- */ + pseudo_bit_t reserved2[0x0001c]; + pseudo_bit_t e_q[0x00001]; /* If set - EEN if cleared - QP in the QPN/EEN field + Not valid on SRQ events */ + pseudo_bit_t reserved3[0x00003]; +/* -------------- */ + pseudo_bit_t reserved4[0x00060]; +/* -------------- */ +}; + +/* InfiniHost-III-EX Type0 Configuration Header */ + +struct arbelprm_mt25208_type0_st { /* Little Endian */ + pseudo_bit_t vendor_id[0x00010]; /* Hardwired to 0x15B3 */ + pseudo_bit_t device_id[0x00010]; /* 25208 (decimal) - InfiniHost-III compatible mode + 25218 (decimal) - InfiniHost-III EX mode (the mode described in this manual) + 25209 (decimal) - Flash burner mode - see Flash burning application note for further details on this mode + */ +/* -------------- */ + pseudo_bit_t command[0x00010]; /* PCI Command Register */ + pseudo_bit_t status[0x00010]; /* PCI Status Register */ +/* -------------- */ + pseudo_bit_t revision_id[0x00008]; + pseudo_bit_t class_code_hca_class_code[0x00018]; +/* -------------- */ + pseudo_bit_t cache_line_size[0x00008];/* Cache Line Size */ + pseudo_bit_t latency_timer[0x00008]; + pseudo_bit_t header_type[0x00008]; /* hardwired to zero */ + pseudo_bit_t bist[0x00008]; +/* -------------- */ + pseudo_bit_t bar0_ctrl[0x00004]; /* hard-wired to 0100 */ + pseudo_bit_t reserved0[0x00010]; + pseudo_bit_t bar0_l[0x0000c]; /* Lower bits of BAR0 (Device Configuration Space) */ +/* -------------- */ + pseudo_bit_t bar0_h[0x00020]; /* Upper 32 bits of BAR0 (Device Configuration Space) */ +/* -------------- */ + pseudo_bit_t bar1_ctrl[0x00004]; /* Hardwired to 1100 */ + pseudo_bit_t reserved1[0x00010]; + pseudo_bit_t bar1_l[0x0000c]; /* Lower bits of BAR1 (User Access Region - UAR - space) */ +/* -------------- */ + pseudo_bit_t bar1_h[0x00020]; /* upper 32 bits of BAR1 (User Access Region - UAR - space) */ +/* -------------- */ + pseudo_bit_t bar2_ctrl[0x00004]; /* Hardwired to 1100 */ + pseudo_bit_t reserved2[0x00010]; + pseudo_bit_t bar2_l[0x0000c]; /* Lower bits of BAR2 - Local Attached Memory if present and enabled. Else zeroed. */ +/* -------------- */ + pseudo_bit_t bar2_h[0x00020]; /* Upper 32 bits of BAR2 - Local Attached Memory if present and enabled. Else zeroed. */ +/* -------------- */ + pseudo_bit_t cardbus_cis_pointer[0x00020]; +/* -------------- */ + pseudo_bit_t subsystem_vendor_id[0x00010];/* Specified by the device NVMEM configuration */ + pseudo_bit_t subsystem_id[0x00010]; /* Specified by the device NVMEM configuration */ +/* -------------- */ + pseudo_bit_t expansion_rom_enable[0x00001];/* Expansion ROM Enable. Hardwired to 0 if expansion ROM is disabled in the device NVMEM configuration. */ + pseudo_bit_t reserved3[0x0000a]; + pseudo_bit_t expansion_rom_base_address[0x00015];/* Expansion ROM Base Address (upper 21 bit). Hardwired to 0 if expansion ROM is disabled in the device NVMEM configuration. */ +/* -------------- */ + pseudo_bit_t capabilities_pointer[0x00008];/* Specified by the device NVMEM configuration */ + pseudo_bit_t reserved4[0x00018]; +/* -------------- */ + pseudo_bit_t reserved5[0x00020]; +/* -------------- */ + pseudo_bit_t interrupt_line[0x00008]; + pseudo_bit_t interrupt_pin[0x00008]; + pseudo_bit_t min_gnt[0x00008]; + pseudo_bit_t max_latency[0x00008]; +/* -------------- */ + pseudo_bit_t reserved6[0x00100]; +/* -------------- */ + pseudo_bit_t msi_cap_id[0x00008]; + pseudo_bit_t msi_next_cap_ptr[0x00008]; + pseudo_bit_t msi_en[0x00001]; + pseudo_bit_t multiple_msg_cap[0x00003]; + pseudo_bit_t multiple_msg_en[0x00003]; + pseudo_bit_t cap_64_bit_addr[0x00001]; + pseudo_bit_t reserved7[0x00008]; +/* -------------- */ + pseudo_bit_t msg_addr_l[0x00020]; +/* -------------- */ + pseudo_bit_t msg_addr_h[0x00020]; +/* -------------- */ + pseudo_bit_t msg_data[0x00010]; + pseudo_bit_t reserved8[0x00010]; +/* -------------- */ + pseudo_bit_t reserved9[0x00080]; +/* -------------- */ + pseudo_bit_t pm_cap_id[0x00008]; /* Power management capability ID - 01h */ + pseudo_bit_t pm_next_cap_ptr[0x00008]; + pseudo_bit_t pm_cap[0x00010]; /* [2:0] Version - 02h + [3] PME clock - 0h + [4] RsvP + [5] Device specific initialization - 0h + [8:6] AUX current - 0h + [9] D1 support - 0h + [10] D2 support - 0h + [15:11] PME support - 0h */ +/* -------------- */ + pseudo_bit_t pm_status_control[0x00010];/* [14:13] - Data scale - 0h */ + pseudo_bit_t pm_control_status_brdg_ext[0x00008]; + pseudo_bit_t data[0x00008]; +/* -------------- */ + pseudo_bit_t reserved10[0x00040]; +/* -------------- */ + pseudo_bit_t vpd_cap_id[0x00008]; /* 03h */ + pseudo_bit_t vpd_next_cap_id[0x00008]; + pseudo_bit_t vpd_address[0x0000f]; + pseudo_bit_t f[0x00001]; +/* -------------- */ + pseudo_bit_t vpd_data[0x00020]; +/* -------------- */ + pseudo_bit_t reserved11[0x00040]; +/* -------------- */ + pseudo_bit_t pciex_cap_id[0x00008]; /* PCI-Express capability ID - 10h */ + pseudo_bit_t pciex_next_cap_ptr[0x00008]; + pseudo_bit_t pciex_cap[0x00010]; /* [3:0] Capability version - 1h + [7:4] Device/Port Type - 0h + [8] Slot implemented - 0h + [13:9] Interrupt message number + */ +/* -------------- */ + pseudo_bit_t device_cap[0x00020]; /* [2:0] Max_Payload_Size supported - 2h + [4:3] Phantom Function supported - 0h + [5] Extended Tag Filed supported - 0h + [8:6] Endpoint L0s Acceptable Latency - TBD + [11:9] Endpoint L1 Acceptable Latency - TBD + [12] Attention Button Present - configured through InfiniBurn + [13] Attention Indicator Present - configured through InfiniBurn + [14] Power Indicator Present - configured through InfiniBurn + [25:18] Captured Slot Power Limit Value + [27:26] Captured Slot Power Limit Scale */ +/* -------------- */ + pseudo_bit_t device_control[0x00010]; + pseudo_bit_t device_status[0x00010]; +/* -------------- */ + pseudo_bit_t link_cap[0x00020]; /* [3:0] Maximum Link Speed - 1h + [9:4] Maximum Link Width - 8h + [11:10] Active State Power Management Support - 3h + [14:12] L0s Exit Latency - TBD + [17:15] L1 Exit Latency - TBD + [31:24] Port Number - 0h */ +/* -------------- */ + pseudo_bit_t link_control[0x00010]; + pseudo_bit_t link_status[0x00010]; /* [3:0] Link Speed - 1h + [9:4] Negotiated Link Width + [12] Slot clock configuration - 1h */ +/* -------------- */ + pseudo_bit_t reserved12[0x00260]; +/* -------------- */ + pseudo_bit_t advanced_error_reporting_cap_id[0x00010];/* 0001h. */ + pseudo_bit_t capability_version[0x00004];/* 1h */ + pseudo_bit_t next_capability_offset[0x0000c];/* 0h */ +/* -------------- */ + pseudo_bit_t uncorrectable_error_status_register[0x00020];/* 0 Training Error Status + 4 Data Link Protocol Error Status + 12 Poisoned TLP Status + 13 Flow Control Protocol Error Status + 14 Completion Timeout Status + 15 Completer Abort Status + 16 Unexpected Completion Status + 17 Receiver Overflow Status + 18 Malformed TLP Status + 19 ECRC Error Status + 20 Unsupported Request Error Status */ +/* -------------- */ + pseudo_bit_t uncorrectable_error_mask_register[0x00020];/* 0 Training Error Mask + 4 Data Link Protocol Error Mask + 12 Poisoned TLP Mask + 13 Flow Control Protocol Error Mask + 14 Completion Timeout Mask + 15 Completer Abort Mask + 16 Unexpected Completion Mask + 17 Receiver Overflow Mask + 18 Malformed TLP Mask + 19 ECRC Error Mask + 20 Unsupported Request Error Mask */ +/* -------------- */ + pseudo_bit_t uncorrectable_severity_mask_register[0x00020];/* 0 Training Error Severity + 4 Data Link Protocol Error Severity + 12 Poisoned TLP Severity + 13 Flow Control Protocol Error Severity + 14 Completion Timeout Severity + 15 Completer Abort Severity + 16 Unexpected Completion Severity + 17 Receiver Overflow Severity + 18 Malformed TLP Severity + 19 ECRC Error Severity + 20 Unsupported Request Error Severity */ +/* -------------- */ + pseudo_bit_t correctable_error_status_register[0x00020];/* 0 Receiver Error Status + 6 Bad TLP Status + 7 Bad DLLP Status + 8 REPLAY_NUM Rollover Status + 12 Replay Timer Timeout Status */ +/* -------------- */ + pseudo_bit_t correctable_error_mask_register[0x00020];/* 0 Receiver Error Mask + 6 Bad TLP Mask + 7 Bad DLLP Mask + 8 REPLAY_NUM Rollover Mask + 12 Replay Timer Timeout Mask */ +/* -------------- */ + pseudo_bit_t advance_error_capabilities_and_control_register[0x00020]; +/* -------------- */ + struct arbelprm_header_log_register_st header_log_register; +/* -------------- */ + pseudo_bit_t reserved13[0x006a0]; +/* -------------- */ +}; + +/* Event Data Field - Performance Monitor */ + +struct arbelprm_performance_monitor_event_st { /* Little Endian */ + struct arbelprm_performance_monitors_st performance_monitor_snapshot;/* Performance monitor snapshot */ +/* -------------- */ + pseudo_bit_t monitor_number[0x00008];/* 0x01 - SQPC + 0x02 - RQPC + 0x03 - CQC + 0x04 - Rkey + 0x05 - TLB + 0x06 - port0 + 0x07 - port1 */ + pseudo_bit_t reserved0[0x00018]; +/* -------------- */ + pseudo_bit_t reserved1[0x00040]; +/* -------------- */ +}; + +/* Event_data Field - Page Faults */ + +struct arbelprm_page_fault_event_data_st { /* Little Endian */ + pseudo_bit_t va_h[0x00020]; /* Virtual Address[63:32] this page fault is reported on */ +/* -------------- */ + pseudo_bit_t va_l[0x00020]; /* Virtual Address[63:32] this page fault is reported on */ +/* -------------- */ + pseudo_bit_t mem_key[0x00020]; /* Memory Key this page fault is reported on */ +/* -------------- */ + pseudo_bit_t qp[0x00018]; /* QP this page fault is reported on */ + pseudo_bit_t reserved0[0x00003]; + pseudo_bit_t a[0x00001]; /* If set the memory access that caused the page fault was atomic */ + pseudo_bit_t lw[0x00001]; /* If set the memory access that caused the page fault was local write */ + pseudo_bit_t lr[0x00001]; /* If set the memory access that caused the page fault was local read */ + pseudo_bit_t rw[0x00001]; /* If set the memory access that caused the page fault was remote write */ + pseudo_bit_t rr[0x00001]; /* If set the memory access that caused the page fault was remote read */ +/* -------------- */ + pseudo_bit_t pd[0x00018]; /* PD this page fault is reported on */ + pseudo_bit_t reserved1[0x00008]; +/* -------------- */ + pseudo_bit_t prefetch_len[0x00020]; /* Indicates how many subsequent pages in the same memory region/window will be accessed by the following transaction after this page fault is resolved. measured in bytes. SW can use this information in order to page-in the subsequent pages if they are not present. */ +/* -------------- */ +}; + +/* WQE segments format */ + +struct arbelprm_wqe_segment_st { /* Little Endian */ + struct arbelprm_send_wqe_segment_st send_wqe_segment;/* Send WQE segment format */ +/* -------------- */ + pseudo_bit_t reserved0[0x00280]; +/* -------------- */ + struct arbelprm_wqe_segment_ctrl_mlx_st mlx_wqe_segment_ctrl;/* MLX WQE segment format */ +/* -------------- */ + pseudo_bit_t reserved1[0x00100]; +/* -------------- */ + struct arbelprm_wqe_segment_ctrl_recv_st recv_wqe_segment_ctrl;/* Receive segment format */ +/* -------------- */ + pseudo_bit_t reserved2[0x00080]; +/* -------------- */ +}; + +/* Event_data Field - Port State Change */ + +struct arbelprm_port_state_change_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00040]; +/* -------------- */ + pseudo_bit_t reserved1[0x0001c]; + pseudo_bit_t p[0x00002]; /* Port number (1 or 2) */ + pseudo_bit_t reserved2[0x00002]; +/* -------------- */ + pseudo_bit_t reserved3[0x00060]; +/* -------------- */ +}; + +/* Event_data Field - Completion Queue Error */ + +struct arbelprm_completion_queue_error_st { /* Little Endian */ + pseudo_bit_t cqn[0x00018]; /* CQ number event is reported for */ + pseudo_bit_t reserved0[0x00008]; +/* -------------- */ + pseudo_bit_t reserved1[0x00020]; +/* -------------- */ + pseudo_bit_t syndrome[0x00008]; /* Error syndrome + 0x01 - CQ overrun + 0x02 - CQ access violation error */ + pseudo_bit_t reserved2[0x00018]; +/* -------------- */ + pseudo_bit_t reserved3[0x00060]; +/* -------------- */ +}; + +/* Event_data Field - Completion Event */ + +struct arbelprm_completion_event_st { /* Little Endian */ + pseudo_bit_t cqn[0x00018]; /* CQ number event is reported for */ + pseudo_bit_t reserved0[0x00008]; +/* -------------- */ + pseudo_bit_t reserved1[0x000a0]; +/* -------------- */ +}; + +/* Event Queue Entry */ + +struct arbelprm_event_queue_entry_st { /* Little Endian */ + pseudo_bit_t event_sub_type[0x00008];/* Event Sub Type. + Defined for events which have sub types, zero elsewhere. */ + pseudo_bit_t reserved0[0x00008]; + pseudo_bit_t event_type[0x00008]; /* Event Type */ + pseudo_bit_t reserved1[0x00008]; +/* -------------- */ + pseudo_bit_t event_data[6][0x00020];/* Delivers auxilary data to handle event. */ +/* -------------- */ + pseudo_bit_t reserved2[0x00007]; + pseudo_bit_t owner[0x00001]; /* Owner of the entry + 0 SW + 1 HW */ + pseudo_bit_t reserved3[0x00018]; +/* -------------- */ +}; + +/* QP/EE State Transitions Command Parameters */ + +struct arbelprm_qp_ee_state_transitions_st { /* Little Endian */ + pseudo_bit_t opt_param_mask[0x00020];/* This field defines which optional parameters are passed. Each bit specifies whether optional parameter is passed (set) or not (cleared). The optparammask is defined for each QP/EE command. */ +/* -------------- */ + pseudo_bit_t reserved0[0x00020]; +/* -------------- */ + struct arbelprm_queue_pair_ee_context_entry_st qpc_eec_data;/* QPC/EEC data */ +/* -------------- */ + pseudo_bit_t reserved1[0x009c0]; +/* -------------- */ +}; + +/* Completion Queue Entry Format */ + +struct arbelprm_completion_queue_entry_st { /* Little Endian */ + pseudo_bit_t my_qpn[0x00018]; /* Indicates the QP for which completion is being reported */ + pseudo_bit_t reserved0[0x00004]; + pseudo_bit_t ver[0x00004]; /* CQE version. + 0 for InfiniHost-III-EX */ +/* -------------- */ + pseudo_bit_t my_ee[0x00018]; /* EE context (for RD only). + Invalid for Bind and Nop operation on RD. + For non RD services this filed reports the CQE timestamp. The Timestamp is a free running counter that is incremented every TimeStampGranularity tick. The counter rolls-over when it reaches saturation. TimeStampGranularity is configured in the INIT_HCA command. This feature is currently not supported. + */ + pseudo_bit_t checksum_15_8[0x00008];/* Checksum[15:8] - See IPoverIB checksum offloading chapter */ +/* -------------- */ + pseudo_bit_t rqpn[0x00018]; /* Remote (source) QP number. Valid in Responder CQE only for Datagram QP. */ + pseudo_bit_t checksum_7_0[0x00008]; /* Checksum[7:0] - See IPoverIB checksum offloading chapter */ +/* -------------- */ + pseudo_bit_t rlid[0x00010]; /* Remote (source) LID of the message. Valid in Responder of UD QP CQE only. */ + pseudo_bit_t ml_path[0x00007]; /* My (destination) LID path bits - these are the lowemost LMC bits of the DLID in an incoming UD packet, higher bits of this field, that are not part of the LMC bits are zeroed by HW. + Valid in responder of UD QP CQE only. + Invalid if incoming message DLID is the permissive LID or incoming message is multicast. */ + pseudo_bit_t g[0x00001]; /* GRH present indicator. Valid in Responder of UD QP CQE only. */ + pseudo_bit_t ipok[0x00001]; /* IP OK - See IPoverIB checksum offloading chapter */ + pseudo_bit_t reserved1[0x00003]; + pseudo_bit_t sl[0x00004]; /* Service Level of the message. Valid in Responder of UD QP CQE only. */ +/* -------------- */ + pseudo_bit_t immediate_ethertype_pkey_indx_eecredits[0x00020];/* Valid for receive queue completion only. + If Opcode field indicates that this was send/write with immediate, this field contains immediate field of the packet. + If completion corresponds to RAW receive queue, bits 15:0 contain Ethertype field of the packet. + If completion corresponds to GSI receive queue, bits 31:16 contain index in PKey table that matches PKey of the message arrived. + If Opcode field indicates that this was send and invalidate, this field contains the key that was invalidated. + For CQE of send queue of the reliable connection service (but send and invalide), bits [4:0] of this field contain the encoded EEcredits received in last ACK of the message. */ +/* -------------- */ + pseudo_bit_t byte_cnt[0x00020]; /* Byte count of data actually transferred (valid for receive queue completions only) */ +/* -------------- */ + pseudo_bit_t reserved2[0x00006]; + pseudo_bit_t wqe_adr[0x0001a]; /* Bits 31:6 of WQE virtual address completion is reported for. The 6 least significant bits are zero. */ +/* -------------- */ + pseudo_bit_t reserved3[0x00007]; + pseudo_bit_t owner[0x00001]; /* Owner field. Zero value of this field means SW ownership of CQE. */ + pseudo_bit_t reserved4[0x0000f]; + pseudo_bit_t s[0x00001]; /* If set, completion is reported for Send queue, if cleared - receive queue. */ + pseudo_bit_t opcode[0x00008]; /* The opcode of WQE completion is reported for. + For CQEs corresponding to send completion, NOPCODE field of the WQE is copied to this field. + For CQEs corresponding to receive completions, opcode field of last packet in the message copied to this field. + For CQEs corresponding to the receive queue of QPs mapped to QP1, the opcode will be SEND with Immediate (messages are guaranteed to be SEND only) + + The following values are reported in case of completion with error: + 0xFE - For completion with error on Receive Queues + 0xFF - For completion with error on Send Queues */ +/* -------------- */ +}; + +/* */ + +struct arbelprm_ecc_detect_event_data_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00080]; +/* -------------- */ + pseudo_bit_t cause_lsb[0x00001]; + pseudo_bit_t reserved1[0x00002]; + pseudo_bit_t cause_msb[0x00001]; + pseudo_bit_t reserved2[0x00002]; + pseudo_bit_t err_rmw[0x00001]; + pseudo_bit_t err_src_id[0x00003]; + pseudo_bit_t err_da[0x00002]; + pseudo_bit_t err_ba[0x00002]; + pseudo_bit_t reserved3[0x00011]; + pseudo_bit_t overflow[0x00001]; +/* -------------- */ + pseudo_bit_t err_ra[0x00010]; + pseudo_bit_t err_ca[0x00010]; +/* -------------- */ +}; + +/* Event_data Field - ECC Detection Event */ + +struct arbelprm_scrubbing_event_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00080]; +/* -------------- */ + pseudo_bit_t cause_lsb[0x00001]; /* data integrity error cause: + single ECC error in the 64bit lsb data, on the rise edge of the clock */ + pseudo_bit_t reserved1[0x00002]; + pseudo_bit_t cause_msb[0x00001]; /* data integrity error cause: + single ECC error in the 64bit msb data, on the fall edge of the clock */ + pseudo_bit_t reserved2[0x00002]; + pseudo_bit_t err_rmw[0x00001]; /* transaction type: + 0 - read + 1 - read/modify/write */ + pseudo_bit_t err_src_id[0x00003]; /* source of the transaction: 0x4 - PCI, other - internal or IB */ + pseudo_bit_t err_da[0x00002]; /* Error DIMM address */ + pseudo_bit_t err_ba[0x00002]; /* Error bank address */ + pseudo_bit_t reserved3[0x00011]; + pseudo_bit_t overflow[0x00001]; /* Fatal: ECC error FIFO overflow - ECC errors were detected, which may or may not have been corrected by InfiniHost-III-EX */ +/* -------------- */ + pseudo_bit_t err_ra[0x00010]; /* Error row address */ + pseudo_bit_t err_ca[0x00010]; /* Error column address */ +/* -------------- */ +}; + +/* Miscellaneous Counters */ + +struct arbelprm_misc_counters_st { /* Little Endian */ + pseudo_bit_t ddr_scan_cnt[0x00020]; /* Number of times whole of LAM was scanned */ +/* -------------- */ + pseudo_bit_t reserved0[0x007e0]; +/* -------------- */ +}; + +/* LAM_EN Output Parameter */ + +struct arbelprm_lam_en_out_param_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00040]; +/* -------------- */ +}; + +/* Extended_Completion_Queue_Entry */ + +struct arbelprm_extended_completion_queue_entry_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00020]; +/* -------------- */ +}; + +/* */ + +struct arbelprm_eq_cmd_doorbell_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00020]; +/* -------------- */ +}; + +/* 0 */ + +struct arbelprm_arbel_prm_st { /* Little Endian */ + struct arbelprm_completion_queue_entry_st completion_queue_entry;/* Completion Queue Entry Format */ +/* -------------- */ + pseudo_bit_t reserved0[0x7ff00]; +/* -------------- */ + struct arbelprm_qp_ee_state_transitions_st qp_ee_state_transitions;/* QP/EE State Transitions Command Parameters */ +/* -------------- */ + pseudo_bit_t reserved1[0x7f000]; +/* -------------- */ + struct arbelprm_event_queue_entry_st event_queue_entry;/* Event Queue Entry */ +/* -------------- */ + pseudo_bit_t reserved2[0x7ff00]; +/* -------------- */ + struct arbelprm_completion_event_st completion_event;/* Event_data Field - Completion Event */ +/* -------------- */ + pseudo_bit_t reserved3[0x7ff40]; +/* -------------- */ + struct arbelprm_completion_queue_error_st completion_queue_error;/* Event_data Field - Completion Queue Error */ +/* -------------- */ + pseudo_bit_t reserved4[0x7ff40]; +/* -------------- */ + struct arbelprm_port_state_change_st port_state_change;/* Event_data Field - Port State Change */ +/* -------------- */ + pseudo_bit_t reserved5[0x7ff40]; +/* -------------- */ + struct arbelprm_wqe_segment_st wqe_segment;/* WQE segments format */ +/* -------------- */ + pseudo_bit_t reserved6[0x7f000]; +/* -------------- */ + struct arbelprm_page_fault_event_data_st page_fault_event_data;/* Event_data Field - Page Faults */ +/* -------------- */ + pseudo_bit_t reserved7[0x7ff40]; +/* -------------- */ + struct arbelprm_performance_monitor_event_st performance_monitor_event;/* Event Data Field - Performance Monitor */ +/* -------------- */ + pseudo_bit_t reserved8[0xfff20]; +/* -------------- */ + struct arbelprm_mt25208_type0_st mt25208_type0;/* InfiniHost-III-EX Type0 Configuration Header */ +/* -------------- */ + pseudo_bit_t reserved9[0x7f000]; +/* -------------- */ + struct arbelprm_qp_ee_event_st qp_ee_event;/* Event_data Field - QP/EE Events */ +/* -------------- */ + pseudo_bit_t reserved10[0x00040]; +/* -------------- */ + struct arbelprm_gpio_event_data_st gpio_event_data; +/* -------------- */ + pseudo_bit_t reserved11[0x7fe40]; +/* -------------- */ + struct arbelprm_ud_address_vector_st ud_address_vector;/* UD Address Vector */ +/* -------------- */ + pseudo_bit_t reserved12[0x7ff00]; +/* -------------- */ + struct arbelprm_queue_pair_ee_context_entry_st queue_pair_ee_context_entry;/* QP and EE Context Entry */ +/* -------------- */ + pseudo_bit_t reserved13[0x7fa00]; +/* -------------- */ + struct arbelprm_address_path_st address_path;/* Address Path */ +/* -------------- */ + pseudo_bit_t reserved14[0x7ff00]; +/* -------------- */ + struct arbelprm_completion_queue_context_st completion_queue_context;/* Completion Queue Context Table Entry */ +/* -------------- */ + pseudo_bit_t reserved15[0x7fe00]; +/* -------------- */ + struct arbelprm_mpt_st mpt; /* Memory Protection Table (MPT) Entry */ +/* -------------- */ + pseudo_bit_t reserved16[0x7fe00]; +/* -------------- */ + struct arbelprm_mtt_st mtt; /* Memory Translation Table (MTT) Entry */ +/* -------------- */ + pseudo_bit_t reserved17[0x7ffc0]; +/* -------------- */ + struct arbelprm_eqc_st eqc; /* Event Queue Context Table Entry */ +/* -------------- */ + pseudo_bit_t reserved18[0x7fe00]; +/* -------------- */ + struct arbelprm_performance_monitors_st performance_monitors;/* Performance Monitors */ +/* -------------- */ + pseudo_bit_t reserved19[0x7ff80]; +/* -------------- */ + struct arbelprm_hca_command_register_st hca_command_register;/* HCA Command Register (HCR) */ +/* -------------- */ + pseudo_bit_t reserved20[0xfff20]; +/* -------------- */ + struct arbelprm_init_hca_st init_hca;/* INIT_HCA & QUERY_HCA Parameters Block */ +/* -------------- */ + pseudo_bit_t reserved21[0x7f000]; +/* -------------- */ + struct arbelprm_qpcbaseaddr_st qpcbaseaddr;/* QPC/EEC/CQC/EQC/RDB Parameters */ +/* -------------- */ + pseudo_bit_t reserved22[0x7fc00]; +/* -------------- */ + struct arbelprm_udavtable_memory_parameters_st udavtable_memory_parameters;/* Memory Access Parameters for UD Address Vector Table */ +/* -------------- */ + pseudo_bit_t reserved23[0x7ffc0]; +/* -------------- */ + struct arbelprm_multicastparam_st multicastparam;/* Multicast Support Parameters */ +/* -------------- */ + pseudo_bit_t reserved24[0x7ff00]; +/* -------------- */ + struct arbelprm_tptparams_st tptparams;/* Translation and Protection Tables Parameters */ +/* -------------- */ + pseudo_bit_t reserved25[0x7ff00]; +/* -------------- */ + struct arbelprm_enable_lam_st enable_lam;/* ENABLE_LAM Parameters Block */ +/* -------------- */ + struct arbelprm_access_lam_st access_lam; +/* -------------- */ + pseudo_bit_t reserved26[0x7f700]; +/* -------------- */ + struct arbelprm_dimminfo_st dimminfo;/* Logical DIMM Information */ +/* -------------- */ + pseudo_bit_t reserved27[0x7ff00]; +/* -------------- */ + struct arbelprm_query_fw_st query_fw;/* QUERY_FW Parameters Block */ +/* -------------- */ + pseudo_bit_t reserved28[0x7f800]; +/* -------------- */ + struct arbelprm_query_adapter_st query_adapter;/* QUERY_ADAPTER Parameters Block */ +/* -------------- */ + pseudo_bit_t reserved29[0x7f800]; +/* -------------- */ + struct arbelprm_query_dev_lim_st query_dev_lim;/* Query Device Limitations */ +/* -------------- */ + pseudo_bit_t reserved30[0x7f800]; +/* -------------- */ + struct arbelprm_uar_params_st uar_params;/* UAR Parameters */ +/* -------------- */ + pseudo_bit_t reserved31[0x7ff00]; +/* -------------- */ + struct arbelprm_init_ib_st init_ib; /* INIT_IB Parameters */ +/* -------------- */ + pseudo_bit_t reserved32[0x7f800]; +/* -------------- */ + struct arbelprm_mgm_entry_st mgm_entry;/* Multicast Group Member */ +/* -------------- */ + pseudo_bit_t reserved33[0x7fe00]; +/* -------------- */ + struct arbelprm_set_ib_st set_ib; /* SET_IB Parameters */ +/* -------------- */ + pseudo_bit_t reserved34[0x7fe00]; +/* -------------- */ + struct arbelprm_rd_send_doorbell_st rd_send_doorbell;/* RD-send doorbell */ +/* -------------- */ + pseudo_bit_t reserved35[0x7ff80]; +/* -------------- */ + struct arbelprm_send_doorbell_st send_doorbell;/* Send doorbell */ +/* -------------- */ + pseudo_bit_t reserved36[0x7ffc0]; +/* -------------- */ + struct arbelprm_receive_doorbell_st receive_doorbell;/* Receive doorbell */ +/* -------------- */ + pseudo_bit_t reserved37[0x7ffc0]; +/* -------------- */ + struct arbelprm_cq_cmd_doorbell_st cq_cmd_doorbell;/* CQ Doorbell */ +/* -------------- */ + pseudo_bit_t reserved38[0xfffc0]; +/* -------------- */ + struct arbelprm_uar_st uar; /* User Access Region */ +/* -------------- */ + pseudo_bit_t reserved39[0x7c000]; +/* -------------- */ + struct arbelprm_mgmqp_st mgmqp; /* Multicast Group Member QP */ +/* -------------- */ + pseudo_bit_t reserved40[0x7ffe0]; +/* -------------- */ + struct arbelprm_query_debug_msg_st query_debug_msg;/* Query Debug Message */ +/* -------------- */ + pseudo_bit_t reserved41[0x7f800]; +/* -------------- */ + struct arbelprm_mad_ifc_st mad_ifc; /* MAD_IFC Input Mailbox */ +/* -------------- */ + pseudo_bit_t reserved42[0x00900]; +/* -------------- */ + struct arbelprm_mad_ifc_input_modifier_st mad_ifc_input_modifier;/* MAD_IFC Input Modifier */ +/* -------------- */ + pseudo_bit_t reserved43[0x7e6e0]; +/* -------------- */ + struct arbelprm_resize_cq_st resize_cq;/* Resize CQ Input Mailbox */ +/* -------------- */ + pseudo_bit_t reserved44[0x7fe00]; +/* -------------- */ + struct arbelprm_completion_with_error_st completion_with_error;/* Completion with Error CQE */ +/* -------------- */ + pseudo_bit_t reserved45[0x7ff00]; +/* -------------- */ + struct arbelprm_hcr_completion_event_st hcr_completion_event;/* Event_data Field - HCR Completion Event */ +/* -------------- */ + pseudo_bit_t reserved46[0x7ff40]; +/* -------------- */ + struct arbelprm_transport_and_ci_error_counters_st transport_and_ci_error_counters;/* Transport and CI Error Counters */ +/* -------------- */ + pseudo_bit_t reserved47[0x7f000]; +/* -------------- */ + struct arbelprm_performance_counters_st performance_counters;/* Performance Counters */ +/* -------------- */ + pseudo_bit_t reserved48[0x9ff800]; +/* -------------- */ + struct arbelprm_fast_registration_segment_st fast_registration_segment;/* Fast Registration Segment */ +/* -------------- */ + pseudo_bit_t reserved49[0x7ff00]; +/* -------------- */ + struct arbelprm_pbl_st pbl; /* Physical Buffer List */ +/* -------------- */ + pseudo_bit_t reserved50[0x7ff00]; +/* -------------- */ + struct arbelprm_srq_context_st srq_context;/* SRQ Context */ +/* -------------- */ + pseudo_bit_t reserved51[0x7fe80]; +/* -------------- */ + struct arbelprm_mod_stat_cfg_st mod_stat_cfg;/* MOD_STAT_CFG */ +/* -------------- */ + pseudo_bit_t reserved52[0x7f800]; +/* -------------- */ + struct arbelprm_virtual_physical_mapping_st virtual_physical_mapping;/* Virtual and Physical Mapping */ +/* -------------- */ + pseudo_bit_t reserved53[0x7ff80]; +/* -------------- */ + struct arbelprm_cq_ci_db_record_st cq_ci_db_record;/* CQ_CI_DB_Record */ +/* -------------- */ + pseudo_bit_t reserved54[0x7ffc0]; +/* -------------- */ + struct arbelprm_cq_arm_db_record_st cq_arm_db_record;/* CQ_ARM_DB_Record */ +/* -------------- */ + pseudo_bit_t reserved55[0x7ffc0]; +/* -------------- */ + struct arbelprm_qp_db_record_st qp_db_record;/* QP_DB_Record */ +/* -------------- */ + pseudo_bit_t reserved56[0x1fffc0]; +/* -------------- */ + struct arbelprm_configuration_registers_st configuration_registers;/* InfiniHost III EX Configuration Registers */ +/* -------------- */ + struct arbelprm_eq_set_ci_table_st eq_set_ci_table;/* EQ Set CI DBs Table */ +/* -------------- */ + pseudo_bit_t reserved57[0x01000]; +/* -------------- */ + struct arbelprm_eq_arm_db_region_st eq_arm_db_region;/* EQ Arm Doorbell Region */ +/* -------------- */ + pseudo_bit_t reserved58[0x00fc0]; +/* -------------- */ + struct arbelprm_clr_int_st clr_int; /* Clear Interrupt Register */ +/* -------------- */ + pseudo_bit_t reserved59[0xffcfc0]; +/* -------------- */ +}; +#endif /* H_prefix_arbelprm_bits_fixnames_MT25218_PRM_csp_H */ diff --git a/gpxe/src/drivers/infiniband/MT25408_PRM.h b/gpxe/src/drivers/infiniband/MT25408_PRM.h new file mode 100644 index 0000000..17882ed --- /dev/null +++ b/gpxe/src/drivers/infiniband/MT25408_PRM.h @@ -0,0 +1,3313 @@ +/* + This software is available to you under a choice of one of two + licenses. You may choose to be licensed under the terms of the GNU + General Public License (GPL) Version 2, available at + , or the OpenIB.org BSD + license, available in the LICENSE.TXT file accompanying this + software. These details are also available at + . + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + Copyright (c) 2004 Mellanox Technologies Ltd. All rights reserved. +*/ + +/*** + *** This file was generated at "Mon Apr 16 23:22:02 2007" + *** by: + *** % csp_bf -copyright=/mswg/misc/license-header.txt -prefix hermonprm_ -bits -fixnames MT25408_PRM.csp + ***/ + +#ifndef H_prefix_hermonprm_bits_fixnames_MT25408_PRM_csp_H +#define H_prefix_hermonprm_bits_fixnames_MT25408_PRM_csp_H + +/* UD Address Vector */ + +struct hermonprm_ud_address_vector_st { /* Little Endian */ + pseudo_bit_t pd[0x00018]; /* Protection Domain */ + pseudo_bit_t port_number[0x00002]; /* Port number + 1 - Port 1 + 2 - Port 2 + other - reserved */ + pseudo_bit_t reserved0[0x00005]; + pseudo_bit_t fl[0x00001]; /* force loopback */ +/* -------------- */ + pseudo_bit_t rlid[0x00010]; /* Remote (Destination) LID */ + pseudo_bit_t my_lid_path_bits[0x00007];/* Source LID - the lower 7 bits (upper bits are taken from PortInfo) */ + pseudo_bit_t g[0x00001]; /* Global address enable - if set, GRH will be formed for packet header */ + pseudo_bit_t reserved1[0x00008]; +/* -------------- */ + pseudo_bit_t hop_limit[0x00008]; /* IPv6 hop limit */ + pseudo_bit_t max_stat_rate[0x00004];/* Maximum static rate control. + 0 - 4X injection rate + 1 - 1X injection rate + other - reserved + */ + pseudo_bit_t reserved2[0x00004]; + pseudo_bit_t mgid_index[0x00007]; /* Index to port GID table + mgid_index = (port_number-1) * 2^log_max_gid + gid_index + Where: + 1. log_max_gid is taken from QUERY_DEV_CAP command + 2. gid_index is the index to the GID table */ + pseudo_bit_t reserved3[0x00009]; +/* -------------- */ + pseudo_bit_t flow_label[0x00014]; /* IPv6 flow label */ + pseudo_bit_t tclass[0x00008]; /* IPv6 TClass */ + pseudo_bit_t sl[0x00004]; /* InfiniBand Service Level (SL) */ +/* -------------- */ + pseudo_bit_t rgid_127_96[0x00020]; /* Remote GID[127:96] */ +/* -------------- */ + pseudo_bit_t rgid_95_64[0x00020]; /* Remote GID[95:64] */ +/* -------------- */ + pseudo_bit_t rgid_63_32[0x00020]; /* Remote GID[63:32] */ +/* -------------- */ + pseudo_bit_t rgid_31_0[0x00020]; /* Remote GID[31:0] if G bit is set. Must be set to 0x2 if G bit is cleared. */ +/* -------------- */ +}; + +/* Send doorbell */ + +struct hermonprm_send_doorbell_st { /* Little Endian */ + pseudo_bit_t nopcode[0x00005]; /* Opcode of descriptor to be executed */ + pseudo_bit_t f[0x00001]; /* Fence bit. If set, descriptor is fenced */ + pseudo_bit_t reserved0[0x00002]; + pseudo_bit_t wqe_counter[0x00010]; /* Modulo-64K counter of WQEs posted to the QP since its creation excluding the newly posted WQEs in this doorbell. Should be zero for the first doorbell on the QP */ + pseudo_bit_t wqe_cnt[0x00008]; /* Number of WQEs posted with this doorbell. Must be grater then zero. */ +/* -------------- */ + pseudo_bit_t nds[0x00006]; /* Next descriptor size (in 16-byte chunks) */ + pseudo_bit_t reserved1[0x00002]; + pseudo_bit_t qpn[0x00018]; /* QP number this doorbell is rung on */ +/* -------------- */ +}; + +/* Send wqe segment data inline */ + +struct hermonprm_wqe_segment_data_inline_st { /* Little Endian */ + pseudo_bit_t byte_count[0x0000a]; /* Not including padding for 16Byte chunks */ + pseudo_bit_t reserved0[0x00015]; + pseudo_bit_t always1[0x00001]; +/* -------------- */ + pseudo_bit_t data[0x00018]; /* Data may be more this segment size - in 16Byte chunks */ + pseudo_bit_t reserved1[0x00008]; +/* -------------- */ + pseudo_bit_t reserved2[0x00040]; +/* -------------- */ +}; + +/* Send wqe segment data ptr */ + +struct hermonprm_wqe_segment_data_ptr_st { /* Little Endian */ + pseudo_bit_t byte_count[0x0001f]; + pseudo_bit_t always0[0x00001]; +/* -------------- */ + pseudo_bit_t l_key[0x00020]; +/* -------------- */ + pseudo_bit_t local_address_h[0x00020]; +/* -------------- */ + pseudo_bit_t local_address_l[0x00020]; +/* -------------- */ +}; + +/* Send wqe segment rd */ + +struct hermonprm_local_invalidate_segment_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00040]; +/* -------------- */ + pseudo_bit_t mem_key[0x00018]; + pseudo_bit_t reserved1[0x00008]; +/* -------------- */ + pseudo_bit_t reserved2[0x000a0]; +/* -------------- */ +}; + +/* Fast_Registration_Segment ####michal - doesn't match PRM (fields were added, see below) new table size in bytes - 0x30 */ + +struct hermonprm_fast_registration_segment_st { /* Little Endian */ + pseudo_bit_t reserved0[0x0001b]; + pseudo_bit_t lr[0x00001]; /* If set - Local Read access will be enabled */ + pseudo_bit_t lw[0x00001]; /* If set - Local Write access will be enabled */ + pseudo_bit_t rr[0x00001]; /* If set - Remote Read access will be enabled */ + pseudo_bit_t rw[0x00001]; /* If set - Remote Write access will be enabled */ + pseudo_bit_t a[0x00001]; /* If set - Remote Atomic access will be enabled */ +/* -------------- */ + pseudo_bit_t pbl_ptr_63_32[0x00020];/* Physical address pointer [63:32] to the physical buffer list ### michal - this field is replaced with mem_key .32 */ +/* -------------- */ + pseudo_bit_t mem_key[0x00020]; /* Memory Key on which the fast registration is executed on. ###michal-this field is replaced with pbl_ptr_63_32 */ +/* -------------- */ + pseudo_bit_t page_size[0x00005]; /* Page size used for the region. Actual size is [4K]*2^Page_size bytes. + page_size should be less than 20. ###michal - field doesn't exsist (see replacement above) */ + pseudo_bit_t reserved1[0x00002]; + pseudo_bit_t zb[0x00001]; /* Zero Based Region ###michal - field doesn't exsist (see replacement above) */ + pseudo_bit_t pbl_ptr_31_8[0x00018]; /* Physical address pointer [31:8] to the physical buffer list ###michal - field doesn't exsist (see replacement above) */ +/* -------------- */ + pseudo_bit_t start_address_h[0x00020];/* Start Address[63:32] - Virtual Address where this region starts */ +/* -------------- */ + pseudo_bit_t start_address_l[0x00020];/* Start Address[31:0] - Virtual Address where this region starts */ +/* -------------- */ + pseudo_bit_t reg_len_h[0x00020]; /* Region Length[63:32] */ +/* -------------- */ + pseudo_bit_t reg_len_l[0x00020]; /* Region Length[31:0] */ +/* -------------- */ +}; + +/* Send wqe segment atomic */ + +struct hermonprm_wqe_segment_atomic_st { /* Little Endian */ + pseudo_bit_t swap_add_h[0x00020]; +/* -------------- */ + pseudo_bit_t swap_add_l[0x00020]; +/* -------------- */ + pseudo_bit_t compare_h[0x00020]; +/* -------------- */ + pseudo_bit_t compare_l[0x00020]; +/* -------------- */ +}; + +/* Send wqe segment remote address */ + +struct hermonprm_wqe_segment_remote_address_st { /* Little Endian */ + pseudo_bit_t remote_virt_addr_h[0x00020]; +/* -------------- */ + pseudo_bit_t remote_virt_addr_l[0x00020]; +/* -------------- */ + pseudo_bit_t rkey[0x00020]; +/* -------------- */ + pseudo_bit_t reserved0[0x00020]; +/* -------------- */ +}; + +/* end wqe segment bind */ + +struct hermonprm_wqe_segment_bind_st { /* Little Endian */ + pseudo_bit_t reserved0[0x0001d]; + pseudo_bit_t rr[0x00001]; /* If set, Remote Read Enable for bound window. */ + pseudo_bit_t rw[0x00001]; /* If set, Remote Write Enable for bound window. + */ + pseudo_bit_t a[0x00001]; /* If set, Atomic Enable for bound window. */ +/* -------------- */ + pseudo_bit_t reserved1[0x0001e]; + pseudo_bit_t zb[0x00001]; /* If set, Window is Zero Based. */ + pseudo_bit_t type[0x00001]; /* Window type. + 0 - Type one window + 1 - Type two window + */ +/* -------------- */ + pseudo_bit_t new_rkey[0x00020]; /* The new RKey of window to bind */ +/* -------------- */ + pseudo_bit_t region_lkey[0x00020]; /* Local key of region, which window will be bound to */ +/* -------------- */ + pseudo_bit_t start_address_h[0x00020]; +/* -------------- */ + pseudo_bit_t start_address_l[0x00020]; +/* -------------- */ + pseudo_bit_t length_h[0x00020]; +/* -------------- */ + pseudo_bit_t length_l[0x00020]; +/* -------------- */ +}; + +/* Send wqe segment ud */ + +struct hermonprm_wqe_segment_ud_st { /* Little Endian */ + struct hermonprm_ud_address_vector_st ud_address_vector;/* UD Address Vector */ +/* -------------- */ + pseudo_bit_t destination_qp[0x00018]; + pseudo_bit_t reserved0[0x00008]; +/* -------------- */ + pseudo_bit_t q_key[0x00020]; +/* -------------- */ + pseudo_bit_t reserved1[0x00040]; +/* -------------- */ +}; + +/* Send wqe segment rd */ + +struct hermonprm_wqe_segment_rd_st { /* Little Endian */ + pseudo_bit_t destination_qp[0x00018]; + pseudo_bit_t reserved0[0x00008]; +/* -------------- */ + pseudo_bit_t q_key[0x00020]; +/* -------------- */ + pseudo_bit_t reserved1[0x00040]; +/* -------------- */ +}; + +/* Send wqe segment ctrl */ + +struct hermonprm_wqe_segment_ctrl_send_st { /* Little Endian */ + pseudo_bit_t opcode[0x00005]; + pseudo_bit_t reserved0[0x0001a]; + pseudo_bit_t owner[0x00001]; +/* -------------- */ + pseudo_bit_t ds[0x00006]; /* descriptor (wqe) size in 16bytes chunk */ + pseudo_bit_t f[0x00001]; /* fence */ + pseudo_bit_t reserved1[0x00019]; +/* -------------- */ + pseudo_bit_t fl[0x00001]; /* Force LoopBack */ + pseudo_bit_t s[0x00001]; /* Remote Solicited Event */ + pseudo_bit_t c[0x00002]; /* completion required: 0b00 - no 0b11 - yes */ + pseudo_bit_t ip[0x00001]; /* When set, InfiniHost III Ex will calculate the IP checksum of the IP header that is present immediately after the IPoverIB encapsulation header. In the case of multiple headers (encapsulation), InfiniHost III Ex will calculate the checksum only for the first IP header following the IPoverIB encapsulation header. Not Valid for IPv6 packets */ + pseudo_bit_t tcp_udp[0x00001]; /* When set, InfiniHost III Ex will calculate the TCP/UDP checksum of the packet that is present immediately after the IP header. In the case of multiple headers (encapsulation), InfiniHost III Ex will calculate the checksum only for the first TCP header following the IP header. This bit may be set only if the entire TCP/UDP segment is present in one IB packet */ + pseudo_bit_t reserved2[0x00001]; + pseudo_bit_t so[0x00001]; /* Strong Ordering - when set, the WQE will be executed only after all previous WQEs have been executed. Can be set for RC WQEs only. This bit must be set in type two BIND, Fast Registration and Local invalidate operations. */ + pseudo_bit_t src_remote_buf[0x00018]; +/* -------------- */ + pseudo_bit_t immediate[0x00020]; /* If the OpCode encodes an operation with Immediate (RDMA-write/SEND), This field will hold the Immediate data to be sent. If the OpCode encodes send and invalidate operations, this field holds the Invalidation key to be inserted into the packet; otherwise, this field is reserved. */ +/* -------------- */ +}; + +/* Address Path # ###michal - match to PRM */ + +struct hermonprm_address_path_st { /* Little Endian */ + pseudo_bit_t pkey_index[0x00007]; /* PKey table index */ + pseudo_bit_t reserved0[0x00016]; + pseudo_bit_t sv[0x00001]; /* Service VLAN on QP */ + pseudo_bit_t cv[0x00001]; /* Customer VLAN in QP */ + pseudo_bit_t fl[0x00001]; /* Force LoopBack */ +/* -------------- */ + pseudo_bit_t rlid[0x00010]; /* Remote (Destination) LID */ + pseudo_bit_t my_lid_smac_idx[0x00007];/* Source LID - the lower 7 bits (upper bits are taken from PortInfo) */ + pseudo_bit_t grh_ip[0x00001]; /* Global address enable - if set, GRH will be formed for packet header */ + pseudo_bit_t reserved1[0x00008]; +/* -------------- */ + pseudo_bit_t hop_limit[0x00008]; /* IPv6 hop limit */ + pseudo_bit_t max_stat_rate[0x00004];/* Maximum static rate control. + 0 - 100% injection rate + 1 - 25% injection rate + 2 - 12.5% injection rate + 3 - 50% injection rate + 7: 2.5 Gb/s. + 8: 10 Gb/s. + 9: 30 Gb/s. + 10: 5 Gb/s. + 11: 20 Gb/s. + 12: 40 Gb/s. + 13: 60 Gb/s. + 14: 80 Gb/s. + 15: 120 Gb/s. */ + pseudo_bit_t reserved2[0x00004]; + pseudo_bit_t mgid_index[0x00007]; /* Index to port GID table */ + pseudo_bit_t reserved3[0x00004]; + pseudo_bit_t ack_timeout[0x00005]; /* Local ACK timeout - Transport timer for activation of retransmission mechanism. Refer to IB spec Vol1 9.7.6.1.3 for further details. + The transport timer is set to 4.096us*2^ack_timeout, if ack_timeout is 0 then transport timer is disabled. */ +/* -------------- */ + pseudo_bit_t flow_label[0x00014]; /* IPv6 flow label */ + pseudo_bit_t tclass[0x00008]; /* IPv6 TClass */ + pseudo_bit_t reserved4[0x00004]; +/* -------------- */ + pseudo_bit_t rgid_127_96[0x00020]; /* Remote GID[127:96] */ +/* -------------- */ + pseudo_bit_t rgid_95_64[0x00020]; /* Remote GID[95:64] */ +/* -------------- */ + pseudo_bit_t rgid_63_32[0x00020]; /* Remote GID[63:32] */ +/* -------------- */ + pseudo_bit_t rgid_31_0[0x00020]; /* Remote GID[31:0] */ +/* -------------- */ + pseudo_bit_t reserved5[0x00008]; + pseudo_bit_t sp[0x00001]; /* if set, spoofing protection is enforced on this QP and Ethertype headers are restricted */ + pseudo_bit_t reserved6[0x00002]; + pseudo_bit_t fvl[0x00001]; /* force VLAN */ + pseudo_bit_t fsip[0x00001]; /* force source IP */ + pseudo_bit_t fsm[0x00001]; /* force source MAC */ + pseudo_bit_t reserved7[0x0000a]; + pseudo_bit_t sched_queue[0x00008]; +/* -------------- */ + pseudo_bit_t dmac_47_32[0x00010]; + pseudo_bit_t vlan_index[0x00007]; + pseudo_bit_t reserved8[0x00001]; + pseudo_bit_t counter_index[0x00008];/* Index to a table of counters that counts egress packets and bytes, 0xFF not valid */ +/* -------------- */ + pseudo_bit_t dmac_31_0[0x00020]; +/* -------------- */ +}; + +/* HCA Command Register (HCR) #### michal - match PRM */ + +struct hermonprm_hca_command_register_st { /* Little Endian */ + pseudo_bit_t in_param_h[0x00020]; /* Input Parameter: parameter[63:32] or pointer[63:32] to input mailbox (see command description) */ +/* -------------- */ + pseudo_bit_t in_param_l[0x00020]; /* Input Parameter: parameter[31:0] or pointer[31:0] to input mailbox (see command description) */ +/* -------------- */ + pseudo_bit_t input_modifier[0x00020];/* Input Parameter Modifier */ +/* -------------- */ + pseudo_bit_t out_param_h[0x00020]; /* Output Parameter: parameter[63:32] or pointer[63:32] to output mailbox (see command description) */ +/* -------------- */ + pseudo_bit_t out_param_l[0x00020]; /* Output Parameter: parameter[31:0] or pointer[31:0] to output mailbox (see command description) */ +/* -------------- */ + pseudo_bit_t reserved0[0x00010]; + pseudo_bit_t token[0x00010]; /* Software assigned token to the command, to uniquely identify it. The token is returned to the software in the EQE reported. */ +/* -------------- */ + pseudo_bit_t opcode[0x0000c]; /* Command opcode */ + pseudo_bit_t opcode_modifier[0x00004];/* Opcode Modifier, see specific description for each command. */ + pseudo_bit_t reserved1[0x00005]; + pseudo_bit_t t[0x00001]; /* Toggle */ + pseudo_bit_t e[0x00001]; /* Event Request + 0 - Don't report event (software will poll the GO bit) + 1 - Report event to EQ when the command completes */ + pseudo_bit_t go[0x00001]; /* Go (0=Software ownership for the HCR, 1=Hardware ownership for the HCR) + Software can write to the HCR only if Go bit is cleared. + Software must set the Go bit to trigger the HW to execute the command. Software must not write to this register value other than 1 for the Go bit. */ + pseudo_bit_t status[0x00008]; /* Command execution status report. Valid only if command interface in under SW ownership (Go bit is cleared) + 0 - command completed without error. If different than zero, command execution completed with error. Syndrom encoding is depended on command executed and is defined for each command */ +/* -------------- */ +}; + +/* CQ Doorbell */ + +struct hermonprm_cq_cmd_doorbell_st { /* Little Endian */ + pseudo_bit_t cqn[0x00018]; /* CQ number accessed */ + pseudo_bit_t cmd[0x00003]; /* Command to be executed on CQ + 0x0 - Reserved + 0x1 - Request notification for next Solicited completion event. CQ_param specifies the current CQ Consumer Counter. + 0x2 - Request notification for next Solicited or Unsolicited completion event. CQ_param specifies the current CQ Consumer Counter. + 0x3 - Request notification for multiple completions (Arm-N). CQ_param specifies the value of the CQ Counter that when reached by HW (i.e. HW generates a CQE into this Counter) Event will be generated + Other - Reserved */ + pseudo_bit_t reserved0[0x00001]; + pseudo_bit_t cmd_sn[0x00002]; /* Command Sequence Number - This field should be incremented upon receiving completion notification of the respective CQ. + This transition is done by ringing Request notification for next Solicited, Request notification for next Solicited or Unsolicited + completion or Request notification for multiple completions doorbells after receiving completion notification. + This field is initialized to Zero */ + pseudo_bit_t reserved1[0x00002]; +/* -------------- */ + pseudo_bit_t cq_param[0x00020]; /* parameter to be used by CQ command */ +/* -------------- */ +}; + +/* RD-send doorbell */ + +struct hermonprm_rd_send_doorbell_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00008]; + pseudo_bit_t een[0x00018]; /* End-to-end context number (reliable datagram) + Must be zero for Nop and Bind operations */ +/* -------------- */ + pseudo_bit_t reserved1[0x00008]; + pseudo_bit_t qpn[0x00018]; /* QP number this doorbell is rung on */ +/* -------------- */ + struct hermonprm_send_doorbell_st send_doorbell;/* Send Parameters */ +/* -------------- */ +}; + +/* Multicast Group Member QP #### michal - match PRM */ + +struct hermonprm_mgmqp_st { /* Little Endian */ + pseudo_bit_t qpn_i[0x00018]; /* QPN_i: QP number which is a member in this multicast group. Valid only if Qi bit is set. Length of the QPN_i list is set in INIT_HCA */ + pseudo_bit_t reserved0[0x00006]; + pseudo_bit_t blck_lb[0x00001]; /* Block self-loopback messages arriving to this qp */ + pseudo_bit_t qi[0x00001]; /* Qi: QPN_i is valid */ +/* -------------- */ +}; + +/* vsd */ + +struct hermonprm_vsd_st { /* Little Endian */ + pseudo_bit_t vsd_dw0[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw1[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw2[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw3[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw4[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw5[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw6[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw7[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw8[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw9[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw10[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw11[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw12[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw13[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw14[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw15[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw16[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw17[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw18[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw19[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw20[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw21[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw22[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw23[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw24[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw25[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw26[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw27[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw28[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw29[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw30[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw31[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw32[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw33[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw34[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw35[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw36[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw37[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw38[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw39[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw40[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw41[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw42[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw43[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw44[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw45[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw46[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw47[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw48[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw49[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw50[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw51[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw52[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw53[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw54[0x00020]; +/* -------------- */ + pseudo_bit_t vsd_dw55[0x00020]; +/* -------------- */ +}; + +/* UAR Parameters */ + +struct hermonprm_uar_params_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00040]; +/* -------------- */ + pseudo_bit_t uar_page_sz[0x00008]; /* This field defines the size of each UAR page. + Size of UAR Page is 4KB*2^UAR_Page_Size */ + pseudo_bit_t log_max_uars[0x00004]; /* Number of UARs supported is 2^log_max_UARs */ + pseudo_bit_t reserved1[0x00014]; +/* -------------- */ + pseudo_bit_t reserved2[0x000a0]; +/* -------------- */ +}; + +/* Translation and Protection Tables Parameters */ + +struct hermonprm_tptparams_st { /* Little Endian */ + pseudo_bit_t dmpt_base_adr_h[0x00020];/* dMPT - Memory Protection Table base physical address [63:32]. + Entry size is 64 bytes. + Table must be aligned to its size. + Address may be set to 0xFFFFFFFF if address translation and protection is not supported. */ +/* -------------- */ + pseudo_bit_t dmpt_base_adr_l[0x00020];/* dMPT - Memory Protection Table base physical address [31:0]. + Entry size is 64 bytes. + Table must be aligned to its size. + Address may be set to 0xFFFFFFFF if address translation and protection is not supported. */ +/* -------------- */ + pseudo_bit_t log_dmpt_sz[0x00006]; /* Log (base 2) of the number of region/windows entries in the dMPT table. */ + pseudo_bit_t reserved0[0x00002]; + pseudo_bit_t pfto[0x00005]; /* Page Fault RNR Timeout - + The field returned in RNR Naks generated when a page fault is detected. + It has no effect when on-demand-paging is not used. */ + pseudo_bit_t reserved1[0x00013]; +/* -------------- */ + pseudo_bit_t reserved2[0x00020]; +/* -------------- */ + pseudo_bit_t mtt_base_addr_h[0x00020];/* MTT - Memory Translation table base physical address [63:32]. + Table must be aligned to its size. + Address may be set to 0xFFFFFFFF if address translation and protection is not supported. */ +/* -------------- */ + pseudo_bit_t mtt_base_addr_l[0x00020];/* MTT - Memory Translation table base physical address [31:0]. + Table must be aligned to its size. + Address may be set to 0xFFFFFFFF if address translation and protection is not supported. */ +/* -------------- */ + pseudo_bit_t cmpt_base_adr_h[0x00020];/* cMPT - Memory Protection Table base physical address [63:32]. + Entry size is 64 bytes. + Table must be aligned to its size. */ +/* -------------- */ + pseudo_bit_t cmpt_base_adr_l[0x00020];/* cMPT - Memory Protection Table base physical address [31:0]. + Entry size is 64 bytes. + Table must be aligned to its size. */ +/* -------------- */ +}; + +/* Multicast Support Parameters #### michal - match PRM */ + +struct hermonprm_multicastparam_st { /* Little Endian */ + pseudo_bit_t mc_base_addr_h[0x00020];/* Base Address of the Multicast Table [63:32]. + The base address must be aligned to the entry size. + Address may be set to 0xFFFFFFFF if multicast is not supported. */ +/* -------------- */ + pseudo_bit_t mc_base_addr_l[0x00020];/* Base Address of the Multicast Table [31:0]. + The base address must be aligned to the entry size. + Address may be set to 0xFFFFFFFF if multicast is not supported. */ +/* -------------- */ + pseudo_bit_t reserved0[0x00040]; +/* -------------- */ + pseudo_bit_t log_mc_table_entry_sz[0x00005];/* Log2 of the Size of multicast group member (MGM) entry. + Must be greater than 5 (to allow CTRL and GID sections). + That implies the number of QPs per MC table entry. */ + pseudo_bit_t reserved1[0x0000b]; + pseudo_bit_t reserved2[0x00010]; +/* -------------- */ + pseudo_bit_t log_mc_table_hash_sz[0x00005];/* Number of entries in multicast DGID hash table (must be power of 2) + INIT_HCA - the required number of entries + QUERY_HCA - the actual number of entries assigned by firmware (will be less than or equal to the amount required in INIT_HCA) */ + pseudo_bit_t reserved3[0x0001b]; +/* -------------- */ + pseudo_bit_t log_mc_table_sz[0x00005];/* Log2 of the overall number of MC entries in the MCG table (includes both hash and auxiliary tables) */ + pseudo_bit_t reserved4[0x00013]; + pseudo_bit_t mc_hash_fn[0x00003]; /* Multicast hash function + 0 - Default hash function + other - reserved */ + pseudo_bit_t reserved5[0x00005]; +/* -------------- */ + pseudo_bit_t reserved6[0x00020]; +/* -------------- */ +}; + +/* QPC/EEC/CQC/EQC/RDB Parameters #### michal - doesn't match PRM (field name are differs. see below) */ + +struct hermonprm_qpcbaseaddr_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00080]; +/* -------------- */ + pseudo_bit_t qpc_base_addr_h[0x00020];/* QPC Base Address [63:32] + Table must be aligned on its size */ +/* -------------- */ + pseudo_bit_t log_num_of_qp[0x00005];/* Log base 2 of number of supported QPs */ + pseudo_bit_t qpc_base_addr_l[0x0001b];/* QPC Base Address [31:7] + Table must be aligned on its size */ +/* -------------- */ + pseudo_bit_t reserved1[0x00040]; +/* -------------- */ + pseudo_bit_t reserved2[0x00040]; +/* -------------- */ + pseudo_bit_t srqc_base_addr_h[0x00020];/* SRQ Context Base Address [63:32] + Table must be aligned on its size + Address may be set to 0xFFFFFFFF if SRQ is not supported. */ +/* -------------- */ + pseudo_bit_t log_num_of_srq[0x00005];/* Log base 2 of number of supported SRQs. */ + pseudo_bit_t srqc_base_addr_l[0x0001b];/* SRQ Context Base Address [31:5] + Table must be aligned on its size + Address may be set to 0xFFFFFFFF if SRQ is not supported. */ +/* -------------- */ + pseudo_bit_t cqc_base_addr_h[0x00020];/* CQC Base Address [63:32] + Table must be aligned on its size */ +/* -------------- */ + pseudo_bit_t log_num_of_cq[0x00005];/* Log base 2 of number of supported CQs. */ + pseudo_bit_t cqc_base_addr_l[0x0001b];/* CQC Base Address [31:6] + Table must be aligned on its size */ +/* -------------- */ + pseudo_bit_t reserved3[0x00040]; +/* -------------- */ + pseudo_bit_t altc_base_addr_h[0x00020];/* AltC Base Address (altc_base_addr_h) [63:32] + Table has same number of entries as QPC table. + Table must be aligned to entry size. */ +/* -------------- */ + pseudo_bit_t altc_base_addr_l[0x00020];/* AltC Base Address (altc_base_addr_l) [31:0] + Table has same number of entries as QPC table. + Table must be aligned to entry size. */ +/* -------------- */ + pseudo_bit_t reserved4[0x00040]; +/* -------------- */ + pseudo_bit_t auxc_base_addr_h[0x00020]; +/* -------------- */ + pseudo_bit_t auxc_base_addr_l[0x00020]; +/* -------------- */ + pseudo_bit_t reserved5[0x00040]; +/* -------------- */ + pseudo_bit_t eqc_base_addr_h[0x00020];/* EQC Base Address [63:32] + Address may be set to 0xFFFFFFFF if EQs are not supported. + Table must be aligned to entry size. */ +/* -------------- */ + pseudo_bit_t log_num_of_eq[0x00005];/* Log base 2 of number of supported EQs. + Must be 6 or less in InfiniHost-III-EX. */ + pseudo_bit_t eqc_base_addr_l[0x0001b];/* EQC Base Address [31:6] + Address may be set to 0xFFFFFFFF if EQs are not supported. + Table must be aligned to entry size. */ +/* -------------- */ + pseudo_bit_t reserved6[0x00040]; +/* -------------- */ + pseudo_bit_t rdmardc_base_addr_h[0x00020];/* rdmardc_base_addr_h: Base address of table that holds remote read and remote atomic requests [63:32]. */ +/* -------------- */ + pseudo_bit_t log_num_rd[0x00003]; /* Log (base 2) of the maximum number of RdmaRdC entries per QP. This denotes the maximum number of outstanding reads/atomics as a responder. */ + pseudo_bit_t reserved7[0x00002]; + pseudo_bit_t rdmardc_base_addr_l[0x0001b];/* rdmardc_base_addr_l: Base address of table that holds remote read and remote atomic requests [31:0]. + Table must be aligned to RDB entry size (32 bytes). */ +/* -------------- */ + pseudo_bit_t reserved8[0x00040]; +/* -------------- */ +}; + +/* Header_Log_Register */ + +struct hermonprm_header_log_register_st { /* Little Endian */ + pseudo_bit_t place_holder[0x00020]; +/* -------------- */ + pseudo_bit_t reserved0[0x00060]; +/* -------------- */ +}; + +/* Performance Monitors */ + +struct hermonprm_performance_monitors_st { /* Little Endian */ + pseudo_bit_t e0[0x00001]; /* Enables counting of respective performance counter */ + pseudo_bit_t e1[0x00001]; /* Enables counting of respective performance counter */ + pseudo_bit_t e2[0x00001]; /* Enables counting of respective performance counter */ + pseudo_bit_t reserved0[0x00001]; + pseudo_bit_t r0[0x00001]; /* If written to as '1 - resets respective performance counter, if written to az '0 - no change to matter */ + pseudo_bit_t r1[0x00001]; /* If written to as '1 - resets respective performance counter, if written to az '0 - no change to matter */ + pseudo_bit_t r2[0x00001]; /* If written to as '1 - resets respective performance counter, if written to az '0 - no change to matter */ + pseudo_bit_t reserved1[0x00001]; + pseudo_bit_t i0[0x00001]; /* Interrupt enable on respective counter overflow. '1 - interrupt enabled, '0 - interrupt disabled. */ + pseudo_bit_t i1[0x00001]; /* Interrupt enable on respective counter overflow. '1 - interrupt enabled, '0 - interrupt disabled. */ + pseudo_bit_t i2[0x00001]; /* Interrupt enable on respective counter overflow. '1 - interrupt enabled, '0 - interrupt disabled. */ + pseudo_bit_t reserved2[0x00001]; + pseudo_bit_t f0[0x00001]; /* Overflow flag. If set, overflow occurred on respective counter. Cleared if written to as '1 */ + pseudo_bit_t f1[0x00001]; /* Overflow flag. If set, overflow occurred on respective counter. Cleared if written to as '1 */ + pseudo_bit_t f2[0x00001]; /* Overflow flag. If set, overflow occurred on respective counter. Cleared if written to as '1 */ + pseudo_bit_t reserved3[0x00001]; + pseudo_bit_t ev_cnt1[0x00005]; /* Specifies event to be counted by Event_counter1 See XXX for events' definition. */ + pseudo_bit_t reserved4[0x00003]; + pseudo_bit_t ev_cnt2[0x00005]; /* Specifies event to be counted by Event_counter2 See XXX for events' definition. */ + pseudo_bit_t reserved5[0x00003]; +/* -------------- */ + pseudo_bit_t clock_counter[0x00020]; +/* -------------- */ + pseudo_bit_t event_counter1[0x00020]; +/* -------------- */ + pseudo_bit_t event_counter2[0x00020];/* Read/write event counter, counting events specified by EvCntl and EvCnt2 fields repsectively. When the event counter reaches is maximum value of 0xFFFFFF, the next event will cause it to roll over to zero, set F1 or F2 bit respectively and generate interrupt by I1 I2 bit respectively. */ +/* -------------- */ +}; + +/* MLX WQE segment format */ + +struct hermonprm_wqe_segment_ctrl_mlx_st { /* Little Endian */ + pseudo_bit_t opcode[0x00005]; /* must be 0xA = SEND */ + pseudo_bit_t reserved0[0x0001a]; + pseudo_bit_t owner[0x00001]; +/* -------------- */ + pseudo_bit_t ds[0x00006]; /* Descriptor Size */ + pseudo_bit_t reserved1[0x0001a]; +/* -------------- */ + pseudo_bit_t fl[0x00001]; /* Force LoopBack */ + pseudo_bit_t reserved2[0x00001]; + pseudo_bit_t c[0x00002]; /* Create CQE (for "requested signalling" QP) */ + pseudo_bit_t icrc[0x00001]; /* last dword of the packet: 0 - Calculate ICRC and put it instead of last dword. 1 - Leave last dword as is. */ + pseudo_bit_t reserved3[0x00003]; + pseudo_bit_t sl[0x00004]; + pseudo_bit_t max_statrate[0x00004]; + pseudo_bit_t slr[0x00001]; /* 0= take slid from port. 1= take slid from given headers */ + pseudo_bit_t v15[0x00001]; /* Send packet over VL15 */ + pseudo_bit_t reserved4[0x0000e]; +/* -------------- */ + pseudo_bit_t reserved5[0x00010]; + pseudo_bit_t rlid[0x00010]; /* Destination LID (must match given headers) */ +/* -------------- */ +}; + +/* Send WQE segment format */ + +struct hermonprm_send_wqe_segment_st { /* Little Endian */ + struct hermonprm_wqe_segment_ctrl_send_st wqe_segment_ctrl_send;/* Send wqe segment ctrl */ +/* -------------- */ + struct hermonprm_wqe_segment_rd_st wqe_segment_rd;/* Send wqe segment rd */ +/* -------------- */ + struct hermonprm_wqe_segment_ud_st wqe_segment_ud;/* Send wqe segment ud */ +/* -------------- */ + struct hermonprm_wqe_segment_bind_st wqe_segment_bind;/* Send wqe segment bind */ +/* -------------- */ + pseudo_bit_t reserved0[0x00180]; +/* -------------- */ + struct hermonprm_wqe_segment_remote_address_st wqe_segment_remote_address;/* Send wqe segment remote address */ +/* -------------- */ + struct hermonprm_wqe_segment_atomic_st wqe_segment_atomic;/* Send wqe segment atomic */ +/* -------------- */ + struct hermonprm_fast_registration_segment_st fast_registration_segment;/* Fast Registration Segment */ +/* -------------- */ + struct hermonprm_local_invalidate_segment_st local_invalidate_segment;/* local invalidate segment */ +/* -------------- */ + struct hermonprm_wqe_segment_data_ptr_st wqe_segment_data_ptr;/* Send wqe segment data ptr */ +/* -------------- */ + struct hermonprm_wqe_segment_data_inline_st wqe_segment_data_inline;/* Send wqe segment data inline */ +/* -------------- */ + pseudo_bit_t reserved1[0x00200]; +/* -------------- */ +}; + +/* QP and EE Context Entry */ + +struct hermonprm_queue_pair_ee_context_entry_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00008]; + pseudo_bit_t reserved1[0x00001]; + pseudo_bit_t reserved2[0x00002]; + pseudo_bit_t pm_state[0x00002]; /* Path migration state (Migrated, Armed or Rearm) + 11-Migrated + 00-Armed + 01-Rearm + 10-Reserved + Should be set to 11 for UD QPs and for QPs which do not support APM */ + pseudo_bit_t reserved3[0x00003]; + pseudo_bit_t st[0x00004]; /* Transport Service Type: RC: 0, UC: 1, RD: 2, UD: 3, FCMND:4, FEXCH:5, SRC:6, MLX 7, Raw Eth 11 */ + pseudo_bit_t reserved4[0x00008]; + pseudo_bit_t state[0x00004]; /* QP/EE state: + 0 - RST + 1 - INIT + 2 - RTR + 3 - RTS + 4 - SQEr + 5 - SQD (Send Queue Drained) + 6 - ERR + 7 - Send Queue Draining + 8 - Reserved + 9 - Suspended + A- F - Reserved + (Valid for QUERY_QPEE and ERR2RST_QPEE commands only) */ +/* -------------- */ + pseudo_bit_t pd[0x00018]; + pseudo_bit_t reserved5[0x00008]; +/* -------------- */ + pseudo_bit_t reserved6[0x00004]; + pseudo_bit_t rlky[0x00001]; /* When set this QP can use the Reserved L_Key */ + pseudo_bit_t reserved7[0x00003]; + pseudo_bit_t log_sq_stride[0x00003];/* Stride on the send queue. WQ entry is 16*(2^log_SQ_stride) bytes. + Stride must be equal or bigger then 64 bytes (minimum log_RQ_stride value allowed is 2). */ + pseudo_bit_t log_sq_size[0x00004]; /* Log2 of the Number of WQEs in the Send Queue. */ + pseudo_bit_t reserved8[0x00001]; + pseudo_bit_t log_rq_stride[0x00003];/* Stride on the receive queue. WQ entry is 16*(2^log_RQ_stride) bytes. + Stride must be equal or bigger then 64 bytes (minimum log_RQ_stride value allowed is 2). */ + pseudo_bit_t log_rq_size[0x00004]; /* Log2 of the Number of WQEs in the Receive Queue. */ + pseudo_bit_t reserved9[0x00001]; + pseudo_bit_t msg_max[0x00005]; /* Max message size allowed on the QP. Maximum message size is 2^msg_Max. + Must be equal to MTU for UD and MLX QPs. */ + pseudo_bit_t mtu[0x00003]; /* MTU of the QP (Must be the same for both paths: primary and alternative): + 0x1 - 256 bytes + 0x2 - 512 + 0x3 - 1024 + 0x4 - 2048 + other - reserved + + Should be configured to 0x4 for UD and MLX QPs. */ +/* -------------- */ + pseudo_bit_t usr_page[0x00018]; /* UAR number to ring doorbells for this QP (aliased to doorbell and Blue Flame pages) */ + pseudo_bit_t reserved10[0x00008]; +/* -------------- */ + pseudo_bit_t local_qpn_een[0x00018];/* Local QP/EE number Lower bits determine position of this record in QPC table, and - thus - constrained + This field is valid for QUERY and ERR2RST commands only. */ + pseudo_bit_t reserved11[0x00008]; +/* -------------- */ + pseudo_bit_t remote_qpn_een[0x00018];/* Remote QP/EE number */ + pseudo_bit_t reserved12[0x00008]; +/* -------------- */ + struct hermonprm_address_path_st primary_address_path;/* Primary address path for the QP/EE */ +/* -------------- */ + struct hermonprm_address_path_st alternative_address_path;/* Alternate address path for the QP/EE */ +/* -------------- */ + pseudo_bit_t reserved13[0x00003]; + pseudo_bit_t reserved14[0x00001]; + pseudo_bit_t reserved15[0x00001]; + pseudo_bit_t cur_retry_cnt[0x00003];/* Current transport retry counter (QUERY_QPEE only). + The current transport retry counter can vary from retry_count down to 1, where 1 means that the last retry attempt is currently executing. */ + pseudo_bit_t cur_rnr_retry[0x00003];/* Current RNR retry counter (QUERY_QPEE only). + The current RNR retry counter can vary from rnr_retry to 1, where 1 means that the last retry attempt is currently executing. */ + pseudo_bit_t fre[0x00001]; /* Fast Registration Work Request Enabled. (Reserved for EE) */ + pseudo_bit_t reserved16[0x00001]; + pseudo_bit_t rnr_retry[0x00003]; + pseudo_bit_t retry_count[0x00003]; /* Transport timeout Retry count */ + pseudo_bit_t reserved17[0x00002]; + pseudo_bit_t sra_max[0x00003]; /* Maximum number of outstanding RDMA-read/Atomic operations allowed in the send queue. Maximum number is 2^SRA_Max. Must be zero in EE context. */ + pseudo_bit_t reserved18[0x00004]; + pseudo_bit_t ack_req_freq[0x00004]; /* ACK required frequency. ACK required bit will be set in every 2^AckReqFreq packets at least. Not valid for RD QP. */ +/* -------------- */ + pseudo_bit_t reserved19[0x00020]; +/* -------------- */ + pseudo_bit_t next_send_psn[0x00018];/* Next PSN to be sent */ + pseudo_bit_t reserved20[0x00008]; +/* -------------- */ + pseudo_bit_t cqn_snd[0x00018]; /* CQ number completions from the send queue to be reported to. Not valid (reserved) in EE context. */ + pseudo_bit_t reserved21[0x00008]; +/* -------------- */ + pseudo_bit_t reserved22[0x00040]; +/* -------------- */ + pseudo_bit_t last_acked_psn[0x00018];/* The last acknowledged PSN for the requester (QUERY_QPEE only) */ + pseudo_bit_t reserved23[0x00008]; +/* -------------- */ + pseudo_bit_t ssn[0x00018]; /* Requester Send Sequence Number (QUERY_QPEE only) */ + pseudo_bit_t reserved24[0x00008]; +/* -------------- */ + pseudo_bit_t reserved25[0x00004]; + pseudo_bit_t ric[0x00001]; /* Invalid Credits. + 1 - place "Invalid Credits" to ACKs sent from this queue. + 0 - ACKs report the actual number of end to end credits on the connection. + Not valid (reserved) in EE context. + Must be set to 1 on QPs which are attached to SRQ. */ + pseudo_bit_t reserved26[0x00001]; + pseudo_bit_t page_offset[0x00006]; /* start address of wqes in first page (11:6), bits [5:0] reserved */ + pseudo_bit_t reserved27[0x00001]; + pseudo_bit_t rae[0x00001]; /* If set - Atomic operations enabled. on receive queue. Not valid (reserved) in EE context. */ + pseudo_bit_t rwe[0x00001]; /* If set - RDMA - write enabled on receive queue. Not valid (reserved) in EE context. */ + pseudo_bit_t rre[0x00001]; /* If set - RDMA - read enabled on receive queue. Not valid (reserved) in EE context. */ + pseudo_bit_t reserved28[0x00005]; + pseudo_bit_t rra_max[0x00003]; /* Maximum number of outstanding RDMA-read/Atomic operations allowed on receive queue is 2^RRA_Max. + Must be 0 for EE context. */ + pseudo_bit_t reserved29[0x00008]; +/* -------------- */ + pseudo_bit_t next_rcv_psn[0x00018]; /* Next (expected) PSN on receive */ + pseudo_bit_t min_rnr_nak[0x00005]; /* Minimum RNR NAK timer value (TTTTT field encoding according to the IB spec Vol1 9.7.5.2.8). + Not valid (reserved) in EE context. */ + pseudo_bit_t reserved30[0x00003]; +/* -------------- */ + pseudo_bit_t srcd[0x00010]; /* Scalable Reliable Connection Domain. Valid for SRC transport service */ + pseudo_bit_t reserved31[0x00010]; +/* -------------- */ + pseudo_bit_t cqn_rcv[0x00018]; /* CQ number completions from receive queue to be reported to. Not valid (reserved) in EE context. */ + pseudo_bit_t reserved32[0x00008]; +/* -------------- */ + pseudo_bit_t db_record_addr_h[0x00020];/* QP DB Record physical address */ +/* -------------- */ + pseudo_bit_t reserved33[0x00002]; + pseudo_bit_t db_record_addr_l[0x0001e];/* QP DB Record physical address */ +/* -------------- */ + pseudo_bit_t q_key[0x00020]; /* Q_Key to be validated against received datagrams. + On send datagrams, if Q_Key[31] specified in the WQE is set, then this Q_Key will be transmitted in the outgoing message. + Not valid (reserved) in EE context. */ +/* -------------- */ + pseudo_bit_t srqn[0x00018]; /* SRQN - Shared Receive Queue Number - specifies the SRQ number from which the QP dequeues receive descriptors. + SRQN is valid only if SRQ bit is set. Not valid (reserved) in EE context. */ + pseudo_bit_t srq[0x00001]; /* SRQ - Shared Receive Queue. If this bit is set, then the QP is associated with a SRQ. Not valid (reserved) in EE context. */ + pseudo_bit_t reserved34[0x00007]; +/* -------------- */ + pseudo_bit_t rmsn[0x00018]; /* Responder current message sequence number (QUERY_QPEE only) */ + pseudo_bit_t reserved35[0x00008]; +/* -------------- */ + pseudo_bit_t sq_wqe_counter[0x00010];/* A 16bits counter that is incremented for each WQE posted to the SQ. + Must be 0x0 in SQ initialization. + (QUERY_QPEE only). */ + pseudo_bit_t rq_wqe_counter[0x00010];/* A 16bits counter that is incremented for each WQE posted to the RQ. + Must be 0x0 in RQ initialization. + (QUERY_QPEE only). */ +/* -------------- */ + pseudo_bit_t reserved36[0x00040]; +/* -------------- */ + pseudo_bit_t rmc_parent_qpn[0x00018];/* reliable multicast parent queue number */ + pseudo_bit_t hs[0x00001]; /* Header Separation. If set, the byte count of the first scatter entry will be ignored. The buffer specified by the first scatter entry will contain packet headers (up to TCP). CQE will report number of bytes scattered to the first scatter entry. Intended for use on IPoverIB on UD QP or Raw Ethernet QP. */ + pseudo_bit_t is[0x00001]; /* when set - inline scatter is enabled for this RQ */ + pseudo_bit_t reserved37[0x00001]; + pseudo_bit_t rme[0x00002]; /* Reliable Multicast + 00 - disabled + 01 - parent QP (requester) + 10 - child QP (requester) + 11 - responder QP + Note that Reliable Multicast is a preliminary definition which can be subject to change. */ + pseudo_bit_t reserved38[0x00002]; + pseudo_bit_t mkey_rmp[0x00001]; /* If set, MKey used to access TPT for incoming RDMA-write request is calculated by adding MKey from the packet to base_MKey field in the QPC. Can be set only for QPs that are not target for RDMA-read request. */ +/* -------------- */ + pseudo_bit_t base_mkey[0x00018]; /* Base Mkey bits [31:8]. Lower 8 bits must be zero. */ + pseudo_bit_t num_rmc_peers[0x00008];/* Number of remote peers in Reliable Multicast group */ +/* -------------- */ + pseudo_bit_t mtt_base_addr_h[0x00008];/* MTT Base Address [39:32] in ICM relative to INIT_HCA.mtt_base_addr */ + pseudo_bit_t reserved39[0x00010]; + pseudo_bit_t log2_page_size[0x00006];/* Log (base 2) of MTT page size in units of 4KByte */ + pseudo_bit_t reserved40[0x00002]; +/* -------------- */ + pseudo_bit_t reserved41[0x00003]; + pseudo_bit_t mtt_base_addr_l[0x0001d];/* MTT Base Address [31:3] in ICM relative to INIT_HCA.mtt_base_addr */ +/* -------------- */ + pseudo_bit_t vft_lan[0x0000c]; + pseudo_bit_t vft_prio[0x00003]; /* The Priority filed in the VFT header for FCP */ + pseudo_bit_t reserved42[0x00001]; + pseudo_bit_t cs_ctl[0x00009]; /* The Priority filed in the VFT header for FCP */ + pseudo_bit_t reserved43[0x00006]; + pseudo_bit_t ve[0x00001]; /* Should we add/check the VFT header */ +/* -------------- */ + pseudo_bit_t exch_base[0x00010]; /* For init QP only - The base exchanges */ + pseudo_bit_t reserved44[0x00008]; + pseudo_bit_t exch_size[0x00004]; /* For CMMD QP only - The size (from base) exchanges is 2exchanges_size */ + pseudo_bit_t reserved45[0x00003]; + pseudo_bit_t fc[0x00001]; /* When set it mean that this QP is used for FIBRE CHANNEL. */ +/* -------------- */ + pseudo_bit_t remote_id[0x00018]; /* Peer NX port ID */ + pseudo_bit_t reserved46[0x00008]; +/* -------------- */ + pseudo_bit_t fcp_mtu[0x0000a]; /* In 4*Bytes units. The MTU Size */ + pseudo_bit_t reserved47[0x00006]; + pseudo_bit_t my_id_indx[0x00008]; /* Index to My NX port ID table */ + pseudo_bit_t vft_hop_count[0x00008];/* HopCnt value for the VFT header */ +/* -------------- */ + pseudo_bit_t reserved48[0x000c0]; +/* -------------- */ +}; + +/* */ + +struct hermonprm_mcg_qp_dw_st { /* Little Endian */ + pseudo_bit_t qpn[0x00018]; + pseudo_bit_t reserved0[0x00006]; + pseudo_bit_t blck_lb[0x00001]; + pseudo_bit_t reserved1[0x00001]; +/* -------------- */ +}; + +/* Clear Interrupt [63:0] #### michal - match to PRM */ + +struct hermonprm_clr_int_st { /* Little Endian */ + pseudo_bit_t clr_int_h[0x00020]; /* Clear Interrupt [63:32] + Write transactions to this register will clear (de-assert) the virtual interrupt output pins of InfiniHost-III-EX. The value to be written in this register is obtained by executing QUERY_ADAPTER command on command interface after system boot. + This register is write-only. Reading from this register will cause undefined result + */ +/* -------------- */ + pseudo_bit_t clr_int_l[0x00020]; /* Clear Interrupt [31:0] + Write transactions to this register will clear (de-assert) the virtual interrupt output pins of InfiniHost-III-EX. The value to be written in this register is obtained by executing QUERY_ADAPTER command on command interface after system boot. + This register is write-only. Reading from this register will cause undefined result */ +/* -------------- */ +}; + +/* EQ Set CI DBs Table */ + +struct hermonprm_eq_set_ci_table_st { /* Little Endian */ + pseudo_bit_t eq0_set_ci[0x00020]; /* EQ0_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved0[0x00020]; +/* -------------- */ + pseudo_bit_t eq1_set_ci[0x00020]; /* EQ1_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved1[0x00020]; +/* -------------- */ + pseudo_bit_t eq2_set_ci[0x00020]; /* EQ2_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved2[0x00020]; +/* -------------- */ + pseudo_bit_t eq3_set_ci[0x00020]; /* EQ3_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved3[0x00020]; +/* -------------- */ + pseudo_bit_t eq4_set_ci[0x00020]; /* EQ4_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved4[0x00020]; +/* -------------- */ + pseudo_bit_t eq5_set_ci[0x00020]; /* EQ5_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved5[0x00020]; +/* -------------- */ + pseudo_bit_t eq6_set_ci[0x00020]; /* EQ6_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved6[0x00020]; +/* -------------- */ + pseudo_bit_t eq7_set_ci[0x00020]; /* EQ7_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved7[0x00020]; +/* -------------- */ + pseudo_bit_t eq8_set_ci[0x00020]; /* EQ8_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved8[0x00020]; +/* -------------- */ + pseudo_bit_t eq9_set_ci[0x00020]; /* EQ9_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved9[0x00020]; +/* -------------- */ + pseudo_bit_t eq10_set_ci[0x00020]; /* EQ10_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved10[0x00020]; +/* -------------- */ + pseudo_bit_t eq11_set_ci[0x00020]; /* EQ11_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved11[0x00020]; +/* -------------- */ + pseudo_bit_t eq12_set_ci[0x00020]; /* EQ12_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved12[0x00020]; +/* -------------- */ + pseudo_bit_t eq13_set_ci[0x00020]; /* EQ13_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved13[0x00020]; +/* -------------- */ + pseudo_bit_t eq14_set_ci[0x00020]; /* EQ14_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved14[0x00020]; +/* -------------- */ + pseudo_bit_t eq15_set_ci[0x00020]; /* EQ15_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved15[0x00020]; +/* -------------- */ + pseudo_bit_t eq16_set_ci[0x00020]; /* EQ16_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved16[0x00020]; +/* -------------- */ + pseudo_bit_t eq17_set_ci[0x00020]; /* EQ17_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved17[0x00020]; +/* -------------- */ + pseudo_bit_t eq18_set_ci[0x00020]; /* EQ18_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved18[0x00020]; +/* -------------- */ + pseudo_bit_t eq19_set_ci[0x00020]; /* EQ19_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved19[0x00020]; +/* -------------- */ + pseudo_bit_t eq20_set_ci[0x00020]; /* EQ20_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved20[0x00020]; +/* -------------- */ + pseudo_bit_t eq21_set_ci[0x00020]; /* EQ21_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved21[0x00020]; +/* -------------- */ + pseudo_bit_t eq22_set_ci[0x00020]; /* EQ22_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved22[0x00020]; +/* -------------- */ + pseudo_bit_t eq23_set_ci[0x00020]; /* EQ23_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved23[0x00020]; +/* -------------- */ + pseudo_bit_t eq24_set_ci[0x00020]; /* EQ24_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved24[0x00020]; +/* -------------- */ + pseudo_bit_t eq25_set_ci[0x00020]; /* EQ25_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved25[0x00020]; +/* -------------- */ + pseudo_bit_t eq26_set_ci[0x00020]; /* EQ26_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved26[0x00020]; +/* -------------- */ + pseudo_bit_t eq27_set_ci[0x00020]; /* EQ27_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved27[0x00020]; +/* -------------- */ + pseudo_bit_t eq28_set_ci[0x00020]; /* EQ28_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved28[0x00020]; +/* -------------- */ + pseudo_bit_t eq29_set_ci[0x00020]; /* EQ29_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved29[0x00020]; +/* -------------- */ + pseudo_bit_t eq30_set_ci[0x00020]; /* EQ30_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved30[0x00020]; +/* -------------- */ + pseudo_bit_t eq31_set_ci[0x00020]; /* EQ31_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved31[0x00020]; +/* -------------- */ + pseudo_bit_t eq32_set_ci[0x00020]; /* EQ32_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved32[0x00020]; +/* -------------- */ + pseudo_bit_t eq33_set_ci[0x00020]; /* EQ33_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved33[0x00020]; +/* -------------- */ + pseudo_bit_t eq34_set_ci[0x00020]; /* EQ34_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved34[0x00020]; +/* -------------- */ + pseudo_bit_t eq35_set_ci[0x00020]; /* EQ35_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved35[0x00020]; +/* -------------- */ + pseudo_bit_t eq36_set_ci[0x00020]; /* EQ36_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved36[0x00020]; +/* -------------- */ + pseudo_bit_t eq37_set_ci[0x00020]; /* EQ37_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved37[0x00020]; +/* -------------- */ + pseudo_bit_t eq38_set_ci[0x00020]; /* EQ38_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved38[0x00020]; +/* -------------- */ + pseudo_bit_t eq39_set_ci[0x00020]; /* EQ39_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved39[0x00020]; +/* -------------- */ + pseudo_bit_t eq40_set_ci[0x00020]; /* EQ40_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved40[0x00020]; +/* -------------- */ + pseudo_bit_t eq41_set_ci[0x00020]; /* EQ41_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved41[0x00020]; +/* -------------- */ + pseudo_bit_t eq42_set_ci[0x00020]; /* EQ42_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved42[0x00020]; +/* -------------- */ + pseudo_bit_t eq43_set_ci[0x00020]; /* EQ43_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved43[0x00020]; +/* -------------- */ + pseudo_bit_t eq44_set_ci[0x00020]; /* EQ44_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved44[0x00020]; +/* -------------- */ + pseudo_bit_t eq45_set_ci[0x00020]; /* EQ45_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved45[0x00020]; +/* -------------- */ + pseudo_bit_t eq46_set_ci[0x00020]; /* EQ46_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved46[0x00020]; +/* -------------- */ + pseudo_bit_t eq47_set_ci[0x00020]; /* EQ47_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved47[0x00020]; +/* -------------- */ + pseudo_bit_t eq48_set_ci[0x00020]; /* EQ48_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved48[0x00020]; +/* -------------- */ + pseudo_bit_t eq49_set_ci[0x00020]; /* EQ49_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved49[0x00020]; +/* -------------- */ + pseudo_bit_t eq50_set_ci[0x00020]; /* EQ50_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved50[0x00020]; +/* -------------- */ + pseudo_bit_t eq51_set_ci[0x00020]; /* EQ51_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved51[0x00020]; +/* -------------- */ + pseudo_bit_t eq52_set_ci[0x00020]; /* EQ52_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved52[0x00020]; +/* -------------- */ + pseudo_bit_t eq53_set_ci[0x00020]; /* EQ53_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved53[0x00020]; +/* -------------- */ + pseudo_bit_t eq54_set_ci[0x00020]; /* EQ54_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved54[0x00020]; +/* -------------- */ + pseudo_bit_t eq55_set_ci[0x00020]; /* EQ55_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved55[0x00020]; +/* -------------- */ + pseudo_bit_t eq56_set_ci[0x00020]; /* EQ56_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved56[0x00020]; +/* -------------- */ + pseudo_bit_t eq57_set_ci[0x00020]; /* EQ57_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved57[0x00020]; +/* -------------- */ + pseudo_bit_t eq58_set_ci[0x00020]; /* EQ58_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved58[0x00020]; +/* -------------- */ + pseudo_bit_t eq59_set_ci[0x00020]; /* EQ59_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved59[0x00020]; +/* -------------- */ + pseudo_bit_t eq60_set_ci[0x00020]; /* EQ60_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved60[0x00020]; +/* -------------- */ + pseudo_bit_t eq61_set_ci[0x00020]; /* EQ61_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved61[0x00020]; +/* -------------- */ + pseudo_bit_t eq62_set_ci[0x00020]; /* EQ62_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved62[0x00020]; +/* -------------- */ + pseudo_bit_t eq63_set_ci[0x00020]; /* EQ63_Set_CI */ +/* -------------- */ + pseudo_bit_t reserved63[0x00020]; +/* -------------- */ +}; + +/* InfiniHost-III-EX Configuration Registers #### michal - match to PRM */ + +struct hermonprm_configuration_registers_st { /* Little Endian */ + pseudo_bit_t reserved0[0x403400]; +/* -------------- */ + struct hermonprm_hca_command_register_st hca_command_interface_register;/* HCA Command Register */ +/* -------------- */ + pseudo_bit_t reserved1[0x3fcb20]; +/* -------------- */ +}; + +/* QP_DB_Record ### michal = gdror fixed */ + +struct hermonprm_qp_db_record_st { /* Little Endian */ + pseudo_bit_t receive_wqe_counter[0x00010];/* Modulo-64K counter of WQEs posted to the QP since its creation. Should be initialized to zero. */ + pseudo_bit_t reserved0[0x00010]; +/* -------------- */ +}; + +/* CQ_ARM_DB_Record */ + +struct hermonprm_cq_arm_db_record_st { /* Little Endian */ + pseudo_bit_t counter[0x00020]; /* CQ counter for the arming request */ +/* -------------- */ + pseudo_bit_t cmd[0x00003]; /* 0x0 - No command + 0x1 - Request notification for next Solicited completion event. Counter filed specifies the current CQ Consumer Counter. + 0x2 - Request notification for next Solicited or Unsolicited completion event. Counter filed specifies the current CQ Consumer counter. + 0x3 - Request notification for multiple completions (Arm-N). Counter filed specifies the value of the CQ Index that when reached by HW (i.e. HW generates a CQE into this Index) Event will be generated + Other - Reserved */ + pseudo_bit_t cmd_sn[0x00002]; /* Command Sequence Number - See Table 35, "CQ Doorbell Layout" for definition of this filed */ + pseudo_bit_t res[0x00003]; /* Must be 0x2 */ + pseudo_bit_t cq_number[0x00018]; /* CQ number */ +/* -------------- */ +}; + +/* CQ_CI_DB_Record */ + +struct hermonprm_cq_ci_db_record_st { /* Little Endian */ + pseudo_bit_t counter[0x00020]; /* CQ counter */ +/* -------------- */ + pseudo_bit_t reserved0[0x00005]; + pseudo_bit_t res[0x00003]; /* Must be 0x1 */ + pseudo_bit_t cq_number[0x00018]; /* CQ number */ +/* -------------- */ +}; + +/* Virtual_Physical_Mapping */ + +struct hermonprm_virtual_physical_mapping_st { /* Little Endian */ + pseudo_bit_t va_h[0x00020]; /* Virtual Address[63:32]. Valid only for MAP_ICM command. */ +/* -------------- */ + pseudo_bit_t reserved0[0x0000c]; + pseudo_bit_t va_l[0x00014]; /* Virtual Address[31:12]. Valid only for MAP_ICM command. */ +/* -------------- */ + pseudo_bit_t pa_h[0x00020]; /* Physical Address[63:32] */ +/* -------------- */ + pseudo_bit_t log2size[0x00006]; /* Log2 of the size in 4KB pages of the physical and virtual contiguous memory that starts at PA_L/H and VA_L/H */ + pseudo_bit_t reserved1[0x00006]; + pseudo_bit_t pa_l[0x00014]; /* Physical Address[31:12] */ +/* -------------- */ +}; + +/* MOD_STAT_CFG #### michal - gdror fix */ + +struct hermonprm_mod_stat_cfg_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00010]; + pseudo_bit_t rx_options[0x00004]; /* number of RX options to sweep when doing SerDes parameters AutoNegotiation. */ + pseudo_bit_t reserved1[0x00003]; + pseudo_bit_t rx_options_m[0x00001]; /* Modify rx_options */ + pseudo_bit_t tx_options[0x00004]; /* number of TX options to sweep when doing SerDes parameters AutoNegotiation. */ + pseudo_bit_t reserved2[0x00003]; + pseudo_bit_t tx_options_m[0x00001]; /* Modify tx_options */ +/* -------------- */ + pseudo_bit_t reserved3[0x00020]; +/* -------------- */ + pseudo_bit_t pre_amp[0x00004]; /* Pre Amplitude */ + pseudo_bit_t pre_emp_pre_amp[0x00004]; + pseudo_bit_t pre_emp_out[0x00004]; /* Pre Emphasis Out */ + pseudo_bit_t voltage[0x00004]; + pseudo_bit_t equ[0x00004]; /* Equalization */ + pseudo_bit_t reserved4[0x0000b]; + pseudo_bit_t serdes_m[0x00001]; /* Modify serdes parameters */ +/* -------------- */ + pseudo_bit_t lid[0x00010]; /* default LID */ + pseudo_bit_t lid_m[0x00001]; /* Modify default LID */ + pseudo_bit_t reserved5[0x00003]; + pseudo_bit_t port_en[0x00001]; /* enable port (E_Key) */ + pseudo_bit_t port_en_m[0x00001]; /* Modify port_en */ + pseudo_bit_t reserved6[0x0000a]; +/* -------------- */ + pseudo_bit_t reserved7[0x0001f]; + pseudo_bit_t guid_hi_m[0x00001]; /* Modify guid_hi */ +/* -------------- */ + pseudo_bit_t guid_hi[0x00020]; +/* -------------- */ + pseudo_bit_t reserved8[0x0001f]; + pseudo_bit_t guid_lo_m[0x00001]; /* Modify guid_lo */ +/* -------------- */ + pseudo_bit_t guid_lo[0x00020]; +/* -------------- */ + pseudo_bit_t reserved9[0x0001f]; + pseudo_bit_t nodeguid_hi_m[0x00001]; +/* -------------- */ + pseudo_bit_t nodeguid_hi[0x00020]; +/* -------------- */ + pseudo_bit_t reserved10[0x0001f]; + pseudo_bit_t nodeguid_lo_m[0x00001]; +/* -------------- */ + pseudo_bit_t nodeguid_lo[0x00020]; +/* -------------- */ + pseudo_bit_t reserved11[0x00680]; +/* -------------- */ +}; + +/* SRQ Context */ + +struct hermonprm_srq_context_st { /* Little Endian */ + pseudo_bit_t srqn[0x00018]; /* SRQ number */ + pseudo_bit_t log_srq_size[0x00004]; /* Log2 of the Number of WQEs in the Receive Queue. + Maximum value is 0x10, i.e. 16M WQEs. */ + pseudo_bit_t state[0x00004]; /* SRQ State: + 1111 - SW Ownership + 0000 - HW Ownership + 0001 - Error + Valid only on QUERY_SRQ and HW2SW_SRQ commands. */ +/* -------------- */ + pseudo_bit_t src_domain[0x00010]; /* The Scalable RC Domain. Messages coming to receive ports specifying this SRQ as receive queue will be served only if SRC_Domain of the SRQ matches SRC_Domain of the transport QP of this message. */ + pseudo_bit_t reserved0[0x00008]; + pseudo_bit_t log_srq_stride[0x00003];/* Stride (max WQE size) on the receive queue. WQ entry is 16*(2^log_RQ_stride) bytes. */ + pseudo_bit_t reserved1[0x00005]; +/* -------------- */ + pseudo_bit_t cqn[0x00018]; /* Completion Queue to report SRC messages directed to this SRQ. */ + pseudo_bit_t page_offset[0x00006]; /* The offset of the first WQE from the beginning of 4Kbyte page (Figure 52,“Work Queue Buffer Structure”) */ + pseudo_bit_t reserved2[0x00002]; +/* -------------- */ + pseudo_bit_t reserved3[0x00020]; +/* -------------- */ + pseudo_bit_t mtt_base_addr_h[0x00008];/* MTT Base Address [39:32] in ICM relative to INIT_HCA.mtt_base_addr */ + pseudo_bit_t reserved4[0x00010]; + pseudo_bit_t log2_page_size[0x00006];/* Log (base 2) of MTT page size in units of 4KByte */ + pseudo_bit_t reserved5[0x00002]; +/* -------------- */ + pseudo_bit_t reserved6[0x00003]; + pseudo_bit_t mtt_base_addr_l[0x0001d];/* MTT Base Address [31:3] in ICM relative to INIT_HCA.mtt_base_addr */ +/* -------------- */ + pseudo_bit_t pd[0x00018]; /* SRQ protection domain */ + pseudo_bit_t reserved7[0x00008]; +/* -------------- */ + pseudo_bit_t wqe_cnt[0x00010]; /* WQE count on the SRQ. Valid only upon QUERY_SRQ and HW2SW_SRQ commands. */ + pseudo_bit_t lwm[0x00010]; /* Limit Water Mark - if the LWM is not zero, and the wqe_cnt drops below LWM when a WQE is dequeued from the SRQ, then an SRQ limit event is fired and the LWM is set to zero. Valid only upon QUERY_SRQ and HW2SW_SRQ commands. */ +/* -------------- */ + pseudo_bit_t srq_wqe_counter[0x00010];/* A 16-bit counter incremented for each WQE posted to the SRQ. Must be 0x0 in SRQ initialization. Valid only upon the QUERY_SRQ command. */ + pseudo_bit_t reserved8[0x00010]; +/* -------------- */ + pseudo_bit_t reserved9[0x00020]; +/* -------------- */ + pseudo_bit_t db_record_addr_h[0x00020];/* SRQ DB Record physical address [63:32] */ +/* -------------- */ + pseudo_bit_t reserved10[0x00002]; + pseudo_bit_t db_record_addr_l[0x0001e];/* SRQ DB Record physical address [31:2] */ +/* -------------- */ +}; + +/* PBL */ + +struct hermonprm_pbl_st { /* Little Endian */ + pseudo_bit_t mtt_0_h[0x00020]; /* First MTT[63:32] */ +/* -------------- */ + pseudo_bit_t mtt_0_l[0x00020]; /* First MTT[31:0] */ +/* -------------- */ + pseudo_bit_t mtt_1_h[0x00020]; /* Second MTT[63:32] */ +/* -------------- */ + pseudo_bit_t mtt_1_l[0x00020]; /* Second MTT[31:0] */ +/* -------------- */ + pseudo_bit_t mtt_2_h[0x00020]; /* Third MTT[63:32] */ +/* -------------- */ + pseudo_bit_t mtt_2_l[0x00020]; /* Third MTT[31:0] */ +/* -------------- */ + pseudo_bit_t mtt_3_h[0x00020]; /* Fourth MTT[63:32] */ +/* -------------- */ + pseudo_bit_t mtt_3_l[0x00020]; /* Fourth MTT[31:0] */ +/* -------------- */ +}; + +/* Performance Counters #### michal - gdror fixed */ + +struct hermonprm_performance_counters_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00080]; +/* -------------- */ + pseudo_bit_t reserved1[0x00080]; +/* -------------- */ + pseudo_bit_t reserved2[0x00080]; +/* -------------- */ + pseudo_bit_t reserved3[0x00060]; +/* -------------- */ + pseudo_bit_t reserved4[0x00620]; +/* -------------- */ +}; + +/* Transport and CI Error Counters */ + +struct hermonprm_transport_and_ci_error_counters_st { /* Little Endian */ + pseudo_bit_t rq_num_lle[0x00020]; /* Responder - number of local length errors */ +/* -------------- */ + pseudo_bit_t sq_num_lle[0x00020]; /* Requester - number of local length errors */ +/* -------------- */ + pseudo_bit_t rq_num_lqpoe[0x00020]; /* Responder - number local QP operation error */ +/* -------------- */ + pseudo_bit_t sq_num_lqpoe[0x00020]; /* Requester - number local QP operation error */ +/* -------------- */ + pseudo_bit_t rq_num_leeoe[0x00020]; /* Responder - number local EE operation error */ +/* -------------- */ + pseudo_bit_t sq_num_leeoe[0x00020]; /* Requester - number local EE operation error */ +/* -------------- */ + pseudo_bit_t rq_num_lpe[0x00020]; /* Responder - number of local protection errors */ +/* -------------- */ + pseudo_bit_t sq_num_lpe[0x00020]; /* Requester - number of local protection errors */ +/* -------------- */ + pseudo_bit_t rq_num_wrfe[0x00020]; /* Responder - number of CQEs with error. + Incremented each time a CQE with error is generated */ +/* -------------- */ + pseudo_bit_t sq_num_wrfe[0x00020]; /* Requester - number of CQEs with error. + Incremented each time a CQE with error is generated */ +/* -------------- */ + pseudo_bit_t reserved0[0x00020]; +/* -------------- */ + pseudo_bit_t sq_num_mwbe[0x00020]; /* Requester - number of memory window bind errors */ +/* -------------- */ + pseudo_bit_t reserved1[0x00020]; +/* -------------- */ + pseudo_bit_t sq_num_bre[0x00020]; /* Requester - number of bad response errors */ +/* -------------- */ + pseudo_bit_t rq_num_lae[0x00020]; /* Responder - number of local access errors */ +/* -------------- */ + pseudo_bit_t reserved2[0x00040]; +/* -------------- */ + pseudo_bit_t sq_num_rire[0x00020]; /* Requester - number of remote invalid request errors + NAK-Invalid Request on: + 1. Unsupported OpCode: Responder detected an unsupported OpCode. + 2. Unexpected OpCode: Responder detected an error in the sequence of OpCodes, such + as a missing "Last" packet. + Note: there is no PSN error, thus this does not indicate a dropped packet. */ +/* -------------- */ + pseudo_bit_t rq_num_rire[0x00020]; /* Responder - number of remote invalid request errors. + NAK may or may not be sent. + 1. QP Async Affiliated Error: Unsupported or Reserved OpCode (RC,RD only): + Inbound request OpCode was either reserved, or was for a function not supported by this + QP. (E.g. RDMA or ATOMIC on QP not set up for this). + 2. Misaligned ATOMIC: VA does not point to an aligned address on an atomic opera-tion. + 3. Too many RDMA READ or ATOMIC Requests: There were more requests received + and not ACKed than allowed for the connection. + 4. Out of Sequence OpCode, current packet is "First" or "Only": The Responder + detected an error in the sequence of OpCodes; a missing "Last" packet + 5. Out of Sequence OpCode, current packet is not "First" or "Only": The Responder + detected an error in the sequence of OpCodes; a missing "First" packet + 6. Local Length Error: Inbound "Send" request message exceeded the responder.s avail-able + buffer space. + 7. Length error: RDMA WRITE request message contained too much or too little pay-load + data compared to the DMA length advertised in the first or only packet. + 8. Length error: Payload length was not consistent with the opcode: + a: 0 byte <= "only" <= PMTU bytes + b: ("first" or "middle") == PMTU bytes + c: 1byte <= "last" <= PMTU bytes + 9. Length error: Inbound message exceeded the size supported by the CA port. */ +/* -------------- */ + pseudo_bit_t sq_num_rae[0x00020]; /* Requester - number of remote access errors. + NAK-Remote Access Error on: + R_Key Violation: Responder detected an invalid R_Key while executing an RDMA + Request. */ +/* -------------- */ + pseudo_bit_t rq_num_rae[0x00020]; /* Responder - number of remote access errors. + R_Key Violation Responder detected an R_Key violation while executing an RDMA + request. + NAK may or may not be sent. */ +/* -------------- */ + pseudo_bit_t sq_num_roe[0x00020]; /* Requester - number of remote operation errors. + NAK-Remote Operation Error on: + Remote Operation Error: Responder encountered an error, (local to the responder), + which prevented it from completing the request. */ +/* -------------- */ + pseudo_bit_t rq_num_roe[0x00020]; /* Responder - number of remote operation errors. + NAK-Remote Operation Error on: + 1. Malformed WQE: Responder detected a malformed Receive Queue WQE while pro-cessing + the packet. + 2. Remote Operation Error: Responder encountered an error, (local to the responder), + which prevented it from completing the request. */ +/* -------------- */ + pseudo_bit_t sq_num_tree[0x00020]; /* Requester - number of transport retries exceeded errors */ +/* -------------- */ + pseudo_bit_t reserved3[0x00020]; +/* -------------- */ + pseudo_bit_t sq_num_rree[0x00020]; /* Requester - number of RNR nak retries exceeded errors */ +/* -------------- */ + pseudo_bit_t rq_num_rnr[0x00020]; /* Responder - the number of RNR Naks sent */ +/* -------------- */ + pseudo_bit_t sq_num_rnr[0x00020]; /* Requester - the number of RNR Naks received */ +/* -------------- */ + pseudo_bit_t reserved4[0x00040]; +/* -------------- */ + pseudo_bit_t reserved5[0x00020]; +/* -------------- */ + pseudo_bit_t sq_num_rabrte[0x00020];/* Requester - number of remote aborted errors */ +/* -------------- */ + pseudo_bit_t reserved6[0x00020]; +/* -------------- */ + pseudo_bit_t sq_num_ieecne[0x00020];/* Requester - number of invalid EE context number errors */ +/* -------------- */ + pseudo_bit_t reserved7[0x00020]; +/* -------------- */ + pseudo_bit_t sq_num_ieecse[0x00020];/* Requester - invalid EE context state errors */ +/* -------------- */ + pseudo_bit_t reserved8[0x00380]; +/* -------------- */ + pseudo_bit_t rq_num_oos[0x00020]; /* Responder - number of out of sequence requests received */ +/* -------------- */ + pseudo_bit_t sq_num_oos[0x00020]; /* Requester - number of out of sequence Naks received */ +/* -------------- */ + pseudo_bit_t rq_num_mce[0x00020]; /* Responder - number of bad multicast packets received */ +/* -------------- */ + pseudo_bit_t reserved9[0x00020]; +/* -------------- */ + pseudo_bit_t rq_num_rsync[0x00020]; /* Responder - number of RESYNC operations */ +/* -------------- */ + pseudo_bit_t sq_num_rsync[0x00020]; /* Requester - number of RESYNC operations */ +/* -------------- */ + pseudo_bit_t rq_num_udsdprd[0x00020];/* The number of UD packets silently discarded on the receive queue due to lack of receive descriptor. */ +/* -------------- */ + pseudo_bit_t reserved10[0x00020]; +/* -------------- */ + pseudo_bit_t rq_num_ucsdprd[0x00020];/* The number of UC packets silently discarded on the receive queue due to lack of receive descriptor. */ +/* -------------- */ + pseudo_bit_t reserved11[0x003e0]; +/* -------------- */ + pseudo_bit_t num_cqovf[0x00020]; /* Number of CQ overflows */ +/* -------------- */ + pseudo_bit_t num_eqovf[0x00020]; /* Number of EQ overflows */ +/* -------------- */ + pseudo_bit_t num_baddb[0x00020]; /* Number of bad doorbells */ +/* -------------- */ + pseudo_bit_t reserved12[0x002a0]; +/* -------------- */ +}; + +/* Event_data Field - HCR Completion Event #### michal - match PRM */ + +struct hermonprm_hcr_completion_event_st { /* Little Endian */ + pseudo_bit_t token[0x00010]; /* HCR Token */ + pseudo_bit_t reserved0[0x00010]; +/* -------------- */ + pseudo_bit_t reserved1[0x00020]; +/* -------------- */ + pseudo_bit_t status[0x00008]; /* HCR Status */ + pseudo_bit_t reserved2[0x00018]; +/* -------------- */ + pseudo_bit_t out_param_h[0x00020]; /* HCR Output Parameter [63:32] */ +/* -------------- */ + pseudo_bit_t out_param_l[0x00020]; /* HCR Output Parameter [31:0] */ +/* -------------- */ + pseudo_bit_t reserved3[0x00020]; +/* -------------- */ +}; + +/* Completion with Error CQE #### michal - gdror fixed */ + +struct hermonprm_completion_with_error_st { /* Little Endian */ + pseudo_bit_t qpn[0x00018]; /* Indicates the QP for which completion is being reported */ + pseudo_bit_t reserved0[0x00008]; +/* -------------- */ + pseudo_bit_t reserved1[0x000a0]; +/* -------------- */ + pseudo_bit_t syndrome[0x00008]; /* Completion with error syndrome: + 0x01 - Local Length Error + 0x02 - Local QP Operation Error + 0x03 - Local EE Context Operation Error + 0x04 - Local Protection Error + 0x05 - Work Request Flushed Error + 0x06 - Memory Window Bind Error + 0x10 - Bad Response Error + 0x11 - Local Access Error + 0x12 - Remote Invalid Request Error + 0x13 - Remote Access Error + 0x14 - Remote Operation Error + 0x15 - Transport Retry Counter Exceeded + 0x16 - RNR Retry Counter Exceeded + 0x20 - Local RDD Violation Error + 0x21 - Remote Invalid RD Request + 0x22 - Remote Aborted Error + 0x23 - Invalid EE Context Number + 0x24 - Invalid EE Context State + other - Reserved + Syndrome is defined according to the IB specification volume 1. For detailed explanation of the syndromes, refer to chapters 10-11 of the IB specification rev 1.1. */ + pseudo_bit_t vendor_error_syndrome[0x00008]; + pseudo_bit_t wqe_counter[0x00010]; +/* -------------- */ + pseudo_bit_t opcode[0x00005]; /* The opcode of WQE completion is reported for. + + The following values are reported in case of completion with error: + 0xFE - For completion with error on Receive Queues + 0xFF - For completion with error on Send Queues */ + pseudo_bit_t reserved2[0x00001]; + pseudo_bit_t s_r[0x00001]; /* send 1 / receive 0 */ + pseudo_bit_t owner[0x00001]; /* HW Flips this bit for every CQ warp around. Initialized to Zero. */ + pseudo_bit_t reserved3[0x00018]; +/* -------------- */ +}; + +/* Resize CQ Input Mailbox */ + +struct hermonprm_resize_cq_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00040]; +/* -------------- */ + pseudo_bit_t reserved1[0x00006]; + pseudo_bit_t page_offset[0x00006]; + pseudo_bit_t reserved2[0x00014]; +/* -------------- */ + pseudo_bit_t reserved3[0x00018]; + pseudo_bit_t log_cq_size[0x00005]; /* Log (base 2) of the CQ size (in entries) */ + pseudo_bit_t reserved4[0x00003]; +/* -------------- */ + pseudo_bit_t reserved5[0x00020]; +/* -------------- */ + pseudo_bit_t mtt_base_addr_h[0x00008]; + pseudo_bit_t reserved6[0x00010]; + pseudo_bit_t log2_page_size[0x00006]; + pseudo_bit_t reserved7[0x00002]; +/* -------------- */ + pseudo_bit_t reserved8[0x00003]; + pseudo_bit_t mtt_base_addr_l[0x0001d]; +/* -------------- */ + pseudo_bit_t reserved9[0x00020]; +/* -------------- */ + pseudo_bit_t reserved10[0x00100]; +/* -------------- */ +}; + +/* MAD_IFC Input Modifier */ + +struct hermonprm_mad_ifc_input_modifier_st { /* Little Endian */ + pseudo_bit_t port_number[0x00008]; /* The packet reception port number (1 or 2). */ + pseudo_bit_t mad_extended_info[0x00001];/* Mad_Extended_Info valid bit (MAD_IFC Input Mailbox data from offset 00100h and down). MAD_Extended_Info is read only if this bit is set. + Required for trap generation when BKey check is enabled and for global routed packets. */ + pseudo_bit_t reserved0[0x00007]; + pseudo_bit_t rlid[0x00010]; /* Remote (source) LID from the received MAD. + This field is required for trap generation upon MKey/BKey validation. */ +/* -------------- */ +}; + +/* MAD_IFC Input Mailbox ###michal -gdror fixed */ + +struct hermonprm_mad_ifc_st { /* Little Endian */ + pseudo_bit_t request_mad_packet[64][0x00020];/* Request MAD Packet (256bytes) */ +/* -------------- */ + pseudo_bit_t my_qpn[0x00018]; /* Destination QP number from the received MAD. + This field is reserved if Mad_extended_info indication in the input modifier is clear. */ + pseudo_bit_t reserved0[0x00008]; +/* -------------- */ + pseudo_bit_t reserved1[0x00020]; +/* -------------- */ + pseudo_bit_t rqpn[0x00018]; /* Remote (source) QP number from the received MAD. + This field is reserved if Mad_extended_info indication in the input modifier is clear. */ + pseudo_bit_t reserved2[0x00008]; +/* -------------- */ + pseudo_bit_t reserved3[0x00010]; + pseudo_bit_t ml_path[0x00007]; /* My (destination) LID path bits from the received MAD. + This field is reserved if Mad_extended_info indication in the input modifier is clear. */ + pseudo_bit_t g[0x00001]; /* If set, the GRH field in valid. + This field is reserved if Mad_extended_info indication in the input modifier is clear. */ + pseudo_bit_t reserved4[0x00004]; + pseudo_bit_t sl[0x00004]; /* Service Level of the received MAD. + This field is reserved if Mad_extended_info indication in the input modifier is clear. */ +/* -------------- */ + pseudo_bit_t pkey_indx[0x00010]; /* Index in PKey table that matches PKey of the received MAD. + This field is reserved if Mad_extended_info indication in the input modifier is clear. */ + pseudo_bit_t reserved5[0x00010]; +/* -------------- */ + pseudo_bit_t reserved6[0x00160]; +/* -------------- */ + pseudo_bit_t grh[10][0x00020]; /* The GRH field of the MAD packet that was scattered to the first 40 bytes pointed to by the scatter list. + Valid if Mad_extended_info bit (in the input modifier) and g bit are set. + Otherwise this field is reserved. */ +/* -------------- */ + pseudo_bit_t reserved7[0x004c0]; +/* -------------- */ +}; + +/* Query Debug Message #### michal - gdror fixed */ + +struct hermonprm_query_debug_msg_st { /* Little Endian */ + pseudo_bit_t phy_addr_h[0x00020]; /* Translation of the address in firmware area. High 32 bits. */ +/* -------------- */ + pseudo_bit_t v[0x00001]; /* Physical translation is valid */ + pseudo_bit_t reserved0[0x0000b]; + pseudo_bit_t phy_addr_l[0x00014]; /* Translation of the address in firmware area. Low 32 bits. */ +/* -------------- */ + pseudo_bit_t fw_area_base[0x00020]; /* Firmware area base address. The format strings and the trace buffers may be located starting from this address. */ +/* -------------- */ + pseudo_bit_t fw_area_size[0x00020]; /* Firmware area size */ +/* -------------- */ + pseudo_bit_t trc_hdr_sz[0x00020]; /* Trace message header size in dwords. */ +/* -------------- */ + pseudo_bit_t trc_arg_num[0x00020]; /* The number of arguments per trace message. */ +/* -------------- */ + pseudo_bit_t reserved1[0x000c0]; +/* -------------- */ + pseudo_bit_t dbg_msk_h[0x00020]; /* Debug messages mask [63:32] */ +/* -------------- */ + pseudo_bit_t dbg_msk_l[0x00020]; /* Debug messages mask [31:0] */ +/* -------------- */ + pseudo_bit_t reserved2[0x00040]; +/* -------------- */ + pseudo_bit_t buff0_addr[0x00020]; /* Address in firmware area of Trace Buffer 0 */ +/* -------------- */ + pseudo_bit_t buff0_size[0x00020]; /* Size of Trace Buffer 0 */ +/* -------------- */ + pseudo_bit_t buff1_addr[0x00020]; /* Address in firmware area of Trace Buffer 1 */ +/* -------------- */ + pseudo_bit_t buff1_size[0x00020]; /* Size of Trace Buffer 1 */ +/* -------------- */ + pseudo_bit_t buff2_addr[0x00020]; /* Address in firmware area of Trace Buffer 2 */ +/* -------------- */ + pseudo_bit_t buff2_size[0x00020]; /* Size of Trace Buffer 2 */ +/* -------------- */ + pseudo_bit_t buff3_addr[0x00020]; /* Address in firmware area of Trace Buffer 3 */ +/* -------------- */ + pseudo_bit_t buff3_size[0x00020]; /* Size of Trace Buffer 3 */ +/* -------------- */ + pseudo_bit_t buff4_addr[0x00020]; /* Address in firmware area of Trace Buffer 4 */ +/* -------------- */ + pseudo_bit_t buff4_size[0x00020]; /* Size of Trace Buffer 4 */ +/* -------------- */ + pseudo_bit_t buff5_addr[0x00020]; /* Address in firmware area of Trace Buffer 5 */ +/* -------------- */ + pseudo_bit_t buff5_size[0x00020]; /* Size of Trace Buffer 5 */ +/* -------------- */ + pseudo_bit_t reserved3[0x00080]; +/* -------------- */ + pseudo_bit_t hw_buff_addr[0x00020]; /* Dror Mux Bohrer tracer */ +/* -------------- */ + pseudo_bit_t hw_buff_size[0x00020]; +/* -------------- */ + pseudo_bit_t reserved4[0x003c0]; +/* -------------- */ +}; + +/* User Access Region */ + +struct hermonprm_uar_st { /* Little Endian */ + struct hermonprm_rd_send_doorbell_st rd_send_doorbell;/* Reliable Datagram send doorbell */ +/* -------------- */ + struct hermonprm_send_doorbell_st send_doorbell;/* Send doorbell */ +/* -------------- */ + pseudo_bit_t reserved0[0x00040]; +/* -------------- */ + struct hermonprm_cq_cmd_doorbell_st cq_command_doorbell;/* CQ Doorbell */ +/* -------------- */ + pseudo_bit_t reserved1[0x03ec0]; +/* -------------- */ +}; + +/* Receive doorbell */ + +struct hermonprm_receive_doorbell_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00008]; + pseudo_bit_t wqe_counter[0x00010]; /* Modulo-64K counter of WQEs posted on this queue since its creation. Should be zero for the first doorbell on the QP */ + pseudo_bit_t reserved1[0x00008]; +/* -------------- */ + pseudo_bit_t reserved2[0x00005]; + pseudo_bit_t srq[0x00001]; /* If set, this is a Shared Receive Queue */ + pseudo_bit_t reserved3[0x00002]; + pseudo_bit_t qpn[0x00018]; /* QP number or SRQ number this doorbell is rung on */ +/* -------------- */ +}; + +/* SET_IB Parameters */ + +struct hermonprm_set_ib_st { /* Little Endian */ + pseudo_bit_t rqk[0x00001]; /* Reset QKey Violation Counter */ + pseudo_bit_t reserved0[0x00011]; + pseudo_bit_t sig[0x00001]; /* Set System Image GUID to system_image_guid specified. + system_image_guid and sig must be the same for all ports. */ + pseudo_bit_t reserved1[0x0000d]; +/* -------------- */ + pseudo_bit_t capability_mask[0x00020];/* PortInfo Capability Mask */ +/* -------------- */ + pseudo_bit_t system_image_guid_h[0x00020];/* System Image GUID[63:32], takes effect only if the SIG bit is set + Must be the same for both ports. */ +/* -------------- */ + pseudo_bit_t system_image_guid_l[0x00020];/* System Image GUID[31:0], takes effect only if the SIG bit is set + Must be the same for both ports. */ +/* -------------- */ + pseudo_bit_t reserved2[0x00180]; +/* -------------- */ +}; + +/* Multicast Group Member #### michal - gdror fixed */ + +struct hermonprm_mgm_entry_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00006]; + pseudo_bit_t next_gid_index[0x0001a];/* Index of next Multicast Group Member whose GID maps to same MGID_HASH number. + The index is into the Multicast Group Table, which is the comprised the MGHT and AMGM tables. + next_gid_index=0 means end of the chain. */ +/* -------------- */ + pseudo_bit_t reserved1[0x00060]; +/* -------------- */ + pseudo_bit_t mgid_128_96[0x00020]; /* Multicast group GID[128:96] in big endian format. + Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */ +/* -------------- */ + pseudo_bit_t mgid_95_64[0x00020]; /* Multicast group GID[95:64] in big endian format. + Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */ +/* -------------- */ + pseudo_bit_t mgid_63_32[0x00020]; /* Multicast group GID[63:32] in big endian format. + Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */ +/* -------------- */ + pseudo_bit_t mgid_31_0[0x00020]; /* Multicast group GID[31:0] in big endian format. + Use the Reserved GID 0:0:0:0:0:0:0:0 for an invalid entry. */ +/* -------------- */ + struct hermonprm_mgmqp_st mgmqp_0; /* Multicast Group Member QP */ +/* -------------- */ + struct hermonprm_mgmqp_st mgmqp_1; /* Multicast Group Member QP */ +/* -------------- */ + struct hermonprm_mgmqp_st mgmqp_2; /* Multicast Group Member QP */ +/* -------------- */ + struct hermonprm_mgmqp_st mgmqp_3; /* Multicast Group Member QP */ +/* -------------- */ + struct hermonprm_mgmqp_st mgmqp_4; /* Multicast Group Member QP */ +/* -------------- */ + struct hermonprm_mgmqp_st mgmqp_5; /* Multicast Group Member QP */ +/* -------------- */ + struct hermonprm_mgmqp_st mgmqp_6; /* Multicast Group Member QP */ +/* -------------- */ + struct hermonprm_mgmqp_st mgmqp_7; /* Multicast Group Member QP */ +/* -------------- */ +}; + +/* INIT_PORT Parameters #### michal - match PRM */ + +struct hermonprm_init_port_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00004]; + pseudo_bit_t vl_cap[0x00004]; /* Maximum VLs supported on the port, excluding VL15. + Legal values are 1,2,4 and 8. */ + pseudo_bit_t port_width_cap[0x00004];/* IB Port Width + 1 - 1x + 3 - 1x, 4x + 11 - 1x, 4x or 12x (must not be used in InfiniHost-III-EX MT25208) + else - Reserved */ + pseudo_bit_t reserved1[0x00004]; + pseudo_bit_t g0[0x00001]; /* Set port GUID0 to GUID0 specified */ + pseudo_bit_t ng[0x00001]; /* Set node GUID to node_guid specified. + node_guid and ng must be the same for all ports. */ + pseudo_bit_t sig[0x00001]; /* Set System Image GUID to system_image_guid specified. + system_image_guid and sig must be the same for all ports. */ + pseudo_bit_t reserved2[0x0000d]; +/* -------------- */ + pseudo_bit_t max_gid[0x00010]; /* Maximum number of GIDs for the port */ + pseudo_bit_t mtu[0x00010]; /* Maximum MTU Supported in bytes + must be: 256, 512, 1024, 2048 or 4096 + For Eth port, can be any + Field must not cross device capabilities as reported + */ +/* -------------- */ + pseudo_bit_t max_pkey[0x00010]; /* Maximum pkeys for the port. + Must be the same for both ports. */ + pseudo_bit_t reserved3[0x00010]; +/* -------------- */ + pseudo_bit_t reserved4[0x00020]; +/* -------------- */ + pseudo_bit_t guid0_h[0x00020]; /* EUI-64 GUID assigned by the manufacturer, takes effect only if the G0 bit is set (bits 63:32) */ +/* -------------- */ + pseudo_bit_t guid0_l[0x00020]; /* EUI-64 GUID assigned by the manufacturer, takes effect only if the G0 bit is set (bits 31:0) */ +/* -------------- */ + pseudo_bit_t node_guid_h[0x00020]; /* Node GUID[63:32], takes effect only if the NG bit is set + Must be the same for both ports. */ +/* -------------- */ + pseudo_bit_t node_guid_l[0x00020]; /* Node GUID[31:0], takes effect only if the NG bit is set + Must be the same for both ports. */ +/* -------------- */ + pseudo_bit_t system_image_guid_h[0x00020];/* System Image GUID[63:32], takes effect only if the SIG bit is set + Must be the same for both ports. */ +/* -------------- */ + pseudo_bit_t system_image_guid_l[0x00020];/* System Image GUID[31:0], takes effect only if the SIG bit is set + Must be the same for both ports. */ +/* -------------- */ + pseudo_bit_t reserved5[0x006c0]; +/* -------------- */ +}; + +/* Query Device Capablities #### michal - gdror fixed */ + +struct hermonprm_query_dev_cap_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00080]; +/* -------------- */ + pseudo_bit_t log_max_qp[0x00005]; /* Log2 of the Maximum number of QPs supported */ + pseudo_bit_t reserved1[0x00003]; + pseudo_bit_t log2_rsvd_qps[0x00004];/* Log (base 2) of the number of QPs reserved for firmware use + The reserved resources are numbered from 0 to 2^log2_rsvd_qps-1 */ + pseudo_bit_t reserved2[0x00004]; + pseudo_bit_t log_max_qp_sz[0x00008];/* The maximum number of WQEs allowed on the RQ or the SQ is 2^log_max_qp_sz-1 */ + pseudo_bit_t log_max_srq_sz[0x00008];/* The maximum number of WQEs allowed on the SRQ is 2^log_max_srq_sz-1 */ +/* -------------- */ + pseudo_bit_t log_max_scqs[0x00004]; /* log base 2 of number of supported schedule queues */ + pseudo_bit_t reserved3[0x00004]; + pseudo_bit_t num_rsvd_scqs[0x00006]; + pseudo_bit_t reserved4[0x00002]; + pseudo_bit_t log_max_srqs[0x00005]; + pseudo_bit_t reserved5[0x00007]; + pseudo_bit_t log2_rsvd_srqs[0x00004]; +/* -------------- */ + pseudo_bit_t log_max_cq[0x00005]; /* Log2 of the Maximum number of CQs supported */ + pseudo_bit_t reserved6[0x00003]; + pseudo_bit_t log2_rsvd_cqs[0x00004];/* Log (base 2) of the number of CQs reserved for firmware use + The reserved resources are numbered from 0 to 2^log2_rsrvd_cqs-1 */ + pseudo_bit_t reserved7[0x00004]; + pseudo_bit_t log_max_cq_sz[0x00008];/* Log2 of the Maximum CQEs allowed in a CQ */ + pseudo_bit_t reserved8[0x00008]; +/* -------------- */ + pseudo_bit_t log_max_eq[0x00004]; /* Log2 of the Maximum number of EQs */ + pseudo_bit_t reserved9[0x00004]; + pseudo_bit_t num_rsvd_eqs[0x00004]; /* The number of EQs reserved for firmware use + The reserved resources are numbered from 0 to num_rsvd_eqs-1 + If 0 - no resources are reserved. */ + pseudo_bit_t reserved10[0x00004]; + pseudo_bit_t log_max_d_mpts[0x00006];/* Log (base 2) of the maximum number of data MPT entries (the number of Regions/Windows) */ + pseudo_bit_t reserved11[0x00002]; + pseudo_bit_t log_max_eq_sz[0x00008];/* Log2 of the Maximum EQEs allowed in a EQ */ +/* -------------- */ + pseudo_bit_t log_max_mtts[0x00006]; /* Log2 of the Maximum number of MTT entries */ + pseudo_bit_t reserved12[0x00002]; + pseudo_bit_t log2_rsvd_mrws[0x00004];/* Log (base 2) of the number of MPTs reserved for firmware use + The reserved resources are numbered from 0 to 2^log2_rsvd_mrws-1 */ + pseudo_bit_t reserved13[0x00004]; + pseudo_bit_t log_max_mrw_sz[0x00007];/* Log2 of the Maximum Size of Memory Region/Window. is it in PRM layout? */ + pseudo_bit_t reserved14[0x00005]; + pseudo_bit_t log2_rsvd_mtts[0x00004];/* Log (base 2) of the number of MTT entries reserved for firmware use + The reserved resources are numbered from 0 to 2^log2_rsvd_mtts-1 + */ +/* -------------- */ + pseudo_bit_t reserved15[0x00020]; +/* -------------- */ + pseudo_bit_t log_max_ra_res_qp[0x00006];/* Log2 of the Maximum number of outstanding RDMA read/Atomic per QP as a responder */ + pseudo_bit_t reserved16[0x0000a]; + pseudo_bit_t log_max_ra_req_qp[0x00006];/* Log2 of the maximum number of outstanding RDMA read/Atomic per QP as a requester */ + pseudo_bit_t reserved17[0x0000a]; +/* -------------- */ + pseudo_bit_t log_max_ra_res_global[0x00006];/* Log2 of the maximum number of RDMA read/atomic operations the HCA responder can support globally. That implies the RDB table size. */ + pseudo_bit_t reserved18[0x0001a]; +/* -------------- */ + pseudo_bit_t rsz_srq[0x00001]; /* Ability to modify the maximum number of WRs per SRQ. */ + pseudo_bit_t reserved19[0x0001f]; +/* -------------- */ + pseudo_bit_t num_ports[0x00004]; /* Number of IB ports. */ + pseudo_bit_t max_vl_ib[0x00004]; /* Maximum VLs supported on each port, excluding VL15 */ + pseudo_bit_t ib_port_width[0x00004];/* IB Port Width + 1 - 1x + 3 - 1x, 4x + 11 - 1x, 4x or 12x + else - Reserved */ + pseudo_bit_t ib_mtu[0x00004]; /* Maximum MTU Supported + 0x0 - Reserved + 0x1 - 256 + 0x2 - 512 + 0x3 - 1024 + 0x4 - 2048 + 0x5 - 4096 + 0x6-0xF Reserved */ + pseudo_bit_t local_ca_ack_delay[0x00005];/* The Local CA ACK Delay. This is the value recommended to be returned in Query HCA verb. + The delay value in microseconds is computed using 4.096us * 2^(local_ca_ack_delay). */ + pseudo_bit_t port_type[0x00004]; /* Hermon New. bit per port. bit0 is first port. value '1' is ehternet. '0' is IB */ + pseudo_bit_t reserved20[0x00004]; + pseudo_bit_t w[0x00001]; /* Hermon New. 10GB eth support */ + pseudo_bit_t j[0x00001]; /* Hermon New. Jumbo frame support */ + pseudo_bit_t reserved21[0x00001]; +/* -------------- */ + pseudo_bit_t log_max_gid[0x00004]; /* Log2 of the maximum number of GIDs per port */ + pseudo_bit_t reserved22[0x00004]; + pseudo_bit_t log_ethtype[0x00004]; /* Hermon New. log2 eth type table size */ + pseudo_bit_t reserved23[0x00004]; + pseudo_bit_t log_drain_size[0x00008];/* Log (base 2) of minimum size of the NoDropVLDrain buffer, specified in 4Kpages units */ + pseudo_bit_t log_max_msg[0x00005]; /* Log (base 2) of the maximum message size supported by the device */ + pseudo_bit_t reserved24[0x00003]; +/* -------------- */ + pseudo_bit_t log_max_pkey[0x00004]; /* Log2 of the max PKey Table Size (per IB port) */ + pseudo_bit_t reserved25[0x0000c]; + pseudo_bit_t stat_rate_support[0x00010];/* bit mask of stat rate supported + bit 0 - full bw + bit 1 - 1/4 bw + bit 2 - 1/8 bw + bit 3 - 1/2 bw; */ +/* -------------- */ + pseudo_bit_t reserved26[0x00020]; +/* -------------- */ + pseudo_bit_t rc[0x00001]; /* RC Transport supported */ + pseudo_bit_t uc[0x00001]; /* UC Transport Supported */ + pseudo_bit_t ud[0x00001]; /* UD Transport Supported */ + pseudo_bit_t src[0x00001]; /* SRC Transport Supported. Hermon New instead of RD. */ + pseudo_bit_t rcm[0x00001]; /* Reliable Multicast support. Hermon New instead of IPv6 Transport Supported */ + pseudo_bit_t fcoib[0x00001]; /* Hermon New */ + pseudo_bit_t srq[0x00001]; /* SRQ is supported + */ + pseudo_bit_t checksum[0x00001]; /* IP over IB checksum is supported */ + pseudo_bit_t pkv[0x00001]; /* PKey Violation Counter Supported */ + pseudo_bit_t qkv[0x00001]; /* QKey Violation Coutner Supported */ + pseudo_bit_t vmm[0x00001]; /* Hermon New */ + pseudo_bit_t reserved27[0x00005]; + pseudo_bit_t mw[0x00001]; /* Memory windows supported */ + pseudo_bit_t apm[0x00001]; /* Automatic Path Migration Supported */ + pseudo_bit_t atm[0x00001]; /* Atomic operations supported (atomicity is guaranteed between QPs on this HCA) */ + pseudo_bit_t rm[0x00001]; /* Raw Multicast Supported */ + pseudo_bit_t avp[0x00001]; /* Address Vector Port checking supported */ + pseudo_bit_t udm[0x00001]; /* UD Multicast Supported */ + pseudo_bit_t reserved28[0x00002]; + pseudo_bit_t pg[0x00001]; /* Paging on demand supported */ + pseudo_bit_t r[0x00001]; /* Router mode supported */ + pseudo_bit_t reserved29[0x00006]; +/* -------------- */ + pseudo_bit_t log_pg_sz[0x00008]; /* Minimum system page size supported (log2). + For proper operation it must be less than or equal the hosting platform (CPU) minimum page size. */ + pseudo_bit_t reserved30[0x00008]; + pseudo_bit_t uar_sz[0x00006]; /* UAR Area Size = 1MB * 2^uar_sz */ + pseudo_bit_t reserved31[0x00006]; + pseudo_bit_t num_rsvd_uars[0x00004];/* The number of UARs reserved for firmware use + The reserved resources are numbered from 0 to num_reserved_uars-1 + Note that UAR number num_reserved_uars is always for the kernel. */ +/* -------------- */ + pseudo_bit_t log_max_bf_pages[0x00006];/* Maximum number of BlueFlame pages is 2^log_max_bf_pages */ + pseudo_bit_t reserved32[0x00002]; + pseudo_bit_t log_max_bf_regs_per_page[0x00006];/* Maximum number of BlueFlame registers per page is 2^log_max_bf_regs_per_page. It may be that only the beginning of a page contains BlueFlame registers. */ + pseudo_bit_t reserved33[0x00002]; + pseudo_bit_t log_bf_reg_size[0x00005];/* BlueFlame register size in bytes is 2^log_bf_reg_size */ + pseudo_bit_t reserved34[0x0000a]; + pseudo_bit_t bf[0x00001]; /* If set to "1" then BlueFlame may be used. */ +/* -------------- */ + pseudo_bit_t max_desc_sz_sq[0x00010];/* Max descriptor size in bytes for the send queue */ + pseudo_bit_t max_sg_sq[0x00008]; /* The maximum S/G list elements in a SQ WQE (max_desc_sz/16 - 3) */ + pseudo_bit_t reserved35[0x00008]; +/* -------------- */ + pseudo_bit_t max_desc_sz_rq[0x00010];/* Max descriptor size in bytes for the receive queue */ + pseudo_bit_t max_sg_rq[0x00008]; /* The maximum S/G list elements in a RQ WQE (max_desc_sz/16 - 3) */ + pseudo_bit_t reserved36[0x00008]; +/* -------------- */ + pseudo_bit_t reserved37[0x00001]; + pseudo_bit_t fexch_base_mpt_31_25[0x00007];/* Hermon New. FC mpt base mpt number */ + pseudo_bit_t fcp_ud_base_23_8[0x00010];/* Hermon New. FC ud QP base QPN */ + pseudo_bit_t fexch_base_qp_23_16[0x00008];/* Hermon New. FC Exchange QP base QPN */ +/* -------------- */ + pseudo_bit_t reserved38[0x00020]; +/* -------------- */ + pseudo_bit_t log_max_mcg[0x00008]; /* Log2 of the maximum number of multicast groups */ + pseudo_bit_t num_rsvd_mcgs[0x00004];/* The number of MGMs reserved for firmware use in the MGHT. + The reserved resources are numbered from 0 to num_reserved_mcgs-1 + If 0 - no resources are reserved. */ + pseudo_bit_t reserved39[0x00004]; + pseudo_bit_t log_max_qp_mcg[0x00008];/* Log2 of the maximum number of QPs per multicast group */ + pseudo_bit_t reserved40[0x00008]; +/* -------------- */ + pseudo_bit_t log_max_srcds[0x00004];/* Log2 of the maximum number of SRC Domains */ + pseudo_bit_t reserved41[0x00008]; + pseudo_bit_t num_rsvd_scrds[0x00004];/* The number of SRCDs reserved for firmware use + The reserved resources are numbered from 0 to num_reserved_rdds-1. + If 0 - no resources are reserved. */ + pseudo_bit_t log_max_pd[0x00005]; /* Log2 of the maximum number of PDs */ + pseudo_bit_t reserved42[0x00007]; + pseudo_bit_t num_rsvd_pds[0x00004]; /* The number of PDs reserved for firmware use + The reserved resources are numbered from 0 to num_reserved_pds-1 + If 0 - no resources are reserved. */ +/* -------------- */ + pseudo_bit_t reserved43[0x000c0]; +/* -------------- */ + pseudo_bit_t qpc_entry_sz[0x00010]; /* QPC Entry Size for the device + For the InfiniHost-III-EX MT25208 entry size is 256 bytes */ + pseudo_bit_t rdmardc_entry_sz[0x00010];/* RdmaRdC Entry Size for the device + For the InfiniHost-III-EX MT25208 entry size is 256 bytes */ +/* -------------- */ + pseudo_bit_t altc_entry_sz[0x00010];/* Extended QPC entry size for the device + For the InfiniHost-III-EX MT25208 entry size is 32 bytes */ + pseudo_bit_t aux_entry_sz[0x00010]; /* Auxilary context entry size */ +/* -------------- */ + pseudo_bit_t cqc_entry_sz[0x00010]; /* CQC entry size for the device + For the InfiniHost-III-EX MT25208 entry size is 64 bytes */ + pseudo_bit_t eqc_entry_sz[0x00010]; /* EQ context entry size for the device + For the InfiniHost-III-EX MT25208 entry size is 64 bytes */ +/* -------------- */ + pseudo_bit_t c_mpt_entry_sz[0x00010];/* cMPT entry size in Bytes for the device. + For the InfiniHost-III-EX MT25208 entry size is 64 bytes */ + pseudo_bit_t srq_entry_sz[0x00010]; /* SRQ context entry size for the device + For the InfiniHost-III-EX MT25208 entry size is 32 bytes */ +/* -------------- */ + pseudo_bit_t d_mpt_entry_sz[0x00010];/* dMPT entry size in Bytes for the device. + For the InfiniHost-III-EX MT25208 entry size is 64 bytes */ + pseudo_bit_t mtt_entry_sz[0x00010]; /* MTT entry size in Bytes for the device. + For the InfiniHost-III-EX MT25208 entry size is 8 bytes */ +/* -------------- */ + pseudo_bit_t bmme[0x00001]; /* Base Memory Management Extension Support */ + pseudo_bit_t win_type[0x00001]; /* Bound Type 2 Memory Window Association mechanism: + 0 - Type 2A - QP Number Association; or + 1 - Type 2B - QP Number and PD Association. */ + pseudo_bit_t mps[0x00001]; /* Ability of this HCA to support multiple page sizes per Memory Region. */ + pseudo_bit_t bl[0x00001]; /* Ability of this HCA to support Block List Physical Buffer Lists. */ + pseudo_bit_t zb[0x00001]; /* Zero Based region/windows supported */ + pseudo_bit_t lif[0x00001]; /* Ability of this HCA to support Local Invalidate Fencing. */ + pseudo_bit_t reserved44[0x0001a]; +/* -------------- */ + pseudo_bit_t resd_lkey[0x00020]; /* The value of the reserved Lkey for Base Memory Management Extension */ +/* -------------- */ + pseudo_bit_t reserved45[0x00020]; +/* -------------- */ + pseudo_bit_t max_icm_size_h[0x00020];/* Bits [63:32] of maximum ICM size InfiniHost III Ex support in bytes. */ +/* -------------- */ + pseudo_bit_t max_icm_size_l[0x00020];/* Bits [31:0] of maximum ICM size InfiniHost III Ex support in bytes. */ +/* -------------- */ + pseudo_bit_t reserved46[0x002c0]; +/* -------------- */ +}; + +/* QUERY_ADAPTER Parameters Block #### michal - gdror fixed */ + +struct hermonprm_query_adapter_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00080]; +/* -------------- */ + pseudo_bit_t reserved1[0x00018]; + pseudo_bit_t intapin[0x00008]; /* Driver should set this field to INTR value in the event queue in order to get Express interrupt messages. */ +/* -------------- */ + pseudo_bit_t reserved2[0x00060]; +/* -------------- */ + struct hermonprm_vsd_st vsd; /* ###michal- this field was replaced by 2 fields : vsd .1664; vsd(continued/psid .128; */ +/* -------------- */ +}; + +/* QUERY_FW Parameters Block #### michal - doesn't match PRM */ + +struct hermonprm_query_fw_st { /* Little Endian */ + pseudo_bit_t fw_rev_major[0x00010]; /* Firmware Revision - Major */ + pseudo_bit_t fw_pages[0x00010]; /* Amount of physical memory to be allocated for FW usage is in 4KByte pages. */ +/* -------------- */ + pseudo_bit_t fw_rev_minor[0x00010]; /* Firmware Revision - Minor */ + pseudo_bit_t fw_rev_subminor[0x00010];/* Firmware Sub-minor version (Patch level). */ +/* -------------- */ + pseudo_bit_t cmd_interface_rev[0x00010];/* Command Interface Interpreter Revision ID */ + pseudo_bit_t reserved0[0x00010]; +/* -------------- */ + pseudo_bit_t log_max_outstanding_cmd[0x00008];/* Log2 of the maximum number of commands the HCR can support simultaneously */ + pseudo_bit_t reserved1[0x00017]; + pseudo_bit_t dt[0x00001]; /* Debug Trace Support + 0 - Debug trace is not supported + 1 - Debug trace is supported */ +/* -------------- */ + pseudo_bit_t reserved2[0x00001]; + pseudo_bit_t ccq[0x00001]; /* CCQ support */ + pseudo_bit_t reserved3[0x00006]; + pseudo_bit_t fw_seconds[0x00008]; /* FW timestamp - seconds. Dispalyed as Hexadecimal number */ + pseudo_bit_t fw_minutes[0x00008]; /* FW timestamp - minutes. Dispalyed as Hexadecimal number */ + pseudo_bit_t fw_hour[0x00008]; /* FW timestamp - hour. Dispalyed as Hexadecimal number */ +/* -------------- */ + pseudo_bit_t fw_day[0x00008]; /* FW timestamp - day. Dispalyed as Hexadecimal number */ + pseudo_bit_t fw_month[0x00008]; /* FW timestamp - month. Dispalyed as Hexadecimal number */ + pseudo_bit_t fw_year[0x00010]; /* FW timestamp - year. Dispalyed as Hexadecimal number (e.g. 0x2005) */ +/* -------------- */ + pseudo_bit_t reserved4[0x00040]; +/* -------------- */ + pseudo_bit_t clr_int_base_offset_h[0x00020];/* Bits [63:32] of the Clear Interrupt register’s offset from clr_int_bar register in PCIaddress space. Points to a 64-bit register. */ +/* -------------- */ + pseudo_bit_t clr_int_base_offset_l[0x00020];/* Bits [31:0] of the Clear Interrupt register’s offset from clr_int_bar register in PCIaddress space. Points to a 64-bit register. */ +/* -------------- */ + pseudo_bit_t reserved5[0x0001e]; + pseudo_bit_t clr_int_bar[0x00002]; /* PCI base address register (BAR) where clr_int register is located. + 00 - BAR 0-1 + 01 - BAR 2-3 + 10 - BAR 4-5 + 11 - Reserved + The PCI BARs of ConnectX are 64 bit BARs. + In ConnectX, clr_int register is located on BAR 0-1. */ +/* -------------- */ + pseudo_bit_t reserved6[0x00020]; +/* -------------- */ + pseudo_bit_t error_buf_offset_h[0x00020];/* Read Only buffer for catastrophic error reports (bits [63:32] of offset from error_buf_bar register in PCI address space.) */ +/* -------------- */ + pseudo_bit_t error_buf_offset_l[0x00020];/* Read Only buffer for catastrophic error reports (bits [31:0] of offset from error_buf_bar register in PCI address space.) */ +/* -------------- */ + pseudo_bit_t error_buf_size[0x00020];/* Size in words */ +/* -------------- */ + pseudo_bit_t reserved7[0x0001e]; + pseudo_bit_t error_buf_bar[0x00002];/* PCI base address register (BAR) where error_buf register is located. + 00 - BAR 0-1 + 01 - BAR 2-3 + 10 - BAR 4-5 + 11 - Reserved + The PCI BARs of ConnectX are 64 bit BARs. + In ConnectX, error_buf register is located on BAR 0-1. */ +/* -------------- */ + pseudo_bit_t reserved8[0x00600]; +/* -------------- */ +}; + +/* Memory Access Parameters for UD Address Vector Table */ + +struct hermonprm_udavtable_memory_parameters_st { /* Little Endian */ + pseudo_bit_t l_key[0x00020]; /* L_Key used to access TPT */ +/* -------------- */ + pseudo_bit_t pd[0x00018]; /* PD used by TPT for matching against PD of region entry being accessed. */ + pseudo_bit_t reserved0[0x00005]; + pseudo_bit_t xlation_en[0x00001]; /* When cleared, address is physical address and no translation will be done. When set, address is virtual. */ + pseudo_bit_t reserved1[0x00002]; +/* -------------- */ +}; + +/* INIT_HCA & QUERY_HCA Parameters Block ####michal-doesn't match PRM (see differs below) new size in bytes:0x300 */ + +struct hermonprm_init_hca_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00018]; + pseudo_bit_t version[0x00008]; +/* -------------- */ + pseudo_bit_t reserved1[0x00040]; +/* -------------- */ + pseudo_bit_t reserved2[0x00010]; + pseudo_bit_t hca_core_clock[0x00010];/* Internal Clock freq in MHz */ +/* -------------- */ + pseudo_bit_t router_qp[0x00018]; /* QP number for router mode (8 LSBits should be 0). Low order 8 bits are taken from the TClass field of the incoming packet. + Valid only if RE bit is set */ + pseudo_bit_t reserved3[0x00005]; + pseudo_bit_t ipr2[0x00001]; /* Hermon New. IP router on port 2 */ + pseudo_bit_t ipr1[0x00001]; /* Hermon New. IP router on port 1 */ + pseudo_bit_t ibr[0x00001]; /* InfiniBand Router Mode */ +/* -------------- */ + pseudo_bit_t udp[0x00001]; /* UD Port Check Enable + 0 - Port field in Address Vector is ignored + 1 - HCA will check the port field in AV entry (fetched for UD descriptor) against the Port of the UD QP executing the descriptor. */ + pseudo_bit_t he[0x00001]; /* Host Endianess - Used for Atomic Operations + 0 - Host is Little Endian + 1 - Host is Big endian + */ + pseudo_bit_t reserved4[0x00001]; + pseudo_bit_t ce[0x00001]; /* Checksum Enabled - when Set IPoverIB checksum generation & checking is enabled */ + pseudo_bit_t reserved5[0x0001c]; +/* -------------- */ + pseudo_bit_t reserved6[0x00040]; +/* -------------- */ + struct hermonprm_qpcbaseaddr_st qpc_eec_cqc_eqc_rdb_parameters;/* ## michal - this field has chenged to - "qpc_cqc_eqc_parameters" - gdror, this is ok for now */ +/* -------------- */ + pseudo_bit_t reserved7[0x00100]; +/* -------------- */ + struct hermonprm_multicastparam_st multicast_parameters;/* ##michal- this field has chenged to - "IBUD/IPv6_multicast_parameters" - gdror - this is OK for now */ +/* -------------- */ + pseudo_bit_t reserved8[0x00080]; +/* -------------- */ + struct hermonprm_tptparams_st tpt_parameters; +/* -------------- */ + pseudo_bit_t reserved9[0x00080]; +/* -------------- */ + struct hermonprm_uar_params_st uar_parameters;/* UAR Parameters */ +/* -------------- */ + pseudo_bit_t reserved10[0x00600]; +/* -------------- */ +}; + +/* Event Queue Context Table Entry #### michal - gdror fixed */ + +struct hermonprm_eqc_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00008]; + pseudo_bit_t st[0x00004]; /* Event delivery state machine + 0x9 - Armed + 0xA - Fired + 0xB - Always_Armed (auto-rearm) + other - reserved */ + pseudo_bit_t reserved1[0x00005]; + pseudo_bit_t oi[0x00001]; /* Oerrun ignore. + If set, HW will not check EQ full condition when writing new EQEs. */ + pseudo_bit_t ec[0x00001]; /* is set, all EQEs are written (coalesced) to first EQ entry */ + pseudo_bit_t reserved2[0x00009]; + pseudo_bit_t status[0x00004]; /* EQ status: + 0000 - OK + 1010 - EQ write failure + Valid for the QUERY_EQ and HW2SW_EQ commands only */ +/* -------------- */ + pseudo_bit_t reserved3[0x00020]; +/* -------------- */ + pseudo_bit_t reserved4[0x00005]; + pseudo_bit_t page_offset[0x00007]; /* offset bits[11:5] of first EQE in the EQ relative to the first page in memory region mapping this EQ */ + pseudo_bit_t reserved5[0x00014]; +/* -------------- */ + pseudo_bit_t reserved6[0x00018]; + pseudo_bit_t log_eq_size[0x00005]; /* Log (base 2) of the EQ size (in entries). Maximum EQ size is 2^22 EQEs (max log_eq_size is 22) */ + pseudo_bit_t reserved7[0x00003]; +/* -------------- */ + pseudo_bit_t eq_max_count[0x00010]; /* Event Generation Moderation counter */ + pseudo_bit_t eq_period[0x00010]; /* Event Generation moderation timed, microseconds */ +/* -------------- */ + pseudo_bit_t intr[0x0000a]; /* MSI-X table entry index to be used to signal interrupts on this EQ. Reserved if MSI-X are not enabled in the PCI configuration header. */ + pseudo_bit_t reserved8[0x00016]; +/* -------------- */ + pseudo_bit_t mtt_base_addr_h[0x00008];/* MTT Base Address [39:32] relative to INIT_HCA.mtt_base_addr */ + pseudo_bit_t reserved9[0x00010]; + pseudo_bit_t log2_page_size[0x00006];/* Log (base 2) of MTT page size in units of 4KByte */ + pseudo_bit_t reserved10[0x00002]; +/* -------------- */ + pseudo_bit_t reserved11[0x00003]; + pseudo_bit_t mtt_base_addr_l[0x0001d];/* MTT Base Address [31:3] relative to INIT_HCA.mtt_base_addr */ +/* -------------- */ + pseudo_bit_t reserved12[0x00040]; +/* -------------- */ + pseudo_bit_t consumer_counter[0x00018];/* Consumer counter. The counter is incremented for each EQE polled from the EQ. + Must be 0x0 in EQ initialization. + Maintained by HW (valid for the QUERY_EQ command only). */ + pseudo_bit_t reserved13[0x00008]; +/* -------------- */ + pseudo_bit_t producer_counter[0x00018];/* Producer Coutner. The counter is incremented for each EQE that is written by the HW to the EQ. + EQ overrun is reported if Producer_counter + 1 equals to Consumer_counter and a EQE needs to be added. + Maintained by HW (valid for the QUERY_EQ command only) */ + pseudo_bit_t reserved14[0x00008]; +/* -------------- */ + pseudo_bit_t reserved15[0x00080]; +/* -------------- */ +}; + +/* Memory Translation Table (MTT) Entry #### michal - match to PRM */ + +struct hermonprm_mtt_st { /* Little Endian */ + pseudo_bit_t ptag_h[0x00020]; /* High-order bits of physical tag. The size of the field depends on the page size of the region. Maximum PTAG size is 52 bits. */ +/* -------------- */ + pseudo_bit_t p[0x00001]; /* Present bit. If set, page entry is valid. If cleared, access to this page will generate non-present page access fault. */ + pseudo_bit_t reserved0[0x00002]; + pseudo_bit_t ptag_l[0x0001d]; /* Low-order bits of Physical tag. The size of the field depends on the page size of the region. Maximum PTAG size is 52 bits. */ +/* -------------- */ +}; + +/* Memory Protection Table (MPT) Entry ### doesn't match PRM (new fields were added). new size in bytes : 0x54 */ + +struct hermonprm_mpt_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00008]; + pseudo_bit_t r_w[0x00001]; /* Defines whether this entry is Region (1) or Window (0) */ + pseudo_bit_t pa[0x00001]; /* Physical address. If set, no virtual-to-physical address translation is performed for this region */ + pseudo_bit_t lr[0x00001]; /* If set - local read access is enabled. Must be set for all MPT Entries. */ + pseudo_bit_t lw[0x00001]; /* If set - local write access is enabled */ + pseudo_bit_t rr[0x00001]; /* If set - remote read access is enabled. */ + pseudo_bit_t rw[0x00001]; /* If set - remote write access is enabled */ + pseudo_bit_t atomic[0x00001]; /* If set - remote Atomic access is allowed. */ + pseudo_bit_t eb[0x00001]; /* If set - bind is enabled. Valid only for regions. */ + pseudo_bit_t atc_req[0x00001]; /* If set, second hop of address translation (PA to MA) to be performed in the device prior to issuing the uplink request. */ + pseudo_bit_t atc_xlated[0x00001]; /* If set, uplink cycle to be issues with “ATC_translated” indicator to force bypass of the chipset IOMMU. */ + pseudo_bit_t reserved1[0x00001]; + pseudo_bit_t no_snoop[0x00001]; /* If set, issue PCIe cycle with ûno Snoopÿ attribute - cycle not to be snooped in CPU caches */ + pseudo_bit_t reserved2[0x00008]; + pseudo_bit_t status[0x00004]; /* 0xF - Not Valid 0x3 - Free. else - HW ownership.Unbound Type1 windows are denoted by reg_wnd_len=0. Unbound Type II windows are denoted by Status = Free. */ +/* -------------- */ + pseudo_bit_t reserved3[0x00007]; + pseudo_bit_t bqp[0x00001]; /* 0 - not bound to qp (type 1 window, MR)1 - bound to qp (type 2 window) */ + pseudo_bit_t qpn[0x00018]; /* QP number this MW is attached to. Valid for type2 memory windows and on QUERY_MPT only */ +/* -------------- */ + pseudo_bit_t mem_key[0x00020]; /* The memory Key. The field holds the mem_key field in the following semantics: {key[7:0],key[31:8]}. */ +/* -------------- */ + pseudo_bit_t pd[0x00018]; /* Protection Domain. If VMM support is enabled PD[17:23] specify Guest VM Identifier */ + pseudo_bit_t en_rinv[0x00001]; /* Enable remote invalidation */ + pseudo_bit_t ei[0x00001]; /* Enable Invalidation - When set, Local/Remote invalidation can be executed on this window/region. Must be set for type2 windows and non-shared physical memory regions. Must be clear for regions that are used to access Work Queues, Completion Queues and Event Queues */ + pseudo_bit_t nce[0x00001]; /* Data can be cached in Network Cache (see ûNetwork Cacheÿ on page 81) */ + pseudo_bit_t fre[0x00001]; /* When set, Fast Registration Operations can be executed on this region */ + pseudo_bit_t rae[0x00001]; /* When set, remote access can be enabled on this region. Used when executing Fast Registration Work Request to validate that remote access rights can be granted to this MPT. If the bit is cleared, Fast Registration Work Request requesting remote access rights will fail */ + pseudo_bit_t w_dif[0x00001]; /* Wire space contains dif */ + pseudo_bit_t m_dif[0x00001]; /* Memory space contains dif */ + pseudo_bit_t reserved4[0x00001]; +/* -------------- */ + pseudo_bit_t start_addr_h[0x00020]; /* Start Address - Virtual Address where this region/window starts */ +/* -------------- */ + pseudo_bit_t start_addr_l[0x00020]; /* Start Address - Virtual Address where this region/window starts */ +/* -------------- */ + pseudo_bit_t len_h[0x00020]; /* Region/Window Length */ +/* -------------- */ + pseudo_bit_t len_l[0x00020]; /* Region/Window Length */ +/* -------------- */ + pseudo_bit_t lkey[0x00020]; /* Must be 0 for SW2HW_MPT. On QUERY_MPT and HW2SW_MPT commands for Memory Window it reflects the LKey of the Region that the Window is bound to.The field holds the lkey field in the following semantics: {key[7:0],key[31:8]}. */ +/* -------------- */ + pseudo_bit_t win_cnt[0x00018]; /* Number of windows bound to this region. Valid for regions only.The field is valid only for the QUERY_MPT and HW2SW_MPT commands. */ + pseudo_bit_t reserved5[0x00008]; +/* -------------- */ + pseudo_bit_t mtt_rep[0x00004]; /* Log (base 2) of the number of time an MTT is replicated.E.g. for 64KB virtual blocks from 512B blocks, a replication factor of 2^7 is needed (MTT_REPLICATION_FACTOR=7).Up to 1MB of replicated block works */ + pseudo_bit_t reserved6[0x00011]; + pseudo_bit_t block_mode[0x00001]; /* If set, the page size is not power of two, and entity_size is in bytes. */ + pseudo_bit_t len64[0x00001]; /* Region/Window Length[64]. This bit added to enable registering 2^64 bytes per region */ + pseudo_bit_t fbo_en[0x00001]; /* If set, mtt_fbo field is valid, otherwise it is calculated from least significant bytes of the address. Must be set when mtt_rep is used or MPT is block-mode region */ + pseudo_bit_t reserved7[0x00008]; +/* -------------- */ + pseudo_bit_t mtt_adr_h[0x00008]; /* Offset to MTT list for this region. Must be aligned on 8 bytes. */ + pseudo_bit_t reserved8[0x00018]; +/* -------------- */ + pseudo_bit_t mtt_adr_l[0x00020]; /* Offset to MTT list for this region. Must be aligned on 8 bytes.###michal-relpaced with: RESERVED .3;mtt_adr_l .29; gdror - this is OK to leave it this way. */ +/* -------------- */ + pseudo_bit_t mtt_size[0x00020]; /* Number of MTT entries allocated for this MR.When Fast Registration Operations cannot be executed on this region (FRE bit is zero) this field is reserved.When Fast Registration Operation is enabled (FRE bit is set) this field indicates the number of MTTs allocated for this MR. If mtt_sz value cannot be zero. */ +/* -------------- */ + pseudo_bit_t entity_size[0x00015]; /* Page/block size. If MPT maps pages, the page size is 2entiry_size. If MPT maps blocks, the entity_size field specifies block size in bytes. The minimum amount of memory that can be mapped with single MTT is 512 bytes. */ + pseudo_bit_t reserved9[0x0000b]; +/* -------------- */ + pseudo_bit_t mtt_fbo[0x00015]; /* First byte offset in the zero-based region - the first byte within the first block/page start address refers to. When mtt_rep is being used, fbo points within the replicated block (i.e. block-size x 2^mtt_rep) */ + pseudo_bit_t reserved10[0x0000b]; +/* -------------- */ +}; + +/* Completion Queue Context Table Entry #### michal - match PRM */ + +struct hermonprm_completion_queue_context_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00008]; + pseudo_bit_t st[0x00004]; /* Event delivery state machine + 0x0 - reserved + 0x9 - ARMED (Request for Notification) + 0x6 - ARMED SOLICITED (Request Solicited Notification) + 0xA - FIRED + other - reserved + + Must be 0x0 in CQ initialization. + Valid for the QUERY_CQ and HW2SW_CQ commands only. */ + pseudo_bit_t reserved1[0x00005]; + pseudo_bit_t oi[0x00001]; /* When set, overrun ignore is enabled. + When set, Updates of CQ consumer counter (poll for completion) or Request completion notifications (Arm CQ) doorbells should not be rang on that CQ. */ + pseudo_bit_t cc[0x00001]; /* is set, all CQEs are written (coalesced) to first CQ entry */ + pseudo_bit_t reserved2[0x00009]; + pseudo_bit_t status[0x00004]; /* CQ status + 0000 - OK + 1001 - CQ overflow + 1010 - CQ write failure + Valid for the QUERY_CQ and HW2SW_CQ commands only */ +/* -------------- */ + pseudo_bit_t reserved3[0x00020]; +/* -------------- */ + pseudo_bit_t reserved4[0x00005]; + pseudo_bit_t page_offset[0x00007]; /* offset of first CQE in the CQ relative to the first page in memory region mapping this CQ */ + pseudo_bit_t reserved5[0x00014]; +/* -------------- */ + pseudo_bit_t usr_page[0x00018]; /* UAR page this CQ can be accessed through (ringinig CQ doorbells) */ + pseudo_bit_t log_cq_size[0x00005]; /* Log (base 2) of the CQ size (in entries). + Maximum CQ size is 2^17 CQEs (max log_cq_size is 17) */ + pseudo_bit_t reserved6[0x00003]; +/* -------------- */ + pseudo_bit_t cq_max_count[0x00010]; /* Event Generation Moderation counter */ + pseudo_bit_t cq_period[0x00010]; /* Event Generation moderation timed, microseconds */ +/* -------------- */ + pseudo_bit_t c_eqn[0x00009]; /* Event Queue this CQ reports completion events to. + Valid values are 0 to 63 + If configured to value other than 0-63, completion events will not be reported on the CQ. */ + pseudo_bit_t reserved7[0x00017]; +/* -------------- */ + pseudo_bit_t mtt_base_addr_h[0x00008];/* MTT Base Address [39:32] in ICM relative to INIT_HCA.mtt_base_addr */ + pseudo_bit_t reserved8[0x00010]; + pseudo_bit_t log2_page_size[0x00006]; + pseudo_bit_t reserved9[0x00002]; +/* -------------- */ + pseudo_bit_t reserved10[0x00003]; + pseudo_bit_t mtt_base_addr_l[0x0001d];/* MTT Base Address [31:3] in ICM relative to INIT_HCA.mtt_base_addr */ +/* -------------- */ + pseudo_bit_t last_notified_indx[0x00018];/* Maintained by HW. + Valid for QUERY_CQ and HW2SW_CQ commands only. */ + pseudo_bit_t reserved11[0x00008]; +/* -------------- */ + pseudo_bit_t solicit_producer_indx[0x00018];/* Maintained by HW. + Valid for QUERY_CQ and HW2SW_CQ commands only. + */ + pseudo_bit_t reserved12[0x00008]; +/* -------------- */ + pseudo_bit_t consumer_counter[0x00018];/* Consumer counter is a 32bits counter that is incremented for each CQE pooled from the CQ. + */ + pseudo_bit_t reserved13[0x00008]; +/* -------------- */ + pseudo_bit_t producer_counter[0x00018];/* Producer counter is a 32bits counter that is incremented for each CQE that is written by the HW to the CQ. + CQ overrun is reported if Producer_counter + 1 equals to Consumer_counter and a CQE needs to be added.. + Maintained by HW (valid for the QUERY_CQ and HW2SW_CQ commands only) */ + pseudo_bit_t reserved14[0x00008]; +/* -------------- */ + pseudo_bit_t reserved15[0x00020]; +/* -------------- */ + pseudo_bit_t reserved16[0x00020]; +/* -------------- */ + pseudo_bit_t db_record_addr_h[0x00020];/* CQ DB Record physical address [63:32] */ +/* -------------- */ + pseudo_bit_t reserved17[0x00003]; + pseudo_bit_t db_record_addr_l[0x0001d];/* CQ DB Record physical address [31:3] */ +/* -------------- */ +}; + +/* GPIO_event_data #### michal - gdror fixed */ + +struct hermonprm_gpio_event_data_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00060]; +/* -------------- */ + pseudo_bit_t gpio_event_hi[0x00020];/* If any bit is set to 1, then a rising/falling event has occurred on the corrsponding GPIO pin. */ +/* -------------- */ + pseudo_bit_t gpio_event_lo[0x00020];/* If any bit is set to 1, then a rising/falling event has occurred on the corrsponding GPIO pin. */ +/* -------------- */ + pseudo_bit_t reserved1[0x00020]; +/* -------------- */ +}; + +/* Event_data Field - QP/EE Events #### michal - doesn't match PRM */ + +struct hermonprm_qp_ee_event_st { /* Little Endian */ + pseudo_bit_t qpn_een[0x00018]; /* QP/EE/SRQ number event is reported for ###michal - field changed to QP number */ + pseudo_bit_t reserved0[0x00008]; +/* -------------- */ + pseudo_bit_t reserved1[0x00020]; +/* -------------- */ + pseudo_bit_t reserved2[0x0001c]; + pseudo_bit_t e_q[0x00001]; /* If set - EEN if cleared - QP in the QPN/EEN field + Not valid on SRQ events ###michal - field replaced with RESERVED */ + pseudo_bit_t reserved3[0x00003]; +/* -------------- */ + pseudo_bit_t reserved4[0x00060]; +/* -------------- */ +}; + +/* InfiniHost-III-EX Type0 Configuration Header ####michal - doesn't match PRM (new fields added, see below) */ + +struct hermonprm_mt25208_type0_st { /* Little Endian */ + pseudo_bit_t vendor_id[0x00010]; /* Hardwired to 0x15B3 */ + pseudo_bit_t device_id[0x00010]; /* 25208 (decimal) - InfiniHost-III compatible mode + 25408 (decimal) - InfiniHost-III EX mode (the mode described in this manual) + 25209 (decimal) - Flash burner mode - see Flash burning application note for further details on this mode + */ +/* -------------- */ + pseudo_bit_t command[0x00010]; /* PCI Command Register */ + pseudo_bit_t status[0x00010]; /* PCI Status Register */ +/* -------------- */ + pseudo_bit_t revision_id[0x00008]; + pseudo_bit_t class_code_hca_class_code[0x00018]; +/* -------------- */ + pseudo_bit_t cache_line_size[0x00008];/* Cache Line Size */ + pseudo_bit_t latency_timer[0x00008]; + pseudo_bit_t header_type[0x00008]; /* hardwired to zero */ + pseudo_bit_t bist[0x00008]; +/* -------------- */ + pseudo_bit_t bar0_ctrl[0x00004]; /* hard-wired to 0100 */ + pseudo_bit_t reserved0[0x00010]; + pseudo_bit_t bar0_l[0x0000c]; /* Lower bits of BAR0 (Device Configuration Space) */ +/* -------------- */ + pseudo_bit_t bar0_h[0x00020]; /* Upper 32 bits of BAR0 (Device Configuration Space) */ +/* -------------- */ + pseudo_bit_t bar1_ctrl[0x00004]; /* Hardwired to 1100 */ + pseudo_bit_t reserved1[0x00010]; + pseudo_bit_t bar1_l[0x0000c]; /* Lower bits of BAR1 (User Access Region - UAR - space) */ +/* -------------- */ + pseudo_bit_t bar1_h[0x00020]; /* upper 32 bits of BAR1 (User Access Region - UAR - space) */ +/* -------------- */ + pseudo_bit_t bar2_ctrl[0x00004]; /* Hardwired to 1100 */ + pseudo_bit_t reserved2[0x00010]; + pseudo_bit_t bar2_l[0x0000c]; /* Lower bits of BAR2 - Local Attached Memory if present and enabled. Else zeroed. */ +/* -------------- */ + pseudo_bit_t bar2_h[0x00020]; /* Upper 32 bits of BAR2 - Local Attached Memory if present and enabled. Else zeroed. */ +/* -------------- */ + pseudo_bit_t cardbus_cis_pointer[0x00020]; +/* -------------- */ + pseudo_bit_t subsystem_vendor_id[0x00010];/* Specified by the device NVMEM configuration */ + pseudo_bit_t subsystem_id[0x00010]; /* Specified by the device NVMEM configuration */ +/* -------------- */ + pseudo_bit_t expansion_rom_enable[0x00001];/* Expansion ROM Enable. Hardwired to 0 if expansion ROM is disabled in the device NVMEM configuration. */ + pseudo_bit_t reserved3[0x0000a]; + pseudo_bit_t expansion_rom_base_address[0x00015];/* Expansion ROM Base Address (upper 21 bit). Hardwired to 0 if expansion ROM is disabled in the device NVMEM configuration. */ +/* -------------- */ + pseudo_bit_t capabilities_pointer[0x00008];/* Specified by the device NVMEM configuration */ + pseudo_bit_t reserved4[0x00018]; +/* -------------- */ + pseudo_bit_t reserved5[0x00020]; +/* -------------- */ + pseudo_bit_t interrupt_line[0x00008]; + pseudo_bit_t interrupt_pin[0x00008]; + pseudo_bit_t min_gnt[0x00008]; + pseudo_bit_t max_latency[0x00008]; +/* -------------- */ + pseudo_bit_t reserved6[0x00100]; +/* -------------- */ + pseudo_bit_t msi_cap_id[0x00008]; + pseudo_bit_t msi_next_cap_ptr[0x00008]; + pseudo_bit_t msi_en[0x00001]; + pseudo_bit_t multiple_msg_cap[0x00003]; + pseudo_bit_t multiple_msg_en[0x00003]; + pseudo_bit_t cap_64_bit_addr[0x00001]; + pseudo_bit_t reserved7[0x00008]; +/* -------------- */ + pseudo_bit_t msg_addr_l[0x00020]; +/* -------------- */ + pseudo_bit_t msg_addr_h[0x00020]; +/* -------------- */ + pseudo_bit_t msg_data[0x00010]; + pseudo_bit_t reserved8[0x00010]; +/* -------------- */ + pseudo_bit_t reserved9[0x00080]; +/* -------------- */ + pseudo_bit_t pm_cap_id[0x00008]; /* Power management capability ID - 01h */ + pseudo_bit_t pm_next_cap_ptr[0x00008]; + pseudo_bit_t pm_cap[0x00010]; /* [2:0] Version - 02h + [3] PME clock - 0h + [4] RsvP + [5] Device specific initialization - 0h + [8:6] AUX current - 0h + [9] D1 support - 0h + [10] D2 support - 0h + [15:11] PME support - 0h */ +/* -------------- */ + pseudo_bit_t pm_status_control[0x00010];/* [14:13] - Data scale - 0h */ + pseudo_bit_t pm_control_status_brdg_ext[0x00008]; + pseudo_bit_t data[0x00008]; +/* -------------- */ + pseudo_bit_t reserved10[0x00040]; +/* -------------- */ + pseudo_bit_t vpd_cap_id[0x00008]; /* 03h */ + pseudo_bit_t vpd_next_cap_id[0x00008]; + pseudo_bit_t vpd_address[0x0000f]; + pseudo_bit_t f[0x00001]; +/* -------------- */ + pseudo_bit_t vpd_data[0x00020]; +/* -------------- */ + pseudo_bit_t reserved11[0x00040]; +/* -------------- */ + pseudo_bit_t pciex_cap_id[0x00008]; /* PCI-Express capability ID - 10h */ + pseudo_bit_t pciex_next_cap_ptr[0x00008]; + pseudo_bit_t pciex_cap[0x00010]; /* [3:0] Capability version - 1h + [7:4] Device/Port Type - 0h + [8] Slot implemented - 0h + [13:9] Interrupt message number + */ +/* -------------- */ + pseudo_bit_t device_cap[0x00020]; /* [2:0] Max_Payload_Size supported - 2h + [4:3] Phantom Function supported - 0h + [5] Extended Tag Filed supported - 0h + [8:6] Endpoint L0s Acceptable Latency - TBD + [11:9] Endpoint L1 Acceptable Latency - TBD + [12] Attention Button Present - configured through InfiniBurn + [13] Attention Indicator Present - configured through InfiniBurn + [14] Power Indicator Present - configured through InfiniBurn + [25:18] Captured Slot Power Limit Value + [27:26] Captured Slot Power Limit Scale */ +/* -------------- */ + pseudo_bit_t device_control[0x00010]; + pseudo_bit_t device_status[0x00010]; +/* -------------- */ + pseudo_bit_t link_cap[0x00020]; /* [3:0] Maximum Link Speed - 1h + [9:4] Maximum Link Width - 8h + [11:10] Active State Power Management Support - 3h + [14:12] L0s Exit Latency - TBD + [17:15] L1 Exit Latency - TBD + [31:24] Port Number - 0h */ +/* -------------- */ + pseudo_bit_t link_control[0x00010]; + pseudo_bit_t link_status[0x00010]; /* [3:0] Link Speed - 1h + [9:4] Negotiated Link Width + [12] Slot clock configuration - 1h */ +/* -------------- */ + pseudo_bit_t reserved12[0x00260]; +/* -------------- */ + pseudo_bit_t advanced_error_reporting_cap_id[0x00010];/* 0001h. */ + pseudo_bit_t capability_version[0x00004];/* 1h */ + pseudo_bit_t next_capability_offset[0x0000c];/* 0h */ +/* -------------- */ + pseudo_bit_t uncorrectable_error_status_register[0x00020];/* 0 Training Error Status + 4 Data Link Protocol Error Status + 12 Poisoned TLP Status + 13 Flow Control Protocol Error Status + 14 Completion Timeout Status + 15 Completer Abort Status + 16 Unexpected Completion Status + 17 Receiver Overflow Status + 18 Malformed TLP Status + 19 ECRC Error Status + 20 Unsupported Request Error Status */ +/* -------------- */ + pseudo_bit_t uncorrectable_error_mask_register[0x00020];/* 0 Training Error Mask + 4 Data Link Protocol Error Mask + 12 Poisoned TLP Mask + 13 Flow Control Protocol Error Mask + 14 Completion Timeout Mask + 15 Completer Abort Mask + 16 Unexpected Completion Mask + 17 Receiver Overflow Mask + 18 Malformed TLP Mask + 19 ECRC Error Mask + 20 Unsupported Request Error Mask */ +/* -------------- */ + pseudo_bit_t uncorrectable_severity_mask_register[0x00020];/* 0 Training Error Severity + 4 Data Link Protocol Error Severity + 12 Poisoned TLP Severity + 13 Flow Control Protocol Error Severity + 14 Completion Timeout Severity + 15 Completer Abort Severity + 16 Unexpected Completion Severity + 17 Receiver Overflow Severity + 18 Malformed TLP Severity + 19 ECRC Error Severity + 20 Unsupported Request Error Severity */ +/* -------------- */ + pseudo_bit_t correctable_error_status_register[0x00020];/* 0 Receiver Error Status + 6 Bad TLP Status + 7 Bad DLLP Status + 8 REPLAY_NUM Rollover Status + 12 Replay Timer Timeout Status */ +/* -------------- */ + pseudo_bit_t correctable_error_mask_register[0x00020];/* 0 Receiver Error Mask + 6 Bad TLP Mask + 7 Bad DLLP Mask + 8 REPLAY_NUM Rollover Mask + 12 Replay Timer Timeout Mask */ +/* -------------- */ + pseudo_bit_t advance_error_capabilities_and_control_register[0x00020]; +/* -------------- */ + struct hermonprm_header_log_register_st header_log_register; +/* -------------- */ + pseudo_bit_t reserved13[0x006a0]; +/* -------------- */ +}; + +/* Event Data Field - Performance Monitor */ + +struct hermonprm_performance_monitor_event_st { /* Little Endian */ + struct hermonprm_performance_monitors_st performance_monitor_snapshot;/* Performance monitor snapshot */ +/* -------------- */ + pseudo_bit_t monitor_number[0x00008];/* 0x01 - SQPC + 0x02 - RQPC + 0x03 - CQC + 0x04 - Rkey + 0x05 - TLB + 0x06 - port0 + 0x07 - port1 */ + pseudo_bit_t reserved0[0x00018]; +/* -------------- */ + pseudo_bit_t reserved1[0x00040]; +/* -------------- */ +}; + +/* Event_data Field - Page Faults */ + +struct hermonprm_page_fault_event_data_st { /* Little Endian */ + pseudo_bit_t va_h[0x00020]; /* Virtual Address[63:32] this page fault is reported on */ +/* -------------- */ + pseudo_bit_t va_l[0x00020]; /* Virtual Address[63:32] this page fault is reported on */ +/* -------------- */ + pseudo_bit_t mem_key[0x00020]; /* Memory Key this page fault is reported on */ +/* -------------- */ + pseudo_bit_t qp[0x00018]; /* QP this page fault is reported on */ + pseudo_bit_t reserved0[0x00003]; + pseudo_bit_t a[0x00001]; /* If set the memory access that caused the page fault was atomic */ + pseudo_bit_t lw[0x00001]; /* If set the memory access that caused the page fault was local write */ + pseudo_bit_t lr[0x00001]; /* If set the memory access that caused the page fault was local read */ + pseudo_bit_t rw[0x00001]; /* If set the memory access that caused the page fault was remote write */ + pseudo_bit_t rr[0x00001]; /* If set the memory access that caused the page fault was remote read */ +/* -------------- */ + pseudo_bit_t pd[0x00018]; /* PD this page fault is reported on */ + pseudo_bit_t reserved1[0x00008]; +/* -------------- */ + pseudo_bit_t prefetch_len[0x00020]; /* Indicates how many subsequent pages in the same memory region/window will be accessed by the following transaction after this page fault is resolved. measured in bytes. SW can use this information in order to page-in the subsequent pages if they are not present. */ +/* -------------- */ +}; + +/* WQE segments format */ + +struct hermonprm_wqe_segment_st { /* Little Endian */ + struct hermonprm_send_wqe_segment_st send_wqe_segment;/* Send WQE segment format */ +/* -------------- */ + pseudo_bit_t reserved0[0x00280]; +/* -------------- */ + struct hermonprm_wqe_segment_ctrl_mlx_st mlx_wqe_segment_ctrl;/* MLX WQE segment format */ +/* -------------- */ + pseudo_bit_t reserved1[0x00100]; +/* -------------- */ + pseudo_bit_t recv_wqe_segment_ctrl[4][0x00020];/* Receive segment format */ +/* -------------- */ + pseudo_bit_t reserved2[0x00080]; +/* -------------- */ +}; + +/* Event_data Field - Port State Change #### michal - match PRM */ + +struct hermonprm_port_state_change_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00040]; +/* -------------- */ + pseudo_bit_t reserved1[0x0001c]; + pseudo_bit_t p[0x00002]; /* Port number (1 or 2) */ + pseudo_bit_t reserved2[0x00002]; +/* -------------- */ + pseudo_bit_t reserved3[0x00060]; +/* -------------- */ +}; + +/* Event_data Field - Completion Queue Error #### michal - match PRM */ + +struct hermonprm_completion_queue_error_st { /* Little Endian */ + pseudo_bit_t cqn[0x00018]; /* CQ number event is reported for */ + pseudo_bit_t reserved0[0x00008]; +/* -------------- */ + pseudo_bit_t reserved1[0x00020]; +/* -------------- */ + pseudo_bit_t syndrome[0x00008]; /* Error syndrome + 0x01 - CQ overrun + 0x02 - CQ access violation error */ + pseudo_bit_t reserved2[0x00018]; +/* -------------- */ + pseudo_bit_t reserved3[0x00060]; +/* -------------- */ +}; + +/* Event_data Field - Completion Event #### michal - match PRM */ + +struct hermonprm_completion_event_st { /* Little Endian */ + pseudo_bit_t cqn[0x00018]; /* CQ number event is reported for */ + pseudo_bit_t reserved0[0x00008]; +/* -------------- */ + pseudo_bit_t reserved1[0x000a0]; +/* -------------- */ +}; + +/* Event Queue Entry #### michal - match to PRM */ + +struct hermonprm_event_queue_entry_st { /* Little Endian */ + pseudo_bit_t event_sub_type[0x00008];/* Event Sub Type. + Defined for events which have sub types, zero elsewhere. */ + pseudo_bit_t reserved0[0x00008]; + pseudo_bit_t event_type[0x00008]; /* Event Type */ + pseudo_bit_t reserved1[0x00008]; +/* -------------- */ + pseudo_bit_t event_data[6][0x00020];/* Delivers auxilary data to handle event. */ +/* -------------- */ + pseudo_bit_t reserved2[0x00007]; + pseudo_bit_t owner[0x00001]; /* Owner of the entry + 0 SW + 1 HW */ + pseudo_bit_t reserved3[0x00018]; +/* -------------- */ +}; + +/* QP/EE State Transitions Command Parameters ###michal - doesn't match PRM (field name changed) */ + +struct hermonprm_qp_ee_state_transitions_st { /* Little Endian */ + pseudo_bit_t opt_param_mask[0x00020];/* This field defines which optional parameters are passed. Each bit specifies whether optional parameter is passed (set) or not (cleared). The optparammask is defined for each QP/EE command. */ +/* -------------- */ + pseudo_bit_t reserved0[0x00020]; +/* -------------- */ + struct hermonprm_queue_pair_ee_context_entry_st qpc_eec_data;/* QPC/EEC data ###michal - field has replaced with "qpc_data" (size .1948) */ +/* -------------- */ + pseudo_bit_t reserved1[0x00800]; +/* -------------- */ +}; + +/* Completion Queue Entry Format #### michal - fixed by gdror */ + +struct hermonprm_completion_queue_entry_st { /* Little Endian */ + pseudo_bit_t qpn[0x00018]; /* Indicates the QP for which completion is being reported */ + pseudo_bit_t reserved0[0x00002]; + pseudo_bit_t d2s[0x00001]; /* Duplicate to Sniffer. This bit is set if both Send and Receive queues are subject for sniffer queue. The HW delivers + packet only to send-associated sniffer receive queue. */ + pseudo_bit_t fcrc_sd[0x00001]; /* FCRC: If set, FC CRC is correct in FC frame encapsulated in payload. Valid for Raw Frame FC receive queue only. + SD: CQ associated with Sniffer receive queue. If set, packets were skipped due to lack of receive buffers on the Sniffer receive queue */ + pseudo_bit_t fl[0x00001]; /* Force Loopback Valid for responder RawEth and UD only. */ + pseudo_bit_t vlan[0x00002]; /* Valid for RawEth and UD over Ethernet only. Applicable for RawEth and UD over Ethernet Receive queue + 00 - No VLAN header was present in the packet + 01 - C-VLAN (802.1q) Header was present in the frame. + 10 - S-VLAN (802.1ad) Header was present in the frame. */ + pseudo_bit_t dife[0x00001]; /* DIF Error */ +/* -------------- */ + pseudo_bit_t immediate_rssvalue_invalidatekey[0x00020];/* For a responder CQE, if completed WQE Opcode is Send With Immediate or Write With Immediate, this field contains immediate field of the received message. + For a responder CQE, if completed WQE Opcode is Send With Invalidate, this field contains the R_key that was invalidated. + For a responder CQE of a GSI packet this filed contains the Pkey Index of the packet. + For IPoIB (UD) and RawEth CQEs this field contains the RSS hash function value. + Otherwise, this field is reserved. */ +/* -------------- */ + pseudo_bit_t srq_rqpn[0x00018]; /* For Responder UD QPs, Remote (source) QP number. + For Responder SRC QPs, SRQ number. + Otherwise, this field is reserved. */ + pseudo_bit_t ml_path_mac_index[0x00007];/* For responder UD over IB CQE: These are the lower LMC bits of the DLID in an incoming UD packet, higher bits of this field, that are not part of the LMC bits are zeroed by HW. Invalid if incoming message DLID is the permissive LID or incoming message is multicast. + For responder UD over Ethernet and RawEth CQEs: Index of the MAC Table entry that the packet DMAC was matched against. + Otherwise, this field is reserved. */ + pseudo_bit_t g[0x00001]; /* For responder UD over IB CQE this bit indicates the presence of a GRH + For responder UD over Ethernet CQE this bit is set if IPv6 L3 header was present in the packet, this bit is cleared if IPv4 L3 Header was present in the packet. + Otherwise, this field is reserved. */ +/* -------------- */ + pseudo_bit_t slid_smac47_32[0x00010];/* For responder UD over IB CQE it is the source LID of the packet. + For responder UD over Ethernet and RawEth CQEs it is the source-MAC[47:32] of the packet. + Otherwise, this field is reserved. */ + pseudo_bit_t vid[0x0000c]; /* Frame VID, valid for Responder Raw Ethernet and UD over Ethernet QP. Otherwise, this field is reserved. */ + pseudo_bit_t sl[0x00004]; /* For responder UD over IB - the Service Level of the packet. + For responder UD over Ethernet and RawEth - it is VLAN-header[15:12] + Otherwise, this field is reserved. */ +/* -------------- */ + pseudo_bit_t smac31_0_rawether_ipoib_status[0x00020];/* For responder UD over Ethernet - source MAC[31:0] of the packet. + For responder RawEth and UD over IB - RawEth-IPoIB status {3 reserved, ipok,udp,tcp,ipv4opt,ipv6,ipv4vf,ipv4,rht(6),ipv6extmask(6),reserved(2),l2am,reserved(2),bfcs,reserved(2),enc} + Otherwise, this field is reserved. */ +/* -------------- */ + pseudo_bit_t byte_cnt[0x00020]; /* Byte count of data transferred. Applicable for RDMA-read, Atomic and all receive operations. completions. + For Receive Queue that is subject for headers. separation, byte_cnt[31:24] specify number of bytes scattered to the first scatter entry (headers. length). Byte_cnt[23:0] specify total byte count received (including headers). */ +/* -------------- */ + pseudo_bit_t checksum[0x00010]; /* Valid for RawEth and IPoIB only. */ + pseudo_bit_t wqe_counter[0x00010]; +/* -------------- */ + pseudo_bit_t opcode[0x00005]; /* Send completions - same encoding as WQE. + Error coding is 0x1F + Receive: + 0x0 - RDMA-Write with Immediate + 0x1 - Send + 0x2 - Send with Immediate + 0x3 - Send & Invalidate + */ + pseudo_bit_t is[0x00001]; /* inline scatter */ + pseudo_bit_t s_r[0x00001]; /* send 1 / receive 0 */ + pseudo_bit_t owner[0x00001]; /* HW Flips this bit for every CQ warp around. Initialized to Zero. */ + pseudo_bit_t reserved1[0x00010]; + pseudo_bit_t reserved2[0x00008]; +/* -------------- */ +}; + +/* */ + +struct hermonprm_mcg_qps_st { /* Little Endian */ + struct hermonprm_mcg_qp_dw_st dw[128]; +/* -------------- */ +}; + +/* */ + +struct hermonprm_mcg_hdr_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00006]; + pseudo_bit_t next_mcg[0x0001a]; +/* -------------- */ + pseudo_bit_t members_count[0x00018]; + pseudo_bit_t reserved1[0x00008]; +/* -------------- */ + pseudo_bit_t reserved2[0x00020]; +/* -------------- */ + pseudo_bit_t reserved3[0x00020]; +/* -------------- */ + pseudo_bit_t gid3[0x00020]; +/* -------------- */ + pseudo_bit_t gid2[0x00020]; +/* -------------- */ + pseudo_bit_t gid1[0x00020]; +/* -------------- */ + pseudo_bit_t gid0[0x00020]; +/* -------------- */ +}; + +/* */ + +struct hermonprm_sched_queue_context_st { /* Little Endian */ + pseudo_bit_t policy[0x00003]; /* Schedule Queue Policy - 0 - LLSQ, 1 - GBSQ, 2 - BESQ */ + pseudo_bit_t vl15[0x00001]; + pseudo_bit_t sl[0x00004]; /* SL this Schedule Queue is associated with (if vl15 bit is 0) */ + pseudo_bit_t port[0x00002]; /* Port this Schedule Queue is associated with */ + pseudo_bit_t reserved0[0x00006]; + pseudo_bit_t weight[0x00010]; /* Weight of this SchQ */ +/* -------------- */ +}; + +/* */ + +struct hermonprm_ecc_detect_event_data_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00080]; +/* -------------- */ + pseudo_bit_t cause_lsb[0x00001]; + pseudo_bit_t reserved1[0x00002]; + pseudo_bit_t cause_msb[0x00001]; + pseudo_bit_t reserved2[0x00002]; + pseudo_bit_t err_rmw[0x00001]; + pseudo_bit_t err_src_id[0x00003]; + pseudo_bit_t err_da[0x00002]; + pseudo_bit_t err_ba[0x00002]; + pseudo_bit_t reserved3[0x00011]; + pseudo_bit_t overflow[0x00001]; +/* -------------- */ + pseudo_bit_t err_ra[0x00010]; + pseudo_bit_t err_ca[0x00010]; +/* -------------- */ +}; + +/* Event_data Field - ECC Detection Event */ + +struct hermonprm_scrubbing_event_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00080]; +/* -------------- */ + pseudo_bit_t cause_lsb[0x00001]; /* data integrity error cause: + single ECC error in the 64bit lsb data, on the rise edge of the clock */ + pseudo_bit_t reserved1[0x00002]; + pseudo_bit_t cause_msb[0x00001]; /* data integrity error cause: + single ECC error in the 64bit msb data, on the fall edge of the clock */ + pseudo_bit_t reserved2[0x00002]; + pseudo_bit_t err_rmw[0x00001]; /* transaction type: + 0 - read + 1 - read/modify/write */ + pseudo_bit_t err_src_id[0x00003]; /* source of the transaction: 0x4 - PCI, other - internal or IB */ + pseudo_bit_t err_da[0x00002]; /* Error DIMM address */ + pseudo_bit_t err_ba[0x00002]; /* Error bank address */ + pseudo_bit_t reserved3[0x00011]; + pseudo_bit_t overflow[0x00001]; /* Fatal: ECC error FIFO overflow - ECC errors were detected, which may or may not have been corrected by InfiniHost-III-EX */ +/* -------------- */ + pseudo_bit_t err_ra[0x00010]; /* Error row address */ + pseudo_bit_t err_ca[0x00010]; /* Error column address */ +/* -------------- */ +}; + +/* */ + +struct hermonprm_eq_cmd_doorbell_st { /* Little Endian */ + pseudo_bit_t reserved0[0x00020]; +/* -------------- */ +}; + +/* 0 */ + +struct hermonprm_hermon_prm_st { /* Little Endian */ + struct hermonprm_completion_queue_entry_st completion_queue_entry;/* Completion Queue Entry Format */ +/* -------------- */ + pseudo_bit_t reserved0[0x7ff00]; +/* -------------- */ + struct hermonprm_qp_ee_state_transitions_st qp_ee_state_transitions;/* QP/EE State Transitions Command Parameters */ +/* -------------- */ + pseudo_bit_t reserved1[0x7f000]; +/* -------------- */ + struct hermonprm_event_queue_entry_st event_queue_entry;/* Event Queue Entry */ +/* -------------- */ + pseudo_bit_t reserved2[0x7ff00]; +/* -------------- */ + struct hermonprm_completion_event_st completion_event;/* Event_data Field - Completion Event */ +/* -------------- */ + pseudo_bit_t reserved3[0x7ff40]; +/* -------------- */ + struct hermonprm_completion_queue_error_st completion_queue_error;/* Event_data Field - Completion Queue Error */ +/* -------------- */ + pseudo_bit_t reserved4[0x7ff40]; +/* -------------- */ + struct hermonprm_port_state_change_st port_state_change;/* Event_data Field - Port State Change */ +/* -------------- */ + pseudo_bit_t reserved5[0x7ff40]; +/* -------------- */ + struct hermonprm_wqe_segment_st wqe_segment;/* WQE segments format */ +/* -------------- */ + pseudo_bit_t reserved6[0x7f000]; +/* -------------- */ + struct hermonprm_page_fault_event_data_st page_fault_event_data;/* Event_data Field - Page Faults */ +/* -------------- */ + pseudo_bit_t reserved7[0x7ff40]; +/* -------------- */ + struct hermonprm_performance_monitor_event_st performance_monitor_event;/* Event Data Field - Performance Monitor */ +/* -------------- */ + pseudo_bit_t reserved8[0xfff20]; +/* -------------- */ + struct hermonprm_mt25208_type0_st mt25208_type0;/* InfiniHost-III-EX Type0 Configuration Header */ +/* -------------- */ + pseudo_bit_t reserved9[0x7f000]; +/* -------------- */ + struct hermonprm_qp_ee_event_st qp_ee_event;/* Event_data Field - QP/EE Events */ +/* -------------- */ + pseudo_bit_t reserved10[0x00040]; +/* -------------- */ + struct hermonprm_gpio_event_data_st gpio_event_data; +/* -------------- */ + pseudo_bit_t reserved11[0x7fe40]; +/* -------------- */ + struct hermonprm_ud_address_vector_st ud_address_vector;/* UD Address Vector */ +/* -------------- */ + pseudo_bit_t reserved12[0x7ff00]; +/* -------------- */ + struct hermonprm_queue_pair_ee_context_entry_st queue_pair_ee_context_entry;/* QP and EE Context Entry */ +/* -------------- */ + pseudo_bit_t reserved13[0x7f840]; +/* -------------- */ + struct hermonprm_address_path_st address_path;/* Address Path */ +/* -------------- */ + pseudo_bit_t reserved14[0x7fea0]; +/* -------------- */ + struct hermonprm_completion_queue_context_st completion_queue_context;/* Completion Queue Context Table Entry */ +/* -------------- */ + pseudo_bit_t reserved15[0x7fe00]; +/* -------------- */ + struct hermonprm_mpt_st mpt; /* Memory Protection Table (MPT) Entry */ +/* -------------- */ + pseudo_bit_t reserved16[0x7fe00]; +/* -------------- */ + struct hermonprm_mtt_st mtt; /* Memory Translation Table (MTT) Entry */ +/* -------------- */ + pseudo_bit_t reserved17[0x7ffc0]; +/* -------------- */ + struct hermonprm_eqc_st eqc; /* Event Queue Context Table Entry */ +/* -------------- */ + pseudo_bit_t reserved18[0x7fe00]; +/* -------------- */ + struct hermonprm_performance_monitors_st performance_monitors;/* Performance Monitors */ +/* -------------- */ + pseudo_bit_t reserved19[0x7ff80]; +/* -------------- */ + struct hermonprm_hca_command_register_st hca_command_register;/* HCA Command Register (HCR) */ +/* -------------- */ + pseudo_bit_t reserved20[0xfff20]; +/* -------------- */ + struct hermonprm_init_hca_st init_hca;/* INIT_HCA & QUERY_HCA Parameters Block */ +/* -------------- */ + pseudo_bit_t reserved21[0x7f000]; +/* -------------- */ + struct hermonprm_qpcbaseaddr_st qpcbaseaddr;/* QPC/EEC/CQC/EQC/RDB Parameters */ +/* -------------- */ + pseudo_bit_t reserved22[0x7fc00]; +/* -------------- */ + struct hermonprm_udavtable_memory_parameters_st udavtable_memory_parameters;/* Memory Access Parameters for UD Address Vector Table */ +/* -------------- */ + pseudo_bit_t reserved23[0x7ffc0]; +/* -------------- */ + struct hermonprm_multicastparam_st multicastparam;/* Multicast Support Parameters */ +/* -------------- */ + pseudo_bit_t reserved24[0x7ff00]; +/* -------------- */ + struct hermonprm_tptparams_st tptparams;/* Translation and Protection Tables Parameters */ +/* -------------- */ + pseudo_bit_t reserved25[0x7ff00]; +/* -------------- */ + pseudo_bit_t reserved26[0x00800]; +/* -------------- */ + pseudo_bit_t reserved27[0x00100]; +/* -------------- */ + pseudo_bit_t reserved28[0x7f700]; +/* -------------- */ + pseudo_bit_t reserved29[0x00100]; +/* -------------- */ + pseudo_bit_t reserved30[0x7ff00]; +/* -------------- */ + struct hermonprm_query_fw_st query_fw;/* QUERY_FW Parameters Block */ +/* -------------- */ + pseudo_bit_t reserved31[0x7f800]; +/* -------------- */ + struct hermonprm_query_adapter_st query_adapter;/* QUERY_ADAPTER Parameters Block */ +/* -------------- */ + pseudo_bit_t reserved32[0x7f800]; +/* -------------- */ + struct hermonprm_query_dev_cap_st query_dev_cap;/* Query Device Limitations */ +/* -------------- */ + pseudo_bit_t reserved33[0x7f800]; +/* -------------- */ + struct hermonprm_uar_params_st uar_params;/* UAR Parameters */ +/* -------------- */ + pseudo_bit_t reserved34[0x7ff00]; +/* -------------- */ + struct hermonprm_init_port_st init_port;/* INIT_PORT Parameters */ +/* -------------- */ + pseudo_bit_t reserved35[0x7f800]; +/* -------------- */ + struct hermonprm_mgm_entry_st mgm_entry;/* Multicast Group Member */ +/* -------------- */ + pseudo_bit_t reserved36[0x7fe00]; +/* -------------- */ + struct hermonprm_set_ib_st set_ib; /* SET_IB Parameters */ +/* -------------- */ + pseudo_bit_t reserved37[0x7fe00]; +/* -------------- */ + struct hermonprm_rd_send_doorbell_st rd_send_doorbell;/* RD-send doorbell */ +/* -------------- */ + pseudo_bit_t reserved38[0x7ff80]; +/* -------------- */ + struct hermonprm_send_doorbell_st send_doorbell;/* Send doorbell */ +/* -------------- */ + pseudo_bit_t reserved39[0x7ffc0]; +/* -------------- */ + struct hermonprm_receive_doorbell_st receive_doorbell;/* Receive doorbell */ +/* -------------- */ + pseudo_bit_t reserved40[0x7ffc0]; +/* -------------- */ + struct hermonprm_cq_cmd_doorbell_st cq_cmd_doorbell;/* CQ Doorbell */ +/* -------------- */ + pseudo_bit_t reserved41[0xfffc0]; +/* -------------- */ + struct hermonprm_uar_st uar; /* User Access Region */ +/* -------------- */ + pseudo_bit_t reserved42[0x7c000]; +/* -------------- */ + struct hermonprm_mgmqp_st mgmqp; /* Multicast Group Member QP */ +/* -------------- */ + pseudo_bit_t reserved43[0x7ffe0]; +/* -------------- */ + struct hermonprm_query_debug_msg_st query_debug_msg;/* Query Debug Message */ +/* -------------- */ + pseudo_bit_t reserved44[0x7f800]; +/* -------------- */ + struct hermonprm_mad_ifc_st mad_ifc; /* MAD_IFC Input Mailbox */ +/* -------------- */ + pseudo_bit_t reserved45[0x00900]; +/* -------------- */ + struct hermonprm_mad_ifc_input_modifier_st mad_ifc_input_modifier;/* MAD_IFC Input Modifier */ +/* -------------- */ + pseudo_bit_t reserved46[0x7e6e0]; +/* -------------- */ + struct hermonprm_resize_cq_st resize_cq;/* Resize CQ Input Mailbox */ +/* -------------- */ + pseudo_bit_t reserved47[0x7fe00]; +/* -------------- */ + struct hermonprm_completion_with_error_st completion_with_error;/* Completion with Error CQE */ +/* -------------- */ + pseudo_bit_t reserved48[0x7ff00]; +/* -------------- */ + struct hermonprm_hcr_completion_event_st hcr_completion_event;/* Event_data Field - HCR Completion Event */ +/* -------------- */ + pseudo_bit_t reserved49[0x7ff40]; +/* -------------- */ + struct hermonprm_transport_and_ci_error_counters_st transport_and_ci_error_counters;/* Transport and CI Error Counters */ +/* -------------- */ + pseudo_bit_t reserved50[0x7f000]; +/* -------------- */ + struct hermonprm_performance_counters_st performance_counters;/* Performance Counters */ +/* -------------- */ + pseudo_bit_t reserved51[0x9ff800]; +/* -------------- */ + struct hermonprm_fast_registration_segment_st fast_registration_segment;/* Fast Registration Segment */ +/* -------------- */ + pseudo_bit_t reserved52[0x7ff00]; +/* -------------- */ + struct hermonprm_pbl_st pbl; /* Physical Buffer List */ +/* -------------- */ + pseudo_bit_t reserved53[0x7ff00]; +/* -------------- */ + struct hermonprm_srq_context_st srq_context;/* SRQ Context */ +/* -------------- */ + pseudo_bit_t reserved54[0x7fe80]; +/* -------------- */ + struct hermonprm_mod_stat_cfg_st mod_stat_cfg;/* MOD_STAT_CFG */ +/* -------------- */ + pseudo_bit_t reserved55[0x7f800]; +/* -------------- */ + struct hermonprm_virtual_physical_mapping_st virtual_physical_mapping;/* Virtual and Physical Mapping */ +/* -------------- */ + pseudo_bit_t reserved56[0x7ff80]; +/* -------------- */ + struct hermonprm_cq_ci_db_record_st cq_ci_db_record;/* CQ_CI_DB_Record */ +/* -------------- */ + pseudo_bit_t reserved57[0x7ffc0]; +/* -------------- */ + struct hermonprm_cq_arm_db_record_st cq_arm_db_record;/* CQ_ARM_DB_Record */ +/* -------------- */ + pseudo_bit_t reserved58[0x7ffc0]; +/* -------------- */ + struct hermonprm_qp_db_record_st qp_db_record;/* QP_DB_Record */ +/* -------------- */ + pseudo_bit_t reserved59[0x00020]; +/* -------------- */ + pseudo_bit_t reserved60[0x1fffc0]; +/* -------------- */ + struct hermonprm_configuration_registers_st configuration_registers;/* InfiniHost III EX Configuration Registers */ +/* -------------- */ + struct hermonprm_eq_set_ci_table_st eq_set_ci_table;/* EQ Set CI DBs Table */ +/* -------------- */ + pseudo_bit_t reserved61[0x01000]; +/* -------------- */ + pseudo_bit_t reserved62[0x00040]; +/* -------------- */ + pseudo_bit_t reserved63[0x00fc0]; +/* -------------- */ + struct hermonprm_clr_int_st clr_int; /* Clear Interrupt Register */ +/* -------------- */ + pseudo_bit_t reserved64[0xffcfc0]; +/* -------------- */ +}; +#endif /* H_prefix_hermonprm_bits_fixnames_MT25408_PRM_csp_H */ diff --git a/gpxe/src/drivers/infiniband/arbel.c b/gpxe/src/drivers/infiniband/arbel.c new file mode 100644 index 0000000..1756a6e --- /dev/null +++ b/gpxe/src/drivers/infiniband/arbel.c @@ -0,0 +1,2253 @@ +/* + * Copyright (C) 2007 Michael Brown . + * + * Based in part upon the original driver by Mellanox Technologies + * Ltd. Portions may be Copyright (c) Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "arbel.h" + +/** + * @file + * + * Mellanox Arbel Infiniband HCA + * + */ + +/*************************************************************************** + * + * Queue number allocation + * + *************************************************************************** + */ + +/** + * Allocate queue number + * + * @v q_inuse Queue usage bitmask + * @v max_inuse Maximum number of in-use queues + * @ret qn_offset Free queue number offset, or negative error + */ +static int arbel_alloc_qn_offset ( arbel_bitmask_t *q_inuse, + unsigned int max_inuse ) { + unsigned int qn_offset = 0; + arbel_bitmask_t mask = 1; + + while ( qn_offset < max_inuse ) { + if ( ( mask & *q_inuse ) == 0 ) { + *q_inuse |= mask; + return qn_offset; + } + qn_offset++; + mask <<= 1; + if ( ! mask ) { + mask = 1; + q_inuse++; + } + } + return -ENFILE; +} + +/** + * Free queue number + * + * @v q_inuse Queue usage bitmask + * @v qn_offset Queue number offset + */ +static void arbel_free_qn_offset ( arbel_bitmask_t *q_inuse, int qn_offset ) { + arbel_bitmask_t mask; + + mask = ( 1 << ( qn_offset % ( 8 * sizeof ( mask ) ) ) ); + q_inuse += ( qn_offset / ( 8 * sizeof ( mask ) ) ); + *q_inuse &= ~mask; +} + +/*************************************************************************** + * + * HCA commands + * + *************************************************************************** + */ + +/** + * Wait for Arbel command completion + * + * @v arbel Arbel device + * @ret rc Return status code + */ +static int arbel_cmd_wait ( struct arbel *arbel, + struct arbelprm_hca_command_register *hcr ) { + unsigned int wait; + + for ( wait = ARBEL_HCR_MAX_WAIT_MS ; wait ; wait-- ) { + hcr->u.dwords[6] = + readl ( arbel->config + ARBEL_HCR_REG ( 6 ) ); + if ( MLX_GET ( hcr, go ) == 0 ) + return 0; + mdelay ( 1 ); + } + return -EBUSY; +} + +/** + * Issue HCA command + * + * @v arbel Arbel device + * @v command Command opcode, flags and input/output lengths + * @v op_mod Opcode modifier (0 if no modifier applicable) + * @v in Input parameters + * @v in_mod Input modifier (0 if no modifier applicable) + * @v out Output parameters + * @ret rc Return status code + */ +static int arbel_cmd ( struct arbel *arbel, unsigned long command, + unsigned int op_mod, const void *in, + unsigned int in_mod, void *out ) { + struct arbelprm_hca_command_register hcr; + unsigned int opcode = ARBEL_HCR_OPCODE ( command ); + size_t in_len = ARBEL_HCR_IN_LEN ( command ); + size_t out_len = ARBEL_HCR_OUT_LEN ( command ); + void *in_buffer; + void *out_buffer; + unsigned int status; + unsigned int i; + int rc; + + assert ( in_len <= ARBEL_MBOX_SIZE ); + assert ( out_len <= ARBEL_MBOX_SIZE ); + + DBGC2 ( arbel, "Arbel %p command %02x in %zx%s out %zx%s\n", + arbel, opcode, in_len, + ( ( command & ARBEL_HCR_IN_MBOX ) ? "(mbox)" : "" ), out_len, + ( ( command & ARBEL_HCR_OUT_MBOX ) ? "(mbox)" : "" ) ); + + /* Check that HCR is free */ + if ( ( rc = arbel_cmd_wait ( arbel, &hcr ) ) != 0 ) { + DBGC ( arbel, "Arbel %p command interface locked\n", arbel ); + return rc; + } + + /* Prepare HCR */ + memset ( &hcr, 0, sizeof ( hcr ) ); + in_buffer = &hcr.u.dwords[0]; + if ( in_len && ( command & ARBEL_HCR_IN_MBOX ) ) { + in_buffer = arbel->mailbox_in; + MLX_FILL_1 ( &hcr, 1, in_param_l, virt_to_bus ( in_buffer ) ); + } + memcpy ( in_buffer, in, in_len ); + MLX_FILL_1 ( &hcr, 2, input_modifier, in_mod ); + out_buffer = &hcr.u.dwords[3]; + if ( out_len && ( command & ARBEL_HCR_OUT_MBOX ) ) { + out_buffer = arbel->mailbox_out; + MLX_FILL_1 ( &hcr, 4, out_param_l, + virt_to_bus ( out_buffer ) ); + } + MLX_FILL_3 ( &hcr, 6, + opcode, opcode, + opcode_modifier, op_mod, + go, 1 ); + DBGC2_HD ( arbel, &hcr, sizeof ( hcr ) ); + if ( in_len ) { + DBGC2 ( arbel, "Input:\n" ); + DBGC2_HD ( arbel, in, ( ( in_len < 512 ) ? in_len : 512 ) ); + } + + /* Issue command */ + for ( i = 0 ; i < ( sizeof ( hcr ) / sizeof ( hcr.u.dwords[0] ) ) ; + i++ ) { + writel ( hcr.u.dwords[i], + arbel->config + ARBEL_HCR_REG ( i ) ); + barrier(); + } + + /* Wait for command completion */ + if ( ( rc = arbel_cmd_wait ( arbel, &hcr ) ) != 0 ) { + DBGC ( arbel, "Arbel %p timed out waiting for command:\n", + arbel ); + DBGC_HD ( arbel, &hcr, sizeof ( hcr ) ); + return rc; + } + + /* Check command status */ + status = MLX_GET ( &hcr, status ); + if ( status != 0 ) { + DBGC ( arbel, "Arbel %p command failed with status %02x:\n", + arbel, status ); + DBGC_HD ( arbel, &hcr, sizeof ( hcr ) ); + return -EIO; + } + + /* Read output parameters, if any */ + hcr.u.dwords[3] = readl ( arbel->config + ARBEL_HCR_REG ( 3 ) ); + hcr.u.dwords[4] = readl ( arbel->config + ARBEL_HCR_REG ( 4 ) ); + memcpy ( out, out_buffer, out_len ); + if ( out_len ) { + DBGC2 ( arbel, "Output:\n" ); + DBGC2_HD ( arbel, out, ( ( out_len < 512 ) ? out_len : 512 ) ); + } + + return 0; +} + +static inline int +arbel_cmd_query_dev_lim ( struct arbel *arbel, + struct arbelprm_query_dev_lim *dev_lim ) { + return arbel_cmd ( arbel, + ARBEL_HCR_OUT_CMD ( ARBEL_HCR_QUERY_DEV_LIM, + 1, sizeof ( *dev_lim ) ), + 0, NULL, 0, dev_lim ); +} + +static inline int +arbel_cmd_query_fw ( struct arbel *arbel, struct arbelprm_query_fw *fw ) { + return arbel_cmd ( arbel, + ARBEL_HCR_OUT_CMD ( ARBEL_HCR_QUERY_FW, + 1, sizeof ( *fw ) ), + 0, NULL, 0, fw ); +} + +static inline int +arbel_cmd_init_hca ( struct arbel *arbel, + const struct arbelprm_init_hca *init_hca ) { + return arbel_cmd ( arbel, + ARBEL_HCR_IN_CMD ( ARBEL_HCR_INIT_HCA, + 1, sizeof ( *init_hca ) ), + 0, init_hca, 0, NULL ); +} + +static inline int +arbel_cmd_close_hca ( struct arbel *arbel ) { + return arbel_cmd ( arbel, + ARBEL_HCR_VOID_CMD ( ARBEL_HCR_CLOSE_HCA ), + 0, NULL, 0, NULL ); +} + +static inline int +arbel_cmd_init_ib ( struct arbel *arbel, unsigned int port, + const struct arbelprm_init_ib *init_ib ) { + return arbel_cmd ( arbel, + ARBEL_HCR_IN_CMD ( ARBEL_HCR_INIT_IB, + 1, sizeof ( *init_ib ) ), + 0, init_ib, port, NULL ); +} + +static inline int +arbel_cmd_close_ib ( struct arbel *arbel, unsigned int port ) { + return arbel_cmd ( arbel, + ARBEL_HCR_VOID_CMD ( ARBEL_HCR_CLOSE_IB ), + 0, NULL, port, NULL ); +} + +static inline int +arbel_cmd_sw2hw_mpt ( struct arbel *arbel, unsigned int index, + const struct arbelprm_mpt *mpt ) { + return arbel_cmd ( arbel, + ARBEL_HCR_IN_CMD ( ARBEL_HCR_SW2HW_MPT, + 1, sizeof ( *mpt ) ), + 0, mpt, index, NULL ); +} + +static inline int +arbel_cmd_map_eq ( struct arbel *arbel, unsigned long index_map, + const struct arbelprm_event_mask *mask ) { + return arbel_cmd ( arbel, + ARBEL_HCR_IN_CMD ( ARBEL_HCR_MAP_EQ, + 0, sizeof ( *mask ) ), + 0, mask, index_map, NULL ); +} + +static inline int +arbel_cmd_sw2hw_eq ( struct arbel *arbel, unsigned int index, + const struct arbelprm_eqc *eqctx ) { + return arbel_cmd ( arbel, + ARBEL_HCR_IN_CMD ( ARBEL_HCR_SW2HW_EQ, + 1, sizeof ( *eqctx ) ), + 0, eqctx, index, NULL ); +} + +static inline int +arbel_cmd_hw2sw_eq ( struct arbel *arbel, unsigned int index, + struct arbelprm_eqc *eqctx ) { + return arbel_cmd ( arbel, + ARBEL_HCR_OUT_CMD ( ARBEL_HCR_HW2SW_EQ, + 1, sizeof ( *eqctx ) ), + 1, NULL, index, eqctx ); +} + +static inline int +arbel_cmd_sw2hw_cq ( struct arbel *arbel, unsigned long cqn, + const struct arbelprm_completion_queue_context *cqctx ) { + return arbel_cmd ( arbel, + ARBEL_HCR_IN_CMD ( ARBEL_HCR_SW2HW_CQ, + 1, sizeof ( *cqctx ) ), + 0, cqctx, cqn, NULL ); +} + +static inline int +arbel_cmd_hw2sw_cq ( struct arbel *arbel, unsigned long cqn, + struct arbelprm_completion_queue_context *cqctx) { + return arbel_cmd ( arbel, + ARBEL_HCR_OUT_CMD ( ARBEL_HCR_HW2SW_CQ, + 1, sizeof ( *cqctx ) ), + 0, NULL, cqn, cqctx ); +} + +static inline int +arbel_cmd_rst2init_qpee ( struct arbel *arbel, unsigned long qpn, + const struct arbelprm_qp_ee_state_transitions *ctx ){ + return arbel_cmd ( arbel, + ARBEL_HCR_IN_CMD ( ARBEL_HCR_RST2INIT_QPEE, + 1, sizeof ( *ctx ) ), + 0, ctx, qpn, NULL ); +} + +static inline int +arbel_cmd_init2rtr_qpee ( struct arbel *arbel, unsigned long qpn, + const struct arbelprm_qp_ee_state_transitions *ctx ){ + return arbel_cmd ( arbel, + ARBEL_HCR_IN_CMD ( ARBEL_HCR_INIT2RTR_QPEE, + 1, sizeof ( *ctx ) ), + 0, ctx, qpn, NULL ); +} + +static inline int +arbel_cmd_rtr2rts_qpee ( struct arbel *arbel, unsigned long qpn, + const struct arbelprm_qp_ee_state_transitions *ctx ) { + return arbel_cmd ( arbel, + ARBEL_HCR_IN_CMD ( ARBEL_HCR_RTR2RTS_QPEE, + 1, sizeof ( *ctx ) ), + 0, ctx, qpn, NULL ); +} + +static inline int +arbel_cmd_rts2rts_qp ( struct arbel *arbel, unsigned long qpn, + const struct arbelprm_qp_ee_state_transitions *ctx ) { + return arbel_cmd ( arbel, + ARBEL_HCR_IN_CMD ( ARBEL_HCR_RTS2RTS_QPEE, + 1, sizeof ( *ctx ) ), + 0, ctx, qpn, NULL ); +} + +static inline int +arbel_cmd_2rst_qpee ( struct arbel *arbel, unsigned long qpn ) { + return arbel_cmd ( arbel, + ARBEL_HCR_VOID_CMD ( ARBEL_HCR_2RST_QPEE ), + 0x03, NULL, qpn, NULL ); +} + +static inline int +arbel_cmd_mad_ifc ( struct arbel *arbel, unsigned int port, + union arbelprm_mad *mad ) { + return arbel_cmd ( arbel, + ARBEL_HCR_INOUT_CMD ( ARBEL_HCR_MAD_IFC, + 1, sizeof ( *mad ), + 1, sizeof ( *mad ) ), + 0x03, mad, port, mad ); +} + +static inline int +arbel_cmd_read_mgm ( struct arbel *arbel, unsigned int index, + struct arbelprm_mgm_entry *mgm ) { + return arbel_cmd ( arbel, + ARBEL_HCR_OUT_CMD ( ARBEL_HCR_READ_MGM, + 1, sizeof ( *mgm ) ), + 0, NULL, index, mgm ); +} + +static inline int +arbel_cmd_write_mgm ( struct arbel *arbel, unsigned int index, + const struct arbelprm_mgm_entry *mgm ) { + return arbel_cmd ( arbel, + ARBEL_HCR_IN_CMD ( ARBEL_HCR_WRITE_MGM, + 1, sizeof ( *mgm ) ), + 0, mgm, index, NULL ); +} + +static inline int +arbel_cmd_mgid_hash ( struct arbel *arbel, const struct ib_gid *gid, + struct arbelprm_mgm_hash *hash ) { + return arbel_cmd ( arbel, + ARBEL_HCR_INOUT_CMD ( ARBEL_HCR_MGID_HASH, + 1, sizeof ( *gid ), + 0, sizeof ( *hash ) ), + 0, gid, 0, hash ); +} + +static inline int +arbel_cmd_run_fw ( struct arbel *arbel ) { + return arbel_cmd ( arbel, + ARBEL_HCR_VOID_CMD ( ARBEL_HCR_RUN_FW ), + 0, NULL, 0, NULL ); +} + +static inline int +arbel_cmd_disable_lam ( struct arbel *arbel ) { + return arbel_cmd ( arbel, + ARBEL_HCR_VOID_CMD ( ARBEL_HCR_DISABLE_LAM ), + 0, NULL, 0, NULL ); +} + +static inline int +arbel_cmd_enable_lam ( struct arbel *arbel, struct arbelprm_access_lam *lam ) { + return arbel_cmd ( arbel, + ARBEL_HCR_OUT_CMD ( ARBEL_HCR_ENABLE_LAM, + 1, sizeof ( *lam ) ), + 1, NULL, 0, lam ); +} + +static inline int +arbel_cmd_unmap_icm ( struct arbel *arbel, unsigned int page_count ) { + return arbel_cmd ( arbel, + ARBEL_HCR_VOID_CMD ( ARBEL_HCR_UNMAP_ICM ), + 0, NULL, page_count, NULL ); +} + +static inline int +arbel_cmd_map_icm ( struct arbel *arbel, + const struct arbelprm_virtual_physical_mapping *map ) { + return arbel_cmd ( arbel, + ARBEL_HCR_IN_CMD ( ARBEL_HCR_MAP_ICM, + 1, sizeof ( *map ) ), + 0, map, 1, NULL ); +} + +static inline int +arbel_cmd_unmap_icm_aux ( struct arbel *arbel ) { + return arbel_cmd ( arbel, + ARBEL_HCR_VOID_CMD ( ARBEL_HCR_UNMAP_ICM_AUX ), + 0, NULL, 0, NULL ); +} + +static inline int +arbel_cmd_map_icm_aux ( struct arbel *arbel, + const struct arbelprm_virtual_physical_mapping *map ) { + return arbel_cmd ( arbel, + ARBEL_HCR_IN_CMD ( ARBEL_HCR_MAP_ICM_AUX, + 1, sizeof ( *map ) ), + 0, map, 1, NULL ); +} + +static inline int +arbel_cmd_set_icm_size ( struct arbel *arbel, + const struct arbelprm_scalar_parameter *icm_size, + struct arbelprm_scalar_parameter *icm_aux_size ) { + return arbel_cmd ( arbel, + ARBEL_HCR_INOUT_CMD ( ARBEL_HCR_SET_ICM_SIZE, + 0, sizeof ( *icm_size ), + 0, sizeof ( *icm_aux_size ) ), + 0, icm_size, 0, icm_aux_size ); +} + +static inline int +arbel_cmd_unmap_fa ( struct arbel *arbel ) { + return arbel_cmd ( arbel, + ARBEL_HCR_VOID_CMD ( ARBEL_HCR_UNMAP_FA ), + 0, NULL, 0, NULL ); +} + +static inline int +arbel_cmd_map_fa ( struct arbel *arbel, + const struct arbelprm_virtual_physical_mapping *map ) { + return arbel_cmd ( arbel, + ARBEL_HCR_IN_CMD ( ARBEL_HCR_MAP_FA, + 1, sizeof ( *map ) ), + 0, map, 1, NULL ); +} + +/*************************************************************************** + * + * MAD operations + * + *************************************************************************** + */ + +/** + * Issue management datagram + * + * @v ibdev Infiniband device + * @v mad Management datagram + * @ret rc Return status code + */ +static int arbel_mad ( struct ib_device *ibdev, union ib_mad *mad ) { + struct arbel *arbel = ib_get_drvdata ( ibdev ); + union arbelprm_mad mad_ifc; + int rc; + + linker_assert ( sizeof ( *mad ) == sizeof ( mad_ifc.mad ), + mad_size_mismatch ); + + /* Copy in request packet */ + memcpy ( &mad_ifc.mad, mad, sizeof ( mad_ifc.mad ) ); + + /* Issue MAD */ + if ( ( rc = arbel_cmd_mad_ifc ( arbel, ibdev->port, + &mad_ifc ) ) != 0 ) { + DBGC ( arbel, "Arbel %p could not issue MAD IFC: %s\n", + arbel, strerror ( rc ) ); + return rc; + } + + /* Copy out reply packet */ + memcpy ( mad, &mad_ifc.mad, sizeof ( *mad ) ); + + if ( mad->hdr.status != 0 ) { + DBGC ( arbel, "Arbel %p MAD IFC status %04x\n", + arbel, ntohs ( mad->hdr.status ) ); + return -EIO; + } + return 0; +} + +/*************************************************************************** + * + * Completion queue operations + * + *************************************************************************** + */ + +/** + * Create completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + * @ret rc Return status code + */ +static int arbel_create_cq ( struct ib_device *ibdev, + struct ib_completion_queue *cq ) { + struct arbel *arbel = ib_get_drvdata ( ibdev ); + struct arbel_completion_queue *arbel_cq; + struct arbelprm_completion_queue_context cqctx; + struct arbelprm_cq_ci_db_record *ci_db_rec; + struct arbelprm_cq_arm_db_record *arm_db_rec; + int cqn_offset; + unsigned int i; + int rc; + + /* Find a free completion queue number */ + cqn_offset = arbel_alloc_qn_offset ( arbel->cq_inuse, ARBEL_MAX_CQS ); + if ( cqn_offset < 0 ) { + DBGC ( arbel, "Arbel %p out of completion queues\n", arbel ); + rc = cqn_offset; + goto err_cqn_offset; + } + cq->cqn = ( arbel->limits.reserved_cqs + cqn_offset ); + + /* Allocate control structures */ + arbel_cq = zalloc ( sizeof ( *arbel_cq ) ); + if ( ! arbel_cq ) { + rc = -ENOMEM; + goto err_arbel_cq; + } + arbel_cq->ci_doorbell_idx = arbel_cq_ci_doorbell_idx ( cqn_offset ); + arbel_cq->arm_doorbell_idx = arbel_cq_arm_doorbell_idx ( cqn_offset ); + + /* Allocate completion queue itself */ + arbel_cq->cqe_size = ( cq->num_cqes * sizeof ( arbel_cq->cqe[0] ) ); + arbel_cq->cqe = malloc_dma ( arbel_cq->cqe_size, + sizeof ( arbel_cq->cqe[0] ) ); + if ( ! arbel_cq->cqe ) { + rc = -ENOMEM; + goto err_cqe; + } + memset ( arbel_cq->cqe, 0, arbel_cq->cqe_size ); + for ( i = 0 ; i < cq->num_cqes ; i++ ) { + MLX_FILL_1 ( &arbel_cq->cqe[i].normal, 7, owner, 1 ); + } + barrier(); + + /* Initialise doorbell records */ + ci_db_rec = &arbel->db_rec[arbel_cq->ci_doorbell_idx].cq_ci; + MLX_FILL_1 ( ci_db_rec, 0, counter, 0 ); + MLX_FILL_2 ( ci_db_rec, 1, + res, ARBEL_UAR_RES_CQ_CI, + cq_number, cq->cqn ); + arm_db_rec = &arbel->db_rec[arbel_cq->arm_doorbell_idx].cq_arm; + MLX_FILL_1 ( arm_db_rec, 0, counter, 0 ); + MLX_FILL_2 ( arm_db_rec, 1, + res, ARBEL_UAR_RES_CQ_ARM, + cq_number, cq->cqn ); + + /* Hand queue over to hardware */ + memset ( &cqctx, 0, sizeof ( cqctx ) ); + MLX_FILL_1 ( &cqctx, 0, st, 0xa /* "Event fired" */ ); + MLX_FILL_1 ( &cqctx, 2, start_address_l, + virt_to_bus ( arbel_cq->cqe ) ); + MLX_FILL_2 ( &cqctx, 3, + usr_page, arbel->limits.reserved_uars, + log_cq_size, fls ( cq->num_cqes - 1 ) ); + MLX_FILL_1 ( &cqctx, 5, c_eqn, ARBEL_NO_EQ ); + MLX_FILL_1 ( &cqctx, 6, pd, ARBEL_GLOBAL_PD ); + MLX_FILL_1 ( &cqctx, 7, l_key, arbel->reserved_lkey ); + MLX_FILL_1 ( &cqctx, 12, cqn, cq->cqn ); + MLX_FILL_1 ( &cqctx, 13, + cq_ci_db_record, arbel_cq->ci_doorbell_idx ); + MLX_FILL_1 ( &cqctx, 14, + cq_state_db_record, arbel_cq->arm_doorbell_idx ); + if ( ( rc = arbel_cmd_sw2hw_cq ( arbel, cq->cqn, &cqctx ) ) != 0 ) { + DBGC ( arbel, "Arbel %p SW2HW_CQ failed: %s\n", + arbel, strerror ( rc ) ); + goto err_sw2hw_cq; + } + + DBGC ( arbel, "Arbel %p CQN %#lx ring at [%p,%p)\n", + arbel, cq->cqn, arbel_cq->cqe, + ( ( ( void * ) arbel_cq->cqe ) + arbel_cq->cqe_size ) ); + ib_cq_set_drvdata ( cq, arbel_cq ); + return 0; + + err_sw2hw_cq: + MLX_FILL_1 ( ci_db_rec, 1, res, ARBEL_UAR_RES_NONE ); + MLX_FILL_1 ( arm_db_rec, 1, res, ARBEL_UAR_RES_NONE ); + free_dma ( arbel_cq->cqe, arbel_cq->cqe_size ); + err_cqe: + free ( arbel_cq ); + err_arbel_cq: + arbel_free_qn_offset ( arbel->cq_inuse, cqn_offset ); + err_cqn_offset: + return rc; +} + +/** + * Destroy completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + */ +static void arbel_destroy_cq ( struct ib_device *ibdev, + struct ib_completion_queue *cq ) { + struct arbel *arbel = ib_get_drvdata ( ibdev ); + struct arbel_completion_queue *arbel_cq = ib_cq_get_drvdata ( cq ); + struct arbelprm_completion_queue_context cqctx; + struct arbelprm_cq_ci_db_record *ci_db_rec; + struct arbelprm_cq_arm_db_record *arm_db_rec; + int cqn_offset; + int rc; + + /* Take ownership back from hardware */ + if ( ( rc = arbel_cmd_hw2sw_cq ( arbel, cq->cqn, &cqctx ) ) != 0 ) { + DBGC ( arbel, "Arbel %p FATAL HW2SW_CQ failed on CQN %#lx: " + "%s\n", arbel, cq->cqn, strerror ( rc ) ); + /* Leak memory and return; at least we avoid corruption */ + return; + } + + /* Clear doorbell records */ + ci_db_rec = &arbel->db_rec[arbel_cq->ci_doorbell_idx].cq_ci; + arm_db_rec = &arbel->db_rec[arbel_cq->arm_doorbell_idx].cq_arm; + MLX_FILL_1 ( ci_db_rec, 1, res, ARBEL_UAR_RES_NONE ); + MLX_FILL_1 ( arm_db_rec, 1, res, ARBEL_UAR_RES_NONE ); + + /* Free memory */ + free_dma ( arbel_cq->cqe, arbel_cq->cqe_size ); + free ( arbel_cq ); + + /* Mark queue number as free */ + cqn_offset = ( cq->cqn - arbel->limits.reserved_cqs ); + arbel_free_qn_offset ( arbel->cq_inuse, cqn_offset ); + + ib_cq_set_drvdata ( cq, NULL ); +} + +/*************************************************************************** + * + * Queue pair operations + * + *************************************************************************** + */ + +/** + * Create send work queue + * + * @v arbel_send_wq Send work queue + * @v num_wqes Number of work queue entries + * @ret rc Return status code + */ +static int arbel_create_send_wq ( struct arbel_send_work_queue *arbel_send_wq, + unsigned int num_wqes ) { + struct arbelprm_ud_send_wqe *wqe; + struct arbelprm_ud_send_wqe *next_wqe; + unsigned int wqe_idx_mask; + unsigned int i; + + /* Allocate work queue */ + arbel_send_wq->wqe_size = ( num_wqes * + sizeof ( arbel_send_wq->wqe[0] ) ); + arbel_send_wq->wqe = malloc_dma ( arbel_send_wq->wqe_size, + sizeof ( arbel_send_wq->wqe[0] ) ); + if ( ! arbel_send_wq->wqe ) + return -ENOMEM; + memset ( arbel_send_wq->wqe, 0, arbel_send_wq->wqe_size ); + + /* Link work queue entries */ + wqe_idx_mask = ( num_wqes - 1 ); + for ( i = 0 ; i < num_wqes ; i++ ) { + wqe = &arbel_send_wq->wqe[i].ud; + next_wqe = &arbel_send_wq->wqe[ ( i + 1 ) & wqe_idx_mask ].ud; + MLX_FILL_1 ( &wqe->next, 0, nda_31_6, + ( virt_to_bus ( next_wqe ) >> 6 ) ); + } + + return 0; +} + +/** + * Create receive work queue + * + * @v arbel_recv_wq Receive work queue + * @v num_wqes Number of work queue entries + * @ret rc Return status code + */ +static int arbel_create_recv_wq ( struct arbel_recv_work_queue *arbel_recv_wq, + unsigned int num_wqes ) { + struct arbelprm_recv_wqe *wqe; + struct arbelprm_recv_wqe *next_wqe; + unsigned int wqe_idx_mask; + size_t nds; + unsigned int i; + unsigned int j; + + /* Allocate work queue */ + arbel_recv_wq->wqe_size = ( num_wqes * + sizeof ( arbel_recv_wq->wqe[0] ) ); + arbel_recv_wq->wqe = malloc_dma ( arbel_recv_wq->wqe_size, + sizeof ( arbel_recv_wq->wqe[0] ) ); + if ( ! arbel_recv_wq->wqe ) + return -ENOMEM; + memset ( arbel_recv_wq->wqe, 0, arbel_recv_wq->wqe_size ); + + /* Link work queue entries */ + wqe_idx_mask = ( num_wqes - 1 ); + nds = ( ( offsetof ( typeof ( *wqe ), data ) + + sizeof ( wqe->data[0] ) ) >> 4 ); + for ( i = 0 ; i < num_wqes ; i++ ) { + wqe = &arbel_recv_wq->wqe[i].recv; + next_wqe = &arbel_recv_wq->wqe[( i + 1 ) & wqe_idx_mask].recv; + MLX_FILL_1 ( &wqe->next, 0, nda_31_6, + ( virt_to_bus ( next_wqe ) >> 6 ) ); + MLX_FILL_1 ( &wqe->next, 1, nds, ( sizeof ( *wqe ) / 16 ) ); + for ( j = 0 ; ( ( ( void * ) &wqe->data[j] ) < + ( ( void * ) ( wqe + 1 ) ) ) ; j++ ) { + MLX_FILL_1 ( &wqe->data[j], 1, + l_key, ARBEL_INVALID_LKEY ); + } + } + + return 0; +} + +/** + * Create queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @ret rc Return status code + */ +static int arbel_create_qp ( struct ib_device *ibdev, + struct ib_queue_pair *qp ) { + struct arbel *arbel = ib_get_drvdata ( ibdev ); + struct arbel_queue_pair *arbel_qp; + struct arbelprm_qp_ee_state_transitions qpctx; + struct arbelprm_qp_db_record *send_db_rec; + struct arbelprm_qp_db_record *recv_db_rec; + int qpn_offset; + int rc; + + /* Find a free queue pair number */ + qpn_offset = arbel_alloc_qn_offset ( arbel->qp_inuse, ARBEL_MAX_QPS ); + if ( qpn_offset < 0 ) { + DBGC ( arbel, "Arbel %p out of queue pairs\n", arbel ); + rc = qpn_offset; + goto err_qpn_offset; + } + qp->qpn = ( ARBEL_QPN_BASE + arbel->limits.reserved_qps + qpn_offset ); + + /* Allocate control structures */ + arbel_qp = zalloc ( sizeof ( *arbel_qp ) ); + if ( ! arbel_qp ) { + rc = -ENOMEM; + goto err_arbel_qp; + } + arbel_qp->send.doorbell_idx = arbel_send_doorbell_idx ( qpn_offset ); + arbel_qp->recv.doorbell_idx = arbel_recv_doorbell_idx ( qpn_offset ); + + /* Create send and receive work queues */ + if ( ( rc = arbel_create_send_wq ( &arbel_qp->send, + qp->send.num_wqes ) ) != 0 ) + goto err_create_send_wq; + if ( ( rc = arbel_create_recv_wq ( &arbel_qp->recv, + qp->recv.num_wqes ) ) != 0 ) + goto err_create_recv_wq; + + /* Initialise doorbell records */ + send_db_rec = &arbel->db_rec[arbel_qp->send.doorbell_idx].qp; + MLX_FILL_1 ( send_db_rec, 0, counter, 0 ); + MLX_FILL_2 ( send_db_rec, 1, + res, ARBEL_UAR_RES_SQ, + qp_number, qp->qpn ); + recv_db_rec = &arbel->db_rec[arbel_qp->recv.doorbell_idx].qp; + MLX_FILL_1 ( recv_db_rec, 0, counter, 0 ); + MLX_FILL_2 ( recv_db_rec, 1, + res, ARBEL_UAR_RES_RQ, + qp_number, qp->qpn ); + + /* Hand queue over to hardware */ + memset ( &qpctx, 0, sizeof ( qpctx ) ); + MLX_FILL_3 ( &qpctx, 2, + qpc_eec_data.de, 1, + qpc_eec_data.pm_state, 0x03 /* Always 0x03 for UD */, + qpc_eec_data.st, ARBEL_ST_UD ); + MLX_FILL_6 ( &qpctx, 4, + qpc_eec_data.mtu, ARBEL_MTU_2048, + qpc_eec_data.msg_max, 11 /* 2^11 = 2048 */, + qpc_eec_data.log_rq_size, fls ( qp->recv.num_wqes - 1 ), + qpc_eec_data.log_rq_stride, + ( fls ( sizeof ( arbel_qp->recv.wqe[0] ) - 1 ) - 4 ), + qpc_eec_data.log_sq_size, fls ( qp->send.num_wqes - 1 ), + qpc_eec_data.log_sq_stride, + ( fls ( sizeof ( arbel_qp->send.wqe[0] ) - 1 ) - 4 ) ); + MLX_FILL_1 ( &qpctx, 5, + qpc_eec_data.usr_page, arbel->limits.reserved_uars ); + MLX_FILL_1 ( &qpctx, 10, qpc_eec_data.primary_address_path.port_number, + ibdev->port ); + MLX_FILL_1 ( &qpctx, 27, qpc_eec_data.pd, ARBEL_GLOBAL_PD ); + MLX_FILL_1 ( &qpctx, 29, qpc_eec_data.wqe_lkey, arbel->reserved_lkey ); + MLX_FILL_1 ( &qpctx, 30, qpc_eec_data.ssc, 1 ); + MLX_FILL_1 ( &qpctx, 33, qpc_eec_data.cqn_snd, qp->send.cq->cqn ); + MLX_FILL_1 ( &qpctx, 34, qpc_eec_data.snd_wqe_base_adr_l, + ( virt_to_bus ( arbel_qp->send.wqe ) >> 6 ) ); + MLX_FILL_1 ( &qpctx, 35, qpc_eec_data.snd_db_record_index, + arbel_qp->send.doorbell_idx ); + MLX_FILL_1 ( &qpctx, 38, qpc_eec_data.rsc, 1 ); + MLX_FILL_1 ( &qpctx, 41, qpc_eec_data.cqn_rcv, qp->recv.cq->cqn ); + MLX_FILL_1 ( &qpctx, 42, qpc_eec_data.rcv_wqe_base_adr_l, + ( virt_to_bus ( arbel_qp->recv.wqe ) >> 6 ) ); + MLX_FILL_1 ( &qpctx, 43, qpc_eec_data.rcv_db_record_index, + arbel_qp->recv.doorbell_idx ); + MLX_FILL_1 ( &qpctx, 44, qpc_eec_data.q_key, qp->qkey ); + if ( ( rc = arbel_cmd_rst2init_qpee ( arbel, qp->qpn, &qpctx )) != 0 ){ + DBGC ( arbel, "Arbel %p RST2INIT_QPEE failed: %s\n", + arbel, strerror ( rc ) ); + goto err_rst2init_qpee; + } + memset ( &qpctx, 0, sizeof ( qpctx ) ); + MLX_FILL_2 ( &qpctx, 4, + qpc_eec_data.mtu, ARBEL_MTU_2048, + qpc_eec_data.msg_max, 11 /* 2^11 = 2048 */ ); + if ( ( rc = arbel_cmd_init2rtr_qpee ( arbel, qp->qpn, &qpctx )) != 0 ){ + DBGC ( arbel, "Arbel %p INIT2RTR_QPEE failed: %s\n", + arbel, strerror ( rc ) ); + goto err_init2rtr_qpee; + } + memset ( &qpctx, 0, sizeof ( qpctx ) ); + if ( ( rc = arbel_cmd_rtr2rts_qpee ( arbel, qp->qpn, &qpctx ) ) != 0 ){ + DBGC ( arbel, "Arbel %p RTR2RTS_QPEE failed: %s\n", + arbel, strerror ( rc ) ); + goto err_rtr2rts_qpee; + } + + DBGC ( arbel, "Arbel %p QPN %#lx send ring at [%p,%p)\n", + arbel, qp->qpn, arbel_qp->send.wqe, + ( ( (void *) arbel_qp->send.wqe ) + arbel_qp->send.wqe_size ) ); + DBGC ( arbel, "Arbel %p QPN %#lx receive ring at [%p,%p)\n", + arbel, qp->qpn, arbel_qp->recv.wqe, + ( ( (void *) arbel_qp->recv.wqe ) + arbel_qp->recv.wqe_size ) ); + ib_qp_set_drvdata ( qp, arbel_qp ); + return 0; + + err_rtr2rts_qpee: + err_init2rtr_qpee: + arbel_cmd_2rst_qpee ( arbel, qp->qpn ); + err_rst2init_qpee: + MLX_FILL_1 ( send_db_rec, 1, res, ARBEL_UAR_RES_NONE ); + MLX_FILL_1 ( recv_db_rec, 1, res, ARBEL_UAR_RES_NONE ); + free_dma ( arbel_qp->recv.wqe, arbel_qp->recv.wqe_size ); + err_create_recv_wq: + free_dma ( arbel_qp->send.wqe, arbel_qp->send.wqe_size ); + err_create_send_wq: + free ( arbel_qp ); + err_arbel_qp: + arbel_free_qn_offset ( arbel->qp_inuse, qpn_offset ); + err_qpn_offset: + return rc; +} + +/** + * Modify queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v mod_list Modification list + * @ret rc Return status code + */ +static int arbel_modify_qp ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + unsigned long mod_list ) { + struct arbel *arbel = ib_get_drvdata ( ibdev ); + struct arbelprm_qp_ee_state_transitions qpctx; + unsigned long optparammask = 0; + int rc; + + /* Construct optparammask */ + if ( mod_list & IB_MODIFY_QKEY ) + optparammask |= ARBEL_QPEE_OPT_PARAM_QKEY; + + /* Issue RTS2RTS_QP */ + memset ( &qpctx, 0, sizeof ( qpctx ) ); + MLX_FILL_1 ( &qpctx, 0, opt_param_mask, optparammask ); + MLX_FILL_1 ( &qpctx, 44, qpc_eec_data.q_key, qp->qkey ); + if ( ( rc = arbel_cmd_rts2rts_qp ( arbel, qp->qpn, &qpctx ) ) != 0 ){ + DBGC ( arbel, "Arbel %p RTS2RTS_QP failed: %s\n", + arbel, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Destroy queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + */ +static void arbel_destroy_qp ( struct ib_device *ibdev, + struct ib_queue_pair *qp ) { + struct arbel *arbel = ib_get_drvdata ( ibdev ); + struct arbel_queue_pair *arbel_qp = ib_qp_get_drvdata ( qp ); + struct arbelprm_qp_db_record *send_db_rec; + struct arbelprm_qp_db_record *recv_db_rec; + int qpn_offset; + int rc; + + /* Take ownership back from hardware */ + if ( ( rc = arbel_cmd_2rst_qpee ( arbel, qp->qpn ) ) != 0 ) { + DBGC ( arbel, "Arbel %p FATAL 2RST_QPEE failed on QPN %#lx: " + "%s\n", arbel, qp->qpn, strerror ( rc ) ); + /* Leak memory and return; at least we avoid corruption */ + return; + } + + /* Clear doorbell records */ + send_db_rec = &arbel->db_rec[arbel_qp->send.doorbell_idx].qp; + recv_db_rec = &arbel->db_rec[arbel_qp->recv.doorbell_idx].qp; + MLX_FILL_1 ( send_db_rec, 1, res, ARBEL_UAR_RES_NONE ); + MLX_FILL_1 ( recv_db_rec, 1, res, ARBEL_UAR_RES_NONE ); + + /* Free memory */ + free_dma ( arbel_qp->send.wqe, arbel_qp->send.wqe_size ); + free_dma ( arbel_qp->recv.wqe, arbel_qp->recv.wqe_size ); + free ( arbel_qp ); + + /* Mark queue number as free */ + qpn_offset = ( qp->qpn - ARBEL_QPN_BASE - arbel->limits.reserved_qps ); + arbel_free_qn_offset ( arbel->qp_inuse, qpn_offset ); + + ib_qp_set_drvdata ( qp, NULL ); +} + +/*************************************************************************** + * + * Work request operations + * + *************************************************************************** + */ + +/** + * Ring doorbell register in UAR + * + * @v arbel Arbel device + * @v db_reg Doorbell register structure + * @v offset Address of doorbell + */ +static void arbel_ring_doorbell ( struct arbel *arbel, + union arbelprm_doorbell_register *db_reg, + unsigned int offset ) { + + DBGC2 ( arbel, "Arbel %p ringing doorbell %08x:%08x at %lx\n", + arbel, db_reg->dword[0], db_reg->dword[1], + virt_to_phys ( arbel->uar + offset ) ); + + barrier(); + writel ( db_reg->dword[0], ( arbel->uar + offset + 0 ) ); + barrier(); + writel ( db_reg->dword[1], ( arbel->uar + offset + 4 ) ); +} + +/** GID used for GID-less send work queue entries */ +static const struct ib_gid arbel_no_gid = { + { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0 } } +}; + +/** + * Post send work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v av Address vector + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int arbel_post_send ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct ib_address_vector *av, + struct io_buffer *iobuf ) { + struct arbel *arbel = ib_get_drvdata ( ibdev ); + struct arbel_queue_pair *arbel_qp = ib_qp_get_drvdata ( qp ); + struct ib_work_queue *wq = &qp->send; + struct arbel_send_work_queue *arbel_send_wq = &arbel_qp->send; + struct arbelprm_ud_send_wqe *prev_wqe; + struct arbelprm_ud_send_wqe *wqe; + struct arbelprm_qp_db_record *qp_db_rec; + union arbelprm_doorbell_register db_reg; + const struct ib_gid *gid; + unsigned int wqe_idx_mask; + size_t nds; + + /* Allocate work queue entry */ + wqe_idx_mask = ( wq->num_wqes - 1 ); + if ( wq->iobufs[wq->next_idx & wqe_idx_mask] ) { + DBGC ( arbel, "Arbel %p send queue full", arbel ); + return -ENOBUFS; + } + wq->iobufs[wq->next_idx & wqe_idx_mask] = iobuf; + prev_wqe = &arbel_send_wq->wqe[(wq->next_idx - 1) & wqe_idx_mask].ud; + wqe = &arbel_send_wq->wqe[wq->next_idx & wqe_idx_mask].ud; + + /* Construct work queue entry */ + MLX_FILL_1 ( &wqe->next, 1, always1, 1 ); + memset ( &wqe->ctrl, 0, sizeof ( wqe->ctrl ) ); + MLX_FILL_1 ( &wqe->ctrl, 0, always1, 1 ); + memset ( &wqe->ud, 0, sizeof ( wqe->ud ) ); + MLX_FILL_2 ( &wqe->ud, 0, + ud_address_vector.pd, ARBEL_GLOBAL_PD, + ud_address_vector.port_number, ibdev->port ); + MLX_FILL_2 ( &wqe->ud, 1, + ud_address_vector.rlid, av->lid, + ud_address_vector.g, av->gid_present ); + MLX_FILL_2 ( &wqe->ud, 2, + ud_address_vector.max_stat_rate, + ( ( av->rate >= 3 ) ? 0 : 1 ), + ud_address_vector.msg, 3 ); + MLX_FILL_1 ( &wqe->ud, 3, ud_address_vector.sl, av->sl ); + gid = ( av->gid_present ? &av->gid : &arbel_no_gid ); + memcpy ( &wqe->ud.u.dwords[4], gid, sizeof ( *gid ) ); + MLX_FILL_1 ( &wqe->ud, 8, destination_qp, av->qpn ); + MLX_FILL_1 ( &wqe->ud, 9, q_key, av->qkey ); + MLX_FILL_1 ( &wqe->data[0], 0, byte_count, iob_len ( iobuf ) ); + MLX_FILL_1 ( &wqe->data[0], 1, l_key, arbel->reserved_lkey ); + MLX_FILL_1 ( &wqe->data[0], 3, + local_address_l, virt_to_bus ( iobuf->data ) ); + + /* Update previous work queue entry's "next" field */ + nds = ( ( offsetof ( typeof ( *wqe ), data ) + + sizeof ( wqe->data[0] ) ) >> 4 ); + MLX_SET ( &prev_wqe->next, nopcode, ARBEL_OPCODE_SEND ); + MLX_FILL_3 ( &prev_wqe->next, 1, + nds, nds, + f, 1, + always1, 1 ); + + /* Update doorbell record */ + barrier(); + qp_db_rec = &arbel->db_rec[arbel_send_wq->doorbell_idx].qp; + MLX_FILL_1 ( qp_db_rec, 0, + counter, ( ( wq->next_idx + 1 ) & 0xffff ) ); + + /* Ring doorbell register */ + MLX_FILL_4 ( &db_reg.send, 0, + nopcode, ARBEL_OPCODE_SEND, + f, 1, + wqe_counter, ( wq->next_idx & 0xffff ), + wqe_cnt, 1 ); + MLX_FILL_2 ( &db_reg.send, 1, + nds, nds, + qpn, qp->qpn ); + arbel_ring_doorbell ( arbel, &db_reg, ARBEL_DB_POST_SND_OFFSET ); + + /* Update work queue's index */ + wq->next_idx++; + + return 0; +} + +/** + * Post receive work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int arbel_post_recv ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct io_buffer *iobuf ) { + struct arbel *arbel = ib_get_drvdata ( ibdev ); + struct arbel_queue_pair *arbel_qp = ib_qp_get_drvdata ( qp ); + struct ib_work_queue *wq = &qp->recv; + struct arbel_recv_work_queue *arbel_recv_wq = &arbel_qp->recv; + struct arbelprm_recv_wqe *wqe; + union arbelprm_doorbell_record *db_rec; + unsigned int wqe_idx_mask; + + /* Allocate work queue entry */ + wqe_idx_mask = ( wq->num_wqes - 1 ); + if ( wq->iobufs[wq->next_idx & wqe_idx_mask] ) { + DBGC ( arbel, "Arbel %p receive queue full", arbel ); + return -ENOBUFS; + } + wq->iobufs[wq->next_idx & wqe_idx_mask] = iobuf; + wqe = &arbel_recv_wq->wqe[wq->next_idx & wqe_idx_mask].recv; + + /* Construct work queue entry */ + MLX_FILL_1 ( &wqe->data[0], 0, byte_count, iob_tailroom ( iobuf ) ); + MLX_FILL_1 ( &wqe->data[0], 1, l_key, arbel->reserved_lkey ); + MLX_FILL_1 ( &wqe->data[0], 3, + local_address_l, virt_to_bus ( iobuf->data ) ); + + /* Update doorbell record */ + barrier(); + db_rec = &arbel->db_rec[arbel_recv_wq->doorbell_idx]; + MLX_FILL_1 ( &db_rec->qp, 0, + counter, ( ( wq->next_idx + 1 ) & 0xffff ) ); + + /* Update work queue's index */ + wq->next_idx++; + + return 0; +} + +/** + * Handle completion + * + * @v ibdev Infiniband device + * @v cq Completion queue + * @v cqe Hardware completion queue entry + * @ret rc Return status code + */ +static int arbel_complete ( struct ib_device *ibdev, + struct ib_completion_queue *cq, + union arbelprm_completion_entry *cqe ) { + struct arbel *arbel = ib_get_drvdata ( ibdev ); + struct ib_work_queue *wq; + struct ib_queue_pair *qp; + struct arbel_queue_pair *arbel_qp; + struct arbel_send_work_queue *arbel_send_wq; + struct arbel_recv_work_queue *arbel_recv_wq; + struct arbelprm_recv_wqe *recv_wqe; + struct io_buffer *iobuf; + struct ib_address_vector av; + struct ib_global_route_header *grh; + unsigned int opcode; + unsigned long qpn; + int is_send; + unsigned long wqe_adr; + unsigned int wqe_idx; + size_t len; + int rc = 0; + + /* Parse completion */ + qpn = MLX_GET ( &cqe->normal, my_qpn ); + is_send = MLX_GET ( &cqe->normal, s ); + wqe_adr = ( MLX_GET ( &cqe->normal, wqe_adr ) << 6 ); + opcode = MLX_GET ( &cqe->normal, opcode ); + if ( opcode >= ARBEL_OPCODE_RECV_ERROR ) { + /* "s" field is not valid for error opcodes */ + is_send = ( opcode == ARBEL_OPCODE_SEND_ERROR ); + DBGC ( arbel, "Arbel %p CPN %lx syndrome %x vendor %x\n", + arbel, cq->cqn, MLX_GET ( &cqe->error, syndrome ), + MLX_GET ( &cqe->error, vendor_code ) ); + rc = -EIO; + /* Don't return immediately; propagate error to completer */ + } + + /* Identify work queue */ + wq = ib_find_wq ( cq, qpn, is_send ); + if ( ! wq ) { + DBGC ( arbel, "Arbel %p CQN %lx unknown %s QPN %lx\n", + arbel, cq->cqn, ( is_send ? "send" : "recv" ), qpn ); + return -EIO; + } + qp = wq->qp; + arbel_qp = ib_qp_get_drvdata ( qp ); + arbel_send_wq = &arbel_qp->send; + arbel_recv_wq = &arbel_qp->recv; + + /* Identify work queue entry index */ + if ( is_send ) { + wqe_idx = ( ( wqe_adr - virt_to_bus ( arbel_send_wq->wqe ) ) / + sizeof ( arbel_send_wq->wqe[0] ) ); + assert ( wqe_idx < qp->send.num_wqes ); + } else { + wqe_idx = ( ( wqe_adr - virt_to_bus ( arbel_recv_wq->wqe ) ) / + sizeof ( arbel_recv_wq->wqe[0] ) ); + assert ( wqe_idx < qp->recv.num_wqes ); + } + + /* Identify I/O buffer */ + iobuf = wq->iobufs[wqe_idx]; + if ( ! iobuf ) { + DBGC ( arbel, "Arbel %p CQN %lx QPN %lx empty WQE %x\n", + arbel, cq->cqn, qpn, wqe_idx ); + return -EIO; + } + wq->iobufs[wqe_idx] = NULL; + + if ( is_send ) { + /* Hand off to completion handler */ + ib_complete_send ( ibdev, qp, iobuf, rc ); + } else { + /* Set received length */ + len = MLX_GET ( &cqe->normal, byte_cnt ); + recv_wqe = &arbel_recv_wq->wqe[wqe_idx].recv; + assert ( MLX_GET ( &recv_wqe->data[0], local_address_l ) == + virt_to_bus ( iobuf->data ) ); + assert ( MLX_GET ( &recv_wqe->data[0], byte_count ) == + iob_tailroom ( iobuf ) ); + MLX_FILL_1 ( &recv_wqe->data[0], 0, byte_count, 0 ); + MLX_FILL_1 ( &recv_wqe->data[0], 1, + l_key, ARBEL_INVALID_LKEY ); + assert ( len <= iob_tailroom ( iobuf ) ); + iob_put ( iobuf, len ); + assert ( iob_len ( iobuf ) >= sizeof ( *grh ) ); + grh = iobuf->data; + iob_pull ( iobuf, sizeof ( *grh ) ); + /* Construct address vector */ + memset ( &av, 0, sizeof ( av ) ); + av.qpn = MLX_GET ( &cqe->normal, rqpn ); + av.lid = MLX_GET ( &cqe->normal, rlid ); + av.sl = MLX_GET ( &cqe->normal, sl ); + av.gid_present = MLX_GET ( &cqe->normal, g ); + memcpy ( &av.gid, &grh->sgid, sizeof ( av.gid ) ); + /* Hand off to completion handler */ + ib_complete_recv ( ibdev, qp, &av, iobuf, rc ); + } + + return rc; +} + +/** + * Poll completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + */ +static void arbel_poll_cq ( struct ib_device *ibdev, + struct ib_completion_queue *cq ) { + struct arbel *arbel = ib_get_drvdata ( ibdev ); + struct arbel_completion_queue *arbel_cq = ib_cq_get_drvdata ( cq ); + struct arbelprm_cq_ci_db_record *ci_db_rec; + union arbelprm_completion_entry *cqe; + unsigned int cqe_idx_mask; + int rc; + + while ( 1 ) { + /* Look for completion entry */ + cqe_idx_mask = ( cq->num_cqes - 1 ); + cqe = &arbel_cq->cqe[cq->next_idx & cqe_idx_mask]; + if ( MLX_GET ( &cqe->normal, owner ) != 0 ) { + /* Entry still owned by hardware; end of poll */ + break; + } + + /* Handle completion */ + if ( ( rc = arbel_complete ( ibdev, cq, cqe ) ) != 0 ) { + DBGC ( arbel, "Arbel %p failed to complete: %s\n", + arbel, strerror ( rc ) ); + DBGC_HD ( arbel, cqe, sizeof ( *cqe ) ); + } + + /* Return ownership to hardware */ + MLX_FILL_1 ( &cqe->normal, 7, owner, 1 ); + barrier(); + /* Update completion queue's index */ + cq->next_idx++; + /* Update doorbell record */ + ci_db_rec = &arbel->db_rec[arbel_cq->ci_doorbell_idx].cq_ci; + MLX_FILL_1 ( ci_db_rec, 0, + counter, ( cq->next_idx & 0xffffffffUL ) ); + } +} + +/*************************************************************************** + * + * Event queues + * + *************************************************************************** + */ + +/** + * Create event queue + * + * @v arbel Arbel device + * @ret rc Return status code + */ +static int arbel_create_eq ( struct arbel *arbel ) { + struct arbel_event_queue *arbel_eq = &arbel->eq; + struct arbelprm_eqc eqctx; + struct arbelprm_event_mask mask; + unsigned int i; + int rc; + + /* Select event queue number */ + arbel_eq->eqn = arbel->limits.reserved_eqs; + + /* Calculate doorbell address */ + arbel_eq->doorbell = ( arbel->eq_ci_doorbells + + ARBEL_DB_EQ_OFFSET ( arbel_eq->eqn ) ); + + /* Allocate event queue itself */ + arbel_eq->eqe_size = + ( ARBEL_NUM_EQES * sizeof ( arbel_eq->eqe[0] ) ); + arbel_eq->eqe = malloc_dma ( arbel_eq->eqe_size, + sizeof ( arbel_eq->eqe[0] ) ); + if ( ! arbel_eq->eqe ) { + rc = -ENOMEM; + goto err_eqe; + } + memset ( arbel_eq->eqe, 0, arbel_eq->eqe_size ); + for ( i = 0 ; i < ARBEL_NUM_EQES ; i++ ) { + MLX_FILL_1 ( &arbel_eq->eqe[i].generic, 7, owner, 1 ); + } + barrier(); + + /* Hand queue over to hardware */ + memset ( &eqctx, 0, sizeof ( eqctx ) ); + MLX_FILL_1 ( &eqctx, 0, st, 0xa /* "Fired" */ ); + MLX_FILL_1 ( &eqctx, 2, + start_address_l, virt_to_phys ( arbel_eq->eqe ) ); + MLX_FILL_1 ( &eqctx, 3, log_eq_size, fls ( ARBEL_NUM_EQES - 1 ) ); + MLX_FILL_1 ( &eqctx, 6, pd, ARBEL_GLOBAL_PD ); + MLX_FILL_1 ( &eqctx, 7, lkey, arbel->reserved_lkey ); + if ( ( rc = arbel_cmd_sw2hw_eq ( arbel, arbel_eq->eqn, + &eqctx ) ) != 0 ) { + DBGC ( arbel, "Arbel %p SW2HW_EQ failed: %s\n", + arbel, strerror ( rc ) ); + goto err_sw2hw_eq; + } + + /* Map events to this event queue */ + memset ( &mask, 0, sizeof ( mask ) ); + MLX_FILL_1 ( &mask, 1, port_state_change, 1 ); + if ( ( rc = arbel_cmd_map_eq ( arbel, + ( ARBEL_MAP_EQ | arbel_eq->eqn ), + &mask ) ) != 0 ) { + DBGC ( arbel, "Arbel %p MAP_EQ failed: %s\n", + arbel, strerror ( rc ) ); + goto err_map_eq; + } + + DBGC ( arbel, "Arbel %p EQN %#lx ring at [%p,%p])\n", + arbel, arbel_eq->eqn, arbel_eq->eqe, + ( ( ( void * ) arbel_eq->eqe ) + arbel_eq->eqe_size ) ); + return 0; + + err_map_eq: + arbel_cmd_hw2sw_eq ( arbel, arbel_eq->eqn, &eqctx ); + err_sw2hw_eq: + free_dma ( arbel_eq->eqe, arbel_eq->eqe_size ); + err_eqe: + memset ( arbel_eq, 0, sizeof ( *arbel_eq ) ); + return rc; +} + +/** + * Destroy event queue + * + * @v arbel Arbel device + */ +static void arbel_destroy_eq ( struct arbel *arbel ) { + struct arbel_event_queue *arbel_eq = &arbel->eq; + struct arbelprm_eqc eqctx; + struct arbelprm_event_mask mask; + int rc; + + /* Unmap events from event queue */ + memset ( &mask, 0, sizeof ( mask ) ); + MLX_FILL_1 ( &mask, 1, port_state_change, 1 ); + if ( ( rc = arbel_cmd_map_eq ( arbel, + ( ARBEL_UNMAP_EQ | arbel_eq->eqn ), + &mask ) ) != 0 ) { + DBGC ( arbel, "Arbel %p FATAL MAP_EQ failed to unmap: %s\n", + arbel, strerror ( rc ) ); + /* Continue; HCA may die but system should survive */ + } + + /* Take ownership back from hardware */ + if ( ( rc = arbel_cmd_hw2sw_eq ( arbel, arbel_eq->eqn, + &eqctx ) ) != 0 ) { + DBGC ( arbel, "Arbel %p FATAL HW2SW_EQ failed: %s\n", + arbel, strerror ( rc ) ); + /* Leak memory and return; at least we avoid corruption */ + return; + } + + /* Free memory */ + free_dma ( arbel_eq->eqe, arbel_eq->eqe_size ); + memset ( arbel_eq, 0, sizeof ( *arbel_eq ) ); +} + +/** + * Handle port state event + * + * @v arbel Arbel device + * @v eqe Port state change event queue entry + */ +static void arbel_event_port_state_change ( struct arbel *arbel, + union arbelprm_event_entry *eqe){ + unsigned int port; + int link_up; + + /* Get port and link status */ + port = ( MLX_GET ( &eqe->port_state_change, data.p ) - 1 ); + link_up = ( MLX_GET ( &eqe->generic, event_sub_type ) & 0x04 ); + DBGC ( arbel, "Arbel %p port %d link %s\n", arbel, ( port + 1 ), + ( link_up ? "up" : "down" ) ); + + /* Sanity check */ + if ( port >= ARBEL_NUM_PORTS ) { + DBGC ( arbel, "Arbel %p port %d does not exist!\n", + arbel, ( port + 1 ) ); + return; + } + + /* Update MAD parameters */ + ib_smc_update ( arbel->ibdev[port], arbel_mad ); + + /* Notify Infiniband core of link state change */ + ib_link_state_changed ( arbel->ibdev[port] ); +} + +/** + * Poll event queue + * + * @v ibdev Infiniband device + */ +static void arbel_poll_eq ( struct ib_device *ibdev ) { + struct arbel *arbel = ib_get_drvdata ( ibdev ); + struct arbel_event_queue *arbel_eq = &arbel->eq; + union arbelprm_event_entry *eqe; + union arbelprm_eq_doorbell_register db_reg; + unsigned int eqe_idx_mask; + unsigned int event_type; + + while ( 1 ) { + /* Look for event entry */ + eqe_idx_mask = ( ARBEL_NUM_EQES - 1 ); + eqe = &arbel_eq->eqe[arbel_eq->next_idx & eqe_idx_mask]; + if ( MLX_GET ( &eqe->generic, owner ) != 0 ) { + /* Entry still owned by hardware; end of poll */ + break; + } + DBGCP ( arbel, "Arbel %p event:\n", arbel ); + DBGCP_HD ( arbel, eqe, sizeof ( *eqe ) ); + + /* Handle event */ + event_type = MLX_GET ( &eqe->generic, event_type ); + switch ( event_type ) { + case ARBEL_EV_PORT_STATE_CHANGE: + arbel_event_port_state_change ( arbel, eqe ); + break; + default: + DBGC ( arbel, "Arbel %p unrecognised event type " + "%#x:\n", arbel, event_type ); + DBGC_HD ( arbel, eqe, sizeof ( *eqe ) ); + break; + } + + /* Return ownership to hardware */ + MLX_FILL_1 ( &eqe->generic, 7, owner, 1 ); + barrier(); + + /* Update event queue's index */ + arbel_eq->next_idx++; + + /* Ring doorbell */ + MLX_FILL_1 ( &db_reg.ci, 0, ci, arbel_eq->next_idx ); + DBGCP ( arbel, "Ringing doorbell %08lx with %08x\n", + virt_to_phys ( arbel_eq->doorbell ), + db_reg.dword[0] ); + writel ( db_reg.dword[0], arbel_eq->doorbell ); + } +} + +/*************************************************************************** + * + * Infiniband link-layer operations + * + *************************************************************************** + */ + +/** + * Initialise Infiniband link + * + * @v ibdev Infiniband device + * @ret rc Return status code + */ +static int arbel_open ( struct ib_device *ibdev ) { + struct arbel *arbel = ib_get_drvdata ( ibdev ); + struct arbelprm_init_ib init_ib; + int rc; + + memset ( &init_ib, 0, sizeof ( init_ib ) ); + MLX_FILL_3 ( &init_ib, 0, + mtu_cap, ARBEL_MTU_2048, + port_width_cap, 3, + vl_cap, 1 ); + MLX_FILL_1 ( &init_ib, 1, max_gid, 1 ); + MLX_FILL_1 ( &init_ib, 2, max_pkey, 64 ); + if ( ( rc = arbel_cmd_init_ib ( arbel, ibdev->port, + &init_ib ) ) != 0 ) { + DBGC ( arbel, "Arbel %p could not intialise IB: %s\n", + arbel, strerror ( rc ) ); + return rc; + } + + /* Update MAD parameters */ + ib_smc_update ( ibdev, arbel_mad ); + + return 0; +} + +/** + * Close Infiniband link + * + * @v ibdev Infiniband device + */ +static void arbel_close ( struct ib_device *ibdev ) { + struct arbel *arbel = ib_get_drvdata ( ibdev ); + int rc; + + if ( ( rc = arbel_cmd_close_ib ( arbel, ibdev->port ) ) != 0 ) { + DBGC ( arbel, "Arbel %p could not close IB: %s\n", + arbel, strerror ( rc ) ); + /* Nothing we can do about this */ + } +} + +/*************************************************************************** + * + * Multicast group operations + * + *************************************************************************** + */ + +/** + * Attach to multicast group + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v gid Multicast GID + * @ret rc Return status code + */ +static int arbel_mcast_attach ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct ib_gid *gid ) { + struct arbel *arbel = ib_get_drvdata ( ibdev ); + struct arbelprm_mgm_hash hash; + struct arbelprm_mgm_entry mgm; + unsigned int index; + int rc; + + /* Generate hash table index */ + if ( ( rc = arbel_cmd_mgid_hash ( arbel, gid, &hash ) ) != 0 ) { + DBGC ( arbel, "Arbel %p could not hash GID: %s\n", + arbel, strerror ( rc ) ); + return rc; + } + index = MLX_GET ( &hash, hash ); + + /* Check for existing hash table entry */ + if ( ( rc = arbel_cmd_read_mgm ( arbel, index, &mgm ) ) != 0 ) { + DBGC ( arbel, "Arbel %p could not read MGM %#x: %s\n", + arbel, index, strerror ( rc ) ); + return rc; + } + if ( MLX_GET ( &mgm, mgmqp_0.qi ) != 0 ) { + /* FIXME: this implementation allows only a single QP + * per multicast group, and doesn't handle hash + * collisions. Sufficient for IPoIB but may need to + * be extended in future. + */ + DBGC ( arbel, "Arbel %p MGID index %#x already in use\n", + arbel, index ); + return -EBUSY; + } + + /* Update hash table entry */ + MLX_FILL_2 ( &mgm, 8, + mgmqp_0.qpn_i, qp->qpn, + mgmqp_0.qi, 1 ); + memcpy ( &mgm.u.dwords[4], gid, sizeof ( *gid ) ); + if ( ( rc = arbel_cmd_write_mgm ( arbel, index, &mgm ) ) != 0 ) { + DBGC ( arbel, "Arbel %p could not write MGM %#x: %s\n", + arbel, index, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Detach from multicast group + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v gid Multicast GID + */ +static void arbel_mcast_detach ( struct ib_device *ibdev, + struct ib_queue_pair *qp __unused, + struct ib_gid *gid ) { + struct arbel *arbel = ib_get_drvdata ( ibdev ); + struct arbelprm_mgm_hash hash; + struct arbelprm_mgm_entry mgm; + unsigned int index; + int rc; + + /* Generate hash table index */ + if ( ( rc = arbel_cmd_mgid_hash ( arbel, gid, &hash ) ) != 0 ) { + DBGC ( arbel, "Arbel %p could not hash GID: %s\n", + arbel, strerror ( rc ) ); + return; + } + index = MLX_GET ( &hash, hash ); + + /* Clear hash table entry */ + memset ( &mgm, 0, sizeof ( mgm ) ); + if ( ( rc = arbel_cmd_write_mgm ( arbel, index, &mgm ) ) != 0 ) { + DBGC ( arbel, "Arbel %p could not write MGM %#x: %s\n", + arbel, index, strerror ( rc ) ); + return; + } +} + +/** Arbel Infiniband operations */ +static struct ib_device_operations arbel_ib_operations = { + .create_cq = arbel_create_cq, + .destroy_cq = arbel_destroy_cq, + .create_qp = arbel_create_qp, + .modify_qp = arbel_modify_qp, + .destroy_qp = arbel_destroy_qp, + .post_send = arbel_post_send, + .post_recv = arbel_post_recv, + .poll_cq = arbel_poll_cq, + .poll_eq = arbel_poll_eq, + .open = arbel_open, + .close = arbel_close, + .mcast_attach = arbel_mcast_attach, + .mcast_detach = arbel_mcast_detach, +}; + +/*************************************************************************** + * + * Firmware control + * + *************************************************************************** + */ + +/** + * Start firmware running + * + * @v arbel Arbel device + * @ret rc Return status code + */ +static int arbel_start_firmware ( struct arbel *arbel ) { + struct arbelprm_query_fw fw; + struct arbelprm_access_lam lam; + struct arbelprm_virtual_physical_mapping map_fa; + unsigned int fw_pages; + unsigned int log2_fw_pages; + size_t fw_size; + physaddr_t fw_base; + uint64_t eq_set_ci_base_addr; + int rc; + + /* Get firmware parameters */ + if ( ( rc = arbel_cmd_query_fw ( arbel, &fw ) ) != 0 ) { + DBGC ( arbel, "Arbel %p could not query firmware: %s\n", + arbel, strerror ( rc ) ); + goto err_query_fw; + } + DBGC ( arbel, "Arbel %p firmware version %d.%d.%d\n", arbel, + MLX_GET ( &fw, fw_rev_major ), MLX_GET ( &fw, fw_rev_minor ), + MLX_GET ( &fw, fw_rev_subminor ) ); + fw_pages = MLX_GET ( &fw, fw_pages ); + log2_fw_pages = fls ( fw_pages - 1 ); + fw_pages = ( 1 << log2_fw_pages ); + DBGC ( arbel, "Arbel %p requires %d kB for firmware\n", + arbel, ( fw_pages * 4 ) ); + eq_set_ci_base_addr = + ( ( (uint64_t) MLX_GET ( &fw, eq_set_ci_base_addr_h ) << 32 ) | + ( (uint64_t) MLX_GET ( &fw, eq_set_ci_base_addr_l ) ) ); + arbel->eq_ci_doorbells = ioremap ( eq_set_ci_base_addr, 0x200 ); + + /* Enable locally-attached memory. Ignore failure; there may + * be no attached memory. + */ + arbel_cmd_enable_lam ( arbel, &lam ); + + /* Allocate firmware pages and map firmware area */ + fw_size = ( fw_pages * 4096 ); + arbel->firmware_area = umalloc ( fw_size * 2 ); + if ( ! arbel->firmware_area ) { + rc = -ENOMEM; + goto err_alloc_fa; + } + fw_base = ( user_to_phys ( arbel->firmware_area, fw_size ) & + ~( fw_size - 1 ) ); + DBGC ( arbel, "Arbel %p firmware area at physical [%lx,%lx)\n", + arbel, fw_base, ( fw_base + fw_size ) ); + memset ( &map_fa, 0, sizeof ( map_fa ) ); + MLX_FILL_2 ( &map_fa, 3, + log2size, log2_fw_pages, + pa_l, ( fw_base >> 12 ) ); + if ( ( rc = arbel_cmd_map_fa ( arbel, &map_fa ) ) != 0 ) { + DBGC ( arbel, "Arbel %p could not map firmware: %s\n", + arbel, strerror ( rc ) ); + goto err_map_fa; + } + + /* Start firmware */ + if ( ( rc = arbel_cmd_run_fw ( arbel ) ) != 0 ) { + DBGC ( arbel, "Arbel %p could not run firmware: %s\n", + arbel, strerror ( rc ) ); + goto err_run_fw; + } + + DBGC ( arbel, "Arbel %p firmware started\n", arbel ); + return 0; + + err_run_fw: + arbel_cmd_unmap_fa ( arbel ); + err_map_fa: + ufree ( arbel->firmware_area ); + arbel->firmware_area = UNULL; + err_alloc_fa: + err_query_fw: + return rc; +} + +/** + * Stop firmware running + * + * @v arbel Arbel device + */ +static void arbel_stop_firmware ( struct arbel *arbel ) { + int rc; + + if ( ( rc = arbel_cmd_unmap_fa ( arbel ) ) != 0 ) { + DBGC ( arbel, "Arbel %p FATAL could not stop firmware: %s\n", + arbel, strerror ( rc ) ); + /* Leak memory and return; at least we avoid corruption */ + return; + } + ufree ( arbel->firmware_area ); + arbel->firmware_area = UNULL; +} + +/*************************************************************************** + * + * Infinihost Context Memory management + * + *************************************************************************** + */ + +/** + * Get device limits + * + * @v arbel Arbel device + * @ret rc Return status code + */ +static int arbel_get_limits ( struct arbel *arbel ) { + struct arbelprm_query_dev_lim dev_lim; + int rc; + + if ( ( rc = arbel_cmd_query_dev_lim ( arbel, &dev_lim ) ) != 0 ) { + DBGC ( arbel, "Arbel %p could not get device limits: %s\n", + arbel, strerror ( rc ) ); + return rc; + } + + arbel->limits.reserved_qps = + ( 1 << MLX_GET ( &dev_lim, log2_rsvd_qps ) ); + arbel->limits.qpc_entry_size = MLX_GET ( &dev_lim, qpc_entry_sz ); + arbel->limits.eqpc_entry_size = MLX_GET ( &dev_lim, eqpc_entry_sz ); + arbel->limits.reserved_srqs = + ( 1 << MLX_GET ( &dev_lim, log2_rsvd_srqs ) ); + arbel->limits.srqc_entry_size = MLX_GET ( &dev_lim, srq_entry_sz ); + arbel->limits.reserved_ees = + ( 1 << MLX_GET ( &dev_lim, log2_rsvd_ees ) ); + arbel->limits.eec_entry_size = MLX_GET ( &dev_lim, eec_entry_sz ); + arbel->limits.eeec_entry_size = MLX_GET ( &dev_lim, eeec_entry_sz ); + arbel->limits.reserved_cqs = + ( 1 << MLX_GET ( &dev_lim, log2_rsvd_cqs ) ); + arbel->limits.cqc_entry_size = MLX_GET ( &dev_lim, cqc_entry_sz ); + arbel->limits.reserved_eqs = MLX_GET ( &dev_lim, num_rsvd_eqs ); + arbel->limits.reserved_mtts = + ( 1 << MLX_GET ( &dev_lim, log2_rsvd_mtts ) ); + arbel->limits.mtt_entry_size = MLX_GET ( &dev_lim, mtt_entry_sz ); + arbel->limits.reserved_mrws = + ( 1 << MLX_GET ( &dev_lim, log2_rsvd_mrws ) ); + arbel->limits.mpt_entry_size = MLX_GET ( &dev_lim, mpt_entry_sz ); + arbel->limits.reserved_rdbs = + ( 1 << MLX_GET ( &dev_lim, log2_rsvd_rdbs ) ); + arbel->limits.eqc_entry_size = MLX_GET ( &dev_lim, eqc_entry_sz ); + arbel->limits.reserved_uars = MLX_GET ( &dev_lim, num_rsvd_uars ); + + return 0; +} + +/** + * Get ICM usage + * + * @v log_num_entries Log2 of the number of entries + * @v entry_size Entry size + * @ret usage Usage size in ICM + */ +static size_t icm_usage ( unsigned int log_num_entries, size_t entry_size ) { + size_t usage; + + usage = ( ( 1 << log_num_entries ) * entry_size ); + usage = ( ( usage + 4095 ) & ~4095 ); + return usage; +} + +/** + * Allocate ICM + * + * @v arbel Arbel device + * @v init_hca INIT_HCA structure to fill in + * @ret rc Return status code + */ +static int arbel_alloc_icm ( struct arbel *arbel, + struct arbelprm_init_hca *init_hca ) { + struct arbelprm_scalar_parameter icm_size; + struct arbelprm_scalar_parameter icm_aux_size; + struct arbelprm_virtual_physical_mapping map_icm_aux; + struct arbelprm_virtual_physical_mapping map_icm; + union arbelprm_doorbell_record *db_rec; + size_t icm_offset = 0; + unsigned int log_num_qps, log_num_srqs, log_num_ees, log_num_cqs; + unsigned int log_num_mtts, log_num_mpts, log_num_rdbs, log_num_eqs; + int rc; + + icm_offset = ( ( arbel->limits.reserved_uars + 1 ) << 12 ); + + /* Queue pair contexts */ + log_num_qps = fls ( arbel->limits.reserved_qps + ARBEL_MAX_QPS - 1 ); + MLX_FILL_2 ( init_hca, 13, + qpc_eec_cqc_eqc_rdb_parameters.qpc_base_addr_l, + ( icm_offset >> 7 ), + qpc_eec_cqc_eqc_rdb_parameters.log_num_of_qp, + log_num_qps ); + DBGC ( arbel, "Arbel %p ICM QPC base = %zx\n", arbel, icm_offset ); + icm_offset += icm_usage ( log_num_qps, arbel->limits.qpc_entry_size ); + + /* Extended queue pair contexts */ + MLX_FILL_1 ( init_hca, 25, + qpc_eec_cqc_eqc_rdb_parameters.eqpc_base_addr_l, + icm_offset ); + DBGC ( arbel, "Arbel %p ICM EQPC base = %zx\n", arbel, icm_offset ); + // icm_offset += icm_usage ( log_num_qps, arbel->limits.eqpc_entry_size ); + icm_offset += icm_usage ( log_num_qps, arbel->limits.qpc_entry_size ); + + /* Shared receive queue contexts */ + log_num_srqs = fls ( arbel->limits.reserved_srqs - 1 ); + MLX_FILL_2 ( init_hca, 19, + qpc_eec_cqc_eqc_rdb_parameters.srqc_base_addr_l, + ( icm_offset >> 5 ), + qpc_eec_cqc_eqc_rdb_parameters.log_num_of_srq, + log_num_srqs ); + DBGC ( arbel, "Arbel %p ICM SRQC base = %zx\n", arbel, icm_offset ); + icm_offset += icm_usage ( log_num_srqs, arbel->limits.srqc_entry_size ); + + /* End-to-end contexts */ + log_num_ees = fls ( arbel->limits.reserved_ees - 1 ); + MLX_FILL_2 ( init_hca, 17, + qpc_eec_cqc_eqc_rdb_parameters.eec_base_addr_l, + ( icm_offset >> 7 ), + qpc_eec_cqc_eqc_rdb_parameters.log_num_of_ee, + log_num_ees ); + DBGC ( arbel, "Arbel %p ICM EEC base = %zx\n", arbel, icm_offset ); + icm_offset += icm_usage ( log_num_ees, arbel->limits.eec_entry_size ); + + /* Extended end-to-end contexts */ + MLX_FILL_1 ( init_hca, 29, + qpc_eec_cqc_eqc_rdb_parameters.eeec_base_addr_l, + icm_offset ); + DBGC ( arbel, "Arbel %p ICM EEEC base = %zx\n", arbel, icm_offset ); + icm_offset += icm_usage ( log_num_ees, arbel->limits.eeec_entry_size ); + + /* Completion queue contexts */ + log_num_cqs = fls ( arbel->limits.reserved_cqs + ARBEL_MAX_CQS - 1 ); + MLX_FILL_2 ( init_hca, 21, + qpc_eec_cqc_eqc_rdb_parameters.cqc_base_addr_l, + ( icm_offset >> 6 ), + qpc_eec_cqc_eqc_rdb_parameters.log_num_of_cq, + log_num_cqs ); + DBGC ( arbel, "Arbel %p ICM CQC base = %zx\n", arbel, icm_offset ); + icm_offset += icm_usage ( log_num_cqs, arbel->limits.cqc_entry_size ); + + /* Memory translation table */ + log_num_mtts = fls ( arbel->limits.reserved_mtts - 1 ); + MLX_FILL_1 ( init_hca, 65, + tpt_parameters.mtt_base_addr_l, icm_offset ); + DBGC ( arbel, "Arbel %p ICM MTT base = %zx\n", arbel, icm_offset ); + icm_offset += icm_usage ( log_num_mtts, arbel->limits.mtt_entry_size ); + + /* Memory protection table */ + log_num_mpts = fls ( arbel->limits.reserved_mrws + 1 - 1 ); + MLX_FILL_1 ( init_hca, 61, + tpt_parameters.mpt_base_adr_l, icm_offset ); + MLX_FILL_1 ( init_hca, 62, + tpt_parameters.log_mpt_sz, log_num_mpts ); + DBGC ( arbel, "Arbel %p ICM MTT base = %zx\n", arbel, icm_offset ); + icm_offset += icm_usage ( log_num_mpts, arbel->limits.mpt_entry_size ); + + /* RDMA something or other */ + log_num_rdbs = fls ( arbel->limits.reserved_rdbs - 1 ); + MLX_FILL_1 ( init_hca, 37, + qpc_eec_cqc_eqc_rdb_parameters.rdb_base_addr_l, + icm_offset ); + DBGC ( arbel, "Arbel %p ICM RDB base = %zx\n", arbel, icm_offset ); + icm_offset += icm_usage ( log_num_rdbs, 32 ); + + /* Event queue contexts */ + log_num_eqs = fls ( arbel->limits.reserved_eqs + ARBEL_MAX_EQS - 1 ); + MLX_FILL_2 ( init_hca, 33, + qpc_eec_cqc_eqc_rdb_parameters.eqc_base_addr_l, + ( icm_offset >> 6 ), + qpc_eec_cqc_eqc_rdb_parameters.log_num_eq, + log_num_eqs ); + DBGC ( arbel, "Arbel %p ICM EQ base = %zx\n", arbel, icm_offset ); + icm_offset += ( ( 1 << log_num_eqs ) * arbel->limits.eqc_entry_size ); + + /* Multicast table */ + MLX_FILL_1 ( init_hca, 49, + multicast_parameters.mc_base_addr_l, icm_offset ); + MLX_FILL_1 ( init_hca, 52, + multicast_parameters.log_mc_table_entry_sz, + fls ( sizeof ( struct arbelprm_mgm_entry ) - 1 ) ); + MLX_FILL_1 ( init_hca, 53, + multicast_parameters.mc_table_hash_sz, 8 ); + MLX_FILL_1 ( init_hca, 54, + multicast_parameters.log_mc_table_sz, 3 ); + DBGC ( arbel, "Arbel %p ICM MC base = %zx\n", arbel, icm_offset ); + icm_offset += ( 8 * sizeof ( struct arbelprm_mgm_entry ) ); + + arbel->icm_len = icm_offset; + arbel->icm_len = ( ( arbel->icm_len + 4095 ) & ~4095 ); + + /* Get ICM auxiliary area size */ + memset ( &icm_size, 0, sizeof ( icm_size ) ); + MLX_FILL_1 ( &icm_size, 1, value, arbel->icm_len ); + if ( ( rc = arbel_cmd_set_icm_size ( arbel, &icm_size, + &icm_aux_size ) ) != 0 ) { + DBGC ( arbel, "Arbel %p could not set ICM size: %s\n", + arbel, strerror ( rc ) ); + goto err_set_icm_size; + } + arbel->icm_aux_len = ( MLX_GET ( &icm_aux_size, value ) * 4096 ); + + /* Allocate ICM data and auxiliary area */ + DBGC ( arbel, "Arbel %p requires %zd kB ICM and %zd kB AUX ICM\n", + arbel, ( arbel->icm_len / 1024 ), + ( arbel->icm_aux_len / 1024 ) ); + arbel->icm = umalloc ( arbel->icm_len + arbel->icm_aux_len ); + if ( ! arbel->icm ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Map ICM auxiliary area */ + memset ( &map_icm_aux, 0, sizeof ( map_icm_aux ) ); + MLX_FILL_2 ( &map_icm_aux, 3, + log2size, fls ( ( arbel->icm_aux_len / 4096 ) - 1 ), + pa_l, + ( user_to_phys ( arbel->icm, arbel->icm_len ) >> 12 ) ); + if ( ( rc = arbel_cmd_map_icm_aux ( arbel, &map_icm_aux ) ) != 0 ) { + DBGC ( arbel, "Arbel %p could not map AUX ICM: %s\n", + arbel, strerror ( rc ) ); + goto err_map_icm_aux; + } + + /* MAP ICM area */ + memset ( &map_icm, 0, sizeof ( map_icm ) ); + MLX_FILL_2 ( &map_icm, 3, + log2size, fls ( ( arbel->icm_len / 4096 ) - 1 ), + pa_l, ( user_to_phys ( arbel->icm, 0 ) >> 12 ) ); + if ( ( rc = arbel_cmd_map_icm ( arbel, &map_icm ) ) != 0 ) { + DBGC ( arbel, "Arbel %p could not map ICM: %s\n", + arbel, strerror ( rc ) ); + goto err_map_icm; + } + + /* Initialise UAR context */ + arbel->db_rec = phys_to_virt ( user_to_phys ( arbel->icm, 0 ) + + ( arbel->limits.reserved_uars * + ARBEL_PAGE_SIZE ) ); + memset ( arbel->db_rec, 0, ARBEL_PAGE_SIZE ); + db_rec = &arbel->db_rec[ARBEL_GROUP_SEPARATOR_DOORBELL]; + MLX_FILL_1 ( &db_rec->qp, 1, res, ARBEL_UAR_RES_GROUP_SEP ); + + return 0; + + arbel_cmd_unmap_icm ( arbel, ( arbel->icm_len / 4096 ) ); + err_map_icm: + arbel_cmd_unmap_icm_aux ( arbel ); + err_map_icm_aux: + ufree ( arbel->icm ); + arbel->icm = UNULL; + err_alloc: + err_set_icm_size: + return rc; +} + +/** + * Free ICM + * + * @v arbel Arbel device + */ +static void arbel_free_icm ( struct arbel *arbel ) { + arbel_cmd_unmap_icm ( arbel, ( arbel->icm_len / 4096 ) ); + arbel_cmd_unmap_icm_aux ( arbel ); + ufree ( arbel->icm ); + arbel->icm = UNULL; +} + +/*************************************************************************** + * + * PCI interface + * + *************************************************************************** + */ + +/** + * Set up memory protection table + * + * @v arbel Arbel device + * @ret rc Return status code + */ +static int arbel_setup_mpt ( struct arbel *arbel ) { + struct arbelprm_mpt mpt; + uint32_t key; + int rc; + + /* Derive key */ + key = ( arbel->limits.reserved_mrws | ARBEL_MKEY_PREFIX ); + arbel->reserved_lkey = ( ( key << 8 ) | ( key >> 24 ) ); + + /* Initialise memory protection table */ + memset ( &mpt, 0, sizeof ( mpt ) ); + MLX_FILL_4 ( &mpt, 0, + r_w, 1, + pa, 1, + lr, 1, + lw, 1 ); + MLX_FILL_1 ( &mpt, 2, mem_key, key ); + MLX_FILL_1 ( &mpt, 3, pd, ARBEL_GLOBAL_PD ); + MLX_FILL_1 ( &mpt, 6, reg_wnd_len_h, 0xffffffffUL ); + MLX_FILL_1 ( &mpt, 7, reg_wnd_len_l, 0xffffffffUL ); + if ( ( rc = arbel_cmd_sw2hw_mpt ( arbel, arbel->limits.reserved_mrws, + &mpt ) ) != 0 ) { + DBGC ( arbel, "Arbel %p could not set up MPT: %s\n", + arbel, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Probe PCI device + * + * @v pci PCI device + * @v id PCI ID + * @ret rc Return status code + */ +static int arbel_probe ( struct pci_device *pci, + const struct pci_device_id *id __unused ) { + struct arbel *arbel; + struct ib_device *ibdev; + struct arbelprm_init_hca init_hca; + int i; + int rc; + + /* Allocate Arbel device */ + arbel = zalloc ( sizeof ( *arbel ) ); + if ( ! arbel ) { + rc = -ENOMEM; + goto err_alloc_arbel; + } + pci_set_drvdata ( pci, arbel ); + + /* Allocate Infiniband devices */ + for ( i = 0 ; i < ARBEL_NUM_PORTS ; i++ ) { + ibdev = alloc_ibdev ( 0 ); + if ( ! ibdev ) { + rc = -ENOMEM; + goto err_alloc_ibdev; + } + arbel->ibdev[i] = ibdev; + ibdev->op = &arbel_ib_operations; + ibdev->dev = &pci->dev; + ibdev->port = ( ARBEL_PORT_BASE + i ); + ib_set_drvdata ( ibdev, arbel ); + } + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Get PCI BARs */ + arbel->config = ioremap ( pci_bar_start ( pci, ARBEL_PCI_CONFIG_BAR ), + ARBEL_PCI_CONFIG_BAR_SIZE ); + arbel->uar = ioremap ( ( pci_bar_start ( pci, ARBEL_PCI_UAR_BAR ) + + ARBEL_PCI_UAR_IDX * ARBEL_PCI_UAR_SIZE ), + ARBEL_PCI_UAR_SIZE ); + + /* Allocate space for mailboxes */ + arbel->mailbox_in = malloc_dma ( ARBEL_MBOX_SIZE, ARBEL_MBOX_ALIGN ); + if ( ! arbel->mailbox_in ) { + rc = -ENOMEM; + goto err_mailbox_in; + } + arbel->mailbox_out = malloc_dma ( ARBEL_MBOX_SIZE, ARBEL_MBOX_ALIGN ); + if ( ! arbel->mailbox_out ) { + rc = -ENOMEM; + goto err_mailbox_out; + } + + /* Start firmware */ + if ( ( rc = arbel_start_firmware ( arbel ) ) != 0 ) + goto err_start_firmware; + + /* Get device limits */ + if ( ( rc = arbel_get_limits ( arbel ) ) != 0 ) + goto err_get_limits; + + /* Allocate ICM */ + memset ( &init_hca, 0, sizeof ( init_hca ) ); + if ( ( rc = arbel_alloc_icm ( arbel, &init_hca ) ) != 0 ) + goto err_alloc_icm; + + /* Initialise HCA */ + MLX_FILL_1 ( &init_hca, 74, uar_parameters.log_max_uars, 1 ); + if ( ( rc = arbel_cmd_init_hca ( arbel, &init_hca ) ) != 0 ) { + DBGC ( arbel, "Arbel %p could not initialise HCA: %s\n", + arbel, strerror ( rc ) ); + goto err_init_hca; + } + + /* Set up memory protection */ + if ( ( rc = arbel_setup_mpt ( arbel ) ) != 0 ) + goto err_setup_mpt; + + /* Set up event queue */ + if ( ( rc = arbel_create_eq ( arbel ) ) != 0 ) + goto err_create_eq; + + /* Update MAD parameters */ + for ( i = 0 ; i < ARBEL_NUM_PORTS ; i++ ) + ib_smc_update ( arbel->ibdev[i], arbel_mad ); + + /* Register Infiniband devices */ + for ( i = 0 ; i < ARBEL_NUM_PORTS ; i++ ) { + if ( ( rc = register_ibdev ( arbel->ibdev[i] ) ) != 0 ) { + DBGC ( arbel, "Arbel %p could not register IB " + "device: %s\n", arbel, strerror ( rc ) ); + goto err_register_ibdev; + } + } + + return 0; + + i = ARBEL_NUM_PORTS; + err_register_ibdev: + for ( i-- ; i >= 0 ; i-- ) + unregister_ibdev ( arbel->ibdev[i] ); + arbel_destroy_eq ( arbel ); + err_create_eq: + err_setup_mpt: + arbel_cmd_close_hca ( arbel ); + err_init_hca: + arbel_free_icm ( arbel ); + err_alloc_icm: + err_get_limits: + arbel_stop_firmware ( arbel ); + err_start_firmware: + free_dma ( arbel->mailbox_out, ARBEL_MBOX_SIZE ); + err_mailbox_out: + free_dma ( arbel->mailbox_in, ARBEL_MBOX_SIZE ); + err_mailbox_in: + i = ARBEL_NUM_PORTS; + err_alloc_ibdev: + for ( i-- ; i >= 0 ; i-- ) + ibdev_put ( arbel->ibdev[i] ); + free ( arbel ); + err_alloc_arbel: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void arbel_remove ( struct pci_device *pci ) { + struct arbel *arbel = pci_get_drvdata ( pci ); + int i; + + for ( i = ( ARBEL_NUM_PORTS - 1 ) ; i >= 0 ; i-- ) + unregister_ibdev ( arbel->ibdev[i] ); + arbel_destroy_eq ( arbel ); + arbel_cmd_close_hca ( arbel ); + arbel_free_icm ( arbel ); + arbel_stop_firmware ( arbel ); + arbel_stop_firmware ( arbel ); + free_dma ( arbel->mailbox_out, ARBEL_MBOX_SIZE ); + free_dma ( arbel->mailbox_in, ARBEL_MBOX_SIZE ); + for ( i = ( ARBEL_NUM_PORTS - 1 ) ; i >= 0 ; i-- ) + ibdev_put ( arbel->ibdev[i] ); + free ( arbel ); +} + +static struct pci_device_id arbel_nics[] = { + PCI_ROM ( 0x15b3, 0x6282, "mt25218", "MT25218 HCA driver" ), + PCI_ROM ( 0x15b3, 0x6274, "mt25204", "MT25204 HCA driver" ), +}; + +struct pci_driver arbel_driver __pci_driver = { + .ids = arbel_nics, + .id_count = ( sizeof ( arbel_nics ) / sizeof ( arbel_nics[0] ) ), + .probe = arbel_probe, + .remove = arbel_remove, +}; diff --git a/gpxe/src/drivers/infiniband/arbel.h b/gpxe/src/drivers/infiniband/arbel.h new file mode 100644 index 0000000..7d97b15 --- /dev/null +++ b/gpxe/src/drivers/infiniband/arbel.h @@ -0,0 +1,542 @@ +#ifndef _ARBEL_H +#define _ARBEL_H + +/** @file + * + * Mellanox Arbel Infiniband HCA driver + * + */ + +#include +#include +#include "mlx_bitops.h" +#include "MT25218_PRM.h" + +/* + * Hardware constants + * + */ + +/* Ports in existence */ +#define ARBEL_NUM_PORTS 2 +#define ARBEL_PORT_BASE 1 + +/* PCI BARs */ +#define ARBEL_PCI_CONFIG_BAR PCI_BASE_ADDRESS_0 +#define ARBEL_PCI_CONFIG_BAR_SIZE 0x100000 +#define ARBEL_PCI_UAR_BAR PCI_BASE_ADDRESS_2 +#define ARBEL_PCI_UAR_IDX 1 +#define ARBEL_PCI_UAR_SIZE 0x1000 + +/* UAR context table (UCE) resource types */ +#define ARBEL_UAR_RES_NONE 0x00 +#define ARBEL_UAR_RES_CQ_CI 0x01 +#define ARBEL_UAR_RES_CQ_ARM 0x02 +#define ARBEL_UAR_RES_SQ 0x03 +#define ARBEL_UAR_RES_RQ 0x04 +#define ARBEL_UAR_RES_GROUP_SEP 0x07 + +/* Work queue entry and completion queue entry opcodes */ +#define ARBEL_OPCODE_SEND 0x0a +#define ARBEL_OPCODE_RECV_ERROR 0xfe +#define ARBEL_OPCODE_SEND_ERROR 0xff + +/* HCA command register opcodes */ +#define ARBEL_HCR_QUERY_DEV_LIM 0x0003 +#define ARBEL_HCR_QUERY_FW 0x0004 +#define ARBEL_HCR_INIT_HCA 0x0007 +#define ARBEL_HCR_CLOSE_HCA 0x0008 +#define ARBEL_HCR_INIT_IB 0x0009 +#define ARBEL_HCR_CLOSE_IB 0x000a +#define ARBEL_HCR_SW2HW_MPT 0x000d +#define ARBEL_HCR_MAP_EQ 0x0012 +#define ARBEL_HCR_SW2HW_EQ 0x0013 +#define ARBEL_HCR_HW2SW_EQ 0x0014 +#define ARBEL_HCR_SW2HW_CQ 0x0016 +#define ARBEL_HCR_HW2SW_CQ 0x0017 +#define ARBEL_HCR_RST2INIT_QPEE 0x0019 +#define ARBEL_HCR_INIT2RTR_QPEE 0x001a +#define ARBEL_HCR_RTR2RTS_QPEE 0x001b +#define ARBEL_HCR_RTS2RTS_QPEE 0x001c +#define ARBEL_HCR_2RST_QPEE 0x0021 +#define ARBEL_HCR_MAD_IFC 0x0024 +#define ARBEL_HCR_READ_MGM 0x0025 +#define ARBEL_HCR_WRITE_MGM 0x0026 +#define ARBEL_HCR_MGID_HASH 0x0027 +#define ARBEL_HCR_RUN_FW 0x0ff6 +#define ARBEL_HCR_DISABLE_LAM 0x0ff7 +#define ARBEL_HCR_ENABLE_LAM 0x0ff8 +#define ARBEL_HCR_UNMAP_ICM 0x0ff9 +#define ARBEL_HCR_MAP_ICM 0x0ffa +#define ARBEL_HCR_UNMAP_ICM_AUX 0x0ffb +#define ARBEL_HCR_MAP_ICM_AUX 0x0ffc +#define ARBEL_HCR_SET_ICM_SIZE 0x0ffd +#define ARBEL_HCR_UNMAP_FA 0x0ffe +#define ARBEL_HCR_MAP_FA 0x0fff + +/* Service types */ +#define ARBEL_ST_UD 0x03 + +/* MTUs */ +#define ARBEL_MTU_2048 0x04 + +#define ARBEL_NO_EQ 64 + +#define ARBEL_INVALID_LKEY 0x00000100UL + +#define ARBEL_PAGE_SIZE 4096 + +#define ARBEL_DB_POST_SND_OFFSET 0x10 +#define ARBEL_DB_EQ_OFFSET(_eqn) ( 0x08 * (_eqn) ) + +#define ARBEL_QPEE_OPT_PARAM_QKEY 0x00000020UL + +#define ARBEL_MAP_EQ ( 0UL << 31 ) +#define ARBEL_UNMAP_EQ ( 1UL << 31 ) + +#define ARBEL_EV_PORT_STATE_CHANGE 0x09 + +/* + * Datatypes that seem to be missing from the autogenerated documentation + * + */ +struct arbelprm_mgm_hash_st { + pseudo_bit_t reserved0[0x00020]; +/* -------------- */ + pseudo_bit_t hash[0x00010]; + pseudo_bit_t reserved1[0x00010]; +} __attribute__ (( packed )); + +struct arbelprm_scalar_parameter_st { + pseudo_bit_t reserved0[0x00020]; +/* -------------- */ + pseudo_bit_t value[0x00020]; +} __attribute__ (( packed )); + +struct arbelprm_event_mask_st { + pseudo_bit_t reserved0[0x00020]; +/* -------------- */ + pseudo_bit_t completion[0x00001]; + pseudo_bit_t reserved1[0x0008]; + pseudo_bit_t port_state_change[0x00001]; + pseudo_bit_t reserved2[0x00016]; +} __attribute__ (( packed )); + +struct arbelprm_eq_set_ci_st { + pseudo_bit_t ci[0x00020]; +} __attribute__ (( packed )); + +struct arbelprm_port_state_change_event_st { + pseudo_bit_t reserved[0x00020]; + struct arbelprm_port_state_change_st data; +} __attribute__ (( packed )); + +/* + * Wrapper structures for hardware datatypes + * + */ + +struct MLX_DECLARE_STRUCT ( arbelprm_access_lam ); +struct MLX_DECLARE_STRUCT ( arbelprm_completion_queue_context ); +struct MLX_DECLARE_STRUCT ( arbelprm_completion_queue_entry ); +struct MLX_DECLARE_STRUCT ( arbelprm_completion_with_error ); +struct MLX_DECLARE_STRUCT ( arbelprm_cq_arm_db_record ); +struct MLX_DECLARE_STRUCT ( arbelprm_cq_ci_db_record ); +struct MLX_DECLARE_STRUCT ( arbelprm_event_mask ); +struct MLX_DECLARE_STRUCT ( arbelprm_event_queue_entry ); +struct MLX_DECLARE_STRUCT ( arbelprm_eq_set_ci ); +struct MLX_DECLARE_STRUCT ( arbelprm_eqc ); +struct MLX_DECLARE_STRUCT ( arbelprm_hca_command_register ); +struct MLX_DECLARE_STRUCT ( arbelprm_init_hca ); +struct MLX_DECLARE_STRUCT ( arbelprm_init_ib ); +struct MLX_DECLARE_STRUCT ( arbelprm_mad_ifc ); +struct MLX_DECLARE_STRUCT ( arbelprm_mgm_entry ); +struct MLX_DECLARE_STRUCT ( arbelprm_mgm_hash ); +struct MLX_DECLARE_STRUCT ( arbelprm_mpt ); +struct MLX_DECLARE_STRUCT ( arbelprm_port_state_change_event ); +struct MLX_DECLARE_STRUCT ( arbelprm_qp_db_record ); +struct MLX_DECLARE_STRUCT ( arbelprm_qp_ee_state_transitions ); +struct MLX_DECLARE_STRUCT ( arbelprm_query_dev_lim ); +struct MLX_DECLARE_STRUCT ( arbelprm_query_fw ); +struct MLX_DECLARE_STRUCT ( arbelprm_queue_pair_ee_context_entry ); +struct MLX_DECLARE_STRUCT ( arbelprm_recv_wqe_segment_next ); +struct MLX_DECLARE_STRUCT ( arbelprm_scalar_parameter ); +struct MLX_DECLARE_STRUCT ( arbelprm_send_doorbell ); +struct MLX_DECLARE_STRUCT ( arbelprm_ud_address_vector ); +struct MLX_DECLARE_STRUCT ( arbelprm_virtual_physical_mapping ); +struct MLX_DECLARE_STRUCT ( arbelprm_wqe_segment_ctrl_send ); +struct MLX_DECLARE_STRUCT ( arbelprm_wqe_segment_data_ptr ); +struct MLX_DECLARE_STRUCT ( arbelprm_wqe_segment_next ); +struct MLX_DECLARE_STRUCT ( arbelprm_wqe_segment_ud ); + +/* + * Composite hardware datatypes + * + */ + +#define ARBEL_MAX_GATHER 1 + +struct arbelprm_ud_send_wqe { + struct arbelprm_wqe_segment_next next; + struct arbelprm_wqe_segment_ctrl_send ctrl; + struct arbelprm_wqe_segment_ud ud; + struct arbelprm_wqe_segment_data_ptr data[ARBEL_MAX_GATHER]; +} __attribute__ (( packed )); + +#define ARBEL_MAX_SCATTER 1 + +struct arbelprm_recv_wqe { + /* The autogenerated header is inconsistent between send and + * receive WQEs. The "ctrl" structure for receive WQEs is + * defined to include the "next" structure. Since the "ctrl" + * part of the "ctrl" structure contains only "reserved, must + * be zero" bits, we ignore its definition and provide + * something more usable. + */ + struct arbelprm_recv_wqe_segment_next next; + uint32_t ctrl[2]; /* All "reserved, must be zero" */ + struct arbelprm_wqe_segment_data_ptr data[ARBEL_MAX_SCATTER]; +} __attribute__ (( packed )); + +union arbelprm_completion_entry { + struct arbelprm_completion_queue_entry normal; + struct arbelprm_completion_with_error error; +} __attribute__ (( packed )); + +union arbelprm_event_entry { + struct arbelprm_event_queue_entry generic; + struct arbelprm_port_state_change_event port_state_change; +} __attribute__ (( packed )); + +union arbelprm_doorbell_record { + struct arbelprm_cq_arm_db_record cq_arm; + struct arbelprm_cq_ci_db_record cq_ci; + struct arbelprm_qp_db_record qp; +} __attribute__ (( packed )); + +union arbelprm_doorbell_register { + struct arbelprm_send_doorbell send; + uint32_t dword[2]; +} __attribute__ (( packed )); + +union arbelprm_eq_doorbell_register { + struct arbelprm_eq_set_ci ci; + uint32_t dword[1]; +} __attribute__ (( packed )); + +union arbelprm_mad { + struct arbelprm_mad_ifc ifc; + union ib_mad mad; +} __attribute__ (( packed )); + +/* + * gPXE-specific definitions + * + */ + +/** Arbel device limits */ +struct arbel_dev_limits { + /** Number of reserved QPs */ + unsigned int reserved_qps; + /** QP context entry size */ + size_t qpc_entry_size; + /** Extended QP context entry size */ + size_t eqpc_entry_size; + /** Number of reserved SRQs */ + unsigned int reserved_srqs; + /** SRQ context entry size */ + size_t srqc_entry_size; + /** Number of reserved EEs */ + unsigned int reserved_ees; + /** EE context entry size */ + size_t eec_entry_size; + /** Extended EE context entry size */ + size_t eeec_entry_size; + /** Number of reserved CQs */ + unsigned int reserved_cqs; + /** CQ context entry size */ + size_t cqc_entry_size; + /** Number of reserved EQs */ + unsigned int reserved_eqs; + /** Number of reserved MTTs */ + unsigned int reserved_mtts; + /** MTT entry size */ + size_t mtt_entry_size; + /** Number of reserved MRWs */ + unsigned int reserved_mrws; + /** MPT entry size */ + size_t mpt_entry_size; + /** Number of reserved RDBs */ + unsigned int reserved_rdbs; + /** EQ context entry size */ + size_t eqc_entry_size; + /** Number of reserved UARs */ + unsigned int reserved_uars; +}; + +/** Alignment of Arbel send work queue entries */ +#define ARBEL_SEND_WQE_ALIGN 128 + +/** An Arbel send work queue entry */ +union arbel_send_wqe { + struct arbelprm_ud_send_wqe ud; + uint8_t force_align[ARBEL_SEND_WQE_ALIGN]; +} __attribute__ (( packed )); + +/** An Arbel send work queue */ +struct arbel_send_work_queue { + /** Doorbell record number */ + unsigned int doorbell_idx; + /** Work queue entries */ + union arbel_send_wqe *wqe; + /** Size of work queue */ + size_t wqe_size; +}; + +/** Alignment of Arbel receive work queue entries */ +#define ARBEL_RECV_WQE_ALIGN 64 + +/** An Arbel receive work queue entry */ +union arbel_recv_wqe { + struct arbelprm_recv_wqe recv; + uint8_t force_align[ARBEL_RECV_WQE_ALIGN]; +} __attribute__ (( packed )); + +/** An Arbel receive work queue */ +struct arbel_recv_work_queue { + /** Doorbell record number */ + unsigned int doorbell_idx; + /** Work queue entries */ + union arbel_recv_wqe *wqe; + /** Size of work queue */ + size_t wqe_size; +}; + +/** Maximum number of allocatable queue pairs + * + * This is a policy decision, not a device limit. + */ +#define ARBEL_MAX_QPS 8 + +/** Base queue pair number */ +#define ARBEL_QPN_BASE 0x550000 + +/** An Arbel queue pair */ +struct arbel_queue_pair { + /** Send work queue */ + struct arbel_send_work_queue send; + /** Receive work queue */ + struct arbel_recv_work_queue recv; +}; + +/** Maximum number of allocatable completion queues + * + * This is a policy decision, not a device limit. + */ +#define ARBEL_MAX_CQS 8 + +/** An Arbel completion queue */ +struct arbel_completion_queue { + /** Consumer counter doorbell record number */ + unsigned int ci_doorbell_idx; + /** Arm queue doorbell record number */ + unsigned int arm_doorbell_idx; + /** Completion queue entries */ + union arbelprm_completion_entry *cqe; + /** Size of completion queue */ + size_t cqe_size; +}; + +/** Maximum number of allocatable event queues + * + * This is a policy decision, not a device limit. + */ +#define ARBEL_MAX_EQS 64 + +/** A Arbel event queue */ +struct arbel_event_queue { + /** Event queue entries */ + union arbelprm_event_entry *eqe; + /** Size of event queue */ + size_t eqe_size; + /** Event queue number */ + unsigned long eqn; + /** Next event queue entry index */ + unsigned long next_idx; + /** Doorbell register */ + void *doorbell; +}; + +/** Number of event queue entries + * + * This is a policy decision. + */ +#define ARBEL_NUM_EQES 4 + + +/** An Arbel resource bitmask */ +typedef uint32_t arbel_bitmask_t; + +/** Size of an Arbel resource bitmask */ +#define ARBEL_BITMASK_SIZE(max_entries) \ + ( ( (max_entries) + ( 8 * sizeof ( arbel_bitmask_t ) ) - 1 ) / \ + ( 8 * sizeof ( arbel_bitmask_t ) ) ) + +/** An Arbel device */ +struct arbel { + /** PCI configuration registers */ + void *config; + /** PCI user Access Region */ + void *uar; + /** Event queue consumer index doorbells */ + void *eq_ci_doorbells; + + /** Command input mailbox */ + void *mailbox_in; + /** Command output mailbox */ + void *mailbox_out; + + /** Firmware area in external memory */ + userptr_t firmware_area; + /** ICM size */ + size_t icm_len; + /** ICM AUX size */ + size_t icm_aux_len; + /** ICM area */ + userptr_t icm; + + /** Event queue */ + struct arbel_event_queue eq; + /** Doorbell records */ + union arbelprm_doorbell_record *db_rec; + /** Reserved LKey + * + * Used to get unrestricted memory access. + */ + unsigned long reserved_lkey; + + /** Completion queue in-use bitmask */ + arbel_bitmask_t cq_inuse[ ARBEL_BITMASK_SIZE ( ARBEL_MAX_CQS ) ]; + /** Queue pair in-use bitmask */ + arbel_bitmask_t qp_inuse[ ARBEL_BITMASK_SIZE ( ARBEL_MAX_QPS ) ]; + + /** Device limits */ + struct arbel_dev_limits limits; + + /** Infiniband devices */ + struct ib_device *ibdev[ARBEL_NUM_PORTS]; +}; + +/** Global protection domain */ +#define ARBEL_GLOBAL_PD 0x123456 + +/** Memory key prefix */ +#define ARBEL_MKEY_PREFIX 0x77000000UL + +/* + * HCA commands + * + */ + +#define ARBEL_HCR_BASE 0x80680 +#define ARBEL_HCR_REG(x) ( ARBEL_HCR_BASE + 4 * (x) ) +#define ARBEL_HCR_MAX_WAIT_MS 2000 +#define ARBEL_MBOX_ALIGN 4096 +#define ARBEL_MBOX_SIZE 512 + +/* HCA command is split into + * + * bits 11:0 Opcode + * bit 12 Input uses mailbox + * bit 13 Output uses mailbox + * bits 22:14 Input parameter length (in dwords) + * bits 31:23 Output parameter length (in dwords) + * + * Encoding the information in this way allows us to cut out several + * parameters to the arbel_command() call. + */ +#define ARBEL_HCR_IN_MBOX 0x00001000UL +#define ARBEL_HCR_OUT_MBOX 0x00002000UL +#define ARBEL_HCR_OPCODE( _command ) ( (_command) & 0xfff ) +#define ARBEL_HCR_IN_LEN( _command ) ( ( (_command) >> 12 ) & 0x7fc ) +#define ARBEL_HCR_OUT_LEN( _command ) ( ( (_command) >> 21 ) & 0x7fc ) + +/** Build HCR command from component parts */ +#define ARBEL_HCR_INOUT_CMD( _opcode, _in_mbox, _in_len, \ + _out_mbox, _out_len ) \ + ( (_opcode) | \ + ( (_in_mbox) ? ARBEL_HCR_IN_MBOX : 0 ) | \ + ( ( (_in_len) / 4 ) << 14 ) | \ + ( (_out_mbox) ? ARBEL_HCR_OUT_MBOX : 0 ) | \ + ( ( (_out_len) / 4 ) << 23 ) ) + +#define ARBEL_HCR_IN_CMD( _opcode, _in_mbox, _in_len ) \ + ARBEL_HCR_INOUT_CMD ( _opcode, _in_mbox, _in_len, 0, 0 ) + +#define ARBEL_HCR_OUT_CMD( _opcode, _out_mbox, _out_len ) \ + ARBEL_HCR_INOUT_CMD ( _opcode, 0, 0, _out_mbox, _out_len ) + +#define ARBEL_HCR_VOID_CMD( _opcode ) \ + ARBEL_HCR_INOUT_CMD ( _opcode, 0, 0, 0, 0 ) + +/* + * Doorbell record allocation + * + * The doorbell record map looks like: + * + * ARBEL_MAX_CQS * Arm completion queue doorbell + * ARBEL_MAX_QPS * Send work request doorbell + * Group separator + * ...(empty space)... + * ARBEL_MAX_QPS * Receive work request doorbell + * ARBEL_MAX_CQS * Completion queue consumer counter update doorbell + */ + +#define ARBEL_MAX_DOORBELL_RECORDS 512 +#define ARBEL_GROUP_SEPARATOR_DOORBELL ( ARBEL_MAX_CQS + ARBEL_MAX_QPS ) + +/** + * Get arm completion queue doorbell index + * + * @v cqn_offset Completion queue number offset + * @ret doorbell_idx Doorbell index + */ +static inline unsigned int +arbel_cq_arm_doorbell_idx ( unsigned int cqn_offset ) { + return cqn_offset; +} + +/** + * Get send work request doorbell index + * + * @v qpn_offset Queue pair number offset + * @ret doorbell_idx Doorbell index + */ +static inline unsigned int +arbel_send_doorbell_idx ( unsigned int qpn_offset ) { + return ( ARBEL_MAX_CQS + qpn_offset ); +} + +/** + * Get receive work request doorbell index + * + * @v qpn_offset Queue pair number offset + * @ret doorbell_idx Doorbell index + */ +static inline unsigned int +arbel_recv_doorbell_idx ( unsigned int qpn_offset ) { + return ( ARBEL_MAX_DOORBELL_RECORDS - ARBEL_MAX_CQS - qpn_offset - 1 ); +} + +/** + * Get completion queue consumer counter doorbell index + * + * @v cqn_offset Completion queue number offset + * @ret doorbell_idx Doorbell index + */ +static inline unsigned int +arbel_cq_ci_doorbell_idx ( unsigned int cqn_offset ) { + return ( ARBEL_MAX_DOORBELL_RECORDS - cqn_offset - 1 ); +} + +#endif /* _ARBEL_H */ diff --git a/gpxe/src/drivers/infiniband/hermon.c b/gpxe/src/drivers/infiniband/hermon.c new file mode 100644 index 0000000..40add28 --- /dev/null +++ b/gpxe/src/drivers/infiniband/hermon.c @@ -0,0 +1,2324 @@ +/* + * Copyright (C) 2008 Michael Brown . + * Copyright (C) 2008 Mellanox Technologies Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hermon.h" + +/** + * @file + * + * Mellanox Hermon Infiniband HCA + * + */ + +/*************************************************************************** + * + * Queue number allocation + * + *************************************************************************** + */ + +/** + * Allocate offsets within usage bitmask + * + * @v bits Usage bitmask + * @v bits_len Length of usage bitmask + * @v num_bits Number of contiguous bits to allocate within bitmask + * @ret bit First free bit within bitmask, or negative error + */ +static int hermon_bitmask_alloc ( hermon_bitmask_t *bits, + unsigned int bits_len, + unsigned int num_bits ) { + unsigned int bit = 0; + hermon_bitmask_t mask = 1; + unsigned int found = 0; + + /* Search bits for num_bits contiguous free bits */ + while ( bit < bits_len ) { + if ( ( mask & *bits ) == 0 ) { + if ( ++found == num_bits ) + goto found; + } else { + found = 0; + } + bit++; + mask = ( mask << 1 ) | ( mask >> ( 8 * sizeof ( mask ) - 1 ) ); + if ( mask == 1 ) + bits++; + } + return -ENFILE; + + found: + /* Mark bits as in-use */ + do { + *bits |= mask; + if ( mask == 1 ) + bits--; + mask = ( mask >> 1 ) | ( mask << ( 8 * sizeof ( mask ) - 1 ) ); + } while ( --found ); + + return ( bit - num_bits + 1 ); +} + +/** + * Free offsets within usage bitmask + * + * @v bits Usage bitmask + * @v bit Starting bit within bitmask + * @v num_bits Number of contiguous bits to free within bitmask + */ +static void hermon_bitmask_free ( hermon_bitmask_t *bits, + int bit, unsigned int num_bits ) { + hermon_bitmask_t mask; + + for ( ; num_bits ; bit++, num_bits-- ) { + mask = ( 1 << ( bit % ( 8 * sizeof ( mask ) ) ) ); + bits[ ( bit / ( 8 * sizeof ( mask ) ) ) ] &= ~mask; + } +} + +/*************************************************************************** + * + * HCA commands + * + *************************************************************************** + */ + +/** + * Wait for Hermon command completion + * + * @v hermon Hermon device + * @v hcr HCA command registers + * @ret rc Return status code + */ +static int hermon_cmd_wait ( struct hermon *hermon, + struct hermonprm_hca_command_register *hcr ) { + unsigned int wait; + + for ( wait = HERMON_HCR_MAX_WAIT_MS ; wait ; wait-- ) { + hcr->u.dwords[6] = + readl ( hermon->config + HERMON_HCR_REG ( 6 ) ); + if ( ( MLX_GET ( hcr, go ) == 0 ) && + ( MLX_GET ( hcr, t ) == hermon->toggle ) ) + return 0; + mdelay ( 1 ); + } + return -EBUSY; +} + +/** + * Issue HCA command + * + * @v hermon Hermon device + * @v command Command opcode, flags and input/output lengths + * @v op_mod Opcode modifier (0 if no modifier applicable) + * @v in Input parameters + * @v in_mod Input modifier (0 if no modifier applicable) + * @v out Output parameters + * @ret rc Return status code + */ +static int hermon_cmd ( struct hermon *hermon, unsigned long command, + unsigned int op_mod, const void *in, + unsigned int in_mod, void *out ) { + struct hermonprm_hca_command_register hcr; + unsigned int opcode = HERMON_HCR_OPCODE ( command ); + size_t in_len = HERMON_HCR_IN_LEN ( command ); + size_t out_len = HERMON_HCR_OUT_LEN ( command ); + void *in_buffer; + void *out_buffer; + unsigned int status; + unsigned int i; + int rc; + + assert ( in_len <= HERMON_MBOX_SIZE ); + assert ( out_len <= HERMON_MBOX_SIZE ); + + DBGC2 ( hermon, "Hermon %p command %02x in %zx%s out %zx%s\n", + hermon, opcode, in_len, + ( ( command & HERMON_HCR_IN_MBOX ) ? "(mbox)" : "" ), out_len, + ( ( command & HERMON_HCR_OUT_MBOX ) ? "(mbox)" : "" ) ); + + /* Check that HCR is free */ + if ( ( rc = hermon_cmd_wait ( hermon, &hcr ) ) != 0 ) { + DBGC ( hermon, "Hermon %p command interface locked\n", + hermon ); + return rc; + } + + /* Flip HCR toggle */ + hermon->toggle = ( 1 - hermon->toggle ); + + /* Prepare HCR */ + memset ( &hcr, 0, sizeof ( hcr ) ); + in_buffer = &hcr.u.dwords[0]; + if ( in_len && ( command & HERMON_HCR_IN_MBOX ) ) { + in_buffer = hermon->mailbox_in; + MLX_FILL_1 ( &hcr, 1, in_param_l, virt_to_bus ( in_buffer ) ); + } + memcpy ( in_buffer, in, in_len ); + MLX_FILL_1 ( &hcr, 2, input_modifier, in_mod ); + out_buffer = &hcr.u.dwords[3]; + if ( out_len && ( command & HERMON_HCR_OUT_MBOX ) ) { + out_buffer = hermon->mailbox_out; + MLX_FILL_1 ( &hcr, 4, out_param_l, + virt_to_bus ( out_buffer ) ); + } + MLX_FILL_4 ( &hcr, 6, + opcode, opcode, + opcode_modifier, op_mod, + go, 1, + t, hermon->toggle ); + DBGC ( hermon, "Hermon %p issuing command:\n", hermon ); + DBGC_HDA ( hermon, virt_to_phys ( hermon->config + HERMON_HCR_BASE ), + &hcr, sizeof ( hcr ) ); + if ( in_len && ( command & HERMON_HCR_IN_MBOX ) ) { + DBGC2 ( hermon, "Input mailbox:\n" ); + DBGC2_HDA ( hermon, virt_to_phys ( in_buffer ), in_buffer, + ( ( in_len < 512 ) ? in_len : 512 ) ); + } + + /* Issue command */ + for ( i = 0 ; i < ( sizeof ( hcr ) / sizeof ( hcr.u.dwords[0] ) ) ; + i++ ) { + writel ( hcr.u.dwords[i], + hermon->config + HERMON_HCR_REG ( i ) ); + barrier(); + } + + /* Wait for command completion */ + if ( ( rc = hermon_cmd_wait ( hermon, &hcr ) ) != 0 ) { + DBGC ( hermon, "Hermon %p timed out waiting for command:\n", + hermon ); + DBGC_HDA ( hermon, + virt_to_phys ( hermon->config + HERMON_HCR_BASE ), + &hcr, sizeof ( hcr ) ); + return rc; + } + + /* Check command status */ + status = MLX_GET ( &hcr, status ); + if ( status != 0 ) { + DBGC ( hermon, "Hermon %p command failed with status %02x:\n", + hermon, status ); + DBGC_HDA ( hermon, + virt_to_phys ( hermon->config + HERMON_HCR_BASE ), + &hcr, sizeof ( hcr ) ); + return -EIO; + } + + /* Read output parameters, if any */ + hcr.u.dwords[3] = readl ( hermon->config + HERMON_HCR_REG ( 3 ) ); + hcr.u.dwords[4] = readl ( hermon->config + HERMON_HCR_REG ( 4 ) ); + memcpy ( out, out_buffer, out_len ); + if ( out_len ) { + DBGC2 ( hermon, "Output%s:\n", + ( command & HERMON_HCR_OUT_MBOX ) ? " mailbox" : "" ); + DBGC2_HDA ( hermon, virt_to_phys ( out_buffer ), out_buffer, + ( ( out_len < 512 ) ? out_len : 512 ) ); + } + + return 0; +} + +static inline int +hermon_cmd_query_dev_cap ( struct hermon *hermon, + struct hermonprm_query_dev_cap *dev_cap ) { + return hermon_cmd ( hermon, + HERMON_HCR_OUT_CMD ( HERMON_HCR_QUERY_DEV_CAP, + 1, sizeof ( *dev_cap ) ), + 0, NULL, 0, dev_cap ); +} + +static inline int +hermon_cmd_query_fw ( struct hermon *hermon, struct hermonprm_query_fw *fw ) { + return hermon_cmd ( hermon, + HERMON_HCR_OUT_CMD ( HERMON_HCR_QUERY_FW, + 1, sizeof ( *fw ) ), + 0, NULL, 0, fw ); +} + +static inline int +hermon_cmd_init_hca ( struct hermon *hermon, + const struct hermonprm_init_hca *init_hca ) { + return hermon_cmd ( hermon, + HERMON_HCR_IN_CMD ( HERMON_HCR_INIT_HCA, + 1, sizeof ( *init_hca ) ), + 0, init_hca, 0, NULL ); +} + +static inline int +hermon_cmd_close_hca ( struct hermon *hermon ) { + return hermon_cmd ( hermon, + HERMON_HCR_VOID_CMD ( HERMON_HCR_CLOSE_HCA ), + 0, NULL, 0, NULL ); +} + +static inline int +hermon_cmd_init_port ( struct hermon *hermon, unsigned int port, + const struct hermonprm_init_port *init_port ) { + return hermon_cmd ( hermon, + HERMON_HCR_IN_CMD ( HERMON_HCR_INIT_PORT, + 1, sizeof ( *init_port ) ), + 0, init_port, port, NULL ); +} + +static inline int +hermon_cmd_close_port ( struct hermon *hermon, unsigned int port ) { + return hermon_cmd ( hermon, + HERMON_HCR_VOID_CMD ( HERMON_HCR_CLOSE_PORT ), + 0, NULL, port, NULL ); +} + +static inline int +hermon_cmd_sw2hw_mpt ( struct hermon *hermon, unsigned int index, + const struct hermonprm_mpt *mpt ) { + return hermon_cmd ( hermon, + HERMON_HCR_IN_CMD ( HERMON_HCR_SW2HW_MPT, + 1, sizeof ( *mpt ) ), + 0, mpt, index, NULL ); +} + +static inline int +hermon_cmd_write_mtt ( struct hermon *hermon, + const struct hermonprm_write_mtt *write_mtt ) { + return hermon_cmd ( hermon, + HERMON_HCR_IN_CMD ( HERMON_HCR_WRITE_MTT, + 1, sizeof ( *write_mtt ) ), + 0, write_mtt, 1, NULL ); +} + +static inline int +hermon_cmd_map_eq ( struct hermon *hermon, unsigned long index_map, + const struct hermonprm_event_mask *mask ) { + return hermon_cmd ( hermon, + HERMON_HCR_IN_CMD ( HERMON_HCR_MAP_EQ, + 0, sizeof ( *mask ) ), + 0, mask, index_map, NULL ); +} + +static inline int +hermon_cmd_sw2hw_eq ( struct hermon *hermon, unsigned int index, + const struct hermonprm_eqc *eqctx ) { + return hermon_cmd ( hermon, + HERMON_HCR_IN_CMD ( HERMON_HCR_SW2HW_EQ, + 1, sizeof ( *eqctx ) ), + 0, eqctx, index, NULL ); +} + +static inline int +hermon_cmd_hw2sw_eq ( struct hermon *hermon, unsigned int index, + struct hermonprm_eqc *eqctx ) { + return hermon_cmd ( hermon, + HERMON_HCR_OUT_CMD ( HERMON_HCR_HW2SW_EQ, + 1, sizeof ( *eqctx ) ), + 1, NULL, index, eqctx ); +} + +static inline int +hermon_cmd_query_eq ( struct hermon *hermon, unsigned int index, + struct hermonprm_eqc *eqctx ) { + return hermon_cmd ( hermon, + HERMON_HCR_OUT_CMD ( HERMON_HCR_QUERY_EQ, + 1, sizeof ( *eqctx ) ), + 0, NULL, index, eqctx ); +} + +static inline int +hermon_cmd_sw2hw_cq ( struct hermon *hermon, unsigned long cqn, + const struct hermonprm_completion_queue_context *cqctx ){ + return hermon_cmd ( hermon, + HERMON_HCR_IN_CMD ( HERMON_HCR_SW2HW_CQ, + 1, sizeof ( *cqctx ) ), + 0, cqctx, cqn, NULL ); +} + +static inline int +hermon_cmd_hw2sw_cq ( struct hermon *hermon, unsigned long cqn, + struct hermonprm_completion_queue_context *cqctx) { + return hermon_cmd ( hermon, + HERMON_HCR_OUT_CMD ( HERMON_HCR_HW2SW_CQ, + 1, sizeof ( *cqctx ) ), + 0, NULL, cqn, cqctx ); +} + +static inline int +hermon_cmd_rst2init_qp ( struct hermon *hermon, unsigned long qpn, + const struct hermonprm_qp_ee_state_transitions *ctx ){ + return hermon_cmd ( hermon, + HERMON_HCR_IN_CMD ( HERMON_HCR_RST2INIT_QP, + 1, sizeof ( *ctx ) ), + 0, ctx, qpn, NULL ); +} + +static inline int +hermon_cmd_init2rtr_qp ( struct hermon *hermon, unsigned long qpn, + const struct hermonprm_qp_ee_state_transitions *ctx ){ + return hermon_cmd ( hermon, + HERMON_HCR_IN_CMD ( HERMON_HCR_INIT2RTR_QP, + 1, sizeof ( *ctx ) ), + 0, ctx, qpn, NULL ); +} + +static inline int +hermon_cmd_rtr2rts_qp ( struct hermon *hermon, unsigned long qpn, + const struct hermonprm_qp_ee_state_transitions *ctx ) { + return hermon_cmd ( hermon, + HERMON_HCR_IN_CMD ( HERMON_HCR_RTR2RTS_QP, + 1, sizeof ( *ctx ) ), + 0, ctx, qpn, NULL ); +} + +static inline int +hermon_cmd_rts2rts_qp ( struct hermon *hermon, unsigned long qpn, + const struct hermonprm_qp_ee_state_transitions *ctx ) { + return hermon_cmd ( hermon, + HERMON_HCR_IN_CMD ( HERMON_HCR_RTS2RTS_QP, + 1, sizeof ( *ctx ) ), + 0, ctx, qpn, NULL ); +} + +static inline int +hermon_cmd_2rst_qp ( struct hermon *hermon, unsigned long qpn ) { + return hermon_cmd ( hermon, + HERMON_HCR_VOID_CMD ( HERMON_HCR_2RST_QP ), + 0x03, NULL, qpn, NULL ); +} + +static inline int +hermon_cmd_mad_ifc ( struct hermon *hermon, unsigned int port, + union hermonprm_mad *mad ) { + return hermon_cmd ( hermon, + HERMON_HCR_INOUT_CMD ( HERMON_HCR_MAD_IFC, + 1, sizeof ( *mad ), + 1, sizeof ( *mad ) ), + 0x03, mad, port, mad ); +} + +static inline int +hermon_cmd_read_mcg ( struct hermon *hermon, unsigned int index, + struct hermonprm_mcg_entry *mcg ) { + return hermon_cmd ( hermon, + HERMON_HCR_OUT_CMD ( HERMON_HCR_READ_MCG, + 1, sizeof ( *mcg ) ), + 0, NULL, index, mcg ); +} + +static inline int +hermon_cmd_write_mcg ( struct hermon *hermon, unsigned int index, + const struct hermonprm_mcg_entry *mcg ) { + return hermon_cmd ( hermon, + HERMON_HCR_IN_CMD ( HERMON_HCR_WRITE_MCG, + 1, sizeof ( *mcg ) ), + 0, mcg, index, NULL ); +} + +static inline int +hermon_cmd_mgid_hash ( struct hermon *hermon, const struct ib_gid *gid, + struct hermonprm_mgm_hash *hash ) { + return hermon_cmd ( hermon, + HERMON_HCR_INOUT_CMD ( HERMON_HCR_MGID_HASH, + 1, sizeof ( *gid ), + 0, sizeof ( *hash ) ), + 0, gid, 0, hash ); +} + +static inline int +hermon_cmd_run_fw ( struct hermon *hermon ) { + return hermon_cmd ( hermon, + HERMON_HCR_VOID_CMD ( HERMON_HCR_RUN_FW ), + 0, NULL, 0, NULL ); +} + +static inline int +hermon_cmd_unmap_icm ( struct hermon *hermon, unsigned int page_count, + const struct hermonprm_scalar_parameter *offset ) { + return hermon_cmd ( hermon, + HERMON_HCR_IN_CMD ( HERMON_HCR_UNMAP_ICM, + 0, sizeof ( *offset ) ), + 0, offset, page_count, NULL ); +} + +static inline int +hermon_cmd_map_icm ( struct hermon *hermon, + const struct hermonprm_virtual_physical_mapping *map ) { + return hermon_cmd ( hermon, + HERMON_HCR_IN_CMD ( HERMON_HCR_MAP_ICM, + 1, sizeof ( *map ) ), + 0, map, 1, NULL ); +} + +static inline int +hermon_cmd_unmap_icm_aux ( struct hermon *hermon ) { + return hermon_cmd ( hermon, + HERMON_HCR_VOID_CMD ( HERMON_HCR_UNMAP_ICM_AUX ), + 0, NULL, 0, NULL ); +} + +static inline int +hermon_cmd_map_icm_aux ( struct hermon *hermon, + const struct hermonprm_virtual_physical_mapping *map ) { + return hermon_cmd ( hermon, + HERMON_HCR_IN_CMD ( HERMON_HCR_MAP_ICM_AUX, + 1, sizeof ( *map ) ), + 0, map, 1, NULL ); +} + +static inline int +hermon_cmd_set_icm_size ( struct hermon *hermon, + const struct hermonprm_scalar_parameter *icm_size, + struct hermonprm_scalar_parameter *icm_aux_size ) { + return hermon_cmd ( hermon, + HERMON_HCR_INOUT_CMD ( HERMON_HCR_SET_ICM_SIZE, + 0, sizeof ( *icm_size ), + 0, sizeof (*icm_aux_size) ), + 0, icm_size, 0, icm_aux_size ); +} + +static inline int +hermon_cmd_unmap_fa ( struct hermon *hermon ) { + return hermon_cmd ( hermon, + HERMON_HCR_VOID_CMD ( HERMON_HCR_UNMAP_FA ), + 0, NULL, 0, NULL ); +} + +static inline int +hermon_cmd_map_fa ( struct hermon *hermon, + const struct hermonprm_virtual_physical_mapping *map ) { + return hermon_cmd ( hermon, + HERMON_HCR_IN_CMD ( HERMON_HCR_MAP_FA, + 1, sizeof ( *map ) ), + 0, map, 1, NULL ); +} + +/*************************************************************************** + * + * Memory translation table operations + * + *************************************************************************** + */ + +/** + * Allocate MTT entries + * + * @v hermon Hermon device + * @v memory Memory to map into MTT + * @v len Length of memory to map + * @v mtt MTT descriptor to fill in + * @ret rc Return status code + */ +static int hermon_alloc_mtt ( struct hermon *hermon, + const void *memory, size_t len, + struct hermon_mtt *mtt ) { + struct hermonprm_write_mtt write_mtt; + physaddr_t start; + unsigned int page_offset; + unsigned int num_pages; + int mtt_offset; + unsigned int mtt_base_addr; + unsigned int i; + int rc; + + /* Find available MTT entries */ + start = virt_to_phys ( memory ); + page_offset = ( start & ( HERMON_PAGE_SIZE - 1 ) ); + start -= page_offset; + len += page_offset; + num_pages = ( ( len + HERMON_PAGE_SIZE - 1 ) / HERMON_PAGE_SIZE ); + mtt_offset = hermon_bitmask_alloc ( hermon->mtt_inuse, HERMON_MAX_MTTS, + num_pages ); + if ( mtt_offset < 0 ) { + DBGC ( hermon, "Hermon %p could not allocate %d MTT entries\n", + hermon, num_pages ); + rc = mtt_offset; + goto err_mtt_offset; + } + mtt_base_addr = ( ( hermon->cap.reserved_mtts + mtt_offset ) * + hermon->cap.mtt_entry_size ); + + /* Fill in MTT structure */ + mtt->mtt_offset = mtt_offset; + mtt->num_pages = num_pages; + mtt->mtt_base_addr = mtt_base_addr; + mtt->page_offset = page_offset; + + /* Construct and issue WRITE_MTT commands */ + for ( i = 0 ; i < num_pages ; i++ ) { + memset ( &write_mtt, 0, sizeof ( write_mtt ) ); + MLX_FILL_1 ( &write_mtt.mtt_base_addr, 1, + value, mtt_base_addr ); + MLX_FILL_2 ( &write_mtt.mtt, 1, + p, 1, + ptag_l, ( start >> 3 ) ); + if ( ( rc = hermon_cmd_write_mtt ( hermon, + &write_mtt ) ) != 0 ) { + DBGC ( hermon, "Hermon %p could not write MTT at %x\n", + hermon, mtt_base_addr ); + goto err_write_mtt; + } + start += HERMON_PAGE_SIZE; + mtt_base_addr += hermon->cap.mtt_entry_size; + } + + return 0; + + err_write_mtt: + hermon_bitmask_free ( hermon->mtt_inuse, mtt_offset, num_pages ); + err_mtt_offset: + return rc; +} + +/** + * Free MTT entries + * + * @v hermon Hermon device + * @v mtt MTT descriptor + */ +static void hermon_free_mtt ( struct hermon *hermon, + struct hermon_mtt *mtt ) { + hermon_bitmask_free ( hermon->mtt_inuse, mtt->mtt_offset, + mtt->num_pages ); +} + +/*************************************************************************** + * + * MAD operations + * + *************************************************************************** + */ + +/** + * Issue management datagram + * + * @v ibdev Infiniband device + * @v mad Management datagram + * @ret rc Return status code + */ +static int hermon_mad ( struct ib_device *ibdev, union ib_mad *mad ) { + struct hermon *hermon = ib_get_drvdata ( ibdev ); + union hermonprm_mad mad_ifc; + int rc; + + linker_assert ( sizeof ( *mad ) == sizeof ( mad_ifc.mad ), + mad_size_mismatch ); + + /* Copy in request packet */ + memcpy ( &mad_ifc.mad, mad, sizeof ( mad_ifc.mad ) ); + + /* Issue MAD */ + if ( ( rc = hermon_cmd_mad_ifc ( hermon, ibdev->port, + &mad_ifc ) ) != 0 ) { + DBGC ( hermon, "Hermon %p could not issue MAD IFC: %s\n", + hermon, strerror ( rc ) ); + return rc; + } + + /* Copy out reply packet */ + memcpy ( mad, &mad_ifc.mad, sizeof ( *mad ) ); + + if ( mad->hdr.status != 0 ) { + DBGC ( hermon, "Hermon %p MAD IFC status %04x\n", + hermon, ntohs ( mad->hdr.status ) ); + return -EIO; + } + return 0; +} + +/*************************************************************************** + * + * Completion queue operations + * + *************************************************************************** + */ + +/** + * Create completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + * @ret rc Return status code + */ +static int hermon_create_cq ( struct ib_device *ibdev, + struct ib_completion_queue *cq ) { + struct hermon *hermon = ib_get_drvdata ( ibdev ); + struct hermon_completion_queue *hermon_cq; + struct hermonprm_completion_queue_context cqctx; + int cqn_offset; + unsigned int i; + int rc; + + /* Find a free completion queue number */ + cqn_offset = hermon_bitmask_alloc ( hermon->cq_inuse, + HERMON_MAX_CQS, 1 ); + if ( cqn_offset < 0 ) { + DBGC ( hermon, "Hermon %p out of completion queues\n", + hermon ); + rc = cqn_offset; + goto err_cqn_offset; + } + cq->cqn = ( hermon->cap.reserved_cqs + cqn_offset ); + + /* Allocate control structures */ + hermon_cq = zalloc ( sizeof ( *hermon_cq ) ); + if ( ! hermon_cq ) { + rc = -ENOMEM; + goto err_hermon_cq; + } + + /* Allocate completion queue itself */ + hermon_cq->cqe_size = ( cq->num_cqes * sizeof ( hermon_cq->cqe[0] ) ); + hermon_cq->cqe = malloc_dma ( hermon_cq->cqe_size, + sizeof ( hermon_cq->cqe[0] ) ); + if ( ! hermon_cq->cqe ) { + rc = -ENOMEM; + goto err_cqe; + } + memset ( hermon_cq->cqe, 0, hermon_cq->cqe_size ); + for ( i = 0 ; i < cq->num_cqes ; i++ ) { + MLX_FILL_1 ( &hermon_cq->cqe[i].normal, 7, owner, 1 ); + } + barrier(); + + /* Allocate MTT entries */ + if ( ( rc = hermon_alloc_mtt ( hermon, hermon_cq->cqe, + hermon_cq->cqe_size, + &hermon_cq->mtt ) ) != 0 ) + goto err_alloc_mtt; + + /* Hand queue over to hardware */ + memset ( &cqctx, 0, sizeof ( cqctx ) ); + MLX_FILL_1 ( &cqctx, 0, st, 0xa /* "Event fired" */ ); + MLX_FILL_1 ( &cqctx, 2, + page_offset, ( hermon_cq->mtt.page_offset >> 5 ) ); + MLX_FILL_2 ( &cqctx, 3, + usr_page, HERMON_UAR_NON_EQ_PAGE, + log_cq_size, fls ( cq->num_cqes - 1 ) ); + MLX_FILL_1 ( &cqctx, 7, mtt_base_addr_l, + ( hermon_cq->mtt.mtt_base_addr >> 3 ) ); + MLX_FILL_1 ( &cqctx, 15, db_record_addr_l, + ( virt_to_phys ( &hermon_cq->doorbell ) >> 3 ) ); + if ( ( rc = hermon_cmd_sw2hw_cq ( hermon, cq->cqn, &cqctx ) ) != 0 ) { + DBGC ( hermon, "Hermon %p SW2HW_CQ failed: %s\n", + hermon, strerror ( rc ) ); + goto err_sw2hw_cq; + } + + DBGC ( hermon, "Hermon %p CQN %#lx ring at [%p,%p)\n", + hermon, cq->cqn, hermon_cq->cqe, + ( ( ( void * ) hermon_cq->cqe ) + hermon_cq->cqe_size ) ); + ib_cq_set_drvdata ( cq, hermon_cq ); + return 0; + + err_sw2hw_cq: + hermon_free_mtt ( hermon, &hermon_cq->mtt ); + err_alloc_mtt: + free_dma ( hermon_cq->cqe, hermon_cq->cqe_size ); + err_cqe: + free ( hermon_cq ); + err_hermon_cq: + hermon_bitmask_free ( hermon->cq_inuse, cqn_offset, 1 ); + err_cqn_offset: + return rc; +} + +/** + * Destroy completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + */ +static void hermon_destroy_cq ( struct ib_device *ibdev, + struct ib_completion_queue *cq ) { + struct hermon *hermon = ib_get_drvdata ( ibdev ); + struct hermon_completion_queue *hermon_cq = ib_cq_get_drvdata ( cq ); + struct hermonprm_completion_queue_context cqctx; + int cqn_offset; + int rc; + + /* Take ownership back from hardware */ + if ( ( rc = hermon_cmd_hw2sw_cq ( hermon, cq->cqn, &cqctx ) ) != 0 ) { + DBGC ( hermon, "Hermon %p FATAL HW2SW_CQ failed on CQN %#lx: " + "%s\n", hermon, cq->cqn, strerror ( rc ) ); + /* Leak memory and return; at least we avoid corruption */ + return; + } + + /* Free MTT entries */ + hermon_free_mtt ( hermon, &hermon_cq->mtt ); + + /* Free memory */ + free_dma ( hermon_cq->cqe, hermon_cq->cqe_size ); + free ( hermon_cq ); + + /* Mark queue number as free */ + cqn_offset = ( cq->cqn - hermon->cap.reserved_cqs ); + hermon_bitmask_free ( hermon->cq_inuse, cqn_offset, 1 ); + + ib_cq_set_drvdata ( cq, NULL ); +} + +/*************************************************************************** + * + * Queue pair operations + * + *************************************************************************** + */ + +/** + * Create queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @ret rc Return status code + */ +static int hermon_create_qp ( struct ib_device *ibdev, + struct ib_queue_pair *qp ) { + struct hermon *hermon = ib_get_drvdata ( ibdev ); + struct hermon_queue_pair *hermon_qp; + struct hermonprm_qp_ee_state_transitions qpctx; + int qpn_offset; + int rc; + + /* Find a free queue pair number */ + qpn_offset = hermon_bitmask_alloc ( hermon->qp_inuse, + HERMON_MAX_QPS, 1 ); + if ( qpn_offset < 0 ) { + DBGC ( hermon, "Hermon %p out of queue pairs\n", hermon ); + rc = qpn_offset; + goto err_qpn_offset; + } + qp->qpn = ( HERMON_QPN_BASE + hermon->cap.reserved_qps + + qpn_offset ); + + /* Allocate control structures */ + hermon_qp = zalloc ( sizeof ( *hermon_qp ) ); + if ( ! hermon_qp ) { + rc = -ENOMEM; + goto err_hermon_qp; + } + + /* Calculate doorbell address */ + hermon_qp->send.doorbell = + ( hermon->uar + HERMON_UAR_NON_EQ_PAGE * HERMON_PAGE_SIZE + + HERMON_DB_POST_SND_OFFSET ); + + /* Allocate work queue buffer */ + hermon_qp->send.num_wqes = ( qp->send.num_wqes /* headroom */ + 1 + + ( 2048 / sizeof ( hermon_qp->send.wqe[0] ) ) ); + hermon_qp->send.num_wqes = + ( 1 << fls ( hermon_qp->send.num_wqes - 1 ) ); /* round up */ + hermon_qp->send.wqe_size = ( hermon_qp->send.num_wqes * + sizeof ( hermon_qp->send.wqe[0] ) ); + hermon_qp->recv.wqe_size = ( qp->recv.num_wqes * + sizeof ( hermon_qp->recv.wqe[0] ) ); + hermon_qp->wqe_size = ( hermon_qp->send.wqe_size + + hermon_qp->recv.wqe_size ); + hermon_qp->wqe = malloc_dma ( hermon_qp->wqe_size, + sizeof ( hermon_qp->send.wqe[0] ) ); + if ( ! hermon_qp->wqe ) { + rc = -ENOMEM; + goto err_alloc_wqe; + } + hermon_qp->send.wqe = hermon_qp->wqe; + memset ( hermon_qp->send.wqe, 0xff, hermon_qp->send.wqe_size ); + hermon_qp->recv.wqe = ( hermon_qp->wqe + hermon_qp->send.wqe_size ); + memset ( hermon_qp->recv.wqe, 0, hermon_qp->recv.wqe_size ); + + /* Allocate MTT entries */ + if ( ( rc = hermon_alloc_mtt ( hermon, hermon_qp->wqe, + hermon_qp->wqe_size, + &hermon_qp->mtt ) ) != 0 ) { + goto err_alloc_mtt; + } + + /* Transition queue to INIT state */ + memset ( &qpctx, 0, sizeof ( qpctx ) ); + MLX_FILL_2 ( &qpctx, 2, + qpc_eec_data.pm_state, 0x03 /* Always 0x03 for UD */, + qpc_eec_data.st, HERMON_ST_UD ); + MLX_FILL_1 ( &qpctx, 3, qpc_eec_data.pd, HERMON_GLOBAL_PD ); + MLX_FILL_4 ( &qpctx, 4, + qpc_eec_data.log_rq_size, fls ( qp->recv.num_wqes - 1 ), + qpc_eec_data.log_rq_stride, + ( fls ( sizeof ( hermon_qp->recv.wqe[0] ) - 1 ) - 4 ), + qpc_eec_data.log_sq_size, + fls ( hermon_qp->send.num_wqes - 1 ), + qpc_eec_data.log_sq_stride, + ( fls ( sizeof ( hermon_qp->send.wqe[0] ) - 1 ) - 4 ) ); + MLX_FILL_1 ( &qpctx, 5, + qpc_eec_data.usr_page, HERMON_UAR_NON_EQ_PAGE ); + MLX_FILL_1 ( &qpctx, 33, qpc_eec_data.cqn_snd, qp->send.cq->cqn ); + MLX_FILL_1 ( &qpctx, 38, qpc_eec_data.page_offset, + ( hermon_qp->mtt.page_offset >> 6 ) ); + MLX_FILL_1 ( &qpctx, 41, qpc_eec_data.cqn_rcv, qp->recv.cq->cqn ); + MLX_FILL_1 ( &qpctx, 43, qpc_eec_data.db_record_addr_l, + ( virt_to_phys ( &hermon_qp->recv.doorbell ) >> 2 ) ); + MLX_FILL_1 ( &qpctx, 44, qpc_eec_data.q_key, qp->qkey ); + MLX_FILL_1 ( &qpctx, 53, qpc_eec_data.mtt_base_addr_l, + ( hermon_qp->mtt.mtt_base_addr >> 3 ) ); + if ( ( rc = hermon_cmd_rst2init_qp ( hermon, qp->qpn, + &qpctx ) ) != 0 ) { + DBGC ( hermon, "Hermon %p RST2INIT_QP failed: %s\n", + hermon, strerror ( rc ) ); + goto err_rst2init_qp; + } + + /* Transition queue to RTR state */ + memset ( &qpctx, 0, sizeof ( qpctx ) ); + MLX_FILL_2 ( &qpctx, 4, + qpc_eec_data.mtu, HERMON_MTU_2048, + qpc_eec_data.msg_max, 11 /* 2^11 = 2048 */ ); + MLX_FILL_1 ( &qpctx, 16, + qpc_eec_data.primary_address_path.sched_queue, + ( 0x83 /* default policy */ | + ( ( ibdev->port - 1 ) << 6 ) ) ); + if ( ( rc = hermon_cmd_init2rtr_qp ( hermon, qp->qpn, + &qpctx ) ) != 0 ) { + DBGC ( hermon, "Hermon %p INIT2RTR_QP failed: %s\n", + hermon, strerror ( rc ) ); + goto err_init2rtr_qp; + } + memset ( &qpctx, 0, sizeof ( qpctx ) ); + if ( ( rc = hermon_cmd_rtr2rts_qp ( hermon, qp->qpn, &qpctx ) ) != 0 ){ + DBGC ( hermon, "Hermon %p RTR2RTS_QP failed: %s\n", + hermon, strerror ( rc ) ); + goto err_rtr2rts_qp; + } + + DBGC ( hermon, "Hermon %p QPN %#lx send ring at [%p,%p)\n", + hermon, qp->qpn, hermon_qp->send.wqe, + ( ((void *)hermon_qp->send.wqe ) + hermon_qp->send.wqe_size ) ); + DBGC ( hermon, "Hermon %p QPN %#lx receive ring at [%p,%p)\n", + hermon, qp->qpn, hermon_qp->recv.wqe, + ( ((void *)hermon_qp->recv.wqe ) + hermon_qp->recv.wqe_size ) ); + ib_qp_set_drvdata ( qp, hermon_qp ); + return 0; + + err_rtr2rts_qp: + err_init2rtr_qp: + hermon_cmd_2rst_qp ( hermon, qp->qpn ); + err_rst2init_qp: + hermon_free_mtt ( hermon, &hermon_qp->mtt ); + err_alloc_mtt: + free_dma ( hermon_qp->wqe, hermon_qp->wqe_size ); + err_alloc_wqe: + free ( hermon_qp ); + err_hermon_qp: + hermon_bitmask_free ( hermon->qp_inuse, qpn_offset, 1 ); + err_qpn_offset: + return rc; +} + +/** + * Modify queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v mod_list Modification list + * @ret rc Return status code + */ +static int hermon_modify_qp ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + unsigned long mod_list ) { + struct hermon *hermon = ib_get_drvdata ( ibdev ); + struct hermonprm_qp_ee_state_transitions qpctx; + unsigned long optparammask = 0; + int rc; + + /* Construct optparammask */ + if ( mod_list & IB_MODIFY_QKEY ) + optparammask |= HERMON_QP_OPT_PARAM_QKEY; + + /* Issue RTS2RTS_QP */ + memset ( &qpctx, 0, sizeof ( qpctx ) ); + MLX_FILL_1 ( &qpctx, 0, opt_param_mask, optparammask ); + MLX_FILL_1 ( &qpctx, 44, qpc_eec_data.q_key, qp->qkey ); + if ( ( rc = hermon_cmd_rts2rts_qp ( hermon, qp->qpn, &qpctx ) ) != 0 ){ + DBGC ( hermon, "Hermon %p RTS2RTS_QP failed: %s\n", + hermon, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Destroy queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + */ +static void hermon_destroy_qp ( struct ib_device *ibdev, + struct ib_queue_pair *qp ) { + struct hermon *hermon = ib_get_drvdata ( ibdev ); + struct hermon_queue_pair *hermon_qp = ib_qp_get_drvdata ( qp ); + int qpn_offset; + int rc; + + /* Take ownership back from hardware */ + if ( ( rc = hermon_cmd_2rst_qp ( hermon, qp->qpn ) ) != 0 ) { + DBGC ( hermon, "Hermon %p FATAL 2RST_QP failed on QPN %#lx: " + "%s\n", hermon, qp->qpn, strerror ( rc ) ); + /* Leak memory and return; at least we avoid corruption */ + return; + } + + /* Free MTT entries */ + hermon_free_mtt ( hermon, &hermon_qp->mtt ); + + /* Free memory */ + free_dma ( hermon_qp->wqe, hermon_qp->wqe_size ); + free ( hermon_qp ); + + /* Mark queue number as free */ + qpn_offset = ( qp->qpn - HERMON_QPN_BASE - + hermon->cap.reserved_qps ); + hermon_bitmask_free ( hermon->qp_inuse, qpn_offset, 1 ); + + ib_qp_set_drvdata ( qp, NULL ); +} + +/*************************************************************************** + * + * Work request operations + * + *************************************************************************** + */ + +/** GID used for GID-less send work queue entries */ +static const struct ib_gid hermon_no_gid = { + { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } } +}; + +/** + * Post send work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v av Address vector + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int hermon_post_send ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct ib_address_vector *av, + struct io_buffer *iobuf ) { + struct hermon *hermon = ib_get_drvdata ( ibdev ); + struct hermon_queue_pair *hermon_qp = ib_qp_get_drvdata ( qp ); + struct ib_work_queue *wq = &qp->send; + struct hermon_send_work_queue *hermon_send_wq = &hermon_qp->send; + struct hermonprm_ud_send_wqe *wqe; + const struct ib_gid *gid; + union hermonprm_doorbell_register db_reg; + unsigned int wqe_idx_mask; + + /* Allocate work queue entry */ + wqe_idx_mask = ( wq->num_wqes - 1 ); + if ( wq->iobufs[wq->next_idx & wqe_idx_mask] ) { + DBGC ( hermon, "Hermon %p send queue full", hermon ); + return -ENOBUFS; + } + wq->iobufs[wq->next_idx & wqe_idx_mask] = iobuf; + wqe = &hermon_send_wq->wqe[ wq->next_idx & + ( hermon_send_wq->num_wqes - 1 ) ].ud; + + /* Construct work queue entry */ + memset ( ( ( ( void * ) wqe ) + 4 /* avoid ctrl.owner */ ), 0, + ( sizeof ( *wqe ) - 4 ) ); + MLX_FILL_1 ( &wqe->ctrl, 1, ds, ( sizeof ( *wqe ) / 16 ) ); + MLX_FILL_1 ( &wqe->ctrl, 2, c, 0x03 /* generate completion */ ); + MLX_FILL_2 ( &wqe->ud, 0, + ud_address_vector.pd, HERMON_GLOBAL_PD, + ud_address_vector.port_number, ibdev->port ); + MLX_FILL_2 ( &wqe->ud, 1, + ud_address_vector.rlid, av->lid, + ud_address_vector.g, av->gid_present ); + MLX_FILL_1 ( &wqe->ud, 2, + ud_address_vector.max_stat_rate, + ( ( ( av->rate < 2 ) || ( av->rate > 10 ) ) ? + 8 : ( av->rate + 5 ) ) ); + MLX_FILL_1 ( &wqe->ud, 3, ud_address_vector.sl, av->sl ); + gid = ( av->gid_present ? &av->gid : &hermon_no_gid ); + memcpy ( &wqe->ud.u.dwords[4], gid, sizeof ( *gid ) ); + MLX_FILL_1 ( &wqe->ud, 8, destination_qp, av->qpn ); + MLX_FILL_1 ( &wqe->ud, 9, q_key, av->qkey ); + MLX_FILL_1 ( &wqe->data[0], 0, byte_count, iob_len ( iobuf ) ); + MLX_FILL_1 ( &wqe->data[0], 1, l_key, hermon->reserved_lkey ); + MLX_FILL_1 ( &wqe->data[0], 3, + local_address_l, virt_to_bus ( iobuf->data ) ); + barrier(); + MLX_FILL_2 ( &wqe->ctrl, 0, + opcode, HERMON_OPCODE_SEND, + owner, + ( ( wq->next_idx & hermon_send_wq->num_wqes ) ? 1 : 0 ) ); + DBGCP ( hermon, "Hermon %p posting send WQE:\n", hermon ); + DBGCP_HD ( hermon, wqe, sizeof ( *wqe ) ); + barrier(); + + /* Ring doorbell register */ + MLX_FILL_1 ( &db_reg.send, 0, qn, qp->qpn ); + DBGCP ( hermon, "Ringing doorbell %08lx with %08x\n", + virt_to_phys ( hermon_send_wq->doorbell ), db_reg.dword[0] ); + writel ( db_reg.dword[0], ( hermon_send_wq->doorbell ) ); + + /* Update work queue's index */ + wq->next_idx++; + + return 0; +} + +/** + * Post receive work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int hermon_post_recv ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct io_buffer *iobuf ) { + struct hermon *hermon = ib_get_drvdata ( ibdev ); + struct hermon_queue_pair *hermon_qp = ib_qp_get_drvdata ( qp ); + struct ib_work_queue *wq = &qp->recv; + struct hermon_recv_work_queue *hermon_recv_wq = &hermon_qp->recv; + struct hermonprm_recv_wqe *wqe; + unsigned int wqe_idx_mask; + + /* Allocate work queue entry */ + wqe_idx_mask = ( wq->num_wqes - 1 ); + if ( wq->iobufs[wq->next_idx & wqe_idx_mask] ) { + DBGC ( hermon, "Hermon %p receive queue full", hermon ); + return -ENOBUFS; + } + wq->iobufs[wq->next_idx & wqe_idx_mask] = iobuf; + wqe = &hermon_recv_wq->wqe[wq->next_idx & wqe_idx_mask].recv; + + /* Construct work queue entry */ + MLX_FILL_1 ( &wqe->data[0], 0, byte_count, iob_tailroom ( iobuf ) ); + MLX_FILL_1 ( &wqe->data[0], 1, l_key, hermon->reserved_lkey ); + MLX_FILL_1 ( &wqe->data[0], 3, + local_address_l, virt_to_bus ( iobuf->data ) ); + + /* Update work queue's index */ + wq->next_idx++; + + /* Update doorbell record */ + barrier(); + MLX_FILL_1 ( &hermon_recv_wq->doorbell, 0, receive_wqe_counter, + ( wq->next_idx & 0xffff ) ); + + return 0; +} + +/** + * Handle completion + * + * @v ibdev Infiniband device + * @v cq Completion queue + * @v cqe Hardware completion queue entry + * @ret rc Return status code + */ +static int hermon_complete ( struct ib_device *ibdev, + struct ib_completion_queue *cq, + union hermonprm_completion_entry *cqe ) { + struct hermon *hermon = ib_get_drvdata ( ibdev ); + struct ib_work_queue *wq; + struct ib_queue_pair *qp; + struct hermon_queue_pair *hermon_qp; + struct io_buffer *iobuf; + struct ib_address_vector av; + struct ib_global_route_header *grh; + unsigned int opcode; + unsigned long qpn; + int is_send; + unsigned int wqe_idx; + size_t len; + int rc = 0; + + /* Parse completion */ + qpn = MLX_GET ( &cqe->normal, qpn ); + is_send = MLX_GET ( &cqe->normal, s_r ); + opcode = MLX_GET ( &cqe->normal, opcode ); + if ( opcode >= HERMON_OPCODE_RECV_ERROR ) { + /* "s" field is not valid for error opcodes */ + is_send = ( opcode == HERMON_OPCODE_SEND_ERROR ); + DBGC ( hermon, "Hermon %p CQN %lx syndrome %x vendor %x\n", + hermon, cq->cqn, MLX_GET ( &cqe->error, syndrome ), + MLX_GET ( &cqe->error, vendor_error_syndrome ) ); + rc = -EIO; + /* Don't return immediately; propagate error to completer */ + } + + /* Identify work queue */ + wq = ib_find_wq ( cq, qpn, is_send ); + if ( ! wq ) { + DBGC ( hermon, "Hermon %p CQN %lx unknown %s QPN %lx\n", + hermon, cq->cqn, ( is_send ? "send" : "recv" ), qpn ); + return -EIO; + } + qp = wq->qp; + hermon_qp = ib_qp_get_drvdata ( qp ); + + /* Identify I/O buffer */ + wqe_idx = ( MLX_GET ( &cqe->normal, wqe_counter ) & + ( wq->num_wqes - 1 ) ); + iobuf = wq->iobufs[wqe_idx]; + if ( ! iobuf ) { + DBGC ( hermon, "Hermon %p CQN %lx QPN %lx empty WQE %x\n", + hermon, cq->cqn, qpn, wqe_idx ); + return -EIO; + } + wq->iobufs[wqe_idx] = NULL; + + if ( is_send ) { + /* Hand off to completion handler */ + ib_complete_send ( ibdev, qp, iobuf, rc ); + } else { + /* Set received length */ + len = MLX_GET ( &cqe->normal, byte_cnt ); + assert ( len <= iob_tailroom ( iobuf ) ); + iob_put ( iobuf, len ); + assert ( iob_len ( iobuf ) >= sizeof ( *grh ) ); + grh = iobuf->data; + iob_pull ( iobuf, sizeof ( *grh ) ); + /* Construct address vector */ + memset ( &av, 0, sizeof ( av ) ); + av.qpn = MLX_GET ( &cqe->normal, srq_rqpn ); + av.lid = MLX_GET ( &cqe->normal, slid_smac47_32 ); + av.sl = MLX_GET ( &cqe->normal, sl ); + av.gid_present = MLX_GET ( &cqe->normal, g ); + memcpy ( &av.gid, &grh->sgid, sizeof ( av.gid ) ); + /* Hand off to completion handler */ + ib_complete_recv ( ibdev, qp, &av, iobuf, rc ); + } + + return rc; +} + +/** + * Poll completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + */ +static void hermon_poll_cq ( struct ib_device *ibdev, + struct ib_completion_queue *cq ) { + struct hermon *hermon = ib_get_drvdata ( ibdev ); + struct hermon_completion_queue *hermon_cq = ib_cq_get_drvdata ( cq ); + union hermonprm_completion_entry *cqe; + unsigned int cqe_idx_mask; + int rc; + + while ( 1 ) { + /* Look for completion entry */ + cqe_idx_mask = ( cq->num_cqes - 1 ); + cqe = &hermon_cq->cqe[cq->next_idx & cqe_idx_mask]; + if ( MLX_GET ( &cqe->normal, owner ) ^ + ( ( cq->next_idx & cq->num_cqes ) ? 1 : 0 ) ) { + /* Entry still owned by hardware; end of poll */ + break; + } + DBGCP ( hermon, "Hermon %p completion:\n", hermon ); + DBGCP_HD ( hermon, cqe, sizeof ( *cqe ) ); + + /* Handle completion */ + if ( ( rc = hermon_complete ( ibdev, cq, cqe ) ) != 0 ) { + DBGC ( hermon, "Hermon %p failed to complete: %s\n", + hermon, strerror ( rc ) ); + DBGC_HD ( hermon, cqe, sizeof ( *cqe ) ); + } + + /* Update completion queue's index */ + cq->next_idx++; + + /* Update doorbell record */ + MLX_FILL_1 ( &hermon_cq->doorbell, 0, update_ci, + ( cq->next_idx & 0x00ffffffUL ) ); + } +} + +/*************************************************************************** + * + * Event queues + * + *************************************************************************** + */ + +/** + * Create event queue + * + * @v hermon Hermon device + * @ret rc Return status code + */ +static int hermon_create_eq ( struct hermon *hermon ) { + struct hermon_event_queue *hermon_eq = &hermon->eq; + struct hermonprm_eqc eqctx; + struct hermonprm_event_mask mask; + unsigned int i; + int rc; + + /* Select event queue number */ + hermon_eq->eqn = ( 4 * hermon->cap.reserved_uars ); + if ( hermon_eq->eqn < hermon->cap.reserved_eqs ) + hermon_eq->eqn = hermon->cap.reserved_eqs; + + /* Calculate doorbell address */ + hermon_eq->doorbell = + ( hermon->uar + HERMON_DB_EQ_OFFSET ( hermon_eq->eqn ) ); + + /* Allocate event queue itself */ + hermon_eq->eqe_size = + ( HERMON_NUM_EQES * sizeof ( hermon_eq->eqe[0] ) ); + hermon_eq->eqe = malloc_dma ( hermon_eq->eqe_size, + sizeof ( hermon_eq->eqe[0] ) ); + if ( ! hermon_eq->eqe ) { + rc = -ENOMEM; + goto err_eqe; + } + memset ( hermon_eq->eqe, 0, hermon_eq->eqe_size ); + for ( i = 0 ; i < HERMON_NUM_EQES ; i++ ) { + MLX_FILL_1 ( &hermon_eq->eqe[i].generic, 7, owner, 1 ); + } + barrier(); + + /* Allocate MTT entries */ + if ( ( rc = hermon_alloc_mtt ( hermon, hermon_eq->eqe, + hermon_eq->eqe_size, + &hermon_eq->mtt ) ) != 0 ) + goto err_alloc_mtt; + + /* Hand queue over to hardware */ + memset ( &eqctx, 0, sizeof ( eqctx ) ); + MLX_FILL_1 ( &eqctx, 0, st, 0xa /* "Fired" */ ); + MLX_FILL_1 ( &eqctx, 2, + page_offset, ( hermon_eq->mtt.page_offset >> 5 ) ); + MLX_FILL_1 ( &eqctx, 3, log_eq_size, fls ( HERMON_NUM_EQES - 1 ) ); + MLX_FILL_1 ( &eqctx, 7, mtt_base_addr_l, + ( hermon_eq->mtt.mtt_base_addr >> 3 ) ); + if ( ( rc = hermon_cmd_sw2hw_eq ( hermon, hermon_eq->eqn, + &eqctx ) ) != 0 ) { + DBGC ( hermon, "Hermon %p SW2HW_EQ failed: %s\n", + hermon, strerror ( rc ) ); + goto err_sw2hw_eq; + } + + /* Map events to this event queue */ + memset ( &mask, 0, sizeof ( mask ) ); + MLX_FILL_1 ( &mask, 1, port_state_change, 1 ); + if ( ( rc = hermon_cmd_map_eq ( hermon, + ( HERMON_MAP_EQ | hermon_eq->eqn ), + &mask ) ) != 0 ) { + DBGC ( hermon, "Hermon %p MAP_EQ failed: %s\n", + hermon, strerror ( rc ) ); + goto err_map_eq; + } + + DBGC ( hermon, "Hermon %p EQN %#lx ring at [%p,%p])\n", + hermon, hermon_eq->eqn, hermon_eq->eqe, + ( ( ( void * ) hermon_eq->eqe ) + hermon_eq->eqe_size ) ); + return 0; + + err_map_eq: + hermon_cmd_hw2sw_eq ( hermon, hermon_eq->eqn, &eqctx ); + err_sw2hw_eq: + hermon_free_mtt ( hermon, &hermon_eq->mtt ); + err_alloc_mtt: + free_dma ( hermon_eq->eqe, hermon_eq->eqe_size ); + err_eqe: + memset ( hermon_eq, 0, sizeof ( *hermon_eq ) ); + return rc; +} + +/** + * Destroy event queue + * + * @v hermon Hermon device + */ +static void hermon_destroy_eq ( struct hermon *hermon ) { + struct hermon_event_queue *hermon_eq = &hermon->eq; + struct hermonprm_eqc eqctx; + struct hermonprm_event_mask mask; + int rc; + + /* Unmap events from event queue */ + memset ( &mask, 0, sizeof ( mask ) ); + MLX_FILL_1 ( &mask, 1, port_state_change, 1 ); + if ( ( rc = hermon_cmd_map_eq ( hermon, + ( HERMON_UNMAP_EQ | hermon_eq->eqn ), + &mask ) ) != 0 ) { + DBGC ( hermon, "Hermon %p FATAL MAP_EQ failed to unmap: %s\n", + hermon, strerror ( rc ) ); + /* Continue; HCA may die but system should survive */ + } + + /* Take ownership back from hardware */ + if ( ( rc = hermon_cmd_hw2sw_eq ( hermon, hermon_eq->eqn, + &eqctx ) ) != 0 ) { + DBGC ( hermon, "Hermon %p FATAL HW2SW_EQ failed: %s\n", + hermon, strerror ( rc ) ); + /* Leak memory and return; at least we avoid corruption */ + return; + } + + /* Free MTT entries */ + hermon_free_mtt ( hermon, &hermon_eq->mtt ); + + /* Free memory */ + free_dma ( hermon_eq->eqe, hermon_eq->eqe_size ); + memset ( hermon_eq, 0, sizeof ( *hermon_eq ) ); +} + +/** + * Handle port state event + * + * @v hermon Hermon device + * @v eqe Port state change event queue entry + */ +static void hermon_event_port_state_change ( struct hermon *hermon, + union hermonprm_event_entry *eqe){ + unsigned int port; + int link_up; + + /* Get port and link status */ + port = ( MLX_GET ( &eqe->port_state_change, data.p ) - 1 ); + link_up = ( MLX_GET ( &eqe->generic, event_sub_type ) & 0x04 ); + DBGC ( hermon, "Hermon %p port %d link %s\n", hermon, ( port + 1 ), + ( link_up ? "up" : "down" ) ); + + /* Sanity check */ + if ( port >= HERMON_NUM_PORTS ) { + DBGC ( hermon, "Hermon %p port %d does not exist!\n", + hermon, ( port + 1 ) ); + return; + } + + /* Update MAD parameters */ + ib_smc_update ( hermon->ibdev[port], hermon_mad ); + + /* Notify Infiniband core of link state change */ + ib_link_state_changed ( hermon->ibdev[port] ); +} + +/** + * Poll event queue + * + * @v ibdev Infiniband device + */ +static void hermon_poll_eq ( struct ib_device *ibdev ) { + struct hermon *hermon = ib_get_drvdata ( ibdev ); + struct hermon_event_queue *hermon_eq = &hermon->eq; + union hermonprm_event_entry *eqe; + union hermonprm_doorbell_register db_reg; + unsigned int eqe_idx_mask; + unsigned int event_type; + + while ( 1 ) { + /* Look for event entry */ + eqe_idx_mask = ( HERMON_NUM_EQES - 1 ); + eqe = &hermon_eq->eqe[hermon_eq->next_idx & eqe_idx_mask]; + if ( MLX_GET ( &eqe->generic, owner ) ^ + ( ( hermon_eq->next_idx & HERMON_NUM_EQES ) ? 1 : 0 ) ) { + /* Entry still owned by hardware; end of poll */ + break; + } + DBGCP ( hermon, "Hermon %p event:\n", hermon ); + DBGCP_HD ( hermon, eqe, sizeof ( *eqe ) ); + + /* Handle event */ + event_type = MLX_GET ( &eqe->generic, event_type ); + switch ( event_type ) { + case HERMON_EV_PORT_STATE_CHANGE: + hermon_event_port_state_change ( hermon, eqe ); + break; + default: + DBGC ( hermon, "Hermon %p unrecognised event type " + "%#x:\n", hermon, event_type ); + DBGC_HD ( hermon, eqe, sizeof ( *eqe ) ); + break; + } + + /* Update event queue's index */ + hermon_eq->next_idx++; + + /* Ring doorbell */ + MLX_FILL_1 ( &db_reg.event, 0, + ci, ( hermon_eq->next_idx & 0x00ffffffUL ) ); + DBGCP ( hermon, "Ringing doorbell %08lx with %08x\n", + virt_to_phys ( hermon_eq->doorbell ), + db_reg.dword[0] ); + writel ( db_reg.dword[0], hermon_eq->doorbell ); + } +} + +/*************************************************************************** + * + * Infiniband link-layer operations + * + *************************************************************************** + */ + +/** + * Initialise Infiniband link + * + * @v ibdev Infiniband device + * @ret rc Return status code + */ +static int hermon_open ( struct ib_device *ibdev ) { + struct hermon *hermon = ib_get_drvdata ( ibdev ); + struct hermonprm_init_port init_port; + int rc; + + memset ( &init_port, 0, sizeof ( init_port ) ); + MLX_FILL_2 ( &init_port, 0, + port_width_cap, 3, + vl_cap, 1 ); + MLX_FILL_2 ( &init_port, 1, + mtu, HERMON_MTU_2048, + max_gid, 1 ); + MLX_FILL_1 ( &init_port, 2, max_pkey, 64 ); + if ( ( rc = hermon_cmd_init_port ( hermon, ibdev->port, + &init_port ) ) != 0 ) { + DBGC ( hermon, "Hermon %p could not intialise port: %s\n", + hermon, strerror ( rc ) ); + return rc; + } + + /* Update MAD parameters */ + ib_smc_update ( ibdev, hermon_mad ); + + return 0; +} + +/** + * Close Infiniband link + * + * @v ibdev Infiniband device + */ +static void hermon_close ( struct ib_device *ibdev ) { + struct hermon *hermon = ib_get_drvdata ( ibdev ); + int rc; + + if ( ( rc = hermon_cmd_close_port ( hermon, ibdev->port ) ) != 0 ) { + DBGC ( hermon, "Hermon %p could not close port: %s\n", + hermon, strerror ( rc ) ); + /* Nothing we can do about this */ + } +} + +/*************************************************************************** + * + * Multicast group operations + * + *************************************************************************** + */ + +/** + * Attach to multicast group + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v gid Multicast GID + * @ret rc Return status code + */ +static int hermon_mcast_attach ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct ib_gid *gid ) { + struct hermon *hermon = ib_get_drvdata ( ibdev ); + struct hermonprm_mgm_hash hash; + struct hermonprm_mcg_entry mcg; + unsigned int index; + int rc; + + /* Generate hash table index */ + if ( ( rc = hermon_cmd_mgid_hash ( hermon, gid, &hash ) ) != 0 ) { + DBGC ( hermon, "Hermon %p could not hash GID: %s\n", + hermon, strerror ( rc ) ); + return rc; + } + index = MLX_GET ( &hash, hash ); + + /* Check for existing hash table entry */ + if ( ( rc = hermon_cmd_read_mcg ( hermon, index, &mcg ) ) != 0 ) { + DBGC ( hermon, "Hermon %p could not read MCG %#x: %s\n", + hermon, index, strerror ( rc ) ); + return rc; + } + if ( MLX_GET ( &mcg, hdr.members_count ) != 0 ) { + /* FIXME: this implementation allows only a single QP + * per multicast group, and doesn't handle hash + * collisions. Sufficient for IPoIB but may need to + * be extended in future. + */ + DBGC ( hermon, "Hermon %p MGID index %#x already in use\n", + hermon, index ); + return -EBUSY; + } + + /* Update hash table entry */ + MLX_FILL_1 ( &mcg, 1, hdr.members_count, 1 ); + MLX_FILL_1 ( &mcg, 8, qp[0].qpn, qp->qpn ); + memcpy ( &mcg.u.dwords[4], gid, sizeof ( *gid ) ); + if ( ( rc = hermon_cmd_write_mcg ( hermon, index, &mcg ) ) != 0 ) { + DBGC ( hermon, "Hermon %p could not write MCG %#x: %s\n", + hermon, index, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Detach from multicast group + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v gid Multicast GID + */ +static void hermon_mcast_detach ( struct ib_device *ibdev, + struct ib_queue_pair *qp __unused, + struct ib_gid *gid ) { + struct hermon *hermon = ib_get_drvdata ( ibdev ); + struct hermonprm_mgm_hash hash; + struct hermonprm_mcg_entry mcg; + unsigned int index; + int rc; + + /* Generate hash table index */ + if ( ( rc = hermon_cmd_mgid_hash ( hermon, gid, &hash ) ) != 0 ) { + DBGC ( hermon, "Hermon %p could not hash GID: %s\n", + hermon, strerror ( rc ) ); + return; + } + index = MLX_GET ( &hash, hash ); + + /* Clear hash table entry */ + memset ( &mcg, 0, sizeof ( mcg ) ); + if ( ( rc = hermon_cmd_write_mcg ( hermon, index, &mcg ) ) != 0 ) { + DBGC ( hermon, "Hermon %p could not write MCG %#x: %s\n", + hermon, index, strerror ( rc ) ); + return; + } +} + +/** Hermon Infiniband operations */ +static struct ib_device_operations hermon_ib_operations = { + .create_cq = hermon_create_cq, + .destroy_cq = hermon_destroy_cq, + .create_qp = hermon_create_qp, + .modify_qp = hermon_modify_qp, + .destroy_qp = hermon_destroy_qp, + .post_send = hermon_post_send, + .post_recv = hermon_post_recv, + .poll_cq = hermon_poll_cq, + .poll_eq = hermon_poll_eq, + .open = hermon_open, + .close = hermon_close, + .mcast_attach = hermon_mcast_attach, + .mcast_detach = hermon_mcast_detach, +}; + +/*************************************************************************** + * + * Firmware control + * + *************************************************************************** + */ + +/** + * Map virtual to physical address for firmware usage + * + * @v hermon Hermon device + * @v map Mapping function + * @v va Virtual address + * @v pa Physical address + * @v len Length of region + * @ret rc Return status code + */ +static int hermon_map_vpm ( struct hermon *hermon, + int ( *map ) ( struct hermon *hermon, + const struct hermonprm_virtual_physical_mapping* ), + uint64_t va, physaddr_t pa, size_t len ) { + struct hermonprm_virtual_physical_mapping mapping; + int rc; + + assert ( ( va & ( HERMON_PAGE_SIZE - 1 ) ) == 0 ); + assert ( ( pa & ( HERMON_PAGE_SIZE - 1 ) ) == 0 ); + assert ( ( len & ( HERMON_PAGE_SIZE - 1 ) ) == 0 ); + + while ( len ) { + memset ( &mapping, 0, sizeof ( mapping ) ); + MLX_FILL_1 ( &mapping, 0, va_h, ( va >> 32 ) ); + MLX_FILL_1 ( &mapping, 1, va_l, ( va >> 12 ) ); + MLX_FILL_2 ( &mapping, 3, + log2size, 0, + pa_l, ( pa >> 12 ) ); + if ( ( rc = map ( hermon, &mapping ) ) != 0 ) { + DBGC ( hermon, "Hermon %p could not map %llx => %lx: " + "%s\n", hermon, va, pa, strerror ( rc ) ); + return rc; + } + pa += HERMON_PAGE_SIZE; + va += HERMON_PAGE_SIZE; + len -= HERMON_PAGE_SIZE; + } + + return 0; +} + +/** + * Start firmware running + * + * @v hermon Hermon device + * @ret rc Return status code + */ +static int hermon_start_firmware ( struct hermon *hermon ) { + struct hermonprm_query_fw fw; + unsigned int fw_pages; + size_t fw_size; + physaddr_t fw_base; + int rc; + + /* Get firmware parameters */ + if ( ( rc = hermon_cmd_query_fw ( hermon, &fw ) ) != 0 ) { + DBGC ( hermon, "Hermon %p could not query firmware: %s\n", + hermon, strerror ( rc ) ); + goto err_query_fw; + } + DBGC ( hermon, "Hermon %p firmware version %d.%d.%d\n", hermon, + MLX_GET ( &fw, fw_rev_major ), MLX_GET ( &fw, fw_rev_minor ), + MLX_GET ( &fw, fw_rev_subminor ) ); + fw_pages = MLX_GET ( &fw, fw_pages ); + DBGC ( hermon, "Hermon %p requires %d pages (%d kB) for firmware\n", + hermon, fw_pages, ( fw_pages * ( HERMON_PAGE_SIZE / 1024 ) ) ); + + /* Allocate firmware pages and map firmware area */ + fw_size = ( fw_pages * HERMON_PAGE_SIZE ); + hermon->firmware_area = umalloc ( fw_size ); + if ( ! hermon->firmware_area ) { + rc = -ENOMEM; + goto err_alloc_fa; + } + fw_base = user_to_phys ( hermon->firmware_area, 0 ); + DBGC ( hermon, "Hermon %p firmware area at physical [%lx,%lx)\n", + hermon, fw_base, ( fw_base + fw_size ) ); + if ( ( rc = hermon_map_vpm ( hermon, hermon_cmd_map_fa, + 0, fw_base, fw_size ) ) != 0 ) { + DBGC ( hermon, "Hermon %p could not map firmware: %s\n", + hermon, strerror ( rc ) ); + goto err_map_fa; + } + + /* Start firmware */ + if ( ( rc = hermon_cmd_run_fw ( hermon ) ) != 0 ) { + DBGC ( hermon, "Hermon %p could not run firmware: %s\n", + hermon, strerror ( rc ) ); + goto err_run_fw; + } + + DBGC ( hermon, "Hermon %p firmware started\n", hermon ); + return 0; + + err_run_fw: + err_map_fa: + hermon_cmd_unmap_fa ( hermon ); + ufree ( hermon->firmware_area ); + hermon->firmware_area = UNULL; + err_alloc_fa: + err_query_fw: + return rc; +} + +/** + * Stop firmware running + * + * @v hermon Hermon device + */ +static void hermon_stop_firmware ( struct hermon *hermon ) { + int rc; + + if ( ( rc = hermon_cmd_unmap_fa ( hermon ) ) != 0 ) { + DBGC ( hermon, "Hermon %p FATAL could not stop firmware: %s\n", + hermon, strerror ( rc ) ); + /* Leak memory and return; at least we avoid corruption */ + return; + } + ufree ( hermon->firmware_area ); + hermon->firmware_area = UNULL; +} + +/*************************************************************************** + * + * Infinihost Context Memory management + * + *************************************************************************** + */ + +/** + * Get device limits + * + * @v hermon Hermon device + * @ret rc Return status code + */ +static int hermon_get_cap ( struct hermon *hermon ) { + struct hermonprm_query_dev_cap dev_cap; + int rc; + + if ( ( rc = hermon_cmd_query_dev_cap ( hermon, &dev_cap ) ) != 0 ) { + DBGC ( hermon, "Hermon %p could not get device limits: %s\n", + hermon, strerror ( rc ) ); + return rc; + } + + hermon->cap.cmpt_entry_size = MLX_GET ( &dev_cap, c_mpt_entry_sz ); + hermon->cap.reserved_qps = + ( 1 << MLX_GET ( &dev_cap, log2_rsvd_qps ) ); + hermon->cap.qpc_entry_size = MLX_GET ( &dev_cap, qpc_entry_sz ); + hermon->cap.altc_entry_size = MLX_GET ( &dev_cap, altc_entry_sz ); + hermon->cap.auxc_entry_size = MLX_GET ( &dev_cap, aux_entry_sz ); + hermon->cap.reserved_srqs = + ( 1 << MLX_GET ( &dev_cap, log2_rsvd_srqs ) ); + hermon->cap.srqc_entry_size = MLX_GET ( &dev_cap, srq_entry_sz ); + hermon->cap.reserved_cqs = + ( 1 << MLX_GET ( &dev_cap, log2_rsvd_cqs ) ); + hermon->cap.cqc_entry_size = MLX_GET ( &dev_cap, cqc_entry_sz ); + hermon->cap.reserved_eqs = MLX_GET ( &dev_cap, num_rsvd_eqs ); + hermon->cap.eqc_entry_size = MLX_GET ( &dev_cap, eqc_entry_sz ); + hermon->cap.reserved_mtts = + ( 1 << MLX_GET ( &dev_cap, log2_rsvd_mtts ) ); + hermon->cap.mtt_entry_size = MLX_GET ( &dev_cap, mtt_entry_sz ); + hermon->cap.reserved_mrws = + ( 1 << MLX_GET ( &dev_cap, log2_rsvd_mrws ) ); + hermon->cap.dmpt_entry_size = MLX_GET ( &dev_cap, d_mpt_entry_sz ); + hermon->cap.reserved_uars = MLX_GET ( &dev_cap, num_rsvd_uars ); + + return 0; +} + +/** + * Get ICM usage + * + * @v log_num_entries Log2 of the number of entries + * @v entry_size Entry size + * @ret usage Usage size in ICM + */ +static size_t icm_usage ( unsigned int log_num_entries, size_t entry_size ) { + size_t usage; + + usage = ( ( 1 << log_num_entries ) * entry_size ); + usage = ( ( usage + HERMON_PAGE_SIZE - 1 ) & + ~( HERMON_PAGE_SIZE - 1 ) ); + return usage; +} + +/** + * Allocate ICM + * + * @v hermon Hermon device + * @v init_hca INIT_HCA structure to fill in + * @ret rc Return status code + */ +static int hermon_alloc_icm ( struct hermon *hermon, + struct hermonprm_init_hca *init_hca ) { + struct hermonprm_scalar_parameter icm_size; + struct hermonprm_scalar_parameter icm_aux_size; + uint64_t icm_offset = 0; + unsigned int log_num_qps, log_num_srqs, log_num_cqs, log_num_eqs; + unsigned int log_num_mtts, log_num_mpts; + size_t cmpt_max_len; + size_t qp_cmpt_len, srq_cmpt_len, cq_cmpt_len, eq_cmpt_len; + size_t icm_len, icm_aux_len; + physaddr_t icm_phys; + int i; + int rc; + + /* + * Start by carving up the ICM virtual address space + * + */ + + /* Calculate number of each object type within ICM */ + log_num_qps = fls ( hermon->cap.reserved_qps + HERMON_MAX_QPS - 1 ); + log_num_srqs = fls ( hermon->cap.reserved_srqs - 1 ); + log_num_cqs = fls ( hermon->cap.reserved_cqs + HERMON_MAX_CQS - 1 ); + log_num_eqs = fls ( hermon->cap.reserved_eqs + HERMON_MAX_EQS - 1 ); + log_num_mtts = fls ( hermon->cap.reserved_mtts + HERMON_MAX_MTTS - 1 ); + + /* ICM starts with the cMPT tables, which are sparse */ + cmpt_max_len = ( HERMON_CMPT_MAX_ENTRIES * + ( ( uint64_t ) hermon->cap.cmpt_entry_size ) ); + qp_cmpt_len = icm_usage ( log_num_qps, hermon->cap.cmpt_entry_size ); + hermon->icm_map[HERMON_ICM_QP_CMPT].offset = icm_offset; + hermon->icm_map[HERMON_ICM_QP_CMPT].len = qp_cmpt_len; + icm_offset += cmpt_max_len; + srq_cmpt_len = icm_usage ( log_num_srqs, hermon->cap.cmpt_entry_size ); + hermon->icm_map[HERMON_ICM_SRQ_CMPT].offset = icm_offset; + hermon->icm_map[HERMON_ICM_SRQ_CMPT].len = srq_cmpt_len; + icm_offset += cmpt_max_len; + cq_cmpt_len = icm_usage ( log_num_cqs, hermon->cap.cmpt_entry_size ); + hermon->icm_map[HERMON_ICM_CQ_CMPT].offset = icm_offset; + hermon->icm_map[HERMON_ICM_CQ_CMPT].len = cq_cmpt_len; + icm_offset += cmpt_max_len; + eq_cmpt_len = icm_usage ( log_num_eqs, hermon->cap.cmpt_entry_size ); + hermon->icm_map[HERMON_ICM_EQ_CMPT].offset = icm_offset; + hermon->icm_map[HERMON_ICM_EQ_CMPT].len = eq_cmpt_len; + icm_offset += cmpt_max_len; + + hermon->icm_map[HERMON_ICM_OTHER].offset = icm_offset; + + /* Queue pair contexts */ + MLX_FILL_1 ( init_hca, 12, + qpc_eec_cqc_eqc_rdb_parameters.qpc_base_addr_h, + ( icm_offset >> 32 ) ); + MLX_FILL_2 ( init_hca, 13, + qpc_eec_cqc_eqc_rdb_parameters.qpc_base_addr_l, + ( icm_offset >> 5 ), + qpc_eec_cqc_eqc_rdb_parameters.log_num_of_qp, + log_num_qps ); + DBGC ( hermon, "Hermon %p ICM QPC base = %llx\n", hermon, icm_offset ); + icm_offset += icm_usage ( log_num_qps, hermon->cap.qpc_entry_size ); + + /* Extended alternate path contexts */ + MLX_FILL_1 ( init_hca, 24, + qpc_eec_cqc_eqc_rdb_parameters.altc_base_addr_h, + ( icm_offset >> 32 ) ); + MLX_FILL_1 ( init_hca, 25, + qpc_eec_cqc_eqc_rdb_parameters.altc_base_addr_l, + icm_offset ); + DBGC ( hermon, "Hermon %p ICM ALTC base = %llx\n", hermon, icm_offset); + icm_offset += icm_usage ( log_num_qps, + hermon->cap.altc_entry_size ); + + /* Extended auxiliary contexts */ + MLX_FILL_1 ( init_hca, 28, + qpc_eec_cqc_eqc_rdb_parameters.auxc_base_addr_h, + ( icm_offset >> 32 ) ); + MLX_FILL_1 ( init_hca, 29, + qpc_eec_cqc_eqc_rdb_parameters.auxc_base_addr_l, + icm_offset ); + DBGC ( hermon, "Hermon %p ICM AUXC base = %llx\n", hermon, icm_offset); + icm_offset += icm_usage ( log_num_qps, + hermon->cap.auxc_entry_size ); + + /* Shared receive queue contexts */ + MLX_FILL_1 ( init_hca, 18, + qpc_eec_cqc_eqc_rdb_parameters.srqc_base_addr_h, + ( icm_offset >> 32 ) ); + MLX_FILL_2 ( init_hca, 19, + qpc_eec_cqc_eqc_rdb_parameters.srqc_base_addr_l, + ( icm_offset >> 5 ), + qpc_eec_cqc_eqc_rdb_parameters.log_num_of_srq, + log_num_srqs ); + DBGC ( hermon, "Hermon %p ICM SRQC base = %llx\n", hermon, icm_offset); + icm_offset += icm_usage ( log_num_srqs, + hermon->cap.srqc_entry_size ); + + /* Completion queue contexts */ + MLX_FILL_1 ( init_hca, 20, + qpc_eec_cqc_eqc_rdb_parameters.cqc_base_addr_h, + ( icm_offset >> 32 ) ); + MLX_FILL_2 ( init_hca, 21, + qpc_eec_cqc_eqc_rdb_parameters.cqc_base_addr_l, + ( icm_offset >> 5 ), + qpc_eec_cqc_eqc_rdb_parameters.log_num_of_cq, + log_num_cqs ); + DBGC ( hermon, "Hermon %p ICM CQC base = %llx\n", hermon, icm_offset ); + icm_offset += icm_usage ( log_num_cqs, hermon->cap.cqc_entry_size ); + + /* Event queue contexts */ + MLX_FILL_1 ( init_hca, 32, + qpc_eec_cqc_eqc_rdb_parameters.eqc_base_addr_h, + ( icm_offset >> 32 ) ); + MLX_FILL_2 ( init_hca, 33, + qpc_eec_cqc_eqc_rdb_parameters.eqc_base_addr_l, + ( icm_offset >> 5 ), + qpc_eec_cqc_eqc_rdb_parameters.log_num_of_eq, + log_num_eqs ); + DBGC ( hermon, "Hermon %p ICM EQC base = %llx\n", hermon, icm_offset ); + icm_offset += icm_usage ( log_num_eqs, hermon->cap.eqc_entry_size ); + + /* Memory translation table */ + MLX_FILL_1 ( init_hca, 64, + tpt_parameters.mtt_base_addr_h, ( icm_offset >> 32 ) ); + MLX_FILL_1 ( init_hca, 65, + tpt_parameters.mtt_base_addr_l, icm_offset ); + DBGC ( hermon, "Hermon %p ICM MTT base = %llx\n", hermon, icm_offset ); + icm_offset += icm_usage ( log_num_mtts, + hermon->cap.mtt_entry_size ); + + /* Memory protection table */ + log_num_mpts = fls ( hermon->cap.reserved_mrws + 1 - 1 ); + MLX_FILL_1 ( init_hca, 60, + tpt_parameters.dmpt_base_adr_h, ( icm_offset >> 32 ) ); + MLX_FILL_1 ( init_hca, 61, + tpt_parameters.dmpt_base_adr_l, icm_offset ); + MLX_FILL_1 ( init_hca, 62, + tpt_parameters.log_dmpt_sz, log_num_mpts ); + DBGC ( hermon, "Hermon %p ICM DMPT base = %llx\n", hermon, icm_offset); + icm_offset += icm_usage ( log_num_mpts, + hermon->cap.dmpt_entry_size ); + + /* Multicast table */ + MLX_FILL_1 ( init_hca, 48, + multicast_parameters.mc_base_addr_h, + ( icm_offset >> 32 ) ); + MLX_FILL_1 ( init_hca, 49, + multicast_parameters.mc_base_addr_l, icm_offset ); + MLX_FILL_1 ( init_hca, 52, + multicast_parameters.log_mc_table_entry_sz, + fls ( sizeof ( struct hermonprm_mcg_entry ) - 1 ) ); + MLX_FILL_1 ( init_hca, 53, + multicast_parameters.log_mc_table_hash_sz, 3 ); + MLX_FILL_1 ( init_hca, 54, + multicast_parameters.log_mc_table_sz, 3 ); + DBGC ( hermon, "Hermon %p ICM MC base = %llx\n", hermon, icm_offset ); + icm_offset += ( ( 8 * sizeof ( struct hermonprm_mcg_entry ) + + HERMON_PAGE_SIZE - 1 ) & ~( HERMON_PAGE_SIZE - 1 ) ); + + hermon->icm_map[HERMON_ICM_OTHER].len = + ( icm_offset - hermon->icm_map[HERMON_ICM_OTHER].offset ); + + /* + * Allocate and map physical memory for (portions of) ICM + * + * Map is: + * ICM AUX area (aligned to its own size) + * cMPT areas + * Other areas + */ + + /* Calculate physical memory required for ICM */ + icm_len = 0; + for ( i = 0 ; i < HERMON_ICM_NUM_REGIONS ; i++ ) { + icm_len += hermon->icm_map[i].len; + } + + /* Get ICM auxiliary area size */ + memset ( &icm_size, 0, sizeof ( icm_size ) ); + MLX_FILL_1 ( &icm_size, 0, value_hi, ( icm_offset >> 32 ) ); + MLX_FILL_1 ( &icm_size, 1, value, icm_offset ); + if ( ( rc = hermon_cmd_set_icm_size ( hermon, &icm_size, + &icm_aux_size ) ) != 0 ) { + DBGC ( hermon, "Hermon %p could not set ICM size: %s\n", + hermon, strerror ( rc ) ); + goto err_set_icm_size; + } + icm_aux_len = ( MLX_GET ( &icm_aux_size, value ) * HERMON_PAGE_SIZE ); + + /* Allocate ICM data and auxiliary area */ + DBGC ( hermon, "Hermon %p requires %zd kB ICM and %zd kB AUX ICM\n", + hermon, ( icm_len / 1024 ), ( icm_aux_len / 1024 ) ); + hermon->icm = umalloc ( icm_aux_len + icm_len ); + if ( ! hermon->icm ) { + rc = -ENOMEM; + goto err_alloc; + } + icm_phys = user_to_phys ( hermon->icm, 0 ); + + /* Map ICM auxiliary area */ + DBGC ( hermon, "Hermon %p mapping ICM AUX => %08lx\n", + hermon, icm_phys ); + if ( ( rc = hermon_map_vpm ( hermon, hermon_cmd_map_icm_aux, + 0, icm_phys, icm_aux_len ) ) != 0 ) { + DBGC ( hermon, "Hermon %p could not map AUX ICM: %s\n", + hermon, strerror ( rc ) ); + goto err_map_icm_aux; + } + icm_phys += icm_aux_len; + + /* MAP ICM area */ + for ( i = 0 ; i < HERMON_ICM_NUM_REGIONS ; i++ ) { + DBGC ( hermon, "Hermon %p mapping ICM %llx+%zx => %08lx\n", + hermon, hermon->icm_map[i].offset, + hermon->icm_map[i].len, icm_phys ); + if ( ( rc = hermon_map_vpm ( hermon, hermon_cmd_map_icm, + hermon->icm_map[i].offset, + icm_phys, + hermon->icm_map[i].len ) ) != 0 ){ + DBGC ( hermon, "Hermon %p could not map ICM: %s\n", + hermon, strerror ( rc ) ); + goto err_map_icm; + } + icm_phys += hermon->icm_map[i].len; + } + + return 0; + + err_map_icm: + assert ( i == 0 ); /* We don't handle partial failure at present */ + err_map_icm_aux: + hermon_cmd_unmap_icm_aux ( hermon ); + ufree ( hermon->icm ); + hermon->icm = UNULL; + err_alloc: + err_set_icm_size: + return rc; +} + +/** + * Free ICM + * + * @v hermon Hermon device + */ +static void hermon_free_icm ( struct hermon *hermon ) { + struct hermonprm_scalar_parameter unmap_icm; + int i; + + for ( i = ( HERMON_ICM_NUM_REGIONS - 1 ) ; i >= 0 ; i-- ) { + memset ( &unmap_icm, 0, sizeof ( unmap_icm ) ); + MLX_FILL_1 ( &unmap_icm, 0, value_hi, + ( hermon->icm_map[i].offset >> 32 ) ); + MLX_FILL_1 ( &unmap_icm, 1, value, + hermon->icm_map[i].offset ); + hermon_cmd_unmap_icm ( hermon, + ( 1 << fls ( ( hermon->icm_map[i].len / + HERMON_PAGE_SIZE ) - 1)), + &unmap_icm ); + } + hermon_cmd_unmap_icm_aux ( hermon ); + ufree ( hermon->icm ); + hermon->icm = UNULL; +} + +/*************************************************************************** + * + * PCI interface + * + *************************************************************************** + */ + +/** + * Set up memory protection table + * + * @v hermon Hermon device + * @ret rc Return status code + */ +static int hermon_setup_mpt ( struct hermon *hermon ) { + struct hermonprm_mpt mpt; + uint32_t key; + int rc; + + /* Derive key */ + key = ( hermon->cap.reserved_mrws | HERMON_MKEY_PREFIX ); + hermon->reserved_lkey = ( ( key << 8 ) | ( key >> 24 ) ); + + /* Initialise memory protection table */ + memset ( &mpt, 0, sizeof ( mpt ) ); + MLX_FILL_4 ( &mpt, 0, + r_w, 1, + pa, 1, + lr, 1, + lw, 1 ); + MLX_FILL_1 ( &mpt, 2, mem_key, key ); + MLX_FILL_1 ( &mpt, 3, pd, HERMON_GLOBAL_PD ); + MLX_FILL_1 ( &mpt, 10, len64, 1 ); + if ( ( rc = hermon_cmd_sw2hw_mpt ( hermon, + hermon->cap.reserved_mrws, + &mpt ) ) != 0 ) { + DBGC ( hermon, "Hermon %p could not set up MPT: %s\n", + hermon, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Probe PCI device + * + * @v pci PCI device + * @v id PCI ID + * @ret rc Return status code + */ +static int hermon_probe ( struct pci_device *pci, + const struct pci_device_id *id __unused ) { + struct hermon *hermon; + struct ib_device *ibdev; + struct hermonprm_init_hca init_hca; + int i; + int rc; + + /* Allocate Hermon device */ + hermon = zalloc ( sizeof ( *hermon ) ); + if ( ! hermon ) { + rc = -ENOMEM; + goto err_alloc_hermon; + } + pci_set_drvdata ( pci, hermon ); + + /* Allocate Infiniband devices */ + for ( i = 0 ; i < HERMON_NUM_PORTS ; i++ ) { + ibdev = alloc_ibdev ( 0 ); + if ( ! ibdev ) { + rc = -ENOMEM; + goto err_alloc_ibdev; + } + hermon->ibdev[i] = ibdev; + ibdev->op = &hermon_ib_operations; + ibdev->dev = &pci->dev; + ibdev->port = ( HERMON_PORT_BASE + i ); + ib_set_drvdata ( ibdev, hermon ); + } + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Get PCI BARs */ + hermon->config = ioremap ( pci_bar_start ( pci, HERMON_PCI_CONFIG_BAR), + HERMON_PCI_CONFIG_BAR_SIZE ); + hermon->uar = ioremap ( pci_bar_start ( pci, HERMON_PCI_UAR_BAR ), + HERMON_UAR_NON_EQ_PAGE * HERMON_PAGE_SIZE ); + + /* Allocate space for mailboxes */ + hermon->mailbox_in = malloc_dma ( HERMON_MBOX_SIZE, + HERMON_MBOX_ALIGN ); + if ( ! hermon->mailbox_in ) { + rc = -ENOMEM; + goto err_mailbox_in; + } + hermon->mailbox_out = malloc_dma ( HERMON_MBOX_SIZE, + HERMON_MBOX_ALIGN ); + if ( ! hermon->mailbox_out ) { + rc = -ENOMEM; + goto err_mailbox_out; + } + + /* Start firmware */ + if ( ( rc = hermon_start_firmware ( hermon ) ) != 0 ) + goto err_start_firmware; + + /* Get device limits */ + if ( ( rc = hermon_get_cap ( hermon ) ) != 0 ) + goto err_get_cap; + + /* Allocate ICM */ + memset ( &init_hca, 0, sizeof ( init_hca ) ); + if ( ( rc = hermon_alloc_icm ( hermon, &init_hca ) ) != 0 ) + goto err_alloc_icm; + + /* Initialise HCA */ + MLX_FILL_1 ( &init_hca, 0, version, 0x02 /* "Must be 0x02" */ ); + MLX_FILL_1 ( &init_hca, 5, udp, 1 ); + MLX_FILL_1 ( &init_hca, 74, uar_parameters.log_max_uars, 8 ); + if ( ( rc = hermon_cmd_init_hca ( hermon, &init_hca ) ) != 0 ) { + DBGC ( hermon, "Hermon %p could not initialise HCA: %s\n", + hermon, strerror ( rc ) ); + goto err_init_hca; + } + + /* Set up memory protection */ + if ( ( rc = hermon_setup_mpt ( hermon ) ) != 0 ) + goto err_setup_mpt; + + /* Set up event queue */ + if ( ( rc = hermon_create_eq ( hermon ) ) != 0 ) + goto err_create_eq; + + /* Update MAD parameters */ + for ( i = 0 ; i < HERMON_NUM_PORTS ; i++ ) + ib_smc_update ( hermon->ibdev[i], hermon_mad ); + + /* Register Infiniband devices */ + for ( i = 0 ; i < HERMON_NUM_PORTS ; i++ ) { + if ( ( rc = register_ibdev ( hermon->ibdev[i] ) ) != 0 ) { + DBGC ( hermon, "Hermon %p could not register IB " + "device: %s\n", hermon, strerror ( rc ) ); + goto err_register_ibdev; + } + } + + return 0; + + i = HERMON_NUM_PORTS; + err_register_ibdev: + for ( i-- ; i >= 0 ; i-- ) + unregister_ibdev ( hermon->ibdev[i] ); + hermon_destroy_eq ( hermon ); + err_create_eq: + err_setup_mpt: + hermon_cmd_close_hca ( hermon ); + err_init_hca: + hermon_free_icm ( hermon ); + err_alloc_icm: + err_get_cap: + hermon_stop_firmware ( hermon ); + err_start_firmware: + free_dma ( hermon->mailbox_out, HERMON_MBOX_SIZE ); + err_mailbox_out: + free_dma ( hermon->mailbox_in, HERMON_MBOX_SIZE ); + err_mailbox_in: + i = HERMON_NUM_PORTS; + err_alloc_ibdev: + for ( i-- ; i >= 0 ; i-- ) + ibdev_put ( hermon->ibdev[i] ); + free ( hermon ); + err_alloc_hermon: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void hermon_remove ( struct pci_device *pci ) { + struct hermon *hermon = pci_get_drvdata ( pci ); + int i; + + for ( i = ( HERMON_NUM_PORTS - 1 ) ; i >= 0 ; i-- ) + unregister_ibdev ( hermon->ibdev[i] ); + hermon_destroy_eq ( hermon ); + hermon_cmd_close_hca ( hermon ); + hermon_free_icm ( hermon ); + hermon_stop_firmware ( hermon ); + hermon_stop_firmware ( hermon ); + free_dma ( hermon->mailbox_out, HERMON_MBOX_SIZE ); + free_dma ( hermon->mailbox_in, HERMON_MBOX_SIZE ); + for ( i = ( HERMON_NUM_PORTS - 1 ) ; i >= 0 ; i-- ) + ibdev_put ( hermon->ibdev[i] ); + free ( hermon ); +} + +static struct pci_device_id hermon_nics[] = { + PCI_ROM ( 0x15b3, 0x6340, "mt25408", "MT25408 HCA driver" ), + PCI_ROM ( 0x15b3, 0x634a, "mt25418", "MT25418 HCA driver" ), + PCI_ROM ( 0x15b3, 0x6732, "mt26418", "MT26418 HCA driver" ), + PCI_ROM ( 0x15b3, 0x673c, "mt26428", "MT26428 HCA driver" ), +}; + +struct pci_driver hermon_driver __pci_driver = { + .ids = hermon_nics, + .id_count = ( sizeof ( hermon_nics ) / sizeof ( hermon_nics[0] ) ), + .probe = hermon_probe, + .remove = hermon_remove, +}; diff --git a/gpxe/src/drivers/infiniband/hermon.h b/gpxe/src/drivers/infiniband/hermon.h new file mode 100644 index 0000000..ed39da6 --- /dev/null +++ b/gpxe/src/drivers/infiniband/hermon.h @@ -0,0 +1,533 @@ +#ifndef _HERMON_H +#define _HERMON_H + +/** @file + * + * Mellanox Hermon Infiniband HCA driver + * + */ + +#include +#include +#include "mlx_bitops.h" +#include "MT25408_PRM.h" + +/* + * Hardware constants + * + */ + +/* Ports in existence */ +#define HERMON_NUM_PORTS 2 +#define HERMON_PORT_BASE 1 + +/* PCI BARs */ +#define HERMON_PCI_CONFIG_BAR PCI_BASE_ADDRESS_0 +#define HERMON_PCI_CONFIG_BAR_SIZE 0x100000 +#define HERMON_PCI_UAR_BAR PCI_BASE_ADDRESS_2 + +/* Work queue entry and completion queue entry opcodes */ +#define HERMON_OPCODE_SEND 0x0a +#define HERMON_OPCODE_RECV_ERROR 0xfe +#define HERMON_OPCODE_SEND_ERROR 0xff + +/* HCA command register opcodes */ +#define HERMON_HCR_QUERY_DEV_CAP 0x0003 +#define HERMON_HCR_QUERY_FW 0x0004 +#define HERMON_HCR_INIT_HCA 0x0007 +#define HERMON_HCR_CLOSE_HCA 0x0008 +#define HERMON_HCR_INIT_PORT 0x0009 +#define HERMON_HCR_CLOSE_PORT 0x000a +#define HERMON_HCR_SW2HW_MPT 0x000d +#define HERMON_HCR_WRITE_MTT 0x0011 +#define HERMON_HCR_MAP_EQ 0x0012 +#define HERMON_HCR_SW2HW_EQ 0x0013 +#define HERMON_HCR_HW2SW_EQ 0x0014 +#define HERMON_HCR_QUERY_EQ 0x0015 +#define HERMON_HCR_SW2HW_CQ 0x0016 +#define HERMON_HCR_HW2SW_CQ 0x0017 +#define HERMON_HCR_RST2INIT_QP 0x0019 +#define HERMON_HCR_INIT2RTR_QP 0x001a +#define HERMON_HCR_RTR2RTS_QP 0x001b +#define HERMON_HCR_RTS2RTS_QP 0x001c +#define HERMON_HCR_2RST_QP 0x0021 +#define HERMON_HCR_MAD_IFC 0x0024 +#define HERMON_HCR_READ_MCG 0x0025 +#define HERMON_HCR_WRITE_MCG 0x0026 +#define HERMON_HCR_MGID_HASH 0x0027 +#define HERMON_HCR_RUN_FW 0x0ff6 +#define HERMON_HCR_DISABLE_LAM 0x0ff7 +#define HERMON_HCR_ENABLE_LAM 0x0ff8 +#define HERMON_HCR_UNMAP_ICM 0x0ff9 +#define HERMON_HCR_MAP_ICM 0x0ffa +#define HERMON_HCR_UNMAP_ICM_AUX 0x0ffb +#define HERMON_HCR_MAP_ICM_AUX 0x0ffc +#define HERMON_HCR_SET_ICM_SIZE 0x0ffd +#define HERMON_HCR_UNMAP_FA 0x0ffe +#define HERMON_HCR_MAP_FA 0x0fff + +/* Service types */ +#define HERMON_ST_UD 0x03 + +/* MTUs */ +#define HERMON_MTU_2048 0x04 + +#define HERMON_INVALID_LKEY 0x00000100UL + +#define HERMON_PAGE_SIZE 4096 + +#define HERMON_DB_POST_SND_OFFSET 0x14 +#define HERMON_DB_EQ_OFFSET(_eqn) \ + ( 0x800 + HERMON_PAGE_SIZE * ( (_eqn) / 4 ) + 0x08 * ( (_eqn) % 4 ) ) + +#define HERMON_QP_OPT_PARAM_QKEY 0x00000020UL + +#define HERMON_MAP_EQ ( 0UL << 31 ) +#define HERMON_UNMAP_EQ ( 1UL << 31 ) + +#define HERMON_EV_PORT_STATE_CHANGE 0x09 + +/* + * Datatypes that seem to be missing from the autogenerated documentation + * + */ +struct hermonprm_mgm_hash_st { + pseudo_bit_t reserved0[0x00020]; +/* -------------- */ + pseudo_bit_t hash[0x00010]; + pseudo_bit_t reserved1[0x00010]; +} __attribute__ (( packed )); + +struct hermonprm_mcg_entry_st { + struct hermonprm_mcg_hdr_st hdr; + struct hermonprm_mcg_qp_dw_st qp[8]; +} __attribute__ (( packed )); + +struct hermonprm_cq_db_record_st { + pseudo_bit_t update_ci[0x00018]; + pseudo_bit_t reserved0[0x00008]; +/* -------------- */ + pseudo_bit_t arm_ci[0x00018]; + pseudo_bit_t cmd[0x00003]; + pseudo_bit_t reserved1[0x00001]; + pseudo_bit_t cmd_sn[0x00002]; + pseudo_bit_t reserved2[0x00002]; +} __attribute__ (( packed )); + +struct hermonprm_send_db_register_st { + pseudo_bit_t reserved[0x00008]; + pseudo_bit_t qn[0x00018]; +} __attribute__ (( packed )); + +struct hermonprm_event_db_register_st { + pseudo_bit_t ci[0x00018]; + pseudo_bit_t reserver[0x00007]; + pseudo_bit_t a[0x00001]; +} __attribute__ (( packed )); + +struct hermonprm_scalar_parameter_st { + pseudo_bit_t value_hi[0x00020]; +/* -------------- */ + pseudo_bit_t value[0x00020]; +} __attribute__ (( packed )); + +struct hermonprm_event_mask_st { + pseudo_bit_t reserved0[0x00020]; +/* -------------- */ + pseudo_bit_t completion[0x00001]; + pseudo_bit_t reserved1[0x0008]; + pseudo_bit_t port_state_change[0x00001]; + pseudo_bit_t reserved2[0x00016]; +} __attribute__ (( packed )); + +struct hermonprm_port_state_change_event_st { + pseudo_bit_t reserved[0x00020]; + struct hermonprm_port_state_change_st data; +} __attribute__ (( packed )); + +/* + * Wrapper structures for hardware datatypes + * + */ + +struct MLX_DECLARE_STRUCT ( hermonprm_completion_queue_context ); +struct MLX_DECLARE_STRUCT ( hermonprm_completion_queue_entry ); +struct MLX_DECLARE_STRUCT ( hermonprm_completion_with_error ); +struct MLX_DECLARE_STRUCT ( hermonprm_cq_db_record ); +struct MLX_DECLARE_STRUCT ( hermonprm_eqc ); +struct MLX_DECLARE_STRUCT ( hermonprm_event_db_register ); +struct MLX_DECLARE_STRUCT ( hermonprm_event_mask ); +struct MLX_DECLARE_STRUCT ( hermonprm_event_queue_entry ); +struct MLX_DECLARE_STRUCT ( hermonprm_hca_command_register ); +struct MLX_DECLARE_STRUCT ( hermonprm_init_hca ); +struct MLX_DECLARE_STRUCT ( hermonprm_init_port ); +struct MLX_DECLARE_STRUCT ( hermonprm_mad_ifc ); +struct MLX_DECLARE_STRUCT ( hermonprm_mcg_entry ); +struct MLX_DECLARE_STRUCT ( hermonprm_mgm_hash ); +struct MLX_DECLARE_STRUCT ( hermonprm_mpt ); +struct MLX_DECLARE_STRUCT ( hermonprm_mtt ); +struct MLX_DECLARE_STRUCT ( hermonprm_port_state_change_event ); +struct MLX_DECLARE_STRUCT ( hermonprm_qp_db_record ); +struct MLX_DECLARE_STRUCT ( hermonprm_qp_ee_state_transitions ); +struct MLX_DECLARE_STRUCT ( hermonprm_query_dev_cap ); +struct MLX_DECLARE_STRUCT ( hermonprm_query_fw ); +struct MLX_DECLARE_STRUCT ( hermonprm_queue_pair_ee_context_entry ); +struct MLX_DECLARE_STRUCT ( hermonprm_scalar_parameter ); +struct MLX_DECLARE_STRUCT ( hermonprm_send_db_register ); +struct MLX_DECLARE_STRUCT ( hermonprm_ud_address_vector ); +struct MLX_DECLARE_STRUCT ( hermonprm_virtual_physical_mapping ); +struct MLX_DECLARE_STRUCT ( hermonprm_wqe_segment_ctrl_send ); +struct MLX_DECLARE_STRUCT ( hermonprm_wqe_segment_data_ptr ); +struct MLX_DECLARE_STRUCT ( hermonprm_wqe_segment_ud ); + +/* + * Composite hardware datatypes + * + */ + +struct hermonprm_write_mtt { + struct hermonprm_scalar_parameter mtt_base_addr; + struct hermonprm_scalar_parameter reserved; + struct hermonprm_mtt mtt; +} __attribute__ (( packed )); + +#define HERMON_MAX_GATHER 1 + +struct hermonprm_ud_send_wqe { + struct hermonprm_wqe_segment_ctrl_send ctrl; + struct hermonprm_wqe_segment_ud ud; + struct hermonprm_wqe_segment_data_ptr data[HERMON_MAX_GATHER]; +} __attribute__ (( packed )); + +#define HERMON_MAX_SCATTER 1 + +struct hermonprm_recv_wqe { + struct hermonprm_wqe_segment_data_ptr data[HERMON_MAX_SCATTER]; +} __attribute__ (( packed )); + +union hermonprm_completion_entry { + struct hermonprm_completion_queue_entry normal; + struct hermonprm_completion_with_error error; +} __attribute__ (( packed )); + +union hermonprm_event_entry { + struct hermonprm_event_queue_entry generic; + struct hermonprm_port_state_change_event port_state_change; +} __attribute__ (( packed )); + +union hermonprm_doorbell_register { + struct hermonprm_send_db_register send; + struct hermonprm_event_db_register event; + uint32_t dword[1]; +} __attribute__ (( packed )); + +union hermonprm_mad { + struct hermonprm_mad_ifc ifc; + union ib_mad mad; +} __attribute__ (( packed )); + +/* + * gPXE-specific definitions + * + */ + +/** Hermon device capabilitiess */ +struct hermon_dev_cap { + /** CMPT entry size */ + size_t cmpt_entry_size; + /** Number of reserved QPs */ + unsigned int reserved_qps; + /** QP context entry size */ + size_t qpc_entry_size; + /** Alternate path context entry size */ + size_t altc_entry_size; + /** Auxiliary context entry size */ + size_t auxc_entry_size; + /** Number of reserved SRQs */ + unsigned int reserved_srqs; + /** SRQ context entry size */ + size_t srqc_entry_size; + /** Number of reserved CQs */ + unsigned int reserved_cqs; + /** CQ context entry size */ + size_t cqc_entry_size; + /** Number of reserved EQs */ + unsigned int reserved_eqs; + /** EQ context entry size */ + size_t eqc_entry_size; + /** Number of reserved MTTs */ + unsigned int reserved_mtts; + /** MTT entry size */ + size_t mtt_entry_size; + /** Number of reserved MRWs */ + unsigned int reserved_mrws; + /** DMPT entry size */ + size_t dmpt_entry_size; + /** Number of reserved UARs */ + unsigned int reserved_uars; +}; + +/** Number of cMPT entries of each type */ +#define HERMON_CMPT_MAX_ENTRIES ( 1 << 24 ) + +/** Hermon ICM memory map entry */ +struct hermon_icm_map { + /** Offset (virtual address within ICM) */ + uint64_t offset; + /** Length */ + size_t len; +}; + +/** Discontiguous regions within Hermon ICM */ +enum hermon_icm_map_regions { + HERMON_ICM_QP_CMPT = 0, + HERMON_ICM_SRQ_CMPT, + HERMON_ICM_CQ_CMPT, + HERMON_ICM_EQ_CMPT, + HERMON_ICM_OTHER, + HERMON_ICM_NUM_REGIONS +}; + +/** UAR page for doorbell accesses + * + * Pages 0-127 are reserved for event queue doorbells only, so we use + * page 128. + */ +#define HERMON_UAR_NON_EQ_PAGE 128 + +/** Maximum number of allocatable MTT entries + * + * This is a policy decision, not a device limit. + */ +#define HERMON_MAX_MTTS 64 + +/** A Hermon MTT descriptor */ +struct hermon_mtt { + /** MTT offset */ + unsigned int mtt_offset; + /** Number of pages */ + unsigned int num_pages; + /** MTT base address */ + unsigned int mtt_base_addr; + /** Offset within page */ + unsigned int page_offset; +}; + +/** Alignment of Hermon send work queue entries */ +#define HERMON_SEND_WQE_ALIGN 128 + +/** A Hermon send work queue entry */ +union hermon_send_wqe { + struct hermonprm_ud_send_wqe ud; + uint8_t force_align[HERMON_SEND_WQE_ALIGN]; +} __attribute__ (( packed )); + +/** A Hermon send work queue */ +struct hermon_send_work_queue { + /** Number of work queue entries, including headroom + * + * Hermon requires us to leave unused space within the send + * WQ, so we create a send WQ with more entries than are + * requested in the create_qp() call. + */ + unsigned int num_wqes; + /** Work queue entries */ + union hermon_send_wqe *wqe; + /** Size of work queue */ + size_t wqe_size; + /** Doorbell register */ + void *doorbell; +}; + +/** Alignment of Hermon receive work queue entries */ +#define HERMON_RECV_WQE_ALIGN 16 + +/** A Hermon receive work queue entry */ +union hermon_recv_wqe { + struct hermonprm_recv_wqe recv; + uint8_t force_align[HERMON_RECV_WQE_ALIGN]; +} __attribute__ (( packed )); + +/** A Hermon receive work queue */ +struct hermon_recv_work_queue { + /** Work queue entries */ + union hermon_recv_wqe *wqe; + /** Size of work queue */ + size_t wqe_size; + /** Doorbell */ + struct hermonprm_qp_db_record doorbell __attribute__ (( aligned (4) )); +}; + +/** Maximum number of allocatable queue pairs + * + * This is a policy decision, not a device limit. + */ +#define HERMON_MAX_QPS 8 + +/** Base queue pair number */ +#define HERMON_QPN_BASE 0x550000 + +/** A Hermon queue pair */ +struct hermon_queue_pair { + /** Work queue buffer */ + void *wqe; + /** Size of work queue buffer */ + size_t wqe_size; + /** MTT descriptor */ + struct hermon_mtt mtt; + /** Send work queue */ + struct hermon_send_work_queue send; + /** Receive work queue */ + struct hermon_recv_work_queue recv; +}; + +/** Maximum number of allocatable completion queues + * + * This is a policy decision, not a device limit. + */ +#define HERMON_MAX_CQS 8 + +/** A Hermon completion queue */ +struct hermon_completion_queue { + /** Completion queue entries */ + union hermonprm_completion_entry *cqe; + /** Size of completion queue */ + size_t cqe_size; + /** MTT descriptor */ + struct hermon_mtt mtt; + /** Doorbell */ + struct hermonprm_cq_db_record doorbell __attribute__ (( aligned (8) )); +}; + +/** Maximum number of allocatable event queues + * + * This is a policy decision, not a device limit. + */ +#define HERMON_MAX_EQS 8 + +/** A Hermon event queue */ +struct hermon_event_queue { + /** Event queue entries */ + union hermonprm_event_entry *eqe; + /** Size of event queue */ + size_t eqe_size; + /** MTT descriptor */ + struct hermon_mtt mtt; + /** Event queue number */ + unsigned long eqn; + /** Next event queue entry index */ + unsigned long next_idx; + /** Doorbell register */ + void *doorbell; +}; + +/** Number of event queue entries + * + * This is a policy decision. + */ +#define HERMON_NUM_EQES 4 + +/** A Hermon resource bitmask */ +typedef uint32_t hermon_bitmask_t; + +/** Size of a hermon resource bitmask */ +#define HERMON_BITMASK_SIZE(max_entries) \ + ( ( (max_entries) + ( 8 * sizeof ( hermon_bitmask_t ) ) - 1 ) / \ + ( 8 * sizeof ( hermon_bitmask_t ) ) ) + +/** A Hermon device */ +struct hermon { + /** PCI configuration registers */ + void *config; + /** PCI user Access Region */ + void *uar; + + /** Command toggle */ + unsigned int toggle; + /** Command input mailbox */ + void *mailbox_in; + /** Command output mailbox */ + void *mailbox_out; + + /** Firmware area in external memory */ + userptr_t firmware_area; + /** ICM map */ + struct hermon_icm_map icm_map[HERMON_ICM_NUM_REGIONS]; + /** ICM area */ + userptr_t icm; + + /** Event queue */ + struct hermon_event_queue eq; + /** Reserved LKey + * + * Used to get unrestricted memory access. + */ + unsigned long reserved_lkey; + + /** Completion queue in-use bitmask */ + hermon_bitmask_t cq_inuse[ HERMON_BITMASK_SIZE ( HERMON_MAX_CQS ) ]; + /** Queue pair in-use bitmask */ + hermon_bitmask_t qp_inuse[ HERMON_BITMASK_SIZE ( HERMON_MAX_QPS ) ]; + /** MTT entry in-use bitmask */ + hermon_bitmask_t mtt_inuse[ HERMON_BITMASK_SIZE ( HERMON_MAX_MTTS ) ]; + + /** Device capabilities */ + struct hermon_dev_cap cap; + + /** Infiniband devices */ + struct ib_device *ibdev[HERMON_NUM_PORTS]; +}; + +/** Global protection domain */ +#define HERMON_GLOBAL_PD 0x123456 + +/** Memory key prefix */ +#define HERMON_MKEY_PREFIX 0x77000000UL + +/* + * HCA commands + * + */ + +#define HERMON_HCR_BASE 0x80680 +#define HERMON_HCR_REG(x) ( HERMON_HCR_BASE + 4 * (x) ) +#define HERMON_HCR_MAX_WAIT_MS 2000 +#define HERMON_MBOX_ALIGN 4096 +#define HERMON_MBOX_SIZE 512 + +/* HCA command is split into + * + * bits 11:0 Opcode + * bit 12 Input uses mailbox + * bit 13 Output uses mailbox + * bits 22:14 Input parameter length (in dwords) + * bits 31:23 Output parameter length (in dwords) + * + * Encoding the information in this way allows us to cut out several + * parameters to the hermon_command() call. + */ +#define HERMON_HCR_IN_MBOX 0x00001000UL +#define HERMON_HCR_OUT_MBOX 0x00002000UL +#define HERMON_HCR_OPCODE( _command ) ( (_command) & 0xfff ) +#define HERMON_HCR_IN_LEN( _command ) ( ( (_command) >> 12 ) & 0x7fc ) +#define HERMON_HCR_OUT_LEN( _command ) ( ( (_command) >> 21 ) & 0x7fc ) + +/** Build HCR command from component parts */ +#define HERMON_HCR_INOUT_CMD( _opcode, _in_mbox, _in_len, \ + _out_mbox, _out_len ) \ + ( (_opcode) | \ + ( (_in_mbox) ? HERMON_HCR_IN_MBOX : 0 ) | \ + ( ( (_in_len) / 4 ) << 14 ) | \ + ( (_out_mbox) ? HERMON_HCR_OUT_MBOX : 0 ) | \ + ( ( (_out_len) / 4 ) << 23 ) ) + +#define HERMON_HCR_IN_CMD( _opcode, _in_mbox, _in_len ) \ + HERMON_HCR_INOUT_CMD ( _opcode, _in_mbox, _in_len, 0, 0 ) + +#define HERMON_HCR_OUT_CMD( _opcode, _out_mbox, _out_len ) \ + HERMON_HCR_INOUT_CMD ( _opcode, 0, 0, _out_mbox, _out_len ) + +#define HERMON_HCR_VOID_CMD( _opcode ) \ + HERMON_HCR_INOUT_CMD ( _opcode, 0, 0, 0, 0 ) + +#endif /* _HERMON_H */ diff --git a/gpxe/src/drivers/infiniband/ib_packet.c b/gpxe/src/drivers/infiniband/ib_packet.c new file mode 100644 index 0000000..0f21617 --- /dev/null +++ b/gpxe/src/drivers/infiniband/ib_packet.c @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @file + * + * Infiniband Packet Formats + * + */ + +/** + * Add IB headers + * + * @v ibdev Infiniband device + * @v iobuf I/O buffer to contain headers + * @v qp Queue pair + * @v payload_len Payload length + * @v av Address vector + */ +int ib_push ( struct ib_device *ibdev, struct io_buffer *iobuf, + struct ib_queue_pair *qp, size_t payload_len, + const struct ib_address_vector *av ) { + struct ib_local_route_header *lrh; + struct ib_global_route_header *grh; + struct ib_base_transport_header *bth; + struct ib_datagram_extended_transport_header *deth; + size_t orig_iob_len = iob_len ( iobuf ); + size_t pad_len; + size_t lrh_len; + size_t grh_len; + unsigned int vl; + unsigned int lnh; + + DBGC2 ( ibdev, "IBDEV %p TX %04x:%08lx => %04x:%08lx (key %08lx)\n", + ibdev, ibdev->lid, qp->qpn, av->lid, av->qpn, av->qkey ); + + /* Calculate packet length */ + pad_len = ( (-payload_len) & 0x3 ); + payload_len += pad_len; + payload_len += 4; /* ICRC */ + + /* Reserve space for headers */ + orig_iob_len = iob_len ( iobuf ); + deth = iob_push ( iobuf, sizeof ( *deth ) ); + bth = iob_push ( iobuf, sizeof ( *bth ) ); + grh_len = ( payload_len + iob_len ( iobuf ) - orig_iob_len ); + grh = ( av->gid_present ? + iob_push ( iobuf, sizeof ( *grh ) ) : NULL ); + lrh = iob_push ( iobuf, sizeof ( *lrh ) ); + lrh_len = ( payload_len + iob_len ( iobuf ) - orig_iob_len ); + + /* Construct LRH */ + vl = ( ( av->qpn == IB_QPN_SMP ) ? IB_VL_SMP : IB_VL_DEFAULT ); + lrh->vl__lver = ( vl << 4 ); + lnh = ( grh ? IB_LNH_GRH : IB_LNH_BTH ); + lrh->sl__lnh = ( ( av->sl << 4 ) | lnh ); + lrh->dlid = htons ( av->lid ); + lrh->length = htons ( lrh_len >> 2 ); + lrh->slid = htons ( ibdev->lid ); + + /* Construct GRH, if required */ + if ( grh ) { + grh->ipver__tclass__flowlabel = + htonl ( IB_GRH_IPVER_IPv6 << 28 ); + grh->paylen = htons ( grh_len ); + grh->nxthdr = IB_GRH_NXTHDR_IBA; + grh->hoplmt = 0; + memcpy ( &grh->sgid, &ibdev->gid, sizeof ( grh->sgid ) ); + memcpy ( &grh->dgid, &av->gid, sizeof ( grh->dgid ) ); + } + + /* Construct BTH */ + bth->opcode = BTH_OPCODE_UD_SEND; + bth->se__m__padcnt__tver = ( pad_len << 4 ); + bth->pkey = htons ( ibdev->pkey ); + bth->dest_qp = htonl ( av->qpn ); + bth->ack__psn = htonl ( ( ibdev->psn++ ) & 0xffffffUL ); + + /* Construct DETH */ + deth->qkey = htonl ( av->qkey ); + deth->src_qp = htonl ( qp->qpn ); + + DBGCP_HDA ( ibdev, 0, iobuf->data, + ( iob_len ( iobuf ) - orig_iob_len ) ); + + return 0; +} + +/** + * Remove IB headers + * + * @v ibdev Infiniband device + * @v iobuf I/O buffer containing headers + * @v qp Queue pair to fill in, or NULL + * @v payload_len Payload length to fill in, or NULL + * @v av Address vector to fill in + */ +int ib_pull ( struct ib_device *ibdev, struct io_buffer *iobuf, + struct ib_queue_pair **qp, size_t *payload_len, + struct ib_address_vector *av ) { + struct ib_local_route_header *lrh; + struct ib_global_route_header *grh; + struct ib_base_transport_header *bth; + struct ib_datagram_extended_transport_header *deth; + size_t orig_iob_len = iob_len ( iobuf ); + unsigned int lnh; + size_t pad_len; + unsigned long qpn; + unsigned int lid; + + /* Clear return values */ + if ( qp ) + *qp = NULL; + if ( payload_len ) + *payload_len = 0; + memset ( av, 0, sizeof ( *av ) ); + + /* Extract LRH */ + if ( iob_len ( iobuf ) < sizeof ( *lrh ) ) { + DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) for LRH\n", + ibdev, iob_len ( iobuf ) ); + return -EINVAL; + } + lrh = iobuf->data; + iob_pull ( iobuf, sizeof ( *lrh ) ); + av->lid = ntohs ( lrh->slid ); + av->sl = ( lrh->sl__lnh >> 4 ); + lnh = ( lrh->sl__lnh & 0x3 ); + lid = ntohs ( lrh->dlid ); + + /* Reject unsupported packets */ + if ( ! ( ( lnh == IB_LNH_BTH ) || ( lnh == IB_LNH_GRH ) ) ) { + DBGC ( ibdev, "IBDEV %p RX unsupported LNH %x\n", + ibdev, lnh ); + return -ENOTSUP; + } + + /* Extract GRH, if present */ + if ( lnh == IB_LNH_GRH ) { + if ( iob_len ( iobuf ) < sizeof ( *grh ) ) { + DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) " + "for GRH\n", ibdev, iob_len ( iobuf ) ); + return -EINVAL; + } + grh = iobuf->data; + iob_pull ( iobuf, sizeof ( *grh ) ); + av->gid_present = 1; + memcpy ( &av->gid, &grh->sgid, sizeof ( av->gid ) ); + } else { + grh = NULL; + } + + /* Extract BTH */ + if ( iob_len ( iobuf ) < sizeof ( *bth ) ) { + DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) for BTH\n", + ibdev, iob_len ( iobuf ) ); + return -EINVAL; + } + bth = iobuf->data; + iob_pull ( iobuf, sizeof ( *bth ) ); + if ( bth->opcode != BTH_OPCODE_UD_SEND ) { + DBGC ( ibdev, "IBDEV %p unsupported BTH opcode %x\n", + ibdev, bth->opcode ); + return -ENOTSUP; + } + qpn = ntohl ( bth->dest_qp ); + + /* Extract DETH */ + if ( iob_len ( iobuf ) < sizeof ( *deth ) ) { + DBGC ( ibdev, "IBDEV %p RX too short (%zd bytes) for DETH\n", + ibdev, iob_len ( iobuf ) ); + return -EINVAL; + } + deth = iobuf->data; + iob_pull ( iobuf, sizeof ( *deth ) ); + av->qpn = ntohl ( deth->src_qp ); + av->qkey = ntohl ( deth->qkey ); + + /* Calculate payload length, if applicable */ + if ( payload_len ) { + pad_len = ( ( bth->se__m__padcnt__tver >> 4 ) & 0x3 ); + *payload_len = ( ( ntohs ( lrh->length ) << 2 ) + - ( orig_iob_len - iob_len ( iobuf ) ) + - pad_len - 4 /* ICRC */ ); + } + + /* Determine destination QP, if applicable */ + if ( qp ) { + if ( IB_LID_MULTICAST ( lid ) && grh ) { + *qp = ib_find_qp_mgid ( ibdev, &grh->dgid ); + } else { + *qp = ib_find_qp_qpn ( ibdev, qpn ); + } + if ( ! *qp ) { + DBGC ( ibdev, "IBDEV %p RX for nonexistent QP\n", + ibdev ); + return -ENODEV; + } + } + + DBGC2 ( ibdev, "IBDEV %p RX %04x:%08lx <= %04x:%08lx (key %08x)\n", + ibdev, lid, + ( IB_LID_MULTICAST( lid ) ? ( qp ? (*qp)->qpn : -1UL ) : qpn ), + av->lid, av->qpn, ntohl ( deth->qkey ) ); + DBGCP_HDA ( ibdev, 0, + ( iobuf->data - ( orig_iob_len - iob_len ( iobuf ) ) ), + ( orig_iob_len - iob_len ( iobuf ) ) ); + + return 0; +} diff --git a/gpxe/src/drivers/infiniband/ib_sma.c b/gpxe/src/drivers/infiniband/ib_sma.c new file mode 100644 index 0000000..2bd3a9e --- /dev/null +++ b/gpxe/src/drivers/infiniband/ib_sma.c @@ -0,0 +1,553 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @file + * + * Infiniband Subnet Management Agent + * + */ + +/** + * Get node information + * + * @v sma Subnet management agent + * @v get Attribute to get + */ +static void ib_sma_get_node_info ( struct ib_sma *sma, + union ib_smp_data *get ) { + struct ib_device *ibdev = sma->ibdev; + struct ib_node_info *node_info = &get->node_info; + struct ib_device *tmp; + + memset ( node_info, 0, sizeof ( *node_info ) ); + node_info->base_version = IB_MGMT_BASE_VERSION; + node_info->class_version = IB_SMP_CLASS_VERSION; + node_info->node_type = IB_NODE_TYPE_HCA; + /* Search for IB devices with the same physical device to + * identify port count and a suitable Node GUID. + */ + for_each_ibdev ( tmp ) { + if ( tmp->dev != ibdev->dev ) + continue; + if ( node_info->num_ports == 0 ) { + memcpy ( node_info->sys_guid, &tmp->gid.u.half[1], + sizeof ( node_info->sys_guid ) ); + memcpy ( node_info->node_guid, &tmp->gid.u.half[1], + sizeof ( node_info->node_guid ) ); + } + node_info->num_ports++; + } + memcpy ( node_info->port_guid, &ibdev->gid.u.half[1], + sizeof ( node_info->port_guid ) ); + node_info->partition_cap = htons ( 1 ); + node_info->local_port_num = ibdev->port; +} + +/** + * Get node description + * + * @v sma Subnet management agent + * @v get Attribute to get + */ +static void ib_sma_get_node_desc ( struct ib_sma *sma, + union ib_smp_data *get ) { + struct ib_device *ibdev = sma->ibdev; + struct ib_node_desc *node_desc = &get->node_desc; + struct ib_gid_half *guid = &ibdev->gid.u.half[1]; + + memset ( node_desc, 0, sizeof ( *node_desc ) ); + snprintf ( node_desc->node_string, sizeof ( node_desc->node_string ), + "gPXE %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x (%s)", + guid->bytes[0], guid->bytes[1], guid->bytes[2], + guid->bytes[3], guid->bytes[4], guid->bytes[5], + guid->bytes[6], guid->bytes[7], ibdev->dev->name ); +} + +/** + * Get GUID information + * + * @v sma Subnet management agent + * @v get Attribute to get + */ +static void ib_sma_get_guid_info ( struct ib_sma *sma, + union ib_smp_data *get ) { + struct ib_device *ibdev = sma->ibdev; + struct ib_guid_info *guid_info = &get->guid_info; + + memset ( guid_info, 0, sizeof ( *guid_info ) ); + memcpy ( guid_info->guid[0], &ibdev->gid.u.half[1], + sizeof ( guid_info->guid[0] ) ); +} + +/** + * Get port information + * + * @v sma Subnet management agent + * @v get Attribute to get + */ +static void ib_sma_get_port_info ( struct ib_sma *sma, + union ib_smp_data *get ) { + struct ib_device *ibdev = sma->ibdev; + struct ib_port_info *port_info = &get->port_info; + + memset ( port_info, 0, sizeof ( *port_info ) ); + memcpy ( port_info->gid_prefix, &ibdev->gid.u.half[0], + sizeof ( port_info->gid_prefix ) ); + port_info->lid = ntohs ( ibdev->lid ); + port_info->mastersm_lid = ntohs ( ibdev->sm_lid ); + port_info->local_port_num = ibdev->port; + port_info->link_width_enabled = ibdev->link_width; + port_info->link_width_supported = ibdev->link_width; + port_info->link_width_active = ibdev->link_width; + port_info->link_speed_supported__port_state = + ( ( ibdev->link_speed << 4 ) | ibdev->port_state ); + port_info->port_phys_state__link_down_def_state = + ( ( IB_PORT_PHYS_STATE_POLLING << 4 ) | + IB_PORT_PHYS_STATE_POLLING ); + port_info->link_speed_active__link_speed_enabled = + ( ( ibdev->link_speed << 4 ) | ibdev->link_speed ); + port_info->neighbour_mtu__mastersm_sl = + ( ( IB_MTU_2048 << 4 ) | ibdev->sm_sl ); + port_info->vl_cap__init_type = ( IB_VL_0 << 4 ); + port_info->init_type_reply__mtu_cap = IB_MTU_2048; + port_info->operational_vls__enforcement = ( IB_VL_0 << 4 ); + port_info->guid_cap = 1; +} + +/** + * Set port information + * + * @v sma Subnet management agent + * @v set Attribute to set + * @ret rc Return status code + */ +static int ib_sma_set_port_info ( struct ib_sma *sma, + const union ib_smp_data *set ) { + struct ib_device *ibdev = sma->ibdev; + const struct ib_port_info *port_info = &set->port_info; + + memcpy ( &ibdev->gid.u.half[0], port_info->gid_prefix, + sizeof ( ibdev->gid.u.half[0] ) ); + ibdev->lid = ntohs ( port_info->lid ); + ibdev->sm_lid = ntohs ( port_info->mastersm_lid ); + ibdev->sm_sl = ( port_info->neighbour_mtu__mastersm_sl & 0xf ); + + if ( ! sma->op->set_port_info ) { + /* Not an error; we just ignore all other settings */ + return 0; + } + + return sma->op->set_port_info ( ibdev, port_info ); +} + +/** + * Get partition key table + * + * @v sma Subnet management agent + * @v get Attribute to get + */ +static void ib_sma_get_pkey_table ( struct ib_sma *sma, + union ib_smp_data *get ) { + struct ib_device *ibdev = sma->ibdev; + struct ib_pkey_table *pkey_table = &get->pkey_table; + + memset ( pkey_table, 0, sizeof ( *pkey_table ) ); + pkey_table->pkey[0] = htons ( ibdev->pkey ); +} + +/** + * Set partition key table + * + * @v sma Subnet management agent + * @v set Attribute to set + */ +static int ib_sma_set_pkey_table ( struct ib_sma *sma, + const union ib_smp_data *get ) { + struct ib_device *ibdev = sma->ibdev; + const struct ib_pkey_table *pkey_table = &get->pkey_table; + + ibdev->pkey = ntohs ( pkey_table->pkey[0] ); + return 0; +} + +/** An attribute handler */ +struct ib_sma_handler { + /** Attribute (in network byte order) */ + uint16_t attr_id; + /** Get attribute + * + * @v sma Subnet management agent + * @v get Attribute to get + * @ret rc Return status code + */ + void ( * get ) ( struct ib_sma *sma, union ib_smp_data *get ); + /** Set attribute + * + * @v sma Subnet management agent + * @v set Attribute to set + * @ret rc Return status code + */ + int ( * set ) ( struct ib_sma *sma, const union ib_smp_data *set ); +}; + +/** List of attribute handlers */ +static struct ib_sma_handler ib_sma_handlers[] = { + { htons ( IB_SMP_ATTR_NODE_DESC ), + ib_sma_get_node_desc, NULL }, + { htons ( IB_SMP_ATTR_NODE_INFO ), + ib_sma_get_node_info, NULL }, + { htons ( IB_SMP_ATTR_GUID_INFO ), + ib_sma_get_guid_info, NULL }, + { htons ( IB_SMP_ATTR_PORT_INFO ), + ib_sma_get_port_info, ib_sma_set_port_info }, + { htons ( IB_SMP_ATTR_PKEY_TABLE ), + ib_sma_get_pkey_table, ib_sma_set_pkey_table }, +}; + +/** + * Identify attribute handler + * + * @v attr_id Attribute ID (in network byte order) + * @ret handler Attribute handler (or NULL) + */ +static struct ib_sma_handler * ib_sma_handler ( uint16_t attr_id ) { + struct ib_sma_handler *handler; + unsigned int i; + + for ( i = 0 ; i < ( sizeof ( ib_sma_handlers ) / + sizeof ( ib_sma_handlers[0] ) ) ; i++ ) { + handler = &ib_sma_handlers[i]; + if ( handler->attr_id == attr_id ) + return handler; + } + + return NULL; +} + +/** + * Respond to management datagram + * + * @v sma Subnet management agent + * @v mad Management datagram + * @ret rc Return status code + */ +static int ib_sma_mad ( struct ib_sma *sma, union ib_mad *mad ) { + struct ib_device *ibdev = sma->ibdev; + struct ib_mad_hdr *hdr = &mad->hdr; + struct ib_mad_smp *smp = &mad->smp; + struct ib_sma_handler *handler = NULL; + unsigned int hop_pointer; + unsigned int hop_count; + int rc; + + DBGC ( sma, "SMA %p received SMP with bv=%02x mc=%02x cv=%02x " + "meth=%02x attr=%04x mod=%08x\n", sma, hdr->base_version, + hdr->mgmt_class, hdr->class_version, hdr->method, + ntohs ( hdr->attr_id ), ntohl ( hdr->attr_mod ) ); + DBGC2_HDA ( sma, 0, mad, sizeof ( *mad ) ); + + /* Sanity checks */ + if ( hdr->base_version != IB_MGMT_BASE_VERSION ) { + DBGC ( sma, "SMA %p unsupported base version %x\n", + sma, hdr->base_version ); + return -ENOTSUP; + } + if ( ( hdr->mgmt_class != IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE ) && + ( hdr->mgmt_class != IB_MGMT_CLASS_SUBN_LID_ROUTED ) ) { + DBGC ( sma, "SMA %p unsupported management class %x\n", + sma, hdr->mgmt_class ); + return -ENOTSUP; + } + if ( hdr->class_version != IB_SMP_CLASS_VERSION ) { + DBGC ( sma, "SMA %p unsupported class version %x\n", + sma, hdr->class_version ); + return -ENOTSUP; + } + if ( ( hdr->method != IB_MGMT_METHOD_GET ) && + ( hdr->method != IB_MGMT_METHOD_SET ) ) { + DBGC ( sma, "SMA %p unsupported method %x\n", + sma, hdr->method ); + return -ENOTSUP; + } + + /* Identify handler */ + if ( ! ( handler = ib_sma_handler ( hdr->attr_id ) ) ) { + DBGC ( sma, "SMA %p unsupported attribute %x\n", + sma, ntohs ( hdr->attr_id ) ); + hdr->status = htons ( IB_MGMT_STATUS_UNSUPPORTED_METHOD_ATTR ); + goto respond_without_data; + } + + /* Set attribute (if applicable) */ + if ( hdr->method != IB_MGMT_METHOD_SET ) { + hdr->status = htons ( IB_MGMT_STATUS_OK ); + goto respond; + } + if ( ! handler->set ) { + DBGC ( sma, "SMA %p attribute %x is unsettable\n", + sma, ntohs ( hdr->attr_id ) ); + hdr->status = htons ( IB_MGMT_STATUS_UNSUPPORTED_METHOD_ATTR ); + goto respond; + } + if ( ( rc = handler->set ( sma, &smp->smp_data ) ) != 0 ) { + DBGC ( sma, "SMA %p could not set attribute %x: %s\n", + sma, ntohs ( hdr->attr_id ), strerror ( rc ) ); + hdr->status = htons ( IB_MGMT_STATUS_UNSUPPORTED_METHOD_ATTR ); + goto respond; + } + + hdr->status = htons ( IB_MGMT_STATUS_OK ); + + respond: + /* Get attribute */ + handler->get ( sma, &smp->smp_data ); + + respond_without_data: + + /* Set method to "Get Response" */ + hdr->method = IB_MGMT_METHOD_GET_RESP; + + /* Set response fields for directed route SMPs */ + if ( hdr->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE ) { + hdr->status |= htons ( IB_SMP_STATUS_D_INBOUND ); + hop_pointer = smp->mad_hdr.class_specific.smp.hop_pointer; + hop_count = smp->mad_hdr.class_specific.smp.hop_count; + assert ( hop_count == hop_pointer ); + if ( hop_pointer < ( sizeof ( smp->return_path.hops ) / + sizeof ( smp->return_path.hops[0] ) ) ) { + smp->return_path.hops[hop_pointer] = ibdev->port; + } else { + DBGC ( sma, "SMA %p invalid hop pointer %d\n", + sma, hop_pointer ); + return -EINVAL; + } + } + + DBGC ( sma, "SMA %p responding with status=%04x\n", + sma, ntohs ( hdr->status ) ); + DBGC2_HDA ( sma, 0, mad, sizeof ( *mad ) ); + + return 0; +} + +/** + * Refill SMA receive ring + * + * @v sma Subnet management agent + */ +static void ib_sma_refill_recv ( struct ib_sma *sma ) { + struct ib_device *ibdev = sma->ibdev; + struct io_buffer *iobuf; + int rc; + + while ( sma->qp->recv.fill < IB_SMA_NUM_RECV_WQES ) { + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( IB_SMA_PAYLOAD_LEN ); + if ( ! iobuf ) { + /* Non-fatal; we will refill on next attempt */ + return; + } + + /* Post I/O buffer */ + if ( ( rc = ib_post_recv ( ibdev, sma->qp, iobuf ) ) != 0 ) { + DBGC ( sma, "SMA %p could not refill: %s\n", + sma, strerror ( rc ) ); + free_iob ( iobuf ); + /* Give up */ + return; + } + } +} + +/** + * Complete SMA send + * + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void ib_sma_complete_send ( struct ib_device *ibdev __unused, + struct ib_queue_pair *qp, + struct io_buffer *iobuf, int rc ) { + struct ib_sma *sma = ib_qp_get_ownerdata ( qp ); + + if ( rc != 0 ) { + DBGC ( sma, "SMA %p send completion error: %s\n", + sma, strerror ( rc ) ); + } + free_iob ( iobuf ); +} + +/** + * Complete SMA receive + * + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v av Address vector + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void ib_sma_complete_recv ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct ib_address_vector *av, + struct io_buffer *iobuf, int rc ) { + struct ib_sma *sma = ib_qp_get_ownerdata ( qp ); + union ib_mad *mad; + + /* Ignore errors */ + if ( rc != 0 ) { + DBGC ( sma, "SMA %p RX error: %s\n", sma, strerror ( rc ) ); + goto err; + } + + /* Sanity check */ + if ( iob_len ( iobuf ) != sizeof ( *mad ) ) { + DBGC ( sma, "SMA %p RX bad size (%zd bytes)\n", + sma, iob_len ( iobuf ) ); + goto err; + } + mad = iobuf->data; + + /* Construct MAD response */ + if ( ( rc = ib_sma_mad ( sma, mad ) ) != 0 ) { + DBGC ( sma, "SMA %p could not construct MAD response: %s\n", + sma, strerror ( rc ) ); + goto err; + } + + /* Send MAD response */ + if ( ( rc = ib_post_send ( ibdev, qp, av, iobuf ) ) != 0 ) { + DBGC ( sma, "SMA %p could not send MAD response: %s\n", + sma, strerror ( rc ) ); + goto err; + } + + return; + + err: + free_iob ( iobuf ); +} + +/** SMA completion operations */ +static struct ib_completion_queue_operations ib_sma_completion_ops = { + .complete_send = ib_sma_complete_send, + .complete_recv = ib_sma_complete_recv, +}; + +/** + * Poll SMA + * + * @v process Process + */ +static void ib_sma_step ( struct process *process ) { + struct ib_sma *sma = + container_of ( process, struct ib_sma, poll ); + struct ib_device *ibdev = sma->ibdev; + + /* Poll the kernel completion queue */ + ib_poll_cq ( ibdev, sma->cq ); + + /* Refill the receive ring */ + ib_sma_refill_recv ( sma ); +} + +/** + * Create SMA + * + * @v sma Subnet management agent + * @v ibdev Infiniband device + * @v op Subnet management operations + * @ret rc Return status code + */ +int ib_create_sma ( struct ib_sma *sma, struct ib_device *ibdev, + struct ib_sma_operations *op ) { + int rc; + + /* Initialise fields */ + memset ( sma, 0, sizeof ( *sma ) ); + sma->ibdev = ibdev; + sma->op = op; + process_init ( &sma->poll, ib_sma_step, &ibdev->refcnt ); + + /* Create completion queue */ + sma->cq = ib_create_cq ( ibdev, IB_SMA_NUM_CQES, + &ib_sma_completion_ops ); + if ( ! sma->cq ) { + rc = -ENOMEM; + goto err_create_cq; + } + + /* Create queue pair */ + sma->qp = ib_create_qp ( ibdev, IB_SMA_NUM_SEND_WQES, sma->cq, + IB_SMA_NUM_RECV_WQES, sma->cq, 0 ); + if ( ! sma->qp ) { + rc = -ENOMEM; + goto err_create_qp; + } + ib_qp_set_ownerdata ( sma->qp, sma ); + + /* If we don't get QP0, we can't function */ + if ( sma->qp->qpn != IB_QPN_SMP ) { + DBGC ( sma, "SMA %p on QPN %lx, needs to be on QPN 0\n", + sma, sma->qp->qpn ); + rc = -ENOTSUP; + goto err_not_qp0; + } + + /* Fill receive ring */ + ib_sma_refill_recv ( sma ); + return 0; + + err_not_qp0: + ib_destroy_qp ( ibdev, sma->qp ); + err_create_qp: + ib_destroy_cq ( ibdev, sma->cq ); + err_create_cq: + process_del ( &sma->poll ); + return rc; +} + +/** + * Destroy SMA + * + * @v sma Subnet management agent + */ +void ib_destroy_sma ( struct ib_sma *sma ) { + struct ib_device *ibdev = sma->ibdev; + + ib_destroy_qp ( ibdev, sma->qp ); + ib_destroy_cq ( ibdev, sma->cq ); + process_del ( &sma->poll ); +} diff --git a/gpxe/src/drivers/infiniband/ib_smc.c b/gpxe/src/drivers/infiniband/ib_smc.c new file mode 100644 index 0000000..af0c4ab --- /dev/null +++ b/gpxe/src/drivers/infiniband/ib_smc.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @file + * + * Infiniband Subnet Management Client + * + */ + +/** + * Get port information + * + * @v ibdev Infiniband device + * @v local_mad Method for issuing local MADs + * @v mad Management datagram to fill in + * @ret rc Return status code + */ +static int ib_smc_get_port_info ( struct ib_device *ibdev, + ib_local_mad_t local_mad, + union ib_mad *mad ) { + int rc; + + /* Construct MAD */ + memset ( mad, 0, sizeof ( *mad ) ); + mad->hdr.base_version = IB_MGMT_BASE_VERSION; + mad->hdr.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED; + mad->hdr.class_version = 1; + mad->hdr.method = IB_MGMT_METHOD_GET; + mad->hdr.attr_id = htons ( IB_SMP_ATTR_PORT_INFO ); + mad->hdr.attr_mod = htonl ( ibdev->port ); + + if ( ( rc = local_mad ( ibdev, mad ) ) != 0 ) { + DBGC ( ibdev, "IBDEV %p could not get port info: %s\n", + ibdev, strerror ( rc ) ); + return rc; + } + return 0; +} + +/** + * Get GUID information + * + * @v ibdev Infiniband device + * @v local_mad Method for issuing local MADs + * @v mad Management datagram to fill in + * @ret rc Return status code + */ +static int ib_smc_get_guid_info ( struct ib_device *ibdev, + ib_local_mad_t local_mad, + union ib_mad *mad ) { + int rc; + + /* Construct MAD */ + memset ( mad, 0, sizeof ( *mad ) ); + mad->hdr.base_version = IB_MGMT_BASE_VERSION; + mad->hdr.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED; + mad->hdr.class_version = 1; + mad->hdr.method = IB_MGMT_METHOD_GET; + mad->hdr.attr_id = htons ( IB_SMP_ATTR_GUID_INFO ); + + if ( ( rc = local_mad ( ibdev, mad ) ) != 0 ) { + DBGC ( ibdev, "IBDEV %p could not get GUID info: %s\n", + ibdev, strerror ( rc ) ); + return rc; + } + return 0; +} + +/** + * Get partition key table + * + * @v ibdev Infiniband device + * @v local_mad Method for issuing local MADs + * @v mad Management datagram to fill in + * @ret rc Return status code + */ +static int ib_smc_get_pkey_table ( struct ib_device *ibdev, + ib_local_mad_t local_mad, + union ib_mad *mad ) { + int rc; + + /* Construct MAD */ + memset ( mad, 0, sizeof ( *mad ) ); + mad->hdr.base_version = IB_MGMT_BASE_VERSION; + mad->hdr.mgmt_class = IB_MGMT_CLASS_SUBN_LID_ROUTED; + mad->hdr.class_version = 1; + mad->hdr.method = IB_MGMT_METHOD_GET; + mad->hdr.attr_id = htons ( IB_SMP_ATTR_PKEY_TABLE ); + + if ( ( rc = local_mad ( ibdev, mad ) ) != 0 ) { + DBGC ( ibdev, "IBDEV %p could not get pkey table: %s\n", + ibdev, strerror ( rc ) ); + return rc; + } + return 0; +} + +/** + * Get MAD parameters + * + * @v ibdev Infiniband device + * @v local_mad Method for issuing local MADs + * @ret rc Return status code + */ +int ib_smc_update ( struct ib_device *ibdev, ib_local_mad_t local_mad ) { + union ib_mad mad; + union ib_smp_data *smp = &mad.smp.smp_data; + int rc; + + /* Port info gives us the link state, the first half of the + * port GID and the SM LID. + */ + if ( ( rc = ib_smc_get_port_info ( ibdev, local_mad, &mad ) ) != 0 ) + return rc; + ibdev->port_state = + ( smp->port_info.link_speed_supported__port_state & 0x0f ); + memcpy ( &ibdev->gid.u.half[0], smp->port_info.gid_prefix, + sizeof ( ibdev->gid.u.half[0] ) ); + ibdev->lid = ntohs ( smp->port_info.lid ); + ibdev->sm_lid = ntohs ( smp->port_info.mastersm_lid ); + ibdev->sm_sl = ( smp->port_info.neighbour_mtu__mastersm_sl & 0xf ); + + /* GUID info gives us the second half of the port GID */ + if ( ( rc = ib_smc_get_guid_info ( ibdev, local_mad, &mad ) ) != 0 ) + return rc; + memcpy ( &ibdev->gid.u.half[1], smp->guid_info.guid[0], + sizeof ( ibdev->gid.u.half[1] ) ); + + /* Get partition key */ + if ( ( rc = ib_smc_get_pkey_table ( ibdev, local_mad, &mad ) ) != 0 ) + return rc; + ibdev->pkey = ntohs ( smp->pkey_table.pkey[0] ); + + DBGC ( ibdev, "IBDEV %p port GID is %08x:%08x:%08x:%08x\n", ibdev, + htonl ( ibdev->gid.u.dwords[0] ), + htonl ( ibdev->gid.u.dwords[1] ), + htonl ( ibdev->gid.u.dwords[2] ), + htonl ( ibdev->gid.u.dwords[3] ) ); + + return 0; +} diff --git a/gpxe/src/drivers/infiniband/linda.c b/gpxe/src/drivers/infiniband/linda.c new file mode 100644 index 0000000..c5d1317 --- /dev/null +++ b/gpxe/src/drivers/infiniband/linda.c @@ -0,0 +1,2397 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "linda.h" + +/** + * @file + * + * QLogic Linda Infiniband HCA + * + */ + +/** A Linda send work queue */ +struct linda_send_work_queue { + /** Send buffer usage */ + uint8_t *send_buf; + /** Producer index */ + unsigned int prod; + /** Consumer index */ + unsigned int cons; +}; + +/** A Linda receive work queue */ +struct linda_recv_work_queue { + /** Receive header ring */ + void *header; + /** Receive header producer offset (written by hardware) */ + struct QIB_7220_scalar header_prod; + /** Receive header consumer offset */ + unsigned int header_cons; + /** Offset within register space of the eager array */ + unsigned long eager_array; + /** Number of entries in eager array */ + unsigned int eager_entries; + /** Eager array producer index */ + unsigned int eager_prod; + /** Eager array consumer index */ + unsigned int eager_cons; +}; + +/** A Linda HCA */ +struct linda { + /** Registers */ + void *regs; + + /** In-use contexts */ + uint8_t used_ctx[LINDA_NUM_CONTEXTS]; + /** Send work queues */ + struct linda_send_work_queue send_wq[LINDA_NUM_CONTEXTS]; + /** Receive work queues */ + struct linda_recv_work_queue recv_wq[LINDA_NUM_CONTEXTS]; + + /** Offset within register space of the first send buffer */ + unsigned long send_buffer_base; + /** Send buffer availability (reported by hardware) */ + struct QIB_7220_SendBufAvail *sendbufavail; + /** Send buffer availability (maintained by software) */ + uint8_t send_buf[LINDA_MAX_SEND_BUFS]; + /** Send buffer availability producer counter */ + unsigned int send_buf_prod; + /** Send buffer availability consumer counter */ + unsigned int send_buf_cons; + /** Number of reserved send buffers (across all QPs) */ + unsigned int reserved_send_bufs; + + /** I2C bit-bashing interface */ + struct i2c_bit_basher i2c; + /** I2C serial EEPROM */ + struct i2c_device eeprom; + + /** Subnet management agent */ + struct ib_sma sma; +}; + +/*************************************************************************** + * + * Linda register access + * + *************************************************************************** + * + * This card requires atomic 64-bit accesses. Strange things happen + * if you try to use 32-bit accesses; sometimes they work, sometimes + * they don't, sometimes you get random data. + * + * These accessors use the "movq" MMX instruction, and so won't work + * on really old Pentiums (which won't have PCIe anyway, so this is + * something of a moot point). + */ + +/** + * Read Linda qword register + * + * @v linda Linda device + * @v dwords Register buffer to read into + * @v offset Register offset + */ +static void linda_readq ( struct linda *linda, uint32_t *dwords, + unsigned long offset ) { + void *addr = ( linda->regs + offset ); + + __asm__ __volatile__ ( "movq (%1), %%mm0\n\t" + "movq %%mm0, (%0)\n\t" + : : "r" ( dwords ), "r" ( addr ) : "memory" ); + + DBGIO ( "[%08lx] => %08x%08x\n", + virt_to_phys ( addr ), dwords[1], dwords[0] ); +} +#define linda_readq( _linda, _ptr, _offset ) \ + linda_readq ( (_linda), (_ptr)->u.dwords, (_offset) ) +#define linda_readq_array8b( _linda, _ptr, _offset, _idx ) \ + linda_readq ( (_linda), (_ptr), ( (_offset) + ( (_idx) * 8 ) ) ) +#define linda_readq_array64k( _linda, _ptr, _offset, _idx ) \ + linda_readq ( (_linda), (_ptr), ( (_offset) + ( (_idx) * 65536 ) ) ) + +/** + * Write Linda qword register + * + * @v linda Linda device + * @v dwords Register buffer to write + * @v offset Register offset + */ +static void linda_writeq ( struct linda *linda, const uint32_t *dwords, + unsigned long offset ) { + void *addr = ( linda->regs + offset ); + + DBGIO ( "[%08lx] <= %08x%08x\n", + virt_to_phys ( addr ), dwords[1], dwords[0] ); + + __asm__ __volatile__ ( "movq (%0), %%mm0\n\t" + "movq %%mm0, (%1)\n\t" + : : "r" ( dwords ), "r" ( addr ) : "memory" ); +} +#define linda_writeq( _linda, _ptr, _offset ) \ + linda_writeq ( (_linda), (_ptr)->u.dwords, (_offset) ) +#define linda_writeq_array8b( _linda, _ptr, _offset, _idx ) \ + linda_writeq ( (_linda), (_ptr), ( (_offset) + ( (_idx) * 8 ) ) ) +#define linda_writeq_array64k( _linda, _ptr, _offset, _idx ) \ + linda_writeq ( (_linda), (_ptr), ( (_offset) + ( (_idx) * 65536 ) ) ) + +/** + * Write Linda dword register + * + * @v linda Linda device + * @v dword Value to write + * @v offset Register offset + */ +static void linda_writel ( struct linda *linda, uint32_t dword, + unsigned long offset ) { + writel ( dword, ( linda->regs + offset ) ); +} + +/*************************************************************************** + * + * Link state management + * + *************************************************************************** + */ + +/** + * Textual representation of link state + * + * @v link_state Link state + * @ret link_text Link state text + */ +static const char * linda_link_state_text ( unsigned int link_state ) { + switch ( link_state ) { + case LINDA_LINK_STATE_DOWN: return "DOWN"; + case LINDA_LINK_STATE_INIT: return "INIT"; + case LINDA_LINK_STATE_ARM: return "ARM"; + case LINDA_LINK_STATE_ACTIVE: return "ACTIVE"; + case LINDA_LINK_STATE_ACT_DEFER:return "ACT_DEFER"; + default: return "UNKNOWN"; + } +} + +/** + * Handle link state change + * + * @v linda Linda device + */ +static void linda_link_state_changed ( struct ib_device *ibdev ) { + struct linda *linda = ib_get_drvdata ( ibdev ); + struct QIB_7220_IBCStatus ibcstatus; + struct QIB_7220_EXTCtrl extctrl; + unsigned int link_state; + unsigned int link_width; + unsigned int link_speed; + + /* Read link state */ + linda_readq ( linda, &ibcstatus, QIB_7220_IBCStatus_offset ); + link_state = BIT_GET ( &ibcstatus, LinkState ); + link_width = BIT_GET ( &ibcstatus, LinkWidthActive ); + link_speed = BIT_GET ( &ibcstatus, LinkSpeedActive ); + DBGC ( linda, "Linda %p link state %s (%s %s)\n", linda, + linda_link_state_text ( link_state ), + ( link_speed ? "DDR" : "SDR" ), ( link_width ? "x4" : "x1" ) ); + + /* Set LEDs according to link state */ + linda_readq ( linda, &extctrl, QIB_7220_EXTCtrl_offset ); + BIT_SET ( &extctrl, LEDPriPortGreenOn, + ( ( link_state >= LINDA_LINK_STATE_INIT ) ? 1 : 0 ) ); + BIT_SET ( &extctrl, LEDPriPortYellowOn, + ( ( link_state >= LINDA_LINK_STATE_ACTIVE ) ? 1 : 0 ) ); + linda_writeq ( linda, &extctrl, QIB_7220_EXTCtrl_offset ); + + /* Notify Infiniband core of link state change */ + ibdev->port_state = ( link_state + 1 ); + ibdev->link_width = + ( link_width ? IB_LINK_WIDTH_4X : IB_LINK_WIDTH_1X ); + ibdev->link_speed = + ( link_speed ? IB_LINK_SPEED_DDR : IB_LINK_SPEED_SDR ); + ib_link_state_changed ( ibdev ); +} + +/** + * Set port information + * + * @v ibdev Infiniband device + * @v port_info New port information + */ +static int linda_set_port_info ( struct ib_device *ibdev, + const struct ib_port_info *port_info ) { + struct linda *linda = ib_get_drvdata ( ibdev ); + struct QIB_7220_IBCCtrl ibcctrl; + unsigned int port_state; + unsigned int link_state; + + /* Set new link state */ + port_state = ( port_info->link_speed_supported__port_state & 0xf ); + if ( port_state ) { + link_state = ( port_state - 1 ); + DBGC ( linda, "Linda %p set link state to %s (%x)\n", linda, + linda_link_state_text ( link_state ), link_state ); + linda_readq ( linda, &ibcctrl, QIB_7220_IBCCtrl_offset ); + BIT_SET ( &ibcctrl, LinkCmd, link_state ); + linda_writeq ( linda, &ibcctrl, QIB_7220_IBCCtrl_offset ); + } + + /* Detect and report link state change */ + linda_link_state_changed ( ibdev ); + + return 0; +} + +/** Linda subnet management operations */ +static struct ib_sma_operations linda_sma_operations = { + .set_port_info = linda_set_port_info, +}; + +/*************************************************************************** + * + * Context allocation + * + *************************************************************************** + */ + +/** + * Map context number to QPN + * + * @v ctx Context index + * @ret qpn Queue pair number + */ +static int linda_ctx_to_qpn ( unsigned int ctx ) { + /* This mapping is fixed by hardware */ + return ( ctx * 2 ); +} + +/** + * Map QPN to context number + * + * @v qpn Queue pair number + * @ret ctx Context index + */ +static int linda_qpn_to_ctx ( unsigned int qpn ) { + /* This mapping is fixed by hardware */ + return ( qpn / 2 ); +} + +/** + * Allocate a context + * + * @v linda Linda device + * @ret ctx Context index, or negative error + */ +static int linda_alloc_ctx ( struct linda *linda ) { + unsigned int ctx; + + for ( ctx = 0 ; ctx < LINDA_NUM_CONTEXTS ; ctx++ ) { + + if ( ! linda->used_ctx[ctx] ) { + linda->used_ctx[ctx ] = 1; + DBGC2 ( linda, "Linda %p CTX %d allocated\n", + linda, ctx ); + return ctx; + } + } + + DBGC ( linda, "Linda %p out of available contexts\n", linda ); + return -ENOENT; +} + +/** + * Free a context + * + * @v linda Linda device + * @v ctx Context index + */ +static void linda_free_ctx ( struct linda *linda, unsigned int ctx ) { + + linda->used_ctx[ctx] = 0; + DBGC2 ( linda, "Linda %p CTX %d freed\n", linda, ctx ); +} + +/*************************************************************************** + * + * Send datapath + * + *************************************************************************** + */ + +/** Send buffer toggle bit + * + * We encode send buffers as 7 bits of send buffer index plus a single + * bit which should match the "check" bit in the SendBufAvail array. + */ +#define LINDA_SEND_BUF_TOGGLE 0x80 + +/** + * Allocate a send buffer + * + * @v linda Linda device + * @ret send_buf Send buffer + * + * You must guarantee that a send buffer is available. This is done + * by refusing to allocate more TX WQEs in total than the number of + * available send buffers. + */ +static unsigned int linda_alloc_send_buf ( struct linda *linda ) { + unsigned int send_buf; + + send_buf = linda->send_buf[linda->send_buf_cons]; + send_buf ^= LINDA_SEND_BUF_TOGGLE; + linda->send_buf_cons = ( ( linda->send_buf_cons + 1 ) % + LINDA_MAX_SEND_BUFS ); + return send_buf; +} + +/** + * Free a send buffer + * + * @v linda Linda device + * @v send_buf Send buffer + */ +static void linda_free_send_buf ( struct linda *linda, + unsigned int send_buf ) { + linda->send_buf[linda->send_buf_prod] = send_buf; + linda->send_buf_prod = ( ( linda->send_buf_prod + 1 ) % + LINDA_MAX_SEND_BUFS ); +} + +/** + * Check to see if send buffer is in use + * + * @v linda Linda device + * @v send_buf Send buffer + * @ret in_use Send buffer is in use + */ +static int linda_send_buf_in_use ( struct linda *linda, + unsigned int send_buf ) { + unsigned int send_idx; + unsigned int send_check; + unsigned int inusecheck; + unsigned int inuse; + unsigned int check; + + send_idx = ( send_buf & ~LINDA_SEND_BUF_TOGGLE ); + send_check = ( !! ( send_buf & LINDA_SEND_BUF_TOGGLE ) ); + inusecheck = BIT_GET ( linda->sendbufavail, InUseCheck[send_idx] ); + inuse = ( !! ( inusecheck & 0x02 ) ); + check = ( !! ( inusecheck & 0x01 ) ); + return ( inuse || ( check != send_check ) ); +} + +/** + * Calculate starting offset for send buffer + * + * @v linda Linda device + * @v send_buf Send buffer + * @ret offset Starting offset + */ +static unsigned long linda_send_buffer_offset ( struct linda *linda, + unsigned int send_buf ) { + return ( linda->send_buffer_base + + ( ( send_buf & ~LINDA_SEND_BUF_TOGGLE ) * + LINDA_SEND_BUF_SIZE ) ); +} + +/** + * Create send work queue + * + * @v linda Linda device + * @v qp Queue pair + */ +static int linda_create_send_wq ( struct linda *linda, + struct ib_queue_pair *qp ) { + struct ib_work_queue *wq = &qp->send; + struct linda_send_work_queue *linda_wq = ib_wq_get_drvdata ( wq ); + int rc; + + /* Reserve send buffers */ + if ( ( linda->reserved_send_bufs + qp->send.num_wqes ) > + LINDA_MAX_SEND_BUFS ) { + DBGC ( linda, "Linda %p out of send buffers (have %d, used " + "%d, need %d)\n", linda, LINDA_MAX_SEND_BUFS, + linda->reserved_send_bufs, qp->send.num_wqes ); + rc = -ENOBUFS; + goto err_reserve_bufs; + } + linda->reserved_send_bufs += qp->send.num_wqes; + + /* Reset work queue */ + linda_wq->prod = 0; + linda_wq->cons = 0; + + /* Allocate space for send buffer uasge list */ + linda_wq->send_buf = zalloc ( qp->send.num_wqes * + sizeof ( linda_wq->send_buf[0] ) ); + if ( ! linda_wq->send_buf ) { + rc = -ENOBUFS; + goto err_alloc_send_buf; + } + + return 0; + + free ( linda_wq->send_buf ); + err_alloc_send_buf: + linda->reserved_send_bufs -= qp->send.num_wqes; + err_reserve_bufs: + return rc; +} + +/** + * Destroy send work queue + * + * @v linda Linda device + * @v qp Queue pair + */ +static void linda_destroy_send_wq ( struct linda *linda, + struct ib_queue_pair *qp ) { + struct ib_work_queue *wq = &qp->send; + struct linda_send_work_queue *linda_wq = ib_wq_get_drvdata ( wq ); + + free ( linda_wq->send_buf ); + linda->reserved_send_bufs -= qp->send.num_wqes; +} + +/** + * Initialise send datapath + * + * @v linda Linda device + * @ret rc Return status code + */ +static int linda_init_send ( struct linda *linda ) { + struct QIB_7220_SendBufBase sendbufbase; + struct QIB_7220_SendBufAvailAddr sendbufavailaddr; + struct QIB_7220_SendCtrl sendctrl; + unsigned int i; + int rc; + + /* Retrieve SendBufBase */ + linda_readq ( linda, &sendbufbase, QIB_7220_SendBufBase_offset ); + linda->send_buffer_base = BIT_GET ( &sendbufbase, + BaseAddr_SmallPIO ); + DBGC ( linda, "Linda %p send buffers at %lx\n", + linda, linda->send_buffer_base ); + + /* Initialise the send_buf[] array */ + for ( i = 0 ; i < LINDA_MAX_SEND_BUFS ; i++ ) + linda->send_buf[i] = i; + + /* Allocate space for the SendBufAvail array */ + linda->sendbufavail = malloc_dma ( sizeof ( *linda->sendbufavail ), + LINDA_SENDBUFAVAIL_ALIGN ); + if ( ! linda->sendbufavail ) { + rc = -ENOMEM; + goto err_alloc_sendbufavail; + } + memset ( linda->sendbufavail, 0, sizeof ( linda->sendbufavail ) ); + + /* Program SendBufAvailAddr into the hardware */ + memset ( &sendbufavailaddr, 0, sizeof ( sendbufavailaddr ) ); + BIT_FILL_1 ( &sendbufavailaddr, SendBufAvailAddr, + ( virt_to_bus ( linda->sendbufavail ) >> 6 ) ); + linda_writeq ( linda, &sendbufavailaddr, + QIB_7220_SendBufAvailAddr_offset ); + + /* Enable sending and DMA of SendBufAvail */ + memset ( &sendctrl, 0, sizeof ( sendctrl ) ); + BIT_FILL_2 ( &sendctrl, + SendBufAvailUpd, 1, + SPioEnable, 1 ); + linda_writeq ( linda, &sendctrl, QIB_7220_SendCtrl_offset ); + + return 0; + + free_dma ( linda->sendbufavail, sizeof ( *linda->sendbufavail ) ); + err_alloc_sendbufavail: + return rc; +} + +/** + * Shut down send datapath + * + * @v linda Linda device + */ +static void linda_fini_send ( struct linda *linda ) { + struct QIB_7220_SendCtrl sendctrl; + + /* Disable sending and DMA of SendBufAvail */ + memset ( &sendctrl, 0, sizeof ( sendctrl ) ); + linda_writeq ( linda, &sendctrl, QIB_7220_SendCtrl_offset ); + mb(); + + /* Ensure hardware has seen this disable */ + linda_readq ( linda, &sendctrl, QIB_7220_SendCtrl_offset ); + + free_dma ( linda->sendbufavail, sizeof ( *linda->sendbufavail ) ); +} + +/*************************************************************************** + * + * Receive datapath + * + *************************************************************************** + */ + +/** + * Create receive work queue + * + * @v linda Linda device + * @v qp Queue pair + * @ret rc Return status code + */ +static int linda_create_recv_wq ( struct linda *linda, + struct ib_queue_pair *qp ) { + struct ib_work_queue *wq = &qp->recv; + struct linda_recv_work_queue *linda_wq = ib_wq_get_drvdata ( wq ); + struct QIB_7220_RcvHdrAddr0 rcvhdraddr; + struct QIB_7220_RcvHdrTailAddr0 rcvhdrtailaddr; + struct QIB_7220_RcvHdrHead0 rcvhdrhead; + struct QIB_7220_scalar rcvegrindexhead; + struct QIB_7220_RcvCtrl rcvctrl; + unsigned int ctx = linda_qpn_to_ctx ( qp->qpn ); + int rc; + + /* Reset context information */ + memset ( &linda_wq->header_prod, 0, + sizeof ( linda_wq->header_prod ) ); + linda_wq->header_cons = 0; + linda_wq->eager_prod = 0; + linda_wq->eager_cons = 0; + + /* Allocate receive header buffer */ + linda_wq->header = malloc_dma ( LINDA_RECV_HEADERS_SIZE, + LINDA_RECV_HEADERS_ALIGN ); + if ( ! linda_wq->header ) { + rc = -ENOMEM; + goto err_alloc_header; + } + + /* Enable context in hardware */ + memset ( &rcvhdraddr, 0, sizeof ( rcvhdraddr ) ); + BIT_FILL_1 ( &rcvhdraddr, RcvHdrAddr0, + ( virt_to_bus ( linda_wq->header ) >> 2 ) ); + linda_writeq_array8b ( linda, &rcvhdraddr, + QIB_7220_RcvHdrAddr0_offset, ctx ); + memset ( &rcvhdrtailaddr, 0, sizeof ( rcvhdrtailaddr ) ); + BIT_FILL_1 ( &rcvhdrtailaddr, RcvHdrTailAddr0, + ( virt_to_bus ( &linda_wq->header_prod ) >> 2 ) ); + linda_writeq_array8b ( linda, &rcvhdrtailaddr, + QIB_7220_RcvHdrTailAddr0_offset, ctx ); + memset ( &rcvhdrhead, 0, sizeof ( rcvhdrhead ) ); + BIT_FILL_1 ( &rcvhdrhead, counter, 1 ); + linda_writeq_array64k ( linda, &rcvhdrhead, + QIB_7220_RcvHdrHead0_offset, ctx ); + memset ( &rcvegrindexhead, 0, sizeof ( rcvegrindexhead ) ); + BIT_FILL_1 ( &rcvegrindexhead, Value, 1 ); + linda_writeq_array64k ( linda, &rcvegrindexhead, + QIB_7220_RcvEgrIndexHead0_offset, ctx ); + linda_readq ( linda, &rcvctrl, QIB_7220_RcvCtrl_offset ); + BIT_SET ( &rcvctrl, PortEnable[ctx], 1 ); + BIT_SET ( &rcvctrl, IntrAvail[ctx], 1 ); + linda_writeq ( linda, &rcvctrl, QIB_7220_RcvCtrl_offset ); + + DBGC ( linda, "Linda %p QPN %ld CTX %d hdrs [%lx,%lx) prod %lx\n", + linda, qp->qpn, ctx, virt_to_bus ( linda_wq->header ), + ( virt_to_bus ( linda_wq->header ) + LINDA_RECV_HEADERS_SIZE ), + virt_to_bus ( &linda_wq->header_prod ) ); + return 0; + + free_dma ( linda_wq->header, LINDA_RECV_HEADERS_SIZE ); + err_alloc_header: + return rc; +} + +/** + * Destroy receive work queue + * + * @v linda Linda device + * @v qp Queue pair + */ +static void linda_destroy_recv_wq ( struct linda *linda, + struct ib_queue_pair *qp ) { + struct ib_work_queue *wq = &qp->recv; + struct linda_recv_work_queue *linda_wq = ib_wq_get_drvdata ( wq ); + struct QIB_7220_RcvCtrl rcvctrl; + unsigned int ctx = linda_qpn_to_ctx ( qp->qpn ); + + /* Disable context in hardware */ + linda_readq ( linda, &rcvctrl, QIB_7220_RcvCtrl_offset ); + BIT_SET ( &rcvctrl, PortEnable[ctx], 0 ); + BIT_SET ( &rcvctrl, IntrAvail[ctx], 0 ); + linda_writeq ( linda, &rcvctrl, QIB_7220_RcvCtrl_offset ); + + /* Make sure the hardware has seen that the context is disabled */ + linda_readq ( linda, &rcvctrl, QIB_7220_RcvCtrl_offset ); + mb(); + + /* Free headers ring */ + free_dma ( linda_wq->header, LINDA_RECV_HEADERS_SIZE ); + + /* Free context */ + linda_free_ctx ( linda, ctx ); +} + +/** + * Initialise receive datapath + * + * @v linda Linda device + * @ret rc Return status code + */ +static int linda_init_recv ( struct linda *linda ) { + struct QIB_7220_RcvCtrl rcvctrl; + struct QIB_7220_scalar rcvegrbase; + struct QIB_7220_scalar rcvhdrentsize; + struct QIB_7220_scalar rcvhdrcnt; + struct QIB_7220_RcvBTHQP rcvbthqp; + unsigned int portcfg; + unsigned long egrbase; + unsigned int eager_array_size_0; + unsigned int eager_array_size_other; + unsigned int ctx; + + /* Select configuration based on number of contexts */ + switch ( LINDA_NUM_CONTEXTS ) { + case 5: + portcfg = LINDA_PORTCFG_5CTX; + eager_array_size_0 = LINDA_EAGER_ARRAY_SIZE_5CTX_0; + eager_array_size_other = LINDA_EAGER_ARRAY_SIZE_5CTX_OTHER; + break; + case 9: + portcfg = LINDA_PORTCFG_9CTX; + eager_array_size_0 = LINDA_EAGER_ARRAY_SIZE_9CTX_0; + eager_array_size_other = LINDA_EAGER_ARRAY_SIZE_9CTX_OTHER; + break; + case 17: + portcfg = LINDA_PORTCFG_17CTX; + eager_array_size_0 = LINDA_EAGER_ARRAY_SIZE_17CTX_0; + eager_array_size_other = LINDA_EAGER_ARRAY_SIZE_17CTX_OTHER; + break; + default: + linker_assert ( 0, invalid_LINDA_NUM_CONTEXTS ); + return -EINVAL; + } + + /* Configure number of contexts */ + memset ( &rcvctrl, 0, sizeof ( rcvctrl ) ); + BIT_FILL_3 ( &rcvctrl, + TailUpd, 1, + PortCfg, portcfg, + RcvQPMapEnable, 1 ); + linda_writeq ( linda, &rcvctrl, QIB_7220_RcvCtrl_offset ); + + /* Configure receive header buffer sizes */ + memset ( &rcvhdrcnt, 0, sizeof ( rcvhdrcnt ) ); + BIT_FILL_1 ( &rcvhdrcnt, Value, LINDA_RECV_HEADER_COUNT ); + linda_writeq ( linda, &rcvhdrcnt, QIB_7220_RcvHdrCnt_offset ); + memset ( &rcvhdrentsize, 0, sizeof ( rcvhdrentsize ) ); + BIT_FILL_1 ( &rcvhdrentsize, Value, ( LINDA_RECV_HEADER_SIZE >> 2 ) ); + linda_writeq ( linda, &rcvhdrentsize, QIB_7220_RcvHdrEntSize_offset ); + + /* Calculate eager array start addresses for each context */ + linda_readq ( linda, &rcvegrbase, QIB_7220_RcvEgrBase_offset ); + egrbase = BIT_GET ( &rcvegrbase, Value ); + linda->recv_wq[0].eager_array = egrbase; + linda->recv_wq[0].eager_entries = eager_array_size_0; + egrbase += ( eager_array_size_0 * sizeof ( struct QIB_7220_RcvEgr ) ); + for ( ctx = 1 ; ctx < LINDA_NUM_CONTEXTS ; ctx++ ) { + linda->recv_wq[ctx].eager_array = egrbase; + linda->recv_wq[ctx].eager_entries = eager_array_size_other; + egrbase += ( eager_array_size_other * + sizeof ( struct QIB_7220_RcvEgr ) ); + } + for ( ctx = 0 ; ctx < LINDA_NUM_CONTEXTS ; ctx++ ) { + DBGC ( linda, "Linda %p CTX %d eager array at %lx (%d " + "entries)\n", linda, ctx, + linda->recv_wq[ctx].eager_array, + linda->recv_wq[ctx].eager_entries ); + } + + /* Set the BTH QP for Infinipath packets to an unused value */ + memset ( &rcvbthqp, 0, sizeof ( rcvbthqp ) ); + BIT_FILL_1 ( &rcvbthqp, RcvBTHQP, LINDA_QP_IDETH ); + linda_writeq ( linda, &rcvbthqp, QIB_7220_RcvBTHQP_offset ); + + return 0; +} + +/** + * Shut down receive datapath + * + * @v linda Linda device + */ +static void linda_fini_recv ( struct linda *linda __unused ) { + /* Nothing to do; all contexts were already disabled when the + * queue pairs were destroyed + */ +} + +/*************************************************************************** + * + * Completion queue operations + * + *************************************************************************** + */ + +/** + * Create completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + * @ret rc Return status code + */ +static int linda_create_cq ( struct ib_device *ibdev, + struct ib_completion_queue *cq ) { + struct linda *linda = ib_get_drvdata ( ibdev ); + static int cqn; + + /* The hardware has no concept of completion queues. We + * simply use the association between CQs and WQs (already + * handled by the IB core) to decide which WQs to poll. + * + * We do set a CQN, just to avoid confusing debug messages + * from the IB core. + */ + cq->cqn = ++cqn; + DBGC ( linda, "Linda %p CQN %ld created\n", linda, cq->cqn ); + + return 0; +} + +/** + * Destroy completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + */ +static void linda_destroy_cq ( struct ib_device *ibdev, + struct ib_completion_queue *cq ) { + struct linda *linda = ib_get_drvdata ( ibdev ); + + /* Nothing to do */ + DBGC ( linda, "Linda %p CQN %ld destroyed\n", linda, cq->cqn ); +} + +/*************************************************************************** + * + * Queue pair operations + * + *************************************************************************** + */ + +/** + * Create queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @ret rc Return status code + */ +static int linda_create_qp ( struct ib_device *ibdev, + struct ib_queue_pair *qp ) { + struct linda *linda = ib_get_drvdata ( ibdev ); + int ctx; + int rc; + + /* Locate an available context */ + ctx = linda_alloc_ctx ( linda ); + if ( ctx < 0 ) { + rc = ctx; + goto err_alloc_ctx; + } + + /* Set queue pair number based on context index */ + qp->qpn = linda_ctx_to_qpn ( ctx ); + + /* Set work-queue private data pointers */ + ib_wq_set_drvdata ( &qp->send, &linda->send_wq[ctx] ); + ib_wq_set_drvdata ( &qp->recv, &linda->recv_wq[ctx] ); + + /* Create receive work queue */ + if ( ( rc = linda_create_recv_wq ( linda, qp ) ) != 0 ) + goto err_create_recv_wq; + + /* Create send work queue */ + if ( ( rc = linda_create_send_wq ( linda, qp ) ) != 0 ) + goto err_create_send_wq; + + return 0; + + linda_destroy_send_wq ( linda, qp ); + err_create_send_wq: + linda_destroy_recv_wq ( linda, qp ); + err_create_recv_wq: + linda_free_ctx ( linda, ctx ); + err_alloc_ctx: + return rc; +} + +/** + * Modify queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v mod_list Modification list + * @ret rc Return status code + */ +static int linda_modify_qp ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + unsigned long mod_list __unused ) { + struct linda *linda = ib_get_drvdata ( ibdev ); + + /* Nothing to do; the hardware doesn't have a notion of queue + * keys + */ + DBGC ( linda, "Linda %p QPN %ld modified\n", linda, qp->qpn ); + return 0; +} + +/** + * Destroy queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + */ +static void linda_destroy_qp ( struct ib_device *ibdev, + struct ib_queue_pair *qp ) { + struct linda *linda = ib_get_drvdata ( ibdev ); + + linda_destroy_send_wq ( linda, qp ); + linda_destroy_recv_wq ( linda, qp ); +} + +/*************************************************************************** + * + * Work request operations + * + *************************************************************************** + */ + +/** + * Post send work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v av Address vector + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int linda_post_send ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct ib_address_vector *av, + struct io_buffer *iobuf ) { + struct linda *linda = ib_get_drvdata ( ibdev ); + struct ib_work_queue *wq = &qp->send; + struct linda_send_work_queue *linda_wq = ib_wq_get_drvdata ( wq ); + struct QIB_7220_SendPbc sendpbc; + uint8_t header_buf[IB_MAX_HEADER_SIZE]; + struct io_buffer headers; + unsigned int send_buf; + unsigned long start_offset; + unsigned long offset; + size_t len; + ssize_t frag_len; + uint32_t *data; + + /* Allocate send buffer and calculate offset */ + send_buf = linda_alloc_send_buf ( linda ); + start_offset = offset = linda_send_buffer_offset ( linda, send_buf ); + + /* Store I/O buffer and send buffer index */ + assert ( wq->iobufs[linda_wq->prod] == NULL ); + wq->iobufs[linda_wq->prod] = iobuf; + linda_wq->send_buf[linda_wq->prod] = send_buf; + + /* Construct headers */ + iob_populate ( &headers, header_buf, 0, sizeof ( header_buf ) ); + iob_reserve ( &headers, sizeof ( header_buf ) ); + ib_push ( ibdev, &headers, qp, iob_len ( iobuf ), av ); + + /* Calculate packet length */ + len = ( ( sizeof ( sendpbc ) + iob_len ( &headers ) + + iob_len ( iobuf ) + 3 ) & ~3 ); + + /* Construct send per-buffer control word */ + memset ( &sendpbc, 0, sizeof ( sendpbc ) ); + BIT_FILL_2 ( &sendpbc, + LengthP1_toibc, ( ( len >> 2 ) - 1 ), + VL15, 1 ); + + /* Write SendPbc */ + DBG_DISABLE ( DBGLVL_IO ); + linda_writeq ( linda, &sendpbc, offset ); + offset += sizeof ( sendpbc ); + + /* Write headers */ + for ( data = headers.data, frag_len = iob_len ( &headers ) ; + frag_len > 0 ; data++, offset += 4, frag_len -= 4 ) { + linda_writel ( linda, *data, offset ); + } + + /* Write data */ + for ( data = iobuf->data, frag_len = iob_len ( iobuf ) ; + frag_len > 0 ; data++, offset += 4, frag_len -= 4 ) { + linda_writel ( linda, *data, offset ); + } + DBG_ENABLE ( DBGLVL_IO ); + + assert ( ( start_offset + len ) == offset ); + DBGC2 ( linda, "Linda %p QPN %ld TX %d(%d) posted [%lx,%lx)\n", + linda, qp->qpn, send_buf, linda_wq->prod, + start_offset, offset ); + + /* Increment producer counter */ + linda_wq->prod = ( ( linda_wq->prod + 1 ) & ( wq->num_wqes - 1 ) ); + + return 0; +} + +/** + * Complete send work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v wqe_idx Work queue entry index + */ +static void linda_complete_send ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + unsigned int wqe_idx ) { + struct linda *linda = ib_get_drvdata ( ibdev ); + struct ib_work_queue *wq = &qp->send; + struct linda_send_work_queue *linda_wq = ib_wq_get_drvdata ( wq ); + struct io_buffer *iobuf; + unsigned int send_buf; + + /* Parse completion */ + send_buf = linda_wq->send_buf[wqe_idx]; + DBGC2 ( linda, "Linda %p QPN %ld TX %d(%d) complete\n", + linda, qp->qpn, send_buf, wqe_idx ); + + /* Complete work queue entry */ + iobuf = wq->iobufs[wqe_idx]; + assert ( iobuf != NULL ); + ib_complete_send ( ibdev, qp, iobuf, 0 ); + wq->iobufs[wqe_idx] = NULL; + + /* Free send buffer */ + linda_free_send_buf ( linda, send_buf ); +} + +/** + * Poll send work queue + * + * @v ibdev Infiniband device + * @v qp Queue pair + */ +static void linda_poll_send_wq ( struct ib_device *ibdev, + struct ib_queue_pair *qp ) { + struct linda *linda = ib_get_drvdata ( ibdev ); + struct ib_work_queue *wq = &qp->send; + struct linda_send_work_queue *linda_wq = ib_wq_get_drvdata ( wq ); + unsigned int send_buf; + + /* Look for completions */ + while ( wq->fill ) { + + /* Check to see if send buffer has completed */ + send_buf = linda_wq->send_buf[linda_wq->cons]; + if ( linda_send_buf_in_use ( linda, send_buf ) ) + break; + + /* Complete this buffer */ + linda_complete_send ( ibdev, qp, linda_wq->cons ); + + /* Increment consumer counter */ + linda_wq->cons = ( ( linda_wq->cons + 1 ) & + ( wq->num_wqes - 1 ) ); + } +} + +/** + * Post receive work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int linda_post_recv ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct io_buffer *iobuf ) { + struct linda *linda = ib_get_drvdata ( ibdev ); + struct ib_work_queue *wq = &qp->recv; + struct linda_recv_work_queue *linda_wq = ib_wq_get_drvdata ( wq ); + struct QIB_7220_RcvEgr rcvegr; + struct QIB_7220_scalar rcvegrindexhead; + unsigned int ctx = linda_qpn_to_ctx ( qp->qpn ); + physaddr_t addr; + size_t len; + unsigned int wqe_idx; + unsigned int bufsize; + + /* Sanity checks */ + addr = virt_to_bus ( iobuf->data ); + len = iob_tailroom ( iobuf ); + if ( addr & ( LINDA_EAGER_BUFFER_ALIGN - 1 ) ) { + DBGC ( linda, "Linda %p QPN %ld misaligned RX buffer " + "(%08lx)\n", linda, qp->qpn, addr ); + return -EINVAL; + } + if ( len != LINDA_RECV_PAYLOAD_SIZE ) { + DBGC ( linda, "Linda %p QPN %ld wrong RX buffer size (%zd)\n", + linda, qp->qpn, len ); + return -EINVAL; + } + + /* Calculate eager producer index and WQE index */ + wqe_idx = ( linda_wq->eager_prod & ( wq->num_wqes - 1 ) ); + assert ( wq->iobufs[wqe_idx] == NULL ); + + /* Store I/O buffer */ + wq->iobufs[wqe_idx] = iobuf; + + /* Calculate buffer size */ + switch ( LINDA_RECV_PAYLOAD_SIZE ) { + case 2048: bufsize = LINDA_EAGER_BUFFER_2K; break; + case 4096: bufsize = LINDA_EAGER_BUFFER_4K; break; + case 8192: bufsize = LINDA_EAGER_BUFFER_8K; break; + case 16384: bufsize = LINDA_EAGER_BUFFER_16K; break; + case 32768: bufsize = LINDA_EAGER_BUFFER_32K; break; + case 65536: bufsize = LINDA_EAGER_BUFFER_64K; break; + default: linker_assert ( 0, invalid_rx_payload_size ); + bufsize = LINDA_EAGER_BUFFER_NONE; + } + + /* Post eager buffer */ + memset ( &rcvegr, 0, sizeof ( rcvegr ) ); + BIT_FILL_2 ( &rcvegr, + Addr, ( addr >> 11 ), + BufSize, bufsize ); + linda_writeq_array8b ( linda, &rcvegr, + linda_wq->eager_array, linda_wq->eager_prod ); + DBGC2 ( linda, "Linda %p QPN %ld RX egr %d(%d) posted [%lx,%lx)\n", + linda, qp->qpn, linda_wq->eager_prod, wqe_idx, + addr, ( addr + len ) ); + + /* Increment producer index */ + linda_wq->eager_prod = ( ( linda_wq->eager_prod + 1 ) & + ( linda_wq->eager_entries - 1 ) ); + + /* Update head index */ + memset ( &rcvegrindexhead, 0, sizeof ( rcvegrindexhead ) ); + BIT_FILL_1 ( &rcvegrindexhead, + Value, ( ( linda_wq->eager_prod + 1 ) & + ( linda_wq->eager_entries - 1 ) ) ); + linda_writeq_array64k ( linda, &rcvegrindexhead, + QIB_7220_RcvEgrIndexHead0_offset, ctx ); + + return 0; +} + +/** + * Complete receive work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v header_offs Header offset + */ +static void linda_complete_recv ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + unsigned int header_offs ) { + struct linda *linda = ib_get_drvdata ( ibdev ); + struct ib_work_queue *wq = &qp->recv; + struct linda_recv_work_queue *linda_wq = ib_wq_get_drvdata ( wq ); + struct QIB_7220_RcvHdrFlags *rcvhdrflags; + struct QIB_7220_RcvEgr rcvegr; + struct io_buffer headers; + struct io_buffer *iobuf; + struct ib_queue_pair *intended_qp; + struct ib_address_vector av; + unsigned int rcvtype; + unsigned int pktlen; + unsigned int egrindex; + unsigned int useegrbfr; + unsigned int iberr, mkerr, tiderr, khdrerr, mtuerr; + unsigned int lenerr, parityerr, vcrcerr, icrcerr; + unsigned int err; + unsigned int hdrqoffset; + unsigned int header_len; + unsigned int padded_payload_len; + unsigned int wqe_idx; + size_t payload_len; + int qp0; + int rc; + + /* RcvHdrFlags are at the end of the header entry */ + rcvhdrflags = ( linda_wq->header + header_offs + + LINDA_RECV_HEADER_SIZE - sizeof ( *rcvhdrflags ) ); + rcvtype = BIT_GET ( rcvhdrflags, RcvType ); + pktlen = ( BIT_GET ( rcvhdrflags, PktLen ) << 2 ); + egrindex = BIT_GET ( rcvhdrflags, EgrIndex ); + useegrbfr = BIT_GET ( rcvhdrflags, UseEgrBfr ); + hdrqoffset = ( BIT_GET ( rcvhdrflags, HdrqOffset ) << 2 ); + iberr = BIT_GET ( rcvhdrflags, IBErr ); + mkerr = BIT_GET ( rcvhdrflags, MKErr ); + tiderr = BIT_GET ( rcvhdrflags, TIDErr ); + khdrerr = BIT_GET ( rcvhdrflags, KHdrErr ); + mtuerr = BIT_GET ( rcvhdrflags, MTUErr ); + lenerr = BIT_GET ( rcvhdrflags, LenErr ); + parityerr = BIT_GET ( rcvhdrflags, ParityErr ); + vcrcerr = BIT_GET ( rcvhdrflags, VCRCErr ); + icrcerr = BIT_GET ( rcvhdrflags, ICRCErr ); + header_len = ( LINDA_RECV_HEADER_SIZE - hdrqoffset - + sizeof ( *rcvhdrflags ) ); + padded_payload_len = ( pktlen - header_len - 4 /* ICRC */ ); + err = ( iberr | mkerr | tiderr | khdrerr | mtuerr | + lenerr | parityerr | vcrcerr | icrcerr ); + /* IB header is placed immediately before RcvHdrFlags */ + iob_populate ( &headers, ( ( ( void * ) rcvhdrflags ) - header_len ), + header_len, header_len ); + + /* Dump diagnostic information */ + if ( err || ( ! useegrbfr ) ) { + DBGC ( linda, "Linda %p QPN %ld RX egr %d%s hdr %d type %d " + "len %d(%d+%d+4)%s%s%s%s%s%s%s%s%s%s%s\n", linda, + qp->qpn, egrindex, ( useegrbfr ? "" : "(unused)" ), + ( header_offs / LINDA_RECV_HEADER_SIZE ), rcvtype, + pktlen, header_len, padded_payload_len, + ( err ? " [Err" : "" ), ( iberr ? " IB" : "" ), + ( mkerr ? " MK" : "" ), ( tiderr ? " TID" : "" ), + ( khdrerr ? " KHdr" : "" ), ( mtuerr ? " MTU" : "" ), + ( lenerr ? " Len" : "" ), ( parityerr ? " Parity" : ""), + ( vcrcerr ? " VCRC" : "" ), ( icrcerr ? " ICRC" : "" ), + ( err ? "]" : "" ) ); + } else { + DBGC2 ( linda, "Linda %p QPN %ld RX egr %d hdr %d type %d " + "len %d(%d+%d+4)\n", linda, qp->qpn, egrindex, + ( header_offs / LINDA_RECV_HEADER_SIZE ), rcvtype, + pktlen, header_len, padded_payload_len ); + } + DBGCP_HDA ( linda, hdrqoffset, headers.data, + ( header_len + sizeof ( *rcvhdrflags ) ) ); + + /* Parse header to generate address vector */ + qp0 = ( qp->qpn == 0 ); + intended_qp = NULL; + if ( ( rc = ib_pull ( ibdev, &headers, ( qp0 ? &intended_qp : NULL ), + &payload_len, &av ) ) != 0 ) { + DBGC ( linda, "Linda %p could not parse headers: %s\n", + linda, strerror ( rc ) ); + err = 1; + } + if ( ! intended_qp ) + intended_qp = qp; + + /* Complete this buffer and any skipped buffers. Note that + * when the hardware runs out of buffers, it will repeatedly + * report the same buffer (the tail) as a TID error, and that + * it also has a habit of sometimes skipping over several + * buffers at once. + */ + while ( 1 ) { + + /* If we have caught up to the producer counter, stop. + * This will happen when the hardware first runs out + * of buffers and starts reporting TID errors against + * the eager buffer it wants to use next. + */ + if ( linda_wq->eager_cons == linda_wq->eager_prod ) + break; + + /* If we have caught up to where we should be after + * completing this egrindex, stop. We phrase the test + * this way to avoid completing the entire ring when + * we receive the same egrindex twice in a row. + */ + if ( ( linda_wq->eager_cons == + ( ( egrindex + 1 ) & ( linda_wq->eager_entries - 1 ) ))) + break; + + /* Identify work queue entry and corresponding I/O + * buffer. + */ + wqe_idx = ( linda_wq->eager_cons & ( wq->num_wqes - 1 ) ); + iobuf = wq->iobufs[wqe_idx]; + assert ( iobuf != NULL ); + wq->iobufs[wqe_idx] = NULL; + + /* Complete the eager buffer */ + if ( linda_wq->eager_cons == egrindex ) { + /* Completing the eager buffer described in + * this header entry. + */ + iob_put ( iobuf, payload_len ); + rc = ( err ? -EIO : ( useegrbfr ? 0 : -ECANCELED ) ); + /* Redirect to target QP if necessary */ + if ( qp != intended_qp ) { + DBGC ( linda, "Linda %p redirecting QPN %ld " + "=> %ld\n", + linda, qp->qpn, intended_qp->qpn ); + /* Compensate for incorrect fill levels */ + qp->recv.fill--; + intended_qp->recv.fill++; + } + ib_complete_recv ( ibdev, intended_qp, &av, iobuf, rc); + } else { + /* Completing on a skipped-over eager buffer */ + ib_complete_recv ( ibdev, qp, &av, iobuf, -ECANCELED ); + } + + /* Clear eager buffer */ + memset ( &rcvegr, 0, sizeof ( rcvegr ) ); + linda_writeq_array8b ( linda, &rcvegr, linda_wq->eager_array, + linda_wq->eager_cons ); + + /* Increment consumer index */ + linda_wq->eager_cons = ( ( linda_wq->eager_cons + 1 ) & + ( linda_wq->eager_entries - 1 ) ); + } +} + +/** + * Poll receive work queue + * + * @v ibdev Infiniband device + * @v qp Queue pair + */ +static void linda_poll_recv_wq ( struct ib_device *ibdev, + struct ib_queue_pair *qp ) { + struct linda *linda = ib_get_drvdata ( ibdev ); + struct ib_work_queue *wq = &qp->recv; + struct linda_recv_work_queue *linda_wq = ib_wq_get_drvdata ( wq ); + struct QIB_7220_RcvHdrHead0 rcvhdrhead; + unsigned int ctx = linda_qpn_to_ctx ( qp->qpn ); + unsigned int header_prod; + + /* Check for received packets */ + header_prod = ( BIT_GET ( &linda_wq->header_prod, Value ) << 2 ); + if ( header_prod == linda_wq->header_cons ) + return; + + /* Process all received packets */ + while ( linda_wq->header_cons != header_prod ) { + + /* Complete the receive */ + linda_complete_recv ( ibdev, qp, linda_wq->header_cons ); + + /* Increment the consumer offset */ + linda_wq->header_cons += LINDA_RECV_HEADER_SIZE; + linda_wq->header_cons %= LINDA_RECV_HEADERS_SIZE; + } + + /* Update consumer offset */ + memset ( &rcvhdrhead, 0, sizeof ( rcvhdrhead ) ); + BIT_FILL_2 ( &rcvhdrhead, + RcvHeadPointer, ( linda_wq->header_cons >> 2 ), + counter, 1 ); + linda_writeq_array64k ( linda, &rcvhdrhead, + QIB_7220_RcvHdrHead0_offset, ctx ); +} + +/** + * Poll completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + */ +static void linda_poll_cq ( struct ib_device *ibdev, + struct ib_completion_queue *cq ) { + struct ib_work_queue *wq; + + /* Poll associated send and receive queues */ + list_for_each_entry ( wq, &cq->work_queues, list ) { + if ( wq->is_send ) { + linda_poll_send_wq ( ibdev, wq->qp ); + } else { + linda_poll_recv_wq ( ibdev, wq->qp ); + } + } +} + +/*************************************************************************** + * + * Event queues + * + *************************************************************************** + */ + +/** + * Poll event queue + * + * @v ibdev Infiniband device + */ +static void linda_poll_eq ( struct ib_device *ibdev ) { + struct linda *linda = ib_get_drvdata ( ibdev ); + struct QIB_7220_ErrStatus errstatus; + struct QIB_7220_ErrClear errclear; + + /* Check for link status changes */ + DBG_DISABLE ( DBGLVL_IO ); + linda_readq ( linda, &errstatus, QIB_7220_ErrStatus_offset ); + DBG_ENABLE ( DBGLVL_IO ); + if ( BIT_GET ( &errstatus, IBStatusChanged ) ) { + linda_link_state_changed ( ibdev ); + memset ( &errclear, 0, sizeof ( errclear ) ); + BIT_FILL_1 ( &errclear, IBStatusChangedClear, 1 ); + linda_writeq ( linda, &errclear, QIB_7220_ErrClear_offset ); + } +} + +/*************************************************************************** + * + * Infiniband link-layer operations + * + *************************************************************************** + */ + +/** + * Initialise Infiniband link + * + * @v ibdev Infiniband device + * @ret rc Return status code + */ +static int linda_open ( struct ib_device *ibdev ) { + struct linda *linda = ib_get_drvdata ( ibdev ); + struct QIB_7220_Control control; + + /* Disable link */ + linda_readq ( linda, &control, QIB_7220_Control_offset ); + BIT_SET ( &control, LinkEn, 1 ); + linda_writeq ( linda, &control, QIB_7220_Control_offset ); + return 0; +} + +/** + * Close Infiniband link + * + * @v ibdev Infiniband device + */ +static void linda_close ( struct ib_device *ibdev ) { + struct linda *linda = ib_get_drvdata ( ibdev ); + struct QIB_7220_Control control; + + /* Disable link */ + linda_readq ( linda, &control, QIB_7220_Control_offset ); + BIT_SET ( &control, LinkEn, 0 ); + linda_writeq ( linda, &control, QIB_7220_Control_offset ); +} + +/*************************************************************************** + * + * Multicast group operations + * + *************************************************************************** + */ + +/** + * Attach to multicast group + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v gid Multicast GID + * @ret rc Return status code + */ +static int linda_mcast_attach ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct ib_gid *gid ) { + struct linda *linda = ib_get_drvdata ( ibdev ); + + ( void ) linda; + ( void ) qp; + ( void ) gid; + return 0; +} + +/** + * Detach from multicast group + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v gid Multicast GID + */ +static void linda_mcast_detach ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct ib_gid *gid ) { + struct linda *linda = ib_get_drvdata ( ibdev ); + + ( void ) linda; + ( void ) qp; + ( void ) gid; +} + +/** Linda Infiniband operations */ +static struct ib_device_operations linda_ib_operations = { + .create_cq = linda_create_cq, + .destroy_cq = linda_destroy_cq, + .create_qp = linda_create_qp, + .modify_qp = linda_modify_qp, + .destroy_qp = linda_destroy_qp, + .post_send = linda_post_send, + .post_recv = linda_post_recv, + .poll_cq = linda_poll_cq, + .poll_eq = linda_poll_eq, + .open = linda_open, + .close = linda_close, + .mcast_attach = linda_mcast_attach, + .mcast_detach = linda_mcast_detach, +}; + +/*************************************************************************** + * + * I2C bus operations + * + *************************************************************************** + */ + +/** Linda I2C bit to GPIO mappings */ +static unsigned int linda_i2c_bits[] = { + [I2C_BIT_SCL] = ( 1 << LINDA_GPIO_SCL ), + [I2C_BIT_SDA] = ( 1 << LINDA_GPIO_SDA ), +}; + +/** + * Read Linda I2C line status + * + * @v basher Bit-bashing interface + * @v bit_id Bit number + * @ret zero Input is a logic 0 + * @ret non-zero Input is a logic 1 + */ +static int linda_i2c_read_bit ( struct bit_basher *basher, + unsigned int bit_id ) { + struct linda *linda = + container_of ( basher, struct linda, i2c.basher ); + struct QIB_7220_EXTStatus extstatus; + unsigned int status; + + DBG_DISABLE ( DBGLVL_IO ); + + linda_readq ( linda, &extstatus, QIB_7220_EXTStatus_offset ); + status = ( BIT_GET ( &extstatus, GPIOIn ) & linda_i2c_bits[bit_id] ); + + DBG_ENABLE ( DBGLVL_IO ); + + return status; +} + +/** + * Write Linda I2C line status + * + * @v basher Bit-bashing interface + * @v bit_id Bit number + * @v data Value to write + */ +static void linda_i2c_write_bit ( struct bit_basher *basher, + unsigned int bit_id, unsigned long data ) { + struct linda *linda = + container_of ( basher, struct linda, i2c.basher ); + struct QIB_7220_EXTCtrl extctrl; + struct QIB_7220_GPIO gpioout; + unsigned int bit = linda_i2c_bits[bit_id]; + unsigned int outputs = 0; + unsigned int output_enables = 0; + + DBG_DISABLE ( DBGLVL_IO ); + + /* Read current GPIO mask and outputs */ + linda_readq ( linda, &extctrl, QIB_7220_EXTCtrl_offset ); + linda_readq ( linda, &gpioout, QIB_7220_GPIOOut_offset ); + + /* Update outputs and output enables. I2C lines are tied + * high, so we always set the output to 0 and use the output + * enable to control the line. + */ + output_enables = BIT_GET ( &extctrl, GPIOOe ); + output_enables = ( ( output_enables & ~bit ) | ( ~data & bit ) ); + outputs = BIT_GET ( &gpioout, GPIO ); + outputs = ( outputs & ~bit ); + BIT_SET ( &extctrl, GPIOOe, output_enables ); + BIT_SET ( &gpioout, GPIO, outputs ); + + /* Write the output enable first; that way we avoid logic + * hazards. + */ + linda_writeq ( linda, &extctrl, QIB_7220_EXTCtrl_offset ); + linda_writeq ( linda, &gpioout, QIB_7220_GPIOOut_offset ); + mb(); + + DBG_ENABLE ( DBGLVL_IO ); +} + +/** Linda I2C bit-bashing interface operations */ +static struct bit_basher_operations linda_i2c_basher_ops = { + .read = linda_i2c_read_bit, + .write = linda_i2c_write_bit, +}; + +/** + * Initialise Linda I2C subsystem + * + * @v linda Linda device + * @ret rc Return status code + */ +static int linda_init_i2c ( struct linda *linda ) { + static int try_eeprom_address[] = { 0x51, 0x50 }; + unsigned int i; + int rc; + + /* Initialise bus */ + if ( ( rc = init_i2c_bit_basher ( &linda->i2c, + &linda_i2c_basher_ops ) ) != 0 ) { + DBGC ( linda, "Linda %p could not initialise I2C bus: %s\n", + linda, strerror ( rc ) ); + return rc; + } + + /* Probe for devices */ + for ( i = 0 ; i < ( sizeof ( try_eeprom_address ) / + sizeof ( try_eeprom_address[0] ) ) ; i++ ) { + init_i2c_eeprom ( &linda->eeprom, try_eeprom_address[i] ); + if ( ( rc = i2c_check_presence ( &linda->i2c.i2c, + &linda->eeprom ) ) == 0 ) { + DBGC2 ( linda, "Linda %p found EEPROM at %02x\n", + linda, try_eeprom_address[i] ); + return 0; + } + } + + DBGC ( linda, "Linda %p could not find EEPROM\n", linda ); + return -ENODEV; +} + +/** + * Read EEPROM parameters + * + * @v linda Linda device + * @v guid GUID to fill in + * @ret rc Return status code + */ +static int linda_read_eeprom ( struct linda *linda, + struct ib_gid_half *guid ) { + struct i2c_interface *i2c = &linda->i2c.i2c; + int rc; + + /* Read GUID */ + if ( ( rc = i2c->read ( i2c, &linda->eeprom, LINDA_EEPROM_GUID_OFFSET, + guid->bytes, sizeof ( *guid ) ) ) != 0 ) { + DBGC ( linda, "Linda %p could not read GUID: %s\n", + linda, strerror ( rc ) ); + return rc; + } + DBGC2 ( linda, "Linda %p has GUID %02x:%02x:%02x:%02x:%02x:%02x:" + "%02x:%02x\n", linda, guid->bytes[0], guid->bytes[1], + guid->bytes[2], guid->bytes[3], guid->bytes[4], + guid->bytes[5], guid->bytes[6], guid->bytes[7] ); + + /* Read serial number (debug only) */ + if ( DBG_LOG ) { + uint8_t serial[LINDA_EEPROM_SERIAL_SIZE + 1]; + + serial[ sizeof ( serial ) - 1 ] = '\0'; + if ( ( rc = i2c->read ( i2c, &linda->eeprom, + LINDA_EEPROM_SERIAL_OFFSET, serial, + ( sizeof ( serial ) - 1 ) ) ) != 0 ) { + DBGC ( linda, "Linda %p could not read serial: %s\n", + linda, strerror ( rc ) ); + return rc; + } + DBGC2 ( linda, "Linda %p has serial number \"%s\"\n", + linda, serial ); + } + + return 0; +} + +/*************************************************************************** + * + * External parallel bus access + * + *************************************************************************** + */ + +/** + * Request ownership of the IB external parallel bus + * + * @v linda Linda device + * @ret rc Return status code + */ +static int linda_ib_epb_request ( struct linda *linda ) { + struct QIB_7220_ibsd_epb_access_ctrl access; + unsigned int i; + + /* Request ownership */ + memset ( &access, 0, sizeof ( access ) ); + BIT_FILL_1 ( &access, sw_ib_epb_req, 1 ); + linda_writeq ( linda, &access, QIB_7220_ibsd_epb_access_ctrl_offset ); + + /* Wait for ownership to be granted */ + for ( i = 0 ; i < LINDA_EPB_REQUEST_MAX_WAIT_US ; i++ ) { + linda_readq ( linda, &access, + QIB_7220_ibsd_epb_access_ctrl_offset ); + if ( BIT_GET ( &access, sw_ib_epb_req_granted ) ) + return 0; + udelay ( 1 ); + } + + DBGC ( linda, "Linda %p timed out waiting for IB EPB request\n", + linda ); + return -ETIMEDOUT; +} + +/** + * Wait for IB external parallel bus transaction to complete + * + * @v linda Linda device + * @v xact Buffer to hold transaction result + * @ret rc Return status code + */ +static int linda_ib_epb_wait ( struct linda *linda, + struct QIB_7220_ibsd_epb_transaction_reg *xact ) { + unsigned int i; + + /* Discard first read to allow for signals crossing clock domains */ + linda_readq ( linda, xact, QIB_7220_ibsd_epb_transaction_reg_offset ); + + for ( i = 0 ; i < LINDA_EPB_XACT_MAX_WAIT_US ; i++ ) { + linda_readq ( linda, xact, + QIB_7220_ibsd_epb_transaction_reg_offset ); + if ( BIT_GET ( xact, ib_epb_rdy ) ) { + if ( BIT_GET ( xact, ib_epb_req_error ) ) { + DBGC ( linda, "Linda %p EPB transaction " + "failed\n", linda ); + return -EIO; + } else { + return 0; + } + } + udelay ( 1 ); + } + + DBGC ( linda, "Linda %p timed out waiting for IB EPB transaction\n", + linda ); + return -ETIMEDOUT; +} + +/** + * Release ownership of the IB external parallel bus + * + * @v linda Linda device + */ +static void linda_ib_epb_release ( struct linda *linda ) { + struct QIB_7220_ibsd_epb_access_ctrl access; + + memset ( &access, 0, sizeof ( access ) ); + BIT_FILL_1 ( &access, sw_ib_epb_req, 0 ); + linda_writeq ( linda, &access, QIB_7220_ibsd_epb_access_ctrl_offset ); +} + +/** + * Read data via IB external parallel bus + * + * @v linda Linda device + * @v location EPB location + * @ret data Data read, or negative error + * + * You must have already acquired ownership of the IB external + * parallel bus. + */ +static int linda_ib_epb_read ( struct linda *linda, unsigned int location ) { + struct QIB_7220_ibsd_epb_transaction_reg xact; + unsigned int data; + int rc; + + /* Ensure no transaction is currently in progress */ + if ( ( rc = linda_ib_epb_wait ( linda, &xact ) ) != 0 ) + return rc; + + /* Process data */ + memset ( &xact, 0, sizeof ( xact ) ); + BIT_FILL_3 ( &xact, + ib_epb_address, LINDA_EPB_LOC_ADDRESS ( location ), + ib_epb_read_write, LINDA_EPB_READ, + ib_epb_cs, LINDA_EPB_LOC_CS ( location ) ); + linda_writeq ( linda, &xact, + QIB_7220_ibsd_epb_transaction_reg_offset ); + + /* Wait for transaction to complete */ + if ( ( rc = linda_ib_epb_wait ( linda, &xact ) ) != 0 ) + return rc; + + data = BIT_GET ( &xact, ib_epb_data ); + return data; +} + +/** + * Write data via IB external parallel bus + * + * @v linda Linda device + * @v location EPB location + * @v data Data to write + * @ret rc Return status code + * + * You must have already acquired ownership of the IB external + * parallel bus. + */ +static int linda_ib_epb_write ( struct linda *linda, unsigned int location, + unsigned int data ) { + struct QIB_7220_ibsd_epb_transaction_reg xact; + int rc; + + /* Ensure no transaction is currently in progress */ + if ( ( rc = linda_ib_epb_wait ( linda, &xact ) ) != 0 ) + return rc; + + /* Process data */ + memset ( &xact, 0, sizeof ( xact ) ); + BIT_FILL_4 ( &xact, + ib_epb_data, data, + ib_epb_address, LINDA_EPB_LOC_ADDRESS ( location ), + ib_epb_read_write, LINDA_EPB_WRITE, + ib_epb_cs, LINDA_EPB_LOC_CS ( location ) ); + linda_writeq ( linda, &xact, + QIB_7220_ibsd_epb_transaction_reg_offset ); + + /* Wait for transaction to complete */ + if ( ( rc = linda_ib_epb_wait ( linda, &xact ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Read/modify/write EPB register + * + * @v linda Linda device + * @v cs Chip select + * @v channel Channel + * @v element Element + * @v reg Register + * @v value Value to set + * @v mask Mask to apply to old value + * @ret rc Return status code + */ +static int linda_ib_epb_mod_reg ( struct linda *linda, unsigned int cs, + unsigned int channel, unsigned int element, + unsigned int reg, unsigned int value, + unsigned int mask ) { + unsigned int location; + int old_value; + int rc; + + DBG_DISABLE ( DBGLVL_IO ); + + /* Sanity check */ + assert ( ( value & mask ) == value ); + + /* Acquire bus ownership */ + if ( ( rc = linda_ib_epb_request ( linda ) ) != 0 ) + goto out; + + /* Read existing value, if necessary */ + location = LINDA_EPB_LOC ( cs, channel, element, reg ); + if ( (~mask) & 0xff ) { + old_value = linda_ib_epb_read ( linda, location ); + if ( old_value < 0 ) { + rc = old_value; + goto out_release; + } + } else { + old_value = 0; + } + + /* Update value */ + value = ( ( old_value & ~mask ) | value ); + DBGCP ( linda, "Linda %p CS %d EPB(%d,%d,%#02x) %#02x => %#02x\n", + linda, cs, channel, element, reg, old_value, value ); + if ( ( rc = linda_ib_epb_write ( linda, location, value ) ) != 0 ) + goto out_release; + + out_release: + /* Release bus */ + linda_ib_epb_release ( linda ); + out: + DBG_ENABLE ( DBGLVL_IO ); + return rc; +} + +/** + * Transfer data to/from microcontroller RAM + * + * @v linda Linda device + * @v address Starting address + * @v write Data to write, or NULL + * @v read Data to read, or NULL + * @v len Length of data + * @ret rc Return status code + */ +static int linda_ib_epb_ram_xfer ( struct linda *linda, unsigned int address, + const void *write, void *read, + size_t len ) { + unsigned int control; + unsigned int address_hi; + unsigned int address_lo; + int data; + int rc; + + DBG_DISABLE ( DBGLVL_IO ); + + assert ( ! ( write && read ) ); + assert ( ( address % LINDA_EPB_UC_CHUNK_SIZE ) == 0 ); + assert ( ( len % LINDA_EPB_UC_CHUNK_SIZE ) == 0 ); + + /* Acquire bus ownership */ + if ( ( rc = linda_ib_epb_request ( linda ) ) != 0 ) + goto out; + + /* Process data */ + while ( len ) { + + /* Reset the address for each new chunk */ + if ( ( address % LINDA_EPB_UC_CHUNK_SIZE ) == 0 ) { + + /* Write the control register */ + control = ( read ? LINDA_EPB_UC_CTL_READ : + LINDA_EPB_UC_CTL_WRITE ); + if ( ( rc = linda_ib_epb_write ( linda, + LINDA_EPB_UC_CTL, + control ) ) != 0 ) + break; + + /* Write the address registers */ + address_hi = ( address >> 8 ); + if ( ( rc = linda_ib_epb_write ( linda, + LINDA_EPB_UC_ADDR_HI, + address_hi ) ) != 0 ) + break; + address_lo = ( address & 0xff ); + if ( ( rc = linda_ib_epb_write ( linda, + LINDA_EPB_UC_ADDR_LO, + address_lo ) ) != 0 ) + break; + } + + /* Read or write the data */ + if ( read ) { + data = linda_ib_epb_read ( linda, LINDA_EPB_UC_DATA ); + if ( data < 0 ) { + rc = data; + break; + } + *( ( uint8_t * ) read++ ) = data; + } else { + data = *( ( uint8_t * ) write++ ); + if ( ( rc = linda_ib_epb_write ( linda, + LINDA_EPB_UC_DATA, + data ) ) != 0 ) + break; + } + address++; + len--; + + /* Reset the control byte after each chunk */ + if ( ( address % LINDA_EPB_UC_CHUNK_SIZE ) == 0 ) { + if ( ( rc = linda_ib_epb_write ( linda, + LINDA_EPB_UC_CTL, + 0 ) ) != 0 ) + break; + } + } + + /* Release bus */ + linda_ib_epb_release ( linda ); + + out: + DBG_ENABLE ( DBGLVL_IO ); + return rc; +} + +/*************************************************************************** + * + * Infiniband SerDes initialisation + * + *************************************************************************** + */ + +/** A Linda SerDes parameter */ +struct linda_serdes_param { + /** EPB address as constructed by LINDA_EPB_ADDRESS() */ + uint16_t address; + /** Value to set */ + uint8_t value; + /** Mask to apply to old value */ + uint8_t mask; +} __packed; + +/** Magic "all channels" channel number */ +#define LINDA_EPB_ALL_CHANNELS 31 + +/** End of SerDes parameter list marker */ +#define LINDA_SERDES_PARAM_END { 0, 0, 0 } + +/** + * Program IB SerDes register(s) + * + * @v linda Linda device + * @v param SerDes parameter + * @ret rc Return status code + */ +static int linda_set_serdes_param ( struct linda *linda, + struct linda_serdes_param *param ) { + unsigned int channel; + unsigned int channel_start; + unsigned int channel_end; + unsigned int element; + unsigned int reg; + int rc; + + /* Break down the EPB address and determine channels */ + channel = LINDA_EPB_ADDRESS_CHANNEL ( param->address ); + element = LINDA_EPB_ADDRESS_ELEMENT ( param->address ); + reg = LINDA_EPB_ADDRESS_REG ( param->address ); + if ( channel == LINDA_EPB_ALL_CHANNELS ) { + channel_start = 0; + channel_end = 3; + } else { + channel_start = channel_end = channel; + } + + /* Modify register for each specified channel */ + for ( channel = channel_start ; channel <= channel_end ; channel++ ) { + if ( ( rc = linda_ib_epb_mod_reg ( linda, LINDA_EPB_CS_SERDES, + channel, element, reg, + param->value, + param->mask ) ) != 0 ) + return rc; + } + + return 0; +} + +/** + * Program IB SerDes registers + * + * @v linda Linda device + * @v param SerDes parameters + * @v count Number of parameters + * @ret rc Return status code + */ +static int linda_set_serdes_params ( struct linda *linda, + struct linda_serdes_param *params ) { + int rc; + + for ( ; params->mask != 0 ; params++ ){ + if ( ( rc = linda_set_serdes_param ( linda, + params ) ) != 0 ) + return rc; + } + + return 0; +} + +#define LINDA_DDS_VAL( amp_d, main_d, ipst_d, ipre_d, \ + amp_s, main_s, ipst_s, ipre_s ) \ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 9, 0x00 ), \ + ( ( ( amp_d & 0x1f ) << 1 ) | 1 ), 0xff }, \ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 9, 0x01 ), \ + ( ( ( amp_s & 0x1f ) << 1 ) | 1 ), 0xff }, \ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 9, 0x09 ), \ + ( ( main_d << 3 ) | 4 | ( ipre_d >> 2 ) ), 0xff }, \ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 9, 0x0a ), \ + ( ( main_s << 3 ) | 4 | ( ipre_s >> 2 ) ), 0xff }, \ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 9, 0x06 ), \ + ( ( ( ipst_d & 0xf ) << 1 ) | \ + ( ( ipre_d & 3 ) << 6 ) | 0x21 ), 0xff }, \ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 9, 0x07 ), \ + ( ( ( ipst_s & 0xf ) << 1 ) | \ + ( ( ipre_s & 3 ) << 6) | 0x21 ), 0xff } + +/** + * Linda SerDes default parameters + * + * These magic start-of-day values are taken from the Linux driver. + */ +static struct linda_serdes_param linda_serdes_defaults1[] = { + /* RXHSCTRL0 */ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 6, 0x00 ), 0xd4, 0xff }, + /* VCDL_DAC2 */ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 6, 0x05 ), 0x2d, 0xff }, + /* VCDL_CTRL2 */ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 6, 0x08 ), 0x03, 0x0f }, + /* START_EQ1 */ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x27 ), 0x10, 0xff }, + /* START_EQ2 */ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x28 ), 0x30, 0xff }, + /* BACTRL */ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 6, 0x0e ), 0x40, 0xff }, + /* LDOUTCTRL1 */ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x06 ), 0x04, 0xff }, + /* RXHSSTATUS */ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 6, 0x0f ), 0x04, 0xff }, + /* End of this block */ + LINDA_SERDES_PARAM_END +}; +static struct linda_serdes_param linda_serdes_defaults2[] = { + /* LDOUTCTRL1 */ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x06 ), 0x00, 0xff }, + /* DDS values */ + LINDA_DDS_VAL ( 31, 19, 12, 0, 29, 22, 9, 0 ), + /* Set Rcv Eq. to Preset node */ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x27 ), 0x10, 0xff }, + /* DFELTHFDR */ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x08 ), 0x00, 0xff }, + /* DFELTHHDR */ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x21 ), 0x00, 0xff }, + /* TLTHFDR */ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x09 ), 0x02, 0xff }, + /* TLTHHDR */ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x23 ), 0x02, 0xff }, + /* ZFR */ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x1b ), 0x0c, 0xff }, + /* ZCNT) */ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x1c ), 0x0c, 0xff }, + /* GFR */ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x1e ), 0x10, 0xff }, + /* GHR */ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x1f ), 0x10, 0xff }, + /* VCDL_CTRL0 toggle */ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 6, 0x06 ), 0x20, 0xff }, + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 6, 0x06 ), 0x00, 0xff }, + /* CMUCTRL5 */ + { LINDA_EPB_ADDRESS ( 7, 0, 0x15 ), 0x80, 0xff }, + /* End of this block */ + LINDA_SERDES_PARAM_END +}; +static struct linda_serdes_param linda_serdes_defaults3[] = { + /* START_EQ1 */ + { LINDA_EPB_ADDRESS ( LINDA_EPB_ALL_CHANNELS, 7, 0x27 ), 0x00, 0x38 }, + /* End of this block */ + LINDA_SERDES_PARAM_END +}; + +/** + * Program the microcontroller RAM + * + * @v linda Linda device + * @ret rc Return status code + */ +static int linda_program_uc_ram ( struct linda *linda ) { + int rc; + + if ( ( rc = linda_ib_epb_ram_xfer ( linda, 0, linda_ib_fw, NULL, + sizeof ( linda_ib_fw ) ) ) != 0 ){ + DBGC ( linda, "Linda %p could not load IB firmware: %s\n", + linda, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Verify the microcontroller RAM + * + * @v linda Linda device + * @ret rc Return status code + */ +static int linda_verify_uc_ram ( struct linda *linda ) { + uint8_t verify[LINDA_EPB_UC_CHUNK_SIZE]; + unsigned int offset; + int rc; + + for ( offset = 0 ; offset < sizeof ( linda_ib_fw ); + offset += sizeof ( verify ) ) { + if ( ( rc = linda_ib_epb_ram_xfer ( linda, offset, + NULL, verify, + sizeof (verify) )) != 0 ){ + DBGC ( linda, "Linda %p could not read back IB " + "firmware: %s\n", linda, strerror ( rc ) ); + return rc; + } + if ( memcmp ( ( linda_ib_fw + offset ), verify, + sizeof ( verify ) ) != 0 ) { + DBGC ( linda, "Linda %p firmware verification failed " + "at offset %#x\n", linda, offset ); + DBGC_HDA ( linda, offset, ( linda_ib_fw + offset ), + sizeof ( verify ) ); + DBGC_HDA ( linda, offset, verify, sizeof ( verify ) ); + return -EIO; + } + } + + DBGC2 ( linda, "Linda %p firmware verified ok\n", linda ); + return 0; +} + +/** + * Use the microcontroller to trim the IB link + * + * @v linda Linda device + * @ret rc Return status code + */ +static int linda_trim_ib ( struct linda *linda ) { + struct QIB_7220_IBSerDesCtrl ctrl; + struct QIB_7220_IntStatus intstatus; + unsigned int i; + int rc; + + /* Bring the microcontroller out of reset */ + linda_readq ( linda, &ctrl, QIB_7220_IBSerDesCtrl_offset ); + BIT_SET ( &ctrl, ResetIB_uC_Core, 0 ); + linda_writeq ( linda, &ctrl, QIB_7220_IBSerDesCtrl_offset ); + + /* Wait for the "trim done" signal */ + for ( i = 0 ; i < LINDA_TRIM_DONE_MAX_WAIT_MS ; i++ ) { + linda_readq ( linda, &intstatus, QIB_7220_IntStatus_offset ); + if ( BIT_GET ( &intstatus, IBSerdesTrimDone ) ) { + rc = 0; + goto out_reset; + } + mdelay ( 1 ); + } + + DBGC ( linda, "Linda %p timed out waiting for trim done\n", linda ); + rc = -ETIMEDOUT; + out_reset: + /* Put the microcontroller back into reset */ + BIT_SET ( &ctrl, ResetIB_uC_Core, 1 ); + linda_writeq ( linda, &ctrl, QIB_7220_IBSerDesCtrl_offset ); + + return rc; +} + +/** + * Initialise the IB SerDes + * + * @v linda Linda device + * @ret rc Return status code + */ +static int linda_init_ib_serdes ( struct linda *linda ) { + struct QIB_7220_Control control; + struct QIB_7220_IBCCtrl ibcctrl; + struct QIB_7220_IBCDDRCtrl ibcddrctrl; + struct QIB_7220_XGXSCfg xgxscfg; + int rc; + + /* Disable link */ + linda_readq ( linda, &control, QIB_7220_Control_offset ); + BIT_SET ( &control, LinkEn, 0 ); + linda_writeq ( linda, &control, QIB_7220_Control_offset ); + + /* Configure sensible defaults for IBC */ + memset ( &ibcctrl, 0, sizeof ( ibcctrl ) ); + BIT_FILL_6 ( &ibcctrl, /* Tuning values taken from Linux driver */ + FlowCtrlPeriod, 0x03, + FlowCtrlWaterMark, 0x05, + MaxPktLen, ( ( LINDA_RECV_HEADER_SIZE + + LINDA_RECV_PAYLOAD_SIZE + + 4 /* ICRC */ ) >> 2 ), + PhyerrThreshold, 0xf, + OverrunThreshold, 0xf, + CreditScale, 0x4 ); + linda_writeq ( linda, &ibcctrl, QIB_7220_IBCCtrl_offset ); + + /* Force SDR only to avoid needing all the DDR tuning, + * Mellanox compatibiltiy hacks etc. SDR is plenty for + * boot-time operation. + */ + linda_readq ( linda, &ibcddrctrl, QIB_7220_IBCDDRCtrl_offset ); + BIT_SET ( &ibcddrctrl, IB_ENHANCED_MODE, 0 ); + BIT_SET ( &ibcddrctrl, SD_SPEED_SDR, 1 ); + BIT_SET ( &ibcddrctrl, SD_SPEED_DDR, 0 ); + BIT_SET ( &ibcddrctrl, SD_SPEED_QDR, 0 ); + BIT_SET ( &ibcddrctrl, HRTBT_ENB, 0 ); + BIT_SET ( &ibcddrctrl, HRTBT_AUTO, 0 ); + linda_writeq ( linda, &ibcddrctrl, QIB_7220_IBCDDRCtrl_offset ); + + /* Set default SerDes parameters */ + if ( ( rc = linda_set_serdes_params ( linda, + linda_serdes_defaults1 ) ) != 0 ) + return rc; + udelay ( 415 ); /* Magic delay while SerDes sorts itself out */ + if ( ( rc = linda_set_serdes_params ( linda, + linda_serdes_defaults2 ) ) != 0 ) + return rc; + + /* Program the microcontroller RAM */ + if ( ( rc = linda_program_uc_ram ( linda ) ) != 0 ) + return rc; + + /* Verify the microcontroller RAM contents */ + if ( DBGLVL_LOG ) { + if ( ( rc = linda_verify_uc_ram ( linda ) ) != 0 ) + return rc; + } + + /* More SerDes tuning */ + if ( ( rc = linda_set_serdes_params ( linda, + linda_serdes_defaults3 ) ) != 0 ) + return rc; + + /* Use the microcontroller to trim the IB link */ + if ( ( rc = linda_trim_ib ( linda ) ) != 0 ) + return rc; + + /* Bring XGXS out of reset */ + linda_readq ( linda, &xgxscfg, QIB_7220_XGXSCfg_offset ); + BIT_SET ( &xgxscfg, tx_rx_reset, 0 ); + BIT_SET ( &xgxscfg, xcv_reset, 0 ); + linda_writeq ( linda, &xgxscfg, QIB_7220_XGXSCfg_offset ); + + return rc; +} + +/*************************************************************************** + * + * PCI layer interface + * + *************************************************************************** + */ + +/** + * Probe PCI device + * + * @v pci PCI device + * @v id PCI ID + * @ret rc Return status code + */ +static int linda_probe ( struct pci_device *pci, + const struct pci_device_id *id __unused ) { + struct ib_device *ibdev; + struct linda *linda; + struct QIB_7220_Revision revision; + int rc; + + /* Allocate Infiniband device */ + ibdev = alloc_ibdev ( sizeof ( *linda ) ); + if ( ! ibdev ) { + rc = -ENOMEM; + goto err_alloc_ibdev; + } + pci_set_drvdata ( pci, ibdev ); + linda = ib_get_drvdata ( ibdev ); + ibdev->op = &linda_ib_operations; + ibdev->dev = &pci->dev; + ibdev->port = 1; + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Get PCI BARs */ + linda->regs = ioremap ( pci->membase, LINDA_BAR0_SIZE ); + DBGC2 ( linda, "Linda %p has BAR at %08lx\n", linda, pci->membase ); + + /* Print some general data */ + linda_readq ( linda, &revision, QIB_7220_Revision_offset ); + DBGC2 ( linda, "Linda %p board %02lx v%ld.%ld.%ld.%ld\n", linda, + BIT_GET ( &revision, BoardID ), + BIT_GET ( &revision, R_SW ), + BIT_GET ( &revision, R_Arch ), + BIT_GET ( &revision, R_ChipRevMajor ), + BIT_GET ( &revision, R_ChipRevMinor ) ); + + /* Initialise I2C subsystem */ + if ( ( rc = linda_init_i2c ( linda ) ) != 0 ) + goto err_init_i2c; + + /* Read EEPROM parameters */ + if ( ( rc = linda_read_eeprom ( linda, &ibdev->gid.u.half[1] ) ) != 0 ) + goto err_read_eeprom; + + /* Initialise send datapath */ + if ( ( rc = linda_init_send ( linda ) ) != 0 ) + goto err_init_send; + + /* Initialise receive datapath */ + if ( ( rc = linda_init_recv ( linda ) ) != 0 ) + goto err_init_recv; + + /* Initialise the IB SerDes */ + if ( ( rc = linda_init_ib_serdes ( linda ) ) != 0 ) + goto err_init_ib_serdes; + + /* Create the SMA */ + if ( ( rc = ib_create_sma ( &linda->sma, ibdev, + &linda_sma_operations ) ) != 0 ) + goto err_create_sma; + /* If the SMA doesn't get context 0, we're screwed */ + assert ( linda_qpn_to_ctx ( linda->sma.qp->qpn ) == 0 ); + + /* Register Infiniband device */ + if ( ( rc = register_ibdev ( ibdev ) ) != 0 ) { + DBGC ( linda, "Linda %p could not register IB " + "device: %s\n", linda, strerror ( rc ) ); + goto err_register_ibdev; + } + + return 0; + + unregister_ibdev ( ibdev ); + err_register_ibdev: + ib_destroy_sma ( &linda->sma ); + err_create_sma: + linda_fini_recv ( linda ); + err_init_recv: + linda_fini_send ( linda ); + err_init_send: + err_init_ib_serdes: + err_read_eeprom: + err_init_i2c: + ibdev_put ( ibdev ); + err_alloc_ibdev: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void linda_remove ( struct pci_device *pci ) { + struct ib_device *ibdev = pci_get_drvdata ( pci ); + struct linda *linda = ib_get_drvdata ( ibdev ); + + unregister_ibdev ( ibdev ); + ib_destroy_sma ( &linda->sma ); + linda_fini_recv ( linda ); + linda_fini_send ( linda ); + ibdev_put ( ibdev ); +} + +static struct pci_device_id linda_nics[] = { + PCI_ROM ( 0x1077, 0x7220, "iba7220", "QLE7240/7280 HCA driver" ), +}; + +struct pci_driver linda_driver __pci_driver = { + .ids = linda_nics, + .id_count = ( sizeof ( linda_nics ) / sizeof ( linda_nics[0] ) ), + .probe = linda_probe, + .remove = linda_remove, +}; diff --git a/gpxe/src/drivers/infiniband/linda.h b/gpxe/src/drivers/infiniband/linda.h new file mode 100644 index 0000000..dd1737a --- /dev/null +++ b/gpxe/src/drivers/infiniband/linda.h @@ -0,0 +1,271 @@ +#ifndef _LINDA_H +#define _LINDA_H + +/* + * Copyright (C) 2008 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. + */ + +/** + * @file + * + * QLogic Linda Infiniband HCA + * + */ + +#define BITOPS_LITTLE_ENDIAN +#include +#include "qib_7220_regs.h" + +struct ib_device; + +/** A Linda GPIO register */ +struct QIB_7220_GPIO_pb { + pseudo_bit_t GPIO[16]; + pseudo_bit_t Reserved[48]; +}; +struct QIB_7220_GPIO { + PSEUDO_BIT_STRUCT ( struct QIB_7220_GPIO_pb ); +}; + +/** A Linda general scalar register */ +struct QIB_7220_scalar_pb { + pseudo_bit_t Value[64]; +}; +struct QIB_7220_scalar { + PSEUDO_BIT_STRUCT ( struct QIB_7220_scalar_pb ); +}; + +/** Linda send per-buffer control word */ +struct QIB_7220_SendPbc_pb { + pseudo_bit_t LengthP1_toibc[11]; + pseudo_bit_t Reserved1[4]; + pseudo_bit_t LengthP1_trigger[11]; + pseudo_bit_t Reserved2[3]; + pseudo_bit_t TestEbp[1]; + pseudo_bit_t Test[1]; + pseudo_bit_t Intr[1]; + pseudo_bit_t Reserved3[31]; + pseudo_bit_t VL15[1]; +}; +struct QIB_7220_SendPbc { + PSEUDO_BIT_STRUCT ( struct QIB_7220_SendPbc_pb ); +}; + +/** Linda send buffer availability */ +struct QIB_7220_SendBufAvail_pb { + pseudo_bit_t InUseCheck[144][2]; + pseudo_bit_t Reserved[32]; +}; +struct QIB_7220_SendBufAvail { + PSEUDO_BIT_STRUCT ( struct QIB_7220_SendBufAvail_pb ); +}; + +/** DMA alignment for send buffer availability */ +#define LINDA_SENDBUFAVAIL_ALIGN 64 + +/** A Linda eager receive descriptor */ +struct QIB_7220_RcvEgr_pb { + pseudo_bit_t Addr[37]; + pseudo_bit_t BufSize[3]; + pseudo_bit_t Reserved[24]; +}; +struct QIB_7220_RcvEgr { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvEgr_pb ); +}; + +/** Linda receive header flags */ +struct QIB_7220_RcvHdrFlags_pb { + pseudo_bit_t PktLen[11]; + pseudo_bit_t RcvType[3]; + pseudo_bit_t SoftB[1]; + pseudo_bit_t SoftA[1]; + pseudo_bit_t EgrIndex[12]; + pseudo_bit_t Reserved1[3]; + pseudo_bit_t UseEgrBfr[1]; + pseudo_bit_t RcvSeq[4]; + pseudo_bit_t HdrqOffset[11]; + pseudo_bit_t Reserved2[8]; + pseudo_bit_t IBErr[1]; + pseudo_bit_t MKErr[1]; + pseudo_bit_t TIDErr[1]; + pseudo_bit_t KHdrErr[1]; + pseudo_bit_t MTUErr[1]; + pseudo_bit_t LenErr[1]; + pseudo_bit_t ParityErr[1]; + pseudo_bit_t VCRCErr[1]; + pseudo_bit_t ICRCErr[1]; +}; +struct QIB_7220_RcvHdrFlags { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvHdrFlags_pb ); +}; + +/** Linda memory BAR size */ +#define LINDA_BAR0_SIZE 0x400000 + +/** Linda I2C SCL line GPIO number */ +#define LINDA_GPIO_SCL 0 + +/** Linda I2C SDA line GPIO number */ +#define LINDA_GPIO_SDA 1 + +/** GUID offset within EEPROM */ +#define LINDA_EEPROM_GUID_OFFSET 3 + +/** GUID size within EEPROM */ +#define LINDA_EEPROM_GUID_SIZE 8 + +/** Board serial number offset within EEPROM */ +#define LINDA_EEPROM_SERIAL_OFFSET 12 + +/** Board serial number size within EEPROM */ +#define LINDA_EEPROM_SERIAL_SIZE 12 + +/** Maximum number of send buffers used + * + * This is a policy decision. Must be less than or equal to the total + * number of send buffers supported by the hardware (128). + */ +#define LINDA_MAX_SEND_BUFS 32 + +/** Linda send buffer size */ +#define LINDA_SEND_BUF_SIZE 4096 + +/** Number of contexts (including kernel context) + * + * This is a policy decision. Must be 5, 9 or 17. + */ +#define LINDA_NUM_CONTEXTS 5 + +/** PortCfg values for different numbers of contexts */ +enum linda_portcfg { + LINDA_PORTCFG_5CTX = 0, + LINDA_PORTCFG_9CTX = 1, + LINDA_PORTCFG_17CTX = 2, +}; + +/** PortCfg values for different numbers of contexts */ +#define LINDA_EAGER_ARRAY_SIZE_5CTX_0 2048 +#define LINDA_EAGER_ARRAY_SIZE_5CTX_OTHER 4096 +#define LINDA_EAGER_ARRAY_SIZE_9CTX_0 2048 +#define LINDA_EAGER_ARRAY_SIZE_9CTX_OTHER 2048 +#define LINDA_EAGER_ARRAY_SIZE_17CTX_0 2048 +#define LINDA_EAGER_ARRAY_SIZE_17CTX_OTHER 1024 + +/** Eager buffer required alignment */ +#define LINDA_EAGER_BUFFER_ALIGN 2048 + +/** Eager buffer size encodings */ +enum linda_eager_buffer_size { + LINDA_EAGER_BUFFER_NONE = 0, + LINDA_EAGER_BUFFER_2K = 1, + LINDA_EAGER_BUFFER_4K = 2, + LINDA_EAGER_BUFFER_8K = 3, + LINDA_EAGER_BUFFER_16K = 4, + LINDA_EAGER_BUFFER_32K = 5, + LINDA_EAGER_BUFFER_64K = 6, +}; + +/** Number of RX headers per context + * + * This is a policy decision. + */ +#define LINDA_RECV_HEADER_COUNT 8 + +/** Maximum size of each RX header + * + * This is a policy decision. Must be divisible by 4. + */ +#define LINDA_RECV_HEADER_SIZE 96 + +/** Total size of an RX header ring */ +#define LINDA_RECV_HEADERS_SIZE \ + ( LINDA_RECV_HEADER_SIZE * LINDA_RECV_HEADER_COUNT ) + +/** RX header alignment */ +#define LINDA_RECV_HEADERS_ALIGN 64 + +/** RX payload size + * + * This is a policy decision. Must be a valid eager buffer size. + */ +#define LINDA_RECV_PAYLOAD_SIZE 2048 + +/** QPN used for Infinipath Packets + * + * This is a policy decision. Must have bit 0 clear. Must not be a + * QPN that we will use. + */ +#define LINDA_QP_IDETH 0xdead0 + +/** Maximum time for wait for external parallel bus request, in us */ +#define LINDA_EPB_REQUEST_MAX_WAIT_US 500 + +/** Maximum time for wait for external parallel bus transaction, in us */ +#define LINDA_EPB_XACT_MAX_WAIT_US 500 + +/** Linda external parallel bus chip selects */ +#define LINDA_EPB_CS_SERDES 1 +#define LINDA_EPB_CS_UC 2 + +/** Linda external parallel bus read/write operations */ +#define LINDA_EPB_WRITE 0 +#define LINDA_EPB_READ 1 + +/** Linda external parallel bus register addresses */ +#define LINDA_EPB_ADDRESS( _channel, _element, _reg ) \ + ( (_element) | ( (_channel) << 4 ) | ( (_reg) << 9 ) ) +#define LINDA_EPB_ADDRESS_CHANNEL( _address ) ( ( (_address) >> 4 ) & 0x1f ) +#define LINDA_EPB_ADDRESS_ELEMENT( _address ) ( ( (_address) >> 0 ) & 0x0f ) +#define LINDA_EPB_ADDRESS_REG( _address ) ( ( (_address) >> 9 ) & 0x3f ) + +/** Linda external parallel bus locations + * + * The location is used by the driver to encode both the chip select + * and the EPB address. + */ +#define LINDA_EPB_LOC( _cs, _channel, _element, _reg) \ + ( ( (_cs) << 16 ) | LINDA_EPB_ADDRESS ( _channel, _element, _reg ) ) +#define LINDA_EPB_LOC_ADDRESS( _loc ) ( (_loc) & 0xffff ) +#define LINDA_EPB_LOC_CS( _loc ) ( (_loc) >> 16 ) + +/** Linda external parallel bus microcontroller register addresses */ +#define LINDA_EPB_UC_CHANNEL 6 +#define LINDA_EPB_UC_LOC( _reg ) \ + LINDA_EPB_LOC ( LINDA_EPB_CS_UC, LINDA_EPB_UC_CHANNEL, 0, (_reg) ) +#define LINDA_EPB_UC_CTL LINDA_EPB_UC_LOC ( 0 ) +#define LINDA_EPB_UC_CTL_WRITE 1 +#define LINDA_EPB_UC_CTL_READ 2 +#define LINDA_EPB_UC_ADDR_LO LINDA_EPB_UC_LOC ( 2 ) +#define LINDA_EPB_UC_ADDR_HI LINDA_EPB_UC_LOC ( 3 ) +#define LINDA_EPB_UC_DATA LINDA_EPB_UC_LOC ( 4 ) +#define LINDA_EPB_UC_CHUNK_SIZE 64 + +extern uint8_t linda_ib_fw[8192]; + +/** Maximum time to wait for "trim done" signal, in ms */ +#define LINDA_TRIM_DONE_MAX_WAIT_MS 1000 + +/** Linda link states */ +enum linda_link_state { + LINDA_LINK_STATE_DOWN = 0, + LINDA_LINK_STATE_INIT = 1, + LINDA_LINK_STATE_ARM = 2, + LINDA_LINK_STATE_ACTIVE = 3, + LINDA_LINK_STATE_ACT_DEFER = 4, +}; + +#endif /* _LINDA_H */ diff --git a/gpxe/src/drivers/infiniband/linda_fw.c b/gpxe/src/drivers/infiniband/linda_fw.c new file mode 100644 index 0000000..fc5ea07 --- /dev/null +++ b/gpxe/src/drivers/infiniband/linda_fw.c @@ -0,0 +1,1067 @@ +/* + * Copyright (c) 2007, 2008 QLogic Corporation. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * This file contains the memory image from the vendor, to be copied into + * the IB SERDES of the IBA7220 during initialization. + * The file also includes the two functions which use this image. + */ + +#include +#include "linda.h" + +uint8_t linda_ib_fw[8192] = { +/*0000*/0x02, 0x0A, 0x29, 0x02, 0x0A, 0x87, 0xE5, 0xE6, + 0x30, 0xE6, 0x04, 0x7F, 0x01, 0x80, 0x02, 0x7F, +/*0010*/0x00, 0xE5, 0xE2, 0x30, 0xE4, 0x04, 0x7E, 0x01, + 0x80, 0x02, 0x7E, 0x00, 0xEE, 0x5F, 0x60, 0x08, +/*0020*/0x53, 0xF9, 0xF7, 0xE4, 0xF5, 0xFE, 0x80, 0x08, + 0x7F, 0x0A, 0x12, 0x17, 0x31, 0x12, 0x0E, 0xA2, +/*0030*/0x75, 0xFC, 0x08, 0xE4, 0xF5, 0xFD, 0xE5, 0xE7, + 0x20, 0xE7, 0x03, 0x43, 0xF9, 0x08, 0x22, 0x00, +/*0040*/0x01, 0x20, 0x11, 0x00, 0x04, 0x20, 0x00, 0x75, + 0x51, 0x01, 0xE4, 0xF5, 0x52, 0xF5, 0x53, 0xF5, +/*0050*/0x52, 0xF5, 0x7E, 0x7F, 0x04, 0x02, 0x04, 0x38, + 0xC2, 0x36, 0x05, 0x52, 0xE5, 0x52, 0xD3, 0x94, +/*0060*/0x0C, 0x40, 0x05, 0x75, 0x52, 0x01, 0xD2, 0x36, + 0x90, 0x07, 0x0C, 0x74, 0x07, 0xF0, 0xA3, 0x74, +/*0070*/0xFF, 0xF0, 0xE4, 0xF5, 0x0C, 0xA3, 0xF0, 0x90, + 0x07, 0x14, 0xF0, 0xA3, 0xF0, 0x75, 0x0B, 0x20, +/*0080*/0xF5, 0x09, 0xE4, 0xF5, 0x08, 0xE5, 0x08, 0xD3, + 0x94, 0x30, 0x40, 0x03, 0x02, 0x04, 0x04, 0x12, +/*0090*/0x00, 0x06, 0x15, 0x0B, 0xE5, 0x08, 0x70, 0x04, + 0x7F, 0x01, 0x80, 0x02, 0x7F, 0x00, 0xE5, 0x09, +/*00A0*/0x70, 0x04, 0x7E, 0x01, 0x80, 0x02, 0x7E, 0x00, + 0xEE, 0x5F, 0x60, 0x05, 0x12, 0x18, 0x71, 0xD2, +/*00B0*/0x35, 0x53, 0xE1, 0xF7, 0xE5, 0x08, 0x45, 0x09, + 0xFF, 0xE5, 0x0B, 0x25, 0xE0, 0x25, 0xE0, 0x24, +/*00C0*/0x83, 0xF5, 0x82, 0xE4, 0x34, 0x07, 0xF5, 0x83, + 0xEF, 0xF0, 0x85, 0xE2, 0x20, 0xE5, 0x52, 0xD3, +/*00D0*/0x94, 0x01, 0x40, 0x0D, 0x12, 0x19, 0xF3, 0xE0, + 0x54, 0xA0, 0x64, 0x40, 0x70, 0x03, 0x02, 0x03, +/*00E0*/0xFB, 0x53, 0xF9, 0xF8, 0x90, 0x94, 0x70, 0xE4, + 0xF0, 0xE0, 0xF5, 0x10, 0xAF, 0x09, 0x12, 0x1E, +/*00F0*/0xB3, 0xAF, 0x08, 0xEF, 0x44, 0x08, 0xF5, 0x82, + 0x75, 0x83, 0x80, 0xE0, 0xF5, 0x29, 0xEF, 0x44, +/*0100*/0x07, 0x12, 0x1A, 0x3C, 0xF5, 0x22, 0x54, 0x40, + 0xD3, 0x94, 0x00, 0x40, 0x1E, 0xE5, 0x29, 0x54, +/*0110*/0xF0, 0x70, 0x21, 0x12, 0x19, 0xF3, 0xE0, 0x44, + 0x80, 0xF0, 0xE5, 0x22, 0x54, 0x30, 0x65, 0x08, +/*0120*/0x70, 0x09, 0x12, 0x19, 0xF3, 0xE0, 0x54, 0xBF, + 0xF0, 0x80, 0x09, 0x12, 0x19, 0xF3, 0x74, 0x40, +/*0130*/0xF0, 0x02, 0x03, 0xFB, 0x12, 0x1A, 0x12, 0x75, + 0x83, 0xAE, 0x74, 0xFF, 0xF0, 0xAF, 0x08, 0x7E, +/*0140*/0x00, 0xEF, 0x44, 0x07, 0xF5, 0x82, 0xE0, 0xFD, + 0xE5, 0x0B, 0x25, 0xE0, 0x25, 0xE0, 0x24, 0x81, +/*0150*/0xF5, 0x82, 0xE4, 0x34, 0x07, 0xF5, 0x83, 0xED, + 0xF0, 0x90, 0x07, 0x0E, 0xE0, 0x04, 0xF0, 0xEF, +/*0160*/0x44, 0x07, 0xF5, 0x82, 0x75, 0x83, 0x98, 0xE0, + 0xF5, 0x28, 0x12, 0x1A, 0x23, 0x40, 0x0C, 0x12, +/*0170*/0x19, 0xF3, 0xE0, 0x44, 0x01, 0x12, 0x1A, 0x32, + 0x02, 0x03, 0xF6, 0xAF, 0x08, 0x7E, 0x00, 0x74, +/*0180*/0x80, 0xCD, 0xEF, 0xCD, 0x8D, 0x82, 0xF5, 0x83, + 0xE0, 0x30, 0xE0, 0x0A, 0x12, 0x19, 0xF3, 0xE0, +/*0190*/0x44, 0x20, 0xF0, 0x02, 0x03, 0xFB, 0x12, 0x19, + 0xF3, 0xE0, 0x54, 0xDF, 0xF0, 0xEE, 0x44, 0xAE, +/*01A0*/0x12, 0x1A, 0x43, 0x30, 0xE4, 0x03, 0x02, 0x03, + 0xFB, 0x74, 0x9E, 0x12, 0x1A, 0x05, 0x20, 0xE0, +/*01B0*/0x03, 0x02, 0x03, 0xFB, 0x8F, 0x82, 0x8E, 0x83, + 0xE0, 0x20, 0xE0, 0x03, 0x02, 0x03, 0xFB, 0x12, +/*01C0*/0x19, 0xF3, 0xE0, 0x44, 0x10, 0xF0, 0xE5, 0xE3, + 0x20, 0xE7, 0x08, 0xE5, 0x08, 0x12, 0x1A, 0x3A, +/*01D0*/0x44, 0x04, 0xF0, 0xAF, 0x08, 0x7E, 0x00, 0xEF, + 0x12, 0x1A, 0x3A, 0x20, 0xE2, 0x34, 0x12, 0x19, +/*01E0*/0xF3, 0xE0, 0x44, 0x08, 0xF0, 0xE5, 0xE4, 0x30, + 0xE6, 0x04, 0x7D, 0x01, 0x80, 0x02, 0x7D, 0x00, +/*01F0*/0xE5, 0x7E, 0xC3, 0x94, 0x04, 0x50, 0x04, 0x7C, + 0x01, 0x80, 0x02, 0x7C, 0x00, 0xEC, 0x4D, 0x60, +/*0200*/0x05, 0xC2, 0x35, 0x02, 0x03, 0xFB, 0xEE, 0x44, + 0xD2, 0x12, 0x1A, 0x43, 0x44, 0x40, 0xF0, 0x02, +/*0210*/0x03, 0xFB, 0x12, 0x19, 0xF3, 0xE0, 0x54, 0xF7, + 0xF0, 0x12, 0x1A, 0x12, 0x75, 0x83, 0xD2, 0xE0, +/*0220*/0x54, 0xBF, 0xF0, 0x90, 0x07, 0x14, 0xE0, 0x04, + 0xF0, 0xE5, 0x7E, 0x70, 0x03, 0x75, 0x7E, 0x01, +/*0230*/0xAF, 0x08, 0x7E, 0x00, 0x12, 0x1A, 0x23, 0x40, + 0x12, 0x12, 0x19, 0xF3, 0xE0, 0x44, 0x01, 0x12, +/*0240*/0x19, 0xF2, 0xE0, 0x54, 0x02, 0x12, 0x1A, 0x32, + 0x02, 0x03, 0xFB, 0x12, 0x19, 0xF3, 0xE0, 0x44, +/*0250*/0x02, 0x12, 0x19, 0xF2, 0xE0, 0x54, 0xFE, 0xF0, + 0xC2, 0x35, 0xEE, 0x44, 0x8A, 0x8F, 0x82, 0xF5, +/*0260*/0x83, 0xE0, 0xF5, 0x17, 0x54, 0x8F, 0x44, 0x40, + 0xF0, 0x74, 0x90, 0xFC, 0xE5, 0x08, 0x44, 0x07, +/*0270*/0xFD, 0xF5, 0x82, 0x8C, 0x83, 0xE0, 0x54, 0x3F, + 0x90, 0x07, 0x02, 0xF0, 0xE0, 0x54, 0xC0, 0x8D, +/*0280*/0x82, 0x8C, 0x83, 0xF0, 0x74, 0x92, 0x12, 0x1A, + 0x05, 0x90, 0x07, 0x03, 0x12, 0x1A, 0x19, 0x74, +/*0290*/0x82, 0x12, 0x1A, 0x05, 0x90, 0x07, 0x04, 0x12, + 0x1A, 0x19, 0x74, 0xB4, 0x12, 0x1A, 0x05, 0x90, +/*02A0*/0x07, 0x05, 0x12, 0x1A, 0x19, 0x74, 0x94, 0xFE, + 0xE5, 0x08, 0x44, 0x06, 0x12, 0x1A, 0x0A, 0xF5, +/*02B0*/0x10, 0x30, 0xE0, 0x04, 0xD2, 0x37, 0x80, 0x02, + 0xC2, 0x37, 0xE5, 0x10, 0x54, 0x7F, 0x8F, 0x82, +/*02C0*/0x8E, 0x83, 0xF0, 0x30, 0x44, 0x30, 0x12, 0x1A, + 0x03, 0x54, 0x80, 0xD3, 0x94, 0x00, 0x40, 0x04, +/*02D0*/0xD2, 0x39, 0x80, 0x02, 0xC2, 0x39, 0x8F, 0x82, + 0x8E, 0x83, 0xE0, 0x44, 0x80, 0xF0, 0x12, 0x1A, +/*02E0*/0x03, 0x54, 0x40, 0xD3, 0x94, 0x00, 0x40, 0x04, + 0xD2, 0x3A, 0x80, 0x02, 0xC2, 0x3A, 0x8F, 0x82, +/*02F0*/0x8E, 0x83, 0xE0, 0x44, 0x40, 0xF0, 0x74, 0x92, + 0xFE, 0xE5, 0x08, 0x44, 0x06, 0x12, 0x1A, 0x0A, +/*0300*/0x30, 0xE7, 0x04, 0xD2, 0x38, 0x80, 0x02, 0xC2, + 0x38, 0x8F, 0x82, 0x8E, 0x83, 0xE0, 0x54, 0x7F, +/*0310*/0xF0, 0x12, 0x1E, 0x46, 0xE4, 0xF5, 0x0A, 0x20, + 0x03, 0x02, 0x80, 0x03, 0x30, 0x43, 0x03, 0x12, +/*0320*/0x19, 0x95, 0x20, 0x02, 0x02, 0x80, 0x03, 0x30, + 0x42, 0x03, 0x12, 0x0C, 0x8F, 0x30, 0x30, 0x06, +/*0330*/0x12, 0x19, 0x95, 0x12, 0x0C, 0x8F, 0x12, 0x0D, + 0x47, 0x12, 0x19, 0xF3, 0xE0, 0x54, 0xFB, 0xF0, +/*0340*/0xE5, 0x0A, 0xC3, 0x94, 0x01, 0x40, 0x46, 0x43, + 0xE1, 0x08, 0x12, 0x19, 0xF3, 0xE0, 0x44, 0x04, +/*0350*/0xF0, 0xE5, 0xE4, 0x20, 0xE7, 0x2A, 0x12, 0x1A, + 0x12, 0x75, 0x83, 0xD2, 0xE0, 0x54, 0x08, 0xD3, +/*0360*/0x94, 0x00, 0x40, 0x04, 0x7F, 0x01, 0x80, 0x02, + 0x7F, 0x00, 0xE5, 0x0A, 0xC3, 0x94, 0x01, 0x40, +/*0370*/0x04, 0x7E, 0x01, 0x80, 0x02, 0x7E, 0x00, 0xEF, + 0x5E, 0x60, 0x05, 0x12, 0x1D, 0xD7, 0x80, 0x17, +/*0380*/0x12, 0x1A, 0x12, 0x75, 0x83, 0xD2, 0xE0, 0x44, + 0x08, 0xF0, 0x02, 0x03, 0xFB, 0x12, 0x1A, 0x12, +/*0390*/0x75, 0x83, 0xD2, 0xE0, 0x54, 0xF7, 0xF0, 0x12, + 0x1E, 0x46, 0x7F, 0x08, 0x12, 0x17, 0x31, 0x74, +/*03A0*/0x8E, 0xFE, 0x12, 0x1A, 0x12, 0x8E, 0x83, 0xE0, + 0xF5, 0x10, 0x54, 0xFE, 0xF0, 0xE5, 0x10, 0x44, +/*03B0*/0x01, 0xFF, 0xE5, 0x08, 0xFD, 0xED, 0x44, 0x07, + 0xF5, 0x82, 0xEF, 0xF0, 0xE5, 0x10, 0x54, 0xFE, +/*03C0*/0xFF, 0xED, 0x44, 0x07, 0xF5, 0x82, 0xEF, 0x12, + 0x1A, 0x11, 0x75, 0x83, 0x86, 0xE0, 0x44, 0x10, +/*03D0*/0x12, 0x1A, 0x11, 0xE0, 0x44, 0x10, 0xF0, 0x12, + 0x19, 0xF3, 0xE0, 0x54, 0xFD, 0x44, 0x01, 0xFF, +/*03E0*/0x12, 0x19, 0xF3, 0xEF, 0x12, 0x1A, 0x32, 0x30, + 0x32, 0x0C, 0xE5, 0x08, 0x44, 0x08, 0xF5, 0x82, +/*03F0*/0x75, 0x83, 0x82, 0x74, 0x05, 0xF0, 0xAF, 0x0B, + 0x12, 0x18, 0xD7, 0x74, 0x10, 0x25, 0x08, 0xF5, +/*0400*/0x08, 0x02, 0x00, 0x85, 0x05, 0x09, 0xE5, 0x09, + 0xD3, 0x94, 0x07, 0x50, 0x03, 0x02, 0x00, 0x82, +/*0410*/0xE5, 0x7E, 0xD3, 0x94, 0x00, 0x40, 0x04, 0x7F, + 0x01, 0x80, 0x02, 0x7F, 0x00, 0xE5, 0x7E, 0xC3, +/*0420*/0x94, 0xFA, 0x50, 0x04, 0x7E, 0x01, 0x80, 0x02, + 0x7E, 0x00, 0xEE, 0x5F, 0x60, 0x02, 0x05, 0x7E, +/*0430*/0x30, 0x35, 0x0B, 0x43, 0xE1, 0x01, 0x7F, 0x09, + 0x12, 0x17, 0x31, 0x02, 0x00, 0x58, 0x53, 0xE1, +/*0440*/0xFE, 0x02, 0x00, 0x58, 0x8E, 0x6A, 0x8F, 0x6B, + 0x8C, 0x6C, 0x8D, 0x6D, 0x75, 0x6E, 0x01, 0x75, +/*0450*/0x6F, 0x01, 0x75, 0x70, 0x01, 0xE4, 0xF5, 0x73, + 0xF5, 0x74, 0xF5, 0x75, 0x90, 0x07, 0x2F, 0xF0, +/*0460*/0xF5, 0x3C, 0xF5, 0x3E, 0xF5, 0x46, 0xF5, 0x47, + 0xF5, 0x3D, 0xF5, 0x3F, 0xF5, 0x6F, 0xE5, 0x6F, +/*0470*/0x70, 0x0F, 0xE5, 0x6B, 0x45, 0x6A, 0x12, 0x07, + 0x2A, 0x75, 0x83, 0x80, 0x74, 0x3A, 0xF0, 0x80, +/*0480*/0x09, 0x12, 0x07, 0x2A, 0x75, 0x83, 0x80, 0x74, + 0x1A, 0xF0, 0xE4, 0xF5, 0x6E, 0xC3, 0x74, 0x3F, +/*0490*/0x95, 0x6E, 0xFF, 0x12, 0x08, 0x65, 0x75, 0x83, + 0x82, 0xEF, 0xF0, 0x12, 0x1A, 0x4D, 0x12, 0x08, +/*04A0*/0xC6, 0xE5, 0x33, 0xF0, 0x12, 0x08, 0xFA, 0x12, + 0x08, 0xB1, 0x40, 0xE1, 0xE5, 0x6F, 0x70, 0x0B, +/*04B0*/0x12, 0x07, 0x2A, 0x75, 0x83, 0x80, 0x74, 0x36, + 0xF0, 0x80, 0x09, 0x12, 0x07, 0x2A, 0x75, 0x83, +/*04C0*/0x80, 0x74, 0x16, 0xF0, 0x75, 0x6E, 0x01, 0x12, + 0x07, 0x2A, 0x75, 0x83, 0xB4, 0xE5, 0x6E, 0xF0, +/*04D0*/0x12, 0x1A, 0x4D, 0x74, 0x3F, 0x25, 0x6E, 0xF5, + 0x82, 0xE4, 0x34, 0x00, 0xF5, 0x83, 0xE5, 0x33, +/*04E0*/0xF0, 0x74, 0xBF, 0x25, 0x6E, 0xF5, 0x82, 0xE4, + 0x34, 0x00, 0x12, 0x08, 0xB1, 0x40, 0xD8, 0xE4, +/*04F0*/0xF5, 0x70, 0xF5, 0x46, 0xF5, 0x47, 0xF5, 0x6E, + 0x12, 0x08, 0xFA, 0xF5, 0x83, 0xE0, 0xFE, 0x12, +/*0500*/0x08, 0xC6, 0xE0, 0x7C, 0x00, 0x24, 0x00, 0xFF, + 0xEC, 0x3E, 0xFE, 0xAD, 0x3B, 0xD3, 0xEF, 0x9D, +/*0510*/0xEE, 0x9C, 0x50, 0x04, 0x7B, 0x01, 0x80, 0x02, + 0x7B, 0x00, 0xE5, 0x70, 0x70, 0x04, 0x7A, 0x01, +/*0520*/0x80, 0x02, 0x7A, 0x00, 0xEB, 0x5A, 0x60, 0x06, + 0x85, 0x6E, 0x46, 0x75, 0x70, 0x01, 0xD3, 0xEF, +/*0530*/0x9D, 0xEE, 0x9C, 0x50, 0x04, 0x7F, 0x01, 0x80, + 0x02, 0x7F, 0x00, 0xE5, 0x70, 0xB4, 0x01, 0x04, +/*0540*/0x7E, 0x01, 0x80, 0x02, 0x7E, 0x00, 0xEF, 0x5E, + 0x60, 0x03, 0x85, 0x6E, 0x47, 0x05, 0x6E, 0xE5, +/*0550*/0x6E, 0x64, 0x7F, 0x70, 0xA3, 0xE5, 0x46, 0x60, + 0x05, 0xE5, 0x47, 0xB4, 0x7E, 0x03, 0x85, 0x46, +/*0560*/0x47, 0xE5, 0x6F, 0x70, 0x08, 0x85, 0x46, 0x76, + 0x85, 0x47, 0x77, 0x80, 0x0E, 0xC3, 0x74, 0x7F, +/*0570*/0x95, 0x46, 0xF5, 0x78, 0xC3, 0x74, 0x7F, 0x95, + 0x47, 0xF5, 0x79, 0xE5, 0x6F, 0x70, 0x37, 0xE5, +/*0580*/0x46, 0x65, 0x47, 0x70, 0x0C, 0x75, 0x73, 0x01, + 0x75, 0x74, 0x01, 0xF5, 0x3C, 0xF5, 0x3D, 0x80, +/*0590*/0x35, 0xE4, 0xF5, 0x4E, 0xC3, 0xE5, 0x47, 0x95, + 0x46, 0xF5, 0x3C, 0xC3, 0x13, 0xF5, 0x71, 0x25, +/*05A0*/0x46, 0xF5, 0x72, 0xC3, 0x94, 0x3F, 0x40, 0x05, + 0xE4, 0xF5, 0x3D, 0x80, 0x40, 0xC3, 0x74, 0x3F, +/*05B0*/0x95, 0x72, 0xF5, 0x3D, 0x80, 0x37, 0xE5, 0x46, + 0x65, 0x47, 0x70, 0x0F, 0x75, 0x73, 0x01, 0x75, +/*05C0*/0x75, 0x01, 0xF5, 0x3E, 0xF5, 0x3F, 0x75, 0x4E, + 0x01, 0x80, 0x22, 0xE4, 0xF5, 0x4E, 0xC3, 0xE5, +/*05D0*/0x47, 0x95, 0x46, 0xF5, 0x3E, 0xC3, 0x13, 0xF5, + 0x71, 0x25, 0x46, 0xF5, 0x72, 0xD3, 0x94, 0x3F, +/*05E0*/0x50, 0x05, 0xE4, 0xF5, 0x3F, 0x80, 0x06, 0xE5, + 0x72, 0x24, 0xC1, 0xF5, 0x3F, 0x05, 0x6F, 0xE5, +/*05F0*/0x6F, 0xC3, 0x94, 0x02, 0x50, 0x03, 0x02, 0x04, + 0x6E, 0xE5, 0x6D, 0x45, 0x6C, 0x70, 0x02, 0x80, +/*0600*/0x04, 0xE5, 0x74, 0x45, 0x75, 0x90, 0x07, 0x2F, + 0xF0, 0x7F, 0x01, 0xE5, 0x3E, 0x60, 0x04, 0xE5, +/*0610*/0x3C, 0x70, 0x14, 0xE4, 0xF5, 0x3C, 0xF5, 0x3D, + 0xF5, 0x3E, 0xF5, 0x3F, 0x12, 0x08, 0xD2, 0x70, +/*0620*/0x04, 0xF0, 0x02, 0x06, 0xA4, 0x80, 0x7A, 0xE5, + 0x3C, 0xC3, 0x95, 0x3E, 0x40, 0x07, 0xE5, 0x3C, +/*0630*/0x95, 0x3E, 0xFF, 0x80, 0x06, 0xC3, 0xE5, 0x3E, + 0x95, 0x3C, 0xFF, 0xE5, 0x76, 0xD3, 0x95, 0x79, +/*0640*/0x40, 0x05, 0x85, 0x76, 0x7A, 0x80, 0x03, 0x85, + 0x79, 0x7A, 0xE5, 0x77, 0xC3, 0x95, 0x78, 0x50, +/*0650*/0x05, 0x85, 0x77, 0x7B, 0x80, 0x03, 0x85, 0x78, + 0x7B, 0xE5, 0x7B, 0xD3, 0x95, 0x7A, 0x40, 0x30, +/*0660*/0xE5, 0x7B, 0x95, 0x7A, 0xF5, 0x3C, 0xF5, 0x3E, + 0xC3, 0xE5, 0x7B, 0x95, 0x7A, 0x90, 0x07, 0x19, +/*0670*/0xF0, 0xE5, 0x3C, 0xC3, 0x13, 0xF5, 0x71, 0x25, + 0x7A, 0xF5, 0x72, 0xC3, 0x94, 0x3F, 0x40, 0x05, +/*0680*/0xE4, 0xF5, 0x3D, 0x80, 0x1F, 0xC3, 0x74, 0x3F, + 0x95, 0x72, 0xF5, 0x3D, 0xF5, 0x3F, 0x80, 0x14, +/*0690*/0xE4, 0xF5, 0x3C, 0xF5, 0x3E, 0x90, 0x07, 0x19, + 0xF0, 0x12, 0x08, 0xD2, 0x70, 0x03, 0xF0, 0x80, +/*06A0*/0x03, 0x74, 0x01, 0xF0, 0x12, 0x08, 0x65, 0x75, + 0x83, 0xD0, 0xE0, 0x54, 0x0F, 0xFE, 0xAD, 0x3C, +/*06B0*/0x70, 0x02, 0x7E, 0x07, 0xBE, 0x0F, 0x02, 0x7E, + 0x80, 0xEE, 0xFB, 0xEF, 0xD3, 0x9B, 0x74, 0x80, +/*06C0*/0xF8, 0x98, 0x40, 0x1F, 0xE4, 0xF5, 0x3C, 0xF5, + 0x3E, 0x12, 0x08, 0xD2, 0x70, 0x03, 0xF0, 0x80, +/*06D0*/0x12, 0x74, 0x01, 0xF0, 0xE5, 0x08, 0xFB, 0xEB, + 0x44, 0x07, 0xF5, 0x82, 0x75, 0x83, 0xD2, 0xE0, +/*06E0*/0x44, 0x10, 0xF0, 0xE5, 0x08, 0xFB, 0xEB, 0x44, + 0x09, 0xF5, 0x82, 0x75, 0x83, 0x9E, 0xED, 0xF0, +/*06F0*/0xEB, 0x44, 0x07, 0xF5, 0x82, 0x75, 0x83, 0xCA, + 0xED, 0xF0, 0x12, 0x08, 0x65, 0x75, 0x83, 0xCC, +/*0700*/0xEF, 0xF0, 0x22, 0xE5, 0x08, 0x44, 0x07, 0xF5, + 0x82, 0x75, 0x83, 0xBC, 0xE0, 0x54, 0xF0, 0xF0, +/*0710*/0xE5, 0x08, 0x44, 0x07, 0xF5, 0x82, 0x75, 0x83, + 0xBE, 0xE0, 0x54, 0xF0, 0xF0, 0xE5, 0x08, 0x44, +/*0720*/0x07, 0xF5, 0x82, 0x75, 0x83, 0xC0, 0xE0, 0x54, + 0xF0, 0xF0, 0xE5, 0x08, 0x44, 0x07, 0xF5, 0x82, +/*0730*/0x22, 0xF0, 0x90, 0x07, 0x28, 0xE0, 0xFE, 0xA3, + 0xE0, 0xF5, 0x82, 0x8E, 0x83, 0x22, 0x85, 0x42, +/*0740*/0x42, 0x85, 0x41, 0x41, 0x85, 0x40, 0x40, 0x74, + 0xC0, 0x2F, 0xF5, 0x82, 0x74, 0x02, 0x3E, 0xF5, +/*0750*/0x83, 0xE5, 0x42, 0xF0, 0x74, 0xE0, 0x2F, 0xF5, + 0x82, 0x74, 0x02, 0x3E, 0xF5, 0x83, 0x22, 0xE5, +/*0760*/0x42, 0x29, 0xFD, 0xE4, 0x33, 0xFC, 0xE5, 0x3C, + 0xC3, 0x9D, 0xEC, 0x64, 0x80, 0xF8, 0x74, 0x80, +/*0770*/0x98, 0x22, 0xF5, 0x83, 0xE0, 0x90, 0x07, 0x22, + 0x54, 0x1F, 0xFD, 0xE0, 0xFA, 0xA3, 0xE0, 0xF5, +/*0780*/0x82, 0x8A, 0x83, 0xED, 0xF0, 0x22, 0x90, 0x07, + 0x22, 0xE0, 0xFC, 0xA3, 0xE0, 0xF5, 0x82, 0x8C, +/*0790*/0x83, 0x22, 0x90, 0x07, 0x24, 0xFF, 0xED, 0x44, + 0x07, 0xCF, 0xF0, 0xA3, 0xEF, 0xF0, 0x22, 0x85, +/*07A0*/0x38, 0x38, 0x85, 0x39, 0x39, 0x85, 0x3A, 0x3A, + 0x74, 0xC0, 0x2F, 0xF5, 0x82, 0x74, 0x02, 0x3E, +/*07B0*/0xF5, 0x83, 0x22, 0x90, 0x07, 0x26, 0xFF, 0xED, + 0x44, 0x07, 0xCF, 0xF0, 0xA3, 0xEF, 0xF0, 0x22, +/*07C0*/0xF0, 0x74, 0xA0, 0x2F, 0xF5, 0x82, 0x74, 0x02, + 0x3E, 0xF5, 0x83, 0x22, 0x74, 0xC0, 0x25, 0x11, +/*07D0*/0xF5, 0x82, 0xE4, 0x34, 0x01, 0xF5, 0x83, 0x22, + 0x74, 0x00, 0x25, 0x11, 0xF5, 0x82, 0xE4, 0x34, +/*07E0*/0x02, 0xF5, 0x83, 0x22, 0x74, 0x60, 0x25, 0x11, + 0xF5, 0x82, 0xE4, 0x34, 0x03, 0xF5, 0x83, 0x22, +/*07F0*/0x74, 0x80, 0x25, 0x11, 0xF5, 0x82, 0xE4, 0x34, + 0x03, 0xF5, 0x83, 0x22, 0x74, 0xE0, 0x25, 0x11, +/*0800*/0xF5, 0x82, 0xE4, 0x34, 0x03, 0xF5, 0x83, 0x22, + 0x74, 0x40, 0x25, 0x11, 0xF5, 0x82, 0xE4, 0x34, +/*0810*/0x06, 0xF5, 0x83, 0x22, 0x74, 0x80, 0x2F, 0xF5, + 0x82, 0x74, 0x02, 0x3E, 0xF5, 0x83, 0x22, 0xAF, +/*0820*/0x08, 0x7E, 0x00, 0xEF, 0x44, 0x07, 0xF5, 0x82, + 0x22, 0xF5, 0x83, 0xE5, 0x82, 0x44, 0x07, 0xF5, +/*0830*/0x82, 0xE5, 0x40, 0xF0, 0x22, 0x74, 0x40, 0x25, + 0x11, 0xF5, 0x82, 0xE4, 0x34, 0x02, 0xF5, 0x83, +/*0840*/0x22, 0x74, 0xC0, 0x25, 0x11, 0xF5, 0x82, 0xE4, + 0x34, 0x03, 0xF5, 0x83, 0x22, 0x74, 0x00, 0x25, +/*0850*/0x11, 0xF5, 0x82, 0xE4, 0x34, 0x06, 0xF5, 0x83, + 0x22, 0x74, 0x20, 0x25, 0x11, 0xF5, 0x82, 0xE4, +/*0860*/0x34, 0x06, 0xF5, 0x83, 0x22, 0xE5, 0x08, 0xFD, + 0xED, 0x44, 0x07, 0xF5, 0x82, 0x22, 0xE5, 0x41, +/*0870*/0xF0, 0xE5, 0x65, 0x64, 0x01, 0x45, 0x64, 0x22, + 0x7E, 0x00, 0xFB, 0x7A, 0x00, 0xFD, 0x7C, 0x00, +/*0880*/0x22, 0x74, 0x20, 0x25, 0x11, 0xF5, 0x82, 0xE4, + 0x34, 0x02, 0x22, 0x74, 0xA0, 0x25, 0x11, 0xF5, +/*0890*/0x82, 0xE4, 0x34, 0x03, 0x22, 0x85, 0x3E, 0x42, + 0x85, 0x3F, 0x41, 0x8F, 0x40, 0x22, 0x85, 0x3C, +/*08A0*/0x42, 0x85, 0x3D, 0x41, 0x8F, 0x40, 0x22, 0x75, + 0x45, 0x3F, 0x90, 0x07, 0x20, 0xE4, 0xF0, 0xA3, +/*08B0*/0x22, 0xF5, 0x83, 0xE5, 0x32, 0xF0, 0x05, 0x6E, + 0xE5, 0x6E, 0xC3, 0x94, 0x40, 0x22, 0xF0, 0xE5, +/*08C0*/0x08, 0x44, 0x06, 0xF5, 0x82, 0x22, 0x74, 0x00, + 0x25, 0x6E, 0xF5, 0x82, 0xE4, 0x34, 0x00, 0xF5, +/*08D0*/0x83, 0x22, 0xE5, 0x6D, 0x45, 0x6C, 0x90, 0x07, + 0x2F, 0x22, 0xE4, 0xF9, 0xE5, 0x3C, 0xD3, 0x95, +/*08E0*/0x3E, 0x22, 0x74, 0x80, 0x2E, 0xF5, 0x82, 0xE4, + 0x34, 0x02, 0xF5, 0x83, 0xE0, 0x22, 0x74, 0xA0, +/*08F0*/0x2E, 0xF5, 0x82, 0xE4, 0x34, 0x02, 0xF5, 0x83, + 0xE0, 0x22, 0x74, 0x80, 0x25, 0x6E, 0xF5, 0x82, +/*0900*/0xE4, 0x34, 0x00, 0x22, 0x25, 0x42, 0xFD, 0xE4, + 0x33, 0xFC, 0x22, 0x85, 0x42, 0x42, 0x85, 0x41, +/*0910*/0x41, 0x85, 0x40, 0x40, 0x22, 0xED, 0x4C, 0x60, + 0x03, 0x02, 0x09, 0xE5, 0xEF, 0x4E, 0x70, 0x37, +/*0920*/0x90, 0x07, 0x26, 0x12, 0x07, 0x89, 0xE0, 0xFD, + 0x12, 0x07, 0xCC, 0xED, 0xF0, 0x90, 0x07, 0x28, +/*0930*/0x12, 0x07, 0x89, 0xE0, 0xFD, 0x12, 0x07, 0xD8, + 0xED, 0xF0, 0x12, 0x07, 0x86, 0xE0, 0x54, 0x1F, +/*0940*/0xFD, 0x12, 0x08, 0x81, 0xF5, 0x83, 0xED, 0xF0, + 0x90, 0x07, 0x24, 0x12, 0x07, 0x89, 0xE0, 0x54, +/*0950*/0x1F, 0xFD, 0x12, 0x08, 0x35, 0xED, 0xF0, 0xEF, + 0x64, 0x04, 0x4E, 0x70, 0x37, 0x90, 0x07, 0x26, +/*0960*/0x12, 0x07, 0x89, 0xE0, 0xFD, 0x12, 0x07, 0xE4, + 0xED, 0xF0, 0x90, 0x07, 0x28, 0x12, 0x07, 0x89, +/*0970*/0xE0, 0xFD, 0x12, 0x07, 0xF0, 0xED, 0xF0, 0x12, + 0x07, 0x86, 0xE0, 0x54, 0x1F, 0xFD, 0x12, 0x08, +/*0980*/0x8B, 0xF5, 0x83, 0xED, 0xF0, 0x90, 0x07, 0x24, + 0x12, 0x07, 0x89, 0xE0, 0x54, 0x1F, 0xFD, 0x12, +/*0990*/0x08, 0x41, 0xED, 0xF0, 0xEF, 0x64, 0x01, 0x4E, + 0x70, 0x04, 0x7D, 0x01, 0x80, 0x02, 0x7D, 0x00, +/*09A0*/0xEF, 0x64, 0x02, 0x4E, 0x70, 0x04, 0x7F, 0x01, + 0x80, 0x02, 0x7F, 0x00, 0xEF, 0x4D, 0x60, 0x78, +/*09B0*/0x90, 0x07, 0x26, 0x12, 0x07, 0x35, 0xE0, 0xFF, + 0x12, 0x07, 0xFC, 0xEF, 0x12, 0x07, 0x31, 0xE0, +/*09C0*/0xFF, 0x12, 0x08, 0x08, 0xEF, 0xF0, 0x90, 0x07, + 0x22, 0x12, 0x07, 0x35, 0xE0, 0x54, 0x1F, 0xFF, +/*09D0*/0x12, 0x08, 0x4D, 0xEF, 0xF0, 0x90, 0x07, 0x24, + 0x12, 0x07, 0x35, 0xE0, 0x54, 0x1F, 0xFF, 0x12, +/*09E0*/0x08, 0x59, 0xEF, 0xF0, 0x22, 0x12, 0x07, 0xCC, + 0xE4, 0xF0, 0x12, 0x07, 0xD8, 0xE4, 0xF0, 0x12, +/*09F0*/0x08, 0x81, 0xF5, 0x83, 0xE4, 0xF0, 0x12, 0x08, + 0x35, 0x74, 0x14, 0xF0, 0x12, 0x07, 0xE4, 0xE4, +/*0A00*/0xF0, 0x12, 0x07, 0xF0, 0xE4, 0xF0, 0x12, 0x08, + 0x8B, 0xF5, 0x83, 0xE4, 0xF0, 0x12, 0x08, 0x41, +/*0A10*/0x74, 0x14, 0xF0, 0x12, 0x07, 0xFC, 0xE4, 0xF0, + 0x12, 0x08, 0x08, 0xE4, 0xF0, 0x12, 0x08, 0x4D, +/*0A20*/0xE4, 0xF0, 0x12, 0x08, 0x59, 0x74, 0x14, 0xF0, + 0x22, 0x53, 0xF9, 0xF7, 0x75, 0xFC, 0x10, 0xE4, +/*0A30*/0xF5, 0xFD, 0x75, 0xFE, 0x30, 0xF5, 0xFF, 0xE5, + 0xE7, 0x20, 0xE7, 0x03, 0x43, 0xF9, 0x08, 0xE5, +/*0A40*/0xE6, 0x20, 0xE7, 0x0B, 0x78, 0xFF, 0xE4, 0xF6, + 0xD8, 0xFD, 0x53, 0xE6, 0xFE, 0x80, 0x09, 0x78, +/*0A50*/0x08, 0xE4, 0xF6, 0xD8, 0xFD, 0x53, 0xE6, 0xFE, + 0x75, 0x81, 0x80, 0xE4, 0xF5, 0xA8, 0xD2, 0xA8, +/*0A60*/0xC2, 0xA9, 0xD2, 0xAF, 0xE5, 0xE2, 0x20, 0xE5, + 0x05, 0x20, 0xE6, 0x02, 0x80, 0x03, 0x43, 0xE1, +/*0A70*/0x02, 0xE5, 0xE2, 0x20, 0xE0, 0x0E, 0x90, 0x00, + 0x00, 0x7F, 0x00, 0x7E, 0x08, 0xE4, 0xF0, 0xA3, +/*0A80*/0xDF, 0xFC, 0xDE, 0xFA, 0x02, 0x0A, 0xDB, 0x43, + 0xFA, 0x01, 0xC0, 0xE0, 0xC0, 0xF0, 0xC0, 0x83, +/*0A90*/0xC0, 0x82, 0xC0, 0xD0, 0x12, 0x1C, 0xE7, 0xD0, + 0xD0, 0xD0, 0x82, 0xD0, 0x83, 0xD0, 0xF0, 0xD0, +/*0AA0*/0xE0, 0x53, 0xFA, 0xFE, 0x32, 0x02, 0x1B, 0x55, + 0xE4, 0x93, 0xA3, 0xF8, 0xE4, 0x93, 0xA3, 0xF6, +/*0AB0*/0x08, 0xDF, 0xF9, 0x80, 0x29, 0xE4, 0x93, 0xA3, + 0xF8, 0x54, 0x07, 0x24, 0x0C, 0xC8, 0xC3, 0x33, +/*0AC0*/0xC4, 0x54, 0x0F, 0x44, 0x20, 0xC8, 0x83, 0x40, + 0x04, 0xF4, 0x56, 0x80, 0x01, 0x46, 0xF6, 0xDF, +/*0AD0*/0xE4, 0x80, 0x0B, 0x01, 0x02, 0x04, 0x08, 0x10, + 0x20, 0x40, 0x80, 0x90, 0x00, 0x3F, 0xE4, 0x7E, +/*0AE0*/0x01, 0x93, 0x60, 0xC1, 0xA3, 0xFF, 0x54, 0x3F, + 0x30, 0xE5, 0x09, 0x54, 0x1F, 0xFE, 0xE4, 0x93, +/*0AF0*/0xA3, 0x60, 0x01, 0x0E, 0xCF, 0x54, 0xC0, 0x25, + 0xE0, 0x60, 0xAD, 0x40, 0xB8, 0x80, 0xFE, 0x8C, +/*0B00*/0x64, 0x8D, 0x65, 0x8A, 0x66, 0x8B, 0x67, 0xE4, + 0xF5, 0x69, 0xEF, 0x4E, 0x70, 0x03, 0x02, 0x1D, +/*0B10*/0x55, 0xE4, 0xF5, 0x68, 0xE5, 0x67, 0x45, 0x66, + 0x70, 0x32, 0x12, 0x07, 0x2A, 0x75, 0x83, 0x90, +/*0B20*/0xE4, 0x12, 0x07, 0x29, 0x75, 0x83, 0xC2, 0xE4, + 0x12, 0x07, 0x29, 0x75, 0x83, 0xC4, 0xE4, 0x12, +/*0B30*/0x08, 0x70, 0x70, 0x29, 0x12, 0x07, 0x2A, 0x75, + 0x83, 0x92, 0xE4, 0x12, 0x07, 0x29, 0x75, 0x83, +/*0B40*/0xC6, 0xE4, 0x12, 0x07, 0x29, 0x75, 0x83, 0xC8, + 0xE4, 0xF0, 0x80, 0x11, 0x90, 0x07, 0x26, 0x12, +/*0B50*/0x07, 0x35, 0xE4, 0x12, 0x08, 0x70, 0x70, 0x05, + 0x12, 0x07, 0x32, 0xE4, 0xF0, 0x12, 0x1D, 0x55, +/*0B60*/0x12, 0x1E, 0xBF, 0xE5, 0x67, 0x45, 0x66, 0x70, + 0x33, 0x12, 0x07, 0x2A, 0x75, 0x83, 0x90, 0xE5, +/*0B70*/0x41, 0x12, 0x07, 0x29, 0x75, 0x83, 0xC2, 0xE5, + 0x41, 0x12, 0x07, 0x29, 0x75, 0x83, 0xC4, 0x12, +/*0B80*/0x08, 0x6E, 0x70, 0x29, 0x12, 0x07, 0x2A, 0x75, + 0x83, 0x92, 0xE5, 0x40, 0x12, 0x07, 0x29, 0x75, +/*0B90*/0x83, 0xC6, 0xE5, 0x40, 0x12, 0x07, 0x29, 0x75, + 0x83, 0xC8, 0x80, 0x0E, 0x90, 0x07, 0x26, 0x12, +/*0BA0*/0x07, 0x35, 0x12, 0x08, 0x6E, 0x70, 0x06, 0x12, + 0x07, 0x32, 0xE5, 0x40, 0xF0, 0xAF, 0x69, 0x7E, +/*0BB0*/0x00, 0xAD, 0x67, 0xAC, 0x66, 0x12, 0x04, 0x44, + 0x12, 0x07, 0x2A, 0x75, 0x83, 0xCA, 0xE0, 0xD3, +/*0BC0*/0x94, 0x00, 0x50, 0x0C, 0x05, 0x68, 0xE5, 0x68, + 0xC3, 0x94, 0x05, 0x50, 0x03, 0x02, 0x0B, 0x14, +/*0BD0*/0x22, 0x8C, 0x60, 0x8D, 0x61, 0x12, 0x08, 0xDA, + 0x74, 0x20, 0x40, 0x0D, 0x2F, 0xF5, 0x82, 0x74, +/*0BE0*/0x03, 0x3E, 0xF5, 0x83, 0xE5, 0x3E, 0xF0, 0x80, + 0x0B, 0x2F, 0xF5, 0x82, 0x74, 0x03, 0x3E, 0xF5, +/*0BF0*/0x83, 0xE5, 0x3C, 0xF0, 0xE5, 0x3C, 0xD3, 0x95, + 0x3E, 0x40, 0x3C, 0xE5, 0x61, 0x45, 0x60, 0x70, +/*0C00*/0x10, 0xE9, 0x12, 0x09, 0x04, 0xE5, 0x3E, 0x12, + 0x07, 0x68, 0x40, 0x3B, 0x12, 0x08, 0x95, 0x80, +/*0C10*/0x18, 0xE5, 0x3E, 0xC3, 0x95, 0x38, 0x40, 0x1D, + 0x85, 0x3E, 0x38, 0xE5, 0x3E, 0x60, 0x05, 0x85, +/*0C20*/0x3F, 0x39, 0x80, 0x03, 0x85, 0x39, 0x39, 0x8F, + 0x3A, 0x12, 0x08, 0x14, 0xE5, 0x3E, 0x12, 0x07, +/*0C30*/0xC0, 0xE5, 0x3F, 0xF0, 0x22, 0x80, 0x43, 0xE5, + 0x61, 0x45, 0x60, 0x70, 0x19, 0x12, 0x07, 0x5F, +/*0C40*/0x40, 0x05, 0x12, 0x08, 0x9E, 0x80, 0x27, 0x12, + 0x09, 0x0B, 0x12, 0x08, 0x14, 0xE5, 0x42, 0x12, +/*0C50*/0x07, 0xC0, 0xE5, 0x41, 0xF0, 0x22, 0xE5, 0x3C, + 0xC3, 0x95, 0x38, 0x40, 0x1D, 0x85, 0x3C, 0x38, +/*0C60*/0xE5, 0x3C, 0x60, 0x05, 0x85, 0x3D, 0x39, 0x80, + 0x03, 0x85, 0x39, 0x39, 0x8F, 0x3A, 0x12, 0x08, +/*0C70*/0x14, 0xE5, 0x3C, 0x12, 0x07, 0xC0, 0xE5, 0x3D, + 0xF0, 0x22, 0x85, 0x38, 0x38, 0x85, 0x39, 0x39, +/*0C80*/0x85, 0x3A, 0x3A, 0x12, 0x08, 0x14, 0xE5, 0x38, + 0x12, 0x07, 0xC0, 0xE5, 0x39, 0xF0, 0x22, 0x7F, +/*0C90*/0x06, 0x12, 0x17, 0x31, 0x12, 0x1D, 0x23, 0x12, + 0x0E, 0x04, 0x12, 0x0E, 0x33, 0xE0, 0x44, 0x0A, +/*0CA0*/0xF0, 0x74, 0x8E, 0xFE, 0x12, 0x0E, 0x04, 0x12, + 0x0E, 0x0B, 0xEF, 0xF0, 0xE5, 0x28, 0x30, 0xE5, +/*0CB0*/0x03, 0xD3, 0x80, 0x01, 0xC3, 0x40, 0x05, 0x75, + 0x14, 0x20, 0x80, 0x03, 0x75, 0x14, 0x08, 0x12, +/*0CC0*/0x0E, 0x04, 0x75, 0x83, 0x8A, 0xE5, 0x14, 0xF0, + 0xB4, 0xFF, 0x05, 0x75, 0x12, 0x80, 0x80, 0x06, +/*0CD0*/0xE5, 0x14, 0xC3, 0x13, 0xF5, 0x12, 0xE4, 0xF5, + 0x16, 0xF5, 0x7F, 0x12, 0x19, 0x36, 0x12, 0x13, +/*0CE0*/0xA3, 0xE5, 0x0A, 0xC3, 0x94, 0x01, 0x50, 0x09, + 0x05, 0x16, 0xE5, 0x16, 0xC3, 0x94, 0x14, 0x40, +/*0CF0*/0xEA, 0xE5, 0xE4, 0x20, 0xE7, 0x28, 0x12, 0x0E, + 0x04, 0x75, 0x83, 0xD2, 0xE0, 0x54, 0x08, 0xD3, +/*0D00*/0x94, 0x00, 0x40, 0x04, 0x7F, 0x01, 0x80, 0x02, + 0x7F, 0x00, 0xE5, 0x0A, 0xC3, 0x94, 0x01, 0x40, +/*0D10*/0x04, 0x7E, 0x01, 0x80, 0x02, 0x7E, 0x00, 0xEF, + 0x5E, 0x60, 0x03, 0x12, 0x1D, 0xD7, 0xE5, 0x7F, +/*0D20*/0xC3, 0x94, 0x11, 0x40, 0x14, 0x12, 0x0E, 0x04, + 0x75, 0x83, 0xD2, 0xE0, 0x44, 0x80, 0xF0, 0xE5, +/*0D30*/0xE4, 0x20, 0xE7, 0x0F, 0x12, 0x1D, 0xD7, 0x80, + 0x0A, 0x12, 0x0E, 0x04, 0x75, 0x83, 0xD2, 0xE0, +/*0D40*/0x54, 0x7F, 0xF0, 0x12, 0x1D, 0x23, 0x22, 0x74, + 0x8A, 0x85, 0x08, 0x82, 0xF5, 0x83, 0xE5, 0x17, +/*0D50*/0xF0, 0x12, 0x0E, 0x3A, 0xE4, 0xF0, 0x90, 0x07, + 0x02, 0xE0, 0x12, 0x0E, 0x17, 0x75, 0x83, 0x90, +/*0D60*/0xEF, 0xF0, 0x74, 0x92, 0xFE, 0xE5, 0x08, 0x44, + 0x07, 0xFF, 0xF5, 0x82, 0x8E, 0x83, 0xE0, 0x54, +/*0D70*/0xC0, 0xFD, 0x90, 0x07, 0x03, 0xE0, 0x54, 0x3F, + 0x4D, 0x8F, 0x82, 0x8E, 0x83, 0xF0, 0x90, 0x07, +/*0D80*/0x04, 0xE0, 0x12, 0x0E, 0x17, 0x75, 0x83, 0x82, + 0xEF, 0xF0, 0x90, 0x07, 0x05, 0xE0, 0xFF, 0xED, +/*0D90*/0x44, 0x07, 0xF5, 0x82, 0x75, 0x83, 0xB4, 0xEF, + 0x12, 0x0E, 0x03, 0x75, 0x83, 0x80, 0xE0, 0x54, +/*0DA0*/0xBF, 0xF0, 0x30, 0x37, 0x0A, 0x12, 0x0E, 0x91, + 0x75, 0x83, 0x94, 0xE0, 0x44, 0x80, 0xF0, 0x30, +/*0DB0*/0x38, 0x0A, 0x12, 0x0E, 0x91, 0x75, 0x83, 0x92, + 0xE0, 0x44, 0x80, 0xF0, 0xE5, 0x28, 0x30, 0xE4, +/*0DC0*/0x1A, 0x20, 0x39, 0x0A, 0x12, 0x0E, 0x04, 0x75, + 0x83, 0x88, 0xE0, 0x54, 0x7F, 0xF0, 0x20, 0x3A, +/*0DD0*/0x0A, 0x12, 0x0E, 0x04, 0x75, 0x83, 0x88, 0xE0, + 0x54, 0xBF, 0xF0, 0x74, 0x8C, 0xFE, 0x12, 0x0E, +/*0DE0*/0x04, 0x8E, 0x83, 0xE0, 0x54, 0x0F, 0x12, 0x0E, + 0x03, 0x75, 0x83, 0x86, 0xE0, 0x54, 0xBF, 0xF0, +/*0DF0*/0xE5, 0x08, 0x44, 0x06, 0x12, 0x0D, 0xFD, 0x75, + 0x83, 0x8A, 0xE4, 0xF0, 0x22, 0xF5, 0x82, 0x75, +/*0E00*/0x83, 0x82, 0xE4, 0xF0, 0xE5, 0x08, 0x44, 0x07, + 0xF5, 0x82, 0x22, 0x8E, 0x83, 0xE0, 0xF5, 0x10, +/*0E10*/0x54, 0xFE, 0xF0, 0xE5, 0x10, 0x44, 0x01, 0xFF, + 0xE5, 0x08, 0xFD, 0xED, 0x44, 0x07, 0xF5, 0x82, +/*0E20*/0x22, 0xE5, 0x15, 0xC4, 0x54, 0x07, 0xFF, 0xE5, + 0x08, 0xFD, 0xED, 0x44, 0x08, 0xF5, 0x82, 0x75, +/*0E30*/0x83, 0x82, 0x22, 0x75, 0x83, 0x80, 0xE0, 0x44, + 0x40, 0xF0, 0xE5, 0x08, 0x44, 0x08, 0xF5, 0x82, +/*0E40*/0x75, 0x83, 0x8A, 0x22, 0xE5, 0x16, 0x25, 0xE0, + 0x25, 0xE0, 0x24, 0xAF, 0xF5, 0x82, 0xE4, 0x34, +/*0E50*/0x1A, 0xF5, 0x83, 0xE4, 0x93, 0xF5, 0x0D, 0x22, + 0x43, 0xE1, 0x10, 0x43, 0xE1, 0x80, 0x53, 0xE1, +/*0E60*/0xFD, 0x85, 0xE1, 0x10, 0x22, 0xE5, 0x16, 0x25, + 0xE0, 0x25, 0xE0, 0x24, 0xB2, 0xF5, 0x82, 0xE4, +/*0E70*/0x34, 0x1A, 0xF5, 0x83, 0xE4, 0x93, 0x22, 0x85, + 0x55, 0x82, 0x85, 0x54, 0x83, 0xE5, 0x15, 0xF0, +/*0E80*/0x22, 0xE5, 0xE2, 0x54, 0x20, 0xD3, 0x94, 0x00, + 0x22, 0xE5, 0xE2, 0x54, 0x40, 0xD3, 0x94, 0x00, +/*0E90*/0x22, 0xE5, 0x08, 0x44, 0x06, 0xF5, 0x82, 0x22, + 0xFD, 0xE5, 0x08, 0xFB, 0xEB, 0x44, 0x07, 0xF5, +/*0EA0*/0x82, 0x22, 0x53, 0xF9, 0xF7, 0x75, 0xFE, 0x30, + 0x22, 0xEF, 0x4E, 0x70, 0x26, 0x12, 0x07, 0xCC, +/*0EB0*/0xE0, 0xFD, 0x90, 0x07, 0x26, 0x12, 0x07, 0x7B, + 0x12, 0x07, 0xD8, 0xE0, 0xFD, 0x90, 0x07, 0x28, +/*0EC0*/0x12, 0x07, 0x7B, 0x12, 0x08, 0x81, 0x12, 0x07, + 0x72, 0x12, 0x08, 0x35, 0xE0, 0x90, 0x07, 0x24, +/*0ED0*/0x12, 0x07, 0x78, 0xEF, 0x64, 0x04, 0x4E, 0x70, + 0x29, 0x12, 0x07, 0xE4, 0xE0, 0xFD, 0x90, 0x07, +/*0EE0*/0x26, 0x12, 0x07, 0x7B, 0x12, 0x07, 0xF0, 0xE0, + 0xFD, 0x90, 0x07, 0x28, 0x12, 0x07, 0x7B, 0x12, +/*0EF0*/0x08, 0x8B, 0x12, 0x07, 0x72, 0x12, 0x08, 0x41, + 0xE0, 0x54, 0x1F, 0xFD, 0x90, 0x07, 0x24, 0x12, +/*0F00*/0x07, 0x7B, 0xEF, 0x64, 0x01, 0x4E, 0x70, 0x04, + 0x7D, 0x01, 0x80, 0x02, 0x7D, 0x00, 0xEF, 0x64, +/*0F10*/0x02, 0x4E, 0x70, 0x04, 0x7F, 0x01, 0x80, 0x02, + 0x7F, 0x00, 0xEF, 0x4D, 0x60, 0x35, 0x12, 0x07, +/*0F20*/0xFC, 0xE0, 0xFF, 0x90, 0x07, 0x26, 0x12, 0x07, + 0x89, 0xEF, 0xF0, 0x12, 0x08, 0x08, 0xE0, 0xFF, +/*0F30*/0x90, 0x07, 0x28, 0x12, 0x07, 0x89, 0xEF, 0xF0, + 0x12, 0x08, 0x4D, 0xE0, 0x54, 0x1F, 0xFF, 0x12, +/*0F40*/0x07, 0x86, 0xEF, 0xF0, 0x12, 0x08, 0x59, 0xE0, + 0x54, 0x1F, 0xFF, 0x90, 0x07, 0x24, 0x12, 0x07, +/*0F50*/0x89, 0xEF, 0xF0, 0x22, 0xE4, 0xF5, 0x53, 0x12, + 0x0E, 0x81, 0x40, 0x04, 0x7F, 0x01, 0x80, 0x02, +/*0F60*/0x7F, 0x00, 0x12, 0x0E, 0x89, 0x40, 0x04, 0x7E, + 0x01, 0x80, 0x02, 0x7E, 0x00, 0xEE, 0x4F, 0x70, +/*0F70*/0x03, 0x02, 0x0F, 0xF6, 0x85, 0xE1, 0x10, 0x43, + 0xE1, 0x02, 0x53, 0xE1, 0x0F, 0x85, 0xE1, 0x10, +/*0F80*/0xE4, 0xF5, 0x51, 0xE5, 0xE3, 0x54, 0x3F, 0xF5, + 0x52, 0x12, 0x0E, 0x89, 0x40, 0x1D, 0xAD, 0x52, +/*0F90*/0xAF, 0x51, 0x12, 0x11, 0x18, 0xEF, 0x60, 0x08, + 0x85, 0xE1, 0x10, 0x43, 0xE1, 0x40, 0x80, 0x0B, +/*0FA0*/0x53, 0xE1, 0xBF, 0x12, 0x0E, 0x58, 0x12, 0x00, + 0x06, 0x80, 0xFB, 0xE5, 0xE3, 0x54, 0x3F, 0xF5, +/*0FB0*/0x51, 0xE5, 0xE4, 0x54, 0x3F, 0xF5, 0x52, 0x12, + 0x0E, 0x81, 0x40, 0x1D, 0xAD, 0x52, 0xAF, 0x51, +/*0FC0*/0x12, 0x11, 0x18, 0xEF, 0x60, 0x08, 0x85, 0xE1, + 0x10, 0x43, 0xE1, 0x20, 0x80, 0x0B, 0x53, 0xE1, +/*0FD0*/0xDF, 0x12, 0x0E, 0x58, 0x12, 0x00, 0x06, 0x80, + 0xFB, 0x12, 0x0E, 0x81, 0x40, 0x04, 0x7F, 0x01, +/*0FE0*/0x80, 0x02, 0x7F, 0x00, 0x12, 0x0E, 0x89, 0x40, + 0x04, 0x7E, 0x01, 0x80, 0x02, 0x7E, 0x00, 0xEE, +/*0FF0*/0x4F, 0x60, 0x03, 0x12, 0x0E, 0x5B, 0x22, 0x12, + 0x0E, 0x21, 0xEF, 0xF0, 0x12, 0x10, 0x91, 0x22, +/*1000*/0x02, 0x11, 0x00, 0x02, 0x10, 0x40, 0x02, 0x10, + 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/*1010*/0x01, 0x20, 0x01, 0x20, 0xE4, 0xF5, 0x57, 0x12, + 0x16, 0xBD, 0x12, 0x16, 0x44, 0xE4, 0x12, 0x10, +/*1020*/0x56, 0x12, 0x14, 0xB7, 0x90, 0x07, 0x26, 0x12, + 0x07, 0x35, 0xE4, 0x12, 0x07, 0x31, 0xE4, 0xF0, +/*1030*/0x12, 0x10, 0x56, 0x12, 0x14, 0xB7, 0x90, 0x07, + 0x26, 0x12, 0x07, 0x35, 0xE5, 0x41, 0x12, 0x07, +/*1040*/0x31, 0xE5, 0x40, 0xF0, 0xAF, 0x57, 0x7E, 0x00, + 0xAD, 0x56, 0x7C, 0x00, 0x12, 0x04, 0x44, 0xAF, +/*1050*/0x56, 0x7E, 0x00, 0x02, 0x11, 0xEE, 0xFF, 0x90, + 0x07, 0x20, 0xA3, 0xE0, 0xFD, 0xE4, 0xF5, 0x56, +/*1060*/0xF5, 0x40, 0xFE, 0xFC, 0xAB, 0x56, 0xFA, 0x12, + 0x11, 0x51, 0x7F, 0x0F, 0x7D, 0x18, 0xE4, 0xF5, +/*1070*/0x56, 0xF5, 0x40, 0xFE, 0xFC, 0xAB, 0x56, 0xFA, + 0x12, 0x15, 0x41, 0xAF, 0x56, 0x7E, 0x00, 0x12, +/*1080*/0x1A, 0xFF, 0xE4, 0xFF, 0xF5, 0x56, 0x7D, 0x1F, + 0xF5, 0x40, 0xFE, 0xFC, 0xAB, 0x56, 0xFA, 0x22, +/*1090*/0x22, 0xE4, 0xF5, 0x55, 0xE5, 0x08, 0xFD, 0x74, + 0xA0, 0xF5, 0x56, 0xED, 0x44, 0x07, 0xF5, 0x57, +/*10A0*/0xE5, 0x28, 0x30, 0xE5, 0x03, 0xD3, 0x80, 0x01, + 0xC3, 0x40, 0x05, 0x7F, 0x28, 0xEF, 0x80, 0x04, +/*10B0*/0x7F, 0x14, 0xEF, 0xC3, 0x13, 0xF5, 0x54, 0xE4, + 0xF9, 0x12, 0x0E, 0x18, 0x75, 0x83, 0x8E, 0xE0, +/*10C0*/0xF5, 0x10, 0xCE, 0xEF, 0xCE, 0xEE, 0xD3, 0x94, + 0x00, 0x40, 0x26, 0xE5, 0x10, 0x54, 0xFE, 0x12, +/*10D0*/0x0E, 0x98, 0x75, 0x83, 0x8E, 0xED, 0xF0, 0xE5, + 0x10, 0x44, 0x01, 0xFD, 0xEB, 0x44, 0x07, 0xF5, +/*10E0*/0x82, 0xED, 0xF0, 0x85, 0x57, 0x82, 0x85, 0x56, + 0x83, 0xE0, 0x30, 0xE3, 0x01, 0x09, 0x1E, 0x80, +/*10F0*/0xD4, 0xC2, 0x34, 0xE9, 0xC3, 0x95, 0x54, 0x40, + 0x02, 0xD2, 0x34, 0x22, 0x02, 0x00, 0x06, 0x22, +/*1100*/0x30, 0x30, 0x11, 0x90, 0x10, 0x00, 0xE4, 0x93, + 0xF5, 0x10, 0x90, 0x10, 0x10, 0xE4, 0x93, 0xF5, +/*1110*/0x10, 0x12, 0x10, 0x90, 0x12, 0x11, 0x50, 0x22, + 0xE4, 0xFC, 0xC3, 0xED, 0x9F, 0xFA, 0xEF, 0xF5, +/*1120*/0x83, 0x75, 0x82, 0x00, 0x79, 0xFF, 0xE4, 0x93, + 0xCC, 0x6C, 0xCC, 0xA3, 0xD9, 0xF8, 0xDA, 0xF6, +/*1130*/0xE5, 0xE2, 0x30, 0xE4, 0x02, 0x8C, 0xE5, 0xED, + 0x24, 0xFF, 0xFF, 0xEF, 0x75, 0x82, 0xFF, 0xF5, +/*1140*/0x83, 0xE4, 0x93, 0x6C, 0x70, 0x03, 0x7F, 0x01, + 0x22, 0x7F, 0x00, 0x22, 0x22, 0x11, 0x00, 0x00, +/*1150*/0x22, 0x8E, 0x58, 0x8F, 0x59, 0x8C, 0x5A, 0x8D, + 0x5B, 0x8A, 0x5C, 0x8B, 0x5D, 0x75, 0x5E, 0x01, +/*1160*/0xE4, 0xF5, 0x5F, 0xF5, 0x60, 0xF5, 0x62, 0x12, + 0x07, 0x2A, 0x75, 0x83, 0xD0, 0xE0, 0xFF, 0xC4, +/*1170*/0x54, 0x0F, 0xF5, 0x61, 0x12, 0x1E, 0xA5, 0x85, + 0x59, 0x5E, 0xD3, 0xE5, 0x5E, 0x95, 0x5B, 0xE5, +/*1180*/0x5A, 0x12, 0x07, 0x6B, 0x50, 0x4B, 0x12, 0x07, + 0x03, 0x75, 0x83, 0xBC, 0xE0, 0x45, 0x5E, 0x12, +/*1190*/0x07, 0x29, 0x75, 0x83, 0xBE, 0xE0, 0x45, 0x5E, + 0x12, 0x07, 0x29, 0x75, 0x83, 0xC0, 0xE0, 0x45, +/*11A0*/0x5E, 0xF0, 0xAF, 0x5F, 0xE5, 0x60, 0x12, 0x08, + 0x78, 0x12, 0x0A, 0xFF, 0xAF, 0x62, 0x7E, 0x00, +/*11B0*/0xAD, 0x5D, 0xAC, 0x5C, 0x12, 0x04, 0x44, 0xE5, + 0x61, 0xAF, 0x5E, 0x7E, 0x00, 0xB4, 0x03, 0x05, +/*11C0*/0x12, 0x1E, 0x21, 0x80, 0x07, 0xAD, 0x5D, 0xAC, + 0x5C, 0x12, 0x13, 0x17, 0x05, 0x5E, 0x02, 0x11, +/*11D0*/0x7A, 0x12, 0x07, 0x03, 0x75, 0x83, 0xBC, 0xE0, + 0x45, 0x40, 0x12, 0x07, 0x29, 0x75, 0x83, 0xBE, +/*11E0*/0xE0, 0x45, 0x40, 0x12, 0x07, 0x29, 0x75, 0x83, + 0xC0, 0xE0, 0x45, 0x40, 0xF0, 0x22, 0x8E, 0x58, +/*11F0*/0x8F, 0x59, 0x75, 0x5A, 0x01, 0x79, 0x01, 0x75, + 0x5B, 0x01, 0xE4, 0xFB, 0x12, 0x07, 0x2A, 0x75, +/*1200*/0x83, 0xAE, 0xE0, 0x54, 0x1A, 0xFF, 0x12, 0x08, + 0x65, 0xE0, 0xC4, 0x13, 0x54, 0x07, 0xFE, 0xEF, +/*1210*/0x70, 0x0C, 0xEE, 0x65, 0x35, 0x70, 0x07, 0x90, + 0x07, 0x2F, 0xE0, 0xB4, 0x01, 0x0D, 0xAF, 0x35, +/*1220*/0x7E, 0x00, 0x12, 0x0E, 0xA9, 0xCF, 0xEB, 0xCF, + 0x02, 0x1E, 0x60, 0xE5, 0x59, 0x64, 0x02, 0x45, +/*1230*/0x58, 0x70, 0x04, 0x7F, 0x01, 0x80, 0x02, 0x7F, + 0x00, 0xE5, 0x59, 0x45, 0x58, 0x70, 0x04, 0x7E, +/*1240*/0x01, 0x80, 0x02, 0x7E, 0x00, 0xEE, 0x4F, 0x60, + 0x23, 0x85, 0x41, 0x49, 0x85, 0x40, 0x4B, 0xE5, +/*1250*/0x59, 0x45, 0x58, 0x70, 0x2C, 0xAF, 0x5A, 0xFE, + 0xCD, 0xE9, 0xCD, 0xFC, 0xAB, 0x59, 0xAA, 0x58, +/*1260*/0x12, 0x0A, 0xFF, 0xAF, 0x5B, 0x7E, 0x00, 0x12, + 0x1E, 0x60, 0x80, 0x15, 0xAF, 0x5B, 0x7E, 0x00, +/*1270*/0x12, 0x1E, 0x60, 0x90, 0x07, 0x26, 0x12, 0x07, + 0x35, 0xE5, 0x49, 0x12, 0x07, 0x31, 0xE5, 0x4B, +/*1280*/0xF0, 0xE4, 0xFD, 0xAF, 0x35, 0xFE, 0xFC, 0x12, + 0x09, 0x15, 0x22, 0x8C, 0x64, 0x8D, 0x65, 0x12, +/*1290*/0x08, 0xDA, 0x40, 0x3C, 0xE5, 0x65, 0x45, 0x64, + 0x70, 0x10, 0x12, 0x09, 0x04, 0xC3, 0xE5, 0x3E, +/*12A0*/0x12, 0x07, 0x69, 0x40, 0x3B, 0x12, 0x08, 0x95, + 0x80, 0x18, 0xE5, 0x3E, 0xC3, 0x95, 0x38, 0x40, +/*12B0*/0x1D, 0x85, 0x3E, 0x38, 0xE5, 0x3E, 0x60, 0x05, + 0x85, 0x3F, 0x39, 0x80, 0x03, 0x85, 0x39, 0x39, +/*12C0*/0x8F, 0x3A, 0x12, 0x07, 0xA8, 0xE5, 0x3E, 0x12, + 0x07, 0x53, 0xE5, 0x3F, 0xF0, 0x22, 0x80, 0x3B, +/*12D0*/0xE5, 0x65, 0x45, 0x64, 0x70, 0x11, 0x12, 0x07, + 0x5F, 0x40, 0x05, 0x12, 0x08, 0x9E, 0x80, 0x1F, +/*12E0*/0x12, 0x07, 0x3E, 0xE5, 0x41, 0xF0, 0x22, 0xE5, + 0x3C, 0xC3, 0x95, 0x38, 0x40, 0x1D, 0x85, 0x3C, +/*12F0*/0x38, 0xE5, 0x3C, 0x60, 0x05, 0x85, 0x3D, 0x39, + 0x80, 0x03, 0x85, 0x39, 0x39, 0x8F, 0x3A, 0x12, +/*1300*/0x07, 0xA8, 0xE5, 0x3C, 0x12, 0x07, 0x53, 0xE5, + 0x3D, 0xF0, 0x22, 0x12, 0x07, 0x9F, 0xE5, 0x38, +/*1310*/0x12, 0x07, 0x53, 0xE5, 0x39, 0xF0, 0x22, 0x8C, + 0x63, 0x8D, 0x64, 0x12, 0x08, 0xDA, 0x40, 0x3C, +/*1320*/0xE5, 0x64, 0x45, 0x63, 0x70, 0x10, 0x12, 0x09, + 0x04, 0xC3, 0xE5, 0x3E, 0x12, 0x07, 0x69, 0x40, +/*1330*/0x3B, 0x12, 0x08, 0x95, 0x80, 0x18, 0xE5, 0x3E, + 0xC3, 0x95, 0x38, 0x40, 0x1D, 0x85, 0x3E, 0x38, +/*1340*/0xE5, 0x3E, 0x60, 0x05, 0x85, 0x3F, 0x39, 0x80, + 0x03, 0x85, 0x39, 0x39, 0x8F, 0x3A, 0x12, 0x07, +/*1350*/0xA8, 0xE5, 0x3E, 0x12, 0x07, 0x53, 0xE5, 0x3F, + 0xF0, 0x22, 0x80, 0x3B, 0xE5, 0x64, 0x45, 0x63, +/*1360*/0x70, 0x11, 0x12, 0x07, 0x5F, 0x40, 0x05, 0x12, + 0x08, 0x9E, 0x80, 0x1F, 0x12, 0x07, 0x3E, 0xE5, +/*1370*/0x41, 0xF0, 0x22, 0xE5, 0x3C, 0xC3, 0x95, 0x38, + 0x40, 0x1D, 0x85, 0x3C, 0x38, 0xE5, 0x3C, 0x60, +/*1380*/0x05, 0x85, 0x3D, 0x39, 0x80, 0x03, 0x85, 0x39, + 0x39, 0x8F, 0x3A, 0x12, 0x07, 0xA8, 0xE5, 0x3C, +/*1390*/0x12, 0x07, 0x53, 0xE5, 0x3D, 0xF0, 0x22, 0x12, + 0x07, 0x9F, 0xE5, 0x38, 0x12, 0x07, 0x53, 0xE5, +/*13A0*/0x39, 0xF0, 0x22, 0xE5, 0x0D, 0xFE, 0xE5, 0x08, + 0x8E, 0x54, 0x44, 0x05, 0xF5, 0x55, 0x75, 0x15, +/*13B0*/0x0F, 0xF5, 0x82, 0x12, 0x0E, 0x7A, 0x12, 0x17, + 0xA3, 0x20, 0x31, 0x05, 0x75, 0x15, 0x03, 0x80, +/*13C0*/0x03, 0x75, 0x15, 0x0B, 0xE5, 0x0A, 0xC3, 0x94, + 0x01, 0x50, 0x38, 0x12, 0x14, 0x20, 0x20, 0x31, +/*13D0*/0x06, 0x05, 0x15, 0x05, 0x15, 0x80, 0x04, 0x15, + 0x15, 0x15, 0x15, 0xE5, 0x0A, 0xC3, 0x94, 0x01, +/*13E0*/0x50, 0x21, 0x12, 0x14, 0x20, 0x20, 0x31, 0x04, + 0x05, 0x15, 0x80, 0x02, 0x15, 0x15, 0xE5, 0x0A, +/*13F0*/0xC3, 0x94, 0x01, 0x50, 0x0E, 0x12, 0x0E, 0x77, + 0x12, 0x17, 0xA3, 0x20, 0x31, 0x05, 0x05, 0x15, +/*1400*/0x12, 0x0E, 0x77, 0xE5, 0x15, 0xB4, 0x08, 0x04, + 0x7F, 0x01, 0x80, 0x02, 0x7F, 0x00, 0xE5, 0x15, +/*1410*/0xB4, 0x07, 0x04, 0x7E, 0x01, 0x80, 0x02, 0x7E, + 0x00, 0xEE, 0x4F, 0x60, 0x02, 0x05, 0x7F, 0x22, +/*1420*/0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xE5, 0x15, + 0xF0, 0x12, 0x17, 0xA3, 0x22, 0x12, 0x07, 0x2A, +/*1430*/0x75, 0x83, 0xAE, 0x74, 0xFF, 0x12, 0x07, 0x29, + 0xE0, 0x54, 0x1A, 0xF5, 0x34, 0xE0, 0xC4, 0x13, +/*1440*/0x54, 0x07, 0xF5, 0x35, 0x24, 0xFE, 0x60, 0x24, + 0x24, 0xFE, 0x60, 0x3C, 0x24, 0x04, 0x70, 0x63, +/*1450*/0x75, 0x31, 0x2D, 0xE5, 0x08, 0xFD, 0x74, 0xB6, + 0x12, 0x07, 0x92, 0x74, 0xBC, 0x90, 0x07, 0x22, +/*1460*/0x12, 0x07, 0x95, 0x74, 0x90, 0x12, 0x07, 0xB3, + 0x74, 0x92, 0x80, 0x3C, 0x75, 0x31, 0x3A, 0xE5, +/*1470*/0x08, 0xFD, 0x74, 0xBA, 0x12, 0x07, 0x92, 0x74, + 0xC0, 0x90, 0x07, 0x22, 0x12, 0x07, 0xB6, 0x74, +/*1480*/0xC4, 0x12, 0x07, 0xB3, 0x74, 0xC8, 0x80, 0x20, + 0x75, 0x31, 0x35, 0xE5, 0x08, 0xFD, 0x74, 0xB8, +/*1490*/0x12, 0x07, 0x92, 0x74, 0xBE, 0xFF, 0xED, 0x44, + 0x07, 0x90, 0x07, 0x22, 0xCF, 0xF0, 0xA3, 0xEF, +/*14A0*/0xF0, 0x74, 0xC2, 0x12, 0x07, 0xB3, 0x74, 0xC6, + 0xFF, 0xED, 0x44, 0x07, 0xA3, 0xCF, 0xF0, 0xA3, +/*14B0*/0xEF, 0xF0, 0x22, 0x75, 0x34, 0x01, 0x22, 0x8E, + 0x58, 0x8F, 0x59, 0x8C, 0x5A, 0x8D, 0x5B, 0x8A, +/*14C0*/0x5C, 0x8B, 0x5D, 0x75, 0x5E, 0x01, 0xE4, 0xF5, + 0x5F, 0x12, 0x1E, 0xA5, 0x85, 0x59, 0x5E, 0xD3, +/*14D0*/0xE5, 0x5E, 0x95, 0x5B, 0xE5, 0x5A, 0x12, 0x07, + 0x6B, 0x50, 0x57, 0xE5, 0x5D, 0x45, 0x5C, 0x70, +/*14E0*/0x30, 0x12, 0x07, 0x2A, 0x75, 0x83, 0x92, 0xE5, + 0x5E, 0x12, 0x07, 0x29, 0x75, 0x83, 0xC6, 0xE5, +/*14F0*/0x5E, 0x12, 0x07, 0x29, 0x75, 0x83, 0xC8, 0xE5, + 0x5E, 0x12, 0x07, 0x29, 0x75, 0x83, 0x90, 0xE5, +/*1500*/0x5E, 0x12, 0x07, 0x29, 0x75, 0x83, 0xC2, 0xE5, + 0x5E, 0x12, 0x07, 0x29, 0x75, 0x83, 0xC4, 0x80, +/*1510*/0x03, 0x12, 0x07, 0x32, 0xE5, 0x5E, 0xF0, 0xAF, + 0x5F, 0x7E, 0x00, 0xAD, 0x5D, 0xAC, 0x5C, 0x12, +/*1520*/0x04, 0x44, 0xAF, 0x5E, 0x7E, 0x00, 0xAD, 0x5D, + 0xAC, 0x5C, 0x12, 0x0B, 0xD1, 0x05, 0x5E, 0x02, +/*1530*/0x14, 0xCF, 0xAB, 0x5D, 0xAA, 0x5C, 0xAD, 0x5B, + 0xAC, 0x5A, 0xAF, 0x59, 0xAE, 0x58, 0x02, 0x1B, +/*1540*/0xFB, 0x8C, 0x5C, 0x8D, 0x5D, 0x8A, 0x5E, 0x8B, + 0x5F, 0x75, 0x60, 0x01, 0xE4, 0xF5, 0x61, 0xF5, +/*1550*/0x62, 0xF5, 0x63, 0x12, 0x1E, 0xA5, 0x8F, 0x60, + 0xD3, 0xE5, 0x60, 0x95, 0x5D, 0xE5, 0x5C, 0x12, +/*1560*/0x07, 0x6B, 0x50, 0x61, 0xE5, 0x5F, 0x45, 0x5E, + 0x70, 0x27, 0x12, 0x07, 0x2A, 0x75, 0x83, 0xB6, +/*1570*/0xE5, 0x60, 0x12, 0x07, 0x29, 0x75, 0x83, 0xB8, + 0xE5, 0x60, 0x12, 0x07, 0x29, 0x75, 0x83, 0xBA, +/*1580*/0xE5, 0x60, 0xF0, 0xAF, 0x61, 0x7E, 0x00, 0xE5, + 0x62, 0x12, 0x08, 0x7A, 0x12, 0x0A, 0xFF, 0x80, +/*1590*/0x19, 0x90, 0x07, 0x24, 0x12, 0x07, 0x35, 0xE5, + 0x60, 0x12, 0x07, 0x29, 0x75, 0x83, 0x8E, 0xE4, +/*15A0*/0x12, 0x07, 0x29, 0x74, 0x01, 0x12, 0x07, 0x29, + 0xE4, 0xF0, 0xAF, 0x63, 0x7E, 0x00, 0xAD, 0x5F, +/*15B0*/0xAC, 0x5E, 0x12, 0x04, 0x44, 0xAF, 0x60, 0x7E, + 0x00, 0xAD, 0x5F, 0xAC, 0x5E, 0x12, 0x12, 0x8B, +/*15C0*/0x05, 0x60, 0x02, 0x15, 0x58, 0x22, 0x90, 0x11, + 0x4D, 0xE4, 0x93, 0x90, 0x07, 0x2E, 0xF0, 0x12, +/*15D0*/0x08, 0x1F, 0x75, 0x83, 0xAE, 0xE0, 0x54, 0x1A, + 0xF5, 0x34, 0x70, 0x67, 0xEF, 0x44, 0x07, 0xF5, +/*15E0*/0x82, 0x75, 0x83, 0xCE, 0xE0, 0xFF, 0x13, 0x13, + 0x13, 0x54, 0x07, 0xF5, 0x36, 0x54, 0x0F, 0xD3, +/*15F0*/0x94, 0x00, 0x40, 0x06, 0x12, 0x14, 0x2D, 0x12, + 0x1B, 0xA9, 0xE5, 0x36, 0x54, 0x0F, 0x24, 0xFE, +/*1600*/0x60, 0x0C, 0x14, 0x60, 0x0C, 0x14, 0x60, 0x19, + 0x24, 0x03, 0x70, 0x37, 0x80, 0x10, 0x02, 0x1E, +/*1610*/0x91, 0x12, 0x1E, 0x91, 0x12, 0x07, 0x2A, 0x75, + 0x83, 0xCE, 0xE0, 0x54, 0xEF, 0xF0, 0x02, 0x1D, +/*1620*/0xAE, 0x12, 0x10, 0x14, 0xE4, 0xF5, 0x55, 0x12, + 0x1D, 0x85, 0x05, 0x55, 0xE5, 0x55, 0xC3, 0x94, +/*1630*/0x05, 0x40, 0xF4, 0x12, 0x07, 0x2A, 0x75, 0x83, + 0xCE, 0xE0, 0x54, 0xC7, 0x12, 0x07, 0x29, 0xE0, +/*1640*/0x44, 0x08, 0xF0, 0x22, 0xE4, 0xF5, 0x58, 0xF5, + 0x59, 0xAF, 0x08, 0xEF, 0x44, 0x07, 0xF5, 0x82, +/*1650*/0x75, 0x83, 0xD0, 0xE0, 0xFD, 0xC4, 0x54, 0x0F, + 0xF5, 0x5A, 0xEF, 0x44, 0x07, 0xF5, 0x82, 0x75, +/*1660*/0x83, 0x80, 0x74, 0x01, 0xF0, 0x12, 0x08, 0x21, + 0x75, 0x83, 0x82, 0xE5, 0x45, 0xF0, 0xEF, 0x44, +/*1670*/0x07, 0xF5, 0x82, 0x75, 0x83, 0x8A, 0x74, 0xFF, + 0xF0, 0x12, 0x1A, 0x4D, 0x12, 0x07, 0x2A, 0x75, +/*1680*/0x83, 0xBC, 0xE0, 0x54, 0xEF, 0x12, 0x07, 0x29, + 0x75, 0x83, 0xBE, 0xE0, 0x54, 0xEF, 0x12, 0x07, +/*1690*/0x29, 0x75, 0x83, 0xC0, 0xE0, 0x54, 0xEF, 0x12, + 0x07, 0x29, 0x75, 0x83, 0xBC, 0xE0, 0x44, 0x10, +/*16A0*/0x12, 0x07, 0x29, 0x75, 0x83, 0xBE, 0xE0, 0x44, + 0x10, 0x12, 0x07, 0x29, 0x75, 0x83, 0xC0, 0xE0, +/*16B0*/0x44, 0x10, 0xF0, 0xAF, 0x58, 0xE5, 0x59, 0x12, + 0x08, 0x78, 0x02, 0x0A, 0xFF, 0xE4, 0xF5, 0x58, +/*16C0*/0x7D, 0x01, 0xF5, 0x59, 0xAF, 0x35, 0xFE, 0xFC, + 0x12, 0x09, 0x15, 0x12, 0x07, 0x2A, 0x75, 0x83, +/*16D0*/0xB6, 0x74, 0x10, 0x12, 0x07, 0x29, 0x75, 0x83, + 0xB8, 0x74, 0x10, 0x12, 0x07, 0x29, 0x75, 0x83, +/*16E0*/0xBA, 0x74, 0x10, 0x12, 0x07, 0x29, 0x75, 0x83, + 0xBC, 0x74, 0x10, 0x12, 0x07, 0x29, 0x75, 0x83, +/*16F0*/0xBE, 0x74, 0x10, 0x12, 0x07, 0x29, 0x75, 0x83, + 0xC0, 0x74, 0x10, 0x12, 0x07, 0x29, 0x75, 0x83, +/*1700*/0x90, 0xE4, 0x12, 0x07, 0x29, 0x75, 0x83, 0xC2, + 0xE4, 0x12, 0x07, 0x29, 0x75, 0x83, 0xC4, 0xE4, +/*1710*/0x12, 0x07, 0x29, 0x75, 0x83, 0x92, 0xE4, 0x12, + 0x07, 0x29, 0x75, 0x83, 0xC6, 0xE4, 0x12, 0x07, +/*1720*/0x29, 0x75, 0x83, 0xC8, 0xE4, 0xF0, 0xAF, 0x58, + 0xFE, 0xE5, 0x59, 0x12, 0x08, 0x7A, 0x02, 0x0A, +/*1730*/0xFF, 0xE5, 0xE2, 0x30, 0xE4, 0x6C, 0xE5, 0xE7, + 0x54, 0xC0, 0x64, 0x40, 0x70, 0x64, 0xE5, 0x09, +/*1740*/0xC4, 0x54, 0x30, 0xFE, 0xE5, 0x08, 0x25, 0xE0, + 0x25, 0xE0, 0x54, 0xC0, 0x4E, 0xFE, 0xEF, 0x54, +/*1750*/0x3F, 0x4E, 0xFD, 0xE5, 0x2B, 0xAE, 0x2A, 0x78, + 0x02, 0xC3, 0x33, 0xCE, 0x33, 0xCE, 0xD8, 0xF9, +/*1760*/0xF5, 0x82, 0x8E, 0x83, 0xED, 0xF0, 0xE5, 0x2B, + 0xAE, 0x2A, 0x78, 0x02, 0xC3, 0x33, 0xCE, 0x33, +/*1770*/0xCE, 0xD8, 0xF9, 0xFF, 0xF5, 0x82, 0x8E, 0x83, + 0xA3, 0xE5, 0xFE, 0xF0, 0x8F, 0x82, 0x8E, 0x83, +/*1780*/0xA3, 0xA3, 0xE5, 0xFD, 0xF0, 0x8F, 0x82, 0x8E, + 0x83, 0xA3, 0xA3, 0xA3, 0xE5, 0xFC, 0xF0, 0xC3, +/*1790*/0xE5, 0x2B, 0x94, 0xFA, 0xE5, 0x2A, 0x94, 0x00, + 0x50, 0x08, 0x05, 0x2B, 0xE5, 0x2B, 0x70, 0x02, +/*17A0*/0x05, 0x2A, 0x22, 0xE4, 0xFF, 0xE4, 0xF5, 0x58, + 0xF5, 0x56, 0xF5, 0x57, 0x74, 0x82, 0xFC, 0x12, +/*17B0*/0x0E, 0x04, 0x8C, 0x83, 0xE0, 0xF5, 0x10, 0x54, + 0x7F, 0xF0, 0xE5, 0x10, 0x44, 0x80, 0x12, 0x0E, +/*17C0*/0x98, 0xED, 0xF0, 0x7E, 0x0A, 0x12, 0x0E, 0x04, + 0x75, 0x83, 0xA0, 0xE0, 0x20, 0xE0, 0x26, 0xDE, +/*17D0*/0xF4, 0x05, 0x57, 0xE5, 0x57, 0x70, 0x02, 0x05, + 0x56, 0xE5, 0x14, 0x24, 0x01, 0xFD, 0xE4, 0x33, +/*17E0*/0xFC, 0xD3, 0xE5, 0x57, 0x9D, 0xE5, 0x56, 0x9C, + 0x40, 0xD9, 0xE5, 0x0A, 0x94, 0x20, 0x50, 0x02, +/*17F0*/0x05, 0x0A, 0x43, 0xE1, 0x08, 0xC2, 0x31, 0x12, + 0x0E, 0x04, 0x75, 0x83, 0xA6, 0xE0, 0x55, 0x12, +/*1800*/0x65, 0x12, 0x70, 0x03, 0xD2, 0x31, 0x22, 0xC2, + 0x31, 0x22, 0x90, 0x07, 0x26, 0xE0, 0xFA, 0xA3, +/*1810*/0xE0, 0xF5, 0x82, 0x8A, 0x83, 0xE0, 0xF5, 0x41, + 0xE5, 0x39, 0xC3, 0x95, 0x41, 0x40, 0x26, 0xE5, +/*1820*/0x39, 0x95, 0x41, 0xC3, 0x9F, 0xEE, 0x12, 0x07, + 0x6B, 0x40, 0x04, 0x7C, 0x01, 0x80, 0x02, 0x7C, +/*1830*/0x00, 0xE5, 0x41, 0x64, 0x3F, 0x60, 0x04, 0x7B, + 0x01, 0x80, 0x02, 0x7B, 0x00, 0xEC, 0x5B, 0x60, +/*1840*/0x29, 0x05, 0x41, 0x80, 0x28, 0xC3, 0xE5, 0x41, + 0x95, 0x39, 0xC3, 0x9F, 0xEE, 0x12, 0x07, 0x6B, +/*1850*/0x40, 0x04, 0x7F, 0x01, 0x80, 0x02, 0x7F, 0x00, + 0xE5, 0x41, 0x60, 0x04, 0x7E, 0x01, 0x80, 0x02, +/*1860*/0x7E, 0x00, 0xEF, 0x5E, 0x60, 0x04, 0x15, 0x41, + 0x80, 0x03, 0x85, 0x39, 0x41, 0x85, 0x3A, 0x40, +/*1870*/0x22, 0xE5, 0xE2, 0x30, 0xE4, 0x60, 0xE5, 0xE1, + 0x30, 0xE2, 0x5B, 0xE5, 0x09, 0x70, 0x04, 0x7F, +/*1880*/0x01, 0x80, 0x02, 0x7F, 0x00, 0xE5, 0x08, 0x70, + 0x04, 0x7E, 0x01, 0x80, 0x02, 0x7E, 0x00, 0xEE, +/*1890*/0x5F, 0x60, 0x43, 0x53, 0xF9, 0xF8, 0xE5, 0xE2, + 0x30, 0xE4, 0x3B, 0xE5, 0xE1, 0x30, 0xE2, 0x2E, +/*18A0*/0x43, 0xFA, 0x02, 0x53, 0xFA, 0xFB, 0xE4, 0xF5, + 0x10, 0x90, 0x94, 0x70, 0xE5, 0x10, 0xF0, 0xE5, +/*18B0*/0xE1, 0x30, 0xE2, 0xE7, 0x90, 0x94, 0x70, 0xE0, + 0x65, 0x10, 0x60, 0x03, 0x43, 0xFA, 0x04, 0x05, +/*18C0*/0x10, 0x90, 0x94, 0x70, 0xE5, 0x10, 0xF0, 0x70, + 0xE6, 0x12, 0x00, 0x06, 0x80, 0xE1, 0x53, 0xFA, +/*18D0*/0xFD, 0x53, 0xFA, 0xFB, 0x80, 0xC0, 0x22, 0x8F, + 0x54, 0x12, 0x00, 0x06, 0xE5, 0xE1, 0x30, 0xE0, +/*18E0*/0x04, 0x7F, 0x01, 0x80, 0x02, 0x7F, 0x00, 0xE5, + 0x7E, 0xD3, 0x94, 0x05, 0x40, 0x04, 0x7E, 0x01, +/*18F0*/0x80, 0x02, 0x7E, 0x00, 0xEE, 0x4F, 0x60, 0x3D, + 0x85, 0x54, 0x11, 0xE5, 0xE2, 0x20, 0xE1, 0x32, +/*1900*/0x74, 0xCE, 0x12, 0x1A, 0x05, 0x30, 0xE7, 0x04, + 0x7D, 0x01, 0x80, 0x02, 0x7D, 0x00, 0x8F, 0x82, +/*1910*/0x8E, 0x83, 0xE0, 0x30, 0xE6, 0x04, 0x7F, 0x01, + 0x80, 0x02, 0x7F, 0x00, 0xEF, 0x5D, 0x70, 0x15, +/*1920*/0x12, 0x15, 0xC6, 0x74, 0xCE, 0x12, 0x1A, 0x05, + 0x30, 0xE6, 0x07, 0xE0, 0x44, 0x80, 0xF0, 0x43, +/*1930*/0xF9, 0x80, 0x12, 0x18, 0x71, 0x22, 0x12, 0x0E, + 0x44, 0xE5, 0x16, 0x25, 0xE0, 0x25, 0xE0, 0x24, +/*1940*/0xB0, 0xF5, 0x82, 0xE4, 0x34, 0x1A, 0xF5, 0x83, + 0xE4, 0x93, 0xF5, 0x0F, 0xE5, 0x16, 0x25, 0xE0, +/*1950*/0x25, 0xE0, 0x24, 0xB1, 0xF5, 0x82, 0xE4, 0x34, + 0x1A, 0xF5, 0x83, 0xE4, 0x93, 0xF5, 0x0E, 0x12, +/*1960*/0x0E, 0x65, 0xF5, 0x10, 0xE5, 0x0F, 0x54, 0xF0, + 0x12, 0x0E, 0x17, 0x75, 0x83, 0x8C, 0xEF, 0xF0, +/*1970*/0xE5, 0x0F, 0x30, 0xE0, 0x0C, 0x12, 0x0E, 0x04, + 0x75, 0x83, 0x86, 0xE0, 0x44, 0x40, 0xF0, 0x80, +/*1980*/0x0A, 0x12, 0x0E, 0x04, 0x75, 0x83, 0x86, 0xE0, + 0x54, 0xBF, 0xF0, 0x12, 0x0E, 0x91, 0x75, 0x83, +/*1990*/0x82, 0xE5, 0x0E, 0xF0, 0x22, 0x7F, 0x05, 0x12, + 0x17, 0x31, 0x12, 0x0E, 0x04, 0x12, 0x0E, 0x33, +/*19A0*/0x74, 0x02, 0xF0, 0x74, 0x8E, 0xFE, 0x12, 0x0E, + 0x04, 0x12, 0x0E, 0x0B, 0xEF, 0xF0, 0x75, 0x15, +/*19B0*/0x70, 0x12, 0x0F, 0xF7, 0x20, 0x34, 0x05, 0x75, + 0x15, 0x10, 0x80, 0x03, 0x75, 0x15, 0x50, 0x12, +/*19C0*/0x0F, 0xF7, 0x20, 0x34, 0x04, 0x74, 0x10, 0x80, + 0x02, 0x74, 0xF0, 0x25, 0x15, 0xF5, 0x15, 0x12, +/*19D0*/0x0E, 0x21, 0xEF, 0xF0, 0x12, 0x10, 0x91, 0x20, + 0x34, 0x17, 0xE5, 0x15, 0x64, 0x30, 0x60, 0x0C, +/*19E0*/0x74, 0x10, 0x25, 0x15, 0xF5, 0x15, 0xB4, 0x80, + 0x03, 0xE4, 0xF5, 0x15, 0x12, 0x0E, 0x21, 0xEF, +/*19F0*/0xF0, 0x22, 0xF0, 0xE5, 0x0B, 0x25, 0xE0, 0x25, + 0xE0, 0x24, 0x82, 0xF5, 0x82, 0xE4, 0x34, 0x07, +/*1A00*/0xF5, 0x83, 0x22, 0x74, 0x88, 0xFE, 0xE5, 0x08, + 0x44, 0x07, 0xFF, 0xF5, 0x82, 0x8E, 0x83, 0xE0, +/*1A10*/0x22, 0xF0, 0xE5, 0x08, 0x44, 0x07, 0xF5, 0x82, + 0x22, 0xF0, 0xE0, 0x54, 0xC0, 0x8F, 0x82, 0x8E, +/*1A20*/0x83, 0xF0, 0x22, 0xEF, 0x44, 0x07, 0xF5, 0x82, + 0x75, 0x83, 0x86, 0xE0, 0x54, 0x10, 0xD3, 0x94, +/*1A30*/0x00, 0x22, 0xF0, 0x90, 0x07, 0x15, 0xE0, 0x04, + 0xF0, 0x22, 0x44, 0x06, 0xF5, 0x82, 0x75, 0x83, +/*1A40*/0x9E, 0xE0, 0x22, 0xFE, 0xEF, 0x44, 0x07, 0xF5, + 0x82, 0x8E, 0x83, 0xE0, 0x22, 0xE4, 0x90, 0x07, +/*1A50*/0x2A, 0xF0, 0xA3, 0xF0, 0x12, 0x07, 0x2A, 0x75, + 0x83, 0x82, 0xE0, 0x54, 0x7F, 0x12, 0x07, 0x29, +/*1A60*/0xE0, 0x44, 0x80, 0xF0, 0x12, 0x10, 0xFC, 0x12, + 0x08, 0x1F, 0x75, 0x83, 0xA0, 0xE0, 0x20, 0xE0, +/*1A70*/0x1A, 0x90, 0x07, 0x2B, 0xE0, 0x04, 0xF0, 0x70, + 0x06, 0x90, 0x07, 0x2A, 0xE0, 0x04, 0xF0, 0x90, +/*1A80*/0x07, 0x2A, 0xE0, 0xB4, 0x10, 0xE1, 0xA3, 0xE0, + 0xB4, 0x00, 0xDC, 0xEE, 0x44, 0xA6, 0xFC, 0xEF, +/*1A90*/0x44, 0x07, 0xF5, 0x82, 0x8C, 0x83, 0xE0, 0xF5, + 0x32, 0xEE, 0x44, 0xA8, 0xFE, 0xEF, 0x44, 0x07, +/*1AA0*/0xF5, 0x82, 0x8E, 0x83, 0xE0, 0xF5, 0x33, 0x22, + 0x01, 0x20, 0x11, 0x00, 0x04, 0x20, 0x00, 0x90, +/*1AB0*/0x00, 0x20, 0x0F, 0x92, 0x00, 0x21, 0x0F, 0x94, + 0x00, 0x22, 0x0F, 0x96, 0x00, 0x23, 0x0F, 0x98, +/*1AC0*/0x00, 0x24, 0x0F, 0x9A, 0x00, 0x25, 0x0F, 0x9C, + 0x00, 0x26, 0x0F, 0x9E, 0x00, 0x27, 0x0F, 0xA0, +/*1AD0*/0x01, 0x20, 0x01, 0xA2, 0x01, 0x21, 0x01, 0xA4, + 0x01, 0x22, 0x01, 0xA6, 0x01, 0x23, 0x01, 0xA8, +/*1AE0*/0x01, 0x24, 0x01, 0xAA, 0x01, 0x25, 0x01, 0xAC, + 0x01, 0x26, 0x01, 0xAE, 0x01, 0x27, 0x01, 0xB0, +/*1AF0*/0x01, 0x28, 0x01, 0xB4, 0x00, 0x28, 0x0F, 0xB6, + 0x40, 0x28, 0x0F, 0xB8, 0x61, 0x28, 0x01, 0xCB, +/*1B00*/0xEF, 0xCB, 0xCA, 0xEE, 0xCA, 0x7F, 0x01, 0xE4, + 0xFD, 0xEB, 0x4A, 0x70, 0x24, 0xE5, 0x08, 0xF5, +/*1B10*/0x82, 0x74, 0xB6, 0x12, 0x08, 0x29, 0xE5, 0x08, + 0xF5, 0x82, 0x74, 0xB8, 0x12, 0x08, 0x29, 0xE5, +/*1B20*/0x08, 0xF5, 0x82, 0x74, 0xBA, 0x12, 0x08, 0x29, + 0x7E, 0x00, 0x7C, 0x00, 0x12, 0x0A, 0xFF, 0x80, +/*1B30*/0x12, 0x90, 0x07, 0x26, 0x12, 0x07, 0x35, 0xE5, + 0x41, 0xF0, 0x90, 0x07, 0x24, 0x12, 0x07, 0x35, +/*1B40*/0xE5, 0x40, 0xF0, 0x12, 0x07, 0x2A, 0x75, 0x83, + 0x8E, 0xE4, 0x12, 0x07, 0x29, 0x74, 0x01, 0x12, +/*1B50*/0x07, 0x29, 0xE4, 0xF0, 0x22, 0xE4, 0xF5, 0x26, + 0xF5, 0x27, 0x53, 0xE1, 0xFE, 0xF5, 0x2A, 0x75, +/*1B60*/0x2B, 0x01, 0xF5, 0x08, 0x7F, 0x01, 0x12, 0x17, + 0x31, 0x30, 0x30, 0x1C, 0x90, 0x1A, 0xA9, 0xE4, +/*1B70*/0x93, 0xF5, 0x10, 0x90, 0x1F, 0xF9, 0xE4, 0x93, + 0xF5, 0x10, 0x90, 0x00, 0x41, 0xE4, 0x93, 0xF5, +/*1B80*/0x10, 0x90, 0x1E, 0xCA, 0xE4, 0x93, 0xF5, 0x10, + 0x7F, 0x02, 0x12, 0x17, 0x31, 0x12, 0x0F, 0x54, +/*1B90*/0x7F, 0x03, 0x12, 0x17, 0x31, 0x12, 0x00, 0x06, + 0xE5, 0xE2, 0x30, 0xE7, 0x09, 0x12, 0x10, 0x00, +/*1BA0*/0x30, 0x30, 0x03, 0x12, 0x11, 0x00, 0x02, 0x00, + 0x47, 0x12, 0x08, 0x1F, 0x75, 0x83, 0xD0, 0xE0, +/*1BB0*/0xC4, 0x54, 0x0F, 0xFD, 0x75, 0x43, 0x01, 0x75, + 0x44, 0xFF, 0x12, 0x08, 0xAA, 0x74, 0x04, 0xF0, +/*1BC0*/0x75, 0x3B, 0x01, 0xED, 0x14, 0x60, 0x0C, 0x14, + 0x60, 0x0B, 0x14, 0x60, 0x0F, 0x24, 0x03, 0x70, +/*1BD0*/0x0B, 0x80, 0x09, 0x80, 0x00, 0x12, 0x08, 0xA7, + 0x04, 0xF0, 0x80, 0x06, 0x12, 0x08, 0xA7, 0x74, +/*1BE0*/0x04, 0xF0, 0xEE, 0x44, 0x82, 0xFE, 0xEF, 0x44, + 0x07, 0xF5, 0x82, 0x8E, 0x83, 0xE5, 0x45, 0x12, +/*1BF0*/0x08, 0xBE, 0x75, 0x83, 0x82, 0xE5, 0x31, 0xF0, + 0x02, 0x11, 0x4C, 0x8E, 0x60, 0x8F, 0x61, 0x12, +/*1C00*/0x1E, 0xA5, 0xE4, 0xFF, 0xCE, 0xED, 0xCE, 0xEE, + 0xD3, 0x95, 0x61, 0xE5, 0x60, 0x12, 0x07, 0x6B, +/*1C10*/0x40, 0x39, 0x74, 0x20, 0x2E, 0xF5, 0x82, 0xE4, + 0x34, 0x03, 0xF5, 0x83, 0xE0, 0x70, 0x03, 0xFF, +/*1C20*/0x80, 0x26, 0x12, 0x08, 0xE2, 0xFD, 0xC3, 0x9F, + 0x40, 0x1E, 0xCF, 0xED, 0xCF, 0xEB, 0x4A, 0x70, +/*1C30*/0x0B, 0x8D, 0x42, 0x12, 0x08, 0xEE, 0xF5, 0x41, + 0x8E, 0x40, 0x80, 0x0C, 0x12, 0x08, 0xE2, 0xF5, +/*1C40*/0x38, 0x12, 0x08, 0xEE, 0xF5, 0x39, 0x8E, 0x3A, + 0x1E, 0x80, 0xBC, 0x22, 0x75, 0x58, 0x01, 0xE5, +/*1C50*/0x35, 0x70, 0x0C, 0x12, 0x07, 0xCC, 0xE0, 0xF5, + 0x4A, 0x12, 0x07, 0xD8, 0xE0, 0xF5, 0x4C, 0xE5, +/*1C60*/0x35, 0xB4, 0x04, 0x0C, 0x12, 0x07, 0xE4, 0xE0, + 0xF5, 0x4A, 0x12, 0x07, 0xF0, 0xE0, 0xF5, 0x4C, +/*1C70*/0xE5, 0x35, 0xB4, 0x01, 0x04, 0x7F, 0x01, 0x80, + 0x02, 0x7F, 0x00, 0xE5, 0x35, 0xB4, 0x02, 0x04, +/*1C80*/0x7E, 0x01, 0x80, 0x02, 0x7E, 0x00, 0xEE, 0x4F, + 0x60, 0x0C, 0x12, 0x07, 0xFC, 0xE0, 0xF5, 0x4A, +/*1C90*/0x12, 0x08, 0x08, 0xE0, 0xF5, 0x4C, 0x85, 0x41, + 0x49, 0x85, 0x40, 0x4B, 0x22, 0x75, 0x5B, 0x01, +/*1CA0*/0x90, 0x07, 0x24, 0x12, 0x07, 0x35, 0xE0, 0x54, + 0x1F, 0xFF, 0xD3, 0x94, 0x02, 0x50, 0x04, 0x8F, +/*1CB0*/0x58, 0x80, 0x05, 0xEF, 0x24, 0xFE, 0xF5, 0x58, + 0xEF, 0xC3, 0x94, 0x18, 0x40, 0x05, 0x75, 0x59, +/*1CC0*/0x18, 0x80, 0x04, 0xEF, 0x04, 0xF5, 0x59, 0x85, + 0x43, 0x5A, 0xAF, 0x58, 0x7E, 0x00, 0xAD, 0x59, +/*1CD0*/0x7C, 0x00, 0xAB, 0x5B, 0x7A, 0x00, 0x12, 0x15, + 0x41, 0xAF, 0x5A, 0x7E, 0x00, 0x12, 0x18, 0x0A, +/*1CE0*/0xAF, 0x5B, 0x7E, 0x00, 0x02, 0x1A, 0xFF, 0xE5, + 0xE2, 0x30, 0xE7, 0x0E, 0x12, 0x10, 0x03, 0xC2, +/*1CF0*/0x30, 0x30, 0x30, 0x03, 0x12, 0x10, 0xFF, 0x20, + 0x33, 0x28, 0xE5, 0xE7, 0x30, 0xE7, 0x05, 0x12, +/*1D00*/0x0E, 0xA2, 0x80, 0x0D, 0xE5, 0xFE, 0xC3, 0x94, + 0x20, 0x50, 0x06, 0x12, 0x0E, 0xA2, 0x43, 0xF9, +/*1D10*/0x08, 0xE5, 0xF2, 0x30, 0xE7, 0x03, 0x53, 0xF9, + 0x7F, 0xE5, 0xF1, 0x54, 0x70, 0xD3, 0x94, 0x00, +/*1D20*/0x50, 0xD8, 0x22, 0x12, 0x0E, 0x04, 0x75, 0x83, + 0x80, 0xE4, 0xF0, 0xE5, 0x08, 0x44, 0x07, 0x12, +/*1D30*/0x0D, 0xFD, 0x75, 0x83, 0x84, 0x12, 0x0E, 0x02, + 0x75, 0x83, 0x86, 0x12, 0x0E, 0x02, 0x75, 0x83, +/*1D40*/0x8C, 0xE0, 0x54, 0xF3, 0x12, 0x0E, 0x03, 0x75, + 0x83, 0x8E, 0x12, 0x0E, 0x02, 0x75, 0x83, 0x94, +/*1D50*/0xE0, 0x54, 0xFB, 0xF0, 0x22, 0x12, 0x07, 0x2A, + 0x75, 0x83, 0x8E, 0xE4, 0x12, 0x07, 0x29, 0x74, +/*1D60*/0x01, 0x12, 0x07, 0x29, 0xE4, 0x12, 0x08, 0xBE, + 0x75, 0x83, 0x8C, 0xE0, 0x44, 0x20, 0x12, 0x08, +/*1D70*/0xBE, 0xE0, 0x54, 0xDF, 0xF0, 0x74, 0x84, 0x85, + 0x08, 0x82, 0xF5, 0x83, 0xE0, 0x54, 0x7F, 0xF0, +/*1D80*/0xE0, 0x44, 0x80, 0xF0, 0x22, 0x75, 0x56, 0x01, + 0xE4, 0xFD, 0xF5, 0x57, 0xAF, 0x35, 0xFE, 0xFC, +/*1D90*/0x12, 0x09, 0x15, 0x12, 0x1C, 0x9D, 0x12, 0x1E, + 0x7A, 0x12, 0x1C, 0x4C, 0xAF, 0x57, 0x7E, 0x00, +/*1DA0*/0xAD, 0x56, 0x7C, 0x00, 0x12, 0x04, 0x44, 0xAF, + 0x56, 0x7E, 0x00, 0x02, 0x11, 0xEE, 0x75, 0x56, +/*1DB0*/0x01, 0xE4, 0xFD, 0xF5, 0x57, 0xAF, 0x35, 0xFE, + 0xFC, 0x12, 0x09, 0x15, 0x12, 0x1C, 0x9D, 0x12, +/*1DC0*/0x1E, 0x7A, 0x12, 0x1C, 0x4C, 0xAF, 0x57, 0x7E, + 0x00, 0xAD, 0x56, 0x7C, 0x00, 0x12, 0x04, 0x44, +/*1DD0*/0xAF, 0x56, 0x7E, 0x00, 0x02, 0x11, 0xEE, 0xE4, + 0xF5, 0x16, 0x12, 0x0E, 0x44, 0xFE, 0xE5, 0x08, +/*1DE0*/0x44, 0x05, 0xFF, 0x12, 0x0E, 0x65, 0x8F, 0x82, + 0x8E, 0x83, 0xF0, 0x05, 0x16, 0xE5, 0x16, 0xC3, +/*1DF0*/0x94, 0x14, 0x40, 0xE6, 0xE5, 0x08, 0x12, 0x0E, + 0x2B, 0xE4, 0xF0, 0x22, 0xE4, 0xF5, 0x58, 0xF5, +/*1E00*/0x59, 0xF5, 0x5A, 0xFF, 0xFE, 0xAD, 0x58, 0xFC, + 0x12, 0x09, 0x15, 0x7F, 0x04, 0x7E, 0x00, 0xAD, +/*1E10*/0x58, 0x7C, 0x00, 0x12, 0x09, 0x15, 0x7F, 0x02, + 0x7E, 0x00, 0xAD, 0x58, 0x7C, 0x00, 0x02, 0x09, +/*1E20*/0x15, 0xE5, 0x3C, 0x25, 0x3E, 0xFC, 0xE5, 0x42, + 0x24, 0x00, 0xFB, 0xE4, 0x33, 0xFA, 0xEC, 0xC3, +/*1E30*/0x9B, 0xEA, 0x12, 0x07, 0x6B, 0x40, 0x0B, 0x8C, + 0x42, 0xE5, 0x3D, 0x25, 0x3F, 0xF5, 0x41, 0x8F, +/*1E40*/0x40, 0x22, 0x12, 0x09, 0x0B, 0x22, 0x74, 0x84, + 0xF5, 0x18, 0x85, 0x08, 0x19, 0x85, 0x19, 0x82, +/*1E50*/0x85, 0x18, 0x83, 0xE0, 0x54, 0x7F, 0xF0, 0xE0, + 0x44, 0x80, 0xF0, 0xE0, 0x44, 0x80, 0xF0, 0x22, +/*1E60*/0xEF, 0x4E, 0x70, 0x0B, 0x12, 0x07, 0x2A, 0x75, + 0x83, 0xD2, 0xE0, 0x54, 0xDF, 0xF0, 0x22, 0x12, +/*1E70*/0x07, 0x2A, 0x75, 0x83, 0xD2, 0xE0, 0x44, 0x20, + 0xF0, 0x22, 0x75, 0x58, 0x01, 0x90, 0x07, 0x26, +/*1E80*/0x12, 0x07, 0x35, 0xE0, 0x54, 0x3F, 0xF5, 0x41, + 0x12, 0x07, 0x32, 0xE0, 0x54, 0x3F, 0xF5, 0x40, +/*1E90*/0x22, 0x75, 0x56, 0x02, 0xE4, 0xF5, 0x57, 0x12, + 0x1D, 0xFC, 0xAF, 0x57, 0x7E, 0x00, 0xAD, 0x56, +/*1EA0*/0x7C, 0x00, 0x02, 0x04, 0x44, 0xE4, 0xF5, 0x42, + 0xF5, 0x41, 0xF5, 0x40, 0xF5, 0x38, 0xF5, 0x39, +/*1EB0*/0xF5, 0x3A, 0x22, 0xEF, 0x54, 0x07, 0xFF, 0xE5, + 0xF9, 0x54, 0xF8, 0x4F, 0xF5, 0xF9, 0x22, 0x7F, +/*1EC0*/0x01, 0xE4, 0xFE, 0x0F, 0x0E, 0xBE, 0xFF, 0xFB, + 0x22, 0x01, 0x20, 0x00, 0x01, 0x04, 0x20, 0x00, +/*1ED0*/0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/*1EE0*/0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/*1EF0*/0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/*1F00*/0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/*1F10*/0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/*1F20*/0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/*1F30*/0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/*1F40*/0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/*1F50*/0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/*1F60*/0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/*1F70*/0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/*1F80*/0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/*1F90*/0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/*1FA0*/0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/*1FB0*/0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/*1FC0*/0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/*1FD0*/0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/*1FE0*/0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/*1FF0*/0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x20, 0x11, 0x00, 0x04, 0x20, 0x00, 0x81 +}; diff --git a/gpxe/src/drivers/infiniband/mlx_bitops.h b/gpxe/src/drivers/infiniband/mlx_bitops.h new file mode 100644 index 0000000..ec57d7b --- /dev/null +++ b/gpxe/src/drivers/infiniband/mlx_bitops.h @@ -0,0 +1,209 @@ +#ifndef _MLX_BITOPS_H +#define _MLX_BITOPS_H + +/* + * Copyright (C) 2007 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * @file + * + * Mellanox bit operations + * + */ + +/* Datatype used to represent a bit in the Mellanox autogenerated headers */ +typedef unsigned char pseudo_bit_t; + +/** + * Wrapper structure for pseudo_bit_t structures + * + * This structure provides a wrapper around the autogenerated + * pseudo_bit_t structures. It has the correct size, and also + * encapsulates type information about the underlying pseudo_bit_t + * structure, which allows the MLX_FILL etc. macros to work without + * requiring explicit type information. + */ +#define MLX_DECLARE_STRUCT( _structure ) \ + _structure { \ + union { \ + uint8_t bytes[ sizeof ( struct _structure ## _st ) / 8 ]; \ + uint32_t dwords[ sizeof ( struct _structure ## _st ) / 32 ]; \ + struct _structure ## _st *dummy[0]; \ + } u; \ + } + +/** Get pseudo_bit_t structure type from wrapper structure pointer */ +#define MLX_PSEUDO_STRUCT( _ptr ) \ + typeof ( *((_ptr)->u.dummy[0]) ) + +/** Bit offset of a field within a pseudo_bit_t structure */ +#define MLX_BIT_OFFSET( _structure_st, _field ) \ + offsetof ( _structure_st, _field ) + +/** Dword offset of a field within a pseudo_bit_t structure */ +#define MLX_DWORD_OFFSET( _structure_st, _field ) \ + ( MLX_BIT_OFFSET ( _structure_st, _field ) / 32 ) + +/** Dword bit offset of a field within a pseudo_bit_t structure + * + * Yes, using mod-32 would work, but would lose the check for the + * error of specifying a mismatched field name and dword index. + */ +#define MLX_DWORD_BIT_OFFSET( _structure_st, _index, _field ) \ + ( MLX_BIT_OFFSET ( _structure_st, _field ) - ( 32 * (_index) ) ) + +/** Bit width of a field within a pseudo_bit_t structure */ +#define MLX_BIT_WIDTH( _structure_st, _field ) \ + sizeof ( ( ( _structure_st * ) NULL )->_field ) + +/** Bit mask for a field within a pseudo_bit_t structure */ +#define MLX_BIT_MASK( _structure_st, _field ) \ + ( ( ~( ( uint32_t ) 0 ) ) >> \ + ( 32 - MLX_BIT_WIDTH ( _structure_st, _field ) ) ) + +/* + * Assemble native-endian dword from named fields and values + * + */ + +#define MLX_ASSEMBLE_1( _structure_st, _index, _field, _value ) \ + ( (_value) << MLX_DWORD_BIT_OFFSET ( _structure_st, _index, _field ) ) + +#define MLX_ASSEMBLE_2( _structure_st, _index, _field, _value, ... ) \ + ( MLX_ASSEMBLE_1 ( _structure_st, _index, _field, _value ) | \ + MLX_ASSEMBLE_1 ( _structure_st, _index, __VA_ARGS__ ) ) + +#define MLX_ASSEMBLE_3( _structure_st, _index, _field, _value, ... ) \ + ( MLX_ASSEMBLE_1 ( _structure_st, _index, _field, _value ) | \ + MLX_ASSEMBLE_2 ( _structure_st, _index, __VA_ARGS__ ) ) + +#define MLX_ASSEMBLE_4( _structure_st, _index, _field, _value, ... ) \ + ( MLX_ASSEMBLE_1 ( _structure_st, _index, _field, _value ) | \ + MLX_ASSEMBLE_3 ( _structure_st, _index, __VA_ARGS__ ) ) + +#define MLX_ASSEMBLE_5( _structure_st, _index, _field, _value, ... ) \ + ( MLX_ASSEMBLE_1 ( _structure_st, _index, _field, _value ) | \ + MLX_ASSEMBLE_4 ( _structure_st, _index, __VA_ARGS__ ) ) + +#define MLX_ASSEMBLE_6( _structure_st, _index, _field, _value, ... ) \ + ( MLX_ASSEMBLE_1 ( _structure_st, _index, _field, _value ) | \ + MLX_ASSEMBLE_5 ( _structure_st, _index, __VA_ARGS__ ) ) + +/* + * Build native-endian (positive) dword bitmasks from named fields + * + */ + +#define MLX_MASK_1( _structure_st, _index, _field ) \ + ( MLX_BIT_MASK ( _structure_st, _field ) << \ + MLX_DWORD_BIT_OFFSET ( _structure_st, _index, _field ) ) + +#define MLX_MASK_2( _structure_st, _index, _field, ... ) \ + ( MLX_MASK_1 ( _structure_st, _index, _field ) | \ + MLX_MASK_1 ( _structure_st, _index, __VA_ARGS__ ) ) + +#define MLX_MASK_3( _structure_st, _index, _field, ... ) \ + ( MLX_MASK_1 ( _structure_st, _index, _field ) | \ + MLX_MASK_2 ( _structure_st, _index, __VA_ARGS__ ) ) + +#define MLX_MASK_4( _structure_st, _index, _field, ... ) \ + ( MLX_MASK_1 ( _structure_st, _index, _field ) | \ + MLX_MASK_3 ( _structure_st, _index, __VA_ARGS__ ) ) + +#define MLX_MASK_5( _structure_st, _index, _field, ... ) \ + ( MLX_MASK_1 ( _structure_st, _index, _field ) | \ + MLX_MASK_4 ( _structure_st, _index, __VA_ARGS__ ) ) + +#define MLX_MASK_6( _structure_st, _index, _field, ... ) \ + ( MLX_MASK_1 ( _structure_st, _index, _field ) | \ + MLX_MASK_5 ( _structure_st, _index, __VA_ARGS__ ) ) + +/* + * Populate big-endian dwords from named fields and values + * + */ + +#define MLX_FILL( _ptr, _index, _assembled ) \ + do { \ + uint32_t *__ptr = &(_ptr)->u.dwords[(_index)]; \ + uint32_t __assembled = (_assembled); \ + *__ptr = cpu_to_be32 ( __assembled ); \ + } while ( 0 ) + +#define MLX_FILL_1( _ptr, _index, ... ) \ + MLX_FILL ( _ptr, _index, MLX_ASSEMBLE_1 ( MLX_PSEUDO_STRUCT ( _ptr ),\ + _index, __VA_ARGS__ ) ) + +#define MLX_FILL_2( _ptr, _index, ... ) \ + MLX_FILL ( _ptr, _index, MLX_ASSEMBLE_2 ( MLX_PSEUDO_STRUCT ( _ptr ),\ + _index, __VA_ARGS__ ) ) + +#define MLX_FILL_3( _ptr, _index, ... ) \ + MLX_FILL ( _ptr, _index, MLX_ASSEMBLE_3 ( MLX_PSEUDO_STRUCT ( _ptr ),\ + _index, __VA_ARGS__ ) ) + +#define MLX_FILL_4( _ptr, _index, ... ) \ + MLX_FILL ( _ptr, _index, MLX_ASSEMBLE_4 ( MLX_PSEUDO_STRUCT ( _ptr ),\ + _index, __VA_ARGS__ ) ) + +#define MLX_FILL_5( _ptr, _index, ... ) \ + MLX_FILL ( _ptr, _index, MLX_ASSEMBLE_5 ( MLX_PSEUDO_STRUCT ( _ptr ),\ + _index, __VA_ARGS__ ) ) + +#define MLX_FILL_6( _ptr, _index, ... ) \ + MLX_FILL ( _ptr, _index, MLX_ASSEMBLE_6 ( MLX_PSEUDO_STRUCT ( _ptr ),\ + _index, __VA_ARGS__ ) ) + +/* + * Modify big-endian dword using named field and value + * + */ + +#define MLX_SET( _ptr, _field, _value ) \ + do { \ + unsigned int __index = \ + MLX_DWORD_OFFSET ( MLX_PSEUDO_STRUCT ( _ptr ), _field ); \ + uint32_t *__ptr = &(_ptr)->u.dwords[__index]; \ + uint32_t __value = be32_to_cpu ( *__ptr ); \ + __value &= ~( MLX_MASK_1 ( MLX_PSEUDO_STRUCT ( _ptr ), \ + __index, _field ) ); \ + __value |= MLX_ASSEMBLE_1 ( MLX_PSEUDO_STRUCT ( _ptr ), \ + __index, _field, _value ); \ + *__ptr = cpu_to_be32 ( __value ); \ + } while ( 0 ) + +/* + * Extract value of named field + * + */ + +#define MLX_GET( _ptr, _field ) \ + ( { \ + unsigned int __index = \ + MLX_DWORD_OFFSET ( MLX_PSEUDO_STRUCT ( _ptr ), _field ); \ + uint32_t *__ptr = &(_ptr)->u.dwords[__index]; \ + uint32_t __value = be32_to_cpu ( *__ptr ); \ + __value >>= \ + MLX_DWORD_BIT_OFFSET ( MLX_PSEUDO_STRUCT ( _ptr ), \ + __index, _field ); \ + __value &= \ + MLX_BIT_MASK ( MLX_PSEUDO_STRUCT ( _ptr ), _field ); \ + __value; \ + } ) + +#endif /* _MLX_BITOPS_H */ diff --git a/gpxe/src/drivers/infiniband/qib_7220_regs.h b/gpxe/src/drivers/infiniband/qib_7220_regs.h new file mode 100644 index 0000000..0dd3c53 --- /dev/null +++ b/gpxe/src/drivers/infiniband/qib_7220_regs.h @@ -0,0 +1,1763 @@ +/* + * Copyright (c) 2008 QLogic Corporation. All rights reserved. + * + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * This file is mechanically generated. Any hand-edits will be lost. + * If not now, soon. + */ + +/* This file has been further processed by ./drivers/infiniband/qib_genbits.pl */ + + +#define QIB_7220_Revision_offset 0x00000000UL +struct QIB_7220_Revision_pb { + pseudo_bit_t R_ChipRevMinor[8]; + pseudo_bit_t R_ChipRevMajor[8]; + pseudo_bit_t R_Arch[8]; + pseudo_bit_t R_SW[8]; + pseudo_bit_t BoardID[8]; + pseudo_bit_t R_Palldium_Revcode[22]; + pseudo_bit_t R_Palladium[1]; + pseudo_bit_t R_Simulator[1]; +}; +struct QIB_7220_Revision { + PSEUDO_BIT_STRUCT ( struct QIB_7220_Revision_pb ); +}; + +#define QIB_7220_Control_offset 0x00000008UL +struct QIB_7220_Control_pb { + pseudo_bit_t SyncReset[1]; + pseudo_bit_t FreezeMode[1]; + pseudo_bit_t LinkEn[1]; + pseudo_bit_t PCIERetryBufDiagEn[1]; + pseudo_bit_t TxLatency[1]; + pseudo_bit_t Reserved[1]; + pseudo_bit_t PCIECplQDiagEn[1]; + pseudo_bit_t SyncResetExceptPcieIRAMRST[1]; + pseudo_bit_t _unused_0[56]; +}; +struct QIB_7220_Control { + PSEUDO_BIT_STRUCT ( struct QIB_7220_Control_pb ); +}; + +#define QIB_7220_PageAlign_offset 0x00000010UL + +#define QIB_7220_PortCnt_offset 0x00000018UL + +#define QIB_7220_DbgPortSel_offset 0x00000020UL +struct QIB_7220_DbgPortSel_pb { + pseudo_bit_t NibbleSel0[4]; + pseudo_bit_t NibbleSel1[4]; + pseudo_bit_t NibbleSel2[4]; + pseudo_bit_t NibbleSel3[4]; + pseudo_bit_t NibbleSel4[4]; + pseudo_bit_t NibbleSel5[4]; + pseudo_bit_t NibbleSel6[4]; + pseudo_bit_t NibbleSel7[4]; + pseudo_bit_t SrcMuxSel[14]; + pseudo_bit_t DbgClkPortSel[5]; + pseudo_bit_t EnDbgPort[1]; + pseudo_bit_t EnEnhancedDebugMode[1]; + pseudo_bit_t EnhMode_SrcMuxSelIndex[10]; + pseudo_bit_t EnhMode_SrcMuxSelWrEn[1]; +}; +struct QIB_7220_DbgPortSel { + PSEUDO_BIT_STRUCT ( struct QIB_7220_DbgPortSel_pb ); +}; + +#define QIB_7220_DebugSigsIntSel_offset 0x00000028UL +struct QIB_7220_DebugSigsIntSel_pb { + pseudo_bit_t debug_port_sel_pcs_pipe_lane07[3]; + pseudo_bit_t debug_port_sel_pcs_pipe_lane815[3]; + pseudo_bit_t debug_port_sel_pcs_sdout[1]; + pseudo_bit_t debug_port_sel_pcs_symlock_elfifo_lane[4]; + pseudo_bit_t debug_port_sel_pcs_rxdet_encdec_lane[4]; + pseudo_bit_t debug_port_sel_pcie_rx_tx[1]; + pseudo_bit_t debug_port_sel_xgxs[4]; + pseudo_bit_t debug_port_sel_epb_pcie[1]; + pseudo_bit_t _unused_0[43]; +}; +struct QIB_7220_DebugSigsIntSel { + PSEUDO_BIT_STRUCT ( struct QIB_7220_DebugSigsIntSel_pb ); +}; + +#define QIB_7220_SendRegBase_offset 0x00000030UL + +#define QIB_7220_UserRegBase_offset 0x00000038UL + +#define QIB_7220_CntrRegBase_offset 0x00000040UL + +#define QIB_7220_Scratch_offset 0x00000048UL + +#define QIB_7220_REG_000050_offset 0x00000050UL + +#define QIB_7220_IntBlocked_offset 0x00000060UL +struct QIB_7220_IntBlocked_pb { + pseudo_bit_t RcvAvail0IntBlocked[1]; + pseudo_bit_t RcvAvail1IntBlocked[1]; + pseudo_bit_t RcvAvail2IntBlocked[1]; + pseudo_bit_t RcvAvail3IntBlocked[1]; + pseudo_bit_t RcvAvail4IntBlocked[1]; + pseudo_bit_t RcvAvail5IntBlocked[1]; + pseudo_bit_t RcvAvail6IntBlocked[1]; + pseudo_bit_t RcvAvail7IntBlocked[1]; + pseudo_bit_t RcvAvail8IntBlocked[1]; + pseudo_bit_t RcvAvail9IntBlocked[1]; + pseudo_bit_t RcvAvail10IntBlocked[1]; + pseudo_bit_t RcvAvail11IntBlocked[1]; + pseudo_bit_t RcvAvail12IntBlocked[1]; + pseudo_bit_t RcvAvail13IntBlocked[1]; + pseudo_bit_t RcvAvail14IntBlocked[1]; + pseudo_bit_t RcvAvail15IntBlocked[1]; + pseudo_bit_t RcvAvail16IntBlocked[1]; + pseudo_bit_t Reserved1[9]; + pseudo_bit_t JIntBlocked[1]; + pseudo_bit_t IBSerdesTrimDoneIntBlocked[1]; + pseudo_bit_t assertGPIOIntBlocked[1]; + pseudo_bit_t PioBufAvailIntBlocked[1]; + pseudo_bit_t PioSetIntBlocked[1]; + pseudo_bit_t ErrorIntBlocked[1]; + pseudo_bit_t RcvUrg0IntBlocked[1]; + pseudo_bit_t RcvUrg1IntBlocked[1]; + pseudo_bit_t RcvUrg2IntBlocked[1]; + pseudo_bit_t RcvUrg3IntBlocked[1]; + pseudo_bit_t RcvUrg4IntBlocked[1]; + pseudo_bit_t RcvUrg5IntBlocked[1]; + pseudo_bit_t RcvUrg6IntBlocked[1]; + pseudo_bit_t RcvUrg7IntBlocked[1]; + pseudo_bit_t RcvUrg8IntBlocked[1]; + pseudo_bit_t RcvUrg9IntBlocked[1]; + pseudo_bit_t RcvUrg10IntBlocked[1]; + pseudo_bit_t RcvUrg11IntBlocked[1]; + pseudo_bit_t RcvUrg12IntBlocked[1]; + pseudo_bit_t RcvUrg13IntBlocked[1]; + pseudo_bit_t RcvUrg14IntBlocked[1]; + pseudo_bit_t RcvUrg15IntBlocked[1]; + pseudo_bit_t RcvUrg16IntBlocked[1]; + pseudo_bit_t Reserved[13]; + pseudo_bit_t SDmaDisabledBlocked[1]; + pseudo_bit_t SDmaIntBlocked[1]; +}; +struct QIB_7220_IntBlocked { + PSEUDO_BIT_STRUCT ( struct QIB_7220_IntBlocked_pb ); +}; + +#define QIB_7220_IntMask_offset 0x00000068UL +struct QIB_7220_IntMask_pb { + pseudo_bit_t RcvAvail0IntMask[1]; + pseudo_bit_t RcvAvail1IntMask[1]; + pseudo_bit_t RcvAvail2IntMask[1]; + pseudo_bit_t RcvAvail3IntMask[1]; + pseudo_bit_t RcvAvail4IntMask[1]; + pseudo_bit_t RcvAvail5IntMask[1]; + pseudo_bit_t RcvAvail6IntMask[1]; + pseudo_bit_t RcvAvail7IntMask[1]; + pseudo_bit_t RcvAvail8IntMask[1]; + pseudo_bit_t RcvAvail9IntMask[1]; + pseudo_bit_t RcvAvail10IntMask[1]; + pseudo_bit_t RcvAvail11IntMask[1]; + pseudo_bit_t RcvAvail12IntMask[1]; + pseudo_bit_t RcvAvail13IntMask[1]; + pseudo_bit_t RcvAvail14IntMask[1]; + pseudo_bit_t RcvAvail15IntMask[1]; + pseudo_bit_t RcvAvail16IntMask[1]; + pseudo_bit_t Reserved1[9]; + pseudo_bit_t JIntMask[1]; + pseudo_bit_t IBSerdesTrimDoneIntMask[1]; + pseudo_bit_t assertGPIOIntMask[1]; + pseudo_bit_t PioBufAvailIntMask[1]; + pseudo_bit_t PioSetIntMask[1]; + pseudo_bit_t ErrorIntMask[1]; + pseudo_bit_t RcvUrg0IntMask[1]; + pseudo_bit_t RcvUrg1IntMask[1]; + pseudo_bit_t RcvUrg2IntMask[1]; + pseudo_bit_t RcvUrg3IntMask[1]; + pseudo_bit_t RcvUrg4IntMask[1]; + pseudo_bit_t RcvUrg5IntMask[1]; + pseudo_bit_t RcvUrg6IntMask[1]; + pseudo_bit_t RcvUrg7IntMask[1]; + pseudo_bit_t RcvUrg8IntMask[1]; + pseudo_bit_t RcvUrg9IntMask[1]; + pseudo_bit_t RcvUrg10IntMask[1]; + pseudo_bit_t RcvUrg11IntMask[1]; + pseudo_bit_t RcvUrg12IntMask[1]; + pseudo_bit_t RcvUrg13IntMask[1]; + pseudo_bit_t RcvUrg14IntMask[1]; + pseudo_bit_t RcvUrg15IntMask[1]; + pseudo_bit_t RcvUrg16IntMask[1]; + pseudo_bit_t Reserved[13]; + pseudo_bit_t SDmaDisabledMasked[1]; + pseudo_bit_t SDmaIntMask[1]; +}; +struct QIB_7220_IntMask { + PSEUDO_BIT_STRUCT ( struct QIB_7220_IntMask_pb ); +}; + +#define QIB_7220_IntStatus_offset 0x00000070UL +struct QIB_7220_IntStatus_pb { + pseudo_bit_t RcvAvail0[1]; + pseudo_bit_t RcvAvail1[1]; + pseudo_bit_t RcvAvail2[1]; + pseudo_bit_t RcvAvail3[1]; + pseudo_bit_t RcvAvail4[1]; + pseudo_bit_t RcvAvail5[1]; + pseudo_bit_t RcvAvail6[1]; + pseudo_bit_t RcvAvail7[1]; + pseudo_bit_t RcvAvail8[1]; + pseudo_bit_t RcvAvail9[1]; + pseudo_bit_t RcvAvail10[1]; + pseudo_bit_t RcvAvail11[1]; + pseudo_bit_t RcvAvail12[1]; + pseudo_bit_t RcvAvail13[1]; + pseudo_bit_t RcvAvail14[1]; + pseudo_bit_t RcvAvail15[1]; + pseudo_bit_t RcvAvail16[1]; + pseudo_bit_t Reserved1[9]; + pseudo_bit_t JInt[1]; + pseudo_bit_t IBSerdesTrimDone[1]; + pseudo_bit_t assertGPIO[1]; + pseudo_bit_t PioBufAvail[1]; + pseudo_bit_t PioSent[1]; + pseudo_bit_t Error[1]; + pseudo_bit_t RcvUrg0[1]; + pseudo_bit_t RcvUrg1[1]; + pseudo_bit_t RcvUrg2[1]; + pseudo_bit_t RcvUrg3[1]; + pseudo_bit_t RcvUrg4[1]; + pseudo_bit_t RcvUrg5[1]; + pseudo_bit_t RcvUrg6[1]; + pseudo_bit_t RcvUrg7[1]; + pseudo_bit_t RcvUrg8[1]; + pseudo_bit_t RcvUrg9[1]; + pseudo_bit_t RcvUrg10[1]; + pseudo_bit_t RcvUrg11[1]; + pseudo_bit_t RcvUrg12[1]; + pseudo_bit_t RcvUrg13[1]; + pseudo_bit_t RcvUrg14[1]; + pseudo_bit_t RcvUrg15[1]; + pseudo_bit_t RcvUrg16[1]; + pseudo_bit_t Reserved[13]; + pseudo_bit_t SDmaDisabled[1]; + pseudo_bit_t SDmaInt[1]; +}; +struct QIB_7220_IntStatus { + PSEUDO_BIT_STRUCT ( struct QIB_7220_IntStatus_pb ); +}; + +#define QIB_7220_IntClear_offset 0x00000078UL +struct QIB_7220_IntClear_pb { + pseudo_bit_t RcvAvail0IntClear[1]; + pseudo_bit_t RcvAvail1IntClear[1]; + pseudo_bit_t RcvAvail2IntClear[1]; + pseudo_bit_t RcvAvail3IntClear[1]; + pseudo_bit_t RcvAvail4IntClear[1]; + pseudo_bit_t RcvAvail5IntClear[1]; + pseudo_bit_t RcvAvail6IntClear[1]; + pseudo_bit_t RcvAvail7IntClear[1]; + pseudo_bit_t RcvAvail8IntClear[1]; + pseudo_bit_t RcvAvail9IntClear[1]; + pseudo_bit_t RcvAvail10IntClear[1]; + pseudo_bit_t RcvAvail11IntClear[1]; + pseudo_bit_t RcvAvail12IntClear[1]; + pseudo_bit_t RcvAvail13IntClear[1]; + pseudo_bit_t RcvAvail14IntClear[1]; + pseudo_bit_t RcvAvail15IntClear[1]; + pseudo_bit_t RcvAvail16IntClear[1]; + pseudo_bit_t Reserved1[9]; + pseudo_bit_t JIntClear[1]; + pseudo_bit_t IBSerdesTrimDoneClear[1]; + pseudo_bit_t assertGPIOIntClear[1]; + pseudo_bit_t PioBufAvailIntClear[1]; + pseudo_bit_t PioSetIntClear[1]; + pseudo_bit_t ErrorIntClear[1]; + pseudo_bit_t RcvUrg0IntClear[1]; + pseudo_bit_t RcvUrg1IntClear[1]; + pseudo_bit_t RcvUrg2IntClear[1]; + pseudo_bit_t RcvUrg3IntClear[1]; + pseudo_bit_t RcvUrg4IntClear[1]; + pseudo_bit_t RcvUrg5IntClear[1]; + pseudo_bit_t RcvUrg6IntClear[1]; + pseudo_bit_t RcvUrg7IntClear[1]; + pseudo_bit_t RcvUrg8IntClear[1]; + pseudo_bit_t RcvUrg9IntClear[1]; + pseudo_bit_t RcvUrg10IntClear[1]; + pseudo_bit_t RcvUrg11IntClear[1]; + pseudo_bit_t RcvUrg12IntClear[1]; + pseudo_bit_t RcvUrg13IntClear[1]; + pseudo_bit_t RcvUrg14IntClear[1]; + pseudo_bit_t RcvUrg15IntClear[1]; + pseudo_bit_t RcvUrg16IntClear[1]; + pseudo_bit_t Reserved[13]; + pseudo_bit_t SDmaDisabledClear[1]; + pseudo_bit_t SDmaIntClear[1]; +}; +struct QIB_7220_IntClear { + PSEUDO_BIT_STRUCT ( struct QIB_7220_IntClear_pb ); +}; + +#define QIB_7220_ErrMask_offset 0x00000080UL +struct QIB_7220_ErrMask_pb { + pseudo_bit_t RcvFormatErrMask[1]; + pseudo_bit_t RcvVCRCErrMask[1]; + pseudo_bit_t RcvICRCErrMask[1]; + pseudo_bit_t RcvMinPktLenErrMask[1]; + pseudo_bit_t RcvMaxPktLenErrMask[1]; + pseudo_bit_t RcvLongPktLenErrMask[1]; + pseudo_bit_t RcvShortPktLenErrMask[1]; + pseudo_bit_t RcvUnexpectedCharErrMask[1]; + pseudo_bit_t RcvUnsupportedVLErrMask[1]; + pseudo_bit_t RcvEBPErrMask[1]; + pseudo_bit_t RcvIBFlowErrMask[1]; + pseudo_bit_t RcvBadVersionErrMask[1]; + pseudo_bit_t RcvEgrFullErrMask[1]; + pseudo_bit_t RcvHdrFullErrMask[1]; + pseudo_bit_t RcvBadTidErrMask[1]; + pseudo_bit_t RcvHdrLenErrMask[1]; + pseudo_bit_t RcvHdrErrMask[1]; + pseudo_bit_t RcvIBLostLinkErrMask[1]; + pseudo_bit_t Reserved1[9]; + pseudo_bit_t SendSpecialTriggerErrMask[1]; + pseudo_bit_t SDmaDisabledErrMask[1]; + pseudo_bit_t SendMinPktLenErrMask[1]; + pseudo_bit_t SendMaxPktLenErrMask[1]; + pseudo_bit_t SendUnderRunErrMask[1]; + pseudo_bit_t SendPktLenErrMask[1]; + pseudo_bit_t SendDroppedSmpPktErrMask[1]; + pseudo_bit_t SendDroppedDataPktErrMask[1]; + pseudo_bit_t SendPioArmLaunchErrMask[1]; + pseudo_bit_t SendUnexpectedPktNumErrMask[1]; + pseudo_bit_t SendUnsupportedVLErrMask[1]; + pseudo_bit_t SendBufMisuseErrMask[1]; + pseudo_bit_t SDmaGenMismatchErrMask[1]; + pseudo_bit_t SDmaOutOfBoundErrMask[1]; + pseudo_bit_t SDmaTailOutOfBoundErrMask[1]; + pseudo_bit_t SDmaBaseErrMask[1]; + pseudo_bit_t SDma1stDescErrMask[1]; + pseudo_bit_t SDmaRpyTagErrMask[1]; + pseudo_bit_t SDmaDwEnErrMask[1]; + pseudo_bit_t SDmaMissingDwErrMask[1]; + pseudo_bit_t SDmaUnexpDataErrMask[1]; + pseudo_bit_t IBStatusChangedMask[1]; + pseudo_bit_t InvalidAddrErrMask[1]; + pseudo_bit_t ResetNegatedMask[1]; + pseudo_bit_t HardwareErrMask[1]; + pseudo_bit_t SDmaDescAddrMisalignErrMask[1]; + pseudo_bit_t InvalidEEPCmdMask[1]; + pseudo_bit_t Reserved[10]; +}; +struct QIB_7220_ErrMask { + PSEUDO_BIT_STRUCT ( struct QIB_7220_ErrMask_pb ); +}; + +#define QIB_7220_ErrStatus_offset 0x00000088UL +struct QIB_7220_ErrStatus_pb { + pseudo_bit_t RcvFormatErr[1]; + pseudo_bit_t RcvVCRCErr[1]; + pseudo_bit_t RcvICRCErr[1]; + pseudo_bit_t RcvMinPktLenErr[1]; + pseudo_bit_t RcvMaxPktLenErr[1]; + pseudo_bit_t RcvLongPktLenErr[1]; + pseudo_bit_t RcvShortPktLenErr[1]; + pseudo_bit_t RcvUnexpectedCharErr[1]; + pseudo_bit_t RcvUnsupportedVLErr[1]; + pseudo_bit_t RcvEBPErr[1]; + pseudo_bit_t RcvIBFlowErr[1]; + pseudo_bit_t RcvBadVersionErr[1]; + pseudo_bit_t RcvEgrFullErr[1]; + pseudo_bit_t RcvHdrFullErr[1]; + pseudo_bit_t RcvBadTidErr[1]; + pseudo_bit_t RcvHdrLenErr[1]; + pseudo_bit_t RcvHdrErr[1]; + pseudo_bit_t RcvIBLostLinkErr[1]; + pseudo_bit_t Reserved1[9]; + pseudo_bit_t SendSpecialTriggerErr[1]; + pseudo_bit_t SDmaDisabledErr[1]; + pseudo_bit_t SendMinPktLenErr[1]; + pseudo_bit_t SendMaxPktLenErr[1]; + pseudo_bit_t SendUnderRunErr[1]; + pseudo_bit_t SendPktLenErr[1]; + pseudo_bit_t SendDroppedSmpPktErr[1]; + pseudo_bit_t SendDroppedDataPktErr[1]; + pseudo_bit_t SendPioArmLaunchErr[1]; + pseudo_bit_t SendUnexpectedPktNumErr[1]; + pseudo_bit_t SendUnsupportedVLErr[1]; + pseudo_bit_t SendBufMisuseErr[1]; + pseudo_bit_t SDmaGenMismatchErr[1]; + pseudo_bit_t SDmaOutOfBoundErr[1]; + pseudo_bit_t SDmaTailOutOfBoundErr[1]; + pseudo_bit_t SDmaBaseErr[1]; + pseudo_bit_t SDma1stDescErr[1]; + pseudo_bit_t SDmaRpyTagErr[1]; + pseudo_bit_t SDmaDwEnErr[1]; + pseudo_bit_t SDmaMissingDwErr[1]; + pseudo_bit_t SDmaUnexpDataErr[1]; + pseudo_bit_t IBStatusChanged[1]; + pseudo_bit_t InvalidAddrErr[1]; + pseudo_bit_t ResetNegated[1]; + pseudo_bit_t HardwareErr[1]; + pseudo_bit_t SDmaDescAddrMisalignErr[1]; + pseudo_bit_t InvalidEEPCmdErr[1]; + pseudo_bit_t Reserved[10]; +}; +struct QIB_7220_ErrStatus { + PSEUDO_BIT_STRUCT ( struct QIB_7220_ErrStatus_pb ); +}; + +#define QIB_7220_ErrClear_offset 0x00000090UL +struct QIB_7220_ErrClear_pb { + pseudo_bit_t RcvFormatErrClear[1]; + pseudo_bit_t RcvVCRCErrClear[1]; + pseudo_bit_t RcvICRCErrClear[1]; + pseudo_bit_t RcvMinPktLenErrClear[1]; + pseudo_bit_t RcvMaxPktLenErrClear[1]; + pseudo_bit_t RcvLongPktLenErrClear[1]; + pseudo_bit_t RcvShortPktLenErrClear[1]; + pseudo_bit_t RcvUnexpectedCharErrClear[1]; + pseudo_bit_t RcvUnsupportedVLErrClear[1]; + pseudo_bit_t RcvEBPErrClear[1]; + pseudo_bit_t RcvIBFlowErrClear[1]; + pseudo_bit_t RcvBadVersionErrClear[1]; + pseudo_bit_t RcvEgrFullErrClear[1]; + pseudo_bit_t RcvHdrFullErrClear[1]; + pseudo_bit_t RcvBadTidErrClear[1]; + pseudo_bit_t RcvHdrLenErrClear[1]; + pseudo_bit_t RcvHdrErrClear[1]; + pseudo_bit_t RcvIBLostLinkErrClear[1]; + pseudo_bit_t Reserved1[9]; + pseudo_bit_t SendSpecialTriggerErrClear[1]; + pseudo_bit_t SDmaDisabledErrClear[1]; + pseudo_bit_t SendMinPktLenErrClear[1]; + pseudo_bit_t SendMaxPktLenErrClear[1]; + pseudo_bit_t SendUnderRunErrClear[1]; + pseudo_bit_t SendPktLenErrClear[1]; + pseudo_bit_t SendDroppedSmpPktErrClear[1]; + pseudo_bit_t SendDroppedDataPktErrClear[1]; + pseudo_bit_t SendPioArmLaunchErrClear[1]; + pseudo_bit_t SendUnexpectedPktNumErrClear[1]; + pseudo_bit_t SendUnsupportedVLErrClear[1]; + pseudo_bit_t SendBufMisuseErrClear[1]; + pseudo_bit_t SDmaGenMismatchErrClear[1]; + pseudo_bit_t SDmaOutOfBoundErrClear[1]; + pseudo_bit_t SDmaTailOutOfBoundErrClear[1]; + pseudo_bit_t SDmaBaseErrClear[1]; + pseudo_bit_t SDma1stDescErrClear[1]; + pseudo_bit_t SDmaRpyTagErrClear[1]; + pseudo_bit_t SDmaDwEnErrClear[1]; + pseudo_bit_t SDmaMissingDwErrClear[1]; + pseudo_bit_t SDmaUnexpDataErrClear[1]; + pseudo_bit_t IBStatusChangedClear[1]; + pseudo_bit_t InvalidAddrErrClear[1]; + pseudo_bit_t ResetNegatedClear[1]; + pseudo_bit_t HardwareErrClear[1]; + pseudo_bit_t SDmaDescAddrMisalignErrClear[1]; + pseudo_bit_t InvalidEEPCmdErrClear[1]; + pseudo_bit_t Reserved[10]; +}; +struct QIB_7220_ErrClear { + PSEUDO_BIT_STRUCT ( struct QIB_7220_ErrClear_pb ); +}; + +#define QIB_7220_HwErrMask_offset 0x00000098UL +struct QIB_7220_HwErrMask_pb { + pseudo_bit_t PCIeMemParityErrMask[8]; + pseudo_bit_t Reserved3[20]; + pseudo_bit_t SDmaMemReadErrMask[1]; + pseudo_bit_t PoisonedTLPMask[1]; + pseudo_bit_t PcieCplTimeoutMask[1]; + pseudo_bit_t PCIeBusParityErrMask[3]; + pseudo_bit_t Reserved2[2]; + pseudo_bit_t PCIEOct0_uC_MemoryParityErrMask[1]; + pseudo_bit_t PCIEOct1_uC_MemoryParityErrMask[1]; + pseudo_bit_t IB_uC_MemoryParityErrMask[1]; + pseudo_bit_t DDSRXEQMemoryParityErrMask[1]; + pseudo_bit_t TXEMemParityErrMask[4]; + pseudo_bit_t RXEMemParityErrMask[7]; + pseudo_bit_t Reserved1[3]; + pseudo_bit_t PowerOnBISTFailedMask[1]; + pseudo_bit_t Reserved[1]; + pseudo_bit_t PCIESerdesQ0PClkNotDetectMask[1]; + pseudo_bit_t PCIESerdesQ1PClkNotDetectMask[1]; + pseudo_bit_t PCIESerdesQ2PClkNotDetectMask[1]; + pseudo_bit_t PCIESerdesQ3PClkNotDetectMask[1]; + pseudo_bit_t IBSerdesPClkNotDetectMask[1]; + pseudo_bit_t Clk_uC_PLLNotLockedMask[1]; + pseudo_bit_t IBCBusToSPCParityErrMask[1]; + pseudo_bit_t IBCBusFromSPCParityErrMask[1]; +}; +struct QIB_7220_HwErrMask { + PSEUDO_BIT_STRUCT ( struct QIB_7220_HwErrMask_pb ); +}; + +#define QIB_7220_HwErrStatus_offset 0x000000a0UL +struct QIB_7220_HwErrStatus_pb { + pseudo_bit_t PCIeMemParity[8]; + pseudo_bit_t Reserved3[20]; + pseudo_bit_t SDmaMemReadErr[1]; + pseudo_bit_t PoisenedTLP[1]; + pseudo_bit_t PcieCplTimeout[1]; + pseudo_bit_t PCIeBusParity[3]; + pseudo_bit_t Reserved2[2]; + pseudo_bit_t PCIE_uC_Oct0MemoryParityErr[1]; + pseudo_bit_t PCIE_uC_Oct1MemoryParityErr[1]; + pseudo_bit_t IB_uC_MemoryParityErr[1]; + pseudo_bit_t DDSRXEQMemoryParityErr[1]; + pseudo_bit_t TXEMemParity[4]; + pseudo_bit_t RXEMemParity[7]; + pseudo_bit_t Reserved1[3]; + pseudo_bit_t PowerOnBISTFailed[1]; + pseudo_bit_t Reserved[1]; + pseudo_bit_t PCIESerdesQ0PClkNotDetect[1]; + pseudo_bit_t PCIESerdesQ1PClkNotDetect[1]; + pseudo_bit_t PCIESerdesQ2PClkNotDetect[1]; + pseudo_bit_t PCIESerdesQ3PClkNotDetect[1]; + pseudo_bit_t IBSerdesPClkNotDetect[1]; + pseudo_bit_t Clk_uC_PLLNotLocked[1]; + pseudo_bit_t IBCBusToSPCParityErr[1]; + pseudo_bit_t IBCBusFromSPCParityErr[1]; +}; +struct QIB_7220_HwErrStatus { + PSEUDO_BIT_STRUCT ( struct QIB_7220_HwErrStatus_pb ); +}; + +#define QIB_7220_HwErrClear_offset 0x000000a8UL +struct QIB_7220_HwErrClear_pb { + pseudo_bit_t PCIeMemParityClr[8]; + pseudo_bit_t Reserved3[20]; + pseudo_bit_t SDmaMemReadErrClear[1]; + pseudo_bit_t PoisonedTLPClear[1]; + pseudo_bit_t PcieCplTimeoutClear[1]; + pseudo_bit_t PCIeBusParityClr[3]; + pseudo_bit_t Reserved2[2]; + pseudo_bit_t PCIE_uC_Oct0MemoryParityErrClear[1]; + pseudo_bit_t PCIE_uC_Oct1MemoryParityErrClear[1]; + pseudo_bit_t IB_uC_MemoryParityErrClear[1]; + pseudo_bit_t DDSRXEQMemoryParityErrClear[1]; + pseudo_bit_t TXEMemParityClear[4]; + pseudo_bit_t RXEMemParityClear[7]; + pseudo_bit_t Reserved1[3]; + pseudo_bit_t PowerOnBISTFailedClear[1]; + pseudo_bit_t Reserved[1]; + pseudo_bit_t PCIESerdesQ0PClkNotDetectClear[1]; + pseudo_bit_t PCIESerdesQ1PClkNotDetectClear[1]; + pseudo_bit_t PCIESerdesQ2PClkNotDetectClear[1]; + pseudo_bit_t PCIESerdesQ3PClkNotDetectClear[1]; + pseudo_bit_t IBSerdesPClkNotDetectClear[1]; + pseudo_bit_t Clk_uC_PLLNotLockedClear[1]; + pseudo_bit_t IBCBusToSPCparityErrClear[1]; + pseudo_bit_t IBCBusFromSPCParityErrClear[1]; +}; +struct QIB_7220_HwErrClear { + PSEUDO_BIT_STRUCT ( struct QIB_7220_HwErrClear_pb ); +}; + +#define QIB_7220_HwDiagCtrl_offset 0x000000b0UL +struct QIB_7220_HwDiagCtrl_pb { + pseudo_bit_t forcePCIeMemParity[8]; + pseudo_bit_t Reserved2[23]; + pseudo_bit_t forcePCIeBusParity[4]; + pseudo_bit_t Reserved1[1]; + pseudo_bit_t ForcePCIE_uC_Oct0MemoryParityErr[1]; + pseudo_bit_t ForcePCIE_uC_Oct1MemoryParityErr[1]; + pseudo_bit_t ForceIB_uC_MemoryParityErr[1]; + pseudo_bit_t ForceDDSRXEQMemoryParityErr[1]; + pseudo_bit_t ForceTxMemparityErr[4]; + pseudo_bit_t ForceRxMemParityErr[7]; + pseudo_bit_t Reserved[9]; + pseudo_bit_t CounterDisable[1]; + pseudo_bit_t CounterWrEnable[1]; + pseudo_bit_t ForceIBCBusToSPCParityErr[1]; + pseudo_bit_t ForceIBCBusFromSPCParityErr[1]; +}; +struct QIB_7220_HwDiagCtrl { + PSEUDO_BIT_STRUCT ( struct QIB_7220_HwDiagCtrl_pb ); +}; + +#define QIB_7220_REG_0000B8_offset 0x000000b8UL + +#define QIB_7220_IBCStatus_offset 0x000000c0UL +struct QIB_7220_IBCStatus_pb { + pseudo_bit_t LinkTrainingState[5]; + pseudo_bit_t LinkState[3]; + pseudo_bit_t LinkSpeedActive[1]; + pseudo_bit_t LinkWidthActive[1]; + pseudo_bit_t DDS_RXEQ_FAIL[1]; + pseudo_bit_t IB_SERDES_TRIM_DONE[1]; + pseudo_bit_t IBRxLaneReversed[1]; + pseudo_bit_t IBTxLaneReversed[1]; + pseudo_bit_t Reserved[16]; + pseudo_bit_t TxReady[1]; + pseudo_bit_t TxCreditOk[1]; + pseudo_bit_t _unused_0[32]; +}; +struct QIB_7220_IBCStatus { + PSEUDO_BIT_STRUCT ( struct QIB_7220_IBCStatus_pb ); +}; + +#define QIB_7220_IBCCtrl_offset 0x000000c8UL +struct QIB_7220_IBCCtrl_pb { + pseudo_bit_t FlowCtrlPeriod[8]; + pseudo_bit_t FlowCtrlWaterMark[8]; + pseudo_bit_t LinkInitCmd[3]; + pseudo_bit_t LinkCmd[2]; + pseudo_bit_t MaxPktLen[11]; + pseudo_bit_t PhyerrThreshold[4]; + pseudo_bit_t OverrunThreshold[4]; + pseudo_bit_t CreditScale[3]; + pseudo_bit_t Reserved[19]; + pseudo_bit_t LinkDownDefaultState[1]; + pseudo_bit_t Loopback[1]; +}; +struct QIB_7220_IBCCtrl { + PSEUDO_BIT_STRUCT ( struct QIB_7220_IBCCtrl_pb ); +}; + +#define QIB_7220_EXTStatus_offset 0x000000d0UL +struct QIB_7220_EXTStatus_pb { + pseudo_bit_t Reserved2[14]; + pseudo_bit_t MemBISTEndTest[1]; + pseudo_bit_t MemBISTDisabled[1]; + pseudo_bit_t Reserved1[16]; + pseudo_bit_t Reserved[16]; + pseudo_bit_t GPIOIn[16]; +}; +struct QIB_7220_EXTStatus { + PSEUDO_BIT_STRUCT ( struct QIB_7220_EXTStatus_pb ); +}; + +#define QIB_7220_EXTCtrl_offset 0x000000d8UL +struct QIB_7220_EXTCtrl_pb { + pseudo_bit_t LEDGblErrRedOff[1]; + pseudo_bit_t LEDGblOkGreenOn[1]; + pseudo_bit_t LEDPriPortYellowOn[1]; + pseudo_bit_t LEDPriPortGreenOn[1]; + pseudo_bit_t Reserved[28]; + pseudo_bit_t GPIOInvert[16]; + pseudo_bit_t GPIOOe[16]; +}; +struct QIB_7220_EXTCtrl { + PSEUDO_BIT_STRUCT ( struct QIB_7220_EXTCtrl_pb ); +}; + +#define QIB_7220_GPIOOut_offset 0x000000e0UL + +#define QIB_7220_GPIOMask_offset 0x000000e8UL + +#define QIB_7220_GPIOStatus_offset 0x000000f0UL + +#define QIB_7220_GPIOClear_offset 0x000000f8UL + +#define QIB_7220_RcvCtrl_offset 0x00000100UL +struct QIB_7220_RcvCtrl_pb { + pseudo_bit_t PortEnable[17]; + pseudo_bit_t IntrAvail[17]; + pseudo_bit_t RcvPartitionKeyDisable[1]; + pseudo_bit_t TailUpd[1]; + pseudo_bit_t PortCfg[2]; + pseudo_bit_t RcvQPMapEnable[1]; + pseudo_bit_t Reserved[25]; +}; +struct QIB_7220_RcvCtrl { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvCtrl_pb ); +}; + +#define QIB_7220_RcvBTHQP_offset 0x00000108UL +struct QIB_7220_RcvBTHQP_pb { + pseudo_bit_t RcvBTHQP[24]; + pseudo_bit_t Reserved[8]; + pseudo_bit_t _unused_0[32]; +}; +struct QIB_7220_RcvBTHQP { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvBTHQP_pb ); +}; + +#define QIB_7220_RcvHdrSize_offset 0x00000110UL + +#define QIB_7220_RcvHdrCnt_offset 0x00000118UL + +#define QIB_7220_RcvHdrEntSize_offset 0x00000120UL + +#define QIB_7220_RcvTIDBase_offset 0x00000128UL + +#define QIB_7220_RcvTIDCnt_offset 0x00000130UL + +#define QIB_7220_RcvEgrBase_offset 0x00000138UL + +#define QIB_7220_RcvEgrCnt_offset 0x00000140UL + +#define QIB_7220_RcvBufBase_offset 0x00000148UL + +#define QIB_7220_RcvBufSize_offset 0x00000150UL + +#define QIB_7220_RxIntMemBase_offset 0x00000158UL + +#define QIB_7220_RxIntMemSize_offset 0x00000160UL + +#define QIB_7220_RcvPartitionKey_offset 0x00000168UL + +#define QIB_7220_RcvQPMulticastPort_offset 0x00000170UL +struct QIB_7220_RcvQPMulticastPort_pb { + pseudo_bit_t RcvQpMcPort[5]; + pseudo_bit_t Reserved[59]; +}; +struct QIB_7220_RcvQPMulticastPort { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvQPMulticastPort_pb ); +}; + +#define QIB_7220_RcvPktLEDCnt_offset 0x00000178UL +struct QIB_7220_RcvPktLEDCnt_pb { + pseudo_bit_t OFFperiod[32]; + pseudo_bit_t ONperiod[32]; +}; +struct QIB_7220_RcvPktLEDCnt { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvPktLEDCnt_pb ); +}; + +#define QIB_7220_IBCDDRCtrl_offset 0x00000180UL +struct QIB_7220_IBCDDRCtrl_pb { + pseudo_bit_t IB_ENHANCED_MODE[1]; + pseudo_bit_t SD_SPEED[1]; + pseudo_bit_t SD_SPEED_SDR[1]; + pseudo_bit_t SD_SPEED_DDR[1]; + pseudo_bit_t SD_SPEED_QDR[1]; + pseudo_bit_t IB_NUM_CHANNELS[2]; + pseudo_bit_t IB_POLARITY_REV_SUPP[1]; + pseudo_bit_t IB_LANE_REV_SUPPORTED[1]; + pseudo_bit_t SD_RX_EQUAL_ENABLE[1]; + pseudo_bit_t SD_ADD_ENB[1]; + pseudo_bit_t SD_DDSV[1]; + pseudo_bit_t SD_DDS[4]; + pseudo_bit_t HRTBT_ENB[1]; + pseudo_bit_t HRTBT_AUTO[1]; + pseudo_bit_t HRTBT_PORT[8]; + pseudo_bit_t HRTBT_REQ[1]; + pseudo_bit_t Reserved[5]; + pseudo_bit_t IB_DLID[16]; + pseudo_bit_t IB_DLID_MASK[16]; +}; +struct QIB_7220_IBCDDRCtrl { + PSEUDO_BIT_STRUCT ( struct QIB_7220_IBCDDRCtrl_pb ); +}; + +#define QIB_7220_HRTBT_GUID_offset 0x00000188UL + +#define QIB_7220_IB_SDTEST_IF_TX_offset 0x00000190UL +struct QIB_7220_IB_SDTEST_IF_TX_pb { + pseudo_bit_t TS_T_TX_VALID[1]; + pseudo_bit_t TS_3_TX_VALID[1]; + pseudo_bit_t Reserved1[9]; + pseudo_bit_t TS_TX_OPCODE[2]; + pseudo_bit_t TS_TX_SPEED[3]; + pseudo_bit_t Reserved[16]; + pseudo_bit_t TS_TX_TX_CFG[16]; + pseudo_bit_t TS_TX_RX_CFG[16]; +}; +struct QIB_7220_IB_SDTEST_IF_TX { + PSEUDO_BIT_STRUCT ( struct QIB_7220_IB_SDTEST_IF_TX_pb ); +}; + +#define QIB_7220_IB_SDTEST_IF_RX_offset 0x00000198UL +struct QIB_7220_IB_SDTEST_IF_RX_pb { + pseudo_bit_t TS_T_RX_VALID[1]; + pseudo_bit_t TS_3_RX_VALID[1]; + pseudo_bit_t Reserved[14]; + pseudo_bit_t TS_RX_A[8]; + pseudo_bit_t TS_RX_B[8]; + pseudo_bit_t TS_RX_TX_CFG[16]; + pseudo_bit_t TS_RX_RX_CFG[16]; +}; +struct QIB_7220_IB_SDTEST_IF_RX { + PSEUDO_BIT_STRUCT ( struct QIB_7220_IB_SDTEST_IF_RX_pb ); +}; + +#define QIB_7220_IBCDDRCtrl2_offset 0x000001a0UL +struct QIB_7220_IBCDDRCtrl2_pb { + pseudo_bit_t IB_FRONT_PORCH[5]; + pseudo_bit_t IB_BACK_PORCH[5]; + pseudo_bit_t _unused_0[54]; +}; +struct QIB_7220_IBCDDRCtrl2 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_IBCDDRCtrl2_pb ); +}; + +#define QIB_7220_IBCDDRStatus_offset 0x000001a8UL +struct QIB_7220_IBCDDRStatus_pb { + pseudo_bit_t LinkRoundTripLatency[26]; + pseudo_bit_t ReqDDSLocalFromRmt[4]; + pseudo_bit_t RxEqLocalDevice[2]; + pseudo_bit_t heartbeat_crosstalk[4]; + pseudo_bit_t heartbeat_timed_out[1]; + pseudo_bit_t _unused_0[27]; +}; +struct QIB_7220_IBCDDRStatus { + PSEUDO_BIT_STRUCT ( struct QIB_7220_IBCDDRStatus_pb ); +}; + +#define QIB_7220_JIntReload_offset 0x000001b0UL +struct QIB_7220_JIntReload_pb { + pseudo_bit_t J_reload[16]; + pseudo_bit_t J_limit_reload[16]; + pseudo_bit_t _unused_0[32]; +}; +struct QIB_7220_JIntReload { + PSEUDO_BIT_STRUCT ( struct QIB_7220_JIntReload_pb ); +}; + +#define QIB_7220_IBNCModeCtrl_offset 0x000001b8UL +struct QIB_7220_IBNCModeCtrl_pb { + pseudo_bit_t TSMEnable_send_TS1[1]; + pseudo_bit_t TSMEnable_send_TS2[1]; + pseudo_bit_t TSMEnable_ignore_TSM_on_rx[1]; + pseudo_bit_t Reserved1[5]; + pseudo_bit_t TSMCode_TS1[9]; + pseudo_bit_t TSMCode_TS2[9]; + pseudo_bit_t Reserved[38]; +}; +struct QIB_7220_IBNCModeCtrl { + PSEUDO_BIT_STRUCT ( struct QIB_7220_IBNCModeCtrl_pb ); +}; + +#define QIB_7220_SendCtrl_offset 0x000001c0UL +struct QIB_7220_SendCtrl_pb { + pseudo_bit_t Abort[1]; + pseudo_bit_t SendIntBufAvail[1]; + pseudo_bit_t SendBufAvailUpd[1]; + pseudo_bit_t SPioEnable[1]; + pseudo_bit_t SSpecialTriggerEn[1]; + pseudo_bit_t Reserved2[4]; + pseudo_bit_t SDmaIntEnable[1]; + pseudo_bit_t SDmaSingleDescriptor[1]; + pseudo_bit_t SDmaEnable[1]; + pseudo_bit_t SDmaHalt[1]; + pseudo_bit_t Reserved1[3]; + pseudo_bit_t DisarmPIOBuf[8]; + pseudo_bit_t AvailUpdThld[5]; + pseudo_bit_t Reserved[2]; + pseudo_bit_t Disarm[1]; + pseudo_bit_t _unused_0[32]; +}; +struct QIB_7220_SendCtrl { + PSEUDO_BIT_STRUCT ( struct QIB_7220_SendCtrl_pb ); +}; + +#define QIB_7220_SendBufBase_offset 0x000001c8UL +struct QIB_7220_SendBufBase_pb { + pseudo_bit_t BaseAddr_SmallPIO[21]; + pseudo_bit_t Reserved1[11]; + pseudo_bit_t BaseAddr_LargePIO[21]; + pseudo_bit_t Reserved[11]; +}; +struct QIB_7220_SendBufBase { + PSEUDO_BIT_STRUCT ( struct QIB_7220_SendBufBase_pb ); +}; + +#define QIB_7220_SendBufSize_offset 0x000001d0UL +struct QIB_7220_SendBufSize_pb { + pseudo_bit_t Size_SmallPIO[12]; + pseudo_bit_t Reserved1[20]; + pseudo_bit_t Size_LargePIO[13]; + pseudo_bit_t Reserved[19]; +}; +struct QIB_7220_SendBufSize { + PSEUDO_BIT_STRUCT ( struct QIB_7220_SendBufSize_pb ); +}; + +#define QIB_7220_SendBufCnt_offset 0x000001d8UL +struct QIB_7220_SendBufCnt_pb { + pseudo_bit_t Num_SmallBuffers[9]; + pseudo_bit_t Reserved1[23]; + pseudo_bit_t Num_LargeBuffers[4]; + pseudo_bit_t Reserved[28]; +}; +struct QIB_7220_SendBufCnt { + PSEUDO_BIT_STRUCT ( struct QIB_7220_SendBufCnt_pb ); +}; + +#define QIB_7220_SendBufAvailAddr_offset 0x000001e0UL +struct QIB_7220_SendBufAvailAddr_pb { + pseudo_bit_t Reserved[6]; + pseudo_bit_t SendBufAvailAddr[34]; + pseudo_bit_t _unused_0[24]; +}; +struct QIB_7220_SendBufAvailAddr { + PSEUDO_BIT_STRUCT ( struct QIB_7220_SendBufAvailAddr_pb ); +}; + +#define QIB_7220_TxIntMemBase_offset 0x000001e8UL + +#define QIB_7220_TxIntMemSize_offset 0x000001f0UL + +#define QIB_7220_SendDmaBase_offset 0x000001f8UL +struct QIB_7220_SendDmaBase_pb { + pseudo_bit_t SendDmaBase[48]; + pseudo_bit_t Reserved[16]; +}; +struct QIB_7220_SendDmaBase { + PSEUDO_BIT_STRUCT ( struct QIB_7220_SendDmaBase_pb ); +}; + +#define QIB_7220_SendDmaLenGen_offset 0x00000200UL +struct QIB_7220_SendDmaLenGen_pb { + pseudo_bit_t Length[16]; + pseudo_bit_t Generation[3]; + pseudo_bit_t Reserved[45]; +}; +struct QIB_7220_SendDmaLenGen { + PSEUDO_BIT_STRUCT ( struct QIB_7220_SendDmaLenGen_pb ); +}; + +#define QIB_7220_SendDmaTail_offset 0x00000208UL +struct QIB_7220_SendDmaTail_pb { + pseudo_bit_t SendDmaTail[16]; + pseudo_bit_t Reserved[48]; +}; +struct QIB_7220_SendDmaTail { + PSEUDO_BIT_STRUCT ( struct QIB_7220_SendDmaTail_pb ); +}; + +#define QIB_7220_SendDmaHead_offset 0x00000210UL +struct QIB_7220_SendDmaHead_pb { + pseudo_bit_t SendDmaHead[16]; + pseudo_bit_t Reserved1[16]; + pseudo_bit_t InternalSendDmaHead[16]; + pseudo_bit_t Reserved[16]; +}; +struct QIB_7220_SendDmaHead { + PSEUDO_BIT_STRUCT ( struct QIB_7220_SendDmaHead_pb ); +}; + +#define QIB_7220_SendDmaHeadAddr_offset 0x00000218UL +struct QIB_7220_SendDmaHeadAddr_pb { + pseudo_bit_t SendDmaHeadAddr[48]; + pseudo_bit_t Reserved[16]; +}; +struct QIB_7220_SendDmaHeadAddr { + PSEUDO_BIT_STRUCT ( struct QIB_7220_SendDmaHeadAddr_pb ); +}; + +#define QIB_7220_SendDmaBufMask0_offset 0x00000220UL +struct QIB_7220_SendDmaBufMask0_pb { + pseudo_bit_t BufMask_63_0[0]; + pseudo_bit_t _unused_0[64]; +}; +struct QIB_7220_SendDmaBufMask0 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_SendDmaBufMask0_pb ); +}; + +#define QIB_7220_SendDmaStatus_offset 0x00000238UL +struct QIB_7220_SendDmaStatus_pb { + pseudo_bit_t SplFifoDescIndex[16]; + pseudo_bit_t SplFifoBufNum[8]; + pseudo_bit_t SplFifoFull[1]; + pseudo_bit_t SplFifoEmpty[1]; + pseudo_bit_t SplFifoDisarmed[1]; + pseudo_bit_t SplFifoReadyToGo[1]; + pseudo_bit_t ScbFetchDescFlag[1]; + pseudo_bit_t ScbEntryValid[1]; + pseudo_bit_t ScbEmpty[1]; + pseudo_bit_t ScbFull[1]; + pseudo_bit_t RpyTag_7_0[8]; + pseudo_bit_t RpyLowAddr_6_0[7]; + pseudo_bit_t ScbDescIndex_13_0[14]; + pseudo_bit_t InternalSDmaEnable[1]; + pseudo_bit_t AbortInProg[1]; + pseudo_bit_t ScoreBoardDrainInProg[1]; +}; +struct QIB_7220_SendDmaStatus { + PSEUDO_BIT_STRUCT ( struct QIB_7220_SendDmaStatus_pb ); +}; + +#define QIB_7220_SendBufErr0_offset 0x00000240UL +struct QIB_7220_SendBufErr0_pb { + pseudo_bit_t SendBufErr_63_0[0]; + pseudo_bit_t _unused_0[64]; +}; +struct QIB_7220_SendBufErr0 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_SendBufErr0_pb ); +}; + +#define QIB_7220_REG_000258_offset 0x00000258UL + +#define QIB_7220_AvailUpdCount_offset 0x00000268UL +struct QIB_7220_AvailUpdCount_pb { + pseudo_bit_t AvailUpdCount[5]; + pseudo_bit_t _unused_0[59]; +}; +struct QIB_7220_AvailUpdCount { + PSEUDO_BIT_STRUCT ( struct QIB_7220_AvailUpdCount_pb ); +}; + +#define QIB_7220_RcvHdrAddr0_offset 0x00000270UL +struct QIB_7220_RcvHdrAddr0_pb { + pseudo_bit_t Reserved[2]; + pseudo_bit_t RcvHdrAddr0[38]; + pseudo_bit_t _unused_0[24]; +}; +struct QIB_7220_RcvHdrAddr0 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvHdrAddr0_pb ); +}; + +#define QIB_7220_REG_0002F8_offset 0x000002f8UL + +#define QIB_7220_RcvHdrTailAddr0_offset 0x00000300UL +struct QIB_7220_RcvHdrTailAddr0_pb { + pseudo_bit_t Reserved[2]; + pseudo_bit_t RcvHdrTailAddr0[38]; + pseudo_bit_t _unused_0[24]; +}; +struct QIB_7220_RcvHdrTailAddr0 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvHdrTailAddr0_pb ); +}; + +#define QIB_7220_REG_000388_offset 0x00000388UL + +#define QIB_7220_ibsd_epb_access_ctrl_offset 0x000003c0UL +struct QIB_7220_ibsd_epb_access_ctrl_pb { + pseudo_bit_t sw_ib_epb_req[1]; + pseudo_bit_t Reserved[7]; + pseudo_bit_t sw_ib_epb_req_granted[1]; + pseudo_bit_t _unused_0[55]; +}; +struct QIB_7220_ibsd_epb_access_ctrl { + PSEUDO_BIT_STRUCT ( struct QIB_7220_ibsd_epb_access_ctrl_pb ); +}; + +#define QIB_7220_ibsd_epb_transaction_reg_offset 0x000003c8UL +struct QIB_7220_ibsd_epb_transaction_reg_pb { + pseudo_bit_t ib_epb_data[8]; + pseudo_bit_t ib_epb_address[15]; + pseudo_bit_t Reserved2[1]; + pseudo_bit_t ib_epb_read_write[1]; + pseudo_bit_t ib_epb_cs[2]; + pseudo_bit_t Reserved1[1]; + pseudo_bit_t mem_data_parity[1]; + pseudo_bit_t Reserved[1]; + pseudo_bit_t ib_epb_req_error[1]; + pseudo_bit_t ib_epb_rdy[1]; + pseudo_bit_t _unused_0[32]; +}; +struct QIB_7220_ibsd_epb_transaction_reg { + PSEUDO_BIT_STRUCT ( struct QIB_7220_ibsd_epb_transaction_reg_pb ); +}; + +#define QIB_7220_REG_0003D0_offset 0x000003d0UL + +#define QIB_7220_XGXSCfg_offset 0x000003d8UL +struct QIB_7220_XGXSCfg_pb { + pseudo_bit_t tx_rx_reset[1]; + pseudo_bit_t Reserved2[1]; + pseudo_bit_t xcv_reset[1]; + pseudo_bit_t Reserved1[6]; + pseudo_bit_t link_sync_mask[10]; + pseudo_bit_t Reserved[44]; + pseudo_bit_t sel_link_down_for_fctrl_lane_sync_reset[1]; +}; +struct QIB_7220_XGXSCfg { + PSEUDO_BIT_STRUCT ( struct QIB_7220_XGXSCfg_pb ); +}; + +#define QIB_7220_IBSerDesCtrl_offset 0x000003e0UL +struct QIB_7220_IBSerDesCtrl_pb { + pseudo_bit_t ResetIB_uC_Core[1]; + pseudo_bit_t Reserved2[7]; + pseudo_bit_t NumSerDesRegsToWrForDDS[5]; + pseudo_bit_t NumSerDesRegsToWrForRXEQ[5]; + pseudo_bit_t Reserved1[14]; + pseudo_bit_t TXINV[1]; + pseudo_bit_t RXINV[1]; + pseudo_bit_t RXIDLE[1]; + pseudo_bit_t TWC[1]; + pseudo_bit_t TXOBPD[1]; + pseudo_bit_t PLLM[3]; + pseudo_bit_t PLLN[2]; + pseudo_bit_t CKSEL_uC[2]; + pseudo_bit_t INT_uC[1]; + pseudo_bit_t Reserved[19]; +}; +struct QIB_7220_IBSerDesCtrl { + PSEUDO_BIT_STRUCT ( struct QIB_7220_IBSerDesCtrl_pb ); +}; + +#define QIB_7220_EEPCtlStat_offset 0x000003e8UL +struct QIB_7220_EEPCtlStat_pb { + pseudo_bit_t EPAccEn[2]; + pseudo_bit_t EPReset[1]; + pseudo_bit_t ByteProg[1]; + pseudo_bit_t PageMode[1]; + pseudo_bit_t LstDatWr[1]; + pseudo_bit_t CmdWrErr[1]; + pseudo_bit_t Reserved[24]; + pseudo_bit_t CtlrStat[1]; + pseudo_bit_t _unused_0[32]; +}; +struct QIB_7220_EEPCtlStat { + PSEUDO_BIT_STRUCT ( struct QIB_7220_EEPCtlStat_pb ); +}; + +#define QIB_7220_EEPAddrCmd_offset 0x000003f0UL +struct QIB_7220_EEPAddrCmd_pb { + pseudo_bit_t EPAddr[24]; + pseudo_bit_t EPCmd[8]; + pseudo_bit_t _unused_0[32]; +}; +struct QIB_7220_EEPAddrCmd { + PSEUDO_BIT_STRUCT ( struct QIB_7220_EEPAddrCmd_pb ); +}; + +#define QIB_7220_EEPData_offset 0x000003f8UL + +#define QIB_7220_pciesd_epb_access_ctrl_offset 0x00000400UL +struct QIB_7220_pciesd_epb_access_ctrl_pb { + pseudo_bit_t sw_pcie_epb_req[1]; + pseudo_bit_t sw_pcieepb_star_en[2]; + pseudo_bit_t Reserved[5]; + pseudo_bit_t sw_pcie_epb_req_granted[1]; + pseudo_bit_t _unused_0[55]; +}; +struct QIB_7220_pciesd_epb_access_ctrl { + PSEUDO_BIT_STRUCT ( struct QIB_7220_pciesd_epb_access_ctrl_pb ); +}; + +#define QIB_7220_pciesd_epb_transaction_reg_offset 0x00000408UL +struct QIB_7220_pciesd_epb_transaction_reg_pb { + pseudo_bit_t pcie_epb_data[8]; + pseudo_bit_t pcie_epb_address[15]; + pseudo_bit_t Reserved1[1]; + pseudo_bit_t pcie_epb_read_write[1]; + pseudo_bit_t pcie_epb_cs[3]; + pseudo_bit_t mem_data_parity[1]; + pseudo_bit_t Reserved[1]; + pseudo_bit_t pcie_epb_req_error[1]; + pseudo_bit_t pcie_epb_rdy[1]; + pseudo_bit_t _unused_0[32]; +}; +struct QIB_7220_pciesd_epb_transaction_reg { + PSEUDO_BIT_STRUCT ( struct QIB_7220_pciesd_epb_transaction_reg_pb ); +}; + +#define QIB_7220_efuse_control_reg_offset 0x00000410UL +struct QIB_7220_efuse_control_reg_pb { + pseudo_bit_t start_op[1]; + pseudo_bit_t operation[1]; + pseudo_bit_t read_valid[1]; + pseudo_bit_t req_error[1]; + pseudo_bit_t Reserved[27]; + pseudo_bit_t rdy[1]; + pseudo_bit_t _unused_0[32]; +}; +struct QIB_7220_efuse_control_reg { + PSEUDO_BIT_STRUCT ( struct QIB_7220_efuse_control_reg_pb ); +}; + +#define QIB_7220_efuse_rddata0_reg_offset 0x00000418UL + +#define QIB_7220_procmon_register_offset 0x00000438UL +struct QIB_7220_procmon_register_pb { + pseudo_bit_t interval_time[12]; + pseudo_bit_t Reserved1[2]; + pseudo_bit_t clear_counter[1]; + pseudo_bit_t start_counter[1]; + pseudo_bit_t procmon_count[9]; + pseudo_bit_t Reserved[6]; + pseudo_bit_t procmon_count_valid[1]; + pseudo_bit_t _unused_0[32]; +}; +struct QIB_7220_procmon_register { + PSEUDO_BIT_STRUCT ( struct QIB_7220_procmon_register_pb ); +}; + +#define QIB_7220_PcieRbufTestReg0_offset 0x00000440UL + +#define QIB_7220_PcieRBufTestReg1_offset 0x00000448UL + +#define QIB_7220_SPC_JTAG_ACCESS_REG_offset 0x00000460UL +struct QIB_7220_SPC_JTAG_ACCESS_REG_pb { + pseudo_bit_t rdy[1]; + pseudo_bit_t tdo[1]; + pseudo_bit_t tdi[1]; + pseudo_bit_t opcode[2]; + pseudo_bit_t bist_en[5]; + pseudo_bit_t SPC_JTAG_ACCESS_EN[1]; + pseudo_bit_t _unused_0[53]; +}; +struct QIB_7220_SPC_JTAG_ACCESS_REG { + PSEUDO_BIT_STRUCT ( struct QIB_7220_SPC_JTAG_ACCESS_REG_pb ); +}; + +#define QIB_7220_LAControlReg_offset 0x00000468UL +struct QIB_7220_LAControlReg_pb { + pseudo_bit_t Finished[1]; + pseudo_bit_t Address[8]; + pseudo_bit_t Mode[2]; + pseudo_bit_t Delay[20]; + pseudo_bit_t Reserved[1]; + pseudo_bit_t _unused_0[32]; +}; +struct QIB_7220_LAControlReg { + PSEUDO_BIT_STRUCT ( struct QIB_7220_LAControlReg_pb ); +}; + +#define QIB_7220_GPIODebugSelReg_offset 0x00000470UL +struct QIB_7220_GPIODebugSelReg_pb { + pseudo_bit_t GPIOSourceSelDebug[16]; + pseudo_bit_t SelPulse[16]; + pseudo_bit_t _unused_0[32]; +}; +struct QIB_7220_GPIODebugSelReg { + PSEUDO_BIT_STRUCT ( struct QIB_7220_GPIODebugSelReg_pb ); +}; + +#define QIB_7220_DebugPortValueReg_offset 0x00000478UL + +#define QIB_7220_SendDmaBufUsed0_offset 0x00000480UL +struct QIB_7220_SendDmaBufUsed0_pb { + pseudo_bit_t BufUsed_63_0[0]; + pseudo_bit_t _unused_0[64]; +}; +struct QIB_7220_SendDmaBufUsed0 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_SendDmaBufUsed0_pb ); +}; + +#define QIB_7220_SendDmaReqTagUsed_offset 0x00000498UL +struct QIB_7220_SendDmaReqTagUsed_pb { + pseudo_bit_t ReqTagUsed_7_0[8]; + pseudo_bit_t _unused_0[8]; + pseudo_bit_t Reserved[48]; + pseudo_bit_t _unused_1[8]; +}; +struct QIB_7220_SendDmaReqTagUsed { + PSEUDO_BIT_STRUCT ( struct QIB_7220_SendDmaReqTagUsed_pb ); +}; + +#define QIB_7220_efuse_pgm_data0_offset 0x000004a0UL + +#define QIB_7220_MEM_0004B0_offset 0x000004b0UL + +#define QIB_7220_SerDes_DDSRXEQ0_offset 0x00000500UL +struct QIB_7220_SerDes_DDSRXEQ0_pb { + pseudo_bit_t element_num[4]; + pseudo_bit_t reg_addr[6]; + pseudo_bit_t _unused_0[54]; +}; +struct QIB_7220_SerDes_DDSRXEQ0 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_SerDes_DDSRXEQ0_pb ); +}; + +#define QIB_7220_MEM_0005F0_offset 0x000005f0UL + +#define QIB_7220_LAMemory_offset 0x00000600UL + +#define QIB_7220_MEM_0007F0_offset 0x000007f0UL + +#define QIB_7220_SendBufAvail0_offset 0x00001000UL +struct QIB_7220_SendBufAvail0_pb { + pseudo_bit_t SendBuf_31_0[0]; + pseudo_bit_t _unused_0[64]; +}; +struct QIB_7220_SendBufAvail0 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_SendBufAvail0_pb ); +}; + +#define QIB_7220_MEM_001028_offset 0x00001028UL + +#define QIB_7220_LBIntCnt_offset 0x00013000UL + +#define QIB_7220_LBFlowStallCnt_offset 0x00013008UL + +#define QIB_7220_TxSDmaDescCnt_offset 0x00013010UL + +#define QIB_7220_TxUnsupVLErrCnt_offset 0x00013018UL + +#define QIB_7220_TxDataPktCnt_offset 0x00013020UL + +#define QIB_7220_TxFlowPktCnt_offset 0x00013028UL + +#define QIB_7220_TxDwordCnt_offset 0x00013030UL + +#define QIB_7220_TxLenErrCnt_offset 0x00013038UL + +#define QIB_7220_TxMaxMinLenErrCnt_offset 0x00013040UL + +#define QIB_7220_TxUnderrunCnt_offset 0x00013048UL + +#define QIB_7220_TxFlowStallCnt_offset 0x00013050UL + +#define QIB_7220_TxDroppedPktCnt_offset 0x00013058UL + +#define QIB_7220_RxDroppedPktCnt_offset 0x00013060UL + +#define QIB_7220_RxDataPktCnt_offset 0x00013068UL + +#define QIB_7220_RxFlowPktCnt_offset 0x00013070UL + +#define QIB_7220_RxDwordCnt_offset 0x00013078UL + +#define QIB_7220_RxLenErrCnt_offset 0x00013080UL + +#define QIB_7220_RxMaxMinLenErrCnt_offset 0x00013088UL + +#define QIB_7220_RxICRCErrCnt_offset 0x00013090UL + +#define QIB_7220_RxVCRCErrCnt_offset 0x00013098UL + +#define QIB_7220_RxFlowCtrlViolCnt_offset 0x000130a0UL + +#define QIB_7220_RxVersionErrCnt_offset 0x000130a8UL + +#define QIB_7220_RxLinkMalformCnt_offset 0x000130b0UL + +#define QIB_7220_RxEBPCnt_offset 0x000130b8UL + +#define QIB_7220_RxLPCRCErrCnt_offset 0x000130c0UL + +#define QIB_7220_RxBufOvflCnt_offset 0x000130c8UL + +#define QIB_7220_RxTIDFullErrCnt_offset 0x000130d0UL + +#define QIB_7220_RxTIDValidErrCnt_offset 0x000130d8UL + +#define QIB_7220_RxPKeyMismatchCnt_offset 0x000130e0UL + +#define QIB_7220_RxP0HdrEgrOvflCnt_offset 0x000130e8UL + +#define QIB_7220_IBStatusChangeCnt_offset 0x00013170UL + +#define QIB_7220_IBLinkErrRecoveryCnt_offset 0x00013178UL + +#define QIB_7220_IBLinkDownedCnt_offset 0x00013180UL + +#define QIB_7220_IBSymbolErrCnt_offset 0x00013188UL + +#define QIB_7220_RxVL15DroppedPktCnt_offset 0x00013190UL + +#define QIB_7220_RxOtherLocalPhyErrCnt_offset 0x00013198UL + +#define QIB_7220_PcieRetryBufDiagQwordCnt_offset 0x000131a0UL + +#define QIB_7220_ExcessBufferOvflCnt_offset 0x000131a8UL + +#define QIB_7220_LocalLinkIntegrityErrCnt_offset 0x000131b0UL + +#define QIB_7220_RxVlErrCnt_offset 0x000131b8UL + +#define QIB_7220_RxDlidFltrCnt_offset 0x000131c0UL + +#define QIB_7220_CNT_0131C8_offset 0x000131c8UL + +#define QIB_7220_PSStat_offset 0x00013200UL + +#define QIB_7220_PSStart_offset 0x00013208UL + +#define QIB_7220_PSInterval_offset 0x00013210UL + +#define QIB_7220_PSRcvDataCount_offset 0x00013218UL + +#define QIB_7220_PSRcvPktsCount_offset 0x00013220UL + +#define QIB_7220_PSXmitDataCount_offset 0x00013228UL + +#define QIB_7220_PSXmitPktsCount_offset 0x00013230UL + +#define QIB_7220_PSXmitWaitCount_offset 0x00013238UL + +#define QIB_7220_CNT_013240_offset 0x00013240UL + +#define QIB_7220_RcvEgrArray_offset 0x00014000UL + +#define QIB_7220_MEM_038000_offset 0x00038000UL + +#define QIB_7220_RcvTIDArray0_offset 0x00053000UL + +#define QIB_7220_PIOLaunchFIFO_offset 0x00064000UL + +#define QIB_7220_MEM_064480_offset 0x00064480UL + +#define QIB_7220_SendPIOpbcCache_offset 0x00064800UL + +#define QIB_7220_MEM_064C80_offset 0x00064c80UL + +#define QIB_7220_PreLaunchFIFO_offset 0x00065000UL + +#define QIB_7220_MEM_065080_offset 0x00065080UL + +#define QIB_7220_ScoreBoard_offset 0x00065400UL + +#define QIB_7220_MEM_065440_offset 0x00065440UL + +#define QIB_7220_DescriptorFIFO_offset 0x00065800UL + +#define QIB_7220_MEM_065880_offset 0x00065880UL + +#define QIB_7220_RcvBuf1_offset 0x00072000UL + +#define QIB_7220_MEM_074800_offset 0x00074800UL + +#define QIB_7220_RcvBuf2_offset 0x00075000UL + +#define QIB_7220_MEM_076400_offset 0x00076400UL + +#define QIB_7220_RcvFlags_offset 0x00077000UL + +#define QIB_7220_MEM_078400_offset 0x00078400UL + +#define QIB_7220_RcvLookupBuf1_offset 0x00079000UL + +#define QIB_7220_MEM_07A400_offset 0x0007a400UL + +#define QIB_7220_RcvDMADatBuf_offset 0x0007b000UL + +#define QIB_7220_RcvDMAHdrBuf_offset 0x0007b800UL + +#define QIB_7220_MiscRXEIntMem_offset 0x0007c000UL + +#define QIB_7220_MEM_07D400_offset 0x0007d400UL + +#define QIB_7220_PCIERcvBuf_offset 0x00080000UL + +#define QIB_7220_PCIERetryBuf_offset 0x00084000UL + +#define QIB_7220_PCIERcvBufRdToWrAddr_offset 0x00088000UL + +#define QIB_7220_PCIECplBuf_offset 0x00090000UL + +#define QIB_7220_IBSerDesMappTable_offset 0x00094000UL + +#define QIB_7220_MEM_095000_offset 0x00095000UL + +#define QIB_7220_SendBuf0_MA_offset 0x00100000UL + +#define QIB_7220_MEM_1A0000_offset 0x001a0000UL + +#define QIB_7220_RcvHdrTail0_offset 0x00200000UL + +#define QIB_7220_RcvHdrHead0_offset 0x00200008UL +struct QIB_7220_RcvHdrHead0_pb { + pseudo_bit_t RcvHeadPointer[32]; + pseudo_bit_t counter[16]; + pseudo_bit_t Reserved[16]; +}; +struct QIB_7220_RcvHdrHead0 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvHdrHead0_pb ); +}; + +#define QIB_7220_RcvEgrIndexTail0_offset 0x00200010UL + +#define QIB_7220_RcvEgrIndexHead0_offset 0x00200018UL + +#define QIB_7220_MEM_200020_offset 0x00200020UL + +#define QIB_7220_RcvHdrTail1_offset 0x00210000UL + +#define QIB_7220_RcvHdrHead1_offset 0x00210008UL +struct QIB_7220_RcvHdrHead1_pb { + pseudo_bit_t RcvHeadPointer[32]; + pseudo_bit_t counter[16]; + pseudo_bit_t Reserved[16]; +}; +struct QIB_7220_RcvHdrHead1 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvHdrHead1_pb ); +}; + +#define QIB_7220_RcvEgrIndexTail1_offset 0x00210010UL + +#define QIB_7220_RcvEgrIndexHead1_offset 0x00210018UL + +#define QIB_7220_MEM_210020_offset 0x00210020UL + +#define QIB_7220_RcvHdrTail2_offset 0x00220000UL + +#define QIB_7220_RcvHdrHead2_offset 0x00220008UL +struct QIB_7220_RcvHdrHead2_pb { + pseudo_bit_t RcvHeadPointer[32]; + pseudo_bit_t counter[16]; + pseudo_bit_t Reserved[16]; +}; +struct QIB_7220_RcvHdrHead2 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvHdrHead2_pb ); +}; + +#define QIB_7220_RcvEgrIndexTail2_offset 0x00220010UL + +#define QIB_7220_RcvEgrIndexHead2_offset 0x00220018UL + +#define QIB_7220_MEM_220020_offset 0x00220020UL + +#define QIB_7220_RcvHdrTail3_offset 0x00230000UL + +#define QIB_7220_RcvHdrHead3_offset 0x00230008UL +struct QIB_7220_RcvHdrHead3_pb { + pseudo_bit_t RcvHeadPointer[32]; + pseudo_bit_t counter[16]; + pseudo_bit_t Reserved[16]; +}; +struct QIB_7220_RcvHdrHead3 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvHdrHead3_pb ); +}; + +#define QIB_7220_RcvEgrIndexTail3_offset 0x00230010UL + +#define QIB_7220_RcvEgrIndexHead3_offset 0x00230018UL + +#define QIB_7220_MEM_230020_offset 0x00230020UL + +#define QIB_7220_RcvHdrTail4_offset 0x00240000UL + +#define QIB_7220_RcvHdrHead4_offset 0x00240008UL +struct QIB_7220_RcvHdrHead4_pb { + pseudo_bit_t RcvHeadPointer[32]; + pseudo_bit_t counter[16]; + pseudo_bit_t Reserved[16]; +}; +struct QIB_7220_RcvHdrHead4 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvHdrHead4_pb ); +}; + +#define QIB_7220_RcvEgrIndexTail4_offset 0x00240010UL + +#define QIB_7220_RcvEgrIndexHead4_offset 0x00240018UL + +#define QIB_7220_MEM_240020_offset 0x00240020UL + +#define QIB_7220_RcvHdrTail5_offset 0x00250000UL + +#define QIB_7220_RcvHdrHead5_offset 0x00250008UL +struct QIB_7220_RcvHdrHead5_pb { + pseudo_bit_t RcvHeadPointer[32]; + pseudo_bit_t counter[16]; + pseudo_bit_t Reserved[16]; +}; +struct QIB_7220_RcvHdrHead5 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvHdrHead5_pb ); +}; + +#define QIB_7220_RcvEgrIndexTail5_offset 0x00250010UL + +#define QIB_7220_RcvEgrIndexHead5_offset 0x00250018UL + +#define QIB_7220_MEM_250020_offset 0x00250020UL + +#define QIB_7220_RcvHdrTail6_offset 0x00260000UL + +#define QIB_7220_RcvHdrHead6_offset 0x00260008UL +struct QIB_7220_RcvHdrHead6_pb { + pseudo_bit_t RcvHeadPointer[32]; + pseudo_bit_t counter[16]; + pseudo_bit_t Reserved[16]; +}; +struct QIB_7220_RcvHdrHead6 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvHdrHead6_pb ); +}; + +#define QIB_7220_RcvEgrIndexTail6_offset 0x00260010UL + +#define QIB_7220_RcvEgrIndexHead6_offset 0x00260018UL + +#define QIB_7220_MEM_260020_offset 0x00260020UL + +#define QIB_7220_RcvHdrTail7_offset 0x00270000UL + +#define QIB_7220_RcvHdrHead7_offset 0x00270008UL +struct QIB_7220_RcvHdrHead7_pb { + pseudo_bit_t RcvHeadPointer[32]; + pseudo_bit_t counter[16]; + pseudo_bit_t Reserved[16]; +}; +struct QIB_7220_RcvHdrHead7 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvHdrHead7_pb ); +}; + +#define QIB_7220_RcvEgrIndexTail7_offset 0x00270010UL + +#define QIB_7220_RcvEgrIndexHead7_offset 0x00270018UL + +#define QIB_7220_MEM_270020_offset 0x00270020UL + +#define QIB_7220_RcvHdrTail8_offset 0x00280000UL + +#define QIB_7220_RcvHdrHead8_offset 0x00280008UL +struct QIB_7220_RcvHdrHead8_pb { + pseudo_bit_t RcvHeadPointer[32]; + pseudo_bit_t counter[16]; + pseudo_bit_t Reserved[16]; +}; +struct QIB_7220_RcvHdrHead8 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvHdrHead8_pb ); +}; + +#define QIB_7220_RcvEgrIndexTail8_offset 0x00280010UL + +#define QIB_7220_RcvEgrIndexHead8_offset 0x00280018UL + +#define QIB_7220_MEM_280020_offset 0x00280020UL + +#define QIB_7220_RcvHdrTail9_offset 0x00290000UL + +#define QIB_7220_RcvHdrHead9_offset 0x00290008UL +struct QIB_7220_RcvHdrHead9_pb { + pseudo_bit_t RcvHeadPointer[32]; + pseudo_bit_t counter[16]; + pseudo_bit_t Reserved[16]; +}; +struct QIB_7220_RcvHdrHead9 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvHdrHead9_pb ); +}; + +#define QIB_7220_RcvEgrIndexTail9_offset 0x00290010UL + +#define QIB_7220_RcvEgrIndexHead9_offset 0x00290018UL + +#define QIB_7220_MEM_290020_offset 0x00290020UL + +#define QIB_7220_RcvHdrTail10_offset 0x002a0000UL + +#define QIB_7220_RcvHdrHead10_offset 0x002a0008UL +struct QIB_7220_RcvHdrHead10_pb { + pseudo_bit_t RcvHeadPointer[32]; + pseudo_bit_t counter[16]; + pseudo_bit_t Reserved[16]; +}; +struct QIB_7220_RcvHdrHead10 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvHdrHead10_pb ); +}; + +#define QIB_7220_RcvEgrIndexTail10_offset 0x002a0010UL + +#define QIB_7220_RcvEgrIndexHead10_offset 0x002a0018UL + +#define QIB_7220_MEM_2A0020_offset 0x002a0020UL + +#define QIB_7220_RcvHdrTail11_offset 0x002b0000UL + +#define QIB_7220_RcvHdrHead11_offset 0x002b0008UL +struct QIB_7220_RcvHdrHead11_pb { + pseudo_bit_t RcvHeadPointer[32]; + pseudo_bit_t counter[16]; + pseudo_bit_t Reserved[16]; +}; +struct QIB_7220_RcvHdrHead11 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvHdrHead11_pb ); +}; + +#define QIB_7220_RcvEgrIndexTail11_offset 0x002b0010UL + +#define QIB_7220_RcvEgrIndexHead11_offset 0x002b0018UL + +#define QIB_7220_MEM_2B0020_offset 0x002b0020UL + +#define QIB_7220_RcvHdrTail12_offset 0x002c0000UL + +#define QIB_7220_RcvHdrHead12_offset 0x002c0008UL +struct QIB_7220_RcvHdrHead12_pb { + pseudo_bit_t RcvHeadPointer[32]; + pseudo_bit_t counter[16]; + pseudo_bit_t Reserved[16]; +}; +struct QIB_7220_RcvHdrHead12 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvHdrHead12_pb ); +}; + +#define QIB_7220_RcvEgrIndexTail12_offset 0x002c0010UL + +#define QIB_7220_RcvEgrIndexHead12_offset 0x002c0018UL + +#define QIB_7220_MEM_2C0020_offset 0x002c0020UL + +#define QIB_7220_RcvHdrTail13_offset 0x002d0000UL + +#define QIB_7220_RcvHdrHead13_offset 0x002d0008UL +struct QIB_7220_RcvHdrHead13_pb { + pseudo_bit_t RcvHeadPointer[32]; + pseudo_bit_t counter[16]; + pseudo_bit_t Reserved[16]; +}; +struct QIB_7220_RcvHdrHead13 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvHdrHead13_pb ); +}; + +#define QIB_7220_RcvEgrIndexTail13_offset 0x002d0010UL + +#define QIB_7220_RcvEgrIndexHead13_offset 0x002d0018UL + +#define QIB_7220_MEM_2D0020_offset 0x002d0020UL + +#define QIB_7220_RcvHdrTail14_offset 0x002e0000UL + +#define QIB_7220_RcvHdrHead14_offset 0x002e0008UL +struct QIB_7220_RcvHdrHead14_pb { + pseudo_bit_t RcvHeadPointer[32]; + pseudo_bit_t counter[16]; + pseudo_bit_t Reserved[16]; +}; +struct QIB_7220_RcvHdrHead14 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvHdrHead14_pb ); +}; + +#define QIB_7220_RcvEgrIndexTail14_offset 0x002e0010UL + +#define QIB_7220_RcvEgrIndexHead14_offset 0x002e0018UL + +#define QIB_7220_MEM_2E0020_offset 0x002e0020UL + +#define QIB_7220_RcvHdrTail15_offset 0x002f0000UL + +#define QIB_7220_RcvHdrHead15_offset 0x002f0008UL +struct QIB_7220_RcvHdrHead15_pb { + pseudo_bit_t RcvHeadPointer[32]; + pseudo_bit_t counter[16]; + pseudo_bit_t Reserved[16]; +}; +struct QIB_7220_RcvHdrHead15 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvHdrHead15_pb ); +}; + +#define QIB_7220_RcvEgrIndexTail15_offset 0x002f0010UL + +#define QIB_7220_RcvEgrIndexHead15_offset 0x002f0018UL + +#define QIB_7220_MEM_2F0020_offset 0x002f0020UL + +#define QIB_7220_RcvHdrTail16_offset 0x00300000UL + +#define QIB_7220_RcvHdrHead16_offset 0x00300008UL +struct QIB_7220_RcvHdrHead16_pb { + pseudo_bit_t RcvHeadPointer[32]; + pseudo_bit_t counter[16]; + pseudo_bit_t Reserved[16]; +}; +struct QIB_7220_RcvHdrHead16 { + PSEUDO_BIT_STRUCT ( struct QIB_7220_RcvHdrHead16_pb ); +}; + +#define QIB_7220_RcvEgrIndexTail16_offset 0x00300010UL + +#define QIB_7220_RcvEgrIndexHead16_offset 0x00300018UL + +#define QIB_7220_MEM_300020_offset 0x00300020UL + diff --git a/gpxe/src/drivers/infiniband/qib_genbits.pl b/gpxe/src/drivers/infiniband/qib_genbits.pl new file mode 100644 index 0000000..9eba4da --- /dev/null +++ b/gpxe/src/drivers/infiniband/qib_genbits.pl @@ -0,0 +1,95 @@ +#!/usr/bin/perl -w +# +# Copyright (C) 2008 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. + +use strict; +use warnings; + +my $offsets = {}; +my $structures = {}; +my $structure = ""; + +while ( <> ) { + chomp; + if ( /^\#define (\S+)_OFFS (\S+)$/ ) { + $structure = $1; + $offsets->{$structure} = $2; + } elsif ( /^\#define ${structure}_(\S+)_LSB (\S+)$/ ) { + $structures->{$structure}->{$1}->{LSB} = $2; + } elsif ( /^\#define ${structure}_(\S+)_RMASK (\S+)$/ ) { + $structures->{$structure}->{$1}->{RMASK} = $2; + } elsif ( /^\s*$/ ) { + # Do nothing + } else { + print "$_\n"; + } +} + +my $data = [ map { { name => $_, offset => $offsets->{$_} }; } + sort { hex ( $offsets->{$a} ) <=> hex ( $offsets->{$b} ) } + keys %$offsets ]; + +foreach my $datum ( @$data ) { + next unless exists $structures->{$datum->{name}}; + $structure = $structures->{$datum->{name}}; + my $fields = [ map { { name => $_, lsb => $structure->{$_}->{LSB}, + rmask => $structure->{$_}->{RMASK} }; } + sort { hex ( $structure->{$a}->{LSB} ) <=> + hex ( $structure->{$b}->{LSB} ) } + keys %$structure ]; + $datum->{fields} = $fields; +} + +print "\n/* This file has been further processed by $0 */\n\n\n"; + +foreach my $datum ( @$data ) { + printf "#define %s_offset 0x%08xUL\n", + $datum->{name}, hex ( $datum->{offset} ); + if ( exists $datum->{fields} ) { + my $lsb = 0; + my $reserved_idx = 0; + printf "struct %s_pb {\n", $datum->{name}; + foreach my $field ( @{$datum->{fields}} ) { + my $pad_width = ( hex ( $field->{lsb} ) - $lsb ); + die "Inconsistent LSB/RMASK in $datum->{name}\n" if $pad_width < 0; + printf "\tpseudo_bit_t _unused_%u[%u];\n", $reserved_idx++, $pad_width + if $pad_width; + # Damn Perl can't cope with 64-bit hex constants + my $width = 0; + my $rmask = $field->{rmask}; + while ( $rmask =~ /^(0x.+)f$/i ) { + $width += 4; + $rmask = $1; + } + $rmask = hex ( $rmask ); + while ( $rmask ) { + $width++; + $rmask >>= 1; + } + printf "\tpseudo_bit_t %s[%u];\n", $field->{name}, $width; + $lsb += $width; + } + my $pad_width = ( 64 - $lsb ); + die "Inconsistent LSB/RMASK in $datum->{name}\n" if $pad_width < 0; + printf "\tpseudo_bit_t _unused_%u[%u];\n", $reserved_idx++, $pad_width + if $pad_width; + printf "};\n"; + printf "struct %s {\n\tPSEUDO_BIT_STRUCT ( struct %s_pb );\n};\n", + $datum->{name}, $datum->{name}; + } + print "\n"; +} diff --git a/gpxe/src/drivers/net/3c503.c b/gpxe/src/drivers/net/3c503.c new file mode 100644 index 0000000..1704dcd --- /dev/null +++ b/gpxe/src/drivers/net/3c503.c @@ -0,0 +1,5 @@ +/* 3Com 3c503, a memory-mapped NS8390-based card */ +#if 0 /* Currently broken! */ +#define INCLUDE_3C503 +#include "ns8390.c" +#endif diff --git a/gpxe/src/drivers/net/3c509-eisa.c b/gpxe/src/drivers/net/3c509-eisa.c new file mode 100644 index 0000000..d57c05b --- /dev/null +++ b/gpxe/src/drivers/net/3c509-eisa.c @@ -0,0 +1,49 @@ +/* + * Split out from 3c509.c, since EISA cards are relatively rare, and + * ROM space in 3c509s is very limited. + * + */ + +#include +#include +#include "console.h" +#include "3c509.h" + +/* + * The EISA probe function + * + */ +static int el3_eisa_probe ( struct nic *nic, struct eisa_device *eisa ) { + + + nic->ioaddr = eisa->ioaddr; + nic->irqno = 0; + enable_eisa_device ( eisa ); + + /* Hand off to generic t5x9 probe routine */ + return t5x9_probe ( nic, ISA_PROD_ID ( PROD_ID ), ISA_PROD_ID_MASK ); +} + +static void el3_eisa_disable ( struct nic *nic, struct eisa_device *eisa ) { + t5x9_disable ( nic ); + disable_eisa_device ( eisa ); +} + +static struct eisa_device_id el3_eisa_adapters[] = { + { "3Com 3c509 EtherLink III (EISA)", MFG_ID, PROD_ID }, +}; + +EISA_DRIVER ( el3_eisa_driver, el3_eisa_adapters ); + +DRIVER ( "3c509 (EISA)", nic_driver, eisa_driver, el3_eisa_driver, + el3_eisa_probe, el3_eisa_disable ); + +ISA_ROM ( "3c509-eisa","3c509 (EISA)" ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/3c509.c b/gpxe/src/drivers/net/3c509.c new file mode 100644 index 0000000..ecfdec5 --- /dev/null +++ b/gpxe/src/drivers/net/3c509.c @@ -0,0 +1,430 @@ +/* + * Split out into 3c509.c and 3c5x9.c, to make it possible to build a + * 3c529 module without including ISA, ISAPnP and EISA code. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "3c509.h" + +/* + * 3c509 cards have their own method of contention resolution; this + * effectively defines another bus type similar to ISAPnP. Even the + * original ISA cards can be programatically mapped to any I/O address + * in the range 0x200-0x3e0. + * + * However, there is a small problem: once you've activated a card, + * the only ways to deactivate it will also wipe its tag, meaning that + * you won't be able to subsequently reactivate it without going + * through the whole ID sequence again. The solution we adopt is to + * isolate and tag all cards at the start, and to immediately + * re-isolate and re-tag a card after disabling it. + * + */ + +static void t509bus_remove ( struct root_device *rootdev ); + +static unsigned int t509_id_port = 0; +static unsigned int t509_max_tag = 0; + +/** A 3c509 device */ +struct t509_device { + /** Generic device */ + struct device dev; + /** Tag */ + unsigned int tag; + /** I/O address */ + uint16_t ioaddr; + /** Driver-private data + * + * Use t509_set_drvdata() and t509_get_drvdata() to access + * this field. + */ + void *priv; +}; + +/** + * Set 3c509 driver-private data + * + * @v t509 3c509 device + * @v priv Private data + */ +static inline void t509_set_drvdata ( struct t509_device *t509, void *priv ) { + t509->priv = priv; +} + +/** + * Get 3c509 driver-private data + * + * @v t509 3c509 device + * @ret priv Private data + */ +static inline void * t509_get_drvdata ( struct t509_device *t509 ) { + return t509->priv; +} + +/* + * t509 utility functions + * + */ + +static inline void t509_set_id_port ( void ) { + outb ( 0x00, t509_id_port ); +} + +static inline void t509_wait_for_id_sequence ( void ) { + outb ( 0x00, t509_id_port ); +} + +static inline void t509_global_reset ( void ) { + outb ( 0xc0, t509_id_port ); +} + +static inline void t509_reset_tag ( void ) { + outb ( 0xd0, t509_id_port ); +} + +static inline void t509_set_tag ( uint8_t tag ) { + outb ( 0xd0 | tag, t509_id_port ); +} + +static inline void t509_select_tag ( uint8_t tag ) { + outb ( 0xd8 | tag, t509_id_port ); +} + +static inline void t509_activate ( uint16_t ioaddr ) { + outb ( 0xe0 | ( ioaddr >> 4 ), t509_id_port ); +} + +static inline void t509_deactivate_and_reset_tag ( uint16_t ioaddr ) { + outb ( GLOBAL_RESET, ioaddr + EP_COMMAND ); +} + +static inline void t509_load_eeprom_word ( uint8_t offset ) { + outb ( 0x80 | offset, t509_id_port ); +} + +/* + * Find a suitable ID port + * + */ +static inline int t509_find_id_port ( void ) { + + for ( t509_id_port = EP_ID_PORT_START ; + t509_id_port < EP_ID_PORT_END ; + t509_id_port += EP_ID_PORT_INC ) { + t509_set_id_port (); + /* See if anything's listening */ + outb ( 0xff, t509_id_port ); + if ( inb ( t509_id_port ) & 0x01 ) { + /* Found a suitable port */ + DBG ( "T509 using ID port at %04x\n", t509_id_port ); + return 0; + } + } + /* No id port available */ + DBG ( "T509 found no available ID port\n" ); + return -ENOENT; +} + +/* + * Send ID sequence to the ID port + * + */ +static void t509_send_id_sequence ( void ) { + unsigned short lrs_state, i; + + t509_set_id_port (); + /* Reset IDS on cards */ + t509_wait_for_id_sequence (); + lrs_state = 0xff; + for ( i = 0; i < 255; i++ ) { + outb ( lrs_state, t509_id_port ); + lrs_state <<= 1; + lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state; + } +} + +/* + * We get eeprom data from the id_port given an offset into the eeprom. + * Basically; after the ID_sequence is sent to all of the cards; they enter + * the ID_CMD state where they will accept command requests. 0x80-0xbf loads + * the eeprom data. We then read the port 16 times and with every read; the + * cards check for contention (ie: if one card writes a 0 bit and another + * writes a 1 bit then the host sees a 0. At the end of the cycle; each card + * compares the data on the bus; if there is a difference then that card goes + * into ID_WAIT state again). In the meantime; one bit of data is returned in + * the AX register which is conveniently returned to us by inb(). Hence; we + * read 16 times getting one bit of data with each read. + */ +static uint16_t t509_id_read_eeprom ( int offset ) { + int i, data = 0; + + t509_load_eeprom_word ( offset ); + /* Do we really need this wait? Won't be noticeable anyway */ + udelay(10000); + + for ( i = 0; i < 16; i++ ) { + data = ( data << 1 ) | ( inw ( t509_id_port ) & 1 ); + } + return data; +} + +/* + * Isolate and tag all t509 cards + * + */ +static int t509_isolate ( void ) { + unsigned int i; + uint16_t contend[3]; + int rc; + + /* Find a suitable ID port */ + if ( ( rc = t509_find_id_port() ) != 0 ) + return rc; + + while ( 1 ) { + + /* All cards are in ID_WAIT state each time we go + * through this loop. + */ + + /* Send the ID sequence */ + t509_send_id_sequence(); + + /* First time through, reset all tags. On subsequent + * iterations, kill off any already-tagged cards + */ + if ( t509_max_tag == 0 ) { + t509_reset_tag(); + } else { + t509_select_tag ( 0 ); + } + + /* Read the manufacturer ID, to see if there are any + * more cards + */ + if ( t509_id_read_eeprom ( EEPROM_MFG_ID ) != MFG_ID ) { + DBG ( "T509 saw %s signs of life\n", + t509_max_tag ? "no further" : "no" ); + break; + } + + /* Perform contention selection on the MAC address */ + for ( i = 0 ; i < 3 ; i++ ) { + contend[i] = t509_id_read_eeprom ( i ); + } + + /* Only one device will still be left alive. Tag it. */ + ++t509_max_tag; + DBG ( "T509 found card %04x%04x%04x, assigning tag %02x\n", + contend[0], contend[1], contend[2], t509_max_tag ); + t509_set_tag ( t509_max_tag ); + + /* Return all cards back to ID_WAIT state */ + t509_wait_for_id_sequence(); + } + + DBG ( "T509 found %d cards using ID port %04x\n", + t509_max_tag, t509_id_port ); + return 0; +} + +/* + * Activate a T509 device + * + * The device will be enabled at whatever ioaddr is specified in the + * struct t509_device; there is no need to stick with the default + * ioaddr read from the EEPROM. + * + */ +static inline void activate_t509_device ( struct t509_device *t509 ) { + t509_send_id_sequence (); + t509_select_tag ( t509->tag ); + t509_activate ( t509->ioaddr ); + DBG ( "T509 activated device %02x at ioaddr %04x\n", + t509->tag, t509->ioaddr ); +} + +/* + * Deactivate a T509 device + * + * Disabling also clears the tag, so we immediately isolate and re-tag + * this card. + * + */ +static inline void deactivate_t509_device ( struct t509_device *t509 ) { + t509_deactivate_and_reset_tag ( t509->ioaddr ); + udelay ( 1000 ); + t509_send_id_sequence (); + t509_select_tag ( 0 ); + t509_set_tag ( t509->tag ); + t509_wait_for_id_sequence (); + DBG ( "T509 deactivated device at %04x and re-tagged as %02x\n", + t509->ioaddr, t509->tag ); +} + +/* + * The ISA probe function + * + */ +static int legacy_t509_probe ( struct nic *nic, void *hwdev ) { + struct t509_device *t509 = hwdev; + + /* We could change t509->ioaddr if we wanted to */ + activate_t509_device ( t509 ); + nic->ioaddr = t509->ioaddr; + + /* Hand off to generic t5x9 probe routine */ + return t5x9_probe ( nic, ISA_PROD_ID ( PROD_ID ), ISA_PROD_ID_MASK ); +} + +static void legacy_t509_disable ( struct nic *nic, void *hwdev ) { + struct t509_device *t509 = hwdev; + + t5x9_disable ( nic ); + deactivate_t509_device ( t509 ); +} + +static inline void legacy_t509_set_drvdata ( void *hwdev, void *priv ) { + t509_set_drvdata ( hwdev, priv ); +} + +static inline void * legacy_t509_get_drvdata ( void *hwdev ) { + return t509_get_drvdata ( hwdev ); +} + +/** + * Probe a 3c509 device + * + * @v t509 3c509 device + * @ret rc Return status code + * + * Searches for a driver for the 3c509 device. If a driver is found, + * its probe() routine is called. + */ +static int t509_probe ( struct t509_device *t509 ) { + DBG ( "Adding 3c509 device %02x (I/O %04x)\n", + t509->tag, t509->ioaddr ); + return legacy_probe ( t509, legacy_t509_set_drvdata, &t509->dev, + legacy_t509_probe, legacy_t509_disable ); +} + +/** + * Remove a 3c509 device + * + * @v t509 3c509 device + */ +static void t509_remove ( struct t509_device *t509 ) { + legacy_remove ( t509, legacy_t509_get_drvdata, legacy_t509_disable ); + DBG ( "Removed 3c509 device %02x\n", t509->tag ); +} + +/** + * Probe 3c509 root bus + * + * @v rootdev 3c509 bus root device + * + * Scans the 3c509 bus for devices and registers all devices it can + * find. + */ +static int t509bus_probe ( struct root_device *rootdev ) { + struct t509_device *t509 = NULL; + unsigned int tag; + unsigned int iobase; + int rc; + + /* Perform isolation and tagging */ + if ( ( rc = t509_isolate() ) != 0 ) + return rc; + + for ( tag = 1 ; tag <= t509_max_tag ; tag++ ) { + /* Allocate struct t509_device */ + if ( ! t509 ) + t509 = malloc ( sizeof ( *t509 ) ); + if ( ! t509 ) { + rc = -ENOMEM; + goto err; + } + memset ( t509, 0, sizeof ( *t509 ) ); + t509->tag = tag; + + /* Send the ID sequence */ + t509_send_id_sequence (); + + /* Select the specified tag */ + t509_select_tag ( t509->tag ); + + /* Read the default I/O address */ + iobase = t509_id_read_eeprom ( EEPROM_ADDR_CFG ); + t509->ioaddr = 0x200 + ( ( iobase & 0x1f ) << 4 ); + + /* Send card back to ID_WAIT */ + t509_wait_for_id_sequence(); + + /* Add to device hierarchy */ + snprintf ( t509->dev.name, sizeof ( t509->dev.name ), + "t509%02x", tag ); + t509->dev.desc.bus_type = BUS_TYPE_ISA; + t509->dev.desc.vendor = MFG_ID; + t509->dev.desc.device = PROD_ID; + t509->dev.parent = &rootdev->dev; + list_add ( &t509->dev.siblings, &rootdev->dev.children ); + INIT_LIST_HEAD ( &t509->dev.children ); + + /* Look for a driver */ + if ( t509_probe ( t509 ) == 0 ) { + /* t509dev registered, we can drop our ref */ + t509 = NULL; + } else { + /* Not registered; re-use struct */ + list_del ( &t509->dev.siblings ); + } + } + + free ( t509 ); + return 0; + + err: + free ( t509 ); + t509bus_remove ( rootdev ); + return rc; +} + +/** + * Remove 3c509 root bus + * + * @v rootdev 3c509 bus root device + */ +static void t509bus_remove ( struct root_device *rootdev ) { + struct t509_device *t509; + struct t509_device *tmp; + + list_for_each_entry_safe ( t509, tmp, &rootdev->dev.children, + dev.siblings ) { + t509_remove ( t509 ); + list_del ( &t509->dev.siblings ); + free ( t509 ); + } +} + +/** 3c509 bus root device driver */ +static struct root_driver t509_root_driver = { + .probe = t509bus_probe, + .remove = t509bus_remove, +}; + +/** 3c509 bus root device */ +struct root_device t509_root_device __root_device = { + .dev = { .name = "3c509" }, + .driver = &t509_root_driver, +}; + +ISA_ROM ( "3c509", "3c509" ); diff --git a/gpxe/src/drivers/net/3c509.h b/gpxe/src/drivers/net/3c509.h new file mode 100644 index 0000000..a06d91e --- /dev/null +++ b/gpxe/src/drivers/net/3c509.h @@ -0,0 +1,392 @@ +/* + * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. The name + * of the author may not be used to endorse or promote products derived from + * this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * if_epreg.h,v 1.4 1994/11/13 10:12:37 gibbs Exp Modified by: + * + October 2, 1994 + + Modified by: Andres Vega Garcia + + INRIA - Sophia Antipolis, France + e-mail: avega@sophia.inria.fr + finger: avega@pax.inria.fr + + */ + +#include "nic.h" + +/* + * Ethernet software status per interface. + */ +/* + * Some global constants + */ + +#define TX_INIT_RATE 16 +#define TX_INIT_MAX_RATE 64 +#define RX_INIT_LATENCY 64 +#define RX_INIT_EARLY_THRESH 64 +#define MIN_RX_EARLY_THRESHF 16 /* not less than ether_header */ +#define MIN_RX_EARLY_THRESHL 4 + +#define EEPROMSIZE 0x40 +#define MAX_EEPROMBUSY 1000 +#define EP_ID_PORT_START 0x110 /* avoid 0x100 to avoid conflict with SB16 */ +#define EP_ID_PORT_INC 0x10 +#define EP_ID_PORT_END 0x200 +#define EP_TAG_MAX 0x7 /* must be 2^n - 1 */ + +/* + * Commands to read/write EEPROM trough EEPROM command register (Window 0, + * Offset 0xa) + */ +#define EEPROM_CMD_RD 0x0080 /* Read: Address required (5 bits) */ +#define EEPROM_CMD_WR 0x0040 /* Write: Address required (5 bits) */ +#define EEPROM_CMD_ERASE 0x00c0 /* Erase: Address required (5 bits) */ +#define EEPROM_CMD_EWEN 0x0030 /* Erase/Write Enable: No data required */ + +#define EEPROM_BUSY (1<<15) +#define EEPROM_TST_MODE (1<<14) + +/* + * Some short functions, worth to let them be a macro + */ +#define is_eeprom_busy(b) (inw((b)+EP_W0_EEPROM_COMMAND)&EEPROM_BUSY) +#define GO_WINDOW(b,x) outw(WINDOW_SELECT|(x), (b)+EP_COMMAND) + +/************************************************************************** + * + * These define the EEPROM data structure. They are used in the probe + * function to verify the existance of the adapter after having sent + * the ID_Sequence. + * + * There are others but only the ones we use are defined here. + * + **************************************************************************/ + +#define EEPROM_NODE_ADDR_0 0x0 /* Word */ +#define EEPROM_NODE_ADDR_1 0x1 /* Word */ +#define EEPROM_NODE_ADDR_2 0x2 /* Word */ +#define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */ +#define EEPROM_MFG_ID 0x7 /* 0x6d50 */ +#define EEPROM_ADDR_CFG 0x8 /* Base addr */ +#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */ + +/************************************************************************** + * + * These are the registers for the 3Com 3c509 and their bit patterns when + * applicable. They have been taken out the the "EtherLink III Parallel + * Tasking EISA and ISA Technical Reference" "Beta Draft 10/30/92" manual + * from 3com. + * + * Getting this document out of 3Com is almost impossible. However, + * archived copies are available at + * http://www.osdever.net/cottontail/downloads/docs/3c5x9b.zip and + * several other places on the web (search for 3c5x9b.pdf). + * + **************************************************************************/ + +#define EP_COMMAND 0x0e /* Write. BASE+0x0e is always a + * command reg. */ +#define EP_STATUS 0x0e /* Read. BASE+0x0e is always status + * reg. */ +#define EP_WINDOW 0x0f /* Read. BASE+0x0f is always window + * reg. */ +/* + * Window 0 registers. Setup. + */ +/* Write */ +#define EP_W0_EEPROM_DATA 0x0c +#define EP_W0_EEPROM_COMMAND 0x0a +#define EP_W0_RESOURCE_CFG 0x08 +#define EP_W0_ADDRESS_CFG 0x06 +#define EP_W0_CONFIG_CTRL 0x04 +/* Read */ +#define EP_W0_PRODUCT_ID 0x02 +#define EP_W0_MFG_ID 0x00 + +/* + * Window 1 registers. Operating Set. + */ +/* Write */ +#define EP_W1_TX_PIO_WR_2 0x02 +#define EP_W1_TX_PIO_WR_1 0x00 +/* Read */ +#define EP_W1_FREE_TX 0x0c +#define EP_W1_TX_STATUS 0x0b /* byte */ +#define EP_W1_TIMER 0x0a /* byte */ +#define EP_W1_RX_STATUS 0x08 +#define EP_W1_RX_PIO_RD_2 0x02 +#define EP_W1_RX_PIO_RD_1 0x00 + +/* + * Window 2 registers. Station Address Setup/Read + */ +/* Read/Write */ +#define EP_W2_ADDR_5 0x05 +#define EP_W2_ADDR_4 0x04 +#define EP_W2_ADDR_3 0x03 +#define EP_W2_ADDR_2 0x02 +#define EP_W2_ADDR_1 0x01 +#define EP_W2_ADDR_0 0x00 + +/* + * Window 3 registers. FIFO Management. + */ +/* Read */ +#define EP_W3_FREE_TX 0x0c +#define EP_W3_FREE_RX 0x0a + +/* + * Window 4 registers. Diagnostics. + */ +/* Read/Write */ +#define EP_W4_MEDIA_TYPE 0x0a +#define EP_W4_CTRLR_STATUS 0x08 +#define EP_W4_NET_DIAG 0x06 +#define EP_W4_FIFO_DIAG 0x04 +#define EP_W4_HOST_DIAG 0x02 +#define EP_W4_TX_DIAG 0x00 + +/* + * Window 5 Registers. Results and Internal status. + */ +/* Read */ +#define EP_W5_READ_0_MASK 0x0c +#define EP_W5_INTR_MASK 0x0a +#define EP_W5_RX_FILTER 0x08 +#define EP_W5_RX_EARLY_THRESH 0x06 +#define EP_W5_TX_AVAIL_THRESH 0x02 +#define EP_W5_TX_START_THRESH 0x00 + +/* + * Window 6 registers. Statistics. + */ +/* Read/Write */ +#define TX_TOTAL_OK 0x0c +#define RX_TOTAL_OK 0x0a +#define TX_DEFERRALS 0x08 +#define RX_FRAMES_OK 0x07 +#define TX_FRAMES_OK 0x06 +#define RX_OVERRUNS 0x05 +#define TX_COLLISIONS 0x04 +#define TX_AFTER_1_COLLISION 0x03 +#define TX_AFTER_X_COLLISIONS 0x02 +#define TX_NO_SQE 0x01 +#define TX_CD_LOST 0x00 + +/**************************************** + * + * Register definitions. + * + ****************************************/ + +/* + * Command register. All windows. + * + * 16 bit register. + * 15-11: 5-bit code for command to be executed. + * 10-0: 11-bit arg if any. For commands with no args; + * this can be set to anything. + */ +#define GLOBAL_RESET (unsigned short) 0x0000 /* Wait at least 1ms + * after issuing */ +#define WINDOW_SELECT (unsigned short) (0x1<<11) +#define START_TRANSCEIVER (unsigned short) (0x2<<11) /* Read ADDR_CFG reg to + * determine whether + * this is needed. If + * so; wait 800 uSec + * before using trans- + * ceiver. */ +#define RX_DISABLE (unsigned short) (0x3<<11) /* state disabled on + * power-up */ +#define RX_ENABLE (unsigned short) (0x4<<11) +#define RX_RESET (unsigned short) (0x5<<11) +#define RX_DISCARD_TOP_PACK (unsigned short) (0x8<<11) +#define TX_ENABLE (unsigned short) (0x9<<11) +#define TX_DISABLE (unsigned short) (0xa<<11) +#define TX_RESET (unsigned short) (0xb<<11) +#define REQ_INTR (unsigned short) (0xc<<11) +#define SET_INTR_MASK (unsigned short) (0xe<<11) +#define SET_RD_0_MASK (unsigned short) (0xf<<11) +#define SET_RX_FILTER (unsigned short) (0x10<<11) +#define FIL_INDIVIDUAL (unsigned short) (0x1) +#define FIL_GROUP (unsigned short) (0x2) +#define FIL_BRDCST (unsigned short) (0x4) +#define FIL_ALL (unsigned short) (0x8) +#define SET_RX_EARLY_THRESH (unsigned short) (0x11<<11) +#define SET_TX_AVAIL_THRESH (unsigned short) (0x12<<11) +#define SET_TX_START_THRESH (unsigned short) (0x13<<11) +#define STATS_ENABLE (unsigned short) (0x15<<11) +#define STATS_DISABLE (unsigned short) (0x16<<11) +#define STOP_TRANSCEIVER (unsigned short) (0x17<<11) +/* + * The following C_* acknowledge the various interrupts. Some of them don't + * do anything. See the manual. + */ +#define ACK_INTR (unsigned short) (0x6800) +#define C_INTR_LATCH (unsigned short) (ACK_INTR|0x1) +#define C_CARD_FAILURE (unsigned short) (ACK_INTR|0x2) +#define C_TX_COMPLETE (unsigned short) (ACK_INTR|0x4) +#define C_TX_AVAIL (unsigned short) (ACK_INTR|0x8) +#define C_RX_COMPLETE (unsigned short) (ACK_INTR|0x10) +#define C_RX_EARLY (unsigned short) (ACK_INTR|0x20) +#define C_INT_RQD (unsigned short) (ACK_INTR|0x40) +#define C_UPD_STATS (unsigned short) (ACK_INTR|0x80) + +/* + * Status register. All windows. + * + * 15-13: Window number(0-7). + * 12: Command_in_progress. + * 11: reserved. + * 10: reserved. + * 9: reserved. + * 8: reserved. + * 7: Update Statistics. + * 6: Interrupt Requested. + * 5: RX Early. + * 4: RX Complete. + * 3: TX Available. + * 2: TX Complete. + * 1: Adapter Failure. + * 0: Interrupt Latch. + */ +#define S_INTR_LATCH (unsigned short) (0x1) +#define S_CARD_FAILURE (unsigned short) (0x2) +#define S_TX_COMPLETE (unsigned short) (0x4) +#define S_TX_AVAIL (unsigned short) (0x8) +#define S_RX_COMPLETE (unsigned short) (0x10) +#define S_RX_EARLY (unsigned short) (0x20) +#define S_INT_RQD (unsigned short) (0x40) +#define S_UPD_STATS (unsigned short) (0x80) +#define S_5_INTS (S_CARD_FAILURE|S_TX_COMPLETE|\ + S_TX_AVAIL|S_RX_COMPLETE|S_RX_EARLY) +#define S_COMMAND_IN_PROGRESS (unsigned short) (0x1000) + +/* + * FIFO Registers. + * RX Status. Window 1/Port 08 + * + * 15: Incomplete or FIFO empty. + * 14: 1: Error in RX Packet 0: Incomplete or no error. + * 13-11: Type of error. + * 1000 = Overrun. + * 1011 = Run Packet Error. + * 1100 = Alignment Error. + * 1101 = CRC Error. + * 1001 = Oversize Packet Error (>1514 bytes) + * 0010 = Dribble Bits. + * (all other error codes, no errors.) + * + * 10-0: RX Bytes (0-1514) + */ +#define ERR_RX_INCOMPLETE (unsigned short) (0x1<<15) +#define ERR_RX (unsigned short) (0x1<<14) +#define ERR_RX_OVERRUN (unsigned short) (0x8<<11) +#define ERR_RX_RUN_PKT (unsigned short) (0xb<<11) +#define ERR_RX_ALIGN (unsigned short) (0xc<<11) +#define ERR_RX_CRC (unsigned short) (0xd<<11) +#define ERR_RX_OVERSIZE (unsigned short) (0x9<<11) +#define ERR_RX_DRIBBLE (unsigned short) (0x2<<11) + +/* + * FIFO Registers. + * TX Status. Window 1/Port 0B + * + * Reports the transmit status of a completed transmission. Writing this + * register pops the transmit completion stack. + * + * Window 1/Port 0x0b. + * + * 7: Complete + * 6: Interrupt on successful transmission requested. + * 5: Jabber Error (TP Only, TX Reset required. ) + * 4: Underrun (TX Reset required. ) + * 3: Maximum Collisions. + * 2: TX Status Overflow. + * 1-0: Undefined. + * + */ +#define TXS_COMPLETE 0x80 +#define TXS_SUCCES_INTR_REQ 0x40 +#define TXS_JABBER 0x20 +#define TXS_UNDERRUN 0x10 +#define TXS_MAX_COLLISION 0x8 +#define TXS_STATUS_OVERFLOW 0x4 + +/* + * Configuration control register. + * Window 0/Port 04 + */ +/* Read */ +#define IS_AUI (1<<13) +#define IS_BNC (1<<12) +#define IS_UTP (1<<9) +/* Write */ +#define ENABLE_DRQ_IRQ 0x0001 +#define W0_P4_CMD_RESET_ADAPTER 0x4 +#define W0_P4_CMD_ENABLE_ADAPTER 0x1 +/* + * Media type and status. + * Window 4/Port 0A + */ +#define ENABLE_UTP 0xc0 +#define DISABLE_UTP 0x0 + +/* + * Resource control register + */ + +#define SET_IRQ(i) ( ((i)<<12) | 0xF00) /* set IRQ i */ + +/* + * Receive status register + */ + +#define RX_BYTES_MASK (unsigned short) (0x07ff) +#define RX_ERROR 0x4000 +#define RX_INCOMPLETE 0x8000 + +/* + * Misc defines for various things. + */ +#define MFG_ID 0x6d50 /* in EEPROM and W0 ADDR_CONFIG */ +#define PROD_ID 0x9150 + +#define AUI 0x1 +#define BNC 0x2 +#define UTP 0x4 + +#define RX_BYTES_MASK (unsigned short) (0x07ff) + +/* + * Function shared between 3c509.c and 3c529.c + */ +extern int t5x9_probe ( struct nic *nic, + uint16_t prod_id_check, uint16_t prod_id_mask ); +extern void t5x9_disable ( struct nic *nic ); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/3c515.c b/gpxe/src/drivers/net/3c515.c new file mode 100644 index 0000000..dcfe66b --- /dev/null +++ b/gpxe/src/drivers/net/3c515.c @@ -0,0 +1,762 @@ +/* +* 3c515.c -- 3COM 3C515 Fast Etherlink ISA 10/100BASE-TX driver for etherboot +* Copyright (C) 2002 Timothy Legge +* +* 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 +* *********************************************************/ + + +/* to get some global routines like printf */ +#include "etherboot.h" +/* to get the interface to the body of the program */ +#include "nic.h" +#include +#include /* for ISA_ROM */ +#include + +static void t3c515_wait(unsigned int nticks) +{ + unsigned int to = currticks() + nticks; + while (currticks() < to) + /* wait */ ; +} + +/* TJL definations */ +#define HZ 100 +static int if_port; +static struct corkscrew_private *vp; +/* Brought directly from 3c515.c by Becker */ +#define CORKSCREW 1 + +/* Maximum events (Rx packets, etc.) to handle at each interrupt. +static int max_interrupt_work = 20; +*/ + +/* Enable the automatic media selection code -- usually set. */ +#define AUTOMEDIA 1 + +/* Allow the use of fragment bus master transfers instead of only + programmed-I/O for Vortex cards. Full-bus-master transfers are always + enabled by default on Boomerang cards. If VORTEX_BUS_MASTER is defined, + the feature may be turned on using 'options'. */ +#define VORTEX_BUS_MASTER + +/* A few values that may be tweaked. */ +/* Keep the ring sizes a power of two for efficiency. */ +#define TX_RING_SIZE 16 +#define RX_RING_SIZE 16 +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */ + +/* "Knobs" for adjusting internal parameters. */ +/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */ +#define DRIVER_DEBUG 1 +/* Some values here only for performance evaluation and path-coverage + debugging. +static int rx_nocopy, rx_copy, queued_packet; +*/ + +#define CORKSCREW_ID 10 + +#define EL3WINDOW(win_num) \ + outw(SelectWindow + (win_num), nic->ioaddr + EL3_CMD) +#define EL3_CMD 0x0e +#define EL3_STATUS 0x0e +#define RX_BYTES_MASK (unsigned short) (0x07ff) + +enum corkscrew_cmd { + TotalReset = 0 << 11, SelectWindow = 1 << 11, StartCoax = 2 << 11, + RxDisable = 3 << 11, RxEnable = 4 << 11, RxReset = 5 << 11, + UpStall = 6 << 11, UpUnstall = (6 << 11) + 1, + DownStall = (6 << 11) + 2, DownUnstall = (6 << 11) + 3, + RxDiscard = 8 << 11, TxEnable = 9 << 11, TxDisable = + 10 << 11, TxReset = 11 << 11, + FakeIntr = 12 << 11, AckIntr = 13 << 11, SetIntrEnb = 14 << 11, + SetStatusEnb = 15 << 11, SetRxFilter = 16 << 11, SetRxThreshold = + 17 << 11, + SetTxThreshold = 18 << 11, SetTxStart = 19 << 11, + StartDMAUp = 20 << 11, StartDMADown = (20 << 11) + 1, StatsEnable = + 21 << 11, + StatsDisable = 22 << 11, StopCoax = 23 << 11, +}; + +/* The SetRxFilter command accepts the following classes: */ +enum RxFilter { + RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 +}; + +/* Bits in the general status register. */ +enum corkscrew_status { + IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, + TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, + IntReq = 0x0040, StatsFull = 0x0080, + DMADone = 1 << 8, DownComplete = 1 << 9, UpComplete = 1 << 10, + DMAInProgress = 1 << 11, /* DMA controller is still busy. */ + CmdInProgress = 1 << 12, /* EL3_CMD is still busy. */ +}; + +/* Register window 1 offsets, the window used in normal operation. + On the Corkscrew this window is always mapped at offsets 0x10-0x1f. */ +enum Window1 { + TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14, + RxStatus = 0x18, Timer = 0x1A, TxStatus = 0x1B, + TxFree = 0x1C, /* Remaining free bytes in Tx buffer. */ +}; +enum Window0 { + Wn0IRQ = 0x08, +#if defined(CORKSCREW) + Wn0EepromCmd = 0x200A, /* Corkscrew EEPROM command register. */ + Wn0EepromData = 0x200C, /* Corkscrew EEPROM results register. */ +#else + Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */ + Wn0EepromData = 12, /* Window 0: EEPROM results register. */ +#endif +}; +enum Win0_EEPROM_bits { + EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0, + EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */ + EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */ +}; + +enum Window3 { /* Window 3: MAC/config bits. */ + Wn3_Config = 0, Wn3_MAC_Ctrl = 6, Wn3_Options = 8, +}; +union wn3_config { + int i; + struct w3_config_fields { + unsigned int ram_size:3, ram_width:1, ram_speed:2, + rom_size:2; + int pad8:8; + unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, + autoselect:1; + int pad24:7; + } u; +}; + +enum Window4 { + Wn4_NetDiag = 6, Wn4_Media = 10, /* Window 4: Xcvr/media bits. */ +}; +enum Win4_Media_bits { + Media_SQE = 0x0008, /* Enable SQE error counting for AUI. */ + Media_10TP = 0x00C0, /* Enable link beat and jabber for 10baseT. */ + Media_Lnk = 0x0080, /* Enable just link beat for 100TX/100FX. */ + Media_LnkBeat = 0x0800, +}; +enum Window7 { /* Window 7: Bus Master control. */ + Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12, +}; + +/* Boomerang-style bus master control registers. Note ISA aliases! */ +enum MasterCtrl { + PktStatus = 0x400, DownListPtr = 0x404, FragAddr = 0x408, FragLen = + 0x40c, + TxFreeThreshold = 0x40f, UpPktStatus = 0x410, UpListPtr = 0x418, +}; + +/* The Rx and Tx descriptor lists. + Caution Alpha hackers: these types are 32 bits! Note also the 8 byte + alignment contraint on tx_ring[] and rx_ring[]. */ +struct boom_rx_desc { + u32 next; + s32 status; + u32 addr; + s32 length; +}; + +/* Values for the Rx status entry. */ +enum rx_desc_status { + RxDComplete = 0x00008000, RxDError = 0x4000, + /* See boomerang_rx() for actual error bits */ +}; + +struct boom_tx_desc { + u32 next; + s32 status; + u32 addr; + s32 length; +}; + +struct corkscrew_private { + const char *product_name; + struct net_device *next_module; + /* The Rx and Tx rings are here to keep them quad-word-aligned. */ + struct boom_rx_desc rx_ring[RX_RING_SIZE]; + struct boom_tx_desc tx_ring[TX_RING_SIZE]; + /* The addresses of transmit- and receive-in-place skbuffs. */ + struct sk_buff *rx_skbuff[RX_RING_SIZE]; + struct sk_buff *tx_skbuff[TX_RING_SIZE]; + unsigned int cur_rx, cur_tx; /* The next free ring entry */ + unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ + struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */ + int capabilities; /* Adapter capabilities word. */ + int options; /* User-settable misc. driver options. */ + int last_rx_packets; /* For media autoselection. */ + unsigned int available_media:8, /* From Wn3_Options */ + media_override:3, /* Passed-in media type. */ + default_media:3, /* Read from the EEPROM. */ + full_duplex:1, autoselect:1, bus_master:1, /* Vortex can only do a fragment bus-m. */ + full_bus_master_tx:1, full_bus_master_rx:1, /* Boomerang */ + tx_full:1; +}; + +/* The action to take with a media selection timer tick. + Note that we deviate from the 3Com order by checking 10base2 before AUI. + */ +enum xcvr_types { + XCVR_10baseT = + 0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx, + XCVR_100baseFx, XCVR_MII = 6, XCVR_Default = 8, +}; + +static struct media_table { + char *name; + unsigned int media_bits:16, /* Bits to set in Wn4_Media register. */ + mask:8, /* The transceiver-present bit in Wn3_Config. */ + next:8; /* The media type to try next. */ + short wait; /* Time before we check media status. */ +} media_tbl[] = { + { + "10baseT", Media_10TP, 0x08, XCVR_10base2, (14 * HZ) / 10} + , { + "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1 * HZ) / 10} + , { + "undefined", 0, 0x80, XCVR_10baseT, 10000} + , { + "10base2", 0, 0x10, XCVR_AUI, (1 * HZ) / 10} + , { + "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, + (14 * HZ) / 10} + , { + "100baseFX", Media_Lnk, 0x04, XCVR_MII, (14 * HZ) / 10} + , { + "MII", 0, 0x40, XCVR_10baseT, 3 * HZ} + , { + "undefined", 0, 0x01, XCVR_10baseT, 10000} + , { + "Default", 0, 0xFF, XCVR_10baseT, 10000} +,}; + +/* TILEG Modified to remove reference to dev */ +static int corkscrew_found_device(int ioaddr, int irq, int product_index, + int options, struct nic *nic); +static int corkscrew_probe1(int ioaddr, int irq, int product_index, + struct nic *nic); + +/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */ +/* Note: this is the only limit on the number of cards supported!! */ +static int options = -1; + +/* End Brought directly from 3c515.c by Becker */ + +/************************************************************************** +RESET - Reset adapter +***************************************************************************/ +static void t515_reset(struct nic *nic) +{ + union wn3_config config; + int i; + + /* Before initializing select the active media port. */ + EL3WINDOW(3); + if (vp->full_duplex) + outb(0x20, nic->ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */ + config.i = inl(nic->ioaddr + Wn3_Config); + + if (vp->media_override != 7) { + DBG ( "Media override to transceiver %d (%s).\n", + vp->media_override, + media_tbl[vp->media_override].name); + if_port = vp->media_override; + } else if (vp->autoselect) { + /* Find first available media type, starting with 100baseTx. */ + if_port = 4; + while (!(vp->available_media & media_tbl[if_port].mask)) + if_port = media_tbl[if_port].next; + + DBG ( "Initial media type %s.\n", + media_tbl[if_port].name); + } else + if_port = vp->default_media; + + config.u.xcvr = if_port; + outl(config.i, nic->ioaddr + Wn3_Config); + + DBG ( "corkscrew_open() InternalConfig 0x%hX.\n", + config.i); + + outw(TxReset, nic->ioaddr + EL3_CMD); + for (i = 20; i >= 0; i--) + if (!(inw(nic->ioaddr + EL3_STATUS) & CmdInProgress)) + break; + + outw(RxReset, nic->ioaddr + EL3_CMD); + /* Wait a few ticks for the RxReset command to complete. */ + for (i = 20; i >= 0; i--) + if (!(inw(nic->ioaddr + EL3_STATUS) & CmdInProgress)) + break; + + outw(SetStatusEnb | 0x00, nic->ioaddr + EL3_CMD); + +#ifdef debug_3c515 + EL3WINDOW(4); + DBG ( "FIXME: fix print for irq, not 9" ); + DBG ( "corkscrew_open() irq %d media status 0x%hX.\n", + 9, inw(nic->ioaddr + Wn4_Media) ); +#endif + + /* Set the station address and mask in window 2 each time opened. */ + EL3WINDOW(2); + for (i = 0; i < 6; i++) + outb(nic->node_addr[i], nic->ioaddr + i); + for (; i < 12; i += 2) + outw(0, nic->ioaddr + i); + + if (if_port == 3) + /* Start the thinnet transceiver. We should really wait 50ms... */ + outw(StartCoax, nic->ioaddr + EL3_CMD); + EL3WINDOW(4); + outw((inw(nic->ioaddr + Wn4_Media) & ~(Media_10TP | Media_SQE)) | + media_tbl[if_port].media_bits, nic->ioaddr + Wn4_Media); + + /* Switch to the stats window, and clear all stats by reading. */ +/* outw(StatsDisable, nic->ioaddr + EL3_CMD);*/ + EL3WINDOW(6); + for (i = 0; i < 10; i++) + inb(nic->ioaddr + i); + inw(nic->ioaddr + 10); + inw(nic->ioaddr + 12); + /* New: On the Vortex we must also clear the BadSSD counter. */ + EL3WINDOW(4); + inb(nic->ioaddr + 12); + /* ..and on the Boomerang we enable the extra statistics bits. */ + outw(0x0040, nic->ioaddr + Wn4_NetDiag); + + /* Switch to register set 7 for normal use. */ + EL3WINDOW(7); + + /* Temporarily left in place. If these FIXMEs are printed + it meand that special logic for that card may need to be added + see Becker's 3c515.c driver */ + if (vp->full_bus_master_rx) { /* Boomerang bus master. */ + printf("FIXME: Is this if necessary"); + vp->cur_rx = vp->dirty_rx = 0; + DBG ( " Filling in the Rx ring.\n" ); + for (i = 0; i < RX_RING_SIZE; i++) { + printf("FIXME: Is this if necessary"); + } + } + if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */ + vp->cur_tx = vp->dirty_tx = 0; + outb(PKT_BUF_SZ >> 8, nic->ioaddr + TxFreeThreshold); /* Room for a packet. */ + /* Clear the Tx ring. */ + for (i = 0; i < TX_RING_SIZE; i++) + vp->tx_skbuff[i] = 0; + outl(0, nic->ioaddr + DownListPtr); + } + /* Set receiver mode: presumably accept b-case and phys addr only. */ + outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm, + nic->ioaddr + EL3_CMD); + + outw(RxEnable, nic->ioaddr + EL3_CMD); /* Enable the receiver. */ + outw(TxEnable, nic->ioaddr + EL3_CMD); /* Enable transmitter. */ + /* Allow status bits to be seen. */ + outw(SetStatusEnb | AdapterFailure | IntReq | StatsFull | + (vp->full_bus_master_tx ? DownComplete : TxAvailable) | + (vp->full_bus_master_rx ? UpComplete : RxComplete) | + (vp->bus_master ? DMADone : 0), nic->ioaddr + EL3_CMD); + /* Ack all pending events, and set active indicator mask. */ + outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, + nic->ioaddr + EL3_CMD); + outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull + | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete, + nic->ioaddr + EL3_CMD); + +} + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int t515_poll(struct nic *nic, int retrieve) +{ + short status, cst; + register short rx_fifo; + + cst = inw(nic->ioaddr + EL3_STATUS); + + if ((cst & RxComplete) == 0) { + /* Ack all pending events, and set active indicator mask. */ + outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, + nic->ioaddr + EL3_CMD); + outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | + StatsFull | (vp-> + bus_master ? DMADone : 0) | UpComplete | + DownComplete, nic->ioaddr + EL3_CMD); + return 0; + } + status = inw(nic->ioaddr + RxStatus); + + if (status & RxDError) { + printf("RxDError\n"); + outw(RxDiscard, nic->ioaddr + EL3_CMD); + return 0; + } + + rx_fifo = status & RX_BYTES_MASK; + if (rx_fifo == 0) + return 0; + + if ( ! retrieve ) return 1; + + DBG ( "[l=%d", rx_fifo ); + insw(nic->ioaddr + RX_FIFO, nic->packet, rx_fifo / 2); + if (rx_fifo & 1) + nic->packet[rx_fifo - 1] = inb(nic->ioaddr + RX_FIFO); + nic->packetlen = rx_fifo; + + while (1) { + status = inw(nic->ioaddr + RxStatus); + DBG ( "0x%hX*", status ); + rx_fifo = status & RX_BYTES_MASK; + + if (rx_fifo > 0) { + insw(nic->ioaddr + RX_FIFO, nic->packet + nic->packetlen, + rx_fifo / 2); + if (rx_fifo & 1) + nic->packet[nic->packetlen + rx_fifo - 1] = + inb(nic->ioaddr + RX_FIFO); + nic->packetlen += rx_fifo; + DBG ( "+%d", rx_fifo ); + } + if ((status & RxComplete) == 0) { + DBG ( "=%d", nic->packetlen ); + break; + } + udelay(1000); + } + + /* acknowledge reception of packet */ + outw(RxDiscard, nic->ioaddr + EL3_CMD); + while (inw(nic->ioaddr + EL3_STATUS) & CmdInProgress); +#ifdef debug_3c515 + { + unsigned short type = 0; + type = (nic->packet[12] << 8) | nic->packet[13]; + if (nic->packet[0] + nic->packet[1] + nic->packet[2] + + nic->packet[3] + nic->packet[4] + nic->packet[5] == + 0xFF * ETH_ALEN) + DBG ( ",t=0x%hX,b]", type ); + else + DBG ( ",t=0x%hX]", type ); + } +#endif + + return 1; +} + +/************************************************************************* + 3Com 515 - specific routines +**************************************************************************/ +static char padmap[] = { + 0, 3, 2, 1 +}; +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static void t515_transmit(struct nic *nic, const char *d, /* Destination */ + unsigned int t, /* Type */ + unsigned int s, /* size */ + const char *p) +{ /* Packet */ + register int len; + int pad; + int status; + + DBG ( "{l=%d,t=0x%hX}", s + ETH_HLEN, t ); + + /* swap bytes of type */ + t = htons(t); + + len = s + ETH_HLEN; /* actual length of packet */ + pad = padmap[len & 3]; + + /* + * The 3c515 automatically pads short packets to minimum ethernet length, + * but we drop packets that are too large. Perhaps we should truncate + * them instead? + Copied from 3c595. Is this true for the 3c515? + */ + if (len + pad > ETH_FRAME_LEN) { + return; + } + /* drop acknowledgements */ + while ((status = inb(nic->ioaddr + TxStatus)) & TxComplete) { + /*if(status & (TXS_UNDERRUN|0x88|TXS_STATUS_OVERFLOW)) { */ + outw(TxReset, nic->ioaddr + EL3_CMD); + outw(TxEnable, nic->ioaddr + EL3_CMD); +/* } */ + + outb(0x0, nic->ioaddr + TxStatus); + } + + while (inw(nic->ioaddr + TxFree) < len + pad + 4) { + /* no room in FIFO */ + } + + outw(len, nic->ioaddr + TX_FIFO); + outw(0x0, nic->ioaddr + TX_FIFO); /* Second dword meaningless */ + + /* write packet */ + outsw(nic->ioaddr + TX_FIFO, d, ETH_ALEN / 2); + outsw(nic->ioaddr + TX_FIFO, nic->node_addr, ETH_ALEN / 2); + outw(t, nic->ioaddr + TX_FIFO); + outsw(nic->ioaddr + TX_FIFO, p, s / 2); + + if (s & 1) + outb(*(p + s - 1), nic->ioaddr + TX_FIFO); + + while (pad--) + outb(0, nic->ioaddr + TX_FIFO); /* Padding */ + + /* wait for Tx complete */ + while ((inw(nic->ioaddr + EL3_STATUS) & CmdInProgress) != 0); +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void t515_disable ( struct nic *nic, + struct isapnp_device *isapnp ) { + + t515_reset(nic); + + /* This is a hack. Since ltsp worked on my + system without any disable functionality I + have no way to determine if this works */ + + /* Disable the receiver and transmitter. */ + outw(RxDisable, nic->ioaddr + EL3_CMD); + outw(TxDisable, nic->ioaddr + EL3_CMD); + + if (if_port == XCVR_10base2) + /* Turn off thinnet power. Green! */ + outw(StopCoax, nic->ioaddr + EL3_CMD); + + + outw(SetIntrEnb | 0x0000, nic->ioaddr + EL3_CMD); + + deactivate_isapnp_device ( isapnp ); + return; +} + +static void t515_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + +static struct nic_operations t515_operations = { + .connect = dummy_connect, + .poll = t515_poll, + .transmit = t515_transmit, + .irq = t515_irq, + +}; + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +You should omit the last argument struct pci_device * for a non-PCI NIC +***************************************************************************/ +static int t515_probe ( struct nic *nic, struct isapnp_device *isapnp ) { + + /* Direct copy from Beckers 3c515.c removing any ISAPNP sections */ + + nic->ioaddr = isapnp->ioaddr; + nic->irqno = isapnp->irqno; + activate_isapnp_device ( isapnp ); + + /* Check the resource configuration for a matching ioaddr. */ + if ((unsigned)(inw(nic->ioaddr + 0x2002) & 0x1f0) + != (nic->ioaddr & 0x1f0)) { + DBG ( "3c515 ioaddr mismatch\n" ); + return 0; + } + + /* Verify by reading the device ID from the EEPROM. */ + { + int timer; + outw(EEPROM_Read + 7, nic->ioaddr + Wn0EepromCmd); + /* Pause for at least 162 us. for the read to take place. */ + for (timer = 4; timer >= 0; timer--) { + t3c515_wait(1); + if ((inw(nic->ioaddr + Wn0EepromCmd) & 0x0200) == 0) + break; + } + if (inw(nic->ioaddr + Wn0EepromData) != 0x6d50) { + DBG ( "3c515 read incorrect vendor ID from EEPROM" ); + return 0; + } + + } + DBG ( "3c515 Resource configuration register 0x%X, DCR 0x%hX.\n", + inl(nic->ioaddr + 0x2002), inw(nic->ioaddr + 0x2000) ); + corkscrew_found_device(nic->ioaddr, nic->irqno, CORKSCREW_ID, + options, nic); + + t515_reset(nic); + nic->nic_op = &t515_operations; + return 1; +} + +static int +corkscrew_found_device(int ioaddr, int irq, + int product_index, int options, struct nic *nic) +{ + /* Direct copy from Becker 3c515.c with unecessary parts removed */ + vp->product_name = "3c515"; + vp->options = options; + if (options >= 0) { + vp->media_override = + ((options & 7) == 2) ? 0 : options & 7; + vp->full_duplex = (options & 8) ? 1 : 0; + vp->bus_master = (options & 16) ? 1 : 0; + } else { + vp->media_override = 7; + vp->full_duplex = 0; + vp->bus_master = 0; + } + + corkscrew_probe1(ioaddr, irq, product_index, nic); + return 0; +} + +static int +corkscrew_probe1(int ioaddr, int irq, int product_index __unused, + struct nic *nic) +{ + unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */ + int i; + + printf("3Com %s at 0x%hX, ", vp->product_name, ioaddr); + + /* Read the station address from the EEPROM. */ + EL3WINDOW(0); + for (i = 0; i < 0x18; i++) { + short *phys_addr = (short *) nic->node_addr; + int timer; + outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd); + /* Pause for at least 162 us. for the read to take place. */ + for (timer = 4; timer >= 0; timer--) { + t3c515_wait(1); + if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0) + break; + } + eeprom[i] = inw(ioaddr + Wn0EepromData); + DBG ( "Value %d: %hX ", i, eeprom[i] ); + checksum ^= eeprom[i]; + if (i < 3) + phys_addr[i] = htons(eeprom[i]); + } + checksum = (checksum ^ (checksum >> 8)) & 0xff; + if (checksum != 0x00) + printf(" ***INVALID CHECKSUM 0x%hX*** ", checksum); + + DBG ( "%s", eth_ntoa ( nic->node_addr ) ); + + if (eeprom[16] == 0x11c7) { /* Corkscrew */ + + } + printf(", IRQ %d\n", irq); + /* Tell them about an invalid IRQ. */ + if ( (irq <= 0 || irq > 15) ) { + DBG (" *** Warning: this IRQ is unlikely to work! ***\n" ); + } + + { + char *ram_split[] = { "5:3", "3:1", "1:1", "3:5" }; + union wn3_config config; + EL3WINDOW(3); + vp->available_media = inw(ioaddr + Wn3_Options); + config.i = inl(ioaddr + Wn3_Config); + DBG ( " Internal config register is %4.4x, " + "transceivers 0x%hX.\n", + config.i, inw(ioaddr + Wn3_Options) ); + printf + (" %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n", + 8 << config.u.ram_size, + config.u.ram_width ? "word" : "byte", + ram_split[config.u.ram_split], + config.u.autoselect ? "autoselect/" : "", + media_tbl[config.u.xcvr].name); + if_port = config.u.xcvr; + vp->default_media = config.u.xcvr; + vp->autoselect = config.u.autoselect; + } + if (vp->media_override != 7) { + printf(" Media override to transceiver type %d (%s).\n", + vp->media_override, + media_tbl[vp->media_override].name); + if_port = vp->media_override; + } + + vp->capabilities = eeprom[16]; + vp->full_bus_master_tx = (vp->capabilities & 0x20) ? 1 : 0; + /* Rx is broken at 10mbps, so we always disable it. */ + /* vp->full_bus_master_rx = 0; */ + vp->full_bus_master_rx = (vp->capabilities & 0x20) ? 1 : 0; + + return 0; +} + +static struct isapnp_device_id t515_adapters[] = { + { "3c515 (ISAPnP)", ISAPNP_VENDOR('T','C','M'), 0x5051 }, +}; + +ISAPNP_DRIVER ( t515_driver, t515_adapters ); + +DRIVER ( "3c515", nic_driver, isapnp_driver, t515_driver, + t515_probe, t515_disable ); + +ISA_ROM ( "3c515", "3c515 Fast EtherLink ISAPnP" ); diff --git a/gpxe/src/drivers/net/3c515.txt b/gpxe/src/drivers/net/3c515.txt new file mode 100644 index 0000000..8f7b3a7 --- /dev/null +++ b/gpxe/src/drivers/net/3c515.txt @@ -0,0 +1,31 @@ +3c515.c -- 3COM 3C515 Fast Etherlink ISA 10/100BASE-TX driver for etherboot +Copyright (C) 2002 Timothy Legge + +This driver is for the 3COM 3C515 Fast Etherlink ISA 10/100BASE-TX + +REVISION HISTORY: +================ +v0.10 4-17-2002 TJL Initial implementation. +v0.11 4-17-2002 TJL Cleanup of the code +v0.12 4-26-2002 TJL Added ISA Plug and Play for Non-PNP Bioses +v0.13 3-31-2003 TJL Fixed issue 1 and 2 below + +The driver is heavily based on the work of others are referenced in the 3c515.c file. + +ISA Plug and Play (ISAPNP) support has been added for Non-PNP Bioses. The ISAPNP code requires the defination of ISA_PNP as: + +#define ISA_PNP + +Issues: +======= +1) RESOLVED - When ISAPNP is defined, the etherboot probe is unable to find the card during the first probe. This is true even though the ISA PNP code actually found and activated the driver. + +2) RESOLVED - When ISA_PNP is defined, the etherboot probe finds the incorrect MAC address for the card. However, when the linux kernel boots and loads the linux 3c515 driver the correct MAC address is found. This means that with ISA_PNP defined, you require both MAC addresses defined in the /etc/dhcpd.conf file. The first MAC address allows the driver to load the LTSP Linux kernel. The second allows the Linux dhclient to resolve its IP address. + +3) Although the ISA PNP docs specify that the IRQ, DMA and IO Address needs to be assigned to the card before it is activated, Etherboot does not seem to care. Therefore the code does not assign the card with these values. + +If you can help address any of thse issues, please feel free. + +Timothy Legge +timlegge@users.sourceforge.net +April 9, 2003 diff --git a/gpxe/src/drivers/net/3c529.c b/gpxe/src/drivers/net/3c529.c new file mode 100644 index 0000000..3193104 --- /dev/null +++ b/gpxe/src/drivers/net/3c529.c @@ -0,0 +1,60 @@ +/* + * Split out from 3c509.c to make build process more sane + * + */ + +#include "etherboot.h" +#include +#include /* for ISA_ROM */ +#include "nic.h" +#include "3c509.h" + +/* + * Several other pieces of the MCA support code were shamelessly + * borrowed from the Linux kernel source. + * + * MCA support added by Adam Fritzler (mid@auk.cx) + * + * Generalised out of the 3c529 driver and into a bus type by Michael + * Brown + * + */ + +static int t529_probe ( struct nic *nic, struct mca_device *mca ) { + + /* Retrieve NIC parameters from MCA device parameters */ + nic->ioaddr = ( ( mca->pos[4] & 0xfc ) | 0x02 ) << 8; + nic->irqno = mca->pos[5] & 0x0f; + printf ( "3c529 board found on MCA at %#hx IRQ %d -", + nic->ioaddr, nic->irqno ); + + /* Hand off to generic t5x9 probe routine */ + return t5x9_probe ( nic, MCA_ID ( mca ), 0xffff ); +} + +static void t529_disable ( struct nic *nic, struct mca_device *mca __unused ) { + t5x9_disable ( nic ); +} + +static struct mca_device_id el3_mca_adapters[] = { + { "3Com 3c529 EtherLink III (10base2)", 0x627c }, + { "3Com 3c529 EtherLink III (10baseT)", 0x627d }, + { "3Com 3c529 EtherLink III (test mode)", 0x62db }, + { "3Com 3c529 EtherLink III (TP or coax)", 0x62f6 }, + { "3Com 3c529 EtherLink III (TP)", 0x62f7 }, +}; + +MCA_DRIVER ( t529_driver, el3_mca_adapters ); + +DRIVER ( "3c529", nic_driver, mca_driver, t529_driver, + t529_probe, t529_disable ); + +ISA_ROM( "3c529", "3c529 == MCA 3c509" ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/3c595.c b/gpxe/src/drivers/net/3c595.c new file mode 100644 index 0000000..198e12e --- /dev/null +++ b/gpxe/src/drivers/net/3c595.c @@ -0,0 +1,551 @@ +/* +* 3c595.c -- 3COM 3C595 Fast Etherlink III PCI driver for etherboot +* +* Copyright (C) 2000 Shusuke Nisiyama +* 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 +#include +#include "3c595.h" + +static struct nic_operations t595_operations; + +static unsigned short eth_nic_base; +static unsigned short vx_connector, vx_connectors; + +static struct connector_entry { + int bit; + char *name; +} conn_tab[VX_CONNECTORS] = { +#define CONNECTOR_UTP 0 + { 0x08, "utp"}, +#define CONNECTOR_AUI 1 + { 0x20, "aui"}, +/* dummy */ + { 0, "???"}, +#define CONNECTOR_BNC 3 + { 0x10, "bnc"}, +#define CONNECTOR_TX 4 + { 0x02, "tx"}, +#define CONNECTOR_FX 5 + { 0x04, "fx"}, +#define CONNECTOR_MII 6 + { 0x40, "mii"}, + { 0, "???"} +}; + +static void vxgetlink(void); +static void vxsetlink(void); + +/************************************************************************** +ETH_RESET - Reset adapter +***************************************************************************/ +static void t595_reset(struct nic *nic) +{ + int i; + + /*********************************************************** + Reset 3Com 595 card + *************************************************************/ + + /* stop card */ + outw(RX_DISABLE, BASE + VX_COMMAND); + outw(RX_DISCARD_TOP_PACK, BASE + VX_COMMAND); + VX_BUSY_WAIT; + outw(TX_DISABLE, BASE + VX_COMMAND); + outw(STOP_TRANSCEIVER, BASE + VX_COMMAND); + udelay(8000); + outw(RX_RESET, BASE + VX_COMMAND); + VX_BUSY_WAIT; + outw(TX_RESET, BASE + VX_COMMAND); + VX_BUSY_WAIT; + outw(C_INTR_LATCH, BASE + VX_COMMAND); + outw(SET_RD_0_MASK, BASE + VX_COMMAND); + outw(SET_INTR_MASK, BASE + VX_COMMAND); + outw(SET_RX_FILTER, BASE + VX_COMMAND); + + /* + * initialize card + */ + VX_BUSY_WAIT; + + GO_WINDOW(0); + + /* Disable the card */ +/* outw(0, BASE + VX_W0_CONFIG_CTRL); */ + + /* Configure IRQ to none */ +/* outw(SET_IRQ(0), BASE + VX_W0_RESOURCE_CFG); */ + + /* Enable the card */ +/* outw(ENABLE_DRQ_IRQ, BASE + VX_W0_CONFIG_CTRL); */ + + GO_WINDOW(2); + + /* Reload the ether_addr. */ + for (i = 0; i < ETH_ALEN; i++) + outb(nic->node_addr[i], BASE + VX_W2_ADDR_0 + i); + + outw(RX_RESET, BASE + VX_COMMAND); + VX_BUSY_WAIT; + outw(TX_RESET, BASE + VX_COMMAND); + VX_BUSY_WAIT; + + /* Window 1 is operating window */ + GO_WINDOW(1); + for (i = 0; i < 31; i++) + inb(BASE + VX_W1_TX_STATUS); + + outw(SET_RD_0_MASK | S_CARD_FAILURE | S_RX_COMPLETE | + S_TX_COMPLETE | S_TX_AVAIL, BASE + VX_COMMAND); + outw(SET_INTR_MASK | S_CARD_FAILURE | S_RX_COMPLETE | + S_TX_COMPLETE | S_TX_AVAIL, BASE + VX_COMMAND); + +/* + * Attempt to get rid of any stray interrupts that occured during + * configuration. On the i386 this isn't possible because one may + * already be queued. However, a single stray interrupt is + * unimportant. + */ + + outw(ACK_INTR | 0xff, BASE + VX_COMMAND); + + outw(SET_RX_FILTER | FIL_INDIVIDUAL | + FIL_BRDCST|FIL_MULTICAST, BASE + VX_COMMAND); + + vxsetlink(); +/*{ + int i,j; + i = CONNECTOR_TX; + GO_WINDOW(3); + j = inl(BASE + VX_W3_INTERNAL_CFG) & ~INTERNAL_CONNECTOR_MASK; + outl(BASE + VX_W3_INTERNAL_CFG, j | (i < 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("%s", 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 = pci->ioaddr; + + nic->irqno = 0; + nic->ioaddr = pci->ioaddr; + + GO_WINDOW(0); + outw(GLOBAL_RESET, BASE + VX_COMMAND); + VX_BUSY_WAIT; + + vxgetlink(); + +/* + printf("\nEEPROM:"); + for (i = 0; i < (EEPROMSIZE/2); i++) { + printf("%hX:", get_e(i)); + } + printf("\n"); +*/ + /* + * Read the station address from the eeprom + */ + p = (unsigned short *) nic->node_addr; + for (i = 0; i < 3; i++) { + GO_WINDOW(0); + p[i] = htons(get_e(EEPROM_OEM_ADDR_0 + i)); + GO_WINDOW(2); + outw(ntohs(p[i]), BASE + VX_W2_ADDR_0 + (i * 2)); + } + + DBG ( "Ethernet address: %s\n", eth_ntoa (nic->node_addr) ); + + t595_reset(nic); + nic->nic_op = &t595_operations; + return 1; + +} + +static struct nic_operations t595_operations = { + .connect = dummy_connect, + .poll = t595_poll, + .transmit = t595_transmit, + .irq = t595_irq, + +}; + +static struct pci_device_id t595_nics[] = { +PCI_ROM(0x10b7, 0x5900, "3c590", "3Com590"), /* Vortex 10Mbps */ +PCI_ROM(0x10b7, 0x5950, "3c595", "3Com595"), /* Vortex 100baseTx */ +PCI_ROM(0x10b7, 0x5951, "3c595-1", "3Com595"), /* Vortex 100baseT4 */ +PCI_ROM(0x10b7, 0x5952, "3c595-2", "3Com595"), /* Vortex 100base-MII */ +PCI_ROM(0x10b7, 0x9000, "3c900-tpo", "3Com900-TPO"), /* 10 Base TPO */ +PCI_ROM(0x10b7, 0x9001, "3c900-t4", "3Com900-Combo"), /* 10/100 T4 */ +PCI_ROM(0x10b7, 0x9004, "3c900b-tpo", "3Com900B-TPO"), /* 10 Base TPO */ +PCI_ROM(0x10b7, 0x9005, "3c900b-combo", "3Com900B-Combo"), /* 10 Base Combo */ +PCI_ROM(0x10b7, 0x9006, "3c900b-tpb2", "3Com900B-2/T"), /* 10 Base TP and Base2 */ +PCI_ROM(0x10b7, 0x900a, "3c900b-fl", "3Com900B-FL"), /* 10 Base F */ +PCI_ROM(0x10b7, 0x9800, "3c980-cyclone-1", "3Com980-Cyclone"), /* Cyclone */ +PCI_ROM(0x10b7, 0x9805, "3c9805-1", "3Com9805"), /* Dual Port Server Cyclone */ +PCI_ROM(0x10b7, 0x7646, "3csoho100-tx-1", "3CSOHO100-TX"), /* Hurricane */ +PCI_ROM(0x10b7, 0x4500, "3c450-1", "3Com450 HomePNA Tornado"), +}; + +PCI_DRIVER ( t595_driver, t595_nics, PCI_NO_CLASS ); + +DRIVER ( "3C595", nic_driver, pci_driver, t595_driver, + t595_probe, t595_disable ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/3c595.h b/gpxe/src/drivers/net/3c595.h new file mode 100644 index 0000000..49d8d9b --- /dev/null +++ b/gpxe/src/drivers/net/3c595.h @@ -0,0 +1,435 @@ +/* + * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. 2. The name + * of the author may not be used to endorse or promote products derived from + * this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + October 2, 1994 + + Modified by: Andres Vega Garcia + + INRIA - Sophia Antipolis, France + e-mail: avega@sophia.inria.fr + finger: avega@pax.inria.fr + + */ + +/* + * Created from if_epreg.h by Fred Gray (fgray@rice.edu) to support the + * 3c590 family. + */ + +/* + * Modified by Shusuke Nisiyama + * for etherboot + * Mar. 14, 2000 +*/ + +/* + * Ethernet software status per interface. + */ + +/* + * Some global constants + */ + +#define TX_INIT_RATE 16 +#define TX_INIT_MAX_RATE 64 +#define RX_INIT_LATENCY 64 +#define RX_INIT_EARLY_THRESH 64 +#define MIN_RX_EARLY_THRESHF 16 /* not less than ether_header */ +#define MIN_RX_EARLY_THRESHL 4 + +#define EEPROMSIZE 0x40 +#define MAX_EEPROMBUSY 1000 +#define VX_LAST_TAG 0xd7 +#define VX_MAX_BOARDS 16 +#define VX_ID_PORT 0x100 + +/* + * some macros to acces long named fields + */ +#define BASE (eth_nic_base) + +/* + * Commands to read/write EEPROM trough EEPROM command register (Window 0, + * Offset 0xa) + */ +#define EEPROM_CMD_RD 0x0080 /* Read: Address required (5 bits) */ +#define EEPROM_CMD_WR 0x0040 /* Write: Address required (5 bits) */ +#define EEPROM_CMD_ERASE 0x00c0 /* Erase: Address required (5 bits) */ +#define EEPROM_CMD_EWEN 0x0030 /* Erase/Write Enable: No data required */ + +#define EEPROM_BUSY (1<<15) + +/* + * Some short functions, worth to let them be a macro + */ + +/************************************************************************** + * * + * These define the EEPROM data structure. They are used in the probe + * function to verify the existence of the adapter after having sent + * the ID_Sequence. + * + * There are others but only the ones we use are defined here. + * + **************************************************************************/ + +#define EEPROM_NODE_ADDR_0 0x0 /* Word */ +#define EEPROM_NODE_ADDR_1 0x1 /* Word */ +#define EEPROM_NODE_ADDR_2 0x2 /* Word */ +#define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */ +#define EEPROM_MFG_ID 0x7 /* 0x6d50 */ +#define EEPROM_ADDR_CFG 0x8 /* Base addr */ +#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */ +#define EEPROM_OEM_ADDR_0 0xa /* Word */ +#define EEPROM_OEM_ADDR_1 0xb /* Word */ +#define EEPROM_OEM_ADDR_2 0xc /* Word */ +#define EEPROM_SOFT_INFO_2 0xf /* Software information 2 */ + +#define NO_RX_OVN_ANOMALY (1<<5) + +/************************************************************************** + * * + * These are the registers for the 3Com 3c509 and their bit patterns when * + * applicable. They have been taken out the the "EtherLink III Parallel * + * Tasking EISA and ISA Technical Reference" "Beta Draft 10/30/92" manual * + * from 3com. * + * * + **************************************************************************/ + +#define VX_COMMAND 0x0e /* Write. BASE+0x0e is always a + * command reg. */ +#define VX_STATUS 0x0e /* Read. BASE+0x0e is always status + * reg. */ +#define VX_WINDOW 0x0f /* Read. BASE+0x0f is always window + * reg. */ +/* + * Window 0 registers. Setup. + */ +/* Write */ +#define VX_W0_EEPROM_DATA 0x0c +#define VX_W0_EEPROM_COMMAND 0x0a +#define VX_W0_RESOURCE_CFG 0x08 +#define VX_W0_ADDRESS_CFG 0x06 +#define VX_W0_CONFIG_CTRL 0x04 + /* Read */ +#define VX_W0_PRODUCT_ID 0x02 +#define VX_W0_MFG_ID 0x00 + + +/* + * Window 1 registers. Operating Set. + */ +/* Write */ +#define VX_W1_TX_PIO_WR_2 0x02 +#define VX_W1_TX_PIO_WR_1 0x00 +/* Read */ +#define VX_W1_FREE_TX 0x0c +#define VX_W1_TX_STATUS 0x0b /* byte */ +#define VX_W1_TIMER 0x0a /* byte */ +#define VX_W1_RX_STATUS 0x08 +#define VX_W1_RX_PIO_RD_2 0x02 +#define VX_W1_RX_PIO_RD_1 0x00 + +/* + * Window 2 registers. Station Address Setup/Read + */ +/* Read/Write */ +#define VX_W2_ADDR_5 0x05 +#define VX_W2_ADDR_4 0x04 +#define VX_W2_ADDR_3 0x03 +#define VX_W2_ADDR_2 0x02 +#define VX_W2_ADDR_1 0x01 +#define VX_W2_ADDR_0 0x00 + +/* + * Window 3 registers. FIFO Management. + */ +/* Read */ +#define VX_W3_INTERNAL_CFG 0x00 +#define VX_W3_RESET_OPT 0x08 +#define VX_W3_FREE_TX 0x0c +#define VX_W3_FREE_RX 0x0a + +/* + * Window 4 registers. Diagnostics. + */ +/* Read/Write */ +#define VX_W4_MEDIA_TYPE 0x0a +#define VX_W4_CTRLR_STATUS 0x08 +#define VX_W4_NET_DIAG 0x06 +#define VX_W4_FIFO_DIAG 0x04 +#define VX_W4_HOST_DIAG 0x02 +#define VX_W4_TX_DIAG 0x00 + +/* + * Window 5 Registers. Results and Internal status. + */ +/* Read */ +#define VX_W5_READ_0_MASK 0x0c +#define VX_W5_INTR_MASK 0x0a +#define VX_W5_RX_FILTER 0x08 +#define VX_W5_RX_EARLY_THRESH 0x06 +#define VX_W5_TX_AVAIL_THRESH 0x02 +#define VX_W5_TX_START_THRESH 0x00 + +/* + * Window 6 registers. Statistics. + */ +/* Read/Write */ +#define TX_TOTAL_OK 0x0c +#define RX_TOTAL_OK 0x0a +#define TX_DEFERRALS 0x08 +#define RX_FRAMES_OK 0x07 +#define TX_FRAMES_OK 0x06 +#define RX_OVERRUNS 0x05 +#define TX_COLLISIONS 0x04 +#define TX_AFTER_1_COLLISION 0x03 +#define TX_AFTER_X_COLLISIONS 0x02 +#define TX_NO_SQE 0x01 +#define TX_CD_LOST 0x00 + +/**************************************** + * + * Register definitions. + * + ****************************************/ + +/* + * Command register. All windows. + * + * 16 bit register. + * 15-11: 5-bit code for command to be executed. + * 10-0: 11-bit arg if any. For commands with no args; + * this can be set to anything. + */ +#define GLOBAL_RESET (unsigned short) 0x0000 /* Wait at least 1ms + * after issuing */ +#define WINDOW_SELECT (unsigned short) (0x1<<11) +#define START_TRANSCEIVER (unsigned short) (0x2<<11) /* Read ADDR_CFG reg to + * determine whether + * this is needed. If + * so; wait 800 uSec + * before using trans- + * ceiver. */ +#define RX_DISABLE (unsigned short) (0x3<<11) /* state disabled on + * power-up */ +#define RX_ENABLE (unsigned short) (0x4<<11) +#define RX_RESET (unsigned short) (0x5<<11) +#define RX_DISCARD_TOP_PACK (unsigned short) (0x8<<11) +#define TX_ENABLE (unsigned short) (0x9<<11) +#define TX_DISABLE (unsigned short) (0xa<<11) +#define TX_RESET (unsigned short) (0xb<<11) +#define REQ_INTR (unsigned short) (0xc<<11) +/* + * The following C_* acknowledge the various interrupts. Some of them don't + * do anything. See the manual. + */ +#define ACK_INTR (unsigned short) (0x6800) +# define C_INTR_LATCH (unsigned short) (ACK_INTR|0x1) +# define C_CARD_FAILURE (unsigned short) (ACK_INTR|0x2) +# define C_TX_COMPLETE (unsigned short) (ACK_INTR|0x4) +# define C_TX_AVAIL (unsigned short) (ACK_INTR|0x8) +# define C_RX_COMPLETE (unsigned short) (ACK_INTR|0x10) +# define C_RX_EARLY (unsigned short) (ACK_INTR|0x20) +# define C_INT_RQD (unsigned short) (ACK_INTR|0x40) +# define C_UPD_STATS (unsigned short) (ACK_INTR|0x80) +#define SET_INTR_MASK (unsigned short) (0xe<<11) +#define SET_RD_0_MASK (unsigned short) (0xf<<11) +#define SET_RX_FILTER (unsigned short) (0x10<<11) +# define FIL_INDIVIDUAL (unsigned short) (0x1) +# define FIL_MULTICAST (unsigned short) (0x02) +# define FIL_BRDCST (unsigned short) (0x04) +# define FIL_PROMISC (unsigned short) (0x08) +#define SET_RX_EARLY_THRESH (unsigned short) (0x11<<11) +#define SET_TX_AVAIL_THRESH (unsigned short) (0x12<<11) +#define SET_TX_START_THRESH (unsigned short) (0x13<<11) +#define STATS_ENABLE (unsigned short) (0x15<<11) +#define STATS_DISABLE (unsigned short) (0x16<<11) +#define STOP_TRANSCEIVER (unsigned short) (0x17<<11) + +/* + * Status register. All windows. + * + * 15-13: Window number(0-7). + * 12: Command_in_progress. + * 11: reserved. + * 10: reserved. + * 9: reserved. + * 8: reserved. + * 7: Update Statistics. + * 6: Interrupt Requested. + * 5: RX Early. + * 4: RX Complete. + * 3: TX Available. + * 2: TX Complete. + * 1: Adapter Failure. + * 0: Interrupt Latch. + */ +#define S_INTR_LATCH (unsigned short) (0x1) +#define S_CARD_FAILURE (unsigned short) (0x2) +#define S_TX_COMPLETE (unsigned short) (0x4) +#define S_TX_AVAIL (unsigned short) (0x8) +#define S_RX_COMPLETE (unsigned short) (0x10) +#define S_RX_EARLY (unsigned short) (0x20) +#define S_INT_RQD (unsigned short) (0x40) +#define S_UPD_STATS (unsigned short) (0x80) +#define S_COMMAND_IN_PROGRESS (unsigned short) (0x1000) + +#define VX_BUSY_WAIT while (inw(BASE + VX_STATUS) & S_COMMAND_IN_PROGRESS) + +/* Address Config. Register. + * Window 0/Port 06 + */ + +#define ACF_CONNECTOR_BITS 14 +#define ACF_CONNECTOR_UTP 0 +#define ACF_CONNECTOR_AUI 1 +#define ACF_CONNECTOR_BNC 3 + +#define INTERNAL_CONNECTOR_BITS 20 +#define INTERNAL_CONNECTOR_MASK 0x01700000 + +/* + * FIFO Registers. RX Status. + * + * 15: Incomplete or FIFO empty. + * 14: 1: Error in RX Packet 0: Incomplete or no error. + * 13-11: Type of error. + * 1000 = Overrun. + * 1011 = Run Packet Error. + * 1100 = Alignment Error. + * 1101 = CRC Error. + * 1001 = Oversize Packet Error (>1514 bytes) + * 0010 = Dribble Bits. + * (all other error codes, no errors.) + * + * 10-0: RX Bytes (0-1514) + */ +#define ERR_INCOMPLETE (unsigned short) (0x8000) +#define ERR_RX (unsigned short) (0x4000) +#define ERR_MASK (unsigned short) (0x7800) +#define ERR_OVERRUN (unsigned short) (0x4000) +#define ERR_RUNT (unsigned short) (0x5800) +#define ERR_ALIGNMENT (unsigned short) (0x6000) +#define ERR_CRC (unsigned short) (0x6800) +#define ERR_OVERSIZE (unsigned short) (0x4800) +#define ERR_DRIBBLE (unsigned short) (0x1000) + +/* + * TX Status. + * + * Reports the transmit status of a completed transmission. Writing this + * register pops the transmit completion stack. + * + * Window 1/Port 0x0b. + * + * 7: Complete + * 6: Interrupt on successful transmission requested. + * 5: Jabber Error (TP Only, TX Reset required. ) + * 4: Underrun (TX Reset required. ) + * 3: Maximum Collisions. + * 2: TX Status Overflow. + * 1-0: Undefined. + * + */ +#define TXS_COMPLETE 0x80 +#define TXS_INTR_REQ 0x40 +#define TXS_JABBER 0x20 +#define TXS_UNDERRUN 0x10 +#define TXS_MAX_COLLISION 0x8 +#define TXS_STATUS_OVERFLOW 0x4 + +#define RS_AUI (1<<5) +#define RS_BNC (1<<4) +#define RS_UTP (1<<3) +#define RS_T4 (1<<0) +#define RS_TX (1<<1) +#define RS_FX (1<<2) +#define RS_MII (1<<6) + + +/* + * FIFO Status (Window 4) + * + * Supports FIFO diagnostics + * + * Window 4/Port 0x04.1 + * + * 15: 1=RX receiving (RO). Set when a packet is being received + * into the RX FIFO. + * 14: Reserved + * 13: 1=RX underrun (RO). Generates Adapter Failure interrupt. + * Requires RX Reset or Global Reset command to recover. + * It is generated when you read past the end of a packet - + * reading past what has been received so far will give bad + * data. + * 12: 1=RX status overrun (RO). Set when there are already 8 + * packets in the RX FIFO. While this bit is set, no additional + * packets are received. Requires no action on the part of + * the host. The condition is cleared once a packet has been + * read out of the RX FIFO. + * 11: 1=RX overrun (RO). Set when the RX FIFO is full (there + * may not be an overrun packet yet). While this bit is set, + * no additional packets will be received (some additional + * bytes can still be pending between the wire and the RX + * FIFO). Requires no action on the part of the host. The + * condition is cleared once a few bytes have been read out + * from the RX FIFO. + * 10: 1=TX overrun (RO). Generates adapter failure interrupt. + * Requires TX Reset or Global Reset command to recover. + * Disables Transmitter. + * 9-8: Unassigned. + * 7-0: Built in self test bits for the RX and TX FIFO's. + */ +#define FIFOS_RX_RECEIVING (unsigned short) 0x8000 +#define FIFOS_RX_UNDERRUN (unsigned short) 0x2000 +#define FIFOS_RX_STATUS_OVERRUN (unsigned short) 0x1000 +#define FIFOS_RX_OVERRUN (unsigned short) 0x0800 +#define FIFOS_TX_OVERRUN (unsigned short) 0x0400 + +/* + * Misc defines for various things. + */ +#define TAG_ADAPTER 0xd0 +#define ACTIVATE_ADAPTER_TO_CONFIG 0xff +#define ENABLE_DRQ_IRQ 0x0001 +#define MFG_ID 0x506d /* `TCM' */ +#define PROD_ID 0x5090 +#define GO_WINDOW(x) outw(WINDOW_SELECT|(x),BASE+VX_COMMAND) +#define JABBER_GUARD_ENABLE 0x40 +#define LINKBEAT_ENABLE 0x80 +#define ENABLE_UTP (JABBER_GUARD_ENABLE | LINKBEAT_ENABLE) +#define DISABLE_UTP 0x0 +#define RX_BYTES_MASK (unsigned short) (0x07ff) +#define RX_ERROR 0x4000 +#define RX_INCOMPLETE 0x8000 +#define TX_INDICATE 1<<15 +#define is_eeprom_busy(b) (inw((b)+VX_W0_EEPROM_COMMAND)&EEPROM_BUSY) + +#define VX_IOSIZE 0x20 + +#define VX_CONNECTORS 8 + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/3c5x9.c b/gpxe/src/drivers/net/3c5x9.c new file mode 100644 index 0000000..565044a --- /dev/null +++ b/gpxe/src/drivers/net/3c5x9.c @@ -0,0 +1,414 @@ +/************************************************************************** +ETHERBOOT - BOOTP/TFTP Bootstrap Program + +Author: Martin Renters. + Date: Mar 22 1995 + + This code is based heavily on David Greenman's if_ed.c driver and + Andres Vega Garcia's if_ep.c driver. + + Copyright (C) 1993-1994, David Greenman, Martin Renters. + Copyright (C) 1993-1995, Andres Vega Garcia. + Copyright (C) 1995, Serge Babkin. + This software may be used, modified, copied, distributed, and sold, in + both source and binary form provided that the above copyright and these + terms are retained. Under no circumstances are the authors responsible for + the proper functioning of this software, nor do the authors assume any + responsibility for damages incurred with its use. + +3c509 support added by Serge Babkin (babkin@hq.icb.chel.su) + +$Id$ + +***************************************************************************/ + +/* #define EDEBUG */ + +#include +#include "etherboot.h" +#include "nic.h" +#include +#include "3c509.h" + +static enum { none, bnc, utp } connector = none; /* for 3C509 */ + +/************************************************************************** +ETH_RESET - Reset adapter +***************************************************************************/ +void t5x9_disable ( struct nic *nic ) { + /* stop card */ + outw(RX_DISABLE, nic->ioaddr + EP_COMMAND); + outw(RX_DISCARD_TOP_PACK, nic->ioaddr + EP_COMMAND); + while (inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS) + ; + outw(TX_DISABLE, nic->ioaddr + EP_COMMAND); + outw(STOP_TRANSCEIVER, nic->ioaddr + EP_COMMAND); + udelay(1000); + outw(RX_RESET, nic->ioaddr + EP_COMMAND); + outw(TX_RESET, nic->ioaddr + EP_COMMAND); + outw(C_INTR_LATCH, nic->ioaddr + EP_COMMAND); + outw(SET_RD_0_MASK, nic->ioaddr + EP_COMMAND); + outw(SET_INTR_MASK, nic->ioaddr + EP_COMMAND); + outw(SET_RX_FILTER, nic->ioaddr + EP_COMMAND); + + /* + * wait for reset to complete + */ + while (inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS) + ; + + GO_WINDOW(nic->ioaddr,0); + + /* Disable the card */ + outw(0, nic->ioaddr + EP_W0_CONFIG_CTRL); + + /* Configure IRQ to none */ + outw(SET_IRQ(0), nic->ioaddr + EP_W0_RESOURCE_CFG); +} + +static void t509_enable ( struct nic *nic ) { + int i; + + /* Enable the card */ + GO_WINDOW(nic->ioaddr,0); + outw(ENABLE_DRQ_IRQ, nic->ioaddr + EP_W0_CONFIG_CTRL); + + GO_WINDOW(nic->ioaddr,2); + + /* Reload the ether_addr. */ + for (i = 0; i < ETH_ALEN; i++) + outb(nic->node_addr[i], nic->ioaddr + EP_W2_ADDR_0 + i); + + outw(RX_RESET, nic->ioaddr + EP_COMMAND); + outw(TX_RESET, nic->ioaddr + EP_COMMAND); + + /* Window 1 is operating window */ + GO_WINDOW(nic->ioaddr,1); + for (i = 0; i < 31; i++) + inb(nic->ioaddr + EP_W1_TX_STATUS); + + /* get rid of stray intr's */ + outw(ACK_INTR | 0xff, nic->ioaddr + EP_COMMAND); + + outw(SET_RD_0_MASK | S_5_INTS, nic->ioaddr + EP_COMMAND); + + outw(SET_INTR_MASK, nic->ioaddr + EP_COMMAND); + + outw(SET_RX_FILTER | FIL_GROUP | FIL_INDIVIDUAL | FIL_BRDCST, + nic->ioaddr + EP_COMMAND); + + /* configure BNC */ + if (connector == bnc) { + outw(START_TRANSCEIVER, nic->ioaddr + EP_COMMAND); + udelay(1000); + } + /* configure UTP */ + else if (connector == utp) { + GO_WINDOW(nic->ioaddr,4); + outw(ENABLE_UTP, nic->ioaddr + EP_W4_MEDIA_TYPE); + sleep(2); /* Give time for media to negotiate */ + GO_WINDOW(nic->ioaddr,1); + } + + /* start transceiver and receiver */ + outw(RX_ENABLE, nic->ioaddr + EP_COMMAND); + outw(TX_ENABLE, nic->ioaddr + EP_COMMAND); + + /* set early threshold for minimal packet length */ + outw(SET_RX_EARLY_THRESH | ETH_ZLEN, nic->ioaddr + EP_COMMAND); + outw(SET_TX_START_THRESH | 16, nic->ioaddr + EP_COMMAND); +} + +static void t509_reset ( struct nic *nic ) { + t5x9_disable ( nic ); + t509_enable ( nic ); +} + +/************************************************************************** +ETH_TRANSMIT - Transmit a frame +***************************************************************************/ +static char padmap[] = { + 0, 3, 2, 1}; + +static void t509_transmit( +struct nic *nic, +const char *d, /* Destination */ +unsigned int t, /* Type */ +unsigned int s, /* size */ +const char *p) /* Packet */ +{ + register unsigned int len; + int pad; + int status; + +#ifdef EDEBUG + printf("{l=%d,t=%hX}",s+ETH_HLEN,t); +#endif + + /* swap bytes of type */ + t= htons(t); + + len=s+ETH_HLEN; /* actual length of packet */ + pad = padmap[len & 3]; + + /* + * The 3c509 automatically pads short packets to minimum ethernet length, + * but we drop packets that are too large. Perhaps we should truncate + * them instead? + */ + if (len + pad > ETH_FRAME_LEN) { + return; + } + + /* drop acknowledgements */ + while ((status=inb(nic->ioaddr + EP_W1_TX_STATUS)) & TXS_COMPLETE ) { + if (status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) { + outw(TX_RESET, nic->ioaddr + EP_COMMAND); + outw(TX_ENABLE, nic->ioaddr + EP_COMMAND); + } + outb(0x0, nic->ioaddr + EP_W1_TX_STATUS); + } + + while (inw(nic->ioaddr + EP_W1_FREE_TX) < (unsigned short)len + pad + 4) + ; /* no room in FIFO */ + + outw(len, nic->ioaddr + EP_W1_TX_PIO_WR_1); + outw(0x0, nic->ioaddr + EP_W1_TX_PIO_WR_1); /* Second dword meaningless */ + + /* write packet */ + outsw(nic->ioaddr + EP_W1_TX_PIO_WR_1, d, ETH_ALEN/2); + outsw(nic->ioaddr + EP_W1_TX_PIO_WR_1, nic->node_addr, ETH_ALEN/2); + outw(t, nic->ioaddr + EP_W1_TX_PIO_WR_1); + outsw(nic->ioaddr + EP_W1_TX_PIO_WR_1, p, s / 2); + if (s & 1) + outb(*(p+s - 1), nic->ioaddr + EP_W1_TX_PIO_WR_1); + + while (pad--) + outb(0, nic->ioaddr + EP_W1_TX_PIO_WR_1); /* Padding */ + + /* wait for Tx complete */ + while((inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS) != 0) + ; +} + +/************************************************************************** +ETH_POLL - Wait for a frame +***************************************************************************/ +static int t509_poll(struct nic *nic, int retrieve) +{ + /* common variables */ + /* variables for 3C509 */ + short status, cst; + register short rx_fifo; + + cst=inw(nic->ioaddr + EP_STATUS); + +#ifdef EDEBUG + if(cst & 0x1FFF) + printf("-%hX-",cst); +#endif + + if( (cst & S_RX_COMPLETE)==0 ) { + /* acknowledge everything */ + outw(ACK_INTR| (cst & S_5_INTS), nic->ioaddr + EP_COMMAND); + outw(C_INTR_LATCH, nic->ioaddr + EP_COMMAND); + + return 0; + } + + status = inw(nic->ioaddr + EP_W1_RX_STATUS); +#ifdef EDEBUG + printf("*%hX*",status); +#endif + + if (status & ERR_RX) { + outw(RX_DISCARD_TOP_PACK, nic->ioaddr + EP_COMMAND); + return 0; + } + + rx_fifo = status & RX_BYTES_MASK; + if (rx_fifo==0) + return 0; + + if ( ! retrieve ) return 1; + + /* read packet */ +#ifdef EDEBUG + printf("[l=%d",rx_fifo); +#endif + insw(nic->ioaddr + EP_W1_RX_PIO_RD_1, nic->packet, rx_fifo / 2); + if(rx_fifo & 1) + nic->packet[rx_fifo-1]=inb(nic->ioaddr + EP_W1_RX_PIO_RD_1); + nic->packetlen=rx_fifo; + + while(1) { + status = inw(nic->ioaddr + EP_W1_RX_STATUS); +#ifdef EDEBUG + printf("*%hX*",status); +#endif + rx_fifo = status & RX_BYTES_MASK; + if(rx_fifo>0) { + insw(nic->ioaddr + EP_W1_RX_PIO_RD_1, nic->packet+nic->packetlen, rx_fifo / 2); + if(rx_fifo & 1) + nic->packet[nic->packetlen+rx_fifo-1]=inb(nic->ioaddr + EP_W1_RX_PIO_RD_1); + nic->packetlen+=rx_fifo; +#ifdef EDEBUG + printf("+%d",rx_fifo); +#endif + } + if(( status & RX_INCOMPLETE )==0) { +#ifdef EDEBUG + printf("=%d",nic->packetlen); +#endif + break; + } + udelay(1000); /* if incomplete wait 1 ms */ + } + /* acknowledge reception of packet */ + outw(RX_DISCARD_TOP_PACK, nic->ioaddr + EP_COMMAND); + while (inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS) + ; +#ifdef EDEBUG +{ + unsigned short type = 0; /* used by EDEBUG */ + type = (nic->packet[12]<<8) | nic->packet[13]; + if(nic->packet[0]+nic->packet[1]+nic->packet[2]+nic->packet[3]+nic->packet[4]+ + nic->packet[5] == 0xFF*ETH_ALEN) + printf(",t=%hX,b]",type); + else + printf(",t=%hX]",type); +} +#endif + return (1); +} + +/************************************************************************** +ETH_IRQ - interrupt handling +***************************************************************************/ +static void t509_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + +/************************************************************************* + 3Com 509 - specific routines +**************************************************************************/ + +static int eeprom_rdy ( uint16_t ioaddr ) { + int i; + + for (i = 0; is_eeprom_busy(ioaddr) && i < MAX_EEPROMBUSY; i++); + if (i >= MAX_EEPROMBUSY) { + /* printf("3c509: eeprom failed to come ready.\n"); */ + /* memory in EPROM is tight */ + /* printf("3c509: eeprom busy.\n"); */ + return (0); + } + return (1); +} + +/* + * get_e: gets a 16 bits word from the EEPROM. + */ +static int get_e ( uint16_t ioaddr, int offset ) { + GO_WINDOW(ioaddr,0); + if (!eeprom_rdy(ioaddr)) + return (0xffff); + outw(EEPROM_CMD_RD | offset, ioaddr + EP_W0_EEPROM_COMMAND); + if (!eeprom_rdy(ioaddr)) + return (0xffff); + return (inw(ioaddr + EP_W0_EEPROM_DATA)); +} + +static struct nic_operations t509_operations = { + .connect = dummy_connect, + .poll = t509_poll, + .transmit = t509_transmit, + .irq = t509_irq, +}; + +/************************************************************************** +ETH_PROBE - Look for an adapter +***************************************************************************/ +int t5x9_probe ( struct nic *nic, + uint16_t prod_id_check, uint16_t prod_id_mask ) { + uint16_t prod_id; + int i,j; + unsigned short *p; + + /* Check product ID */ + prod_id = get_e ( nic->ioaddr, EEPROM_PROD_ID ); + if ( ( prod_id & prod_id_mask ) != prod_id_check ) { + printf ( "EEPROM Product ID is incorrect (%hx & %hx != %hx)\n", + prod_id, prod_id_mask, prod_id_check ); + return 0; + } + + /* test for presence of connectors */ + GO_WINDOW(nic->ioaddr,0); + i = inw(nic->ioaddr + EP_W0_CONFIG_CTRL); + j = (inw(nic->ioaddr + EP_W0_ADDRESS_CFG) >> 14) & 0x3; + + switch(j) { + case 0: + if (i & IS_UTP) { + printf("10baseT\n"); + connector = utp; + } else { + printf("10baseT not present\n"); + return 0; + } + break; + case 1: + if (i & IS_AUI) { + printf("10base5\n"); + } else { + printf("10base5 not present\n"); + return 0; + } + break; + case 3: + if (i & IS_BNC) { + printf("10base2\n"); + connector = bnc; + } else { + printf("10base2 not present\n"); + return 0; + } + break; + default: + printf("unknown connector\n"); + return 0; + } + + /* + * Read the station address from the eeprom + */ + p = (unsigned short *) nic->node_addr; + for (i = 0; i < ETH_ALEN / 2; i++) { + p[i] = htons(get_e(nic->ioaddr,i)); + GO_WINDOW(nic->ioaddr,2); + outw(ntohs(p[i]), nic->ioaddr + EP_W2_ADDR_0 + (i * 2)); + } + + DBG ( "Ethernet Address: %s\n", eth_ntoa ( nic->node_addr ) ); + + t509_reset(nic); + + nic->nic_op = &t509_operations; + return 1; + +} + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/3c90x.c b/gpxe/src/drivers/net/3c90x.c new file mode 100644 index 0000000..a98e662 --- /dev/null +++ b/gpxe/src/drivers/net/3c90x.c @@ -0,0 +1,1027 @@ +/* + * 3c90x.c -- This file implements the 3c90x driver for etherboot. Written + * by Greg Beeley, Greg.Beeley@LightSys.org. Modified by Steve Smith, + * Steve.Smith@Juno.Com. Alignment bug fix Neil Newell (nn@icenoir.net). + * + * This program Copyright (C) 1999 LightSys Technology Services, Inc. + * Portions Copyright (C) 1999 Steve Smith + * + * This program may be re-distributed in source or binary form, modified, + * sold, or copied for any purpose, provided that the above copyright message + * and this text are included with all source copies or derivative works, and + * provided that the above copyright message and this text are included in the + * documentation of any binary-only distributions. This program is distributed + * WITHOUT ANY WARRANTY, without even the warranty of FITNESS FOR A PARTICULAR + * PURPOSE or MERCHANTABILITY. Please read the associated documentation + * "3c90x.txt" before compiling and using this driver. + * + * -------- + * + * Program written with the assistance of the 3com documentation for + * the 3c905B-TX card, as well as with some assistance from the 3c59x + * driver Donald Becker wrote for the Linux kernel, and with some assistance + * from the remainder of the Etherboot distribution. + * + * REVISION HISTORY: + * + * v0.10 1-26-1998 GRB Initial implementation. + * v0.90 1-27-1998 GRB System works. + * v1.00pre1 2-11-1998 GRB Got prom boot issue fixed. + * v2.0 9-24-1999 SCS Modified for 3c905 (from 3c905b code) + * Re-wrote poll and transmit for + * better error recovery and heavy + * network traffic operation + * v2.01 5-26-2003 NN Fixed driver alignment issue which + * caused system lockups if driver structures + * not 8-byte aligned. + * v2.02 11-28-2007 GSt Got polling working again by replacing + * "for(i=0;i<40000;i++);" with "mdelay(1);" + * + */ + +#include "etherboot.h" +#include "nic.h" +#include +#include + +static struct nic_operations a3c90x_operations; + +#define XCVR_MAGIC (0x5A00) +/** any single transmission fails after 16 collisions or other errors + ** this is the number of times to retry the transmission -- this should + ** be plenty + **/ +#define XMIT_RETRIES 250 + +/*** Register definitions for the 3c905 ***/ +enum Registers + { + regPowerMgmtCtrl_w = 0x7c, /** 905B Revision Only **/ + regUpMaxBurst_w = 0x7a, /** 905B Revision Only **/ + regDnMaxBurst_w = 0x78, /** 905B Revision Only **/ + regDebugControl_w = 0x74, /** 905B Revision Only **/ + regDebugData_l = 0x70, /** 905B Revision Only **/ + regRealTimeCnt_l = 0x40, /** Universal **/ + regUpBurstThresh_b = 0x3e, /** 905B Revision Only **/ + regUpPoll_b = 0x3d, /** 905B Revision Only **/ + regUpPriorityThresh_b = 0x3c, /** 905B Revision Only **/ + regUpListPtr_l = 0x38, /** Universal **/ + regCountdown_w = 0x36, /** Universal **/ + regFreeTimer_w = 0x34, /** Universal **/ + regUpPktStatus_l = 0x30, /** Universal with Exception, pg 130 **/ + regTxFreeThresh_b = 0x2f, /** 90X Revision Only **/ + regDnPoll_b = 0x2d, /** 905B Revision Only **/ + regDnPriorityThresh_b = 0x2c, /** 905B Revision Only **/ + regDnBurstThresh_b = 0x2a, /** 905B Revision Only **/ + regDnListPtr_l = 0x24, /** Universal with Exception, pg 107 **/ + regDmaCtrl_l = 0x20, /** Universal with Exception, pg 106 **/ + /** **/ + regIntStatusAuto_w = 0x1e, /** 905B Revision Only **/ + regTxStatus_b = 0x1b, /** Universal with Exception, pg 113 **/ + regTimer_b = 0x1a, /** Universal **/ + regTxPktId_b = 0x18, /** 905B Revision Only **/ + regCommandIntStatus_w = 0x0e, /** Universal (Command Variations) **/ + }; + +/** following are windowed registers **/ +enum Registers7 + { + regPowerMgmtEvent_7_w = 0x0c, /** 905B Revision Only **/ + regVlanEtherType_7_w = 0x04, /** 905B Revision Only **/ + regVlanMask_7_w = 0x00, /** 905B Revision Only **/ + }; + +enum Registers6 + { + regBytesXmittedOk_6_w = 0x0c, /** Universal **/ + regBytesRcvdOk_6_w = 0x0a, /** Universal **/ + regUpperFramesOk_6_b = 0x09, /** Universal **/ + regFramesDeferred_6_b = 0x08, /** Universal **/ + regFramesRecdOk_6_b = 0x07, /** Universal with Exceptions, pg 142 **/ + regFramesXmittedOk_6_b = 0x06, /** Universal **/ + regRxOverruns_6_b = 0x05, /** Universal **/ + regLateCollisions_6_b = 0x04, /** Universal **/ + regSingleCollisions_6_b = 0x03, /** Universal **/ + regMultipleCollisions_6_b = 0x02, /** Universal **/ + regSqeErrors_6_b = 0x01, /** Universal **/ + regCarrierLost_6_b = 0x00, /** Universal **/ + }; + +enum Registers5 + { + regIndicationEnable_5_w = 0x0c, /** Universal **/ + regInterruptEnable_5_w = 0x0a, /** Universal **/ + regTxReclaimThresh_5_b = 0x09, /** 905B Revision Only **/ + regRxFilter_5_b = 0x08, /** Universal **/ + regRxEarlyThresh_5_w = 0x06, /** Universal **/ + regTxStartThresh_5_w = 0x00, /** Universal **/ + }; + +enum Registers4 + { + regUpperBytesOk_4_b = 0x0d, /** Universal **/ + regBadSSD_4_b = 0x0c, /** Universal **/ + regMediaStatus_4_w = 0x0a, /** Universal with Exceptions, pg 201 **/ + regPhysicalMgmt_4_w = 0x08, /** Universal **/ + regNetworkDiagnostic_4_w = 0x06, /** Universal with Exceptions, pg 203 **/ + regFifoDiagnostic_4_w = 0x04, /** Universal with Exceptions, pg 196 **/ + regVcoDiagnostic_4_w = 0x02, /** Undocumented? **/ + }; + +enum Registers3 + { + regTxFree_3_w = 0x0c, /** Universal **/ + regRxFree_3_w = 0x0a, /** Universal with Exceptions, pg 125 **/ + regResetMediaOptions_3_w = 0x08, /** Media Options on B Revision, **/ + /** Reset Options on Non-B Revision **/ + regMacControl_3_w = 0x06, /** Universal with Exceptions, pg 199 **/ + regMaxPktSize_3_w = 0x04, /** 905B Revision Only **/ + regInternalConfig_3_l = 0x00, /** Universal, different bit **/ + /** definitions, pg 59 **/ + }; + +enum Registers2 + { + regResetOptions_2_w = 0x0c, /** 905B Revision Only **/ + regStationMask_2_3w = 0x06, /** Universal with Exceptions, pg 127 **/ + regStationAddress_2_3w = 0x00, /** Universal with Exceptions, pg 127 **/ + }; + +enum Registers1 + { + regRxStatus_1_w = 0x0a, /** 90X Revision Only, Pg 126 **/ + }; + +enum Registers0 + { + regEepromData_0_w = 0x0c, /** Universal **/ + regEepromCommand_0_w = 0x0a, /** Universal **/ + regBiosRomData_0_b = 0x08, /** 905B Revision Only **/ + regBiosRomAddr_0_l = 0x04, /** 905B Revision Only **/ + }; + + +/*** The names for the eight register windows ***/ +enum Windows + { + winPowerVlan7 = 0x07, + winStatistics6 = 0x06, + winTxRxControl5 = 0x05, + winDiagnostics4 = 0x04, + winTxRxOptions3 = 0x03, + winAddressing2 = 0x02, + winUnused1 = 0x01, + winEepromBios0 = 0x00, + }; + + +/*** Command definitions for the 3c90X ***/ +enum Commands + { + cmdGlobalReset = 0x00, /** Universal with Exceptions, pg 151 **/ + cmdSelectRegisterWindow = 0x01, /** Universal **/ + cmdEnableDcConverter = 0x02, /** **/ + cmdRxDisable = 0x03, /** **/ + cmdRxEnable = 0x04, /** Universal **/ + cmdRxReset = 0x05, /** Universal **/ + cmdStallCtl = 0x06, /** Universal **/ + cmdTxEnable = 0x09, /** Universal **/ + cmdTxDisable = 0x0A, /** **/ + cmdTxReset = 0x0B, /** Universal **/ + cmdRequestInterrupt = 0x0C, /** **/ + cmdAcknowledgeInterrupt = 0x0D, /** Universal **/ + cmdSetInterruptEnable = 0x0E, /** Universal **/ + cmdSetIndicationEnable = 0x0F, /** Universal **/ + cmdSetRxFilter = 0x10, /** Universal **/ + cmdSetRxEarlyThresh = 0x11, /** **/ + cmdSetTxStartThresh = 0x13, /** **/ + cmdStatisticsEnable = 0x15, /** **/ + cmdStatisticsDisable = 0x16, /** **/ + cmdDisableDcConverter = 0x17, /** **/ + cmdSetTxReclaimThresh = 0x18, /** **/ + cmdSetHashFilterBit = 0x19, /** **/ + }; + + +/*** Values for int status register bitmask **/ +#define INT_INTERRUPTLATCH (1<<0) +#define INT_HOSTERROR (1<<1) +#define INT_TXCOMPLETE (1<<2) +#define INT_RXCOMPLETE (1<<4) +#define INT_RXEARLY (1<<5) +#define INT_INTREQUESTED (1<<6) +#define INT_UPDATESTATS (1<<7) +#define INT_LINKEVENT (1<<8) +#define INT_DNCOMPLETE (1<<9) +#define INT_UPCOMPLETE (1<<10) +#define INT_CMDINPROGRESS (1<<12) +#define INT_WINDOWNUMBER (7<<13) + + +/*** TX descriptor ***/ +typedef struct + { + unsigned int DnNextPtr; + unsigned int FrameStartHeader; + unsigned int HdrAddr; + unsigned int HdrLength; + unsigned int DataAddr; + unsigned int DataLength; + } + TXD __attribute__ ((aligned(8))); /* 64-bit aligned for bus mastering */ + +/*** RX descriptor ***/ +typedef struct + { + unsigned int UpNextPtr; + unsigned int UpPktStatus; + unsigned int DataAddr; + unsigned int DataLength; + } + RXD __attribute__ ((aligned(8))); /* 64-bit aligned for bus mastering */ + +/*** Global variables ***/ +static struct + { + unsigned int is3c556; + unsigned char isBrev; + unsigned char CurrentWindow; + unsigned int IOAddr; + unsigned char HWAddr[ETH_ALEN]; + TXD TransmitDPD; + RXD ReceiveUPD; + } + INF_3C90X; + + +/*** a3c90x_internal_IssueCommand: sends a command to the 3c90x card + ***/ +static int +a3c90x_internal_IssueCommand(int ioaddr, int cmd, int param) + { + unsigned int val; + + /** Build the cmd. **/ + val = cmd; + val <<= 11; + val |= param; + + /** Send the cmd to the cmd register **/ + outw(val, ioaddr + regCommandIntStatus_w); + + /** Wait for the cmd to complete, if necessary **/ + while (inw(ioaddr + regCommandIntStatus_w) & INT_CMDINPROGRESS); + + return 0; + } + + +/*** a3c90x_internal_SetWindow: selects a register window set. + ***/ +static int +a3c90x_internal_SetWindow(int ioaddr, int window) + { + + /** Window already as set? **/ + if (INF_3C90X.CurrentWindow == window) return 0; + + /** Issue the window command. **/ + a3c90x_internal_IssueCommand(ioaddr, cmdSelectRegisterWindow, window); + INF_3C90X.CurrentWindow = window; + + return 0; + } + + +/*** a3c90x_internal_ReadEeprom - read data from the serial eeprom. + ***/ +static unsigned short +a3c90x_internal_ReadEeprom(int ioaddr, int address) + { + unsigned short val; + + /** Select correct window **/ + a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winEepromBios0); + + /** Make sure the eeprom isn't busy **/ + while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); + + /** Read the value. **/ + if (INF_3C90X.is3c556) + { + outw(address + (0x230), ioaddr + regEepromCommand_0_w); + } + else + { + outw(address + ((0x02)<<6), ioaddr + regEepromCommand_0_w); + } + + while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); + val = inw(ioaddr + regEepromData_0_w); + + return val; + } + + +#if 0 +/*** a3c90x_internal_WriteEepromWord - write a physical word of + *** data to the onboard serial eeprom (not the BIOS prom, but the + *** nvram in the card that stores, among other things, the MAC + *** address). + ***/ +static int +a3c90x_internal_WriteEepromWord(int ioaddr, int address, unsigned short value) + { + /** Select register window **/ + a3c90x_internal_SetWindow(ioaddr, winEepromBios0); + + /** Verify Eeprom not busy **/ + while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); + + /** Issue WriteEnable, and wait for completion. **/ + outw(0x30, ioaddr + regEepromCommand_0_w); + while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); + + /** Issue EraseRegister, and wait for completion. **/ + outw(address + ((0x03)<<6), ioaddr + regEepromCommand_0_w); + while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); + + /** Send the new data to the eeprom, and wait for completion. **/ + outw(value, ioaddr + regEepromData_0_w); + outw(0x30, ioaddr + regEepromCommand_0_w); + while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); + + /** Burn the new data into the eeprom, and wait for completion. **/ + outw(address + ((0x01)<<6), ioaddr + regEepromCommand_0_w); + while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); + + return 0; + } +#endif + +#if 0 +/*** a3c90x_internal_WriteEeprom - write data to the serial eeprom, + *** and re-compute the eeprom checksum. + ***/ +static int +a3c90x_internal_WriteEeprom(int ioaddr, int address, unsigned short value) + { + int cksum = 0,v; + int i; + int maxAddress, cksumAddress; + + if (INF_3C90X.isBrev) + { + maxAddress=0x1f; + cksumAddress=0x20; + } + else + { + maxAddress=0x16; + cksumAddress=0x17; + } + + /** Write the value. **/ + if (a3c90x_internal_WriteEepromWord(ioaddr, address, value) == -1) + return -1; + + /** Recompute the checksum. **/ + for(i=0;i<=maxAddress;i++) + { + v = a3c90x_internal_ReadEeprom(ioaddr, i); + cksum ^= (v & 0xFF); + cksum ^= ((v>>8) & 0xFF); + } + /** Write the checksum to the location in the eeprom **/ + if (a3c90x_internal_WriteEepromWord(ioaddr, cksumAddress, cksum) == -1) + return -1; + + return 0; + } +#endif + +/*** a3c90x_reset: exported function that resets the card to its default + *** state. This is so the Linux driver can re-set the card up the way + *** it wants to. If CFG_3C90X_PRESERVE_XCVR is defined, then the reset will + *** not alter the selected transceiver that we used to download the boot + *** image. + ***/ +static void a3c90x_reset(void) + { +#ifdef CFG_3C90X_PRESERVE_XCVR + int cfg; + /** Read the current InternalConfig value. **/ + a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3); + cfg = inl(INF_3C90X.IOAddr + regInternalConfig_3_l); +#endif + + /** Send the reset command to the card **/ + printf("Issuing RESET:\n"); + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdGlobalReset, 0); + + /** wait for reset command to complete **/ + while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS); + + /** global reset command resets station mask, non-B revision cards + ** require explicit reset of values + **/ + a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2); + outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+0); + outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+2); + outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+4); + +#ifdef CFG_3C90X_PRESERVE_XCVR + /** Re-set the original InternalConfig value from before reset **/ + a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3); + outl(cfg, INF_3C90X.IOAddr + regInternalConfig_3_l); + + /** enable DC converter for 10-Base-T **/ + if ((cfg&0x0300) == 0x0300) + { + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdEnableDcConverter, 0); + } +#endif + + /** Issue transmit reset, wait for command completion **/ + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxReset, 0); + while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS) + ; + if (! INF_3C90X.isBrev) + outb(0x01, INF_3C90X.IOAddr + regTxFreeThresh_b); + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0); + + /** + ** reset of the receiver on B-revision cards re-negotiates the link + ** takes several seconds (a computer eternity) + **/ + if (INF_3C90X.isBrev) + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x04); + else + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x00); + while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS); + ; + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxEnable, 0); + + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, + cmdSetInterruptEnable, 0); + /** enable rxComplete and txComplete **/ + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, + cmdSetIndicationEnable, 0x0014); + /** acknowledge any pending status flags **/ + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, + cmdAcknowledgeInterrupt, 0x661); + + return; + } + + + +/*** a3c90x_transmit: exported function that transmits a packet. Does not + *** return any particular status. Parameters are: + *** d[6] - destination address, ethernet; + *** t - protocol type (ARP, IP, etc); + *** s - size of the non-header part of the packet that needs transmitted; + *** p - the pointer to the packet data itself. + ***/ +static void +a3c90x_transmit(struct nic *nic __unused, const char *d, unsigned int t, + unsigned int s, const char *p) + { + + struct eth_hdr + { + unsigned char dst_addr[ETH_ALEN]; + unsigned char src_addr[ETH_ALEN]; + unsigned short type; + } hdr; + + unsigned char status; + unsigned i, retries; + unsigned long ct; + + for (retries=0; retries < XMIT_RETRIES ; retries++) + { + /** Stall the download engine **/ + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 2); + + /** Make sure the card is not waiting on us **/ + inw(INF_3C90X.IOAddr + regCommandIntStatus_w); + inw(INF_3C90X.IOAddr + regCommandIntStatus_w); + + while (inw(INF_3C90X.IOAddr+regCommandIntStatus_w) & + INT_CMDINPROGRESS) + ; + + /** Set the ethernet packet type **/ + hdr.type = htons(t); + + /** Copy the destination address **/ + memcpy(hdr.dst_addr, d, ETH_ALEN); + + /** Copy our MAC address **/ + memcpy(hdr.src_addr, INF_3C90X.HWAddr, ETH_ALEN); + + /** Setup the DPD (download descriptor) **/ + INF_3C90X.TransmitDPD.DnNextPtr = 0; + /** set notification for transmission completion (bit 15) **/ + INF_3C90X.TransmitDPD.FrameStartHeader = (s + sizeof(hdr)) | 0x8000; + INF_3C90X.TransmitDPD.HdrAddr = virt_to_bus(&hdr); + INF_3C90X.TransmitDPD.HdrLength = sizeof(hdr); + INF_3C90X.TransmitDPD.DataAddr = virt_to_bus(p); + INF_3C90X.TransmitDPD.DataLength = s + (1<<31); + + /** Send the packet **/ + outl(virt_to_bus(&(INF_3C90X.TransmitDPD)), + INF_3C90X.IOAddr + regDnListPtr_l); + + /** End Stall and Wait for upload to complete. **/ + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 3); + while(inl(INF_3C90X.IOAddr + regDnListPtr_l) != 0) + ; + + /** Wait for NIC Transmit to Complete **/ + ct = currticks(); + + while (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0004) && + ct + 10*1000 < currticks()); + ; + + if (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0004)) + { + printf("3C90X: Tx Timeout\n"); + continue; + } + + status = inb(INF_3C90X.IOAddr + regTxStatus_b); + + /** acknowledge transmit interrupt by writing status **/ + outb(0x00, INF_3C90X.IOAddr + regTxStatus_b); + + /** successful completion (sans "interrupt Requested" bit) **/ + if ((status & 0xbf) == 0x80) + return; + + printf("3C90X: Status (%hhX)\n", status); + /** check error codes **/ + if (status & 0x02) + { + printf("3C90X: Tx Reclaim Error (%hhX)\n", status); + a3c90x_reset(); + } + else if (status & 0x04) + { + printf("3C90X: Tx Status Overflow (%hhX)\n", status); + for (i=0; i<32; i++) + outb(0x00, INF_3C90X.IOAddr + regTxStatus_b); + /** must re-enable after max collisions before re-issuing tx **/ + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0); + } + else if (status & 0x08) + { + printf("3C90X: Tx Max Collisions (%hhX)\n", status); + /** must re-enable after max collisions before re-issuing tx **/ + a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0); + } + else if (status & 0x10) + { + printf("3C90X: Tx Underrun (%hhX)\n", status); + a3c90x_reset(); + } + else if (status & 0x20) + { + printf("3C90X: Tx Jabber (%hhX)\n", status); + a3c90x_reset(); + } + else if ((status & 0x80) != 0x80) + { + printf("3C90X: Internal Error - Incomplete Transmission (%hhX)\n", + status); + a3c90x_reset(); + } + } + + /** failed after RETRY attempts **/ + printf("Failed to send after %d retries\n", retries); + return; + + } + + + +/*** a3c90x_poll: exported routine that waits for a certain length of time + *** for a packet, and if it sees none, returns 0. This routine should + *** copy the packet to nic->packet if it gets a packet and set the size + *** in nic->packetlen. Return 1 if a packet was found. + ***/ +static int +a3c90x_poll(struct nic *nic, int retrieve) + { + int errcode; + + if (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0010)) + { + return 0; + } + + if ( ! retrieve ) return 1; + + /** we don't need to acknowledge rxComplete -- the upload engine + ** does it for us. + **/ + + /** Build the up-load descriptor **/ + INF_3C90X.ReceiveUPD.UpNextPtr = 0; + INF_3C90X.ReceiveUPD.UpPktStatus = 0; + INF_3C90X.ReceiveUPD.DataAddr = virt_to_bus(nic->packet); + INF_3C90X.ReceiveUPD.DataLength = 1536 + (1<<31); + + /** Submit the upload descriptor to the NIC **/ + outl(virt_to_bus(&(INF_3C90X.ReceiveUPD)), + INF_3C90X.IOAddr + regUpListPtr_l); + + /** Wait for upload completion (upComplete(15) or upError (14)) **/ + mdelay(1); + while((INF_3C90X.ReceiveUPD.UpPktStatus & ((1<<14) | (1<<15))) == 0) + mdelay(1); + + /** Check for Error (else we have good packet) **/ + if (INF_3C90X.ReceiveUPD.UpPktStatus & (1<<14)) + { + errcode = INF_3C90X.ReceiveUPD.UpPktStatus; + if (errcode & (1<<16)) + printf("3C90X: Rx Overrun (%hX)\n",errcode>>16); + else if (errcode & (1<<17)) + printf("3C90X: Runt Frame (%hX)\n",errcode>>16); + else if (errcode & (1<<18)) + printf("3C90X: Alignment Error (%hX)\n",errcode>>16); + else if (errcode & (1<<19)) + printf("3C90X: CRC Error (%hX)\n",errcode>>16); + else if (errcode & (1<<20)) + printf("3C90X: Oversized Frame (%hX)\n",errcode>>16); + else + printf("3C90X: Packet error (%hX)\n",errcode>>16); + return 0; + } + + /** Ok, got packet. Set length in nic->packetlen. **/ + nic->packetlen = (INF_3C90X.ReceiveUPD.UpPktStatus & 0x1FFF); + + return 1; + } + + + +/*** a3c90x_disable: exported routine to disable the card. What's this for? + *** the eepro100.c driver didn't have one, so I just left this one empty too. + *** Ideas anyone? + *** Must turn off receiver at least so stray packets will not corrupt memory + *** [Ken] + ***/ +static void +a3c90x_disable ( struct nic *nic __unused ) { + a3c90x_reset(); + /* Disable the receiver and transmitter. */ + outw(cmdRxDisable, INF_3C90X.IOAddr + regCommandIntStatus_w); + outw(cmdTxDisable, INF_3C90X.IOAddr + regCommandIntStatus_w); +} + +static void a3c90x_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + +/*** a3c90x_probe: exported routine to probe for the 3c905 card and perform + *** initialization. If this routine is called, the pci functions did find the + *** card. We just have to init it here. + ***/ +static int a3c90x_probe ( struct nic *nic, struct pci_device *pci ) { + + int i, c; + unsigned short eeprom[0x21]; + unsigned int cfg; + unsigned int mopt; + unsigned int mstat; + unsigned short linktype; +#define HWADDR_OFFSET 10 + + if (pci->ioaddr == 0) + return 0; + + adjust_pci_device(pci); + + nic->ioaddr = pci->ioaddr; + nic->irqno = 0; + + INF_3C90X.is3c556 = (pci->device == 0x6055); + INF_3C90X.IOAddr = pci->ioaddr & ~3; + INF_3C90X.CurrentWindow = 255; + switch (a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, 0x03)) + { + case 0x9000: /** 10 Base TPO **/ + case 0x9001: /** 10/100 T4 **/ + case 0x9050: /** 10/100 TPO **/ + case 0x9051: /** 10 Base Combo **/ + INF_3C90X.isBrev = 0; + break; + + case 0x9004: /** 10 Base TPO **/ + case 0x9005: /** 10 Base Combo **/ + case 0x9006: /** 10 Base TPO and Base2 **/ + case 0x900A: /** 10 Base FL **/ + case 0x9055: /** 10/100 TPO **/ + case 0x9056: /** 10/100 T4 **/ + case 0x905A: /** 10 Base FX **/ + default: + INF_3C90X.isBrev = 1; + break; + } + + /** Load the EEPROM contents **/ + if (INF_3C90X.isBrev) + { + for(i=0;i<=0x20;i++) + { + eeprom[i] = a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, i); + } + +#ifdef CFG_3C90X_BOOTROM_FIX + /** Set xcvrSelect in InternalConfig in eeprom. **/ + /* only necessary for 3c905b revision cards with boot PROM bug!!! */ + a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x13, 0x0160); +#endif + +#ifdef CFG_3C90X_XCVR + if (CFG_3C90X_XCVR == 255) + { + /** Clear the LanWorks register **/ + a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x16, 0); + } + else + { + /** Set the selected permanent-xcvrSelect in the + ** LanWorks register + **/ + a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x16, + XCVR_MAGIC + ((CFG_3C90X_XCVR) & 0x000F)); + } +#endif + } + else + { + for(i=0;i<=0x17;i++) + { + eeprom[i] = a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, i); + } + } + + /** Print identification message **/ + printf("\n\n3C90X Driver 2.02 " + "Copyright 1999 LightSys Technology Services, Inc.\n" + "Portions Copyright 1999 Steve Smith\n"); + printf("Provided with ABSOLUTELY NO WARRANTY.\n"); +#ifdef CFG_3C90X_BOOTROM_FIX + if (INF_3C90X.isBrev) + { + printf("NOTE: 3c905b bootrom fix enabled; has side " + "effects. See 3c90x.txt for info.\n"); + } +#endif + printf("-------------------------------------------------------" + "------------------------\n"); + + /** Retrieve the Hardware address and print it on the screen. **/ + INF_3C90X.HWAddr[0] = eeprom[HWADDR_OFFSET + 0]>>8; + INF_3C90X.HWAddr[1] = eeprom[HWADDR_OFFSET + 0]&0xFF; + INF_3C90X.HWAddr[2] = eeprom[HWADDR_OFFSET + 1]>>8; + INF_3C90X.HWAddr[3] = eeprom[HWADDR_OFFSET + 1]&0xFF; + INF_3C90X.HWAddr[4] = eeprom[HWADDR_OFFSET + 2]>>8; + INF_3C90X.HWAddr[5] = eeprom[HWADDR_OFFSET + 2]&0xFF; + + DBG ( "MAC Address = %s\n", eth_ntoa ( INF_3C90X.HWAddr ) ); + + /** 3C556: Invert MII power **/ + if (INF_3C90X.is3c556) { + unsigned int tmp; + a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2); + tmp = inw(INF_3C90X.IOAddr + regResetOptions_2_w); + tmp |= 0x4000; + outw(tmp, INF_3C90X.IOAddr + regResetOptions_2_w); + } + + /* Test if the link is good, if not continue */ + a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winDiagnostics4); + mstat = inw(INF_3C90X.IOAddr + regMediaStatus_4_w); + if((mstat & (1<<11)) == 0) { + printf("Valid link not established\n"); + return 0; + } + + /** Program the MAC address into the station address registers **/ + a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2); + outw(htons(eeprom[HWADDR_OFFSET + 0]), INF_3C90X.IOAddr + regStationAddress_2_3w); + outw(htons(eeprom[HWADDR_OFFSET + 1]), INF_3C90X.IOAddr + regStationAddress_2_3w+2); + outw(htons(eeprom[HWADDR_OFFSET + 2]), INF_3C90X.IOAddr + regStationAddress_2_3w+4); + outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+0); + outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+2); + outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+4); + + /** Fill in our entry in the etherboot arp table **/ + for(i=0;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 **/ + nic->nic_op = &a3c90x_operations; + return 1; +} + +static struct nic_operations a3c90x_operations = { + .connect = dummy_connect, + .poll = a3c90x_poll, + .transmit = a3c90x_transmit, + .irq = a3c90x_irq, + +}; + +static struct pci_device_id a3c90x_nics[] = { +/* Original 90x revisions: */ +PCI_ROM(0x10b7, 0x6055, "3c556", "3C556"), /* Huricane */ +PCI_ROM(0x10b7, 0x9000, "3c905-tpo", "3Com900-TPO"), /* 10 Base TPO */ +PCI_ROM(0x10b7, 0x9001, "3c905-t4", "3Com900-Combo"), /* 10/100 T4 */ +PCI_ROM(0x10b7, 0x9050, "3c905-tpo100", "3Com905-TX"), /* 100 Base TX / 10/100 TPO */ +PCI_ROM(0x10b7, 0x9051, "3c905-combo", "3Com905-T4"), /* 100 Base T4 / 10 Base Combo */ +/* Newer 90xB revisions: */ +PCI_ROM(0x10b7, 0x9004, "3c905b-tpo", "3Com900B-TPO"), /* 10 Base TPO */ +PCI_ROM(0x10b7, 0x9005, "3c905b-combo", "3Com900B-Combo"), /* 10 Base Combo */ +PCI_ROM(0x10b7, 0x9006, "3c905b-tpb2", "3Com900B-2/T"), /* 10 Base TP and Base2 */ +PCI_ROM(0x10b7, 0x900a, "3c905b-fl", "3Com900B-FL"), /* 10 Base FL */ +PCI_ROM(0x10b7, 0x9055, "3c905b-tpo100", "3Com905B-TX"), /* 10/100 TPO */ +PCI_ROM(0x10b7, 0x9056, "3c905b-t4", "3Com905B-T4"), /* 10/100 T4 */ +PCI_ROM(0x10b7, 0x9058, "3c905b-9058", "3Com905B-9058"), /* Cyclone 10/100/BNC */ +PCI_ROM(0x10b7, 0x905a, "3c905b-fx", "3Com905B-FL"), /* 100 Base FX / 10 Base FX */ +/* Newer 90xC revision: */ +PCI_ROM(0x10b7, 0x9200, "3c905c-tpo", "3Com905C-TXM"), /* 10/100 TPO (3C905C-TXM) */ +PCI_ROM(0x10b7, 0x9202, "3c920b-emb-ati", "3c920B-EMB-WNM (ATI Radeon 9100 IGP)"), /* 3c920B-EMB-WNM (ATI Radeon 9100 IGP) */ +PCI_ROM(0x10b7, 0x9210, "3c920b-emb-wnm","3Com20B-EMB WNM"), +PCI_ROM(0x10b7, 0x9800, "3c980", "3Com980-Cyclone"), /* Cyclone */ +PCI_ROM(0x10b7, 0x9805, "3c9805", "3Com9805"), /* Dual Port Server Cyclone */ +PCI_ROM(0x10b7, 0x7646, "3csoho100-tx", "3CSOHO100-TX"), /* Hurricane */ +PCI_ROM(0x10b7, 0x4500, "3c450", "3Com450 HomePNA Tornado"), +PCI_ROM(0x10b7, 0x1201, "3c982a", "3Com982A"), +PCI_ROM(0x10b7, 0x1202, "3c982b", "3Com982B"), +}; + +PCI_DRIVER ( a3c90x_driver, a3c90x_nics, PCI_NO_CLASS ); + +DRIVER ( "3C90X", nic_driver, pci_driver, a3c90x_driver, + a3c90x_probe, a3c90x_disable ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/3c90x.txt b/gpxe/src/drivers/net/3c90x.txt new file mode 100644 index 0000000..3d6746c --- /dev/null +++ b/gpxe/src/drivers/net/3c90x.txt @@ -0,0 +1,307 @@ + + Instructions for use of the 3C90X driver for EtherBoot + + Original 3C905B support by: + Greg Beeley (Greg.Beeley@LightSys.org), + LightSys Technology Services, Inc. + February 11, 1999 + + Updates for 3C90X family by: + Steve Smith (steve.smith@juno.com) + October 1, 1999 + + Minor documentation updates by + Greg Beeley (Greg.Beeley@LightSys.org) + March 29, 2000 + +------------------------------------------------------------------------------- + +I OVERVIEW + + The 3c90X series ethernet cards are a group of high-performance busmaster + DMA cards from 3Com. This particular driver supports both the 3c90x and + the 3c90xB revision cards. 3C90xC family support has been tested to some + degree but not extensively. + + Here's the licensing information: + + This program Copyright (C) 1999 LightSys Technology Services, Inc. + Portions Copyright (C) 1999 Steve Smith. + + This program may be re-distributed in source or binary form, modified, + sold, or copied for any purpose, provided that the above copyright message + and this text are included with all source copies or derivative works, and + provided that the above copyright message and this text are included in the + documentation of any binary-only distributions. This program is + distributed WITHOUT ANY WARRANTY, without even the warranty of FITNESS FOR + A PARTICULAR PURPOSE or MERCHANTABILITY. Please read the associated + documentation "3c90x.txt" before compiling and using this driver. + + +II FLASH PROMS + + The 3c90xB cards, according to the 3Com documentation, only accept the + following flash memory chips: + + Atmel AT29C512 (64 kilobyte) + Atmel AT29C010 (128 kilobyte) + + The 3c90x cards, according to the 3Com documentation, accept the + following flash memory chips capacities: + + 64 kb (8 kB) + 128 kb (16 kB) + 256 kb (32 kB) and + 512 kb (64 kB) + + Atmel AT29C512 (64 kilobyte) chips are specifically listed for both + adapters, but flashing on the 3c905b cards would only be supported + through the Atmel parts. Any device, of the supported size, should + be supported when programmed by a dedicated PROM programmer (e.g. + not the card). + + To use this driver in such a PROM, visit Atmel's web site and download + their .PDF file containing a list of their distributors. Contact the + distributors for pricing information. The prices are quite reasonable + (about $3 US each for the 64 kB part), and are comparable to what one would + expect for similarly sized standard EPROMs. And, the flash chips are much + easier to work with, as they don't need to be UV-erased to be reprogrammed. + The 3C905B card actually provides a method to program the flash memory + while it is resident on board the card itself; if someone would like to + write a small DOS program to do the programming, I can provide the + information about the registers and so forth. + + A utility program, 3c90xutil, is provided with Etherboot in the 'contrib' + directory that allows for the on-board flashing of the ROM while Linux + is running. The program has been successfully used under Linux, but I + have heard problem reports of its use under FreeBSD. Anyone willing to + make it work under FreeBSD is more than welcome to do so! + + You also have the option of using EPROM chips - the 3C905B-TX-NM has been + successfully tested with 27C256 (32kB) and 27C512 (64kB) chips with a + specified access time of 100ns and faster. + + +III GENERAL USE + + Normally, the basic procedure for using this driver is as follows: + + 1. Run the 3c90xcfg program on the driver diskette to enable the + boot PROM and set it to 64k or 128k, as appropriate. + 2. Build the appropriate 3c90x.fd0 or 3c90x.fd0 floppy image with + possibly the value CFG_3C90X_XCVR defined to the transceiver type that + you want to use (i.e., 10/100 rj45, AUI, coax, MII). + 3. Run the floppy image on the PC to be network booted, to get + it configured, and to verify that it will boot properly. + 4. Build the 3c90x.rom or 3c90x.lzrom PROM image and program + it into the flash or EPROM memory chip. + 5. Put the PROM in the ethernet card, boot and enable 'boot from + network first' in the system BIOS, save and reboot. + + Here are some issues to be aware of: + + 1. If you experience crashes or different behaviour when using the + boot PROM, add the setting CFG_3C90X_BOOTROM_FIX and go through the + steps 2-5 above. This works around a bug in some 3c905B cards (see + below), but has some side-effects which may not be desirable. + Please note that you have to boot off a floppy (not PROM!) once for + this fix to take effect. + 2. The possible need to manually set the CFG_3C90X_XCVR value to + configure the transceiver type. Values are listed below. + 3. The possible need to define CFG_3C90X_PRESERVE_XCVR for use in + operating systems that don't intelligently determine the + transceiver type. + + Some things that are on the 'To-Do' list, perhaps for me, but perhaps + for any other volunteers out there: + + 1. Extend the driver to fully implement the auto-select + algorithm if the card has multiple media ports. + 2. Fix any bugs in the code .... + 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/gpxe/src/drivers/net/amd8111e.c b/gpxe/src/drivers/net/amd8111e.c new file mode 100644 index 0000000..1c41add --- /dev/null +++ b/gpxe/src/drivers/net/amd8111e.c @@ -0,0 +1,691 @@ +/* Advanced Micro Devices Inc. AMD8111E Linux Network Driver + * Copyright (C) 2004 Advanced Micro Devices + * Copyright (C) 2005 Liu Tao [etherboot port] + * + * Copyright 2001,2002 Jeff Garzik [ 8139cp.c,tg3.c ] + * Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com)[ tg3.c] + * Copyright 1996-1999 Thomas Bogendoerfer [ pcnet32.c ] + * Derived from the lance driver written 1993,1994,1995 by Donald Becker. + * Copyright 1993 United States Government as represented by the + * Director, National Security Agency.[ pcnet32.c ] + * Carsten Langgaard, carstenl@mips.com [ pcnet32.c ] + * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include "etherboot.h" +#include "nic.h" +#include "mii.h" +#include +#include +#include "string.h" +#include "stdint.h" +#include "amd8111e.h" + + +/* driver definitions */ +#define NUM_TX_SLOTS 2 +#define NUM_RX_SLOTS 4 +#define TX_SLOTS_MASK 1 +#define RX_SLOTS_MASK 3 + +#define TX_BUF_LEN 1536 +#define RX_BUF_LEN 1536 + +#define TX_PKT_LEN_MAX (ETH_FRAME_LEN - ETH_HLEN) +#define RX_PKT_LEN_MIN 60 +#define RX_PKT_LEN_MAX ETH_FRAME_LEN + +#define TX_TIMEOUT 3000 +#define TX_PROCESS_TIME 10 +#define TX_RETRY (TX_TIMEOUT / TX_PROCESS_TIME) + +#define PHY_RW_RETRY 10 + + +struct amd8111e_tx_desc { + u16 buf_len; + u16 tx_flags; + u16 tag_ctrl_info; + u16 tag_ctrl_cmd; + u32 buf_phy_addr; + u32 reserved; +}; + +struct amd8111e_rx_desc { + u32 reserved; + u16 msg_len; + u16 tag_ctrl_info; + u16 buf_len; + u16 rx_flags; + u32 buf_phy_addr; +}; + +struct eth_frame { + u8 dst_addr[ETH_ALEN]; + u8 src_addr[ETH_ALEN]; + u16 type; + u8 data[ETH_FRAME_LEN - ETH_HLEN]; +} __attribute__((packed)); + +struct amd8111e_priv { + struct amd8111e_tx_desc tx_ring[NUM_TX_SLOTS]; + struct amd8111e_rx_desc rx_ring[NUM_RX_SLOTS]; + unsigned char tx_buf[NUM_TX_SLOTS][TX_BUF_LEN]; + unsigned char rx_buf[NUM_RX_SLOTS][RX_BUF_LEN]; + unsigned long tx_idx, rx_idx; + int tx_consistent; + + char opened; + char link; + char speed; + char duplex; + int ext_phy_addr; + u32 ext_phy_id; + + struct pci_device *pdev; + struct nic *nic; + void *mmio; +}; + +static struct amd8111e_priv amd8111e; + + +/******************************************************** + * locale functions * + ********************************************************/ +static void amd8111e_init_hw_default(struct amd8111e_priv *lp); +static int amd8111e_start(struct amd8111e_priv *lp); +static int amd8111e_read_phy(struct amd8111e_priv *lp, int phy_addr, int reg, u32 *val); +#if 0 +static int amd8111e_write_phy(struct amd8111e_priv *lp, int phy_addr, int reg, u32 val); +#endif +static void amd8111e_probe_ext_phy(struct amd8111e_priv *lp); +static void amd8111e_disable_interrupt(struct amd8111e_priv *lp); +static void amd8111e_enable_interrupt(struct amd8111e_priv *lp); +static void amd8111e_force_interrupt(struct amd8111e_priv *lp); +static int amd8111e_get_mac_address(struct amd8111e_priv *lp); +static int amd8111e_init_rx_ring(struct amd8111e_priv *lp); +static int amd8111e_init_tx_ring(struct amd8111e_priv *lp); +static int amd8111e_wait_tx_ring(struct amd8111e_priv *lp, unsigned int index); +static void amd8111e_wait_link(struct amd8111e_priv *lp); +static void amd8111e_poll_link(struct amd8111e_priv *lp); +static void amd8111e_restart(struct amd8111e_priv *lp); + + +/* + * This function clears necessary the device registers. + */ +static void amd8111e_init_hw_default(struct amd8111e_priv *lp) +{ + unsigned int reg_val; + void *mmio = lp->mmio; + + /* stop the chip */ + writel(RUN, mmio + CMD0); + + /* Clear RCV_RING_BASE_ADDR */ + writel(0, mmio + RCV_RING_BASE_ADDR0); + + /* Clear XMT_RING_BASE_ADDR */ + writel(0, mmio + XMT_RING_BASE_ADDR0); + writel(0, mmio + XMT_RING_BASE_ADDR1); + writel(0, mmio + XMT_RING_BASE_ADDR2); + writel(0, mmio + XMT_RING_BASE_ADDR3); + + /* Clear CMD0 */ + writel(CMD0_CLEAR, mmio + CMD0); + + /* Clear CMD2 */ + writel(CMD2_CLEAR, mmio + CMD2); + + /* Clear CMD7 */ + writel(CMD7_CLEAR, mmio + CMD7); + + /* Clear DLY_INT_A and DLY_INT_B */ + writel(0x0, mmio + DLY_INT_A); + writel(0x0, mmio + DLY_INT_B); + + /* Clear FLOW_CONTROL */ + writel(0x0, mmio + FLOW_CONTROL); + + /* Clear INT0 write 1 to clear register */ + reg_val = readl(mmio + INT0); + writel(reg_val, mmio + INT0); + + /* Clear STVAL */ + writel(0x0, mmio + STVAL); + + /* Clear INTEN0 */ + writel(INTEN0_CLEAR, mmio + INTEN0); + + /* Clear LADRF */ + writel(0x0, mmio + LADRF); + + /* Set SRAM_SIZE & SRAM_BOUNDARY registers */ + writel(0x80010, mmio + SRAM_SIZE); + + /* Clear RCV_RING0_LEN */ + writel(0x0, mmio + RCV_RING_LEN0); + + /* Clear XMT_RING0/1/2/3_LEN */ + writel(0x0, mmio + XMT_RING_LEN0); + writel(0x0, mmio + XMT_RING_LEN1); + writel(0x0, mmio + XMT_RING_LEN2); + writel(0x0, mmio + XMT_RING_LEN3); + + /* Clear XMT_RING_LIMIT */ + writel(0x0, mmio + XMT_RING_LIMIT); + + /* Clear MIB */ + writew(MIB_CLEAR, mmio + MIB_ADDR); + + /* Clear LARF */ + writel( 0, mmio + LADRF); + writel( 0, mmio + LADRF + 4); + + /* SRAM_SIZE register */ + reg_val = readl(mmio + SRAM_SIZE); + + /* Set default value to CTRL1 Register */ + writel(CTRL1_DEFAULT, mmio + CTRL1); + + /* To avoid PCI posting bug */ + readl(mmio + CMD2); +} + +/* + * This function initializes the device registers and starts the device. + */ +static int amd8111e_start(struct amd8111e_priv *lp) +{ + struct nic *nic = lp->nic; + void *mmio = lp->mmio; + int i, reg_val; + + /* stop the chip */ + writel(RUN, mmio + CMD0); + + /* AUTOPOLL0 Register *//*TBD default value is 8100 in FPS */ + writew(0x8100 | lp->ext_phy_addr, mmio + AUTOPOLL0); + + /* enable the port manager and set auto negotiation always */ + writel(VAL1 | EN_PMGR, mmio + CMD3 ); + writel(XPHYANE | XPHYRST, mmio + CTRL2); + + /* set control registers */ + reg_val = readl(mmio + CTRL1); + reg_val &= ~XMTSP_MASK; + writel(reg_val | XMTSP_128 | CACHE_ALIGN, mmio + CTRL1); + + /* initialize tx and rx ring base addresses */ + amd8111e_init_tx_ring(lp); + amd8111e_init_rx_ring(lp); + writel(virt_to_bus(lp->tx_ring), mmio + XMT_RING_BASE_ADDR0); + writel(virt_to_bus(lp->rx_ring), mmio + RCV_RING_BASE_ADDR0); + writew(NUM_TX_SLOTS, mmio + XMT_RING_LEN0); + writew(NUM_RX_SLOTS, mmio + RCV_RING_LEN0); + + /* set default IPG to 96 */ + writew(DEFAULT_IPG, mmio + IPG); + writew(DEFAULT_IPG - IFS1_DELTA, mmio + IFS1); + + /* AutoPAD transmit, Retransmit on Underflow */ + writel(VAL0 | APAD_XMT | REX_RTRY | REX_UFLO, mmio + CMD2); + + /* JUMBO disabled */ + writel(JUMBO, mmio + CMD3); + + /* Setting the MAC address to the device */ + for(i = 0; i < ETH_ALEN; i++) + writeb(nic->node_addr[i], mmio + PADR + i); + + /* set RUN bit to start the chip, interrupt not enabled */ + writel(VAL2 | RDMD0 | VAL0 | RUN, mmio + CMD0); + + /* To avoid PCI posting bug */ + readl(mmio + CMD0); + return 0; +} + +/* +This function will read the PHY registers. +*/ +static int amd8111e_read_phy(struct amd8111e_priv *lp, int phy_addr, int reg, u32 *val) +{ + void *mmio = lp->mmio; + unsigned int reg_val; + unsigned int retry = PHY_RW_RETRY; + + reg_val = readl(mmio + PHY_ACCESS); + while (reg_val & PHY_CMD_ACTIVE) + reg_val = readl(mmio + PHY_ACCESS); + + writel(PHY_RD_CMD | ((phy_addr & 0x1f) << 21) | ((reg & 0x1f) << 16), + mmio + PHY_ACCESS); + do { + reg_val = readl(mmio + PHY_ACCESS); + udelay(30); /* It takes 30 us to read/write data */ + } while (--retry && (reg_val & PHY_CMD_ACTIVE)); + + if (reg_val & PHY_RD_ERR) { + *val = 0; + return -1; + } + + *val = reg_val & 0xffff; + return 0; +} + +/* +This function will write into PHY registers. +*/ +#if 0 +static int amd8111e_write_phy(struct amd8111e_priv *lp, int phy_addr, int reg, u32 val) +{ + void *mmio = lp->mmio; + unsigned int reg_val; + unsigned int retry = PHY_RW_RETRY; + + reg_val = readl(mmio + PHY_ACCESS); + while (reg_val & PHY_CMD_ACTIVE) + reg_val = readl(mmio + PHY_ACCESS); + + writel(PHY_WR_CMD | ((phy_addr & 0x1f) << 21) | ((reg & 0x1f) << 16) | val, + mmio + PHY_ACCESS); + do { + reg_val = readl(mmio + PHY_ACCESS); + udelay(30); /* It takes 30 us to read/write the data */ + } while (--retry && (reg_val & PHY_CMD_ACTIVE)); + + if(reg_val & PHY_RD_ERR) + return -1; + + return 0; +} +#endif + +static void amd8111e_probe_ext_phy(struct amd8111e_priv *lp) +{ + int i; + + lp->ext_phy_id = 0; + lp->ext_phy_addr = 1; + + for (i = 0x1e; i >= 0; i--) { + u32 id1, id2; + + if (amd8111e_read_phy(lp, i, MII_PHYSID1, &id1)) + continue; + if (amd8111e_read_phy(lp, i, MII_PHYSID2, &id2)) + continue; + lp->ext_phy_id = (id1 << 16) | id2; + lp->ext_phy_addr = i; + break; + } + + if (lp->ext_phy_id) + printf("Found MII PHY ID 0x%08x at address 0x%02x\n", + (unsigned int) lp->ext_phy_id, lp->ext_phy_addr); + else + printf("Couldn't detect MII PHY, assuming address 0x01\n"); +} + +static void amd8111e_disable_interrupt(struct amd8111e_priv *lp) +{ + void *mmio = lp->mmio; + unsigned int int0; + + writel(INTREN, mmio + CMD0); + writel(INTEN0_CLEAR, mmio + INTEN0); + int0 = readl(mmio + INT0); + writel(int0, mmio + INT0); + readl(mmio + INT0); +} + +static void amd8111e_enable_interrupt(struct amd8111e_priv *lp) +{ + void *mmio = lp->mmio; + + writel(VAL3 | LCINTEN | VAL1 | TINTEN0 | VAL0 | RINTEN0, mmio + INTEN0); + writel(VAL0 | INTREN, mmio + CMD0); + readl(mmio + CMD0); +} + +static void amd8111e_force_interrupt(struct amd8111e_priv *lp) +{ + void *mmio = lp->mmio; + + writel(VAL0 | UINTCMD, mmio + CMD0); + readl(mmio + CMD0); +} + +static int amd8111e_get_mac_address(struct amd8111e_priv *lp) +{ + struct nic *nic = lp->nic; + void *mmio = lp->mmio; + int i; + + /* BIOS should have set mac address to PADR register, + * so we read PADR to get it. + */ + for (i = 0; i < ETH_ALEN; i++) + nic->node_addr[i] = readb(mmio + PADR + i); + + DBG ( "Ethernet addr: %s\n", eth_ntoa ( nic->node_addr ) ); + + return 0; +} + +static int amd8111e_init_rx_ring(struct amd8111e_priv *lp) +{ + int i; + + lp->rx_idx = 0; + + /* Initilaizing receive descriptors */ + for (i = 0; i < NUM_RX_SLOTS; i++) { + lp->rx_ring[i].buf_phy_addr = cpu_to_le32(virt_to_bus(lp->rx_buf[i])); + lp->rx_ring[i].buf_len = cpu_to_le16(RX_BUF_LEN); + wmb(); + lp->rx_ring[i].rx_flags = cpu_to_le16(OWN_BIT); + } + + return 0; +} + +static int amd8111e_init_tx_ring(struct amd8111e_priv *lp) +{ + int i; + + lp->tx_idx = 0; + lp->tx_consistent = 1; + + /* Initializing transmit descriptors */ + for (i = 0; i < NUM_TX_SLOTS; i++) { + lp->tx_ring[i].tx_flags = 0; + lp->tx_ring[i].buf_phy_addr = 0; + lp->tx_ring[i].buf_len = 0; + } + + return 0; +} + +static int amd8111e_wait_tx_ring(struct amd8111e_priv *lp, unsigned int index) +{ + volatile u16 status; + int retry = TX_RETRY; + + status = le16_to_cpu(lp->tx_ring[index].tx_flags); + while (--retry && (status & OWN_BIT)) { + mdelay(TX_PROCESS_TIME); + status = le16_to_cpu(lp->tx_ring[index].tx_flags); + } + if (status & OWN_BIT) { + printf("Error: tx slot %d timeout, stat = 0x%x\n", index, status); + amd8111e_restart(lp); + return -1; + } + + return 0; +} + +static void amd8111e_wait_link(struct amd8111e_priv *lp) +{ + unsigned int status; + u32 reg_val; + + do { + /* read phy to update STAT0 register */ + amd8111e_read_phy(lp, lp->ext_phy_addr, MII_BMCR, ®_val); + amd8111e_read_phy(lp, lp->ext_phy_addr, MII_BMSR, ®_val); + amd8111e_read_phy(lp, lp->ext_phy_addr, MII_ADVERTISE, ®_val); + amd8111e_read_phy(lp, lp->ext_phy_addr, MII_LPA, ®_val); + status = readl(lp->mmio + STAT0); + } while (!(status & AUTONEG_COMPLETE) || !(status & LINK_STATS)); +} + +static void amd8111e_poll_link(struct amd8111e_priv *lp) +{ + unsigned int status, speed; + u32 reg_val; + + if (!lp->link) { + /* read phy to update STAT0 register */ + amd8111e_read_phy(lp, lp->ext_phy_addr, MII_BMCR, ®_val); + amd8111e_read_phy(lp, lp->ext_phy_addr, MII_BMSR, ®_val); + amd8111e_read_phy(lp, lp->ext_phy_addr, MII_ADVERTISE, ®_val); + amd8111e_read_phy(lp, lp->ext_phy_addr, MII_LPA, ®_val); + status = readl(lp->mmio + STAT0); + + if (status & LINK_STATS) { + lp->link = 1; + speed = (status & SPEED_MASK) >> 7; + if (speed == PHY_SPEED_100) + lp->speed = 1; + else + lp->speed = 0; + if (status & FULL_DPLX) + lp->duplex = 1; + else + lp->duplex = 0; + + printf("Link is up: %s Mbps %s duplex\n", + lp->speed ? "100" : "10", lp->duplex ? "full" : "half"); + } + } else { + status = readl(lp->mmio + STAT0); + if (!(status & LINK_STATS)) { + lp->link = 0; + printf("Link is down\n"); + } + } +} + +static void amd8111e_restart(struct amd8111e_priv *lp) +{ + printf("\nStarting nic...\n"); + amd8111e_disable_interrupt(lp); + amd8111e_init_hw_default(lp); + amd8111e_probe_ext_phy(lp); + amd8111e_get_mac_address(lp); + amd8111e_start(lp); + + printf("Waiting link up...\n"); + lp->link = 0; + amd8111e_wait_link(lp); + amd8111e_poll_link(lp); +} + + +/******************************************************** + * Interface Functions * + ********************************************************/ + +static void amd8111e_transmit(struct nic *nic, const char *dst_addr, + unsigned int type, unsigned int size, const char *packet) +{ + struct amd8111e_priv *lp = nic->priv_data; + struct eth_frame *frame; + unsigned int index; + + /* check packet size */ + if (size > TX_PKT_LEN_MAX) { + printf("amd8111e_transmit(): too large packet, drop\n"); + return; + } + + /* get tx slot */ + index = lp->tx_idx; + if (amd8111e_wait_tx_ring(lp, index)) + return; + + /* fill frame */ + frame = (struct eth_frame *)lp->tx_buf[index]; + memset(frame->data, 0, TX_PKT_LEN_MAX); + memcpy(frame->dst_addr, dst_addr, ETH_ALEN); + memcpy(frame->src_addr, nic->node_addr, ETH_ALEN); + frame->type = htons(type); + memcpy(frame->data, packet, size); + + /* start xmit */ + lp->tx_ring[index].buf_len = cpu_to_le16(ETH_HLEN + size); + lp->tx_ring[index].buf_phy_addr = cpu_to_le32(virt_to_bus(frame)); + wmb(); + lp->tx_ring[index].tx_flags = + cpu_to_le16(OWN_BIT | STP_BIT | ENP_BIT | ADD_FCS_BIT | LTINT_BIT); + writel(VAL1 | TDMD0, lp->mmio + CMD0); + readl(lp->mmio + CMD0); + + /* update slot pointer */ + lp->tx_idx = (lp->tx_idx + 1) & TX_SLOTS_MASK; +} + +static int amd8111e_poll(struct nic *nic, int retrieve) +{ + /* return true if there's an ethernet packet ready to read */ + /* nic->packet should contain data on return */ + /* nic->packetlen should contain length of data */ + + struct amd8111e_priv *lp = nic->priv_data; + u16 status, pkt_len; + unsigned int index, pkt_ok; + + amd8111e_poll_link(lp); + + index = lp->rx_idx; + status = le16_to_cpu(lp->rx_ring[index].rx_flags); + pkt_len = le16_to_cpu(lp->rx_ring[index].msg_len) - 4; /* remove 4bytes FCS */ + + if (status & OWN_BIT) + return 0; + + if (status & ERR_BIT) + pkt_ok = 0; + else if (!(status & STP_BIT)) + pkt_ok = 0; + else if (!(status & ENP_BIT)) + pkt_ok = 0; + else if (pkt_len < RX_PKT_LEN_MIN) + pkt_ok = 0; + else if (pkt_len > RX_PKT_LEN_MAX) + pkt_ok = 0; + else + pkt_ok = 1; + + if (pkt_ok) { + if (!retrieve) + return 1; + nic->packetlen = pkt_len; + memcpy(nic->packet, lp->rx_buf[index], nic->packetlen); + } + + lp->rx_ring[index].buf_phy_addr = cpu_to_le32(virt_to_bus(lp->rx_buf[index])); + lp->rx_ring[index].buf_len = cpu_to_le16(RX_BUF_LEN); + wmb(); + lp->rx_ring[index].rx_flags = cpu_to_le16(OWN_BIT); + writel(VAL2 | RDMD0, lp->mmio + CMD0); + readl(lp->mmio + CMD0); + + lp->rx_idx = (lp->rx_idx + 1) & RX_SLOTS_MASK; + return pkt_ok; +} + +static void amd8111e_disable(struct nic *nic) +{ + struct amd8111e_priv *lp = nic->priv_data; + + /* disable interrupt */ + amd8111e_disable_interrupt(lp); + + /* stop chip */ + amd8111e_init_hw_default(lp); + + /* unmap mmio */ + iounmap(lp->mmio); + + /* update status */ + lp->opened = 0; +} + +static void amd8111e_irq(struct nic *nic, irq_action_t action) +{ + struct amd8111e_priv *lp = nic->priv_data; + + switch (action) { + case DISABLE: + amd8111e_disable_interrupt(lp); + break; + case ENABLE: + amd8111e_enable_interrupt(lp); + break; + case FORCE: + amd8111e_force_interrupt(lp); + break; + } +} + +static struct nic_operations amd8111e_operations = { + .connect = dummy_connect, + .poll = amd8111e_poll, + .transmit = amd8111e_transmit, + .irq = amd8111e_irq, +}; + +static int amd8111e_probe(struct nic *nic, struct pci_device *pdev) +{ + struct amd8111e_priv *lp = &amd8111e; + unsigned long mmio_start, mmio_len; + + nic->ioaddr = pdev->ioaddr; + nic->irqno = pdev->irq; + + mmio_start = pci_bar_start(pdev, PCI_BASE_ADDRESS_0); + mmio_len = pci_bar_size(pdev, PCI_BASE_ADDRESS_0); + + memset(lp, 0, sizeof(*lp)); + lp->pdev = pdev; + lp->nic = nic; + lp->mmio = ioremap(mmio_start, mmio_len); + lp->opened = 1; + adjust_pci_device(pdev); + + nic->priv_data = lp; + + amd8111e_restart(lp); + + nic->nic_op = &amd8111e_operations; + return 1; +} + +static struct pci_device_id amd8111e_nics[] = { + PCI_ROM(0x1022, 0x7462, "amd8111e", "AMD8111E"), +}; + +PCI_DRIVER ( amd8111e_driver, amd8111e_nics, PCI_NO_CLASS ); + +DRIVER ( "AMD8111E", nic_driver, pci_driver, amd8111e_driver, + amd8111e_probe, amd8111e_disable ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/amd8111e.h b/gpxe/src/drivers/net/amd8111e.h new file mode 100644 index 0000000..82b8f7a --- /dev/null +++ b/gpxe/src/drivers/net/amd8111e.h @@ -0,0 +1,629 @@ +/* + * Advanced Micro Devices Inc. AMD8111E Linux Network Driver + * Copyright (C) 2003 Advanced Micro Devices + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + +Module Name: + + amd8111e.h + +Abstract: + + AMD8111 based 10/100 Ethernet Controller driver definitions. + +Environment: + + Kernel Mode + +Revision History: + 3.0.0 + Initial Revision. + 3.0.1 +*/ + +#ifndef _AMD811E_H +#define _AMD811E_H + +/* Command style register access + +Registers CMD0, CMD2, CMD3,CMD7 and INTEN0 uses a write access technique called command style access. It allows the write to selected bits of this register without altering the bits that are not selected. Command style registers are divided into 4 bytes that can be written independently. Higher order bit of each byte is the value bit that specifies the value that will be written into the selected bits of register. + +eg., if the value 10011010b is written into the least significant byte of a command style register, bits 1,3 and 4 of the register will be set to 1, and the other bits will not be altered. If the value 00011010b is written into the same byte, bits 1,3 and 4 will be cleared to 0 and the other bits will not be altered. + +*/ + +/* Offset for Memory Mapped Registers. */ +/* 32 bit registers */ + +#define ASF_STAT 0x00 /* ASF status register */ +#define CHIPID 0x04 /* Chip ID regsiter */ +#define MIB_DATA 0x10 /* MIB data register */ +#define MIB_ADDR 0x14 /* MIB address register */ +#define STAT0 0x30 /* Status0 register */ +#define INT0 0x38 /* Interrupt0 register */ +#define INTEN0 0x40 /* Interrupt0 enable register*/ +#define CMD0 0x48 /* Command0 register */ +#define CMD2 0x50 /* Command2 register */ +#define CMD3 0x54 /* Command3 resiter */ +#define CMD7 0x64 /* Command7 register */ + +#define CTRL1 0x6C /* Control1 register */ +#define CTRL2 0x70 /* Control2 register */ + +#define XMT_RING_LIMIT 0x7C /* Transmit ring limit register */ + +#define AUTOPOLL0 0x88 /* Auto-poll0 register */ +#define AUTOPOLL1 0x8A /* Auto-poll1 register */ +#define AUTOPOLL2 0x8C /* Auto-poll2 register */ +#define AUTOPOLL3 0x8E /* Auto-poll3 register */ +#define AUTOPOLL4 0x90 /* Auto-poll4 register */ +#define AUTOPOLL5 0x92 /* Auto-poll5 register */ + +#define AP_VALUE 0x98 /* Auto-poll value register */ +#define DLY_INT_A 0xA8 /* Group A delayed interrupt register */ +#define DLY_INT_B 0xAC /* Group B delayed interrupt register */ + +#define FLOW_CONTROL 0xC8 /* Flow control register */ +#define PHY_ACCESS 0xD0 /* PHY access register */ + +#define STVAL 0xD8 /* Software timer value register */ + +#define XMT_RING_BASE_ADDR0 0x100 /* Transmit ring0 base addr register */ +#define XMT_RING_BASE_ADDR1 0x108 /* Transmit ring1 base addr register */ +#define XMT_RING_BASE_ADDR2 0x110 /* Transmit ring2 base addr register */ +#define XMT_RING_BASE_ADDR3 0x118 /* Transmit ring2 base addr register */ + +#define RCV_RING_BASE_ADDR0 0x120 /* Transmit ring0 base addr register */ + +#define PMAT0 0x190 /* OnNow pattern register0 */ +#define PMAT1 0x194 /* OnNow pattern register1 */ + +/* 16bit registers */ + +#define XMT_RING_LEN0 0x140 /* Transmit Ring0 length register */ +#define XMT_RING_LEN1 0x144 /* Transmit Ring1 length register */ +#define XMT_RING_LEN2 0x148 /* Transmit Ring2 length register */ +#define XMT_RING_LEN3 0x14C /* Transmit Ring3 length register */ + +#define RCV_RING_LEN0 0x150 /* Receive Ring0 length register */ + +#define SRAM_SIZE 0x178 /* SRAM size register */ +#define SRAM_BOUNDARY 0x17A /* SRAM boundary register */ + +/* 48bit register */ + +#define PADR 0x160 /* Physical address register */ + +#define IFS1 0x18C /* Inter-frame spacing Part1 register */ +#define IFS 0x18D /* Inter-frame spacing register */ +#define IPG 0x18E /* Inter-frame gap register */ +/* 64bit register */ + +#define LADRF 0x168 /* Logical address filter register */ + + +/* Register Bit Definitions */ +typedef enum { + + ASF_INIT_DONE = (1 << 1), + ASF_INIT_PRESENT = (1 << 0), + +}STAT_ASF_BITS; + +typedef enum { + + MIB_CMD_ACTIVE = (1 << 15 ), + MIB_RD_CMD = (1 << 13 ), + MIB_CLEAR = (1 << 12 ), + MIB_ADDRESS = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3)| + (1 << 4) | (1 << 5), +}MIB_ADDR_BITS; + + +typedef enum { + + PMAT_DET = (1 << 12), + MP_DET = (1 << 11), + LC_DET = (1 << 10), + SPEED_MASK = (1 << 9)|(1 << 8)|(1 << 7), + FULL_DPLX = (1 << 6), + LINK_STATS = (1 << 5), + AUTONEG_COMPLETE = (1 << 4), + MIIPD = (1 << 3), + RX_SUSPENDED = (1 << 2), + TX_SUSPENDED = (1 << 1), + RUNNING = (1 << 0), + +}STAT0_BITS; + +#define PHY_SPEED_10 0x2 +#define PHY_SPEED_100 0x3 + +/* INT0 0x38, 32bit register */ +typedef enum { + + INTR = (1 << 31), + PCSINT = (1 << 28), + LCINT = (1 << 27), + APINT5 = (1 << 26), + APINT4 = (1 << 25), + APINT3 = (1 << 24), + TINT_SUM = (1 << 23), + APINT2 = (1 << 22), + APINT1 = (1 << 21), + APINT0 = (1 << 20), + MIIPDTINT = (1 << 19), + MCCINT = (1 << 17), + MREINT = (1 << 16), + RINT_SUM = (1 << 15), + SPNDINT = (1 << 14), + MPINT = (1 << 13), + SINT = (1 << 12), + TINT3 = (1 << 11), + TINT2 = (1 << 10), + TINT1 = (1 << 9), + TINT0 = (1 << 8), + UINT = (1 << 7), + STINT = (1 << 4), + RINT0 = (1 << 0), + +}INT0_BITS; + +typedef enum { + + VAL3 = (1 << 31), /* VAL bit for byte 3 */ + VAL2 = (1 << 23), /* VAL bit for byte 2 */ + VAL1 = (1 << 15), /* VAL bit for byte 1 */ + VAL0 = (1 << 7), /* VAL bit for byte 0 */ + +}VAL_BITS; + +typedef enum { + + /* VAL3 */ + LCINTEN = (1 << 27), + APINT5EN = (1 << 26), + APINT4EN = (1 << 25), + APINT3EN = (1 << 24), + /* VAL2 */ + APINT2EN = (1 << 22), + APINT1EN = (1 << 21), + APINT0EN = (1 << 20), + MIIPDTINTEN = (1 << 19), + MCCIINTEN = (1 << 18), + MCCINTEN = (1 << 17), + MREINTEN = (1 << 16), + /* VAL1 */ + SPNDINTEN = (1 << 14), + MPINTEN = (1 << 13), + TINTEN3 = (1 << 11), + SINTEN = (1 << 12), + TINTEN2 = (1 << 10), + TINTEN1 = (1 << 9), + TINTEN0 = (1 << 8), + /* VAL0 */ + STINTEN = (1 << 4), + RINTEN0 = (1 << 0), + + INTEN0_CLEAR = 0x1F7F7F1F, /* Command style register */ + +}INTEN0_BITS; + +typedef enum { + /* VAL2 */ + RDMD0 = (1 << 16), + /* VAL1 */ + TDMD3 = (1 << 11), + TDMD2 = (1 << 10), + TDMD1 = (1 << 9), + TDMD0 = (1 << 8), + /* VAL0 */ + UINTCMD = (1 << 6), + RX_FAST_SPND = (1 << 5), + TX_FAST_SPND = (1 << 4), + RX_SPND = (1 << 3), + TX_SPND = (1 << 2), + INTREN = (1 << 1), + RUN = (1 << 0), + + CMD0_CLEAR = 0x000F0F7F, /* Command style register */ + +}CMD0_BITS; + +typedef enum { + + /* VAL3 */ + CONDUIT_MODE = (1 << 29), + /* VAL2 */ + RPA = (1 << 19), + DRCVPA = (1 << 18), + DRCVBC = (1 << 17), + PROM = (1 << 16), + /* VAL1 */ + ASTRP_RCV = (1 << 13), + RCV_DROP0 = (1 << 12), + EMBA = (1 << 11), + DXMT2PD = (1 << 10), + LTINTEN = (1 << 9), + DXMTFCS = (1 << 8), + /* VAL0 */ + APAD_XMT = (1 << 6), + DRTY = (1 << 5), + INLOOP = (1 << 4), + EXLOOP = (1 << 3), + REX_RTRY = (1 << 2), + REX_UFLO = (1 << 1), + REX_LCOL = (1 << 0), + + CMD2_CLEAR = 0x3F7F3F7F, /* Command style register */ + +}CMD2_BITS; + +typedef enum { + + /* VAL3 */ + ASF_INIT_DONE_ALIAS = (1 << 29), + /* VAL2 */ + JUMBO = (1 << 21), + VSIZE = (1 << 20), + VLONLY = (1 << 19), + VL_TAG_DEL = (1 << 18), + /* VAL1 */ + EN_PMGR = (1 << 14), + INTLEVEL = (1 << 13), + FORCE_FULL_DUPLEX = (1 << 12), + FORCE_LINK_STATUS = (1 << 11), + APEP = (1 << 10), + MPPLBA = (1 << 9), + /* VAL0 */ + RESET_PHY_PULSE = (1 << 2), + RESET_PHY = (1 << 1), + PHY_RST_POL = (1 << 0), + +}CMD3_BITS; + + +typedef enum { + + /* VAL0 */ + PMAT_SAVE_MATCH = (1 << 4), + PMAT_MODE = (1 << 3), + MPEN_SW = (1 << 1), + LCMODE_SW = (1 << 0), + + CMD7_CLEAR = 0x0000001B /* Command style register */ + +}CMD7_BITS; + + +typedef enum { + + RESET_PHY_WIDTH = (0xF << 16) | (0xF<< 20), /* 0x00FF0000 */ + XMTSP_MASK = (1 << 9) | (1 << 8), /* 9:8 */ + XMTSP_128 = (1 << 9), /* 9 */ + XMTSP_64 = (1 << 8), + CACHE_ALIGN = (1 << 4), + BURST_LIMIT_MASK = (0xF << 0 ), + CTRL1_DEFAULT = 0x00010111, + +}CTRL1_BITS; + +typedef enum { + + FMDC_MASK = (1 << 9)|(1 << 8), /* 9:8 */ + XPHYRST = (1 << 7), + XPHYANE = (1 << 6), + XPHYFD = (1 << 5), + XPHYSP = (1 << 4) | (1 << 3), /* 4:3 */ + APDW_MASK = (1 << 2) | (1 << 1) | (1 << 0), /* 2:0 */ + +}CTRL2_BITS; + +/* XMT_RING_LIMIT 0x7C, 32bit register */ +typedef enum { + + XMT_RING2_LIMIT = (0xFF << 16), /* 23:16 */ + XMT_RING1_LIMIT = (0xFF << 8), /* 15:8 */ + XMT_RING0_LIMIT = (0xFF << 0), /* 7:0 */ + +}XMT_RING_LIMIT_BITS; + +typedef enum { + + AP_REG0_EN = (1 << 15), + AP_REG0_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */ + AP_PHY0_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */ + +}AUTOPOLL0_BITS; + +/* AUTOPOLL1 0x8A, 16bit register */ +typedef enum { + + AP_REG1_EN = (1 << 15), + AP_REG1_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */ + AP_PRE_SUP1 = (1 << 6), + AP_PHY1_DFLT = (1 << 5), + AP_PHY1_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */ + +}AUTOPOLL1_BITS; + + +typedef enum { + + AP_REG2_EN = (1 << 15), + AP_REG2_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */ + AP_PRE_SUP2 = (1 << 6), + AP_PHY2_DFLT = (1 << 5), + AP_PHY2_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */ + +}AUTOPOLL2_BITS; + +typedef enum { + + AP_REG3_EN = (1 << 15), + AP_REG3_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */ + AP_PRE_SUP3 = (1 << 6), + AP_PHY3_DFLT = (1 << 5), + AP_PHY3_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */ + +}AUTOPOLL3_BITS; + + +typedef enum { + + AP_REG4_EN = (1 << 15), + AP_REG4_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */ + AP_PRE_SUP4 = (1 << 6), + AP_PHY4_DFLT = (1 << 5), + AP_PHY4_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */ + +}AUTOPOLL4_BITS; + + +typedef enum { + + AP_REG5_EN = (1 << 15), + AP_REG5_ADDR_MASK = (0xF << 8) |(1 << 12),/* 12:8 */ + AP_PRE_SUP5 = (1 << 6), + AP_PHY5_DFLT = (1 << 5), + AP_PHY5_ADDR_MASK = (0xF << 0) |(1 << 4),/* 4:0 */ + +}AUTOPOLL5_BITS; + + + + +/* AP_VALUE 0x98, 32bit ragister */ +typedef enum { + + AP_VAL_ACTIVE = (1 << 31), + AP_VAL_RD_CMD = ( 1 << 29), + AP_ADDR = (1 << 18)|(1 << 17)|(1 << 16), /* 18:16 */ + AP_VAL = (0xF << 0) | (0xF << 4) |( 0xF << 8) | + (0xF << 12), /* 15:0 */ + +}AP_VALUE_BITS; + +typedef enum { + + DLY_INT_A_R3 = (1 << 31), + DLY_INT_A_R2 = (1 << 30), + DLY_INT_A_R1 = (1 << 29), + DLY_INT_A_R0 = (1 << 28), + DLY_INT_A_T3 = (1 << 27), + DLY_INT_A_T2 = (1 << 26), + DLY_INT_A_T1 = (1 << 25), + DLY_INT_A_T0 = ( 1 << 24), + EVENT_COUNT_A = (0xF << 16) | (0x1 << 20),/* 20:16 */ + MAX_DELAY_TIME_A = (0xF << 0) | (0xF << 4) | (1 << 8)| + (1 << 9) | (1 << 10), /* 10:0 */ + +}DLY_INT_A_BITS; + +typedef enum { + + DLY_INT_B_R3 = (1 << 31), + DLY_INT_B_R2 = (1 << 30), + DLY_INT_B_R1 = (1 << 29), + DLY_INT_B_R0 = (1 << 28), + DLY_INT_B_T3 = (1 << 27), + DLY_INT_B_T2 = (1 << 26), + DLY_INT_B_T1 = (1 << 25), + DLY_INT_B_T0 = ( 1 << 24), + EVENT_COUNT_B = (0xF << 16) | (0x1 << 20),/* 20:16 */ + MAX_DELAY_TIME_B = (0xF << 0) | (0xF << 4) | (1 << 8)| + (1 << 9) | (1 << 10), /* 10:0 */ +}DLY_INT_B_BITS; + + +/* FLOW_CONTROL 0xC8, 32bit register */ +typedef enum { + + PAUSE_LEN_CHG = (1 << 30), + FTPE = (1 << 22), + FRPE = (1 << 21), + NAPA = (1 << 20), + NPA = (1 << 19), + FIXP = ( 1 << 18), + FCCMD = ( 1 << 16), + PAUSE_LEN = (0xF << 0) | (0xF << 4) |( 0xF << 8) | (0xF << 12), /* 15:0 */ + +}FLOW_CONTROL_BITS; + +/* PHY_ ACCESS 0xD0, 32bit register */ +typedef enum { + + PHY_CMD_ACTIVE = (1 << 31), + PHY_WR_CMD = (1 << 30), + PHY_RD_CMD = (1 << 29), + PHY_RD_ERR = (1 << 28), + PHY_PRE_SUP = (1 << 27), + PHY_ADDR = (1 << 21) | (1 << 22) | (1 << 23)| + (1 << 24) |(1 << 25),/* 25:21 */ + PHY_REG_ADDR = (1 << 16) | (1 << 17) | (1 << 18)| (1 << 19) | (1 << 20),/* 20:16 */ + PHY_DATA = (0xF << 0)|(0xF << 4) |(0xF << 8)| + (0xF << 12),/* 15:0 */ + +}PHY_ACCESS_BITS; + + +/* PMAT0 0x190, 32bit register */ +typedef enum { + PMR_ACTIVE = (1 << 31), + PMR_WR_CMD = (1 << 30), + PMR_RD_CMD = (1 << 29), + PMR_BANK = (1 <<28), + PMR_ADDR = (0xF << 16)|(1 << 20)|(1 << 21)| + (1 << 22),/* 22:16 */ + PMR_B4 = (0xF << 0) | (0xF << 4),/* 15:0 */ +}PMAT0_BITS; + + +/* PMAT1 0x194, 32bit register */ +typedef enum { + PMR_B3 = (0xF << 24) | (0xF <<28),/* 31:24 */ + PMR_B2 = (0xF << 16) |(0xF << 20),/* 23:16 */ + PMR_B1 = (0xF << 8) | (0xF <<12), /* 15:8 */ + PMR_B0 = (0xF << 0)|(0xF << 4),/* 7:0 */ +}PMAT1_BITS; + +/************************************************************************/ +/* */ +/* MIB counter definitions */ +/* */ +/************************************************************************/ + +#define rcv_miss_pkts 0x00 +#define rcv_octets 0x01 +#define rcv_broadcast_pkts 0x02 +#define rcv_multicast_pkts 0x03 +#define rcv_undersize_pkts 0x04 +#define rcv_oversize_pkts 0x05 +#define rcv_fragments 0x06 +#define rcv_jabbers 0x07 +#define rcv_unicast_pkts 0x08 +#define rcv_alignment_errors 0x09 +#define rcv_fcs_errors 0x0A +#define rcv_good_octets 0x0B +#define rcv_mac_ctrl 0x0C +#define rcv_flow_ctrl 0x0D +#define rcv_pkts_64_octets 0x0E +#define rcv_pkts_65to127_octets 0x0F +#define rcv_pkts_128to255_octets 0x10 +#define rcv_pkts_256to511_octets 0x11 +#define rcv_pkts_512to1023_octets 0x12 +#define rcv_pkts_1024to1518_octets 0x13 +#define rcv_unsupported_opcode 0x14 +#define rcv_symbol_errors 0x15 +#define rcv_drop_pkts_ring1 0x16 +#define rcv_drop_pkts_ring2 0x17 +#define rcv_drop_pkts_ring3 0x18 +#define rcv_drop_pkts_ring4 0x19 +#define rcv_jumbo_pkts 0x1A + +#define xmt_underrun_pkts 0x20 +#define xmt_octets 0x21 +#define xmt_packets 0x22 +#define xmt_broadcast_pkts 0x23 +#define xmt_multicast_pkts 0x24 +#define xmt_collisions 0x25 +#define xmt_unicast_pkts 0x26 +#define xmt_one_collision 0x27 +#define xmt_multiple_collision 0x28 +#define xmt_deferred_transmit 0x29 +#define xmt_late_collision 0x2A +#define xmt_excessive_defer 0x2B +#define xmt_loss_carrier 0x2C +#define xmt_excessive_collision 0x2D +#define xmt_back_pressure 0x2E +#define xmt_flow_ctrl 0x2F +#define xmt_pkts_64_octets 0x30 +#define xmt_pkts_65to127_octets 0x31 +#define xmt_pkts_128to255_octets 0x32 +#define xmt_pkts_256to511_octets 0x33 +#define xmt_pkts_512to1023_octets 0x34 +#define xmt_pkts_1024to1518_octet 0x35 +#define xmt_oversize_pkts 0x36 +#define xmt_jumbo_pkts 0x37 + +/* ipg parameters */ +#define DEFAULT_IPG 0x60 +#define IFS1_DELTA 36 +#define IPG_CONVERGE_JIFFIES (HZ/2) +#define IPG_STABLE_TIME 5 +#define MIN_IPG 96 +#define MAX_IPG 255 +#define IPG_STEP 16 +#define CSTATE 1 +#define SSTATE 2 + +/* amd8111e decriptor flag definitions */ +typedef enum { + + OWN_BIT = (1 << 15), + ADD_FCS_BIT = (1 << 13), + LTINT_BIT = (1 << 12), + STP_BIT = (1 << 9), + ENP_BIT = (1 << 8), + KILL_BIT = (1 << 6), + TCC_VLAN_INSERT = (1 << 1), + TCC_VLAN_REPLACE = (1 << 1) |( 1<< 0), + +}TX_FLAG_BITS; + +typedef enum { + ERR_BIT = (1 << 14), + FRAM_BIT = (1 << 13), + OFLO_BIT = (1 << 12), + CRC_BIT = (1 << 11), + PAM_BIT = (1 << 6), + LAFM_BIT = (1 << 5), + BAM_BIT = (1 << 4), + TT_VLAN_TAGGED = (1 << 3) |(1 << 2),/* 0x000 */ + TT_PRTY_TAGGED = (1 << 3),/* 0x0008 */ + +}RX_FLAG_BITS; + +#define RESET_RX_FLAGS 0x0000 +#define TT_MASK 0x000c +#define TCC_MASK 0x0003 + +/* driver ioctl parameters */ +#define AMD8111E_REG_DUMP_LEN 13*sizeof(u32) + +/* crc generator constants */ +#define CRC32 0xedb88320 +#define INITCRC 0xFFFFFFFF + +/* kernel provided writeq does not write 64 bits into the amd8111e device register instead writes only higher 32bits data into lower 32bits of the register. +BUG? */ +#define amd8111e_writeq(_UlData,_memMap) \ + writel(*(u32*)(&_UlData), _memMap); \ + writel(*(u32*)((u8*)(&_UlData)+4), _memMap+4) + +/* maps the external speed options to internal value */ +typedef enum { + SPEED_AUTONEG, + SPEED10_HALF, + SPEED10_FULL, + SPEED100_HALF, + SPEED100_FULL, +}EXT_PHY_OPTION; + + +#endif /* _AMD8111E_H */ + diff --git a/gpxe/src/drivers/net/b44.c b/gpxe/src/drivers/net/b44.c new file mode 100644 index 0000000..4c9c8e6 --- /dev/null +++ b/gpxe/src/drivers/net/b44.c @@ -0,0 +1,949 @@ +/* + * Copyright (c) 2008 Stefan Hajnoczi + * Copyright (c) 2008 Pantelis Koukousoulas + * + * 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. + * + * This driver is a port of the b44 linux driver version 1.01 + * + * Copyright (c) 2002 David S. Miller + * Copyright (c) Pekka Pietikainen + * Copyright (C) 2006 Broadcom Corporation. + * + * Some ssb bits copied from version 2.0 of the b44 driver + * Copyright (c) Michael Buesch + * + * Copyright (c) a lot of people too. Please respect their work. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "b44.h" + + +static inline int ring_next(int index) +{ + /* B44_RING_SIZE is a power of 2 :) */ + return (index + 1) & (B44_RING_SIZE - 1); +} + + +/* Memory-mapped I/O wrappers */ + +static inline u32 br32(const struct b44_private *bp, u32 reg) +{ + return readl(bp->regs + reg); +} + + +static inline void bw32(const struct b44_private *bp, u32 reg, u32 val) +{ + writel(val, bp->regs + reg); +} + + +static inline void bflush(const struct b44_private *bp, u32 reg, u32 timeout) +{ + readl(bp->regs + reg); + udelay(timeout); +} + + +#define VIRT_TO_B44(addr) ( virt_to_bus(addr) + SB_PCI_DMA ) + + +/** + * Return non-zero if the installed RAM is within + * the limit given and zero if it is outside. + * Hopefully will be removed soon. + */ +int phys_ram_within_limit(u64 limit) +{ + struct memory_map memmap; + struct memory_region *highest = NULL; + get_memmap(&memmap); + + highest = &memmap.regions[memmap.count - 1]; + + return (highest->end < limit); +} + + +/** + * Ring cells waiting to be processed are between 'tx_cur' and 'pending' + * indexes in the ring. + */ +static u32 pending_tx_index(struct b44_private *bp) +{ + u32 pending = br32(bp, B44_DMATX_STAT); + pending &= DMATX_STAT_CDMASK; + + pending /= sizeof(struct dma_desc); + return pending & (B44_RING_SIZE - 1); +} + + +/** + * Ring cells waiting to be processed are between 'rx_cur' and 'pending' + * indexes in the ring. + */ +static u32 pending_rx_index(struct b44_private *bp) +{ + u32 pending = br32(bp, B44_DMARX_STAT); + pending &= DMARX_STAT_CDMASK; + + pending /= sizeof(struct dma_desc); + return pending & (B44_RING_SIZE - 1); +} + + +/** + * Wait until the given bit is set/cleared. + */ +static int b44_wait_bit(struct b44_private *bp, unsigned long reg, u32 bit, + unsigned long timeout, const int clear) +{ + unsigned long i; + + for (i = 0; i < timeout; i++) { + u32 val = br32(bp, reg); + + if (clear && !(val & bit)) + break; + + if (!clear && (val & bit)) + break; + + udelay(10); + } + if (i == timeout) { + return -ENODEV; + } + return 0; +} + + +/* + * Sonics Silicon Backplane support. SSB is a mini-bus interconnecting + * so-called IP Cores. One of those cores implements the Fast Ethernet + * functionality and another one the PCI engine. + * + * You need to switch to the core you want to talk to before actually + * sending commands. + * + * See: http://bcm-v4.sipsolutions.net/Backplane for (reverse-engineered) + * specs. + */ + +static inline u32 ssb_get_core_rev(struct b44_private *bp) +{ + return (br32(bp, B44_SBIDHIGH) & SBIDHIGH_RC_MASK); +} + + +static inline int ssb_is_core_up(struct b44_private *bp) +{ + return ((br32(bp, B44_SBTMSLOW) & (SSB_CORE_DOWN | SBTMSLOW_CLOCK)) + == SBTMSLOW_CLOCK); +} + + +static u32 ssb_pci_setup(struct b44_private *bp, u32 cores) +{ + u32 bar_orig, pci_rev, val; + + pci_read_config_dword(bp->pci, SSB_BAR0_WIN, &bar_orig); + pci_write_config_dword(bp->pci, SSB_BAR0_WIN, + BCM4400_PCI_CORE_ADDR); + pci_rev = ssb_get_core_rev(bp); + + val = br32(bp, B44_SBINTVEC); + val |= cores; + bw32(bp, B44_SBINTVEC, val); + + val = br32(bp, SSB_PCI_TRANS_2); + val |= SSB_PCI_PREF | SSB_PCI_BURST; + bw32(bp, SSB_PCI_TRANS_2, val); + + pci_write_config_dword(bp->pci, SSB_BAR0_WIN, bar_orig); + + return pci_rev; +} + + +static void ssb_core_disable(struct b44_private *bp) +{ + if (br32(bp, B44_SBTMSLOW) & SBTMSLOW_RESET) + return; + + bw32(bp, B44_SBTMSLOW, (SBTMSLOW_REJECT | SBTMSLOW_CLOCK)); + b44_wait_bit(bp, B44_SBTMSLOW, SBTMSLOW_REJECT, 100000, 0); + b44_wait_bit(bp, B44_SBTMSHIGH, SBTMSHIGH_BUSY, 100000, 1); + + bw32(bp, B44_SBTMSLOW, (SBTMSLOW_FGC | SBTMSLOW_CLOCK | + SSB_CORE_DOWN)); + bflush(bp, B44_SBTMSLOW, 1); + + bw32(bp, B44_SBTMSLOW, SSB_CORE_DOWN); + bflush(bp, B44_SBTMSLOW, 1); +} + + +static void ssb_core_reset(struct b44_private *bp) +{ + u32 val; + const u32 mask = (SBTMSLOW_CLOCK | SBTMSLOW_FGC | SBTMSLOW_RESET); + + ssb_core_disable(bp); + + bw32(bp, B44_SBTMSLOW, mask); + bflush(bp, B44_SBTMSLOW, 1); + + /* Clear SERR if set, this is a hw bug workaround. */ + if (br32(bp, B44_SBTMSHIGH) & SBTMSHIGH_SERR) + bw32(bp, B44_SBTMSHIGH, 0); + + val = br32(bp, B44_SBIMSTATE); + if (val & (SBIMSTATE_BAD)) { + bw32(bp, B44_SBIMSTATE, val & ~SBIMSTATE_BAD); + } + + bw32(bp, B44_SBTMSLOW, (SBTMSLOW_CLOCK | SBTMSLOW_FGC)); + bflush(bp, B44_SBTMSLOW, 1); + + bw32(bp, B44_SBTMSLOW, (SBTMSLOW_CLOCK)); + bflush(bp, B44_SBTMSLOW, 1); +} + + +/* + * Driver helper functions + */ + +/* + * Chip reset provides power to the b44 MAC & PCI cores, which + * is necessary for MAC register access. We only do a partial + * reset in case of transmit/receive errors (ISTAT_ERRORS) to + * avoid the chip being hung for an unnecessary long time in + * this case. + * + * Called-by: b44_close, b44_halt, b44_inithw(b44_open), b44_probe + */ +static void b44_chip_reset(struct b44_private *bp, int reset_kind) +{ + if (ssb_is_core_up(bp)) { + bw32(bp, B44_RCV_LAZY, 0); + + bw32(bp, B44_ENET_CTRL, ENET_CTRL_DISABLE); + + b44_wait_bit(bp, B44_ENET_CTRL, ENET_CTRL_DISABLE, 200, 1); + + bw32(bp, B44_DMATX_CTRL, 0); + + bp->tx_dirty = bp->tx_cur = 0; + + if (br32(bp, B44_DMARX_STAT) & DMARX_STAT_EMASK) + b44_wait_bit(bp, B44_DMARX_STAT, DMARX_STAT_SIDLE, + 100, 0); + + bw32(bp, B44_DMARX_CTRL, 0); + + bp->rx_cur = 0; + } else { + ssb_pci_setup(bp, SBINTVEC_ENET0); + } + + ssb_core_reset(bp); + + /* Don't enable PHY if we are only doing a partial reset. */ + if (reset_kind == B44_CHIP_RESET_PARTIAL) + return; + + /* Make PHY accessible. */ + bw32(bp, B44_MDIO_CTRL, + (MDIO_CTRL_PREAMBLE | (0x0d & MDIO_CTRL_MAXF_MASK))); + bflush(bp, B44_MDIO_CTRL, 1); + + /* Enable internal or external PHY */ + if (!(br32(bp, B44_DEVCTRL) & DEVCTRL_IPP)) { + bw32(bp, B44_ENET_CTRL, ENET_CTRL_EPSEL); + bflush(bp, B44_ENET_CTRL, 1); + } else { + u32 val = br32(bp, B44_DEVCTRL); + if (val & DEVCTRL_EPR) { + bw32(bp, B44_DEVCTRL, (val & ~DEVCTRL_EPR)); + bflush(bp, B44_DEVCTRL, 100); + } + } +} + + +/** + * called by b44_poll in the error path + */ +static void b44_halt(struct b44_private *bp) +{ + /* disable ints */ + bw32(bp, B44_IMASK, 0); + bflush(bp, B44_IMASK, 1); + + DBG("b44: powering down PHY\n"); + bw32(bp, B44_MAC_CTRL, MAC_CTRL_PHY_PDOWN); + + /* + * Now reset the chip, but without enabling + * the MAC&PHY part of it. + * This has to be done _after_ we shut down the PHY + */ + b44_chip_reset(bp, B44_CHIP_RESET_PARTIAL); +} + + + +/* + * Called at device open time to get the chip ready for + * packet processing. + * + * Called-by: b44_open + */ +static void b44_init_hw(struct b44_private *bp, int reset_kind) +{ + u32 val; +#define CTRL_MASK (DMARX_CTRL_ENABLE | (RX_PKT_OFFSET << DMARX_CTRL_ROSHIFT)) + + b44_chip_reset(bp, B44_CHIP_RESET_FULL); + if (reset_kind == B44_FULL_RESET) { + b44_phy_reset(bp); + } + + /* Enable CRC32, set proper LED modes and power on PHY */ + bw32(bp, B44_MAC_CTRL, MAC_CTRL_CRC32_ENAB | MAC_CTRL_PHY_LEDCTRL); + bw32(bp, B44_RCV_LAZY, (1 << RCV_LAZY_FC_SHIFT)); + + /* This sets the MAC address too. */ + b44_set_rx_mode(bp->netdev); + + /* MTU + eth header + possible VLAN tag + struct rx_header */ + bw32(bp, B44_RXMAXLEN, B44_MAX_MTU + ETH_HLEN + 8 + RX_HEADER_LEN); + bw32(bp, B44_TXMAXLEN, B44_MAX_MTU + ETH_HLEN + 8 + RX_HEADER_LEN); + + bw32(bp, B44_TX_HIWMARK, TX_HIWMARK_DEFLT); + if (reset_kind == B44_PARTIAL_RESET) { + bw32(bp, B44_DMARX_CTRL, CTRL_MASK); + } else { + bw32(bp, B44_DMATX_CTRL, DMATX_CTRL_ENABLE); + bw32(bp, B44_DMATX_ADDR, VIRT_TO_B44(bp->tx)); + + bw32(bp, B44_DMARX_CTRL, CTRL_MASK); + bw32(bp, B44_DMARX_ADDR, VIRT_TO_B44(bp->rx)); + bw32(bp, B44_DMARX_PTR, B44_RX_RING_LEN_BYTES); + + bw32(bp, B44_MIB_CTRL, MIB_CTRL_CLR_ON_READ); + } + + val = br32(bp, B44_ENET_CTRL); + bw32(bp, B44_ENET_CTRL, (val | ENET_CTRL_ENABLE)); +#undef CTRL_MASK +} + + +/*** Management of ring descriptors ***/ + + +static void b44_populate_rx_descriptor(struct b44_private *bp, u32 idx) +{ + struct rx_header *rh; + u32 ctrl, addr; + + rh = bp->rx_iobuf[idx]->data; + rh->len = 0; + rh->flags = 0; + ctrl = DESC_CTRL_LEN & (RX_PKT_BUF_SZ - RX_PKT_OFFSET); + if (idx == B44_RING_LAST) { + ctrl |= DESC_CTRL_EOT; + } + addr = VIRT_TO_B44(bp->rx_iobuf[idx]->data); + + bp->rx[idx].ctrl = cpu_to_le32(ctrl); + bp->rx[idx].addr = cpu_to_le32(addr); + bw32(bp, B44_DMARX_PTR, idx * sizeof(struct dma_desc)); +} + + +/* + * Refill RX ring descriptors with buffers. This is needed + * because during rx we are passing ownership of descriptor + * buffers to the network stack. + */ +static void b44_rx_refill(struct b44_private *bp, u32 pending) +{ + u32 i; + + // skip pending + for (i = pending + 1; i != bp->rx_cur; i = ring_next(i)) { + if (bp->rx_iobuf[i] != NULL) + continue; + + bp->rx_iobuf[i] = alloc_iob(RX_PKT_BUF_SZ); + if (!bp->rx_iobuf[i]) { + DBG("Refill rx ring failed!!\n"); + break; + } + + b44_populate_rx_descriptor(bp, i); + } +} + + +static void b44_free_rx_ring(struct b44_private *bp) +{ + u32 i; + + if (bp->rx) { + for (i = 0; i < B44_RING_SIZE; i++) { + free_iob(bp->rx_iobuf[i]); + bp->rx_iobuf[i] = NULL; + } + free_dma(bp->rx, B44_RX_RING_LEN_BYTES); + bp->rx = NULL; + } +} + + +static int b44_init_rx_ring(struct b44_private *bp) +{ + b44_free_rx_ring(bp); + + bp->rx = malloc_dma(B44_RX_RING_LEN_BYTES, B44_DMA_ALIGNMENT); + if (!bp->rx) + return -ENOMEM; + + memset(bp->rx_iobuf, 0, sizeof(bp->rx_iobuf)); + + bp->rx_iobuf[0] = alloc_iob(RX_PKT_BUF_SZ); + b44_populate_rx_descriptor(bp, 0); + b44_rx_refill(bp, 0); + + DBG("Init RX rings: rx=0x%08lx\n", VIRT_TO_B44(bp->rx)); + return 0; +} + + +static void b44_free_tx_ring(struct b44_private *bp) +{ + if (bp->tx) { + free_dma(bp->tx, B44_TX_RING_LEN_BYTES); + bp->tx = NULL; + } +} + + +static int b44_init_tx_ring(struct b44_private *bp) +{ + b44_free_tx_ring(bp); + + bp->tx = malloc_dma(B44_TX_RING_LEN_BYTES, B44_DMA_ALIGNMENT); + if (!bp->tx) + return -ENOMEM; + + memset(bp->tx, 0, B44_TX_RING_LEN_BYTES); + memset(bp->tx_iobuf, 0, sizeof(bp->tx_iobuf)); + + DBG("Init TX rings: tx=0x%08lx\n", VIRT_TO_B44(bp->tx)); + return 0; +} + + +/*** Interaction with the PHY ***/ + + +static int b44_phy_read(struct b44_private *bp, int reg, u32 * val) +{ + int err; + + u32 arg1 = (MDIO_OP_READ << MDIO_DATA_OP_SHIFT); + u32 arg2 = (bp->phy_addr << MDIO_DATA_PMD_SHIFT); + u32 arg3 = (reg << MDIO_DATA_RA_SHIFT); + u32 arg4 = (MDIO_TA_VALID << MDIO_DATA_TA_SHIFT); + u32 argv = arg1 | arg2 | arg3 | arg4; + + bw32(bp, B44_EMAC_ISTAT, EMAC_INT_MII); + bw32(bp, B44_MDIO_DATA, (MDIO_DATA_SB_START | argv)); + err = b44_wait_bit(bp, B44_EMAC_ISTAT, EMAC_INT_MII, 100, 0); + *val = br32(bp, B44_MDIO_DATA) & MDIO_DATA_DATA; + + return err; +} + + +static int b44_phy_write(struct b44_private *bp, int reg, u32 val) +{ + u32 arg1 = (MDIO_OP_WRITE << MDIO_DATA_OP_SHIFT); + u32 arg2 = (bp->phy_addr << MDIO_DATA_PMD_SHIFT); + u32 arg3 = (reg << MDIO_DATA_RA_SHIFT); + u32 arg4 = (MDIO_TA_VALID << MDIO_DATA_TA_SHIFT); + u32 arg5 = (val & MDIO_DATA_DATA); + u32 argv = arg1 | arg2 | arg3 | arg4 | arg5; + + + bw32(bp, B44_EMAC_ISTAT, EMAC_INT_MII); + bw32(bp, B44_MDIO_DATA, (MDIO_DATA_SB_START | argv)); + return b44_wait_bit(bp, B44_EMAC_ISTAT, EMAC_INT_MII, 100, 0); +} + + +static int b44_phy_reset(struct b44_private *bp) +{ + u32 val; + int err; + + err = b44_phy_write(bp, MII_BMCR, BMCR_RESET); + if (err) + return err; + + udelay(100); + err = b44_phy_read(bp, MII_BMCR, &val); + if (!err) { + if (val & BMCR_RESET) { + return -ENODEV; + } + } + + return 0; +} + + +/* + * The BCM44xx CAM (Content Addressable Memory) stores the MAC + * and PHY address. + */ +static void b44_cam_write(struct b44_private *bp, unsigned char *data, + int index) +{ + u32 val; + + val = ((u32) data[2]) << 24; + val |= ((u32) data[3]) << 16; + val |= ((u32) data[4]) << 8; + val |= ((u32) data[5]) << 0; + bw32(bp, B44_CAM_DATA_LO, val); + + + val = (CAM_DATA_HI_VALID | + (((u32) data[0]) << 8) | (((u32) data[1]) << 0)); + + bw32(bp, B44_CAM_DATA_HI, val); + + val = CAM_CTRL_WRITE | (index << CAM_CTRL_INDEX_SHIFT); + bw32(bp, B44_CAM_CTRL, val); + + b44_wait_bit(bp, B44_CAM_CTRL, CAM_CTRL_BUSY, 100, 1); +} + + +static void b44_set_mac_addr(struct b44_private *bp) +{ + u32 val; + bw32(bp, B44_CAM_CTRL, 0); + b44_cam_write(bp, bp->netdev->ll_addr, 0); + val = br32(bp, B44_CAM_CTRL); + bw32(bp, B44_CAM_CTRL, val | CAM_CTRL_ENABLE); +} + + +/* Read 128-bytes of EEPROM. */ +static void b44_read_eeprom(struct b44_private *bp, u8 * data) +{ + long i; + u16 *ptr = (u16 *) data; + + for (i = 0; i < 128; i += 2) + ptr[i / 2] = cpu_to_le16(readw(bp->regs + 4096 + i)); +} + + +static void b44_load_mac_and_phy_addr(struct b44_private *bp) +{ + u8 eeprom[128]; + + /* Load MAC address, note byteswapping */ + b44_read_eeprom(bp, &eeprom[0]); + bp->netdev->ll_addr[0] = eeprom[79]; + bp->netdev->ll_addr[1] = eeprom[78]; + bp->netdev->ll_addr[2] = eeprom[81]; + bp->netdev->ll_addr[3] = eeprom[80]; + bp->netdev->ll_addr[4] = eeprom[83]; + bp->netdev->ll_addr[5] = eeprom[82]; + + /* Load PHY address */ + bp->phy_addr = eeprom[90] & 0x1f; +} + + +static void b44_set_rx_mode(struct net_device *netdev) +{ + struct b44_private *bp = netdev_priv(netdev); + unsigned char zero[6] = { 0, 0, 0, 0, 0, 0 }; + u32 val; + int i; + + val = br32(bp, B44_RXCONFIG); + val &= ~RXCONFIG_PROMISC; + val |= RXCONFIG_ALLMULTI; + + b44_set_mac_addr(bp); + + for (i = 1; i < 64; i++) + b44_cam_write(bp, zero, i); + + bw32(bp, B44_RXCONFIG, val); + val = br32(bp, B44_CAM_CTRL); + bw32(bp, B44_CAM_CTRL, val | CAM_CTRL_ENABLE); +} + + +/*** Implementation of gPXE driver callbacks ***/ + +/** + * Probe device + * + * @v pci PCI device + * @v id Matching entry in ID table + * @ret rc Return status code + */ +static int b44_probe(struct pci_device *pci, const struct pci_device_id *id) +{ + struct net_device *netdev; + struct b44_private *bp; + int rc; + + /* + * Bail out if more than 1GB of physical RAM is installed. + * This limitation will be removed later when dma mapping + * is merged into mainline. + */ + if (!phys_ram_within_limit(B44_30BIT_DMA_MASK)) { + DBG("Sorry, this version of the driver does not\n" + "support systems with more than 1GB of RAM.\n"); + return -ENOMEM; + } + + /* Set up netdev */ + netdev = alloc_etherdev(sizeof(*bp)); + if (!netdev) + return -ENOMEM; + + netdev_init(netdev, &b44_operations); + pci_set_drvdata(pci, netdev); + netdev->dev = &pci->dev; + + /* Set up private data */ + bp = netdev_priv(netdev); + memset(bp, 0, sizeof(*bp)); + bp->netdev = netdev; + bp->pci = pci; + + /* Map device registers */ + bp->regs = ioremap(pci->membase, B44_REGS_SIZE); + if (!bp->regs) { + netdev_put(netdev); + return -ENOMEM; + } + + /* Enable PCI bus mastering */ + adjust_pci_device(pci); + + b44_load_mac_and_phy_addr(bp); + + /* Link management currently not implemented */ + netdev_link_up(netdev); + + rc = register_netdev(netdev); + if (rc != 0) { + iounmap(bp->regs); + netdev_put(netdev); + return rc; + } + + b44_chip_reset(bp, B44_CHIP_RESET_FULL); + + DBG("b44 %s (%04x:%04x) regs=%p MAC=%s\n", id->name, id->vendor, + id->device, bp->regs, eth_ntoa(netdev->ll_addr)); + + return 0; +} + + +/** + * Remove device + * + * @v pci PCI device + */ +static void b44_remove(struct pci_device *pci) +{ + struct net_device *netdev = pci_get_drvdata(pci); + struct b44_private *bp = netdev_priv(netdev); + + ssb_core_disable(bp); + unregister_netdev(netdev); + iounmap(bp->regs); + netdev_nullify(netdev); + netdev_put(netdev); +} + + +/** Enable or disable interrupts + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ +static void b44_irq(struct net_device *netdev, int enable) +{ + struct b44_private *bp = netdev_priv(netdev); + + /* Interrupt mask specifies which events generate interrupts */ + bw32(bp, B44_IMASK, enable ? IMASK_DEF : IMASK_DISABLE); +} + + +/** Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int b44_open(struct net_device *netdev) +{ + struct b44_private *bp = netdev_priv(netdev); + int rc; + + rc = b44_init_tx_ring(bp); + if (rc != 0) + return rc; + + rc = b44_init_rx_ring(bp); + if (rc != 0) + return rc; + + b44_init_hw(bp, B44_FULL_RESET); + + /* Disable interrupts */ + b44_irq(netdev, 0); + + return 0; +} + + +/** Close network device + * + * @v netdev Network device + */ +static void b44_close(struct net_device *netdev) +{ + struct b44_private *bp = netdev_priv(netdev); + + b44_chip_reset(bp, B44_FULL_RESET); + b44_free_tx_ring(bp); + b44_free_rx_ring(bp); +} + + +/** Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int b44_transmit(struct net_device *netdev, struct io_buffer *iobuf) +{ + struct b44_private *bp = netdev_priv(netdev); + u32 cur = bp->tx_cur; + u32 ctrl; + + /* Check for TX ring overflow */ + if (bp->tx[cur].ctrl) { + DBG("tx overflow\n"); + return -ENOBUFS; + } + + /* Will call netdev_tx_complete() on the iobuf later */ + bp->tx_iobuf[cur] = iobuf; + + /* Set up TX descriptor */ + ctrl = (iob_len(iobuf) & DESC_CTRL_LEN) | + DESC_CTRL_IOC | DESC_CTRL_SOF | DESC_CTRL_EOF; + + if (cur == B44_RING_LAST) + ctrl |= DESC_CTRL_EOT; + + bp->tx[cur].ctrl = cpu_to_le32(ctrl); + bp->tx[cur].addr = cpu_to_le32(VIRT_TO_B44(iobuf->data)); + + /* Update next available descriptor index */ + cur = ring_next(cur); + bp->tx_cur = cur; + wmb(); + + /* Tell card that a new TX descriptor is ready */ + bw32(bp, B44_DMATX_PTR, cur * sizeof(struct dma_desc)); + return 0; +} + + +/** Recycles sent TX descriptors and notifies network stack + * + * @v bp Driver state + */ +static void b44_tx_complete(struct b44_private *bp) +{ + u32 cur, i; + + cur = pending_tx_index(bp); + + for (i = bp->tx_dirty; i != cur; i = ring_next(i)) { + /* Free finished frame */ + netdev_tx_complete(bp->netdev, bp->tx_iobuf[i]); + bp->tx_iobuf[i] = NULL; + + /* Clear TX descriptor */ + bp->tx[i].ctrl = 0; + bp->tx[i].addr = 0; + } + bp->tx_dirty = cur; +} + + +static void b44_process_rx_packets(struct b44_private *bp) +{ + struct io_buffer *iob; /* received data */ + struct rx_header *rh; + u32 pending, i; + u16 len; + + pending = pending_rx_index(bp); + + for (i = bp->rx_cur; i != pending; i = ring_next(i)) { + iob = bp->rx_iobuf[i]; + if (iob == NULL) + break; + + rh = iob->data; + len = le16_to_cpu(rh->len); + + /* + * Guard against incompletely written RX descriptors. + * Without this, things can get really slow! + */ + if (len == 0) + break; + + /* Discard CRC that is generated by the card */ + len -= 4; + + /* Check for invalid packets and errors */ + if (len > RX_PKT_BUF_SZ - RX_PKT_OFFSET || + (rh->flags & cpu_to_le16(RX_FLAG_ERRORS))) { + DBG("rx error len=%d flags=%04x\n", len, + cpu_to_le16(rh->flags)); + rh->len = 0; + rh->flags = 0; + netdev_rx_err(bp->netdev, iob, -EINVAL); + continue; + } + + /* Clear RX descriptor */ + rh->len = 0; + rh->flags = 0; + bp->rx_iobuf[i] = NULL; + + /* Hand off the IO buffer to the network stack */ + iob_reserve(iob, RX_PKT_OFFSET); + iob_put(iob, len); + netdev_rx(bp->netdev, iob); + } + bp->rx_cur = i; + b44_rx_refill(bp, pending_rx_index(bp)); +} + + +/** Poll for completed and received packets + * + * @v netdev Network device + */ +static void b44_poll(struct net_device *netdev) +{ + struct b44_private *bp = netdev_priv(netdev); + u32 istat; + + /* Interrupt status */ + istat = br32(bp, B44_ISTAT); + istat &= IMASK_DEF; /* only the events we care about */ + + if (!istat) + return; + if (istat & ISTAT_TX) + b44_tx_complete(bp); + if (istat & ISTAT_RX) + b44_process_rx_packets(bp); + if (istat & ISTAT_ERRORS) { + DBG("b44 error istat=0x%08x\n", istat); + + /* Reset B44 core partially to avoid long waits */ + b44_irq(bp->netdev, 0); + b44_halt(bp); + b44_init_tx_ring(bp); + b44_init_rx_ring(bp); + b44_init_hw(bp, B44_FULL_RESET_SKIP_PHY); + } + + /* Acknowledge interrupt */ + bw32(bp, B44_ISTAT, 0); + bflush(bp, B44_ISTAT, 1); +} + + +static struct net_device_operations b44_operations = { + .open = b44_open, + .close = b44_close, + .transmit = b44_transmit, + .poll = b44_poll, + .irq = b44_irq, +}; + + +static struct pci_device_id b44_nics[] = { + PCI_ROM(0x14e4, 0x4401, "BCM4401", "BCM4401"), + PCI_ROM(0x14e4, 0x170c, "BCM4401-B0", "BCM4401-B0"), + PCI_ROM(0x14e4, 0x4402, "BCM4401-B1", "BCM4401-B1"), +}; + + +struct pci_driver b44_driver __pci_driver = { + .ids = b44_nics, + .id_count = sizeof b44_nics / sizeof b44_nics[0], + .probe = b44_probe, + .remove = b44_remove, +}; diff --git a/gpxe/src/drivers/net/b44.h b/gpxe/src/drivers/net/b44.h new file mode 100644 index 0000000..fb36757 --- /dev/null +++ b/gpxe/src/drivers/net/b44.h @@ -0,0 +1,467 @@ +/* + * Copyright (c) 2008 Stefan Hajnoczi + * Copyright (c) 2008 Pantelis Koukousoulas + * + * 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. + * + * This driver is a port of the b44 linux driver version 1.01 + * + * Copyright (c) 2002 David S. Miller + * Copyright (c) Pekka Pietikainen + * Copyright (C) 2006 Broadcom Corporation. + * + * Some ssb bits copied from version 2.0 of the b44 driver + * Copyright (c) Michael Buesch + * + * Copyright (c) a lot of people too. Please respect their work. + */ +#ifndef _B44_H +#define _B44_H + +/* BCM44xx Register layout */ +#define B44_DEVCTRL 0x0000UL /* Device Control */ +#define DEVCTRL_MPM 0x00000040 /* MP PME Enable (B0 only) */ +#define DEVCTRL_PFE 0x00000080 /* Pattern Filtering Enable */ +#define DEVCTRL_IPP 0x00000400 /* Internal EPHY Present */ +#define DEVCTRL_EPR 0x00008000 /* EPHY Reset */ +#define DEVCTRL_PME 0x00001000 /* PHY Mode Enable */ +#define DEVCTRL_PMCE 0x00002000 /* PHY Mode Clocks Enable */ +#define DEVCTRL_PADDR 0x0007c000 /* PHY Address */ +#define DEVCTRL_PADDR_SHIFT 18 +#define B44_BIST_STAT 0x000CUL /* Built-In Self-Test Status */ +#define B44_WKUP_LEN 0x0010UL /* Wakeup Length */ +#define WKUP_LEN_P0_MASK 0x0000007f /* Pattern 0 */ +#define WKUP_LEN_D0 0x00000080 +#define WKUP_LEN_P1_MASK 0x00007f00 /* Pattern 1 */ +#define WKUP_LEN_P1_SHIFT 8 +#define WKUP_LEN_D1 0x00008000 +#define WKUP_LEN_P2_MASK 0x007f0000 /* Pattern 2 */ +#define WKUP_LEN_P2_SHIFT 16 +#define WKUP_LEN_D2 0x00000000 +#define WKUP_LEN_P3_MASK 0x7f000000 /* Pattern 3 */ +#define WKUP_LEN_P3_SHIFT 24 +#define WKUP_LEN_D3 0x80000000 +#define WKUP_LEN_DISABLE 0x80808080 +#define WKUP_LEN_ENABLE_TWO 0x80800000 +#define WKUP_LEN_ENABLE_THREE 0x80000000 +#define B44_ISTAT 0x0020UL /* Interrupt Status */ +#define ISTAT_LS 0x00000020 /* Link Change (B0 only) */ +#define ISTAT_PME 0x00000040 /* Power Management Event */ +#define ISTAT_TO 0x00000080 /* General Purpose Timeout */ +#define ISTAT_DSCE 0x00000400 /* Descriptor Error */ +#define ISTAT_DATAE 0x00000800 /* Data Error */ +#define ISTAT_DPE 0x00001000 /* Descr. Protocol Error */ +#define ISTAT_RDU 0x00002000 /* Receive Descr. Underflow */ +#define ISTAT_RFO 0x00004000 /* Receive FIFO Overflow */ +#define ISTAT_TFU 0x00008000 /* Transmit FIFO Underflow */ +#define ISTAT_RX 0x00010000 /* RX Interrupt */ +#define ISTAT_TX 0x01000000 /* TX Interrupt */ +#define ISTAT_EMAC 0x04000000 /* EMAC Interrupt */ +#define ISTAT_MII_WRITE 0x08000000 /* MII Write Interrupt */ +#define ISTAT_MII_READ 0x10000000 /* MII Read Interrupt */ +#define ISTAT_ERRORS (ISTAT_DSCE|ISTAT_DATAE|ISTAT_DPE|\ + ISTAT_RDU|ISTAT_RFO|ISTAT_TFU) +#define B44_IMASK 0x0024UL /* Interrupt Mask */ +#define IMASK_DEF (ISTAT_ERRORS | ISTAT_RX | ISTAT_TX) +#define IMASK_DISABLE 0 +#define B44_GPTIMER 0x0028UL /* General Purpose Timer */ +#define B44_ADDR_LO 0x0088UL /* ENET Address Lo (B0 only) */ +#define B44_ADDR_HI 0x008CUL /* ENET Address Hi (B0 only) */ +#define B44_FILT_ADDR 0x0090UL /* ENET Filter Address */ +#define B44_FILT_DATA 0x0094UL /* ENET Filter Data */ +#define B44_TXBURST 0x00A0UL /* TX Max Burst Length */ +#define B44_RXBURST 0x00A4UL /* RX Max Burst Length */ +#define B44_MAC_CTRL 0x00A8UL /* MAC Control */ +#define MAC_CTRL_CRC32_ENAB 0x00000001 /* CRC32 Generation Enable */ +#define MAC_CTRL_PHY_PDOWN 0x00000004 /* Onchip EPHY Powerdown */ +#define MAC_CTRL_PHY_EDET 0x00000008 /* Onchip EPHY Energy Detected*/ +#define MAC_CTRL_PHY_LEDCTRL 0x000000e0 /* Onchip EPHY LED Control */ +#define MAC_CTRL_PHY_LEDCTRL_SHIFT 5 +#define B44_MAC_FLOW 0x00ACUL /* MAC Flow Control */ +#define MAC_FLOW_RX_HI_WATER 0x000000ff /* Receive FIFO HI Water Mark */ +#define MAC_FLOW_PAUSE_ENAB 0x00008000 /* Enbl Pause Frm Generation */ +#define B44_RCV_LAZY 0x0100UL /* Lazy Interrupt Control */ +#define RCV_LAZY_TO_MASK 0x00ffffff /* Timeout */ +#define RCV_LAZY_FC_MASK 0xff000000 /* Frame Count */ +#define RCV_LAZY_FC_SHIFT 24 +#define B44_DMATX_CTRL 0x0200UL /* DMA TX Control */ +#define DMATX_CTRL_ENABLE 0x00000001 /* Enable */ +#define DMATX_CTRL_SUSPEND 0x00000002 /* Suepend Request */ +#define DMATX_CTRL_LPBACK 0x00000004 /* Loopback Enable */ +#define DMATX_CTRL_FAIRPRIOR 0x00000008 /* Fair Priority */ +#define DMATX_CTRL_FLUSH 0x00000010 /* Flush Request */ +#define B44_DMATX_ADDR 0x0204UL /* DMA TX Descriptor Ring Addr */ +#define B44_DMATX_PTR 0x0208UL /* DMA TX Last Posted Desc. */ +#define B44_DMATX_STAT 0x020CUL /* DMA TX Cur Actve Desc. + Sts */ +#define DMATX_STAT_CDMASK 0x00000fff /* Current Descriptor Mask */ +#define DMATX_STAT_SMASK 0x0000f000 /* State Mask */ +#define DMATX_STAT_SDISABLED 0x00000000 /* State Disabled */ +#define DMATX_STAT_SACTIVE 0x00001000 /* State Active */ +#define DMATX_STAT_SIDLE 0x00002000 /* State Idle Wait */ +#define DMATX_STAT_SSTOPPED 0x00003000 /* State Stopped */ +#define DMATX_STAT_SSUSP 0x00004000 /* State Suspend Pending */ +#define DMATX_STAT_EMASK 0x000f0000 /* Error Mask */ +#define DMATX_STAT_ENONE 0x00000000 /* Error None */ +#define DMATX_STAT_EDPE 0x00010000 /* Error Desc. Protocol Error */ +#define DMATX_STAT_EDFU 0x00020000 /* Error Data FIFO Underrun */ +#define DMATX_STAT_EBEBR 0x00030000 /* Bus Error on Buffer Read */ +#define DMATX_STAT_EBEDA 0x00040000 /* Bus Error on Desc. Access */ +#define DMATX_STAT_FLUSHED 0x00100000 /* Flushed */ +#define B44_DMARX_CTRL 0x0210UL /* DMA RX Control */ +#define DMARX_CTRL_ENABLE 0x00000001 /* Enable */ +#define DMARX_CTRL_ROMASK 0x000000fe /* Receive Offset Mask */ +#define DMARX_CTRL_ROSHIFT 1 /* Receive Offset Shift */ +#define B44_DMARX_ADDR 0x0214UL /* DMA RX Descriptor Ring Addr */ +#define B44_DMARX_PTR 0x0218UL /* DMA RX Last Posted Desc */ +#define B44_DMARX_STAT 0x021CUL /* Cur Active Desc. + Status */ +#define DMARX_STAT_CDMASK 0x00000fff /* Current Descriptor Mask */ +#define DMARX_STAT_SMASK 0x0000f000 /* State Mask */ +#define DMARX_STAT_SDISABLED 0x00000000 /* State Disbaled */ +#define DMARX_STAT_SACTIVE 0x00001000 /* State Active */ +#define DMARX_STAT_SIDLE 0x00002000 /* State Idle Wait */ +#define DMARX_STAT_SSTOPPED 0x00003000 /* State Stopped */ +#define DMARX_STAT_EMASK 0x000f0000 /* Error Mask */ +#define DMARX_STAT_ENONE 0x00000000 /* Error None */ +#define DMARX_STAT_EDPE 0x00010000 /* Error Desc. Protocol Error */ +#define DMARX_STAT_EDFO 0x00020000 /* Error Data FIFO Overflow */ +#define DMARX_STAT_EBEBW 0x00030000 /* Error on Buffer Write */ +#define DMARX_STAT_EBEDA 0x00040000 /* Bus Error on Desc. Access */ +#define B44_DMAFIFO_AD 0x0220UL /* DMA FIFO Diag Address */ +#define DMAFIFO_AD_OMASK 0x0000ffff /* Offset Mask */ +#define DMAFIFO_AD_SMASK 0x000f0000 /* Select Mask */ +#define DMAFIFO_AD_SXDD 0x00000000 /* Select Transmit DMA Data */ +#define DMAFIFO_AD_SXDP 0x00010000 /* Sel Transmit DMA Pointers */ +#define DMAFIFO_AD_SRDD 0x00040000 /* Select Receive DMA Data */ +#define DMAFIFO_AD_SRDP 0x00050000 /* Sel Receive DMA Pointers */ +#define DMAFIFO_AD_SXFD 0x00080000 /* Select Transmit FIFO Data */ +#define DMAFIFO_AD_SXFP 0x00090000 /* Sel Transmit FIFO Pointers */ +#define DMAFIFO_AD_SRFD 0x000c0000 /* Select Receive FIFO Data */ +#define DMAFIFO_AD_SRFP 0x000c0000 /* Sel Receive FIFO Pointers */ +#define B44_DMAFIFO_LO 0x0224UL /* DMA FIFO Diag Low Data */ +#define B44_DMAFIFO_HI 0x0228UL /* DMA FIFO Diag High Data */ +#define B44_RXCONFIG 0x0400UL /* EMAC RX Config */ +#define RXCONFIG_DBCAST 0x00000001 /* Disable Broadcast */ +#define RXCONFIG_ALLMULTI 0x00000002 /* Accept All Multicast */ +#define RXCONFIG_NORX_WHILE_TX 0x00000004 /* Rcv Disble While TX */ +#define RXCONFIG_PROMISC 0x00000008 /* Promiscuous Enable */ +#define RXCONFIG_LPBACK 0x00000010 /* Loopback Enable */ +#define RXCONFIG_FLOW 0x00000020 /* Flow Control Enable */ +#define RXCONFIG_FLOW_ACCEPT 0x00000040 /* Accept UFC Frame */ +#define RXCONFIG_RFILT 0x00000080 /* Reject Filter */ +#define B44_RXMAXLEN 0x0404UL /* EMAC RX Max Packet Length */ +#define B44_TXMAXLEN 0x0408UL /* EMAC TX Max Packet Length */ +#define B44_MDIO_CTRL 0x0410UL /* EMAC MDIO Control */ +#define MDIO_CTRL_MAXF_MASK 0x0000007f /* MDC Frequency */ +#define MDIO_CTRL_PREAMBLE 0x00000080 /* MII Preamble Enable */ +#define B44_MDIO_DATA 0x0414UL /* EMAC MDIO Data */ +#define MDIO_DATA_DATA 0x0000ffff /* R/W Data */ +#define MDIO_DATA_TA_MASK 0x00030000 /* Turnaround Value */ +#define MDIO_DATA_TA_SHIFT 16 +#define MDIO_TA_VALID 2 +#define MDIO_DATA_RA_MASK 0x007c0000 /* Register Address */ +#define MDIO_DATA_RA_SHIFT 18 +#define MDIO_DATA_PMD_MASK 0x0f800000 /* Physical Media Device */ +#define MDIO_DATA_PMD_SHIFT 23 +#define MDIO_DATA_OP_MASK 0x30000000 /* Opcode */ +#define MDIO_DATA_OP_SHIFT 28 +#define MDIO_OP_WRITE 1 +#define MDIO_OP_READ 2 +#define MDIO_DATA_SB_MASK 0xc0000000 /* Start Bits */ +#define MDIO_DATA_SB_SHIFT 30 +#define MDIO_DATA_SB_START 0x40000000 /* Start Of Frame */ +#define B44_EMAC_IMASK 0x0418UL /* EMAC Interrupt Mask */ +#define B44_EMAC_ISTAT 0x041CUL /* EMAC Interrupt Status */ +#define EMAC_INT_MII 0x00000001 /* MII MDIO Interrupt */ +#define EMAC_INT_MIB 0x00000002 /* MIB Interrupt */ +#define EMAC_INT_FLOW 0x00000003 /* Flow Control Interrupt */ +#define B44_CAM_DATA_LO 0x0420UL /* EMAC CAM Data Low */ +#define B44_CAM_DATA_HI 0x0424UL /* EMAC CAM Data High */ +#define CAM_DATA_HI_VALID 0x00010000 /* Valid Bit */ +#define B44_CAM_CTRL 0x0428UL /* EMAC CAM Control */ +#define CAM_CTRL_ENABLE 0x00000001 /* CAM Enable */ +#define CAM_CTRL_MSEL 0x00000002 /* Mask Select */ +#define CAM_CTRL_READ 0x00000004 /* Read */ +#define CAM_CTRL_WRITE 0x00000008 /* Read */ +#define CAM_CTRL_INDEX_MASK 0x003f0000 /* Index Mask */ +#define CAM_CTRL_INDEX_SHIFT 16 +#define CAM_CTRL_BUSY 0x80000000 /* CAM Busy */ +#define B44_ENET_CTRL 0x042CUL /* EMAC ENET Control */ +#define ENET_CTRL_ENABLE 0x00000001 /* EMAC Enable */ +#define ENET_CTRL_DISABLE 0x00000002 /* EMAC Disable */ +#define ENET_CTRL_SRST 0x00000004 /* EMAC Soft Reset */ +#define ENET_CTRL_EPSEL 0x00000008 /* External PHY Select */ +#define B44_TX_CTRL 0x0430UL /* EMAC TX Control */ +#define TX_CTRL_DUPLEX 0x00000001 /* Full Duplex */ +#define TX_CTRL_FMODE 0x00000002 /* Flow Mode */ +#define TX_CTRL_SBENAB 0x00000004 /* Single Backoff Enable */ +#define TX_CTRL_SMALL_SLOT 0x00000008 /* Small Slottime */ +#define B44_TX_HIWMARK 0x0434UL /* EMAC TX High Watermark */ +#define TX_HIWMARK_DEFLT 56 /* Default used in all drivers */ +#define B44_MIB_CTRL 0x0438UL /* EMAC MIB Control */ +#define MIB_CTRL_CLR_ON_READ 0x00000001 /* Autoclear on Read */ +#define B44_TX_GOOD_O 0x0500UL /* MIB TX Good Octets */ +#define B44_TX_GOOD_P 0x0504UL /* MIB TX Good Packets */ +#define B44_TX_O 0x0508UL /* MIB TX Octets */ +#define B44_TX_P 0x050CUL /* MIB TX Packets */ +#define B44_TX_BCAST 0x0510UL /* MIB TX Broadcast Packets */ +#define B44_TX_MCAST 0x0514UL /* MIB TX Multicast Packets */ +#define B44_TX_64 0x0518UL /* MIB TX <= 64 byte Packets */ +#define B44_TX_65_127 0x051CUL /* MIB TX 65 to 127 byte Pkts */ +#define B44_TX_128_255 0x0520UL /* MIB TX 128 to 255 byte Pkts */ +#define B44_TX_256_511 0x0524UL /* MIB TX 256 to 511 byte Pkts */ +#define B44_TX_512_1023 0x0528UL /* MIB TX 512 to 1023 byte Pkts */ +#define B44_TX_1024_MAX 0x052CUL /* MIB TX 1024 to max byte Pkts */ +#define B44_TX_JABBER 0x0530UL /* MIB TX Jabber Packets */ +#define B44_TX_OSIZE 0x0534UL /* MIB TX Oversize Packets */ +#define B44_TX_FRAG 0x0538UL /* MIB TX Fragment Packets */ +#define B44_TX_URUNS 0x053CUL /* MIB TX Underruns */ +#define B44_TX_TCOLS 0x0540UL /* MIB TX Total Collisions */ +#define B44_TX_SCOLS 0x0544UL /* MIB TX Single Collisions */ +#define B44_TX_MCOLS 0x0548UL /* MIB TX Multiple Collisions */ +#define B44_TX_ECOLS 0x054CUL /* MIB TX Excessive Collisions */ +#define B44_TX_LCOLS 0x0550UL /* MIB TX Late Collisions */ +#define B44_TX_DEFERED 0x0554UL /* MIB TX Defered Packets */ +#define B44_TX_CLOST 0x0558UL /* MIB TX Carrier Lost */ +#define B44_TX_PAUSE 0x055CUL /* MIB TX Pause Packets */ +#define B44_RX_GOOD_O 0x0580UL /* MIB RX Good Octets */ +#define B44_RX_GOOD_P 0x0584UL /* MIB RX Good Packets */ +#define B44_RX_O 0x0588UL /* MIB RX Octets */ +#define B44_RX_P 0x058CUL /* MIB RX Packets */ +#define B44_RX_BCAST 0x0590UL /* MIB RX Broadcast Packets */ +#define B44_RX_MCAST 0x0594UL /* MIB RX Multicast Packets */ +#define B44_RX_64 0x0598UL /* MIB RX <= 64 byte Packets */ +#define B44_RX_65_127 0x059CUL /* MIB RX 65 to 127 byte Pkts */ +#define B44_RX_128_255 0x05A0UL /* MIB RX 128 to 255 byte Pkts */ +#define B44_RX_256_511 0x05A4UL /* MIB RX 256 to 511 byte Pkts */ +#define B44_RX_512_1023 0x05A8UL /* MIB RX 512 to 1023 byte Pkts */ +#define B44_RX_1024_MAX 0x05ACUL /* MIB RX 1024 to max byte Pkts */ +#define B44_RX_JABBER 0x05B0UL /* MIB RX Jabber Packets */ +#define B44_RX_OSIZE 0x05B4UL /* MIB RX Oversize Packets */ +#define B44_RX_FRAG 0x05B8UL /* MIB RX Fragment Packets */ +#define B44_RX_MISS 0x05BCUL /* MIB RX Missed Packets */ +#define B44_RX_CRCA 0x05C0UL /* MIB RX CRC Align Errors */ +#define B44_RX_USIZE 0x05C4UL /* MIB RX Undersize Packets */ +#define B44_RX_CRC 0x05C8UL /* MIB RX CRC Errors */ +#define B44_RX_ALIGN 0x05CCUL /* MIB RX Align Errors */ +#define B44_RX_SYM 0x05D0UL /* MIB RX Symbol Errors */ +#define B44_RX_PAUSE 0x05D4UL /* MIB RX Pause Packets */ +#define B44_RX_NPAUSE 0x05D8UL /* MIB RX Non-Pause Packets */ + +/* Sonics Silicon backplane register definitions */ +#define B44_SBIMSTATE 0x0F90UL /* SB Initiator Agent State */ +#define SBIMSTATE_PC 0x0000000f /* Pipe Count */ +#define SBIMSTATE_AP_MASK 0x00000030 /* Arbitration Priority */ +#define SBIMSTATE_AP_BOTH 0x00000000 /* both timeslices and token */ +#define SBIMSTATE_AP_TS 0x00000010 /* Use timeslices only */ +#define SBIMSTATE_AP_TK 0x00000020 /* Use token only */ +#define SBIMSTATE_AP_RSV 0x00000030 /* Reserved */ +#define SBIMSTATE_IBE 0x00020000 /* In Band Error */ +#define SBIMSTATE_TO 0x00040000 /* Timeout */ +#define SBIMSTATE_BAD ( SBIMSTATE_IBE | SBIMSTATE_TO ) +#define B44_SBINTVEC 0x0F94UL /* SB Interrupt Mask */ +#define SBINTVEC_PCI 0x00000001 /* Enable interrupts for PCI */ +#define SBINTVEC_ENET0 0x00000002 /* Enable ints for enet 0 */ +#define SBINTVEC_ILINE20 0x00000004 /* Enable ints for iline20 */ +#define SBINTVEC_CODEC 0x00000008 /* Enable ints for v90 codec */ +#define SBINTVEC_USB 0x00000010 /* Enable intts for usb */ +#define SBINTVEC_EXTIF 0x00000020 /* Enable ints for ext i/f */ +#define SBINTVEC_ENET1 0x00000040 /* Enable ints for enet 1 */ +#define B44_SBTMSLOW 0x0F98UL /* SB Target State Low */ +#define SBTMSLOW_RESET 0x00000001 /* Reset */ +#define SBTMSLOW_REJECT 0x00000002 /* Reject */ +#define SBTMSLOW_CLOCK 0x00010000 /* Clock Enable */ +#define SBTMSLOW_FGC 0x00020000 /* Force Gated Clocks On */ +#define SBTMSLOW_PE 0x40000000 /* Power Management Enable */ +#define SBTMSLOW_BE 0x80000000 /* BIST Enable */ +#define B44_SBTMSHIGH 0x0F9CUL /* SB Target State High */ +#define SBTMSHIGH_SERR 0x00000001 /* S-error */ +#define SBTMSHIGH_INT 0x00000002 /* Interrupt */ +#define SBTMSHIGH_BUSY 0x00000004 /* Busy */ +#define SBTMSHIGH_GCR 0x20000000 /* Gated Clock Request */ +#define SBTMSHIGH_BISTF 0x40000000 /* BIST Failed */ +#define SBTMSHIGH_BISTD 0x80000000 /* BIST Done */ +#define B44_SBIDHIGH 0x0FFCUL /* SB Identification High */ +#define SBIDHIGH_RC_MASK 0x0000000f /* Revision Code */ +#define SBIDHIGH_CC_MASK 0x0000fff0 /* Core Code */ +#define SBIDHIGH_CC_SHIFT 4 +#define SBIDHIGH_VC_MASK 0xffff0000 /* Vendor Code */ +#define SBIDHIGH_VC_SHIFT 16 + +/* SSB PCI config space registers. */ +#define SSB_PMCSR 0x44 +#define SSB_PE 0x100 +#define SSB_BAR0_WIN 0x80 +#define SSB_BAR1_WIN 0x84 +#define SSB_SPROM_CONTROL 0x88 +#define SSB_BAR1_CONTROL 0x8c + +/* SSB core and host control registers. */ +#define SSB_CONTROL 0x0000UL +#define SSB_ARBCONTROL 0x0010UL +#define SSB_ISTAT 0x0020UL +#define SSB_IMASK 0x0024UL +#define SSB_MBOX 0x0028UL +#define SSB_BCAST_ADDR 0x0050UL +#define SSB_BCAST_DATA 0x0054UL +#define SSB_PCI_TRANS_0 0x0100UL +#define SSB_PCI_TRANS_1 0x0104UL +#define SSB_PCI_TRANS_2 0x0108UL +#define SSB_SPROM 0x0800UL + +#define SSB_PCI_MEM 0x00000000 +#define SSB_PCI_IO 0x00000001 +#define SSB_PCI_CFG0 0x00000002 +#define SSB_PCI_CFG1 0x00000003 +#define SSB_PCI_PREF 0x00000004 +#define SSB_PCI_BURST 0x00000008 +#define SSB_PCI_MASK0 0xfc000000 +#define SSB_PCI_MASK1 0xfc000000 +#define SSB_PCI_MASK2 0xc0000000 + +/* 4400 PHY registers */ +#define B44_MII_AUXCTRL 24 /* Auxiliary Control */ +#define MII_AUXCTRL_DUPLEX 0x0001 /* Full Duplex */ +#define MII_AUXCTRL_SPEED 0x0002 /* 1=100Mbps, 0=10Mbps */ +#define MII_AUXCTRL_FORCED 0x0004 /* Forced 10/100 */ +#define B44_MII_ALEDCTRL 26 /* Activity LED */ +#define MII_ALEDCTRL_ALLMSK 0x7fff +#define B44_MII_TLEDCTRL 27 /* Traffic Meter LED */ +#define MII_TLEDCTRL_ENABLE 0x0040 + +/* RX/TX descriptor */ +struct dma_desc { + u32 ctrl; /* length of data and flags */ + u32 addr; /* address of data */ +}; + +/* There are only 12 bits in the DMA engine for descriptor offsetting + * so the table must be aligned on a boundary of this. + */ +#define B44_DMA_ALIGNMENT 4096 + +/* The DMA engine can only address the first gigabyte of address space + */ +#define B44_30BIT_DMA_MASK 0x3fffffff + +#define DESC_CTRL_LEN 0x00001fff +#define DESC_CTRL_CMASK 0x0ff00000 /* Core specific bits */ +#define DESC_CTRL_EOT 0x10000000 /* End of Table */ +#define DESC_CTRL_IOC 0x20000000 /* Interrupt On Completion */ +#define DESC_CTRL_EOF 0x40000000 /* End of Frame */ +#define DESC_CTRL_SOF 0x80000000 /* Start of Frame */ + +struct rx_header { + u16 len; + u16 flags; + u16 pad[12]; +}; +#define RX_HEADER_LEN 28 + +#define RX_FLAG_OFIFO 0x00000001 /* FIFO Overflow */ +#define RX_FLAG_CRCERR 0x00000002 /* CRC Error */ +#define RX_FLAG_SERR 0x00000004 /* Receive Symbol Error */ +#define RX_FLAG_ODD 0x00000008 /* Frame has odd number of nibbles */ +#define RX_FLAG_LARGE 0x00000010 /* Frame is > RX MAX Length */ +#define RX_FLAG_MCAST 0x00000020 /* Dest is Multicast Address */ +#define RX_FLAG_BCAST 0x00000040 /* Dest is Broadcast Address */ +#define RX_FLAG_MISS 0x00000080 /* Received due to promisc mode */ +#define RX_FLAG_LAST 0x00000800 /* Last buffer in frame */ +#define RX_FLAG_ERRORS (RX_FLAG_ODD | RX_FLAG_SERR |\ + RX_FLAG_CRCERR | RX_FLAG_OFIFO) + +/* Client Mode PCI memory access space (1 GB) */ +#define SB_PCI_DMA 0x40000000 + + /* Address of PCI core on BCM4400 cards */ +#define BCM4400_PCI_CORE_ADDR 0x18002000 + +/* Hardware minimum and maximum for a single frame's data payload */ +#define B44_MIN_MTU 60 +#define B44_MAX_MTU 1500 + +#define B44_RING_SIZE 8 +#define B44_RING_LAST ( B44_RING_SIZE - 1 ) + +#define B44_RX_RING_LEN_BYTES ( sizeof bp->rx[0] * B44_RING_SIZE ) +#define B44_TX_RING_LEN_BYTES ( sizeof bp->tx[0] * B44_RING_SIZE ) + +#define RX_PKT_OFFSET 30 +#define RX_PKT_BUF_SZ (1536 + RX_PKT_OFFSET + 64) + +#define B44_FULL_RESET 1 +#define B44_FULL_RESET_SKIP_PHY 2 +#define B44_PARTIAL_RESET 3 +#define B44_CHIP_RESET_FULL 4 +#define B44_CHIP_RESET_PARTIAL 5 + +#define SSB_CORE_DOWN ( SBTMSLOW_RESET | SBTMSLOW_REJECT ) + +#define B44_REGS_SIZE 8192 + +/** Driver private state */ +struct b44_private { + struct net_device *netdev; + struct pci_device *pci; + u8 *regs; /* memory-mapped registers */ + u8 phy_addr; + + struct dma_desc *tx; + struct io_buffer *tx_iobuf[B44_RING_SIZE]; + u32 tx_cur; /* next available descriptor */ + u32 tx_dirty; /* oldest pending descriptor */ + + struct dma_desc *rx; + struct io_buffer *rx_iobuf[B44_RING_SIZE]; + u32 rx_cur; /* next descriptor to read */ +}; + + +static void ssb_core_reset ( struct b44_private *bp ); +static void ssb_core_disable ( struct b44_private *bp ); +static u32 ssb_pci_setup ( struct b44_private *bp, u32 cores ); + +static void b44_chip_reset ( struct b44_private *bp, int reset_kind ); +static void b44_init_hw ( struct b44_private *bp, int reset_kind ); +static void b44_cam_write ( struct b44_private *bp, u8 *data, int index ); +static void b44_set_mac_addr ( struct b44_private *bp ); +static void b44_set_rx_mode ( struct net_device *netdev ); +static void b44_halt(struct b44_private *); + +static int b44_phy_reset ( struct b44_private *bp ); +static int b44_phy_write ( struct b44_private *bp, int reg, u32 val ); +static int b44_phy_read ( struct b44_private *bp, int reg, u32 *val ); + +static int b44_init_tx_ring ( struct b44_private *bp ); +static void b44_free_tx_ring ( struct b44_private *bp ); +static int b44_init_rx_ring ( struct b44_private *bp ); +static void b44_free_rx_ring ( struct b44_private *bp ); +static void b44_rx_refill ( struct b44_private *bp, u32 pending ); +static void b44_populate_rx_descriptor (struct b44_private *bp, u32 index); + +static int b44_probe ( struct pci_device *pci, + const struct pci_device_id *id ); +static void b44_remove ( struct pci_device *pci ); + +static int b44_open ( struct net_device *netdev ); +static void b44_close ( struct net_device *netdev ); +static void b44_irq ( struct net_device *netdev, int enable ); +static void b44_poll ( struct net_device *netdev ); +static void b44_process_rx_packets ( struct b44_private *bp ); +static int b44_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ); + +static struct net_device_operations b44_operations; + +#endif /* _B44_H */ diff --git a/gpxe/src/drivers/net/bnx2.c b/gpxe/src/drivers/net/bnx2.c new file mode 100644 index 0000000..9c989c4 --- /dev/null +++ b/gpxe/src/drivers/net/bnx2.c @@ -0,0 +1,2695 @@ +/* bnx2.c: Broadcom NX2 network driver. + * + * Copyright (c) 2004, 2005, 2006 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + * + * Written by: Michael Chan (mchan@broadcom.com) + * + * Etherboot port by Ryan Jackson (rjackson@lnxi.com), based on driver + * version 1.4.40 from linux 2.6.17 + */ + + +#include "etherboot.h" +#include "nic.h" +#include +#include +#include +#include "string.h" +#include "bnx2.h" +#include "bnx2_fw.h" + +#if 0 +/* Dummy defines for error handling */ +#define EBUSY 1 +#define ENODEV 2 +#define EINVAL 3 +#define ENOMEM 4 +#define EIO 5 +#endif + +/* The bnx2 seems to be picky about the alignment of the receive buffers + * and possibly the status block. + */ +static struct bss { + struct tx_bd tx_desc_ring[TX_DESC_CNT]; + struct rx_bd rx_desc_ring[RX_DESC_CNT]; + unsigned char rx_buf[RX_BUF_CNT][RX_BUF_SIZE]; + struct status_block status_blk; + struct statistics_block stats_blk; +} bnx2_bss; + +static struct bnx2 bnx2; + +static struct flash_spec flash_table[] = +{ + /* Slow EEPROM */ + {0x00000000, 0x40830380, 0x009f0081, 0xa184a053, 0xaf000400, + 1, SEEPROM_PAGE_BITS, SEEPROM_PAGE_SIZE, + SEEPROM_BYTE_ADDR_MASK, SEEPROM_TOTAL_SIZE, + "EEPROM - slow"}, + /* Expansion entry 0001 */ + {0x08000002, 0x4b808201, 0x00050081, 0x03840253, 0xaf020406, + 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, + SAIFUN_FLASH_BYTE_ADDR_MASK, 0, + "Entry 0001"}, + /* Saifun SA25F010 (non-buffered flash) */ + /* strap, cfg1, & write1 need updates */ + {0x04000001, 0x47808201, 0x00050081, 0x03840253, 0xaf020406, + 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, + SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE*2, + "Non-buffered flash (128kB)"}, + /* Saifun SA25F020 (non-buffered flash) */ + /* strap, cfg1, & write1 need updates */ + {0x0c000003, 0x4f808201, 0x00050081, 0x03840253, 0xaf020406, + 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, + SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE*4, + "Non-buffered flash (256kB)"}, + /* Expansion entry 0100 */ + {0x11000000, 0x53808201, 0x00050081, 0x03840253, 0xaf020406, + 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, + SAIFUN_FLASH_BYTE_ADDR_MASK, 0, + "Entry 0100"}, + /* Entry 0101: ST M45PE10 (non-buffered flash, TetonII B0) */ + {0x19000002, 0x5b808201, 0x000500db, 0x03840253, 0xaf020406, + 0, ST_MICRO_FLASH_PAGE_BITS, ST_MICRO_FLASH_PAGE_SIZE, + ST_MICRO_FLASH_BYTE_ADDR_MASK, ST_MICRO_FLASH_BASE_TOTAL_SIZE*2, + "Entry 0101: ST M45PE10 (128kB non-bufferred)"}, + /* Entry 0110: ST M45PE20 (non-buffered flash)*/ + {0x15000001, 0x57808201, 0x000500db, 0x03840253, 0xaf020406, + 0, ST_MICRO_FLASH_PAGE_BITS, ST_MICRO_FLASH_PAGE_SIZE, + ST_MICRO_FLASH_BYTE_ADDR_MASK, ST_MICRO_FLASH_BASE_TOTAL_SIZE*4, + "Entry 0110: ST M45PE20 (256kB non-bufferred)"}, + /* Saifun SA25F005 (non-buffered flash) */ + /* strap, cfg1, & write1 need updates */ + {0x1d000003, 0x5f808201, 0x00050081, 0x03840253, 0xaf020406, + 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, + SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE, + "Non-buffered flash (64kB)"}, + /* Fast EEPROM */ + {0x22000000, 0x62808380, 0x009f0081, 0xa184a053, 0xaf000400, + 1, SEEPROM_PAGE_BITS, SEEPROM_PAGE_SIZE, + SEEPROM_BYTE_ADDR_MASK, SEEPROM_TOTAL_SIZE, + "EEPROM - fast"}, + /* Expansion entry 1001 */ + {0x2a000002, 0x6b808201, 0x00050081, 0x03840253, 0xaf020406, + 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, + SAIFUN_FLASH_BYTE_ADDR_MASK, 0, + "Entry 1001"}, + /* Expansion entry 1010 */ + {0x26000001, 0x67808201, 0x00050081, 0x03840253, 0xaf020406, + 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, + SAIFUN_FLASH_BYTE_ADDR_MASK, 0, + "Entry 1010"}, + /* ATMEL AT45DB011B (buffered flash) */ + {0x2e000003, 0x6e808273, 0x00570081, 0x68848353, 0xaf000400, + 1, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE, + BUFFERED_FLASH_BYTE_ADDR_MASK, BUFFERED_FLASH_TOTAL_SIZE, + "Buffered flash (128kB)"}, + /* Expansion entry 1100 */ + {0x33000000, 0x73808201, 0x00050081, 0x03840253, 0xaf020406, + 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, + SAIFUN_FLASH_BYTE_ADDR_MASK, 0, + "Entry 1100"}, + /* Expansion entry 1101 */ + {0x3b000002, 0x7b808201, 0x00050081, 0x03840253, 0xaf020406, + 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, + SAIFUN_FLASH_BYTE_ADDR_MASK, 0, + "Entry 1101"}, + /* Ateml Expansion entry 1110 */ + {0x37000001, 0x76808273, 0x00570081, 0x68848353, 0xaf000400, + 1, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE, + BUFFERED_FLASH_BYTE_ADDR_MASK, 0, + "Entry 1110 (Atmel)"}, + /* ATMEL AT45DB021B (buffered flash) */ + {0x3f000003, 0x7e808273, 0x00570081, 0x68848353, 0xaf000400, + 1, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE, + BUFFERED_FLASH_BYTE_ADDR_MASK, BUFFERED_FLASH_TOTAL_SIZE*2, + "Buffered flash (256kB)"}, +}; + +static u32 +bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset) +{ + REG_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, offset); + return (REG_RD(bp, BNX2_PCICFG_REG_WINDOW)); +} + +static void +bnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val) +{ + REG_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, offset); + REG_WR(bp, BNX2_PCICFG_REG_WINDOW, val); +} + +static void +bnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val) +{ + offset += cid_addr; + REG_WR(bp, BNX2_CTX_DATA_ADR, offset); + REG_WR(bp, BNX2_CTX_DATA, val); +} + +static int +bnx2_read_phy(struct bnx2 *bp, u32 reg, u32 *val) +{ + u32 val1; + int i, ret; + + if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) { + val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE); + val1 &= ~BNX2_EMAC_MDIO_MODE_AUTO_POLL; + + REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1); + REG_RD(bp, BNX2_EMAC_MDIO_MODE); + + udelay(40); + } + + val1 = (bp->phy_addr << 21) | (reg << 16) | + BNX2_EMAC_MDIO_COMM_COMMAND_READ | BNX2_EMAC_MDIO_COMM_DISEXT | + BNX2_EMAC_MDIO_COMM_START_BUSY; + REG_WR(bp, BNX2_EMAC_MDIO_COMM, val1); + + for (i = 0; i < 50; i++) { + udelay(10); + + val1 = REG_RD(bp, BNX2_EMAC_MDIO_COMM); + if (!(val1 & BNX2_EMAC_MDIO_COMM_START_BUSY)) { + udelay(5); + + val1 = REG_RD(bp, BNX2_EMAC_MDIO_COMM); + val1 &= BNX2_EMAC_MDIO_COMM_DATA; + + break; + } + } + + if (val1 & BNX2_EMAC_MDIO_COMM_START_BUSY) { + *val = 0x0; + ret = -EBUSY; + } + else { + *val = val1; + ret = 0; + } + + if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) { + val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE); + val1 |= BNX2_EMAC_MDIO_MODE_AUTO_POLL; + + REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1); + REG_RD(bp, BNX2_EMAC_MDIO_MODE); + + udelay(40); + } + + return ret; +} + +static int +bnx2_write_phy(struct bnx2 *bp, u32 reg, u32 val) +{ + u32 val1; + int i, ret; + + if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) { + val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE); + val1 &= ~BNX2_EMAC_MDIO_MODE_AUTO_POLL; + + REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1); + REG_RD(bp, BNX2_EMAC_MDIO_MODE); + + udelay(40); + } + + val1 = (bp->phy_addr << 21) | (reg << 16) | val | + BNX2_EMAC_MDIO_COMM_COMMAND_WRITE | + BNX2_EMAC_MDIO_COMM_START_BUSY | BNX2_EMAC_MDIO_COMM_DISEXT; + REG_WR(bp, BNX2_EMAC_MDIO_COMM, val1); + + for (i = 0; i < 50; i++) { + udelay(10); + + val1 = REG_RD(bp, BNX2_EMAC_MDIO_COMM); + if (!(val1 & BNX2_EMAC_MDIO_COMM_START_BUSY)) { + udelay(5); + break; + } + } + + if (val1 & BNX2_EMAC_MDIO_COMM_START_BUSY) + ret = -EBUSY; + else + ret = 0; + + if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) { + val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE); + val1 |= BNX2_EMAC_MDIO_MODE_AUTO_POLL; + + REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1); + REG_RD(bp, BNX2_EMAC_MDIO_MODE); + + udelay(40); + } + + return ret; +} + +static void +bnx2_disable_int(struct bnx2 *bp) +{ + REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, + BNX2_PCICFG_INT_ACK_CMD_MASK_INT); + REG_RD(bp, BNX2_PCICFG_INT_ACK_CMD); + +} + +static int +bnx2_alloc_mem(struct bnx2 *bp) +{ + bp->tx_desc_ring = bnx2_bss.tx_desc_ring; + bp->tx_desc_mapping = virt_to_bus(bp->tx_desc_ring); + + bp->rx_desc_ring = bnx2_bss.rx_desc_ring; + memset(bp->rx_desc_ring, 0, sizeof(struct rx_bd) * RX_DESC_CNT); + bp->rx_desc_mapping = virt_to_bus(bp->rx_desc_ring); + + memset(&bnx2_bss.status_blk, 0, sizeof(struct status_block)); + bp->status_blk = &bnx2_bss.status_blk; + bp->status_blk_mapping = virt_to_bus(&bnx2_bss.status_blk); + + bp->stats_blk = &bnx2_bss.stats_blk; + memset(&bnx2_bss.stats_blk, 0, sizeof(struct statistics_block)); + bp->stats_blk_mapping = virt_to_bus(&bnx2_bss.stats_blk); + + return 0; +} + +static void +bnx2_report_fw_link(struct bnx2 *bp) +{ + u32 fw_link_status = 0; + + if (bp->link_up) { + u32 bmsr; + + switch (bp->line_speed) { + case SPEED_10: + if (bp->duplex == DUPLEX_HALF) + fw_link_status = BNX2_LINK_STATUS_10HALF; + else + fw_link_status = BNX2_LINK_STATUS_10FULL; + break; + case SPEED_100: + if (bp->duplex == DUPLEX_HALF) + fw_link_status = BNX2_LINK_STATUS_100HALF; + else + fw_link_status = BNX2_LINK_STATUS_100FULL; + break; + case SPEED_1000: + if (bp->duplex == DUPLEX_HALF) + fw_link_status = BNX2_LINK_STATUS_1000HALF; + else + fw_link_status = BNX2_LINK_STATUS_1000FULL; + break; + case SPEED_2500: + if (bp->duplex == DUPLEX_HALF) + fw_link_status = BNX2_LINK_STATUS_2500HALF; + else + fw_link_status = BNX2_LINK_STATUS_2500FULL; + break; + } + + fw_link_status |= BNX2_LINK_STATUS_LINK_UP; + + if (bp->autoneg) { + fw_link_status |= BNX2_LINK_STATUS_AN_ENABLED; + + bnx2_read_phy(bp, MII_BMSR, &bmsr); + bnx2_read_phy(bp, MII_BMSR, &bmsr); + + if (!(bmsr & BMSR_ANEGCOMPLETE) || + bp->phy_flags & PHY_PARALLEL_DETECT_FLAG) + fw_link_status |= BNX2_LINK_STATUS_PARALLEL_DET; + else + fw_link_status |= BNX2_LINK_STATUS_AN_COMPLETE; + } + } + else + fw_link_status = BNX2_LINK_STATUS_LINK_DOWN; + + REG_WR_IND(bp, bp->shmem_base + BNX2_LINK_STATUS, fw_link_status); +} + +static void +bnx2_report_link(struct bnx2 *bp) +{ + if (bp->link_up) { + printf("NIC Link is Up, "); + + printf("%d Mbps ", bp->line_speed); + + if (bp->duplex == DUPLEX_FULL) + printf("full duplex"); + else + printf("half duplex"); + + if (bp->flow_ctrl) { + if (bp->flow_ctrl & FLOW_CTRL_RX) { + printf(", receive "); + if (bp->flow_ctrl & FLOW_CTRL_TX) + printf("& transmit "); + } + else { + printf(", transmit "); + } + printf("flow control ON"); + } + printf("\n"); + } + else { + printf("NIC Link is Down\n"); + } + + bnx2_report_fw_link(bp); +} + +static void +bnx2_resolve_flow_ctrl(struct bnx2 *bp) +{ + u32 local_adv, remote_adv; + + bp->flow_ctrl = 0; + if ((bp->autoneg & (AUTONEG_SPEED | AUTONEG_FLOW_CTRL)) != + (AUTONEG_SPEED | AUTONEG_FLOW_CTRL)) { + + if (bp->duplex == DUPLEX_FULL) { + bp->flow_ctrl = bp->req_flow_ctrl; + } + return; + } + + if (bp->duplex != DUPLEX_FULL) { + return; + } + + if ((bp->phy_flags & PHY_SERDES_FLAG) && + (CHIP_NUM(bp) == CHIP_NUM_5708)) { + u32 val; + + bnx2_read_phy(bp, BCM5708S_1000X_STAT1, &val); + if (val & BCM5708S_1000X_STAT1_TX_PAUSE) + bp->flow_ctrl |= FLOW_CTRL_TX; + if (val & BCM5708S_1000X_STAT1_RX_PAUSE) + bp->flow_ctrl |= FLOW_CTRL_RX; + return; + } + + bnx2_read_phy(bp, MII_ADVERTISE, &local_adv); + bnx2_read_phy(bp, MII_LPA, &remote_adv); + + if (bp->phy_flags & PHY_SERDES_FLAG) { + u32 new_local_adv = 0; + u32 new_remote_adv = 0; + + if (local_adv & ADVERTISE_1000XPAUSE) + new_local_adv |= ADVERTISE_PAUSE_CAP; + if (local_adv & ADVERTISE_1000XPSE_ASYM) + new_local_adv |= ADVERTISE_PAUSE_ASYM; + if (remote_adv & ADVERTISE_1000XPAUSE) + new_remote_adv |= ADVERTISE_PAUSE_CAP; + if (remote_adv & ADVERTISE_1000XPSE_ASYM) + new_remote_adv |= ADVERTISE_PAUSE_ASYM; + + local_adv = new_local_adv; + remote_adv = new_remote_adv; + } + + /* See Table 28B-3 of 802.3ab-1999 spec. */ + if (local_adv & ADVERTISE_PAUSE_CAP) { + if(local_adv & ADVERTISE_PAUSE_ASYM) { + if (remote_adv & ADVERTISE_PAUSE_CAP) { + bp->flow_ctrl = FLOW_CTRL_TX | FLOW_CTRL_RX; + } + else if (remote_adv & ADVERTISE_PAUSE_ASYM) { + bp->flow_ctrl = FLOW_CTRL_RX; + } + } + else { + if (remote_adv & ADVERTISE_PAUSE_CAP) { + bp->flow_ctrl = FLOW_CTRL_TX | FLOW_CTRL_RX; + } + } + } + else if (local_adv & ADVERTISE_PAUSE_ASYM) { + if ((remote_adv & ADVERTISE_PAUSE_CAP) && + (remote_adv & ADVERTISE_PAUSE_ASYM)) { + + bp->flow_ctrl = FLOW_CTRL_TX; + } + } +} + +static int +bnx2_5708s_linkup(struct bnx2 *bp) +{ + u32 val; + + bp->link_up = 1; + bnx2_read_phy(bp, BCM5708S_1000X_STAT1, &val); + switch (val & BCM5708S_1000X_STAT1_SPEED_MASK) { + case BCM5708S_1000X_STAT1_SPEED_10: + bp->line_speed = SPEED_10; + break; + case BCM5708S_1000X_STAT1_SPEED_100: + bp->line_speed = SPEED_100; + break; + case BCM5708S_1000X_STAT1_SPEED_1G: + bp->line_speed = SPEED_1000; + break; + case BCM5708S_1000X_STAT1_SPEED_2G5: + bp->line_speed = SPEED_2500; + break; + } + if (val & BCM5708S_1000X_STAT1_FD) + bp->duplex = DUPLEX_FULL; + else + bp->duplex = DUPLEX_HALF; + + return 0; +} + +static int +bnx2_5706s_linkup(struct bnx2 *bp) +{ + u32 bmcr, local_adv, remote_adv, common; + + bp->link_up = 1; + bp->line_speed = SPEED_1000; + + bnx2_read_phy(bp, MII_BMCR, &bmcr); + if (bmcr & BMCR_FULLDPLX) { + bp->duplex = DUPLEX_FULL; + } + else { + bp->duplex = DUPLEX_HALF; + } + + if (!(bmcr & BMCR_ANENABLE)) { + return 0; + } + + bnx2_read_phy(bp, MII_ADVERTISE, &local_adv); + bnx2_read_phy(bp, MII_LPA, &remote_adv); + + common = local_adv & remote_adv; + if (common & (ADVERTISE_1000XHALF | ADVERTISE_1000XFULL)) { + + if (common & ADVERTISE_1000XFULL) { + bp->duplex = DUPLEX_FULL; + } + else { + bp->duplex = DUPLEX_HALF; + } + } + + return 0; +} + +static int +bnx2_copper_linkup(struct bnx2 *bp) +{ + u32 bmcr; + + bnx2_read_phy(bp, MII_BMCR, &bmcr); + if (bmcr & BMCR_ANENABLE) { + u32 local_adv, remote_adv, common; + + bnx2_read_phy(bp, MII_CTRL1000, &local_adv); + bnx2_read_phy(bp, MII_STAT1000, &remote_adv); + + common = local_adv & (remote_adv >> 2); + if (common & ADVERTISE_1000FULL) { + bp->line_speed = SPEED_1000; + bp->duplex = DUPLEX_FULL; + } + else if (common & ADVERTISE_1000HALF) { + bp->line_speed = SPEED_1000; + bp->duplex = DUPLEX_HALF; + } + else { + bnx2_read_phy(bp, MII_ADVERTISE, &local_adv); + bnx2_read_phy(bp, MII_LPA, &remote_adv); + + common = local_adv & remote_adv; + if (common & ADVERTISE_100FULL) { + bp->line_speed = SPEED_100; + bp->duplex = DUPLEX_FULL; + } + else if (common & ADVERTISE_100HALF) { + bp->line_speed = SPEED_100; + bp->duplex = DUPLEX_HALF; + } + else if (common & ADVERTISE_10FULL) { + bp->line_speed = SPEED_10; + bp->duplex = DUPLEX_FULL; + } + else if (common & ADVERTISE_10HALF) { + bp->line_speed = SPEED_10; + bp->duplex = DUPLEX_HALF; + } + else { + bp->line_speed = 0; + bp->link_up = 0; + } + } + } + else { + if (bmcr & BMCR_SPEED100) { + bp->line_speed = SPEED_100; + } + else { + bp->line_speed = SPEED_10; + } + if (bmcr & BMCR_FULLDPLX) { + bp->duplex = DUPLEX_FULL; + } + else { + bp->duplex = DUPLEX_HALF; + } + } + + return 0; +} + +static int +bnx2_set_mac_link(struct bnx2 *bp) +{ + u32 val; + + REG_WR(bp, BNX2_EMAC_TX_LENGTHS, 0x2620); + if (bp->link_up && (bp->line_speed == SPEED_1000) && + (bp->duplex == DUPLEX_HALF)) { + REG_WR(bp, BNX2_EMAC_TX_LENGTHS, 0x26ff); + } + + /* Configure the EMAC mode register. */ + val = REG_RD(bp, BNX2_EMAC_MODE); + + val &= ~(BNX2_EMAC_MODE_PORT | BNX2_EMAC_MODE_HALF_DUPLEX | + BNX2_EMAC_MODE_MAC_LOOP | BNX2_EMAC_MODE_FORCE_LINK | + BNX2_EMAC_MODE_25G); + + if (bp->link_up) { + switch (bp->line_speed) { + case SPEED_10: + if (CHIP_NUM(bp) == CHIP_NUM_5708) { + val |= BNX2_EMAC_MODE_PORT_MII_10; + break; + } + /* fall through */ + case SPEED_100: + val |= BNX2_EMAC_MODE_PORT_MII; + break; + case SPEED_2500: + val |= BNX2_EMAC_MODE_25G; + /* fall through */ + case SPEED_1000: + val |= BNX2_EMAC_MODE_PORT_GMII; + break; + } + } + else { + val |= BNX2_EMAC_MODE_PORT_GMII; + } + + /* Set the MAC to operate in the appropriate duplex mode. */ + if (bp->duplex == DUPLEX_HALF) + val |= BNX2_EMAC_MODE_HALF_DUPLEX; + REG_WR(bp, BNX2_EMAC_MODE, val); + + /* Enable/disable rx PAUSE. */ + bp->rx_mode &= ~BNX2_EMAC_RX_MODE_FLOW_EN; + + if (bp->flow_ctrl & FLOW_CTRL_RX) + bp->rx_mode |= BNX2_EMAC_RX_MODE_FLOW_EN; + REG_WR(bp, BNX2_EMAC_RX_MODE, bp->rx_mode); + + /* Enable/disable tx PAUSE. */ + val = REG_RD(bp, BNX2_EMAC_TX_MODE); + val &= ~BNX2_EMAC_TX_MODE_FLOW_EN; + + if (bp->flow_ctrl & FLOW_CTRL_TX) + val |= BNX2_EMAC_TX_MODE_FLOW_EN; + REG_WR(bp, BNX2_EMAC_TX_MODE, val); + + /* Acknowledge the interrupt. */ + REG_WR(bp, BNX2_EMAC_STATUS, BNX2_EMAC_STATUS_LINK_CHANGE); + + return 0; +} + +static int +bnx2_set_link(struct bnx2 *bp) +{ + u32 bmsr; + u8 link_up; + + if (bp->loopback == MAC_LOOPBACK) { + bp->link_up = 1; + return 0; + } + + link_up = bp->link_up; + + bnx2_read_phy(bp, MII_BMSR, &bmsr); + bnx2_read_phy(bp, MII_BMSR, &bmsr); + + if ((bp->phy_flags & PHY_SERDES_FLAG) && + (CHIP_NUM(bp) == CHIP_NUM_5706)) { + u32 val; + + val = REG_RD(bp, BNX2_EMAC_STATUS); + if (val & BNX2_EMAC_STATUS_LINK) + bmsr |= BMSR_LSTATUS; + else + bmsr &= ~BMSR_LSTATUS; + } + + if (bmsr & BMSR_LSTATUS) { + bp->link_up = 1; + + if (bp->phy_flags & PHY_SERDES_FLAG) { + if (CHIP_NUM(bp) == CHIP_NUM_5706) + bnx2_5706s_linkup(bp); + else if (CHIP_NUM(bp) == CHIP_NUM_5708) + bnx2_5708s_linkup(bp); + } + else { + bnx2_copper_linkup(bp); + } + bnx2_resolve_flow_ctrl(bp); + } + else { + if ((bp->phy_flags & PHY_SERDES_FLAG) && + (bp->autoneg & AUTONEG_SPEED)) { + + u32 bmcr; + + bnx2_read_phy(bp, MII_BMCR, &bmcr); + if (!(bmcr & BMCR_ANENABLE)) { + bnx2_write_phy(bp, MII_BMCR, bmcr | + BMCR_ANENABLE); + } + } + bp->phy_flags &= ~PHY_PARALLEL_DETECT_FLAG; + bp->link_up = 0; + } + + if (bp->link_up != link_up) { + bnx2_report_link(bp); + } + + bnx2_set_mac_link(bp); + + return 0; +} + +static int +bnx2_reset_phy(struct bnx2 *bp) +{ + int i; + u32 reg; + + bnx2_write_phy(bp, MII_BMCR, BMCR_RESET); + +#define PHY_RESET_MAX_WAIT 100 + for (i = 0; i < PHY_RESET_MAX_WAIT; i++) { + udelay(10); + + bnx2_read_phy(bp, MII_BMCR, ®); + if (!(reg & BMCR_RESET)) { + udelay(20); + break; + } + } + if (i == PHY_RESET_MAX_WAIT) { + return -EBUSY; + } + return 0; +} + +static u32 +bnx2_phy_get_pause_adv(struct bnx2 *bp) +{ + u32 adv = 0; + + if ((bp->req_flow_ctrl & (FLOW_CTRL_RX | FLOW_CTRL_TX)) == + (FLOW_CTRL_RX | FLOW_CTRL_TX)) { + + if (bp->phy_flags & PHY_SERDES_FLAG) { + adv = ADVERTISE_1000XPAUSE; + } + else { + adv = ADVERTISE_PAUSE_CAP; + } + } + else if (bp->req_flow_ctrl & FLOW_CTRL_TX) { + if (bp->phy_flags & PHY_SERDES_FLAG) { + adv = ADVERTISE_1000XPSE_ASYM; + } + else { + adv = ADVERTISE_PAUSE_ASYM; + } + } + else if (bp->req_flow_ctrl & FLOW_CTRL_RX) { + if (bp->phy_flags & PHY_SERDES_FLAG) { + adv = ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM; + } + else { + adv = ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; + } + } + return adv; +} + +static int +bnx2_setup_serdes_phy(struct bnx2 *bp) +{ + u32 adv, bmcr, up1; + u32 new_adv = 0; + + if (!(bp->autoneg & AUTONEG_SPEED)) { + u32 new_bmcr; + int force_link_down = 0; + + if (CHIP_NUM(bp) == CHIP_NUM_5708) { + bnx2_read_phy(bp, BCM5708S_UP1, &up1); + if (up1 & BCM5708S_UP1_2G5) { + up1 &= ~BCM5708S_UP1_2G5; + bnx2_write_phy(bp, BCM5708S_UP1, up1); + force_link_down = 1; + } + } + + bnx2_read_phy(bp, MII_ADVERTISE, &adv); + adv &= ~(ADVERTISE_1000XFULL | ADVERTISE_1000XHALF); + + bnx2_read_phy(bp, MII_BMCR, &bmcr); + new_bmcr = bmcr & ~BMCR_ANENABLE; + new_bmcr |= BMCR_SPEED1000; + if (bp->req_duplex == DUPLEX_FULL) { + adv |= ADVERTISE_1000XFULL; + new_bmcr |= BMCR_FULLDPLX; + } + else { + adv |= ADVERTISE_1000XHALF; + new_bmcr &= ~BMCR_FULLDPLX; + } + if ((new_bmcr != bmcr) || (force_link_down)) { + /* Force a link down visible on the other side */ + if (bp->link_up) { + bnx2_write_phy(bp, MII_ADVERTISE, adv & + ~(ADVERTISE_1000XFULL | + ADVERTISE_1000XHALF)); + bnx2_write_phy(bp, MII_BMCR, bmcr | + BMCR_ANRESTART | BMCR_ANENABLE); + + bp->link_up = 0; + bnx2_write_phy(bp, MII_BMCR, new_bmcr); + } + bnx2_write_phy(bp, MII_ADVERTISE, adv); + bnx2_write_phy(bp, MII_BMCR, new_bmcr); + } + return 0; + } + + if (bp->phy_flags & PHY_2_5G_CAPABLE_FLAG) { + bnx2_read_phy(bp, BCM5708S_UP1, &up1); + up1 |= BCM5708S_UP1_2G5; + bnx2_write_phy(bp, BCM5708S_UP1, up1); + } + + if (bp->advertising & ADVERTISED_1000baseT_Full) + new_adv |= ADVERTISE_1000XFULL; + + new_adv |= bnx2_phy_get_pause_adv(bp); + + bnx2_read_phy(bp, MII_ADVERTISE, &adv); + bnx2_read_phy(bp, MII_BMCR, &bmcr); + + bp->serdes_an_pending = 0; + if ((adv != new_adv) || ((bmcr & BMCR_ANENABLE) == 0)) { + /* Force a link down visible on the other side */ + if (bp->link_up) { + int i; + + bnx2_write_phy(bp, MII_BMCR, BMCR_LOOPBACK); + for (i = 0; i < 110; i++) { + udelay(100); + } + } + + bnx2_write_phy(bp, MII_ADVERTISE, new_adv); + bnx2_write_phy(bp, MII_BMCR, bmcr | BMCR_ANRESTART | + BMCR_ANENABLE); +#if 0 + if (CHIP_NUM(bp) == CHIP_NUM_5706) { + /* Speed up link-up time when the link partner + * does not autonegotiate which is very common + * in blade servers. Some blade servers use + * IPMI for kerboard input and it's important + * to minimize link disruptions. Autoneg. involves + * exchanging base pages plus 3 next pages and + * normally completes in about 120 msec. + */ + bp->current_interval = SERDES_AN_TIMEOUT; + bp->serdes_an_pending = 1; + mod_timer(&bp->timer, jiffies + bp->current_interval); + } +#endif + } + + return 0; +} + +#define ETHTOOL_ALL_FIBRE_SPEED \ + (ADVERTISED_1000baseT_Full) + +#define ETHTOOL_ALL_COPPER_SPEED \ + (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | \ + ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | \ + ADVERTISED_1000baseT_Full) + +#define PHY_ALL_10_100_SPEED (ADVERTISE_10HALF | ADVERTISE_10FULL | \ + ADVERTISE_100HALF | ADVERTISE_100FULL | ADVERTISE_CSMA) + +#define PHY_ALL_1000_SPEED (ADVERTISE_1000HALF | ADVERTISE_1000FULL) + +static int +bnx2_setup_copper_phy(struct bnx2 *bp) +{ + u32 bmcr; + u32 new_bmcr; + + bnx2_read_phy(bp, MII_BMCR, &bmcr); + + if (bp->autoneg & AUTONEG_SPEED) { + u32 adv_reg, adv1000_reg; + u32 new_adv_reg = 0; + u32 new_adv1000_reg = 0; + + bnx2_read_phy(bp, MII_ADVERTISE, &adv_reg); + adv_reg &= (PHY_ALL_10_100_SPEED | ADVERTISE_PAUSE_CAP | + ADVERTISE_PAUSE_ASYM); + + bnx2_read_phy(bp, MII_CTRL1000, &adv1000_reg); + adv1000_reg &= PHY_ALL_1000_SPEED; + + if (bp->advertising & ADVERTISED_10baseT_Half) + new_adv_reg |= ADVERTISE_10HALF; + if (bp->advertising & ADVERTISED_10baseT_Full) + new_adv_reg |= ADVERTISE_10FULL; + if (bp->advertising & ADVERTISED_100baseT_Half) + new_adv_reg |= ADVERTISE_100HALF; + if (bp->advertising & ADVERTISED_100baseT_Full) + new_adv_reg |= ADVERTISE_100FULL; + if (bp->advertising & ADVERTISED_1000baseT_Full) + new_adv1000_reg |= ADVERTISE_1000FULL; + + new_adv_reg |= ADVERTISE_CSMA; + + new_adv_reg |= bnx2_phy_get_pause_adv(bp); + + if ((adv1000_reg != new_adv1000_reg) || + (adv_reg != new_adv_reg) || + ((bmcr & BMCR_ANENABLE) == 0)) { + + bnx2_write_phy(bp, MII_ADVERTISE, new_adv_reg); + bnx2_write_phy(bp, MII_CTRL1000, new_adv1000_reg); + bnx2_write_phy(bp, MII_BMCR, BMCR_ANRESTART | + BMCR_ANENABLE); + } + else if (bp->link_up) { + /* Flow ctrl may have changed from auto to forced */ + /* or vice-versa. */ + + bnx2_resolve_flow_ctrl(bp); + bnx2_set_mac_link(bp); + } + return 0; + } + + new_bmcr = 0; + if (bp->req_line_speed == SPEED_100) { + new_bmcr |= BMCR_SPEED100; + } + if (bp->req_duplex == DUPLEX_FULL) { + new_bmcr |= BMCR_FULLDPLX; + } + if (new_bmcr != bmcr) { + u32 bmsr; + int i = 0; + + bnx2_read_phy(bp, MII_BMSR, &bmsr); + bnx2_read_phy(bp, MII_BMSR, &bmsr); + + if (bmsr & BMSR_LSTATUS) { + /* Force link down */ + bnx2_write_phy(bp, MII_BMCR, BMCR_LOOPBACK); + do { + udelay(100); + bnx2_read_phy(bp, MII_BMSR, &bmsr); + bnx2_read_phy(bp, MII_BMSR, &bmsr); + i++; + } while ((bmsr & BMSR_LSTATUS) && (i < 620)); + } + + bnx2_write_phy(bp, MII_BMCR, new_bmcr); + + /* Normally, the new speed is setup after the link has + * gone down and up again. In some cases, link will not go + * down so we need to set up the new speed here. + */ + if (bmsr & BMSR_LSTATUS) { + bp->line_speed = bp->req_line_speed; + bp->duplex = bp->req_duplex; + bnx2_resolve_flow_ctrl(bp); + bnx2_set_mac_link(bp); + } + } + return 0; +} + +static int +bnx2_setup_phy(struct bnx2 *bp) +{ + if (bp->loopback == MAC_LOOPBACK) + return 0; + + if (bp->phy_flags & PHY_SERDES_FLAG) { + return (bnx2_setup_serdes_phy(bp)); + } + else { + return (bnx2_setup_copper_phy(bp)); + } +} + +static int +bnx2_init_5708s_phy(struct bnx2 *bp) +{ + u32 val; + + bnx2_write_phy(bp, BCM5708S_BLK_ADDR, BCM5708S_BLK_ADDR_DIG3); + bnx2_write_phy(bp, BCM5708S_DIG_3_0, BCM5708S_DIG_3_0_USE_IEEE); + bnx2_write_phy(bp, BCM5708S_BLK_ADDR, BCM5708S_BLK_ADDR_DIG); + + bnx2_read_phy(bp, BCM5708S_1000X_CTL1, &val); + val |= BCM5708S_1000X_CTL1_FIBER_MODE | BCM5708S_1000X_CTL1_AUTODET_EN; + bnx2_write_phy(bp, BCM5708S_1000X_CTL1, val); + + bnx2_read_phy(bp, BCM5708S_1000X_CTL2, &val); + val |= BCM5708S_1000X_CTL2_PLLEL_DET_EN; + bnx2_write_phy(bp, BCM5708S_1000X_CTL2, val); + + if (bp->phy_flags & PHY_2_5G_CAPABLE_FLAG) { + bnx2_read_phy(bp, BCM5708S_UP1, &val); + val |= BCM5708S_UP1_2G5; + bnx2_write_phy(bp, BCM5708S_UP1, val); + } + + if ((CHIP_ID(bp) == CHIP_ID_5708_A0) || + (CHIP_ID(bp) == CHIP_ID_5708_B0) || + (CHIP_ID(bp) == CHIP_ID_5708_B1)) { + /* increase tx signal amplitude */ + bnx2_write_phy(bp, BCM5708S_BLK_ADDR, + BCM5708S_BLK_ADDR_TX_MISC); + bnx2_read_phy(bp, BCM5708S_TX_ACTL1, &val); + val &= ~BCM5708S_TX_ACTL1_DRIVER_VCM; + bnx2_write_phy(bp, BCM5708S_TX_ACTL1, val); + bnx2_write_phy(bp, BCM5708S_BLK_ADDR, BCM5708S_BLK_ADDR_DIG); + } + + val = REG_RD_IND(bp, bp->shmem_base + BNX2_PORT_HW_CFG_CONFIG) & + BNX2_PORT_HW_CFG_CFG_TXCTL3_MASK; + + if (val) { + u32 is_backplane; + + is_backplane = REG_RD_IND(bp, bp->shmem_base + + BNX2_SHARED_HW_CFG_CONFIG); + if (is_backplane & BNX2_SHARED_HW_CFG_PHY_BACKPLANE) { + bnx2_write_phy(bp, BCM5708S_BLK_ADDR, + BCM5708S_BLK_ADDR_TX_MISC); + bnx2_write_phy(bp, BCM5708S_TX_ACTL3, val); + bnx2_write_phy(bp, BCM5708S_BLK_ADDR, + BCM5708S_BLK_ADDR_DIG); + } + } + return 0; +} + +static int +bnx2_init_5706s_phy(struct bnx2 *bp) +{ + u32 val; + + bp->phy_flags &= ~PHY_PARALLEL_DETECT_FLAG; + + if (CHIP_NUM(bp) == CHIP_NUM_5706) { + REG_WR(bp, BNX2_MISC_UNUSED0, 0x300); + } + + + bnx2_write_phy(bp, 0x18, 0x7); + bnx2_read_phy(bp, 0x18, &val); + bnx2_write_phy(bp, 0x18, val & ~0x4007); + + bnx2_write_phy(bp, 0x1c, 0x6c00); + bnx2_read_phy(bp, 0x1c, &val); + bnx2_write_phy(bp, 0x1c, (val & 0x3fd) | 0xec00); + + return 0; +} + +static int +bnx2_init_copper_phy(struct bnx2 *bp) +{ + u32 val; + + bp->phy_flags |= PHY_CRC_FIX_FLAG; + + if (bp->phy_flags & PHY_CRC_FIX_FLAG) { + bnx2_write_phy(bp, 0x18, 0x0c00); + bnx2_write_phy(bp, 0x17, 0x000a); + bnx2_write_phy(bp, 0x15, 0x310b); + bnx2_write_phy(bp, 0x17, 0x201f); + bnx2_write_phy(bp, 0x15, 0x9506); + bnx2_write_phy(bp, 0x17, 0x401f); + bnx2_write_phy(bp, 0x15, 0x14e2); + bnx2_write_phy(bp, 0x18, 0x0400); + } + + bnx2_write_phy(bp, 0x18, 0x7); + bnx2_read_phy(bp, 0x18, &val); + bnx2_write_phy(bp, 0x18, val & ~0x4007); + + bnx2_read_phy(bp, 0x10, &val); + bnx2_write_phy(bp, 0x10, val & ~0x1); + + /* ethernet@wirespeed */ + bnx2_write_phy(bp, 0x18, 0x7007); + bnx2_read_phy(bp, 0x18, &val); + bnx2_write_phy(bp, 0x18, val | (1 << 15) | (1 << 4)); + return 0; +} + +static int +bnx2_init_phy(struct bnx2 *bp) +{ + u32 val; + int rc = 0; + + bp->phy_flags &= ~PHY_INT_MODE_MASK_FLAG; + bp->phy_flags |= PHY_INT_MODE_LINK_READY_FLAG; + + REG_WR(bp, BNX2_EMAC_ATTENTION_ENA, BNX2_EMAC_ATTENTION_ENA_LINK); + + bnx2_reset_phy(bp); + + bnx2_read_phy(bp, MII_PHYSID1, &val); + bp->phy_id = val << 16; + bnx2_read_phy(bp, MII_PHYSID2, &val); + bp->phy_id |= val & 0xffff; + + if (bp->phy_flags & PHY_SERDES_FLAG) { + if (CHIP_NUM(bp) == CHIP_NUM_5706) + rc = bnx2_init_5706s_phy(bp); + else if (CHIP_NUM(bp) == CHIP_NUM_5708) + rc = bnx2_init_5708s_phy(bp); + } + else { + rc = bnx2_init_copper_phy(bp); + } + + bnx2_setup_phy(bp); + + return rc; +} + +static int +bnx2_fw_sync(struct bnx2 *bp, u32 msg_data, int silent) +{ + int i; + u32 val; + + bp->fw_wr_seq++; + msg_data |= bp->fw_wr_seq; + + REG_WR_IND(bp, bp->shmem_base + BNX2_DRV_MB, msg_data); + + /* wait for an acknowledgement. */ + for (i = 0; i < (FW_ACK_TIME_OUT_MS / 50); i++) { + mdelay(50); + + val = REG_RD_IND(bp, bp->shmem_base + BNX2_FW_MB); + + if ((val & BNX2_FW_MSG_ACK) == (msg_data & BNX2_DRV_MSG_SEQ)) + break; + } + if ((msg_data & BNX2_DRV_MSG_DATA) == BNX2_DRV_MSG_DATA_WAIT0) + return 0; + + /* If we timed out, inform the firmware that this is the case. */ + if ((val & BNX2_FW_MSG_ACK) != (msg_data & BNX2_DRV_MSG_SEQ)) { + if (!silent) + printf("fw sync timeout, reset code = %x\n", (unsigned int) msg_data); + + msg_data &= ~BNX2_DRV_MSG_CODE; + msg_data |= BNX2_DRV_MSG_CODE_FW_TIMEOUT; + + REG_WR_IND(bp, bp->shmem_base + BNX2_DRV_MB, msg_data); + + return -EBUSY; + } + + if ((val & BNX2_FW_MSG_STATUS_MASK) != BNX2_FW_MSG_STATUS_OK) + return -EIO; + + return 0; +} + +static void +bnx2_init_context(struct bnx2 *bp) +{ + u32 vcid; + + vcid = 96; + while (vcid) { + u32 vcid_addr, pcid_addr, offset; + + vcid--; + + if (CHIP_ID(bp) == CHIP_ID_5706_A0) { + u32 new_vcid; + + vcid_addr = GET_PCID_ADDR(vcid); + if (vcid & 0x8) { + new_vcid = 0x60 + (vcid & 0xf0) + (vcid & 0x7); + } + else { + new_vcid = vcid; + } + pcid_addr = GET_PCID_ADDR(new_vcid); + } + else { + vcid_addr = GET_CID_ADDR(vcid); + pcid_addr = vcid_addr; + } + + REG_WR(bp, BNX2_CTX_VIRT_ADDR, 0x00); + REG_WR(bp, BNX2_CTX_PAGE_TBL, pcid_addr); + + /* Zero out the context. */ + for (offset = 0; offset < PHY_CTX_SIZE; offset += 4) { + CTX_WR(bp, 0x00, offset, 0); + } + + REG_WR(bp, BNX2_CTX_VIRT_ADDR, vcid_addr); + REG_WR(bp, BNX2_CTX_PAGE_TBL, pcid_addr); + } +} + +static int +bnx2_alloc_bad_rbuf(struct bnx2 *bp) +{ + u16 good_mbuf[512]; + u32 good_mbuf_cnt; + u32 val; + + REG_WR(bp, BNX2_MISC_ENABLE_SET_BITS, + BNX2_MISC_ENABLE_SET_BITS_RX_MBUF_ENABLE); + + good_mbuf_cnt = 0; + + /* Allocate a bunch of mbufs and save the good ones in an array. */ + val = REG_RD_IND(bp, BNX2_RBUF_STATUS1); + while (val & BNX2_RBUF_STATUS1_FREE_COUNT) { + REG_WR_IND(bp, BNX2_RBUF_COMMAND, BNX2_RBUF_COMMAND_ALLOC_REQ); + + val = REG_RD_IND(bp, BNX2_RBUF_FW_BUF_ALLOC); + + val &= BNX2_RBUF_FW_BUF_ALLOC_VALUE; + + /* The addresses with Bit 9 set are bad memory blocks. */ + if (!(val & (1 << 9))) { + good_mbuf[good_mbuf_cnt] = (u16) val; + good_mbuf_cnt++; + } + + val = REG_RD_IND(bp, BNX2_RBUF_STATUS1); + } + + /* Free the good ones back to the mbuf pool thus discarding + * all the bad ones. */ + while (good_mbuf_cnt) { + good_mbuf_cnt--; + + val = good_mbuf[good_mbuf_cnt]; + val = (val << 9) | val | 1; + + REG_WR_IND(bp, BNX2_RBUF_FW_BUF_FREE, val); + } + return 0; +} + +static void +bnx2_set_mac_addr(struct bnx2 *bp) +{ + u32 val; + u8 *mac_addr = bp->nic->node_addr; + + val = (mac_addr[0] << 8) | mac_addr[1]; + + REG_WR(bp, BNX2_EMAC_MAC_MATCH0, val); + + val = (mac_addr[2] << 24) | (mac_addr[3] << 16) | + (mac_addr[4] << 8) | mac_addr[5]; + + REG_WR(bp, BNX2_EMAC_MAC_MATCH1, val); +} + +static void +bnx2_set_rx_mode(struct nic *nic __unused) +{ + struct bnx2 *bp = &bnx2; + u32 rx_mode, sort_mode; + int i; + + rx_mode = bp->rx_mode & ~(BNX2_EMAC_RX_MODE_PROMISCUOUS | + BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG); + sort_mode = 1 | BNX2_RPM_SORT_USER0_BC_EN; + + if (!(bp->flags & ASF_ENABLE_FLAG)) { + rx_mode |= BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG; + } + + /* Accept all multicasts */ + for (i = 0; i < NUM_MC_HASH_REGISTERS; i++) { + REG_WR(bp, BNX2_EMAC_MULTICAST_HASH0 + (i * 4), + 0xffffffff); + } + sort_mode |= BNX2_RPM_SORT_USER0_MC_EN; + + if (rx_mode != bp->rx_mode) { + bp->rx_mode = rx_mode; + REG_WR(bp, BNX2_EMAC_RX_MODE, rx_mode); + } + + REG_WR(bp, BNX2_RPM_SORT_USER0, 0x0); + REG_WR(bp, BNX2_RPM_SORT_USER0, sort_mode); + REG_WR(bp, BNX2_RPM_SORT_USER0, sort_mode | BNX2_RPM_SORT_USER0_ENA); +} + +static void +load_rv2p_fw(struct bnx2 *bp, u32 *rv2p_code, u32 rv2p_code_len, u32 rv2p_proc) +{ + unsigned int i; + u32 val; + + + for (i = 0; i < rv2p_code_len; i += 8) { + REG_WR(bp, BNX2_RV2P_INSTR_HIGH, *rv2p_code); + rv2p_code++; + REG_WR(bp, BNX2_RV2P_INSTR_LOW, *rv2p_code); + rv2p_code++; + + if (rv2p_proc == RV2P_PROC1) { + val = (i / 8) | BNX2_RV2P_PROC1_ADDR_CMD_RDWR; + REG_WR(bp, BNX2_RV2P_PROC1_ADDR_CMD, val); + } + else { + val = (i / 8) | BNX2_RV2P_PROC2_ADDR_CMD_RDWR; + REG_WR(bp, BNX2_RV2P_PROC2_ADDR_CMD, val); + } + } + + /* Reset the processor, un-stall is done later. */ + if (rv2p_proc == RV2P_PROC1) { + REG_WR(bp, BNX2_RV2P_COMMAND, BNX2_RV2P_COMMAND_PROC1_RESET); + } + else { + REG_WR(bp, BNX2_RV2P_COMMAND, BNX2_RV2P_COMMAND_PROC2_RESET); + } +} + +static void +load_cpu_fw(struct bnx2 *bp, struct cpu_reg *cpu_reg, struct fw_info *fw) +{ + u32 offset; + u32 val; + + /* Halt the CPU. */ + val = REG_RD_IND(bp, cpu_reg->mode); + val |= cpu_reg->mode_value_halt; + REG_WR_IND(bp, cpu_reg->mode, val); + REG_WR_IND(bp, cpu_reg->state, cpu_reg->state_value_clear); + + /* Load the Text area. */ + offset = cpu_reg->spad_base + (fw->text_addr - cpu_reg->mips_view_base); + if (fw->text) { + unsigned int j; + + for (j = 0; j < (fw->text_len / 4); j++, offset += 4) { + REG_WR_IND(bp, offset, fw->text[j]); + } + } + + /* Load the Data area. */ + offset = cpu_reg->spad_base + (fw->data_addr - cpu_reg->mips_view_base); + if (fw->data) { + unsigned int j; + + for (j = 0; j < (fw->data_len / 4); j++, offset += 4) { + REG_WR_IND(bp, offset, fw->data[j]); + } + } + + /* Load the SBSS area. */ + offset = cpu_reg->spad_base + (fw->sbss_addr - cpu_reg->mips_view_base); + if (fw->sbss) { + unsigned int j; + + for (j = 0; j < (fw->sbss_len / 4); j++, offset += 4) { + REG_WR_IND(bp, offset, fw->sbss[j]); + } + } + + /* Load the BSS area. */ + offset = cpu_reg->spad_base + (fw->bss_addr - cpu_reg->mips_view_base); + if (fw->bss) { + unsigned int j; + + for (j = 0; j < (fw->bss_len/4); j++, offset += 4) { + REG_WR_IND(bp, offset, fw->bss[j]); + } + } + + /* Load the Read-Only area. */ + offset = cpu_reg->spad_base + + (fw->rodata_addr - cpu_reg->mips_view_base); + if (fw->rodata) { + unsigned int j; + + for (j = 0; j < (fw->rodata_len / 4); j++, offset += 4) { + REG_WR_IND(bp, offset, fw->rodata[j]); + } + } + + /* Clear the pre-fetch instruction. */ + REG_WR_IND(bp, cpu_reg->inst, 0); + REG_WR_IND(bp, cpu_reg->pc, fw->start_addr); + + /* Start the CPU. */ + val = REG_RD_IND(bp, cpu_reg->mode); + val &= ~cpu_reg->mode_value_halt; + REG_WR_IND(bp, cpu_reg->state, cpu_reg->state_value_clear); + REG_WR_IND(bp, cpu_reg->mode, val); +} + +static void +bnx2_init_cpus(struct bnx2 *bp) +{ + struct cpu_reg cpu_reg; + struct fw_info fw; + + /* Unfortunately, it looks like we need to load the firmware + * before the card will work properly. That means this driver + * will be huge by Etherboot standards (approx. 50K compressed). + */ + + /* Initialize the RV2P processor. */ + load_rv2p_fw(bp, bnx2_rv2p_proc1, sizeof(bnx2_rv2p_proc1), RV2P_PROC1); + load_rv2p_fw(bp, bnx2_rv2p_proc2, sizeof(bnx2_rv2p_proc2), RV2P_PROC2); + + /* Initialize the RX Processor. */ + cpu_reg.mode = BNX2_RXP_CPU_MODE; + cpu_reg.mode_value_halt = BNX2_RXP_CPU_MODE_SOFT_HALT; + cpu_reg.mode_value_sstep = BNX2_RXP_CPU_MODE_STEP_ENA; + cpu_reg.state = BNX2_RXP_CPU_STATE; + cpu_reg.state_value_clear = 0xffffff; + cpu_reg.gpr0 = BNX2_RXP_CPU_REG_FILE; + cpu_reg.evmask = BNX2_RXP_CPU_EVENT_MASK; + cpu_reg.pc = BNX2_RXP_CPU_PROGRAM_COUNTER; + cpu_reg.inst = BNX2_RXP_CPU_INSTRUCTION; + cpu_reg.bp = BNX2_RXP_CPU_HW_BREAKPOINT; + cpu_reg.spad_base = BNX2_RXP_SCRATCH; + cpu_reg.mips_view_base = 0x8000000; + + fw.ver_major = bnx2_RXP_b06FwReleaseMajor; + fw.ver_minor = bnx2_RXP_b06FwReleaseMinor; + fw.ver_fix = bnx2_RXP_b06FwReleaseFix; + fw.start_addr = bnx2_RXP_b06FwStartAddr; + + fw.text_addr = bnx2_RXP_b06FwTextAddr; + fw.text_len = bnx2_RXP_b06FwTextLen; + fw.text_index = 0; + fw.text = bnx2_RXP_b06FwText; + + fw.data_addr = bnx2_RXP_b06FwDataAddr; + fw.data_len = bnx2_RXP_b06FwDataLen; + fw.data_index = 0; + fw.data = bnx2_RXP_b06FwData; + + fw.sbss_addr = bnx2_RXP_b06FwSbssAddr; + fw.sbss_len = bnx2_RXP_b06FwSbssLen; + fw.sbss_index = 0; + fw.sbss = bnx2_RXP_b06FwSbss; + + fw.bss_addr = bnx2_RXP_b06FwBssAddr; + fw.bss_len = bnx2_RXP_b06FwBssLen; + fw.bss_index = 0; + fw.bss = bnx2_RXP_b06FwBss; + + fw.rodata_addr = bnx2_RXP_b06FwRodataAddr; + fw.rodata_len = bnx2_RXP_b06FwRodataLen; + fw.rodata_index = 0; + fw.rodata = bnx2_RXP_b06FwRodata; + + load_cpu_fw(bp, &cpu_reg, &fw); + + /* Initialize the TX Processor. */ + cpu_reg.mode = BNX2_TXP_CPU_MODE; + cpu_reg.mode_value_halt = BNX2_TXP_CPU_MODE_SOFT_HALT; + cpu_reg.mode_value_sstep = BNX2_TXP_CPU_MODE_STEP_ENA; + cpu_reg.state = BNX2_TXP_CPU_STATE; + cpu_reg.state_value_clear = 0xffffff; + cpu_reg.gpr0 = BNX2_TXP_CPU_REG_FILE; + cpu_reg.evmask = BNX2_TXP_CPU_EVENT_MASK; + cpu_reg.pc = BNX2_TXP_CPU_PROGRAM_COUNTER; + cpu_reg.inst = BNX2_TXP_CPU_INSTRUCTION; + cpu_reg.bp = BNX2_TXP_CPU_HW_BREAKPOINT; + cpu_reg.spad_base = BNX2_TXP_SCRATCH; + cpu_reg.mips_view_base = 0x8000000; + + fw.ver_major = bnx2_TXP_b06FwReleaseMajor; + fw.ver_minor = bnx2_TXP_b06FwReleaseMinor; + fw.ver_fix = bnx2_TXP_b06FwReleaseFix; + fw.start_addr = bnx2_TXP_b06FwStartAddr; + + fw.text_addr = bnx2_TXP_b06FwTextAddr; + fw.text_len = bnx2_TXP_b06FwTextLen; + fw.text_index = 0; + fw.text = bnx2_TXP_b06FwText; + + fw.data_addr = bnx2_TXP_b06FwDataAddr; + fw.data_len = bnx2_TXP_b06FwDataLen; + fw.data_index = 0; + fw.data = bnx2_TXP_b06FwData; + + fw.sbss_addr = bnx2_TXP_b06FwSbssAddr; + fw.sbss_len = bnx2_TXP_b06FwSbssLen; + fw.sbss_index = 0; + fw.sbss = bnx2_TXP_b06FwSbss; + + fw.bss_addr = bnx2_TXP_b06FwBssAddr; + fw.bss_len = bnx2_TXP_b06FwBssLen; + fw.bss_index = 0; + fw.bss = bnx2_TXP_b06FwBss; + + fw.rodata_addr = bnx2_TXP_b06FwRodataAddr; + fw.rodata_len = bnx2_TXP_b06FwRodataLen; + fw.rodata_index = 0; + fw.rodata = bnx2_TXP_b06FwRodata; + + load_cpu_fw(bp, &cpu_reg, &fw); + + /* Initialize the TX Patch-up Processor. */ + cpu_reg.mode = BNX2_TPAT_CPU_MODE; + cpu_reg.mode_value_halt = BNX2_TPAT_CPU_MODE_SOFT_HALT; + cpu_reg.mode_value_sstep = BNX2_TPAT_CPU_MODE_STEP_ENA; + cpu_reg.state = BNX2_TPAT_CPU_STATE; + cpu_reg.state_value_clear = 0xffffff; + cpu_reg.gpr0 = BNX2_TPAT_CPU_REG_FILE; + cpu_reg.evmask = BNX2_TPAT_CPU_EVENT_MASK; + cpu_reg.pc = BNX2_TPAT_CPU_PROGRAM_COUNTER; + cpu_reg.inst = BNX2_TPAT_CPU_INSTRUCTION; + cpu_reg.bp = BNX2_TPAT_CPU_HW_BREAKPOINT; + cpu_reg.spad_base = BNX2_TPAT_SCRATCH; + cpu_reg.mips_view_base = 0x8000000; + + fw.ver_major = bnx2_TPAT_b06FwReleaseMajor; + fw.ver_minor = bnx2_TPAT_b06FwReleaseMinor; + fw.ver_fix = bnx2_TPAT_b06FwReleaseFix; + fw.start_addr = bnx2_TPAT_b06FwStartAddr; + + fw.text_addr = bnx2_TPAT_b06FwTextAddr; + fw.text_len = bnx2_TPAT_b06FwTextLen; + fw.text_index = 0; + fw.text = bnx2_TPAT_b06FwText; + + fw.data_addr = bnx2_TPAT_b06FwDataAddr; + fw.data_len = bnx2_TPAT_b06FwDataLen; + fw.data_index = 0; + fw.data = bnx2_TPAT_b06FwData; + + fw.sbss_addr = bnx2_TPAT_b06FwSbssAddr; + fw.sbss_len = bnx2_TPAT_b06FwSbssLen; + fw.sbss_index = 0; + fw.sbss = bnx2_TPAT_b06FwSbss; + + fw.bss_addr = bnx2_TPAT_b06FwBssAddr; + fw.bss_len = bnx2_TPAT_b06FwBssLen; + fw.bss_index = 0; + fw.bss = bnx2_TPAT_b06FwBss; + + fw.rodata_addr = bnx2_TPAT_b06FwRodataAddr; + fw.rodata_len = bnx2_TPAT_b06FwRodataLen; + fw.rodata_index = 0; + fw.rodata = bnx2_TPAT_b06FwRodata; + + load_cpu_fw(bp, &cpu_reg, &fw); + + /* Initialize the Completion Processor. */ + cpu_reg.mode = BNX2_COM_CPU_MODE; + cpu_reg.mode_value_halt = BNX2_COM_CPU_MODE_SOFT_HALT; + cpu_reg.mode_value_sstep = BNX2_COM_CPU_MODE_STEP_ENA; + cpu_reg.state = BNX2_COM_CPU_STATE; + cpu_reg.state_value_clear = 0xffffff; + cpu_reg.gpr0 = BNX2_COM_CPU_REG_FILE; + cpu_reg.evmask = BNX2_COM_CPU_EVENT_MASK; + cpu_reg.pc = BNX2_COM_CPU_PROGRAM_COUNTER; + cpu_reg.inst = BNX2_COM_CPU_INSTRUCTION; + cpu_reg.bp = BNX2_COM_CPU_HW_BREAKPOINT; + cpu_reg.spad_base = BNX2_COM_SCRATCH; + cpu_reg.mips_view_base = 0x8000000; + + fw.ver_major = bnx2_COM_b06FwReleaseMajor; + fw.ver_minor = bnx2_COM_b06FwReleaseMinor; + fw.ver_fix = bnx2_COM_b06FwReleaseFix; + fw.start_addr = bnx2_COM_b06FwStartAddr; + + fw.text_addr = bnx2_COM_b06FwTextAddr; + fw.text_len = bnx2_COM_b06FwTextLen; + fw.text_index = 0; + fw.text = bnx2_COM_b06FwText; + + fw.data_addr = bnx2_COM_b06FwDataAddr; + fw.data_len = bnx2_COM_b06FwDataLen; + fw.data_index = 0; + fw.data = bnx2_COM_b06FwData; + + fw.sbss_addr = bnx2_COM_b06FwSbssAddr; + fw.sbss_len = bnx2_COM_b06FwSbssLen; + fw.sbss_index = 0; + fw.sbss = bnx2_COM_b06FwSbss; + + fw.bss_addr = bnx2_COM_b06FwBssAddr; + fw.bss_len = bnx2_COM_b06FwBssLen; + fw.bss_index = 0; + fw.bss = bnx2_COM_b06FwBss; + + fw.rodata_addr = bnx2_COM_b06FwRodataAddr; + fw.rodata_len = bnx2_COM_b06FwRodataLen; + fw.rodata_index = 0; + fw.rodata = bnx2_COM_b06FwRodata; + + load_cpu_fw(bp, &cpu_reg, &fw); + +} + +static int +bnx2_set_power_state_0(struct bnx2 *bp) +{ + u16 pmcsr; + u32 val; + + pci_read_config_word(bp->pdev, bp->pm_cap + PCI_PM_CTRL, &pmcsr); + + pci_write_config_word(bp->pdev, bp->pm_cap + PCI_PM_CTRL, + (pmcsr & ~PCI_PM_CTRL_STATE_MASK) | + PCI_PM_CTRL_PME_STATUS); + + if (pmcsr & PCI_PM_CTRL_STATE_MASK) + /* delay required during transition out of D3hot */ + mdelay(20); + + val = REG_RD(bp, BNX2_EMAC_MODE); + val |= BNX2_EMAC_MODE_MPKT_RCVD | BNX2_EMAC_MODE_ACPI_RCVD; + val &= ~BNX2_EMAC_MODE_MPKT; + REG_WR(bp, BNX2_EMAC_MODE, val); + + val = REG_RD(bp, BNX2_RPM_CONFIG); + val &= ~BNX2_RPM_CONFIG_ACPI_ENA; + REG_WR(bp, BNX2_RPM_CONFIG, val); + + return 0; +} + +static void +bnx2_enable_nvram_access(struct bnx2 *bp) +{ + u32 val; + + val = REG_RD(bp, BNX2_NVM_ACCESS_ENABLE); + /* Enable both bits, even on read. */ + REG_WR(bp, BNX2_NVM_ACCESS_ENABLE, + val | BNX2_NVM_ACCESS_ENABLE_EN | BNX2_NVM_ACCESS_ENABLE_WR_EN); +} + +static void +bnx2_disable_nvram_access(struct bnx2 *bp) +{ + u32 val; + + val = REG_RD(bp, BNX2_NVM_ACCESS_ENABLE); + /* Disable both bits, even after read. */ + REG_WR(bp, BNX2_NVM_ACCESS_ENABLE, + val & ~(BNX2_NVM_ACCESS_ENABLE_EN | + BNX2_NVM_ACCESS_ENABLE_WR_EN)); +} + +static int +bnx2_init_nvram(struct bnx2 *bp) +{ + u32 val; + int j, entry_count, rc; + struct flash_spec *flash; + + /* Determine the selected interface. */ + val = REG_RD(bp, BNX2_NVM_CFG1); + + entry_count = sizeof(flash_table) / sizeof(struct flash_spec); + + rc = 0; + if (val & 0x40000000) { + /* Flash interface has been reconfigured */ + for (j = 0, flash = &flash_table[0]; j < entry_count; + j++, flash++) { + if ((val & FLASH_BACKUP_STRAP_MASK) == + (flash->config1 & FLASH_BACKUP_STRAP_MASK)) { + bp->flash_info = flash; + break; + } + } + } + else { + u32 mask; + /* Not yet been reconfigured */ + + if (val & (1 << 23)) + mask = FLASH_BACKUP_STRAP_MASK; + else + mask = FLASH_STRAP_MASK; + + for (j = 0, flash = &flash_table[0]; j < entry_count; + j++, flash++) { + + if ((val & mask) == (flash->strapping & mask)) { + bp->flash_info = flash; + + /* Enable access to flash interface */ + bnx2_enable_nvram_access(bp); + + /* Reconfigure the flash interface */ + REG_WR(bp, BNX2_NVM_CFG1, flash->config1); + REG_WR(bp, BNX2_NVM_CFG2, flash->config2); + REG_WR(bp, BNX2_NVM_CFG3, flash->config3); + REG_WR(bp, BNX2_NVM_WRITE1, flash->write1); + + /* Disable access to flash interface */ + bnx2_disable_nvram_access(bp); + + break; + } + } + } /* if (val & 0x40000000) */ + + if (j == entry_count) { + bp->flash_info = NULL; + printf("Unknown flash/EEPROM type.\n"); + return -ENODEV; + } + + val = REG_RD_IND(bp, bp->shmem_base + BNX2_SHARED_HW_CFG_CONFIG2); + val &= BNX2_SHARED_HW_CFG2_NVM_SIZE_MASK; + if (val) { + bp->flash_size = val; + } + else { + bp->flash_size = bp->flash_info->total_size; + } + + return rc; +} + +static int +bnx2_reset_chip(struct bnx2 *bp, u32 reset_code) +{ + u32 val; + int i, rc = 0; + + /* Wait for the current PCI transaction to complete before + * issuing a reset. */ + REG_WR(bp, BNX2_MISC_ENABLE_CLR_BITS, + BNX2_MISC_ENABLE_CLR_BITS_TX_DMA_ENABLE | + BNX2_MISC_ENABLE_CLR_BITS_DMA_ENGINE_ENABLE | + BNX2_MISC_ENABLE_CLR_BITS_RX_DMA_ENABLE | + BNX2_MISC_ENABLE_CLR_BITS_HOST_COALESCE_ENABLE); + val = REG_RD(bp, BNX2_MISC_ENABLE_CLR_BITS); + udelay(5); + + + /* Wait for the firmware to tell us it is ok to issue a reset. */ + bnx2_fw_sync(bp, BNX2_DRV_MSG_DATA_WAIT0 | reset_code, 1); + + /* Deposit a driver reset signature so the firmware knows that + * this is a soft reset. */ + REG_WR_IND(bp, bp->shmem_base + BNX2_DRV_RESET_SIGNATURE, + BNX2_DRV_RESET_SIGNATURE_MAGIC); + + /* Do a dummy read to force the chip to complete all current transaction + * before we issue a reset. */ + val = REG_RD(bp, BNX2_MISC_ID); + + val = BNX2_PCICFG_MISC_CONFIG_CORE_RST_REQ | + BNX2_PCICFG_MISC_CONFIG_REG_WINDOW_ENA | + BNX2_PCICFG_MISC_CONFIG_TARGET_MB_WORD_SWAP; + + /* Chip reset. */ + REG_WR(bp, BNX2_PCICFG_MISC_CONFIG, val); + + if ((CHIP_ID(bp) == CHIP_ID_5706_A0) || + (CHIP_ID(bp) == CHIP_ID_5706_A1)) + mdelay(15); + + /* Reset takes approximate 30 usec */ + for (i = 0; i < 10; i++) { + val = REG_RD(bp, BNX2_PCICFG_MISC_CONFIG); + if ((val & (BNX2_PCICFG_MISC_CONFIG_CORE_RST_REQ | + BNX2_PCICFG_MISC_CONFIG_CORE_RST_BSY)) == 0) { + break; + } + udelay(10); + } + + if (val & (BNX2_PCICFG_MISC_CONFIG_CORE_RST_REQ | + BNX2_PCICFG_MISC_CONFIG_CORE_RST_BSY)) { + printf("Chip reset did not complete\n"); + return -EBUSY; + } + + /* Make sure byte swapping is properly configured. */ + val = REG_RD(bp, BNX2_PCI_SWAP_DIAG0); + if (val != 0x01020304) { + printf("Chip not in correct endian mode\n"); + return -ENODEV; + } + + /* Wait for the firmware to finish its initialization. */ + rc = bnx2_fw_sync(bp, BNX2_DRV_MSG_DATA_WAIT1 | reset_code, 0); + if (rc) { + return rc; + } + + if (CHIP_ID(bp) == CHIP_ID_5706_A0) { + /* Adjust the voltage regular to two steps lower. The default + * of this register is 0x0000000e. */ + REG_WR(bp, BNX2_MISC_VREG_CONTROL, 0x000000fa); + + /* Remove bad rbuf memory from the free pool. */ + rc = bnx2_alloc_bad_rbuf(bp); + } + + return rc; +} + +static void +bnx2_disable(struct nic *nic __unused) +{ + struct bnx2* bp = &bnx2; + + if (bp->regview) { + bnx2_reset_chip(bp, BNX2_DRV_MSG_CODE_UNLOAD); + iounmap(bp->regview); + } +} + +static int +bnx2_init_chip(struct bnx2 *bp) +{ + u32 val; + int rc; + + /* Make sure the interrupt is not active. */ + REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, BNX2_PCICFG_INT_ACK_CMD_MASK_INT); + + val = BNX2_DMA_CONFIG_DATA_BYTE_SWAP | + BNX2_DMA_CONFIG_DATA_WORD_SWAP | +#if __BYTE_ORDER == __BIG_ENDIAN + BNX2_DMA_CONFIG_CNTL_BYTE_SWAP | +#endif + BNX2_DMA_CONFIG_CNTL_WORD_SWAP | + DMA_READ_CHANS << 12 | + DMA_WRITE_CHANS << 16; + + val |= (0x2 << 20) | (1 << 11); + + if ((bp->flags & PCIX_FLAG) && (bp->bus_speed_mhz == 133)) + val |= (1 << 23); + + if ((CHIP_NUM(bp) == CHIP_NUM_5706) && + (CHIP_ID(bp) != CHIP_ID_5706_A0) && !(bp->flags & PCIX_FLAG)) + val |= BNX2_DMA_CONFIG_CNTL_PING_PONG_DMA; + + REG_WR(bp, BNX2_DMA_CONFIG, val); + + if (CHIP_ID(bp) == CHIP_ID_5706_A0) { + val = REG_RD(bp, BNX2_TDMA_CONFIG); + val |= BNX2_TDMA_CONFIG_ONE_DMA; + REG_WR(bp, BNX2_TDMA_CONFIG, val); + } + + if (bp->flags & PCIX_FLAG) { + u16 val16; + + pci_read_config_word(bp->pdev, bp->pcix_cap + PCI_X_CMD, + &val16); + pci_write_config_word(bp->pdev, bp->pcix_cap + PCI_X_CMD, + val16 & ~PCI_X_CMD_ERO); + } + + REG_WR(bp, BNX2_MISC_ENABLE_SET_BITS, + BNX2_MISC_ENABLE_SET_BITS_HOST_COALESCE_ENABLE | + BNX2_MISC_ENABLE_STATUS_BITS_RX_V2P_ENABLE | + BNX2_MISC_ENABLE_STATUS_BITS_CONTEXT_ENABLE); + + /* Initialize context mapping and zero out the quick contexts. The + * context block must have already been enabled. */ + bnx2_init_context(bp); + + bnx2_init_nvram(bp); + bnx2_init_cpus(bp); + + bnx2_set_mac_addr(bp); + + val = REG_RD(bp, BNX2_MQ_CONFIG); + val &= ~BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE; + val |= BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_256; + REG_WR(bp, BNX2_MQ_CONFIG, val); + + val = 0x10000 + (MAX_CID_CNT * MB_KERNEL_CTX_SIZE); + REG_WR(bp, BNX2_MQ_KNL_BYP_WIND_START, val); + REG_WR(bp, BNX2_MQ_KNL_WIND_END, val); + + val = (BCM_PAGE_BITS - 8) << 24; + REG_WR(bp, BNX2_RV2P_CONFIG, val); + + /* Configure page size. */ + val = REG_RD(bp, BNX2_TBDR_CONFIG); + val &= ~BNX2_TBDR_CONFIG_PAGE_SIZE; + val |= (BCM_PAGE_BITS - 8) << 24 | 0x40; + REG_WR(bp, BNX2_TBDR_CONFIG, val); + + val = bp->mac_addr[0] + + (bp->mac_addr[1] << 8) + + (bp->mac_addr[2] << 16) + + bp->mac_addr[3] + + (bp->mac_addr[4] << 8) + + (bp->mac_addr[5] << 16); + REG_WR(bp, BNX2_EMAC_BACKOFF_SEED, val); + + /* Program the MTU. Also include 4 bytes for CRC32. */ + val = ETH_MAX_MTU + ETH_HLEN + 4; + if (val > (MAX_ETHERNET_PACKET_SIZE + 4)) + val |= BNX2_EMAC_RX_MTU_SIZE_JUMBO_ENA; + REG_WR(bp, BNX2_EMAC_RX_MTU_SIZE, val); + + bp->last_status_idx = 0; + bp->rx_mode = BNX2_EMAC_RX_MODE_SORT_MODE; + + /* Set up how to generate a link change interrupt. */ + REG_WR(bp, BNX2_EMAC_ATTENTION_ENA, BNX2_EMAC_ATTENTION_ENA_LINK); + + REG_WR(bp, BNX2_HC_STATUS_ADDR_L, + (u64) bp->status_blk_mapping & 0xffffffff); + REG_WR(bp, BNX2_HC_STATUS_ADDR_H, (u64) bp->status_blk_mapping >> 32); + + REG_WR(bp, BNX2_HC_STATISTICS_ADDR_L, + (u64) bp->stats_blk_mapping & 0xffffffff); + REG_WR(bp, BNX2_HC_STATISTICS_ADDR_H, + (u64) bp->stats_blk_mapping >> 32); + + REG_WR(bp, BNX2_HC_TX_QUICK_CONS_TRIP, + (bp->tx_quick_cons_trip_int << 16) | bp->tx_quick_cons_trip); + + REG_WR(bp, BNX2_HC_RX_QUICK_CONS_TRIP, + (bp->rx_quick_cons_trip_int << 16) | bp->rx_quick_cons_trip); + + REG_WR(bp, BNX2_HC_COMP_PROD_TRIP, + (bp->comp_prod_trip_int << 16) | bp->comp_prod_trip); + + REG_WR(bp, BNX2_HC_TX_TICKS, (bp->tx_ticks_int << 16) | bp->tx_ticks); + + REG_WR(bp, BNX2_HC_RX_TICKS, (bp->rx_ticks_int << 16) | bp->rx_ticks); + + REG_WR(bp, BNX2_HC_COM_TICKS, + (bp->com_ticks_int << 16) | bp->com_ticks); + + REG_WR(bp, BNX2_HC_CMD_TICKS, + (bp->cmd_ticks_int << 16) | bp->cmd_ticks); + + REG_WR(bp, BNX2_HC_STATS_TICKS, bp->stats_ticks & 0xffff00); + REG_WR(bp, BNX2_HC_STAT_COLLECT_TICKS, 0xbb8); /* 3ms */ + + if (CHIP_ID(bp) == CHIP_ID_5706_A1) + REG_WR(bp, BNX2_HC_CONFIG, BNX2_HC_CONFIG_COLLECT_STATS); + else { + REG_WR(bp, BNX2_HC_CONFIG, BNX2_HC_CONFIG_RX_TMR_MODE | + BNX2_HC_CONFIG_TX_TMR_MODE | + BNX2_HC_CONFIG_COLLECT_STATS); + } + + /* Clear internal stats counters. */ + REG_WR(bp, BNX2_HC_COMMAND, BNX2_HC_COMMAND_CLR_STAT_NOW); + + REG_WR(bp, BNX2_HC_ATTN_BITS_ENABLE, STATUS_ATTN_BITS_LINK_STATE); + + if (REG_RD_IND(bp, bp->shmem_base + BNX2_PORT_FEATURE) & + BNX2_PORT_FEATURE_ASF_ENABLED) + bp->flags |= ASF_ENABLE_FLAG; + + /* Initialize the receive filter. */ + bnx2_set_rx_mode(bp->nic); + + rc = bnx2_fw_sync(bp, BNX2_DRV_MSG_DATA_WAIT2 | BNX2_DRV_MSG_CODE_RESET, + 0); + + REG_WR(bp, BNX2_MISC_ENABLE_SET_BITS, 0x5ffffff); + REG_RD(bp, BNX2_MISC_ENABLE_SET_BITS); + + udelay(20); + + bp->hc_cmd = REG_RD(bp, BNX2_HC_COMMAND); + + return rc; +} + +static void +bnx2_init_tx_ring(struct bnx2 *bp) +{ + struct tx_bd *txbd; + u32 val; + + txbd = &bp->tx_desc_ring[MAX_TX_DESC_CNT]; + + /* Etherboot lives below 4GB, so hi is always 0 */ + txbd->tx_bd_haddr_hi = 0; + txbd->tx_bd_haddr_lo = bp->tx_desc_mapping; + + bp->tx_prod = 0; + bp->tx_cons = 0; + bp->hw_tx_cons = 0; + bp->tx_prod_bseq = 0; + + val = BNX2_L2CTX_TYPE_TYPE_L2; + val |= BNX2_L2CTX_TYPE_SIZE_L2; + CTX_WR(bp, GET_CID_ADDR(TX_CID), BNX2_L2CTX_TYPE, val); + + val = BNX2_L2CTX_CMD_TYPE_TYPE_L2; + val |= 8 << 16; + CTX_WR(bp, GET_CID_ADDR(TX_CID), BNX2_L2CTX_CMD_TYPE, val); + + /* Etherboot lives below 4GB, so hi is always 0 */ + CTX_WR(bp, GET_CID_ADDR(TX_CID), BNX2_L2CTX_TBDR_BHADDR_HI, 0); + + val = (u64) bp->tx_desc_mapping & 0xffffffff; + CTX_WR(bp, GET_CID_ADDR(TX_CID), BNX2_L2CTX_TBDR_BHADDR_LO, val); +} + +static void +bnx2_init_rx_ring(struct bnx2 *bp) +{ + struct rx_bd *rxbd; + unsigned int i; + u16 prod, ring_prod; + u32 val; + + bp->rx_buf_use_size = RX_BUF_USE_SIZE; + bp->rx_buf_size = RX_BUF_SIZE; + + ring_prod = prod = bp->rx_prod = 0; + bp->rx_cons = 0; + bp->hw_rx_cons = 0; + bp->rx_prod_bseq = 0; + + memset(bnx2_bss.rx_buf, 0, sizeof(bnx2_bss.rx_buf)); + + rxbd = &bp->rx_desc_ring[0]; + for (i = 0; i < MAX_RX_DESC_CNT; i++, rxbd++) { + rxbd->rx_bd_len = bp->rx_buf_use_size; + rxbd->rx_bd_flags = RX_BD_FLAGS_START | RX_BD_FLAGS_END; + } + rxbd->rx_bd_haddr_hi = 0; + rxbd->rx_bd_haddr_lo = (u64) bp->rx_desc_mapping & 0xffffffff; + + val = BNX2_L2CTX_CTX_TYPE_CTX_BD_CHN_TYPE_VALUE; + val |= BNX2_L2CTX_CTX_TYPE_SIZE_L2; + val |= 0x02 << 8; + CTX_WR(bp, GET_CID_ADDR(RX_CID), BNX2_L2CTX_CTX_TYPE, val); + + /* Etherboot doesn't use memory above 4GB, so this is always 0 */ + CTX_WR(bp, GET_CID_ADDR(RX_CID), BNX2_L2CTX_NX_BDHADDR_HI, 0); + + val = bp->rx_desc_mapping & 0xffffffff; + CTX_WR(bp, GET_CID_ADDR(RX_CID), BNX2_L2CTX_NX_BDHADDR_LO, val); + + for (i = 0; (int) i < bp->rx_ring_size; i++) { + rxbd = &bp->rx_desc_ring[RX_RING_IDX(ring_prod)]; + rxbd->rx_bd_haddr_hi = 0; + rxbd->rx_bd_haddr_lo = virt_to_bus(&bnx2_bss.rx_buf[ring_prod][0]); + bp->rx_prod_bseq += bp->rx_buf_use_size; + prod = NEXT_RX_BD(prod); + ring_prod = RX_RING_IDX(prod); + } + bp->rx_prod = prod; + + REG_WR16(bp, MB_RX_CID_ADDR + BNX2_L2CTX_HOST_BDIDX, bp->rx_prod); + + REG_WR(bp, MB_RX_CID_ADDR + BNX2_L2CTX_HOST_BSEQ, bp->rx_prod_bseq); +} + +static int +bnx2_reset_nic(struct bnx2 *bp, u32 reset_code) +{ + int rc; + + rc = bnx2_reset_chip(bp, reset_code); + if (rc) { + return rc; + } + + bnx2_init_chip(bp); + bnx2_init_tx_ring(bp); + bnx2_init_rx_ring(bp); + return 0; +} + +static int +bnx2_init_nic(struct bnx2 *bp) +{ + int rc; + + if ((rc = bnx2_reset_nic(bp, BNX2_DRV_MSG_CODE_RESET)) != 0) + return rc; + + bnx2_init_phy(bp); + bnx2_set_link(bp); + return 0; +} + +static int +bnx2_init_board(struct pci_device *pdev, struct nic *nic) +{ + unsigned long bnx2reg_base, bnx2reg_len; + struct bnx2 *bp = &bnx2; + int rc; + u32 reg; + + bp->flags = 0; + bp->phy_flags = 0; + + /* enable device (incl. PCI PM wakeup), and bus-mastering */ + adjust_pci_device(pdev); + + nic->ioaddr = pdev->ioaddr & ~3; + nic->irqno = 0; + + rc = 0; + bp->pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); + if (bp->pm_cap == 0) { + printf("Cannot find power management capability, aborting.\n"); + rc = -EIO; + goto err_out_disable; + } + + bp->pcix_cap = pci_find_capability(pdev, PCI_CAP_ID_PCIX); + if (bp->pcix_cap == 0) { + printf("Cannot find PCIX capability, aborting.\n"); + rc = -EIO; + goto err_out_disable; + } + + bp->pdev = pdev; + bp->nic = nic; + + bnx2reg_base = pci_bar_start(pdev, PCI_BASE_ADDRESS_0); + bnx2reg_len = MB_GET_CID_ADDR(17); + + bp->regview = ioremap(bnx2reg_base, bnx2reg_len); + + if (!bp->regview) { + printf("Cannot map register space, aborting.\n"); + rc = -EIO; + goto err_out_disable; + } + + /* Configure byte swap and enable write to the reg_window registers. + * Rely on CPU to do target byte swapping on big endian systems + * The chip's target access swapping will not swap all accesses + */ + pci_write_config_dword(bp->pdev, BNX2_PCICFG_MISC_CONFIG, + BNX2_PCICFG_MISC_CONFIG_REG_WINDOW_ENA | + BNX2_PCICFG_MISC_CONFIG_TARGET_MB_WORD_SWAP); + + bnx2_set_power_state_0(bp); + + bp->chip_id = REG_RD(bp, BNX2_MISC_ID); + + /* Get bus information. */ + reg = REG_RD(bp, BNX2_PCICFG_MISC_STATUS); + if (reg & BNX2_PCICFG_MISC_STATUS_PCIX_DET) { + u32 clkreg; + + bp->flags |= PCIX_FLAG; + + clkreg = REG_RD(bp, BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS); + + clkreg &= BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET; + switch (clkreg) { + case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_133MHZ: + bp->bus_speed_mhz = 133; + break; + + case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_95MHZ: + bp->bus_speed_mhz = 100; + break; + + case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_66MHZ: + case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_80MHZ: + bp->bus_speed_mhz = 66; + break; + + case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_48MHZ: + case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_55MHZ: + bp->bus_speed_mhz = 50; + break; + + case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_LOW: + case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_32MHZ: + case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_38MHZ: + bp->bus_speed_mhz = 33; + break; + } + } + else { + if (reg & BNX2_PCICFG_MISC_STATUS_M66EN) + bp->bus_speed_mhz = 66; + else + bp->bus_speed_mhz = 33; + } + + if (reg & BNX2_PCICFG_MISC_STATUS_32BIT_DET) + bp->flags |= PCI_32BIT_FLAG; + + /* 5706A0 may falsely detect SERR and PERR. */ + if (CHIP_ID(bp) == CHIP_ID_5706_A0) { + reg = REG_RD(bp, PCI_COMMAND); + reg &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY); + REG_WR(bp, PCI_COMMAND, reg); + } + else if ((CHIP_ID(bp) == CHIP_ID_5706_A1) && + !(bp->flags & PCIX_FLAG)) { + + printf("5706 A1 can only be used in a PCIX bus, aborting.\n"); + goto err_out_disable; + } + + bnx2_init_nvram(bp); + + reg = REG_RD_IND(bp, BNX2_SHM_HDR_SIGNATURE); + + if ((reg & BNX2_SHM_HDR_SIGNATURE_SIG_MASK) == + BNX2_SHM_HDR_SIGNATURE_SIG) + bp->shmem_base = REG_RD_IND(bp, BNX2_SHM_HDR_ADDR_0); + else + bp->shmem_base = HOST_VIEW_SHMEM_BASE; + + /* Get the permanent MAC address. First we need to make sure the + * firmware is actually running. + */ + reg = REG_RD_IND(bp, bp->shmem_base + BNX2_DEV_INFO_SIGNATURE); + + if ((reg & BNX2_DEV_INFO_SIGNATURE_MAGIC_MASK) != + BNX2_DEV_INFO_SIGNATURE_MAGIC) { + printf("Firmware not running, aborting.\n"); + rc = -ENODEV; + goto err_out_disable; + } + + bp->fw_ver = REG_RD_IND(bp, bp->shmem_base + BNX2_DEV_INFO_BC_REV); + + reg = REG_RD_IND(bp, bp->shmem_base + BNX2_PORT_HW_CFG_MAC_UPPER); + bp->mac_addr[0] = (u8) (reg >> 8); + bp->mac_addr[1] = (u8) reg; + + reg = REG_RD_IND(bp, bp->shmem_base + BNX2_PORT_HW_CFG_MAC_LOWER); + bp->mac_addr[2] = (u8) (reg >> 24); + bp->mac_addr[3] = (u8) (reg >> 16); + bp->mac_addr[4] = (u8) (reg >> 8); + bp->mac_addr[5] = (u8) reg; + + bp->tx_ring_size = MAX_TX_DESC_CNT; + bp->rx_ring_size = RX_BUF_CNT; + bp->rx_max_ring_idx = MAX_RX_DESC_CNT; + + bp->rx_offset = RX_OFFSET; + + bp->tx_quick_cons_trip_int = 20; + bp->tx_quick_cons_trip = 20; + bp->tx_ticks_int = 80; + bp->tx_ticks = 80; + + bp->rx_quick_cons_trip_int = 6; + bp->rx_quick_cons_trip = 6; + bp->rx_ticks_int = 18; + bp->rx_ticks = 18; + + bp->stats_ticks = 1000000 & 0xffff00; + + bp->phy_addr = 1; + + /* No need for WOL support in Etherboot */ + bp->flags |= NO_WOL_FLAG; + + /* Disable WOL support if we are running on a SERDES chip. */ + if (CHIP_BOND_ID(bp) & CHIP_BOND_ID_SERDES_BIT) { + bp->phy_flags |= PHY_SERDES_FLAG; + if (CHIP_NUM(bp) == CHIP_NUM_5708) { + bp->phy_addr = 2; + reg = REG_RD_IND(bp, bp->shmem_base + + BNX2_SHARED_HW_CFG_CONFIG); + if (reg & BNX2_SHARED_HW_CFG_PHY_2_5G) + bp->phy_flags |= PHY_2_5G_CAPABLE_FLAG; + } + } + + if (CHIP_ID(bp) == CHIP_ID_5706_A0) { + bp->tx_quick_cons_trip_int = + bp->tx_quick_cons_trip; + bp->tx_ticks_int = bp->tx_ticks; + bp->rx_quick_cons_trip_int = + bp->rx_quick_cons_trip; + bp->rx_ticks_int = bp->rx_ticks; + bp->comp_prod_trip_int = bp->comp_prod_trip; + bp->com_ticks_int = bp->com_ticks; + bp->cmd_ticks_int = bp->cmd_ticks; + } + + bp->autoneg = AUTONEG_SPEED | AUTONEG_FLOW_CTRL; + bp->req_line_speed = 0; + if (bp->phy_flags & PHY_SERDES_FLAG) { + bp->advertising = ETHTOOL_ALL_FIBRE_SPEED | ADVERTISED_Autoneg; + + reg = REG_RD_IND(bp, bp->shmem_base + BNX2_PORT_HW_CFG_CONFIG); + reg &= BNX2_PORT_HW_CFG_CFG_DFLT_LINK_MASK; + if (reg == BNX2_PORT_HW_CFG_CFG_DFLT_LINK_1G) { + bp->autoneg = 0; + bp->req_line_speed = bp->line_speed = SPEED_1000; + bp->req_duplex = DUPLEX_FULL; + } + } + else { + bp->advertising = ETHTOOL_ALL_COPPER_SPEED | ADVERTISED_Autoneg; + } + + bp->req_flow_ctrl = FLOW_CTRL_RX | FLOW_CTRL_TX; + + /* Disable driver heartbeat checking */ + REG_WR_IND(bp, bp->shmem_base + BNX2_DRV_PULSE_MB, + BNX2_DRV_MSG_DATA_PULSE_CODE_ALWAYS_ALIVE); + REG_RD_IND(bp, bp->shmem_base + BNX2_DRV_PULSE_MB); + + return 0; + +err_out_disable: + bnx2_disable(nic); + + return rc; +} + +static void +bnx2_transmit(struct nic *nic, const char *dst_addr, + unsigned int type, unsigned int size, const char *packet) +{ + /* Sometimes the nic will be behind by a frame. Using two transmit + * buffers prevents us from timing out in that case. + */ + static struct eth_frame { + uint8_t dst_addr[ETH_ALEN]; + uint8_t src_addr[ETH_ALEN]; + uint16_t type; + uint8_t data [ETH_FRAME_LEN - ETH_HLEN]; + } frame[2]; + static int frame_idx = 0; + + /* send the packet to destination */ + struct tx_bd *txbd; + struct bnx2 *bp = &bnx2; + u16 prod, ring_prod; + u16 hw_cons; + int i = 0; + + prod = bp->tx_prod; + ring_prod = TX_RING_IDX(prod); + hw_cons = bp->status_blk->status_tx_quick_consumer_index0; + if ((hw_cons & MAX_TX_DESC_CNT) == MAX_TX_DESC_CNT) { + hw_cons++; + } + + while((hw_cons != prod) && (hw_cons != (PREV_TX_BD(prod)))) { + mdelay(10); /* give the nic a chance */ + //poll_interruptions(); + if (++i > 500) { /* timeout 5s for transmit */ + printf("transmit timed out\n"); + bnx2_disable(bp->nic); + bnx2_init_board(bp->pdev, bp->nic); + return; + } + } + if (i != 0) { + printf("#"); + } + + /* Copy the packet to the our local buffer */ + memcpy(&frame[frame_idx].dst_addr, dst_addr, ETH_ALEN); + memcpy(&frame[frame_idx].src_addr, nic->node_addr, ETH_ALEN); + frame[frame_idx].type = htons(type); + memset(&frame[frame_idx].data, 0, sizeof(frame[frame_idx].data)); + memcpy(&frame[frame_idx].data, packet, size); + + /* Setup the ring buffer entry to transmit */ + txbd = &bp->tx_desc_ring[ring_prod]; + txbd->tx_bd_haddr_hi = 0; /* Etherboot runs under 4GB */ + txbd->tx_bd_haddr_lo = virt_to_bus(&frame[frame_idx]); + txbd->tx_bd_mss_nbytes = (size + ETH_HLEN); + txbd->tx_bd_vlan_tag_flags = TX_BD_FLAGS_START | TX_BD_FLAGS_END; + + /* Advance to the next entry */ + prod = NEXT_TX_BD(prod); + frame_idx ^= 1; + + bp->tx_prod_bseq += (size + ETH_HLEN); + + REG_WR16(bp, MB_TX_CID_ADDR + BNX2_L2CTX_TX_HOST_BIDX, prod); + REG_WR(bp, MB_TX_CID_ADDR + BNX2_L2CTX_TX_HOST_BSEQ, bp->tx_prod_bseq); + + wmb(); + + bp->tx_prod = prod; +} + +static int +bnx2_poll_link(struct bnx2 *bp) +{ + u32 new_link_state, old_link_state, emac_status; + + new_link_state = bp->status_blk->status_attn_bits & + STATUS_ATTN_BITS_LINK_STATE; + + old_link_state = bp->status_blk->status_attn_bits_ack & + STATUS_ATTN_BITS_LINK_STATE; + + if (!new_link_state && !old_link_state) { + /* For some reason the card doesn't always update the link + * status bits properly. Kick the stupid thing and try again. + */ + u32 bmsr; + + bnx2_read_phy(bp, MII_BMSR, &bmsr); + bnx2_read_phy(bp, MII_BMSR, &bmsr); + + if ((bp->phy_flags & PHY_SERDES_FLAG) && + (CHIP_NUM(bp) == CHIP_NUM_5706)) { + REG_RD(bp, BNX2_EMAC_STATUS); + } + + new_link_state = bp->status_blk->status_attn_bits & + STATUS_ATTN_BITS_LINK_STATE; + + old_link_state = bp->status_blk->status_attn_bits_ack & + STATUS_ATTN_BITS_LINK_STATE; + + /* Okay, for some reason the above doesn't work with some + * switches (like HP ProCurve). If the above doesn't work, + * check the MAC directly to see if we have a link. Perhaps we + * should always check the MAC instead probing the MII. + */ + if (!new_link_state && !old_link_state) { + emac_status = REG_RD(bp, BNX2_EMAC_STATUS); + if (emac_status & BNX2_EMAC_STATUS_LINK_CHANGE) { + /* Acknowledge the link change */ + REG_WR(bp, BNX2_EMAC_STATUS, BNX2_EMAC_STATUS_LINK_CHANGE); + } else if (emac_status & BNX2_EMAC_STATUS_LINK) { + new_link_state = !old_link_state; + } + } + + } + + if (new_link_state != old_link_state) { + if (new_link_state) { + REG_WR(bp, BNX2_PCICFG_STATUS_BIT_SET_CMD, + STATUS_ATTN_BITS_LINK_STATE); + } + else { + REG_WR(bp, BNX2_PCICFG_STATUS_BIT_CLEAR_CMD, + STATUS_ATTN_BITS_LINK_STATE); + } + + bnx2_set_link(bp); + + /* This is needed to take care of transient status + * during link changes. + */ + + REG_WR(bp, BNX2_HC_COMMAND, + bp->hc_cmd | BNX2_HC_COMMAND_COAL_NOW_WO_INT); + REG_RD(bp, BNX2_HC_COMMAND); + + } + + return bp->link_up; +} + +static int +bnx2_poll(struct nic* nic, int retrieve) +{ + struct bnx2 *bp = &bnx2; + struct rx_bd *cons_bd, *prod_bd; + u16 hw_cons, sw_cons, sw_ring_cons, sw_prod, sw_ring_prod; + struct l2_fhdr *rx_hdr; + int result = 0; + unsigned int len; + unsigned char *data; + u32 status; + +#if 0 + if ((bp->status_blk->status_idx == bp->last_status_idx) && + (REG_RD(bp, BNX2_PCICFG_MISC_STATUS) & + BNX2_PCICFG_MISC_STATUS_INTA_VALUE)) { + + bp->last_status_idx = bp->status_blk->status_idx; + REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, + BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | + BNX2_PCICFG_INT_ACK_CMD_MASK_INT | + bp->last_status_idx); + return 0; + } +#endif + + if ((bp->status_blk->status_rx_quick_consumer_index0 != bp->rx_cons) && !retrieve) + return 1; + + if (bp->status_blk->status_rx_quick_consumer_index0 != bp->rx_cons) { + + hw_cons = bp->hw_rx_cons = bp->status_blk->status_rx_quick_consumer_index0; + if ((hw_cons & MAX_RX_DESC_CNT) == MAX_RX_DESC_CNT) { + hw_cons++; + } + sw_cons = bp->rx_cons; + sw_prod = bp->rx_prod; + + rmb(); + if (sw_cons != hw_cons) { + + sw_ring_cons = RX_RING_IDX(sw_cons); + sw_ring_prod = RX_RING_IDX(sw_prod); + + data = bus_to_virt(bp->rx_desc_ring[sw_ring_cons].rx_bd_haddr_lo); + + rx_hdr = (struct l2_fhdr *)data; + len = rx_hdr->l2_fhdr_pkt_len - 4; + if ((len > (ETH_MAX_MTU + ETH_HLEN)) || + ((status = rx_hdr->l2_fhdr_status) & + (L2_FHDR_ERRORS_BAD_CRC | + L2_FHDR_ERRORS_PHY_DECODE | + L2_FHDR_ERRORS_ALIGNMENT | + L2_FHDR_ERRORS_TOO_SHORT | + L2_FHDR_ERRORS_GIANT_FRAME))) { + result = 0; + } + else + { + nic->packetlen = len; + memcpy(nic->packet, data + bp->rx_offset, len); + result = 1; + } + + /* Reuse the buffer */ + bp->rx_prod_bseq += bp->rx_buf_use_size; + if (sw_cons != sw_prod) { + cons_bd = &bp->rx_desc_ring[sw_ring_cons]; + prod_bd = &bp->rx_desc_ring[sw_ring_prod]; + prod_bd->rx_bd_haddr_hi = 0; /* Etherboot runs under 4GB */ + prod_bd->rx_bd_haddr_lo = cons_bd->rx_bd_haddr_lo; + } + + sw_cons = NEXT_RX_BD(sw_cons); + sw_prod = NEXT_RX_BD(sw_prod); + + } + + bp->rx_cons = sw_cons; + bp->rx_prod = sw_prod; + + REG_WR16(bp, MB_RX_CID_ADDR + BNX2_L2CTX_HOST_BDIDX, bp->rx_prod); + + REG_WR(bp, MB_RX_CID_ADDR + BNX2_L2CTX_HOST_BSEQ, bp->rx_prod_bseq); + + wmb(); + + } + + bnx2_poll_link(bp); + +#if 0 + bp->last_status_idx = bp->status_blk->status_idx; + rmb(); + + REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, + BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | + BNX2_PCICFG_INT_ACK_CMD_MASK_INT | + bp->last_status_idx); + + REG_WR(bp, BNX2_HC_COMMAND, bp->hc_cmd | BNX2_HC_COMMAND_COAL_NOW_WO_INT); +#endif + + return result; +} + +static void +bnx2_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE: break; + case ENABLE: break; + case FORCE: break; + } +} + +static struct nic_operations bnx2_operations = { + .connect = dummy_connect, + .poll = bnx2_poll, + .transmit = bnx2_transmit, + .irq = bnx2_irq, +}; + +static int +bnx2_probe(struct nic *nic, struct pci_device *pdev) +{ + struct bnx2 *bp = &bnx2; + int i, rc; + + if (pdev == 0) + return 0; + + memset(bp, 0, sizeof(*bp)); + + rc = bnx2_init_board(pdev, nic); + if (rc < 0) { + return 0; + } + + /* + nic->disable = bnx2_disable; + nic->transmit = bnx2_transmit; + nic->poll = bnx2_poll; + nic->irq = bnx2_irq; + */ + + nic->nic_op = &bnx2_operations; + + memcpy(nic->node_addr, bp->mac_addr, ETH_ALEN); + printf("Ethernet addr: %s\n", eth_ntoa( nic->node_addr ) ); + printf("Broadcom NetXtreme II (%c%d) PCI%s %s %dMHz\n", + (int) ((CHIP_ID(bp) & 0xf000) >> 12) + 'A', + (int) ((CHIP_ID(bp) & 0x0ff0) >> 4), + ((bp->flags & PCIX_FLAG) ? "-X" : ""), + ((bp->flags & PCI_32BIT_FLAG) ? "32-bit" : "64-bit"), + bp->bus_speed_mhz); + + bnx2_set_power_state_0(bp); + bnx2_disable_int(bp); + + bnx2_alloc_mem(bp); + + rc = bnx2_init_nic(bp); + if (rc) { + return 0; + } + + bnx2_poll_link(bp); + for(i = 0; !bp->link_up && (i < VALID_LINK_TIMEOUT*100); i++) { + mdelay(1); + bnx2_poll_link(bp); + } +#if 1 + if (!bp->link_up){ + printf("Valid link not established\n"); + goto err_out_disable; + } +#endif + + return 1; + +err_out_disable: + bnx2_disable(nic); + return 0; +} + +static struct pci_device_id bnx2_nics[] = { + PCI_ROM(0x14e4, 0x164a, "bnx2-5706", "Broadcom NetXtreme II BCM5706"), + PCI_ROM(0x14e4, 0x164c, "bnx2-5708", "Broadcom NetXtreme II BCM5708"), + PCI_ROM(0x14e4, 0x16aa, "bnx2-5706S", "Broadcom NetXtreme II BCM5706S"), + PCI_ROM(0x14e4, 0x16ac, "bnx2-5708S", "Broadcom NetXtreme II BCM5708S"), +}; + +PCI_DRIVER ( bnx2_driver, bnx2_nics, PCI_NO_CLASS ); + +DRIVER ( "BNX2", nic_driver, pci_driver, bnx2_driver, bnx2_probe, bnx2_disable ); + +/* +static struct pci_driver bnx2_driver __pci_driver = { + .type = NIC_DRIVER, + .name = "BNX2", + .probe = bnx2_probe, + .ids = bnx2_nics, + .id_count = sizeof(bnx2_nics)/sizeof(bnx2_nics[0]), + .class = 0, +}; +*/ diff --git a/gpxe/src/drivers/net/bnx2.h b/gpxe/src/drivers/net/bnx2.h new file mode 100644 index 0000000..57d5fee --- /dev/null +++ b/gpxe/src/drivers/net/bnx2.h @@ -0,0 +1,4704 @@ +/* bnx2.h: Broadcom NX2 network driver. + * + * Copyright (c) 2004, 2005, 2006 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + * + * Written by: Michael Chan (mchan@broadcom.com) + */ + + +#ifndef BNX2_H +#define BNX2_H + +#define L1_CACHE_BYTES 128 /* Rough approximaition of the cache line size */ +#define L1_CACHE_ALIGN(X) (((X) + L1_CACHE_BYTES-1)&~(L1_CACHE_BYTES -1)) + +typedef unsigned long dma_addr_t; + +/* From pci.h */ +typedef int pci_power_t; + +#define PCI_D0 ((pci_power_t) 0) +#define PCI_D1 ((pci_power_t) 1) +#define PCI_D2 ((pci_power_t) 2) +#define PCI_D3hot ((pci_power_t) 3) +#define PCI_D3cold ((pci_power_t) 4) +#define PCI_UNKNOWN ((pci_power_t) 5) +#define PCI_POWER_ERROR ((pci_power_t) -1) + +/* From pci_regs.h */ + +#define PCI_CAP_ID_PCIX 0x07 /* PCI-X */ +#define PCI_X_CMD 2 /* Modes & Features */ +#define PCI_X_CMD_ERO 0x0002 /* Enable Relaxed Ordering */ + +/* From mii.h */ + +/* Indicates what features are advertised by the interface. */ +#define ADVERTISED_10baseT_Half (1 << 0) +#define ADVERTISED_10baseT_Full (1 << 1) +#define ADVERTISED_100baseT_Half (1 << 2) +#define ADVERTISED_100baseT_Full (1 << 3) +#define ADVERTISED_1000baseT_Half (1 << 4) +#define ADVERTISED_1000baseT_Full (1 << 5) +#define ADVERTISED_Autoneg (1 << 6) +#define ADVERTISED_TP (1 << 7) +#define ADVERTISED_AUI (1 << 8) +#define ADVERTISED_MII (1 << 9) +#define ADVERTISED_FIBRE (1 << 10) +#define ADVERTISED_BNC (1 << 11) + +/* The following are all involved in forcing a particular link + * mode for the device for setting things. When getting the + * devices settings, these indicate the current mode and whether + * it was foced up into this mode or autonegotiated. + */ + +/* Duplex, half or full. */ +#define DUPLEX_HALF 0x00 +#define DUPLEX_FULL 0x01 +#define DUPLEX_INVALID 0x02 + +/* Which connector port. */ +#define PORT_TP 0x00 +#define PORT_AUI 0x01 +#define PORT_MII 0x02 +#define PORT_FIBRE 0x03 +#define PORT_BNC 0x04 + +/* Which tranceiver to use. */ +#define XCVR_INTERNAL 0x00 +#define XCVR_EXTERNAL 0x01 +#define XCVR_DUMMY1 0x02 +#define XCVR_DUMMY2 0x03 +#define XCVR_DUMMY3 0x04 + +/* Enable or disable autonegotiation. If this is set to enable, + * the forced link modes above are completely ignored. + */ +#define AUTONEG_DISABLE 0x00 +#define AUTONEG_ENABLE 0x01 + +/* Wake-On-Lan options. */ +#define WAKE_PHY (1 << 0) +#define WAKE_UCAST (1 << 1) +#define WAKE_MCAST (1 << 2) +#define WAKE_BCAST (1 << 3) +#define WAKE_ARP (1 << 4) +#define WAKE_MAGIC (1 << 5) +#define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */ + +/* Generic MII registers. */ + +#define MII_BMCR 0x00 /* Basic mode control register */ +#define MII_BMSR 0x01 /* Basic mode status register */ +#define MII_PHYSID1 0x02 /* PHYS ID 1 */ +#define MII_PHYSID2 0x03 /* PHYS ID 2 */ +#define MII_ADVERTISE 0x04 /* Advertisement control reg */ +#define MII_LPA 0x05 /* Link partner ability reg */ +#define MII_EXPANSION 0x06 /* Expansion register */ +#define MII_CTRL1000 0x09 /* 1000BASE-T control */ +#define MII_STAT1000 0x0a /* 1000BASE-T status */ +#define MII_DCOUNTER 0x12 /* Disconnect counter */ +#define MII_FCSCOUNTER 0x13 /* False carrier counter */ +#define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */ +#define MII_RERRCOUNTER 0x15 /* Receive error counter */ +#define MII_SREVISION 0x16 /* Silicon revision */ +#define MII_RESV1 0x17 /* Reserved... */ +#define MII_LBRERROR 0x18 /* Lpback, rx, bypass error */ +#define MII_PHYADDR 0x19 /* PHY address */ +#define MII_RESV2 0x1a /* Reserved... */ +#define MII_TPISTATUS 0x1b /* TPI status for 10mbps */ +#define MII_NCONFIG 0x1c /* Network interface config */ + +/* Basic mode control register. */ +#define BMCR_RESV 0x007f /* Unused... */ +#define BMCR_SPEED1000 0x0040 /* MSB of Speed (1000) */ +#define BMCR_CTST 0x0080 /* Collision test */ +#define BMCR_FULLDPLX 0x0100 /* Full duplex */ +#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */ +#define BMCR_ISOLATE 0x0400 /* Disconnect DP83840 from MII */ +#define BMCR_PDOWN 0x0800 /* Powerdown the DP83840 */ +#define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */ +#define BMCR_SPEED100 0x2000 /* Select 100Mbps */ +#define BMCR_LOOPBACK 0x4000 /* TXD loopback bits */ +#define BMCR_RESET 0x8000 /* Reset the DP83840 */ + +/* Basic mode status register. */ +#define BMSR_ERCAP 0x0001 /* Ext-reg capability */ +#define BMSR_JCD 0x0002 /* Jabber detected */ +#define BMSR_LSTATUS 0x0004 /* Link status */ +#define BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */ +#define BMSR_RFAULT 0x0010 /* Remote fault detected */ +#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */ +#define BMSR_RESV 0x07c0 /* Unused... */ +#define BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */ +#define BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */ +#define BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */ +#define BMSR_100FULL 0x4000 /* Can do 100mbps, full-duplex */ +#define BMSR_100BASE4 0x8000 /* Can do 100mbps, 4k packets */ + +/* Advertisement control register. */ +#define ADVERTISE_SLCT 0x001f /* Selector bits */ +#define ADVERTISE_CSMA 0x0001 /* Only selector supported */ +#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ +#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ +#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ +#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ +#define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */ +#define ADVERTISE_RESV 0x1c00 /* Unused... */ +#define ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */ +#define ADVERTISE_LPACK 0x4000 /* Ack link partners response */ +#define ADVERTISE_NPAGE 0x8000 /* Next page bit */ +#define ADVERTISE_1000XFULL 0x0020 /* Try for 1000BASE-X full-duplex */ +#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ +#define ADVERTISE_1000XHALF 0x0040 /* Try for 1000BASE-X half-duplex */ +#define ADVERTISE_1000XPAUSE 0x0080 /* Try for 1000BASE-X pause */ +#define ADVERTISE_PAUSE_CAP 0x0400 /* Try for pause */ +#define ADVERTISE_1000XPSE_ASYM 0x0100 /* Try for 1000BASE-X asym pause */ +#define ADVERTISE_PAUSE_ASYM 0x0800 /* Try for asymetric pause */ + +#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | \ + ADVERTISE_CSMA) +#define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \ + ADVERTISE_100HALF | ADVERTISE_100FULL) + +/* Link partner ability register. */ +#define LPA_SLCT 0x001f /* Same as advertise selector */ +#define LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */ +#define LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */ +#define LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */ +#define LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */ +#define LPA_100BASE4 0x0200 /* Can do 100mbps 4k packets */ +#define LPA_RESV 0x1c00 /* Unused... */ +#define LPA_RFAULT 0x2000 /* Link partner faulted */ +#define LPA_LPACK 0x4000 /* Link partner acked us */ +#define LPA_NPAGE 0x8000 /* Next page bit */ + +#define LPA_DUPLEX (LPA_10FULL | LPA_100FULL) +#define LPA_100 (LPA_100FULL | LPA_100HALF | LPA_100BASE4) + +/* Expansion register for auto-negotiation. */ +#define EXPANSION_NWAY 0x0001 /* Can do N-way auto-nego */ +#define EXPANSION_LCWP 0x0002 /* Got new RX page code word */ +#define EXPANSION_ENABLENPAGE 0x0004 /* This enables npage words */ +#define EXPANSION_NPCAPABLE 0x0008 /* Link partner supports npage */ +#define EXPANSION_MFAULTS 0x0010 /* Multiple faults detected */ +#define EXPANSION_RESV 0xffe0 /* Unused... */ + +/* 1000BASE-T Control register */ +#define ADVERTISE_1000FULL 0x0200 /* Advertise 1000BASE-T full duplex */ +#define ADVERTISE_1000HALF 0x0100 /* Advertise 1000BASE-T half duplex */ + +/* N-way test register. */ +#define NWAYTEST_RESV1 0x00ff /* Unused... */ +#define NWAYTEST_LOOPBACK 0x0100 /* Enable loopback for N-way */ +#define NWAYTEST_RESV2 0xfe00 /* Unused... */ + +/* The following are all involved in forcing a particular link + * * mode for the device for setting things. When getting the + * * devices settings, these indicate the current mode and whether + * * it was foced up into this mode or autonegotiated. + * */ + +/* The forced speed, 10Mb, 100Mb, gigabit. */ +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 +#define SPEED_2500 2500 +#define SPEED_INVALID 0 /* XXX was 3 */ + + +/* Duplex, half or full. */ +#define DUPLEX_HALF 0x00 +#define DUPLEX_FULL 0x01 +#define DUPLEX_INVALID 0x02 + +/* Which connector port. */ +#define PORT_TP 0x00 +#define PORT_AUI 0x01 +#define PORT_MII 0x02 +#define PORT_FIBRE 0x03 +#define PORT_BNC 0x04 + +/* Which tranceiver to use. */ +#define XCVR_INTERNAL 0x00 +#define XCVR_EXTERNAL 0x01 +#define XCVR_DUMMY1 0x02 +#define XCVR_DUMMY2 0x03 +#define XCVR_DUMMY3 0x04 + +/* Enable or disable autonegotiation. If this is set to enable, + * * the forced link modes above are completely ignored. + * */ +#define AUTONEG_DISABLE 0x00 +#define AUTONEG_ENABLE 0x01 + +/* Wake-On-Lan options. */ +#define WAKE_PHY (1 << 0) +#define WAKE_UCAST (1 << 1) +#define WAKE_MCAST (1 << 2) +#define WAKE_BCAST (1 << 3) +#define WAKE_ARP (1 << 4) +#define WAKE_MAGIC (1 << 5) +#define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */ + +/* Hardware data structures and register definitions automatically + * generated from RTL code. Do not modify. + */ + +/* + * tx_bd definition + */ +struct tx_bd { + u32 tx_bd_haddr_hi; + u32 tx_bd_haddr_lo; + u32 tx_bd_mss_nbytes; + u32 tx_bd_vlan_tag_flags; + #define TX_BD_FLAGS_CONN_FAULT (1<<0) + #define TX_BD_FLAGS_TCP_UDP_CKSUM (1<<1) + #define TX_BD_FLAGS_IP_CKSUM (1<<2) + #define TX_BD_FLAGS_VLAN_TAG (1<<3) + #define TX_BD_FLAGS_COAL_NOW (1<<4) + #define TX_BD_FLAGS_DONT_GEN_CRC (1<<5) + #define TX_BD_FLAGS_END (1<<6) + #define TX_BD_FLAGS_START (1<<7) + #define TX_BD_FLAGS_SW_OPTION_WORD (0x1f<<8) + #define TX_BD_FLAGS_SW_FLAGS (1<<13) + #define TX_BD_FLAGS_SW_SNAP (1<<14) + #define TX_BD_FLAGS_SW_LSO (1<<15) + +}; + + +/* + * rx_bd definition + */ +struct rx_bd { + u32 rx_bd_haddr_hi; + u32 rx_bd_haddr_lo; + u32 rx_bd_len; + u32 rx_bd_flags; + #define RX_BD_FLAGS_NOPUSH (1<<0) + #define RX_BD_FLAGS_DUMMY (1<<1) + #define RX_BD_FLAGS_END (1<<2) + #define RX_BD_FLAGS_START (1<<3) + +}; + + +/* + * status_block definition + */ +struct status_block { + u32 status_attn_bits; + #define STATUS_ATTN_BITS_LINK_STATE (1L<<0) + #define STATUS_ATTN_BITS_TX_SCHEDULER_ABORT (1L<<1) + #define STATUS_ATTN_BITS_TX_BD_READ_ABORT (1L<<2) + #define STATUS_ATTN_BITS_TX_BD_CACHE_ABORT (1L<<3) + #define STATUS_ATTN_BITS_TX_PROCESSOR_ABORT (1L<<4) + #define STATUS_ATTN_BITS_TX_DMA_ABORT (1L<<5) + #define STATUS_ATTN_BITS_TX_PATCHUP_ABORT (1L<<6) + #define STATUS_ATTN_BITS_TX_ASSEMBLER_ABORT (1L<<7) + #define STATUS_ATTN_BITS_RX_PARSER_MAC_ABORT (1L<<8) + #define STATUS_ATTN_BITS_RX_PARSER_CATCHUP_ABORT (1L<<9) + #define STATUS_ATTN_BITS_RX_MBUF_ABORT (1L<<10) + #define STATUS_ATTN_BITS_RX_LOOKUP_ABORT (1L<<11) + #define STATUS_ATTN_BITS_RX_PROCESSOR_ABORT (1L<<12) + #define STATUS_ATTN_BITS_RX_V2P_ABORT (1L<<13) + #define STATUS_ATTN_BITS_RX_BD_CACHE_ABORT (1L<<14) + #define STATUS_ATTN_BITS_RX_DMA_ABORT (1L<<15) + #define STATUS_ATTN_BITS_COMPLETION_ABORT (1L<<16) + #define STATUS_ATTN_BITS_HOST_COALESCE_ABORT (1L<<17) + #define STATUS_ATTN_BITS_MAILBOX_QUEUE_ABORT (1L<<18) + #define STATUS_ATTN_BITS_CONTEXT_ABORT (1L<<19) + #define STATUS_ATTN_BITS_CMD_SCHEDULER_ABORT (1L<<20) + #define STATUS_ATTN_BITS_CMD_PROCESSOR_ABORT (1L<<21) + #define STATUS_ATTN_BITS_MGMT_PROCESSOR_ABORT (1L<<22) + #define STATUS_ATTN_BITS_MAC_ABORT (1L<<23) + #define STATUS_ATTN_BITS_TIMER_ABORT (1L<<24) + #define STATUS_ATTN_BITS_DMAE_ABORT (1L<<25) + #define STATUS_ATTN_BITS_FLSH_ABORT (1L<<26) + #define STATUS_ATTN_BITS_GRC_ABORT (1L<<27) + #define STATUS_ATTN_BITS_PARITY_ERROR (1L<<31) + + u32 status_attn_bits_ack; +#if __BYTE_ORDER == __BIG_ENDIAN + u16 status_tx_quick_consumer_index0; + u16 status_tx_quick_consumer_index1; + u16 status_tx_quick_consumer_index2; + u16 status_tx_quick_consumer_index3; + u16 status_rx_quick_consumer_index0; + u16 status_rx_quick_consumer_index1; + u16 status_rx_quick_consumer_index2; + u16 status_rx_quick_consumer_index3; + u16 status_rx_quick_consumer_index4; + u16 status_rx_quick_consumer_index5; + u16 status_rx_quick_consumer_index6; + u16 status_rx_quick_consumer_index7; + u16 status_rx_quick_consumer_index8; + u16 status_rx_quick_consumer_index9; + u16 status_rx_quick_consumer_index10; + u16 status_rx_quick_consumer_index11; + u16 status_rx_quick_consumer_index12; + u16 status_rx_quick_consumer_index13; + u16 status_rx_quick_consumer_index14; + u16 status_rx_quick_consumer_index15; + u16 status_completion_producer_index; + u16 status_cmd_consumer_index; + u16 status_idx; + u16 status_unused; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + u16 status_tx_quick_consumer_index1; + u16 status_tx_quick_consumer_index0; + u16 status_tx_quick_consumer_index3; + u16 status_tx_quick_consumer_index2; + u16 status_rx_quick_consumer_index1; + u16 status_rx_quick_consumer_index0; + u16 status_rx_quick_consumer_index3; + u16 status_rx_quick_consumer_index2; + u16 status_rx_quick_consumer_index5; + u16 status_rx_quick_consumer_index4; + u16 status_rx_quick_consumer_index7; + u16 status_rx_quick_consumer_index6; + u16 status_rx_quick_consumer_index9; + u16 status_rx_quick_consumer_index8; + u16 status_rx_quick_consumer_index11; + u16 status_rx_quick_consumer_index10; + u16 status_rx_quick_consumer_index13; + u16 status_rx_quick_consumer_index12; + u16 status_rx_quick_consumer_index15; + u16 status_rx_quick_consumer_index14; + u16 status_cmd_consumer_index; + u16 status_completion_producer_index; + u16 status_unused; + u16 status_idx; +#endif +}; + + +/* + * statistics_block definition + */ +struct statistics_block { + u32 stat_IfHCInOctets_hi; + u32 stat_IfHCInOctets_lo; + u32 stat_IfHCInBadOctets_hi; + u32 stat_IfHCInBadOctets_lo; + u32 stat_IfHCOutOctets_hi; + u32 stat_IfHCOutOctets_lo; + u32 stat_IfHCOutBadOctets_hi; + u32 stat_IfHCOutBadOctets_lo; + u32 stat_IfHCInUcastPkts_hi; + u32 stat_IfHCInUcastPkts_lo; + u32 stat_IfHCInMulticastPkts_hi; + u32 stat_IfHCInMulticastPkts_lo; + u32 stat_IfHCInBroadcastPkts_hi; + u32 stat_IfHCInBroadcastPkts_lo; + u32 stat_IfHCOutUcastPkts_hi; + u32 stat_IfHCOutUcastPkts_lo; + u32 stat_IfHCOutMulticastPkts_hi; + u32 stat_IfHCOutMulticastPkts_lo; + u32 stat_IfHCOutBroadcastPkts_hi; + u32 stat_IfHCOutBroadcastPkts_lo; + u32 stat_emac_tx_stat_dot3statsinternalmactransmiterrors; + u32 stat_Dot3StatsCarrierSenseErrors; + u32 stat_Dot3StatsFCSErrors; + u32 stat_Dot3StatsAlignmentErrors; + u32 stat_Dot3StatsSingleCollisionFrames; + u32 stat_Dot3StatsMultipleCollisionFrames; + u32 stat_Dot3StatsDeferredTransmissions; + u32 stat_Dot3StatsExcessiveCollisions; + u32 stat_Dot3StatsLateCollisions; + u32 stat_EtherStatsCollisions; + u32 stat_EtherStatsFragments; + u32 stat_EtherStatsJabbers; + u32 stat_EtherStatsUndersizePkts; + u32 stat_EtherStatsOverrsizePkts; + u32 stat_EtherStatsPktsRx64Octets; + u32 stat_EtherStatsPktsRx65Octetsto127Octets; + u32 stat_EtherStatsPktsRx128Octetsto255Octets; + u32 stat_EtherStatsPktsRx256Octetsto511Octets; + u32 stat_EtherStatsPktsRx512Octetsto1023Octets; + u32 stat_EtherStatsPktsRx1024Octetsto1522Octets; + u32 stat_EtherStatsPktsRx1523Octetsto9022Octets; + u32 stat_EtherStatsPktsTx64Octets; + u32 stat_EtherStatsPktsTx65Octetsto127Octets; + u32 stat_EtherStatsPktsTx128Octetsto255Octets; + u32 stat_EtherStatsPktsTx256Octetsto511Octets; + u32 stat_EtherStatsPktsTx512Octetsto1023Octets; + u32 stat_EtherStatsPktsTx1024Octetsto1522Octets; + u32 stat_EtherStatsPktsTx1523Octetsto9022Octets; + u32 stat_XonPauseFramesReceived; + u32 stat_XoffPauseFramesReceived; + u32 stat_OutXonSent; + u32 stat_OutXoffSent; + u32 stat_FlowControlDone; + u32 stat_MacControlFramesReceived; + u32 stat_XoffStateEntered; + u32 stat_IfInFramesL2FilterDiscards; + u32 stat_IfInRuleCheckerDiscards; + u32 stat_IfInFTQDiscards; + u32 stat_IfInMBUFDiscards; + u32 stat_IfInRuleCheckerP4Hit; + u32 stat_CatchupInRuleCheckerDiscards; + u32 stat_CatchupInFTQDiscards; + u32 stat_CatchupInMBUFDiscards; + u32 stat_CatchupInRuleCheckerP4Hit; + u32 stat_GenStat00; + u32 stat_GenStat01; + u32 stat_GenStat02; + u32 stat_GenStat03; + u32 stat_GenStat04; + u32 stat_GenStat05; + u32 stat_GenStat06; + u32 stat_GenStat07; + u32 stat_GenStat08; + u32 stat_GenStat09; + u32 stat_GenStat10; + u32 stat_GenStat11; + u32 stat_GenStat12; + u32 stat_GenStat13; + u32 stat_GenStat14; + u32 stat_GenStat15; +}; + + +/* + * l2_fhdr definition + */ +struct l2_fhdr { + u32 l2_fhdr_status; + #define L2_FHDR_STATUS_RULE_CLASS (0x7<<0) + #define L2_FHDR_STATUS_RULE_P2 (1<<3) + #define L2_FHDR_STATUS_RULE_P3 (1<<4) + #define L2_FHDR_STATUS_RULE_P4 (1<<5) + #define L2_FHDR_STATUS_L2_VLAN_TAG (1<<6) + #define L2_FHDR_STATUS_L2_LLC_SNAP (1<<7) + #define L2_FHDR_STATUS_RSS_HASH (1<<8) + #define L2_FHDR_STATUS_IP_DATAGRAM (1<<13) + #define L2_FHDR_STATUS_TCP_SEGMENT (1<<14) + #define L2_FHDR_STATUS_UDP_DATAGRAM (1<<15) + + #define L2_FHDR_ERRORS_BAD_CRC (1<<17) + #define L2_FHDR_ERRORS_PHY_DECODE (1<<18) + #define L2_FHDR_ERRORS_ALIGNMENT (1<<19) + #define L2_FHDR_ERRORS_TOO_SHORT (1<<20) + #define L2_FHDR_ERRORS_GIANT_FRAME (1<<21) + #define L2_FHDR_ERRORS_TCP_XSUM (1<<28) + #define L2_FHDR_ERRORS_UDP_XSUM (1<<31) + + u32 l2_fhdr_hash; +#if __BYTE_ORDER == __BIG_ENDIAN + u16 l2_fhdr_pkt_len; + u16 l2_fhdr_vlan_tag; + u16 l2_fhdr_ip_xsum; + u16 l2_fhdr_tcp_udp_xsum; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + u16 l2_fhdr_vlan_tag; + u16 l2_fhdr_pkt_len; + u16 l2_fhdr_tcp_udp_xsum; + u16 l2_fhdr_ip_xsum; +#endif +}; + + +/* + * l2_context definition + */ +#define BNX2_L2CTX_TYPE 0x00000000 +#define BNX2_L2CTX_TYPE_SIZE_L2 ((0xc0/0x20)<<16) +#define BNX2_L2CTX_TYPE_TYPE (0xf<<28) +#define BNX2_L2CTX_TYPE_TYPE_EMPTY (0<<28) +#define BNX2_L2CTX_TYPE_TYPE_L2 (1<<28) + +#define BNX2_L2CTX_TX_HOST_BIDX 0x00000088 +#define BNX2_L2CTX_EST_NBD 0x00000088 +#define BNX2_L2CTX_CMD_TYPE 0x00000088 +#define BNX2_L2CTX_CMD_TYPE_TYPE (0xf<<24) +#define BNX2_L2CTX_CMD_TYPE_TYPE_L2 (0<<24) +#define BNX2_L2CTX_CMD_TYPE_TYPE_TCP (1<<24) + +#define BNX2_L2CTX_TX_HOST_BSEQ 0x00000090 +#define BNX2_L2CTX_TSCH_BSEQ 0x00000094 +#define BNX2_L2CTX_TBDR_BSEQ 0x00000098 +#define BNX2_L2CTX_TBDR_BOFF 0x0000009c +#define BNX2_L2CTX_TBDR_BIDX 0x0000009c +#define BNX2_L2CTX_TBDR_BHADDR_HI 0x000000a0 +#define BNX2_L2CTX_TBDR_BHADDR_LO 0x000000a4 +#define BNX2_L2CTX_TXP_BOFF 0x000000a8 +#define BNX2_L2CTX_TXP_BIDX 0x000000a8 +#define BNX2_L2CTX_TXP_BSEQ 0x000000ac + + +/* + * l2_bd_chain_context definition + */ +#define BNX2_L2CTX_BD_PRE_READ 0x00000000 +#define BNX2_L2CTX_CTX_SIZE 0x00000000 +#define BNX2_L2CTX_CTX_TYPE 0x00000000 +#define BNX2_L2CTX_CTX_TYPE_SIZE_L2 ((0x20/20)<<16) +#define BNX2_L2CTX_CTX_TYPE_CTX_BD_CHN_TYPE (0xf<<28) +#define BNX2_L2CTX_CTX_TYPE_CTX_BD_CHN_TYPE_UNDEFINED (0<<28) +#define BNX2_L2CTX_CTX_TYPE_CTX_BD_CHN_TYPE_VALUE (1<<28) + +#define BNX2_L2CTX_HOST_BDIDX 0x00000004 +#define BNX2_L2CTX_HOST_BSEQ 0x00000008 +#define BNX2_L2CTX_NX_BSEQ 0x0000000c +#define BNX2_L2CTX_NX_BDHADDR_HI 0x00000010 +#define BNX2_L2CTX_NX_BDHADDR_LO 0x00000014 +#define BNX2_L2CTX_NX_BDIDX 0x00000018 + + +/* + * pci_config_l definition + * offset: 0000 + */ +#define BNX2_PCICFG_MISC_CONFIG 0x00000068 +#define BNX2_PCICFG_MISC_CONFIG_TARGET_BYTE_SWAP (1L<<2) +#define BNX2_PCICFG_MISC_CONFIG_TARGET_MB_WORD_SWAP (1L<<3) +#define BNX2_PCICFG_MISC_CONFIG_CLOCK_CTL_ENA (1L<<5) +#define BNX2_PCICFG_MISC_CONFIG_TARGET_GRC_WORD_SWAP (1L<<6) +#define BNX2_PCICFG_MISC_CONFIG_REG_WINDOW_ENA (1L<<7) +#define BNX2_PCICFG_MISC_CONFIG_CORE_RST_REQ (1L<<8) +#define BNX2_PCICFG_MISC_CONFIG_CORE_RST_BSY (1L<<9) +#define BNX2_PCICFG_MISC_CONFIG_ASIC_METAL_REV (0xffL<<16) +#define BNX2_PCICFG_MISC_CONFIG_ASIC_BASE_REV (0xfL<<24) +#define BNX2_PCICFG_MISC_CONFIG_ASIC_ID (0xfL<<28) + +#define BNX2_PCICFG_MISC_STATUS 0x0000006c +#define BNX2_PCICFG_MISC_STATUS_INTA_VALUE (1L<<0) +#define BNX2_PCICFG_MISC_STATUS_32BIT_DET (1L<<1) +#define BNX2_PCICFG_MISC_STATUS_M66EN (1L<<2) +#define BNX2_PCICFG_MISC_STATUS_PCIX_DET (1L<<3) +#define BNX2_PCICFG_MISC_STATUS_PCIX_SPEED (0x3L<<4) +#define BNX2_PCICFG_MISC_STATUS_PCIX_SPEED_66 (0L<<4) +#define BNX2_PCICFG_MISC_STATUS_PCIX_SPEED_100 (1L<<4) +#define BNX2_PCICFG_MISC_STATUS_PCIX_SPEED_133 (2L<<4) +#define BNX2_PCICFG_MISC_STATUS_PCIX_SPEED_PCI_MODE (3L<<4) + +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS 0x00000070 +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET (0xfL<<0) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_32MHZ (0L<<0) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_38MHZ (1L<<0) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_48MHZ (2L<<0) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_55MHZ (3L<<0) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_66MHZ (4L<<0) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_80MHZ (5L<<0) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_95MHZ (6L<<0) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_133MHZ (7L<<0) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_LOW (0xfL<<0) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_DISABLE (1L<<6) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_ALT (1L<<7) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC (0x7L<<8) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_UNDEF (0L<<8) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_12 (1L<<8) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_6 (2L<<8) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_62 (4L<<8) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PLAY_DEAD (1L<<11) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED (0xfL<<12) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_100 (0L<<12) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_80 (1L<<12) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_50 (2L<<12) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_40 (4L<<12) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_25 (8L<<12) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_CORE_CLK_PLL_STOP (1L<<16) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_PLL_STOP (1L<<17) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_RESERVED_18 (1L<<18) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_USE_SPD_DET (1L<<19) +#define BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_RESERVED (0xfffL<<20) + +#define BNX2_PCICFG_REG_WINDOW_ADDRESS 0x00000078 +#define BNX2_PCICFG_REG_WINDOW 0x00000080 +#define BNX2_PCICFG_INT_ACK_CMD 0x00000084 +#define BNX2_PCICFG_INT_ACK_CMD_INDEX (0xffffL<<0) +#define BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID (1L<<16) +#define BNX2_PCICFG_INT_ACK_CMD_USE_INT_HC_PARAM (1L<<17) +#define BNX2_PCICFG_INT_ACK_CMD_MASK_INT (1L<<18) + +#define BNX2_PCICFG_STATUS_BIT_SET_CMD 0x00000088 +#define BNX2_PCICFG_STATUS_BIT_CLEAR_CMD 0x0000008c +#define BNX2_PCICFG_MAILBOX_QUEUE_ADDR 0x00000090 +#define BNX2_PCICFG_MAILBOX_QUEUE_DATA 0x00000094 + + +/* + * pci_reg definition + * offset: 0x400 + */ +#define BNX2_PCI_GRC_WINDOW_ADDR 0x00000400 +#define BNX2_PCI_GRC_WINDOW_ADDR_PCI_GRC_WINDOW_ADDR_VALUE (0x3ffffL<<8) + +#define BNX2_PCI_CONFIG_1 0x00000404 +#define BNX2_PCI_CONFIG_1_READ_BOUNDARY (0x7L<<8) +#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_OFF (0L<<8) +#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_16 (1L<<8) +#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_32 (2L<<8) +#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_64 (3L<<8) +#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_128 (4L<<8) +#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_256 (5L<<8) +#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_512 (6L<<8) +#define BNX2_PCI_CONFIG_1_READ_BOUNDARY_1024 (7L<<8) +#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY (0x7L<<11) +#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_OFF (0L<<11) +#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_16 (1L<<11) +#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_32 (2L<<11) +#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_64 (3L<<11) +#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_128 (4L<<11) +#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_256 (5L<<11) +#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_512 (6L<<11) +#define BNX2_PCI_CONFIG_1_WRITE_BOUNDARY_1024 (7L<<11) + +#define BNX2_PCI_CONFIG_2 0x00000408 +#define BNX2_PCI_CONFIG_2_BAR1_SIZE (0xfL<<0) +#define BNX2_PCI_CONFIG_2_BAR1_SIZE_DISABLED (0L<<0) +#define BNX2_PCI_CONFIG_2_BAR1_SIZE_64K (1L<<0) +#define BNX2_PCI_CONFIG_2_BAR1_SIZE_128K (2L<<0) +#define BNX2_PCI_CONFIG_2_BAR1_SIZE_256K (3L<<0) +#define BNX2_PCI_CONFIG_2_BAR1_SIZE_512K (4L<<0) +#define BNX2_PCI_CONFIG_2_BAR1_SIZE_1M (5L<<0) +#define BNX2_PCI_CONFIG_2_BAR1_SIZE_2M (6L<<0) +#define BNX2_PCI_CONFIG_2_BAR1_SIZE_4M (7L<<0) +#define BNX2_PCI_CONFIG_2_BAR1_SIZE_8M (8L<<0) +#define BNX2_PCI_CONFIG_2_BAR1_SIZE_16M (9L<<0) +#define BNX2_PCI_CONFIG_2_BAR1_SIZE_32M (10L<<0) +#define BNX2_PCI_CONFIG_2_BAR1_SIZE_64M (11L<<0) +#define BNX2_PCI_CONFIG_2_BAR1_SIZE_128M (12L<<0) +#define BNX2_PCI_CONFIG_2_BAR1_SIZE_256M (13L<<0) +#define BNX2_PCI_CONFIG_2_BAR1_SIZE_512M (14L<<0) +#define BNX2_PCI_CONFIG_2_BAR1_SIZE_1G (15L<<0) +#define BNX2_PCI_CONFIG_2_BAR1_64ENA (1L<<4) +#define BNX2_PCI_CONFIG_2_EXP_ROM_RETRY (1L<<5) +#define BNX2_PCI_CONFIG_2_CFG_CYCLE_RETRY (1L<<6) +#define BNX2_PCI_CONFIG_2_FIRST_CFG_DONE (1L<<7) +#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE (0xffL<<8) +#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_DISABLED (0L<<8) +#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_1K (1L<<8) +#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_2K (2L<<8) +#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_4K (3L<<8) +#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_8K (4L<<8) +#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_16K (5L<<8) +#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_32K (6L<<8) +#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_64K (7L<<8) +#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_128K (8L<<8) +#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_256K (9L<<8) +#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_512K (10L<<8) +#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_1M (11L<<8) +#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_2M (12L<<8) +#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_4M (13L<<8) +#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_8M (14L<<8) +#define BNX2_PCI_CONFIG_2_EXP_ROM_SIZE_16M (15L<<8) +#define BNX2_PCI_CONFIG_2_MAX_SPLIT_LIMIT (0x1fL<<16) +#define BNX2_PCI_CONFIG_2_MAX_READ_LIMIT (0x3L<<21) +#define BNX2_PCI_CONFIG_2_MAX_READ_LIMIT_512 (0L<<21) +#define BNX2_PCI_CONFIG_2_MAX_READ_LIMIT_1K (1L<<21) +#define BNX2_PCI_CONFIG_2_MAX_READ_LIMIT_2K (2L<<21) +#define BNX2_PCI_CONFIG_2_MAX_READ_LIMIT_4K (3L<<21) +#define BNX2_PCI_CONFIG_2_FORCE_32_BIT_MSTR (1L<<23) +#define BNX2_PCI_CONFIG_2_FORCE_32_BIT_TGT (1L<<24) +#define BNX2_PCI_CONFIG_2_KEEP_REQ_ASSERT (1L<<25) + +#define BNX2_PCI_CONFIG_3 0x0000040c +#define BNX2_PCI_CONFIG_3_STICKY_BYTE (0xffL<<0) +#define BNX2_PCI_CONFIG_3_FORCE_PME (1L<<24) +#define BNX2_PCI_CONFIG_3_PME_STATUS (1L<<25) +#define BNX2_PCI_CONFIG_3_PME_ENABLE (1L<<26) +#define BNX2_PCI_CONFIG_3_PM_STATE (0x3L<<27) +#define BNX2_PCI_CONFIG_3_VAUX_PRESET (1L<<30) +#define BNX2_PCI_CONFIG_3_PCI_POWER (1L<<31) + +#define BNX2_PCI_PM_DATA_A 0x00000410 +#define BNX2_PCI_PM_DATA_A_PM_DATA_0_PRG (0xffL<<0) +#define BNX2_PCI_PM_DATA_A_PM_DATA_1_PRG (0xffL<<8) +#define BNX2_PCI_PM_DATA_A_PM_DATA_2_PRG (0xffL<<16) +#define BNX2_PCI_PM_DATA_A_PM_DATA_3_PRG (0xffL<<24) + +#define BNX2_PCI_PM_DATA_B 0x00000414 +#define BNX2_PCI_PM_DATA_B_PM_DATA_4_PRG (0xffL<<0) +#define BNX2_PCI_PM_DATA_B_PM_DATA_5_PRG (0xffL<<8) +#define BNX2_PCI_PM_DATA_B_PM_DATA_6_PRG (0xffL<<16) +#define BNX2_PCI_PM_DATA_B_PM_DATA_7_PRG (0xffL<<24) + +#define BNX2_PCI_SWAP_DIAG0 0x00000418 +#define BNX2_PCI_SWAP_DIAG1 0x0000041c +#define BNX2_PCI_EXP_ROM_ADDR 0x00000420 +#define BNX2_PCI_EXP_ROM_ADDR_ADDRESS (0x3fffffL<<2) +#define BNX2_PCI_EXP_ROM_ADDR_REQ (1L<<31) + +#define BNX2_PCI_EXP_ROM_DATA 0x00000424 +#define BNX2_PCI_VPD_INTF 0x00000428 +#define BNX2_PCI_VPD_INTF_INTF_REQ (1L<<0) + +#define BNX2_PCI_VPD_ADDR_FLAG 0x0000042c +#define BNX2_PCI_VPD_ADDR_FLAG_ADDRESS (0x1fff<<2) +#define BNX2_PCI_VPD_ADDR_FLAG_WR (1<<15) + +#define BNX2_PCI_VPD_DATA 0x00000430 +#define BNX2_PCI_ID_VAL1 0x00000434 +#define BNX2_PCI_ID_VAL1_DEVICE_ID (0xffffL<<0) +#define BNX2_PCI_ID_VAL1_VENDOR_ID (0xffffL<<16) + +#define BNX2_PCI_ID_VAL2 0x00000438 +#define BNX2_PCI_ID_VAL2_SUBSYSTEM_VENDOR_ID (0xffffL<<0) +#define BNX2_PCI_ID_VAL2_SUBSYSTEM_ID (0xffffL<<16) + +#define BNX2_PCI_ID_VAL3 0x0000043c +#define BNX2_PCI_ID_VAL3_CLASS_CODE (0xffffffL<<0) +#define BNX2_PCI_ID_VAL3_REVISION_ID (0xffL<<24) + +#define BNX2_PCI_ID_VAL4 0x00000440 +#define BNX2_PCI_ID_VAL4_CAP_ENA (0xfL<<0) +#define BNX2_PCI_ID_VAL4_CAP_ENA_0 (0L<<0) +#define BNX2_PCI_ID_VAL4_CAP_ENA_1 (1L<<0) +#define BNX2_PCI_ID_VAL4_CAP_ENA_2 (2L<<0) +#define BNX2_PCI_ID_VAL4_CAP_ENA_3 (3L<<0) +#define BNX2_PCI_ID_VAL4_CAP_ENA_4 (4L<<0) +#define BNX2_PCI_ID_VAL4_CAP_ENA_5 (5L<<0) +#define BNX2_PCI_ID_VAL4_CAP_ENA_6 (6L<<0) +#define BNX2_PCI_ID_VAL4_CAP_ENA_7 (7L<<0) +#define BNX2_PCI_ID_VAL4_CAP_ENA_8 (8L<<0) +#define BNX2_PCI_ID_VAL4_CAP_ENA_9 (9L<<0) +#define BNX2_PCI_ID_VAL4_CAP_ENA_10 (10L<<0) +#define BNX2_PCI_ID_VAL4_CAP_ENA_11 (11L<<0) +#define BNX2_PCI_ID_VAL4_CAP_ENA_12 (12L<<0) +#define BNX2_PCI_ID_VAL4_CAP_ENA_13 (13L<<0) +#define BNX2_PCI_ID_VAL4_CAP_ENA_14 (14L<<0) +#define BNX2_PCI_ID_VAL4_CAP_ENA_15 (15L<<0) +#define BNX2_PCI_ID_VAL4_PM_SCALE_PRG (0x3L<<6) +#define BNX2_PCI_ID_VAL4_PM_SCALE_PRG_0 (0L<<6) +#define BNX2_PCI_ID_VAL4_PM_SCALE_PRG_1 (1L<<6) +#define BNX2_PCI_ID_VAL4_PM_SCALE_PRG_2 (2L<<6) +#define BNX2_PCI_ID_VAL4_PM_SCALE_PRG_3 (3L<<6) +#define BNX2_PCI_ID_VAL4_MSI_LIMIT (0x7L<<9) +#define BNX2_PCI_ID_VAL4_MSI_ADVERTIZE (0x7L<<12) +#define BNX2_PCI_ID_VAL4_MSI_ENABLE (1L<<15) +#define BNX2_PCI_ID_VAL4_MAX_64_ADVERTIZE (1L<<16) +#define BNX2_PCI_ID_VAL4_MAX_133_ADVERTIZE (1L<<17) +#define BNX2_PCI_ID_VAL4_MAX_MEM_READ_SIZE (0x3L<<21) +#define BNX2_PCI_ID_VAL4_MAX_SPLIT_SIZE (0x7L<<23) +#define BNX2_PCI_ID_VAL4_MAX_CUMULATIVE_SIZE (0x7L<<26) + +#define BNX2_PCI_ID_VAL5 0x00000444 +#define BNX2_PCI_ID_VAL5_D1_SUPPORT (1L<<0) +#define BNX2_PCI_ID_VAL5_D2_SUPPORT (1L<<1) +#define BNX2_PCI_ID_VAL5_PME_IN_D0 (1L<<2) +#define BNX2_PCI_ID_VAL5_PME_IN_D1 (1L<<3) +#define BNX2_PCI_ID_VAL5_PME_IN_D2 (1L<<4) +#define BNX2_PCI_ID_VAL5_PME_IN_D3_HOT (1L<<5) + +#define BNX2_PCI_PCIX_EXTENDED_STATUS 0x00000448 +#define BNX2_PCI_PCIX_EXTENDED_STATUS_NO_SNOOP (1L<<8) +#define BNX2_PCI_PCIX_EXTENDED_STATUS_LONG_BURST (1L<<9) +#define BNX2_PCI_PCIX_EXTENDED_STATUS_SPLIT_COMP_MSG_CLASS (0xfL<<16) +#define BNX2_PCI_PCIX_EXTENDED_STATUS_SPLIT_COMP_MSG_IDX (0xffL<<24) + +#define BNX2_PCI_ID_VAL6 0x0000044c +#define BNX2_PCI_ID_VAL6_MAX_LAT (0xffL<<0) +#define BNX2_PCI_ID_VAL6_MIN_GNT (0xffL<<8) +#define BNX2_PCI_ID_VAL6_BIST (0xffL<<16) + +#define BNX2_PCI_MSI_DATA 0x00000450 +#define BNX2_PCI_MSI_DATA_PCI_MSI_DATA (0xffffL<<0) + +#define BNX2_PCI_MSI_ADDR_H 0x00000454 +#define BNX2_PCI_MSI_ADDR_L 0x00000458 + + +/* + * misc_reg definition + * offset: 0x800 + */ +#define BNX2_MISC_COMMAND 0x00000800 +#define BNX2_MISC_COMMAND_ENABLE_ALL (1L<<0) +#define BNX2_MISC_COMMAND_DISABLE_ALL (1L<<1) +#define BNX2_MISC_COMMAND_CORE_RESET (1L<<4) +#define BNX2_MISC_COMMAND_HARD_RESET (1L<<5) +#define BNX2_MISC_COMMAND_PAR_ERROR (1L<<8) +#define BNX2_MISC_COMMAND_PAR_ERR_RAM (0x7fL<<16) + +#define BNX2_MISC_CFG 0x00000804 +#define BNX2_MISC_CFG_PCI_GRC_TMOUT (1L<<0) +#define BNX2_MISC_CFG_NVM_WR_EN (0x3L<<1) +#define BNX2_MISC_CFG_NVM_WR_EN_PROTECT (0L<<1) +#define BNX2_MISC_CFG_NVM_WR_EN_PCI (1L<<1) +#define BNX2_MISC_CFG_NVM_WR_EN_ALLOW (2L<<1) +#define BNX2_MISC_CFG_NVM_WR_EN_ALLOW2 (3L<<1) +#define BNX2_MISC_CFG_BIST_EN (1L<<3) +#define BNX2_MISC_CFG_CK25_OUT_ALT_SRC (1L<<4) +#define BNX2_MISC_CFG_BYPASS_BSCAN (1L<<5) +#define BNX2_MISC_CFG_BYPASS_EJTAG (1L<<6) +#define BNX2_MISC_CFG_CLK_CTL_OVERRIDE (1L<<7) +#define BNX2_MISC_CFG_LEDMODE (0x3L<<8) +#define BNX2_MISC_CFG_LEDMODE_MAC (0L<<8) +#define BNX2_MISC_CFG_LEDMODE_GPHY1 (1L<<8) +#define BNX2_MISC_CFG_LEDMODE_GPHY2 (2L<<8) + +#define BNX2_MISC_ID 0x00000808 +#define BNX2_MISC_ID_BOND_ID (0xfL<<0) +#define BNX2_MISC_ID_CHIP_METAL (0xffL<<4) +#define BNX2_MISC_ID_CHIP_REV (0xfL<<12) +#define BNX2_MISC_ID_CHIP_NUM (0xffffL<<16) + +#define BNX2_MISC_ENABLE_STATUS_BITS 0x0000080c +#define BNX2_MISC_ENABLE_STATUS_BITS_TX_SCHEDULER_ENABLE (1L<<0) +#define BNX2_MISC_ENABLE_STATUS_BITS_TX_BD_READ_ENABLE (1L<<1) +#define BNX2_MISC_ENABLE_STATUS_BITS_TX_BD_CACHE_ENABLE (1L<<2) +#define BNX2_MISC_ENABLE_STATUS_BITS_TX_PROCESSOR_ENABLE (1L<<3) +#define BNX2_MISC_ENABLE_STATUS_BITS_TX_DMA_ENABLE (1L<<4) +#define BNX2_MISC_ENABLE_STATUS_BITS_TX_PATCHUP_ENABLE (1L<<5) +#define BNX2_MISC_ENABLE_STATUS_BITS_TX_PAYLOAD_Q_ENABLE (1L<<6) +#define BNX2_MISC_ENABLE_STATUS_BITS_TX_HEADER_Q_ENABLE (1L<<7) +#define BNX2_MISC_ENABLE_STATUS_BITS_TX_ASSEMBLER_ENABLE (1L<<8) +#define BNX2_MISC_ENABLE_STATUS_BITS_EMAC_ENABLE (1L<<9) +#define BNX2_MISC_ENABLE_STATUS_BITS_RX_PARSER_MAC_ENABLE (1L<<10) +#define BNX2_MISC_ENABLE_STATUS_BITS_RX_PARSER_CATCHUP_ENABLE (1L<<11) +#define BNX2_MISC_ENABLE_STATUS_BITS_RX_MBUF_ENABLE (1L<<12) +#define BNX2_MISC_ENABLE_STATUS_BITS_RX_LOOKUP_ENABLE (1L<<13) +#define BNX2_MISC_ENABLE_STATUS_BITS_RX_PROCESSOR_ENABLE (1L<<14) +#define BNX2_MISC_ENABLE_STATUS_BITS_RX_V2P_ENABLE (1L<<15) +#define BNX2_MISC_ENABLE_STATUS_BITS_RX_BD_CACHE_ENABLE (1L<<16) +#define BNX2_MISC_ENABLE_STATUS_BITS_RX_DMA_ENABLE (1L<<17) +#define BNX2_MISC_ENABLE_STATUS_BITS_COMPLETION_ENABLE (1L<<18) +#define BNX2_MISC_ENABLE_STATUS_BITS_HOST_COALESCE_ENABLE (1L<<19) +#define BNX2_MISC_ENABLE_STATUS_BITS_MAILBOX_QUEUE_ENABLE (1L<<20) +#define BNX2_MISC_ENABLE_STATUS_BITS_CONTEXT_ENABLE (1L<<21) +#define BNX2_MISC_ENABLE_STATUS_BITS_CMD_SCHEDULER_ENABLE (1L<<22) +#define BNX2_MISC_ENABLE_STATUS_BITS_CMD_PROCESSOR_ENABLE (1L<<23) +#define BNX2_MISC_ENABLE_STATUS_BITS_MGMT_PROCESSOR_ENABLE (1L<<24) +#define BNX2_MISC_ENABLE_STATUS_BITS_TIMER_ENABLE (1L<<25) +#define BNX2_MISC_ENABLE_STATUS_BITS_DMA_ENGINE_ENABLE (1L<<26) +#define BNX2_MISC_ENABLE_STATUS_BITS_UMP_ENABLE (1L<<27) + +#define BNX2_MISC_ENABLE_SET_BITS 0x00000810 +#define BNX2_MISC_ENABLE_SET_BITS_TX_SCHEDULER_ENABLE (1L<<0) +#define BNX2_MISC_ENABLE_SET_BITS_TX_BD_READ_ENABLE (1L<<1) +#define BNX2_MISC_ENABLE_SET_BITS_TX_BD_CACHE_ENABLE (1L<<2) +#define BNX2_MISC_ENABLE_SET_BITS_TX_PROCESSOR_ENABLE (1L<<3) +#define BNX2_MISC_ENABLE_SET_BITS_TX_DMA_ENABLE (1L<<4) +#define BNX2_MISC_ENABLE_SET_BITS_TX_PATCHUP_ENABLE (1L<<5) +#define BNX2_MISC_ENABLE_SET_BITS_TX_PAYLOAD_Q_ENABLE (1L<<6) +#define BNX2_MISC_ENABLE_SET_BITS_TX_HEADER_Q_ENABLE (1L<<7) +#define BNX2_MISC_ENABLE_SET_BITS_TX_ASSEMBLER_ENABLE (1L<<8) +#define BNX2_MISC_ENABLE_SET_BITS_EMAC_ENABLE (1L<<9) +#define BNX2_MISC_ENABLE_SET_BITS_RX_PARSER_MAC_ENABLE (1L<<10) +#define BNX2_MISC_ENABLE_SET_BITS_RX_PARSER_CATCHUP_ENABLE (1L<<11) +#define BNX2_MISC_ENABLE_SET_BITS_RX_MBUF_ENABLE (1L<<12) +#define BNX2_MISC_ENABLE_SET_BITS_RX_LOOKUP_ENABLE (1L<<13) +#define BNX2_MISC_ENABLE_SET_BITS_RX_PROCESSOR_ENABLE (1L<<14) +#define BNX2_MISC_ENABLE_SET_BITS_RX_V2P_ENABLE (1L<<15) +#define BNX2_MISC_ENABLE_SET_BITS_RX_BD_CACHE_ENABLE (1L<<16) +#define BNX2_MISC_ENABLE_SET_BITS_RX_DMA_ENABLE (1L<<17) +#define BNX2_MISC_ENABLE_SET_BITS_COMPLETION_ENABLE (1L<<18) +#define BNX2_MISC_ENABLE_SET_BITS_HOST_COALESCE_ENABLE (1L<<19) +#define BNX2_MISC_ENABLE_SET_BITS_MAILBOX_QUEUE_ENABLE (1L<<20) +#define BNX2_MISC_ENABLE_SET_BITS_CONTEXT_ENABLE (1L<<21) +#define BNX2_MISC_ENABLE_SET_BITS_CMD_SCHEDULER_ENABLE (1L<<22) +#define BNX2_MISC_ENABLE_SET_BITS_CMD_PROCESSOR_ENABLE (1L<<23) +#define BNX2_MISC_ENABLE_SET_BITS_MGMT_PROCESSOR_ENABLE (1L<<24) +#define BNX2_MISC_ENABLE_SET_BITS_TIMER_ENABLE (1L<<25) +#define BNX2_MISC_ENABLE_SET_BITS_DMA_ENGINE_ENABLE (1L<<26) +#define BNX2_MISC_ENABLE_SET_BITS_UMP_ENABLE (1L<<27) + +#define BNX2_MISC_ENABLE_CLR_BITS 0x00000814 +#define BNX2_MISC_ENABLE_CLR_BITS_TX_SCHEDULER_ENABLE (1L<<0) +#define BNX2_MISC_ENABLE_CLR_BITS_TX_BD_READ_ENABLE (1L<<1) +#define BNX2_MISC_ENABLE_CLR_BITS_TX_BD_CACHE_ENABLE (1L<<2) +#define BNX2_MISC_ENABLE_CLR_BITS_TX_PROCESSOR_ENABLE (1L<<3) +#define BNX2_MISC_ENABLE_CLR_BITS_TX_DMA_ENABLE (1L<<4) +#define BNX2_MISC_ENABLE_CLR_BITS_TX_PATCHUP_ENABLE (1L<<5) +#define BNX2_MISC_ENABLE_CLR_BITS_TX_PAYLOAD_Q_ENABLE (1L<<6) +#define BNX2_MISC_ENABLE_CLR_BITS_TX_HEADER_Q_ENABLE (1L<<7) +#define BNX2_MISC_ENABLE_CLR_BITS_TX_ASSEMBLER_ENABLE (1L<<8) +#define BNX2_MISC_ENABLE_CLR_BITS_EMAC_ENABLE (1L<<9) +#define BNX2_MISC_ENABLE_CLR_BITS_RX_PARSER_MAC_ENABLE (1L<<10) +#define BNX2_MISC_ENABLE_CLR_BITS_RX_PARSER_CATCHUP_ENABLE (1L<<11) +#define BNX2_MISC_ENABLE_CLR_BITS_RX_MBUF_ENABLE (1L<<12) +#define BNX2_MISC_ENABLE_CLR_BITS_RX_LOOKUP_ENABLE (1L<<13) +#define BNX2_MISC_ENABLE_CLR_BITS_RX_PROCESSOR_ENABLE (1L<<14) +#define BNX2_MISC_ENABLE_CLR_BITS_RX_V2P_ENABLE (1L<<15) +#define BNX2_MISC_ENABLE_CLR_BITS_RX_BD_CACHE_ENABLE (1L<<16) +#define BNX2_MISC_ENABLE_CLR_BITS_RX_DMA_ENABLE (1L<<17) +#define BNX2_MISC_ENABLE_CLR_BITS_COMPLETION_ENABLE (1L<<18) +#define BNX2_MISC_ENABLE_CLR_BITS_HOST_COALESCE_ENABLE (1L<<19) +#define BNX2_MISC_ENABLE_CLR_BITS_MAILBOX_QUEUE_ENABLE (1L<<20) +#define BNX2_MISC_ENABLE_CLR_BITS_CONTEXT_ENABLE (1L<<21) +#define BNX2_MISC_ENABLE_CLR_BITS_CMD_SCHEDULER_ENABLE (1L<<22) +#define BNX2_MISC_ENABLE_CLR_BITS_CMD_PROCESSOR_ENABLE (1L<<23) +#define BNX2_MISC_ENABLE_CLR_BITS_MGMT_PROCESSOR_ENABLE (1L<<24) +#define BNX2_MISC_ENABLE_CLR_BITS_TIMER_ENABLE (1L<<25) +#define BNX2_MISC_ENABLE_CLR_BITS_DMA_ENGINE_ENABLE (1L<<26) +#define BNX2_MISC_ENABLE_CLR_BITS_UMP_ENABLE (1L<<27) + +#define BNX2_MISC_CLOCK_CONTROL_BITS 0x00000818 +#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET (0xfL<<0) +#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_32MHZ (0L<<0) +#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_38MHZ (1L<<0) +#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_48MHZ (2L<<0) +#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_55MHZ (3L<<0) +#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_66MHZ (4L<<0) +#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_80MHZ (5L<<0) +#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_95MHZ (6L<<0) +#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_133MHZ (7L<<0) +#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_LOW (0xfL<<0) +#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_DISABLE (1L<<6) +#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_ALT (1L<<7) +#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC (0x7L<<8) +#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_UNDEF (0L<<8) +#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_12 (1L<<8) +#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_6 (2L<<8) +#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_ALT_SRC_62 (4L<<8) +#define BNX2_MISC_CLOCK_CONTROL_BITS_PLAY_DEAD (1L<<11) +#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED (0xfL<<12) +#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_100 (0L<<12) +#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_80 (1L<<12) +#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_50 (2L<<12) +#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_40 (4L<<12) +#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_PLL_SPEED_25 (8L<<12) +#define BNX2_MISC_CLOCK_CONTROL_BITS_CORE_CLK_PLL_STOP (1L<<16) +#define BNX2_MISC_CLOCK_CONTROL_BITS_PCI_PLL_STOP (1L<<17) +#define BNX2_MISC_CLOCK_CONTROL_BITS_RESERVED_18 (1L<<18) +#define BNX2_MISC_CLOCK_CONTROL_BITS_USE_SPD_DET (1L<<19) +#define BNX2_MISC_CLOCK_CONTROL_BITS_RESERVED (0xfffL<<20) + +#define BNX2_MISC_GPIO 0x0000081c +#define BNX2_MISC_GPIO_VALUE (0xffL<<0) +#define BNX2_MISC_GPIO_SET (0xffL<<8) +#define BNX2_MISC_GPIO_CLR (0xffL<<16) +#define BNX2_MISC_GPIO_FLOAT (0xffL<<24) + +#define BNX2_MISC_GPIO_INT 0x00000820 +#define BNX2_MISC_GPIO_INT_INT_STATE (0xfL<<0) +#define BNX2_MISC_GPIO_INT_OLD_VALUE (0xfL<<8) +#define BNX2_MISC_GPIO_INT_OLD_SET (0xfL<<16) +#define BNX2_MISC_GPIO_INT_OLD_CLR (0xfL<<24) + +#define BNX2_MISC_CONFIG_LFSR 0x00000824 +#define BNX2_MISC_CONFIG_LFSR_DIV (0xffffL<<0) + +#define BNX2_MISC_LFSR_MASK_BITS 0x00000828 +#define BNX2_MISC_LFSR_MASK_BITS_TX_SCHEDULER_ENABLE (1L<<0) +#define BNX2_MISC_LFSR_MASK_BITS_TX_BD_READ_ENABLE (1L<<1) +#define BNX2_MISC_LFSR_MASK_BITS_TX_BD_CACHE_ENABLE (1L<<2) +#define BNX2_MISC_LFSR_MASK_BITS_TX_PROCESSOR_ENABLE (1L<<3) +#define BNX2_MISC_LFSR_MASK_BITS_TX_DMA_ENABLE (1L<<4) +#define BNX2_MISC_LFSR_MASK_BITS_TX_PATCHUP_ENABLE (1L<<5) +#define BNX2_MISC_LFSR_MASK_BITS_TX_PAYLOAD_Q_ENABLE (1L<<6) +#define BNX2_MISC_LFSR_MASK_BITS_TX_HEADER_Q_ENABLE (1L<<7) +#define BNX2_MISC_LFSR_MASK_BITS_TX_ASSEMBLER_ENABLE (1L<<8) +#define BNX2_MISC_LFSR_MASK_BITS_EMAC_ENABLE (1L<<9) +#define BNX2_MISC_LFSR_MASK_BITS_RX_PARSER_MAC_ENABLE (1L<<10) +#define BNX2_MISC_LFSR_MASK_BITS_RX_PARSER_CATCHUP_ENABLE (1L<<11) +#define BNX2_MISC_LFSR_MASK_BITS_RX_MBUF_ENABLE (1L<<12) +#define BNX2_MISC_LFSR_MASK_BITS_RX_LOOKUP_ENABLE (1L<<13) +#define BNX2_MISC_LFSR_MASK_BITS_RX_PROCESSOR_ENABLE (1L<<14) +#define BNX2_MISC_LFSR_MASK_BITS_RX_V2P_ENABLE (1L<<15) +#define BNX2_MISC_LFSR_MASK_BITS_RX_BD_CACHE_ENABLE (1L<<16) +#define BNX2_MISC_LFSR_MASK_BITS_RX_DMA_ENABLE (1L<<17) +#define BNX2_MISC_LFSR_MASK_BITS_COMPLETION_ENABLE (1L<<18) +#define BNX2_MISC_LFSR_MASK_BITS_HOST_COALESCE_ENABLE (1L<<19) +#define BNX2_MISC_LFSR_MASK_BITS_MAILBOX_QUEUE_ENABLE (1L<<20) +#define BNX2_MISC_LFSR_MASK_BITS_CONTEXT_ENABLE (1L<<21) +#define BNX2_MISC_LFSR_MASK_BITS_CMD_SCHEDULER_ENABLE (1L<<22) +#define BNX2_MISC_LFSR_MASK_BITS_CMD_PROCESSOR_ENABLE (1L<<23) +#define BNX2_MISC_LFSR_MASK_BITS_MGMT_PROCESSOR_ENABLE (1L<<24) +#define BNX2_MISC_LFSR_MASK_BITS_TIMER_ENABLE (1L<<25) +#define BNX2_MISC_LFSR_MASK_BITS_DMA_ENGINE_ENABLE (1L<<26) +#define BNX2_MISC_LFSR_MASK_BITS_UMP_ENABLE (1L<<27) + +#define BNX2_MISC_ARB_REQ0 0x0000082c +#define BNX2_MISC_ARB_REQ1 0x00000830 +#define BNX2_MISC_ARB_REQ2 0x00000834 +#define BNX2_MISC_ARB_REQ3 0x00000838 +#define BNX2_MISC_ARB_REQ4 0x0000083c +#define BNX2_MISC_ARB_FREE0 0x00000840 +#define BNX2_MISC_ARB_FREE1 0x00000844 +#define BNX2_MISC_ARB_FREE2 0x00000848 +#define BNX2_MISC_ARB_FREE3 0x0000084c +#define BNX2_MISC_ARB_FREE4 0x00000850 +#define BNX2_MISC_ARB_REQ_STATUS0 0x00000854 +#define BNX2_MISC_ARB_REQ_STATUS1 0x00000858 +#define BNX2_MISC_ARB_REQ_STATUS2 0x0000085c +#define BNX2_MISC_ARB_REQ_STATUS3 0x00000860 +#define BNX2_MISC_ARB_REQ_STATUS4 0x00000864 +#define BNX2_MISC_ARB_GNT0 0x00000868 +#define BNX2_MISC_ARB_GNT0_0 (0x7L<<0) +#define BNX2_MISC_ARB_GNT0_1 (0x7L<<4) +#define BNX2_MISC_ARB_GNT0_2 (0x7L<<8) +#define BNX2_MISC_ARB_GNT0_3 (0x7L<<12) +#define BNX2_MISC_ARB_GNT0_4 (0x7L<<16) +#define BNX2_MISC_ARB_GNT0_5 (0x7L<<20) +#define BNX2_MISC_ARB_GNT0_6 (0x7L<<24) +#define BNX2_MISC_ARB_GNT0_7 (0x7L<<28) + +#define BNX2_MISC_ARB_GNT1 0x0000086c +#define BNX2_MISC_ARB_GNT1_8 (0x7L<<0) +#define BNX2_MISC_ARB_GNT1_9 (0x7L<<4) +#define BNX2_MISC_ARB_GNT1_10 (0x7L<<8) +#define BNX2_MISC_ARB_GNT1_11 (0x7L<<12) +#define BNX2_MISC_ARB_GNT1_12 (0x7L<<16) +#define BNX2_MISC_ARB_GNT1_13 (0x7L<<20) +#define BNX2_MISC_ARB_GNT1_14 (0x7L<<24) +#define BNX2_MISC_ARB_GNT1_15 (0x7L<<28) + +#define BNX2_MISC_ARB_GNT2 0x00000870 +#define BNX2_MISC_ARB_GNT2_16 (0x7L<<0) +#define BNX2_MISC_ARB_GNT2_17 (0x7L<<4) +#define BNX2_MISC_ARB_GNT2_18 (0x7L<<8) +#define BNX2_MISC_ARB_GNT2_19 (0x7L<<12) +#define BNX2_MISC_ARB_GNT2_20 (0x7L<<16) +#define BNX2_MISC_ARB_GNT2_21 (0x7L<<20) +#define BNX2_MISC_ARB_GNT2_22 (0x7L<<24) +#define BNX2_MISC_ARB_GNT2_23 (0x7L<<28) + +#define BNX2_MISC_ARB_GNT3 0x00000874 +#define BNX2_MISC_ARB_GNT3_24 (0x7L<<0) +#define BNX2_MISC_ARB_GNT3_25 (0x7L<<4) +#define BNX2_MISC_ARB_GNT3_26 (0x7L<<8) +#define BNX2_MISC_ARB_GNT3_27 (0x7L<<12) +#define BNX2_MISC_ARB_GNT3_28 (0x7L<<16) +#define BNX2_MISC_ARB_GNT3_29 (0x7L<<20) +#define BNX2_MISC_ARB_GNT3_30 (0x7L<<24) +#define BNX2_MISC_ARB_GNT3_31 (0x7L<<28) + +#define BNX2_MISC_PRBS_CONTROL 0x00000878 +#define BNX2_MISC_PRBS_CONTROL_EN (1L<<0) +#define BNX2_MISC_PRBS_CONTROL_RSTB (1L<<1) +#define BNX2_MISC_PRBS_CONTROL_INV (1L<<2) +#define BNX2_MISC_PRBS_CONTROL_ERR_CLR (1L<<3) +#define BNX2_MISC_PRBS_CONTROL_ORDER (0x3L<<4) +#define BNX2_MISC_PRBS_CONTROL_ORDER_7TH (0L<<4) +#define BNX2_MISC_PRBS_CONTROL_ORDER_15TH (1L<<4) +#define BNX2_MISC_PRBS_CONTROL_ORDER_23RD (2L<<4) +#define BNX2_MISC_PRBS_CONTROL_ORDER_31ST (3L<<4) + +#define BNX2_MISC_PRBS_STATUS 0x0000087c +#define BNX2_MISC_PRBS_STATUS_LOCK (1L<<0) +#define BNX2_MISC_PRBS_STATUS_STKY (1L<<1) +#define BNX2_MISC_PRBS_STATUS_ERRORS (0x3fffL<<2) +#define BNX2_MISC_PRBS_STATUS_STATE (0xfL<<16) + +#define BNX2_MISC_SM_ASF_CONTROL 0x00000880 +#define BNX2_MISC_SM_ASF_CONTROL_ASF_RST (1L<<0) +#define BNX2_MISC_SM_ASF_CONTROL_TSC_EN (1L<<1) +#define BNX2_MISC_SM_ASF_CONTROL_WG_TO (1L<<2) +#define BNX2_MISC_SM_ASF_CONTROL_HB_TO (1L<<3) +#define BNX2_MISC_SM_ASF_CONTROL_PA_TO (1L<<4) +#define BNX2_MISC_SM_ASF_CONTROL_PL_TO (1L<<5) +#define BNX2_MISC_SM_ASF_CONTROL_RT_TO (1L<<6) +#define BNX2_MISC_SM_ASF_CONTROL_SMB_EVENT (1L<<7) +#define BNX2_MISC_SM_ASF_CONTROL_RES (0xfL<<8) +#define BNX2_MISC_SM_ASF_CONTROL_SMB_EN (1L<<12) +#define BNX2_MISC_SM_ASF_CONTROL_SMB_BB_EN (1L<<13) +#define BNX2_MISC_SM_ASF_CONTROL_SMB_NO_ADDR_FILT (1L<<14) +#define BNX2_MISC_SM_ASF_CONTROL_SMB_AUTOREAD (1L<<15) +#define BNX2_MISC_SM_ASF_CONTROL_NIC_SMB_ADDR1 (0x3fL<<16) +#define BNX2_MISC_SM_ASF_CONTROL_NIC_SMB_ADDR2 (0x3fL<<24) +#define BNX2_MISC_SM_ASF_CONTROL_EN_NIC_SMB_ADDR_0 (1L<<30) +#define BNX2_MISC_SM_ASF_CONTROL_SMB_EARLY_ATTN (1L<<31) + +#define BNX2_MISC_SMB_IN 0x00000884 +#define BNX2_MISC_SMB_IN_DAT_IN (0xffL<<0) +#define BNX2_MISC_SMB_IN_RDY (1L<<8) +#define BNX2_MISC_SMB_IN_DONE (1L<<9) +#define BNX2_MISC_SMB_IN_FIRSTBYTE (1L<<10) +#define BNX2_MISC_SMB_IN_STATUS (0x7L<<11) +#define BNX2_MISC_SMB_IN_STATUS_OK (0x0L<<11) +#define BNX2_MISC_SMB_IN_STATUS_PEC (0x1L<<11) +#define BNX2_MISC_SMB_IN_STATUS_OFLOW (0x2L<<11) +#define BNX2_MISC_SMB_IN_STATUS_STOP (0x3L<<11) +#define BNX2_MISC_SMB_IN_STATUS_TIMEOUT (0x4L<<11) + +#define BNX2_MISC_SMB_OUT 0x00000888 +#define BNX2_MISC_SMB_OUT_DAT_OUT (0xffL<<0) +#define BNX2_MISC_SMB_OUT_RDY (1L<<8) +#define BNX2_MISC_SMB_OUT_START (1L<<9) +#define BNX2_MISC_SMB_OUT_LAST (1L<<10) +#define BNX2_MISC_SMB_OUT_ACC_TYPE (1L<<11) +#define BNX2_MISC_SMB_OUT_ENB_PEC (1L<<12) +#define BNX2_MISC_SMB_OUT_GET_RX_LEN (1L<<13) +#define BNX2_MISC_SMB_OUT_SMB_READ_LEN (0x3fL<<14) +#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS (0xfL<<20) +#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_OK (0L<<20) +#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_FIRST_NACK (1L<<20) +#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_SUB_NACK (9L<<20) +#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_UFLOW (2L<<20) +#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_STOP (3L<<20) +#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_TIMEOUT (4L<<20) +#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_FIRST_LOST (5L<<20) +#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_SUB_LOST (0xdL<<20) +#define BNX2_MISC_SMB_OUT_SMB_OUT_STATUS_BADACK (0x6L<<20) +#define BNX2_MISC_SMB_OUT_SMB_OUT_SLAVEMODE (1L<<24) +#define BNX2_MISC_SMB_OUT_SMB_OUT_DAT_EN (1L<<25) +#define BNX2_MISC_SMB_OUT_SMB_OUT_DAT_IN (1L<<26) +#define BNX2_MISC_SMB_OUT_SMB_OUT_CLK_EN (1L<<27) +#define BNX2_MISC_SMB_OUT_SMB_OUT_CLK_IN (1L<<28) + +#define BNX2_MISC_SMB_WATCHDOG 0x0000088c +#define BNX2_MISC_SMB_WATCHDOG_WATCHDOG (0xffffL<<0) + +#define BNX2_MISC_SMB_HEARTBEAT 0x00000890 +#define BNX2_MISC_SMB_HEARTBEAT_HEARTBEAT (0xffffL<<0) + +#define BNX2_MISC_SMB_POLL_ASF 0x00000894 +#define BNX2_MISC_SMB_POLL_ASF_POLL_ASF (0xffffL<<0) + +#define BNX2_MISC_SMB_POLL_LEGACY 0x00000898 +#define BNX2_MISC_SMB_POLL_LEGACY_POLL_LEGACY (0xffffL<<0) + +#define BNX2_MISC_SMB_RETRAN 0x0000089c +#define BNX2_MISC_SMB_RETRAN_RETRAN (0xffL<<0) + +#define BNX2_MISC_SMB_TIMESTAMP 0x000008a0 +#define BNX2_MISC_SMB_TIMESTAMP_TIMESTAMP (0xffffffffL<<0) + +#define BNX2_MISC_PERR_ENA0 0x000008a4 +#define BNX2_MISC_PERR_ENA0_COM_MISC_CTXC (1L<<0) +#define BNX2_MISC_PERR_ENA0_COM_MISC_REGF (1L<<1) +#define BNX2_MISC_PERR_ENA0_COM_MISC_SCPAD (1L<<2) +#define BNX2_MISC_PERR_ENA0_CP_MISC_CTXC (1L<<3) +#define BNX2_MISC_PERR_ENA0_CP_MISC_REGF (1L<<4) +#define BNX2_MISC_PERR_ENA0_CP_MISC_SCPAD (1L<<5) +#define BNX2_MISC_PERR_ENA0_CS_MISC_TMEM (1L<<6) +#define BNX2_MISC_PERR_ENA0_CTX_MISC_ACCM0 (1L<<7) +#define BNX2_MISC_PERR_ENA0_CTX_MISC_ACCM1 (1L<<8) +#define BNX2_MISC_PERR_ENA0_CTX_MISC_ACCM2 (1L<<9) +#define BNX2_MISC_PERR_ENA0_CTX_MISC_ACCM3 (1L<<10) +#define BNX2_MISC_PERR_ENA0_CTX_MISC_ACCM4 (1L<<11) +#define BNX2_MISC_PERR_ENA0_CTX_MISC_ACCM5 (1L<<12) +#define BNX2_MISC_PERR_ENA0_CTX_MISC_PGTBL (1L<<13) +#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DR0 (1L<<14) +#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DR1 (1L<<15) +#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DR2 (1L<<16) +#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DR3 (1L<<17) +#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DR4 (1L<<18) +#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DW0 (1L<<19) +#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DW1 (1L<<20) +#define BNX2_MISC_PERR_ENA0_DMAE_MISC_DW2 (1L<<21) +#define BNX2_MISC_PERR_ENA0_HC_MISC_DMA (1L<<22) +#define BNX2_MISC_PERR_ENA0_MCP_MISC_REGF (1L<<23) +#define BNX2_MISC_PERR_ENA0_MCP_MISC_SCPAD (1L<<24) +#define BNX2_MISC_PERR_ENA0_MQ_MISC_CTX (1L<<25) +#define BNX2_MISC_PERR_ENA0_RBDC_MISC (1L<<26) +#define BNX2_MISC_PERR_ENA0_RBUF_MISC_MB (1L<<27) +#define BNX2_MISC_PERR_ENA0_RBUF_MISC_PTR (1L<<28) +#define BNX2_MISC_PERR_ENA0_RDE_MISC_RPC (1L<<29) +#define BNX2_MISC_PERR_ENA0_RDE_MISC_RPM (1L<<30) +#define BNX2_MISC_PERR_ENA0_RV2P_MISC_CB0REGS (1L<<31) + +#define BNX2_MISC_PERR_ENA1 0x000008a8 +#define BNX2_MISC_PERR_ENA1_RV2P_MISC_CB1REGS (1L<<0) +#define BNX2_MISC_PERR_ENA1_RV2P_MISC_P1IRAM (1L<<1) +#define BNX2_MISC_PERR_ENA1_RV2P_MISC_P2IRAM (1L<<2) +#define BNX2_MISC_PERR_ENA1_RXP_MISC_CTXC (1L<<3) +#define BNX2_MISC_PERR_ENA1_RXP_MISC_REGF (1L<<4) +#define BNX2_MISC_PERR_ENA1_RXP_MISC_SCPAD (1L<<5) +#define BNX2_MISC_PERR_ENA1_RXP_MISC_RBUFC (1L<<6) +#define BNX2_MISC_PERR_ENA1_TBDC_MISC (1L<<7) +#define BNX2_MISC_PERR_ENA1_TDMA_MISC (1L<<8) +#define BNX2_MISC_PERR_ENA1_THBUF_MISC_MB0 (1L<<9) +#define BNX2_MISC_PERR_ENA1_THBUF_MISC_MB1 (1L<<10) +#define BNX2_MISC_PERR_ENA1_TPAT_MISC_REGF (1L<<11) +#define BNX2_MISC_PERR_ENA1_TPAT_MISC_SCPAD (1L<<12) +#define BNX2_MISC_PERR_ENA1_TPBUF_MISC_MB (1L<<13) +#define BNX2_MISC_PERR_ENA1_TSCH_MISC_LR (1L<<14) +#define BNX2_MISC_PERR_ENA1_TXP_MISC_CTXC (1L<<15) +#define BNX2_MISC_PERR_ENA1_TXP_MISC_REGF (1L<<16) +#define BNX2_MISC_PERR_ENA1_TXP_MISC_SCPAD (1L<<17) +#define BNX2_MISC_PERR_ENA1_UMP_MISC_FIORX (1L<<18) +#define BNX2_MISC_PERR_ENA1_UMP_MISC_FIOTX (1L<<19) +#define BNX2_MISC_PERR_ENA1_UMP_MISC_RX (1L<<20) +#define BNX2_MISC_PERR_ENA1_UMP_MISC_TX (1L<<21) +#define BNX2_MISC_PERR_ENA1_RDMAQ_MISC (1L<<22) +#define BNX2_MISC_PERR_ENA1_CSQ_MISC (1L<<23) +#define BNX2_MISC_PERR_ENA1_CPQ_MISC (1L<<24) +#define BNX2_MISC_PERR_ENA1_MCPQ_MISC (1L<<25) +#define BNX2_MISC_PERR_ENA1_RV2PMQ_MISC (1L<<26) +#define BNX2_MISC_PERR_ENA1_RV2PPQ_MISC (1L<<27) +#define BNX2_MISC_PERR_ENA1_RV2PTQ_MISC (1L<<28) +#define BNX2_MISC_PERR_ENA1_RXPQ_MISC (1L<<29) +#define BNX2_MISC_PERR_ENA1_RXPCQ_MISC (1L<<30) +#define BNX2_MISC_PERR_ENA1_RLUPQ_MISC (1L<<31) + +#define BNX2_MISC_PERR_ENA2 0x000008ac +#define BNX2_MISC_PERR_ENA2_COMQ_MISC (1L<<0) +#define BNX2_MISC_PERR_ENA2_COMXQ_MISC (1L<<1) +#define BNX2_MISC_PERR_ENA2_COMTQ_MISC (1L<<2) +#define BNX2_MISC_PERR_ENA2_TSCHQ_MISC (1L<<3) +#define BNX2_MISC_PERR_ENA2_TBDRQ_MISC (1L<<4) +#define BNX2_MISC_PERR_ENA2_TXPQ_MISC (1L<<5) +#define BNX2_MISC_PERR_ENA2_TDMAQ_MISC (1L<<6) +#define BNX2_MISC_PERR_ENA2_TPATQ_MISC (1L<<7) +#define BNX2_MISC_PERR_ENA2_TASQ_MISC (1L<<8) + +#define BNX2_MISC_DEBUG_VECTOR_SEL 0x000008b0 +#define BNX2_MISC_DEBUG_VECTOR_SEL_0 (0xfffL<<0) +#define BNX2_MISC_DEBUG_VECTOR_SEL_1 (0xfffL<<12) + +#define BNX2_MISC_VREG_CONTROL 0x000008b4 +#define BNX2_MISC_VREG_CONTROL_1_2 (0xfL<<0) +#define BNX2_MISC_VREG_CONTROL_2_5 (0xfL<<4) + +#define BNX2_MISC_FINAL_CLK_CTL_VAL 0x000008b8 +#define BNX2_MISC_FINAL_CLK_CTL_VAL_MISC_FINAL_CLK_CTL_VAL (0x3ffffffL<<6) + +#define BNX2_MISC_UNUSED0 0x000008bc + + +/* + * nvm_reg definition + * offset: 0x6400 + */ +#define BNX2_NVM_COMMAND 0x00006400 +#define BNX2_NVM_COMMAND_RST (1L<<0) +#define BNX2_NVM_COMMAND_DONE (1L<<3) +#define BNX2_NVM_COMMAND_DOIT (1L<<4) +#define BNX2_NVM_COMMAND_WR (1L<<5) +#define BNX2_NVM_COMMAND_ERASE (1L<<6) +#define BNX2_NVM_COMMAND_FIRST (1L<<7) +#define BNX2_NVM_COMMAND_LAST (1L<<8) +#define BNX2_NVM_COMMAND_WREN (1L<<16) +#define BNX2_NVM_COMMAND_WRDI (1L<<17) +#define BNX2_NVM_COMMAND_EWSR (1L<<18) +#define BNX2_NVM_COMMAND_WRSR (1L<<19) + +#define BNX2_NVM_STATUS 0x00006404 +#define BNX2_NVM_STATUS_PI_FSM_STATE (0xfL<<0) +#define BNX2_NVM_STATUS_EE_FSM_STATE (0xfL<<4) +#define BNX2_NVM_STATUS_EQ_FSM_STATE (0xfL<<8) + +#define BNX2_NVM_WRITE 0x00006408 +#define BNX2_NVM_WRITE_NVM_WRITE_VALUE (0xffffffffL<<0) +#define BNX2_NVM_WRITE_NVM_WRITE_VALUE_BIT_BANG (0L<<0) +#define BNX2_NVM_WRITE_NVM_WRITE_VALUE_EECLK (1L<<0) +#define BNX2_NVM_WRITE_NVM_WRITE_VALUE_EEDATA (2L<<0) +#define BNX2_NVM_WRITE_NVM_WRITE_VALUE_SCLK (4L<<0) +#define BNX2_NVM_WRITE_NVM_WRITE_VALUE_CS_B (8L<<0) +#define BNX2_NVM_WRITE_NVM_WRITE_VALUE_SO (16L<<0) +#define BNX2_NVM_WRITE_NVM_WRITE_VALUE_SI (32L<<0) + +#define BNX2_NVM_ADDR 0x0000640c +#define BNX2_NVM_ADDR_NVM_ADDR_VALUE (0xffffffL<<0) +#define BNX2_NVM_ADDR_NVM_ADDR_VALUE_BIT_BANG (0L<<0) +#define BNX2_NVM_ADDR_NVM_ADDR_VALUE_EECLK (1L<<0) +#define BNX2_NVM_ADDR_NVM_ADDR_VALUE_EEDATA (2L<<0) +#define BNX2_NVM_ADDR_NVM_ADDR_VALUE_SCLK (4L<<0) +#define BNX2_NVM_ADDR_NVM_ADDR_VALUE_CS_B (8L<<0) +#define BNX2_NVM_ADDR_NVM_ADDR_VALUE_SO (16L<<0) +#define BNX2_NVM_ADDR_NVM_ADDR_VALUE_SI (32L<<0) + +#define BNX2_NVM_READ 0x00006410 +#define BNX2_NVM_READ_NVM_READ_VALUE (0xffffffffL<<0) +#define BNX2_NVM_READ_NVM_READ_VALUE_BIT_BANG (0L<<0) +#define BNX2_NVM_READ_NVM_READ_VALUE_EECLK (1L<<0) +#define BNX2_NVM_READ_NVM_READ_VALUE_EEDATA (2L<<0) +#define BNX2_NVM_READ_NVM_READ_VALUE_SCLK (4L<<0) +#define BNX2_NVM_READ_NVM_READ_VALUE_CS_B (8L<<0) +#define BNX2_NVM_READ_NVM_READ_VALUE_SO (16L<<0) +#define BNX2_NVM_READ_NVM_READ_VALUE_SI (32L<<0) + +#define BNX2_NVM_CFG1 0x00006414 +#define BNX2_NVM_CFG1_FLASH_MODE (1L<<0) +#define BNX2_NVM_CFG1_BUFFER_MODE (1L<<1) +#define BNX2_NVM_CFG1_PASS_MODE (1L<<2) +#define BNX2_NVM_CFG1_BITBANG_MODE (1L<<3) +#define BNX2_NVM_CFG1_STATUS_BIT (0x7L<<4) +#define BNX2_NVM_CFG1_STATUS_BIT_FLASH_RDY (0L<<4) +#define BNX2_NVM_CFG1_STATUS_BIT_BUFFER_RDY (7L<<4) +#define BNX2_NVM_CFG1_SPI_CLK_DIV (0xfL<<7) +#define BNX2_NVM_CFG1_SEE_CLK_DIV (0x7ffL<<11) +#define BNX2_NVM_CFG1_PROTECT_MODE (1L<<24) +#define BNX2_NVM_CFG1_FLASH_SIZE (1L<<25) +#define BNX2_NVM_CFG1_COMPAT_BYPASSS (1L<<31) + +#define BNX2_NVM_CFG2 0x00006418 +#define BNX2_NVM_CFG2_ERASE_CMD (0xffL<<0) +#define BNX2_NVM_CFG2_DUMMY (0xffL<<8) +#define BNX2_NVM_CFG2_STATUS_CMD (0xffL<<16) + +#define BNX2_NVM_CFG3 0x0000641c +#define BNX2_NVM_CFG3_BUFFER_RD_CMD (0xffL<<0) +#define BNX2_NVM_CFG3_WRITE_CMD (0xffL<<8) +#define BNX2_NVM_CFG3_BUFFER_WRITE_CMD (0xffL<<16) +#define BNX2_NVM_CFG3_READ_CMD (0xffL<<24) + +#define BNX2_NVM_SW_ARB 0x00006420 +#define BNX2_NVM_SW_ARB_ARB_REQ_SET0 (1L<<0) +#define BNX2_NVM_SW_ARB_ARB_REQ_SET1 (1L<<1) +#define BNX2_NVM_SW_ARB_ARB_REQ_SET2 (1L<<2) +#define BNX2_NVM_SW_ARB_ARB_REQ_SET3 (1L<<3) +#define BNX2_NVM_SW_ARB_ARB_REQ_CLR0 (1L<<4) +#define BNX2_NVM_SW_ARB_ARB_REQ_CLR1 (1L<<5) +#define BNX2_NVM_SW_ARB_ARB_REQ_CLR2 (1L<<6) +#define BNX2_NVM_SW_ARB_ARB_REQ_CLR3 (1L<<7) +#define BNX2_NVM_SW_ARB_ARB_ARB0 (1L<<8) +#define BNX2_NVM_SW_ARB_ARB_ARB1 (1L<<9) +#define BNX2_NVM_SW_ARB_ARB_ARB2 (1L<<10) +#define BNX2_NVM_SW_ARB_ARB_ARB3 (1L<<11) +#define BNX2_NVM_SW_ARB_REQ0 (1L<<12) +#define BNX2_NVM_SW_ARB_REQ1 (1L<<13) +#define BNX2_NVM_SW_ARB_REQ2 (1L<<14) +#define BNX2_NVM_SW_ARB_REQ3 (1L<<15) + +#define BNX2_NVM_ACCESS_ENABLE 0x00006424 +#define BNX2_NVM_ACCESS_ENABLE_EN (1L<<0) +#define BNX2_NVM_ACCESS_ENABLE_WR_EN (1L<<1) + +#define BNX2_NVM_WRITE1 0x00006428 +#define BNX2_NVM_WRITE1_WREN_CMD (0xffL<<0) +#define BNX2_NVM_WRITE1_WRDI_CMD (0xffL<<8) +#define BNX2_NVM_WRITE1_SR_DATA (0xffL<<16) + + + +/* + * dma_reg definition + * offset: 0xc00 + */ +#define BNX2_DMA_COMMAND 0x00000c00 +#define BNX2_DMA_COMMAND_ENABLE (1L<<0) + +#define BNX2_DMA_STATUS 0x00000c04 +#define BNX2_DMA_STATUS_PAR_ERROR_STATE (1L<<0) +#define BNX2_DMA_STATUS_READ_TRANSFERS_STAT (1L<<16) +#define BNX2_DMA_STATUS_READ_DELAY_PCI_CLKS_STAT (1L<<17) +#define BNX2_DMA_STATUS_BIG_READ_TRANSFERS_STAT (1L<<18) +#define BNX2_DMA_STATUS_BIG_READ_DELAY_PCI_CLKS_STAT (1L<<19) +#define BNX2_DMA_STATUS_BIG_READ_RETRY_AFTER_DATA_STAT (1L<<20) +#define BNX2_DMA_STATUS_WRITE_TRANSFERS_STAT (1L<<21) +#define BNX2_DMA_STATUS_WRITE_DELAY_PCI_CLKS_STAT (1L<<22) +#define BNX2_DMA_STATUS_BIG_WRITE_TRANSFERS_STAT (1L<<23) +#define BNX2_DMA_STATUS_BIG_WRITE_DELAY_PCI_CLKS_STAT (1L<<24) +#define BNX2_DMA_STATUS_BIG_WRITE_RETRY_AFTER_DATA_STAT (1L<<25) + +#define BNX2_DMA_CONFIG 0x00000c08 +#define BNX2_DMA_CONFIG_DATA_BYTE_SWAP (1L<<0) +#define BNX2_DMA_CONFIG_DATA_WORD_SWAP (1L<<1) +#define BNX2_DMA_CONFIG_CNTL_BYTE_SWAP (1L<<4) +#define BNX2_DMA_CONFIG_CNTL_WORD_SWAP (1L<<5) +#define BNX2_DMA_CONFIG_ONE_DMA (1L<<6) +#define BNX2_DMA_CONFIG_CNTL_TWO_DMA (1L<<7) +#define BNX2_DMA_CONFIG_CNTL_FPGA_MODE (1L<<8) +#define BNX2_DMA_CONFIG_CNTL_PING_PONG_DMA (1L<<10) +#define BNX2_DMA_CONFIG_CNTL_PCI_COMP_DLY (1L<<11) +#define BNX2_DMA_CONFIG_NO_RCHANS_IN_USE (0xfL<<12) +#define BNX2_DMA_CONFIG_NO_WCHANS_IN_USE (0xfL<<16) +#define BNX2_DMA_CONFIG_PCI_CLK_CMP_BITS (0x7L<<20) +#define BNX2_DMA_CONFIG_PCI_FAST_CLK_CMP (1L<<23) +#define BNX2_DMA_CONFIG_BIG_SIZE (0xfL<<24) +#define BNX2_DMA_CONFIG_BIG_SIZE_NONE (0x0L<<24) +#define BNX2_DMA_CONFIG_BIG_SIZE_64 (0x1L<<24) +#define BNX2_DMA_CONFIG_BIG_SIZE_128 (0x2L<<24) +#define BNX2_DMA_CONFIG_BIG_SIZE_256 (0x4L<<24) +#define BNX2_DMA_CONFIG_BIG_SIZE_512 (0x8L<<24) + +#define BNX2_DMA_BLACKOUT 0x00000c0c +#define BNX2_DMA_BLACKOUT_RD_RETRY_BLACKOUT (0xffL<<0) +#define BNX2_DMA_BLACKOUT_2ND_RD_RETRY_BLACKOUT (0xffL<<8) +#define BNX2_DMA_BLACKOUT_WR_RETRY_BLACKOUT (0xffL<<16) + +#define BNX2_DMA_RCHAN_STAT 0x00000c30 +#define BNX2_DMA_RCHAN_STAT_COMP_CODE_0 (0x7L<<0) +#define BNX2_DMA_RCHAN_STAT_PAR_ERR_0 (1L<<3) +#define BNX2_DMA_RCHAN_STAT_COMP_CODE_1 (0x7L<<4) +#define BNX2_DMA_RCHAN_STAT_PAR_ERR_1 (1L<<7) +#define BNX2_DMA_RCHAN_STAT_COMP_CODE_2 (0x7L<<8) +#define BNX2_DMA_RCHAN_STAT_PAR_ERR_2 (1L<<11) +#define BNX2_DMA_RCHAN_STAT_COMP_CODE_3 (0x7L<<12) +#define BNX2_DMA_RCHAN_STAT_PAR_ERR_3 (1L<<15) +#define BNX2_DMA_RCHAN_STAT_COMP_CODE_4 (0x7L<<16) +#define BNX2_DMA_RCHAN_STAT_PAR_ERR_4 (1L<<19) +#define BNX2_DMA_RCHAN_STAT_COMP_CODE_5 (0x7L<<20) +#define BNX2_DMA_RCHAN_STAT_PAR_ERR_5 (1L<<23) +#define BNX2_DMA_RCHAN_STAT_COMP_CODE_6 (0x7L<<24) +#define BNX2_DMA_RCHAN_STAT_PAR_ERR_6 (1L<<27) +#define BNX2_DMA_RCHAN_STAT_COMP_CODE_7 (0x7L<<28) +#define BNX2_DMA_RCHAN_STAT_PAR_ERR_7 (1L<<31) + +#define BNX2_DMA_WCHAN_STAT 0x00000c34 +#define BNX2_DMA_WCHAN_STAT_COMP_CODE_0 (0x7L<<0) +#define BNX2_DMA_WCHAN_STAT_PAR_ERR_0 (1L<<3) +#define BNX2_DMA_WCHAN_STAT_COMP_CODE_1 (0x7L<<4) +#define BNX2_DMA_WCHAN_STAT_PAR_ERR_1 (1L<<7) +#define BNX2_DMA_WCHAN_STAT_COMP_CODE_2 (0x7L<<8) +#define BNX2_DMA_WCHAN_STAT_PAR_ERR_2 (1L<<11) +#define BNX2_DMA_WCHAN_STAT_COMP_CODE_3 (0x7L<<12) +#define BNX2_DMA_WCHAN_STAT_PAR_ERR_3 (1L<<15) +#define BNX2_DMA_WCHAN_STAT_COMP_CODE_4 (0x7L<<16) +#define BNX2_DMA_WCHAN_STAT_PAR_ERR_4 (1L<<19) +#define BNX2_DMA_WCHAN_STAT_COMP_CODE_5 (0x7L<<20) +#define BNX2_DMA_WCHAN_STAT_PAR_ERR_5 (1L<<23) +#define BNX2_DMA_WCHAN_STAT_COMP_CODE_6 (0x7L<<24) +#define BNX2_DMA_WCHAN_STAT_PAR_ERR_6 (1L<<27) +#define BNX2_DMA_WCHAN_STAT_COMP_CODE_7 (0x7L<<28) +#define BNX2_DMA_WCHAN_STAT_PAR_ERR_7 (1L<<31) + +#define BNX2_DMA_RCHAN_ASSIGNMENT 0x00000c38 +#define BNX2_DMA_RCHAN_ASSIGNMENT_0 (0xfL<<0) +#define BNX2_DMA_RCHAN_ASSIGNMENT_1 (0xfL<<4) +#define BNX2_DMA_RCHAN_ASSIGNMENT_2 (0xfL<<8) +#define BNX2_DMA_RCHAN_ASSIGNMENT_3 (0xfL<<12) +#define BNX2_DMA_RCHAN_ASSIGNMENT_4 (0xfL<<16) +#define BNX2_DMA_RCHAN_ASSIGNMENT_5 (0xfL<<20) +#define BNX2_DMA_RCHAN_ASSIGNMENT_6 (0xfL<<24) +#define BNX2_DMA_RCHAN_ASSIGNMENT_7 (0xfL<<28) + +#define BNX2_DMA_WCHAN_ASSIGNMENT 0x00000c3c +#define BNX2_DMA_WCHAN_ASSIGNMENT_0 (0xfL<<0) +#define BNX2_DMA_WCHAN_ASSIGNMENT_1 (0xfL<<4) +#define BNX2_DMA_WCHAN_ASSIGNMENT_2 (0xfL<<8) +#define BNX2_DMA_WCHAN_ASSIGNMENT_3 (0xfL<<12) +#define BNX2_DMA_WCHAN_ASSIGNMENT_4 (0xfL<<16) +#define BNX2_DMA_WCHAN_ASSIGNMENT_5 (0xfL<<20) +#define BNX2_DMA_WCHAN_ASSIGNMENT_6 (0xfL<<24) +#define BNX2_DMA_WCHAN_ASSIGNMENT_7 (0xfL<<28) + +#define BNX2_DMA_RCHAN_STAT_00 0x00000c40 +#define BNX2_DMA_RCHAN_STAT_00_RCHAN_STA_HOST_ADDR_LOW (0xffffffffL<<0) + +#define BNX2_DMA_RCHAN_STAT_01 0x00000c44 +#define BNX2_DMA_RCHAN_STAT_01_RCHAN_STA_HOST_ADDR_HIGH (0xffffffffL<<0) + +#define BNX2_DMA_RCHAN_STAT_02 0x00000c48 +#define BNX2_DMA_RCHAN_STAT_02_LENGTH (0xffffL<<0) +#define BNX2_DMA_RCHAN_STAT_02_WORD_SWAP (1L<<16) +#define BNX2_DMA_RCHAN_STAT_02_BYTE_SWAP (1L<<17) +#define BNX2_DMA_RCHAN_STAT_02_PRIORITY_LVL (1L<<18) + +#define BNX2_DMA_RCHAN_STAT_10 0x00000c4c +#define BNX2_DMA_RCHAN_STAT_11 0x00000c50 +#define BNX2_DMA_RCHAN_STAT_12 0x00000c54 +#define BNX2_DMA_RCHAN_STAT_20 0x00000c58 +#define BNX2_DMA_RCHAN_STAT_21 0x00000c5c +#define BNX2_DMA_RCHAN_STAT_22 0x00000c60 +#define BNX2_DMA_RCHAN_STAT_30 0x00000c64 +#define BNX2_DMA_RCHAN_STAT_31 0x00000c68 +#define BNX2_DMA_RCHAN_STAT_32 0x00000c6c +#define BNX2_DMA_RCHAN_STAT_40 0x00000c70 +#define BNX2_DMA_RCHAN_STAT_41 0x00000c74 +#define BNX2_DMA_RCHAN_STAT_42 0x00000c78 +#define BNX2_DMA_RCHAN_STAT_50 0x00000c7c +#define BNX2_DMA_RCHAN_STAT_51 0x00000c80 +#define BNX2_DMA_RCHAN_STAT_52 0x00000c84 +#define BNX2_DMA_RCHAN_STAT_60 0x00000c88 +#define BNX2_DMA_RCHAN_STAT_61 0x00000c8c +#define BNX2_DMA_RCHAN_STAT_62 0x00000c90 +#define BNX2_DMA_RCHAN_STAT_70 0x00000c94 +#define BNX2_DMA_RCHAN_STAT_71 0x00000c98 +#define BNX2_DMA_RCHAN_STAT_72 0x00000c9c +#define BNX2_DMA_WCHAN_STAT_00 0x00000ca0 +#define BNX2_DMA_WCHAN_STAT_00_WCHAN_STA_HOST_ADDR_LOW (0xffffffffL<<0) + +#define BNX2_DMA_WCHAN_STAT_01 0x00000ca4 +#define BNX2_DMA_WCHAN_STAT_01_WCHAN_STA_HOST_ADDR_HIGH (0xffffffffL<<0) + +#define BNX2_DMA_WCHAN_STAT_02 0x00000ca8 +#define BNX2_DMA_WCHAN_STAT_02_LENGTH (0xffffL<<0) +#define BNX2_DMA_WCHAN_STAT_02_WORD_SWAP (1L<<16) +#define BNX2_DMA_WCHAN_STAT_02_BYTE_SWAP (1L<<17) +#define BNX2_DMA_WCHAN_STAT_02_PRIORITY_LVL (1L<<18) + +#define BNX2_DMA_WCHAN_STAT_10 0x00000cac +#define BNX2_DMA_WCHAN_STAT_11 0x00000cb0 +#define BNX2_DMA_WCHAN_STAT_12 0x00000cb4 +#define BNX2_DMA_WCHAN_STAT_20 0x00000cb8 +#define BNX2_DMA_WCHAN_STAT_21 0x00000cbc +#define BNX2_DMA_WCHAN_STAT_22 0x00000cc0 +#define BNX2_DMA_WCHAN_STAT_30 0x00000cc4 +#define BNX2_DMA_WCHAN_STAT_31 0x00000cc8 +#define BNX2_DMA_WCHAN_STAT_32 0x00000ccc +#define BNX2_DMA_WCHAN_STAT_40 0x00000cd0 +#define BNX2_DMA_WCHAN_STAT_41 0x00000cd4 +#define BNX2_DMA_WCHAN_STAT_42 0x00000cd8 +#define BNX2_DMA_WCHAN_STAT_50 0x00000cdc +#define BNX2_DMA_WCHAN_STAT_51 0x00000ce0 +#define BNX2_DMA_WCHAN_STAT_52 0x00000ce4 +#define BNX2_DMA_WCHAN_STAT_60 0x00000ce8 +#define BNX2_DMA_WCHAN_STAT_61 0x00000cec +#define BNX2_DMA_WCHAN_STAT_62 0x00000cf0 +#define BNX2_DMA_WCHAN_STAT_70 0x00000cf4 +#define BNX2_DMA_WCHAN_STAT_71 0x00000cf8 +#define BNX2_DMA_WCHAN_STAT_72 0x00000cfc +#define BNX2_DMA_ARB_STAT_00 0x00000d00 +#define BNX2_DMA_ARB_STAT_00_MASTER (0xffffL<<0) +#define BNX2_DMA_ARB_STAT_00_MASTER_ENC (0xffL<<16) +#define BNX2_DMA_ARB_STAT_00_CUR_BINMSTR (0xffL<<24) + +#define BNX2_DMA_ARB_STAT_01 0x00000d04 +#define BNX2_DMA_ARB_STAT_01_LPR_RPTR (0xfL<<0) +#define BNX2_DMA_ARB_STAT_01_LPR_WPTR (0xfL<<4) +#define BNX2_DMA_ARB_STAT_01_LPB_RPTR (0xfL<<8) +#define BNX2_DMA_ARB_STAT_01_LPB_WPTR (0xfL<<12) +#define BNX2_DMA_ARB_STAT_01_HPR_RPTR (0xfL<<16) +#define BNX2_DMA_ARB_STAT_01_HPR_WPTR (0xfL<<20) +#define BNX2_DMA_ARB_STAT_01_HPB_RPTR (0xfL<<24) +#define BNX2_DMA_ARB_STAT_01_HPB_WPTR (0xfL<<28) + +#define BNX2_DMA_FUSE_CTRL0_CMD 0x00000f00 +#define BNX2_DMA_FUSE_CTRL0_CMD_PWRUP_DONE (1L<<0) +#define BNX2_DMA_FUSE_CTRL0_CMD_SHIFT_DONE (1L<<1) +#define BNX2_DMA_FUSE_CTRL0_CMD_SHIFT (1L<<2) +#define BNX2_DMA_FUSE_CTRL0_CMD_LOAD (1L<<3) +#define BNX2_DMA_FUSE_CTRL0_CMD_SEL (0xfL<<8) + +#define BNX2_DMA_FUSE_CTRL0_DATA 0x00000f04 +#define BNX2_DMA_FUSE_CTRL1_CMD 0x00000f08 +#define BNX2_DMA_FUSE_CTRL1_CMD_PWRUP_DONE (1L<<0) +#define BNX2_DMA_FUSE_CTRL1_CMD_SHIFT_DONE (1L<<1) +#define BNX2_DMA_FUSE_CTRL1_CMD_SHIFT (1L<<2) +#define BNX2_DMA_FUSE_CTRL1_CMD_LOAD (1L<<3) +#define BNX2_DMA_FUSE_CTRL1_CMD_SEL (0xfL<<8) + +#define BNX2_DMA_FUSE_CTRL1_DATA 0x00000f0c +#define BNX2_DMA_FUSE_CTRL2_CMD 0x00000f10 +#define BNX2_DMA_FUSE_CTRL2_CMD_PWRUP_DONE (1L<<0) +#define BNX2_DMA_FUSE_CTRL2_CMD_SHIFT_DONE (1L<<1) +#define BNX2_DMA_FUSE_CTRL2_CMD_SHIFT (1L<<2) +#define BNX2_DMA_FUSE_CTRL2_CMD_LOAD (1L<<3) +#define BNX2_DMA_FUSE_CTRL2_CMD_SEL (0xfL<<8) + +#define BNX2_DMA_FUSE_CTRL2_DATA 0x00000f14 + + +/* + * context_reg definition + * offset: 0x1000 + */ +#define BNX2_CTX_COMMAND 0x00001000 +#define BNX2_CTX_COMMAND_ENABLED (1L<<0) + +#define BNX2_CTX_STATUS 0x00001004 +#define BNX2_CTX_STATUS_LOCK_WAIT (1L<<0) +#define BNX2_CTX_STATUS_READ_STAT (1L<<16) +#define BNX2_CTX_STATUS_WRITE_STAT (1L<<17) +#define BNX2_CTX_STATUS_ACC_STALL_STAT (1L<<18) +#define BNX2_CTX_STATUS_LOCK_STALL_STAT (1L<<19) + +#define BNX2_CTX_VIRT_ADDR 0x00001008 +#define BNX2_CTX_VIRT_ADDR_VIRT_ADDR (0x7fffL<<6) + +#define BNX2_CTX_PAGE_TBL 0x0000100c +#define BNX2_CTX_PAGE_TBL_PAGE_TBL (0x3fffL<<6) + +#define BNX2_CTX_DATA_ADR 0x00001010 +#define BNX2_CTX_DATA_ADR_DATA_ADR (0x7ffffL<<2) + +#define BNX2_CTX_DATA 0x00001014 +#define BNX2_CTX_LOCK 0x00001018 +#define BNX2_CTX_LOCK_TYPE (0x7L<<0) +#define BNX2_CTX_LOCK_TYPE_LOCK_TYPE_VOID (0x0L<<0) +#define BNX2_CTX_LOCK_TYPE_LOCK_TYPE_COMPLETE (0x7L<<0) +#define BNX2_CTX_LOCK_TYPE_LOCK_TYPE_PROTOCOL (0x1L<<0) +#define BNX2_CTX_LOCK_TYPE_LOCK_TYPE_TX (0x2L<<0) +#define BNX2_CTX_LOCK_TYPE_LOCK_TYPE_TIMER (0x4L<<0) +#define BNX2_CTX_LOCK_CID_VALUE (0x3fffL<<7) +#define BNX2_CTX_LOCK_GRANTED (1L<<26) +#define BNX2_CTX_LOCK_MODE (0x7L<<27) +#define BNX2_CTX_LOCK_MODE_UNLOCK (0x0L<<27) +#define BNX2_CTX_LOCK_MODE_IMMEDIATE (0x1L<<27) +#define BNX2_CTX_LOCK_MODE_SURE (0x2L<<27) +#define BNX2_CTX_LOCK_STATUS (1L<<30) +#define BNX2_CTX_LOCK_REQ (1L<<31) + +#define BNX2_CTX_ACCESS_STATUS 0x00001040 +#define BNX2_CTX_ACCESS_STATUS_MASTERENCODED (0xfL<<0) +#define BNX2_CTX_ACCESS_STATUS_ACCESSMEMORYSM (0x3L<<10) +#define BNX2_CTX_ACCESS_STATUS_PAGETABLEINITSM (0x3L<<12) +#define BNX2_CTX_ACCESS_STATUS_ACCESSMEMORYINITSM (0x3L<<14) +#define BNX2_CTX_ACCESS_STATUS_QUALIFIED_REQUEST (0x7ffL<<17) + +#define BNX2_CTX_DBG_LOCK_STATUS 0x00001044 +#define BNX2_CTX_DBG_LOCK_STATUS_SM (0x3ffL<<0) +#define BNX2_CTX_DBG_LOCK_STATUS_MATCH (0x3ffL<<22) + +#define BNX2_CTX_CHNL_LOCK_STATUS_0 0x00001080 +#define BNX2_CTX_CHNL_LOCK_STATUS_0_CID (0x3fffL<<0) +#define BNX2_CTX_CHNL_LOCK_STATUS_0_TYPE (0x3L<<14) +#define BNX2_CTX_CHNL_LOCK_STATUS_0_MODE (1L<<16) + +#define BNX2_CTX_CHNL_LOCK_STATUS_1 0x00001084 +#define BNX2_CTX_CHNL_LOCK_STATUS_2 0x00001088 +#define BNX2_CTX_CHNL_LOCK_STATUS_3 0x0000108c +#define BNX2_CTX_CHNL_LOCK_STATUS_4 0x00001090 +#define BNX2_CTX_CHNL_LOCK_STATUS_5 0x00001094 +#define BNX2_CTX_CHNL_LOCK_STATUS_6 0x00001098 +#define BNX2_CTX_CHNL_LOCK_STATUS_7 0x0000109c +#define BNX2_CTX_CHNL_LOCK_STATUS_8 0x000010a0 + + +/* + * emac_reg definition + * offset: 0x1400 + */ +#define BNX2_EMAC_MODE 0x00001400 +#define BNX2_EMAC_MODE_RESET (1L<<0) +#define BNX2_EMAC_MODE_HALF_DUPLEX (1L<<1) +#define BNX2_EMAC_MODE_PORT (0x3L<<2) +#define BNX2_EMAC_MODE_PORT_NONE (0L<<2) +#define BNX2_EMAC_MODE_PORT_MII (1L<<2) +#define BNX2_EMAC_MODE_PORT_GMII (2L<<2) +#define BNX2_EMAC_MODE_PORT_MII_10 (3L<<2) +#define BNX2_EMAC_MODE_MAC_LOOP (1L<<4) +#define BNX2_EMAC_MODE_25G (1L<<5) +#define BNX2_EMAC_MODE_TAGGED_MAC_CTL (1L<<7) +#define BNX2_EMAC_MODE_TX_BURST (1L<<8) +#define BNX2_EMAC_MODE_MAX_DEFER_DROP_ENA (1L<<9) +#define BNX2_EMAC_MODE_EXT_LINK_POL (1L<<10) +#define BNX2_EMAC_MODE_FORCE_LINK (1L<<11) +#define BNX2_EMAC_MODE_MPKT (1L<<18) +#define BNX2_EMAC_MODE_MPKT_RCVD (1L<<19) +#define BNX2_EMAC_MODE_ACPI_RCVD (1L<<20) + +#define BNX2_EMAC_STATUS 0x00001404 +#define BNX2_EMAC_STATUS_LINK (1L<<11) +#define BNX2_EMAC_STATUS_LINK_CHANGE (1L<<12) +#define BNX2_EMAC_STATUS_MI_COMPLETE (1L<<22) +#define BNX2_EMAC_STATUS_MI_INT (1L<<23) +#define BNX2_EMAC_STATUS_AP_ERROR (1L<<24) +#define BNX2_EMAC_STATUS_PARITY_ERROR_STATE (1L<<31) + +#define BNX2_EMAC_ATTENTION_ENA 0x00001408 +#define BNX2_EMAC_ATTENTION_ENA_LINK (1L<<11) +#define BNX2_EMAC_ATTENTION_ENA_MI_COMPLETE (1L<<22) +#define BNX2_EMAC_ATTENTION_ENA_MI_INT (1L<<23) +#define BNX2_EMAC_ATTENTION_ENA_AP_ERROR (1L<<24) + +#define BNX2_EMAC_LED 0x0000140c +#define BNX2_EMAC_LED_OVERRIDE (1L<<0) +#define BNX2_EMAC_LED_1000MB_OVERRIDE (1L<<1) +#define BNX2_EMAC_LED_100MB_OVERRIDE (1L<<2) +#define BNX2_EMAC_LED_10MB_OVERRIDE (1L<<3) +#define BNX2_EMAC_LED_TRAFFIC_OVERRIDE (1L<<4) +#define BNX2_EMAC_LED_BLNK_TRAFFIC (1L<<5) +#define BNX2_EMAC_LED_TRAFFIC (1L<<6) +#define BNX2_EMAC_LED_1000MB (1L<<7) +#define BNX2_EMAC_LED_100MB (1L<<8) +#define BNX2_EMAC_LED_10MB (1L<<9) +#define BNX2_EMAC_LED_TRAFFIC_STAT (1L<<10) +#define BNX2_EMAC_LED_BLNK_RATE (0xfffL<<19) +#define BNX2_EMAC_LED_BLNK_RATE_ENA (1L<<31) + +#define BNX2_EMAC_MAC_MATCH0 0x00001410 +#define BNX2_EMAC_MAC_MATCH1 0x00001414 +#define BNX2_EMAC_MAC_MATCH2 0x00001418 +#define BNX2_EMAC_MAC_MATCH3 0x0000141c +#define BNX2_EMAC_MAC_MATCH4 0x00001420 +#define BNX2_EMAC_MAC_MATCH5 0x00001424 +#define BNX2_EMAC_MAC_MATCH6 0x00001428 +#define BNX2_EMAC_MAC_MATCH7 0x0000142c +#define BNX2_EMAC_MAC_MATCH8 0x00001430 +#define BNX2_EMAC_MAC_MATCH9 0x00001434 +#define BNX2_EMAC_MAC_MATCH10 0x00001438 +#define BNX2_EMAC_MAC_MATCH11 0x0000143c +#define BNX2_EMAC_MAC_MATCH12 0x00001440 +#define BNX2_EMAC_MAC_MATCH13 0x00001444 +#define BNX2_EMAC_MAC_MATCH14 0x00001448 +#define BNX2_EMAC_MAC_MATCH15 0x0000144c +#define BNX2_EMAC_MAC_MATCH16 0x00001450 +#define BNX2_EMAC_MAC_MATCH17 0x00001454 +#define BNX2_EMAC_MAC_MATCH18 0x00001458 +#define BNX2_EMAC_MAC_MATCH19 0x0000145c +#define BNX2_EMAC_MAC_MATCH20 0x00001460 +#define BNX2_EMAC_MAC_MATCH21 0x00001464 +#define BNX2_EMAC_MAC_MATCH22 0x00001468 +#define BNX2_EMAC_MAC_MATCH23 0x0000146c +#define BNX2_EMAC_MAC_MATCH24 0x00001470 +#define BNX2_EMAC_MAC_MATCH25 0x00001474 +#define BNX2_EMAC_MAC_MATCH26 0x00001478 +#define BNX2_EMAC_MAC_MATCH27 0x0000147c +#define BNX2_EMAC_MAC_MATCH28 0x00001480 +#define BNX2_EMAC_MAC_MATCH29 0x00001484 +#define BNX2_EMAC_MAC_MATCH30 0x00001488 +#define BNX2_EMAC_MAC_MATCH31 0x0000148c +#define BNX2_EMAC_BACKOFF_SEED 0x00001498 +#define BNX2_EMAC_BACKOFF_SEED_EMAC_BACKOFF_SEED (0x3ffL<<0) + +#define BNX2_EMAC_RX_MTU_SIZE 0x0000149c +#define BNX2_EMAC_RX_MTU_SIZE_MTU_SIZE (0xffffL<<0) +#define BNX2_EMAC_RX_MTU_SIZE_JUMBO_ENA (1L<<31) + +#define BNX2_EMAC_SERDES_CNTL 0x000014a4 +#define BNX2_EMAC_SERDES_CNTL_RXR (0x7L<<0) +#define BNX2_EMAC_SERDES_CNTL_RXG (0x3L<<3) +#define BNX2_EMAC_SERDES_CNTL_RXCKSEL (1L<<6) +#define BNX2_EMAC_SERDES_CNTL_TXBIAS (0x7L<<7) +#define BNX2_EMAC_SERDES_CNTL_BGMAX (1L<<10) +#define BNX2_EMAC_SERDES_CNTL_BGMIN (1L<<11) +#define BNX2_EMAC_SERDES_CNTL_TXMODE (1L<<12) +#define BNX2_EMAC_SERDES_CNTL_TXEDGE (1L<<13) +#define BNX2_EMAC_SERDES_CNTL_SERDES_MODE (1L<<14) +#define BNX2_EMAC_SERDES_CNTL_PLLTEST (1L<<15) +#define BNX2_EMAC_SERDES_CNTL_CDET_EN (1L<<16) +#define BNX2_EMAC_SERDES_CNTL_TBI_LBK (1L<<17) +#define BNX2_EMAC_SERDES_CNTL_REMOTE_LBK (1L<<18) +#define BNX2_EMAC_SERDES_CNTL_REV_PHASE (1L<<19) +#define BNX2_EMAC_SERDES_CNTL_REGCTL12 (0x3L<<20) +#define BNX2_EMAC_SERDES_CNTL_REGCTL25 (0x3L<<22) + +#define BNX2_EMAC_SERDES_STATUS 0x000014a8 +#define BNX2_EMAC_SERDES_STATUS_RX_STAT (0xffL<<0) +#define BNX2_EMAC_SERDES_STATUS_COMMA_DET (1L<<8) + +#define BNX2_EMAC_MDIO_COMM 0x000014ac +#define BNX2_EMAC_MDIO_COMM_DATA (0xffffL<<0) +#define BNX2_EMAC_MDIO_COMM_REG_ADDR (0x1fL<<16) +#define BNX2_EMAC_MDIO_COMM_PHY_ADDR (0x1fL<<21) +#define BNX2_EMAC_MDIO_COMM_COMMAND (0x3L<<26) +#define BNX2_EMAC_MDIO_COMM_COMMAND_UNDEFINED_0 (0L<<26) +#define BNX2_EMAC_MDIO_COMM_COMMAND_WRITE (1L<<26) +#define BNX2_EMAC_MDIO_COMM_COMMAND_READ (2L<<26) +#define BNX2_EMAC_MDIO_COMM_COMMAND_UNDEFINED_3 (3L<<26) +#define BNX2_EMAC_MDIO_COMM_FAIL (1L<<28) +#define BNX2_EMAC_MDIO_COMM_START_BUSY (1L<<29) +#define BNX2_EMAC_MDIO_COMM_DISEXT (1L<<30) + +#define BNX2_EMAC_MDIO_STATUS 0x000014b0 +#define BNX2_EMAC_MDIO_STATUS_LINK (1L<<0) +#define BNX2_EMAC_MDIO_STATUS_10MB (1L<<1) + +#define BNX2_EMAC_MDIO_MODE 0x000014b4 +#define BNX2_EMAC_MDIO_MODE_SHORT_PREAMBLE (1L<<1) +#define BNX2_EMAC_MDIO_MODE_AUTO_POLL (1L<<4) +#define BNX2_EMAC_MDIO_MODE_BIT_BANG (1L<<8) +#define BNX2_EMAC_MDIO_MODE_MDIO (1L<<9) +#define BNX2_EMAC_MDIO_MODE_MDIO_OE (1L<<10) +#define BNX2_EMAC_MDIO_MODE_MDC (1L<<11) +#define BNX2_EMAC_MDIO_MODE_MDINT (1L<<12) +#define BNX2_EMAC_MDIO_MODE_CLOCK_CNT (0x1fL<<16) + +#define BNX2_EMAC_MDIO_AUTO_STATUS 0x000014b8 +#define BNX2_EMAC_MDIO_AUTO_STATUS_AUTO_ERR (1L<<0) + +#define BNX2_EMAC_TX_MODE 0x000014bc +#define BNX2_EMAC_TX_MODE_RESET (1L<<0) +#define BNX2_EMAC_TX_MODE_EXT_PAUSE_EN (1L<<3) +#define BNX2_EMAC_TX_MODE_FLOW_EN (1L<<4) +#define BNX2_EMAC_TX_MODE_BIG_BACKOFF (1L<<5) +#define BNX2_EMAC_TX_MODE_LONG_PAUSE (1L<<6) +#define BNX2_EMAC_TX_MODE_LINK_AWARE (1L<<7) + +#define BNX2_EMAC_TX_STATUS 0x000014c0 +#define BNX2_EMAC_TX_STATUS_XOFFED (1L<<0) +#define BNX2_EMAC_TX_STATUS_XOFF_SENT (1L<<1) +#define BNX2_EMAC_TX_STATUS_XON_SENT (1L<<2) +#define BNX2_EMAC_TX_STATUS_LINK_UP (1L<<3) +#define BNX2_EMAC_TX_STATUS_UNDERRUN (1L<<4) + +#define BNX2_EMAC_TX_LENGTHS 0x000014c4 +#define BNX2_EMAC_TX_LENGTHS_SLOT (0xffL<<0) +#define BNX2_EMAC_TX_LENGTHS_IPG (0xfL<<8) +#define BNX2_EMAC_TX_LENGTHS_IPG_CRS (0x3L<<12) + +#define BNX2_EMAC_RX_MODE 0x000014c8 +#define BNX2_EMAC_RX_MODE_RESET (1L<<0) +#define BNX2_EMAC_RX_MODE_FLOW_EN (1L<<2) +#define BNX2_EMAC_RX_MODE_KEEP_MAC_CONTROL (1L<<3) +#define BNX2_EMAC_RX_MODE_KEEP_PAUSE (1L<<4) +#define BNX2_EMAC_RX_MODE_ACCEPT_OVERSIZE (1L<<5) +#define BNX2_EMAC_RX_MODE_ACCEPT_RUNTS (1L<<6) +#define BNX2_EMAC_RX_MODE_LLC_CHK (1L<<7) +#define BNX2_EMAC_RX_MODE_PROMISCUOUS (1L<<8) +#define BNX2_EMAC_RX_MODE_NO_CRC_CHK (1L<<9) +#define BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG (1L<<10) +#define BNX2_EMAC_RX_MODE_FILT_BROADCAST (1L<<11) +#define BNX2_EMAC_RX_MODE_SORT_MODE (1L<<12) + +#define BNX2_EMAC_RX_STATUS 0x000014cc +#define BNX2_EMAC_RX_STATUS_FFED (1L<<0) +#define BNX2_EMAC_RX_STATUS_FF_RECEIVED (1L<<1) +#define BNX2_EMAC_RX_STATUS_N_RECEIVED (1L<<2) + +#define BNX2_EMAC_MULTICAST_HASH0 0x000014d0 +#define BNX2_EMAC_MULTICAST_HASH1 0x000014d4 +#define BNX2_EMAC_MULTICAST_HASH2 0x000014d8 +#define BNX2_EMAC_MULTICAST_HASH3 0x000014dc +#define BNX2_EMAC_MULTICAST_HASH4 0x000014e0 +#define BNX2_EMAC_MULTICAST_HASH5 0x000014e4 +#define BNX2_EMAC_MULTICAST_HASH6 0x000014e8 +#define BNX2_EMAC_MULTICAST_HASH7 0x000014ec +#define BNX2_EMAC_RX_STAT_IFHCINOCTETS 0x00001500 +#define BNX2_EMAC_RX_STAT_IFHCINBADOCTETS 0x00001504 +#define BNX2_EMAC_RX_STAT_ETHERSTATSFRAGMENTS 0x00001508 +#define BNX2_EMAC_RX_STAT_IFHCINUCASTPKTS 0x0000150c +#define BNX2_EMAC_RX_STAT_IFHCINMULTICASTPKTS 0x00001510 +#define BNX2_EMAC_RX_STAT_IFHCINBROADCASTPKTS 0x00001514 +#define BNX2_EMAC_RX_STAT_DOT3STATSFCSERRORS 0x00001518 +#define BNX2_EMAC_RX_STAT_DOT3STATSALIGNMENTERRORS 0x0000151c +#define BNX2_EMAC_RX_STAT_DOT3STATSCARRIERSENSEERRORS 0x00001520 +#define BNX2_EMAC_RX_STAT_XONPAUSEFRAMESRECEIVED 0x00001524 +#define BNX2_EMAC_RX_STAT_XOFFPAUSEFRAMESRECEIVED 0x00001528 +#define BNX2_EMAC_RX_STAT_MACCONTROLFRAMESRECEIVED 0x0000152c +#define BNX2_EMAC_RX_STAT_XOFFSTATEENTERED 0x00001530 +#define BNX2_EMAC_RX_STAT_DOT3STATSFRAMESTOOLONG 0x00001534 +#define BNX2_EMAC_RX_STAT_ETHERSTATSJABBERS 0x00001538 +#define BNX2_EMAC_RX_STAT_ETHERSTATSUNDERSIZEPKTS 0x0000153c +#define BNX2_EMAC_RX_STAT_ETHERSTATSPKTS64OCTETS 0x00001540 +#define BNX2_EMAC_RX_STAT_ETHERSTATSPKTS65OCTETSTO127OCTETS 0x00001544 +#define BNX2_EMAC_RX_STAT_ETHERSTATSPKTS128OCTETSTO255OCTETS 0x00001548 +#define BNX2_EMAC_RX_STAT_ETHERSTATSPKTS256OCTETSTO511OCTETS 0x0000154c +#define BNX2_EMAC_RX_STAT_ETHERSTATSPKTS512OCTETSTO1023OCTETS 0x00001550 +#define BNX2_EMAC_RX_STAT_ETHERSTATSPKTS1024OCTETSTO1522OCTETS 0x00001554 +#define BNX2_EMAC_RX_STAT_ETHERSTATSPKTS1523OCTETSTO9022OCTETS 0x00001558 +#define BNX2_EMAC_RXMAC_DEBUG0 0x0000155c +#define BNX2_EMAC_RXMAC_DEBUG1 0x00001560 +#define BNX2_EMAC_RXMAC_DEBUG1_LENGTH_NE_BYTE_COUNT (1L<<0) +#define BNX2_EMAC_RXMAC_DEBUG1_LENGTH_OUT_RANGE (1L<<1) +#define BNX2_EMAC_RXMAC_DEBUG1_BAD_CRC (1L<<2) +#define BNX2_EMAC_RXMAC_DEBUG1_RX_ERROR (1L<<3) +#define BNX2_EMAC_RXMAC_DEBUG1_ALIGN_ERROR (1L<<4) +#define BNX2_EMAC_RXMAC_DEBUG1_LAST_DATA (1L<<5) +#define BNX2_EMAC_RXMAC_DEBUG1_ODD_BYTE_START (1L<<6) +#define BNX2_EMAC_RXMAC_DEBUG1_BYTE_COUNT (0xffffL<<7) +#define BNX2_EMAC_RXMAC_DEBUG1_SLOT_TIME (0xffL<<23) + +#define BNX2_EMAC_RXMAC_DEBUG2 0x00001564 +#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE (0x7L<<0) +#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_IDLE (0x0L<<0) +#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_SFD (0x1L<<0) +#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_DATA (0x2L<<0) +#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_SKEEP (0x3L<<0) +#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_EXT (0x4L<<0) +#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_DROP (0x5L<<0) +#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_SDROP (0x6L<<0) +#define BNX2_EMAC_RXMAC_DEBUG2_SM_STATE_FC (0x7L<<0) +#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE (0xfL<<3) +#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_IDLE (0x0L<<3) +#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_DATA0 (0x1L<<3) +#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_DATA1 (0x2L<<3) +#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_DATA2 (0x3L<<3) +#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_DATA3 (0x4L<<3) +#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_ABORT (0x5L<<3) +#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_WAIT (0x6L<<3) +#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_STATUS (0x7L<<3) +#define BNX2_EMAC_RXMAC_DEBUG2_IDI_STATE_LAST (0x8L<<3) +#define BNX2_EMAC_RXMAC_DEBUG2_BYTE_IN (0xffL<<7) +#define BNX2_EMAC_RXMAC_DEBUG2_FALSEC (1L<<15) +#define BNX2_EMAC_RXMAC_DEBUG2_TAGGED (1L<<16) +#define BNX2_EMAC_RXMAC_DEBUG2_PAUSE_STATE (1L<<18) +#define BNX2_EMAC_RXMAC_DEBUG2_PAUSE_STATE_IDLE (0L<<18) +#define BNX2_EMAC_RXMAC_DEBUG2_PAUSE_STATE_PAUSED (1L<<18) +#define BNX2_EMAC_RXMAC_DEBUG2_SE_COUNTER (0xfL<<19) +#define BNX2_EMAC_RXMAC_DEBUG2_QUANTA (0x1fL<<23) + +#define BNX2_EMAC_RXMAC_DEBUG3 0x00001568 +#define BNX2_EMAC_RXMAC_DEBUG3_PAUSE_CTR (0xffffL<<0) +#define BNX2_EMAC_RXMAC_DEBUG3_TMP_PAUSE_CTR (0xffffL<<16) + +#define BNX2_EMAC_RXMAC_DEBUG4 0x0000156c +#define BNX2_EMAC_RXMAC_DEBUG4_TYPE_FIELD (0xffffL<<0) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE (0x3fL<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_IDLE (0x0L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_UMAC2 (0x1L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_UMAC3 (0x2L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_UNI (0x3L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MMAC2 (0x7L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MMAC3 (0x5L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_PSA1 (0x6L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_PSA2 (0x7L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_PSA3 (0x8L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MC2 (0x9L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MC3 (0xaL<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MWAIT1 (0xeL<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MWAIT2 (0xfL<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MCHECK (0x10L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MC (0x11L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_BC2 (0x12L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_BC3 (0x13L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_BSA1 (0x14L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_BSA2 (0x15L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_BSA3 (0x16L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_BTYPE (0x17L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_BC (0x18L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_PTYPE (0x19L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_CMD (0x1aL<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MAC (0x1bL<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_LATCH (0x1cL<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_XOFF (0x1dL<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_XON (0x1eL<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_PAUSED (0x1fL<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_NPAUSED (0x20L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_TTYPE (0x21L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_TVAL (0x22L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_USA1 (0x23L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_USA2 (0x24L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_USA3 (0x25L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_UTYPE (0x26L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_UTTYPE (0x27L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_UTVAL (0x28L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_MTYPE (0x29L<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_FILT_STATE_DROP (0x2aL<<16) +#define BNX2_EMAC_RXMAC_DEBUG4_DROP_PKT (1L<<22) +#define BNX2_EMAC_RXMAC_DEBUG4_SLOT_FILLED (1L<<23) +#define BNX2_EMAC_RXMAC_DEBUG4_FALSE_CARRIER (1L<<24) +#define BNX2_EMAC_RXMAC_DEBUG4_LAST_DATA (1L<<25) +#define BNX2_EMAC_RXMAC_DEBUG4_sfd_FOUND (1L<<26) +#define BNX2_EMAC_RXMAC_DEBUG4_ADVANCE (1L<<27) +#define BNX2_EMAC_RXMAC_DEBUG4_START (1L<<28) + +#define BNX2_EMAC_RXMAC_DEBUG5 0x00001570 +#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM (0x7L<<0) +#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM_IDLE (0L<<0) +#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM_WAIT_EOF (1L<<0) +#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM_WAIT_STAT (2L<<0) +#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM_SET_EOF4FCRC (3L<<0) +#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM_SET_EOF4RDE (4L<<0) +#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM_SET_EOF4ALL (5L<<0) +#define BNX2_EMAC_RXMAC_DEBUG5_PS_IDISM_1WD_WAIT_STAT (6L<<0) +#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1 (0x7L<<4) +#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1_VDW (0x0L<<4) +#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1_STAT (0x1L<<4) +#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1_AEOF (0x2L<<4) +#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1_NEOF (0x3L<<4) +#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1_SOF (0x4L<<4) +#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1_SAEOF (0x6L<<4) +#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF1_SNEOF (0x7L<<4) +#define BNX2_EMAC_RXMAC_DEBUG5_EOF_DETECTED (1L<<7) +#define BNX2_EMAC_RXMAC_DEBUG5_CCODE_BUF0 (0x7L<<8) +#define BNX2_EMAC_RXMAC_DEBUG5_RPM_IDI_FIFO_FULL (1L<<11) +#define BNX2_EMAC_RXMAC_DEBUG5_LOAD_CCODE (1L<<12) +#define BNX2_EMAC_RXMAC_DEBUG5_LOAD_DATA (1L<<13) +#define BNX2_EMAC_RXMAC_DEBUG5_LOAD_STAT (1L<<14) +#define BNX2_EMAC_RXMAC_DEBUG5_CLR_STAT (1L<<15) +#define BNX2_EMAC_RXMAC_DEBUG5_IDI_RPM_CCODE (0x3L<<16) +#define BNX2_EMAC_RXMAC_DEBUG5_IDI_RPM_ACCEPT (1L<<19) +#define BNX2_EMAC_RXMAC_DEBUG5_FMLEN (0xfffL<<20) + +#define BNX2_EMAC_RX_STAT_AC0 0x00001580 +#define BNX2_EMAC_RX_STAT_AC1 0x00001584 +#define BNX2_EMAC_RX_STAT_AC2 0x00001588 +#define BNX2_EMAC_RX_STAT_AC3 0x0000158c +#define BNX2_EMAC_RX_STAT_AC4 0x00001590 +#define BNX2_EMAC_RX_STAT_AC5 0x00001594 +#define BNX2_EMAC_RX_STAT_AC6 0x00001598 +#define BNX2_EMAC_RX_STAT_AC7 0x0000159c +#define BNX2_EMAC_RX_STAT_AC8 0x000015a0 +#define BNX2_EMAC_RX_STAT_AC9 0x000015a4 +#define BNX2_EMAC_RX_STAT_AC10 0x000015a8 +#define BNX2_EMAC_RX_STAT_AC11 0x000015ac +#define BNX2_EMAC_RX_STAT_AC12 0x000015b0 +#define BNX2_EMAC_RX_STAT_AC13 0x000015b4 +#define BNX2_EMAC_RX_STAT_AC14 0x000015b8 +#define BNX2_EMAC_RX_STAT_AC15 0x000015bc +#define BNX2_EMAC_RX_STAT_AC16 0x000015c0 +#define BNX2_EMAC_RX_STAT_AC17 0x000015c4 +#define BNX2_EMAC_RX_STAT_AC18 0x000015c8 +#define BNX2_EMAC_RX_STAT_AC19 0x000015cc +#define BNX2_EMAC_RX_STAT_AC20 0x000015d0 +#define BNX2_EMAC_RX_STAT_AC21 0x000015d4 +#define BNX2_EMAC_RX_STAT_AC22 0x000015d8 +#define BNX2_EMAC_RXMAC_SUC_DBG_OVERRUNVEC 0x000015dc +#define BNX2_EMAC_TX_STAT_IFHCOUTOCTETS 0x00001600 +#define BNX2_EMAC_TX_STAT_IFHCOUTBADOCTETS 0x00001604 +#define BNX2_EMAC_TX_STAT_ETHERSTATSCOLLISIONS 0x00001608 +#define BNX2_EMAC_TX_STAT_OUTXONSENT 0x0000160c +#define BNX2_EMAC_TX_STAT_OUTXOFFSENT 0x00001610 +#define BNX2_EMAC_TX_STAT_FLOWCONTROLDONE 0x00001614 +#define BNX2_EMAC_TX_STAT_DOT3STATSSINGLECOLLISIONFRAMES 0x00001618 +#define BNX2_EMAC_TX_STAT_DOT3STATSMULTIPLECOLLISIONFRAMES 0x0000161c +#define BNX2_EMAC_TX_STAT_DOT3STATSDEFERREDTRANSMISSIONS 0x00001620 +#define BNX2_EMAC_TX_STAT_DOT3STATSEXCESSIVECOLLISIONS 0x00001624 +#define BNX2_EMAC_TX_STAT_DOT3STATSLATECOLLISIONS 0x00001628 +#define BNX2_EMAC_TX_STAT_IFHCOUTUCASTPKTS 0x0000162c +#define BNX2_EMAC_TX_STAT_IFHCOUTMULTICASTPKTS 0x00001630 +#define BNX2_EMAC_TX_STAT_IFHCOUTBROADCASTPKTS 0x00001634 +#define BNX2_EMAC_TX_STAT_ETHERSTATSPKTS64OCTETS 0x00001638 +#define BNX2_EMAC_TX_STAT_ETHERSTATSPKTS65OCTETSTO127OCTETS 0x0000163c +#define BNX2_EMAC_TX_STAT_ETHERSTATSPKTS128OCTETSTO255OCTETS 0x00001640 +#define BNX2_EMAC_TX_STAT_ETHERSTATSPKTS256OCTETSTO511OCTETS 0x00001644 +#define BNX2_EMAC_TX_STAT_ETHERSTATSPKTS512OCTETSTO1023OCTETS 0x00001648 +#define BNX2_EMAC_TX_STAT_ETHERSTATSPKTS1024OCTETSTO1522OCTETS 0x0000164c +#define BNX2_EMAC_TX_STAT_ETHERSTATSPKTS1523OCTETSTO9022OCTETS 0x00001650 +#define BNX2_EMAC_TX_STAT_DOT3STATSINTERNALMACTRANSMITERRORS 0x00001654 +#define BNX2_EMAC_TXMAC_DEBUG0 0x00001658 +#define BNX2_EMAC_TXMAC_DEBUG1 0x0000165c +#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE (0xfL<<0) +#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_IDLE (0x0L<<0) +#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_START0 (0x1L<<0) +#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_DATA0 (0x4L<<0) +#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_DATA1 (0x5L<<0) +#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_DATA2 (0x6L<<0) +#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_DATA3 (0x7L<<0) +#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_WAIT0 (0x8L<<0) +#define BNX2_EMAC_TXMAC_DEBUG1_ODI_STATE_WAIT1 (0x9L<<0) +#define BNX2_EMAC_TXMAC_DEBUG1_CRS_ENABLE (1L<<4) +#define BNX2_EMAC_TXMAC_DEBUG1_BAD_CRC (1L<<5) +#define BNX2_EMAC_TXMAC_DEBUG1_SE_COUNTER (0xfL<<6) +#define BNX2_EMAC_TXMAC_DEBUG1_SEND_PAUSE (1L<<10) +#define BNX2_EMAC_TXMAC_DEBUG1_LATE_COLLISION (1L<<11) +#define BNX2_EMAC_TXMAC_DEBUG1_MAX_DEFER (1L<<12) +#define BNX2_EMAC_TXMAC_DEBUG1_DEFERRED (1L<<13) +#define BNX2_EMAC_TXMAC_DEBUG1_ONE_BYTE (1L<<14) +#define BNX2_EMAC_TXMAC_DEBUG1_IPG_TIME (0xfL<<15) +#define BNX2_EMAC_TXMAC_DEBUG1_SLOT_TIME (0xffL<<19) + +#define BNX2_EMAC_TXMAC_DEBUG2 0x00001660 +#define BNX2_EMAC_TXMAC_DEBUG2_BACK_OFF (0x3ffL<<0) +#define BNX2_EMAC_TXMAC_DEBUG2_BYTE_COUNT (0xffffL<<10) +#define BNX2_EMAC_TXMAC_DEBUG2_COL_COUNT (0x1fL<<26) +#define BNX2_EMAC_TXMAC_DEBUG2_COL_BIT (1L<<31) + +#define BNX2_EMAC_TXMAC_DEBUG3 0x00001664 +#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE (0xfL<<0) +#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_IDLE (0x0L<<0) +#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_PRE1 (0x1L<<0) +#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_PRE2 (0x2L<<0) +#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_SFD (0x3L<<0) +#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_DATA (0x4L<<0) +#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_CRC1 (0x5L<<0) +#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_CRC2 (0x6L<<0) +#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_EXT (0x7L<<0) +#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_STATB (0x8L<<0) +#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_STATG (0x9L<<0) +#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_JAM (0xaL<<0) +#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_EJAM (0xbL<<0) +#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_BJAM (0xcL<<0) +#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_SWAIT (0xdL<<0) +#define BNX2_EMAC_TXMAC_DEBUG3_SM_STATE_BACKOFF (0xeL<<0) +#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE (0x7L<<4) +#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE_IDLE (0x0L<<4) +#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE_WAIT (0x1L<<4) +#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE_UNI (0x2L<<4) +#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE_MC (0x3L<<4) +#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE_BC2 (0x4L<<4) +#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE_BC3 (0x5L<<4) +#define BNX2_EMAC_TXMAC_DEBUG3_FILT_STATE_BC (0x6L<<4) +#define BNX2_EMAC_TXMAC_DEBUG3_CRS_DONE (1L<<7) +#define BNX2_EMAC_TXMAC_DEBUG3_XOFF (1L<<8) +#define BNX2_EMAC_TXMAC_DEBUG3_SE_COUNTER (0xfL<<9) +#define BNX2_EMAC_TXMAC_DEBUG3_QUANTA_COUNTER (0x1fL<<13) + +#define BNX2_EMAC_TXMAC_DEBUG4 0x00001668 +#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_COUNTER (0xffffL<<0) +#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE (0xfL<<16) +#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_IDLE (0x0L<<16) +#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_MCA1 (0x2L<<16) +#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_MCA2 (0x3L<<16) +#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_MCA3 (0x6L<<16) +#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_SRC1 (0x7L<<16) +#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_SRC2 (0x5L<<16) +#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_SRC3 (0x4L<<16) +#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_TYPE (0xcL<<16) +#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_CMD (0xeL<<16) +#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_TIME (0xaL<<16) +#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_CRC1 (0x8L<<16) +#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_CRC2 (0x9L<<16) +#define BNX2_EMAC_TXMAC_DEBUG4_PAUSE_STATE_WAIT (0xdL<<16) +#define BNX2_EMAC_TXMAC_DEBUG4_STATS0_VALID (1L<<20) +#define BNX2_EMAC_TXMAC_DEBUG4_APPEND_CRC (1L<<21) +#define BNX2_EMAC_TXMAC_DEBUG4_SLOT_FILLED (1L<<22) +#define BNX2_EMAC_TXMAC_DEBUG4_MAX_DEFER (1L<<23) +#define BNX2_EMAC_TXMAC_DEBUG4_SEND_EXTEND (1L<<24) +#define BNX2_EMAC_TXMAC_DEBUG4_SEND_PADDING (1L<<25) +#define BNX2_EMAC_TXMAC_DEBUG4_EOF_LOC (1L<<26) +#define BNX2_EMAC_TXMAC_DEBUG4_COLLIDING (1L<<27) +#define BNX2_EMAC_TXMAC_DEBUG4_COL_IN (1L<<28) +#define BNX2_EMAC_TXMAC_DEBUG4_BURSTING (1L<<29) +#define BNX2_EMAC_TXMAC_DEBUG4_ADVANCE (1L<<30) +#define BNX2_EMAC_TXMAC_DEBUG4_GO (1L<<31) + +#define BNX2_EMAC_TX_STAT_AC0 0x00001680 +#define BNX2_EMAC_TX_STAT_AC1 0x00001684 +#define BNX2_EMAC_TX_STAT_AC2 0x00001688 +#define BNX2_EMAC_TX_STAT_AC3 0x0000168c +#define BNX2_EMAC_TX_STAT_AC4 0x00001690 +#define BNX2_EMAC_TX_STAT_AC5 0x00001694 +#define BNX2_EMAC_TX_STAT_AC6 0x00001698 +#define BNX2_EMAC_TX_STAT_AC7 0x0000169c +#define BNX2_EMAC_TX_STAT_AC8 0x000016a0 +#define BNX2_EMAC_TX_STAT_AC9 0x000016a4 +#define BNX2_EMAC_TX_STAT_AC10 0x000016a8 +#define BNX2_EMAC_TX_STAT_AC11 0x000016ac +#define BNX2_EMAC_TX_STAT_AC12 0x000016b0 +#define BNX2_EMAC_TX_STAT_AC13 0x000016b4 +#define BNX2_EMAC_TX_STAT_AC14 0x000016b8 +#define BNX2_EMAC_TX_STAT_AC15 0x000016bc +#define BNX2_EMAC_TX_STAT_AC16 0x000016c0 +#define BNX2_EMAC_TX_STAT_AC17 0x000016c4 +#define BNX2_EMAC_TX_STAT_AC18 0x000016c8 +#define BNX2_EMAC_TX_STAT_AC19 0x000016cc +#define BNX2_EMAC_TX_STAT_AC20 0x000016d0 +#define BNX2_EMAC_TX_STAT_AC21 0x000016d4 +#define BNX2_EMAC_TXMAC_SUC_DBG_OVERRUNVEC 0x000016d8 + + +/* + * rpm_reg definition + * offset: 0x1800 + */ +#define BNX2_RPM_COMMAND 0x00001800 +#define BNX2_RPM_COMMAND_ENABLED (1L<<0) +#define BNX2_RPM_COMMAND_OVERRUN_ABORT (1L<<4) + +#define BNX2_RPM_STATUS 0x00001804 +#define BNX2_RPM_STATUS_MBUF_WAIT (1L<<0) +#define BNX2_RPM_STATUS_FREE_WAIT (1L<<1) + +#define BNX2_RPM_CONFIG 0x00001808 +#define BNX2_RPM_CONFIG_NO_PSD_HDR_CKSUM (1L<<0) +#define BNX2_RPM_CONFIG_ACPI_ENA (1L<<1) +#define BNX2_RPM_CONFIG_ACPI_KEEP (1L<<2) +#define BNX2_RPM_CONFIG_MP_KEEP (1L<<3) +#define BNX2_RPM_CONFIG_SORT_VECT_VAL (0xfL<<4) +#define BNX2_RPM_CONFIG_IGNORE_VLAN (1L<<31) + +#define BNX2_RPM_VLAN_MATCH0 0x00001810 +#define BNX2_RPM_VLAN_MATCH0_RPM_VLAN_MTCH0_VALUE (0xfffL<<0) + +#define BNX2_RPM_VLAN_MATCH1 0x00001814 +#define BNX2_RPM_VLAN_MATCH1_RPM_VLAN_MTCH1_VALUE (0xfffL<<0) + +#define BNX2_RPM_VLAN_MATCH2 0x00001818 +#define BNX2_RPM_VLAN_MATCH2_RPM_VLAN_MTCH2_VALUE (0xfffL<<0) + +#define BNX2_RPM_VLAN_MATCH3 0x0000181c +#define BNX2_RPM_VLAN_MATCH3_RPM_VLAN_MTCH3_VALUE (0xfffL<<0) + +#define BNX2_RPM_SORT_USER0 0x00001820 +#define BNX2_RPM_SORT_USER0_PM_EN (0xffffL<<0) +#define BNX2_RPM_SORT_USER0_BC_EN (1L<<16) +#define BNX2_RPM_SORT_USER0_MC_EN (1L<<17) +#define BNX2_RPM_SORT_USER0_MC_HSH_EN (1L<<18) +#define BNX2_RPM_SORT_USER0_PROM_EN (1L<<19) +#define BNX2_RPM_SORT_USER0_VLAN_EN (0xfL<<20) +#define BNX2_RPM_SORT_USER0_PROM_VLAN (1L<<24) +#define BNX2_RPM_SORT_USER0_ENA (1L<<31) + +#define BNX2_RPM_SORT_USER1 0x00001824 +#define BNX2_RPM_SORT_USER1_PM_EN (0xffffL<<0) +#define BNX2_RPM_SORT_USER1_BC_EN (1L<<16) +#define BNX2_RPM_SORT_USER1_MC_EN (1L<<17) +#define BNX2_RPM_SORT_USER1_MC_HSH_EN (1L<<18) +#define BNX2_RPM_SORT_USER1_PROM_EN (1L<<19) +#define BNX2_RPM_SORT_USER1_VLAN_EN (0xfL<<20) +#define BNX2_RPM_SORT_USER1_PROM_VLAN (1L<<24) +#define BNX2_RPM_SORT_USER1_ENA (1L<<31) + +#define BNX2_RPM_SORT_USER2 0x00001828 +#define BNX2_RPM_SORT_USER2_PM_EN (0xffffL<<0) +#define BNX2_RPM_SORT_USER2_BC_EN (1L<<16) +#define BNX2_RPM_SORT_USER2_MC_EN (1L<<17) +#define BNX2_RPM_SORT_USER2_MC_HSH_EN (1L<<18) +#define BNX2_RPM_SORT_USER2_PROM_EN (1L<<19) +#define BNX2_RPM_SORT_USER2_VLAN_EN (0xfL<<20) +#define BNX2_RPM_SORT_USER2_PROM_VLAN (1L<<24) +#define BNX2_RPM_SORT_USER2_ENA (1L<<31) + +#define BNX2_RPM_SORT_USER3 0x0000182c +#define BNX2_RPM_SORT_USER3_PM_EN (0xffffL<<0) +#define BNX2_RPM_SORT_USER3_BC_EN (1L<<16) +#define BNX2_RPM_SORT_USER3_MC_EN (1L<<17) +#define BNX2_RPM_SORT_USER3_MC_HSH_EN (1L<<18) +#define BNX2_RPM_SORT_USER3_PROM_EN (1L<<19) +#define BNX2_RPM_SORT_USER3_VLAN_EN (0xfL<<20) +#define BNX2_RPM_SORT_USER3_PROM_VLAN (1L<<24) +#define BNX2_RPM_SORT_USER3_ENA (1L<<31) + +#define BNX2_RPM_STAT_L2_FILTER_DISCARDS 0x00001840 +#define BNX2_RPM_STAT_RULE_CHECKER_DISCARDS 0x00001844 +#define BNX2_RPM_STAT_IFINFTQDISCARDS 0x00001848 +#define BNX2_RPM_STAT_IFINMBUFDISCARD 0x0000184c +#define BNX2_RPM_STAT_RULE_CHECKER_P4_HIT 0x00001850 +#define BNX2_RPM_STAT_AC0 0x00001880 +#define BNX2_RPM_STAT_AC1 0x00001884 +#define BNX2_RPM_STAT_AC2 0x00001888 +#define BNX2_RPM_STAT_AC3 0x0000188c +#define BNX2_RPM_STAT_AC4 0x00001890 +#define BNX2_RPM_RC_CNTL_0 0x00001900 +#define BNX2_RPM_RC_CNTL_0_OFFSET (0xffL<<0) +#define BNX2_RPM_RC_CNTL_0_CLASS (0x7L<<8) +#define BNX2_RPM_RC_CNTL_0_PRIORITY (1L<<11) +#define BNX2_RPM_RC_CNTL_0_P4 (1L<<12) +#define BNX2_RPM_RC_CNTL_0_HDR_TYPE (0x7L<<13) +#define BNX2_RPM_RC_CNTL_0_HDR_TYPE_START (0L<<13) +#define BNX2_RPM_RC_CNTL_0_HDR_TYPE_IP (1L<<13) +#define BNX2_RPM_RC_CNTL_0_HDR_TYPE_TCP (2L<<13) +#define BNX2_RPM_RC_CNTL_0_HDR_TYPE_UDP (3L<<13) +#define BNX2_RPM_RC_CNTL_0_HDR_TYPE_DATA (4L<<13) +#define BNX2_RPM_RC_CNTL_0_COMP (0x3L<<16) +#define BNX2_RPM_RC_CNTL_0_COMP_EQUAL (0L<<16) +#define BNX2_RPM_RC_CNTL_0_COMP_NEQUAL (1L<<16) +#define BNX2_RPM_RC_CNTL_0_COMP_GREATER (2L<<16) +#define BNX2_RPM_RC_CNTL_0_COMP_LESS (3L<<16) +#define BNX2_RPM_RC_CNTL_0_SBIT (1L<<19) +#define BNX2_RPM_RC_CNTL_0_CMDSEL (0xfL<<20) +#define BNX2_RPM_RC_CNTL_0_MAP (1L<<24) +#define BNX2_RPM_RC_CNTL_0_DISCARD (1L<<25) +#define BNX2_RPM_RC_CNTL_0_MASK (1L<<26) +#define BNX2_RPM_RC_CNTL_0_P1 (1L<<27) +#define BNX2_RPM_RC_CNTL_0_P2 (1L<<28) +#define BNX2_RPM_RC_CNTL_0_P3 (1L<<29) +#define BNX2_RPM_RC_CNTL_0_NBIT (1L<<30) + +#define BNX2_RPM_RC_VALUE_MASK_0 0x00001904 +#define BNX2_RPM_RC_VALUE_MASK_0_VALUE (0xffffL<<0) +#define BNX2_RPM_RC_VALUE_MASK_0_MASK (0xffffL<<16) + +#define BNX2_RPM_RC_CNTL_1 0x00001908 +#define BNX2_RPM_RC_CNTL_1_A (0x3ffffL<<0) +#define BNX2_RPM_RC_CNTL_1_B (0xfffL<<19) + +#define BNX2_RPM_RC_VALUE_MASK_1 0x0000190c +#define BNX2_RPM_RC_CNTL_2 0x00001910 +#define BNX2_RPM_RC_CNTL_2_A (0x3ffffL<<0) +#define BNX2_RPM_RC_CNTL_2_B (0xfffL<<19) + +#define BNX2_RPM_RC_VALUE_MASK_2 0x00001914 +#define BNX2_RPM_RC_CNTL_3 0x00001918 +#define BNX2_RPM_RC_CNTL_3_A (0x3ffffL<<0) +#define BNX2_RPM_RC_CNTL_3_B (0xfffL<<19) + +#define BNX2_RPM_RC_VALUE_MASK_3 0x0000191c +#define BNX2_RPM_RC_CNTL_4 0x00001920 +#define BNX2_RPM_RC_CNTL_4_A (0x3ffffL<<0) +#define BNX2_RPM_RC_CNTL_4_B (0xfffL<<19) + +#define BNX2_RPM_RC_VALUE_MASK_4 0x00001924 +#define BNX2_RPM_RC_CNTL_5 0x00001928 +#define BNX2_RPM_RC_CNTL_5_A (0x3ffffL<<0) +#define BNX2_RPM_RC_CNTL_5_B (0xfffL<<19) + +#define BNX2_RPM_RC_VALUE_MASK_5 0x0000192c +#define BNX2_RPM_RC_CNTL_6 0x00001930 +#define BNX2_RPM_RC_CNTL_6_A (0x3ffffL<<0) +#define BNX2_RPM_RC_CNTL_6_B (0xfffL<<19) + +#define BNX2_RPM_RC_VALUE_MASK_6 0x00001934 +#define BNX2_RPM_RC_CNTL_7 0x00001938 +#define BNX2_RPM_RC_CNTL_7_A (0x3ffffL<<0) +#define BNX2_RPM_RC_CNTL_7_B (0xfffL<<19) + +#define BNX2_RPM_RC_VALUE_MASK_7 0x0000193c +#define BNX2_RPM_RC_CNTL_8 0x00001940 +#define BNX2_RPM_RC_CNTL_8_A (0x3ffffL<<0) +#define BNX2_RPM_RC_CNTL_8_B (0xfffL<<19) + +#define BNX2_RPM_RC_VALUE_MASK_8 0x00001944 +#define BNX2_RPM_RC_CNTL_9 0x00001948 +#define BNX2_RPM_RC_CNTL_9_A (0x3ffffL<<0) +#define BNX2_RPM_RC_CNTL_9_B (0xfffL<<19) + +#define BNX2_RPM_RC_VALUE_MASK_9 0x0000194c +#define BNX2_RPM_RC_CNTL_10 0x00001950 +#define BNX2_RPM_RC_CNTL_10_A (0x3ffffL<<0) +#define BNX2_RPM_RC_CNTL_10_B (0xfffL<<19) + +#define BNX2_RPM_RC_VALUE_MASK_10 0x00001954 +#define BNX2_RPM_RC_CNTL_11 0x00001958 +#define BNX2_RPM_RC_CNTL_11_A (0x3ffffL<<0) +#define BNX2_RPM_RC_CNTL_11_B (0xfffL<<19) + +#define BNX2_RPM_RC_VALUE_MASK_11 0x0000195c +#define BNX2_RPM_RC_CNTL_12 0x00001960 +#define BNX2_RPM_RC_CNTL_12_A (0x3ffffL<<0) +#define BNX2_RPM_RC_CNTL_12_B (0xfffL<<19) + +#define BNX2_RPM_RC_VALUE_MASK_12 0x00001964 +#define BNX2_RPM_RC_CNTL_13 0x00001968 +#define BNX2_RPM_RC_CNTL_13_A (0x3ffffL<<0) +#define BNX2_RPM_RC_CNTL_13_B (0xfffL<<19) + +#define BNX2_RPM_RC_VALUE_MASK_13 0x0000196c +#define BNX2_RPM_RC_CNTL_14 0x00001970 +#define BNX2_RPM_RC_CNTL_14_A (0x3ffffL<<0) +#define BNX2_RPM_RC_CNTL_14_B (0xfffL<<19) + +#define BNX2_RPM_RC_VALUE_MASK_14 0x00001974 +#define BNX2_RPM_RC_CNTL_15 0x00001978 +#define BNX2_RPM_RC_CNTL_15_A (0x3ffffL<<0) +#define BNX2_RPM_RC_CNTL_15_B (0xfffL<<19) + +#define BNX2_RPM_RC_VALUE_MASK_15 0x0000197c +#define BNX2_RPM_RC_CONFIG 0x00001980 +#define BNX2_RPM_RC_CONFIG_RULE_ENABLE (0xffffL<<0) +#define BNX2_RPM_RC_CONFIG_DEF_CLASS (0x7L<<24) + +#define BNX2_RPM_DEBUG0 0x00001984 +#define BNX2_RPM_DEBUG0_FM_BCNT (0xffffL<<0) +#define BNX2_RPM_DEBUG0_T_DATA_OFST_VLD (1L<<16) +#define BNX2_RPM_DEBUG0_T_UDP_OFST_VLD (1L<<17) +#define BNX2_RPM_DEBUG0_T_TCP_OFST_VLD (1L<<18) +#define BNX2_RPM_DEBUG0_T_IP_OFST_VLD (1L<<19) +#define BNX2_RPM_DEBUG0_IP_MORE_FRGMT (1L<<20) +#define BNX2_RPM_DEBUG0_T_IP_NO_TCP_UDP_HDR (1L<<21) +#define BNX2_RPM_DEBUG0_LLC_SNAP (1L<<22) +#define BNX2_RPM_DEBUG0_FM_STARTED (1L<<23) +#define BNX2_RPM_DEBUG0_DONE (1L<<24) +#define BNX2_RPM_DEBUG0_WAIT_4_DONE (1L<<25) +#define BNX2_RPM_DEBUG0_USE_TPBUF_CKSUM (1L<<26) +#define BNX2_RPM_DEBUG0_RX_NO_PSD_HDR_CKSUM (1L<<27) +#define BNX2_RPM_DEBUG0_IGNORE_VLAN (1L<<28) +#define BNX2_RPM_DEBUG0_RP_ENA_ACTIVE (1L<<31) + +#define BNX2_RPM_DEBUG1 0x00001988 +#define BNX2_RPM_DEBUG1_FSM_CUR_ST (0xffffL<<0) +#define BNX2_RPM_DEBUG1_FSM_CUR_ST_IDLE (0L<<0) +#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ETYPE_B6_ALL (1L<<0) +#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ETYPE_B2_IPLLC (2L<<0) +#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ETYPE_B6_IP (4L<<0) +#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ETYPE_B2_IP (8L<<0) +#define BNX2_RPM_DEBUG1_FSM_CUR_ST_IP_START (16L<<0) +#define BNX2_RPM_DEBUG1_FSM_CUR_ST_IP (32L<<0) +#define BNX2_RPM_DEBUG1_FSM_CUR_ST_TCP (64L<<0) +#define BNX2_RPM_DEBUG1_FSM_CUR_ST_UDP (128L<<0) +#define BNX2_RPM_DEBUG1_FSM_CUR_ST_AH (256L<<0) +#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ESP (512L<<0) +#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ESP_PAYLOAD (1024L<<0) +#define BNX2_RPM_DEBUG1_FSM_CUR_ST_DATA (2048L<<0) +#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ADD_CARRY (0x2000L<<0) +#define BNX2_RPM_DEBUG1_FSM_CUR_ST_ADD_CARRYOUT (0x4000L<<0) +#define BNX2_RPM_DEBUG1_FSM_CUR_ST_LATCH_RESULT (0x8000L<<0) +#define BNX2_RPM_DEBUG1_HDR_BCNT (0x7ffL<<16) +#define BNX2_RPM_DEBUG1_UNKNOWN_ETYPE_D (1L<<28) +#define BNX2_RPM_DEBUG1_VLAN_REMOVED_D2 (1L<<29) +#define BNX2_RPM_DEBUG1_VLAN_REMOVED_D1 (1L<<30) +#define BNX2_RPM_DEBUG1_EOF_0XTRA_WD (1L<<31) + +#define BNX2_RPM_DEBUG2 0x0000198c +#define BNX2_RPM_DEBUG2_CMD_HIT_VEC (0xffffL<<0) +#define BNX2_RPM_DEBUG2_IP_BCNT (0xffL<<16) +#define BNX2_RPM_DEBUG2_THIS_CMD_M4 (1L<<24) +#define BNX2_RPM_DEBUG2_THIS_CMD_M3 (1L<<25) +#define BNX2_RPM_DEBUG2_THIS_CMD_M2 (1L<<26) +#define BNX2_RPM_DEBUG2_THIS_CMD_M1 (1L<<27) +#define BNX2_RPM_DEBUG2_IPIPE_EMPTY (1L<<28) +#define BNX2_RPM_DEBUG2_FM_DISCARD (1L<<29) +#define BNX2_RPM_DEBUG2_LAST_RULE_IN_FM_D2 (1L<<30) +#define BNX2_RPM_DEBUG2_LAST_RULE_IN_FM_D1 (1L<<31) + +#define BNX2_RPM_DEBUG3 0x00001990 +#define BNX2_RPM_DEBUG3_AVAIL_MBUF_PTR (0x1ffL<<0) +#define BNX2_RPM_DEBUG3_RDE_RLUPQ_WR_REQ_INT (1L<<9) +#define BNX2_RPM_DEBUG3_RDE_RBUF_WR_LAST_INT (1L<<10) +#define BNX2_RPM_DEBUG3_RDE_RBUF_WR_REQ_INT (1L<<11) +#define BNX2_RPM_DEBUG3_RDE_RBUF_FREE_REQ (1L<<12) +#define BNX2_RPM_DEBUG3_RDE_RBUF_ALLOC_REQ (1L<<13) +#define BNX2_RPM_DEBUG3_DFSM_MBUF_NOTAVAIL (1L<<14) +#define BNX2_RPM_DEBUG3_RBUF_RDE_SOF_DROP (1L<<15) +#define BNX2_RPM_DEBUG3_DFIFO_VLD_ENTRY_CT (0xfL<<16) +#define BNX2_RPM_DEBUG3_RDE_SRC_FIFO_ALMFULL (1L<<21) +#define BNX2_RPM_DEBUG3_DROP_NXT_VLD (1L<<22) +#define BNX2_RPM_DEBUG3_DROP_NXT (1L<<23) +#define BNX2_RPM_DEBUG3_FTQ_FSM (0x3L<<24) +#define BNX2_RPM_DEBUG3_FTQ_FSM_IDLE (0x0L<<24) +#define BNX2_RPM_DEBUG3_FTQ_FSM_WAIT_ACK (0x1L<<24) +#define BNX2_RPM_DEBUG3_FTQ_FSM_WAIT_FREE (0x2L<<24) +#define BNX2_RPM_DEBUG3_MBWRITE_FSM (0x3L<<26) +#define BNX2_RPM_DEBUG3_MBWRITE_FSM_WAIT_SOF (0x0L<<26) +#define BNX2_RPM_DEBUG3_MBWRITE_FSM_GET_MBUF (0x1L<<26) +#define BNX2_RPM_DEBUG3_MBWRITE_FSM_DMA_DATA (0x2L<<26) +#define BNX2_RPM_DEBUG3_MBWRITE_FSM_WAIT_DATA (0x3L<<26) +#define BNX2_RPM_DEBUG3_MBWRITE_FSM_WAIT_EOF (0x4L<<26) +#define BNX2_RPM_DEBUG3_MBWRITE_FSM_WAIT_MF_ACK (0x5L<<26) +#define BNX2_RPM_DEBUG3_MBWRITE_FSM_WAIT_DROP_NXT_VLD (0x6L<<26) +#define BNX2_RPM_DEBUG3_MBWRITE_FSM_DONE (0x7L<<26) +#define BNX2_RPM_DEBUG3_MBFREE_FSM (1L<<29) +#define BNX2_RPM_DEBUG3_MBFREE_FSM_IDLE (0L<<29) +#define BNX2_RPM_DEBUG3_MBFREE_FSM_WAIT_ACK (1L<<29) +#define BNX2_RPM_DEBUG3_MBALLOC_FSM (1L<<30) +#define BNX2_RPM_DEBUG3_MBALLOC_FSM_ET_MBUF (0x0L<<30) +#define BNX2_RPM_DEBUG3_MBALLOC_FSM_IVE_MBUF (0x1L<<30) +#define BNX2_RPM_DEBUG3_CCODE_EOF_ERROR (1L<<31) + +#define BNX2_RPM_DEBUG4 0x00001994 +#define BNX2_RPM_DEBUG4_DFSM_MBUF_CLUSTER (0x1ffffffL<<0) +#define BNX2_RPM_DEBUG4_DFIFO_CUR_CCODE (0x7L<<25) +#define BNX2_RPM_DEBUG4_MBWRITE_FSM (0x7L<<28) +#define BNX2_RPM_DEBUG4_DFIFO_EMPTY (1L<<31) + +#define BNX2_RPM_DEBUG5 0x00001998 +#define BNX2_RPM_DEBUG5_RDROP_WPTR (0x1fL<<0) +#define BNX2_RPM_DEBUG5_RDROP_ACPI_RPTR (0x1fL<<5) +#define BNX2_RPM_DEBUG5_RDROP_MC_RPTR (0x1fL<<10) +#define BNX2_RPM_DEBUG5_RDROP_RC_RPTR (0x1fL<<15) +#define BNX2_RPM_DEBUG5_RDROP_ACPI_EMPTY (1L<<20) +#define BNX2_RPM_DEBUG5_RDROP_MC_EMPTY (1L<<21) +#define BNX2_RPM_DEBUG5_RDROP_AEOF_VEC_AT_RDROP_MC_RPTR (1L<<22) +#define BNX2_RPM_DEBUG5_HOLDREG_WOL_DROP_INT (1L<<23) +#define BNX2_RPM_DEBUG5_HOLDREG_DISCARD (1L<<24) +#define BNX2_RPM_DEBUG5_HOLDREG_MBUF_NOTAVAIL (1L<<25) +#define BNX2_RPM_DEBUG5_HOLDREG_MC_EMPTY (1L<<26) +#define BNX2_RPM_DEBUG5_HOLDREG_RC_EMPTY (1L<<27) +#define BNX2_RPM_DEBUG5_HOLDREG_FC_EMPTY (1L<<28) +#define BNX2_RPM_DEBUG5_HOLDREG_ACPI_EMPTY (1L<<29) +#define BNX2_RPM_DEBUG5_HOLDREG_FULL_T (1L<<30) +#define BNX2_RPM_DEBUG5_HOLDREG_RD (1L<<31) + +#define BNX2_RPM_DEBUG6 0x0000199c +#define BNX2_RPM_DEBUG6_ACPI_VEC (0xffffL<<0) +#define BNX2_RPM_DEBUG6_VEC (0xffffL<<16) + +#define BNX2_RPM_DEBUG7 0x000019a0 +#define BNX2_RPM_DEBUG7_RPM_DBG7_LAST_CRC (0xffffffffL<<0) + +#define BNX2_RPM_DEBUG8 0x000019a4 +#define BNX2_RPM_DEBUG8_PS_ACPI_FSM (0xfL<<0) +#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_IDLE (0L<<0) +#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_SOF_W1_ADDR (1L<<0) +#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_SOF_W2_ADDR (2L<<0) +#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_SOF_W3_ADDR (3L<<0) +#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_SOF_WAIT_THBUF (4L<<0) +#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_W3_DATA (5L<<0) +#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_W0_ADDR (6L<<0) +#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_W1_ADDR (7L<<0) +#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_W2_ADDR (8L<<0) +#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_W3_ADDR (9L<<0) +#define BNX2_RPM_DEBUG8_PS_ACPI_FSM_WAIT_THBUF (10L<<0) +#define BNX2_RPM_DEBUG8_COMPARE_AT_W0 (1L<<4) +#define BNX2_RPM_DEBUG8_COMPARE_AT_W3_DATA (1L<<5) +#define BNX2_RPM_DEBUG8_COMPARE_AT_SOF_WAIT (1L<<6) +#define BNX2_RPM_DEBUG8_COMPARE_AT_SOF_W3 (1L<<7) +#define BNX2_RPM_DEBUG8_COMPARE_AT_SOF_W2 (1L<<8) +#define BNX2_RPM_DEBUG8_EOF_W_LTEQ6_VLDBYTES (1L<<9) +#define BNX2_RPM_DEBUG8_EOF_W_LTEQ4_VLDBYTES (1L<<10) +#define BNX2_RPM_DEBUG8_NXT_EOF_W_12_VLDBYTES (1L<<11) +#define BNX2_RPM_DEBUG8_EOF_DET (1L<<12) +#define BNX2_RPM_DEBUG8_SOF_DET (1L<<13) +#define BNX2_RPM_DEBUG8_WAIT_4_SOF (1L<<14) +#define BNX2_RPM_DEBUG8_ALL_DONE (1L<<15) +#define BNX2_RPM_DEBUG8_THBUF_ADDR (0x7fL<<16) +#define BNX2_RPM_DEBUG8_BYTE_CTR (0xffL<<24) + +#define BNX2_RPM_DEBUG9 0x000019a8 +#define BNX2_RPM_DEBUG9_OUTFIFO_COUNT (0x7L<<0) +#define BNX2_RPM_DEBUG9_RDE_ACPI_RDY (1L<<3) +#define BNX2_RPM_DEBUG9_VLD_RD_ENTRY_CT (0x7L<<4) +#define BNX2_RPM_DEBUG9_OUTFIFO_OVERRUN_OCCURRED (1L<<28) +#define BNX2_RPM_DEBUG9_INFIFO_OVERRUN_OCCURRED (1L<<29) +#define BNX2_RPM_DEBUG9_ACPI_MATCH_INT (1L<<30) +#define BNX2_RPM_DEBUG9_ACPI_ENABLE_SYN (1L<<31) + +#define BNX2_RPM_ACPI_DBG_BUF_W00 0x000019c0 +#define BNX2_RPM_ACPI_DBG_BUF_W01 0x000019c4 +#define BNX2_RPM_ACPI_DBG_BUF_W02 0x000019c8 +#define BNX2_RPM_ACPI_DBG_BUF_W03 0x000019cc +#define BNX2_RPM_ACPI_DBG_BUF_W10 0x000019d0 +#define BNX2_RPM_ACPI_DBG_BUF_W11 0x000019d4 +#define BNX2_RPM_ACPI_DBG_BUF_W12 0x000019d8 +#define BNX2_RPM_ACPI_DBG_BUF_W13 0x000019dc +#define BNX2_RPM_ACPI_DBG_BUF_W20 0x000019e0 +#define BNX2_RPM_ACPI_DBG_BUF_W21 0x000019e4 +#define BNX2_RPM_ACPI_DBG_BUF_W22 0x000019e8 +#define BNX2_RPM_ACPI_DBG_BUF_W23 0x000019ec +#define BNX2_RPM_ACPI_DBG_BUF_W30 0x000019f0 +#define BNX2_RPM_ACPI_DBG_BUF_W31 0x000019f4 +#define BNX2_RPM_ACPI_DBG_BUF_W32 0x000019f8 +#define BNX2_RPM_ACPI_DBG_BUF_W33 0x000019fc + + +/* + * rbuf_reg definition + * offset: 0x200000 + */ +#define BNX2_RBUF_COMMAND 0x00200000 +#define BNX2_RBUF_COMMAND_ENABLED (1L<<0) +#define BNX2_RBUF_COMMAND_FREE_INIT (1L<<1) +#define BNX2_RBUF_COMMAND_RAM_INIT (1L<<2) +#define BNX2_RBUF_COMMAND_OVER_FREE (1L<<4) +#define BNX2_RBUF_COMMAND_ALLOC_REQ (1L<<5) + +#define BNX2_RBUF_STATUS1 0x00200004 +#define BNX2_RBUF_STATUS1_FREE_COUNT (0x3ffL<<0) + +#define BNX2_RBUF_STATUS2 0x00200008 +#define BNX2_RBUF_STATUS2_FREE_TAIL (0x3ffL<<0) +#define BNX2_RBUF_STATUS2_FREE_HEAD (0x3ffL<<16) + +#define BNX2_RBUF_CONFIG 0x0020000c +#define BNX2_RBUF_CONFIG_XOFF_TRIP (0x3ffL<<0) +#define BNX2_RBUF_CONFIG_XON_TRIP (0x3ffL<<16) + +#define BNX2_RBUF_FW_BUF_ALLOC 0x00200010 +#define BNX2_RBUF_FW_BUF_ALLOC_VALUE (0x1ffL<<7) + +#define BNX2_RBUF_FW_BUF_FREE 0x00200014 +#define BNX2_RBUF_FW_BUF_FREE_COUNT (0x7fL<<0) +#define BNX2_RBUF_FW_BUF_FREE_TAIL (0x1ffL<<7) +#define BNX2_RBUF_FW_BUF_FREE_HEAD (0x1ffL<<16) + +#define BNX2_RBUF_FW_BUF_SEL 0x00200018 +#define BNX2_RBUF_FW_BUF_SEL_COUNT (0x7fL<<0) +#define BNX2_RBUF_FW_BUF_SEL_TAIL (0x1ffL<<7) +#define BNX2_RBUF_FW_BUF_SEL_HEAD (0x1ffL<<16) + +#define BNX2_RBUF_CONFIG2 0x0020001c +#define BNX2_RBUF_CONFIG2_MAC_DROP_TRIP (0x3ffL<<0) +#define BNX2_RBUF_CONFIG2_MAC_KEEP_TRIP (0x3ffL<<16) + +#define BNX2_RBUF_CONFIG3 0x00200020 +#define BNX2_RBUF_CONFIG3_CU_DROP_TRIP (0x3ffL<<0) +#define BNX2_RBUF_CONFIG3_CU_KEEP_TRIP (0x3ffL<<16) + +#define BNX2_RBUF_PKT_DATA 0x00208000 +#define BNX2_RBUF_CLIST_DATA 0x00210000 +#define BNX2_RBUF_BUF_DATA 0x00220000 + + +/* + * rv2p_reg definition + * offset: 0x2800 + */ +#define BNX2_RV2P_COMMAND 0x00002800 +#define BNX2_RV2P_COMMAND_ENABLED (1L<<0) +#define BNX2_RV2P_COMMAND_PROC1_INTRPT (1L<<1) +#define BNX2_RV2P_COMMAND_PROC2_INTRPT (1L<<2) +#define BNX2_RV2P_COMMAND_ABORT0 (1L<<4) +#define BNX2_RV2P_COMMAND_ABORT1 (1L<<5) +#define BNX2_RV2P_COMMAND_ABORT2 (1L<<6) +#define BNX2_RV2P_COMMAND_ABORT3 (1L<<7) +#define BNX2_RV2P_COMMAND_ABORT4 (1L<<8) +#define BNX2_RV2P_COMMAND_ABORT5 (1L<<9) +#define BNX2_RV2P_COMMAND_PROC1_RESET (1L<<16) +#define BNX2_RV2P_COMMAND_PROC2_RESET (1L<<17) +#define BNX2_RV2P_COMMAND_CTXIF_RESET (1L<<18) + +#define BNX2_RV2P_STATUS 0x00002804 +#define BNX2_RV2P_STATUS_ALWAYS_0 (1L<<0) +#define BNX2_RV2P_STATUS_RV2P_GEN_STAT0_CNT (1L<<8) +#define BNX2_RV2P_STATUS_RV2P_GEN_STAT1_CNT (1L<<9) +#define BNX2_RV2P_STATUS_RV2P_GEN_STAT2_CNT (1L<<10) +#define BNX2_RV2P_STATUS_RV2P_GEN_STAT3_CNT (1L<<11) +#define BNX2_RV2P_STATUS_RV2P_GEN_STAT4_CNT (1L<<12) +#define BNX2_RV2P_STATUS_RV2P_GEN_STAT5_CNT (1L<<13) + +#define BNX2_RV2P_CONFIG 0x00002808 +#define BNX2_RV2P_CONFIG_STALL_PROC1 (1L<<0) +#define BNX2_RV2P_CONFIG_STALL_PROC2 (1L<<1) +#define BNX2_RV2P_CONFIG_PROC1_STALL_ON_ABORT0 (1L<<8) +#define BNX2_RV2P_CONFIG_PROC1_STALL_ON_ABORT1 (1L<<9) +#define BNX2_RV2P_CONFIG_PROC1_STALL_ON_ABORT2 (1L<<10) +#define BNX2_RV2P_CONFIG_PROC1_STALL_ON_ABORT3 (1L<<11) +#define BNX2_RV2P_CONFIG_PROC1_STALL_ON_ABORT4 (1L<<12) +#define BNX2_RV2P_CONFIG_PROC1_STALL_ON_ABORT5 (1L<<13) +#define BNX2_RV2P_CONFIG_PROC2_STALL_ON_ABORT0 (1L<<16) +#define BNX2_RV2P_CONFIG_PROC2_STALL_ON_ABORT1 (1L<<17) +#define BNX2_RV2P_CONFIG_PROC2_STALL_ON_ABORT2 (1L<<18) +#define BNX2_RV2P_CONFIG_PROC2_STALL_ON_ABORT3 (1L<<19) +#define BNX2_RV2P_CONFIG_PROC2_STALL_ON_ABORT4 (1L<<20) +#define BNX2_RV2P_CONFIG_PROC2_STALL_ON_ABORT5 (1L<<21) +#define BNX2_RV2P_CONFIG_PAGE_SIZE (0xfL<<24) +#define BNX2_RV2P_CONFIG_PAGE_SIZE_256 (0L<<24) +#define BNX2_RV2P_CONFIG_PAGE_SIZE_512 (1L<<24) +#define BNX2_RV2P_CONFIG_PAGE_SIZE_1K (2L<<24) +#define BNX2_RV2P_CONFIG_PAGE_SIZE_2K (3L<<24) +#define BNX2_RV2P_CONFIG_PAGE_SIZE_4K (4L<<24) +#define BNX2_RV2P_CONFIG_PAGE_SIZE_8K (5L<<24) +#define BNX2_RV2P_CONFIG_PAGE_SIZE_16K (6L<<24) +#define BNX2_RV2P_CONFIG_PAGE_SIZE_32K (7L<<24) +#define BNX2_RV2P_CONFIG_PAGE_SIZE_64K (8L<<24) +#define BNX2_RV2P_CONFIG_PAGE_SIZE_128K (9L<<24) +#define BNX2_RV2P_CONFIG_PAGE_SIZE_256K (10L<<24) +#define BNX2_RV2P_CONFIG_PAGE_SIZE_512K (11L<<24) +#define BNX2_RV2P_CONFIG_PAGE_SIZE_1M (12L<<24) + +#define BNX2_RV2P_GEN_BFR_ADDR_0 0x00002810 +#define BNX2_RV2P_GEN_BFR_ADDR_0_VALUE (0xffffL<<16) + +#define BNX2_RV2P_GEN_BFR_ADDR_1 0x00002814 +#define BNX2_RV2P_GEN_BFR_ADDR_1_VALUE (0xffffL<<16) + +#define BNX2_RV2P_GEN_BFR_ADDR_2 0x00002818 +#define BNX2_RV2P_GEN_BFR_ADDR_2_VALUE (0xffffL<<16) + +#define BNX2_RV2P_GEN_BFR_ADDR_3 0x0000281c +#define BNX2_RV2P_GEN_BFR_ADDR_3_VALUE (0xffffL<<16) + +#define BNX2_RV2P_INSTR_HIGH 0x00002830 +#define BNX2_RV2P_INSTR_HIGH_HIGH (0x1fL<<0) + +#define BNX2_RV2P_INSTR_LOW 0x00002834 +#define BNX2_RV2P_PROC1_ADDR_CMD 0x00002838 +#define BNX2_RV2P_PROC1_ADDR_CMD_ADD (0x3ffL<<0) +#define BNX2_RV2P_PROC1_ADDR_CMD_RDWR (1L<<31) + +#define BNX2_RV2P_PROC2_ADDR_CMD 0x0000283c +#define BNX2_RV2P_PROC2_ADDR_CMD_ADD (0x3ffL<<0) +#define BNX2_RV2P_PROC2_ADDR_CMD_RDWR (1L<<31) + +#define BNX2_RV2P_PROC1_GRC_DEBUG 0x00002840 +#define BNX2_RV2P_PROC2_GRC_DEBUG 0x00002844 +#define BNX2_RV2P_GRC_PROC_DEBUG 0x00002848 +#define BNX2_RV2P_DEBUG_VECT_PEEK 0x0000284c +#define BNX2_RV2P_DEBUG_VECT_PEEK_1_VALUE (0x7ffL<<0) +#define BNX2_RV2P_DEBUG_VECT_PEEK_1_PEEK_EN (1L<<11) +#define BNX2_RV2P_DEBUG_VECT_PEEK_1_SEL (0xfL<<12) +#define BNX2_RV2P_DEBUG_VECT_PEEK_2_VALUE (0x7ffL<<16) +#define BNX2_RV2P_DEBUG_VECT_PEEK_2_PEEK_EN (1L<<27) +#define BNX2_RV2P_DEBUG_VECT_PEEK_2_SEL (0xfL<<28) + +#define BNX2_RV2P_PFTQ_DATA 0x00002b40 +#define BNX2_RV2P_PFTQ_CMD 0x00002b78 +#define BNX2_RV2P_PFTQ_CMD_OFFSET (0x3ffL<<0) +#define BNX2_RV2P_PFTQ_CMD_WR_TOP (1L<<10) +#define BNX2_RV2P_PFTQ_CMD_WR_TOP_0 (0L<<10) +#define BNX2_RV2P_PFTQ_CMD_WR_TOP_1 (1L<<10) +#define BNX2_RV2P_PFTQ_CMD_SFT_RESET (1L<<25) +#define BNX2_RV2P_PFTQ_CMD_RD_DATA (1L<<26) +#define BNX2_RV2P_PFTQ_CMD_ADD_INTERVEN (1L<<27) +#define BNX2_RV2P_PFTQ_CMD_ADD_DATA (1L<<28) +#define BNX2_RV2P_PFTQ_CMD_INTERVENE_CLR (1L<<29) +#define BNX2_RV2P_PFTQ_CMD_POP (1L<<30) +#define BNX2_RV2P_PFTQ_CMD_BUSY (1L<<31) + +#define BNX2_RV2P_PFTQ_CTL 0x00002b7c +#define BNX2_RV2P_PFTQ_CTL_INTERVENE (1L<<0) +#define BNX2_RV2P_PFTQ_CTL_OVERFLOW (1L<<1) +#define BNX2_RV2P_PFTQ_CTL_FORCE_INTERVENE (1L<<2) +#define BNX2_RV2P_PFTQ_CTL_MAX_DEPTH (0x3ffL<<12) +#define BNX2_RV2P_PFTQ_CTL_CUR_DEPTH (0x3ffL<<22) + +#define BNX2_RV2P_TFTQ_DATA 0x00002b80 +#define BNX2_RV2P_TFTQ_CMD 0x00002bb8 +#define BNX2_RV2P_TFTQ_CMD_OFFSET (0x3ffL<<0) +#define BNX2_RV2P_TFTQ_CMD_WR_TOP (1L<<10) +#define BNX2_RV2P_TFTQ_CMD_WR_TOP_0 (0L<<10) +#define BNX2_RV2P_TFTQ_CMD_WR_TOP_1 (1L<<10) +#define BNX2_RV2P_TFTQ_CMD_SFT_RESET (1L<<25) +#define BNX2_RV2P_TFTQ_CMD_RD_DATA (1L<<26) +#define BNX2_RV2P_TFTQ_CMD_ADD_INTERVEN (1L<<27) +#define BNX2_RV2P_TFTQ_CMD_ADD_DATA (1L<<28) +#define BNX2_RV2P_TFTQ_CMD_INTERVENE_CLR (1L<<29) +#define BNX2_RV2P_TFTQ_CMD_POP (1L<<30) +#define BNX2_RV2P_TFTQ_CMD_BUSY (1L<<31) + +#define BNX2_RV2P_TFTQ_CTL 0x00002bbc +#define BNX2_RV2P_TFTQ_CTL_INTERVENE (1L<<0) +#define BNX2_RV2P_TFTQ_CTL_OVERFLOW (1L<<1) +#define BNX2_RV2P_TFTQ_CTL_FORCE_INTERVENE (1L<<2) +#define BNX2_RV2P_TFTQ_CTL_MAX_DEPTH (0x3ffL<<12) +#define BNX2_RV2P_TFTQ_CTL_CUR_DEPTH (0x3ffL<<22) + +#define BNX2_RV2P_MFTQ_DATA 0x00002bc0 +#define BNX2_RV2P_MFTQ_CMD 0x00002bf8 +#define BNX2_RV2P_MFTQ_CMD_OFFSET (0x3ffL<<0) +#define BNX2_RV2P_MFTQ_CMD_WR_TOP (1L<<10) +#define BNX2_RV2P_MFTQ_CMD_WR_TOP_0 (0L<<10) +#define BNX2_RV2P_MFTQ_CMD_WR_TOP_1 (1L<<10) +#define BNX2_RV2P_MFTQ_CMD_SFT_RESET (1L<<25) +#define BNX2_RV2P_MFTQ_CMD_RD_DATA (1L<<26) +#define BNX2_RV2P_MFTQ_CMD_ADD_INTERVEN (1L<<27) +#define BNX2_RV2P_MFTQ_CMD_ADD_DATA (1L<<28) +#define BNX2_RV2P_MFTQ_CMD_INTERVENE_CLR (1L<<29) +#define BNX2_RV2P_MFTQ_CMD_POP (1L<<30) +#define BNX2_RV2P_MFTQ_CMD_BUSY (1L<<31) + +#define BNX2_RV2P_MFTQ_CTL 0x00002bfc +#define BNX2_RV2P_MFTQ_CTL_INTERVENE (1L<<0) +#define BNX2_RV2P_MFTQ_CTL_OVERFLOW (1L<<1) +#define BNX2_RV2P_MFTQ_CTL_FORCE_INTERVENE (1L<<2) +#define BNX2_RV2P_MFTQ_CTL_MAX_DEPTH (0x3ffL<<12) +#define BNX2_RV2P_MFTQ_CTL_CUR_DEPTH (0x3ffL<<22) + + + +/* + * mq_reg definition + * offset: 0x3c00 + */ +#define BNX2_MQ_COMMAND 0x00003c00 +#define BNX2_MQ_COMMAND_ENABLED (1L<<0) +#define BNX2_MQ_COMMAND_OVERFLOW (1L<<4) +#define BNX2_MQ_COMMAND_WR_ERROR (1L<<5) +#define BNX2_MQ_COMMAND_RD_ERROR (1L<<6) + +#define BNX2_MQ_STATUS 0x00003c04 +#define BNX2_MQ_STATUS_CTX_ACCESS_STAT (1L<<16) +#define BNX2_MQ_STATUS_CTX_ACCESS64_STAT (1L<<17) +#define BNX2_MQ_STATUS_PCI_STALL_STAT (1L<<18) + +#define BNX2_MQ_CONFIG 0x00003c08 +#define BNX2_MQ_CONFIG_TX_HIGH_PRI (1L<<0) +#define BNX2_MQ_CONFIG_HALT_DIS (1L<<1) +#define BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE (0x7L<<4) +#define BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_256 (0L<<4) +#define BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_512 (1L<<4) +#define BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_1K (2L<<4) +#define BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_2K (3L<<4) +#define BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_4K (4L<<4) +#define BNX2_MQ_CONFIG_MAX_DEPTH (0x7fL<<8) +#define BNX2_MQ_CONFIG_CUR_DEPTH (0x7fL<<20) + +#define BNX2_MQ_ENQUEUE1 0x00003c0c +#define BNX2_MQ_ENQUEUE1_OFFSET (0x3fL<<2) +#define BNX2_MQ_ENQUEUE1_CID (0x3fffL<<8) +#define BNX2_MQ_ENQUEUE1_BYTE_MASK (0xfL<<24) +#define BNX2_MQ_ENQUEUE1_KNL_MODE (1L<<28) + +#define BNX2_MQ_ENQUEUE2 0x00003c10 +#define BNX2_MQ_BAD_WR_ADDR 0x00003c14 +#define BNX2_MQ_BAD_RD_ADDR 0x00003c18 +#define BNX2_MQ_KNL_BYP_WIND_START 0x00003c1c +#define BNX2_MQ_KNL_BYP_WIND_START_VALUE (0xfffffL<<12) + +#define BNX2_MQ_KNL_WIND_END 0x00003c20 +#define BNX2_MQ_KNL_WIND_END_VALUE (0xffffffL<<8) + +#define BNX2_MQ_KNL_WRITE_MASK1 0x00003c24 +#define BNX2_MQ_KNL_TX_MASK1 0x00003c28 +#define BNX2_MQ_KNL_CMD_MASK1 0x00003c2c +#define BNX2_MQ_KNL_COND_ENQUEUE_MASK1 0x00003c30 +#define BNX2_MQ_KNL_RX_V2P_MASK1 0x00003c34 +#define BNX2_MQ_KNL_WRITE_MASK2 0x00003c38 +#define BNX2_MQ_KNL_TX_MASK2 0x00003c3c +#define BNX2_MQ_KNL_CMD_MASK2 0x00003c40 +#define BNX2_MQ_KNL_COND_ENQUEUE_MASK2 0x00003c44 +#define BNX2_MQ_KNL_RX_V2P_MASK2 0x00003c48 +#define BNX2_MQ_KNL_BYP_WRITE_MASK1 0x00003c4c +#define BNX2_MQ_KNL_BYP_TX_MASK1 0x00003c50 +#define BNX2_MQ_KNL_BYP_CMD_MASK1 0x00003c54 +#define BNX2_MQ_KNL_BYP_COND_ENQUEUE_MASK1 0x00003c58 +#define BNX2_MQ_KNL_BYP_RX_V2P_MASK1 0x00003c5c +#define BNX2_MQ_KNL_BYP_WRITE_MASK2 0x00003c60 +#define BNX2_MQ_KNL_BYP_TX_MASK2 0x00003c64 +#define BNX2_MQ_KNL_BYP_CMD_MASK2 0x00003c68 +#define BNX2_MQ_KNL_BYP_COND_ENQUEUE_MASK2 0x00003c6c +#define BNX2_MQ_KNL_BYP_RX_V2P_MASK2 0x00003c70 +#define BNX2_MQ_MEM_WR_ADDR 0x00003c74 +#define BNX2_MQ_MEM_WR_ADDR_VALUE (0x3fL<<0) + +#define BNX2_MQ_MEM_WR_DATA0 0x00003c78 +#define BNX2_MQ_MEM_WR_DATA0_VALUE (0xffffffffL<<0) + +#define BNX2_MQ_MEM_WR_DATA1 0x00003c7c +#define BNX2_MQ_MEM_WR_DATA1_VALUE (0xffffffffL<<0) + +#define BNX2_MQ_MEM_WR_DATA2 0x00003c80 +#define BNX2_MQ_MEM_WR_DATA2_VALUE (0x3fffffffL<<0) + +#define BNX2_MQ_MEM_RD_ADDR 0x00003c84 +#define BNX2_MQ_MEM_RD_ADDR_VALUE (0x3fL<<0) + +#define BNX2_MQ_MEM_RD_DATA0 0x00003c88 +#define BNX2_MQ_MEM_RD_DATA0_VALUE (0xffffffffL<<0) + +#define BNX2_MQ_MEM_RD_DATA1 0x00003c8c +#define BNX2_MQ_MEM_RD_DATA1_VALUE (0xffffffffL<<0) + +#define BNX2_MQ_MEM_RD_DATA2 0x00003c90 +#define BNX2_MQ_MEM_RD_DATA2_VALUE (0x3fffffffL<<0) + + + +/* + * tbdr_reg definition + * offset: 0x5000 + */ +#define BNX2_TBDR_COMMAND 0x00005000 +#define BNX2_TBDR_COMMAND_ENABLE (1L<<0) +#define BNX2_TBDR_COMMAND_SOFT_RST (1L<<1) +#define BNX2_TBDR_COMMAND_MSTR_ABORT (1L<<4) + +#define BNX2_TBDR_STATUS 0x00005004 +#define BNX2_TBDR_STATUS_DMA_WAIT (1L<<0) +#define BNX2_TBDR_STATUS_FTQ_WAIT (1L<<1) +#define BNX2_TBDR_STATUS_FIFO_OVERFLOW (1L<<2) +#define BNX2_TBDR_STATUS_FIFO_UNDERFLOW (1L<<3) +#define BNX2_TBDR_STATUS_SEARCHMISS_ERROR (1L<<4) +#define BNX2_TBDR_STATUS_FTQ_ENTRY_CNT (1L<<5) +#define BNX2_TBDR_STATUS_BURST_CNT (1L<<6) + +#define BNX2_TBDR_CONFIG 0x00005008 +#define BNX2_TBDR_CONFIG_MAX_BDS (0xffL<<0) +#define BNX2_TBDR_CONFIG_SWAP_MODE (1L<<8) +#define BNX2_TBDR_CONFIG_PRIORITY (1L<<9) +#define BNX2_TBDR_CONFIG_CACHE_NEXT_PAGE_PTRS (1L<<10) +#define BNX2_TBDR_CONFIG_PAGE_SIZE (0xfL<<24) +#define BNX2_TBDR_CONFIG_PAGE_SIZE_256 (0L<<24) +#define BNX2_TBDR_CONFIG_PAGE_SIZE_512 (1L<<24) +#define BNX2_TBDR_CONFIG_PAGE_SIZE_1K (2L<<24) +#define BNX2_TBDR_CONFIG_PAGE_SIZE_2K (3L<<24) +#define BNX2_TBDR_CONFIG_PAGE_SIZE_4K (4L<<24) +#define BNX2_TBDR_CONFIG_PAGE_SIZE_8K (5L<<24) +#define BNX2_TBDR_CONFIG_PAGE_SIZE_16K (6L<<24) +#define BNX2_TBDR_CONFIG_PAGE_SIZE_32K (7L<<24) +#define BNX2_TBDR_CONFIG_PAGE_SIZE_64K (8L<<24) +#define BNX2_TBDR_CONFIG_PAGE_SIZE_128K (9L<<24) +#define BNX2_TBDR_CONFIG_PAGE_SIZE_256K (10L<<24) +#define BNX2_TBDR_CONFIG_PAGE_SIZE_512K (11L<<24) +#define BNX2_TBDR_CONFIG_PAGE_SIZE_1M (12L<<24) + +#define BNX2_TBDR_DEBUG_VECT_PEEK 0x0000500c +#define BNX2_TBDR_DEBUG_VECT_PEEK_1_VALUE (0x7ffL<<0) +#define BNX2_TBDR_DEBUG_VECT_PEEK_1_PEEK_EN (1L<<11) +#define BNX2_TBDR_DEBUG_VECT_PEEK_1_SEL (0xfL<<12) +#define BNX2_TBDR_DEBUG_VECT_PEEK_2_VALUE (0x7ffL<<16) +#define BNX2_TBDR_DEBUG_VECT_PEEK_2_PEEK_EN (1L<<27) +#define BNX2_TBDR_DEBUG_VECT_PEEK_2_SEL (0xfL<<28) + +#define BNX2_TBDR_FTQ_DATA 0x000053c0 +#define BNX2_TBDR_FTQ_CMD 0x000053f8 +#define BNX2_TBDR_FTQ_CMD_OFFSET (0x3ffL<<0) +#define BNX2_TBDR_FTQ_CMD_WR_TOP (1L<<10) +#define BNX2_TBDR_FTQ_CMD_WR_TOP_0 (0L<<10) +#define BNX2_TBDR_FTQ_CMD_WR_TOP_1 (1L<<10) +#define BNX2_TBDR_FTQ_CMD_SFT_RESET (1L<<25) +#define BNX2_TBDR_FTQ_CMD_RD_DATA (1L<<26) +#define BNX2_TBDR_FTQ_CMD_ADD_INTERVEN (1L<<27) +#define BNX2_TBDR_FTQ_CMD_ADD_DATA (1L<<28) +#define BNX2_TBDR_FTQ_CMD_INTERVENE_CLR (1L<<29) +#define BNX2_TBDR_FTQ_CMD_POP (1L<<30) +#define BNX2_TBDR_FTQ_CMD_BUSY (1L<<31) + +#define BNX2_TBDR_FTQ_CTL 0x000053fc +#define BNX2_TBDR_FTQ_CTL_INTERVENE (1L<<0) +#define BNX2_TBDR_FTQ_CTL_OVERFLOW (1L<<1) +#define BNX2_TBDR_FTQ_CTL_FORCE_INTERVENE (1L<<2) +#define BNX2_TBDR_FTQ_CTL_MAX_DEPTH (0x3ffL<<12) +#define BNX2_TBDR_FTQ_CTL_CUR_DEPTH (0x3ffL<<22) + + + +/* + * tdma_reg definition + * offset: 0x5c00 + */ +#define BNX2_TDMA_COMMAND 0x00005c00 +#define BNX2_TDMA_COMMAND_ENABLED (1L<<0) +#define BNX2_TDMA_COMMAND_MASTER_ABORT (1L<<4) +#define BNX2_TDMA_COMMAND_BAD_L2_LENGTH_ABORT (1L<<7) + +#define BNX2_TDMA_STATUS 0x00005c04 +#define BNX2_TDMA_STATUS_DMA_WAIT (1L<<0) +#define BNX2_TDMA_STATUS_PAYLOAD_WAIT (1L<<1) +#define BNX2_TDMA_STATUS_PATCH_FTQ_WAIT (1L<<2) +#define BNX2_TDMA_STATUS_LOCK_WAIT (1L<<3) +#define BNX2_TDMA_STATUS_FTQ_ENTRY_CNT (1L<<16) +#define BNX2_TDMA_STATUS_BURST_CNT (1L<<17) + +#define BNX2_TDMA_CONFIG 0x00005c08 +#define BNX2_TDMA_CONFIG_ONE_DMA (1L<<0) +#define BNX2_TDMA_CONFIG_ONE_RECORD (1L<<1) +#define BNX2_TDMA_CONFIG_LIMIT_SZ (0xfL<<4) +#define BNX2_TDMA_CONFIG_LIMIT_SZ_64 (0L<<4) +#define BNX2_TDMA_CONFIG_LIMIT_SZ_128 (0x4L<<4) +#define BNX2_TDMA_CONFIG_LIMIT_SZ_256 (0x6L<<4) +#define BNX2_TDMA_CONFIG_LIMIT_SZ_512 (0x8L<<4) +#define BNX2_TDMA_CONFIG_LINE_SZ (0xfL<<8) +#define BNX2_TDMA_CONFIG_LINE_SZ_64 (0L<<8) +#define BNX2_TDMA_CONFIG_LINE_SZ_128 (4L<<8) +#define BNX2_TDMA_CONFIG_LINE_SZ_256 (6L<<8) +#define BNX2_TDMA_CONFIG_LINE_SZ_512 (8L<<8) +#define BNX2_TDMA_CONFIG_ALIGN_ENA (1L<<15) +#define BNX2_TDMA_CONFIG_CHK_L2_BD (1L<<16) +#define BNX2_TDMA_CONFIG_FIFO_CMP (0xfL<<20) + +#define BNX2_TDMA_PAYLOAD_PROD 0x00005c0c +#define BNX2_TDMA_PAYLOAD_PROD_VALUE (0x1fffL<<3) + +#define BNX2_TDMA_DBG_WATCHDOG 0x00005c10 +#define BNX2_TDMA_DBG_TRIGGER 0x00005c14 +#define BNX2_TDMA_DMAD_FSM 0x00005c80 +#define BNX2_TDMA_DMAD_FSM_BD_INVLD (1L<<0) +#define BNX2_TDMA_DMAD_FSM_PUSH (0xfL<<4) +#define BNX2_TDMA_DMAD_FSM_ARB_TBDC (0x3L<<8) +#define BNX2_TDMA_DMAD_FSM_ARB_CTX (1L<<12) +#define BNX2_TDMA_DMAD_FSM_DR_INTF (1L<<16) +#define BNX2_TDMA_DMAD_FSM_DMAD (0x7L<<20) +#define BNX2_TDMA_DMAD_FSM_BD (0xfL<<24) + +#define BNX2_TDMA_DMAD_STATUS 0x00005c84 +#define BNX2_TDMA_DMAD_STATUS_RHOLD_PUSH_ENTRY (0x3L<<0) +#define BNX2_TDMA_DMAD_STATUS_RHOLD_DMAD_ENTRY (0x3L<<4) +#define BNX2_TDMA_DMAD_STATUS_RHOLD_BD_ENTRY (0x3L<<8) +#define BNX2_TDMA_DMAD_STATUS_IFTQ_ENUM (0xfL<<12) + +#define BNX2_TDMA_DR_INTF_FSM 0x00005c88 +#define BNX2_TDMA_DR_INTF_FSM_L2_COMP (0x3L<<0) +#define BNX2_TDMA_DR_INTF_FSM_TPATQ (0x7L<<4) +#define BNX2_TDMA_DR_INTF_FSM_TPBUF (0x3L<<8) +#define BNX2_TDMA_DR_INTF_FSM_DR_BUF (0x7L<<12) +#define BNX2_TDMA_DR_INTF_FSM_DMAD (0x7L<<16) + +#define BNX2_TDMA_DR_INTF_STATUS 0x00005c8c +#define BNX2_TDMA_DR_INTF_STATUS_HOLE_PHASE (0x7L<<0) +#define BNX2_TDMA_DR_INTF_STATUS_DATA_AVAIL (0x3L<<4) +#define BNX2_TDMA_DR_INTF_STATUS_SHIFT_ADDR (0x7L<<8) +#define BNX2_TDMA_DR_INTF_STATUS_NXT_PNTR (0xfL<<12) +#define BNX2_TDMA_DR_INTF_STATUS_BYTE_COUNT (0x7L<<16) + +#define BNX2_TDMA_FTQ_DATA 0x00005fc0 +#define BNX2_TDMA_FTQ_CMD 0x00005ff8 +#define BNX2_TDMA_FTQ_CMD_OFFSET (0x3ffL<<0) +#define BNX2_TDMA_FTQ_CMD_WR_TOP (1L<<10) +#define BNX2_TDMA_FTQ_CMD_WR_TOP_0 (0L<<10) +#define BNX2_TDMA_FTQ_CMD_WR_TOP_1 (1L<<10) +#define BNX2_TDMA_FTQ_CMD_SFT_RESET (1L<<25) +#define BNX2_TDMA_FTQ_CMD_RD_DATA (1L<<26) +#define BNX2_TDMA_FTQ_CMD_ADD_INTERVEN (1L<<27) +#define BNX2_TDMA_FTQ_CMD_ADD_DATA (1L<<28) +#define BNX2_TDMA_FTQ_CMD_INTERVENE_CLR (1L<<29) +#define BNX2_TDMA_FTQ_CMD_POP (1L<<30) +#define BNX2_TDMA_FTQ_CMD_BUSY (1L<<31) + +#define BNX2_TDMA_FTQ_CTL 0x00005ffc +#define BNX2_TDMA_FTQ_CTL_INTERVENE (1L<<0) +#define BNX2_TDMA_FTQ_CTL_OVERFLOW (1L<<1) +#define BNX2_TDMA_FTQ_CTL_FORCE_INTERVENE (1L<<2) +#define BNX2_TDMA_FTQ_CTL_MAX_DEPTH (0x3ffL<<12) +#define BNX2_TDMA_FTQ_CTL_CUR_DEPTH (0x3ffL<<22) + + + +/* + * hc_reg definition + * offset: 0x6800 + */ +#define BNX2_HC_COMMAND 0x00006800 +#define BNX2_HC_COMMAND_ENABLE (1L<<0) +#define BNX2_HC_COMMAND_SKIP_ABORT (1L<<4) +#define BNX2_HC_COMMAND_COAL_NOW (1L<<16) +#define BNX2_HC_COMMAND_COAL_NOW_WO_INT (1L<<17) +#define BNX2_HC_COMMAND_STATS_NOW (1L<<18) +#define BNX2_HC_COMMAND_FORCE_INT (0x3L<<19) +#define BNX2_HC_COMMAND_FORCE_INT_NULL (0L<<19) +#define BNX2_HC_COMMAND_FORCE_INT_HIGH (1L<<19) +#define BNX2_HC_COMMAND_FORCE_INT_LOW (2L<<19) +#define BNX2_HC_COMMAND_FORCE_INT_FREE (3L<<19) +#define BNX2_HC_COMMAND_CLR_STAT_NOW (1L<<21) + +#define BNX2_HC_STATUS 0x00006804 +#define BNX2_HC_STATUS_MASTER_ABORT (1L<<0) +#define BNX2_HC_STATUS_PARITY_ERROR_STATE (1L<<1) +#define BNX2_HC_STATUS_PCI_CLK_CNT_STAT (1L<<16) +#define BNX2_HC_STATUS_CORE_CLK_CNT_STAT (1L<<17) +#define BNX2_HC_STATUS_NUM_STATUS_BLOCKS_STAT (1L<<18) +#define BNX2_HC_STATUS_NUM_INT_GEN_STAT (1L<<19) +#define BNX2_HC_STATUS_NUM_INT_MBOX_WR_STAT (1L<<20) +#define BNX2_HC_STATUS_CORE_CLKS_TO_HW_INTACK_STAT (1L<<23) +#define BNX2_HC_STATUS_CORE_CLKS_TO_SW_INTACK_STAT (1L<<24) +#define BNX2_HC_STATUS_CORE_CLKS_DURING_SW_INTACK_STAT (1L<<25) + +#define BNX2_HC_CONFIG 0x00006808 +#define BNX2_HC_CONFIG_COLLECT_STATS (1L<<0) +#define BNX2_HC_CONFIG_RX_TMR_MODE (1L<<1) +#define BNX2_HC_CONFIG_TX_TMR_MODE (1L<<2) +#define BNX2_HC_CONFIG_COM_TMR_MODE (1L<<3) +#define BNX2_HC_CONFIG_CMD_TMR_MODE (1L<<4) +#define BNX2_HC_CONFIG_STATISTIC_PRIORITY (1L<<5) +#define BNX2_HC_CONFIG_STATUS_PRIORITY (1L<<6) +#define BNX2_HC_CONFIG_STAT_MEM_ADDR (0xffL<<8) + +#define BNX2_HC_ATTN_BITS_ENABLE 0x0000680c +#define BNX2_HC_STATUS_ADDR_L 0x00006810 +#define BNX2_HC_STATUS_ADDR_H 0x00006814 +#define BNX2_HC_STATISTICS_ADDR_L 0x00006818 +#define BNX2_HC_STATISTICS_ADDR_H 0x0000681c +#define BNX2_HC_TX_QUICK_CONS_TRIP 0x00006820 +#define BNX2_HC_TX_QUICK_CONS_TRIP_VALUE (0xffL<<0) +#define BNX2_HC_TX_QUICK_CONS_TRIP_INT (0xffL<<16) + +#define BNX2_HC_COMP_PROD_TRIP 0x00006824 +#define BNX2_HC_COMP_PROD_TRIP_VALUE (0xffL<<0) +#define BNX2_HC_COMP_PROD_TRIP_INT (0xffL<<16) + +#define BNX2_HC_RX_QUICK_CONS_TRIP 0x00006828 +#define BNX2_HC_RX_QUICK_CONS_TRIP_VALUE (0xffL<<0) +#define BNX2_HC_RX_QUICK_CONS_TRIP_INT (0xffL<<16) + +#define BNX2_HC_RX_TICKS 0x0000682c +#define BNX2_HC_RX_TICKS_VALUE (0x3ffL<<0) +#define BNX2_HC_RX_TICKS_INT (0x3ffL<<16) + +#define BNX2_HC_TX_TICKS 0x00006830 +#define BNX2_HC_TX_TICKS_VALUE (0x3ffL<<0) +#define BNX2_HC_TX_TICKS_INT (0x3ffL<<16) + +#define BNX2_HC_COM_TICKS 0x00006834 +#define BNX2_HC_COM_TICKS_VALUE (0x3ffL<<0) +#define BNX2_HC_COM_TICKS_INT (0x3ffL<<16) + +#define BNX2_HC_CMD_TICKS 0x00006838 +#define BNX2_HC_CMD_TICKS_VALUE (0x3ffL<<0) +#define BNX2_HC_CMD_TICKS_INT (0x3ffL<<16) + +#define BNX2_HC_PERIODIC_TICKS 0x0000683c +#define BNX2_HC_PERIODIC_TICKS_HC_PERIODIC_TICKS (0xffffL<<0) + +#define BNX2_HC_STAT_COLLECT_TICKS 0x00006840 +#define BNX2_HC_STAT_COLLECT_TICKS_HC_STAT_COLL_TICKS (0xffL<<4) + +#define BNX2_HC_STATS_TICKS 0x00006844 +#define BNX2_HC_STATS_TICKS_HC_STAT_TICKS (0xffffL<<8) + +#define BNX2_HC_STAT_MEM_DATA 0x0000684c +#define BNX2_HC_STAT_GEN_SEL_0 0x00006850 +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0 (0x7fL<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT0 (0L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT1 (1L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT2 (2L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT3 (3L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT4 (4L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT5 (5L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT6 (6L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT7 (7L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT8 (8L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT9 (9L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT10 (10L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXP_STAT11 (11L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT0 (12L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT1 (13L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT2 (14L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT3 (15L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT4 (16L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT5 (17L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT6 (18L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXP_STAT7 (19L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT0 (20L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT1 (21L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT2 (22L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT3 (23L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT4 (24L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT5 (25L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT6 (26L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT7 (27L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT8 (28L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT9 (29L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT10 (30L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COM_STAT11 (31L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TPAT_STAT0 (32L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TPAT_STAT1 (33L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TPAT_STAT2 (34L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TPAT_STAT3 (35L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT0 (36L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT1 (37L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT2 (38L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT3 (39L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT4 (40L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT5 (41L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT6 (42L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CP_STAT7 (43L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT0 (44L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT1 (45L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT2 (46L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT3 (47L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT4 (48L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT5 (49L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT6 (50L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MCP_STAT7 (51L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_PCI_CLK_CNT (52L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CORE_CLK_CNT (53L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_HC_NUM_STATUS_BLOCKS (54L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_HC_NUM_INT_GEN (55L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_HC_NUM_INT_MBOX_WR (56L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_HC_CORE_CLKS_TO_HW_INTACK (59L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_HC_CORE_CLKS_TO_SW_INTACK (60L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_HC_CORE_CLKS_DURING_SW_INTACK (61L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TSCH_CMD_CNT (62L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TSCH_SLOT_CNT (63L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CSCH_CMD_CNT (64L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CSCH_SLOT_CNT (65L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RLUPQ_VALID_CNT (66L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXPQ_VALID_CNT (67L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RXPCQ_VALID_CNT (68L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2PPQ_VALID_CNT (69L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2PMQ_VALID_CNT (70L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2PTQ_VALID_CNT (71L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RDMAQ_VALID_CNT (72L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TSCHQ_VALID_CNT (73L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TBDRQ_VALID_CNT (74L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TXPQ_VALID_CNT (75L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TDMAQ_VALID_CNT (76L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TPATQ_VALID_CNT (77L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TASQ_VALID_CNT (78L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CSQ_VALID_CNT (79L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CPQ_VALID_CNT (80L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COMXQ_VALID_CNT (81L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COMTQ_VALID_CNT (82L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_COMQ_VALID_CNT (83L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MGMQ_VALID_CNT (84L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_READ_TRANSFERS_CNT (85L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_READ_DELAY_PCI_CLKS_CNT (86L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_BIG_READ_TRANSFERS_CNT (87L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_BIG_READ_DELAY_PCI_CLKS_CNT (88L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_BIG_READ_RETRY_AFTER_DATA_CNT (89L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_WRITE_TRANSFERS_CNT (90L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_WRITE_DELAY_PCI_CLKS_CNT (91L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_BIG_WRITE_TRANSFERS_CNT (92L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_BIG_WRITE_DELAY_PCI_CLKS_CNT (93L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_DMAE_BIG_WRITE_RETRY_AFTER_DATA_CNT (94L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CTX_WR_CNT64 (95L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CTX_RD_CNT64 (96L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CTX_ACC_STALL_CLKS (97L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_CTX_LOCK_STALL_CLKS (98L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MBQ_CTX_ACCESS_STAT (99L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MBQ_CTX_ACCESS64_STAT (100L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_MBQ_PCI_STALL_STAT (101L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TBDR_FTQ_ENTRY_CNT (102L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TBDR_BURST_CNT (103L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TDMA_FTQ_ENTRY_CNT (104L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TDMA_BURST_CNT (105L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RDMA_FTQ_ENTRY_CNT (106L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RDMA_BURST_CNT (107L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RLUP_MATCH_CNT (108L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TMR_POLL_PASS_CNT (109L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TMR_TMR1_CNT (110L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TMR_TMR2_CNT (111L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TMR_TMR3_CNT (112L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TMR_TMR4_CNT (113L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_TMR_TMR5_CNT (114L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2P_STAT0 (115L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2P_STAT1 (116L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2P_STAT2 (117L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2P_STAT3 (118L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2P_STAT4 (119L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RV2P_STAT5 (120L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RBDC_PROC1_MISS (121L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RBDC_PROC2_MISS (122L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_0_RBDC_BURST_CNT (127L<<0) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_1 (0x7fL<<8) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_2 (0x7fL<<16) +#define BNX2_HC_STAT_GEN_SEL_0_GEN_SEL_3 (0x7fL<<24) + +#define BNX2_HC_STAT_GEN_SEL_1 0x00006854 +#define BNX2_HC_STAT_GEN_SEL_1_GEN_SEL_4 (0x7fL<<0) +#define BNX2_HC_STAT_GEN_SEL_1_GEN_SEL_5 (0x7fL<<8) +#define BNX2_HC_STAT_GEN_SEL_1_GEN_SEL_6 (0x7fL<<16) +#define BNX2_HC_STAT_GEN_SEL_1_GEN_SEL_7 (0x7fL<<24) + +#define BNX2_HC_STAT_GEN_SEL_2 0x00006858 +#define BNX2_HC_STAT_GEN_SEL_2_GEN_SEL_8 (0x7fL<<0) +#define BNX2_HC_STAT_GEN_SEL_2_GEN_SEL_9 (0x7fL<<8) +#define BNX2_HC_STAT_GEN_SEL_2_GEN_SEL_10 (0x7fL<<16) +#define BNX2_HC_STAT_GEN_SEL_2_GEN_SEL_11 (0x7fL<<24) + +#define BNX2_HC_STAT_GEN_SEL_3 0x0000685c +#define BNX2_HC_STAT_GEN_SEL_3_GEN_SEL_12 (0x7fL<<0) +#define BNX2_HC_STAT_GEN_SEL_3_GEN_SEL_13 (0x7fL<<8) +#define BNX2_HC_STAT_GEN_SEL_3_GEN_SEL_14 (0x7fL<<16) +#define BNX2_HC_STAT_GEN_SEL_3_GEN_SEL_15 (0x7fL<<24) + +#define BNX2_HC_STAT_GEN_STAT0 0x00006888 +#define BNX2_HC_STAT_GEN_STAT1 0x0000688c +#define BNX2_HC_STAT_GEN_STAT2 0x00006890 +#define BNX2_HC_STAT_GEN_STAT3 0x00006894 +#define BNX2_HC_STAT_GEN_STAT4 0x00006898 +#define BNX2_HC_STAT_GEN_STAT5 0x0000689c +#define BNX2_HC_STAT_GEN_STAT6 0x000068a0 +#define BNX2_HC_STAT_GEN_STAT7 0x000068a4 +#define BNX2_HC_STAT_GEN_STAT8 0x000068a8 +#define BNX2_HC_STAT_GEN_STAT9 0x000068ac +#define BNX2_HC_STAT_GEN_STAT10 0x000068b0 +#define BNX2_HC_STAT_GEN_STAT11 0x000068b4 +#define BNX2_HC_STAT_GEN_STAT12 0x000068b8 +#define BNX2_HC_STAT_GEN_STAT13 0x000068bc +#define BNX2_HC_STAT_GEN_STAT14 0x000068c0 +#define BNX2_HC_STAT_GEN_STAT15 0x000068c4 +#define BNX2_HC_STAT_GEN_STAT_AC0 0x000068c8 +#define BNX2_HC_STAT_GEN_STAT_AC1 0x000068cc +#define BNX2_HC_STAT_GEN_STAT_AC2 0x000068d0 +#define BNX2_HC_STAT_GEN_STAT_AC3 0x000068d4 +#define BNX2_HC_STAT_GEN_STAT_AC4 0x000068d8 +#define BNX2_HC_STAT_GEN_STAT_AC5 0x000068dc +#define BNX2_HC_STAT_GEN_STAT_AC6 0x000068e0 +#define BNX2_HC_STAT_GEN_STAT_AC7 0x000068e4 +#define BNX2_HC_STAT_GEN_STAT_AC8 0x000068e8 +#define BNX2_HC_STAT_GEN_STAT_AC9 0x000068ec +#define BNX2_HC_STAT_GEN_STAT_AC10 0x000068f0 +#define BNX2_HC_STAT_GEN_STAT_AC11 0x000068f4 +#define BNX2_HC_STAT_GEN_STAT_AC12 0x000068f8 +#define BNX2_HC_STAT_GEN_STAT_AC13 0x000068fc +#define BNX2_HC_STAT_GEN_STAT_AC14 0x00006900 +#define BNX2_HC_STAT_GEN_STAT_AC15 0x00006904 +#define BNX2_HC_VIS 0x00006908 +#define BNX2_HC_VIS_STAT_BUILD_STATE (0xfL<<0) +#define BNX2_HC_VIS_STAT_BUILD_STATE_IDLE (0L<<0) +#define BNX2_HC_VIS_STAT_BUILD_STATE_START (1L<<0) +#define BNX2_HC_VIS_STAT_BUILD_STATE_REQUEST (2L<<0) +#define BNX2_HC_VIS_STAT_BUILD_STATE_UPDATE64 (3L<<0) +#define BNX2_HC_VIS_STAT_BUILD_STATE_UPDATE32 (4L<<0) +#define BNX2_HC_VIS_STAT_BUILD_STATE_UPDATE_DONE (5L<<0) +#define BNX2_HC_VIS_STAT_BUILD_STATE_DMA (6L<<0) +#define BNX2_HC_VIS_STAT_BUILD_STATE_MSI_CONTROL (7L<<0) +#define BNX2_HC_VIS_STAT_BUILD_STATE_MSI_LOW (8L<<0) +#define BNX2_HC_VIS_STAT_BUILD_STATE_MSI_HIGH (9L<<0) +#define BNX2_HC_VIS_STAT_BUILD_STATE_MSI_DATA (10L<<0) +#define BNX2_HC_VIS_DMA_STAT_STATE (0xfL<<8) +#define BNX2_HC_VIS_DMA_STAT_STATE_IDLE (0L<<8) +#define BNX2_HC_VIS_DMA_STAT_STATE_STATUS_PARAM (1L<<8) +#define BNX2_HC_VIS_DMA_STAT_STATE_STATUS_DMA (2L<<8) +#define BNX2_HC_VIS_DMA_STAT_STATE_WRITE_COMP (3L<<8) +#define BNX2_HC_VIS_DMA_STAT_STATE_COMP (4L<<8) +#define BNX2_HC_VIS_DMA_STAT_STATE_STATISTIC_PARAM (5L<<8) +#define BNX2_HC_VIS_DMA_STAT_STATE_STATISTIC_DMA (6L<<8) +#define BNX2_HC_VIS_DMA_STAT_STATE_WRITE_COMP_1 (7L<<8) +#define BNX2_HC_VIS_DMA_STAT_STATE_WRITE_COMP_2 (8L<<8) +#define BNX2_HC_VIS_DMA_STAT_STATE_WAIT (9L<<8) +#define BNX2_HC_VIS_DMA_STAT_STATE_ABORT (15L<<8) +#define BNX2_HC_VIS_DMA_MSI_STATE (0x7L<<12) +#define BNX2_HC_VIS_STATISTIC_DMA_EN_STATE (0x3L<<15) +#define BNX2_HC_VIS_STATISTIC_DMA_EN_STATE_IDLE (0L<<15) +#define BNX2_HC_VIS_STATISTIC_DMA_EN_STATE_COUNT (1L<<15) +#define BNX2_HC_VIS_STATISTIC_DMA_EN_STATE_START (2L<<15) + +#define BNX2_HC_VIS_1 0x0000690c +#define BNX2_HC_VIS_1_HW_INTACK_STATE (1L<<4) +#define BNX2_HC_VIS_1_HW_INTACK_STATE_IDLE (0L<<4) +#define BNX2_HC_VIS_1_HW_INTACK_STATE_COUNT (1L<<4) +#define BNX2_HC_VIS_1_SW_INTACK_STATE (1L<<5) +#define BNX2_HC_VIS_1_SW_INTACK_STATE_IDLE (0L<<5) +#define BNX2_HC_VIS_1_SW_INTACK_STATE_COUNT (1L<<5) +#define BNX2_HC_VIS_1_DURING_SW_INTACK_STATE (1L<<6) +#define BNX2_HC_VIS_1_DURING_SW_INTACK_STATE_IDLE (0L<<6) +#define BNX2_HC_VIS_1_DURING_SW_INTACK_STATE_COUNT (1L<<6) +#define BNX2_HC_VIS_1_MAILBOX_COUNT_STATE (1L<<7) +#define BNX2_HC_VIS_1_MAILBOX_COUNT_STATE_IDLE (0L<<7) +#define BNX2_HC_VIS_1_MAILBOX_COUNT_STATE_COUNT (1L<<7) +#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE (0xfL<<17) +#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_IDLE (0L<<17) +#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_DMA (1L<<17) +#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_UPDATE (2L<<17) +#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_ASSIGN (3L<<17) +#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_WAIT (4L<<17) +#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_REG_UPDATE (5L<<17) +#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_REG_ASSIGN (6L<<17) +#define BNX2_HC_VIS_1_RAM_RD_ARB_STATE_REG_WAIT (7L<<17) +#define BNX2_HC_VIS_1_RAM_WR_ARB_STATE (0x3L<<21) +#define BNX2_HC_VIS_1_RAM_WR_ARB_STATE_NORMAL (0L<<21) +#define BNX2_HC_VIS_1_RAM_WR_ARB_STATE_CLEAR (1L<<21) +#define BNX2_HC_VIS_1_INT_GEN_STATE (1L<<23) +#define BNX2_HC_VIS_1_INT_GEN_STATE_DLE (0L<<23) +#define BNX2_HC_VIS_1_INT_GEN_STATE_NTERRUPT (1L<<23) +#define BNX2_HC_VIS_1_STAT_CHAN_ID (0x7L<<24) +#define BNX2_HC_VIS_1_INT_B (1L<<27) + +#define BNX2_HC_DEBUG_VECT_PEEK 0x00006910 +#define BNX2_HC_DEBUG_VECT_PEEK_1_VALUE (0x7ffL<<0) +#define BNX2_HC_DEBUG_VECT_PEEK_1_PEEK_EN (1L<<11) +#define BNX2_HC_DEBUG_VECT_PEEK_1_SEL (0xfL<<12) +#define BNX2_HC_DEBUG_VECT_PEEK_2_VALUE (0x7ffL<<16) +#define BNX2_HC_DEBUG_VECT_PEEK_2_PEEK_EN (1L<<27) +#define BNX2_HC_DEBUG_VECT_PEEK_2_SEL (0xfL<<28) + + + +/* + * txp_reg definition + * offset: 0x40000 + */ +#define BNX2_TXP_CPU_MODE 0x00045000 +#define BNX2_TXP_CPU_MODE_LOCAL_RST (1L<<0) +#define BNX2_TXP_CPU_MODE_STEP_ENA (1L<<1) +#define BNX2_TXP_CPU_MODE_PAGE_0_DATA_ENA (1L<<2) +#define BNX2_TXP_CPU_MODE_PAGE_0_INST_ENA (1L<<3) +#define BNX2_TXP_CPU_MODE_MSG_BIT1 (1L<<6) +#define BNX2_TXP_CPU_MODE_INTERRUPT_ENA (1L<<7) +#define BNX2_TXP_CPU_MODE_SOFT_HALT (1L<<10) +#define BNX2_TXP_CPU_MODE_BAD_DATA_HALT_ENA (1L<<11) +#define BNX2_TXP_CPU_MODE_BAD_INST_HALT_ENA (1L<<12) +#define BNX2_TXP_CPU_MODE_FIO_ABORT_HALT_ENA (1L<<13) +#define BNX2_TXP_CPU_MODE_SPAD_UNDERFLOW_HALT_ENA (1L<<15) + +#define BNX2_TXP_CPU_STATE 0x00045004 +#define BNX2_TXP_CPU_STATE_BREAKPOINT (1L<<0) +#define BNX2_TXP_CPU_STATE_BAD_INST_HALTED (1L<<2) +#define BNX2_TXP_CPU_STATE_PAGE_0_DATA_HALTED (1L<<3) +#define BNX2_TXP_CPU_STATE_PAGE_0_INST_HALTED (1L<<4) +#define BNX2_TXP_CPU_STATE_BAD_DATA_ADDR_HALTED (1L<<5) +#define BNX2_TXP_CPU_STATE_BAD_pc_HALTED (1L<<6) +#define BNX2_TXP_CPU_STATE_ALIGN_HALTED (1L<<7) +#define BNX2_TXP_CPU_STATE_FIO_ABORT_HALTED (1L<<8) +#define BNX2_TXP_CPU_STATE_SOFT_HALTED (1L<<10) +#define BNX2_TXP_CPU_STATE_SPAD_UNDERFLOW (1L<<11) +#define BNX2_TXP_CPU_STATE_INTERRRUPT (1L<<12) +#define BNX2_TXP_CPU_STATE_DATA_ACCESS_STALL (1L<<14) +#define BNX2_TXP_CPU_STATE_INST_FETCH_STALL (1L<<15) +#define BNX2_TXP_CPU_STATE_BLOCKED_READ (1L<<31) + +#define BNX2_TXP_CPU_EVENT_MASK 0x00045008 +#define BNX2_TXP_CPU_EVENT_MASK_BREAKPOINT_MASK (1L<<0) +#define BNX2_TXP_CPU_EVENT_MASK_BAD_INST_HALTED_MASK (1L<<2) +#define BNX2_TXP_CPU_EVENT_MASK_PAGE_0_DATA_HALTED_MASK (1L<<3) +#define BNX2_TXP_CPU_EVENT_MASK_PAGE_0_INST_HALTED_MASK (1L<<4) +#define BNX2_TXP_CPU_EVENT_MASK_BAD_DATA_ADDR_HALTED_MASK (1L<<5) +#define BNX2_TXP_CPU_EVENT_MASK_BAD_PC_HALTED_MASK (1L<<6) +#define BNX2_TXP_CPU_EVENT_MASK_ALIGN_HALTED_MASK (1L<<7) +#define BNX2_TXP_CPU_EVENT_MASK_FIO_ABORT_MASK (1L<<8) +#define BNX2_TXP_CPU_EVENT_MASK_SOFT_HALTED_MASK (1L<<10) +#define BNX2_TXP_CPU_EVENT_MASK_SPAD_UNDERFLOW_MASK (1L<<11) +#define BNX2_TXP_CPU_EVENT_MASK_INTERRUPT_MASK (1L<<12) + +#define BNX2_TXP_CPU_PROGRAM_COUNTER 0x0004501c +#define BNX2_TXP_CPU_INSTRUCTION 0x00045020 +#define BNX2_TXP_CPU_DATA_ACCESS 0x00045024 +#define BNX2_TXP_CPU_INTERRUPT_ENABLE 0x00045028 +#define BNX2_TXP_CPU_INTERRUPT_VECTOR 0x0004502c +#define BNX2_TXP_CPU_INTERRUPT_SAVED_PC 0x00045030 +#define BNX2_TXP_CPU_HW_BREAKPOINT 0x00045034 +#define BNX2_TXP_CPU_HW_BREAKPOINT_DISABLE (1L<<0) +#define BNX2_TXP_CPU_HW_BREAKPOINT_ADDRESS (0x3fffffffL<<2) + +#define BNX2_TXP_CPU_DEBUG_VECT_PEEK 0x00045038 +#define BNX2_TXP_CPU_DEBUG_VECT_PEEK_1_VALUE (0x7ffL<<0) +#define BNX2_TXP_CPU_DEBUG_VECT_PEEK_1_PEEK_EN (1L<<11) +#define BNX2_TXP_CPU_DEBUG_VECT_PEEK_1_SEL (0xfL<<12) +#define BNX2_TXP_CPU_DEBUG_VECT_PEEK_2_VALUE (0x7ffL<<16) +#define BNX2_TXP_CPU_DEBUG_VECT_PEEK_2_PEEK_EN (1L<<27) +#define BNX2_TXP_CPU_DEBUG_VECT_PEEK_2_SEL (0xfL<<28) + +#define BNX2_TXP_CPU_LAST_BRANCH_ADDR 0x00045048 +#define BNX2_TXP_CPU_LAST_BRANCH_ADDR_TYPE (1L<<1) +#define BNX2_TXP_CPU_LAST_BRANCH_ADDR_TYPE_JUMP (0L<<1) +#define BNX2_TXP_CPU_LAST_BRANCH_ADDR_TYPE_BRANCH (1L<<1) +#define BNX2_TXP_CPU_LAST_BRANCH_ADDR_LBA (0x3fffffffL<<2) + +#define BNX2_TXP_CPU_REG_FILE 0x00045200 +#define BNX2_TXP_FTQ_DATA 0x000453c0 +#define BNX2_TXP_FTQ_CMD 0x000453f8 +#define BNX2_TXP_FTQ_CMD_OFFSET (0x3ffL<<0) +#define BNX2_TXP_FTQ_CMD_WR_TOP (1L<<10) +#define BNX2_TXP_FTQ_CMD_WR_TOP_0 (0L<<10) +#define BNX2_TXP_FTQ_CMD_WR_TOP_1 (1L<<10) +#define BNX2_TXP_FTQ_CMD_SFT_RESET (1L<<25) +#define BNX2_TXP_FTQ_CMD_RD_DATA (1L<<26) +#define BNX2_TXP_FTQ_CMD_ADD_INTERVEN (1L<<27) +#define BNX2_TXP_FTQ_CMD_ADD_DATA (1L<<28) +#define BNX2_TXP_FTQ_CMD_INTERVENE_CLR (1L<<29) +#define BNX2_TXP_FTQ_CMD_POP (1L<<30) +#define BNX2_TXP_FTQ_CMD_BUSY (1L<<31) + +#define BNX2_TXP_FTQ_CTL 0x000453fc +#define BNX2_TXP_FTQ_CTL_INTERVENE (1L<<0) +#define BNX2_TXP_FTQ_CTL_OVERFLOW (1L<<1) +#define BNX2_TXP_FTQ_CTL_FORCE_INTERVENE (1L<<2) +#define BNX2_TXP_FTQ_CTL_MAX_DEPTH (0x3ffL<<12) +#define BNX2_TXP_FTQ_CTL_CUR_DEPTH (0x3ffL<<22) + +#define BNX2_TXP_SCRATCH 0x00060000 + + +/* + * tpat_reg definition + * offset: 0x80000 + */ +#define BNX2_TPAT_CPU_MODE 0x00085000 +#define BNX2_TPAT_CPU_MODE_LOCAL_RST (1L<<0) +#define BNX2_TPAT_CPU_MODE_STEP_ENA (1L<<1) +#define BNX2_TPAT_CPU_MODE_PAGE_0_DATA_ENA (1L<<2) +#define BNX2_TPAT_CPU_MODE_PAGE_0_INST_ENA (1L<<3) +#define BNX2_TPAT_CPU_MODE_MSG_BIT1 (1L<<6) +#define BNX2_TPAT_CPU_MODE_INTERRUPT_ENA (1L<<7) +#define BNX2_TPAT_CPU_MODE_SOFT_HALT (1L<<10) +#define BNX2_TPAT_CPU_MODE_BAD_DATA_HALT_ENA (1L<<11) +#define BNX2_TPAT_CPU_MODE_BAD_INST_HALT_ENA (1L<<12) +#define BNX2_TPAT_CPU_MODE_FIO_ABORT_HALT_ENA (1L<<13) +#define BNX2_TPAT_CPU_MODE_SPAD_UNDERFLOW_HALT_ENA (1L<<15) + +#define BNX2_TPAT_CPU_STATE 0x00085004 +#define BNX2_TPAT_CPU_STATE_BREAKPOINT (1L<<0) +#define BNX2_TPAT_CPU_STATE_BAD_INST_HALTED (1L<<2) +#define BNX2_TPAT_CPU_STATE_PAGE_0_DATA_HALTED (1L<<3) +#define BNX2_TPAT_CPU_STATE_PAGE_0_INST_HALTED (1L<<4) +#define BNX2_TPAT_CPU_STATE_BAD_DATA_ADDR_HALTED (1L<<5) +#define BNX2_TPAT_CPU_STATE_BAD_pc_HALTED (1L<<6) +#define BNX2_TPAT_CPU_STATE_ALIGN_HALTED (1L<<7) +#define BNX2_TPAT_CPU_STATE_FIO_ABORT_HALTED (1L<<8) +#define BNX2_TPAT_CPU_STATE_SOFT_HALTED (1L<<10) +#define BNX2_TPAT_CPU_STATE_SPAD_UNDERFLOW (1L<<11) +#define BNX2_TPAT_CPU_STATE_INTERRRUPT (1L<<12) +#define BNX2_TPAT_CPU_STATE_DATA_ACCESS_STALL (1L<<14) +#define BNX2_TPAT_CPU_STATE_INST_FETCH_STALL (1L<<15) +#define BNX2_TPAT_CPU_STATE_BLOCKED_READ (1L<<31) + +#define BNX2_TPAT_CPU_EVENT_MASK 0x00085008 +#define BNX2_TPAT_CPU_EVENT_MASK_BREAKPOINT_MASK (1L<<0) +#define BNX2_TPAT_CPU_EVENT_MASK_BAD_INST_HALTED_MASK (1L<<2) +#define BNX2_TPAT_CPU_EVENT_MASK_PAGE_0_DATA_HALTED_MASK (1L<<3) +#define BNX2_TPAT_CPU_EVENT_MASK_PAGE_0_INST_HALTED_MASK (1L<<4) +#define BNX2_TPAT_CPU_EVENT_MASK_BAD_DATA_ADDR_HALTED_MASK (1L<<5) +#define BNX2_TPAT_CPU_EVENT_MASK_BAD_PC_HALTED_MASK (1L<<6) +#define BNX2_TPAT_CPU_EVENT_MASK_ALIGN_HALTED_MASK (1L<<7) +#define BNX2_TPAT_CPU_EVENT_MASK_FIO_ABORT_MASK (1L<<8) +#define BNX2_TPAT_CPU_EVENT_MASK_SOFT_HALTED_MASK (1L<<10) +#define BNX2_TPAT_CPU_EVENT_MASK_SPAD_UNDERFLOW_MASK (1L<<11) +#define BNX2_TPAT_CPU_EVENT_MASK_INTERRUPT_MASK (1L<<12) + +#define BNX2_TPAT_CPU_PROGRAM_COUNTER 0x0008501c +#define BNX2_TPAT_CPU_INSTRUCTION 0x00085020 +#define BNX2_TPAT_CPU_DATA_ACCESS 0x00085024 +#define BNX2_TPAT_CPU_INTERRUPT_ENABLE 0x00085028 +#define BNX2_TPAT_CPU_INTERRUPT_VECTOR 0x0008502c +#define BNX2_TPAT_CPU_INTERRUPT_SAVED_PC 0x00085030 +#define BNX2_TPAT_CPU_HW_BREAKPOINT 0x00085034 +#define BNX2_TPAT_CPU_HW_BREAKPOINT_DISABLE (1L<<0) +#define BNX2_TPAT_CPU_HW_BREAKPOINT_ADDRESS (0x3fffffffL<<2) + +#define BNX2_TPAT_CPU_DEBUG_VECT_PEEK 0x00085038 +#define BNX2_TPAT_CPU_DEBUG_VECT_PEEK_1_VALUE (0x7ffL<<0) +#define BNX2_TPAT_CPU_DEBUG_VECT_PEEK_1_PEEK_EN (1L<<11) +#define BNX2_TPAT_CPU_DEBUG_VECT_PEEK_1_SEL (0xfL<<12) +#define BNX2_TPAT_CPU_DEBUG_VECT_PEEK_2_VALUE (0x7ffL<<16) +#define BNX2_TPAT_CPU_DEBUG_VECT_PEEK_2_PEEK_EN (1L<<27) +#define BNX2_TPAT_CPU_DEBUG_VECT_PEEK_2_SEL (0xfL<<28) + +#define BNX2_TPAT_CPU_LAST_BRANCH_ADDR 0x00085048 +#define BNX2_TPAT_CPU_LAST_BRANCH_ADDR_TYPE (1L<<1) +#define BNX2_TPAT_CPU_LAST_BRANCH_ADDR_TYPE_JUMP (0L<<1) +#define BNX2_TPAT_CPU_LAST_BRANCH_ADDR_TYPE_BRANCH (1L<<1) +#define BNX2_TPAT_CPU_LAST_BRANCH_ADDR_LBA (0x3fffffffL<<2) + +#define BNX2_TPAT_CPU_REG_FILE 0x00085200 +#define BNX2_TPAT_FTQ_DATA 0x000853c0 +#define BNX2_TPAT_FTQ_CMD 0x000853f8 +#define BNX2_TPAT_FTQ_CMD_OFFSET (0x3ffL<<0) +#define BNX2_TPAT_FTQ_CMD_WR_TOP (1L<<10) +#define BNX2_TPAT_FTQ_CMD_WR_TOP_0 (0L<<10) +#define BNX2_TPAT_FTQ_CMD_WR_TOP_1 (1L<<10) +#define BNX2_TPAT_FTQ_CMD_SFT_RESET (1L<<25) +#define BNX2_TPAT_FTQ_CMD_RD_DATA (1L<<26) +#define BNX2_TPAT_FTQ_CMD_ADD_INTERVEN (1L<<27) +#define BNX2_TPAT_FTQ_CMD_ADD_DATA (1L<<28) +#define BNX2_TPAT_FTQ_CMD_INTERVENE_CLR (1L<<29) +#define BNX2_TPAT_FTQ_CMD_POP (1L<<30) +#define BNX2_TPAT_FTQ_CMD_BUSY (1L<<31) + +#define BNX2_TPAT_FTQ_CTL 0x000853fc +#define BNX2_TPAT_FTQ_CTL_INTERVENE (1L<<0) +#define BNX2_TPAT_FTQ_CTL_OVERFLOW (1L<<1) +#define BNX2_TPAT_FTQ_CTL_FORCE_INTERVENE (1L<<2) +#define BNX2_TPAT_FTQ_CTL_MAX_DEPTH (0x3ffL<<12) +#define BNX2_TPAT_FTQ_CTL_CUR_DEPTH (0x3ffL<<22) + +#define BNX2_TPAT_SCRATCH 0x000a0000 + + +/* + * rxp_reg definition + * offset: 0xc0000 + */ +#define BNX2_RXP_CPU_MODE 0x000c5000 +#define BNX2_RXP_CPU_MODE_LOCAL_RST (1L<<0) +#define BNX2_RXP_CPU_MODE_STEP_ENA (1L<<1) +#define BNX2_RXP_CPU_MODE_PAGE_0_DATA_ENA (1L<<2) +#define BNX2_RXP_CPU_MODE_PAGE_0_INST_ENA (1L<<3) +#define BNX2_RXP_CPU_MODE_MSG_BIT1 (1L<<6) +#define BNX2_RXP_CPU_MODE_INTERRUPT_ENA (1L<<7) +#define BNX2_RXP_CPU_MODE_SOFT_HALT (1L<<10) +#define BNX2_RXP_CPU_MODE_BAD_DATA_HALT_ENA (1L<<11) +#define BNX2_RXP_CPU_MODE_BAD_INST_HALT_ENA (1L<<12) +#define BNX2_RXP_CPU_MODE_FIO_ABORT_HALT_ENA (1L<<13) +#define BNX2_RXP_CPU_MODE_SPAD_UNDERFLOW_HALT_ENA (1L<<15) + +#define BNX2_RXP_CPU_STATE 0x000c5004 +#define BNX2_RXP_CPU_STATE_BREAKPOINT (1L<<0) +#define BNX2_RXP_CPU_STATE_BAD_INST_HALTED (1L<<2) +#define BNX2_RXP_CPU_STATE_PAGE_0_DATA_HALTED (1L<<3) +#define BNX2_RXP_CPU_STATE_PAGE_0_INST_HALTED (1L<<4) +#define BNX2_RXP_CPU_STATE_BAD_DATA_ADDR_HALTED (1L<<5) +#define BNX2_RXP_CPU_STATE_BAD_pc_HALTED (1L<<6) +#define BNX2_RXP_CPU_STATE_ALIGN_HALTED (1L<<7) +#define BNX2_RXP_CPU_STATE_FIO_ABORT_HALTED (1L<<8) +#define BNX2_RXP_CPU_STATE_SOFT_HALTED (1L<<10) +#define BNX2_RXP_CPU_STATE_SPAD_UNDERFLOW (1L<<11) +#define BNX2_RXP_CPU_STATE_INTERRRUPT (1L<<12) +#define BNX2_RXP_CPU_STATE_DATA_ACCESS_STALL (1L<<14) +#define BNX2_RXP_CPU_STATE_INST_FETCH_STALL (1L<<15) +#define BNX2_RXP_CPU_STATE_BLOCKED_READ (1L<<31) + +#define BNX2_RXP_CPU_EVENT_MASK 0x000c5008 +#define BNX2_RXP_CPU_EVENT_MASK_BREAKPOINT_MASK (1L<<0) +#define BNX2_RXP_CPU_EVENT_MASK_BAD_INST_HALTED_MASK (1L<<2) +#define BNX2_RXP_CPU_EVENT_MASK_PAGE_0_DATA_HALTED_MASK (1L<<3) +#define BNX2_RXP_CPU_EVENT_MASK_PAGE_0_INST_HALTED_MASK (1L<<4) +#define BNX2_RXP_CPU_EVENT_MASK_BAD_DATA_ADDR_HALTED_MASK (1L<<5) +#define BNX2_RXP_CPU_EVENT_MASK_BAD_PC_HALTED_MASK (1L<<6) +#define BNX2_RXP_CPU_EVENT_MASK_ALIGN_HALTED_MASK (1L<<7) +#define BNX2_RXP_CPU_EVENT_MASK_FIO_ABORT_MASK (1L<<8) +#define BNX2_RXP_CPU_EVENT_MASK_SOFT_HALTED_MASK (1L<<10) +#define BNX2_RXP_CPU_EVENT_MASK_SPAD_UNDERFLOW_MASK (1L<<11) +#define BNX2_RXP_CPU_EVENT_MASK_INTERRUPT_MASK (1L<<12) + +#define BNX2_RXP_CPU_PROGRAM_COUNTER 0x000c501c +#define BNX2_RXP_CPU_INSTRUCTION 0x000c5020 +#define BNX2_RXP_CPU_DATA_ACCESS 0x000c5024 +#define BNX2_RXP_CPU_INTERRUPT_ENABLE 0x000c5028 +#define BNX2_RXP_CPU_INTERRUPT_VECTOR 0x000c502c +#define BNX2_RXP_CPU_INTERRUPT_SAVED_PC 0x000c5030 +#define BNX2_RXP_CPU_HW_BREAKPOINT 0x000c5034 +#define BNX2_RXP_CPU_HW_BREAKPOINT_DISABLE (1L<<0) +#define BNX2_RXP_CPU_HW_BREAKPOINT_ADDRESS (0x3fffffffL<<2) + +#define BNX2_RXP_CPU_DEBUG_VECT_PEEK 0x000c5038 +#define BNX2_RXP_CPU_DEBUG_VECT_PEEK_1_VALUE (0x7ffL<<0) +#define BNX2_RXP_CPU_DEBUG_VECT_PEEK_1_PEEK_EN (1L<<11) +#define BNX2_RXP_CPU_DEBUG_VECT_PEEK_1_SEL (0xfL<<12) +#define BNX2_RXP_CPU_DEBUG_VECT_PEEK_2_VALUE (0x7ffL<<16) +#define BNX2_RXP_CPU_DEBUG_VECT_PEEK_2_PEEK_EN (1L<<27) +#define BNX2_RXP_CPU_DEBUG_VECT_PEEK_2_SEL (0xfL<<28) + +#define BNX2_RXP_CPU_LAST_BRANCH_ADDR 0x000c5048 +#define BNX2_RXP_CPU_LAST_BRANCH_ADDR_TYPE (1L<<1) +#define BNX2_RXP_CPU_LAST_BRANCH_ADDR_TYPE_JUMP (0L<<1) +#define BNX2_RXP_CPU_LAST_BRANCH_ADDR_TYPE_BRANCH (1L<<1) +#define BNX2_RXP_CPU_LAST_BRANCH_ADDR_LBA (0x3fffffffL<<2) + +#define BNX2_RXP_CPU_REG_FILE 0x000c5200 +#define BNX2_RXP_CFTQ_DATA 0x000c5380 +#define BNX2_RXP_CFTQ_CMD 0x000c53b8 +#define BNX2_RXP_CFTQ_CMD_OFFSET (0x3ffL<<0) +#define BNX2_RXP_CFTQ_CMD_WR_TOP (1L<<10) +#define BNX2_RXP_CFTQ_CMD_WR_TOP_0 (0L<<10) +#define BNX2_RXP_CFTQ_CMD_WR_TOP_1 (1L<<10) +#define BNX2_RXP_CFTQ_CMD_SFT_RESET (1L<<25) +#define BNX2_RXP_CFTQ_CMD_RD_DATA (1L<<26) +#define BNX2_RXP_CFTQ_CMD_ADD_INTERVEN (1L<<27) +#define BNX2_RXP_CFTQ_CMD_ADD_DATA (1L<<28) +#define BNX2_RXP_CFTQ_CMD_INTERVENE_CLR (1L<<29) +#define BNX2_RXP_CFTQ_CMD_POP (1L<<30) +#define BNX2_RXP_CFTQ_CMD_BUSY (1L<<31) + +#define BNX2_RXP_CFTQ_CTL 0x000c53bc +#define BNX2_RXP_CFTQ_CTL_INTERVENE (1L<<0) +#define BNX2_RXP_CFTQ_CTL_OVERFLOW (1L<<1) +#define BNX2_RXP_CFTQ_CTL_FORCE_INTERVENE (1L<<2) +#define BNX2_RXP_CFTQ_CTL_MAX_DEPTH (0x3ffL<<12) +#define BNX2_RXP_CFTQ_CTL_CUR_DEPTH (0x3ffL<<22) + +#define BNX2_RXP_FTQ_DATA 0x000c53c0 +#define BNX2_RXP_FTQ_CMD 0x000c53f8 +#define BNX2_RXP_FTQ_CMD_OFFSET (0x3ffL<<0) +#define BNX2_RXP_FTQ_CMD_WR_TOP (1L<<10) +#define BNX2_RXP_FTQ_CMD_WR_TOP_0 (0L<<10) +#define BNX2_RXP_FTQ_CMD_WR_TOP_1 (1L<<10) +#define BNX2_RXP_FTQ_CMD_SFT_RESET (1L<<25) +#define BNX2_RXP_FTQ_CMD_RD_DATA (1L<<26) +#define BNX2_RXP_FTQ_CMD_ADD_INTERVEN (1L<<27) +#define BNX2_RXP_FTQ_CMD_ADD_DATA (1L<<28) +#define BNX2_RXP_FTQ_CMD_INTERVENE_CLR (1L<<29) +#define BNX2_RXP_FTQ_CMD_POP (1L<<30) +#define BNX2_RXP_FTQ_CMD_BUSY (1L<<31) + +#define BNX2_RXP_FTQ_CTL 0x000c53fc +#define BNX2_RXP_FTQ_CTL_INTERVENE (1L<<0) +#define BNX2_RXP_FTQ_CTL_OVERFLOW (1L<<1) +#define BNX2_RXP_FTQ_CTL_FORCE_INTERVENE (1L<<2) +#define BNX2_RXP_FTQ_CTL_MAX_DEPTH (0x3ffL<<12) +#define BNX2_RXP_FTQ_CTL_CUR_DEPTH (0x3ffL<<22) + +#define BNX2_RXP_SCRATCH 0x000e0000 + + +/* + * com_reg definition + * offset: 0x100000 + */ +#define BNX2_COM_CPU_MODE 0x00105000 +#define BNX2_COM_CPU_MODE_LOCAL_RST (1L<<0) +#define BNX2_COM_CPU_MODE_STEP_ENA (1L<<1) +#define BNX2_COM_CPU_MODE_PAGE_0_DATA_ENA (1L<<2) +#define BNX2_COM_CPU_MODE_PAGE_0_INST_ENA (1L<<3) +#define BNX2_COM_CPU_MODE_MSG_BIT1 (1L<<6) +#define BNX2_COM_CPU_MODE_INTERRUPT_ENA (1L<<7) +#define BNX2_COM_CPU_MODE_SOFT_HALT (1L<<10) +#define BNX2_COM_CPU_MODE_BAD_DATA_HALT_ENA (1L<<11) +#define BNX2_COM_CPU_MODE_BAD_INST_HALT_ENA (1L<<12) +#define BNX2_COM_CPU_MODE_FIO_ABORT_HALT_ENA (1L<<13) +#define BNX2_COM_CPU_MODE_SPAD_UNDERFLOW_HALT_ENA (1L<<15) + +#define BNX2_COM_CPU_STATE 0x00105004 +#define BNX2_COM_CPU_STATE_BREAKPOINT (1L<<0) +#define BNX2_COM_CPU_STATE_BAD_INST_HALTED (1L<<2) +#define BNX2_COM_CPU_STATE_PAGE_0_DATA_HALTED (1L<<3) +#define BNX2_COM_CPU_STATE_PAGE_0_INST_HALTED (1L<<4) +#define BNX2_COM_CPU_STATE_BAD_DATA_ADDR_HALTED (1L<<5) +#define BNX2_COM_CPU_STATE_BAD_pc_HALTED (1L<<6) +#define BNX2_COM_CPU_STATE_ALIGN_HALTED (1L<<7) +#define BNX2_COM_CPU_STATE_FIO_ABORT_HALTED (1L<<8) +#define BNX2_COM_CPU_STATE_SOFT_HALTED (1L<<10) +#define BNX2_COM_CPU_STATE_SPAD_UNDERFLOW (1L<<11) +#define BNX2_COM_CPU_STATE_INTERRRUPT (1L<<12) +#define BNX2_COM_CPU_STATE_DATA_ACCESS_STALL (1L<<14) +#define BNX2_COM_CPU_STATE_INST_FETCH_STALL (1L<<15) +#define BNX2_COM_CPU_STATE_BLOCKED_READ (1L<<31) + +#define BNX2_COM_CPU_EVENT_MASK 0x00105008 +#define BNX2_COM_CPU_EVENT_MASK_BREAKPOINT_MASK (1L<<0) +#define BNX2_COM_CPU_EVENT_MASK_BAD_INST_HALTED_MASK (1L<<2) +#define BNX2_COM_CPU_EVENT_MASK_PAGE_0_DATA_HALTED_MASK (1L<<3) +#define BNX2_COM_CPU_EVENT_MASK_PAGE_0_INST_HALTED_MASK (1L<<4) +#define BNX2_COM_CPU_EVENT_MASK_BAD_DATA_ADDR_HALTED_MASK (1L<<5) +#define BNX2_COM_CPU_EVENT_MASK_BAD_PC_HALTED_MASK (1L<<6) +#define BNX2_COM_CPU_EVENT_MASK_ALIGN_HALTED_MASK (1L<<7) +#define BNX2_COM_CPU_EVENT_MASK_FIO_ABORT_MASK (1L<<8) +#define BNX2_COM_CPU_EVENT_MASK_SOFT_HALTED_MASK (1L<<10) +#define BNX2_COM_CPU_EVENT_MASK_SPAD_UNDERFLOW_MASK (1L<<11) +#define BNX2_COM_CPU_EVENT_MASK_INTERRUPT_MASK (1L<<12) + +#define BNX2_COM_CPU_PROGRAM_COUNTER 0x0010501c +#define BNX2_COM_CPU_INSTRUCTION 0x00105020 +#define BNX2_COM_CPU_DATA_ACCESS 0x00105024 +#define BNX2_COM_CPU_INTERRUPT_ENABLE 0x00105028 +#define BNX2_COM_CPU_INTERRUPT_VECTOR 0x0010502c +#define BNX2_COM_CPU_INTERRUPT_SAVED_PC 0x00105030 +#define BNX2_COM_CPU_HW_BREAKPOINT 0x00105034 +#define BNX2_COM_CPU_HW_BREAKPOINT_DISABLE (1L<<0) +#define BNX2_COM_CPU_HW_BREAKPOINT_ADDRESS (0x3fffffffL<<2) + +#define BNX2_COM_CPU_DEBUG_VECT_PEEK 0x00105038 +#define BNX2_COM_CPU_DEBUG_VECT_PEEK_1_VALUE (0x7ffL<<0) +#define BNX2_COM_CPU_DEBUG_VECT_PEEK_1_PEEK_EN (1L<<11) +#define BNX2_COM_CPU_DEBUG_VECT_PEEK_1_SEL (0xfL<<12) +#define BNX2_COM_CPU_DEBUG_VECT_PEEK_2_VALUE (0x7ffL<<16) +#define BNX2_COM_CPU_DEBUG_VECT_PEEK_2_PEEK_EN (1L<<27) +#define BNX2_COM_CPU_DEBUG_VECT_PEEK_2_SEL (0xfL<<28) + +#define BNX2_COM_CPU_LAST_BRANCH_ADDR 0x00105048 +#define BNX2_COM_CPU_LAST_BRANCH_ADDR_TYPE (1L<<1) +#define BNX2_COM_CPU_LAST_BRANCH_ADDR_TYPE_JUMP (0L<<1) +#define BNX2_COM_CPU_LAST_BRANCH_ADDR_TYPE_BRANCH (1L<<1) +#define BNX2_COM_CPU_LAST_BRANCH_ADDR_LBA (0x3fffffffL<<2) + +#define BNX2_COM_CPU_REG_FILE 0x00105200 +#define BNX2_COM_COMXQ_FTQ_DATA 0x00105340 +#define BNX2_COM_COMXQ_FTQ_CMD 0x00105378 +#define BNX2_COM_COMXQ_FTQ_CMD_OFFSET (0x3ffL<<0) +#define BNX2_COM_COMXQ_FTQ_CMD_WR_TOP (1L<<10) +#define BNX2_COM_COMXQ_FTQ_CMD_WR_TOP_0 (0L<<10) +#define BNX2_COM_COMXQ_FTQ_CMD_WR_TOP_1 (1L<<10) +#define BNX2_COM_COMXQ_FTQ_CMD_SFT_RESET (1L<<25) +#define BNX2_COM_COMXQ_FTQ_CMD_RD_DATA (1L<<26) +#define BNX2_COM_COMXQ_FTQ_CMD_ADD_INTERVEN (1L<<27) +#define BNX2_COM_COMXQ_FTQ_CMD_ADD_DATA (1L<<28) +#define BNX2_COM_COMXQ_FTQ_CMD_INTERVENE_CLR (1L<<29) +#define BNX2_COM_COMXQ_FTQ_CMD_POP (1L<<30) +#define BNX2_COM_COMXQ_FTQ_CMD_BUSY (1L<<31) + +#define BNX2_COM_COMXQ_FTQ_CTL 0x0010537c +#define BNX2_COM_COMXQ_FTQ_CTL_INTERVENE (1L<<0) +#define BNX2_COM_COMXQ_FTQ_CTL_OVERFLOW (1L<<1) +#define BNX2_COM_COMXQ_FTQ_CTL_FORCE_INTERVENE (1L<<2) +#define BNX2_COM_COMXQ_FTQ_CTL_MAX_DEPTH (0x3ffL<<12) +#define BNX2_COM_COMXQ_FTQ_CTL_CUR_DEPTH (0x3ffL<<22) + +#define BNX2_COM_COMTQ_FTQ_DATA 0x00105380 +#define BNX2_COM_COMTQ_FTQ_CMD 0x001053b8 +#define BNX2_COM_COMTQ_FTQ_CMD_OFFSET (0x3ffL<<0) +#define BNX2_COM_COMTQ_FTQ_CMD_WR_TOP (1L<<10) +#define BNX2_COM_COMTQ_FTQ_CMD_WR_TOP_0 (0L<<10) +#define BNX2_COM_COMTQ_FTQ_CMD_WR_TOP_1 (1L<<10) +#define BNX2_COM_COMTQ_FTQ_CMD_SFT_RESET (1L<<25) +#define BNX2_COM_COMTQ_FTQ_CMD_RD_DATA (1L<<26) +#define BNX2_COM_COMTQ_FTQ_CMD_ADD_INTERVEN (1L<<27) +#define BNX2_COM_COMTQ_FTQ_CMD_ADD_DATA (1L<<28) +#define BNX2_COM_COMTQ_FTQ_CMD_INTERVENE_CLR (1L<<29) +#define BNX2_COM_COMTQ_FTQ_CMD_POP (1L<<30) +#define BNX2_COM_COMTQ_FTQ_CMD_BUSY (1L<<31) + +#define BNX2_COM_COMTQ_FTQ_CTL 0x001053bc +#define BNX2_COM_COMTQ_FTQ_CTL_INTERVENE (1L<<0) +#define BNX2_COM_COMTQ_FTQ_CTL_OVERFLOW (1L<<1) +#define BNX2_COM_COMTQ_FTQ_CTL_FORCE_INTERVENE (1L<<2) +#define BNX2_COM_COMTQ_FTQ_CTL_MAX_DEPTH (0x3ffL<<12) +#define BNX2_COM_COMTQ_FTQ_CTL_CUR_DEPTH (0x3ffL<<22) + +#define BNX2_COM_COMQ_FTQ_DATA 0x001053c0 +#define BNX2_COM_COMQ_FTQ_CMD 0x001053f8 +#define BNX2_COM_COMQ_FTQ_CMD_OFFSET (0x3ffL<<0) +#define BNX2_COM_COMQ_FTQ_CMD_WR_TOP (1L<<10) +#define BNX2_COM_COMQ_FTQ_CMD_WR_TOP_0 (0L<<10) +#define BNX2_COM_COMQ_FTQ_CMD_WR_TOP_1 (1L<<10) +#define BNX2_COM_COMQ_FTQ_CMD_SFT_RESET (1L<<25) +#define BNX2_COM_COMQ_FTQ_CMD_RD_DATA (1L<<26) +#define BNX2_COM_COMQ_FTQ_CMD_ADD_INTERVEN (1L<<27) +#define BNX2_COM_COMQ_FTQ_CMD_ADD_DATA (1L<<28) +#define BNX2_COM_COMQ_FTQ_CMD_INTERVENE_CLR (1L<<29) +#define BNX2_COM_COMQ_FTQ_CMD_POP (1L<<30) +#define BNX2_COM_COMQ_FTQ_CMD_BUSY (1L<<31) + +#define BNX2_COM_COMQ_FTQ_CTL 0x001053fc +#define BNX2_COM_COMQ_FTQ_CTL_INTERVENE (1L<<0) +#define BNX2_COM_COMQ_FTQ_CTL_OVERFLOW (1L<<1) +#define BNX2_COM_COMQ_FTQ_CTL_FORCE_INTERVENE (1L<<2) +#define BNX2_COM_COMQ_FTQ_CTL_MAX_DEPTH (0x3ffL<<12) +#define BNX2_COM_COMQ_FTQ_CTL_CUR_DEPTH (0x3ffL<<22) + +#define BNX2_COM_SCRATCH 0x00120000 + + +/* + * cp_reg definition + * offset: 0x180000 + */ +#define BNX2_CP_CPU_MODE 0x00185000 +#define BNX2_CP_CPU_MODE_LOCAL_RST (1L<<0) +#define BNX2_CP_CPU_MODE_STEP_ENA (1L<<1) +#define BNX2_CP_CPU_MODE_PAGE_0_DATA_ENA (1L<<2) +#define BNX2_CP_CPU_MODE_PAGE_0_INST_ENA (1L<<3) +#define BNX2_CP_CPU_MODE_MSG_BIT1 (1L<<6) +#define BNX2_CP_CPU_MODE_INTERRUPT_ENA (1L<<7) +#define BNX2_CP_CPU_MODE_SOFT_HALT (1L<<10) +#define BNX2_CP_CPU_MODE_BAD_DATA_HALT_ENA (1L<<11) +#define BNX2_CP_CPU_MODE_BAD_INST_HALT_ENA (1L<<12) +#define BNX2_CP_CPU_MODE_FIO_ABORT_HALT_ENA (1L<<13) +#define BNX2_CP_CPU_MODE_SPAD_UNDERFLOW_HALT_ENA (1L<<15) + +#define BNX2_CP_CPU_STATE 0x00185004 +#define BNX2_CP_CPU_STATE_BREAKPOINT (1L<<0) +#define BNX2_CP_CPU_STATE_BAD_INST_HALTED (1L<<2) +#define BNX2_CP_CPU_STATE_PAGE_0_DATA_HALTED (1L<<3) +#define BNX2_CP_CPU_STATE_PAGE_0_INST_HALTED (1L<<4) +#define BNX2_CP_CPU_STATE_BAD_DATA_ADDR_HALTED (1L<<5) +#define BNX2_CP_CPU_STATE_BAD_pc_HALTED (1L<<6) +#define BNX2_CP_CPU_STATE_ALIGN_HALTED (1L<<7) +#define BNX2_CP_CPU_STATE_FIO_ABORT_HALTED (1L<<8) +#define BNX2_CP_CPU_STATE_SOFT_HALTED (1L<<10) +#define BNX2_CP_CPU_STATE_SPAD_UNDERFLOW (1L<<11) +#define BNX2_CP_CPU_STATE_INTERRRUPT (1L<<12) +#define BNX2_CP_CPU_STATE_DATA_ACCESS_STALL (1L<<14) +#define BNX2_CP_CPU_STATE_INST_FETCH_STALL (1L<<15) +#define BNX2_CP_CPU_STATE_BLOCKED_READ (1L<<31) + +#define BNX2_CP_CPU_EVENT_MASK 0x00185008 +#define BNX2_CP_CPU_EVENT_MASK_BREAKPOINT_MASK (1L<<0) +#define BNX2_CP_CPU_EVENT_MASK_BAD_INST_HALTED_MASK (1L<<2) +#define BNX2_CP_CPU_EVENT_MASK_PAGE_0_DATA_HALTED_MASK (1L<<3) +#define BNX2_CP_CPU_EVENT_MASK_PAGE_0_INST_HALTED_MASK (1L<<4) +#define BNX2_CP_CPU_EVENT_MASK_BAD_DATA_ADDR_HALTED_MASK (1L<<5) +#define BNX2_CP_CPU_EVENT_MASK_BAD_PC_HALTED_MASK (1L<<6) +#define BNX2_CP_CPU_EVENT_MASK_ALIGN_HALTED_MASK (1L<<7) +#define BNX2_CP_CPU_EVENT_MASK_FIO_ABORT_MASK (1L<<8) +#define BNX2_CP_CPU_EVENT_MASK_SOFT_HALTED_MASK (1L<<10) +#define BNX2_CP_CPU_EVENT_MASK_SPAD_UNDERFLOW_MASK (1L<<11) +#define BNX2_CP_CPU_EVENT_MASK_INTERRUPT_MASK (1L<<12) + +#define BNX2_CP_CPU_PROGRAM_COUNTER 0x0018501c +#define BNX2_CP_CPU_INSTRUCTION 0x00185020 +#define BNX2_CP_CPU_DATA_ACCESS 0x00185024 +#define BNX2_CP_CPU_INTERRUPT_ENABLE 0x00185028 +#define BNX2_CP_CPU_INTERRUPT_VECTOR 0x0018502c +#define BNX2_CP_CPU_INTERRUPT_SAVED_PC 0x00185030 +#define BNX2_CP_CPU_HW_BREAKPOINT 0x00185034 +#define BNX2_CP_CPU_HW_BREAKPOINT_DISABLE (1L<<0) +#define BNX2_CP_CPU_HW_BREAKPOINT_ADDRESS (0x3fffffffL<<2) + +#define BNX2_CP_CPU_DEBUG_VECT_PEEK 0x00185038 +#define BNX2_CP_CPU_DEBUG_VECT_PEEK_1_VALUE (0x7ffL<<0) +#define BNX2_CP_CPU_DEBUG_VECT_PEEK_1_PEEK_EN (1L<<11) +#define BNX2_CP_CPU_DEBUG_VECT_PEEK_1_SEL (0xfL<<12) +#define BNX2_CP_CPU_DEBUG_VECT_PEEK_2_VALUE (0x7ffL<<16) +#define BNX2_CP_CPU_DEBUG_VECT_PEEK_2_PEEK_EN (1L<<27) +#define BNX2_CP_CPU_DEBUG_VECT_PEEK_2_SEL (0xfL<<28) + +#define BNX2_CP_CPU_LAST_BRANCH_ADDR 0x00185048 +#define BNX2_CP_CPU_LAST_BRANCH_ADDR_TYPE (1L<<1) +#define BNX2_CP_CPU_LAST_BRANCH_ADDR_TYPE_JUMP (0L<<1) +#define BNX2_CP_CPU_LAST_BRANCH_ADDR_TYPE_BRANCH (1L<<1) +#define BNX2_CP_CPU_LAST_BRANCH_ADDR_LBA (0x3fffffffL<<2) + +#define BNX2_CP_CPU_REG_FILE 0x00185200 +#define BNX2_CP_CPQ_FTQ_DATA 0x001853c0 +#define BNX2_CP_CPQ_FTQ_CMD 0x001853f8 +#define BNX2_CP_CPQ_FTQ_CMD_OFFSET (0x3ffL<<0) +#define BNX2_CP_CPQ_FTQ_CMD_WR_TOP (1L<<10) +#define BNX2_CP_CPQ_FTQ_CMD_WR_TOP_0 (0L<<10) +#define BNX2_CP_CPQ_FTQ_CMD_WR_TOP_1 (1L<<10) +#define BNX2_CP_CPQ_FTQ_CMD_SFT_RESET (1L<<25) +#define BNX2_CP_CPQ_FTQ_CMD_RD_DATA (1L<<26) +#define BNX2_CP_CPQ_FTQ_CMD_ADD_INTERVEN (1L<<27) +#define BNX2_CP_CPQ_FTQ_CMD_ADD_DATA (1L<<28) +#define BNX2_CP_CPQ_FTQ_CMD_INTERVENE_CLR (1L<<29) +#define BNX2_CP_CPQ_FTQ_CMD_POP (1L<<30) +#define BNX2_CP_CPQ_FTQ_CMD_BUSY (1L<<31) + +#define BNX2_CP_CPQ_FTQ_CTL 0x001853fc +#define BNX2_CP_CPQ_FTQ_CTL_INTERVENE (1L<<0) +#define BNX2_CP_CPQ_FTQ_CTL_OVERFLOW (1L<<1) +#define BNX2_CP_CPQ_FTQ_CTL_FORCE_INTERVENE (1L<<2) +#define BNX2_CP_CPQ_FTQ_CTL_MAX_DEPTH (0x3ffL<<12) +#define BNX2_CP_CPQ_FTQ_CTL_CUR_DEPTH (0x3ffL<<22) + +#define BNX2_CP_SCRATCH 0x001a0000 + + +/* + * mcp_reg definition + * offset: 0x140000 + */ +#define BNX2_MCP_CPU_MODE 0x00145000 +#define BNX2_MCP_CPU_MODE_LOCAL_RST (1L<<0) +#define BNX2_MCP_CPU_MODE_STEP_ENA (1L<<1) +#define BNX2_MCP_CPU_MODE_PAGE_0_DATA_ENA (1L<<2) +#define BNX2_MCP_CPU_MODE_PAGE_0_INST_ENA (1L<<3) +#define BNX2_MCP_CPU_MODE_MSG_BIT1 (1L<<6) +#define BNX2_MCP_CPU_MODE_INTERRUPT_ENA (1L<<7) +#define BNX2_MCP_CPU_MODE_SOFT_HALT (1L<<10) +#define BNX2_MCP_CPU_MODE_BAD_DATA_HALT_ENA (1L<<11) +#define BNX2_MCP_CPU_MODE_BAD_INST_HALT_ENA (1L<<12) +#define BNX2_MCP_CPU_MODE_FIO_ABORT_HALT_ENA (1L<<13) +#define BNX2_MCP_CPU_MODE_SPAD_UNDERFLOW_HALT_ENA (1L<<15) + +#define BNX2_MCP_CPU_STATE 0x00145004 +#define BNX2_MCP_CPU_STATE_BREAKPOINT (1L<<0) +#define BNX2_MCP_CPU_STATE_BAD_INST_HALTED (1L<<2) +#define BNX2_MCP_CPU_STATE_PAGE_0_DATA_HALTED (1L<<3) +#define BNX2_MCP_CPU_STATE_PAGE_0_INST_HALTED (1L<<4) +#define BNX2_MCP_CPU_STATE_BAD_DATA_ADDR_HALTED (1L<<5) +#define BNX2_MCP_CPU_STATE_BAD_pc_HALTED (1L<<6) +#define BNX2_MCP_CPU_STATE_ALIGN_HALTED (1L<<7) +#define BNX2_MCP_CPU_STATE_FIO_ABORT_HALTED (1L<<8) +#define BNX2_MCP_CPU_STATE_SOFT_HALTED (1L<<10) +#define BNX2_MCP_CPU_STATE_SPAD_UNDERFLOW (1L<<11) +#define BNX2_MCP_CPU_STATE_INTERRRUPT (1L<<12) +#define BNX2_MCP_CPU_STATE_DATA_ACCESS_STALL (1L<<14) +#define BNX2_MCP_CPU_STATE_INST_FETCH_STALL (1L<<15) +#define BNX2_MCP_CPU_STATE_BLOCKED_READ (1L<<31) + +#define BNX2_MCP_CPU_EVENT_MASK 0x00145008 +#define BNX2_MCP_CPU_EVENT_MASK_BREAKPOINT_MASK (1L<<0) +#define BNX2_MCP_CPU_EVENT_MASK_BAD_INST_HALTED_MASK (1L<<2) +#define BNX2_MCP_CPU_EVENT_MASK_PAGE_0_DATA_HALTED_MASK (1L<<3) +#define BNX2_MCP_CPU_EVENT_MASK_PAGE_0_INST_HALTED_MASK (1L<<4) +#define BNX2_MCP_CPU_EVENT_MASK_BAD_DATA_ADDR_HALTED_MASK (1L<<5) +#define BNX2_MCP_CPU_EVENT_MASK_BAD_PC_HALTED_MASK (1L<<6) +#define BNX2_MCP_CPU_EVENT_MASK_ALIGN_HALTED_MASK (1L<<7) +#define BNX2_MCP_CPU_EVENT_MASK_FIO_ABORT_MASK (1L<<8) +#define BNX2_MCP_CPU_EVENT_MASK_SOFT_HALTED_MASK (1L<<10) +#define BNX2_MCP_CPU_EVENT_MASK_SPAD_UNDERFLOW_MASK (1L<<11) +#define BNX2_MCP_CPU_EVENT_MASK_INTERRUPT_MASK (1L<<12) + +#define BNX2_MCP_CPU_PROGRAM_COUNTER 0x0014501c +#define BNX2_MCP_CPU_INSTRUCTION 0x00145020 +#define BNX2_MCP_CPU_DATA_ACCESS 0x00145024 +#define BNX2_MCP_CPU_INTERRUPT_ENABLE 0x00145028 +#define BNX2_MCP_CPU_INTERRUPT_VECTOR 0x0014502c +#define BNX2_MCP_CPU_INTERRUPT_SAVED_PC 0x00145030 +#define BNX2_MCP_CPU_HW_BREAKPOINT 0x00145034 +#define BNX2_MCP_CPU_HW_BREAKPOINT_DISABLE (1L<<0) +#define BNX2_MCP_CPU_HW_BREAKPOINT_ADDRESS (0x3fffffffL<<2) + +#define BNX2_MCP_CPU_DEBUG_VECT_PEEK 0x00145038 +#define BNX2_MCP_CPU_DEBUG_VECT_PEEK_1_VALUE (0x7ffL<<0) +#define BNX2_MCP_CPU_DEBUG_VECT_PEEK_1_PEEK_EN (1L<<11) +#define BNX2_MCP_CPU_DEBUG_VECT_PEEK_1_SEL (0xfL<<12) +#define BNX2_MCP_CPU_DEBUG_VECT_PEEK_2_VALUE (0x7ffL<<16) +#define BNX2_MCP_CPU_DEBUG_VECT_PEEK_2_PEEK_EN (1L<<27) +#define BNX2_MCP_CPU_DEBUG_VECT_PEEK_2_SEL (0xfL<<28) + +#define BNX2_MCP_CPU_LAST_BRANCH_ADDR 0x00145048 +#define BNX2_MCP_CPU_LAST_BRANCH_ADDR_TYPE (1L<<1) +#define BNX2_MCP_CPU_LAST_BRANCH_ADDR_TYPE_JUMP (0L<<1) +#define BNX2_MCP_CPU_LAST_BRANCH_ADDR_TYPE_BRANCH (1L<<1) +#define BNX2_MCP_CPU_LAST_BRANCH_ADDR_LBA (0x3fffffffL<<2) + +#define BNX2_MCP_CPU_REG_FILE 0x00145200 +#define BNX2_MCP_MCPQ_FTQ_DATA 0x001453c0 +#define BNX2_MCP_MCPQ_FTQ_CMD 0x001453f8 +#define BNX2_MCP_MCPQ_FTQ_CMD_OFFSET (0x3ffL<<0) +#define BNX2_MCP_MCPQ_FTQ_CMD_WR_TOP (1L<<10) +#define BNX2_MCP_MCPQ_FTQ_CMD_WR_TOP_0 (0L<<10) +#define BNX2_MCP_MCPQ_FTQ_CMD_WR_TOP_1 (1L<<10) +#define BNX2_MCP_MCPQ_FTQ_CMD_SFT_RESET (1L<<25) +#define BNX2_MCP_MCPQ_FTQ_CMD_RD_DATA (1L<<26) +#define BNX2_MCP_MCPQ_FTQ_CMD_ADD_INTERVEN (1L<<27) +#define BNX2_MCP_MCPQ_FTQ_CMD_ADD_DATA (1L<<28) +#define BNX2_MCP_MCPQ_FTQ_CMD_INTERVENE_CLR (1L<<29) +#define BNX2_MCP_MCPQ_FTQ_CMD_POP (1L<<30) +#define BNX2_MCP_MCPQ_FTQ_CMD_BUSY (1L<<31) + +#define BNX2_MCP_MCPQ_FTQ_CTL 0x001453fc +#define BNX2_MCP_MCPQ_FTQ_CTL_INTERVENE (1L<<0) +#define BNX2_MCP_MCPQ_FTQ_CTL_OVERFLOW (1L<<1) +#define BNX2_MCP_MCPQ_FTQ_CTL_FORCE_INTERVENE (1L<<2) +#define BNX2_MCP_MCPQ_FTQ_CTL_MAX_DEPTH (0x3ffL<<12) +#define BNX2_MCP_MCPQ_FTQ_CTL_CUR_DEPTH (0x3ffL<<22) + +#define BNX2_MCP_ROM 0x00150000 +#define BNX2_MCP_SCRATCH 0x00160000 + +#define BNX2_SHM_HDR_SIGNATURE BNX2_MCP_SCRATCH +#define BNX2_SHM_HDR_SIGNATURE_SIG_MASK 0xffff0000 +#define BNX2_SHM_HDR_SIGNATURE_SIG 0x53530000 +#define BNX2_SHM_HDR_SIGNATURE_VER_MASK 0x000000ff +#define BNX2_SHM_HDR_SIGNATURE_VER_ONE 0x00000001 + +#define BNX2_SHM_HDR_ADDR_0 BNX2_MCP_SCRATCH + 4 +#define BNX2_SHM_HDR_ADDR_1 BNX2_MCP_SCRATCH + 8 + + +#define NUM_MC_HASH_REGISTERS 8 + + +/* PHY_ID1: bits 31-16; PHY_ID2: bits 15-0. */ +#define PHY_BCM5706_PHY_ID 0x00206160 + +#define PHY_ID(id) ((id) & 0xfffffff0) +#define PHY_REV_ID(id) ((id) & 0xf) + +/* 5708 Serdes PHY registers */ + +#define BCM5708S_UP1 0xb + +#define BCM5708S_UP1_2G5 0x1 + +#define BCM5708S_BLK_ADDR 0x1f + +#define BCM5708S_BLK_ADDR_DIG 0x0000 +#define BCM5708S_BLK_ADDR_DIG3 0x0002 +#define BCM5708S_BLK_ADDR_TX_MISC 0x0005 + +/* Digital Block */ +#define BCM5708S_1000X_CTL1 0x10 + +#define BCM5708S_1000X_CTL1_FIBER_MODE 0x0001 +#define BCM5708S_1000X_CTL1_AUTODET_EN 0x0010 + +#define BCM5708S_1000X_CTL2 0x11 + +#define BCM5708S_1000X_CTL2_PLLEL_DET_EN 0x0001 + +#define BCM5708S_1000X_STAT1 0x14 + +#define BCM5708S_1000X_STAT1_SGMII 0x0001 +#define BCM5708S_1000X_STAT1_LINK 0x0002 +#define BCM5708S_1000X_STAT1_FD 0x0004 +#define BCM5708S_1000X_STAT1_SPEED_MASK 0x0018 +#define BCM5708S_1000X_STAT1_SPEED_10 0x0000 +#define BCM5708S_1000X_STAT1_SPEED_100 0x0008 +#define BCM5708S_1000X_STAT1_SPEED_1G 0x0010 +#define BCM5708S_1000X_STAT1_SPEED_2G5 0x0018 +#define BCM5708S_1000X_STAT1_TX_PAUSE 0x0020 +#define BCM5708S_1000X_STAT1_RX_PAUSE 0x0040 + +/* Digital3 Block */ +#define BCM5708S_DIG_3_0 0x10 + +#define BCM5708S_DIG_3_0_USE_IEEE 0x0001 + +/* Tx/Misc Block */ +#define BCM5708S_TX_ACTL1 0x15 + +#define BCM5708S_TX_ACTL1_DRIVER_VCM 0x30 + +#define BCM5708S_TX_ACTL3 0x17 + +#define MIN_ETHERNET_PACKET_SIZE 60 +#define MAX_ETHERNET_PACKET_SIZE 1514 +#define MAX_ETHERNET_JUMBO_PACKET_SIZE 9014 + +#define RX_COPY_THRESH 92 + +#define DMA_READ_CHANS 5 +#define DMA_WRITE_CHANS 3 + +#define BCM_PAGE_BITS 12 +#define BCM_PAGE_SIZE (1 << BCM_PAGE_BITS) + +#define TX_DESC_CNT (BCM_PAGE_SIZE / sizeof(struct tx_bd)) +#define MAX_TX_DESC_CNT (TX_DESC_CNT - 1) + +#define MAX_RX_RINGS 4 +#define RX_DESC_CNT (BCM_PAGE_SIZE / sizeof(struct rx_bd)) +#define MAX_RX_DESC_CNT (RX_DESC_CNT - 1) +#define MAX_TOTAL_RX_DESC_CNT (MAX_RX_DESC_CNT * MAX_RX_RINGS) + +#define NEXT_TX_BD(x) (((x) & (MAX_TX_DESC_CNT - 1)) == \ + (MAX_TX_DESC_CNT - 1)) ? \ + (x) + 2 : (x) + 1 + +#define PREV_TX_BD(x) ((((x)-1) & (MAX_TX_DESC_CNT)) == \ + (MAX_TX_DESC_CNT)) ? \ + (x) - 2 : (x) - 1 + +#define TX_RING_IDX(x) ((x) & MAX_TX_DESC_CNT) + +#define NEXT_RX_BD(x) (((x) & (MAX_RX_DESC_CNT - 1)) == \ + (MAX_RX_DESC_CNT - 1)) ? \ + (x) + 2 : (x) + 1 + +#define RX_RING_IDX(x) ((x) & bp->rx_max_ring_idx) + +//#define RX_RING(x) (((x) & ~MAX_RX_DESC_CNT) >> 8) +#define RX_IDX(x) ((x) & MAX_RX_DESC_CNT) + +/* Context size. */ +#define CTX_SHIFT 7 +#define CTX_SIZE (1 << CTX_SHIFT) +#define CTX_MASK (CTX_SIZE - 1) +#define GET_CID_ADDR(_cid) ((_cid) << CTX_SHIFT) +#define GET_CID(_cid_addr) ((_cid_addr) >> CTX_SHIFT) + +#define PHY_CTX_SHIFT 6 +#define PHY_CTX_SIZE (1 << PHY_CTX_SHIFT) +#define PHY_CTX_MASK (PHY_CTX_SIZE - 1) +#define GET_PCID_ADDR(_pcid) ((_pcid) << PHY_CTX_SHIFT) +#define GET_PCID(_pcid_addr) ((_pcid_addr) >> PHY_CTX_SHIFT) + +#define MB_KERNEL_CTX_SHIFT 8 +#define MB_KERNEL_CTX_SIZE (1 << MB_KERNEL_CTX_SHIFT) +#define MB_KERNEL_CTX_MASK (MB_KERNEL_CTX_SIZE - 1) +#define MB_GET_CID_ADDR(_cid) (0x10000 + ((_cid) << MB_KERNEL_CTX_SHIFT)) + +#define MAX_CID_CNT 0x4000 +#define MAX_CID_ADDR (GET_CID_ADDR(MAX_CID_CNT)) +#define INVALID_CID_ADDR 0xffffffff + +#define TX_CID 16 +#define RX_CID 0 + +#define MB_TX_CID_ADDR MB_GET_CID_ADDR(TX_CID) +#define MB_RX_CID_ADDR MB_GET_CID_ADDR(RX_CID) + +#if 0 +struct sw_bd { + struct sk_buff *skb; + DECLARE_PCI_UNMAP_ADDR(mapping) +}; +#endif + +/* Buffered flash (Atmel: AT45DB011B) specific information */ +#define SEEPROM_PAGE_BITS 2 +#define SEEPROM_PHY_PAGE_SIZE (1 << SEEPROM_PAGE_BITS) +#define SEEPROM_BYTE_ADDR_MASK (SEEPROM_PHY_PAGE_SIZE-1) +#define SEEPROM_PAGE_SIZE 4 +#define SEEPROM_TOTAL_SIZE 65536 + +#define BUFFERED_FLASH_PAGE_BITS 9 +#define BUFFERED_FLASH_PHY_PAGE_SIZE (1 << BUFFERED_FLASH_PAGE_BITS) +#define BUFFERED_FLASH_BYTE_ADDR_MASK (BUFFERED_FLASH_PHY_PAGE_SIZE-1) +#define BUFFERED_FLASH_PAGE_SIZE 264 +#define BUFFERED_FLASH_TOTAL_SIZE 0x21000 + +#define SAIFUN_FLASH_PAGE_BITS 8 +#define SAIFUN_FLASH_PHY_PAGE_SIZE (1 << SAIFUN_FLASH_PAGE_BITS) +#define SAIFUN_FLASH_BYTE_ADDR_MASK (SAIFUN_FLASH_PHY_PAGE_SIZE-1) +#define SAIFUN_FLASH_PAGE_SIZE 256 +#define SAIFUN_FLASH_BASE_TOTAL_SIZE 65536 + +#define ST_MICRO_FLASH_PAGE_BITS 8 +#define ST_MICRO_FLASH_PHY_PAGE_SIZE (1 << ST_MICRO_FLASH_PAGE_BITS) +#define ST_MICRO_FLASH_BYTE_ADDR_MASK (ST_MICRO_FLASH_PHY_PAGE_SIZE-1) +#define ST_MICRO_FLASH_PAGE_SIZE 256 +#define ST_MICRO_FLASH_BASE_TOTAL_SIZE 65536 + +#define NVRAM_TIMEOUT_COUNT 30000 + + +#define FLASH_STRAP_MASK (BNX2_NVM_CFG1_FLASH_MODE | \ + BNX2_NVM_CFG1_BUFFER_MODE | \ + BNX2_NVM_CFG1_PROTECT_MODE | \ + BNX2_NVM_CFG1_FLASH_SIZE) + +#define FLASH_BACKUP_STRAP_MASK (0xf << 26) + +struct flash_spec { + u32 strapping; + u32 config1; + u32 config2; + u32 config3; + u32 write1; + u32 buffered; + u32 page_bits; + u32 page_size; + u32 addr_mask; + u32 total_size; + char *name; +}; + +struct bnx2 { + /* Fields used in the tx and intr/napi performance paths are grouped */ + /* together in the beginning of the structure. */ + void /*__iomem*/ *regview; + + struct nic *nic; + struct pci_device *pdev; + + /* atomic_t intr_sem; */ + + struct status_block *status_blk; + u32 last_status_idx; + + u32 flags; +#define PCIX_FLAG 1 +#define PCI_32BIT_FLAG 2 +#define ONE_TDMA_FLAG 4 /* no longer used */ +#define NO_WOL_FLAG 8 +#define USING_DAC_FLAG 0x10 +#define USING_MSI_FLAG 0x20 +#define ASF_ENABLE_FLAG 0x40 + + /* Put tx producer and consumer fields in separate cache lines. */ + u32 tx_prod_bseq __attribute__((aligned(L1_CACHE_BYTES))); + u16 tx_prod; + + struct tx_bd *tx_desc_ring; + struct sw_bd *tx_buf_ring; + int tx_ring_size; + + u16 tx_cons __attribute__((aligned(L1_CACHE_BYTES))); + u16 hw_tx_cons; + +#ifdef BCM_VLAN + struct vlan_group *vlgrp; +#endif + + u32 rx_offset; + u32 rx_buf_use_size; /* useable size */ + u32 rx_buf_size; /* with alignment */ + u32 rx_max_ring_idx; + + u32 rx_prod_bseq; + u16 rx_prod; + u16 rx_cons; + u16 hw_rx_cons; + + u32 rx_csum; + +#if 0 + struct rx_bd *rx_desc_ring[MAX_RX_RINGS]; +#endif + struct rx_bd *rx_desc_ring; + + /* End of fields used in the performance code paths. */ + + char *name; + +#if 0 + int timer_interval; + int current_interval; + struct timer_list timer; + struct work_struct reset_task; + int in_reset_task; + + /* Used to synchronize phy accesses. */ + spinlock_t phy_lock; +#endif + + u32 phy_flags; +#define PHY_SERDES_FLAG 1 +#define PHY_CRC_FIX_FLAG 2 +#define PHY_PARALLEL_DETECT_FLAG 4 +#define PHY_2_5G_CAPABLE_FLAG 8 +#define PHY_INT_MODE_MASK_FLAG 0x300 +#define PHY_INT_MODE_AUTO_POLLING_FLAG 0x100 +#define PHY_INT_MODE_LINK_READY_FLAG 0x200 + + u32 chip_id; + /* chip num:16-31, rev:12-15, metal:4-11, bond_id:0-3 */ +#define CHIP_NUM(bp) (((bp)->chip_id) & 0xffff0000) +#define CHIP_NUM_5706 0x57060000 +#define CHIP_NUM_5708 0x57080000 + +#define CHIP_REV(bp) (((bp)->chip_id) & 0x0000f000) +#define CHIP_REV_Ax 0x00000000 +#define CHIP_REV_Bx 0x00001000 +#define CHIP_REV_Cx 0x00002000 + +#define CHIP_METAL(bp) (((bp)->chip_id) & 0x00000ff0) +#define CHIP_BONDING(bp) (((bp)->chip_id) & 0x0000000f) + +#define CHIP_ID(bp) (((bp)->chip_id) & 0xfffffff0) +#define CHIP_ID_5706_A0 0x57060000 +#define CHIP_ID_5706_A1 0x57060010 +#define CHIP_ID_5706_A2 0x57060020 +#define CHIP_ID_5708_A0 0x57080000 +#define CHIP_ID_5708_B0 0x57081000 +#define CHIP_ID_5708_B1 0x57081010 + +#define CHIP_BOND_ID(bp) (((bp)->chip_id) & 0xf) + +/* A serdes chip will have the first bit of the bond id set. */ +#define CHIP_BOND_ID_SERDES_BIT 0x01 + + u32 phy_addr; + u32 phy_id; + + u16 bus_speed_mhz; + u8 wol; + + u8 pad; + + u16 fw_wr_seq; + u16 fw_drv_pulse_wr_seq; + + dma_addr_t tx_desc_mapping; + + + int rx_max_ring; + int rx_ring_size; +#if 0 + dma_addr_t rx_desc_mapping[MAX_RX_RINGS]; +#endif + dma_addr_t rx_desc_mapping; + + u16 tx_quick_cons_trip; + u16 tx_quick_cons_trip_int; + u16 rx_quick_cons_trip; + u16 rx_quick_cons_trip_int; + u16 comp_prod_trip; + u16 comp_prod_trip_int; + u16 tx_ticks; + u16 tx_ticks_int; + u16 com_ticks; + u16 com_ticks_int; + u16 cmd_ticks; + u16 cmd_ticks_int; + u16 rx_ticks; + u16 rx_ticks_int; + + u32 stats_ticks; + + dma_addr_t status_blk_mapping; + + struct statistics_block *stats_blk; + dma_addr_t stats_blk_mapping; + + u32 hc_cmd; + u32 rx_mode; + + u16 req_line_speed; + u8 req_duplex; + + u8 link_up; + + u16 line_speed; + u8 duplex; + u8 flow_ctrl; /* actual flow ctrl settings */ + /* may be different from */ + /* req_flow_ctrl if autoneg */ +#define FLOW_CTRL_TX 1 +#define FLOW_CTRL_RX 2 + + u32 advertising; + + u8 req_flow_ctrl; /* flow ctrl advertisement */ + /* settings or forced */ + /* settings */ + u8 autoneg; +#define AUTONEG_SPEED 1 +#define AUTONEG_FLOW_CTRL 2 + + u8 loopback; +#define MAC_LOOPBACK 1 +#define PHY_LOOPBACK 2 + + u8 serdes_an_pending; +#define SERDES_AN_TIMEOUT (HZ / 3) + + u8 mac_addr[8]; + + u32 shmem_base; + + u32 fw_ver; + + int pm_cap; + int pcix_cap; + + /* struct net_device_stats net_stats; */ + + struct flash_spec *flash_info; + u32 flash_size; + + int status_stats_size; +}; + +static u32 bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset); +static void bnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val); + +#define REG_RD(bp, offset) \ + readl(bp->regview + offset) + +#define REG_WR(bp, offset, val) \ + writel(val, bp->regview + offset) + +#define REG_WR16(bp, offset, val) \ + writew(val, bp->regview + offset) + +#define REG_RD_IND(bp, offset) \ + bnx2_reg_rd_ind(bp, offset) + +#define REG_WR_IND(bp, offset, val) \ + bnx2_reg_wr_ind(bp, offset, val) + +/* Indirect context access. Unlike the MBQ_WR, these macros will not + * trigger a chip event. */ +static void bnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val); + +#define CTX_WR(bp, cid_addr, offset, val) \ + bnx2_ctx_wr(bp, cid_addr, offset, val) + +struct cpu_reg { + u32 mode; + u32 mode_value_halt; + u32 mode_value_sstep; + + u32 state; + u32 state_value_clear; + + u32 gpr0; + u32 evmask; + u32 pc; + u32 inst; + u32 bp; + + u32 spad_base; + + u32 mips_view_base; +}; + +struct fw_info { + u32 ver_major; + u32 ver_minor; + u32 ver_fix; + + u32 start_addr; + + /* Text section. */ + u32 text_addr; + u32 text_len; + u32 text_index; + u32 *text; + + /* Data section. */ + u32 data_addr; + u32 data_len; + u32 data_index; + u32 *data; + + /* SBSS section. */ + u32 sbss_addr; + u32 sbss_len; + u32 sbss_index; + u32 *sbss; + + /* BSS section. */ + u32 bss_addr; + u32 bss_len; + u32 bss_index; + u32 *bss; + + /* Read-only section. */ + u32 rodata_addr; + u32 rodata_len; + u32 rodata_index; + u32 *rodata; +}; + +#define RV2P_PROC1 0 +#define RV2P_PROC2 1 + + +/* This value (in milliseconds) determines the frequency of the driver + * issuing the PULSE message code. The firmware monitors this periodic + * pulse to determine when to switch to an OS-absent mode. */ +#define DRV_PULSE_PERIOD_MS 250 + +/* This value (in milliseconds) determines how long the driver should + * wait for an acknowledgement from the firmware before timing out. Once + * the firmware has timed out, the driver will assume there is no firmware + * running and there won't be any firmware-driver synchronization during a + * driver reset. */ +#define FW_ACK_TIME_OUT_MS 100 + + +#define BNX2_DRV_RESET_SIGNATURE 0x00000000 +#define BNX2_DRV_RESET_SIGNATURE_MAGIC 0x4841564b /* HAVK */ +//#define DRV_RESET_SIGNATURE_MAGIC 0x47495352 /* RSIG */ + +#define BNX2_DRV_MB 0x00000004 +#define BNX2_DRV_MSG_CODE 0xff000000 +#define BNX2_DRV_MSG_CODE_RESET 0x01000000 +#define BNX2_DRV_MSG_CODE_UNLOAD 0x02000000 +#define BNX2_DRV_MSG_CODE_SHUTDOWN 0x03000000 +#define BNX2_DRV_MSG_CODE_SUSPEND_WOL 0x04000000 +#define BNX2_DRV_MSG_CODE_FW_TIMEOUT 0x05000000 +#define BNX2_DRV_MSG_CODE_PULSE 0x06000000 +#define BNX2_DRV_MSG_CODE_DIAG 0x07000000 +#define BNX2_DRV_MSG_CODE_SUSPEND_NO_WOL 0x09000000 + +#define BNX2_DRV_MSG_DATA 0x00ff0000 +#define BNX2_DRV_MSG_DATA_WAIT0 0x00010000 +#define BNX2_DRV_MSG_DATA_WAIT1 0x00020000 +#define BNX2_DRV_MSG_DATA_WAIT2 0x00030000 +#define BNX2_DRV_MSG_DATA_WAIT3 0x00040000 + +#define BNX2_DRV_MSG_SEQ 0x0000ffff + +#define BNX2_FW_MB 0x00000008 +#define BNX2_FW_MSG_ACK 0x0000ffff +#define BNX2_FW_MSG_STATUS_MASK 0x00ff0000 +#define BNX2_FW_MSG_STATUS_OK 0x00000000 +#define BNX2_FW_MSG_STATUS_FAILURE 0x00ff0000 + +#define BNX2_LINK_STATUS 0x0000000c +#define BNX2_LINK_STATUS_INIT_VALUE 0xffffffff +#define BNX2_LINK_STATUS_LINK_UP 0x1 +#define BNX2_LINK_STATUS_LINK_DOWN 0x0 +#define BNX2_LINK_STATUS_SPEED_MASK 0x1e +#define BNX2_LINK_STATUS_AN_INCOMPLETE (0<<1) +#define BNX2_LINK_STATUS_10HALF (1<<1) +#define BNX2_LINK_STATUS_10FULL (2<<1) +#define BNX2_LINK_STATUS_100HALF (3<<1) +#define BNX2_LINK_STATUS_100BASE_T4 (4<<1) +#define BNX2_LINK_STATUS_100FULL (5<<1) +#define BNX2_LINK_STATUS_1000HALF (6<<1) +#define BNX2_LINK_STATUS_1000FULL (7<<1) +#define BNX2_LINK_STATUS_2500HALF (8<<1) +#define BNX2_LINK_STATUS_2500FULL (9<<1) +#define BNX2_LINK_STATUS_AN_ENABLED (1<<5) +#define BNX2_LINK_STATUS_AN_COMPLETE (1<<6) +#define BNX2_LINK_STATUS_PARALLEL_DET (1<<7) +#define BNX2_LINK_STATUS_RESERVED (1<<8) +#define BNX2_LINK_STATUS_PARTNER_AD_1000FULL (1<<9) +#define BNX2_LINK_STATUS_PARTNER_AD_1000HALF (1<<10) +#define BNX2_LINK_STATUS_PARTNER_AD_100BT4 (1<<11) +#define BNX2_LINK_STATUS_PARTNER_AD_100FULL (1<<12) +#define BNX2_LINK_STATUS_PARTNER_AD_100HALF (1<<13) +#define BNX2_LINK_STATUS_PARTNER_AD_10FULL (1<<14) +#define BNX2_LINK_STATUS_PARTNER_AD_10HALF (1<<15) +#define BNX2_LINK_STATUS_TX_FC_ENABLED (1<<16) +#define BNX2_LINK_STATUS_RX_FC_ENABLED (1<<17) +#define BNX2_LINK_STATUS_PARTNER_SYM_PAUSE_CAP (1<<18) +#define BNX2_LINK_STATUS_PARTNER_ASYM_PAUSE_CAP (1<<19) +#define BNX2_LINK_STATUS_SERDES_LINK (1<<20) +#define BNX2_LINK_STATUS_PARTNER_AD_2500FULL (1<<21) +#define BNX2_LINK_STATUS_PARTNER_AD_2500HALF (1<<22) + +#define BNX2_DRV_PULSE_MB 0x00000010 +#define BNX2_DRV_PULSE_SEQ_MASK 0x00007fff + +/* Indicate to the firmware not to go into the + * OS absent when it is not getting driver pulse. + * This is used for debugging. */ +#define BNX2_DRV_MSG_DATA_PULSE_CODE_ALWAYS_ALIVE 0x00080000 + +#define BNX2_DEV_INFO_SIGNATURE 0x00000020 +#define BNX2_DEV_INFO_SIGNATURE_MAGIC 0x44564900 +#define BNX2_DEV_INFO_SIGNATURE_MAGIC_MASK 0xffffff00 +#define BNX2_DEV_INFO_FEATURE_CFG_VALID 0x01 +#define BNX2_DEV_INFO_SECONDARY_PORT 0x80 +#define BNX2_DEV_INFO_DRV_ALWAYS_ALIVE 0x40 + +#define BNX2_SHARED_HW_CFG_PART_NUM 0x00000024 + +#define BNX2_SHARED_HW_CFG_POWER_DISSIPATED 0x00000034 +#define BNX2_SHARED_HW_CFG_POWER_STATE_D3_MASK 0xff000000 +#define BNX2_SHARED_HW_CFG_POWER_STATE_D2_MASK 0xff0000 +#define BNX2_SHARED_HW_CFG_POWER_STATE_D1_MASK 0xff00 +#define BNX2_SHARED_HW_CFG_POWER_STATE_D0_MASK 0xff + +#define BNX2_SHARED_HW_CFG POWER_CONSUMED 0x00000038 +#define BNX2_SHARED_HW_CFG_CONFIG 0x0000003c +#define BNX2_SHARED_HW_CFG_DESIGN_NIC 0 +#define BNX2_SHARED_HW_CFG_DESIGN_LOM 0x1 +#define BNX2_SHARED_HW_CFG_PHY_COPPER 0 +#define BNX2_SHARED_HW_CFG_PHY_FIBER 0x2 +#define BNX2_SHARED_HW_CFG_PHY_2_5G 0x20 +#define BNX2_SHARED_HW_CFG_PHY_BACKPLANE 0x40 +#define BNX2_SHARED_HW_CFG_LED_MODE_SHIFT_BITS 8 +#define BNX2_SHARED_HW_CFG_LED_MODE_MASK 0x300 +#define BNX2_SHARED_HW_CFG_LED_MODE_MAC 0 +#define BNX2_SHARED_HW_CFG_LED_MODE_GPHY1 0x100 +#define BNX2_SHARED_HW_CFG_LED_MODE_GPHY2 0x200 + +#define BNX2_SHARED_HW_CFG_CONFIG2 0x00000040 +#define BNX2_SHARED_HW_CFG2_NVM_SIZE_MASK 0x00fff000 + +#define BNX2_DEV_INFO_BC_REV 0x0000004c + +#define BNX2_PORT_HW_CFG_MAC_UPPER 0x00000050 +#define BNX2_PORT_HW_CFG_UPPERMAC_MASK 0xffff + +#define BNX2_PORT_HW_CFG_MAC_LOWER 0x00000054 +#define BNX2_PORT_HW_CFG_CONFIG 0x00000058 +#define BNX2_PORT_HW_CFG_CFG_TXCTL3_MASK 0x0000ffff +#define BNX2_PORT_HW_CFG_CFG_DFLT_LINK_MASK 0x001f0000 +#define BNX2_PORT_HW_CFG_CFG_DFLT_LINK_AN 0x00000000 +#define BNX2_PORT_HW_CFG_CFG_DFLT_LINK_1G 0x00030000 +#define BNX2_PORT_HW_CFG_CFG_DFLT_LINK_2_5G 0x00040000 + +#define BNX2_PORT_HW_CFG_IMD_MAC_A_UPPER 0x00000068 +#define BNX2_PORT_HW_CFG_IMD_MAC_A_LOWER 0x0000006c +#define BNX2_PORT_HW_CFG_IMD_MAC_B_UPPER 0x00000070 +#define BNX2_PORT_HW_CFG_IMD_MAC_B_LOWER 0x00000074 +#define BNX2_PORT_HW_CFG_ISCSI_MAC_UPPER 0x00000078 +#define BNX2_PORT_HW_CFG_ISCSI_MAC_LOWER 0x0000007c + +#define BNX2_DEV_INFO_PER_PORT_HW_CONFIG2 0x000000b4 + +#define BNX2_DEV_INFO_FORMAT_REV 0x000000c4 +#define BNX2_DEV_INFO_FORMAT_REV_MASK 0xff000000 +#define BNX2_DEV_INFO_FORMAT_REV_ID ('A' << 24) + +#define BNX2_SHARED_FEATURE 0x000000c8 +#define BNX2_SHARED_FEATURE_MASK 0xffffffff + +#define BNX2_PORT_FEATURE 0x000000d8 +#define BNX2_PORT2_FEATURE 0x00000014c +#define BNX2_PORT_FEATURE_WOL_ENABLED 0x01000000 +#define BNX2_PORT_FEATURE_MBA_ENABLED 0x02000000 +#define BNX2_PORT_FEATURE_ASF_ENABLED 0x04000000 +#define BNX2_PORT_FEATURE_IMD_ENABLED 0x08000000 +#define BNX2_PORT_FEATURE_BAR1_SIZE_MASK 0xf +#define BNX2_PORT_FEATURE_BAR1_SIZE_DISABLED 0x0 +#define BNX2_PORT_FEATURE_BAR1_SIZE_64K 0x1 +#define BNX2_PORT_FEATURE_BAR1_SIZE_128K 0x2 +#define BNX2_PORT_FEATURE_BAR1_SIZE_256K 0x3 +#define BNX2_PORT_FEATURE_BAR1_SIZE_512K 0x4 +#define BNX2_PORT_FEATURE_BAR1_SIZE_1M 0x5 +#define BNX2_PORT_FEATURE_BAR1_SIZE_2M 0x6 +#define BNX2_PORT_FEATURE_BAR1_SIZE_4M 0x7 +#define BNX2_PORT_FEATURE_BAR1_SIZE_8M 0x8 +#define BNX2_PORT_FEATURE_BAR1_SIZE_16M 0x9 +#define BNX2_PORT_FEATURE_BAR1_SIZE_32M 0xa +#define BNX2_PORT_FEATURE_BAR1_SIZE_64M 0xb +#define BNX2_PORT_FEATURE_BAR1_SIZE_128M 0xc +#define BNX2_PORT_FEATURE_BAR1_SIZE_256M 0xd +#define BNX2_PORT_FEATURE_BAR1_SIZE_512M 0xe +#define BNX2_PORT_FEATURE_BAR1_SIZE_1G 0xf + +#define BNX2_PORT_FEATURE_WOL 0xdc +#define BNX2_PORT2_FEATURE_WOL 0x150 +#define BNX2_PORT_FEATURE_WOL_DEFAULT_SHIFT_BITS 4 +#define BNX2_PORT_FEATURE_WOL_DEFAULT_MASK 0x30 +#define BNX2_PORT_FEATURE_WOL_DEFAULT_DISABLE 0 +#define BNX2_PORT_FEATURE_WOL_DEFAULT_MAGIC 0x10 +#define BNX2_PORT_FEATURE_WOL_DEFAULT_ACPI 0x20 +#define BNX2_PORT_FEATURE_WOL_DEFAULT_MAGIC_AND_ACPI 0x30 +#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_MASK 0xf +#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_AUTONEG 0 +#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_10HALF 1 +#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_10FULL 2 +#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_100HALF 3 +#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_100FULL 4 +#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_1000HALF 5 +#define BNX2_PORT_FEATURE_WOL_LINK_SPEED_1000FULL 6 +#define BNX2_PORT_FEATURE_WOL_AUTONEG_ADVERTISE_1000 0x40 +#define BNX2_PORT_FEATURE_WOL_RESERVED_PAUSE_CAP 0x400 +#define BNX2_PORT_FEATURE_WOL_RESERVED_ASYM_PAUSE_CAP 0x800 + +#define BNX2_PORT_FEATURE_MBA 0xe0 +#define BNX2_PORT2_FEATURE_MBA 0x154 +#define BNX2_PORT_FEATURE_MBA_BOOT_AGENT_TYPE_SHIFT_BITS 0 +#define BNX2_PORT_FEATURE_MBA_BOOT_AGENT_TYPE_MASK 0x3 +#define BNX2_PORT_FEATURE_MBA_BOOT_AGENT_TYPE_PXE 0 +#define BNX2_PORT_FEATURE_MBA_BOOT_AGENT_TYPE_RPL 1 +#define BNX2_PORT_FEATURE_MBA_BOOT_AGENT_TYPE_BOOTP 2 +#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_SHIFT_BITS 2 +#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_MASK 0x3c +#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_AUTONEG 0 +#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_10HALF 0x4 +#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_10FULL 0x8 +#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_100HALF 0xc +#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_100FULL 0x10 +#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_1000HALF 0x14 +#define BNX2_PORT_FEATURE_MBA_LINK_SPEED_1000FULL 0x18 +#define BNX2_PORT_FEATURE_MBA_SETUP_PROMPT_ENABLE 0x40 +#define BNX2_PORT_FEATURE_MBA_HOTKEY_CTRL_S 0 +#define BNX2_PORT_FEATURE_MBA_HOTKEY_CTRL_B 0x80 +#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_SHIFT_BITS 8 +#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_MASK 0xff00 +#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_DISABLED 0 +#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_1K 0x100 +#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_2K 0x200 +#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_4K 0x300 +#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_8K 0x400 +#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_16K 0x500 +#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_32K 0x600 +#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_64K 0x700 +#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_128K 0x800 +#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_256K 0x900 +#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_512K 0xa00 +#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_1M 0xb00 +#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_2M 0xc00 +#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_4M 0xd00 +#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_8M 0xe00 +#define BNX2_PORT_FEATURE_MBA_EXP_ROM_SIZE_16M 0xf00 +#define BNX2_PORT_FEATURE_MBA_MSG_TIMEOUT_SHIFT_BITS 16 +#define BNX2_PORT_FEATURE_MBA_MSG_TIMEOUT_MASK 0xf0000 +#define BNX2_PORT_FEATURE_MBA_BIOS_BOOTSTRAP_SHIFT_BITS 20 +#define BNX2_PORT_FEATURE_MBA_BIOS_BOOTSTRAP_MASK 0x300000 +#define BNX2_PORT_FEATURE_MBA_BIOS_BOOTSTRAP_AUTO 0 +#define BNX2_PORT_FEATURE_MBA_BIOS_BOOTSTRAP_BBS 0x100000 +#define BNX2_PORT_FEATURE_MBA_BIOS_BOOTSTRAP_INT18H 0x200000 +#define BNX2_PORT_FEATURE_MBA_BIOS_BOOTSTRAP_INT19H 0x300000 + +#define BNX2_PORT_FEATURE_IMD 0xe4 +#define BNX2_PORT2_FEATURE_IMD 0x158 +#define BNX2_PORT_FEATURE_IMD_LINK_OVERRIDE_DEFAULT 0 +#define BNX2_PORT_FEATURE_IMD_LINK_OVERRIDE_ENABLE 1 + +#define BNX2_PORT_FEATURE_VLAN 0xe8 +#define BNX2_PORT2_FEATURE_VLAN 0x15c +#define BNX2_PORT_FEATURE_MBA_VLAN_TAG_MASK 0xffff +#define BNX2_PORT_FEATURE_MBA_VLAN_ENABLE 0x10000 + +#define BNX2_BC_STATE_RESET_TYPE 0x000001c0 +#define BNX2_BC_STATE_RESET_TYPE_SIG 0x00005254 +#define BNX2_BC_STATE_RESET_TYPE_SIG_MASK 0x0000ffff +#define BNX2_BC_STATE_RESET_TYPE_NONE (BNX2_BC_STATE_RESET_TYPE_SIG | \ + 0x00010000) +#define BNX2_BC_STATE_RESET_TYPE_PCI (BNX2_BC_STATE_RESET_TYPE_SIG | \ + 0x00020000) +#define BNX2_BC_STATE_RESET_TYPE_VAUX (BNX2_BC_STATE_RESET_TYPE_SIG | \ + 0x00030000) +#define BNX2_BC_STATE_RESET_TYPE_DRV_MASK DRV_MSG_CODE +#define BNX2_BC_STATE_RESET_TYPE_DRV_RESET (BNX2_BC_STATE_RESET_TYPE_SIG | \ + DRV_MSG_CODE_RESET) +#define BNX2_BC_STATE_RESET_TYPE_DRV_UNLOAD (BNX2_BC_STATE_RESET_TYPE_SIG | \ + DRV_MSG_CODE_UNLOAD) +#define BNX2_BC_STATE_RESET_TYPE_DRV_SHUTDOWN (BNX2_BC_STATE_RESET_TYPE_SIG | \ + DRV_MSG_CODE_SHUTDOWN) +#define BNX2_BC_STATE_RESET_TYPE_DRV_WOL (BNX2_BC_STATE_RESET_TYPE_SIG | \ + DRV_MSG_CODE_WOL) +#define BNX2_BC_STATE_RESET_TYPE_DRV_DIAG (BNX2_BC_STATE_RESET_TYPE_SIG | \ + DRV_MSG_CODE_DIAG) +#define BNX2_BC_STATE_RESET_TYPE_VALUE(msg) (BNX2_BC_STATE_RESET_TYPE_SIG | \ + (msg)) + +#define BNX2_BC_STATE 0x000001c4 +#define BNX2_BC_STATE_ERR_MASK 0x0000ff00 +#define BNX2_BC_STATE_SIGN 0x42530000 +#define BNX2_BC_STATE_SIGN_MASK 0xffff0000 +#define BNX2_BC_STATE_BC1_START (BNX2_BC_STATE_SIGN | 0x1) +#define BNX2_BC_STATE_GET_NVM_CFG1 (BNX2_BC_STATE_SIGN | 0x2) +#define BNX2_BC_STATE_PROG_BAR (BNX2_BC_STATE_SIGN | 0x3) +#define BNX2_BC_STATE_INIT_VID (BNX2_BC_STATE_SIGN | 0x4) +#define BNX2_BC_STATE_GET_NVM_CFG2 (BNX2_BC_STATE_SIGN | 0x5) +#define BNX2_BC_STATE_APPLY_WKARND (BNX2_BC_STATE_SIGN | 0x6) +#define BNX2_BC_STATE_LOAD_BC2 (BNX2_BC_STATE_SIGN | 0x7) +#define BNX2_BC_STATE_GOING_BC2 (BNX2_BC_STATE_SIGN | 0x8) +#define BNX2_BC_STATE_GOING_DIAG (BNX2_BC_STATE_SIGN | 0x9) +#define BNX2_BC_STATE_RT_FINAL_INIT (BNX2_BC_STATE_SIGN | 0x81) +#define BNX2_BC_STATE_RT_WKARND (BNX2_BC_STATE_SIGN | 0x82) +#define BNX2_BC_STATE_RT_DRV_PULSE (BNX2_BC_STATE_SIGN | 0x83) +#define BNX2_BC_STATE_RT_FIOEVTS (BNX2_BC_STATE_SIGN | 0x84) +#define BNX2_BC_STATE_RT_DRV_CMD (BNX2_BC_STATE_SIGN | 0x85) +#define BNX2_BC_STATE_RT_LOW_POWER (BNX2_BC_STATE_SIGN | 0x86) +#define BNX2_BC_STATE_RT_SET_WOL (BNX2_BC_STATE_SIGN | 0x87) +#define BNX2_BC_STATE_RT_OTHER_FW (BNX2_BC_STATE_SIGN | 0x88) +#define BNX2_BC_STATE_RT_GOING_D3 (BNX2_BC_STATE_SIGN | 0x89) +#define BNX2_BC_STATE_ERR_BAD_VERSION (BNX2_BC_STATE_SIGN | 0x0100) +#define BNX2_BC_STATE_ERR_BAD_BC2_CRC (BNX2_BC_STATE_SIGN | 0x0200) +#define BNX2_BC_STATE_ERR_BC1_LOOP (BNX2_BC_STATE_SIGN | 0x0300) +#define BNX2_BC_STATE_ERR_UNKNOWN_CMD (BNX2_BC_STATE_SIGN | 0x0400) +#define BNX2_BC_STATE_ERR_DRV_DEAD (BNX2_BC_STATE_SIGN | 0x0500) +#define BNX2_BC_STATE_ERR_NO_RXP (BNX2_BC_STATE_SIGN | 0x0600) +#define BNX2_BC_STATE_ERR_TOO_MANY_RBUF (BNX2_BC_STATE_SIGN | 0x0700) + +#define BNX2_BC_STATE_DEBUG_CMD 0x1dc +#define BNX2_BC_STATE_BC_DBG_CMD_SIGNATURE 0x42440000 +#define BNX2_BC_STATE_BC_DBG_CMD_SIGNATURE_MASK 0xffff0000 +#define BNX2_BC_STATE_BC_DBG_CMD_LOOP_CNT_MASK 0xffff +#define BNX2_BC_STATE_BC_DBG_CMD_LOOP_INFINITE 0xffff + +#define HOST_VIEW_SHMEM_BASE 0x167c00 + +/* Enable or disable autonegotiation. If this is set to enable, + * the forced link modes above are completely ignored. + */ +#define AUTONEG_DISABLE 0x00 +#define AUTONEG_ENABLE 0x01 + +#define RX_OFFSET (sizeof(struct l2_fhdr) + 2) + +#define RX_BUF_CNT 20 + +/* 8 for CRC and VLAN */ +#define RX_BUF_USE_SIZE (ETH_MAX_MTU + ETH_HLEN + RX_OFFSET + 8) + +/* 8 for alignment */ +//#define RX_BUF_SIZE (RX_BUF_USE_SIZE + 8) +#define RX_BUF_SIZE (L1_CACHE_ALIGN(RX_BUF_USE_SIZE + 8)) + + +#endif diff --git a/gpxe/src/drivers/net/bnx2_fw.h b/gpxe/src/drivers/net/bnx2_fw.h new file mode 100644 index 0000000..8158974 --- /dev/null +++ b/gpxe/src/drivers/net/bnx2_fw.h @@ -0,0 +1,3494 @@ +/* bnx2_fw.h: Broadcom NX2 network driver. + * + * Copyright (c) 2004, 2005, 2006 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, except as noted below. + * + * This file contains firmware data derived from proprietary unpublished + * source code, Copyright (c) 2004, 2005 Broadcom Corporation. + * + * Permission is hereby granted for the distribution of this firmware data + * in hexadecimal or equivalent format, provided this copyright notice is + * accompanying it. + */ + +static const int bnx2_COM_b06FwReleaseMajor = 0x1; +static const int bnx2_COM_b06FwReleaseMinor = 0x0; +static const int bnx2_COM_b06FwReleaseFix = 0x0; +static const u32 bnx2_COM_b06FwStartAddr = 0x080008b4; +static const u32 bnx2_COM_b06FwTextAddr = 0x08000000; +static const int bnx2_COM_b06FwTextLen = 0x57bc; +static const u32 bnx2_COM_b06FwDataAddr = 0x08005840; +static const int bnx2_COM_b06FwDataLen = 0x0; +static const u32 bnx2_COM_b06FwRodataAddr = 0x080057c0; +static const int bnx2_COM_b06FwRodataLen = 0x58; +static const u32 bnx2_COM_b06FwBssAddr = 0x08005860; +static const int bnx2_COM_b06FwBssLen = 0x88; +static const u32 bnx2_COM_b06FwSbssAddr = 0x08005840; +static const int bnx2_COM_b06FwSbssLen = 0x1c; +static u32 bnx2_COM_b06FwText[(0x57bc/4) + 1] = { + 0x0a00022d, 0x00000000, 0x00000000, 0x0000000d, 0x636f6d20, 0x322e352e, + 0x38000000, 0x02050802, 0x00000000, 0x00000003, 0x00000014, 0x00000032, + 0x00000003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000010, 0x000003e8, 0x0000ea60, 0x00000001, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x0000ffff, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000002, 0x00000020, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x10000003, 0x00000000, 0x0000000d, 0x0000000d, 0x3c020800, 0x24425840, + 0x3c030800, 0x246358e8, 0xac400000, 0x0043202b, 0x1480fffd, 0x24420004, + 0x3c1d0800, 0x37bd7ffc, 0x03a0f021, 0x3c100800, 0x261008b4, 0x3c1c0800, + 0x279c5840, 0x0e0002f7, 0x00000000, 0x0000000d, 0x27bdffe8, 0x3c1a8000, + 0x3c020008, 0x0342d825, 0x3c036010, 0xafbf0010, 0x8c655000, 0x3c020800, + 0x24470f30, 0x3c040800, 0x24865860, 0x2402ff7f, 0x00a22824, 0x34a5380c, + 0xac655000, 0x00002821, 0x24020037, 0x24030c80, 0xaf420008, 0xaf430024, + 0xacc70000, 0x24a50001, 0x2ca20016, 0x1440fffc, 0x24c60004, 0x24845860, + 0x3c020800, 0x24420f3c, 0x3c030800, 0x24630e2c, 0xac820004, 0x3c020800, + 0x24420a2c, 0x3c050800, 0x24a51268, 0xac82000c, 0x3c020800, 0x244243dc, + 0xac830008, 0x3c030800, 0x24633698, 0xac820014, 0x3c020800, 0x24423c24, + 0xac830018, 0xac83001c, 0x3c030800, 0x24630f44, 0xac820024, 0x3c020800, + 0x244243ac, 0xac83002c, 0x3c030800, 0x246343cc, 0xac820030, 0x3c020800, + 0x244242f0, 0xac830034, 0x3c030800, 0x24633d78, 0xac82003c, 0x3c020800, + 0x24420fd4, 0xac850010, 0xac850020, 0xac830040, 0x0e0010b7, 0xac820050, + 0x8fbf0010, 0x03e00008, 0x27bd0018, 0x27bdffe0, 0xafb00010, 0x27500100, + 0xafbf0018, 0xafb10014, 0x9203000b, 0x24020003, 0x1462005b, 0x96110008, + 0x32220001, 0x10400009, 0x27430080, 0x8e020000, 0x96040014, 0x000211c2, + 0x00021040, 0x00621821, 0xa4640000, 0x0a0002d0, 0x3c020800, 0x3c020800, + 0x8c430020, 0x1060002a, 0x3c030800, 0x0e00148e, 0x00000000, 0x97420108, + 0x8f850018, 0x9743010c, 0x3042003e, 0x00021400, 0x00621825, 0xaca30000, + 0x8f840018, 0x8f420100, 0xac820004, 0x97430116, 0x9742010e, 0x8f840018, + 0x00031c00, 0x00431025, 0xac820008, 0x97430110, 0x97440112, 0x8f850018, + 0x00031c00, 0x00832025, 0xaca4000c, 0x97420114, 0x8f840018, 0x3042ffff, + 0xac820010, 0x8f830018, 0xac600014, 0x8f820018, 0x3c030800, 0xac400018, + 0x946258ce, 0x8f840018, 0x3c032000, 0x00431025, 0xac82001c, 0x0e0014cc, + 0x24040001, 0x3c030800, 0x8c620040, 0x24420001, 0xac620040, 0x3c020800, + 0x8c430044, 0x32240004, 0x24630001, 0x10800017, 0xac430044, 0x8f4202b8, + 0x04430007, 0x8e020020, 0x3c040800, 0x8c830060, 0x24020001, 0x24630001, + 0x0a0002f2, 0xac830060, 0x3c060800, 0x8cc4005c, 0xaf420280, 0x96030016, + 0x00001021, 0xa7430284, 0x8e050004, 0x24840001, 0x3c031000, 0xaf450288, + 0xaf4302b8, 0x0a0002f2, 0xacc4005c, 0x32220002, 0x0a0002f2, 0x0002102b, + 0x3c026000, 0xac400808, 0x0000000d, 0x00001021, 0x8fbf0018, 0x8fb10014, + 0x8fb00010, 0x03e00008, 0x27bd0020, 0x27bdffc8, 0xafbf0034, 0xafbe0030, + 0xafb7002c, 0xafb60028, 0xafb50024, 0xafb40020, 0xafb3001c, 0xafb20018, + 0xafb10014, 0x0e000244, 0xafb00010, 0x3c170800, 0x3c160800, 0x24110020, + 0x24150030, 0x2794000c, 0x27930008, 0x3c124000, 0x3c1e0800, 0x8f820004, + 0x3c040800, 0x8c830020, 0x10430005, 0x8ee200a4, 0xaf830004, 0x0e001593, + 0x00000000, 0x8ee200a4, 0x8ec300a0, 0x10430004, 0x26c400a0, 0x94820002, + 0xa742009e, 0xaee300a4, 0x8f500000, 0x32020007, 0x1040ffee, 0x32020001, + 0x1040002c, 0x32020002, 0x8f420100, 0xaf420020, 0x8f430104, 0xaf4300a8, + 0x9342010b, 0x93630000, 0x306300ff, 0x10710005, 0x304400ff, 0x10750006, + 0x2c820016, 0x0a000333, 0x00000000, 0xaf940000, 0x0a000334, 0x2c820016, + 0xaf930000, 0x0a000334, 0x00000000, 0xaf800000, 0x14400005, 0x00041880, + 0x0e0003cc, 0x00000000, 0x0a000340, 0x00000000, 0x3c020800, 0x24425860, + 0x00621821, 0x8c620000, 0x0040f809, 0x00000000, 0x10400005, 0x3c030800, + 0x8f420104, 0x3c016020, 0xac220014, 0x3c030800, 0x8c620034, 0xaf520138, + 0x24420001, 0xac620034, 0x32020002, 0x1040001a, 0x32020004, 0x8f420140, + 0xaf420020, 0x93630000, 0x306300ff, 0x10710005, 0x00000000, 0x10750006, + 0x00000000, 0x0a00035d, 0x00000000, 0xaf940000, 0x0a00035e, 0x00000000, + 0xaf930000, 0x0a00035e, 0x00000000, 0xaf800000, 0x0e000c7b, 0x00000000, + 0x3c040800, 0x8c820038, 0xaf520178, 0x24420001, 0xac820038, 0x32020004, + 0x1040ffa4, 0x00000000, 0x8f420180, 0xaf420020, 0x93630000, 0x306300ff, + 0x10710005, 0x00000000, 0x10750006, 0x00000000, 0x0a000378, 0x00000000, + 0xaf940000, 0x0a000379, 0x00000000, 0xaf930000, 0x0a000379, 0x00000000, + 0xaf800000, 0x8f430180, 0x24020f00, 0x14620005, 0x00000000, 0x8f420188, + 0xa742009c, 0x0a000387, 0x8fc2003c, 0x93620000, 0x14510004, 0x8fc2003c, + 0x0e000bad, 0x00000000, 0x8fc2003c, 0xaf5201b8, 0x24420001, 0x0a00030b, + 0xafc2003c, 0x27bdffe8, 0xafbf0010, 0x97420108, 0x24033000, 0x30447000, + 0x10830016, 0x28823001, 0x10400007, 0x24024000, 0x1080000b, 0x24022000, + 0x1082000c, 0x00000000, 0x0a0003b3, 0x00000000, 0x10820010, 0x24025000, + 0x10820012, 0x00000000, 0x0a0003b3, 0x00000000, 0x0000000d, 0x0a0003b5, + 0x00001021, 0x0e000442, 0x00000000, 0x0a0003b6, 0x8fbf0010, 0x0e00041a, + 0x00000000, 0x0a0003b5, 0x00001021, 0x0e000669, 0x00000000, 0x0a0003b5, + 0x00001021, 0x0e001467, 0x00000000, 0x0a0003b5, 0x00001021, 0x0000000d, + 0x00001021, 0x8fbf0010, 0x03e00008, 0x27bd0018, 0x93620000, 0x24030020, + 0x304400ff, 0x10830005, 0x24020030, 0x10820007, 0x00000000, 0x0a0003c9, + 0x00000000, 0x2782000c, 0xaf820000, 0x03e00008, 0x00000000, 0x27820008, + 0xaf820000, 0x03e00008, 0x00000000, 0xaf800000, 0x03e00008, 0x00000000, + 0x0000000d, 0x03e00008, 0x00001021, 0x03e00008, 0x00001021, 0x27440100, + 0x94830008, 0x30620004, 0x10400017, 0x30620002, 0x8f4202b8, 0x04430007, + 0x8c820020, 0x3c040800, 0x8c830060, 0x24020001, 0x24630001, 0x03e00008, + 0xac830060, 0xaf420280, 0x94830016, 0x3c060800, 0xa7430284, 0x8c850004, + 0x8cc4005c, 0x00001021, 0x3c031000, 0x24840001, 0xaf450288, 0xaf4302b8, + 0x03e00008, 0xacc4005c, 0x14400003, 0x3c040800, 0x03e00008, 0x00001021, + 0x8c830084, 0x24020001, 0x24630001, 0x03e00008, 0xac830084, 0x27450100, + 0x3c040800, 0x8c820088, 0x94a3000c, 0x24420001, 0x007a1821, 0xac820088, + 0x8ca40018, 0x90664000, 0xaf440038, 0x8ca2001c, 0x2403fff8, 0x00063600, + 0x00431024, 0x34420004, 0x3c030005, 0xaf42003c, 0xaf430030, 0x00000000, + 0x00000000, 0x00000000, 0xaf460404, 0x00000000, 0x00000000, 0x00000000, + 0x3c020006, 0x34420001, 0xaf420030, 0x00000000, 0x00000000, 0x00000000, + 0x8f420000, 0x30420010, 0x1040fffd, 0x00001021, 0x03e00008, 0x00000000, + 0x3c020800, 0x8c430020, 0x27bdffe8, 0xafb00010, 0x27500100, 0x1060001e, + 0xafbf0014, 0x0e00148e, 0x00000000, 0x8f830018, 0x8e020018, 0xac620000, + 0x8f840018, 0x9602000c, 0xac820004, 0x8f830018, 0xac600008, 0x8f820018, + 0xac40000c, 0x8f830018, 0xac600010, 0x8f820018, 0xac400014, 0x8f840018, + 0x3c026000, 0x8c434448, 0xac830018, 0x96020008, 0x3c030800, 0x946458ce, + 0x8f850018, 0x00021400, 0x00441025, 0x24040001, 0x0e0014cc, 0xaca2001c, + 0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, 0x27bdffe8, 0xafb00010, + 0x27500100, 0xafbf0014, 0x92020009, 0x14400003, 0x3c020800, 0x0a00046c, + 0x24020001, 0x8c430020, 0x1060001f, 0x00001021, 0x0e00148e, 0x00000000, + 0x8f830018, 0x8e020018, 0xac620000, 0x8f840018, 0x9602000c, 0xac820004, + 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010, + 0x8f820018, 0xac400014, 0x8f840018, 0x3c026000, 0x8c434448, 0xac830018, + 0x96020008, 0x3c030800, 0x946458ce, 0x8f850018, 0x00021400, 0x00441025, + 0x24040001, 0x0e0014cc, 0xaca2001c, 0x00001021, 0x8fbf0014, 0x8fb00010, + 0x03e00008, 0x27bd0018, 0x3c0b0800, 0x8d6808b0, 0x3c070800, 0x24e700b0, + 0x00084900, 0x01271821, 0xac640000, 0x93620005, 0x97660008, 0x00e95021, + 0x93630023, 0x9364003f, 0x25080001, 0x00021600, 0x00063400, 0x00461025, + 0x00031a00, 0x00431025, 0x00822025, 0xad440004, 0x9362007e, 0x9366007f, + 0x8f630178, 0x9364007a, 0x00021600, 0x00063400, 0x00461025, 0x00031a00, + 0x00431025, 0x00822025, 0xad440008, 0x93620080, 0x9363007d, 0x3108007f, + 0x01403821, 0xad6808b0, 0x00021600, 0x00031c00, 0x00431025, 0x00451025, + 0x03e00008, 0xace2000c, 0x27bdffb8, 0xafb3002c, 0x00009821, 0xafbe0040, + 0x0000f021, 0xafb50034, 0x27550100, 0xafbf0044, 0xafb7003c, 0xafb60038, + 0xafb40030, 0xafb20028, 0xafb10024, 0xafb00020, 0xafa00010, 0xafa00014, + 0x96a20008, 0x8f540100, 0x8eb10018, 0x30420001, 0x10400037, 0x02a0b821, + 0x8f630054, 0x2622ffff, 0x00431023, 0x18400006, 0x00000000, 0x0000000d, + 0x00000000, 0x2400015c, 0x0a0004e5, 0x00002021, 0x8f62004c, 0x02221023, + 0x18400028, 0x00002021, 0x93650120, 0x93640121, 0x3c030800, 0x8c62008c, + 0x308400ff, 0x24420001, 0x30a500ff, 0x00803821, 0x1485000b, 0xac62008c, + 0x3c040800, 0x8c830090, 0x24630001, 0xac830090, 0x93620122, 0x30420001, + 0x00021023, 0x30420005, 0x0a0004e5, 0x34440004, 0x27660100, 0x00041080, + 0x00c21021, 0x8c430000, 0x02231823, 0x04600004, 0x24820001, 0x30440007, + 0x1485fff9, 0x00041080, 0x10870007, 0x3c030800, 0xa3640121, 0x8c620094, + 0x24040005, 0x24420001, 0x0a0004e5, 0xac620094, 0x24040004, 0x00809821, + 0x9362003f, 0x304400ff, 0x38830016, 0x2c630001, 0x38820010, 0x2c420001, + 0x00621825, 0x1460000c, 0x24020001, 0x38830008, 0x2c630001, 0x38820014, + 0x2c420001, 0x00621825, 0x14600005, 0x24020001, 0x24020012, 0x14820002, + 0x00001021, 0x24020001, 0x10400009, 0x00000000, 0x8ea20020, 0x8f630040, + 0x0040b021, 0x00431023, 0x5c400010, 0x8f760040, 0x0a000511, 0x00000000, + 0x9343010b, 0x24020004, 0x1462000a, 0x8eb60020, 0x8f630040, 0x3c021000, + 0x00761823, 0x0043102a, 0x10400004, 0x00000000, 0x0000000d, 0x00000000, + 0x240002fa, 0x9343010b, 0x24020004, 0x5462000b, 0x96a20008, 0x24020001, + 0xafa20010, 0x96a20008, 0x24030001, 0xafa30018, 0x8eb2001c, 0x36730002, + 0x30420020, 0x0a000526, 0xafa20014, 0x36730080, 0x30420002, 0x10400003, + 0xafa00018, 0x0a000526, 0x8eb2001c, 0x8eb20014, 0x2402fffb, 0x02628024, + 0x1200002a, 0x3c030800, 0x8c620030, 0x02021024, 0x10400026, 0x3c020800, + 0x8c430020, 0x10600024, 0x32620004, 0x0e00148e, 0x00000000, 0x8f830018, + 0x8f420100, 0xac620000, 0x8f840018, 0x02401821, 0x32620002, 0xac900004, + 0x8f840018, 0x54400001, 0x02c01821, 0xac830008, 0x8f830018, 0x8ee20020, + 0xac62000c, 0x8f840018, 0x8f620040, 0xac820010, 0x8f830018, 0x8ee20018, + 0xac620014, 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001, 0x3c020800, + 0xaca30018, 0x944358ce, 0x8f850018, 0x3c024010, 0x00621825, 0x0e0014cc, + 0xaca3001c, 0x32620004, 0x10400063, 0x00003821, 0x3c029000, 0x34420001, + 0x3c038000, 0x02821025, 0xa360007c, 0xaf420020, 0x8f420020, 0x00431024, + 0x1440fffd, 0x00000000, 0x93620023, 0x30420080, 0x10400011, 0x00000000, + 0x8f65005c, 0x8f63004c, 0x9764003c, 0x8f620064, 0x00a32823, 0x00852821, + 0x00a2102b, 0x54400006, 0x3c023fff, 0x93620023, 0x3042007f, 0xa3620023, + 0xaf710064, 0x3c023fff, 0x0a000580, 0x3442ffff, 0x8f62005c, 0x02221023, + 0x04400011, 0x00000000, 0x8f65005c, 0x8f630064, 0x9764003c, 0x3c023fff, + 0x3442ffff, 0xaf710064, 0x00a32823, 0x00852821, 0x0045102b, 0x10400004, + 0x02251021, 0x3c053fff, 0x34a5ffff, 0x02251021, 0xaf62005c, 0x24070001, + 0xaf71004c, 0x8f620054, 0x16220005, 0x00000000, 0x93620023, 0x30420040, + 0x10400017, 0x24020001, 0x9762006a, 0x00022880, 0x50a00001, 0x24050001, + 0x97630068, 0x93640081, 0x3c020800, 0x8c46004c, 0x00652821, 0x00852804, + 0x00c5102b, 0x54400001, 0x00a03021, 0x3c020800, 0x8c440050, 0x00c4182b, + 0x54600001, 0x00c02021, 0x8f420074, 0x2403fffe, 0x00832824, 0x00a21021, + 0xaf62000c, 0x93620082, 0x30420080, 0x50400001, 0xa3600081, 0x3c028000, + 0x34420001, 0x02821025, 0xaf420020, 0x9363007e, 0x9362007a, 0x10620004, + 0x00000000, 0x0e0013c4, 0x00000000, 0x00403821, 0x54e00001, 0x241e0001, + 0x8f700040, 0x8f620040, 0x14520003, 0x00521023, 0x0a0005bf, 0x00001021, + 0x28420001, 0x10400041, 0x8fa20010, 0x0e000fae, 0x02402021, 0xaf720040, + 0x9362003e, 0x30420001, 0x1440000b, 0x3c029000, 0x93620022, 0x24420001, + 0xa3620022, 0x93630022, 0x3c020800, 0x8c440098, 0x0064182b, 0x14600027, + 0x3c020800, 0x3c029000, 0x34420001, 0x02821025, 0xaf420020, 0x3c038000, + 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x9362007d, 0x3c038000, + 0x34420001, 0xa362007d, 0x8f640074, 0x34630001, 0x02831825, 0xaf430020, + 0x04810006, 0x3c038000, 0x02802021, 0x0e000470, 0x24050273, 0x0a0005f2, + 0x24050001, 0x8f4201f8, 0x00431024, 0x1440fffd, 0x24020002, 0x3c031000, + 0xaf5401c0, 0xa34201c4, 0xaf4301f8, 0x24050001, 0x24020001, 0xa7620012, + 0xa3600022, 0x0a0005fe, 0x2ca20001, 0x9743007a, 0x9444002a, 0x00002821, + 0x00641821, 0x3063fffe, 0xa7630012, 0x2ca20001, 0x00021023, 0x03c2f024, + 0x8fa20010, 0x10400004, 0x8fa30014, 0x0e0013c1, 0x00000000, 0x8fa30014, + 0x10600003, 0x00000000, 0x0e0010eb, 0x00000000, 0x13c0001f, 0x3c029000, + 0x34420001, 0x02821025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024, + 0x1440fffd, 0x00000000, 0x9362007d, 0x3c038000, 0xa362007d, 0x8f640074, + 0x34630001, 0x02831825, 0xaf430020, 0x04810006, 0x3c038000, 0x02802021, + 0x0e000470, 0x2405036c, 0x0a00062b, 0x8fa20018, 0x8f4201f8, 0x00431024, + 0x1440fffd, 0x24020002, 0x3c031000, 0xaf5401c0, 0xa34201c4, 0xaf4301f8, + 0x8fa20018, 0x5040002f, 0x96a20008, 0x8f620048, 0x8f630024, 0x00761821, + 0xaf630048, 0x9764003c, 0x00501023, 0x0044102b, 0x10400025, 0x3c029000, + 0x34420001, 0x3c040800, 0x8c830080, 0x8f450100, 0x3c068000, 0x24630001, + 0x00a21025, 0xac830080, 0xaf420020, 0x8f420020, 0x00461024, 0x1440fffd, + 0x00000000, 0x9362007d, 0x3c038000, 0x34420004, 0xa362007d, 0x8f640074, + 0x34630001, 0x00a31825, 0xaf430020, 0x04810006, 0x3c038000, 0x00a02021, + 0x0e000470, 0x2405038a, 0x0a00065b, 0x96a20008, 0x8f4201f8, 0x00431024, + 0x1440fffd, 0x24020002, 0x3c031000, 0xaf4501c0, 0xa34201c4, 0xaf4301f8, + 0x96a20008, 0x8fbf0044, 0x8fbe0040, 0x8fb7003c, 0x8fb60038, 0x8fb50034, + 0x8fb40030, 0x8fb3002c, 0x8fb20028, 0x8fb10024, 0x8fb00020, 0x00021042, + 0x30420001, 0x03e00008, 0x27bd0048, 0x27bdffe0, 0xafbf0018, 0x97420108, + 0x24030019, 0x304400ff, 0x10830065, 0x2882001a, 0x1040001a, 0x2882000a, + 0x1040000f, 0x28820008, 0x10400040, 0x24020001, 0x1082003a, 0x28820002, + 0x50400005, 0x24020006, 0x10800032, 0x3c026000, 0x0a0006fb, 0x00000000, + 0x1082003d, 0x00000000, 0x0a0006fb, 0x00000000, 0x2402000b, 0x10820044, + 0x2882000b, 0x1440004b, 0x2402000e, 0x10820045, 0x00000000, 0x0a0006fb, + 0x00000000, 0x24020020, 0x10820062, 0x28820021, 0x1040000e, 0x2402001c, + 0x1082004c, 0x2882001d, 0x10400005, 0x2402001b, 0x10820043, 0x00000000, + 0x0a0006fb, 0x00000000, 0x2402001f, 0x10820050, 0x00000000, 0x0a0006fb, + 0x00000000, 0x240200c1, 0x10820042, 0x288200c2, 0x10400005, 0x24020080, + 0x10820021, 0x00000000, 0x0a0006fb, 0x00000000, 0x240200c2, 0x1082003d, + 0x240200c9, 0x50820049, 0xafa00010, 0x0a0006fb, 0x00000000, 0x0e001163, + 0xac400808, 0x0a0006fd, 0x8fbf0018, 0x3c026000, 0x8c444448, 0x3c030800, + 0xac640064, 0x0e001163, 0x00000000, 0x3c026000, 0x8c444448, 0x3c030800, + 0x0a0006fc, 0xac640068, 0x8f440100, 0x0e0006ff, 0x00000000, 0x3c026000, + 0x8c444448, 0x3c030800, 0x0a0006fc, 0xac64006c, 0x0e001191, 0x00000000, + 0x0a0006fd, 0x8fbf0018, 0x8f440100, 0x0e0011bb, 0x00000000, 0x0a0006fd, + 0x8fbf0018, 0x0e001202, 0x00000000, 0x0a0006fd, 0x8fbf0018, 0x0000000d, + 0x0a0006fd, 0x8fbf0018, 0x0e000826, 0x00000000, 0x0a0006fd, 0x8fbf0018, + 0x8f440100, 0x0e001264, 0x00000000, 0x0a0006fd, 0x8fbf0018, 0x0e00134e, + 0x00000000, 0x0a0006fd, 0x8fbf0018, 0x0e00087c, 0x27440100, 0x0a0006fd, + 0x8fbf0018, 0x8f640040, 0x0e000fae, 0x00000000, 0x0a0006fd, 0x8fbf0018, + 0x8f440100, 0x0e001059, 0x00000000, 0x0a0006fd, 0x8fbf0018, 0x0e001417, + 0x00000000, 0x0a0006fd, 0x8fbf0018, 0xafa00014, 0x8f440100, 0x8f450118, + 0x8f46011c, 0x0e001439, 0x8f470120, 0x0a0006fd, 0x8fbf0018, 0x0000000d, + 0x8fbf0018, 0x03e00008, 0x27bd0020, 0x27bdffe8, 0xafbf0010, 0x9742010c, + 0x1440005e, 0x00803821, 0x3c029000, 0x34420001, 0x00e21025, 0xaf420020, + 0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x93620023, + 0x30420010, 0x14400026, 0x3c030800, 0x8f630074, 0x3c027fff, 0x3442ffff, + 0x00621824, 0xaf630074, 0x93620005, 0x34420001, 0xa3620005, 0x8f63004c, + 0x8f620054, 0x10620021, 0x24040001, 0x9762006a, 0x00022880, 0x50a00001, + 0x24050001, 0x97630068, 0x93640081, 0x3c020800, 0x8c46004c, 0x00652821, + 0x00852804, 0x00c5102b, 0x54400001, 0x00a03021, 0x3c020800, 0x8c440050, + 0x00c4182b, 0x54600001, 0x00c02021, 0x8f420074, 0x2403fffe, 0x00832824, + 0x00a21021, 0xaf62000c, 0x0a00073d, 0x24040001, 0x8c6200a8, 0x00002021, + 0x24420001, 0xac6200a8, 0x0000000d, 0x00000000, 0x2400044d, 0x3c028000, + 0x34420001, 0x00e21025, 0xaf420020, 0x1080001f, 0x3c029000, 0x34420001, + 0x00e21025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd, + 0x00000000, 0x9362007d, 0x3c038000, 0xa362007d, 0x8f640074, 0x34630001, + 0x00e31825, 0xaf430020, 0x04810006, 0x3c038000, 0x00e02021, 0x0e000470, + 0x24050455, 0x0a000761, 0x00000000, 0x8f4201f8, 0x00431024, 0x1440fffd, + 0x24020002, 0x3c031000, 0xaf4701c0, 0xa34201c4, 0xaf4301f8, 0x0e001163, + 0x00000000, 0x8fbf0010, 0x03e00008, 0x27bd0018, 0x27bdffd8, 0xafbf0024, + 0xafb40020, 0xafb3001c, 0xafb20018, 0xafb10014, 0xafb00010, 0x93630005, + 0x00809821, 0x24020030, 0x30630030, 0x146200ac, 0x00a0a021, 0x3c020800, + 0x8c430020, 0x106000a6, 0x00000000, 0x0e00148e, 0x00000000, 0x8f830018, + 0xac730000, 0x936200c4, 0x30420002, 0x10400004, 0x24020001, 0x8f830018, + 0x0a000784, 0x00000000, 0x8f830018, 0x24020003, 0xac620004, 0x8f6200dc, + 0x8f630040, 0x00431023, 0x18400004, 0x00000000, 0x0000000d, 0x00000000, + 0x24000509, 0x8f840018, 0x8f6200dc, 0xac820008, 0x8f830018, 0xac60000c, + 0x8f820018, 0xac400010, 0x8f830018, 0x8f62004c, 0x3c100800, 0xac620014, + 0x8f850018, 0x3c026000, 0x8c434448, 0x261258c0, 0x00002021, 0xaca30018, + 0x9642000e, 0x8f850018, 0x3c034010, 0x00431025, 0x0e0014cc, 0xaca2001c, + 0x8f830018, 0xac730000, 0x9362003e, 0x9363003f, 0x8f840018, 0x00021200, + 0x00621825, 0xac830004, 0x93620081, 0x93630082, 0x8f840018, 0x00021600, + 0x00031c00, 0x00431025, 0xac820008, 0x8f830018, 0x8f620040, 0xac62000c, + 0x8f840018, 0x8f620048, 0xac820010, 0x8f71004c, 0x8f820018, 0xac510014, + 0x8f620050, 0x8f850018, 0x00401821, 0x02221023, 0x5c400001, 0x02201821, + 0x00002021, 0xaca30018, 0x9642000e, 0x8f850018, 0x3c03c00b, 0x00431025, + 0x0e0014cc, 0xaca2001c, 0x8f620054, 0x8f840018, 0x00401821, 0x02221023, + 0x5c400001, 0x02201821, 0xac830000, 0x8f840018, 0x8f630058, 0xac830004, + 0x93620023, 0x30420010, 0x10400004, 0x00000000, 0x8f830018, 0x0a0007dd, + 0x8f620148, 0x8f830018, 0x8f62005c, 0xac620008, 0x8f830018, 0x8f620060, + 0xac62000c, 0x8f840018, 0x8f620064, 0xac820010, 0x97630068, 0x9762006a, + 0x8f840018, 0x00031c00, 0x00431025, 0xac820014, 0x8f850018, 0x00002021, + 0x2402ffff, 0x260358c0, 0xaca20018, 0x9462000e, 0x8f850018, 0x3c03c00c, + 0x00431025, 0x0e0014cc, 0xaca2001c, 0x8f840018, 0x8f630018, 0xac830000, + 0x936200c4, 0x30420002, 0x10400006, 0x00000000, 0x976200c8, 0x8f830018, + 0x3042ffff, 0x0a000803, 0xac620004, 0x8f820018, 0xac400004, 0x8f830018, + 0x8f62006c, 0xac620008, 0x8f840018, 0x8f6200dc, 0xac82000c, 0x8f830018, + 0xac600010, 0x93620005, 0x8f830018, 0x00021600, 0x00541025, 0xac620014, + 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001, 0x260258c0, 0xaca30018, + 0x9443000e, 0x8f850018, 0x3c02400d, 0x00621825, 0x0e0014cc, 0xaca3001c, + 0x0e00122e, 0x02602021, 0x8fbf0024, 0x8fb40020, 0x8fb3001c, 0x8fb20018, + 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0028, 0x27bdffe0, 0xafb00010, + 0x27500100, 0xafbf0018, 0xafb10014, 0x9603000c, 0x240200c1, 0x54620024, + 0x8e040000, 0x3c029000, 0x8f450100, 0x34420001, 0x3c038000, 0x00a21025, + 0xaf420020, 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x9362007d, + 0x3c038000, 0x34420004, 0xa362007d, 0x8f640074, 0x34630001, 0x00a31825, + 0xaf430020, 0x04810006, 0x3c038000, 0x00a02021, 0x0e000470, 0x240505b2, + 0x0a000878, 0x8fbf0018, 0x8f4201f8, 0x00431024, 0x1440fffd, 0x24020002, + 0x3c031000, 0xaf4501c0, 0xa34201c4, 0xaf4301f8, 0x0a000878, 0x8fbf0018, + 0x8f65004c, 0x24060001, 0x0e0012a3, 0x240705be, 0x3c020800, 0x8c430020, + 0x9611000c, 0x1060001d, 0x8e100000, 0x0e00148e, 0x00000000, 0x8f820018, + 0xac500000, 0x8f840018, 0x00111400, 0xac820004, 0x8f830018, 0xac600008, + 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010, 0x8f840018, 0x240205c1, + 0xac820014, 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001, 0x3c020800, + 0xaca30018, 0x944358ce, 0x8f850018, 0x3c024019, 0x00621825, 0x0e0014cc, + 0xaca3001c, 0x8fbf0018, 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020, + 0x27bdffb0, 0xafb5003c, 0x0000a821, 0xafbe0048, 0x0000f021, 0xafb70044, + 0x0000b821, 0xafb30034, 0x00009821, 0xafb60040, 0x0080b021, 0xafbf004c, + 0xafb40038, 0xafb20030, 0xafb1002c, 0xafb00028, 0xafa00010, 0x8f620040, + 0x8ec30014, 0x96d1000c, 0x00431023, 0x04410025, 0x8ed40000, 0x32220401, + 0x1040030c, 0x3c029000, 0x34420001, 0x02821025, 0xaf420020, 0x3c038000, + 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x9362007d, 0x3c038000, + 0x34420004, 0xa362007d, 0x8f640074, 0x34630001, 0x02831825, 0xaf430020, + 0x04810006, 0x3c038000, 0x02802021, 0x0e000470, 0x24050664, 0x0a000ba2, + 0x8fbf004c, 0x8f4201f8, 0x00431024, 0x1440fffd, 0x24020002, 0x3c031000, + 0xaf5401c0, 0xa34201c4, 0xaf4301f8, 0x0a000ba2, 0x8fbf004c, 0x32220010, + 0x1040006b, 0x00003021, 0x9362003f, 0x92c6000f, 0x304500ff, 0x24c3fff8, + 0x2c62000f, 0x10400057, 0x3c020800, 0x244257c0, 0x00031880, 0x00621821, + 0x8c640000, 0x00800008, 0x00000000, 0x38a20012, 0x0a000924, 0x0002a82b, + 0x2402000e, 0x14a20004, 0x2402000c, 0x24150001, 0x0a000924, 0x24060010, + 0x10a20049, 0x38a30010, 0x2c630001, 0x38a20016, 0x2c420001, 0x00621825, + 0x1460004d, 0x0000a821, 0x24020014, 0x10a2004a, 0x00000000, 0x0000000d, + 0x00000000, 0x2400069c, 0x0a000924, 0x0000a821, 0x24020016, 0x14a20005, + 0x2402000c, 0x24150001, 0x24060010, 0x0a000924, 0x3231fffd, 0x10a20032, + 0x38a30010, 0x2c630001, 0x38a2000e, 0x2c420001, 0x00621825, 0x14600036, + 0x0000a821, 0x24020014, 0x14a20003, 0x24150001, 0x0a000924, 0x24060012, + 0x0000000d, 0x00000000, 0x240006bc, 0x0a000924, 0x0000a821, 0x2402000e, + 0x14a20004, 0x24020016, 0x24150001, 0x0a000924, 0x3231fffb, 0x14a20004, + 0x24020014, 0x24150001, 0x0a000924, 0x3231fffd, 0x54a20013, 0x92c2000e, + 0x24150001, 0x24060012, 0x0a000924, 0x3231fffd, 0x2402000c, 0x54a2000c, + 0x92c2000e, 0x92c3000e, 0x2402000a, 0x10620005, 0x24150001, 0x0000000d, + 0x00000000, 0x240006e8, 0x24150001, 0x0a000924, 0x24060014, 0x92c2000e, + 0x14a20003, 0x00000000, 0x0a000924, 0x24150001, 0x10a6ffc1, 0x24020012, + 0x10a20005, 0x0000a821, 0x0000000d, 0x00000000, 0x24000704, 0x0000a821, + 0x12a00022, 0x32220004, 0x10400002, 0x24020001, 0xafa20010, 0x32230102, + 0x24020002, 0x1462000f, 0x00000000, 0x92c2000a, 0x30420020, 0x1440000b, + 0x00000000, 0x8f630048, 0x8f620040, 0x14620004, 0x00000000, 0x8f620048, + 0x24420001, 0xaf620048, 0x8f620040, 0x24420001, 0xaf620040, 0xa366003f, + 0x38c30012, 0x2c630001, 0x38c20010, 0x2c420001, 0x00621825, 0x10600005, + 0x3c030800, 0x8c620074, 0x24420001, 0x0e00140d, 0xac620074, 0x32220040, + 0x32230020, 0xafa30020, 0x32230080, 0xafa30024, 0x32230001, 0xafa30018, + 0x32230008, 0xafa3001c, 0x32230100, 0x104000c4, 0xafa30014, 0x8ec60010, + 0x8f630054, 0x24c2ffff, 0x00431023, 0x18400006, 0x00000000, 0x0000000d, + 0x00000000, 0x2400015c, 0x0a000989, 0x00009021, 0x8f62004c, 0x00c21023, + 0x18400028, 0x00009021, 0x93650120, 0x93640121, 0x3c030800, 0x8c62008c, + 0x308400ff, 0x24420001, 0x30a500ff, 0x00804021, 0x1485000b, 0xac62008c, + 0x3c040800, 0x8c830090, 0x24630001, 0xac830090, 0x93620122, 0x30420001, + 0x00021023, 0x30420005, 0x0a000989, 0x34520004, 0x27670100, 0x00041080, + 0x00e21021, 0x8c430000, 0x00c31823, 0x04600004, 0x24820001, 0x30440007, + 0x1485fff9, 0x00041080, 0x10880007, 0x3c030800, 0xa3640121, 0x8c620094, + 0x24120005, 0x24420001, 0x0a000989, 0xac620094, 0x24120004, 0x32420001, + 0x10400021, 0x3c020800, 0x8c430020, 0x8ed00000, 0x1060001c, 0x8ed30010, + 0x0e00148e, 0x00000000, 0x8f820018, 0xac500000, 0x8f840018, 0x24020001, + 0xac820004, 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, + 0xac600010, 0x8f820018, 0xac530014, 0x8f850018, 0x3c026000, 0x8c434448, + 0x24040001, 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018, 0x3c024010, + 0x00621825, 0x0e0014cc, 0xaca3001c, 0x24130001, 0x32420004, 0x10400068, + 0x00003821, 0x3c029000, 0x8ec60010, 0x34420001, 0x3c038000, 0x02821025, + 0xa360007c, 0xaf420020, 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, + 0x93620023, 0x30420080, 0x10400011, 0x00000000, 0x8f65005c, 0x8f63004c, + 0x9764003c, 0x8f620064, 0x00a32823, 0x00852821, 0x00a2102b, 0x54400006, + 0x3c023fff, 0x93620023, 0x3042007f, 0xa3620023, 0xaf660064, 0x3c023fff, + 0x0a0009da, 0x3442ffff, 0x8f62005c, 0x00c21023, 0x04400011, 0x00000000, + 0x8f65005c, 0x8f630064, 0x9764003c, 0x3c023fff, 0x3442ffff, 0xaf660064, + 0x00a32823, 0x00852821, 0x0045102b, 0x10400004, 0x00c51021, 0x3c053fff, + 0x34a5ffff, 0x00c51021, 0xaf62005c, 0x24070001, 0xaf66004c, 0x8fa20010, + 0x10400003, 0x00000000, 0xaf660050, 0xaf660054, 0x8f620054, 0x14c20005, + 0x00000000, 0x93620023, 0x30420040, 0x10400017, 0x24020001, 0x9762006a, + 0x00022880, 0x50a00001, 0x24050001, 0x97630068, 0x93640081, 0x3c020800, + 0x8c46004c, 0x00652821, 0x00852804, 0x00c5102b, 0x54400001, 0x00a03021, + 0x3c020800, 0x8c440050, 0x00c4182b, 0x54600001, 0x00c02021, 0x8f420074, + 0x2403fffe, 0x00832824, 0x00a21021, 0xaf62000c, 0x93620082, 0x30420080, + 0x50400001, 0xa3600081, 0x3c028000, 0x34420001, 0x02821025, 0xaf420020, + 0x9363007e, 0x9362007a, 0x10620005, 0x00e0b821, 0x0e0013c4, 0x00000000, + 0x00403821, 0x00e0b821, 0x8fa30020, 0x10600009, 0x8fa20010, 0x8ec20018, + 0xaf620018, 0x8ec3001c, 0xaf63001c, 0x8ec20020, 0x24170001, 0xaf620058, + 0x8fa20010, 0x10400057, 0x8fa30024, 0x93620023, 0x30420040, 0x10400053, + 0x00000000, 0x16600021, 0x3c120800, 0x8e420020, 0x8f70004c, 0x1040001e, + 0x24130001, 0x0e00148e, 0x00000000, 0x8f820018, 0xac540000, 0x8f840018, + 0x24020001, 0xac820004, 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, + 0x8f830018, 0xac600010, 0x8f820018, 0xac500014, 0x8f850018, 0x3c026000, + 0x8c434448, 0x24040001, 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018, + 0x3c024010, 0x00621825, 0xaca3001c, 0x0e0014cc, 0x24130001, 0x8e420020, + 0x1040001c, 0x8ed00000, 0x0e00148e, 0x00000000, 0x8f820018, 0xac500000, + 0x8f830018, 0xac600004, 0x8f820018, 0xac400008, 0x8f830018, 0xac60000c, + 0x8f820018, 0xac400010, 0x8f830018, 0x24020798, 0xac620014, 0x8f850018, + 0x3c026000, 0x8c434448, 0x24040001, 0x3c020800, 0xaca30018, 0x944358ce, + 0x8f850018, 0x3c024019, 0x00621825, 0x0e0014cc, 0xaca3001c, 0x3c029000, + 0x34420001, 0x02821025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024, + 0x1440fffd, 0x24020001, 0xaf62000c, 0x93630023, 0x3c028000, 0x34420001, + 0x02821025, 0x306300bf, 0xa3630023, 0xaf420020, 0x8fa30024, 0x10600012, + 0x8fa30018, 0x9362007c, 0x24420001, 0xa362007c, 0x9363007e, 0x9362007a, + 0x1462000b, 0x8fa30018, 0x9362007c, 0x3c030800, 0x8c640024, 0x0044102b, + 0x14400005, 0x8fa30018, 0x0e0013c4, 0x00000000, 0x02e2b825, 0x8fa30018, + 0x3062ffff, 0x10400003, 0x32220200, 0x0a000a94, 0x241e0004, 0x10400003, + 0x00000000, 0x241e0040, 0x24170001, 0x12a000d0, 0x32220002, 0x104000cf, + 0x8fa2001c, 0x92c2000a, 0x30420002, 0x5040003b, 0x92c2000a, 0x93620023, + 0x30420008, 0x54400037, 0x92c2000a, 0x3c020800, 0x8c430020, 0x10600023, + 0x3c029000, 0x0e00148e, 0x00000000, 0x8f840018, 0x8ec30000, 0xac830000, + 0x92c2000a, 0x8f830018, 0x00021600, 0xac620004, 0x8f840018, 0x8f620040, + 0xac820008, 0x8f850018, 0x8f63004c, 0xaca3000c, 0x9362003f, 0x8f840018, + 0x304200ff, 0xac820010, 0x8f830018, 0x3c026000, 0xac600014, 0x8f850018, + 0x8c434448, 0x24040001, 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018, + 0x3c02401a, 0x00621825, 0x0e0014cc, 0xaca3001c, 0x3c029000, 0x34420001, + 0x02821025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd, + 0x00000000, 0x93630023, 0x3c028000, 0x34420001, 0x02821025, 0x34630008, + 0xa3630023, 0xaf420020, 0x92c2000a, 0x30420020, 0x1040008e, 0x8fa2001c, + 0x93620023, 0x30420001, 0x14400035, 0x3c020800, 0x8c430020, 0x10600023, + 0x3c029000, 0x0e00148e, 0x00000000, 0x8f840018, 0x8ec30000, 0xac830000, + 0x92c2000a, 0x8f830018, 0x00021600, 0xac620004, 0x8f840018, 0x8f620040, + 0xac820008, 0x8f850018, 0x8f63004c, 0xaca3000c, 0x9362003f, 0x8f840018, + 0x304200ff, 0xac820010, 0x8f830018, 0x3c026000, 0xac600014, 0x8f850018, + 0x8c434448, 0x24040001, 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018, + 0x3c02401a, 0x00621825, 0x0e0014cc, 0xaca3001c, 0x3c029000, 0x34420001, + 0x02821025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd, + 0x00000000, 0x93630023, 0x3c028000, 0x34420001, 0x02821025, 0x34630001, + 0xa3630023, 0xaf420020, 0x93620023, 0x30420040, 0x10400052, 0x8fa2001c, + 0x16600020, 0x3c120800, 0x8e420020, 0x8f70004c, 0x1040003c, 0x3c029000, + 0x0e00148e, 0x00000000, 0x8f820018, 0xac540000, 0x8f840018, 0x24020001, + 0xac820004, 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, + 0xac600010, 0x8f820018, 0xac500014, 0x8f850018, 0x3c026000, 0x8c434448, + 0x24040001, 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018, 0x3c024010, + 0x00621825, 0x0e0014cc, 0xaca3001c, 0x8e420020, 0x1040001e, 0x3c029000, + 0x0e00148e, 0x00000000, 0x8f820018, 0xac540000, 0x8f840018, 0x3c02008d, + 0xac820004, 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, + 0xac600010, 0x8f840018, 0x240207ee, 0xac820014, 0x8f850018, 0x3c026000, + 0x8c434448, 0x24040001, 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018, + 0x3c024019, 0x00621825, 0x0e0014cc, 0xaca3001c, 0x3c029000, 0x34420001, + 0x02821025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd, + 0x00000000, 0x93630023, 0x3c028000, 0x34420001, 0x02821025, 0x306300bf, + 0xa3630023, 0xaf420020, 0x8fa2001c, 0x1040000e, 0x8fa20014, 0x92c2000a, + 0xa3620082, 0x57c00005, 0x37de0008, 0x8fa30014, 0x10600004, 0x00000000, + 0x37de0008, 0x0a000b75, 0x24170001, 0x0e0012cf, 0x02802021, 0x8fa20014, + 0x10400003, 0x00000000, 0x37de0010, 0x24170001, 0x12e00020, 0x3c029000, + 0x34420001, 0x02821025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024, + 0x1440fffd, 0x00000000, 0x9362007d, 0x3c038000, 0x03c21025, 0xa362007d, + 0x8f640074, 0x34630001, 0x02831825, 0xaf430020, 0x04810006, 0x3c038000, + 0x02802021, 0x0e000470, 0x2405082a, 0x0a000b9b, 0x00000000, 0x8f4201f8, + 0x00431024, 0x1440fffd, 0x24020002, 0x3c031000, 0xaf5401c0, 0xa34201c4, + 0xaf4301f8, 0x9363003f, 0x24020012, 0x14620004, 0x8fbf004c, 0x0e00140d, + 0x00000000, 0x8fbf004c, 0x8fbe0048, 0x8fb70044, 0x8fb60040, 0x8fb5003c, + 0x8fb40038, 0x8fb30034, 0x8fb20030, 0x8fb1002c, 0x8fb00028, 0x03e00008, + 0x27bd0050, 0x27bdffe8, 0xafbf0014, 0xafb00010, 0x8f500180, 0x97420184, + 0x30420200, 0x14400015, 0x00000000, 0x8f430188, 0x3c02ff00, 0x00621824, + 0x3c020200, 0x10620031, 0x0043102b, 0x14400007, 0x3c020300, 0x1060000b, + 0x3c020100, 0x1062000d, 0x00000000, 0x0a000c2c, 0x00000000, 0x10620027, + 0x3c020400, 0x1062003e, 0x02002021, 0x0a000c2c, 0x00000000, 0x0e000c31, + 0x02002021, 0x0a000c2e, 0x8fbf0014, 0x93620005, 0x30420020, 0x1440005e, + 0x8fbf0014, 0x3c029000, 0x34420001, 0x02021025, 0xaf420020, 0x3c038000, + 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x93620005, 0x3c038000, + 0x34630001, 0x02031825, 0x34420020, 0xa3620005, 0xaf430020, 0x93620005, + 0x30420020, 0x14400003, 0x02002021, 0x0000000d, 0x02002021, 0x0e000766, + 0x24055854, 0x0a000c2e, 0x8fbf0014, 0x93620005, 0x30420001, 0x1040003f, + 0x3c029000, 0x34420001, 0x02021025, 0xaf420020, 0x3c038000, 0x8f420020, + 0x00431024, 0x1440fffd, 0x00000000, 0x93620023, 0x34420004, 0xa3620023, + 0x93630005, 0x3c048000, 0x3c020800, 0x306300fe, 0xa3630005, 0x8c430020, + 0x34840001, 0x02042025, 0x0a000c0a, 0xaf440020, 0x00002821, 0x00003021, + 0x0e000fb1, 0x240708d9, 0x3c020800, 0x8c430020, 0x10600023, 0x8fbf0014, + 0x0e00148e, 0x00000000, 0x8f820018, 0xac500000, 0x93630082, 0x9362003f, + 0x8f840018, 0x00031a00, 0x00431025, 0xac820004, 0x8f830018, 0xac600008, + 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010, 0x8f820018, 0xac400014, + 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001, 0x3c020800, 0xaca30018, + 0x944358ce, 0x8f850018, 0x3c02400a, 0x00621825, 0x0e0014cc, 0xaca3001c, + 0x0a000c2e, 0x8fbf0014, 0x0000000d, 0x8fbf0014, 0x8fb00010, 0x03e00008, + 0x27bd0018, 0x27bdffe8, 0xafbf0010, 0x8f420188, 0x00803021, 0x93640000, + 0x24030020, 0x00021402, 0x10830008, 0x304500ff, 0x3c036018, 0x8c625000, + 0x34420400, 0xac625000, 0x0000000d, 0x00000000, 0x24000955, 0x9363003f, + 0x24020012, 0x14620023, 0x3c029000, 0x34420001, 0x3c038000, 0x00c21025, + 0xaf650178, 0xa365007a, 0xaf420020, 0x8f420020, 0x00431024, 0x1440fffd, + 0x00000000, 0x9362007d, 0x3c038000, 0xa362007d, 0x8f640074, 0x34630001, + 0x00c31825, 0xaf430020, 0x04810006, 0x3c038000, 0x00c02021, 0x0e000470, + 0x24050963, 0x0a000c79, 0x8fbf0010, 0x8f4201f8, 0x00431024, 0x1440fffd, + 0x24020002, 0x3c031000, 0xaf4601c0, 0xa34201c4, 0xaf4301f8, 0x0a000c79, + 0x8fbf0010, 0x9362007e, 0x1445000e, 0x00000000, 0x8f620178, 0x1045000b, + 0x00000000, 0x8f820000, 0xaf650178, 0x8f660178, 0x8f440180, 0x8f65004c, + 0x8c430000, 0x0060f809, 0x30c600ff, 0x0a000c79, 0x8fbf0010, 0xaf650178, + 0x8fbf0010, 0x03e00008, 0x27bd0018, 0x27bdffe8, 0xafbf0010, 0x93630000, + 0x24020020, 0x10620005, 0x00000000, 0x93630000, 0x24020030, 0x1462004d, + 0x8fbf0010, 0x93420148, 0x2444ffff, 0x2c830005, 0x10600047, 0x3c020800, + 0x24425800, 0x00041880, 0x00621821, 0x8c640000, 0x00800008, 0x00000000, + 0x8f430144, 0x8f62000c, 0x14620006, 0x24020001, 0xaf62000c, 0x0e000d59, + 0x00000000, 0x0a000cd1, 0x8fbf0010, 0x8f62000c, 0x0a000cca, 0x00000000, + 0x97630010, 0x8f420144, 0x14430006, 0x24020001, 0xa7620010, 0x0e00137a, + 0x00000000, 0x0a000cd1, 0x8fbf0010, 0x97620010, 0x0a000cca, 0x00000000, + 0x97630012, 0x8f420144, 0x14430006, 0x24020001, 0xa7620012, 0x0e001395, + 0x00000000, 0x0a000cd1, 0x8fbf0010, 0x97620012, 0x0a000cca, 0x00000000, + 0x97630014, 0x8f420144, 0x14430006, 0x24020001, 0xa7620014, 0x0e0013bb, + 0x00000000, 0x0a000cd1, 0x8fbf0010, 0x97620014, 0x0a000cca, 0x00000000, + 0x97630016, 0x8f420144, 0x14430006, 0x24020001, 0xa7620016, 0x0e0013be, + 0x00000000, 0x0a000cd1, 0x8fbf0010, 0x97620016, 0x14400006, 0x8fbf0010, + 0x3c030800, 0x8c620070, 0x24420001, 0xac620070, 0x8fbf0010, 0x03e00008, + 0x27bd0018, 0x27bdffe0, 0x3c029000, 0xafbf001c, 0xafb20018, 0xafb10014, + 0xafb00010, 0x8f500140, 0x34420001, 0x3c038000, 0x02021025, 0xaf420020, + 0x8f420020, 0x00431024, 0x1440fffd, 0x24020012, 0x24030080, 0xa362003f, + 0xa3630082, 0x93620023, 0x30420040, 0x10400007, 0x00008821, 0x93620023, + 0x24110001, 0x304200bf, 0xa3620023, 0x0a000cf0, 0x3c028000, 0x3c028000, + 0x34420001, 0x3c039000, 0x34630001, 0x3c048000, 0x02021025, 0x02031825, + 0xaf420020, 0xaf430020, 0x8f420020, 0x00441024, 0x1440fffd, 0x00000000, + 0x9362007d, 0x3c038000, 0x34420020, 0xa362007d, 0x8f640074, 0x34630001, + 0x02031825, 0xaf430020, 0x04810006, 0x3c038000, 0x02002021, 0x0e000470, + 0x24050a63, 0x0a000d13, 0x00000000, 0x8f4201f8, 0x00431024, 0x1440fffd, + 0x24020002, 0x3c031000, 0xaf5001c0, 0xa34201c4, 0xaf4301f8, 0x1220003f, + 0x3c120800, 0x8e420020, 0x8f71004c, 0x1040003c, 0x8fbf001c, 0x0e00148e, + 0x00000000, 0x8f820018, 0xac500000, 0x8f840018, 0x24020001, 0xac820004, + 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010, + 0x8f820018, 0xac510014, 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001, + 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018, 0x3c024010, 0x00621825, + 0x0e0014cc, 0xaca3001c, 0x8e420020, 0x1040001e, 0x8fbf001c, 0x0e00148e, + 0x00000000, 0x8f820018, 0xac500000, 0x8f840018, 0x3c02008d, 0xac820004, + 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010, + 0x8f840018, 0x24020a6a, 0xac820014, 0x8f850018, 0x3c026000, 0x8c434448, + 0x24040001, 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018, 0x3c024019, + 0x00621825, 0x0e0014cc, 0xaca3001c, 0x8fbf001c, 0x8fb20018, 0x8fb10014, + 0x8fb00010, 0x03e00008, 0x27bd0020, 0x27bdffe8, 0xafbf0010, 0x93620081, + 0x3c030800, 0x8c640048, 0x0044102b, 0x14400005, 0x00000000, 0x0e000cd3, + 0x00000000, 0x0a000da4, 0x8fbf0010, 0x93620081, 0x24420001, 0x0e0013c4, + 0xa3620081, 0x9763006a, 0x00032880, 0x14a00002, 0x00403821, 0x24050001, + 0x97630068, 0x93640081, 0x3c020800, 0x8c46004c, 0x00652821, 0x00852804, + 0x00c5102b, 0x54400001, 0x00a03021, 0x3c020800, 0x8c440050, 0x00c4182b, + 0x54600001, 0x00c02021, 0x8f420074, 0x2403fffe, 0x00832824, 0x00a21021, + 0xaf62000c, 0x10e00021, 0x3c029000, 0x8f450140, 0x34420001, 0x3c038000, + 0x00a21025, 0xaf420020, 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, + 0x9362007d, 0x3c038000, 0x34420004, 0xa362007d, 0x8f640074, 0x34630001, + 0x00a31825, 0xaf430020, 0x04810006, 0x3c038000, 0x00a02021, 0x0e000470, + 0x24050a92, 0x0a000da4, 0x8fbf0010, 0x8f4201f8, 0x00431024, 0x1440fffd, + 0x24020002, 0x3c031000, 0xaf4501c0, 0xa34201c4, 0xaf4301f8, 0x8fbf0010, + 0x03e00008, 0x27bd0018, 0x27bdffd8, 0xafb3001c, 0x27530100, 0xafbf0024, + 0xafb40020, 0xafb20018, 0xafb10014, 0xafb00010, 0x96620008, 0x3c140800, + 0x8f520100, 0x30420001, 0x104000da, 0x00000000, 0x8e700018, 0x8f630054, + 0x2602ffff, 0x00431023, 0x18400006, 0x00000000, 0x0000000d, 0x00000000, + 0x2400015c, 0x0a000dea, 0x00008821, 0x8f62004c, 0x02021023, 0x18400028, + 0x00008821, 0x93650120, 0x93640121, 0x3c030800, 0x8c62008c, 0x308400ff, + 0x24420001, 0x30a500ff, 0x00803821, 0x1485000b, 0xac62008c, 0x3c040800, + 0x8c830090, 0x24630001, 0xac830090, 0x93620122, 0x30420001, 0x00021023, + 0x30420005, 0x0a000dea, 0x34510004, 0x27660100, 0x00041080, 0x00c21021, + 0x8c430000, 0x02031823, 0x04600004, 0x24820001, 0x30440007, 0x1485fff9, + 0x00041080, 0x10870007, 0x3c030800, 0xa3640121, 0x8c620094, 0x24110005, + 0x24420001, 0x0a000dea, 0xac620094, 0x24110004, 0x32220001, 0x1040001e, + 0x8e820020, 0x1040001d, 0x32220004, 0x0e00148e, 0x00000000, 0x8f820018, + 0xac520000, 0x8f840018, 0x24020001, 0xac820004, 0x8f830018, 0xac600008, + 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010, 0x8f820018, 0xac500014, + 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001, 0x3c020800, 0xaca30018, + 0x944358ce, 0x8f850018, 0x3c024010, 0x00621825, 0x0e0014cc, 0xaca3001c, + 0x32220004, 0x10400081, 0x00003821, 0x3c029000, 0x34420001, 0x3c038000, + 0x02421025, 0xa360007c, 0xaf420020, 0x8f420020, 0x00431024, 0x1440fffd, + 0x00000000, 0x93620023, 0x30420080, 0x10400011, 0x00000000, 0x8f65005c, + 0x8f63004c, 0x9764003c, 0x8f620064, 0x00a32823, 0x00852821, 0x00a2102b, + 0x54400006, 0x3c023fff, 0x93620023, 0x3042007f, 0xa3620023, 0xaf700064, + 0x3c023fff, 0x0a000e37, 0x3442ffff, 0x8f62005c, 0x02021023, 0x04400011, + 0x00000000, 0x8f65005c, 0x8f630064, 0x9764003c, 0x3c023fff, 0x3442ffff, + 0xaf700064, 0x00a32823, 0x00852821, 0x0045102b, 0x10400004, 0x02051021, + 0x3c053fff, 0x34a5ffff, 0x02051021, 0xaf62005c, 0x24070001, 0xaf70004c, + 0x8f620054, 0x16020005, 0x00000000, 0x93620023, 0x30420040, 0x10400017, + 0x24020001, 0x9762006a, 0x00022880, 0x50a00001, 0x24050001, 0x97630068, + 0x93640081, 0x3c020800, 0x8c46004c, 0x00652821, 0x00852804, 0x00c5102b, + 0x54400001, 0x00a03021, 0x3c020800, 0x8c440050, 0x00c4182b, 0x54600001, + 0x00c02021, 0x8f420074, 0x2403fffe, 0x00832824, 0x00a21021, 0xaf62000c, + 0x93620082, 0x30420080, 0x50400001, 0xa3600081, 0x3c028000, 0x34420001, + 0x02421025, 0xaf420020, 0x9363007e, 0x9362007a, 0x10620004, 0x00000000, + 0x0e0013c4, 0x00000000, 0x00403821, 0x10e0001f, 0x3c029000, 0x34420001, + 0x02421025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd, + 0x00000000, 0x9362007d, 0x3c038000, 0xa362007d, 0x8f640074, 0x34630001, + 0x02431825, 0xaf430020, 0x04810006, 0x3c038000, 0x02402021, 0x0e000470, + 0x24050b3d, 0x0a000e8d, 0x00000000, 0x8f4201f8, 0x00431024, 0x1440fffd, + 0x24020002, 0x3c031000, 0xaf5201c0, 0xa34201c4, 0xaf4301f8, 0x9342010b, + 0x9343010b, 0x8e820020, 0x27500100, 0x38630006, 0x10400029, 0x2c710001, + 0x0e00148e, 0x00000000, 0x8f830018, 0x8e020000, 0xac620000, 0x8f840018, + 0x96020008, 0xac820004, 0x8f830018, 0x8e020014, 0xac620008, 0x8f850018, + 0x3c026000, 0x8c434448, 0xaca3000c, 0x8f840018, 0x96020012, 0xac820010, + 0x8f850018, 0x8e030020, 0xaca30014, 0x9602000c, 0x9603000e, 0x8f840018, + 0x00021400, 0x00431025, 0xac820018, 0x12200005, 0x3c020800, 0x944358ce, + 0x8f840018, 0x0a000eb8, 0x3c024013, 0x944358ce, 0x8f840018, 0x3c024014, + 0x00621825, 0xac83001c, 0x0e0014cc, 0x24040001, 0x8e700014, 0x8f620040, + 0x14500003, 0x00501023, 0x0a000ec3, 0x00001021, 0x28420001, 0x1040003a, + 0x00000000, 0x0e000fae, 0x02002021, 0xaf700040, 0x9362003e, 0x30420001, + 0x1440000b, 0x3c029000, 0x93620022, 0x24420001, 0xa3620022, 0x93630022, + 0x3c020800, 0x8c440098, 0x0064182b, 0x14600025, 0x3c020800, 0x3c029000, + 0x34420001, 0x02421025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024, + 0x1440fffd, 0x00000000, 0x9362007d, 0x3c038000, 0x34420001, 0xa362007d, + 0x8f640074, 0x34630001, 0x02431825, 0xaf430020, 0x04810006, 0x3c038000, + 0x02402021, 0x0e000470, 0x24050273, 0x0a000ef6, 0x24020001, 0x8f4201f8, + 0x00431024, 0x1440fffd, 0x24020002, 0x3c031000, 0xaf5201c0, 0xa34201c4, + 0xaf4301f8, 0x24020001, 0xa7620012, 0x0a000efe, 0xa3600022, 0x9743007a, + 0x9444002a, 0x00641821, 0x3063fffe, 0xa7630012, 0x97420108, 0x8fbf0024, + 0x8fb40020, 0x8fb3001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, 0x00021042, + 0x30420001, 0x03e00008, 0x27bd0028, 0x27bdffe0, 0xafb20018, 0x3c120800, + 0x8e420020, 0xafb00010, 0x27500100, 0xafbf001c, 0x10400046, 0xafb10014, + 0x0e00148e, 0x00000000, 0x8f840018, 0x8e020000, 0xac820000, 0x936300b1, + 0x936200c5, 0x8f850018, 0x00031e00, 0x00021400, 0x34420100, 0x00621825, + 0xaca30004, 0x8f840018, 0x8e02001c, 0xac820008, 0x8f830018, 0x8f620048, + 0xac62000c, 0x8f840018, 0x96020012, 0xac820010, 0x8f830018, 0x8f620040, + 0x24040001, 0xac620014, 0x8f850018, 0x3c026000, 0x8c434448, 0x3c020800, + 0x245158c0, 0xaca30018, 0x9623000e, 0x8f850018, 0x3c024016, 0x00621825, + 0x0e0014cc, 0xaca3001c, 0x96030008, 0x30630010, 0x1060001c, 0x8e420020, + 0x1040001a, 0x8e100000, 0x0e00148e, 0x00000000, 0x8f820018, 0xac500000, + 0x8f830018, 0xac600004, 0x8f820018, 0xac400008, 0x8f830018, 0xac60000c, + 0x8f820018, 0xac400010, 0x8f830018, 0xac600014, 0x8f850018, 0x3c036000, + 0x8c634448, 0x24040001, 0xaca30018, 0x9622000e, 0x8f850018, 0x3c034015, + 0x00431025, 0x0e0014cc, 0xaca2001c, 0x00001021, 0x8fbf001c, 0x8fb20018, + 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020, 0x27bdffe0, 0xafb20018, + 0x3c120800, 0x8e420020, 0xafb00010, 0x27500100, 0xafbf001c, 0x10400041, + 0xafb10014, 0x0e00148e, 0x00000000, 0x8f830018, 0x8e020000, 0xac620000, + 0x8f840018, 0x24020100, 0xac820004, 0x8f830018, 0x8e02001c, 0xac620008, + 0x8f840018, 0x8e020018, 0xac82000c, 0x8f830018, 0x96020012, 0xac620010, + 0x8f840018, 0x96020008, 0xac820014, 0x8f850018, 0x3c026000, 0x8c434448, + 0x24040001, 0x3c020800, 0x245158c0, 0xaca30018, 0x9623000e, 0x8f850018, + 0x3c024017, 0x00621825, 0x0e0014cc, 0xaca3001c, 0x96030008, 0x30630010, + 0x1060001c, 0x8e420020, 0x1040001a, 0x8e100000, 0x0e00148e, 0x00000000, + 0x8f820018, 0xac500000, 0x8f830018, 0xac600004, 0x8f820018, 0xac400008, + 0x8f830018, 0xac60000c, 0x8f820018, 0xac400010, 0x8f830018, 0xac600014, + 0x8f850018, 0x3c036000, 0x8c634448, 0x24040001, 0xaca30018, 0x9622000e, + 0x8f850018, 0x3c034015, 0x00431025, 0x0e0014cc, 0xaca2001c, 0x00001021, + 0x8fbf001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020, + 0x27bdfff0, 0x03e00008, 0x27bd0010, 0x27bdffd0, 0xafb10014, 0x00808821, + 0xafb40020, 0x00c0a021, 0xafbf0028, 0xafb50024, 0xafb3001c, 0xafb20018, + 0xafb00010, 0x93620023, 0x00e0a821, 0x30420040, 0x1040003e, 0x30b3ffff, + 0x3c120800, 0x8e420020, 0x1040003a, 0x8f70004c, 0x0e00148e, 0x00000000, + 0x8f820018, 0xac510000, 0x8f840018, 0x24020001, 0xac820004, 0x8f830018, + 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010, 0x8f820018, + 0x24040001, 0xac500014, 0x8f850018, 0x3c026000, 0x8c434448, 0x3c020800, + 0x245058c0, 0xaca30018, 0x9603000e, 0x8f850018, 0x3c024010, 0x00621825, + 0x0e0014cc, 0xaca3001c, 0x8e430020, 0x1060001b, 0x00000000, 0x0e00148e, + 0x00000000, 0x8f820018, 0xac510000, 0x8f840018, 0x3c02008d, 0xac820004, + 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010, + 0x8f820018, 0xac550014, 0x8f850018, 0x3c036000, 0x8c634448, 0x24040001, + 0xaca30018, 0x9602000e, 0x8f850018, 0x3c034019, 0x00431025, 0x0e0014cc, + 0xaca2001c, 0x93620023, 0x30420020, 0x14400003, 0x3c120800, 0x1280003f, + 0x3c029000, 0x8e420020, 0x8f70004c, 0x1040003b, 0x3c029000, 0x0e00148e, + 0x00000000, 0x8f820018, 0xac510000, 0x8f840018, 0x24020001, 0xac820004, + 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010, + 0x8f820018, 0x24040001, 0xac500014, 0x8f850018, 0x3c026000, 0x8c434448, + 0x3c020800, 0x245058c0, 0xaca30018, 0x9603000e, 0x8f850018, 0x3c024010, + 0x00621825, 0x0e0014cc, 0xaca3001c, 0x8e430020, 0x1060001c, 0x3c029000, + 0x0e00148e, 0x00000000, 0x8f820018, 0xac510000, 0x8f840018, 0x00131400, + 0xac820004, 0x8f830018, 0xac750008, 0x8f820018, 0xac40000c, 0x8f830018, + 0xac600010, 0x8f820018, 0xac400014, 0x8f850018, 0x3c036000, 0x8c634448, + 0x24040001, 0xaca30018, 0x9602000e, 0x8f850018, 0x3c03401b, 0x00431025, + 0x0e0014cc, 0xaca2001c, 0x3c029000, 0x34420001, 0x02221025, 0xaf420020, + 0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x93630023, + 0x3c028000, 0x34420001, 0x02221025, 0x8fbf0028, 0x8fb50024, 0x8fb40020, + 0x8fb3001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, 0x3063009f, 0xa3630023, + 0xaf420020, 0x03e00008, 0x27bd0030, 0x27bdffe0, 0xafb10014, 0x27510100, + 0x3c029000, 0x34420001, 0xafb00010, 0x00808021, 0x02021025, 0x3c038000, + 0xafbf0018, 0xaf420020, 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, + 0xa7600008, 0x8f63005c, 0x3c028000, 0x34420001, 0xaf630148, 0x8f640050, + 0x02021025, 0x3c039000, 0xaf64017c, 0xaf420020, 0x8f450100, 0x34630001, + 0x3c048000, 0x00a31825, 0xaf430020, 0x8f420020, 0x00441024, 0x1440fffd, + 0x00000000, 0x9362007d, 0x3c038000, 0x34420001, 0xa362007d, 0x8f640074, + 0x34630001, 0x00a31825, 0xaf430020, 0x04810006, 0x3c038000, 0x00a02021, + 0x0e000470, 0x24050de5, 0x0a001093, 0x3c020800, 0x8f4201f8, 0x00431024, + 0x1440fffd, 0x24020002, 0x3c031000, 0xaf4501c0, 0xa34201c4, 0xaf4301f8, + 0x3c020800, 0x8c430020, 0x1060001e, 0x8fbf0018, 0x0e00148e, 0x00000000, + 0x8f830018, 0xac700000, 0x9622000c, 0x8f840018, 0x00021400, 0xac820004, + 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010, + 0x8f820018, 0xac400014, 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001, + 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018, 0x3c02401f, 0x00621825, + 0x0e0014cc, 0xaca3001c, 0x8fbf0018, 0x8fb10014, 0x8fb00010, 0x03e00008, + 0x27bd0020, 0x3c020800, 0x24424c3c, 0xaf82000c, 0x03e00008, 0x00000000, + 0x27bdffe8, 0xafb00010, 0x27500100, 0xafbf0014, 0x8e02001c, 0x14400003, + 0x3c020800, 0x0000000d, 0x3c020800, 0x8c430020, 0x10600020, 0x00001021, + 0x0e00148e, 0x00000000, 0x8f830018, 0x8e020000, 0xac620000, 0x8f840018, + 0x8e02001c, 0xac820004, 0x8f830018, 0xac600008, 0x8f840018, 0x8e020018, + 0xac82000c, 0x8f850018, 0x96020012, 0xaca20010, 0x8f830018, 0x3c026000, + 0xac600014, 0x8f840018, 0x8c434448, 0x3c020800, 0xac830018, 0x944358ce, + 0x8f840018, 0x3c024012, 0x00621825, 0xac83001c, 0x0e0014cc, 0x24040001, + 0x00001021, 0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, 0x3c020800, + 0x97430078, 0x9444002e, 0x00001021, 0x00641821, 0x3063fffe, 0x03e00008, + 0xa7630010, 0x27bdfff0, 0x00001021, 0x03e00008, 0x27bd0010, 0x8f420100, + 0x34420001, 0xaf4200a4, 0x03e00008, 0x00001021, 0x27bdffe0, 0xafbf0018, + 0xafb10014, 0xafb00010, 0x9362007e, 0x30d000ff, 0x16020031, 0x00808821, + 0x8f620178, 0x1602002e, 0x00000000, 0x9362007f, 0x1602002b, 0x00000000, + 0x9362007a, 0x16020004, 0x00000000, 0x0000000d, 0x00000000, 0x240009d2, + 0x0e0013e6, 0x00000000, 0x3c039000, 0x34630001, 0x3c048000, 0x02231825, + 0xa370007a, 0xaf430020, 0x8f420020, 0x00441024, 0x1440fffd, 0x00000000, + 0x9362007d, 0x3c038000, 0xa362007d, 0x8f640074, 0x34630001, 0x02231825, + 0xaf430020, 0x04810006, 0x3c038000, 0x02202021, 0x0e000470, 0x240509dd, + 0x0a001138, 0x8fbf0018, 0x8f4201f8, 0x00431024, 0x1440fffd, 0x24020002, + 0x3c031000, 0xaf5101c0, 0xa34201c4, 0xaf4301f8, 0x0a001138, 0x8fbf0018, + 0x0000000d, 0x00000000, 0x240009e2, 0x8fbf0018, 0x8fb10014, 0x8fb00010, + 0x03e00008, 0x27bd0020, 0x27bdffe8, 0x30a500ff, 0x3c029000, 0x34420001, + 0x00803821, 0x00e21025, 0x3c038000, 0xafbf0010, 0xaf420020, 0x8f420020, + 0x00431024, 0x1440fffd, 0x00000000, 0x9362007d, 0x3c038000, 0x00a21025, + 0xa362007d, 0x8f640074, 0x34630001, 0x00e31825, 0xaf430020, 0x04810006, + 0x3c038000, 0x00e02021, 0x0e000470, 0x00c02821, 0x0a001161, 0x8fbf0010, + 0x8f4201f8, 0x00431024, 0x1440fffd, 0x24020002, 0x3c031000, 0xaf4701c0, + 0xa34201c4, 0xaf4301f8, 0x8fbf0010, 0x03e00008, 0x27bd0018, 0x3c020800, + 0x8c430020, 0x27bdffe8, 0xafb00010, 0x27500100, 0x10600024, 0xafbf0014, + 0x0e00148e, 0x00000000, 0x8f830018, 0x8e020000, 0xac620000, 0x8f840018, + 0x8e020004, 0xac820004, 0x8f830018, 0x8e020018, 0xac620008, 0x8f840018, + 0x8e03001c, 0xac83000c, 0x9602000c, 0x9203000a, 0x8f840018, 0x00021400, + 0x00431025, 0xac820010, 0x8f830018, 0x3c026000, 0xac600014, 0x8f840018, + 0x8c434448, 0xac830018, 0x96020008, 0x3c030800, 0x946458ce, 0x8f850018, + 0x00021400, 0x00441025, 0x24040001, 0x0e0014cc, 0xaca2001c, 0x8fbf0014, + 0x8fb00010, 0x03e00008, 0x27bd0018, 0x3c020800, 0x8c430020, 0x27bdffe8, + 0xafb00010, 0x27500100, 0x10600020, 0xafbf0014, 0x0e00148e, 0x00000000, + 0x8f820018, 0xac400000, 0x8f830018, 0xac600004, 0x8f820018, 0xac400008, + 0x8f830018, 0xac60000c, 0x9602000c, 0x9603000e, 0x8f840018, 0x00021400, + 0x00431025, 0xac820010, 0x8f830018, 0x3c026000, 0xac600014, 0x8f840018, + 0x8c434448, 0xac830018, 0x96020008, 0x3c030800, 0x946458ce, 0x8f850018, + 0x00021400, 0x00441025, 0x24040001, 0x0e0014cc, 0xaca2001c, 0x8fbf0014, + 0x8fb00010, 0x03e00008, 0x27bd0018, 0x27bdffe8, 0xafb00010, 0x27500100, + 0xafbf0014, 0x9602000c, 0x10400024, 0x00802821, 0x3c020800, 0x8c430020, + 0x1060003a, 0x8fbf0014, 0x0e00148e, 0x00000000, 0x8f840018, 0x8e030000, + 0xac830000, 0x9602000c, 0x8f840018, 0x00021400, 0xac820004, 0x8f830018, + 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, 0xac600010, 0x8f820018, + 0xac400014, 0x8f850018, 0x3c026000, 0x8c434448, 0x24040001, 0x3c020800, + 0xaca30018, 0x944358ce, 0x8f850018, 0x3c02400b, 0x00621825, 0x0e0014cc, + 0xaca3001c, 0x0a0011ff, 0x8fbf0014, 0x93620005, 0x30420010, 0x14400015, + 0x3c029000, 0x34420001, 0x00a21025, 0xaf420020, 0x3c038000, 0x8f420020, + 0x00431024, 0x1440fffd, 0x00000000, 0x3c038000, 0x93620005, 0x34630001, + 0x00a02021, 0x00a31825, 0x24055852, 0x34420010, 0xa3620005, 0x0e000766, + 0xaf430020, 0x0a0011ff, 0x8fbf0014, 0x0000000d, 0x8fbf0014, 0x8fb00010, + 0x03e00008, 0x27bd0018, 0x3c020800, 0x8c430020, 0x27bdffe8, 0xafb00010, + 0x27500100, 0x10600022, 0xafbf0014, 0x0e00148e, 0x00000000, 0x8f840018, + 0x8e020004, 0xac820000, 0x9603000c, 0x9762002c, 0x8f840018, 0x00031c00, + 0x00431025, 0xac820004, 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, + 0x8f830018, 0xac600010, 0x8f820018, 0xac400014, 0x8f850018, 0x3c026000, + 0x8c434448, 0x24040001, 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018, + 0x3c02400e, 0x00621825, 0x0e0014cc, 0xaca3001c, 0x0e00122e, 0x8e040000, + 0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, 0x3c038000, 0x8f420278, + 0x00431024, 0x1440fffd, 0x24020002, 0x3c031000, 0xaf440240, 0xa3420244, + 0x03e00008, 0xaf430278, 0x3c020800, 0x8c430020, 0x27bdffe0, 0xafb10014, + 0x00808821, 0xafb20018, 0x00c09021, 0xafb00010, 0x30b0ffff, 0x1060001c, + 0xafbf001c, 0x0e00148e, 0x00000000, 0x8f820018, 0xac510000, 0x8f840018, + 0x00101400, 0xac820004, 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, + 0x8f830018, 0xac600010, 0x8f820018, 0xac520014, 0x8f840018, 0x3c026000, + 0x8c434448, 0x3c020800, 0xac830018, 0x944358ce, 0x8f840018, 0x3c024019, + 0x00621825, 0xac83001c, 0x0e0014cc, 0x24040001, 0x8fbf001c, 0x8fb20018, + 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020, 0x27bdffe8, 0x27450100, + 0xafbf0010, 0x94a3000c, 0x240200c1, 0x14620031, 0x00803021, 0x3c029000, + 0x34420001, 0x00c21025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024, + 0x1440fffd, 0x3c028000, 0x34420001, 0x3c049000, 0x34840001, 0x3c058000, + 0x24030012, 0x00c21025, 0x00c42025, 0xa363003f, 0xaf420020, 0xaf440020, + 0x8f420020, 0x00451024, 0x1440fffd, 0x00000000, 0x9362007d, 0x3c038000, + 0x34420020, 0xa362007d, 0x8f640074, 0x34630001, 0x00c31825, 0xaf430020, + 0x04810006, 0x3c038000, 0x00c02021, 0x0e000470, 0x24050906, 0x0a0012a1, + 0x8fbf0010, 0x8f4201f8, 0x00431024, 0x1440fffd, 0x24020002, 0x3c031000, + 0xaf4601c0, 0xa34201c4, 0xaf4301f8, 0x0a0012a1, 0x8fbf0010, 0x00c02021, + 0x94a5000c, 0x24060001, 0x0e000fb1, 0x2407090e, 0x8fbf0010, 0x03e00008, + 0x27bd0018, 0x3c020800, 0x8c430020, 0x27bdffe0, 0xafb00010, 0x00808021, + 0xafb20018, 0x00a09021, 0xafb10014, 0x30d100ff, 0x1060001c, 0xafbf001c, + 0x0e00148e, 0x00000000, 0x8f820018, 0xac500000, 0x8f840018, 0x24020001, + 0xac820004, 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, + 0xac600010, 0x8f820018, 0xac520014, 0x8f840018, 0x3c026000, 0x8c434448, + 0x3c020800, 0xac830018, 0x944358ce, 0x8f840018, 0x3c024010, 0x00621825, + 0xac83001c, 0x0e0014cc, 0x02202021, 0x8fbf001c, 0x8fb20018, 0x8fb10014, + 0x8fb00010, 0x03e00008, 0x27bd0020, 0x27bdffe8, 0xafbf0014, 0xafb00010, + 0x93620005, 0x30420001, 0x10400036, 0x00808021, 0x3c029000, 0x34420001, + 0x02021025, 0xaf420020, 0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd, + 0x00000000, 0x93620023, 0x34420004, 0xa3620023, 0x93630005, 0x3c048000, + 0x3c020800, 0x306300fe, 0xa3630005, 0x8c430020, 0x34840001, 0x02042025, + 0xaf440020, 0x10600020, 0x8fbf0014, 0x0e00148e, 0x00000000, 0x8f820018, + 0xac500000, 0x93630082, 0x9362003f, 0x8f840018, 0x00031a00, 0x00431025, + 0xac820004, 0x8f830018, 0xac600008, 0x8f820018, 0xac40000c, 0x8f830018, + 0xac600010, 0x8f820018, 0xac400014, 0x8f840018, 0x3c026000, 0x8c434448, + 0x3c020800, 0xac830018, 0x944358ce, 0x8f840018, 0x3c02400a, 0x00621825, + 0xac83001c, 0x0e0014cc, 0x24040001, 0x8fbf0014, 0x8fb00010, 0x03e00008, + 0x27bd0018, 0x3c020800, 0x8c430020, 0x27bdffe0, 0xafb10014, 0x00808821, + 0xafb20018, 0x00a09021, 0xafb00010, 0x30d000ff, 0x1060002f, 0xafbf001c, + 0x0e00148e, 0x00000000, 0x8f820018, 0xac510000, 0x8f830018, 0xac700004, + 0x8f820018, 0xac520008, 0x8f830018, 0xac60000c, 0x8f820018, 0xac400010, + 0x9763006a, 0x00032880, 0x50a00001, 0x24050001, 0x97630068, 0x93640081, + 0x3c020800, 0x8c46004c, 0x00652821, 0x00852804, 0x00c5102b, 0x54400001, + 0x00a03021, 0x3c020800, 0x8c440050, 0x00c4182b, 0x54600001, 0x00c02021, + 0x8f830018, 0x2402fffe, 0x00822824, 0x3c026000, 0xac650014, 0x8f840018, + 0x8c434448, 0x3c020800, 0xac830018, 0x944358ce, 0x8f840018, 0x3c024011, + 0x00621825, 0xac83001c, 0x0e0014cc, 0x24040001, 0x8fbf001c, 0x8fb20018, + 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020, 0x27bdffe8, 0xafbf0014, + 0xafb00010, 0x8f440100, 0x27500100, 0x8f650050, 0x0e0010fc, 0x9206001b, + 0x3c020800, 0x8c430020, 0x1060001d, 0x8e100018, 0x0e00148e, 0x00000000, + 0x8f840018, 0x8f420100, 0xac820000, 0x8f830018, 0xac700004, 0x8f840018, + 0x8f620050, 0xac820008, 0x8f830018, 0xac60000c, 0x8f820018, 0xac400010, + 0x8f830018, 0x3c026000, 0xac600014, 0x8f850018, 0x8c434448, 0x24040001, + 0x3c020800, 0xaca30018, 0x944358ce, 0x8f850018, 0x3c02401c, 0x00621825, + 0x0e0014cc, 0xaca3001c, 0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, + 0x8f430238, 0x3c020800, 0x04610013, 0x8c44009c, 0x2406fffe, 0x3c050800, + 0x3c038000, 0x2484ffff, 0x14800009, 0x00000000, 0x97420078, 0x8ca3007c, + 0x24420001, 0x00461024, 0x24630001, 0xa7620010, 0x03e00008, 0xaca3007c, + 0x8f420238, 0x00431024, 0x1440fff3, 0x2484ffff, 0x8f420140, 0x3c031000, + 0xaf420200, 0x03e00008, 0xaf430238, 0x27bdffe8, 0x3c029000, 0xafbf0010, + 0x8f450140, 0x34420001, 0x3c038000, 0x00a21025, 0xaf420020, 0x8f420020, + 0x00431024, 0x1440fffd, 0x00000000, 0x9362007d, 0x3c038000, 0x34420001, + 0xa362007d, 0x8f640074, 0x34630001, 0x00a31825, 0xaf430020, 0x04810006, + 0x3c038000, 0x00a02021, 0x0e000470, 0x24050ac7, 0x0a0013b9, 0x8fbf0010, + 0x8f4201f8, 0x00431024, 0x1440fffd, 0x24020002, 0x3c031000, 0xaf4501c0, + 0xa34201c4, 0xaf4301f8, 0x8fbf0010, 0x03e00008, 0x27bd0018, 0x0000000d, + 0x03e00008, 0x00000000, 0x0000000d, 0x03e00008, 0x00000000, 0x24020001, + 0x03e00008, 0xa7620010, 0x9362003f, 0x304400ff, 0x3883000e, 0x2c630001, + 0x38820010, 0x2c420001, 0x00621825, 0x14600003, 0x24020012, 0x14820003, + 0x00000000, 0x03e00008, 0x00001021, 0x9363007e, 0x9362007a, 0x14620006, + 0x00000000, 0x9363007e, 0x24020001, 0x24630001, 0x03e00008, 0xa363007e, + 0x9362007e, 0x8f630178, 0x304200ff, 0x14430006, 0x00000000, 0x9363000b, + 0x24020001, 0x24630001, 0x03e00008, 0xa363000b, 0x03e00008, 0x00001021, + 0x9362000b, 0x10400023, 0x00001021, 0xa360000b, 0x9362003f, 0x304400ff, + 0x3883000e, 0x2c630001, 0x38820010, 0x2c420001, 0x00621825, 0x14600017, + 0x00001821, 0x24020012, 0x10820014, 0x00000000, 0x9363007e, 0x9362007a, + 0x14620007, 0x00000000, 0x9362007e, 0x24030001, 0x24420001, 0xa362007e, + 0x03e00008, 0x00601021, 0x9362007e, 0x8f630178, 0x304200ff, 0x14430005, + 0x00001821, 0x9362000b, 0x24030001, 0x24420001, 0xa362000b, 0x03e00008, + 0x00601021, 0x03e00008, 0x00000000, 0x24040001, 0xaf64000c, 0x8f6300dc, + 0x8f6200cc, 0x50620001, 0xa7640010, 0xa7640012, 0xa7640014, 0x03e00008, + 0xa7640016, 0x3c020800, 0x8c430020, 0x27bdffe8, 0x1060001b, 0xafbf0010, + 0x0e00148e, 0x00000000, 0x8f820018, 0xac400000, 0x8f830018, 0xac600004, + 0x8f820018, 0xac400008, 0x8f830018, 0xac60000c, 0x8f820018, 0xac400010, + 0x8f830018, 0x3c026000, 0xac600014, 0x8f840018, 0x8c434448, 0x3c020800, + 0xac830018, 0x944358ce, 0x8f840018, 0x3c024020, 0x00621825, 0xac83001c, + 0x0e0014cc, 0x24040001, 0x8fbf0010, 0x03e00008, 0x27bd0018, 0x3c020800, + 0x8c430020, 0x27bdffe0, 0xafb00010, 0x00a08021, 0xafb10014, 0x00c08821, + 0xafb20018, 0x00e09021, 0x1060001e, 0xafbf001c, 0x0e00148e, 0x00000000, + 0x8f840018, 0x8f420100, 0xac820000, 0x8f830018, 0xac700004, 0x8f820018, + 0xac510008, 0x8f830018, 0xac72000c, 0x8f840018, 0x8fa20030, 0xac820010, + 0x8f830018, 0x8fa20034, 0xac620014, 0x8f840018, 0x3c026000, 0x8c434448, + 0x3c020800, 0xac830018, 0x944358ce, 0x8f840018, 0x3c0240c9, 0x00621825, + 0xac83001c, 0x0e0014cc, 0x24040001, 0x8fbf001c, 0x8fb20018, 0x8fb10014, + 0x8fb00010, 0x03e00008, 0x27bd0020, 0x3c020800, 0x8c430020, 0x27bdffe8, + 0xafb00010, 0x27500100, 0x1060001d, 0xafbf0014, 0x0e00148e, 0x00000000, + 0x8f830018, 0x8e020004, 0xac620000, 0x8f840018, 0x8e020018, 0xac820004, + 0x8f850018, 0x8e020000, 0xaca20008, 0x8f830018, 0xac60000c, 0x8f820018, + 0xac400010, 0x8f830018, 0xac600014, 0x8f820018, 0xac400018, 0x96030008, + 0x3c020800, 0x944458ce, 0x8f850018, 0x00031c00, 0x00641825, 0x24040001, + 0x0e0014cc, 0xaca3001c, 0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, + 0x3c060800, 0x24c558c0, 0x3c02000a, 0x03421821, 0x94640006, 0x94a2000a, + 0x00441023, 0x00021400, 0x00021c03, 0x04610006, 0xa4a40006, 0x0000000d, + 0x00000000, 0x2400005a, 0x0a0014a3, 0x24020001, 0x8f820014, 0x0062102b, + 0x14400002, 0x00001021, 0x24020001, 0x304200ff, 0x1040001c, 0x274a0400, + 0x3c07000a, 0x3c020800, 0x244558c0, 0x94a9000a, 0x8f880014, 0x03471021, + 0x94430006, 0x00402021, 0xa4a30006, 0x94820006, 0xa4a20006, 0x01221023, + 0x00021400, 0x00021403, 0x04410006, 0x0048102b, 0x0000000d, 0x00000000, + 0x2400005a, 0x0a0014be, 0x24020001, 0x14400002, 0x00001021, 0x24020001, + 0x304200ff, 0x1440ffec, 0x03471021, 0x24c458c0, 0x8c820010, 0xaf420038, + 0x8c830014, 0x3c020005, 0xaf43003c, 0xaf420030, 0xaf800010, 0xaf8a0018, + 0x03e00008, 0x00000000, 0x27bdffe0, 0x8f820010, 0x8f850018, 0x3c070800, + 0x24e858c0, 0xafbf001c, 0xafb20018, 0xafb10014, 0xafb00010, 0x9503000a, + 0x8d060014, 0x00009021, 0x309000ff, 0x00e08821, 0x24420001, 0x24a50020, + 0x24630001, 0xaf820010, 0xaf850018, 0xa503000a, 0x24c30020, 0x3c028000, + 0x04c10007, 0xad030014, 0x00621024, 0x14400005, 0x262258c0, 0x8d020010, + 0x24420001, 0xad020010, 0x262258c0, 0x9444000a, 0x94450018, 0x0010102b, + 0x00a41826, 0x2c630001, 0x00621825, 0x1060001c, 0x3c030006, 0x8f820010, + 0x24120001, 0x00021140, 0x00431025, 0xaf420030, 0x00000000, 0x00000000, + 0x00000000, 0x27450400, 0x8f420000, 0x30420010, 0x1040fffd, 0x262258c0, + 0x9444000a, 0x94430018, 0xaf800010, 0xaf850018, 0x14830012, 0x262758c0, + 0x0e00155a, 0x00000000, 0x1600000e, 0x262758c0, 0x0e00148e, 0x00000000, + 0x0a001517, 0x262758c0, 0x00041c00, 0x00031c03, 0x00051400, 0x00021403, + 0x00621823, 0x18600002, 0x3c026000, 0xac400808, 0x262758c0, 0x94e2000e, + 0x94e3000c, 0x24420001, 0xa4e2000e, 0x3042ffff, 0x50430001, 0xa4e0000e, + 0x12000005, 0x3c02000a, 0x94e2000a, 0xa74200a2, 0x0a001554, 0x02401021, + 0x03421821, 0x94640006, 0x94e2000a, 0x00441023, 0x00021400, 0x00021c03, + 0x04610006, 0xa4e40006, 0x0000000d, 0x00000000, 0x2400005a, 0x0a001536, + 0x24020001, 0x8f820014, 0x0062102b, 0x14400002, 0x00001021, 0x24020001, + 0x304200ff, 0x1040001b, 0x3c020800, 0x3c06000a, 0x244558c0, 0x94a8000a, + 0x8f870014, 0x03461021, 0x94430006, 0x00402021, 0xa4a30006, 0x94820006, + 0xa4a20006, 0x01021023, 0x00021400, 0x00021403, 0x04410006, 0x0047102b, + 0x0000000d, 0x00000000, 0x2400005a, 0x0a001550, 0x24020001, 0x14400002, + 0x00001021, 0x24020001, 0x304200ff, 0x1440ffec, 0x03461021, 0x02401021, + 0x8fbf001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020, + 0x3c020800, 0x244558c0, 0x94a3001a, 0x8ca40024, 0x00403021, 0x000318c0, + 0x00832021, 0xaf44003c, 0x8ca20020, 0xaf420038, 0x3c020050, 0x34420008, + 0xaf420030, 0x00000000, 0x00000000, 0x00000000, 0x8f420000, 0x30420020, + 0x1040fffd, 0x00000000, 0x8f430400, 0x24c658c0, 0xacc30010, 0x8f420404, + 0x3c030020, 0xacc20014, 0xaf430030, 0x94c40018, 0x94c3001c, 0x94c2001a, + 0x94c5001e, 0x00832021, 0x24420001, 0xa4c2001a, 0x3042ffff, 0x14450002, + 0xa4c40018, 0xa4c0001a, 0x03e00008, 0x00000000, 0x8f820010, 0x3c030006, + 0x00021140, 0x00431025, 0xaf420030, 0x00000000, 0x00000000, 0x00000000, + 0x27430400, 0x8f420000, 0x30420010, 0x1040fffd, 0x00000000, 0xaf800010, + 0xaf830018, 0x03e00008, 0x00000000, 0x27bdffe8, 0xafb00010, 0x3c100800, + 0x261058c0, 0x3c05000a, 0x02002021, 0x03452821, 0xafbf0014, 0x0e0015b0, + 0x2406000a, 0x96020002, 0x9603001e, 0x3042000f, 0x24420003, 0x00431804, + 0x24027fff, 0x0043102b, 0xaf830014, 0x10400004, 0x00000000, 0x0000000d, + 0x00000000, 0x24000043, 0x0e00155a, 0x00000000, 0x8fbf0014, 0x8fb00010, + 0x03e00008, 0x27bd0018, 0x10c00007, 0x00000000, 0x8ca20000, 0x24c6ffff, + 0x24a50004, 0xac820000, 0x14c0fffb, 0x24840004, 0x03e00008, 0x00000000, + 0x0a0015c1, 0x00a01021, 0xac860000, 0x00000000, 0x00000000, 0x24840004, + 0x00a01021, 0x1440fffa, 0x24a5ffff, 0x03e00008, 0x00000000, 0x3c036000, + 0x8c642b7c, 0x3c036010, 0x8c6553fc, 0x00041582, 0x00042302, 0x308403ff, + 0x00052d82, 0x00441026, 0x0002102b, 0x0005282b, 0x00451025, 0x1440000d, + 0x3c020050, 0x34420004, 0xaf400038, 0xaf40003c, 0xaf420030, 0x00000000, + 0x00000000, 0x8f420000, 0x30420020, 0x1040fffd, 0x3c020020, 0xaf420030, + 0x0000000d, 0x03e00008, 0x00000000, 0x3c020050, 0x34420004, 0xaf440038, + 0xaf45003c, 0xaf420030, 0x00000000, 0x00000000, 0x8f420000, 0x30420020, + 0x1040fffd, 0x3c020020, 0xaf420030, 0x03e00008, 0x00000000, 0x00000000}; + +static u32 bnx2_COM_b06FwData[(0x0/4) + 1] = { 0x0 }; +static u32 bnx2_COM_b06FwRodata[(0x58/4) + 1] = { + 0x08002428, 0x0800245c, 0x0800245c, 0x0800245c, 0x0800245c, 0x0800245c, + 0x08002380, 0x0800245c, 0x080023e4, 0x0800245c, 0x0800231c, 0x0800245c, + 0x0800245c, 0x0800245c, 0x08002328, 0x00000000, 0x08003240, 0x08003270, + 0x080032a0, 0x080032d0, 0x08003300, 0x00000000, 0x00000000 }; +static u32 bnx2_COM_b06FwBss[(0x88/4) + 1] = { 0x0 }; +static u32 bnx2_COM_b06FwSbss[(0x1c/4) + 1] = { 0x0 }; + +static int bnx2_RXP_b06FwReleaseMajor = 0x1; +static int bnx2_RXP_b06FwReleaseMinor = 0x0; +static int bnx2_RXP_b06FwReleaseFix = 0x0; +static u32 bnx2_RXP_b06FwStartAddr = 0x08003184; +static u32 bnx2_RXP_b06FwTextAddr = 0x08000000; +static int bnx2_RXP_b06FwTextLen = 0x588c; +static u32 bnx2_RXP_b06FwDataAddr = 0x080058e0; +static int bnx2_RXP_b06FwDataLen = 0x0; +static u32 bnx2_RXP_b06FwRodataAddr = 0x08005890; +static int bnx2_RXP_b06FwRodataLen = 0x28; +static u32 bnx2_RXP_b06FwBssAddr = 0x08005900; +static int bnx2_RXP_b06FwBssLen = 0x13a4; +static u32 bnx2_RXP_b06FwSbssAddr = 0x080058e0; +static int bnx2_RXP_b06FwSbssLen = 0x1c; +static u32 bnx2_RXP_b06FwText[(0x588c/4) + 1] = { + 0x0a000c61, 0x00000000, 0x00000000, 0x0000000d, 0x72787020, 0x322e362e, + 0x31000000, 0x02060103, 0x00000000, 0x0000000d, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x10000003, 0x00000000, 0x0000000d, 0x0000000d, + 0x3c020800, 0x244258e0, 0x3c030800, 0x24636ca4, 0xac400000, 0x0043202b, + 0x1480fffd, 0x24420004, 0x3c1d0800, 0x37bd7ffc, 0x03a0f021, 0x3c100800, + 0x26103184, 0x3c1c0800, 0x279c58e0, 0x0e00104a, 0x00000000, 0x0000000d, + 0x27bdffe8, 0xafb00010, 0xafbf0014, 0x0e000f1d, 0x00808021, 0x1440000d, + 0x00000000, 0x8f820010, 0x10400005, 0x00000000, 0x9743011c, 0x9742011e, + 0x0a000c89, 0x00021400, 0x9743011e, 0x9742011c, 0x00021400, 0x00621825, + 0xaf830004, 0x8f840008, 0x3c020020, 0x34424000, 0x00821824, 0x54620004, + 0x3c020020, 0x8f820014, 0x0a000c9a, 0x34421000, 0x34428000, 0x00821824, + 0x14620004, 0x00000000, 0x8f820014, 0x34428000, 0xaf820014, 0x8f820008, + 0x9743010c, 0x00403021, 0x30421000, 0x10400010, 0x3069ffff, 0x30c20020, + 0x1440000e, 0x24070005, 0x3c021000, 0x00c21024, 0x10400009, 0x3c030dff, + 0x3463ffff, 0x3c020e00, 0x00c21024, 0x0062182b, 0x50600004, 0x24070001, + 0x0a000cb2, 0x3c020800, 0x24070001, 0x3c020800, 0x8c430034, 0x1460001d, + 0x00405821, 0x8f820014, 0x30424000, 0x1440001a, 0x3c020001, 0x3c021f01, + 0x00c24024, 0x3c031000, 0x15030015, 0x3c020001, 0x31220200, 0x14400012, + 0x3c020001, 0x9744010e, 0x24020003, 0xa342018b, 0x97850016, 0x24020002, + 0x34e30002, 0xaf400180, 0xa742018c, 0xa7430188, 0x24840004, 0x30a5bfff, + 0xa744018e, 0xa74501a6, 0xaf4801b8, 0x0a000f19, 0x00001021, 0x3c020001, + 0x00c21024, 0x1040002f, 0x00000000, 0x9742010e, 0x3c038000, 0x3046ffff, + 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020003, 0xa342018b, 0x9784000a, + 0x8f850004, 0x8f870014, 0x24020080, 0x24030002, 0xaf420180, 0x24020003, + 0xa743018c, 0xa746018e, 0xa7420188, 0x30e28000, 0xa7440190, 0x1040000c, + 0xaf4501a8, 0x93420116, 0x304200fc, 0x005a1021, 0x24424004, 0x8c430000, + 0x3063ffff, 0x14600004, 0x3c02ffff, 0x34427fff, 0x00e21024, 0xaf820014, + 0x97820016, 0x9743010c, 0x8f440104, 0x3042bfff, 0x00031c00, 0x3084ffff, + 0x00641825, 0xa74201a6, 0xaf4301ac, 0x3c021000, 0xaf4201b8, 0x0a000f19, + 0x00001021, 0x8f820014, 0x30434000, 0x10600016, 0x00404021, 0x3c020f00, + 0x00c21024, 0x14400012, 0x00000000, 0x93420116, 0x34424000, 0x03421821, + 0x94650002, 0x2ca21389, 0x1040000b, 0x3c020800, 0x24425900, 0x00051942, + 0x00031880, 0x00621821, 0x30a5001f, 0x8c640000, 0x24020001, 0x00a21004, + 0x00822024, 0x02048025, 0x12000030, 0x3c021000, 0x9742010e, 0x34e80002, + 0x3c038000, 0x24420004, 0x3046ffff, 0x8f4201b8, 0x00431024, 0x1440fffd, + 0x24020003, 0xa342018b, 0x9784000a, 0x8f850004, 0x8f870014, 0x24020180, + 0x24030002, 0xaf420180, 0xa743018c, 0xa746018e, 0xa7480188, 0x30e28000, + 0xa7440190, 0x1040000c, 0xaf4501a8, 0x93420116, 0x304200fc, 0x005a1021, + 0x24424004, 0x8c430000, 0x3063ffff, 0x14600004, 0x3c02ffff, 0x34427fff, + 0x00e21024, 0xaf820014, 0x97820016, 0x9743010c, 0x8f440104, 0x3042bfff, + 0x00031c00, 0x3084ffff, 0x00641825, 0xa74201a6, 0xaf4301ac, 0x3c021000, + 0xaf4201b8, 0x0a000f19, 0x00001021, 0x00c21024, 0x104000c0, 0x3c020800, + 0x8c430030, 0x10600037, 0x31024000, 0x10400035, 0x3c030f00, 0x00c31824, + 0x3c020100, 0x0043102b, 0x14400031, 0x3c030800, 0x9742010e, 0x34e80002, + 0x3c038000, 0x24420004, 0x3046ffff, 0x8f4201b8, 0x00431024, 0x1440fffd, + 0x24020003, 0xa342018b, 0x9784000a, 0x8f850004, 0x8f870014, 0x24020080, + 0x24030002, 0xaf420180, 0xa743018c, 0xa746018e, 0xa7480188, 0x30e28000, + 0xa7440190, 0x1040000c, 0xaf4501a8, 0x93420116, 0x304200fc, 0x005a1021, + 0x24424004, 0x8c430000, 0x3063ffff, 0x14600004, 0x3c02ffff, 0x34427fff, + 0x00e21024, 0xaf820014, 0x97820016, 0x9743010c, 0x8f440104, 0x3042bfff, + 0x00031c00, 0x3084ffff, 0x00641825, 0xa74201a6, 0xaf4301ac, 0x3c021000, + 0xaf4201b8, 0x0a000f19, 0x00001021, 0x3c030800, 0x8c620024, 0x30420008, + 0x10400035, 0x34ea0002, 0x3c020f00, 0x00c21024, 0x14400032, 0x8d620034, + 0x31220200, 0x1040002f, 0x8d620034, 0x9742010e, 0x30e8fffb, 0x3c038000, + 0x24420004, 0x3046ffff, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020003, + 0xa342018b, 0x9784000a, 0x8f850004, 0x8f870014, 0x24020180, 0x24030002, + 0xaf420180, 0xa743018c, 0xa746018e, 0xa7480188, 0x30e28000, 0xa7440190, + 0x1040000c, 0xaf4501a8, 0x93420116, 0x304200fc, 0x005a1021, 0x24424004, + 0x8c430000, 0x3063ffff, 0x14600004, 0x3c02ffff, 0x34427fff, 0x00e21024, + 0xaf820014, 0x97820016, 0x9743010c, 0x8f440104, 0x3042bfff, 0x00031c00, + 0x3084ffff, 0x00641825, 0xa74201a6, 0xaf4301ac, 0x3c021000, 0xaf4201b8, + 0x8d620034, 0x8f860008, 0x10400012, 0x30c20100, 0x10400010, 0x3c020f00, + 0x00c21024, 0x3c030200, 0x1043000c, 0x3c020800, 0x8c430038, 0x8f840004, + 0x3c020800, 0x2442003c, 0x2463ffff, 0x00832024, 0x00822021, 0x90830000, + 0x24630004, 0x0a000de1, 0x000329c0, 0x00000000, 0x00061602, 0x3042000f, + 0x000229c0, 0x3c04fc00, 0x00441021, 0x3c030300, 0x0062182b, 0x50600001, + 0x24050800, 0x9742010e, 0x3148ffff, 0x3c038000, 0x24420004, 0x3046ffff, + 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020003, 0xa342018b, 0x9783000a, + 0x8f840004, 0x8f870014, 0x24020002, 0xaf450180, 0xa742018c, 0xa746018e, + 0xa7480188, 0x30e28000, 0xa7430190, 0x1040000c, 0xaf4401a8, 0x93420116, + 0x304200fc, 0x005a1021, 0x24424004, 0x8c430000, 0x3063ffff, 0x14600004, + 0x3c02ffff, 0x34427fff, 0x00e21024, 0xaf820014, 0x97820016, 0x9743010c, + 0x8f440104, 0x3042bfff, 0x00031c00, 0x3084ffff, 0x00641825, 0xa74201a6, + 0xaf4301ac, 0x3c021000, 0xaf4201b8, 0x0a000f19, 0x00001021, 0x8f424000, + 0x30420100, 0x104000d5, 0x3c020800, 0x8c440024, 0x24030001, 0x1483002f, + 0x00405021, 0x9742010e, 0x34e70002, 0x3c038000, 0x24420004, 0x3045ffff, + 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020003, 0xa342018b, 0x9783000a, + 0x8f840004, 0x8f860014, 0x24020002, 0xaf400180, 0xa742018c, 0xa745018e, + 0xa7470188, 0x30c28000, 0xa7430190, 0x1040000c, 0xaf4401a8, 0x93420116, + 0x304200fc, 0x005a1021, 0x24424004, 0x8c430000, 0x3063ffff, 0x14600004, + 0x3c02ffff, 0x34427fff, 0x00c21024, 0xaf820014, 0x97820016, 0x9743010c, + 0x8f440104, 0x3042bfff, 0x00031c00, 0x3084ffff, 0x00641825, 0xa74201a6, + 0xaf4301ac, 0x3c021000, 0xaf4201b8, 0x0a000f19, 0x00001021, 0x30820001, + 0x1040002e, 0x30eb0004, 0x9742010e, 0x30e9fffb, 0x3c038000, 0x24420004, + 0x3045ffff, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020003, 0xa342018b, + 0x9783000a, 0x8f840004, 0x8f860014, 0x24020002, 0xaf400180, 0xa742018c, + 0xa745018e, 0xa7470188, 0x30c28000, 0xa7430190, 0x1040000c, 0xaf4401a8, + 0x93420116, 0x304200fc, 0x005a1021, 0x24424004, 0x8c430000, 0x3063ffff, + 0x14600004, 0x3c02ffff, 0x34427fff, 0x00c21024, 0xaf820014, 0x97820016, + 0x9743010c, 0x8f440104, 0x3042bfff, 0x00031c00, 0x3084ffff, 0x00641825, + 0xa74201a6, 0xaf4301ac, 0x3c021000, 0xaf4201b8, 0x3127ffff, 0x8d420024, + 0x30420004, 0x10400030, 0x8d420024, 0x9742010e, 0x30e9fffb, 0x3c038000, + 0x24420004, 0x3046ffff, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020003, + 0xa342018b, 0x9784000a, 0x8f850004, 0x8f880014, 0x24020100, 0x24030002, + 0xaf420180, 0xa743018c, 0xa746018e, 0xa7470188, 0x31028000, 0xa7440190, + 0x1040000c, 0xaf4501a8, 0x93420116, 0x304200fc, 0x005a1021, 0x24424004, + 0x8c430000, 0x3063ffff, 0x14600004, 0x3c02ffff, 0x34427fff, 0x01021024, + 0xaf820014, 0x97820016, 0x9743010c, 0x8f440104, 0x3042bfff, 0x00031c00, + 0x3084ffff, 0x00641825, 0xa74201a6, 0xaf4301ac, 0x3c021000, 0xaf4201b8, + 0x3127ffff, 0x8d420024, 0x30420008, 0x1040002d, 0x00000000, 0x9742010e, + 0x3c038000, 0x24420004, 0x3046ffff, 0x8f4201b8, 0x00431024, 0x1440fffd, + 0x24020003, 0xa342018b, 0x9784000a, 0x8f850004, 0x8f880014, 0x24020180, + 0x24030002, 0xaf420180, 0xa743018c, 0xa746018e, 0xa7470188, 0x31028000, + 0xa7440190, 0x1040000c, 0xaf4501a8, 0x93420116, 0x304200fc, 0x005a1021, + 0x24424004, 0x8c430000, 0x3063ffff, 0x14600004, 0x3c02ffff, 0x34427fff, + 0x01021024, 0xaf820014, 0x97820016, 0x9743010c, 0x8f440104, 0x3042bfff, + 0x00031c00, 0x3084ffff, 0x00641825, 0xa74201a6, 0xaf4301ac, 0x3c021000, + 0xaf4201b8, 0x15600041, 0x00001021, 0x27440180, 0x3c038000, 0x8f4201b8, + 0x00431024, 0x1440fffd, 0x24022000, 0x24030002, 0xa4820008, 0xa083000b, + 0xa4800010, 0x3c021000, 0xaf4201b8, 0x0a000f19, 0x00001021, 0x3c030800, + 0x8c620024, 0x30420001, 0x1040002e, 0x00001021, 0x9742010e, 0x34e70002, + 0x3c038000, 0x24420004, 0x3045ffff, 0x8f4201b8, 0x00431024, 0x1440fffd, + 0x24020003, 0xa342018b, 0x9783000a, 0x8f840004, 0x8f860014, 0x24020002, + 0xaf400180, 0xa742018c, 0xa745018e, 0xa7470188, 0x30c28000, 0xa7430190, + 0x1040000c, 0xaf4401a8, 0x93420116, 0x304200fc, 0x005a1021, 0x24424004, + 0x8c430000, 0x3063ffff, 0x14600004, 0x3c02ffff, 0x34427fff, 0x00c21024, + 0xaf820014, 0x97820016, 0x9743010c, 0x8f440104, 0x3042bfff, 0x00031c00, + 0x3084ffff, 0x00641825, 0xa74201a6, 0xaf4301ac, 0x3c021000, 0xaf4201b8, + 0x00001021, 0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, 0x8f4b0070, + 0x93420112, 0x8f840008, 0x00022882, 0x30820100, 0x14400003, 0x24a30003, + 0x03e00008, 0x00001021, 0x30824000, 0x10400010, 0x27424000, 0x00031880, + 0x00431021, 0x8c470000, 0x24a30004, 0x00031880, 0x27424000, 0x00431021, + 0x8c490000, 0x93430116, 0x27424000, 0x306300fc, 0x00431021, 0x8c4a0000, + 0x0a000f45, 0x3c030800, 0x30822000, 0x1040ffea, 0x00031880, 0x27424000, + 0x00431021, 0x8c470000, 0x24a30004, 0x00031880, 0x27424000, 0x00431021, + 0x8c490000, 0x00005021, 0x3c030800, 0x24680100, 0x00071602, 0x00021080, + 0x00481021, 0x8c460000, 0x00071b82, 0x306303fc, 0x01031821, 0x8c640400, + 0x00071182, 0x304203fc, 0x01021021, 0x8c450800, 0x30e300ff, 0x00031880, + 0x01031821, 0x00091602, 0x00021080, 0x01021021, 0x00c43026, 0x8c640c00, + 0x8c431000, 0x00c53026, 0x00091382, 0x304203fc, 0x01021021, 0x8c451400, + 0x312200ff, 0x00021080, 0x01021021, 0x00c43026, 0x00c33026, 0x00091982, + 0x306303fc, 0x01031821, 0x8c641800, 0x8c431c00, 0x00c53026, 0x00c43026, + 0x11400015, 0x00c33026, 0x000a1602, 0x00021080, 0x01021021, 0x8c432000, + 0x000a1382, 0x304203fc, 0x01021021, 0x8c452400, 0x314200ff, 0x00021080, + 0x01021021, 0x00c33026, 0x000a1982, 0x306303fc, 0x01031821, 0x8c642800, + 0x8c432c00, 0x00c53026, 0x00c43026, 0x00c33026, 0x8f430070, 0x3c050800, + 0x8ca43100, 0x2c820020, 0x10400008, 0x006b5823, 0x3c020800, 0x24423104, + 0x00041880, 0x00621821, 0x24820001, 0xac6b0000, 0xaca23100, 0xaf860004, + 0x03e00008, 0x24020001, 0x27bdffe8, 0xafbf0010, 0x8f460128, 0x8f840010, + 0xaf460020, 0x8f450104, 0x8f420100, 0x24030800, 0xaf850008, 0xaf820014, + 0xaf4301b8, 0x1080000a, 0x3c020800, 0x8c430034, 0x10600007, 0x30a22000, + 0x10400005, 0x34a30100, 0x8f82000c, 0xaf830008, 0x24420001, 0xaf82000c, + 0x3c020800, 0x8c4300c0, 0x10600006, 0x3c030800, 0x8c6200c4, 0x24040001, + 0x24420001, 0x0a000fd5, 0xac6200c4, 0x8f820008, 0x3c030010, 0x00431024, + 0x14400009, 0x3c02001f, 0x3c030800, 0x8c620020, 0x00002021, 0x24420001, + 0x0e000c78, 0xac620020, 0x0a000fd5, 0x00402021, 0x3442ff00, 0x14c20009, + 0x2403bfff, 0x3c030800, 0x8c620020, 0x24040001, 0x24420001, 0x0e000c78, + 0xac620020, 0x0a000fd5, 0x00402021, 0x8f820014, 0x00431024, 0x14400006, + 0x00000000, 0xaf400048, 0x0e0011a9, 0xaf400040, 0x0a000fd5, 0x00402021, + 0x0e001563, 0x00000000, 0x00402021, 0x10800005, 0x3c024000, 0x8f430124, + 0x3c026020, 0xac430014, 0x3c024000, 0xaf420138, 0x00000000, 0x8fbf0010, + 0x03e00008, 0x27bd0018, 0x27bdffe0, 0xafbf0018, 0xafb10014, 0xafb00010, + 0x8f420140, 0xaf420020, 0x8f430148, 0x3c027000, 0x00621824, 0x3c023000, + 0x10620021, 0x0043102b, 0x14400006, 0x3c024000, 0x3c022000, 0x10620009, + 0x3c024000, 0x0a001040, 0x00000000, 0x10620045, 0x3c025000, 0x10620047, + 0x3c024000, 0x0a001040, 0x00000000, 0x27440180, 0x3c038000, 0x8f4201b8, + 0x00431024, 0x1440fffd, 0x00000000, 0x8f420148, 0x24030002, 0xa083000b, + 0x00021402, 0xa4820008, 0x8f430148, 0xa4830010, 0x8f420144, 0x3c031000, + 0xac820024, 0xaf4301b8, 0x0a001040, 0x3c024000, 0x8f420148, 0x24030002, + 0x3044ffff, 0x00021402, 0x305000ff, 0x1203000c, 0x27510180, 0x2a020003, + 0x10400005, 0x24020003, 0x0600001d, 0x36053000, 0x0a001027, 0x3c038000, + 0x12020007, 0x00000000, 0x0a001034, 0x00000000, 0x0e00112c, 0x00000000, + 0x0a001025, 0x00402021, 0x0e00113e, 0x00000000, 0x00402021, 0x36053000, + 0x3c038000, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020002, 0xa6250008, + 0xa222000b, 0xa6240010, 0x8f420144, 0x3c031000, 0xae220024, 0xaf4301b8, + 0x0a001040, 0x3c024000, 0x0000000d, 0x00000000, 0x240002bf, 0x0a001040, + 0x3c024000, 0x0e001441, 0x00000000, 0x0a001040, 0x3c024000, 0x0e0015ea, + 0x00000000, 0x3c024000, 0xaf420178, 0x00000000, 0x8fbf0018, 0x8fb10014, + 0x8fb00010, 0x03e00008, 0x27bd0020, 0x24020800, 0x03e00008, 0xaf4201b8, + 0x27bdffe8, 0x3c04600c, 0xafbf0014, 0xafb00010, 0x8c825000, 0x3c1a8000, + 0x2403ff7f, 0x3c106000, 0x00431024, 0x3442380c, 0x24030003, 0xac825000, + 0x3c020008, 0xaf430008, 0x8e040808, 0x0342d825, 0x8e020808, 0x3c030800, + 0xac600020, 0x3084fff0, 0x2c840001, 0x3042fff0, 0x38420010, 0x2c420001, + 0xaf840010, 0xaf820000, 0x0e00160c, 0x00000000, 0x0e001561, 0x00000000, + 0x3c020400, 0x3442000c, 0x3c03ffff, 0x34630806, 0xae021948, 0xae03194c, + 0x8e021980, 0x34420200, 0xae021980, 0x8f500000, 0x32020003, 0x1040fffd, + 0x32020001, 0x10400004, 0x32020002, 0x0e000f92, 0x00000000, 0x32020002, + 0x1040fff6, 0x00000000, 0x0e000fe0, 0x00000000, 0x0a001071, 0x00000000, + 0x27bdffe8, 0x3c04600c, 0xafbf0014, 0xafb00010, 0x8c825000, 0x3c1a8000, + 0x2403ff7f, 0x3c106000, 0x00431024, 0x3442380c, 0x24030003, 0xac825000, + 0x3c020008, 0xaf430008, 0x8e040808, 0x0342d825, 0x8e020808, 0x3c030800, + 0xac600020, 0x3084fff0, 0x2c840001, 0x3042fff0, 0x38420010, 0x2c420001, + 0xaf840010, 0xaf820000, 0x0e00160c, 0x00000000, 0x0e001561, 0x00000000, + 0x3c020400, 0x3442000c, 0x3c03ffff, 0x34630806, 0xae021948, 0xae03194c, + 0x8e021980, 0x8fbf0014, 0x34420200, 0xae021980, 0x8fb00010, 0x03e00008, + 0x27bd0018, 0x00804821, 0x30a5ffff, 0x30c6ffff, 0x30e7ffff, 0x3c038000, + 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020003, 0xa342018b, 0x9783000a, + 0x8f840004, 0x8f880014, 0xaf490180, 0xa745018c, 0xa746018e, 0xa7470188, + 0x31028000, 0xa7430190, 0x1040000c, 0xaf4401a8, 0x93420116, 0x304200fc, + 0x005a1021, 0x24424004, 0x8c430000, 0x3063ffff, 0x14600004, 0x3c02ffff, + 0x34427fff, 0x01021024, 0xaf820014, 0x97820016, 0x9743010c, 0x8f440104, + 0x3042bfff, 0x00031c00, 0x3084ffff, 0x00641825, 0xa74201a6, 0xaf4301ac, + 0x3c021000, 0xaf4201b8, 0x03e00008, 0x00000000, 0x27440180, 0x3c038000, + 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24022000, 0x24030002, 0xa4820008, + 0xa083000b, 0xa4800010, 0x3c021000, 0xaf4201b8, 0x03e00008, 0x00000000, + 0x27440180, 0x3c038000, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x00000000, + 0x8f420148, 0x24030002, 0xa083000b, 0x00021402, 0xa4820008, 0x8f430148, + 0xa4830010, 0x8f420144, 0x3c031000, 0xac820024, 0x03e00008, 0xaf4301b8, + 0x27bdffe0, 0xafbf0018, 0xafb10014, 0xafb00010, 0x8f420148, 0x24030002, + 0x3044ffff, 0x00021402, 0x305000ff, 0x1203000c, 0x27510180, 0x2a020003, + 0x10400005, 0x24020003, 0x0600001d, 0x36053000, 0x0a001117, 0x3c038000, + 0x12020007, 0x00000000, 0x0a001124, 0x00000000, 0x0e00112c, 0x00000000, + 0x0a001115, 0x00402021, 0x0e00113e, 0x00000000, 0x00402021, 0x36053000, + 0x3c038000, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020002, 0xa6250008, + 0xa222000b, 0xa6240010, 0x8f420144, 0x3c031000, 0xae220024, 0xaf4301b8, + 0x0a001128, 0x8fbf0018, 0x0000000d, 0x00000000, 0x240002bf, 0x8fbf0018, + 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020, 0x3084ffff, 0x2c821389, + 0x1040000d, 0x00001021, 0x3c030800, 0x24635900, 0x00042942, 0x00052880, + 0x00a32821, 0x3086001f, 0x8ca40000, 0x24030001, 0x00c31804, 0x00832025, + 0x03e00008, 0xaca40000, 0x03e00008, 0x24020091, 0x3084ffff, 0x2c821389, + 0x1040000e, 0x00001021, 0x3c030800, 0x24635900, 0x00042942, 0x00052880, + 0x00a32821, 0x3086001f, 0x24030001, 0x8ca40000, 0x00c31804, 0x00031827, + 0x00832024, 0x03e00008, 0xaca40000, 0x03e00008, 0x24020091, 0x9482000c, + 0x24870014, 0x00021302, 0x00021080, 0x00824021, 0x00e8182b, 0x1060004f, + 0x00000000, 0x90e30000, 0x2c620009, 0x10400047, 0x3c020800, 0x24425890, + 0x00031880, 0x00621821, 0x8c640000, 0x00800008, 0x00000000, 0x0a0011a4, + 0x24e70001, 0x90e30001, 0x2402000a, 0x54620024, 0x01003821, 0x01071023, + 0x2c42000a, 0x54400020, 0x01003821, 0x3c050800, 0x8ca26c98, 0x24e70002, + 0x34420100, 0xaca26c98, 0x90e30000, 0x90e20001, 0x90e40002, 0x90e60003, + 0x24e70004, 0x24a56c98, 0x00031e00, 0x00021400, 0x00621825, 0x00042200, + 0x00641825, 0x00661825, 0xaca30004, 0x90e20000, 0x90e30001, 0x90e40002, + 0x90e60003, 0x24e70004, 0x00021600, 0x00031c00, 0x00431025, 0x00042200, + 0x00441025, 0x00461025, 0x0a0011a4, 0xaca20008, 0x90e30001, 0x24020004, + 0x1062000e, 0x00601021, 0x0a00119e, 0x01001021, 0x90e30001, 0x24020003, + 0x10620008, 0x00601021, 0x0a00119e, 0x01001021, 0x90e30001, 0x24020002, + 0x14620003, 0x01001021, 0x00601021, 0x00e21021, 0x0a0011a4, 0x00403821, + 0x90e20001, 0x0a0011a4, 0x00e23821, 0x01003821, 0x00e8102b, 0x5440ffb4, + 0x90e30000, 0x03e00008, 0x24020001, 0x27bdff90, 0x3c030800, 0xafbf006c, + 0xafbe0068, 0xafb70064, 0xafb60060, 0xafb5005c, 0xafb40058, 0xafb30054, + 0xafb20050, 0xafb1004c, 0xafb00048, 0xac606c98, 0x93620023, 0x30420010, + 0x1440027c, 0x24020001, 0x93420116, 0x93630005, 0x34424000, 0x30630001, + 0x14600005, 0x0342b021, 0x0e0015e0, 0x00000000, 0x0a001436, 0x8fbf006c, + 0x93420112, 0x8f430104, 0x3c040020, 0x34424000, 0x00641824, 0x10600012, + 0x03422821, 0x27450180, 0x3c038000, 0x8f4201b8, 0x00431024, 0x1440fffd, + 0x00000000, 0x8f420128, 0xaca20000, 0x8f640040, 0x24030008, 0x240240c1, + 0xa4a20008, 0x24020002, 0xa0a2000b, 0x3c021000, 0x0a0011f1, 0xa0a3000a, + 0x8f420104, 0x3c030040, 0x00431024, 0x1040001d, 0x3c038000, 0x27450180, + 0x8f4201b8, 0x00431024, 0x1440fffd, 0x00000000, 0x8f420128, 0xaca20000, + 0x8f640040, 0x24030010, 0x240240c1, 0xa4a20008, 0x24020002, 0xa0a3000a, + 0x24030008, 0xa0a2000b, 0x3c021000, 0xa4a30010, 0xa0a00012, 0xa0a00013, + 0xaca00014, 0xaca00024, 0xaca00028, 0xaca0002c, 0xaca40018, 0x0e0015e0, + 0xaf4201b8, 0x0a001436, 0x8fbf006c, 0x8f820000, 0x10400016, 0x00000000, + 0x8f420104, 0x3c030001, 0x00431024, 0x10400011, 0x00000000, 0x8ca3000c, + 0x8f620030, 0x1462022d, 0x24020001, 0x8ca30010, 0x8f62002c, 0x14620229, + 0x24020001, 0x9763003a, 0x96c20000, 0x14430225, 0x24020001, 0x97630038, + 0x96c20002, 0x14430221, 0x24020001, 0xaf400048, 0xaf400054, 0xaf400040, + 0x8f740040, 0x8f650048, 0x00b43023, 0x04c10004, 0x00000000, 0x0000000d, + 0x00000000, 0x240001af, 0x9742011a, 0x3052ffff, 0x12400004, 0x8ed30004, + 0x02721021, 0x0a001228, 0x2451ffff, 0x02608821, 0x92d7000d, 0xa7a00020, + 0xa3a0001a, 0xafa00028, 0x9362003f, 0x32e30004, 0x1060003a, 0x305000ff, + 0x24040012, 0x16040006, 0x24020001, 0x3c040800, 0x8c830028, 0x24630001, + 0x0a001328, 0xac830028, 0x8f620044, 0x16620010, 0x27a60010, 0x27450180, + 0x3c038000, 0x2402001a, 0xa7a20020, 0x24020020, 0xafb40028, 0xa3b00022, + 0xa3a40023, 0xa3a2001a, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x00000000, + 0x0a00130d, 0x00000000, 0x8f620044, 0x02621023, 0x0440001a, 0x02651023, + 0x044100d9, 0x24020001, 0x3c020800, 0x8c4300d8, 0x10600004, 0x24020001, + 0xa7a20020, 0x0a00125e, 0xafb40028, 0x2402001a, 0xa7a20020, 0x24020020, + 0xafb40028, 0xa3b00022, 0xa3a40023, 0xa3a2001a, 0x27a60010, 0x27450180, + 0x3c038000, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x00000000, 0x0a00130d, + 0x00000000, 0x0a001328, 0x24020001, 0x0293f023, 0x1bc00016, 0x025e102a, + 0x54400007, 0x32f700fe, 0x57d2000f, 0x027e9821, 0x32e20001, 0x5440000c, + 0x027e9821, 0x32f700fe, 0x0240f021, 0x3c040800, 0x8c8300c8, 0x00009021, + 0x24020001, 0xa7a20020, 0xafb40028, 0x24630001, 0x0a001282, 0xac8300c8, + 0x025e1023, 0x0a001282, 0x3052ffff, 0x0000f021, 0x24a2ffff, 0x02221823, + 0x1860001f, 0x0072102a, 0x54400019, 0x00a08821, 0x97a20020, 0x3c040800, + 0x8c8300cc, 0xafb40028, 0x34420001, 0x24630001, 0xa7a20020, 0x02741026, + 0x2c420001, 0xac8300cc, 0x2cc30001, 0x00431024, 0x1440000a, 0x02401821, + 0x27a60010, 0x27450180, 0x3c038000, 0x8f4201b8, 0x00431024, 0x1440fffd, + 0x00000000, 0x0a00130d, 0x00000000, 0x00a08821, 0x02431023, 0x3052ffff, + 0x0a0012ae, 0x32f700f6, 0x02741023, 0x18400008, 0x97a20020, 0x3c040800, + 0x8c8300d4, 0xafb30028, 0x34420400, 0x24630001, 0xa7a20020, 0xac8300d4, + 0x32e20002, 0x1040001c, 0x32e20010, 0x8f620044, 0x1662000d, 0x27a60010, + 0x97a20020, 0x27450180, 0x3c038000, 0xafb40028, 0x34420001, 0xa7a20020, + 0x8f4201b8, 0x00431024, 0x1440fffd, 0x00000000, 0x0a00130d, 0x00000000, + 0x97a20020, 0x27450180, 0x3c038000, 0xafb40028, 0x34420001, 0xa7a20020, + 0x8f4201b8, 0x00431024, 0x1440fffd, 0x00000000, 0x0a00130d, 0x00000000, + 0x54400003, 0x8ed50008, 0x0a001328, 0x24020001, 0x8f630054, 0x26a2ffff, + 0x00431023, 0x18400011, 0x27a60010, 0x97a20020, 0x3c040800, 0x8c8300d0, + 0x27450180, 0x3c078000, 0xafb40028, 0x34420001, 0x24630001, 0xa7a20020, + 0xac8300d0, 0x8f4201b8, 0x00471024, 0x1440fffd, 0x00000000, 0x0a00130d, + 0x00000000, 0x32e20020, 0x10400011, 0x00000000, 0x96c20012, 0x0052102b, + 0x10400008, 0x97a20020, 0x96d20012, 0x12400003, 0x02721021, 0x0a0012f2, + 0x2451ffff, 0x02608821, 0x97a20020, 0x93a3001a, 0x34420008, 0x34630004, + 0xa7a20020, 0xa3a3001a, 0x8f420104, 0x3c030080, 0x00431024, 0x10400037, + 0x3a03000a, 0x0e001151, 0x02c02021, 0x24030002, 0x1443002b, 0x3c030800, + 0x27a60010, 0x97a20020, 0x27450180, 0x3c038000, 0xafb40028, 0x34420001, + 0xa7a20020, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x00000000, 0x8f420128, + 0xaca20000, 0x8cc30018, 0x240240c1, 0xa4a20008, 0xaca30018, 0x90c4000a, + 0x24020002, 0xa0a2000b, 0xa0a4000a, 0x94c20010, 0xa4a20010, 0x90c30012, + 0xa0a30012, 0x90c20013, 0xa0a20013, 0x8cc30014, 0xaca30014, 0x8cc20024, + 0xaca20024, 0x8cc30028, 0xaca30028, 0x8cc4002c, 0x24020001, 0x3c031000, + 0xaca4002c, 0xaf4301b8, 0xaf400044, 0xaf400050, 0x0a001436, 0x8fbf006c, + 0x8c626c98, 0x30420100, 0x10400003, 0x24636c98, 0x8c620004, 0xaf62017c, + 0x3a03000a, 0x2c630001, 0x3a02000c, 0x2c420001, 0x00621825, 0x14600003, + 0x2402000e, 0x56020030, 0x00009021, 0x52400008, 0x96c4000e, 0x12400004, + 0xa7b20040, 0x02721021, 0x0a001343, 0x2451ffff, 0x02608821, 0x96c4000e, + 0x93630035, 0x8f62004c, 0x00642004, 0x00952021, 0x00821023, 0x18400015, + 0x00000000, 0x8f620018, 0x02621023, 0x1c400015, 0x97a20020, 0x8f620018, + 0x1662001c, 0x00000000, 0x8f62001c, 0x02a21023, 0x1c40000e, 0x97a20020, + 0x8f62001c, 0x16a20015, 0x00000000, 0x8f620058, 0x00821023, 0x18400011, + 0x97a20020, 0x0a001364, 0xafb10028, 0x8f620058, 0x00821023, 0x0441000b, + 0x97a20020, 0xafb10028, 0xafb30034, 0xafb50038, 0xafa4003c, 0x34420020, + 0x0a00136d, 0xa7a20020, 0x02809821, 0x02608821, 0x8f640058, 0x8f62004c, + 0x02a21023, 0x18400009, 0x00000000, 0x8f620054, 0x02a21023, 0x1c400005, + 0x97a20020, 0xafb10028, 0xafb50024, 0x0a001385, 0x34420040, 0x9742011a, + 0x1440000c, 0x24020014, 0x8f620058, 0x14820009, 0x24020014, 0x8f63004c, + 0x8f620054, 0x10620004, 0x97a20020, 0xafb10028, 0x34420080, 0xa7a20020, + 0x24020014, 0x1202000a, 0x2a020015, 0x10400005, 0x2402000c, 0x12020006, + 0x32e20001, 0x0a0013c6, 0x00000000, 0x24020016, 0x16020035, 0x32e20001, + 0x8f620084, 0x24420001, 0x16a20031, 0x32e20001, 0x24020014, 0x12020021, + 0x2a020015, 0x10400005, 0x2402000c, 0x12020008, 0x32e20001, 0x0a0013c6, + 0x00000000, 0x24020016, 0x1202000c, 0x32e20001, 0x0a0013c6, 0x00000000, + 0x97a30020, 0x2402000e, 0xafb10028, 0xa3b00022, 0xa3a20023, 0xafb50024, + 0x34630054, 0x0a0013c5, 0xa7a30020, 0x97a20020, 0x93a4001a, 0x24030010, + 0xafb10028, 0xa3b00022, 0xa3a30023, 0xafb50024, 0x3442005d, 0x34840002, + 0xa7a20020, 0x0a0013c5, 0xa3a4001a, 0x97a20020, 0x24030012, 0xa3a30023, + 0x93a3001a, 0xafb10028, 0xa3b00022, 0xafb50024, 0x3042fffe, 0x3442005c, + 0x34630002, 0xa7a20020, 0xa3a3001a, 0x32e20001, 0x10400030, 0x2402000c, + 0x12020013, 0x2a02000d, 0x10400005, 0x2402000a, 0x12020008, 0x97a20020, + 0x0a0013f8, 0x32e20009, 0x2402000e, 0x1202001b, 0x32e20009, 0x0a0013f9, + 0x0002102b, 0x93a4001a, 0x24030008, 0xafb10028, 0xa3b00022, 0xa3a30023, + 0x0a0013f4, 0x34420013, 0x97a30020, 0x30620004, 0x14400005, 0x93a2001a, + 0x3463001b, 0xa7a30020, 0x0a0013e7, 0x24030016, 0x3463001b, 0xa7a30020, + 0x24030010, 0xafb10028, 0xa3b00022, 0xa3a30023, 0x34420002, 0x0a0013f7, + 0xa3a2001a, 0x97a20020, 0x93a4001a, 0x24030010, 0xafb10028, 0xa3b00022, + 0xa3a30023, 0x3442001b, 0x34840002, 0xa7a20020, 0xa3a4001a, 0x32e20009, + 0x0002102b, 0x00021023, 0x30420007, 0x12400015, 0x34450003, 0x8f820018, + 0x24030800, 0x27440180, 0x24420001, 0xaf820018, 0x24020004, 0xaf4301b8, + 0xa4850008, 0xa082000b, 0x93430120, 0x00003021, 0x3c021000, 0xa492000e, + 0xac950024, 0xac930028, 0x007e1821, 0xa483000c, 0xaf4201b8, 0x0a001413, + 0x97a20020, 0x24060001, 0x97a20020, 0x10400020, 0x27450180, 0x3c038000, + 0x8f4201b8, 0x00431024, 0x1440fffd, 0x00000000, 0x8f420128, 0xaca20000, + 0x8fa30028, 0x240240c1, 0xa4a20008, 0xaca30018, 0x93a4001a, 0x24020002, + 0xa0a2000b, 0xa0a4000a, 0x97a20020, 0xa4a20010, 0x93a30022, 0xa0a30012, + 0x93a20023, 0xa0a20013, 0x8fa30024, 0xaca30014, 0x8fa20034, 0xaca20024, + 0x8fa30038, 0xaca30028, 0x8fa2003c, 0x3c031000, 0xaca2002c, 0xaf4301b8, + 0x00c01021, 0x8fbf006c, 0x8fbe0068, 0x8fb70064, 0x8fb60060, 0x8fb5005c, + 0x8fb40058, 0x8fb30054, 0x8fb20050, 0x8fb1004c, 0x8fb00048, 0x03e00008, + 0x27bd0070, 0x8f470140, 0x8f460148, 0x3c028000, 0x00c24024, 0x00062c02, + 0x30a300ff, 0x24020019, 0x106200e7, 0x27440180, 0x2862001a, 0x1040001f, + 0x24020008, 0x106200be, 0x28620009, 0x1040000d, 0x24020001, 0x10620046, + 0x28620002, 0x50400005, 0x24020006, 0x1060002e, 0x00a01821, 0x0a00155e, + 0x00000000, 0x1062005b, 0x00a01821, 0x0a00155e, 0x00000000, 0x2402000b, + 0x10620084, 0x2862000c, 0x10400005, 0x24020009, 0x106200bc, 0x00061c02, + 0x0a00155e, 0x00000000, 0x2402000e, 0x106200b7, 0x00061c02, 0x0a00155e, + 0x00000000, 0x28620021, 0x10400009, 0x2862001f, 0x104000c1, 0x2402001b, + 0x106200bf, 0x2402001c, 0x1062009a, 0x00061c02, 0x0a00155e, 0x00000000, + 0x240200c2, 0x106200ca, 0x286200c3, 0x10400005, 0x24020080, 0x1062005a, + 0x00a01821, 0x0a00155e, 0x00000000, 0x240200c9, 0x106200cd, 0x30c5ffff, + 0x0a00155e, 0x00000000, 0x3c058000, 0x8f4201b8, 0x00451024, 0x1440fffd, + 0x24020001, 0xa4830008, 0x24030002, 0xac870000, 0xac800004, 0xa082000a, + 0xa083000b, 0xa4860010, 0x8f430144, 0x3c021000, 0xac800028, 0xac830024, + 0x3c036000, 0xaf4201b8, 0x03e00008, 0xac600808, 0x11000009, 0x00a01821, + 0x3c020800, 0x24030002, 0xa0436c88, 0x24426c88, 0xac470008, 0x8f430144, + 0x03e00008, 0xac430004, 0x3c058000, 0x8f4201b8, 0x00451024, 0x1440fffd, + 0x24020002, 0xac800000, 0xac870004, 0xa4830008, 0xa082000a, 0xa082000b, + 0xa4860010, 0xac800024, 0x8f420144, 0x3c031000, 0xac820028, 0x3c026000, + 0xaf4301b8, 0x03e00008, 0xac400808, 0x3c080800, 0x3c058000, 0x8f4201b8, + 0x00451024, 0x1440fffd, 0x00000000, 0xac870000, 0x91026c88, 0x00002821, + 0x10400002, 0x25076c88, 0x8ce50008, 0xac850004, 0xa4830008, 0x91036c88, + 0x24020002, 0xa082000b, 0xa4860010, 0x34630001, 0xa083000a, 0x8f420144, + 0xac820024, 0x91036c88, 0x10600002, 0x00001021, 0x8ce20004, 0xac820028, + 0x3c021000, 0xaf4201b8, 0x3c026000, 0xa1006c88, 0x03e00008, 0xac400808, + 0x3c058000, 0x8f4201b8, 0x00451024, 0x1440fffd, 0x24020002, 0xa082000b, + 0xa4830008, 0xa4860010, 0x8f420144, 0x3c031000, 0xa4820012, 0x03e00008, + 0xaf4301b8, 0x30c2ffff, 0x14400028, 0x00061c02, 0x93620005, 0x30420004, + 0x14400020, 0x3c029000, 0x34420001, 0x00e21025, 0xaf420020, 0x3c038000, + 0x8f420020, 0x00431024, 0x1440fffd, 0x00000000, 0x93620005, 0x3c038000, + 0x34630001, 0x00e31825, 0x34420004, 0xa3620005, 0xaf430020, 0x93620005, + 0x30420004, 0x14400003, 0x3c038000, 0x0000000d, 0x3c038000, 0x8f4201b8, + 0x00431024, 0x1440fffd, 0x24020005, 0x3c031000, 0xac870000, 0xa082000b, + 0xaf4301b8, 0x0a00150d, 0x00061c02, 0x0000000d, 0x03e00008, 0x00000000, + 0x00061c02, 0x3c058000, 0x8f4201b8, 0x00451024, 0x1440fffd, 0x24020001, + 0xa4830008, 0x24030002, 0xac870000, 0xac800004, 0xa082000a, 0xa083000b, + 0xa4860010, 0x8f430144, 0x3c021000, 0xac800028, 0xac830024, 0x03e00008, + 0xaf4201b8, 0x3c058000, 0x8f4201b8, 0x00451024, 0x1440fffd, 0x24020002, + 0xac800000, 0xac870004, 0xa4830008, 0xa082000a, 0xa082000b, 0xa4860010, + 0xac800024, 0x8f420144, 0x3c031000, 0xac820028, 0x03e00008, 0xaf4301b8, + 0x00061c02, 0x3c058000, 0x8f4201b8, 0x00451024, 0x1440fffd, 0x24020001, + 0xa4830008, 0x24030002, 0xa082000a, 0x3c021000, 0xac870000, 0xac800004, + 0xa083000b, 0xa4860010, 0xac800024, 0xac800028, 0x03e00008, 0xaf4201b8, + 0x00a01821, 0x3c058000, 0x8f4201b8, 0x00451024, 0x1440fffd, 0x24020002, + 0xac870000, 0xac800004, 0xa4830008, 0xa080000a, 0x0a001518, 0xa082000b, + 0x8f440144, 0x3c038000, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020002, + 0x240340c9, 0xaf470180, 0xa342018b, 0x3c021000, 0xa7430188, 0xaf4401a4, + 0xaf4501a8, 0xaf4001ac, 0x03e00008, 0xaf4201b8, 0x0000000d, 0x03e00008, + 0x00000000, 0x03e00008, 0x00000000, 0x8f420100, 0x3042003e, 0x14400011, + 0x24020001, 0xaf400048, 0x8f420100, 0x304207c0, 0x10400005, 0x00000000, + 0xaf40004c, 0xaf400050, 0x03e00008, 0x24020001, 0xaf400054, 0xaf400040, + 0x8f420100, 0x30423800, 0x54400001, 0xaf400044, 0x24020001, 0x03e00008, + 0x00000000, 0x3c038000, 0x8f4201b8, 0x00431024, 0x1440fffd, 0x24020002, + 0x240340c9, 0xaf440180, 0xa342018b, 0x3c021000, 0xa7430188, 0xaf4501a4, + 0xaf4601a8, 0xaf4701ac, 0x03e00008, 0xaf4201b8, 0x3c029000, 0x34420001, + 0x00822025, 0xaf440020, 0x3c038000, 0x8f420020, 0x00431024, 0x1440fffd, + 0x00000000, 0x03e00008, 0x00000000, 0x3c028000, 0x34420001, 0x00822025, + 0x03e00008, 0xaf440020, 0x308600ff, 0x27450180, 0x3c038000, 0x8f4201b8, + 0x00431024, 0x1440fffd, 0x00000000, 0x8f420128, 0xaca20000, 0x8f640040, + 0x24030008, 0x240240c1, 0xa4a20008, 0x24020002, 0xa0a2000b, 0x3c021000, + 0xa0a6000a, 0xa4a30010, 0xa0a00012, 0xa0a00013, 0xaca00014, 0xaca00024, + 0xaca00028, 0xaca0002c, 0xaca40018, 0x03e00008, 0xaf4201b8, 0x24020001, + 0xacc40000, 0x03e00008, 0xa4e50000, 0x24020001, 0xaf400044, 0x03e00008, + 0xaf400050, 0x00803021, 0x27450180, 0x3c038000, 0x8f4201b8, 0x00431024, + 0x1440fffd, 0x00000000, 0x8f420128, 0xaca20000, 0x8cc30018, 0x240240c1, + 0xa4a20008, 0xaca30018, 0x90c4000a, 0x24020002, 0xa0a2000b, 0xa0a4000a, + 0x94c20010, 0xa4a20010, 0x90c30012, 0xa0a30012, 0x90c20013, 0xa0a20013, + 0x8cc30014, 0xaca30014, 0x8cc20024, 0xaca20024, 0x8cc30028, 0xaca30028, + 0x8cc2002c, 0x3c031000, 0xaca2002c, 0x24020001, 0xaf4301b8, 0xaf400044, + 0x03e00008, 0xaf400050, 0x27bdffe8, 0xafbf0010, 0x0e001047, 0x00000000, + 0x00002021, 0x0e000c78, 0xaf400180, 0x8fbf0010, 0x03e00008, 0x27bd0018, + 0x8f460148, 0x27450180, 0x3c038000, 0x00061402, 0x304700ff, 0x8f4201b8, + 0x00431024, 0x1440fffd, 0x00000000, 0x8f440140, 0x00061202, 0x304200ff, + 0x00061c02, 0xaca20004, 0x24020002, 0xa4a30008, 0x30c300ff, 0xa0a2000b, + 0xaca30024, 0x10e0000a, 0xaca40000, 0x28e20004, 0x14400005, 0x24020001, + 0x24020005, 0x54e20005, 0xa0a0000a, 0x24020001, 0x0a001609, 0xa0a2000a, + 0xa0a0000a, 0x3c021000, 0x03e00008, 0xaf4201b8, 0x03e00008, 0x00001021, + 0x10c00007, 0x00000000, 0x8ca20000, 0x24c6ffff, 0x24a50004, 0xac820000, + 0x14c0fffb, 0x24840004, 0x03e00008, 0x00000000, 0x0a00161f, 0x00a01021, + 0xac860000, 0x00000000, 0x00000000, 0x24840004, 0x00a01021, 0x1440fffa, + 0x24a5ffff, 0x03e00008, 0x00000000, 0x00000000 }; + +static u32 bnx2_RXP_b06FwData[(0x0/4) + 1] = { 0x0 }; +static u32 bnx2_RXP_b06FwRodata[(0x28/4) + 1] = { + 0x0800468c, 0x0800458c, 0x08004630, 0x08004648, 0x08004660, 0x08004680, + 0x0800468c, 0x0800468c, 0x08004594, 0x00000000, 0x00000000 }; +static u32 bnx2_RXP_b06FwBss[(0x13a4/4) + 1] = { 0x0 }; +static u32 bnx2_RXP_b06FwSbss[(0x1c/4) + 1] = { 0x0 }; + +static u32 bnx2_rv2p_proc1[] = { + 0x00000008, 0xac000001, 0x0000000c, 0x2f800001, 0x00000010, 0x213f0004, + 0x00000010, 0x20bf002c, 0x00000010, 0x203f0143, 0x00000018, 0x8000fffd, + 0x00000010, 0xb1b8b017, 0x0000000b, 0x2fdf0002, 0x00000000, 0x03d80000, + 0x00000000, 0x2c380000, 0x00000008, 0x2c800000, 0x00000008, 0x2d000000, + 0x00000010, 0x91d40000, 0x00000008, 0x2d800108, 0x00000008, 0x02000002, + 0x00000010, 0x91de0000, 0x0000000f, 0x42e0001c, 0x00000010, 0x91840a08, + 0x00000008, 0x2c8000b0, 0x00000008, 0x2d000008, 0x00000008, 0x2d800150, + 0x00000000, 0x00000000, 0x00000010, 0x91de0000, 0x00000010, 0x2c620002, + 0x00000018, 0x80000012, 0x0000000b, 0x2fdf0002, 0x0000000c, 0x1f800002, + 0x00000000, 0x2c070000, 0x00000018, 0x8000ffe6, 0x00000008, 0x02000002, + 0x0000000f, 0x42e0001c, 0x00000010, 0x91840a08, 0x00000008, 0x2c8000b0, + 0x00000008, 0x2d000008, 0x00000010, 0x91d40000, 0x00000008, 0x2d800108, + 0x00000000, 0x00000000, 0x00000010, 0x91de0000, 0x00000018, 0x80000004, + 0x0000000c, 0x1f800002, 0x00000000, 0x00000000, 0x00000018, 0x8000ffd9, + 0x0000000c, 0x29800002, 0x0000000c, 0x1f800002, 0x00000000, 0x2adf0000, + 0x00000008, 0x2a000005, 0x00000018, 0x8000ffd4, 0x00000008, 0x02240030, + 0x00000018, 0x00040000, 0x00000018, 0x80000015, 0x00000018, 0x80000017, + 0x00000018, 0x8000001b, 0x00000018, 0x8000004c, 0x00000018, 0x8000008c, + 0x00000018, 0x8000000f, 0x00000018, 0x8000000e, 0x00000018, 0x8000000d, + 0x00000018, 0x8000000c, 0x00000018, 0x800000c2, 0x00000018, 0x8000000a, + 0x00000018, 0x80000009, 0x00000018, 0x80000008, 0x00000018, 0x800000fd, + 0x00000018, 0x80000006, 0x00000018, 0x80000005, 0x00000018, 0x800000ff, + 0x00000018, 0x80000104, 0x00000018, 0x80000002, 0x00000018, 0x80000098, + 0x00000018, 0x80000000, 0x0000000c, 0x1f800001, 0x00000000, 0x00000000, + 0x00000018, 0x8000ffba, 0x00000010, 0x91d40000, 0x0000000c, 0x29800001, + 0x0000000c, 0x1f800001, 0x00000008, 0x2a000002, 0x00000018, 0x8000ffb5, + 0x00000010, 0xb1a0b012, 0x0000000b, 0x2fdf0002, 0x00000000, 0x2c200000, + 0x00000008, 0x2c800000, 0x00000008, 0x2d000000, 0x00000010, 0x91d40000, + 0x00000008, 0x2d80011c, 0x00000000, 0x00000000, 0x00000010, 0x91de0000, + 0x0000000f, 0x47600008, 0x0000000f, 0x060e0001, 0x00000010, 0x001f0000, + 0x00000000, 0x0f580000, 0x00000000, 0x0a640000, 0x00000000, 0x0ae50000, + 0x00000000, 0x0b660000, 0x00000000, 0x0d610000, 0x00000018, 0x80000013, + 0x0000000f, 0x47600008, 0x0000000b, 0x2fdf0002, 0x00000008, 0x2c800000, + 0x00000008, 0x2d000000, 0x00000010, 0x91d40000, 0x00000008, 0x2d80011c, + 0x0000000f, 0x060e0001, 0x00000010, 0x001f0000, 0x00000000, 0x0f580000, + 0x00000010, 0x91de0000, 0x00000000, 0x0a640000, 0x00000000, 0x0ae50000, + 0x00000000, 0x0b660000, 0x00000000, 0x0d610000, 0x00000000, 0x02620000, + 0x0000000b, 0x2fdf0002, 0x00000000, 0x309a0000, 0x00000000, 0x31040000, + 0x00000000, 0x0c961800, 0x00000009, 0x0c99ffff, 0x00000004, 0xcc993400, + 0x00000010, 0xb1963202, 0x00000008, 0x0f800000, 0x0000000c, 0x29800001, + 0x00000010, 0x00220002, 0x0000000c, 0x29520001, 0x0000000c, 0x29520000, + 0x00000008, 0x22000001, 0x0000000c, 0x1f800001, 0x00000000, 0x2adf0000, + 0x00000008, 0x2a000003, 0x00000018, 0x8000ff83, 0x00000010, 0xb1a0b01d, + 0x0000000b, 0x2fdf0002, 0x00000000, 0x2c200000, 0x00000008, 0x2c8000b0, + 0x00000008, 0x2d000008, 0x00000010, 0x91d40000, 0x00000008, 0x2d800150, + 0x00000000, 0x00000000, 0x00000010, 0x205f0000, 0x00000008, 0x2c800000, + 0x00000008, 0x2d000000, 0x00000008, 0x2d800108, 0x00000000, 0x00000000, + 0x00000010, 0x91de0000, 0x0000000f, 0x47600008, 0x00000000, 0x060e0000, + 0x00000010, 0x001f0000, 0x00000000, 0x0f580000, 0x00000010, 0x91de0000, + 0x00000000, 0x0a640000, 0x00000000, 0x0ae50000, 0x00000000, 0x0b670000, + 0x00000000, 0x0d620000, 0x00000000, 0x0ce71800, 0x00000009, 0x0c99ffff, + 0x00000004, 0xcc993400, 0x00000010, 0xb1963220, 0x00000008, 0x0f800000, + 0x00000018, 0x8000001e, 0x0000000f, 0x47600008, 0x0000000b, 0x2fdf0002, + 0x00000008, 0x2c8000b0, 0x00000008, 0x2d000008, 0x00000010, 0x91d40000, + 0x00000008, 0x2d80012c, 0x0000000f, 0x060e0001, 0x00000010, 0x001f0000, + 0x00000000, 0x0f580000, 0x00000010, 0x91de0000, 0x00000000, 0x0a640000, + 0x00000000, 0x0ae50000, 0x00000000, 0x0b670000, 0x00000000, 0x0d620000, + 0x00000000, 0x02630000, 0x0000000f, 0x47620010, 0x00000000, 0x0ce71800, + 0x0000000b, 0x2fdf0002, 0x00000000, 0x311a0000, 0x00000000, 0x31840000, + 0x0000000b, 0xc20000ff, 0x00000002, 0x42040000, 0x00000001, 0x31620800, + 0x0000000f, 0x020e0010, 0x00000002, 0x31620800, 0x00000009, 0x0c99ffff, + 0x00000004, 0xcc993400, 0x00000010, 0xb1963202, 0x00000008, 0x0f800000, + 0x0000000c, 0x29800001, 0x0000000c, 0x1f800001, 0x0000000c, 0x61420006, + 0x00000008, 0x22000008, 0x00000000, 0x2adf0000, 0x00000008, 0x2a000004, + 0x00000018, 0x8000ff42, 0x00000008, 0x2c8000b0, 0x00000008, 0x2d000008, + 0x00000010, 0x91a0b008, 0x00000010, 0x91d40000, 0x0000000c, 0x31620018, + 0x00000008, 0x2d800001, 0x00000000, 0x00000000, 0x00000010, 0x91de0000, + 0x00000008, 0xac000001, 0x00000018, 0x8000000e, 0x00000000, 0x0380b000, + 0x0000000b, 0x2fdf0002, 0x00000000, 0x2c004000, 0x00000010, 0x91d40000, + 0x00000008, 0x2d800101, 0x00000000, 0x00000000, 0x00000010, 0x91de0000, + 0x0000000c, 0x31620018, 0x00000008, 0x2d800001, 0x00000000, 0x00000000, + 0x00000010, 0x91de0000, 0x0000000b, 0x2fdf0002, 0x00000000, 0x2c000e00, + 0x0000000c, 0x29800001, 0x0000000c, 0x1f800001, 0x00000008, 0x2a000007, + 0x00000018, 0x8000ff27, 0x00000010, 0xb1a0b016, 0x0000000b, 0x2fdf0002, + 0x00000000, 0x03d80000, 0x00000000, 0x2c200000, 0x00000008, 0x2c8000b0, + 0x00000008, 0x2d000008, 0x00000010, 0x91d40000, 0x00000008, 0x2d800150, + 0x00000000, 0x00000000, 0x00000010, 0x205f0000, 0x00000008, 0x2c800000, + 0x00000008, 0x2d000000, 0x00000008, 0x2d800108, 0x00000008, 0x07000001, + 0x00000010, 0xb5de1c00, 0x00000010, 0x2c620002, 0x00000018, 0x8000000a, + 0x0000000b, 0x2fdf0002, 0x00000000, 0x2c070000, 0x0000000c, 0x1f800001, + 0x00000010, 0x91de0000, 0x00000018, 0x8000ff11, 0x00000008, 0x2c8000b0, + 0x00000008, 0x2d000008, 0x00000010, 0x91d40000, 0x00000008, 0x2d800108, + 0x0000000c, 0x29800001, 0x0000000c, 0x1f800001, 0x00000010, 0x91de0000, + 0x00000000, 0x2adf0000, 0x00000008, 0x2a00000a, 0x00000018, 0x8000ff07, + 0x00000000, 0x82265600, 0x0000000f, 0x47220008, 0x00000009, 0x070e000f, + 0x00000008, 0x070e0008, 0x00000008, 0x02800001, 0x00000007, 0x02851c00, + 0x00000008, 0x82850001, 0x00000000, 0x02840a00, 0x00000007, 0x42851c00, + 0x00000003, 0xc3aa5200, 0x00000000, 0x03b10e00, 0x00000010, 0x001f0000, + 0x0000000f, 0x0f280007, 0x00000007, 0x4b071c00, 0x00000000, 0x00000000, + 0x0000000f, 0x0a960003, 0x00000000, 0x0a955c00, 0x00000000, 0x4a005a00, + 0x00000000, 0x0c960a00, 0x00000009, 0x0c99ffff, 0x00000008, 0x0d00ffff, + 0x00000010, 0xb1963202, 0x00000008, 0x0f800005, 0x00000010, 0x00220020, + 0x00000000, 0x02a70000, 0x00000010, 0xb1850002, 0x00000008, 0x82850200, + 0x00000000, 0x02000000, 0x00000000, 0x03a60000, 0x00000018, 0x8000004e, + 0x00000000, 0x072b0000, 0x00000001, 0x878c1c00, 0x00000000, 0x870e1e00, + 0x00000000, 0x860c1e00, 0x00000000, 0x03061e00, 0x00000010, 0xb18e0003, + 0x00000018, 0x80000047, 0x00000018, 0x8000fffa, 0x00000010, 0x918c0003, + 0x00000010, 0xb1870002, 0x00000018, 0x80000043, 0x00000010, 0x91d40000, + 0x0000000c, 0x29800001, 0x00000000, 0x2a860000, 0x00000000, 0x230c0000, + 0x00000000, 0x2b070000, 0x00000010, 0xb187000e, 0x00000008, 0x2a000008, + 0x00000018, 0x8000003b, 0x00000010, 0x91d40000, 0x00000000, 0x28d18c00, + 0x00000000, 0x2a860000, 0x00000000, 0x230c0000, 0x00000000, 0x2b070000, + 0x00000018, 0x8000fff8, 0x00000010, 0x91d40000, 0x0000000c, 0x29800001, + 0x00000000, 0x2aab0000, 0x00000000, 0xa3265600, 0x00000000, 0x2b000000, + 0x0000000c, 0x1f800001, 0x00000008, 0x2a000008, 0x00000018, 0x8000fec8, + 0x00000010, 0x91d40000, 0x0000000c, 0x29800001, 0x0000000c, 0x1f800001, + 0x00000008, 0x2a000009, 0x00000018, 0x8000fec3, 0x00000010, 0x91d40000, + 0x0000000c, 0x29800001, 0x0000000c, 0x1f800001, 0x00000000, 0x29420000, + 0x00000008, 0x2a000002, 0x00000018, 0x8000febd, 0x00000018, 0x8000febc, + 0x00000010, 0xb1bcb016, 0x0000000b, 0x2fdf0002, 0x00000000, 0x03d80000, + 0x00000000, 0x2c3c0000, 0x00000008, 0x2c8000b0, 0x00000008, 0x2d000008, + 0x00000010, 0x91d40000, 0x00000008, 0x2d800150, 0x00000000, 0x00000000, + 0x00000010, 0x205f0000, 0x00000008, 0x2c800000, 0x00000008, 0x2d000000, + 0x00000008, 0x2d800108, 0x00000008, 0x07000001, 0x00000010, 0xb5de1c00, + 0x00000010, 0x2c620002, 0x00000018, 0x8000000a, 0x0000000b, 0x2fdf0002, + 0x00000000, 0x2c070000, 0x0000000c, 0x1f800000, 0x00000010, 0x91de0000, + 0x00000018, 0x8000fea6, 0x00000008, 0x2c8000b0, 0x00000008, 0x2d000008, + 0x00000010, 0x91d40000, 0x00000008, 0x2d800108, 0x0000000c, 0x29800000, + 0x0000000c, 0x1f800000, 0x00000010, 0x91de0000, 0x00000000, 0x2adf0000, + 0x00000008, 0x2a000006, 0x00000018, 0x8000fe9c, 0x00000008, 0x03050004, + 0x00000006, 0x83040c00, 0x00000008, 0x02850200, 0x00000000, 0x86050c00, + 0x00000001, 0x860c0e00, 0x00000008, 0x02040004, 0x00000000, 0x02041800, + 0x00000000, 0x83871800, 0x00000018, 0x00020000 }; + +static u32 bnx2_rv2p_proc2[] = { + 0x00000000, 0x2a000000, 0x00000010, 0xb1d40000, 0x00000008, 0x02540003, + 0x00000018, 0x00040000, 0x00000018, 0x8000000a, 0x00000018, 0x8000000a, + 0x00000018, 0x8000000e, 0x00000018, 0x80000056, 0x00000018, 0x800001b9, + 0x00000018, 0x800001e1, 0x00000018, 0x8000019b, 0x00000018, 0x800001f9, + 0x00000018, 0x8000019f, 0x00000018, 0x800001a6, 0x00000018, 0x80000000, + 0x0000000c, 0x29800001, 0x00000000, 0x2a000000, 0x0000000c, 0x29800000, + 0x00000010, 0x20530000, 0x00000018, 0x8000ffee, 0x0000000c, 0x29800001, + 0x00000010, 0x91de0000, 0x00000010, 0x001f0000, 0x00000000, 0x2f80aa00, + 0x00000000, 0x2a000000, 0x00000000, 0x0d610000, 0x00000000, 0x03620000, + 0x00000000, 0x2c400000, 0x00000000, 0x02638c00, 0x00000000, 0x26460000, + 0x00000010, 0x00420002, 0x00000008, 0x02040012, 0x00000010, 0xb9060836, + 0x00000000, 0x0f580000, 0x00000000, 0x0a640000, 0x00000000, 0x0ae50000, + 0x00000000, 0x0b660000, 0x00000000, 0x0c000000, 0x00000000, 0x0b800000, + 0x00000010, 0x00420009, 0x00000008, 0x0cc60012, 0x00000008, 0x0f800003, + 0x00000000, 0x00000000, 0x00000010, 0x009f0000, 0x00000008, 0x27110012, + 0x00000000, 0x66900000, 0x00000008, 0xa31b0012, 0x00000018, 0x80000008, + 0x00000000, 0x0cc60000, 0x00000008, 0x0f800003, 0x00000000, 0x00000000, + 0x00000010, 0x009f0000, 0x00000000, 0x27110000, 0x00000000, 0x66900000, + 0x00000000, 0x231b0000, 0x00000010, 0xb197320e, 0x00000000, 0x25960000, + 0x00000000, 0x021b0000, 0x00000010, 0x001f0000, 0x00000008, 0x0f800003, + 0x0000000c, 0x29800000, 0x00000010, 0x20530000, 0x00000000, 0x22c50800, + 0x00000010, 0x009f0000, 0x00000000, 0x27002200, 0x00000000, 0x26802000, + 0x00000000, 0x231b0000, 0x0000000c, 0x69520001, 0x00000018, 0x8000fff3, + 0x00000010, 0x01130002, 0x00000010, 0xb1980003, 0x00000010, 0x001f0000, + 0x00000008, 0x0f800004, 0x00000008, 0x22000003, 0x00000008, 0x2c80000c, + 0x00000008, 0x2d00000c, 0x00000010, 0x009f0000, 0x00000000, 0x25960000, + 0x0000000c, 0x29800000, 0x00000000, 0x32140000, 0x00000000, 0x32950000, + 0x00000000, 0x33160000, 0x00000000, 0x31e32e00, 0x00000008, 0x2d800010, + 0x00000010, 0x20530000, 0x00000018, 0x8000ffac, 0x00000000, 0x23000000, + 0x00000000, 0x25e60000, 0x00000008, 0x2200000b, 0x0000000c, 0x69520000, + 0x0000000c, 0x29800000, 0x00000010, 0x20530000, 0x00000018, 0x8000ffa5, + 0x0000000c, 0x29800001, 0x00000010, 0x91de0000, 0x00000000, 0x2fd50000, + 0x00000010, 0x001f0000, 0x00000000, 0x02700000, 0x00000000, 0x0d620000, + 0x00000000, 0xbb630800, 0x00000000, 0x2a000000, 0x00000009, 0x076000ff, + 0x0000000f, 0x2c0e0007, 0x00000008, 0x2c800000, 0x00000008, 0x2d000064, + 0x00000008, 0x2d80011c, 0x00000009, 0x06420002, 0x0000000c, 0x61420001, + 0x00000000, 0x0f400000, 0x00000000, 0x02d08c00, 0x00000000, 0x23000000, + 0x00000004, 0x826da000, 0x00000000, 0x8304a000, 0x00000000, 0x22c50c00, + 0x00000000, 0x03760000, 0x00000004, 0x83860a00, 0x00000000, 0x83870c00, + 0x00000010, 0x91de0000, 0x00000000, 0x037c0000, 0x00000000, 0x837b0c00, + 0x00000001, 0x83060e00, 0x00000000, 0x83870c00, 0x00000000, 0x82850e00, + 0x00000010, 0xb1860016, 0x0000000f, 0x47610018, 0x00000000, 0x068e0000, + 0x0000000f, 0x47670010, 0x0000000f, 0x47e20010, 0x00000000, 0x870e1e00, + 0x00000010, 0xb70e1a10, 0x00000010, 0x0ce7000e, 0x00000008, 0x22000009, + 0x00000000, 0x286d0000, 0x0000000f, 0x65680010, 0x00000003, 0xf66c9400, + 0x00000010, 0xb972a003, 0x0000000c, 0x73e70019, 0x0000000c, 0x21420004, + 0x00000018, 0x8000023f, 0x00000000, 0x37ed0000, 0x0000000c, 0x73e7001a, + 0x00000010, 0x20530000, 0x00000008, 0x22000008, 0x0000000c, 0x61420004, + 0x00000000, 0x02f60000, 0x00000004, 0x82840a00, 0x00000010, 0xb1840a2b, + 0x00000010, 0x2d67000a, 0x00000010, 0xb96d0804, 0x00000004, 0xb6ed0a00, + 0x00000000, 0x37ed0000, 0x00000018, 0x80000029, 0x0000000c, 0x61420000, + 0x00000000, 0x37040000, 0x00000000, 0x37850000, 0x0000000c, 0x33e7001a, + 0x00000018, 0x80000024, 0x00000010, 0xb96d0809, 0x00000004, 0xb6ed0a00, + 0x00000000, 0x036d0000, 0x00000004, 0xb76e0c00, 0x00000010, 0x91ee0c1f, + 0x0000000c, 0x73e7001a, 0x00000004, 0xb6ef0c00, 0x00000000, 0x37ed0000, + 0x00000018, 0x8000001b, 0x0000000c, 0x61420000, 0x00000010, 0xb7ee0a05, + 0x00000010, 0xb96f0815, 0x00000003, 0xb76e0800, 0x00000004, 0xb7ef0a00, + 0x00000018, 0x80000015, 0x00000010, 0x0ce7000c, 0x00000008, 0x22000009, + 0x00000000, 0x286d0000, 0x0000000f, 0x65680010, 0x00000003, 0xf66c9400, + 0x00000010, 0xb972a003, 0x0000000c, 0x73e70019, 0x0000000c, 0x21420004, + 0x00000018, 0x80000215, 0x00000010, 0x20530000, 0x00000008, 0x22000008, + 0x0000000c, 0x61420004, 0x00000000, 0x37040000, 0x00000000, 0x37850000, + 0x00000000, 0x036d0000, 0x00000003, 0xb8f10c00, 0x00000018, 0x80000004, + 0x00000000, 0x02840000, 0x00000002, 0x21421800, 0x0000000c, 0x61420000, + 0x00000000, 0x286d0000, 0x0000000f, 0x65ed0010, 0x00000009, 0x266dffff, + 0x00000000, 0x23000000, 0x00000010, 0xb1840a3d, 0x00000010, 0x01420002, + 0x00000004, 0xb8f10a00, 0x00000003, 0x83760a00, 0x00000010, 0xb8040c39, + 0x00000010, 0xb7e6080a, 0x00000000, 0x0a640000, 0x00000000, 0x0ae50000, + 0x00000009, 0x0c68ffff, 0x00000009, 0x0b67ffff, 0x00000000, 0x0be60000, + 0x00000000, 0x0c840000, 0x00000010, 0xb197320c, 0x00000008, 0x0f800002, + 0x00000018, 0x8000000a, 0x00000000, 0x0a6a0000, 0x00000000, 0x0aeb0000, + 0x00000000, 0x0c000000, 0x00000009, 0x0b6cffff, 0x00000000, 0x0be90000, + 0x00000000, 0x0c840000, 0x00000010, 0xb1973203, 0x00000008, 0x0f800002, + 0x00000018, 0x80000001, 0x00000010, 0x001f0000, 0x00000000, 0x0c860000, + 0x00000000, 0x06980000, 0x00000008, 0x0f800003, 0x00000000, 0x00000000, + 0x00000010, 0x009f0000, 0x00000010, 0xb1973210, 0x00000000, 0x231b0000, + 0x00000000, 0x02043600, 0x00000003, 0x8384a000, 0x0000000f, 0x65870010, + 0x00000009, 0x2607ffff, 0x00000000, 0x27111a00, 0x00000000, 0x66900000, + 0x0000000c, 0x29000000, 0x00000018, 0x800001de, 0x00000000, 0x06980000, + 0x00000010, 0x20530000, 0x00000000, 0x22c58c00, 0x00000010, 0x001f0000, + 0x00000008, 0x0f800003, 0x00000018, 0x8000fff0, 0x00000000, 0x02043600, + 0x00000000, 0x231b0000, 0x00000003, 0x8384a000, 0x0000000f, 0x65870010, + 0x00000009, 0x2607ffff, 0x00000000, 0x27111a00, 0x00000000, 0x66900000, + 0x0000000c, 0x29000000, 0x00000010, 0x91840a02, 0x00000002, 0x21421800, + 0x00000000, 0x32140000, 0x00000000, 0x32950000, 0x00000005, 0x73e72c00, + 0x00000005, 0x74683000, 0x00000000, 0x33170000, 0x00000018, 0x80000138, + 0x00000010, 0x91c60004, 0x00000008, 0x07000004, 0x00000010, 0xb1c41c02, + 0x00000010, 0x91840a04, 0x00000018, 0x800001c3, 0x00000010, 0x20530000, + 0x00000000, 0x22c58c00, 0x00000010, 0xb1840a8e, 0x0000000c, 0x21420006, + 0x00000010, 0x0ce7001a, 0x0000000f, 0x43680010, 0x00000000, 0x03f30c00, + 0x00000010, 0x91870850, 0x0000000f, 0x46ec0010, 0x00000010, 0xb68d0c4e, + 0x00000000, 0x838d0c00, 0x00000000, 0xa3050800, 0x00000001, 0xa3460e00, + 0x00000000, 0x02048c00, 0x00000010, 0x91840a02, 0x00000002, 0x21421800, + 0x00000010, 0x001f0000, 0x00000008, 0x22000008, 0x00000003, 0x8384a000, + 0x0000000f, 0x65870010, 0x00000009, 0x2607ffff, 0x00000000, 0x27750c00, + 0x00000000, 0x66f40000, 0x0000000c, 0x29000000, 0x00000018, 0x800001aa, + 0x00000000, 0x03068c00, 0x00000003, 0xf4680c00, 0x00000010, 0x20530000, + 0x00000000, 0x22c58c00, 0x00000018, 0x8000ffe5, 0x00000000, 0x39760000, + 0x00000000, 0x39840000, 0x0000000c, 0x33e70019, 0x00000010, 0x001f0000, + 0x00000000, 0x031e0000, 0x00000000, 0x0760fe00, 0x0000000f, 0x0f0e0007, + 0x00000000, 0x83850800, 0x00000000, 0x0a7d0000, 0x00000000, 0x0afe0000, + 0x00000000, 0x0b7f0000, 0x00000000, 0x0d7a0000, 0x00000000, 0x0c000000, + 0x00000000, 0x0bfc0000, 0x00000000, 0x0c970e00, 0x00000008, 0x0f800003, + 0x0000000f, 0x47670010, 0x00000008, 0x070e0001, 0x0000000b, 0xc38000ff, + 0x00000002, 0x43870000, 0x00000001, 0x33e70e00, 0x0000000f, 0x038e0010, + 0x00000002, 0x33e70e00, 0x00000000, 0x28f30000, 0x00000010, 0x009f0000, + 0x00000000, 0x02043600, 0x00000010, 0x91840a02, 0x00000002, 0x21421800, + 0x00000008, 0x22000006, 0x00000000, 0x231b0000, 0x00000000, 0x23ff0000, + 0x00000000, 0x241b0000, 0x00000003, 0x8384a000, 0x0000000f, 0x65870010, + 0x00000009, 0x2607ffff, 0x00000000, 0x27110000, 0x00000000, 0x26900000, + 0x0000000c, 0x29000000, 0x00000018, 0x8000017e, 0x00000003, 0xf4683600, + 0x00000000, 0x3a100000, 0x00000000, 0x3a910000, 0x00000003, 0xf66c2400, + 0x00000010, 0x001f0000, 0x00000010, 0xb1923604, 0x00000008, 0x0f800004, + 0x00000000, 0x00000000, 0x00000010, 0x009f0000, 0x00000000, 0x3e170000, + 0x00000000, 0x3e940000, 0x00000000, 0x3f150000, 0x00000000, 0x3f960000, + 0x00000010, 0x001f0000, 0x00000000, 0x0f060000, 0x00000010, 0x20530000, + 0x00000000, 0x22c53600, 0x00000018, 0x8000ffac, 0x00000010, 0x001f0000, + 0x00000000, 0x031e0000, 0x00000000, 0x83850800, 0x00000009, 0x076000ff, + 0x0000000f, 0x0f0e0007, 0x00000000, 0x0c000000, 0x00000000, 0x0a7d0000, + 0x00000000, 0x0afe0000, 0x00000000, 0x0b7f0000, 0x00000000, 0x0d7a0000, + 0x00000000, 0x0bfc0000, 0x00000000, 0x0c970e00, 0x00000008, 0x0f800003, + 0x0000000f, 0x47670010, 0x00000008, 0x070e0001, 0x0000000b, 0xc38000ff, + 0x00000002, 0x43870000, 0x00000001, 0x33e70e00, 0x0000000f, 0x038e0010, + 0x00000002, 0x33e70e00, 0x00000000, 0x39840000, 0x00000003, 0xb9720800, + 0x00000000, 0x28f30000, 0x0000000f, 0x65680010, 0x00000010, 0x009f0000, + 0x00000000, 0x02043600, 0x00000010, 0x91840a02, 0x00000002, 0x21421800, + 0x00000008, 0x22000007, 0x00000000, 0x231b0000, 0x00000000, 0x23ff0000, + 0x00000000, 0x241b0000, 0x00000003, 0x8384a000, 0x0000000f, 0x65870010, + 0x00000009, 0x2607ffff, 0x00000000, 0x27110000, 0x00000000, 0x26900000, + 0x0000000c, 0x29000000, 0x00000018, 0x80000145, 0x00000003, 0xf4683600, + 0x00000000, 0x3a100000, 0x00000000, 0x3a910000, 0x00000003, 0xf66c2400, + 0x00000010, 0x001f0000, 0x00000010, 0xb1923604, 0x00000008, 0x0f800004, + 0x00000000, 0x00000000, 0x00000010, 0x009f0000, 0x00000000, 0x3e170000, + 0x00000000, 0x3e940000, 0x00000000, 0x3f150000, 0x00000000, 0x3f960000, + 0x00000010, 0x001f0000, 0x00000000, 0x0f060000, 0x00000010, 0x20530000, + 0x00000000, 0x22c53600, 0x00000018, 0x8000ff73, 0x00000010, 0x0ce70005, + 0x00000008, 0x2c80000c, 0x00000008, 0x2d000070, 0x00000008, 0x2d800010, + 0x00000000, 0x00000000, 0x00000010, 0x205f0000, 0x00000018, 0x8000011d, + 0x00000000, 0x2c1e0000, 0x00000008, 0x2c8000b8, 0x00000008, 0x2d000010, + 0x00000008, 0x2d800048, 0x00000000, 0x00000000, 0x00000010, 0x91de0000, + 0x00000018, 0x8000fe5d, 0x0000000c, 0x29800001, 0x00000000, 0x2a000000, + 0x00000010, 0x001f0000, 0x00000000, 0x0f008000, 0x00000008, 0x0f800007, + 0x00000018, 0x80000006, 0x0000000c, 0x29800001, 0x00000000, 0x2a000000, + 0x00000010, 0x001f0000, 0x0000000f, 0x0f470007, 0x00000008, 0x0f800008, + 0x00000018, 0x80000119, 0x00000010, 0x20530000, 0x00000018, 0x8000fe4f, + 0x0000000c, 0x29800001, 0x00000010, 0x91de0000, 0x00000000, 0x2fd50000, + 0x00000000, 0x2a000000, 0x00000009, 0x0261ffff, 0x0000000d, 0x70e10001, + 0x00000018, 0x80000101, 0x00000000, 0x2c400000, 0x00000008, 0x2c8000c4, + 0x00000008, 0x2d00001c, 0x00000008, 0x2d800001, 0x00000005, 0x70e10800, + 0x00000010, 0x91de0000, 0x00000018, 0x8000fe41, 0x0000000c, 0x29800001, + 0x00000010, 0x91de0000, 0x00000000, 0x2fd50000, 0x00000010, 0x001f0000, + 0x00000000, 0x02700000, 0x00000000, 0x0d620000, 0x00000000, 0xbb630800, + 0x00000000, 0x2a000000, 0x00000000, 0x0f400000, 0x00000000, 0x2c400000, + 0x0000000c, 0x73e7001b, 0x00000010, 0x0ce7000e, 0x00000000, 0x286d0000, + 0x0000000f, 0x65ed0010, 0x00000009, 0x266dffff, 0x00000018, 0x80000069, + 0x00000008, 0x02000004, 0x00000010, 0x91c40803, 0x00000018, 0x800000f6, + 0x00000010, 0x20530000, 0x00000018, 0x800000e5, 0x00000008, 0x2c8000b8, + 0x00000008, 0x2d000010, 0x00000008, 0x2d800048, 0x00000018, 0x80000005, + 0x00000008, 0x2c8000c4, 0x00000008, 0x2d00001c, 0x00000008, 0x2d800001, + 0x00000000, 0x00000000, 0x00000010, 0x205f0000, 0x00000008, 0x2c800048, + 0x00000008, 0x2d000068, 0x00000008, 0x2d800104, 0x00000000, 0x00000000, + 0x00000010, 0x91de0000, 0x00000000, 0x27f60000, 0x00000010, 0xb87a9e04, + 0x00000008, 0x2200000d, 0x00000018, 0x800000e2, 0x00000010, 0x20530000, + 0x00000018, 0x8000fe18, 0x0000000c, 0x29800001, 0x00000010, 0x91de0000, + 0x00000000, 0x2fd50000, 0x00000010, 0x001f0000, 0x00000000, 0x02700000, + 0x00000000, 0x0d620000, 0x00000000, 0xbb630800, 0x00000000, 0x2a000000, + 0x00000010, 0x0e670011, 0x00000000, 0x286d0000, 0x0000000f, 0x65ed0010, + 0x00000009, 0x266dffff, 0x00000004, 0xb8f1a000, 0x00000000, 0x0f400000, + 0x0000000c, 0x73e7001c, 0x00000018, 0x80000040, 0x00000008, 0x02000004, + 0x00000010, 0x91c40802, 0x00000018, 0x800000cd, 0x00000000, 0x2c1e0000, + 0x00000008, 0x2c8000b8, 0x00000008, 0x2d000010, 0x00000008, 0x2d800048, + 0x00000010, 0x20530000, 0x00000010, 0x91de0000, 0x00000018, 0x8000fdfe, + 0x0000000c, 0x29800001, 0x00000000, 0x03550000, 0x00000000, 0x06460000, + 0x00000000, 0x03d60000, 0x00000000, 0x2a000000, 0x0000000f, 0x0f480007, + 0x00000010, 0xb18c0027, 0x0000000f, 0x47420008, 0x00000009, 0x070e000f, + 0x00000008, 0x070e0008, 0x00000010, 0x001f0000, 0x00000008, 0x09000001, + 0x00000007, 0x09121c00, 0x00000003, 0xcbca9200, 0x00000000, 0x0b97a200, + 0x00000007, 0x4b171c00, 0x0000000f, 0x0a960003, 0x00000000, 0x0a959c00, + 0x00000000, 0x4a009a00, 0x00000008, 0x82120001, 0x00000001, 0x0c170800, + 0x00000000, 0x02180000, 0x00000000, 0x0c971800, 0x00000008, 0x0d00ffff, + 0x00000008, 0x0f800006, 0x0000000c, 0x29000000, 0x00000008, 0x22000001, + 0x00000000, 0x22c50c00, 0x00000010, 0x009f0000, 0x00000010, 0xb197320b, + 0x00000000, 0x231b0000, 0x00000000, 0x27110800, 0x00000000, 0x66900000, + 0x00000018, 0x800000a4, 0x00000000, 0x02180000, 0x00000010, 0x20530000, + 0x00000000, 0x22c53600, 0x00000010, 0x001f0000, 0x00000008, 0x0f800006, + 0x00000018, 0x8000fff5, 0x00000010, 0x91870002, 0x00000008, 0x2200000a, + 0x00000000, 0x231b0000, 0x00000000, 0x27110800, 0x00000000, 0x66900000, + 0x00000018, 0x80000098, 0x00000008, 0x0200000a, 0x00000010, 0x91c40804, + 0x00000010, 0x02c20003, 0x00000010, 0x001f0000, 0x00000008, 0x0f800008, + 0x00000010, 0x20530000, 0x00000018, 0x8000fdc9, 0x00000000, 0x06820000, + 0x00000010, 0x001f0000, 0x00000010, 0x0ce70028, 0x00000000, 0x03720000, + 0x00000000, 0xa8760c00, 0x00000000, 0x0cf60000, 0x00000010, 0xb8723224, + 0x00000000, 0x03440000, 0x00000008, 0x22000010, 0x00000000, 0x03ca0000, + 0x0000000f, 0x65680010, 0x00000000, 0x0bcf0000, 0x00000000, 0x27f20000, + 0x00000010, 0xb7ef3203, 0x0000000c, 0x21420004, 0x0000000c, 0x73e70019, + 0x00000000, 0x07520000, 0x00000000, 0x29000000, 0x00000018, 0x8000007e, + 0x00000004, 0xb9723200, 0x00000010, 0x20530000, 0x00000000, 0x22060000, + 0x0000000c, 0x61420004, 0x00000000, 0x25070000, 0x00000000, 0x27970000, + 0x00000000, 0x290e0000, 0x00000010, 0x0ce70010, 0x00000010, 0xb873320f, + 0x0000000f, 0x436c0010, 0x00000000, 0x03f30c00, 0x00000000, 0x03f30000, + 0x00000000, 0x83990e00, 0x00000001, 0x83860e00, 0x00000000, 0x83060e00, + 0x00000003, 0xf66c0c00, 0x00000000, 0x39f30e00, 0x00000000, 0x3af50e00, + 0x00000000, 0x7a740000, 0x0000000f, 0x43680010, 0x00000001, 0x83860e00, + 0x00000000, 0x83060e00, 0x00000003, 0xf4680c00, 0x00000000, 0x286d0000, + 0x00000000, 0x03690000, 0x00000010, 0xb1f60c54, 0x00000000, 0x0a6a0000, + 0x00000000, 0x0aeb0000, 0x00000009, 0x0b6cffff, 0x00000000, 0x0c000000, + 0x00000000, 0x0be90000, 0x00000003, 0x8cf6a000, 0x0000000c, 0x09800002, + 0x00000010, 0x009f0000, 0x00000010, 0xb8173209, 0x00000000, 0x35140000, + 0x00000000, 0x35950000, 0x00000005, 0x766c2c00, 0x00000000, 0x34970000, + 0x00000004, 0xb8f12e00, 0x00000010, 0x001f0000, 0x00000008, 0x0f800004, + 0x00000018, 0x8000fff7, 0x00000000, 0x03e90000, 0x00000010, 0xb8f6a01a, + 0x00000010, 0x20130019, 0x00000010, 0xb1f10e18, 0x00000000, 0x83973200, + 0x00000000, 0x38700e00, 0x00000000, 0xbb760e00, 0x00000000, 0x37d00000, + 0x0000000c, 0x73e7001a, 0x00000003, 0xb8f1a000, 0x00000000, 0x32140000, + 0x00000000, 0x32950000, 0x00000005, 0x73e72c00, 0x00000000, 0x33190000, + 0x00000005, 0x74680000, 0x00000010, 0x0ce7000d, 0x00000008, 0x22000009, + 0x00000000, 0x07520000, 0x00000000, 0x29000000, 0x0000000c, 0x73e70019, + 0x0000000f, 0x65680010, 0x0000000c, 0x21420004, 0x00000018, 0x8000003c, + 0x00000010, 0x20530000, 0x0000000c, 0x61420004, 0x00000000, 0x290e0000, + 0x00000018, 0x80000002, 0x00000010, 0x91973206, 0x00000000, 0x35140000, + 0x00000000, 0x35950000, 0x00000005, 0x766c2c00, 0x00000000, 0x34990000, + 0x00000004, 0xb8f13200, 0x00000000, 0x83690c00, 0x00000010, 0xb1860013, + 0x00000000, 0x28e90000, 0x00000008, 0x22000004, 0x00000000, 0x23ec0000, + 0x00000000, 0x03690000, 0x00000010, 0xb8660c07, 0x00000009, 0x036cffff, + 0x00000000, 0x326a0000, 0x00000000, 0x32eb0000, 0x00000005, 0x73e70c00, + 0x00000000, 0x33690000, 0x00000005, 0x74680000, 0x0000000c, 0x73e7001c, + 0x00000000, 0x03690000, 0x00000010, 0xb1f60c12, 0x00000010, 0xb1d00c11, + 0x0000000c, 0x21420005, 0x0000000c, 0x33e7001c, 0x00000018, 0x8000000e, + 0x00000010, 0x2e67000d, 0x00000000, 0x03690000, 0x00000010, 0xb1f60c0b, + 0x00000010, 0xb1d00c0a, 0x00000000, 0x03440000, 0x00000008, 0x2200000c, + 0x00000000, 0x07520000, 0x00000000, 0x29000000, 0x00000018, 0x80000015, + 0x0000000c, 0x33e7001c, 0x00000010, 0x20530000, 0x00000000, 0x22060000, + 0x00000000, 0x290e0000, 0x00000018, 0x000d0000, 0x00000000, 0x06820000, + 0x00000010, 0x2de7000d, 0x00000010, 0x0ce7000c, 0x00000000, 0x27f20000, + 0x00000010, 0xb96d9e0a, 0x00000000, 0xa86d9e00, 0x00000009, 0x0361ffff, + 0x00000010, 0xb7500c07, 0x00000008, 0x2200000f, 0x0000000f, 0x65680010, + 0x00000000, 0x29000000, 0x00000018, 0x80000004, 0x0000000c, 0x33e7001b, + 0x00000010, 0x20530000, 0x00000018, 0x000d0000, 0x00000000, 0x2b820000, + 0x00000010, 0x20d2002f, 0x00000010, 0x0052002e, 0x00000009, 0x054e0007, + 0x00000010, 0xb18a002c, 0x00000000, 0x050a8c00, 0x00000008, 0x850a0008, + 0x00000010, 0x918a0029, 0x00000003, 0xc5008800, 0x00000008, 0xa3460001, + 0x00000010, 0xb1c60007, 0x00000008, 0x22000001, 0x0000000c, 0x29800000, + 0x00000010, 0x20530000, 0x00000000, 0x274e8c00, 0x00000000, 0x66cd0000, + 0x00000000, 0x22c58c00, 0x00000008, 0x22000014, 0x00000003, 0x22c58e00, + 0x00000003, 0x23c58e00, 0x00000003, 0x22c58e00, 0x00000003, 0x26cd9e00, + 0x00000003, 0x27cd9e00, 0x00000003, 0x26cd9e00, 0x00000003, 0x274ea000, + 0x00000003, 0x284ea000, 0x00000003, 0x274ea000, 0x0000000c, 0x69520000, + 0x0000000c, 0x29800000, 0x00000010, 0x20530000, 0x00000003, 0x22c58e00, + 0x00000003, 0x23c58e00, 0x00000003, 0x22c58e00, 0x00000003, 0x26cd9e00, + 0x00000003, 0x27cd9e00, 0x00000003, 0x26cd9e00, 0x00000003, 0x274ea000, + 0x00000003, 0x284ea000, 0x00000003, 0x274ea000, 0x00000000, 0xa2c58c00, + 0x00000000, 0xa74e8c00, 0x00000000, 0xe6cd0000, 0x0000000f, 0x620a0010, + 0x00000008, 0x23460001, 0x0000000c, 0x29800000, 0x00000010, 0x20530000, + 0x0000000c, 0x29520000, 0x00000018, 0x80000002, 0x0000000c, 0x29800000, + 0x00000018, 0x00570000 }; + +static const int bnx2_TPAT_b06FwReleaseMajor = 0x1; +static const int bnx2_TPAT_b06FwReleaseMinor = 0x0; +static const int bnx2_TPAT_b06FwReleaseFix = 0x0; +static const u32 bnx2_TPAT_b06FwStartAddr = 0x08000860; +static const u32 bnx2_TPAT_b06FwTextAddr = 0x08000800; +static const int bnx2_TPAT_b06FwTextLen = 0x122c; +static const u32 bnx2_TPAT_b06FwDataAddr = 0x08001a60; +static const int bnx2_TPAT_b06FwDataLen = 0x0; +static const u32 bnx2_TPAT_b06FwRodataAddr = 0x00000000; +static const int bnx2_TPAT_b06FwRodataLen = 0x0; +static const u32 bnx2_TPAT_b06FwBssAddr = 0x08001aa0; +static const int bnx2_TPAT_b06FwBssLen = 0x250; +static const u32 bnx2_TPAT_b06FwSbssAddr = 0x08001a60; +static const int bnx2_TPAT_b06FwSbssLen = 0x34; +static u32 bnx2_TPAT_b06FwText[(0x122c/4) + 1] = { + 0x0a000218, 0x00000000, 0x00000000, 0x0000000d, 0x74706174, 0x20322e35, + 0x2e313100, 0x02050b01, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x10000003, 0x00000000, 0x0000000d, 0x0000000d, 0x3c020800, + 0x24421a60, 0x3c030800, 0x24631cf0, 0xac400000, 0x0043202b, 0x1480fffd, + 0x24420004, 0x3c1d0800, 0x37bd2ffc, 0x03a0f021, 0x3c100800, 0x26100860, + 0x3c1c0800, 0x279c1a60, 0x0e000546, 0x00000000, 0x0000000d, 0x8f820010, + 0x8c450008, 0x24030800, 0xaf430178, 0x97430104, 0x3c020008, 0xaf420140, + 0x8f820024, 0x30420001, 0x10400007, 0x3069ffff, 0x24020002, 0x2523fffe, + 0xa7420146, 0xa7430148, 0x0a000242, 0x3c020800, 0xa7400146, 0x3c020800, + 0x8c43083c, 0x1460000e, 0x24020f00, 0x8f820024, 0x30430020, 0x0003182b, + 0x00031823, 0x30650009, 0x30420c00, 0x24030400, 0x14430002, 0x34a40001, + 0x34a40005, 0xa744014a, 0x0a000264, 0x3c020800, 0x8f830014, 0x14620008, + 0x00000000, 0x8f820024, 0x30420020, 0x0002102b, 0x00021023, 0x3042000d, + 0x0a000262, 0x34420005, 0x8f820024, 0x30420020, 0x0002102b, 0x00021023, + 0x30420009, 0x34420001, 0xa742014a, 0x3c020800, 0x8c430820, 0x8f840024, + 0x3c020048, 0x00621825, 0x30840006, 0x24020002, 0x1082000d, 0x2c820003, + 0x50400005, 0x24020004, 0x10800012, 0x3c020001, 0x0a000284, 0x00000000, + 0x10820007, 0x24020006, 0x1482000f, 0x3c020111, 0x0a00027c, 0x00621025, + 0x0a00027b, 0x3c020101, 0x3c020011, 0x00621025, 0x24030001, 0xaf421000, + 0xaf830020, 0x0a000284, 0x00000000, 0x00621025, 0xaf421000, 0xaf800020, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8f830020, 0x1060003f, + 0x3c048000, 0x8f421000, 0x00441024, 0x1040fffd, 0x00000000, 0x10600039, + 0x00000000, 0x8f421000, 0x3c030020, 0x00431024, 0x10400034, 0x00000000, + 0x97421014, 0x14400031, 0x00000000, 0x97421008, 0x8f840010, 0x24420006, + 0x00024082, 0x00081880, 0x00643821, 0x8ce50000, 0x30430003, 0x30420001, + 0x10400004, 0x00000000, 0x0000000d, 0x0a0002c3, 0x00081080, 0x5460000f, + 0x30a5ffff, 0x3c06ffff, 0x00a62824, 0x0005182b, 0x00a61026, 0x0002102b, + 0x00621824, 0x10600004, 0x00000000, 0x0000000d, 0x00000000, 0x240001fb, + 0x8ce20000, 0x0a0002c2, 0x00462825, 0x0005182b, 0x38a2ffff, 0x0002102b, + 0x00621824, 0x10600004, 0x00000000, 0x0000000d, 0x00000000, 0x24000205, + 0x8ce20000, 0x3445ffff, 0x00081080, 0x00441021, 0x3c030800, 0xac450000, + 0x8c620830, 0x24420001, 0xac620830, 0x8f840018, 0x01202821, 0x24820008, + 0x30421fff, 0x24434000, 0x0343d821, 0x30a30007, 0xaf84000c, 0xaf820018, + 0xaf420084, 0x10600002, 0x24a20007, 0x3045fff8, 0x8f820030, 0x8f840000, + 0x00451821, 0xaf82001c, 0x0064102b, 0xaf830030, 0x14400002, 0x00641023, + 0xaf820030, 0x8f840030, 0x34028000, 0x00821021, 0x03421821, 0x3c021000, + 0xaf830010, 0xaf440080, 0x03e00008, 0xaf420178, 0x8f830024, 0x27bdffe0, + 0xafbf0018, 0xafb10014, 0x30620200, 0x14400004, 0xafb00010, 0x0000000d, + 0x00000000, 0x24000242, 0x00031a82, 0x30630003, 0x000310c0, 0x00431021, + 0x00021080, 0x00431021, 0x00021080, 0x3c030800, 0x24631aa0, 0x00438821, + 0x8e240000, 0x10800004, 0x00000000, 0x0000000d, 0x00000000, 0x2400024d, + 0x8f850010, 0x24020001, 0xae220000, 0x8ca70008, 0xa2200007, 0x8f620004, + 0x26300014, 0x02002021, 0x00021402, 0xa2220004, 0x304600ff, 0x24c60005, + 0x0e000673, 0x00063082, 0x8f620004, 0xa6220008, 0x8f430108, 0x3c021000, + 0x00621824, 0x10600008, 0x00000000, 0x97420104, 0x92230007, 0x2442ffec, + 0x3045ffff, 0x34630002, 0x0a000321, 0xa2230007, 0x97420104, 0x2442fff0, + 0x3045ffff, 0x8f620004, 0x3042ffff, 0x2c420013, 0x54400005, 0x92230007, + 0x92220007, 0x34420001, 0xa2220007, 0x92230007, 0x24020001, 0x10620009, + 0x28620002, 0x14400014, 0x24020002, 0x10620012, 0x24020003, 0x1062000a, + 0x00000000, 0x0a000342, 0x00000000, 0x8f820010, 0x8c43000c, 0x3c04ffff, + 0x00641824, 0x00651825, 0x0a000342, 0xac43000c, 0x8f820010, 0x8c430010, + 0x3c04ffff, 0x00641824, 0x00651825, 0xac430010, 0x8f620004, 0x3042ffff, + 0x24420002, 0x00021083, 0xa2220005, 0x304500ff, 0x8f820010, 0x3c04ffff, + 0x00052880, 0x00a22821, 0x8ca70000, 0x96220008, 0x97430104, 0x00e42024, + 0x24420002, 0x00621823, 0x00833825, 0xaca70000, 0x92240005, 0x00041080, + 0x02021021, 0x90430000, 0x3c05fff6, 0x34a5ffff, 0x3063000f, 0x00832021, + 0xa2240006, 0x308200ff, 0x24420003, 0x00021080, 0x02021021, 0x8c460000, + 0x308300ff, 0x8f820010, 0x3c04ff3f, 0x00031880, 0x00c53824, 0x00621821, + 0xae26000c, 0xac67000c, 0x8e22000c, 0x92230006, 0x3484ffff, 0x00441024, + 0x24630003, 0x00031880, 0x02031821, 0x00e42024, 0xae22000c, 0xac640000, + 0x92220006, 0x24420004, 0x00021080, 0x02021021, 0x94470002, 0xac470000, + 0x92230006, 0x8f820010, 0x00031880, 0x00621821, 0x24020010, 0xac670010, + 0x24030002, 0xa7420140, 0xa7400142, 0xa7400144, 0xa7430146, 0x97420104, + 0x24030001, 0x2442fffe, 0xa7420148, 0xa743014a, 0x8f820024, 0x24030002, + 0x30440006, 0x1083000d, 0x2c820003, 0x10400005, 0x24020004, 0x10800011, + 0x3c020009, 0x0a0003a5, 0x00000000, 0x10820007, 0x24020006, 0x1482000d, + 0x3c020119, 0x0a00039f, 0x24030001, 0x0a00039e, 0x3c020109, 0x3c020019, + 0x24030001, 0xaf421000, 0xaf830020, 0x0a0003a5, 0x00000000, 0xaf421000, + 0xaf800020, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x92220004, + 0x24030008, 0x8f840020, 0x24420002, 0x30420007, 0x00621823, 0x30630007, + 0x10800006, 0xae230010, 0x3c038000, 0x8f421000, 0x00431024, 0x1040fffd, + 0x00000000, 0x8f820018, 0xaf82000c, 0x24420010, 0x30421fff, 0xaf820018, + 0xaf420084, 0x97430104, 0x24424000, 0x0342d821, 0x3063ffff, 0x30620007, + 0x10400002, 0x24620007, 0x3043fff8, 0x8f820030, 0x8f840000, 0x00431821, + 0xaf82001c, 0x0064102b, 0xaf830030, 0x14400002, 0x00641023, 0xaf820030, + 0x8f840030, 0x34028000, 0x8fbf0018, 0x8fb10014, 0x8fb00010, 0x00821021, + 0x03421821, 0x3c021000, 0xaf830010, 0xaf440080, 0xaf420178, 0x03e00008, + 0x27bd0020, 0x8f830024, 0x27bdffe0, 0xafbf0018, 0xafb10014, 0x30620200, + 0x14400004, 0xafb00010, 0x0000000d, 0x00000000, 0x240002e4, 0x00031a82, + 0x30630003, 0x000310c0, 0x00431021, 0x00021080, 0x00431021, 0x00021080, + 0x3c030800, 0x24631aa0, 0x00438021, 0x8e040000, 0x14800004, 0x00000000, + 0x0000000d, 0x00000000, 0x240002e9, 0x8f620004, 0x04410008, 0x26050014, + 0x92020006, 0x8e03000c, 0x24420003, 0x00021080, 0x00a21021, 0xac430000, + 0xae000000, 0x92020005, 0x24420001, 0x00021080, 0x00a21021, 0x8c430000, + 0x3c040001, 0x00641821, 0xac430000, 0x92060004, 0x27710008, 0x02202021, + 0x24c60005, 0x0e000673, 0x00063082, 0x92040006, 0x3c057fff, 0x8f620004, + 0x00042080, 0x00912021, 0x8c830004, 0x34a5ffff, 0x00451024, 0x00621821, + 0xac830004, 0x92050005, 0x3c07ffff, 0x92040004, 0x00052880, 0x00b12821, + 0x8ca30000, 0x97420104, 0x96060008, 0x00671824, 0x00441021, 0x00461023, + 0x3042ffff, 0x00621825, 0xaca30000, 0x92030007, 0x24020001, 0x1062000a, + 0x28620002, 0x1440001d, 0x2402000a, 0x24020002, 0x10620019, 0x24020003, + 0x1062000e, 0x2402000a, 0x0a000447, 0x00000000, 0x92020004, 0x97430104, + 0x8e24000c, 0x00621821, 0x2463fff2, 0x3063ffff, 0x00872024, 0x00832025, + 0xae24000c, 0x0a000447, 0x2402000a, 0x92020004, 0x97430104, 0x8e240010, + 0x00621821, 0x2463ffee, 0x3063ffff, 0x00872024, 0x00832025, 0xae240010, + 0x2402000a, 0xa7420140, 0x96030012, 0x8f840024, 0xa7430142, 0x92020004, + 0xa7420144, 0xa7400146, 0x97430104, 0x30840006, 0x24020001, 0xa7430148, + 0xa742014a, 0x24020002, 0x1082000d, 0x2c820003, 0x10400005, 0x24020004, + 0x10800011, 0x3c020041, 0x0a00046c, 0x00000000, 0x10820007, 0x24020006, + 0x1482000d, 0x3c020151, 0x0a000466, 0x24030001, 0x0a000465, 0x3c020141, + 0x3c020051, 0x24030001, 0xaf421000, 0xaf830020, 0x0a00046c, 0x00000000, + 0xaf421000, 0xaf800020, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x8f820020, 0x8f840018, 0x10400006, 0x92030004, 0x3c058000, 0x8f421000, + 0x00451024, 0x1040fffd, 0x00000000, 0x2463000a, 0x30620007, 0x10400002, + 0x24620007, 0x304303f8, 0x00831021, 0x30421fff, 0xaf84000c, 0xaf820018, + 0xaf420084, 0x97430104, 0x24424000, 0x0342d821, 0x3063ffff, 0x30620007, + 0x10400002, 0x24620007, 0x3043fff8, 0x8f820030, 0x8f840000, 0x00431821, + 0xaf82001c, 0x0064102b, 0xaf830030, 0x14400002, 0x00641023, 0xaf820030, + 0x8f840030, 0x34028000, 0x8fbf0018, 0x8fb10014, 0x8fb00010, 0x00821021, + 0x03421821, 0x3c021000, 0xaf830010, 0xaf440080, 0xaf420178, 0x03e00008, + 0x27bd0020, 0x8f620000, 0x97430104, 0x3c048000, 0x3045ffff, 0x3066ffff, + 0x8f420178, 0x00441024, 0x1440fffd, 0x2402000a, 0x30a30007, 0xa7420140, + 0x24020008, 0x00431023, 0x30420007, 0x24a3fffe, 0xa7420142, 0xa7430144, + 0xa7400146, 0xa7460148, 0x8f420108, 0x8f830024, 0x30420020, 0x0002102b, + 0x00021023, 0x30420009, 0x34420001, 0x30630006, 0xa742014a, 0x24020002, + 0x1062000d, 0x2c620003, 0x10400005, 0x24020004, 0x10600011, 0x3c020041, + 0x0a0004d6, 0x00000000, 0x10620007, 0x24020006, 0x1462000d, 0x3c020151, + 0x0a0004d0, 0x24030001, 0x0a0004cf, 0x3c020141, 0x3c020051, 0x24030001, + 0xaf421000, 0xaf830020, 0x0a0004d6, 0x00000000, 0xaf421000, 0xaf800020, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8f820020, 0x24a30008, + 0x8f850018, 0x10400006, 0x30c6ffff, 0x3c048000, 0x8f421000, 0x00441024, + 0x1040fffd, 0x00000000, 0x3063ffff, 0x30620007, 0x10400002, 0x24620007, + 0x3043fff8, 0x00a31021, 0x30421fff, 0x24434000, 0x0343d821, 0x00c02021, + 0x30830007, 0xaf85000c, 0xaf820018, 0xaf420084, 0x10600002, 0x24820007, + 0x3044fff8, 0x8f820030, 0x8f850000, 0x00441821, 0xaf82001c, 0x0065102b, + 0xaf830030, 0x14400002, 0x00651023, 0xaf820030, 0x8f840030, 0x34028000, + 0x3c030800, 0x8c650834, 0x00821021, 0x03421821, 0xaf830010, 0xaf440080, + 0x10a00006, 0x2402000e, 0x9383002f, 0x14620004, 0x3c021000, 0x2402043f, + 0xa7420148, 0x3c021000, 0x03e00008, 0xaf420178, 0x8f820024, 0x30424000, + 0x10400005, 0x24020800, 0x0000000d, 0x00000000, 0x2400040e, 0x24020800, + 0xaf420178, 0x97440104, 0x3c030008, 0xaf430140, 0x8f820024, 0x30420001, + 0x10400006, 0x3085ffff, 0x24020002, 0x24a3fffe, 0xa7420146, 0x0a000526, + 0xa7430148, 0xa7400146, 0x8f840018, 0x2402000d, 0xa742014a, 0x24830008, + 0x30631fff, 0x24624000, 0x0342d821, 0x30a20007, 0xaf84000c, 0xaf830018, + 0xaf430084, 0x10400002, 0x24a20007, 0x3045fff8, 0x8f820030, 0x8f840000, + 0x00451821, 0xaf82001c, 0x0064102b, 0xaf830030, 0x14400002, 0x00641023, + 0xaf820030, 0x8f840030, 0x34028000, 0x00821021, 0x03421821, 0x3c021000, + 0xaf830010, 0xaf440080, 0x03e00008, 0xaf420178, 0x27bdffe8, 0x3c046008, + 0xafbf0014, 0xafb00010, 0x8c825000, 0x3c1a8000, 0x2403ff7f, 0x375b4000, + 0x00431024, 0x3442380c, 0xac825000, 0x8f430008, 0x3c100800, 0x37428000, + 0x34630001, 0xaf430008, 0xaf820010, 0x3c02601c, 0xaf800018, 0xaf400080, + 0xaf400084, 0x8c450008, 0x3c036000, 0x8c620808, 0x3c040800, 0x3c030080, + 0xac830820, 0x3042fff0, 0x38420010, 0x2c420001, 0xaf850000, 0xaf820004, + 0x0e000658, 0x00000000, 0x8f420000, 0x30420001, 0x1040fffb, 0x00000000, + 0x8f430108, 0x8f440100, 0x30622000, 0xaf830024, 0xaf840014, 0x10400004, + 0x8e02082c, 0x24420001, 0x0a0005c6, 0xae02082c, 0x30620200, 0x14400003, + 0x24020f00, 0x14820027, 0x24020d00, 0x97420104, 0x1040001c, 0x30624000, + 0x14400005, 0x00000000, 0x0e00022f, 0x00000000, 0x0a0005bb, 0x00000000, + 0x8f620008, 0x8f630000, 0x24020030, 0x00031e02, 0x306300f0, 0x10620007, + 0x28620031, 0x1440002f, 0x24020040, 0x10620007, 0x00000000, 0x0a0005bb, + 0x00000000, 0x0e0002e8, 0x00000000, 0x0a0005bb, 0x00000000, 0x0e0003db, + 0x00000000, 0x0a0005bb, 0x00000000, 0x30620040, 0x1440002b, 0x00000000, + 0x0000000d, 0x00000000, 0x240004b2, 0x0a0005c6, 0x00000000, 0x1482000f, + 0x30620006, 0x97420104, 0x10400005, 0x30620040, 0x0e000510, 0x00000000, + 0x0a0005bb, 0x00000000, 0x1440001b, 0x00000000, 0x0000000d, 0x00000000, + 0x240004c4, 0x0a0005c6, 0x00000000, 0x1040000e, 0x30621000, 0x10400005, + 0x00000000, 0x0e000688, 0x00000000, 0x0a0005bb, 0x00000000, 0x0e0004a1, + 0x00000000, 0x8f82002c, 0x24420001, 0xaf82002c, 0x0a0005c6, 0x00000000, + 0x30620040, 0x14400004, 0x00000000, 0x0000000d, 0x00000000, 0x240004db, + 0x8f420138, 0x3c034000, 0x00431025, 0xaf420138, 0x0a000566, 0x00000000, + 0x3c046008, 0x8c835000, 0x3c1a8000, 0x2402ff7f, 0x375b4000, 0x00621824, + 0x3463380c, 0xac835000, 0x8f420008, 0x3c056000, 0x3c03601c, 0x34420001, + 0xaf420008, 0x37428000, 0xaf800018, 0xaf820010, 0xaf400080, 0xaf400084, + 0x8c660008, 0x8ca20808, 0x3c040800, 0x3c030080, 0xac830820, 0x3042fff0, + 0x38420010, 0x2c420001, 0xaf860000, 0xaf820004, 0x03e00008, 0x00000000, + 0x3084ffff, 0x30820007, 0x10400002, 0x24820007, 0x3044fff8, 0x8f820018, + 0x00441821, 0x30631fff, 0x24644000, 0x0344d821, 0xaf82000c, 0xaf830018, + 0x03e00008, 0xaf430084, 0x3084ffff, 0x30820007, 0x10400002, 0x24820007, + 0x3044fff8, 0x8f820030, 0x8f830000, 0x00442021, 0xaf82001c, 0x0083102b, + 0xaf840030, 0x14400002, 0x00831023, 0xaf820030, 0x8f820030, 0x34038000, + 0x00431821, 0x03432021, 0xaf840010, 0x03e00008, 0xaf420080, 0x8f830024, + 0x24020002, 0x30630006, 0x1062000d, 0x2c620003, 0x50400005, 0x24020004, + 0x10600012, 0x3c020001, 0x0a00062a, 0x00000000, 0x10620007, 0x24020006, + 0x1462000f, 0x3c020111, 0x0a000622, 0x00821025, 0x0a000621, 0x3c020101, + 0x3c020011, 0x00821025, 0x24030001, 0xaf421000, 0xaf830020, 0x0a00062a, + 0x00000000, 0x00821025, 0xaf421000, 0xaf800020, 0x00000000, 0x00000000, + 0x00000000, 0x03e00008, 0x00000000, 0x8f820020, 0x10400005, 0x3c038000, + 0x8f421000, 0x00431024, 0x1040fffd, 0x00000000, 0x03e00008, 0x00000000, + 0x8f820024, 0x27bdffe8, 0x30424000, 0x14400005, 0xafbf0010, 0x0e00022f, + 0x00000000, 0x0a000656, 0x8fbf0010, 0x8f620008, 0x8f630000, 0x24020030, + 0x00031e02, 0x306300f0, 0x10620008, 0x28620031, 0x1440000d, 0x8fbf0010, + 0x24020040, 0x10620007, 0x00000000, 0x0a000656, 0x00000000, 0x0e0002e8, + 0x00000000, 0x0a000656, 0x8fbf0010, 0x0e0003db, 0x00000000, 0x8fbf0010, + 0x03e00008, 0x27bd0018, 0x8f840028, 0x1080000f, 0x3c026000, 0x8c430c3c, + 0x30630fff, 0xaf830008, 0x14600011, 0x3082000f, 0x10400005, 0x308200f0, + 0x10400003, 0x30820f00, 0x14400006, 0x00000000, 0x0000000d, 0x00000000, + 0x2400051a, 0x03e00008, 0x00000000, 0x0000000d, 0x00000000, 0x2400051f, + 0x03e00008, 0x00000000, 0xaf830028, 0x03e00008, 0x00000000, 0x10c00007, + 0x00000000, 0x8ca20000, 0x24c6ffff, 0x24a50004, 0xac820000, 0x14c0fffb, + 0x24840004, 0x03e00008, 0x00000000, 0x0a000684, 0x00a01021, 0xac860000, + 0x00000000, 0x00000000, 0x24840004, 0x00a01021, 0x1440fffa, 0x24a5ffff, + 0x03e00008, 0x00000000, 0x0000000d, 0x03e00008, 0x00000000, 0x00000000}; + +static u32 bnx2_TPAT_b06FwData[(0x0/4) + 1] = { 0x0 }; +static u32 bnx2_TPAT_b06FwRodata[(0x0/4) + 1] = { 0x0 }; +static u32 bnx2_TPAT_b06FwBss[(0x250/4) + 1] = { 0x0 }; +static u32 bnx2_TPAT_b06FwSbss[(0x34/4) + 1] = { 0x0 }; + +static const int bnx2_TXP_b06FwReleaseMajor = 0x1; +static const int bnx2_TXP_b06FwReleaseMinor = 0x0; +static const int bnx2_TXP_b06FwReleaseFix = 0x0; +static const u32 bnx2_TXP_b06FwStartAddr = 0x080034b0; +static const u32 bnx2_TXP_b06FwTextAddr = 0x08000000; +static const int bnx2_TXP_b06FwTextLen = 0x5748; +static const u32 bnx2_TXP_b06FwDataAddr = 0x08005760; +static const int bnx2_TXP_b06FwDataLen = 0x0; +static const u32 bnx2_TXP_b06FwRodataAddr = 0x00000000; +static const int bnx2_TXP_b06FwRodataLen = 0x0; +static const u32 bnx2_TXP_b06FwBssAddr = 0x080057a0; +static const int bnx2_TXP_b06FwBssLen = 0x1c4; +static const u32 bnx2_TXP_b06FwSbssAddr = 0x08005760; +static const int bnx2_TXP_b06FwSbssLen = 0x38; +static u32 bnx2_TXP_b06FwText[(0x5748/4) + 1] = { + 0x0a000d2c, 0x00000000, 0x00000000, 0x0000000d, 0x74787020, 0x322e352e, + 0x38000000, 0x02050800, 0x0000000a, 0x000003e8, 0x0000ea60, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x10000003, 0x00000000, 0x0000000d, 0x0000000d, 0x3c020800, + 0x24425760, 0x3c030800, 0x24635964, 0xac400000, 0x0043202b, 0x1480fffd, + 0x24420004, 0x3c1d0800, 0x37bd7ffc, 0x03a0f021, 0x3c100800, 0x261034b0, + 0x3c1c0800, 0x279c5760, 0x0e000f5b, 0x00000000, 0x0000000d, 0x8f840014, + 0x27bdffe8, 0xafb10014, 0xafb00010, 0x8f460104, 0x8f830008, 0x8c8500ac, + 0xaf430080, 0x948200a8, 0xa7420e10, 0x948300aa, 0xa7430e12, 0x8c8200ac, + 0xaf420e18, 0x97430e10, 0xa7430e14, 0x97420e12, 0x00008021, 0xa7420e16, + 0x8f430e18, 0x00006021, 0x00c53023, 0xaf430e1c, 0x10c001a2, 0x2d820001, + 0x3c0e1000, 0x2419fff8, 0x24110010, 0x240f0f00, 0x3c188100, 0x93620008, + 0x10400009, 0x00000000, 0x97620010, 0x00c2102b, 0x14400005, 0x00000000, + 0x97620010, 0x3042ffff, 0x0a000d6d, 0xaf420e00, 0xaf460e00, 0x8f420000, + 0x30420008, 0x1040fffd, 0x00000000, 0x97420e08, 0x8f450e04, 0x3044ffff, + 0x30820001, 0x14400005, 0x00000000, 0x14a00005, 0x3083a040, 0x0a000f34, + 0x00000000, 0x0000000d, 0x3083a040, 0x24020040, 0x1462004f, 0x3082a000, + 0x308a0036, 0x8f88000c, 0x30890008, 0x24020800, 0xaf420178, 0x01001821, + 0x9742008a, 0x00431023, 0x2442ffff, 0x30421fff, 0x2c420008, 0x1440fffa, + 0x00a06021, 0x8f820018, 0x00cc3023, 0x24070001, 0x8f830008, 0x304b00ff, + 0x24420001, 0xaf820018, 0x25024000, 0x106f0005, 0x03422021, 0x93820012, + 0x30420007, 0x00021240, 0x34470001, 0x000b1400, 0x3c030100, 0x00431025, + 0xac820000, 0x8f830018, 0x00ea3825, 0x1120000f, 0xac830004, 0x97430e0a, + 0x8f84000c, 0x00ee3825, 0x2402000e, 0x00781825, 0xaf430160, 0x25830006, + 0x24840008, 0x30841fff, 0xa742015a, 0xa7430158, 0xaf84000c, 0x0a000db7, + 0x00000000, 0x8f83000c, 0x25820002, 0xa7420158, 0x24630008, 0x30631fff, + 0xaf83000c, 0x54c0000f, 0x8f420e14, 0x8f820008, 0x504f0002, 0x24100001, + 0x34e70040, 0x97420e10, 0x97430e12, 0x8f850014, 0x00021400, 0x00621825, + 0xaca300a8, 0x8f840014, 0x8f420e18, 0xac8200ac, 0x8f420e14, 0x8f430e1c, + 0xaf420144, 0xaf430148, 0xa34b0152, 0xaf470154, 0x0a000efb, 0xaf4e0178, + 0x10400165, 0x00000000, 0x93620008, 0x50400008, 0xafa60008, 0x97620010, + 0x00a2102b, 0x10400003, 0x30820040, 0x1040015c, 0x00000000, 0xafa60008, + 0xa7840010, 0xaf850004, 0x93620008, 0x1440005f, 0x27ac0008, 0xaf60000c, + 0x97820010, 0x30424000, 0x10400002, 0x2403000e, 0x24030016, 0xa363000a, + 0x24034007, 0xaf630014, 0x93820012, 0x8f630014, 0x30420007, 0x00021240, + 0x00621825, 0xaf630014, 0x97820010, 0x8f630014, 0x30420010, 0x00621825, + 0xaf630014, 0x97820010, 0x30420008, 0x5040000e, 0x00002821, 0x8f620014, + 0x004e1025, 0xaf620014, 0x97430e0a, 0x2402000e, 0x00781825, 0xaf630004, + 0xa3620002, 0x9363000a, 0x3405fffc, 0x24630004, 0x0a000e06, 0xa363000a, + 0xaf600004, 0xa3600002, 0x97820010, 0x9363000a, 0x30421f00, 0x00021182, + 0x24420028, 0x00621821, 0xa3630009, 0x97420e0c, 0xa7620010, 0x93630009, + 0x24020008, 0x24630002, 0x30630007, 0x00431023, 0x30420007, 0xa362000b, + 0x93640009, 0x97620010, 0x8f890004, 0x97830010, 0x00441021, 0x00a21021, + 0x30630040, 0x10600007, 0x3045ffff, 0x00a9102b, 0x14400005, 0x0125102b, + 0x3c068000, 0x0a000e3a, 0x00005821, 0x0125102b, 0x544000c7, 0x00006021, + 0x97420e14, 0xa7420e10, 0x97430e16, 0xa7430e12, 0x8f420e1c, 0xaf420e18, + 0xaf450e00, 0x8f420000, 0x30420008, 0x1040fffd, 0x00000000, 0x97420e08, + 0x00a04821, 0xa7820010, 0x8f430e04, 0x00003021, 0x240b0001, 0xaf830004, + 0x97620010, 0x0a000e4c, 0x304dffff, 0x8f890004, 0x97820010, 0x30420040, + 0x10400004, 0x01206821, 0x3c068000, 0x0a000e4c, 0x00005821, 0x97630010, + 0x8f820004, 0x10430003, 0x00003021, 0x0a000eee, 0x00006021, 0x240b0001, + 0x8d820000, 0x00491023, 0x1440000d, 0xad820000, 0x8f620014, 0x34420040, + 0xaf620014, 0x97430e10, 0x97420e12, 0x8f840014, 0x00031c00, 0x00431025, + 0xac8200a8, 0x8f830014, 0x8f420e18, 0xac6200ac, 0x93620008, 0x1440003e, + 0x00000000, 0x25260002, 0x8f84000c, 0x9743008a, 0x3063ffff, 0xafa30000, + 0x8fa20000, 0x00441023, 0x2442ffff, 0x30421fff, 0x2c420010, 0x1440fff7, + 0x00000000, 0x8f82000c, 0x8f830018, 0x00021082, 0x00021080, 0x24424000, + 0x03422821, 0x00605021, 0x24630001, 0x314200ff, 0x00021400, 0xaf830018, + 0x3c033200, 0x00431025, 0xaca20000, 0x93630009, 0x9362000a, 0x00031c00, + 0x00431025, 0xaca20004, 0x8f830018, 0xaca30008, 0x97820010, 0x30420008, + 0x10400002, 0x00c04021, 0x25280006, 0x97430e14, 0x93640002, 0x8f450e1c, + 0x8f660004, 0x8f670014, 0x3063ffff, 0xa7430144, 0x97420e16, 0xa7420146, + 0xaf450148, 0xa34a0152, 0x8f82000c, 0x308400ff, 0xa744015a, 0xaf460160, + 0xa7480158, 0xaf470154, 0xaf4e0178, 0x00511021, 0x30421fff, 0xaf82000c, + 0x0a000ed9, 0x8d820000, 0x93620009, 0x9363000b, 0x8f85000c, 0x2463000a, + 0x00435021, 0x25440007, 0x00992024, 0x9743008a, 0x3063ffff, 0xafa30000, + 0x8fa20000, 0x00451023, 0x2442ffff, 0x30421fff, 0x0044102b, 0x1440fff7, + 0x00000000, 0x8f82000c, 0x8f840018, 0x00021082, 0x00021080, 0x24424000, + 0x03422821, 0x00804021, 0x24840001, 0xaf840018, 0x93630009, 0x310200ff, + 0x00022400, 0x3c024100, 0x24630002, 0x00621825, 0x00832025, 0xaca40000, + 0x8f62000c, 0x00461025, 0xaca20004, 0x97430e14, 0x93640002, 0x8f450e1c, + 0x8f660004, 0x8f670014, 0x3063ffff, 0xa7430144, 0x97420e16, 0x308400ff, + 0xa7420146, 0xaf450148, 0xa3480152, 0x8f83000c, 0x25420007, 0x00591024, + 0xa744015a, 0xaf460160, 0xa7490158, 0xaf470154, 0xaf4e0178, 0x00621821, + 0x30631fff, 0xaf83000c, 0x8d820000, 0x14400005, 0x00000000, 0x8f620014, + 0x2403ffbf, 0x00431024, 0xaf620014, 0x8f62000c, 0x004d1021, 0xaf62000c, + 0x93630008, 0x14600008, 0x00000000, 0x11600006, 0x00000000, 0x8f630014, + 0x3c02efff, 0x3442fffe, 0x00621824, 0xaf630014, 0xa36b0008, 0x01206021, + 0x1580000c, 0x8fa60008, 0x97420e14, 0x97430e16, 0x8f850014, 0x00021400, + 0x00621825, 0xaca300a8, 0x8f840014, 0x8f420e1c, 0xac8200ac, 0x0a000efd, + 0x2d820001, 0x14c0fe65, 0x2d820001, 0x00501025, 0x10400058, 0x24020f00, + 0x8f830008, 0x14620023, 0x3c048000, 0x11800009, 0x3c038000, 0x97420e08, + 0x30420040, 0x14400005, 0x00000000, 0x0000000d, 0x00000000, 0x2400032c, + 0x3c038000, 0x8f420178, 0x00431024, 0x1440fffd, 0x00000000, 0x97420e10, + 0x3c030500, 0x00431025, 0xaf42014c, 0x97430e14, 0xa7430144, 0x97420e16, + 0xa7420146, 0x8f430e1c, 0x24022000, 0xaf430148, 0x3c031000, 0xa3400152, + 0xa740015a, 0xaf400160, 0xa7400158, 0xaf420154, 0xaf430178, 0x8f830008, + 0x3c048000, 0x8f420178, 0x00441024, 0x1440fffd, 0x24020f00, 0x10620016, + 0x00000000, 0x97420e14, 0xa7420144, 0x97430e16, 0xa7430146, 0x8f420e1c, + 0x3c031000, 0xaf420148, 0x0a000f51, 0x24020240, 0x97420e14, 0x97430e16, + 0x8f840014, 0x00021400, 0x00621825, 0xac8300a8, 0x8f850014, 0x8f420e1c, + 0x00006021, 0xaca200ac, 0x0a000efd, 0x2d820001, 0xaf40014c, 0x11800007, + 0x00000000, 0x97420e10, 0xa7420144, 0x97430e12, 0xa7430146, 0x0a000f4e, + 0x8f420e18, 0x97420e14, 0xa7420144, 0x97430e16, 0xa7430146, 0x8f420e1c, + 0xaf420148, 0x24020040, 0x3c031000, 0xa3400152, 0xa740015a, 0xaf400160, + 0xa7400158, 0xaf420154, 0xaf430178, 0x8fb10014, 0x8fb00010, 0x03e00008, + 0x27bd0018, 0x27bdffd0, 0x3c1a8000, 0x3c0420ff, 0x3484fffd, 0x3c020008, + 0x03421821, 0xafbf002c, 0xafb60028, 0xafb50024, 0xafb40020, 0xafb3001c, + 0xafb20018, 0xafb10014, 0xafb00010, 0xaf830014, 0xaf440e00, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x3c0200ff, 0x3442fffd, + 0x3c046004, 0xaf420e00, 0x8c835000, 0x24160800, 0x24150d00, 0x3c140800, + 0x24130f00, 0x3c120800, 0x3c114000, 0x2402ff7f, 0x00621824, 0x3463380c, + 0x24020009, 0xac835000, 0xaf420008, 0xaf800018, 0xaf80000c, 0x0e001559, + 0x00000000, 0x0e000ff0, 0x00000000, 0x3c020800, 0x245057c0, 0x8f420000, + 0x30420001, 0x1040fffd, 0x00000000, 0x8f440100, 0xaf840008, 0xaf440020, + 0xaf560178, 0x93430108, 0xa3830012, 0x93820012, 0x30420001, 0x10400008, + 0x00000000, 0x93820012, 0x30420006, 0x00021100, 0x0e000d43, 0x0050d821, + 0x0a000fac, 0x00000000, 0x14950005, 0x00000000, 0x0e000d43, 0x269b5840, + 0x0a000fac, 0x00000000, 0x14930005, 0x00000000, 0x0e000d43, 0x265b5860, + 0x0a000fac, 0x00000000, 0x0e0010ea, 0x00000000, 0xaf510138, 0x0a000f89, + 0x00000000, 0x27bdfff8, 0x3084ffff, 0x24820007, 0x3044fff8, 0x8f85000c, + 0x9743008a, 0x3063ffff, 0xafa30000, 0x8fa20000, 0x00451023, 0x2442ffff, + 0x30421fff, 0x0044102b, 0x1440fff7, 0x00000000, 0x8f82000c, 0x00021082, + 0x00021080, 0x24424000, 0x03421021, 0x03e00008, 0x27bd0008, 0x3084ffff, + 0x8f82000c, 0x24840007, 0x3084fff8, 0x00441021, 0x30421fff, 0xaf82000c, + 0x03e00008, 0x00000000, 0x27bdffe8, 0x3c1a8000, 0x3c0420ff, 0x3484fffd, + 0x3c020008, 0x03421821, 0xafbf0010, 0xaf830014, 0xaf440e00, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x3c0200ff, 0x3442fffd, + 0x3c046004, 0xaf420e00, 0x8c825000, 0x2403ff7f, 0x00431024, 0x3442380c, + 0x24030009, 0xac825000, 0xaf430008, 0xaf800018, 0xaf80000c, 0x0e001559, + 0x00000000, 0x0e000ff0, 0x00000000, 0x8fbf0010, 0x03e00008, 0x27bd0018, + 0x27bdffe8, 0x3c02000a, 0x03421821, 0x3c040800, 0x24845880, 0x24050019, + 0xafbf0010, 0xaf830024, 0x0e001565, 0x00003021, 0x3c050800, 0x3c020800, + 0x24425330, 0xaca258e8, 0x24a558e8, 0x3c020800, 0x244254f8, 0x3c030800, + 0x2463550c, 0x3c040800, 0xaca20004, 0x3c020800, 0x24425338, 0xaca30008, + 0xac825900, 0x24845900, 0x3c020800, 0x244253c4, 0x3c070800, 0x24e75404, + 0x3c060800, 0x24c65520, 0x3c050800, 0x24a55438, 0x3c030800, 0xac820004, + 0x3c020800, 0x24425528, 0xac870008, 0xac86000c, 0xac850010, 0xac625920, + 0x24635920, 0x8fbf0010, 0x3c020800, 0x24425540, 0xac620004, 0x3c020800, + 0xac670008, 0xac66000c, 0xac650010, 0xac400048, 0x03e00008, 0x27bd0018, + 0x974309da, 0x00804021, 0xad030000, 0x8f4209dc, 0xad020004, 0x8f4309e0, + 0xad030008, 0x934409d9, 0x24020001, 0x30840003, 0x1082001f, 0x30a900ff, + 0x28820002, 0x10400005, 0x24020002, 0x10800009, 0x3c0a0800, 0x0a001078, + 0x93420934, 0x1082000b, 0x24020003, 0x10820026, 0x3c0a0800, 0x0a001078, + 0x93420934, 0x974209e4, 0x00021400, 0x34420800, 0xad02000c, 0x0a001077, + 0x25080010, 0x974209e4, 0x00021400, 0x34428100, 0xad02000c, 0x974309e8, + 0x3c0a0800, 0x00031c00, 0x34630800, 0xad030010, 0x0a001077, 0x25080014, + 0x974409e4, 0x3c050800, 0x24a25880, 0x9443001c, 0x94460014, 0x94470010, + 0x00a05021, 0x24020800, 0xad000010, 0xad020014, 0x00042400, 0x00661821, + 0x00671823, 0x2463fff2, 0x00832025, 0xad04000c, 0x0a001077, 0x25080018, + 0x974209e4, 0x3c050800, 0x00021400, 0x34428100, 0xad02000c, 0x974409e8, + 0x24a25880, 0x9443001c, 0x94460014, 0x94470010, 0x00a05021, 0x24020800, + 0xad000014, 0xad020018, 0x00042400, 0x00661821, 0x00671823, 0x2463ffee, + 0x00832025, 0xad040010, 0x2508001c, 0x93420934, 0x93450921, 0x3c074000, + 0x25445880, 0x94830018, 0x94860014, 0x00021082, 0x00021600, 0x00052c00, + 0x00a72825, 0x00451025, 0x00661821, 0x00431025, 0xad020000, 0x9783002c, + 0x974209ea, 0x00621821, 0x00031c00, 0xad030004, 0x9782002c, 0x24420001, + 0x30427fff, 0xa782002c, 0x93430920, 0x3c020006, 0x00031e00, 0x00621825, + 0xad030008, 0x8f42092c, 0xad02000c, 0x8f430930, 0xad030010, 0x8f440938, + 0x25080014, 0xad040000, 0x8f820020, 0x11200004, 0xad020004, 0x8f420940, + 0x0a0010a1, 0x2442ffff, 0x8f420940, 0xad020008, 0x8f440948, 0x8f420940, + 0x93430936, 0x00823023, 0x00663006, 0x3402ffff, 0x0046102b, 0x54400001, + 0x3406ffff, 0x93420937, 0x25445880, 0x90830024, 0xad000010, 0x00021700, + 0x34630010, 0x00031c00, 0x00431025, 0x00461025, 0xad02000c, 0x8c830008, + 0x14600031, 0x25080014, 0x3c020800, 0x8c430048, 0x1060002d, 0x00000000, + 0x9342010b, 0xad020000, 0x8f830000, 0x8c6200b0, 0xad020004, 0x8f830000, + 0x8c6200b4, 0xad020008, 0x8f830000, 0x8c6200c0, 0xad02000c, 0x8f830000, + 0x8c6200c4, 0xad020010, 0x8f830000, 0x8c6200c8, 0xad020014, 0x8f830000, + 0x8c6200cc, 0xad020018, 0x8f830000, 0x8c6200e0, 0xad02001c, 0x8f830000, + 0x8c6200e8, 0xad020020, 0x8f830000, 0x8c6200f0, 0x3c04600e, 0xad020024, + 0x8c8200d0, 0xad020028, 0x8c8300d4, 0xad03002c, 0x8f820028, 0x3c046012, + 0xad020030, 0x8c8200a8, 0xad020034, 0x8c8300ac, 0x3c026000, 0xad030038, + 0x8c434448, 0xad03003c, 0x03e00008, 0x01001021, 0x27bdffa8, 0x3c020008, + 0x03423021, 0xafbf0054, 0xafbe0050, 0xafb7004c, 0xafb60048, 0xafb50044, + 0xafb40040, 0xafb3003c, 0xafb20038, 0xafb10034, 0xafb00030, 0xaf860000, + 0x24020040, 0xaf420814, 0xaf400810, 0x8f420944, 0x8f430950, 0x8f440954, + 0x8f45095c, 0xaf820034, 0xaf830020, 0xaf84001c, 0xaf850030, 0x90c20000, + 0x24030020, 0x304400ff, 0x10830005, 0x24020030, 0x10820022, 0x3c030800, + 0x0a001139, 0x8c62002c, 0x24020088, 0xaf420818, 0x3c020800, 0x244258e8, + 0xafa20020, 0x93430109, 0x3c020800, 0x10600009, 0x24575900, 0x3c026000, + 0x24030100, 0xac43081c, 0x3c030001, 0xac43081c, 0x0000000d, 0x00000000, + 0x24000376, 0x9342010a, 0x30420080, 0x14400021, 0x24020800, 0x3c026000, + 0x24030100, 0xac43081c, 0x3c030001, 0xac43081c, 0x0000000d, 0x00000000, + 0x2400037d, 0x0a001141, 0x24020800, 0x93430109, 0x3063007f, 0x00031140, + 0x000318c0, 0x00431021, 0x24430088, 0xaf430818, 0x0000000d, 0x3c020800, + 0x24425940, 0x3c030800, 0x24775950, 0x0a001140, 0xafa20020, 0x24420001, + 0xac62002c, 0x0000000d, 0x00000000, 0x24000395, 0x0a0014c1, 0x8fbf0054, + 0x24020800, 0xaf420178, 0x8f450104, 0x8f420988, 0x00a21023, 0x58400005, + 0x8f4309a0, 0x0000000d, 0x00000000, 0x240003b1, 0x8f4309a0, 0x3c100800, + 0xae0358b0, 0x8f4209a4, 0x8f830020, 0x260458b0, 0x2491ffd0, 0xae220034, + 0x00a21023, 0xae230028, 0xac82ffd0, 0x8fa30020, 0x8c620000, 0x0040f809, + 0x0200b021, 0x00409021, 0x32440010, 0x32420002, 0x10400007, 0xafa40024, + 0x8e220020, 0x32530040, 0x2403ffbf, 0x00431024, 0x0a001493, 0xae220020, + 0x32420020, 0x10400002, 0x3c020800, 0x24575920, 0x32420001, 0x14400007, + 0x00000000, 0x8f820008, 0xaf420080, 0x8ec358b0, 0xaf430e10, 0x8e220034, + 0xaf420e18, 0x9343010b, 0x93420905, 0x30420008, 0x1040003c, 0x307400ff, + 0x8f820000, 0x8c430074, 0x0460000a, 0x00000000, 0x3c026000, 0x24030100, + 0xac43081c, 0x3c030001, 0xac43081c, 0x0000000d, 0x00000000, 0x240003ed, + 0x8f820000, 0x9044007b, 0x9343010a, 0x14830027, 0x32530040, 0x00003821, + 0x24052000, 0x3c090800, 0x3c038000, 0x8f420178, 0x00431024, 0x1440fffd, + 0x8ec258b0, 0x26c458b0, 0x2484ffd0, 0xaf420144, 0x8c820034, 0x3c030100, + 0xaf420148, 0x24020047, 0xaf43014c, 0xa3420152, 0x8d230030, 0x3c021000, + 0xa7470158, 0xaf450154, 0xaf420178, 0x8c860034, 0x24630001, 0xad230030, + 0x9342010a, 0x3c030047, 0xafa50014, 0x00021600, 0x00431025, 0x00471025, + 0xafa20010, 0x9343010b, 0xafa30018, 0x8f440100, 0x8f450104, 0x0e00159b, + 0x3c070100, 0x3c050800, 0x24a25880, 0x0a001250, 0x8c430020, 0x32820002, + 0x10400050, 0x00000000, 0x0e0015b9, 0x32530040, 0x3c039000, 0x34630001, + 0x8f820008, 0x3c048000, 0x00431025, 0xaf420020, 0x8f420020, 0x00441024, + 0x1440fffd, 0x00000000, 0x8f830000, 0x90620005, 0x34420008, 0xa0620005, + 0x8f840000, 0x8c820074, 0x3c038000, 0x00431025, 0xac820074, 0x90830000, + 0x24020020, 0x10620004, 0x00000000, 0x0000000d, 0x00000000, 0x2400040b, + 0x8f830008, 0x3c028000, 0x34420001, 0x00621825, 0xaf430020, 0x9084007b, + 0x9342010a, 0x14820028, 0x3c030800, 0x00003821, 0x24052000, 0x3c090800, + 0x3c038000, 0x8f420178, 0x00431024, 0x1440fffd, 0x8ec258b0, 0x26c458b0, + 0x2484ffd0, 0xaf420144, 0x8c820034, 0x3c030100, 0xaf420148, 0x24020046, + 0xaf43014c, 0xa3420152, 0x8d230030, 0x3c021000, 0xa7470158, 0xaf450154, + 0xaf420178, 0x8c860034, 0x24630001, 0xad230030, 0x9342010a, 0x3c030046, + 0xafa50014, 0x00021600, 0x00431025, 0x00471025, 0xafa20010, 0x9343010b, + 0xafa30018, 0x8f440100, 0x8f450104, 0x0e00159b, 0x3c070100, 0x3c030800, + 0x24625880, 0x0a001250, 0x8c430020, 0x93420108, 0x30420010, 0x50400056, + 0x9343093f, 0x8f860000, 0x90c2007f, 0x8cc30178, 0x304800ff, 0x15030004, + 0x00000000, 0x0000000d, 0x00000000, 0x24000425, 0x90c2007e, 0x90c40080, + 0x00081c00, 0x00021600, 0x00431025, 0x00042200, 0x90c3007a, 0x90c5000a, + 0x00441025, 0x11050028, 0x00623825, 0xa0c8000a, 0x00004021, 0x24056000, + 0x3c090800, 0x3c038000, 0x8f420178, 0x00431024, 0x1440fffd, 0x8ec258b0, + 0x26c458b0, 0x2484ffd0, 0xaf420144, 0x8c820034, 0xaf420148, 0x24020052, + 0xaf47014c, 0xa3420152, 0x8d230030, 0x3c021000, 0xa7480158, 0xaf450154, + 0xaf420178, 0x8c860034, 0x24630001, 0xad230030, 0x9342010a, 0x3c030052, + 0xafa50014, 0x00021600, 0x00431025, 0x00481025, 0xafa20010, 0x9343010b, + 0xafa30018, 0x8f440100, 0x0e00159b, 0x8f450104, 0x0a00124a, 0x00000000, + 0x3c026000, 0x24030100, 0xac43081c, 0x3c030001, 0xac43081c, 0x0000000d, + 0x00000000, 0x2400043e, 0x16800009, 0x3c050800, 0x3c040800, 0x24825880, + 0x8c430020, 0x32530040, 0x2404ffbf, 0x00641824, 0x0a001493, 0xac430020, + 0x8ca25880, 0x10400005, 0x3c030800, 0x8c620034, 0xaca05880, 0x24420001, + 0xac620034, 0x9343093f, 0x24020012, 0x5462000e, 0x97420908, 0x32820038, + 0x14400009, 0x3c030800, 0x8f830000, 0x8c62004c, 0xac62005c, 0x3c020800, + 0x24445880, 0x8c820020, 0x0a001285, 0x32530040, 0xac605880, 0x97420908, + 0x5440001c, 0x97420908, 0x3c039000, 0x34630001, 0x8f820008, 0x32530040, + 0x3c048000, 0x00431025, 0xaf420020, 0x8f420020, 0x00441024, 0x1440fffd, + 0x3c028000, 0x8f840000, 0x8f850008, 0x8c830050, 0x34420001, 0x00a22825, + 0xaf830020, 0xac830070, 0xac83005c, 0xaf450020, 0x3c050800, 0x24a45880, + 0x8c820020, 0x2403ffbf, 0x00431024, 0x0a001493, 0xac820020, 0x000211c0, + 0xaf420024, 0x97420908, 0x3c030080, 0x34630003, 0x000211c0, 0xaf42080c, + 0xaf43081c, 0x974209ec, 0x8f4309a4, 0xa782002c, 0x3c020800, 0x24445880, + 0xac83002c, 0x93420937, 0x93430934, 0x00021080, 0x00621821, 0xa4830018, + 0x934209d8, 0x32850038, 0xafa50028, 0x00621821, 0xa483001a, 0x934209d8, + 0x93430934, 0x3c1e0800, 0x00809821, 0x00431021, 0x24420010, 0xa4820016, + 0x24020006, 0xae620020, 0x8fa20028, 0x10400003, 0x0000a821, 0x0a0012f0, + 0x24120008, 0x8f420958, 0x8f830020, 0x8f840030, 0x00431023, 0x00832023, + 0x04800003, 0xae620004, 0x04410003, 0x0082102b, 0x0a0012bc, 0xae600004, + 0x54400001, 0xae640004, 0x8ee20000, 0x0040f809, 0x00000000, 0x00409021, + 0x32420001, 0x5440001e, 0x8ee20004, 0x8e630008, 0x1060002b, 0x3c02c000, + 0x00621025, 0xaf420e00, 0x8f420000, 0x30420008, 0x1040fffd, 0x00000000, + 0x97420e08, 0xa7820010, 0x8f430e04, 0x8e620008, 0xaf830004, 0x8f840004, + 0x0044102b, 0x1040000b, 0x24150001, 0x24020100, 0x3c016000, 0xac22081c, + 0x3c020001, 0x3c016000, 0xac22081c, 0x0000000d, 0x00000000, 0x240004cd, + 0x24150001, 0x8ee20004, 0x0040f809, 0x00000000, 0x02429025, 0x32420002, + 0x5040001d, 0x8f470940, 0x12a00006, 0x8ec258b0, 0x8f830000, 0xac6200a8, + 0x8f840000, 0x8e620034, 0xac8200ac, 0x32420004, 0x50400013, 0x8f470940, + 0x3c020800, 0x3283007d, 0x10600110, 0x24575920, 0x32820001, 0x50400006, + 0x36520002, 0x8f830034, 0x8f420940, 0x10620109, 0x00000000, 0x36520002, + 0x24020008, 0xa6600010, 0xa6620012, 0xae600008, 0xa2600024, 0x8f470940, + 0x3c030800, 0x24685880, 0x8d02002c, 0x8d050008, 0x95040010, 0x9506000a, + 0x95030026, 0x00451021, 0x00862021, 0x00641821, 0xaf870034, 0xad02002c, + 0x32820030, 0x10400008, 0xa5030014, 0x91020024, 0x32910040, 0x34420004, + 0xa1020024, 0xaf400048, 0x0a001345, 0x3c040800, 0x93420923, 0x30420002, + 0x10400029, 0x32910040, 0x8f830000, 0x8f840020, 0x8c620084, 0x00441023, + 0x0442000a, 0x3c039000, 0x95020014, 0x8c630084, 0x00821021, 0x00621823, + 0x1c600004, 0x3c039000, 0x91020024, 0x34420001, 0xa1020024, 0x34630001, + 0x8f820008, 0x32910040, 0x3c048000, 0x00431025, 0xaf420020, 0x8f420020, + 0x00441024, 0x1440fffd, 0x00000000, 0x8f840000, 0x9083003f, 0x2402000a, + 0x10620005, 0x2402000c, 0x9083003f, 0x24020008, 0x14620002, 0x24020014, + 0xa082003f, 0x8f830008, 0x3c028000, 0x34420001, 0x00621825, 0xaf430020, + 0x3c040800, 0x24865880, 0x94c20010, 0x94c3001a, 0x8cc40008, 0x00432821, + 0x14800006, 0xa4c5001c, 0x3c020800, 0x8c430048, 0x10600002, 0x24a20040, + 0xa4c2001c, 0x27d05880, 0x9604001c, 0x96020012, 0x00822021, 0x24840002, + 0x0e000faf, 0x3084ffff, 0x8f850018, 0x00a01821, 0xa2030025, 0x8ee60008, + 0x00402021, 0x24a50001, 0xaf850018, 0x00c0f809, 0x00000000, 0x00402021, + 0x0e001026, 0x02202821, 0x8ee3000c, 0x0060f809, 0x00402021, 0x9604001c, + 0x96020012, 0x00822021, 0x24840002, 0x0e000fc5, 0x3084ffff, 0x8fc25880, + 0x8e030008, 0x00431023, 0x14400012, 0xafc25880, 0x54600006, 0x8e020020, + 0x3243004a, 0x24020002, 0x14620005, 0x00000000, 0x8e020020, 0x34420040, + 0x0a001382, 0xae020020, 0x52a00006, 0x36520002, 0x8e020030, 0xaf420e10, + 0x8e030034, 0xaf430e18, 0x36520002, 0x52a00008, 0x96670014, 0x8f830000, + 0x8f420e10, 0xac6200a8, 0x8f840000, 0x8f420e18, 0xac8200ac, 0x96670014, + 0x92680024, 0x24020040, 0xaf420814, 0x8f830020, 0x8f82001c, 0x00671821, + 0x00621023, 0xaf830020, 0x18400008, 0x00000000, 0x8f820000, 0xaf83001c, + 0xac430054, 0x54e00005, 0xaf400040, 0x0a0013a0, 0x8f42095c, 0x54e00001, + 0xaf400044, 0x8f42095c, 0x31030008, 0xaf820030, 0x1060001a, 0x00000000, + 0x8f840000, 0x90820120, 0x90830121, 0x304600ff, 0x00c31823, 0x30630007, + 0x24020007, 0x1062000e, 0x00000000, 0x90820122, 0x304200fe, 0xa0820122, + 0x8f850000, 0x00061880, 0x8f840020, 0x24a20100, 0x00431021, 0x24c30001, + 0x30630007, 0xac440000, 0x0a0013bd, 0xa0a30120, 0x90820122, 0x34420001, + 0xa0820122, 0x14e00003, 0x31020001, 0x10400031, 0x32510002, 0x8f820000, + 0x8c43000c, 0x30630001, 0x1060002c, 0x32510002, 0x3c029000, 0x8f830008, + 0x34420001, 0x3c048000, 0x00621825, 0xaf430020, 0x8f420020, 0x00441024, + 0x1440fffd, 0x00000000, 0x8f870000, 0x8ce2000c, 0x30420001, 0x10400018, + 0x00000000, 0x94e2006a, 0x00022880, 0x50a00001, 0x24050001, 0x94e30068, + 0x90e40081, 0x3c020800, 0x8c460024, 0x00652821, 0x00852804, 0x00c5102b, + 0x54400001, 0x00a03021, 0x3c020800, 0x8c440028, 0x00c4182b, 0x54600001, + 0x00c02021, 0x8f430074, 0x2402fffe, 0x00822824, 0x00a31821, 0xace3000c, + 0x8f830008, 0x3c028000, 0x34420001, 0x00621825, 0xaf430020, 0x8f820020, + 0x3c050800, 0x24b05880, 0xae020028, 0x8ee30010, 0x0060f809, 0x00000000, + 0x8f820028, 0x24420001, 0xaf820028, 0x12a00005, 0xaf40004c, 0x8f420e10, + 0xae020030, 0x8f430e18, 0xae030034, 0x1220fea7, 0x24020006, 0x8f870024, + 0x9786002c, 0x8f830000, 0x8f820034, 0x8f840020, 0x8f85001c, 0x32530040, + 0xa4e6002c, 0xac620044, 0x32420008, 0xac640050, 0xac650054, 0x1040007a, + 0x32820020, 0x10400027, 0x32910010, 0x00003821, 0x24052000, 0x3c090800, + 0x3c038000, 0x8f420178, 0x00431024, 0x1440fffd, 0x8ec258b0, 0x26c458b0, + 0x2484ffd0, 0xaf420144, 0x8c820034, 0x3c030400, 0xaf420148, 0x24020041, + 0xaf43014c, 0xa3420152, 0x8d230030, 0x3c021000, 0xa7470158, 0xaf450154, + 0xaf420178, 0x8c860034, 0x24630001, 0xad230030, 0x9342010a, 0x3c030041, + 0xafa50014, 0x00021600, 0x00431025, 0x00471025, 0xafa20010, 0x9343010b, + 0xafa30018, 0x8f440100, 0x8f450104, 0x0e00159b, 0x3c070400, 0x12200028, + 0x00003821, 0x24052000, 0x3c090800, 0x3c038000, 0x8f420178, 0x00431024, + 0x1440fffd, 0x8ec258b0, 0x26c458b0, 0x2484ffd0, 0xaf420144, 0x8c820034, + 0x3c030300, 0xaf420148, 0x2402004e, 0xaf43014c, 0xa3420152, 0x8d230030, + 0x3c021000, 0xa7470158, 0xaf450154, 0xaf420178, 0x8c860034, 0x24630001, + 0xad230030, 0x9342010a, 0x3c03004e, 0xafa50014, 0x00021600, 0x00431025, + 0x00471025, 0xafa20010, 0x9343010b, 0xafa30018, 0x8f440100, 0x8f450104, + 0x0e00159b, 0x3c070300, 0x0a00148b, 0x8fa20024, 0x32820008, 0x10400026, + 0x24052000, 0x00003821, 0x3c090800, 0x3c038000, 0x8f420178, 0x00431024, + 0x1440fffd, 0x8ec258b0, 0x26c458b0, 0x2484ffd0, 0xaf420144, 0x8c820034, + 0x3c030200, 0xaf420148, 0x2402004b, 0xaf43014c, 0xa3420152, 0x8d230030, + 0x3c021000, 0xa7470158, 0xaf450154, 0xaf420178, 0x8c860034, 0x24630001, + 0xad230030, 0x9342010a, 0x3c03004b, 0xafa50014, 0x00021600, 0x00431025, + 0x00471025, 0xafa20010, 0x9343010b, 0xafa30018, 0x8f440100, 0x8f450104, + 0x0e00159b, 0x3c070200, 0x8fa20024, 0x14400004, 0x8fa30020, 0x32420010, + 0x10400004, 0x00000000, 0x8c620004, 0x0040f809, 0x00000000, 0x12600006, + 0x8fa40020, 0x8c820008, 0x0040f809, 0x00000000, 0x0a0014c1, 0x8fbf0054, + 0x3c030800, 0x8c6258a0, 0x30420040, 0x14400023, 0x8fbf0054, 0x00002821, + 0x24040040, 0x8f870020, 0x3c038000, 0x8f420178, 0x00431024, 0x1440fffd, + 0x8ec258b0, 0x26c358b0, 0x2463ffd0, 0xaf420144, 0x8c620034, 0xaf420148, + 0x24020049, 0xaf47014c, 0xa3420152, 0x3c021000, 0xa7450158, 0xaf440154, + 0xaf420178, 0x8c660034, 0x9342010a, 0x3c030049, 0xafa40014, 0x00021600, + 0x00431025, 0x00451025, 0xafa20010, 0x9343010b, 0xafa30018, 0x8f440100, + 0x0e00159b, 0x8f450104, 0x8fbf0054, 0x8fbe0050, 0x8fb7004c, 0x8fb60048, + 0x8fb50044, 0x8fb40040, 0x8fb3003c, 0x8fb20038, 0x8fb10034, 0x8fb00030, + 0x03e00008, 0x27bd0058, 0x03e00008, 0x00001021, 0x3c020800, 0x24435880, + 0x8c650004, 0x8c445880, 0x0085182b, 0x10600002, 0x00403021, 0x00802821, + 0x9744093c, 0x00a4102b, 0x54400001, 0x00a02021, 0x93420923, 0x0004182b, + 0x00021042, 0x30420001, 0x00431024, 0x1040000d, 0x24c25880, 0x8f850000, + 0x8f830020, 0x8ca20084, 0x00431023, 0x04420007, 0x24c25880, 0x8ca20084, + 0x00641821, 0x00431023, 0x28420001, 0x00822023, 0x24c25880, 0xac440008, + 0xa4400026, 0x03e00008, 0x00001021, 0x8f850004, 0x97840010, 0x3c030800, + 0x24635880, 0x24020008, 0xa4620012, 0x8f820004, 0xa4600010, 0x000420c2, + 0x30840008, 0x2c420001, 0x00021023, 0x30420006, 0xac650008, 0x03e00008, + 0xa0640024, 0x3c020800, 0x24425880, 0x90450025, 0x9443001c, 0x3c021100, + 0xac800004, 0x00052c00, 0x24630002, 0x00621825, 0x00a32825, 0x24820008, + 0x03e00008, 0xac850000, 0x27bdffd8, 0x3c020800, 0x24425880, 0xafbf0020, + 0x90480025, 0x8c440008, 0x8c460020, 0x8f870020, 0x3c030800, 0x3c058000, + 0x8f420178, 0x00451024, 0x1440fffd, 0x8c6258b0, 0x246358b0, 0x2469ffd0, + 0xaf420144, 0x8d220034, 0x30c32000, 0xaf420148, 0x3c021000, 0xaf47014c, + 0xa3480152, 0xa7440158, 0xaf460154, 0xaf420178, 0x10600004, 0x3c030800, + 0x8c620030, 0x24420001, 0xac620030, 0x9342010a, 0x00081c00, 0x3084ffff, + 0xafa60014, 0x00021600, 0x00431025, 0x00441025, 0xafa20010, 0x9343010b, + 0xafa30018, 0x8f440100, 0x8f450104, 0x0e00159b, 0x8d260034, 0x8fbf0020, + 0x03e00008, 0x27bd0028, 0x0000000d, 0x00000000, 0x2400019d, 0x03e00008, + 0x00000000, 0x0000000d, 0x00000000, 0x240001a9, 0x03e00008, 0x00000000, + 0x03e00008, 0x00000000, 0x3c020800, 0x24425880, 0xac400008, 0xa4400026, + 0x03e00008, 0x24020001, 0x3c020800, 0x24425880, 0x24030008, 0xac400008, + 0xa4400010, 0xa4430012, 0xa0400024, 0x03e00008, 0x24020004, 0x03e00008, + 0x00001021, 0x10c00007, 0x00000000, 0x8ca20000, 0x24c6ffff, 0x24a50004, + 0xac820000, 0x14c0fffb, 0x24840004, 0x03e00008, 0x00000000, 0x0a00156c, + 0x00a01021, 0xac860000, 0x00000000, 0x00000000, 0x24840004, 0x00a01021, + 0x1440fffa, 0x24a5ffff, 0x03e00008, 0x00000000, 0x3c0a0800, 0x8d490068, + 0x3c050800, 0x24a52098, 0x00093140, 0x00c51021, 0xac440000, 0x8f440e04, + 0x00a61021, 0xac440004, 0x97430e08, 0x97420e0c, 0x00a62021, 0x00031c00, + 0x00431025, 0xac820008, 0x8f430e10, 0x00801021, 0xac43000c, 0x8f440e14, + 0xac440010, 0x8f430e18, 0x3c0800ff, 0xac430014, 0x8f470e1c, 0x3508ffff, + 0x25290001, 0xac470018, 0x3c070800, 0x8ce3006c, 0x9344010a, 0x3c026000, + 0x24630001, 0xace3006c, 0x8c434448, 0x3129007f, 0x00a62821, 0xad490068, + 0x00042600, 0x00681824, 0x00832025, 0x03e00008, 0xaca4001c, 0x8fac0010, + 0x8fad0014, 0x8fae0018, 0x3c0b0800, 0x8d6a0060, 0x3c080800, 0x25080080, + 0x000a4940, 0x01281021, 0x01091821, 0xac440000, 0x00601021, 0xac650004, + 0xac460008, 0xac67000c, 0xac4c0010, 0xac6d0014, 0x3c036000, 0xac4e0018, + 0x8c654448, 0x3c040800, 0x8c820064, 0x254a0001, 0x314a00ff, 0x01094021, + 0xad6a0060, 0x24420001, 0xac820064, 0x03e00008, 0xad05001c, 0x3c030800, + 0x3c090800, 0x8d250070, 0x246330b0, 0x8f460100, 0x00053900, 0x00e31021, + 0xac460000, 0x8f440104, 0x00671021, 0xac440004, 0x8f460108, 0x8f840014, + 0x24a50001, 0xac460008, 0x8c880074, 0x3c060800, 0x8cc20074, 0x30a5003f, + 0x00671821, 0xad250070, 0x24420001, 0xacc20074, 0x03e00008, 0xac68000c, + 0x00000000 }; + +static u32 bnx2_TXP_b06FwData[(0x0/4) + 1] = { 0x0 }; +static u32 bnx2_TXP_b06FwRodata[(0x0/4) + 1] = { 0x0 }; +static u32 bnx2_TXP_b06FwBss[(0x1c4/4) + 1] = { 0x0 }; +static u32 bnx2_TXP_b06FwSbss[(0x38/4) + 1] = { 0x0 }; diff --git a/gpxe/src/drivers/net/cs89x0.c b/gpxe/src/drivers/net/cs89x0.c new file mode 100644 index 0000000..1f647a8 --- /dev/null +++ b/gpxe/src/drivers/net/cs89x0.c @@ -0,0 +1,737 @@ +#ifdef ALLMULTI +#error multicast support is not yet implemented +#endif + +/** + Per an email message from Russ Nelson on + 18 March 2008 this file is now licensed under GPL Version 2. + + From: Russ Nelson + Date: Tue, 18 Mar 2008 12:42:00 -0400 + Subject: Re: [Etherboot-developers] cs89x0 driver in etherboot + -- quote from email + As copyright holder, if I say it doesn't conflict with the GPL, + then it doesn't conflict with the GPL. + + However, there's no point in causing people's brains to overheat, + so yes, I grant permission for the code to be relicensed under the + GPLv2. Please make sure that this change in licensing makes its + way upstream. -russ + -- quote from email +**/ + +/* cs89x0.c: A Crystal Semiconductor CS89[02]0 driver for etherboot. */ +/* + Permission is granted to distribute the enclosed cs89x0.[ch] driver + only in conjunction with the Etherboot package. The code is + ordinarily distributed under the GPL. + + Russ Nelson, January 2000 + + ChangeLog: + + Thu Dec 6 22:40:00 1996 Markus Gutschke + + * 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 +#include +#include "etherboot.h" +#include "nic.h" +#include +#include "console.h" +#include "cs89x0.h" + +static unsigned short eth_nic_base; +static unsigned long eth_mem_start; +static unsigned short eth_irqno; +static unsigned short eth_cs_type; /* one of: CS8900, CS8920, CS8920M */ +static unsigned short eth_auto_neg_cnf; +static unsigned short eth_adapter_cnf; +static unsigned short eth_linectl; + +/************************************************************************* + CS89x0 - specific routines +**************************************************************************/ + +static inline int readreg(int portno) +{ + outw(portno, eth_nic_base + ADD_PORT); + return inw(eth_nic_base + DATA_PORT); +} + +static inline void writereg(int portno, int value) +{ + outw(portno, eth_nic_base + ADD_PORT); + outw(value, eth_nic_base + DATA_PORT); + return; +} + +/************************************************************************* +EEPROM access +**************************************************************************/ + +static int wait_eeprom_ready(void) +{ + unsigned long tmo = currticks() + 4*TICKS_PER_SEC; + + /* check to see if the EEPROM is ready, a timeout is used - + just in case EEPROM is ready when SI_BUSY in the + PP_SelfST is clear */ + while(readreg(PP_SelfST) & SI_BUSY) { + if (currticks() >= tmo) + return -1; } + return 0; +} + +static int get_eeprom_data(int off, int len, unsigned short *buffer) +{ + int i; + +#ifdef EDEBUG + printf("\ncs: EEPROM data from %hX for %hX:",off,len); +#endif + for (i = 0; i < len; i++) { + if (wait_eeprom_ready() < 0) + return -1; + /* Now send the EEPROM read command and EEPROM location + to read */ + writereg(PP_EECMD, (off + i) | EEPROM_READ_CMD); + if (wait_eeprom_ready() < 0) + return -1; + buffer[i] = readreg(PP_EEData); +#ifdef EDEBUG + if (!(i%10)) + printf("\ncs: "); + printf("%hX ", buffer[i]); +#endif + } +#ifdef EDEBUG + putchar('\n'); +#endif + + return(0); +} + +static int get_eeprom_chksum(int off __unused, int len, unsigned short *buffer) +{ + int i, cksum; + + cksum = 0; + for (i = 0; i < len; i++) + cksum += buffer[i]; + cksum &= 0xffff; + if (cksum == 0) + return 0; + return -1; +} + +/************************************************************************* +Activate all of the available media and probe for network +**************************************************************************/ + +static void clrline(void) +{ + int i; + + putchar('\r'); + for (i = 79; i--; ) putchar(' '); + printf("\rcs: "); + return; +} + +static void control_dc_dc(int on_not_off) +{ + unsigned int selfcontrol; + unsigned long tmo = currticks() + TICKS_PER_SEC; + + /* control the DC to DC convertor in the SelfControl register. */ + selfcontrol = HCB1_ENBL; /* Enable the HCB1 bit as an output */ + if (((eth_adapter_cnf & A_CNF_DC_DC_POLARITY) != 0) ^ on_not_off) + selfcontrol |= HCB1; + else + selfcontrol &= ~HCB1; + writereg(PP_SelfCTL, selfcontrol); + + /* Wait for the DC/DC converter to power up - 1000ms */ + while (currticks() < tmo); + + return; +} + +static int detect_tp(void) +{ + unsigned long tmo; + + /* Turn on the chip auto detection of 10BT/ AUI */ + + clrline(); printf("attempting %s:","TP"); + + /* If connected to another full duplex capable 10-Base-T card + the link pulses seem to be lost when the auto detect bit in + the LineCTL is set. To overcome this the auto detect bit + will be cleared whilst testing the 10-Base-T interface. + This would not be necessary for the sparrow chip but is + simpler to do it anyway. */ + writereg(PP_LineCTL, eth_linectl &~ AUI_ONLY); + control_dc_dc(0); + + /* Delay for the hardware to work out if the TP cable is + present - 150ms */ + for (tmo = currticks() + 4; currticks() < tmo; ); + + if ((readreg(PP_LineST) & LINK_OK) == 0) + return 0; + + if (eth_cs_type != CS8900) { + + writereg(PP_AutoNegCTL, eth_auto_neg_cnf & AUTO_NEG_MASK); + + if ((eth_auto_neg_cnf & AUTO_NEG_BITS) == AUTO_NEG_ENABLE) { + printf(" negotiating duplex... "); + while (readreg(PP_AutoNegST) & AUTO_NEG_BUSY) { + if (currticks() - tmo > 40*TICKS_PER_SEC) { + printf("time out "); + break; + } + } + } + if (readreg(PP_AutoNegST) & FDX_ACTIVE) + printf("using full duplex"); + else + printf("using half duplex"); + } + + return A_CNF_MEDIA_10B_T; +} + +/* send a test packet - return true if carrier bits are ok */ +static int send_test_pkt(struct nic *nic) +{ + static unsigned char testpacket[] = { 0,0,0,0,0,0, 0,0,0,0,0,0, + 0, 46, /*A 46 in network order */ + 0, 0, /*DSAP=0 & SSAP=0 fields */ + 0xf3,0 /*Control (Test Req+P bit set)*/ }; + unsigned long tmo; + + writereg(PP_LineCTL, readreg(PP_LineCTL) | SERIAL_TX_ON); + + memcpy(testpacket, nic->node_addr, ETH_ALEN); + memcpy(testpacket+ETH_ALEN, nic->node_addr, ETH_ALEN); + + outw(TX_AFTER_ALL, eth_nic_base + TX_CMD_PORT); + outw(ETH_ZLEN, eth_nic_base + TX_LEN_PORT); + + /* Test to see if the chip has allocated memory for the packet */ + for (tmo = currticks() + 2; + (readreg(PP_BusST) & READY_FOR_TX_NOW) == 0; ) + if (currticks() >= tmo) + return(0); + + /* Write the contents of the packet */ + outsw(eth_nic_base + TX_FRAME_PORT, testpacket, + (ETH_ZLEN+1)>>1); + + printf(" sending test packet "); + /* wait a couple of timer ticks for packet to be received */ + for (tmo = currticks() + 2; currticks() < tmo; ); + + if ((readreg(PP_TxEvent) & TX_SEND_OK_BITS) == TX_OK) { + printf("succeeded"); + return 1; + } + printf("failed"); + return 0; +} + + +static int detect_aui(struct nic *nic) +{ + clrline(); printf("attempting %s:","AUI"); + control_dc_dc(0); + + writereg(PP_LineCTL, (eth_linectl & ~AUTO_AUI_10BASET) | AUI_ONLY); + + if (send_test_pkt(nic)) { + return A_CNF_MEDIA_AUI; } + else + return 0; +} + +static int detect_bnc(struct nic *nic) +{ + clrline(); printf("attempting %s:","BNC"); + control_dc_dc(1); + + writereg(PP_LineCTL, (eth_linectl & ~AUTO_AUI_10BASET) | AUI_ONLY); + + if (send_test_pkt(nic)) { + return A_CNF_MEDIA_10B_2; } + else + return 0; +} + +/************************************************************************** +ETH_RESET - Reset adapter +***************************************************************************/ + +static void cs89x0_reset(struct nic *nic) +{ + int i; + unsigned long reset_tmo; + + writereg(PP_SelfCTL, readreg(PP_SelfCTL) | POWER_ON_RESET); + + /* wait for two ticks; that is 2*55ms */ + for (reset_tmo = currticks() + 2; currticks() < reset_tmo; ); + + if (eth_cs_type != CS8900) { + /* Hardware problem requires PNP registers to be reconfigured + after a reset */ + if (eth_irqno != 0xFFFF) { + outw(PP_CS8920_ISAINT, eth_nic_base + ADD_PORT); + outb(eth_irqno, eth_nic_base + DATA_PORT); + outb(0, eth_nic_base + DATA_PORT + 1); } + + if (eth_mem_start) { + outw(PP_CS8920_ISAMemB, eth_nic_base + ADD_PORT); + outb((eth_mem_start >> 8) & 0xff, eth_nic_base + DATA_PORT); + outb((eth_mem_start >> 24) & 0xff, eth_nic_base + DATA_PORT + 1); } } + + /* Wait until the chip is reset */ + for (reset_tmo = currticks() + 2; + (readreg(PP_SelfST) & INIT_DONE) == 0 && + currticks() < reset_tmo; ); + + /* disable interrupts and memory accesses */ + writereg(PP_BusCTL, 0); + + /* set the ethernet address */ + for (i=0; i < ETH_ALEN/2; i++) + writereg(PP_IA+i*2, + nic->node_addr[i*2] | + (nic->node_addr[i*2+1] << 8)); + + /* receive only error free packets addressed to this card */ + writereg(PP_RxCTL, DEF_RX_ACCEPT); + + /* do not generate any interrupts on receive operations */ + writereg(PP_RxCFG, 0); + + /* do not generate any interrupts on transmit operations */ + writereg(PP_TxCFG, 0); + + /* do not generate any interrupts on buffer operations */ + writereg(PP_BufCFG, 0); + + /* reset address port, so that autoprobing will keep working */ + outw(PP_ChipID, eth_nic_base + ADD_PORT); + + return; +} + +/************************************************************************** +ETH_TRANSMIT - Transmit a frame +***************************************************************************/ + +static void cs89x0_transmit( + struct nic *nic, + const char *d, /* Destination */ + unsigned int t, /* Type */ + unsigned int s, /* size */ + const char *p) /* Packet */ +{ + unsigned long tmo; + int sr; + + /* does this size have to be rounded??? please, + somebody have a look in the specs */ + if ((sr = ((s + ETH_HLEN + 1)&~1)) < ETH_ZLEN) + sr = ETH_ZLEN; + +retry: + /* initiate a transmit sequence */ + outw(TX_AFTER_ALL, eth_nic_base + TX_CMD_PORT); + outw(sr, eth_nic_base + TX_LEN_PORT); + + /* Test to see if the chip has allocated memory for the packet */ + if ((readreg(PP_BusST) & READY_FOR_TX_NOW) == 0) { + /* Oops... this should not happen! */ + printf("cs: unable to send packet; retrying...\n"); + for (tmo = currticks() + 5*TICKS_PER_SEC; currticks() < tmo; ); + cs89x0_reset(nic); + goto retry; } + + /* Write the contents of the packet */ + outsw(eth_nic_base + TX_FRAME_PORT, d, ETH_ALEN/2); + outsw(eth_nic_base + TX_FRAME_PORT, nic->node_addr, + ETH_ALEN/2); + outw(((t >> 8)&0xFF)|(t << 8), eth_nic_base + TX_FRAME_PORT); + outsw(eth_nic_base + TX_FRAME_PORT, p, (s+1)/2); + for (sr = sr/2 - (s+1)/2 - ETH_ALEN - 1; sr > 0; sr--) + outw(0, eth_nic_base + TX_FRAME_PORT); + + /* wait for transfer to succeed */ + for (tmo = currticks()+5*TICKS_PER_SEC; + (s = readreg(PP_TxEvent)&~0x1F) == 0 && currticks() < tmo;) + /* nothing */ ; + if ((s & TX_SEND_OK_BITS) != TX_OK) { + printf("\ntransmission error %#hX\n", s); + } + + return; +} + +/************************************************************************** +ETH_POLL - Wait for a frame +***************************************************************************/ + +static int cs89x0_poll(struct nic *nic, int retrieve) +{ + int status; + + status = readreg(PP_RxEvent); + + if ((status & RX_OK) == 0) + return(0); + + if ( ! retrieve ) return 1; + + status = inw(eth_nic_base + RX_FRAME_PORT); + nic->packetlen = inw(eth_nic_base + RX_FRAME_PORT); + insw(eth_nic_base + RX_FRAME_PORT, nic->packet, nic->packetlen >> 1); + if (nic->packetlen & 1) + nic->packet[nic->packetlen-1] = inw(eth_nic_base + RX_FRAME_PORT); + return 1; +} + +static void cs89x0_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + +static struct nic_operations cs89x0_operations = { + .connect = dummy_connect, + .poll = cs89x0_poll, + .transmit = cs89x0_transmit, + .irq = cs89x0_irq, +}; + +/************************************************************************** +ETH_PROBE - Look for an adapter +***************************************************************************/ + +static int cs89x0_probe_addr ( isa_probe_addr_t ioaddr ) { + /* if they give us an odd I/O address, then do ONE write to + the address port, to get it back to address zero, where we + expect to find the EISA signature word. */ + if (ioaddr & 1) { + ioaddr &= ~1; + if ((inw(ioaddr + ADD_PORT) & ADD_MASK) != ADD_SIG) + return 0; + outw(PP_ChipID, ioaddr + ADD_PORT); + } + + if (inw(ioaddr + DATA_PORT) != CHIP_EISA_ID_SIG) + return 0; + + return 1; +} + +static int cs89x0_probe ( struct nic *nic, struct isa_device *isa __unused ) { + int i, result = -1; + unsigned rev_type = 0, isa_cnf, cs_revision; + unsigned short eeprom_buff[CHKSUM_LEN]; + + nic->ioaddr &= ~1; /* LSB = 1 indicates a more aggressive probe */ + eth_nic_base = nic->ioaddr; + + /* get the chip type */ + rev_type = readreg(PRODUCT_ID_ADD); + eth_cs_type = rev_type &~ REVISON_BITS; + cs_revision = ((rev_type & REVISON_BITS) >> 8) + 'A'; + + printf("\ncs: cs89%c0%s rev %c, base %#hX", + eth_cs_type==CS8900?'0':'2', + eth_cs_type==CS8920M?"M":"", + cs_revision, + eth_nic_base); +#ifndef EMBEDDED + /* First check to see if an EEPROM is attached*/ + if ((readreg(PP_SelfST) & EEPROM_PRESENT) == 0) { + printf("\ncs: no EEPROM...\n"); + outw(PP_ChipID, eth_nic_base + ADD_PORT); + return 0; + } else if (get_eeprom_data(START_EEPROM_DATA,CHKSUM_LEN, + eeprom_buff) < 0) { + printf("\ncs: EEPROM read failed...\n"); + outw(PP_ChipID, eth_nic_base + ADD_PORT); + return 0; + } else if (get_eeprom_chksum(START_EEPROM_DATA,CHKSUM_LEN, + eeprom_buff) < 0) { + printf("\ncs: EEPROM checksum bad...\n"); + outw(PP_ChipID, eth_nic_base + ADD_PORT); + return 0; + } + + /* get transmission control word but keep the + autonegotiation bits */ + eth_auto_neg_cnf = eeprom_buff[AUTO_NEG_CNF_OFFSET/2]; + /* Store adapter configuration */ + eth_adapter_cnf = eeprom_buff[ADAPTER_CNF_OFFSET/2]; + /* Store ISA configuration */ + isa_cnf = eeprom_buff[ISA_CNF_OFFSET/2]; + + /* store the initial memory base address */ + eth_mem_start = eeprom_buff[PACKET_PAGE_OFFSET/2] << 8; + + printf("%s%s%s, addr ", + (eth_adapter_cnf & A_CNF_10B_T)?", RJ-45":"", + (eth_adapter_cnf & A_CNF_AUI)?", AUI":"", + (eth_adapter_cnf & A_CNF_10B_2)?", BNC":""); + + /* If this is a CS8900 then no pnp soft */ + if (eth_cs_type != CS8900 && + /* Check if the ISA IRQ has been set */ + (i = readreg(PP_CS8920_ISAINT) & 0xff, + (i != 0 && i < CS8920_NO_INTS))) + eth_irqno = i; + else { + i = isa_cnf & INT_NO_MASK; + if (eth_cs_type == CS8900) { + /* the table that follows is dependent + upon how you wired up your cs8900 + in your system. The table is the + same as the cs8900 engineering demo + board. irq_map also depends on the + contents of the table. Also see + write_irq, which is the reverse + mapping of the table below. */ + if (i < 4) i = "\012\013\014\005"[i]; + else printf("\ncs: BUG: isa_config is %d\n", i); } + eth_irqno = i; } + + nic->irqno = eth_irqno; + + /* Retrieve and print the ethernet address. */ + for (i=0; inode_addr[i] = ((unsigned char *)eeprom_buff)[i]; + } + + DBG ( "%s\n", eth_ntoa ( nic->node_addr ) ); + +#endif +#ifdef EMBEDDED + /* Retrieve and print the ethernet address. */ + { + unsigned char MAC_HW_ADDR[6]={MAC_HW_ADDR_DRV}; + memcpy(nic->node_addr, MAC_HW_ADDR, 6); + } + + DBG ( "%s\n", eth_ntoa ( nic->node_addr ) ); + + eth_adapter_cnf = A_CNF_10B_T | A_CNF_MEDIA_10B_T; + eth_auto_neg_cnf = EE_AUTO_NEG_ENABLE | IMM_BIT; +#endif +#ifndef EMBEDDED + /* Set the LineCTL quintuplet based on adapter + configuration read from EEPROM */ + if ((eth_adapter_cnf & A_CNF_EXTND_10B_2) && + (eth_adapter_cnf & A_CNF_LOW_RX_SQUELCH)) + eth_linectl = LOW_RX_SQUELCH; + else + eth_linectl = 0; + + /* check to make sure that they have the "right" + hardware available */ + switch(eth_adapter_cnf & A_CNF_MEDIA_TYPE) { + case A_CNF_MEDIA_10B_T: result = eth_adapter_cnf & A_CNF_10B_T; + break; + case A_CNF_MEDIA_AUI: result = eth_adapter_cnf & A_CNF_AUI; + break; + case A_CNF_MEDIA_10B_2: result = eth_adapter_cnf & A_CNF_10B_2; + break; + default: result = eth_adapter_cnf & (A_CNF_10B_T | A_CNF_AUI | + A_CNF_10B_2); + } + if (!result) { + printf("cs: EEPROM is configured for unavailable media\n"); + error: + writereg(PP_LineCTL, readreg(PP_LineCTL) & + ~(SERIAL_TX_ON | SERIAL_RX_ON)); + outw(PP_ChipID, eth_nic_base + ADD_PORT); + return 0; + } +#endif + /* Initialize the card for probing of the attached media */ + cs89x0_reset(nic); + + /* set the hardware to the configured choice */ + switch(eth_adapter_cnf & A_CNF_MEDIA_TYPE) { + case A_CNF_MEDIA_10B_T: + result = detect_tp(); + if (!result) { + clrline(); + printf("10Base-T (RJ-45%s", + ") has no cable\n"); } + /* check "ignore missing media" bit */ + if (eth_auto_neg_cnf & IMM_BIT) + /* Yes! I don't care if I see a link pulse */ + result = A_CNF_MEDIA_10B_T; + break; + case A_CNF_MEDIA_AUI: + result = detect_aui(nic); + if (!result) { + clrline(); + printf("10Base-5 (AUI%s", + ") has no cable\n"); } + /* check "ignore missing media" bit */ + if (eth_auto_neg_cnf & IMM_BIT) + /* Yes! I don't care if I see a carrrier */ + result = A_CNF_MEDIA_AUI; + break; + case A_CNF_MEDIA_10B_2: + result = detect_bnc(nic); + if (!result) { + clrline(); + printf("10Base-2 (BNC%s", + ") has no cable\n"); } + /* check "ignore missing media" bit */ + if (eth_auto_neg_cnf & IMM_BIT) + /* Yes! I don't care if I can xmit a packet */ + result = A_CNF_MEDIA_10B_2; + break; + case A_CNF_MEDIA_AUTO: + writereg(PP_LineCTL, eth_linectl | AUTO_AUI_10BASET); + if (eth_adapter_cnf & A_CNF_10B_T) + if ((result = detect_tp()) != 0) + break; + if (eth_adapter_cnf & A_CNF_AUI) + if ((result = detect_aui(nic)) != 0) + break; + if (eth_adapter_cnf & A_CNF_10B_2) + if ((result = detect_bnc(nic)) != 0) + break; + clrline(); printf("no media detected\n"); + goto error; + } + clrline(); + switch(result) { + case 0: printf("no network cable attached to configured media\n"); + goto error; + case A_CNF_MEDIA_10B_T: printf("using 10Base-T (RJ-45)\n"); + break; + case A_CNF_MEDIA_AUI: printf("using 10Base-5 (AUI)\n"); + break; + case A_CNF_MEDIA_10B_2: printf("using 10Base-2 (BNC)\n"); + break; + } + + /* Turn on both receive and transmit operations */ + writereg(PP_LineCTL, readreg(PP_LineCTL) | SERIAL_RX_ON | + SERIAL_TX_ON); + + return 0; +#ifdef EMBEDDED + error: + writereg(PP_LineCTL, readreg(PP_LineCTL) & + ~(SERIAL_TX_ON | SERIAL_RX_ON)); + outw(PP_ChipID, eth_nic_base + ADD_PORT); + return 0; +#endif + + nic->nic_op = &cs89x0_operations; + return 1; +} + +static void cs89x0_disable ( struct nic *nic, + struct isa_device *isa __unused ) { + cs89x0_reset(nic); +} + +static isa_probe_addr_t cs89x0_probe_addrs[] = { +#ifndef EMBEDDED + /* use "conservative" default values for autoprobing */ + 0x300, 0x320, 0x340, 0x200, 0x220, 0x240, + 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, + /* if that did not work, then be more aggressive */ + 0x301, 0x321, 0x341, 0x201, 0x221, 0x241, + 0x261, 0x281, 0x2a1, 0x2c1, 0x2e1, +#else + 0x01000300, +#endif +}; + +ISA_DRIVER ( cs89x0_driver, cs89x0_probe_addrs, cs89x0_probe_addr, + ISAPNP_VENDOR('C','S','C'), 0x0007 ); + +DRIVER ( "cs89x0", nic_driver, isa_driver, cs89x0_driver, + cs89x0_probe, cs89x0_disable ); + +ISA_ROM ( "cs89x0", "Crystal Semiconductor CS89x0" ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/cs89x0.h b/gpxe/src/drivers/net/cs89x0.h new file mode 100644 index 0000000..696191c --- /dev/null +++ b/gpxe/src/drivers/net/cs89x0.h @@ -0,0 +1,479 @@ +/** + Per an email message from Russ Nelson on + 18 March 2008 this file is now licensed under GPL Version 2. + + From: Russ Nelson + Date: Tue, 18 Mar 2008 12:42:00 -0400 + Subject: Re: [Etherboot-developers] cs89x0 driver in etherboot + -- quote from email + As copyright holder, if I say it doesn't conflict with the GPL, + then it doesn't conflict with the GPL. + + However, there's no point in causing people's brains to overheat, + so yes, I grant permission for the code to be relicensed under the + GPLv2. Please make sure that this change in licensing makes its + way upstream. -russ + -- quote from email +**/ + +/* Copyright, 1988-1992, Russell Nelson, Crynwr Software + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, version 1. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#define PP_ChipID 0x0000 /* offset 0h -> Corp -ID */ + /* offset 2h -> Model/Product Number */ + /* offset 3h -> Chip Revision Number */ + +#define PP_ISAIOB 0x0020 /* IO base address */ +#define PP_CS8900_ISAINT 0x0022 /* ISA interrupt select */ +#define PP_CS8920_ISAINT 0x0370 /* ISA interrupt select */ +#define PP_CS8900_ISADMA 0x0024 /* ISA Rec DMA channel */ +#define PP_CS8920_ISADMA 0x0374 /* ISA Rec DMA channel */ +#define PP_ISASOF 0x0026 /* ISA DMA offset */ +#define PP_DmaFrameCnt 0x0028 /* ISA DMA Frame count */ +#define PP_DmaByteCnt 0x002A /* ISA DMA Byte count */ +#define PP_CS8900_ISAMemB 0x002C /* Memory base */ +#define PP_CS8920_ISAMemB 0x0348 /* */ + +#define PP_ISABootBase 0x0030 /* Boot Prom base */ +#define PP_ISABootMask 0x0034 /* Boot Prom Mask */ + +/* EEPROM data and command registers */ +#define PP_EECMD 0x0040 /* NVR Interface Command register */ +#define PP_EEData 0x0042 /* NVR Interface Data Register */ +#define PP_DebugReg 0x0044 /* Debug Register */ + +#define PP_RxCFG 0x0102 /* Rx Bus config */ +#define PP_RxCTL 0x0104 /* Receive Control Register */ +#define PP_TxCFG 0x0106 /* Transmit Config Register */ +#define PP_TxCMD 0x0108 /* Transmit Command Register */ +#define PP_BufCFG 0x010A /* Bus configuration Register */ +#define PP_LineCTL 0x0112 /* Line Config Register */ +#define PP_SelfCTL 0x0114 /* Self Command Register */ +#define PP_BusCTL 0x0116 /* ISA bus control Register */ +#define PP_TestCTL 0x0118 /* Test Register */ +#define PP_AutoNegCTL 0x011C /* Auto Negotiation Ctrl */ + +#define PP_ISQ 0x0120 /* Interrupt Status */ +#define PP_RxEvent 0x0124 /* Rx Event Register */ +#define PP_TxEvent 0x0128 /* Tx Event Register */ +#define PP_BufEvent 0x012C /* Bus Event Register */ +#define PP_RxMiss 0x0130 /* Receive Miss Count */ +#define PP_TxCol 0x0132 /* Transmit Collision Count */ +#define PP_LineST 0x0134 /* Line State Register */ +#define PP_SelfST 0x0136 /* Self State register */ +#define PP_BusST 0x0138 /* Bus Status */ +#define PP_TDR 0x013C /* Time Domain Reflectometry */ +#define PP_AutoNegST 0x013E /* Auto Neg Status */ +#define PP_TxCommand 0x0144 /* Tx Command */ +#define PP_TxLength 0x0146 /* Tx Length */ +#define PP_LAF 0x0150 /* Hash Table */ +#define PP_IA 0x0158 /* Physical Address Register */ + +#define PP_RxStatus 0x0400 /* Receive start of frame */ +#define PP_RxLength 0x0402 /* Receive Length of frame */ +#define PP_RxFrame 0x0404 /* Receive frame pointer */ +#define PP_TxFrame 0x0A00 /* Transmit frame pointer */ + +/* Primary I/O Base Address. If no I/O base is supplied by the user, then this */ +/* can be used as the default I/O base to access the PacketPage Area. */ +#define DEFAULTIOBASE 0x0300 +#define FIRST_IO 0x020C /* First I/O port to check */ +#define LAST_IO 0x037C /* Last I/O port to check (+10h) */ +#define ADD_MASK 0x3000 /* Mask it use of the ADD_PORT register */ +#define ADD_SIG 0x3000 /* Expected ID signature */ + +#define CHIP_EISA_ID_SIG 0x630E /* Product ID Code for Crystal Chip (CS8900 spec 4.3) */ + +#ifdef IBMEIPKT +#define EISA_ID_SIG 0x4D24 /* IBM */ +#define PART_NO_SIG 0x1010 /* IBM */ +#define MONGOOSE_BIT 0x0000 /* IBM */ +#else +#define EISA_ID_SIG 0x630E /* PnP Vendor ID (same as chip id for Crystal board) */ +#define PART_NO_SIG 0x4000 /* ID code CS8920 board (PnP Vendor Product code) */ +#define MONGOOSE_BIT 0x2000 /* PART_NO_SIG + MONGOOSE_BUT => ID of mongoose */ +#endif + +#define PRODUCT_ID_ADD 0x0002 /* Address of product ID */ + +/* Mask to find out the types of registers */ +#define REG_TYPE_MASK 0x001F + +/* Eeprom Commands */ +#define ERSE_WR_ENBL 0x00F0 +#define ERSE_WR_DISABLE 0x0000 + +/* Defines Control/Config register quintuplet numbers */ +#define RX_BUF_CFG 0x0003 +#define RX_CONTROL 0x0005 +#define TX_CFG 0x0007 +#define TX_COMMAND 0x0009 +#define BUF_CFG 0x000B +#define LINE_CONTROL 0x0013 +#define SELF_CONTROL 0x0015 +#define BUS_CONTROL 0x0017 +#define TEST_CONTROL 0x0019 + +/* Defines Status/Count registers quintuplet numbers */ +#define RX_EVENT 0x0004 +#define TX_EVENT 0x0008 +#define BUF_EVENT 0x000C +#define RX_MISS_COUNT 0x0010 +#define TX_COL_COUNT 0x0012 +#define LINE_STATUS 0x0014 +#define SELF_STATUS 0x0016 +#define BUS_STATUS 0x0018 +#define TDR 0x001C + +/* PP_RxCFG - Receive Configuration and Interrupt Mask bit definition - Read/write */ +#define SKIP_1 0x0040 +#define RX_STREAM_ENBL 0x0080 +#define RX_OK_ENBL 0x0100 +#define RX_DMA_ONLY 0x0200 +#define AUTO_RX_DMA 0x0400 +#define BUFFER_CRC 0x0800 +#define RX_CRC_ERROR_ENBL 0x1000 +#define RX_RUNT_ENBL 0x2000 +#define RX_EXTRA_DATA_ENBL 0x4000 + +/* PP_RxCTL - Receive Control bit definition - Read/write */ +#define RX_IA_HASH_ACCEPT 0x0040 +#define RX_PROM_ACCEPT 0x0080 +#define RX_OK_ACCEPT 0x0100 +#define RX_MULTCAST_ACCEPT 0x0200 +#define RX_IA_ACCEPT 0x0400 +#define RX_BROADCAST_ACCEPT 0x0800 +#define RX_BAD_CRC_ACCEPT 0x1000 +#define RX_RUNT_ACCEPT 0x2000 +#define RX_EXTRA_DATA_ACCEPT 0x4000 +#define RX_ALL_ACCEPT (RX_PROM_ACCEPT|RX_BAD_CRC_ACCEPT|RX_RUNT_ACCEPT|RX_EXTRA_DATA_ACCEPT) +/* Default receive mode - individually addressed, broadcast, and error free */ +#define DEF_RX_ACCEPT (RX_IA_ACCEPT | RX_BROADCAST_ACCEPT | RX_OK_ACCEPT) + +/* PP_TxCFG - Transmit Configuration Interrupt Mask bit definition - Read/write */ +#define TX_LOST_CRS_ENBL 0x0040 +#define TX_SQE_ERROR_ENBL 0x0080 +#define TX_OK_ENBL 0x0100 +#define TX_LATE_COL_ENBL 0x0200 +#define TX_JBR_ENBL 0x0400 +#define TX_ANY_COL_ENBL 0x0800 +#define TX_16_COL_ENBL 0x8000 + +/* PP_TxCMD - Transmit Command bit definition - Read-only */ +#define TX_START_4_BYTES 0x0000 +#define TX_START_64_BYTES 0x0040 +#define TX_START_128_BYTES 0x0080 +#define TX_START_ALL_BYTES 0x00C0 +#define TX_FORCE 0x0100 +#define TX_ONE_COL 0x0200 +#define TX_TWO_PART_DEFF_DISABLE 0x0400 +#define TX_NO_CRC 0x1000 +#define TX_RUNT 0x2000 + +/* PP_BufCFG - Buffer Configuration Interrupt Mask bit definition - Read/write */ +#define GENERATE_SW_INTERRUPT 0x0040 +#define RX_DMA_ENBL 0x0080 +#define READY_FOR_TX_ENBL 0x0100 +#define TX_UNDERRUN_ENBL 0x0200 +#define RX_MISS_ENBL 0x0400 +#define RX_128_BYTE_ENBL 0x0800 +#define TX_COL_COUNT_OVRFLOW_ENBL 0x1000 +#define RX_MISS_COUNT_OVRFLOW_ENBL 0x2000 +#define RX_DEST_MATCH_ENBL 0x8000 + +/* PP_LineCTL - Line Control bit definition - Read/write */ +#define SERIAL_RX_ON 0x0040 +#define SERIAL_TX_ON 0x0080 +#define AUI_ONLY 0x0100 +#define AUTO_AUI_10BASET 0x0200 +#define MODIFIED_BACKOFF 0x0800 +#define NO_AUTO_POLARITY 0x1000 +#define TWO_PART_DEFDIS 0x2000 +#define LOW_RX_SQUELCH 0x4000 + +/* PP_SelfCTL - Software Self Control bit definition - Read/write */ +#define POWER_ON_RESET 0x0040 +#define SW_STOP 0x0100 +#define SLEEP_ON 0x0200 +#define AUTO_WAKEUP 0x0400 +#define HCB0_ENBL 0x1000 +#define HCB1_ENBL 0x2000 +#define HCB0 0x4000 +#define HCB1 0x8000 + +/* PP_BusCTL - ISA Bus Control bit definition - Read/write */ +#define RESET_RX_DMA 0x0040 +#define MEMORY_ON 0x0400 +#define DMA_BURST_MODE 0x0800 +#define IO_CHANNEL_READY_ON 0x1000 +#define RX_DMA_SIZE_64K 0x2000 +#define ENABLE_IRQ 0x8000 + +/* PP_TestCTL - Test Control bit definition - Read/write */ +#define LINK_OFF 0x0080 +#define ENDEC_LOOPBACK 0x0200 +#define AUI_LOOPBACK 0x0400 +#define BACKOFF_OFF 0x0800 +#define FAST_TEST 0x8000 + +/* PP_RxEvent - Receive Event Bit definition - Read-only */ +#define RX_IA_HASHED 0x0040 +#define RX_DRIBBLE 0x0080 +#define RX_OK 0x0100 +#define RX_HASHED 0x0200 +#define RX_IA 0x0400 +#define RX_BROADCAST 0x0800 +#define RX_CRC_ERROR 0x1000 +#define RX_RUNT 0x2000 +#define RX_EXTRA_DATA 0x4000 + +#define HASH_INDEX_MASK 0x0FC00 + +/* PP_TxEvent - Transmit Event Bit definition - Read-only */ +#define TX_LOST_CRS 0x0040 +#define TX_SQE_ERROR 0x0080 +#define TX_OK 0x0100 +#define TX_LATE_COL 0x0200 +#define TX_JBR 0x0400 +#define TX_16_COL 0x8000 +#define TX_SEND_OK_BITS (TX_OK|TX_LOST_CRS) +#define TX_COL_COUNT_MASK 0x7800 + +/* PP_BufEvent - Buffer Event Bit definition - Read-only */ +#define SW_INTERRUPT 0x0040 +#define RX_DMA 0x0080 +#define READY_FOR_TX 0x0100 +#define TX_UNDERRUN 0x0200 +#define RX_MISS 0x0400 +#define RX_128_BYTE 0x0800 +#define TX_COL_OVRFLW 0x1000 +#define RX_MISS_OVRFLW 0x2000 +#define RX_DEST_MATCH 0x8000 + +/* PP_LineST - Ethernet Line Status bit definition - Read-only */ +#define LINK_OK 0x0080 +#define AUI_ON 0x0100 +#define TENBASET_ON 0x0200 +#define POLARITY_OK 0x1000 +#define CRS_OK 0x4000 + +/* PP_SelfST - Chip Software Status bit definition */ +#define ACTIVE_33V 0x0040 +#define INIT_DONE 0x0080 +#define SI_BUSY 0x0100 +#define EEPROM_PRESENT 0x0200 +#define EEPROM_OK 0x0400 +#define EL_PRESENT 0x0800 +#define EE_SIZE_64 0x1000 + +/* PP_BusST - ISA Bus Status bit definition */ +#define TX_BID_ERROR 0x0080 +#define READY_FOR_TX_NOW 0x0100 + +/* PP_AutoNegCTL - Auto Negotiation Control bit definition */ +#define RE_NEG_NOW 0x0040 +#define ALLOW_FDX 0x0080 +#define AUTO_NEG_ENABLE 0x0100 +#define NLP_ENABLE 0x0200 +#define FORCE_FDX 0x8000 +#define AUTO_NEG_BITS (FORCE_FDX|NLP_ENABLE|AUTO_NEG_ENABLE) +#define AUTO_NEG_MASK (FORCE_FDX|NLP_ENABLE|AUTO_NEG_ENABLE|ALLOW_FDX|RE_NEG_NOW) + +/* PP_AutoNegST - Auto Negotiation Status bit definition */ +#define AUTO_NEG_BUSY 0x0080 +#define FLP_LINK 0x0100 +#define FLP_LINK_GOOD 0x0800 +#define LINK_FAULT 0x1000 +#define HDX_ACTIVE 0x4000 +#define FDX_ACTIVE 0x8000 + +/* The following block defines the ISQ event types */ +#define ISQ_RECEIVER_EVENT 0x04 +#define ISQ_TRANSMITTER_EVENT 0x08 +#define ISQ_BUFFER_EVENT 0x0c +#define ISQ_RX_MISS_EVENT 0x10 +#define ISQ_TX_COL_EVENT 0x12 + +#define ISQ_EVENT_MASK 0x003F /* ISQ mask to find out type of event */ +#define ISQ_HIST 16 /* small history buffer */ +#define AUTOINCREMENT 0x8000 /* Bit mask to set bit-15 for autoincrement */ + +#define TXRXBUFSIZE 0x0600 +#define RXDMABUFSIZE 0x8000 +#define RXDMASIZE 0x4000 +#define TXRX_LENGTH_MASK 0x07FF + +/* rx options bits */ +#define RCV_WITH_RXON 1 /* Set SerRx ON */ +#define RCV_COUNTS 2 /* Use Framecnt1 */ +#define RCV_PONG 4 /* Pong respondent */ +#define RCV_DONG 8 /* Dong operation */ +#define RCV_POLLING 0x10 /* Poll RxEvent */ +#define RCV_ISQ 0x20 /* Use ISQ, int */ +#define RCV_AUTO_DMA 0x100 /* Set AutoRxDMAE */ +#define RCV_DMA 0x200 /* Set RxDMA only */ +#define RCV_DMA_ALL 0x400 /* Copy all DMA'ed */ +#define RCV_FIXED_DATA 0x800 /* Every frame same */ +#define RCV_IO 0x1000 /* Use ISA IO only */ +#define RCV_MEMORY 0x2000 /* Use ISA Memory */ + +#define RAM_SIZE 0x1000 /* The card has 4k bytes or RAM */ +#define PKT_START PP_TxFrame /* Start of packet RAM */ + +#define RX_FRAME_PORT 0x0000 +#define TX_FRAME_PORT RX_FRAME_PORT +#define TX_CMD_PORT 0x0004 +#define TX_NOW 0x0000 /* Tx packet after 5 bytes copied */ +#define TX_AFTER_381 0x0020 /* Tx packet after 381 bytes copied */ +#define TX_AFTER_ALL 0x00C0 /* Tx packet after all bytes copied */ +#define TX_LEN_PORT 0x0006 +#define ISQ_PORT 0x0008 +#define ADD_PORT 0x000A +#define DATA_PORT 0x000C + +#define EEPROM_WRITE_EN 0x00F0 +#define EEPROM_WRITE_DIS 0x0000 +#define EEPROM_WRITE_CMD 0x0100 +#define EEPROM_READ_CMD 0x0200 + +/* Receive Header */ +/* Description of header of each packet in receive area of memory */ +#define RBUF_EVENT_LOW 0 /* Low byte of RxEvent - status of received frame */ +#define RBUF_EVENT_HIGH 1 /* High byte of RxEvent - status of received frame */ +#define RBUF_LEN_LOW 2 /* Length of received data - low byte */ +#define RBUF_LEN_HI 3 /* Length of received data - high byte */ +#define RBUF_HEAD_LEN 4 /* Length of this header */ + +#define CHIP_READ 0x1 /* Used to mark state of the repins code (chip or dma) */ +#define DMA_READ 0x2 /* Used to mark state of the repins code (chip or dma) */ + +/* for bios scan */ +/* */ +#ifdef CSDEBUG +/* use these values for debugging bios scan */ +#define BIOS_START_SEG 0x00000 +#define BIOS_OFFSET_INC 0x0010 +#else +#define BIOS_START_SEG 0x0c000 +#define BIOS_OFFSET_INC 0x0200 +#endif + +#define BIOS_LAST_OFFSET 0x0fc00 + +/* Byte offsets into the EEPROM configuration buffer */ +#define ISA_CNF_OFFSET 0x6 +#define TX_CTL_OFFSET (ISA_CNF_OFFSET + 8) /* 8900 eeprom */ +#define AUTO_NEG_CNF_OFFSET (ISA_CNF_OFFSET + 8) /* 8920 eeprom */ + + /* the assumption here is that the bits in the eeprom are generally */ + /* in the same position as those in the autonegctl register. */ + /* Of course the IMM bit is not in that register so it must be */ + /* masked out */ +#define EE_FORCE_FDX 0x8000 +#define EE_NLP_ENABLE 0x0200 +#define EE_AUTO_NEG_ENABLE 0x0100 +#define EE_ALLOW_FDX 0x0080 +#define EE_AUTO_NEG_CNF_MASK (EE_FORCE_FDX|EE_NLP_ENABLE|EE_AUTO_NEG_ENABLE|EE_ALLOW_FDX) + +#define IMM_BIT 0x0040 /* ignore missing media */ + +#define ADAPTER_CNF_OFFSET (AUTO_NEG_CNF_OFFSET + 2) +#define A_CNF_10B_T 0x0001 +#define A_CNF_AUI 0x0002 +#define A_CNF_10B_2 0x0004 +#define A_CNF_MEDIA_TYPE 0x0060 +#define A_CNF_MEDIA_AUTO 0x0000 +#define A_CNF_MEDIA_10B_T 0x0020 +#define A_CNF_MEDIA_AUI 0x0040 +#define A_CNF_MEDIA_10B_2 0x0060 +#define A_CNF_DC_DC_POLARITY 0x0080 +#define A_CNF_NO_AUTO_POLARITY 0x2000 +#define A_CNF_LOW_RX_SQUELCH 0x4000 +#define A_CNF_EXTND_10B_2 0x8000 + +#define PACKET_PAGE_OFFSET 0x8 + +/* Bit definitions for the ISA configuration word from the EEPROM */ +#define INT_NO_MASK 0x000F +#define DMA_NO_MASK 0x0070 +#define ISA_DMA_SIZE 0x0200 +#define ISA_AUTO_RxDMA 0x0400 +#define ISA_RxDMA 0x0800 +#define DMA_BURST 0x1000 +#define STREAM_TRANSFER 0x2000 +#define ANY_ISA_DMA (ISA_AUTO_RxDMA | ISA_RxDMA) + +/* DMA controller registers */ +#define DMA_BASE 0x00 /* DMA controller base */ +#define DMA_BASE_2 0x0C0 /* DMA controller base */ + +#define DMA_STAT 0x0D0 /* DMA controller status register */ +#define DMA_MASK 0x0D4 /* DMA controller mask register */ +#define DMA_MODE 0x0D6 /* DMA controller mode register */ +#define DMA_RESETFF 0x0D8 /* DMA controller first/last flip flop */ + +/* DMA data */ +#define DMA_DISABLE 0x04 /* Disable channel n */ +#define DMA_ENABLE 0x00 /* Enable channel n */ +/* Demand transfers, incr. address, auto init, writes, ch. n */ +#define DMA_RX_MODE 0x14 +/* Demand transfers, incr. address, auto init, reads, ch. n */ +#define DMA_TX_MODE 0x18 + +#define DMA_SIZE (16*1024) /* Size of dma buffer - 16k */ + +#define CS8900 0x0000 +#define CS8920 0x4000 +#define CS8920M 0x6000 +#define REVISON_BITS 0x1F00 +#define EEVER_NUMBER 0x12 +#define CHKSUM_LEN 0x14 +#define CHKSUM_VAL 0x0000 +#define START_EEPROM_DATA 0x001c /* Offset into eeprom for start of data */ +#define IRQ_MAP_EEPROM_DATA 0x0046 /* Offset into eeprom for the IRQ map */ +#define IRQ_MAP_LEN 0x0004 /* No of bytes to read for the IRQ map */ +#define PNP_IRQ_FRMT 0x0022 /* PNP small item IRQ format */ +#define CS8900_IRQ_MAP 0x1c20 /* This IRQ map is fixed */ + +#define CS8920_NO_INTS 0x0F /* Max CS8920 interrupt select # */ + +#define PNP_ADD_PORT 0x0279 +#define PNP_WRITE_PORT 0x0A79 + +#define GET_PNP_ISA_STRUCT 0x40 +#define PNP_ISA_STRUCT_LEN 0x06 +#define PNP_CSN_CNT_OFF 0x01 +#define PNP_RD_PORT_OFF 0x02 +#define PNP_FUNCTION_OK 0x00 +#define PNP_WAKE 0x03 +#define PNP_RSRC_DATA 0x04 +#define PNP_RSRC_READY 0x01 +#define PNP_STATUS 0x05 +#define PNP_ACTIVATE 0x30 +#define PNP_CNF_IO_H 0x60 +#define PNP_CNF_IO_L 0x61 +#define PNP_CNF_INT 0x70 +#define PNP_CNF_DMA 0x74 +#define PNP_CNF_MEM 0x48 + +#define BIT0 1 +#define BIT15 0x8000 + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ + diff --git a/gpxe/src/drivers/net/cs89x0.txt b/gpxe/src/drivers/net/cs89x0.txt new file mode 100644 index 0000000..72b7df2 --- /dev/null +++ b/gpxe/src/drivers/net/cs89x0.txt @@ -0,0 +1,45 @@ +/** + Per an email message from Russ Nelson on + 18 March 2008 the files cs89x0.[ch] are now licensed under GPL + Version 2. + + From: Russ Nelson + Date: Tue, 18 Mar 2008 12:42:00 -0400 + Subject: Re: [Etherboot-developers] cs89x0 driver in etherboot + -- quote from email + As copyright holder, if I say it doesn't conflict with the GPL, + then it doesn't conflict with the GPL. + + However, there's no point in causing people's brains to overheat, + so yes, I grant permission for the code to be relicensed under the + GPLv2. Please make sure that this change in licensing makes its + way upstream. -russ + -- quote from email +**/ + +Permission is granted to distribute the enclosed cs89x0.[ch] driver +only in conjunction with the Etherboot package. The code is +ordinarily distributed under the GPL. + +Russ Nelson, January 2000 + +CREDITS + +I want to thank + + Mike Cruse + 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/gpxe/src/drivers/net/davicom.c b/gpxe/src/drivers/net/davicom.c new file mode 100644 index 0000000..bb6d7e9 --- /dev/null +++ b/gpxe/src/drivers/net/davicom.c @@ -0,0 +1,725 @@ +#ifdef ALLMULTI +#error multicast support is not yet implemented +#endif +/* + DAVICOM DM9009/DM9102/DM9102A Etherboot Driver V1.00 + + This driver was ported from Marty Connor's Tulip Etherboot driver. + Thanks Marty Connor (mdc@etherboot.org) + + This davicom etherboot driver supports DM9009/DM9102/DM9102A/ + DM9102A+DM9801/DM9102A+DM9802 NICs. + + This software may be used and distributed according to the terms + of the GNU Public License, incorporated herein by reference. + +*/ + +/*********************************************************************/ +/* Revision History */ +/*********************************************************************/ + +/* + 19 OCT 2000 Sten 1.00 + Different half and full duplex mode + Do the different programming for DM9801/DM9802 + + 12 OCT 2000 Sten 0.90 + This driver was ported from tulip driver and it + has the following difference. + Changed symbol tulip/TULIP to davicom/DAVICOM + Deleted some code that did not use in this driver. + Used chain-strcture to replace ring structure + for both TX/RX descriptor. + Allocated two tx descriptor. + According current media mode to set operating + register(CR6) +*/ + + +/*********************************************************************/ +/* Declarations */ +/*********************************************************************/ + +#include "etherboot.h" +#include "nic.h" +#include +#include + +#undef DAVICOM_DEBUG +#undef DAVICOM_DEBUG_WHERE + +#define TX_TIME_OUT 2*TICKS_PER_SEC + +/* Register offsets for davicom device */ +enum davicom_offsets { + CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28, + CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58, + CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78, CSR16=0x80, CSR20=0xA0 +}; + +/* EEPROM Address width definitions */ +#define EEPROM_ADDRLEN 6 +#define EEPROM_SIZE 32 /* 1 << EEPROM_ADDRLEN */ +/* Used to be 128, but we only need to read enough to get the MAC + address at bytes 20..25 */ + +/* Data Read from the EEPROM */ +static unsigned char ee_data[EEPROM_SIZE]; + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD (5 << addr_len) +#define EE_READ_CMD (6 << addr_len) +#define EE_ERASE_CMD (7 << addr_len) + +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */ +#define EE_CS 0x01 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x01 +#define EE_WRITE_1 0x05 +#define EE_DATA_READ 0x08 /* EEPROM chip data out. */ +#define EE_ENB (0x4800 | EE_CS) + +/* Sten 10/11 for phyxcer */ +#define PHY_DATA_0 0x0 +#define PHY_DATA_1 0x20000 +#define MDCLKH 0x10000 + +/* Delay between EEPROM clock transitions. Even at 33Mhz current PCI + implementations don't overrun the EEPROM clock. We add a bus + turn-around to insure that this remains true. */ +#define eeprom_delay() inl(ee_addr) + +/* helpful macro if on a big_endian machine for changing byte order. + not strictly needed on Intel + Already defined in Etherboot includes +#define le16_to_cpu(val) (val) +*/ + +/* transmit and receive descriptor format */ +struct txdesc { + volatile unsigned long status; /* owner, status */ + unsigned long buf1sz:11, /* size of buffer 1 */ + buf2sz:11, /* size of buffer 2 */ + control:10; /* control bits */ + const unsigned char *buf1addr; /* buffer 1 address */ + const unsigned char *buf2addr; /* buffer 2 address */ +}; + +struct rxdesc { + volatile unsigned long status; /* owner, status */ + unsigned long buf1sz:11, /* size of buffer 1 */ + buf2sz:11, /* size of buffer 2 */ + control:10; /* control bits */ + unsigned char *buf1addr; /* buffer 1 address */ + unsigned char *buf2addr; /* buffer 2 address */ +}; + +/* Size of transmit and receive buffers */ +#define BUFLEN 1536 + +/*********************************************************************/ +/* Global Storage */ +/*********************************************************************/ + +static struct nic_operations davicom_operations; + +/* PCI Bus parameters */ +static unsigned short vendor, dev_id; +static unsigned long ioaddr; + +/* Note: transmit and receive buffers must be longword aligned and + longword divisable */ + +/* transmit descriptor and buffer */ +#define NTXD 2 +#define NRXD 4 +struct { + struct txdesc txd[NTXD] __attribute__ ((aligned(4))); + unsigned char txb[BUFLEN] __attribute__ ((aligned(4))); + struct rxdesc rxd[NRXD] __attribute__ ((aligned(4))); + unsigned char rxb[NRXD * BUFLEN] __attribute__ ((aligned(4))); +} davicom_bufs __shared; +#define txd davicom_bufs.txd +#define txb davicom_bufs.txb +#define rxd davicom_bufs.rxd +#define rxb davicom_bufs.rxb +static int rxd_tail; +static int TxPtr; + + +/*********************************************************************/ +/* Function Prototypes */ +/*********************************************************************/ +static void whereami(const char *str); +static int read_eeprom(unsigned long ioaddr, int location, int addr_len); +static int davicom_probe(struct nic *nic,struct pci_device *pci); +static void davicom_init_chain(struct nic *nic); /* Sten 10/9 */ +static void davicom_reset(struct nic *nic); +static void davicom_transmit(struct nic *nic, const char *d, unsigned int t, + unsigned int s, const char *p); +static int davicom_poll(struct nic *nic, int retrieve); +static void davicom_disable(struct nic *nic); +#ifdef DAVICOM_DEBUG +static void davicom_more(void); +#endif /* DAVICOM_DEBUG */ +static void davicom_wait(unsigned int nticks); +static int phy_read(int); +static void phy_write(int, u16); +static void phy_write_1bit(u32, u32); +static int phy_read_1bit(u32); +static void davicom_media_chk(struct nic *); + + +/*********************************************************************/ +/* Utility Routines */ +/*********************************************************************/ +static inline void whereami(const char *str) +{ + printf("%s\n", str); + /* sleep(2); */ +} + +#ifdef DAVICOM_DEBUG +static void davicom_more() +{ + printf("\n\n-- more --"); + while (!iskey()) + /* wait */; + getchar(); + printf("\n\n"); +} +#endif /* DAVICOM_DEBUG */ + +static void davicom_wait(unsigned int nticks) +{ + unsigned int to = currticks() + nticks; + while (currticks() < to) + /* wait */ ; +} + + +/*********************************************************************/ +/* For DAVICOM phyxcer register by MII interface */ +/*********************************************************************/ +/* + Read a word data from phy register +*/ +static int phy_read(int location) +{ + int i, phy_addr=1; + u16 phy_data; + u32 io_dcr9; + + whereami("phy_read\n"); + + io_dcr9 = ioaddr + CSR9; + + /* Send 33 synchronization clock to Phy controller */ + for (i=0; i<34; i++) + phy_write_1bit(io_dcr9, PHY_DATA_1); + + /* Send start command(01) to Phy */ + phy_write_1bit(io_dcr9, PHY_DATA_0); + phy_write_1bit(io_dcr9, PHY_DATA_1); + + /* Send read command(10) to Phy */ + phy_write_1bit(io_dcr9, PHY_DATA_1); + phy_write_1bit(io_dcr9, PHY_DATA_0); + + /* Send Phy addres */ + for (i=0x10; i>0; i=i>>1) + phy_write_1bit(io_dcr9, phy_addr&i ? PHY_DATA_1: PHY_DATA_0); + + /* Send register addres */ + for (i=0x10; i>0; i=i>>1) + phy_write_1bit(io_dcr9, location&i ? PHY_DATA_1: PHY_DATA_0); + + /* Skip transition state */ + phy_read_1bit(io_dcr9); + + /* read 16bit data */ + for (phy_data=0, i=0; i<16; i++) { + phy_data<<=1; + phy_data|=phy_read_1bit(io_dcr9); + } + + return phy_data; +} + +/* + Write a word to Phy register +*/ +static void phy_write(int location, u16 phy_data) +{ + u16 i, phy_addr=1; + u32 io_dcr9; + + whereami("phy_write\n"); + + io_dcr9 = ioaddr + CSR9; + + /* Send 33 synchronization clock to Phy controller */ + for (i=0; i<34; i++) + phy_write_1bit(io_dcr9, PHY_DATA_1); + + /* Send start command(01) to Phy */ + phy_write_1bit(io_dcr9, PHY_DATA_0); + phy_write_1bit(io_dcr9, PHY_DATA_1); + + /* Send write command(01) to Phy */ + phy_write_1bit(io_dcr9, PHY_DATA_0); + phy_write_1bit(io_dcr9, PHY_DATA_1); + + /* Send Phy addres */ + for (i=0x10; i>0; i=i>>1) + phy_write_1bit(io_dcr9, phy_addr&i ? PHY_DATA_1: PHY_DATA_0); + + /* Send register addres */ + for (i=0x10; i>0; i=i>>1) + phy_write_1bit(io_dcr9, location&i ? PHY_DATA_1: PHY_DATA_0); + + /* written trasnition */ + phy_write_1bit(io_dcr9, PHY_DATA_1); + phy_write_1bit(io_dcr9, PHY_DATA_0); + + /* Write a word data to PHY controller */ + for (i=0x8000; i>0; i>>=1) + phy_write_1bit(io_dcr9, phy_data&i ? PHY_DATA_1: PHY_DATA_0); +} + +/* + Write one bit data to Phy Controller +*/ +static void phy_write_1bit(u32 ee_addr, u32 phy_data) +{ + whereami("phy_write_1bit\n"); + outl(phy_data, ee_addr); /* MII Clock Low */ + eeprom_delay(); + outl(phy_data|MDCLKH, ee_addr); /* MII Clock High */ + eeprom_delay(); + outl(phy_data, ee_addr); /* MII Clock Low */ + eeprom_delay(); +} + +/* + Read one bit phy data from PHY controller +*/ +static int phy_read_1bit(u32 ee_addr) +{ + int phy_data; + + whereami("phy_read_1bit\n"); + + outl(0x50000, ee_addr); + eeprom_delay(); + + phy_data=(inl(ee_addr)>>19) & 0x1; + + outl(0x40000, ee_addr); + eeprom_delay(); + + return phy_data; +} + +/* + DM9801/DM9802 present check and program +*/ +static void HPNA_process(void) +{ + + if ( (phy_read(3) & 0xfff0) == 0xb900 ) { + if ( phy_read(31) == 0x4404 ) { + /* DM9801 present */ + if (phy_read(3) == 0xb901) + phy_write(16, 0x5); /* DM9801 E4 */ + else + phy_write(16, 0x1005); /* DM9801 E3 and others */ + phy_write(25, ((phy_read(24) + 3) & 0xff) | 0xf000); + } else { + /* DM9802 present */ + phy_write(16, 0x5); + phy_write(25, (phy_read(25) & 0xff00) + 2); + } + } +} + +/* + Sense media mode and set CR6 +*/ +static void davicom_media_chk(struct nic * nic __unused) +{ + unsigned long to, csr6; + + csr6 = 0x00200000; /* SF */ + outl(csr6, ioaddr + CSR6); + +#define PCI_DEVICE_ID_DM9009 0x9009 + if (vendor == PCI_VENDOR_ID_DAVICOM && dev_id == PCI_DEVICE_ID_DM9009) { + /* Set to 10BaseT mode for DM9009 */ + phy_write(0, 0); + } else { + /* For DM9102/DM9102A */ + to = currticks() + 2 * TICKS_PER_SEC; + while ( ((phy_read(1) & 0x24)!=0x24) && (currticks() < to)) + /* wait */ ; + + if ( (phy_read(1) & 0x24) == 0x24 ) { + if (phy_read(17) & 0xa000) + csr6 |= 0x00000200; /* Full Duplex mode */ + } else + csr6 |= 0x00040000; /* Select DM9801/DM9802 when Ethernet link failed */ + } + + /* set the chip's operating mode */ + outl(csr6, ioaddr + CSR6); + + /* DM9801/DM9802 present check & program */ + if (csr6 & 0x40000) + HPNA_process(); +} + + +/*********************************************************************/ +/* EEPROM Reading Code */ +/*********************************************************************/ +/* EEPROM routines adapted from the Linux Tulip Code */ +/* Reading a serial EEPROM is a "bit" grungy, but we work our way + through:->. +*/ +static int read_eeprom(unsigned long ioaddr, int location, int addr_len) +{ + int i; + unsigned short retval = 0; + long ee_addr = ioaddr + CSR9; + int read_cmd = location | EE_READ_CMD; + + whereami("read_eeprom\n"); + + outl(EE_ENB & ~EE_CS, ee_addr); + outl(EE_ENB, ee_addr); + + /* Shift the read command bits out. */ + for (i = 4 + addr_len; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + outl(EE_ENB | dataval, ee_addr); + eeprom_delay(); + outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(); + } + outl(EE_ENB, ee_addr); + + for (i = 16; i > 0; i--) { + outl(EE_ENB | EE_SHIFT_CLK, ee_addr); + eeprom_delay(); + retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0); + outl(EE_ENB, ee_addr); + eeprom_delay(); + } + + /* Terminate the EEPROM access. */ + outl(EE_ENB & ~EE_CS, ee_addr); + return retval; +} + +/*********************************************************************/ +/* davicom_init_chain - setup the tx and rx descriptors */ +/* Sten 10/9 */ +/*********************************************************************/ +static void davicom_init_chain(struct nic *nic) +{ + int i; + + /* setup the transmit descriptor */ + /* Sten: Set 2 TX descriptor but use one TX buffer because + it transmit a packet and wait complete every time. */ + for (i=0; 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 nic *nic ) { + + whereami("davicom_disable\n"); + + davicom_reset(nic); + + /* disable interrupts */ + outl(0x00000000, ioaddr + CSR7); + + /* Stop the chip's Tx and Rx processes. */ + outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6); + + /* Clear the missed-packet counter. */ + inl(ioaddr + CSR8); +} + + +/*********************************************************************/ +/* eth_irq - enable, disable and force interrupts */ +/*********************************************************************/ +static void davicom_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + + +/*********************************************************************/ +/* eth_probe - Look for an adapter */ +/*********************************************************************/ +static int davicom_probe ( struct nic *nic, struct pci_device *pci ) { + + unsigned int i; + + whereami("davicom_probe\n"); + + if (pci->ioaddr == 0) + return 0; + + vendor = pci->vendor; + dev_id = pci->device; + ioaddr = pci->ioaddr; + + nic->ioaddr = pci->ioaddr; + nic->irqno = 0; + + /* wakeup chip */ + pci_write_config_dword(pci, 0x40, 0x00000000); + + /* Stop the chip's Tx and Rx processes. */ + outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6); + + /* Clear the missed-packet counter. */ + 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]; + + DBG ( "Davicom %s at IOADDR %4.4lx\n", eth_ntoa ( nic->node_addr ), ioaddr ); + + /* initialize device */ + davicom_reset(nic); + nic->nic_op = &davicom_operations; + return 1; +} + +static struct nic_operations davicom_operations = { + .connect = dummy_connect, + .poll = davicom_poll, + .transmit = davicom_transmit, + .irq = davicom_irq, + +}; + +static struct pci_device_id davicom_nics[] = { +PCI_ROM(0x1282, 0x9100, "davicom9100", "Davicom 9100"), +PCI_ROM(0x1282, 0x9102, "davicom9102", "Davicom 9102"), +PCI_ROM(0x1282, 0x9009, "davicom9009", "Davicom 9009"), +PCI_ROM(0x1282, 0x9132, "davicom9132", "Davicom 9132"), /* Needs probably some fixing */ +}; + +PCI_DRIVER ( davicom_driver, davicom_nics, PCI_NO_CLASS ); + +DRIVER ( "DAVICOM", nic_driver, pci_driver, davicom_driver, + davicom_probe, davicom_disable ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/depca.c b/gpxe/src/drivers/net/depca.c new file mode 100644 index 0000000..7372e60 --- /dev/null +++ b/gpxe/src/drivers/net/depca.c @@ -0,0 +1,803 @@ +/* #warning "depca.c: FIXME: fix relocation" */ + +#if 0 +/* Not fixed for relocation yet. Probably won't work relocated above 16MB */ +#ifdef ALLMULTI +#error multicast support is not yet implemented +#endif +/* Etherboot: depca.h merged, comments from Linux driver retained */ +/* depca.c: A DIGITAL DEPCA & EtherWORKS ethernet driver for linux. + + Written 1994, 1995 by David C. Davies. + + + Copyright 1994 David C. Davies + and + United States Government + (as represented by the Director, National Security Agency). + + Copyright 1995 Digital Equipment Corporation. + + + This software may be used and distributed according to the terms of + the GNU Public License, incorporated herein by reference. + + This driver is written for the Digital Equipment Corporation series + of DEPCA and EtherWORKS ethernet cards: + + DEPCA (the original) + DE100 + DE101 + DE200 Turbo + DE201 Turbo + DE202 Turbo (TP BNC) + DE210 + DE422 (EISA) + + The driver has been tested on DE100, DE200 and DE202 cards in a + relatively busy network. The DE422 has been tested a little. + + This driver will NOT work for the DE203, DE204 and DE205 series of + cards, since they have a new custom ASIC in place of the AMD LANCE + chip. See the 'ewrk3.c' driver in the Linux source tree for running + those cards. + + I have benchmarked the driver with a DE100 at 595kB/s to (542kB/s from) + a DECstation 5000/200. + + The author may be reached at davies@maniac.ultranet.com + + ========================================================================= + + The driver was originally based on the 'lance.c' driver from Donald + Becker which is included with the standard driver distribution for + linux. V0.4 is a complete re-write with only the kernel interface + remaining from the original code. + + 1) Lance.c code in /linux/drivers/net/ + 2) "Ethernet/IEEE 802.3 Family. 1992 World Network Data Book/Handbook", + AMD, 1992 [(800) 222-9323]. + 3) "Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE)", + AMD, Pub. #17881, May 1993. + 4) "Am79C960 PCnet-ISA(tm), Single-Chip Ethernet Controller for ISA", + AMD, Pub. #16907, May 1992 + 5) "DEC EtherWORKS LC Ethernet Controller Owners Manual", + Digital Equipment corporation, 1990, Pub. #EK-DE100-OM.003 + 6) "DEC EtherWORKS Turbo Ethernet Controller Owners Manual", + Digital Equipment corporation, 1990, Pub. #EK-DE200-OM.003 + 7) "DEPCA Hardware Reference Manual", Pub. #EK-DEPCA-PR + Digital Equipment Corporation, 1989 + 8) "DEC EtherWORKS Turbo_(TP BNC) Ethernet Controller Owners Manual", + Digital Equipment corporation, 1991, Pub. #EK-DE202-OM.001 + + + Peter Bauer's depca.c (V0.5) was referred to when debugging V0.1 of this + driver. + + The original DEPCA card requires that the ethernet ROM address counter + be enabled to count and has an 8 bit NICSR. The ROM counter enabling is + only done when a 0x08 is read as the first address octet (to minimise + the chances of writing over some other hardware's I/O register). The + NICSR accesses have been changed to byte accesses for all the cards + supported by this driver, since there is only one useful bit in the MSB + (remote boot timeout) and it is not used. Also, there is a maximum of + only 48kB network RAM for this card. My thanks to Torbjorn Lindh for + help debugging all this (and holding my feet to the fire until I got it + right). + + The DE200 series boards have on-board 64kB RAM for use as a shared + memory network buffer. Only the DE100 cards make use of a 2kB buffer + mode which has not been implemented in this driver (only the 32kB and + 64kB modes are supported [16kB/48kB for the original DEPCA]). + + At the most only 2 DEPCA cards can be supported on the ISA bus because + there is only provision for two I/O base addresses on each card (0x300 + and 0x200). The I/O address is detected by searching for a byte sequence + in the Ethernet station address PROM at the expected I/O address for the + Ethernet PROM. The shared memory base address is 'autoprobed' by + looking for the self test PROM and detecting the card name. When a + second DEPCA is detected, information is placed in the base_addr + variable of the next device structure (which is created if necessary), + thus enabling ethif_probe initialization for the device. More than 2 + EISA cards can be supported, but care will be needed assigning the + shared memory to ensure that each slot has the correct IRQ, I/O address + and shared memory address assigned. + + ************************************************************************ + + NOTE: If you are using two ISA DEPCAs, it is important that you assign + the base memory addresses correctly. The driver autoprobes I/O 0x300 + then 0x200. The base memory address for the first device must be less + than that of the second so that the auto probe will correctly assign the + I/O and memory addresses on the same card. I can't think of a way to do + this unambiguously at the moment, since there is nothing on the cards to + tie I/O and memory information together. + + I am unable to test 2 cards together for now, so this code is + unchecked. All reports, good or bad, are welcome. + + ************************************************************************ + + The board IRQ setting must be at an unused IRQ which is auto-probed + using Donald Becker's autoprobe routines. DEPCA and DE100 board IRQs are + {2,3,4,5,7}, whereas the DE200 is at {5,9,10,11,15}. Note that IRQ2 is + really IRQ9 in machines with 16 IRQ lines. + + No 16MB memory limitation should exist with this driver as DMA is not + used and the common memory area is in low memory on the network card (my + current system has 20MB and I've not had problems yet). + + The ability to load this driver as a loadable module has been added. To + utilise this ability, you have to do <8 things: + + 0) have a copy of the loadable modules code installed on your system. + 1) copy depca.c from the /linux/drivers/net directory to your favourite + temporary directory. + 2) if you wish, edit the source code near line 1530 to reflect the I/O + address and IRQ you're using (see also 5). + 3) compile depca.c, but include -DMODULE in the command line to ensure + that the correct bits are compiled (see end of source code). + 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a + kernel with the depca configuration turned off and reboot. + 5) insmod depca.o [irq=7] [io=0x200] [mem=0xd0000] [adapter_name=DE100] + [Alan Cox: Changed the code to allow command line irq/io assignments] + [Dave Davies: Changed the code to allow command line mem/name + assignments] + 6) run the net startup bits for your eth?? interface manually + (usually /etc/rc.inet[12] at boot time). + 7) enjoy! + + Note that autoprobing is not allowed in loadable modules - the system is + already up and running and you're messing with interrupts. + + To unload a module, turn off the associated interface + 'ifconfig eth?? down' then 'rmmod depca'. + + To assign a base memory address for the shared memory when running as a + loadable module, see 5 above. To include the adapter name (if you have + no PROM but know the card name) also see 5 above. Note that this last + option will not work with kernel built-in depca's. + + The shared memory assignment for a loadable module makes sense to avoid + the 'memory autoprobe' picking the wrong shared memory (for the case of + 2 depca's in a PC). + + ************************************************************************ + Support for MCA EtherWORKS cards added 11-3-98. + Verified to work with up to 2 DE212 cards in a system (although not + fully stress-tested). + + Currently known bugs/limitations: + + Note: with the MCA stuff as a module, it trusts the MCA configuration, + not the command line for IRQ and memory address. You can + specify them if you want, but it will throw your values out. + You still have to pass the IO address it was configured as + though. + + ************************************************************************ + TO DO: + ------ + + + Revision History + ---------------- + + Version Date Description + + 0.1 25-jan-94 Initial writing. + 0.2 27-jan-94 Added LANCE TX hardware buffer chaining. + 0.3 1-feb-94 Added multiple DEPCA support. + 0.31 4-feb-94 Added DE202 recognition. + 0.32 19-feb-94 Tidy up. Improve multi-DEPCA support. + 0.33 25-feb-94 Fix DEPCA ethernet ROM counter enable. + Add jabber packet fix from murf@perftech.com + and becker@super.org + 0.34 7-mar-94 Fix DEPCA max network memory RAM & NICSR access. + 0.35 8-mar-94 Added DE201 recognition. Tidied up. + 0.351 30-apr-94 Added EISA support. Added DE422 recognition. + 0.36 16-may-94 DE422 fix released. + 0.37 22-jul-94 Added MODULE support + 0.38 15-aug-94 Added DBR ROM switch in depca_close(). + Multi DEPCA bug fix. + 0.38axp 15-sep-94 Special version for Alpha AXP Linux V1.0. + 0.381 12-dec-94 Added DE101 recognition, fix multicast bug. + 0.382 9-feb-95 Fix recognition bug reported by . + 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 +#include "console.h" +#include + +/* +** I/O addresses. Note that the 2k buffer option is not supported in +** this driver. +*/ +#define DEPCA_NICSR 0x00 /* Network interface CSR */ +#define DEPCA_RBI 0x02 /* RAM buffer index (2k buffer mode) */ +#define DEPCA_DATA 0x04 /* LANCE registers' data port */ +#define DEPCA_ADDR 0x06 /* LANCE registers' address port */ +#define DEPCA_HBASE 0x08 /* EISA high memory base address reg. */ +#define DEPCA_PROM 0x0c /* Ethernet address ROM data port */ +#define DEPCA_CNFG 0x0c /* EISA Configuration port */ +#define DEPCA_RBSA 0x0e /* RAM buffer starting address (2k buff.) */ + +/* +** These are LANCE registers addressable through nic->ioaddr + DEPCA_ADDR +*/ +#define CSR0 0 +#define CSR1 1 +#define CSR2 2 +#define CSR3 3 + +/* +** NETWORK INTERFACE CSR (NI_CSR) bit definitions +*/ + +#define TO 0x0100 /* Time Out for remote boot */ +#define SHE 0x0080 /* SHadow memory Enable */ +#define BS 0x0040 /* Bank Select */ +#define BUF 0x0020 /* BUFfer size (1->32k, 0->64k) */ +#define RBE 0x0010 /* Remote Boot Enable (1->net boot) */ +#define AAC 0x0008 /* Address ROM Address Counter (1->enable) */ +#define _128KB 0x0008 /* 128kB Network RAM (1->enable) */ +#define IM 0x0004 /* Interrupt Mask (1->mask) */ +#define IEN 0x0002 /* Interrupt tristate ENable (1->enable) */ +#define LED 0x0001 /* LED control */ + +/* +** Control and Status Register 0 (CSR0) bit definitions +*/ + +#define ERR 0x8000 /* Error summary */ +#define BABL 0x4000 /* Babble transmitter timeout error */ +#define CERR 0x2000 /* Collision Error */ +#define MISS 0x1000 /* Missed packet */ +#define MERR 0x0800 /* Memory Error */ +#define RINT 0x0400 /* Receiver Interrupt */ +#define TINT 0x0200 /* Transmit Interrupt */ +#define IDON 0x0100 /* Initialization Done */ +#define INTR 0x0080 /* Interrupt Flag */ +#define INEA 0x0040 /* Interrupt Enable */ +#define RXON 0x0020 /* Receiver on */ +#define TXON 0x0010 /* Transmitter on */ +#define TDMD 0x0008 /* Transmit Demand */ +#define STOP 0x0004 /* Stop */ +#define STRT 0x0002 /* Start */ +#define INIT 0x0001 /* Initialize */ +#define INTM 0xff00 /* Interrupt Mask */ +#define INTE 0xfff0 /* Interrupt Enable */ + +/* +** CONTROL AND STATUS REGISTER 3 (CSR3) +*/ + +#define BSWP 0x0004 /* Byte SWaP */ +#define ACON 0x0002 /* ALE control */ +#define BCON 0x0001 /* Byte CONtrol */ + +/* +** Initialization Block Mode Register +*/ + +#define PROM 0x8000 /* Promiscuous Mode */ +#define EMBA 0x0080 /* Enable Modified Back-off Algorithm */ +#define INTL 0x0040 /* Internal Loopback */ +#define DRTY 0x0020 /* Disable Retry */ +#define COLL 0x0010 /* Force Collision */ +#define DTCR 0x0008 /* Disable Transmit CRC */ +#define LOOP 0x0004 /* Loopback */ +#define DTX 0x0002 /* Disable the Transmitter */ +#define DRX 0x0001 /* Disable the Receiver */ + +/* +** Receive Message Descriptor 1 (RMD1) bit definitions. +*/ + +#define R_OWN 0x80000000 /* Owner bit 0 = host, 1 = lance */ +#define R_ERR 0x4000 /* Error Summary */ +#define R_FRAM 0x2000 /* Framing Error */ +#define R_OFLO 0x1000 /* Overflow Error */ +#define R_CRC 0x0800 /* CRC Error */ +#define R_BUFF 0x0400 /* Buffer Error */ +#define R_STP 0x0200 /* Start of Packet */ +#define R_ENP 0x0100 /* End of Packet */ + +/* +** Transmit Message Descriptor 1 (TMD1) bit definitions. +*/ + +#define T_OWN 0x80000000 /* Owner bit 0 = host, 1 = lance */ +#define T_ERR 0x4000 /* Error Summary */ +#define T_ADD_FCS 0x2000 /* More the 1 retry needed to Xmit */ +#define T_MORE 0x1000 /* >1 retry to transmit packet */ +#define T_ONE 0x0800 /* 1 try needed to transmit the packet */ +#define T_DEF 0x0400 /* Deferred */ +#define T_STP 0x02000000 /* Start of Packet */ +#define T_ENP 0x01000000 /* End of Packet */ +#define T_FLAGS 0xff000000 /* TX Flags Field */ + +/* +** Transmit Message Descriptor 3 (TMD3) bit definitions. +*/ + +#define TMD3_BUFF 0x8000 /* BUFFer error */ +#define TMD3_UFLO 0x4000 /* UnderFLOw error */ +#define TMD3_RES 0x2000 /* REServed */ +#define TMD3_LCOL 0x1000 /* Late COLlision */ +#define TMD3_LCAR 0x0800 /* Loss of CARrier */ +#define TMD3_RTRY 0x0400 /* ReTRY error */ + +/* +** Ethernet PROM defines +*/ +#define PROBE_LENGTH 32 + +/* +** Set the number of Tx and Rx buffers. Ensure that the memory requested +** here is <= to the amount of shared memory set up by the board switches. +** The number of descriptors MUST BE A POWER OF 2. +** +** total_memory = NUM_RX_DESC*(8+RX_BUFF_SZ) + NUM_TX_DESC*(8+TX_BUFF_SZ) +*/ +#define NUM_RX_DESC 2 /* Number of RX descriptors */ +#define NUM_TX_DESC 2 /* Number of TX descriptors */ +#define RX_BUFF_SZ 1536 /* Buffer size for each Rx buffer */ +#define TX_BUFF_SZ 1536 /* Buffer size for each Tx buffer */ + +/* +** ISA Bus defines +*/ +#ifndef DEPCA_MODEL +#define DEPCA_MODEL DEPCA +#endif + +static enum { + DEPCA, DE100, DE101, DE200, DE201, DE202, DE210, DE212, DE422, unknown +} adapter = DEPCA_MODEL; + +/* +** Name <-> Adapter mapping +*/ + +static char *adapter_name[] = { + "DEPCA", + "DE100","DE101", + "DE200","DE201","DE202", + "DE210","DE212", + "DE422", + "" +}; + +#ifndef DEPCA_RAM_BASE +#define DEPCA_RAM_BASE 0xd0000 +#endif + +/* +** Memory Alignment. Each descriptor is 4 longwords long. To force a +** particular alignment on the TX descriptor, adjust DESC_SKIP_LEN and +** DESC_ALIGN. ALIGN aligns the start address of the private memory area +** and hence the RX descriptor ring's first entry. +*/ +#define ALIGN4 ((u32)4 - 1) /* 1 longword align */ +#define ALIGN8 ((u32)8 - 1) /* 2 longword (quadword) align */ +#define ALIGN ALIGN8 /* Keep the LANCE happy... */ + +/* +** The DEPCA Rx and Tx ring descriptors. +*/ +struct depca_rx_desc { + volatile s32 base; + s16 buf_length; /* This length is negative 2's complement! */ + s16 msg_length; /* This length is "normal". */ +}; + +struct depca_tx_desc { + volatile s32 base; + s16 length; /* This length is negative 2's complement! */ + s16 misc; /* Errors and TDR info */ +}; + +#define LA_MASK 0x0000ffff /* LANCE address mask for mapping network RAM + to LANCE memory address space */ + +/* +** The Lance initialization block, described in databook, in common memory. +*/ +struct depca_init { + u16 mode; /* Mode register */ + u8 phys_addr[ETH_ALEN]; /* Physical ethernet address */ + u8 mcast_table[8]; /* Multicast Hash Table. */ + u32 rx_ring; /* Rx ring base pointer & ring length */ + u32 tx_ring; /* Tx ring base pointer & ring length */ +}; + +struct depca_private { + struct depca_rx_desc *rx_ring; + struct depca_tx_desc *tx_ring; + struct depca_init init_block; /* Shadow init block */ + char *rx_memcpy[NUM_RX_DESC]; + char *tx_memcpy[NUM_TX_DESC]; + u32 bus_offset; /* ISA bus address offset */ + u32 sh_mem; /* address of shared mem */ + u32 dma_buffs; /* Rx & Tx buffer start */ + int rx_cur, tx_cur; /* Next free ring entry */ + int txRingMask, rxRingMask; + s32 rx_rlen, tx_rlen; + /* log2([rt]xRingMask+1) for the descriptors */ +}; + +static Address mem_start = DEPCA_RAM_BASE; +static Address mem_len, offset; +static struct depca_private lp; + +/* +** Miscellaneous defines... +*/ +#define STOP_DEPCA(ioaddr) \ + outw(CSR0, ioaddr + DEPCA_ADDR);\ + outw(STOP, ioaddr + DEPCA_DATA) + +/* Initialize the lance Rx and Tx descriptor rings. */ +static void depca_init_ring(struct nic *nic) +{ + int i; + u32 p; + + lp.rx_cur = lp.tx_cur = 0; + /* Initialize the base addresses and length of each buffer in the ring */ + for (i = 0; i <= lp.rxRingMask; i++) { + writel((p = lp.dma_buffs + i * RX_BUFF_SZ) | R_OWN, &lp.rx_ring[i].base); + writew(-RX_BUFF_SZ, &lp.rx_ring[i].buf_length); + lp.rx_memcpy[i] = (char *) (p + lp.bus_offset); + } + for (i = 0; i <= lp.txRingMask; i++) { + writel((p = lp.dma_buffs + (i + lp.txRingMask + 1) * TX_BUFF_SZ) & 0x00ffffff, &lp.tx_ring[i].base); + lp.tx_memcpy[i] = (char *) (p + lp.bus_offset); + } + + /* Set up the initialization block */ + lp.init_block.rx_ring = ((u32) ((u32) lp.rx_ring) & LA_MASK) | lp.rx_rlen; + lp.init_block.tx_ring = ((u32) ((u32) lp.tx_ring) & LA_MASK) | lp.tx_rlen; + for (i = 0; i < ETH_ALEN; i++) + lp.init_block.phys_addr[i] = nic->node_addr[i]; + lp.init_block.mode = 0x0000; /* Enable the Tx and Rx */ + memset(lp.init_block.mcast_table, 0, sizeof(lp.init_block.mcast_table)); +} + +static inline void LoadCSRs(struct nic *nic) +{ + outw(CSR1, nic->ioaddr + DEPCA_ADDR); /* initialisation block address LSW */ + outw((u16) (lp.sh_mem & LA_MASK), nic->ioaddr + DEPCA_DATA); + outw(CSR2, nic->ioaddr + DEPCA_ADDR); /* initialisation block address MSW */ + outw((u16) ((lp.sh_mem & LA_MASK) >> 16), nic->ioaddr + DEPCA_DATA); + outw(CSR3, nic->ioaddr + DEPCA_ADDR); /* ALE control */ + outw(ACON, nic->ioaddr + DEPCA_DATA); + outw(CSR0, nic->ioaddr + DEPCA_ADDR); /* Point back to CSR0 */ +} + +static inline int InitRestartDepca(struct nic *nic) +{ + int i; + + /* Copy the shadow init_block to shared memory */ + memcpy_toio((char *)lp.sh_mem, &lp.init_block, sizeof(struct depca_init)); + outw(CSR0, nic->ioaddr + DEPCA_ADDR); /* point back to CSR0 */ + outw(INIT, nic->ioaddr + DEPCA_DATA); /* initialise DEPCA */ + + for (i = 0; i < 100 && !(inw(nic->ioaddr + DEPCA_DATA) & IDON); i++) + ; + if (i < 100) { + /* clear IDON by writing a 1, and start LANCE */ + outw(IDON | STRT, nic->ioaddr + DEPCA_DATA); + } else { + printf("DEPCA not initialised\n"); + return (1); + } + return (0); +} + +/************************************************************************** +RESET - Reset adapter +***************************************************************************/ +static void depca_reset(struct nic *nic) +{ + s16 nicsr; + int i, j; + + STOP_DEPCA(nic->ioaddr); + nicsr = inb(nic->ioaddr + DEPCA_NICSR); + nicsr = ((nicsr & ~SHE & ~RBE & ~IEN) | IM); + outb(nicsr, nic->ioaddr + DEPCA_NICSR); + if (inw(nic->ioaddr + DEPCA_DATA) != STOP) + { + printf("depca: Cannot stop NIC\n"); + return; + } + + /* Initialisation block */ + lp.sh_mem = mem_start; + mem_start += sizeof(struct depca_init); + /* Tx & Rx descriptors (aligned to a quadword boundary) */ + mem_start = (mem_start + ALIGN) & ~ALIGN; + lp.rx_ring = (struct depca_rx_desc *) mem_start; + mem_start += (sizeof(struct depca_rx_desc) * NUM_RX_DESC); + lp.tx_ring = (struct depca_tx_desc *) mem_start; + mem_start += (sizeof(struct depca_tx_desc) * NUM_TX_DESC); + + lp.bus_offset = mem_start & 0x00ff0000; + /* LANCE re-mapped start address */ + lp.dma_buffs = mem_start & LA_MASK; + + /* Finish initialising the ring information. */ + lp.rxRingMask = NUM_RX_DESC - 1; + lp.txRingMask = NUM_TX_DESC - 1; + + /* Calculate Tx/Rx RLEN size for the descriptors. */ + for (i = 0, j = lp.rxRingMask; j > 0; i++) { + j >>= 1; + } + lp.rx_rlen = (s32) (i << 29); + for (i = 0, j = lp.txRingMask; j > 0; i++) { + j >>= 1; + } + lp.tx_rlen = (s32) (i << 29); + + /* Load the initialisation block */ + depca_init_ring(nic); + LoadCSRs(nic); + InitRestartDepca(nic); +} + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int depca_poll(struct nic *nic, int retrieve) +{ + int entry; + u32 status; + + entry = lp.rx_cur; + if ((status = readl(&lp.rx_ring[entry].base) & R_OWN)) + return (0); + + if ( ! retrieve ) return 1; + + memcpy(nic->packet, lp.rx_memcpy[entry], nic->packetlen = lp.rx_ring[entry].msg_length); + lp.rx_ring[entry].base |= R_OWN; + lp.rx_cur = (++lp.rx_cur) & lp.rxRingMask; + return (1); +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static void depca_transmit( + struct nic *nic, + const char *d, /* Destination */ + unsigned int t, /* Type */ + unsigned int s, /* size */ + const char *p) /* Packet */ +{ + int entry, len; + char *mem; + + /* send the packet to destination */ + /* + ** Caution: the right order is important here... dont + ** setup the ownership rights until all the other + ** information is in place + */ + mem = lp.tx_memcpy[entry = lp.tx_cur]; + memcpy_toio(mem, d, ETH_ALEN); + memcpy_toio(mem + ETH_ALEN, nic->node_addr, ETH_ALEN); + mem[ETH_ALEN * 2] = t >> 8; + mem[ETH_ALEN * 2 + 1] = t; + memcpy_toio(mem + ETH_HLEN, p, s); + s += ETH_HLEN; + len = (s < ETH_ZLEN ? ETH_ZLEN : s); + /* clean out flags */ + writel(readl(&lp.tx_ring[entry].base) & ~T_FLAGS, &lp.tx_ring[entry].base); + /* clears other error flags */ + writew(0x0000, &lp.tx_ring[entry].misc); + /* packet length in buffer */ + writew(-len, &lp.tx_ring[entry].length); + /* start and end of packet, ownership */ + writel(readl(&lp.tx_ring[entry].base) | (T_STP|T_ENP|T_OWN), &lp.tx_ring[entry].base); + /* update current pointers */ + lp.tx_cur = (++lp.tx_cur) & lp.txRingMask; +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void depca_disable ( struct nic *nic ) { + depca_reset(nic); + + STOP_DEPCA(nic->ioaddr); +} + +/************************************************************************** +IRQ - Interrupt Control +***************************************************************************/ +static void depca_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + +/* +** Look for a special sequence in the Ethernet station address PROM that +** is common across all DEPCA products. Note that the original DEPCA needs +** its ROM address counter to be initialized and enabled. Only enable +** if the first address octet is a 0x08 - this minimises the chances of +** messing around with some other hardware, but it assumes that this DEPCA +** card initialized itself correctly. +** +** Search the Ethernet address ROM for the signature. Since the ROM address +** counter can start at an arbitrary point, the search must include the entire +** probe sequence length plus the (length_of_the_signature - 1). +** Stop the search IMMEDIATELY after the signature is found so that the +** PROM address counter is correctly positioned at the start of the +** ethernet address for later read out. +*/ + + +/* + * Ugly, ugly, ugly. I can't quite make out where the split should be + * between probe1 and probe()... + * + */ +static u8 nicsr; + + +static int depca_probe1 ( isa_probe_addr_t ioaddr ) { + u8 data; + /* This is only correct for little endian machines, but then + Etherboot doesn't work on anything but a PC */ + u8 sig[] = { 0xFF, 0x00, 0x55, 0xAA, 0xFF, 0x00, 0x55, 0xAA }; + int i, j; + + data = inb(ioaddr + DEPCA_PROM); /* clear counter on DEPCA */ + data = inb(ioaddr + DEPCA_PROM); /* read data */ + if (data == 0x8) { + nicsr = inb(ioaddr + DEPCA_NICSR); + nicsr |= AAC; + outb(nicsr, ioaddr + DEPCA_NICSR); + } + for (i = 0, j = 0; j < (int)sizeof(sig) && i < PROBE_LENGTH+((int)sizeof(sig))-1; ++i) { + data = inb(ioaddr + DEPCA_PROM); + if (data == sig[j]) /* track signature */ + ++j; + else + j = (data == sig[0]) ? 1 : 0; + } + if (j != sizeof(sig)) + return (0); + /* put the card in its initial state */ + STOP_DEPCA(ioaddr); + nicsr = ((inb(ioaddr + DEPCA_NICSR) & ~SHE & ~RBE & ~IEN) | IM); + outb(nicsr, ioaddr + DEPCA_NICSR); + if (inw(ioaddr + DEPCA_DATA) != STOP) + return (0); + memcpy((char *)mem_start, sig, sizeof(sig)); + if (memcmp((char *)mem_start, sig, sizeof(sig)) != 0) + return (0); + + return 1; +} + +static struct nic_operations depca_operations = { + .connect = dummy_connect, + .poll = depca_poll, + .transmit = depca_transmit, + .irq = depca_irq, + +}; + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ +static int depca_probe ( struct nic *nic, struct isa_device *isa ) { + + int i, j; + long sum, chksum; + + nic->irqno = 0; + nic->ioaddr = isa->ioaddr; + + for (i = 0, j = 0, sum = 0; j < 3; j++) { + sum <<= 1; + if (sum > 0xFFFF) + sum -= 0xFFFF; + sum += (u8)(nic->node_addr[i++] = inb(nic->ioaddr + DEPCA_PROM)); + sum += (u16)((nic->node_addr[i++] = inb(nic->ioaddr + DEPCA_PROM)) << 8); + if (sum > 0xFFFF) + sum -= 0xFFFF; + } + if (sum == 0xFFFF) + sum = 0; + chksum = (u8)inb(nic->ioaddr + DEPCA_PROM); + chksum |= (u16)(inb(nic->ioaddr + DEPCA_PROM) << 8); + mem_len = (adapter == DEPCA) ? (48 << 10) : (64 << 10); + offset = 0; + if (nicsr & BUF) { + offset = 0x8000; + nicsr &= ~BS; + mem_len -= (32 << 10); + } + if (adapter != DEPCA) /* enable shadow RAM */ + outb(nicsr |= SHE, nic->ioaddr + DEPCA_NICSR); + DBG ( "%s base %4.4x, memory [%4.4lx-%4.4lx] addr %s", + adapter_name[adapter], nic->ioaddr, mem_start, + mem_start + mem_len, eth_ntoa ( nic->node_addr ) ); + if (sum != chksum) + printf(" (bad checksum)"); + putchar('\n'); + + depca_reset(nic); + + /* point to NIC specific routines */ + nic->nic_op = &depca_operations; + return 1; +} + +static isa_probe_addr_t depca_probe_addrs[] = { + 0x300, 0x200, +}; + +ISA_DRIVER ( depca_driver, depca_probe_addrs, depca_probe1, + GENERIC_ISAPNP_VENDOR, 0x80f7 ); + +DRIVER ( "depce", nic_driver, isa_driver, depca_driver, + depca_probe, depca_disable ); + +ISA_ROM ( "depca", "Digital DE100 and DE200" ); + +#endif + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/dmfe.c b/gpxe/src/drivers/net/dmfe.c new file mode 100644 index 0000000..26021e6 --- /dev/null +++ b/gpxe/src/drivers/net/dmfe.c @@ -0,0 +1,1224 @@ +/************************************************************************** +* +* dmfe.c -- Etherboot device driver for the Davicom +* DM9102/DM9102A/DM9102A+DM9801/DM9102A+DM9802 NIC fast ethernet card +* +* Written 2003-2003 by Timothy Legge +* +* 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 +#include + +/* #define EDEBUG 1 */ +#ifdef EDEBUG +#define dprintf(x) printf x +#else +#define dprintf(x) +#endif + +/* Condensed operations for readability. */ +#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr)) + +/* Board/System/Debug information/definition ---------------- */ +#define PCI_DM9132_ID 0x91321282 /* Davicom DM9132 ID */ +#define PCI_DM9102_ID 0x91021282 /* Davicom DM9102 ID */ +#define PCI_DM9100_ID 0x91001282 /* Davicom DM9100 ID */ +#define PCI_DM9009_ID 0x90091282 /* Davicom DM9009 ID */ + +#define DM9102_IO_SIZE 0x80 +#define DM9102A_IO_SIZE 0x100 +#define TX_MAX_SEND_CNT 0x1 /* Maximum tx packet per time */ +#define TX_DESC_CNT 0x10 /* Allocated Tx descriptors */ +#define RX_DESC_CNT 0x20 /* Allocated Rx descriptors */ +#define TX_FREE_DESC_CNT (TX_DESC_CNT - 2) /* Max TX packet count */ +#define TX_WAKE_DESC_CNT (TX_DESC_CNT - 3) /* TX wakeup count */ +#define DESC_ALL_CNT (TX_DESC_CNT + RX_DESC_CNT) +#define TX_BUF_ALLOC 0x600 +#define RX_ALLOC_SIZE 0x620 +#define DM910X_RESET 1 +#define CR0_DEFAULT 0x00E00000 /* TX & RX burst mode */ +#define CR6_DEFAULT 0x00080000 /* HD */ +#define CR7_DEFAULT 0x180c1 +#define CR15_DEFAULT 0x06 /* TxJabber RxWatchdog */ +#define TDES0_ERR_MASK 0x4302 /* TXJT, LC, EC, FUE */ +#define MAX_PACKET_SIZE 1514 +#define DMFE_MAX_MULTICAST 14 +#define RX_COPY_SIZE 100 +#define MAX_CHECK_PACKET 0x8000 +#define DM9801_NOISE_FLOOR 8 +#define DM9802_NOISE_FLOOR 5 + +#define DMFE_10MHF 0 +#define DMFE_100MHF 1 +#define DMFE_10MFD 4 +#define DMFE_100MFD 5 +#define DMFE_AUTO 8 +#define DMFE_1M_HPNA 0x10 + +#define DMFE_TXTH_72 0x400000 /* TX TH 72 byte */ +#define DMFE_TXTH_96 0x404000 /* TX TH 96 byte */ +#define DMFE_TXTH_128 0x0000 /* TX TH 128 byte */ +#define DMFE_TXTH_256 0x4000 /* TX TH 256 byte */ +#define DMFE_TXTH_512 0x8000 /* TX TH 512 byte */ +#define DMFE_TXTH_1K 0xC000 /* TX TH 1K byte */ + +#define DMFE_TIMER_WUT (jiffies + HZ * 1) /* timer wakeup time : 1 second */ +#define DMFE_TX_TIMEOUT ((3*HZ)/2) /* tx packet time-out time 1.5 s" */ +#define DMFE_TX_KICK (HZ/2) /* tx packet Kick-out time 0.5 s" */ + +#define DMFE_DBUG(dbug_now, msg, value) if (dmfe_debug || (dbug_now)) printk(KERN_ERR DRV_NAME ": %s %lx\n", (msg), (long) (value)) + +#define SHOW_MEDIA_TYPE(mode) printk(KERN_ERR DRV_NAME ": Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half"); + + +/* CR9 definition: SROM/MII */ +#define CR9_SROM_READ 0x4800 +#define CR9_SRCS 0x1 +#define CR9_SRCLK 0x2 +#define CR9_CRDOUT 0x8 +#define SROM_DATA_0 0x0 +#define SROM_DATA_1 0x4 +#define PHY_DATA_1 0x20000 +#define PHY_DATA_0 0x00000 +#define MDCLKH 0x10000 + +#define PHY_POWER_DOWN 0x800 + +#define SROM_V41_CODE 0x14 + +#define SROM_CLK_WRITE(data, ioaddr) outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);udelay(5);outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr);udelay(5);outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);udelay(5); + +#define __CHK_IO_SIZE(pci_id, dev_rev) ( ((pci_id)==PCI_DM9132_ID) || ((dev_rev) >= 0x02000030) ) ? DM9102A_IO_SIZE: DM9102_IO_SIZE +#define CHK_IO_SIZE(pci_dev, dev_rev) __CHK_IO_SIZE(((pci_dev)->device << 16) | (pci_dev)->vendor, dev_rev) + +/* Sten Check */ +#define DEVICE net_device + +/* Structure/enum declaration ------------------------------- */ +struct tx_desc { + u32 tdes0, tdes1, tdes2, tdes3; /* Data for the card */ + void * tx_buf_ptr; /* Data for us */ + struct tx_desc * next_tx_desc; +} __attribute__ ((aligned(32))); + +struct rx_desc { + u32 rdes0, rdes1, rdes2, rdes3; /* Data for the card */ + void * rx_skb_ptr; /* Data for us */ + struct rx_desc * next_rx_desc; +} __attribute__ ((aligned(32))); + +static struct dmfe_private { + u32 chip_id; /* Chip vendor/Device ID */ + u32 chip_revision; /* Chip revision */ + u32 cr0_data; +// u32 cr5_data; + u32 cr6_data; + u32 cr7_data; + u32 cr15_data; + + u16 HPNA_command; /* For HPNA register 16 */ + u16 HPNA_timer; /* For HPNA remote device check */ + u16 NIC_capability; /* NIC media capability */ + u16 PHY_reg4; /* Saved Phyxcer register 4 value */ + + u8 HPNA_present; /* 0:none, 1:DM9801, 2:DM9802 */ + u8 chip_type; /* Keep DM9102A chip type */ + u8 media_mode; /* user specify media mode */ + u8 op_mode; /* real work media mode */ + u8 phy_addr; + u8 dm910x_chk_mode; /* Operating mode check */ + + /* NIC SROM data */ + unsigned char srom[128]; + /* Etherboot Only */ + u8 cur_tx; + u8 cur_rx; +} dfx; + +static struct dmfe_private *db; + +enum dmfe_offsets { + DCR0 = 0x00, DCR1 = 0x08, DCR2 = 0x10, DCR3 = 0x18, DCR4 = 0x20, + DCR5 = 0x28, DCR6 = 0x30, DCR7 = 0x38, DCR8 = 0x40, DCR9 = 0x48, + DCR10 = 0x50, DCR11 = 0x58, DCR12 = 0x60, DCR13 = 0x68, DCR14 = + 0x70, + DCR15 = 0x78 +}; + +enum dmfe_CR6_bits { + CR6_RXSC = 0x2, CR6_PBF = 0x8, CR6_PM = 0x40, CR6_PAM = 0x80, + CR6_FDM = 0x200, CR6_TXSC = 0x2000, CR6_STI = 0x100000, + CR6_SFT = 0x200000, CR6_RXA = 0x40000000, CR6_NO_PURGE = 0x20000000 +}; + +/* Global variable declaration ----------------------------- */ +static struct nic_operations dmfe_operations; + +static unsigned char dmfe_media_mode = DMFE_AUTO; +static u32 dmfe_cr6_user_set; + +/* For module input parameter */ +static u8 chkmode = 1; +static u8 HPNA_mode; /* Default: Low Power/High Speed */ +static u8 HPNA_rx_cmd; /* Default: Disable Rx remote command */ +static u8 HPNA_tx_cmd; /* Default: Don't issue remote command */ +static u8 HPNA_NoiseFloor; /* Default: HPNA NoiseFloor */ +static u8 SF_mode; /* Special Function: 1:VLAN, 2:RX Flow Control + 4: TX pause packet */ + + +/********************************************** +* Descriptor Ring and Buffer defination +***********************************************/ +struct { + struct tx_desc txd[TX_DESC_CNT] __attribute__ ((aligned(32))); + unsigned char txb[TX_BUF_ALLOC * TX_DESC_CNT] + __attribute__ ((aligned(32))); + struct rx_desc rxd[RX_DESC_CNT] __attribute__ ((aligned(32))); + unsigned char rxb[RX_ALLOC_SIZE * RX_DESC_CNT] + __attribute__ ((aligned(32))); +} dmfe_bufs __shared; +#define txd dmfe_bufs.txd +#define txb dmfe_bufs.txb +#define rxd dmfe_bufs.rxd +#define rxb dmfe_bufs.rxb + +/* NIC specific static variables go here */ +static long int BASE; + +static u16 read_srom_word(long ioaddr, int offset); +static void dmfe_init_dm910x(struct nic *nic); +static void dmfe_descriptor_init(struct nic *, unsigned long ioaddr); +static void update_cr6(u32, unsigned long); +static void send_filter_frame(struct nic *nic); +static void dm9132_id_table(struct nic *nic); + +static u16 phy_read(unsigned long, u8, u8, u32); +static void phy_write(unsigned long, u8, u8, u16, u32); +static void phy_write_1bit(unsigned long, u32); +static u16 phy_read_1bit(unsigned long); +static void dmfe_set_phyxcer(struct nic *nic); + +static void dmfe_parse_srom(struct nic *nic); +static void dmfe_program_DM9801(struct nic *nic, int); +static void dmfe_program_DM9802(struct nic *nic); + +static void dmfe_reset(struct nic *nic) +{ + /* system variable init */ + db->cr6_data = CR6_DEFAULT | dmfe_cr6_user_set; + + db->NIC_capability = 0xf; /* All capability */ + db->PHY_reg4 = 0x1e0; + + /* CR6 operation mode decision */ + if (!chkmode || (db->chip_id == PCI_DM9132_ID) || + (db->chip_revision >= 0x02000030)) { + db->cr6_data |= DMFE_TXTH_256; + db->cr0_data = CR0_DEFAULT; + db->dm910x_chk_mode = 4; /* Enter the normal mode */ + } else { + db->cr6_data |= CR6_SFT; /* Store & Forward mode */ + db->cr0_data = 0; + db->dm910x_chk_mode = 1; /* Enter the check mode */ + } + /* Initilize DM910X board */ + dmfe_init_dm910x(nic); + + return; +} + +/* Initilize DM910X board + * Reset DM910X board + * Initilize TX/Rx descriptor chain structure + * Send the set-up frame + * Enable Tx/Rx machine + */ + +static void dmfe_init_dm910x(struct nic *nic) +{ + unsigned long ioaddr = BASE; + + /* Reset DM910x MAC controller */ + outl(DM910X_RESET, ioaddr + DCR0); /* RESET MAC */ + udelay(100); + outl(db->cr0_data, ioaddr + DCR0); + udelay(5); + + /* Phy addr : DM910(A)2/DM9132/9801, phy address = 1 */ + db->phy_addr = 1; + + /* Parser SROM and media mode */ + dmfe_parse_srom(nic); + db->media_mode = dmfe_media_mode; + + /* RESET Phyxcer Chip by GPR port bit 7 */ + outl(0x180, ioaddr + DCR12); /* Let bit 7 output port */ + if (db->chip_id == PCI_DM9009_ID) { + outl(0x80, ioaddr + DCR12); /* Issue RESET signal */ + mdelay(300); /* Delay 300 ms */ + } + outl(0x0, ioaddr + DCR12); /* Clear RESET signal */ + + /* Process Phyxcer Media Mode */ + if (!(db->media_mode & 0x10)) /* Force 1M mode */ + dmfe_set_phyxcer(nic); + + /* Media Mode Process */ + if (!(db->media_mode & DMFE_AUTO)) + db->op_mode = db->media_mode; /* Force Mode */ + + /* Initiliaze Transmit/Receive decriptor and CR3/4 */ + dmfe_descriptor_init(nic, ioaddr); + + /* tx descriptor start pointer */ + outl(virt_to_le32desc(&txd[0]), ioaddr + DCR4); /* TX DESC address */ + + /* rx descriptor start pointer */ + outl(virt_to_le32desc(&rxd[0]), ioaddr + DCR3); /* RX DESC address */ + + /* Init CR6 to program DM910x operation */ + update_cr6(db->cr6_data, ioaddr); + + /* Send setup frame */ + if (db->chip_id == PCI_DM9132_ID) { + dm9132_id_table(nic); /* DM9132 */ + } else { + send_filter_frame(nic); /* DM9102/DM9102A */ + } + + /* Init CR7, interrupt active bit */ + db->cr7_data = CR7_DEFAULT; + outl(db->cr7_data, ioaddr + DCR7); + /* Init CR15, Tx jabber and Rx watchdog timer */ + outl(db->cr15_data, ioaddr + DCR15); + /* Enable DM910X Tx/Rx function */ + db->cr6_data |= CR6_RXSC | CR6_TXSC | 0x40000; + update_cr6(db->cr6_data, ioaddr); +} +#ifdef EDEBUG +void hex_dump(const char *data, const unsigned int len); +#endif +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int dmfe_poll(struct nic *nic, int retrieve) +{ + u32 rdes0; + int entry = db->cur_rx % RX_DESC_CNT; + int rxlen; + rdes0 = le32_to_cpu(rxd[entry].rdes0); + if (rdes0 & 0x80000000) + return 0; + + if (!retrieve) + return 1; + + if ((rdes0 & 0x300) != 0x300) { + /* A packet without First/Last flag */ + printf("strange Packet\n"); + rxd[entry].rdes0 = cpu_to_le32(0x80000000); + return 0; + } else { + /* A packet with First/Last flag */ + rxlen = ((rdes0 >> 16) & 0x3fff) - 4; + /* error summary bit check */ + if (rdes0 & 0x8000) { + printf("Error\n"); + return 0; + } + if (!(rdes0 & 0x8000) || + ((db->cr6_data & CR6_PM) && (rxlen > 6))) { + if (db->dm910x_chk_mode & 1) + printf("Silly check mode\n"); + + nic->packetlen = rxlen; + memcpy(nic->packet, rxb + (entry * RX_ALLOC_SIZE), + nic->packetlen); + } + } + rxd[entry].rdes0 = cpu_to_le32(0x80000000); + db->cur_rx++; + return 1; +} + +static void dmfe_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static void dmfe_transmit(struct nic *nic, + const char *dest, /* Destination */ + unsigned int type, /* Type */ + unsigned int size, /* size */ + const char *packet) /* Packet */ +{ + u16 nstype; + u8 *ptxb; + + ptxb = &txb[db->cur_tx]; + + /* Stop Tx */ + outl(0, BASE + DCR7); + memcpy(ptxb, dest, ETH_ALEN); + memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN); + nstype = htons((u16) type); + memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2); + memcpy(ptxb + ETH_HLEN, packet, size); + + size += ETH_HLEN; + while (size < ETH_ZLEN) + ptxb[size++] = '\0'; + + /* setup the transmit descriptor */ + txd[db->cur_tx].tdes1 = cpu_to_le32(0xe1000000 | size); + txd[db->cur_tx].tdes0 = cpu_to_le32(0x80000000); /* give ownership to device */ + + /* immediate transmit demand */ + outl(0x1, BASE + DCR1); + outl(db->cr7_data, BASE + DCR7); + + /* Point to next TX descriptor */ + db->cur_tx++; + db->cur_tx = db->cur_tx % TX_DESC_CNT; +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void dmfe_disable ( struct nic *nic __unused ) { + /* Reset & stop DM910X board */ + outl(DM910X_RESET, BASE + DCR0); + udelay(5); + phy_write(BASE, db->phy_addr, 0, 0x8000, db->chip_id); + +} + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ + +#define board_found 1 +#define valid_link 0 +static int dmfe_probe ( struct nic *nic, struct pci_device *pci ) { + + uint32_t dev_rev, pci_pmr; + int i; + + if (pci->ioaddr == 0) + return 0; + + BASE = pci->ioaddr; + printf("dmfe.c: Found %s Vendor=0x%hX Device=0x%hX\n", + pci->driver_name, pci->vendor, pci->device); + + /* Read Chip revision */ + pci_read_config_dword(pci, PCI_REVISION_ID, &dev_rev); + dprintf(("Revision %lX\n", dev_rev)); + + /* point to private storage */ + db = &dfx; + + db->chip_id = ((u32) pci->device << 16) | pci->vendor; + BASE = pci_bar_start(pci, PCI_BASE_ADDRESS_0); + db->chip_revision = dev_rev; + + pci_read_config_dword(pci, 0x50, &pci_pmr); + pci_pmr &= 0x70000; + if ((pci_pmr == 0x10000) && (dev_rev == 0x02000031)) + db->chip_type = 1; /* DM9102A E3 */ + else + db->chip_type = 0; + + dprintf(("Chip type : %d\n", db->chip_type)); + + /* read 64 word srom data */ + for (i = 0; i < 64; i++) + ((u16 *) db->srom)[i] = cpu_to_le16(read_srom_word(BASE, i)); + + /* Set Node address */ + for (i = 0; i < 6; i++) + nic->node_addr[i] = db->srom[20 + i]; + + /* Print out some hardware info */ + DBG ( "%s: %s at ioaddr %4.4lx\n", pci->driver_name, eth_ntoa ( nic->node_addr ), BASE ); + + /* Set the card as PCI Bus Master */ + adjust_pci_device(pci); + + dmfe_reset(nic); + + nic->irqno = 0; + nic->ioaddr = pci->ioaddr; + + /* point to NIC specific routines */ + nic->nic_op = &dmfe_operations; + + return 1; +} + +/* + * Initialize transmit/Receive descriptor + * Using Chain structure, and allocate Tx/Rx buffer + */ + +static void dmfe_descriptor_init(struct nic *nic __unused, unsigned long ioaddr) +{ + int i; + db->cur_tx = 0; + db->cur_rx = 0; + + /* tx descriptor start pointer */ + outl(virt_to_le32desc(&txd[0]), ioaddr + DCR4); /* TX DESC address */ + + /* rx descriptor start pointer */ + outl(virt_to_le32desc(&rxd[0]), ioaddr + DCR3); /* RX DESC address */ + + /* Init Transmit chain */ + for (i = 0; i < TX_DESC_CNT; i++) { + txd[i].tx_buf_ptr = &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 = &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 = &txd[0]; + + /* receive descriptor chain */ + for (i = 0; i < RX_DESC_CNT; i++) { + rxd[i].rx_skb_ptr = &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 = &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 = &rxd[0]; + +} + +/* + * Update CR6 value + * Firstly stop DM910X , then written value and start + */ + +static void update_cr6(u32 cr6_data, unsigned long ioaddr) +{ + u32 cr6_tmp; + + cr6_tmp = cr6_data & ~0x2002; /* stop Tx/Rx */ + outl(cr6_tmp, ioaddr + DCR6); + udelay(5); + outl(cr6_data, ioaddr + DCR6); + udelay(5); +} + + +/* + * Send a setup frame for DM9132 + * This setup frame initilize DM910X addres filter mode +*/ + +static void dm9132_id_table(struct nic *nic __unused) +{ +#ifdef LINUX + u16 *addrptr; + u8 dmi_addr[8]; + unsigned long ioaddr = BASE + 0xc0; /* ID Table */ + u32 hash_val; + u16 i, hash_table[4]; +#endif + dprintf(("dm9132_id_table\n")); + + printf("FIXME: This function is broken. If you have this card contact " + "Timothy Legge at the etherboot-user list\n"); + +#ifdef LINUX + //DMFE_DBUG(0, "dm9132_id_table()", 0); + + /* Node address */ + addrptr = (u16 *) nic->node_addr; + outw(addrptr[0], ioaddr); + ioaddr += 4; + outw(addrptr[1], ioaddr); + ioaddr += 4; + outw(addrptr[2], ioaddr); + ioaddr += 4; + + /* Clear Hash Table */ + for (i = 0; i < 4; i++) + hash_table[i] = 0x0; + + /* broadcast address */ + hash_table[3] = 0x8000; + + /* the multicast address in Hash Table : 64 bits */ + for (mcptr = mc_list, i = 0; i < mc_cnt; i++, mcptr = mcptr->next) { + hash_val = cal_CRC((char *) mcptr->dmi_addr, 6, 0) & 0x3f; + hash_table[hash_val / 16] |= (u16) 1 << (hash_val % 16); + } + + /* Write the hash table to MAC MD table */ + for (i = 0; i < 4; i++, ioaddr += 4) + outw(hash_table[i], ioaddr); +#endif +} + + +/* + * Send a setup frame for DM9102/DM9102A + * This setup frame initilize DM910X addres filter mode + */ + +static void send_filter_frame(struct nic *nic) +{ + + u8 *ptxb; + int i; + + dprintf(("send_filter_frame\n")); + /* point to the current txb incase multiple tx_rings are used */ + ptxb = &txb[db->cur_tx]; + + /* construct perfect filter frame with mac address as first match + and broadcast address for all others */ + for (i = 0; i < 192; i++) + ptxb[i] = 0xFF; + ptxb[0] = nic->node_addr[0]; + ptxb[1] = nic->node_addr[1]; + ptxb[4] = nic->node_addr[2]; + ptxb[5] = nic->node_addr[3]; + ptxb[8] = nic->node_addr[4]; + ptxb[9] = nic->node_addr[5]; + + /* prepare the setup frame */ + txd[db->cur_tx].tdes1 = cpu_to_le32(0x890000c0); + txd[db->cur_tx].tdes0 = cpu_to_le32(0x80000000); + update_cr6(db->cr6_data | 0x2000, BASE); + outl(0x1, BASE + DCR1); /* Issue Tx polling */ + update_cr6(db->cr6_data, BASE); + db->cur_tx++; +} + +/* + * Read one word data from the serial ROM + */ + +static u16 read_srom_word(long ioaddr, int offset) +{ + int i; + u16 srom_data = 0; + long cr9_ioaddr = ioaddr + DCR9; + + outl(CR9_SROM_READ, cr9_ioaddr); + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + + /* Send the Read Command 110b */ + SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr); + SROM_CLK_WRITE(SROM_DATA_1, cr9_ioaddr); + SROM_CLK_WRITE(SROM_DATA_0, cr9_ioaddr); + + /* Send the offset */ + for (i = 5; i >= 0; i--) { + srom_data = + (offset & (1 << i)) ? SROM_DATA_1 : SROM_DATA_0; + SROM_CLK_WRITE(srom_data, cr9_ioaddr); + } + + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + + for (i = 16; i > 0; i--) { + outl(CR9_SROM_READ | CR9_SRCS | CR9_SRCLK, cr9_ioaddr); + udelay(5); + srom_data = + (srom_data << 1) | ((inl(cr9_ioaddr) & CR9_CRDOUT) ? 1 + : 0); + outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); + udelay(5); + } + + outl(CR9_SROM_READ, cr9_ioaddr); + return srom_data; +} + + +/* + * Auto sense the media mode + */ + +#if 0 /* not used */ +static u8 dmfe_sense_speed(struct nic *nic __unused) +{ + u8 ErrFlag = 0; + u16 phy_mode; + + /* CR6 bit18=0, select 10/100M */ + update_cr6((db->cr6_data & ~0x40000), BASE); + + phy_mode = phy_read(BASE, db->phy_addr, 1, db->chip_id); + phy_mode = phy_read(BASE, db->phy_addr, 1, db->chip_id); + + if ((phy_mode & 0x24) == 0x24) { + if (db->chip_id == PCI_DM9132_ID) /* DM9132 */ + phy_mode = + phy_read(BASE, db->phy_addr, 7, + db->chip_id) & 0xf000; + else /* DM9102/DM9102A */ + phy_mode = + phy_read(BASE, db->phy_addr, 17, + db->chip_id) & 0xf000; + /* printk(DRV_NAME ": Phy_mode %x ",phy_mode); */ + switch (phy_mode) { + case 0x1000: + db->op_mode = DMFE_10MHF; + break; + case 0x2000: + db->op_mode = DMFE_10MFD; + break; + case 0x4000: + db->op_mode = DMFE_100MHF; + break; + case 0x8000: + db->op_mode = DMFE_100MFD; + break; + default: + db->op_mode = DMFE_10MHF; + ErrFlag = 1; + break; + } + } else { + db->op_mode = DMFE_10MHF; + //DMFE_DBUG(0, "Link Failed :", phy_mode); + ErrFlag = 1; + } + + return ErrFlag; +} +#endif + +/* + * Set 10/100 phyxcer capability + * AUTO mode : phyxcer register4 is NIC capability + * Force mode: phyxcer register4 is the force media + */ + +static void dmfe_set_phyxcer(struct nic *nic __unused) +{ + u16 phy_reg; + + /* Select 10/100M phyxcer */ + db->cr6_data &= ~0x40000; + update_cr6(db->cr6_data, BASE); + + /* DM9009 Chip: Phyxcer reg18 bit12=0 */ + if (db->chip_id == PCI_DM9009_ID) { + phy_reg = + phy_read(BASE, db->phy_addr, 18, + db->chip_id) & ~0x1000; + phy_write(BASE, db->phy_addr, 18, phy_reg, db->chip_id); + } + + /* Phyxcer capability setting */ + phy_reg = phy_read(BASE, db->phy_addr, 4, db->chip_id) & ~0x01e0; + + if (db->media_mode & DMFE_AUTO) { + /* AUTO Mode */ + phy_reg |= db->PHY_reg4; + } else { + /* Force Mode */ + switch (db->media_mode) { + case DMFE_10MHF: + phy_reg |= 0x20; + break; + case DMFE_10MFD: + phy_reg |= 0x40; + break; + case DMFE_100MHF: + phy_reg |= 0x80; + break; + case DMFE_100MFD: + phy_reg |= 0x100; + break; + } + if (db->chip_id == PCI_DM9009_ID) + phy_reg &= 0x61; + } + + /* Write new capability to Phyxcer Reg4 */ + if (!(phy_reg & 0x01e0)) { + phy_reg |= db->PHY_reg4; + db->media_mode |= DMFE_AUTO; + } + phy_write(BASE, db->phy_addr, 4, phy_reg, db->chip_id); + + /* Restart Auto-Negotiation */ + if (db->chip_type && (db->chip_id == PCI_DM9102_ID)) + phy_write(BASE, db->phy_addr, 0, 0x1800, db->chip_id); + if (!db->chip_type) + phy_write(BASE, db->phy_addr, 0, 0x1200, db->chip_id); +} + + +/* + * Process op-mode + * AUTO mode : PHY controller in Auto-negotiation Mode + * Force mode: PHY controller in force mode with HUB + * N-way force capability with SWITCH + */ + +#if 0 /* not used */ +static void dmfe_process_mode(struct nic *nic __unused) +{ + u16 phy_reg; + + /* Full Duplex Mode Check */ + if (db->op_mode & 0x4) + db->cr6_data |= CR6_FDM; /* Set Full Duplex Bit */ + else + db->cr6_data &= ~CR6_FDM; /* Clear Full Duplex Bit */ + + /* Transciver Selection */ + if (db->op_mode & 0x10) /* 1M HomePNA */ + db->cr6_data |= 0x40000; /* External MII select */ + else + db->cr6_data &= ~0x40000; /* Internal 10/100 transciver */ + + update_cr6(db->cr6_data, BASE); + + /* 10/100M phyxcer force mode need */ + if (!(db->media_mode & 0x18)) { + /* Forece Mode */ + phy_reg = phy_read(BASE, db->phy_addr, 6, db->chip_id); + if (!(phy_reg & 0x1)) { + /* parter without N-Way capability */ + phy_reg = 0x0; + switch (db->op_mode) { + case DMFE_10MHF: + phy_reg = 0x0; + break; + case DMFE_10MFD: + phy_reg = 0x100; + break; + case DMFE_100MHF: + phy_reg = 0x2000; + break; + case DMFE_100MFD: + phy_reg = 0x2100; + break; + } + phy_write(BASE, db->phy_addr, 0, phy_reg, + db->chip_id); + if (db->chip_type + && (db->chip_id == PCI_DM9102_ID)) + mdelay(20); + phy_write(BASE, db->phy_addr, 0, phy_reg, + db->chip_id); + } + } +} +#endif + +/* + * Write a word to Phy register + */ + +static void phy_write(unsigned long iobase, u8 phy_addr, u8 offset, + u16 phy_data, u32 chip_id) +{ + u16 i; + unsigned long ioaddr; + + if (chip_id == PCI_DM9132_ID) { + ioaddr = iobase + 0x80 + offset * 4; + outw(phy_data, ioaddr); + } else { + /* DM9102/DM9102A Chip */ + ioaddr = iobase + DCR9; + + /* Send 33 synchronization clock to Phy controller */ + for (i = 0; i < 35; i++) + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send start command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0); + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send write command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0); + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send Phy addres */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, + phy_addr & i ? PHY_DATA_1 : + PHY_DATA_0); + + /* Send register addres */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, + offset & i ? PHY_DATA_1 : + PHY_DATA_0); + + /* written trasnition */ + phy_write_1bit(ioaddr, PHY_DATA_1); + phy_write_1bit(ioaddr, PHY_DATA_0); + + /* Write a word data to PHY controller */ + for (i = 0x8000; i > 0; i >>= 1) + phy_write_1bit(ioaddr, + phy_data & i ? PHY_DATA_1 : + PHY_DATA_0); + } +} + + +/* + * Read a word data from phy register + */ + +static u16 phy_read(unsigned long iobase, u8 phy_addr, u8 offset, + u32 chip_id) +{ + int i; + u16 phy_data; + unsigned long ioaddr; + + if (chip_id == PCI_DM9132_ID) { + /* DM9132 Chip */ + ioaddr = iobase + 0x80 + offset * 4; + phy_data = inw(ioaddr); + } else { + /* DM9102/DM9102A Chip */ + ioaddr = iobase + DCR9; + + /* Send 33 synchronization clock to Phy controller */ + for (i = 0; i < 35; i++) + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send start command(01) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_0); + phy_write_1bit(ioaddr, PHY_DATA_1); + + /* Send read command(10) to Phy */ + phy_write_1bit(ioaddr, PHY_DATA_1); + phy_write_1bit(ioaddr, PHY_DATA_0); + + /* Send Phy addres */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, + phy_addr & i ? PHY_DATA_1 : + PHY_DATA_0); + + /* Send register addres */ + for (i = 0x10; i > 0; i = i >> 1) + phy_write_1bit(ioaddr, + offset & i ? PHY_DATA_1 : + PHY_DATA_0); + + /* Skip transition state */ + phy_read_1bit(ioaddr); + + /* read 16bit data */ + for (phy_data = 0, i = 0; i < 16; i++) { + phy_data <<= 1; + phy_data |= phy_read_1bit(ioaddr); + } + } + + return phy_data; +} + + +/* + * Write one bit data to Phy Controller + */ + +static void phy_write_1bit(unsigned long ioaddr, u32 phy_data) +{ + outl(phy_data, ioaddr); /* MII Clock Low */ + udelay(1); + outl(phy_data | MDCLKH, ioaddr); /* MII Clock High */ + udelay(1); + outl(phy_data, ioaddr); /* MII Clock Low */ + udelay(1); +} + + +/* + * Read one bit phy data from PHY controller + */ + +static u16 phy_read_1bit(unsigned long ioaddr) +{ + u16 phy_data; + + outl(0x50000, ioaddr); + udelay(1); + phy_data = (inl(ioaddr) >> 19) & 0x1; + outl(0x40000, ioaddr); + udelay(1); + + return phy_data; +} + + +/* + * Parser SROM and media mode + */ + +static void dmfe_parse_srom(struct nic *nic) +{ + unsigned char *srom = db->srom; + int dmfe_mode, tmp_reg; + + /* Init CR15 */ + db->cr15_data = CR15_DEFAULT; + + /* Check SROM Version */ + if (((int) srom[18] & 0xff) == SROM_V41_CODE) { + /* SROM V4.01 */ + /* Get NIC support media mode */ + db->NIC_capability = *(u16 *) (srom + 34); + db->PHY_reg4 = 0; + for (tmp_reg = 1; tmp_reg < 0x10; tmp_reg <<= 1) { + switch (db->NIC_capability & tmp_reg) { + case 0x1: + db->PHY_reg4 |= 0x0020; + break; + case 0x2: + db->PHY_reg4 |= 0x0040; + break; + case 0x4: + db->PHY_reg4 |= 0x0080; + break; + case 0x8: + db->PHY_reg4 |= 0x0100; + break; + } + } + + /* Media Mode Force or not check */ + dmfe_mode = *((int *) srom + 34) & *((int *) srom + 36); + switch (dmfe_mode) { + case 0x4: + dmfe_media_mode = DMFE_100MHF; + break; /* 100MHF */ + case 0x2: + dmfe_media_mode = DMFE_10MFD; + break; /* 10MFD */ + case 0x8: + dmfe_media_mode = DMFE_100MFD; + break; /* 100MFD */ + case 0x100: + case 0x200: + dmfe_media_mode = DMFE_1M_HPNA; + break; /* HomePNA */ + } + + /* Special Function setting */ + /* VLAN function */ + if ((SF_mode & 0x1) || (srom[43] & 0x80)) + db->cr15_data |= 0x40; + + /* Flow Control */ + if ((SF_mode & 0x2) || (srom[40] & 0x1)) + db->cr15_data |= 0x400; + + /* TX pause packet */ + if ((SF_mode & 0x4) || (srom[40] & 0xe)) + db->cr15_data |= 0x9800; + } + + /* Parse HPNA parameter */ + db->HPNA_command = 1; + + /* Accept remote command or not */ + if (HPNA_rx_cmd == 0) + db->HPNA_command |= 0x8000; + + /* Issue remote command & operation mode */ + if (HPNA_tx_cmd == 1) + switch (HPNA_mode) { /* Issue Remote Command */ + case 0: + db->HPNA_command |= 0x0904; + break; + case 1: + db->HPNA_command |= 0x0a00; + break; + case 2: + db->HPNA_command |= 0x0506; + break; + case 3: + db->HPNA_command |= 0x0602; + break; + } else + switch (HPNA_mode) { /* Don't Issue */ + case 0: + db->HPNA_command |= 0x0004; + break; + case 1: + db->HPNA_command |= 0x0000; + break; + case 2: + db->HPNA_command |= 0x0006; + break; + case 3: + db->HPNA_command |= 0x0002; + break; + } + + /* Check DM9801 or DM9802 present or not */ + db->HPNA_present = 0; + update_cr6(db->cr6_data | 0x40000, BASE); + tmp_reg = phy_read(BASE, db->phy_addr, 3, db->chip_id); + if ((tmp_reg & 0xfff0) == 0xb900) { + /* DM9801 or DM9802 present */ + db->HPNA_timer = 8; + if (phy_read(BASE, db->phy_addr, 31, db->chip_id) == + 0x4404) { + /* DM9801 HomeRun */ + db->HPNA_present = 1; + dmfe_program_DM9801(nic, tmp_reg); + } else { + /* DM9802 LongRun */ + db->HPNA_present = 2; + dmfe_program_DM9802(nic); + } + } + +} + +/* + * Init HomeRun DM9801 + */ + +static void dmfe_program_DM9801(struct nic *nic __unused, int HPNA_rev) +{ + u32 reg17, reg25; + + if (!HPNA_NoiseFloor) + HPNA_NoiseFloor = DM9801_NOISE_FLOOR; + switch (HPNA_rev) { + case 0xb900: /* DM9801 E3 */ + db->HPNA_command |= 0x1000; + reg25 = phy_read(BASE, db->phy_addr, 24, db->chip_id); + reg25 = ((reg25 + HPNA_NoiseFloor) & 0xff) | 0xf000; + reg17 = phy_read(BASE, db->phy_addr, 17, db->chip_id); + break; + case 0xb901: /* DM9801 E4 */ + reg25 = phy_read(BASE, db->phy_addr, 25, db->chip_id); + reg25 = (reg25 & 0xff00) + HPNA_NoiseFloor; + reg17 = phy_read(BASE, db->phy_addr, 17, db->chip_id); + reg17 = (reg17 & 0xfff0) + HPNA_NoiseFloor + 3; + break; + case 0xb902: /* DM9801 E5 */ + case 0xb903: /* DM9801 E6 */ + default: + db->HPNA_command |= 0x1000; + reg25 = phy_read(BASE, db->phy_addr, 25, db->chip_id); + reg25 = (reg25 & 0xff00) + HPNA_NoiseFloor - 5; + reg17 = phy_read(BASE, db->phy_addr, 17, db->chip_id); + reg17 = (reg17 & 0xfff0) + HPNA_NoiseFloor; + break; + } + phy_write(BASE, db->phy_addr, 16, db->HPNA_command, db->chip_id); + phy_write(BASE, db->phy_addr, 17, reg17, db->chip_id); + phy_write(BASE, db->phy_addr, 25, reg25, db->chip_id); +} + + +/* + * Init HomeRun DM9802 + */ + +static void dmfe_program_DM9802(struct nic *nic __unused) +{ + u32 phy_reg; + + if (!HPNA_NoiseFloor) + HPNA_NoiseFloor = DM9802_NOISE_FLOOR; + phy_write(BASE, db->phy_addr, 16, db->HPNA_command, db->chip_id); + phy_reg = phy_read(BASE, db->phy_addr, 25, db->chip_id); + phy_reg = (phy_reg & 0xff00) + HPNA_NoiseFloor; + phy_write(BASE, db->phy_addr, 25, phy_reg, db->chip_id); +} + +static struct nic_operations dmfe_operations = { + .connect = dummy_connect, + .poll = dmfe_poll, + .transmit = dmfe_transmit, + .irq = dmfe_irq, + +}; + +static struct pci_device_id dmfe_nics[] = { + PCI_ROM(0x1282, 0x9100, "dmfe9100", "Davicom 9100"), + PCI_ROM(0x1282, 0x9102, "dmfe9102", "Davicom 9102"), + PCI_ROM(0x1282, 0x9009, "dmfe9009", "Davicom 9009"), + PCI_ROM(0x1282, 0x9132, "dmfe9132", "Davicom 9132"), /* Needs probably some fixing */ +}; + +PCI_DRIVER ( dmfe_driver, dmfe_nics, PCI_NO_CLASS ); + +DRIVER ( "DMFE/PCI", nic_driver, pci_driver, dmfe_driver, + dmfe_probe, dmfe_disable ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/e1000/e1000.c b/gpxe/src/drivers/net/e1000/e1000.c new file mode 100644 index 0000000..c1a4a52 --- /dev/null +++ b/gpxe/src/drivers/net/e1000/e1000.c @@ -0,0 +1,1119 @@ +/* + * gPXE driver for Intel eepro1000 ethernet cards + * + * Written by Marty Connor + * + * Copyright Entity Cyber, Inc. 2007 + * + * This software may be used and distributed according to the terms of + * the GNU General Public License (GPL), incorporated herein by + * reference. Drivers based on or derived from this code fall under + * the GPL and must retain the authorship, copyright and license + * notice. + * + */ + +/******************************************************************************* + + Intel PRO/1000 Linux driver + Copyright(c) 1999 - 2006 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#include "e1000.h" + +/** + * e1000_get_hw_control - get control of the h/w from f/w + * + * @v adapter e1000 private structure + * + * e1000_get_hw_control sets {CTRL_EXT|FWSM}:DRV_LOAD bit. + * For ASF and Pass Through versions of f/w this means that + * the driver is loaded. For AMT version (only with 82573) + * of the f/w this means that the network i/f is open. + * + **/ +static void +e1000_get_hw_control ( struct e1000_adapter *adapter ) +{ + uint32_t ctrl_ext; + uint32_t swsm; + + DBG ( "e1000_get_hw_control\n" ); + + /* Let firmware know the driver has taken over */ + switch (adapter->hw.mac_type) { + case e1000_82573: + swsm = E1000_READ_REG(&adapter->hw, SWSM); + E1000_WRITE_REG(&adapter->hw, SWSM, + swsm | E1000_SWSM_DRV_LOAD); + break; + case e1000_82571: + case e1000_82572: + case e1000_80003es2lan: + case e1000_ich8lan: + ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT); + E1000_WRITE_REG(&adapter->hw, CTRL_EXT, + ctrl_ext | E1000_CTRL_EXT_DRV_LOAD); + break; + default: + break; + } +} + +/** + * e1000_irq_enable - Enable default interrupt generation settings + * + * @v adapter e1000 private structure + **/ +static void +e1000_irq_enable ( struct e1000_adapter *adapter ) +{ + E1000_WRITE_REG ( &adapter->hw, IMS, E1000_IMS_RXDMT0 | + E1000_IMS_RXSEQ ); + E1000_WRITE_FLUSH ( &adapter->hw ); +} + +/** + * e1000_irq_disable - Mask off interrupt generation on the NIC + * + * @v adapter e1000 private structure + **/ +static void +e1000_irq_disable ( struct e1000_adapter *adapter ) +{ + E1000_WRITE_REG ( &adapter->hw, IMC, ~0 ); + E1000_WRITE_FLUSH ( &adapter->hw ); +} + +/** + * e1000_irq_force - trigger interrupt + * + * @v adapter e1000 private structure + **/ +static void +e1000_irq_force ( struct e1000_adapter *adapter ) +{ + E1000_WRITE_REG ( &adapter->hw, ICS, E1000_ICS_RXDMT0 ); + E1000_WRITE_FLUSH ( &adapter->hw ); +} + +/** + * e1000_sw_init - Initialize general software structures (struct e1000_adapter) + * + * @v adapter e1000 private structure + * + * e1000_sw_init initializes the Adapter private data structure. + * Fields are initialized based on PCI device information and + * OS network device settings (MTU size). + **/ +static int +e1000_sw_init ( struct e1000_adapter *adapter ) +{ + struct e1000_hw *hw = &adapter->hw; + struct pci_device *pdev = adapter->pdev; + + /* PCI config space info */ + + hw->vendor_id = pdev->vendor; + hw->device_id = pdev->device; + + pci_read_config_word ( pdev, PCI_COMMAND, &hw->pci_cmd_word ); + + /* Disable Flow Control */ + hw->fc = E1000_FC_NONE; + + adapter->eeprom_wol = 0; + adapter->wol = adapter->eeprom_wol; + adapter->en_mng_pt = 0; + adapter->rx_int_delay = 0; + adapter->rx_abs_int_delay = 0; + + adapter->rx_buffer_len = MAXIMUM_ETHERNET_VLAN_SIZE; + adapter->rx_ps_bsize0 = E1000_RXBUFFER_128; + hw->max_frame_size = MAXIMUM_ETHERNET_VLAN_SIZE + + ENET_HEADER_SIZE + ETHERNET_FCS_SIZE; + hw->min_frame_size = MINIMUM_ETHERNET_FRAME_SIZE; + + /* identify the MAC */ + + if ( e1000_set_mac_type ( hw ) ) { + DBG ( "Unknown MAC Type\n" ); + return -EIO; + } + + switch ( hw->mac_type ) { + default: + break; + case e1000_82541: + case e1000_82547: + case e1000_82541_rev_2: + case e1000_82547_rev_2: + hw->phy_init_script = 1; + break; + } + + e1000_set_media_type ( hw ); + + hw->autoneg = TRUE; + hw->autoneg_advertised = AUTONEG_ADVERTISE_SPEED_DEFAULT; + hw->wait_autoneg_complete = TRUE; + + hw->tbi_compatibility_en = TRUE; + hw->adaptive_ifs = TRUE; + + /* Copper options */ + + if ( hw->media_type == e1000_media_type_copper ) { + hw->mdix = AUTO_ALL_MODES; + hw->disable_polarity_correction = FALSE; + hw->master_slave = E1000_MASTER_SLAVE; + } + + e1000_irq_disable ( adapter ); + + return 0; +} + +/** + * e1000_setup_tx_resources - allocate Tx resources (Descriptors) + * + * @v adapter e1000 private structure + * + * @ret rc Returns 0 on success, negative on failure + **/ +static int +e1000_setup_tx_resources ( struct e1000_adapter *adapter ) +{ + DBG ( "e1000_setup_tx_resources\n" ); + + /* Allocate transmit descriptor ring memory. + It must not cross a 64K boundary because of hardware errata #23 + so we use malloc_dma() requesting a 128 byte block that is + 128 byte aligned. This should guarantee that the memory + allocated will not cross a 64K boundary, because 128 is an + even multiple of 65536 ( 65536 / 128 == 512 ), so all possible + allocations of 128 bytes on a 128 byte boundary will not + cross 64K bytes. + */ + + adapter->tx_base = + malloc_dma ( adapter->tx_ring_size, adapter->tx_ring_size ); + + if ( ! adapter->tx_base ) { + return -ENOMEM; + } + + memset ( adapter->tx_base, 0, adapter->tx_ring_size ); + + DBG ( "adapter->tx_base = %#08lx\n", virt_to_bus ( adapter->tx_base ) ); + + return 0; +} + +static void +e1000_free_tx_resources ( struct e1000_adapter *adapter ) +{ + DBG ( "e1000_free_tx_resources\n" ); + + free_dma ( adapter->tx_base, adapter->tx_ring_size ); +} + +/** + * e1000_configure_tx - Configure 8254x Transmit Unit after Reset + * @adapter: board private structure + * + * Configure the Tx unit of the MAC after a reset. + **/ +static void +e1000_configure_tx ( struct e1000_adapter *adapter ) +{ + struct e1000_hw *hw = &adapter->hw; + uint32_t tctl; + + DBG ( "e1000_configure_tx\n" ); + + E1000_WRITE_REG ( hw, TDBAH, 0 ); + E1000_WRITE_REG ( hw, TDBAL, virt_to_bus ( adapter->tx_base ) ); + E1000_WRITE_REG ( hw, TDLEN, adapter->tx_ring_size ); + + DBG ( "TDBAL: %#08x\n", E1000_READ_REG ( hw, TDBAL ) ); + DBG ( "TDLEN: %d\n", E1000_READ_REG ( hw, TDLEN ) ); + + /* Setup the HW Tx Head and Tail descriptor pointers */ + E1000_WRITE_REG ( hw, TDH, 0 ); + E1000_WRITE_REG ( hw, TDT, 0 ); + + adapter->tx_head = 0; + adapter->tx_tail = 0; + adapter->tx_fill_ctr = 0; + + /* Setup Transmit Descriptor Settings for eop descriptor */ + tctl = E1000_TCTL_PSP | E1000_TCTL_EN | + (E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT) | + (E1000_HDX_COLLISION_DISTANCE << E1000_COLD_SHIFT); + + e1000_config_collision_dist ( hw ); + + E1000_WRITE_REG ( hw, TCTL, tctl ); + E1000_WRITE_FLUSH ( hw ); +} + +/** + * e1000_setup_rx_resources - allocate Rx resources (Descriptors) + * + * @v adapter e1000 private structure + * + * @ret rc Returns 0 on success, negative on failure + **/ +static int +e1000_setup_rx_resources ( struct e1000_adapter *adapter ) +{ + int i, j; + struct e1000_rx_desc *rx_curr_desc; + + DBG ( "e1000_setup_rx_resources\n" ); + + /* Allocate receive descriptor ring memory. + It must not cross a 64K boundary because of hardware errata + */ + + adapter->rx_base = + malloc_dma ( adapter->rx_ring_size, adapter->rx_ring_size ); + + if ( ! adapter->rx_base ) { + return -ENOMEM; + } + memset ( adapter->rx_base, 0, adapter->rx_ring_size ); + + for ( i = 0; i < NUM_RX_DESC; i++ ) { + + adapter->rx_iobuf[i] = alloc_iob ( MAXIMUM_ETHERNET_VLAN_SIZE ); + + /* If unable to allocate all iobufs, free any that + * were successfully allocated, and return an error + */ + if ( ! adapter->rx_iobuf[i] ) { + for ( j = 0; j < i; j++ ) { + free_iob ( adapter->rx_iobuf[j] ); + } + return -ENOMEM; + } + + rx_curr_desc = ( void * ) ( adapter->rx_base ) + + ( i * sizeof ( *adapter->rx_base ) ); + + rx_curr_desc->buffer_addr = virt_to_bus ( adapter->rx_iobuf[i]->data ); + + DBG ( "i = %d rx_curr_desc->buffer_addr = %#16llx\n", + i, rx_curr_desc->buffer_addr ); + + } + return 0; +} + +static void +e1000_free_rx_resources ( struct e1000_adapter *adapter ) +{ + int i; + + DBG ( "e1000_free_rx_resources\n" ); + + free_dma ( adapter->rx_base, adapter->rx_ring_size ); + + for ( i = 0; i < NUM_RX_DESC; i++ ) { + free_iob ( adapter->rx_iobuf[i] ); + } +} + +/** + * e1000_configure_rx - Configure 8254x Receive Unit after Reset + * @adapter: board private structure + * + * Configure the Rx unit of the MAC after a reset. + **/ +static void +e1000_configure_rx ( struct e1000_adapter *adapter ) +{ + struct e1000_hw *hw = &adapter->hw; + uint32_t rctl; + + DBG ( "e1000_configure_rx\n" ); + + /* disable receives while setting up the descriptors */ + rctl = E1000_READ_REG ( hw, RCTL ); + E1000_WRITE_REG ( hw, RCTL, rctl & ~E1000_RCTL_EN ); + + adapter->rx_curr = 0; + + /* Setup the HW Rx Head and Tail Descriptor Pointers and + * the Base and Length of the Rx Descriptor Ring */ + + E1000_WRITE_REG ( hw, RDBAL, virt_to_bus ( adapter->rx_base ) ); + E1000_WRITE_REG ( hw, RDBAH, 0 ); + E1000_WRITE_REG ( hw, RDLEN, adapter->rx_ring_size ); + + E1000_WRITE_REG ( hw, RDH, 0 ); + E1000_WRITE_REG ( hw, RDT, NUM_RX_DESC - 1 ); + + /* Enable Receives */ + rctl = ( E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_SZ_2048 | + E1000_RCTL_MPE + ); + + E1000_WRITE_REG ( hw, RCTL, rctl ); + E1000_WRITE_FLUSH ( hw ); + + DBG ( "RDBAL: %#08x\n", E1000_READ_REG ( hw, RDBAL ) ); + DBG ( "RDLEN: %d\n", E1000_READ_REG ( hw, RDLEN ) ); + DBG ( "RCTL: %#08x\n", E1000_READ_REG ( hw, RCTL ) ); +} + +/** + * e1000_reset - Put e1000 NIC in known initial state + * + * @v adapter e1000 private structure + **/ +static void +e1000_reset ( struct e1000_adapter *adapter ) +{ + uint32_t pba = 0; + uint16_t fc_high_water_mark = E1000_FC_HIGH_DIFF; + + DBG ( "e1000_reset\n" ); + + switch (adapter->hw.mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + case e1000_82543: + case e1000_82544: + case e1000_82540: + case e1000_82541: + case e1000_82541_rev_2: + pba = E1000_PBA_48K; + break; + case e1000_82545: + case e1000_82545_rev_3: + case e1000_82546: + case e1000_82546_rev_3: + pba = E1000_PBA_48K; + break; + case e1000_82547: + case e1000_82547_rev_2: + pba = E1000_PBA_30K; + break; + case e1000_82571: + case e1000_82572: + case e1000_80003es2lan: + pba = E1000_PBA_38K; + break; + case e1000_82573: + pba = E1000_PBA_20K; + break; + case e1000_ich8lan: + pba = E1000_PBA_8K; + case e1000_undefined: + case e1000_num_macs: + break; + } + + E1000_WRITE_REG ( &adapter->hw, PBA, pba ); + + /* flow control settings */ + /* Set the FC high water mark to 90% of the FIFO size. + * Required to clear last 3 LSB */ + fc_high_water_mark = ((pba * 9216)/10) & 0xFFF8; + /* We can't use 90% on small FIFOs because the remainder + * would be less than 1 full frame. In this case, we size + * it to allow at least a full frame above the high water + * mark. */ + if (pba < E1000_PBA_16K) + fc_high_water_mark = (pba * 1024) - 1600; + + adapter->hw.fc_high_water = fc_high_water_mark; + adapter->hw.fc_low_water = fc_high_water_mark - 8; + if (adapter->hw.mac_type == e1000_80003es2lan) + adapter->hw.fc_pause_time = 0xFFFF; + else + adapter->hw.fc_pause_time = E1000_FC_PAUSE_TIME; + adapter->hw.fc_send_xon = 1; + adapter->hw.fc = adapter->hw.original_fc; + /* Allow time for pending master requests to run */ + + e1000_reset_hw ( &adapter->hw ); + + if ( adapter->hw.mac_type >= e1000_82544 ) + E1000_WRITE_REG ( &adapter->hw, WUC, 0 ); + + if ( e1000_init_hw ( &adapter->hw ) ) + DBG ( "Hardware Error\n" ); + + /* if (adapter->hwflags & HWFLAGS_PHY_PWR_BIT) { */ + if (adapter->hw.mac_type >= e1000_82544 && + adapter->hw.mac_type <= e1000_82547_rev_2 && + adapter->hw.autoneg == 1 && + adapter->hw.autoneg_advertised == ADVERTISE_1000_FULL) { + uint32_t ctrl = E1000_READ_REG(&adapter->hw, CTRL); + /* clear phy power management bit if we are in gig only mode, + * which if enabled will attempt negotiation to 100Mb, which + * can cause a loss of link at power off or driver unload */ + ctrl &= ~E1000_CTRL_SWDPIN3; + E1000_WRITE_REG(&adapter->hw, CTRL, ctrl); + } + + e1000_phy_get_info ( &adapter->hw, &adapter->phy_info ); + + if (!adapter->smart_power_down && + (adapter->hw.mac_type == e1000_82571 || + adapter->hw.mac_type == e1000_82572)) { + uint16_t phy_data = 0; + /* speed up time to link by disabling smart power down, ignore + * the return value of this function because there is nothing + * different we would do if it failed */ + e1000_read_phy_reg(&adapter->hw, IGP02E1000_PHY_POWER_MGMT, + &phy_data); + phy_data &= ~IGP02E1000_PM_SPD; + e1000_write_phy_reg(&adapter->hw, IGP02E1000_PHY_POWER_MGMT, + phy_data); + } +} + +/** Functions that implement the gPXE driver API **/ + +/** + * e1000_close - Disables a network interface + * + * @v netdev network interface device structure + * + **/ +static void +e1000_close ( struct net_device *netdev ) +{ + struct e1000_adapter *adapter = netdev_priv ( netdev ); + struct e1000_hw *hw = &adapter->hw; + uint32_t rctl; + uint32_t icr; + + DBG ( "e1000_close\n" ); + + /* Acknowledge interrupts */ + icr = E1000_READ_REG ( hw, ICR ); + + e1000_irq_disable ( adapter ); + + /* disable receives */ + rctl = E1000_READ_REG ( hw, RCTL ); + E1000_WRITE_REG ( hw, RCTL, rctl & ~E1000_RCTL_EN ); + E1000_WRITE_FLUSH ( hw ); + + e1000_reset_hw ( hw ); + + e1000_free_tx_resources ( adapter ); + e1000_free_rx_resources ( adapter ); +} + +/** + * e1000_transmit - Transmit a packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * + * @ret rc Returns 0 on success, negative on failure + */ +static int +e1000_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) +{ + struct e1000_adapter *adapter = netdev_priv( netdev ); + struct e1000_hw *hw = &adapter->hw; + uint32_t tx_curr = adapter->tx_tail; + struct e1000_tx_desc *tx_curr_desc; + + DBG ("e1000_transmit\n"); + + if ( adapter->tx_fill_ctr == NUM_TX_DESC ) { + DBG ("TX overflow\n"); + return -ENOBUFS; + } + + /* Save pointer to iobuf we have been given to transmit, + netdev_tx_complete() will need it later + */ + adapter->tx_iobuf[tx_curr] = iobuf; + + tx_curr_desc = ( void * ) ( adapter->tx_base ) + + ( tx_curr * sizeof ( *adapter->tx_base ) ); + + DBG ( "tx_curr_desc = %#08lx\n", virt_to_bus ( tx_curr_desc ) ); + DBG ( "tx_curr_desc + 16 = %#08lx\n", virt_to_bus ( tx_curr_desc ) + 16 ); + DBG ( "iobuf->data = %#08lx\n", virt_to_bus ( iobuf->data ) ); + + /* Add the packet to TX ring + */ + tx_curr_desc->buffer_addr = + virt_to_bus ( iobuf->data ); + tx_curr_desc->lower.data = + E1000_TXD_CMD_RPS | E1000_TXD_CMD_EOP | + E1000_TXD_CMD_IFCS | iob_len ( iobuf ); + tx_curr_desc->upper.data = 0; + + DBG ( "TX fill: %d tx_curr: %d addr: %#08lx len: %zd\n", adapter->tx_fill_ctr, + tx_curr, virt_to_bus ( iobuf->data ), iob_len ( iobuf ) ); + + /* Point to next free descriptor */ + adapter->tx_tail = ( adapter->tx_tail + 1 ) % NUM_TX_DESC; + adapter->tx_fill_ctr++; + + /* Write new tail to NIC, making packet available for transmit + */ + E1000_WRITE_REG ( hw, TDT, adapter->tx_tail ); + + return 0; +} + +/** + * e1000_poll - Poll for received packets + * + * @v netdev Network device + */ +static void +e1000_poll ( struct net_device *netdev ) +{ + struct e1000_adapter *adapter = netdev_priv( netdev ); + struct e1000_hw *hw = &adapter->hw; + + uint32_t icr; + uint32_t tx_status; + uint32_t rx_status; + uint32_t rx_len; + uint32_t rx_err; + struct io_buffer *rx_iob; + struct e1000_tx_desc *tx_curr_desc; + struct e1000_rx_desc *rx_curr_desc; + uint32_t i; + uint64_t tmp_buffer_addr; + + DBGP ( "e1000_poll\n" ); + + /* Acknowledge interrupts */ + icr = E1000_READ_REG ( hw, ICR ); + if ( ! icr ) + return; + + DBG ( "e1000_poll: intr_status = %#08x\n", icr ); + + /* Check status of transmitted packets + */ + while ( ( i = adapter->tx_head ) != adapter->tx_tail ) { + + tx_curr_desc = ( void * ) ( adapter->tx_base ) + + ( i * sizeof ( *adapter->tx_base ) ); + + tx_status = tx_curr_desc->upper.data; + + /* if the packet at tx_head is not owned by hardware it is for us */ + if ( ! ( tx_status & E1000_TXD_STAT_DD ) ) + break; + + DBG ( "Sent packet. tx_head: %d tx_tail: %d tx_status: %#08x\n", + adapter->tx_head, adapter->tx_tail, tx_status ); + + if ( tx_status & ( E1000_TXD_STAT_EC | E1000_TXD_STAT_LC | + E1000_TXD_STAT_TU ) ) { + netdev_tx_complete_err ( netdev, adapter->tx_iobuf[i], -EINVAL ); + DBG ( "Error transmitting packet, tx_status: %#08x\n", + tx_status ); + } else { + netdev_tx_complete ( netdev, adapter->tx_iobuf[i] ); + DBG ( "Success transmitting packet, tx_status: %#08x\n", + tx_status ); + } + + /* Decrement count of used descriptors, clear this descriptor + */ + adapter->tx_fill_ctr--; + memset ( tx_curr_desc, 0, sizeof ( *tx_curr_desc ) ); + + adapter->tx_head = ( adapter->tx_head + 1 ) % NUM_TX_DESC; + } + + /* Process received packets + */ + while ( 1 ) { + + i = adapter->rx_curr; + + rx_curr_desc = ( void * ) ( adapter->rx_base ) + + ( i * sizeof ( *adapter->rx_base ) ); + rx_status = rx_curr_desc->status; + + DBG2 ( "Before DD Check RX_status: %#08x\n", rx_status ); + + if ( ! ( rx_status & E1000_RXD_STAT_DD ) ) + break; + + DBG ( "RCTL = %#08x\n", E1000_READ_REG ( &adapter->hw, RCTL ) ); + + rx_len = rx_curr_desc->length; + + DBG ( "Received packet, rx_curr: %d rx_status: %#08x rx_len: %d\n", + i, rx_status, rx_len ); + + rx_err = rx_curr_desc->errors; + + if ( rx_err & E1000_RXD_ERR_FRAME_ERR_MASK ) { + + netdev_rx_err ( netdev, NULL, -EINVAL ); + DBG ( "e1000_poll: Corrupted packet received!" + " rx_err: %#08x\n", rx_err ); + } else { + + /* If unable allocate space for this packet, + * try again next poll + */ + rx_iob = alloc_iob ( rx_len ); + if ( ! rx_iob ) + break; + + memcpy ( iob_put ( rx_iob, rx_len ), + adapter->rx_iobuf[i]->data, rx_len ); + + /* Add this packet to the receive queue. + */ + netdev_rx ( netdev, rx_iob ); + } + + tmp_buffer_addr = rx_curr_desc->buffer_addr; + memset ( rx_curr_desc, 0, sizeof ( *rx_curr_desc ) ); + rx_curr_desc->buffer_addr = tmp_buffer_addr; + + E1000_WRITE_REG ( hw, RDT, adapter->rx_curr ); + + adapter->rx_curr = ( adapter->rx_curr + 1 ) % NUM_RX_DESC; + } +} + +/** + * e1000_irq - enable or Disable interrupts + * + * @v adapter e1000 adapter + * @v action requested interrupt action + **/ +static void +e1000_irq ( struct net_device *netdev, int enable ) +{ + struct e1000_adapter *adapter = netdev_priv(netdev); + + DBG ( "e1000_irq\n" ); + + switch ( enable ) { + case 0 : + e1000_irq_disable ( adapter ); + break; + case 1 : + e1000_irq_enable ( adapter ); + break; + case 2 : + e1000_irq_force ( adapter ); + break; + } +} + +static struct net_device_operations e1000_operations; + +/** + * e1000_probe - Initial configuration of e1000 NIC + * + * @v pci PCI device + * @v id PCI IDs + * + * @ret rc Return status code + **/ +static int +e1000_probe ( struct pci_device *pdev, + const struct pci_device_id *id __unused ) +{ + int i, err; + struct net_device *netdev; + struct e1000_adapter *adapter; + unsigned long mmio_start, mmio_len; + unsigned long flash_start, flash_len; + + DBG ( "e1000_probe\n" ); + + err = -ENOMEM; + + /* Allocate net device ( also allocates memory for netdev->priv + and makes netdev-priv point to it ) */ + netdev = alloc_etherdev ( sizeof ( struct e1000_adapter ) ); + if ( ! netdev ) + goto err_alloc_etherdev; + + /* Associate e1000-specific network operations operations with + * generic network device layer */ + netdev_init ( netdev, &e1000_operations ); + + /* Associate this network device with given PCI device */ + pci_set_drvdata ( pdev, netdev ); + netdev->dev = &pdev->dev; + + /* Initialize driver private storage */ + adapter = netdev_priv ( netdev ); + memset ( adapter, 0, ( sizeof ( *adapter ) ) ); + + adapter->hw.io_base = pdev->ioaddr; + adapter->ioaddr = pdev->ioaddr; + adapter->irqno = pdev->irq; + adapter->netdev = netdev; + adapter->pdev = pdev; + adapter->hw.back = adapter; + + adapter->tx_ring_size = sizeof ( *adapter->tx_base ) * NUM_TX_DESC; + adapter->rx_ring_size = sizeof ( *adapter->rx_base ) * NUM_RX_DESC; + + mmio_start = pci_bar_start ( pdev, PCI_BASE_ADDRESS_0 ); + mmio_len = pci_bar_size ( pdev, PCI_BASE_ADDRESS_0 ); + + DBG ( "mmio_start: %#08lx\n", mmio_start ); + DBG ( "mmio_len: %#08lx\n", mmio_len ); + + /* Fix up PCI device */ + adjust_pci_device ( pdev ); + + err = -EIO; + + adapter->hw.hw_addr = ioremap ( mmio_start, mmio_len ); + DBG ( "adapter->hw.hw_addr: %p\n", adapter->hw.hw_addr ); + + if ( ! adapter->hw.hw_addr ) + goto err_ioremap; + + /* setup the private structure */ + if ( ( err = e1000_sw_init ( adapter ) ) ) + goto err_sw_init; + + DBG ( "adapter->hw.mac_type: %#08x\n", adapter->hw.mac_type ); + + /* Flash BAR mapping must happen after e1000_sw_init + * because it depends on mac_type + */ + if ( ( adapter->hw.mac_type == e1000_ich8lan ) && ( pdev->ioaddr ) ) { + flash_start = pci_bar_start ( pdev, PCI_BASE_ADDRESS_1 ); + flash_len = pci_bar_size ( pdev, PCI_BASE_ADDRESS_1 ); + adapter->hw.flash_address = ioremap ( flash_start, flash_len ); + if ( ! adapter->hw.flash_address ) + goto err_flashmap; + } + + /* initialize eeprom parameters */ + if ( e1000_init_eeprom_params ( &adapter->hw ) ) { + DBG ( "EEPROM initialization failed\n" ); + goto err_eeprom; + } + + /* before reading the EEPROM, reset the controller to + * put the device in a known good starting state + */ + err = e1000_reset_hw ( &adapter->hw ); + if ( err < 0 ) { + DBG ( "Hardware Initialization Failed\n" ); + goto err_reset; + } + + /* make sure the EEPROM is good */ + if ( e1000_validate_eeprom_checksum( &adapter->hw ) < 0 ) { + DBG ( "The EEPROM Checksum Is Not Valid\n" ); + goto err_eeprom; + } + + /* copy the MAC address out of the EEPROM */ + if ( e1000_read_mac_addr ( &adapter->hw ) ) + DBG ( "EEPROM Read Error\n" ); + + memcpy ( netdev->ll_addr, adapter->hw.mac_addr, ETH_ALEN ); + + /* print bus type/speed/width info */ + { + struct e1000_hw *hw = &adapter->hw; + DBG ( "(PCI%s:%s:%s) ", + ((hw->bus_type == e1000_bus_type_pcix) ? "-X" : + (hw->bus_type == e1000_bus_type_pci_express ? " Express":"")), + ((hw->bus_speed == e1000_bus_speed_2500) ? "2.5Gb/s" : + (hw->bus_speed == e1000_bus_speed_133) ? "133MHz" : + (hw->bus_speed == e1000_bus_speed_120) ? "120MHz" : + (hw->bus_speed == e1000_bus_speed_100) ? "100MHz" : + (hw->bus_speed == e1000_bus_speed_66) ? "66MHz" : "33MHz"), + ((hw->bus_width == e1000_bus_width_64) ? "64-bit" : + (hw->bus_width == e1000_bus_width_pciex_4) ? "Width x4" : + (hw->bus_width == e1000_bus_width_pciex_1) ? "Width x1" : + "32-bit")); + } + for (i = 0; i < 6; i++) + DBG ("%02x%s", netdev->ll_addr[i], i == 5 ? "\n" : ":"); + + /* reset the hardware with the new settings */ + e1000_reset ( adapter ); + + e1000_get_hw_control ( adapter ); + + /* Mark as link up; we don't yet handle link state */ + netdev_link_up ( netdev ); + + if ( ( err = register_netdev ( netdev ) ) != 0) + goto err_register; + + DBG ( "e1000_probe succeeded!\n" ); + + /* No errors, return success */ + return 0; + +/* Error return paths */ +err_reset: +err_register: +err_eeprom: + if ( ! e1000_check_phy_reset_block ( &adapter->hw ) ) + e1000_phy_hw_reset ( &adapter->hw ); + if ( adapter->hw.flash_address ) + iounmap ( adapter->hw.flash_address ); +err_flashmap: +err_sw_init: + iounmap ( adapter->hw.hw_addr ); +err_ioremap: + netdev_put ( netdev ); +err_alloc_etherdev: + return err; +} + +/** + * e1000_remove - Device Removal Routine + * + * @v pdev PCI device information struct + * + **/ +static void +e1000_remove ( struct pci_device *pdev ) +{ + struct net_device *netdev = pci_get_drvdata ( pdev ); + struct e1000_adapter *adapter = netdev_priv ( netdev ); + + DBG ( "e1000_remove\n" ); + + if ( adapter->hw.flash_address ) + iounmap ( adapter->hw.flash_address ); + if ( adapter->hw.hw_addr ) + iounmap ( adapter->hw.hw_addr ); + + unregister_netdev ( netdev ); + e1000_reset_hw ( &adapter->hw ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** + * e1000_open - Called when a network interface is made active + * + * @v netdev network interface device structure + * @ret rc Return status code, 0 on success, negative value on failure + * + **/ +static int +e1000_open ( struct net_device *netdev ) +{ + struct e1000_adapter *adapter = netdev_priv(netdev); + int err; + + DBG ( "e1000_open\n" ); + + /* allocate transmit descriptors */ + err = e1000_setup_tx_resources ( adapter ); + if ( err ) { + DBG ( "Error setting up TX resources!\n" ); + goto err_setup_tx; + } + + /* allocate receive descriptors */ + err = e1000_setup_rx_resources ( adapter ); + if ( err ) { + DBG ( "Error setting up RX resources!\n" ); + goto err_setup_rx; + } + + e1000_configure_tx ( adapter ); + + e1000_configure_rx ( adapter ); + + DBG ( "RXDCTL: %#08x\n", E1000_READ_REG ( &adapter->hw, RXDCTL ) ); + + return 0; + +err_setup_rx: + e1000_free_tx_resources ( adapter ); +err_setup_tx: + e1000_reset ( adapter ); + + return err; +} + +/** e1000 net device operations */ +static struct net_device_operations e1000_operations = { + .open = e1000_open, + .close = e1000_close, + .transmit = e1000_transmit, + .poll = e1000_poll, + .irq = e1000_irq, +}; + +int32_t +e1000_read_pcie_cap_reg(struct e1000_hw *hw, uint32_t reg, uint16_t *value) +{ + struct e1000_adapter *adapter = hw->back; + uint16_t cap_offset; + +#define PCI_CAP_ID_EXP 0x10 /* PCI Express */ + cap_offset = pci_find_capability(adapter->pdev, PCI_CAP_ID_EXP); + if (!cap_offset) + return -E1000_ERR_CONFIG; + + pci_read_config_word(adapter->pdev, cap_offset + reg, value); + + return 0; +} + +void +e1000_pci_clear_mwi ( struct e1000_hw *hw ) +{ + struct e1000_adapter *adapter = hw->back; + + pci_write_config_word ( adapter->pdev, PCI_COMMAND, + hw->pci_cmd_word & ~PCI_COMMAND_INVALIDATE ); +} + +void +e1000_pci_set_mwi ( struct e1000_hw *hw ) +{ + struct e1000_adapter *adapter = hw->back; + + pci_write_config_word ( adapter->pdev, PCI_COMMAND, hw->pci_cmd_word ); +} + +void +e1000_read_pci_cfg ( struct e1000_hw *hw, uint32_t reg, uint16_t *value ) +{ + struct e1000_adapter *adapter = hw->back; + + pci_read_config_word ( adapter->pdev, reg, value ); +} + +void +e1000_write_pci_cfg ( struct e1000_hw *hw, uint32_t reg, uint16_t *value ) +{ + struct e1000_adapter *adapter = hw->back; + + pci_write_config_word ( adapter->pdev, reg, *value ); +} + +void +e1000_io_write ( struct e1000_hw *hw __unused, unsigned long port, uint32_t value ) +{ + outl ( value, port ); +} + +static struct pci_device_id e1000_nics[] = { + PCI_ROM(0x8086, 0x1000, "e1000-0x1000", "e1000-0x1000"), + PCI_ROM(0x8086, 0x1001, "e1000-0x1001", "e1000-0x1001"), + PCI_ROM(0x8086, 0x1004, "e1000-0x1004", "e1000-0x1004"), + PCI_ROM(0x8086, 0x1008, "e1000-0x1008", "e1000-0x1008"), + PCI_ROM(0x8086, 0x1009, "e1000-0x1009", "e1000-0x1009"), + PCI_ROM(0x8086, 0x100c, "e1000-0x100c", "e1000-0x100c"), + PCI_ROM(0x8086, 0x100d, "e1000-0x100d", "e1000-0x100d"), + PCI_ROM(0x8086, 0x100e, "e1000-0x100e", "e1000-0x100e"), + PCI_ROM(0x8086, 0x100f, "e1000-0x100f", "e1000-0x100f"), + PCI_ROM(0x8086, 0x1010, "e1000-0x1010", "e1000-0x1010"), + PCI_ROM(0x8086, 0x1011, "e1000-0x1011", "e1000-0x1011"), + PCI_ROM(0x8086, 0x1012, "e1000-0x1012", "e1000-0x1012"), + PCI_ROM(0x8086, 0x1013, "e1000-0x1013", "e1000-0x1013"), + PCI_ROM(0x8086, 0x1014, "e1000-0x1014", "e1000-0x1014"), + PCI_ROM(0x8086, 0x1015, "e1000-0x1015", "e1000-0x1015"), + PCI_ROM(0x8086, 0x1016, "e1000-0x1016", "e1000-0x1016"), + PCI_ROM(0x8086, 0x1017, "e1000-0x1017", "e1000-0x1017"), + PCI_ROM(0x8086, 0x1018, "e1000-0x1018", "e1000-0x1018"), + PCI_ROM(0x8086, 0x1019, "e1000-0x1019", "e1000-0x1019"), + PCI_ROM(0x8086, 0x101a, "e1000-0x101a", "e1000-0x101a"), + PCI_ROM(0x8086, 0x101d, "e1000-0x101d", "e1000-0x101d"), + PCI_ROM(0x8086, 0x101e, "e1000-0x101e", "e1000-0x101e"), + PCI_ROM(0x8086, 0x1026, "e1000-0x1026", "e1000-0x1026"), + PCI_ROM(0x8086, 0x1027, "e1000-0x1027", "e1000-0x1027"), + PCI_ROM(0x8086, 0x1028, "e1000-0x1028", "e1000-0x1028"), + PCI_ROM(0x8086, 0x1049, "e1000-0x1049", "e1000-0x1049"), + PCI_ROM(0x8086, 0x104a, "e1000-0x104a", "e1000-0x104a"), + PCI_ROM(0x8086, 0x104b, "e1000-0x104b", "e1000-0x104b"), + PCI_ROM(0x8086, 0x104c, "e1000-0x104c", "e1000-0x104c"), + PCI_ROM(0x8086, 0x104d, "e1000-0x104d", "e1000-0x104d"), + PCI_ROM(0x8086, 0x105e, "e1000-0x105e", "e1000-0x105e"), + PCI_ROM(0x8086, 0x105f, "e1000-0x105f", "e1000-0x105f"), + PCI_ROM(0x8086, 0x1060, "e1000-0x1060", "e1000-0x1060"), + PCI_ROM(0x8086, 0x1075, "e1000-0x1075", "e1000-0x1075"), + PCI_ROM(0x8086, 0x1076, "e1000-0x1076", "e1000-0x1076"), + PCI_ROM(0x8086, 0x1077, "e1000-0x1077", "e1000-0x1077"), + PCI_ROM(0x8086, 0x1078, "e1000-0x1078", "e1000-0x1078"), + PCI_ROM(0x8086, 0x1079, "e1000-0x1079", "e1000-0x1079"), + PCI_ROM(0x8086, 0x107a, "e1000-0x107a", "e1000-0x107a"), + PCI_ROM(0x8086, 0x107b, "e1000-0x107b", "e1000-0x107b"), + PCI_ROM(0x8086, 0x107c, "e1000-0x107c", "e1000-0x107c"), + PCI_ROM(0x8086, 0x107d, "e1000-0x107d", "e1000-0x107d"), + PCI_ROM(0x8086, 0x107e, "e1000-0x107e", "e1000-0x107e"), + PCI_ROM(0x8086, 0x107f, "e1000-0x107f", "e1000-0x107f"), + PCI_ROM(0x8086, 0x108a, "e1000-0x108a", "e1000-0x108a"), + PCI_ROM(0x8086, 0x108b, "e1000-0x108b", "e1000-0x108b"), + PCI_ROM(0x8086, 0x108c, "e1000-0x108c", "e1000-0x108c"), + PCI_ROM(0x8086, 0x1096, "e1000-0x1096", "e1000-0x1096"), + PCI_ROM(0x8086, 0x1098, "e1000-0x1098", "e1000-0x1098"), + PCI_ROM(0x8086, 0x1099, "e1000-0x1099", "e1000-0x1099"), + PCI_ROM(0x8086, 0x109a, "e1000-0x109a", "e1000-0x109a"), + PCI_ROM(0x8086, 0x10a4, "e1000-0x10a4", "e1000-0x10a4"), + PCI_ROM(0x8086, 0x10a5, "e1000-0x10a5", "e1000-0x10a5"), + PCI_ROM(0x8086, 0x10b5, "e1000-0x10b5", "e1000-0x10b5"), + PCI_ROM(0x8086, 0x10b9, "e1000-0x10b9", "e1000-0x10b9"), + PCI_ROM(0x8086, 0x10ba, "e1000-0x10ba", "e1000-0x10ba"), + PCI_ROM(0x8086, 0x10bb, "e1000-0x10bb", "e1000-0x10bb"), + PCI_ROM(0x8086, 0x10bc, "e1000-0x10bc", "e1000-0x10bc"), + PCI_ROM(0x8086, 0x10c4, "e1000-0x10c4", "e1000-0x10c4"), + PCI_ROM(0x8086, 0x10c5, "e1000-0x10c5", "e1000-0x10c5"), + PCI_ROM(0x8086, 0x10d9, "e1000-0x10d9", "e1000-0x10d9"), + PCI_ROM(0x8086, 0x10da, "e1000-0x10da", "e1000-0x10da"), +}; + +struct pci_driver e1000_driver __pci_driver = { + .ids = e1000_nics, + .id_count = (sizeof (e1000_nics) / sizeof (e1000_nics[0])), + .probe = e1000_probe, + .remove = e1000_remove, +}; + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/e1000/e1000.h b/gpxe/src/drivers/net/e1000/e1000.h new file mode 100644 index 0000000..77a09ef --- /dev/null +++ b/gpxe/src/drivers/net/e1000/e1000.h @@ -0,0 +1,304 @@ +/******************************************************************************* + + Intel PRO/1000 Linux driver + Copyright(c) 1999 - 2006 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +/* Linux PRO/1000 Ethernet Driver main header file */ + +#ifndef _E1000_H_ +#define _E1000_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BAR_0 0 +#define BAR_1 1 +#define BAR_5 5 + +struct e1000_adapter; + +#include "e1000_hw.h" + +/* Supported Rx Buffer Sizes */ +#define E1000_RXBUFFER_128 128 /* Used for packet split */ +#define E1000_RXBUFFER_256 256 /* Used for packet split */ +#define E1000_RXBUFFER_512 512 +#define E1000_RXBUFFER_1024 1024 +#define E1000_RXBUFFER_2048 2048 +#define E1000_RXBUFFER_4096 4096 +#define E1000_RXBUFFER_8192 8192 +#define E1000_RXBUFFER_16384 16384 + +/* SmartSpeed delimiters */ +#define E1000_SMARTSPEED_DOWNSHIFT 3 +#define E1000_SMARTSPEED_MAX 15 + +/* Packet Buffer allocations */ +#define E1000_PBA_BYTES_SHIFT 0xA +#define E1000_TX_HEAD_ADDR_SHIFT 7 +#define E1000_PBA_TX_MASK 0xFFFF0000 + +/* Flow Control Watermarks */ +#define E1000_FC_HIGH_DIFF 0x1638 /* High: 5688 bytes below Rx FIFO size */ +#define E1000_FC_LOW_DIFF 0x1640 /* Low: 5696 bytes below Rx FIFO size */ + +#define E1000_FC_PAUSE_TIME 0x0680 /* 858 usec */ + +/* this is the size past which hardware will drop packets when setting LPE=0 */ +#define MAXIMUM_ETHERNET_VLAN_SIZE 1522 + +/* How many Tx Descriptors do we need to call netif_wake_queue ? */ +#define E1000_TX_QUEUE_WAKE 16 +/* How many Rx Buffers do we bundle into one write to the hardware ? */ +#define E1000_RX_BUFFER_WRITE 16 /* Must be power of 2 */ + +#define AUTO_ALL_MODES 0 +#define E1000_EEPROM_82544_APM 0x0004 +#define E1000_EEPROM_ICH8_APME 0x0004 +#define E1000_EEPROM_APME 0x0400 + +#ifndef E1000_MASTER_SLAVE +/* Switch to override PHY master/slave setting */ +#define E1000_MASTER_SLAVE e1000_ms_hw_default +#endif + +/* wrapper around a pointer to a socket buffer, + * so a DMA handle can be stored along with the buffer */ +struct e1000_buffer { + struct sk_buff *skb; + unsigned long time_stamp; + uint16_t length; + uint16_t next_to_watch; +}; + +struct e1000_tx_ring { + /* pointer to the descriptor ring memory */ + void *desc; + /* length of descriptor ring in bytes */ + unsigned int size; + /* number of descriptors in the ring */ + unsigned int count; + /* next descriptor to associate a buffer with */ + unsigned int next_to_use; + /* next descriptor to check for DD status bit */ + unsigned int next_to_clean; + /* array of buffer information structs */ + struct e1000_buffer *buffer_info; + + uint16_t tdh; + uint16_t tdt; + boolean_t last_tx_tso; +}; + +struct e1000_rx_ring { + /* pointer to the descriptor ring memory */ + void *desc; + /* length of descriptor ring in bytes */ + unsigned int size; + /* number of descriptors in the ring */ + unsigned int count; + /* next descriptor to associate a buffer with */ + unsigned int next_to_use; + /* next descriptor to check for DD status bit */ + unsigned int next_to_clean; + /* array of buffer information structs */ + struct e1000_buffer *buffer_info; + /* arrays of page information for packet split */ + struct e1000_ps_page *ps_page; + struct e1000_ps_page_dma *ps_page_dma; + + /* cpu for rx queue */ + int cpu; + + uint16_t rdh; + uint16_t rdt; +}; + +#define E1000_DESC_UNUSED(R) \ + ((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->count) + \ + (R)->next_to_clean - (R)->next_to_use - 1) + +#define E1000_RX_DESC_PS(R, i) \ + (&(((union e1000_rx_desc_packet_split *)((R).desc))[i])) +#define E1000_RX_DESC_EXT(R, i) \ + (&(((union e1000_rx_desc_extended *)((R).desc))[i])) +#define E1000_GET_DESC(R, i, type) (&(((struct type *)((R).desc))[i])) +#define E1000_RX_DESC(R, i) E1000_GET_DESC(R, i, e1000_rx_desc) +#define E1000_TX_DESC(R, i) E1000_GET_DESC(R, i, e1000_tx_desc) +#define E1000_CONTEXT_DESC(R, i) E1000_GET_DESC(R, i, e1000_context_desc) + +/* board specific private data structure */ + +struct e1000_adapter { + struct vlan_group *vlgrp; + uint16_t mng_vlan_id; + uint32_t bd_number; + uint32_t rx_buffer_len; + uint32_t wol; + uint32_t smartspeed; + uint32_t en_mng_pt; + uint16_t link_speed; + uint16_t link_duplex; + + unsigned int total_tx_bytes; + unsigned int total_tx_packets; + unsigned int total_rx_bytes; + unsigned int total_rx_packets; + /* Interrupt Throttle Rate */ + uint32_t itr; + uint32_t itr_setting; + uint16_t tx_itr; + uint16_t rx_itr; + + uint8_t fc_autoneg; + + unsigned long led_status; + + /* TX */ + struct e1000_tx_ring *tx_ring; /* One per active queue */ + unsigned int restart_queue; + unsigned long tx_queue_len; + uint32_t txd_cmd; + uint32_t tx_int_delay; + uint32_t tx_abs_int_delay; + uint32_t gotcl; + uint64_t gotcl_old; + uint64_t tpt_old; + uint64_t colc_old; + uint32_t tx_timeout_count; + uint32_t tx_fifo_head; + uint32_t tx_head_addr; + uint32_t tx_fifo_size; + uint8_t tx_timeout_factor; + boolean_t pcix_82544; + boolean_t detect_tx_hung; + + /* RX */ + boolean_t (*clean_rx) (struct e1000_adapter *adapter, + struct e1000_rx_ring *rx_ring); + void (*alloc_rx_buf) (struct e1000_adapter *adapter, + struct e1000_rx_ring *rx_ring, + int cleaned_count); + struct e1000_rx_ring *rx_ring; /* One per active queue */ + int num_tx_queues; + int num_rx_queues; + + uint64_t hw_csum_err; + uint64_t hw_csum_good; + uint64_t rx_hdr_split; + uint32_t alloc_rx_buff_failed; + uint32_t rx_int_delay; + uint32_t rx_abs_int_delay; + boolean_t rx_csum; + unsigned int rx_ps_pages; + uint32_t gorcl; + uint64_t gorcl_old; + uint16_t rx_ps_bsize0; + + + /* OS defined structs */ + struct net_device *netdev; + struct pci_device *pdev; + struct net_device_stats net_stats; + + /* structs defined in e1000_hw.h */ + struct e1000_hw hw; + struct e1000_hw_stats stats; + struct e1000_phy_info phy_info; + struct e1000_phy_stats phy_stats; + + uint32_t test_icr; + struct e1000_tx_ring test_tx_ring; + struct e1000_rx_ring test_rx_ring; + + int msg_enable; + boolean_t have_msi; + + /* to not mess up cache alignment, always add to the bottom */ + boolean_t tso_force; + boolean_t smart_power_down; /* phy smart power down */ + boolean_t quad_port_a; + unsigned long flags; + uint32_t eeprom_wol; + +#define NUM_TX_DESC 8 +#define NUM_RX_DESC 8 + + struct io_buffer *tx_iobuf[NUM_TX_DESC]; + struct io_buffer *rx_iobuf[NUM_RX_DESC]; + + struct e1000_tx_desc *tx_base; + struct e1000_rx_desc *rx_base; + + uint32_t tx_ring_size; + uint32_t rx_ring_size; + + uint32_t tx_head; + uint32_t tx_tail; + uint32_t tx_fill_ctr; + + uint32_t rx_curr; + + uint32_t ioaddr; + uint32_t irqno; + +}; + +enum e1000_state_t { + __E1000_TESTING, + __E1000_RESETTING, + __E1000_DOWN +}; + +#define E1000_MNG2HOST_PORT_623 (1 << 5) +#define E1000_MNG2HOST_PORT_664 (1 << 6) + +#define E1000_ERT_2048 0x100 + +#define IORESOURCE_IO 0x00000100 +#define IORESOURCE_MEM 0x00000200 +#define IORESOURCE_PREFETCH 0x00001000 + +#endif /* _E1000_H_ */ + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/e1000/e1000_hw.c b/gpxe/src/drivers/net/e1000/e1000_hw.c new file mode 100644 index 0000000..1054b90 --- /dev/null +++ b/gpxe/src/drivers/net/e1000/e1000_hw.c @@ -0,0 +1,9050 @@ +/******************************************************************************* + + Intel PRO/1000 Linux driver + Copyright(c) 1999 - 2006 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +/* e1000_hw.c + * Shared functions for accessing and configuring the MAC + */ + + +#include "e1000_hw.h" + +static int32_t e1000_swfw_sync_acquire(struct e1000_hw *hw, uint16_t mask); +static void e1000_swfw_sync_release(struct e1000_hw *hw, uint16_t mask); +static int32_t e1000_read_kmrn_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *data); +static int32_t e1000_write_kmrn_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t data); +static int32_t e1000_get_software_semaphore(struct e1000_hw *hw); +static void e1000_release_software_semaphore(struct e1000_hw *hw); + +static uint8_t e1000_arc_subsystem_valid(struct e1000_hw *hw); +static int32_t e1000_check_downshift(struct e1000_hw *hw); +static int32_t e1000_check_polarity(struct e1000_hw *hw, e1000_rev_polarity *polarity); +static void e1000_clear_hw_cntrs(struct e1000_hw *hw); +static void e1000_clear_vfta(struct e1000_hw *hw); +static int32_t e1000_commit_shadow_ram(struct e1000_hw *hw); +static int32_t e1000_config_dsp_after_link_change(struct e1000_hw *hw, boolean_t link_up); +static int32_t e1000_config_fc_after_link_up(struct e1000_hw *hw); +static int32_t e1000_detect_gig_phy(struct e1000_hw *hw); +static int32_t e1000_erase_ich8_4k_segment(struct e1000_hw *hw, uint32_t bank); +static int32_t e1000_get_auto_rd_done(struct e1000_hw *hw); +static int32_t e1000_get_cable_length(struct e1000_hw *hw, uint16_t *min_length, uint16_t *max_length); +static int32_t e1000_get_hw_eeprom_semaphore(struct e1000_hw *hw); +static int32_t e1000_get_phy_cfg_done(struct e1000_hw *hw); +static int32_t e1000_get_software_flag(struct e1000_hw *hw); +static int32_t e1000_ich8_cycle_init(struct e1000_hw *hw); +static int32_t e1000_ich8_flash_cycle(struct e1000_hw *hw, uint32_t timeout); +static int32_t e1000_id_led_init(struct e1000_hw *hw); +static int32_t e1000_init_lcd_from_nvm_config_region(struct e1000_hw *hw, uint32_t cnf_base_addr, uint32_t cnf_size); +static int32_t e1000_init_lcd_from_nvm(struct e1000_hw *hw); +static void e1000_init_rx_addrs(struct e1000_hw *hw); +static void e1000_initialize_hardware_bits(struct e1000_hw *hw); +static boolean_t e1000_is_onboard_nvm_eeprom(struct e1000_hw *hw); +static int32_t e1000_kumeran_lock_loss_workaround(struct e1000_hw *hw); +static int32_t e1000_mng_enable_host_if(struct e1000_hw *hw); +static int32_t e1000_mng_host_if_write(struct e1000_hw *hw, uint8_t *buffer, uint16_t length, uint16_t offset, uint8_t *sum); +static int32_t e1000_mng_write_cmd_header(struct e1000_hw* hw, struct e1000_host_mng_command_header* hdr); +static int32_t e1000_mng_write_commit(struct e1000_hw *hw); +static int32_t e1000_phy_ife_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info); +static int32_t e1000_phy_igp_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info); +static int32_t e1000_read_eeprom_eerd(struct e1000_hw *hw, uint16_t offset, uint16_t words, uint16_t *data); +static int32_t e1000_write_eeprom_eewr(struct e1000_hw *hw, uint16_t offset, uint16_t words, uint16_t *data); +static int32_t e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int eerd); +static int32_t e1000_phy_m88_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info); +static void e1000_put_hw_eeprom_semaphore(struct e1000_hw *hw); +static int32_t e1000_read_ich8_byte(struct e1000_hw *hw, uint32_t index, uint8_t *data); +static int32_t e1000_verify_write_ich8_byte(struct e1000_hw *hw, uint32_t index, uint8_t byte); +static int32_t e1000_write_ich8_byte(struct e1000_hw *hw, uint32_t index, uint8_t byte); +static int32_t e1000_read_ich8_word(struct e1000_hw *hw, uint32_t index, uint16_t *data); +static int32_t e1000_read_ich8_data(struct e1000_hw *hw, uint32_t index, uint32_t size, uint16_t *data); +static int32_t e1000_write_ich8_data(struct e1000_hw *hw, uint32_t index, uint32_t size, uint16_t data); +static int32_t e1000_read_eeprom_ich8(struct e1000_hw *hw, uint16_t offset, uint16_t words, uint16_t *data); +static int32_t e1000_write_eeprom_ich8(struct e1000_hw *hw, uint16_t offset, uint16_t words, uint16_t *data); +static void e1000_release_software_flag(struct e1000_hw *hw); +static int32_t e1000_set_d3_lplu_state(struct e1000_hw *hw, boolean_t active); +static int32_t e1000_set_d0_lplu_state(struct e1000_hw *hw, boolean_t active); +static int32_t e1000_set_pci_ex_no_snoop(struct e1000_hw *hw, uint32_t no_snoop); +static void e1000_set_pci_express_master_disable(struct e1000_hw *hw); +static int32_t e1000_wait_autoneg(struct e1000_hw *hw); +static void e1000_write_reg_io(struct e1000_hw *hw, uint32_t offset, uint32_t value); +static int32_t e1000_set_phy_type(struct e1000_hw *hw); +static void e1000_phy_init_script(struct e1000_hw *hw); +static int32_t e1000_setup_copper_link(struct e1000_hw *hw); +static int32_t e1000_setup_fiber_serdes_link(struct e1000_hw *hw); +static int32_t e1000_adjust_serdes_amplitude(struct e1000_hw *hw); +static int32_t e1000_phy_force_speed_duplex(struct e1000_hw *hw); +static int32_t e1000_config_mac_to_phy(struct e1000_hw *hw); +static void e1000_raise_mdi_clk(struct e1000_hw *hw, uint32_t *ctrl); +static void e1000_lower_mdi_clk(struct e1000_hw *hw, uint32_t *ctrl); +static void e1000_shift_out_mdi_bits(struct e1000_hw *hw, uint32_t data, + uint16_t count); +static uint16_t e1000_shift_in_mdi_bits(struct e1000_hw *hw); +static int32_t e1000_phy_reset_dsp(struct e1000_hw *hw); +static int32_t e1000_write_eeprom_spi(struct e1000_hw *hw, uint16_t offset, + uint16_t words, uint16_t *data); +static int32_t e1000_write_eeprom_microwire(struct e1000_hw *hw, + uint16_t offset, uint16_t words, + uint16_t *data); +static int32_t e1000_spi_eeprom_ready(struct e1000_hw *hw); +static void e1000_raise_ee_clk(struct e1000_hw *hw, uint32_t *eecd); +static void e1000_lower_ee_clk(struct e1000_hw *hw, uint32_t *eecd); +static void e1000_shift_out_ee_bits(struct e1000_hw *hw, uint16_t data, + uint16_t count); +static int32_t e1000_write_phy_reg_ex(struct e1000_hw *hw, uint32_t reg_addr, + uint16_t phy_data); +static int32_t e1000_read_phy_reg_ex(struct e1000_hw *hw,uint32_t reg_addr, + uint16_t *phy_data); +static uint16_t e1000_shift_in_ee_bits(struct e1000_hw *hw, uint16_t count); +static int32_t e1000_acquire_eeprom(struct e1000_hw *hw); +static void e1000_release_eeprom(struct e1000_hw *hw); +static void e1000_standby_eeprom(struct e1000_hw *hw); +static int32_t e1000_set_vco_speed(struct e1000_hw *hw); +static int32_t e1000_polarity_reversal_workaround(struct e1000_hw *hw); +static int32_t e1000_set_phy_mode(struct e1000_hw *hw); +static int32_t e1000_host_if_read_cookie(struct e1000_hw *hw, uint8_t *buffer); +static uint8_t e1000_calculate_mng_checksum(char *buffer, uint32_t length); +static int32_t e1000_configure_kmrn_for_10_100(struct e1000_hw *hw, + uint16_t duplex); +static int32_t e1000_configure_kmrn_for_1000(struct e1000_hw *hw); + +/* IGP cable length table */ +static const +uint16_t e1000_igp_cable_length_table[IGP01E1000_AGC_LENGTH_TABLE_SIZE] = + { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 10, 10, 10, 10, 10, 10, 10, 20, 20, 20, 20, 20, 25, 25, 25, + 25, 25, 25, 25, 30, 30, 30, 30, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 50, 50, 50, 50, 50, 50, 50, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 70, 70, 70, 70, 70, 70, 80, 80, 80, 80, 80, 80, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120}; + +static const +uint16_t e1000_igp_2_cable_length_table[IGP02E1000_AGC_LENGTH_TABLE_SIZE] = + { 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 8, 11, 13, 16, 18, 21, + 0, 0, 0, 3, 6, 10, 13, 16, 19, 23, 26, 29, 32, 35, 38, 41, + 6, 10, 14, 18, 22, 26, 30, 33, 37, 41, 44, 48, 51, 54, 58, 61, + 21, 26, 31, 35, 40, 44, 49, 53, 57, 61, 65, 68, 72, 75, 79, 82, + 40, 45, 51, 56, 61, 66, 70, 75, 79, 83, 87, 91, 94, 98, 101, 104, + 60, 66, 72, 77, 82, 87, 92, 96, 100, 104, 108, 111, 114, 117, 119, 121, + 83, 89, 95, 100, 105, 109, 113, 116, 119, 122, 124, + 104, 109, 114, 118, 121, 124}; + +/****************************************************************************** + * Set the phy type member in the hw struct. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static int32_t +e1000_set_phy_type(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_set_phy_type"); + + if (hw->mac_type == e1000_undefined) + return -E1000_ERR_PHY_TYPE; + + switch (hw->phy_id) { + case M88E1000_E_PHY_ID: + case M88E1000_I_PHY_ID: + case M88E1011_I_PHY_ID: + case M88E1111_I_PHY_ID: + hw->phy_type = e1000_phy_m88; + break; + case IGP01E1000_I_PHY_ID: + if (hw->mac_type == e1000_82541 || + hw->mac_type == e1000_82541_rev_2 || + hw->mac_type == e1000_82547 || + hw->mac_type == e1000_82547_rev_2) { + hw->phy_type = e1000_phy_igp; + break; + } + case IGP03E1000_E_PHY_ID: + hw->phy_type = e1000_phy_igp_3; + break; + case IFE_E_PHY_ID: + case IFE_PLUS_E_PHY_ID: + case IFE_C_E_PHY_ID: + hw->phy_type = e1000_phy_ife; + break; + case GG82563_E_PHY_ID: + if (hw->mac_type == e1000_80003es2lan) { + hw->phy_type = e1000_phy_gg82563; + break; + } + /* Fall Through */ + default: + /* Should never have loaded on this device */ + hw->phy_type = e1000_phy_undefined; + return -E1000_ERR_PHY_TYPE; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** + * IGP phy init script - initializes the GbE PHY + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static void +e1000_phy_init_script(struct e1000_hw *hw) +{ + uint32_t ret_val; + uint16_t phy_saved_data; + + DEBUGFUNC("e1000_phy_init_script"); + + if (hw->phy_init_script) { + msleep(20); + + /* Save off the current value of register 0x2F5B to be restored at + * the end of this routine. */ + ret_val = e1000_read_phy_reg(hw, 0x2F5B, &phy_saved_data); + + /* Disabled the PHY transmitter */ + e1000_write_phy_reg(hw, 0x2F5B, 0x0003); + + msleep(20); + + e1000_write_phy_reg(hw,0x0000,0x0140); + + msleep(5); + + switch (hw->mac_type) { + case e1000_82541: + case e1000_82547: + e1000_write_phy_reg(hw, 0x1F95, 0x0001); + + e1000_write_phy_reg(hw, 0x1F71, 0xBD21); + + e1000_write_phy_reg(hw, 0x1F79, 0x0018); + + e1000_write_phy_reg(hw, 0x1F30, 0x1600); + + e1000_write_phy_reg(hw, 0x1F31, 0x0014); + + e1000_write_phy_reg(hw, 0x1F32, 0x161C); + + e1000_write_phy_reg(hw, 0x1F94, 0x0003); + + e1000_write_phy_reg(hw, 0x1F96, 0x003F); + + e1000_write_phy_reg(hw, 0x2010, 0x0008); + break; + + case e1000_82541_rev_2: + case e1000_82547_rev_2: + e1000_write_phy_reg(hw, 0x1F73, 0x0099); + break; + default: + break; + } + + e1000_write_phy_reg(hw, 0x0000, 0x3300); + + msleep(20); + + /* Now enable the transmitter */ + e1000_write_phy_reg(hw, 0x2F5B, phy_saved_data); + + if (hw->mac_type == e1000_82547) { + uint16_t fused, fine, coarse; + + /* Move to analog registers page */ + e1000_read_phy_reg(hw, IGP01E1000_ANALOG_SPARE_FUSE_STATUS, &fused); + + if (!(fused & IGP01E1000_ANALOG_SPARE_FUSE_ENABLED)) { + e1000_read_phy_reg(hw, IGP01E1000_ANALOG_FUSE_STATUS, &fused); + + fine = fused & IGP01E1000_ANALOG_FUSE_FINE_MASK; + coarse = fused & IGP01E1000_ANALOG_FUSE_COARSE_MASK; + + if (coarse > IGP01E1000_ANALOG_FUSE_COARSE_THRESH) { + coarse -= IGP01E1000_ANALOG_FUSE_COARSE_10; + fine -= IGP01E1000_ANALOG_FUSE_FINE_1; + } else if (coarse == IGP01E1000_ANALOG_FUSE_COARSE_THRESH) + fine -= IGP01E1000_ANALOG_FUSE_FINE_10; + + fused = (fused & IGP01E1000_ANALOG_FUSE_POLY_MASK) | + (fine & IGP01E1000_ANALOG_FUSE_FINE_MASK) | + (coarse & IGP01E1000_ANALOG_FUSE_COARSE_MASK); + + e1000_write_phy_reg(hw, IGP01E1000_ANALOG_FUSE_CONTROL, fused); + e1000_write_phy_reg(hw, IGP01E1000_ANALOG_FUSE_BYPASS, + IGP01E1000_ANALOG_FUSE_ENABLE_SW_CONTROL); + } + } + } +} + +/****************************************************************************** + * Set the mac type member in the hw struct. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +e1000_set_mac_type(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_set_mac_type"); + + switch (hw->device_id) { + case E1000_DEV_ID_82542: + switch (hw->revision_id) { + case E1000_82542_2_0_REV_ID: + hw->mac_type = e1000_82542_rev2_0; + break; + case E1000_82542_2_1_REV_ID: + hw->mac_type = e1000_82542_rev2_1; + break; + default: + /* Invalid 82542 revision ID */ + return -E1000_ERR_MAC_TYPE; + } + break; + case E1000_DEV_ID_82543GC_FIBER: + case E1000_DEV_ID_82543GC_COPPER: + hw->mac_type = e1000_82543; + break; + case E1000_DEV_ID_82544EI_COPPER: + case E1000_DEV_ID_82544EI_FIBER: + case E1000_DEV_ID_82544GC_COPPER: + case E1000_DEV_ID_82544GC_LOM: + hw->mac_type = e1000_82544; + break; + case E1000_DEV_ID_82540EM: + case E1000_DEV_ID_82540EM_LOM: + case E1000_DEV_ID_82540EP: + case E1000_DEV_ID_82540EP_LOM: + case E1000_DEV_ID_82540EP_LP: + hw->mac_type = e1000_82540; + break; + case E1000_DEV_ID_82545EM_COPPER: + case E1000_DEV_ID_82545EM_FIBER: + hw->mac_type = e1000_82545; + break; + case E1000_DEV_ID_82545GM_COPPER: + case E1000_DEV_ID_82545GM_FIBER: + case E1000_DEV_ID_82545GM_SERDES: + hw->mac_type = e1000_82545_rev_3; + break; + case E1000_DEV_ID_82546EB_COPPER: + case E1000_DEV_ID_82546EB_FIBER: + case E1000_DEV_ID_82546EB_QUAD_COPPER: + hw->mac_type = e1000_82546; + break; + case E1000_DEV_ID_82546GB_COPPER: + case E1000_DEV_ID_82546GB_FIBER: + case E1000_DEV_ID_82546GB_SERDES: + case E1000_DEV_ID_82546GB_PCIE: + case E1000_DEV_ID_82546GB_QUAD_COPPER: + case E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3: + hw->mac_type = e1000_82546_rev_3; + break; + case E1000_DEV_ID_82541EI: + case E1000_DEV_ID_82541EI_MOBILE: + case E1000_DEV_ID_82541ER_LOM: + hw->mac_type = e1000_82541; + break; + case E1000_DEV_ID_82541ER: + case E1000_DEV_ID_82541GI: + case E1000_DEV_ID_82541GI_LF: + case E1000_DEV_ID_82541GI_MOBILE: + hw->mac_type = e1000_82541_rev_2; + break; + case E1000_DEV_ID_82547EI: + case E1000_DEV_ID_82547EI_MOBILE: + hw->mac_type = e1000_82547; + break; + case E1000_DEV_ID_82547GI: + hw->mac_type = e1000_82547_rev_2; + break; + case E1000_DEV_ID_82571EB_COPPER: + case E1000_DEV_ID_82571EB_FIBER: + case E1000_DEV_ID_82571EB_SERDES: + case E1000_DEV_ID_82571EB_SERDES_DUAL: + case E1000_DEV_ID_82571EB_SERDES_QUAD: + case E1000_DEV_ID_82571EB_QUAD_COPPER: + case E1000_DEV_ID_82571EB_QUAD_FIBER: + case E1000_DEV_ID_82571EB_QUAD_COPPER_LOWPROFILE: + hw->mac_type = e1000_82571; + break; + case E1000_DEV_ID_82572EI_COPPER: + case E1000_DEV_ID_82572EI_FIBER: + case E1000_DEV_ID_82572EI_SERDES: + case E1000_DEV_ID_82572EI: + hw->mac_type = e1000_82572; + break; + case E1000_DEV_ID_82573E: + case E1000_DEV_ID_82573E_IAMT: + case E1000_DEV_ID_82573L: + hw->mac_type = e1000_82573; + break; + case E1000_DEV_ID_80003ES2LAN_COPPER_SPT: + case E1000_DEV_ID_80003ES2LAN_SERDES_SPT: + case E1000_DEV_ID_80003ES2LAN_COPPER_DPT: + case E1000_DEV_ID_80003ES2LAN_SERDES_DPT: + hw->mac_type = e1000_80003es2lan; + break; + case E1000_DEV_ID_ICH8_IGP_M_AMT: + case E1000_DEV_ID_ICH8_IGP_AMT: + case E1000_DEV_ID_ICH8_IGP_C: + case E1000_DEV_ID_ICH8_IFE: + case E1000_DEV_ID_ICH8_IFE_GT: + case E1000_DEV_ID_ICH8_IFE_G: + case E1000_DEV_ID_ICH8_IGP_M: + hw->mac_type = e1000_ich8lan; + break; + default: + /* Should never have loaded on this device */ + return -E1000_ERR_MAC_TYPE; + } + + switch (hw->mac_type) { + case e1000_ich8lan: + hw->swfwhw_semaphore_present = TRUE; + hw->asf_firmware_present = TRUE; + break; + case e1000_80003es2lan: + hw->swfw_sync_present = TRUE; + /* fall through */ + case e1000_82571: + case e1000_82572: + case e1000_82573: + hw->eeprom_semaphore_present = TRUE; + /* fall through */ + case e1000_82541: + case e1000_82547: + case e1000_82541_rev_2: + case e1000_82547_rev_2: + hw->asf_firmware_present = TRUE; + break; + default: + break; + } + + /* The 82543 chip does not count tx_carrier_errors properly in + * FD mode + */ + if (hw->mac_type == e1000_82543) + hw->bad_tx_carr_stats_fd = TRUE; + + /* capable of receiving management packets to the host */ + if (hw->mac_type >= e1000_82571) + hw->has_manc2h = TRUE; + + /* In rare occasions, ESB2 systems would end up started without + * the RX unit being turned on. + */ + if (hw->mac_type == e1000_80003es2lan) + hw->rx_needs_kicking = TRUE; + + if (hw->mac_type > e1000_82544) + hw->has_smbus = TRUE; + + return E1000_SUCCESS; +} + +/***************************************************************************** + * Set media type and TBI compatibility. + * + * hw - Struct containing variables accessed by shared code + * **************************************************************************/ +void +e1000_set_media_type(struct e1000_hw *hw) +{ + uint32_t status; + + DEBUGFUNC("e1000_set_media_type"); + + if (hw->mac_type != e1000_82543) { + /* tbi_compatibility is only valid on 82543 */ + hw->tbi_compatibility_en = FALSE; + } + + switch (hw->device_id) { + case E1000_DEV_ID_82545GM_SERDES: + case E1000_DEV_ID_82546GB_SERDES: + case E1000_DEV_ID_82571EB_SERDES: + case E1000_DEV_ID_82571EB_SERDES_DUAL: + case E1000_DEV_ID_82571EB_SERDES_QUAD: + case E1000_DEV_ID_82572EI_SERDES: + case E1000_DEV_ID_80003ES2LAN_SERDES_DPT: + hw->media_type = e1000_media_type_internal_serdes; + break; + default: + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + hw->media_type = e1000_media_type_fiber; + break; + case e1000_ich8lan: + case e1000_82573: + /* The STATUS_TBIMODE bit is reserved or reused for the this + * device. + */ + hw->media_type = e1000_media_type_copper; + break; + default: + status = E1000_READ_REG(hw, STATUS); + if (status & E1000_STATUS_TBIMODE) { + hw->media_type = e1000_media_type_fiber; + /* tbi_compatibility not valid on fiber */ + hw->tbi_compatibility_en = FALSE; + } else { + hw->media_type = e1000_media_type_copper; + } + break; + } + } +} + +/****************************************************************************** + * Reset the transmit and receive units; mask and clear all interrupts. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +e1000_reset_hw(struct e1000_hw *hw) +{ + uint32_t ctrl; + uint32_t ctrl_ext; + uint32_t icr; + uint32_t manc; + uint32_t led_ctrl; + uint32_t timeout; + uint32_t extcnf_ctrl; + int32_t ret_val; + + DEBUGFUNC("e1000_reset_hw"); + + /* For 82542 (rev 2.0), disable MWI before issuing a device reset */ + if (hw->mac_type == e1000_82542_rev2_0) { + DEBUGOUT("Disabling MWI on 82542 rev 2.0\n"); + e1000_pci_clear_mwi(hw); + } + + if (hw->bus_type == e1000_bus_type_pci_express) { + /* Prevent the PCI-E bus from sticking if there is no TLP connection + * on the last TLP read/write transaction when MAC is reset. + */ + if (e1000_disable_pciex_master(hw) != E1000_SUCCESS) { + DEBUGOUT("PCI-E Master disable polling has failed.\n"); + } + } + + /* Clear interrupt mask to stop board from generating interrupts */ + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(hw, IMC, 0xffffffff); + + /* Disable the Transmit and Receive units. Then delay to allow + * any pending transactions to complete before we hit the MAC with + * the global reset. + */ + E1000_WRITE_REG(hw, RCTL, 0); + E1000_WRITE_REG(hw, TCTL, E1000_TCTL_PSP); + E1000_WRITE_FLUSH(hw); + + /* The tbi_compatibility_on Flag must be cleared when Rctl is cleared. */ + hw->tbi_compatibility_on = FALSE; + + /* Delay to allow any outstanding PCI transactions to complete before + * resetting the device + */ + msleep(10); + + ctrl = E1000_READ_REG(hw, CTRL); + + /* Must reset the PHY before resetting the MAC */ + if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) { + E1000_WRITE_REG(hw, CTRL, (ctrl | E1000_CTRL_PHY_RST)); + msleep(5); + } + + /* Must acquire the MDIO ownership before MAC reset. + * Ownership defaults to firmware after a reset. */ + if (hw->mac_type == e1000_82573) { + timeout = 10; + + extcnf_ctrl = E1000_READ_REG(hw, EXTCNF_CTRL); + extcnf_ctrl |= E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP; + + do { + E1000_WRITE_REG(hw, EXTCNF_CTRL, extcnf_ctrl); + extcnf_ctrl = E1000_READ_REG(hw, EXTCNF_CTRL); + + if (extcnf_ctrl & E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP) + break; + else + extcnf_ctrl |= E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP; + + msleep(2); + timeout--; + } while (timeout); + } + + /* Workaround for ICH8 bit corruption issue in FIFO memory */ + if (hw->mac_type == e1000_ich8lan) { + /* Set Tx and Rx buffer allocation to 8k apiece. */ + E1000_WRITE_REG(hw, PBA, E1000_PBA_8K); + /* Set Packet Buffer Size to 16k. */ + E1000_WRITE_REG(hw, PBS, E1000_PBS_16K); + } + + /* Issue a global reset to the MAC. This will reset the chip's + * transmit, receive, DMA, and link units. It will not effect + * the current PCI configuration. The global reset bit is self- + * clearing, and should clear within a microsecond. + */ + DEBUGOUT("Issuing a global reset to MAC\n"); + + switch (hw->mac_type) { + case e1000_82544: + case e1000_82540: + case e1000_82545: + case e1000_82546: + case e1000_82541: + case e1000_82541_rev_2: + /* These controllers can't ack the 64-bit write when issuing the + * reset, so use IO-mapping as a workaround to issue the reset */ + E1000_WRITE_REG_IO(hw, CTRL, (ctrl | E1000_CTRL_RST)); + break; + case e1000_82545_rev_3: + case e1000_82546_rev_3: + /* Reset is performed on a shadow of the control register */ + E1000_WRITE_REG(hw, CTRL_DUP, (ctrl | E1000_CTRL_RST)); + break; + case e1000_ich8lan: + if (!hw->phy_reset_disable && + e1000_check_phy_reset_block(hw) == E1000_SUCCESS) { + /* e1000_ich8lan PHY HW reset requires MAC CORE reset + * at the same time to make sure the interface between + * MAC and the external PHY is reset. + */ + ctrl |= E1000_CTRL_PHY_RST; + } + + e1000_get_software_flag(hw); + E1000_WRITE_REG(hw, CTRL, (ctrl | E1000_CTRL_RST)); + msleep(5); + break; + default: + E1000_WRITE_REG(hw, CTRL, (ctrl | E1000_CTRL_RST)); + break; + } + + /* After MAC reset, force reload of EEPROM to restore power-on settings to + * device. Later controllers reload the EEPROM automatically, so just wait + * for reload to complete. + */ + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + case e1000_82543: + case e1000_82544: + /* Wait for reset to complete */ + udelay(10); + ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_EE_RST; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + /* Wait for EEPROM reload */ + msleep(2); + break; + case e1000_82541: + case e1000_82541_rev_2: + case e1000_82547: + case e1000_82547_rev_2: + /* Wait for EEPROM reload */ + msleep(20); + break; + case e1000_82573: + if (e1000_is_onboard_nvm_eeprom(hw) == FALSE) { + udelay(10); + ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_EE_RST; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + } + /* fall through */ + default: + /* Auto read done will delay 5ms or poll based on mac type */ + ret_val = e1000_get_auto_rd_done(hw); + if (ret_val) + return ret_val; + break; + } + + /* Disable HW ARPs on ASF enabled adapters */ + if (hw->mac_type >= e1000_82540 && hw->mac_type <= e1000_82547_rev_2) { + manc = E1000_READ_REG(hw, MANC); + manc &= ~(E1000_MANC_ARP_EN); + E1000_WRITE_REG(hw, MANC, manc); + } + + if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) { + e1000_phy_init_script(hw); + + /* Configure activity LED after PHY reset */ + led_ctrl = E1000_READ_REG(hw, LEDCTL); + led_ctrl &= IGP_ACTIVITY_LED_MASK; + led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); + E1000_WRITE_REG(hw, LEDCTL, led_ctrl); + } + + /* Clear interrupt mask to stop board from generating interrupts */ + DEBUGOUT("Masking off all interrupts\n"); + E1000_WRITE_REG(hw, IMC, 0xffffffff); + + /* Clear any pending interrupt events. */ + icr = E1000_READ_REG(hw, ICR); + + /* If MWI was previously enabled, reenable it. */ + if (hw->mac_type == e1000_82542_rev2_0) { + if (hw->pci_cmd_word & PCI_COMMAND_INVALIDATE) + e1000_pci_set_mwi(hw); + } + + if (hw->mac_type == e1000_ich8lan) { + uint32_t kab = E1000_READ_REG(hw, KABGTXD); + kab |= E1000_KABGTXD_BGSQLBIAS; + E1000_WRITE_REG(hw, KABGTXD, kab); + } + + return E1000_SUCCESS; +} + +/****************************************************************************** + * + * Initialize a number of hardware-dependent bits + * + * hw: Struct containing variables accessed by shared code + * + * This function contains hardware limitation workarounds for PCI-E adapters + * + *****************************************************************************/ +static void +e1000_initialize_hardware_bits(struct e1000_hw *hw) +{ + if ((hw->mac_type >= e1000_82571) && (!hw->initialize_hw_bits_disable)) { + /* Settings common to all PCI-express silicon */ + uint32_t reg_ctrl, reg_ctrl_ext; + uint32_t reg_tarc0, reg_tarc1; + uint32_t reg_tctl; + uint32_t reg_txdctl, reg_txdctl1; + + /* link autonegotiation/sync workarounds */ + reg_tarc0 = E1000_READ_REG(hw, TARC0); + reg_tarc0 &= ~((1 << 30)|(1 << 29)|(1 << 28)|(1 << 27)); + + /* Enable not-done TX descriptor counting */ + reg_txdctl = E1000_READ_REG(hw, TXDCTL); + reg_txdctl |= E1000_TXDCTL_COUNT_DESC; + E1000_WRITE_REG(hw, TXDCTL, reg_txdctl); + reg_txdctl1 = E1000_READ_REG(hw, TXDCTL1); + reg_txdctl1 |= E1000_TXDCTL_COUNT_DESC; + E1000_WRITE_REG(hw, TXDCTL1, reg_txdctl1); + + switch (hw->mac_type) { + case e1000_82571: + case e1000_82572: + /* Clear PHY TX compatible mode bits */ + reg_tarc1 = E1000_READ_REG(hw, TARC1); + reg_tarc1 &= ~((1 << 30)|(1 << 29)); + + /* link autonegotiation/sync workarounds */ + reg_tarc0 |= ((1 << 26)|(1 << 25)|(1 << 24)|(1 << 23)); + + /* TX ring control fixes */ + reg_tarc1 |= ((1 << 26)|(1 << 25)|(1 << 24)); + + /* Multiple read bit is reversed polarity */ + reg_tctl = E1000_READ_REG(hw, TCTL); + if (reg_tctl & E1000_TCTL_MULR) + reg_tarc1 &= ~(1 << 28); + else + reg_tarc1 |= (1 << 28); + + E1000_WRITE_REG(hw, TARC1, reg_tarc1); + break; + case e1000_82573: + reg_ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + reg_ctrl_ext &= ~(1 << 23); + reg_ctrl_ext |= (1 << 22); + + /* TX byte count fix */ + reg_ctrl = E1000_READ_REG(hw, CTRL); + reg_ctrl &= ~(1 << 29); + + E1000_WRITE_REG(hw, CTRL_EXT, reg_ctrl_ext); + E1000_WRITE_REG(hw, CTRL, reg_ctrl); + break; + case e1000_80003es2lan: + /* improve small packet performace for fiber/serdes */ + if ((hw->media_type == e1000_media_type_fiber) || + (hw->media_type == e1000_media_type_internal_serdes)) { + reg_tarc0 &= ~(1 << 20); + } + + /* Multiple read bit is reversed polarity */ + reg_tctl = E1000_READ_REG(hw, TCTL); + reg_tarc1 = E1000_READ_REG(hw, TARC1); + if (reg_tctl & E1000_TCTL_MULR) + reg_tarc1 &= ~(1 << 28); + else + reg_tarc1 |= (1 << 28); + + E1000_WRITE_REG(hw, TARC1, reg_tarc1); + break; + case e1000_ich8lan: + /* Reduce concurrent DMA requests to 3 from 4 */ + if ((hw->revision_id < 3) || + ((hw->device_id != E1000_DEV_ID_ICH8_IGP_M_AMT) && + (hw->device_id != E1000_DEV_ID_ICH8_IGP_M))) + reg_tarc0 |= ((1 << 29)|(1 << 28)); + + reg_ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + reg_ctrl_ext |= (1 << 22); + E1000_WRITE_REG(hw, CTRL_EXT, reg_ctrl_ext); + + /* workaround TX hang with TSO=on */ + reg_tarc0 |= ((1 << 27)|(1 << 26)|(1 << 24)|(1 << 23)); + + /* Multiple read bit is reversed polarity */ + reg_tctl = E1000_READ_REG(hw, TCTL); + reg_tarc1 = E1000_READ_REG(hw, TARC1); + if (reg_tctl & E1000_TCTL_MULR) + reg_tarc1 &= ~(1 << 28); + else + reg_tarc1 |= (1 << 28); + + /* workaround TX hang with TSO=on */ + reg_tarc1 |= ((1 << 30)|(1 << 26)|(1 << 24)); + + E1000_WRITE_REG(hw, TARC1, reg_tarc1); + break; + default: + break; + } + + E1000_WRITE_REG(hw, TARC0, reg_tarc0); + } +} + +/****************************************************************************** + * Performs basic configuration of the adapter. + * + * hw - Struct containing variables accessed by shared code + * + * Assumes that the controller has previously been reset and is in a + * post-reset uninitialized state. Initializes the receive address registers, + * multicast table, and VLAN filter table. Calls routines to setup link + * configuration and flow control settings. Clears all on-chip counters. Leaves + * the transmit and receive units disabled and uninitialized. + *****************************************************************************/ +int32_t +e1000_init_hw(struct e1000_hw *hw) +{ + uint32_t ctrl; + uint32_t i; + int32_t ret_val; + uint16_t pcix_cmd_word; + uint16_t pcix_stat_hi_word; + uint16_t cmd_mmrbc; + uint16_t stat_mmrbc; + uint32_t mta_size; + uint32_t reg_data; + uint32_t ctrl_ext; + + DEBUGFUNC("e1000_init_hw"); + + /* force full DMA clock frequency for 10/100 on ICH8 A0-B0 */ + if ((hw->mac_type == e1000_ich8lan) && + ((hw->revision_id < 3) || + ((hw->device_id != E1000_DEV_ID_ICH8_IGP_M_AMT) && + (hw->device_id != E1000_DEV_ID_ICH8_IGP_M)))) { + reg_data = E1000_READ_REG(hw, STATUS); + reg_data &= ~0x80000000; + E1000_WRITE_REG(hw, STATUS, reg_data); + } + + /* Initialize Identification LED */ + ret_val = e1000_id_led_init(hw); + if (ret_val) { + DEBUGOUT("Error Initializing Identification LED\n"); + return ret_val; + } + + /* Set the media type and TBI compatibility */ + e1000_set_media_type(hw); + + /* Must be called after e1000_set_media_type because media_type is used */ + e1000_initialize_hardware_bits(hw); + + /* Disabling VLAN filtering. */ + DEBUGOUT("Initializing the IEEE VLAN\n"); + /* VET hardcoded to standard value and VFTA removed in ICH8 LAN */ + if (hw->mac_type != e1000_ich8lan) { + if (hw->mac_type < e1000_82545_rev_3) + E1000_WRITE_REG(hw, VET, 0); + e1000_clear_vfta(hw); + } + + /* For 82542 (rev 2.0), disable MWI and put the receiver into reset */ + if (hw->mac_type == e1000_82542_rev2_0) { + DEBUGOUT("Disabling MWI on 82542 rev 2.0\n"); + e1000_pci_clear_mwi(hw); + E1000_WRITE_REG(hw, RCTL, E1000_RCTL_RST); + E1000_WRITE_FLUSH(hw); + msleep(5); + } + + /* Setup the receive address. This involves initializing all of the Receive + * Address Registers (RARs 0 - 15). + */ + e1000_init_rx_addrs(hw); + + /* For 82542 (rev 2.0), take the receiver out of reset and enable MWI */ + if (hw->mac_type == e1000_82542_rev2_0) { + E1000_WRITE_REG(hw, RCTL, 0); + E1000_WRITE_FLUSH(hw); + msleep(1); + if (hw->pci_cmd_word & PCI_COMMAND_INVALIDATE) + e1000_pci_set_mwi(hw); + } + + /* Zero out the Multicast HASH table */ + DEBUGOUT("Zeroing the MTA\n"); + mta_size = E1000_MC_TBL_SIZE; + if (hw->mac_type == e1000_ich8lan) + mta_size = E1000_MC_TBL_SIZE_ICH8LAN; + for (i = 0; i < mta_size; i++) { + E1000_WRITE_REG_ARRAY(hw, MTA, i, 0); + /* use write flush to prevent Memory Write Block (MWB) from + * occuring when accessing our register space */ + E1000_WRITE_FLUSH(hw); + } + + /* Set the PCI priority bit correctly in the CTRL register. This + * determines if the adapter gives priority to receives, or if it + * gives equal priority to transmits and receives. Valid only on + * 82542 and 82543 silicon. + */ + if (hw->dma_fairness && hw->mac_type <= e1000_82543) { + ctrl = E1000_READ_REG(hw, CTRL); + E1000_WRITE_REG(hw, CTRL, ctrl | E1000_CTRL_PRIOR); + } + + switch (hw->mac_type) { + case e1000_82545_rev_3: + case e1000_82546_rev_3: + break; + default: + /* Workaround for PCI-X problem when BIOS sets MMRBC incorrectly. */ + if (hw->bus_type == e1000_bus_type_pcix) { + e1000_read_pci_cfg(hw, PCIX_COMMAND_REGISTER, &pcix_cmd_word); + e1000_read_pci_cfg(hw, PCIX_STATUS_REGISTER_HI, + &pcix_stat_hi_word); + cmd_mmrbc = (pcix_cmd_word & PCIX_COMMAND_MMRBC_MASK) >> + PCIX_COMMAND_MMRBC_SHIFT; + stat_mmrbc = (pcix_stat_hi_word & PCIX_STATUS_HI_MMRBC_MASK) >> + PCIX_STATUS_HI_MMRBC_SHIFT; + if (stat_mmrbc == PCIX_STATUS_HI_MMRBC_4K) + stat_mmrbc = PCIX_STATUS_HI_MMRBC_2K; + if (cmd_mmrbc > stat_mmrbc) { + pcix_cmd_word &= ~PCIX_COMMAND_MMRBC_MASK; + pcix_cmd_word |= stat_mmrbc << PCIX_COMMAND_MMRBC_SHIFT; + e1000_write_pci_cfg(hw, PCIX_COMMAND_REGISTER, + &pcix_cmd_word); + } + } + break; + } + + /* More time needed for PHY to initialize */ + if (hw->mac_type == e1000_ich8lan) + msleep(15); + + /* Call a subroutine to configure the link and setup flow control. */ + ret_val = e1000_setup_link(hw); + + /* Set the transmit descriptor write-back policy */ + if (hw->mac_type > e1000_82544) { + ctrl = E1000_READ_REG(hw, TXDCTL); + ctrl = (ctrl & ~E1000_TXDCTL_WTHRESH) | E1000_TXDCTL_FULL_TX_DESC_WB; + E1000_WRITE_REG(hw, TXDCTL, ctrl); + } + + if (hw->mac_type == e1000_82573) { + e1000_enable_tx_pkt_filtering(hw); + } + + switch (hw->mac_type) { + default: + break; + case e1000_80003es2lan: + /* Enable retransmit on late collisions */ + reg_data = E1000_READ_REG(hw, TCTL); + reg_data |= E1000_TCTL_RTLC; + E1000_WRITE_REG(hw, TCTL, reg_data); + + /* Configure Gigabit Carry Extend Padding */ + reg_data = E1000_READ_REG(hw, TCTL_EXT); + reg_data &= ~E1000_TCTL_EXT_GCEX_MASK; + reg_data |= DEFAULT_80003ES2LAN_TCTL_EXT_GCEX; + E1000_WRITE_REG(hw, TCTL_EXT, reg_data); + + /* Configure Transmit Inter-Packet Gap */ + reg_data = E1000_READ_REG(hw, TIPG); + reg_data &= ~E1000_TIPG_IPGT_MASK; + reg_data |= DEFAULT_80003ES2LAN_TIPG_IPGT_1000; + E1000_WRITE_REG(hw, TIPG, reg_data); + + reg_data = E1000_READ_REG_ARRAY(hw, FFLT, 0x0001); + reg_data &= ~0x00100000; + E1000_WRITE_REG_ARRAY(hw, FFLT, 0x0001, reg_data); + /* Fall through */ + case e1000_82571: + case e1000_82572: + case e1000_ich8lan: + ctrl = E1000_READ_REG(hw, TXDCTL1); + ctrl = (ctrl & ~E1000_TXDCTL_WTHRESH) | E1000_TXDCTL_FULL_TX_DESC_WB; + E1000_WRITE_REG(hw, TXDCTL1, ctrl); + break; + } + + + if (hw->mac_type == e1000_82573) { + uint32_t gcr = E1000_READ_REG(hw, GCR); + gcr |= E1000_GCR_L1_ACT_WITHOUT_L0S_RX; + E1000_WRITE_REG(hw, GCR, gcr); + } + + /* Clear all of the statistics registers (clear on read). It is + * important that we do this after we have tried to establish link + * because the symbol error count will increment wildly if there + * is no link. + */ + e1000_clear_hw_cntrs(hw); + + /* ICH8 No-snoop bits are opposite polarity. + * Set to snoop by default after reset. */ + if (hw->mac_type == e1000_ich8lan) + e1000_set_pci_ex_no_snoop(hw, PCI_EX_82566_SNOOP_ALL); + + if (hw->device_id == E1000_DEV_ID_82546GB_QUAD_COPPER || + hw->device_id == E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3) { + ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + /* Relaxed ordering must be disabled to avoid a parity + * error crash in a PCI slot. */ + ctrl_ext |= E1000_CTRL_EXT_RO_DIS; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + } + + return ret_val; +} + +/****************************************************************************** + * Adjust SERDES output amplitude based on EEPROM setting. + * + * hw - Struct containing variables accessed by shared code. + *****************************************************************************/ +static int32_t +e1000_adjust_serdes_amplitude(struct e1000_hw *hw) +{ + uint16_t eeprom_data; + int32_t ret_val; + + DEBUGFUNC("e1000_adjust_serdes_amplitude"); + + if (hw->media_type != e1000_media_type_internal_serdes) + return E1000_SUCCESS; + + switch (hw->mac_type) { + case e1000_82545_rev_3: + case e1000_82546_rev_3: + break; + default: + return E1000_SUCCESS; + } + + ret_val = e1000_read_eeprom(hw, EEPROM_SERDES_AMPLITUDE, 1, &eeprom_data); + if (ret_val) { + return ret_val; + } + + if (eeprom_data != EEPROM_RESERVED_WORD) { + /* Adjust SERDES output amplitude only. */ + eeprom_data &= EEPROM_SERDES_AMPLITUDE_MASK; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_EXT_CTRL, eeprom_data); + if (ret_val) + return ret_val; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Configures flow control and link settings. + * + * hw - Struct containing variables accessed by shared code + * + * Determines which flow control settings to use. Calls the apropriate media- + * specific link configuration function. Configures the flow control settings. + * Assuming the adapter has a valid link partner, a valid link should be + * established. Assumes the hardware has previously been reset and the + * transmitter and receiver are not enabled. + *****************************************************************************/ +int32_t +e1000_setup_link(struct e1000_hw *hw) +{ + uint32_t ctrl_ext; + int32_t ret_val; + uint16_t eeprom_data; + + DEBUGFUNC("e1000_setup_link"); + + /* In the case of the phy reset being blocked, we already have a link. + * We do not have to set it up again. */ + if (e1000_check_phy_reset_block(hw)) + return E1000_SUCCESS; + + /* Read and store word 0x0F of the EEPROM. This word contains bits + * that determine the hardware's default PAUSE (flow control) mode, + * a bit that determines whether the HW defaults to enabling or + * disabling auto-negotiation, and the direction of the + * SW defined pins. If there is no SW over-ride of the flow + * control setting, then the variable hw->fc will + * be initialized based on a value in the EEPROM. + */ + if (hw->fc == E1000_FC_DEFAULT) { + switch (hw->mac_type) { + case e1000_ich8lan: + case e1000_82573: + hw->fc = E1000_FC_FULL; + break; + default: + ret_val = e1000_read_eeprom(hw, EEPROM_INIT_CONTROL2_REG, + 1, &eeprom_data); + if (ret_val) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + if ((eeprom_data & EEPROM_WORD0F_PAUSE_MASK) == 0) + hw->fc = E1000_FC_NONE; + else if ((eeprom_data & EEPROM_WORD0F_PAUSE_MASK) == + EEPROM_WORD0F_ASM_DIR) + hw->fc = E1000_FC_TX_PAUSE; + else + hw->fc = E1000_FC_FULL; + break; + } + } + + /* We want to save off the original Flow Control configuration just + * in case we get disconnected and then reconnected into a different + * hub or switch with different Flow Control capabilities. + */ + if (hw->mac_type == e1000_82542_rev2_0) + hw->fc &= (~E1000_FC_TX_PAUSE); + + if ((hw->mac_type < e1000_82543) && (hw->report_tx_early == 1)) + hw->fc &= (~E1000_FC_RX_PAUSE); + + hw->original_fc = hw->fc; + + DEBUGOUT1("After fix-ups FlowControl is now = %x\n", hw->fc); + + /* Take the 4 bits from EEPROM word 0x0F that determine the initial + * polarity value for the SW controlled pins, and setup the + * Extended Device Control reg with that info. + * This is needed because one of the SW controlled pins is used for + * signal detection. So this should be done before e1000_setup_pcs_link() + * or e1000_phy_setup() is called. + */ + if (hw->mac_type == e1000_82543) { + ret_val = e1000_read_eeprom(hw, EEPROM_INIT_CONTROL2_REG, + 1, &eeprom_data); + if (ret_val) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + ctrl_ext = ((eeprom_data & EEPROM_WORD0F_SWPDIO_EXT) << + SWDPIO__EXT_SHIFT); + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + } + + /* Call the necessary subroutine to configure the link. */ + ret_val = (hw->media_type == e1000_media_type_copper) ? + e1000_setup_copper_link(hw) : + e1000_setup_fiber_serdes_link(hw); + + /* Initialize the flow control address, type, and PAUSE timer + * registers to their default values. This is done even if flow + * control is disabled, because it does not hurt anything to + * initialize these registers. + */ + DEBUGOUT("Initializing the Flow Control address, type and timer regs\n"); + + /* FCAL/H and FCT are hardcoded to standard values in e1000_ich8lan. */ + if (hw->mac_type != e1000_ich8lan) { + E1000_WRITE_REG(hw, FCT, FLOW_CONTROL_TYPE); + E1000_WRITE_REG(hw, FCAH, FLOW_CONTROL_ADDRESS_HIGH); + E1000_WRITE_REG(hw, FCAL, FLOW_CONTROL_ADDRESS_LOW); + } + + E1000_WRITE_REG(hw, FCTTV, hw->fc_pause_time); + + /* Set the flow control receive threshold registers. Normally, + * these registers will be set to a default threshold that may be + * adjusted later by the driver's runtime code. However, if the + * ability to transmit pause frames in not enabled, then these + * registers will be set to 0. + */ + if (!(hw->fc & E1000_FC_TX_PAUSE)) { + E1000_WRITE_REG(hw, FCRTL, 0); + E1000_WRITE_REG(hw, FCRTH, 0); + } else { + /* We need to set up the Receive Threshold high and low water marks + * as well as (optionally) enabling the transmission of XON frames. + */ + if (hw->fc_send_xon) { + E1000_WRITE_REG(hw, FCRTL, (hw->fc_low_water | E1000_FCRTL_XONE)); + E1000_WRITE_REG(hw, FCRTH, hw->fc_high_water); + } else { + E1000_WRITE_REG(hw, FCRTL, hw->fc_low_water); + E1000_WRITE_REG(hw, FCRTH, hw->fc_high_water); + } + } + return ret_val; +} + +/****************************************************************************** + * Sets up link for a fiber based or serdes based adapter + * + * hw - Struct containing variables accessed by shared code + * + * Manipulates Physical Coding Sublayer functions in order to configure + * link. Assumes the hardware has been previously reset and the transmitter + * and receiver are not enabled. + *****************************************************************************/ +static int32_t +e1000_setup_fiber_serdes_link(struct e1000_hw *hw) +{ + uint32_t ctrl; + uint32_t status; + uint32_t txcw = 0; + uint32_t i; + uint32_t signal = 0; + int32_t ret_val; + + DEBUGFUNC("e1000_setup_fiber_serdes_link"); + + /* On 82571 and 82572 Fiber connections, SerDes loopback mode persists + * until explicitly turned off or a power cycle is performed. A read to + * the register does not indicate its status. Therefore, we ensure + * loopback mode is disabled during initialization. + */ + if (hw->mac_type == e1000_82571 || hw->mac_type == e1000_82572) + E1000_WRITE_REG(hw, SCTL, E1000_DISABLE_SERDES_LOOPBACK); + + /* On adapters with a MAC newer than 82544, SWDP 1 will be + * set when the optics detect a signal. On older adapters, it will be + * cleared when there is a signal. This applies to fiber media only. + * If we're on serdes media, adjust the output amplitude to value + * set in the EEPROM. + */ + ctrl = E1000_READ_REG(hw, CTRL); + if (hw->media_type == e1000_media_type_fiber) + signal = (hw->mac_type > e1000_82544) ? E1000_CTRL_SWDPIN1 : 0; + + ret_val = e1000_adjust_serdes_amplitude(hw); + if (ret_val) + return ret_val; + + /* Take the link out of reset */ + ctrl &= ~(E1000_CTRL_LRST); + + /* Adjust VCO speed to improve BER performance */ + ret_val = e1000_set_vco_speed(hw); + if (ret_val) + return ret_val; + + e1000_config_collision_dist(hw); + + /* Check for a software override of the flow control settings, and setup + * the device accordingly. If auto-negotiation is enabled, then software + * will have to set the "PAUSE" bits to the correct value in the Tranmsit + * Config Word Register (TXCW) and re-start auto-negotiation. However, if + * auto-negotiation is disabled, then software will have to manually + * configure the two flow control enable bits in the CTRL register. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames, but + * not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames but we do + * not support receiving pause frames). + * 3: Both Rx and TX flow control (symmetric) are enabled. + */ + switch (hw->fc) { + case E1000_FC_NONE: + /* Flow control is completely disabled by a software over-ride. */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD); + break; + case E1000_FC_RX_PAUSE: + /* RX Flow control is enabled and TX Flow control is disabled by a + * software over-ride. Since there really isn't a way to advertise + * that we are capable of RX Pause ONLY, we will advertise that we + * support both symmetric and asymmetric RX PAUSE. Later, we will + * disable the adapter's ability to send PAUSE frames. + */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); + break; + case E1000_FC_TX_PAUSE: + /* TX Flow control is enabled, and RX Flow control is disabled, by a + * software over-ride. + */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_ASM_DIR); + break; + case E1000_FC_FULL: + /* Flow control (both RX and TX) is enabled by a software over-ride. */ + txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + return -E1000_ERR_CONFIG; + break; + } + + /* Since auto-negotiation is enabled, take the link out of reset (the link + * will be in reset, because we previously reset the chip). This will + * restart auto-negotiation. If auto-neogtiation is successful then the + * link-up status bit will be set and the flow control enable bits (RFCE + * and TFCE) will be set according to their negotiated value. + */ + DEBUGOUT("Auto-negotiation enabled\n"); + + E1000_WRITE_REG(hw, TXCW, txcw); + E1000_WRITE_REG(hw, CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + + hw->txcw = txcw; + msleep(1); + + /* If we have a signal (the cable is plugged in) then poll for a "Link-Up" + * indication in the Device Status Register. Time-out if a link isn't + * seen in 500 milliseconds seconds (Auto-negotiation should complete in + * less than 500 milliseconds even if the other end is doing it in SW). + * For internal serdes, we just assume a signal is present, then poll. + */ + if (hw->media_type == e1000_media_type_internal_serdes || + (E1000_READ_REG(hw, CTRL) & E1000_CTRL_SWDPIN1) == signal) { + DEBUGOUT("Looking for Link\n"); + for (i = 0; i < (LINK_UP_TIMEOUT / 10); i++) { + msleep(10); + status = E1000_READ_REG(hw, STATUS); + if (status & E1000_STATUS_LU) break; + } + if (i == (LINK_UP_TIMEOUT / 10)) { + DEBUGOUT("Never got a valid link from auto-neg!!!\n"); + hw->autoneg_failed = 1; + /* AutoNeg failed to achieve a link, so we'll call + * e1000_check_for_link. This routine will force the link up if + * we detect a signal. This will allow us to communicate with + * non-autonegotiating link partners. + */ + ret_val = e1000_check_for_link(hw); + if (ret_val) { + DEBUGOUT("Error while checking for link\n"); + return ret_val; + } + hw->autoneg_failed = 0; + } else { + hw->autoneg_failed = 0; + DEBUGOUT("Valid Link Found\n"); + } + } else { + DEBUGOUT("No Signal Detected\n"); + } + return E1000_SUCCESS; +} + +/****************************************************************************** +* Make sure we have a valid PHY and change PHY mode before link setup. +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +e1000_copper_link_preconfig(struct e1000_hw *hw) +{ + uint32_t ctrl; + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("e1000_copper_link_preconfig"); + + ctrl = E1000_READ_REG(hw, CTRL); + /* With 82543, we need to force speed and duplex on the MAC equal to what + * the PHY speed and duplex configuration is. In addition, we need to + * perform a hardware reset on the PHY to take it out of reset. + */ + if (hw->mac_type > e1000_82543) { + ctrl |= E1000_CTRL_SLU; + ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + E1000_WRITE_REG(hw, CTRL, ctrl); + } else { + ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX | E1000_CTRL_SLU); + E1000_WRITE_REG(hw, CTRL, ctrl); + ret_val = e1000_phy_hw_reset(hw); + if (ret_val) + return ret_val; + } + + /* Make sure we have a valid PHY */ + ret_val = e1000_detect_gig_phy(hw); + if (ret_val) { + DEBUGOUT("Error, did not detect valid phy.\n"); + return ret_val; + } + DEBUGOUT1("Phy ID = %#08x \n", hw->phy_id); + + /* Set PHY to class A mode (if necessary) */ + ret_val = e1000_set_phy_mode(hw); + if (ret_val) + return ret_val; + + if ((hw->mac_type == e1000_82545_rev_3) || + (hw->mac_type == e1000_82546_rev_3)) { + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + phy_data |= 0x00000008; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + } + + if (hw->mac_type <= e1000_82543 || + hw->mac_type == e1000_82541 || hw->mac_type == e1000_82547 || + hw->mac_type == e1000_82541_rev_2 || hw->mac_type == e1000_82547_rev_2) + hw->phy_reset_disable = FALSE; + + return E1000_SUCCESS; +} + + +/******************************************************************** +* Copper link setup for e1000_phy_igp series. +* +* hw - Struct containing variables accessed by shared code +*********************************************************************/ +static int32_t +e1000_copper_link_igp_setup(struct e1000_hw *hw) +{ + uint32_t led_ctrl; + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("e1000_copper_link_igp_setup"); + + if (hw->phy_reset_disable) + return E1000_SUCCESS; + + ret_val = e1000_phy_reset(hw); + if (ret_val) { + DEBUGOUT("Error Resetting the PHY\n"); + return ret_val; + } + + /* Wait 15ms for MAC to configure PHY from eeprom settings */ + msleep(15); + if (hw->mac_type != e1000_ich8lan) { + /* Configure activity LED after PHY reset */ + led_ctrl = E1000_READ_REG(hw, LEDCTL); + led_ctrl &= IGP_ACTIVITY_LED_MASK; + led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); + E1000_WRITE_REG(hw, LEDCTL, led_ctrl); + } + + /* The NVM settings will configure LPLU in D3 for IGP2 and IGP3 PHYs */ + if (hw->phy_type == e1000_phy_igp) { + /* disable lplu d3 during driver init */ + ret_val = e1000_set_d3_lplu_state(hw, FALSE); + if (ret_val) { + DEBUGOUT("Error Disabling LPLU D3\n"); + return ret_val; + } + } + + /* disable lplu d0 during driver init */ + ret_val = e1000_set_d0_lplu_state(hw, FALSE); + if (ret_val) { + DEBUGOUT("Error Disabling LPLU D0\n"); + return ret_val; + } + /* Configure mdi-mdix settings */ + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data); + if (ret_val) + return ret_val; + + if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) { + hw->dsp_config_state = e1000_dsp_config_disabled; + /* Force MDI for earlier revs of the IGP PHY */ + phy_data &= ~(IGP01E1000_PSCR_AUTO_MDIX | IGP01E1000_PSCR_FORCE_MDI_MDIX); + hw->mdix = 1; + + } else { + hw->dsp_config_state = e1000_dsp_config_enabled; + phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX; + + switch (hw->mdix) { + case 1: + phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX; + break; + case 2: + phy_data |= IGP01E1000_PSCR_FORCE_MDI_MDIX; + break; + case 0: + default: + phy_data |= IGP01E1000_PSCR_AUTO_MDIX; + break; + } + } + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, phy_data); + if (ret_val) + return ret_val; + + /* set auto-master slave resolution settings */ + if (hw->autoneg) { + e1000_ms_type phy_ms_setting = hw->master_slave; + + if (hw->ffe_config_state == e1000_ffe_config_active) + hw->ffe_config_state = e1000_ffe_config_enabled; + + if (hw->dsp_config_state == e1000_dsp_config_activated) + hw->dsp_config_state = e1000_dsp_config_enabled; + + /* when autonegotiation advertisment is only 1000Mbps then we + * should disable SmartSpeed and enable Auto MasterSlave + * resolution as hardware default. */ + if (hw->autoneg_advertised == ADVERTISE_1000_FULL) { + /* Disable SmartSpeed */ + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + &phy_data); + if (ret_val) + return ret_val; + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + phy_data); + if (ret_val) + return ret_val; + /* Set auto Master/Slave resolution process */ + ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &phy_data); + if (ret_val) + return ret_val; + phy_data &= ~CR_1000T_MS_ENABLE; + ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, phy_data); + if (ret_val) + return ret_val; + } + + ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &phy_data); + if (ret_val) + return ret_val; + + /* load defaults for future use */ + hw->original_master_slave = (phy_data & CR_1000T_MS_ENABLE) ? + ((phy_data & CR_1000T_MS_VALUE) ? + e1000_ms_force_master : + e1000_ms_force_slave) : + e1000_ms_auto; + + switch (phy_ms_setting) { + case e1000_ms_force_master: + phy_data |= (CR_1000T_MS_ENABLE | CR_1000T_MS_VALUE); + break; + case e1000_ms_force_slave: + phy_data |= CR_1000T_MS_ENABLE; + phy_data &= ~(CR_1000T_MS_VALUE); + break; + case e1000_ms_auto: + phy_data &= ~CR_1000T_MS_ENABLE; + default: + break; + } + ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, phy_data); + if (ret_val) + return ret_val; + } + + return E1000_SUCCESS; +} + +/******************************************************************** +* Copper link setup for e1000_phy_gg82563 series. +* +* hw - Struct containing variables accessed by shared code +*********************************************************************/ +static int32_t +e1000_copper_link_ggp_setup(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data; + uint32_t reg_data; + + DEBUGFUNC("e1000_copper_link_ggp_setup"); + + if (!hw->phy_reset_disable) { + + /* Enable CRS on TX for half-duplex operation. */ + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_MAC_SPEC_CTRL, + &phy_data); + if (ret_val) + return ret_val; + + phy_data |= GG82563_MSCR_ASSERT_CRS_ON_TX; + /* Use 25MHz for both link down and 1000BASE-T for Tx clock */ + phy_data |= GG82563_MSCR_TX_CLK_1000MBPS_25MHZ; + + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_MAC_SPEC_CTRL, + phy_data); + if (ret_val) + return ret_val; + + /* Options: + * MDI/MDI-X = 0 (default) + * 0 - Auto for all speeds + * 1 - MDI mode + * 2 - MDI-X mode + * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) + */ + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~GG82563_PSCR_CROSSOVER_MODE_MASK; + + switch (hw->mdix) { + case 1: + phy_data |= GG82563_PSCR_CROSSOVER_MODE_MDI; + break; + case 2: + phy_data |= GG82563_PSCR_CROSSOVER_MODE_MDIX; + break; + case 0: + default: + phy_data |= GG82563_PSCR_CROSSOVER_MODE_AUTO; + break; + } + + /* Options: + * disable_polarity_correction = 0 (default) + * Automatic Correction for Reversed Cable Polarity + * 0 - Disabled + * 1 - Enabled + */ + phy_data &= ~GG82563_PSCR_POLARITY_REVERSAL_DISABLE; + if (hw->disable_polarity_correction == 1) + phy_data |= GG82563_PSCR_POLARITY_REVERSAL_DISABLE; + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_SPEC_CTRL, phy_data); + + if (ret_val) + return ret_val; + + /* SW Reset the PHY so all changes take effect */ + ret_val = e1000_phy_reset(hw); + if (ret_val) { + DEBUGOUT("Error Resetting the PHY\n"); + return ret_val; + } + } /* phy_reset_disable */ + + if (hw->mac_type == e1000_80003es2lan) { + /* Bypass RX and TX FIFO's */ + ret_val = e1000_write_kmrn_reg(hw, E1000_KUMCTRLSTA_OFFSET_FIFO_CTRL, + E1000_KUMCTRLSTA_FIFO_CTRL_RX_BYPASS | + E1000_KUMCTRLSTA_FIFO_CTRL_TX_BYPASS); + if (ret_val) + return ret_val; + + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_SPEC_CTRL_2, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~GG82563_PSCR2_REVERSE_AUTO_NEG; + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_SPEC_CTRL_2, phy_data); + + if (ret_val) + return ret_val; + + reg_data = E1000_READ_REG(hw, CTRL_EXT); + reg_data &= ~(E1000_CTRL_EXT_LINK_MODE_MASK); + E1000_WRITE_REG(hw, CTRL_EXT, reg_data); + + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_PWR_MGMT_CTRL, + &phy_data); + if (ret_val) + return ret_val; + + /* Do not init these registers when the HW is in IAMT mode, since the + * firmware will have already initialized them. We only initialize + * them if the HW is not in IAMT mode. + */ + if (e1000_check_mng_mode(hw) == FALSE) { + /* Enable Electrical Idle on the PHY */ + phy_data |= GG82563_PMCR_ENABLE_ELECTRICAL_IDLE; + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_PWR_MGMT_CTRL, + phy_data); + if (ret_val) + return ret_val; + + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, + &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, + phy_data); + + if (ret_val) + return ret_val; + } + + /* Workaround: Disable padding in Kumeran interface in the MAC + * and in the PHY to avoid CRC errors. + */ + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_INBAND_CTRL, + &phy_data); + if (ret_val) + return ret_val; + phy_data |= GG82563_ICR_DIS_PADDING; + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_INBAND_CTRL, + phy_data); + if (ret_val) + return ret_val; + } + + return E1000_SUCCESS; +} + +/******************************************************************** +* Copper link setup for e1000_phy_m88 series. +* +* hw - Struct containing variables accessed by shared code +*********************************************************************/ +static int32_t +e1000_copper_link_mgp_setup(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("e1000_copper_link_mgp_setup"); + + if (hw->phy_reset_disable) + return E1000_SUCCESS; + + /* Enable CRS on TX. This must be set for half-duplex operation. */ + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; + + /* Options: + * MDI/MDI-X = 0 (default) + * 0 - Auto for all speeds + * 1 - MDI mode + * 2 - MDI-X mode + * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) + */ + phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; + + switch (hw->mdix) { + case 1: + phy_data |= M88E1000_PSCR_MDI_MANUAL_MODE; + break; + case 2: + phy_data |= M88E1000_PSCR_MDIX_MANUAL_MODE; + break; + case 3: + phy_data |= M88E1000_PSCR_AUTO_X_1000T; + break; + case 0: + default: + phy_data |= M88E1000_PSCR_AUTO_X_MODE; + break; + } + + /* Options: + * disable_polarity_correction = 0 (default) + * Automatic Correction for Reversed Cable Polarity + * 0 - Disabled + * 1 - Enabled + */ + phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL; + if (hw->disable_polarity_correction == 1) + phy_data |= M88E1000_PSCR_POLARITY_REVERSAL; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + + if (hw->phy_revision < M88E1011_I_REV_4) { + /* Force TX_CLK in the Extended PHY Specific Control Register + * to 25MHz clock. + */ + ret_val = e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= M88E1000_EPSCR_TX_CLK_25; + + if ((hw->phy_revision == E1000_REVISION_2) && + (hw->phy_id == M88E1111_I_PHY_ID)) { + /* Vidalia Phy, set the downshift counter to 5x */ + phy_data &= ~(M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK); + phy_data |= M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X; + ret_val = e1000_write_phy_reg(hw, + M88E1000_EXT_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + } else { + /* Configure Master and Slave downshift values */ + phy_data &= ~(M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK | + M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK); + phy_data |= (M88E1000_EPSCR_MASTER_DOWNSHIFT_1X | + M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X); + ret_val = e1000_write_phy_reg(hw, + M88E1000_EXT_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + } + } + + /* SW Reset the PHY so all changes take effect */ + ret_val = e1000_phy_reset(hw); + if (ret_val) { + DEBUGOUT("Error Resetting the PHY\n"); + return ret_val; + } + + return E1000_SUCCESS; +} + +/******************************************************************** +* Setup auto-negotiation and flow control advertisements, +* and then perform auto-negotiation. +* +* hw - Struct containing variables accessed by shared code +*********************************************************************/ +static int32_t +e1000_copper_link_autoneg(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("e1000_copper_link_autoneg"); + + /* Perform some bounds checking on the hw->autoneg_advertised + * parameter. If this variable is zero, then set it to the default. + */ + hw->autoneg_advertised &= AUTONEG_ADVERTISE_SPEED_DEFAULT; + + /* If autoneg_advertised is zero, we assume it was not defaulted + * by the calling code so we set to advertise full capability. + */ + if (hw->autoneg_advertised == 0) + hw->autoneg_advertised = AUTONEG_ADVERTISE_SPEED_DEFAULT; + + /* IFE phy only supports 10/100 */ + if (hw->phy_type == e1000_phy_ife) + hw->autoneg_advertised &= AUTONEG_ADVERTISE_10_100_ALL; + + DEBUGOUT("Reconfiguring auto-neg advertisement params\n"); + ret_val = e1000_phy_setup_autoneg(hw); + if (ret_val) { + DEBUGOUT("Error Setting up Auto-Negotiation\n"); + return ret_val; + } + DEBUGOUT("Restarting Auto-Neg\n"); + + /* Restart auto-negotiation by setting the Auto Neg Enable bit and + * the Auto Neg Restart bit in the PHY control register. + */ + ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); + ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data); + if (ret_val) + return ret_val; + + /* Does the user want to wait for Auto-Neg to complete here, or + * check at a later time (for example, callback routine). + */ + if (hw->wait_autoneg_complete) { + ret_val = e1000_wait_autoneg(hw); + if (ret_val) { + DEBUGOUT("Error while waiting for autoneg to complete\n"); + return ret_val; + } + } + + hw->get_link_status = TRUE; + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Config the MAC and the PHY after link is up. +* 1) Set up the MAC to the current PHY speed/duplex +* if we are on 82543. If we +* are on newer silicon, we only need to configure +* collision distance in the Transmit Control Register. +* 2) Set up flow control on the MAC to that established with +* the link partner. +* 3) Config DSP to improve Gigabit link quality for some PHY revisions. +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +e1000_copper_link_postconfig(struct e1000_hw *hw) +{ + int32_t ret_val; + DEBUGFUNC("e1000_copper_link_postconfig"); + + if (hw->mac_type >= e1000_82544) { + e1000_config_collision_dist(hw); + } else { + ret_val = e1000_config_mac_to_phy(hw); + if (ret_val) { + DEBUGOUT("Error configuring MAC to PHY settings\n"); + return ret_val; + } + } + ret_val = e1000_config_fc_after_link_up(hw); + if (ret_val) { + DEBUGOUT("Error Configuring Flow Control\n"); + return ret_val; + } + + /* Config DSP to improve Giga link quality */ + if (hw->phy_type == e1000_phy_igp) { + ret_val = e1000_config_dsp_after_link_change(hw, TRUE); + if (ret_val) { + DEBUGOUT("Error Configuring DSP after link up\n"); + return ret_val; + } + } + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Detects which PHY is present and setup the speed and duplex +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +e1000_setup_copper_link(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t i; + uint16_t phy_data; + uint16_t reg_data; + + DEBUGFUNC("e1000_setup_copper_link"); + + switch (hw->mac_type) { + case e1000_80003es2lan: + case e1000_ich8lan: + /* Set the mac to wait the maximum time between each + * iteration and increase the max iterations when + * polling the phy; this fixes erroneous timeouts at 10Mbps. */ + ret_val = e1000_write_kmrn_reg(hw, GG82563_REG(0x34, 4), 0xFFFF); + if (ret_val) + return ret_val; + ret_val = e1000_read_kmrn_reg(hw, GG82563_REG(0x34, 9), ®_data); + if (ret_val) + return ret_val; + reg_data |= 0x3F; + ret_val = e1000_write_kmrn_reg(hw, GG82563_REG(0x34, 9), reg_data); + if (ret_val) + return ret_val; + default: + break; + } + + /* Check if it is a valid PHY and set PHY mode if necessary. */ + ret_val = e1000_copper_link_preconfig(hw); + if (ret_val) + return ret_val; + + switch (hw->mac_type) { + case e1000_80003es2lan: + /* Kumeran registers are written-only */ + reg_data = E1000_KUMCTRLSTA_INB_CTRL_LINK_STATUS_TX_TIMEOUT_DEFAULT; + reg_data |= E1000_KUMCTRLSTA_INB_CTRL_DIS_PADDING; + ret_val = e1000_write_kmrn_reg(hw, E1000_KUMCTRLSTA_OFFSET_INB_CTRL, + reg_data); + if (ret_val) + return ret_val; + break; + default: + break; + } + + if (hw->phy_type == e1000_phy_igp || + hw->phy_type == e1000_phy_igp_3 || + hw->phy_type == e1000_phy_igp_2) { + ret_val = e1000_copper_link_igp_setup(hw); + if (ret_val) + return ret_val; + } else if (hw->phy_type == e1000_phy_m88) { + ret_val = e1000_copper_link_mgp_setup(hw); + if (ret_val) + return ret_val; + } else if (hw->phy_type == e1000_phy_gg82563) { + ret_val = e1000_copper_link_ggp_setup(hw); + if (ret_val) + return ret_val; + } + + if (hw->autoneg) { + /* Setup autoneg and flow control advertisement + * and perform autonegotiation */ + ret_val = e1000_copper_link_autoneg(hw); + if (ret_val) + return ret_val; + } else { + /* PHY will be set to 10H, 10F, 100H,or 100F + * depending on value from forced_speed_duplex. */ + DEBUGOUT("Forcing speed and duplex\n"); + ret_val = e1000_phy_force_speed_duplex(hw); + if (ret_val) { + DEBUGOUT("Error Forcing Speed and Duplex\n"); + return ret_val; + } + } + + /* Check link status. Wait up to 100 microseconds for link to become + * valid. + */ + for (i = 0; i < 10; i++) { + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if (ret_val) + return ret_val; + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if (ret_val) + return ret_val; + + if (phy_data & MII_SR_LINK_STATUS) { + /* Config the MAC and PHY after link is up */ + ret_val = e1000_copper_link_postconfig(hw); + if (ret_val) + return ret_val; + + DEBUGOUT("Valid link established!!!\n"); + return E1000_SUCCESS; + } + udelay(10); + } + + DEBUGOUT("Unable to establish link!!!\n"); + return E1000_SUCCESS; +} + +/****************************************************************************** +* Configure the MAC-to-PHY interface for 10/100Mbps +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +e1000_configure_kmrn_for_10_100(struct e1000_hw *hw, uint16_t duplex) +{ + int32_t ret_val = E1000_SUCCESS; + uint32_t tipg; + uint16_t reg_data; + + DEBUGFUNC("e1000_configure_kmrn_for_10_100"); + + reg_data = E1000_KUMCTRLSTA_HD_CTRL_10_100_DEFAULT; + ret_val = e1000_write_kmrn_reg(hw, E1000_KUMCTRLSTA_OFFSET_HD_CTRL, + reg_data); + if (ret_val) + return ret_val; + + /* Configure Transmit Inter-Packet Gap */ + tipg = E1000_READ_REG(hw, TIPG); + tipg &= ~E1000_TIPG_IPGT_MASK; + tipg |= DEFAULT_80003ES2LAN_TIPG_IPGT_10_100; + E1000_WRITE_REG(hw, TIPG, tipg); + + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data); + + if (ret_val) + return ret_val; + + if (duplex == HALF_DUPLEX) + reg_data |= GG82563_KMCR_PASS_FALSE_CARRIER; + else + reg_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; + + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, reg_data); + + return ret_val; +} + +static int32_t +e1000_configure_kmrn_for_1000(struct e1000_hw *hw) +{ + int32_t ret_val = E1000_SUCCESS; + uint16_t reg_data; + uint32_t tipg; + + DEBUGFUNC("e1000_configure_kmrn_for_1000"); + + reg_data = E1000_KUMCTRLSTA_HD_CTRL_1000_DEFAULT; + ret_val = e1000_write_kmrn_reg(hw, E1000_KUMCTRLSTA_OFFSET_HD_CTRL, + reg_data); + if (ret_val) + return ret_val; + + /* Configure Transmit Inter-Packet Gap */ + tipg = E1000_READ_REG(hw, TIPG); + tipg &= ~E1000_TIPG_IPGT_MASK; + tipg |= DEFAULT_80003ES2LAN_TIPG_IPGT_1000; + E1000_WRITE_REG(hw, TIPG, tipg); + + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data); + + if (ret_val) + return ret_val; + + reg_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, reg_data); + + return ret_val; +} + +/****************************************************************************** +* Configures PHY autoneg and flow control advertisement settings +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +int32_t +e1000_phy_setup_autoneg(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t mii_autoneg_adv_reg; + uint16_t mii_1000t_ctrl_reg; + + DEBUGFUNC("e1000_phy_setup_autoneg"); + + /* Read the MII Auto-Neg Advertisement Register (Address 4). */ + ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg); + if (ret_val) + return ret_val; + + if (hw->phy_type != e1000_phy_ife) { + /* Read the MII 1000Base-T Control Register (Address 9). */ + ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &mii_1000t_ctrl_reg); + if (ret_val) + return ret_val; + } else + mii_1000t_ctrl_reg=0; + + /* Need to parse both autoneg_advertised and fc and set up + * the appropriate PHY registers. First we will parse for + * autoneg_advertised software override. Since we can advertise + * a plethora of combinations, we need to check each bit + * individually. + */ + + /* First we clear all the 10/100 mb speed bits in the Auto-Neg + * Advertisement Register (Address 4) and the 1000 mb speed bits in + * the 1000Base-T Control Register (Address 9). + */ + mii_autoneg_adv_reg &= ~REG4_SPEED_MASK; + mii_1000t_ctrl_reg &= ~REG9_SPEED_MASK; + + DEBUGOUT1("autoneg_advertised %x\n", hw->autoneg_advertised); + + /* Do we want to advertise 10 Mb Half Duplex? */ + if (hw->autoneg_advertised & ADVERTISE_10_HALF) { + DEBUGOUT("Advertise 10mb Half duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS; + } + + /* Do we want to advertise 10 Mb Full Duplex? */ + if (hw->autoneg_advertised & ADVERTISE_10_FULL) { + DEBUGOUT("Advertise 10mb Full duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS; + } + + /* Do we want to advertise 100 Mb Half Duplex? */ + if (hw->autoneg_advertised & ADVERTISE_100_HALF) { + DEBUGOUT("Advertise 100mb Half duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS; + } + + /* Do we want to advertise 100 Mb Full Duplex? */ + if (hw->autoneg_advertised & ADVERTISE_100_FULL) { + DEBUGOUT("Advertise 100mb Full duplex\n"); + mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS; + } + + /* We do not allow the Phy to advertise 1000 Mb Half Duplex */ + if (hw->autoneg_advertised & ADVERTISE_1000_HALF) { + DEBUGOUT("Advertise 1000mb Half duplex requested, request denied!\n"); + } + + /* Do we want to advertise 1000 Mb Full Duplex? */ + if (hw->autoneg_advertised & ADVERTISE_1000_FULL) { + DEBUGOUT("Advertise 1000mb Full duplex\n"); + mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS; + if (hw->phy_type == e1000_phy_ife) { + DEBUGOUT("e1000_phy_ife is a 10/100 PHY. Gigabit speed is not supported.\n"); + } + } + + /* Check for a software override of the flow control settings, and + * setup the PHY advertisement registers accordingly. If + * auto-negotiation is enabled, then software will have to set the + * "PAUSE" bits to the correct value in the Auto-Negotiation + * Advertisement Register (PHY_AUTONEG_ADV) and re-start auto-negotiation. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause frames + * but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames + * but we do not support receiving pause frames). + * 3: Both Rx and TX flow control (symmetric) are enabled. + * other: No software override. The flow control configuration + * in the EEPROM is used. + */ + switch (hw->fc) { + case E1000_FC_NONE: /* 0 */ + /* Flow control (RX & TX) is completely disabled by a + * software over-ride. + */ + mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + case E1000_FC_RX_PAUSE: /* 1 */ + /* RX Flow control is enabled, and TX Flow control is + * disabled, by a software over-ride. + */ + /* Since there really isn't a way to advertise that we are + * capable of RX Pause ONLY, we will advertise that we + * support both symmetric and asymmetric RX PAUSE. Later + * (in e1000_config_fc_after_link_up) we will disable the + *hw's ability to send PAUSE frames. + */ + mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + case E1000_FC_TX_PAUSE: /* 2 */ + /* TX Flow control is enabled, and RX Flow control is + * disabled, by a software over-ride. + */ + mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR; + mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE; + break; + case E1000_FC_FULL: /* 3 */ + /* Flow control (both RX and TX) is enabled by a software + * over-ride. + */ + mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + return -E1000_ERR_CONFIG; + } + + ret_val = e1000_write_phy_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg); + if (ret_val) + return ret_val; + + DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg); + + if (hw->phy_type != e1000_phy_ife) { + ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, mii_1000t_ctrl_reg); + if (ret_val) + return ret_val; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Force PHY speed and duplex settings to hw->forced_speed_duplex +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +e1000_phy_force_speed_duplex(struct e1000_hw *hw) +{ + uint32_t ctrl; + int32_t ret_val; + uint16_t mii_ctrl_reg; + uint16_t mii_status_reg; + uint16_t phy_data; + uint16_t i; + + DEBUGFUNC("e1000_phy_force_speed_duplex"); + + /* Turn off Flow control if we are forcing speed and duplex. */ + hw->fc = E1000_FC_NONE; + + DEBUGOUT1("hw->fc = %d\n", hw->fc); + + /* Read the Device Control Register. */ + ctrl = E1000_READ_REG(hw, CTRL); + + /* Set the bits to Force Speed and Duplex in the Device Ctrl Reg. */ + ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + ctrl &= ~(DEVICE_SPEED_MASK); + + /* Clear the Auto Speed Detect Enable bit. */ + ctrl &= ~E1000_CTRL_ASDE; + + /* Read the MII Control Register. */ + ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &mii_ctrl_reg); + if (ret_val) + return ret_val; + + /* We need to disable autoneg in order to force link and duplex. */ + + mii_ctrl_reg &= ~MII_CR_AUTO_NEG_EN; + + /* Are we forcing Full or Half Duplex? */ + if (hw->forced_speed_duplex == e1000_100_full || + hw->forced_speed_duplex == e1000_10_full) { + /* We want to force full duplex so we SET the full duplex bits in the + * Device and MII Control Registers. + */ + ctrl |= E1000_CTRL_FD; + mii_ctrl_reg |= MII_CR_FULL_DUPLEX; + DEBUGOUT("Full Duplex\n"); + } else { + /* We want to force half duplex so we CLEAR the full duplex bits in + * the Device and MII Control Registers. + */ + ctrl &= ~E1000_CTRL_FD; + mii_ctrl_reg &= ~MII_CR_FULL_DUPLEX; + DEBUGOUT("Half Duplex\n"); + } + + /* Are we forcing 100Mbps??? */ + if (hw->forced_speed_duplex == e1000_100_full || + hw->forced_speed_duplex == e1000_100_half) { + /* Set the 100Mb bit and turn off the 1000Mb and 10Mb bits. */ + ctrl |= E1000_CTRL_SPD_100; + mii_ctrl_reg |= MII_CR_SPEED_100; + mii_ctrl_reg &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_10); + DEBUGOUT("Forcing 100mb "); + } else { + /* Set the 10Mb bit and turn off the 1000Mb and 100Mb bits. */ + ctrl &= ~(E1000_CTRL_SPD_1000 | E1000_CTRL_SPD_100); + mii_ctrl_reg |= MII_CR_SPEED_10; + mii_ctrl_reg &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_100); + DEBUGOUT("Forcing 10mb "); + } + + e1000_config_collision_dist(hw); + + /* Write the configured values back to the Device Control Reg. */ + E1000_WRITE_REG(hw, CTRL, ctrl); + + if ((hw->phy_type == e1000_phy_m88) || + (hw->phy_type == e1000_phy_gg82563)) { + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + /* Clear Auto-Crossover to force MDI manually. M88E1000 requires MDI + * forced whenever speed are duplex are forced. + */ + phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + + DEBUGOUT1("M88E1000 PSCR: %x \n", phy_data); + + /* Need to reset the PHY or these changes will be ignored */ + mii_ctrl_reg |= MII_CR_RESET; + + /* Disable MDI-X support for 10/100 */ + } else if (hw->phy_type == e1000_phy_ife) { + ret_val = e1000_read_phy_reg(hw, IFE_PHY_MDIX_CONTROL, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IFE_PMC_AUTO_MDIX; + phy_data &= ~IFE_PMC_FORCE_MDIX; + + ret_val = e1000_write_phy_reg(hw, IFE_PHY_MDIX_CONTROL, phy_data); + if (ret_val) + return ret_val; + + } else { + /* Clear Auto-Crossover to force MDI manually. IGP requires MDI + * forced whenever speed or duplex are forced. + */ + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX; + phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX; + + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, phy_data); + if (ret_val) + return ret_val; + } + + /* Write back the modified PHY MII control register. */ + ret_val = e1000_write_phy_reg(hw, PHY_CTRL, mii_ctrl_reg); + if (ret_val) + return ret_val; + + udelay(1); + + /* The wait_autoneg_complete flag may be a little misleading here. + * Since we are forcing speed and duplex, Auto-Neg is not enabled. + * But we do want to delay for a period while forcing only so we + * don't generate false No Link messages. So we will wait here + * only if the user has set wait_autoneg_complete to 1, which is + * the default. + */ + if (hw->wait_autoneg_complete) { + /* We will wait for autoneg to complete. */ + DEBUGOUT("Waiting for forced speed/duplex link.\n"); + mii_status_reg = 0; + + /* We will wait for autoneg to complete or 4.5 seconds to expire. */ + for (i = PHY_FORCE_TIME; i > 0; i--) { + /* Read the MII Status Register and wait for Auto-Neg Complete bit + * to be set. + */ + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + + if (mii_status_reg & MII_SR_LINK_STATUS) break; + msleep(100); + } + if ((i == 0) && + ((hw->phy_type == e1000_phy_m88) || + (hw->phy_type == e1000_phy_gg82563))) { + /* We didn't get link. Reset the DSP and wait again for link. */ + ret_val = e1000_phy_reset_dsp(hw); + if (ret_val) { + DEBUGOUT("Error Resetting PHY DSP\n"); + return ret_val; + } + } + /* This loop will early-out if the link condition has been met. */ + for (i = PHY_FORCE_TIME; i > 0; i--) { + if (mii_status_reg & MII_SR_LINK_STATUS) break; + msleep(100); + /* Read the MII Status Register and wait for Auto-Neg Complete bit + * to be set. + */ + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + } + } + + if (hw->phy_type == e1000_phy_m88) { + /* Because we reset the PHY above, we need to re-force TX_CLK in the + * Extended PHY Specific Control Register to 25MHz clock. This value + * defaults back to a 2.5MHz clock when the PHY is reset. + */ + ret_val = e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= M88E1000_EPSCR_TX_CLK_25; + ret_val = e1000_write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + + /* In addition, because of the s/w reset above, we need to enable CRS on + * TX. This must be set for both full and half duplex operation. + */ + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + + if ((hw->mac_type == e1000_82544 || hw->mac_type == e1000_82543) && + (!hw->autoneg) && (hw->forced_speed_duplex == e1000_10_full || + hw->forced_speed_duplex == e1000_10_half)) { + ret_val = e1000_polarity_reversal_workaround(hw); + if (ret_val) + return ret_val; + } + } else if (hw->phy_type == e1000_phy_gg82563) { + /* The TX_CLK of the Extended PHY Specific Control Register defaults + * to 2.5MHz on a reset. We need to re-force it back to 25MHz, if + * we're not in a forced 10/duplex configuration. */ + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_MAC_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~GG82563_MSCR_TX_CLK_MASK; + if ((hw->forced_speed_duplex == e1000_10_full) || + (hw->forced_speed_duplex == e1000_10_half)) + phy_data |= GG82563_MSCR_TX_CLK_10MBPS_2_5MHZ; + else + phy_data |= GG82563_MSCR_TX_CLK_100MBPS_25MHZ; + + /* Also due to the reset, we need to enable CRS on Tx. */ + phy_data |= GG82563_MSCR_ASSERT_CRS_ON_TX; + + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_MAC_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + } + return E1000_SUCCESS; +} + +/****************************************************************************** +* Sets the collision distance in the Transmit Control register +* +* hw - Struct containing variables accessed by shared code +* +* Link should have been established previously. Reads the speed and duplex +* information from the Device Status register. +******************************************************************************/ +void +e1000_config_collision_dist(struct e1000_hw *hw) +{ + uint32_t tctl, coll_dist; + + DEBUGFUNC("e1000_config_collision_dist"); + + if (hw->mac_type < e1000_82543) + coll_dist = E1000_COLLISION_DISTANCE_82542; + else + coll_dist = E1000_COLLISION_DISTANCE; + + tctl = E1000_READ_REG(hw, TCTL); + + tctl &= ~E1000_TCTL_COLD; + tctl |= coll_dist << E1000_COLD_SHIFT; + + E1000_WRITE_REG(hw, TCTL, tctl); + E1000_WRITE_FLUSH(hw); +} + +/****************************************************************************** +* Sets MAC speed and duplex settings to reflect the those in the PHY +* +* hw - Struct containing variables accessed by shared code +* mii_reg - data to write to the MII control register +* +* The contents of the PHY register containing the needed information need to +* be passed in. +******************************************************************************/ +static int32_t +e1000_config_mac_to_phy(struct e1000_hw *hw) +{ + uint32_t ctrl; + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("e1000_config_mac_to_phy"); + + /* 82544 or newer MAC, Auto Speed Detection takes care of + * MAC speed/duplex configuration.*/ + if (hw->mac_type >= e1000_82544) + return E1000_SUCCESS; + + /* Read the Device Control Register and set the bits to Force Speed + * and Duplex. + */ + ctrl = E1000_READ_REG(hw, CTRL); + ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + ctrl &= ~(E1000_CTRL_SPD_SEL | E1000_CTRL_ILOS); + + /* Set up duplex in the Device Control and Transmit Control + * registers depending on negotiated values. + */ + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); + if (ret_val) + return ret_val; + + if (phy_data & M88E1000_PSSR_DPLX) + ctrl |= E1000_CTRL_FD; + else + ctrl &= ~E1000_CTRL_FD; + + e1000_config_collision_dist(hw); + + /* Set up speed in the Device Control register depending on + * negotiated values. + */ + if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) + ctrl |= E1000_CTRL_SPD_1000; + else if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_100MBS) + ctrl |= E1000_CTRL_SPD_100; + + /* Write the configured values back to the Device Control Reg. */ + E1000_WRITE_REG(hw, CTRL, ctrl); + return E1000_SUCCESS; +} + +/****************************************************************************** + * Forces the MAC's flow control settings. + * + * hw - Struct containing variables accessed by shared code + * + * Sets the TFCE and RFCE bits in the device control register to reflect + * the adapter settings. TFCE and RFCE need to be explicitly set by + * software when a Copper PHY is used because autonegotiation is managed + * by the PHY rather than the MAC. Software must also configure these + * bits when link is forced on a fiber connection. + *****************************************************************************/ +int32_t +e1000_force_mac_fc(struct e1000_hw *hw) +{ + uint32_t ctrl; + + DEBUGFUNC("e1000_force_mac_fc"); + + /* Get the current configuration of the Device Control Register */ + ctrl = E1000_READ_REG(hw, CTRL); + + /* Because we didn't get link via the internal auto-negotiation + * mechanism (we either forced link or we got link via PHY + * auto-neg), we have to manually enable/disable transmit an + * receive flow control. + * + * The "Case" statement below enables/disable flow control + * according to the "hw->fc" parameter. + * + * The possible values of the "fc" parameter are: + * 0: Flow control is completely disabled + * 1: Rx flow control is enabled (we can receive pause + * frames but not send pause frames). + * 2: Tx flow control is enabled (we can send pause frames + * frames but we do not receive pause frames). + * 3: Both Rx and TX flow control (symmetric) is enabled. + * other: No other values should be possible at this point. + */ + + switch (hw->fc) { + case E1000_FC_NONE: + ctrl &= (~(E1000_CTRL_TFCE | E1000_CTRL_RFCE)); + break; + case E1000_FC_RX_PAUSE: + ctrl &= (~E1000_CTRL_TFCE); + ctrl |= E1000_CTRL_RFCE; + break; + case E1000_FC_TX_PAUSE: + ctrl &= (~E1000_CTRL_RFCE); + ctrl |= E1000_CTRL_TFCE; + break; + case E1000_FC_FULL: + ctrl |= (E1000_CTRL_TFCE | E1000_CTRL_RFCE); + break; + default: + DEBUGOUT("Flow control param set incorrectly\n"); + return -E1000_ERR_CONFIG; + } + + /* Disable TX Flow Control for 82542 (rev 2.0) */ + if (hw->mac_type == e1000_82542_rev2_0) + ctrl &= (~E1000_CTRL_TFCE); + + E1000_WRITE_REG(hw, CTRL, ctrl); + return E1000_SUCCESS; +} + +/****************************************************************************** + * Configures flow control settings after link is established + * + * hw - Struct containing variables accessed by shared code + * + * Should be called immediately after a valid link has been established. + * Forces MAC flow control settings if link was forced. When in MII/GMII mode + * and autonegotiation is enabled, the MAC flow control settings will be set + * based on the flow control negotiated by the PHY. In TBI mode, the TFCE + * and RFCE bits will be automaticaly set to the negotiated flow control mode. + *****************************************************************************/ +static int32_t +e1000_config_fc_after_link_up(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t mii_status_reg; + uint16_t mii_nway_adv_reg; + uint16_t mii_nway_lp_ability_reg; + uint16_t speed; + uint16_t duplex; + + DEBUGFUNC("e1000_config_fc_after_link_up"); + + /* Check for the case where we have fiber media and auto-neg failed + * so we had to force link. In this case, we need to force the + * configuration of the MAC to match the "fc" parameter. + */ + if (((hw->media_type == e1000_media_type_fiber) && (hw->autoneg_failed)) || + ((hw->media_type == e1000_media_type_internal_serdes) && + (hw->autoneg_failed)) || + ((hw->media_type == e1000_media_type_copper) && (!hw->autoneg))) { + ret_val = e1000_force_mac_fc(hw); + if (ret_val) { + DEBUGOUT("Error forcing flow control settings\n"); + return ret_val; + } + } + + /* Check for the case where we have copper media and auto-neg is + * enabled. In this case, we need to check and see if Auto-Neg + * has completed, and if so, how the PHY and link partner has + * flow control configured. + */ + if ((hw->media_type == e1000_media_type_copper) && hw->autoneg) { + /* Read the MII Status Register and check to see if AutoNeg + * has completed. We read this twice because this reg has + * some "sticky" (latched) bits. + */ + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + + if (mii_status_reg & MII_SR_AUTONEG_COMPLETE) { + /* The AutoNeg process has completed, so we now need to + * read both the Auto Negotiation Advertisement Register + * (Address 4) and the Auto_Negotiation Base Page Ability + * Register (Address 5) to determine how flow control was + * negotiated. + */ + ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV, + &mii_nway_adv_reg); + if (ret_val) + return ret_val; + ret_val = e1000_read_phy_reg(hw, PHY_LP_ABILITY, + &mii_nway_lp_ability_reg); + if (ret_val) + return ret_val; + + /* Two bits in the Auto Negotiation Advertisement Register + * (Address 4) and two bits in the Auto Negotiation Base + * Page Ability Register (Address 5) determine flow control + * for both the PHY and the link partner. The following + * table, taken out of the IEEE 802.3ab/D6.0 dated March 25, + * 1999, describes these PAUSE resolution bits and how flow + * control is determined based upon these settings. + * NOTE: DC = Don't Care + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution + *-------|---------|-------|---------|-------------------- + * 0 | 0 | DC | DC | E1000_FC_NONE + * 0 | 1 | 0 | DC | E1000_FC_NONE + * 0 | 1 | 1 | 0 | E1000_FC_NONE + * 0 | 1 | 1 | 1 | E1000_FC_TX_PAUSE + * 1 | 0 | 0 | DC | E1000_FC_NONE + * 1 | DC | 1 | DC | E1000_FC_FULL + * 1 | 1 | 0 | 0 | E1000_FC_NONE + * 1 | 1 | 0 | 1 | E1000_FC_RX_PAUSE + * + */ + /* Are both PAUSE bits set to 1? If so, this implies + * Symmetric Flow Control is enabled at both ends. The + * ASM_DIR bits are irrelevant per the spec. + * + * For Symmetric Flow Control: + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | DC | 1 | DC | E1000_FC_FULL + * + */ + if ((mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE)) { + /* Now we need to check if the user selected RX ONLY + * of pause frames. In this case, we had to advertise + * FULL flow control because we could not advertise RX + * ONLY. Hence, we must now check to see if we need to + * turn OFF the TRANSMISSION of PAUSE frames. + */ + if (hw->original_fc == E1000_FC_FULL) { + hw->fc = E1000_FC_FULL; + DEBUGOUT("Flow Control = FULL.\n"); + } else { + hw->fc = E1000_FC_RX_PAUSE; + DEBUGOUT("Flow Control = RX PAUSE frames only.\n"); + } + } + /* For receiving PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 0 | 1 | 1 | 1 | E1000_FC_TX_PAUSE + * + */ + else if (!(mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && + (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) { + hw->fc = E1000_FC_TX_PAUSE; + DEBUGOUT("Flow Control = TX PAUSE frames only.\n"); + } + /* For transmitting PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | 1 | 0 | 1 | E1000_FC_RX_PAUSE + * + */ + else if ((mii_nway_adv_reg & NWAY_AR_PAUSE) && + (mii_nway_adv_reg & NWAY_AR_ASM_DIR) && + !(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) && + (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) { + hw->fc = E1000_FC_RX_PAUSE; + DEBUGOUT("Flow Control = RX PAUSE frames only.\n"); + } + /* Per the IEEE spec, at this point flow control should be + * disabled. However, we want to consider that we could + * be connected to a legacy switch that doesn't advertise + * desired flow control, but can be forced on the link + * partner. So if we advertised no flow control, that is + * what we will resolve to. If we advertised some kind of + * receive capability (Rx Pause Only or Full Flow Control) + * and the link partner advertised none, we will configure + * ourselves to enable Rx Flow Control only. We can do + * this safely for two reasons: If the link partner really + * didn't want flow control enabled, and we enable Rx, no + * harm done since we won't be receiving any PAUSE frames + * anyway. If the intent on the link partner was to have + * flow control enabled, then by us enabling RX only, we + * can at least receive pause frames and process them. + * This is a good idea because in most cases, since we are + * predominantly a server NIC, more times than not we will + * be asked to delay transmission of packets than asking + * our link partner to pause transmission of frames. + */ + else if ((hw->original_fc == E1000_FC_NONE || + hw->original_fc == E1000_FC_TX_PAUSE) || + hw->fc_strict_ieee) { + hw->fc = E1000_FC_NONE; + DEBUGOUT("Flow Control = NONE.\n"); + } else { + hw->fc = E1000_FC_RX_PAUSE; + DEBUGOUT("Flow Control = RX PAUSE frames only.\n"); + } + + /* Now we need to do one last check... If we auto- + * negotiated to HALF DUPLEX, flow control should not be + * enabled per IEEE 802.3 spec. + */ + ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex); + if (ret_val) { + DEBUGOUT("Error getting link speed and duplex\n"); + return ret_val; + } + + if (duplex == HALF_DUPLEX) + hw->fc = E1000_FC_NONE; + + /* Now we call a subroutine to actually force the MAC + * controller to use the correct flow control settings. + */ + ret_val = e1000_force_mac_fc(hw); + if (ret_val) { + DEBUGOUT("Error forcing flow control settings\n"); + return ret_val; + } + } else { + DEBUGOUT("Copper PHY and Auto Neg has not completed.\n"); + } + } + return E1000_SUCCESS; +} + +/****************************************************************************** + * Checks to see if the link status of the hardware has changed. + * + * hw - Struct containing variables accessed by shared code + * + * Called by any function that needs to check the link status of the adapter. + *****************************************************************************/ +int32_t +e1000_check_for_link(struct e1000_hw *hw) +{ + uint32_t rxcw = 0; + uint32_t ctrl; + uint32_t status; + uint32_t rctl; + uint32_t icr; + uint32_t signal = 0; + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("e1000_check_for_link"); + + ctrl = E1000_READ_REG(hw, CTRL); + status = E1000_READ_REG(hw, STATUS); + + /* On adapters with a MAC newer than 82544, SW Defineable pin 1 will be + * set when the optics detect a signal. On older adapters, it will be + * cleared when there is a signal. This applies to fiber media only. + */ + if ((hw->media_type == e1000_media_type_fiber) || + (hw->media_type == e1000_media_type_internal_serdes)) { + rxcw = E1000_READ_REG(hw, RXCW); + + if (hw->media_type == e1000_media_type_fiber) { + signal = (hw->mac_type > e1000_82544) ? E1000_CTRL_SWDPIN1 : 0; + if (status & E1000_STATUS_LU) + hw->get_link_status = FALSE; + } + } + + /* If we have a copper PHY then we only want to go out to the PHY + * registers to see if Auto-Neg has completed and/or if our link + * status has changed. The get_link_status flag will be set if we + * receive a Link Status Change interrupt or we have Rx Sequence + * Errors. + */ + if ((hw->media_type == e1000_media_type_copper) && hw->get_link_status) { + /* First we want to see if the MII Status Register reports + * link. If so, then we want to get the current speed/duplex + * of the PHY. + * Read the register twice since the link bit is sticky. + */ + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if (ret_val) + return ret_val; + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if (ret_val) + return ret_val; + + if (phy_data & MII_SR_LINK_STATUS) { + hw->get_link_status = FALSE; + /* Check if there was DownShift, must be checked immediately after + * link-up */ + e1000_check_downshift(hw); + + /* If we are on 82544 or 82543 silicon and speed/duplex + * are forced to 10H or 10F, then we will implement the polarity + * reversal workaround. We disable interrupts first, and upon + * returning, place the devices interrupt state to its previous + * value except for the link status change interrupt which will + * happen due to the execution of this workaround. + */ + + if ((hw->mac_type == e1000_82544 || hw->mac_type == e1000_82543) && + (!hw->autoneg) && + (hw->forced_speed_duplex == e1000_10_full || + hw->forced_speed_duplex == e1000_10_half)) { + E1000_WRITE_REG(hw, IMC, 0xffffffff); + ret_val = e1000_polarity_reversal_workaround(hw); + icr = E1000_READ_REG(hw, ICR); + E1000_WRITE_REG(hw, ICS, (icr & ~E1000_ICS_LSC)); + E1000_WRITE_REG(hw, IMS, IMS_ENABLE_MASK); + } + + } else { + /* No link detected */ + e1000_config_dsp_after_link_change(hw, FALSE); + return 0; + } + + /* If we are forcing speed/duplex, then we simply return since + * we have already determined whether we have link or not. + */ + if (!hw->autoneg) return -E1000_ERR_CONFIG; + + /* optimize the dsp settings for the igp phy */ + e1000_config_dsp_after_link_change(hw, TRUE); + + /* We have a M88E1000 PHY and Auto-Neg is enabled. If we + * have Si on board that is 82544 or newer, Auto + * Speed Detection takes care of MAC speed/duplex + * configuration. So we only need to configure Collision + * Distance in the MAC. Otherwise, we need to force + * speed/duplex on the MAC to the current PHY speed/duplex + * settings. + */ + if (hw->mac_type >= e1000_82544) + e1000_config_collision_dist(hw); + else { + ret_val = e1000_config_mac_to_phy(hw); + if (ret_val) { + DEBUGOUT("Error configuring MAC to PHY settings\n"); + return ret_val; + } + } + + /* Configure Flow Control now that Auto-Neg has completed. First, we + * need to restore the desired flow control settings because we may + * have had to re-autoneg with a different link partner. + */ + ret_val = e1000_config_fc_after_link_up(hw); + if (ret_val) { + DEBUGOUT("Error configuring flow control\n"); + return ret_val; + } + + /* At this point we know that we are on copper and we have + * auto-negotiated link. These are conditions for checking the link + * partner capability register. We use the link speed to determine if + * TBI compatibility needs to be turned on or off. If the link is not + * at gigabit speed, then TBI compatibility is not needed. If we are + * at gigabit speed, we turn on TBI compatibility. + */ + if (hw->tbi_compatibility_en) { + uint16_t speed, duplex; + ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex); + if (ret_val) { + DEBUGOUT("Error getting link speed and duplex\n"); + return ret_val; + } + if (speed != SPEED_1000) { + /* If link speed is not set to gigabit speed, we do not need + * to enable TBI compatibility. + */ + if (hw->tbi_compatibility_on) { + /* If we previously were in the mode, turn it off. */ + rctl = E1000_READ_REG(hw, RCTL); + rctl &= ~E1000_RCTL_SBP; + E1000_WRITE_REG(hw, RCTL, rctl); + hw->tbi_compatibility_on = FALSE; + } + } else { + /* If TBI compatibility is was previously off, turn it on. For + * compatibility with a TBI link partner, we will store bad + * packets. Some frames have an additional byte on the end and + * will look like CRC errors to to the hardware. + */ + if (!hw->tbi_compatibility_on) { + hw->tbi_compatibility_on = TRUE; + rctl = E1000_READ_REG(hw, RCTL); + rctl |= E1000_RCTL_SBP; + E1000_WRITE_REG(hw, RCTL, rctl); + } + } + } + } + /* If we don't have link (auto-negotiation failed or link partner cannot + * auto-negotiate), the cable is plugged in (we have signal), and our + * link partner is not trying to auto-negotiate with us (we are receiving + * idles or data), we need to force link up. We also need to give + * auto-negotiation time to complete, in case the cable was just plugged + * in. The autoneg_failed flag does this. + */ + else if ((((hw->media_type == e1000_media_type_fiber) && + ((ctrl & E1000_CTRL_SWDPIN1) == signal)) || + (hw->media_type == e1000_media_type_internal_serdes)) && + (!(status & E1000_STATUS_LU)) && + (!(rxcw & E1000_RXCW_C))) { + if (hw->autoneg_failed == 0) { + hw->autoneg_failed = 1; + return 0; + } + DEBUGOUT("NOT RXing /C/, disable AutoNeg and force link.\n"); + + /* Disable auto-negotiation in the TXCW register */ + E1000_WRITE_REG(hw, TXCW, (hw->txcw & ~E1000_TXCW_ANE)); + + /* Force link-up and also force full-duplex. */ + ctrl = E1000_READ_REG(hw, CTRL); + ctrl |= (E1000_CTRL_SLU | E1000_CTRL_FD); + E1000_WRITE_REG(hw, CTRL, ctrl); + + /* Configure Flow Control after forcing link up. */ + ret_val = e1000_config_fc_after_link_up(hw); + if (ret_val) { + DEBUGOUT("Error configuring flow control\n"); + return ret_val; + } + } + /* If we are forcing link and we are receiving /C/ ordered sets, re-enable + * auto-negotiation in the TXCW register and disable forced link in the + * Device Control register in an attempt to auto-negotiate with our link + * partner. + */ + else if (((hw->media_type == e1000_media_type_fiber) || + (hw->media_type == e1000_media_type_internal_serdes)) && + (ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) { + DEBUGOUT("RXing /C/, enable AutoNeg and stop forcing link.\n"); + E1000_WRITE_REG(hw, TXCW, hw->txcw); + E1000_WRITE_REG(hw, CTRL, (ctrl & ~E1000_CTRL_SLU)); + + hw->serdes_link_down = FALSE; + } + /* If we force link for non-auto-negotiation switch, check link status + * based on MAC synchronization for internal serdes media type. + */ + else if ((hw->media_type == e1000_media_type_internal_serdes) && + !(E1000_TXCW_ANE & E1000_READ_REG(hw, TXCW))) { + /* SYNCH bit and IV bit are sticky. */ + udelay(10); + if (E1000_RXCW_SYNCH & E1000_READ_REG(hw, RXCW)) { + if (!(rxcw & E1000_RXCW_IV)) { + hw->serdes_link_down = FALSE; + DEBUGOUT("SERDES: Link is up.\n"); + } + } else { + hw->serdes_link_down = TRUE; + DEBUGOUT("SERDES: Link is down.\n"); + } + } + if ((hw->media_type == e1000_media_type_internal_serdes) && + (E1000_TXCW_ANE & E1000_READ_REG(hw, TXCW))) { + hw->serdes_link_down = !(E1000_STATUS_LU & E1000_READ_REG(hw, STATUS)); + } + return E1000_SUCCESS; +} + +/****************************************************************************** + * Detects the current speed and duplex settings of the hardware. + * + * hw - Struct containing variables accessed by shared code + * speed - Speed of the connection + * duplex - Duplex setting of the connection + *****************************************************************************/ +int32_t +e1000_get_speed_and_duplex(struct e1000_hw *hw, + uint16_t *speed, + uint16_t *duplex) +{ + uint32_t status; + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("e1000_get_speed_and_duplex"); + + if (hw->mac_type >= e1000_82543) { + status = E1000_READ_REG(hw, STATUS); + if (status & E1000_STATUS_SPEED_1000) { + *speed = SPEED_1000; + DEBUGOUT("1000 Mbs, "); + } else if (status & E1000_STATUS_SPEED_100) { + *speed = SPEED_100; + DEBUGOUT("100 Mbs, "); + } else { + *speed = SPEED_10; + DEBUGOUT("10 Mbs, "); + } + + if (status & E1000_STATUS_FD) { + *duplex = FULL_DUPLEX; + DEBUGOUT("Full Duplex\n"); + } else { + *duplex = HALF_DUPLEX; + DEBUGOUT(" Half Duplex\n"); + } + } else { + DEBUGOUT("1000 Mbs, Full Duplex\n"); + *speed = SPEED_1000; + *duplex = FULL_DUPLEX; + } + + /* IGP01 PHY may advertise full duplex operation after speed downgrade even + * if it is operating at half duplex. Here we set the duplex settings to + * match the duplex in the link partner's capabilities. + */ + if (hw->phy_type == e1000_phy_igp && hw->speed_downgraded) { + ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_EXP, &phy_data); + if (ret_val) + return ret_val; + + if (!(phy_data & NWAY_ER_LP_NWAY_CAPS)) + *duplex = HALF_DUPLEX; + else { + ret_val = e1000_read_phy_reg(hw, PHY_LP_ABILITY, &phy_data); + if (ret_val) + return ret_val; + if ((*speed == SPEED_100 && !(phy_data & NWAY_LPAR_100TX_FD_CAPS)) || + (*speed == SPEED_10 && !(phy_data & NWAY_LPAR_10T_FD_CAPS))) + *duplex = HALF_DUPLEX; + } + } + + if ((hw->mac_type == e1000_80003es2lan) && + (hw->media_type == e1000_media_type_copper)) { + if (*speed == SPEED_1000) + ret_val = e1000_configure_kmrn_for_1000(hw); + else + ret_val = e1000_configure_kmrn_for_10_100(hw, *duplex); + if (ret_val) + return ret_val; + } + + if ((hw->phy_type == e1000_phy_igp_3) && (*speed == SPEED_1000)) { + ret_val = e1000_kumeran_lock_loss_workaround(hw); + if (ret_val) + return ret_val; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Blocks until autoneg completes or times out (~4.5 seconds) +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +e1000_wait_autoneg(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t i; + uint16_t phy_data; + + DEBUGFUNC("e1000_wait_autoneg"); + DEBUGOUT("Waiting for Auto-Neg to complete.\n"); + + /* We will wait for autoneg to complete or 4.5 seconds to expire. */ + for (i = PHY_AUTO_NEG_TIME; i > 0; i--) { + /* Read the MII Status Register and wait for Auto-Neg + * Complete bit to be set. + */ + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if (ret_val) + return ret_val; + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if (ret_val) + return ret_val; + if (phy_data & MII_SR_AUTONEG_COMPLETE) { + return E1000_SUCCESS; + } + msleep(100); + } + return E1000_SUCCESS; +} + +/****************************************************************************** +* Raises the Management Data Clock +* +* hw - Struct containing variables accessed by shared code +* ctrl - Device control register's current value +******************************************************************************/ +static void +e1000_raise_mdi_clk(struct e1000_hw *hw, + uint32_t *ctrl) +{ + /* Raise the clock input to the Management Data Clock (by setting the MDC + * bit), and then delay 10 microseconds. + */ + E1000_WRITE_REG(hw, CTRL, (*ctrl | E1000_CTRL_MDC)); + E1000_WRITE_FLUSH(hw); + udelay(10); +} + +/****************************************************************************** +* Lowers the Management Data Clock +* +* hw - Struct containing variables accessed by shared code +* ctrl - Device control register's current value +******************************************************************************/ +static void +e1000_lower_mdi_clk(struct e1000_hw *hw, + uint32_t *ctrl) +{ + /* Lower the clock input to the Management Data Clock (by clearing the MDC + * bit), and then delay 10 microseconds. + */ + E1000_WRITE_REG(hw, CTRL, (*ctrl & ~E1000_CTRL_MDC)); + E1000_WRITE_FLUSH(hw); + udelay(10); +} + +/****************************************************************************** +* Shifts data bits out to the PHY +* +* hw - Struct containing variables accessed by shared code +* data - Data to send out to the PHY +* count - Number of bits to shift out +* +* Bits are shifted out in MSB to LSB order. +******************************************************************************/ +static void +e1000_shift_out_mdi_bits(struct e1000_hw *hw, + uint32_t data, + uint16_t count) +{ + uint32_t ctrl; + uint32_t mask; + + /* We need to shift "count" number of bits out to the PHY. So, the value + * in the "data" parameter will be shifted out to the PHY one bit at a + * time. In order to do this, "data" must be broken down into bits. + */ + mask = 0x01; + mask <<= (count - 1); + + ctrl = E1000_READ_REG(hw, CTRL); + + /* Set MDIO_DIR and MDC_DIR direction bits to be used as output pins. */ + ctrl |= (E1000_CTRL_MDIO_DIR | E1000_CTRL_MDC_DIR); + + while (mask) { + /* A "1" is shifted out to the PHY by setting the MDIO bit to "1" and + * then raising and lowering the Management Data Clock. A "0" is + * shifted out to the PHY by setting the MDIO bit to "0" and then + * raising and lowering the clock. + */ + if (data & mask) + ctrl |= E1000_CTRL_MDIO; + else + ctrl &= ~E1000_CTRL_MDIO; + + E1000_WRITE_REG(hw, CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + + udelay(10); + + e1000_raise_mdi_clk(hw, &ctrl); + e1000_lower_mdi_clk(hw, &ctrl); + + mask = mask >> 1; + } +} + +/****************************************************************************** +* Shifts data bits in from the PHY +* +* hw - Struct containing variables accessed by shared code +* +* Bits are shifted in in MSB to LSB order. +******************************************************************************/ +static uint16_t +e1000_shift_in_mdi_bits(struct e1000_hw *hw) +{ + uint32_t ctrl; + uint16_t data = 0; + uint8_t i; + + /* In order to read a register from the PHY, we need to shift in a total + * of 18 bits from the PHY. The first two bit (turnaround) times are used + * to avoid contention on the MDIO pin when a read operation is performed. + * These two bits are ignored by us and thrown away. Bits are "shifted in" + * by raising the input to the Management Data Clock (setting the MDC bit), + * and then reading the value of the MDIO bit. + */ + ctrl = E1000_READ_REG(hw, CTRL); + + /* Clear MDIO_DIR (SWDPIO1) to indicate this bit is to be used as input. */ + ctrl &= ~E1000_CTRL_MDIO_DIR; + ctrl &= ~E1000_CTRL_MDIO; + + E1000_WRITE_REG(hw, CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + + /* Raise and Lower the clock before reading in the data. This accounts for + * the turnaround bits. The first clock occurred when we clocked out the + * last bit of the Register Address. + */ + e1000_raise_mdi_clk(hw, &ctrl); + e1000_lower_mdi_clk(hw, &ctrl); + + for (data = 0, i = 0; i < 16; i++) { + data = data << 1; + e1000_raise_mdi_clk(hw, &ctrl); + ctrl = E1000_READ_REG(hw, CTRL); + /* Check to see if we shifted in a "1". */ + if (ctrl & E1000_CTRL_MDIO) + data |= 1; + e1000_lower_mdi_clk(hw, &ctrl); + } + + e1000_raise_mdi_clk(hw, &ctrl); + e1000_lower_mdi_clk(hw, &ctrl); + + return data; +} + +static int32_t +e1000_swfw_sync_acquire(struct e1000_hw *hw, uint16_t mask) +{ + uint32_t swfw_sync = 0; + uint32_t swmask = mask; + uint32_t fwmask = mask << 16; + int32_t timeout = 200; + + DEBUGFUNC("e1000_swfw_sync_acquire"); + + if (hw->swfwhw_semaphore_present) + return e1000_get_software_flag(hw); + + if (!hw->swfw_sync_present) + return e1000_get_hw_eeprom_semaphore(hw); + + while (timeout) { + if (e1000_get_hw_eeprom_semaphore(hw)) + return -E1000_ERR_SWFW_SYNC; + + swfw_sync = E1000_READ_REG(hw, SW_FW_SYNC); + if (!(swfw_sync & (fwmask | swmask))) { + break; + } + + /* firmware currently using resource (fwmask) */ + /* or other software thread currently using resource (swmask) */ + e1000_put_hw_eeprom_semaphore(hw); + mdelay(5); + timeout--; + } + + if (!timeout) { + DEBUGOUT("Driver can't access resource, SW_FW_SYNC timeout.\n"); + return -E1000_ERR_SWFW_SYNC; + } + + swfw_sync |= swmask; + E1000_WRITE_REG(hw, SW_FW_SYNC, swfw_sync); + + e1000_put_hw_eeprom_semaphore(hw); + return E1000_SUCCESS; +} + +static void +e1000_swfw_sync_release(struct e1000_hw *hw, uint16_t mask) +{ + uint32_t swfw_sync; + uint32_t swmask = mask; + + DEBUGFUNC("e1000_swfw_sync_release"); + + if (hw->swfwhw_semaphore_present) { + e1000_release_software_flag(hw); + return; + } + + if (!hw->swfw_sync_present) { + e1000_put_hw_eeprom_semaphore(hw); + return; + } + + /* if (e1000_get_hw_eeprom_semaphore(hw)) + * return -E1000_ERR_SWFW_SYNC; */ + while (e1000_get_hw_eeprom_semaphore(hw) != E1000_SUCCESS); + /* empty */ + + swfw_sync = E1000_READ_REG(hw, SW_FW_SYNC); + swfw_sync &= ~swmask; + E1000_WRITE_REG(hw, SW_FW_SYNC, swfw_sync); + + e1000_put_hw_eeprom_semaphore(hw); +} + +/***************************************************************************** +* Reads the value from a PHY register, if the value is on a specific non zero +* page, sets the page first. +* hw - Struct containing variables accessed by shared code +* reg_addr - address of the PHY register to read +******************************************************************************/ +int32_t +e1000_read_phy_reg(struct e1000_hw *hw, + uint32_t reg_addr, + uint16_t *phy_data) +{ + uint32_t ret_val; + uint16_t swfw; + + DEBUGFUNC("e1000_read_phy_reg"); + + if ((hw->mac_type == e1000_80003es2lan) && + (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) { + swfw = E1000_SWFW_PHY1_SM; + } else { + swfw = E1000_SWFW_PHY0_SM; + } + if (e1000_swfw_sync_acquire(hw, swfw)) + return -E1000_ERR_SWFW_SYNC; + + if ((hw->phy_type == e1000_phy_igp || + hw->phy_type == e1000_phy_igp_3 || + hw->phy_type == e1000_phy_igp_2) && + (reg_addr > MAX_PHY_MULTI_PAGE_REG)) { + ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT, + (uint16_t)reg_addr); + if (ret_val) { + e1000_swfw_sync_release(hw, swfw); + return ret_val; + } + } else if (hw->phy_type == e1000_phy_gg82563) { + if (((reg_addr & MAX_PHY_REG_ADDRESS) > MAX_PHY_MULTI_PAGE_REG) || + (hw->mac_type == e1000_80003es2lan)) { + /* Select Configuration Page */ + if ((reg_addr & MAX_PHY_REG_ADDRESS) < GG82563_MIN_ALT_REG) { + ret_val = e1000_write_phy_reg_ex(hw, GG82563_PHY_PAGE_SELECT, + (uint16_t)((uint16_t)reg_addr >> GG82563_PAGE_SHIFT)); + } else { + /* Use Alternative Page Select register to access + * registers 30 and 31 + */ + ret_val = e1000_write_phy_reg_ex(hw, + GG82563_PHY_PAGE_SELECT_ALT, + (uint16_t)((uint16_t)reg_addr >> GG82563_PAGE_SHIFT)); + } + + if (ret_val) { + e1000_swfw_sync_release(hw, swfw); + return ret_val; + } + } + } + + ret_val = e1000_read_phy_reg_ex(hw, MAX_PHY_REG_ADDRESS & reg_addr, + phy_data); + + e1000_swfw_sync_release(hw, swfw); + return ret_val; +} + +static int32_t +e1000_read_phy_reg_ex(struct e1000_hw *hw, uint32_t reg_addr, + uint16_t *phy_data) +{ + uint32_t i; + uint32_t mdic = 0; + const uint32_t phy_addr = 1; + + DEBUGFUNC("e1000_read_phy_reg_ex"); + + if (reg_addr > MAX_PHY_REG_ADDRESS) { + DEBUGOUT1("PHY Address %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 +******************************************************************************/ +int32_t +e1000_write_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, + uint16_t phy_data) +{ + uint32_t ret_val; + uint16_t swfw; + + DEBUGFUNC("e1000_write_phy_reg"); + + if ((hw->mac_type == e1000_80003es2lan) && + (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) { + swfw = E1000_SWFW_PHY1_SM; + } else { + swfw = E1000_SWFW_PHY0_SM; + } + if (e1000_swfw_sync_acquire(hw, swfw)) + return -E1000_ERR_SWFW_SYNC; + + if ((hw->phy_type == e1000_phy_igp || + hw->phy_type == e1000_phy_igp_3 || + hw->phy_type == e1000_phy_igp_2) && + (reg_addr > MAX_PHY_MULTI_PAGE_REG)) { + ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT, + (uint16_t)reg_addr); + if (ret_val) { + e1000_swfw_sync_release(hw, swfw); + return ret_val; + } + } else if (hw->phy_type == e1000_phy_gg82563) { + if (((reg_addr & MAX_PHY_REG_ADDRESS) > MAX_PHY_MULTI_PAGE_REG) || + (hw->mac_type == e1000_80003es2lan)) { + /* Select Configuration Page */ + if ((reg_addr & MAX_PHY_REG_ADDRESS) < GG82563_MIN_ALT_REG) { + ret_val = e1000_write_phy_reg_ex(hw, GG82563_PHY_PAGE_SELECT, + (uint16_t)((uint16_t)reg_addr >> GG82563_PAGE_SHIFT)); + } else { + /* Use Alternative Page Select register to access + * registers 30 and 31 + */ + ret_val = e1000_write_phy_reg_ex(hw, + GG82563_PHY_PAGE_SELECT_ALT, + (uint16_t)((uint16_t)reg_addr >> GG82563_PAGE_SHIFT)); + } + + if (ret_val) { + e1000_swfw_sync_release(hw, swfw); + return ret_val; + } + } + } + + ret_val = e1000_write_phy_reg_ex(hw, MAX_PHY_REG_ADDRESS & reg_addr, + phy_data); + + e1000_swfw_sync_release(hw, swfw); + return ret_val; +} + +static int32_t +e1000_write_phy_reg_ex(struct e1000_hw *hw, uint32_t reg_addr, + uint16_t phy_data) +{ + uint32_t i; + uint32_t mdic = 0; + const uint32_t phy_addr = 1; + + DEBUGFUNC("e1000_write_phy_reg_ex"); + + if (reg_addr > MAX_PHY_REG_ADDRESS) { + DEBUGOUT1("PHY Address %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 < 641; i++) { + udelay(5); + mdic = E1000_READ_REG(hw, MDIC); + if (mdic & E1000_MDIC_READY) break; + } + if (!(mdic & E1000_MDIC_READY)) { + DEBUGOUT("MDI Write did not complete\n"); + return -E1000_ERR_PHY; + } + } else { + /* We'll need to use the SW defined pins to shift the write command + * out to the PHY. We first send a preamble to the PHY to signal the + * beginning of the MII instruction. This is done by sending 32 + * consecutive "1" bits. + */ + e1000_shift_out_mdi_bits(hw, PHY_PREAMBLE, PHY_PREAMBLE_SIZE); + + /* Now combine the remaining required fields that will indicate a + * write operation. We use this method instead of calling the + * e1000_shift_out_mdi_bits routine for each field in the command. The + * format of a MII write instruction is as follows: + * . + */ + mdic = ((PHY_TURNAROUND) | (reg_addr << 2) | (phy_addr << 7) | + (PHY_OP_WRITE << 12) | (PHY_SOF << 14)); + mdic <<= 16; + mdic |= (uint32_t) phy_data; + + e1000_shift_out_mdi_bits(hw, mdic, 32); + } + + return E1000_SUCCESS; +} + +static int32_t +e1000_read_kmrn_reg(struct e1000_hw *hw, + uint32_t reg_addr, + uint16_t *data) +{ + uint32_t reg_val; + uint16_t swfw; + DEBUGFUNC("e1000_read_kmrn_reg"); + + if ((hw->mac_type == e1000_80003es2lan) && + (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) { + swfw = E1000_SWFW_PHY1_SM; + } else { + swfw = E1000_SWFW_PHY0_SM; + } + if (e1000_swfw_sync_acquire(hw, swfw)) + return -E1000_ERR_SWFW_SYNC; + + /* Write register address */ + reg_val = ((reg_addr << E1000_KUMCTRLSTA_OFFSET_SHIFT) & + E1000_KUMCTRLSTA_OFFSET) | + E1000_KUMCTRLSTA_REN; + E1000_WRITE_REG(hw, KUMCTRLSTA, reg_val); + udelay(2); + + /* Read the data returned */ + reg_val = E1000_READ_REG(hw, KUMCTRLSTA); + *data = (uint16_t)reg_val; + + e1000_swfw_sync_release(hw, swfw); + return E1000_SUCCESS; +} + +static int32_t +e1000_write_kmrn_reg(struct e1000_hw *hw, + uint32_t reg_addr, + uint16_t data) +{ + uint32_t reg_val; + uint16_t swfw; + DEBUGFUNC("e1000_write_kmrn_reg"); + + if ((hw->mac_type == e1000_80003es2lan) && + (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) { + swfw = E1000_SWFW_PHY1_SM; + } else { + swfw = E1000_SWFW_PHY0_SM; + } + if (e1000_swfw_sync_acquire(hw, swfw)) + return -E1000_ERR_SWFW_SYNC; + + reg_val = ((reg_addr << E1000_KUMCTRLSTA_OFFSET_SHIFT) & + E1000_KUMCTRLSTA_OFFSET) | data; + E1000_WRITE_REG(hw, KUMCTRLSTA, reg_val); + udelay(2); + + e1000_swfw_sync_release(hw, swfw); + return E1000_SUCCESS; +} + +/****************************************************************************** +* Returns the PHY to the power-on reset state +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +int32_t +e1000_phy_hw_reset(struct e1000_hw *hw) +{ + uint32_t ctrl, ctrl_ext; + uint32_t led_ctrl; + int32_t ret_val; + uint16_t swfw; + + DEBUGFUNC("e1000_phy_hw_reset"); + + /* In the case of the phy reset being blocked, it's not an error, we + * simply return success without performing the reset. */ + ret_val = e1000_check_phy_reset_block(hw); + if (ret_val) + return E1000_SUCCESS; + + DEBUGOUT("Resetting Phy...\n"); + + if (hw->mac_type > e1000_82543) { + if ((hw->mac_type == e1000_80003es2lan) && + (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1)) { + swfw = E1000_SWFW_PHY1_SM; + } else { + swfw = E1000_SWFW_PHY0_SM; + } + if (e1000_swfw_sync_acquire(hw, swfw)) { + DEBUGOUT("Unable to acquire swfw sync\n"); + return -E1000_ERR_SWFW_SYNC; + } + /* Read the device control register and assert the E1000_CTRL_PHY_RST + * bit. Then, take it out of reset. + * For pre-e1000_82571 hardware, we delay for 10ms between the assert + * and deassert. For e1000_82571 hardware and later, we instead delay + * for 50us between and 10ms after the deassertion. + */ + ctrl = E1000_READ_REG(hw, CTRL); + E1000_WRITE_REG(hw, CTRL, ctrl | E1000_CTRL_PHY_RST); + E1000_WRITE_FLUSH(hw); + + if (hw->mac_type < e1000_82571) + msleep(10); + else + udelay(100); + + E1000_WRITE_REG(hw, CTRL, ctrl); + E1000_WRITE_FLUSH(hw); + + if (hw->mac_type >= e1000_82571) + mdelay(10); + + e1000_swfw_sync_release(hw, swfw); + } else { + /* Read the Extended Device Control Register, assert the PHY_RESET_DIR + * bit to put the PHY into reset. Then, take it out of reset. + */ + ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_SDP4_DIR; + ctrl_ext &= ~E1000_CTRL_EXT_SDP4_DATA; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + msleep(10); + ctrl_ext |= E1000_CTRL_EXT_SDP4_DATA; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + E1000_WRITE_FLUSH(hw); + } + udelay(150); + + if ((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) { + /* Configure activity LED after PHY reset */ + led_ctrl = E1000_READ_REG(hw, LEDCTL); + led_ctrl &= IGP_ACTIVITY_LED_MASK; + led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); + E1000_WRITE_REG(hw, LEDCTL, led_ctrl); + } + + /* Wait for FW to finish PHY configuration. */ + ret_val = e1000_get_phy_cfg_done(hw); + if (ret_val != E1000_SUCCESS) + return ret_val; + e1000_release_software_semaphore(hw); + + if ((hw->mac_type == e1000_ich8lan) && (hw->phy_type == e1000_phy_igp_3)) + ret_val = e1000_init_lcd_from_nvm(hw); + + return ret_val; +} + +/****************************************************************************** +* Resets the PHY +* +* hw - Struct containing variables accessed by shared code +* +* Sets bit 15 of the MII Control register +******************************************************************************/ +int32_t +e1000_phy_reset(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("e1000_phy_reset"); + + /* In the case of the phy reset being blocked, it's not an error, we + * simply return success without performing the reset. */ + ret_val = e1000_check_phy_reset_block(hw); + if (ret_val) + return E1000_SUCCESS; + + switch (hw->phy_type) { + case e1000_phy_igp: + case e1000_phy_igp_2: + case e1000_phy_igp_3: + case e1000_phy_ife: + ret_val = e1000_phy_hw_reset(hw); + if (ret_val) + return ret_val; + break; + default: + ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= MII_CR_RESET; + ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data); + if (ret_val) + return ret_val; + + udelay(1); + break; + } + + if (hw->phy_type == e1000_phy_igp || hw->phy_type == e1000_phy_igp_2) + e1000_phy_init_script(hw); + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Work-around for 82566 power-down: on D3 entry- +* 1) disable gigabit link +* 2) write VR power-down enable +* 3) read it back +* if successful continue, else issue LCD reset and repeat +* +* hw - struct containing variables accessed by shared code +******************************************************************************/ +void +e1000_phy_powerdown_workaround(struct e1000_hw *hw) +{ + int32_t reg; + uint16_t phy_data; + int32_t retry = 0; + + DEBUGFUNC("e1000_phy_powerdown_workaround"); + + if (hw->phy_type != e1000_phy_igp_3) + return; + + do { + /* Disable link */ + reg = E1000_READ_REG(hw, PHY_CTRL); + E1000_WRITE_REG(hw, PHY_CTRL, reg | E1000_PHY_CTRL_GBE_DISABLE | + E1000_PHY_CTRL_NOND0A_GBE_DISABLE); + + /* Write VR power-down enable - bits 9:8 should be 10b */ + e1000_read_phy_reg(hw, IGP3_VR_CTRL, &phy_data); + phy_data |= (1 << 9); + phy_data &= ~(1 << 8); + e1000_write_phy_reg(hw, IGP3_VR_CTRL, phy_data); + + /* Read it back and test */ + e1000_read_phy_reg(hw, IGP3_VR_CTRL, &phy_data); + if (((phy_data & IGP3_VR_CTRL_MODE_MASK) == IGP3_VR_CTRL_MODE_SHUT) || retry) + break; + + /* Issue PHY reset and repeat at most one more time */ + reg = E1000_READ_REG(hw, CTRL); + E1000_WRITE_REG(hw, CTRL, reg | E1000_CTRL_PHY_RST); + retry++; + } while (retry); + + return; + +} + +/****************************************************************************** +* Work-around for 82566 Kumeran PCS lock loss: +* On link status change (i.e. PCI reset, speed change) and link is up and +* speed is gigabit- +* 0) if workaround is optionally disabled do nothing +* 1) wait 1ms for Kumeran link to come up +* 2) check Kumeran Diagnostic register PCS lock loss bit +* 3) if not set the link is locked (all is good), otherwise... +* 4) reset the PHY +* 5) repeat up to 10 times +* Note: this is only called for IGP3 copper when speed is 1gb. +* +* hw - struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +e1000_kumeran_lock_loss_workaround(struct e1000_hw *hw) +{ + int32_t ret_val; + int32_t reg; + int32_t cnt; + uint16_t phy_data; + + if (hw->kmrn_lock_loss_workaround_disabled) + return E1000_SUCCESS; + + /* Make sure link is up before proceeding. If not just return. + * Attempting this while link is negotiating fouled up link + * stability */ + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + + if (phy_data & MII_SR_LINK_STATUS) { + for (cnt = 0; cnt < 10; cnt++) { + /* read once to clear */ + ret_val = e1000_read_phy_reg(hw, IGP3_KMRN_DIAG, &phy_data); + if (ret_val) + return ret_val; + /* and again to get new status */ + ret_val = e1000_read_phy_reg(hw, IGP3_KMRN_DIAG, &phy_data); + if (ret_val) + return ret_val; + + /* check for PCS lock */ + if (!(phy_data & IGP3_KMRN_DIAG_PCS_LOCK_LOSS)) + return E1000_SUCCESS; + + /* Issue PHY reset */ + e1000_phy_hw_reset(hw); + mdelay(5); + } + /* Disable GigE link negotiation */ + reg = E1000_READ_REG(hw, PHY_CTRL); + E1000_WRITE_REG(hw, PHY_CTRL, reg | E1000_PHY_CTRL_GBE_DISABLE | + E1000_PHY_CTRL_NOND0A_GBE_DISABLE); + + /* unable to acquire PCS lock */ + return E1000_ERR_PHY; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Probes the expected PHY address for known PHY IDs +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +e1000_detect_gig_phy(struct e1000_hw *hw) +{ + int32_t phy_init_status, ret_val; + uint16_t phy_id_high, phy_id_low; + boolean_t match = FALSE; + + DEBUGFUNC("e1000_detect_gig_phy"); + + if (hw->phy_id != 0) + return E1000_SUCCESS; + + /* The 82571 firmware may still be configuring the PHY. In this + * case, we cannot access the PHY until the configuration is done. So + * we explicitly set the PHY values. */ + if (hw->mac_type == e1000_82571 || + hw->mac_type == e1000_82572) { + hw->phy_id = IGP01E1000_I_PHY_ID; + hw->phy_type = e1000_phy_igp_2; + return E1000_SUCCESS; + } + + /* ESB-2 PHY reads require e1000_phy_gg82563 to be set because of a work- + * around that forces PHY page 0 to be set or the reads fail. The rest of + * the code in this routine uses e1000_read_phy_reg to read the PHY ID. + * So for ESB-2 we need to have this set so our reads won't fail. If the + * attached PHY is not a e1000_phy_gg82563, the routines below will figure + * this out as well. */ + if (hw->mac_type == e1000_80003es2lan) + hw->phy_type = e1000_phy_gg82563; + + /* Read the PHY ID Registers to identify which PHY is onboard. */ + ret_val = e1000_read_phy_reg(hw, PHY_ID1, &phy_id_high); + if (ret_val) + return ret_val; + + hw->phy_id = (uint32_t) (phy_id_high << 16); + udelay(20); + ret_val = e1000_read_phy_reg(hw, PHY_ID2, &phy_id_low); + if (ret_val) + return ret_val; + + hw->phy_id |= (uint32_t) (phy_id_low & PHY_REVISION_MASK); + hw->phy_revision = (uint32_t) phy_id_low & ~PHY_REVISION_MASK; + + switch (hw->mac_type) { + case e1000_82543: + if (hw->phy_id == M88E1000_E_PHY_ID) match = TRUE; + break; + case e1000_82544: + if (hw->phy_id == M88E1000_I_PHY_ID) match = TRUE; + break; + case e1000_82540: + case e1000_82545: + case e1000_82545_rev_3: + case e1000_82546: + case e1000_82546_rev_3: + if (hw->phy_id == M88E1011_I_PHY_ID) match = TRUE; + break; + case e1000_82541: + case e1000_82541_rev_2: + case e1000_82547: + case e1000_82547_rev_2: + if (hw->phy_id == IGP01E1000_I_PHY_ID) match = TRUE; + break; + case e1000_82573: + if (hw->phy_id == M88E1111_I_PHY_ID) match = TRUE; + break; + case e1000_80003es2lan: + if (hw->phy_id == GG82563_E_PHY_ID) match = TRUE; + break; + case e1000_ich8lan: + if (hw->phy_id == IGP03E1000_E_PHY_ID) match = TRUE; + if (hw->phy_id == IFE_E_PHY_ID) match = TRUE; + if (hw->phy_id == IFE_PLUS_E_PHY_ID) match = TRUE; + if (hw->phy_id == IFE_C_E_PHY_ID) match = TRUE; + break; + default: + DEBUGOUT1("Invalid MAC type %d\n", hw->mac_type); + return -E1000_ERR_CONFIG; + } + phy_init_status = e1000_set_phy_type(hw); + + if ((match) && (phy_init_status == E1000_SUCCESS)) { + DEBUGOUT1("PHY ID %#08x detected\n", hw->phy_id); + return E1000_SUCCESS; + } + DEBUGOUT1("Invalid PHY ID %#08x\n", hw->phy_id); + return -E1000_ERR_PHY; +} + +/****************************************************************************** +* Resets the PHY's DSP +* +* hw - Struct containing variables accessed by shared code +******************************************************************************/ +static int32_t +e1000_phy_reset_dsp(struct e1000_hw *hw) +{ + int32_t ret_val; + DEBUGFUNC("e1000_phy_reset_dsp"); + + do { + if (hw->phy_type != e1000_phy_gg82563) { + ret_val = e1000_write_phy_reg(hw, 29, 0x001d); + if (ret_val) break; + } + ret_val = e1000_write_phy_reg(hw, 30, 0x00c1); + if (ret_val) break; + ret_val = e1000_write_phy_reg(hw, 30, 0x0000); + if (ret_val) break; + ret_val = E1000_SUCCESS; + } while (0); + + return ret_val; +} + +/****************************************************************************** +* Get PHY information from various PHY registers for igp PHY only. +* +* hw - Struct containing variables accessed by shared code +* phy_info - PHY information structure +******************************************************************************/ +static int32_t +e1000_phy_igp_get_info(struct e1000_hw *hw, + struct e1000_phy_info *phy_info) +{ + int32_t ret_val; + uint16_t phy_data, min_length, max_length, average; + e1000_rev_polarity polarity; + + DEBUGFUNC("e1000_phy_igp_get_info"); + + /* The downshift status is checked only once, after link is established, + * and it stored in the hw->speed_downgraded parameter. */ + phy_info->downshift = (e1000_downshift)hw->speed_downgraded; + + /* IGP01E1000 does not need to support it. */ + phy_info->extended_10bt_distance = e1000_10bt_ext_dist_enable_normal; + + /* IGP01E1000 always correct polarity reversal */ + phy_info->polarity_correction = e1000_polarity_reversal_enabled; + + /* Check polarity status */ + ret_val = e1000_check_polarity(hw, &polarity); + if (ret_val) + return ret_val; + + phy_info->cable_polarity = polarity; + + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, &phy_data); + if (ret_val) + return ret_val; + + phy_info->mdix_mode = (e1000_auto_x_mode)((phy_data & IGP01E1000_PSSR_MDIX) >> + IGP01E1000_PSSR_MDIX_SHIFT); + + if ((phy_data & IGP01E1000_PSSR_SPEED_MASK) == + IGP01E1000_PSSR_SPEED_1000MBPS) { + /* Local/Remote Receiver Information are only valid at 1000 Mbps */ + ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data); + if (ret_val) + return ret_val; + + phy_info->local_rx = ((phy_data & SR_1000T_LOCAL_RX_STATUS) >> + SR_1000T_LOCAL_RX_STATUS_SHIFT) ? + e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok; + phy_info->remote_rx = ((phy_data & SR_1000T_REMOTE_RX_STATUS) >> + SR_1000T_REMOTE_RX_STATUS_SHIFT) ? + e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok; + + /* Get cable length */ + ret_val = e1000_get_cable_length(hw, &min_length, &max_length); + if (ret_val) + return ret_val; + + /* Translate to old method */ + average = (max_length + min_length) / 2; + + if (average <= e1000_igp_cable_length_50) + phy_info->cable_length = e1000_cable_length_50; + else if (average <= e1000_igp_cable_length_80) + phy_info->cable_length = e1000_cable_length_50_80; + else if (average <= e1000_igp_cable_length_110) + phy_info->cable_length = e1000_cable_length_80_110; + else if (average <= e1000_igp_cable_length_140) + phy_info->cable_length = e1000_cable_length_110_140; + else + phy_info->cable_length = e1000_cable_length_140; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Get PHY information from various PHY registers for ife PHY only. +* +* hw - Struct containing variables accessed by shared code +* phy_info - PHY information structure +******************************************************************************/ +static int32_t +e1000_phy_ife_get_info(struct e1000_hw *hw, + struct e1000_phy_info *phy_info) +{ + int32_t ret_val; + uint16_t phy_data; + e1000_rev_polarity polarity; + + DEBUGFUNC("e1000_phy_ife_get_info"); + + phy_info->downshift = (e1000_downshift)hw->speed_downgraded; + phy_info->extended_10bt_distance = e1000_10bt_ext_dist_enable_normal; + + ret_val = e1000_read_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL, &phy_data); + if (ret_val) + return ret_val; + phy_info->polarity_correction = + ((phy_data & IFE_PSC_AUTO_POLARITY_DISABLE) >> + IFE_PSC_AUTO_POLARITY_DISABLE_SHIFT) ? + e1000_polarity_reversal_disabled : e1000_polarity_reversal_enabled; + + if (phy_info->polarity_correction == e1000_polarity_reversal_enabled) { + ret_val = e1000_check_polarity(hw, &polarity); + if (ret_val) + return ret_val; + } else { + /* Polarity is forced. */ + polarity = ((phy_data & IFE_PSC_FORCE_POLARITY) >> + IFE_PSC_FORCE_POLARITY_SHIFT) ? + e1000_rev_polarity_reversed : e1000_rev_polarity_normal; + } + phy_info->cable_polarity = polarity; + + ret_val = e1000_read_phy_reg(hw, IFE_PHY_MDIX_CONTROL, &phy_data); + if (ret_val) + return ret_val; + + phy_info->mdix_mode = (e1000_auto_x_mode) + ((phy_data & (IFE_PMC_AUTO_MDIX | IFE_PMC_FORCE_MDIX)) >> + IFE_PMC_MDIX_MODE_SHIFT); + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Get PHY information from various PHY registers fot m88 PHY only. +* +* hw - Struct containing variables accessed by shared code +* phy_info - PHY information structure +******************************************************************************/ +static int32_t +e1000_phy_m88_get_info(struct e1000_hw *hw, + struct e1000_phy_info *phy_info) +{ + int32_t ret_val; + uint16_t phy_data; + e1000_rev_polarity polarity; + + DEBUGFUNC("e1000_phy_m88_get_info"); + + /* The downshift status is checked only once, after link is established, + * and it stored in the hw->speed_downgraded parameter. */ + phy_info->downshift = (e1000_downshift)hw->speed_downgraded; + + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_info->extended_10bt_distance = + ((phy_data & M88E1000_PSCR_10BT_EXT_DIST_ENABLE) >> + M88E1000_PSCR_10BT_EXT_DIST_ENABLE_SHIFT) ? + e1000_10bt_ext_dist_enable_lower : e1000_10bt_ext_dist_enable_normal; + + phy_info->polarity_correction = + ((phy_data & M88E1000_PSCR_POLARITY_REVERSAL) >> + M88E1000_PSCR_POLARITY_REVERSAL_SHIFT) ? + e1000_polarity_reversal_disabled : e1000_polarity_reversal_enabled; + + /* Check polarity status */ + ret_val = e1000_check_polarity(hw, &polarity); + if (ret_val) + return ret_val; + phy_info->cable_polarity = polarity; + + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); + if (ret_val) + return ret_val; + + phy_info->mdix_mode = (e1000_auto_x_mode)((phy_data & M88E1000_PSSR_MDIX) >> + M88E1000_PSSR_MDIX_SHIFT); + + if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) { + /* Cable Length Estimation and Local/Remote Receiver Information + * are only valid at 1000 Mbps. + */ + if (hw->phy_type != e1000_phy_gg82563) { + phy_info->cable_length = (e1000_cable_length)((phy_data & M88E1000_PSSR_CABLE_LENGTH) >> + M88E1000_PSSR_CABLE_LENGTH_SHIFT); + } else { + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_DSP_DISTANCE, + &phy_data); + if (ret_val) + return ret_val; + + phy_info->cable_length = (e1000_cable_length)(phy_data & GG82563_DSPD_CABLE_LENGTH); + } + + ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data); + if (ret_val) + return ret_val; + + phy_info->local_rx = ((phy_data & SR_1000T_LOCAL_RX_STATUS) >> + SR_1000T_LOCAL_RX_STATUS_SHIFT) ? + e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok; + phy_info->remote_rx = ((phy_data & SR_1000T_REMOTE_RX_STATUS) >> + SR_1000T_REMOTE_RX_STATUS_SHIFT) ? + e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok; + + } + + return E1000_SUCCESS; +} + +/****************************************************************************** +* Get PHY information from various PHY registers +* +* hw - Struct containing variables accessed by shared code +* phy_info - PHY information structure +******************************************************************************/ +int32_t +e1000_phy_get_info(struct e1000_hw *hw, + struct e1000_phy_info *phy_info) +{ + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("e1000_phy_get_info"); + + phy_info->cable_length = e1000_cable_length_undefined; + phy_info->extended_10bt_distance = e1000_10bt_ext_dist_enable_undefined; + phy_info->cable_polarity = e1000_rev_polarity_undefined; + phy_info->downshift = e1000_downshift_undefined; + phy_info->polarity_correction = e1000_polarity_reversal_undefined; + phy_info->mdix_mode = e1000_auto_x_mode_undefined; + phy_info->local_rx = e1000_1000t_rx_status_undefined; + phy_info->remote_rx = e1000_1000t_rx_status_undefined; + + if (hw->media_type != e1000_media_type_copper) { + DEBUGOUT("PHY info is only valid for copper media\n"); + return -E1000_ERR_CONFIG; + } + + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if (ret_val) + return ret_val; + + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if (ret_val) + return ret_val; + + if ((phy_data & MII_SR_LINK_STATUS) != MII_SR_LINK_STATUS) { + DEBUGOUT("PHY info is only valid if link is up\n"); + return -E1000_ERR_CONFIG; + } + + if (hw->phy_type == e1000_phy_igp || + hw->phy_type == e1000_phy_igp_3 || + hw->phy_type == e1000_phy_igp_2) + return e1000_phy_igp_get_info(hw, phy_info); + else if (hw->phy_type == e1000_phy_ife) + return e1000_phy_ife_get_info(hw, phy_info); + else + return e1000_phy_m88_get_info(hw, phy_info); +} + +int32_t +e1000_validate_mdi_setting(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_validate_mdi_settings"); + + if (!hw->autoneg && (hw->mdix == 0 || hw->mdix == 3)) { + DEBUGOUT("Invalid MDI setting detected\n"); + hw->mdix = 1; + return -E1000_ERR_CONFIG; + } + return E1000_SUCCESS; +} + + +/****************************************************************************** + * Sets up eeprom variables in the hw struct. Must be called after mac_type + * is configured. Additionally, if this is ICH8, the flash controller GbE + * registers must be mapped, or this will crash. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +e1000_init_eeprom_params(struct e1000_hw *hw) +{ + struct e1000_eeprom_info *eeprom = &hw->eeprom; + uint32_t eecd = E1000_READ_REG(hw, EECD); + int32_t ret_val = E1000_SUCCESS; + uint16_t eeprom_size; + + DEBUGFUNC("e1000_init_eeprom_params"); + + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + case e1000_82543: + case e1000_82544: + eeprom->type = e1000_eeprom_microwire; + eeprom->word_size = 64; + eeprom->opcode_bits = 3; + eeprom->address_bits = 6; + eeprom->delay_usec = 50; + eeprom->use_eerd = FALSE; + eeprom->use_eewr = FALSE; + break; + case e1000_82540: + case e1000_82545: + case e1000_82545_rev_3: + case e1000_82546: + case e1000_82546_rev_3: + eeprom->type = e1000_eeprom_microwire; + eeprom->opcode_bits = 3; + eeprom->delay_usec = 50; + if (eecd & E1000_EECD_SIZE) { + eeprom->word_size = 256; + eeprom->address_bits = 8; + } else { + eeprom->word_size = 64; + eeprom->address_bits = 6; + } + eeprom->use_eerd = FALSE; + eeprom->use_eewr = FALSE; + break; + case e1000_82541: + case e1000_82541_rev_2: + case e1000_82547: + case e1000_82547_rev_2: + if (eecd & E1000_EECD_TYPE) { + eeprom->type = e1000_eeprom_spi; + eeprom->opcode_bits = 8; + eeprom->delay_usec = 1; + if (eecd & E1000_EECD_ADDR_BITS) { + eeprom->page_size = 32; + eeprom->address_bits = 16; + } else { + eeprom->page_size = 8; + eeprom->address_bits = 8; + } + } else { + eeprom->type = e1000_eeprom_microwire; + eeprom->opcode_bits = 3; + eeprom->delay_usec = 50; + if (eecd & E1000_EECD_ADDR_BITS) { + eeprom->word_size = 256; + eeprom->address_bits = 8; + } else { + eeprom->word_size = 64; + eeprom->address_bits = 6; + } + } + eeprom->use_eerd = FALSE; + eeprom->use_eewr = FALSE; + break; + case e1000_82571: + case e1000_82572: + eeprom->type = e1000_eeprom_spi; + eeprom->opcode_bits = 8; + eeprom->delay_usec = 1; + if (eecd & E1000_EECD_ADDR_BITS) { + eeprom->page_size = 32; + eeprom->address_bits = 16; + } else { + eeprom->page_size = 8; + eeprom->address_bits = 8; + } + eeprom->use_eerd = FALSE; + eeprom->use_eewr = FALSE; + break; + case e1000_82573: + eeprom->type = e1000_eeprom_spi; + eeprom->opcode_bits = 8; + eeprom->delay_usec = 1; + if (eecd & E1000_EECD_ADDR_BITS) { + eeprom->page_size = 32; + eeprom->address_bits = 16; + } else { + eeprom->page_size = 8; + eeprom->address_bits = 8; + } + eeprom->use_eerd = TRUE; + eeprom->use_eewr = TRUE; + if (e1000_is_onboard_nvm_eeprom(hw) == FALSE) { + eeprom->type = e1000_eeprom_flash; + eeprom->word_size = 2048; + + /* Ensure that the Autonomous FLASH update bit is cleared due to + * Flash update issue on parts which use a FLASH for NVM. */ + eecd &= ~E1000_EECD_AUPDEN; + E1000_WRITE_REG(hw, EECD, eecd); + } + break; + case e1000_80003es2lan: + eeprom->type = e1000_eeprom_spi; + eeprom->opcode_bits = 8; + eeprom->delay_usec = 1; + if (eecd & E1000_EECD_ADDR_BITS) { + eeprom->page_size = 32; + eeprom->address_bits = 16; + } else { + eeprom->page_size = 8; + eeprom->address_bits = 8; + } + eeprom->use_eerd = TRUE; + eeprom->use_eewr = FALSE; + break; + case e1000_ich8lan: + { + int32_t i = 0; + uint32_t flash_size = E1000_READ_ICH_FLASH_REG(hw, ICH_FLASH_GFPREG); + + eeprom->type = e1000_eeprom_ich8; + eeprom->use_eerd = FALSE; + eeprom->use_eewr = FALSE; + eeprom->word_size = E1000_SHADOW_RAM_WORDS; + + /* Zero the shadow RAM structure. But don't load it from NVM + * so as to save time for driver init */ + if (hw->eeprom_shadow_ram != NULL) { + for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) { + hw->eeprom_shadow_ram[i].modified = FALSE; + hw->eeprom_shadow_ram[i].eeprom_word = 0xFFFF; + } + } + + hw->flash_base_addr = (flash_size & ICH_GFPREG_BASE_MASK) * + ICH_FLASH_SECTOR_SIZE; + + hw->flash_bank_size = ((flash_size >> 16) & ICH_GFPREG_BASE_MASK) + 1; + hw->flash_bank_size -= (flash_size & ICH_GFPREG_BASE_MASK); + + hw->flash_bank_size *= ICH_FLASH_SECTOR_SIZE; + + hw->flash_bank_size /= 2 * sizeof(uint16_t); + + break; + } + default: + break; + } + + if (eeprom->type == e1000_eeprom_spi) { + /* eeprom_size will be an enum [0..8] that maps to eeprom sizes 128B to + * 32KB (incremented by powers of 2). + */ + if (hw->mac_type <= e1000_82547_rev_2) { + /* Set to default value for initial eeprom read. */ + eeprom->word_size = 64; + ret_val = e1000_read_eeprom(hw, EEPROM_CFG, 1, &eeprom_size); + if (ret_val) + return ret_val; + eeprom_size = (eeprom_size & EEPROM_SIZE_MASK) >> EEPROM_SIZE_SHIFT; + /* 256B eeprom size was not supported in earlier hardware, so we + * bump eeprom_size up one to ensure that "1" (which maps to 256B) + * is never the result used in the shifting logic below. */ + if (eeprom_size) + eeprom_size++; + } else { + eeprom_size = (uint16_t)((eecd & E1000_EECD_SIZE_EX_MASK) >> + E1000_EECD_SIZE_EX_SHIFT); + } + + eeprom->word_size = 1 << (eeprom_size + EEPROM_WORD_SIZE_SHIFT); + } + return ret_val; +} + +/****************************************************************************** + * Raises the EEPROM's clock input. + * + * hw - Struct containing variables accessed by shared code + * eecd - EECD's current value + *****************************************************************************/ +static void +e1000_raise_ee_clk(struct e1000_hw *hw, + uint32_t *eecd) +{ + /* Raise the clock input to the EEPROM (by setting the SK bit), and then + * wait microseconds. + */ + *eecd = *eecd | E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, *eecd); + E1000_WRITE_FLUSH(hw); + udelay(hw->eeprom.delay_usec); +} + +/****************************************************************************** + * Lowers the EEPROM's clock input. + * + * hw - Struct containing variables accessed by shared code + * eecd - EECD's current value + *****************************************************************************/ +static void +e1000_lower_ee_clk(struct e1000_hw *hw, + uint32_t *eecd) +{ + /* Lower the clock input to the EEPROM (by clearing the SK bit), and then + * wait 50 microseconds. + */ + *eecd = *eecd & ~E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, *eecd); + E1000_WRITE_FLUSH(hw); + udelay(hw->eeprom.delay_usec); +} + +/****************************************************************************** + * Shift data bits out to the EEPROM. + * + * hw - Struct containing variables accessed by shared code + * data - data to send to the EEPROM + * count - number of bits to shift out + *****************************************************************************/ +static void +e1000_shift_out_ee_bits(struct e1000_hw *hw, + uint16_t data, + uint16_t count) +{ + struct e1000_eeprom_info *eeprom = &hw->eeprom; + uint32_t eecd; + uint32_t mask; + + /* We need to shift "count" bits out to the EEPROM. So, value in the + * "data" parameter will be shifted out to the EEPROM one bit at a time. + * In order to do this, "data" must be broken down into bits. + */ + mask = 0x01 << (count - 1); + eecd = E1000_READ_REG(hw, EECD); + if (eeprom->type == e1000_eeprom_microwire) { + eecd &= ~E1000_EECD_DO; + } else if (eeprom->type == e1000_eeprom_spi) { + eecd |= E1000_EECD_DO; + } + do { + /* A "1" is shifted out to the EEPROM by setting bit "DI" to a "1", + * and then raising and then lowering the clock (the SK bit controls + * the clock input to the EEPROM). A "0" is shifted out to the EEPROM + * by setting "DI" to "0" and then raising and then lowering the clock. + */ + eecd &= ~E1000_EECD_DI; + + if (data & mask) + eecd |= E1000_EECD_DI; + + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + + udelay(eeprom->delay_usec); + + e1000_raise_ee_clk(hw, &eecd); + e1000_lower_ee_clk(hw, &eecd); + + mask = mask >> 1; + + } while (mask); + + /* We leave the "DI" bit set to "0" when we leave this routine. */ + eecd &= ~E1000_EECD_DI; + E1000_WRITE_REG(hw, EECD, eecd); +} + +/****************************************************************************** + * Shift data bits in from the EEPROM + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static uint16_t +e1000_shift_in_ee_bits(struct e1000_hw *hw, + uint16_t count) +{ + uint32_t eecd; + uint32_t i; + uint16_t data; + + /* In order to read a register from the EEPROM, we need to shift 'count' + * bits in from the EEPROM. Bits are "shifted in" by raising the clock + * input to the EEPROM (setting the SK bit), and then reading the value of + * the "DO" bit. During this "shifting in" process the "DI" bit should + * always be clear. + */ + + eecd = E1000_READ_REG(hw, EECD); + + eecd &= ~(E1000_EECD_DO | E1000_EECD_DI); + data = 0; + + for (i = 0; i < count; i++) { + data = data << 1; + e1000_raise_ee_clk(hw, &eecd); + + eecd = E1000_READ_REG(hw, EECD); + + eecd &= ~(E1000_EECD_DI); + if (eecd & E1000_EECD_DO) + data |= 1; + + e1000_lower_ee_clk(hw, &eecd); + } + + return data; +} + +/****************************************************************************** + * Prepares EEPROM for access + * + * hw - Struct containing variables accessed by shared code + * + * Lowers EEPROM clock. Clears input pin. Sets the chip select pin. This + * function should be called before issuing a command to the EEPROM. + *****************************************************************************/ +static int32_t +e1000_acquire_eeprom(struct e1000_hw *hw) +{ + struct e1000_eeprom_info *eeprom = &hw->eeprom; + uint32_t eecd, i=0; + + DEBUGFUNC("e1000_acquire_eeprom"); + + if (e1000_swfw_sync_acquire(hw, E1000_SWFW_EEP_SM)) + return -E1000_ERR_SWFW_SYNC; + eecd = E1000_READ_REG(hw, EECD); + + if (hw->mac_type != e1000_82573) { + /* Request EEPROM Access */ + if (hw->mac_type > e1000_82544) { + eecd |= E1000_EECD_REQ; + E1000_WRITE_REG(hw, EECD, eecd); + eecd = E1000_READ_REG(hw, EECD); + while ((!(eecd & E1000_EECD_GNT)) && + (i < E1000_EEPROM_GRANT_ATTEMPTS)) { + i++; + udelay(5); + eecd = E1000_READ_REG(hw, EECD); + } + if (!(eecd & E1000_EECD_GNT)) { + eecd &= ~E1000_EECD_REQ; + E1000_WRITE_REG(hw, EECD, eecd); + DEBUGOUT("Could not acquire EEPROM grant\n"); + e1000_swfw_sync_release(hw, E1000_SWFW_EEP_SM); + return -E1000_ERR_EEPROM; + } + } + } + + /* Setup EEPROM for Read/Write */ + + if (eeprom->type == e1000_eeprom_microwire) { + /* Clear SK and DI */ + eecd &= ~(E1000_EECD_DI | E1000_EECD_SK); + E1000_WRITE_REG(hw, EECD, eecd); + + /* Set CS */ + eecd |= E1000_EECD_CS; + E1000_WRITE_REG(hw, EECD, eecd); + } else if (eeprom->type == e1000_eeprom_spi) { + /* Clear SK and CS */ + eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); + E1000_WRITE_REG(hw, EECD, eecd); + udelay(1); + } + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Returns EEPROM to a "standby" state + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static void +e1000_standby_eeprom(struct e1000_hw *hw) +{ + struct e1000_eeprom_info *eeprom = &hw->eeprom; + uint32_t eecd; + + eecd = E1000_READ_REG(hw, EECD); + + if (eeprom->type == e1000_eeprom_microwire) { + eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(eeprom->delay_usec); + + /* Clock high */ + eecd |= E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(eeprom->delay_usec); + + /* Select EEPROM */ + eecd |= E1000_EECD_CS; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(eeprom->delay_usec); + + /* Clock low */ + eecd &= ~E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(eeprom->delay_usec); + } else if (eeprom->type == e1000_eeprom_spi) { + /* Toggle CS to flush commands */ + eecd |= E1000_EECD_CS; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(eeprom->delay_usec); + eecd &= ~E1000_EECD_CS; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(eeprom->delay_usec); + } +} + +/****************************************************************************** + * Terminates a command by inverting the EEPROM's chip select pin + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static void +e1000_release_eeprom(struct e1000_hw *hw) +{ + uint32_t eecd; + + DEBUGFUNC("e1000_release_eeprom"); + + eecd = E1000_READ_REG(hw, EECD); + + if (hw->eeprom.type == e1000_eeprom_spi) { + eecd |= E1000_EECD_CS; /* Pull CS high */ + eecd &= ~E1000_EECD_SK; /* Lower SCK */ + + E1000_WRITE_REG(hw, EECD, eecd); + + udelay(hw->eeprom.delay_usec); + } else if (hw->eeprom.type == e1000_eeprom_microwire) { + /* cleanup eeprom */ + + /* CS on Microwire is active-high */ + eecd &= ~(E1000_EECD_CS | E1000_EECD_DI); + + E1000_WRITE_REG(hw, EECD, eecd); + + /* Rising edge of clock */ + eecd |= E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(hw->eeprom.delay_usec); + + /* Falling edge of clock */ + eecd &= ~E1000_EECD_SK; + E1000_WRITE_REG(hw, EECD, eecd); + E1000_WRITE_FLUSH(hw); + udelay(hw->eeprom.delay_usec); + } + + /* Stop requesting EEPROM access */ + if (hw->mac_type > e1000_82544) { + eecd &= ~E1000_EECD_REQ; + E1000_WRITE_REG(hw, EECD, eecd); + } + + e1000_swfw_sync_release(hw, E1000_SWFW_EEP_SM); +} + +/****************************************************************************** + * Reads a 16 bit word from the EEPROM. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static int32_t +e1000_spi_eeprom_ready(struct e1000_hw *hw) +{ + uint16_t retry_count = 0; + uint8_t spi_stat_reg; + + DEBUGFUNC("e1000_spi_eeprom_ready"); + + /* Read "Status Register" repeatedly until the LSB is cleared. The + * EEPROM will signal that the command has been completed by clearing + * bit 0 of the internal status register. If it's not cleared within + * 5 milliseconds, then error out. + */ + retry_count = 0; + do { + e1000_shift_out_ee_bits(hw, EEPROM_RDSR_OPCODE_SPI, + hw->eeprom.opcode_bits); + spi_stat_reg = (uint8_t)e1000_shift_in_ee_bits(hw, 8); + if (!(spi_stat_reg & EEPROM_STATUS_RDY_SPI)) + break; + + udelay(5); + retry_count += 5; + + e1000_standby_eeprom(hw); + } while (retry_count < EEPROM_MAX_RETRY_SPI); + + /* ATMEL SPI write time could vary from 0-20mSec on 3.3V devices (and + * only 0-5mSec on 5V devices) + */ + if (retry_count >= EEPROM_MAX_RETRY_SPI) { + DEBUGOUT("SPI EEPROM Status error\n"); + return -E1000_ERR_EEPROM; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Reads a 16 bit word from the EEPROM. + * + * hw - Struct containing variables accessed by shared code + * offset - offset of word in the EEPROM to read + * data - word read from the EEPROM + * words - number of words to read + *****************************************************************************/ +int32_t +e1000_read_eeprom(struct e1000_hw *hw, + uint16_t offset, + uint16_t words, + uint16_t *data) +{ + struct e1000_eeprom_info *eeprom = &hw->eeprom; + uint32_t i = 0; + + DEBUGFUNC("e1000_read_eeprom"); + + /* If eeprom is not yet detected, do so now */ + if (eeprom->word_size == 0) + e1000_init_eeprom_params(hw); + + /* A check for invalid values: offset too large, too many words, and not + * enough words. + */ + if ((offset >= eeprom->word_size) || (words > eeprom->word_size - offset) || + (words == 0)) { + DEBUGOUT2("\"words\" parameter out of bounds. Words = %d, size = %d\n", offset, eeprom->word_size); + return -E1000_ERR_EEPROM; + } + + /* EEPROM's that don't use EERD to read require us to bit-bang the SPI + * directly. In this case, we need to acquire the EEPROM so that + * FW or other port software does not interrupt. + */ + if (e1000_is_onboard_nvm_eeprom(hw) == TRUE && + hw->eeprom.use_eerd == FALSE) { + /* Prepare the EEPROM for bit-bang reading */ + if (e1000_acquire_eeprom(hw) != E1000_SUCCESS) + return -E1000_ERR_EEPROM; + } + + /* Eerd register EEPROM access requires no eeprom aquire/release */ + if (eeprom->use_eerd == TRUE) + return e1000_read_eeprom_eerd(hw, offset, words, data); + + /* ICH EEPROM access is done via the ICH flash controller */ + if (eeprom->type == e1000_eeprom_ich8) + return e1000_read_eeprom_ich8(hw, offset, words, data); + + /* Set up the SPI or Microwire EEPROM for bit-bang reading. We have + * acquired the EEPROM at this point, so any returns should relase it */ + if (eeprom->type == e1000_eeprom_spi) { + uint16_t word_in; + uint8_t read_opcode = EEPROM_READ_OPCODE_SPI; + + if (e1000_spi_eeprom_ready(hw)) { + e1000_release_eeprom(hw); + return -E1000_ERR_EEPROM; + } + + e1000_standby_eeprom(hw); + + /* Some SPI eeproms use the 8th address bit embedded in the opcode */ + if ((eeprom->address_bits == 8) && (offset >= 128)) + read_opcode |= EEPROM_A8_OPCODE_SPI; + + /* Send the READ command (opcode + addr) */ + e1000_shift_out_ee_bits(hw, read_opcode, eeprom->opcode_bits); + e1000_shift_out_ee_bits(hw, (uint16_t)(offset*2), eeprom->address_bits); + + /* Read the data. The address of the eeprom internally increments with + * each byte (spi) being read, saving on the overhead of eeprom setup + * and tear-down. The address counter will roll over if reading beyond + * the size of the eeprom, thus allowing the entire memory to be read + * starting from any offset. */ + for (i = 0; i < words; i++) { + word_in = e1000_shift_in_ee_bits(hw, 16); + data[i] = (word_in >> 8) | (word_in << 8); + } + } else if (eeprom->type == e1000_eeprom_microwire) { + for (i = 0; i < words; i++) { + /* Send the READ command (opcode + addr) */ + e1000_shift_out_ee_bits(hw, EEPROM_READ_OPCODE_MICROWIRE, + eeprom->opcode_bits); + e1000_shift_out_ee_bits(hw, (uint16_t)(offset + i), + eeprom->address_bits); + + /* Read the data. For microwire, each word requires the overhead + * of eeprom setup and tear-down. */ + data[i] = e1000_shift_in_ee_bits(hw, 16); + e1000_standby_eeprom(hw); + } + } + + /* End this read operation */ + e1000_release_eeprom(hw); + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Reads a 16 bit word from the EEPROM using the EERD register. + * + * hw - Struct containing variables accessed by shared code + * offset - offset of word in the EEPROM to read + * data - word read from the EEPROM + * words - number of words to read + *****************************************************************************/ +static int32_t +e1000_read_eeprom_eerd(struct e1000_hw *hw, + uint16_t offset, + uint16_t words, + uint16_t *data) +{ + uint32_t i, eerd = 0; + int32_t error = 0; + + for (i = 0; i < words; i++) { + eerd = ((offset+i) << E1000_EEPROM_RW_ADDR_SHIFT) + + E1000_EEPROM_RW_REG_START; + + E1000_WRITE_REG(hw, EERD, eerd); + error = e1000_poll_eerd_eewr_done(hw, E1000_EEPROM_POLL_READ); + + if (error) { + break; + } + data[i] = (E1000_READ_REG(hw, EERD) >> E1000_EEPROM_RW_REG_DATA); + + } + + return error; +} + +/****************************************************************************** + * Writes a 16 bit word from the EEPROM using the EEWR register. + * + * hw - Struct containing variables accessed by shared code + * offset - offset of word in the EEPROM to read + * data - word read from the EEPROM + * words - number of words to read + *****************************************************************************/ +static int32_t +e1000_write_eeprom_eewr(struct e1000_hw *hw, + uint16_t offset, + uint16_t words, + uint16_t *data) +{ + uint32_t register_value = 0; + uint32_t i = 0; + int32_t error = 0; + + if (e1000_swfw_sync_acquire(hw, E1000_SWFW_EEP_SM)) + return -E1000_ERR_SWFW_SYNC; + + for (i = 0; i < words; i++) { + register_value = (data[i] << E1000_EEPROM_RW_REG_DATA) | + ((offset+i) << E1000_EEPROM_RW_ADDR_SHIFT) | + E1000_EEPROM_RW_REG_START; + + error = e1000_poll_eerd_eewr_done(hw, E1000_EEPROM_POLL_WRITE); + if (error) { + break; + } + + E1000_WRITE_REG(hw, EEWR, register_value); + + error = e1000_poll_eerd_eewr_done(hw, E1000_EEPROM_POLL_WRITE); + + if (error) { + break; + } + } + + e1000_swfw_sync_release(hw, E1000_SWFW_EEP_SM); + return error; +} + +/****************************************************************************** + * Polls the status bit (bit 1) of the EERD to determine when the read is done. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static int32_t +e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int eerd) +{ + uint32_t attempts = 100000; + uint32_t i, reg = 0; + int32_t done = E1000_ERR_EEPROM; + + for (i = 0; i < attempts; i++) { + if (eerd == E1000_EEPROM_POLL_READ) + reg = E1000_READ_REG(hw, EERD); + else + reg = E1000_READ_REG(hw, EEWR); + + if (reg & E1000_EEPROM_RW_REG_DONE) { + done = E1000_SUCCESS; + break; + } + udelay(5); + } + + return done; +} + +/*************************************************************************** +* Description: Determines if the onboard NVM is FLASH or EEPROM. +* +* hw - Struct containing variables accessed by shared code +****************************************************************************/ +static boolean_t +e1000_is_onboard_nvm_eeprom(struct e1000_hw *hw) +{ + uint32_t eecd = 0; + + DEBUGFUNC("e1000_is_onboard_nvm_eeprom"); + + if (hw->mac_type == e1000_ich8lan) + return FALSE; + + if (hw->mac_type == e1000_82573) { + eecd = E1000_READ_REG(hw, EECD); + + /* Isolate bits 15 & 16 */ + eecd = ((eecd >> 15) & 0x03); + + /* If both bits are set, device is Flash type */ + if (eecd == 0x03) { + return FALSE; + } + } + return TRUE; +} + +/****************************************************************************** + * Verifies that the EEPROM has a valid checksum + * + * hw - Struct containing variables accessed by shared code + * + * Reads the first 64 16 bit words of the EEPROM and sums the values read. + * If the the sum of the 64 16 bit words is 0xBABA, the EEPROM's checksum is + * valid. + *****************************************************************************/ +int32_t +e1000_validate_eeprom_checksum(struct e1000_hw *hw) +{ + uint16_t checksum = 0; + uint16_t i, eeprom_data; + + DEBUGFUNC("e1000_validate_eeprom_checksum"); + + if ((hw->mac_type == e1000_82573) && + (e1000_is_onboard_nvm_eeprom(hw) == FALSE)) { + /* Check bit 4 of word 10h. If it is 0, firmware is done updating + * 10h-12h. Checksum may need to be fixed. */ + e1000_read_eeprom(hw, 0x10, 1, &eeprom_data); + if ((eeprom_data & 0x10) == 0) { + /* Read 0x23 and check bit 15. This bit is a 1 when the checksum + * has already been fixed. If the checksum is still wrong and this + * bit is a 1, we need to return bad checksum. Otherwise, we need + * to set this bit to a 1 and update the checksum. */ + e1000_read_eeprom(hw, 0x23, 1, &eeprom_data); + if ((eeprom_data & 0x8000) == 0) { + eeprom_data |= 0x8000; + e1000_write_eeprom(hw, 0x23, 1, &eeprom_data); + e1000_update_eeprom_checksum(hw); + } + } + } + + if (hw->mac_type == e1000_ich8lan) { + /* Drivers must allocate the shadow ram structure for the + * EEPROM checksum to be updated. Otherwise, this bit as well + * as the checksum must both be set correctly for this + * validation to pass. + */ + e1000_read_eeprom(hw, 0x19, 1, &eeprom_data); + if ((eeprom_data & 0x40) == 0) { + eeprom_data |= 0x40; + e1000_write_eeprom(hw, 0x19, 1, &eeprom_data); + e1000_update_eeprom_checksum(hw); + } + } + + for (i = 0; i < (EEPROM_CHECKSUM_REG + 1); i++) { + if (e1000_read_eeprom(hw, i, 1, &eeprom_data) < 0) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + checksum += eeprom_data; + } + + if (checksum == (uint16_t) EEPROM_SUM) + return E1000_SUCCESS; + else { + DEBUGOUT("EEPROM Checksum Invalid\n"); + return -E1000_ERR_EEPROM; + } +} + +/****************************************************************************** + * Calculates the EEPROM checksum and writes it to the EEPROM + * + * hw - Struct containing variables accessed by shared code + * + * Sums the first 63 16 bit words of the EEPROM. Subtracts the sum from 0xBABA. + * Writes the difference to word offset 63 of the EEPROM. + *****************************************************************************/ +int32_t +e1000_update_eeprom_checksum(struct e1000_hw *hw) +{ + uint32_t ctrl_ext; + uint16_t checksum = 0; + uint16_t i, eeprom_data; + + DEBUGFUNC("e1000_update_eeprom_checksum"); + + for (i = 0; i < EEPROM_CHECKSUM_REG; i++) { + if (e1000_read_eeprom(hw, i, 1, &eeprom_data) < 0) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + checksum += eeprom_data; + } + checksum = (uint16_t) EEPROM_SUM - checksum; + if (e1000_write_eeprom(hw, EEPROM_CHECKSUM_REG, 1, &checksum) < 0) { + DEBUGOUT("EEPROM Write Error\n"); + return -E1000_ERR_EEPROM; + } else if (hw->eeprom.type == e1000_eeprom_flash) { + e1000_commit_shadow_ram(hw); + } else if (hw->eeprom.type == e1000_eeprom_ich8) { + e1000_commit_shadow_ram(hw); + /* Reload the EEPROM, or else modifications will not appear + * until after next adapter reset. */ + ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_EE_RST; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + msleep(10); + } + return E1000_SUCCESS; +} + +/****************************************************************************** + * Parent function for writing words to the different EEPROM types. + * + * hw - Struct containing variables accessed by shared code + * offset - offset within the EEPROM to be written to + * words - number of words to write + * data - 16 bit word to be written to the EEPROM + * + * If e1000_update_eeprom_checksum is not called after this function, the + * EEPROM will most likely contain an invalid checksum. + *****************************************************************************/ +int32_t +e1000_write_eeprom(struct e1000_hw *hw, + uint16_t offset, + uint16_t words, + uint16_t *data) +{ + struct e1000_eeprom_info *eeprom = &hw->eeprom; + int32_t status = 0; + + DEBUGFUNC("e1000_write_eeprom"); + + /* If eeprom is not yet detected, do so now */ + if (eeprom->word_size == 0) + e1000_init_eeprom_params(hw); + + /* A check for invalid values: offset too large, too many words, and not + * enough words. + */ + if ((offset >= eeprom->word_size) || (words > eeprom->word_size - offset) || + (words == 0)) { + DEBUGOUT("\"words\" parameter out of bounds\n"); + return -E1000_ERR_EEPROM; + } + + /* 82573 writes only through eewr */ + if (eeprom->use_eewr == TRUE) + return e1000_write_eeprom_eewr(hw, offset, words, data); + + if (eeprom->type == e1000_eeprom_ich8) + return e1000_write_eeprom_ich8(hw, offset, words, data); + + /* Prepare the EEPROM for writing */ + if (e1000_acquire_eeprom(hw) != E1000_SUCCESS) + return -E1000_ERR_EEPROM; + + if (eeprom->type == e1000_eeprom_microwire) { + status = e1000_write_eeprom_microwire(hw, offset, words, data); + } else { + status = e1000_write_eeprom_spi(hw, offset, words, data); + msleep(10); + } + + /* Done with writing */ + e1000_release_eeprom(hw); + + return status; +} + +/****************************************************************************** + * Writes a 16 bit word to a given offset in an SPI EEPROM. + * + * hw - Struct containing variables accessed by shared code + * offset - offset within the EEPROM to be written to + * words - number of words to write + * data - pointer to array of 8 bit words to be written to the EEPROM + * + *****************************************************************************/ +static int32_t +e1000_write_eeprom_spi(struct e1000_hw *hw, + uint16_t offset, + uint16_t words, + uint16_t *data) +{ + struct e1000_eeprom_info *eeprom = &hw->eeprom; + uint16_t widx = 0; + + DEBUGFUNC("e1000_write_eeprom_spi"); + + while (widx < words) { + uint8_t write_opcode = EEPROM_WRITE_OPCODE_SPI; + + if (e1000_spi_eeprom_ready(hw)) return -E1000_ERR_EEPROM; + + e1000_standby_eeprom(hw); + + /* Send the WRITE ENABLE command (8 bit opcode ) */ + e1000_shift_out_ee_bits(hw, EEPROM_WREN_OPCODE_SPI, + eeprom->opcode_bits); + + e1000_standby_eeprom(hw); + + /* Some SPI eeproms use the 8th address bit embedded in the opcode */ + if ((eeprom->address_bits == 8) && (offset >= 128)) + write_opcode |= EEPROM_A8_OPCODE_SPI; + + /* Send the Write command (8-bit opcode + addr) */ + e1000_shift_out_ee_bits(hw, write_opcode, eeprom->opcode_bits); + + e1000_shift_out_ee_bits(hw, (uint16_t)((offset + widx)*2), + eeprom->address_bits); + + /* Send the data */ + + /* Loop to allow for up to whole page write (32 bytes) of eeprom */ + while (widx < words) { + uint16_t word_out = data[widx]; + word_out = (word_out >> 8) | (word_out << 8); + e1000_shift_out_ee_bits(hw, word_out, 16); + widx++; + + /* Some larger eeprom sizes are capable of a 32-byte PAGE WRITE + * operation, while the smaller eeproms are capable of an 8-byte + * PAGE WRITE operation. Break the inner loop to pass new address + */ + if ((((offset + widx)*2) % eeprom->page_size) == 0) { + e1000_standby_eeprom(hw); + break; + } + } + } + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Writes a 16 bit word to a given offset in a Microwire EEPROM. + * + * hw - Struct containing variables accessed by shared code + * offset - offset within the EEPROM to be written to + * words - number of words to write + * data - pointer to array of 16 bit words to be written to the EEPROM + * + *****************************************************************************/ +static int32_t +e1000_write_eeprom_microwire(struct e1000_hw *hw, + uint16_t offset, + uint16_t words, + uint16_t *data) +{ + struct e1000_eeprom_info *eeprom = &hw->eeprom; + uint32_t eecd; + uint16_t words_written = 0; + uint16_t i = 0; + + DEBUGFUNC("e1000_write_eeprom_microwire"); + + /* Send the write enable command to the EEPROM (3-bit opcode plus + * 6/8-bit dummy address beginning with 11). It's less work to include + * the 11 of the dummy address as part of the opcode than it is to shift + * it over the correct number of bits for the address. This puts the + * EEPROM into write/erase mode. + */ + e1000_shift_out_ee_bits(hw, EEPROM_EWEN_OPCODE_MICROWIRE, + (uint16_t)(eeprom->opcode_bits + 2)); + + e1000_shift_out_ee_bits(hw, 0, (uint16_t)(eeprom->address_bits - 2)); + + /* Prepare the EEPROM */ + e1000_standby_eeprom(hw); + + while (words_written < words) { + /* Send the Write command (3-bit opcode + addr) */ + e1000_shift_out_ee_bits(hw, EEPROM_WRITE_OPCODE_MICROWIRE, + eeprom->opcode_bits); + + e1000_shift_out_ee_bits(hw, (uint16_t)(offset + words_written), + eeprom->address_bits); + + /* Send the data */ + e1000_shift_out_ee_bits(hw, data[words_written], 16); + + /* Toggle the CS line. This in effect tells the EEPROM to execute + * the previous command. + */ + e1000_standby_eeprom(hw); + + /* Read DO repeatedly until it is high (equal to '1'). The EEPROM will + * signal that the command has been completed by raising the DO signal. + * If DO does not go high in 10 milliseconds, then error out. + */ + for (i = 0; i < 200; i++) { + eecd = E1000_READ_REG(hw, EECD); + if (eecd & E1000_EECD_DO) break; + udelay(50); + } + if (i == 200) { + DEBUGOUT("EEPROM Write did not complete\n"); + return -E1000_ERR_EEPROM; + } + + /* Recover from write */ + e1000_standby_eeprom(hw); + + words_written++; + } + + /* Send the write disable command to the EEPROM (3-bit opcode plus + * 6/8-bit dummy address beginning with 10). It's less work to include + * the 10 of the dummy address as part of the opcode than it is to shift + * it over the correct number of bits for the address. This takes the + * EEPROM out of write/erase mode. + */ + e1000_shift_out_ee_bits(hw, EEPROM_EWDS_OPCODE_MICROWIRE, + (uint16_t)(eeprom->opcode_bits + 2)); + + e1000_shift_out_ee_bits(hw, 0, (uint16_t)(eeprom->address_bits - 2)); + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Flushes the cached eeprom to NVM. This is done by saving the modified values + * in the eeprom cache and the non modified values in the currently active bank + * to the new bank. + * + * hw - Struct containing variables accessed by shared code + * offset - offset of word in the EEPROM to read + * data - word read from the EEPROM + * words - number of words to read + *****************************************************************************/ +static int32_t +e1000_commit_shadow_ram(struct e1000_hw *hw) +{ + uint32_t attempts = 100000; + uint32_t eecd = 0; + uint32_t flop = 0; + uint32_t i = 0; + int32_t error = E1000_SUCCESS; + uint32_t old_bank_offset = 0; + uint32_t new_bank_offset = 0; + uint8_t low_byte = 0; + uint8_t high_byte = 0; + boolean_t sector_write_failed = FALSE; + + if (hw->mac_type == e1000_82573) { + /* The flop register will be used to determine if flash type is STM */ + flop = E1000_READ_REG(hw, FLOP); + for (i=0; i < attempts; i++) { + eecd = E1000_READ_REG(hw, EECD); + if ((eecd & E1000_EECD_FLUPD) == 0) { + break; + } + udelay(5); + } + + if (i == attempts) { + return -E1000_ERR_EEPROM; + } + + /* If STM opcode located in bits 15:8 of flop, reset firmware */ + if ((flop & 0xFF00) == E1000_STM_OPCODE) { + E1000_WRITE_REG(hw, HICR, E1000_HICR_FW_RESET); + } + + /* Perform the flash update */ + E1000_WRITE_REG(hw, EECD, eecd | E1000_EECD_FLUPD); + + for (i=0; i < attempts; i++) { + eecd = E1000_READ_REG(hw, EECD); + if ((eecd & E1000_EECD_FLUPD) == 0) { + break; + } + udelay(5); + } + + if (i == attempts) { + return -E1000_ERR_EEPROM; + } + } + + if (hw->mac_type == e1000_ich8lan && hw->eeprom_shadow_ram != NULL) { + /* We're writing to the opposite bank so if we're on bank 1, + * write to bank 0 etc. We also need to erase the segment that + * is going to be written */ + if (!(E1000_READ_REG(hw, EECD) & E1000_EECD_SEC1VAL)) { + new_bank_offset = hw->flash_bank_size * 2; + old_bank_offset = 0; + e1000_erase_ich8_4k_segment(hw, 1); + } else { + old_bank_offset = hw->flash_bank_size * 2; + new_bank_offset = 0; + e1000_erase_ich8_4k_segment(hw, 0); + } + + sector_write_failed = FALSE; + /* Loop for every byte in the shadow RAM, + * which is in units of words. */ + for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) { + /* Determine whether to write the value stored + * in the other NVM bank or a modified value stored + * in the shadow RAM */ + if (hw->eeprom_shadow_ram[i].modified == TRUE) { + low_byte = (uint8_t)hw->eeprom_shadow_ram[i].eeprom_word; + udelay(100); + error = e1000_verify_write_ich8_byte(hw, + (i << 1) + new_bank_offset, low_byte); + + if (error != E1000_SUCCESS) + sector_write_failed = TRUE; + else { + high_byte = + (uint8_t)(hw->eeprom_shadow_ram[i].eeprom_word >> 8); + udelay(100); + } + } else { + e1000_read_ich8_byte(hw, (i << 1) + old_bank_offset, + &low_byte); + udelay(100); + error = e1000_verify_write_ich8_byte(hw, + (i << 1) + new_bank_offset, low_byte); + + if (error != E1000_SUCCESS) + sector_write_failed = TRUE; + else { + e1000_read_ich8_byte(hw, (i << 1) + old_bank_offset + 1, + &high_byte); + udelay(100); + } + } + + /* If the write of the low byte was successful, go ahread and + * write the high byte while checking to make sure that if it + * is the signature byte, then it is handled properly */ + if (sector_write_failed == FALSE) { + /* If the word is 0x13, then make sure the signature bits + * (15:14) are 11b until the commit has completed. + * This will allow us to write 10b which indicates the + * signature is valid. We want to do this after the write + * has completed so that we don't mark the segment valid + * while the write is still in progress */ + if (i == E1000_ICH_NVM_SIG_WORD) + high_byte = E1000_ICH_NVM_SIG_MASK | high_byte; + + error = e1000_verify_write_ich8_byte(hw, + (i << 1) + new_bank_offset + 1, high_byte); + if (error != E1000_SUCCESS) + sector_write_failed = TRUE; + + } else { + /* If the write failed then break from the loop and + * return an error */ + break; + } + } + + /* Don't bother writing the segment valid bits if sector + * programming failed. */ + if (sector_write_failed == FALSE) { + /* Finally validate the new segment by setting bit 15:14 + * to 10b in word 0x13 , this can be done without an + * erase as well since these bits are 11 to start with + * and we need to change bit 14 to 0b */ + e1000_read_ich8_byte(hw, + E1000_ICH_NVM_SIG_WORD * 2 + 1 + new_bank_offset, + &high_byte); + high_byte &= 0xBF; + error = e1000_verify_write_ich8_byte(hw, + E1000_ICH_NVM_SIG_WORD * 2 + 1 + new_bank_offset, high_byte); + /* And invalidate the previously valid segment by setting + * its signature word (0x13) high_byte to 0b. This can be + * done without an erase because flash erase sets all bits + * to 1's. We can write 1's to 0's without an erase */ + if (error == E1000_SUCCESS) { + error = e1000_verify_write_ich8_byte(hw, + E1000_ICH_NVM_SIG_WORD * 2 + 1 + old_bank_offset, 0); + } + + /* Clear the now not used entry in the cache */ + for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) { + hw->eeprom_shadow_ram[i].modified = FALSE; + hw->eeprom_shadow_ram[i].eeprom_word = 0xFFFF; + } + } + } + + return error; +} + +/****************************************************************************** + * Reads the adapter's MAC address from the EEPROM and inverts the LSB for the + * second function of dual function devices + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +e1000_read_mac_addr(struct e1000_hw * hw) +{ + uint16_t offset; + uint16_t eeprom_data, i; + + DEBUGFUNC("e1000_read_mac_addr"); + + for (i = 0; i < NODE_ADDRESS_SIZE; i += 2) { + offset = i >> 1; + if (e1000_read_eeprom(hw, offset, 1, &eeprom_data) < 0) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + hw->perm_mac_addr[i] = (uint8_t) (eeprom_data & 0x00FF); + hw->perm_mac_addr[i+1] = (uint8_t) (eeprom_data >> 8); + } + + switch (hw->mac_type) { + default: + break; + case e1000_82546: + case e1000_82546_rev_3: + case e1000_82571: + case e1000_80003es2lan: + if (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1) + hw->perm_mac_addr[5] ^= 0x01; + break; + } + + for (i = 0; i < NODE_ADDRESS_SIZE; i++) + hw->mac_addr[i] = hw->perm_mac_addr[i]; + return E1000_SUCCESS; +} + +/****************************************************************************** + * Initializes receive address filters. + * + * hw - Struct containing variables accessed by shared code + * + * Places the MAC address in receive address register 0 and clears the rest + * of the receive addresss registers. Clears the multicast table. Assumes + * the receiver is in reset when the routine is called. + *****************************************************************************/ +static void +e1000_init_rx_addrs(struct e1000_hw *hw) +{ + uint32_t i; + uint32_t rar_num; + + DEBUGFUNC("e1000_init_rx_addrs"); + + /* Setup the receive address. */ + DEBUGOUT("Programming MAC Address into RAR[0]\n"); + + e1000_rar_set(hw, hw->mac_addr, 0); + + rar_num = E1000_RAR_ENTRIES; + + /* Reserve a spot for the Locally Administered Address to work around + * an 82571 issue in which a reset on one port will reload the MAC on + * the other port. */ + if ((hw->mac_type == e1000_82571) && (hw->laa_is_present == TRUE)) + rar_num -= 1; + if (hw->mac_type == e1000_ich8lan) + rar_num = E1000_RAR_ENTRIES_ICH8LAN; + + /* Zero out the other 15 receive addresses. */ + DEBUGOUT("Clearing RAR[1-15]\n"); + for (i = 1; i < rar_num; i++) { + E1000_WRITE_REG_ARRAY(hw, RA, (i << 1), 0); + E1000_WRITE_FLUSH(hw); + E1000_WRITE_REG_ARRAY(hw, RA, ((i << 1) + 1), 0); + E1000_WRITE_FLUSH(hw); + } +} + +/****************************************************************************** + * Hashes an address to determine its location in the multicast table + * + * hw - Struct containing variables accessed by shared code + * mc_addr - the multicast address to hash + *****************************************************************************/ +uint32_t +e1000_hash_mc_addr(struct e1000_hw *hw, + uint8_t *mc_addr) +{ + uint32_t hash_value = 0; + + /* The portion of the address that is used for the hash table is + * determined by the mc_filter_type setting. + */ + switch (hw->mc_filter_type) { + /* [0] [1] [2] [3] [4] [5] + * 01 AA 00 12 34 56 + * LSB MSB + */ + case 0: + if (hw->mac_type == e1000_ich8lan) { + /* [47:38] i.e. 0x158 for above example address */ + hash_value = ((mc_addr[4] >> 6) | (((uint16_t) mc_addr[5]) << 2)); + } else { + /* [47:36] i.e. 0x563 for above example address */ + hash_value = ((mc_addr[4] >> 4) | (((uint16_t) mc_addr[5]) << 4)); + } + break; + case 1: + if (hw->mac_type == e1000_ich8lan) { + /* [46:37] i.e. 0x2B1 for above example address */ + hash_value = ((mc_addr[4] >> 5) | (((uint16_t) mc_addr[5]) << 3)); + } else { + /* [46:35] i.e. 0xAC6 for above example address */ + hash_value = ((mc_addr[4] >> 3) | (((uint16_t) mc_addr[5]) << 5)); + } + break; + case 2: + if (hw->mac_type == e1000_ich8lan) { + /*[45:36] i.e. 0x163 for above example address */ + hash_value = ((mc_addr[4] >> 4) | (((uint16_t) mc_addr[5]) << 4)); + } else { + /* [45:34] i.e. 0x5D8 for above example address */ + hash_value = ((mc_addr[4] >> 2) | (((uint16_t) mc_addr[5]) << 6)); + } + break; + case 3: + if (hw->mac_type == e1000_ich8lan) { + /* [43:34] i.e. 0x18D for above example address */ + hash_value = ((mc_addr[4] >> 2) | (((uint16_t) mc_addr[5]) << 6)); + } else { + /* [43:32] i.e. 0x634 for above example address */ + hash_value = ((mc_addr[4]) | (((uint16_t) mc_addr[5]) << 8)); + } + break; + } + + hash_value &= 0xFFF; + if (hw->mac_type == e1000_ich8lan) + hash_value &= 0x3FF; + + return hash_value; +} + +/****************************************************************************** + * Sets the bit in the multicast table corresponding to the hash value. + * + * hw - Struct containing variables accessed by shared code + * hash_value - Multicast address hash value + *****************************************************************************/ +void +e1000_mta_set(struct e1000_hw *hw, + uint32_t hash_value) +{ + uint32_t hash_bit, hash_reg; + uint32_t mta; + uint32_t temp; + + /* The MTA is a register array of 128 32-bit registers. + * It is treated like an array of 4096 bits. We want to set + * bit BitArray[hash_value]. So we figure out what register + * the bit is in, read it, OR in the new bit, then write + * back the new value. The register is determined by the + * upper 7 bits of the hash value and the bit within that + * register are determined by the lower 5 bits of the value. + */ + hash_reg = (hash_value >> 5) & 0x7F; + if (hw->mac_type == e1000_ich8lan) + hash_reg &= 0x1F; + + hash_bit = hash_value & 0x1F; + + mta = E1000_READ_REG_ARRAY(hw, MTA, hash_reg); + + mta |= (1 << hash_bit); + + /* If we are on an 82544 and we are trying to write an odd offset + * in the MTA, save off the previous entry before writing and + * restore the old value after writing. + */ + if ((hw->mac_type == e1000_82544) && ((hash_reg & 0x1) == 1)) { + temp = E1000_READ_REG_ARRAY(hw, MTA, (hash_reg - 1)); + E1000_WRITE_REG_ARRAY(hw, MTA, hash_reg, mta); + E1000_WRITE_FLUSH(hw); + E1000_WRITE_REG_ARRAY(hw, MTA, (hash_reg - 1), temp); + E1000_WRITE_FLUSH(hw); + } else { + E1000_WRITE_REG_ARRAY(hw, MTA, hash_reg, mta); + E1000_WRITE_FLUSH(hw); + } +} + +/****************************************************************************** + * Puts an ethernet address into a receive address register. + * + * hw - Struct containing variables accessed by shared code + * addr - Address to put into receive address register + * index - Receive address register to write + *****************************************************************************/ +void +e1000_rar_set(struct e1000_hw *hw, + uint8_t *addr, + uint32_t index) +{ + uint32_t rar_low, rar_high; + + /* HW expects these in little endian so we reverse the byte order + * from network order (big endian) to little endian + */ + rar_low = ((uint32_t) addr[0] | + ((uint32_t) addr[1] << 8) | + ((uint32_t) addr[2] << 16) | ((uint32_t) addr[3] << 24)); + rar_high = ((uint32_t) addr[4] | ((uint32_t) addr[5] << 8)); + + /* Disable Rx and flush all Rx frames before enabling RSS to avoid Rx + * unit hang. + * + * Description: + * If there are any Rx frames queued up or otherwise present in the HW + * before RSS is enabled, and then we enable RSS, the HW Rx unit will + * hang. To work around this issue, we have to disable receives and + * flush out all Rx frames before we enable RSS. To do so, we modify we + * redirect all Rx traffic to manageability and then reset the HW. + * This flushes away Rx frames, and (since the redirections to + * manageability persists across resets) keeps new ones from coming in + * while we work. Then, we clear the Address Valid AV bit for all MAC + * addresses and undo the re-direction to manageability. + * Now, frames are coming in again, but the MAC won't accept them, so + * far so good. We now proceed to initialize RSS (if necessary) and + * configure the Rx unit. Last, we re-enable the AV bits and continue + * on our merry way. + */ + switch (hw->mac_type) { + case e1000_82571: + case e1000_82572: + case e1000_80003es2lan: + if (hw->leave_av_bit_off == TRUE) + break; + default: + /* Indicate to hardware the Address is Valid. */ + rar_high |= E1000_RAH_AV; + break; + } + + E1000_WRITE_REG_ARRAY(hw, RA, (index << 1), rar_low); + E1000_WRITE_FLUSH(hw); + E1000_WRITE_REG_ARRAY(hw, RA, ((index << 1) + 1), rar_high); + E1000_WRITE_FLUSH(hw); +} + +/****************************************************************************** + * Writes a value to the specified offset in the VLAN filter table. + * + * hw - Struct containing variables accessed by shared code + * offset - Offset in VLAN filer table to write + * value - Value to write into VLAN filter table + *****************************************************************************/ +void +e1000_write_vfta(struct e1000_hw *hw, + uint32_t offset, + uint32_t value) +{ + uint32_t temp; + + if (hw->mac_type == e1000_ich8lan) + return; + + if ((hw->mac_type == e1000_82544) && ((offset & 0x1) == 1)) { + temp = E1000_READ_REG_ARRAY(hw, VFTA, (offset - 1)); + E1000_WRITE_REG_ARRAY(hw, VFTA, offset, value); + E1000_WRITE_FLUSH(hw); + E1000_WRITE_REG_ARRAY(hw, VFTA, (offset - 1), temp); + E1000_WRITE_FLUSH(hw); + } else { + E1000_WRITE_REG_ARRAY(hw, VFTA, offset, value); + E1000_WRITE_FLUSH(hw); + } +} + +/****************************************************************************** + * Clears the VLAN filer table + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static void +e1000_clear_vfta(struct e1000_hw *hw) +{ + uint32_t offset; + uint32_t vfta_value = 0; + uint32_t vfta_offset = 0; + uint32_t vfta_bit_in_reg = 0; + + if (hw->mac_type == e1000_ich8lan) + return; + + if (hw->mac_type == e1000_82573) { + if (hw->mng_cookie.vlan_id != 0) { + /* The VFTA is a 4096b bit-field, each identifying a single VLAN + * ID. The following operations determine which 32b entry + * (i.e. offset) into the array we want to set the VLAN ID + * (i.e. bit) of the manageability unit. */ + vfta_offset = (hw->mng_cookie.vlan_id >> + E1000_VFTA_ENTRY_SHIFT) & + E1000_VFTA_ENTRY_MASK; + vfta_bit_in_reg = 1 << (hw->mng_cookie.vlan_id & + E1000_VFTA_ENTRY_BIT_SHIFT_MASK); + } + } + for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) { + /* If the offset we want to clear is the same offset of the + * manageability VLAN ID, then clear all bits except that of the + * manageability unit */ + vfta_value = (offset == vfta_offset) ? vfta_bit_in_reg : 0; + E1000_WRITE_REG_ARRAY(hw, VFTA, offset, vfta_value); + E1000_WRITE_FLUSH(hw); + } +} + +static int32_t +e1000_id_led_init(struct e1000_hw * hw) +{ + uint32_t ledctl; + const uint32_t ledctl_mask = 0x000000FF; + const uint32_t ledctl_on = E1000_LEDCTL_MODE_LED_ON; + const uint32_t ledctl_off = E1000_LEDCTL_MODE_LED_OFF; + uint16_t eeprom_data, i, temp; + const uint16_t led_mask = 0x0F; + + DEBUGFUNC("e1000_id_led_init"); + + if (hw->mac_type < e1000_82540) { + /* Nothing to do */ + return E1000_SUCCESS; + } + + ledctl = E1000_READ_REG(hw, LEDCTL); + hw->ledctl_default = ledctl; + hw->ledctl_mode1 = hw->ledctl_default; + hw->ledctl_mode2 = hw->ledctl_default; + + if (e1000_read_eeprom(hw, EEPROM_ID_LED_SETTINGS, 1, &eeprom_data) < 0) { + DEBUGOUT("EEPROM Read Error\n"); + return -E1000_ERR_EEPROM; + } + + if ((hw->mac_type == e1000_82573) && + (eeprom_data == ID_LED_RESERVED_82573)) + eeprom_data = ID_LED_DEFAULT_82573; + else if ((eeprom_data == ID_LED_RESERVED_0000) || + (eeprom_data == ID_LED_RESERVED_FFFF)) { + if (hw->mac_type == e1000_ich8lan) + eeprom_data = ID_LED_DEFAULT_ICH8LAN; + else + eeprom_data = ID_LED_DEFAULT; + } + + for (i = 0; i < 4; i++) { + temp = (eeprom_data >> (i << 2)) & led_mask; + switch (temp) { + case ID_LED_ON1_DEF2: + case ID_LED_ON1_ON2: + case ID_LED_ON1_OFF2: + hw->ledctl_mode1 &= ~(ledctl_mask << (i << 3)); + hw->ledctl_mode1 |= ledctl_on << (i << 3); + break; + case ID_LED_OFF1_DEF2: + case ID_LED_OFF1_ON2: + case ID_LED_OFF1_OFF2: + hw->ledctl_mode1 &= ~(ledctl_mask << (i << 3)); + hw->ledctl_mode1 |= ledctl_off << (i << 3); + break; + default: + /* Do nothing */ + break; + } + switch (temp) { + case ID_LED_DEF1_ON2: + case ID_LED_ON1_ON2: + case ID_LED_OFF1_ON2: + hw->ledctl_mode2 &= ~(ledctl_mask << (i << 3)); + hw->ledctl_mode2 |= ledctl_on << (i << 3); + break; + case ID_LED_DEF1_OFF2: + case ID_LED_ON1_OFF2: + case ID_LED_OFF1_OFF2: + hw->ledctl_mode2 &= ~(ledctl_mask << (i << 3)); + hw->ledctl_mode2 |= ledctl_off << (i << 3); + break; + default: + /* Do nothing */ + break; + } + } + return E1000_SUCCESS; +} + +/****************************************************************************** + * Prepares SW controlable LED for use and saves the current state of the LED. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +e1000_setup_led(struct e1000_hw *hw) +{ + uint32_t ledctl; + int32_t ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_setup_led"); + + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + case e1000_82543: + case e1000_82544: + /* No setup necessary */ + break; + case e1000_82541: + case e1000_82547: + case e1000_82541_rev_2: + case e1000_82547_rev_2: + /* Turn off PHY Smart Power Down (if enabled) */ + ret_val = e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO, + &hw->phy_spd_default); + if (ret_val) + return ret_val; + ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, + (uint16_t)(hw->phy_spd_default & + ~IGP01E1000_GMII_SPD)); + if (ret_val) + return ret_val; + /* Fall Through */ + default: + if (hw->media_type == e1000_media_type_fiber) { + ledctl = E1000_READ_REG(hw, LEDCTL); + /* Save current LEDCTL settings */ + hw->ledctl_default = ledctl; + /* Turn off LED0 */ + ledctl &= ~(E1000_LEDCTL_LED0_IVRT | + E1000_LEDCTL_LED0_BLINK | + E1000_LEDCTL_LED0_MODE_MASK); + ledctl |= (E1000_LEDCTL_MODE_LED_OFF << + E1000_LEDCTL_LED0_MODE_SHIFT); + E1000_WRITE_REG(hw, LEDCTL, ledctl); + } else if (hw->media_type == e1000_media_type_copper) + E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode1); + break; + } + + return E1000_SUCCESS; +} + + +/****************************************************************************** + * Used on 82571 and later Si that has LED blink bits. + * Callers must use their own timer and should have already called + * e1000_id_led_init() + * Call e1000_cleanup led() to stop blinking + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +e1000_blink_led_start(struct e1000_hw *hw) +{ + int16_t i; + uint32_t ledctl_blink = 0; + + DEBUGFUNC("e1000_id_led_blink_on"); + + if (hw->mac_type < e1000_82571) { + /* Nothing to do */ + return E1000_SUCCESS; + } + if (hw->media_type == e1000_media_type_fiber) { + /* always blink LED0 for PCI-E fiber */ + ledctl_blink = E1000_LEDCTL_LED0_BLINK | + (E1000_LEDCTL_MODE_LED_ON << E1000_LEDCTL_LED0_MODE_SHIFT); + } else { + /* set the blink bit for each LED that's "on" (0x0E) in ledctl_mode2 */ + ledctl_blink = hw->ledctl_mode2; + for (i=0; i < 4; i++) + if (((hw->ledctl_mode2 >> (i * 8)) & 0xFF) == + E1000_LEDCTL_MODE_LED_ON) + ledctl_blink |= (E1000_LEDCTL_LED0_BLINK << (i * 8)); + } + + E1000_WRITE_REG(hw, LEDCTL, ledctl_blink); + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Restores the saved state of the SW controlable LED. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +e1000_cleanup_led(struct e1000_hw *hw) +{ + int32_t ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_cleanup_led"); + + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + case e1000_82543: + case e1000_82544: + /* No cleanup necessary */ + break; + case e1000_82541: + case e1000_82547: + case e1000_82541_rev_2: + case e1000_82547_rev_2: + /* Turn on PHY Smart Power Down (if previously enabled) */ + ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, + hw->phy_spd_default); + if (ret_val) + return ret_val; + /* Fall Through */ + default: + if (hw->phy_type == e1000_phy_ife) { + e1000_write_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED, 0); + break; + } + /* Restore LEDCTL settings */ + E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_default); + break; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Turns on the software controllable LED + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +e1000_led_on(struct e1000_hw *hw) +{ + uint32_t ctrl = E1000_READ_REG(hw, CTRL); + + DEBUGFUNC("e1000_led_on"); + + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + case e1000_82543: + /* Set SW Defineable Pin 0 to turn on the LED */ + ctrl |= E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + break; + case e1000_82544: + if (hw->media_type == e1000_media_type_fiber) { + /* Set SW Defineable Pin 0 to turn on the LED */ + ctrl |= E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } else { + /* Clear SW Defineable Pin 0 to turn on the LED */ + ctrl &= ~E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } + break; + default: + if (hw->media_type == e1000_media_type_fiber) { + /* Clear SW Defineable Pin 0 to turn on the LED */ + ctrl &= ~E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } else if (hw->phy_type == e1000_phy_ife) { + e1000_write_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED, + (IFE_PSCL_PROBE_MODE | IFE_PSCL_PROBE_LEDS_ON)); + } else if (hw->media_type == e1000_media_type_copper) { + E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode2); + return E1000_SUCCESS; + } + break; + } + + E1000_WRITE_REG(hw, CTRL, ctrl); + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Turns off the software controllable LED + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +e1000_led_off(struct e1000_hw *hw) +{ + uint32_t ctrl = E1000_READ_REG(hw, CTRL); + + DEBUGFUNC("e1000_led_off"); + + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + case e1000_82543: + /* Clear SW Defineable Pin 0 to turn off the LED */ + ctrl &= ~E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + break; + case e1000_82544: + if (hw->media_type == e1000_media_type_fiber) { + /* Clear SW Defineable Pin 0 to turn off the LED */ + ctrl &= ~E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } else { + /* Set SW Defineable Pin 0 to turn off the LED */ + ctrl |= E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } + break; + default: + if (hw->media_type == e1000_media_type_fiber) { + /* Set SW Defineable Pin 0 to turn off the LED */ + ctrl |= E1000_CTRL_SWDPIN0; + ctrl |= E1000_CTRL_SWDPIO0; + } else if (hw->phy_type == e1000_phy_ife) { + e1000_write_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED, + (IFE_PSCL_PROBE_MODE | IFE_PSCL_PROBE_LEDS_OFF)); + } else if (hw->media_type == e1000_media_type_copper) { + E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode1); + return E1000_SUCCESS; + } + break; + } + + E1000_WRITE_REG(hw, CTRL, ctrl); + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Clears all hardware statistics counters. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static void +e1000_clear_hw_cntrs(struct e1000_hw *hw) +{ + volatile uint32_t temp; + + temp = E1000_READ_REG(hw, CRCERRS); + temp = E1000_READ_REG(hw, SYMERRS); + temp = E1000_READ_REG(hw, MPC); + temp = E1000_READ_REG(hw, SCC); + temp = E1000_READ_REG(hw, ECOL); + temp = E1000_READ_REG(hw, MCC); + temp = E1000_READ_REG(hw, LATECOL); + temp = E1000_READ_REG(hw, COLC); + temp = E1000_READ_REG(hw, DC); + temp = E1000_READ_REG(hw, SEC); + temp = E1000_READ_REG(hw, RLEC); + temp = E1000_READ_REG(hw, XONRXC); + temp = E1000_READ_REG(hw, XONTXC); + temp = E1000_READ_REG(hw, XOFFRXC); + temp = E1000_READ_REG(hw, XOFFTXC); + temp = E1000_READ_REG(hw, FCRUC); + + if (hw->mac_type != e1000_ich8lan) { + temp = E1000_READ_REG(hw, PRC64); + temp = E1000_READ_REG(hw, PRC127); + temp = E1000_READ_REG(hw, PRC255); + temp = E1000_READ_REG(hw, PRC511); + temp = E1000_READ_REG(hw, PRC1023); + temp = E1000_READ_REG(hw, PRC1522); + } + + temp = E1000_READ_REG(hw, GPRC); + temp = E1000_READ_REG(hw, BPRC); + temp = E1000_READ_REG(hw, MPRC); + temp = E1000_READ_REG(hw, GPTC); + temp = E1000_READ_REG(hw, GORCL); + temp = E1000_READ_REG(hw, GORCH); + temp = E1000_READ_REG(hw, GOTCL); + temp = E1000_READ_REG(hw, GOTCH); + temp = E1000_READ_REG(hw, RNBC); + temp = E1000_READ_REG(hw, RUC); + temp = E1000_READ_REG(hw, RFC); + temp = E1000_READ_REG(hw, ROC); + temp = E1000_READ_REG(hw, RJC); + temp = E1000_READ_REG(hw, TORL); + temp = E1000_READ_REG(hw, TORH); + temp = E1000_READ_REG(hw, TOTL); + temp = E1000_READ_REG(hw, TOTH); + temp = E1000_READ_REG(hw, TPR); + temp = E1000_READ_REG(hw, TPT); + + if (hw->mac_type != e1000_ich8lan) { + temp = E1000_READ_REG(hw, PTC64); + temp = E1000_READ_REG(hw, PTC127); + temp = E1000_READ_REG(hw, PTC255); + temp = E1000_READ_REG(hw, PTC511); + temp = E1000_READ_REG(hw, PTC1023); + temp = E1000_READ_REG(hw, PTC1522); + } + + temp = E1000_READ_REG(hw, MPTC); + temp = E1000_READ_REG(hw, BPTC); + + if (hw->mac_type < e1000_82543) return; + + temp = E1000_READ_REG(hw, ALGNERRC); + temp = E1000_READ_REG(hw, RXERRC); + temp = E1000_READ_REG(hw, TNCRS); + temp = E1000_READ_REG(hw, CEXTERR); + temp = E1000_READ_REG(hw, TSCTC); + temp = E1000_READ_REG(hw, TSCTFC); + + if (hw->mac_type <= e1000_82544) return; + + temp = E1000_READ_REG(hw, MGTPRC); + temp = E1000_READ_REG(hw, MGTPDC); + temp = E1000_READ_REG(hw, MGTPTC); + + if (hw->mac_type <= e1000_82547_rev_2) return; + + temp = E1000_READ_REG(hw, IAC); + temp = E1000_READ_REG(hw, ICRXOC); + + if (hw->mac_type == e1000_ich8lan) return; + + temp = E1000_READ_REG(hw, ICRXPTC); + temp = E1000_READ_REG(hw, ICRXATC); + temp = E1000_READ_REG(hw, ICTXPTC); + temp = E1000_READ_REG(hw, ICTXATC); + temp = E1000_READ_REG(hw, ICTXQEC); + temp = E1000_READ_REG(hw, ICTXQMTC); + temp = E1000_READ_REG(hw, ICRXDMTC); +} + +/****************************************************************************** + * Resets Adaptive IFS to its default state. + * + * hw - Struct containing variables accessed by shared code + * + * Call this after e1000_init_hw. You may override the IFS defaults by setting + * hw->ifs_params_forced to TRUE. However, you must initialize hw-> + * current_ifs_val, ifs_min_val, ifs_max_val, ifs_step_size, and ifs_ratio + * before calling this function. + *****************************************************************************/ +void +e1000_reset_adaptive(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_reset_adaptive"); + + if (hw->adaptive_ifs) { + if (!hw->ifs_params_forced) { + hw->current_ifs_val = 0; + hw->ifs_min_val = IFS_MIN; + hw->ifs_max_val = IFS_MAX; + hw->ifs_step_size = IFS_STEP; + hw->ifs_ratio = IFS_RATIO; + } + hw->in_ifs_mode = FALSE; + E1000_WRITE_REG(hw, AIT, 0); + } else { + DEBUGOUT("Not in Adaptive IFS mode!\n"); + } +} + +/****************************************************************************** + * Called during the callback/watchdog routine to update IFS value based on + * the ratio of transmits to collisions. + * + * hw - Struct containing variables accessed by shared code + * tx_packets - Number of transmits since last callback + * total_collisions - Number of collisions since last callback + *****************************************************************************/ +void +e1000_update_adaptive(struct e1000_hw *hw) +{ + DEBUGFUNC("e1000_update_adaptive"); + + if (hw->adaptive_ifs) { + if ((hw->collision_delta * hw->ifs_ratio) > hw->tx_packet_delta) { + if (hw->tx_packet_delta > MIN_NUM_XMITS) { + hw->in_ifs_mode = TRUE; + if (hw->current_ifs_val < hw->ifs_max_val) { + if (hw->current_ifs_val == 0) + hw->current_ifs_val = hw->ifs_min_val; + else + hw->current_ifs_val += hw->ifs_step_size; + E1000_WRITE_REG(hw, AIT, hw->current_ifs_val); + } + } + } else { + if (hw->in_ifs_mode && (hw->tx_packet_delta <= MIN_NUM_XMITS)) { + hw->current_ifs_val = 0; + hw->in_ifs_mode = FALSE; + E1000_WRITE_REG(hw, AIT, 0); + } + } + } else { + DEBUGOUT("Not in Adaptive IFS mode!\n"); + } +} + +/****************************************************************************** + * Adjusts the statistic counters when a frame is accepted by TBI_ACCEPT + * + * hw - Struct containing variables accessed by shared code + * frame_len - The length of the frame in question + * mac_addr - The Ethernet destination address of the frame in question + *****************************************************************************/ +void +e1000_tbi_adjust_stats(struct e1000_hw *hw, + struct e1000_hw_stats *stats, + uint32_t frame_len, + uint8_t *mac_addr) +{ + uint64_t carry_bit; + + /* First adjust the frame length. */ + frame_len--; + /* We need to adjust the statistics counters, since the hardware + * counters overcount this packet as a CRC error and undercount + * the packet as a good packet + */ + /* This packet should not be counted as a CRC error. */ + stats->crcerrs--; + /* This packet does count as a Good Packet Received. */ + stats->gprc++; + + /* Adjust the Good Octets received counters */ + carry_bit = 0x80000000 & stats->gorcl; + stats->gorcl += frame_len; + /* If the high bit of Gorcl (the low 32 bits of the Good Octets + * Received Count) was one before the addition, + * AND it is zero after, then we lost the carry out, + * need to add one to Gorch (Good Octets Received Count High). + * This could be simplified if all environments supported + * 64-bit integers. + */ + if (carry_bit && ((stats->gorcl & 0x80000000) == 0)) + stats->gorch++; + /* Is this a broadcast or multicast? Check broadcast first, + * since the test for a multicast frame will test positive on + * a broadcast frame. + */ + if ((mac_addr[0] == (uint8_t) 0xff) && (mac_addr[1] == (uint8_t) 0xff)) + /* Broadcast packet */ + stats->bprc++; + else if (*mac_addr & 0x01) + /* Multicast packet */ + stats->mprc++; + + if (frame_len == hw->max_frame_size) { + /* In this case, the hardware has overcounted the number of + * oversize frames. + */ + if (stats->roc > 0) + stats->roc--; + } + + /* Adjust the bin counters when the extra byte put the frame in the + * wrong bin. Remember that the frame_len was adjusted above. + */ + if (frame_len == 64) { + stats->prc64++; + stats->prc127--; + } else if (frame_len == 127) { + stats->prc127++; + stats->prc255--; + } else if (frame_len == 255) { + stats->prc255++; + stats->prc511--; + } else if (frame_len == 511) { + stats->prc511++; + stats->prc1023--; + } else if (frame_len == 1023) { + stats->prc1023++; + stats->prc1522--; + } else if (frame_len == 1522) { + stats->prc1522++; + } +} + +/****************************************************************************** + * Gets the current PCI bus type, speed, and width of the hardware + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +void +e1000_get_bus_info(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t pci_ex_link_status; + uint32_t status; + + switch (hw->mac_type) { + case e1000_82542_rev2_0: + case e1000_82542_rev2_1: + hw->bus_type = e1000_bus_type_pci; + hw->bus_speed = e1000_bus_speed_unknown; + hw->bus_width = e1000_bus_width_unknown; + break; + case e1000_82571: + case e1000_82572: + case e1000_82573: + case e1000_80003es2lan: + hw->bus_type = e1000_bus_type_pci_express; + hw->bus_speed = e1000_bus_speed_2500; + ret_val = e1000_read_pcie_cap_reg(hw, + PCI_EX_LINK_STATUS, + &pci_ex_link_status); + if (ret_val) + hw->bus_width = e1000_bus_width_unknown; + else + hw->bus_width = (pci_ex_link_status & PCI_EX_LINK_WIDTH_MASK) >> + PCI_EX_LINK_WIDTH_SHIFT; + break; + case e1000_ich8lan: + hw->bus_type = e1000_bus_type_pci_express; + hw->bus_speed = e1000_bus_speed_2500; + hw->bus_width = e1000_bus_width_pciex_1; + break; + default: + status = E1000_READ_REG(hw, STATUS); + hw->bus_type = (status & E1000_STATUS_PCIX_MODE) ? + e1000_bus_type_pcix : e1000_bus_type_pci; + + if (hw->device_id == E1000_DEV_ID_82546EB_QUAD_COPPER) { + hw->bus_speed = (hw->bus_type == e1000_bus_type_pci) ? + e1000_bus_speed_66 : e1000_bus_speed_120; + } else if (hw->bus_type == e1000_bus_type_pci) { + hw->bus_speed = (status & E1000_STATUS_PCI66) ? + e1000_bus_speed_66 : e1000_bus_speed_33; + } else { + switch (status & E1000_STATUS_PCIX_SPEED) { + case E1000_STATUS_PCIX_SPEED_66: + hw->bus_speed = e1000_bus_speed_66; + break; + case E1000_STATUS_PCIX_SPEED_100: + hw->bus_speed = e1000_bus_speed_100; + break; + case E1000_STATUS_PCIX_SPEED_133: + hw->bus_speed = e1000_bus_speed_133; + break; + default: + hw->bus_speed = e1000_bus_speed_reserved; + break; + } + } + hw->bus_width = (status & E1000_STATUS_BUS64) ? + e1000_bus_width_64 : e1000_bus_width_32; + break; + } +} + +/****************************************************************************** + * Writes a value to one of the devices registers using port I/O (as opposed to + * memory mapped I/O). Only 82544 and newer devices support port I/O. + * + * hw - Struct containing variables accessed by shared code + * offset - offset to write to + * value - value to write + *****************************************************************************/ +static void +e1000_write_reg_io(struct e1000_hw *hw, + uint32_t offset, + uint32_t value) +{ + unsigned long io_addr = hw->io_base; + unsigned long io_data = hw->io_base + 4; + + e1000_io_write(hw, io_addr, offset); + e1000_io_write(hw, io_data, value); +} + +/****************************************************************************** + * Estimates the cable length. + * + * hw - Struct containing variables accessed by shared code + * min_length - The estimated minimum length + * max_length - The estimated maximum length + * + * returns: - E1000_ERR_XXX + * E1000_SUCCESS + * + * This function always returns a ranged length (minimum & maximum). + * So for M88 phy's, this function interprets the one value returned from the + * register to the minimum and maximum range. + * For IGP phy's, the function calculates the range by the AGC registers. + *****************************************************************************/ +static int32_t +e1000_get_cable_length(struct e1000_hw *hw, + uint16_t *min_length, + uint16_t *max_length) +{ + int32_t ret_val; + uint16_t agc_value = 0; + uint16_t i, phy_data; + uint16_t cable_length; + + DEBUGFUNC("e1000_get_cable_length"); + + *min_length = *max_length = 0; + + /* Use old method for Phy older than IGP */ + if (hw->phy_type == e1000_phy_m88) { + + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, + &phy_data); + if (ret_val) + return ret_val; + cable_length = (phy_data & M88E1000_PSSR_CABLE_LENGTH) >> + M88E1000_PSSR_CABLE_LENGTH_SHIFT; + + /* Convert the enum value to ranged values */ + switch (cable_length) { + case e1000_cable_length_50: + *min_length = 0; + *max_length = e1000_igp_cable_length_50; + break; + case e1000_cable_length_50_80: + *min_length = e1000_igp_cable_length_50; + *max_length = e1000_igp_cable_length_80; + break; + case e1000_cable_length_80_110: + *min_length = e1000_igp_cable_length_80; + *max_length = e1000_igp_cable_length_110; + break; + case e1000_cable_length_110_140: + *min_length = e1000_igp_cable_length_110; + *max_length = e1000_igp_cable_length_140; + break; + case e1000_cable_length_140: + *min_length = e1000_igp_cable_length_140; + *max_length = e1000_igp_cable_length_170; + break; + default: + return -E1000_ERR_PHY; + break; + } + } else if (hw->phy_type == e1000_phy_gg82563) { + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_DSP_DISTANCE, + &phy_data); + if (ret_val) + return ret_val; + cable_length = phy_data & GG82563_DSPD_CABLE_LENGTH; + + switch (cable_length) { + case e1000_gg_cable_length_60: + *min_length = 0; + *max_length = e1000_igp_cable_length_60; + break; + case e1000_gg_cable_length_60_115: + *min_length = e1000_igp_cable_length_60; + *max_length = e1000_igp_cable_length_115; + break; + case e1000_gg_cable_length_115_150: + *min_length = e1000_igp_cable_length_115; + *max_length = e1000_igp_cable_length_150; + break; + case e1000_gg_cable_length_150: + *min_length = e1000_igp_cable_length_150; + *max_length = e1000_igp_cable_length_180; + break; + default: + return -E1000_ERR_PHY; + break; + } + } else if (hw->phy_type == e1000_phy_igp) { /* For IGP PHY */ + uint16_t cur_agc_value; + uint16_t min_agc_value = IGP01E1000_AGC_LENGTH_TABLE_SIZE; + uint16_t agc_reg_array[IGP01E1000_PHY_CHANNEL_NUM] = + {IGP01E1000_PHY_AGC_A, + IGP01E1000_PHY_AGC_B, + IGP01E1000_PHY_AGC_C, + IGP01E1000_PHY_AGC_D}; + /* Read the AGC registers for all channels */ + for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) { + + ret_val = e1000_read_phy_reg(hw, agc_reg_array[i], &phy_data); + if (ret_val) + return ret_val; + + cur_agc_value = phy_data >> IGP01E1000_AGC_LENGTH_SHIFT; + + /* Value bound check. */ + if ((cur_agc_value >= IGP01E1000_AGC_LENGTH_TABLE_SIZE - 1) || + (cur_agc_value == 0)) + return -E1000_ERR_PHY; + + agc_value += cur_agc_value; + + /* Update minimal AGC value. */ + if (min_agc_value > cur_agc_value) + min_agc_value = cur_agc_value; + } + + /* Remove the minimal AGC result for length < 50m */ + if (agc_value < IGP01E1000_PHY_CHANNEL_NUM * e1000_igp_cable_length_50) { + agc_value -= min_agc_value; + + /* Get the average length of the remaining 3 channels */ + agc_value /= (IGP01E1000_PHY_CHANNEL_NUM - 1); + } else { + /* Get the average length of all the 4 channels. */ + agc_value /= IGP01E1000_PHY_CHANNEL_NUM; + } + + /* Set the range of the calculated length. */ + *min_length = ((e1000_igp_cable_length_table[agc_value] - + IGP01E1000_AGC_RANGE) > 0) ? + (e1000_igp_cable_length_table[agc_value] - + IGP01E1000_AGC_RANGE) : 0; + *max_length = e1000_igp_cable_length_table[agc_value] + + IGP01E1000_AGC_RANGE; + } else if (hw->phy_type == e1000_phy_igp_2 || + hw->phy_type == e1000_phy_igp_3) { + uint16_t cur_agc_index, max_agc_index = 0; + uint16_t min_agc_index = IGP02E1000_AGC_LENGTH_TABLE_SIZE - 1; + uint16_t agc_reg_array[IGP02E1000_PHY_CHANNEL_NUM] = + {IGP02E1000_PHY_AGC_A, + IGP02E1000_PHY_AGC_B, + IGP02E1000_PHY_AGC_C, + IGP02E1000_PHY_AGC_D}; + /* Read the AGC registers for all channels */ + for (i = 0; i < IGP02E1000_PHY_CHANNEL_NUM; i++) { + ret_val = e1000_read_phy_reg(hw, agc_reg_array[i], &phy_data); + if (ret_val) + return ret_val; + + /* Getting bits 15:9, which represent the combination of course and + * fine gain values. The result is a number that can be put into + * the lookup table to obtain the approximate cable length. */ + cur_agc_index = (phy_data >> IGP02E1000_AGC_LENGTH_SHIFT) & + IGP02E1000_AGC_LENGTH_MASK; + + /* Array index bound check. */ + if ((cur_agc_index >= IGP02E1000_AGC_LENGTH_TABLE_SIZE) || + (cur_agc_index == 0)) + return -E1000_ERR_PHY; + + /* Remove min & max AGC values from calculation. */ + if (e1000_igp_2_cable_length_table[min_agc_index] > + e1000_igp_2_cable_length_table[cur_agc_index]) + min_agc_index = cur_agc_index; + if (e1000_igp_2_cable_length_table[max_agc_index] < + e1000_igp_2_cable_length_table[cur_agc_index]) + max_agc_index = cur_agc_index; + + agc_value += e1000_igp_2_cable_length_table[cur_agc_index]; + } + + agc_value -= (e1000_igp_2_cable_length_table[min_agc_index] + + e1000_igp_2_cable_length_table[max_agc_index]); + agc_value /= (IGP02E1000_PHY_CHANNEL_NUM - 2); + + /* Calculate cable length with the error range of +/- 10 meters. */ + *min_length = ((agc_value - IGP02E1000_AGC_RANGE) > 0) ? + (agc_value - IGP02E1000_AGC_RANGE) : 0; + *max_length = agc_value + IGP02E1000_AGC_RANGE; + } + + return E1000_SUCCESS; +} + +/****************************************************************************** + * Check the cable polarity + * + * hw - Struct containing variables accessed by shared code + * polarity - output parameter : 0 - Polarity is not reversed + * 1 - Polarity is reversed. + * + * returns: - E1000_ERR_XXX + * E1000_SUCCESS + * + * For phy's older then IGP, this function simply reads the polarity bit in the + * Phy Status register. For IGP phy's, this bit is valid only if link speed is + * 10 Mbps. If the link speed is 100 Mbps there is no polarity so this bit will + * return 0. If the link speed is 1000 Mbps the polarity status is in the + * IGP01E1000_PHY_PCS_INIT_REG. + *****************************************************************************/ +static int32_t +e1000_check_polarity(struct e1000_hw *hw, + e1000_rev_polarity *polarity) +{ + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("e1000_check_polarity"); + + if ((hw->phy_type == e1000_phy_m88) || + (hw->phy_type == e1000_phy_gg82563)) { + /* return the Polarity bit in the Status register. */ + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, + &phy_data); + if (ret_val) + return ret_val; + *polarity = ((phy_data & M88E1000_PSSR_REV_POLARITY) >> + M88E1000_PSSR_REV_POLARITY_SHIFT) ? + e1000_rev_polarity_reversed : e1000_rev_polarity_normal; + + } else if (hw->phy_type == e1000_phy_igp || + hw->phy_type == e1000_phy_igp_3 || + hw->phy_type == e1000_phy_igp_2) { + /* Read the Status register to check the speed */ + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, + &phy_data); + if (ret_val) + return ret_val; + + /* If speed is 1000 Mbps, must read the IGP01E1000_PHY_PCS_INIT_REG to + * find the polarity status */ + if ((phy_data & IGP01E1000_PSSR_SPEED_MASK) == + IGP01E1000_PSSR_SPEED_1000MBPS) { + + /* Read the GIG initialization PCS register (0x00B4) */ + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PCS_INIT_REG, + &phy_data); + if (ret_val) + return ret_val; + + /* Check the polarity bits */ + *polarity = (phy_data & IGP01E1000_PHY_POLARITY_MASK) ? + e1000_rev_polarity_reversed : e1000_rev_polarity_normal; + } else { + /* For 10 Mbps, read the polarity bit in the status register. (for + * 100 Mbps this bit is always 0) */ + *polarity = (phy_data & IGP01E1000_PSSR_POLARITY_REVERSED) ? + e1000_rev_polarity_reversed : e1000_rev_polarity_normal; + } + } else if (hw->phy_type == e1000_phy_ife) { + ret_val = e1000_read_phy_reg(hw, IFE_PHY_EXTENDED_STATUS_CONTROL, + &phy_data); + if (ret_val) + return ret_val; + *polarity = ((phy_data & IFE_PESC_POLARITY_REVERSED) >> + IFE_PESC_POLARITY_REVERSED_SHIFT) ? + e1000_rev_polarity_reversed : e1000_rev_polarity_normal; + } + return E1000_SUCCESS; +} + +/****************************************************************************** + * Check if Downshift occured + * + * hw - Struct containing variables accessed by shared code + * downshift - output parameter : 0 - No Downshift ocured. + * 1 - Downshift ocured. + * + * returns: - E1000_ERR_XXX + * E1000_SUCCESS + * + * For phy's older then IGP, this function reads the Downshift bit in the Phy + * Specific Status register. For IGP phy's, it reads the Downgrade bit in the + * Link Health register. In IGP this bit is latched high, so the driver must + * read it immediately after link is established. + *****************************************************************************/ +static int32_t +e1000_check_downshift(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC("e1000_check_downshift"); + + if (hw->phy_type == e1000_phy_igp || + hw->phy_type == e1000_phy_igp_3 || + hw->phy_type == e1000_phy_igp_2) { + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_LINK_HEALTH, + &phy_data); + if (ret_val) + return ret_val; + + hw->speed_downgraded = (phy_data & IGP01E1000_PLHR_SS_DOWNGRADE) ? 1 : 0; + } else if ((hw->phy_type == e1000_phy_m88) || + (hw->phy_type == e1000_phy_gg82563)) { + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, + &phy_data); + if (ret_val) + return ret_val; + + hw->speed_downgraded = (phy_data & M88E1000_PSSR_DOWNSHIFT) >> + M88E1000_PSSR_DOWNSHIFT_SHIFT; + } else if (hw->phy_type == e1000_phy_ife) { + /* e1000_phy_ife supports 10/100 speed only */ + hw->speed_downgraded = FALSE; + } + + return E1000_SUCCESS; +} + +/***************************************************************************** + * + * 82541_rev_2 & 82547_rev_2 have the capability to configure the DSP when a + * gigabit link is achieved to improve link quality. + * + * hw: Struct containing variables accessed by shared code + * + * returns: - E1000_ERR_PHY if fail to read/write the PHY + * E1000_SUCCESS at any other case. + * + ****************************************************************************/ + +static int32_t +e1000_config_dsp_after_link_change(struct e1000_hw *hw, + boolean_t link_up) +{ + int32_t ret_val; + uint16_t phy_data, phy_saved_data, speed, duplex, i; + uint16_t dsp_reg_array[IGP01E1000_PHY_CHANNEL_NUM] = + {IGP01E1000_PHY_AGC_PARAM_A, + IGP01E1000_PHY_AGC_PARAM_B, + IGP01E1000_PHY_AGC_PARAM_C, + IGP01E1000_PHY_AGC_PARAM_D}; + uint16_t min_length, max_length; + + DEBUGFUNC("e1000_config_dsp_after_link_change"); + + if (hw->phy_type != e1000_phy_igp) + return E1000_SUCCESS; + + if (link_up) { + ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex); + if (ret_val) { + DEBUGOUT("Error getting link speed and duplex\n"); + return ret_val; + } + + if (speed == SPEED_1000) { + + ret_val = e1000_get_cable_length(hw, &min_length, &max_length); + if (ret_val) + return ret_val; + + if ((hw->dsp_config_state == e1000_dsp_config_enabled) && + min_length >= e1000_igp_cable_length_50) { + + for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) { + ret_val = e1000_read_phy_reg(hw, dsp_reg_array[i], + &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PHY_EDAC_MU_INDEX; + + ret_val = e1000_write_phy_reg(hw, dsp_reg_array[i], + phy_data); + if (ret_val) + return ret_val; + } + hw->dsp_config_state = e1000_dsp_config_activated; + } + + if ((hw->ffe_config_state == e1000_ffe_config_enabled) && + (min_length < e1000_igp_cable_length_50)) { + + uint16_t ffe_idle_err_timeout = FFE_IDLE_ERR_COUNT_TIMEOUT_20; + uint32_t idle_errs = 0; + + /* clear previous idle error counts */ + ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, + &phy_data); + if (ret_val) + return ret_val; + + for (i = 0; i < ffe_idle_err_timeout; i++) { + udelay(1000); + ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, + &phy_data); + if (ret_val) + return ret_val; + + idle_errs += (phy_data & SR_1000T_IDLE_ERROR_CNT); + if (idle_errs > SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT) { + hw->ffe_config_state = e1000_ffe_config_active; + + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_DSP_FFE, + IGP01E1000_PHY_DSP_FFE_CM_CP); + if (ret_val) + return ret_val; + break; + } + + if (idle_errs) + ffe_idle_err_timeout = FFE_IDLE_ERR_COUNT_TIMEOUT_100; + } + } + } + } else { + if (hw->dsp_config_state == e1000_dsp_config_activated) { + /* Save off the current value of register 0x2F5B to be restored at + * the end of the routines. */ + ret_val = e1000_read_phy_reg(hw, 0x2F5B, &phy_saved_data); + + if (ret_val) + return ret_val; + + /* Disable the PHY transmitter */ + ret_val = e1000_write_phy_reg(hw, 0x2F5B, 0x0003); + + if (ret_val) + return ret_val; + + mdelay(20); + + ret_val = e1000_write_phy_reg(hw, 0x0000, + IGP01E1000_IEEE_FORCE_GIGA); + if (ret_val) + return ret_val; + for (i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) { + ret_val = e1000_read_phy_reg(hw, dsp_reg_array[i], &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PHY_EDAC_MU_INDEX; + phy_data |= IGP01E1000_PHY_EDAC_SIGN_EXT_9_BITS; + + ret_val = e1000_write_phy_reg(hw,dsp_reg_array[i], phy_data); + if (ret_val) + return ret_val; + } + + ret_val = e1000_write_phy_reg(hw, 0x0000, + IGP01E1000_IEEE_RESTART_AUTONEG); + if (ret_val) + return ret_val; + + mdelay(20); + + /* Now enable the transmitter */ + ret_val = e1000_write_phy_reg(hw, 0x2F5B, phy_saved_data); + + if (ret_val) + return ret_val; + + hw->dsp_config_state = e1000_dsp_config_enabled; + } + + if (hw->ffe_config_state == e1000_ffe_config_active) { + /* Save off the current value of register 0x2F5B to be restored at + * the end of the routines. */ + ret_val = e1000_read_phy_reg(hw, 0x2F5B, &phy_saved_data); + + if (ret_val) + return ret_val; + + /* Disable the PHY transmitter */ + ret_val = e1000_write_phy_reg(hw, 0x2F5B, 0x0003); + + if (ret_val) + return ret_val; + + mdelay(20); + + ret_val = e1000_write_phy_reg(hw, 0x0000, + IGP01E1000_IEEE_FORCE_GIGA); + if (ret_val) + return ret_val; + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_DSP_FFE, + IGP01E1000_PHY_DSP_FFE_DEFAULT); + if (ret_val) + return ret_val; + + ret_val = e1000_write_phy_reg(hw, 0x0000, + IGP01E1000_IEEE_RESTART_AUTONEG); + if (ret_val) + return ret_val; + + mdelay(20); + + /* Now enable the transmitter */ + ret_val = e1000_write_phy_reg(hw, 0x2F5B, phy_saved_data); + + if (ret_val) + return ret_val; + + hw->ffe_config_state = e1000_ffe_config_enabled; + } + } + return E1000_SUCCESS; +} + +/***************************************************************************** + * Set PHY to class A mode + * Assumes the following operations will follow to enable the new class mode. + * 1. Do a PHY soft reset + * 2. Restart auto-negotiation or force link. + * + * hw - Struct containing variables accessed by shared code + ****************************************************************************/ +static int32_t +e1000_set_phy_mode(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t eeprom_data; + + DEBUGFUNC("e1000_set_phy_mode"); + + if ((hw->mac_type == e1000_82545_rev_3) && + (hw->media_type == e1000_media_type_copper)) { + ret_val = e1000_read_eeprom(hw, EEPROM_PHY_CLASS_WORD, 1, &eeprom_data); + if (ret_val) { + return ret_val; + } + + if ((eeprom_data != EEPROM_RESERVED_WORD) && + (eeprom_data & EEPROM_PHY_CLASS_A)) { + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x000B); + if (ret_val) + return ret_val; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0x8104); + if (ret_val) + return ret_val; + + hw->phy_reset_disable = FALSE; + } + } + + return E1000_SUCCESS; +} + +/***************************************************************************** + * + * This function sets the lplu state according to the active flag. When + * activating lplu this function also disables smart speed and vise versa. + * lplu will not be activated unless the device autonegotiation advertisment + * meets standards of either 10 or 10/100 or 10/100/1000 at all duplexes. + * hw: Struct containing variables accessed by shared code + * active - true to enable lplu false to disable lplu. + * + * returns: - E1000_ERR_PHY if fail to read/write the PHY + * E1000_SUCCESS at any other case. + * + ****************************************************************************/ + +static int32_t +e1000_set_d3_lplu_state(struct e1000_hw *hw, + boolean_t active) +{ + uint32_t phy_ctrl = 0; + int32_t ret_val; + uint16_t phy_data; + DEBUGFUNC("e1000_set_d3_lplu_state"); + + if (hw->phy_type != e1000_phy_igp && hw->phy_type != e1000_phy_igp_2 + && hw->phy_type != e1000_phy_igp_3) + return E1000_SUCCESS; + + /* During driver activity LPLU should not be used or it will attain link + * from the lowest speeds starting from 10Mbps. The capability is used for + * Dx transitions and states */ + if (hw->mac_type == e1000_82541_rev_2 || hw->mac_type == e1000_82547_rev_2) { + ret_val = e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO, &phy_data); + if (ret_val) + return ret_val; + } else if (hw->mac_type == e1000_ich8lan) { + /* MAC writes into PHY register based on the state transition + * and start auto-negotiation. SW driver can overwrite the settings + * in CSR PHY power control E1000_PHY_CTRL register. */ + phy_ctrl = E1000_READ_REG(hw, PHY_CTRL); + } else { + ret_val = e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, &phy_data); + if (ret_val) + return ret_val; + } + + if (!active) { + if (hw->mac_type == e1000_82541_rev_2 || + hw->mac_type == e1000_82547_rev_2) { + phy_data &= ~IGP01E1000_GMII_FLEX_SPD; + ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, phy_data); + if (ret_val) + return ret_val; + } else { + if (hw->mac_type == e1000_ich8lan) { + phy_ctrl &= ~E1000_PHY_CTRL_NOND0A_LPLU; + E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + } else { + phy_data &= ~IGP02E1000_PM_D3_LPLU; + ret_val = e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, + phy_data); + if (ret_val) + return ret_val; + } + } + + /* LPLU and SmartSpeed are mutually exclusive. LPLU is used during + * Dx states where the power conservation is most important. During + * driver activity we should enable SmartSpeed, so performance is + * maintained. */ + if (hw->smart_speed == e1000_smart_speed_on) { + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + &phy_data); + if (ret_val) + return ret_val; + + phy_data |= IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + phy_data); + if (ret_val) + return ret_val; + } else if (hw->smart_speed == e1000_smart_speed_off) { + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + phy_data); + if (ret_val) + return ret_val; + } + + } else if ((hw->autoneg_advertised == AUTONEG_ADVERTISE_SPEED_DEFAULT) || + (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_ALL ) || + (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_100_ALL)) { + + if (hw->mac_type == e1000_82541_rev_2 || + hw->mac_type == e1000_82547_rev_2) { + phy_data |= IGP01E1000_GMII_FLEX_SPD; + ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, phy_data); + if (ret_val) + return ret_val; + } else { + if (hw->mac_type == e1000_ich8lan) { + phy_ctrl |= E1000_PHY_CTRL_NOND0A_LPLU; + E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + } else { + phy_data |= IGP02E1000_PM_D3_LPLU; + ret_val = e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, + phy_data); + if (ret_val) + return ret_val; + } + } + + /* When LPLU is enabled we should disable SmartSpeed */ + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, phy_data); + if (ret_val) + return ret_val; + + } + return E1000_SUCCESS; +} + +/***************************************************************************** + * + * This function sets the lplu d0 state according to the active flag. When + * activating lplu this function also disables smart speed and vise versa. + * lplu will not be activated unless the device autonegotiation advertisment + * meets standards of either 10 or 10/100 or 10/100/1000 at all duplexes. + * hw: Struct containing variables accessed by shared code + * active - true to enable lplu false to disable lplu. + * + * returns: - E1000_ERR_PHY if fail to read/write the PHY + * E1000_SUCCESS at any other case. + * + ****************************************************************************/ + +static int32_t +e1000_set_d0_lplu_state(struct e1000_hw *hw, + boolean_t active) +{ + uint32_t phy_ctrl = 0; + int32_t ret_val; + uint16_t phy_data; + DEBUGFUNC("e1000_set_d0_lplu_state"); + + if (hw->mac_type <= e1000_82547_rev_2) + return E1000_SUCCESS; + + if (hw->mac_type == e1000_ich8lan) { + phy_ctrl = E1000_READ_REG(hw, PHY_CTRL); + } else { + ret_val = e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, &phy_data); + if (ret_val) + return ret_val; + } + + if (!active) { + if (hw->mac_type == e1000_ich8lan) { + phy_ctrl &= ~E1000_PHY_CTRL_D0A_LPLU; + E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + } else { + phy_data &= ~IGP02E1000_PM_D0_LPLU; + ret_val = e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, phy_data); + if (ret_val) + return ret_val; + } + + /* LPLU and SmartSpeed are mutually exclusive. LPLU is used during + * Dx states where the power conservation is most important. During + * driver activity we should enable SmartSpeed, so performance is + * maintained. */ + if (hw->smart_speed == e1000_smart_speed_on) { + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + &phy_data); + if (ret_val) + return ret_val; + + phy_data |= IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + phy_data); + if (ret_val) + return ret_val; + } else if (hw->smart_speed == e1000_smart_speed_off) { + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + phy_data); + if (ret_val) + return ret_val; + } + + + } else { + + if (hw->mac_type == e1000_ich8lan) { + phy_ctrl |= E1000_PHY_CTRL_D0A_LPLU; + E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + } else { + phy_data |= IGP02E1000_PM_D0_LPLU; + ret_val = e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, phy_data); + if (ret_val) + return ret_val; + } + + /* When LPLU is enabled we should disable SmartSpeed */ + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, phy_data); + if (ret_val) + return ret_val; + + } + return E1000_SUCCESS; +} + +/****************************************************************************** + * Change VCO speed register to improve Bit Error Rate performance of SERDES. + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +static int32_t +e1000_set_vco_speed(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t default_page = 0; + uint16_t phy_data; + + DEBUGFUNC("e1000_set_vco_speed"); + + switch (hw->mac_type) { + case e1000_82545_rev_3: + case e1000_82546_rev_3: + break; + default: + return E1000_SUCCESS; + } + + /* Set PHY register 30, page 5, bit 8 to 0 */ + + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, &default_page); + if (ret_val) + return ret_val; + + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0005); + if (ret_val) + return ret_val; + + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~M88E1000_PHY_VCO_REG_BIT8; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data); + if (ret_val) + return ret_val; + + /* Set PHY register 30, page 4, bit 11 to 1 */ + + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0004); + if (ret_val) + return ret_val; + + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= M88E1000_PHY_VCO_REG_BIT11; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data); + if (ret_val) + return ret_val; + + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, default_page); + if (ret_val) + return ret_val; + + return E1000_SUCCESS; +} + + +/***************************************************************************** + * This function reads the cookie from ARC ram. + * + * returns: - E1000_SUCCESS . + ****************************************************************************/ +static int32_t +e1000_host_if_read_cookie(struct e1000_hw * hw, uint8_t *buffer) +{ + uint8_t i; + uint32_t offset = E1000_MNG_DHCP_COOKIE_OFFSET; + uint8_t length = E1000_MNG_DHCP_COOKIE_LENGTH; + + length = (length >> 2); + offset = (offset >> 2); + + for (i = 0; i < length; i++) { + *((uint32_t *) buffer + i) = + E1000_READ_REG_ARRAY_DWORD(hw, HOST_IF, offset + i); + } + return E1000_SUCCESS; +} + + +/***************************************************************************** + * This function checks whether the HOST IF is enabled for command operaton + * and also checks whether the previous command is completed. + * It busy waits in case of previous command is not completed. + * + * returns: - E1000_ERR_HOST_INTERFACE_COMMAND in case if is not ready or + * timeout + * - E1000_SUCCESS for success. + ****************************************************************************/ +static int32_t +e1000_mng_enable_host_if(struct e1000_hw * hw) +{ + uint32_t hicr; + uint8_t i; + + /* Check that the host interface is enabled. */ + hicr = E1000_READ_REG(hw, HICR); + if ((hicr & E1000_HICR_EN) == 0) { + DEBUGOUT("E1000_HOST_EN bit disabled.\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + /* check the previous command is completed */ + for (i = 0; i < E1000_MNG_DHCP_COMMAND_TIMEOUT; i++) { + hicr = E1000_READ_REG(hw, HICR); + if (!(hicr & E1000_HICR_C)) + break; + mdelay(1); + } + + if (i == E1000_MNG_DHCP_COMMAND_TIMEOUT) { + DEBUGOUT("Previous command timeout failed .\n"); + return -E1000_ERR_HOST_INTERFACE_COMMAND; + } + return E1000_SUCCESS; +} + +/***************************************************************************** + * This function writes the buffer content at the offset given on the host if. + * It also does alignment considerations to do the writes in most efficient way. + * Also fills up the sum of the buffer in *buffer parameter. + * + * returns - E1000_SUCCESS for success. + ****************************************************************************/ +static int32_t +e1000_mng_host_if_write(struct e1000_hw * hw, uint8_t *buffer, + uint16_t length, uint16_t offset, uint8_t *sum) +{ + uint8_t *tmp; + uint8_t *bufptr = buffer; + uint32_t data = 0; + uint16_t remaining, i, j, prev_bytes; + + /* sum = only sum of the data and it is not checksum */ + + if (length == 0 || offset + length > E1000_HI_MAX_MNG_DATA_LENGTH) { + return -E1000_ERR_PARAM; + } + + tmp = (uint8_t *)&data; + prev_bytes = offset & 0x3; + offset &= 0xFFFC; + offset >>= 2; + + if (prev_bytes) { + data = E1000_READ_REG_ARRAY_DWORD(hw, HOST_IF, offset); + for (j = prev_bytes; j < sizeof(uint32_t); j++) { + *(tmp + j) = *bufptr++; + *sum += *(tmp + j); + } + E1000_WRITE_REG_ARRAY_DWORD(hw, HOST_IF, offset, data); + length -= j - prev_bytes; + offset++; + } + + remaining = length & 0x3; + length -= remaining; + + /* Calculate length in DWORDs */ + length >>= 2; + + /* The device driver writes the relevant command block into the + * ram area. */ + for (i = 0; i < length; i++) { + for (j = 0; j < sizeof(uint32_t); j++) { + *(tmp + j) = *bufptr++; + *sum += *(tmp + j); + } + + E1000_WRITE_REG_ARRAY_DWORD(hw, HOST_IF, offset + i, data); + } + if (remaining) { + for (j = 0; j < sizeof(uint32_t); j++) { + if (j < remaining) + *(tmp + j) = *bufptr++; + else + *(tmp + j) = 0; + + *sum += *(tmp + j); + } + E1000_WRITE_REG_ARRAY_DWORD(hw, HOST_IF, offset + i, data); + } + + return E1000_SUCCESS; +} + + +/***************************************************************************** + * This function writes the command header after does the checksum calculation. + * + * returns - E1000_SUCCESS for success. + ****************************************************************************/ +static int32_t +e1000_mng_write_cmd_header(struct e1000_hw * hw, + struct e1000_host_mng_command_header * hdr) +{ + uint16_t i; + uint8_t sum; + uint8_t *buffer; + + /* Write the whole command header structure which includes sum of + * the buffer */ + + uint16_t length = sizeof(struct e1000_host_mng_command_header); + + sum = hdr->checksum; + hdr->checksum = 0; + + buffer = (uint8_t *) hdr; + i = length; + while (i--) + sum += buffer[i]; + + hdr->checksum = 0 - sum; + + length >>= 2; + /* The device driver writes the relevant command block into the ram area. */ + for (i = 0; i < length; i++) { + E1000_WRITE_REG_ARRAY_DWORD(hw, HOST_IF, i, *((uint32_t *) hdr + i)); + E1000_WRITE_FLUSH(hw); + } + + return E1000_SUCCESS; +} + + +/***************************************************************************** + * This function indicates to ARC that a new command is pending which completes + * one write operation by the driver. + * + * returns - E1000_SUCCESS for success. + ****************************************************************************/ +static int32_t +e1000_mng_write_commit(struct e1000_hw * hw) +{ + uint32_t hicr; + + hicr = E1000_READ_REG(hw, HICR); + /* Setting this bit tells the ARC that a new command is pending. */ + E1000_WRITE_REG(hw, HICR, hicr | E1000_HICR_C); + + return E1000_SUCCESS; +} + + +/***************************************************************************** + * This function checks the mode of the firmware. + * + * returns - TRUE when the mode is IAMT or FALSE. + ****************************************************************************/ +boolean_t +e1000_check_mng_mode(struct e1000_hw *hw) +{ + uint32_t fwsm; + + fwsm = E1000_READ_REG(hw, FWSM); + + if (hw->mac_type == e1000_ich8lan) { + if ((fwsm & E1000_FWSM_MODE_MASK) == + (E1000_MNG_ICH_IAMT_MODE << E1000_FWSM_MODE_SHIFT)) + return TRUE; + } else if ((fwsm & E1000_FWSM_MODE_MASK) == + (E1000_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT)) + return TRUE; + + return FALSE; +} + + +/***************************************************************************** + * This function writes the dhcp info . + ****************************************************************************/ +int32_t +e1000_mng_write_dhcp_info(struct e1000_hw * hw, uint8_t *buffer, + uint16_t length) +{ + int32_t ret_val; + struct e1000_host_mng_command_header hdr; + + hdr.command_id = E1000_MNG_DHCP_TX_PAYLOAD_CMD; + hdr.command_length = length; + hdr.reserved1 = 0; + hdr.reserved2 = 0; + hdr.checksum = 0; + + ret_val = e1000_mng_enable_host_if(hw); + if (ret_val == E1000_SUCCESS) { + ret_val = e1000_mng_host_if_write(hw, buffer, length, sizeof(hdr), + &(hdr.checksum)); + if (ret_val == E1000_SUCCESS) { + ret_val = e1000_mng_write_cmd_header(hw, &hdr); + if (ret_val == E1000_SUCCESS) + ret_val = e1000_mng_write_commit(hw); + } + } + return ret_val; +} + + +/***************************************************************************** + * This function calculates the checksum. + * + * returns - checksum of buffer contents. + ****************************************************************************/ +static uint8_t +e1000_calculate_mng_checksum(char *buffer, uint32_t length) +{ + uint8_t sum = 0; + uint32_t i; + + if (!buffer) + return 0; + + for (i=0; i < length; i++) + sum += buffer[i]; + + return (uint8_t) (0 - sum); +} + +/***************************************************************************** + * This function checks whether tx pkt filtering needs to be enabled or not. + * + * returns - TRUE for packet filtering or FALSE. + ****************************************************************************/ +boolean_t +e1000_enable_tx_pkt_filtering(struct e1000_hw *hw) +{ + /* called in init as well as watchdog timer functions */ + + int32_t ret_val, checksum; + boolean_t tx_filter = FALSE; + struct e1000_host_mng_dhcp_cookie *hdr = &(hw->mng_cookie); + uint8_t *buffer = (uint8_t *) &(hw->mng_cookie); + + if (e1000_check_mng_mode(hw)) { + ret_val = e1000_mng_enable_host_if(hw); + if (ret_val == E1000_SUCCESS) { + ret_val = e1000_host_if_read_cookie(hw, buffer); + if (ret_val == E1000_SUCCESS) { + checksum = hdr->checksum; + hdr->checksum = 0; + if ((hdr->signature == E1000_IAMT_SIGNATURE) && + checksum == e1000_calculate_mng_checksum((char *)buffer, + E1000_MNG_DHCP_COOKIE_LENGTH)) { + if (hdr->status & + E1000_MNG_DHCP_COOKIE_STATUS_PARSING_SUPPORT) + tx_filter = TRUE; + } else + tx_filter = TRUE; + } else + tx_filter = TRUE; + } + } + + hw->tx_pkt_filtering = tx_filter; + return tx_filter; +} + +/****************************************************************************** + * Verifies the hardware needs to allow ARPs to be processed by the host + * + * hw - Struct containing variables accessed by shared code + * + * returns: - TRUE/FALSE + * + *****************************************************************************/ +uint32_t +e1000_enable_mng_pass_thru(struct e1000_hw *hw) +{ + uint32_t manc; + uint32_t fwsm, factps; + + if (hw->asf_firmware_present) { + manc = E1000_READ_REG(hw, MANC); + + if (!(manc & E1000_MANC_RCV_TCO_EN) || + !(manc & E1000_MANC_EN_MAC_ADDR_FILTER)) + return FALSE; + if (e1000_arc_subsystem_valid(hw) == TRUE) { + fwsm = E1000_READ_REG(hw, FWSM); + factps = E1000_READ_REG(hw, FACTPS); + + if ((((fwsm & E1000_FWSM_MODE_MASK) >> E1000_FWSM_MODE_SHIFT) == + e1000_mng_mode_pt) && !(factps & E1000_FACTPS_MNGCG)) + return TRUE; + } else + if ((manc & E1000_MANC_SMBUS_EN) && !(manc & E1000_MANC_ASF_EN)) + return TRUE; + } + return FALSE; +} + +static int32_t +e1000_polarity_reversal_workaround(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t mii_status_reg; + uint16_t i; + + /* Polarity reversal workaround for forced 10F/10H links. */ + + /* Disable the transmitter on the PHY */ + + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0019); + if (ret_val) + return ret_val; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFFFF); + if (ret_val) + return ret_val; + + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0000); + if (ret_val) + return ret_val; + + /* This loop will early-out if the NO link condition has been met. */ + for (i = PHY_FORCE_TIME; i > 0; i--) { + /* Read the MII Status Register and wait for Link Status bit + * to be clear. + */ + + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + + if ((mii_status_reg & ~MII_SR_LINK_STATUS) == 0) break; + mdelay(100); + } + + /* Recommended delay time after link has been lost */ + mdelay(1000); + + /* Now we will re-enable th transmitter on the PHY */ + + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0019); + if (ret_val) + return ret_val; + mdelay(50); + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFFF0); + if (ret_val) + return ret_val; + mdelay(50); + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0xFF00); + if (ret_val) + return ret_val; + mdelay(50); + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0x0000); + if (ret_val) + return ret_val; + + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0000); + if (ret_val) + return ret_val; + + /* This loop will early-out if the link condition has been met. */ + for (i = PHY_FORCE_TIME; i > 0; i--) { + /* Read the MII Status Register and wait for Link Status bit + * to be set. + */ + + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if (ret_val) + return ret_val; + + if (mii_status_reg & MII_SR_LINK_STATUS) break; + mdelay(100); + } + return E1000_SUCCESS; +} + +/*************************************************************************** + * + * Disables PCI-Express master access. + * + * hw: Struct containing variables accessed by shared code + * + * returns: - none. + * + ***************************************************************************/ +static void +e1000_set_pci_express_master_disable(struct e1000_hw *hw) +{ + uint32_t ctrl; + + DEBUGFUNC("e1000_set_pci_express_master_disable"); + + if (hw->bus_type != e1000_bus_type_pci_express) + return; + + ctrl = E1000_READ_REG(hw, CTRL); + ctrl |= E1000_CTRL_GIO_MASTER_DISABLE; + E1000_WRITE_REG(hw, CTRL, ctrl); +} + +/******************************************************************************* + * + * Disables PCI-Express master access and verifies there are no pending requests + * + * hw: Struct containing variables accessed by shared code + * + * returns: - E1000_ERR_MASTER_REQUESTS_PENDING if master disable bit hasn't + * caused the master requests to be disabled. + * E1000_SUCCESS master requests disabled. + * + ******************************************************************************/ +int32_t +e1000_disable_pciex_master(struct e1000_hw *hw) +{ + int32_t timeout = MASTER_DISABLE_TIMEOUT; /* 80ms */ + + DEBUGFUNC("e1000_disable_pciex_master"); + + if (hw->bus_type != e1000_bus_type_pci_express) + return E1000_SUCCESS; + + e1000_set_pci_express_master_disable(hw); + + while (timeout) { + if (!(E1000_READ_REG(hw, STATUS) & E1000_STATUS_GIO_MASTER_ENABLE)) + break; + else + udelay(100); + timeout--; + } + + if (!timeout) { + DEBUGOUT("Master requests are pending.\n"); + return -E1000_ERR_MASTER_REQUESTS_PENDING; + } + + return E1000_SUCCESS; +} + +/******************************************************************************* + * + * Check for EEPROM Auto Read bit done. + * + * hw: Struct containing variables accessed by shared code + * + * returns: - E1000_ERR_RESET if fail to reset MAC + * E1000_SUCCESS at any other case. + * + ******************************************************************************/ +static int32_t +e1000_get_auto_rd_done(struct e1000_hw *hw) +{ + int32_t timeout = AUTO_READ_DONE_TIMEOUT; + + DEBUGFUNC("e1000_get_auto_rd_done"); + + switch (hw->mac_type) { + default: + msleep(5); + break; + case e1000_82571: + case e1000_82572: + case e1000_82573: + case e1000_80003es2lan: + case e1000_ich8lan: + while (timeout) { + if (E1000_READ_REG(hw, EECD) & E1000_EECD_AUTO_RD) + break; + else msleep(1); + timeout--; + } + + if (!timeout) { + DEBUGOUT("Auto read by HW from EEPROM has not completed.\n"); + return -E1000_ERR_RESET; + } + break; + } + + /* PHY configuration from NVM just starts after EECD_AUTO_RD sets to high. + * Need to wait for PHY configuration completion before accessing NVM + * and PHY. */ + if (hw->mac_type == e1000_82573) + msleep(25); + + return E1000_SUCCESS; +} + +/*************************************************************************** + * Checks if the PHY configuration is done + * + * hw: Struct containing variables accessed by shared code + * + * returns: - E1000_ERR_RESET if fail to reset MAC + * E1000_SUCCESS at any other case. + * + ***************************************************************************/ +static int32_t +e1000_get_phy_cfg_done(struct e1000_hw *hw) +{ + int32_t timeout = PHY_CFG_TIMEOUT; + uint32_t cfg_mask = E1000_EEPROM_CFG_DONE; + + DEBUGFUNC("e1000_get_phy_cfg_done"); + + switch (hw->mac_type) { + default: + mdelay(10); + break; + case e1000_80003es2lan: + /* Separate *_CFG_DONE_* bit for each port */ + if (E1000_READ_REG(hw, STATUS) & E1000_STATUS_FUNC_1) + cfg_mask = E1000_EEPROM_CFG_DONE_PORT_1; + /* Fall Through */ + case e1000_82571: + case e1000_82572: + while (timeout) { + if (E1000_READ_REG(hw, EEMNGCTL) & cfg_mask) + break; + else + msleep(1); + timeout--; + } + if (!timeout) { + DEBUGOUT("MNG configuration cycle has not completed.\n"); + return -E1000_ERR_RESET; + } + break; + } + + return E1000_SUCCESS; +} + +/*************************************************************************** + * + * Using the combination of SMBI and SWESMBI semaphore bits when resetting + * adapter or Eeprom access. + * + * hw: Struct containing variables accessed by shared code + * + * returns: - E1000_ERR_EEPROM if fail to access EEPROM. + * E1000_SUCCESS at any other case. + * + ***************************************************************************/ +static int32_t +e1000_get_hw_eeprom_semaphore(struct e1000_hw *hw) +{ + int32_t timeout; + uint32_t swsm; + + DEBUGFUNC("e1000_get_hw_eeprom_semaphore"); + + if (!hw->eeprom_semaphore_present) + return E1000_SUCCESS; + + if (hw->mac_type == e1000_80003es2lan) { + /* Get the SW semaphore. */ + if (e1000_get_software_semaphore(hw) != E1000_SUCCESS) + return -E1000_ERR_EEPROM; + } + + /* Get the FW semaphore. */ + timeout = hw->eeprom.word_size + 1; + while (timeout) { + swsm = E1000_READ_REG(hw, SWSM); + swsm |= E1000_SWSM_SWESMBI; + E1000_WRITE_REG(hw, SWSM, swsm); + /* if we managed to set the bit we got the semaphore. */ + swsm = E1000_READ_REG(hw, SWSM); + if (swsm & E1000_SWSM_SWESMBI) + break; + + udelay(50); + timeout--; + } + + if (!timeout) { + /* Release semaphores */ + e1000_put_hw_eeprom_semaphore(hw); + DEBUGOUT("Driver can't access the Eeprom - SWESMBI bit is set.\n"); + return -E1000_ERR_EEPROM; + } + + return E1000_SUCCESS; +} + +/*************************************************************************** + * This function clears HW semaphore bits. + * + * hw: Struct containing variables accessed by shared code + * + * returns: - None. + * + ***************************************************************************/ +static void +e1000_put_hw_eeprom_semaphore(struct e1000_hw *hw) +{ + uint32_t swsm; + + DEBUGFUNC("e1000_put_hw_eeprom_semaphore"); + + if (!hw->eeprom_semaphore_present) + return; + + swsm = E1000_READ_REG(hw, SWSM); + if (hw->mac_type == e1000_80003es2lan) { + /* Release both semaphores. */ + swsm &= ~(E1000_SWSM_SMBI | E1000_SWSM_SWESMBI); + } else + swsm &= ~(E1000_SWSM_SWESMBI); + E1000_WRITE_REG(hw, SWSM, swsm); +} + +/*************************************************************************** + * + * Obtaining software semaphore bit (SMBI) before resetting PHY. + * + * hw: Struct containing variables accessed by shared code + * + * returns: - E1000_ERR_RESET if fail to obtain semaphore. + * E1000_SUCCESS at any other case. + * + ***************************************************************************/ +static int32_t +e1000_get_software_semaphore(struct e1000_hw *hw) +{ + int32_t timeout = hw->eeprom.word_size + 1; + uint32_t swsm; + + DEBUGFUNC("e1000_get_software_semaphore"); + + if (hw->mac_type != e1000_80003es2lan) { + return E1000_SUCCESS; + } + + while (timeout) { + swsm = E1000_READ_REG(hw, SWSM); + /* If SMBI bit cleared, it is now set and we hold the semaphore */ + if (!(swsm & E1000_SWSM_SMBI)) + break; + mdelay(1); + timeout--; + } + + if (!timeout) { + DEBUGOUT("Driver can't access device - SMBI bit is set.\n"); + return -E1000_ERR_RESET; + } + + return E1000_SUCCESS; +} + +/*************************************************************************** + * + * Release semaphore bit (SMBI). + * + * hw: Struct containing variables accessed by shared code + * + ***************************************************************************/ +static void +e1000_release_software_semaphore(struct e1000_hw *hw) +{ + uint32_t swsm; + + DEBUGFUNC("e1000_release_software_semaphore"); + + if (hw->mac_type != e1000_80003es2lan) { + return; + } + + swsm = E1000_READ_REG(hw, SWSM); + /* Release the SW semaphores.*/ + swsm &= ~E1000_SWSM_SMBI; + E1000_WRITE_REG(hw, SWSM, swsm); +} + +/****************************************************************************** + * Checks if PHY reset is blocked due to SOL/IDER session, for example. + * Returning E1000_BLK_PHY_RESET isn't necessarily an error. But it's up to + * the caller to figure out how to deal with it. + * + * hw - Struct containing variables accessed by shared code + * + * returns: - E1000_BLK_PHY_RESET + * E1000_SUCCESS + * + *****************************************************************************/ +int32_t +e1000_check_phy_reset_block(struct e1000_hw *hw) +{ + uint32_t manc = 0; + uint32_t fwsm = 0; + + if (hw->mac_type == e1000_ich8lan) { + fwsm = E1000_READ_REG(hw, FWSM); + return (fwsm & E1000_FWSM_RSPCIPHY) ? E1000_SUCCESS + : E1000_BLK_PHY_RESET; + } + + if (hw->mac_type > e1000_82547_rev_2) + manc = E1000_READ_REG(hw, MANC); + return (manc & E1000_MANC_BLK_PHY_RST_ON_IDE) ? + E1000_BLK_PHY_RESET : E1000_SUCCESS; +} + +static uint8_t +e1000_arc_subsystem_valid(struct e1000_hw *hw) +{ + uint32_t fwsm; + + /* On 8257x silicon, registers in the range of 0x8800 - 0x8FFC + * may not be provided a DMA clock when no manageability features are + * enabled. We do not want to perform any reads/writes to these registers + * if this is the case. We read FWSM to determine the manageability mode. + */ + switch (hw->mac_type) { + case e1000_82571: + case e1000_82572: + case e1000_82573: + case e1000_80003es2lan: + fwsm = E1000_READ_REG(hw, FWSM); + if ((fwsm & E1000_FWSM_MODE_MASK) != 0) + return TRUE; + break; + case e1000_ich8lan: + return TRUE; + default: + break; + } + return FALSE; +} + + +/****************************************************************************** + * Configure PCI-Ex no-snoop + * + * hw - Struct containing variables accessed by shared code. + * no_snoop - Bitmap of no-snoop events. + * + * returns: E1000_SUCCESS + * + *****************************************************************************/ +static int32_t +e1000_set_pci_ex_no_snoop(struct e1000_hw *hw, uint32_t no_snoop) +{ + uint32_t gcr_reg = 0; + + DEBUGFUNC("e1000_set_pci_ex_no_snoop"); + + if (hw->bus_type == e1000_bus_type_unknown) + e1000_get_bus_info(hw); + + if (hw->bus_type != e1000_bus_type_pci_express) + return E1000_SUCCESS; + + if (no_snoop) { + gcr_reg = E1000_READ_REG(hw, GCR); + gcr_reg &= ~(PCI_EX_NO_SNOOP_ALL); + gcr_reg |= no_snoop; + E1000_WRITE_REG(hw, GCR, gcr_reg); + } + if (hw->mac_type == e1000_ich8lan) { + uint32_t ctrl_ext; + + E1000_WRITE_REG(hw, GCR, PCI_EX_82566_SNOOP_ALL); + + ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_RO_DIS; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + } + + return E1000_SUCCESS; +} + +/*************************************************************************** + * + * Get software semaphore FLAG bit (SWFLAG). + * SWFLAG is used to synchronize the access to all shared resource between + * SW, FW and HW. + * + * hw: Struct containing variables accessed by shared code + * + ***************************************************************************/ +static int32_t +e1000_get_software_flag(struct e1000_hw *hw) +{ + int32_t timeout = PHY_CFG_TIMEOUT; + uint32_t extcnf_ctrl; + + DEBUGFUNC("e1000_get_software_flag"); + + if (hw->mac_type == e1000_ich8lan) { + while (timeout) { + extcnf_ctrl = E1000_READ_REG(hw, EXTCNF_CTRL); + extcnf_ctrl |= E1000_EXTCNF_CTRL_SWFLAG; + E1000_WRITE_REG(hw, EXTCNF_CTRL, extcnf_ctrl); + + extcnf_ctrl = E1000_READ_REG(hw, EXTCNF_CTRL); + if (extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG) + break; + mdelay(1); + timeout--; + } + + if (!timeout) { + DEBUGOUT("FW or HW locks the resource too long.\n"); + return -E1000_ERR_CONFIG; + } + } + + return E1000_SUCCESS; +} + +/*************************************************************************** + * + * Release software semaphore FLAG bit (SWFLAG). + * SWFLAG is used to synchronize the access to all shared resource between + * SW, FW and HW. + * + * hw: Struct containing variables accessed by shared code + * + ***************************************************************************/ +static void +e1000_release_software_flag(struct e1000_hw *hw) +{ + uint32_t extcnf_ctrl; + + DEBUGFUNC("e1000_release_software_flag"); + + if (hw->mac_type == e1000_ich8lan) { + extcnf_ctrl= E1000_READ_REG(hw, EXTCNF_CTRL); + extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG; + E1000_WRITE_REG(hw, EXTCNF_CTRL, extcnf_ctrl); + } + + return; +} + +/****************************************************************************** + * Reads a 16 bit word or words from the EEPROM using the ICH8's flash access + * register. + * + * hw - Struct containing variables accessed by shared code + * offset - offset of word in the EEPROM to read + * data - word read from the EEPROM + * words - number of words to read + *****************************************************************************/ +static int32_t +e1000_read_eeprom_ich8(struct e1000_hw *hw, uint16_t offset, uint16_t words, + uint16_t *data) +{ + int32_t error = E1000_SUCCESS; + uint32_t flash_bank = 0; + uint32_t act_offset = 0; + uint32_t bank_offset = 0; + uint16_t word = 0; + uint16_t i = 0; + + /* We need to know which is the valid flash bank. In the event + * that we didn't allocate eeprom_shadow_ram, we may not be + * managing flash_bank. So it cannot be trusted and needs + * to be updated with each read. + */ + /* Value of bit 22 corresponds to the flash bank we're on. */ + flash_bank = (E1000_READ_REG(hw, EECD) & E1000_EECD_SEC1VAL) ? 1 : 0; + + /* Adjust offset appropriately if we're on bank 1 - adjust for word size */ + bank_offset = flash_bank * (hw->flash_bank_size * 2); + + error = e1000_get_software_flag(hw); + if (error != E1000_SUCCESS) + return error; + + for (i = 0; i < words; i++) { + if (hw->eeprom_shadow_ram != NULL && + hw->eeprom_shadow_ram[offset+i].modified == TRUE) { + data[i] = hw->eeprom_shadow_ram[offset+i].eeprom_word; + } else { + /* The NVM part needs a byte offset, hence * 2 */ + act_offset = bank_offset + ((offset + i) * 2); + error = e1000_read_ich8_word(hw, act_offset, &word); + if (error != E1000_SUCCESS) + break; + data[i] = word; + } + } + + e1000_release_software_flag(hw); + + return error; +} + +/****************************************************************************** + * Writes a 16 bit word or words to the EEPROM using the ICH8's flash access + * register. Actually, writes are written to the shadow ram cache in the hw + * structure hw->e1000_shadow_ram. e1000_commit_shadow_ram flushes this to + * the NVM, which occurs when the NVM checksum is updated. + * + * hw - Struct containing variables accessed by shared code + * offset - offset of word in the EEPROM to write + * words - number of words to write + * data - words to write to the EEPROM + *****************************************************************************/ +static int32_t +e1000_write_eeprom_ich8(struct e1000_hw *hw, uint16_t offset, uint16_t words, + uint16_t *data) +{ + uint32_t i = 0; + int32_t error = E1000_SUCCESS; + + error = e1000_get_software_flag(hw); + if (error != E1000_SUCCESS) + return error; + + /* A driver can write to the NVM only if it has eeprom_shadow_ram + * allocated. Subsequent reads to the modified words are read from + * this cached structure as well. Writes will only go into this + * cached structure unless it's followed by a call to + * e1000_update_eeprom_checksum() where it will commit the changes + * and clear the "modified" field. + */ + if (hw->eeprom_shadow_ram != NULL) { + for (i = 0; i < words; i++) { + if ((offset + i) < E1000_SHADOW_RAM_WORDS) { + hw->eeprom_shadow_ram[offset+i].modified = TRUE; + hw->eeprom_shadow_ram[offset+i].eeprom_word = data[i]; + } else { + error = -E1000_ERR_EEPROM; + break; + } + } + } else { + /* Drivers have the option to not allocate eeprom_shadow_ram as long + * as they don't perform any NVM writes. An attempt in doing so + * will result in this error. + */ + error = -E1000_ERR_EEPROM; + } + + e1000_release_software_flag(hw); + + return error; +} + +/****************************************************************************** + * This function does initial flash setup so that a new read/write/erase cycle + * can be started. + * + * hw - The pointer to the hw structure + ****************************************************************************/ +static int32_t +e1000_ich8_cycle_init(struct e1000_hw *hw) +{ + union ich8_hws_flash_status hsfsts; + int32_t error = E1000_ERR_EEPROM; + int32_t i = 0; + + DEBUGFUNC("e1000_ich8_cycle_init"); + + hsfsts.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS); + + /* May be check the Flash Des Valid bit in Hw status */ + if (hsfsts.hsf_status.fldesvalid == 0) { + DEBUGOUT("Flash descriptor invalid. SW Sequencing must be used."); + return error; + } + + /* Clear FCERR in Hw status by writing 1 */ + /* Clear DAEL in Hw status by writing a 1 */ + hsfsts.hsf_status.flcerr = 1; + hsfsts.hsf_status.dael = 1; + + E1000_WRITE_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS, hsfsts.regval); + + /* Either we should have a hardware SPI cycle in progress bit to check + * against, in order to start a new cycle or FDONE bit should be changed + * in the hardware so that it is 1 after harware reset, which can then be + * used as an indication whether a cycle is in progress or has been + * completed .. we should also have some software semaphore mechanism to + * guard FDONE or the cycle in progress bit so that two threads access to + * those bits can be sequentiallized or a way so that 2 threads dont + * start the cycle at the same time */ + + if (hsfsts.hsf_status.flcinprog == 0) { + /* There is no cycle running at present, so we can start a cycle */ + /* Begin by setting Flash Cycle Done. */ + hsfsts.hsf_status.flcdone = 1; + E1000_WRITE_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS, hsfsts.regval); + error = E1000_SUCCESS; + } else { + /* otherwise poll for sometime so the current cycle has a chance + * to end before giving up. */ + for (i = 0; i < ICH_FLASH_COMMAND_TIMEOUT; i++) { + hsfsts.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS); + if (hsfsts.hsf_status.flcinprog == 0) { + error = E1000_SUCCESS; + break; + } + udelay(1); + } + if (error == E1000_SUCCESS) { + /* Successful in waiting for previous cycle to timeout, + * now set the Flash Cycle Done. */ + hsfsts.hsf_status.flcdone = 1; + E1000_WRITE_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS, hsfsts.regval); + } else { + DEBUGOUT("Flash controller busy, cannot get access"); + } + } + return error; +} + +/****************************************************************************** + * This function starts a flash cycle and waits for its completion + * + * hw - The pointer to the hw structure + ****************************************************************************/ +static int32_t +e1000_ich8_flash_cycle(struct e1000_hw *hw, uint32_t timeout) +{ + union ich8_hws_flash_ctrl hsflctl; + union ich8_hws_flash_status hsfsts; + int32_t error = E1000_ERR_EEPROM; + uint32_t i = 0; + + /* Start a cycle by writing 1 in Flash Cycle Go in Hw Flash Control */ + hsflctl.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL); + hsflctl.hsf_ctrl.flcgo = 1; + E1000_WRITE_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL, hsflctl.regval); + + /* wait till FDONE bit is set to 1 */ + do { + hsfsts.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS); + if (hsfsts.hsf_status.flcdone == 1) + break; + udelay(1); + i++; + } while (i < timeout); + if (hsfsts.hsf_status.flcdone == 1 && hsfsts.hsf_status.flcerr == 0) { + error = E1000_SUCCESS; + } + return error; +} + +/****************************************************************************** + * Reads a byte or word from the NVM using the ICH8 flash access registers. + * + * hw - The pointer to the hw structure + * index - The index of the byte or word to read. + * size - Size of data to read, 1=byte 2=word + * data - Pointer to the word to store the value read. + *****************************************************************************/ +static int32_t +e1000_read_ich8_data(struct e1000_hw *hw, uint32_t index, + uint32_t size, uint16_t* data) +{ + union ich8_hws_flash_status hsfsts; + union ich8_hws_flash_ctrl hsflctl; + uint32_t flash_linear_address; + uint32_t flash_data = 0; + int32_t error = -E1000_ERR_EEPROM; + int32_t count = 0; + + DEBUGFUNC("e1000_read_ich8_data"); + + if (size < 1 || size > 2 || data == 0x0 || + index > ICH_FLASH_LINEAR_ADDR_MASK) + return error; + + flash_linear_address = (ICH_FLASH_LINEAR_ADDR_MASK & index) + + hw->flash_base_addr; + + do { + udelay(1); + /* Steps */ + error = e1000_ich8_cycle_init(hw); + if (error != E1000_SUCCESS) + break; + + hsflctl.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL); + /* 0b/1b corresponds to 1 or 2 byte size, respectively. */ + hsflctl.hsf_ctrl.fldbcount = size - 1; + hsflctl.hsf_ctrl.flcycle = ICH_CYCLE_READ; + E1000_WRITE_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL, hsflctl.regval); + + /* Write the last 24 bits of index into Flash Linear address field in + * Flash Address */ + /* TODO: TBD maybe check the index against the size of flash */ + + E1000_WRITE_ICH_FLASH_REG(hw, ICH_FLASH_FADDR, flash_linear_address); + + error = e1000_ich8_flash_cycle(hw, ICH_FLASH_COMMAND_TIMEOUT); + + /* Check if FCERR is set to 1, if set to 1, clear it and try the whole + * sequence a few more times, else read in (shift in) the Flash Data0, + * the order is least significant byte first msb to lsb */ + if (error == E1000_SUCCESS) { + flash_data = E1000_READ_ICH_FLASH_REG(hw, ICH_FLASH_FDATA0); + if (size == 1) { + *data = (uint8_t)(flash_data & 0x000000FF); + } else if (size == 2) { + *data = (uint16_t)(flash_data & 0x0000FFFF); + } + break; + } else { + /* If we've gotten here, then things are probably completely hosed, + * but if the error condition is detected, it won't hurt to give + * it another try...ICH_FLASH_CYCLE_REPEAT_COUNT times. + */ + hsfsts.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS); + if (hsfsts.hsf_status.flcerr == 1) { + /* Repeat for some time before giving up. */ + continue; + } else if (hsfsts.hsf_status.flcdone == 0) { + DEBUGOUT("Timeout error - flash cycle did not complete."); + break; + } + } + } while (count++ < ICH_FLASH_CYCLE_REPEAT_COUNT); + + return error; +} + +/****************************************************************************** + * Writes One /two bytes to the NVM using the ICH8 flash access registers. + * + * hw - The pointer to the hw structure + * index - The index of the byte/word to read. + * size - Size of data to read, 1=byte 2=word + * data - The byte(s) to write to the NVM. + *****************************************************************************/ +static int32_t +e1000_write_ich8_data(struct e1000_hw *hw, uint32_t index, uint32_t size, + uint16_t data) +{ + union ich8_hws_flash_status hsfsts; + union ich8_hws_flash_ctrl hsflctl; + uint32_t flash_linear_address; + uint32_t flash_data = 0; + int32_t error = -E1000_ERR_EEPROM; + int32_t count = 0; + + DEBUGFUNC("e1000_write_ich8_data"); + + if (size < 1 || size > 2 || data > size * 0xff || + index > ICH_FLASH_LINEAR_ADDR_MASK) + return error; + + flash_linear_address = (ICH_FLASH_LINEAR_ADDR_MASK & index) + + hw->flash_base_addr; + + do { + udelay(1); + /* Steps */ + error = e1000_ich8_cycle_init(hw); + if (error != E1000_SUCCESS) + break; + + hsflctl.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL); + /* 0b/1b corresponds to 1 or 2 byte size, respectively. */ + hsflctl.hsf_ctrl.fldbcount = size -1; + hsflctl.hsf_ctrl.flcycle = ICH_CYCLE_WRITE; + E1000_WRITE_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL, hsflctl.regval); + + /* Write the last 24 bits of index into Flash Linear address field in + * Flash Address */ + E1000_WRITE_ICH_FLASH_REG(hw, ICH_FLASH_FADDR, flash_linear_address); + + if (size == 1) + flash_data = (uint32_t)data & 0x00FF; + else + flash_data = (uint32_t)data; + + E1000_WRITE_ICH_FLASH_REG(hw, ICH_FLASH_FDATA0, flash_data); + + /* check if FCERR is set to 1 , if set to 1, clear it and try the whole + * sequence a few more times else done */ + error = e1000_ich8_flash_cycle(hw, ICH_FLASH_COMMAND_TIMEOUT); + if (error == E1000_SUCCESS) { + break; + } else { + /* If we're here, then things are most likely completely hosed, + * but if the error condition is detected, it won't hurt to give + * it another try...ICH_FLASH_CYCLE_REPEAT_COUNT times. + */ + hsfsts.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS); + if (hsfsts.hsf_status.flcerr == 1) { + /* Repeat for some time before giving up. */ + continue; + } else if (hsfsts.hsf_status.flcdone == 0) { + DEBUGOUT("Timeout error - flash cycle did not complete."); + break; + } + } + } while (count++ < ICH_FLASH_CYCLE_REPEAT_COUNT); + + return error; +} + +/****************************************************************************** + * Reads a single byte from the NVM using the ICH8 flash access registers. + * + * hw - pointer to e1000_hw structure + * index - The index of the byte to read. + * data - Pointer to a byte to store the value read. + *****************************************************************************/ +static int32_t +e1000_read_ich8_byte(struct e1000_hw *hw, uint32_t index, uint8_t* data) +{ + int32_t status = E1000_SUCCESS; + uint16_t word = 0; + + status = e1000_read_ich8_data(hw, index, 1, &word); + if (status == E1000_SUCCESS) { + *data = (uint8_t)word; + } + + return status; +} + +/****************************************************************************** + * Writes a single byte to the NVM using the ICH8 flash access registers. + * Performs verification by reading back the value and then going through + * a retry algorithm before giving up. + * + * hw - pointer to e1000_hw structure + * index - The index of the byte to write. + * byte - The byte to write to the NVM. + *****************************************************************************/ +static int32_t +e1000_verify_write_ich8_byte(struct e1000_hw *hw, uint32_t index, uint8_t byte) +{ + int32_t error = E1000_SUCCESS; + int32_t program_retries = 0; + + DEBUGOUT2("Byte := %2.2X Offset := %d\n", byte, index); + + error = e1000_write_ich8_byte(hw, index, byte); + + if (error != E1000_SUCCESS) { + for (program_retries = 0; program_retries < 100; program_retries++) { + DEBUGOUT2("Retrying \t Byte := %2.2X Offset := %d\n", byte, index); + error = e1000_write_ich8_byte(hw, index, byte); + udelay(100); + if (error == E1000_SUCCESS) + break; + } + } + + if (program_retries == 100) + error = E1000_ERR_EEPROM; + + return error; +} + +/****************************************************************************** + * Writes a single byte to the NVM using the ICH8 flash access registers. + * + * hw - pointer to e1000_hw structure + * index - The index of the byte to read. + * data - The byte to write to the NVM. + *****************************************************************************/ +static int32_t +e1000_write_ich8_byte(struct e1000_hw *hw, uint32_t index, uint8_t data) +{ + int32_t status = E1000_SUCCESS; + uint16_t word = (uint16_t)data; + + status = e1000_write_ich8_data(hw, index, 1, word); + + return status; +} + +/****************************************************************************** + * Reads a word from the NVM using the ICH8 flash access registers. + * + * hw - pointer to e1000_hw structure + * index - The starting byte index of the word to read. + * data - Pointer to a word to store the value read. + *****************************************************************************/ +static int32_t +e1000_read_ich8_word(struct e1000_hw *hw, uint32_t index, uint16_t *data) +{ + int32_t status = E1000_SUCCESS; + status = e1000_read_ich8_data(hw, index, 2, data); + return status; +} + +/****************************************************************************** + * Erases the bank specified. Each bank may be a 4, 8 or 64k block. Banks are 0 + * based. + * + * hw - pointer to e1000_hw structure + * bank - 0 for first bank, 1 for second bank + * + * Note that this function may actually erase as much as 8 or 64 KBytes. The + * amount of NVM used in each bank is a *minimum* of 4 KBytes, but in fact the + * bank size may be 4, 8 or 64 KBytes + *****************************************************************************/ +int32_t +e1000_erase_ich8_4k_segment(struct e1000_hw *hw, uint32_t bank) +{ + union ich8_hws_flash_status hsfsts; + union ich8_hws_flash_ctrl hsflctl; + uint32_t flash_linear_address; + int32_t count = 0; + int32_t error = E1000_ERR_EEPROM; + int32_t iteration; + int32_t sub_sector_size = 0; + int32_t bank_size; + int32_t j = 0; + int32_t error_flag = 0; + + hsfsts.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS); + + /* Determine HW Sector size: Read BERASE bits of Hw flash Status register */ + /* 00: The Hw sector is 256 bytes, hence we need to erase 16 + * consecutive sectors. The start index for the nth Hw sector can be + * calculated as bank * 4096 + n * 256 + * 01: The Hw sector is 4K bytes, hence we need to erase 1 sector. + * The start index for the nth Hw sector can be calculated + * as bank * 4096 + * 10: The HW sector is 8K bytes + * 11: The Hw sector size is 64K bytes */ + if (hsfsts.hsf_status.berasesz == 0x0) { + /* Hw sector size 256 */ + sub_sector_size = ICH_FLASH_SEG_SIZE_256; + bank_size = ICH_FLASH_SECTOR_SIZE; + iteration = ICH_FLASH_SECTOR_SIZE / ICH_FLASH_SEG_SIZE_256; + } else if (hsfsts.hsf_status.berasesz == 0x1) { + bank_size = ICH_FLASH_SEG_SIZE_4K; + iteration = 1; + } else if (hsfsts.hsf_status.berasesz == 0x3) { + bank_size = ICH_FLASH_SEG_SIZE_64K; + iteration = 1; + } else { + return error; + } + + for (j = 0; j < iteration ; j++) { + do { + count++; + /* Steps */ + error = e1000_ich8_cycle_init(hw); + if (error != E1000_SUCCESS) { + error_flag = 1; + break; + } + + /* Write a value 11 (block Erase) in Flash Cycle field in Hw flash + * Control */ + hsflctl.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL); + hsflctl.hsf_ctrl.flcycle = ICH_CYCLE_ERASE; + E1000_WRITE_ICH_FLASH_REG16(hw, ICH_FLASH_HSFCTL, hsflctl.regval); + + /* Write the last 24 bits of an index within the block into Flash + * Linear address field in Flash Address. This probably needs to + * be calculated here based off the on-chip erase sector size and + * the software bank size (4, 8 or 64 KBytes) */ + flash_linear_address = bank * bank_size + j * sub_sector_size; + flash_linear_address += hw->flash_base_addr; + flash_linear_address &= ICH_FLASH_LINEAR_ADDR_MASK; + + E1000_WRITE_ICH_FLASH_REG(hw, ICH_FLASH_FADDR, flash_linear_address); + + error = e1000_ich8_flash_cycle(hw, ICH_FLASH_ERASE_TIMEOUT); + /* Check if FCERR is set to 1. If 1, clear it and try the whole + * sequence a few more times else Done */ + if (error == E1000_SUCCESS) { + break; + } else { + hsfsts.regval = E1000_READ_ICH_FLASH_REG16(hw, ICH_FLASH_HSFSTS); + if (hsfsts.hsf_status.flcerr == 1) { + /* repeat for some time before giving up */ + continue; + } else if (hsfsts.hsf_status.flcdone == 0) { + error_flag = 1; + break; + } + } + } while ((count < ICH_FLASH_CYCLE_REPEAT_COUNT) && !error_flag); + if (error_flag == 1) + break; + } + if (error_flag != 1) + error = E1000_SUCCESS; + return error; +} + +static int32_t +e1000_init_lcd_from_nvm_config_region(struct e1000_hw *hw, + uint32_t cnf_base_addr, uint32_t cnf_size) +{ + uint32_t ret_val = E1000_SUCCESS; + uint16_t word_addr, reg_data, reg_addr; + uint16_t i; + + /* cnf_base_addr is in DWORD */ + word_addr = (uint16_t)(cnf_base_addr << 1); + + /* cnf_size is returned in size of dwords */ + for (i = 0; i < cnf_size; i++) { + ret_val = e1000_read_eeprom(hw, (word_addr + i*2), 1, ®_data); + if (ret_val) + return ret_val; + + ret_val = e1000_read_eeprom(hw, (word_addr + i*2 + 1), 1, ®_addr); + if (ret_val) + return ret_val; + + ret_val = e1000_get_software_flag(hw); + if (ret_val != E1000_SUCCESS) + return ret_val; + + ret_val = e1000_write_phy_reg_ex(hw, (uint32_t)reg_addr, reg_data); + + e1000_release_software_flag(hw); + } + + return ret_val; +} + + +/****************************************************************************** + * This function initializes the PHY from the NVM on ICH8 platforms. This + * is needed due to an issue where the NVM configuration is not properly + * autoloaded after power transitions. Therefore, after each PHY reset, we + * will load the configuration data out of the NVM manually. + * + * hw: Struct containing variables accessed by shared code + *****************************************************************************/ +static int32_t +e1000_init_lcd_from_nvm(struct e1000_hw *hw) +{ + uint32_t reg_data, cnf_base_addr, cnf_size, ret_val, loop; + + if (hw->phy_type != e1000_phy_igp_3) + return E1000_SUCCESS; + + /* Check if SW needs configure the PHY */ + reg_data = E1000_READ_REG(hw, FEXTNVM); + if (!(reg_data & FEXTNVM_SW_CONFIG)) + return E1000_SUCCESS; + + /* Wait for basic configuration completes before proceeding*/ + loop = 0; + do { + reg_data = E1000_READ_REG(hw, STATUS) & E1000_STATUS_LAN_INIT_DONE; + udelay(100); + loop++; + } while ((!reg_data) && (loop < 50)); + + /* Clear the Init Done bit for the next init event */ + reg_data = E1000_READ_REG(hw, STATUS); + reg_data &= ~E1000_STATUS_LAN_INIT_DONE; + E1000_WRITE_REG(hw, STATUS, reg_data); + + /* Make sure HW does not configure LCD from PHY extended configuration + before SW configuration */ + reg_data = E1000_READ_REG(hw, EXTCNF_CTRL); + if ((reg_data & E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE) == 0x0000) { + reg_data = E1000_READ_REG(hw, EXTCNF_SIZE); + cnf_size = reg_data & E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH; + cnf_size >>= 16; + if (cnf_size) { + reg_data = E1000_READ_REG(hw, EXTCNF_CTRL); + cnf_base_addr = reg_data & E1000_EXTCNF_CTRL_EXT_CNF_POINTER; + /* cnf_base_addr is in DWORD */ + cnf_base_addr >>= 16; + + /* Configure LCD from extended configuration region. */ + ret_val = e1000_init_lcd_from_nvm_config_region(hw, cnf_base_addr, + cnf_size); + if (ret_val) + return ret_val; + } + } + + return E1000_SUCCESS; +} + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/e1000/e1000_hw.h b/gpxe/src/drivers/net/e1000/e1000_hw.h new file mode 100644 index 0000000..9e31939 --- /dev/null +++ b/gpxe/src/drivers/net/e1000/e1000_hw.h @@ -0,0 +1,3413 @@ +/******************************************************************************* + + Intel PRO/1000 Linux driver + Copyright(c) 1999 - 2006 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +/* e1000_hw.h + * Structures, enums, and macros for the MAC + */ + +#ifndef _E1000_HW_H_ +#define _E1000_HW_H_ + +#include "e1000_osdep.h" + + +/* Forward declarations of structures used by the shared code */ +struct e1000_hw; +struct e1000_hw_stats; + +/* Enumerated types specific to the e1000 hardware */ +/* Media Access Controlers */ +typedef enum { + e1000_undefined = 0, + e1000_82542_rev2_0, + e1000_82542_rev2_1, + e1000_82543, + e1000_82544, + e1000_82540, + e1000_82545, + e1000_82545_rev_3, + e1000_82546, + e1000_82546_rev_3, + e1000_82541, + e1000_82541_rev_2, + e1000_82547, + e1000_82547_rev_2, + e1000_82571, + e1000_82572, + e1000_82573, + e1000_80003es2lan, + e1000_ich8lan, + e1000_num_macs +} e1000_mac_type; + +typedef enum { + e1000_eeprom_uninitialized = 0, + e1000_eeprom_spi, + e1000_eeprom_microwire, + e1000_eeprom_flash, + e1000_eeprom_ich8, + e1000_eeprom_none, /* No NVM support */ + e1000_num_eeprom_types +} e1000_eeprom_type; + +/* Media Types */ +typedef enum { + e1000_media_type_copper = 0, + e1000_media_type_fiber = 1, + e1000_media_type_internal_serdes = 2, + e1000_num_media_types +} e1000_media_type; + +typedef enum { + e1000_10_half = 0, + e1000_10_full = 1, + e1000_100_half = 2, + e1000_100_full = 3 +} e1000_speed_duplex_type; + +/* Flow Control Settings */ +typedef enum { + E1000_FC_NONE = 0, + E1000_FC_RX_PAUSE = 1, + E1000_FC_TX_PAUSE = 2, + E1000_FC_FULL = 3, + E1000_FC_DEFAULT = 0xFF +} e1000_fc_type; + +struct e1000_shadow_ram { + uint16_t eeprom_word; + boolean_t modified; +}; + +/* PCI bus types */ +typedef enum { + e1000_bus_type_unknown = 0, + e1000_bus_type_pci, + e1000_bus_type_pcix, + e1000_bus_type_pci_express, + e1000_bus_type_reserved +} e1000_bus_type; + +/* PCI bus speeds */ +typedef enum { + e1000_bus_speed_unknown = 0, + e1000_bus_speed_33, + e1000_bus_speed_66, + e1000_bus_speed_100, + e1000_bus_speed_120, + e1000_bus_speed_133, + e1000_bus_speed_2500, + e1000_bus_speed_reserved +} e1000_bus_speed; + +/* PCI bus widths */ +typedef enum { + e1000_bus_width_unknown = 0, + /* These PCIe values should literally match the possible return values + * from config space */ + e1000_bus_width_pciex_1 = 1, + e1000_bus_width_pciex_2 = 2, + e1000_bus_width_pciex_4 = 4, + e1000_bus_width_32, + e1000_bus_width_64, + e1000_bus_width_reserved +} e1000_bus_width; + +/* PHY status info structure and supporting enums */ +typedef enum { + e1000_cable_length_50 = 0, + e1000_cable_length_50_80, + e1000_cable_length_80_110, + e1000_cable_length_110_140, + e1000_cable_length_140, + e1000_cable_length_undefined = 0xFF +} e1000_cable_length; + +typedef enum { + e1000_gg_cable_length_60 = 0, + e1000_gg_cable_length_60_115 = 1, + e1000_gg_cable_length_115_150 = 2, + e1000_gg_cable_length_150 = 4 +} e1000_gg_cable_length; + +typedef enum { + e1000_igp_cable_length_10 = 10, + e1000_igp_cable_length_20 = 20, + e1000_igp_cable_length_30 = 30, + e1000_igp_cable_length_40 = 40, + e1000_igp_cable_length_50 = 50, + e1000_igp_cable_length_60 = 60, + e1000_igp_cable_length_70 = 70, + e1000_igp_cable_length_80 = 80, + e1000_igp_cable_length_90 = 90, + e1000_igp_cable_length_100 = 100, + e1000_igp_cable_length_110 = 110, + e1000_igp_cable_length_115 = 115, + e1000_igp_cable_length_120 = 120, + e1000_igp_cable_length_130 = 130, + e1000_igp_cable_length_140 = 140, + e1000_igp_cable_length_150 = 150, + e1000_igp_cable_length_160 = 160, + e1000_igp_cable_length_170 = 170, + e1000_igp_cable_length_180 = 180 +} e1000_igp_cable_length; + +typedef enum { + e1000_10bt_ext_dist_enable_normal = 0, + e1000_10bt_ext_dist_enable_lower, + e1000_10bt_ext_dist_enable_undefined = 0xFF +} e1000_10bt_ext_dist_enable; + +typedef enum { + e1000_rev_polarity_normal = 0, + e1000_rev_polarity_reversed, + e1000_rev_polarity_undefined = 0xFF +} e1000_rev_polarity; + +typedef enum { + e1000_downshift_normal = 0, + e1000_downshift_activated, + e1000_downshift_undefined = 0xFF +} e1000_downshift; + +typedef enum { + e1000_smart_speed_default = 0, + e1000_smart_speed_on, + e1000_smart_speed_off +} e1000_smart_speed; + +typedef enum { + e1000_polarity_reversal_enabled = 0, + e1000_polarity_reversal_disabled, + e1000_polarity_reversal_undefined = 0xFF +} e1000_polarity_reversal; + +typedef enum { + e1000_auto_x_mode_manual_mdi = 0, + e1000_auto_x_mode_manual_mdix, + e1000_auto_x_mode_auto1, + e1000_auto_x_mode_auto2, + e1000_auto_x_mode_undefined = 0xFF +} e1000_auto_x_mode; + +typedef enum { + e1000_1000t_rx_status_not_ok = 0, + e1000_1000t_rx_status_ok, + e1000_1000t_rx_status_undefined = 0xFF +} e1000_1000t_rx_status; + +typedef enum { + e1000_phy_m88 = 0, + e1000_phy_igp, + e1000_phy_igp_2, + e1000_phy_gg82563, + e1000_phy_igp_3, + e1000_phy_ife, + e1000_phy_undefined = 0xFF +} e1000_phy_type; + +typedef enum { + e1000_ms_hw_default = 0, + e1000_ms_force_master, + e1000_ms_force_slave, + e1000_ms_auto +} e1000_ms_type; + +typedef enum { + e1000_ffe_config_enabled = 0, + e1000_ffe_config_active, + e1000_ffe_config_blocked +} e1000_ffe_config; + +typedef enum { + e1000_dsp_config_disabled = 0, + e1000_dsp_config_enabled, + e1000_dsp_config_activated, + e1000_dsp_config_undefined = 0xFF +} e1000_dsp_config; + +struct e1000_phy_info { + e1000_cable_length cable_length; + e1000_10bt_ext_dist_enable extended_10bt_distance; + e1000_rev_polarity cable_polarity; + e1000_downshift downshift; + e1000_polarity_reversal polarity_correction; + e1000_auto_x_mode mdix_mode; + e1000_1000t_rx_status local_rx; + e1000_1000t_rx_status remote_rx; +}; + +struct e1000_phy_stats { + uint32_t idle_errors; + uint32_t receive_errors; +}; + +struct e1000_eeprom_info { + e1000_eeprom_type type; + uint16_t word_size; + uint16_t opcode_bits; + uint16_t address_bits; + uint16_t delay_usec; + uint16_t page_size; + boolean_t use_eerd; + boolean_t use_eewr; +}; + +/* Flex ASF Information */ +#define E1000_HOST_IF_MAX_SIZE 2048 + +typedef enum { + e1000_byte_align = 0, + e1000_word_align = 1, + e1000_dword_align = 2 +} e1000_align_type; + + + +/* Error Codes */ +#define E1000_SUCCESS 0 +#define E1000_ERR_EEPROM 1 +#define E1000_ERR_PHY 2 +#define E1000_ERR_CONFIG 3 +#define E1000_ERR_PARAM 4 +#define E1000_ERR_MAC_TYPE 5 +#define E1000_ERR_PHY_TYPE 6 +#define E1000_ERR_RESET 9 +#define E1000_ERR_MASTER_REQUESTS_PENDING 10 +#define E1000_ERR_HOST_INTERFACE_COMMAND 11 +#define E1000_BLK_PHY_RESET 12 +#define E1000_ERR_SWFW_SYNC 13 + +#define E1000_BYTE_SWAP_WORD(_value) ((((_value) & 0x00ff) << 8) | \ + (((_value) & 0xff00) >> 8)) + +/* Function prototypes */ +/* Initialization */ +int32_t e1000_reset_hw(struct e1000_hw *hw); +int32_t e1000_init_hw(struct e1000_hw *hw); +int32_t e1000_set_mac_type(struct e1000_hw *hw); +void e1000_set_media_type(struct e1000_hw *hw); + +/* Link Configuration */ +int32_t e1000_setup_link(struct e1000_hw *hw); +int32_t e1000_phy_setup_autoneg(struct e1000_hw *hw); +void e1000_config_collision_dist(struct e1000_hw *hw); +int32_t e1000_check_for_link(struct e1000_hw *hw); +int32_t e1000_get_speed_and_duplex(struct e1000_hw *hw, uint16_t *speed, uint16_t *duplex); +int32_t e1000_force_mac_fc(struct e1000_hw *hw); + +/* PHY */ +int32_t e1000_read_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *phy_data); +int32_t e1000_write_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t data); +int32_t e1000_phy_hw_reset(struct e1000_hw *hw); +int32_t e1000_phy_reset(struct e1000_hw *hw); +int32_t e1000_phy_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info); +int32_t e1000_validate_mdi_setting(struct e1000_hw *hw); + +void e1000_phy_powerdown_workaround(struct e1000_hw *hw); + +/* EEPROM Functions */ +int32_t e1000_init_eeprom_params(struct e1000_hw *hw); + +/* MNG HOST IF functions */ +uint32_t e1000_enable_mng_pass_thru(struct e1000_hw *hw); + +#define E1000_MNG_DHCP_TX_PAYLOAD_CMD 64 +#define E1000_HI_MAX_MNG_DATA_LENGTH 0x6F8 /* Host Interface data length */ + +#define E1000_MNG_DHCP_COMMAND_TIMEOUT 10 /* Time in ms to process MNG command */ +#define E1000_MNG_DHCP_COOKIE_OFFSET 0x6F0 /* Cookie offset */ +#define E1000_MNG_DHCP_COOKIE_LENGTH 0x10 /* Cookie length */ +#define E1000_MNG_IAMT_MODE 0x3 +#define E1000_MNG_ICH_IAMT_MODE 0x2 +#define E1000_IAMT_SIGNATURE 0x544D4149 /* Intel(R) Active Management Technology signature */ + +#define E1000_MNG_DHCP_COOKIE_STATUS_PARSING_SUPPORT 0x1 /* DHCP parsing enabled */ +#define E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT 0x2 /* DHCP parsing enabled */ +#define E1000_VFTA_ENTRY_SHIFT 0x5 +#define E1000_VFTA_ENTRY_MASK 0x7F +#define E1000_VFTA_ENTRY_BIT_SHIFT_MASK 0x1F + +struct e1000_host_mng_command_header { + uint8_t command_id; + uint8_t checksum; + uint16_t reserved1; + uint16_t reserved2; + uint16_t command_length; +}; + +struct e1000_host_mng_command_info { + struct e1000_host_mng_command_header command_header; /* Command Head/Command Result Head has 4 bytes */ + uint8_t command_data[E1000_HI_MAX_MNG_DATA_LENGTH]; /* Command data can length 0..0x658*/ +}; +#ifdef __BIG_ENDIAN +struct e1000_host_mng_dhcp_cookie{ + uint32_t signature; + uint16_t vlan_id; + uint8_t reserved0; + uint8_t status; + uint32_t reserved1; + uint8_t checksum; + uint8_t reserved3; + uint16_t reserved2; +}; +#else +struct e1000_host_mng_dhcp_cookie{ + uint32_t signature; + uint8_t status; + uint8_t reserved0; + uint16_t vlan_id; + uint32_t reserved1; + uint16_t reserved2; + uint8_t reserved3; + uint8_t checksum; +}; +#endif + +int32_t e1000_mng_write_dhcp_info(struct e1000_hw *hw, uint8_t *buffer, + uint16_t length); +boolean_t e1000_check_mng_mode(struct e1000_hw *hw); +boolean_t e1000_enable_tx_pkt_filtering(struct e1000_hw *hw); +int32_t e1000_read_eeprom(struct e1000_hw *hw, uint16_t reg, uint16_t words, uint16_t *data); +int32_t e1000_validate_eeprom_checksum(struct e1000_hw *hw); +int32_t e1000_update_eeprom_checksum(struct e1000_hw *hw); +int32_t e1000_write_eeprom(struct e1000_hw *hw, uint16_t reg, uint16_t words, uint16_t *data); +int32_t e1000_read_mac_addr(struct e1000_hw * hw); + +/* Filters (multicast, vlan, receive) */ +uint32_t e1000_hash_mc_addr(struct e1000_hw *hw, uint8_t * mc_addr); +void e1000_mta_set(struct e1000_hw *hw, uint32_t hash_value); +void e1000_rar_set(struct e1000_hw *hw, uint8_t * mc_addr, uint32_t rar_index); +void e1000_write_vfta(struct e1000_hw *hw, uint32_t offset, uint32_t value); + +/* LED functions */ +int32_t e1000_setup_led(struct e1000_hw *hw); +int32_t e1000_cleanup_led(struct e1000_hw *hw); +int32_t e1000_led_on(struct e1000_hw *hw); +int32_t e1000_led_off(struct e1000_hw *hw); +int32_t e1000_blink_led_start(struct e1000_hw *hw); + +/* Adaptive IFS Functions */ + +/* Everything else */ +void e1000_reset_adaptive(struct e1000_hw *hw); +void e1000_update_adaptive(struct e1000_hw *hw); +void e1000_tbi_adjust_stats(struct e1000_hw *hw, struct e1000_hw_stats *stats, uint32_t frame_len, uint8_t * mac_addr); +void e1000_get_bus_info(struct e1000_hw *hw); +void e1000_pci_set_mwi(struct e1000_hw *hw); +void e1000_pci_clear_mwi(struct e1000_hw *hw); +void e1000_read_pci_cfg(struct e1000_hw *hw, uint32_t reg, uint16_t * value); +void e1000_write_pci_cfg(struct e1000_hw *hw, uint32_t reg, uint16_t * value); +int32_t e1000_read_pcie_cap_reg(struct e1000_hw *hw, uint32_t reg, uint16_t *value); +/* Port I/O is only supported on 82544 and newer */ +void e1000_io_write(struct e1000_hw *hw, unsigned long port, uint32_t value); +int32_t e1000_disable_pciex_master(struct e1000_hw *hw); +int32_t e1000_check_phy_reset_block(struct e1000_hw *hw); + + +#define E1000_READ_REG_IO(a, reg) \ + e1000_read_reg_io((a), E1000_##reg) +#define E1000_WRITE_REG_IO(a, reg, val) \ + e1000_write_reg_io((a), E1000_##reg, val) + +/* PCI Device IDs */ +#define E1000_DEV_ID_82542 0x1000 +#define E1000_DEV_ID_82543GC_FIBER 0x1001 +#define E1000_DEV_ID_82543GC_COPPER 0x1004 +#define E1000_DEV_ID_82544EI_COPPER 0x1008 +#define E1000_DEV_ID_82544EI_FIBER 0x1009 +#define E1000_DEV_ID_82544GC_COPPER 0x100C +#define E1000_DEV_ID_82544GC_LOM 0x100D +#define E1000_DEV_ID_82540EM 0x100E +#define E1000_DEV_ID_82540EM_LOM 0x1015 +#define E1000_DEV_ID_82540EP_LOM 0x1016 +#define E1000_DEV_ID_82540EP 0x1017 +#define E1000_DEV_ID_82540EP_LP 0x101E +#define E1000_DEV_ID_82545EM_COPPER 0x100F +#define E1000_DEV_ID_82545EM_FIBER 0x1011 +#define E1000_DEV_ID_82545GM_COPPER 0x1026 +#define E1000_DEV_ID_82545GM_FIBER 0x1027 +#define E1000_DEV_ID_82545GM_SERDES 0x1028 +#define E1000_DEV_ID_82546EB_COPPER 0x1010 +#define E1000_DEV_ID_82546EB_FIBER 0x1012 +#define E1000_DEV_ID_82546EB_QUAD_COPPER 0x101D +#define E1000_DEV_ID_82541EI 0x1013 +#define E1000_DEV_ID_82541EI_MOBILE 0x1018 +#define E1000_DEV_ID_82541ER_LOM 0x1014 +#define E1000_DEV_ID_82541ER 0x1078 +#define E1000_DEV_ID_82547GI 0x1075 +#define E1000_DEV_ID_82541GI 0x1076 +#define E1000_DEV_ID_82541GI_MOBILE 0x1077 +#define E1000_DEV_ID_82541GI_LF 0x107C +#define E1000_DEV_ID_82546GB_COPPER 0x1079 +#define E1000_DEV_ID_82546GB_FIBER 0x107A +#define E1000_DEV_ID_82546GB_SERDES 0x107B +#define E1000_DEV_ID_82546GB_PCIE 0x108A +#define E1000_DEV_ID_82546GB_QUAD_COPPER 0x1099 +#define E1000_DEV_ID_82547EI 0x1019 +#define E1000_DEV_ID_82547EI_MOBILE 0x101A +#define E1000_DEV_ID_82571EB_COPPER 0x105E +#define E1000_DEV_ID_82571EB_FIBER 0x105F +#define E1000_DEV_ID_82571EB_SERDES 0x1060 +#define E1000_DEV_ID_82571EB_QUAD_COPPER 0x10A4 +#define E1000_DEV_ID_82571EB_QUAD_FIBER 0x10A5 +#define E1000_DEV_ID_82571EB_QUAD_COPPER_LOWPROFILE 0x10BC +#define E1000_DEV_ID_82571EB_SERDES_DUAL 0x10D9 +#define E1000_DEV_ID_82571EB_SERDES_QUAD 0x10DA +#define E1000_DEV_ID_82572EI_COPPER 0x107D +#define E1000_DEV_ID_82572EI_FIBER 0x107E +#define E1000_DEV_ID_82572EI_SERDES 0x107F +#define E1000_DEV_ID_82572EI 0x10B9 +#define E1000_DEV_ID_82573E 0x108B +#define E1000_DEV_ID_82573E_IAMT 0x108C +#define E1000_DEV_ID_82573L 0x109A +#define E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3 0x10B5 +#define E1000_DEV_ID_80003ES2LAN_COPPER_DPT 0x1096 +#define E1000_DEV_ID_80003ES2LAN_SERDES_DPT 0x1098 +#define E1000_DEV_ID_80003ES2LAN_COPPER_SPT 0x10BA +#define E1000_DEV_ID_80003ES2LAN_SERDES_SPT 0x10BB + +#define E1000_DEV_ID_ICH8_IGP_M_AMT 0x1049 +#define E1000_DEV_ID_ICH8_IGP_AMT 0x104A +#define E1000_DEV_ID_ICH8_IGP_C 0x104B +#define E1000_DEV_ID_ICH8_IFE 0x104C +#define E1000_DEV_ID_ICH8_IFE_GT 0x10C4 +#define E1000_DEV_ID_ICH8_IFE_G 0x10C5 +#define E1000_DEV_ID_ICH8_IGP_M 0x104D + + +#define NODE_ADDRESS_SIZE 6 +#define ETH_LENGTH_OF_ADDRESS 6 + +/* MAC decode size is 128K - This is the size of BAR0 */ +#define MAC_DECODE_SIZE (128 * 1024) + +#define E1000_82542_2_0_REV_ID 2 +#define E1000_82542_2_1_REV_ID 3 +#define E1000_REVISION_0 0 +#define E1000_REVISION_1 1 +#define E1000_REVISION_2 2 +#define E1000_REVISION_3 3 + +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 +#define HALF_DUPLEX 1 +#define FULL_DUPLEX 2 + +/* The sizes (in bytes) of a ethernet packet */ +#define ENET_HEADER_SIZE 14 +#define MAXIMUM_ETHERNET_FRAME_SIZE 1518 /* With FCS */ +#define MINIMUM_ETHERNET_FRAME_SIZE 64 /* With FCS */ +#define ETHERNET_FCS_SIZE 4 +#define MAXIMUM_ETHERNET_PACKET_SIZE \ + (MAXIMUM_ETHERNET_FRAME_SIZE - ETHERNET_FCS_SIZE) +#define MINIMUM_ETHERNET_PACKET_SIZE \ + (MINIMUM_ETHERNET_FRAME_SIZE - ETHERNET_FCS_SIZE) +#define CRC_LENGTH ETHERNET_FCS_SIZE +#define MAX_JUMBO_FRAME_SIZE 0x3F00 + + +/* 802.1q VLAN Packet Sizes */ +#define VLAN_TAG_SIZE 4 /* 802.3ac tag (not DMAed) */ + +/* Ethertype field values */ +#define ETHERNET_IEEE_VLAN_TYPE 0x8100 /* 802.3ac packet */ +#define ETHERNET_IP_TYPE 0x0800 /* IP packets */ +#define ETHERNET_ARP_TYPE 0x0806 /* Address Resolution Protocol (ARP) */ + +/* Packet Header defines */ +#define IP_PROTOCOL_TCP 6 +#define IP_PROTOCOL_UDP 0x11 + +/* This defines the bits that are set in the Interrupt Mask + * Set/Read Register. Each bit is documented below: + * o RXDMT0 = Receive Descriptor Minimum Threshold hit (ring 0) + * o RXSEQ = Receive Sequence Error + */ +#define POLL_IMS_ENABLE_MASK ( \ + E1000_IMS_RXDMT0 | \ + E1000_IMS_RXSEQ) + +/* This defines the bits that are set in the Interrupt Mask + * Set/Read Register. Each bit is documented below: + * o RXT0 = Receiver Timer Interrupt (ring 0) + * o TXDW = Transmit Descriptor Written Back + * o RXDMT0 = Receive Descriptor Minimum Threshold hit (ring 0) + * o RXSEQ = Receive Sequence Error + * o LSC = Link Status Change + */ +#define IMS_ENABLE_MASK ( \ + E1000_IMS_RXT0 | \ + E1000_IMS_TXDW | \ + E1000_IMS_RXDMT0 | \ + E1000_IMS_RXSEQ | \ + E1000_IMS_LSC) + +/* Additional interrupts need to be handled for e1000_ich8lan: + DSW = The FW changed the status of the DISSW bit in FWSM + PHYINT = The LAN connected device generates an interrupt + EPRST = Manageability reset event */ +#define IMS_ICH8LAN_ENABLE_MASK (\ + E1000_IMS_DSW | \ + E1000_IMS_PHYINT | \ + E1000_IMS_EPRST) + +/* Number of high/low register pairs in the RAR. The RAR (Receive Address + * Registers) holds the directed and multicast addresses that we monitor. We + * reserve one of these spots for our directed address, allowing us room for + * E1000_RAR_ENTRIES - 1 multicast addresses. + */ +#define E1000_RAR_ENTRIES 15 + +#define E1000_RAR_ENTRIES_ICH8LAN 6 + +#define MIN_NUMBER_OF_DESCRIPTORS 8 +#define MAX_NUMBER_OF_DESCRIPTORS 0xFFF8 + +/* Receive Descriptor */ +struct e1000_rx_desc { + uint64_t buffer_addr; /* Address of the descriptor's data buffer */ + uint16_t length; /* Length of data DMAed into data buffer */ + uint16_t csum; /* Packet checksum */ + uint8_t status; /* Descriptor status */ + uint8_t errors; /* Descriptor Errors */ + uint16_t special; +}; + +/* Receive Descriptor - Extended */ +union e1000_rx_desc_extended { + struct { + uint64_t buffer_addr; + uint64_t reserved; + } read; + struct { + struct { + uint32_t mrq; /* Multiple Rx Queues */ + union { + uint32_t rss; /* RSS Hash */ + struct { + uint16_t ip_id; /* IP id */ + uint16_t csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + uint32_t status_error; /* ext status/error */ + uint16_t length; + uint16_t vlan; /* VLAN tag */ + } upper; + } wb; /* writeback */ +}; + +#define MAX_PS_BUFFERS 4 +/* Receive Descriptor - Packet Split */ +union e1000_rx_desc_packet_split { + struct { + /* one buffer for protocol header(s), three data buffers */ + uint64_t buffer_addr[MAX_PS_BUFFERS]; + } read; + struct { + struct { + uint32_t mrq; /* Multiple Rx Queues */ + union { + uint32_t rss; /* RSS Hash */ + struct { + uint16_t ip_id; /* IP id */ + uint16_t csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + uint32_t status_error; /* ext status/error */ + uint16_t length0; /* length of buffer 0 */ + uint16_t vlan; /* VLAN tag */ + } middle; + struct { + uint16_t header_status; + uint16_t length[3]; /* length of buffers 1-3 */ + } upper; + uint64_t reserved; + } wb; /* writeback */ +}; + +/* Receive Decriptor bit definitions */ +#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ +#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */ +#define E1000_RXD_STAT_IXSM 0x04 /* Ignore checksum */ +#define E1000_RXD_STAT_VP 0x08 /* IEEE VLAN Packet */ +#define E1000_RXD_STAT_UDPCS 0x10 /* UDP xsum caculated */ +#define E1000_RXD_STAT_TCPCS 0x20 /* TCP xsum calculated */ +#define E1000_RXD_STAT_IPCS 0x40 /* IP xsum calculated */ +#define E1000_RXD_STAT_PIF 0x80 /* passed in-exact filter */ +#define E1000_RXD_STAT_IPIDV 0x200 /* IP identification valid */ +#define E1000_RXD_STAT_UDPV 0x400 /* Valid UDP checksum */ +#define E1000_RXD_STAT_ACK 0x8000 /* ACK Packet indication */ +#define E1000_RXD_ERR_CE 0x01 /* CRC Error */ +#define E1000_RXD_ERR_SE 0x02 /* Symbol Error */ +#define E1000_RXD_ERR_SEQ 0x04 /* Sequence Error */ +#define E1000_RXD_ERR_CXE 0x10 /* Carrier Extension Error */ +#define E1000_RXD_ERR_TCPE 0x20 /* TCP/UDP Checksum Error */ +#define E1000_RXD_ERR_IPE 0x40 /* IP Checksum Error */ +#define E1000_RXD_ERR_RXE 0x80 /* Rx Data Error */ +#define E1000_RXD_SPC_VLAN_MASK 0x0FFF /* VLAN ID is in lower 12 bits */ +#define E1000_RXD_SPC_PRI_MASK 0xE000 /* Priority is in upper 3 bits */ +#define E1000_RXD_SPC_PRI_SHIFT 13 +#define E1000_RXD_SPC_CFI_MASK 0x1000 /* CFI is bit 12 */ +#define E1000_RXD_SPC_CFI_SHIFT 12 + +#define E1000_RXDEXT_STATERR_CE 0x01000000 +#define E1000_RXDEXT_STATERR_SE 0x02000000 +#define E1000_RXDEXT_STATERR_SEQ 0x04000000 +#define E1000_RXDEXT_STATERR_CXE 0x10000000 +#define E1000_RXDEXT_STATERR_TCPE 0x20000000 +#define E1000_RXDEXT_STATERR_IPE 0x40000000 +#define E1000_RXDEXT_STATERR_RXE 0x80000000 + +#define E1000_RXDPS_HDRSTAT_HDRSP 0x00008000 +#define E1000_RXDPS_HDRSTAT_HDRLEN_MASK 0x000003FF + +/* mask to determine if packets should be dropped due to frame errors */ +#define E1000_RXD_ERR_FRAME_ERR_MASK ( \ + E1000_RXD_ERR_CE | \ + E1000_RXD_ERR_SE | \ + E1000_RXD_ERR_SEQ | \ + E1000_RXD_ERR_CXE | \ + E1000_RXD_ERR_RXE) + + +/* Same mask, but for extended and packet split descriptors */ +#define E1000_RXDEXT_ERR_FRAME_ERR_MASK ( \ + E1000_RXDEXT_STATERR_CE | \ + E1000_RXDEXT_STATERR_SE | \ + E1000_RXDEXT_STATERR_SEQ | \ + E1000_RXDEXT_STATERR_CXE | \ + E1000_RXDEXT_STATERR_RXE) + + +/* Transmit Descriptor */ +struct e1000_tx_desc { + uint64_t buffer_addr; /* Address of the descriptor's data buffer */ + union { + uint32_t data; + struct { + uint16_t length; /* Data buffer length */ + uint8_t cso; /* Checksum offset */ + uint8_t cmd; /* Descriptor control */ + } flags; + } lower; + union { + uint32_t data; + struct { + uint8_t status; /* Descriptor status */ + uint8_t css; /* Checksum start */ + uint16_t special; + } fields; + } upper; +}; + +/* Transmit Descriptor bit definitions */ +#define E1000_TXD_DTYP_D 0x00100000 /* Data Descriptor */ +#define E1000_TXD_DTYP_C 0x00000000 /* Context Descriptor */ +#define E1000_TXD_POPTS_IXSM 0x01 /* Insert IP checksum */ +#define E1000_TXD_POPTS_TXSM 0x02 /* Insert TCP/UDP checksum */ +#define E1000_TXD_CMD_EOP 0x01000000 /* End of Packet */ +#define E1000_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */ +#define E1000_TXD_CMD_IC 0x04000000 /* Insert Checksum */ +#define E1000_TXD_CMD_RS 0x08000000 /* Report Status */ +#define E1000_TXD_CMD_RPS 0x10000000 /* Report Packet Sent */ +#define E1000_TXD_CMD_DEXT 0x20000000 /* Descriptor extension (0 = legacy) */ +#define E1000_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */ +#define E1000_TXD_CMD_IDE 0x80000000 /* Enable Tidv register */ +#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */ +#define E1000_TXD_STAT_EC 0x00000002 /* Excess Collisions */ +#define E1000_TXD_STAT_LC 0x00000004 /* Late Collisions */ +#define E1000_TXD_STAT_TU 0x00000008 /* Transmit underrun */ +#define E1000_TXD_CMD_TCP 0x01000000 /* TCP packet */ +#define E1000_TXD_CMD_IP 0x02000000 /* IP packet */ +#define E1000_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */ +#define E1000_TXD_STAT_TC 0x00000004 /* Tx Underrun */ + +/* Offload Context Descriptor */ +struct e1000_context_desc { + union { + uint32_t ip_config; + struct { + uint8_t ipcss; /* IP checksum start */ + uint8_t ipcso; /* IP checksum offset */ + uint16_t ipcse; /* IP checksum end */ + } ip_fields; + } lower_setup; + union { + uint32_t tcp_config; + struct { + uint8_t tucss; /* TCP checksum start */ + uint8_t tucso; /* TCP checksum offset */ + uint16_t tucse; /* TCP checksum end */ + } tcp_fields; + } upper_setup; + uint32_t cmd_and_length; /* */ + union { + uint32_t data; + struct { + uint8_t status; /* Descriptor status */ + uint8_t hdr_len; /* Header length */ + uint16_t mss; /* Maximum segment size */ + } fields; + } tcp_seg_setup; +}; + +/* Offload data descriptor */ +struct e1000_data_desc { + uint64_t buffer_addr; /* Address of the descriptor's buffer address */ + union { + uint32_t data; + struct { + uint16_t length; /* Data buffer length */ + uint8_t typ_len_ext; /* */ + uint8_t cmd; /* */ + } flags; + } lower; + union { + uint32_t data; + struct { + uint8_t status; /* Descriptor status */ + uint8_t popts; /* Packet Options */ + uint16_t special; /* */ + } fields; + } upper; +}; + +/* Filters */ +#define E1000_NUM_UNICAST 16 /* Unicast filter entries */ +#define E1000_MC_TBL_SIZE 128 /* Multicast Filter Table (4096 bits) */ +#define E1000_VLAN_FILTER_TBL_SIZE 128 /* VLAN Filter Table (4096 bits) */ + +#define E1000_NUM_UNICAST_ICH8LAN 7 +#define E1000_MC_TBL_SIZE_ICH8LAN 32 + + +/* Receive Address Register */ +struct e1000_rar { + volatile uint32_t low; /* receive address low */ + volatile uint32_t high; /* receive address high */ +}; + +/* Number of entries in the Multicast Table Array (MTA). */ +#define E1000_NUM_MTA_REGISTERS 128 +#define E1000_NUM_MTA_REGISTERS_ICH8LAN 32 + +/* IPv4 Address Table Entry */ +struct e1000_ipv4_at_entry { + volatile uint32_t ipv4_addr; /* IP Address (RW) */ + volatile uint32_t reserved; +}; + +/* Four wakeup IP addresses are supported */ +#define E1000_WAKEUP_IP_ADDRESS_COUNT_MAX 4 +#define E1000_IP4AT_SIZE E1000_WAKEUP_IP_ADDRESS_COUNT_MAX +#define E1000_IP4AT_SIZE_ICH8LAN 3 +#define E1000_IP6AT_SIZE 1 + +/* IPv6 Address Table Entry */ +struct e1000_ipv6_at_entry { + volatile uint8_t ipv6_addr[16]; +}; + +/* Flexible Filter Length Table Entry */ +struct e1000_fflt_entry { + volatile uint32_t length; /* Flexible Filter Length (RW) */ + volatile uint32_t reserved; +}; + +/* Flexible Filter Mask Table Entry */ +struct e1000_ffmt_entry { + volatile uint32_t mask; /* Flexible Filter Mask (RW) */ + volatile uint32_t reserved; +}; + +/* Flexible Filter Value Table Entry */ +struct e1000_ffvt_entry { + volatile uint32_t value; /* Flexible Filter Value (RW) */ + volatile uint32_t reserved; +}; + +/* Four Flexible Filters are supported */ +#define E1000_FLEXIBLE_FILTER_COUNT_MAX 4 + +/* Each Flexible Filter is at most 128 (0x80) bytes in length */ +#define E1000_FLEXIBLE_FILTER_SIZE_MAX 128 + +#define E1000_FFLT_SIZE E1000_FLEXIBLE_FILTER_COUNT_MAX +#define E1000_FFMT_SIZE E1000_FLEXIBLE_FILTER_SIZE_MAX +#define E1000_FFVT_SIZE E1000_FLEXIBLE_FILTER_SIZE_MAX + +#define E1000_DISABLE_SERDES_LOOPBACK 0x0400 + +/* Register Set. (82543, 82544) + * + * Registers are defined to be 32 bits and should be accessed as 32 bit values. + * These registers are physically located on the NIC, but are mapped into the + * host memory address space. + * + * RW - register is both readable and writable + * RO - register is read only + * WO - register is write only + * R/clr - register is read only and is cleared when read + * A - register array + */ +#define E1000_CTRL 0x00000 /* Device Control - RW */ +#define E1000_CTRL_DUP 0x00004 /* Device Control Duplicate (Shadow) - RW */ +#define E1000_STATUS 0x00008 /* Device Status - RO */ +#define E1000_EECD 0x00010 /* EEPROM/Flash Control - RW */ +#define E1000_EERD 0x00014 /* EEPROM Read - RW */ +#define E1000_CTRL_EXT 0x00018 /* Extended Device Control - RW */ +#define E1000_FLA 0x0001C /* Flash Access - RW */ +#define E1000_MDIC 0x00020 /* MDI Control - RW */ +#define E1000_SCTL 0x00024 /* SerDes Control - RW */ +#define E1000_FEXTNVM 0x00028 /* Future Extended NVM register */ +#define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */ +#define E1000_FCAH 0x0002C /* Flow Control Address High -RW */ +#define E1000_FCT 0x00030 /* Flow Control Type - RW */ +#define E1000_VET 0x00038 /* VLAN Ether Type - RW */ +#define E1000_ICR 0x000C0 /* Interrupt Cause Read - R/clr */ +#define E1000_ITR 0x000C4 /* Interrupt Throttling Rate - RW */ +#define E1000_ICS 0x000C8 /* Interrupt Cause Set - WO */ +#define E1000_IMS 0x000D0 /* Interrupt Mask Set - RW */ +#define E1000_IMC 0x000D8 /* Interrupt Mask Clear - WO */ +#define E1000_IAM 0x000E0 /* Interrupt Acknowledge Auto Mask */ +#define E1000_RCTL 0x00100 /* RX Control - RW */ +#define E1000_RDTR1 0x02820 /* RX Delay Timer (1) - RW */ +#define E1000_RDBAL1 0x02900 /* RX Descriptor Base Address Low (1) - RW */ +#define E1000_RDBAH1 0x02904 /* RX Descriptor Base Address High (1) - RW */ +#define E1000_RDLEN1 0x02908 /* RX Descriptor Length (1) - RW */ +#define E1000_RDH1 0x02910 /* RX Descriptor Head (1) - RW */ +#define E1000_RDT1 0x02918 /* RX Descriptor Tail (1) - RW */ +#define E1000_FCTTV 0x00170 /* Flow Control Transmit Timer Value - RW */ +#define E1000_TXCW 0x00178 /* TX Configuration Word - RW */ +#define E1000_RXCW 0x00180 /* RX Configuration Word - RO */ +#define E1000_TCTL 0x00400 /* TX Control - RW */ +#define E1000_TCTL_EXT 0x00404 /* Extended TX Control - RW */ +#define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */ +#define E1000_TBT 0x00448 /* TX Burst Timer - RW */ +#define E1000_AIT 0x00458 /* Adaptive Interframe Spacing Throttle - RW */ +#define E1000_LEDCTL 0x00E00 /* LED Control - RW */ +#define E1000_EXTCNF_CTRL 0x00F00 /* Extended Configuration Control */ +#define E1000_EXTCNF_SIZE 0x00F08 /* Extended Configuration Size */ +#define E1000_PHY_CTRL 0x00F10 /* PHY Control Register in CSR */ +#define FEXTNVM_SW_CONFIG 0x0001 +#define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */ +#define E1000_PBS 0x01008 /* Packet Buffer Size */ +#define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */ +#define E1000_FLASH_UPDATES 1000 +#define E1000_EEARBC 0x01024 /* EEPROM Auto Read Bus Control */ +#define E1000_FLASHT 0x01028 /* FLASH Timer Register */ +#define E1000_EEWR 0x0102C /* EEPROM Write Register - RW */ +#define E1000_FLSWCTL 0x01030 /* FLASH control register */ +#define E1000_FLSWDATA 0x01034 /* FLASH data register */ +#define E1000_FLSWCNT 0x01038 /* FLASH Access Counter */ +#define E1000_FLOP 0x0103C /* FLASH Opcode Register */ +#define E1000_ERT 0x02008 /* Early Rx Threshold - RW */ +#define E1000_FCRTL 0x02160 /* Flow Control Receive Threshold Low - RW */ +#define E1000_FCRTH 0x02168 /* Flow Control Receive Threshold High - RW */ +#define E1000_PSRCTL 0x02170 /* Packet Split Receive Control - RW */ +#define E1000_RDBAL 0x02800 /* RX Descriptor Base Address Low - RW */ +#define E1000_RDBAH 0x02804 /* RX Descriptor Base Address High - RW */ +#define E1000_RDLEN 0x02808 /* RX Descriptor Length - RW */ +#define E1000_RDH 0x02810 /* RX Descriptor Head - RW */ +#define E1000_RDT 0x02818 /* RX Descriptor Tail - RW */ +#define E1000_RDTR 0x02820 /* RX Delay Timer - RW */ +#define E1000_RDBAL0 E1000_RDBAL /* RX Desc Base Address Low (0) - RW */ +#define E1000_RDBAH0 E1000_RDBAH /* RX Desc Base Address High (0) - RW */ +#define E1000_RDLEN0 E1000_RDLEN /* RX Desc Length (0) - RW */ +#define E1000_RDH0 E1000_RDH /* RX Desc Head (0) - RW */ +#define E1000_RDT0 E1000_RDT /* RX Desc Tail (0) - RW */ +#define E1000_RDTR0 E1000_RDTR /* RX Delay Timer (0) - RW */ +#define E1000_RXDCTL 0x02828 /* RX Descriptor Control queue 0 - RW */ +#define E1000_RXDCTL1 0x02928 /* RX Descriptor Control queue 1 - RW */ +#define E1000_RADV 0x0282C /* RX Interrupt Absolute Delay Timer - RW */ +#define E1000_RSRPD 0x02C00 /* RX Small Packet Detect - RW */ +#define E1000_RAID 0x02C08 /* Receive Ack Interrupt Delay - RW */ +#define E1000_TXDMAC 0x03000 /* TX DMA Control - RW */ +#define E1000_KABGTXD 0x03004 /* AFE Band Gap Transmit Ref Data */ +#define E1000_TDFH 0x03410 /* TX Data FIFO Head - RW */ +#define E1000_TDFT 0x03418 /* TX Data FIFO Tail - RW */ +#define E1000_TDFHS 0x03420 /* TX Data FIFO Head Saved - RW */ +#define E1000_TDFTS 0x03428 /* TX Data FIFO Tail Saved - RW */ +#define E1000_TDFPC 0x03430 /* TX Data FIFO Packet Count - RW */ +#define E1000_TDBAL 0x03800 /* TX Descriptor Base Address Low - RW */ +#define E1000_TDBAH 0x03804 /* TX Descriptor Base Address High - RW */ +#define E1000_TDLEN 0x03808 /* TX Descriptor Length - RW */ +#define E1000_TDH 0x03810 /* TX Descriptor Head - RW */ +#define E1000_TDT 0x03818 /* TX Descripotr Tail - RW */ +#define E1000_TIDV 0x03820 /* TX Interrupt Delay Value - RW */ +#define E1000_TXDCTL 0x03828 /* TX Descriptor Control - RW */ +#define E1000_TADV 0x0382C /* TX Interrupt Absolute Delay Val - RW */ +#define E1000_TSPMT 0x03830 /* TCP Segmentation PAD & Min Threshold - RW */ +#define E1000_TARC0 0x03840 /* TX Arbitration Count (0) */ +#define E1000_TDBAL1 0x03900 /* TX Desc Base Address Low (1) - RW */ +#define E1000_TDBAH1 0x03904 /* TX Desc Base Address High (1) - RW */ +#define E1000_TDLEN1 0x03908 /* TX Desc Length (1) - RW */ +#define E1000_TDH1 0x03910 /* TX Desc Head (1) - RW */ +#define E1000_TDT1 0x03918 /* TX Desc Tail (1) - RW */ +#define E1000_TXDCTL1 0x03928 /* TX Descriptor Control (1) - RW */ +#define E1000_TARC1 0x03940 /* TX Arbitration Count (1) */ +#define E1000_CRCERRS 0x04000 /* CRC Error Count - R/clr */ +#define E1000_ALGNERRC 0x04004 /* Alignment Error Count - R/clr */ +#define E1000_SYMERRS 0x04008 /* Symbol Error Count - R/clr */ +#define E1000_RXERRC 0x0400C /* Receive Error Count - R/clr */ +#define E1000_MPC 0x04010 /* Missed Packet Count - R/clr */ +#define E1000_SCC 0x04014 /* Single Collision Count - R/clr */ +#define E1000_ECOL 0x04018 /* Excessive Collision Count - R/clr */ +#define E1000_MCC 0x0401C /* Multiple Collision Count - R/clr */ +#define E1000_LATECOL 0x04020 /* Late Collision Count - R/clr */ +#define E1000_COLC 0x04028 /* Collision Count - R/clr */ +#define E1000_DC 0x04030 /* Defer Count - R/clr */ +#define E1000_TNCRS 0x04034 /* TX-No CRS - R/clr */ +#define E1000_SEC 0x04038 /* Sequence Error Count - R/clr */ +#define E1000_CEXTERR 0x0403C /* Carrier Extension Error Count - R/clr */ +#define E1000_RLEC 0x04040 /* Receive Length Error Count - R/clr */ +#define E1000_XONRXC 0x04048 /* XON RX Count - R/clr */ +#define E1000_XONTXC 0x0404C /* XON TX Count - R/clr */ +#define E1000_XOFFRXC 0x04050 /* XOFF RX Count - R/clr */ +#define E1000_XOFFTXC 0x04054 /* XOFF TX Count - R/clr */ +#define E1000_FCRUC 0x04058 /* Flow Control RX Unsupported Count- R/clr */ +#define E1000_PRC64 0x0405C /* Packets RX (64 bytes) - R/clr */ +#define E1000_PRC127 0x04060 /* Packets RX (65-127 bytes) - R/clr */ +#define E1000_PRC255 0x04064 /* Packets RX (128-255 bytes) - R/clr */ +#define E1000_PRC511 0x04068 /* Packets RX (255-511 bytes) - R/clr */ +#define E1000_PRC1023 0x0406C /* Packets RX (512-1023 bytes) - R/clr */ +#define E1000_PRC1522 0x04070 /* Packets RX (1024-1522 bytes) - R/clr */ +#define E1000_GPRC 0x04074 /* Good Packets RX Count - R/clr */ +#define E1000_BPRC 0x04078 /* Broadcast Packets RX Count - R/clr */ +#define E1000_MPRC 0x0407C /* Multicast Packets RX Count - R/clr */ +#define E1000_GPTC 0x04080 /* Good Packets TX Count - R/clr */ +#define E1000_GORCL 0x04088 /* Good Octets RX Count Low - R/clr */ +#define E1000_GORCH 0x0408C /* Good Octets RX Count High - R/clr */ +#define E1000_GOTCL 0x04090 /* Good Octets TX Count Low - R/clr */ +#define E1000_GOTCH 0x04094 /* Good Octets TX Count High - R/clr */ +#define E1000_RNBC 0x040A0 /* RX No Buffers Count - R/clr */ +#define E1000_RUC 0x040A4 /* RX Undersize Count - R/clr */ +#define E1000_RFC 0x040A8 /* RX Fragment Count - R/clr */ +#define E1000_ROC 0x040AC /* RX Oversize Count - R/clr */ +#define E1000_RJC 0x040B0 /* RX Jabber Count - R/clr */ +#define E1000_MGTPRC 0x040B4 /* Management Packets RX Count - R/clr */ +#define E1000_MGTPDC 0x040B8 /* Management Packets Dropped Count - R/clr */ +#define E1000_MGTPTC 0x040BC /* Management Packets TX Count - R/clr */ +#define E1000_TORL 0x040C0 /* Total Octets RX Low - R/clr */ +#define E1000_TORH 0x040C4 /* Total Octets RX High - R/clr */ +#define E1000_TOTL 0x040C8 /* Total Octets TX Low - R/clr */ +#define E1000_TOTH 0x040CC /* Total Octets TX High - R/clr */ +#define E1000_TPR 0x040D0 /* Total Packets RX - R/clr */ +#define E1000_TPT 0x040D4 /* Total Packets TX - R/clr */ +#define E1000_PTC64 0x040D8 /* Packets TX (64 bytes) - R/clr */ +#define E1000_PTC127 0x040DC /* Packets TX (65-127 bytes) - R/clr */ +#define E1000_PTC255 0x040E0 /* Packets TX (128-255 bytes) - R/clr */ +#define E1000_PTC511 0x040E4 /* Packets TX (256-511 bytes) - R/clr */ +#define E1000_PTC1023 0x040E8 /* Packets TX (512-1023 bytes) - R/clr */ +#define E1000_PTC1522 0x040EC /* Packets TX (1024-1522 Bytes) - R/clr */ +#define E1000_MPTC 0x040F0 /* Multicast Packets TX Count - R/clr */ +#define E1000_BPTC 0x040F4 /* Broadcast Packets TX Count - R/clr */ +#define E1000_TSCTC 0x040F8 /* TCP Segmentation Context TX - R/clr */ +#define E1000_TSCTFC 0x040FC /* TCP Segmentation Context TX Fail - R/clr */ +#define E1000_IAC 0x04100 /* Interrupt Assertion Count */ +#define E1000_ICRXPTC 0x04104 /* Interrupt Cause Rx Packet Timer Expire Count */ +#define E1000_ICRXATC 0x04108 /* Interrupt Cause Rx Absolute Timer Expire Count */ +#define E1000_ICTXPTC 0x0410C /* Interrupt Cause Tx Packet Timer Expire Count */ +#define E1000_ICTXATC 0x04110 /* Interrupt Cause Tx Absolute Timer Expire Count */ +#define E1000_ICTXQEC 0x04118 /* Interrupt Cause Tx Queue Empty Count */ +#define E1000_ICTXQMTC 0x0411C /* Interrupt Cause Tx Queue Minimum Threshold Count */ +#define E1000_ICRXDMTC 0x04120 /* Interrupt Cause Rx Descriptor Minimum Threshold Count */ +#define E1000_ICRXOC 0x04124 /* Interrupt Cause Receiver Overrun Count */ +#define E1000_RXCSUM 0x05000 /* RX Checksum Control - RW */ +#define E1000_RFCTL 0x05008 /* Receive Filter Control*/ +#define E1000_MTA 0x05200 /* Multicast Table Array - RW Array */ +#define E1000_RA 0x05400 /* Receive Address - RW Array */ +#define E1000_VFTA 0x05600 /* VLAN Filter Table Array - RW Array */ +#define E1000_WUC 0x05800 /* Wakeup Control - RW */ +#define E1000_WUFC 0x05808 /* Wakeup Filter Control - RW */ +#define E1000_WUS 0x05810 /* Wakeup Status - RO */ +#define E1000_MANC 0x05820 /* Management Control - RW */ +#define E1000_IPAV 0x05838 /* IP Address Valid - RW */ +#define E1000_IP4AT 0x05840 /* IPv4 Address Table - RW Array */ +#define E1000_IP6AT 0x05880 /* IPv6 Address Table - RW Array */ +#define E1000_WUPL 0x05900 /* Wakeup Packet Length - RW */ +#define E1000_WUPM 0x05A00 /* Wakeup Packet Memory - RO A */ +#define E1000_FFLT 0x05F00 /* Flexible Filter Length Table - RW Array */ +#define E1000_HOST_IF 0x08800 /* Host Interface */ +#define E1000_FFMT 0x09000 /* Flexible Filter Mask Table - RW Array */ +#define E1000_FFVT 0x09800 /* Flexible Filter Value Table - RW Array */ + +#define E1000_KUMCTRLSTA 0x00034 /* MAC-PHY interface - RW */ +#define E1000_MDPHYA 0x0003C /* PHY address - RW */ +#define E1000_MANC2H 0x05860 /* Managment Control To Host - RW */ +#define E1000_SW_FW_SYNC 0x05B5C /* Software-Firmware Synchronization - RW */ + +#define E1000_GCR 0x05B00 /* PCI-Ex Control */ +#define E1000_GSCL_1 0x05B10 /* PCI-Ex Statistic Control #1 */ +#define E1000_GSCL_2 0x05B14 /* PCI-Ex Statistic Control #2 */ +#define E1000_GSCL_3 0x05B18 /* PCI-Ex Statistic Control #3 */ +#define E1000_GSCL_4 0x05B1C /* PCI-Ex Statistic Control #4 */ +#define E1000_FACTPS 0x05B30 /* Function Active and Power State to MNG */ +#define E1000_SWSM 0x05B50 /* SW Semaphore */ +#define E1000_FWSM 0x05B54 /* FW Semaphore */ +#define E1000_FFLT_DBG 0x05F04 /* Debug Register */ +#define E1000_HICR 0x08F00 /* Host Inteface Control */ + +/* RSS registers */ +#define E1000_CPUVEC 0x02C10 /* CPU Vector Register - RW */ +#define E1000_MRQC 0x05818 /* Multiple Receive Control - RW */ +#define E1000_RETA 0x05C00 /* Redirection Table - RW Array */ +#define E1000_RSSRK 0x05C80 /* RSS Random Key - RW Array */ +#define E1000_RSSIM 0x05864 /* RSS Interrupt Mask */ +#define E1000_RSSIR 0x05868 /* RSS Interrupt Request */ +/* Register Set (82542) + * + * Some of the 82542 registers are located at different offsets than they are + * in more current versions of the 8254x. Despite the difference in location, + * the registers function in the same manner. + */ +#define E1000_82542_CTRL E1000_CTRL +#define E1000_82542_CTRL_DUP E1000_CTRL_DUP +#define E1000_82542_STATUS E1000_STATUS +#define E1000_82542_EECD E1000_EECD +#define E1000_82542_EERD E1000_EERD +#define E1000_82542_CTRL_EXT E1000_CTRL_EXT +#define E1000_82542_FLA E1000_FLA +#define E1000_82542_MDIC E1000_MDIC +#define E1000_82542_SCTL E1000_SCTL +#define E1000_82542_FEXTNVM E1000_FEXTNVM +#define E1000_82542_FCAL E1000_FCAL +#define E1000_82542_FCAH E1000_FCAH +#define E1000_82542_FCT E1000_FCT +#define E1000_82542_VET E1000_VET +#define E1000_82542_RA 0x00040 +#define E1000_82542_ICR E1000_ICR +#define E1000_82542_ITR E1000_ITR +#define E1000_82542_ICS E1000_ICS +#define E1000_82542_IMS E1000_IMS +#define E1000_82542_IMC E1000_IMC +#define E1000_82542_RCTL E1000_RCTL +#define E1000_82542_RDTR 0x00108 +#define E1000_82542_RDBAL 0x00110 +#define E1000_82542_RDBAH 0x00114 +#define E1000_82542_RDLEN 0x00118 +#define E1000_82542_RDH 0x00120 +#define E1000_82542_RDT 0x00128 +#define E1000_82542_RDTR0 E1000_82542_RDTR +#define E1000_82542_RDBAL0 E1000_82542_RDBAL +#define E1000_82542_RDBAH0 E1000_82542_RDBAH +#define E1000_82542_RDLEN0 E1000_82542_RDLEN +#define E1000_82542_RDH0 E1000_82542_RDH +#define E1000_82542_RDT0 E1000_82542_RDT +#define E1000_82542_SRRCTL(_n) (0x280C + ((_n) << 8)) /* Split and Replication + * RX Control - RW */ +#define E1000_82542_DCA_RXCTRL(_n) (0x02814 + ((_n) << 8)) +#define E1000_82542_RDBAH3 0x02B04 /* RX Desc Base High Queue 3 - RW */ +#define E1000_82542_RDBAL3 0x02B00 /* RX Desc Low Queue 3 - RW */ +#define E1000_82542_RDLEN3 0x02B08 /* RX Desc Length Queue 3 - RW */ +#define E1000_82542_RDH3 0x02B10 /* RX Desc Head Queue 3 - RW */ +#define E1000_82542_RDT3 0x02B18 /* RX Desc Tail Queue 3 - RW */ +#define E1000_82542_RDBAL2 0x02A00 /* RX Desc Base Low Queue 2 - RW */ +#define E1000_82542_RDBAH2 0x02A04 /* RX Desc Base High Queue 2 - RW */ +#define E1000_82542_RDLEN2 0x02A08 /* RX Desc Length Queue 2 - RW */ +#define E1000_82542_RDH2 0x02A10 /* RX Desc Head Queue 2 - RW */ +#define E1000_82542_RDT2 0x02A18 /* RX Desc Tail Queue 2 - RW */ +#define E1000_82542_RDTR1 0x00130 +#define E1000_82542_RDBAL1 0x00138 +#define E1000_82542_RDBAH1 0x0013C +#define E1000_82542_RDLEN1 0x00140 +#define E1000_82542_RDH1 0x00148 +#define E1000_82542_RDT1 0x00150 +#define E1000_82542_FCRTH 0x00160 +#define E1000_82542_FCRTL 0x00168 +#define E1000_82542_FCTTV E1000_FCTTV +#define E1000_82542_TXCW E1000_TXCW +#define E1000_82542_RXCW E1000_RXCW +#define E1000_82542_MTA 0x00200 +#define E1000_82542_TCTL E1000_TCTL +#define E1000_82542_TCTL_EXT E1000_TCTL_EXT +#define E1000_82542_TIPG E1000_TIPG +#define E1000_82542_TDBAL 0x00420 +#define E1000_82542_TDBAH 0x00424 +#define E1000_82542_TDLEN 0x00428 +#define E1000_82542_TDH 0x00430 +#define E1000_82542_TDT 0x00438 +#define E1000_82542_TIDV 0x00440 +#define E1000_82542_TBT E1000_TBT +#define E1000_82542_AIT E1000_AIT +#define E1000_82542_VFTA 0x00600 +#define E1000_82542_LEDCTL E1000_LEDCTL +#define E1000_82542_PBA E1000_PBA +#define E1000_82542_PBS E1000_PBS +#define E1000_82542_EEMNGCTL E1000_EEMNGCTL +#define E1000_82542_EEARBC E1000_EEARBC +#define E1000_82542_FLASHT E1000_FLASHT +#define E1000_82542_EEWR E1000_EEWR +#define E1000_82542_FLSWCTL E1000_FLSWCTL +#define E1000_82542_FLSWDATA E1000_FLSWDATA +#define E1000_82542_FLSWCNT E1000_FLSWCNT +#define E1000_82542_FLOP E1000_FLOP +#define E1000_82542_EXTCNF_CTRL E1000_EXTCNF_CTRL +#define E1000_82542_EXTCNF_SIZE E1000_EXTCNF_SIZE +#define E1000_82542_PHY_CTRL E1000_PHY_CTRL +#define E1000_82542_ERT E1000_ERT +#define E1000_82542_RXDCTL E1000_RXDCTL +#define E1000_82542_RXDCTL1 E1000_RXDCTL1 +#define E1000_82542_RADV E1000_RADV +#define E1000_82542_RSRPD E1000_RSRPD +#define E1000_82542_TXDMAC E1000_TXDMAC +#define E1000_82542_KABGTXD E1000_KABGTXD +#define E1000_82542_TDFHS E1000_TDFHS +#define E1000_82542_TDFTS E1000_TDFTS +#define E1000_82542_TDFPC E1000_TDFPC +#define E1000_82542_TXDCTL E1000_TXDCTL +#define E1000_82542_TADV E1000_TADV +#define E1000_82542_TSPMT E1000_TSPMT +#define E1000_82542_CRCERRS E1000_CRCERRS +#define E1000_82542_ALGNERRC E1000_ALGNERRC +#define E1000_82542_SYMERRS E1000_SYMERRS +#define E1000_82542_RXERRC E1000_RXERRC +#define E1000_82542_MPC E1000_MPC +#define E1000_82542_SCC E1000_SCC +#define E1000_82542_ECOL E1000_ECOL +#define E1000_82542_MCC E1000_MCC +#define E1000_82542_LATECOL E1000_LATECOL +#define E1000_82542_COLC E1000_COLC +#define E1000_82542_DC E1000_DC +#define E1000_82542_TNCRS E1000_TNCRS +#define E1000_82542_SEC E1000_SEC +#define E1000_82542_CEXTERR E1000_CEXTERR +#define E1000_82542_RLEC E1000_RLEC +#define E1000_82542_XONRXC E1000_XONRXC +#define E1000_82542_XONTXC E1000_XONTXC +#define E1000_82542_XOFFRXC E1000_XOFFRXC +#define E1000_82542_XOFFTXC E1000_XOFFTXC +#define E1000_82542_FCRUC E1000_FCRUC +#define E1000_82542_PRC64 E1000_PRC64 +#define E1000_82542_PRC127 E1000_PRC127 +#define E1000_82542_PRC255 E1000_PRC255 +#define E1000_82542_PRC511 E1000_PRC511 +#define E1000_82542_PRC1023 E1000_PRC1023 +#define E1000_82542_PRC1522 E1000_PRC1522 +#define E1000_82542_GPRC E1000_GPRC +#define E1000_82542_BPRC E1000_BPRC +#define E1000_82542_MPRC E1000_MPRC +#define E1000_82542_GPTC E1000_GPTC +#define E1000_82542_GORCL E1000_GORCL +#define E1000_82542_GORCH E1000_GORCH +#define E1000_82542_GOTCL E1000_GOTCL +#define E1000_82542_GOTCH E1000_GOTCH +#define E1000_82542_RNBC E1000_RNBC +#define E1000_82542_RUC E1000_RUC +#define E1000_82542_RFC E1000_RFC +#define E1000_82542_ROC E1000_ROC +#define E1000_82542_RJC E1000_RJC +#define E1000_82542_MGTPRC E1000_MGTPRC +#define E1000_82542_MGTPDC E1000_MGTPDC +#define E1000_82542_MGTPTC E1000_MGTPTC +#define E1000_82542_TORL E1000_TORL +#define E1000_82542_TORH E1000_TORH +#define E1000_82542_TOTL E1000_TOTL +#define E1000_82542_TOTH E1000_TOTH +#define E1000_82542_TPR E1000_TPR +#define E1000_82542_TPT E1000_TPT +#define E1000_82542_PTC64 E1000_PTC64 +#define E1000_82542_PTC127 E1000_PTC127 +#define E1000_82542_PTC255 E1000_PTC255 +#define E1000_82542_PTC511 E1000_PTC511 +#define E1000_82542_PTC1023 E1000_PTC1023 +#define E1000_82542_PTC1522 E1000_PTC1522 +#define E1000_82542_MPTC E1000_MPTC +#define E1000_82542_BPTC E1000_BPTC +#define E1000_82542_TSCTC E1000_TSCTC +#define E1000_82542_TSCTFC E1000_TSCTFC +#define E1000_82542_RXCSUM E1000_RXCSUM +#define E1000_82542_WUC E1000_WUC +#define E1000_82542_WUFC E1000_WUFC +#define E1000_82542_WUS E1000_WUS +#define E1000_82542_MANC E1000_MANC +#define E1000_82542_IPAV E1000_IPAV +#define E1000_82542_IP4AT E1000_IP4AT +#define E1000_82542_IP6AT E1000_IP6AT +#define E1000_82542_WUPL E1000_WUPL +#define E1000_82542_WUPM E1000_WUPM +#define E1000_82542_FFLT E1000_FFLT +#define E1000_82542_TDFH 0x08010 +#define E1000_82542_TDFT 0x08018 +#define E1000_82542_FFMT E1000_FFMT +#define E1000_82542_FFVT E1000_FFVT +#define E1000_82542_HOST_IF E1000_HOST_IF +#define E1000_82542_IAM E1000_IAM +#define E1000_82542_EEMNGCTL E1000_EEMNGCTL +#define E1000_82542_PSRCTL E1000_PSRCTL +#define E1000_82542_RAID E1000_RAID +#define E1000_82542_TARC0 E1000_TARC0 +#define E1000_82542_TDBAL1 E1000_TDBAL1 +#define E1000_82542_TDBAH1 E1000_TDBAH1 +#define E1000_82542_TDLEN1 E1000_TDLEN1 +#define E1000_82542_TDH1 E1000_TDH1 +#define E1000_82542_TDT1 E1000_TDT1 +#define E1000_82542_TXDCTL1 E1000_TXDCTL1 +#define E1000_82542_TARC1 E1000_TARC1 +#define E1000_82542_RFCTL E1000_RFCTL +#define E1000_82542_GCR E1000_GCR +#define E1000_82542_GSCL_1 E1000_GSCL_1 +#define E1000_82542_GSCL_2 E1000_GSCL_2 +#define E1000_82542_GSCL_3 E1000_GSCL_3 +#define E1000_82542_GSCL_4 E1000_GSCL_4 +#define E1000_82542_FACTPS E1000_FACTPS +#define E1000_82542_SWSM E1000_SWSM +#define E1000_82542_FWSM E1000_FWSM +#define E1000_82542_FFLT_DBG E1000_FFLT_DBG +#define E1000_82542_IAC E1000_IAC +#define E1000_82542_ICRXPTC E1000_ICRXPTC +#define E1000_82542_ICRXATC E1000_ICRXATC +#define E1000_82542_ICTXPTC E1000_ICTXPTC +#define E1000_82542_ICTXATC E1000_ICTXATC +#define E1000_82542_ICTXQEC E1000_ICTXQEC +#define E1000_82542_ICTXQMTC E1000_ICTXQMTC +#define E1000_82542_ICRXDMTC E1000_ICRXDMTC +#define E1000_82542_ICRXOC E1000_ICRXOC +#define E1000_82542_HICR E1000_HICR + +#define E1000_82542_CPUVEC E1000_CPUVEC +#define E1000_82542_MRQC E1000_MRQC +#define E1000_82542_RETA E1000_RETA +#define E1000_82542_RSSRK E1000_RSSRK +#define E1000_82542_RSSIM E1000_RSSIM +#define E1000_82542_RSSIR E1000_RSSIR +#define E1000_82542_KUMCTRLSTA E1000_KUMCTRLSTA +#define E1000_82542_SW_FW_SYNC E1000_SW_FW_SYNC +#define E1000_82542_MANC2H E1000_MANC2H + +/* Statistics counters collected by the MAC */ +struct e1000_hw_stats { + uint64_t crcerrs; + uint64_t algnerrc; + uint64_t symerrs; + uint64_t rxerrc; + uint64_t txerrc; + uint64_t mpc; + uint64_t scc; + uint64_t ecol; + uint64_t mcc; + uint64_t latecol; + uint64_t colc; + uint64_t dc; + uint64_t tncrs; + uint64_t sec; + uint64_t cexterr; + uint64_t rlec; + uint64_t xonrxc; + uint64_t xontxc; + uint64_t xoffrxc; + uint64_t xofftxc; + uint64_t fcruc; + uint64_t prc64; + uint64_t prc127; + uint64_t prc255; + uint64_t prc511; + uint64_t prc1023; + uint64_t prc1522; + uint64_t gprc; + uint64_t bprc; + uint64_t mprc; + uint64_t gptc; + uint64_t gorcl; + uint64_t gorch; + uint64_t gotcl; + uint64_t gotch; + uint64_t rnbc; + uint64_t ruc; + uint64_t rfc; + uint64_t roc; + uint64_t rlerrc; + uint64_t rjc; + uint64_t mgprc; + uint64_t mgpdc; + uint64_t mgptc; + uint64_t torl; + uint64_t torh; + uint64_t totl; + uint64_t toth; + uint64_t tpr; + uint64_t tpt; + uint64_t ptc64; + uint64_t ptc127; + uint64_t ptc255; + uint64_t ptc511; + uint64_t ptc1023; + uint64_t ptc1522; + uint64_t mptc; + uint64_t bptc; + uint64_t tsctc; + uint64_t tsctfc; + uint64_t iac; + uint64_t icrxptc; + uint64_t icrxatc; + uint64_t ictxptc; + uint64_t ictxatc; + uint64_t ictxqec; + uint64_t ictxqmtc; + uint64_t icrxdmtc; + uint64_t icrxoc; +}; + +/* Structure containing variables used by the shared code (e1000_hw.c) */ +struct e1000_hw { + uint8_t *hw_addr; + uint8_t *flash_address; + e1000_mac_type mac_type; + e1000_phy_type phy_type; + uint32_t phy_init_script; + e1000_media_type media_type; + void *back; + struct e1000_shadow_ram *eeprom_shadow_ram; + uint32_t flash_bank_size; + uint32_t flash_base_addr; + e1000_fc_type fc; + e1000_bus_speed bus_speed; + e1000_bus_width bus_width; + e1000_bus_type bus_type; + struct e1000_eeprom_info eeprom; + e1000_ms_type master_slave; + e1000_ms_type original_master_slave; + e1000_ffe_config ffe_config_state; + uint32_t asf_firmware_present; + uint32_t eeprom_semaphore_present; + uint32_t swfw_sync_present; + uint32_t swfwhw_semaphore_present; + unsigned long io_base; + uint32_t phy_id; + uint32_t phy_revision; + uint32_t phy_addr; + uint32_t original_fc; + uint32_t txcw; + uint32_t autoneg_failed; + uint32_t max_frame_size; + uint32_t min_frame_size; + uint32_t mc_filter_type; + uint32_t num_mc_addrs; + uint32_t collision_delta; + uint32_t tx_packet_delta; + uint32_t ledctl_default; + uint32_t ledctl_mode1; + uint32_t ledctl_mode2; + boolean_t tx_pkt_filtering; + struct e1000_host_mng_dhcp_cookie mng_cookie; + uint16_t phy_spd_default; + uint16_t autoneg_advertised; + uint16_t pci_cmd_word; + uint16_t fc_high_water; + uint16_t fc_low_water; + uint16_t fc_pause_time; + uint16_t current_ifs_val; + uint16_t ifs_min_val; + uint16_t ifs_max_val; + uint16_t ifs_step_size; + uint16_t ifs_ratio; + uint16_t device_id; + uint16_t vendor_id; + uint16_t subsystem_id; + uint16_t subsystem_vendor_id; + uint8_t revision_id; + uint8_t autoneg; + uint8_t mdix; + uint8_t forced_speed_duplex; + uint8_t wait_autoneg_complete; + uint8_t dma_fairness; + uint8_t mac_addr[NODE_ADDRESS_SIZE]; + uint8_t perm_mac_addr[NODE_ADDRESS_SIZE]; + boolean_t disable_polarity_correction; + boolean_t speed_downgraded; + e1000_smart_speed smart_speed; + e1000_dsp_config dsp_config_state; + boolean_t get_link_status; + boolean_t serdes_link_down; + boolean_t tbi_compatibility_en; + boolean_t tbi_compatibility_on; + boolean_t laa_is_present; + boolean_t phy_reset_disable; + boolean_t initialize_hw_bits_disable; + boolean_t fc_send_xon; + boolean_t fc_strict_ieee; + boolean_t report_tx_early; + boolean_t adaptive_ifs; + boolean_t ifs_params_forced; + boolean_t in_ifs_mode; + boolean_t mng_reg_access_disabled; + boolean_t leave_av_bit_off; + boolean_t kmrn_lock_loss_workaround_disabled; + boolean_t bad_tx_carr_stats_fd; + boolean_t has_manc2h; + boolean_t rx_needs_kicking; + boolean_t has_smbus; +}; + + +#define E1000_EEPROM_SWDPIN0 0x0001 /* SWDPIN 0 EEPROM Value */ +#define E1000_EEPROM_LED_LOGIC 0x0020 /* Led Logic Word */ +#define E1000_EEPROM_RW_REG_DATA 16 /* Offset to data in EEPROM read/write registers */ +#define E1000_EEPROM_RW_REG_DONE 2 /* Offset to READ/WRITE done bit */ +#define E1000_EEPROM_RW_REG_START 1 /* First bit for telling part to start operation */ +#define E1000_EEPROM_RW_ADDR_SHIFT 2 /* Shift to the address bits */ +#define E1000_EEPROM_POLL_WRITE 1 /* Flag for polling for write complete */ +#define E1000_EEPROM_POLL_READ 0 /* Flag for polling for read complete */ +/* Register Bit Masks */ +/* Device Control */ +#define E1000_CTRL_FD 0x00000001 /* Full duplex.0=half; 1=full */ +#define E1000_CTRL_BEM 0x00000002 /* Endian Mode.0=little,1=big */ +#define E1000_CTRL_PRIOR 0x00000004 /* Priority on PCI. 0=rx,1=fair */ +#define E1000_CTRL_GIO_MASTER_DISABLE 0x00000004 /*Blocks new Master requests */ +#define E1000_CTRL_LRST 0x00000008 /* Link reset. 0=normal,1=reset */ +#define E1000_CTRL_TME 0x00000010 /* Test mode. 0=normal,1=test */ +#define E1000_CTRL_SLE 0x00000020 /* Serial Link on 0=dis,1=en */ +#define E1000_CTRL_ASDE 0x00000020 /* Auto-speed detect enable */ +#define E1000_CTRL_SLU 0x00000040 /* Set link up (Force Link) */ +#define E1000_CTRL_ILOS 0x00000080 /* Invert Loss-Of Signal */ +#define E1000_CTRL_SPD_SEL 0x00000300 /* Speed Select Mask */ +#define E1000_CTRL_SPD_10 0x00000000 /* Force 10Mb */ +#define E1000_CTRL_SPD_100 0x00000100 /* Force 100Mb */ +#define E1000_CTRL_SPD_1000 0x00000200 /* Force 1Gb */ +#define E1000_CTRL_BEM32 0x00000400 /* Big Endian 32 mode */ +#define E1000_CTRL_FRCSPD 0x00000800 /* Force Speed */ +#define E1000_CTRL_FRCDPX 0x00001000 /* Force Duplex */ +#define E1000_CTRL_D_UD_EN 0x00002000 /* Dock/Undock enable */ +#define E1000_CTRL_D_UD_POLARITY 0x00004000 /* Defined polarity of Dock/Undock indication in SDP[0] */ +#define E1000_CTRL_FORCE_PHY_RESET 0x00008000 /* Reset both PHY ports, through PHYRST_N pin */ +#define E1000_CTRL_EXT_LINK_EN 0x00010000 /* enable link status from external LINK_0 and LINK_1 pins */ +#define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */ +#define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */ +#define E1000_CTRL_SWDPIN2 0x00100000 /* SWDPIN 2 value */ +#define E1000_CTRL_SWDPIN3 0x00200000 /* SWDPIN 3 value */ +#define E1000_CTRL_SWDPIO0 0x00400000 /* SWDPIN 0 Input or output */ +#define E1000_CTRL_SWDPIO1 0x00800000 /* SWDPIN 1 input or output */ +#define E1000_CTRL_SWDPIO2 0x01000000 /* SWDPIN 2 input or output */ +#define E1000_CTRL_SWDPIO3 0x02000000 /* SWDPIN 3 input or output */ +#define E1000_CTRL_RST 0x04000000 /* Global reset */ +#define E1000_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */ +#define E1000_CTRL_TFCE 0x10000000 /* Transmit flow control enable */ +#define E1000_CTRL_RTE 0x20000000 /* Routing tag enable */ +#define E1000_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */ +#define E1000_CTRL_PHY_RST 0x80000000 /* PHY Reset */ +#define E1000_CTRL_SW2FW_INT 0x02000000 /* Initiate an interrupt to manageability engine */ + +/* Device Status */ +#define E1000_STATUS_FD 0x00000001 /* Full duplex.0=half,1=full */ +#define E1000_STATUS_LU 0x00000002 /* Link up.0=no,1=link */ +#define E1000_STATUS_FUNC_MASK 0x0000000C /* PCI Function Mask */ +#define E1000_STATUS_FUNC_SHIFT 2 +#define E1000_STATUS_FUNC_0 0x00000000 /* Function 0 */ +#define E1000_STATUS_FUNC_1 0x00000004 /* Function 1 */ +#define E1000_STATUS_TXOFF 0x00000010 /* transmission paused */ +#define E1000_STATUS_TBIMODE 0x00000020 /* TBI mode */ +#define E1000_STATUS_SPEED_MASK 0x000000C0 +#define E1000_STATUS_SPEED_10 0x00000000 /* Speed 10Mb/s */ +#define E1000_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */ +#define E1000_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */ +#define E1000_STATUS_LAN_INIT_DONE 0x00000200 /* Lan Init Completion + by EEPROM/Flash */ +#define E1000_STATUS_ASDV 0x00000300 /* Auto speed detect value */ +#define E1000_STATUS_DOCK_CI 0x00000800 /* Change in Dock/Undock state. Clear on write '0'. */ +#define E1000_STATUS_GIO_MASTER_ENABLE 0x00080000 /* Status of Master requests. */ +#define E1000_STATUS_MTXCKOK 0x00000400 /* MTX clock running OK */ +#define E1000_STATUS_PCI66 0x00000800 /* In 66Mhz slot */ +#define E1000_STATUS_BUS64 0x00001000 /* In 64 bit slot */ +#define E1000_STATUS_PCIX_MODE 0x00002000 /* PCI-X mode */ +#define E1000_STATUS_PCIX_SPEED 0x0000C000 /* PCI-X bus speed */ +#define E1000_STATUS_BMC_SKU_0 0x00100000 /* BMC USB redirect disabled */ +#define E1000_STATUS_BMC_SKU_1 0x00200000 /* BMC SRAM disabled */ +#define E1000_STATUS_BMC_SKU_2 0x00400000 /* BMC SDRAM disabled */ +#define E1000_STATUS_BMC_CRYPTO 0x00800000 /* BMC crypto disabled */ +#define E1000_STATUS_BMC_LITE 0x01000000 /* BMC external code execution disabled */ +#define E1000_STATUS_RGMII_ENABLE 0x02000000 /* RGMII disabled */ +#define E1000_STATUS_FUSE_8 0x04000000 +#define E1000_STATUS_FUSE_9 0x08000000 +#define E1000_STATUS_SERDES0_DIS 0x10000000 /* SERDES disabled on port 0 */ +#define E1000_STATUS_SERDES1_DIS 0x20000000 /* SERDES disabled on port 1 */ + +/* Constants used to intrepret the masked PCI-X bus speed. */ +#define E1000_STATUS_PCIX_SPEED_66 0x00000000 /* PCI-X bus speed 50-66 MHz */ +#define E1000_STATUS_PCIX_SPEED_100 0x00004000 /* PCI-X bus speed 66-100 MHz */ +#define E1000_STATUS_PCIX_SPEED_133 0x00008000 /* PCI-X bus speed 100-133 MHz */ + +/* EEPROM/Flash Control */ +#define E1000_EECD_SK 0x00000001 /* EEPROM Clock */ +#define E1000_EECD_CS 0x00000002 /* EEPROM Chip Select */ +#define E1000_EECD_DI 0x00000004 /* EEPROM Data In */ +#define E1000_EECD_DO 0x00000008 /* EEPROM Data Out */ +#define E1000_EECD_FWE_MASK 0x00000030 +#define E1000_EECD_FWE_DIS 0x00000010 /* Disable FLASH writes */ +#define E1000_EECD_FWE_EN 0x00000020 /* Enable FLASH writes */ +#define E1000_EECD_FWE_SHIFT 4 +#define E1000_EECD_REQ 0x00000040 /* EEPROM Access Request */ +#define E1000_EECD_GNT 0x00000080 /* EEPROM Access Grant */ +#define E1000_EECD_PRES 0x00000100 /* EEPROM Present */ +#define E1000_EECD_SIZE 0x00000200 /* EEPROM Size (0=64 word 1=256 word) */ +#define E1000_EECD_ADDR_BITS 0x00000400 /* EEPROM Addressing bits based on type + * (0-small, 1-large) */ +#define E1000_EECD_TYPE 0x00002000 /* EEPROM Type (1-SPI, 0-Microwire) */ +#ifndef E1000_EEPROM_GRANT_ATTEMPTS +#define E1000_EEPROM_GRANT_ATTEMPTS 1000 /* EEPROM # attempts to gain grant */ +#endif +#define E1000_EECD_AUTO_RD 0x00000200 /* EEPROM Auto Read done */ +#define E1000_EECD_SIZE_EX_MASK 0x00007800 /* EEprom Size */ +#define E1000_EECD_SIZE_EX_SHIFT 11 +#define E1000_EECD_NVADDS 0x00018000 /* NVM Address Size */ +#define E1000_EECD_SELSHAD 0x00020000 /* Select Shadow RAM */ +#define E1000_EECD_INITSRAM 0x00040000 /* Initialize Shadow RAM */ +#define E1000_EECD_FLUPD 0x00080000 /* Update FLASH */ +#define E1000_EECD_AUPDEN 0x00100000 /* Enable Autonomous FLASH update */ +#define E1000_EECD_SHADV 0x00200000 /* Shadow RAM Data Valid */ +#define E1000_EECD_SEC1VAL 0x00400000 /* Sector One Valid */ +#define E1000_EECD_SECVAL_SHIFT 22 +#define E1000_STM_OPCODE 0xDB00 +#define E1000_HICR_FW_RESET 0xC0 + +#define E1000_SHADOW_RAM_WORDS 2048 +#define E1000_ICH_NVM_SIG_WORD 0x13 +#define E1000_ICH_NVM_SIG_MASK 0xC0 + +/* EEPROM Read */ +#define E1000_EERD_START 0x00000001 /* Start Read */ +#define E1000_EERD_DONE 0x00000010 /* Read Done */ +#define E1000_EERD_ADDR_SHIFT 8 +#define E1000_EERD_ADDR_MASK 0x0000FF00 /* Read Address */ +#define E1000_EERD_DATA_SHIFT 16 +#define E1000_EERD_DATA_MASK 0xFFFF0000 /* Read Data */ + +/* SPI EEPROM Status Register */ +#define EEPROM_STATUS_RDY_SPI 0x01 +#define EEPROM_STATUS_WEN_SPI 0x02 +#define EEPROM_STATUS_BP0_SPI 0x04 +#define EEPROM_STATUS_BP1_SPI 0x08 +#define EEPROM_STATUS_WPEN_SPI 0x80 + +/* Extended Device Control */ +#define E1000_CTRL_EXT_GPI0_EN 0x00000001 /* Maps SDP4 to GPI0 */ +#define E1000_CTRL_EXT_GPI1_EN 0x00000002 /* Maps SDP5 to GPI1 */ +#define E1000_CTRL_EXT_PHYINT_EN E1000_CTRL_EXT_GPI1_EN +#define E1000_CTRL_EXT_GPI2_EN 0x00000004 /* Maps SDP6 to GPI2 */ +#define E1000_CTRL_EXT_GPI3_EN 0x00000008 /* Maps SDP7 to GPI3 */ +#define E1000_CTRL_EXT_SDP4_DATA 0x00000010 /* Value of SW Defineable Pin 4 */ +#define E1000_CTRL_EXT_SDP5_DATA 0x00000020 /* Value of SW Defineable Pin 5 */ +#define E1000_CTRL_EXT_PHY_INT E1000_CTRL_EXT_SDP5_DATA +#define E1000_CTRL_EXT_SDP6_DATA 0x00000040 /* Value of SW Defineable Pin 6 */ +#define E1000_CTRL_EXT_SDP7_DATA 0x00000080 /* Value of SW Defineable Pin 7 */ +#define E1000_CTRL_EXT_SDP4_DIR 0x00000100 /* Direction of SDP4 0=in 1=out */ +#define E1000_CTRL_EXT_SDP5_DIR 0x00000200 /* Direction of SDP5 0=in 1=out */ +#define E1000_CTRL_EXT_SDP6_DIR 0x00000400 /* Direction of SDP6 0=in 1=out */ +#define E1000_CTRL_EXT_SDP7_DIR 0x00000800 /* Direction of SDP7 0=in 1=out */ +#define E1000_CTRL_EXT_ASDCHK 0x00001000 /* Initiate an ASD sequence */ +#define E1000_CTRL_EXT_EE_RST 0x00002000 /* Reinitialize from EEPROM */ +#define E1000_CTRL_EXT_IPS 0x00004000 /* Invert Power State */ +#define E1000_CTRL_EXT_SPD_BYPS 0x00008000 /* Speed Select Bypass */ +#define E1000_CTRL_EXT_RO_DIS 0x00020000 /* Relaxed Ordering disable */ +#define E1000_CTRL_EXT_LINK_MODE_MASK 0x00C00000 +#define E1000_CTRL_EXT_LINK_MODE_GMII 0x00000000 +#define E1000_CTRL_EXT_LINK_MODE_TBI 0x00C00000 +#define E1000_CTRL_EXT_LINK_MODE_KMRN 0x00000000 +#define E1000_CTRL_EXT_LINK_MODE_SERDES 0x00C00000 +#define E1000_CTRL_EXT_LINK_MODE_SGMII 0x00800000 +#define E1000_CTRL_EXT_WR_WMARK_MASK 0x03000000 +#define E1000_CTRL_EXT_WR_WMARK_256 0x00000000 +#define E1000_CTRL_EXT_WR_WMARK_320 0x01000000 +#define E1000_CTRL_EXT_WR_WMARK_384 0x02000000 +#define E1000_CTRL_EXT_WR_WMARK_448 0x03000000 +#define E1000_CTRL_EXT_DRV_LOAD 0x10000000 /* Driver loaded bit for FW */ +#define E1000_CTRL_EXT_IAME 0x08000000 /* Interrupt acknowledge Auto-mask */ +#define E1000_CTRL_EXT_INT_TIMER_CLR 0x20000000 /* Clear Interrupt timers after IMS clear */ +#define E1000_CRTL_EXT_PB_PAREN 0x01000000 /* packet buffer parity error detection enabled */ +#define E1000_CTRL_EXT_DF_PAREN 0x02000000 /* descriptor FIFO parity error detection enable */ +#define E1000_CTRL_EXT_GHOST_PAREN 0x40000000 + +/* MDI Control */ +#define E1000_MDIC_DATA_MASK 0x0000FFFF +#define E1000_MDIC_REG_MASK 0x001F0000 +#define E1000_MDIC_REG_SHIFT 16 +#define E1000_MDIC_PHY_MASK 0x03E00000 +#define E1000_MDIC_PHY_SHIFT 21 +#define E1000_MDIC_OP_WRITE 0x04000000 +#define E1000_MDIC_OP_READ 0x08000000 +#define E1000_MDIC_READY 0x10000000 +#define E1000_MDIC_INT_EN 0x20000000 +#define E1000_MDIC_ERROR 0x40000000 + +#define E1000_KUMCTRLSTA_MASK 0x0000FFFF +#define E1000_KUMCTRLSTA_OFFSET 0x001F0000 +#define E1000_KUMCTRLSTA_OFFSET_SHIFT 16 +#define E1000_KUMCTRLSTA_REN 0x00200000 + +#define E1000_KUMCTRLSTA_OFFSET_FIFO_CTRL 0x00000000 +#define E1000_KUMCTRLSTA_OFFSET_CTRL 0x00000001 +#define E1000_KUMCTRLSTA_OFFSET_INB_CTRL 0x00000002 +#define E1000_KUMCTRLSTA_OFFSET_DIAG 0x00000003 +#define E1000_KUMCTRLSTA_OFFSET_TIMEOUTS 0x00000004 +#define E1000_KUMCTRLSTA_OFFSET_INB_PARAM 0x00000009 +#define E1000_KUMCTRLSTA_OFFSET_HD_CTRL 0x00000010 +#define E1000_KUMCTRLSTA_OFFSET_M2P_SERDES 0x0000001E +#define E1000_KUMCTRLSTA_OFFSET_M2P_MODES 0x0000001F + +/* FIFO Control */ +#define E1000_KUMCTRLSTA_FIFO_CTRL_RX_BYPASS 0x00000008 +#define E1000_KUMCTRLSTA_FIFO_CTRL_TX_BYPASS 0x00000800 + +/* In-Band Control */ +#define E1000_KUMCTRLSTA_INB_CTRL_LINK_STATUS_TX_TIMEOUT_DEFAULT 0x00000500 +#define E1000_KUMCTRLSTA_INB_CTRL_DIS_PADDING 0x00000010 + +/* Half-Duplex Control */ +#define E1000_KUMCTRLSTA_HD_CTRL_10_100_DEFAULT 0x00000004 +#define E1000_KUMCTRLSTA_HD_CTRL_1000_DEFAULT 0x00000000 + +#define E1000_KUMCTRLSTA_OFFSET_K0S_CTRL 0x0000001E + +#define E1000_KUMCTRLSTA_DIAG_FELPBK 0x2000 +#define E1000_KUMCTRLSTA_DIAG_NELPBK 0x1000 + +#define E1000_KUMCTRLSTA_K0S_100_EN 0x2000 +#define E1000_KUMCTRLSTA_K0S_GBE_EN 0x1000 +#define E1000_KUMCTRLSTA_K0S_ENTRY_LATENCY_MASK 0x0003 + +#define E1000_KABGTXD_BGSQLBIAS 0x00050000 + +#define E1000_PHY_CTRL_SPD_EN 0x00000001 +#define E1000_PHY_CTRL_D0A_LPLU 0x00000002 +#define E1000_PHY_CTRL_NOND0A_LPLU 0x00000004 +#define E1000_PHY_CTRL_NOND0A_GBE_DISABLE 0x00000008 +#define E1000_PHY_CTRL_GBE_DISABLE 0x00000040 +#define E1000_PHY_CTRL_B2B_EN 0x00000080 + +/* LED Control */ +#define E1000_LEDCTL_LED0_MODE_MASK 0x0000000F +#define E1000_LEDCTL_LED0_MODE_SHIFT 0 +#define E1000_LEDCTL_LED0_BLINK_RATE 0x0000020 +#define E1000_LEDCTL_LED0_IVRT 0x00000040 +#define E1000_LEDCTL_LED0_BLINK 0x00000080 +#define E1000_LEDCTL_LED1_MODE_MASK 0x00000F00 +#define E1000_LEDCTL_LED1_MODE_SHIFT 8 +#define E1000_LEDCTL_LED1_BLINK_RATE 0x0002000 +#define E1000_LEDCTL_LED1_IVRT 0x00004000 +#define E1000_LEDCTL_LED1_BLINK 0x00008000 +#define E1000_LEDCTL_LED2_MODE_MASK 0x000F0000 +#define E1000_LEDCTL_LED2_MODE_SHIFT 16 +#define E1000_LEDCTL_LED2_BLINK_RATE 0x00200000 +#define E1000_LEDCTL_LED2_IVRT 0x00400000 +#define E1000_LEDCTL_LED2_BLINK 0x00800000 +#define E1000_LEDCTL_LED3_MODE_MASK 0x0F000000 +#define E1000_LEDCTL_LED3_MODE_SHIFT 24 +#define E1000_LEDCTL_LED3_BLINK_RATE 0x20000000 +#define E1000_LEDCTL_LED3_IVRT 0x40000000 +#define E1000_LEDCTL_LED3_BLINK 0x80000000 + +#define E1000_LEDCTL_MODE_LINK_10_1000 0x0 +#define E1000_LEDCTL_MODE_LINK_100_1000 0x1 +#define E1000_LEDCTL_MODE_LINK_UP 0x2 +#define E1000_LEDCTL_MODE_ACTIVITY 0x3 +#define E1000_LEDCTL_MODE_LINK_ACTIVITY 0x4 +#define E1000_LEDCTL_MODE_LINK_10 0x5 +#define E1000_LEDCTL_MODE_LINK_100 0x6 +#define E1000_LEDCTL_MODE_LINK_1000 0x7 +#define E1000_LEDCTL_MODE_PCIX_MODE 0x8 +#define E1000_LEDCTL_MODE_FULL_DUPLEX 0x9 +#define E1000_LEDCTL_MODE_COLLISION 0xA +#define E1000_LEDCTL_MODE_BUS_SPEED 0xB +#define E1000_LEDCTL_MODE_BUS_SIZE 0xC +#define E1000_LEDCTL_MODE_PAUSED 0xD +#define E1000_LEDCTL_MODE_LED_ON 0xE +#define E1000_LEDCTL_MODE_LED_OFF 0xF + +/* Receive Address */ +#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */ + +/* Interrupt Cause Read */ +#define E1000_ICR_TXDW 0x00000001 /* Transmit desc written back */ +#define E1000_ICR_TXQE 0x00000002 /* Transmit Queue empty */ +#define E1000_ICR_LSC 0x00000004 /* Link Status Change */ +#define E1000_ICR_RXSEQ 0x00000008 /* rx sequence error */ +#define E1000_ICR_RXDMT0 0x00000010 /* rx desc min. threshold (0) */ +#define E1000_ICR_RXO 0x00000040 /* rx overrun */ +#define E1000_ICR_RXT0 0x00000080 /* rx timer intr (ring 0) */ +#define E1000_ICR_MDAC 0x00000200 /* MDIO access complete */ +#define E1000_ICR_RXCFG 0x00000400 /* RX /c/ ordered set */ +#define E1000_ICR_GPI_EN0 0x00000800 /* GP Int 0 */ +#define E1000_ICR_GPI_EN1 0x00001000 /* GP Int 1 */ +#define E1000_ICR_GPI_EN2 0x00002000 /* GP Int 2 */ +#define E1000_ICR_GPI_EN3 0x00004000 /* GP Int 3 */ +#define E1000_ICR_TXD_LOW 0x00008000 +#define E1000_ICR_SRPD 0x00010000 +#define E1000_ICR_ACK 0x00020000 /* Receive Ack frame */ +#define E1000_ICR_MNG 0x00040000 /* Manageability event */ +#define E1000_ICR_DOCK 0x00080000 /* Dock/Undock */ +#define E1000_ICR_INT_ASSERTED 0x80000000 /* If this bit asserted, the driver should claim the interrupt */ +#define E1000_ICR_RXD_FIFO_PAR0 0x00100000 /* queue 0 Rx descriptor FIFO parity error */ +#define E1000_ICR_TXD_FIFO_PAR0 0x00200000 /* queue 0 Tx descriptor FIFO parity error */ +#define E1000_ICR_HOST_ARB_PAR 0x00400000 /* host arb read buffer parity error */ +#define E1000_ICR_PB_PAR 0x00800000 /* packet buffer parity error */ +#define E1000_ICR_RXD_FIFO_PAR1 0x01000000 /* queue 1 Rx descriptor FIFO parity error */ +#define E1000_ICR_TXD_FIFO_PAR1 0x02000000 /* queue 1 Tx descriptor FIFO parity error */ +#define E1000_ICR_ALL_PARITY 0x03F00000 /* all parity error bits */ +#define E1000_ICR_DSW 0x00000020 /* FW changed the status of DISSW bit in the FWSM */ +#define E1000_ICR_PHYINT 0x00001000 /* LAN connected device generates an interrupt */ +#define E1000_ICR_EPRST 0x00100000 /* ME handware reset occurs */ + +/* Interrupt Cause Set */ +#define E1000_ICS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_ICS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_ICS_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_ICS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_ICS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_ICS_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_ICS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_ICS_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_ICS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_ICS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_ICS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_ICS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_ICS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_ICS_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_ICS_SRPD E1000_ICR_SRPD +#define E1000_ICS_ACK E1000_ICR_ACK /* Receive Ack frame */ +#define E1000_ICS_MNG E1000_ICR_MNG /* Manageability event */ +#define E1000_ICS_DOCK E1000_ICR_DOCK /* Dock/Undock */ +#define E1000_ICS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ +#define E1000_ICS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ +#define E1000_ICS_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ +#define E1000_ICS_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ +#define E1000_ICS_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ +#define E1000_ICS_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ +#define E1000_ICS_DSW E1000_ICR_DSW +#define E1000_ICS_PHYINT E1000_ICR_PHYINT +#define E1000_ICS_EPRST E1000_ICR_EPRST + +/* Interrupt Mask Set */ +#define E1000_IMS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_IMS_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_IMS_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_IMS_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_IMS_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_IMS_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_IMS_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_IMS_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_IMS_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_IMS_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_IMS_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_IMS_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_IMS_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_IMS_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_IMS_SRPD E1000_ICR_SRPD +#define E1000_IMS_ACK E1000_ICR_ACK /* Receive Ack frame */ +#define E1000_IMS_MNG E1000_ICR_MNG /* Manageability event */ +#define E1000_IMS_DOCK E1000_ICR_DOCK /* Dock/Undock */ +#define E1000_IMS_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ +#define E1000_IMS_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ +#define E1000_IMS_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ +#define E1000_IMS_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ +#define E1000_IMS_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ +#define E1000_IMS_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ +#define E1000_IMS_DSW E1000_ICR_DSW +#define E1000_IMS_PHYINT E1000_ICR_PHYINT +#define E1000_IMS_EPRST E1000_ICR_EPRST + +/* Interrupt Mask Clear */ +#define E1000_IMC_TXDW E1000_ICR_TXDW /* Transmit desc written back */ +#define E1000_IMC_TXQE E1000_ICR_TXQE /* Transmit Queue empty */ +#define E1000_IMC_LSC E1000_ICR_LSC /* Link Status Change */ +#define E1000_IMC_RXSEQ E1000_ICR_RXSEQ /* rx sequence error */ +#define E1000_IMC_RXDMT0 E1000_ICR_RXDMT0 /* rx desc min. threshold */ +#define E1000_IMC_RXO E1000_ICR_RXO /* rx overrun */ +#define E1000_IMC_RXT0 E1000_ICR_RXT0 /* rx timer intr */ +#define E1000_IMC_MDAC E1000_ICR_MDAC /* MDIO access complete */ +#define E1000_IMC_RXCFG E1000_ICR_RXCFG /* RX /c/ ordered set */ +#define E1000_IMC_GPI_EN0 E1000_ICR_GPI_EN0 /* GP Int 0 */ +#define E1000_IMC_GPI_EN1 E1000_ICR_GPI_EN1 /* GP Int 1 */ +#define E1000_IMC_GPI_EN2 E1000_ICR_GPI_EN2 /* GP Int 2 */ +#define E1000_IMC_GPI_EN3 E1000_ICR_GPI_EN3 /* GP Int 3 */ +#define E1000_IMC_TXD_LOW E1000_ICR_TXD_LOW +#define E1000_IMC_SRPD E1000_ICR_SRPD +#define E1000_IMC_ACK E1000_ICR_ACK /* Receive Ack frame */ +#define E1000_IMC_MNG E1000_ICR_MNG /* Manageability event */ +#define E1000_IMC_DOCK E1000_ICR_DOCK /* Dock/Undock */ +#define E1000_IMC_RXD_FIFO_PAR0 E1000_ICR_RXD_FIFO_PAR0 /* queue 0 Rx descriptor FIFO parity error */ +#define E1000_IMC_TXD_FIFO_PAR0 E1000_ICR_TXD_FIFO_PAR0 /* queue 0 Tx descriptor FIFO parity error */ +#define E1000_IMC_HOST_ARB_PAR E1000_ICR_HOST_ARB_PAR /* host arb read buffer parity error */ +#define E1000_IMC_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ +#define E1000_IMC_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ +#define E1000_IMC_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ +#define E1000_IMC_DSW E1000_ICR_DSW +#define E1000_IMC_PHYINT E1000_ICR_PHYINT +#define E1000_IMC_EPRST E1000_ICR_EPRST + +/* Receive Control */ +#define E1000_RCTL_RST 0x00000001 /* Software reset */ +#define E1000_RCTL_EN 0x00000002 /* enable */ +#define E1000_RCTL_SBP 0x00000004 /* store bad packet */ +#define E1000_RCTL_UPE 0x00000008 /* unicast promiscuous enable */ +#define E1000_RCTL_MPE 0x00000010 /* multicast promiscuous enab */ +#define E1000_RCTL_LPE 0x00000020 /* long packet enable */ +#define E1000_RCTL_LBM_NO 0x00000000 /* no loopback mode */ +#define E1000_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */ +#define E1000_RCTL_LBM_SLP 0x00000080 /* serial link loopback mode */ +#define E1000_RCTL_LBM_TCVR 0x000000C0 /* tcvr loopback mode */ +#define E1000_RCTL_DTYP_MASK 0x00000C00 /* Descriptor type mask */ +#define E1000_RCTL_DTYP_PS 0x00000400 /* Packet Split descriptor */ +#define E1000_RCTL_RDMTS_HALF 0x00000000 /* rx desc min threshold size */ +#define E1000_RCTL_RDMTS_QUAT 0x00000100 /* rx desc min threshold size */ +#define E1000_RCTL_RDMTS_EIGTH 0x00000200 /* rx desc min threshold size */ +#define E1000_RCTL_MO_SHIFT 12 /* multicast offset shift */ +#define E1000_RCTL_MO_0 0x00000000 /* multicast offset 11:0 */ +#define E1000_RCTL_MO_1 0x00001000 /* multicast offset 12:1 */ +#define E1000_RCTL_MO_2 0x00002000 /* multicast offset 13:2 */ +#define E1000_RCTL_MO_3 0x00003000 /* multicast offset 15:4 */ +#define E1000_RCTL_MDR 0x00004000 /* multicast desc ring 0 */ +#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */ +/* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */ +#define E1000_RCTL_SZ_2048 0x00000000 /* rx buffer size 2048 */ +#define E1000_RCTL_SZ_1024 0x00010000 /* rx buffer size 1024 */ +#define E1000_RCTL_SZ_512 0x00020000 /* rx buffer size 512 */ +#define E1000_RCTL_SZ_256 0x00030000 /* rx buffer size 256 */ +/* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */ +#define E1000_RCTL_SZ_16384 0x00010000 /* rx buffer size 16384 */ +#define E1000_RCTL_SZ_8192 0x00020000 /* rx buffer size 8192 */ +#define E1000_RCTL_SZ_4096 0x00030000 /* rx buffer size 4096 */ +#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */ +#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */ +#define E1000_RCTL_CFI 0x00100000 /* canonical form indicator */ +#define E1000_RCTL_DPF 0x00400000 /* discard pause frames */ +#define E1000_RCTL_PMCF 0x00800000 /* pass MAC control frames */ +#define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */ +#define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */ +#define E1000_RCTL_FLXBUF_MASK 0x78000000 /* Flexible buffer size */ +#define E1000_RCTL_FLXBUF_SHIFT 27 /* Flexible buffer shift */ + +/* Use byte values for the following shift parameters + * Usage: + * psrctl |= (((ROUNDUP(value0, 128) >> E1000_PSRCTL_BSIZE0_SHIFT) & + * E1000_PSRCTL_BSIZE0_MASK) | + * ((ROUNDUP(value1, 1024) >> E1000_PSRCTL_BSIZE1_SHIFT) & + * E1000_PSRCTL_BSIZE1_MASK) | + * ((ROUNDUP(value2, 1024) << E1000_PSRCTL_BSIZE2_SHIFT) & + * E1000_PSRCTL_BSIZE2_MASK) | + * ((ROUNDUP(value3, 1024) << E1000_PSRCTL_BSIZE3_SHIFT) |; + * E1000_PSRCTL_BSIZE3_MASK)) + * where value0 = [128..16256], default=256 + * value1 = [1024..64512], default=4096 + * value2 = [0..64512], default=4096 + * value3 = [0..64512], default=0 + */ + +#define E1000_PSRCTL_BSIZE0_MASK 0x0000007F +#define E1000_PSRCTL_BSIZE1_MASK 0x00003F00 +#define E1000_PSRCTL_BSIZE2_MASK 0x003F0000 +#define E1000_PSRCTL_BSIZE3_MASK 0x3F000000 + +#define E1000_PSRCTL_BSIZE0_SHIFT 7 /* Shift _right_ 7 */ +#define E1000_PSRCTL_BSIZE1_SHIFT 2 /* Shift _right_ 2 */ +#define E1000_PSRCTL_BSIZE2_SHIFT 6 /* Shift _left_ 6 */ +#define E1000_PSRCTL_BSIZE3_SHIFT 14 /* Shift _left_ 14 */ + +/* SW_W_SYNC definitions */ +#define E1000_SWFW_EEP_SM 0x0001 +#define E1000_SWFW_PHY0_SM 0x0002 +#define E1000_SWFW_PHY1_SM 0x0004 +#define E1000_SWFW_MAC_CSR_SM 0x0008 + +/* Receive Descriptor */ +#define E1000_RDT_DELAY 0x0000ffff /* Delay timer (1=1024us) */ +#define E1000_RDT_FPDB 0x80000000 /* Flush descriptor block */ +#define E1000_RDLEN_LEN 0x0007ff80 /* descriptor length */ +#define E1000_RDH_RDH 0x0000ffff /* receive descriptor head */ +#define E1000_RDT_RDT 0x0000ffff /* receive descriptor tail */ + +/* Flow Control */ +#define E1000_FCRTH_RTH 0x0000FFF8 /* Mask Bits[15:3] for RTH */ +#define E1000_FCRTH_XFCE 0x80000000 /* External Flow Control Enable */ +#define E1000_FCRTL_RTL 0x0000FFF8 /* Mask Bits[15:3] for RTL */ +#define E1000_FCRTL_XONE 0x80000000 /* Enable XON frame transmission */ + +/* Header split receive */ +#define E1000_RFCTL_ISCSI_DIS 0x00000001 +#define E1000_RFCTL_ISCSI_DWC_MASK 0x0000003E +#define E1000_RFCTL_ISCSI_DWC_SHIFT 1 +#define E1000_RFCTL_NFSW_DIS 0x00000040 +#define E1000_RFCTL_NFSR_DIS 0x00000080 +#define E1000_RFCTL_NFS_VER_MASK 0x00000300 +#define E1000_RFCTL_NFS_VER_SHIFT 8 +#define E1000_RFCTL_IPV6_DIS 0x00000400 +#define E1000_RFCTL_IPV6_XSUM_DIS 0x00000800 +#define E1000_RFCTL_ACK_DIS 0x00001000 +#define E1000_RFCTL_ACKD_DIS 0x00002000 +#define E1000_RFCTL_IPFRSP_DIS 0x00004000 +#define E1000_RFCTL_EXTEN 0x00008000 +#define E1000_RFCTL_IPV6_EX_DIS 0x00010000 +#define E1000_RFCTL_NEW_IPV6_EXT_DIS 0x00020000 + +/* Receive Descriptor Control */ +#define E1000_RXDCTL_PTHRESH 0x0000003F /* RXDCTL Prefetch Threshold */ +#define E1000_RXDCTL_HTHRESH 0x00003F00 /* RXDCTL Host Threshold */ +#define E1000_RXDCTL_WTHRESH 0x003F0000 /* RXDCTL Writeback Threshold */ +#define E1000_RXDCTL_GRAN 0x01000000 /* RXDCTL Granularity */ + +/* Transmit Descriptor Control */ +#define E1000_TXDCTL_PTHRESH 0x0000003F /* TXDCTL Prefetch Threshold */ +#define E1000_TXDCTL_HTHRESH 0x00003F00 /* TXDCTL Host Threshold */ +#define E1000_TXDCTL_WTHRESH 0x003F0000 /* TXDCTL Writeback Threshold */ +#define E1000_TXDCTL_GRAN 0x01000000 /* TXDCTL Granularity */ +#define E1000_TXDCTL_LWTHRESH 0xFE000000 /* TXDCTL Low Threshold */ +#define E1000_TXDCTL_FULL_TX_DESC_WB 0x01010000 /* GRAN=1, WTHRESH=1 */ +#define E1000_TXDCTL_COUNT_DESC 0x00400000 /* Enable the counting of desc. + still to be processed. */ +/* Transmit Configuration Word */ +#define E1000_TXCW_FD 0x00000020 /* TXCW full duplex */ +#define E1000_TXCW_HD 0x00000040 /* TXCW half duplex */ +#define E1000_TXCW_PAUSE 0x00000080 /* TXCW sym pause request */ +#define E1000_TXCW_ASM_DIR 0x00000100 /* TXCW astm pause direction */ +#define E1000_TXCW_PAUSE_MASK 0x00000180 /* TXCW pause request mask */ +#define E1000_TXCW_RF 0x00003000 /* TXCW remote fault */ +#define E1000_TXCW_NP 0x00008000 /* TXCW next page */ +#define E1000_TXCW_CW 0x0000ffff /* TxConfigWord mask */ +#define E1000_TXCW_TXC 0x40000000 /* Transmit Config control */ +#define E1000_TXCW_ANE 0x80000000 /* Auto-neg enable */ + +/* Receive Configuration Word */ +#define E1000_RXCW_CW 0x0000ffff /* RxConfigWord mask */ +#define E1000_RXCW_NC 0x04000000 /* Receive config no carrier */ +#define E1000_RXCW_IV 0x08000000 /* Receive config invalid */ +#define E1000_RXCW_CC 0x10000000 /* Receive config change */ +#define E1000_RXCW_C 0x20000000 /* Receive config */ +#define E1000_RXCW_SYNCH 0x40000000 /* Receive config synch */ +#define E1000_RXCW_ANC 0x80000000 /* Auto-neg complete */ + +/* Transmit Control */ +#define E1000_TCTL_RST 0x00000001 /* software reset */ +#define E1000_TCTL_EN 0x00000002 /* enable tx */ +#define E1000_TCTL_BCE 0x00000004 /* busy check enable */ +#define E1000_TCTL_PSP 0x00000008 /* pad short packets */ +#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */ +#define E1000_TCTL_COLD 0x003ff000 /* collision distance */ +#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */ +#define E1000_TCTL_PBE 0x00800000 /* Packet Burst Enable */ +#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */ +#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */ +#define E1000_TCTL_MULR 0x10000000 /* Multiple request support */ +/* Extended Transmit Control */ +#define E1000_TCTL_EXT_BST_MASK 0x000003FF /* Backoff Slot Time */ +#define E1000_TCTL_EXT_GCEX_MASK 0x000FFC00 /* Gigabit Carry Extend Padding */ + +#define DEFAULT_80003ES2LAN_TCTL_EXT_GCEX 0x00010000 + +/* Receive Checksum Control */ +#define E1000_RXCSUM_PCSS_MASK 0x000000FF /* Packet Checksum Start */ +#define E1000_RXCSUM_IPOFL 0x00000100 /* IPv4 checksum offload */ +#define E1000_RXCSUM_TUOFL 0x00000200 /* TCP / UDP checksum offload */ +#define E1000_RXCSUM_IPV6OFL 0x00000400 /* IPv6 checksum offload */ +#define E1000_RXCSUM_IPPCSE 0x00001000 /* IP payload checksum enable */ +#define E1000_RXCSUM_PCSD 0x00002000 /* packet checksum disabled */ + +/* Multiple Receive Queue Control */ +#define E1000_MRQC_ENABLE_MASK 0x00000003 +#define E1000_MRQC_ENABLE_RSS_2Q 0x00000001 +#define E1000_MRQC_ENABLE_RSS_INT 0x00000004 +#define E1000_MRQC_RSS_FIELD_MASK 0xFFFF0000 +#define E1000_MRQC_RSS_FIELD_IPV4_TCP 0x00010000 +#define E1000_MRQC_RSS_FIELD_IPV4 0x00020000 +#define E1000_MRQC_RSS_FIELD_IPV6_TCP_EX 0x00040000 +#define E1000_MRQC_RSS_FIELD_IPV6_EX 0x00080000 +#define E1000_MRQC_RSS_FIELD_IPV6 0x00100000 +#define E1000_MRQC_RSS_FIELD_IPV6_TCP 0x00200000 + +/* Definitions for power management and wakeup registers */ +/* Wake Up Control */ +#define E1000_WUC_APME 0x00000001 /* APM Enable */ +#define E1000_WUC_PME_EN 0x00000002 /* PME Enable */ +#define E1000_WUC_PME_STATUS 0x00000004 /* PME Status */ +#define E1000_WUC_APMPME 0x00000008 /* Assert PME on APM Wakeup */ +#define E1000_WUC_SPM 0x80000000 /* Enable SPM */ + +/* Wake Up Filter Control */ +#define E1000_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */ +#define E1000_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */ +#define E1000_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */ +#define E1000_WUFC_MC 0x00000008 /* Directed Multicast Wakeup Enable */ +#define E1000_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */ +#define E1000_WUFC_ARP 0x00000020 /* ARP Request Packet Wakeup Enable */ +#define E1000_WUFC_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Enable */ +#define E1000_WUFC_IPV6 0x00000080 /* Directed IPv6 Packet Wakeup Enable */ +#define E1000_WUFC_IGNORE_TCO 0x00008000 /* Ignore WakeOn TCO packets */ +#define E1000_WUFC_FLX0 0x00010000 /* Flexible Filter 0 Enable */ +#define E1000_WUFC_FLX1 0x00020000 /* Flexible Filter 1 Enable */ +#define E1000_WUFC_FLX2 0x00040000 /* Flexible Filter 2 Enable */ +#define E1000_WUFC_FLX3 0x00080000 /* Flexible Filter 3 Enable */ +#define E1000_WUFC_ALL_FILTERS 0x000F00FF /* Mask for all wakeup filters */ +#define E1000_WUFC_FLX_OFFSET 16 /* Offset to the Flexible Filters bits */ +#define E1000_WUFC_FLX_FILTERS 0x000F0000 /* Mask for the 4 flexible filters */ + +/* Wake Up Status */ +#define E1000_WUS_LNKC 0x00000001 /* Link Status Changed */ +#define E1000_WUS_MAG 0x00000002 /* Magic Packet Received */ +#define E1000_WUS_EX 0x00000004 /* Directed Exact Received */ +#define E1000_WUS_MC 0x00000008 /* Directed Multicast Received */ +#define E1000_WUS_BC 0x00000010 /* Broadcast Received */ +#define E1000_WUS_ARP 0x00000020 /* ARP Request Packet Received */ +#define E1000_WUS_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Received */ +#define E1000_WUS_IPV6 0x00000080 /* Directed IPv6 Packet Wakeup Received */ +#define E1000_WUS_FLX0 0x00010000 /* Flexible Filter 0 Match */ +#define E1000_WUS_FLX1 0x00020000 /* Flexible Filter 1 Match */ +#define E1000_WUS_FLX2 0x00040000 /* Flexible Filter 2 Match */ +#define E1000_WUS_FLX3 0x00080000 /* Flexible Filter 3 Match */ +#define E1000_WUS_FLX_FILTERS 0x000F0000 /* Mask for the 4 flexible filters */ + +/* Management Control */ +#define E1000_MANC_SMBUS_EN 0x00000001 /* SMBus Enabled - RO */ +#define E1000_MANC_ASF_EN 0x00000002 /* ASF Enabled - RO */ +#define E1000_MANC_R_ON_FORCE 0x00000004 /* Reset on Force TCO - RO */ +#define E1000_MANC_RMCP_EN 0x00000100 /* Enable RCMP 026Fh Filtering */ +#define E1000_MANC_0298_EN 0x00000200 /* Enable RCMP 0298h Filtering */ +#define E1000_MANC_IPV4_EN 0x00000400 /* Enable IPv4 */ +#define E1000_MANC_IPV6_EN 0x00000800 /* Enable IPv6 */ +#define E1000_MANC_SNAP_EN 0x00001000 /* Accept LLC/SNAP */ +#define E1000_MANC_ARP_EN 0x00002000 /* Enable ARP Request Filtering */ +#define E1000_MANC_NEIGHBOR_EN 0x00004000 /* Enable Neighbor Discovery + * Filtering */ +#define E1000_MANC_ARP_RES_EN 0x00008000 /* Enable ARP response Filtering */ +#define E1000_MANC_TCO_RESET 0x00010000 /* TCO Reset Occurred */ +#define E1000_MANC_RCV_TCO_EN 0x00020000 /* Receive TCO Packets Enabled */ +#define E1000_MANC_REPORT_STATUS 0x00040000 /* Status Reporting Enabled */ +#define E1000_MANC_RCV_ALL 0x00080000 /* Receive All Enabled */ +#define E1000_MANC_BLK_PHY_RST_ON_IDE 0x00040000 /* Block phy resets */ +#define E1000_MANC_EN_MAC_ADDR_FILTER 0x00100000 /* Enable MAC address + * filtering */ +#define E1000_MANC_EN_MNG2HOST 0x00200000 /* Enable MNG packets to host + * memory */ +#define E1000_MANC_EN_IP_ADDR_FILTER 0x00400000 /* Enable IP address + * filtering */ +#define E1000_MANC_EN_XSUM_FILTER 0x00800000 /* Enable checksum filtering */ +#define E1000_MANC_BR_EN 0x01000000 /* Enable broadcast filtering */ +#define E1000_MANC_SMB_REQ 0x01000000 /* SMBus Request */ +#define E1000_MANC_SMB_GNT 0x02000000 /* SMBus Grant */ +#define E1000_MANC_SMB_CLK_IN 0x04000000 /* SMBus Clock In */ +#define E1000_MANC_SMB_DATA_IN 0x08000000 /* SMBus Data In */ +#define E1000_MANC_SMB_DATA_OUT 0x10000000 /* SMBus Data Out */ +#define E1000_MANC_SMB_CLK_OUT 0x20000000 /* SMBus Clock Out */ + +#define E1000_MANC_SMB_DATA_OUT_SHIFT 28 /* SMBus Data Out Shift */ +#define E1000_MANC_SMB_CLK_OUT_SHIFT 29 /* SMBus Clock Out Shift */ + +/* SW Semaphore Register */ +#define E1000_SWSM_SMBI 0x00000001 /* Driver Semaphore bit */ +#define E1000_SWSM_SWESMBI 0x00000002 /* FW Semaphore bit */ +#define E1000_SWSM_WMNG 0x00000004 /* Wake MNG Clock */ +#define E1000_SWSM_DRV_LOAD 0x00000008 /* Driver Loaded Bit */ + +/* FW Semaphore Register */ +#define E1000_FWSM_MODE_MASK 0x0000000E /* FW mode */ +#define E1000_FWSM_MODE_SHIFT 1 +#define E1000_FWSM_FW_VALID 0x00008000 /* FW established a valid mode */ + +#define E1000_FWSM_RSPCIPHY 0x00000040 /* Reset PHY on PCI reset */ +#define E1000_FWSM_DISSW 0x10000000 /* FW disable SW Write Access */ +#define E1000_FWSM_SKUSEL_MASK 0x60000000 /* LAN SKU select */ +#define E1000_FWSM_SKUEL_SHIFT 29 +#define E1000_FWSM_SKUSEL_EMB 0x0 /* Embedded SKU */ +#define E1000_FWSM_SKUSEL_CONS 0x1 /* Consumer SKU */ +#define E1000_FWSM_SKUSEL_PERF_100 0x2 /* Perf & Corp 10/100 SKU */ +#define E1000_FWSM_SKUSEL_PERF_GBE 0x3 /* Perf & Copr GbE SKU */ + +/* FFLT Debug Register */ +#define E1000_FFLT_DBG_INVC 0x00100000 /* Invalid /C/ code handling */ + +typedef enum { + e1000_mng_mode_none = 0, + e1000_mng_mode_asf, + e1000_mng_mode_pt, + e1000_mng_mode_ipmi, + e1000_mng_mode_host_interface_only +} e1000_mng_mode; + +/* Host Inteface Control Register */ +#define E1000_HICR_EN 0x00000001 /* Enable Bit - RO */ +#define E1000_HICR_C 0x00000002 /* Driver sets this bit when done + * to put command in RAM */ +#define E1000_HICR_SV 0x00000004 /* Status Validity */ +#define E1000_HICR_FWR 0x00000080 /* FW reset. Set by the Host */ + +/* Host Interface Command Interface - Address range 0x8800-0x8EFF */ +#define E1000_HI_MAX_DATA_LENGTH 252 /* Host Interface data length */ +#define E1000_HI_MAX_BLOCK_BYTE_LENGTH 1792 /* Number of bytes in range */ +#define E1000_HI_MAX_BLOCK_DWORD_LENGTH 448 /* Number of dwords in range */ +#define E1000_HI_COMMAND_TIMEOUT 500 /* Time in ms to process HI command */ + +struct e1000_host_command_header { + uint8_t command_id; + uint8_t command_length; + uint8_t command_options; /* I/F bits for command, status for return */ + uint8_t checksum; +}; +struct e1000_host_command_info { + struct e1000_host_command_header command_header; /* Command Head/Command Result Head has 4 bytes */ + uint8_t command_data[E1000_HI_MAX_DATA_LENGTH]; /* Command data can length 0..252 */ +}; + +/* Host SMB register #0 */ +#define E1000_HSMC0R_CLKIN 0x00000001 /* SMB Clock in */ +#define E1000_HSMC0R_DATAIN 0x00000002 /* SMB Data in */ +#define E1000_HSMC0R_DATAOUT 0x00000004 /* SMB Data out */ +#define E1000_HSMC0R_CLKOUT 0x00000008 /* SMB Clock out */ + +/* Host SMB register #1 */ +#define E1000_HSMC1R_CLKIN E1000_HSMC0R_CLKIN +#define E1000_HSMC1R_DATAIN E1000_HSMC0R_DATAIN +#define E1000_HSMC1R_DATAOUT E1000_HSMC0R_DATAOUT +#define E1000_HSMC1R_CLKOUT E1000_HSMC0R_CLKOUT + +/* FW Status Register */ +#define E1000_FWSTS_FWS_MASK 0x000000FF /* FW Status */ + +/* Wake Up Packet Length */ +#define E1000_WUPL_LENGTH_MASK 0x0FFF /* Only the lower 12 bits are valid */ + +#define E1000_MDALIGN 4096 + +/* PCI-Ex registers*/ + +/* PCI-Ex Control Register */ +#define E1000_GCR_RXD_NO_SNOOP 0x00000001 +#define E1000_GCR_RXDSCW_NO_SNOOP 0x00000002 +#define E1000_GCR_RXDSCR_NO_SNOOP 0x00000004 +#define E1000_GCR_TXD_NO_SNOOP 0x00000008 +#define E1000_GCR_TXDSCW_NO_SNOOP 0x00000010 +#define E1000_GCR_TXDSCR_NO_SNOOP 0x00000020 + +#define PCI_EX_NO_SNOOP_ALL (E1000_GCR_RXD_NO_SNOOP | \ + E1000_GCR_RXDSCW_NO_SNOOP | \ + E1000_GCR_RXDSCR_NO_SNOOP | \ + E1000_GCR_TXD_NO_SNOOP | \ + E1000_GCR_TXDSCW_NO_SNOOP | \ + E1000_GCR_TXDSCR_NO_SNOOP) + +#define PCI_EX_82566_SNOOP_ALL PCI_EX_NO_SNOOP_ALL + +#define E1000_GCR_L1_ACT_WITHOUT_L0S_RX 0x08000000 +/* Function Active and Power State to MNG */ +#define E1000_FACTPS_FUNC0_POWER_STATE_MASK 0x00000003 +#define E1000_FACTPS_LAN0_VALID 0x00000004 +#define E1000_FACTPS_FUNC0_AUX_EN 0x00000008 +#define E1000_FACTPS_FUNC1_POWER_STATE_MASK 0x000000C0 +#define E1000_FACTPS_FUNC1_POWER_STATE_SHIFT 6 +#define E1000_FACTPS_LAN1_VALID 0x00000100 +#define E1000_FACTPS_FUNC1_AUX_EN 0x00000200 +#define E1000_FACTPS_FUNC2_POWER_STATE_MASK 0x00003000 +#define E1000_FACTPS_FUNC2_POWER_STATE_SHIFT 12 +#define E1000_FACTPS_IDE_ENABLE 0x00004000 +#define E1000_FACTPS_FUNC2_AUX_EN 0x00008000 +#define E1000_FACTPS_FUNC3_POWER_STATE_MASK 0x000C0000 +#define E1000_FACTPS_FUNC3_POWER_STATE_SHIFT 18 +#define E1000_FACTPS_SP_ENABLE 0x00100000 +#define E1000_FACTPS_FUNC3_AUX_EN 0x00200000 +#define E1000_FACTPS_FUNC4_POWER_STATE_MASK 0x03000000 +#define E1000_FACTPS_FUNC4_POWER_STATE_SHIFT 24 +#define E1000_FACTPS_IPMI_ENABLE 0x04000000 +#define E1000_FACTPS_FUNC4_AUX_EN 0x08000000 +#define E1000_FACTPS_MNGCG 0x20000000 +#define E1000_FACTPS_LAN_FUNC_SEL 0x40000000 +#define E1000_FACTPS_PM_STATE_CHANGED 0x80000000 + +/* PCI-Ex Config Space */ +#define PCI_EX_LINK_STATUS 0x12 +#define PCI_EX_LINK_WIDTH_MASK 0x3F0 +#define PCI_EX_LINK_WIDTH_SHIFT 4 + +/* EEPROM Commands - Microwire */ +#define EEPROM_READ_OPCODE_MICROWIRE 0x6 /* EEPROM read opcode */ +#define EEPROM_WRITE_OPCODE_MICROWIRE 0x5 /* EEPROM write opcode */ +#define EEPROM_ERASE_OPCODE_MICROWIRE 0x7 /* EEPROM erase opcode */ +#define EEPROM_EWEN_OPCODE_MICROWIRE 0x13 /* EEPROM erase/write enable */ +#define EEPROM_EWDS_OPCODE_MICROWIRE 0x10 /* EEPROM erast/write disable */ + +/* EEPROM Commands - SPI */ +#define EEPROM_MAX_RETRY_SPI 5000 /* Max wait of 5ms, for RDY signal */ +#define EEPROM_READ_OPCODE_SPI 0x03 /* EEPROM read opcode */ +#define EEPROM_WRITE_OPCODE_SPI 0x02 /* EEPROM write opcode */ +#define EEPROM_A8_OPCODE_SPI 0x08 /* opcode bit-3 = address bit-8 */ +#define EEPROM_WREN_OPCODE_SPI 0x06 /* EEPROM set Write Enable latch */ +#define EEPROM_WRDI_OPCODE_SPI 0x04 /* EEPROM reset Write Enable latch */ +#define EEPROM_RDSR_OPCODE_SPI 0x05 /* EEPROM read Status register */ +#define EEPROM_WRSR_OPCODE_SPI 0x01 /* EEPROM write Status register */ +#define EEPROM_ERASE4K_OPCODE_SPI 0x20 /* EEPROM ERASE 4KB */ +#define EEPROM_ERASE64K_OPCODE_SPI 0xD8 /* EEPROM ERASE 64KB */ +#define EEPROM_ERASE256_OPCODE_SPI 0xDB /* EEPROM ERASE 256B */ + +/* EEPROM Size definitions */ +#define EEPROM_WORD_SIZE_SHIFT 6 +#define EEPROM_SIZE_SHIFT 10 +#define EEPROM_SIZE_MASK 0x1C00 + +/* EEPROM Word Offsets */ +#define EEPROM_COMPAT 0x0003 +#define EEPROM_ID_LED_SETTINGS 0x0004 +#define EEPROM_VERSION 0x0005 +#define EEPROM_SERDES_AMPLITUDE 0x0006 /* For SERDES output amplitude adjustment. */ +#define EEPROM_PHY_CLASS_WORD 0x0007 +#define EEPROM_INIT_CONTROL1_REG 0x000A +#define EEPROM_INIT_CONTROL2_REG 0x000F +#define EEPROM_SWDEF_PINS_CTRL_PORT_1 0x0010 +#define EEPROM_INIT_CONTROL3_PORT_B 0x0014 +#define EEPROM_INIT_3GIO_3 0x001A +#define EEPROM_SWDEF_PINS_CTRL_PORT_0 0x0020 +#define EEPROM_INIT_CONTROL3_PORT_A 0x0024 +#define EEPROM_CFG 0x0012 +#define EEPROM_FLASH_VERSION 0x0032 +#define EEPROM_CHECKSUM_REG 0x003F + +#define E1000_EEPROM_CFG_DONE 0x00040000 /* MNG config cycle done */ +#define E1000_EEPROM_CFG_DONE_PORT_1 0x00080000 /* ...for second port */ + +/* Word definitions for ID LED Settings */ +#define ID_LED_RESERVED_0000 0x0000 +#define ID_LED_RESERVED_FFFF 0xFFFF +#define ID_LED_RESERVED_82573 0xF746 +#define ID_LED_DEFAULT_82573 0x1811 +#define ID_LED_DEFAULT ((ID_LED_OFF1_ON2 << 12) | \ + (ID_LED_OFF1_OFF2 << 8) | \ + (ID_LED_DEF1_DEF2 << 4) | \ + (ID_LED_DEF1_DEF2)) +#define ID_LED_DEFAULT_ICH8LAN ((ID_LED_DEF1_DEF2 << 12) | \ + (ID_LED_DEF1_OFF2 << 8) | \ + (ID_LED_DEF1_ON2 << 4) | \ + (ID_LED_DEF1_DEF2)) +#define ID_LED_DEF1_DEF2 0x1 +#define ID_LED_DEF1_ON2 0x2 +#define ID_LED_DEF1_OFF2 0x3 +#define ID_LED_ON1_DEF2 0x4 +#define ID_LED_ON1_ON2 0x5 +#define ID_LED_ON1_OFF2 0x6 +#define ID_LED_OFF1_DEF2 0x7 +#define ID_LED_OFF1_ON2 0x8 +#define ID_LED_OFF1_OFF2 0x9 + +#define IGP_ACTIVITY_LED_MASK 0xFFFFF0FF +#define IGP_ACTIVITY_LED_ENABLE 0x0300 +#define IGP_LED3_MODE 0x07000000 + + +/* Mask bits for SERDES amplitude adjustment in Word 6 of the EEPROM */ +#define EEPROM_SERDES_AMPLITUDE_MASK 0x000F + +/* Mask bit for PHY class in Word 7 of the EEPROM */ +#define EEPROM_PHY_CLASS_A 0x8000 + +/* Mask bits for fields in Word 0x0a of the EEPROM */ +#define EEPROM_WORD0A_ILOS 0x0010 +#define EEPROM_WORD0A_SWDPIO 0x01E0 +#define EEPROM_WORD0A_LRST 0x0200 +#define EEPROM_WORD0A_FD 0x0400 +#define EEPROM_WORD0A_66MHZ 0x0800 + +/* Mask bits for fields in Word 0x0f of the EEPROM */ +#define EEPROM_WORD0F_PAUSE_MASK 0x3000 +#define EEPROM_WORD0F_PAUSE 0x1000 +#define EEPROM_WORD0F_ASM_DIR 0x2000 +#define EEPROM_WORD0F_ANE 0x0800 +#define EEPROM_WORD0F_SWPDIO_EXT 0x00F0 +#define EEPROM_WORD0F_LPLU 0x0001 + +/* Mask bits for fields in Word 0x10/0x20 of the EEPROM */ +#define EEPROM_WORD1020_GIGA_DISABLE 0x0010 +#define EEPROM_WORD1020_GIGA_DISABLE_NON_D0A 0x0008 + +/* Mask bits for fields in Word 0x1a of the EEPROM */ +#define EEPROM_WORD1A_ASPM_MASK 0x000C + +/* For checksumming, the sum of all words in the EEPROM should equal 0xBABA. */ +#define EEPROM_SUM 0xBABA + +/* EEPROM Map defines (WORD OFFSETS)*/ +#define EEPROM_NODE_ADDRESS_BYTE_0 0 +#define EEPROM_PBA_BYTE_1 8 + +#define EEPROM_RESERVED_WORD 0xFFFF + +/* EEPROM Map Sizes (Byte Counts) */ +#define PBA_SIZE 4 + +/* Collision related configuration parameters */ +#define E1000_COLLISION_THRESHOLD 15 +#define E1000_CT_SHIFT 4 +/* Collision distance is a 0-based value that applies to + * half-duplex-capable hardware only. */ +#define E1000_COLLISION_DISTANCE 63 +#define E1000_COLLISION_DISTANCE_82542 64 +#define E1000_FDX_COLLISION_DISTANCE E1000_COLLISION_DISTANCE +#define E1000_HDX_COLLISION_DISTANCE E1000_COLLISION_DISTANCE +#define E1000_COLD_SHIFT 12 + +/* Number of Transmit and Receive Descriptors must be a multiple of 8 */ +#define REQ_TX_DESCRIPTOR_MULTIPLE 8 +#define REQ_RX_DESCRIPTOR_MULTIPLE 8 + +/* Default values for the transmit IPG register */ +#define DEFAULT_82542_TIPG_IPGT 10 +#define DEFAULT_82543_TIPG_IPGT_FIBER 9 +#define DEFAULT_82543_TIPG_IPGT_COPPER 8 + +#define E1000_TIPG_IPGT_MASK 0x000003FF +#define E1000_TIPG_IPGR1_MASK 0x000FFC00 +#define E1000_TIPG_IPGR2_MASK 0x3FF00000 + +#define DEFAULT_82542_TIPG_IPGR1 2 +#define DEFAULT_82543_TIPG_IPGR1 8 +#define E1000_TIPG_IPGR1_SHIFT 10 + +#define DEFAULT_82542_TIPG_IPGR2 10 +#define DEFAULT_82543_TIPG_IPGR2 6 +#define DEFAULT_80003ES2LAN_TIPG_IPGR2 7 +#define E1000_TIPG_IPGR2_SHIFT 20 + +#define DEFAULT_80003ES2LAN_TIPG_IPGT_10_100 0x00000009 +#define DEFAULT_80003ES2LAN_TIPG_IPGT_1000 0x00000008 +#define E1000_TXDMAC_DPP 0x00000001 + +/* Adaptive IFS defines */ +#define TX_THRESHOLD_START 8 +#define TX_THRESHOLD_INCREMENT 10 +#define TX_THRESHOLD_DECREMENT 1 +#define TX_THRESHOLD_STOP 190 +#define TX_THRESHOLD_DISABLE 0 +#define TX_THRESHOLD_TIMER_MS 10000 +#define MIN_NUM_XMITS 1000 +#define IFS_MAX 80 +#define IFS_STEP 10 +#define IFS_MIN 40 +#define IFS_RATIO 4 + +/* Extended Configuration Control and Size */ +#define E1000_EXTCNF_CTRL_PCIE_WRITE_ENABLE 0x00000001 +#define E1000_EXTCNF_CTRL_PHY_WRITE_ENABLE 0x00000002 +#define E1000_EXTCNF_CTRL_D_UD_ENABLE 0x00000004 +#define E1000_EXTCNF_CTRL_D_UD_LATENCY 0x00000008 +#define E1000_EXTCNF_CTRL_D_UD_OWNER 0x00000010 +#define E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP 0x00000020 +#define E1000_EXTCNF_CTRL_MDIO_HW_OWNERSHIP 0x00000040 +#define E1000_EXTCNF_CTRL_EXT_CNF_POINTER 0x0FFF0000 + +#define E1000_EXTCNF_SIZE_EXT_PHY_LENGTH 0x000000FF +#define E1000_EXTCNF_SIZE_EXT_DOCK_LENGTH 0x0000FF00 +#define E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH 0x00FF0000 +#define E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE 0x00000001 +#define E1000_EXTCNF_CTRL_SWFLAG 0x00000020 + +/* PBA constants */ +#define E1000_PBA_8K 0x0008 /* 8KB, default Rx allocation */ +#define E1000_PBA_12K 0x000C /* 12KB, default Rx allocation */ +#define E1000_PBA_16K 0x0010 /* 16KB, default TX allocation */ +#define E1000_PBA_20K 0x0014 +#define E1000_PBA_22K 0x0016 +#define E1000_PBA_24K 0x0018 +#define E1000_PBA_30K 0x001E +#define E1000_PBA_32K 0x0020 +#define E1000_PBA_34K 0x0022 +#define E1000_PBA_38K 0x0026 +#define E1000_PBA_40K 0x0028 +#define E1000_PBA_48K 0x0030 /* 48KB, default RX allocation */ + +#define E1000_PBS_16K E1000_PBA_16K + +/* Flow Control Constants */ +#define FLOW_CONTROL_ADDRESS_LOW 0x00C28001 +#define FLOW_CONTROL_ADDRESS_HIGH 0x00000100 +#define FLOW_CONTROL_TYPE 0x8808 + +/* The historical defaults for the flow control values are given below. */ +#define FC_DEFAULT_HI_THRESH (0x8000) /* 32KB */ +#define FC_DEFAULT_LO_THRESH (0x4000) /* 16KB */ +#define FC_DEFAULT_TX_TIMER (0x100) /* ~130 us */ + +/* PCIX Config space */ +#define PCIX_COMMAND_REGISTER 0xE6 +#define PCIX_STATUS_REGISTER_LO 0xE8 +#define PCIX_STATUS_REGISTER_HI 0xEA + +#define PCIX_COMMAND_MMRBC_MASK 0x000C +#define PCIX_COMMAND_MMRBC_SHIFT 0x2 +#define PCIX_STATUS_HI_MMRBC_MASK 0x0060 +#define PCIX_STATUS_HI_MMRBC_SHIFT 0x5 +#define PCIX_STATUS_HI_MMRBC_4K 0x3 +#define PCIX_STATUS_HI_MMRBC_2K 0x2 + + +/* Number of bits required to shift right the "pause" bits from the + * EEPROM (bits 13:12) to the "pause" (bits 8:7) field in the TXCW register. + */ +#define PAUSE_SHIFT 5 + +/* Number of bits required to shift left the "SWDPIO" bits from the + * EEPROM (bits 8:5) to the "SWDPIO" (bits 25:22) field in the CTRL register. + */ +#define SWDPIO_SHIFT 17 + +/* Number of bits required to shift left the "SWDPIO_EXT" bits from the + * EEPROM word F (bits 7:4) to the bits 11:8 of The Extended CTRL register. + */ +#define SWDPIO__EXT_SHIFT 4 + +/* Number of bits required to shift left the "ILOS" bit from the EEPROM + * (bit 4) to the "ILOS" (bit 7) field in the CTRL register. + */ +#define ILOS_SHIFT 3 + + +#define RECEIVE_BUFFER_ALIGN_SIZE (256) + +/* Number of milliseconds we wait for auto-negotiation to complete */ +#define LINK_UP_TIMEOUT 500 + +/* Number of 100 microseconds we wait for PCI Express master disable */ +#define MASTER_DISABLE_TIMEOUT 800 +/* Number of milliseconds we wait for Eeprom auto read bit done after MAC reset */ +#define AUTO_READ_DONE_TIMEOUT 10 +/* Number of milliseconds we wait for PHY configuration done after MAC reset */ +#define PHY_CFG_TIMEOUT 100 + +#define E1000_TX_BUFFER_SIZE ((uint32_t)1514) + +/* The carrier extension symbol, as received by the NIC. */ +#define CARRIER_EXTENSION 0x0F + +/* TBI_ACCEPT macro definition: + * + * This macro requires: + * adapter = a pointer to struct e1000_hw + * status = the 8 bit status field of the RX descriptor with EOP set + * error = the 8 bit error field of the RX descriptor with EOP set + * length = the sum of all the length fields of the RX descriptors that + * make up the current frame + * last_byte = the last byte of the frame DMAed by the hardware + * max_frame_length = the maximum frame length we want to accept. + * min_frame_length = the minimum frame length we want to accept. + * + * This macro is a conditional that should be used in the interrupt + * handler's Rx processing routine when RxErrors have been detected. + * + * Typical use: + * ... + * if (TBI_ACCEPT) { + * accept_frame = TRUE; + * e1000_tbi_adjust_stats(adapter, MacAddress); + * frame_length--; + * } else { + * accept_frame = FALSE; + * } + * ... + */ + +#define TBI_ACCEPT(adapter, status, errors, length, last_byte) \ + ((adapter)->tbi_compatibility_on && \ + (((errors) & E1000_RXD_ERR_FRAME_ERR_MASK) == E1000_RXD_ERR_CE) && \ + ((last_byte) == CARRIER_EXTENSION) && \ + (((status) & E1000_RXD_STAT_VP) ? \ + (((length) > ((adapter)->min_frame_size - VLAN_TAG_SIZE)) && \ + ((length) <= ((adapter)->max_frame_size + 1))) : \ + (((length) > (adapter)->min_frame_size) && \ + ((length) <= ((adapter)->max_frame_size + VLAN_TAG_SIZE + 1))))) + + +/* Structures, enums, and macros for the PHY */ + +/* Bit definitions for the Management Data IO (MDIO) and Management Data + * Clock (MDC) pins in the Device Control Register. + */ +#define E1000_CTRL_PHY_RESET_DIR E1000_CTRL_SWDPIO0 +#define E1000_CTRL_PHY_RESET E1000_CTRL_SWDPIN0 +#define E1000_CTRL_MDIO_DIR E1000_CTRL_SWDPIO2 +#define E1000_CTRL_MDIO E1000_CTRL_SWDPIN2 +#define E1000_CTRL_MDC_DIR E1000_CTRL_SWDPIO3 +#define E1000_CTRL_MDC E1000_CTRL_SWDPIN3 +#define E1000_CTRL_PHY_RESET_DIR4 E1000_CTRL_EXT_SDP4_DIR +#define E1000_CTRL_PHY_RESET4 E1000_CTRL_EXT_SDP4_DATA + +/* PHY 1000 MII Register/Bit Definitions */ +/* PHY Registers defined by IEEE */ +#define PHY_CTRL 0x00 /* Control Register */ +#define PHY_STATUS 0x01 /* Status Regiser */ +#define PHY_ID1 0x02 /* Phy Id Reg (word 1) */ +#define PHY_ID2 0x03 /* Phy Id Reg (word 2) */ +#define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */ +#define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */ +#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */ +#define PHY_NEXT_PAGE_TX 0x07 /* Next Page TX */ +#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */ +#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */ +#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */ +#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */ + +#define MAX_PHY_REG_ADDRESS 0x1F /* 5 bit address bus (0-0x1F) */ +#define MAX_PHY_MULTI_PAGE_REG 0xF /* Registers equal on all pages */ + +/* M88E1000 Specific Registers */ +#define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Register */ +#define M88E1000_PHY_SPEC_STATUS 0x11 /* PHY Specific Status Register */ +#define M88E1000_INT_ENABLE 0x12 /* Interrupt Enable Register */ +#define M88E1000_INT_STATUS 0x13 /* Interrupt Status Register */ +#define M88E1000_EXT_PHY_SPEC_CTRL 0x14 /* Extended PHY Specific Control */ +#define M88E1000_RX_ERR_CNTR 0x15 /* Receive Error Counter */ + +#define M88E1000_PHY_EXT_CTRL 0x1A /* PHY extend control register */ +#define M88E1000_PHY_PAGE_SELECT 0x1D /* Reg 29 for page number setting */ +#define M88E1000_PHY_GEN_CONTROL 0x1E /* Its meaning depends on reg 29 */ +#define M88E1000_PHY_VCO_REG_BIT8 0x100 /* Bits 8 & 11 are adjusted for */ +#define M88E1000_PHY_VCO_REG_BIT11 0x800 /* improved BER performance */ + +#define IGP01E1000_IEEE_REGS_PAGE 0x0000 +#define IGP01E1000_IEEE_RESTART_AUTONEG 0x3300 +#define IGP01E1000_IEEE_FORCE_GIGA 0x0140 + +/* IGP01E1000 Specific Registers */ +#define IGP01E1000_PHY_PORT_CONFIG 0x10 /* PHY Specific Port Config Register */ +#define IGP01E1000_PHY_PORT_STATUS 0x11 /* PHY Specific Status Register */ +#define IGP01E1000_PHY_PORT_CTRL 0x12 /* PHY Specific Control Register */ +#define IGP01E1000_PHY_LINK_HEALTH 0x13 /* PHY Link Health Register */ +#define IGP01E1000_GMII_FIFO 0x14 /* GMII FIFO Register */ +#define IGP01E1000_PHY_CHANNEL_QUALITY 0x15 /* PHY Channel Quality Register */ +#define IGP02E1000_PHY_POWER_MGMT 0x19 +#define IGP01E1000_PHY_PAGE_SELECT 0x1F /* PHY Page Select Core Register */ + +/* IGP01E1000 AGC Registers - stores the cable length values*/ +#define IGP01E1000_PHY_AGC_A 0x1172 +#define IGP01E1000_PHY_AGC_B 0x1272 +#define IGP01E1000_PHY_AGC_C 0x1472 +#define IGP01E1000_PHY_AGC_D 0x1872 + +/* IGP02E1000 AGC Registers for cable length values */ +#define IGP02E1000_PHY_AGC_A 0x11B1 +#define IGP02E1000_PHY_AGC_B 0x12B1 +#define IGP02E1000_PHY_AGC_C 0x14B1 +#define IGP02E1000_PHY_AGC_D 0x18B1 + +/* IGP01E1000 DSP Reset Register */ +#define IGP01E1000_PHY_DSP_RESET 0x1F33 +#define IGP01E1000_PHY_DSP_SET 0x1F71 +#define IGP01E1000_PHY_DSP_FFE 0x1F35 + +#define IGP01E1000_PHY_CHANNEL_NUM 4 +#define IGP02E1000_PHY_CHANNEL_NUM 4 + +#define IGP01E1000_PHY_AGC_PARAM_A 0x1171 +#define IGP01E1000_PHY_AGC_PARAM_B 0x1271 +#define IGP01E1000_PHY_AGC_PARAM_C 0x1471 +#define IGP01E1000_PHY_AGC_PARAM_D 0x1871 + +#define IGP01E1000_PHY_EDAC_MU_INDEX 0xC000 +#define IGP01E1000_PHY_EDAC_SIGN_EXT_9_BITS 0x8000 + +#define IGP01E1000_PHY_ANALOG_TX_STATE 0x2890 +#define IGP01E1000_PHY_ANALOG_CLASS_A 0x2000 +#define IGP01E1000_PHY_FORCE_ANALOG_ENABLE 0x0004 +#define IGP01E1000_PHY_DSP_FFE_CM_CP 0x0069 + +#define IGP01E1000_PHY_DSP_FFE_DEFAULT 0x002A +/* IGP01E1000 PCS Initialization register - stores the polarity status when + * speed = 1000 Mbps. */ +#define IGP01E1000_PHY_PCS_INIT_REG 0x00B4 +#define IGP01E1000_PHY_PCS_CTRL_REG 0x00B5 + +#define IGP01E1000_ANALOG_REGS_PAGE 0x20C0 + +/* Bits... + * 15-5: page + * 4-0: register offset + */ +#define GG82563_PAGE_SHIFT 5 +#define GG82563_REG(page, reg) \ + (((page) << GG82563_PAGE_SHIFT) | ((reg) & MAX_PHY_REG_ADDRESS)) +#define GG82563_MIN_ALT_REG 30 + +/* GG82563 Specific Registers */ +#define GG82563_PHY_SPEC_CTRL \ + GG82563_REG(0, 16) /* PHY Specific Control */ +#define GG82563_PHY_SPEC_STATUS \ + GG82563_REG(0, 17) /* PHY Specific Status */ +#define GG82563_PHY_INT_ENABLE \ + GG82563_REG(0, 18) /* Interrupt Enable */ +#define GG82563_PHY_SPEC_STATUS_2 \ + GG82563_REG(0, 19) /* PHY Specific Status 2 */ +#define GG82563_PHY_RX_ERR_CNTR \ + GG82563_REG(0, 21) /* Receive Error Counter */ +#define GG82563_PHY_PAGE_SELECT \ + GG82563_REG(0, 22) /* Page Select */ +#define GG82563_PHY_SPEC_CTRL_2 \ + GG82563_REG(0, 26) /* PHY Specific Control 2 */ +#define GG82563_PHY_PAGE_SELECT_ALT \ + GG82563_REG(0, 29) /* Alternate Page Select */ +#define GG82563_PHY_TEST_CLK_CTRL \ + GG82563_REG(0, 30) /* Test Clock Control (use reg. 29 to select) */ + +#define GG82563_PHY_MAC_SPEC_CTRL \ + GG82563_REG(2, 21) /* MAC Specific Control Register */ +#define GG82563_PHY_MAC_SPEC_CTRL_2 \ + GG82563_REG(2, 26) /* MAC Specific Control 2 */ + +#define GG82563_PHY_DSP_DISTANCE \ + GG82563_REG(5, 26) /* DSP Distance */ + +/* Page 193 - Port Control Registers */ +#define GG82563_PHY_KMRN_MODE_CTRL \ + GG82563_REG(193, 16) /* Kumeran Mode Control */ +#define GG82563_PHY_PORT_RESET \ + GG82563_REG(193, 17) /* Port Reset */ +#define GG82563_PHY_REVISION_ID \ + GG82563_REG(193, 18) /* Revision ID */ +#define GG82563_PHY_DEVICE_ID \ + GG82563_REG(193, 19) /* Device ID */ +#define GG82563_PHY_PWR_MGMT_CTRL \ + GG82563_REG(193, 20) /* Power Management Control */ +#define GG82563_PHY_RATE_ADAPT_CTRL \ + GG82563_REG(193, 25) /* Rate Adaptation Control */ + +/* Page 194 - KMRN Registers */ +#define GG82563_PHY_KMRN_FIFO_CTRL_STAT \ + GG82563_REG(194, 16) /* FIFO's Control/Status */ +#define GG82563_PHY_KMRN_CTRL \ + GG82563_REG(194, 17) /* Control */ +#define GG82563_PHY_INBAND_CTRL \ + GG82563_REG(194, 18) /* Inband Control */ +#define GG82563_PHY_KMRN_DIAGNOSTIC \ + GG82563_REG(194, 19) /* Diagnostic */ +#define GG82563_PHY_ACK_TIMEOUTS \ + GG82563_REG(194, 20) /* Acknowledge Timeouts */ +#define GG82563_PHY_ADV_ABILITY \ + GG82563_REG(194, 21) /* Advertised Ability */ +#define GG82563_PHY_LINK_PARTNER_ADV_ABILITY \ + GG82563_REG(194, 23) /* Link Partner Advertised Ability */ +#define GG82563_PHY_ADV_NEXT_PAGE \ + GG82563_REG(194, 24) /* Advertised Next Page */ +#define GG82563_PHY_LINK_PARTNER_ADV_NEXT_PAGE \ + GG82563_REG(194, 25) /* Link Partner Advertised Next page */ +#define GG82563_PHY_KMRN_MISC \ + GG82563_REG(194, 26) /* Misc. */ + +/* PHY Control Register */ +#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ +#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ +#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ +#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */ +#define MII_CR_POWER_DOWN 0x0800 /* Power down */ +#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ +#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ +#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ + +/* PHY Status Register */ +#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ +#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ +#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ +#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ +#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ +#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ +#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ +#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ +#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ +#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ +#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ +#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ +#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ +#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ +#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ + +/* Autoneg Advertisement Register */ +#define NWAY_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */ +#define NWAY_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */ +#define NWAY_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */ +#define NWAY_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */ +#define NWAY_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */ +#define NWAY_AR_100T4_CAPS 0x0200 /* 100T4 Capable */ +#define NWAY_AR_PAUSE 0x0400 /* Pause operation desired */ +#define NWAY_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */ +#define NWAY_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */ +#define NWAY_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Link Partner Ability Register (Base Page) */ +#define NWAY_LPAR_SELECTOR_FIELD 0x0000 /* LP protocol selector field */ +#define NWAY_LPAR_10T_HD_CAPS 0x0020 /* LP is 10T Half Duplex Capable */ +#define NWAY_LPAR_10T_FD_CAPS 0x0040 /* LP is 10T Full Duplex Capable */ +#define NWAY_LPAR_100TX_HD_CAPS 0x0080 /* LP is 100TX Half Duplex Capable */ +#define NWAY_LPAR_100TX_FD_CAPS 0x0100 /* LP is 100TX Full Duplex Capable */ +#define NWAY_LPAR_100T4_CAPS 0x0200 /* LP is 100T4 Capable */ +#define NWAY_LPAR_PAUSE 0x0400 /* LP Pause operation desired */ +#define NWAY_LPAR_ASM_DIR 0x0800 /* LP Asymmetric Pause Direction bit */ +#define NWAY_LPAR_REMOTE_FAULT 0x2000 /* LP has detected Remote Fault */ +#define NWAY_LPAR_ACKNOWLEDGE 0x4000 /* LP has rx'd link code word */ +#define NWAY_LPAR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Autoneg Expansion Register */ +#define NWAY_ER_LP_NWAY_CAPS 0x0001 /* LP has Auto Neg Capability */ +#define NWAY_ER_PAGE_RXD 0x0002 /* LP is 10T Half Duplex Capable */ +#define NWAY_ER_NEXT_PAGE_CAPS 0x0004 /* LP is 10T Full Duplex Capable */ +#define NWAY_ER_LP_NEXT_PAGE_CAPS 0x0008 /* LP is 100TX Half Duplex Capable */ +#define NWAY_ER_PAR_DETECT_FAULT 0x0010 /* LP is 100TX Full Duplex Capable */ + +/* Next Page TX Register */ +#define NPTX_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define NPTX_TOGGLE 0x0800 /* Toggles between exchanges + * of different NP + */ +#define NPTX_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg + * 0 = cannot comply with msg + */ +#define NPTX_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ +#define NPTX_NEXT_PAGE 0x8000 /* 1 = addition NP will follow + * 0 = sending last NP + */ + +/* Link Partner Next Page Register */ +#define LP_RNPR_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define LP_RNPR_TOGGLE 0x0800 /* Toggles between exchanges + * of different NP + */ +#define LP_RNPR_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg + * 0 = cannot comply with msg + */ +#define LP_RNPR_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ +#define LP_RNPR_ACKNOWLDGE 0x4000 /* 1 = ACK / 0 = NO ACK */ +#define LP_RNPR_NEXT_PAGE 0x8000 /* 1 = addition NP will follow + * 0 = sending last NP + */ + +/* 1000BASE-T Control Register */ +#define CR_1000T_ASYM_PAUSE 0x0080 /* Advertise asymmetric pause bit */ +#define CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */ +#define CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */ +#define CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port */ + /* 0=DTE device */ +#define CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master */ + /* 0=Configure PHY as Slave */ +#define CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value */ + /* 0=Automatic Master/Slave config */ +#define CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ +#define CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ +#define CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ +#define CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ +#define CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ + +/* 1000BASE-T Status Register */ +#define SR_1000T_IDLE_ERROR_CNT 0x00FF /* Num idle errors since last read */ +#define SR_1000T_ASYM_PAUSE_DIR 0x0100 /* LP asymmetric pause direction bit */ +#define SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */ +#define SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */ +#define SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */ +#define SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */ +#define SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local TX is Master, 0=Slave */ +#define SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */ +#define SR_1000T_REMOTE_RX_STATUS_SHIFT 12 +#define SR_1000T_LOCAL_RX_STATUS_SHIFT 13 +#define SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT 5 +#define FFE_IDLE_ERR_COUNT_TIMEOUT_20 20 +#define FFE_IDLE_ERR_COUNT_TIMEOUT_100 100 + +/* Extended Status Register */ +#define IEEE_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */ +#define IEEE_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */ +#define IEEE_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */ +#define IEEE_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */ + +#define PHY_TX_POLARITY_MASK 0x0100 /* register 10h bit 8 (polarity bit) */ +#define PHY_TX_NORMAL_POLARITY 0 /* register 10h bit 8 (normal polarity) */ + +#define AUTO_POLARITY_DISABLE 0x0010 /* register 11h bit 4 */ + /* (0=enable, 1=disable) */ + +/* M88E1000 PHY Specific Control Register */ +#define M88E1000_PSCR_JABBER_DISABLE 0x0001 /* 1=Jabber Function disabled */ +#define M88E1000_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reversal enabled */ +#define M88E1000_PSCR_SQE_TEST 0x0004 /* 1=SQE Test enabled */ +#define M88E1000_PSCR_CLK125_DISABLE 0x0010 /* 1=CLK125 low, + * 0=CLK125 toggling + */ +#define M88E1000_PSCR_MDI_MANUAL_MODE 0x0000 /* MDI Crossover Mode bits 6:5 */ + /* Manual MDI configuration */ +#define M88E1000_PSCR_MDIX_MANUAL_MODE 0x0020 /* Manual MDIX configuration */ +#define M88E1000_PSCR_AUTO_X_1000T 0x0040 /* 1000BASE-T: Auto crossover, + * 100BASE-TX/10BASE-T: + * MDI Mode + */ +#define M88E1000_PSCR_AUTO_X_MODE 0x0060 /* Auto crossover enabled + * all speeds. + */ +#define M88E1000_PSCR_10BT_EXT_DIST_ENABLE 0x0080 + /* 1=Enable Extended 10BASE-T distance + * (Lower 10BASE-T RX Threshold) + * 0=Normal 10BASE-T RX Threshold */ +#define M88E1000_PSCR_MII_5BIT_ENABLE 0x0100 + /* 1=5-Bit interface in 100BASE-TX + * 0=MII interface in 100BASE-TX */ +#define M88E1000_PSCR_SCRAMBLER_DISABLE 0x0200 /* 1=Scrambler disable */ +#define M88E1000_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force link good */ +#define M88E1000_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Transmit */ + +#define M88E1000_PSCR_POLARITY_REVERSAL_SHIFT 1 +#define M88E1000_PSCR_AUTO_X_MODE_SHIFT 5 +#define M88E1000_PSCR_10BT_EXT_DIST_ENABLE_SHIFT 7 + +/* M88E1000 PHY Specific Status Register */ +#define M88E1000_PSSR_JABBER 0x0001 /* 1=Jabber */ +#define M88E1000_PSSR_REV_POLARITY 0x0002 /* 1=Polarity reversed */ +#define M88E1000_PSSR_DOWNSHIFT 0x0020 /* 1=Downshifted */ +#define M88E1000_PSSR_MDIX 0x0040 /* 1=MDIX; 0=MDI */ +#define M88E1000_PSSR_CABLE_LENGTH 0x0380 /* 0=<50M;1=50-80M;2=80-110M; + * 3=110-140M;4=>140M */ +#define M88E1000_PSSR_LINK 0x0400 /* 1=Link up, 0=Link down */ +#define M88E1000_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */ +#define M88E1000_PSSR_PAGE_RCVD 0x1000 /* 1=Page received */ +#define M88E1000_PSSR_DPLX 0x2000 /* 1=Duplex 0=Half Duplex */ +#define M88E1000_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */ +#define M88E1000_PSSR_10MBS 0x0000 /* 00=10Mbs */ +#define M88E1000_PSSR_100MBS 0x4000 /* 01=100Mbs */ +#define M88E1000_PSSR_1000MBS 0x8000 /* 10=1000Mbs */ + +#define M88E1000_PSSR_REV_POLARITY_SHIFT 1 +#define M88E1000_PSSR_DOWNSHIFT_SHIFT 5 +#define M88E1000_PSSR_MDIX_SHIFT 6 +#define M88E1000_PSSR_CABLE_LENGTH_SHIFT 7 + +/* M88E1000 Extended PHY Specific Control Register */ +#define M88E1000_EPSCR_FIBER_LOOPBACK 0x4000 /* 1=Fiber loopback */ +#define M88E1000_EPSCR_DOWN_NO_IDLE 0x8000 /* 1=Lost lock detect enabled. + * Will assert lost lock and bring + * link down if idle not seen + * within 1ms in 1000BASE-T + */ +/* Number of times we will attempt to autonegotiate before downshifting if we + * are the master */ +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK 0x0C00 +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_1X 0x0000 +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_2X 0x0400 +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_3X 0x0800 +#define M88E1000_EPSCR_MASTER_DOWNSHIFT_4X 0x0C00 +/* Number of times we will attempt to autonegotiate before downshifting if we + * are the slave */ +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK 0x0300 +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_DIS 0x0000 +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X 0x0100 +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_2X 0x0200 +#define M88E1000_EPSCR_SLAVE_DOWNSHIFT_3X 0x0300 +#define M88E1000_EPSCR_TX_CLK_2_5 0x0060 /* 2.5 MHz TX_CLK */ +#define M88E1000_EPSCR_TX_CLK_25 0x0070 /* 25 MHz TX_CLK */ +#define M88E1000_EPSCR_TX_CLK_0 0x0000 /* NO TX_CLK */ + +/* M88EC018 Rev 2 specific DownShift settings */ +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK 0x0E00 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_1X 0x0000 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_2X 0x0200 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_3X 0x0400 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_4X 0x0600 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X 0x0800 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_6X 0x0A00 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_7X 0x0C00 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_8X 0x0E00 + +/* IGP01E1000 Specific Port Config Register - R/W */ +#define IGP01E1000_PSCFR_AUTO_MDIX_PAR_DETECT 0x0010 +#define IGP01E1000_PSCFR_PRE_EN 0x0020 +#define IGP01E1000_PSCFR_SMART_SPEED 0x0080 +#define IGP01E1000_PSCFR_DISABLE_TPLOOPBACK 0x0100 +#define IGP01E1000_PSCFR_DISABLE_JABBER 0x0400 +#define IGP01E1000_PSCFR_DISABLE_TRANSMIT 0x2000 + +/* IGP01E1000 Specific Port Status Register - R/O */ +#define IGP01E1000_PSSR_AUTONEG_FAILED 0x0001 /* RO LH SC */ +#define IGP01E1000_PSSR_POLARITY_REVERSED 0x0002 +#define IGP01E1000_PSSR_CABLE_LENGTH 0x007C +#define IGP01E1000_PSSR_FULL_DUPLEX 0x0200 +#define IGP01E1000_PSSR_LINK_UP 0x0400 +#define IGP01E1000_PSSR_MDIX 0x0800 +#define IGP01E1000_PSSR_SPEED_MASK 0xC000 /* speed bits mask */ +#define IGP01E1000_PSSR_SPEED_10MBPS 0x4000 +#define IGP01E1000_PSSR_SPEED_100MBPS 0x8000 +#define IGP01E1000_PSSR_SPEED_1000MBPS 0xC000 +#define IGP01E1000_PSSR_CABLE_LENGTH_SHIFT 0x0002 /* shift right 2 */ +#define IGP01E1000_PSSR_MDIX_SHIFT 0x000B /* shift right 11 */ + +/* IGP01E1000 Specific Port Control Register - R/W */ +#define IGP01E1000_PSCR_TP_LOOPBACK 0x0010 +#define IGP01E1000_PSCR_CORRECT_NC_SCMBLR 0x0200 +#define IGP01E1000_PSCR_TEN_CRS_SELECT 0x0400 +#define IGP01E1000_PSCR_FLIP_CHIP 0x0800 +#define IGP01E1000_PSCR_AUTO_MDIX 0x1000 +#define IGP01E1000_PSCR_FORCE_MDI_MDIX 0x2000 /* 0-MDI, 1-MDIX */ + +/* IGP01E1000 Specific Port Link Health Register */ +#define IGP01E1000_PLHR_SS_DOWNGRADE 0x8000 +#define IGP01E1000_PLHR_GIG_SCRAMBLER_ERROR 0x4000 +#define IGP01E1000_PLHR_MASTER_FAULT 0x2000 +#define IGP01E1000_PLHR_MASTER_RESOLUTION 0x1000 +#define IGP01E1000_PLHR_GIG_REM_RCVR_NOK 0x0800 /* LH */ +#define IGP01E1000_PLHR_IDLE_ERROR_CNT_OFLOW 0x0400 /* LH */ +#define IGP01E1000_PLHR_DATA_ERR_1 0x0200 /* LH */ +#define IGP01E1000_PLHR_DATA_ERR_0 0x0100 +#define IGP01E1000_PLHR_AUTONEG_FAULT 0x0040 +#define IGP01E1000_PLHR_AUTONEG_ACTIVE 0x0010 +#define IGP01E1000_PLHR_VALID_CHANNEL_D 0x0008 +#define IGP01E1000_PLHR_VALID_CHANNEL_C 0x0004 +#define IGP01E1000_PLHR_VALID_CHANNEL_B 0x0002 +#define IGP01E1000_PLHR_VALID_CHANNEL_A 0x0001 + +/* IGP01E1000 Channel Quality Register */ +#define IGP01E1000_MSE_CHANNEL_D 0x000F +#define IGP01E1000_MSE_CHANNEL_C 0x00F0 +#define IGP01E1000_MSE_CHANNEL_B 0x0F00 +#define IGP01E1000_MSE_CHANNEL_A 0xF000 + +#define IGP02E1000_PM_SPD 0x0001 /* Smart Power Down */ +#define IGP02E1000_PM_D3_LPLU 0x0004 /* Enable LPLU in non-D0a modes */ +#define IGP02E1000_PM_D0_LPLU 0x0002 /* Enable LPLU in D0a mode */ + +/* IGP01E1000 DSP reset macros */ +#define DSP_RESET_ENABLE 0x0 +#define DSP_RESET_DISABLE 0x2 +#define E1000_MAX_DSP_RESETS 10 + +/* IGP01E1000 & IGP02E1000 AGC Registers */ + +#define IGP01E1000_AGC_LENGTH_SHIFT 7 /* Coarse - 13:11, Fine - 10:7 */ +#define IGP02E1000_AGC_LENGTH_SHIFT 9 /* Coarse - 15:13, Fine - 12:9 */ + +/* IGP02E1000 AGC Register Length 9-bit mask */ +#define IGP02E1000_AGC_LENGTH_MASK 0x7F + +/* 7 bits (3 Coarse + 4 Fine) --> 128 optional values */ +#define IGP01E1000_AGC_LENGTH_TABLE_SIZE 128 +#define IGP02E1000_AGC_LENGTH_TABLE_SIZE 113 + +/* The precision error of the cable length is +/- 10 meters */ +#define IGP01E1000_AGC_RANGE 10 +#define IGP02E1000_AGC_RANGE 15 + +/* IGP01E1000 PCS Initialization register */ +/* bits 3:6 in the PCS registers stores the channels polarity */ +#define IGP01E1000_PHY_POLARITY_MASK 0x0078 + +/* IGP01E1000 GMII FIFO Register */ +#define IGP01E1000_GMII_FLEX_SPD 0x10 /* Enable flexible speed + * on Link-Up */ +#define IGP01E1000_GMII_SPD 0x20 /* Enable SPD */ + +/* IGP01E1000 Analog Register */ +#define IGP01E1000_ANALOG_SPARE_FUSE_STATUS 0x20D1 +#define IGP01E1000_ANALOG_FUSE_STATUS 0x20D0 +#define IGP01E1000_ANALOG_FUSE_CONTROL 0x20DC +#define IGP01E1000_ANALOG_FUSE_BYPASS 0x20DE + +#define IGP01E1000_ANALOG_FUSE_POLY_MASK 0xF000 +#define IGP01E1000_ANALOG_FUSE_FINE_MASK 0x0F80 +#define IGP01E1000_ANALOG_FUSE_COARSE_MASK 0x0070 +#define IGP01E1000_ANALOG_SPARE_FUSE_ENABLED 0x0100 +#define IGP01E1000_ANALOG_FUSE_ENABLE_SW_CONTROL 0x0002 + +#define IGP01E1000_ANALOG_FUSE_COARSE_THRESH 0x0040 +#define IGP01E1000_ANALOG_FUSE_COARSE_10 0x0010 +#define IGP01E1000_ANALOG_FUSE_FINE_1 0x0080 +#define IGP01E1000_ANALOG_FUSE_FINE_10 0x0500 + +/* GG82563 PHY Specific Status Register (Page 0, Register 16 */ +#define GG82563_PSCR_DISABLE_JABBER 0x0001 /* 1=Disable Jabber */ +#define GG82563_PSCR_POLARITY_REVERSAL_DISABLE 0x0002 /* 1=Polarity Reversal Disabled */ +#define GG82563_PSCR_POWER_DOWN 0x0004 /* 1=Power Down */ +#define GG82563_PSCR_COPPER_TRANSMITER_DISABLE 0x0008 /* 1=Transmitter Disabled */ +#define GG82563_PSCR_CROSSOVER_MODE_MASK 0x0060 +#define GG82563_PSCR_CROSSOVER_MODE_MDI 0x0000 /* 00=Manual MDI configuration */ +#define GG82563_PSCR_CROSSOVER_MODE_MDIX 0x0020 /* 01=Manual MDIX configuration */ +#define GG82563_PSCR_CROSSOVER_MODE_AUTO 0x0060 /* 11=Automatic crossover */ +#define GG82563_PSCR_ENALBE_EXTENDED_DISTANCE 0x0080 /* 1=Enable Extended Distance */ +#define GG82563_PSCR_ENERGY_DETECT_MASK 0x0300 +#define GG82563_PSCR_ENERGY_DETECT_OFF 0x0000 /* 00,01=Off */ +#define GG82563_PSCR_ENERGY_DETECT_RX 0x0200 /* 10=Sense on Rx only (Energy Detect) */ +#define GG82563_PSCR_ENERGY_DETECT_RX_TM 0x0300 /* 11=Sense and Tx NLP */ +#define GG82563_PSCR_FORCE_LINK_GOOD 0x0400 /* 1=Force Link Good */ +#define GG82563_PSCR_DOWNSHIFT_ENABLE 0x0800 /* 1=Enable Downshift */ +#define GG82563_PSCR_DOWNSHIFT_COUNTER_MASK 0x7000 +#define GG82563_PSCR_DOWNSHIFT_COUNTER_SHIFT 12 + +/* PHY Specific Status Register (Page 0, Register 17) */ +#define GG82563_PSSR_JABBER 0x0001 /* 1=Jabber */ +#define GG82563_PSSR_POLARITY 0x0002 /* 1=Polarity Reversed */ +#define GG82563_PSSR_LINK 0x0008 /* 1=Link is Up */ +#define GG82563_PSSR_ENERGY_DETECT 0x0010 /* 1=Sleep, 0=Active */ +#define GG82563_PSSR_DOWNSHIFT 0x0020 /* 1=Downshift */ +#define GG82563_PSSR_CROSSOVER_STATUS 0x0040 /* 1=MDIX, 0=MDI */ +#define GG82563_PSSR_RX_PAUSE_ENABLED 0x0100 /* 1=Receive Pause Enabled */ +#define GG82563_PSSR_TX_PAUSE_ENABLED 0x0200 /* 1=Transmit Pause Enabled */ +#define GG82563_PSSR_LINK_UP 0x0400 /* 1=Link Up */ +#define GG82563_PSSR_SPEED_DUPLEX_RESOLVED 0x0800 /* 1=Resolved */ +#define GG82563_PSSR_PAGE_RECEIVED 0x1000 /* 1=Page Received */ +#define GG82563_PSSR_DUPLEX 0x2000 /* 1-Full-Duplex */ +#define GG82563_PSSR_SPEED_MASK 0xC000 +#define GG82563_PSSR_SPEED_10MBPS 0x0000 /* 00=10Mbps */ +#define GG82563_PSSR_SPEED_100MBPS 0x4000 /* 01=100Mbps */ +#define GG82563_PSSR_SPEED_1000MBPS 0x8000 /* 10=1000Mbps */ + +/* PHY Specific Status Register 2 (Page 0, Register 19) */ +#define GG82563_PSSR2_JABBER 0x0001 /* 1=Jabber */ +#define GG82563_PSSR2_POLARITY_CHANGED 0x0002 /* 1=Polarity Changed */ +#define GG82563_PSSR2_ENERGY_DETECT_CHANGED 0x0010 /* 1=Energy Detect Changed */ +#define GG82563_PSSR2_DOWNSHIFT_INTERRUPT 0x0020 /* 1=Downshift Detected */ +#define GG82563_PSSR2_MDI_CROSSOVER_CHANGE 0x0040 /* 1=Crossover Changed */ +#define GG82563_PSSR2_FALSE_CARRIER 0x0100 /* 1=False Carrier */ +#define GG82563_PSSR2_SYMBOL_ERROR 0x0200 /* 1=Symbol Error */ +#define GG82563_PSSR2_LINK_STATUS_CHANGED 0x0400 /* 1=Link Status Changed */ +#define GG82563_PSSR2_AUTO_NEG_COMPLETED 0x0800 /* 1=Auto-Neg Completed */ +#define GG82563_PSSR2_PAGE_RECEIVED 0x1000 /* 1=Page Received */ +#define GG82563_PSSR2_DUPLEX_CHANGED 0x2000 /* 1=Duplex Changed */ +#define GG82563_PSSR2_SPEED_CHANGED 0x4000 /* 1=Speed Changed */ +#define GG82563_PSSR2_AUTO_NEG_ERROR 0x8000 /* 1=Auto-Neg Error */ + +/* PHY Specific Control Register 2 (Page 0, Register 26) */ +#define GG82563_PSCR2_10BT_POLARITY_FORCE 0x0002 /* 1=Force Negative Polarity */ +#define GG82563_PSCR2_1000MB_TEST_SELECT_MASK 0x000C +#define GG82563_PSCR2_1000MB_TEST_SELECT_NORMAL 0x0000 /* 00,01=Normal Operation */ +#define GG82563_PSCR2_1000MB_TEST_SELECT_112NS 0x0008 /* 10=Select 112ns Sequence */ +#define GG82563_PSCR2_1000MB_TEST_SELECT_16NS 0x000C /* 11=Select 16ns Sequence */ +#define GG82563_PSCR2_REVERSE_AUTO_NEG 0x2000 /* 1=Reverse Auto-Negotiation */ +#define GG82563_PSCR2_1000BT_DISABLE 0x4000 /* 1=Disable 1000BASE-T */ +#define GG82563_PSCR2_TRANSMITER_TYPE_MASK 0x8000 +#define GG82563_PSCR2_TRANSMITTER_TYPE_CLASS_B 0x0000 /* 0=Class B */ +#define GG82563_PSCR2_TRANSMITTER_TYPE_CLASS_A 0x8000 /* 1=Class A */ + +/* MAC Specific Control Register (Page 2, Register 21) */ +/* Tx clock speed for Link Down and 1000BASE-T for the following speeds */ +#define GG82563_MSCR_TX_CLK_MASK 0x0007 +#define GG82563_MSCR_TX_CLK_10MBPS_2_5MHZ 0x0004 +#define GG82563_MSCR_TX_CLK_100MBPS_25MHZ 0x0005 +#define GG82563_MSCR_TX_CLK_1000MBPS_2_5MHZ 0x0006 +#define GG82563_MSCR_TX_CLK_1000MBPS_25MHZ 0x0007 + +#define GG82563_MSCR_ASSERT_CRS_ON_TX 0x0010 /* 1=Assert */ + +/* DSP Distance Register (Page 5, Register 26) */ +#define GG82563_DSPD_CABLE_LENGTH 0x0007 /* 0 = <50M; + 1 = 50-80M; + 2 = 80-110M; + 3 = 110-140M; + 4 = >140M */ + +/* Kumeran Mode Control Register (Page 193, Register 16) */ +#define GG82563_KMCR_PHY_LEDS_EN 0x0020 /* 1=PHY LEDs, 0=Kumeran Inband LEDs */ +#define GG82563_KMCR_FORCE_LINK_UP 0x0040 /* 1=Force Link Up */ +#define GG82563_KMCR_SUPPRESS_SGMII_EPD_EXT 0x0080 +#define GG82563_KMCR_MDIO_BUS_SPEED_SELECT_MASK 0x0400 +#define GG82563_KMCR_MDIO_BUS_SPEED_SELECT 0x0400 /* 1=6.25MHz, 0=0.8MHz */ +#define GG82563_KMCR_PASS_FALSE_CARRIER 0x0800 + +/* Power Management Control Register (Page 193, Register 20) */ +#define GG82563_PMCR_ENABLE_ELECTRICAL_IDLE 0x0001 /* 1=Enalbe SERDES Electrical Idle */ +#define GG82563_PMCR_DISABLE_PORT 0x0002 /* 1=Disable Port */ +#define GG82563_PMCR_DISABLE_SERDES 0x0004 /* 1=Disable SERDES */ +#define GG82563_PMCR_REVERSE_AUTO_NEG 0x0008 /* 1=Enable Reverse Auto-Negotiation */ +#define GG82563_PMCR_DISABLE_1000_NON_D0 0x0010 /* 1=Disable 1000Mbps Auto-Neg in non D0 */ +#define GG82563_PMCR_DISABLE_1000 0x0020 /* 1=Disable 1000Mbps Auto-Neg Always */ +#define GG82563_PMCR_REVERSE_AUTO_NEG_D0A 0x0040 /* 1=Enable D0a Reverse Auto-Negotiation */ +#define GG82563_PMCR_FORCE_POWER_STATE 0x0080 /* 1=Force Power State */ +#define GG82563_PMCR_PROGRAMMED_POWER_STATE_MASK 0x0300 +#define GG82563_PMCR_PROGRAMMED_POWER_STATE_DR 0x0000 /* 00=Dr */ +#define GG82563_PMCR_PROGRAMMED_POWER_STATE_D0U 0x0100 /* 01=D0u */ +#define GG82563_PMCR_PROGRAMMED_POWER_STATE_D0A 0x0200 /* 10=D0a */ +#define GG82563_PMCR_PROGRAMMED_POWER_STATE_D3 0x0300 /* 11=D3 */ + +/* In-Band Control Register (Page 194, Register 18) */ +#define GG82563_ICR_DIS_PADDING 0x0010 /* Disable Padding Use */ + + +/* Bit definitions for valid PHY IDs. */ +/* I = Integrated + * E = External + */ +#define M88_VENDOR 0x0141 +#define M88E1000_E_PHY_ID 0x01410C50 +#define M88E1000_I_PHY_ID 0x01410C30 +#define M88E1011_I_PHY_ID 0x01410C20 +#define IGP01E1000_I_PHY_ID 0x02A80380 +#define M88E1000_12_PHY_ID M88E1000_E_PHY_ID +#define M88E1000_14_PHY_ID M88E1000_E_PHY_ID +#define M88E1011_I_REV_4 0x04 +#define M88E1111_I_PHY_ID 0x01410CC0 +#define L1LXT971A_PHY_ID 0x001378E0 +#define GG82563_E_PHY_ID 0x01410CA0 + + +/* Bits... + * 15-5: page + * 4-0: register offset + */ +#define PHY_PAGE_SHIFT 5 +#define PHY_REG(page, reg) \ + (((page) << PHY_PAGE_SHIFT) | ((reg) & MAX_PHY_REG_ADDRESS)) + +#define IGP3_PHY_PORT_CTRL \ + PHY_REG(769, 17) /* Port General Configuration */ +#define IGP3_PHY_RATE_ADAPT_CTRL \ + PHY_REG(769, 25) /* Rate Adapter Control Register */ + +#define IGP3_KMRN_FIFO_CTRL_STATS \ + PHY_REG(770, 16) /* KMRN FIFO's control/status register */ +#define IGP3_KMRN_POWER_MNG_CTRL \ + PHY_REG(770, 17) /* KMRN Power Management Control Register */ +#define IGP3_KMRN_INBAND_CTRL \ + PHY_REG(770, 18) /* KMRN Inband Control Register */ +#define IGP3_KMRN_DIAG \ + PHY_REG(770, 19) /* KMRN Diagnostic register */ +#define IGP3_KMRN_DIAG_PCS_LOCK_LOSS 0x0002 /* RX PCS is not synced */ +#define IGP3_KMRN_ACK_TIMEOUT \ + PHY_REG(770, 20) /* KMRN Acknowledge Timeouts register */ + +#define IGP3_VR_CTRL \ + PHY_REG(776, 18) /* Voltage regulator control register */ +#define IGP3_VR_CTRL_MODE_SHUT 0x0200 /* Enter powerdown, shutdown VRs */ +#define IGP3_VR_CTRL_MODE_MASK 0x0300 /* Shutdown VR Mask */ + +#define IGP3_CAPABILITY \ + PHY_REG(776, 19) /* IGP3 Capability Register */ + +/* Capabilities for SKU Control */ +#define IGP3_CAP_INITIATE_TEAM 0x0001 /* Able to initiate a team */ +#define IGP3_CAP_WFM 0x0002 /* Support WoL and PXE */ +#define IGP3_CAP_ASF 0x0004 /* Support ASF */ +#define IGP3_CAP_LPLU 0x0008 /* Support Low Power Link Up */ +#define IGP3_CAP_DC_AUTO_SPEED 0x0010 /* Support AC/DC Auto Link Speed */ +#define IGP3_CAP_SPD 0x0020 /* Support Smart Power Down */ +#define IGP3_CAP_MULT_QUEUE 0x0040 /* Support 2 tx & 2 rx queues */ +#define IGP3_CAP_RSS 0x0080 /* Support RSS */ +#define IGP3_CAP_8021PQ 0x0100 /* Support 802.1Q & 802.1p */ +#define IGP3_CAP_AMT_CB 0x0200 /* Support active manageability and circuit breaker */ + +#define IGP3_PPC_JORDAN_EN 0x0001 +#define IGP3_PPC_JORDAN_GIGA_SPEED 0x0002 + +#define IGP3_KMRN_PMC_EE_IDLE_LINK_DIS 0x0001 +#define IGP3_KMRN_PMC_K0S_ENTRY_LATENCY_MASK 0x001E +#define IGP3_KMRN_PMC_K0S_MODE1_EN_GIGA 0x0020 +#define IGP3_KMRN_PMC_K0S_MODE1_EN_100 0x0040 + +#define IGP3E1000_PHY_MISC_CTRL 0x1B /* Misc. Ctrl register */ +#define IGP3_PHY_MISC_DUPLEX_MANUAL_SET 0x1000 /* Duplex Manual Set */ + +#define IGP3_KMRN_EXT_CTRL PHY_REG(770, 18) +#define IGP3_KMRN_EC_DIS_INBAND 0x0080 + +#define IGP03E1000_E_PHY_ID 0x02A80390 +#define IFE_E_PHY_ID 0x02A80330 /* 10/100 PHY */ +#define IFE_PLUS_E_PHY_ID 0x02A80320 +#define IFE_C_E_PHY_ID 0x02A80310 + +#define IFE_PHY_EXTENDED_STATUS_CONTROL 0x10 /* 100BaseTx Extended Status, Control and Address */ +#define IFE_PHY_SPECIAL_CONTROL 0x11 /* 100BaseTx PHY special control register */ +#define IFE_PHY_RCV_FALSE_CARRIER 0x13 /* 100BaseTx Receive False Carrier Counter */ +#define IFE_PHY_RCV_DISCONNECT 0x14 /* 100BaseTx Receive Disconnet Counter */ +#define IFE_PHY_RCV_ERROT_FRAME 0x15 /* 100BaseTx Receive Error Frame Counter */ +#define IFE_PHY_RCV_SYMBOL_ERR 0x16 /* Receive Symbol Error Counter */ +#define IFE_PHY_PREM_EOF_ERR 0x17 /* 100BaseTx Receive Premature End Of Frame Error Counter */ +#define IFE_PHY_RCV_EOF_ERR 0x18 /* 10BaseT Receive End Of Frame Error Counter */ +#define IFE_PHY_TX_JABBER_DETECT 0x19 /* 10BaseT Transmit Jabber Detect Counter */ +#define IFE_PHY_EQUALIZER 0x1A /* PHY Equalizer Control and Status */ +#define IFE_PHY_SPECIAL_CONTROL_LED 0x1B /* PHY special control and LED configuration */ +#define IFE_PHY_MDIX_CONTROL 0x1C /* MDI/MDI-X Control register */ +#define IFE_PHY_HWI_CONTROL 0x1D /* Hardware Integrity Control (HWI) */ + +#define IFE_PESC_REDUCED_POWER_DOWN_DISABLE 0x2000 /* Defaut 1 = Disable auto reduced power down */ +#define IFE_PESC_100BTX_POWER_DOWN 0x0400 /* Indicates the power state of 100BASE-TX */ +#define IFE_PESC_10BTX_POWER_DOWN 0x0200 /* Indicates the power state of 10BASE-T */ +#define IFE_PESC_POLARITY_REVERSED 0x0100 /* Indicates 10BASE-T polarity */ +#define IFE_PESC_PHY_ADDR_MASK 0x007C /* Bit 6:2 for sampled PHY address */ +#define IFE_PESC_SPEED 0x0002 /* Auto-negotiation speed result 1=100Mbs, 0=10Mbs */ +#define IFE_PESC_DUPLEX 0x0001 /* Auto-negotiation duplex result 1=Full, 0=Half */ +#define IFE_PESC_POLARITY_REVERSED_SHIFT 8 + +#define IFE_PSC_DISABLE_DYNAMIC_POWER_DOWN 0x0100 /* 1 = Dyanmic Power Down disabled */ +#define IFE_PSC_FORCE_POLARITY 0x0020 /* 1=Reversed Polarity, 0=Normal */ +#define IFE_PSC_AUTO_POLARITY_DISABLE 0x0010 /* 1=Auto Polarity Disabled, 0=Enabled */ +#define IFE_PSC_JABBER_FUNC_DISABLE 0x0001 /* 1=Jabber Disabled, 0=Normal Jabber Operation */ +#define IFE_PSC_FORCE_POLARITY_SHIFT 5 +#define IFE_PSC_AUTO_POLARITY_DISABLE_SHIFT 4 + +#define IFE_PMC_AUTO_MDIX 0x0080 /* 1=enable MDI/MDI-X feature, default 0=disabled */ +#define IFE_PMC_FORCE_MDIX 0x0040 /* 1=force MDIX-X, 0=force MDI */ +#define IFE_PMC_MDIX_STATUS 0x0020 /* 1=MDI-X, 0=MDI */ +#define IFE_PMC_AUTO_MDIX_COMPLETE 0x0010 /* Resolution algorithm is completed */ +#define IFE_PMC_MDIX_MODE_SHIFT 6 +#define IFE_PHC_MDIX_RESET_ALL_MASK 0x0000 /* Disable auto MDI-X */ + +#define IFE_PHC_HWI_ENABLE 0x8000 /* Enable the HWI feature */ +#define IFE_PHC_ABILITY_CHECK 0x4000 /* 1= Test Passed, 0=failed */ +#define IFE_PHC_TEST_EXEC 0x2000 /* PHY launch test pulses on the wire */ +#define IFE_PHC_HIGHZ 0x0200 /* 1 = Open Circuit */ +#define IFE_PHC_LOWZ 0x0400 /* 1 = Short Circuit */ +#define IFE_PHC_LOW_HIGH_Z_MASK 0x0600 /* Mask for indication type of problem on the line */ +#define IFE_PHC_DISTANCE_MASK 0x01FF /* Mask for distance to the cable problem, in 80cm granularity */ +#define IFE_PHC_RESET_ALL_MASK 0x0000 /* Disable HWI */ +#define IFE_PSCL_PROBE_MODE 0x0020 /* LED Probe mode */ +#define IFE_PSCL_PROBE_LEDS_OFF 0x0006 /* Force LEDs 0 and 2 off */ +#define IFE_PSCL_PROBE_LEDS_ON 0x0007 /* Force LEDs 0 and 2 on */ + +#define ICH_FLASH_COMMAND_TIMEOUT 5000 /* 5000 uSecs - adjusted */ +#define ICH_FLASH_ERASE_TIMEOUT 3000000 /* Up to 3 seconds - worst case */ +#define ICH_FLASH_CYCLE_REPEAT_COUNT 10 /* 10 cycles */ +#define ICH_FLASH_SEG_SIZE_256 256 +#define ICH_FLASH_SEG_SIZE_4K 4096 +#define ICH_FLASH_SEG_SIZE_64K 65536 + +#define ICH_CYCLE_READ 0x0 +#define ICH_CYCLE_RESERVED 0x1 +#define ICH_CYCLE_WRITE 0x2 +#define ICH_CYCLE_ERASE 0x3 + +#define ICH_FLASH_GFPREG 0x0000 +#define ICH_FLASH_HSFSTS 0x0004 +#define ICH_FLASH_HSFCTL 0x0006 +#define ICH_FLASH_FADDR 0x0008 +#define ICH_FLASH_FDATA0 0x0010 +#define ICH_FLASH_FRACC 0x0050 +#define ICH_FLASH_FREG0 0x0054 +#define ICH_FLASH_FREG1 0x0058 +#define ICH_FLASH_FREG2 0x005C +#define ICH_FLASH_FREG3 0x0060 +#define ICH_FLASH_FPR0 0x0074 +#define ICH_FLASH_FPR1 0x0078 +#define ICH_FLASH_SSFSTS 0x0090 +#define ICH_FLASH_SSFCTL 0x0092 +#define ICH_FLASH_PREOP 0x0094 +#define ICH_FLASH_OPTYPE 0x0096 +#define ICH_FLASH_OPMENU 0x0098 + +#define ICH_FLASH_REG_MAPSIZE 0x00A0 +#define ICH_FLASH_SECTOR_SIZE 4096 +#define ICH_GFPREG_BASE_MASK 0x1FFF +#define ICH_FLASH_LINEAR_ADDR_MASK 0x00FFFFFF + +/* ICH8 GbE Flash Hardware Sequencing Flash Status Register bit breakdown */ +/* Offset 04h HSFSTS */ +union ich8_hws_flash_status { + struct ich8_hsfsts { +#ifdef E1000_BIG_ENDIAN + uint16_t reserved2 :6; + uint16_t fldesvalid :1; + uint16_t flockdn :1; + uint16_t flcdone :1; + uint16_t flcerr :1; + uint16_t dael :1; + uint16_t berasesz :2; + uint16_t flcinprog :1; + uint16_t reserved1 :2; +#else + uint16_t flcdone :1; /* bit 0 Flash Cycle Done */ + uint16_t flcerr :1; /* bit 1 Flash Cycle Error */ + uint16_t dael :1; /* bit 2 Direct Access error Log */ + uint16_t berasesz :2; /* bit 4:3 Block/Sector Erase Size */ + uint16_t flcinprog :1; /* bit 5 flash SPI cycle in Progress */ + uint16_t reserved1 :2; /* bit 13:6 Reserved */ + uint16_t reserved2 :6; /* bit 13:6 Reserved */ + uint16_t fldesvalid :1; /* bit 14 Flash Descriptor Valid */ + uint16_t flockdn :1; /* bit 15 Flash Configuration Lock-Down */ +#endif + } hsf_status; + uint16_t regval; +}; + +/* ICH8 GbE Flash Hardware Sequencing Flash control Register bit breakdown */ +/* Offset 06h FLCTL */ +union ich8_hws_flash_ctrl { + struct ich8_hsflctl { +#ifdef E1000_BIG_ENDIAN + uint16_t fldbcount :2; + uint16_t flockdn :6; + uint16_t flcgo :1; + uint16_t flcycle :2; + uint16_t reserved :5; +#else + uint16_t flcgo :1; /* 0 Flash Cycle Go */ + uint16_t flcycle :2; /* 2:1 Flash Cycle */ + uint16_t reserved :5; /* 7:3 Reserved */ + uint16_t fldbcount :2; /* 9:8 Flash Data Byte Count */ + uint16_t flockdn :6; /* 15:10 Reserved */ +#endif + } hsf_ctrl; + uint16_t regval; +}; + +/* ICH8 Flash Region Access Permissions */ +union ich8_hws_flash_regacc { + struct ich8_flracc { +#ifdef E1000_BIG_ENDIAN + uint32_t gmwag :8; + uint32_t gmrag :8; + uint32_t grwa :8; + uint32_t grra :8; +#else + uint32_t grra :8; /* 0:7 GbE region Read Access */ + uint32_t grwa :8; /* 8:15 GbE region Write Access */ + uint32_t gmrag :8; /* 23:16 GbE Master Read Access Grant */ + uint32_t gmwag :8; /* 31:24 GbE Master Write Access Grant */ +#endif + } hsf_flregacc; + uint16_t regval; +}; + +/* Miscellaneous PHY bit definitions. */ +#define PHY_PREAMBLE 0xFFFFFFFF +#define PHY_SOF 0x01 +#define PHY_OP_READ 0x02 +#define PHY_OP_WRITE 0x01 +#define PHY_TURNAROUND 0x02 +#define PHY_PREAMBLE_SIZE 32 +#define MII_CR_SPEED_1000 0x0040 +#define MII_CR_SPEED_100 0x2000 +#define MII_CR_SPEED_10 0x0000 +#define E1000_PHY_ADDRESS 0x01 +#define PHY_AUTO_NEG_TIME 45 /* 4.5 Seconds */ +#define PHY_FORCE_TIME 20 /* 2.0 Seconds */ +#define PHY_REVISION_MASK 0xFFFFFFF0 +#define DEVICE_SPEED_MASK 0x00000300 /* Device Ctrl Reg Speed Mask */ +#define REG4_SPEED_MASK 0x01E0 +#define REG9_SPEED_MASK 0x0300 +#define ADVERTISE_10_HALF 0x0001 +#define ADVERTISE_10_FULL 0x0002 +#define ADVERTISE_100_HALF 0x0004 +#define ADVERTISE_100_FULL 0x0008 +#define ADVERTISE_1000_HALF 0x0010 +#define ADVERTISE_1000_FULL 0x0020 +#define AUTONEG_ADVERTISE_SPEED_DEFAULT 0x002F /* Everything but 1000-Half */ +#define AUTONEG_ADVERTISE_10_100_ALL 0x000F /* All 10/100 speeds*/ +#define AUTONEG_ADVERTISE_10_ALL 0x0003 /* 10Mbps Full & Half speeds*/ + +#endif /* _E1000_HW_H_ */ + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/e1000/e1000_osdep.h b/gpxe/src/drivers/net/e1000/e1000_osdep.h new file mode 100644 index 0000000..c2d9eb9 --- /dev/null +++ b/gpxe/src/drivers/net/e1000/e1000_osdep.h @@ -0,0 +1,142 @@ +/******************************************************************************* + + Intel PRO/1000 Linux driver + Copyright(c) 1999 - 2006 Intel Corporation. + + This program is free software; you can redistribute it and/or modify it + under the terms and conditions of the GNU General Public License, + version 2, as published by the Free Software Foundation. + + This program is distributed in the hope it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". + + Contact Information: + Linux NICS + e1000-devel Mailing List + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + + +/* glue for the OS independent part of e1000 + * includes register access macros + */ + +#ifndef _E1000_OSDEP_H_ +#define _E1000_OSDEP_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum { +#undef FALSE + FALSE = 0, +#undef TRUE + TRUE = 1 +} boolean_t; + +/* Debugging #defines */ + +#if 1 +#define DEBUGFUNC(F) DBG(F "\n") +#else +#define DEBUGFUNC(F) +#endif + +#if 1 +#define DEBUGOUT(S) DBG(S) +#define DEBUGOUT1(S, A...) DBG(S, A) +#else +#define DEBUGOUT(S) +#define DEBUGOUT1(S, A...) +#endif + +#define DEBUGOUT2 DEBUGOUT1 +#define DEBUGOUT3 DEBUGOUT1 +#define DEBUGOUT7 DEBUGOUT1 + +#define E1000_WRITE_REG(a, reg, value) \ + writel((value), ((a)->hw_addr + \ + (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg))) + +#define E1000_READ_REG(a, reg) \ + readl((a)->hw_addr + \ + (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg)) + +#define E1000_WRITE_REG_ARRAY(a, reg, offset, value) \ + writel((value), ((a)->hw_addr + \ + (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg) + \ + ((offset) << 2))) + +#define E1000_READ_REG_ARRAY(a, reg, offset) \ + readl((a)->hw_addr + \ + (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg) + \ + ((offset) << 2)) + +#define E1000_READ_REG_ARRAY_DWORD E1000_READ_REG_ARRAY +#define E1000_WRITE_REG_ARRAY_DWORD E1000_WRITE_REG_ARRAY + +#define E1000_WRITE_REG_ARRAY_WORD(a, reg, offset, value) \ + writew((value), ((a)->hw_addr + \ + (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg) + \ + ((offset) << 1))) + +#define E1000_READ_REG_ARRAY_WORD(a, reg, offset) \ + readw((a)->hw_addr + \ + (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg) + \ + ((offset) << 1)) + +#define E1000_WRITE_REG_ARRAY_BYTE(a, reg, offset, value) \ + writeb((value), ((a)->hw_addr + \ + (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg) + \ + (offset))) + +#define E1000_READ_REG_ARRAY_BYTE(a, reg, offset) \ + readb((a)->hw_addr + \ + (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg) + \ + (offset)) + +#define E1000_WRITE_FLUSH(a) E1000_READ_REG(a, STATUS) + +#define E1000_WRITE_ICH_FLASH_REG(a, reg, value) \ + writel((value), ((a)->flash_address + reg)) + +#define E1000_READ_ICH_FLASH_REG(a, reg) \ + readl((a)->flash_address + reg) + +#define E1000_WRITE_ICH_FLASH_REG16(a, reg, value) \ + writew((value), ((a)->flash_address + reg)) + +#define E1000_READ_ICH_FLASH_REG16(a, reg) \ + readw((a)->flash_address + reg) + +#define msleep(n) mdelay(n) + +#endif /* _E1000_OSDEP_H_ */ + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/eepro.c b/gpxe/src/drivers/net/eepro.c new file mode 100644 index 0000000..2a163d1 --- /dev/null +++ b/gpxe/src/drivers/net/eepro.c @@ -0,0 +1,635 @@ +#ifdef ALLMULTI +#error multicast support is not yet implemented +#endif +/************************************************************************** +Etherboot - BOOTP/TFTP Bootstrap Program +Intel EEPRO/10 NIC driver for Etherboot +Adapted from Linux eepro.c from kernel 2.2.17 + +This board accepts a 32 pin EEPROM (29C256), however a test with a +27C010 shows that this EPROM also works in the socket, but it's not clear +how repeatably. The two top address pins appear to be held low, thus +the bottom 32kB of the 27C010 is visible in the CPU's address space. +To be sure you could put 4 copies of the code in the 27C010, then +it doesn't matter whether the extra lines are held low or high, just +hopefully not floating as CMOS chips don't like floating inputs. + +Be careful with seating the EPROM as the socket on my board actually +has 34 pins, the top row of 2 are not used. +***************************************************************************/ + +/* + + timlegge 2005-05-18 remove the relocation changes cards that + write directly to the hardware don't need it +*/ + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, or (at + * your option) any later version. + */ + +#include "etherboot.h" +#include +#include "nic.h" +#include +#include + +/* 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; data sheet asks for 250us delay */ +#define eepro_full_reset(ioaddr) outb(RESET_CMD, ioaddr); udelay(255); + +/* do a nice reset */ +#define eepro_sel_reset(ioaddr) \ + do { \ + outb ( SEL_RESET_CMD, ioaddr ); \ + (void) SLOW_DOWN; \ + (void) SLOW_DOWN; \ + } while (0) + +/* clear all interrupts */ +#define eepro_clear_int(ioaddr) outb(ALL_MASK, ioaddr + STATUS_REG) + +/* enable rx */ +#define eepro_en_rx(ioaddr) outb(RCV_ENABLE_CMD, ioaddr) + +/* disable rx */ +#define eepro_dis_rx(ioaddr) outb(RCV_DISABLE_CMD, ioaddr) + +/* switch bank */ +#define eepro_sw2bank0(ioaddr) outb(BANK0_SELECT, ioaddr) +#define eepro_sw2bank1(ioaddr) outb(BANK1_SELECT, ioaddr) +#define eepro_sw2bank2(ioaddr) outb(BANK2_SELECT, ioaddr) + +static unsigned int rx_start, tx_start; +static int tx_last; +static unsigned int tx_end; +static int eepro = 0; +static unsigned int mem_start, mem_end = RCV_DEFAULT_RAM / 1024; + +/************************************************************************** +RESET - Reset adapter +***************************************************************************/ +static void eepro_reset(struct nic *nic) +{ + int temp_reg, i; + + /* put the card in its initial state */ + eepro_sw2bank2(nic->ioaddr); /* be careful, bank2 now */ + temp_reg = inb(nic->ioaddr + eeprom_reg); + DBG("Stepping %d\n", temp_reg >> 5); + if (temp_reg & 0x10) /* check the TurnOff Enable bit */ + outb(temp_reg & 0xEF, nic->ioaddr + eeprom_reg); + for (i = 0; i < ETH_ALEN; i++) /* fill the MAC address */ + outb(nic->node_addr[i], nic->ioaddr + I_ADD_REG0 + i); + temp_reg = inb(nic->ioaddr + REG1); + /* setup Transmit Chaining and discard bad RCV frames */ + outb(temp_reg | XMT_Chain_Int | XMT_Chain_ErrStop + | RCV_Discard_BadFrame, nic->ioaddr + REG1); + temp_reg = inb(nic->ioaddr + REG2); /* match broadcast */ + outb(temp_reg | 0x14, nic->ioaddr + REG2); + temp_reg = inb(nic->ioaddr + REG3); + outb(temp_reg & 0x3F, nic->ioaddr + REG3); /* clear test mode */ + /* set the receiving mode */ + eepro_sw2bank1(nic->ioaddr); /* be careful, bank1 now */ + /* initialise the RCV and XMT upper and lower limits */ + outb(RCV_LOWER_LIMIT, nic->ioaddr + RCV_LOWER_LIMIT_REG); + outb(RCV_UPPER_LIMIT, nic->ioaddr + RCV_UPPER_LIMIT_REG); + outb(XMT_LOWER_LIMIT, nic->ioaddr + xmt_lower_limit_reg); + outb(XMT_UPPER_LIMIT, nic->ioaddr + xmt_upper_limit_reg); + eepro_sw2bank0(nic->ioaddr); /* Switch back to bank 0 */ + eepro_clear_int(nic->ioaddr); + /* Initialise RCV */ + outw(rx_start = (RCV_LOWER_LIMIT << 8), nic->ioaddr + RCV_BAR); + outw(((RCV_UPPER_LIMIT << 8) | 0xFE), nic->ioaddr + RCV_STOP); + /* Make sure 1st poll won't find a valid packet header */ + outw((RCV_LOWER_LIMIT << 8), nic->ioaddr + HOST_ADDRESS_REG); + outw(0, nic->ioaddr + IO_PORT); + /* Intialise XMT */ + outw((XMT_LOWER_LIMIT << 8), nic->ioaddr + xmt_bar); + eepro_sel_reset(nic->ioaddr); + tx_start = tx_end = (unsigned int) (XMT_LOWER_LIMIT << 8); + tx_last = 0; + eepro_en_rx(nic->ioaddr); +} + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int eepro_poll(struct nic *nic, int retrieve) +{ + unsigned int rcv_car = rx_start; + unsigned int rcv_event, rcv_status, rcv_next_frame, rcv_size; + + /* return true if there's an ethernet packet ready to read */ + /* nic->packet should contain data on return */ + /* nic->packetlen should contain length of data */ +#if 0 + if ((inb(nic->ioaddr + STATUS_REG) & 0x40) == 0) + return (0); + outb(0x40, nic->ioaddr + STATUS_REG); +#endif + outw(rcv_car, nic->ioaddr + HOST_ADDRESS_REG); + rcv_event = inw(nic->ioaddr + IO_PORT); + if (rcv_event != RCV_DONE) + return (0); + + /* FIXME: I'm guessing this might not work with this card, since + it looks like once a rcv_event is started it must be completed. + maybe there's another way. */ + if ( ! retrieve ) return 1; + + rcv_status = inw(nic->ioaddr + IO_PORT); + rcv_next_frame = inw(nic->ioaddr + IO_PORT); + rcv_size = inw(nic->ioaddr + IO_PORT); +#if 0 + printf("%hX %hX %d %hhX\n", rcv_status, rcv_next_frame, rcv_size, + inb(nic->ioaddr + STATUS_REG)); +#endif + if ((rcv_status & (RX_OK|RX_ERROR)) != RX_OK) { + printf("Receive error %hX\n", rcv_status); + return (0); + } + rcv_size &= 0x3FFF; + insw(nic->ioaddr + IO_PORT, nic->packet, ((rcv_size + 3) >> 1)); +#if 0 +{ + int i; + for (i = 0; i < 48; i++) { + printf("%hhX", nic->packet[i]); + putchar(i % 16 == 15 ? '\n' : ' '); + } +} +#endif + nic->packetlen = rcv_size; + rcv_car = (rx_start + RCV_HEADER + rcv_size); + rx_start = rcv_next_frame; +/* + hex_dump(rcv_car, nic->packetlen); +*/ + + if (rcv_car == 0) + rcv_car = ((RCV_UPPER_LIMIT << 8) | 0xff); + outw(rcv_car - 1, nic->ioaddr + RCV_STOP); + return (1); +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static void eepro_transmit( + struct nic *nic, + const char *d, /* Destination */ + unsigned int t, /* Type */ + unsigned int s, /* size */ + const char *p) /* Packet */ +{ + unsigned int status, tx_available, last, end, length; + unsigned short type; + int boguscount = 20; + + length = s + ETH_HLEN; + if (tx_end > tx_start) + tx_available = XMT_RAM - (tx_end - tx_start); + else if (tx_end < tx_start) + tx_available = tx_start - tx_end; + else + tx_available = XMT_RAM; + last = tx_end; + end = last + (((length + 3) >> 1) << 1) + XMT_HEADER; + if (end >= (XMT_UPPER_LIMIT << 8)) { + last = (XMT_LOWER_LIMIT << 8); + end = last + (((length + 3) >> 1) << 1) + XMT_HEADER; + } + outw(last, nic->ioaddr + HOST_ADDRESS_REG); + outw(XMT_CMD, nic->ioaddr + IO_PORT); + outw(0, nic->ioaddr + IO_PORT); + outw(end, nic->ioaddr + IO_PORT); + outw(length, nic->ioaddr + IO_PORT); + outsw(nic->ioaddr + IO_PORT, d, ETH_ALEN / 2); + outsw(nic->ioaddr + IO_PORT, nic->node_addr, ETH_ALEN / 2); + type = htons(t); + outsw(nic->ioaddr + IO_PORT, &type, sizeof(type) / 2); + outsw(nic->ioaddr + IO_PORT, p, (s + 3) >> 1); + /* A dummy read to flush the DRAM write pipeline */ + status = inw(nic->ioaddr + IO_PORT); + outw(last, nic->ioaddr + xmt_bar); + outb(XMT_CMD, nic->ioaddr); + tx_start = last; + tx_last = last; + tx_end = end; +#if 0 + printf("%d %d\n", tx_start, tx_end); +#endif + while (boguscount > 0) { + if (((status = inw(nic->ioaddr + IO_PORT)) & TX_DONE_BIT) == 0) { + udelay(40); + boguscount--; + continue; + } + if ((status & 0x2000) == 0) { + DBG("Transmit status %hX\n", status); + } + } +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void eepro_disable ( struct nic *nic, struct isa_device *isa __unused ) { + eepro_sw2bank0(nic->ioaddr); /* Switch to bank 0 */ + /* Flush the Tx and disable Rx */ + outb(STOP_RCV_CMD, nic->ioaddr); + tx_start = tx_end = (XMT_LOWER_LIMIT << 8); + tx_last = 0; + /* Reset the 82595 */ + eepro_full_reset(nic->ioaddr); +} + +/************************************************************************** +DISABLE - Enable, Disable, or Force interrupts +***************************************************************************/ +static void eepro_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + +static int read_eeprom(uint16_t ioaddr, int location) +{ + int i; + unsigned short retval = 0; + int ee_addr = ioaddr + eeprom_reg; + int read_cmd = location | EE_READ_CMD; + int ctrl_val = EECS; + + if (eepro == LAN595FX_10ISA) { + eepro_sw2bank1(ioaddr); + outb(0x00, ioaddr + STATUS_REG); + } + eepro_sw2bank2(ioaddr); + outb(ctrl_val, ee_addr); + /* shift the read command bits out */ + for (i = 8; i >= 0; i--) { + short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI : ctrl_val; + outb(outval, ee_addr); + outb(outval | EESK, ee_addr); /* EEPROM clock tick */ + eeprom_delay(); + outb(outval, ee_addr); /* finish EEPROM clock tick */ + eeprom_delay(); + } + outb(ctrl_val, ee_addr); + for (i = 16; i > 0; i--) { + outb(ctrl_val | EESK, ee_addr); + eeprom_delay(); + retval = (retval << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0); + outb(ctrl_val, ee_addr); + eeprom_delay(); + } + /* terminate the EEPROM access */ + ctrl_val &= ~EECS; + outb(ctrl_val | EESK, ee_addr); + eeprom_delay(); + outb(ctrl_val, ee_addr); + eeprom_delay(); + eepro_sw2bank0(ioaddr); + return (retval); +} + +static int eepro_probe1 ( isa_probe_addr_t ioaddr ) { + int id, counter; + + id = inb(ioaddr + ID_REG); + if ((id & ID_REG_MASK) != ID_REG_SIG) + return (0); + counter = id & R_ROBIN_BITS; + if (((id = inb(ioaddr + ID_REG)) & R_ROBIN_BITS) != (counter + 0x40)) + return (0); + /* yes the 82595 has been found */ + return (1); +} + +static struct nic_operations eepro_operations = { + .connect = dummy_connect, + .poll = eepro_poll, + .transmit = eepro_transmit, + .irq = eepro_irq, + +}; + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ +static int eepro_probe ( struct nic *nic, struct isa_device *isa ) { + + int i, l_eepro = 0; + union { + unsigned char caddr[ETH_ALEN]; + unsigned short saddr[ETH_ALEN/2]; + } station_addr; + const char *name; + + nic->irqno = 0; + nic->ioaddr = isa->ioaddr; + + station_addr.saddr[2] = read_eeprom(nic->ioaddr,2); + if ( ( station_addr.saddr[2] == 0x0000 ) || + ( station_addr.saddr[2] == 0xFFFF ) ) { + l_eepro = 3; + eepro = LAN595FX_10ISA; + eeprom_reg= EEPROM_REG_10; + rcv_start = RCV_START_10; + xmt_lower_limit_reg = XMT_LOWER_LIMIT_REG_10; + xmt_upper_limit_reg = XMT_UPPER_LIMIT_REG_10; + station_addr.saddr[2] = read_eeprom(nic->ioaddr,2); + } + station_addr.saddr[1] = read_eeprom(nic->ioaddr,3); + station_addr.saddr[0] = read_eeprom(nic->ioaddr,4); + if (l_eepro) + name = "Intel EtherExpress 10 ISA"; + else if (read_eeprom(nic->ioaddr,7) == ee_FX_INT2IRQ) { + name = "Intel EtherExpress Pro/10+ ISA"; + l_eepro = 2; + } else if (station_addr.saddr[0] == SA_ADDR1) { + name = "Intel EtherExpress Pro/10 ISA"; + l_eepro = 1; + } else { + l_eepro = 0; + name = "Intel 82595-based LAN card"; + } + station_addr.saddr[0] = swap16(station_addr.saddr[0]); + station_addr.saddr[1] = swap16(station_addr.saddr[1]); + station_addr.saddr[2] = swap16(station_addr.saddr[2]); + for (i = 0; i < ETH_ALEN; i++) { + nic->node_addr[i] = station_addr.caddr[i]; + } + + DBG ( "%s ioaddr %#hX, addr %s", name, nic->ioaddr, eth_ntoa ( nic->node_addr ) ); + + mem_start = RCV_LOWER_LIMIT << 8; + if ((mem_end & 0x3F) < 3 || (mem_end & 0x3F) > 29) + mem_end = RCV_UPPER_LIMIT << 8; + else { + mem_end = mem_end * 1024 + (RCV_LOWER_LIMIT << 8); + rcv_ram = mem_end - (RCV_LOWER_LIMIT << 8); + } + printf(", Rx mem %dK, if %s\n", (mem_end - mem_start) >> 10, + GetBit(read_eeprom(nic->ioaddr,5), ee_BNC_TPE) ? "BNC" : "TP"); + + eepro_reset(nic); + + /* point to NIC specific routines */ + nic->nic_op = &eepro_operations; + return 1; +} + +static isa_probe_addr_t eepro_probe_addrs[] = { + 0x300, 0x210, 0x240, 0x280, 0x2C0, 0x200, 0x320, 0x340, 0x360, +}; + +ISA_DRIVER ( eepro_driver, eepro_probe_addrs, eepro_probe1, + GENERIC_ISAPNP_VENDOR, 0x828a ); + +DRIVER ( "eepro", nic_driver, isa_driver, eepro_driver, + eepro_probe, eepro_disable ); + +ISA_ROM ( "eepro", "Intel Etherexpress Pro/10" ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/eepro100.c b/gpxe/src/drivers/net/eepro100.c new file mode 100644 index 0000000..e6e7db4 --- /dev/null +++ b/gpxe/src/drivers/net/eepro100.c @@ -0,0 +1,851 @@ +/* + * eepro100.c -- This file implements the eepro100 driver for etherboot. + * + * + * Copyright (C) AW Computer Systems. + * written by R.E.Wolff -- R.E.Wolff@BitWizard.nl + * + * + * AW Computer Systems is contributing to the free software community + * by paying for this driver and then putting the result under GPL. + * + * If you need a Linux device driver, please contact BitWizard for a + * quote. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * date version by what + * Written: May 29 1997 V0.10 REW Initial revision. + * changes: May 31 1997 V0.90 REW Works! + * Jun 1 1997 V0.91 REW Cleanup + * Jun 2 1997 V0.92 REW Add some code documentation + * Jul 25 1997 V1.00 REW Tested by AW to work in a PROM + * Cleanup for publication + * Dez 11 2004 V1.10 Kiszka Add RX ring buffer support + * + * This is the etherboot intel etherexpress Pro/100B driver. + * + * It was written from scratch, with Donald Beckers eepro100.c kernel + * driver as a guideline. Mostly the 82557 related definitions and the + * lower level routines have been cut-and-pasted into this source. + * + * The driver was finished before Intel got the NDA out of the closet. + * I still don't have the docs. + * + * + * Datasheet is now published and available from + * ftp://download.intel.com/design/network/manuals/8255X_OpenSDM.pdf + * - Michael Brown + * */ + +/* Philosophy of this driver. + * + * Probing: + * + * Using the pci.c functions of the Etherboot code, the 82557 chip is detected. + * It is verified that the BIOS initialized everything properly and if + * something is missing it is done now. + * + * + * Initialization: + * + * + * The chip is then initialized to "know" its ethernet address, and to + * start recieving packets. The Linux driver has a whole transmit and + * recieve ring of buffers. This is neat if you need high performance: + * you can write the buffers asynchronously to the chip reading the + * buffers and transmitting them over the network. Performance is NOT + * an issue here. We can boot a 400k kernel in about two + * seconds. (Theory: 0.4 seconds). Booting a system is going to take + * about half a minute anyway, so getting 10 times closer to the + * theoretical limit is going to make a difference of a few percent. */ +/* Not totally true: busy networks can cause packet drops due to RX + * buffer overflows. Fixed in V1.10 of this driver. [Kiszka] */ +/* + * + * Transmitting and recieving. + * + * We have only one transmit descriptor. It has two buffer descriptors: + * one for the header, and the other for the data. + * We have multiple receive buffers (currently: 4). The chip is told to + * receive packets and suspend itself once it ran on the last free buffer. + * The recieve (poll) routine simply looks at the current recieve buffer, + * picks the packet if any, and releases this buffer again (classic ring + * buffer concept). This helps to avoid packet drops on busy networks. + * + * Caveats: + * + * The Etherboot framework moves the code to the 48k segment from + * 0x94000 to 0xa0000. There is just a little room between the end of + * this driver and the 0xa0000 address. If you compile in too many + * features, this will overflow. + * The number under "hex" in the output of size that scrolls by while + * compiling should be less than 8000. Maybe even the stack is up there, + * so that you need even more headroom. + */ + +/* The etherboot authors seem to dislike the argument ordering in + * outb macros that Linux uses. I disklike the confusion that this + * has caused even more.... This file uses the Linux argument ordering. */ +/* Sorry not us. It's inherited code from FreeBSD. [The authors] */ + +#include "etherboot.h" +#include "nic.h" +#include +#include + +static int ioaddr; + +enum speedo_offsets { + SCBStatus = 0, SCBCmd = 2, /* Rx/Command Unit command and status. */ + SCBPointer = 4, /* General purpose pointer. */ + SCBPort = 8, /* Misc. commands and operands. */ + SCBflash = 12, SCBeeprom = 14, /* EEPROM and flash memory control. */ + SCBCtrlMDI = 16, /* MDI interface control. */ + SCBEarlyRx = 20, /* Early receive byte count. */ +}; + +enum SCBCmdBits { + SCBMaskCmdDone=0x8000, SCBMaskRxDone=0x4000, SCBMaskCmdIdle=0x2000, + SCBMaskRxSuspend=0x1000, SCBMaskEarlyRx=0x0800, SCBMaskFlowCtl=0x0400, + SCBTriggerIntr=0x0200, SCBMaskAll=0x0100, + /* The rest are Rx and Tx commands. */ + CUStart=0x0010, CUResume=0x0020, CUStatsAddr=0x0040, CUShowStats=0x0050, + CUCmdBase=0x0060, /* CU Base address (set to zero) . */ + CUDumpStats=0x0070, /* Dump then reset stats counters. */ + RxStart=0x0001, RxResume=0x0002, RxAbort=0x0004, RxAddrLoad=0x0006, + RxResumeNoResources=0x0007, +}; + +static int do_eeprom_cmd(int cmd, int cmd_len); +void hd(void *where, int n); + +/***********************************************************************/ +/* I82557 related defines */ +/***********************************************************************/ + +/* Serial EEPROM section. + A "bit" grungy, but we work our way through bit-by-bit :->. */ +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x01 /* EEPROM shift clock. */ +#define EE_CS 0x02 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ +#define EE_DATA_READ 0x08 /* EEPROM chip data out. */ +#define EE_WRITE_0 0x4802 +#define EE_WRITE_1 0x4806 +#define EE_ENB (0x4800 | EE_CS) + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_READ_CMD 6 + +/* The SCB accepts the following controls for the Tx and Rx units: */ +#define CU_START 0x0010 +#define CU_RESUME 0x0020 +#define CU_STATSADDR 0x0040 +#define CU_SHOWSTATS 0x0050 /* Dump statistics counters. */ +#define CU_CMD_BASE 0x0060 /* Base address to add to add CU commands. */ +#define CU_DUMPSTATS 0x0070 /* Dump then reset stats counters. */ + +#define RX_START 0x0001 +#define RX_RESUME 0x0002 +#define RX_ABORT 0x0004 +#define RX_ADDR_LOAD 0x0006 +#define RX_RESUMENR 0x0007 +#define INT_MASK 0x0100 +#define DRVR_INT 0x0200 /* Driver generated interrupt. */ + +enum phy_chips { NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240, + S80C24, PhyUndefined, DP83840A=10, }; + +/* Commands that can be put in a command list entry. */ +enum commands { + CmdNOp = 0, + CmdIASetup = 1, + CmdConfigure = 2, + CmdMulticastList = 3, + CmdTx = 4, + CmdTDR = 5, + CmdDump = 6, + CmdDiagnose = 7, + + /* And some extra flags: */ + CmdSuspend = 0x4000, /* Suspend after completion. */ + CmdIntr = 0x2000, /* Interrupt after completion. */ + CmdTxFlex = 0x0008, /* Use "Flexible mode" for CmdTx command. */ +}; + +/* How to wait for the command unit to accept a command. + Typically this takes 0 ticks. */ +static inline void wait_for_cmd_done(int cmd_ioaddr) +{ + int wait = 0; + int delayed_cmd; + + do + if (inb(cmd_ioaddr) == 0) return; + while(++wait <= 100); + delayed_cmd = inb(cmd_ioaddr); + do + if (inb(cmd_ioaddr) == 0) break; + while(++wait <= 10000); + printf("Command %2.2x was not immediately accepted, %d ticks!\n", + delayed_cmd, wait); +} + +/* Elements of the dump_statistics block. This block must be lword aligned. */ +static struct speedo_stats { + u32 tx_good_frames; + u32 tx_coll16_errs; + u32 tx_late_colls; + u32 tx_underruns; + u32 tx_lost_carrier; + u32 tx_deferred; + u32 tx_one_colls; + u32 tx_multi_colls; + u32 tx_total_colls; + u32 rx_good_frames; + u32 rx_crc_errs; + u32 rx_align_errs; + u32 rx_resource_errs; + u32 rx_overrun_errs; + u32 rx_colls_errs; + u32 rx_runt_errs; + u32 done_marker; +} lstats; + +/* A speedo3 TX buffer descriptor with two buffers... */ +static struct TxFD { + volatile s16 status; + s16 command; + u32 link; /* void * */ + u32 tx_desc_addr; /* (almost) Always points to the tx_buf_addr element. */ + s32 count; /* # of TBD (=2), Tx start thresh., etc. */ + /* This constitutes two "TBD" entries: hdr and data */ + u32 tx_buf_addr0; /* void *, header of frame to be transmitted. */ + s32 tx_buf_size0; /* Length of Tx hdr. */ + u32 tx_buf_addr1; /* void *, data to be transmitted. */ + s32 tx_buf_size1; /* Length of Tx data. */ +} txfd; + +struct RxFD { /* Receive frame descriptor. */ + volatile s16 status; + s16 command; + u32 link; /* struct RxFD * */ + u32 rx_buf_addr; /* void * */ + u16 count; + u16 size; + char packet[1518]; +}; + +static struct nic_operations eepro100_operations; + +#define RXFD_COUNT 4 +struct { + struct RxFD rxfds[RXFD_COUNT]; +} eepro100_bufs __shared; +#define rxfds eepro100_bufs.rxfds +static unsigned int rxfd = 0; + +static int congenb = 0; /* Enable congestion control in the DP83840. */ +static int txfifo = 8; /* Tx FIFO threshold in 4 byte units, 0-15 */ +static int rxfifo = 8; /* Rx FIFO threshold, default 32 bytes. */ +static int txdmacount = 0; /* Tx DMA burst length, 0-127, default 0. */ +static int rxdmacount = 0; /* Rx DMA length, 0 means no preemption. */ + +/* I don't understand a byte in this structure. It was copied from the + * Linux kernel initialization for the eepro100. -- REW */ +static struct ConfCmd { + s16 status; + s16 command; + u32 link; + unsigned char data[22]; +} confcmd = { + 0, 0, 0, /* filled in later */ + {22, 0x08, 0, 0, 0, 0x80, 0x32, 0x03, 1, /* 1=Use MII 0=Use AUI */ + 0, 0x2E, 0, 0x60, 0, + 0xf2, 0x48, 0, 0x40, 0xf2, 0x80, /* 0x40=Force full-duplex */ + 0x3f, 0x05, } +}; + +/***********************************************************************/ +/* Locally used functions */ +/***********************************************************************/ + +/* Support function: mdio_write + * + * This probably writes to the "physical media interface chip". + * -- REW + */ + +static int mdio_write(int phy_id, int location, int value) +{ + int val, boguscnt = 64*4; /* <64 usec. to complete, typ 27 ticks */ + + outl(0x04000000 | (location<<16) | (phy_id<<21) | value, + ioaddr + SCBCtrlMDI); + do { + udelay(16); + + val = inl(ioaddr + SCBCtrlMDI); + if (--boguscnt < 0) { + printf(" mdio_write() timed out with val = %X.\n", val); + break; + } + } while (! (val & 0x10000000)); + return val & 0xffff; +} + +/* Support function: mdio_read + * + * This probably reads a register in the "physical media interface chip". + * -- REW + */ +static int mdio_read(int phy_id, int location) +{ + int val, boguscnt = 64*4; /* <64 usec. to complete, typ 27 ticks */ + outl(0x08000000 | (location<<16) | (phy_id<<21), ioaddr + SCBCtrlMDI); + do { + udelay(16); + + val = inl(ioaddr + SCBCtrlMDI); + + if (--boguscnt < 0) { + printf( " mdio_read() timed out with val = %X.\n", val); + break; + } + } while (! (val & 0x10000000)); + return val & 0xffff; +} + +/* The fixes for the code were kindly provided by Dragan Stancevic + 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; + unsigned long ct; + + status = inw(ioaddr + SCBStatus); + /* Acknowledge all of the current interrupt sources ASAP. */ + outw(status & 0xfc00, ioaddr + SCBStatus); + +#ifdef DEBUG + printf ("transmitting type %hX packet (%d bytes). status = %hX, cmd=%hX\n", + t, s, status, inw (ioaddr + SCBCmd)); +#endif + + memcpy (&hdr.dst_addr, d, ETH_ALEN); + memcpy (&hdr.src_addr, nic->node_addr, ETH_ALEN); + + hdr.type = htons (t); + + txfd.status = 0; + txfd.command = CmdSuspend | CmdTx | CmdTxFlex; + txfd.link = virt_to_bus (&txfd); + txfd.count = 0x02208000; + txfd.tx_desc_addr = virt_to_bus(&txfd.tx_buf_addr0); + + txfd.tx_buf_addr0 = virt_to_bus (&hdr); + txfd.tx_buf_size0 = sizeof (hdr); + + txfd.tx_buf_addr1 = virt_to_bus (p); + txfd.tx_buf_size1 = s; + +#ifdef DEBUG + printf ("txfd: \n"); + hd (&txfd, sizeof (txfd)); +#endif + + outl(virt_to_bus(&txfd), ioaddr + SCBPointer); + outb(CU_START, ioaddr + SCBCmd); + wait_for_cmd_done(ioaddr + SCBCmd); + + s1 = inw (ioaddr + SCBStatus); + + ct = currticks(); + /* timeout 10 ms for transmit */ + while (!txfd.status && ct + 10*1000) + /* Wait */; + s2 = inw (ioaddr + SCBStatus); + +#ifdef DEBUG + printf ("s1 = %hX, s2 = %hX.\n", s1, s2); +#endif +} + +/* + * Sometimes the receiver stops making progress. This routine knows how to + * get it going again, without losing packets or being otherwise nasty like + * a chip reset would be. Previously the driver had a whole sequence + * of if RxSuspended, if it's no buffers do one thing, if it's no resources, + * do another, etc. But those things don't really matter. Separate logic + * in the ISR provides for allocating buffers--the other half of operation + * is just making sure the receiver is active. speedo_rx_soft_reset does that. + * This problem with the old, more involved algorithm is shown up under + * ping floods on the order of 60K packets/second on a 100Mbps fdx network. + */ +static void +speedo_rx_soft_reset(void) +{ + int i; + + +#ifdef DEBUG + printf("reset\n"); +#endif + wait_for_cmd_done(ioaddr + SCBCmd); + /* + * Put the hardware into a known state. + */ + outb(RX_ABORT, ioaddr + SCBCmd); + + for (i = 0; i < RXFD_COUNT; i++) { + rxfds[i].status = 0; + rxfds[i].rx_buf_addr = 0xffffffff; + rxfds[i].count = 0; + rxfds[i].size = 1528; + } + + wait_for_cmd_done(ioaddr + SCBCmd); + + outl(virt_to_bus(&rxfds[rxfd]), ioaddr + SCBPointer); + outb(RX_START, ioaddr + SCBCmd); +} + +/* function: eepro100_poll / eth_poll + * This receives a packet from the network. + * + * Arguments: none + * + * returns: 1 if a packet was received. + * 0 if no packet was received. + * side effects: + * returns the packet in the array nic->packet. + * returns the length of the packet in nic->packetlen. + */ + +static int eepro100_poll(struct nic *nic, int retrieve) +{ + if (rxfds[rxfd].status) { + if (!retrieve) + return 1; +#ifdef DEBUG + printf("Got a packet: Len = %d, rxfd = %d.\n", + rxfds[rxfd].count & 0x3fff, rxfd); +#endif + /* First save the data from the rxfd */ + nic->packetlen = rxfds[rxfd].count & 0x3fff; + memcpy(nic->packet, rxfds[rxfd].packet, nic->packetlen); + + rxfds[rxfd].status = 0; + rxfds[rxfd].command = 0xc000; + rxfds[rxfd].rx_buf_addr = 0xFFFFFFFF; + rxfds[rxfd].count = 0; + rxfds[rxfd].size = 1528; + rxfds[(rxfd-1) % RXFD_COUNT].command = 0x0000; + rxfd = (rxfd+1) % RXFD_COUNT; + +#ifdef DEBUG + hd (nic->packet, 0x30); +#endif + + /* Acknowledge all conceivable interrupts */ + outw(0xff00, ioaddr + SCBStatus); + + return 1; + } + + /* + * The chip may have suspended reception for various reasons. + * Check for that, and re-prime it should this be the case. + */ + switch ((inw(ioaddr + SCBStatus) >> 2) & 0xf) { + case 0: /* Idle */ + break; + case 1: /* Suspended */ + case 2: /* No resources (RxFDs) */ + case 9: /* Suspended with no more RBDs */ + case 10: /* No resources due to no RBDs */ + case 12: /* Ready with no RBDs */ + speedo_rx_soft_reset(); + break; + default: + /* reserved values */ + break; + } + return 0; +} + +/* function: eepro100_disable + * resets the card. This is used to allow Etherboot or Linux + * to probe the card again from a "virginal" state.... + * Arguments: none + * + * returns: void. + */ +static void eepro100_disable ( struct nic *nic __unused ) { +/* from eepro100_reset */ + outl(0, ioaddr + SCBPort); +/* from eepro100_disable */ + /* See if this PartialReset solves the problem with interfering with + kernel operation after Etherboot hands over. - Ken 20001102 */ + outl(2, ioaddr + SCBPort); + + /* The following is from the Intel e100 driver. + * This hopefully solves the problem with hanging hard DOS images. */ + + /* wait for the reset to take effect */ + udelay(20); + + /* Mask off our interrupt line -- it is unmasked after reset */ + { + u16 intr_status; + /* Disable interrupts on our PCI board by setting the mask bit */ + outw(INT_MASK, ioaddr + SCBCmd); + intr_status = inw(ioaddr + SCBStatus); + /* ack and clear intrs */ + outw(intr_status, ioaddr + SCBStatus); + inw(ioaddr + SCBStatus); + } +} + +/* exported function: eepro100_probe / eth_probe + * initializes a card + * + * side effects: + * leaves the ioaddress of the 82557 chip in the variable ioaddr. + * leaves the 82557 initialized, and ready to recieve packets. + */ + +static int eepro100_probe ( struct nic *nic, struct pci_device *pci ) { + + unsigned short sum = 0; + int i; + int read_cmd, ee_size; + int options; + int rx_mode; + unsigned long ct; + + /* we cache only the first few words of the EEPROM data + be careful not to access beyond this array */ + unsigned short eeprom[16]; + + if (pci->ioaddr == 0) + return 0; + + adjust_pci_device(pci); + + nic->ioaddr = pci->ioaddr; + nic->irqno = pci->irq; + + ioaddr = nic->ioaddr; + + if ((do_eeprom_cmd(EE_READ_CMD << 24, 27) & 0xffe0000) + == 0xffe0000) { + ee_size = 0x100; + read_cmd = EE_READ_CMD << 24; + } else { + ee_size = 0x40; + read_cmd = EE_READ_CMD << 22; + } + + for (i = 0, sum = 0; i < ee_size; i++) { + unsigned short value = do_eeprom_cmd(read_cmd | (i << 16), 27); + if (i < (int)(sizeof(eeprom)/sizeof(eeprom[0]))) + eeprom[i] = value; + sum += value; + } + + for (i=0;inode_addr[i] = (eeprom[i/2] >> (8*(i&1))) & 0xff; + } + + DBG ( "Ethernet addr: %s\n", eth_ntoa ( nic->node_addr ) ); + + if (sum != 0xBABA) + printf("eepro100: Invalid EEPROM checksum %#hX, " + "check settings before activating this device!\n", sum); + outl(0, ioaddr + SCBPort); + udelay (10000); + whereami ("Got eeprom."); + + /* Base = 0, disable all interrupts */ + outl(0, ioaddr + SCBPointer); + outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd); + wait_for_cmd_done(ioaddr + SCBCmd); + whereami ("set rx base addr."); + + outl(virt_to_bus(&lstats), ioaddr + SCBPointer); + outb(CU_STATSADDR, ioaddr + SCBCmd); + wait_for_cmd_done(ioaddr + SCBCmd); + whereami ("set stats addr."); + + /* INIT RX stuff. */ + for (i = 0; i < RXFD_COUNT; i++) { + rxfds[i].status = 0x0000; + rxfds[i].command = 0x0000; + rxfds[i].rx_buf_addr = 0xFFFFFFFF; + rxfds[i].count = 0; + rxfds[i].size = 1528; + rxfds[i].link = virt_to_bus(&rxfds[i+1]); + } + + rxfds[RXFD_COUNT-1].status = 0x0000; + rxfds[RXFD_COUNT-1].command = 0xC000; + rxfds[RXFD_COUNT-1].link = virt_to_bus(&rxfds[0]); + + outl(virt_to_bus(&rxfds[0]), ioaddr + SCBPointer); + outb(RX_START, ioaddr + SCBCmd); + wait_for_cmd_done(ioaddr + SCBCmd); + + whereami ("started RX process."); + + /* INIT TX stuff. */ + + /* Base = 0 */ + outl(0, ioaddr + SCBPointer); + outb(CU_CMD_BASE, ioaddr + SCBCmd); + wait_for_cmd_done(ioaddr + SCBCmd); + + whereami ("set TX base addr."); + + txfd.command = (CmdIASetup); + txfd.status = 0x0000; + txfd.link = virt_to_bus (&confcmd); + + { + char *t = (char *)&txfd.tx_desc_addr; + + for (i=0;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)."); + + ct = currticks(); + while (!txfd.status && ct + 10*1000 < currticks()) + /* Wait */; + + /* Read the status register once to disgard stale data */ + mdio_read(eeprom[6] & 0x1f, 1); + /* Check to see if the network cable is plugged in. + * This allows for faster failure if there is nothing + * we can do. + */ + if (!(mdio_read(eeprom[6] & 0x1f, 1) & (1 << 2))) { + printf("Valid link not established\n"); + eepro100_disable(nic); + return 0; + } + nic->nic_op = &eepro100_operations; + return 1; +} + +/*********************************************************************/ + +#ifdef DEBUG + +/* Hexdump a number of bytes from memory... */ +void hd (void *where, int n) +{ + int i; + + while (n > 0) { + printf ("%X ", where); + for (i=0;i < ( (n>16)?16:n);i++) + printf (" %hhX", ((char *)where)[i]); + printf ("\n"); + n -= 16; + where += 16; + } +} +#endif + +static struct nic_operations eepro100_operations = { + .connect = dummy_connect, + .poll = eepro100_poll, + .transmit = eepro100_transmit, + .irq = eepro100_irq, + +}; + +static struct pci_device_id eepro100_nics[] = { +PCI_ROM(0x8086, 0x1029, "id1029", "Intel EtherExpressPro100 ID1029"), +PCI_ROM(0x8086, 0x1030, "id1030", "Intel EtherExpressPro100 ID1030"), +PCI_ROM(0x8086, 0x1031, "82801cam", "Intel 82801CAM (ICH3) Chipset Ethernet Controller"), +PCI_ROM(0x8086, 0x1032, "eepro100-1032", "Intel PRO/100 VE Network Connection"), +PCI_ROM(0x8086, 0x1033, "eepro100-1033", "Intel PRO/100 VM Network Connection"), +PCI_ROM(0x8086, 0x1034, "eepro100-1034", "Intel PRO/100 VM Network Connection"), +PCI_ROM(0x8086, 0x1035, "eepro100-1035", "Intel 82801CAM (ICH3) Chipset Ethernet Controller"), +PCI_ROM(0x8086, 0x1036, "eepro100-1036", "Intel 82801CAM (ICH3) Chipset Ethernet Controller"), +PCI_ROM(0x8086, 0x1037, "eepro100-1037", "Intel 82801CAM (ICH3) Chipset Ethernet Controller"), +PCI_ROM(0x8086, 0x1038, "id1038", "Intel PRO/100 VM Network Connection"), +PCI_ROM(0x8086, 0x1039, "82562et", "Intel PRO100 VE 82562ET"), +PCI_ROM(0x8086, 0x103a, "id103a", "Intel Corporation 82559 InBusiness 10/100"), +PCI_ROM(0x8086, 0x103b, "82562etb", "Intel PRO100 VE 82562ETB"), +PCI_ROM(0x8086, 0x103c, "eepro100-103c", "Intel PRO/100 VM Network Connection"), +PCI_ROM(0x8086, 0x103d, "eepro100-103d", "Intel PRO/100 VE Network Connection"), +PCI_ROM(0x8086, 0x103e, "eepro100-103e", "Intel PRO/100 VM Network Connection"), +PCI_ROM(0x8086, 0x1051, "prove", "Intel PRO/100 VE Network Connection"), +PCI_ROM(0x8086, 0x1059, "82551qm", "Intel PRO/100 M Mobile Connection"), +PCI_ROM(0x8086, 0x1209, "82559er", "Intel EtherExpressPro100 82559ER"), +PCI_ROM(0x8086, 0x1227, "82865", "Intel 82865 EtherExpress PRO/100A"), +PCI_ROM(0x8086, 0x1228, "82556", "Intel 82556 EtherExpress PRO/100 Smart"), +PCI_ROM(0x8086, 0x1229, "eepro100", "Intel EtherExpressPro100"), +PCI_ROM(0x8086, 0x2449, "82562em", "Intel EtherExpressPro100 82562EM"), +PCI_ROM(0x8086, 0x2459, "82562-1", "Intel 82562 based Fast Ethernet Connection"), +PCI_ROM(0x8086, 0x245d, "82562-2", "Intel 82562 based Fast Ethernet Connection"), +PCI_ROM(0x8086, 0x1050, "82562ez", "Intel 82562EZ Network Connection"), +PCI_ROM(0x8086, 0x1051, "eepro100-1051", "Intel 82801EB/ER (ICH5/ICH5R) Chipset Ethernet Controller"), +PCI_ROM(0x8086, 0x1065, "82562-3", "Intel 82562 based Fast Ethernet Connection"), +PCI_ROM(0x8086, 0x5200, "eepro100-5200", "Intel EtherExpress PRO/100 Intelligent Server"), +PCI_ROM(0x8086, 0x5201, "eepro100-5201", "Intel EtherExpress PRO/100 Intelligent Server"), +}; + +/* Cards with device ids 0x1030 to 0x103F, 0x2449, 0x2459 or 0x245D might need + * a workaround for hardware bug on 10 mbit half duplex (see linux driver eepro100.c) + * 2003/03/17 gbaum */ + + +PCI_DRIVER ( eepro100_driver, eepro100_nics, PCI_NO_CLASS ); + +DRIVER ( "EEPRO100", nic_driver, pci_driver, eepro100_driver, + eepro100_probe, eepro100_disable ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/epic100.c b/gpxe/src/drivers/net/epic100.c new file mode 100644 index 0000000..1e36a68 --- /dev/null +++ b/gpxe/src/drivers/net/epic100.c @@ -0,0 +1,537 @@ + +/* epic100.c: A SMC 83c170 EPIC/100 fast ethernet driver for Etherboot */ + +/* 05/06/2003 timlegge Fixed relocation and implemented Multicast */ +#define LINUX_OUT_MACROS + +#include "etherboot.h" +#include +#include +#include "nic.h" +#include "console.h" +#include "epic100.h" + +/* Condensed operations for readability */ +#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr)) + +#define TX_RING_SIZE 2 /* use at least 2 buffers for TX */ +#define RX_RING_SIZE 2 + +#define PKT_BUF_SZ 1536 /* Size of each temporary Tx/Rx buffer.*/ + +/* +#define DEBUG_RX +#define DEBUG_TX +#define DEBUG_EEPROM +*/ + +#define EPIC_DEBUG 0 /* debug level */ + +/* The EPIC100 Rx and Tx buffer descriptors. */ +struct epic_rx_desc { + unsigned long status; + unsigned long bufaddr; + unsigned long buflength; + unsigned long next; +}; +/* description of the tx descriptors control bits commonly used */ +#define TD_STDFLAGS TD_LASTDESC + +struct epic_tx_desc { + unsigned long status; + unsigned long bufaddr; + unsigned long buflength; + unsigned long next; +}; + +#define delay(nanosec) do { int _i = 3; while (--_i > 0) \ + { __SLOW_DOWN_IO; }} while (0) + +static void epic100_open(void); +static void epic100_init_ring(void); +static void epic100_disable(struct nic *nic); +static int epic100_poll(struct nic *nic, int retrieve); +static void epic100_transmit(struct nic *nic, const char *destaddr, + unsigned int type, unsigned int len, const char *data); +#ifdef DEBUG_EEPROM +static int read_eeprom(int location); +#endif +static int mii_read(int phy_id, int location); +static void epic100_irq(struct nic *nic, irq_action_t action); + +static struct nic_operations epic100_operations; + +static int ioaddr; + +static int command; +static int intstat; +static int intmask; +static int genctl ; +static int eectl ; +static int test ; +static int mmctl ; +static int mmdata ; +static int lan0 ; +static int mc0 ; +static int rxcon ; +static int txcon ; +static int prcdar ; +static int ptcdar ; +static int eththr ; + +static unsigned int cur_rx, cur_tx; /* The next free ring entry */ +#ifdef DEBUG_EEPROM +static unsigned short eeprom[64]; +#endif +static signed char phys[4]; /* MII device addresses. */ +struct { + struct epic_rx_desc rx_ring[RX_RING_SIZE] + __attribute__ ((aligned(4))); + struct epic_tx_desc tx_ring[TX_RING_SIZE] + __attribute__ ((aligned(4))); + unsigned char rx_packet[PKT_BUF_SZ * RX_RING_SIZE]; + unsigned char tx_packet[PKT_BUF_SZ * TX_RING_SIZE]; +} epic100_bufs __shared; +#define rx_ring epic100_bufs.rx_ring +#define tx_ring epic100_bufs.tx_ring +#define rx_packet epic100_bufs.rx_packet +#define tx_packet epic100_bufs.tx_packet + +/***********************************************************************/ +/* Externally visible functions */ +/***********************************************************************/ + + +static int +epic100_probe ( struct nic *nic, struct pci_device *pci ) { + + int i; + unsigned short* ap; + unsigned int phy, phy_idx; + + if (pci->ioaddr == 0) + return 0; + + /* Ideally we would detect all network cards in slot order. That would + be best done a central PCI probe dispatch, which wouldn't work + well with the current structure. So instead we detect just the + Epic cards in slot order. */ + + ioaddr = pci->ioaddr; + + nic->irqno = 0; + nic->ioaddr = pci->ioaddr & ~3; + + /* compute all used static epic100 registers address */ + command = ioaddr + COMMAND; /* Control Register */ + intstat = ioaddr + INTSTAT; /* Interrupt Status */ + intmask = ioaddr + INTMASK; /* Interrupt Mask */ + genctl = ioaddr + GENCTL; /* General Control */ + eectl = ioaddr + EECTL; /* EEPROM Control */ + test = ioaddr + TEST; /* Test register (clocks) */ + mmctl = ioaddr + MMCTL; /* MII Management Interface Control */ + mmdata = ioaddr + MMDATA; /* MII Management Interface Data */ + lan0 = ioaddr + LAN0; /* MAC address. (0x40-0x48) */ + mc0 = ioaddr + MC0; /* Multicast Control */ + rxcon = ioaddr + RXCON; /* Receive Control */ + txcon = ioaddr + TXCON; /* Transmit Control */ + prcdar = ioaddr + PRCDAR; /* PCI Receive Current Descr Address */ + ptcdar = ioaddr + PTCDAR; /* PCI Transmit Current Descr Address */ + eththr = ioaddr + ETHTHR; /* Early Transmit Threshold */ + + /* Reset the chip & bring it out of low-power mode. */ + outl(GC_SOFT_RESET, genctl); + + /* Disable ALL interrupts by setting the interrupt mask. */ + outl(INTR_DISABLE, intmask); + + /* + * set the internal clocks: + * Application Note 7.15 says: + * In order to set the CLOCK TEST bit in the TEST register, + * perform the following: + * + * Write 0x0008 to the test register at least sixteen + * consecutive times. + * + * The CLOCK TEST bit is Write-Only. Writing it several times + * consecutively insures a successful write to the bit... + */ + + for (i = 0; i < 16; i++) { + outl(0x00000008, test); + } + +#ifdef DEBUG_EEPROM +{ + unsigned short sum = 0; + unsigned short value; + for (i = 0; i < 64; i++) { + value = read_eeprom(i); + eeprom[i] = value; + sum += value; + } +} + +#if (EPIC_DEBUG > 1) + printf("EEPROM contents\n"); + for (i = 0; i < 64; i++) { + printf(" %hhX%s", eeprom[i], i % 16 == 15 ? "\n" : ""); + } +#endif +#endif + + /* This could also be read from the EEPROM. */ + ap = (unsigned short*)nic->node_addr; + for (i = 0; i < 3; i++) + *ap++ = inw(lan0 + i*4); + + DBG ( " I/O %4.4x %s ", ioaddr, eth_ntoa ( nic->node_addr ) ); + + /* Find the connected MII xcvrs. */ + for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(phys); phy++) { + int mii_status = mii_read(phy, 0); + + if (mii_status != 0xffff && mii_status != 0x0000) { + phys[phy_idx++] = phy; +#if (EPIC_DEBUG > 1) + printf("MII transceiver found at address %d.\n", phy); +#endif + } + } + if (phy_idx == 0) { +#if (EPIC_DEBUG > 1) + printf("***WARNING***: No MII transceiver found!\n"); +#endif + /* Use the known PHY address of the EPII. */ + phys[0] = 3; + } + + epic100_open(); + nic->nic_op = &epic100_operations; + + return 1; +} + +static void set_rx_mode(void) +{ + unsigned char mc_filter[8]; + int i; + memset(mc_filter, 0xff, sizeof(mc_filter)); + outl(0x0C, rxcon); + for(i = 0; i < 4; i++) + outw(((unsigned short *)mc_filter)[i], mc0 + i*4); + return; +} + + static void +epic100_open(void) +{ + int mii_reg5; + int full_duplex = 0; + unsigned long tmp; + + epic100_init_ring(); + + /* Pull the chip out of low-power mode, and set for PCI read multiple. */ + outl(GC_RX_FIFO_THR_64 | GC_MRC_READ_MULT | GC_ONE_COPY, genctl); + + outl(TX_FIFO_THRESH, eththr); + + tmp = TC_EARLY_TX_ENABLE | TX_SLOT_TIME; + + mii_reg5 = mii_read(phys[0], 5); + if (mii_reg5 != 0xffff && (mii_reg5 & 0x0100)) { + full_duplex = 1; + printf(" full-duplex mode"); + tmp |= TC_LM_FULL_DPX; + } else + tmp |= TC_LM_NORMAL; + + outl(tmp, txcon); + + /* Give adress of RX and TX ring to the chip */ + outl(virt_to_le32desc(&rx_ring), prcdar); + outl(virt_to_le32desc(&tx_ring), ptcdar); + + /* Start the chip's Rx process: receive unicast and broadcast */ + set_rx_mode(); + outl(CR_START_RX | CR_QUEUE_RX, command); + + putchar('\n'); +} + +/* Initialize the Rx and Tx rings. */ + static void +epic100_init_ring(void) +{ + int i; + + cur_rx = cur_tx = 0; + + for (i = 0; i < RX_RING_SIZE; i++) { + rx_ring[i].status = cpu_to_le32(RRING_OWN); /* Owned by Epic chip */ + rx_ring[i].buflength = cpu_to_le32(PKT_BUF_SZ); + rx_ring[i].bufaddr = virt_to_bus(&rx_packet[i * PKT_BUF_SZ]); + rx_ring[i].next = virt_to_le32desc(&rx_ring[i + 1]) ; + } + /* Mark the last entry as wrapping the ring. */ + rx_ring[i-1].next = virt_to_le32desc(&rx_ring[0]); + + /* + *The Tx buffer descriptor is filled in as needed, + * but we do need to clear the ownership bit. + */ + + for (i = 0; i < TX_RING_SIZE; i++) { + tx_ring[i].status = 0x0000; /* Owned by CPU */ + tx_ring[i].buflength = 0x0000 | cpu_to_le32(TD_STDFLAGS << 16); + tx_ring[i].bufaddr = virt_to_bus(&tx_packet[i * PKT_BUF_SZ]); + tx_ring[i].next = virt_to_le32desc(&tx_ring[i + 1]); + } + tx_ring[i-1].next = virt_to_le32desc(&tx_ring[0]); +} + +/* function: epic100_transmit + * This transmits a packet. + * + * Arguments: char d[6]: destination ethernet address. + * unsigned short t: ethernet protocol type. + * unsigned short s: size of the data-part of the packet. + * char *p: the data for the packet. + * returns: void. + */ + static void +epic100_transmit(struct nic *nic, const char *destaddr, unsigned int type, + unsigned int len, const char *data) +{ + unsigned short nstype; + unsigned char *txp; + int entry; + unsigned long ct; + + /* Calculate the next Tx descriptor entry. */ + entry = cur_tx % TX_RING_SIZE; + + if ((tx_ring[entry].status & TRING_OWN) == TRING_OWN) { + printf("eth_transmit: Unable to transmit. status=%4.4lx. Resetting...\n", + tx_ring[entry].status); + + epic100_open(); + return; + } + + txp = tx_packet + (entry * PKT_BUF_SZ); + + memcpy(txp, destaddr, ETH_ALEN); + memcpy(txp + ETH_ALEN, nic->node_addr, ETH_ALEN); + nstype = htons(type); + memcpy(txp + 12, (char*)&nstype, 2); + memcpy(txp + ETH_HLEN, data, len); + + len += ETH_HLEN; + len &= 0x0FFF; + while(len < ETH_ZLEN) + txp[len++] = '\0'; + /* + * Caution: the write order is important here, + * set the base address with the "ownership" + * bits last. + */ + + tx_ring[entry].buflength |= cpu_to_le32(len); + tx_ring[entry].status = cpu_to_le32(len << 16) | + cpu_to_le32(TRING_OWN); /* Pass ownership to the chip. */ + + cur_tx++; + + /* Trigger an immediate transmit demand. */ + outl(CR_QUEUE_TX, command); + + ct = currticks(); + /* timeout 10 ms for transmit */ + while ((le32_to_cpu(tx_ring[entry].status) & (TRING_OWN)) && + ct + 10*1000 < currticks()) + /* Wait */; + + if ((le32_to_cpu(tx_ring[entry].status) & TRING_OWN) != 0) + printf("Oops, transmitter timeout, status=%4.4lX\n", + tx_ring[entry].status); +} + +/* function: epic100_poll / eth_poll + * This receives a packet from the network. + * + * Arguments: none + * + * returns: 1 if a packet was received. + * 0 if no pacet was received. + * side effects: + * returns the packet in the array nic->packet. + * returns the length of the packet in nic->packetlen. + */ + + static int +epic100_poll(struct nic *nic, int retrieve) +{ + int entry; + int retcode; + int status; + entry = cur_rx % RX_RING_SIZE; + + if ((rx_ring[entry].status & cpu_to_le32(RRING_OWN)) == RRING_OWN) + return (0); + + if ( ! retrieve ) return 1; + + status = le32_to_cpu(rx_ring[entry].status); + /* We own the next entry, it's a new packet. Send it up. */ + +#if (EPIC_DEBUG > 4) + printf("epic_poll: entry %d status %hX\n", entry, status); +#endif + + cur_rx++; + if (status & 0x2000) { + printf("epic_poll: Giant packet\n"); + retcode = 0; + } else if (status & 0x0006) { + /* Rx Frame errors are counted in hardware. */ + printf("epic_poll: Frame received with errors\n"); + retcode = 0; + } else { + /* Omit the four octet CRC from the length. */ + nic->packetlen = le32_to_cpu((rx_ring[entry].buflength))- 4; + memcpy(nic->packet, &rx_packet[entry * PKT_BUF_SZ], nic->packetlen); + retcode = 1; + } + + /* Clear all error sources. */ + outl(status & INTR_CLEARERRS, intstat); + + /* Give the descriptor back to the chip */ + rx_ring[entry].status = RRING_OWN; + + /* Restart Receiver */ + outl(CR_START_RX | CR_QUEUE_RX, command); + + return retcode; +} + + +static void epic100_disable ( struct nic *nic __unused ) { + /* Soft reset the chip. */ + outl(GC_SOFT_RESET, genctl); +} + +static void epic100_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + +#ifdef DEBUG_EEPROM +/* Serial EEPROM section. */ + +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */ +#define EE_CS 0x02 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x08 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x01 +#define EE_WRITE_1 0x09 +#define EE_DATA_READ 0x10 /* EEPROM chip data out. */ +#define EE_ENB (0x0001 | EE_CS) + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD (5 << 6) +#define EE_READ_CMD (6 << 6) +#define EE_ERASE_CMD (7 << 6) + +#define eeprom_delay(n) delay(n) + + static int +read_eeprom(int location) +{ + int i; + int retval = 0; + int read_cmd = location | EE_READ_CMD; + + outl(EE_ENB & ~EE_CS, eectl); + outl(EE_ENB, eectl); + + /* Shift the read command bits out. */ + for (i = 10; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + outl(EE_ENB | dataval, eectl); + eeprom_delay(100); + outl(EE_ENB | dataval | EE_SHIFT_CLK, eectl); + eeprom_delay(150); + outl(EE_ENB | dataval, eectl); /* Finish EEPROM a clock tick. */ + eeprom_delay(250); + } + outl(EE_ENB, eectl); + + for (i = 16; i > 0; i--) { + outl(EE_ENB | EE_SHIFT_CLK, eectl); + eeprom_delay(100); + retval = (retval << 1) | ((inl(eectl) & EE_DATA_READ) ? 1 : 0); + outl(EE_ENB, eectl); + eeprom_delay(100); + } + + /* Terminate the EEPROM access. */ + outl(EE_ENB & ~EE_CS, eectl); + return retval; +} +#endif + + +#define MII_READOP 1 +#define MII_WRITEOP 2 + + static int +mii_read(int phy_id, int location) +{ + int i; + + outl((phy_id << 9) | (location << 4) | MII_READOP, mmctl); + /* Typical operation takes < 50 ticks. */ + + for (i = 4000; i > 0; i--) + if ((inl(mmctl) & MII_READOP) == 0) + break; + return inw(mmdata); +} + +static struct nic_operations epic100_operations = { + .connect = dummy_connect, + .poll = epic100_poll, + .transmit = epic100_transmit, + .irq = epic100_irq, + +}; + +static struct pci_device_id epic100_nics[] = { +PCI_ROM(0x10b8, 0x0005, "epic100", "SMC EtherPowerII"), /* SMC 83c170 EPIC/100 */ +PCI_ROM(0x10b8, 0x0006, "smc-83c175", "SMC EPIC/C 83c175"), +}; + +PCI_DRIVER ( epic100_driver, epic100_nics, PCI_NO_CLASS ); + +DRIVER ( "EPIC100", nic_driver, pci_driver, epic100_driver, + epic100_probe, epic100_disable ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/epic100.h b/gpxe/src/drivers/net/epic100.h new file mode 100644 index 0000000..61bd1d9 --- /dev/null +++ b/gpxe/src/drivers/net/epic100.h @@ -0,0 +1,188 @@ +#ifndef _EPIC100_H_ +# define _EPIC100_H_ + +#ifndef PCI_VENDOR_SMC +# define PCI_VENDOR_SMC 0x10B8 +#endif + +#ifndef PCI_DEVICE_SMC_EPIC100 +# define PCI_DEVICE_SMC_EPIC100 0x0005 +#endif + +#define PCI_DEVICE_ID_NONE 0xFFFF + +/* Offsets to registers (using SMC names). */ +enum epic100_registers { + COMMAND= 0, /* Control Register */ + INTSTAT= 4, /* Interrupt Status */ + INTMASK= 8, /* Interrupt Mask */ + GENCTL = 0x0C, /* General Control */ + NVCTL = 0x10, /* Non Volatile Control */ + EECTL = 0x14, /* EEPROM Control */ + TEST = 0x1C, /* Test register: marked as reserved (see in source code) */ + CRCCNT = 0x20, /* CRC Error Counter */ + ALICNT = 0x24, /* Frame Alignment Error Counter */ + MPCNT = 0x28, /* Missed Packet Counter */ + MMCTL = 0x30, /* MII Management Interface Control */ + MMDATA = 0x34, /* MII Management Interface Data */ + MIICFG = 0x38, /* MII Configuration */ + IPG = 0x3C, /* InterPacket Gap */ + LAN0 = 0x40, /* MAC address. (0x40-0x48) */ + IDCHK = 0x4C, /* BoardID/ Checksum */ + MC0 = 0x50, /* Multicast filter table. (0x50-0x5c) */ + RXCON = 0x60, /* Receive Control */ + TXCON = 0x70, /* Transmit Control */ + TXSTAT = 0x74, /* Transmit Status */ + PRCDAR = 0x84, /* PCI Receive Current Descriptor Address */ + PRSTAT = 0xA4, /* PCI Receive DMA Status */ + PRCPTHR= 0xB0, /* PCI Receive Copy Threshold */ + PTCDAR = 0xC4, /* PCI Transmit Current Descriptor Address */ + ETHTHR = 0xDC /* Early Transmit Threshold */ +}; + +/* Command register (CR_) bits */ +#define CR_STOP_RX (0x00000001) +#define CR_START_RX (0x00000002) +#define CR_QUEUE_TX (0x00000004) +#define CR_QUEUE_RX (0x00000008) +#define CR_NEXTFRAME (0x00000010) +#define CR_STOP_TX_DMA (0x00000020) +#define CR_STOP_RX_DMA (0x00000040) +#define CR_TX_UGO (0x00000080) + +/* Interrupt register bits. NI means No Interrupt generated */ + +#define INTR_RX_THR_STA (0x00400000) /* rx copy threshold status NI */ +#define INTR_RX_BUFF_EMPTY (0x00200000) /* rx buffers empty. NI */ +#define INTR_TX_IN_PROG (0x00100000) /* tx copy in progess. NI */ +#define INTR_RX_IN_PROG (0x00080000) /* rx copy in progress. NI */ +#define INTR_TXIDLE (0x00040000) /* tx idle. NI */ +#define INTR_RXIDLE (0x00020000) /* rx idle. NI */ +#define INTR_INTR_ACTIVE (0x00010000) /* Interrupt active. NI */ +#define INTR_RX_STATUS_OK (0x00008000) /* rx status valid. NI */ +#define INTR_PCI_TGT_ABT (0x00004000) /* PCI Target abort */ +#define INTR_PCI_MASTER_ABT (0x00002000) /* PCI Master abort */ +#define INTR_PCI_PARITY_ERR (0x00001000) /* PCI adress parity error */ +#define INTR_PCI_DATA_ERR (0x00000800) /* PCI data parity error */ +#define INTR_RX_THR_CROSSED (0x00000400) /* rx copy threshold crossed */ +#define INTR_CNTFULL (0x00000200) /* Counter overflow */ +#define INTR_TXUNDERRUN (0x00000100) /* tx underrun. */ +#define INTR_TXEMPTY (0x00000080) /* tx queue empty */ +#define INTR_TX_CH_COMPLETE (0x00000040) /* tx chain complete */ +#define INTR_TXDONE (0x00000020) /* tx complete (w or w/o err) */ +#define INTR_RXERROR (0x00000010) /* rx error (CRC) */ +#define INTR_RXOVERFLOW (0x00000008) /* rx buffer overflow */ +#define INTR_RX_QUEUE_EMPTY (0x00000004) /* rx queue empty. */ +#define INTR_RXHEADER (0x00000002) /* header copy complete */ +#define INTR_RXDONE (0x00000001) /* Receive copy complete */ + +#define INTR_CLEARINTR (0x00007FFF) +#define INTR_VALIDBITS (0x007FFFFF) +#define INTR_DISABLE (0x00000000) +#define INTR_CLEARERRS (0x00007F18) +#define INTR_ABNINTR (INTR_CNTFULL | INTR_TXUNDERRUN | INTR_RXOVERFLOW) + +/* General Control (GC_) bits */ + +#define GC_SOFT_RESET (0x00000001) +#define GC_INTR_ENABLE (0x00000002) +#define GC_SOFT_INTR (0x00000004) +#define GC_POWER_DOWN (0x00000008) +#define GC_ONE_COPY (0x00000010) +#define GC_BIG_ENDIAN (0x00000020) +#define GC_RX_PREEMPT_TX (0x00000040) +#define GC_TX_PREEMPT_RX (0x00000080) + +/* + * Receive FIFO Threshold values + * Control the level at which the PCI burst state machine + * begins to empty the receive FIFO. Possible values: 0-3 + * + * 0 => 32, 1 => 64, 2 => 96 3 => 128 bytes. + */ +#define GC_RX_FIFO_THR_32 (0x00000000) +#define GC_RX_FIFO_THR_64 (0x00000100) +#define GC_RX_FIFO_THR_96 (0x00000200) +#define GC_RX_FIFO_THR_128 (0x00000300) + +/* Memory Read Control (MRC_) values */ +#define GC_MRC_MEM_READ (0x00000000) +#define GC_MRC_READ_MULT (0x00000400) +#define GC_MRC_READ_LINE (0x00000800) + +#define GC_SOFTBIT0 (0x00001000) +#define GC_SOFTBIT1 (0x00002000) +#define GC_RESET_PHY (0x00004000) + +/* Definitions of the Receive Control (RC_) register bits */ + +#define RC_SAVE_ERRORED_PKT (0x00000001) +#define RC_SAVE_RUNT_FRAMES (0x00000002) +#define RC_RCV_BROADCAST (0x00000004) +#define RC_RCV_MULTICAST (0x00000008) +#define RC_RCV_INVERSE_PKT (0x00000010) +#define RC_PROMISCUOUS_MODE (0x00000020) +#define RC_MONITOR_MODE (0x00000040) +#define RC_EARLY_RCV_ENABLE (0x00000080) + +/* description of the rx descriptors control bits */ +#define RD_FRAGLIST (0x0001) /* Desc points to a fragment list */ +#define RD_LLFORM (0x0002) /* Frag list format */ +#define RD_HDR_CPY (0x0004) /* Desc used for header copy */ + +/* Definition of the Transmit CONTROL (TC) register bits */ + +#define TC_EARLY_TX_ENABLE (0x00000001) + +/* Loopback Mode (LM_) Select valuesbits */ +#define TC_LM_NORMAL (0x00000000) +#define TC_LM_INTERNAL (0x00000002) +#define TC_LM_EXTERNAL (0x00000004) +#define TC_LM_FULL_DPX (0x00000006) + +#define TX_SLOT_TIME (0x00000078) + +/* Bytes transferred to chip before transmission starts. */ +#define TX_FIFO_THRESH 128 /* Rounded down to 4 byte units. */ + +/* description of rx descriptors status bits */ +#define RRING_PKT_INTACT (0x0001) +#define RRING_ALIGN_ERR (0x0002) +#define RRING_CRC_ERR (0x0004) +#define RRING_MISSED_PKT (0x0008) +#define RRING_MULTICAST (0x0010) +#define RRING_BROADCAST (0x0020) +#define RRING_RECEIVER_DISABLE (0x0040) +#define RRING_STATUS_VALID (0x1000) +#define RRING_FRAGLIST_ERR (0x2000) +#define RRING_HDR_COPIED (0x4000) +#define RRING_OWN (0x8000) + +/* error summary */ +#define RRING_ERROR (RRING_ALIGN_ERR|RRING_CRC_ERR) + +/* description of tx descriptors status bits */ +#define TRING_PKT_INTACT (0x0001) /* pkt transmitted. */ +#define TRING_PKT_NONDEFER (0x0002) /* pkt xmitted w/o deferring */ +#define TRING_COLL (0x0004) /* pkt xmitted w collisions */ +#define TRING_CARR (0x0008) /* carrier sense lost */ +#define TRING_UNDERRUN (0x0010) /* DMA underrun */ +#define TRING_HB_COLL (0x0020) /* Collision detect Heartbeat */ +#define TRING_WIN_COLL (0x0040) /* out of window collision */ +#define TRING_DEFERRED (0x0080) /* Deferring */ +#define TRING_COLL_COUNT (0x0F00) /* collision counter (mask) */ +#define TRING_COLL_EXCESS (0x1000) /* tx aborted: excessive colls */ +#define TRING_OWN (0x8000) /* desc ownership bit */ + +/* error summary */ +#define TRING_ABORT (TRING_COLL_EXCESS|TRING_WIN_COLL|TRING_UNDERRUN) +#define TRING_ERROR (TRING_DEFERRED|TRING_WIN_COLL|TRING_UNDERRUN|TRING_CARR/*|TRING_COLL*/ ) + +/* description of the tx descriptors control bits */ +#define TD_FRAGLIST (0x0001) /* Desc points to a fragment list */ +#define TD_LLFORM (0x0002) /* Frag list format */ +#define TD_IAF (0x0004) /* Generate Interrupt after tx */ +#define TD_NOCRC (0x0008) /* No CRC generated */ +#define TD_LASTDESC (0x0010) /* Last desc for this frame */ + +#endif /* _EPIC100_H_ */ diff --git a/gpxe/src/drivers/net/etherfabric.c b/gpxe/src/drivers/net/etherfabric.c new file mode 100644 index 0000000..704ce98 --- /dev/null +++ b/gpxe/src/drivers/net/etherfabric.c @@ -0,0 +1,4248 @@ +/************************************************************************** + * + * Etherboot driver for Level 5 Etherfabric network cards + * + * Written by Michael Brown + * + * Copyright Fen Systems Ltd. 2005 + * Copyright Level 5 Networks Inc. 2005 + * + * This software may be used and distributed according to the terms of + * the GNU General Public License (GPL), incorporated herein by + * reference. Drivers based on or derived from this code fall under + * the GPL and must retain the authorship, copyright and license + * notice. + * + ************************************************************************** + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "etherfabric.h" +#include "etherfabric_nic.h" + +/************************************************************************** + * + * Constants and macros + * + ************************************************************************** + */ + +#define EFAB_REGDUMP(...) +#define EFAB_TRACE(...) DBGP(__VA_ARGS__) + +// printf() is not allowed within drivers. Use DBG() instead. +#define EFAB_LOG(...) DBG(__VA_ARGS__) +#define EFAB_ERR(...) DBG(__VA_ARGS__) + +#define FALCON_USE_IO_BAR 0 + +#define HZ 100 +#define EFAB_BYTE 1 + +/************************************************************************** + * + * Hardware data structures and sizing + * + ************************************************************************** + */ +extern int __invalid_queue_size; +#define FQS(_prefix, _x) \ + ( ( (_x) == 512 ) ? _prefix ## _SIZE_512 : \ + ( ( (_x) == 1024 ) ? _prefix ## _SIZE_1K : \ + ( ( (_x) == 2048 ) ? _prefix ## _SIZE_2K : \ + ( ( (_x) == 4096) ? _prefix ## _SIZE_4K : \ + __invalid_queue_size ) ) ) ) + + +#define EFAB_MAX_FRAME_LEN(mtu) \ + ( ( ( ( mtu ) + 4/* FCS */ ) + 7 ) & ~7 ) + +/************************************************************************** + * + * GMII routines + * + ************************************************************************** + */ + +static void falcon_mdio_write (struct efab_nic *efab, int device, + int location, int value ); +static int falcon_mdio_read ( struct efab_nic *efab, int device, int location ); + +/* GMII registers */ +#define MII_BMSR 0x01 /* Basic mode status register */ +#define MII_ADVERTISE 0x04 /* Advertisement control register */ +#define MII_LPA 0x05 /* Link partner ability register*/ +#define GMII_GTCR 0x09 /* 1000BASE-T control register */ +#define GMII_GTSR 0x0a /* 1000BASE-T status register */ +#define GMII_PSSR 0x11 /* PHY-specific status register */ + +/* Basic mode status register. */ +#define BMSR_LSTATUS 0x0004 /* Link status */ + +/* Link partner ability register. */ +#define LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */ +#define LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */ +#define LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */ +#define LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */ +#define LPA_100BASE4 0x0200 /* Can do 100mbps 4k packets */ +#define LPA_PAUSE 0x0400 /* Bit 10 - MAC pause */ + +/* Pseudo extensions to the link partner ability register */ +#define LPA_1000FULL 0x00020000 +#define LPA_1000HALF 0x00010000 +#define LPA_10000FULL 0x00040000 +#define LPA_10000HALF 0x00080000 + +#define LPA_100 (LPA_100FULL | LPA_100HALF | LPA_100BASE4) +#define LPA_1000 ( LPA_1000FULL | LPA_1000HALF ) +#define LPA_10000 ( LPA_10000FULL | LPA_10000HALF ) +#define LPA_DUPLEX ( LPA_10FULL | LPA_100FULL | LPA_1000FULL | \ + LPA_10000FULL ) + +/* Mask of bits not associated with speed or duplexity. */ +#define LPA_OTHER ~( LPA_10FULL | LPA_10HALF | LPA_100FULL | \ + LPA_100HALF | LPA_1000FULL | LPA_1000HALF ) + +/* PHY-specific status register */ +#define PSSR_LSTATUS 0x0400 /* Bit 10 - link status */ + +/** + * Retrieve GMII autonegotiation advertised abilities + * + */ +static unsigned int +gmii_autoneg_advertised ( struct efab_nic *efab ) +{ + unsigned int mii_advertise; + unsigned int gmii_advertise; + + /* Extended bits are in bits 8 and 9 of GMII_GTCR */ + mii_advertise = falcon_mdio_read ( efab, 0, MII_ADVERTISE ); + gmii_advertise = ( ( falcon_mdio_read ( efab, 0, GMII_GTCR ) >> 8 ) + & 0x03 ); + return ( ( gmii_advertise << 16 ) | mii_advertise ); +} + +/** + * Retrieve GMII autonegotiation link partner abilities + * + */ +static unsigned int +gmii_autoneg_lpa ( struct efab_nic *efab ) +{ + unsigned int mii_lpa; + unsigned int gmii_lpa; + + /* Extended bits are in bits 10 and 11 of GMII_GTSR */ + mii_lpa = falcon_mdio_read ( efab, 0, MII_LPA ); + gmii_lpa = ( falcon_mdio_read ( efab, 0, GMII_GTSR ) >> 10 ) & 0x03; + return ( ( gmii_lpa << 16 ) | mii_lpa ); +} + +/** + * Calculate GMII autonegotiated link technology + * + */ +static unsigned int +gmii_nway_result ( unsigned int negotiated ) +{ + unsigned int other_bits; + + /* Mask out the speed and duplexity bits */ + other_bits = negotiated & LPA_OTHER; + + if ( negotiated & LPA_1000FULL ) + return ( other_bits | LPA_1000FULL ); + else if ( negotiated & LPA_1000HALF ) + return ( other_bits | LPA_1000HALF ); + else if ( negotiated & LPA_100FULL ) + return ( other_bits | LPA_100FULL ); + else if ( negotiated & LPA_100BASE4 ) + return ( other_bits | LPA_100BASE4 ); + else if ( negotiated & LPA_100HALF ) + return ( other_bits | LPA_100HALF ); + else if ( negotiated & LPA_10FULL ) + return ( other_bits | LPA_10FULL ); + else return ( other_bits | LPA_10HALF ); +} + +/** + * Check GMII PHY link status + * + */ +static int +gmii_link_ok ( struct efab_nic *efab ) +{ + int status; + int phy_status; + + /* BMSR is latching - it returns "link down" if the link has + * been down at any point since the last read. To get a + * real-time status, we therefore read the register twice and + * use the result of the second read. + */ + (void) falcon_mdio_read ( efab, 0, MII_BMSR ); + status = falcon_mdio_read ( efab, 0, MII_BMSR ); + + /* Read the PHY-specific Status Register. This is + * non-latching, so we need do only a single read. + */ + phy_status = falcon_mdio_read ( efab, 0, GMII_PSSR ); + + return ( ( status & BMSR_LSTATUS ) && ( phy_status & PSSR_LSTATUS ) ); +} + +/************************************************************************** + * + * MDIO routines + * + ************************************************************************** + */ + +/* Numbering of the MDIO Manageable Devices (MMDs) */ +/* Physical Medium Attachment/ Physical Medium Dependent sublayer */ +#define MDIO_MMD_PMAPMD (1) +/* WAN Interface Sublayer */ +#define MDIO_MMD_WIS (2) +/* Physical Coding Sublayer */ +#define MDIO_MMD_PCS (3) +/* PHY Extender Sublayer */ +#define MDIO_MMD_PHYXS (4) +/* Extender Sublayer */ +#define MDIO_MMD_DTEXS (5) +/* Transmission convergence */ +#define MDIO_MMD_TC (6) +/* Auto negotiation */ +#define MDIO_MMD_AN (7) + +/* Generic register locations */ +#define MDIO_MMDREG_CTRL1 (0) +#define MDIO_MMDREG_STAT1 (1) +#define MDIO_MMDREG_DEVS0 (5) +#define MDIO_MMDREG_STAT2 (8) + +/* Bits in MMDREG_CTRL1 */ +/* Reset */ +#define MDIO_MMDREG_CTRL1_RESET_LBN (15) +#define MDIO_MMDREG_CTRL1_RESET_WIDTH (1) + +/* Bits in MMDREG_STAT1 */ +#define MDIO_MMDREG_STAT1_FAULT_LBN (7) +#define MDIO_MMDREG_STAT1_FAULT_WIDTH (1) + +/* Link state */ +#define MDIO_MMDREG_STAT1_LINK_LBN (2) +#define MDIO_MMDREG_STAT1_LINK_WIDTH (1) + +/* Bits in MMDREG_DEVS0. */ +#define DEV_PRESENT_BIT(_b) (1 << _b) + +#define MDIO_MMDREG_DEVS0_DTEXS DEV_PRESENT_BIT(MDIO_MMD_DTEXS) +#define MDIO_MMDREG_DEVS0_PHYXS DEV_PRESENT_BIT(MDIO_MMD_PHYXS) +#define MDIO_MMDREG_DEVS0_PCS DEV_PRESENT_BIT(MDIO_MMD_PCS) +#define MDIO_MMDREG_DEVS0_WIS DEV_PRESENT_BIT(MDIO_MMD_WIS) +#define MDIO_MMDREG_DEVS0_PMAPMD DEV_PRESENT_BIT(MDIO_MMD_PMAPMD) + +#define MDIO_MMDREG_DEVS0_AN DEV_PRESENT_BIT(MDIO_MMD_AN) + +/* Bits in MMDREG_STAT2 */ +#define MDIO_MMDREG_STAT2_PRESENT_VAL (2) +#define MDIO_MMDREG_STAT2_PRESENT_LBN (14) +#define MDIO_MMDREG_STAT2_PRESENT_WIDTH (2) + +/* PHY XGXS lane state */ +#define MDIO_PHYXS_LANE_STATE (0x18) +#define MDIO_PHYXS_LANE_ALIGNED_LBN (12) +#define MDIO_PHYXS_LANE_SYNC0_LBN (0) +#define MDIO_PHYXS_LANE_SYNC1_LBN (1) +#define MDIO_PHYXS_LANE_SYNC2_LBN (2) +#define MDIO_PHYXS_LANE_SYNC3_LBN (3) + +/* This ought to be ridiculous overkill. We expect it to fail rarely */ +#define MDIO45_RESET_TRIES 100 +#define MDIO45_RESET_SPINTIME 10 + +static int +mdio_clause45_wait_reset_mmds ( struct efab_nic* efab ) +{ + int tries = MDIO45_RESET_TRIES; + int in_reset; + + while(tries) { + int mask = efab->phy_op->mmds; + int mmd = 0; + in_reset = 0; + while(mask) { + if (mask & 1) { + int stat = falcon_mdio_read ( efab, mmd, + MDIO_MMDREG_CTRL1 ); + if (stat < 0) { + EFAB_ERR("Failed to read status of MMD %d\n", + mmd ); + in_reset = 1; + break; + } + if (stat & (1 << MDIO_MMDREG_CTRL1_RESET_LBN)) + in_reset |= (1 << mmd); + } + mask = mask >> 1; + mmd++; + } + if (!in_reset) + break; + tries--; + mdelay ( MDIO45_RESET_SPINTIME ); + } + if (in_reset != 0) { + EFAB_ERR("Not all MMDs came out of reset in time. MMDs " + "still in reset: %x\n", in_reset); + return -ETIMEDOUT; + } + return 0; +} + +static int +mdio_clause45_reset_mmd ( struct efab_nic *efab, int mmd ) +{ + int tries = MDIO45_RESET_TRIES; + int ctrl; + + falcon_mdio_write ( efab, mmd, MDIO_MMDREG_CTRL1, + ( 1 << MDIO_MMDREG_CTRL1_RESET_LBN ) ); + + /* Wait for the reset bit to clear. */ + do { + mdelay ( MDIO45_RESET_SPINTIME ); + + ctrl = falcon_mdio_read ( efab, mmd, MDIO_MMDREG_CTRL1 ); + if ( ~ctrl & ( 1 << MDIO_MMDREG_CTRL1_RESET_LBN ) ) + return 0; + } while ( --tries ); + + EFAB_ERR ( "Failed to reset mmd %d\n", mmd ); + + return -ETIMEDOUT; +} + +static int +mdio_clause45_links_ok(struct efab_nic *efab ) +{ + int status, good; + int ok = 1; + int mmd = 0; + int mmd_mask = efab->phy_op->mmds; + + while (mmd_mask) { + if (mmd_mask & 1) { + /* Double reads because link state is latched, and a + * read moves the current state into the register */ + status = falcon_mdio_read ( efab, mmd, + MDIO_MMDREG_STAT1 ); + status = falcon_mdio_read ( efab, mmd, + MDIO_MMDREG_STAT1 ); + + good = status & (1 << MDIO_MMDREG_STAT1_LINK_LBN); + ok = ok && good; + } + mmd_mask = (mmd_mask >> 1); + mmd++; + } + return ok; +} + +static int +mdio_clause45_check_mmds ( struct efab_nic *efab ) +{ + int mmd = 0; + int devices = falcon_mdio_read ( efab, MDIO_MMD_PHYXS, + MDIO_MMDREG_DEVS0 ); + int mmd_mask = efab->phy_op->mmds; + + /* Check all the expected MMDs are present */ + if ( devices < 0 ) { + EFAB_ERR ( "Failed to read devices present\n" ); + return -EIO; + } + if ( ( devices & mmd_mask ) != mmd_mask ) { + EFAB_ERR ( "required MMDs not present: got %x, wanted %x\n", + devices, mmd_mask ); + return -EIO; + } + + /* Check all required MMDs are responding and happy. */ + while ( mmd_mask ) { + if ( mmd_mask & 1 ) { + efab_dword_t reg; + int status; + reg.opaque = falcon_mdio_read ( efab, mmd, + MDIO_MMDREG_STAT2 ); + status = EFAB_DWORD_FIELD ( reg, + MDIO_MMDREG_STAT2_PRESENT ); + if ( status != MDIO_MMDREG_STAT2_PRESENT_VAL ) { + + + return -EIO; + } + } + mmd_mask >>= 1; + mmd++; + } + + return 0; +} + +/* I/O BAR address register */ +#define FCN_IOM_IND_ADR_REG 0x0 + +/* I/O BAR data register */ +#define FCN_IOM_IND_DAT_REG 0x4 + +/* Address region register */ +#define FCN_ADR_REGION_REG_KER 0x00 +#define FCN_ADR_REGION0_LBN 0 +#define FCN_ADR_REGION0_WIDTH 18 +#define FCN_ADR_REGION1_LBN 32 +#define FCN_ADR_REGION1_WIDTH 18 +#define FCN_ADR_REGION2_LBN 64 +#define FCN_ADR_REGION2_WIDTH 18 +#define FCN_ADR_REGION3_LBN 96 +#define FCN_ADR_REGION3_WIDTH 18 + +/* Interrupt enable register */ +#define FCN_INT_EN_REG_KER 0x0010 +#define FCN_MEM_PERR_INT_EN_KER_LBN 5 +#define FCN_MEM_PERR_INT_EN_KER_WIDTH 1 +#define FCN_KER_INT_CHAR_LBN 4 +#define FCN_KER_INT_CHAR_WIDTH 1 +#define FCN_KER_INT_KER_LBN 3 +#define FCN_KER_INT_KER_WIDTH 1 +#define FCN_ILL_ADR_ERR_INT_EN_KER_LBN 2 +#define FCN_ILL_ADR_ERR_INT_EN_KER_WIDTH 1 +#define FCN_SRM_PERR_INT_EN_KER_LBN 1 +#define FCN_SRM_PERR_INT_EN_KER_WIDTH 1 +#define FCN_DRV_INT_EN_KER_LBN 0 +#define FCN_DRV_INT_EN_KER_WIDTH 1 + +/* Interrupt status register */ +#define FCN_INT_ADR_REG_KER 0x0030 +#define FCN_INT_ADR_KER_LBN 0 +#define FCN_INT_ADR_KER_WIDTH EFAB_DMA_TYPE_WIDTH ( 64 ) + +/* Interrupt status register (B0 only) */ +#define INT_ISR0_B0 0x90 +#define INT_ISR1_B0 0xA0 + +/* Interrupt acknowledge register (A0/A1 only) */ +#define FCN_INT_ACK_KER_REG_A1 0x0050 +#define INT_ACK_DUMMY_DATA_LBN 0 +#define INT_ACK_DUMMY_DATA_WIDTH 32 + +/* Interrupt acknowledge work-around register (A0/A1 only )*/ +#define WORK_AROUND_BROKEN_PCI_READS_REG_KER_A1 0x0070 + +/* Hardware initialisation register */ +#define FCN_HW_INIT_REG_KER 0x00c0 +#define FCN_BCSR_TARGET_MASK_LBN 101 +#define FCN_BCSR_TARGET_MASK_WIDTH 4 + +/* SPI host command register */ +#define FCN_EE_SPI_HCMD_REG 0x0100 +#define FCN_EE_SPI_HCMD_CMD_EN_LBN 31 +#define FCN_EE_SPI_HCMD_CMD_EN_WIDTH 1 +#define FCN_EE_WR_TIMER_ACTIVE_LBN 28 +#define FCN_EE_WR_TIMER_ACTIVE_WIDTH 1 +#define FCN_EE_SPI_HCMD_SF_SEL_LBN 24 +#define FCN_EE_SPI_HCMD_SF_SEL_WIDTH 1 +#define FCN_EE_SPI_EEPROM 0 +#define FCN_EE_SPI_FLASH 1 +#define FCN_EE_SPI_HCMD_DABCNT_LBN 16 +#define FCN_EE_SPI_HCMD_DABCNT_WIDTH 5 +#define FCN_EE_SPI_HCMD_READ_LBN 15 +#define FCN_EE_SPI_HCMD_READ_WIDTH 1 +#define FCN_EE_SPI_READ 1 +#define FCN_EE_SPI_WRITE 0 +#define FCN_EE_SPI_HCMD_DUBCNT_LBN 12 +#define FCN_EE_SPI_HCMD_DUBCNT_WIDTH 2 +#define FCN_EE_SPI_HCMD_ADBCNT_LBN 8 +#define FCN_EE_SPI_HCMD_ADBCNT_WIDTH 2 +#define FCN_EE_SPI_HCMD_ENC_LBN 0 +#define FCN_EE_SPI_HCMD_ENC_WIDTH 8 + +/* SPI host address register */ +#define FCN_EE_SPI_HADR_REG 0x0110 +#define FCN_EE_SPI_HADR_DUBYTE_LBN 24 +#define FCN_EE_SPI_HADR_DUBYTE_WIDTH 8 +#define FCN_EE_SPI_HADR_ADR_LBN 0 +#define FCN_EE_SPI_HADR_ADR_WIDTH 24 + +/* SPI host data register */ +#define FCN_EE_SPI_HDATA_REG 0x0120 +#define FCN_EE_SPI_HDATA3_LBN 96 +#define FCN_EE_SPI_HDATA3_WIDTH 32 +#define FCN_EE_SPI_HDATA2_LBN 64 +#define FCN_EE_SPI_HDATA2_WIDTH 32 +#define FCN_EE_SPI_HDATA1_LBN 32 +#define FCN_EE_SPI_HDATA1_WIDTH 32 +#define FCN_EE_SPI_HDATA0_LBN 0 +#define FCN_EE_SPI_HDATA0_WIDTH 32 + +/* VPD Config 0 Register register */ +#define FCN_EE_VPD_CFG_REG 0x0140 +#define FCN_EE_VPD_EN_LBN 0 +#define FCN_EE_VPD_EN_WIDTH 1 +#define FCN_EE_VPD_EN_AD9_MODE_LBN 1 +#define FCN_EE_VPD_EN_AD9_MODE_WIDTH 1 +#define FCN_EE_EE_CLOCK_DIV_LBN 112 +#define FCN_EE_EE_CLOCK_DIV_WIDTH 7 +#define FCN_EE_SF_CLOCK_DIV_LBN 120 +#define FCN_EE_SF_CLOCK_DIV_WIDTH 7 + + +/* NIC status register */ +#define FCN_NIC_STAT_REG 0x0200 +#define FCN_ONCHIP_SRAM_LBN 16 +#define FCN_ONCHIP_SRAM_WIDTH 1 +#define FCN_SF_PRST_LBN 9 +#define FCN_SF_PRST_WIDTH 1 +#define FCN_EE_PRST_LBN 8 +#define FCN_EE_PRST_WIDTH 1 +#define FCN_EE_STRAP_LBN 7 +#define FCN_EE_STRAP_WIDTH 1 +#define FCN_PCI_PCIX_MODE_LBN 4 +#define FCN_PCI_PCIX_MODE_WIDTH 3 +#define FCN_PCI_PCIX_MODE_PCI33_DECODE 0 +#define FCN_PCI_PCIX_MODE_PCI66_DECODE 1 +#define FCN_PCI_PCIX_MODE_PCIX66_DECODE 5 +#define FCN_PCI_PCIX_MODE_PCIX100_DECODE 6 +#define FCN_PCI_PCIX_MODE_PCIX133_DECODE 7 +#define FCN_STRAP_ISCSI_EN_LBN 3 +#define FCN_STRAP_ISCSI_EN_WIDTH 1 +#define FCN_STRAP_PINS_LBN 0 +#define FCN_STRAP_PINS_WIDTH 3 +#define FCN_STRAP_10G_LBN 2 +#define FCN_STRAP_10G_WIDTH 1 +#define FCN_STRAP_DUAL_PORT_LBN 1 +#define FCN_STRAP_DUAL_PORT_WIDTH 1 +#define FCN_STRAP_PCIE_LBN 0 +#define FCN_STRAP_PCIE_WIDTH 1 + +/* Falcon revisions */ +#define FALCON_REV_A0 0 +#define FALCON_REV_A1 1 +#define FALCON_REV_B0 2 + +/* GPIO control register */ +#define FCN_GPIO_CTL_REG_KER 0x0210 +#define FCN_GPIO_CTL_REG_KER 0x0210 + +#define FCN_GPIO3_OEN_LBN 27 +#define FCN_GPIO3_OEN_WIDTH 1 +#define FCN_GPIO2_OEN_LBN 26 +#define FCN_GPIO2_OEN_WIDTH 1 +#define FCN_GPIO1_OEN_LBN 25 +#define FCN_GPIO1_OEN_WIDTH 1 +#define FCN_GPIO0_OEN_LBN 24 +#define FCN_GPIO0_OEN_WIDTH 1 + +#define FCN_GPIO3_OUT_LBN 19 +#define FCN_GPIO3_OUT_WIDTH 1 +#define FCN_GPIO2_OUT_LBN 18 +#define FCN_GPIO2_OUT_WIDTH 1 +#define FCN_GPIO1_OUT_LBN 17 +#define FCN_GPIO1_OUT_WIDTH 1 +#define FCN_GPIO0_OUT_LBN 16 +#define FCN_GPIO0_OUT_WIDTH 1 + +#define FCN_GPIO3_IN_LBN 11 +#define FCN_GPIO3_IN_WIDTH 1 +#define FCN_GPIO2_IN_LBN 10 +#define FCN_GPIO2_IN_WIDTH 1 +#define FCN_GPIO1_IN_LBN 9 +#define FCN_GPIO1_IN_WIDTH 1 +#define FCN_GPIO0_IN_LBN 8 +#define FCN_GPIO0_IN_WIDTH 1 + +#define FCN_FLASH_PRESENT_LBN 7 +#define FCN_FLASH_PRESENT_WIDTH 1 +#define FCN_EEPROM_PRESENT_LBN 6 +#define FCN_EEPROM_PRESENT_WIDTH 1 +#define FCN_BOOTED_USING_NVDEVICE_LBN 3 +#define FCN_BOOTED_USING_NVDEVICE_WIDTH 1 + +/* Defines for extra non-volatile storage */ +#define FCN_NV_MAGIC_NUMBER 0xFA1C + +/* Global control register */ +#define FCN_GLB_CTL_REG_KER 0x0220 +#define FCN_EXT_PHY_RST_CTL_LBN 63 +#define FCN_EXT_PHY_RST_CTL_WIDTH 1 +#define FCN_PCIE_SD_RST_CTL_LBN 61 +#define FCN_PCIE_SD_RST_CTL_WIDTH 1 +#define FCN_PCIE_STCK_RST_CTL_LBN 59 +#define FCN_PCIE_STCK_RST_CTL_WIDTH 1 +#define FCN_PCIE_NSTCK_RST_CTL_LBN 58 +#define FCN_PCIE_NSTCK_RST_CTL_WIDTH 1 +#define FCN_PCIE_CORE_RST_CTL_LBN 57 +#define FCN_PCIE_CORE_RST_CTL_WIDTH 1 +#define FCN_EE_RST_CTL_LBN 49 +#define FCN_EE_RST_CTL_WIDTH 1 +#define FCN_RST_EXT_PHY_LBN 31 +#define FCN_RST_EXT_PHY_WIDTH 1 +#define FCN_EXT_PHY_RST_DUR_LBN 1 +#define FCN_EXT_PHY_RST_DUR_WIDTH 3 +#define FCN_SWRST_LBN 0 +#define FCN_SWRST_WIDTH 1 +#define INCLUDE_IN_RESET 0 +#define EXCLUDE_FROM_RESET 1 + +/* FPGA build version */ +#define FCN_ALTERA_BUILD_REG_KER 0x0300 +#define FCN_VER_MAJOR_LBN 24 +#define FCN_VER_MAJOR_WIDTH 8 +#define FCN_VER_MINOR_LBN 16 +#define FCN_VER_MINOR_WIDTH 8 +#define FCN_VER_BUILD_LBN 0 +#define FCN_VER_BUILD_WIDTH 16 +#define FCN_VER_ALL_LBN 0 +#define FCN_VER_ALL_WIDTH 32 + +/* Spare EEPROM bits register (flash 0x390) */ +#define FCN_SPARE_REG_KER 0x310 +#define FCN_MEM_PERR_EN_TX_DATA_LBN 72 +#define FCN_MEM_PERR_EN_TX_DATA_WIDTH 2 + +/* Timer table for kernel access */ +#define FCN_TIMER_CMD_REG_KER 0x420 +#define FCN_TIMER_MODE_LBN 12 +#define FCN_TIMER_MODE_WIDTH 2 +#define FCN_TIMER_MODE_DIS 0 +#define FCN_TIMER_MODE_INT_HLDOFF 1 +#define FCN_TIMER_VAL_LBN 0 +#define FCN_TIMER_VAL_WIDTH 12 + +/* Receive configuration register */ +#define FCN_RX_CFG_REG_KER 0x800 +#define FCN_RX_XOFF_EN_LBN 0 +#define FCN_RX_XOFF_EN_WIDTH 1 + +/* SRAM receive descriptor cache configuration register */ +#define FCN_SRM_RX_DC_CFG_REG_KER 0x610 +#define FCN_SRM_RX_DC_BASE_ADR_LBN 0 +#define FCN_SRM_RX_DC_BASE_ADR_WIDTH 21 + +/* SRAM transmit descriptor cache configuration register */ +#define FCN_SRM_TX_DC_CFG_REG_KER 0x620 +#define FCN_SRM_TX_DC_BASE_ADR_LBN 0 +#define FCN_SRM_TX_DC_BASE_ADR_WIDTH 21 + +/* SRAM configuration register */ +#define FCN_SRM_CFG_REG_KER 0x630 +#define FCN_SRAM_OOB_ADR_INTEN_LBN 5 +#define FCN_SRAM_OOB_ADR_INTEN_WIDTH 1 +#define FCN_SRAM_OOB_BUF_INTEN_LBN 4 +#define FCN_SRAM_OOB_BUF_INTEN_WIDTH 1 +#define FCN_SRAM_OOB_BT_INIT_EN_LBN 3 +#define FCN_SRAM_OOB_BT_INIT_EN_WIDTH 1 +#define FCN_SRM_NUM_BANK_LBN 2 +#define FCN_SRM_NUM_BANK_WIDTH 1 +#define FCN_SRM_BANK_SIZE_LBN 0 +#define FCN_SRM_BANK_SIZE_WIDTH 2 +#define FCN_SRM_NUM_BANKS_AND_BANK_SIZE_LBN 0 +#define FCN_SRM_NUM_BANKS_AND_BANK_SIZE_WIDTH 3 + +#define FCN_RX_CFG_REG_KER 0x800 +#define FCN_RX_INGR_EN_B0_LBN 47 +#define FCN_RX_INGR_EN_B0_WIDTH 1 +#define FCN_RX_USR_BUF_SIZE_B0_LBN 19 +#define FCN_RX_USR_BUF_SIZE_B0_WIDTH 9 +#define FCN_RX_XON_MAC_TH_B0_LBN 10 +#define FCN_RX_XON_MAC_TH_B0_WIDTH 9 +#define FCN_RX_XOFF_MAC_TH_B0_LBN 1 +#define FCN_RX_XOFF_MAC_TH_B0_WIDTH 9 +#define FCN_RX_XOFF_MAC_EN_B0_LBN 0 +#define FCN_RX_XOFF_MAC_EN_B0_WIDTH 1 +#define FCN_RX_USR_BUF_SIZE_A1_LBN 11 +#define FCN_RX_USR_BUF_SIZE_A1_WIDTH 9 +#define FCN_RX_XON_MAC_TH_A1_LBN 6 +#define FCN_RX_XON_MAC_TH_A1_WIDTH 5 +#define FCN_RX_XOFF_MAC_TH_A1_LBN 1 +#define FCN_RX_XOFF_MAC_TH_A1_WIDTH 5 +#define FCN_RX_XOFF_MAC_EN_A1_LBN 0 +#define FCN_RX_XOFF_MAC_EN_A1_WIDTH 1 + +#define FCN_RX_USR_BUF_SIZE_A1_LBN 11 +#define FCN_RX_USR_BUF_SIZE_A1_WIDTH 9 +#define FCN_RX_XOFF_MAC_EN_A1_LBN 0 +#define FCN_RX_XOFF_MAC_EN_A1_WIDTH 1 + +/* Receive filter control register */ +#define FCN_RX_FILTER_CTL_REG_KER 0x810 +#define FCN_UDP_FULL_SRCH_LIMIT_LBN 32 +#define FCN_UDP_FULL_SRCH_LIMIT_WIDTH 8 +#define FCN_NUM_KER_LBN 24 +#define FCN_NUM_KER_WIDTH 2 +#define FCN_UDP_WILD_SRCH_LIMIT_LBN 16 +#define FCN_UDP_WILD_SRCH_LIMIT_WIDTH 8 +#define FCN_TCP_WILD_SRCH_LIMIT_LBN 8 +#define FCN_TCP_WILD_SRCH_LIMIT_WIDTH 8 +#define FCN_TCP_FULL_SRCH_LIMIT_LBN 0 +#define FCN_TCP_FULL_SRCH_LIMIT_WIDTH 8 + +/* RX queue flush register */ +#define FCN_RX_FLUSH_DESCQ_REG_KER 0x0820 +#define FCN_RX_FLUSH_DESCQ_CMD_LBN 24 +#define FCN_RX_FLUSH_DESCQ_CMD_WIDTH 1 +#define FCN_RX_FLUSH_DESCQ_LBN 0 +#define FCN_RX_FLUSH_DESCQ_WIDTH 12 + +/* Receive descriptor update register */ +#define FCN_RX_DESC_UPD_REG_KER 0x0830 +#define FCN_RX_DESC_WPTR_LBN 96 +#define FCN_RX_DESC_WPTR_WIDTH 12 +#define FCN_RX_DESC_UPD_REG_KER_DWORD ( FCN_RX_DESC_UPD_REG_KER + 12 ) +#define FCN_RX_DESC_WPTR_DWORD_LBN 0 +#define FCN_RX_DESC_WPTR_DWORD_WIDTH 12 + +/* Receive descriptor cache configuration register */ +#define FCN_RX_DC_CFG_REG_KER 0x840 +#define FCN_RX_DC_SIZE_LBN 0 +#define FCN_RX_DC_SIZE_WIDTH 2 + +#define FCN_RX_SELF_RST_REG_KER 0x890 +#define FCN_RX_ISCSI_DIS_LBN 17 +#define FCN_RX_ISCSI_DIS_WIDTH 1 +#define FCN_RX_NODESC_WAIT_DIS_LBN 9 +#define FCN_RX_NODESC_WAIT_DIS_WIDTH 1 +#define FCN_RX_RECOVERY_EN_LBN 8 +#define FCN_RX_RECOVERY_EN_WIDTH 1 + +/* TX queue flush register */ +#define FCN_TX_FLUSH_DESCQ_REG_KER 0x0a00 +#define FCN_TX_FLUSH_DESCQ_CMD_LBN 12 +#define FCN_TX_FLUSH_DESCQ_CMD_WIDTH 1 +#define FCN_TX_FLUSH_DESCQ_LBN 0 +#define FCN_TX_FLUSH_DESCQ_WIDTH 12 + +/* Transmit configuration register 2 */ +#define FCN_TX_CFG2_REG_KER 0xa80 +#define FCN_TX_DIS_NON_IP_EV_LBN 17 +#define FCN_TX_DIS_NON_IP_EV_WIDTH 1 + +/* Transmit descriptor update register */ +#define FCN_TX_DESC_UPD_REG_KER 0x0a10 +#define FCN_TX_DESC_WPTR_LBN 96 +#define FCN_TX_DESC_WPTR_WIDTH 12 +#define FCN_TX_DESC_UPD_REG_KER_DWORD ( FCN_TX_DESC_UPD_REG_KER + 12 ) +#define FCN_TX_DESC_WPTR_DWORD_LBN 0 +#define FCN_TX_DESC_WPTR_DWORD_WIDTH 12 + +/* Transmit descriptor cache configuration register */ +#define FCN_TX_DC_CFG_REG_KER 0xa20 +#define FCN_TX_DC_SIZE_LBN 0 +#define FCN_TX_DC_SIZE_WIDTH 2 + +/* PHY management transmit data register */ +#define FCN_MD_TXD_REG_KER 0xc00 +#define FCN_MD_TXD_LBN 0 +#define FCN_MD_TXD_WIDTH 16 + +/* PHY management receive data register */ +#define FCN_MD_RXD_REG_KER 0xc10 +#define FCN_MD_RXD_LBN 0 +#define FCN_MD_RXD_WIDTH 16 + +/* PHY management configuration & status register */ +#define FCN_MD_CS_REG_KER 0xc20 +#define FCN_MD_GC_LBN 4 +#define FCN_MD_GC_WIDTH 1 +#define FCN_MD_RIC_LBN 2 +#define FCN_MD_RIC_WIDTH 1 +#define FCN_MD_RDC_LBN 1 +#define FCN_MD_RDC_WIDTH 1 +#define FCN_MD_WRC_LBN 0 +#define FCN_MD_WRC_WIDTH 1 + +/* PHY management PHY address register */ +#define FCN_MD_PHY_ADR_REG_KER 0xc30 +#define FCN_MD_PHY_ADR_LBN 0 +#define FCN_MD_PHY_ADR_WIDTH 16 + +/* PHY management ID register */ +#define FCN_MD_ID_REG_KER 0xc40 +#define FCN_MD_PRT_ADR_LBN 11 +#define FCN_MD_PRT_ADR_WIDTH 5 +#define FCN_MD_DEV_ADR_LBN 6 +#define FCN_MD_DEV_ADR_WIDTH 5 + +/* PHY management status & mask register */ +#define FCN_MD_STAT_REG_KER 0xc50 +#define FCN_MD_PINT_LBN 4 +#define FCN_MD_PINT_WIDTH 1 +#define FCN_MD_DONE_LBN 3 +#define FCN_MD_DONE_WIDTH 1 +#define FCN_MD_BSERR_LBN 2 +#define FCN_MD_BSERR_WIDTH 1 +#define FCN_MD_LNFL_LBN 1 +#define FCN_MD_LNFL_WIDTH 1 +#define FCN_MD_BSY_LBN 0 +#define FCN_MD_BSY_WIDTH 1 + +/* Port 0 and 1 MAC control registers */ +#define FCN_MAC0_CTRL_REG_KER 0xc80 +#define FCN_MAC1_CTRL_REG_KER 0xc90 +#define FCN_MAC_XOFF_VAL_LBN 16 +#define FCN_MAC_XOFF_VAL_WIDTH 16 +#define FCN_MAC_BCAD_ACPT_LBN 4 +#define FCN_MAC_BCAD_ACPT_WIDTH 1 +#define FCN_MAC_UC_PROM_LBN 3 +#define FCN_MAC_UC_PROM_WIDTH 1 +#define FCN_MAC_LINK_STATUS_LBN 2 +#define FCN_MAC_LINK_STATUS_WIDTH 1 +#define FCN_MAC_SPEED_LBN 0 +#define FCN_MAC_SPEED_WIDTH 2 + +/* 10Gig Xaui XGXS Default Values */ +#define XX_TXDRV_DEQ_DEFAULT 0xe /* deq=.6 */ +#define XX_TXDRV_DTX_DEFAULT 0x5 /* 1.25 */ +#define XX_SD_CTL_DRV_DEFAULT 0 /* 20mA */ + +/* GMAC registers */ +#define FALCON_GMAC_REGBANK 0xe00 +#define FALCON_GMAC_REGBANK_SIZE 0x200 +#define FALCON_GMAC_REG_SIZE 0x10 + +/* XGMAC registers */ +#define FALCON_XMAC_REGBANK 0x1200 +#define FALCON_XMAC_REGBANK_SIZE 0x200 +#define FALCON_XMAC_REG_SIZE 0x10 + +/* XGMAC address register low */ +#define FCN_XM_ADR_LO_REG_MAC 0x00 +#define FCN_XM_ADR_3_LBN 24 +#define FCN_XM_ADR_3_WIDTH 8 +#define FCN_XM_ADR_2_LBN 16 +#define FCN_XM_ADR_2_WIDTH 8 +#define FCN_XM_ADR_1_LBN 8 +#define FCN_XM_ADR_1_WIDTH 8 +#define FCN_XM_ADR_0_LBN 0 +#define FCN_XM_ADR_0_WIDTH 8 + +/* XGMAC address register high */ +#define FCN_XM_ADR_HI_REG_MAC 0x01 +#define FCN_XM_ADR_5_LBN 8 +#define FCN_XM_ADR_5_WIDTH 8 +#define FCN_XM_ADR_4_LBN 0 +#define FCN_XM_ADR_4_WIDTH 8 + +/* XGMAC global configuration - port 0*/ +#define FCN_XM_GLB_CFG_REG_MAC 0x02 +#define FCN_XM_RX_STAT_EN_LBN 11 +#define FCN_XM_RX_STAT_EN_WIDTH 1 +#define FCN_XM_TX_STAT_EN_LBN 10 +#define FCN_XM_TX_STAT_EN_WIDTH 1 +#define FCN_XM_RX_JUMBO_MODE_LBN 6 +#define FCN_XM_RX_JUMBO_MODE_WIDTH 1 +#define FCN_XM_CORE_RST_LBN 0 +#define FCN_XM_CORE_RST_WIDTH 1 + +/* XGMAC transmit configuration - port 0 */ +#define FCN_XM_TX_CFG_REG_MAC 0x03 +#define FCN_XM_IPG_LBN 16 +#define FCN_XM_IPG_WIDTH 4 +#define FCN_XM_FCNTL_LBN 10 +#define FCN_XM_FCNTL_WIDTH 1 +#define FCN_XM_TXCRC_LBN 8 +#define FCN_XM_TXCRC_WIDTH 1 +#define FCN_XM_AUTO_PAD_LBN 5 +#define FCN_XM_AUTO_PAD_WIDTH 1 +#define FCN_XM_TX_PRMBL_LBN 2 +#define FCN_XM_TX_PRMBL_WIDTH 1 +#define FCN_XM_TXEN_LBN 1 +#define FCN_XM_TXEN_WIDTH 1 + +/* XGMAC receive configuration - port 0 */ +#define FCN_XM_RX_CFG_REG_MAC 0x04 +#define FCN_XM_PASS_CRC_ERR_LBN 25 +#define FCN_XM_PASS_CRC_ERR_WIDTH 1 +#define FCN_XM_AUTO_DEPAD_LBN 8 +#define FCN_XM_AUTO_DEPAD_WIDTH 1 +#define FCN_XM_RXEN_LBN 1 +#define FCN_XM_RXEN_WIDTH 1 + +/* XGMAC management interrupt mask register */ +#define FCN_XM_MGT_INT_MSK_REG_MAC_B0 0x5 +#define FCN_XM_MSK_PRMBLE_ERR_LBN 2 +#define FCN_XM_MSK_PRMBLE_ERR_WIDTH 1 +#define FCN_XM_MSK_RMTFLT_LBN 1 +#define FCN_XM_MSK_RMTFLT_WIDTH 1 +#define FCN_XM_MSK_LCLFLT_LBN 0 +#define FCN_XM_MSK_LCLFLT_WIDTH 1 + +/* XGMAC flow control register */ +#define FCN_XM_FC_REG_MAC 0x7 +#define FCN_XM_PAUSE_TIME_LBN 16 +#define FCN_XM_PAUSE_TIME_WIDTH 16 +#define FCN_XM_DIS_FCNTL_LBN 0 +#define FCN_XM_DIS_FCNTL_WIDTH 1 + +/* XGMAC transmit parameter register */ +#define FCN_XM_TX_PARAM_REG_MAC 0x0d +#define FCN_XM_TX_JUMBO_MODE_LBN 31 +#define FCN_XM_TX_JUMBO_MODE_WIDTH 1 +#define FCN_XM_MAX_TX_FRM_SIZE_LBN 16 +#define FCN_XM_MAX_TX_FRM_SIZE_WIDTH 14 +#define FCN_XM_ACPT_ALL_MCAST_LBN 11 +#define FCN_XM_ACPT_ALL_MCAST_WIDTH 1 + +/* XGMAC receive parameter register */ +#define FCN_XM_RX_PARAM_REG_MAC 0x0e +#define FCN_XM_MAX_RX_FRM_SIZE_LBN 0 +#define FCN_XM_MAX_RX_FRM_SIZE_WIDTH 14 + +/* XGMAC management interrupt status register */ +#define FCN_XM_MGT_INT_REG_MAC_B0 0x0f +#define FCN_XM_PRMBLE_ERR 2 +#define FCN_XM_PRMBLE_WIDTH 1 +#define FCN_XM_RMTFLT_LBN 1 +#define FCN_XM_RMTFLT_WIDTH 1 +#define FCN_XM_LCLFLT_LBN 0 +#define FCN_XM_LCLFLT_WIDTH 1 + +/* XAUI XGXS core status register */ +#define FCN_XX_ALIGN_DONE_LBN 20 +#define FCN_XX_ALIGN_DONE_WIDTH 1 +#define FCN_XX_CORE_STAT_REG_MAC 0x16 +#define FCN_XX_SYNC_STAT_LBN 16 +#define FCN_XX_SYNC_STAT_WIDTH 4 +#define FCN_XX_SYNC_STAT_DECODE_SYNCED 0xf +#define FCN_XX_COMMA_DET_LBN 12 +#define FCN_XX_COMMA_DET_WIDTH 4 +#define FCN_XX_COMMA_DET_RESET 0xf +#define FCN_XX_CHARERR_LBN 4 +#define FCN_XX_CHARERR_WIDTH 4 +#define FCN_XX_CHARERR_RESET 0xf +#define FCN_XX_DISPERR_LBN 0 +#define FCN_XX_DISPERR_WIDTH 4 +#define FCN_XX_DISPERR_RESET 0xf + +/* XGXS/XAUI powerdown/reset register */ +#define FCN_XX_PWR_RST_REG_MAC 0x10 +#define FCN_XX_PWRDND_EN_LBN 15 +#define FCN_XX_PWRDND_EN_WIDTH 1 +#define FCN_XX_PWRDNC_EN_LBN 14 +#define FCN_XX_PWRDNC_EN_WIDTH 1 +#define FCN_XX_PWRDNB_EN_LBN 13 +#define FCN_XX_PWRDNB_EN_WIDTH 1 +#define FCN_XX_PWRDNA_EN_LBN 12 +#define FCN_XX_PWRDNA_EN_WIDTH 1 +#define FCN_XX_RSTPLLCD_EN_LBN 9 +#define FCN_XX_RSTPLLCD_EN_WIDTH 1 +#define FCN_XX_RSTPLLAB_EN_LBN 8 +#define FCN_XX_RSTPLLAB_EN_WIDTH 1 +#define FCN_XX_RESETD_EN_LBN 7 +#define FCN_XX_RESETD_EN_WIDTH 1 +#define FCN_XX_RESETC_EN_LBN 6 +#define FCN_XX_RESETC_EN_WIDTH 1 +#define FCN_XX_RESETB_EN_LBN 5 +#define FCN_XX_RESETB_EN_WIDTH 1 +#define FCN_XX_RESETA_EN_LBN 4 +#define FCN_XX_RESETA_EN_WIDTH 1 +#define FCN_XX_RSTXGXSRX_EN_LBN 2 +#define FCN_XX_RSTXGXSRX_EN_WIDTH 1 +#define FCN_XX_RSTXGXSTX_EN_LBN 1 +#define FCN_XX_RSTXGXSTX_EN_WIDTH 1 +#define FCN_XX_RST_XX_EN_LBN 0 +#define FCN_XX_RST_XX_EN_WIDTH 1 + + +/* XGXS/XAUI powerdown/reset control register */ +#define FCN_XX_SD_CTL_REG_MAC 0x11 +#define FCN_XX_TERMADJ1_LBN 17 +#define FCN_XX_TERMADJ1_WIDTH 1 +#define FCN_XX_TERMADJ0_LBN 16 +#define FCN_XX_TERMADJ0_WIDTH 1 +#define FCN_XX_HIDRVD_LBN 15 +#define FCN_XX_HIDRVD_WIDTH 1 +#define FCN_XX_LODRVD_LBN 14 +#define FCN_XX_LODRVD_WIDTH 1 +#define FCN_XX_HIDRVC_LBN 13 +#define FCN_XX_HIDRVC_WIDTH 1 +#define FCN_XX_LODRVC_LBN 12 +#define FCN_XX_LODRVC_WIDTH 1 +#define FCN_XX_HIDRVB_LBN 11 +#define FCN_XX_HIDRVB_WIDTH 1 +#define FCN_XX_LODRVB_LBN 10 +#define FCN_XX_LODRVB_WIDTH 1 +#define FCN_XX_HIDRVA_LBN 9 +#define FCN_XX_HIDRVA_WIDTH 1 +#define FCN_XX_LODRVA_LBN 8 +#define FCN_XX_LODRVA_WIDTH 1 +#define FCN_XX_LPBKD_LBN 3 +#define FCN_XX_LPBKD_WIDTH 1 +#define FCN_XX_LPBKC_LBN 2 +#define FCN_XX_LPBKC_WIDTH 1 +#define FCN_XX_LPBKB_LBN 1 +#define FCN_XX_LPBKB_WIDTH 1 +#define FCN_XX_LPBKA_LBN 0 +#define FCN_XX_LPBKA_WIDTH 1 + +#define FCN_XX_TXDRV_CTL_REG_MAC 0x12 +#define FCN_XX_DEQD_LBN 28 +#define FCN_XX_DEQD_WIDTH 4 +#define FCN_XX_DEQC_LBN 24 +#define FCN_XX_DEQC_WIDTH 4 +#define FCN_XX_DEQB_LBN 20 +#define FCN_XX_DEQB_WIDTH 4 +#define FCN_XX_DEQA_LBN 16 +#define FCN_XX_DEQA_WIDTH 4 +#define FCN_XX_DTXD_LBN 12 +#define FCN_XX_DTXD_WIDTH 4 +#define FCN_XX_DTXC_LBN 8 +#define FCN_XX_DTXC_WIDTH 4 +#define FCN_XX_DTXB_LBN 4 +#define FCN_XX_DTXB_WIDTH 4 +#define FCN_XX_DTXA_LBN 0 +#define FCN_XX_DTXA_WIDTH 4 + +/* Receive filter table */ +#define FCN_RX_FILTER_TBL0 0xF00000 + +/* Receive descriptor pointer table */ +#define FCN_RX_DESC_PTR_TBL_KER_A1 0x11800 +#define FCN_RX_DESC_PTR_TBL_KER_B0 0xF40000 +#define FCN_RX_ISCSI_DDIG_EN_LBN 88 +#define FCN_RX_ISCSI_DDIG_EN_WIDTH 1 +#define FCN_RX_ISCSI_HDIG_EN_LBN 87 +#define FCN_RX_ISCSI_HDIG_EN_WIDTH 1 +#define FCN_RX_DESCQ_BUF_BASE_ID_LBN 36 +#define FCN_RX_DESCQ_BUF_BASE_ID_WIDTH 20 +#define FCN_RX_DESCQ_EVQ_ID_LBN 24 +#define FCN_RX_DESCQ_EVQ_ID_WIDTH 12 +#define FCN_RX_DESCQ_OWNER_ID_LBN 10 +#define FCN_RX_DESCQ_OWNER_ID_WIDTH 14 +#define FCN_RX_DESCQ_SIZE_LBN 3 +#define FCN_RX_DESCQ_SIZE_WIDTH 2 +#define FCN_RX_DESCQ_SIZE_4K 3 +#define FCN_RX_DESCQ_SIZE_2K 2 +#define FCN_RX_DESCQ_SIZE_1K 1 +#define FCN_RX_DESCQ_SIZE_512 0 +#define FCN_RX_DESCQ_TYPE_LBN 2 +#define FCN_RX_DESCQ_TYPE_WIDTH 1 +#define FCN_RX_DESCQ_JUMBO_LBN 1 +#define FCN_RX_DESCQ_JUMBO_WIDTH 1 +#define FCN_RX_DESCQ_EN_LBN 0 +#define FCN_RX_DESCQ_EN_WIDTH 1 + +/* Transmit descriptor pointer table */ +#define FCN_TX_DESC_PTR_TBL_KER_A1 0x11900 +#define FCN_TX_DESC_PTR_TBL_KER_B0 0xF50000 +#define FCN_TX_NON_IP_DROP_DIS_B0_LBN 91 +#define FCN_TX_NON_IP_DROP_DIS_B0_WIDTH 1 +#define FCN_TX_DESCQ_EN_LBN 88 +#define FCN_TX_DESCQ_EN_WIDTH 1 +#define FCN_TX_ISCSI_DDIG_EN_LBN 87 +#define FCN_TX_ISCSI_DDIG_EN_WIDTH 1 +#define FCN_TX_ISCSI_HDIG_EN_LBN 86 +#define FCN_TX_ISCSI_HDIG_EN_WIDTH 1 +#define FCN_TX_DESCQ_BUF_BASE_ID_LBN 36 +#define FCN_TX_DESCQ_BUF_BASE_ID_WIDTH 20 +#define FCN_TX_DESCQ_EVQ_ID_LBN 24 +#define FCN_TX_DESCQ_EVQ_ID_WIDTH 12 +#define FCN_TX_DESCQ_OWNER_ID_LBN 10 +#define FCN_TX_DESCQ_OWNER_ID_WIDTH 14 +#define FCN_TX_DESCQ_SIZE_LBN 3 +#define FCN_TX_DESCQ_SIZE_WIDTH 2 +#define FCN_TX_DESCQ_SIZE_4K 3 +#define FCN_TX_DESCQ_SIZE_2K 2 +#define FCN_TX_DESCQ_SIZE_1K 1 +#define FCN_TX_DESCQ_SIZE_512 0 +#define FCN_TX_DESCQ_TYPE_LBN 1 +#define FCN_TX_DESCQ_TYPE_WIDTH 2 +#define FCN_TX_DESCQ_FLUSH_LBN 0 +#define FCN_TX_DESCQ_FLUSH_WIDTH 1 + +/* Event queue pointer */ +#define FCN_EVQ_PTR_TBL_KER_A1 0x11a00 +#define FCN_EVQ_PTR_TBL_KER_B0 0xf60000 +#define FCN_EVQ_EN_LBN 23 +#define FCN_EVQ_EN_WIDTH 1 +#define FCN_EVQ_SIZE_LBN 20 +#define FCN_EVQ_SIZE_WIDTH 3 +#define FCN_EVQ_SIZE_32K 6 +#define FCN_EVQ_SIZE_16K 5 +#define FCN_EVQ_SIZE_8K 4 +#define FCN_EVQ_SIZE_4K 3 +#define FCN_EVQ_SIZE_2K 2 +#define FCN_EVQ_SIZE_1K 1 +#define FCN_EVQ_SIZE_512 0 +#define FCN_EVQ_BUF_BASE_ID_LBN 0 +#define FCN_EVQ_BUF_BASE_ID_WIDTH 20 + +/* RSS indirection table */ +#define FCN_RX_RSS_INDIR_TBL_B0 0xFB0000 + +/* Event queue read pointer */ +#define FCN_EVQ_RPTR_REG_KER_A1 0x11b00 +#define FCN_EVQ_RPTR_REG_KER_B0 0xfa0000 +#define FCN_EVQ_RPTR_LBN 0 +#define FCN_EVQ_RPTR_WIDTH 14 +#define FCN_EVQ_RPTR_REG_KER_DWORD_A1 ( FCN_EVQ_RPTR_REG_KER_A1 + 0 ) +#define FCN_EVQ_RPTR_REG_KER_DWORD_B0 ( FCN_EVQ_RPTR_REG_KER_B0 + 0 ) +#define FCN_EVQ_RPTR_DWORD_LBN 0 +#define FCN_EVQ_RPTR_DWORD_WIDTH 14 + +/* Special buffer descriptors */ +#define FCN_BUF_FULL_TBL_KER_A1 0x18000 +#define FCN_BUF_FULL_TBL_KER_B0 0x800000 +#define FCN_IP_DAT_BUF_SIZE_LBN 50 +#define FCN_IP_DAT_BUF_SIZE_WIDTH 1 +#define FCN_IP_DAT_BUF_SIZE_8K 1 +#define FCN_IP_DAT_BUF_SIZE_4K 0 +#define FCN_BUF_ADR_FBUF_LBN 14 +#define FCN_BUF_ADR_FBUF_WIDTH 34 +#define FCN_BUF_OWNER_ID_FBUF_LBN 0 +#define FCN_BUF_OWNER_ID_FBUF_WIDTH 14 + +/** Offset of a GMAC register within Falcon */ +#define FALCON_GMAC_REG( efab, mac_reg ) \ + ( FALCON_GMAC_REGBANK + \ + ( (mac_reg) * FALCON_GMAC_REG_SIZE ) ) + +/** Offset of an XMAC register within Falcon */ +#define FALCON_XMAC_REG( efab_port, mac_reg ) \ + ( FALCON_XMAC_REGBANK + \ + ( (mac_reg) * FALCON_XMAC_REG_SIZE ) ) + +#define FCN_MAC_DATA_LBN 0 +#define FCN_MAC_DATA_WIDTH 32 + +/* Transmit descriptor */ +#define FCN_TX_KER_PORT_LBN 63 +#define FCN_TX_KER_PORT_WIDTH 1 +#define FCN_TX_KER_BYTE_CNT_LBN 48 +#define FCN_TX_KER_BYTE_CNT_WIDTH 14 +#define FCN_TX_KER_BUF_ADR_LBN 0 +#define FCN_TX_KER_BUF_ADR_WIDTH EFAB_DMA_TYPE_WIDTH ( 46 ) + + +/* Receive descriptor */ +#define FCN_RX_KER_BUF_SIZE_LBN 48 +#define FCN_RX_KER_BUF_SIZE_WIDTH 14 +#define FCN_RX_KER_BUF_ADR_LBN 0 +#define FCN_RX_KER_BUF_ADR_WIDTH EFAB_DMA_TYPE_WIDTH ( 46 ) + +/* Event queue entries */ +#define FCN_EV_CODE_LBN 60 +#define FCN_EV_CODE_WIDTH 4 +#define FCN_RX_IP_EV_DECODE 0 +#define FCN_TX_IP_EV_DECODE 2 +#define FCN_DRIVER_EV_DECODE 5 + +/* Receive events */ +#define FCN_RX_EV_PKT_OK_LBN 56 +#define FCN_RX_EV_PKT_OK_WIDTH 1 +#define FCN_RX_PORT_LBN 30 +#define FCN_RX_PORT_WIDTH 1 +#define FCN_RX_EV_BYTE_CNT_LBN 16 +#define FCN_RX_EV_BYTE_CNT_WIDTH 14 +#define FCN_RX_EV_DESC_PTR_LBN 0 +#define FCN_RX_EV_DESC_PTR_WIDTH 12 + +/* Transmit events */ +#define FCN_TX_EV_DESC_PTR_LBN 0 +#define FCN_TX_EV_DESC_PTR_WIDTH 12 + +/******************************************************************************* + * + * + * Low-level hardware access + * + * + *******************************************************************************/ + +#define FCN_REVISION_REG(efab, reg) \ + ( ( efab->pci_revision == FALCON_REV_B0 ) ? reg ## _B0 : reg ## _A1 ) + +#define EFAB_SET_OWORD_FIELD_VER(efab, reg, field, val) \ + if ( efab->pci_revision == FALCON_REV_B0 ) \ + EFAB_SET_OWORD_FIELD ( reg, field ## _B0, val ); \ + else \ + EFAB_SET_OWORD_FIELD ( reg, field ## _A1, val ); + +#if FALCON_USE_IO_BAR + +/* Write dword via the I/O BAR */ +static inline void _falcon_writel ( struct efab_nic *efab, uint32_t value, + unsigned int reg ) { + outl ( reg, efab->iobase + FCN_IOM_IND_ADR_REG ); + outl ( value, efab->iobase + FCN_IOM_IND_DAT_REG ); +} + +/* Read dword via the I/O BAR */ +static inline uint32_t _falcon_readl ( struct efab_nic *efab, + unsigned int reg ) { + outl ( reg, efab->iobase + FCN_IOM_IND_ADR_REG ); + return inl ( efab->iobase + FCN_IOM_IND_DAT_REG ); +} + +#else /* FALCON_USE_IO_BAR */ + +#define _falcon_writel( efab, value, reg ) \ + writel ( (value), (efab)->membase + (reg) ) +#define _falcon_readl( efab, reg ) readl ( (efab)->membase + (reg) ) + +#endif /* FALCON_USE_IO_BAR */ + +/** + * Write to a Falcon register + * + */ +static inline void +falcon_write ( struct efab_nic *efab, efab_oword_t *value, unsigned int reg ) +{ + + EFAB_REGDUMP ( "Writing register %x with " EFAB_OWORD_FMT "\n", + reg, EFAB_OWORD_VAL ( *value ) ); + + _falcon_writel ( efab, value->u32[0], reg + 0 ); + _falcon_writel ( efab, value->u32[1], reg + 4 ); + _falcon_writel ( efab, value->u32[2], reg + 8 ); + wmb(); + _falcon_writel ( efab, value->u32[3], reg + 12 ); + wmb(); +} + +/** + * Write to Falcon SRAM + * + */ +static inline void +falcon_write_sram ( struct efab_nic *efab, efab_qword_t *value, + unsigned int index ) +{ + unsigned int reg = ( FCN_REVISION_REG ( efab, FCN_BUF_FULL_TBL_KER ) + + ( index * sizeof ( *value ) ) ); + + EFAB_REGDUMP ( "Writing SRAM register %x with " EFAB_QWORD_FMT "\n", + reg, EFAB_QWORD_VAL ( *value ) ); + + _falcon_writel ( efab, value->u32[0], reg + 0 ); + _falcon_writel ( efab, value->u32[1], reg + 4 ); + wmb(); +} + +/** + * Write dword to Falcon register that allows partial writes + * + */ +static inline void +falcon_writel ( struct efab_nic *efab, efab_dword_t *value, unsigned int reg ) +{ + EFAB_REGDUMP ( "Writing partial register %x with " EFAB_DWORD_FMT "\n", + reg, EFAB_DWORD_VAL ( *value ) ); + _falcon_writel ( efab, value->u32[0], reg ); +} + +/** + * Read from a Falcon register + * + */ +static inline void +falcon_read ( struct efab_nic *efab, efab_oword_t *value, unsigned int reg ) +{ + value->u32[0] = _falcon_readl ( efab, reg + 0 ); + wmb(); + value->u32[1] = _falcon_readl ( efab, reg + 4 ); + value->u32[2] = _falcon_readl ( efab, reg + 8 ); + value->u32[3] = _falcon_readl ( efab, reg + 12 ); + + EFAB_REGDUMP ( "Read from register %x, got " EFAB_OWORD_FMT "\n", + reg, EFAB_OWORD_VAL ( *value ) ); +} + +/** + * Read from Falcon SRAM + * + */ +static inline void +falcon_read_sram ( struct efab_nic *efab, efab_qword_t *value, + unsigned int index ) +{ + unsigned int reg = ( FCN_REVISION_REG ( efab, FCN_BUF_FULL_TBL_KER ) + + ( index * sizeof ( *value ) ) ); + + value->u32[0] = _falcon_readl ( efab, reg + 0 ); + value->u32[1] = _falcon_readl ( efab, reg + 4 ); + EFAB_REGDUMP ( "Read from SRAM register %x, got " EFAB_QWORD_FMT "\n", + reg, EFAB_QWORD_VAL ( *value ) ); +} + +/** + * Read dword from a portion of a Falcon register + * + */ +static inline void +falcon_readl ( struct efab_nic *efab, efab_dword_t *value, unsigned int reg ) +{ + value->u32[0] = _falcon_readl ( efab, reg ); + EFAB_REGDUMP ( "Read from register %x, got " EFAB_DWORD_FMT "\n", + reg, EFAB_DWORD_VAL ( *value ) ); +} + +#define FCN_DUMP_REG( efab, _reg ) do { \ + efab_oword_t reg; \ + falcon_read ( efab, ®, _reg ); \ + EFAB_LOG ( #_reg " = " EFAB_OWORD_FMT "\n", \ + EFAB_OWORD_VAL ( reg ) ); \ + } while ( 0 ); + +#define FCN_DUMP_MAC_REG( efab, _mac_reg ) do { \ + efab_dword_t reg; \ + efab->mac_op->mac_readl ( efab, ®, _mac_reg ); \ + EFAB_LOG ( #_mac_reg " = " EFAB_DWORD_FMT "\n", \ + EFAB_DWORD_VAL ( reg ) ); \ + } while ( 0 ); + +/** + * See if an event is present + * + * @v event Falcon event structure + * @ret True An event is pending + * @ret False No event is pending + * + * We check both the high and low dword of the event for all ones. We + * wrote all ones when we cleared the event, and no valid event can + * have all ones in either its high or low dwords. This approach is + * robust against reordering. + * + * Note that using a single 64-bit comparison is incorrect; even + * though the CPU read will be atomic, the DMA write may not be. + */ +static inline int +falcon_event_present ( falcon_event_t* event ) +{ + return ( ! ( EFAB_DWORD_IS_ALL_ONES ( event->dword[0] ) | + EFAB_DWORD_IS_ALL_ONES ( event->dword[1] ) ) ); +} + +static void +falcon_eventq_read_ack ( struct efab_nic *efab, struct efab_ev_queue *ev_queue ) +{ + efab_dword_t reg; + + EFAB_POPULATE_DWORD_1 ( reg, FCN_EVQ_RPTR_DWORD, ev_queue->read_ptr ); + falcon_writel ( efab, ®, + FCN_REVISION_REG ( efab, FCN_EVQ_RPTR_REG_KER_DWORD ) ); +} + +#if 0 +/** + * Dump register contents (for debugging) + * + * Marked as static inline so that it will not be compiled in if not + * used. + */ +static inline void +falcon_dump_regs ( struct efab_nic *efab ) +{ + FCN_DUMP_REG ( efab, FCN_INT_EN_REG_KER ); + FCN_DUMP_REG ( efab, FCN_INT_ADR_REG_KER ); + FCN_DUMP_REG ( efab, FCN_GLB_CTL_REG_KER ); + FCN_DUMP_REG ( efab, FCN_TIMER_CMD_REG_KER ); + FCN_DUMP_REG ( efab, FCN_SRM_RX_DC_CFG_REG_KER ); + FCN_DUMP_REG ( efab, FCN_SRM_TX_DC_CFG_REG_KER ); + FCN_DUMP_REG ( efab, FCN_RX_FILTER_CTL_REG_KER ); + FCN_DUMP_REG ( efab, FCN_RX_DC_CFG_REG_KER ); + FCN_DUMP_REG ( efab, FCN_TX_DC_CFG_REG_KER ); + FCN_DUMP_REG ( efab, FCN_MAC0_CTRL_REG_KER ); + FCN_DUMP_REG ( efab, FCN_MAC1_CTRL_REG_KER ); + FCN_DUMP_REG ( efab, FCN_REVISION_REG ( efab, FCN_RX_DESC_PTR_TBL_KER ) ); + FCN_DUMP_REG ( efab, FCN_REVISION_REG ( efab, FCN_TX_DESC_PTR_TBL_KER ) ); + FCN_DUMP_REG ( efab, FCN_REVISION_REG ( efab, FCN_EVQ_PTR_TBL_KER ) ); + FCN_DUMP_MAC_REG ( efab, GM_CFG1_REG_MAC ); + FCN_DUMP_MAC_REG ( efab, GM_CFG2_REG_MAC ); + FCN_DUMP_MAC_REG ( efab, GM_MAX_FLEN_REG_MAC ); + FCN_DUMP_MAC_REG ( efab, GM_MII_MGMT_CFG_REG_MAC ); + FCN_DUMP_MAC_REG ( efab, GM_ADR1_REG_MAC ); + FCN_DUMP_MAC_REG ( efab, GM_ADR2_REG_MAC ); + FCN_DUMP_MAC_REG ( efab, GMF_CFG0_REG_MAC ); + FCN_DUMP_MAC_REG ( efab, GMF_CFG1_REG_MAC ); + FCN_DUMP_MAC_REG ( efab, GMF_CFG2_REG_MAC ); + FCN_DUMP_MAC_REG ( efab, GMF_CFG3_REG_MAC ); + FCN_DUMP_MAC_REG ( efab, GMF_CFG4_REG_MAC ); + FCN_DUMP_MAC_REG ( efab, GMF_CFG5_REG_MAC ); +} +#endif + +static void +falcon_interrupts ( struct efab_nic *efab, int enabled, int force ) +{ + efab_oword_t int_en_reg_ker; + + EFAB_POPULATE_OWORD_2 ( int_en_reg_ker, + FCN_KER_INT_KER, force, + FCN_DRV_INT_EN_KER, enabled ); + falcon_write ( efab, &int_en_reg_ker, FCN_INT_EN_REG_KER ); +} + +/******************************************************************************* + * + * + * SPI access + * + * + *******************************************************************************/ + + +/** Maximum length for a single SPI transaction */ +#define FALCON_SPI_MAX_LEN 16 + +static int +falcon_spi_wait ( struct efab_nic *efab ) +{ + efab_oword_t reg; + int count; + + count = 0; + do { + udelay ( 100 ); + falcon_read ( efab, ®, FCN_EE_SPI_HCMD_REG ); + if ( EFAB_OWORD_FIELD ( reg, FCN_EE_SPI_HCMD_CMD_EN ) == 0 ) + return 0; + } while ( ++count < 1000 ); + + EFAB_ERR ( "Timed out waiting for SPI\n" ); + return -ETIMEDOUT; +} + +static int +falcon_spi_rw ( struct spi_bus* bus, struct spi_device *device, + unsigned int command, int address, + const void* data_out, void *data_in, size_t len ) +{ + struct efab_nic *efab = container_of ( bus, struct efab_nic, spi_bus ); + int address_len, rc, device_id, read_cmd; + efab_oword_t reg; + + /* falcon_init_spi_device() should have reduced the block size + * down so this constraint holds */ + assert ( len <= FALCON_SPI_MAX_LEN ); + + /* Is this the FLASH or EEPROM device? */ + if ( device == &efab->spi_flash ) + device_id = FCN_EE_SPI_FLASH; + else if ( device == &efab->spi_eeprom ) + device_id = FCN_EE_SPI_EEPROM; + else { + EFAB_ERR ( "Unknown device %p\n", device ); + return -EINVAL; + } + + EFAB_TRACE ( "Executing spi command %d on device %d at %d for %zd bytes\n", + command, device_id, address, len ); + + /* The bus must be idle */ + rc = falcon_spi_wait ( efab ); + if ( rc ) + goto fail1; + + /* Copy data out */ + if ( data_out ) { + memcpy ( ®, data_out, len ); + falcon_write ( efab, ®, FCN_EE_SPI_HDATA_REG ); + } + + /* Program address register */ + if ( address >= 0 ) { + EFAB_POPULATE_OWORD_1 ( reg, FCN_EE_SPI_HADR_ADR, address ); + falcon_write ( efab, ®, FCN_EE_SPI_HADR_REG ); + } + + /* Issue command */ + address_len = ( address >= 0 ) ? device->address_len / 8 : 0; + read_cmd = ( data_in ? FCN_EE_SPI_READ : FCN_EE_SPI_WRITE ); + EFAB_POPULATE_OWORD_7 ( reg, + FCN_EE_SPI_HCMD_CMD_EN, 1, + FCN_EE_SPI_HCMD_SF_SEL, device_id, + FCN_EE_SPI_HCMD_DABCNT, len, + FCN_EE_SPI_HCMD_READ, read_cmd, + FCN_EE_SPI_HCMD_DUBCNT, 0, + FCN_EE_SPI_HCMD_ADBCNT, address_len, + FCN_EE_SPI_HCMD_ENC, command ); + falcon_write ( efab, ®, FCN_EE_SPI_HCMD_REG ); + + /* Wait for the command to complete */ + rc = falcon_spi_wait ( efab ); + if ( rc ) + goto fail2; + + /* Copy data in */ + if ( data_in ) { + falcon_read ( efab, ®, FCN_EE_SPI_HDATA_REG ); + memcpy ( data_in, ®, len ); + } + + return 0; + +fail2: +fail1: + EFAB_ERR ( "Failed SPI command %d to device %d address 0x%x len 0x%zx\n", + command, device_id, address, len ); + + return rc; +} + +/** Portion of EEPROM available for non-volatile options */ +static struct nvo_fragment falcon_nvo_fragments[] = { + { 0x100, 0xf0 }, + { 0, 0 } +}; + +/******************************************************************************* + * + * + * Falcon bit-bashed I2C interface + * + * + *******************************************************************************/ + +static void +falcon_i2c_bit_write ( struct bit_basher *basher, unsigned int bit_id, + unsigned long data ) +{ + struct efab_nic *efab = container_of ( basher, struct efab_nic, + i2c_bb.basher ); + efab_oword_t reg; + + falcon_read ( efab, ®, FCN_GPIO_CTL_REG_KER ); + switch ( bit_id ) { + case I2C_BIT_SCL: + EFAB_SET_OWORD_FIELD ( reg, FCN_GPIO0_OEN, ( data ? 0 : 1 ) ); + break; + case I2C_BIT_SDA: + EFAB_SET_OWORD_FIELD ( reg, FCN_GPIO3_OEN, ( data ? 0 : 1 ) ); + break; + default: + EFAB_ERR ( "%s bit=%d\n", __func__, bit_id ); + break; + } + + falcon_write ( efab, ®, FCN_GPIO_CTL_REG_KER ); +} + +static int +falcon_i2c_bit_read ( struct bit_basher *basher, unsigned int bit_id ) +{ + struct efab_nic *efab = container_of ( basher, struct efab_nic, + i2c_bb.basher ); + efab_oword_t reg; + + falcon_read ( efab, ®, FCN_GPIO_CTL_REG_KER ); + switch ( bit_id ) { + case I2C_BIT_SCL: + return EFAB_OWORD_FIELD ( reg, FCN_GPIO0_IN ); + break; + case I2C_BIT_SDA: + return EFAB_OWORD_FIELD ( reg, FCN_GPIO3_IN ); + break; + default: + EFAB_ERR ( "%s bit=%d\n", __func__, bit_id ); + break; + } + + return -1; +} + +static struct bit_basher_operations falcon_i2c_bit_ops = { + .read = falcon_i2c_bit_read, + .write = falcon_i2c_bit_write, +}; + + +/******************************************************************************* + * + * + * MDIO access + * + * + *******************************************************************************/ + +static int +falcon_gmii_wait ( struct efab_nic *efab ) +{ + efab_dword_t md_stat; + int count; + + /* wait upto 10ms */ + for (count = 0; count < 1000; count++) { + falcon_readl ( efab, &md_stat, FCN_MD_STAT_REG_KER ); + if ( EFAB_DWORD_FIELD ( md_stat, FCN_MD_BSY ) == 0 ) { + if ( EFAB_DWORD_FIELD ( md_stat, FCN_MD_LNFL ) != 0 || + EFAB_DWORD_FIELD ( md_stat, FCN_MD_BSERR ) != 0 ) { + EFAB_ERR ( "Error from GMII access " + EFAB_DWORD_FMT"\n", + EFAB_DWORD_VAL ( md_stat )); + return -EIO; + } + return 0; + } + udelay(10); + } + + EFAB_ERR ( "Timed out waiting for GMII\n" ); + return -ETIMEDOUT; +} + +static void +falcon_mdio_write ( struct efab_nic *efab, int device, + int location, int value ) +{ + efab_oword_t reg; + + EFAB_TRACE ( "Writing GMII %d register %02x with %04x\n", + device, location, value ); + + /* Check MII not currently being accessed */ + if ( falcon_gmii_wait ( efab ) ) + return; + + /* Write the address/ID register */ + EFAB_POPULATE_OWORD_1 ( reg, FCN_MD_PHY_ADR, location ); + falcon_write ( efab, ®, FCN_MD_PHY_ADR_REG_KER ); + + if ( efab->phy_10g ) { + /* clause45 */ + EFAB_POPULATE_OWORD_2 ( reg, + FCN_MD_PRT_ADR, efab->phy_addr, + FCN_MD_DEV_ADR, device ); + } + else { + /* clause22 */ + assert ( device == 0 ); + + EFAB_POPULATE_OWORD_2 ( reg, + FCN_MD_PRT_ADR, efab->phy_addr, + FCN_MD_DEV_ADR, location ); + } + falcon_write ( efab, ®, FCN_MD_ID_REG_KER ); + + + /* Write data */ + EFAB_POPULATE_OWORD_1 ( reg, FCN_MD_TXD, value ); + falcon_write ( efab, ®, FCN_MD_TXD_REG_KER ); + + EFAB_POPULATE_OWORD_2 ( reg, + FCN_MD_WRC, 1, + FCN_MD_GC, ( efab->phy_10g ? 0 : 1 ) ); + falcon_write ( efab, ®, FCN_MD_CS_REG_KER ); + + /* Wait for data to be written */ + if ( falcon_gmii_wait ( efab ) ) { + /* Abort the write operation */ + EFAB_POPULATE_OWORD_2 ( reg, + FCN_MD_WRC, 0, + FCN_MD_GC, 1); + falcon_write ( efab, ®, FCN_MD_CS_REG_KER ); + udelay(10); + } +} + +static int +falcon_mdio_read ( struct efab_nic *efab, int device, int location ) +{ + efab_oword_t reg; + int value; + + /* Check MII not currently being accessed */ + if ( falcon_gmii_wait ( efab ) ) + return -1; + + if ( efab->phy_10g ) { + /* clause45 */ + EFAB_POPULATE_OWORD_1 ( reg, FCN_MD_PHY_ADR, location ); + falcon_write ( efab, ®, FCN_MD_PHY_ADR_REG_KER ); + + EFAB_POPULATE_OWORD_2 ( reg, + FCN_MD_PRT_ADR, efab->phy_addr, + FCN_MD_DEV_ADR, device ); + falcon_write ( efab, ®, FCN_MD_ID_REG_KER); + + /* request data to be read */ + EFAB_POPULATE_OWORD_2 ( reg, + FCN_MD_RDC, 1, + FCN_MD_GC, 0 ); + } + else { + /* clause22 */ + assert ( device == 0 ); + + EFAB_POPULATE_OWORD_2 ( reg, + FCN_MD_PRT_ADR, efab->phy_addr, + FCN_MD_DEV_ADR, location ); + falcon_write ( efab, ®, FCN_MD_ID_REG_KER ); + + /* Request data to be read */ + EFAB_POPULATE_OWORD_2 ( reg, + FCN_MD_RIC, 1, + FCN_MD_GC, 1 ); + } + + falcon_write ( efab, ®, FCN_MD_CS_REG_KER ); + + /* Wait for data to become available */ + if ( falcon_gmii_wait ( efab ) ) { + /* Abort the read operation */ + EFAB_POPULATE_OWORD_2 ( reg, + FCN_MD_RIC, 0, + FCN_MD_GC, 1 ); + falcon_write ( efab, ®, FCN_MD_CS_REG_KER ); + udelay ( 10 ); + value = -1; + } + else { + /* Read the data */ + falcon_read ( efab, ®, FCN_MD_RXD_REG_KER ); + value = EFAB_OWORD_FIELD ( reg, FCN_MD_RXD ); + } + + EFAB_TRACE ( "Read from GMII %d register %02x, got %04x\n", + device, location, value ); + + return value; +} + +/******************************************************************************* + * + * + * MAC wrapper + * + * + *******************************************************************************/ + +static void +falcon_reconfigure_mac_wrapper ( struct efab_nic *efab ) +{ + efab_oword_t reg; + int link_speed; + + if ( efab->link_options & LPA_10000 ) { + link_speed = 0x3; + } else if ( efab->link_options & LPA_1000 ) { + link_speed = 0x2; + } else if ( efab->link_options & LPA_100 ) { + link_speed = 0x1; + } else { + link_speed = 0x0; + } + EFAB_POPULATE_OWORD_5 ( reg, + FCN_MAC_XOFF_VAL, 0xffff /* datasheet */, + FCN_MAC_BCAD_ACPT, 1, + FCN_MAC_UC_PROM, 0, + FCN_MAC_LINK_STATUS, 1, + FCN_MAC_SPEED, link_speed ); + + falcon_write ( efab, ®, FCN_MAC0_CTRL_REG_KER ); +} + +/******************************************************************************* + * + * + * GMAC handling + * + * + *******************************************************************************/ + +/* GMAC configuration register 1 */ +#define GM_CFG1_REG_MAC 0x00 +#define GM_SW_RST_LBN 31 +#define GM_SW_RST_WIDTH 1 +#define GM_RX_FC_EN_LBN 5 +#define GM_RX_FC_EN_WIDTH 1 +#define GM_TX_FC_EN_LBN 4 +#define GM_TX_FC_EN_WIDTH 1 +#define GM_RX_EN_LBN 2 +#define GM_RX_EN_WIDTH 1 +#define GM_TX_EN_LBN 0 +#define GM_TX_EN_WIDTH 1 + +/* GMAC configuration register 2 */ +#define GM_CFG2_REG_MAC 0x01 +#define GM_PAMBL_LEN_LBN 12 +#define GM_PAMBL_LEN_WIDTH 4 +#define GM_IF_MODE_LBN 8 +#define GM_IF_MODE_WIDTH 2 +#define GM_PAD_CRC_EN_LBN 2 +#define GM_PAD_CRC_EN_WIDTH 1 +#define GM_FD_LBN 0 +#define GM_FD_WIDTH 1 + +/* GMAC maximum frame length register */ +#define GM_MAX_FLEN_REG_MAC 0x04 +#define GM_MAX_FLEN_LBN 0 +#define GM_MAX_FLEN_WIDTH 16 + +/* GMAC MII management configuration register */ +#define GM_MII_MGMT_CFG_REG_MAC 0x08 +#define GM_MGMT_CLK_SEL_LBN 0 +#define GM_MGMT_CLK_SEL_WIDTH 3 + +/* GMAC MII management command register */ +#define GM_MII_MGMT_CMD_REG_MAC 0x09 +#define GM_MGMT_SCAN_CYC_LBN 1 +#define GM_MGMT_SCAN_CYC_WIDTH 1 +#define GM_MGMT_RD_CYC_LBN 0 +#define GM_MGMT_RD_CYC_WIDTH 1 + +/* GMAC MII management address register */ +#define GM_MII_MGMT_ADR_REG_MAC 0x0a +#define GM_MGMT_PHY_ADDR_LBN 8 +#define GM_MGMT_PHY_ADDR_WIDTH 5 +#define GM_MGMT_REG_ADDR_LBN 0 +#define GM_MGMT_REG_ADDR_WIDTH 5 + +/* GMAC MII management control register */ +#define GM_MII_MGMT_CTL_REG_MAC 0x0b +#define GM_MGMT_CTL_LBN 0 +#define GM_MGMT_CTL_WIDTH 16 + +/* GMAC MII management status register */ +#define GM_MII_MGMT_STAT_REG_MAC 0x0c +#define GM_MGMT_STAT_LBN 0 +#define GM_MGMT_STAT_WIDTH 16 + +/* GMAC MII management indicators register */ +#define GM_MII_MGMT_IND_REG_MAC 0x0d +#define GM_MGMT_BUSY_LBN 0 +#define GM_MGMT_BUSY_WIDTH 1 + +/* GMAC station address register 1 */ +#define GM_ADR1_REG_MAC 0x10 +#define GM_HWADDR_5_LBN 24 +#define GM_HWADDR_5_WIDTH 8 +#define GM_HWADDR_4_LBN 16 +#define GM_HWADDR_4_WIDTH 8 +#define GM_HWADDR_3_LBN 8 +#define GM_HWADDR_3_WIDTH 8 +#define GM_HWADDR_2_LBN 0 +#define GM_HWADDR_2_WIDTH 8 + +/* GMAC station address register 2 */ +#define GM_ADR2_REG_MAC 0x11 +#define GM_HWADDR_1_LBN 24 +#define GM_HWADDR_1_WIDTH 8 +#define GM_HWADDR_0_LBN 16 +#define GM_HWADDR_0_WIDTH 8 + +/* GMAC FIFO configuration register 0 */ +#define GMF_CFG0_REG_MAC 0x12 +#define GMF_FTFENREQ_LBN 12 +#define GMF_FTFENREQ_WIDTH 1 +#define GMF_STFENREQ_LBN 11 +#define GMF_STFENREQ_WIDTH 1 +#define GMF_FRFENREQ_LBN 10 +#define GMF_FRFENREQ_WIDTH 1 +#define GMF_SRFENREQ_LBN 9 +#define GMF_SRFENREQ_WIDTH 1 +#define GMF_WTMENREQ_LBN 8 +#define GMF_WTMENREQ_WIDTH 1 + +/* GMAC FIFO configuration register 1 */ +#define GMF_CFG1_REG_MAC 0x13 +#define GMF_CFGFRTH_LBN 16 +#define GMF_CFGFRTH_WIDTH 5 +#define GMF_CFGXOFFRTX_LBN 0 +#define GMF_CFGXOFFRTX_WIDTH 16 + +/* GMAC FIFO configuration register 2 */ +#define GMF_CFG2_REG_MAC 0x14 +#define GMF_CFGHWM_LBN 16 +#define GMF_CFGHWM_WIDTH 6 +#define GMF_CFGLWM_LBN 0 +#define GMF_CFGLWM_WIDTH 6 + +/* GMAC FIFO configuration register 3 */ +#define GMF_CFG3_REG_MAC 0x15 +#define GMF_CFGHWMFT_LBN 16 +#define GMF_CFGHWMFT_WIDTH 6 +#define GMF_CFGFTTH_LBN 0 +#define GMF_CFGFTTH_WIDTH 6 + +/* GMAC FIFO configuration register 4 */ +#define GMF_CFG4_REG_MAC 0x16 +#define GMF_HSTFLTRFRM_PAUSE_LBN 12 +#define GMF_HSTFLTRFRM_PAUSE_WIDTH 12 + +/* GMAC FIFO configuration register 5 */ +#define GMF_CFG5_REG_MAC 0x17 +#define GMF_CFGHDPLX_LBN 22 +#define GMF_CFGHDPLX_WIDTH 1 +#define GMF_CFGBYTMODE_LBN 19 +#define GMF_CFGBYTMODE_WIDTH 1 +#define GMF_HSTDRPLT64_LBN 18 +#define GMF_HSTDRPLT64_WIDTH 1 +#define GMF_HSTFLTRFRMDC_PAUSE_LBN 12 +#define GMF_HSTFLTRFRMDC_PAUSE_WIDTH 1 + +static void +falcon_gmac_writel ( struct efab_nic *efab, efab_dword_t *value, + unsigned int mac_reg ) +{ + efab_oword_t temp; + + EFAB_POPULATE_OWORD_1 ( temp, FCN_MAC_DATA, + EFAB_DWORD_FIELD ( *value, FCN_MAC_DATA ) ); + falcon_write ( efab, &temp, FALCON_GMAC_REG ( efab, mac_reg ) ); +} + +static void +falcon_gmac_readl ( struct efab_nic *efab, efab_dword_t *value, + unsigned int mac_reg ) +{ + efab_oword_t temp; + + falcon_read ( efab, &temp, FALCON_GMAC_REG ( efab, mac_reg ) ); + EFAB_POPULATE_DWORD_1 ( *value, FCN_MAC_DATA, + EFAB_OWORD_FIELD ( temp, FCN_MAC_DATA ) ); +} + +static void +mentormac_reset ( struct efab_nic *efab ) +{ + efab_dword_t reg; + + /* Take into reset */ + EFAB_POPULATE_DWORD_1 ( reg, GM_SW_RST, 1 ); + falcon_gmac_writel ( efab, ®, GM_CFG1_REG_MAC ); + udelay ( 1000 ); + + /* Take out of reset */ + EFAB_POPULATE_DWORD_1 ( reg, GM_SW_RST, 0 ); + falcon_gmac_writel ( efab, ®, GM_CFG1_REG_MAC ); + udelay ( 1000 ); + + /* Configure GMII interface so PHY is accessible. Note that + * GMII interface is connected only to port 0, and that on + * Falcon this is a no-op. + */ + EFAB_POPULATE_DWORD_1 ( reg, GM_MGMT_CLK_SEL, 0x4 ); + falcon_gmac_writel ( efab, ®, GM_MII_MGMT_CFG_REG_MAC ); + udelay ( 10 ); +} + +static void +mentormac_init ( struct efab_nic *efab ) +{ + int pause, if_mode, full_duplex, bytemode, half_duplex; + efab_dword_t reg; + + /* Configuration register 1 */ + pause = ( efab->link_options & LPA_PAUSE ) ? 1 : 0; + if ( ! ( efab->link_options & LPA_DUPLEX ) ) { + /* Half-duplex operation requires TX flow control */ + pause = 1; + } + EFAB_POPULATE_DWORD_4 ( reg, + GM_TX_EN, 1, + GM_TX_FC_EN, pause, + GM_RX_EN, 1, + GM_RX_FC_EN, 1 ); + falcon_gmac_writel ( efab, ®, GM_CFG1_REG_MAC ); + udelay ( 10 ); + + /* Configuration register 2 */ + if_mode = ( efab->link_options & LPA_1000 ) ? 2 : 1; + full_duplex = ( efab->link_options & LPA_DUPLEX ) ? 1 : 0; + EFAB_POPULATE_DWORD_4 ( reg, + GM_IF_MODE, if_mode, + GM_PAD_CRC_EN, 1, + GM_FD, full_duplex, + GM_PAMBL_LEN, 0x7 /* ? */ ); + falcon_gmac_writel ( efab, ®, GM_CFG2_REG_MAC ); + udelay ( 10 ); + + /* Max frame len register */ + EFAB_POPULATE_DWORD_1 ( reg, GM_MAX_FLEN, + EFAB_MAX_FRAME_LEN ( ETH_FRAME_LEN ) ); + falcon_gmac_writel ( efab, ®, GM_MAX_FLEN_REG_MAC ); + udelay ( 10 ); + + /* FIFO configuration register 0 */ + EFAB_POPULATE_DWORD_5 ( reg, + GMF_FTFENREQ, 1, + GMF_STFENREQ, 1, + GMF_FRFENREQ, 1, + GMF_SRFENREQ, 1, + GMF_WTMENREQ, 1 ); + falcon_gmac_writel ( efab, ®, GMF_CFG0_REG_MAC ); + udelay ( 10 ); + + /* FIFO configuration register 1 */ + EFAB_POPULATE_DWORD_2 ( reg, + GMF_CFGFRTH, 0x12, + GMF_CFGXOFFRTX, 0xffff ); + falcon_gmac_writel ( efab, ®, GMF_CFG1_REG_MAC ); + udelay ( 10 ); + + /* FIFO configuration register 2 */ + EFAB_POPULATE_DWORD_2 ( reg, + GMF_CFGHWM, 0x3f, + GMF_CFGLWM, 0xa ); + falcon_gmac_writel ( efab, ®, GMF_CFG2_REG_MAC ); + udelay ( 10 ); + + /* FIFO configuration register 3 */ + EFAB_POPULATE_DWORD_2 ( reg, + GMF_CFGHWMFT, 0x1c, + GMF_CFGFTTH, 0x08 ); + falcon_gmac_writel ( efab, ®, GMF_CFG3_REG_MAC ); + udelay ( 10 ); + + /* FIFO configuration register 4 */ + EFAB_POPULATE_DWORD_1 ( reg, GMF_HSTFLTRFRM_PAUSE, 1 ); + falcon_gmac_writel ( efab, ®, GMF_CFG4_REG_MAC ); + udelay ( 10 ); + + /* FIFO configuration register 5 */ + bytemode = ( efab->link_options & LPA_1000 ) ? 1 : 0; + half_duplex = ( efab->link_options & LPA_DUPLEX ) ? 0 : 1; + falcon_gmac_readl ( efab, ®, GMF_CFG5_REG_MAC ); + EFAB_SET_DWORD_FIELD ( reg, GMF_CFGBYTMODE, bytemode ); + EFAB_SET_DWORD_FIELD ( reg, GMF_CFGHDPLX, half_duplex ); + EFAB_SET_DWORD_FIELD ( reg, GMF_HSTDRPLT64, half_duplex ); + EFAB_SET_DWORD_FIELD ( reg, GMF_HSTFLTRFRMDC_PAUSE, 0 ); + falcon_gmac_writel ( efab, ®, GMF_CFG5_REG_MAC ); + udelay ( 10 ); + + /* MAC address */ + EFAB_POPULATE_DWORD_4 ( reg, + GM_HWADDR_5, efab->mac_addr[5], + GM_HWADDR_4, efab->mac_addr[4], + GM_HWADDR_3, efab->mac_addr[3], + GM_HWADDR_2, efab->mac_addr[2] ); + falcon_gmac_writel ( efab, ®, GM_ADR1_REG_MAC ); + udelay ( 10 ); + EFAB_POPULATE_DWORD_2 ( reg, + GM_HWADDR_1, efab->mac_addr[1], + GM_HWADDR_0, efab->mac_addr[0] ); + falcon_gmac_writel ( efab, ®, GM_ADR2_REG_MAC ); + udelay ( 10 ); +} + +static int +falcon_init_gmac ( struct efab_nic *efab ) +{ + /* Reset the MAC */ + mentormac_reset ( efab ); + + /* Initialise PHY */ + efab->phy_op->init ( efab ); + + /* check the link is up */ + if ( !efab->link_up ) + return -EAGAIN; + + /* Initialise MAC */ + mentormac_init ( efab ); + + /* reconfigure the MAC wrapper */ + falcon_reconfigure_mac_wrapper ( efab ); + + return 0; +} + +static struct efab_mac_operations falcon_gmac_operations = { + .init = falcon_init_gmac, +}; + + +/******************************************************************************* + * + * + * XMAC handling + * + * + *******************************************************************************/ + +/** + * Write dword to a Falcon XMAC register + * + */ +static void +falcon_xmac_writel ( struct efab_nic *efab, efab_dword_t *value, + unsigned int mac_reg ) +{ + efab_oword_t temp; + + EFAB_POPULATE_OWORD_1 ( temp, FCN_MAC_DATA, + EFAB_DWORD_FIELD ( *value, FCN_MAC_DATA ) ); + falcon_write ( efab, &temp, + FALCON_XMAC_REG ( efab, mac_reg ) ); +} + +/** + * Read dword from a Falcon XMAC register + * + */ +static void +falcon_xmac_readl ( struct efab_nic *efab, efab_dword_t *value, + unsigned int mac_reg ) +{ + efab_oword_t temp; + + falcon_read ( efab, &temp, + FALCON_XMAC_REG ( efab, mac_reg ) ); + EFAB_POPULATE_DWORD_1 ( *value, FCN_MAC_DATA, + EFAB_OWORD_FIELD ( temp, FCN_MAC_DATA ) ); +} + +/** + * Configure Falcon XAUI output + */ +static void +falcon_setup_xaui ( struct efab_nic *efab ) +{ + efab_dword_t sdctl, txdrv; + + falcon_xmac_readl ( efab, &sdctl, FCN_XX_SD_CTL_REG_MAC ); + EFAB_SET_DWORD_FIELD ( sdctl, FCN_XX_HIDRVD, XX_SD_CTL_DRV_DEFAULT ); + EFAB_SET_DWORD_FIELD ( sdctl, FCN_XX_LODRVD, XX_SD_CTL_DRV_DEFAULT ); + EFAB_SET_DWORD_FIELD ( sdctl, FCN_XX_HIDRVC, XX_SD_CTL_DRV_DEFAULT ); + EFAB_SET_DWORD_FIELD ( sdctl, FCN_XX_LODRVC, XX_SD_CTL_DRV_DEFAULT ); + EFAB_SET_DWORD_FIELD ( sdctl, FCN_XX_HIDRVB, XX_SD_CTL_DRV_DEFAULT ); + EFAB_SET_DWORD_FIELD ( sdctl, FCN_XX_LODRVB, XX_SD_CTL_DRV_DEFAULT ); + EFAB_SET_DWORD_FIELD ( sdctl, FCN_XX_HIDRVA, XX_SD_CTL_DRV_DEFAULT ); + EFAB_SET_DWORD_FIELD ( sdctl, FCN_XX_LODRVA, XX_SD_CTL_DRV_DEFAULT ); + falcon_xmac_writel ( efab, &sdctl, FCN_XX_SD_CTL_REG_MAC ); + + EFAB_POPULATE_DWORD_8 ( txdrv, + FCN_XX_DEQD, XX_TXDRV_DEQ_DEFAULT, + FCN_XX_DEQC, XX_TXDRV_DEQ_DEFAULT, + FCN_XX_DEQB, XX_TXDRV_DEQ_DEFAULT, + FCN_XX_DEQA, XX_TXDRV_DEQ_DEFAULT, + FCN_XX_DTXD, XX_TXDRV_DTX_DEFAULT, + FCN_XX_DTXC, XX_TXDRV_DTX_DEFAULT, + FCN_XX_DTXB, XX_TXDRV_DTX_DEFAULT, + FCN_XX_DTXA, XX_TXDRV_DTX_DEFAULT); + falcon_xmac_writel ( efab, &txdrv, FCN_XX_TXDRV_CTL_REG_MAC); +} + +static int +falcon_xgmii_status ( struct efab_nic *efab ) +{ + efab_dword_t reg; + + if ( efab->pci_revision < FALCON_REV_B0 ) + return 1; + /* The ISR latches, so clear it and re-read */ + falcon_xmac_readl ( efab, ®, FCN_XM_MGT_INT_REG_MAC_B0 ); + falcon_xmac_readl ( efab, ®, FCN_XM_MGT_INT_REG_MAC_B0 ); + + if ( EFAB_DWORD_FIELD ( reg, FCN_XM_LCLFLT ) || + EFAB_DWORD_FIELD ( reg, FCN_XM_RMTFLT ) ) { + EFAB_TRACE ( "MGT_INT: "EFAB_DWORD_FMT"\n", + EFAB_DWORD_VAL ( reg ) ); + return 0; + } + + return 1; +} + +static void +falcon_mask_status_intr ( struct efab_nic *efab, int enable ) +{ + efab_dword_t reg; + + if ( efab->pci_revision < FALCON_REV_B0 ) + return; + + /* Flush the ISR */ + if ( enable ) + falcon_xmac_readl ( efab, ®, FCN_XM_MGT_INT_REG_MAC_B0 ); + + EFAB_POPULATE_DWORD_2 ( reg, + FCN_XM_MSK_RMTFLT, !enable, + FCN_XM_MSK_LCLFLT, !enable); + falcon_xmac_readl ( efab, ®, FCN_XM_MGT_INT_MSK_REG_MAC_B0 ); +} + +/** + * Reset 10G MAC connected to port + * + */ +static int +falcon_reset_xmac ( struct efab_nic *efab ) +{ + efab_dword_t reg; + int count; + + EFAB_POPULATE_DWORD_1 ( reg, FCN_XM_CORE_RST, 1 ); + falcon_xmac_writel ( efab, ®, FCN_XM_GLB_CFG_REG_MAC ); + + for ( count = 0 ; count < 1000 ; count++ ) { + udelay ( 10 ); + falcon_xmac_readl ( efab, ®, + FCN_XM_GLB_CFG_REG_MAC ); + if ( EFAB_DWORD_FIELD ( reg, FCN_XM_CORE_RST ) == 0 ) + return 0; + } + return -ETIMEDOUT; +} + + +static int +falcon_reset_xaui ( struct efab_nic *efab ) +{ + efab_dword_t reg; + int count; + + if (!efab->is_asic) + return 0; + + EFAB_POPULATE_DWORD_1 ( reg, FCN_XX_RST_XX_EN, 1 ); + falcon_xmac_writel ( efab, ®, FCN_XX_PWR_RST_REG_MAC ); + + /* Give some time for the link to establish */ + for (count = 0; count < 1000; count++) { /* wait upto 10ms */ + falcon_xmac_readl ( efab, ®, FCN_XX_PWR_RST_REG_MAC ); + if ( EFAB_DWORD_FIELD ( reg, FCN_XX_RST_XX_EN ) == 0 ) { + falcon_setup_xaui ( efab ); + return 0; + } + udelay(10); + } + EFAB_ERR ( "timed out waiting for XAUI/XGXS reset\n" ); + return -ETIMEDOUT; +} + +static int +falcon_xaui_link_ok ( struct efab_nic *efab ) +{ + efab_dword_t reg; + int align_done, lane_status, sync; + int has_phyxs; + int link_ok = 1; + + /* Read Falcon XAUI side */ + if ( efab->is_asic ) { + /* Read link status */ + falcon_xmac_readl ( efab, ®, FCN_XX_CORE_STAT_REG_MAC ); + align_done = EFAB_DWORD_FIELD ( reg, FCN_XX_ALIGN_DONE ); + + sync = EFAB_DWORD_FIELD ( reg, FCN_XX_SYNC_STAT ); + sync = ( sync == FCN_XX_SYNC_STAT_DECODE_SYNCED ); + + link_ok = align_done && sync; + } + + /* Clear link status ready for next read */ + EFAB_SET_DWORD_FIELD ( reg, FCN_XX_COMMA_DET, FCN_XX_COMMA_DET_RESET ); + EFAB_SET_DWORD_FIELD ( reg, FCN_XX_CHARERR, FCN_XX_CHARERR_RESET); + EFAB_SET_DWORD_FIELD ( reg, FCN_XX_DISPERR, FCN_XX_DISPERR_RESET); + falcon_xmac_writel ( efab, ®, FCN_XX_CORE_STAT_REG_MAC ); + + has_phyxs = ( efab->phy_op->mmds & ( 1 << MDIO_MMD_PHYXS ) ); + if ( link_ok && has_phyxs ) { + lane_status = falcon_mdio_read ( efab, MDIO_MMD_PHYXS, + MDIO_PHYXS_LANE_STATE ); + link_ok = ( lane_status & ( 1 << MDIO_PHYXS_LANE_ALIGNED_LBN ) ); + + if (!link_ok ) + EFAB_LOG ( "XGXS lane status: %x\n", lane_status ); + } + + return link_ok; +} + +/** + * Initialise XMAC + * + */ +static void +falcon_reconfigure_xmac ( struct efab_nic *efab ) +{ + efab_dword_t reg; + int max_frame_len; + + /* Configure MAC - cut-thru mode is hard wired on */ + EFAB_POPULATE_DWORD_3 ( reg, + FCN_XM_RX_JUMBO_MODE, 1, + FCN_XM_TX_STAT_EN, 1, + FCN_XM_RX_STAT_EN, 1); + falcon_xmac_writel ( efab, ®, FCN_XM_GLB_CFG_REG_MAC ); + + /* Configure TX */ + EFAB_POPULATE_DWORD_6 ( reg, + FCN_XM_TXEN, 1, + FCN_XM_TX_PRMBL, 1, + FCN_XM_AUTO_PAD, 1, + FCN_XM_TXCRC, 1, + FCN_XM_FCNTL, 1, + FCN_XM_IPG, 0x3 ); + falcon_xmac_writel ( efab, ®, FCN_XM_TX_CFG_REG_MAC ); + + /* Configure RX */ + EFAB_POPULATE_DWORD_4 ( reg, + FCN_XM_RXEN, 1, + FCN_XM_AUTO_DEPAD, 0, + FCN_XM_ACPT_ALL_MCAST, 1, + FCN_XM_PASS_CRC_ERR, 1 ); + falcon_xmac_writel ( efab, ®, FCN_XM_RX_CFG_REG_MAC ); + + /* Set frame length */ + max_frame_len = EFAB_MAX_FRAME_LEN ( ETH_FRAME_LEN ); + EFAB_POPULATE_DWORD_1 ( reg, + FCN_XM_MAX_RX_FRM_SIZE, max_frame_len ); + falcon_xmac_writel ( efab, ®, FCN_XM_RX_PARAM_REG_MAC ); + EFAB_POPULATE_DWORD_2 ( reg, + FCN_XM_MAX_TX_FRM_SIZE, max_frame_len, + FCN_XM_TX_JUMBO_MODE, 1 ); + falcon_xmac_writel ( efab, ®, FCN_XM_TX_PARAM_REG_MAC ); + + /* Enable flow control receipt */ + EFAB_POPULATE_DWORD_2 ( reg, + FCN_XM_PAUSE_TIME, 0xfffe, + FCN_XM_DIS_FCNTL, 0 ); + falcon_xmac_writel ( efab, ®, FCN_XM_FC_REG_MAC ); + + /* Set MAC address */ + EFAB_POPULATE_DWORD_4 ( reg, + FCN_XM_ADR_0, efab->mac_addr[0], + FCN_XM_ADR_1, efab->mac_addr[1], + FCN_XM_ADR_2, efab->mac_addr[2], + FCN_XM_ADR_3, efab->mac_addr[3] ); + falcon_xmac_writel ( efab, ®, FCN_XM_ADR_LO_REG_MAC ); + EFAB_POPULATE_DWORD_2 ( reg, + FCN_XM_ADR_4, efab->mac_addr[4], + FCN_XM_ADR_5, efab->mac_addr[5] ); + falcon_xmac_writel ( efab, ®, FCN_XM_ADR_HI_REG_MAC ); +} + +static int +falcon_init_xmac ( struct efab_nic *efab ) +{ + int count, rc; + + /* Mask the PHY management interrupt */ + falcon_mask_status_intr ( efab, 0 ); + + /* Initialise the PHY to instantiate the clock. */ + rc = efab->phy_op->init ( efab ); + if ( rc ) { + EFAB_ERR ( "unable to initialise PHY\n" ); + goto fail1; + } + + falcon_reset_xaui ( efab ); + + /* Give the PHY and MAC time to faff */ + mdelay ( 100 ); + + /* Reset and reconfigure the XMAC */ + rc = falcon_reset_xmac ( efab ); + if ( rc ) + goto fail2; + falcon_reconfigure_xmac ( efab ); + falcon_reconfigure_mac_wrapper ( efab ); + /** + * Now wait for the link to come up. This may take a while + * for some slower PHY's. + */ + for (count=0; count<50; count++) { + int link_ok = 1; + + /* Wait a while for the link to come up. */ + mdelay ( 100 ); + if ((count % 5) == 0) + putchar ( '.' ); + + /* Does the PHY think the wire-side link is up? */ + link_ok = mdio_clause45_links_ok ( efab ); + /* Ensure the XAUI link to the PHY is good */ + if ( link_ok ) { + link_ok = falcon_xaui_link_ok ( efab ); + if ( !link_ok ) + falcon_reset_xaui ( efab ); + } + + /* Check fault indication */ + if ( link_ok ) + link_ok = falcon_xgmii_status ( efab ); + + efab->link_up = link_ok; + if ( link_ok ) { + /* unmask the status interrupt */ + falcon_mask_status_intr ( efab, 1 ); + return 0; + } + } + + /* Link failed to come up, but initialisation was fine. */ + rc = -ETIMEDOUT; + +fail2: +fail1: + return rc; +} + +static struct efab_mac_operations falcon_xmac_operations = { + .init = falcon_init_xmac, +}; + +/******************************************************************************* + * + * + * Null PHY handling + * + * + *******************************************************************************/ + +static int +falcon_xaui_phy_init ( struct efab_nic *efab ) +{ + /* CX4 is always 10000FD only */ + efab->link_options = LPA_10000FULL; + + /* There is no PHY! */ + return 0; +} + +static struct efab_phy_operations falcon_xaui_phy_ops = { + .init = falcon_xaui_phy_init, + .mmds = 0, +}; + + +/******************************************************************************* + * + * + * Alaska PHY + * + * + *******************************************************************************/ + +/** + * Initialise Alaska PHY + * + */ +static int +alaska_init ( struct efab_nic *efab ) +{ + unsigned int advertised, lpa; + + /* Read link up status */ + efab->link_up = gmii_link_ok ( efab ); + + if ( ! efab->link_up ) + return -EIO; + + /* Determine link options from PHY. */ + advertised = gmii_autoneg_advertised ( efab ); + lpa = gmii_autoneg_lpa ( efab ); + efab->link_options = gmii_nway_result ( advertised & lpa ); + + return 0; +} + +static struct efab_phy_operations falcon_alaska_phy_ops = { + .init = alaska_init, +}; + +/******************************************************************************* + * + * + * xfp + * + * + *******************************************************************************/ + +#define XFP_REQUIRED_DEVS ( MDIO_MMDREG_DEVS0_PCS | \ + MDIO_MMDREG_DEVS0_PMAPMD | \ + MDIO_MMDREG_DEVS0_PHYXS ) + +static int +falcon_xfp_phy_init ( struct efab_nic *efab ) +{ + int rc; + + /* Optical link is always 10000FD only */ + efab->link_options = LPA_10000FULL; + + /* Reset the PHY */ + rc = mdio_clause45_reset_mmd ( efab, MDIO_MMD_PHYXS ); + if ( rc ) + return rc; + + return 0; +} + +static struct efab_phy_operations falcon_xfp_phy_ops = { + .init = falcon_xfp_phy_init, + .mmds = XFP_REQUIRED_DEVS, +}; + +/******************************************************************************* + * + * + * txc43128 + * + * + *******************************************************************************/ + +/* Command register */ +#define TXC_GLRGS_GLCMD (0xc004) +#define TXC_GLCMD_LMTSWRST_LBN (14) + +/* Amplitude on lanes 0+1, 2+3 */ +#define TXC_ALRGS_ATXAMP0 (0xc041) +#define TXC_ALRGS_ATXAMP1 (0xc042) +/* Bit position of value for lane 0+2, 1+3 */ +#define TXC_ATXAMP_LANE02_LBN (3) +#define TXC_ATXAMP_LANE13_LBN (11) + +#define TXC_ATXAMP_1280_mV (0) +#define TXC_ATXAMP_1200_mV (8) +#define TXC_ATXAMP_1120_mV (12) +#define TXC_ATXAMP_1060_mV (14) +#define TXC_ATXAMP_0820_mV (25) +#define TXC_ATXAMP_0720_mV (26) +#define TXC_ATXAMP_0580_mV (27) +#define TXC_ATXAMP_0440_mV (28) + +#define TXC_ATXAMP_0820_BOTH ( (TXC_ATXAMP_0820_mV << TXC_ATXAMP_LANE02_LBN) | \ + (TXC_ATXAMP_0820_mV << TXC_ATXAMP_LANE13_LBN) ) + +#define TXC_ATXAMP_DEFAULT (0x6060) /* From databook */ + +/* Preemphasis on lanes 0+1, 2+3 */ +#define TXC_ALRGS_ATXPRE0 (0xc043) +#define TXC_ALRGS_ATXPRE1 (0xc044) + +#define TXC_ATXPRE_NONE (0) +#define TXC_ATXPRE_DEFAULT (0x1010) /* From databook */ + +#define TXC_REQUIRED_DEVS ( MDIO_MMDREG_DEVS0_PCS | \ + MDIO_MMDREG_DEVS0_PMAPMD | \ + MDIO_MMDREG_DEVS0_PHYXS ) + +static int +falcon_txc_logic_reset ( struct efab_nic *efab ) +{ + int val; + int tries = 50; + + val = falcon_mdio_read ( efab, MDIO_MMD_PCS, TXC_GLRGS_GLCMD ); + val |= (1 << TXC_GLCMD_LMTSWRST_LBN); + falcon_mdio_write ( efab, MDIO_MMD_PCS, TXC_GLRGS_GLCMD, val ); + + while ( tries--) { + val = falcon_mdio_read ( efab, MDIO_MMD_PCS, TXC_GLRGS_GLCMD ); + if ( ~val & ( 1 << TXC_GLCMD_LMTSWRST_LBN ) ) + return 0; + udelay(1); + } + + EFAB_ERR ( "logic reset failed\n" ); + + return -ETIMEDOUT; +} + +static int +falcon_txc_phy_init ( struct efab_nic *efab ) +{ + int rc; + + /* CX4 is always 10000FD only */ + efab->link_options = LPA_10000FULL; + + /* reset the phy */ + rc = mdio_clause45_reset_mmd ( efab, MDIO_MMD_PMAPMD ); + if ( rc ) + goto fail1; + + rc = mdio_clause45_check_mmds ( efab ); + if ( rc ) + goto fail2; + + /* Turn amplitude down and preemphasis off on the host side + * (PHY<->MAC) as this is believed less likely to upset falcon + * and no adverse effects have been noted. It probably also + * saves a picowatt or two */ + + /* Turn off preemphasis */ + falcon_mdio_write ( efab, MDIO_MMD_PHYXS, TXC_ALRGS_ATXPRE0, + TXC_ATXPRE_NONE ); + falcon_mdio_write ( efab, MDIO_MMD_PHYXS, TXC_ALRGS_ATXPRE1, + TXC_ATXPRE_NONE ); + + /* Turn down the amplitude */ + falcon_mdio_write ( efab, MDIO_MMD_PHYXS, TXC_ALRGS_ATXAMP0, + TXC_ATXAMP_0820_BOTH ); + falcon_mdio_write ( efab, MDIO_MMD_PHYXS, TXC_ALRGS_ATXAMP1, + TXC_ATXAMP_0820_BOTH ); + + /* Set the line side amplitude and preemphasis to the databook + * defaults as an erratum causes them to be 0 on at least some + * PHY rev.s */ + falcon_mdio_write ( efab, MDIO_MMD_PMAPMD, TXC_ALRGS_ATXPRE0, + TXC_ATXPRE_DEFAULT ); + falcon_mdio_write ( efab, MDIO_MMD_PMAPMD, TXC_ALRGS_ATXPRE1, + TXC_ATXPRE_DEFAULT ); + falcon_mdio_write ( efab, MDIO_MMD_PMAPMD, TXC_ALRGS_ATXAMP0, + TXC_ATXAMP_DEFAULT ); + falcon_mdio_write ( efab, MDIO_MMD_PMAPMD, TXC_ALRGS_ATXAMP1, + TXC_ATXAMP_DEFAULT ); + + rc = falcon_txc_logic_reset ( efab ); + if ( rc ) + goto fail3; + + return 0; + +fail3: +fail2: +fail1: + return rc; +} + +static struct efab_phy_operations falcon_txc_phy_ops = { + .init = falcon_txc_phy_init, + .mmds = TXC_REQUIRED_DEVS, +}; + +/******************************************************************************* + * + * + * tenxpress + * + * + *******************************************************************************/ + + +#define TENXPRESS_REQUIRED_DEVS ( MDIO_MMDREG_DEVS0_PMAPMD | \ + MDIO_MMDREG_DEVS0_PCS | \ + MDIO_MMDREG_DEVS0_PHYXS ) + +#define PCS_TEST_SELECT_REG 0xd807 /* PRM 10.5.8 */ +#define CLK312_EN_LBN 3 +#define CLK312_EN_WIDTH 1 + +#define PCS_CLOCK_CTRL_REG 0xd801 +#define PLL312_RST_N_LBN 2 + +/* Special Software reset register */ +#define PMA_PMD_EXT_CTRL_REG 49152 +#define PMA_PMD_EXT_SSR_LBN 15 + +/* Boot status register */ +#define PCS_BOOT_STATUS_REG 0xd000 +#define PCS_BOOT_FATAL_ERR_LBN 0 +#define PCS_BOOT_PROGRESS_LBN 1 +#define PCS_BOOT_PROGRESS_WIDTH 2 +#define PCS_BOOT_COMPLETE_LBN 3 + +#define PCS_SOFT_RST2_REG 0xd806 +#define SERDES_RST_N_LBN 13 +#define XGXS_RST_N_LBN 12 + +static int +falcon_tenxpress_check_c11 ( struct efab_nic *efab ) +{ + int count; + uint32_t boot_stat; + + /* Check that the C11 CPU has booted */ + for (count=0; count<10; count++) { + boot_stat = falcon_mdio_read ( efab, MDIO_MMD_PCS, + PCS_BOOT_STATUS_REG ); + if ( boot_stat & ( 1 << PCS_BOOT_COMPLETE_LBN ) ) + return 0; + + udelay(10); + } + + EFAB_ERR ( "C11 failed to boot\n" ); + return -ETIMEDOUT; +} + +static int +falcon_tenxpress_phy_init ( struct efab_nic *efab ) +{ + int rc, reg; + + /* 10XPRESS is always 10000FD (at the moment) */ + efab->link_options = LPA_10000FULL; + + /* Wait for the blocks to come out of reset */ + rc = mdio_clause45_wait_reset_mmds ( efab ); + if ( rc ) + goto fail1; + + rc = mdio_clause45_check_mmds ( efab ); + if ( rc ) + goto fail2; + + /* Turn on the clock */ + reg = (1 << CLK312_EN_LBN); + falcon_mdio_write ( efab, MDIO_MMD_PCS, PCS_TEST_SELECT_REG, reg); + + /* Wait 200ms for the PHY to boot */ + mdelay(200); + + rc = falcon_tenxpress_check_c11 ( efab ); + if ( rc ) + goto fail3; + + return 0; + +fail3: +fail2: +fail1: + return rc; +} + +static struct efab_phy_operations falcon_tenxpress_phy_ops = { + .init = falcon_tenxpress_phy_init, + .mmds = TENXPRESS_REQUIRED_DEVS, +}; + +/******************************************************************************* + * + * + * PM8358 + * + * + *******************************************************************************/ + +/* The PM8358 just presents a DTE XS */ +#define PM8358_REQUIRED_DEVS (MDIO_MMDREG_DEVS0_DTEXS) + +/* PHY-specific definitions */ +/* Master ID and Global Performance Monitor Update */ +#define PMC_MASTER_REG (0xd000) +/* Analog Tx Rx settings under software control */ +#define PMC_MASTER_ANLG_CTRL (1<< 11) + +/* Master Configuration register 2 */ +#define PMC_MCONF2_REG (0xd002) +/* Drive Tx off centre of data eye (1) vs. clock edge (0) */ +#define PMC_MCONF2_TEDGE (1 << 2) +/* Drive Rx off centre of data eye (1) vs. clock edge (0) */ +#define PMC_MCONF2_REDGE (1 << 3) + +/* Analog Rx settings */ +#define PMC_ANALOG_RX_CFG0 (0xd025) +#define PMC_ANALOG_RX_CFG1 (0xd02d) +#define PMC_ANALOG_RX_CFG2 (0xd035) +#define PMC_ANALOG_RX_CFG3 (0xd03d) + + +#define PMC_ANALOG_RX_TERM (1 << 15) /* Bit 15 of RX CFG: 0 for 100 ohms float, + 1 for 50 to 1.2V */ +#define PMC_ANALOG_RX_EQ_MASK (3 << 8) +#define PMC_ANALOG_RX_EQ_NONE (0 << 8) +#define PMC_ANALOG_RX_EQ_HALF (1 << 8) +#define PMC_ANALOG_RX_EQ_FULL (2 << 8) +#define PMC_ANALOG_RX_EQ_RSVD (3 << 8) + +static int +falcon_pm8358_phy_init ( struct efab_nic *efab ) +{ + int rc, reg, i; + + /* This is a XAUI retimer part */ + efab->link_options = LPA_10000FULL; + + rc = mdio_clause45_reset_mmd ( efab, MDIO_MMDREG_DEVS0_DTEXS ); + if ( rc ) + return rc; + + /* Enable software control of analogue settings */ + reg = falcon_mdio_read ( efab, MDIO_MMD_DTEXS, PMC_MASTER_REG ); + reg |= PMC_MASTER_ANLG_CTRL; + falcon_mdio_write ( efab, MDIO_MMD_DTEXS, PMC_MASTER_REG, reg ); + + /* Turn rx eq on for all channels */ + for (i=0; i< 3; i++) { + /* The analog CFG registers are evenly spaced 8 apart */ + uint16_t addr = PMC_ANALOG_RX_CFG0 + 8*i; + reg = falcon_mdio_read ( efab, MDIO_MMD_DTEXS, addr ); + reg = ( reg & ~PMC_ANALOG_RX_EQ_MASK ) | PMC_ANALOG_RX_EQ_FULL; + falcon_mdio_write ( efab, MDIO_MMD_DTEXS, addr, reg ); + } + + /* Set TEDGE, clear REDGE */ + reg = falcon_mdio_read ( efab, MDIO_MMD_DTEXS, PMC_MCONF2_REG ); + reg = ( reg & ~PMC_MCONF2_REDGE) | PMC_MCONF2_TEDGE; + falcon_mdio_write ( efab, MDIO_MMD_DTEXS, PMC_MCONF2_REG, reg ); + + return 0; +} + +static struct efab_phy_operations falcon_pm8358_phy_ops = { + .init = falcon_pm8358_phy_init, + .mmds = PM8358_REQUIRED_DEVS, +}; + +/******************************************************************************* + * + * + * SFE4001 support + * + * + *******************************************************************************/ + +#define MAX_TEMP_THRESH 90 + +/* I2C Expander */ +#define PCA9539 0x74 + +#define P0_IN 0x00 +#define P0_OUT 0x02 +#define P0_CONFIG 0x06 + +#define P0_EN_1V0X_LBN 0 +#define P0_EN_1V0X_WIDTH 1 +#define P0_EN_1V2_LBN 1 +#define P0_EN_1V2_WIDTH 1 +#define P0_EN_2V5_LBN 2 +#define P0_EN_2V5_WIDTH 1 +#define P0_EN_3V3X_LBN 3 +#define P0_EN_3V3X_WIDTH 1 +#define P0_EN_5V_LBN 4 +#define P0_EN_5V_WIDTH 1 +#define P0_X_TRST_LBN 6 +#define P0_X_TRST_WIDTH 1 + +#define P1_IN 0x01 +#define P1_CONFIG 0x07 + +#define P1_AFE_PWD_LBN 0 +#define P1_AFE_PWD_WIDTH 1 +#define P1_DSP_PWD25_LBN 1 +#define P1_DSP_PWD25_WIDTH 1 +#define P1_SPARE_LBN 4 +#define P1_SPARE_WIDTH 4 + +/* Temperature Sensor */ +#define MAX6647 0x4e + +#define RSL 0x02 +#define RLHN 0x05 +#define WLHO 0x0b + +static struct i2c_device i2c_pca9539 = { + .dev_addr = PCA9539, + .dev_addr_len = 1, + .word_addr_len = 1, +}; + + +static struct i2c_device i2c_max6647 = { + .dev_addr = MAX6647, + .dev_addr_len = 1, + .word_addr_len = 1, +}; + +static int +sfe4001_init ( struct efab_nic *efab ) +{ + struct i2c_interface *i2c = &efab->i2c_bb.i2c; + efab_dword_t reg; + uint8_t in, cfg, out; + int count, rc; + + EFAB_LOG ( "Initialise SFE4001 board\n" ); + + /* Ensure XGXS and XAUI SerDes are held in reset */ + EFAB_POPULATE_DWORD_7 ( reg, + FCN_XX_PWRDNA_EN, 1, + FCN_XX_PWRDNB_EN, 1, + FCN_XX_RSTPLLAB_EN, 1, + FCN_XX_RESETA_EN, 1, + FCN_XX_RESETB_EN, 1, + FCN_XX_RSTXGXSRX_EN, 1, + FCN_XX_RSTXGXSTX_EN, 1 ); + falcon_xmac_writel ( efab, ®, FCN_XX_PWR_RST_REG_MAC); + udelay(10); + + /* Set DSP over-temperature alert threshold */ + cfg = MAX_TEMP_THRESH; + rc = i2c->write ( i2c, &i2c_max6647, WLHO, &cfg, EFAB_BYTE ); + if ( rc ) + goto fail1; + + /* Read it back and verify */ + rc = i2c->read ( i2c, &i2c_max6647, RLHN, &in, EFAB_BYTE ); + if ( rc ) + goto fail2; + + if ( in != MAX_TEMP_THRESH ) { + EFAB_ERR ( "Unable to verify MAX6647 limit (requested=%d " + "confirmed=%d)\n", cfg, in ); + rc = -EIO; + goto fail3; + } + + /* Clear any previous over-temperature alert */ + rc = i2c->read ( i2c, &i2c_max6647, RSL, &in, EFAB_BYTE ); + if ( rc ) + goto fail4; + + /* Enable port 0 and 1 outputs on IO expander */ + cfg = 0x00; + rc = i2c->write ( i2c, &i2c_pca9539, P0_CONFIG, &cfg, EFAB_BYTE ); + if ( rc ) + goto fail5; + cfg = 0xff & ~(1 << P1_SPARE_LBN); + rc = i2c->write ( i2c, &i2c_pca9539, P1_CONFIG, &cfg, EFAB_BYTE ); + if ( rc ) + goto fail6; + + /* Turn all power off then wait 1 sec. This ensures PHY is reset */ + out = 0xff & ~((0 << P0_EN_1V2_LBN) | (0 << P0_EN_2V5_LBN) | + (0 << P0_EN_3V3X_LBN) | (0 << P0_EN_5V_LBN) | + (0 << P0_EN_1V0X_LBN)); + + rc = i2c->write ( i2c, &i2c_pca9539, P0_OUT, &out, EFAB_BYTE ); + if ( rc ) + goto fail7; + + mdelay(1000); + + for (count=0; count<20; count++) { + /* Turn on 1.2V, 2.5V, 3.3V and 5V power rails */ + out = 0xff & ~( (1 << P0_EN_1V2_LBN) | (1 << P0_EN_2V5_LBN) | + (1 << P0_EN_3V3X_LBN) | (1 << P0_EN_5V_LBN) | + (1 << P0_X_TRST_LBN) ); + + rc = i2c->write ( i2c, &i2c_pca9539, P0_OUT, &out, EFAB_BYTE ); + if ( rc ) + goto fail8; + + mdelay ( 10 ); + + /* Turn on the 1V power rail */ + out &= ~( 1 << P0_EN_1V0X_LBN ); + rc = i2c->write ( i2c, &i2c_pca9539, P0_OUT, &out, EFAB_BYTE ); + if ( rc ) + goto fail9; + + EFAB_LOG ( "Waiting for power...(attempt %d)\n", count); + mdelay ( 1000 ); + + /* Check DSP is powered */ + rc = i2c->read ( i2c, &i2c_pca9539, P1_IN, &in, EFAB_BYTE ); + if ( rc ) + goto fail10; + + if ( in & ( 1 << P1_AFE_PWD_LBN ) ) + return 0; + } + + rc = -ETIMEDOUT; + +fail10: +fail9: +fail8: +fail7: + /* Turn off power rails */ + out = 0xff; + (void) i2c->write ( i2c, &i2c_pca9539, P0_OUT, &out, EFAB_BYTE ); + /* Disable port 1 outputs on IO expander */ + out = 0xff; + (void) i2c->write ( i2c, &i2c_pca9539, P1_CONFIG, &out, EFAB_BYTE ); +fail6: + /* Disable port 0 outputs */ + out = 0xff; + (void) i2c->write ( i2c, &i2c_pca9539, P1_CONFIG, &out, EFAB_BYTE ); +fail5: +fail4: +fail3: +fail2: +fail1: + EFAB_ERR ( "Failed initialising SFE4001 board\n" ); + return rc; +} + +static void +sfe4001_fini ( struct efab_nic *efab ) +{ + struct i2c_interface *i2c = &efab->i2c_bb.i2c; + uint8_t in, cfg, out; + + EFAB_ERR ( "Turning off SFE4001\n" ); + + /* Turn off all power rails */ + out = 0xff; + (void) i2c->write ( i2c, &i2c_pca9539, P0_OUT, &out, EFAB_BYTE ); + + /* Disable port 1 outputs on IO expander */ + cfg = 0xff; + (void) i2c->write ( i2c, &i2c_pca9539, P1_CONFIG, &cfg, EFAB_BYTE ); + + /* Disable port 0 outputs on IO expander */ + cfg = 0xff; + (void) i2c->write ( i2c, &i2c_pca9539, P0_CONFIG, &cfg, EFAB_BYTE ); + + /* Clear any over-temperature alert */ + (void) i2c->read ( i2c, &i2c_max6647, RSL, &in, EFAB_BYTE ); +} + +struct efab_board_operations sfe4001_ops = { + .init = sfe4001_init, + .fini = sfe4001_fini, +}; + +static int sfe4002_init ( struct efab_nic *efab __attribute__((unused)) ) +{ + return 0; +} +static void sfe4002_fini ( struct efab_nic *efab __attribute__((unused)) ) +{ +} + +struct efab_board_operations sfe4002_ops = { + .init = sfe4002_init, + .fini = sfe4002_fini, +}; + +static int sfe4003_init ( struct efab_nic *efab __attribute__((unused)) ) +{ + return 0; +} +static void sfe4003_fini ( struct efab_nic *efab __attribute__((unused)) ) +{ +} + +struct efab_board_operations sfe4003_ops = { + .init = sfe4003_init, + .fini = sfe4003_fini, +}; + +/******************************************************************************* + * + * + * Hardware initialisation + * + * + *******************************************************************************/ + +static void +falcon_free_special_buffer ( void *p ) +{ + /* We don't bother cleaning up the buffer table entries - + * we're hardly limited */ + free_dma ( p, EFAB_BUF_ALIGN ); +} + +static void* +falcon_alloc_special_buffer ( struct efab_nic *efab, int bytes, + struct efab_special_buffer *entry ) +{ + void* buffer; + int remaining; + efab_qword_t buf_desc; + unsigned long dma_addr; + + /* Allocate the buffer, aligned on a buffer address boundary */ + buffer = malloc_dma ( bytes, EFAB_BUF_ALIGN ); + if ( ! buffer ) + return NULL; + + /* Push buffer table entries to back the buffer */ + entry->id = efab->buffer_head; + entry->dma_addr = dma_addr = virt_to_bus ( buffer ); + assert ( ( dma_addr & ( EFAB_BUF_ALIGN - 1 ) ) == 0 ); + + remaining = bytes; + while ( remaining > 0 ) { + EFAB_POPULATE_QWORD_3 ( buf_desc, + FCN_IP_DAT_BUF_SIZE, FCN_IP_DAT_BUF_SIZE_4K, + FCN_BUF_ADR_FBUF, ( dma_addr >> 12 ), + FCN_BUF_OWNER_ID_FBUF, 0 ); + + falcon_write_sram ( efab, &buf_desc, efab->buffer_head ); + + ++efab->buffer_head; + dma_addr += EFAB_BUF_ALIGN; + remaining -= EFAB_BUF_ALIGN; + } + + EFAB_TRACE ( "Allocated 0x%x bytes at %p backed by buffer table " + "entries 0x%x..0x%x\n", bytes, buffer, entry->id, + efab->buffer_head - 1 ); + + return buffer; +} + +static void +clear_b0_fpga_memories ( struct efab_nic *efab) +{ + efab_oword_t blanko, temp; + efab_dword_t blankd; + int offset; + + EFAB_ZERO_OWORD ( blanko ); + EFAB_ZERO_DWORD ( blankd ); + + /* Clear the address region register */ + EFAB_POPULATE_OWORD_4 ( temp, + FCN_ADR_REGION0, 0, + FCN_ADR_REGION1, ( 1 << 16 ), + FCN_ADR_REGION2, ( 2 << 16 ), + FCN_ADR_REGION3, ( 3 << 16 ) ); + falcon_write ( efab, &temp, FCN_ADR_REGION_REG_KER ); + + EFAB_TRACE ( "Clearing filter and RSS tables\n" ); + + for ( offset = FCN_RX_FILTER_TBL0 ; + offset < FCN_RX_RSS_INDIR_TBL_B0+0x800 ; + offset += 0x10 ) { + falcon_write ( efab, &blanko, offset ); + } + + EFAB_TRACE ( "Wiping buffer tables\n" ); + + /* Notice the 8 byte access mode */ + for ( offset = 0x2800000 ; + offset < 0x3000000 ; + offset += 0x8) { + _falcon_writel ( efab, 0, offset ); + _falcon_writel ( efab, 0, offset + 4 ); + wmb(); + } +} + +static int +falcon_reset ( struct efab_nic *efab ) +{ + efab_oword_t glb_ctl_reg_ker; + + /* Initiate software reset */ + EFAB_POPULATE_OWORD_6 ( glb_ctl_reg_ker, + FCN_PCIE_CORE_RST_CTL, EXCLUDE_FROM_RESET, + FCN_PCIE_NSTCK_RST_CTL, EXCLUDE_FROM_RESET, + FCN_PCIE_SD_RST_CTL, EXCLUDE_FROM_RESET, + FCN_EE_RST_CTL, EXCLUDE_FROM_RESET, + FCN_EXT_PHY_RST_DUR, 0x7, /* 10ms */ + FCN_SWRST, 1 ); + + falcon_write ( efab, &glb_ctl_reg_ker, FCN_GLB_CTL_REG_KER ); + + /* Allow 50ms for reset */ + mdelay ( 50 ); + + /* Check for device reset complete */ + falcon_read ( efab, &glb_ctl_reg_ker, FCN_GLB_CTL_REG_KER ); + if ( EFAB_OWORD_FIELD ( glb_ctl_reg_ker, FCN_SWRST ) != 0 ) { + EFAB_ERR ( "Reset failed\n" ); + return -ETIMEDOUT; + } + + if ( ( efab->pci_revision == FALCON_REV_B0 ) && !efab->is_asic ) { + clear_b0_fpga_memories ( efab ); + } + + return 0; +} + +/** Offset of MAC address within EEPROM or Flash */ +#define FALCON_MAC_ADDRESS_OFFSET 0x310 + +/* + * Falcon EEPROM structure + */ +#define SF_NV_CONFIG_BASE 0x300 +#define SF_NV_CONFIG_EXTRA 0xA0 + +struct falcon_nv_config_ver2 { + uint16_t nports; + uint8_t port0_phy_addr; + uint8_t port0_phy_type; + uint8_t port1_phy_addr; + uint8_t port1_phy_type; + uint16_t asic_sub_revision; + uint16_t board_revision; + uint8_t mac_location; +}; + +struct falcon_nv_extra { + uint16_t magicnumber; + uint16_t structure_version; + uint16_t checksum; + union { + struct falcon_nv_config_ver2 ver2; + } ver_specific; +}; + +#define BOARD_TYPE(_rev) (_rev >> 8) + +static void +falcon_probe_nic_variant ( struct efab_nic *efab, struct pci_device *pci ) +{ + efab_oword_t altera_build, nic_stat; + int is_pcie, fpga_version; + uint8_t revision; + + /* PCI revision */ + pci_read_config_byte ( pci, PCI_CLASS_REVISION, &revision ); + efab->pci_revision = revision; + + /* Asic vs FPGA */ + falcon_read ( efab, &altera_build, FCN_ALTERA_BUILD_REG_KER ); + fpga_version = EFAB_OWORD_FIELD ( altera_build, FCN_VER_ALL ); + efab->is_asic = (fpga_version == 0); + + /* MAC and PCI type */ + falcon_read ( efab, &nic_stat, FCN_NIC_STAT_REG ); + if ( efab->pci_revision == FALCON_REV_B0 ) { + is_pcie = 1; + efab->phy_10g = EFAB_OWORD_FIELD ( nic_stat, FCN_STRAP_10G ); + } + else if ( efab->is_asic ) { + is_pcie = EFAB_OWORD_FIELD ( nic_stat, FCN_STRAP_PCIE ); + efab->phy_10g = EFAB_OWORD_FIELD ( nic_stat, FCN_STRAP_10G ); + } + else { + int minor = EFAB_OWORD_FIELD ( altera_build, FCN_VER_MINOR ); + is_pcie = 0; + efab->phy_10g = ( minor == 0x14 ); + } +} + +static void +falcon_init_spi_device ( struct efab_nic *efab, struct spi_device *spi ) +{ + /* Falcon's SPI interface only supports reads/writes of up to 16 bytes. + * Reduce the nvs block size down to satisfy this - which means callers + * should use the nvs_* functions rather than spi_*. */ + if ( spi->nvs.block_size > FALCON_SPI_MAX_LEN ) + spi->nvs.block_size = FALCON_SPI_MAX_LEN; + + spi->bus = &efab->spi_bus; + efab->spi = spi; +} + +static int +falcon_probe_spi ( struct efab_nic *efab ) +{ + efab_oword_t nic_stat, gpio_ctl, ee_vpd_cfg; + int has_flash, has_eeprom, ad9bit; + + falcon_read ( efab, &nic_stat, FCN_NIC_STAT_REG ); + falcon_read ( efab, &gpio_ctl, FCN_GPIO_CTL_REG_KER ); + falcon_read ( efab, &ee_vpd_cfg, FCN_EE_VPD_CFG_REG ); + + /* determine if FLASH / EEPROM is present */ + if ( ( efab->pci_revision >= FALCON_REV_B0 ) || efab->is_asic ) { + has_flash = EFAB_OWORD_FIELD ( nic_stat, FCN_SF_PRST ); + has_eeprom = EFAB_OWORD_FIELD ( nic_stat, FCN_EE_PRST ); + } else { + has_flash = EFAB_OWORD_FIELD ( gpio_ctl, FCN_FLASH_PRESENT ); + has_eeprom = EFAB_OWORD_FIELD ( gpio_ctl, FCN_EEPROM_PRESENT ); + } + ad9bit = EFAB_OWORD_FIELD ( ee_vpd_cfg, FCN_EE_VPD_EN_AD9_MODE ); + + /* Configure the SPI and I2C bus */ + efab->spi_bus.rw = falcon_spi_rw; + init_i2c_bit_basher ( &efab->i2c_bb, &falcon_i2c_bit_ops ); + + /* Configure the EEPROM SPI device. Generally, an Atmel 25040 + * (or similar) is used, but this is only possible if there is also + * a flash device present to store the boot-time chip configuration. + */ + if ( has_eeprom ) { + if ( has_flash && ad9bit ) + init_at25040 ( &efab->spi_eeprom ); + else + init_mc25xx640 ( &efab->spi_eeprom ); + falcon_init_spi_device ( efab, &efab->spi_eeprom ); + } + + /* Configure the FLASH SPI device */ + if ( has_flash ) { + init_at25f1024 ( &efab->spi_flash ); + falcon_init_spi_device ( efab, &efab->spi_flash ); + } + + EFAB_LOG ( "flash is %s, EEPROM is %s%s\n", + ( has_flash ? "present" : "absent" ), + ( has_eeprom ? "present " : "absent" ), + ( has_eeprom ? (ad9bit ? "(9bit)" : "(16bit)") : "") ); + + /* The device MUST have flash or eeprom */ + if ( ! efab->spi ) { + EFAB_ERR ( "Device appears to have no flash or eeprom\n" ); + return -EIO; + } + + /* If the device has EEPROM attached, then advertise NVO space */ + if ( has_eeprom ) + nvo_init ( &efab->nvo, &efab->spi_eeprom.nvs, falcon_nvo_fragments, + &efab->netdev->refcnt ); + + return 0; +} + +static int +falcon_probe_nvram ( struct efab_nic *efab ) +{ + struct nvs_device *nvs = &efab->spi->nvs; + struct falcon_nv_extra nv; + int rc, board_revision; + + /* Read the MAC address */ + rc = nvs_read ( nvs, FALCON_MAC_ADDRESS_OFFSET, + efab->mac_addr, ETH_ALEN ); + if ( rc ) + return rc; + + /* Poke through the NVRAM structure for the PHY type. */ + rc = nvs_read ( nvs, SF_NV_CONFIG_BASE + SF_NV_CONFIG_EXTRA, + &nv, sizeof ( nv ) ); + if ( rc ) + return rc; + + /* Handle each supported NVRAM version */ + if ( ( le16_to_cpu ( nv.magicnumber ) == FCN_NV_MAGIC_NUMBER ) && + ( le16_to_cpu ( nv.structure_version ) >= 2 ) ) { + struct falcon_nv_config_ver2* ver2 = &nv.ver_specific.ver2; + + /* Get the PHY type */ + efab->phy_addr = le16_to_cpu ( ver2->port0_phy_addr ); + efab->phy_type = le16_to_cpu ( ver2->port0_phy_type ); + board_revision = le16_to_cpu ( ver2->board_revision ); + } + else { + EFAB_ERR ( "NVram is not recognised\n" ); + return -EINVAL; + } + + efab->board_type = BOARD_TYPE ( board_revision ); + + EFAB_TRACE ( "Falcon board %d phy %d @ addr %d\n", + efab->board_type, efab->phy_type, efab->phy_addr ); + + /* Patch in the board operations */ + switch ( efab->board_type ) { + case EFAB_BOARD_SFE4001: + efab->board_op = &sfe4001_ops; + break; + case EFAB_BOARD_SFE4002: + efab->board_op = &sfe4002_ops; + break; + case EFAB_BOARD_SFE4003: + efab->board_op = &sfe4003_ops; + break; + default: + EFAB_ERR ( "Unrecognised board type\n" ); + return -EINVAL; + } + + /* Patch in MAC operations */ + if ( efab->phy_10g ) + efab->mac_op = &falcon_xmac_operations; + else + efab->mac_op = &falcon_gmac_operations; + + /* Hook in the PHY ops */ + switch ( efab->phy_type ) { + case PHY_TYPE_10XPRESS: + efab->phy_op = &falcon_tenxpress_phy_ops; + break; + case PHY_TYPE_CX4: + efab->phy_op = &falcon_xaui_phy_ops; + break; + case PHY_TYPE_XFP: + efab->phy_op = &falcon_xfp_phy_ops; + break; + case PHY_TYPE_CX4_RTMR: + efab->phy_op = &falcon_txc_phy_ops; + break; + case PHY_TYPE_PM8358: + efab->phy_op = &falcon_pm8358_phy_ops; + break; + case PHY_TYPE_1GIG_ALASKA: + efab->phy_op = &falcon_alaska_phy_ops; + break; + default: + EFAB_ERR ( "Unknown PHY type: %d\n", efab->phy_type ); + return -EINVAL; + } + + return 0; +} + +static int +falcon_init_sram ( struct efab_nic *efab ) +{ + efab_oword_t reg; + int count; + + /* use card in internal SRAM mode */ + falcon_read ( efab, ®, FCN_NIC_STAT_REG ); + EFAB_SET_OWORD_FIELD ( reg, FCN_ONCHIP_SRAM, 1 ); + falcon_write ( efab, ®, FCN_NIC_STAT_REG ); + + /* Deactivate any external SRAM that might be present */ + EFAB_POPULATE_OWORD_2 ( reg, + FCN_GPIO1_OEN, 1, + FCN_GPIO1_OUT, 1 ); + falcon_write ( efab, ®, FCN_GPIO_CTL_REG_KER ); + + /* Initiate SRAM reset */ + EFAB_POPULATE_OWORD_2 ( reg, + FCN_SRAM_OOB_BT_INIT_EN, 1, + FCN_SRM_NUM_BANKS_AND_BANK_SIZE, 0 ); + falcon_write ( efab, ®, FCN_SRM_CFG_REG_KER ); + + /* Wait for SRAM reset to complete */ + count = 0; + do { + /* SRAM reset is slow; expect around 16ms */ + mdelay ( 20 ); + + /* Check for reset complete */ + falcon_read ( efab, ®, FCN_SRM_CFG_REG_KER ); + if ( !EFAB_OWORD_FIELD ( reg, FCN_SRAM_OOB_BT_INIT_EN ) ) + return 0; + } while (++count < 20); /* wait upto 0.4 sec */ + + EFAB_ERR ( "timed out waiting for SRAM reset\n"); + return -ETIMEDOUT; +} + +static void +falcon_setup_nic ( struct efab_nic *efab ) +{ + efab_dword_t timer_cmd; + efab_oword_t reg; + int tx_fc, xoff_thresh, xon_thresh; + + /* bug5129: Clear the parity enables on the TX data fifos as + * they produce false parity errors because of timing issues + */ + falcon_read ( efab, ®, FCN_SPARE_REG_KER ); + EFAB_SET_OWORD_FIELD ( reg, FCN_MEM_PERR_EN_TX_DATA, 0 ); + falcon_write ( efab, ®, FCN_SPARE_REG_KER ); + + /* Set up TX and RX descriptor caches in SRAM */ + EFAB_POPULATE_OWORD_1 ( reg, FCN_SRM_TX_DC_BASE_ADR, 0x130000 ); + falcon_write ( efab, ®, FCN_SRM_TX_DC_CFG_REG_KER ); + EFAB_POPULATE_OWORD_1 ( reg, FCN_TX_DC_SIZE, 1 /* 16 descriptors */ ); + falcon_write ( efab, ®, FCN_TX_DC_CFG_REG_KER ); + EFAB_POPULATE_OWORD_1 ( reg, FCN_SRM_RX_DC_BASE_ADR, 0x100000 ); + falcon_write ( efab, ®, FCN_SRM_RX_DC_CFG_REG_KER ); + EFAB_POPULATE_OWORD_1 ( reg, FCN_RX_DC_SIZE, 2 /* 32 descriptors */ ); + falcon_write ( efab, ®, FCN_RX_DC_CFG_REG_KER ); + + /* Set number of RSS CPUs + * bug7244: Increase filter depth to reduce RX_RESET likelyhood + */ + EFAB_POPULATE_OWORD_5 ( reg, + FCN_NUM_KER, 0, + FCN_UDP_FULL_SRCH_LIMIT, 8, + FCN_UDP_WILD_SRCH_LIMIT, 8, + FCN_TCP_WILD_SRCH_LIMIT, 8, + FCN_TCP_FULL_SRCH_LIMIT, 8); + falcon_write ( efab, ®, FCN_RX_FILTER_CTL_REG_KER ); + udelay ( 1000 ); + + /* Setup RX. Wait for descriptor is broken and must + * be disabled. RXDP recovery shouldn't be needed, but is. + * disable ISCSI parsing because we don't need it + */ + falcon_read ( efab, ®, FCN_RX_SELF_RST_REG_KER ); + EFAB_SET_OWORD_FIELD ( reg, FCN_RX_NODESC_WAIT_DIS, 1 ); + EFAB_SET_OWORD_FIELD ( reg, FCN_RX_RECOVERY_EN, 1 ); + EFAB_SET_OWORD_FIELD ( reg, FCN_RX_ISCSI_DIS, 1 ); + falcon_write ( efab, ®, FCN_RX_SELF_RST_REG_KER ); + + /* Determine recommended flow control settings. * + * Flow control is qualified on B0 and A1/1G, not on A1/10G */ + if ( efab->pci_revision == FALCON_REV_B0 ) { + tx_fc = 1; + xoff_thresh = 54272; /* ~80Kb - 3*max MTU */ + xon_thresh = 27648; /* ~3*max MTU */ + } + else if ( !efab->phy_10g ) { + tx_fc = 1; + xoff_thresh = 2048; + xon_thresh = 512; + } + else { + tx_fc = xoff_thresh = xon_thresh = 0; + } + + /* Setup TX and RX */ + falcon_read ( efab, ®, FCN_TX_CFG2_REG_KER ); + EFAB_SET_OWORD_FIELD ( reg, FCN_TX_DIS_NON_IP_EV, 1 ); + falcon_write ( efab, ®, FCN_TX_CFG2_REG_KER ); + + falcon_read ( efab, ®, FCN_RX_CFG_REG_KER ); + EFAB_SET_OWORD_FIELD_VER ( efab, reg, FCN_RX_USR_BUF_SIZE, + (3*4096) / 32 ); + if ( efab->pci_revision == FALCON_REV_B0) + EFAB_SET_OWORD_FIELD ( reg, FCN_RX_INGR_EN_B0, 1 ); + EFAB_SET_OWORD_FIELD_VER ( efab, reg, FCN_RX_XON_MAC_TH, + xon_thresh / 256); + EFAB_SET_OWORD_FIELD_VER ( efab, reg, FCN_RX_XOFF_MAC_TH, + xoff_thresh / 256); + EFAB_SET_OWORD_FIELD_VER ( efab, reg, FCN_RX_XOFF_MAC_EN, tx_fc); + falcon_write ( efab, ®, FCN_RX_CFG_REG_KER ); + + /* Set timer register */ + EFAB_POPULATE_DWORD_2 ( timer_cmd, + FCN_TIMER_MODE, FCN_TIMER_MODE_DIS, + FCN_TIMER_VAL, 0 ); + falcon_writel ( efab, &timer_cmd, FCN_TIMER_CMD_REG_KER ); +} + +static void +falcon_init_resources ( struct efab_nic *efab ) +{ + struct efab_ev_queue *ev_queue = &efab->ev_queue; + struct efab_rx_queue *rx_queue = &efab->rx_queue; + struct efab_tx_queue *tx_queue = &efab->tx_queue; + + efab_oword_t reg; + int jumbo; + + /* Initialise the ptrs */ + tx_queue->read_ptr = tx_queue->write_ptr = 0; + rx_queue->read_ptr = rx_queue->write_ptr = 0; + ev_queue->read_ptr = 0; + + /* Push the event queue to the hardware */ + EFAB_POPULATE_OWORD_3 ( reg, + FCN_EVQ_EN, 1, + FCN_EVQ_SIZE, FQS(FCN_EVQ, EFAB_EVQ_SIZE), + FCN_EVQ_BUF_BASE_ID, ev_queue->entry.id ); + falcon_write ( efab, ®, + FCN_REVISION_REG ( efab, FCN_EVQ_PTR_TBL_KER ) ); + + /* Push the tx queue to the hardware */ + EFAB_POPULATE_OWORD_8 ( reg, + FCN_TX_DESCQ_EN, 1, + FCN_TX_ISCSI_DDIG_EN, 0, + FCN_TX_ISCSI_DDIG_EN, 0, + FCN_TX_DESCQ_BUF_BASE_ID, tx_queue->entry.id, + FCN_TX_DESCQ_EVQ_ID, 0, + FCN_TX_DESCQ_SIZE, FQS(FCN_TX_DESCQ, EFAB_TXD_SIZE), + FCN_TX_DESCQ_TYPE, 0 /* kernel queue */, + FCN_TX_NON_IP_DROP_DIS_B0, 1 ); + falcon_write ( efab, ®, + FCN_REVISION_REG ( efab, FCN_TX_DESC_PTR_TBL_KER ) ); + + /* Push the rx queue to the hardware */ + jumbo = ( efab->pci_revision == FALCON_REV_B0 ) ? 0 : 1; + EFAB_POPULATE_OWORD_8 ( reg, + FCN_RX_ISCSI_DDIG_EN, 0, + FCN_RX_ISCSI_HDIG_EN, 0, + FCN_RX_DESCQ_BUF_BASE_ID, rx_queue->entry.id, + FCN_RX_DESCQ_EVQ_ID, 0, + FCN_RX_DESCQ_SIZE, FQS(FCN_RX_DESCQ, EFAB_RXD_SIZE), + FCN_RX_DESCQ_TYPE, 0 /* kernel queue */, + FCN_RX_DESCQ_JUMBO, jumbo, + FCN_RX_DESCQ_EN, 1 ); + falcon_write ( efab, ®, + FCN_REVISION_REG ( efab, FCN_RX_DESC_PTR_TBL_KER ) ); + + /* Program INT_ADR_REG_KER */ + EFAB_POPULATE_OWORD_1 ( reg, + FCN_INT_ADR_KER, virt_to_bus ( &efab->int_ker ) ); + falcon_write ( efab, ®, FCN_INT_ADR_REG_KER ); + + /* Ack the event queue */ + falcon_eventq_read_ack ( efab, ev_queue ); +} + +static void +falcon_fini_resources ( struct efab_nic *efab ) +{ + efab_oword_t cmd; + + /* Disable interrupts */ + falcon_interrupts ( efab, 0, 0 ); + + /* Flush the dma queues */ + EFAB_POPULATE_OWORD_2 ( cmd, + FCN_TX_FLUSH_DESCQ_CMD, 1, + FCN_TX_FLUSH_DESCQ, 0 ); + falcon_write ( efab, &cmd, + FCN_REVISION_REG ( efab, FCN_TX_DESC_PTR_TBL_KER ) ); + + EFAB_POPULATE_OWORD_2 ( cmd, + FCN_RX_FLUSH_DESCQ_CMD, 1, + FCN_RX_FLUSH_DESCQ, 0 ); + falcon_write ( efab, &cmd, + FCN_REVISION_REG ( efab, FCN_RX_DESC_PTR_TBL_KER ) ); + + mdelay ( 100 ); + + /* Remove descriptor rings from card */ + EFAB_ZERO_OWORD ( cmd ); + falcon_write ( efab, &cmd, + FCN_REVISION_REG ( efab, FCN_TX_DESC_PTR_TBL_KER ) ); + falcon_write ( efab, &cmd, + FCN_REVISION_REG ( efab, FCN_RX_DESC_PTR_TBL_KER ) ); + falcon_write ( efab, &cmd, + FCN_REVISION_REG ( efab, FCN_EVQ_PTR_TBL_KER ) ); +} + +/******************************************************************************* + * + * + * Hardware rx path + * + * + *******************************************************************************/ + +static void +falcon_build_rx_desc ( falcon_rx_desc_t *rxd, struct io_buffer *iob ) +{ + EFAB_POPULATE_QWORD_2 ( *rxd, + FCN_RX_KER_BUF_SIZE, EFAB_RX_BUF_SIZE, + FCN_RX_KER_BUF_ADR, virt_to_bus ( iob->data ) ); +} + +static void +falcon_notify_rx_desc ( struct efab_nic *efab, struct efab_rx_queue *rx_queue ) +{ + efab_dword_t reg; + int ptr = rx_queue->write_ptr % EFAB_RXD_SIZE; + + EFAB_POPULATE_DWORD_1 ( reg, FCN_RX_DESC_WPTR_DWORD, ptr ); + falcon_writel ( efab, ®, FCN_RX_DESC_UPD_REG_KER_DWORD ); +} + + +/******************************************************************************* + * + * + * Hardware tx path + * + * + *******************************************************************************/ + +static void +falcon_build_tx_desc ( falcon_tx_desc_t *txd, struct io_buffer *iob ) +{ + EFAB_POPULATE_QWORD_2 ( *txd, + FCN_TX_KER_BYTE_CNT, iob_len ( iob ), + FCN_TX_KER_BUF_ADR, virt_to_bus ( iob->data ) ); +} + +static void +falcon_notify_tx_desc ( struct efab_nic *efab, + struct efab_tx_queue *tx_queue ) +{ + efab_dword_t reg; + int ptr = tx_queue->write_ptr % EFAB_TXD_SIZE; + + EFAB_POPULATE_DWORD_1 ( reg, FCN_TX_DESC_WPTR_DWORD, ptr ); + falcon_writel ( efab, ®, FCN_TX_DESC_UPD_REG_KER_DWORD ); +} + + +/******************************************************************************* + * + * + * Software receive interface + * + * + *******************************************************************************/ + +static int +efab_fill_rx_queue ( struct efab_nic *efab, + struct efab_rx_queue *rx_queue ) +{ + int fill_level = rx_queue->write_ptr - rx_queue->read_ptr; + int space = EFAB_NUM_RX_DESC - fill_level - 1; + int pushed = 0; + + while ( space ) { + int buf_id = rx_queue->write_ptr % EFAB_NUM_RX_DESC; + int desc_id = rx_queue->write_ptr % EFAB_RXD_SIZE; + struct io_buffer *iob; + falcon_rx_desc_t *rxd; + + assert ( rx_queue->buf[buf_id] == NULL ); + iob = alloc_iob ( EFAB_RX_BUF_SIZE ); + if ( !iob ) + break; + + EFAB_TRACE ( "pushing rx_buf[%d] iob %p data %p\n", + buf_id, iob, iob->data ); + + rx_queue->buf[buf_id] = iob; + rxd = rx_queue->ring + desc_id; + falcon_build_rx_desc ( rxd, iob ); + ++rx_queue->write_ptr; + ++pushed; + --space; + } + + if ( pushed ) { + /* Push the ptr to hardware */ + falcon_notify_rx_desc ( efab, rx_queue ); + + fill_level = rx_queue->write_ptr - rx_queue->read_ptr; + EFAB_TRACE ( "pushed %d rx buffers to fill level %d\n", + pushed, fill_level ); + } + + if ( fill_level == 0 ) + return -ENOMEM; + return 0; +} + +static void +efab_receive ( struct efab_nic *efab, unsigned int id, int len, int drop ) +{ + struct efab_rx_queue *rx_queue = &efab->rx_queue; + struct io_buffer *iob; + unsigned int read_ptr = rx_queue->read_ptr % EFAB_RXD_SIZE; + unsigned int buf_ptr = rx_queue->read_ptr % EFAB_NUM_RX_DESC; + + assert ( id == read_ptr ); + + /* Pop this rx buffer out of the software ring */ + iob = rx_queue->buf[buf_ptr]; + rx_queue->buf[buf_ptr] = NULL; + + EFAB_TRACE ( "popping rx_buf[%d] iob %p data %p with %d bytes %s\n", + id, iob, iob->data, len, drop ? "bad" : "ok" ); + + /* Pass the packet up if required */ + if ( drop ) + free_iob ( iob ); + else { + iob_put ( iob, len ); + netdev_rx ( efab->netdev, iob ); + } + + ++rx_queue->read_ptr; +} + +/******************************************************************************* + * + * + * Software transmit interface + * + * + *******************************************************************************/ + +static int +efab_transmit ( struct net_device *netdev, struct io_buffer *iob ) +{ + struct efab_nic *efab = netdev_priv ( netdev ); + struct efab_tx_queue *tx_queue = &efab->tx_queue; + int fill_level, space; + falcon_tx_desc_t *txd; + int buf_id; + + fill_level = tx_queue->write_ptr - tx_queue->read_ptr; + space = EFAB_TXD_SIZE - fill_level - 1; + if ( space < 1 ) + return -ENOBUFS; + + /* Save the iobuffer for later completion */ + buf_id = tx_queue->write_ptr % EFAB_TXD_SIZE; + assert ( tx_queue->buf[buf_id] == NULL ); + tx_queue->buf[buf_id] = iob; + + EFAB_TRACE ( "tx_buf[%d] for iob %p data %p len %zd\n", + buf_id, iob, iob->data, iob_len ( iob ) ); + + /* Form the descriptor, and push it to hardware */ + txd = tx_queue->ring + buf_id; + falcon_build_tx_desc ( txd, iob ); + ++tx_queue->write_ptr; + falcon_notify_tx_desc ( efab, tx_queue ); + + return 0; +} + +static int +efab_transmit_done ( struct efab_nic *efab, int id ) +{ + struct efab_tx_queue *tx_queue = &efab->tx_queue; + unsigned int read_ptr, stop; + + /* Complete all buffers from read_ptr up to and including id */ + read_ptr = tx_queue->read_ptr % EFAB_TXD_SIZE; + stop = ( id + 1 ) % EFAB_TXD_SIZE; + + while ( read_ptr != stop ) { + struct io_buffer *iob = tx_queue->buf[read_ptr]; + assert ( iob ); + + /* Complete the tx buffer */ + if ( iob ) + netdev_tx_complete ( efab->netdev, iob ); + tx_queue->buf[read_ptr] = NULL; + + ++tx_queue->read_ptr; + read_ptr = tx_queue->read_ptr % EFAB_TXD_SIZE; + } + + return 0; +} + +/******************************************************************************* + * + * + * Hardware event path + * + * + *******************************************************************************/ + +static void +falcon_clear_interrupts ( struct efab_nic *efab ) +{ + efab_dword_t reg; + + if ( efab->pci_revision == FALCON_REV_B0 ) { + /* read the ISR */ + falcon_readl( efab, ®, INT_ISR0_B0 ); + } + else { + /* write to the INT_ACK register */ + falcon_writel ( efab, 0, FCN_INT_ACK_KER_REG_A1 ); + mb(); + falcon_readl ( efab, ®, + WORK_AROUND_BROKEN_PCI_READS_REG_KER_A1 ); + } +} + +static void +falcon_handle_event ( struct efab_nic *efab, falcon_event_t *evt ) +{ + int ev_code, desc_ptr, len, drop; + + /* Decode event */ + ev_code = EFAB_QWORD_FIELD ( *evt, FCN_EV_CODE ); + switch ( ev_code ) { + case FCN_TX_IP_EV_DECODE: + desc_ptr = EFAB_QWORD_FIELD ( *evt, FCN_TX_EV_DESC_PTR ); + efab_transmit_done ( efab, desc_ptr ); + break; + + case FCN_RX_IP_EV_DECODE: + desc_ptr = EFAB_QWORD_FIELD ( *evt, FCN_RX_EV_DESC_PTR ); + len = EFAB_QWORD_FIELD ( *evt, FCN_RX_EV_BYTE_CNT ); + drop = !EFAB_QWORD_FIELD ( *evt, FCN_RX_EV_PKT_OK ); + + efab_receive ( efab, desc_ptr, len, drop ); + break; + + default: + EFAB_TRACE ( "Unknown event type %d\n", ev_code ); + break; + } +} + +/******************************************************************************* + * + * + * Software (polling) interrupt handler + * + * + *******************************************************************************/ + +static void +efab_poll ( struct net_device *netdev ) +{ + struct efab_nic *efab = netdev_priv ( netdev ); + struct efab_ev_queue *ev_queue = &efab->ev_queue; + struct efab_rx_queue *rx_queue = &efab->rx_queue; + falcon_event_t *evt; + + /* Read the event queue by directly looking for events + * (we don't even bother to read the eventq write ptr) */ + evt = ev_queue->ring + ev_queue->read_ptr; + while ( falcon_event_present ( evt ) ) { + + EFAB_TRACE ( "Event at index 0x%x address %p is " + EFAB_QWORD_FMT "\n", ev_queue->read_ptr, + evt, EFAB_QWORD_VAL ( *evt ) ); + + falcon_handle_event ( efab, evt ); + + /* Clear the event */ + EFAB_SET_QWORD ( *evt ); + + /* Move to the next event. We don't ack the event + * queue until the end */ + ev_queue->read_ptr = ( ( ev_queue->read_ptr + 1 ) % + EFAB_EVQ_SIZE ); + evt = ev_queue->ring + ev_queue->read_ptr; + } + + /* Push more buffers if needed */ + (void) efab_fill_rx_queue ( efab, rx_queue ); + + /* Clear any pending interrupts */ + falcon_clear_interrupts ( efab ); + + /* Ack the event queue */ + falcon_eventq_read_ack ( efab, ev_queue ); +} + +static void +efab_irq ( struct net_device *netdev, int enable ) +{ + struct efab_nic *efab = netdev_priv ( netdev ); + struct efab_ev_queue *ev_queue = &efab->ev_queue; + + switch ( enable ) { + case 0: + falcon_interrupts ( efab, 0, 0 ); + break; + case 1: + falcon_interrupts ( efab, 1, 0 ); + falcon_eventq_read_ack ( efab, ev_queue ); + break; + case 2: + falcon_interrupts ( efab, 1, 1 ); + break; + } +} + +/******************************************************************************* + * + * + * Software open/close + * + * + *******************************************************************************/ + +static void +efab_free_resources ( struct efab_nic *efab ) +{ + struct efab_ev_queue *ev_queue = &efab->ev_queue; + struct efab_rx_queue *rx_queue = &efab->rx_queue; + struct efab_tx_queue *tx_queue = &efab->tx_queue; + int i; + + for ( i = 0; i < EFAB_NUM_RX_DESC; i++ ) { + if ( rx_queue->buf[i] ) + free_iob ( rx_queue->buf[i] ); + } + + for ( i = 0; i < EFAB_TXD_SIZE; i++ ) { + if ( tx_queue->buf[i] ) + netdev_tx_complete ( efab->netdev, tx_queue->buf[i] ); + } + + if ( rx_queue->ring ) + falcon_free_special_buffer ( rx_queue->ring ); + + if ( tx_queue->ring ) + falcon_free_special_buffer ( tx_queue->ring ); + + if ( ev_queue->ring ) + falcon_free_special_buffer ( ev_queue->ring ); + + memset ( rx_queue, 0, sizeof ( *rx_queue ) ); + memset ( tx_queue, 0, sizeof ( *tx_queue ) ); + memset ( ev_queue, 0, sizeof ( *ev_queue ) ); + + /* Ensure subsequent buffer allocations start at id 0 */ + efab->buffer_head = 0; +} + +static int +efab_alloc_resources ( struct efab_nic *efab ) +{ + struct efab_ev_queue *ev_queue = &efab->ev_queue; + struct efab_rx_queue *rx_queue = &efab->rx_queue; + struct efab_tx_queue *tx_queue = &efab->tx_queue; + size_t bytes; + + /* Allocate the hardware event queue */ + bytes = sizeof ( falcon_event_t ) * EFAB_TXD_SIZE; + ev_queue->ring = falcon_alloc_special_buffer ( efab, bytes, + &ev_queue->entry ); + if ( !ev_queue->ring ) + goto fail1; + + /* Initialise the hardware event queue */ + memset ( ev_queue->ring, 0xff, bytes ); + + /* Allocate the hardware tx queue */ + bytes = sizeof ( falcon_tx_desc_t ) * EFAB_TXD_SIZE; + tx_queue->ring = falcon_alloc_special_buffer ( efab, bytes, + &tx_queue->entry ); + if ( ! tx_queue->ring ) + goto fail2; + + /* Allocate the hardware rx queue */ + bytes = sizeof ( falcon_rx_desc_t ) * EFAB_RXD_SIZE; + rx_queue->ring = falcon_alloc_special_buffer ( efab, bytes, + &rx_queue->entry ); + if ( ! rx_queue->ring ) + goto fail3; + + return 0; + +fail3: + falcon_free_special_buffer ( tx_queue->ring ); + tx_queue->ring = NULL; +fail2: + falcon_free_special_buffer ( ev_queue->ring ); + ev_queue->ring = NULL; +fail1: + return -ENOMEM; +} + +static int +efab_init_mac ( struct efab_nic *efab ) +{ + int count, rc; + + /* This can take several seconds */ + EFAB_LOG ( "Waiting for link..\n" ); + for ( count=0; count<5; count++ ) { + rc = efab->mac_op->init ( efab ); + if ( rc ) { + EFAB_ERR ( "Failed reinitialising MAC, error %s\n", + strerror ( rc )); + return rc; + } + + /* Sleep for 2s to wait for the link to settle, either + * because we want to use it, or because we're about + * to reset the mac anyway + */ + sleep ( 2 ); + + if ( ! efab->link_up ) { + EFAB_ERR ( "!\n" ); + continue; + } + + EFAB_LOG ( "\n%dMbps %s-duplex\n", + ( efab->link_options & LPA_10000 ? 10000 : + ( efab->link_options & LPA_1000 ? 1000 : + ( efab->link_options & LPA_100 ? 100 : 10 ) ) ), + ( efab->link_options & LPA_DUPLEX ? + "full" : "half" ) ); + + /* TODO: Move link state handling to the poll() routine */ + netdev_link_up ( efab->netdev ); + return 0; + } + + EFAB_ERR ( "timed initialising MAC\n" ); + return -ETIMEDOUT; +} + +static void +efab_close ( struct net_device *netdev ) +{ + struct efab_nic *efab = netdev_priv ( netdev ); + + falcon_fini_resources ( efab ); + efab_free_resources ( efab ); + efab->board_op->fini ( efab ); + falcon_reset ( efab ); +} + +static int +efab_open ( struct net_device *netdev ) +{ + struct efab_nic *efab = netdev_priv ( netdev ); + struct efab_rx_queue *rx_queue = &efab->rx_queue; + int rc; + + rc = falcon_reset ( efab ); + if ( rc ) + goto fail1; + + rc = efab->board_op->init ( efab ); + if ( rc ) + goto fail2; + + rc = falcon_init_sram ( efab ); + if ( rc ) + goto fail3; + + /* Configure descriptor caches before pushing hardware queues */ + falcon_setup_nic ( efab ); + + rc = efab_alloc_resources ( efab ); + if ( rc ) + goto fail4; + + falcon_init_resources ( efab ); + + /* Push rx buffers */ + rc = efab_fill_rx_queue ( efab, rx_queue ); + if ( rc ) + goto fail5; + + /* Try and bring the interface up */ + rc = efab_init_mac ( efab ); + if ( rc ) + goto fail6; + + return 0; + +fail6: +fail5: + efab_free_resources ( efab ); +fail4: +fail3: + efab->board_op->fini ( efab ); +fail2: + falcon_reset ( efab ); +fail1: + return rc; +} + +static struct net_device_operations efab_operations = { + .open = efab_open, + .close = efab_close, + .transmit = efab_transmit, + .poll = efab_poll, + .irq = efab_irq, +}; + +static void +efab_remove ( struct pci_device *pci ) +{ + struct net_device *netdev = pci_get_drvdata ( pci ); + struct efab_nic *efab = netdev_priv ( netdev ); + + if ( efab->membase ) { + falcon_reset ( efab ); + + iounmap ( efab->membase ); + efab->membase = NULL; + } + + if ( efab->nvo.nvs ) { + unregister_nvo ( &efab->nvo ); + efab->nvo.nvs = NULL; + } + + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +static int +efab_probe ( struct pci_device *pci, + const struct pci_device_id *id ) +{ + struct net_device *netdev; + struct efab_nic *efab; + unsigned long mmio_start, mmio_len; + int rc; + + /* Create the network adapter */ + netdev = alloc_etherdev ( sizeof ( struct efab_nic ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto fail1; + } + + /* Initialise the network adapter, and initialise private storage */ + netdev_init ( netdev, &efab_operations ); + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + + efab = netdev_priv ( netdev ); + memset ( efab, 0, sizeof ( *efab ) ); + efab->netdev = netdev; + + /* Get iobase/membase */ + mmio_start = pci_bar_start ( pci, PCI_BASE_ADDRESS_2 ); + mmio_len = pci_bar_size ( pci, PCI_BASE_ADDRESS_2 ); + efab->membase = ioremap ( mmio_start, mmio_len ); + EFAB_TRACE ( "BAR of %lx bytes at phys %lx mapped at %p\n", + mmio_len, mmio_start, efab->membase ); + + /* Enable the PCI device */ + adjust_pci_device ( pci ); + efab->iobase = pci->ioaddr & ~3; + + /* Determine the NIC variant */ + falcon_probe_nic_variant ( efab, pci ); + + /* Read the SPI interface and determine the MAC address, + * and the board and phy variant. Hook in the op tables */ + rc = falcon_probe_spi ( efab ); + if ( rc ) + goto fail2; + rc = falcon_probe_nvram ( efab ); + if ( rc ) + goto fail3; + + memcpy ( netdev->ll_addr, efab->mac_addr, ETH_ALEN ); + + netdev_link_up ( netdev ); + rc = register_netdev ( netdev ); + if ( rc ) + goto fail4; + + /* Advertise non-volatile storage */ + if ( efab->nvo.nvs ) { + rc = register_nvo ( &efab->nvo, netdev_settings ( netdev ) ); + if ( rc ) + goto fail5; + } + + EFAB_LOG ( "Found %s EtherFabric %s %s revision %d\n", id->name, + efab->is_asic ? "ASIC" : "FPGA", + efab->phy_10g ? "10G" : "1G", + efab->pci_revision ); + + return 0; + +fail5: + unregister_netdev ( netdev ); +fail4: +fail3: +fail2: + iounmap ( efab->membase ); + efab->membase = NULL; + netdev_put ( netdev ); +fail1: + return rc; +} + + +static struct pci_device_id efab_nics[] = { + PCI_ROM(0x1924, 0x0703, "falcon", "EtherFabric Falcon"), + PCI_ROM(0x1924, 0x0710, "falconb0", "EtherFabric FalconB0"), +}; + +struct pci_driver etherfabric_driver __pci_driver = { + .ids = efab_nics, + .id_count = sizeof ( efab_nics ) / sizeof ( efab_nics[0] ), + .probe = efab_probe, + .remove = efab_remove, +}; + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/etherfabric.h b/gpxe/src/drivers/net/etherfabric.h new file mode 100644 index 0000000..950f8de --- /dev/null +++ b/gpxe/src/drivers/net/etherfabric.h @@ -0,0 +1,551 @@ +/************************************************************************** + * + * GPL net driver for Level 5 Etherfabric network cards + * + * Written by Michael Brown + * + * Copyright Fen Systems Ltd. 2005 + * Copyright Level 5 Networks Inc. 2005 + * + * This software may be used and distributed according to the terms of + * the GNU General Public License (GPL), incorporated herein by + * reference. Drivers based on or derived from this code fall under + * the GPL and must retain the authorship, copyright and license + * notice. This file is not a complete program and may only be used + * when the entire operating system is licensed under the GPL. + * + ************************************************************************** + */ + +#ifndef EFAB_BITFIELD_H +#define EFAB_BITFIELD_H + +/** @file + * + * Etherfabric bitfield access + * + * Etherfabric NICs make extensive use of bitfields up to 128 bits + * wide. Since there is no native 128-bit datatype on most systems, + * and since 64-bit datatypes are inefficient on 32-bit systems and + * vice versa, we wrap accesses in a way that uses the most efficient + * datatype. + * + * The NICs are PCI devices and therefore little-endian. Since most + * of the quantities that we deal with are DMAed to/from host memory, + * we define our datatypes (efab_oword_t, efab_qword_t and + * efab_dword_t) to be little-endian. + * + * In the less common case of using PIO for individual register + * writes, we construct the little-endian datatype in host memory and + * then use non-swapping equivalents of writel/writeq, rather than + * constructing a native-endian datatype and relying on the implicit + * byte-swapping done by writel/writeq. (We use a similar strategy + * for register reads.) + */ + +/** Dummy field low bit number */ +#define EFAB_DUMMY_FIELD_LBN 0 +/** Dummy field width */ +#define EFAB_DUMMY_FIELD_WIDTH 0 +/** Dword 0 low bit number */ +#define EFAB_DWORD_0_LBN 0 +/** Dword 0 width */ +#define EFAB_DWORD_0_WIDTH 32 +/** Dword 1 low bit number */ +#define EFAB_DWORD_1_LBN 32 +/** Dword 1 width */ +#define EFAB_DWORD_1_WIDTH 32 +/** Dword 2 low bit number */ +#define EFAB_DWORD_2_LBN 64 +/** Dword 2 width */ +#define EFAB_DWORD_2_WIDTH 32 +/** Dword 3 low bit number */ +#define EFAB_DWORD_3_LBN 96 +/** Dword 3 width */ +#define EFAB_DWORD_3_WIDTH 32 + +/** Specified attribute (e.g. LBN) of the specified field */ +#define EFAB_VAL(field,attribute) field ## _ ## attribute +/** Low bit number of the specified field */ +#define EFAB_LOW_BIT( field ) EFAB_VAL ( field, LBN ) +/** Bit width of the specified field */ +#define EFAB_WIDTH( field ) EFAB_VAL ( field, WIDTH ) +/** High bit number of the specified field */ +#define EFAB_HIGH_BIT(field) ( EFAB_LOW_BIT(field) + EFAB_WIDTH(field) - 1 ) +/** Mask equal in width to the specified field. + * + * For example, a field with width 5 would have a mask of 0x1f. + * + * The maximum width mask that can be generated is 64 bits. + */ +#define EFAB_MASK64( field ) \ + ( EFAB_WIDTH(field) == 64 ? ~( ( uint64_t ) 0 ) : \ + ( ( ( ( ( uint64_t ) 1 ) << EFAB_WIDTH(field) ) ) - 1 ) ) + +/** Mask equal in width to the specified field. + * + * For example, a field with width 5 would have a mask of 0x1f. + * + * The maximum width mask that can be generated is 32 bits. Use + * EFAB_MASK64 for higher width fields. + */ +#define EFAB_MASK32( field ) \ + ( EFAB_WIDTH(field) == 32 ? ~( ( uint32_t ) 0 ) : \ + ( ( ( ( ( uint32_t ) 1 ) << EFAB_WIDTH(field) ) ) - 1 ) ) + +/** A doubleword (i.e. 4 byte) datatype + * + * This datatype is defined to be little-endian. + */ +typedef union efab_dword { + uint32_t u32[1]; + uint32_t opaque; /* For bitwise operations between two efab_dwords */ +} efab_dword_t; + +/** A quadword (i.e. 8 byte) datatype + * + * This datatype is defined to be little-endian. + */ +typedef union efab_qword { + uint64_t u64[1]; + uint32_t u32[2]; + efab_dword_t dword[2]; +} efab_qword_t; + +/** + * An octword (eight-word, i.e. 16 byte) datatype + * + * This datatype is defined to be little-endian. + */ +typedef union efab_oword { + uint64_t u64[2]; + efab_qword_t qword[2]; + uint32_t u32[4]; + efab_dword_t dword[4]; +} efab_oword_t; + +/** Format string for printing an efab_dword_t */ +#define EFAB_DWORD_FMT "%08x" + +/** Format string for printing an efab_qword_t */ +#define EFAB_QWORD_FMT "%08x:%08x" + +/** Format string for printing an efab_oword_t */ +#define EFAB_OWORD_FMT "%08x:%08x:%08x:%08x" + +/** printk parameters for printing an efab_dword_t */ +#define EFAB_DWORD_VAL(dword) \ + ( ( unsigned int ) le32_to_cpu ( (dword).u32[0] ) ) + +/** printk parameters for printing an efab_qword_t */ +#define EFAB_QWORD_VAL(qword) \ + ( ( unsigned int ) le32_to_cpu ( (qword).u32[1] ) ), \ + ( ( unsigned int ) le32_to_cpu ( (qword).u32[0] ) ) + +/** printk parameters for printing an efab_oword_t */ +#define EFAB_OWORD_VAL(oword) \ + ( ( unsigned int ) le32_to_cpu ( (oword).u32[3] ) ), \ + ( ( unsigned int ) le32_to_cpu ( (oword).u32[2] ) ), \ + ( ( unsigned int ) le32_to_cpu ( (oword).u32[1] ) ), \ + ( ( unsigned int ) le32_to_cpu ( (oword).u32[0] ) ) + +/** + * Extract bit field portion [low,high) from the native-endian element + * which contains bits [min,max). + * + * For example, suppose "element" represents the high 32 bits of a + * 64-bit value, and we wish to extract the bits belonging to the bit + * field occupying bits 28-45 of this 64-bit value. + * + * Then EFAB_EXTRACT ( element, 32, 63, 28, 45 ) would give + * + * ( element ) << 4 + * + * The result will contain the relevant bits filled in in the range + * [0,high-low), with garbage in bits [high-low+1,...). + */ +#define EFAB_EXTRACT_NATIVE( native_element, min ,max ,low ,high ) \ + ( ( ( low > max ) || ( high < min ) ) ? 0 : \ + ( ( low > min ) ? \ + ( (native_element) >> ( low - min ) ) : \ + ( (native_element) << ( min - low ) ) ) ) + +/** + * Extract bit field portion [low,high) from the 64-bit little-endian + * element which contains bits [min,max) + */ +#define EFAB_EXTRACT64( element, min, max, low, high ) \ + EFAB_EXTRACT_NATIVE ( le64_to_cpu(element), min, max, low, high ) + +/** + * Extract bit field portion [low,high) from the 32-bit little-endian + * element which contains bits [min,max) + */ +#define EFAB_EXTRACT32( element, min, max, low, high ) \ + EFAB_EXTRACT_NATIVE ( le32_to_cpu(element), min, max, low, high ) + +#define EFAB_EXTRACT_OWORD64( oword, low, high ) \ + ( EFAB_EXTRACT64 ( (oword).u64[0], 0, 63, low, high ) | \ + EFAB_EXTRACT64 ( (oword).u64[1], 64, 127, low, high ) ) + +#define EFAB_EXTRACT_QWORD64( qword, low, high ) \ + ( EFAB_EXTRACT64 ( (qword).u64[0], 0, 63, low, high ) ) + +#define EFAB_EXTRACT_OWORD32( oword, low, high ) \ + ( EFAB_EXTRACT32 ( (oword).u32[0], 0, 31, low, high ) | \ + EFAB_EXTRACT32 ( (oword).u32[1], 32, 63, low, high ) | \ + EFAB_EXTRACT32 ( (oword).u32[2], 64, 95, low, high ) | \ + EFAB_EXTRACT32 ( (oword).u32[3], 96, 127, low, high ) ) + +#define EFAB_EXTRACT_QWORD32( qword, low, high ) \ + ( EFAB_EXTRACT32 ( (qword).u32[0], 0, 31, low, high ) | \ + EFAB_EXTRACT32 ( (qword).u32[1], 32, 63, low, high ) ) + +#define EFAB_EXTRACT_DWORD( dword, low, high ) \ + ( EFAB_EXTRACT32 ( (dword).u32[0], 0, 31, low, high ) ) + +#define EFAB_OWORD_FIELD64( oword, field ) \ + ( EFAB_EXTRACT_OWORD64 ( oword, EFAB_LOW_BIT ( field ), \ + EFAB_HIGH_BIT ( field ) ) & \ + EFAB_MASK64 ( field ) ) + +#define EFAB_QWORD_FIELD64( qword, field ) \ + ( EFAB_EXTRACT_QWORD64 ( qword, EFAB_LOW_BIT ( field ), \ + EFAB_HIGH_BIT ( field ) ) & \ + EFAB_MASK64 ( field ) ) + +#define EFAB_OWORD_FIELD32( oword, field ) \ + ( EFAB_EXTRACT_OWORD32 ( oword, EFAB_LOW_BIT ( field ), \ + EFAB_HIGH_BIT ( field ) ) & \ + EFAB_MASK32 ( field ) ) + +#define EFAB_QWORD_FIELD32( qword, field ) \ + ( EFAB_EXTRACT_QWORD32 ( qword, EFAB_LOW_BIT ( field ), \ + EFAB_HIGH_BIT ( field ) ) & \ + EFAB_MASK32 ( field ) ) + +#define EFAB_DWORD_FIELD( dword, field ) \ + ( EFAB_EXTRACT_DWORD ( dword, EFAB_LOW_BIT ( field ), \ + EFAB_HIGH_BIT ( field ) ) & \ + EFAB_MASK32 ( field ) ) + +#define EFAB_OWORD_IS_ZERO64( oword ) \ + ( ! ( (oword).u64[0] || (oword).u64[1] ) ) + +#define EFAB_QWORD_IS_ZERO64( qword ) \ + ( ! ( (qword).u64[0] ) ) + +#define EFAB_OWORD_IS_ZERO32( oword ) \ + ( ! ( (oword).u32[0] || (oword).u32[1] || \ + (oword).u32[2] || (oword).u32[3] ) ) + +#define EFAB_QWORD_IS_ZERO32( qword ) \ + ( ! ( (qword).u32[0] || (qword).u32[1] ) ) + +#define EFAB_DWORD_IS_ZERO( dword ) \ + ( ! ( (dword).u32[0] ) ) + +#define EFAB_OWORD_IS_ALL_ONES64( oword ) \ + ( ( (oword).u64[0] & (oword).u64[1] ) == ~( ( uint64_t ) 0 ) ) + +#define EFAB_QWORD_IS_ALL_ONES64( qword ) \ + ( (qword).u64[0] == ~( ( uint64_t ) 0 ) ) + +#define EFAB_OWORD_IS_ALL_ONES32( oword ) \ + ( ( (oword).u32[0] & (oword).u32[1] & \ + (oword).u32[2] & (oword).u32[3] ) == ~( ( uint32_t ) 0 ) ) + +#define EFAB_QWORD_IS_ALL_ONES32( qword ) \ + ( ( (qword).u32[0] & (qword).u32[1] ) == ~( ( uint32_t ) 0 ) ) + +#define EFAB_DWORD_IS_ALL_ONES( dword ) \ + ( (dword).u32[0] == ~( ( uint32_t ) 0 ) ) + +#if ( BITS_PER_LONG == 64 ) +#define EFAB_OWORD_FIELD EFAB_OWORD_FIELD64 +#define EFAB_QWORD_FIELD EFAB_QWORD_FIELD64 +#define EFAB_OWORD_IS_ZERO EFAB_OWORD_IS_ZERO64 +#define EFAB_QWORD_IS_ZERO EFAB_QWORD_IS_ZERO64 +#define EFAB_OWORD_IS_ALL_ONES EFAB_OWORD_IS_ALL_ONES64 +#define EFAB_QWORD_IS_ALL_ONES EFAB_QWORD_IS_ALL_ONES64 +#else +#define EFAB_OWORD_FIELD EFAB_OWORD_FIELD32 +#define EFAB_QWORD_FIELD EFAB_QWORD_FIELD32 +#define EFAB_OWORD_IS_ZERO EFAB_OWORD_IS_ZERO32 +#define EFAB_QWORD_IS_ZERO EFAB_QWORD_IS_ZERO32 +#define EFAB_OWORD_IS_ALL_ONES EFAB_OWORD_IS_ALL_ONES32 +#define EFAB_QWORD_IS_ALL_ONES EFAB_QWORD_IS_ALL_ONES32 +#endif + +/** + * Construct bit field portion + * + * Creates the portion of the bit field [low,high) that lies within + * the range [min,max). + */ +#define EFAB_INSERT_NATIVE64( min, max, low, high, value ) \ + ( ( ( low > max ) || ( high < min ) ) ? 0 : \ + ( ( low > min ) ? \ + ( ( ( uint64_t ) (value) ) << ( low - min ) ) : \ + ( ( ( uint64_t ) (value) ) >> ( min - low ) ) ) ) + +#define EFAB_INSERT_NATIVE32( min, max, low, high, value ) \ + ( ( ( low > max ) || ( high < min ) ) ? 0 : \ + ( ( low > min ) ? \ + ( ( ( uint32_t ) (value) ) << ( low - min ) ) : \ + ( ( ( uint32_t ) (value) ) >> ( min - low ) ) ) ) + +#define EFAB_INSERT_NATIVE( min, max, low, high, value ) \ + ( ( ( ( max - min ) >= 32 ) || \ + ( ( high - low ) >= 32 ) ) \ + ? EFAB_INSERT_NATIVE64 ( min, max, low, high, value ) \ + : EFAB_INSERT_NATIVE32 ( min, max, low, high, value ) ) + +/** + * Construct bit field portion + * + * Creates the portion of the named bit field that lies within the + * range [min,max). + */ +#define EFAB_INSERT_FIELD_NATIVE( min, max, field, value ) \ + EFAB_INSERT_NATIVE ( min, max, EFAB_LOW_BIT ( field ), \ + EFAB_HIGH_BIT ( field ), value ) + +/** + * Construct bit field + * + * Creates the portion of the named bit fields that lie within the + * range [min,max). + */ +#define EFAB_INSERT_FIELDS_NATIVE( min, max, \ + field1, value1, \ + field2, value2, \ + field3, value3, \ + field4, value4, \ + field5, value5, \ + field6, value6, \ + field7, value7, \ + field8, value8, \ + field9, value9, \ + field10, value10 ) \ + ( EFAB_INSERT_FIELD_NATIVE ( min, max, field1, value1 ) | \ + EFAB_INSERT_FIELD_NATIVE ( min, max, field2, value2 ) | \ + EFAB_INSERT_FIELD_NATIVE ( min, max, field3, value3 ) | \ + EFAB_INSERT_FIELD_NATIVE ( min, max, field4, value4 ) | \ + EFAB_INSERT_FIELD_NATIVE ( min, max, field5, value5 ) | \ + EFAB_INSERT_FIELD_NATIVE ( min, max, field6, value6 ) | \ + EFAB_INSERT_FIELD_NATIVE ( min, max, field7, value7 ) | \ + EFAB_INSERT_FIELD_NATIVE ( min, max, field8, value8 ) | \ + EFAB_INSERT_FIELD_NATIVE ( min, max, field9, value9 ) | \ + EFAB_INSERT_FIELD_NATIVE ( min, max, field10, value10 ) ) + +#define EFAB_INSERT_FIELDS64( ... ) \ + cpu_to_le64 ( EFAB_INSERT_FIELDS_NATIVE ( __VA_ARGS__ ) ) + +#define EFAB_INSERT_FIELDS32( ... ) \ + cpu_to_le32 ( EFAB_INSERT_FIELDS_NATIVE ( __VA_ARGS__ ) ) + +#define EFAB_POPULATE_OWORD64( oword, ... ) do { \ + (oword).u64[0] = EFAB_INSERT_FIELDS64 ( 0, 63, __VA_ARGS__ );\ + (oword).u64[1] = EFAB_INSERT_FIELDS64 ( 64, 127, __VA_ARGS__ );\ + } while ( 0 ) + +#define EFAB_POPULATE_QWORD64( qword, ... ) do { \ + (qword).u64[0] = EFAB_INSERT_FIELDS64 ( 0, 63, __VA_ARGS__ );\ + } while ( 0 ) + +#define EFAB_POPULATE_OWORD32( oword, ... ) do { \ + (oword).u32[0] = EFAB_INSERT_FIELDS32 ( 0, 31, __VA_ARGS__ );\ + (oword).u32[1] = EFAB_INSERT_FIELDS32 ( 32, 63, __VA_ARGS__ );\ + (oword).u32[2] = EFAB_INSERT_FIELDS32 ( 64, 95, __VA_ARGS__ );\ + (oword).u32[3] = EFAB_INSERT_FIELDS32 ( 96, 127, __VA_ARGS__ );\ + } while ( 0 ) + +#define EFAB_POPULATE_QWORD32( qword, ... ) do { \ + (qword).u32[0] = EFAB_INSERT_FIELDS32 ( 0, 31, __VA_ARGS__ );\ + (qword).u32[1] = EFAB_INSERT_FIELDS32 ( 32, 63, __VA_ARGS__ );\ + } while ( 0 ) + +#define EFAB_POPULATE_DWORD( dword, ... ) do { \ + (dword).u32[0] = EFAB_INSERT_FIELDS32 ( 0, 31, __VA_ARGS__ );\ + } while ( 0 ) + +#if ( BITS_PER_LONG == 64 ) +#define EFAB_POPULATE_OWORD EFAB_POPULATE_OWORD64 +#define EFAB_POPULATE_QWORD EFAB_POPULATE_QWORD64 +#else +#define EFAB_POPULATE_OWORD EFAB_POPULATE_OWORD32 +#define EFAB_POPULATE_QWORD EFAB_POPULATE_QWORD32 +#endif + +/* Populate an octword field with various numbers of arguments */ +#define EFAB_POPULATE_OWORD_10 EFAB_POPULATE_OWORD +#define EFAB_POPULATE_OWORD_9( oword, ... ) \ + EFAB_POPULATE_OWORD_10 ( oword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_OWORD_8( oword, ... ) \ + EFAB_POPULATE_OWORD_9 ( oword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_OWORD_7( oword, ... ) \ + EFAB_POPULATE_OWORD_8 ( oword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_OWORD_6( oword, ... ) \ + EFAB_POPULATE_OWORD_7 ( oword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_OWORD_5( oword, ... ) \ + EFAB_POPULATE_OWORD_6 ( oword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_OWORD_4( oword, ... ) \ + EFAB_POPULATE_OWORD_5 ( oword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_OWORD_3( oword, ... ) \ + EFAB_POPULATE_OWORD_4 ( oword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_OWORD_2( oword, ... ) \ + EFAB_POPULATE_OWORD_3 ( oword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_OWORD_1( oword, ... ) \ + EFAB_POPULATE_OWORD_2 ( oword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_ZERO_OWORD( oword ) \ + EFAB_POPULATE_OWORD_1 ( oword, EFAB_DUMMY_FIELD, 0 ) +#define EFAB_SET_OWORD( oword ) \ + EFAB_POPULATE_OWORD_4 ( oword, \ + EFAB_DWORD_0, 0xffffffff, \ + EFAB_DWORD_1, 0xffffffff, \ + EFAB_DWORD_2, 0xffffffff, \ + EFAB_DWORD_3, 0xffffffff ) + +/* Populate a quadword field with various numbers of arguments */ +#define EFAB_POPULATE_QWORD_10 EFAB_POPULATE_QWORD +#define EFAB_POPULATE_QWORD_9( qword, ... ) \ + EFAB_POPULATE_QWORD_10 ( qword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_QWORD_8( qword, ... ) \ + EFAB_POPULATE_QWORD_9 ( qword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_QWORD_7( qword, ... ) \ + EFAB_POPULATE_QWORD_8 ( qword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_QWORD_6( qword, ... ) \ + EFAB_POPULATE_QWORD_7 ( qword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_QWORD_5( qword, ... ) \ + EFAB_POPULATE_QWORD_6 ( qword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_QWORD_4( qword, ... ) \ + EFAB_POPULATE_QWORD_5 ( qword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_QWORD_3( qword, ... ) \ + EFAB_POPULATE_QWORD_4 ( qword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_QWORD_2( qword, ... ) \ + EFAB_POPULATE_QWORD_3 ( qword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_QWORD_1( qword, ... ) \ + EFAB_POPULATE_QWORD_2 ( qword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_ZERO_QWORD( qword ) \ + EFAB_POPULATE_QWORD_1 ( qword, EFAB_DUMMY_FIELD, 0 ) +#define EFAB_SET_QWORD( qword ) \ + EFAB_POPULATE_QWORD_2 ( qword, \ + EFAB_DWORD_0, 0xffffffff, \ + EFAB_DWORD_1, 0xffffffff ) + +/* Populate a dword field with various numbers of arguments */ +#define EFAB_POPULATE_DWORD_10 EFAB_POPULATE_DWORD +#define EFAB_POPULATE_DWORD_9( dword, ... ) \ + EFAB_POPULATE_DWORD_10 ( dword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_DWORD_8( dword, ... ) \ + EFAB_POPULATE_DWORD_9 ( dword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_DWORD_7( dword, ... ) \ + EFAB_POPULATE_DWORD_8 ( dword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_DWORD_6( dword, ... ) \ + EFAB_POPULATE_DWORD_7 ( dword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_DWORD_5( dword, ... ) \ + EFAB_POPULATE_DWORD_6 ( dword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_DWORD_4( dword, ... ) \ + EFAB_POPULATE_DWORD_5 ( dword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_DWORD_3( dword, ... ) \ + EFAB_POPULATE_DWORD_4 ( dword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_DWORD_2( dword, ... ) \ + EFAB_POPULATE_DWORD_3 ( dword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_POPULATE_DWORD_1( dword, ... ) \ + EFAB_POPULATE_DWORD_2 ( dword, EFAB_DUMMY_FIELD, 0, __VA_ARGS__ ) +#define EFAB_ZERO_DWORD( dword ) \ + EFAB_POPULATE_DWORD_1 ( dword, EFAB_DUMMY_FIELD, 0 ) +#define EFAB_SET_DWORD( dword ) \ + EFAB_POPULATE_DWORD_1 ( dword, EFAB_DWORD_0, 0xffffffff ) + +/* + * Modify a named field within an already-populated structure. Used + * for read-modify-write operations. + * + */ + +#define EFAB_INSERT_FIELD64( ... ) \ + cpu_to_le64 ( EFAB_INSERT_FIELD_NATIVE ( __VA_ARGS__ ) ) + +#define EFAB_INSERT_FIELD32( ... ) \ + cpu_to_le32 ( EFAB_INSERT_FIELD_NATIVE ( __VA_ARGS__ ) ) + +#define EFAB_INPLACE_MASK64( min, max, field ) \ + EFAB_INSERT_FIELD64 ( min, max, field, EFAB_MASK64 ( field ) ) + +#define EFAB_INPLACE_MASK32( min, max, field ) \ + EFAB_INSERT_FIELD32 ( min, max, field, EFAB_MASK32 ( field ) ) + +#define EFAB_SET_OWORD_FIELD64( oword, field, value ) do { \ + (oword).u64[0] = ( ( (oword).u64[0] \ + & ~EFAB_INPLACE_MASK64 ( 0, 63, field ) ) \ + | EFAB_INSERT_FIELD64 ( 0, 63, field, value ) ); \ + (oword).u64[1] = ( ( (oword).u64[1] \ + & ~EFAB_INPLACE_MASK64 ( 64, 127, field ) ) \ + | EFAB_INSERT_FIELD64 ( 64, 127, field, value ) ); \ + } while ( 0 ) + +#define EFAB_SET_QWORD_FIELD64( qword, field, value ) do { \ + (qword).u64[0] = ( ( (qword).u64[0] \ + & ~EFAB_INPLACE_MASK64 ( 0, 63, field ) ) \ + | EFAB_INSERT_FIELD64 ( 0, 63, field, value ) ); \ + } while ( 0 ) + +#define EFAB_SET_OWORD_FIELD32( oword, field, value ) do { \ + (oword).u32[0] = ( ( (oword).u32[0] \ + & ~EFAB_INPLACE_MASK32 ( 0, 31, field ) ) \ + | EFAB_INSERT_FIELD32 ( 0, 31, field, value ) ); \ + (oword).u32[1] = ( ( (oword).u32[1] \ + & ~EFAB_INPLACE_MASK32 ( 32, 63, field ) ) \ + | EFAB_INSERT_FIELD32 ( 32, 63, field, value ) ); \ + (oword).u32[2] = ( ( (oword).u32[2] \ + & ~EFAB_INPLACE_MASK32 ( 64, 95, field ) ) \ + | EFAB_INSERT_FIELD32 ( 64, 95, field, value ) ); \ + (oword).u32[3] = ( ( (oword).u32[3] \ + & ~EFAB_INPLACE_MASK32 ( 96, 127, field ) ) \ + | EFAB_INSERT_FIELD32 ( 96, 127, field, value ) ); \ + } while ( 0 ) + +#define EFAB_SET_QWORD_FIELD32( qword, field, value ) do { \ + (qword).u32[0] = ( ( (qword).u32[0] \ + & ~EFAB_INPLACE_MASK32 ( 0, 31, field ) ) \ + | EFAB_INSERT_FIELD32 ( 0, 31, field, value ) ); \ + (qword).u32[1] = ( ( (qword).u32[1] \ + & ~EFAB_INPLACE_MASK32 ( 32, 63, field ) ) \ + | EFAB_INSERT_FIELD32 ( 32, 63, field, value ) ); \ + } while ( 0 ) + +#define EFAB_SET_DWORD_FIELD( dword, field, value ) do { \ + (dword).u32[0] = ( ( (dword).u32[0] \ + & ~EFAB_INPLACE_MASK32 ( 0, 31, field ) ) \ + | EFAB_INSERT_FIELD32 ( 0, 31, field, value ) ); \ + } while ( 0 ) + +#if ( BITS_PER_LONG == 64 ) +#define EFAB_SET_OWORD_FIELD EFAB_SET_OWORD_FIELD64 +#define EFAB_SET_QWORD_FIELD EFAB_SET_QWORD_FIELD64 +#else +#define EFAB_SET_OWORD_FIELD EFAB_SET_OWORD_FIELD32 +#define EFAB_SET_QWORD_FIELD EFAB_SET_QWORD_FIELD32 +#endif + +/* Used to avoid compiler warnings about shift range exceeding width + * of the data types when dma_addr_t is only 32 bits wide. + */ +#define DMA_ADDR_T_WIDTH ( 8 * sizeof ( dma_addr_t ) ) +#define EFAB_DMA_TYPE_WIDTH( width ) \ + ( ( (width) < DMA_ADDR_T_WIDTH ) ? (width) : DMA_ADDR_T_WIDTH ) +#define EFAB_DMA_MAX_MASK ( ( DMA_ADDR_T_WIDTH == 64 ) ? \ + ~( ( uint64_t ) 0 ) : ~( ( uint32_t ) 0 ) ) +#define EFAB_DMA_MASK(mask) ( (mask) & EFAB_DMA_MAX_MASK ) + +#endif /* EFAB_BITFIELD_H */ + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/etherfabric_nic.h b/gpxe/src/drivers/net/etherfabric_nic.h new file mode 100644 index 0000000..4be50fb --- /dev/null +++ b/gpxe/src/drivers/net/etherfabric_nic.h @@ -0,0 +1,201 @@ +/************************************************************************** + * + * Etherboot driver for Level 5 Etherfabric network cards + * + * Written by Michael Brown + * + * Copyright Fen Systems Ltd. 2005 + * Copyright Level 5 Networks Inc. 2005 + * + * This software may be used and distributed according to the terms of + * the GNU General Public License (GPL), incorporated herein by + * reference. Drivers based on or derived from this code fall under + * the GPL and must retain the authorship, copyright and license + * notice. + * + ************************************************************************** + */ +#ifndef EFAB_NIC_H +#define EFAB_NIC_H +#include +#include +#include +#include +#include +/************************************************************************** + * + * Constants and macros + * + ************************************************************************** + */ +/* Board IDs. Early boards have no board_type, (e.g. EF1002 and 401/403) + * But newer boards are getting bigger... + */ +typedef enum { + EFAB_BOARD_INVALID = 0, /* Early boards do not have board rev. info. */ + EFAB_BOARD_SFE4001 = 1, + EFAB_BOARD_SFE4002 = 2, + EFAB_BOARD_SFE4003 = 3, + /* Insert new types before here */ + EFAB_BOARD_MAX +} efab_board_type; + +/* PHY types. */ +typedef enum { + PHY_TYPE_AUTO = 0, /* on development board detect between CX4 & alaska */ + PHY_TYPE_CX4_RTMR = 1, + PHY_TYPE_1GIG_ALASKA = 2, + PHY_TYPE_10XPRESS = 3, + PHY_TYPE_XFP = 4, + PHY_TYPE_CX4 = 5, + PHY_TYPE_PM8358 = 6, +} phy_type_t; + +/************************************************************************** + * + * Hardware data structures and sizing + * + ************************************************************************** + */ + +#define dma_addr_t unsigned long +typedef efab_qword_t falcon_rx_desc_t; +typedef efab_qword_t falcon_tx_desc_t; +typedef efab_qword_t falcon_event_t; + +#define EFAB_BUF_ALIGN 4096 +#define EFAB_RXD_SIZE 512 +#define EFAB_TXD_SIZE 512 +#define EFAB_EVQ_SIZE 512 + +#define EFAB_NUM_RX_DESC 16 +#define EFAB_RX_BUF_SIZE 1600 + +/************************************************************************** + * + * Data structures + * + ************************************************************************** + */ + +struct efab_nic; + +/* A buffer table allocation backing a tx dma, rx dma or eventq */ +struct efab_special_buffer { + dma_addr_t dma_addr; + int id; +}; + +/* A TX queue */ +struct efab_tx_queue { + /* The hardware ring */ + falcon_tx_desc_t *ring; + + /* The software ring storing io_buffers. */ + struct io_buffer *buf[EFAB_TXD_SIZE]; + + /* The buffer table reservation pushed to hardware */ + struct efab_special_buffer entry; + + /* Software descriptor write ptr */ + unsigned int write_ptr; + + /* Hardware descriptor read ptr */ + unsigned int read_ptr; +}; + +/* An RX queue */ +struct efab_rx_queue { + /* The hardware ring */ + falcon_rx_desc_t *ring; + + /* The software ring storing io_buffers */ + struct io_buffer *buf[EFAB_NUM_RX_DESC]; + + /* The buffer table reservation pushed to hardware */ + struct efab_special_buffer entry; + + /* Descriptor write ptr, into both the hardware and software rings */ + unsigned int write_ptr; + + /* Hardware completion ptr */ + unsigned int read_ptr; +}; + +/* An event queue */ +struct efab_ev_queue { + /* The hardware ring to push to hardware. + * Must be the first entry in the structure */ + falcon_event_t *ring; + + /* The buffer table reservation pushed to hardware */ + struct efab_special_buffer entry; + + /* Pointers into the ring */ + unsigned int read_ptr; +}; + +struct efab_mac_operations { + int ( * init ) ( struct efab_nic *efab ); +}; + +struct efab_phy_operations { + int ( * init ) ( struct efab_nic *efab ); + unsigned int mmds; +}; + +struct efab_board_operations { + int ( * init ) ( struct efab_nic *efab ); + void ( * fini ) ( struct efab_nic *efab ); +}; + +struct efab_nic { + struct net_device *netdev; + int pci_revision; + int is_asic; + + /* I2C bit-bashed interface */ + struct i2c_bit_basher i2c_bb; + + /** SPI bus and devices, and the user visible NVO area */ + struct spi_bus spi_bus; + struct spi_device spi_flash; + struct spi_device spi_eeprom; + struct spi_device *spi; + struct nvo_block nvo; + + /** Board, MAC, and PHY operations tables */ + struct efab_board_operations *board_op; + struct efab_mac_operations *mac_op; + struct efab_phy_operations *phy_op; + + /* PHY and board types */ + int phy_addr; + int phy_type; + int phy_10g; + int board_type; + + /** Memory and IO base */ + void *membase; + unsigned int iobase; + + /* Buffer table allocation head */ + int buffer_head; + + /* Queues */ + struct efab_rx_queue rx_queue; + struct efab_tx_queue tx_queue; + struct efab_ev_queue ev_queue; + + /** MAC address */ + uint8_t mac_addr[ETH_ALEN]; + /** GMII link options */ + unsigned int link_options; + /** Link status */ + int link_up; + + /** INT_REG_KER */ + efab_oword_t int_ker __attribute__ (( aligned ( 16 ) )); +}; +#endif /* EFAB_NIC_H */ + diff --git a/gpxe/src/drivers/net/forcedeth.c b/gpxe/src/drivers/net/forcedeth.c new file mode 100644 index 0000000..a30f137 --- /dev/null +++ b/gpxe/src/drivers/net/forcedeth.c @@ -0,0 +1,1440 @@ +/************************************************************************** +* 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.30, 25 Sep 2004 +* Linux Kernel 2.6.10 +* +* +* REVISION HISTORY: +* ================ +* v1.0 01-31-2004 timlegge Initial port of Linux driver +* v1.1 02-03-2004 timlegge Large Clean up, first release +* v1.2 05-14-2005 timlegge Add Linux 0.22 to .030 features +* +* Indent Options: indent -kr -i8 +***************************************************************************/ + +/* to get some global routines like printf */ +#include "etherboot.h" +/* to get the interface to the body of the program */ +#include "nic.h" +/* to get the PCI support functions, if this is a PCI NIC */ +#include +/* Include timer support functions */ +#include +#include "mii.h" + +#define drv_version "v1.2" +#define drv_date "05-14-2005" + +//#define TFTM_DEBUG +#ifdef TFTM_DEBUG +#define dprintf(x) printf x +#else +#define dprintf(x) +#endif + +#define ETH_DATA_LEN 1500 + +/* Condensed operations for readability. */ +#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr)) + +static unsigned long BASE; +/* NIC specific static variables go here */ +#define PCI_DEVICE_ID_NVIDIA_NVENET_1 0x01c3 +#define PCI_DEVICE_ID_NVIDIA_NVENET_2 0x0066 +#define PCI_DEVICE_ID_NVIDIA_NVENET_4 0x0086 +#define PCI_DEVICE_ID_NVIDIA_NVENET_5 0x008c +#define PCI_DEVICE_ID_NVIDIA_NVENET_3 0x00d6 +#define PCI_DEVICE_ID_NVIDIA_NVENET_7 0x00df +#define PCI_DEVICE_ID_NVIDIA_NVENET_6 0x00e6 +#define PCI_DEVICE_ID_NVIDIA_NVENET_8 0x0056 +#define PCI_DEVICE_ID_NVIDIA_NVENET_9 0x0057 +#define PCI_DEVICE_ID_NVIDIA_NVENET_10 0x0037 +#define PCI_DEVICE_ID_NVIDIA_NVENET_11 0x0038 +#define PCI_DEVICE_ID_NVIDIA_NVENET_15 0x0373 + + +/* + * Hardware access: + */ + +#define DEV_NEED_LASTPACKET1 0x0001 /* set LASTPACKET1 in tx flags */ +#define DEV_IRQMASK_1 0x0002 /* use NVREG_IRQMASK_WANTED_1 for irq mask */ +#define DEV_IRQMASK_2 0x0004 /* use NVREG_IRQMASK_WANTED_2 for irq mask */ +#define DEV_NEED_TIMERIRQ 0x0008 /* set the timer irq flag in the irq mask */ +#define DEV_NEED_LINKTIMER 0x0010 /* poll link settings. Relies on the timer irq */ + +enum { + NvRegIrqStatus = 0x000, +#define NVREG_IRQSTAT_MIIEVENT 0040 +#define NVREG_IRQSTAT_MASK 0x1ff + NvRegIrqMask = 0x004, +#define NVREG_IRQ_RX_ERROR 0x0001 +#define NVREG_IRQ_RX 0x0002 +#define NVREG_IRQ_RX_NOBUF 0x0004 +#define NVREG_IRQ_TX_ERR 0x0008 +#define NVREG_IRQ_TX2 0x0010 +#define NVREG_IRQ_TIMER 0x0020 +#define NVREG_IRQ_LINK 0x0040 +#define NVREG_IRQ_TX1 0x0100 +#define NVREG_IRQMASK_WANTED_1 0x005f +#define NVREG_IRQMASK_WANTED_2 0x0147 +#define NVREG_IRQ_UNKNOWN (~(NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_TX_ERR|NVREG_IRQ_TX2|NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_TX1)) + + NvRegUnknownSetupReg6 = 0x008, +#define NVREG_UNKSETUP6_VAL 3 + +/* + * NVREG_POLL_DEFAULT is the interval length of the timer source on the nic + * NVREG_POLL_DEFAULT=97 would result in an interval length of 1 ms + */ + NvRegPollingInterval = 0x00c, +#define NVREG_POLL_DEFAULT 970 + NvRegMisc1 = 0x080, +#define NVREG_MISC1_HD 0x02 +#define NVREG_MISC1_FORCE 0x3b0f3c + + NvRegTransmitterControl = 0x084, +#define NVREG_XMITCTL_START 0x01 + NvRegTransmitterStatus = 0x088, +#define NVREG_XMITSTAT_BUSY 0x01 + + NvRegPacketFilterFlags = 0x8c, +#define NVREG_PFF_ALWAYS 0x7F0008 +#define NVREG_PFF_PROMISC 0x80 +#define NVREG_PFF_MYADDR 0x20 + + NvRegOffloadConfig = 0x90, +#define NVREG_OFFLOAD_HOMEPHY 0x601 +#define NVREG_OFFLOAD_NORMAL RX_NIC_BUFSIZE + NvRegReceiverControl = 0x094, +#define NVREG_RCVCTL_START 0x01 + NvRegReceiverStatus = 0x98, +#define NVREG_RCVSTAT_BUSY 0x01 + + NvRegRandomSeed = 0x9c, +#define NVREG_RNDSEED_MASK 0x00ff +#define NVREG_RNDSEED_FORCE 0x7f00 +#define NVREG_RNDSEED_FORCE2 0x2d00 +#define NVREG_RNDSEED_FORCE3 0x7400 + + NvRegUnknownSetupReg1 = 0xA0, +#define NVREG_UNKSETUP1_VAL 0x16070f + NvRegUnknownSetupReg2 = 0xA4, +#define NVREG_UNKSETUP2_VAL 0x16 + NvRegMacAddrA = 0xA8, + NvRegMacAddrB = 0xAC, + NvRegMulticastAddrA = 0xB0, +#define NVREG_MCASTADDRA_FORCE 0x01 + NvRegMulticastAddrB = 0xB4, + NvRegMulticastMaskA = 0xB8, + NvRegMulticastMaskB = 0xBC, + + NvRegPhyInterface = 0xC0, +#define PHY_RGMII 0x10000000 + + NvRegTxRingPhysAddr = 0x100, + NvRegRxRingPhysAddr = 0x104, + NvRegRingSizes = 0x108, +#define NVREG_RINGSZ_TXSHIFT 0 +#define NVREG_RINGSZ_RXSHIFT 16 + NvRegUnknownTransmitterReg = 0x10c, + NvRegLinkSpeed = 0x110, +#define NVREG_LINKSPEED_FORCE 0x10000 +#define NVREG_LINKSPEED_10 1000 +#define NVREG_LINKSPEED_100 100 +#define NVREG_LINKSPEED_1000 50 + NvRegUnknownSetupReg5 = 0x130, +#define NVREG_UNKSETUP5_BIT31 (1<<31) + NvRegUnknownSetupReg3 = 0x13c, +#define NVREG_UNKSETUP3_VAL1 0x200010 + NvRegTxRxControl = 0x144, +#define NVREG_TXRXCTL_KICK 0x0001 +#define NVREG_TXRXCTL_BIT1 0x0002 +#define NVREG_TXRXCTL_BIT2 0x0004 +#define NVREG_TXRXCTL_IDLE 0x0008 +#define NVREG_TXRXCTL_RESET 0x0010 +#define NVREG_TXRXCTL_RXCHECK 0x0400 + NvRegMIIStatus = 0x180, +#define NVREG_MIISTAT_ERROR 0x0001 +#define NVREG_MIISTAT_LINKCHANGE 0x0008 +#define NVREG_MIISTAT_MASK 0x000f +#define NVREG_MIISTAT_MASK2 0x000f + NvRegUnknownSetupReg4 = 0x184, +#define NVREG_UNKSETUP4_VAL 8 + + NvRegAdapterControl = 0x188, +#define NVREG_ADAPTCTL_START 0x02 +#define NVREG_ADAPTCTL_LINKUP 0x04 +#define NVREG_ADAPTCTL_PHYVALID 0x40000 +#define NVREG_ADAPTCTL_RUNNING 0x100000 +#define NVREG_ADAPTCTL_PHYSHIFT 24 + NvRegMIISpeed = 0x18c, +#define NVREG_MIISPEED_BIT8 (1<<8) +#define NVREG_MIIDELAY 5 + NvRegMIIControl = 0x190, +#define NVREG_MIICTL_INUSE 0x08000 +#define NVREG_MIICTL_WRITE 0x00400 +#define NVREG_MIICTL_ADDRSHIFT 5 + NvRegMIIData = 0x194, + NvRegWakeUpFlags = 0x200, +#define NVREG_WAKEUPFLAGS_VAL 0x7770 +#define NVREG_WAKEUPFLAGS_BUSYSHIFT 24 +#define NVREG_WAKEUPFLAGS_ENABLESHIFT 16 +#define NVREG_WAKEUPFLAGS_D3SHIFT 12 +#define NVREG_WAKEUPFLAGS_D2SHIFT 8 +#define NVREG_WAKEUPFLAGS_D1SHIFT 4 +#define NVREG_WAKEUPFLAGS_D0SHIFT 0 +#define NVREG_WAKEUPFLAGS_ACCEPT_MAGPAT 0x01 +#define NVREG_WAKEUPFLAGS_ACCEPT_WAKEUPPAT 0x02 +#define NVREG_WAKEUPFLAGS_ACCEPT_LINKCHANGE 0x04 +#define NVREG_WAKEUPFLAGS_ENABLE 0x1111 + + NvRegPatternCRC = 0x204, + NvRegPatternMask = 0x208, + NvRegPowerCap = 0x268, +#define NVREG_POWERCAP_D3SUPP (1<<30) +#define NVREG_POWERCAP_D2SUPP (1<<26) +#define NVREG_POWERCAP_D1SUPP (1<<25) + NvRegPowerState = 0x26c, +#define NVREG_POWERSTATE_POWEREDUP 0x8000 +#define NVREG_POWERSTATE_VALID 0x0100 +#define NVREG_POWERSTATE_MASK 0x0003 +#define NVREG_POWERSTATE_D0 0x0000 +#define NVREG_POWERSTATE_D1 0x0001 +#define NVREG_POWERSTATE_D2 0x0002 +#define NVREG_POWERSTATE_D3 0x0003 +}; + +#define FLAG_MASK_V1 0xffff0000 +#define FLAG_MASK_V2 0xffffc000 +#define LEN_MASK_V1 (0xffffffff ^ FLAG_MASK_V1) +#define LEN_MASK_V2 (0xffffffff ^ FLAG_MASK_V2) + +#define NV_TX_LASTPACKET (1<<16) +#define NV_TX_RETRYERROR (1<<19) +#define NV_TX_LASTPACKET1 (1<<24) +#define NV_TX_DEFERRED (1<<26) +#define NV_TX_CARRIERLOST (1<<27) +#define NV_TX_LATECOLLISION (1<<28) +#define NV_TX_UNDERFLOW (1<<29) +#define NV_TX_ERROR (1<<30) +#define NV_TX_VALID (1<<31) + +#define NV_TX2_LASTPACKET (1<<29) +#define NV_TX2_RETRYERROR (1<<18) +#define NV_TX2_LASTPACKET1 (1<<23) +#define NV_TX2_DEFERRED (1<<25) +#define NV_TX2_CARRIERLOST (1<<26) +#define NV_TX2_LATECOLLISION (1<<27) +#define NV_TX2_UNDERFLOW (1<<28) +/* error and valid are the same for both */ +#define NV_TX2_ERROR (1<<30) +#define NV_TX2_VALID (1<<31) + +#define NV_RX_DESCRIPTORVALID (1<<16) +#define NV_RX_MISSEDFRAME (1<<17) +#define NV_RX_SUBSTRACT1 (1<<18) +#define NV_RX_ERROR1 (1<<23) +#define NV_RX_ERROR2 (1<<24) +#define NV_RX_ERROR3 (1<<25) +#define NV_RX_ERROR4 (1<<26) +#define NV_RX_CRCERR (1<<27) +#define NV_RX_OVERFLOW (1<<28) +#define NV_RX_FRAMINGERR (1<<29) +#define NV_RX_ERROR (1<<30) +#define NV_RX_AVAIL (1<<31) + +#define NV_RX2_CHECKSUMMASK (0x1C000000) +#define NV_RX2_CHECKSUMOK1 (0x10000000) +#define NV_RX2_CHECKSUMOK2 (0x14000000) +#define NV_RX2_CHECKSUMOK3 (0x18000000) +#define NV_RX2_DESCRIPTORVALID (1<<29) +#define NV_RX2_SUBSTRACT1 (1<<25) +#define NV_RX2_ERROR1 (1<<18) +#define NV_RX2_ERROR2 (1<<19) +#define NV_RX2_ERROR3 (1<<20) +#define NV_RX2_ERROR4 (1<<21) +#define NV_RX2_CRCERR (1<<22) +#define NV_RX2_OVERFLOW (1<<23) +#define NV_RX2_FRAMINGERR (1<<24) +/* error and avail are the same for both */ +#define NV_RX2_ERROR (1<<30) +#define NV_RX2_AVAIL (1<<31) + +/* Miscelaneous hardware related defines: */ +#define NV_PCI_REGSZ 0x270 + +/* various timeout delays: all in usec */ +#define NV_TXRX_RESET_DELAY 4 +#define NV_TXSTOP_DELAY1 10 +#define NV_TXSTOP_DELAY1MAX 500000 +#define NV_TXSTOP_DELAY2 100 +#define NV_RXSTOP_DELAY1 10 +#define NV_RXSTOP_DELAY1MAX 500000 +#define NV_RXSTOP_DELAY2 100 +#define NV_SETUP5_DELAY 5 +#define NV_SETUP5_DELAYMAX 50000 +#define NV_POWERUP_DELAY 5 +#define NV_POWERUP_DELAYMAX 5000 +#define NV_MIIBUSY_DELAY 50 +#define NV_MIIPHY_DELAY 10 +#define NV_MIIPHY_DELAYMAX 10000 + +#define NV_WAKEUPPATTERNS 5 +#define NV_WAKEUPMASKENTRIES 4 + +/* General driver defaults */ +#define NV_WATCHDOG_TIMEO (5*HZ) + +#define RX_RING 4 +#define TX_RING 2 + +/* + * If your nic mysteriously hangs then try to reduce the limits + * to 1/0: It might be required to set NV_TX_LASTPACKET in the + * last valid ring entry. But this would be impossible to + * implement - probably a disassembly error. + */ +#define TX_LIMIT_STOP 63 +#define TX_LIMIT_START 62 + +/* rx/tx mac addr + type + vlan + align + slack*/ +#define RX_NIC_BUFSIZE (ETH_DATA_LEN + 64) +/* even more slack */ +#define RX_ALLOC_BUFSIZE (ETH_DATA_LEN + 128) + +#define OOM_REFILL (1+HZ/20) +#define POLL_WAIT (1+HZ/100) +#define LINK_TIMEOUT (3*HZ) + +/* + * desc_ver values: + * This field has two purposes: + * - Newer nics uses a different ring layout. The layout is selected by + * comparing np->desc_ver with DESC_VER_xy. + * - It contains bits that are forced on when writing to NvRegTxRxControl. + */ +#define DESC_VER_1 0x0 +#define DESC_VER_2 (0x02100|NVREG_TXRXCTL_RXCHECK) + +/* PHY defines */ +#define PHY_OUI_MARVELL 0x5043 +#define PHY_OUI_CICADA 0x03f1 +#define PHYID1_OUI_MASK 0x03ff +#define PHYID1_OUI_SHFT 6 +#define PHYID2_OUI_MASK 0xfc00 +#define PHYID2_OUI_SHFT 10 +#define PHY_INIT1 0x0f000 +#define PHY_INIT2 0x0e00 +#define PHY_INIT3 0x01000 +#define PHY_INIT4 0x0200 +#define PHY_INIT5 0x0004 +#define PHY_INIT6 0x02000 +#define PHY_GIGABIT 0x0100 + +#define PHY_TIMEOUT 0x1 +#define PHY_ERROR 0x2 + +#define PHY_100 0x1 +#define PHY_1000 0x2 +#define PHY_HALF 0x100 + +/* FIXME: MII defines that should be added to */ +#define MII_1000BT_CR 0x09 +#define MII_1000BT_SR 0x0a +#define ADVERTISE_1000FULL 0x0200 +#define ADVERTISE_1000HALF 0x0100 +#define LPA_1000FULL 0x0800 +#define LPA_1000HALF 0x0400 + +/* Big endian: should work, but is untested */ +struct ring_desc { + u32 PacketBuffer; + u32 FlagLen; +}; + + +/* Define the TX and RX Descriptor and Buffers */ +struct { + struct ring_desc tx_ring[TX_RING]; + unsigned char txb[TX_RING * RX_NIC_BUFSIZE]; + struct ring_desc rx_ring[RX_RING]; + unsigned char rxb[RX_RING * RX_NIC_BUFSIZE]; +} forcedeth_bufs __shared; +#define tx_ring forcedeth_bufs.tx_ring +#define rx_ring forcedeth_bufs.rx_ring +#define txb forcedeth_bufs.txb +#define rxb forcedeth_bufs.rxb + +/* Private Storage for the NIC */ +static struct forcedeth_private { + /* General data: + * Locking: spin_lock(&np->lock); */ + int in_shutdown; + u32 linkspeed; + int duplex; + int phyaddr; + int wolenabled; + unsigned int phy_oui; + u16 gigabit; + + /* General data: RO fields */ + u8 *ring_addr; + u32 orig_mac[2]; + u32 irqmask; + u32 desc_ver; + /* rx specific fields. + * Locking: Within irq hander or disable_irq+spin_lock(&np->lock); + */ + unsigned int cur_rx, refill_rx; + + /* + * tx specific fields. + */ + unsigned int next_tx, nic_tx; + u32 tx_flags; +} npx; + +static struct forcedeth_private *np; + +static inline void pci_push(u8 * base) +{ + /* force out pending posted writes */ + readl(base); +} + +static inline u32 nv_descr_getlength(struct ring_desc *prd, u32 v) +{ + return le32_to_cpu(prd->FlagLen) + & ((v == DESC_VER_1) ? LEN_MASK_V1 : LEN_MASK_V2); +} + +static int reg_delay(int offset, u32 mask, + u32 target, int delay, int delaymax, const char *msg) +{ + u8 *base = (u8 *) BASE; + + pci_push(base); + do { + udelay(delay); + delaymax -= delay; + if (delaymax < 0) { + if (msg) + printf("%s", msg); + return 1; + } + } while ((readl(base + offset) & mask) != target); + return 0; +} + +#define MII_READ (-1) +#define MII_PHYSID1 0x02 /* PHYS ID 1 */ +#define MII_PHYSID2 0x03 /* PHYS ID 2 */ +#define MII_BMCR 0x00 /* Basic mode control register */ +#define MII_BMSR 0x01 /* Basic mode status register */ +#define MII_ADVERTISE 0x04 /* Advertisement control reg */ +#define MII_LPA 0x05 /* Link partner ability reg */ + +#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */ + +/* Link partner ability register. */ +#define LPA_SLCT 0x001f /* Same as advertise selector */ +#define LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */ +#define LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */ +#define LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */ +#define LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */ +#define LPA_100BASE4 0x0200 /* Can do 100mbps 4k packets */ +#define LPA_RESV 0x1c00 /* Unused... */ +#define LPA_RFAULT 0x2000 /* Link partner faulted */ +#define LPA_LPACK 0x4000 /* Link partner acked us */ +#define LPA_NPAGE 0x8000 /* Next page bit */ + +/* mii_rw: read/write a register on the PHY. + * + * Caller must guarantee serialization + */ +static int mii_rw(struct nic *nic __unused, int addr, int miireg, + int value) +{ + u8 *base = (u8 *) BASE; + u32 reg; + int retval; + + writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus); + + reg = readl(base + NvRegMIIControl); + if (reg & NVREG_MIICTL_INUSE) { + writel(NVREG_MIICTL_INUSE, base + NvRegMIIControl); + udelay(NV_MIIBUSY_DELAY); + } + + reg = + (addr << NVREG_MIICTL_ADDRSHIFT) | miireg; + if (value != MII_READ) { + writel(value, base + NvRegMIIData); + reg |= NVREG_MIICTL_WRITE; + } + writel(reg, base + NvRegMIIControl); + + if (reg_delay(NvRegMIIControl, NVREG_MIICTL_INUSE, 0, + NV_MIIPHY_DELAY, NV_MIIPHY_DELAYMAX, NULL)) { + dprintf(("mii_rw of reg %d at PHY %d timed out.\n", + miireg, addr)); + retval = -1; + } else if (value != MII_READ) { + /* it was a write operation - fewer failures are detectable */ + dprintf(("mii_rw wrote 0x%x to reg %d at PHY %d\n", + value, miireg, addr)); + retval = 0; + } else if (readl(base + NvRegMIIStatus) & NVREG_MIISTAT_ERROR) { + dprintf(("mii_rw of reg %d at PHY %d failed.\n", + miireg, addr)); + retval = -1; + } else { + retval = readl(base + NvRegMIIData); + dprintf(("mii_rw read from reg %d at PHY %d: 0x%x.\n", + miireg, addr, retval)); + } + return retval; +} + +static int phy_reset(struct nic *nic) +{ + + u32 miicontrol; + unsigned int tries = 0; + + miicontrol = mii_rw(nic, np->phyaddr, MII_BMCR, MII_READ); + miicontrol |= BMCR_RESET; + if (mii_rw(nic, np->phyaddr, MII_BMCR, miicontrol)) { + return -1; + } + + /* wait for 500ms */ + mdelay(500); + + /* must wait till reset is deasserted */ + while (miicontrol & BMCR_RESET) { + mdelay(10); + miicontrol = mii_rw(nic, np->phyaddr, MII_BMCR, MII_READ); + /* FIXME: 100 tries seem excessive */ + if (tries++ > 100) + return -1; + } + return 0; +} + +static int phy_init(struct nic *nic) +{ + u8 *base = (u8 *) BASE; + u32 phyinterface, phy_reserved, mii_status, mii_control, + mii_control_1000, reg; + + /* set advertise register */ + reg = mii_rw(nic, np->phyaddr, MII_ADVERTISE, MII_READ); + reg |= + (ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF | + ADVERTISE_100FULL | 0x800 | 0x400); + if (mii_rw(nic, np->phyaddr, MII_ADVERTISE, reg)) { + printf("phy write to advertise failed.\n"); + return PHY_ERROR; + } + + /* get phy interface type */ + phyinterface = readl(base + NvRegPhyInterface); + + /* see if gigabit phy */ + mii_status = mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ); + + if (mii_status & PHY_GIGABIT) { + np->gigabit = PHY_GIGABIT; + mii_control_1000 = + mii_rw(nic, np->phyaddr, MII_1000BT_CR, MII_READ); + mii_control_1000 &= ~ADVERTISE_1000HALF; + if (phyinterface & PHY_RGMII) + mii_control_1000 |= ADVERTISE_1000FULL; + else + mii_control_1000 &= ~ADVERTISE_1000FULL; + + if (mii_rw + (nic, np->phyaddr, MII_1000BT_CR, mii_control_1000)) { + printf("phy init failed.\n"); + return PHY_ERROR; + } + } else + np->gigabit = 0; + + /* reset the phy */ + if (phy_reset(nic)) { + printf("phy reset failed\n"); + return PHY_ERROR; + } + + /* phy vendor specific configuration */ + if ((np->phy_oui == PHY_OUI_CICADA) && (phyinterface & PHY_RGMII)) { + phy_reserved = + mii_rw(nic, np->phyaddr, MII_RESV1, MII_READ); + phy_reserved &= ~(PHY_INIT1 | PHY_INIT2); + phy_reserved |= (PHY_INIT3 | PHY_INIT4); + if (mii_rw(nic, np->phyaddr, MII_RESV1, phy_reserved)) { + printf("phy init failed.\n"); + return PHY_ERROR; + } + phy_reserved = + mii_rw(nic, np->phyaddr, MII_NCONFIG, MII_READ); + phy_reserved |= PHY_INIT5; + if (mii_rw(nic, np->phyaddr, MII_NCONFIG, phy_reserved)) { + printf("phy init failed.\n"); + return PHY_ERROR; + } + } + if (np->phy_oui == PHY_OUI_CICADA) { + phy_reserved = + mii_rw(nic, np->phyaddr, MII_SREVISION, MII_READ); + phy_reserved |= PHY_INIT6; + if (mii_rw(nic, np->phyaddr, MII_SREVISION, phy_reserved)) { + printf("phy init failed.\n"); + return PHY_ERROR; + } + } + + /* restart auto negotiation */ + mii_control = mii_rw(nic, np->phyaddr, MII_BMCR, MII_READ); + mii_control |= (BMCR_ANRESTART | BMCR_ANENABLE); + if (mii_rw(nic, np->phyaddr, MII_BMCR, mii_control)) { + return PHY_ERROR; + } + + return 0; +} + +static void start_rx(struct nic *nic __unused) +{ + u8 *base = (u8 *) BASE; + + dprintf(("start_rx\n")); + /* Already running? Stop it. */ + if (readl(base + NvRegReceiverControl) & NVREG_RCVCTL_START) { + writel(0, base + NvRegReceiverControl); + pci_push(base); + } + writel(np->linkspeed, base + NvRegLinkSpeed); + pci_push(base); + writel(NVREG_RCVCTL_START, base + NvRegReceiverControl); + pci_push(base); +} + +static void stop_rx(void) +{ + u8 *base = (u8 *) BASE; + + dprintf(("stop_rx\n")); + writel(0, base + NvRegReceiverControl); + reg_delay(NvRegReceiverStatus, NVREG_RCVSTAT_BUSY, 0, + NV_RXSTOP_DELAY1, NV_RXSTOP_DELAY1MAX, + "stop_rx: ReceiverStatus remained busy"); + + udelay(NV_RXSTOP_DELAY2); + writel(0, base + NvRegLinkSpeed); +} + +static void start_tx(struct nic *nic __unused) +{ + u8 *base = (u8 *) BASE; + + dprintf(("start_tx\n")); + writel(NVREG_XMITCTL_START, base + NvRegTransmitterControl); + pci_push(base); +} + +static void stop_tx(void) +{ + u8 *base = (u8 *) BASE; + + dprintf(("stop_tx\n")); + writel(0, base + NvRegTransmitterControl); + reg_delay(NvRegTransmitterStatus, NVREG_XMITSTAT_BUSY, 0, + NV_TXSTOP_DELAY1, NV_TXSTOP_DELAY1MAX, + "stop_tx: TransmitterStatus remained busy"); + + udelay(NV_TXSTOP_DELAY2); + writel(0, base + NvRegUnknownTransmitterReg); +} + + +static void txrx_reset(struct nic *nic __unused) +{ + u8 *base = (u8 *) BASE; + + dprintf(("txrx_reset\n")); + writel(NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET | np->desc_ver, + base + NvRegTxRxControl); + + pci_push(base); + udelay(NV_TXRX_RESET_DELAY); + writel(NVREG_TXRXCTL_BIT2 | np->desc_ver, base + NvRegTxRxControl); + pci_push(base); +} + +/* + * alloc_rx: fill rx ring entries. + * Return 1 if the allocations for the skbs failed and the + * rx engine is without Available descriptors + */ +static int alloc_rx(struct nic *nic __unused) +{ + unsigned int refill_rx = np->refill_rx; + int i; + //while (np->cur_rx != refill_rx) { + for (i = 0; i < RX_RING; i++) { + //int nr = refill_rx % RX_RING; + rx_ring[i].PacketBuffer = + virt_to_le32desc(&rxb[i * RX_NIC_BUFSIZE]); + wmb(); + rx_ring[i].FlagLen = + cpu_to_le32(RX_NIC_BUFSIZE | NV_RX_AVAIL); + /* printf("alloc_rx: Packet %d marked as Available\n", + refill_rx); */ + refill_rx++; + } + np->refill_rx = refill_rx; + if (np->cur_rx - refill_rx == RX_RING) + return 1; + return 0; +} + +static int update_linkspeed(struct nic *nic) +{ + int adv, lpa; + u32 newls; + int newdup = np->duplex; + u32 mii_status; + int retval = 0; + u32 control_1000, status_1000, phyreg; + u8 *base = (u8 *) BASE; + int i; + + /* BMSR_LSTATUS is latched, read it twice: + * we want the current value. + */ + mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ); + mii_status = mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ); + +#if 1 + //yhlu + for(i=0;i<30;i++) { + mii_status = mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ); + if((mii_status & BMSR_LSTATUS) && (mii_status & BMSR_ANEGCOMPLETE)) break; + mdelay(100); + } +#endif + + if (!(mii_status & BMSR_LSTATUS)) { + printf + ("no link detected by phy - falling back to 10HD.\n"); + newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10; + newdup = 0; + retval = 0; + goto set_speed; + } + + /* check auto negotiation is complete */ + if (!(mii_status & BMSR_ANEGCOMPLETE)) { + /* still in autonegotiation - configure nic for 10 MBit HD and wait. */ + newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10; + newdup = 0; + retval = 0; + printf("autoneg not completed - falling back to 10HD.\n"); + goto set_speed; + } + + retval = 1; + if (np->gigabit == PHY_GIGABIT) { + control_1000 = + mii_rw(nic, np->phyaddr, MII_1000BT_CR, MII_READ); + status_1000 = + mii_rw(nic, np->phyaddr, MII_1000BT_SR, MII_READ); + + if ((control_1000 & ADVERTISE_1000FULL) && + (status_1000 & LPA_1000FULL)) { + printf + ("update_linkspeed: GBit ethernet detected.\n"); + newls = + NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_1000; + newdup = 1; + goto set_speed; + } + } + + adv = mii_rw(nic, np->phyaddr, MII_ADVERTISE, MII_READ); + lpa = mii_rw(nic, np->phyaddr, MII_LPA, MII_READ); + dprintf(("update_linkspeed: PHY advertises 0x%hX, lpa 0x%hX.\n", + adv, lpa)); + + /* FIXME: handle parallel detection properly, handle gigabit ethernet */ + lpa = lpa & adv; + if (lpa & LPA_100FULL) { + newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_100; + newdup = 1; + } else if (lpa & LPA_100HALF) { + newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_100; + newdup = 0; + } else if (lpa & LPA_10FULL) { + newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10; + newdup = 1; + } else if (lpa & LPA_10HALF) { + newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10; + newdup = 0; + } else { + printf("bad ability %hX - falling back to 10HD.\n", lpa); + newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10; + newdup = 0; + } + + set_speed: + if (np->duplex == newdup && np->linkspeed == newls) + return retval; + + dprintf(("changing link setting from %d/%s to %d/%s.\n", + np->linkspeed, np->duplex ? "Full-Duplex": "Half-Duplex", newls, newdup ? "Full-Duplex": "Half-Duplex")); + + np->duplex = newdup; + np->linkspeed = newls; + + if (np->gigabit == PHY_GIGABIT) { + phyreg = readl(base + NvRegRandomSeed); + phyreg &= ~(0x3FF00); + if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_10) + phyreg |= NVREG_RNDSEED_FORCE3; + else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_100) + phyreg |= NVREG_RNDSEED_FORCE2; + else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_1000) + phyreg |= NVREG_RNDSEED_FORCE; + writel(phyreg, base + NvRegRandomSeed); + } + + phyreg = readl(base + NvRegPhyInterface); + phyreg &= ~(PHY_HALF | PHY_100 | PHY_1000); + if (np->duplex == 0) + phyreg |= PHY_HALF; + if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_100) + phyreg |= PHY_100; + else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_1000) + phyreg |= PHY_1000; + writel(phyreg, base + NvRegPhyInterface); + + writel(NVREG_MISC1_FORCE | (np->duplex ? 0 : NVREG_MISC1_HD), + base + NvRegMisc1); + pci_push(base); + writel(np->linkspeed, base + NvRegLinkSpeed); + pci_push(base); + + return retval; +} + +#if 0 /* Not used */ +static void nv_linkchange(struct nic *nic) +{ + if (update_linkspeed(nic)) { +// if (netif_carrier_ok(nic)) { + stop_rx(); +//= } else { + // netif_carrier_on(dev); + // printk(KERN_INFO "%s: link up.\n", dev->name); + // } + start_rx(nic); + } else { + // if (netif_carrier_ok(dev)) { + // netif_carrier_off(dev); + // printk(KERN_INFO "%s: link down.\n", dev->name); + stop_rx(); + // } + } +} +#endif + +static int init_ring(struct nic *nic) +{ + int i; + + np->next_tx = np->nic_tx = 0; + for (i = 0; i < TX_RING; i++) + tx_ring[i].FlagLen = 0; + + np->cur_rx = 0; + np->refill_rx = 0; + for (i = 0; i < RX_RING; i++) + rx_ring[i].FlagLen = 0; + return alloc_rx(nic); +} + +static void set_multicast(struct nic *nic) +{ + + u8 *base = (u8 *) BASE; + u32 addr[2]; + u32 mask[2]; + u32 pff; + u32 alwaysOff[2]; + u32 alwaysOn[2]; + + memset(addr, 0, sizeof(addr)); + memset(mask, 0, sizeof(mask)); + + pff = NVREG_PFF_MYADDR; + + alwaysOn[0] = alwaysOn[1] = alwaysOff[0] = alwaysOff[1] = 0; + + addr[0] = alwaysOn[0]; + addr[1] = alwaysOn[1]; + mask[0] = alwaysOn[0] | alwaysOff[0]; + mask[1] = alwaysOn[1] | alwaysOff[1]; + + addr[0] |= NVREG_MCASTADDRA_FORCE; + pff |= NVREG_PFF_ALWAYS; + stop_rx(); + writel(addr[0], base + NvRegMulticastAddrA); + writel(addr[1], base + NvRegMulticastAddrB); + writel(mask[0], base + NvRegMulticastMaskA); + writel(mask[1], base + NvRegMulticastMaskB); + writel(pff, base + NvRegPacketFilterFlags); + start_rx(nic); +} + +/************************************************************************** +RESET - Reset the NIC to prepare for use +***************************************************************************/ +static int forcedeth_reset(struct nic *nic) +{ + u8 *base = (u8 *) BASE; + int ret, oom, i; + ret = 0; + dprintf(("forcedeth: open\n")); + + /* 1) erase previous misconfiguration */ + /* 4.1-1: stop adapter: ignored, 4.3 seems to be overkill */ + writel(NVREG_MCASTADDRA_FORCE, base + NvRegMulticastAddrA); + writel(0, base + NvRegMulticastAddrB); + writel(0, base + NvRegMulticastMaskA); + writel(0, base + NvRegMulticastMaskB); + writel(0, base + NvRegPacketFilterFlags); + + writel(0, base + NvRegTransmitterControl); + writel(0, base + NvRegReceiverControl); + + writel(0, base + NvRegAdapterControl); + + /* 2) initialize descriptor rings */ + oom = init_ring(nic); + + writel(0, base + NvRegLinkSpeed); + writel(0, base + NvRegUnknownTransmitterReg); + txrx_reset(nic); + writel(0, base + NvRegUnknownSetupReg6); + + np->in_shutdown = 0; + + /* 3) set mac address */ + { + u32 mac[2]; + + mac[0] = + (nic->node_addr[0] << 0) + (nic->node_addr[1] << 8) + + (nic->node_addr[2] << 16) + (nic->node_addr[3] << 24); + mac[1] = + (nic->node_addr[4] << 0) + (nic->node_addr[5] << 8); + + writel(mac[0], base + NvRegMacAddrA); + writel(mac[1], base + NvRegMacAddrB); + } + + /* 4) give hw rings */ + writel((u32) virt_to_le32desc(&rx_ring[0]), + base + NvRegRxRingPhysAddr); + writel((u32) virt_to_le32desc(&tx_ring[0]), + base + NvRegTxRingPhysAddr); + + writel(((RX_RING - 1) << NVREG_RINGSZ_RXSHIFT) + + ((TX_RING - 1) << NVREG_RINGSZ_TXSHIFT), + base + NvRegRingSizes); + + /* 5) continue setup */ + np->linkspeed = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10; + np->duplex = 0; + writel(np->linkspeed, base + NvRegLinkSpeed); + writel(NVREG_UNKSETUP3_VAL1, base + NvRegUnknownSetupReg3); + writel(np->desc_ver, base + NvRegTxRxControl); + pci_push(base); + writel(NVREG_TXRXCTL_BIT1 | np->desc_ver, base + NvRegTxRxControl); + reg_delay(NvRegUnknownSetupReg5, NVREG_UNKSETUP5_BIT31, + NVREG_UNKSETUP5_BIT31, NV_SETUP5_DELAY, + NV_SETUP5_DELAYMAX, + "open: SetupReg5, Bit 31 remained off\n"); + + writel(0, base + NvRegUnknownSetupReg4); +// writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus); + writel(NVREG_MIISTAT_MASK2, base + NvRegMIIStatus); +#if 0 + printf("%d-Mbs Link, %s-Duplex\n", + np->linkspeed & NVREG_LINKSPEED_10 ? 10 : 100, + np->duplex ? "Full" : "Half"); +#endif + + /* 6) continue setup */ + writel(NVREG_MISC1_FORCE | NVREG_MISC1_HD, base + NvRegMisc1); + writel(readl(base + NvRegTransmitterStatus), + base + NvRegTransmitterStatus); + writel(NVREG_PFF_ALWAYS, base + NvRegPacketFilterFlags); + writel(NVREG_OFFLOAD_NORMAL, base + NvRegOffloadConfig); + + writel(readl(base + NvRegReceiverStatus), + base + NvRegReceiverStatus); + + /* Get a random number */ + i = random(); + writel(NVREG_RNDSEED_FORCE | (i & NVREG_RNDSEED_MASK), + base + NvRegRandomSeed); + writel(NVREG_UNKSETUP1_VAL, base + NvRegUnknownSetupReg1); + writel(NVREG_UNKSETUP2_VAL, base + NvRegUnknownSetupReg2); + writel(NVREG_POLL_DEFAULT, base + NvRegPollingInterval); + writel(NVREG_UNKSETUP6_VAL, base + NvRegUnknownSetupReg6); + writel((np-> + phyaddr << NVREG_ADAPTCTL_PHYSHIFT) | + NVREG_ADAPTCTL_PHYVALID | NVREG_ADAPTCTL_RUNNING, + base + NvRegAdapterControl); + writel(NVREG_MIISPEED_BIT8 | NVREG_MIIDELAY, base + NvRegMIISpeed); + writel(NVREG_UNKSETUP4_VAL, base + NvRegUnknownSetupReg4); + writel(NVREG_WAKEUPFLAGS_VAL, base + NvRegWakeUpFlags); + + i = readl(base + NvRegPowerState); + if ((i & NVREG_POWERSTATE_POWEREDUP) == 0) + writel(NVREG_POWERSTATE_POWEREDUP | i, + base + NvRegPowerState); + + pci_push(base); + udelay(10); + writel(readl(base + NvRegPowerState) | NVREG_POWERSTATE_VALID, + base + NvRegPowerState); + + writel(0, base + NvRegIrqMask); + pci_push(base); + writel(NVREG_MIISTAT_MASK2, base + NvRegMIIStatus); + writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus); + pci_push(base); +/* + writel(np->irqmask, base + NvRegIrqMask); +*/ + writel(NVREG_MCASTADDRA_FORCE, base + NvRegMulticastAddrA); + writel(0, base + NvRegMulticastAddrB); + writel(0, base + NvRegMulticastMaskA); + writel(0, base + NvRegMulticastMaskB); + writel(NVREG_PFF_ALWAYS | NVREG_PFF_MYADDR, + base + NvRegPacketFilterFlags); + + set_multicast(nic); + /* One manual link speed update: Interrupts are enabled, future link + * speed changes cause interrupts and are handled by nv_link_irq(). + */ + { + u32 miistat; + miistat = readl(base + NvRegMIIStatus); + writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus); + dprintf(("startup: got 0x%hX.\n", miistat)); + } + ret = update_linkspeed(nic); + + //start_rx(nic); + start_tx(nic); + + if (ret) { + //Start Connection netif_carrier_on(dev); + } else { + printf("no link during initialization.\n"); + } + + return ret; +} + +/* + * extern void hex_dump(const char *data, const unsigned int len); +*/ +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int forcedeth_poll(struct nic *nic, int retrieve) +{ + /* return true if there's an ethernet packet ready to read */ + /* nic->packet should contain data on return */ + /* nic->packetlen should contain length of data */ + + int len; + int i; + u32 Flags; + + i = np->cur_rx % RX_RING; + + Flags = le32_to_cpu(rx_ring[i].FlagLen); + len = nv_descr_getlength(&rx_ring[i], np->desc_ver); + + if (Flags & NV_RX_AVAIL) + return 0; /* still owned by hardware, */ + + if (np->desc_ver == DESC_VER_1) { + if (!(Flags & NV_RX_DESCRIPTORVALID)) + return 0; + } else { + if (!(Flags & NV_RX2_DESCRIPTORVALID)) + return 0; + } + + if (!retrieve) + return 1; + + /* got a valid packet - forward it to the network core */ + nic->packetlen = len; + memcpy(nic->packet, rxb + (i * RX_NIC_BUFSIZE), nic->packetlen); +/* + * hex_dump(rxb + (i * RX_NIC_BUFSIZE), len); +*/ + wmb(); + np->cur_rx++; + alloc_rx(nic); + return 1; +} + + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static void forcedeth_transmit(struct nic *nic, const char *d, /* Destination */ + unsigned int t, /* Type */ + unsigned int s, /* size */ + const char *p) +{ /* Packet */ + /* send the packet to destination */ + u8 *ptxb; + u16 nstype; + u8 *base = (u8 *) BASE; + int nr = np->next_tx % TX_RING; + + /* point to the current txb incase multiple tx_rings are used */ + ptxb = txb + (nr * RX_NIC_BUFSIZE); + //np->tx_skbuff[nr] = ptxb; + + /* copy the packet to ring buffer */ + memcpy(ptxb, d, ETH_ALEN); /* dst */ + memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */ + nstype = htons((u16) t); /* type */ + memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2); /* type */ + memcpy(ptxb + ETH_HLEN, p, s); + + s += ETH_HLEN; + while (s < ETH_ZLEN) /* pad to min length */ + ptxb[s++] = '\0'; + + tx_ring[nr].PacketBuffer = (u32) virt_to_le32desc(ptxb); + + wmb(); + tx_ring[nr].FlagLen = cpu_to_le32((s - 1) | np->tx_flags); + + writel(NVREG_TXRXCTL_KICK | np->desc_ver, base + NvRegTxRxControl); + pci_push(base); + np->next_tx++; +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void forcedeth_disable ( struct nic *nic __unused ) { + /* put the card in its initial state */ + /* This function serves 3 purposes. + * This disables DMA and interrupts so we don't receive + * unexpected packets or interrupts from the card after + * etherboot has finished. + * This frees resources so etherboot may use + * this driver on another interface + * This allows etherboot to reinitialize the interface + * if something is something goes wrong. + */ + u8 *base = (u8 *) BASE; + np->in_shutdown = 1; + stop_tx(); + stop_rx(); + + /* disable interrupts on the nic or we will lock up */ + writel(0, base + NvRegIrqMask); + pci_push(base); + dprintf(("Irqmask is zero again\n")); + + /* specia op:o write back the misordered MAC address - otherwise + * the next probe_nic would see a wrong address. + */ + writel(np->orig_mac[0], base + NvRegMacAddrA); + writel(np->orig_mac[1], base + NvRegMacAddrB); +} + +/************************************************************************** +IRQ - Enable, Disable, or Force interrupts +***************************************************************************/ +static void forcedeth_irq(struct nic *nic __unused, + irq_action_t action __unused) +{ + switch (action) { + case DISABLE: + break; + case ENABLE: + break; + case FORCE: + break; + } +} + +static struct nic_operations forcedeth_operations = { + .connect = dummy_connect, + .poll = forcedeth_poll, + .transmit = forcedeth_transmit, + .irq = forcedeth_irq, + +}; + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ +#define IORESOURCE_MEM 0x00000200 +#define board_found 1 +#define valid_link 0 +static int forcedeth_probe ( struct nic *nic, struct pci_device *pci ) { + + unsigned long addr; + int sz; + u8 *base; + int i; + + if (pci->ioaddr == 0) + return 0; + + printf("forcedeth.c: Found %s, vendor=0x%hX, device=0x%hX\n", + pci->driver_name, pci->vendor, pci->device); + + nic->ioaddr = pci->ioaddr; + nic->irqno = 0; + + /* point to private storage */ + np = &npx; + + adjust_pci_device(pci); + + addr = pci_bar_start(pci, PCI_BASE_ADDRESS_0); + sz = pci_bar_size(pci, PCI_BASE_ADDRESS_0); + + /* BASE is used throughout to address the card */ + BASE = (unsigned long) ioremap(addr, sz); + if (!BASE) + return 0; + + /* handle different descriptor versions */ + if (pci->device == PCI_DEVICE_ID_NVIDIA_NVENET_1 || + pci->device == PCI_DEVICE_ID_NVIDIA_NVENET_2 || + pci->device == PCI_DEVICE_ID_NVIDIA_NVENET_3) + np->desc_ver = DESC_VER_1; + else + np->desc_ver = DESC_VER_2; + + //rx_ring[0] = rx_ring; + //tx_ring[0] = tx_ring; + + /* read the mac address */ + base = (u8 *) BASE; + np->orig_mac[0] = readl(base + NvRegMacAddrA); + np->orig_mac[1] = readl(base + NvRegMacAddrB); + + nic->node_addr[0] = (np->orig_mac[1] >> 8) & 0xff; + nic->node_addr[1] = (np->orig_mac[1] >> 0) & 0xff; + nic->node_addr[2] = (np->orig_mac[0] >> 24) & 0xff; + nic->node_addr[3] = (np->orig_mac[0] >> 16) & 0xff; + nic->node_addr[4] = (np->orig_mac[0] >> 8) & 0xff; + nic->node_addr[5] = (np->orig_mac[0] >> 0) & 0xff; +#ifdef LINUX + if (!is_valid_ether_addr(dev->dev_addr)) { + /* + * Bad mac address. At least one bios sets the mac address + * to 01:23:45:67:89:ab + */ + printk(KERN_ERR + "%s: Invalid Mac address detected: %02x:%02x:%02x:%02x:%02x:%02x\n", + pci_name(pci_dev), dev->dev_addr[0], + dev->dev_addr[1], dev->dev_addr[2], + dev->dev_addr[3], dev->dev_addr[4], + dev->dev_addr[5]); + printk(KERN_ERR + "Please complain to your hardware vendor. Switching to a random MAC.\n"); + dev->dev_addr[0] = 0x00; + dev->dev_addr[1] = 0x00; + dev->dev_addr[2] = 0x6c; + get_random_bytes(&dev->dev_addr[3], 3); + } +#endif + + DBG ( "%s: MAC Address %s\n", pci->driver_name, eth_ntoa ( nic->node_addr ) ); + + /* disable WOL */ + writel(0, base + NvRegWakeUpFlags); + np->wolenabled = 0; + + if (np->desc_ver == DESC_VER_1) { + np->tx_flags = NV_TX_LASTPACKET | NV_TX_VALID; + } else { + np->tx_flags = NV_TX2_LASTPACKET | NV_TX2_VALID; + } + + switch (pci->device) { + case 0x01C3: // nforce + // DEV_IRQMASK_1|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER, + np->irqmask = NVREG_IRQMASK_WANTED_2 | NVREG_IRQ_TIMER; + // np->need_linktimer = 1; + // np->link_timeout = jiffies + LINK_TIMEOUT; + break; + case 0x0066: + /* Fall Through */ + case 0x00D6: + // DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER + np->irqmask = NVREG_IRQMASK_WANTED_2; + np->irqmask |= NVREG_IRQ_TIMER; + // np->need_linktimer = 1; + // np->link_timeout = jiffies + LINK_TIMEOUT; + if (np->desc_ver == DESC_VER_1) + np->tx_flags |= NV_TX_LASTPACKET1; + else + np->tx_flags |= NV_TX2_LASTPACKET1; + break; + case 0x0373: + /* Fall Through */ + case 0x0086: + /* Fall Through */ + case 0x008c: + /* Fall Through */ + case 0x00e6: + /* Fall Through */ + case 0x00df: + /* Fall Through */ + case 0x0056: + /* Fall Through */ + case 0x0057: + /* Fall Through */ + case 0x0037: + /* Fall Through */ + case 0x0038: + //DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ + np->irqmask = NVREG_IRQMASK_WANTED_2; + np->irqmask |= NVREG_IRQ_TIMER; + // np->need_linktimer = 1; + // np->link_timeout = jiffies + LINK_TIMEOUT; + if (np->desc_ver == DESC_VER_1) + np->tx_flags |= NV_TX_LASTPACKET1; + else + np->tx_flags |= NV_TX2_LASTPACKET1; + break; + default: + printf + ("Your card was undefined in this driver. Review driver_data in Linux driver and send a patch\n"); + } + + /* find a suitable phy */ + for (i = 1; i < 32; i++) { + int id1, id2; + id1 = mii_rw(nic, i, MII_PHYSID1, MII_READ); + if (id1 < 0 || id1 == 0xffff) + continue; + id2 = mii_rw(nic, i, MII_PHYSID2, MII_READ); + if (id2 < 0 || id2 == 0xffff) + continue; + id1 = (id1 & PHYID1_OUI_MASK) << PHYID1_OUI_SHFT; + id2 = (id2 & PHYID2_OUI_MASK) >> PHYID2_OUI_SHFT; + dprintf + (("%s: open: Found PHY %hX:%hX at address %d.\n", + pci->driver_name, id1, id2, i)); + np->phyaddr = i; + np->phy_oui = id1 | id2; + break; + } + if (i == 32) { + /* PHY in isolate mode? No phy attached and user wants to + * test loopback? Very odd, but can be correct. + */ + printf + ("%s: open: Could not find a valid PHY.\n", pci->driver_name); + } + + if (i != 32) { + /* reset it */ + phy_init(nic); + } + + dprintf(("%s: forcedeth.c: subsystem: %hX:%hX bound to %s\n", + pci->driver_name, pci->vendor, pci->dev_id, pci->driver_name)); + if(!forcedeth_reset(nic)) return 0; // no valid link + + /* point to NIC specific routines */ + nic->nic_op = &forcedeth_operations; + return 1; +} + +static struct pci_device_id forcedeth_nics[] = { +PCI_ROM(0x10de, 0x01C3, "nforce", "nForce NVENET_1 Ethernet Controller"), +PCI_ROM(0x10de, 0x0066, "nforce2", "nForce NVENET_2 Ethernet Controller"), +PCI_ROM(0x10de, 0x00D6, "nforce3", "nForce NVENET_3 Ethernet Controller"), +PCI_ROM(0x10de, 0x0086, "nforce4", "nForce NVENET_4 Ethernet Controller"), +PCI_ROM(0x10de, 0x008c, "nforce5", "nForce NVENET_5 Ethernet Controller"), +PCI_ROM(0x10de, 0x00e6, "nforce6", "nForce NVENET_6 Ethernet Controller"), +PCI_ROM(0x10de, 0x00df, "nforce7", "nForce NVENET_7 Ethernet Controller"), +PCI_ROM(0x10de, 0x0056, "nforce8", "nForce NVENET_8 Ethernet Controller"), +PCI_ROM(0x10de, 0x0057, "nforce9", "nForce NVENET_9 Ethernet Controller"), +PCI_ROM(0x10de, 0x0037, "nforce10", "nForce NVENET_10 Ethernet Controller"), +PCI_ROM(0x10de, 0x0038, "nforce11", "nForce NVENET_11 Ethernet Controller"), +PCI_ROM(0x10de, 0x0373, "nforce15", "nForce NVENET_15 Ethernet Controller") +}; + +PCI_DRIVER ( forcedeth_driver, forcedeth_nics, PCI_NO_CLASS ); + +DRIVER ( "forcedeth", nic_driver, pci_driver, forcedeth_driver, + forcedeth_probe, forcedeth_disable ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/hfa384x.h b/gpxe/src/drivers/net/hfa384x.h new file mode 100644 index 0000000..afb5069 --- /dev/null +++ b/gpxe/src/drivers/net/hfa384x.h @@ -0,0 +1,3067 @@ +/* src/prism2/include/prism2/hfa384x.h +* +* Defines the constants and data structures for the hfa384x +* +* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. +* -------------------------------------------------------------------- +* +* linux-wlan +* +* The contents of this file are subject to the Mozilla Public +* License Version 1.1 (the "License"); you may not use this file +* except in compliance with the License. You may obtain a copy of +* the License at http://www.mozilla.org/MPL/ +* +* Software distributed under the License is distributed on an "AS +* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +* implied. See the License for the specific language governing +* rights and limitations under the License. +* +* Alternatively, the contents of this file may be used under the +* terms of the GNU Public License version 2 (the "GPL"), in which +* case the provisions of the GPL are applicable instead of the +* above. If you wish to allow the use of your version of this file +* only under the terms of the GPL and not to allow others to use +* your version of this file under the MPL, indicate your decision +* by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL. If you do not delete +* the provisions above, a recipient may use your version of this +* file under either the MPL or the GPL. +* +* -------------------------------------------------------------------- +* +* Inquiries regarding the linux-wlan Open Source project can be +* made directly to: +* +* AbsoluteValue Systems Inc. +* info@linux-wlan.com +* http://www.linux-wlan.com +* +* -------------------------------------------------------------------- +* +* Portions of the development of this software were funded by +* Intersil Corporation as part of PRISM(R) chipset product development. +* +* -------------------------------------------------------------------- +* +* [Implementation and usage notes] +* +* [References] +* CW10 Programmer's Manual v1.5 +* IEEE 802.11 D10.0 +* +* -------------------------------------------------------------------- +*/ + +#ifndef _HFA384x_H +#define _HFA384x_H + +/*=============================================================*/ +#define HFA384x_FIRMWARE_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +#define HFA384x_LEVEL_TO_dBm(v) (0x100 + (v) * 100 / 255 - 100) + +/*------ Constants --------------------------------------------*/ +/*--- Mins & Maxs -----------------------------------*/ +#define HFA384x_CMD_ALLOC_LEN_MIN ((UINT16)4) +#define HFA384x_CMD_ALLOC_LEN_MAX ((UINT16)2400) +#define HFA384x_BAP_DATALEN_MAX ((UINT16)4096) +#define HFA384x_BAP_OFFSET_MAX ((UINT16)4096) +#define HFA384x_PORTID_MAX ((UINT16)7) +#define HFA384x_NUMPORTS_MAX ((UINT16)(HFA384x_PORTID_MAX+1)) +#define HFA384x_PDR_LEN_MAX ((UINT16)512) /* in bytes, from EK */ +#define HFA384x_PDA_RECS_MAX ((UINT16)200) /* a guess */ +#define HFA384x_PDA_LEN_MAX ((UINT16)1024) /* in bytes, from EK */ +#define HFA384x_SCANRESULT_MAX ((UINT16)31) +#define HFA384x_HSCANRESULT_MAX ((UINT16)31) +#define HFA384x_CHINFORESULT_MAX ((UINT16)16) +#define HFA384x_DRVR_FIDSTACKLEN_MAX (10) +#define HFA384x_DRVR_TXBUF_MAX (sizeof(hfa384x_tx_frame_t) + \ + WLAN_DATA_MAXLEN - \ + WLAN_WEP_IV_LEN - \ + WLAN_WEP_ICV_LEN + 2) +#define HFA384x_DRVR_MAGIC (0x4a2d) +#define HFA384x_INFODATA_MAXLEN (sizeof(hfa384x_infodata_t)) +#define HFA384x_INFOFRM_MAXLEN (sizeof(hfa384x_InfFrame_t)) +#define HFA384x_RID_GUESSING_MAXLEN 2048 /* I'm not really sure */ +#define HFA384x_RIDDATA_MAXLEN HFA384x_RID_GUESSING_MAXLEN +#define HFA384x_USB_RWMEM_MAXLEN 2048 + +/*--- Support Constants -----------------------------*/ +#define HFA384x_BAP_PROC ((UINT16)0) +#define HFA384x_BAP_INT ((UINT16)1) +#define HFA384x_PORTTYPE_IBSS ((UINT16)0) +#define HFA384x_PORTTYPE_BSS ((UINT16)1) +#define HFA384x_PORTTYPE_WDS ((UINT16)2) +#define HFA384x_PORTTYPE_PSUEDOIBSS ((UINT16)3) +#define HFA384x_PORTTYPE_HOSTAP ((UINT16)6) +#define HFA384x_WEPFLAGS_PRIVINVOKED ((UINT16)BIT0) +#define HFA384x_WEPFLAGS_EXCLUDE ((UINT16)BIT1) +#define HFA384x_WEPFLAGS_DISABLE_TXCRYPT ((UINT16)BIT4) +#define HFA384x_WEPFLAGS_DISABLE_RXCRYPT ((UINT16)BIT7) +#define HFA384x_WEPFLAGS_DISALLOW_MIXED ((UINT16)BIT11) +#define HFA384x_WEPFLAGS_IV_INTERVAL1 ((UINT16)0) +#define HFA384x_WEPFLAGS_IV_INTERVAL10 ((UINT16)BIT5) +#define HFA384x_WEPFLAGS_IV_INTERVAL50 ((UINT16)BIT6) +#define HFA384x_WEPFLAGS_IV_INTERVAL100 ((UINT16)(BIT5 | BIT6)) +#define HFA384x_WEPFLAGS_FIRMWARE_WPA ((UINT16)BIT8) +#define HFA384x_WEPFLAGS_HOST_MIC ((UINT16)BIT9) +#define HFA384x_ROAMMODE_FWSCAN_FWROAM ((UINT16)1) +#define HFA384x_ROAMMODE_FWSCAN_HOSTROAM ((UINT16)2) +#define HFA384x_ROAMMODE_HOSTSCAN_HOSTROAM ((UINT16)3) +#define HFA384x_PORTSTATUS_DISABLED ((UINT16)1) +#define HFA384x_PORTSTATUS_INITSRCH ((UINT16)2) +#define HFA384x_PORTSTATUS_CONN_IBSS ((UINT16)3) +#define HFA384x_PORTSTATUS_CONN_ESS ((UINT16)4) +#define HFA384x_PORTSTATUS_OOR_ESS ((UINT16)5) +#define HFA384x_PORTSTATUS_CONN_WDS ((UINT16)6) +#define HFA384x_PORTSTATUS_HOSTAP ((UINT16)8) +#define HFA384x_RATEBIT_1 ((UINT16)1) +#define HFA384x_RATEBIT_2 ((UINT16)2) +#define HFA384x_RATEBIT_5dot5 ((UINT16)4) +#define HFA384x_RATEBIT_11 ((UINT16)8) + +/*--- Just some symbolic names for legibility -------*/ +#define HFA384x_TXCMD_NORECL ((UINT16)0) +#define HFA384x_TXCMD_RECL ((UINT16)1) + +/*--- MAC Internal memory constants and macros ------*/ +/* masks and macros used to manipulate MAC internal memory addresses. */ +/* MAC internal memory addresses are 23 bit quantities. The MAC uses + * a paged address space where the upper 16 bits are the page number + * and the lower 7 bits are the offset. There are various Host API + * elements that require two 16-bit quantities to specify a MAC + * internal memory address. Unfortunately, some of the API's use a + * page/offset format where the offset value is JUST the lower seven + * bits and the page is the remaining 16 bits. Some of the API's + * assume that the 23 bit address has been split at the 16th bit. We + * refer to these two formats as AUX format and CMD format. The + * macros below help handle some of this. + */ + +/* Handy constant */ +#define HFA384x_ADDR_AUX_OFF_MAX ((UINT16)0x007f) + +/* Mask bits for discarding unwanted pieces in a flat address */ +#define HFA384x_ADDR_FLAT_AUX_PAGE_MASK (0x007fff80) +#define HFA384x_ADDR_FLAT_AUX_OFF_MASK (0x0000007f) +#define HFA384x_ADDR_FLAT_CMD_PAGE_MASK (0xffff0000) +#define HFA384x_ADDR_FLAT_CMD_OFF_MASK (0x0000ffff) + +/* Mask bits for discarding unwanted pieces in AUX format 16-bit address parts */ +#define HFA384x_ADDR_AUX_PAGE_MASK (0xffff) +#define HFA384x_ADDR_AUX_OFF_MASK (0x007f) + +/* Mask bits for discarding unwanted pieces in CMD format 16-bit address parts */ +#define HFA384x_ADDR_CMD_PAGE_MASK (0x007f) +#define HFA384x_ADDR_CMD_OFF_MASK (0xffff) + +/* Make a 32-bit flat address from AUX format 16-bit page and offset */ +#define HFA384x_ADDR_AUX_MKFLAT(p,o) \ + (((UINT32)(((UINT16)(p))&HFA384x_ADDR_AUX_PAGE_MASK)) <<7) | \ + ((UINT32)(((UINT16)(o))&HFA384x_ADDR_AUX_OFF_MASK)) + +/* Make a 32-bit flat address from CMD format 16-bit page and offset */ +#define HFA384x_ADDR_CMD_MKFLAT(p,o) \ + (((UINT32)(((UINT16)(p))&HFA384x_ADDR_CMD_PAGE_MASK)) <<16) | \ + ((UINT32)(((UINT16)(o))&HFA384x_ADDR_CMD_OFF_MASK)) + +/* Make AUX format offset and page from a 32-bit flat address */ +#define HFA384x_ADDR_AUX_MKPAGE(f) \ + ((UINT16)((((UINT32)(f))&HFA384x_ADDR_FLAT_AUX_PAGE_MASK)>>7)) +#define HFA384x_ADDR_AUX_MKOFF(f) \ + ((UINT16)(((UINT32)(f))&HFA384x_ADDR_FLAT_AUX_OFF_MASK)) + +/* Make CMD format offset and page from a 32-bit flat address */ +#define HFA384x_ADDR_CMD_MKPAGE(f) \ + ((UINT16)((((UINT32)(f))&HFA384x_ADDR_FLAT_CMD_PAGE_MASK)>>16)) +#define HFA384x_ADDR_CMD_MKOFF(f) \ + ((UINT16)(((UINT32)(f))&HFA384x_ADDR_FLAT_CMD_OFF_MASK)) + +/*--- Aux register masks/tests ----------------------*/ +/* Some of the upper bits of the AUX offset register are used to */ +/* select address space. */ +#define HFA384x_AUX_CTL_EXTDS (0x00) +#define HFA384x_AUX_CTL_NV (0x01) +#define HFA384x_AUX_CTL_PHY (0x02) +#define HFA384x_AUX_CTL_ICSRAM (0x03) + +/* Make AUX register offset and page values from a flat address */ +#define HFA384x_AUX_MKOFF(f, c) \ + (HFA384x_ADDR_AUX_MKOFF(f) | (((UINT16)(c))<<12)) +#define HFA384x_AUX_MKPAGE(f) HFA384x_ADDR_AUX_MKPAGE(f) + + +/*--- Controller Memory addresses -------------------*/ +#define HFA3842_PDA_BASE (0x007f0000UL) +#define HFA3841_PDA_BASE (0x003f0000UL) +#define HFA3841_PDA_BOGUS_BASE (0x00390000UL) + +/*--- Driver Download states -----------------------*/ +#define HFA384x_DLSTATE_DISABLED 0 +#define HFA384x_DLSTATE_RAMENABLED 1 +#define HFA384x_DLSTATE_FLASHENABLED 2 +#define HFA384x_DLSTATE_FLASHWRITTEN 3 +#define HFA384x_DLSTATE_FLASHWRITEPENDING 4 +#define HFA384x_DLSTATE_GENESIS 5 + +/*--- Register I/O offsets --------------------------*/ +#if ((WLAN_HOSTIF == WLAN_PCMCIA) || (WLAN_HOSTIF == WLAN_PLX)) + +#define HFA384x_CMD_OFF (0x00) +#define HFA384x_PARAM0_OFF (0x02) +#define HFA384x_PARAM1_OFF (0x04) +#define HFA384x_PARAM2_OFF (0x06) +#define HFA384x_STATUS_OFF (0x08) +#define HFA384x_RESP0_OFF (0x0A) +#define HFA384x_RESP1_OFF (0x0C) +#define HFA384x_RESP2_OFF (0x0E) +#define HFA384x_INFOFID_OFF (0x10) +#define HFA384x_RXFID_OFF (0x20) +#define HFA384x_ALLOCFID_OFF (0x22) +#define HFA384x_TXCOMPLFID_OFF (0x24) +#define HFA384x_SELECT0_OFF (0x18) +#define HFA384x_OFFSET0_OFF (0x1C) +#define HFA384x_DATA0_OFF (0x36) +#define HFA384x_SELECT1_OFF (0x1A) +#define HFA384x_OFFSET1_OFF (0x1E) +#define HFA384x_DATA1_OFF (0x38) +#define HFA384x_EVSTAT_OFF (0x30) +#define HFA384x_INTEN_OFF (0x32) +#define HFA384x_EVACK_OFF (0x34) +#define HFA384x_CONTROL_OFF (0x14) +#define HFA384x_SWSUPPORT0_OFF (0x28) +#define HFA384x_SWSUPPORT1_OFF (0x2A) +#define HFA384x_SWSUPPORT2_OFF (0x2C) +#define HFA384x_AUXPAGE_OFF (0x3A) +#define HFA384x_AUXOFFSET_OFF (0x3C) +#define HFA384x_AUXDATA_OFF (0x3E) + +#elif (WLAN_HOSTIF == WLAN_PCI || WLAN_HOSTIF == WLAN_USB) + +#define HFA384x_CMD_OFF (0x00) +#define HFA384x_PARAM0_OFF (0x04) +#define HFA384x_PARAM1_OFF (0x08) +#define HFA384x_PARAM2_OFF (0x0c) +#define HFA384x_STATUS_OFF (0x10) +#define HFA384x_RESP0_OFF (0x14) +#define HFA384x_RESP1_OFF (0x18) +#define HFA384x_RESP2_OFF (0x1c) +#define HFA384x_INFOFID_OFF (0x20) +#define HFA384x_RXFID_OFF (0x40) +#define HFA384x_ALLOCFID_OFF (0x44) +#define HFA384x_TXCOMPLFID_OFF (0x48) +#define HFA384x_SELECT0_OFF (0x30) +#define HFA384x_OFFSET0_OFF (0x38) +#define HFA384x_DATA0_OFF (0x6c) +#define HFA384x_SELECT1_OFF (0x34) +#define HFA384x_OFFSET1_OFF (0x3c) +#define HFA384x_DATA1_OFF (0x70) +#define HFA384x_EVSTAT_OFF (0x60) +#define HFA384x_INTEN_OFF (0x64) +#define HFA384x_EVACK_OFF (0x68) +#define HFA384x_CONTROL_OFF (0x28) +#define HFA384x_SWSUPPORT0_OFF (0x50) +#define HFA384x_SWSUPPORT1_OFF (0x54) +#define HFA384x_SWSUPPORT2_OFF (0x58) +#define HFA384x_AUXPAGE_OFF (0x74) +#define HFA384x_AUXOFFSET_OFF (0x78) +#define HFA384x_AUXDATA_OFF (0x7c) +#define HFA384x_PCICOR_OFF (0x4c) +#define HFA384x_PCIHCR_OFF (0x5c) +#define HFA384x_PCI_M0_ADDRH_OFF (0x80) +#define HFA384x_PCI_M0_ADDRL_OFF (0x84) +#define HFA384x_PCI_M0_LEN_OFF (0x88) +#define HFA384x_PCI_M0_CTL_OFF (0x8c) +#define HFA384x_PCI_STATUS_OFF (0x98) +#define HFA384x_PCI_M1_ADDRH_OFF (0xa0) +#define HFA384x_PCI_M1_ADDRL_OFF (0xa4) +#define HFA384x_PCI_M1_LEN_OFF (0xa8) +#define HFA384x_PCI_M1_CTL_OFF (0xac) + +#endif + +/*--- Register Field Masks --------------------------*/ +#define HFA384x_CMD_BUSY ((UINT16)BIT15) +#define HFA384x_CMD_AINFO ((UINT16)(BIT14 | BIT13 | BIT12 | BIT11 | BIT10 | BIT9 | BIT8)) +#define HFA384x_CMD_MACPORT ((UINT16)(BIT10 | BIT9 | BIT8)) +#define HFA384x_CMD_RECL ((UINT16)BIT8) +#define HFA384x_CMD_WRITE ((UINT16)BIT8) +#define HFA384x_CMD_PROGMODE ((UINT16)(BIT9 | BIT8)) +#define HFA384x_CMD_CMDCODE ((UINT16)(BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0)) + +#define HFA384x_STATUS_RESULT ((UINT16)(BIT14 | BIT13 | BIT12 | BIT11 | BIT10 | BIT9 | BIT8)) +#define HFA384x_STATUS_CMDCODE ((UINT16)(BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0)) + +#define HFA384x_OFFSET_BUSY ((UINT16)BIT15) +#define HFA384x_OFFSET_ERR ((UINT16)BIT14) +#define HFA384x_OFFSET_DATAOFF ((UINT16)(BIT11 | BIT10 | BIT9 | BIT8 | BIT7 | BIT6 | BIT5 | BIT4 | BIT3 | BIT2 | BIT1)) + +#define HFA384x_EVSTAT_TICK ((UINT16)BIT15) +#define HFA384x_EVSTAT_WTERR ((UINT16)BIT14) +#define HFA384x_EVSTAT_INFDROP ((UINT16)BIT13) +#define HFA384x_EVSTAT_INFO ((UINT16)BIT7) +#define HFA384x_EVSTAT_DTIM ((UINT16)BIT5) +#define HFA384x_EVSTAT_CMD ((UINT16)BIT4) +#define HFA384x_EVSTAT_ALLOC ((UINT16)BIT3) +#define HFA384x_EVSTAT_TXEXC ((UINT16)BIT2) +#define HFA384x_EVSTAT_TX ((UINT16)BIT1) +#define HFA384x_EVSTAT_RX ((UINT16)BIT0) + +#define HFA384x_INT_BAP_OP (HFA384x_EVSTAT_INFO|HFA384x_EVSTAT_RX|HFA384x_EVSTAT_TX|HFA384x_EVSTAT_TXEXC) + +#define HFA384x_INT_NORMAL (HFA384x_EVSTAT_INFO|HFA384x_EVSTAT_RX|HFA384x_EVSTAT_TX|HFA384x_EVSTAT_TXEXC|HFA384x_EVSTAT_INFDROP|HFA384x_EVSTAT_ALLOC|HFA384x_EVSTAT_DTIM) + +#define HFA384x_INTEN_TICK ((UINT16)BIT15) +#define HFA384x_INTEN_WTERR ((UINT16)BIT14) +#define HFA384x_INTEN_INFDROP ((UINT16)BIT13) +#define HFA384x_INTEN_INFO ((UINT16)BIT7) +#define HFA384x_INTEN_DTIM ((UINT16)BIT5) +#define HFA384x_INTEN_CMD ((UINT16)BIT4) +#define HFA384x_INTEN_ALLOC ((UINT16)BIT3) +#define HFA384x_INTEN_TXEXC ((UINT16)BIT2) +#define HFA384x_INTEN_TX ((UINT16)BIT1) +#define HFA384x_INTEN_RX ((UINT16)BIT0) + +#define HFA384x_EVACK_TICK ((UINT16)BIT15) +#define HFA384x_EVACK_WTERR ((UINT16)BIT14) +#define HFA384x_EVACK_INFDROP ((UINT16)BIT13) +#define HFA384x_EVACK_INFO ((UINT16)BIT7) +#define HFA384x_EVACK_DTIM ((UINT16)BIT5) +#define HFA384x_EVACK_CMD ((UINT16)BIT4) +#define HFA384x_EVACK_ALLOC ((UINT16)BIT3) +#define HFA384x_EVACK_TXEXC ((UINT16)BIT2) +#define HFA384x_EVACK_TX ((UINT16)BIT1) +#define HFA384x_EVACK_RX ((UINT16)BIT0) + +#define HFA384x_CONTROL_AUXEN ((UINT16)(BIT15 | BIT14)) + + +/*--- Command Code Constants --------------------------*/ +/*--- Controller Commands --------------------------*/ +#define HFA384x_CMDCODE_INIT ((UINT16)0x00) +#define HFA384x_CMDCODE_ENABLE ((UINT16)0x01) +#define HFA384x_CMDCODE_DISABLE ((UINT16)0x02) +#define HFA384x_CMDCODE_DIAG ((UINT16)0x03) + +/*--- Buffer Mgmt Commands --------------------------*/ +#define HFA384x_CMDCODE_ALLOC ((UINT16)0x0A) +#define HFA384x_CMDCODE_TX ((UINT16)0x0B) +#define HFA384x_CMDCODE_CLRPRST ((UINT16)0x12) + +/*--- Regulate Commands --------------------------*/ +#define HFA384x_CMDCODE_NOTIFY ((UINT16)0x10) +#define HFA384x_CMDCODE_INQ ((UINT16)0x11) + +/*--- Configure Commands --------------------------*/ +#define HFA384x_CMDCODE_ACCESS ((UINT16)0x21) +#define HFA384x_CMDCODE_DOWNLD ((UINT16)0x22) + +/*--- Debugging Commands -----------------------------*/ +#define HFA384x_CMDCODE_MONITOR ((UINT16)(0x38)) +#define HFA384x_MONITOR_ENABLE ((UINT16)(0x0b)) +#define HFA384x_MONITOR_DISABLE ((UINT16)(0x0f)) + +/*--- Result Codes --------------------------*/ +#define HFA384x_SUCCESS ((UINT16)(0x00)) +#define HFA384x_CARD_FAIL ((UINT16)(0x01)) +#define HFA384x_NO_BUFF ((UINT16)(0x05)) +#define HFA384x_CMD_ERR ((UINT16)(0x7F)) + +/*--- Programming Modes -------------------------- + MODE 0: Disable programming + MODE 1: Enable volatile memory programming + MODE 2: Enable non-volatile memory programming + MODE 3: Program non-volatile memory section +--------------------------------------------------*/ +#define HFA384x_PROGMODE_DISABLE ((UINT16)0x00) +#define HFA384x_PROGMODE_RAM ((UINT16)0x01) +#define HFA384x_PROGMODE_NV ((UINT16)0x02) +#define HFA384x_PROGMODE_NVWRITE ((UINT16)0x03) + +/*--- AUX register enable --------------------------*/ +#define HFA384x_AUXPW0 ((UINT16)0xfe01) +#define HFA384x_AUXPW1 ((UINT16)0xdc23) +#define HFA384x_AUXPW2 ((UINT16)0xba45) + +#define HFA384x_CONTROL_AUX_ISDISABLED ((UINT16)0x0000) +#define HFA384x_CONTROL_AUX_ISENABLED ((UINT16)0xc000) +#define HFA384x_CONTROL_AUX_DOENABLE ((UINT16)0x8000) +#define HFA384x_CONTROL_AUX_DODISABLE ((UINT16)0x4000) + +/*--- Record ID Constants --------------------------*/ +/*-------------------------------------------------------------------- +Configuration RIDs: Network Parameters, Static Configuration Entities +--------------------------------------------------------------------*/ +#define HFA384x_RID_CNFPORTTYPE ((UINT16)0xFC00) +#define HFA384x_RID_CNFOWNMACADDR ((UINT16)0xFC01) +#define HFA384x_RID_CNFDESIREDSSID ((UINT16)0xFC02) +#define HFA384x_RID_CNFOWNCHANNEL ((UINT16)0xFC03) +#define HFA384x_RID_CNFOWNSSID ((UINT16)0xFC04) +#define HFA384x_RID_CNFOWNATIMWIN ((UINT16)0xFC05) +#define HFA384x_RID_CNFSYSSCALE ((UINT16)0xFC06) +#define HFA384x_RID_CNFMAXDATALEN ((UINT16)0xFC07) +#define HFA384x_RID_CNFWDSADDR ((UINT16)0xFC08) +#define HFA384x_RID_CNFPMENABLED ((UINT16)0xFC09) +#define HFA384x_RID_CNFPMEPS ((UINT16)0xFC0A) +#define HFA384x_RID_CNFMULTICASTRX ((UINT16)0xFC0B) +#define HFA384x_RID_CNFMAXSLEEPDUR ((UINT16)0xFC0C) +#define HFA384x_RID_CNFPMHOLDDUR ((UINT16)0xFC0D) +#define HFA384x_RID_CNFOWNNAME ((UINT16)0xFC0E) +#define HFA384x_RID_CNFOWNDTIMPER ((UINT16)0xFC10) +#define HFA384x_RID_CNFWDSADDR1 ((UINT16)0xFC11) +#define HFA384x_RID_CNFWDSADDR2 ((UINT16)0xFC12) +#define HFA384x_RID_CNFWDSADDR3 ((UINT16)0xFC13) +#define HFA384x_RID_CNFWDSADDR4 ((UINT16)0xFC14) +#define HFA384x_RID_CNFWDSADDR5 ((UINT16)0xFC15) +#define HFA384x_RID_CNFWDSADDR6 ((UINT16)0xFC16) +#define HFA384x_RID_CNFMCASTPMBUFF ((UINT16)0xFC17) + +/*-------------------------------------------------------------------- +Configuration RID lengths: Network Params, Static Config Entities + This is the length of JUST the DATA part of the RID (does not + include the len or code fields) +--------------------------------------------------------------------*/ +/* TODO: fill in the rest of these */ +#define HFA384x_RID_CNFPORTTYPE_LEN ((UINT16)2) +#define HFA384x_RID_CNFOWNMACADDR_LEN ((UINT16)6) +#define HFA384x_RID_CNFDESIREDSSID_LEN ((UINT16)34) +#define HFA384x_RID_CNFOWNCHANNEL_LEN ((UINT16)2) +#define HFA384x_RID_CNFOWNSSID_LEN ((UINT16)34) +#define HFA384x_RID_CNFOWNATIMWIN_LEN ((UINT16)2) +#define HFA384x_RID_CNFSYSSCALE_LEN ((UINT16)0) +#define HFA384x_RID_CNFMAXDATALEN_LEN ((UINT16)0) +#define HFA384x_RID_CNFWDSADDR_LEN ((UINT16)6) +#define HFA384x_RID_CNFPMENABLED_LEN ((UINT16)0) +#define HFA384x_RID_CNFPMEPS_LEN ((UINT16)0) +#define HFA384x_RID_CNFMULTICASTRX_LEN ((UINT16)0) +#define HFA384x_RID_CNFMAXSLEEPDUR_LEN ((UINT16)0) +#define HFA384x_RID_CNFPMHOLDDUR_LEN ((UINT16)0) +#define HFA384x_RID_CNFOWNNAME_LEN ((UINT16)34) +#define HFA384x_RID_CNFOWNDTIMPER_LEN ((UINT16)0) +#define HFA384x_RID_CNFWDSADDR1_LEN ((UINT16)6) +#define HFA384x_RID_CNFWDSADDR2_LEN ((UINT16)6) +#define HFA384x_RID_CNFWDSADDR3_LEN ((UINT16)6) +#define HFA384x_RID_CNFWDSADDR4_LEN ((UINT16)6) +#define HFA384x_RID_CNFWDSADDR5_LEN ((UINT16)6) +#define HFA384x_RID_CNFWDSADDR6_LEN ((UINT16)6) +#define HFA384x_RID_CNFMCASTPMBUFF_LEN ((UINT16)0) +#define HFA384x_RID_CNFAUTHENTICATION_LEN ((UINT16)sizeof(UINT16)) +#define HFA384x_RID_CNFMAXSLEEPDUR_LEN ((UINT16)0) + +/*-------------------------------------------------------------------- +Configuration RIDs: Network Parameters, Dynamic Configuration Entities +--------------------------------------------------------------------*/ +#define HFA384x_RID_GROUPADDR ((UINT16)0xFC80) +#define HFA384x_RID_CREATEIBSS ((UINT16)0xFC81) +#define HFA384x_RID_FRAGTHRESH ((UINT16)0xFC82) +#define HFA384x_RID_RTSTHRESH ((UINT16)0xFC83) +#define HFA384x_RID_TXRATECNTL ((UINT16)0xFC84) +#define HFA384x_RID_PROMISCMODE ((UINT16)0xFC85) +#define HFA384x_RID_FRAGTHRESH0 ((UINT16)0xFC90) +#define HFA384x_RID_FRAGTHRESH1 ((UINT16)0xFC91) +#define HFA384x_RID_FRAGTHRESH2 ((UINT16)0xFC92) +#define HFA384x_RID_FRAGTHRESH3 ((UINT16)0xFC93) +#define HFA384x_RID_FRAGTHRESH4 ((UINT16)0xFC94) +#define HFA384x_RID_FRAGTHRESH5 ((UINT16)0xFC95) +#define HFA384x_RID_FRAGTHRESH6 ((UINT16)0xFC96) +#define HFA384x_RID_RTSTHRESH0 ((UINT16)0xFC97) +#define HFA384x_RID_RTSTHRESH1 ((UINT16)0xFC98) +#define HFA384x_RID_RTSTHRESH2 ((UINT16)0xFC99) +#define HFA384x_RID_RTSTHRESH3 ((UINT16)0xFC9A) +#define HFA384x_RID_RTSTHRESH4 ((UINT16)0xFC9B) +#define HFA384x_RID_RTSTHRESH5 ((UINT16)0xFC9C) +#define HFA384x_RID_RTSTHRESH6 ((UINT16)0xFC9D) +#define HFA384x_RID_TXRATECNTL0 ((UINT16)0xFC9E) +#define HFA384x_RID_TXRATECNTL1 ((UINT16)0xFC9F) +#define HFA384x_RID_TXRATECNTL2 ((UINT16)0xFCA0) +#define HFA384x_RID_TXRATECNTL3 ((UINT16)0xFCA1) +#define HFA384x_RID_TXRATECNTL4 ((UINT16)0xFCA2) +#define HFA384x_RID_TXRATECNTL5 ((UINT16)0xFCA3) +#define HFA384x_RID_TXRATECNTL6 ((UINT16)0xFCA4) + +/*-------------------------------------------------------------------- +Configuration RID Lengths: Network Param, Dynamic Config Entities + This is the length of JUST the DATA part of the RID (does not + include the len or code fields) +--------------------------------------------------------------------*/ +/* TODO: fill in the rest of these */ +#define HFA384x_RID_GROUPADDR_LEN ((UINT16)16 * WLAN_ADDR_LEN) +#define HFA384x_RID_CREATEIBSS_LEN ((UINT16)0) +#define HFA384x_RID_FRAGTHRESH_LEN ((UINT16)0) +#define HFA384x_RID_RTSTHRESH_LEN ((UINT16)0) +#define HFA384x_RID_TXRATECNTL_LEN ((UINT16)4) +#define HFA384x_RID_PROMISCMODE_LEN ((UINT16)2) +#define HFA384x_RID_FRAGTHRESH0_LEN ((UINT16)0) +#define HFA384x_RID_FRAGTHRESH1_LEN ((UINT16)0) +#define HFA384x_RID_FRAGTHRESH2_LEN ((UINT16)0) +#define HFA384x_RID_FRAGTHRESH3_LEN ((UINT16)0) +#define HFA384x_RID_FRAGTHRESH4_LEN ((UINT16)0) +#define HFA384x_RID_FRAGTHRESH5_LEN ((UINT16)0) +#define HFA384x_RID_FRAGTHRESH6_LEN ((UINT16)0) +#define HFA384x_RID_RTSTHRESH0_LEN ((UINT16)0) +#define HFA384x_RID_RTSTHRESH1_LEN ((UINT16)0) +#define HFA384x_RID_RTSTHRESH2_LEN ((UINT16)0) +#define HFA384x_RID_RTSTHRESH3_LEN ((UINT16)0) +#define HFA384x_RID_RTSTHRESH4_LEN ((UINT16)0) +#define HFA384x_RID_RTSTHRESH5_LEN ((UINT16)0) +#define HFA384x_RID_RTSTHRESH6_LEN ((UINT16)0) +#define HFA384x_RID_TXRATECNTL0_LEN ((UINT16)0) +#define HFA384x_RID_TXRATECNTL1_LEN ((UINT16)0) +#define HFA384x_RID_TXRATECNTL2_LEN ((UINT16)0) +#define HFA384x_RID_TXRATECNTL3_LEN ((UINT16)0) +#define HFA384x_RID_TXRATECNTL4_LEN ((UINT16)0) +#define HFA384x_RID_TXRATECNTL5_LEN ((UINT16)0) +#define HFA384x_RID_TXRATECNTL6_LEN ((UINT16)0) + +/*-------------------------------------------------------------------- +Configuration RIDs: Behavior Parameters +--------------------------------------------------------------------*/ +#define HFA384x_RID_ITICKTIME ((UINT16)0xFCE0) + +/*-------------------------------------------------------------------- +Configuration RID Lengths: Behavior Parameters + This is the length of JUST the DATA part of the RID (does not + include the len or code fields) +--------------------------------------------------------------------*/ +#define HFA384x_RID_ITICKTIME_LEN ((UINT16)2) + +/*---------------------------------------------------------------------- +Information RIDs: NIC Information +--------------------------------------------------------------------*/ +#define HFA384x_RID_MAXLOADTIME ((UINT16)0xFD00) +#define HFA384x_RID_DOWNLOADBUFFER ((UINT16)0xFD01) +#define HFA384x_RID_PRIIDENTITY ((UINT16)0xFD02) +#define HFA384x_RID_PRISUPRANGE ((UINT16)0xFD03) +#define HFA384x_RID_PRI_CFIACTRANGES ((UINT16)0xFD04) +#define HFA384x_RID_NICSERIALNUMBER ((UINT16)0xFD0A) +#define HFA384x_RID_NICIDENTITY ((UINT16)0xFD0B) +#define HFA384x_RID_MFISUPRANGE ((UINT16)0xFD0C) +#define HFA384x_RID_CFISUPRANGE ((UINT16)0xFD0D) +#define HFA384x_RID_CHANNELLIST ((UINT16)0xFD10) +#define HFA384x_RID_REGULATORYDOMAINS ((UINT16)0xFD11) +#define HFA384x_RID_TEMPTYPE ((UINT16)0xFD12) +#define HFA384x_RID_CIS ((UINT16)0xFD13) +#define HFA384x_RID_STAIDENTITY ((UINT16)0xFD20) +#define HFA384x_RID_STASUPRANGE ((UINT16)0xFD21) +#define HFA384x_RID_STA_MFIACTRANGES ((UINT16)0xFD22) +#define HFA384x_RID_STA_CFIACTRANGES ((UINT16)0xFD23) +#define HFA384x_RID_BUILDSEQ ((UINT16)0xFFFE) +#define HFA384x_RID_FWID ((UINT16)0xFFFF) + +/*---------------------------------------------------------------------- +Information RID Lengths: NIC Information + This is the length of JUST the DATA part of the RID (does not + include the len or code fields) +--------------------------------------------------------------------*/ +#define HFA384x_RID_MAXLOADTIME_LEN ((UINT16)0) +#define HFA384x_RID_DOWNLOADBUFFER_LEN ((UINT16)sizeof(hfa384x_downloadbuffer_t)) +#define HFA384x_RID_PRIIDENTITY_LEN ((UINT16)8) +#define HFA384x_RID_PRISUPRANGE_LEN ((UINT16)10) +#define HFA384x_RID_CFIACTRANGES_LEN ((UINT16)10) +#define HFA384x_RID_NICSERIALNUMBER_LEN ((UINT16)12) +#define HFA384x_RID_NICIDENTITY_LEN ((UINT16)8) +#define HFA384x_RID_MFISUPRANGE_LEN ((UINT16)10) +#define HFA384x_RID_CFISUPRANGE_LEN ((UINT16)10) +#define HFA384x_RID_CHANNELLIST_LEN ((UINT16)0) +#define HFA384x_RID_REGULATORYDOMAINS_LEN ((UINT16)12) +#define HFA384x_RID_TEMPTYPE_LEN ((UINT16)0) +#define HFA384x_RID_CIS_LEN ((UINT16)480) +#define HFA384x_RID_STAIDENTITY_LEN ((UINT16)8) +#define HFA384x_RID_STASUPRANGE_LEN ((UINT16)10) +#define HFA384x_RID_MFIACTRANGES_LEN ((UINT16)10) +#define HFA384x_RID_CFIACTRANGES2_LEN ((UINT16)10) +#define HFA384x_RID_BUILDSEQ_LEN ((UINT16)sizeof(hfa384x_BuildSeq_t)) +#define HFA384x_RID_FWID_LEN ((UINT16)sizeof(hfa384x_FWID_t)) + +/*-------------------------------------------------------------------- +Information RIDs: MAC Information +--------------------------------------------------------------------*/ +#define HFA384x_RID_PORTSTATUS ((UINT16)0xFD40) +#define HFA384x_RID_CURRENTSSID ((UINT16)0xFD41) +#define HFA384x_RID_CURRENTBSSID ((UINT16)0xFD42) +#define HFA384x_RID_COMMSQUALITY ((UINT16)0xFD43) +#define HFA384x_RID_CURRENTTXRATE ((UINT16)0xFD44) +#define HFA384x_RID_CURRENTBCNINT ((UINT16)0xFD45) +#define HFA384x_RID_CURRENTSCALETHRESH ((UINT16)0xFD46) +#define HFA384x_RID_PROTOCOLRSPTIME ((UINT16)0xFD47) +#define HFA384x_RID_SHORTRETRYLIMIT ((UINT16)0xFD48) +#define HFA384x_RID_LONGRETRYLIMIT ((UINT16)0xFD49) +#define HFA384x_RID_MAXTXLIFETIME ((UINT16)0xFD4A) +#define HFA384x_RID_MAXRXLIFETIME ((UINT16)0xFD4B) +#define HFA384x_RID_CFPOLLABLE ((UINT16)0xFD4C) +#define HFA384x_RID_AUTHALGORITHMS ((UINT16)0xFD4D) +#define HFA384x_RID_PRIVACYOPTIMP ((UINT16)0xFD4F) +#define HFA384x_RID_DBMCOMMSQUALITY ((UINT16)0xFD51) +#define HFA384x_RID_CURRENTTXRATE1 ((UINT16)0xFD80) +#define HFA384x_RID_CURRENTTXRATE2 ((UINT16)0xFD81) +#define HFA384x_RID_CURRENTTXRATE3 ((UINT16)0xFD82) +#define HFA384x_RID_CURRENTTXRATE4 ((UINT16)0xFD83) +#define HFA384x_RID_CURRENTTXRATE5 ((UINT16)0xFD84) +#define HFA384x_RID_CURRENTTXRATE6 ((UINT16)0xFD85) +#define HFA384x_RID_OWNMACADDRESS ((UINT16)0xFD86) +// #define HFA384x_RID_PCFINFO ((UINT16)0xFD87) +#define HFA384x_RID_SCANRESULTS ((UINT16)0xFD88) // NEW +#define HFA384x_RID_HOSTSCANRESULTS ((UINT16)0xFD89) // NEW +#define HFA384x_RID_AUTHENTICATIONUSED ((UINT16)0xFD8A) // NEW +#define HFA384x_RID_ASSOCIATEFAILURE ((UINT16)0xFD8D) // 1.8.0 + +/*-------------------------------------------------------------------- +Information RID Lengths: MAC Information + This is the length of JUST the DATA part of the RID (does not + include the len or code fields) +--------------------------------------------------------------------*/ +#define HFA384x_RID_PORTSTATUS_LEN ((UINT16)0) +#define HFA384x_RID_CURRENTSSID_LEN ((UINT16)34) +#define HFA384x_RID_CURRENTBSSID_LEN ((UINT16)WLAN_BSSID_LEN) +#define HFA384x_RID_COMMSQUALITY_LEN ((UINT16)sizeof(hfa384x_commsquality_t)) +#define HFA384x_RID_DBMCOMMSQUALITY_LEN ((UINT16)sizeof(hfa384x_dbmcommsquality_t)) +#define HFA384x_RID_CURRENTTXRATE_LEN ((UINT16)0) +#define HFA384x_RID_CURRENTBCNINT_LEN ((UINT16)0) +#define HFA384x_RID_STACURSCALETHRESH_LEN ((UINT16)12) +#define HFA384x_RID_APCURSCALETHRESH_LEN ((UINT16)6) +#define HFA384x_RID_PROTOCOLRSPTIME_LEN ((UINT16)0) +#define HFA384x_RID_SHORTRETRYLIMIT_LEN ((UINT16)0) +#define HFA384x_RID_LONGRETRYLIMIT_LEN ((UINT16)0) +#define HFA384x_RID_MAXTXLIFETIME_LEN ((UINT16)0) +#define HFA384x_RID_MAXRXLIFETIME_LEN ((UINT16)0) +#define HFA384x_RID_CFPOLLABLE_LEN ((UINT16)0) +#define HFA384x_RID_AUTHALGORITHMS_LEN ((UINT16)4) +#define HFA384x_RID_PRIVACYOPTIMP_LEN ((UINT16)0) +#define HFA384x_RID_CURRENTTXRATE1_LEN ((UINT16)0) +#define HFA384x_RID_CURRENTTXRATE2_LEN ((UINT16)0) +#define HFA384x_RID_CURRENTTXRATE3_LEN ((UINT16)0) +#define HFA384x_RID_CURRENTTXRATE4_LEN ((UINT16)0) +#define HFA384x_RID_CURRENTTXRATE5_LEN ((UINT16)0) +#define HFA384x_RID_CURRENTTXRATE6_LEN ((UINT16)0) +#define HFA384x_RID_OWNMACADDRESS_LEN ((UINT16)6) +#define HFA384x_RID_PCFINFO_LEN ((UINT16)6) +#define HFA384x_RID_CNFAPPCFINFO_LEN ((UINT16)sizeof(hfa384x_PCFInfo_data_t)) +#define HFA384x_RID_SCANREQUEST_LEN ((UINT16)sizeof(hfa384x_ScanRequest_data_t)) +#define HFA384x_RID_JOINREQUEST_LEN ((UINT16)sizeof(hfa384x_JoinRequest_data_t)) +#define HFA384x_RID_AUTHENTICATESTA_LEN ((UINT16)sizeof(hfa384x_authenticateStation_data_t)) +#define HFA384x_RID_CHANNELINFOREQUEST_LEN ((UINT16)sizeof(hfa384x_ChannelInfoRequest_data_t)) +/*-------------------------------------------------------------------- +Information RIDs: Modem Information +--------------------------------------------------------------------*/ +#define HFA384x_RID_PHYTYPE ((UINT16)0xFDC0) +#define HFA384x_RID_CURRENTCHANNEL ((UINT16)0xFDC1) +#define HFA384x_RID_CURRENTPOWERSTATE ((UINT16)0xFDC2) +#define HFA384x_RID_CCAMODE ((UINT16)0xFDC3) +#define HFA384x_RID_SUPPORTEDDATARATES ((UINT16)0xFDC6) +#define HFA384x_RID_LFOSTATUS ((UINT16)0xFDC7) // 1.7.1 + +/*-------------------------------------------------------------------- +Information RID Lengths: Modem Information + This is the length of JUST the DATA part of the RID (does not + include the len or code fields) +--------------------------------------------------------------------*/ +#define HFA384x_RID_PHYTYPE_LEN ((UINT16)0) +#define HFA384x_RID_CURRENTCHANNEL_LEN ((UINT16)0) +#define HFA384x_RID_CURRENTPOWERSTATE_LEN ((UINT16)0) +#define HFA384x_RID_CCAMODE_LEN ((UINT16)0) +#define HFA384x_RID_SUPPORTEDDATARATES_LEN ((UINT16)10) + +/*-------------------------------------------------------------------- +API ENHANCEMENTS (NOT ALREADY IMPLEMENTED) +--------------------------------------------------------------------*/ +#define HFA384x_RID_CNFWEPDEFAULTKEYID ((UINT16)0xFC23) +#define HFA384x_RID_CNFWEPDEFAULTKEY0 ((UINT16)0xFC24) +#define HFA384x_RID_CNFWEPDEFAULTKEY1 ((UINT16)0xFC25) +#define HFA384x_RID_CNFWEPDEFAULTKEY2 ((UINT16)0xFC26) +#define HFA384x_RID_CNFWEPDEFAULTKEY3 ((UINT16)0xFC27) +#define HFA384x_RID_CNFWEPFLAGS ((UINT16)0xFC28) +#define HFA384x_RID_CNFWEPKEYMAPTABLE ((UINT16)0xFC29) +#define HFA384x_RID_CNFAUTHENTICATION ((UINT16)0xFC2A) +#define HFA384x_RID_CNFMAXASSOCSTATIONS ((UINT16)0xFC2B) +#define HFA384x_RID_CNFTXCONTROL ((UINT16)0xFC2C) +#define HFA384x_RID_CNFROAMINGMODE ((UINT16)0xFC2D) +#define HFA384x_RID_CNFHOSTAUTHASSOC ((UINT16)0xFC2E) +#define HFA384x_RID_CNFRCVCRCERROR ((UINT16)0xFC30) +// #define HFA384x_RID_CNFMMLIFE ((UINT16)0xFC31) +#define HFA384x_RID_CNFALTRETRYCNT ((UINT16)0xFC32) +#define HFA384x_RID_CNFAPBCNINT ((UINT16)0xFC33) +#define HFA384x_RID_CNFAPPCFINFO ((UINT16)0xFC34) +#define HFA384x_RID_CNFSTAPCFINFO ((UINT16)0xFC35) +#define HFA384x_RID_CNFPRIORITYQUSAGE ((UINT16)0xFC37) +#define HFA384x_RID_CNFTIMCTRL ((UINT16)0xFC40) +#define HFA384x_RID_CNFTHIRTY2TALLY ((UINT16)0xFC42) +#define HFA384x_RID_CNFENHSECURITY ((UINT16)0xFC43) +#define HFA384x_RID_CNFDBMADJUST ((UINT16)0xFC46) // NEW +#define HFA384x_RID_CNFWPADATA ((UINT16)0xFC48) // 1.7.0 +#define HFA384x_RID_CNFPROPOGATIONDELAY ((UINT16)0xFC49) // 1.7.6 +#define HFA384x_RID_CNFSHORTPREAMBLE ((UINT16)0xFCB0) +#define HFA384x_RID_CNFEXCLONGPREAMBLE ((UINT16)0xFCB1) +#define HFA384x_RID_CNFAUTHRSPTIMEOUT ((UINT16)0xFCB2) +#define HFA384x_RID_CNFBASICRATES ((UINT16)0xFCB3) +#define HFA384x_RID_CNFSUPPRATES ((UINT16)0xFCB4) +#define HFA384x_RID_CNFFALLBACKCTRL ((UINT16)0xFCB5) // NEW +#define HFA384x_RID_WEPKEYSTATUS ((UINT16)0xFCB6) // NEW +#define HFA384x_RID_WEPKEYMAPINDEX ((UINT16)0xFCB7) // NEW +#define HFA384x_RID_BROADCASTKEYID ((UINT16)0xFCB8) // NEW +#define HFA384x_RID_ENTSECFLAGEYID ((UINT16)0xFCB9) // NEW +#define HFA384x_RID_CNFPASSIVESCANCTRL ((UINT16)0xFCBA) // NEW STA +#define HFA384x_RID_CNFWPAHANDLING ((UINT16)0xFCBB) // 1.7.0 +#define HFA384x_RID_MDCCONTROL ((UINT16)0xFCBC) // 1.7.0/1.4.0 +#define HFA384x_RID_MDCCOUNTRY ((UINT16)0xFCBD) // 1.7.0/1.4.0 +#define HFA384x_RID_TXPOWERMAX ((UINT16)0xFCBE) // 1.7.0/1.4.0 +#define HFA384x_RID_CNFLFOENBLED ((UINT16)0xFCBF) // 1.6.3 +#define HFA384x_RID_CAPINFO ((UINT16)0xFCC0) // 1.7.0/1.3.7 +#define HFA384x_RID_LISTENINTERVAL ((UINT16)0xFCC1) // 1.7.0/1.3.7 +#define HFA384x_RID_DIVERSITYENABLED ((UINT16)0xFCC2) // 1.7.0/1.3.7 +#define HFA384x_RID_LED_CONTROL ((UINT16)0xFCC4) // 1.7.6 +#define HFA384x_RID_HFO_DELAY ((UINT16)0xFCC5) // 1.7.6 +#define HFA384x_RID_DISSALOWEDBSSID ((UINT16)0xFCC6) // 1.8.0 +#define HFA384x_RID_SCANREQUEST ((UINT16)0xFCE1) +#define HFA384x_RID_JOINREQUEST ((UINT16)0xFCE2) +#define HFA384x_RID_AUTHENTICATESTA ((UINT16)0xFCE3) +#define HFA384x_RID_CHANNELINFOREQUEST ((UINT16)0xFCE4) +#define HFA384x_RID_HOSTSCAN ((UINT16)0xFCE5) // NEW STA +#define HFA384x_RID_ASSOCIATESTA ((UINT16)0xFCE6) + +#define HFA384x_RID_CNFWEPDEFAULTKEY_LEN ((UINT16)6) +#define HFA384x_RID_CNFWEP128DEFAULTKEY_LEN ((UINT16)14) +#define HFA384x_RID_CNFPRIOQUSAGE_LEN ((UINT16)4) +/*-------------------------------------------------------------------- +PD Record codes +--------------------------------------------------------------------*/ +#define HFA384x_PDR_PCB_PARTNUM ((UINT16)0x0001) +#define HFA384x_PDR_PDAVER ((UINT16)0x0002) +#define HFA384x_PDR_NIC_SERIAL ((UINT16)0x0003) +#define HFA384x_PDR_MKK_MEASUREMENTS ((UINT16)0x0004) +#define HFA384x_PDR_NIC_RAMSIZE ((UINT16)0x0005) +#define HFA384x_PDR_MFISUPRANGE ((UINT16)0x0006) +#define HFA384x_PDR_CFISUPRANGE ((UINT16)0x0007) +#define HFA384x_PDR_NICID ((UINT16)0x0008) +//#define HFA384x_PDR_REFDAC_MEASUREMENTS ((UINT16)0x0010) +//#define HFA384x_PDR_VGDAC_MEASUREMENTS ((UINT16)0x0020) +//#define HFA384x_PDR_LEVEL_COMP_MEASUREMENTS ((UINT16)0x0030) +//#define HFA384x_PDR_MODEM_TRIMDAC_MEASUREMENTS ((UINT16)0x0040) +//#define HFA384x_PDR_COREGA_HACK ((UINT16)0x00ff) +#define HFA384x_PDR_MAC_ADDRESS ((UINT16)0x0101) +//#define HFA384x_PDR_MKK_CALLNAME ((UINT16)0x0102) +#define HFA384x_PDR_REGDOMAIN ((UINT16)0x0103) +#define HFA384x_PDR_ALLOWED_CHANNEL ((UINT16)0x0104) +#define HFA384x_PDR_DEFAULT_CHANNEL ((UINT16)0x0105) +//#define HFA384x_PDR_PRIVACY_OPTION ((UINT16)0x0106) +#define HFA384x_PDR_TEMPTYPE ((UINT16)0x0107) +//#define HFA384x_PDR_REFDAC_SETUP ((UINT16)0x0110) +//#define HFA384x_PDR_VGDAC_SETUP ((UINT16)0x0120) +//#define HFA384x_PDR_LEVEL_COMP_SETUP ((UINT16)0x0130) +//#define HFA384x_PDR_TRIMDAC_SETUP ((UINT16)0x0140) +#define HFA384x_PDR_IFR_SETTING ((UINT16)0x0200) +#define HFA384x_PDR_RFR_SETTING ((UINT16)0x0201) +#define HFA384x_PDR_HFA3861_BASELINE ((UINT16)0x0202) +#define HFA384x_PDR_HFA3861_SHADOW ((UINT16)0x0203) +#define HFA384x_PDR_HFA3861_IFRF ((UINT16)0x0204) +#define HFA384x_PDR_HFA3861_CHCALSP ((UINT16)0x0300) +#define HFA384x_PDR_HFA3861_CHCALI ((UINT16)0x0301) +#define HFA384x_PDR_MAX_TX_POWER ((UINT16)0x0302) +#define HFA384x_PDR_MASTER_CHAN_LIST ((UINT16)0x0303) +#define HFA384x_PDR_3842_NIC_CONFIG ((UINT16)0x0400) +#define HFA384x_PDR_USB_ID ((UINT16)0x0401) +#define HFA384x_PDR_PCI_ID ((UINT16)0x0402) +#define HFA384x_PDR_PCI_IFCONF ((UINT16)0x0403) +#define HFA384x_PDR_PCI_PMCONF ((UINT16)0x0404) +#define HFA384x_PDR_RFENRGY ((UINT16)0x0406) +#define HFA384x_PDR_USB_POWER_TYPE ((UINT16)0x0407) +//#define HFA384x_PDR_UNKNOWN408 ((UINT16)0x0408) +#define HFA384x_PDR_USB_MAX_POWER ((UINT16)0x0409) +#define HFA384x_PDR_USB_MANUFACTURER ((UINT16)0x0410) +#define HFA384x_PDR_USB_PRODUCT ((UINT16)0x0411) +#define HFA384x_PDR_ANT_DIVERSITY ((UINT16)0x0412) +#define HFA384x_PDR_HFO_DELAY ((UINT16)0x0413) +#define HFA384x_PDR_SCALE_THRESH ((UINT16)0x0414) + +#define HFA384x_PDR_HFA3861_MANF_TESTSP ((UINT16)0x0900) +#define HFA384x_PDR_HFA3861_MANF_TESTI ((UINT16)0x0901) +#define HFA384x_PDR_END_OF_PDA ((UINT16)0x0000) + + +/*=============================================================*/ +/*------ Macros -----------------------------------------------*/ + +/*--- Register ID macros ------------------------*/ + +#define HFA384x_CMD HFA384x_CMD_OFF +#define HFA384x_PARAM0 HFA384x_PARAM0_OFF +#define HFA384x_PARAM1 HFA384x_PARAM1_OFF +#define HFA384x_PARAM2 HFA384x_PARAM2_OFF +#define HFA384x_STATUS HFA384x_STATUS_OFF +#define HFA384x_RESP0 HFA384x_RESP0_OFF +#define HFA384x_RESP1 HFA384x_RESP1_OFF +#define HFA384x_RESP2 HFA384x_RESP2_OFF +#define HFA384x_INFOFID HFA384x_INFOFID_OFF +#define HFA384x_RXFID HFA384x_RXFID_OFF +#define HFA384x_ALLOCFID HFA384x_ALLOCFID_OFF +#define HFA384x_TXCOMPLFID HFA384x_TXCOMPLFID_OFF +#define HFA384x_SELECT0 HFA384x_SELECT0_OFF +#define HFA384x_OFFSET0 HFA384x_OFFSET0_OFF +#define HFA384x_DATA0 HFA384x_DATA0_OFF +#define HFA384x_SELECT1 HFA384x_SELECT1_OFF +#define HFA384x_OFFSET1 HFA384x_OFFSET1_OFF +#define HFA384x_DATA1 HFA384x_DATA1_OFF +#define HFA384x_EVSTAT HFA384x_EVSTAT_OFF +#define HFA384x_INTEN HFA384x_INTEN_OFF +#define HFA384x_EVACK HFA384x_EVACK_OFF +#define HFA384x_CONTROL HFA384x_CONTROL_OFF +#define HFA384x_SWSUPPORT0 HFA384x_SWSUPPORT0_OFF +#define HFA384x_SWSUPPORT1 HFA384x_SWSUPPORT1_OFF +#define HFA384x_SWSUPPORT2 HFA384x_SWSUPPORT2_OFF +#define HFA384x_AUXPAGE HFA384x_AUXPAGE_OFF +#define HFA384x_AUXOFFSET HFA384x_AUXOFFSET_OFF +#define HFA384x_AUXDATA HFA384x_AUXDATA_OFF +#define HFA384x_PCICOR HFA384x_PCICOR_OFF +#define HFA384x_PCIHCR HFA384x_PCIHCR_OFF + + +/*--- Register Test/Get/Set Field macros ------------------------*/ + +#define HFA384x_CMD_ISBUSY(value) ((UINT16)(((UINT16)value) & HFA384x_CMD_BUSY)) +#define HFA384x_CMD_AINFO_GET(value) ((UINT16)(((UINT16)(value) & HFA384x_CMD_AINFO) >> 8)) +#define HFA384x_CMD_AINFO_SET(value) ((UINT16)((UINT16)(value) << 8)) +#define HFA384x_CMD_MACPORT_GET(value) ((UINT16)(HFA384x_CMD_AINFO_GET((UINT16)(value) & HFA384x_CMD_MACPORT))) +#define HFA384x_CMD_MACPORT_SET(value) ((UINT16)HFA384x_CMD_AINFO_SET(value)) +#define HFA384x_CMD_ISRECL(value) ((UINT16)(HFA384x_CMD_AINFO_GET((UINT16)(value) & HFA384x_CMD_RECL))) +#define HFA384x_CMD_RECL_SET(value) ((UINT16)HFA384x_CMD_AINFO_SET(value)) +#define HFA384x_CMD_QOS_GET(value) ((UINT16((((UINT16)(value))&((UINT16)0x3000)) >> 12)) +#define HFA384x_CMD_QOS_SET(value) ((UINT16)((((UINT16)(value)) << 12) & 0x3000)) +#define HFA384x_CMD_ISWRITE(value) ((UINT16)(HFA384x_CMD_AINFO_GET((UINT16)(value) & HFA384x_CMD_WRITE))) +#define HFA384x_CMD_WRITE_SET(value) ((UINT16)HFA384x_CMD_AINFO_SET((UINT16)value)) +#define HFA384x_CMD_PROGMODE_GET(value) ((UINT16)(HFA384x_CMD_AINFO_GET((UINT16)(value) & HFA384x_CMD_PROGMODE))) +#define HFA384x_CMD_PROGMODE_SET(value) ((UINT16)HFA384x_CMD_AINFO_SET((UINT16)value)) +#define HFA384x_CMD_CMDCODE_GET(value) ((UINT16)(((UINT16)(value)) & HFA384x_CMD_CMDCODE)) +#define HFA384x_CMD_CMDCODE_SET(value) ((UINT16)(value)) + +#define HFA384x_STATUS_RESULT_GET(value) ((UINT16)((((UINT16)(value)) & HFA384x_STATUS_RESULT) >> 8)) +#define HFA384x_STATUS_RESULT_SET(value) (((UINT16)(value)) << 8) +#define HFA384x_STATUS_CMDCODE_GET(value) (((UINT16)(value)) & HFA384x_STATUS_CMDCODE) +#define HFA384x_STATUS_CMDCODE_SET(value) ((UINT16)(value)) + +#define HFA384x_OFFSET_ISBUSY(value) ((UINT16)(((UINT16)(value)) & HFA384x_OFFSET_BUSY)) +#define HFA384x_OFFSET_ISERR(value) ((UINT16)(((UINT16)(value)) & HFA384x_OFFSET_ERR)) +#define HFA384x_OFFSET_DATAOFF_GET(value) ((UINT16)(((UINT16)(value)) & HFA384x_OFFSET_DATAOFF)) +#define HFA384x_OFFSET_DATAOFF_SET(value) ((UINT16)(value)) + +#define HFA384x_EVSTAT_ISTICK(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_TICK)) +#define HFA384x_EVSTAT_ISWTERR(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_WTERR)) +#define HFA384x_EVSTAT_ISINFDROP(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_INFDROP)) +#define HFA384x_EVSTAT_ISINFO(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_INFO)) +#define HFA384x_EVSTAT_ISDTIM(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_DTIM)) +#define HFA384x_EVSTAT_ISCMD(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_CMD)) +#define HFA384x_EVSTAT_ISALLOC(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_ALLOC)) +#define HFA384x_EVSTAT_ISTXEXC(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_TXEXC)) +#define HFA384x_EVSTAT_ISTX(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_TX)) +#define HFA384x_EVSTAT_ISRX(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVSTAT_RX)) + +#define HFA384x_EVSTAT_ISBAP_OP(value) ((UINT16)(((UINT16)(value)) & HFA384x_INT_BAP_OP)) + +#define HFA384x_INTEN_ISTICK(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_TICK)) +#define HFA384x_INTEN_TICK_SET(value) ((UINT16)(((UINT16)(value)) << 15)) +#define HFA384x_INTEN_ISWTERR(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_WTERR)) +#define HFA384x_INTEN_WTERR_SET(value) ((UINT16)(((UINT16)(value)) << 14)) +#define HFA384x_INTEN_ISINFDROP(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_INFDROP)) +#define HFA384x_INTEN_INFDROP_SET(value) ((UINT16)(((UINT16)(value)) << 13)) +#define HFA384x_INTEN_ISINFO(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_INFO)) +#define HFA384x_INTEN_INFO_SET(value) ((UINT16)(((UINT16)(value)) << 7)) +#define HFA384x_INTEN_ISDTIM(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_DTIM)) +#define HFA384x_INTEN_DTIM_SET(value) ((UINT16)(((UINT16)(value)) << 5)) +#define HFA384x_INTEN_ISCMD(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_CMD)) +#define HFA384x_INTEN_CMD_SET(value) ((UINT16)(((UINT16)(value)) << 4)) +#define HFA384x_INTEN_ISALLOC(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_ALLOC)) +#define HFA384x_INTEN_ALLOC_SET(value) ((UINT16)(((UINT16)(value)) << 3)) +#define HFA384x_INTEN_ISTXEXC(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_TXEXC)) +#define HFA384x_INTEN_TXEXC_SET(value) ((UINT16)(((UINT16)(value)) << 2)) +#define HFA384x_INTEN_ISTX(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_TX)) +#define HFA384x_INTEN_TX_SET(value) ((UINT16)(((UINT16)(value)) << 1)) +#define HFA384x_INTEN_ISRX(value) ((UINT16)(((UINT16)(value)) & HFA384x_INTEN_RX)) +#define HFA384x_INTEN_RX_SET(value) ((UINT16)(((UINT16)(value)) << 0)) + +#define HFA384x_EVACK_ISTICK(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_TICK)) +#define HFA384x_EVACK_TICK_SET(value) ((UINT16)(((UINT16)(value)) << 15)) +#define HFA384x_EVACK_ISWTERR(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_WTERR)) +#define HFA384x_EVACK_WTERR_SET(value) ((UINT16)(((UINT16)(value)) << 14)) +#define HFA384x_EVACK_ISINFDROP(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_INFDROP)) +#define HFA384x_EVACK_INFDROP_SET(value) ((UINT16)(((UINT16)(value)) << 13)) +#define HFA384x_EVACK_ISINFO(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_INFO)) +#define HFA384x_EVACK_INFO_SET(value) ((UINT16)(((UINT16)(value)) << 7)) +#define HFA384x_EVACK_ISDTIM(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_DTIM)) +#define HFA384x_EVACK_DTIM_SET(value) ((UINT16)(((UINT16)(value)) << 5)) +#define HFA384x_EVACK_ISCMD(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_CMD)) +#define HFA384x_EVACK_CMD_SET(value) ((UINT16)(((UINT16)(value)) << 4)) +#define HFA384x_EVACK_ISALLOC(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_ALLOC)) +#define HFA384x_EVACK_ALLOC_SET(value) ((UINT16)(((UINT16)(value)) << 3)) +#define HFA384x_EVACK_ISTXEXC(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_TXEXC)) +#define HFA384x_EVACK_TXEXC_SET(value) ((UINT16)(((UINT16)(value)) << 2)) +#define HFA384x_EVACK_ISTX(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_TX)) +#define HFA384x_EVACK_TX_SET(value) ((UINT16)(((UINT16)(value)) << 1)) +#define HFA384x_EVACK_ISRX(value) ((UINT16)(((UINT16)(value)) & HFA384x_EVACK_RX)) +#define HFA384x_EVACK_RX_SET(value) ((UINT16)(((UINT16)(value)) << 0)) + +#define HFA384x_CONTROL_AUXEN_SET(value) ((UINT16)(((UINT16)(value)) << 14)) +#define HFA384x_CONTROL_AUXEN_GET(value) ((UINT16)(((UINT16)(value)) >> 14)) + +/* Byte Order */ +#ifdef __KERNEL__ +#define hfa384x2host_16(n) (__le16_to_cpu((UINT16)(n))) +#define hfa384x2host_32(n) (__le32_to_cpu((UINT32)(n))) +#define host2hfa384x_16(n) (__cpu_to_le16((UINT16)(n))) +#define host2hfa384x_32(n) (__cpu_to_le32((UINT32)(n))) +#endif + +/* Host Maintained State Info */ +#define HFA384x_STATE_PREINIT 0 +#define HFA384x_STATE_INIT 1 +#define HFA384x_STATE_RUNNING 2 + +/*=============================================================*/ +/*------ Types and their related constants --------------------*/ + +#define HFA384x_HOSTAUTHASSOC_HOSTAUTH BIT0 +#define HFA384x_HOSTAUTHASSOC_HOSTASSOC BIT1 + +#define HFA384x_WHAHANDLING_DISABLED 0 +#define HFA384x_WHAHANDLING_PASSTHROUGH BIT1 + +/*-------------------------------------------------------------*/ +/* Commonly used basic types */ +typedef struct hfa384x_bytestr +{ + UINT16 len; + UINT8 data[0]; +} __WLAN_ATTRIB_PACK__ hfa384x_bytestr_t; + +typedef struct hfa384x_bytestr32 +{ + UINT16 len; + UINT8 data[32]; +} __WLAN_ATTRIB_PACK__ hfa384x_bytestr32_t; + +/*-------------------------------------------------------------------- +Configuration Record Structures: + Network Parameters, Static Configuration Entities +--------------------------------------------------------------------*/ +/* Prototype structure: all configuration record structures start with +these members */ + +typedef struct hfa384x_record +{ + UINT16 reclen; + UINT16 rid; +} __WLAN_ATTRIB_PACK__ hfa384x_rec_t; + +typedef struct hfa384x_record16 +{ + UINT16 reclen; + UINT16 rid; + UINT16 val; +} __WLAN_ATTRIB_PACK__ hfa384x_rec16_t; + +typedef struct hfa384x_record32 +{ + UINT16 reclen; + UINT16 rid; + UINT32 val; +} __WLAN_ATTRIB_PACK__ hfa384x_rec32; + +/*-- Hardware/Firmware Component Information ----------*/ +typedef struct hfa384x_compident +{ + UINT16 id; + UINT16 variant; + UINT16 major; + UINT16 minor; +} __WLAN_ATTRIB_PACK__ hfa384x_compident_t; + +typedef struct hfa384x_caplevel +{ + UINT16 role; + UINT16 id; + UINT16 variant; + UINT16 bottom; + UINT16 top; +} __WLAN_ATTRIB_PACK__ hfa384x_caplevel_t; + +/*-- Configuration Record: cnfPortType --*/ +typedef struct hfa384x_cnfPortType +{ + UINT16 cnfPortType; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfPortType_t; + +/*-- Configuration Record: cnfOwnMACAddress --*/ +typedef struct hfa384x_cnfOwnMACAddress +{ + UINT8 cnfOwnMACAddress[6]; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnMACAddress_t; + +/*-- Configuration Record: cnfDesiredSSID --*/ +typedef struct hfa384x_cnfDesiredSSID +{ + UINT8 cnfDesiredSSID[34]; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfDesiredSSID_t; + +/*-- Configuration Record: cnfOwnChannel --*/ +typedef struct hfa384x_cnfOwnChannel +{ + UINT16 cnfOwnChannel; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnChannel_t; + +/*-- Configuration Record: cnfOwnSSID --*/ +typedef struct hfa384x_cnfOwnSSID +{ + UINT8 cnfOwnSSID[34]; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnSSID_t; + +/*-- Configuration Record: cnfOwnATIMWindow --*/ +typedef struct hfa384x_cnfOwnATIMWindow +{ + UINT16 cnfOwnATIMWindow; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnATIMWindow_t; + +/*-- Configuration Record: cnfSystemScale --*/ +typedef struct hfa384x_cnfSystemScale +{ + UINT16 cnfSystemScale; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfSystemScale_t; + +/*-- Configuration Record: cnfMaxDataLength --*/ +typedef struct hfa384x_cnfMaxDataLength +{ + UINT16 cnfMaxDataLength; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfMaxDataLength_t; + +/*-- Configuration Record: cnfWDSAddress --*/ +typedef struct hfa384x_cnfWDSAddress +{ + UINT8 cnfWDSAddress[6]; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfWDSAddress_t; + +/*-- Configuration Record: cnfPMEnabled --*/ +typedef struct hfa384x_cnfPMEnabled +{ + UINT16 cnfPMEnabled; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfPMEnabled_t; + +/*-- Configuration Record: cnfPMEPS --*/ +typedef struct hfa384x_cnfPMEPS +{ + UINT16 cnfPMEPS; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfPMEPS_t; + +/*-- Configuration Record: cnfMulticastReceive --*/ +typedef struct hfa384x_cnfMulticastReceive +{ + UINT16 cnfMulticastReceive; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfMulticastReceive_t; + +/*-- Configuration Record: cnfAuthentication --*/ +#define HFA384x_CNFAUTHENTICATION_OPENSYSTEM 0x0001 +#define HFA384x_CNFAUTHENTICATION_SHAREDKEY 0x0002 +#define HFA384x_CNFAUTHENTICATION_LEAP 0x0004 + +/*-- Configuration Record: cnfMaxSleepDuration --*/ +typedef struct hfa384x_cnfMaxSleepDuration +{ + UINT16 cnfMaxSleepDuration; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfMaxSleepDuration_t; + +/*-- Configuration Record: cnfPMHoldoverDuration --*/ +typedef struct hfa384x_cnfPMHoldoverDuration +{ + UINT16 cnfPMHoldoverDuration; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfPMHoldoverDuration_t; + +/*-- Configuration Record: cnfOwnName --*/ +typedef struct hfa384x_cnfOwnName +{ + UINT8 cnfOwnName[34]; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnName_t; + +/*-- Configuration Record: cnfOwnDTIMPeriod --*/ +typedef struct hfa384x_cnfOwnDTIMPeriod +{ + UINT16 cnfOwnDTIMPeriod; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfOwnDTIMPeriod_t; + +/*-- Configuration Record: cnfWDSAddress --*/ +typedef struct hfa384x_cnfWDSAddressN +{ + UINT8 cnfWDSAddress[6]; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfWDSAddressN_t; + +/*-- Configuration Record: cnfMulticastPMBuffering --*/ +typedef struct hfa384x_cnfMulticastPMBuffering +{ + UINT16 cnfMulticastPMBuffering; +} __WLAN_ATTRIB_PACK__ hfa384x_cnfMulticastPMBuffering_t; + +/*-------------------------------------------------------------------- +Configuration Record Structures: + Network Parameters, Dynamic Configuration Entities +--------------------------------------------------------------------*/ + +/*-- Configuration Record: GroupAddresses --*/ +typedef struct hfa384x_GroupAddresses +{ + UINT8 MACAddress[16][6]; +} __WLAN_ATTRIB_PACK__ hfa384x_GroupAddresses_t; + +/*-- Configuration Record: CreateIBSS --*/ +typedef struct hfa384x_CreateIBSS +{ + UINT16 CreateIBSS; +} __WLAN_ATTRIB_PACK__ hfa384x_CreateIBSS_t; + +#define HFA384x_CREATEIBSS_JOINCREATEIBSS 0 +#define HFA384x_CREATEIBSS_JOINESS_JOINCREATEIBSS 1 +#define HFA384x_CREATEIBSS_JOINIBSS 2 +#define HFA384x_CREATEIBSS_JOINESS_JOINIBSS 3 + +/*-- Configuration Record: FragmentationThreshold --*/ +typedef struct hfa384x_FragmentationThreshold +{ + UINT16 FragmentationThreshold; +} __WLAN_ATTRIB_PACK__ hfa384x_FragmentationThreshold_t; + +/*-- Configuration Record: RTSThreshold --*/ +typedef struct hfa384x_RTSThreshold +{ + UINT16 RTSThreshold; +} __WLAN_ATTRIB_PACK__ hfa384x_RTSThreshold_t; + +/*-- Configuration Record: TxRateControl --*/ +typedef struct hfa384x_TxRateControl +{ + UINT16 TxRateControl; +} __WLAN_ATTRIB_PACK__ hfa384x_TxRateControl_t; + +/*-- Configuration Record: PromiscuousMode --*/ +typedef struct hfa384x_PromiscuousMode +{ + UINT16 PromiscuousMode; +} __WLAN_ATTRIB_PACK__ hfa384x_PromiscuousMode_t; + +/*-- Configuration Record: ScanRequest (data portion only) --*/ +typedef struct hfa384x_ScanRequest_data +{ + UINT16 channelList; + UINT16 txRate; +} __WLAN_ATTRIB_PACK__ hfa384x_ScanRequest_data_t; + +/*-- Configuration Record: HostScanRequest (data portion only) --*/ +typedef struct hfa384x_HostScanRequest_data +{ + UINT16 channelList; + UINT16 txRate; + hfa384x_bytestr32_t ssid; +} __WLAN_ATTRIB_PACK__ hfa384x_HostScanRequest_data_t; + +/*-- Configuration Record: JoinRequest (data portion only) --*/ +typedef struct hfa384x_JoinRequest_data +{ + UINT8 bssid[WLAN_BSSID_LEN]; + UINT16 channel; +} __WLAN_ATTRIB_PACK__ hfa384x_JoinRequest_data_t; + +/*-- Configuration Record: authenticateStation (data portion only) --*/ +typedef struct hfa384x_authenticateStation_data +{ + UINT8 address[WLAN_ADDR_LEN]; + UINT16 status; + UINT16 algorithm; +} __WLAN_ATTRIB_PACK__ hfa384x_authenticateStation_data_t; + +/*-- Configuration Record: associateStation (data portion only) --*/ +typedef struct hfa384x_associateStation_data +{ + UINT8 address[WLAN_ADDR_LEN]; + UINT16 status; + UINT16 type; +} __WLAN_ATTRIB_PACK__ hfa384x_associateStation_data_t; + +/*-- Configuration Record: ChannelInfoRequest (data portion only) --*/ +typedef struct hfa384x_ChannelInfoRequest_data +{ + UINT16 channelList; + UINT16 channelDwellTime; +} __WLAN_ATTRIB_PACK__ hfa384x_ChannelInfoRequest_data_t; + +/*-- Configuration Record: WEPKeyMapping (data portion only) --*/ +typedef struct hfa384x_WEPKeyMapping +{ + UINT8 address[WLAN_ADDR_LEN]; + UINT16 key_index; + UINT8 key[16]; + UINT8 mic_transmit_key[4]; + UINT8 mic_receive_key[4]; +} __WLAN_ATTRIB_PACK__ hfa384x_WEPKeyMapping_t; + +/*-- Configuration Record: WPAData (data portion only) --*/ +typedef struct hfa384x_WPAData +{ + UINT16 datalen; + UINT8 data[0]; // max 80 +} __WLAN_ATTRIB_PACK__ hfa384x_WPAData_t; + +/*-------------------------------------------------------------------- +Configuration Record Structures: Behavior Parameters +--------------------------------------------------------------------*/ + +/*-- Configuration Record: TickTime --*/ +typedef struct hfa384x_TickTime +{ + UINT16 TickTime; +} __WLAN_ATTRIB_PACK__ hfa384x_TickTime_t; + +/*-------------------------------------------------------------------- +Information Record Structures: NIC Information +--------------------------------------------------------------------*/ + +/*-- Information Record: MaxLoadTime --*/ +typedef struct hfa384x_MaxLoadTime +{ + UINT16 MaxLoadTime; +} __WLAN_ATTRIB_PACK__ hfa384x_MaxLoadTime_t; + +/*-- Information Record: DownLoadBuffer --*/ +/* NOTE: The page and offset are in AUX format */ +typedef struct hfa384x_downloadbuffer +{ + UINT16 page; + UINT16 offset; + UINT16 len; +} __WLAN_ATTRIB_PACK__ hfa384x_downloadbuffer_t; + +/*-- Information Record: PRIIdentity --*/ +typedef struct hfa384x_PRIIdentity +{ + UINT16 PRICompID; + UINT16 PRIVariant; + UINT16 PRIMajorVersion; + UINT16 PRIMinorVersion; +} __WLAN_ATTRIB_PACK__ hfa384x_PRIIdentity_t; + +/*-- Information Record: PRISupRange --*/ +typedef struct hfa384x_PRISupRange +{ + UINT16 PRIRole; + UINT16 PRIID; + UINT16 PRIVariant; + UINT16 PRIBottom; + UINT16 PRITop; +} __WLAN_ATTRIB_PACK__ hfa384x_PRISupRange_t; + +/*-- Information Record: CFIActRanges --*/ +typedef struct hfa384x_CFIActRanges +{ + UINT16 CFIRole; + UINT16 CFIID; + UINT16 CFIVariant; + UINT16 CFIBottom; + UINT16 CFITop; +} __WLAN_ATTRIB_PACK__ hfa384x_CFIActRanges_t; + +/*-- Information Record: NICSerialNumber --*/ +typedef struct hfa384x_NICSerialNumber +{ + UINT8 NICSerialNumber[12]; +} __WLAN_ATTRIB_PACK__ hfa384x_NICSerialNumber_t; + +/*-- Information Record: NICIdentity --*/ +typedef struct hfa384x_NICIdentity +{ + UINT16 NICCompID; + UINT16 NICVariant; + UINT16 NICMajorVersion; + UINT16 NICMinorVersion; +} __WLAN_ATTRIB_PACK__ hfa384x_NICIdentity_t; + +/*-- Information Record: MFISupRange --*/ +typedef struct hfa384x_MFISupRange +{ + UINT16 MFIRole; + UINT16 MFIID; + UINT16 MFIVariant; + UINT16 MFIBottom; + UINT16 MFITop; +} __WLAN_ATTRIB_PACK__ hfa384x_MFISupRange_t; + +/*-- Information Record: CFISupRange --*/ +typedef struct hfa384x_CFISupRange +{ + UINT16 CFIRole; + UINT16 CFIID; + UINT16 CFIVariant; + UINT16 CFIBottom; + UINT16 CFITop; +} __WLAN_ATTRIB_PACK__ hfa384x_CFISupRange_t; + +/*-- Information Record: BUILDSEQ:BuildSeq --*/ +typedef struct hfa384x_BuildSeq { + UINT16 primary; + UINT16 secondary; +} __WLAN_ATTRIB_PACK__ hfa384x_BuildSeq_t; + +/*-- Information Record: FWID --*/ +#define HFA384x_FWID_LEN 14 +typedef struct hfa384x_FWID { + UINT8 primary[HFA384x_FWID_LEN]; + UINT8 secondary[HFA384x_FWID_LEN]; +} __WLAN_ATTRIB_PACK__ hfa384x_FWID_t; + +/*-- Information Record: ChannelList --*/ +typedef struct hfa384x_ChannelList +{ + UINT16 ChannelList; +} __WLAN_ATTRIB_PACK__ hfa384x_ChannelList_t; + +/*-- Information Record: RegulatoryDomains --*/ +typedef struct hfa384x_RegulatoryDomains +{ + UINT8 RegulatoryDomains[12]; +} __WLAN_ATTRIB_PACK__ hfa384x_RegulatoryDomains_t; + +/*-- Information Record: TempType --*/ +typedef struct hfa384x_TempType +{ + UINT16 TempType; +} __WLAN_ATTRIB_PACK__ hfa384x_TempType_t; + +/*-- Information Record: CIS --*/ +typedef struct hfa384x_CIS +{ + UINT8 CIS[480]; +} __WLAN_ATTRIB_PACK__ hfa384x_CIS_t; + +/*-- Information Record: STAIdentity --*/ +typedef struct hfa384x_STAIdentity +{ + UINT16 STACompID; + UINT16 STAVariant; + UINT16 STAMajorVersion; + UINT16 STAMinorVersion; +} __WLAN_ATTRIB_PACK__ hfa384x_STAIdentity_t; + +/*-- Information Record: STASupRange --*/ +typedef struct hfa384x_STASupRange +{ + UINT16 STARole; + UINT16 STAID; + UINT16 STAVariant; + UINT16 STABottom; + UINT16 STATop; +} __WLAN_ATTRIB_PACK__ hfa384x_STASupRange_t; + +/*-- Information Record: MFIActRanges --*/ +typedef struct hfa384x_MFIActRanges +{ + UINT16 MFIRole; + UINT16 MFIID; + UINT16 MFIVariant; + UINT16 MFIBottom; + UINT16 MFITop; +} __WLAN_ATTRIB_PACK__ hfa384x_MFIActRanges_t; + +/*-------------------------------------------------------------------- +Information Record Structures: NIC Information +--------------------------------------------------------------------*/ + +/*-- Information Record: PortStatus --*/ +typedef struct hfa384x_PortStatus +{ + UINT16 PortStatus; +} __WLAN_ATTRIB_PACK__ hfa384x_PortStatus_t; + +#define HFA384x_PSTATUS_DISABLED ((UINT16)1) +#define HFA384x_PSTATUS_SEARCHING ((UINT16)2) +#define HFA384x_PSTATUS_CONN_IBSS ((UINT16)3) +#define HFA384x_PSTATUS_CONN_ESS ((UINT16)4) +#define HFA384x_PSTATUS_OUTOFRANGE ((UINT16)5) +#define HFA384x_PSTATUS_CONN_WDS ((UINT16)6) + +/*-- Information Record: CurrentSSID --*/ +typedef struct hfa384x_CurrentSSID +{ + UINT8 CurrentSSID[34]; +} __WLAN_ATTRIB_PACK__ hfa384x_CurrentSSID_t; + +/*-- Information Record: CurrentBSSID --*/ +typedef struct hfa384x_CurrentBSSID +{ + UINT8 CurrentBSSID[6]; +} __WLAN_ATTRIB_PACK__ hfa384x_CurrentBSSID_t; + +/*-- Information Record: commsquality --*/ +typedef struct hfa384x_commsquality +{ + UINT16 CQ_currBSS; + UINT16 ASL_currBSS; + UINT16 ANL_currFC; +} __WLAN_ATTRIB_PACK__ hfa384x_commsquality_t; + +/*-- Information Record: dmbcommsquality --*/ +typedef struct hfa384x_dbmcommsquality +{ + UINT16 CQdbm_currBSS; + UINT16 ASLdbm_currBSS; + UINT16 ANLdbm_currFC; +} __WLAN_ATTRIB_PACK__ hfa384x_dbmcommsquality_t; + +/*-- Information Record: CurrentTxRate --*/ +typedef struct hfa384x_CurrentTxRate +{ + UINT16 CurrentTxRate; +} __WLAN_ATTRIB_PACK__ hfa384x_CurrentTxRate_t; + +/*-- Information Record: CurrentBeaconInterval --*/ +typedef struct hfa384x_CurrentBeaconInterval +{ + UINT16 CurrentBeaconInterval; +} __WLAN_ATTRIB_PACK__ hfa384x_CurrentBeaconInterval_t; + +/*-- Information Record: CurrentScaleThresholds --*/ +typedef struct hfa384x_CurrentScaleThresholds +{ + UINT16 EnergyDetectThreshold; + UINT16 CarrierDetectThreshold; + UINT16 DeferDetectThreshold; + UINT16 CellSearchThreshold; /* Stations only */ + UINT16 DeadSpotThreshold; /* Stations only */ +} __WLAN_ATTRIB_PACK__ hfa384x_CurrentScaleThresholds_t; + +/*-- Information Record: ProtocolRspTime --*/ +typedef struct hfa384x_ProtocolRspTime +{ + UINT16 ProtocolRspTime; +} __WLAN_ATTRIB_PACK__ hfa384x_ProtocolRspTime_t; + +/*-- Information Record: ShortRetryLimit --*/ +typedef struct hfa384x_ShortRetryLimit +{ + UINT16 ShortRetryLimit; +} __WLAN_ATTRIB_PACK__ hfa384x_ShortRetryLimit_t; + +/*-- Information Record: LongRetryLimit --*/ +typedef struct hfa384x_LongRetryLimit +{ + UINT16 LongRetryLimit; +} __WLAN_ATTRIB_PACK__ hfa384x_LongRetryLimit_t; + +/*-- Information Record: MaxTransmitLifetime --*/ +typedef struct hfa384x_MaxTransmitLifetime +{ + UINT16 MaxTransmitLifetime; +} __WLAN_ATTRIB_PACK__ hfa384x_MaxTransmitLifetime_t; + +/*-- Information Record: MaxReceiveLifetime --*/ +typedef struct hfa384x_MaxReceiveLifetime +{ + UINT16 MaxReceiveLifetime; +} __WLAN_ATTRIB_PACK__ hfa384x_MaxReceiveLifetime_t; + +/*-- Information Record: CFPollable --*/ +typedef struct hfa384x_CFPollable +{ + UINT16 CFPollable; +} __WLAN_ATTRIB_PACK__ hfa384x_CFPollable_t; + +/*-- Information Record: AuthenticationAlgorithms --*/ +typedef struct hfa384x_AuthenticationAlgorithms +{ + UINT16 AuthenticationType; + UINT16 TypeEnabled; +} __WLAN_ATTRIB_PACK__ hfa384x_AuthenticationAlgorithms_t; + +/*-- Information Record: AuthenticationAlgorithms +(data only --*/ +typedef struct hfa384x_AuthenticationAlgorithms_data +{ + UINT16 AuthenticationType; + UINT16 TypeEnabled; +} __WLAN_ATTRIB_PACK__ hfa384x_AuthenticationAlgorithms_data_t; + +/*-- Information Record: PrivacyOptionImplemented --*/ +typedef struct hfa384x_PrivacyOptionImplemented +{ + UINT16 PrivacyOptionImplemented; +} __WLAN_ATTRIB_PACK__ hfa384x_PrivacyOptionImplemented_t; + +/*-- Information Record: OwnMACAddress --*/ +typedef struct hfa384x_OwnMACAddress +{ + UINT8 OwnMACAddress[6]; +} __WLAN_ATTRIB_PACK__ hfa384x_OwnMACAddress_t; + +/*-- Information Record: PCFInfo --*/ +typedef struct hfa384x_PCFInfo +{ + UINT16 MediumOccupancyLimit; + UINT16 CFPPeriod; + UINT16 CFPMaxDuration; + UINT16 CFPFlags; +} __WLAN_ATTRIB_PACK__ hfa384x_PCFInfo_t; + +/*-- Information Record: PCFInfo (data portion only) --*/ +typedef struct hfa384x_PCFInfo_data +{ + UINT16 MediumOccupancyLimit; + UINT16 CFPPeriod; + UINT16 CFPMaxDuration; + UINT16 CFPFlags; +} __WLAN_ATTRIB_PACK__ hfa384x_PCFInfo_data_t; + +/*-------------------------------------------------------------------- +Information Record Structures: Modem Information Records +--------------------------------------------------------------------*/ + +/*-- Information Record: PHYType --*/ +typedef struct hfa384x_PHYType +{ + UINT16 PHYType; +} __WLAN_ATTRIB_PACK__ hfa384x_PHYType_t; + +/*-- Information Record: CurrentChannel --*/ +typedef struct hfa384x_CurrentChannel +{ + UINT16 CurrentChannel; +} __WLAN_ATTRIB_PACK__ hfa384x_CurrentChannel_t; + +/*-- Information Record: CurrentPowerState --*/ +typedef struct hfa384x_CurrentPowerState +{ + UINT16 CurrentPowerState; +} __WLAN_ATTRIB_PACK__ hfa384x_CurrentPowerState_t; + +/*-- Information Record: CCAMode --*/ +typedef struct hfa384x_CCAMode +{ + UINT16 CCAMode; +} __WLAN_ATTRIB_PACK__ hfa384x_CCAMode_t; + +/*-- Information Record: SupportedDataRates --*/ +typedef struct hfa384x_SupportedDataRates +{ + UINT8 SupportedDataRates[10]; +} __WLAN_ATTRIB_PACK__ hfa384x_SupportedDataRates_t; + +/*-- Information Record: LFOStatus --*/ +typedef struct hfa384x_LFOStatus +{ + UINT16 TestResults; + UINT16 LFOResult; + UINT16 VRHFOResult; +} __WLAN_ATTRIB_PACK__ hfa384x_LFOStatus_t; + +#define HFA384x_TESTRESULT_ALLPASSED BIT0 +#define HFA384x_TESTRESULT_LFO_FAIL BIT1 +#define HFA384x_TESTRESULT_VR_HF0_FAIL BIT2 +#define HFA384x_HOST_FIRM_COORDINATE BIT7 +#define HFA384x_TESTRESULT_COORDINATE BIT15 + +/*-- Information Record: LEDControl --*/ +typedef struct hfa384x_LEDControl +{ + UINT16 searching_on; + UINT16 searching_off; + UINT16 assoc_on; + UINT16 assoc_off; + UINT16 activity; +} __WLAN_ATTRIB_PACK__ hfa384x_LEDControl_t; + +/*-------------------------------------------------------------------- + FRAME DESCRIPTORS AND FRAME STRUCTURES + +FRAME DESCRIPTORS: Offsets + +---------------------------------------------------------------------- +Control Info (offset 44-51) +--------------------------------------------------------------------*/ +#define HFA384x_FD_STATUS_OFF ((UINT16)0x44) +#define HFA384x_FD_TIME_OFF ((UINT16)0x46) +#define HFA384x_FD_SWSUPPORT_OFF ((UINT16)0x4A) +#define HFA384x_FD_SILENCE_OFF ((UINT16)0x4A) +#define HFA384x_FD_SIGNAL_OFF ((UINT16)0x4B) +#define HFA384x_FD_RATE_OFF ((UINT16)0x4C) +#define HFA384x_FD_RXFLOW_OFF ((UINT16)0x4D) +#define HFA384x_FD_RESERVED_OFF ((UINT16)0x4E) +#define HFA384x_FD_TXCONTROL_OFF ((UINT16)0x50) +/*-------------------------------------------------------------------- +802.11 Header (offset 52-6B) +--------------------------------------------------------------------*/ +#define HFA384x_FD_FRAMECONTROL_OFF ((UINT16)0x52) +#define HFA384x_FD_DURATIONID_OFF ((UINT16)0x54) +#define HFA384x_FD_ADDRESS1_OFF ((UINT16)0x56) +#define HFA384x_FD_ADDRESS2_OFF ((UINT16)0x5C) +#define HFA384x_FD_ADDRESS3_OFF ((UINT16)0x62) +#define HFA384x_FD_SEQCONTROL_OFF ((UINT16)0x68) +#define HFA384x_FD_ADDRESS4_OFF ((UINT16)0x6A) +#define HFA384x_FD_DATALEN_OFF ((UINT16)0x70) +/*-------------------------------------------------------------------- +802.3 Header (offset 72-7F) +--------------------------------------------------------------------*/ +#define HFA384x_FD_DESTADDRESS_OFF ((UINT16)0x72) +#define HFA384x_FD_SRCADDRESS_OFF ((UINT16)0x78) +#define HFA384x_FD_DATALENGTH_OFF ((UINT16)0x7E) + +/*-------------------------------------------------------------------- +FRAME STRUCTURES: Communication Frames +---------------------------------------------------------------------- +Communication Frames: Transmit Frames +--------------------------------------------------------------------*/ +/*-- Communication Frame: Transmit Frame Structure --*/ +typedef struct hfa384x_tx_frame +{ + UINT16 status; + UINT16 reserved1; + UINT16 reserved2; + UINT32 sw_support; + UINT8 tx_retrycount; + UINT8 tx_rate; + UINT16 tx_control; + + /*-- 802.11 Header Information --*/ + + UINT16 frame_control; + UINT16 duration_id; + UINT8 address1[6]; + UINT8 address2[6]; + UINT8 address3[6]; + UINT16 sequence_control; + UINT8 address4[6]; + UINT16 data_len; /* little endian format */ + + /*-- 802.3 Header Information --*/ + + UINT8 dest_addr[6]; + UINT8 src_addr[6]; + UINT16 data_length; /* big endian format */ +} __WLAN_ATTRIB_PACK__ hfa384x_tx_frame_t; +/*-------------------------------------------------------------------- +Communication Frames: Field Masks for Transmit Frames +--------------------------------------------------------------------*/ +/*-- Status Field --*/ +#define HFA384x_TXSTATUS_ACKERR ((UINT16)BIT5) +#define HFA384x_TXSTATUS_FORMERR ((UINT16)BIT3) +#define HFA384x_TXSTATUS_DISCON ((UINT16)BIT2) +#define HFA384x_TXSTATUS_AGEDERR ((UINT16)BIT1) +#define HFA384x_TXSTATUS_RETRYERR ((UINT16)BIT0) +/*-- Transmit Control Field --*/ +#define HFA384x_TX_CFPOLL ((UINT16)BIT12) +#define HFA384x_TX_PRST ((UINT16)BIT11) +#define HFA384x_TX_MACPORT ((UINT16)(BIT10 | BIT9 | BIT8)) +#define HFA384x_TX_NOENCRYPT ((UINT16)BIT7) +#define HFA384x_TX_RETRYSTRAT ((UINT16)(BIT6 | BIT5)) +#define HFA384x_TX_STRUCTYPE ((UINT16)(BIT4 | BIT3)) +#define HFA384x_TX_TXEX ((UINT16)BIT2) +#define HFA384x_TX_TXOK ((UINT16)BIT1) +/*-------------------------------------------------------------------- +Communication Frames: Test/Get/Set Field Values for Transmit Frames +--------------------------------------------------------------------*/ +/*-- Status Field --*/ +#define HFA384x_TXSTATUS_ISERROR(v) \ + (((UINT16)(v))&\ + (HFA384x_TXSTATUS_ACKERR|HFA384x_TXSTATUS_FORMERR|\ + HFA384x_TXSTATUS_DISCON|HFA384x_TXSTATUS_AGEDERR|\ + HFA384x_TXSTATUS_RETRYERR)) + +#define HFA384x_TXSTATUS_ISACKERR(v) ((UINT16)(((UINT16)(v)) & HFA384x_TXSTATUS_ACKERR)) +#define HFA384x_TXSTATUS_ISFORMERR(v) ((UINT16)(((UINT16)(v)) & HFA384x_TXSTATUS_FORMERR)) +#define HFA384x_TXSTATUS_ISDISCON(v) ((UINT16)(((UINT16)(v)) & HFA384x_TXSTATUS_DISCON)) +#define HFA384x_TXSTATUS_ISAGEDERR(v) ((UINT16)(((UINT16)(v)) & HFA384x_TXSTATUS_AGEDERR)) +#define HFA384x_TXSTATUS_ISRETRYERR(v) ((UINT16)(((UINT16)(v)) & HFA384x_TXSTATUS_RETRYERR)) + +#define HFA384x_TX_GET(v,m,s) ((((UINT16)(v))&((UINT16)(m)))>>((UINT16)(s))) +#define HFA384x_TX_SET(v,m,s) ((((UINT16)(v))<<((UINT16)(s)))&((UINT16)(m))) + +#define HFA384x_TX_CFPOLL_GET(v) HFA384x_TX_GET(v, HFA384x_TX_CFPOLL,12) +#define HFA384x_TX_CFPOLL_SET(v) HFA384x_TX_SET(v, HFA384x_TX_CFPOLL,12) +#define HFA384x_TX_PRST_GET(v) HFA384x_TX_GET(v, HFA384x_TX_PRST,11) +#define HFA384x_TX_PRST_SET(v) HFA384x_TX_SET(v, HFA384x_TX_PRST,11) +#define HFA384x_TX_MACPORT_GET(v) HFA384x_TX_GET(v, HFA384x_TX_MACPORT, 8) +#define HFA384x_TX_MACPORT_SET(v) HFA384x_TX_SET(v, HFA384x_TX_MACPORT, 8) +#define HFA384x_TX_NOENCRYPT_GET(v) HFA384x_TX_GET(v, HFA384x_TX_NOENCRYPT, 7) +#define HFA384x_TX_NOENCRYPT_SET(v) HFA384x_TX_SET(v, HFA384x_TX_NOENCRYPT, 7) +#define HFA384x_TX_RETRYSTRAT_GET(v) HFA384x_TX_GET(v, HFA384x_TX_RETRYSTRAT, 5) +#define HFA384x_TX_RETRYSTRAT_SET(v) HFA384x_TX_SET(v, HFA384x_TX_RETRYSTRAT, 5) +#define HFA384x_TX_STRUCTYPE_GET(v) HFA384x_TX_GET(v, HFA384x_TX_STRUCTYPE, 3) +#define HFA384x_TX_STRUCTYPE_SET(v) HFA384x_TX_SET(v, HFA384x_TX_STRUCTYPE, 3) +#define HFA384x_TX_TXEX_GET(v) HFA384x_TX_GET(v, HFA384x_TX_TXEX, 2) +#define HFA384x_TX_TXEX_SET(v) HFA384x_TX_SET(v, HFA384x_TX_TXEX, 2) +#define HFA384x_TX_TXOK_GET(v) HFA384x_TX_GET(v, HFA384x_TX_TXOK, 1) +#define HFA384x_TX_TXOK_SET(v) HFA384x_TX_SET(v, HFA384x_TX_TXOK, 1) +/*-------------------------------------------------------------------- +Communication Frames: Receive Frames +--------------------------------------------------------------------*/ +/*-- Communication Frame: Receive Frame Structure --*/ +typedef struct hfa384x_rx_frame +{ + /*-- MAC rx descriptor (hfa384x byte order) --*/ + UINT16 status; + UINT32 time; + UINT8 silence; + UINT8 signal; + UINT8 rate; + UINT8 rx_flow; + UINT16 reserved1; + UINT16 reserved2; + + /*-- 802.11 Header Information (802.11 byte order) --*/ + UINT16 frame_control; + UINT16 duration_id; + UINT8 address1[6]; + UINT8 address2[6]; + UINT8 address3[6]; + UINT16 sequence_control; + UINT8 address4[6]; + UINT16 data_len; /* hfa384x (little endian) format */ + + /*-- 802.3 Header Information --*/ + UINT8 dest_addr[6]; + UINT8 src_addr[6]; + UINT16 data_length; /* IEEE? (big endian) format */ +} __WLAN_ATTRIB_PACK__ hfa384x_rx_frame_t; +/*-------------------------------------------------------------------- +Communication Frames: Field Masks for Receive Frames +--------------------------------------------------------------------*/ +/*-- Offsets --------*/ +#define HFA384x_RX_DATA_LEN_OFF ((UINT16)44) +#define HFA384x_RX_80211HDR_OFF ((UINT16)14) +#define HFA384x_RX_DATA_OFF ((UINT16)60) + +/*-- Status Fields --*/ +#define HFA384x_RXSTATUS_MSGTYPE ((UINT16)(BIT15 | BIT14 | BIT13)) +#define HFA384x_RXSTATUS_MACPORT ((UINT16)(BIT10 | BIT9 | BIT8)) +#define HFA384x_RXSTATUS_UNDECR ((UINT16)BIT1) +#define HFA384x_RXSTATUS_FCSERR ((UINT16)BIT0) +/*-------------------------------------------------------------------- +Communication Frames: Test/Get/Set Field Values for Receive Frames +--------------------------------------------------------------------*/ +#define HFA384x_RXSTATUS_MSGTYPE_GET(value) ((UINT16)((((UINT16)(value)) & HFA384x_RXSTATUS_MSGTYPE) >> 13)) +#define HFA384x_RXSTATUS_MSGTYPE_SET(value) ((UINT16)(((UINT16)(value)) << 13)) +#define HFA384x_RXSTATUS_MACPORT_GET(value) ((UINT16)((((UINT16)(value)) & HFA384x_RXSTATUS_MACPORT) >> 8)) +#define HFA384x_RXSTATUS_MACPORT_SET(value) ((UINT16)(((UINT16)(value)) << 8)) +#define HFA384x_RXSTATUS_ISUNDECR(value) ((UINT16)(((UINT16)(value)) & HFA384x_RXSTATUS_UNDECR)) +#define HFA384x_RXSTATUS_ISFCSERR(value) ((UINT16)(((UINT16)(value)) & HFA384x_RXSTATUS_FCSERR)) +/*-------------------------------------------------------------------- + FRAME STRUCTURES: Information Types and Information Frame Structures +---------------------------------------------------------------------- +Information Types +--------------------------------------------------------------------*/ +#define HFA384x_IT_HANDOVERADDR ((UINT16)0xF000UL) +#define HFA384x_IT_HANDOVERDEAUTHADDRESS ((UINT16)0xF001UL)//AP 1.3.7 +#define HFA384x_IT_COMMTALLIES ((UINT16)0xF100UL) +#define HFA384x_IT_SCANRESULTS ((UINT16)0xF101UL) +#define HFA384x_IT_CHINFORESULTS ((UINT16)0xF102UL) +#define HFA384x_IT_HOSTSCANRESULTS ((UINT16)0xF103UL) +#define HFA384x_IT_LINKSTATUS ((UINT16)0xF200UL) +#define HFA384x_IT_ASSOCSTATUS ((UINT16)0xF201UL) +#define HFA384x_IT_AUTHREQ ((UINT16)0xF202UL) +#define HFA384x_IT_PSUSERCNT ((UINT16)0xF203UL) +#define HFA384x_IT_KEYIDCHANGED ((UINT16)0xF204UL) +#define HFA384x_IT_ASSOCREQ ((UINT16)0xF205UL) +#define HFA384x_IT_MICFAILURE ((UINT16)0xF206UL) + +/*-------------------------------------------------------------------- +Information Frames Structures +---------------------------------------------------------------------- +Information Frames: Notification Frame Structures +--------------------------------------------------------------------*/ +/*-- Notification Frame,MAC Mgmt: Handover Address --*/ +typedef struct hfa384x_HandoverAddr +{ + UINT16 framelen; + UINT16 infotype; + UINT8 handover_addr[WLAN_BSSID_LEN]; +} __WLAN_ATTRIB_PACK__ hfa384x_HandoverAddr_t; + +/*-- Inquiry Frame, Diagnose: Communication Tallies --*/ +typedef struct hfa384x_CommTallies16 +{ + UINT16 txunicastframes; + UINT16 txmulticastframes; + UINT16 txfragments; + UINT16 txunicastoctets; + UINT16 txmulticastoctets; + UINT16 txdeferredtrans; + UINT16 txsingleretryframes; + UINT16 txmultipleretryframes; + UINT16 txretrylimitexceeded; + UINT16 txdiscards; + UINT16 rxunicastframes; + UINT16 rxmulticastframes; + UINT16 rxfragments; + UINT16 rxunicastoctets; + UINT16 rxmulticastoctets; + UINT16 rxfcserrors; + UINT16 rxdiscardsnobuffer; + UINT16 txdiscardswrongsa; + UINT16 rxdiscardswepundecr; + UINT16 rxmsginmsgfrag; + UINT16 rxmsginbadmsgfrag; +} __WLAN_ATTRIB_PACK__ hfa384x_CommTallies16_t; + +typedef struct hfa384x_CommTallies32 +{ + UINT32 txunicastframes; + UINT32 txmulticastframes; + UINT32 txfragments; + UINT32 txunicastoctets; + UINT32 txmulticastoctets; + UINT32 txdeferredtrans; + UINT32 txsingleretryframes; + UINT32 txmultipleretryframes; + UINT32 txretrylimitexceeded; + UINT32 txdiscards; + UINT32 rxunicastframes; + UINT32 rxmulticastframes; + UINT32 rxfragments; + UINT32 rxunicastoctets; + UINT32 rxmulticastoctets; + UINT32 rxfcserrors; + UINT32 rxdiscardsnobuffer; + UINT32 txdiscardswrongsa; + UINT32 rxdiscardswepundecr; + UINT32 rxmsginmsgfrag; + UINT32 rxmsginbadmsgfrag; +} __WLAN_ATTRIB_PACK__ hfa384x_CommTallies32_t; + +/*-- Inquiry Frame, Diagnose: Scan Results & Subfields--*/ +typedef struct hfa384x_ScanResultSub +{ + UINT16 chid; + UINT16 anl; + UINT16 sl; + UINT8 bssid[WLAN_BSSID_LEN]; + UINT16 bcnint; + UINT16 capinfo; + hfa384x_bytestr32_t ssid; + UINT8 supprates[10]; /* 802.11 info element */ + UINT16 proberesp_rate; +} __WLAN_ATTRIB_PACK__ hfa384x_ScanResultSub_t; + +typedef struct hfa384x_ScanResult +{ + UINT16 rsvd; + UINT16 scanreason; + hfa384x_ScanResultSub_t + result[HFA384x_SCANRESULT_MAX]; +} __WLAN_ATTRIB_PACK__ hfa384x_ScanResult_t; + +/*-- Inquiry Frame, Diagnose: ChInfo Results & Subfields--*/ +typedef struct hfa384x_ChInfoResultSub +{ + UINT16 chid; + UINT16 anl; + UINT16 pnl; + UINT16 active; +} __WLAN_ATTRIB_PACK__ hfa384x_ChInfoResultSub_t; + +#define HFA384x_CHINFORESULT_BSSACTIVE BIT0 +#define HFA384x_CHINFORESULT_PCFACTIVE BIT1 + +typedef struct hfa384x_ChInfoResult +{ + UINT16 scanchannels; + hfa384x_ChInfoResultSub_t + result[HFA384x_CHINFORESULT_MAX]; +} __WLAN_ATTRIB_PACK__ hfa384x_ChInfoResult_t; + +/*-- Inquiry Frame, Diagnose: Host Scan Results & Subfields--*/ +typedef struct hfa384x_HScanResultSub +{ + UINT16 chid; + UINT16 anl; + UINT16 sl; + UINT8 bssid[WLAN_BSSID_LEN]; + UINT16 bcnint; + UINT16 capinfo; + hfa384x_bytestr32_t ssid; + UINT8 supprates[10]; /* 802.11 info element */ + UINT16 proberesp_rate; + UINT16 atim; +} __WLAN_ATTRIB_PACK__ hfa384x_HScanResultSub_t; + +typedef struct hfa384x_HScanResult +{ + UINT16 nresult; + UINT16 rsvd; + hfa384x_HScanResultSub_t + result[HFA384x_HSCANRESULT_MAX]; +} __WLAN_ATTRIB_PACK__ hfa384x_HScanResult_t; + +/*-- Unsolicited Frame, MAC Mgmt: LinkStatus --*/ + +#define HFA384x_LINK_NOTCONNECTED ((UINT16)0) +#define HFA384x_LINK_CONNECTED ((UINT16)1) +#define HFA384x_LINK_DISCONNECTED ((UINT16)2) +#define HFA384x_LINK_AP_CHANGE ((UINT16)3) +#define HFA384x_LINK_AP_OUTOFRANGE ((UINT16)4) +#define HFA384x_LINK_AP_INRANGE ((UINT16)5) +#define HFA384x_LINK_ASSOCFAIL ((UINT16)6) + +typedef struct hfa384x_LinkStatus +{ + UINT16 linkstatus; +} __WLAN_ATTRIB_PACK__ hfa384x_LinkStatus_t; + + +/*-- Unsolicited Frame, MAC Mgmt: AssociationStatus (--*/ + +#define HFA384x_ASSOCSTATUS_STAASSOC ((UINT16)1) +#define HFA384x_ASSOCSTATUS_REASSOC ((UINT16)2) +#define HFA384x_ASSOCSTATUS_DISASSOC ((UINT16)3) +#define HFA384x_ASSOCSTATUS_ASSOCFAIL ((UINT16)4) +#define HFA384x_ASSOCSTATUS_AUTHFAIL ((UINT16)5) + +typedef struct hfa384x_AssocStatus +{ + UINT16 assocstatus; + UINT8 sta_addr[WLAN_ADDR_LEN]; + /* old_ap_addr is only valid if assocstatus == 2 */ + UINT8 old_ap_addr[WLAN_ADDR_LEN]; + UINT16 reason; + UINT16 reserved; +} __WLAN_ATTRIB_PACK__ hfa384x_AssocStatus_t; + +/*-- Unsolicited Frame, MAC Mgmt: AuthRequest (AP Only) --*/ + +typedef struct hfa384x_AuthRequest +{ + UINT8 sta_addr[WLAN_ADDR_LEN]; + UINT16 algorithm; +} __WLAN_ATTRIB_PACK__ hfa384x_AuthReq_t; + +/*-- Unsolicited Frame, MAC Mgmt: AssocRequest (AP Only) --*/ + +typedef struct hfa384x_AssocRequest +{ + UINT8 sta_addr[WLAN_ADDR_LEN]; + UINT16 type; + UINT8 wpa_data[80]; +} __WLAN_ATTRIB_PACK__ hfa384x_AssocReq_t; + + +#define HFA384x_ASSOCREQ_TYPE_ASSOC 0 +#define HFA384x_ASSOCREQ_TYPE_REASSOC 1 + +/*-- Unsolicited Frame, MAC Mgmt: MIC Failure (AP Only) --*/ + +typedef struct hfa384x_MicFailure +{ + UINT8 sender[WLAN_ADDR_LEN]; + UINT8 dest[WLAN_ADDR_LEN]; +} __WLAN_ATTRIB_PACK__ hfa384x_MicFailure_t; + +/*-- Unsolicited Frame, MAC Mgmt: PSUserCount (AP Only) --*/ + +typedef struct hfa384x_PSUserCount +{ + UINT16 usercnt; +} __WLAN_ATTRIB_PACK__ hfa384x_PSUserCount_t; + +typedef struct hfa384x_KeyIDChanged +{ + UINT8 sta_addr[WLAN_ADDR_LEN]; + UINT16 keyid; +} __WLAN_ATTRIB_PACK__ hfa384x_KeyIDChanged_t; + +/*-- Collection of all Inf frames ---------------*/ +typedef union hfa384x_infodata { + hfa384x_CommTallies16_t commtallies16; + hfa384x_CommTallies32_t commtallies32; + hfa384x_ScanResult_t scanresult; + hfa384x_ChInfoResult_t chinforesult; + hfa384x_HScanResult_t hscanresult; + hfa384x_LinkStatus_t linkstatus; + hfa384x_AssocStatus_t assocstatus; + hfa384x_AuthReq_t authreq; + hfa384x_PSUserCount_t psusercnt; + hfa384x_KeyIDChanged_t keyidchanged; +} __WLAN_ATTRIB_PACK__ hfa384x_infodata_t; + +typedef struct hfa384x_InfFrame +{ + UINT16 framelen; + UINT16 infotype; + hfa384x_infodata_t info; +} __WLAN_ATTRIB_PACK__ hfa384x_InfFrame_t; + +#if (WLAN_HOSTIF == WLAN_USB) +/*-------------------------------------------------------------------- +USB Packet structures and constants. +--------------------------------------------------------------------*/ + +/* Should be sent to the ctrlout endpoint */ +#define HFA384x_USB_ENBULKIN 6 + +/* Should be sent to the bulkout endpoint */ +#define HFA384x_USB_TXFRM 0 +#define HFA384x_USB_CMDREQ 1 +#define HFA384x_USB_WRIDREQ 2 +#define HFA384x_USB_RRIDREQ 3 +#define HFA384x_USB_WMEMREQ 4 +#define HFA384x_USB_RMEMREQ 5 + +/* Received from the bulkin endpoint */ +#define HFA384x_USB_ISFRM(a) (!((a) & 0x8000)) +#define HFA384x_USB_ISTXFRM(a) (((a) & 0x9000) == 0x1000) +#define HFA384x_USB_ISRXFRM(a) (!((a) & 0x9000)) +#define HFA384x_USB_INFOFRM 0x8000 +#define HFA384x_USB_CMDRESP 0x8001 +#define HFA384x_USB_WRIDRESP 0x8002 +#define HFA384x_USB_RRIDRESP 0x8003 +#define HFA384x_USB_WMEMRESP 0x8004 +#define HFA384x_USB_RMEMRESP 0x8005 +#define HFA384x_USB_BUFAVAIL 0x8006 +#define HFA384x_USB_ERROR 0x8007 + +/*------------------------------------*/ +/* Request (bulk OUT) packet contents */ + +typedef struct hfa384x_usb_txfrm { + hfa384x_tx_frame_t desc; + UINT8 data[WLAN_DATA_MAXLEN]; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_txfrm_t; + +typedef struct hfa384x_usb_cmdreq { + UINT16 type; + UINT16 cmd; + UINT16 parm0; + UINT16 parm1; + UINT16 parm2; + UINT8 pad[54]; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_cmdreq_t; + +typedef struct hfa384x_usb_wridreq { + UINT16 type; + UINT16 frmlen; + UINT16 rid; + UINT8 data[HFA384x_RIDDATA_MAXLEN]; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_wridreq_t; + +typedef struct hfa384x_usb_rridreq { + UINT16 type; + UINT16 frmlen; + UINT16 rid; + UINT8 pad[58]; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_rridreq_t; + +typedef struct hfa384x_usb_wmemreq { + UINT16 type; + UINT16 frmlen; + UINT16 offset; + UINT16 page; + UINT8 data[HFA384x_USB_RWMEM_MAXLEN]; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_wmemreq_t; + +typedef struct hfa384x_usb_rmemreq { + UINT16 type; + UINT16 frmlen; + UINT16 offset; + UINT16 page; + UINT8 pad[56]; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_rmemreq_t; + +/*------------------------------------*/ +/* Response (bulk IN) packet contents */ + +typedef struct hfa384x_usb_rxfrm { + hfa384x_rx_frame_t desc; + UINT8 data[WLAN_DATA_MAXLEN]; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_rxfrm_t; + +typedef struct hfa384x_usb_infofrm { + UINT16 type; + hfa384x_InfFrame_t info; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_infofrm_t; + +typedef struct hfa384x_usb_statusresp { + UINT16 type; + UINT16 status; + UINT16 resp0; + UINT16 resp1; + UINT16 resp2; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_cmdresp_t; + +typedef hfa384x_usb_cmdresp_t hfa384x_usb_wridresp_t; + +typedef struct hfa384x_usb_rridresp { + UINT16 type; + UINT16 frmlen; + UINT16 rid; + UINT8 data[HFA384x_RIDDATA_MAXLEN]; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_rridresp_t; + +typedef hfa384x_usb_cmdresp_t hfa384x_usb_wmemresp_t; + +typedef struct hfa384x_usb_rmemresp { + UINT16 type; + UINT16 frmlen; + UINT8 data[HFA384x_USB_RWMEM_MAXLEN]; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_rmemresp_t; + +typedef struct hfa384x_usb_bufavail { + UINT16 type; + UINT16 frmlen; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_bufavail_t; + +typedef struct hfa384x_usb_error { + UINT16 type; + UINT16 errortype; +} __WLAN_ATTRIB_PACK__ hfa384x_usb_error_t; + +/*----------------------------------------------------------*/ +/* Unions for packaging all the known packet types together */ + +typedef union hfa384x_usbout { + UINT16 type; + hfa384x_usb_txfrm_t txfrm; + hfa384x_usb_cmdreq_t cmdreq; + hfa384x_usb_wridreq_t wridreq; + hfa384x_usb_rridreq_t rridreq; + hfa384x_usb_wmemreq_t wmemreq; + hfa384x_usb_rmemreq_t rmemreq; +} __WLAN_ATTRIB_PACK__ hfa384x_usbout_t; + +typedef union hfa384x_usbin { + UINT16 type; + hfa384x_usb_rxfrm_t rxfrm; + hfa384x_usb_txfrm_t txfrm; + hfa384x_usb_infofrm_t infofrm; + hfa384x_usb_cmdresp_t cmdresp; + hfa384x_usb_wridresp_t wridresp; + hfa384x_usb_rridresp_t rridresp; + hfa384x_usb_wmemresp_t wmemresp; + hfa384x_usb_rmemresp_t rmemresp; + hfa384x_usb_bufavail_t bufavail; + hfa384x_usb_error_t usberror; + UINT8 boguspad[3000]; +} __WLAN_ATTRIB_PACK__ hfa384x_usbin_t; + +#endif /* WLAN_USB */ + +/*-------------------------------------------------------------------- +PD record structures. +--------------------------------------------------------------------*/ + +typedef struct hfa384x_pdr_pcb_partnum +{ + UINT8 num[8]; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_pcb_partnum_t; + +typedef struct hfa384x_pdr_pcb_tracenum +{ + UINT8 num[8]; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_pcb_tracenum_t; + +typedef struct hfa384x_pdr_nic_serial +{ + UINT8 num[12]; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_nic_serial_t; + +typedef struct hfa384x_pdr_mkk_measurements +{ + double carrier_freq; + double occupied_band; + double power_density; + double tx_spur_f1; + double tx_spur_f2; + double tx_spur_f3; + double tx_spur_f4; + double tx_spur_l1; + double tx_spur_l2; + double tx_spur_l3; + double tx_spur_l4; + double rx_spur_f1; + double rx_spur_f2; + double rx_spur_l1; + double rx_spur_l2; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_mkk_measurements_t; + +typedef struct hfa384x_pdr_nic_ramsize +{ + UINT8 size[12]; /* units of KB */ +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_nic_ramsize_t; + +typedef struct hfa384x_pdr_mfisuprange +{ + UINT16 id; + UINT16 variant; + UINT16 bottom; + UINT16 top; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_mfisuprange_t; + +typedef struct hfa384x_pdr_cfisuprange +{ + UINT16 id; + UINT16 variant; + UINT16 bottom; + UINT16 top; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_cfisuprange_t; + +typedef struct hfa384x_pdr_nicid +{ + UINT16 id; + UINT16 variant; + UINT16 major; + UINT16 minor; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_nicid_t; + + +typedef struct hfa384x_pdr_refdac_measurements +{ + UINT16 value[0]; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_refdac_measurements_t; + +typedef struct hfa384x_pdr_vgdac_measurements +{ + UINT16 value[0]; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_vgdac_measurements_t; + +typedef struct hfa384x_pdr_level_comp_measurements +{ + UINT16 value[0]; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_level_compc_measurements_t; + +typedef struct hfa384x_pdr_mac_address +{ + UINT8 addr[6]; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_mac_address_t; + +typedef struct hfa384x_pdr_mkk_callname +{ + UINT8 callname[8]; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_mkk_callname_t; + +typedef struct hfa384x_pdr_regdomain +{ + UINT16 numdomains; + UINT16 domain[5]; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_regdomain_t; + +typedef struct hfa384x_pdr_allowed_channel +{ + UINT16 ch_bitmap; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_allowed_channel_t; + +typedef struct hfa384x_pdr_default_channel +{ + UINT16 channel; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_default_channel_t; + +typedef struct hfa384x_pdr_privacy_option +{ + UINT16 available; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_privacy_option_t; + +typedef struct hfa384x_pdr_temptype +{ + UINT16 type; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_temptype_t; + +typedef struct hfa384x_pdr_refdac_setup +{ + UINT16 ch_value[14]; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_refdac_setup_t; + +typedef struct hfa384x_pdr_vgdac_setup +{ + UINT16 ch_value[14]; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_vgdac_setup_t; + +typedef struct hfa384x_pdr_level_comp_setup +{ + UINT16 ch_value[14]; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_level_comp_setup_t; + +typedef struct hfa384x_pdr_trimdac_setup +{ + UINT16 trimidac; + UINT16 trimqdac; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_trimdac_setup_t; + +typedef struct hfa384x_pdr_ifr_setting +{ + UINT16 value[3]; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_ifr_setting_t; + +typedef struct hfa384x_pdr_rfr_setting +{ + UINT16 value[3]; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_rfr_setting_t; + +typedef struct hfa384x_pdr_hfa3861_baseline +{ + UINT16 value[50]; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_baseline_t; + +typedef struct hfa384x_pdr_hfa3861_shadow +{ + UINT32 value[32]; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_shadow_t; + +typedef struct hfa384x_pdr_hfa3861_ifrf +{ + UINT32 value[20]; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_ifrf_t; + +typedef struct hfa384x_pdr_hfa3861_chcalsp +{ + UINT16 value[14]; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_chcalsp_t; + +typedef struct hfa384x_pdr_hfa3861_chcali +{ + UINT16 value[17]; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_chcali_t; + +typedef struct hfa384x_pdr_hfa3861_nic_config +{ + UINT16 config_bitmap; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_nic_config_t; + +typedef struct hfa384x_pdr_hfo_delay +{ + UINT8 hfo_delay; +} __WLAN_ATTRIB_PACK__ hfa384x_hfo_delay_t; + +typedef struct hfa384x_pdr_hfa3861_manf_testsp +{ + UINT16 value[30]; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_manf_testsp_t; + +typedef struct hfa384x_pdr_hfa3861_manf_testi +{ + UINT16 value[30]; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_hfa3861_manf_testi_t; + +typedef struct hfa384x_end_of_pda +{ + UINT16 crc; +} __WLAN_ATTRIB_PACK__ hfa384x_pdr_end_of_pda_t; + +typedef struct hfa384x_pdrec +{ + UINT16 len; /* in words */ + UINT16 code; + union pdr { + hfa384x_pdr_pcb_partnum_t pcb_partnum; + hfa384x_pdr_pcb_tracenum_t pcb_tracenum; + hfa384x_pdr_nic_serial_t nic_serial; + hfa384x_pdr_mkk_measurements_t mkk_measurements; + hfa384x_pdr_nic_ramsize_t nic_ramsize; + hfa384x_pdr_mfisuprange_t mfisuprange; + hfa384x_pdr_cfisuprange_t cfisuprange; + hfa384x_pdr_nicid_t nicid; + hfa384x_pdr_refdac_measurements_t refdac_measurements; + hfa384x_pdr_vgdac_measurements_t vgdac_measurements; + hfa384x_pdr_level_compc_measurements_t level_compc_measurements; + hfa384x_pdr_mac_address_t mac_address; + hfa384x_pdr_mkk_callname_t mkk_callname; + hfa384x_pdr_regdomain_t regdomain; + hfa384x_pdr_allowed_channel_t allowed_channel; + hfa384x_pdr_default_channel_t default_channel; + hfa384x_pdr_privacy_option_t privacy_option; + hfa384x_pdr_temptype_t temptype; + hfa384x_pdr_refdac_setup_t refdac_setup; + hfa384x_pdr_vgdac_setup_t vgdac_setup; + hfa384x_pdr_level_comp_setup_t level_comp_setup; + hfa384x_pdr_trimdac_setup_t trimdac_setup; + hfa384x_pdr_ifr_setting_t ifr_setting; + hfa384x_pdr_rfr_setting_t rfr_setting; + hfa384x_pdr_hfa3861_baseline_t hfa3861_baseline; + hfa384x_pdr_hfa3861_shadow_t hfa3861_shadow; + hfa384x_pdr_hfa3861_ifrf_t hfa3861_ifrf; + hfa384x_pdr_hfa3861_chcalsp_t hfa3861_chcalsp; + hfa384x_pdr_hfa3861_chcali_t hfa3861_chcali; + hfa384x_pdr_nic_config_t nic_config; + hfa384x_hfo_delay_t hfo_delay; + hfa384x_pdr_hfa3861_manf_testsp_t hfa3861_manf_testsp; + hfa384x_pdr_hfa3861_manf_testi_t hfa3861_manf_testi; + hfa384x_pdr_end_of_pda_t end_of_pda; + + } data; +} __WLAN_ATTRIB_PACK__ hfa384x_pdrec_t; + + +#ifdef __KERNEL__ +/*-------------------------------------------------------------------- +--- MAC state structure, argument to all functions -- +--- Also, a collection of support types -- +--------------------------------------------------------------------*/ +typedef struct hfa384x_statusresult +{ + UINT16 status; + UINT16 resp0; + UINT16 resp1; + UINT16 resp2; +} hfa384x_cmdresult_t; + +#if (WLAN_HOSTIF == WLAN_USB) + +/* USB Control Exchange (CTLX): + * A queue of the structure below is maintained for all of the + * Request/Response type USB packets supported by Prism2. + */ +/* The following hfa384x_* structures are arguments to + * the usercb() for the different CTLX types. + */ +typedef hfa384x_cmdresult_t hfa384x_wridresult_t; +typedef hfa384x_cmdresult_t hfa384x_wmemresult_t; + +typedef struct hfa384x_rridresult +{ + UINT16 rid; + const void *riddata; + UINT riddata_len; +} hfa384x_rridresult_t; + +enum ctlx_state { + CTLX_START = 0, /* Start state, not queued */ + + CTLX_COMPLETE, /* CTLX successfully completed */ + CTLX_REQ_FAILED, /* OUT URB completed w/ error */ + + CTLX_PENDING, /* Queued, data valid */ + CTLX_REQ_SUBMITTED, /* OUT URB submitted */ + CTLX_REQ_COMPLETE, /* OUT URB complete */ + CTLX_RESP_COMPLETE /* IN URB received */ +}; +typedef enum ctlx_state CTLX_STATE; + +struct hfa384x_usbctlx; +struct hfa384x; + +typedef void (*ctlx_cmdcb_t)( struct hfa384x*, const struct hfa384x_usbctlx* ); + +typedef void (*ctlx_usercb_t)( + struct hfa384x *hw, + void *ctlxresult, + void *usercb_data); + +typedef struct hfa384x_usbctlx +{ + struct list_head list; + + size_t outbufsize; + hfa384x_usbout_t outbuf; /* pkt buf for OUT */ + hfa384x_usbin_t inbuf; /* pkt buf for IN(a copy) */ + + CTLX_STATE state; /* Tracks running state */ + + struct completion done; + volatile int reapable; /* Food for the reaper task */ + + ctlx_cmdcb_t cmdcb; /* Async command callback */ + ctlx_usercb_t usercb; /* Async user callback, */ + void *usercb_data; /* at CTLX completion */ + + int variant; /* Identifies cmd variant */ +} hfa384x_usbctlx_t; + +typedef struct hfa384x_usbctlxq +{ + spinlock_t lock; + struct list_head pending; + struct list_head active; + struct list_head completing; + struct list_head reapable; +} hfa384x_usbctlxq_t; +#endif + +typedef struct hfa484x_metacmd +{ + UINT16 cmd; + + UINT16 parm0; + UINT16 parm1; + UINT16 parm2; + +#if 0 //XXX cmd irq stuff + UINT16 bulkid; /* what RID/FID to copy down. */ + int bulklen; /* how much to copy from BAP */ + char *bulkdata; /* And to where? */ +#endif + + hfa384x_cmdresult_t result; +} hfa384x_metacmd_t; + +#define MAX_PRISM2_GRP_ADDR 16 +#define MAX_GRP_ADDR 32 +#define WLAN_COMMENT_MAX 80 /* Max. length of user comment string. */ + +#define MM_SAT_PCF (BIT14) +#define MM_GCSD_PCF (BIT15) +#define MM_GCSD_PCF_EB (BIT14 | BIT15) + +#define WLAN_STATE_STOPPED 0 /* Network is not active. */ +#define WLAN_STATE_STARTED 1 /* Network has been started. */ + +#define WLAN_AUTH_MAX 60 /* Max. # of authenticated stations. */ +#define WLAN_ACCESS_MAX 60 /* Max. # of stations in an access list. */ +#define WLAN_ACCESS_NONE 0 /* No stations may be authenticated. */ +#define WLAN_ACCESS_ALL 1 /* All stations may be authenticated. */ +#define WLAN_ACCESS_ALLOW 2 /* Authenticate only "allowed" stations. */ +#define WLAN_ACCESS_DENY 3 /* Do not authenticate "denied" stations. */ + +/* XXX These are going away ASAP */ +typedef struct prism2sta_authlist +{ + UINT cnt; + UINT8 addr[WLAN_AUTH_MAX][WLAN_ADDR_LEN]; + UINT8 assoc[WLAN_AUTH_MAX]; +} prism2sta_authlist_t; + +typedef struct prism2sta_accesslist +{ + UINT modify; + UINT cnt; + UINT8 addr[WLAN_ACCESS_MAX][WLAN_ADDR_LEN]; + UINT cnt1; + UINT8 addr1[WLAN_ACCESS_MAX][WLAN_ADDR_LEN]; +} prism2sta_accesslist_t; + +typedef struct hfa384x +{ +#if (WLAN_HOSTIF != WLAN_USB) + /* Resource config */ + UINT32 iobase; + char __iomem *membase; + UINT32 irq; +#else + /* USB support data */ + struct usb_device *usb; + struct urb rx_urb; + struct sk_buff *rx_urb_skb; + struct urb tx_urb; + struct urb ctlx_urb; + hfa384x_usbout_t txbuff; + hfa384x_usbctlxq_t ctlxq; + struct timer_list reqtimer; + struct timer_list resptimer; + + struct timer_list throttle; + + struct tasklet_struct reaper_bh; + struct tasklet_struct completion_bh; + + struct work_struct usb_work; + + unsigned long usb_flags; +#define THROTTLE_RX 0 +#define THROTTLE_TX 1 +#define WORK_RX_HALT 2 +#define WORK_TX_HALT 3 +#define WORK_RX_RESUME 4 +#define WORK_TX_RESUME 5 + + unsigned short req_timer_done:1; + unsigned short resp_timer_done:1; + + int endp_in; + int endp_out; +#endif /* !USB */ + +#if (WLAN_HOSTIF == WLAN_PCMCIA) +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16) + struct pcmcia_device *pdev; +#else + dev_link_t *link; +#endif + dev_node_t node; +#endif + + int sniff_fcs; + int sniff_channel; + int sniff_truncate; + int sniffhdr; + + wait_queue_head_t cmdq; /* wait queue itself */ + + /* Controller state */ + UINT32 state; + UINT32 isap; + UINT8 port_enabled[HFA384x_NUMPORTS_MAX]; +#if (WLAN_HOSTIF != WLAN_USB) + UINT auxen; + UINT isram16; +#endif /* !USB */ + + /* Download support */ + UINT dlstate; + hfa384x_downloadbuffer_t bufinfo; + UINT16 dltimeout; + +#if (WLAN_HOSTIF != WLAN_USB) + spinlock_t cmdlock; + volatile int cmdflag; /* wait queue flag */ + hfa384x_metacmd_t *cmddata; /* for our async callback */ + + /* BAP support */ + spinlock_t baplock; + struct tasklet_struct bap_tasklet; + + /* MAC buffer ids */ + UINT16 txfid_head; + UINT16 txfid_tail; + UINT txfid_N; + UINT16 txfid_queue[HFA384x_DRVR_FIDSTACKLEN_MAX]; + UINT16 infofid; + struct semaphore infofid_sem; +#endif /* !USB */ + + int scanflag; /* to signal scan comlete */ + int join_ap; /* are we joined to a specific ap */ + int join_retries; /* number of join retries till we fail */ + hfa384x_JoinRequest_data_t joinreq; /* join request saved data */ + + wlandevice_t *wlandev; + /* Timer to allow for the deferred processing of linkstatus messages */ + struct work_struct link_bh; + + struct work_struct commsqual_bh; + hfa384x_commsquality_t qual; + struct timer_list commsqual_timer; + + UINT16 link_status; + UINT16 link_status_new; + struct sk_buff_head authq; + + /* And here we have stuff that used to be in priv */ + + /* State variables */ + UINT presniff_port_type; + UINT16 presniff_wepflags; + UINT32 dot11_desired_bss_type; + int ap; /* AP flag: 0 - Station, 1 - Access Point. */ + + int dbmadjust; + + /* Group Addresses - right now, there are up to a total + of MAX_GRP_ADDR group addresses */ + UINT8 dot11_grp_addr[MAX_GRP_ADDR][WLAN_ADDR_LEN]; + UINT dot11_grpcnt; + + /* Component Identities */ + hfa384x_compident_t ident_nic; + hfa384x_compident_t ident_pri_fw; + hfa384x_compident_t ident_sta_fw; + hfa384x_compident_t ident_ap_fw; + UINT16 mm_mods; + + /* Supplier compatibility ranges */ + hfa384x_caplevel_t cap_sup_mfi; + hfa384x_caplevel_t cap_sup_cfi; + hfa384x_caplevel_t cap_sup_pri; + hfa384x_caplevel_t cap_sup_sta; + hfa384x_caplevel_t cap_sup_ap; + + /* Actor compatibility ranges */ + hfa384x_caplevel_t cap_act_pri_cfi; /* pri f/w to controller interface */ + hfa384x_caplevel_t cap_act_sta_cfi; /* sta f/w to controller interface */ + hfa384x_caplevel_t cap_act_sta_mfi; /* sta f/w to modem interface */ + hfa384x_caplevel_t cap_act_ap_cfi; /* ap f/w to controller interface */ + hfa384x_caplevel_t cap_act_ap_mfi; /* ap f/w to modem interface */ + + UINT32 psusercount; /* Power save user count. */ + hfa384x_CommTallies32_t tallies; /* Communication tallies. */ + UINT8 comment[WLAN_COMMENT_MAX+1]; /* User comment */ + + /* Channel Info request results (AP only) */ + struct { + atomic_t done; + UINT8 count; + hfa384x_ChInfoResult_t results; + } channel_info; + + hfa384x_InfFrame_t *scanresults; + + + prism2sta_authlist_t authlist; /* Authenticated station list. */ + UINT accessmode; /* Access mode. */ + prism2sta_accesslist_t allow; /* Allowed station list. */ + prism2sta_accesslist_t deny; /* Denied station list. */ + +} hfa384x_t; + +/*=============================================================*/ +/*--- Function Declarations -----------------------------------*/ +/*=============================================================*/ +#if (WLAN_HOSTIF == WLAN_USB) +void +hfa384x_create( + hfa384x_t *hw, + struct usb_device *usb); +#else +void +hfa384x_create( + hfa384x_t *hw, + UINT irq, + UINT32 iobase, + UINT8 __iomem *membase); +#endif + +void hfa384x_destroy(hfa384x_t *hw); + +irqreturn_t +hfa384x_interrupt(int irq, void *dev_id PT_REGS); +int +hfa384x_corereset( hfa384x_t *hw, int holdtime, int settletime, int genesis); +int +hfa384x_drvr_chinforesults( hfa384x_t *hw); +int +hfa384x_drvr_commtallies( hfa384x_t *hw); +int +hfa384x_drvr_disable(hfa384x_t *hw, UINT16 macport); +int +hfa384x_drvr_enable(hfa384x_t *hw, UINT16 macport); +int +hfa384x_drvr_flashdl_enable(hfa384x_t *hw); +int +hfa384x_drvr_flashdl_disable(hfa384x_t *hw); +int +hfa384x_drvr_flashdl_write(hfa384x_t *hw, UINT32 daddr, void* buf, UINT32 len); +int +hfa384x_drvr_getconfig(hfa384x_t *hw, UINT16 rid, void *buf, UINT16 len); +int +hfa384x_drvr_handover( hfa384x_t *hw, UINT8 *addr); +int +hfa384x_drvr_hostscanresults( hfa384x_t *hw); +int +hfa384x_drvr_low_level(hfa384x_t *hw, hfa384x_metacmd_t *cmd); +int +hfa384x_drvr_mmi_read(hfa384x_t *hw, UINT32 address, UINT32 *result); +int +hfa384x_drvr_mmi_write(hfa384x_t *hw, UINT32 address, UINT32 data); +int +hfa384x_drvr_ramdl_enable(hfa384x_t *hw, UINT32 exeaddr); +int +hfa384x_drvr_ramdl_disable(hfa384x_t *hw); +int +hfa384x_drvr_ramdl_write(hfa384x_t *hw, UINT32 daddr, void* buf, UINT32 len); +int +hfa384x_drvr_readpda(hfa384x_t *hw, void *buf, UINT len); +int +hfa384x_drvr_scanresults( hfa384x_t *hw); + +int +hfa384x_drvr_setconfig(hfa384x_t *hw, UINT16 rid, void *buf, UINT16 len); + +static inline int +hfa384x_drvr_getconfig16(hfa384x_t *hw, UINT16 rid, void *val) +{ + int result = 0; + result = hfa384x_drvr_getconfig(hw, rid, val, sizeof(UINT16)); + if ( result == 0 ) { + *((UINT16*)val) = hfa384x2host_16(*((UINT16*)val)); + } + return result; +} + +static inline int +hfa384x_drvr_getconfig32(hfa384x_t *hw, UINT16 rid, void *val) +{ + int result = 0; + + result = hfa384x_drvr_getconfig(hw, rid, val, sizeof(UINT32)); + if ( result == 0 ) { + *((UINT32*)val) = hfa384x2host_32(*((UINT32*)val)); + } + + return result; +} + +static inline int +hfa384x_drvr_setconfig16(hfa384x_t *hw, UINT16 rid, UINT16 val) +{ + UINT16 value = host2hfa384x_16(val); + return hfa384x_drvr_setconfig(hw, rid, &value, sizeof(value)); +} + +static inline int +hfa384x_drvr_setconfig32(hfa384x_t *hw, UINT16 rid, UINT32 val) +{ + UINT32 value = host2hfa384x_32(val); + return hfa384x_drvr_setconfig(hw, rid, &value, sizeof(value)); +} + +#if (WLAN_HOSTIF == WLAN_USB) +int +hfa384x_drvr_getconfig_async(hfa384x_t *hw, + UINT16 rid, + ctlx_usercb_t usercb, + void *usercb_data); + +int +hfa384x_drvr_setconfig_async(hfa384x_t *hw, + UINT16 rid, + void *buf, + UINT16 len, + ctlx_usercb_t usercb, + void *usercb_data); +#else +static inline int +hfa384x_drvr_setconfig_async(hfa384x_t *hw, UINT16 rid, void *buf, UINT16 len, + void *ptr1, void *ptr2) +{ + (void)ptr1; + (void)ptr2; + return hfa384x_drvr_setconfig(hw, rid, buf, len); +} +#endif + +static inline int +hfa384x_drvr_setconfig16_async(hfa384x_t *hw, UINT16 rid, UINT16 val) +{ + UINT16 value = host2hfa384x_16(val); + return hfa384x_drvr_setconfig_async(hw, rid, &value, sizeof(value), + NULL , NULL); +} + +static inline int +hfa384x_drvr_setconfig32_async(hfa384x_t *hw, UINT16 rid, UINT32 val) +{ + UINT32 value = host2hfa384x_32(val); + return hfa384x_drvr_setconfig_async(hw, rid, &value, sizeof(value), + NULL , NULL); +} + + +int +hfa384x_drvr_start(hfa384x_t *hw); +int +hfa384x_drvr_stop(hfa384x_t *hw); +int +hfa384x_drvr_txframe(hfa384x_t *hw, struct sk_buff *skb, p80211_hdr_t *p80211_hdr, p80211_metawep_t *p80211_wep); +void +hfa384x_tx_timeout(wlandevice_t *wlandev); + +int +hfa384x_cmd_initialize(hfa384x_t *hw); +int +hfa384x_cmd_enable(hfa384x_t *hw, UINT16 macport); +int +hfa384x_cmd_disable(hfa384x_t *hw, UINT16 macport); +int +hfa384x_cmd_diagnose(hfa384x_t *hw); +int +hfa384x_cmd_allocate(hfa384x_t *hw, UINT16 len); +int +hfa384x_cmd_transmit(hfa384x_t *hw, UINT16 reclaim, UINT16 qos, UINT16 fid); +int +hfa384x_cmd_clearpersist(hfa384x_t *hw, UINT16 fid); +int +hfa384x_cmd_notify(hfa384x_t *hw, UINT16 reclaim, UINT16 fid, void *buf, UINT16 len); +int +hfa384x_cmd_inquire(hfa384x_t *hw, UINT16 fid); +int +hfa384x_cmd_access(hfa384x_t *hw, UINT16 write, UINT16 rid, void *buf, UINT16 len); +int +hfa384x_cmd_monitor(hfa384x_t *hw, UINT16 enable); +int +hfa384x_cmd_download( + hfa384x_t *hw, + UINT16 mode, + UINT16 lowaddr, + UINT16 highaddr, + UINT16 codelen); +int +hfa384x_cmd_aux_enable(hfa384x_t *hw, int force); +int +hfa384x_cmd_aux_disable(hfa384x_t *hw); +int +hfa384x_copy_from_bap( + hfa384x_t *hw, + UINT16 bap, + UINT16 id, + UINT16 offset, + void *buf, + UINT len); +int +hfa384x_copy_to_bap( + hfa384x_t *hw, + UINT16 bap, + UINT16 id, + UINT16 offset, + void *buf, + UINT len); +void +hfa384x_copy_from_aux( + hfa384x_t *hw, + UINT32 cardaddr, + UINT32 auxctl, + void *buf, + UINT len); +void +hfa384x_copy_to_aux( + hfa384x_t *hw, + UINT32 cardaddr, + UINT32 auxctl, + void *buf, + UINT len); + +#if (WLAN_HOSTIF != WLAN_USB) + +/* + HFA384x is a LITTLE ENDIAN part. + + the get/setreg functions implicitly byte-swap the data to LE. + the _noswap variants do not perform a byte-swap on the data. +*/ + +static inline UINT16 +__hfa384x_getreg(hfa384x_t *hw, UINT reg); + +static inline void +__hfa384x_setreg(hfa384x_t *hw, UINT16 val, UINT reg); + +static inline UINT16 +__hfa384x_getreg_noswap(hfa384x_t *hw, UINT reg); + +static inline void +__hfa384x_setreg_noswap(hfa384x_t *hw, UINT16 val, UINT reg); + +#ifdef REVERSE_ENDIAN +#define hfa384x_getreg __hfa384x_getreg_noswap +#define hfa384x_setreg __hfa384x_setreg_noswap +#define hfa384x_getreg_noswap __hfa384x_getreg +#define hfa384x_setreg_noswap __hfa384x_setreg +#else +#define hfa384x_getreg __hfa384x_getreg +#define hfa384x_setreg __hfa384x_setreg +#define hfa384x_getreg_noswap __hfa384x_getreg_noswap +#define hfa384x_setreg_noswap __hfa384x_setreg_noswap +#endif + +/*---------------------------------------------------------------- +* hfa384x_getreg +* +* Retrieve the value of one of the MAC registers. Done here +* because different PRISM2 MAC parts use different buses and such. +* NOTE: This function returns the value in HOST ORDER!!!!!! +* +* Arguments: +* hw MAC part structure +* reg Register identifier (offset for I/O based i/f) +* +* Returns: +* Value from the register in HOST ORDER!!!! +----------------------------------------------------------------*/ +static inline UINT16 +__hfa384x_getreg(hfa384x_t *hw, UINT reg) +{ +/* printk(KERN_DEBUG "Reading from 0x%0x\n", hw->membase + reg); */ +#if ((WLAN_HOSTIF == WLAN_PCMCIA) || (WLAN_HOSTIF == WLAN_PLX)) + return wlan_inw_le16_to_cpu(hw->iobase+reg); +#elif (WLAN_HOSTIF == WLAN_PCI) + return __le16_to_cpu(readw(hw->membase + reg)); +#endif +} + +/*---------------------------------------------------------------- +* hfa384x_setreg +* +* Set the value of one of the MAC registers. Done here +* because different PRISM2 MAC parts use different buses and such. +* NOTE: This function assumes the value is in HOST ORDER!!!!!! +* +* Arguments: +* hw MAC part structure +* val Value, in HOST ORDER!!, to put in the register +* reg Register identifier (offset for I/O based i/f) +* +* Returns: +* Nothing +----------------------------------------------------------------*/ +static inline void +__hfa384x_setreg(hfa384x_t *hw, UINT16 val, UINT reg) +{ +#if ((WLAN_HOSTIF == WLAN_PCMCIA) || (WLAN_HOSTIF == WLAN_PLX)) + wlan_outw_cpu_to_le16( val, hw->iobase + reg); + return; +#elif (WLAN_HOSTIF == WLAN_PCI) + writew(__cpu_to_le16(val), hw->membase + reg); + return; +#endif +} + + +/*---------------------------------------------------------------- +* hfa384x_getreg_noswap +* +* Retrieve the value of one of the MAC registers. Done here +* because different PRISM2 MAC parts use different buses and such. +* +* Arguments: +* hw MAC part structure +* reg Register identifier (offset for I/O based i/f) +* +* Returns: +* Value from the register. +----------------------------------------------------------------*/ +static inline UINT16 +__hfa384x_getreg_noswap(hfa384x_t *hw, UINT reg) +{ +#if ((WLAN_HOSTIF == WLAN_PCMCIA) || (WLAN_HOSTIF == WLAN_PLX)) + return wlan_inw(hw->iobase+reg); +#elif (WLAN_HOSTIF == WLAN_PCI) + return readw(hw->membase + reg); +#endif +} + + +/*---------------------------------------------------------------- +* hfa384x_setreg_noswap +* +* Set the value of one of the MAC registers. Done here +* because different PRISM2 MAC parts use different buses and such. +* +* Arguments: +* hw MAC part structure +* val Value to put in the register +* reg Register identifier (offset for I/O based i/f) +* +* Returns: +* Nothing +----------------------------------------------------------------*/ +static inline void +__hfa384x_setreg_noswap(hfa384x_t *hw, UINT16 val, UINT reg) +{ +#if ((WLAN_HOSTIF == WLAN_PCMCIA) || (WLAN_HOSTIF == WLAN_PLX)) + wlan_outw( val, hw->iobase + reg); + return; +#elif (WLAN_HOSTIF == WLAN_PCI) + writew(val, hw->membase + reg); + return; +#endif +} + + +static inline void hfa384x_events_all(hfa384x_t *hw) +{ + hfa384x_setreg(hw, + HFA384x_INT_NORMAL +#ifdef CMD_IRQ + | HFA384x_INTEN_CMD_SET(1) +#endif + , + HFA384x_INTEN); + +} + +static inline void hfa384x_events_nobap(hfa384x_t *hw) +{ + hfa384x_setreg(hw, + (HFA384x_INT_NORMAL & ~HFA384x_INT_BAP_OP) +#ifdef CMD_IRQ + | HFA384x_INTEN_CMD_SET(1) +#endif + , + HFA384x_INTEN); + +} + +#endif /* WLAN_HOSTIF != WLAN_USB */ +#endif /* __KERNEL__ */ + +#endif /* _HFA384x_H */ diff --git a/gpxe/src/drivers/net/ipoib.c b/gpxe/src/drivers/net/ipoib.c new file mode 100644 index 0000000..8ad2c29 --- /dev/null +++ b/gpxe/src/drivers/net/ipoib.c @@ -0,0 +1,1128 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * IP over Infiniband + */ + +/** Number of IPoIB data send work queue entries */ +#define IPOIB_DATA_NUM_SEND_WQES 2 + +/** Number of IPoIB data receive work queue entries */ +#define IPOIB_DATA_NUM_RECV_WQES 4 + +/** Number of IPoIB data completion entries */ +#define IPOIB_DATA_NUM_CQES 8 + +/** Number of IPoIB metadata send work queue entries */ +#define IPOIB_META_NUM_SEND_WQES 2 + +/** Number of IPoIB metadata receive work queue entries */ +#define IPOIB_META_NUM_RECV_WQES 2 + +/** Number of IPoIB metadata completion entries */ +#define IPOIB_META_NUM_CQES 8 + +/** An IPoIB queue set */ +struct ipoib_queue_set { + /** Completion queue */ + struct ib_completion_queue *cq; + /** Queue pair */ + struct ib_queue_pair *qp; + /** Receive work queue maximum fill level */ + unsigned int recv_max_fill; +}; + +/** An IPoIB device */ +struct ipoib_device { + /** Network device */ + struct net_device *netdev; + /** Underlying Infiniband device */ + struct ib_device *ibdev; + /** Data queue set */ + struct ipoib_queue_set data; + /** Data queue set */ + struct ipoib_queue_set meta; + /** Broadcast GID */ + struct ib_gid broadcast_gid; + /** Broadcast LID */ + unsigned int broadcast_lid; + /** Data queue key */ + unsigned long data_qkey; + /** Attached to multicast group + * + * This flag indicates whether or not we have attached our + * data queue pair to the broadcast multicast GID. + */ + int broadcast_attached; +}; + +/** TID half used to identify get path record replies */ +#define IPOIB_TID_GET_PATH_REC 0x11111111UL + +/** TID half used to identify multicast member record replies */ +#define IPOIB_TID_MC_MEMBER_REC 0x22222222UL + +/** IPoIB metadata TID */ +static uint32_t ipoib_meta_tid = 0; + +/** Broadcast QPN used in IPoIB MAC addresses + * + * This is a guaranteed invalid real QPN + */ +#define IPOIB_BROADCAST_QPN 0xffffffffUL + +/** Broadcast IPoIB address */ +static struct ipoib_mac ipoib_broadcast = { + .qpn = ntohl ( IPOIB_BROADCAST_QPN ), + .gid.u.bytes = { 0xff, 0x12, 0x40, 0x1b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff }, +}; + +/**************************************************************************** + * + * IPoIB peer cache + * + **************************************************************************** + */ + +/** + * IPoIB peer address + * + * This serves a similar role to the ARP cache for Ethernet. (ARP + * *is* used on IPoIB; we have two caches to maintain.) + */ +struct ipoib_peer { + /** Key */ + uint8_t key; + /** MAC address */ + struct ipoib_mac mac; + /** LID */ + unsigned int lid; + /** Service level */ + unsigned int sl; + /** Rate */ + unsigned int rate; +}; + +/** Number of IPoIB peer cache entries + * + * Must be a power of two. + */ +#define IPOIB_NUM_CACHED_PEERS 4 + +/** IPoIB peer address cache */ +static struct ipoib_peer ipoib_peer_cache[IPOIB_NUM_CACHED_PEERS]; + +/** Oldest IPoIB peer cache entry index */ +static unsigned int ipoib_peer_cache_idx = 1; + +/** + * Look up cached peer by key + * + * @v key Peer cache key + * @ret peer Peer cache entry, or NULL + */ +static struct ipoib_peer * ipoib_lookup_peer_by_key ( unsigned int key ) { + struct ipoib_peer *peer; + unsigned int i; + + for ( i = 0 ; i < IPOIB_NUM_CACHED_PEERS ; i++ ) { + peer = &ipoib_peer_cache[i]; + if ( peer->key == key ) + return peer; + } + + if ( key != 0 ) { + DBG ( "IPoIB warning: peer cache lost track of key %x while " + "still in use\n", key ); + } + return NULL; +} + +/** + * Look up cached peer by GID + * + * @v gid Peer GID + * @ret peer Peer cache entry, or NULL + */ +static struct ipoib_peer * +ipoib_lookup_peer_by_gid ( const struct ib_gid *gid ) { + struct ipoib_peer *peer; + unsigned int i; + + for ( i = 0 ; i < IPOIB_NUM_CACHED_PEERS ; i++ ) { + peer = &ipoib_peer_cache[i]; + if ( memcmp ( &peer->mac.gid, gid, + sizeof ( peer->mac.gid) ) == 0 ) { + return peer; + } + } + + return NULL; +} + +/** + * Store GID and QPN in peer cache + * + * @v gid Peer GID + * @v qpn Peer QPN + * @ret peer Peer cache entry + */ +static struct ipoib_peer * +ipoib_cache_peer ( const struct ib_gid *gid, unsigned long qpn ) { + struct ipoib_peer *peer; + unsigned int key; + + /* Look for existing cache entry */ + peer = ipoib_lookup_peer_by_gid ( gid ); + if ( peer ) { + assert ( peer->mac.qpn = ntohl ( qpn ) ); + return peer; + } + + /* No entry found: create a new one */ + key = ipoib_peer_cache_idx++; + peer = &ipoib_peer_cache[ key % IPOIB_NUM_CACHED_PEERS ]; + if ( peer->key ) + DBG ( "IPoIB peer %x evicted from cache\n", peer->key ); + + memset ( peer, 0, sizeof ( *peer ) ); + peer->key = key; + peer->mac.qpn = htonl ( qpn ); + memcpy ( &peer->mac.gid, gid, sizeof ( peer->mac.gid ) ); + DBG ( "IPoIB peer %x has GID %08x:%08x:%08x:%08x and QPN %lx\n", + peer->key, htonl ( gid->u.dwords[0] ), + htonl ( gid->u.dwords[1] ), htonl ( gid->u.dwords[2] ), + htonl ( gid->u.dwords[3] ), qpn ); + return peer; +} + +/**************************************************************************** + * + * IPoIB link layer + * + **************************************************************************** + */ + +/** + * Add IPoIB link-layer header + * + * @v iobuf I/O buffer + * @v ll_dest Link-layer destination address + * @v ll_source Source link-layer address + * @v net_proto Network-layer protocol, in network-byte order + * @ret rc Return status code + */ +static int ipoib_push ( struct io_buffer *iobuf, const void *ll_dest, + const void *ll_source __unused, uint16_t net_proto ) { + struct ipoib_hdr *ipoib_hdr = + iob_push ( iobuf, sizeof ( *ipoib_hdr ) ); + const struct ipoib_mac *dest_mac = ll_dest; + const struct ipoib_mac *src_mac = ll_source; + struct ipoib_peer *dest; + struct ipoib_peer *src; + + /* Add link-layer addresses to cache */ + dest = ipoib_cache_peer ( &dest_mac->gid, ntohl ( dest_mac->qpn ) ); + src = ipoib_cache_peer ( &src_mac->gid, ntohl ( src_mac->qpn ) ); + + /* Build IPoIB header */ + ipoib_hdr->proto = net_proto; + ipoib_hdr->u.peer.dest = dest->key; + ipoib_hdr->u.peer.src = src->key; + + return 0; +} + +/** + * Remove IPoIB link-layer header + * + * @v iobuf I/O buffer + * @ret ll_dest Link-layer destination address + * @ret ll_source Source link-layer address + * @ret net_proto Network-layer protocol, in network-byte order + * @ret rc Return status code + */ +static int ipoib_pull ( struct io_buffer *iobuf, const void **ll_dest, + const void **ll_source, uint16_t *net_proto ) { + struct ipoib_hdr *ipoib_hdr = iobuf->data; + struct ipoib_peer *dest; + struct ipoib_peer *source; + + /* Sanity check */ + if ( iob_len ( iobuf ) < sizeof ( *ipoib_hdr ) ) { + DBG ( "IPoIB packet too short for link-layer header\n" ); + DBG_HD ( iobuf->data, iob_len ( iobuf ) ); + return -EINVAL; + } + + /* Strip off IPoIB header */ + iob_pull ( iobuf, sizeof ( *ipoib_hdr ) ); + + /* Identify source and destination addresses, and clear + * reserved word in IPoIB header + */ + dest = ipoib_lookup_peer_by_key ( ipoib_hdr->u.peer.dest ); + source = ipoib_lookup_peer_by_key ( ipoib_hdr->u.peer.src ); + ipoib_hdr->u.reserved = 0; + + /* Fill in required fields */ + *ll_dest = ( dest ? &dest->mac : &ipoib_broadcast ); + *ll_source = ( source ? &source->mac : &ipoib_broadcast ); + *net_proto = ipoib_hdr->proto; + + return 0; +} + +/** + * Transcribe IPoIB address + * + * @v ll_addr Link-layer address + * @ret string Link-layer address in human-readable format + */ +const char * ipoib_ntoa ( const void *ll_addr ) { + static char buf[45]; + const struct ipoib_mac *mac = ll_addr; + + snprintf ( buf, sizeof ( buf ), "%08x:%08x:%08x:%08x:%08x", + htonl ( mac->qpn ), htonl ( mac->gid.u.dwords[0] ), + htonl ( mac->gid.u.dwords[1] ), + htonl ( mac->gid.u.dwords[2] ), + htonl ( mac->gid.u.dwords[3] ) ); + return buf; +} + +/** + * Hash multicast address + * + * @v af Address family + * @v net_addr Network-layer address + * @v ll_addr Link-layer address to fill in + * @ret rc Return status code + */ +static int ipoib_mc_hash ( unsigned int af __unused, + const void *net_addr __unused, + void *ll_addr __unused ) { + + return -ENOTSUP; +} + +/** IPoIB protocol */ +struct ll_protocol ipoib_protocol __ll_protocol = { + .name = "IPoIB", + .ll_proto = htons ( ARPHRD_INFINIBAND ), + .ll_addr_len = IPOIB_ALEN, + .ll_header_len = IPOIB_HLEN, + .ll_broadcast = ( uint8_t * ) &ipoib_broadcast, + .push = ipoib_push, + .pull = ipoib_pull, + .ntoa = ipoib_ntoa, + .mc_hash = ipoib_mc_hash, +}; + +/**************************************************************************** + * + * IPoIB network device + * + **************************************************************************** + */ + +/** + * Destroy queue set + * + * @v ipoib IPoIB device + * @v qset Queue set + */ +static void ipoib_destroy_qset ( struct ipoib_device *ipoib, + struct ipoib_queue_set *qset ) { + struct ib_device *ibdev = ipoib->ibdev; + + if ( qset->qp ) + ib_destroy_qp ( ibdev, qset->qp ); + if ( qset->cq ) + ib_destroy_cq ( ibdev, qset->cq ); + memset ( qset, 0, sizeof ( *qset ) ); +} + +/** + * Create queue set + * + * @v ipoib IPoIB device + * @v qset Queue set + * @v num_cqes Number of completion queue entries + * @v cq_op Completion queue operations + * @v num_send_wqes Number of send work queue entries + * @v num_recv_wqes Number of receive work queue entries + * @v qkey Queue key + * @ret rc Return status code + */ +static int ipoib_create_qset ( struct ipoib_device *ipoib, + struct ipoib_queue_set *qset, + unsigned int num_cqes, + struct ib_completion_queue_operations *cq_op, + unsigned int num_send_wqes, + unsigned int num_recv_wqes, + unsigned long qkey ) { + struct ib_device *ibdev = ipoib->ibdev; + int rc; + + /* Sanity check */ + assert ( qset->cq == NULL ); + assert ( qset->qp == NULL ); + + /* Store queue parameters */ + qset->recv_max_fill = num_recv_wqes; + + /* Allocate completion queue */ + qset->cq = ib_create_cq ( ibdev, num_cqes, cq_op ); + if ( ! qset->cq ) { + DBGC ( ipoib, "IPoIB %p could not allocate completion queue\n", + ipoib ); + rc = -ENOMEM; + goto err; + } + + /* Allocate queue pair */ + qset->qp = ib_create_qp ( ibdev, num_send_wqes, qset->cq, + num_recv_wqes, qset->cq, qkey ); + if ( ! qset->qp ) { + DBGC ( ipoib, "IPoIB %p could not allocate queue pair\n", + ipoib ); + rc = -ENOMEM; + goto err; + } + ib_qp_set_ownerdata ( qset->qp, ipoib->netdev ); + + return 0; + + err: + ipoib_destroy_qset ( ipoib, qset ); + return rc; +} + +/** + * Transmit path record request + * + * @v ipoib IPoIB device + * @v gid Destination GID + * @ret rc Return status code + */ +static int ipoib_get_path_record ( struct ipoib_device *ipoib, + struct ib_gid *gid ) { + struct ib_device *ibdev = ipoib->ibdev; + struct io_buffer *iobuf; + struct ib_mad_sa *sa; + struct ib_address_vector av; + int rc; + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( sizeof ( *sa ) ); + if ( ! iobuf ) + return -ENOMEM; + iob_put ( iobuf, sizeof ( *sa ) ); + sa = iobuf->data; + memset ( sa, 0, sizeof ( *sa ) ); + + /* Construct path record request */ + sa->mad_hdr.base_version = IB_MGMT_BASE_VERSION; + sa->mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_ADM; + sa->mad_hdr.class_version = 2; + sa->mad_hdr.method = IB_MGMT_METHOD_GET; + sa->mad_hdr.attr_id = htons ( IB_SA_ATTR_PATH_REC ); + sa->mad_hdr.tid[0] = IPOIB_TID_GET_PATH_REC; + sa->mad_hdr.tid[1] = ipoib_meta_tid++; + sa->sa_hdr.comp_mask[1] = + htonl ( IB_SA_PATH_REC_DGID | IB_SA_PATH_REC_SGID ); + memcpy ( &sa->sa_data.path_record.dgid, gid, + sizeof ( sa->sa_data.path_record.dgid ) ); + memcpy ( &sa->sa_data.path_record.sgid, &ibdev->gid, + sizeof ( sa->sa_data.path_record.sgid ) ); + + /* Construct address vector */ + memset ( &av, 0, sizeof ( av ) ); + av.lid = ibdev->sm_lid; + av.sl = ibdev->sm_sl; + av.qpn = IB_SA_QPN; + av.qkey = IB_GLOBAL_QKEY; + + /* Post send request */ + if ( ( rc = ib_post_send ( ibdev, ipoib->meta.qp, &av, + iobuf ) ) != 0 ) { + DBGC ( ipoib, "IPoIB %p could not send get path record: %s\n", + ipoib, strerror ( rc ) ); + free_iob ( iobuf ); + return rc; + } + + return 0; +} + +/** + * Transmit multicast group membership request + * + * @v ipoib IPoIB device + * @v gid Multicast GID + * @v join Join (rather than leave) group + * @ret rc Return status code + */ +static int ipoib_mc_member_record ( struct ipoib_device *ipoib, + struct ib_gid *gid, int join ) { + struct ib_device *ibdev = ipoib->ibdev; + struct io_buffer *iobuf; + struct ib_mad_sa *sa; + struct ib_address_vector av; + int rc; + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( sizeof ( *sa ) ); + if ( ! iobuf ) + return -ENOMEM; + iob_put ( iobuf, sizeof ( *sa ) ); + sa = iobuf->data; + memset ( sa, 0, sizeof ( *sa ) ); + + /* Construct path record request */ + sa->mad_hdr.base_version = IB_MGMT_BASE_VERSION; + sa->mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_ADM; + sa->mad_hdr.class_version = 2; + sa->mad_hdr.method = + ( join ? IB_MGMT_METHOD_SET : IB_MGMT_METHOD_DELETE ); + sa->mad_hdr.attr_id = htons ( IB_SA_ATTR_MC_MEMBER_REC ); + sa->mad_hdr.tid[0] = IPOIB_TID_MC_MEMBER_REC; + sa->mad_hdr.tid[1] = ipoib_meta_tid++; + sa->sa_hdr.comp_mask[1] = + htonl ( IB_SA_MCMEMBER_REC_MGID | IB_SA_MCMEMBER_REC_PORT_GID | + IB_SA_MCMEMBER_REC_JOIN_STATE ); + sa->sa_data.mc_member_record.scope__join_state = 1; + memcpy ( &sa->sa_data.mc_member_record.mgid, gid, + sizeof ( sa->sa_data.mc_member_record.mgid ) ); + memcpy ( &sa->sa_data.mc_member_record.port_gid, &ibdev->gid, + sizeof ( sa->sa_data.mc_member_record.port_gid ) ); + + /* Construct address vector */ + memset ( &av, 0, sizeof ( av ) ); + av.lid = ibdev->sm_lid; + av.sl = ibdev->sm_sl; + av.qpn = IB_SA_QPN; + av.qkey = IB_GLOBAL_QKEY; + + /* Post send request */ + if ( ( rc = ib_post_send ( ibdev, ipoib->meta.qp, &av, + iobuf ) ) != 0 ) { + DBGC ( ipoib, "IPoIB %p could not send get path record: %s\n", + ipoib, strerror ( rc ) ); + free_iob ( iobuf ); + return rc; + } + + return 0; +} + +/** + * Transmit packet via IPoIB network device + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int ipoib_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct ipoib_device *ipoib = netdev->priv; + struct ib_device *ibdev = ipoib->ibdev; + struct ipoib_hdr *ipoib_hdr; + struct ipoib_peer *dest; + struct ib_address_vector av; + struct ib_gid *gid; + + /* Sanity check */ + if ( iob_len ( iobuf ) < sizeof ( *ipoib_hdr ) ) { + DBGC ( ipoib, "IPoIB %p buffer too short\n", ipoib ); + return -EINVAL; + } + ipoib_hdr = iobuf->data; + + /* Attempting transmission while link is down will put the + * queue pair into an error state, so don't try it. + */ + if ( ! ib_link_ok ( ibdev ) ) + return -ENETUNREACH; + + /* Identify destination address */ + dest = ipoib_lookup_peer_by_key ( ipoib_hdr->u.peer.dest ); + if ( ! dest ) + return -ENXIO; + ipoib_hdr->u.reserved = 0; + + /* Construct address vector */ + memset ( &av, 0, sizeof ( av ) ); + av.qkey = ipoib->data_qkey; + av.gid_present = 1; + if ( dest->mac.qpn == htonl ( IPOIB_BROADCAST_QPN ) ) { + /* Broadcast */ + av.qpn = IB_BROADCAST_QPN; + av.lid = ipoib->broadcast_lid; + gid = &ipoib->broadcast_gid; + } else { + /* Unicast */ + if ( ! dest->lid ) { + /* No LID yet - get path record to fetch LID */ + ipoib_get_path_record ( ipoib, &dest->mac.gid ); + return -ENOENT; + } + av.qpn = ntohl ( dest->mac.qpn ); + av.lid = dest->lid; + av.rate = dest->rate; + av.sl = dest->sl; + gid = &dest->mac.gid; + } + memcpy ( &av.gid, gid, sizeof ( av.gid ) ); + + return ib_post_send ( ibdev, ipoib->data.qp, &av, iobuf ); +} + +/** + * Handle IPoIB data send completion + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void ipoib_data_complete_send ( struct ib_device *ibdev __unused, + struct ib_queue_pair *qp, + struct io_buffer *iobuf, int rc ) { + struct net_device *netdev = ib_qp_get_ownerdata ( qp ); + + netdev_tx_complete_err ( netdev, iobuf, rc ); +} + +/** + * Handle IPoIB data receive completion + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v av Address vector, or NULL + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void ipoib_data_complete_recv ( struct ib_device *ibdev __unused, + struct ib_queue_pair *qp, + struct ib_address_vector *av, + struct io_buffer *iobuf, int rc ) { + struct net_device *netdev = ib_qp_get_ownerdata ( qp ); + struct ipoib_device *ipoib = netdev->priv; + struct ipoib_hdr *ipoib_hdr; + struct ipoib_peer *src; + + if ( rc != 0 ) { + netdev_rx_err ( netdev, iobuf, rc ); + return; + } + + /* Sanity check */ + if ( iob_len ( iobuf ) < sizeof ( struct ipoib_hdr ) ) { + DBGC ( ipoib, "IPoIB %p received data packet too short to " + "contain IPoIB header\n", ipoib ); + DBGC_HD ( ipoib, iobuf->data, iob_len ( iobuf ) ); + netdev_rx_err ( netdev, iobuf, -EIO ); + return; + } + ipoib_hdr = iobuf->data; + + /* Parse source address */ + if ( av->gid_present ) { + src = ipoib_cache_peer ( &av->gid, av->qpn ); + ipoib_hdr->u.peer.src = src->key; + } + + /* Hand off to network layer */ + netdev_rx ( netdev, iobuf ); +} + +/** IPoIB data completion operations */ +static struct ib_completion_queue_operations ipoib_data_cq_op = { + .complete_send = ipoib_data_complete_send, + .complete_recv = ipoib_data_complete_recv, +}; + +/** + * Handle IPoIB metadata send completion + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void ipoib_meta_complete_send ( struct ib_device *ibdev __unused, + struct ib_queue_pair *qp, + struct io_buffer *iobuf, int rc ) { + struct net_device *netdev = ib_qp_get_ownerdata ( qp ); + struct ipoib_device *ipoib = netdev->priv; + + if ( rc != 0 ) { + DBGC ( ipoib, "IPoIB %p metadata TX completion error: %s\n", + ipoib, strerror ( rc ) ); + } + free_iob ( iobuf ); +} + +/** + * Handle received IPoIB path record + * + * @v ipoib IPoIB device + * @v path_record Path record + */ +static void ipoib_recv_path_record ( struct ipoib_device *ipoib, + struct ib_path_record *path_record ) { + struct ipoib_peer *peer; + + /* Locate peer cache entry */ + peer = ipoib_lookup_peer_by_gid ( &path_record->dgid ); + if ( ! peer ) { + DBGC ( ipoib, "IPoIB %p received unsolicited path record\n", + ipoib ); + return; + } + + /* Update path cache entry */ + peer->lid = ntohs ( path_record->dlid ); + peer->sl = ( path_record->reserved__sl & 0x0f ); + peer->rate = ( path_record->rate_selector__rate & 0x3f ); + + DBG ( "IPoIB peer %x has dlid %x sl %x rate %x\n", + peer->key, peer->lid, peer->sl, peer->rate ); +} + +/** + * Handle received IPoIB multicast membership record + * + * @v ipoib IPoIB device + * @v mc_member_record Multicast membership record + */ +static void ipoib_recv_mc_member_record ( struct ipoib_device *ipoib, + struct ib_mc_member_record *mc_member_record ) { + int joined; + int rc; + + /* Record parameters */ + joined = ( mc_member_record->scope__join_state & 0x0f ); + ipoib->data_qkey = ntohl ( mc_member_record->qkey ); + ipoib->broadcast_lid = ntohs ( mc_member_record->mlid ); + DBGC ( ipoib, "IPoIB %p %s broadcast group: qkey %lx mlid %x\n", + ipoib, ( joined ? "joined" : "left" ), ipoib->data_qkey, + ipoib->broadcast_lid ); + + /* Update data queue pair qkey */ + if ( ( rc = ib_modify_qp ( ipoib->ibdev, ipoib->data.qp, + IB_MODIFY_QKEY, ipoib->data_qkey ) ) != 0 ){ + DBGC ( ipoib, "IPoIB %p could not update data qkey: %s\n", + ipoib, strerror ( rc ) ); + return; + } +} + +/** + * Handle IPoIB metadata receive completion + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v av Address vector, or NULL + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void +ipoib_meta_complete_recv ( struct ib_device *ibdev __unused, + struct ib_queue_pair *qp, + struct ib_address_vector *av __unused, + struct io_buffer *iobuf, int rc ) { + struct net_device *netdev = ib_qp_get_ownerdata ( qp ); + struct ipoib_device *ipoib = netdev->priv; + struct ib_mad_sa *sa; + + if ( rc != 0 ) { + DBGC ( ipoib, "IPoIB %p metadata RX completion error: %s\n", + ipoib, strerror ( rc ) ); + goto done; + } + + if ( iob_len ( iobuf ) < sizeof ( *sa ) ) { + DBGC ( ipoib, "IPoIB %p received metadata packet too short " + "to contain reply\n", ipoib ); + DBGC_HD ( ipoib, iobuf->data, iob_len ( iobuf ) ); + goto done; + } + sa = iobuf->data; + + if ( sa->mad_hdr.status != 0 ) { + DBGC ( ipoib, "IPoIB %p metadata RX err status %04x\n", + ipoib, ntohs ( sa->mad_hdr.status ) ); + goto done; + } + + switch ( sa->mad_hdr.tid[0] ) { + case IPOIB_TID_GET_PATH_REC: + ipoib_recv_path_record ( ipoib, &sa->sa_data.path_record ); + break; + case IPOIB_TID_MC_MEMBER_REC: + ipoib_recv_mc_member_record ( ipoib, + &sa->sa_data.mc_member_record ); + break; + default: + DBGC ( ipoib, "IPoIB %p unwanted response:\n", + ipoib ); + DBGC_HD ( ipoib, sa, sizeof ( *sa ) ); + break; + } + + done: + free_iob ( iobuf ); +} + +/** IPoIB metadata completion operations */ +static struct ib_completion_queue_operations ipoib_meta_cq_op = { + .complete_send = ipoib_meta_complete_send, + .complete_recv = ipoib_meta_complete_recv, +}; + +/** + * Refill IPoIB receive ring + * + * @v ipoib IPoIB device + */ +static void ipoib_refill_recv ( struct ipoib_device *ipoib, + struct ipoib_queue_set *qset ) { + struct ib_device *ibdev = ipoib->ibdev; + struct io_buffer *iobuf; + int rc; + + while ( qset->qp->recv.fill < qset->recv_max_fill ) { + iobuf = alloc_iob ( IPOIB_PKT_LEN ); + if ( ! iobuf ) + break; + if ( ( rc = ib_post_recv ( ibdev, qset->qp, iobuf ) ) != 0 ) { + free_iob ( iobuf ); + break; + } + } +} + +/** + * Poll IPoIB network device + * + * @v netdev Network device + */ +static void ipoib_poll ( struct net_device *netdev ) { + struct ipoib_device *ipoib = netdev->priv; + struct ib_device *ibdev = ipoib->ibdev; + + ib_poll_cq ( ibdev, ipoib->meta.cq ); + ib_poll_cq ( ibdev, ipoib->data.cq ); + ipoib_refill_recv ( ipoib, &ipoib->meta ); + ipoib_refill_recv ( ipoib, &ipoib->data ); +} + +/** + * Enable/disable interrupts on IPoIB network device + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ +static void ipoib_irq ( struct net_device *netdev __unused, + int enable __unused ) { + /* No implementation */ +} + +/** + * Join IPv4 broadcast multicast group + * + * @v ipoib IPoIB device + * @ret rc Return status code + */ +static int ipoib_join_broadcast_group ( struct ipoib_device *ipoib ) { + int rc; + + /* Sanity check */ + if ( ! ipoib->data.qp ) + return 0; + + /* Attach data queue to broadcast multicast GID */ + assert ( ipoib->broadcast_attached == 0 ); + if ( ( rc = ib_mcast_attach ( ipoib->ibdev, ipoib->data.qp, + &ipoib->broadcast_gid ) ) != 0 ){ + DBGC ( ipoib, "IPoIB %p could not attach to broadcast GID: " + "%s\n", ipoib, strerror ( rc ) ); + return rc; + } + ipoib->broadcast_attached = 1; + + /* Initiate broadcast group join */ + if ( ( rc = ipoib_mc_member_record ( ipoib, &ipoib->broadcast_gid, + 1 ) ) != 0 ) { + DBGC ( ipoib, "IPoIB %p could not send broadcast join: %s\n", + ipoib, strerror ( rc ) ); + return rc; + } + + /* We will set link up on the network device when we receive + * the broadcast join response. + */ + + return 0; +} + +/** + * Leave IPv4 broadcast multicast group + * + * @v ipoib IPoIB device + */ +static void ipoib_leave_broadcast_group ( struct ipoib_device *ipoib ) { + + /* Detach data queue from broadcast multicast GID */ + if ( ipoib->broadcast_attached ) { + assert ( ipoib->data.qp != NULL ); + ib_mcast_detach ( ipoib->ibdev, ipoib->data.qp, + &ipoib->broadcast_gid ); + ipoib->broadcast_attached = 0; + } +} + +/** + * Open IPoIB network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ipoib_open ( struct net_device *netdev ) { + struct ipoib_device *ipoib = netdev->priv; + struct ipoib_mac *mac = ( ( struct ipoib_mac * ) netdev->ll_addr ); + int rc; + + /* Open IB device */ + if ( ( rc = ib_open ( ipoib->ibdev ) ) != 0 ) { + DBGC ( ipoib, "IPoIB %p could not open device: %s\n", + ipoib, strerror ( rc ) ); + goto err_ib_open; + } + + /* Allocate metadata queue set */ + if ( ( rc = ipoib_create_qset ( ipoib, &ipoib->meta, + IPOIB_META_NUM_CQES, + &ipoib_meta_cq_op, + IPOIB_META_NUM_SEND_WQES, + IPOIB_META_NUM_RECV_WQES, + IB_GLOBAL_QKEY ) ) != 0 ) { + DBGC ( ipoib, "IPoIB %p could not allocate metadata QP: %s\n", + ipoib, strerror ( rc ) ); + goto err_create_meta_qset; + } + + /* Allocate data queue set */ + if ( ( rc = ipoib_create_qset ( ipoib, &ipoib->data, + IPOIB_DATA_NUM_CQES, + &ipoib_data_cq_op, + IPOIB_DATA_NUM_SEND_WQES, + IPOIB_DATA_NUM_RECV_WQES, + IB_GLOBAL_QKEY ) ) != 0 ) { + DBGC ( ipoib, "IPoIB %p could not allocate data QP: %s\n", + ipoib, strerror ( rc ) ); + goto err_create_data_qset; + } + + /* Update MAC address with data QPN */ + mac->qpn = htonl ( ipoib->data.qp->qpn ); + + /* Fill receive rings */ + ipoib_refill_recv ( ipoib, &ipoib->meta ); + ipoib_refill_recv ( ipoib, &ipoib->data ); + + /* Join broadcast group */ + if ( ( rc = ipoib_join_broadcast_group ( ipoib ) ) != 0 ) { + DBGC ( ipoib, "IPoIB %p could not join broadcast group: %s\n", + ipoib, strerror ( rc ) ); + goto err_join_broadcast; + } + + return 0; + + err_join_broadcast: + ipoib_destroy_qset ( ipoib, &ipoib->data ); + err_create_data_qset: + ipoib_destroy_qset ( ipoib, &ipoib->meta ); + err_create_meta_qset: + ib_close ( ipoib->ibdev ); + err_ib_open: + return rc; +} + +/** + * Close IPoIB network device + * + * @v netdev Network device + */ +static void ipoib_close ( struct net_device *netdev ) { + struct ipoib_device *ipoib = netdev->priv; + struct ipoib_mac *mac = ( ( struct ipoib_mac * ) netdev->ll_addr ); + + /* Leave broadcast group */ + ipoib_leave_broadcast_group ( ipoib ); + + /* Remove data QPN from MAC address */ + mac->qpn = 0; + + /* Tear down the queues */ + ipoib_destroy_qset ( ipoib, &ipoib->data ); + ipoib_destroy_qset ( ipoib, &ipoib->meta ); + + /* Close IB device */ + ib_close ( ipoib->ibdev ); +} + +/** IPoIB network device operations */ +static struct net_device_operations ipoib_operations = { + .open = ipoib_open, + .close = ipoib_close, + .transmit = ipoib_transmit, + .poll = ipoib_poll, + .irq = ipoib_irq, +}; + +/** + * Update IPoIB dynamic Infiniband parameters + * + * @v ipoib IPoIB device + * + * The Infiniband port GID and partition key will change at runtime, + * when the link is established (or lost). The MAC address is based + * on the port GID, and the broadcast GID is based on the partition + * key. This function recalculates these IPoIB device parameters. + */ +static void ipoib_set_ib_params ( struct ipoib_device *ipoib ) { + struct ib_device *ibdev = ipoib->ibdev; + struct net_device *netdev = ipoib->netdev; + struct ipoib_mac *mac; + + /* Calculate GID portion of MAC address based on port GID */ + mac = ( ( struct ipoib_mac * ) netdev->ll_addr ); + memcpy ( &mac->gid, &ibdev->gid, sizeof ( mac->gid ) ); + + /* Calculate broadcast GID based on partition key */ + memcpy ( &ipoib->broadcast_gid, &ipoib_broadcast.gid, + sizeof ( ipoib->broadcast_gid ) ); + ipoib->broadcast_gid.u.words[2] = htons ( ibdev->pkey ); + + /* Set net device link state to reflect Infiniband link state */ + if ( ib_link_ok ( ibdev ) ) { + netdev_link_up ( netdev ); + } else { + netdev_link_down ( netdev ); + } +} + +/** + * Handle link status change + * + * @v ibdev Infiniband device + */ +void ipoib_link_state_changed ( struct ib_device *ibdev ) { + struct net_device *netdev = ib_get_ownerdata ( ibdev ); + struct ipoib_device *ipoib = netdev->priv; + int rc; + + /* Leave existing broadcast group */ + ipoib_leave_broadcast_group ( ipoib ); + + /* Update MAC address and broadcast GID based on new port GID + * and partition key. + */ + ipoib_set_ib_params ( ipoib ); + + /* Join new broadcast group */ + if ( ( rc = ipoib_join_broadcast_group ( ipoib ) ) != 0 ) { + DBGC ( ipoib, "IPoIB %p could not rejoin broadcast group: " + "%s\n", ipoib, strerror ( rc ) ); + return; + } +} + +/** + * Probe IPoIB device + * + * @v ibdev Infiniband device + * @ret rc Return status code + */ +int ipoib_probe ( struct ib_device *ibdev ) { + struct net_device *netdev; + struct ipoib_device *ipoib; + int rc; + + /* Allocate network device */ + netdev = alloc_ipoibdev ( sizeof ( *ipoib ) ); + if ( ! netdev ) + return -ENOMEM; + netdev_init ( netdev, &ipoib_operations ); + ipoib = netdev->priv; + ib_set_ownerdata ( ibdev, netdev ); + netdev->dev = ibdev->dev; + memset ( ipoib, 0, sizeof ( *ipoib ) ); + ipoib->netdev = netdev; + ipoib->ibdev = ibdev; + + /* Calculate as much of the broadcast GID and the MAC address + * as we can. We won't know either of these in full until we + * have link-up. + */ + ipoib_set_ib_params ( ipoib ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + return 0; + + err_register_netdev: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + return rc; +} + +/** + * Remove IPoIB device + * + * @v ibdev Infiniband device + */ +void ipoib_remove ( struct ib_device *ibdev ) { + struct net_device *netdev = ib_get_ownerdata ( ibdev ); + + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} diff --git a/gpxe/src/drivers/net/legacy.c b/gpxe/src/drivers/net/legacy.c new file mode 100644 index 0000000..cbec3cf --- /dev/null +++ b/gpxe/src/drivers/net/legacy.c @@ -0,0 +1,155 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Quick and dirty compatibility layer + * + * This should allow old-API PCI drivers to at least function until + * they are updated. It will not help non-PCI drivers. + * + * No drivers should rely on this code. It will be removed asap. + * + */ + +struct nic nic; + +static int legacy_registered = 0; + +static int legacy_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { + struct nic *nic = netdev->priv; + struct ethhdr *ethhdr; + + DBG ( "Transmitting %zd bytes\n", iob_len ( iobuf ) ); + iob_pad ( iobuf, ETH_ZLEN ); + ethhdr = iobuf->data; + iob_pull ( iobuf, sizeof ( *ethhdr ) ); + nic->nic_op->transmit ( nic, ( const char * ) ethhdr->h_dest, + ntohs ( ethhdr->h_protocol ), + iob_len ( iobuf ), iobuf->data ); + netdev_tx_complete ( netdev, iobuf ); + return 0; +} + +static void legacy_poll ( struct net_device *netdev ) { + struct nic *nic = netdev->priv; + struct io_buffer *iobuf; + + iobuf = alloc_iob ( ETH_FRAME_LEN ); + if ( ! iobuf ) + return; + + nic->packet = iobuf->data; + if ( nic->nic_op->poll ( nic, 1 ) ) { + DBG ( "Received %d bytes\n", nic->packetlen ); + iob_put ( iobuf, nic->packetlen ); + netdev_rx ( netdev, iobuf ); + } else { + free_iob ( iobuf ); + } +} + +static int legacy_open ( struct net_device *netdev __unused ) { + /* Nothing to do */ + return 0; +} + +static void legacy_close ( struct net_device *netdev __unused ) { + /* Nothing to do */ +} + +static void legacy_irq ( struct net_device *netdev __unused, int enable ) { + struct nic *nic = netdev->priv; + + nic->nic_op->irq ( nic, ( enable ? ENABLE : DISABLE ) ); +} + +static struct net_device_operations legacy_operations = { + .open = legacy_open, + .close = legacy_close, + .transmit = legacy_transmit, + .poll = legacy_poll, + .irq = legacy_irq, +}; + +int legacy_probe ( void *hwdev, + void ( * set_drvdata ) ( void *hwdev, void *priv ), + struct device *dev, + int ( * probe ) ( struct nic *nic, void *hwdev ), + void ( * disable ) ( struct nic *nic, void *hwdev ) ) { + struct net_device *netdev; + int rc; + + if ( legacy_registered ) + return -EBUSY; + + netdev = alloc_etherdev ( 0 ); + if ( ! netdev ) + return -ENOMEM; + netdev_init ( netdev, &legacy_operations ); + netdev->priv = &nic; + memset ( &nic, 0, sizeof ( nic ) ); + set_drvdata ( hwdev, netdev ); + netdev->dev = dev; + + nic.node_addr = netdev->ll_addr; + nic.irqno = dev->desc.irq; + + if ( ! probe ( &nic, hwdev ) ) { + rc = -ENODEV; + goto err_probe; + } + + /* Overwrite the IRQ number. Some legacy devices set + * nic->irqno to 0 in the probe routine to indicate that they + * don't support interrupts; doing this allows the timer + * interrupt to be used instead. + */ + dev->desc.irq = nic.irqno; + + /* Mark as link up; legacy devices don't handle link state */ + netdev_link_up ( netdev ); + + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + /* Do not remove this message */ + printf ( "WARNING: Using legacy NIC wrapper on %s\n", + ethernet_protocol.ntoa ( nic.node_addr ) ); + + legacy_registered = 1; + return 0; + + err_register: + disable ( &nic, hwdev ); + err_probe: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + return rc; +} + +void legacy_remove ( void *hwdev, + void * ( * get_drvdata ) ( void *hwdev ), + void ( * disable ) ( struct nic *nic, void *hwdev ) ) { + struct net_device *netdev = get_drvdata ( hwdev ); + struct nic *nic = netdev->priv; + + unregister_netdev ( netdev ); + disable ( nic, hwdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); + legacy_registered = 0; +} + +int dummy_connect ( struct nic *nic __unused ) { + return 1; +} + +void dummy_irq ( struct nic *nic __unused, irq_action_t irq_action __unused ) { + return; +} diff --git a/gpxe/src/drivers/net/mtd80x.c b/gpxe/src/drivers/net/mtd80x.c new file mode 100644 index 0000000..7cc8ce2 --- /dev/null +++ b/gpxe/src/drivers/net/mtd80x.c @@ -0,0 +1,1084 @@ +/************************************************************************** +* +* mtd80x.c: Etherboot device driver for the mtd80x Ethernet chip. +* Written 2004-2004 by Erdem Güven +* +* 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 +#include + +/* 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, +}; + +#if 0 /* not used */ +static +struct chip_info +{ + u16 dev_id; + int flag; +} +mtd80x_chips[] = { + {0x0800, HAS_MII_XCVR}, + {0x0803, HAS_CHIP_XCVR}, + {0x0891, HAS_MII_XCVR} + }; +static int chip_cnt = sizeof( mtd80x_chips ) / sizeof( struct chip_info ); +#endif + +/* Offsets to the Command and Status Registers. */ +enum mtd_offsets { + PAR0 = 0x0, /* physical address 0-3 */ + PAR1 = 0x04, /* physical address 4-5 */ + MAR0 = 0x08, /* multicast address 0-3 */ + MAR1 = 0x0C, /* multicast address 4-7 */ + FAR0 = 0x10, /* flow-control address 0-3 */ + FAR1 = 0x14, /* flow-control address 4-5 */ + TCRRCR = 0x18, /* receive & transmit configuration */ + BCR = 0x1C, /* bus command */ + TXPDR = 0x20, /* transmit polling demand */ + RXPDR = 0x24, /* receive polling demand */ + RXCWP = 0x28, /* receive current word pointer */ + TXLBA = 0x2C, /* transmit list base address */ + RXLBA = 0x30, /* receive list base address */ + ISR = 0x34, /* interrupt status */ + IMR = 0x38, /* interrupt mask */ + FTH = 0x3C, /* flow control high/low threshold */ + MANAGEMENT = 0x40, /* bootrom/eeprom and mii management */ + TALLY = 0x44, /* tally counters for crc and mpa */ + TSR = 0x48, /* tally counter for transmit status */ + BMCRSR = 0x4c, /* basic mode control and status */ + PHYIDENTIFIER = 0x50, /* phy identifier */ + ANARANLPAR = 0x54, /* auto-negotiation advertisement and link + partner ability */ + ANEROCR = 0x58, /* auto-negotiation expansion and pci conf. */ + BPREMRPSR = 0x5c, /* bypass & receive error mask and phy status */ +}; + +/* Bits in the interrupt status/enable registers. */ +/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */ +enum intr_status_bits { + RFCON = 0x00020000, /* receive flow control xon packet */ + RFCOFF = 0x00010000, /* receive flow control xoff packet */ + LSCStatus = 0x00008000, /* link status change */ + ANCStatus = 0x00004000, /* autonegotiation completed */ + FBE = 0x00002000, /* fatal bus error */ + FBEMask = 0x00001800, /* mask bit12-11 */ + ParityErr = 0x00000000, /* parity error */ + TargetErr = 0x00001000, /* target abort */ + MasterErr = 0x00000800, /* master error */ + TUNF = 0x00000400, /* transmit underflow */ + ROVF = 0x00000200, /* receive overflow */ + ETI = 0x00000100, /* transmit early int */ + ERI = 0x00000080, /* receive early int */ + CNTOVF = 0x00000040, /* counter overflow */ + RBU = 0x00000020, /* receive buffer unavailable */ + TBU = 0x00000010, /* transmit buffer unavilable */ + TI = 0x00000008, /* transmit interrupt */ + RI = 0x00000004, /* receive interrupt */ + RxErr = 0x00000002, /* receive error */ +}; + +/* Bits in the NetworkConfig register. */ +enum rx_mode_bits { + RxModeMask = 0xe0, + AcceptAllPhys = 0x80, /* promiscuous mode */ + AcceptBroadcast = 0x40, /* accept broadcast */ + AcceptMulticast = 0x20, /* accept mutlicast */ + AcceptRunt = 0x08, /* receive runt pkt */ + ALP = 0x04, /* receive long pkt */ + AcceptErr = 0x02, /* receive error pkt */ + + AcceptMyPhys = 0x00000000, + RxEnable = 0x00000001, + RxFlowCtrl = 0x00002000, + TxEnable = 0x00040000, + TxModeFDX = 0x00100000, + TxThreshold = 0x00e00000, + + PS1000 = 0x00010000, + PS10 = 0x00080000, + FD = 0x00100000, +}; + +/* Bits in network_desc.status */ +enum rx_desc_status_bits { + RXOWN = 0x80000000, /* own bit */ + FLNGMASK = 0x0fff0000, /* frame length */ + FLNGShift = 16, + MARSTATUS = 0x00004000, /* multicast address received */ + BARSTATUS = 0x00002000, /* broadcast address received */ + PHYSTATUS = 0x00001000, /* physical address received */ + RXFSD = 0x00000800, /* first descriptor */ + RXLSD = 0x00000400, /* last descriptor */ + ErrorSummary = 0x80, /* error summary */ + RUNT = 0x40, /* runt packet received */ + LONG = 0x20, /* long packet received */ + FAE = 0x10, /* frame align error */ + CRC = 0x08, /* crc error */ + RXER = 0x04, /* receive error */ +}; + +enum rx_desc_control_bits { + RXIC = 0x00800000, /* interrupt control */ + RBSShift = 0, +}; + +enum tx_desc_status_bits { + TXOWN = 0x80000000, /* own bit */ + JABTO = 0x00004000, /* jabber timeout */ + CSL = 0x00002000, /* carrier sense lost */ + LC = 0x00001000, /* late collision */ + EC = 0x00000800, /* excessive collision */ + UDF = 0x00000400, /* fifo underflow */ + DFR = 0x00000200, /* deferred */ + HF = 0x00000100, /* heartbeat fail */ + NCRMask = 0x000000ff, /* collision retry count */ + NCRShift = 0, +}; + +enum tx_desc_control_bits { + TXIC = 0x80000000, /* interrupt control */ + ETIControl = 0x40000000, /* early transmit interrupt */ + TXLD = 0x20000000, /* last descriptor */ + TXFD = 0x10000000, /* first descriptor */ + CRCEnable = 0x08000000, /* crc control */ + PADEnable = 0x04000000, /* padding control */ + RetryTxLC = 0x02000000, /* retry late collision */ + PKTSMask = 0x3ff800, /* packet size bit21-11 */ + PKTSShift = 11, + TBSMask = 0x000007ff, /* transmit buffer bit 10-0 */ + TBSShift = 0, +}; + +/* BootROM/EEPROM/MII Management Register */ +#define MASK_MIIR_MII_READ 0x00000000 +#define MASK_MIIR_MII_WRITE 0x00000008 +#define MASK_MIIR_MII_MDO 0x00000004 +#define MASK_MIIR_MII_MDI 0x00000002 +#define MASK_MIIR_MII_MDC 0x00000001 + +/* ST+OP+PHYAD+REGAD+TA */ +#define OP_READ 0x6000 /* ST:01+OP:10+PHYAD+REGAD+TA:Z0 */ +#define OP_WRITE 0x5002 /* ST:01+OP:01+PHYAD+REGAD+TA:10 */ + +/* ------------------------------------------------------------------------- */ +/* Constants for Myson PHY */ +/* ------------------------------------------------------------------------- */ +#define MysonPHYID 0xd0000302 +/* 89-7-27 add, (begin) */ +#define MysonPHYID0 0x0302 +#define StatusRegister 18 +#define SPEED100 0x0400 // bit10 +#define FULLMODE 0x0800 // bit11 +/* 89-7-27 add, (end) */ + +/* ------------------------------------------------------------------------- */ +/* Constants for Seeq 80225 PHY */ +/* ------------------------------------------------------------------------- */ +#define SeeqPHYID0 0x0016 + +#define MIIRegister18 18 +#define SPD_DET_100 0x80 +#define DPLX_DET_FULL 0x40 + +/* ------------------------------------------------------------------------- */ +/* Constants for Ahdoc 101 PHY */ +/* ------------------------------------------------------------------------- */ +#define AhdocPHYID0 0x0022 + +#define DiagnosticReg 18 +#define DPLX_FULL 0x0800 +#define Speed_100 0x0400 + +/* 89/6/13 add, */ +/* -------------------------------------------------------------------------- */ +/* Constants */ +/* -------------------------------------------------------------------------- */ +#define MarvellPHYID0 0x0141 +#define LevelOnePHYID0 0x0013 + +#define MII1000BaseTControlReg 9 +#define MII1000BaseTStatusReg 10 +#define SpecificReg 17 + +/* for 1000BaseT Control Register */ +#define PHYAbletoPerform1000FullDuplex 0x0200 +#define PHYAbletoPerform1000HalfDuplex 0x0100 +#define PHY1000AbilityMask 0x300 + +// for phy specific status register, marvell phy. +#define SpeedMask 0x0c000 +#define Speed_1000M 0x08000 +#define Speed_100M 0x4000 +#define Speed_10M 0 +#define Full_Duplex 0x2000 + +// 89/12/29 add, for phy specific status register, levelone phy, (begin) +#define LXT1000_100M 0x08000 +#define LXT1000_1000M 0x0c000 +#define LXT1000_Full 0x200 +// 89/12/29 add, for phy specific status register, levelone phy, (end) + +#if 0 +/* for 3-in-1 case */ +#define PS10 0x00080000 +#define FD 0x00100000 +#define PS1000 0x00010000 +#endif + +/* for PHY */ +#define LinkIsUp 0x0004 +#define LinkIsUp2 0x00040000 + +/* Create a static buffer of size PKT_BUF_SZ for each +RX and TX Descriptor. All descriptors point to a +part of this buffer */ +struct { + u8 txb[PKT_BUF_SZ * TX_RING_SIZE] __attribute__ ((aligned(8))); + u8 rxb[PKT_BUF_SZ * RX_RING_SIZE] __attribute__ ((aligned(8))); +} mtd80x_bufs __shared; +#define txb mtd80x_bufs.txb +#define rxb mtd80x_bufs.rxb + +/* The Tulip Rx and Tx buffer descriptors. */ +struct mtd_desc +{ + s32 status; + s32 control; + u32 buffer; + u32 next_desc; + struct mtd_desc *next_desc_logical; + u8* skbuff; + u32 reserved1; + u32 reserved2; +}; + +struct mtd_private +{ + struct mtd_desc rx_ring[RX_RING_SIZE]; + struct mtd_desc tx_ring[TX_RING_SIZE]; + + /* Frequently used values: keep some adjacent for cache effect. */ + int flags; + struct pci_dev *pci_dev; + unsigned long crvalue; + unsigned long bcrvalue; + /*unsigned long imrvalue;*/ + struct mtd_desc *cur_rx; + struct mtd_desc *lack_rxbuf; + int really_rx_count; + struct mtd_desc *cur_tx; + struct mtd_desc *cur_tx_copy; + int really_tx_count; + int free_tx_count; + unsigned int rx_buf_sz; /* Based on MTU+slack. */ + + /* These values are keep track of the transceiver/media in use. */ + unsigned int linkok; + unsigned int line_speed; + unsigned int duplexmode; + unsigned int default_port: + 4; /* Last dev->if_port value. */ + unsigned int PHYType; + + /* MII transceiver section. */ + int mii_cnt; /* MII device addresses. */ + unsigned char phys[1]; /* MII device addresses. */ + + /*other*/ + const char *nic_name; + int ioaddr; + u16 dev_id; +}; + +static struct mtd_private mtdx; + +static int mdio_read(struct nic * , int phy_id, int location); +static void getlinktype(struct nic * ); +static void getlinkstatus(struct nic * ); +static void set_rx_mode(struct nic *); + +/************************************************************************** + * init_ring - setup the tx and rx descriptors + *************************************************************************/ +static void init_ring(struct nic *nic __unused) +{ + int i; + + mtdx.cur_rx = &mtdx.rx_ring[0]; + + mtdx.rx_buf_sz = PKT_BUF_SZ; + /*mtdx.rx_head_desc = &mtdx.rx_ring[0];*/ + + /* Initialize all Rx descriptors. */ + /* Fill in the Rx buffers. Handle allocation failure gracefully. */ + for (i = 0; i < RX_RING_SIZE; i++) + { + mtdx.rx_ring[i].status = RXOWN; + mtdx.rx_ring[i].control = mtdx.rx_buf_sz << RBSShift; + mtdx.rx_ring[i].next_desc = virt_to_le32desc(&mtdx.rx_ring[i+1]); + mtdx.rx_ring[i].next_desc_logical = &mtdx.rx_ring[i+1]; + mtdx.rx_ring[i].buffer = virt_to_le32desc(&rxb[i * PKT_BUF_SZ]); + mtdx.rx_ring[i].skbuff = &rxb[i * PKT_BUF_SZ]; + } + /* Mark the last entry as wrapping the ring. */ + mtdx.rx_ring[i-1].next_desc = virt_to_le32desc(&mtdx.rx_ring[0]); + mtdx.rx_ring[i-1].next_desc_logical = &mtdx.rx_ring[0]; + + /* We only use one transmit buffer, but two + * descriptors so transmit engines have somewhere + * to point should they feel the need */ + mtdx.tx_ring[0].status = 0x00000000; + mtdx.tx_ring[0].buffer = virt_to_bus(&txb[0]); + mtdx.tx_ring[0].next_desc = virt_to_le32desc(&mtdx.tx_ring[1]); + + /* This descriptor is never used */ + mtdx.tx_ring[1].status = 0x00000000; + mtdx.tx_ring[1].buffer = 0; /*virt_to_bus(&txb[1]); */ + mtdx.tx_ring[1].next_desc = virt_to_le32desc(&mtdx.tx_ring[0]); + + return; +} + +/************************************************************************** +RESET - Reset Adapter +***************************************************************************/ +static void mtd_reset( struct nic *nic ) +{ + /* Reset the chip to erase previous misconfiguration. */ + outl(0x00000001, mtdx.ioaddr + BCR); + + init_ring(nic); + + outl(virt_to_bus(mtdx.rx_ring), mtdx.ioaddr + RXLBA); + outl(virt_to_bus(mtdx.tx_ring), mtdx.ioaddr + TXLBA); + + /* Initialize other registers. */ + /* Configure the PCI bus bursts and FIFO thresholds. */ + mtdx.bcrvalue = 0x10; /* little-endian, 8 burst length */ + mtdx.crvalue = 0xa00; /* rx 128 burst length */ + + if ( mtdx.dev_id == 0x891 ) { + mtdx.bcrvalue |= 0x200; /* set PROG bit */ + mtdx.crvalue |= 0x02000000; /* set enhanced bit */ + } + + outl( mtdx.bcrvalue, mtdx.ioaddr + BCR); + + /* Restart Rx engine if stopped. */ + outl(0, mtdx.ioaddr + RXPDR); + + getlinkstatus(nic); + if (mtdx.linkok) + { + static const char* texts[]={"half","full","10","100","1000"}; + getlinktype(nic); + DBG ( "Link is OK : %s %s\n", texts[mtdx.duplexmode-1], texts[mtdx.line_speed+1] ); + } else + { + DBG ( "No link!!!\n" ); + } + + mtdx.crvalue |= /*TxEnable |*/ RxEnable | TxThreshold; + set_rx_mode(nic); + + /* Clear interrupts by setting the interrupt mask. */ + outl(FBE | TUNF | CNTOVF | RBU | TI | RI, mtdx.ioaddr + ISR); + outl( 0, mtdx.ioaddr + IMR); +} + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int mtd_poll(struct nic *nic, __unused int retrieve) +{ + s32 rx_status = mtdx.cur_rx->status; + int retval = 0; + + if( ( rx_status & RXOWN ) != 0 ) + { + return 0; + } + + if (rx_status & ErrorSummary) + { /* there was a fatal error */ + printf( "%s: Receive error, Rx status %8.8x, Error(s) %s%s%s\n", + mtdx.nic_name, (unsigned int) rx_status, + (rx_status & (LONG | RUNT)) ? "length_error ":"", + (rx_status & RXER) ? "frame_error ":"", + (rx_status & CRC) ? "crc_error ":"" ); + retval = 0; + } else if( !((rx_status & RXFSD) && (rx_status & RXLSD)) ) + { + /* this pkt is too long, over one rx buffer */ + printf("Pkt is too long, over one rx buffer.\n"); + retval = 0; + } else + { /* this received pkt is ok */ + /* Omit the four octet CRC from the length. */ + short pkt_len = ((rx_status & FLNGMASK) >> FLNGShift) - 4; + + DBG ( " netdev_rx() normal Rx pkt length %d" + " status %x.\n", pkt_len, (unsigned int) rx_status ); + + nic->packetlen = pkt_len; + memcpy(nic->packet, mtdx.cur_rx->skbuff, pkt_len); + + retval = 1; + } + + while( ( mtdx.cur_rx->status & RXOWN ) == 0 ) + { + mtdx.cur_rx->status = RXOWN; + mtdx.cur_rx = mtdx.cur_rx->next_desc_logical; + } + + /* Restart Rx engine if stopped. */ + outl(0, mtdx.ioaddr + RXPDR); + + return retval; +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static void mtd_transmit( + struct nic *nic, + const char *dest, /* Destination */ + unsigned int type, /* Type */ + unsigned int size, /* size */ + const char *data) /* Packet */ +{ + u32 to; + u32 tx_status; + unsigned int nstype = htons ( type ); + + memcpy( txb, dest, ETH_ALEN ); + memcpy( txb + ETH_ALEN, nic->node_addr, ETH_ALEN ); + memcpy( txb + 2 * ETH_ALEN, &nstype, 2 ); + memcpy( txb + ETH_HLEN, data, size ); + + size += ETH_HLEN; + size &= 0x0FFF; + while( size < ETH_ZLEN ) + { + txb[size++] = '\0'; + } + + mtdx.tx_ring[0].control = TXLD | TXFD | CRCEnable | PADEnable; + mtdx.tx_ring[0].control |= (size << PKTSShift); /* pkt size */ + mtdx.tx_ring[0].control |= (size << TBSShift); /* buffer size */ + mtdx.tx_ring[0].status = TXOWN; + + /* Point to transmit descriptor */ + outl(virt_to_bus(mtdx.tx_ring), mtdx.ioaddr + TXLBA); + /* Enable Tx */ + outl( mtdx.crvalue | TxEnable, mtdx.ioaddr + TCRRCR); + /* Wake the potentially-idle transmit channel. */ + outl(0, mtdx.ioaddr + TXPDR); + + to = currticks() + TX_TIME_OUT; + while(( mtdx.tx_ring[0].status & TXOWN) && (currticks() < to)); + + /* Disable Tx */ + outl( mtdx.crvalue & (~TxEnable), mtdx.ioaddr + TCRRCR); + + tx_status = mtdx.tx_ring[0].status; + if (currticks() >= to){ + DBG ( "TX Time Out" ); + } else if( tx_status & (CSL | LC | EC | UDF | HF)){ + printf( "Transmit error: %8.8x %s %s %s %s %s\n", + (unsigned int) tx_status, + tx_status & EC ? "abort" : "", + tx_status & CSL ? "carrier" : "", + tx_status & LC ? "late" : "", + tx_status & UDF ? "fifo" : "", + tx_status & HF ? "heartbeat" : "" ); + } + + /*hex_dump( txb, size );*/ + /*pause();*/ + + DBG ( "TRANSMIT\n" ); +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void mtd_disable ( struct nic *nic ) { + + /* Disable Tx Rx*/ + outl( mtdx.crvalue & (~TxEnable) & (~RxEnable), mtdx.ioaddr + TCRRCR ); + + /* Reset the chip to erase previous misconfiguration. */ + mtd_reset(nic); + + DBG ( "DISABLE\n" ); +} + +static struct nic_operations mtd_operations = { + .connect = dummy_connect, + .poll = mtd_poll, + .transmit = mtd_transmit, + .irq = dummy_irq, + +}; + +static struct pci_device_id mtd80x_nics[] = { + PCI_ROM(0x1516, 0x0800, "MTD800", "Myson MTD800"), + PCI_ROM(0x1516, 0x0803, "MTD803", "Surecom EP-320X"), + PCI_ROM(0x1516, 0x0891, "MTD891", "Myson MTD891"), +}; + +PCI_DRIVER ( mtd80x_driver, mtd80x_nics, PCI_NO_CLASS ); + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ + +static int mtd_probe ( struct nic *nic, struct pci_device *pci ) { + + int i; + + if (pci->ioaddr == 0) + return 0; + + adjust_pci_device(pci); + + nic->ioaddr = pci->ioaddr; + nic->irqno = 0; + + mtdx.nic_name = pci->driver_name; + mtdx.dev_id = pci->device; + mtdx.ioaddr = nic->ioaddr; + + /* read ethernet id */ + for (i = 0; i < 6; ++i) + { + nic->node_addr[i] = inb(mtdx.ioaddr + PAR0 + i); + } + + if (memcmp(nic->node_addr, "\0\0\0\0\0\0", 6) == 0) + { + return 0; + } + + DBG ( "%s: ioaddr %4.4x MAC %s\n", mtdx.nic_name, mtdx.ioaddr, eth_ntoa ( nic->node_addr ) ); + + /* Reset the chip to erase previous misconfiguration. */ + outl(0x00000001, mtdx.ioaddr + BCR); + + /* find the connected MII xcvrs */ + + if( mtdx.dev_id != 0x803 ) + { + int phy, phy_idx = 0; + + for (phy = 1; phy < 32 && phy_idx < 1; phy++) { + int mii_status = mdio_read(nic, phy, 1); + + if (mii_status != 0xffff && mii_status != 0x0000) { + mtdx.phys[phy_idx] = phy; + + DBG ( "%s: MII PHY found at address %d, status " + "0x%4.4x.\n", mtdx.nic_name, phy, mii_status ); + /* get phy type */ + { + unsigned int data; + + data = mdio_read(nic, mtdx.phys[phy_idx], 2); + if (data == SeeqPHYID0) + mtdx.PHYType = SeeqPHY; + else if (data == AhdocPHYID0) + mtdx.PHYType = AhdocPHY; + else if (data == MarvellPHYID0) + mtdx.PHYType = MarvellPHY; + else if (data == MysonPHYID0) + mtdx.PHYType = Myson981; + else if (data == LevelOnePHYID0) + mtdx.PHYType = LevelOnePHY; + else + mtdx.PHYType = OtherPHY; + } + phy_idx++; + } + } + + mtdx.mii_cnt = phy_idx; + if (phy_idx == 0) { + printf("%s: MII PHY not found -- this device may " + "not operate correctly.\n", mtdx.nic_name); + } + } else { + mtdx.phys[0] = 32; + /* get phy type */ + if (inl(mtdx.ioaddr + PHYIDENTIFIER) == MysonPHYID ) { + mtdx.PHYType = MysonPHY; + DBG ( "MysonPHY\n" ); + } else { + mtdx.PHYType = OtherPHY; + DBG ( "OtherPHY\n" ); + } + } + + getlinkstatus(nic); + if( !mtdx.linkok ) + { + printf("No link!!!\n"); + return 0; + } + + mtd_reset( nic ); + + /* point to NIC specific routines */ + nic->nic_op = &mtd_operations; + return 1; +} + + +/**************************************************************************/ +static void set_rx_mode(struct nic *nic __unused) +{ + u32 mc_filter[2]; /* Multicast hash filter */ + u32 rx_mode; + + /* Too many to match, or accept all multicasts. */ + mc_filter[1] = mc_filter[0] = ~0; + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + + outl(mc_filter[0], mtdx.ioaddr + MAR0); + outl(mc_filter[1], mtdx.ioaddr + MAR1); + + mtdx.crvalue = ( mtdx.crvalue & ~RxModeMask ) | rx_mode; + outb( mtdx.crvalue, mtdx.ioaddr + TCRRCR); +} +/**************************************************************************/ +static unsigned int m80x_read_tick(void) +/* function: Reads the Timer tick count register which decrements by 2 from */ +/* 65536 to 0 every 1/36.414 of a second. Each 2 decrements of the */ +/* count represents 838 nsec's. */ +/* input : none. */ +/* output : none. */ +{ + unsigned char tmp; + int value; + + outb((char) 0x06, 0x43); // Command 8254 to latch T0's count + + // now read the count. + tmp = (unsigned char) inb(0x40); + value = ((int) tmp) << 8; + tmp = (unsigned char) inb(0x40); + value |= (((int) tmp) & 0xff); + return (value); +} + +static void m80x_delay(unsigned int interval) +/* function: to wait for a specified time. */ +/* input : interval ... the specified time. */ +/* output : none. */ +{ + unsigned int interval1, interval2, i = 0; + + interval1 = m80x_read_tick(); // get initial value + do + { + interval2 = m80x_read_tick(); + if (interval1 < interval2) + interval1 += 65536; + ++i; + } while (((interval1 - interval2) < (u16) interval) && (i < 65535)); +} + + +static u32 m80x_send_cmd_to_phy(long miiport, int opcode, int phyad, int regad) +{ + u32 miir; + int i; + unsigned int mask, data; + + /* enable MII output */ + miir = (u32) inl(miiport); + miir &= 0xfffffff0; + + miir |= MASK_MIIR_MII_WRITE + MASK_MIIR_MII_MDO; + + /* send 32 1's preamble */ + for (i = 0; i < 32; i++) { + /* low MDC; MDO is already high (miir) */ + miir &= ~MASK_MIIR_MII_MDC; + outl(miir, miiport); + + /* high MDC */ + miir |= MASK_MIIR_MII_MDC; + outl(miir, miiport); + } + + /* calculate ST+OP+PHYAD+REGAD+TA */ + data = opcode | (phyad << 7) | (regad << 2); + + /* sent out */ + mask = 0x8000; + while (mask) { + /* low MDC, prepare MDO */ + miir &= ~(MASK_MIIR_MII_MDC + MASK_MIIR_MII_MDO); + if (mask & data) + miir |= MASK_MIIR_MII_MDO; + + outl(miir, miiport); + /* high MDC */ + miir |= MASK_MIIR_MII_MDC; + outl(miir, miiport); + m80x_delay(30); + + /* next */ + mask >>= 1; + if (mask == 0x2 && opcode == OP_READ) + miir &= ~MASK_MIIR_MII_WRITE; + } + return miir; +} + +static int mdio_read(struct nic *nic __unused, int phyad, int regad) +{ + long miiport = mtdx.ioaddr + MANAGEMENT; + u32 miir; + unsigned int mask, data; + + miir = m80x_send_cmd_to_phy(miiport, OP_READ, phyad, regad); + + /* read data */ + mask = 0x8000; + data = 0; + while (mask) + { + /* low MDC */ + miir &= ~MASK_MIIR_MII_MDC; + outl(miir, miiport); + + /* read MDI */ + miir = inl(miiport); + if (miir & MASK_MIIR_MII_MDI) + data |= mask; + + /* high MDC, and wait */ + miir |= MASK_MIIR_MII_MDC; + outl(miir, miiport); + m80x_delay((int) 30); + + /* next */ + mask >>= 1; + } + + /* low MDC */ + miir &= ~MASK_MIIR_MII_MDC; + outl(miir, miiport); + + return data & 0xffff; +} + +#if 0 /* not used */ +static void mdio_write(struct nic *nic __unused, int phyad, int regad, + int data) +{ + long miiport = mtdx.ioaddr + MANAGEMENT; + u32 miir; + unsigned int mask; + + miir = m80x_send_cmd_to_phy(miiport, OP_WRITE, phyad, regad); + + /* write data */ + mask = 0x8000; + while (mask) + { + /* low MDC, prepare MDO */ + miir &= ~(MASK_MIIR_MII_MDC + MASK_MIIR_MII_MDO); + if (mask & data) + miir |= MASK_MIIR_MII_MDO; + outl(miir, miiport); + + /* high MDC */ + miir |= MASK_MIIR_MII_MDC; + outl(miir, miiport); + + /* next */ + mask >>= 1; + } + + /* low MDC */ + miir &= ~MASK_MIIR_MII_MDC; + outl(miir, miiport); + + return; +} +#endif + +static void getlinkstatus(struct nic *nic) +/* function: Routine will read MII Status Register to get link status. */ +/* input : dev... pointer to the adapter block. */ +/* output : none. */ +{ + unsigned int i, DelayTime = 0x1000; + + mtdx.linkok = 0; + + if (mtdx.PHYType == MysonPHY) + { + for (i = 0; i < DelayTime; ++i) { + if (inl(mtdx.ioaddr + BMCRSR) & LinkIsUp2) { + mtdx.linkok = 1; + return; + } + // delay + m80x_delay(100); + } + } else + { + for (i = 0; i < DelayTime; ++i) { + if (mdio_read(nic, mtdx.phys[0], MII_BMSR) & BMSR_LSTATUS) { + mtdx.linkok = 1; + return; + } + // delay + m80x_delay(100); + } + } +} + + +static void getlinktype(struct nic *dev) +{ + if (mtdx.PHYType == MysonPHY) + { /* 3-in-1 case */ + if (inl(mtdx.ioaddr + TCRRCR) & FD) + mtdx.duplexmode = 2; /* full duplex */ + else + mtdx.duplexmode = 1; /* half duplex */ + if (inl(mtdx.ioaddr + TCRRCR) & PS10) + mtdx.line_speed = 1; /* 10M */ + else + mtdx.line_speed = 2; /* 100M */ + } else + { + if (mtdx.PHYType == SeeqPHY) { /* this PHY is SEEQ 80225 */ + unsigned int data; + + data = mdio_read(dev, mtdx.phys[0], MIIRegister18); + if (data & SPD_DET_100) + mtdx.line_speed = 2; /* 100M */ + else + mtdx.line_speed = 1; /* 10M */ + if (data & DPLX_DET_FULL) + mtdx.duplexmode = 2; /* full duplex mode */ + else + mtdx.duplexmode = 1; /* half duplex mode */ + } else if (mtdx.PHYType == AhdocPHY) { + unsigned int data; + + data = mdio_read(dev, mtdx.phys[0], DiagnosticReg); + if (data & Speed_100) + mtdx.line_speed = 2; /* 100M */ + else + mtdx.line_speed = 1; /* 10M */ + if (data & DPLX_FULL) + mtdx.duplexmode = 2; /* full duplex mode */ + else + mtdx.duplexmode = 1; /* half duplex mode */ + } + /* 89/6/13 add, (begin) */ + else if (mtdx.PHYType == MarvellPHY) { + unsigned int data; + + data = mdio_read(dev, mtdx.phys[0], SpecificReg); + if (data & Full_Duplex) + mtdx.duplexmode = 2; /* full duplex mode */ + else + mtdx.duplexmode = 1; /* half duplex mode */ + data &= SpeedMask; + if (data == Speed_1000M) + mtdx.line_speed = 3; /* 1000M */ + else if (data == Speed_100M) + mtdx.line_speed = 2; /* 100M */ + else + mtdx.line_speed = 1; /* 10M */ + } + /* 89/6/13 add, (end) */ + /* 89/7/27 add, (begin) */ + else if (mtdx.PHYType == Myson981) { + unsigned int data; + + data = mdio_read(dev, mtdx.phys[0], StatusRegister); + + if (data & SPEED100) + mtdx.line_speed = 2; + else + mtdx.line_speed = 1; + + if (data & FULLMODE) + mtdx.duplexmode = 2; + else + mtdx.duplexmode = 1; + } + /* 89/7/27 add, (end) */ + /* 89/12/29 add */ + else if (mtdx.PHYType == LevelOnePHY) { + unsigned int data; + + data = mdio_read(dev, mtdx.phys[0], SpecificReg); + if (data & LXT1000_Full) + mtdx.duplexmode = 2; /* full duplex mode */ + else + mtdx.duplexmode = 1; /* half duplex mode */ + data &= SpeedMask; + if (data == LXT1000_1000M) + mtdx.line_speed = 3; /* 1000M */ + else if (data == LXT1000_100M) + mtdx.line_speed = 2; /* 100M */ + else + mtdx.line_speed = 1; /* 10M */ + } + // chage crvalue + // mtdx.crvalue&=(~PS10)&(~FD); + mtdx.crvalue &= (~PS10) & (~FD) & (~PS1000); + if (mtdx.line_speed == 1) + mtdx.crvalue |= PS10; + else if (mtdx.line_speed == 3) + mtdx.crvalue |= PS1000; + if (mtdx.duplexmode == 2) + mtdx.crvalue |= FD; + } +} + +DRIVER ( "MTD80X", nic_driver, pci_driver, mtd80x_driver, + mtd_probe, mtd_disable ); diff --git a/gpxe/src/drivers/net/mtnic.c b/gpxe/src/drivers/net/mtnic.c new file mode 100644 index 0000000..0d84a44 --- /dev/null +++ b/gpxe/src/drivers/net/mtnic.c @@ -0,0 +1,1850 @@ +/* + * Copyright (c) 2007 Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mtnic.h" + + +/* + + + mtnic.c - gPXE driver for Mellanox 10Gig ConnectX EN + + +*/ + + + +/******************************************************************** +* +* MTNIC allocation functions +* +*********************************************************************/ +/** +* mtnic_alloc_aligned +* +* @v unsigned int size size +* @v void **va virtual address +* @v u32 *pa physical address +* @v u32 aligment aligment +* +* Function allocate aligned buffer and put it's virtual address in 'va' +* and it's physical aligned address in 'pa' +*/ +static int +mtnic_alloc_aligned(unsigned int size, void **va, unsigned long *pa, unsigned int alignment) +{ + *va = alloc_memblock(size, alignment); + if (!*va) { + return -EADDRINUSE; + } + *pa = (u32)virt_to_bus(*va); + return 0; +} + + + +/** + * + * mtnic alloc command interface + * + */ +static int +mtnic_alloc_cmdif(struct mtnic *mtnic) +{ + u32 bar = mtnic_pci_dev.dev.bar[0]; + + mtnic->hcr = ioremap(bar + MTNIC_HCR_BASE, MTNIC_HCR_SIZE); + if ( !mtnic->hcr ) { + DBG("Couldn't map command register\n"); + return -EADDRINUSE; + } + mtnic_alloc_aligned(PAGE_SIZE, (void *)&mtnic->cmd.buf, &mtnic->cmd.mapping, PAGE_SIZE); + if ( !mtnic->cmd.buf ) { + DBG("Error in allocating buffer for command interface\n"); + return -EADDRINUSE; + } + return 0; +} + +/** + * Free RX io buffers + */ +static void +mtnic_free_io_buffers(struct mtnic_ring *ring) +{ + int index; + + for (; ring->cons <= ring->prod; ++ring->cons) { + index = ring->cons & ring->size_mask; + if ( ring->iobuf[index] ) { + free_iob(ring->iobuf[index]); + } + } +} + + + +/** + * + * mtnic alloc and attach io buffers + * + */ +static int +mtnic_alloc_iobuf(struct mtnic_port *priv, struct mtnic_ring *ring, + unsigned int size) +{ + struct mtnic_rx_desc *rx_desc_ptr = ring->buf; + u32 index; + + while ((u32)(ring->prod - ring->cons) < UNITS_BUFFER_SIZE) { + index = ring->prod & ring->size_mask; + ring->iobuf[index] = alloc_iob(size); + if (!ring->iobuf[index]) { + if (ring->prod <= (ring->cons + 1)) { + DBG ( "Dropping packet, buffer is full\n" ); + } + break; + } + + /* Attach io_buffer to descriptor */ + rx_desc_ptr = ring->buf + + (sizeof(struct mtnic_rx_desc) * index); + rx_desc_ptr->data.count = cpu_to_be32(size); + rx_desc_ptr->data.mem_type = priv->mtnic->fw.mem_type_snoop_be; + rx_desc_ptr->data.addr_l = cpu_to_be32( + virt_to_bus(ring->iobuf[index]->data)); + + ++ ring->prod; + } + + /* Update RX producer index (PI) */ + ring->db->count = cpu_to_be32(ring->prod & 0xffff); + return 0; +} + + +/** + * mtnic alloc ring + * + * Alloc and configure TX or RX ring + * + */ +static int +mtnic_alloc_ring(struct mtnic_port *priv, struct mtnic_ring *ring, + u32 size, u16 stride, u16 cq, u8 is_rx) +{ + unsigned int i; + int err; + struct mtnic_rx_desc *rx_desc; + struct mtnic_tx_desc *tx_desc; + + ring->size = size; /* Number of descriptors */ + ring->size_mask = size - 1; + ring->stride = stride; /* Size of each entry */ + ring->cq = cq; /* CQ number associated with this ring */ + ring->cons = 0; + ring->prod = 0; + + /* Alloc descriptors buffer */ + ring->buf_size = ring->size * ((is_rx) ? sizeof(struct mtnic_rx_desc) : + sizeof(struct mtnic_tx_desc)); + err = mtnic_alloc_aligned(ring->buf_size, (void *)&ring->buf, + &ring->dma, PAGE_SIZE); + if (err) { + DBG("Failed allocating descriptor ring sizeof %x\n", + ring->buf_size); + return -EADDRINUSE; + } + memset(ring->buf, 0, ring->buf_size); + + DBG("Allocated %s ring (addr:%p) - buf:%p size:%x" + "buf_size:%x dma:%lx\n", + is_rx ? "Rx" : "Tx", ring, ring->buf, ring->size, + ring->buf_size, ring->dma); + + + if (is_rx) { /* RX ring */ + /* Alloc doorbell */ + err = mtnic_alloc_aligned(sizeof(struct mtnic_cq_db_record), + (void *)&ring->db, &ring->db_dma, 32); + if (err) { + DBG("Failed allocating Rx ring doorbell record\n"); + free_memblock(ring->buf, ring->buf_size); + return -EADDRINUSE; + } + + /* ==- Configure Descriptor -== */ + /* Init ctrl seg of rx desc */ + for (i = 0; i < UNITS_BUFFER_SIZE; ++i) { + rx_desc = ring->buf + + (sizeof(struct mtnic_rx_desc) * i); + /* Pre-link descriptor */ + rx_desc->next = cpu_to_be16(i + 1); + } + /*The last ctrl descriptor is '0' and points to the first one*/ + + /* Alloc IO_BUFFERS */ + err = mtnic_alloc_iobuf ( priv, ring, DEF_IOBUF_SIZE ); + if (err) { + DBG("ERROR Allocating io buffer\n"); + free_memblock(ring->buf, ring->buf_size); + return -EADDRINUSE; + } + + } else { /* TX ring */ + /* Set initial ownership of all Tx Desc' to SW (1) */ + for (i = 0; i < ring->size; i++) { + tx_desc = ring->buf + ring->stride * i; + tx_desc->ctrl.op_own = cpu_to_be32(MTNIC_BIT_DESC_OWN); + } + /* DB */ + ring->db_offset = cpu_to_be32( + ((u32) priv->mtnic->fw.tx_offset[priv->port]) << 8); + + /* Map Tx+CQ doorbells */ + DBG("Mapping TxCQ doorbell at offset:0x%x\n", + priv->mtnic->fw.txcq_db_offset); + ring->txcq_db = ioremap(mtnic_pci_dev.dev.bar[2] + + priv->mtnic->fw.txcq_db_offset, PAGE_SIZE); + if (!ring->txcq_db) { + DBG("Couldn't map txcq doorbell, aborting...\n"); + free_memblock(ring->buf, ring->buf_size); + return -EADDRINUSE; + } + } + + return 0; +} + + + +/** + * mtnic alloc CQ + * + * Alloc and configure CQ. + * + */ +static int +mtnic_alloc_cq(struct net_device *dev, int num, struct mtnic_cq *cq, + u8 is_rx, u32 size, u32 offset_ind) +{ + int err ; + unsigned int i; + + cq->num = num; + cq->dev = dev; + cq->size = size; + cq->last = 0; + cq->is_rx = is_rx; + cq->offset_ind = offset_ind; + + /* Alloc doorbell */ + err = mtnic_alloc_aligned(sizeof(struct mtnic_cq_db_record), + (void *)&cq->db, &cq->db_dma, 32); + if (err) { + DBG("Failed allocating CQ doorbell record\n"); + return -EADDRINUSE; + } + memset(cq->db, 0, sizeof(struct mtnic_cq_db_record)); + + /* Alloc CQEs buffer */ + cq->buf_size = size * sizeof(struct mtnic_cqe); + err = mtnic_alloc_aligned(cq->buf_size, + (void *)&cq->buf, &cq->dma, PAGE_SIZE); + if (err) { + DBG("Failed allocating CQ buffer\n"); + free_memblock(cq->db, sizeof(struct mtnic_cq_db_record)); + return -EADDRINUSE; + } + memset(cq->buf, 0, cq->buf_size); + DBG("Allocated CQ (addr:%p) - size:%x buf:%p buf_size:%x " + "dma:%lx db:%p db_dma:%lx\n" + "cqn offset:%x \n", cq, cq->size, cq->buf, + cq->buf_size, cq->dma, cq->db, + cq->db_dma, offset_ind); + + + /* Set ownership of all CQEs to HW */ + DBG("Setting HW ownership for CQ:%d\n", num); + for (i = 0; i < cq->size; i++) { + /* Initial HW ownership is 1 */ + cq->buf[i].op_tr_own = MTNIC_BIT_CQ_OWN; + } + return 0; +} + + + +/** + * mtnic_alloc_resources + * + * Alloc and configure CQs, Tx, Rx + */ +unsigned int +mtnic_alloc_resources(struct net_device *dev) +{ + struct mtnic_port *priv = netdev_priv(dev); + int err; + int cq_ind = 0; + int cq_offset = priv->mtnic->fw.cq_offset; + + /* Alloc 1st CQ */ + err = mtnic_alloc_cq(dev, cq_ind, &priv->cq[cq_ind], 1 /* RX */, + UNITS_BUFFER_SIZE, cq_offset + cq_ind); + if (err) { + DBG("Failed allocating Rx CQ\n"); + return -EADDRINUSE; + } + + + /* Alloc RX */ + err = mtnic_alloc_ring(priv, &priv->rx_ring, UNITS_BUFFER_SIZE, + sizeof(struct mtnic_rx_desc), cq_ind, /* RX */1); + if (err) { + DBG("Failed allocating Rx Ring\n"); + goto cq0_error; + } + + + ++cq_ind; + + /* alloc 2nd CQ */ + err = mtnic_alloc_cq(dev, cq_ind, &priv->cq[cq_ind], 0 /* TX */, + UNITS_BUFFER_SIZE, cq_offset + cq_ind); + if (err) { + DBG("Failed allocating Tx CQ\n"); + goto rx_error; + } + + /* Alloc TX */ + err = mtnic_alloc_ring(priv, &priv->tx_ring, UNITS_BUFFER_SIZE, + sizeof(struct mtnic_tx_desc), cq_ind, /* TX */ 0); + if (err) { + DBG("Failed allocating Tx ring\n"); + goto cq1_error; + } + + return 0; + +cq1_error: + free_memblock(priv->cq[1].buf, priv->cq[1].buf_size); + free_memblock(priv->cq[1].db, sizeof(struct mtnic_cq_db_record)); + +rx_error: + free_memblock(priv->rx_ring.buf, priv->rx_ring.buf_size); + free_memblock(priv->rx_ring.db, sizeof(struct mtnic_cq_db_record)); + mtnic_free_io_buffers(&priv->rx_ring); +cq0_error: + free_memblock(priv->cq[0].buf, priv->cq[0].buf_size); + free_memblock(priv->cq[0].db, sizeof(struct mtnic_cq_db_record)); + + return -EADDRINUSE; +} + + +/** + * mtnic alloc_eq + * + * Note: EQ is not used by the driver but must be allocated + */ +static int +mtnic_alloc_eq(struct mtnic *mtnic) +{ + int err; + unsigned int i; + struct mtnic_eqe *eqe_desc = NULL; + + /* Allocating doorbell */ + mtnic->eq_db = ioremap(mtnic_pci_dev.dev.bar[2] + + mtnic->fw.eq_db_offset, sizeof(u32)); + if (!mtnic->eq_db) { + DBG("Couldn't map EQ doorbell, aborting...\n"); + return -EADDRINUSE; + } + + /* Allocating buffer */ + mtnic->eq.size = NUM_EQES; + mtnic->eq.buf_size = mtnic->eq.size * sizeof(struct mtnic_eqe); + err = mtnic_alloc_aligned(mtnic->eq.buf_size, (void *)&mtnic->eq.buf, + &mtnic->eq.dma, PAGE_SIZE); + if (err) { + DBG("Failed allocating EQ buffer\n"); + iounmap(mtnic->eq_db); + return -EADDRINUSE; + } + memset(mtnic->eq.buf, 0, mtnic->eq.buf_size); + + for (i = 0; i < mtnic->eq.size; i++) + eqe_desc = mtnic->eq.buf + (sizeof(struct mtnic_eqe) * i); + eqe_desc->own |= MTNIC_BIT_EQE_OWN; + + mdelay(20); + return 0; +} + + + + + + + + + + + +/******************************************************************** +* +* Mtnic commands functions +* -=-=-=-=-=-=-=-=-=-=-=-= +* +* +* +*********************************************************************/ +static inline int +cmdif_go_bit(struct mtnic *mtnic) +{ + struct mtnic_if_cmd_reg *hcr = mtnic->hcr; + u32 status; + int i; + + for (i = 0; i < TBIT_RETRIES; i++) { + status = be32_to_cpu(readl(&hcr->status_go_opcode)); + if ((status & MTNIC_BC_MASK(MTNIC_MASK_CMD_REG_T_BIT)) == + (mtnic->cmd.tbit << MTNIC_BC_OFF(MTNIC_MASK_CMD_REG_T_BIT))) { + /* Read expected t-bit - now return go-bit value */ + return status & MTNIC_BC_MASK(MTNIC_MASK_CMD_REG_GO_BIT); + } + } + + DBG("Invalid tbit after %d retries!\n", TBIT_RETRIES); + return -EBUSY; /* Return busy... */ +} + +/* Base Command interface */ +static int +mtnic_cmd(struct mtnic *mtnic, void *in_imm, + void *out_imm, u32 in_modifier, u16 op) +{ + + struct mtnic_if_cmd_reg *hcr = mtnic->hcr; + int err = 0; + u32 out_param_h = 0; + u32 out_param_l = 0; + u32 in_param_h = 0; + u32 in_param_l = 0; + + + static u16 token = 0x8000; + u32 status; + unsigned int timeout = 0; + + token++; + + if ( cmdif_go_bit ( mtnic ) ) { + DBG("GO BIT BUSY:%p.\n", hcr + 6); + err = -EBUSY; + goto out; + } + if (in_imm) { + in_param_h = *((u32*)in_imm); + in_param_l = *((u32*)in_imm + 1); + } else { + in_param_l = cpu_to_be32(mtnic->cmd.mapping); + } + out_param_l = cpu_to_be32(mtnic->cmd.mapping); + + /* writing to MCR */ + writel(in_param_h, &hcr->in_param_h); + writel(in_param_l, &hcr->in_param_l); + writel((u32) cpu_to_be32(in_modifier), &hcr->input_modifier); + writel(out_param_h, &hcr->out_param_h); + writel(out_param_l, &hcr->out_param_l); + writel((u32)cpu_to_be32(token << 16), &hcr->token); + wmb(); + + /* flip toggle bit before each write to the HCR */ + mtnic->cmd.tbit = !mtnic->cmd.tbit; + writel( ( u32 ) + cpu_to_be32(MTNIC_BC_MASK(MTNIC_MASK_CMD_REG_GO_BIT) | + ( mtnic->cmd.tbit << MTNIC_BC_OFF ( MTNIC_MASK_CMD_REG_T_BIT ) ) | op ), + &hcr->status_go_opcode); + + while ( cmdif_go_bit ( mtnic ) && ( timeout <= GO_BIT_TIMEOUT ) ) { + mdelay ( 1 ); + ++timeout; + } + + if ( cmdif_go_bit ( mtnic ) ) { + DBG("Command opcode:0x%x token:0x%x TIMEOUT.\n", op, token); + err = -EBUSY; + goto out; + } + + if (out_imm) { + *((u32 *)out_imm) = readl(&hcr->out_param_h); + *((u32 *)out_imm + 1) = readl(&hcr->out_param_l); + } + + status = be32_to_cpu((u32)readl(&hcr->status_go_opcode)) >> 24; + + if (status) { + DBG("Command opcode:0x%x token:0x%x returned:0x%x\n", + op, token, status); + return status; + } + +out: + return err; +} + +/* MAP PAGES wrapper */ +static int +mtnic_map_cmd(struct mtnic *mtnic, u16 op, struct mtnic_pages pages) +{ + unsigned int j; + u32 addr; + unsigned int len; + u32 *page_arr = mtnic->cmd.buf; + int nent = 0; + int err = 0; + + memset(page_arr, 0, PAGE_SIZE); + + len = PAGE_SIZE * pages.num; + pages.buf = (u32 *)umalloc(PAGE_SIZE * (pages.num + 1)); + addr = PAGE_SIZE + ((virt_to_bus(pages.buf) & 0xfffff000) + PAGE_SIZE); + DBG("Mapping pages: size: %x address: %p\n", pages.num, pages.buf); + + if (addr & (PAGE_MASK)) { + DBG("Got FW area not aligned to %d (%llx/%x)\n", + PAGE_SIZE, (u64) addr, len); + return -EADDRINUSE; + } + + /* Function maps each PAGE seperately */ + for (j = 0; j < len; j+= PAGE_SIZE) { + page_arr[nent * 4 + 3] = cpu_to_be32(addr + j); + if (++nent == MTNIC_MAILBOX_SIZE / 16) { + err = mtnic_cmd(mtnic, NULL, NULL, nent, op); + if (err) + return -EIO; + nent = 0; + } + } + + if (nent) { + err = mtnic_cmd(mtnic, NULL, NULL, nent, op); + } + return err; +} + + + +/* + * Query FW + */ +static int +mtnic_QUERY_FW ( struct mtnic *mtnic ) +{ + int err; + struct mtnic_if_query_fw_out_mbox *cmd = mtnic->cmd.buf; + + err = mtnic_cmd(mtnic, NULL, NULL, 0, MTNIC_IF_CMD_QUERY_FW); + if (err) + return -EIO; + + /* Get FW and interface versions */ + mtnic->fw_ver = ((u64) be16_to_cpu(cmd->rev_maj) << 32) | + ((u64) be16_to_cpu(cmd->rev_min) << 16) | + (u64) be16_to_cpu(cmd->rev_smin); + mtnic->fw.ifc_rev = be16_to_cpu(cmd->ifc_rev); + + /* Get offset for internal error reports (debug) */ + mtnic->fw.err_buf.offset = be64_to_cpu(cmd->err_buf_start); + mtnic->fw.err_buf.size = be32_to_cpu(cmd->err_buf_size); + + DBG("Error buf offset is %llx\n", mtnic->fw.err_buf.offset); + + /* Get number of required FW (4k) pages */ + mtnic->fw.fw_pages.num = be16_to_cpu(cmd->fw_pages); + + return 0; +} + + +static int +mtnic_OPEN_NIC(struct mtnic *mtnic) +{ + struct mtnic_if_open_nic_in_mbox *open_nic = mtnic->cmd.buf; + u32 extra_pages[2] = {0}; + int err; + + memset(open_nic, 0, sizeof *open_nic); + + /* port 1 */ + open_nic->log_rx_p1 = 0; + open_nic->log_cq_p1 = 1; + + open_nic->log_tx_p1 = 0; + open_nic->steer_p1 = MTNIC_IF_STEER_RSS; + /* MAC + VLAN - leave reserved */ + + /* port 2 */ + open_nic->log_rx_p2 = 0; + open_nic->log_cq_p2 = 1; + + open_nic->log_tx_p2 = 0; + open_nic->steer_p2 = MTNIC_IF_STEER_RSS; + /* MAC + VLAN - leave reserved */ + + err = mtnic_cmd(mtnic, NULL, extra_pages, 0, MTNIC_IF_CMD_OPEN_NIC); + + mtnic->fw.extra_pages.num = be32_to_cpu(*(extra_pages+1)); + DBG("Extra pages num is %x\n", mtnic->fw.extra_pages.num); + return err; +} + +static int +mtnic_CONFIG_RX(struct mtnic *mtnic) +{ + struct mtnic_if_config_rx_in_imm config_rx; + + memset(&config_rx, 0, sizeof config_rx); + return mtnic_cmd(mtnic, &config_rx, NULL, 0, MTNIC_IF_CMD_CONFIG_RX); +} + +static int +mtnic_CONFIG_TX(struct mtnic *mtnic) +{ + struct mtnic_if_config_send_in_imm config_tx; + + config_tx.enph_gpf = 0; + return mtnic_cmd(mtnic, &config_tx, NULL, 0, MTNIC_IF_CMD_CONFIG_TX); +} + +static int +mtnic_HEART_BEAT(struct mtnic_port *priv, u32 *link_state) +{ + struct mtnic_if_heart_beat_out_imm heart_beat; + + int err; + u32 flags; + err = mtnic_cmd(priv->mtnic, NULL, &heart_beat, 0, MTNIC_IF_CMD_HEART_BEAT); + if (!err) { + flags = be32_to_cpu(heart_beat.flags); + if (flags & MTNIC_BC_MASK(MTNIC_MASK_HEAR_BEAT_INT_ERROR)) { + DBG("Internal error detected\n"); + return -EIO; + } + *link_state = flags & + ~((u32) MTNIC_BC_MASK(MTNIC_MASK_HEAR_BEAT_INT_ERROR)); + } + return err; +} + + +/* + * Port commands + */ + +static int +mtnic_SET_PORT_DEFAULT_RING(struct mtnic_port *priv, u8 port, u16 ring) +{ + struct mtnic_if_set_port_default_ring_in_imm def_ring; + + memset(&def_ring, 0, sizeof(def_ring)); + def_ring.ring = ring; + return mtnic_cmd(priv->mtnic, &def_ring, NULL, port + 1, + MTNIC_IF_CMD_SET_PORT_DEFAULT_RING); +} + +static int +mtnic_CONFIG_PORT_RSS_STEER(struct mtnic_port *priv, int port) +{ + memset(priv->mtnic->cmd.buf, 0, PAGE_SIZE); + return mtnic_cmd(priv->mtnic, NULL, NULL, port + 1, + MTNIC_IF_CMD_CONFIG_PORT_RSS_STEER); +} + +static int +mtnic_SET_PORT_RSS_INDIRECTION(struct mtnic_port *priv, int port) + +{ + memset(priv->mtnic->cmd.buf, 0, PAGE_SIZE); + return mtnic_cmd(priv->mtnic, NULL, NULL, port + 1, + MTNIC_IF_CMD_SET_PORT_RSS_INDIRECTION); +} + + +/* + * Config commands + */ +static int +mtnic_CONFIG_CQ(struct mtnic_port *priv, int port, + u16 cq_ind, struct mtnic_cq *cq) +{ + struct mtnic_if_config_cq_in_mbox *config_cq = priv->mtnic->cmd.buf; + + memset(config_cq, 0, sizeof *config_cq); + config_cq->cq = cq_ind; + config_cq->size = fls(UNITS_BUFFER_SIZE - 1); + config_cq->offset = ((cq->dma) & (PAGE_MASK)) >> 6; + config_cq->db_record_addr_l = cpu_to_be32(cq->db_dma); + config_cq->page_address[1] = cpu_to_be32(cq->dma); + DBG("config cq address: %x dma_address: %lx" + "offset: %d size %d index: %d\n" + , config_cq->page_address[1],cq->dma, + config_cq->offset, config_cq->size, config_cq->cq ); + + return mtnic_cmd(priv->mtnic, NULL, NULL, port + 1, + MTNIC_IF_CMD_CONFIG_CQ); +} + + +static int +mtnic_CONFIG_TX_RING(struct mtnic_port *priv, u8 port, + u16 ring_ind, struct mtnic_ring *ring) +{ + struct mtnic_if_config_send_ring_in_mbox *config_tx_ring = priv->mtnic->cmd.buf; + memset(config_tx_ring, 0, sizeof *config_tx_ring); + config_tx_ring->ring = cpu_to_be16(ring_ind); + config_tx_ring->size = fls(UNITS_BUFFER_SIZE - 1); + config_tx_ring->cq = cpu_to_be16(ring->cq); + config_tx_ring->page_address[1] = cpu_to_be32(ring->dma); + + return mtnic_cmd(priv->mtnic, NULL, NULL, port + 1, + MTNIC_IF_CMD_CONFIG_TX_RING); +} + +static int +mtnic_CONFIG_RX_RING(struct mtnic_port *priv, u8 port, + u16 ring_ind, struct mtnic_ring *ring) +{ + struct mtnic_if_config_rx_ring_in_mbox *config_rx_ring = priv->mtnic->cmd.buf; + memset(config_rx_ring, 0, sizeof *config_rx_ring); + config_rx_ring->ring = ring_ind; + MTNIC_BC_PUT(config_rx_ring->stride_size, fls(UNITS_BUFFER_SIZE - 1), + MTNIC_MASK_CONFIG_RX_RING_SIZE); + MTNIC_BC_PUT(config_rx_ring->stride_size, 1, + MTNIC_MASK_CONFIG_RX_RING_STRIDE); + config_rx_ring->cq = cpu_to_be16(ring->cq); + config_rx_ring->db_record_addr_l = cpu_to_be32(ring->db_dma); + + DBG("Config RX ring starting at address:%lx\n", ring->dma); + + config_rx_ring->page_address[1] = cpu_to_be32(ring->dma); + + return mtnic_cmd(priv->mtnic, NULL, NULL, port + 1, + MTNIC_IF_CMD_CONFIG_RX_RING); +} + +static int +mtnic_CONFIG_EQ(struct mtnic *mtnic) +{ + struct mtnic_if_config_eq_in_mbox *eq = mtnic->cmd.buf; + + if (mtnic->eq.dma & (PAGE_MASK)) { + DBG("misalligned eq buffer:%lx\n", + mtnic->eq.dma); + return -EADDRINUSE; + } + + memset(eq, 0, sizeof *eq); + MTNIC_BC_PUT(eq->offset, mtnic->eq.dma >> 6, MTNIC_MASK_CONFIG_EQ_OFFSET); + MTNIC_BC_PUT(eq->size, fls(mtnic->eq.size - 1) - 1, MTNIC_MASK_CONFIG_EQ_SIZE); + MTNIC_BC_PUT(eq->int_vector, 0, MTNIC_MASK_CONFIG_EQ_INT_VEC); + eq->page_address[1] = cpu_to_be32(mtnic->eq.dma); + + return mtnic_cmd(mtnic, NULL, NULL, 0, MTNIC_IF_CMD_CONFIG_EQ); +} + + + + +static int +mtnic_SET_RX_RING_ADDR(struct mtnic_port *priv, u8 port, u64* mac) +{ + struct mtnic_if_set_rx_ring_addr_in_imm ring_addr; + u32 modifier = ((u32) port + 1) << 16; + + memset(&ring_addr, 0, sizeof(ring_addr)); + + ring_addr.mac_31_0 = cpu_to_be32(*mac & 0xffffffff); + ring_addr.mac_47_32 = cpu_to_be16((*mac >> 32) & 0xffff); + ring_addr.flags_vlan_id |= cpu_to_be16( + MTNIC_BC_MASK(MTNIC_MASK_SET_RX_RING_ADDR_BY_MAC)); + + return mtnic_cmd(priv->mtnic, &ring_addr, NULL, modifier, MTNIC_IF_CMD_SET_RX_RING_ADDR); +} + +static int +mtnic_SET_PORT_STATE(struct mtnic_port *priv, u8 port, u8 state) +{ + struct mtnic_if_set_port_state_in_imm port_state; + + port_state.state = state ? cpu_to_be32( + MTNIC_BC_MASK(MTNIC_MASK_CONFIG_PORT_STATE)) : 0; + port_state.reserved = 0; + return mtnic_cmd(priv->mtnic, &port_state, NULL, port + 1, + MTNIC_IF_CMD_SET_PORT_STATE); +} + +static int +mtnic_SET_PORT_MTU(struct mtnic_port *priv, u8 port, u16 mtu) +{ + struct mtnic_if_set_port_mtu_in_imm set_mtu; + + memset(&set_mtu, 0, sizeof(set_mtu)); + set_mtu.mtu = cpu_to_be16(mtu); + return mtnic_cmd(priv->mtnic, &set_mtu, NULL, port + 1, + MTNIC_IF_CMD_SET_PORT_MTU); +} + +/* +static int +mtnic_CONFIG_PORT_VLAN_FILTER(struct mtnic_port *priv, int port) +{ + struct mtnic_if_config_port_vlan_filter_in_mbox *vlan_filter = priv->mtnic->cmd.buf; + + // When no vlans are configured we disable the filter + // (i.e., pass all vlans) because we ignore them anyhow + memset(vlan_filter, 0xff, sizeof(*vlan_filter)); + return mtnic_cmd(priv->mtnic, NULL, NULL, port + 1, + MTNIC_IF_CMD_CONFIG_PORT_VLAN_FILTER); +} +*/ + + +static int +mtnic_RELEASE_RESOURCE(struct mtnic_port *priv, u8 port, u8 type, u8 index) +{ + struct mtnic_if_release_resource_in_imm rel; + memset(&rel, 0, sizeof rel); + rel.index = index; + rel.type = type; + return mtnic_cmd ( priv->mtnic, + &rel, NULL, ( type == MTNIC_IF_RESOURCE_TYPE_EQ ) ? + 0 : port + 1, MTNIC_IF_CMD_RELEASE_RESOURCE ); +} + + +static int +mtnic_QUERY_CAP(struct mtnic *mtnic, u8 index, u8 mod, u64 *result) +{ + struct mtnic_if_query_cap_in_imm cap; + u32 out_imm[2]; + int err; + + memset(&cap, 0, sizeof cap); + cap.cap_index = index; + cap.cap_modifier = mod; + err = mtnic_cmd(mtnic, &cap, &out_imm, 0, MTNIC_IF_CMD_QUERY_CAP); + + *((u32*)result) = be32_to_cpu(*(out_imm+1)); + *((u32*)result + 1) = be32_to_cpu(*out_imm); + + DBG("Called Query cap with index:0x%x mod:%d result:0x%llx" + " error:%d\n", index, mod, *result, err); + return err; +} + + +#define DO_QUERY_CAP(cap, mod, var) \ + err = mtnic_QUERY_CAP(mtnic, cap, mod, &result);\ + if (err) \ + return err; \ + (var) = result + +static int +mtnic_query_num_ports(struct mtnic *mtnic) +{ + int err = 0; + u64 result; + + DO_QUERY_CAP(MTNIC_IF_CAP_NUM_PORTS, 0, mtnic->fw.num_ports); + + return 0; +} + +static int +mtnic_query_mac(struct mtnic *mtnic) +{ + int err = 0; + int i; + u64 result; + + for (i = 0; i < mtnic->fw.num_ports; i++) { + DO_QUERY_CAP(MTNIC_IF_CAP_DEFAULT_MAC, i + 1, mtnic->fw.mac[i]); + } + + return 0; +} + +static int +mtnic_query_offsets(struct mtnic *mtnic) +{ + int err; + int i; + u64 result; + + DO_QUERY_CAP(MTNIC_IF_CAP_MEM_KEY, + MTNIC_IF_MEM_TYPE_SNOOP, + mtnic->fw.mem_type_snoop_be); + mtnic->fw.mem_type_snoop_be = cpu_to_be32(mtnic->fw.mem_type_snoop_be); + DO_QUERY_CAP(MTNIC_IF_CAP_TX_CQ_DB_OFFSET, 0, mtnic->fw.txcq_db_offset); + DO_QUERY_CAP(MTNIC_IF_CAP_EQ_DB_OFFSET, 0, mtnic->fw.eq_db_offset); + + for (i = 0; i < mtnic->fw.num_ports; i++) { + DO_QUERY_CAP(MTNIC_IF_CAP_CQ_OFFSET, i + 1, mtnic->fw.cq_offset); + DO_QUERY_CAP(MTNIC_IF_CAP_TX_OFFSET, i + 1, mtnic->fw.tx_offset[i]); + DO_QUERY_CAP(MTNIC_IF_CAP_RX_OFFSET, i + 1, mtnic->fw.rx_offset[i]); + DBG("--> Port %d CQ offset:0x%x\n", i, mtnic->fw.cq_offset); + DBG("--> Port %d Tx offset:0x%x\n", i, mtnic->fw.tx_offset[i]); + DBG("--> Port %d Rx offset:0x%x\n", i, mtnic->fw.rx_offset[i]); + } + + mdelay(20); + return 0; +} + + + + + + + + + + + +/******************************************************************** +* +* MTNIC initalization functions +* +* +* +* +*********************************************************************/ + +/** + * Reset device + */ +void +mtnic_reset ( void ) +{ + void *reset = ioremap ( mtnic_pci_dev.dev.bar[0] + MTNIC_RESET_OFFSET, + 4 ); + writel ( cpu_to_be32 ( 1 ), reset ); + iounmap ( reset ); +} + + +/** + * Restore PCI config + */ +static int +restore_config(void) +{ + int i; + int rc; + + for (i = 0; i < 64; ++i) { + if (i != 22 && i != 23) { + rc = pci_write_config_dword(mtnic_pci_dev.dev.dev, + i << 2, + mtnic_pci_dev.dev. + dev_config_space[i]); + if (rc) + return rc; + } + } + return 0; +} + + + +/** + * Init PCI configuration + */ +static int +mtnic_init_pci(struct pci_device *dev) +{ + int i; + int err; + + /* save bars */ + DBG("bus=%d devfn=0x%x\n", dev->bus, dev->devfn); + for (i = 0; i < 6; ++i) { + mtnic_pci_dev.dev.bar[i] = + pci_bar_start(dev, PCI_BASE_ADDRESS_0 + (i << 2)); + DBG("bar[%d]= 0x%08lx \n", i, mtnic_pci_dev.dev.bar[i]); + } + + /* save config space */ + for (i = 0; i < 64; ++i) { + err = pci_read_config_dword(dev, i << 2, + &mtnic_pci_dev.dev. + dev_config_space[i]); + if (err) { + DBG("Can not save configuration space"); + return err; + } + } + + mtnic_pci_dev.dev.dev = dev; + + return 0; +} + +/** + * Initial hardware + */ +static inline +int mtnic_init_card(struct mtnic *mtnic) +{ + int err = 0; + + + /* Alloc command interface */ + err = mtnic_alloc_cmdif ( mtnic ); + if (err) { + DBG("Failed to init command interface, aborting\n"); + return -EADDRINUSE; + } + + + /** + * Bring up HW + */ + err = mtnic_QUERY_FW ( mtnic ); + if (err) { + DBG("QUERY_FW command failed, aborting\n"); + goto cmd_error; + } + DBG("Command interface revision:%d\n", mtnic->fw.ifc_rev); + + /* Allocate memory for FW and start it */ + err = mtnic_map_cmd(mtnic, MTNIC_IF_CMD_MAP_FW, mtnic->fw.fw_pages); + if (err) { + DBG("Eror In MAP_FW\n"); + if (mtnic->fw.fw_pages.buf) + ufree((intptr_t)mtnic->fw.fw_pages.buf); + goto cmd_error; + } + + /* Run firmware */ + err = mtnic_cmd(mtnic, NULL, NULL, 0, MTNIC_IF_CMD_RUN_FW); + if (err) { + DBG("Eror In RUN FW\n"); + goto map_fw_error; + } + + DBG("FW version:%d.%d.%d\n", + (u16) (mtnic->fw_ver >> 32), + (u16) ((mtnic->fw_ver >> 16) & 0xffff), + (u16) (mtnic->fw_ver & 0xffff)); + + + /* Query num ports */ + err = mtnic_query_num_ports(mtnic); + if (err) { + DBG("Insufficient resources, aborting\n"); + goto map_fw_error; + } + + /* Open NIC */ + err = mtnic_OPEN_NIC(mtnic); + if (err) { + DBG("Failed opening NIC, aborting\n"); + goto map_fw_error; + } + + /* Allocate and map pages worksace */ + err = mtnic_map_cmd(mtnic, MTNIC_IF_CMD_MAP_PAGES, mtnic->fw.extra_pages); + if (err) { + DBG("Couldn't allocate %x FW extra pages, aborting\n", + mtnic->fw.extra_pages.num); + if (mtnic->fw.extra_pages.buf) + ufree((intptr_t)mtnic->fw.extra_pages.buf); + goto map_fw_error; + } + + + /* Get device information */ + err = mtnic_query_mac(mtnic); + if (err) { + DBG("Insufficient resources in quesry mac, aborting\n"); + goto map_fw_error; + } + + /* Get device offsets */ + err = mtnic_query_offsets(mtnic); + if (err) { + DBG("Failed retrieving resource offests, aborting\n"); + ufree((intptr_t)mtnic->fw.extra_pages.buf); + goto map_extra_error; + } + + + /* Alloc EQ */ + err = mtnic_alloc_eq(mtnic); + if (err) { + DBG("Failed init shared resources. error: %d\n", err); + goto map_extra_error; + } + + /* Configure HW */ + err = mtnic_CONFIG_EQ(mtnic); + if (err) { + DBG("Failed configuring EQ\n"); + goto eq_error; + } + err = mtnic_CONFIG_RX(mtnic); + if (err) { + DBG("Failed Rx configuration\n"); + goto eq_error; + } + err = mtnic_CONFIG_TX(mtnic); + if (err) { + DBG("Failed Tx configuration\n"); + goto eq_error; + } + + + return 0; + + +eq_error: + iounmap(mtnic->eq_db); + free_memblock(mtnic->eq.buf, mtnic->eq.buf_size); +map_extra_error: + ufree((intptr_t)mtnic->fw.extra_pages.buf); +map_fw_error: + ufree((intptr_t)mtnic->fw.fw_pages.buf); + +cmd_error: + iounmap(mtnic->hcr); + free_memblock(mtnic->cmd.buf, PAGE_SIZE); + + return -EADDRINUSE; +} + + + + + + + + + + +/******************************************************************* +* +* Process functions +* +* process compliations of TX and RX +* +* +********************************************************************/ +void mtnic_process_tx_cq(struct mtnic_port *priv, struct net_device *dev, + struct mtnic_cq *cq) +{ + struct mtnic_cqe *cqe = cq->buf; + struct mtnic_ring *ring = &priv->tx_ring; + u16 index; + + + index = cq->last & (cq->size-1); + cqe = &cq->buf[index]; + + /* Owner bit changes every round */ + while (XNOR(cqe->op_tr_own & MTNIC_BIT_CQ_OWN, cq->last & cq->size)) { + netdev_tx_complete (dev, ring->iobuf[index]); + ++cq->last; + index = cq->last & (cq->size-1); + cqe = &cq->buf[index]; + } + + /* Update consumer index */ + cq->db->update_ci = cpu_to_be32(cq->last & 0xffffff); + wmb(); /* ensure HW sees CQ consumer before we post new buffers */ + ring->cons = cq->last; +} + + +int mtnic_process_rx_cq(struct mtnic_port *priv, + struct net_device *dev, + struct mtnic_cq *cq) +{ + struct mtnic_cqe *cqe; + struct mtnic_ring *ring = &priv->rx_ring; + int index; + int err; + struct io_buffer *rx_iob; + unsigned int length; + + + /* We assume a 1:1 mapping between CQEs and Rx descriptors, so Rx + * descriptor offset can be deduced from the CQE index instead of + * reading 'cqe->index' */ + index = cq->last & (cq->size-1); + cqe = &cq->buf[index]; + + /* Process all completed CQEs */ + while (XNOR(cqe->op_tr_own & MTNIC_BIT_CQ_OWN, cq->last & cq->size)) { + /* Drop packet on bad receive or bad checksum */ + if ((cqe->op_tr_own & 0x1f) == MTNIC_OPCODE_ERROR) { + DBG("CQE completed with error - vendor \n"); + free_iob(ring->iobuf[index]); + goto next; + } + if (cqe->enc_bf & MTNIC_BIT_BAD_FCS) { + DBG("Accepted packet with bad FCS\n"); + free_iob(ring->iobuf[index]); + goto next; + } + + /* + * Packet is OK - process it. + */ + length = be32_to_cpu(cqe->byte_cnt); + rx_iob = ring->iobuf[index]; + iob_put(rx_iob, length); + + /* Add this packet to the receive queue. */ + netdev_rx(dev, rx_iob); + ring->iobuf[index] = NULL; + +next: + ++cq->last; + index = cq->last & (cq->size-1); + cqe = &cq->buf[index]; + + + + } + + /* Update consumer index */ + cq->db->update_ci = cpu_to_be32(cq->last & 0xffffff); + wmb(); /* ensure HW sees CQ consumer before we post new buffers */ + ring->cons = cq->last; + + if (ring->prod - ring->cons < (MAX_GAP_PROD_CONS)) { + err = mtnic_alloc_iobuf(priv, &priv->rx_ring, DEF_IOBUF_SIZE); + if (err) { + DBG("ERROR Allocating io buffer"); + return -EADDRINUSE; + } + } + + return 0; +} + + + + + + + + + + + + + + + + +/******************************************************************** +* +* net_device functions +* +* +* open, poll, close, probe, disable, irq +* +*********************************************************************/ +static int +mtnic_open(struct net_device *dev) +{ + struct mtnic_port *priv = netdev_priv(dev); + + int err = 0; + struct mtnic_ring *ring; + struct mtnic_cq *cq; + int cq_ind = 0; + u32 dev_link_state; + int link_check; + + DBG("starting port:%d, MAC Address: 0x%12llx\n", + priv->port, priv->mtnic->fw.mac[priv->port]); + + /* Alloc and configure CQs, TX, RX */ + err = mtnic_alloc_resources ( dev ); + if (err) { + DBG("Error allocating resources\n"); + return -EADDRINUSE; + } + + /* Pass CQs configuration to HW */ + for (cq_ind = 0; cq_ind < NUM_CQS; ++cq_ind) { + cq = &priv->cq[cq_ind]; + err = mtnic_CONFIG_CQ(priv, priv->port, cq_ind, cq); + if (err) { + DBG("Failed configuring CQ:%d error %d\n", + cq_ind, err); + if (cq_ind) + goto cq_error; + else + goto allocation_error; + } + /* Update consumer index */ + cq->db->update_ci = cpu_to_be32(cq->last & 0xffffff); + } + + + + /* Pass Tx configuration to HW */ + ring = &priv->tx_ring; + err = mtnic_CONFIG_TX_RING(priv, priv->port, 0, ring); + if (err) { + DBG("Failed configuring Tx ring:0\n"); + goto cq_error; + } + + /* Pass RX configuration to HW */ + ring = &priv->rx_ring; + err = mtnic_CONFIG_RX_RING(priv, priv->port, 0, ring); + if (err) { + DBG("Failed configuring Rx ring:0\n"); + goto tx_error; + } + + /* Configure Rx steering */ + err = mtnic_CONFIG_PORT_RSS_STEER(priv, priv->port); + if (!err) + err = mtnic_SET_PORT_RSS_INDIRECTION(priv, priv->port); + if (err) { + DBG("Failed configuring RSS steering\n"); + goto rx_error; + } + + + /* Set the port default ring to ring 0 */ + err = mtnic_SET_PORT_DEFAULT_RING(priv, priv->port, 0); + if (err) { + DBG("Failed setting default ring\n"); + goto rx_error; + } + + /* Set Mac address */ + err = mtnic_SET_RX_RING_ADDR(priv, priv->port, &priv->mtnic->fw.mac[priv->port]); + if (err) { + DBG("Failed setting default MAC address\n"); + goto rx_error; + } + + /* Set MTU */ + err = mtnic_SET_PORT_MTU(priv, priv->port, DEF_MTU); + if (err) { + DBG("Failed setting MTU\n"); + goto rx_error; + } + + /* Configure VLAN filter */ + /* By adding this function, The second port won't accept packets + err = mtnic_CONFIG_PORT_VLAN_FILTER(priv, priv->port); + if (err) { + DBG("Failed configuring VLAN filter\n"); + goto rx_error; + } + */ + + + /* Bring up physical link */ + err = mtnic_SET_PORT_STATE(priv, priv->port, 1); + if (err) { + DBG("Failed bringing up port\n"); + goto rx_error; + } + + /* PORT IS UP */ + priv->state = CARD_UP; + + + /* Checking Link is up */ + DBG ( "Checking if link is up\n" ); + + + for ( link_check = 0; link_check < CHECK_LINK_TIMES; link_check ++ ) { + /* Let link state stabilize if cable was connected */ + mdelay ( DELAY_LINK_CHECK ); + + err = mtnic_HEART_BEAT(priv, &dev_link_state); + if (err) { + DBG("Failed getting device link state\n"); + return -ENETDOWN; + } + + if ( dev_link_state & priv->port ) { + /* Link is up */ + break; + } + } + + + if ( ! ( dev_link_state & 0x3 ) ) { + DBG("Link down, check cables and restart\n"); + netdev_link_down ( dev ); + return -ENETDOWN; + } + + DBG ( "Link is up!\n" ); + + /* Mark as link up */ + netdev_link_up ( dev ); + + return 0; + +rx_error: + err = mtnic_RELEASE_RESOURCE(priv, priv->port, + MTNIC_IF_RESOURCE_TYPE_RX_RING, 0); +tx_error: + err |= mtnic_RELEASE_RESOURCE(priv, priv->port, + MTNIC_IF_RESOURCE_TYPE_TX_RING, 0); + +cq_error: + while (cq_ind) { + err |= mtnic_RELEASE_RESOURCE(priv, priv->port, + MTNIC_IF_RESOURCE_TYPE_CQ, --cq_ind); + } + if (err) + DBG("Eror Releasing resources\n"); + +allocation_error: + + free_memblock(priv->tx_ring.buf, priv->tx_ring.buf_size); + iounmap(priv->tx_ring.txcq_db); + free_memblock(priv->cq[1].buf, priv->cq[1].buf_size); + free_memblock(priv->cq[1].db, sizeof(struct mtnic_cq_db_record)); + free_memblock(priv->rx_ring.buf, priv->rx_ring.buf_size); + free_memblock(priv->rx_ring.db, sizeof(struct mtnic_cq_db_record)); + free_memblock(priv->cq[0].buf, priv->cq[0].buf_size); + free_memblock(priv->cq[0].db, sizeof(struct mtnic_cq_db_record)); + + mtnic_free_io_buffers(&priv->rx_ring); + + return -ENETDOWN; +} + + + + +/** Check if we got completion for receive and transmit and + * check the line with heart_bit command */ +static void +mtnic_poll ( struct net_device *dev ) +{ + struct mtnic_port *priv = netdev_priv(dev); + struct mtnic_cq *cq; + u32 dev_link_state; + int err; + unsigned int i; + + /* In case of an old error then return */ + if (priv->state != CARD_UP) + return; + + /* We do not check the device every call _poll call, + since it will slow it down */ + if ((priv->poll_counter % ROUND_TO_CHECK) == 0) { + /* Check device */ + err = mtnic_HEART_BEAT(priv, &dev_link_state); + if (err) { + DBG("Device has internal error\n"); + priv->state = CARD_LINK_DOWN; + return; + } + if (!(dev_link_state & 0x3)) { + DBG("Link down, check cables and restart\n"); + priv->state = CARD_LINK_DOWN; + return; + } + } + /* Polling CQ */ + for (i = 0; i < NUM_CQS; i++) { + cq = &priv->cq[i]; //Passing on the 2 cqs. + + if (cq->is_rx) { + err = mtnic_process_rx_cq(priv, cq->dev, cq); + if (err) { + priv->state = CARD_LINK_DOWN; + DBG(" Error allocating RX buffers\n"); + return; + } + } else { + mtnic_process_tx_cq(priv, cq->dev, cq); + } + } + ++ priv->poll_counter; +} + + + +static int +mtnic_transmit( struct net_device *dev, struct io_buffer *iobuf ) +{ + + struct mtnic_port *priv = netdev_priv(dev); + struct mtnic_ring *ring; + struct mtnic_tx_desc *tx_desc; + struct mtnic_data_seg *data; + u32 index; + + /* In case of an error then return */ + if (priv->state != CARD_UP) + return -ENETDOWN; + + ring = &priv->tx_ring; + + index = ring->prod & ring->size_mask; + if ((ring->prod - ring->cons) >= ring->size) { + DBG("No space left for descriptors!!! cons: %x prod: %x\n", + ring->cons, ring->prod); + mdelay(5); + return -EAGAIN;/* no space left */ + } + + /* get current descriptor */ + tx_desc = ring->buf + (index * sizeof(struct mtnic_tx_desc)); + + /* Prepare Data Seg */ + data = &tx_desc->data; + data->addr_l = cpu_to_be32((u32)virt_to_bus(iobuf->data)); + data->count = cpu_to_be32(iob_len(iobuf)); + data->mem_type = priv->mtnic->fw.mem_type_snoop_be; + + /* Prepare ctrl segement */ + tx_desc->ctrl.size_vlan = cpu_to_be32(2); + tx_desc->ctrl.flags = cpu_to_be32(MTNIC_BIT_TX_COMP | + MTNIC_BIT_NO_ICRC); + tx_desc->ctrl.op_own = cpu_to_be32(MTNIC_OPCODE_SEND) | + ((ring->prod & ring->size) ? + cpu_to_be32(MTNIC_BIT_DESC_OWN) : 0); + + /* Attach io_buffer */ + ring->iobuf[index] = iobuf; + + /* Update producer index */ + ++ring->prod; + + /* Ring doorbell! */ + wmb(); + writel((u32) ring->db_offset, &ring->txcq_db->send_db); + + return 0; +} + + +static void +mtnic_close(struct net_device *dev) +{ + struct mtnic_port *priv = netdev_priv(dev); + int err = 0; + DBG("Close called for port:%d\n", priv->port); + + if ( ( priv->state == CARD_UP ) || + ( priv->state == CARD_LINK_DOWN ) ) { + + /* Disable port */ + err |= mtnic_SET_PORT_STATE(priv, priv->port, 0); + /* + * Stop HW associated with this port + */ + mdelay(5); + + /* Stop RX */ + err |= mtnic_RELEASE_RESOURCE(priv, priv->port, + MTNIC_IF_RESOURCE_TYPE_RX_RING, 0); + + /* Stop TX */ + err |= mtnic_RELEASE_RESOURCE(priv, priv->port, + MTNIC_IF_RESOURCE_TYPE_TX_RING, 0); + + /* Stop CQs */ + err |= mtnic_RELEASE_RESOURCE(priv, priv->port, + MTNIC_IF_RESOURCE_TYPE_CQ, 0); + err |= mtnic_RELEASE_RESOURCE(priv, priv->port, + MTNIC_IF_RESOURCE_TYPE_CQ, 1); + if (err) { + DBG("Close reported error %d\n", err); + } + + mdelay ( 10 ); + + /* free memory */ + free_memblock(priv->tx_ring.buf, priv->tx_ring.buf_size); + iounmap(priv->tx_ring.txcq_db); + free_memblock(priv->cq[1].buf, priv->cq[1].buf_size); + free_memblock(priv->cq[1].db, sizeof(struct mtnic_cq_db_record)); + free_memblock(priv->rx_ring.buf, priv->rx_ring.buf_size); + free_memblock(priv->rx_ring.db, sizeof(struct mtnic_cq_db_record)); + free_memblock(priv->cq[0].buf, priv->cq[0].buf_size); + free_memblock(priv->cq[0].db, sizeof(struct mtnic_cq_db_record)); + + /* Free RX buffers */ + mtnic_free_io_buffers(&priv->rx_ring); + + + + } + + priv->state = CARD_INITIALIZED; + +} + + +static void +mtnic_disable(struct pci_device *pci) +{ + + int err; + int i; + struct mtnic *mtnic = pci_get_drvdata(pci); + + + struct net_device *dev; + struct mtnic_port *priv; + + for ( i = ( mtnic->fw.num_ports - 1 ); i >= 0; i-- ) { + + dev = mtnic->netdev[i]; + + priv = netdev_priv(dev); + + /* Just in case */ + if ( ( priv->state == CARD_UP ) || + ( priv->state == CARD_LINK_DOWN ) ) + mtnic_close ( dev ); + } + + /* Releasing EQ */ + priv = netdev_priv ( mtnic->netdev[0] ); + err = mtnic_RELEASE_RESOURCE(priv, 1, + MTNIC_IF_RESOURCE_TYPE_EQ, 0); + + DBG("Calling MTNIC_CLOSE command\n"); + err |= mtnic_cmd(mtnic, NULL, NULL, 0, + MTNIC_IF_CMD_CLOSE_NIC); + if (err) { + DBG("Error Releasing resources %d\n", err); + } + + free_memblock(mtnic->cmd.buf, PAGE_SIZE); + iounmap(mtnic->hcr); + ufree((intptr_t)mtnic->fw.fw_pages.buf); + ufree((intptr_t)mtnic->fw.extra_pages.buf); + free_memblock(mtnic->eq.buf, mtnic->eq.buf_size); + iounmap(mtnic->eq_db); + + + for ( i = ( mtnic->fw.num_ports - 1 ); i >= 0; i-- ) { + dev = mtnic->netdev[i]; + unregister_netdev ( dev ); + netdev_nullify ( dev ); + netdev_put ( dev ); + } + + free ( mtnic ); + + + mtnic_reset (); + mdelay ( 1000 ); + /* Restore config, if we would like to retry booting */ + restore_config (); + + +} + + + +static void +mtnic_irq(struct net_device *netdev __unused, int enable __unused) +{ + /* Not implemented */ +} + + + +/** mtnic net device operations */ +static struct net_device_operations mtnic_operations = { + .open = mtnic_open, + .close = mtnic_close, + .transmit = mtnic_transmit, + .poll = mtnic_poll, + .irq = mtnic_irq, +}; + + + + + + + +static int +mtnic_probe(struct pci_device *pci, + const struct pci_device_id *id __unused) +{ + struct mtnic_port *priv; + struct mtnic *mtnic; + int err; + u64 mac; + int port_index; + + + adjust_pci_device(pci); + + err = mtnic_init_pci(pci); + if (err) { + DBG("Error in pci_init\n"); + return -EIO; + } + + mtnic_reset(); + mdelay(1000); + + err = restore_config(); + if (err) { + DBG("Error in restoring config\n"); + return err; + } + + mtnic = zalloc ( sizeof ( *mtnic ) ); + if ( ! mtnic ) { + DBG ( "Error Allocating mtnic buffer\n" ); + return -EADDRINUSE; + } + + pci_set_drvdata(pci, mtnic); + + mtnic->pdev = pci; + + + /* Initialize hardware */ + err = mtnic_init_card ( mtnic ); + if (err) { + DBG("Error in init_card\n"); + goto err_init_card; + } + + for ( port_index = 0; port_index < mtnic->fw.num_ports; port_index ++ ) { + /* Initializing net device */ + mtnic->netdev[port_index] = alloc_etherdev( sizeof ( struct mtnic_port ) ); + if ( mtnic->netdev[port_index] == NULL ) { + DBG("Net device allocation failed\n"); + goto err_alloc_mtnic; + } + + /* + * Initialize driver private data + */ + + mtnic->netdev[port_index]->dev = &pci->dev; + priv = netdev_priv ( mtnic->netdev[port_index] ); + memset ( priv, 0, sizeof ( struct mtnic_port ) ); + priv->mtnic = mtnic; + priv->netdev = mtnic->netdev[port_index]; + + /* Attach pci device */ + netdev_init(mtnic->netdev[port_index], &mtnic_operations); + + /* Set port number */ + priv->port = port_index; + + /* Set state */ + priv->state = CARD_DOWN; + } + + + int mac_idx; + for ( port_index = 0; port_index < mtnic->fw.num_ports; port_index ++ ) { + priv = netdev_priv ( mtnic->netdev[port_index] ); + /* Program the MAC address */ + mac = priv->mtnic->fw.mac[port_index]; + for (mac_idx = 0; mac_idx < MAC_ADDRESS_SIZE; ++mac_idx) { + mtnic->netdev[port_index]->ll_addr[MAC_ADDRESS_SIZE - mac_idx - 1] = mac & 0xFF; + mac = mac >> 8; + } + + if ( register_netdev ( mtnic->netdev[port_index] ) ) { + DBG("Netdev registration failed\n"); + priv->state = CARD_INITIALIZED; + goto err_alloc_mtnic; + } + } + + + return 0; + +err_alloc_mtnic: + free ( mtnic ); +err_init_card: + return -EIO; +} + + + + +static struct pci_device_id mtnic_nics[] = { + PCI_ROM ( 0x15b3, 0x6368, "mt25448", "Mellanox ConnectX EN driver" ), + PCI_ROM ( 0x15b3, 0x6372, "mt25458", "Mellanox ConnectX ENt driver" ), + PCI_ROM ( 0x15b3, 0x6750, "mt26448", "Mellanox ConnectX EN GEN2 driver" ), + PCI_ROM ( 0x15b3, 0x675a, "mt26458", "Mellanox ConnectX ENt GEN2 driver" ), +}; + +struct pci_driver mtnic_driver __pci_driver = { + .ids = mtnic_nics, + .id_count = sizeof(mtnic_nics) / sizeof(mtnic_nics[0]), + .probe = mtnic_probe, + .remove = mtnic_disable, +}; + diff --git a/gpxe/src/drivers/net/mtnic.h b/gpxe/src/drivers/net/mtnic.h new file mode 100644 index 0000000..57a7b98 --- /dev/null +++ b/gpxe/src/drivers/net/mtnic.h @@ -0,0 +1,719 @@ +/* + * Copyright (c) 2007 Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#ifndef H_MTNIC_IF_DEFS_H +#define H_MTNIC_IF_DEFS_H + + + +/* +* Device setup +*/ +#define MTNIC_MAX_PORTS 2 +#define MTNIC_PORT1 0 +#define MTNIC_PORT2 1 +#define NUM_TX_RINGS 1 +#define NUM_RX_RINGS 1 +#define NUM_CQS (NUM_RX_RINGS + NUM_TX_RINGS) +#define GO_BIT_TIMEOUT 6000 +#define TBIT_RETRIES 100 +#define UNITS_BUFFER_SIZE 8 /* can be configured to 4/8/16 */ +#define MAX_GAP_PROD_CONS ( UNITS_BUFFER_SIZE / 4 ) +#define ETH_DEF_LEN 1540 /* 40 bytes used by the card */ +#define ETH_FCS_LEN 14 +#define DEF_MTU ETH_DEF_LEN + ETH_FCS_LEN +#define DEF_IOBUF_SIZE ETH_DEF_LEN + +#define MAC_ADDRESS_SIZE 6 +#define NUM_EQES 16 +#define ROUND_TO_CHECK 0x400 + +#define DELAY_LINK_CHECK 300 +#define CHECK_LINK_TIMES 7 + + +#define XNOR(x,y) (!(x) == !(y)) +#define dma_addr_t unsigned long +#define PAGE_SIZE 4096 +#define PAGE_MASK (PAGE_SIZE - 1) +#define MTNIC_MAILBOX_SIZE PAGE_SIZE + + + + +/* BITOPS */ +#define MTNIC_BC_OFF(bc) ((bc) >> 8) +#define MTNIC_BC_SZ(bc) ((bc) & 0xff) +#define MTNIC_BC_ONES(size) (~((int)0x80000000 >> (31 - size))) +#define MTNIC_BC_MASK(bc) \ + (MTNIC_BC_ONES(MTNIC_BC_SZ(bc)) << MTNIC_BC_OFF(bc)) +#define MTNIC_BC_VAL(val, bc) \ + (((val) & MTNIC_BC_ONES(MTNIC_BC_SZ(bc))) << MTNIC_BC_OFF(bc)) +/* + * Sub word fields - bit code base extraction/setting etc + */ + +/* Encode two values */ +#define MTNIC_BC(off, size) ((off << 8) | (size & 0xff)) + +/* Get value of field 'bc' from 'x' */ +#define MTNIC_BC_GET(x, bc) \ + (((x) >> MTNIC_BC_OFF(bc)) & MTNIC_BC_ONES(MTNIC_BC_SZ(bc))) + +/* Set value of field 'bc' of 'x' to 'val' */ +#define MTNIC_BC_SET(x, val, bc) \ + ((x) = ((x) & ~MTNIC_BC_MASK(bc)) | MTNIC_BC_VAL(val, bc)) + +/* Like MTNIC_BC_SET, except the previous value is assumed to be 0 */ +#define MTNIC_BC_PUT(x, val, bc) ((x) |= MTNIC_BC_VAL(val, bc)) + + + +/* + * Device constants + */ +typedef enum mtnic_if_cmd { + /* NIC commands: */ + MTNIC_IF_CMD_QUERY_FW = 0x004, /* query FW (size, version, etc) */ + MTNIC_IF_CMD_MAP_FW = 0xfff, /* map pages for FW image */ + MTNIC_IF_CMD_RUN_FW = 0xff6, /* run the FW */ + MTNIC_IF_CMD_QUERY_CAP = 0x001, /* query MTNIC capabilities */ + MTNIC_IF_CMD_MAP_PAGES = 0x002, /* map physical pages to HW */ + MTNIC_IF_CMD_OPEN_NIC = 0x003, /* run the firmware */ + MTNIC_IF_CMD_CONFIG_RX = 0x005, /* general receive configuration */ + MTNIC_IF_CMD_CONFIG_TX = 0x006, /* general transmit configuration */ + MTNIC_IF_CMD_CONFIG_INT_FREQ = 0x007, /* interrupt timers freq limits */ + MTNIC_IF_CMD_HEART_BEAT = 0x008, /* NOP command testing liveliness */ + MTNIC_IF_CMD_CLOSE_NIC = 0x009, /* release memory and stop the NIC */ + + /* Port commands: */ + MTNIC_IF_CMD_CONFIG_PORT_RSS_STEER = 0x10, /* set RSS mode */ + MTNIC_IF_CMD_SET_PORT_RSS_INDIRECTION = 0x11, /* set RSS indirection tbl */ + MTNIC_IF_CMD_CONFIG_PORT_PRIO_STEERING = 0x12, /* set PRIORITY mode */ + MTNIC_IF_CMD_CONFIG_PORT_ADDR_STEER = 0x13, /* set Address steer mode */ + MTNIC_IF_CMD_CONFIG_PORT_VLAN_FILTER = 0x14, /* configure VLAN filter */ + MTNIC_IF_CMD_CONFIG_PORT_MCAST_FILTER = 0x15, /* configure mcast filter */ + MTNIC_IF_CMD_ENABLE_PORT_MCAST_FILTER = 0x16, /* enable/disable */ + MTNIC_IF_CMD_SET_PORT_MTU = 0x17, /* set port MTU */ + MTNIC_IF_CMD_SET_PORT_PROMISCUOUS_MODE = 0x18, /* enable/disable promisc */ + MTNIC_IF_CMD_SET_PORT_DEFAULT_RING = 0x19, /* set the default ring */ + MTNIC_IF_CMD_SET_PORT_STATE = 0x1a, /* set link up/down */ + MTNIC_IF_CMD_DUMP_STAT = 0x1b, /* dump statistics */ + MTNIC_IF_CMD_ARM_PORT_STATE_EVENT = 0x1c, /* arm the port state event */ + + /* Ring / Completion queue commands: */ + MTNIC_IF_CMD_CONFIG_CQ = 0x20, /* set up completion queue */ + MTNIC_IF_CMD_CONFIG_RX_RING = 0x21, /* setup Rx ring */ + MTNIC_IF_CMD_SET_RX_RING_ADDR = 0x22, /* set Rx ring filter by address */ + MTNIC_IF_CMD_SET_RX_RING_MCAST = 0x23, /* set Rx ring mcast filter */ + MTNIC_IF_CMD_ARM_RX_RING_WM = 0x24, /* one-time low-watermark INT */ + MTNIC_IF_CMD_CONFIG_TX_RING = 0x25, /* set up Tx ring */ + MTNIC_IF_CMD_ENFORCE_TX_RING_ADDR = 0x26, /* setup anti spoofing */ + MTNIC_IF_CMD_CONFIG_EQ = 0x27, /* config EQ ring */ + MTNIC_IF_CMD_RELEASE_RESOURCE = 0x28, /* release internal ref to resource */ +} +mtnic_if_cmd_t; + + +/** selectors for MTNIC_IF_CMD_QUERY_CAP */ +typedef enum mtnic_if_caps { + MTNIC_IF_CAP_MAX_TX_RING_PER_PORT = 0x0, + MTNIC_IF_CAP_MAX_RX_RING_PER_PORT = 0x1, + MTNIC_IF_CAP_MAX_CQ_PER_PORT = 0x2, + MTNIC_IF_CAP_NUM_PORTS = 0x3, + MTNIC_IF_CAP_MAX_TX_DESC = 0x4, + MTNIC_IF_CAP_MAX_RX_DESC = 0x5, + MTNIC_IF_CAP_MAX_CQES = 0x6, + MTNIC_IF_CAP_MAX_TX_SG_ENTRIES = 0x7, + MTNIC_IF_CAP_MAX_RX_SG_ENTRIES = 0x8, + MTNIC_IF_CAP_MEM_KEY = 0x9, /* key to mem (after map_pages) */ + MTNIC_IF_CAP_RSS_HASH_TYPE = 0xa, /* one of mtnic_if_rss_types_t */ + MTNIC_IF_CAP_MAX_PORT_UCAST_ADDR = 0xc, + MTNIC_IF_CAP_MAX_RING_UCAST_ADDR = 0xd, /* only for ADDR steer */ + MTNIC_IF_CAP_MAX_PORT_MCAST_ADDR = 0xe, + MTNIC_IF_CAP_MAX_RING_MCAST_ADDR = 0xf, /* only for ADDR steer */ + MTNIC_IF_CAP_INTA = 0x10, + MTNIC_IF_CAP_BOARD_ID_LOW = 0x11, + MTNIC_IF_CAP_BOARD_ID_HIGH = 0x12, + MTNIC_IF_CAP_TX_CQ_DB_OFFSET = 0x13, /* offset in bytes for TX, CQ doorbell record */ + MTNIC_IF_CAP_EQ_DB_OFFSET = 0x14, /* offset in bytes for EQ doorbell record */ + + /* These are per port - using port number from cap modifier field */ + MTNIC_IF_CAP_SPEED = 0x20, + MTNIC_IF_CAP_DEFAULT_MAC = 0x21, + MTNIC_IF_CAP_EQ_OFFSET = 0x22, + MTNIC_IF_CAP_CQ_OFFSET = 0x23, + MTNIC_IF_CAP_TX_OFFSET = 0x24, + MTNIC_IF_CAP_RX_OFFSET = 0x25, + +} mtnic_if_caps_t; + +typedef enum mtnic_if_steer_types { + MTNIC_IF_STEER_NONE = 0, + MTNIC_IF_STEER_PRIORITY = 1, + MTNIC_IF_STEER_RSS = 2, + MTNIC_IF_STEER_ADDRESS = 3, +} mtnic_if_steer_types_t; + +/** types of memory access modes */ +typedef enum mtnic_if_memory_types { + MTNIC_IF_MEM_TYPE_SNOOP = 1, + MTNIC_IF_MEM_TYPE_NO_SNOOP = 2 +} mtnic_if_memory_types_t; + + +enum { + MTNIC_HCR_BASE = 0x1f000, + MTNIC_HCR_SIZE = 0x0001c, + MTNIC_CLR_INT_SIZE = 0x00008, +}; + +#define MTNIC_RESET_OFFSET 0xF0010 + + + +/******************************************************************** +* Device private data structures +* +* This section contains structures of all device private data: +* descriptors, rings, CQs, EQ .... +* +* +*********************************************************************/ +/* + * Descriptor format + */ +struct mtnic_ctrl_seg { + u32 op_own; +#define MTNIC_BIT_DESC_OWN 0x80000000 +#define MTNIC_OPCODE_SEND 0xa + u32 size_vlan; + u32 flags; +#define MTNIC_BIT_NO_ICRC 0x2 +#define MTNIC_BIT_TX_COMP 0xc + u32 reserved; +}; + +struct mtnic_data_seg { + u32 count; +#define MTNIC_INLINE 0x80000000 + u32 mem_type; +#define MTNIC_MEMTYPE_PAD 0x100 + u32 addr_h; + u32 addr_l; +}; + +struct mtnic_tx_desc { + struct mtnic_ctrl_seg ctrl; + struct mtnic_data_seg data; /* at least one data segment */ +}; + +struct mtnic_rx_desc { + u16 reserved1; + u16 next; + u32 reserved2[3]; + struct mtnic_data_seg data; /* actual number of entries depends on + * rx ring stride */ +}; + +/* + * Rings + */ +struct mtnic_rx_db_record { + u32 count; +}; + +struct mtnic_ring { + u32 size; /* REMOVE ____cacheline_aligned_in_smp; *//* number of Rx descs or TXBBs */ + u32 size_mask; + u16 stride; + u16 cq; /* index of port CQ associated with this ring */ + u32 prod; + u32 cons; /* holds the last consumed index */ + + /* Buffers */ + u32 buf_size; /* ring buffer size in bytes */ + dma_addr_t dma; + void *buf; + struct io_buffer *iobuf[UNITS_BUFFER_SIZE]; + + /* Tx only */ + struct mtnic_txcq_db *txcq_db; + u32 db_offset; + + /* Rx ring only */ + dma_addr_t iobuf_dma; + struct mtnic_rx_db_record *db; + dma_addr_t db_dma; +}; + +/* + * CQ + */ + +struct mtnic_cqe { + u8 vp; /* VLAN present */ + u8 reserved1[3]; + u32 rss_hash; + u32 reserved2; + u16 vlan_prio; + u16 reserved3; + u8 flags_h; + u8 flags_l_rht; + u8 ipv6_mask; + u8 enc_bf; +#define MTNIC_BIT_BAD_FCS 0x10 +#define MTNIC_OPCODE_ERROR 0x1e + u32 byte_cnt; + u16 index; + u16 chksum; + u8 reserved4[3]; + u8 op_tr_own; +#define MTNIC_BIT_CQ_OWN 0x80 +}; + + +struct mtnic_cq_db_record { + u32 update_ci; + u32 cmd_ci; +}; + +struct mtnic_cq { + int num; /* CQ number (on attached port) */ + u32 size; /* number of CQEs in CQ */ + u32 last; /* number of CQEs consumed */ + struct mtnic_cq_db_record *db; + struct net_device *dev; + + dma_addr_t db_dma; + u8 is_rx; + u16 ring; /* ring associated with this CQ */ + u32 offset_ind; + + /* CQE ring */ + u32 buf_size; /* ring size in bytes */ + struct mtnic_cqe *buf; + dma_addr_t dma; +}; + +/* + * EQ + */ + +struct mtnic_eqe { + u8 reserved1; + u8 type; + u8 reserved2; + u8 subtype; + u8 reserved3[3]; + u8 ring_cq; + u32 reserved4; + u8 port; +#define MTNIC_MASK_EQE_PORT MTNIC_BC(4,2) + u8 reserved5[2]; + u8 syndrome; + u8 reserved6[15]; + u8 own; +#define MTNIC_BIT_EQE_OWN 0x80 +}; + +struct mtnic_eq { + u32 size; /* number of EQEs in ring */ + u32 buf_size; /* EQ size in bytes */ + void *buf; + dma_addr_t dma; +}; + +enum mtnic_state { + CARD_DOWN, + CARD_INITIALIZED, + CARD_UP, + CARD_LINK_DOWN, +}; + +/* FW */ +struct mtnic_pages { + u32 num; + u32 *buf; +}; +struct mtnic_err_buf { + u64 offset; + u32 size; +}; + + + +struct mtnic_cmd { + void *buf; + unsigned long mapping; + u32 tbit; +}; + + +struct mtnic_txcq_db { + u32 reserved1[5]; + u32 send_db; + u32 reserved2[2]; + u32 cq_arm; + u32 cq_ci; +}; + + + +/* + * Device private data + * + */ +struct mtnic { + struct net_device *netdev[MTNIC_MAX_PORTS]; + struct mtnic_if_cmd_reg *hcr; + struct mtnic_cmd cmd; + struct pci_device *pdev; + + struct mtnic_eq eq; + u32 *eq_db; + + /* Firmware and board info */ + u64 fw_ver; + struct { + struct mtnic_pages fw_pages; + struct mtnic_pages extra_pages; + struct mtnic_err_buf err_buf; + u16 ifc_rev; + u8 num_ports; + u64 mac[MTNIC_MAX_PORTS]; + u16 cq_offset; + u16 tx_offset[MTNIC_MAX_PORTS]; + u16 rx_offset[MTNIC_MAX_PORTS]; + u32 mem_type_snoop_be; + u32 txcq_db_offset; + u32 eq_db_offset; + } fw; +}; + + + + + +struct mtnic_port { + + struct mtnic *mtnic; + u8 port; + + enum mtnic_state state; + + /* TX, RX, CQs, EQ */ + struct mtnic_ring tx_ring; + struct mtnic_ring rx_ring; + struct mtnic_cq cq[NUM_CQS]; + u32 poll_counter; + struct net_device *netdev; + + +}; + + + + + + + + + + + + +/*************************************************************************** + * NIC COMMANDS + * + * The section below provides struct definition for commands parameters, + * and arguments values enumeration. + * + * The format used for the struct names is: + * mtnic_if___ + * + ***************************************************************************/ +/** + * Command Register (Command interface) + */ +struct mtnic_if_cmd_reg { + unsigned long in_param_h; + u32 in_param_l; + u32 input_modifier; + u32 out_param_h; + u32 out_param_l; + u32 token; +#define MTNIC_MASK_CMD_REG_TOKEN MTNIC_BC(16,32) + u32 status_go_opcode; +#define MTNIC_MASK_CMD_REG_OPCODE MTNIC_BC(0,16) +#define MTNIC_MASK_CMD_REG_T_BIT MTNIC_BC(21,1) +#define MTNIC_MASK_CMD_REG_GO_BIT MTNIC_BC(23,1) +#define MTNIC_MASK_CMD_REG_STATUS MTNIC_BC(24,8) +}; + + + +/* CMD QUERY_FW */ +struct mtnic_if_query_fw_out_mbox { + u16 fw_pages; /* Total number of memory pages the device requires */ + u16 rev_maj; + u16 rev_smin; + u16 rev_min; + u16 reserved1; + u16 ifc_rev; /* major revision of the command interface */ + u8 ft; + u8 reserved2[3]; + u32 reserved3[4]; + u64 clr_int_base; + u32 reserved4[2]; + u64 err_buf_start; + u32 err_buf_size; +}; + +/* CMD MTNIC_IF_CMD_QUERY_CAP */ +struct mtnic_if_query_cap_in_imm { + u16 reserved1; + u8 cap_modifier; /* a modifier for the particular capability */ + u8 cap_index; /* the index of the capability queried */ + u32 reserved2; +}; + +/* CMD OPEN_NIC */ +struct mtnic_if_open_nic_in_mbox { + u16 reserved1; + u16 mkey; /* number of mem keys for all chip*/ + u32 mkey_entry; /* mem key entries for each key*/ + u8 log_rx_p1; /* log2 rx rings for port1 */ + u8 log_cq_p1; /* log2 cq for port1 */ + u8 log_tx_p1; /* log2 tx rings for port1 */ + u8 steer_p1; /* port 1 steering mode */ + u16 reserved2; + u8 log_vlan_p1; /* log2 vlan per rx port1 */ + u8 log_mac_p1; /* log2 mac per rx port1 */ + + u8 log_rx_p2; /* log2 rx rings for port1 */ + u8 log_cq_p2; /* log2 cq for port1 */ + u8 log_tx_p2; /* log2 tx rings for port1 */ + u8 steer_p2; /* port 1 steering mode */ + u16 reserved3; + u8 log_vlan_p2; /* log2 vlan per rx port1 */ + u8 log_mac_p2; /* log2 mac per rx port1 */ +}; + + +/* CMD CONFIG_RX */ +struct mtnic_if_config_rx_in_imm { + u16 spkt_size; /* size of small packets interrupts enabled on CQ */ + u16 resp_rcv_pause_frm_mcast_vlan_comp; /* Two flags see MASK below */ + /* Enable response to receive pause frames */ + /* Use VLAN in exact-match multicast checks (see SET_RX_RING_MCAST) */ +}; + +/* CMD CONFIG_TX */ +struct mtnic_if_config_send_in_imm { + u32 enph_gpf; /* Enable PseudoHeader and GeneratePauseFrames flags */ + u32 reserved; +}; + +/* CMD HEART_BEAT */ +struct mtnic_if_heart_beat_out_imm { + u32 flags; /* several flags */ +#define MTNIC_MASK_HEAR_BEAT_INT_ERROR MTNIC_BC(31,1) + u32 reserved; +}; + + +/* + * PORT COMMANDS + */ +/* CMD CONFIG_PORT_VLAN_FILTER */ +/* in mbox is a 4K bits mask - bit per VLAN */ +struct mtnic_if_config_port_vlan_filter_in_mbox { + u64 filter[64]; /* vlans[63:0] sit in filter[0], vlans[127:64] sit in filter[1] .. */ +}; + + +/* CMD SET_PORT_MTU */ +struct mtnic_if_set_port_mtu_in_imm { + u16 reserved1; + u16 mtu; /* The MTU of the port in bytes */ + u32 reserved2; +}; + +/* CMD SET_PORT_DEFAULT_RING */ +struct mtnic_if_set_port_default_ring_in_imm { + u8 reserved1[3]; + u8 ring; /* Index of ring that collects promiscuous traffic */ + u32 reserved2; +}; + +/* CMD SET_PORT_STATE */ +struct mtnic_if_set_port_state_in_imm { + u32 state; /* if 1 the port state should be up */ +#define MTNIC_MASK_CONFIG_PORT_STATE MTNIC_BC(0,1) + u32 reserved; +}; + +/* CMD CONFIG_CQ */ +struct mtnic_if_config_cq_in_mbox { + u8 reserved1; + u8 cq; + u8 size; /* Num CQs is 2^size (size <= 22) */ + u8 offset; /* start address of CQE in first page (11:6) */ + u16 tlast; /* interrupt moderation timer from last completion usec */ + u8 flags; /* flags */ + u8 int_vector; /* MSI index if MSI is enabled, otherwise reserved */ + u16 reserved2; + u16 max_cnt; /* interrupt moderation counter */ + u8 page_size; /* each mapped page is 2^(12+page_size) bytes */ + u8 reserved4[3]; + u32 db_record_addr_h; /*physical address of CQ doorbell record */ + u32 db_record_addr_l; /*physical address of CQ doorbell record */ + u32 page_address[0]; /* 64 bit page addresses of CQ buffer */ +}; + +/* CMD CONFIG_RX_RING */ +struct mtnic_if_config_rx_ring_in_mbox { + u8 reserved1; + u8 ring; /* The ring index (with offset) */ + u8 stride_size; /* stride and size */ + /* Entry size = 16* (2^stride) bytes */ +#define MTNIC_MASK_CONFIG_RX_RING_STRIDE MTNIC_BC(4,3) + /* Rx ring size is 2^size entries */ +#define MTNIC_MASK_CONFIG_RX_RING_SIZE MTNIC_BC(0,4) + u8 flags; /* Bit0 - header separation */ + u8 page_size; /* Each mapped page is 2^(12+page_size) bytes */ + u8 reserved2[2]; + u8 cq; /* CQ associated with this ring */ + u32 db_record_addr_h; + u32 db_record_addr_l; + u32 page_address[0];/* Array of 2^size 64b page descriptor addresses */ + /* Must hold all Rx descriptors + doorbell record. */ +}; + +/* The modifier for SET_RX_RING_ADDR */ +struct mtnic_if_set_rx_ring_modifier { + u8 reserved; + u8 port_num; + u8 index; + u8 ring; +}; + +/* CMD SET_RX_RING_ADDR */ +struct mtnic_if_set_rx_ring_addr_in_imm { + u16 mac_47_32; /* UCAST MAC Address bits 47:32 */ + u16 flags_vlan_id; /* MAC/VLAN flags and vlan id */ +#define MTNIC_MASK_SET_RX_RING_ADDR_VLAN_ID MTNIC_BC(0,12) +#define MTNIC_MASK_SET_RX_RING_ADDR_BY_MAC MTNIC_BC(12,1) +#define MTNIC_MASK_SET_RX_RING_ADDR_BY_VLAN MTNIC_BC(13,1) + u32 mac_31_0; /* UCAST MAC Address bits 31:0 */ +}; + +/* CMD CONFIG_TX_RING */ +struct mtnic_if_config_send_ring_in_mbox { + u16 ring; /* The ring index (with offset) */ +#define MTNIC_MASK_CONFIG_TX_RING_INDEX MTNIC_BC(0,8) + u8 size; /* Tx ring size is 32*2^size bytes */ +#define MTNIC_MASK_CONFIG_TX_RING_SIZE MTNIC_BC(0,4) + u8 reserved; + u8 page_size; /* Each mapped page is 2^(12+page_size) bytes */ + u8 qos_class; /* The COS used for this Tx */ + u16 cq; /* CQ associated with this ring */ +#define MTNIC_MASK_CONFIG_TX_CQ_INDEX MTNIC_BC(0,8) + u32 page_address[0]; /* 64 bit page addresses of descriptor buffer. */ + /* The buffer must accommodate all Tx descriptors */ +}; + +/* CMD CONFIG_EQ */ +struct mtnic_if_config_eq_in_mbox { + u8 reserved1; + u8 int_vector; /* MSI index if MSI enabled; otherwise reserved */ +#define MTNIC_MASK_CONFIG_EQ_INT_VEC MTNIC_BC(0,6) + u8 size; /* Num CQs is 2^size entries (size <= 22) */ +#define MTNIC_MASK_CONFIG_EQ_SIZE MTNIC_BC(0,5) + u8 offset; /* Start address of CQE in first page (11:6) */ +#define MTNIC_MASK_CONFIG_EQ_OFFSET MTNIC_BC(0,6) + u8 page_size; /* Each mapped page is 2^(12+page_size) bytes*/ + u8 reserved[3]; + u32 page_address[0]; /* 64 bit page addresses of EQ buffer */ +}; + +/* CMD RELEASE_RESOURCE */ +enum mtnic_if_resource_types { + MTNIC_IF_RESOURCE_TYPE_CQ = 0, + MTNIC_IF_RESOURCE_TYPE_RX_RING, + MTNIC_IF_RESOURCE_TYPE_TX_RING, + MTNIC_IF_RESOURCE_TYPE_EQ +}; + +struct mtnic_if_release_resource_in_imm { + u8 reserved1; + u8 index; /* must be 0 for TYPE_EQ */ + u8 reserved2; + u8 type; /* see enum mtnic_if_resource_types */ + u32 reserved3; +}; + + + + + + + + + +/******************************************************************* +* +* PCI addon structures +* +********************************************************************/ + +struct pcidev { + unsigned long bar[6]; + u32 dev_config_space[64]; + struct pci_device *dev; + u8 bus; + u8 devfn; +}; + +struct dev_pci_struct { + struct pcidev dev; + struct pcidev br; +}; + +/* The only global var */ +struct dev_pci_struct mtnic_pci_dev; + + + +#endif /* H_MTNIC_IF_DEFS_H */ + diff --git a/gpxe/src/drivers/net/natsemi.c b/gpxe/src/drivers/net/natsemi.c new file mode 100644 index 0000000..3a77623 --- /dev/null +++ b/gpxe/src/drivers/net/natsemi.c @@ -0,0 +1,607 @@ +/* + natsemi.c - gPXE driver for the NatSemi DP8381x series. + + Based on: + + natsemi.c: An Etherboot driver for the NatSemi DP8381x series. + + Copyright (C) 2001 Entity Cyber, Inc. + + This development of this Etherboot driver was funded by + + Sicom Systems: http://www.sicompos.com/ + + Author: Marty Connor + Adapted from a Linux driver which was written by Donald Becker + + This software may be used and distributed according to the terms + of the GNU Public License (GPL), incorporated herein by reference. + + Original Copyright Notice: + + Written/copyright 1999-2001 by Donald Becker. + + This software may be used and distributed according to the terms of + the GNU General Public License (GPL), incorporated herein by reference. + Drivers based on or derived from this code fall under the GPL and must + retain the authorship, copyright and license notice. This file is not + a complete program and may only be used when the entire operating + system is licensed under the GPL. License for under other terms may be + available. Contact the original author for details. + + The original author may be reached as becker@scyld.com, or at + Scyld Computing Corporation + 410 Severn Ave., Suite 210 + Annapolis MD 21403 + + Support information and updates available at + http://www.scyld.com/network/netsemi.html + + References: + + http://www.scyld.com/expert/100mbps.html + http://www.scyld.com/expert/NWay.html + Datasheet is available from: + http://www.national.com/pf/DP/DP83815.html + +*/ + +/* Revision History */ + +/* + 02 Jul 2007 Udayan Kumar 1.2 ported the driver from etherboot to gPXE API. + Fully rewritten,adapting the old driver. + Added a circular buffer for transmit and receive. + transmit routine will not wait for transmission to finish. + poll routine deals with it. + 13 Dec 2003 Tim Legge 1.1 Enabled Multicast Support + 29 May 2001 Marty Connor 1.0 Initial Release. Tested with Netgear FA311 and FA312 boards +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "natsemi.h" + +/* Function Prototypes: */ + +static int natsemi_spi_read_bit ( struct bit_basher *, unsigned int ); +static void natsemi_spi_write_bit ( struct bit_basher *,unsigned int, unsigned long ); +static void natsemi_init_eeprom ( struct natsemi_private * ); +static int natsemi_probe (struct pci_device *pci, const struct pci_device_id *id); +static void natsemi_reset (struct net_device *netdev); +static int natsemi_open (struct net_device *netdev); +static int natsemi_transmit (struct net_device *netdev, struct io_buffer *iobuf); +static void natsemi_poll (struct net_device *netdev); +static void natsemi_close (struct net_device *netdev); +static void natsemi_irq (struct net_device *netdev, int enable); +static void natsemi_remove (struct pci_device *pci); + +/** natsemi net device operations */ +static struct net_device_operations natsemi_operations = { + .open = natsemi_open, + .close = natsemi_close, + .transmit = natsemi_transmit, + .poll = natsemi_poll, + .irq = natsemi_irq, +}; + +static int natsemi_spi_read_bit ( struct bit_basher *basher, + unsigned int bit_id ) { + struct natsemi_private *np = container_of ( basher, struct natsemi_private, + spibit.basher ); + uint8_t mask = natsemi_ee_bits[bit_id]; + uint8_t eereg; + + eereg = inb ( np->ioaddr + EE_REG ); + return ( eereg & mask ); +} + +static void natsemi_spi_write_bit ( struct bit_basher *basher, + unsigned int bit_id, unsigned long data ) { + struct natsemi_private *np = container_of ( basher, struct natsemi_private, + spibit.basher ); + uint8_t mask = natsemi_ee_bits[bit_id]; + uint8_t eereg; + + eereg = inb ( np->ioaddr + EE_REG ); + eereg &= ~mask; + eereg |= ( data & mask ); + outb ( eereg, np->ioaddr + EE_REG ); +} + +static struct bit_basher_operations natsemi_basher_ops = { + .read = natsemi_spi_read_bit, + .write = natsemi_spi_write_bit, +}; + +/* It looks that this portion of EEPROM can be used for + * non-volatile stored options. Data sheet does not talk about this region. + * Currently it is not working. But with some efforts it can. + */ +static struct nvo_fragment natsemi_nvo_fragments[] = { + { 0x0c, 0x68 }, + { 0, 0 } +}; + +/* + * Set up for EEPROM access + * + * @v NAT NATSEMI NIC + */ +static void natsemi_init_eeprom ( struct natsemi_private *np ) { + + /* Initialise three-wire bus + */ + np->spibit.basher.op = &natsemi_basher_ops; + np->spibit.bus.mode = SPI_MODE_THREEWIRE; + np->spibit.endianness = SPI_BIT_LITTLE_ENDIAN; + init_spi_bit_basher ( &np->spibit ); + + /*natsemi DP 83815 only supports at93c46 + */ + init_at93c46 ( &np->eeprom, 16 ); + np->eeprom.bus = &np->spibit.bus; + np->nvo.nvs = &np->eeprom.nvs; + np->nvo.fragments = natsemi_nvo_fragments; +} + +/** + * Probe PCI device + * + * @v pci PCI device + * @v id PCI ID + * @ret rc Return status code + */ +static int natsemi_probe (struct pci_device *pci, + const struct pci_device_id *id __unused) { + struct net_device *netdev; + struct natsemi_private *np = NULL; + uint8_t ll_addr_encoded[MAX_LL_ADDR_LEN]; + uint8_t last=0,last1=0; + uint8_t prev_bytes[2]; + int i; + int rc; + + /* Allocate net device + */ + netdev = alloc_etherdev (sizeof (*np)); + if (! netdev) + return -ENOMEM; + + netdev_init (netdev, &natsemi_operations); + np = netdev->priv; + pci_set_drvdata (pci, netdev); + netdev->dev = &pci->dev; + memset (np, 0, sizeof (*np)); + np->ioaddr = pci->ioaddr; + + adjust_pci_device (pci); + + natsemi_reset (netdev); + natsemi_init_eeprom ( np ); + nvs_read ( &np->eeprom.nvs, EE_MAC-1, prev_bytes, 1 ); + nvs_read ( &np->eeprom.nvs, EE_MAC, ll_addr_encoded, ETH_ALEN ); + + /* decoding the MAC address read from NVS + * and save it in netdev->ll_addr + */ + last = prev_bytes[1] >> 7; + for ( i = 0 ; i < ETH_ALEN ; i++ ) { + last1 = ll_addr_encoded[i] >> 7; + netdev->ll_addr[i] = ll_addr_encoded[i] << 1 | last; + last = last1; + } + + /* Mark as link up; we don't yet handle link state */ + netdev_link_up ( netdev ); + + if ((rc = register_netdev (netdev)) != 0) + goto err_register_netdev; + + return 0; + +err_register_netdev: + + natsemi_reset (netdev); + netdev_put (netdev); + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void natsemi_remove (struct pci_device *pci) { + struct net_device *netdev = pci_get_drvdata (pci); + + unregister_netdev (netdev); + natsemi_reset (netdev); + netdev_nullify ( netdev ); + netdev_put (netdev); +} + +/** + * Reset NIC + * + * @v NATSEMI NIC + * + * Issues a hardware reset and waits for the reset to complete. + */ +static void natsemi_reset (struct net_device *netdev) +{ + struct natsemi_private *np = netdev->priv; + int i; + u32 cfg; + u32 wcsr; + u32 rfcr; + u16 pmatch[3]; + u16 sopass[3]; + + natsemi_irq (netdev, 0); + + /* + * Resetting the chip causes some registers to be lost. + * Natsemi suggests NOT reloading the EEPROM while live, so instead + * we save the state that would have been loaded from EEPROM + * on a normal power-up (see the spec EEPROM map). + */ + + /* CFG */ + cfg = inl (np->ioaddr + ChipConfig) & CFG_RESET_SAVE; + + /* WCSR */ + wcsr = inl (np->ioaddr + WOLCmd) & WCSR_RESET_SAVE; + + /* RFCR */ + rfcr = readl (np->ioaddr + RxFilterAddr) & RFCR_RESET_SAVE; + + /* PMATCH */ + for (i = 0; i < 3; i++) { + outl(i*2, np->ioaddr + RxFilterAddr); + pmatch[i] = inw(np->ioaddr + RxFilterData); + } + + /* SOPAS */ + for (i = 0; i < 3; i++) { + outl(0xa+(i*2), np->ioaddr + RxFilterAddr); + sopass[i] = inw(np->ioaddr + RxFilterData); + } + + /* now whack the chip */ + outl(ChipReset, np->ioaddr + ChipCmd); + for (i=0; iioaddr + ChipCmd) & ChipReset)) + break; + udelay(5); + } + if (i == NATSEMI_HW_TIMEOUT) { + DBG ("natsemi_reset: reset did not complete in %d usec.\n", i*5); + } + + /* restore CFG */ + cfg |= inl(np->ioaddr + ChipConfig) & ~CFG_RESET_SAVE; + cfg &= ~(CfgExtPhy | CfgPhyDis); + outl (cfg, np->ioaddr + ChipConfig); + + /* restore WCSR */ + wcsr |= inl (np->ioaddr + WOLCmd) & ~WCSR_RESET_SAVE; + outl (wcsr, np->ioaddr + WOLCmd); + + /* read RFCR */ + rfcr |= inl (np->ioaddr + RxFilterAddr) & ~RFCR_RESET_SAVE; + + /* restore PMATCH */ + for (i = 0; i < 3; i++) { + outl (i*2, np->ioaddr + RxFilterAddr); + outw (pmatch[i], np->ioaddr + RxFilterData); + } + for (i = 0; i < 3; i++) { + outl (0xa+(i*2), np->ioaddr + RxFilterAddr); + outw (sopass[i], np->ioaddr + RxFilterData); + } + /* restore RFCR */ + outl (rfcr, np->ioaddr + RxFilterAddr); +} + +/** + * Open NIC + * + * @v netdev Net device + * @ret rc Return status code + */ +static int natsemi_open (struct net_device *netdev) +{ + struct natsemi_private *np = netdev->priv; + uint32_t tx_config, rx_config; + int i; + + /* Disable PME: + * The PME bit is initialized from the EEPROM contents. + * PCI cards probably have PME disabled, but motherboard + * implementations may have PME set to enable WakeOnLan. + * With PME set the chip will scan incoming packets but + * nothing will be written to memory. + */ + outl (inl (np->ioaddr + ClkRun) & ~0x100, np->ioaddr + ClkRun); + + /* Set MAC address in NIC + */ + for (i = 0 ; i < ETH_ALEN ; i+=2) { + outl (i, np->ioaddr + RxFilterAddr); + outw (netdev->ll_addr[i] + (netdev->ll_addr[i + 1] << 8), + np->ioaddr + RxFilterData); + } + + /* Setup Tx Ring + */ + np->tx_cur = 0; + np->tx_dirty = 0; + for (i = 0 ; i < TX_RING_SIZE ; i++) { + np->tx[i].link = virt_to_bus ((i + 1 < TX_RING_SIZE) ? &np->tx[i + 1] : &np->tx[0]); + np->tx[i].cmdsts = 0; + np->tx[i].bufptr = 0; + } + outl (virt_to_bus (&np->tx[0]),np->ioaddr + TxRingPtr); + + DBG ("Natsemi Tx descriptor loaded with: %#08x\n", + inl (np->ioaddr + TxRingPtr)); + + /* Setup RX ring + */ + np->rx_cur = 0; + for (i = 0 ; i < NUM_RX_DESC ; i++) { + np->iobuf[i] = alloc_iob (RX_BUF_SIZE); + if (! np->iobuf[i]) + goto memory_alloc_err; + np->rx[i].link = virt_to_bus ((i + 1 < NUM_RX_DESC) + ? &np->rx[i + 1] : &np->rx[0]); + np->rx[i].cmdsts = RX_BUF_SIZE; + np->rx[i].bufptr = virt_to_bus (np->iobuf[i]->data); + DBG (" Address of iobuf [%d] = %p and iobuf->data = %p \n", i, + &np->iobuf[i], &np->iobuf[i]->data); + } + outl (virt_to_bus (&np->rx[0]), np->ioaddr + RxRingPtr); + + DBG ("Natsemi Rx descriptor loaded with: %#08x\n", + inl (np->ioaddr + RxRingPtr)); + + /* Setup RX Filter + */ + outl (RxFilterEnable | AcceptBroadcast | AcceptAllMulticast | AcceptMyPhys, + np->ioaddr + RxFilterAddr); + + /* Initialize other registers. + * Configure the PCI bus bursts and FIFO thresholds. + * Configure for standard, in-spec Ethernet. + */ + if (inl (np->ioaddr + ChipConfig) & 0x20000000) { /* Full duplex */ + DBG ("Full duplex\n"); + tx_config = 0xD0801002 | 0xC0000000; + rx_config = 0x10000020 | 0x10000000; + } else { + DBG ("Half duplex\n"); + tx_config = 0x10801002 & ~0xC0000000; + rx_config = 0x00000020 & ~0x10000000; + } + outl (tx_config, np->ioaddr + TxConfig); + outl (rx_config, np->ioaddr + RxConfig); + + DBG ("Tx config register = %#08x Rx config register = %#08x\n", + inl (np->ioaddr + TxConfig), + inl (np->ioaddr + RxConfig)); + + /*Set the Interrupt Mask register + */ + outl((RxOk|RxErr|TxOk|TxErr),np->ioaddr + IntrMask); + /*start the receiver + */ + outl (RxOn, np->ioaddr + ChipCmd); + + return 0; + +memory_alloc_err: + + /* Frees any allocated buffers when memory + * for all buffers requested is not available + */ + i = 0; + while (np->rx[i].cmdsts == RX_BUF_SIZE) { + free_iob (np->iobuf[i]); + i++; + } + return -ENOMEM; +} + +/** + * Close NIC + * + * @v netdev Net device + */ +static void natsemi_close (struct net_device *netdev) +{ + struct natsemi_private *np = netdev->priv; + int i; + + natsemi_reset (netdev); + + for (i = 0; i < NUM_RX_DESC ; i++) { + free_iob (np->iobuf[i]); + } +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int natsemi_transmit (struct net_device *netdev, struct io_buffer *iobuf) +{ + struct natsemi_private *np = netdev->priv; + + if (np->tx[np->tx_cur].cmdsts != 0) { + DBG ("TX overflow\n"); + return -ENOBUFS; + } + + /* Used by netdev_tx_complete () + */ + np->tx_iobuf[np->tx_cur] = iobuf; + + /* Pad and align packet has not been used because its not required + * by the hardware. + * iob_pad (iobuf, ETH_ZLEN); + * can be used to achieve it, if required + */ + + /* Add the packet to TX ring + */ + np->tx[np->tx_cur].bufptr = virt_to_bus (iobuf->data); + np->tx[np->tx_cur].cmdsts = iob_len (iobuf) | OWN; + + DBG ("TX id %d at %#08lx + %#08zx\n", np->tx_cur, + virt_to_bus (&iobuf->data), iob_len (iobuf)); + + /* increment the circular buffer pointer to the next buffer location + */ + np->tx_cur = (np->tx_cur + 1) % TX_RING_SIZE; + + /*start the transmitter + */ + outl (TxOn, np->ioaddr + ChipCmd); + + return 0; +} + +/** + * Poll for received packets + * + * @v netdev Network device + */ +static void natsemi_poll (struct net_device *netdev) +{ + struct natsemi_private *np = netdev->priv; + unsigned int tx_status; + unsigned int rx_status; + unsigned int intr_status; + unsigned int rx_len; + struct io_buffer *rx_iob; + int i; + + /* read the interrupt register + */ + intr_status = inl (np->ioaddr + IntrStatus); + + if (!intr_status) + goto end; + + DBG ("natsemi_poll: intr_status = %#08x\n", intr_status); + + /* Check status of transmitted packets + */ + i = np->tx_dirty; + while (i != np->tx_cur) { + tx_status = np->tx[np->tx_dirty].cmdsts; + + DBG ("tx_dirty = %d tx_cur=%d tx_status=%#08x\n", + np->tx_dirty, np->tx_cur, tx_status); + + if (tx_status & OWN) + break; + + if (! (tx_status & DescPktOK)) { + netdev_tx_complete_err (netdev,np->tx_iobuf[np->tx_dirty],-EINVAL); + DBG ("Error transmitting packet, tx_status: %#08x\n", + tx_status); + } else { + netdev_tx_complete (netdev, np->tx_iobuf[np->tx_dirty]); + DBG ("Success transmitting packet\n"); + } + + np->tx[np->tx_dirty].cmdsts = 0; + np->tx_dirty = (np->tx_dirty + 1) % TX_RING_SIZE; + i = (i + 1) % TX_RING_SIZE; + } + + /* Process received packets + */ + rx_status = (unsigned int) np->rx[np->rx_cur].cmdsts; + while ((rx_status & OWN)) { + rx_len = (rx_status & DSIZE) - CRC_SIZE; + + DBG ("Received packet, rx_curr = %d, rx_status = %#08x, rx_len = %d\n", + np->rx_cur, rx_status, rx_len); + + if ((rx_status & (DescMore | DescPktOK | RxTooLong)) != DescPktOK) { + netdev_rx_err (netdev, NULL, -EINVAL); + + DBG ("natsemi_poll: Corrupted packet received!" + " Status = %#08x\n", + np->rx[np->rx_cur].cmdsts); + + } else { + + + /* If unable allocate space for this packet, + * try again next poll + */ + rx_iob = alloc_iob (rx_len); + if (! rx_iob) + goto end; + memcpy (iob_put (rx_iob, rx_len), + np->iobuf[np->rx_cur]->data, rx_len); + /* Add this packet to the receive queue. + */ + netdev_rx (netdev, rx_iob); + } + np->rx[np->rx_cur].cmdsts = RX_BUF_SIZE; + np->rx_cur = (np->rx_cur + 1) % NUM_RX_DESC; + rx_status = np->rx[np->rx_cur].cmdsts; + } +end: + /* re-enable the potentially idle receive state machine + */ + outl (RxOn, np->ioaddr + ChipCmd); +} + +/** + * Enable/disable interrupts + * + * @v netdev Network device + * @v enable Non-zero for enable, zero for disable + */ +static void natsemi_irq (struct net_device *netdev, int enable) +{ + struct natsemi_private *np = netdev->priv; + + outl ((enable ? (RxOk | RxErr | TxOk|TxErr) : 0), + np->ioaddr + IntrMask); + outl ((enable ? 1 : 0), np->ioaddr + IntrEnable); +} + +static struct pci_device_id natsemi_nics[] = { + PCI_ROM(0x100b, 0x0020, "dp83815", "DP83815"), +}; + +struct pci_driver natsemi_driver __pci_driver = { + .ids = natsemi_nics, + .id_count = (sizeof (natsemi_nics) / sizeof (natsemi_nics[0])), + .probe = natsemi_probe, + .remove = natsemi_remove, +}; diff --git a/gpxe/src/drivers/net/natsemi.h b/gpxe/src/drivers/net/natsemi.h new file mode 100644 index 0000000..13b5545 --- /dev/null +++ b/gpxe/src/drivers/net/natsemi.h @@ -0,0 +1,230 @@ +#define NATSEMI_HW_TIMEOUT 400 + +#define TX_RING_SIZE 4 +#define NUM_RX_DESC 4 +#define RX_BUF_SIZE 1536 +#define OWN 0x80000000 +#define DSIZE 0x00000FFF +#define CRC_SIZE 4 + +struct natsemi_tx { + uint32_t link; + uint32_t cmdsts; + uint32_t bufptr; +}; + +struct natsemi_rx { + uint32_t link; + uint32_t cmdsts; + uint32_t bufptr; +}; + +struct natsemi_private { + unsigned short ioaddr; + unsigned short tx_cur; + unsigned short tx_dirty; + unsigned short rx_cur; + struct natsemi_tx tx[TX_RING_SIZE]; + struct natsemi_rx rx[NUM_RX_DESC]; + + /* need to add iobuf as we cannot free iobuf->data in close without this + * alternatively substracting sizeof(head) and sizeof(list_head) can also + * give the same. + */ + struct io_buffer *iobuf[NUM_RX_DESC]; + + /* netdev_tx_complete needs pointer to the iobuf of the data so as to free + * it from the memory. + */ + struct io_buffer *tx_iobuf[TX_RING_SIZE]; + struct spi_bit_basher spibit; + struct spi_device eeprom; + struct nvo_block nvo; +}; + +/* + * Support for fibre connections on Am79C874: + * This phy needs a special setup when connected to a fibre cable. + * http://www.amd.com/files/connectivitysolutions/networking/archivednetworking/22235.pdf + */ +#define PHYID_AM79C874 0x0022561b + +enum { + MII_MCTRL = 0x15, /* mode control register */ + MII_FX_SEL = 0x0001, /* 100BASE-FX (fiber) */ + MII_EN_SCRM = 0x0004, /* enable scrambler (tp) */ +}; + + + +/* values we might find in the silicon revision register */ +#define SRR_DP83815_C 0x0302 +#define SRR_DP83815_D 0x0403 +#define SRR_DP83816_A4 0x0504 +#define SRR_DP83816_A5 0x0505 + +/* NATSEMI: Offsets to the device registers. + * Unlike software-only systems, device drivers interact with complex hardware. + * It's not useful to define symbolic names for every register bit in the + * device. + */ +enum register_offsets { + ChipCmd = 0x00, + ChipConfig = 0x04, + EECtrl = 0x08, + PCIBusCfg = 0x0C, + IntrStatus = 0x10, + IntrMask = 0x14, + IntrEnable = 0x18, + TxRingPtr = 0x20, + TxConfig = 0x24, + RxRingPtr = 0x30, + RxConfig = 0x34, + ClkRun = 0x3C, + WOLCmd = 0x40, + PauseCmd = 0x44, + RxFilterAddr = 0x48, + RxFilterData = 0x4C, + BootRomAddr = 0x50, + BootRomData = 0x54, + SiliconRev = 0x58, + StatsCtrl = 0x5C, + StatsData = 0x60, + RxPktErrs = 0x60, + RxMissed = 0x68, + RxCRCErrs = 0x64, + PCIPM = 0x44, + PhyStatus = 0xC0, + MIntrCtrl = 0xC4, + MIntrStatus = 0xC8, + + /* These are from the spec, around page 78... on a separate table. + */ + PGSEL = 0xCC, + PMDCSR = 0xE4, + TSTDAT = 0xFC, + DSPCFG = 0xF4, + SDCFG = 0x8C, + BasicControl = 0x80, + BasicStatus = 0x84 + +}; + +/* the values for the 'magic' registers above (PGSEL=1) */ +#define PMDCSR_VAL 0x189c /* enable preferred adaptation circuitry */ +#define TSTDAT_VAL 0x0 +#define DSPCFG_VAL 0x5040 +#define SDCFG_VAL 0x008c /* set voltage thresholds for Signal Detect */ +#define DSPCFG_LOCK 0x20 /* coefficient lock bit in DSPCFG */ +#define DSPCFG_COEF 0x1000 /* see coefficient (in TSTDAT) bit in DSPCFG */ +#define TSTDAT_FIXED 0xe8 /* magic number for bad coefficients */ + +/* Bit in ChipCmd. + */ +enum ChipCmdBits { + ChipReset = 0x100, + RxReset = 0x20, + TxReset = 0x10, + RxOff = 0x08, + RxOn = 0x04, + TxOff = 0x02, + TxOn = 0x01 +}; + +enum ChipConfig_bits { + CfgPhyDis = 0x200, + CfgPhyRst = 0x400, + CfgExtPhy = 0x1000, + CfgAnegEnable = 0x2000, + CfgAneg100 = 0x4000, + CfgAnegFull = 0x8000, + CfgAnegDone = 0x8000000, + CfgFullDuplex = 0x20000000, + CfgSpeed100 = 0x40000000, + CfgLink = 0x80000000, +}; + + +/* Bits in the RxMode register. + */ +enum rx_mode_bits { + AcceptErr = 0x20, + AcceptRunt = 0x10, + AcceptBroadcast = 0xC0000000, + AcceptMulticast = 0x00200000, + AcceptAllMulticast = 0x20000000, + AcceptAllPhys = 0x10000000, + AcceptMyPhys = 0x08000000, + RxFilterEnable = 0x80000000 +}; + +/* Bits in network_desc.status + */ +enum desc_status_bits { + DescOwn = 0x80000000, + DescMore = 0x40000000, + DescIntr = 0x20000000, + DescNoCRC = 0x10000000, + DescPktOK = 0x08000000, + RxTooLong = 0x00400000 +}; + +/*Bits in Interrupt Mask register + */ +enum Intr_mask_register_bits { + RxOk = 0x001, + RxErr = 0x004, + TxOk = 0x040, + TxErr = 0x100 +}; + +enum MIntrCtrl_bits { + MICRIntEn = 0x2, +}; + +/* CFG bits [13:16] [18:23] */ +#define CFG_RESET_SAVE 0xfde000 +/* WCSR bits [0:4] [9:10] */ +#define WCSR_RESET_SAVE 0x61f +/* RFCR bits [20] [22] [27:31] */ +#define RFCR_RESET_SAVE 0xf8500000; + +/* Delay between EEPROM clock transitions. + No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need + a delay. */ +#define eeprom_delay(ee_addr) inl(ee_addr) + +enum EEPROM_Ctrl_Bits { + EE_ShiftClk = 0x04, + EE_DataIn = 0x01, + EE_ChipSelect = 0x08, + EE_DataOut = 0x02 +}; + +#define EE_Write0 (EE_ChipSelect) +#define EE_Write1 (EE_ChipSelect | EE_DataIn) + +/* The EEPROM commands include the alway-set leading bit. */ +enum EEPROM_Cmds { + EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6), +}; + +/* EEPROM access , values are devices specific + */ +#define EE_CS 0x08 /* EEPROM chip select */ +#define EE_SK 0x04 /* EEPROM shift clock */ +#define EE_DI 0x01 /* Data in */ +#define EE_DO 0x02 /* Data out */ + +/* Offsets within EEPROM (these are word offsets) + */ +#define EE_MAC 7 +#define EE_REG EECtrl + +static const uint8_t natsemi_ee_bits[] = { + [SPI_BIT_SCLK] = EE_SK, + [SPI_BIT_MOSI] = EE_DI, + [SPI_BIT_MISO] = EE_DO, + [SPI_BIT_SS(0)] = EE_CS, +}; + diff --git a/gpxe/src/drivers/net/ne.c b/gpxe/src/drivers/net/ne.c new file mode 100644 index 0000000..50347de --- /dev/null +++ b/gpxe/src/drivers/net/ne.c @@ -0,0 +1,6 @@ +/* ISA I/O mapped NS8390-based cards, including NE2000 */ +#if 0 /* Currently broken! */ +#define INCLUDE_NE 1 +#define NE_SCAN 0x300,0x280,0x320,0x340,0x380 +#include "ns8390.c" +#endif diff --git a/gpxe/src/drivers/net/ne2k_isa.c b/gpxe/src/drivers/net/ne2k_isa.c new file mode 100644 index 0000000..f8a45cc --- /dev/null +++ b/gpxe/src/drivers/net/ne2k_isa.c @@ -0,0 +1,373 @@ +/************************************************************************** + 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 + Card Detect support adapted from the eCos driver (Christian Plessl ) + Extracted from ns8390.c and adapted by Pantelis Koukousoulas + **************************************************************************/ + +#include "ns8390.h" +#include "etherboot.h" +#include "nic.h" +#include +#include +#include + +#define ASIC_PIO NE_DATA + +static unsigned char eth_vendor, eth_flags; +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; + +static struct nic_operations ne_operations; +static void ne_reset(struct nic *nic, struct isa_device *isa); + +static isa_probe_addr_t ne_probe_addrs[] = { 0x300, 0x280, 0x320, 0x340, 0x380, 0x220, }; + +/************************************************************************** + ETH_PIO_READ - Read a frame via Programmed I/O + **************************************************************************/ +static void eth_pio_read(unsigned int src, unsigned char *dst, unsigned int cnt) { + 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); + if (eth_flags & FLAG_16BIT) + cnt = (cnt + 1) >> 1; + + while (cnt--) { + if (eth_flags & FLAG_16BIT) { + *((unsigned short *) dst) = inw(eth_asic_base + ASIC_PIO); + dst += 2; + } else + *(dst++) = inb(eth_asic_base + ASIC_PIO); + } +} + +/************************************************************************** + 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) { + 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); + if (eth_flags & FLAG_16BIT) + cnt = (cnt + 1) >> 1; + + while (cnt--) { + + if (eth_flags & FLAG_16BIT) { + outw(*((unsigned short *) src), eth_asic_base + ASIC_PIO); + src += 2; + } else + outb(*(src++), eth_asic_base + ASIC_PIO); + } +} + +/************************************************************************** + enable_multicast - 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]) + DBG("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); +} + +/************************************************************************** + NE_PROBE1 - Look for an adapter on the ISA bus + **************************************************************************/ +static int ne_probe1(isa_probe_addr_t ioaddr) { + //From the eCos driver + unsigned int regd; + unsigned int state; + + state = inb(ioaddr); + outb(ioaddr, D8390_COMMAND_RD2 | D8390_COMMAND_PS1 | D8390_COMMAND_STP); + regd = inb(ioaddr + D8390_P0_TCR); + + if (inb(ioaddr + D8390_P0_TCR)) { + outb(ioaddr, state); + outb(ioaddr + 0x0d, regd); + return 0; + } + + return 1; +} + +/************************************************************************** + NE_PROBE - Initialize an adapter ??? + **************************************************************************/ +static int ne_probe(struct nic *nic, struct isa_device *isa) { + int i; + unsigned char c; + unsigned char romdata[16]; + unsigned char testbuf[32]; + + eth_vendor = VENDOR_NONE; + eth_drain_receiver = 0; + + nic->irqno = 0; + nic->ioaddr = isa->ioaddr; + eth_nic_base = isa->ioaddr; + + /****************************************************************** + Search for NE1000/2000 if no WD/SMC or 3com cards + ******************************************************************/ + if (eth_vendor == VENDOR_NONE) { + + static unsigned char test[] = "NE*000 memory"; + + eth_bmem = 0; /* No shared memory */ + + eth_flags = FLAG_PIO; + eth_asic_base = eth_nic_base + NE_ASIC_OFFSET; + eth_memsize = MEM_16384; + eth_tx_start = 32; + eth_rx_start = 32 + D8390_TXBUF_SIZE; + c = inb(eth_asic_base + NE_RESET); + outb(c, eth_asic_base + NE_RESET); + (void) inb(0x84); + outb(D8390_COMMAND_STP | D8390_COMMAND_RD2, eth_nic_base + + D8390_P0_COMMAND); + outb(D8390_RCR_MON, eth_nic_base + D8390_P0_RCR); + outb(D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR); + outb(MEM_8192, eth_nic_base + D8390_P0_PSTART); + outb(MEM_16384, eth_nic_base + D8390_P0_PSTOP); + eth_pio_write((unsigned char *) test, 8192, sizeof(test)); + eth_pio_read(8192, testbuf, sizeof(test)); + if (!memcmp(test, testbuf, sizeof(test))) + goto out; + eth_flags |= FLAG_16BIT; + eth_memsize = MEM_32768; + eth_tx_start = 64; + eth_rx_start = 64 + D8390_TXBUF_SIZE; + outb(D8390_DCR_WTS | D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + + D8390_P0_DCR); + outb(MEM_16384, eth_nic_base + D8390_P0_PSTART); + outb(MEM_32768, eth_nic_base + D8390_P0_PSTOP); + eth_pio_write((unsigned char *) test, 16384, sizeof(test)); + eth_pio_read(16384, testbuf, sizeof(test)); + if (!memcmp(testbuf, test, sizeof(test))) + goto out; + + +out: + if (eth_nic_base == 0) + return (0); + if (eth_nic_base > ISA_MAX_ADDR) /* PCI probably */ + eth_flags |= FLAG_16BIT; + eth_vendor = VENDOR_NOVELL; + eth_pio_read(0, romdata, sizeof(romdata)); + for (i = 0; i < ETH_ALEN; i++) { + nic->node_addr[i] = romdata[i + ((eth_flags & FLAG_16BIT) ? i : 0)]; + } + nic->ioaddr = eth_nic_base; + DBG("\nNE%c000 base %4.4x, MAC Addr %s\n", + (eth_flags & FLAG_16BIT) ? '2' : '1', eth_nic_base, eth_ntoa( + nic->node_addr)); + } + + if (eth_vendor == VENDOR_NONE) + return (0); + + if (eth_vendor != VENDOR_3COM) + eth_rmem = eth_bmem; + + ne_reset(nic, isa); + nic->nic_op = &ne_operations; + return 1; +} + + +/************************************************************************** + NE_DISABLE - Turn off adapter + **************************************************************************/ +static void ne_disable(struct nic *nic, struct isa_device *isa) { + ne_reset(nic, isa); +} + + +/************************************************************************** + NE_RESET - Reset adapter + **************************************************************************/ +static void ne_reset(struct nic *nic, struct isa_device *isa __unused) +{ + int i; + + eth_drain_receiver = 0; + 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); + + 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); + 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; i= 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; + + pktoff = next << 8; + if (eth_flags & FLAG_PIO) + eth_pio_read(pktoff, (unsigned char *)&pkthdr, 4); + else + memcpy(&pkthdr, bus_to_virt(eth_rmem + pktoff), 4); + pktoff += sizeof(pkthdr); + /* incoming length includes FCS so must sub 4 */ + len = pkthdr.len - 4; + if ((pkthdr.status & D8390_RSTAT_PRX) == 0 || len < ETH_ZLEN + || len> ETH_FRAME_LEN) { + DBG("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; + } + 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); +} + + +/************************************************************************** + NE_TRANSMIT - Transmit a frame + **************************************************************************/ +static void ne_transmit(struct nic *nic, const char *d, /* Destination */ +unsigned int t, /* Type */ +unsigned int s, /* size */ +const char *p) { /* Packet */ + + /* Programmed I/O */ + unsigned short type; + type = (t >> 8) | (t << 8); + eth_pio_write((unsigned char *) d, eth_tx_start << 8, ETH_ALEN); + eth_pio_write(nic->node_addr, (eth_tx_start << 8) + ETH_ALEN, ETH_ALEN); + /* bcc generates worse code without (const+const) below */ + eth_pio_write((unsigned char *) &type, (eth_tx_start << 8) + (ETH_ALEN + + ETH_ALEN), 2); + eth_pio_write((unsigned char *) p, (eth_tx_start << 8) + ETH_HLEN, s); + s += ETH_HLEN; + if (s < ETH_ZLEN) + s = ETH_ZLEN; + + 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); + + outb(D8390_COMMAND_PS0 | D8390_COMMAND_TXP | D8390_COMMAND_RD2 + | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND); +} + +static struct nic_operations ne_operations = { .connect = dummy_connect, + .poll = ne_poll, .transmit = ne_transmit, .irq = dummy_irq, +}; + +ISA_DRIVER ( ne_driver, ne_probe_addrs, ne_probe1, + GENERIC_ISAPNP_VENDOR, 0x0600 ); + +DRIVER ( "ne", nic_driver, isapnp_driver, ne_driver, + ne_probe, ne_disable ); + +ISA_ROM("ne","NE1000/2000 and clones"); diff --git a/gpxe/src/drivers/net/ns83820.c b/gpxe/src/drivers/net/ns83820.c new file mode 100755 index 0000000..36091c0 --- /dev/null +++ b/gpxe/src/drivers/net/ns83820.c @@ -0,0 +1,1012 @@ +/************************************************************************** +* ns83820.c: Etherboot device driver for the National Semiconductor 83820 +* Written 2004 by Timothy Legge +* +* 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 + +#if ARCH == ia64 /* Support 64-bit addressing */ +#define USE_64BIT_ADDR +#endif + +//#define DDEBUG +#ifdef DDEBUG +#define dprintf(x) printf x +#else +#define dprintf(x) +#endif + +#define HZ 100 + +/* Condensed operations for readability. */ +#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr)) + +/* NIC specific static variables go here */ + +/* Global parameters. See MODULE_PARM near the bottom. */ +// static int ihr = 2; +static int reset_phy = 0; +static int lnksts = 0; /* CFG_LNKSTS bit polarity */ + +#if defined(CONFIG_HIGHMEM64G) || defined(__ia64__) +#define USE_64BIT_ADDR "+" +#endif + +#if defined(USE_64BIT_ADDR) +#define TRY_DAC 1 +#else +#define TRY_DAC 0 +#endif + +/* tunables */ +#define RX_BUF_SIZE 1500 /* 8192 */ + +/* Must not exceed ~65000. */ +#define NR_RX_DESC 64 +#define NR_TX_DESC 1 + + /* not tunable *//* Extra 6 bytes for 64 bit alignment (divisable by 8) */ +#define REAL_RX_BUF_SIZE (RX_BUF_SIZE + 14 + 6) /* rx/tx mac addr + type */ + +#define MIN_TX_DESC_FREE 8 + +/* register defines */ +#define CFGCS 0x04 + +#define CR_TXE 0x00000001 +#define CR_TXD 0x00000002 +/* Ramit : Here's a tip, don't do a RXD immediately followed by an RXE + * The Receive engine skips one descriptor and moves + * onto the next one!! */ +#define CR_RXE 0x00000004 +#define CR_RXD 0x00000008 +#define CR_TXR 0x00000010 +#define CR_RXR 0x00000020 +#define CR_SWI 0x00000080 +#define CR_RST 0x00000100 + +#define PTSCR_EEBIST_FAIL 0x00000001 +#define PTSCR_EEBIST_EN 0x00000002 +#define PTSCR_EELOAD_EN 0x00000004 +#define PTSCR_RBIST_FAIL 0x000001b8 +#define PTSCR_RBIST_DONE 0x00000200 +#define PTSCR_RBIST_EN 0x00000400 +#define PTSCR_RBIST_RST 0x00002000 + +#define MEAR_EEDI 0x00000001 +#define MEAR_EEDO 0x00000002 +#define MEAR_EECLK 0x00000004 +#define MEAR_EESEL 0x00000008 +#define MEAR_MDIO 0x00000010 +#define MEAR_MDDIR 0x00000020 +#define MEAR_MDC 0x00000040 + +#define ISR_TXDESC3 0x40000000 +#define ISR_TXDESC2 0x20000000 +#define ISR_TXDESC1 0x10000000 +#define ISR_TXDESC0 0x08000000 +#define ISR_RXDESC3 0x04000000 +#define ISR_RXDESC2 0x02000000 +#define ISR_RXDESC1 0x01000000 +#define ISR_RXDESC0 0x00800000 +#define ISR_TXRCMP 0x00400000 +#define ISR_RXRCMP 0x00200000 +#define ISR_DPERR 0x00100000 +#define ISR_SSERR 0x00080000 +#define ISR_RMABT 0x00040000 +#define ISR_RTABT 0x00020000 +#define ISR_RXSOVR 0x00010000 +#define ISR_HIBINT 0x00008000 +#define ISR_PHY 0x00004000 +#define ISR_PME 0x00002000 +#define ISR_SWI 0x00001000 +#define ISR_MIB 0x00000800 +#define ISR_TXURN 0x00000400 +#define ISR_TXIDLE 0x00000200 +#define ISR_TXERR 0x00000100 +#define ISR_TXDESC 0x00000080 +#define ISR_TXOK 0x00000040 +#define ISR_RXORN 0x00000020 +#define ISR_RXIDLE 0x00000010 +#define ISR_RXEARLY 0x00000008 +#define ISR_RXERR 0x00000004 +#define ISR_RXDESC 0x00000002 +#define ISR_RXOK 0x00000001 + +#define TXCFG_CSI 0x80000000 +#define TXCFG_HBI 0x40000000 +#define TXCFG_MLB 0x20000000 +#define TXCFG_ATP 0x10000000 +#define TXCFG_ECRETRY 0x00800000 +#define TXCFG_BRST_DIS 0x00080000 +#define TXCFG_MXDMA1024 0x00000000 +#define TXCFG_MXDMA512 0x00700000 +#define TXCFG_MXDMA256 0x00600000 +#define TXCFG_MXDMA128 0x00500000 +#define TXCFG_MXDMA64 0x00400000 +#define TXCFG_MXDMA32 0x00300000 +#define TXCFG_MXDMA16 0x00200000 +#define TXCFG_MXDMA8 0x00100000 + +#define CFG_LNKSTS 0x80000000 +#define CFG_SPDSTS 0x60000000 +#define CFG_SPDSTS1 0x40000000 +#define CFG_SPDSTS0 0x20000000 +#define CFG_DUPSTS 0x10000000 +#define CFG_TBI_EN 0x01000000 +#define CFG_MODE_1000 0x00400000 +/* Ramit : Dont' ever use AUTO_1000, it never works and is buggy. + * Read the Phy response and then configure the MAC accordingly */ +#define CFG_AUTO_1000 0x00200000 +#define CFG_PINT_CTL 0x001c0000 +#define CFG_PINT_DUPSTS 0x00100000 +#define CFG_PINT_LNKSTS 0x00080000 +#define CFG_PINT_SPDSTS 0x00040000 +#define CFG_TMRTEST 0x00020000 +#define CFG_MRM_DIS 0x00010000 +#define CFG_MWI_DIS 0x00008000 +#define CFG_T64ADDR 0x00004000 +#define CFG_PCI64_DET 0x00002000 +#define CFG_DATA64_EN 0x00001000 +#define CFG_M64ADDR 0x00000800 +#define CFG_PHY_RST 0x00000400 +#define CFG_PHY_DIS 0x00000200 +#define CFG_EXTSTS_EN 0x00000100 +#define CFG_REQALG 0x00000080 +#define CFG_SB 0x00000040 +#define CFG_POW 0x00000020 +#define CFG_EXD 0x00000010 +#define CFG_PESEL 0x00000008 +#define CFG_BROM_DIS 0x00000004 +#define CFG_EXT_125 0x00000002 +#define CFG_BEM 0x00000001 + +#define EXTSTS_UDPPKT 0x00200000 +#define EXTSTS_TCPPKT 0x00080000 +#define EXTSTS_IPPKT 0x00020000 + +#define SPDSTS_POLARITY (CFG_SPDSTS1 | CFG_SPDSTS0 | CFG_DUPSTS | (lnksts ? CFG_LNKSTS : 0)) + +#define MIBC_MIBS 0x00000008 +#define MIBC_ACLR 0x00000004 +#define MIBC_FRZ 0x00000002 +#define MIBC_WRN 0x00000001 + +#define PCR_PSEN (1 << 31) +#define PCR_PS_MCAST (1 << 30) +#define PCR_PS_DA (1 << 29) +#define PCR_STHI_8 (3 << 23) +#define PCR_STLO_4 (1 << 23) +#define PCR_FFHI_8K (3 << 21) +#define PCR_FFLO_4K (1 << 21) +#define PCR_PAUSE_CNT 0xFFFE + +#define RXCFG_AEP 0x80000000 +#define RXCFG_ARP 0x40000000 +#define RXCFG_STRIPCRC 0x20000000 +#define RXCFG_RX_FD 0x10000000 +#define RXCFG_ALP 0x08000000 +#define RXCFG_AIRL 0x04000000 +#define RXCFG_MXDMA512 0x00700000 +#define RXCFG_DRTH 0x0000003e +#define RXCFG_DRTH0 0x00000002 + +#define RFCR_RFEN 0x80000000 +#define RFCR_AAB 0x40000000 +#define RFCR_AAM 0x20000000 +#define RFCR_AAU 0x10000000 +#define RFCR_APM 0x08000000 +#define RFCR_APAT 0x07800000 +#define RFCR_APAT3 0x04000000 +#define RFCR_APAT2 0x02000000 +#define RFCR_APAT1 0x01000000 +#define RFCR_APAT0 0x00800000 +#define RFCR_AARP 0x00400000 +#define RFCR_MHEN 0x00200000 +#define RFCR_UHEN 0x00100000 +#define RFCR_ULM 0x00080000 + +#define VRCR_RUDPE 0x00000080 +#define VRCR_RTCPE 0x00000040 +#define VRCR_RIPE 0x00000020 +#define VRCR_IPEN 0x00000010 +#define VRCR_DUTF 0x00000008 +#define VRCR_DVTF 0x00000004 +#define VRCR_VTREN 0x00000002 +#define VRCR_VTDEN 0x00000001 + +#define VTCR_PPCHK 0x00000008 +#define VTCR_GCHK 0x00000004 +#define VTCR_VPPTI 0x00000002 +#define VTCR_VGTI 0x00000001 + +#define CR 0x00 +#define CFG 0x04 +#define MEAR 0x08 +#define PTSCR 0x0c +#define ISR 0x10 +#define IMR 0x14 +#define IER 0x18 +#define IHR 0x1c +#define TXDP 0x20 +#define TXDP_HI 0x24 +#define TXCFG 0x28 +#define GPIOR 0x2c +#define RXDP 0x30 +#define RXDP_HI 0x34 +#define RXCFG 0x38 +#define PQCR 0x3c +#define WCSR 0x40 +#define PCR 0x44 +#define RFCR 0x48 +#define RFDR 0x4c + +#define SRR 0x58 + +#define VRCR 0xbc +#define VTCR 0xc0 +#define VDR 0xc4 +#define CCSR 0xcc + +#define TBICR 0xe0 +#define TBISR 0xe4 +#define TANAR 0xe8 +#define TANLPAR 0xec +#define TANER 0xf0 +#define TESR 0xf4 + +#define TBICR_MR_AN_ENABLE 0x00001000 +#define TBICR_MR_RESTART_AN 0x00000200 + +#define TBISR_MR_LINK_STATUS 0x00000020 +#define TBISR_MR_AN_COMPLETE 0x00000004 + +#define TANAR_PS2 0x00000100 +#define TANAR_PS1 0x00000080 +#define TANAR_HALF_DUP 0x00000040 +#define TANAR_FULL_DUP 0x00000020 + +#define GPIOR_GP5_OE 0x00000200 +#define GPIOR_GP4_OE 0x00000100 +#define GPIOR_GP3_OE 0x00000080 +#define GPIOR_GP2_OE 0x00000040 +#define GPIOR_GP1_OE 0x00000020 +#define GPIOR_GP3_OUT 0x00000004 +#define GPIOR_GP1_OUT 0x00000001 + +#define LINK_AUTONEGOTIATE 0x01 +#define LINK_DOWN 0x02 +#define LINK_UP 0x04 + + +#define __kick_rx() writel(CR_RXE, ns->base + CR) + +#define kick_rx() do { \ + dprintf(("kick_rx: maybe kicking\n")); \ + writel(virt_to_le32desc(&rx_ring[ns->cur_rx]), ns->base + RXDP); \ + if (ns->next_rx == ns->next_empty) \ + printf("uh-oh: next_rx == next_empty???\n"); \ + __kick_rx(); \ +} while(0) + + +#ifdef USE_64BIT_ADDR +#define HW_ADDR_LEN 8 +#else +#define HW_ADDR_LEN 4 +#endif + +#define CMDSTS_OWN 0x80000000 +#define CMDSTS_MORE 0x40000000 +#define CMDSTS_INTR 0x20000000 +#define CMDSTS_ERR 0x10000000 +#define CMDSTS_OK 0x08000000 +#define CMDSTS_LEN_MASK 0x0000ffff + +#define CMDSTS_DEST_MASK 0x01800000 +#define CMDSTS_DEST_SELF 0x00800000 +#define CMDSTS_DEST_MULTI 0x01000000 + +#define DESC_SIZE 8 /* Should be cache line sized */ + +#ifdef USE_64BIT_ADDR +struct ring_desc { + uint64_t link; + uint64_t bufptr; + u32 cmdsts; + u32 extsts; /* Extended status field */ +}; +#else +struct ring_desc { + u32 link; + u32 bufptr; + u32 cmdsts; + u32 extsts; /* Extended status field */ +}; +#endif + +/* Private Storage for the NIC */ +static struct ns83820_private { + u8 *base; + int up; + long idle; + u32 *next_rx_desc; + u16 next_rx, next_empty; + u32 cur_rx; + u32 *descs; + unsigned ihr; + u32 CFG_cache; + u32 MEAR_cache; + u32 IMR_cache; + int linkstate; + u16 tx_done_idx; + u16 tx_idx; + u16 tx_intr_idx; + u32 phy_descs; + u32 *tx_descs; + +} nsx; +static struct ns83820_private *ns; + +/* Define the TX and RX Descriptor and Buffers */ +struct { + struct ring_desc tx_ring[NR_TX_DESC] __attribute__ ((aligned(8))); + unsigned char txb[NR_TX_DESC * REAL_RX_BUF_SIZE]; + struct ring_desc rx_ring[NR_RX_DESC] __attribute__ ((aligned(8))); + unsigned char rxb[NR_RX_DESC * REAL_RX_BUF_SIZE] + __attribute__ ((aligned(8))); +} ns83820_bufs __shared; +#define tx_ring ns83820_bufs.tx_ring +#define rx_ring ns83820_bufs.rx_ring +#define txb ns83820_bufs.txb +#define rxb ns83820_bufs.rxb + +static void phy_intr(struct nic *nic __unused) +{ + static char *speeds[] = + { "10", "100", "1000", "1000(?)", "1000F" }; + u32 cfg, new_cfg; + u32 tbisr, tanar, tanlpar; + int speed, fullduplex, newlinkstate; + + cfg = readl(ns->base + CFG) ^ SPDSTS_POLARITY; + if (ns->CFG_cache & CFG_TBI_EN) { + /* we have an optical transceiver */ + tbisr = readl(ns->base + TBISR); + tanar = readl(ns->base + TANAR); + tanlpar = readl(ns->base + TANLPAR); + dprintf(("phy_intr: tbisr=%hX, tanar=%hX, tanlpar=%hX\n", + tbisr, tanar, tanlpar)); + + if ((fullduplex = (tanlpar & TANAR_FULL_DUP) + && (tanar & TANAR_FULL_DUP))) { + + /* both of us are full duplex */ + writel(readl(ns->base + TXCFG) + | TXCFG_CSI | TXCFG_HBI | TXCFG_ATP, + ns->base + TXCFG); + writel(readl(ns->base + RXCFG) | RXCFG_RX_FD, + ns->base + RXCFG); + /* Light up full duplex LED */ + writel(readl(ns->base + GPIOR) | GPIOR_GP1_OUT, + ns->base + GPIOR); + + } else if (((tanlpar & TANAR_HALF_DUP) + && (tanar & TANAR_HALF_DUP)) + || ((tanlpar & TANAR_FULL_DUP) + && (tanar & TANAR_HALF_DUP)) + || ((tanlpar & TANAR_HALF_DUP) + && (tanar & TANAR_FULL_DUP))) { + + /* one or both of us are half duplex */ + writel((readl(ns->base + TXCFG) + & ~(TXCFG_CSI | TXCFG_HBI)) | TXCFG_ATP, + ns->base + TXCFG); + writel(readl(ns->base + RXCFG) & ~RXCFG_RX_FD, + ns->base + RXCFG); + /* Turn off full duplex LED */ + writel(readl(ns->base + GPIOR) & ~GPIOR_GP1_OUT, + ns->base + GPIOR); + } + + speed = 4; /* 1000F */ + + } else { + /* we have a copper transceiver */ + new_cfg = + ns->CFG_cache & ~(CFG_SB | CFG_MODE_1000 | CFG_SPDSTS); + + if (cfg & CFG_SPDSTS1) + new_cfg |= CFG_MODE_1000; + else + new_cfg &= ~CFG_MODE_1000; + + speed = ((cfg / CFG_SPDSTS0) & 3); + fullduplex = (cfg & CFG_DUPSTS); + + if (fullduplex) + new_cfg |= CFG_SB; + + if ((cfg & CFG_LNKSTS) && + ((new_cfg ^ ns->CFG_cache) & CFG_MODE_1000)) { + writel(new_cfg, ns->base + CFG); + ns->CFG_cache = new_cfg; + } + + ns->CFG_cache &= ~CFG_SPDSTS; + ns->CFG_cache |= cfg & CFG_SPDSTS; + } + + newlinkstate = (cfg & CFG_LNKSTS) ? LINK_UP : LINK_DOWN; + + if (newlinkstate & LINK_UP && ns->linkstate != newlinkstate) { + printf("link now %s mbps, %s duplex and up.\n", + speeds[speed], fullduplex ? "full" : "half"); + } else if (newlinkstate & LINK_DOWN + && ns->linkstate != newlinkstate) { + printf("link now down.\n"); + } + ns->linkstate = newlinkstate; +} +static void ns83820_set_multicast(struct nic *nic __unused); +static void ns83820_setup_rx(struct nic *nic) +{ + unsigned i; + ns->idle = 1; + ns->next_rx = 0; + ns->next_rx_desc = ns->descs; + ns->next_empty = 0; + ns->cur_rx = 0; + + + for (i = 0; i < NR_RX_DESC; i++) { + rx_ring[i].link = virt_to_le32desc(&rx_ring[i + 1]); + rx_ring[i].bufptr = + virt_to_le32desc(&rxb[i * REAL_RX_BUF_SIZE]); + rx_ring[i].cmdsts = cpu_to_le32(REAL_RX_BUF_SIZE); + rx_ring[i].extsts = cpu_to_le32(0); + } +// No need to wrap the ring +// rx_ring[i].link = virt_to_le32desc(&rx_ring[0]); + writel(0, ns->base + RXDP_HI); + writel(virt_to_le32desc(&rx_ring[0]), ns->base + RXDP); + + dprintf(("starting receiver\n")); + + writel(0x0001, ns->base + CCSR); + writel(0, ns->base + RFCR); + writel(0x7fc00000, ns->base + RFCR); + writel(0xffc00000, ns->base + RFCR); + + ns->up = 1; + + phy_intr(nic); + + /* Okay, let it rip */ + ns->IMR_cache |= ISR_PHY; + ns->IMR_cache |= ISR_RXRCMP; + //dev->IMR_cache |= ISR_RXERR; + //dev->IMR_cache |= ISR_RXOK; + ns->IMR_cache |= ISR_RXORN; + ns->IMR_cache |= ISR_RXSOVR; + ns->IMR_cache |= ISR_RXDESC; + ns->IMR_cache |= ISR_RXIDLE; + ns->IMR_cache |= ISR_TXDESC; + ns->IMR_cache |= ISR_TXIDLE; + + // No reason to enable interupts... + // writel(ns->IMR_cache, ns->base + IMR); + // writel(1, ns->base + IER); + ns83820_set_multicast(nic); + kick_rx(); +} + + +static void ns83820_do_reset(struct nic *nic __unused, u32 which) +{ + dprintf(("resetting chip...\n")); + writel(which, ns->base + CR); + do { + + } while (readl(ns->base + CR) & which); + dprintf(("okay!\n")); +} + +static void ns83820_reset(struct nic *nic) +{ + unsigned i; + dprintf(("ns83820_reset\n")); + + writel(0, ns->base + PQCR); + + ns83820_setup_rx(nic); + + for (i = 0; i < NR_TX_DESC; i++) { + tx_ring[i].link = 0; + tx_ring[i].bufptr = 0; + tx_ring[i].cmdsts = cpu_to_le32(0); + tx_ring[i].extsts = cpu_to_le32(0); + } + + ns->tx_idx = 0; + ns->tx_done_idx = 0; + writel(0, ns->base + TXDP_HI); + return; +} +static void ns83820_getmac(struct nic *nic __unused, u8 * mac) +{ + unsigned i; + for (i = 0; i < 3; i++) { + u32 data; + /* Read from the perfect match memory: this is loaded by + * the chip from the EEPROM via the EELOAD self test. + */ + writel(i * 2, ns->base + RFCR); + data = readl(ns->base + RFDR); + *mac++ = data; + *mac++ = data >> 8; + } +} + +static void ns83820_set_multicast(struct nic *nic __unused) +{ + u8 *rfcr = ns->base + RFCR; + u32 and_mask = 0xffffffff; + u32 or_mask = 0; + u32 val; + + /* Support Multicast */ + and_mask &= ~(RFCR_AAU | RFCR_AAM); + or_mask |= RFCR_AAM; + val = (readl(rfcr) & and_mask) | or_mask; + /* Ramit : RFCR Write Fix doc says RFEN must be 0 modify other bits */ + writel(val & ~RFCR_RFEN, rfcr); + writel(val, rfcr); + +} +static void ns83820_run_bist(struct nic *nic __unused, const char *name, + u32 enable, u32 done, u32 fail) +{ + int timed_out = 0; + long start; + u32 status; + int loops = 0; + + dprintf(("start %s\n", name)) + + start = currticks(); + + writel(enable, ns->base + PTSCR); + for (;;) { + loops++; + status = readl(ns->base + PTSCR); + if (!(status & enable)) + break; + if (status & done) + break; + if (status & fail) + break; + if ((currticks() - start) >= HZ) { + timed_out = 1; + break; + } + } + + if (status & fail) + printf("%s failed! (0x%hX & 0x%hX)\n", name, (unsigned int) status, + (unsigned int) fail); + else if (timed_out) + printf("run_bist %s timed out! (%hX)\n", name, (unsigned int) status); + dprintf(("done %s in %d loops\n", name, loops)); +} + +/************************************* +Check Link +*************************************/ +static void ns83820_check_intr(struct nic *nic) { + int i; + u32 isr = readl(ns->base + ISR); + if(ISR_PHY & isr) + phy_intr(nic); + if(( ISR_RXIDLE | ISR_RXDESC | ISR_RXERR) & isr) + kick_rx(); + for (i = 0; i < NR_RX_DESC; i++) { + if (rx_ring[i].cmdsts == CMDSTS_OWN) { +// rx_ring[i].link = virt_to_le32desc(&rx_ring[i + 1]); + rx_ring[i].cmdsts = cpu_to_le32(REAL_RX_BUF_SIZE); + } + } +} +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int ns83820_poll(struct nic *nic, int retrieve) +{ + /* return true if there's an ethernet packet ready to read */ + /* nic->packet should contain data on return */ + /* nic->packetlen should contain length of data */ + u32 cmdsts; + int entry = ns->cur_rx; + + ns83820_check_intr(nic); + + cmdsts = le32_to_cpu(rx_ring[entry].cmdsts); + + if ( ! ( (CMDSTS_OWN & (cmdsts)) && (cmdsts != (CMDSTS_OWN)) ) ) + return 0; + + if ( ! retrieve ) return 1; + + if (! (CMDSTS_OK & cmdsts) ) + return 0; + + nic->packetlen = cmdsts & 0xffff; + memcpy(nic->packet, + rxb + (entry * REAL_RX_BUF_SIZE), + nic->packetlen); + // rx_ring[entry].link = 0; + rx_ring[entry].cmdsts = cpu_to_le32(CMDSTS_OWN); + + ns->cur_rx = ++ns->cur_rx % NR_RX_DESC; + + if (ns->cur_rx == 0) /* We have wrapped the ring */ + kick_rx(); + + return 1; +} + +static inline void kick_tx(struct nic *nic __unused) +{ + dprintf(("kick_tx\n")); + writel(CR_TXE, ns->base + CR); +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static void ns83820_transmit(struct nic *nic, const char *d, /* Destination */ + unsigned int t, /* Type */ + unsigned int s, /* size */ + const char *p) +{ /* Packet */ + /* send the packet to destination */ + + u16 nstype; + u32 cmdsts, extsts; + int cur_tx = 0; + u32 isr = readl(ns->base + ISR); + if (ISR_TXIDLE & isr) + kick_tx(nic); + /* point to the current txb incase multiple tx_rings are used */ + memcpy(txb, d, ETH_ALEN); + memcpy(txb + ETH_ALEN, nic->node_addr, ETH_ALEN); + nstype = htons((u16) t); + memcpy(txb + 2 * ETH_ALEN, (u8 *) & nstype, 2); + memcpy(txb + ETH_HLEN, p, s); + s += ETH_HLEN; + s &= 0x0FFF; + while (s < ETH_ZLEN) + txb[s++] = '\0'; + + /* Setup the transmit descriptor */ + extsts = 0; + extsts |= EXTSTS_UDPPKT; + + tx_ring[cur_tx].bufptr = virt_to_le32desc(&txb); + tx_ring[cur_tx].extsts = cpu_to_le32(extsts); + + cmdsts = cpu_to_le32(0); + cmdsts |= cpu_to_le32(CMDSTS_OWN | s); + tx_ring[cur_tx].cmdsts = cpu_to_le32(cmdsts); + + writel(virt_to_le32desc(&tx_ring[0]), ns->base + TXDP); + kick_tx(nic); +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void ns83820_disable ( struct nic *nic ) { + + /* put the card in its initial state */ + /* This function serves 3 purposes. + * This disables DMA and interrupts so we don't receive + * unexpected packets or interrupts from the card after + * etherboot has finished. + * This frees resources so etherboot may use + * this driver on another interface + * This allows etherboot to reinitialize the interface + * if something is something goes wrong. + */ + /* disable interrupts */ + writel(0, ns->base + IMR); + writel(0, ns->base + IER); + readl(ns->base + IER); + + ns->up = 0; + + ns83820_do_reset(nic, CR_RST); + + ns->IMR_cache &= + ~(ISR_RXOK | ISR_RXDESC | ISR_RXERR | ISR_RXEARLY | + ISR_RXIDLE); + writel(ns->IMR_cache, ns->base + IMR); + + /* touch the pci bus... */ + readl(ns->base + IMR); + + /* assumes the transmitter is already disabled and reset */ + writel(0, ns->base + RXDP_HI); + writel(0, ns->base + RXDP); +} + +/************************************************************************** +IRQ - Enable, Disable, or Force interrupts +***************************************************************************/ +static void ns83820_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + +static struct nic_operations ns83820_operations = { + .connect = dummy_connect, + .poll = ns83820_poll, + .transmit = ns83820_transmit, + .irq = ns83820_irq, + +}; + +static struct pci_device_id ns83820_nics[] = { + PCI_ROM(0x100b, 0x0022, "ns83820", "National Semiconductor 83820"), +}; + +PCI_DRIVER ( ns83820_driver, ns83820_nics, PCI_NO_CLASS ); + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ + +#define board_found 1 +#define valid_link 0 +static int ns83820_probe ( struct nic *nic, struct pci_device *pci ) { + + long addr; + int using_dac = 0; + + if (pci->ioaddr == 0) + return 0; + + printf("ns83820.c: Found %s, vendor=0x%hX, device=0x%hX\n", + pci->driver_name, pci->vendor, pci->device); + + /* point to private storage */ + ns = &nsx; + + adjust_pci_device(pci); + + addr = pci_bar_start(pci, PCI_BASE_ADDRESS_1); + + ns->base = ioremap(addr, (1UL << 12)); + + if (!ns->base) + return 0; + + nic->irqno = 0; + nic->ioaddr = pci->ioaddr & ~3; + + /* disable interrupts */ + writel(0, ns->base + IMR); + writel(0, ns->base + IER); + readl(ns->base + IER); + + ns->IMR_cache = 0; + + ns83820_do_reset(nic, CR_RST); + + /* Must reset the ram bist before running it */ + writel(PTSCR_RBIST_RST, ns->base + PTSCR); + ns83820_run_bist(nic, "sram bist", PTSCR_RBIST_EN, + PTSCR_RBIST_DONE, PTSCR_RBIST_FAIL); + ns83820_run_bist(nic, "eeprom bist", PTSCR_EEBIST_EN, 0, + PTSCR_EEBIST_FAIL); + ns83820_run_bist(nic, "eeprom load", PTSCR_EELOAD_EN, 0, 0); + + /* I love config registers */ + ns->CFG_cache = readl(ns->base + CFG); + + if ((ns->CFG_cache & CFG_PCI64_DET)) { + printf("%s: detected 64 bit PCI data bus.\n", pci->driver_name); + /*dev->CFG_cache |= CFG_DATA64_EN; */ + if (!(ns->CFG_cache & CFG_DATA64_EN)) + printf + ("%s: EEPROM did not enable 64 bit bus. Disabled.\n", + pci->driver_name); + } else + ns->CFG_cache &= ~(CFG_DATA64_EN); + + ns->CFG_cache &= (CFG_TBI_EN | CFG_MRM_DIS | CFG_MWI_DIS | + CFG_T64ADDR | CFG_DATA64_EN | CFG_EXT_125 | + CFG_M64ADDR); + ns->CFG_cache |= + CFG_PINT_DUPSTS | CFG_PINT_LNKSTS | CFG_PINT_SPDSTS | + CFG_EXTSTS_EN | CFG_EXD | CFG_PESEL; + ns->CFG_cache |= CFG_REQALG; + ns->CFG_cache |= CFG_POW; + ns->CFG_cache |= CFG_TMRTEST; + + /* When compiled with 64 bit addressing, we must always enable + * the 64 bit descriptor format. + */ +#ifdef USE_64BIT_ADDR + ns->CFG_cache |= CFG_M64ADDR; +#endif + +//FIXME: Enable section on dac or remove this + if (using_dac) + ns->CFG_cache |= CFG_T64ADDR; + + /* Big endian mode does not seem to do what the docs suggest */ + ns->CFG_cache &= ~CFG_BEM; + + /* setup optical transceiver if we have one */ + if (ns->CFG_cache & CFG_TBI_EN) { + dprintf(("%s: enabling optical transceiver\n", pci->driver_name)); + writel(readl(ns->base + GPIOR) | 0x3e8, ns->base + GPIOR); + + /* setup auto negotiation feature advertisement */ + writel(readl(ns->base + TANAR) + | TANAR_HALF_DUP | TANAR_FULL_DUP, + ns->base + TANAR); + + /* start auto negotiation */ + writel(TBICR_MR_AN_ENABLE | TBICR_MR_RESTART_AN, + ns->base + TBICR); + writel(TBICR_MR_AN_ENABLE, ns->base + TBICR); + ns->linkstate = LINK_AUTONEGOTIATE; + + ns->CFG_cache |= CFG_MODE_1000; + } + writel(ns->CFG_cache, ns->base + CFG); + dprintf(("CFG: %hX\n", ns->CFG_cache)); + + /* FIXME: reset_phy is defaulted to 0, should we reset anyway? */ + if (reset_phy) { + dprintf(("%s: resetting phy\n", pci->driver_name)); + writel(ns->CFG_cache | CFG_PHY_RST, ns->base + CFG); + writel(ns->CFG_cache, ns->base + CFG); + } +#if 0 /* Huh? This sets the PCI latency register. Should be done via + * the PCI layer. FIXME. + */ + if (readl(dev->base + SRR)) + writel(readl(dev->base + 0x20c) | 0xfe00, + dev->base + 0x20c); +#endif + + /* Note! The DMA burst size interacts with packet + * transmission, such that the largest packet that + * can be transmitted is 8192 - FLTH - burst size. + * If only the transmit fifo was larger... + */ + /* Ramit : 1024 DMA is not a good idea, it ends up banging + * some DELL and COMPAQ SMP systems */ + writel(TXCFG_CSI | TXCFG_HBI | TXCFG_ATP | TXCFG_MXDMA512 + | ((1600 / 32) * 0x100), ns->base + TXCFG); + + /* Set Rx to full duplex, don't accept runt, errored, long or length + * range errored packets. Use 512 byte DMA. + */ + /* Ramit : 1024 DMA is not a good idea, it ends up banging + * some DELL and COMPAQ SMP systems + * Turn on ALP, only we are accpeting Jumbo Packets */ + writel(RXCFG_AEP | RXCFG_ARP | RXCFG_AIRL | RXCFG_RX_FD + | RXCFG_STRIPCRC + //| RXCFG_ALP + | (RXCFG_MXDMA512) | 0, ns->base + RXCFG); + + /* Disable priority queueing */ + writel(0, ns->base + PQCR); + + /* Enable IP checksum validation and detetion of VLAN headers. + * Note: do not set the reject options as at least the 0x102 + * revision of the chip does not properly accept IP fragments + * at least for UDP. + */ + /* Ramit : Be sure to turn on RXCFG_ARP if VLAN's are enabled, since + * the MAC it calculates the packetsize AFTER stripping the VLAN + * header, and if a VLAN Tagged packet of 64 bytes is received (like + * a ping with a VLAN header) then the card, strips the 4 byte VLAN + * tag and then checks the packet size, so if RXCFG_ARP is not enabled, + * it discrards it!. These guys...... + */ + writel(VRCR_IPEN | VRCR_VTDEN, ns->base + VRCR); + + /* Enable per-packet TCP/UDP/IP checksumming */ + writel(VTCR_PPCHK, ns->base + VTCR); + + /* Ramit : Enable async and sync pause frames */ +// writel(0, ns->base + PCR); + writel((PCR_PS_MCAST | PCR_PS_DA | PCR_PSEN | PCR_FFLO_4K | + PCR_FFHI_8K | PCR_STLO_4 | PCR_STHI_8 | PCR_PAUSE_CNT), + ns->base + PCR); + + /* Disable Wake On Lan */ + writel(0, ns->base + WCSR); + + ns83820_getmac(nic, nic->node_addr); + + if (using_dac) { + dprintf(("%s: using 64 bit addressing.\n", pci->driver_name)); + } + + dprintf(("%s: DP83820 %d.%d: %! io=0x%hX\n", + pci->driver_name, + (unsigned) readl(ns->base + SRR) >> 8, + (unsigned) readl(ns->base + SRR) & 0xff, + nic->node_addr, pci->ioaddr)); + +#ifdef PHY_CODE_IS_FINISHED + ns83820_probe_phy(dev); +#endif + + ns83820_reset(nic); + /* point to NIC specific routines */ + nic->nic_op = &ns83820_operations; + return 1; +} + +DRIVER ( "NS83820/PCI", nic_driver, pci_driver, ns83820_driver, + ns83820_probe, ns83820_disable ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/ns8390.c b/gpxe/src/drivers/net/ns8390.c new file mode 100644 index 0000000..3461c21 --- /dev/null +++ b/gpxe/src/drivers/net/ns8390.c @@ -0,0 +1,1035 @@ +/************************************************************************** +ETHERBOOT - BOOTP/TFTP Bootstrap Program + +Author: Martin Renters + Date: May/94 + + This code is based heavily on David Greenman's if_ed.c driver + + Copyright (C) 1993-1994, David Greenman, Martin Renters. + This software may be used, modified, copied, distributed, and sold, in + both source and binary form provided that the above copyright and these + terms are retained. Under no circumstances are the authors responsible for + the proper functioning of this software, nor do the authors assume any + responsibility for damages incurred with its use. + +Multicast support added by Timothy Legge (timlegge@users.sourceforge.net) 09/28/2003 +Relocation support added by Ken Yap (ken_yap@users.sourceforge.net) 28/12/02 +3c503 support added by Bill Paul (wpaul@ctr.columbia.edu) on 11/15/94 +SMC8416 support added by Bill Paul (wpaul@ctr.columbia.edu) on 12/25/94 +3c503 PIO support added by Jim Hague (jim.hague@acm.org) on 2/17/98 +RX overrun by Klaus Espenlaub (espenlaub@informatik.uni-ulm.de) on 3/10/99 + parts taken from the Linux 8390 driver (by Donald Becker and Paul Gortmaker) +SMC8416 PIO support added by Andrew Bettison (andrewb@zip.com.au) on 4/3/02 + based on the Linux 8390 driver (by Donald Becker and Paul Gortmaker) + +**************************************************************************/ + +/* #warning "ns8390.c: FIXME: split ISA and PCI, clean up" */ + +#if 1 + +#if !defined(INCLUDE_NS8390) && !defined(INCLUDE_WD) && \ + !defined(INCLUDE_NE) && !defined(INCLUDE_3C503) + /* The driver named ns8390 is the PCI driver, often called + "PCI ne2000 clones". */ +# define INCLUDE_NS8390 1 +#endif + +#include "etherboot.h" +#include "nic.h" +#include "ns8390.h" +#include +#ifdef INCLUDE_NS8390 +#include +#else +#include +#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( (unsigned char *) d, eth_tx_start<<8, ETH_ALEN); + eth_pio_write(nic->node_addr, (eth_tx_start<<8)+ETH_ALEN, ETH_ALEN); + /* bcc generates worse code without (const+const) below */ + eth_pio_write((unsigned char *)&type, (eth_tx_start<<8)+(ETH_ALEN+ETH_ALEN), 2); + eth_pio_write( (unsigned char *) p, (eth_tx_start<<8)+ETH_HLEN, s); + s += ETH_HLEN; + if (s < ETH_ZLEN) s = ETH_ZLEN; + } +#endif +#if defined(INCLUDE_3C503) +#endif + +#ifdef INCLUDE_WD + if (eth_flags & FLAG_16BIT) { + outb(eth_laar & ~WD_LAAR_M16EN, eth_asic_base + WD_LAAR); + inb(0x84); + } + if (eth_flags & FLAG_790) + outb(D8390_COMMAND_PS0 | + D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND); + else +#endif + outb(D8390_COMMAND_PS0 | + D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND); + outb(eth_tx_start, eth_nic_base+D8390_P0_TPSR); + outb(s, eth_nic_base+D8390_P0_TBCR0); + outb(s>>8, eth_nic_base+D8390_P0_TBCR1); +#ifdef INCLUDE_WD + if (eth_flags & FLAG_790) + outb(D8390_COMMAND_PS0 | + D8390_COMMAND_TXP | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND); + else +#endif + outb(D8390_COMMAND_PS0 | + D8390_COMMAND_TXP | D8390_COMMAND_RD2 | + D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND); +} + +/************************************************************************** +NS8390_POLL - Wait for a frame +**************************************************************************/ +static int ns8390_poll(struct nic *nic, int retrieve) +{ + int ret = 0; + unsigned char rstat, curr, next; + unsigned short len, frag; + unsigned short pktoff; + unsigned char *p; + struct ringbuffer pkthdr; + +#ifndef INCLUDE_3C503 + /* avoid infinite recursion: see eth_rx_overrun() */ + if (!eth_drain_receiver && (inb(eth_nic_base+D8390_P0_ISR) & D8390_ISR_OVW)) { + eth_rx_overrun(nic); + return(0); + } +#endif /* INCLUDE_3C503 */ + rstat = inb(eth_nic_base+D8390_P0_RSR); + if (!(rstat & D8390_RSTAT_PRX)) return(0); + next = inb(eth_nic_base+D8390_P0_BOUND)+1; + if (next >= eth_memsize) next = eth_rx_start; + outb(D8390_COMMAND_PS1, eth_nic_base+D8390_P0_COMMAND); + curr = inb(eth_nic_base+D8390_P1_CURR); + outb(D8390_COMMAND_PS0, eth_nic_base+D8390_P0_COMMAND); + if (curr >= eth_memsize) curr=eth_rx_start; + if (curr == next) return(0); + + if ( ! retrieve ) return 1; + +#ifdef INCLUDE_WD + if (eth_flags & FLAG_16BIT) { + outb(eth_laar | WD_LAAR_M16EN, eth_asic_base + WD_LAAR); + inb(0x84); + } +#ifndef WD_790_PIO + if (eth_flags & FLAG_790) { + outb(WD_MSR_MENB, eth_asic_base + WD_MSR); + inb(0x84); + } +#endif + inb(0x84); +#endif + pktoff = next << 8; + if (eth_flags & FLAG_PIO) + eth_pio_read(pktoff, (unsigned char *)&pkthdr, 4); + else + memcpy(&pkthdr, bus_to_virt(eth_rmem + pktoff), 4); + pktoff += sizeof(pkthdr); + /* incoming length includes FCS so must sub 4 */ + len = pkthdr.len - 4; + if ((pkthdr.status & D8390_RSTAT_PRX) == 0 || len < ETH_ZLEN + || len > ETH_FRAME_LEN) { + printf("Bogus packet, ignoring\n"); + return (0); + } + else { + p = nic->packet; + nic->packetlen = len; /* available to caller */ + frag = (eth_memsize << 8) - pktoff; + if (len > frag) { /* We have a wrap-around */ + /* read first part */ + if (eth_flags & FLAG_PIO) + eth_pio_read(pktoff, p, frag); + else + memcpy(p, bus_to_virt(eth_rmem + pktoff), frag); + pktoff = eth_rx_start << 8; + p += frag; + len -= frag; + } + /* read second part */ + if (eth_flags & FLAG_PIO) + eth_pio_read(pktoff, p, len); + else + memcpy(p, bus_to_virt(eth_rmem + pktoff), len); + ret = 1; + } +#ifdef INCLUDE_WD +#ifndef WD_790_PIO + if (eth_flags & FLAG_790) { + outb(0, eth_asic_base + WD_MSR); + inb(0x84); + } +#endif + if (eth_flags & FLAG_16BIT) { + outb(eth_laar & ~WD_LAAR_M16EN, eth_asic_base + WD_LAAR); + inb(0x84); + } + inb(0x84); +#endif + next = pkthdr.next; /* frame number of next packet */ + if (next == eth_rx_start) + next = eth_memsize; + outb(next-1, eth_nic_base+D8390_P0_BOUND); + return(ret); +} + +/************************************************************************** +NS8390_DISABLE - Turn off adapter +**************************************************************************/ +static void ns8390_disable ( struct nic *nic ) { + ns8390_reset(nic); +} + +/************************************************************************** +NS8390_IRQ - Enable, Disable, or Force interrupts +**************************************************************************/ +static void ns8390_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + +static struct nic_operations ns8390_operations; +static struct nic_operations ns8390_operations = { + .connect = dummy_connect, + .poll = ns8390_poll, + .transmit = ns8390_transmit, + .irq = ns8390_irq, +}; + +/************************************************************************** +ETH_PROBE - Look for an adapter +**************************************************************************/ +#ifdef INCLUDE_NS8390 +static int eth_probe (struct nic *nic, struct pci_device *pci) +#else +static int eth_probe (struct dev *dev, unsigned short *probe_addrs __unused) +#endif +{ + int i; +#ifdef INCLUDE_NS8390 + unsigned short pci_probe_addrs[] = { pci->ioaddr, 0 }; + unsigned short *probe_addrs = pci_probe_addrs; +#endif + eth_vendor = VENDOR_NONE; + eth_drain_receiver = 0; + + nic->irqno = 0; + +#ifdef INCLUDE_WD +{ + /****************************************************************** + Search for WD/SMC cards + ******************************************************************/ + struct wd_board *brd; + unsigned short chksum; + unsigned char c; + for (eth_asic_base = WD_LOW_BASE; eth_asic_base <= WD_HIGH_BASE; + eth_asic_base += 0x20) { + chksum = 0; + for (i=8; i<16; i++) + chksum += inb(eth_asic_base+i); + /* Extra checks to avoid soundcard */ + if ((chksum & 0xFF) == 0xFF && + inb(eth_asic_base+8) != 0xFF && + inb(eth_asic_base+9) != 0xFF) + break; + } + if (eth_asic_base > WD_HIGH_BASE) + return (0); + /* We've found a board */ + eth_vendor = VENDOR_WD; + eth_nic_base = eth_asic_base + WD_NIC_ADDR; + + nic->ioaddr = eth_nic_base; + + c = inb(eth_asic_base+WD_BID); /* Get board id */ + for (brd = wd_boards; brd->name; brd++) + if (brd->id == c) break; + if (!brd->name) { + printf("Unknown WD/SMC NIC type %hhX\n", c); + return (0); /* Unknown type */ + } + eth_flags = brd->flags; + eth_memsize = brd->memsize; + eth_tx_start = 0; + eth_rx_start = D8390_TXBUF_SIZE; + if ((c == TYPE_WD8013EP) && + (inb(eth_asic_base + WD_ICR) & WD_ICR_16BIT)) { + eth_flags = FLAG_16BIT; + eth_memsize = MEM_16384; + } + if ((c & WD_SOFTCONFIG) && (!(eth_flags & FLAG_790))) { + eth_bmem = (0x80000 | + ((inb(eth_asic_base + WD_MSR) & 0x3F) << 13)); + } else + eth_bmem = WD_DEFAULT_MEM; + if (brd->id == TYPE_SMC8216T || brd->id == TYPE_SMC8216C) { + /* from Linux driver, 8416BT detects as 8216 sometimes */ + unsigned int addr = inb(eth_asic_base + 0xb); + if (((addr >> 4) & 3) == 0) { + brd += 2; + eth_memsize = brd->memsize; + } + } + outb(0x80, eth_asic_base + WD_MSR); /* Reset */ + for (i=0; inode_addr[i] = inb(i+eth_asic_base+WD_LAR); + } + DBG ( "\n%s base %4.4x", brd->name, eth_asic_base ); + if (eth_flags & FLAG_790) { +#ifdef WD_790_PIO + DBG ( ", PIO mode, addr %s\n", eth_ntoa ( nic->node_addr ) ); + eth_bmem = 0; + eth_flags |= FLAG_PIO; /* force PIO mode */ + outb(0, eth_asic_base+WD_MSR); +#else + DBG ( ", Memory %x, MAC Addr %s\n", eth_bmem, eth_ntoa ( nic->node_addr) ); + + outb(WD_MSR_MENB, eth_asic_base+WD_MSR); + outb((inb(eth_asic_base+0x04) | + 0x80), eth_asic_base+0x04); + outb(((unsigned)(eth_bmem >> 13) & 0x0F) | + ((unsigned)(eth_bmem >> 11) & 0x40) | + (inb(eth_asic_base+0x0B) & 0xB0), eth_asic_base+0x0B); + outb((inb(eth_asic_base+0x04) & + ~0x80), eth_asic_base+0x04); +#endif + } else { + + DBG (", Memory %x, MAC Addr %s\n", eth_bmem, eth_ntoa ( nic->node_addr) ); + + outb(((unsigned)(eth_bmem >> 13) & 0x3F) | 0x40, eth_asic_base+WD_MSR); + } + if (eth_flags & FLAG_16BIT) { + if (eth_flags & FLAG_790) { + eth_laar = inb(eth_asic_base + WD_LAAR); + outb(WD_LAAR_M16EN, eth_asic_base + WD_LAAR); + } else { + outb((eth_laar = + WD_LAAR_L16EN | 1), eth_asic_base + WD_LAAR); +/* + The previous line used to be + WD_LAAR_M16EN | WD_LAAR_L16EN | 1)); + jluke@deakin.edu.au reported that removing WD_LAAR_M16EN made + it work for WD8013s. This seems to work for my 8013 boards. I + don't know what is really happening. I wish I had data sheets + or more time to decode the Linux driver. - Ken +*/ + } + inb(0x84); + } +} +#endif +#ifdef INCLUDE_3C503 +#ifdef T503_AUI + nic->flags = 1; /* aui */ +#else + nic->flags = 0; /* no aui */ +#endif + /****************************************************************** + Search for 3Com 3c503 if no WD/SMC cards + ******************************************************************/ + if (eth_vendor == VENDOR_NONE) { + int idx; + int iobase_reg, membase_reg; + static unsigned short base[] = { + 0x300, 0x310, 0x330, 0x350, + 0x250, 0x280, 0x2A0, 0x2E0, 0 }; + + /* Loop through possible addresses checking each one */ + + for (idx = 0; (eth_nic_base = base[idx]) != 0; ++idx) { + + eth_asic_base = eth_nic_base + _3COM_ASIC_OFFSET; +/* + * Note that we use the same settings for both 8 and 16 bit cards: + * both have an 8K bank of memory at page 1 while only the 16 bit + * cards have a bank at page 0. + */ + eth_memsize = MEM_16384; + eth_tx_start = 32; + eth_rx_start = 32 + D8390_TXBUF_SIZE; + + /* Check our base address. iobase and membase should */ + /* both have a maximum of 1 bit set or be 0. */ + + iobase_reg = inb(eth_asic_base + _3COM_BCFR); + membase_reg = inb(eth_asic_base + _3COM_PCFR); + + if ((iobase_reg & (iobase_reg - 1)) || + (membase_reg & (membase_reg - 1))) + continue; /* nope */ + + /* Now get the shared memory address */ + + eth_flags = 0; + + switch (membase_reg) { + case _3COM_PCFR_DC000: + eth_bmem = 0xdc000; + break; + case _3COM_PCFR_D8000: + eth_bmem = 0xd8000; + break; + case _3COM_PCFR_CC000: + eth_bmem = 0xcc000; + break; + case _3COM_PCFR_C8000: + eth_bmem = 0xc8000; + break; + case _3COM_PCFR_PIO: + eth_flags |= FLAG_PIO; + eth_bmem = 0; + break; + default: + continue; /* nope */ + } + break; + } + + if (base[idx] == 0) /* not found */ + return (0); +#ifndef T503_SHMEM + eth_flags |= FLAG_PIO; /* force PIO mode */ + eth_bmem = 0; +#endif + eth_vendor = VENDOR_3COM; + + + /* Need this to make ns8390_poll() happy. */ + + eth_rmem = eth_bmem - 0x2000; + + /* Reset NIC and ASIC */ + + outb(_3COM_CR_RST | _3COM_CR_XSEL, eth_asic_base + _3COM_CR ); + outb(_3COM_CR_XSEL, eth_asic_base + _3COM_CR ); + + /* Get our ethernet address */ + + outb(_3COM_CR_EALO | _3COM_CR_XSEL, eth_asic_base + _3COM_CR); + nic->ioaddr = eth_nic_base; + DBG ( "\n3Com 3c503 base %4.4x, ", eth_nic_base ); + if (eth_flags & FLAG_PIO) + DBG ( "PIO mode" ); + else + DBG ( "memory %4.4x", eth_bmem ); + for (i=0; inode_addr[i] = inb(eth_nic_base+i); + } + DBG ( ", %s, MAC Addr %s\n", nic->flags ? "AUI" : "internal xcvr", + eth_ntoa ( nic->node_addr ) ); + + outb(_3COM_CR_XSEL, eth_asic_base + _3COM_CR); + /* + * Initialize GA configuration register. Set bank and enable shared + * mem. We always use bank 1. Disable interrupts. + */ + outb(_3COM_GACFR_RSEL | + _3COM_GACFR_MBS0 | _3COM_GACFR_TCM | _3COM_GACFR_NIM, eth_asic_base + _3COM_GACFR); + + outb(0xff, eth_asic_base + _3COM_VPTR2); + outb(0xff, eth_asic_base + _3COM_VPTR1); + outb(0x00, eth_asic_base + _3COM_VPTR0); + /* + * Clear memory and verify that it worked (we use only 8K) + */ + + if (!(eth_flags & FLAG_PIO)) { + memset(bus_to_virt(eth_bmem), 0, 0x2000); + for(i = 0; i < 0x2000; ++i) + if (*((char *)(bus_to_virt(eth_bmem+i)))) { + printf ("Failed to clear 3c503 shared mem.\n"); + return (0); + } + } + /* + * Initialize GA page/start/stop registers. + */ + outb(eth_tx_start, eth_asic_base + _3COM_PSTR); + outb(eth_memsize, eth_asic_base + _3COM_PSPR); + } +#endif +#if defined(INCLUDE_NE) || defined(INCLUDE_NS8390) +{ + /****************************************************************** + Search for NE1000/2000 if no WD/SMC or 3com cards + ******************************************************************/ + unsigned char c; + if (eth_vendor == VENDOR_NONE) { + unsigned char romdata[16]; + unsigned char testbuf[32]; + int idx; + static unsigned char test[] = "NE*000 memory"; + static unsigned short base[] = { +#ifdef NE_SCAN + NE_SCAN, +#endif + 0 }; + /* if no addresses supplied, fall back on defaults */ + if (probe_addrs == 0 || probe_addrs[0] == 0) + probe_addrs = base; + eth_bmem = 0; /* No shared memory */ + for (idx = 0; (eth_nic_base = probe_addrs[idx]) != 0; ++idx) { + eth_flags = FLAG_PIO; + eth_asic_base = eth_nic_base + NE_ASIC_OFFSET; + eth_memsize = MEM_16384; + eth_tx_start = 32; + eth_rx_start = 32 + D8390_TXBUF_SIZE; + c = inb(eth_asic_base + NE_RESET); + outb(c, eth_asic_base + NE_RESET); + (void) inb(0x84); + outb(D8390_COMMAND_STP | + D8390_COMMAND_RD2, eth_nic_base + D8390_P0_COMMAND); + outb(D8390_RCR_MON, eth_nic_base + D8390_P0_RCR); + outb(D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR); + outb(MEM_8192, eth_nic_base + D8390_P0_PSTART); + outb(MEM_16384, eth_nic_base + D8390_P0_PSTOP); +#ifdef NS8390_FORCE_16BIT + eth_flags |= FLAG_16BIT; /* force 16-bit mode */ +#endif + + eth_pio_write( (unsigned char *) test, 8192, sizeof(test)); + eth_pio_read(8192, testbuf, sizeof(test)); + if (!memcmp(test, testbuf, sizeof(test))) + break; + eth_flags |= FLAG_16BIT; + eth_memsize = MEM_32768; + eth_tx_start = 64; + eth_rx_start = 64 + D8390_TXBUF_SIZE; + outb(D8390_DCR_WTS | + D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR); + outb(MEM_16384, eth_nic_base + D8390_P0_PSTART); + outb(MEM_32768, eth_nic_base + D8390_P0_PSTOP); + eth_pio_write( (unsigned char *) test, 16384, sizeof(test)); + eth_pio_read(16384, testbuf, sizeof(test)); + if (!memcmp(testbuf, test, sizeof(test))) + break; + } + if (eth_nic_base == 0) + return (0); + if (eth_nic_base > ISA_MAX_ADDR) /* PCI probably */ + eth_flags |= FLAG_16BIT; + eth_vendor = VENDOR_NOVELL; + eth_pio_read(0, romdata, sizeof(romdata)); + for (i=0; inode_addr[i] = romdata[i + ((eth_flags & FLAG_16BIT) ? i : 0)]; + } + nic->ioaddr = eth_nic_base; + DBG ( "\nNE%c000 base %4.4x, MAC Addr %s\n", + (eth_flags & FLAG_16BIT) ? '2' : '1', eth_nic_base, + eth_ntoa ( nic->node_addr ) ); + } +} +#endif + if (eth_vendor == VENDOR_NONE) + return(0); + if (eth_vendor != VENDOR_3COM) + eth_rmem = eth_bmem; + ns8390_reset(nic); + nic->nic_op = &ns8390_operations; + + /* Based on PnP ISA map */ +#ifdef INCLUDE_WD + dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR); + dev->devid.device_id = htons(0x812a); +#endif +#ifdef INCLUDE_3C503 + dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR); + dev->devid.device_id = htons(0x80f3); +#endif +#ifdef INCLUDE_NE + dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR); + dev->devid.device_id = htons(0x80d6); +#endif + return 1; +} + +#ifdef INCLUDE_WD +struct isa_driver wd_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "WD", + .probe = wd_probe, + .ioaddrs = 0, +}; +ISA_ROM("wd","WD8003/8013, SMC8216/8416, SMC 83c790 (EtherEZ)"); +#endif + +#ifdef INCLUDE_3C503 +struct isa_driver t503_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "3C503", + .probe = t503_probe, + .ioaddrs = 0, +}; +ISA_ROM("3c503","3Com503, Etherlink II[/16]"); +#endif + +#ifdef INCLUDE_NE +struct isa_driver ne_driver __isa_driver = { + .type = NIC_DRIVER, + .name = "NE*000", + .probe = ne_probe, + .ioaddrs = 0, +}; +ISA_ROM("ne","NE1000/2000 and clones"); +#endif + +#ifdef INCLUDE_NS8390 +static struct pci_device_id nepci_nics[] = { +/* A few NE2000 PCI clones, list not exhaustive */ +PCI_ROM(0x10ec, 0x8029, "rtl8029", "Realtek 8029"), +PCI_ROM(0x1186, 0x0300, "dlink-528", "D-Link DE-528"), +PCI_ROM(0x1050, 0x0940, "winbond940", "Winbond NE2000-PCI"), /* Winbond 86C940 / 89C940 */ +PCI_ROM(0x1050, 0x5a5a, "winbond940f", "Winbond W89c940F"), /* Winbond 89C940F */ +PCI_ROM(0x11f6, 0x1401, "compexrl2000", "Compex ReadyLink 2000"), +PCI_ROM(0x8e2e, 0x3000, "ktiet32p2", "KTI ET32P2"), +PCI_ROM(0x4a14, 0x5000, "nv5000sc", "NetVin NV5000SC"), +PCI_ROM(0x12c3, 0x0058, "holtek80232", "Holtek HT80232"), +PCI_ROM(0x12c3, 0x5598, "holtek80229", "Holtek HT80229"), +PCI_ROM(0x10bd, 0x0e34, "surecom-ne34", "Surecom NE34"), +PCI_ROM(0x1106, 0x0926, "via86c926", "Via 86c926"), +}; + +PCI_DRIVER ( nepci_driver, nepci_nics, PCI_NO_CLASS ); + +DRIVER ( "NE2000/PCI", nic_driver, pci_driver, nepci_driver, + nepci_probe, ns8390_disable ); + +#endif /* INCLUDE_NS8390 */ + +#endif + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/ns8390.h b/gpxe/src/drivers/net/ns8390.h new file mode 100644 index 0000000..2c4e972 --- /dev/null +++ b/gpxe/src/drivers/net/ns8390.h @@ -0,0 +1,238 @@ +/************************************************************************** +ETHERBOOT - BOOTP/TFTP Bootstrap Program + +Author: Martin Renters + Date: Jun/94 + +**************************************************************************/ + +#define VENDOR_NONE 0 +#define VENDOR_WD 1 +#define VENDOR_NOVELL 2 +#define VENDOR_3COM 3 + +#define FLAG_PIO 0x01 +#define FLAG_16BIT 0x02 +#define FLAG_790 0x04 + +#define MEM_8192 32 +#define MEM_16384 64 +#define MEM_32768 128 + +#define ISA_MAX_ADDR 0x400 + +/************************************************************************** +Western Digital/SMC Board Definitions +**************************************************************************/ +#define WD_LOW_BASE 0x200 +#define WD_HIGH_BASE 0x3e0 +#ifndef WD_DEFAULT_MEM +#define WD_DEFAULT_MEM 0xD0000 +#endif +#define WD_NIC_ADDR 0x10 + +/************************************************************************** +Western Digital/SMC ASIC Addresses +**************************************************************************/ +#define WD_MSR 0x00 +#define WD_ICR 0x01 +#define WD_IAR 0x02 +#define WD_BIO 0x03 +#define WD_IRR 0x04 +#define WD_LAAR 0x05 +#define WD_IJR 0x06 +#define WD_GP2 0x07 +#define WD_LAR 0x08 +#define WD_BID 0x0E + +#define WD_ICR_16BIT 0x01 + +#define WD_MSR_MENB 0x40 + +#define WD_LAAR_L16EN 0x40 +#define WD_LAAR_M16EN 0x80 + +#define WD_SOFTCONFIG 0x20 + +/************************************************************************** +Western Digital/SMC Board Types +**************************************************************************/ +#define TYPE_WD8003S 0x02 +#define TYPE_WD8003E 0x03 +#define TYPE_WD8013EBT 0x05 +#define TYPE_WD8003W 0x24 +#define TYPE_WD8003EB 0x25 +#define TYPE_WD8013W 0x26 +#define TYPE_WD8013EP 0x27 +#define TYPE_WD8013WC 0x28 +#define TYPE_WD8013EPC 0x29 +#define TYPE_SMC8216T 0x2a +#define TYPE_SMC8216C 0x2b +#define TYPE_SMC8416T 0x00 /* Bogus entries: the 8416 generates the */ +#define TYPE_SMC8416C 0x00 /* the same codes as the 8216. */ +#define TYPE_SMC8013EBP 0x2c + +/************************************************************************** +3com 3c503 definitions +**************************************************************************/ + +#ifndef _3COM_BASE +#define _3COM_BASE 0x300 +#endif + +#define _3COM_TX_PAGE_OFFSET_8BIT 0x20 +#define _3COM_TX_PAGE_OFFSET_16BIT 0x0 +#define _3COM_RX_PAGE_OFFSET_16BIT 0x20 + +#define _3COM_ASIC_OFFSET 0x400 +#define _3COM_NIC_OFFSET 0x0 + +#define _3COM_PSTR 0 +#define _3COM_PSPR 1 + +#define _3COM_BCFR 3 +#define _3COM_BCFR_2E0 0x01 +#define _3COM_BCFR_2A0 0x02 +#define _3COM_BCFR_280 0x04 +#define _3COM_BCFR_250 0x08 +#define _3COM_BCFR_350 0x10 +#define _3COM_BCFR_330 0x20 +#define _3COM_BCFR_310 0x40 +#define _3COM_BCFR_300 0x80 +#define _3COM_PCFR 4 +#define _3COM_PCFR_PIO 0 +#define _3COM_PCFR_C8000 0x10 +#define _3COM_PCFR_CC000 0x20 +#define _3COM_PCFR_D8000 0x40 +#define _3COM_PCFR_DC000 0x80 +#define _3COM_CR 6 +#define _3COM_CR_RST 0x01 /* Reset GA and NIC */ +#define _3COM_CR_XSEL 0x02 /* Transceiver select. BNC=1(def) AUI=0 */ +#define _3COM_CR_EALO 0x04 /* window EA PROM 0-15 to I/O base */ +#define _3COM_CR_EAHI 0x08 /* window EA PROM 16-31 to I/O base */ +#define _3COM_CR_SHARE 0x10 /* select interrupt sharing option */ +#define _3COM_CR_DBSEL 0x20 /* Double buffer select */ +#define _3COM_CR_DDIR 0x40 /* DMA direction select */ +#define _3COM_CR_START 0x80 /* Start DMA controller */ +#define _3COM_GACFR 5 +#define _3COM_GACFR_MBS0 0x01 +#define _3COM_GACFR_MBS1 0x02 +#define _3COM_GACFR_MBS2 0x04 +#define _3COM_GACFR_RSEL 0x08 /* enable shared memory */ +#define _3COM_GACFR_TEST 0x10 /* for GA testing */ +#define _3COM_GACFR_OWS 0x20 /* select 0WS access to GA */ +#define _3COM_GACFR_TCM 0x40 /* Mask DMA interrupts */ +#define _3COM_GACFR_NIM 0x80 /* Mask NIC interrupts */ +#define _3COM_STREG 7 +#define _3COM_STREG_REV 0x07 /* GA revision */ +#define _3COM_STREG_DIP 0x08 /* DMA in progress */ +#define _3COM_STREG_DTC 0x10 /* DMA terminal count */ +#define _3COM_STREG_OFLW 0x20 /* Overflow */ +#define _3COM_STREG_UFLW 0x40 /* Underflow */ +#define _3COM_STREG_DPRDY 0x80 /* Data port ready */ +#define _3COM_IDCFR 8 +#define _3COM_IDCFR_DRQ0 0x01 /* DMA request 1 select */ +#define _3COM_IDCFR_DRQ1 0x02 /* DMA request 2 select */ +#define _3COM_IDCFR_DRQ2 0x04 /* DMA request 3 select */ +#define _3COM_IDCFR_UNUSED 0x08 /* not used */ +#define _3COM_IDCFR_IRQ2 0x10 /* Interrupt request 2 select */ +#define _3COM_IDCFR_IRQ3 0x20 /* Interrupt request 3 select */ +#define _3COM_IDCFR_IRQ4 0x40 /* Interrupt request 4 select */ +#define _3COM_IDCFR_IRQ5 0x80 /* Interrupt request 5 select */ +#define _3COM_IRQ2 2 +#define _3COM_IRQ3 3 +#define _3COM_IRQ4 4 +#define _3COM_IRQ5 5 +#define _3COM_DAMSB 9 +#define _3COM_DALSB 0x0a +#define _3COM_VPTR2 0x0b +#define _3COM_VPTR1 0x0c +#define _3COM_VPTR0 0x0d +#define _3COM_RFMSB 0x0e +#define _3COM_RFLSB 0x0f + +/************************************************************************** +NE1000/2000 definitions +**************************************************************************/ +#define NE_ASIC_OFFSET 0x10 +#define NE_RESET 0x0F /* Used to reset card */ +#define NE_DATA 0x00 /* Used to read/write NIC mem */ + +#define COMPEX_RL2000_TRIES 200 + +/************************************************************************** +8390 Register Definitions +**************************************************************************/ +#define D8390_P0_COMMAND 0x00 +#define D8390_P0_PSTART 0x01 +#define D8390_P0_PSTOP 0x02 +#define D8390_P0_BOUND 0x03 +#define D8390_P0_TSR 0x04 +#define D8390_P0_TPSR 0x04 +#define D8390_P0_TBCR0 0x05 +#define D8390_P0_TBCR1 0x06 +#define D8390_P0_ISR 0x07 +#define D8390_P0_RSAR0 0x08 +#define D8390_P0_RSAR1 0x09 +#define D8390_P0_RBCR0 0x0A +#define D8390_P0_RBCR1 0x0B +#define D8390_P0_RSR 0x0C +#define D8390_P0_RCR 0x0C +#define D8390_P0_TCR 0x0D +#define D8390_P0_DCR 0x0E +#define D8390_P0_IMR 0x0F +#define D8390_P1_COMMAND 0x00 +#define D8390_P1_PAR0 0x01 +#define D8390_P1_PAR1 0x02 +#define D8390_P1_PAR2 0x03 +#define D8390_P1_PAR3 0x04 +#define D8390_P1_PAR4 0x05 +#define D8390_P1_PAR5 0x06 +#define D8390_P1_CURR 0x07 +#define D8390_P1_MAR0 0x08 + +#define D8390_COMMAND_PS0 0x0 /* Page 0 select */ +#define D8390_COMMAND_PS1 0x40 /* Page 1 select */ +#define D8390_COMMAND_PS2 0x80 /* Page 2 select */ +#define D8390_COMMAND_RD2 0x20 /* Remote DMA control */ +#define D8390_COMMAND_RD1 0x10 +#define D8390_COMMAND_RD0 0x08 +#define D8390_COMMAND_TXP 0x04 /* transmit packet */ +#define D8390_COMMAND_STA 0x02 /* start */ +#define D8390_COMMAND_STP 0x01 /* stop */ + +#define D8390_RCR_MON 0x20 /* monitor mode */ + +#define D8390_DCR_FT1 0x40 +#define D8390_DCR_LS 0x08 /* Loopback select */ +#define D8390_DCR_WTS 0x01 /* Word transfer select */ + +#define D8390_ISR_PRX 0x01 /* successful recv */ +#define D8390_ISR_PTX 0x02 /* successful xmit */ +#define D8390_ISR_RXE 0x04 /* receive error */ +#define D8390_ISR_TXE 0x08 /* transmit error */ +#define D8390_ISR_OVW 0x10 /* Overflow */ +#define D8390_ISR_CNT 0x20 /* Counter overflow */ +#define D8390_ISR_RDC 0x40 /* Remote DMA complete */ +#define D8390_ISR_RST 0x80 /* reset */ + +#define D8390_RSTAT_PRX 0x01 /* successful recv */ +#define D8390_RSTAT_CRC 0x02 /* CRC error */ +#define D8390_RSTAT_FAE 0x04 /* Frame alignment error */ +#define D8390_RSTAT_OVER 0x08 /* FIFO overrun */ + +#define D8390_TXBUF_SIZE 6 +#define D8390_RXBUF_END 32 +#define D8390_PAGE_SIZE 256 + +struct ringbuffer { + unsigned char status; + unsigned char next; + unsigned short len; +}; +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ + diff --git a/gpxe/src/drivers/net/p80211hdr.h b/gpxe/src/drivers/net/p80211hdr.h new file mode 100644 index 0000000..d9a8bbb --- /dev/null +++ b/gpxe/src/drivers/net/p80211hdr.h @@ -0,0 +1,299 @@ +/* src/include/wlan/p80211hdr.h +* +* Macros, types, and functions for handling 802.11 MAC headers +* +* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. +* -------------------------------------------------------------------- +* +* linux-wlan +* +* The contents of this file are subject to the Mozilla Public +* License Version 1.1 (the "License"); you may not use this file +* except in compliance with the License. You may obtain a copy of +* the License at http://www.mozilla.org/MPL/ +* +* Software distributed under the License is distributed on an "AS +* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +* implied. See the License for the specific language governing +* rights and limitations under the License. +* +* Alternatively, the contents of this file may be used under the +* terms of the GNU Public License version 2 (the "GPL"), in which +* case the provisions of the GPL are applicable instead of the +* above. If you wish to allow the use of your version of this file +* only under the terms of the GPL and not to allow others to use +* your version of this file under the MPL, indicate your decision +* by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL. If you do not delete +* the provisions above, a recipient may use your version of this +* file under either the MPL or the GPL. +* +* -------------------------------------------------------------------- +* +* Inquiries regarding the linux-wlan Open Source project can be +* made directly to: +* +* AbsoluteValue Systems Inc. +* info@linux-wlan.com +* http://www.linux-wlan.com +* +* -------------------------------------------------------------------- +* +* Portions of the development of this software were funded by +* Intersil Corporation as part of PRISM(R) chipset product development. +* +* -------------------------------------------------------------------- +* +* This file declares the constants and types used in the interface +* between a wlan driver and the user mode utilities. +* +* Note: +* - Constant values are always in HOST byte order. To assign +* values to multi-byte fields they _must_ be converted to +* ieee byte order. To retrieve multi-byte values from incoming +* frames, they must be converted to host order. +* +* All functions declared here are implemented in p80211.c +* -------------------------------------------------------------------- +*/ + +#ifndef _P80211HDR_H +#define _P80211HDR_H + +/*================================================================*/ +/* System Includes */ + +/*================================================================*/ +/* Project Includes */ + +#ifndef _WLAN_COMPAT_H +#include +#endif + + +/*================================================================*/ +/* Constants */ + +/*--- Sizes -----------------------------------------------*/ +#define WLAN_ADDR_LEN 6 +#define WLAN_CRC_LEN 4 +#define WLAN_BSSID_LEN 6 +#define WLAN_BSS_TS_LEN 8 +#define WLAN_HDR_A3_LEN 24 +#define WLAN_HDR_A4_LEN 30 +#define WLAN_SSID_MAXLEN 32 +#define WLAN_DATA_MAXLEN 2312 +#define WLAN_A3FR_MAXLEN (WLAN_HDR_A3_LEN + WLAN_DATA_MAXLEN + WLAN_CRC_LEN) +#define WLAN_A4FR_MAXLEN (WLAN_HDR_A4_LEN + WLAN_DATA_MAXLEN + WLAN_CRC_LEN) +#define WLAN_BEACON_FR_MAXLEN (WLAN_HDR_A3_LEN + 334) +#define WLAN_ATIM_FR_MAXLEN (WLAN_HDR_A3_LEN + 0) +#define WLAN_DISASSOC_FR_MAXLEN (WLAN_HDR_A3_LEN + 2) +#define WLAN_ASSOCREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 48) +#define WLAN_ASSOCRESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 16) +#define WLAN_REASSOCREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 54) +#define WLAN_REASSOCRESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 16) +#define WLAN_PROBEREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 44) +#define WLAN_PROBERESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 78) +#define WLAN_AUTHEN_FR_MAXLEN (WLAN_HDR_A3_LEN + 261) +#define WLAN_DEAUTHEN_FR_MAXLEN (WLAN_HDR_A3_LEN + 2) +#define WLAN_WEP_NKEYS 4 +#define WLAN_WEP_MAXKEYLEN 13 +#define WLAN_CHALLENGE_IE_LEN 130 +#define WLAN_CHALLENGE_LEN 128 +#define WLAN_WEP_IV_LEN 4 +#define WLAN_WEP_ICV_LEN 4 + +/*--- Frame Control Field -------------------------------------*/ +/* Frame Types */ +#define WLAN_FTYPE_MGMT 0x00 +#define WLAN_FTYPE_CTL 0x01 +#define WLAN_FTYPE_DATA 0x02 + +/* Frame subtypes */ +/* Management */ +#define WLAN_FSTYPE_ASSOCREQ 0x00 +#define WLAN_FSTYPE_ASSOCRESP 0x01 +#define WLAN_FSTYPE_REASSOCREQ 0x02 +#define WLAN_FSTYPE_REASSOCRESP 0x03 +#define WLAN_FSTYPE_PROBEREQ 0x04 +#define WLAN_FSTYPE_PROBERESP 0x05 +#define WLAN_FSTYPE_BEACON 0x08 +#define WLAN_FSTYPE_ATIM 0x09 +#define WLAN_FSTYPE_DISASSOC 0x0a +#define WLAN_FSTYPE_AUTHEN 0x0b +#define WLAN_FSTYPE_DEAUTHEN 0x0c + +/* Control */ +#define WLAN_FSTYPE_BLOCKACKREQ 0x8 +#define WLAN_FSTYPE_BLOCKACK 0x9 +#define WLAN_FSTYPE_PSPOLL 0x0a +#define WLAN_FSTYPE_RTS 0x0b +#define WLAN_FSTYPE_CTS 0x0c +#define WLAN_FSTYPE_ACK 0x0d +#define WLAN_FSTYPE_CFEND 0x0e +#define WLAN_FSTYPE_CFENDCFACK 0x0f + +/* Data */ +#define WLAN_FSTYPE_DATAONLY 0x00 +#define WLAN_FSTYPE_DATA_CFACK 0x01 +#define WLAN_FSTYPE_DATA_CFPOLL 0x02 +#define WLAN_FSTYPE_DATA_CFACK_CFPOLL 0x03 +#define WLAN_FSTYPE_NULL 0x04 +#define WLAN_FSTYPE_CFACK 0x05 +#define WLAN_FSTYPE_CFPOLL 0x06 +#define WLAN_FSTYPE_CFACK_CFPOLL 0x07 + + +/*================================================================*/ +/* Macros */ + +/*--- FC Macros ----------------------------------------------*/ +/* Macros to get/set the bitfields of the Frame Control Field */ +/* GET_FC_??? - takes the host byte-order value of an FC */ +/* and retrieves the value of one of the */ +/* bitfields and moves that value so its lsb is */ +/* in bit 0. */ +/* SET_FC_??? - takes a host order value for one of the FC */ +/* bitfields and moves it to the proper bit */ +/* location for ORing into a host order FC. */ +/* To send the FC produced from SET_FC_???, */ +/* one must put the bytes in IEEE order. */ +/* e.g. */ +/* printf("the frame subtype is %x", */ +/* GET_FC_FTYPE( ieee2host( rx.fc ))) */ +/* */ +/* tx.fc = host2ieee( SET_FC_FTYPE(WLAN_FTYP_CTL) | */ +/* SET_FC_FSTYPE(WLAN_FSTYPE_RTS) ); */ +/*------------------------------------------------------------*/ + +#define WLAN_GET_FC_PVER(n) (((UINT16)(n)) & (BIT0 | BIT1)) +#define WLAN_GET_FC_FTYPE(n) ((((UINT16)(n)) & (BIT2 | BIT3)) >> 2) +#define WLAN_GET_FC_FSTYPE(n) ((((UINT16)(n)) & (BIT4|BIT5|BIT6|BIT7)) >> 4) +#define WLAN_GET_FC_TODS(n) ((((UINT16)(n)) & (BIT8)) >> 8) +#define WLAN_GET_FC_FROMDS(n) ((((UINT16)(n)) & (BIT9)) >> 9) +#define WLAN_GET_FC_MOREFRAG(n) ((((UINT16)(n)) & (BIT10)) >> 10) +#define WLAN_GET_FC_RETRY(n) ((((UINT16)(n)) & (BIT11)) >> 11) +#define WLAN_GET_FC_PWRMGT(n) ((((UINT16)(n)) & (BIT12)) >> 12) +#define WLAN_GET_FC_MOREDATA(n) ((((UINT16)(n)) & (BIT13)) >> 13) +#define WLAN_GET_FC_ISWEP(n) ((((UINT16)(n)) & (BIT14)) >> 14) +#define WLAN_GET_FC_ORDER(n) ((((UINT16)(n)) & (BIT15)) >> 15) + +#define WLAN_SET_FC_PVER(n) ((UINT16)(n)) +#define WLAN_SET_FC_FTYPE(n) (((UINT16)(n)) << 2) +#define WLAN_SET_FC_FSTYPE(n) (((UINT16)(n)) << 4) +#define WLAN_SET_FC_TODS(n) (((UINT16)(n)) << 8) +#define WLAN_SET_FC_FROMDS(n) (((UINT16)(n)) << 9) +#define WLAN_SET_FC_MOREFRAG(n) (((UINT16)(n)) << 10) +#define WLAN_SET_FC_RETRY(n) (((UINT16)(n)) << 11) +#define WLAN_SET_FC_PWRMGT(n) (((UINT16)(n)) << 12) +#define WLAN_SET_FC_MOREDATA(n) (((UINT16)(n)) << 13) +#define WLAN_SET_FC_ISWEP(n) (((UINT16)(n)) << 14) +#define WLAN_SET_FC_ORDER(n) (((UINT16)(n)) << 15) + +/*--- Duration Macros ----------------------------------------*/ +/* Macros to get/set the bitfields of the Duration Field */ +/* - the duration value is only valid when bit15 is zero */ +/* - the firmware handles these values, so I'm not going */ +/* these macros right now. */ +/*------------------------------------------------------------*/ + +/*--- Sequence Control Macros -------------------------------*/ +/* Macros to get/set the bitfields of the Sequence Control */ +/* Field. */ +/*------------------------------------------------------------*/ +#define WLAN_GET_SEQ_FRGNUM(n) (((UINT16)(n)) & (BIT0|BIT1|BIT2|BIT3)) +#define WLAN_GET_SEQ_SEQNUM(n) ((((UINT16)(n)) & (~(BIT0|BIT1|BIT2|BIT3))) >> 4) + +/*--- Data ptr macro -----------------------------------------*/ +/* Creates a UINT8* to the data portion of a frame */ +/* Assumes you're passing in a ptr to the beginning of the hdr*/ +/*------------------------------------------------------------*/ +#define WLAN_HDR_A3_DATAP(p) (((UINT8*)(p)) + WLAN_HDR_A3_LEN) +#define WLAN_HDR_A4_DATAP(p) (((UINT8*)(p)) + WLAN_HDR_A4_LEN) + +#define DOT11_RATE5_ISBASIC_GET(r) (((UINT8)(r)) & BIT7) + +/*================================================================*/ +/* Types */ + +/* BSS Timestamp */ +typedef UINT8 wlan_bss_ts_t[WLAN_BSS_TS_LEN]; + +/* Generic 802.11 Header types */ + +typedef struct p80211_hdr_a3 +{ + UINT16 fc; + UINT16 dur; + UINT8 a1[WLAN_ADDR_LEN]; + UINT8 a2[WLAN_ADDR_LEN]; + UINT8 a3[WLAN_ADDR_LEN]; + UINT16 seq; +} __WLAN_ATTRIB_PACK__ p80211_hdr_a3_t; + +typedef struct p80211_hdr_a4 +{ + UINT16 fc; + UINT16 dur; + UINT8 a1[WLAN_ADDR_LEN]; + UINT8 a2[WLAN_ADDR_LEN]; + UINT8 a3[WLAN_ADDR_LEN]; + UINT16 seq; + UINT8 a4[WLAN_ADDR_LEN]; +} __WLAN_ATTRIB_PACK__ p80211_hdr_a4_t; + +typedef union p80211_hdr +{ + p80211_hdr_a3_t a3; + p80211_hdr_a4_t a4; +} __WLAN_ATTRIB_PACK__ p80211_hdr_t; + + +/*================================================================*/ +/* Extern Declarations */ + + +/*================================================================*/ +/* Function Declarations */ + +/* Frame and header lenght macros */ + +#define WLAN_CTL_FRAMELEN(fstype) (\ + (fstype) == WLAN_FSTYPE_BLOCKACKREQ ? 24 : \ + (fstype) == WLAN_FSTYPE_BLOCKACK ? 152 : \ + (fstype) == WLAN_FSTYPE_PSPOLL ? 20 : \ + (fstype) == WLAN_FSTYPE_RTS ? 20 : \ + (fstype) == WLAN_FSTYPE_CTS ? 14 : \ + (fstype) == WLAN_FSTYPE_ACK ? 14 : \ + (fstype) == WLAN_FSTYPE_CFEND ? 20 : \ + (fstype) == WLAN_FSTYPE_CFENDCFACK ? 20 : 4) + +#define WLAN_FCS_LEN 4 + +/* ftcl in HOST order */ +inline static UINT16 p80211_headerlen(UINT16 fctl) +{ + UINT16 hdrlen = 0; + + switch ( WLAN_GET_FC_FTYPE(fctl) ) { + case WLAN_FTYPE_MGMT: + hdrlen = WLAN_HDR_A3_LEN; + break; + case WLAN_FTYPE_DATA: + hdrlen = WLAN_HDR_A3_LEN; + if ( WLAN_GET_FC_TODS(fctl) && WLAN_GET_FC_FROMDS(fctl) ) { + hdrlen += WLAN_ADDR_LEN; + } + break; + case WLAN_FTYPE_CTL: + hdrlen = WLAN_CTL_FRAMELEN(WLAN_GET_FC_FSTYPE(fctl)) - + WLAN_FCS_LEN; + break; + default: + hdrlen = WLAN_HDR_A3_LEN; + } + + return hdrlen; +} + +#endif /* _P80211HDR_H */ diff --git a/gpxe/src/drivers/net/pcnet32.c b/gpxe/src/drivers/net/pcnet32.c new file mode 100644 index 0000000..d8529da --- /dev/null +++ b/gpxe/src/drivers/net/pcnet32.c @@ -0,0 +1,1025 @@ +/************************************************************************** +* +* pcnet32.c -- Etherboot device driver for the AMD PCnet32 +* Written 2003-2003 by Timothy Legge +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Portions of this code based on: +* pcnet32.c: An AMD PCnet32 ethernet driver for linux: +* +* (C) 1996-1999 Thomas Bogendoerfer +* See Linux Driver for full information +* +* The transmit and poll functions were written with reference to: +* lance.c - LANCE NIC driver for Etherboot written by Ken Yap +* +* Linux Driver Version 1.27a, 10.02.2002 +* +* +* REVISION HISTORY: +* ================ +* v1.0 08-06-2003 timlegge Initial port of Linux driver +* v1.1 08-23-2003 timlegge Add multicast support +* v1.2 01-17-2004 timlegge Initial driver output cleanup +* v1.3 03-29-2004 timlegge More driver cleanup +* +* Indent Options: indent -kr -i8 +***************************************************************************/ + +#include "etherboot.h" +#include "nic.h" +#include +#include +#include "mii.h" + +/* void hex_dump(const char *data, const unsigned int len); */ + +/* Etherboot Specific definations */ +#define drv_version "v1.3" +#define drv_date "03-29-2004" + +static u32 ioaddr; /* Globally used for the card's io address */ +static struct nic_operations pcnet32_operations; + +#ifdef EDEBUG +#define dprintf(x) printf x +#else +#define dprintf(x) +#endif + +/* Condensed operations for readability. */ +#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr)) + +/* End Etherboot Specific */ + +static int cards_found = 0 /* __initdata */ ; + +#ifdef REMOVE +/* FIXME: Remove these they are probably pointless */ + +/* + * VLB I/O addresses + */ +static unsigned int pcnet32_portlist[] /*__initdata */ = +{ 0x300, 0x320, 0x340, 0x360, 0 }; + +static int pcnet32_debug = 1; +static int tx_start = 1; /* Mapping -- 0:20, 1:64, 2:128, 3:~220 (depends on chip vers) */ +static int pcnet32vlb; /* check for VLB cards ? */ + +static struct net_device *pcnet32_dev; + +static int max_interrupt_work = 80; +static int rx_copybreak = 200; +#endif +#define PCNET32_PORT_AUI 0x00 +#define PCNET32_PORT_10BT 0x01 +#define PCNET32_PORT_GPSI 0x02 +#define PCNET32_PORT_MII 0x03 + +#define PCNET32_PORT_PORTSEL 0x03 +#define PCNET32_PORT_ASEL 0x04 +#define PCNET32_PORT_100 0x40 +#define PCNET32_PORT_FD 0x80 + +#define PCNET32_DMA_MASK 0xffffffff + +/* + * table to translate option values from tulip + * to internal options + */ +static unsigned char options_mapping[] = { + PCNET32_PORT_ASEL, /* 0 Auto-select */ + PCNET32_PORT_AUI, /* 1 BNC/AUI */ + PCNET32_PORT_AUI, /* 2 AUI/BNC */ + PCNET32_PORT_ASEL, /* 3 not supported */ + PCNET32_PORT_10BT | PCNET32_PORT_FD, /* 4 10baseT-FD */ + PCNET32_PORT_ASEL, /* 5 not supported */ + PCNET32_PORT_ASEL, /* 6 not supported */ + PCNET32_PORT_ASEL, /* 7 not supported */ + PCNET32_PORT_ASEL, /* 8 not supported */ + PCNET32_PORT_MII, /* 9 MII 10baseT */ + PCNET32_PORT_MII | PCNET32_PORT_FD, /* 10 MII 10baseT-FD */ + PCNET32_PORT_MII, /* 11 MII (autosel) */ + PCNET32_PORT_10BT, /* 12 10BaseT */ + PCNET32_PORT_MII | PCNET32_PORT_100, /* 13 MII 100BaseTx */ + PCNET32_PORT_MII | PCNET32_PORT_100 | PCNET32_PORT_FD, /* 14 MII 100BaseTx-FD */ + PCNET32_PORT_ASEL /* 15 not supported */ +}; + +#define MAX_UNITS 8 /* More are supported, limit only on options */ +static int options[MAX_UNITS]; +static int full_duplex[MAX_UNITS]; + +/* + * Theory of Operation + * + * This driver uses the same software structure as the normal lance + * driver. So look for a verbose description in lance.c. The differences + * to the normal lance driver is the use of the 32bit mode of PCnet32 + * and PCnetPCI chips. Because these chips are 32bit chips, there is no + * 16MB limitation and we don't need bounce buffers. + */ + + + +/* + * Set the number of Tx and Rx buffers, using Log_2(# buffers). + * Reasonable default values are 4 Tx buffers, and 16 Rx buffers. + * That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4). + */ +#ifndef PCNET32_LOG_TX_BUFFERS +#define PCNET32_LOG_TX_BUFFERS 1 +#define PCNET32_LOG_RX_BUFFERS 2 +#endif + +#define TX_RING_SIZE (1 << (PCNET32_LOG_TX_BUFFERS)) +#define TX_RING_MOD_MASK (TX_RING_SIZE - 1) +/* FIXME: Fix this to allow multiple tx_ring descriptors */ +#define TX_RING_LEN_BITS 0x0000 /*PCNET32_LOG_TX_BUFFERS) << 12) */ + +#define RX_RING_SIZE (1 << (PCNET32_LOG_RX_BUFFERS)) +#define RX_RING_MOD_MASK (RX_RING_SIZE - 1) +#define RX_RING_LEN_BITS ((PCNET32_LOG_RX_BUFFERS) << 4) + +#define PKT_BUF_SZ 1544 + +/* Offsets from base I/O address. */ +#define PCNET32_WIO_RDP 0x10 +#define PCNET32_WIO_RAP 0x12 +#define PCNET32_WIO_RESET 0x14 +#define PCNET32_WIO_BDP 0x16 + +#define PCNET32_DWIO_RDP 0x10 +#define PCNET32_DWIO_RAP 0x14 +#define PCNET32_DWIO_RESET 0x18 +#define PCNET32_DWIO_BDP 0x1C + +#define PCNET32_TOTAL_SIZE 0x20 + +/* The PCNET32 Rx and Tx ring descriptors. */ +struct pcnet32_rx_head { + u32 base; + s16 buf_length; + s16 status; + u32 msg_length; + u32 reserved; +}; + +struct pcnet32_tx_head { + u32 base; + s16 length; + s16 status; + u32 misc; + u32 reserved; +}; + +/* The PCNET32 32-Bit initialization block, described in databook. */ +struct pcnet32_init_block { + u16 mode; + u16 tlen_rlen; + u8 phys_addr[6]; + u16 reserved; + u32 filter[2]; + /* Receive and transmit ring base, along with extra bits. */ + u32 rx_ring; + u32 tx_ring; +}; +/* PCnet32 access functions */ +struct pcnet32_access { + u16(*read_csr) (unsigned long, int); + void (*write_csr) (unsigned long, int, u16); + u16(*read_bcr) (unsigned long, int); + void (*write_bcr) (unsigned long, int, u16); + u16(*read_rap) (unsigned long); + void (*write_rap) (unsigned long, u16); + void (*reset) (unsigned long); +}; + +/* Define the TX and RX Descriptors and Rings */ +struct { + struct pcnet32_tx_head tx_ring[TX_RING_SIZE] + __attribute__ ((aligned(16))); + struct pcnet32_rx_head rx_ring[RX_RING_SIZE] + __attribute__ ((aligned(16))); + unsigned char txb[PKT_BUF_SZ * TX_RING_SIZE]; + unsigned char rxb[RX_RING_SIZE * PKT_BUF_SZ]; +} pcnet32_bufs __shared; + +/* May need to be moved to mii.h */ +struct mii_if_info { + int phy_id; + int advertising; + unsigned int full_duplex:1; /* is full duplex? */ +}; + +/* + * The first three fields of pcnet32_private are read by the ethernet device + * so we allocate the structure should be allocated by pci_alloc_consistent(). + */ +#define MII_CNT 4 +struct pcnet32_private { + struct pcnet32_init_block init_block; + struct pci_dev *pci_dev; /* Pointer to the associated pci device structure */ + const char *name; + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct sk_buff *tx_skbuff[TX_RING_SIZE]; + struct sk_buff *rx_skbuff[RX_RING_SIZE]; + struct pcnet32_access a; + unsigned int cur_rx, cur_tx; /* The next free ring entry */ + char tx_full; + int options; + int shared_irq:1, /* shared irq possible */ + ltint:1, /* enable TxDone-intr inhibitor */ + dxsuflo:1, /* disable transmit stop on uflo */ + mii:1; /* mii port available */ + struct mii_if_info mii_if; + unsigned char phys[MII_CNT]; + struct net_device *next; + int full_duplex:1; +} lpx; + +static struct pcnet32_private *lp; + +static int mdio_read(struct nic *nic __unused, int phy_id, int reg_num); +#if 0 +static void mdio_write(struct nic *nic __unused, int phy_id, int reg_num, + int val); +#endif +enum pci_flags_bit { + PCI_USES_IO = 1, PCI_USES_MEM = 2, PCI_USES_MASTER = 4, + PCI_ADDR0 = 0x10 << 0, PCI_ADDR1 = 0x10 << 1, PCI_ADDR2 = + 0x10 << 2, PCI_ADDR3 = 0x10 << 3, +}; + + +static u16 pcnet32_wio_read_csr(unsigned long addr, int index) +{ + outw(index, addr + PCNET32_WIO_RAP); + return inw(addr + PCNET32_WIO_RDP); +} + +static void pcnet32_wio_write_csr(unsigned long addr, int index, u16 val) +{ + outw(index, addr + PCNET32_WIO_RAP); + outw(val, addr + PCNET32_WIO_RDP); +} + +static u16 pcnet32_wio_read_bcr(unsigned long addr, int index) +{ + outw(index, addr + PCNET32_WIO_RAP); + return inw(addr + PCNET32_WIO_BDP); +} + +static void pcnet32_wio_write_bcr(unsigned long addr, int index, u16 val) +{ + outw(index, addr + PCNET32_WIO_RAP); + outw(val, addr + PCNET32_WIO_BDP); +} + +static u16 pcnet32_wio_read_rap(unsigned long addr) +{ + return inw(addr + PCNET32_WIO_RAP); +} + +static void pcnet32_wio_write_rap(unsigned long addr, u16 val) +{ + outw(val, addr + PCNET32_WIO_RAP); +} + +static void pcnet32_wio_reset(unsigned long addr) +{ + inw(addr + PCNET32_WIO_RESET); +} + +static int pcnet32_wio_check(unsigned long addr) +{ + outw(88, addr + PCNET32_WIO_RAP); + return (inw(addr + PCNET32_WIO_RAP) == 88); +} + +static struct pcnet32_access pcnet32_wio = { + read_csr:pcnet32_wio_read_csr, + write_csr:pcnet32_wio_write_csr, + read_bcr:pcnet32_wio_read_bcr, + write_bcr:pcnet32_wio_write_bcr, + read_rap:pcnet32_wio_read_rap, + write_rap:pcnet32_wio_write_rap, + reset:pcnet32_wio_reset +}; + +static u16 pcnet32_dwio_read_csr(unsigned long addr, int index) +{ + outl(index, addr + PCNET32_DWIO_RAP); + return (inl(addr + PCNET32_DWIO_RDP) & 0xffff); +} + +static void pcnet32_dwio_write_csr(unsigned long addr, int index, u16 val) +{ + outl(index, addr + PCNET32_DWIO_RAP); + outl(val, addr + PCNET32_DWIO_RDP); +} + +static u16 pcnet32_dwio_read_bcr(unsigned long addr, int index) +{ + outl(index, addr + PCNET32_DWIO_RAP); + return (inl(addr + PCNET32_DWIO_BDP) & 0xffff); +} + +static void pcnet32_dwio_write_bcr(unsigned long addr, int index, u16 val) +{ + outl(index, addr + PCNET32_DWIO_RAP); + outl(val, addr + PCNET32_DWIO_BDP); +} + +static u16 pcnet32_dwio_read_rap(unsigned long addr) +{ + return (inl(addr + PCNET32_DWIO_RAP) & 0xffff); +} + +static void pcnet32_dwio_write_rap(unsigned long addr, u16 val) +{ + outl(val, addr + PCNET32_DWIO_RAP); +} + +static void pcnet32_dwio_reset(unsigned long addr) +{ + inl(addr + PCNET32_DWIO_RESET); +} + +static int pcnet32_dwio_check(unsigned long addr) +{ + outl(88, addr + PCNET32_DWIO_RAP); + return ((inl(addr + PCNET32_DWIO_RAP) & 0xffff) == 88); +} + +static struct pcnet32_access pcnet32_dwio = { + read_csr:pcnet32_dwio_read_csr, + write_csr:pcnet32_dwio_write_csr, + read_bcr:pcnet32_dwio_read_bcr, + write_bcr:pcnet32_dwio_write_bcr, + read_rap:pcnet32_dwio_read_rap, + write_rap:pcnet32_dwio_write_rap, + reset:pcnet32_dwio_reset +}; + + +/* Initialize the PCNET32 Rx and Tx rings. */ +static int pcnet32_init_ring(struct nic *nic) +{ + int i; + + lp->tx_full = 0; + lp->cur_rx = lp->cur_tx = 0; + + for (i = 0; i < RX_RING_SIZE; i++) { + pcnet32_bufs.rx_ring[i].base = + virt_to_le32desc(&pcnet32_bufs.rxb[i]); + pcnet32_bufs.rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ); + pcnet32_bufs.rx_ring[i].status = le16_to_cpu(0x8000); + } + + /* The Tx buffer address is filled in as needed, but we do need to clear + the upper ownership bit. */ + for (i = 0; i < TX_RING_SIZE; i++) { + pcnet32_bufs.tx_ring[i].base = 0; + pcnet32_bufs.tx_ring[i].status = 0; + } + + + lp->init_block.tlen_rlen = + le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS); + for (i = 0; i < 6; i++) + lp->init_block.phys_addr[i] = nic->node_addr[i]; + lp->init_block.rx_ring = virt_to_le32desc(&pcnet32_bufs.rx_ring[0]); + lp->init_block.tx_ring = virt_to_le32desc(&pcnet32_bufs.tx_ring[0]); + return 0; +} + +/************************************************************************** +RESET - Reset adapter +***************************************************************************/ +static void pcnet32_reset(struct nic *nic) +{ + /* put the card in its initial state */ + u16 val; + int i; + + /* Reset the PCNET32 */ + lp->a.reset(ioaddr); + + /* switch pcnet32 to 32bit mode */ + lp->a.write_bcr(ioaddr, 20, 2); + + /* set/reset autoselect bit */ + val = lp->a.read_bcr(ioaddr, 2) & ~2; + if (lp->options & PCNET32_PORT_ASEL) + val |= 2; + lp->a.write_bcr(ioaddr, 2, val); + + /* handle full duplex setting */ + if (lp->full_duplex) { + val = lp->a.read_bcr(ioaddr, 9) & ~3; + if (lp->options & PCNET32_PORT_FD) { + val |= 1; + if (lp->options == + (PCNET32_PORT_FD | PCNET32_PORT_AUI)) + val |= 2; + } else if (lp->options & PCNET32_PORT_ASEL) { + /* workaround of xSeries250, turn on for 79C975 only */ + i = ((lp->a. + read_csr(ioaddr, + 88) | (lp->a.read_csr(ioaddr, + 89) << 16)) >> + 12) & 0xffff; + if (i == 0x2627) + val |= 3; + } + lp->a.write_bcr(ioaddr, 9, val); + } + + /* set/reset GPSI bit in test register */ + val = lp->a.read_csr(ioaddr, 124) & ~0x10; + if ((lp->options & PCNET32_PORT_PORTSEL) == PCNET32_PORT_GPSI) + val |= 0x10; + lp->a.write_csr(ioaddr, 124, val); + + if (lp->mii && !(lp->options & PCNET32_PORT_ASEL)) { + val = lp->a.read_bcr(ioaddr, 32) & ~0x38; /* disable Auto Negotiation, set 10Mpbs, HD */ + if (lp->options & PCNET32_PORT_FD) + val |= 0x10; + if (lp->options & PCNET32_PORT_100) + val |= 0x08; + lp->a.write_bcr(ioaddr, 32, val); + } else { + if (lp->options & PCNET32_PORT_ASEL) { /* enable auto negotiate, setup, disable fd */ + val = lp->a.read_bcr(ioaddr, 32) & ~0x98; + val |= 0x20; + lp->a.write_bcr(ioaddr, 32, val); + } + } + +#ifdef DO_DXSUFLO + if (lp->dxsuflo) { /* Disable transmit stop on underflow */ + val = lp->a.read_csr(ioaddr, 3); + val |= 0x40; + lp->a.write_csr(ioaddr, 3, val); + } +#endif + if (1) + { + //disable interrupts + val = lp->a.read_csr(ioaddr, 3); + val = val + | (1 << 14) //BABLM intr disabled + | (1 << 12) //MISSM missed frame mask intr disabled + | (1 << 10) //RINTM receive intr disabled + | (1 << 9) //TINTM transmit intr disabled + | (1 << 8) //IDONM init done intr disabled + ; + lp->a.write_csr(ioaddr, 3, val); + } + + if (lp->ltint) { /* Enable TxDone-intr inhibitor */ + val = lp->a.read_csr(ioaddr, 5); + val |= (1 << 14); + lp->a.write_csr(ioaddr, 5, val); + } + lp->init_block.mode = + le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7); + lp->init_block.filter[0] = 0xffffffff; + lp->init_block.filter[1] = 0xffffffff; + + pcnet32_init_ring(nic); + + + /* Re-initialize the PCNET32, and start it when done. */ + lp->a.write_csr(ioaddr, 1, + (virt_to_bus(&lp->init_block)) & 0xffff); + lp->a.write_csr(ioaddr, 2, (virt_to_bus(&lp->init_block)) >> 16); + lp->a.write_csr(ioaddr, 4, 0x0915); + lp->a.write_csr(ioaddr, 0, 0x0001); + + + i = 0; + while (i++ < 100) + if (lp->a.read_csr(ioaddr, 0) & 0x0100) + break; + /* + * We used to clear the InitDone bit, 0x0100, here but Mark Stockton + * reports that doing so triggers a bug in the '974. + */ + lp->a.write_csr(ioaddr, 0, 0x0042); + + dprintf(("pcnet32 open, csr0 %hX.\n", lp->a.read_csr(ioaddr, 0))); + +} + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int pcnet32_poll(struct nic *nic __unused, int retrieve) +{ + /* return true if there's an ethernet packet ready to read */ + /* nic->packet should contain data on return */ + /* nic->packetlen should contain length of data */ + + signed char status; + int entry; + + entry = lp->cur_rx & RX_RING_MOD_MASK; + status = (le16_to_cpu(pcnet32_bufs.rx_ring[entry].status) >> 8); + + if (status < 0) + return 0; + + if ( ! retrieve ) return 1; + + if (status == 0x03) { + nic->packetlen = + (le32_to_cpu(pcnet32_bufs.rx_ring[entry].msg_length) + & 0xfff) - 4; + memcpy(nic->packet, &pcnet32_bufs.rxb[entry], nic->packetlen); + + /* Andrew Boyd of QNX reports that some revs of the 79C765 + * clear the buffer length */ + pcnet32_bufs.rx_ring[entry].buf_length + = le16_to_cpu(-PKT_BUF_SZ); + /* prime for next receive */ + pcnet32_bufs.rx_ring[entry].status |= le16_to_cpu(0x8000); + /* Switch to the next Rx ring buffer */ + lp->cur_rx++; + + } else { + return 0; + } + + return 1; +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static void pcnet32_transmit(struct nic *nic __unused, const char *d, /* Destination */ + unsigned int t, /* Type */ + unsigned int s, /* size */ + const char *p) +{ /* Packet */ + /* send the packet to destination */ + unsigned long time; + u8 *ptxb; + u16 nstype; + u16 status; + int entry = 0; /*lp->cur_tx & TX_RING_MOD_MASK; */ + + status = 0x8300; + /* point to the current txb incase multiple tx_rings are used */ + ptxb = pcnet32_bufs.txb + (lp->cur_tx * PKT_BUF_SZ); + + /* copy the packet to ring buffer */ + memcpy(ptxb, d, ETH_ALEN); /* dst */ + memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */ + nstype = htons((u16) t); /* type */ + memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2); /* type */ + memcpy(ptxb + ETH_HLEN, p, s); + + s += ETH_HLEN; + while (s < ETH_ZLEN) /* pad to min length */ + ptxb[s++] = '\0'; + + pcnet32_bufs.tx_ring[entry].length = le16_to_cpu(-s); + pcnet32_bufs.tx_ring[entry].misc = 0x00000000; + pcnet32_bufs.tx_ring[entry].base = (u32) virt_to_le32desc(ptxb); + + /* we set the top byte as the very last thing */ + pcnet32_bufs.tx_ring[entry].status = le16_to_cpu(status); + + + /* Trigger an immediate send poll */ + lp->a.write_csr(ioaddr, 0, 0x0048); + + /* wait for transmit complete */ + lp->cur_tx = 0; /* (lp->cur_tx + 1); */ + time = currticks() + TICKS_PER_SEC; /* wait one second */ + while (currticks() < time && + ((short) le16_to_cpu(pcnet32_bufs.tx_ring[entry].status) < 0)); + + if ((short) le16_to_cpu(pcnet32_bufs.tx_ring[entry].status) < 0) + printf("PCNET32 timed out on transmit\n"); + + /* Stop pointing at the current txb + * otherwise the card continues to send the packet */ + pcnet32_bufs.tx_ring[entry].base = 0; + +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void pcnet32_disable ( struct nic *nic __unused ) { + /* Stop the PCNET32 here -- it ocassionally polls memory if we don't */ + lp->a.write_csr(ioaddr, 0, 0x0004); + + /* + * Switch back to 16-bit mode to avoid problems with dumb + * DOS packet driver after a warm reboot + */ + lp->a.write_bcr(ioaddr, 20, 0); +} + +/************************************************************************** +IRQ - Enable, Disable, or Force interrupts +***************************************************************************/ +static void pcnet32_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +You should omit the last argument struct pci_device * for a non-PCI NIC +***************************************************************************/ +static int pcnet32_probe ( struct nic *nic, struct pci_device *pci ) { + + int i, media; + int fdx, mii, fset, dxsuflo, ltint; + int chip_version; + struct pcnet32_access *a = NULL; + char *chipname; + u8 promaddr[6]; + int shared = 1; + + if (pci->ioaddr == 0) + return 0; + + /* BASE is used throughout to address the card */ + ioaddr = pci->ioaddr; + printf("pcnet32.c: Found %s, Vendor=0x%hX Device=0x%hX\n", + pci->driver_name, pci->vendor, pci->device); + + nic->irqno = 0; + nic->ioaddr = pci->ioaddr & ~3; + + /* reset the chip */ + pcnet32_wio_reset(ioaddr); + + /* NOTE: 16-bit check is first, otherwise some older PCnet chips fail */ + if (pcnet32_wio_read_csr(ioaddr, 0) == 4 + && pcnet32_wio_check(ioaddr)) { + a = &pcnet32_wio; + } else { + pcnet32_dwio_reset(ioaddr); + if (pcnet32_dwio_read_csr(ioaddr, 0) == 4 + && pcnet32_dwio_check(ioaddr)) { + a = &pcnet32_dwio; + } else + return 0; + } + + chip_version = + a->read_csr(ioaddr, 88) | (a->read_csr(ioaddr, 89) << 16); + + dprintf(("PCnet chip version is 0x%X\n", chip_version)); + if ((chip_version & 0xfff) != 0x003) + return 0; + + /* initialize variables */ + fdx = mii = fset = dxsuflo = ltint = 0; + chip_version = (chip_version >> 12) & 0xffff; + + switch (chip_version) { + case 0x2420: + chipname = "PCnet/PCI 79C970"; /* PCI */ + break; + case 0x2430: + if (shared) + chipname = "PCnet/PCI 79C970"; /* 970 gives the wrong chip id back */ + else + chipname = "PCnet/32 79C965"; /* 486/VL bus */ + break; + case 0x2621: + chipname = "PCnet/PCI II 79C970A"; /* PCI */ + fdx = 1; + break; + case 0x2623: + chipname = "PCnet/FAST 79C971"; /* PCI */ + fdx = 1; + mii = 1; + fset = 1; + ltint = 1; + break; + case 0x2624: + chipname = "PCnet/FAST+ 79C972"; /* PCI */ + fdx = 1; + mii = 1; + fset = 1; + break; + case 0x2625: + chipname = "PCnet/FAST III 79C973"; /* PCI */ + fdx = 1; + mii = 1; + break; + case 0x2626: + chipname = "PCnet/Home 79C978"; /* PCI */ + fdx = 1; + /* + * This is based on specs published at www.amd.com. This section + * assumes that a card with a 79C978 wants to go into 1Mb HomePNA + * mode. The 79C978 can also go into standard ethernet, and there + * probably should be some sort of module option to select the + * mode by which the card should operate + */ + /* switch to home wiring mode */ + media = a->read_bcr(ioaddr, 49); + + printf("media reset to %#x.\n", media); + a->write_bcr(ioaddr, 49, media); + break; + case 0x2627: + chipname = "PCnet/FAST III 79C975"; /* PCI */ + fdx = 1; + mii = 1; + break; + default: + chipname = "UNKNOWN"; + printf("PCnet version %#x, no PCnet32 chip.\n", + chip_version); + return 0; + } + + /* + * On selected chips turn on the BCR18:NOUFLO bit. This stops transmit + * starting until the packet is loaded. Strike one for reliability, lose + * one for latency - although on PCI this isnt a big loss. Older chips + * have FIFO's smaller than a packet, so you can't do this. + */ + + if (fset) { + a->write_bcr(ioaddr, 18, + (a->read_bcr(ioaddr, 18) | 0x0800)); + a->write_csr(ioaddr, 80, + (a->read_csr(ioaddr, 80) & 0x0C00) | 0x0c00); + dxsuflo = 1; + ltint = 1; + } + + DBG ( "%s at %hX,", chipname, (unsigned int) ioaddr ); + + /* read PROM address */ + for (i = 0; i < 6; i++) + promaddr[i] = inb(ioaddr + i); + + /* Update the nic structure with the MAC Address */ + for (i = 0; i < ETH_ALEN; i++) { + nic->node_addr[i] = promaddr[i]; + } + + /* Print out some hardware info */ + DBG ( "%s: IO Addr 0x%hX, MAC Addr %s\n ", chipname, (unsigned int) ioaddr, + eth_ntoa ( nic->node_addr ) ); + + /* Set to pci bus master */ + adjust_pci_device(pci); + + /* point to private storage */ + lp = &lpx; + +#if EBDEBUG + if (((chip_version + 1) & 0xfffe) == 0x2624) { /* Version 0x2623 or 0x2624 */ + i = a->read_csr(ioaddr, 80) & 0x0C00; /* Check tx_start_pt */ + dprintf((" tx_start_pt(0x%hX):", i)); + switch (i >> 10) { + case 0: + dprintf((" 20 bytes,")); + break; + case 1: + dprintf((" 64 bytes,")); + break; + case 2: + dprintf((" 128 bytes,")); + break; + case 3: + dprintf(("~220 bytes,")); + break; + } + i = a->read_bcr(ioaddr, 18); /* Check Burst/Bus control */ + dprintf((" BCR18(%hX):", i & 0xffff)); + if (i & (1 << 5)) + dprintf(("BurstWrEn ")); + if (i & (1 << 6)) + dprintf(("BurstRdEn ")); + if (i & (1 << 7)) + dprintf(("DWordIO ")); + if (i & (1 << 11)) + dprintf(("NoUFlow ")); + i = a->read_bcr(ioaddr, 25); + dprintf((" SRAMSIZE=0x%hX,", i << 8)); + i = a->read_bcr(ioaddr, 26); + dprintf((" SRAM_BND=0x%hX,", i << 8)); + i = a->read_bcr(ioaddr, 27); + if (i & (1 << 14)) + dprintf(("LowLatRx")); + } +#endif + lp->name = chipname; + lp->shared_irq = shared; + lp->full_duplex = fdx; + lp->dxsuflo = dxsuflo; + lp->ltint = ltint; + lp->mii = mii; + /* FIXME: Fix Options for only one card */ + if ((cards_found >= MAX_UNITS) + || ((unsigned int) options[cards_found] > sizeof(options_mapping))) + lp->options = PCNET32_PORT_ASEL; + else + lp->options = options_mapping[options[cards_found]]; + + if (fdx && !(lp->options & PCNET32_PORT_ASEL) && + ((cards_found >= MAX_UNITS) || full_duplex[cards_found])) + lp->options |= PCNET32_PORT_FD; + + if (!a) { + printf("No access methods\n"); + return 0; + } + + // lp->a = *a; + // Causes a loader: + // bin/blib.a(pcnet32.o)(.text+0x6b6): In function `pcnet32_probe': + // drivers/net/pcnet32.c:871: undefined reference to `memcpy' + // make: *** [bin/pcnet32.dsk.tmp] Error 1 + // So we do: + memcpy ( &lp->a, a, sizeof ( lp->a ) ); + // To explicity call memcpy. + + /* detect special T1/E1 WAN card by checking for MAC address */ + if (nic->node_addr[0] == 0x00 && nic->node_addr[1] == 0xe0 + && nic->node_addr[2] == 0x75) + lp->options = PCNET32_PORT_FD | PCNET32_PORT_GPSI; + + lp->init_block.mode = le16_to_cpu(0x0003); /* Disable Rx and Tx. */ + lp->init_block.tlen_rlen = + le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS); + for (i = 0; i < 6; i++) + lp->init_block.phys_addr[i] = nic->node_addr[i]; + lp->init_block.filter[0] = 0xffffffff; + lp->init_block.filter[1] = 0xffffffff; + lp->init_block.rx_ring = virt_to_bus(&pcnet32_bufs.rx_ring); + lp->init_block.tx_ring = virt_to_bus(&pcnet32_bufs.tx_ring); + + /* switch pcnet32 to 32bit mode */ + a->write_bcr(ioaddr, 20, 2); + + a->write_csr(ioaddr, 1, (virt_to_bus(&lp->init_block)) & 0xffff); + a->write_csr(ioaddr, 2, (virt_to_bus(&lp->init_block)) >> 16); + + /* + * To auto-IRQ we enable the initialization-done and DMA error + * interrupts. For ISA boards we get a DMA error, but VLB and PCI + * boards will work. + */ + /* Trigger an initialization just for the interrupt. */ + + +// a->write_csr(ioaddr, 0, 0x41); +// mdelay(1); + + cards_found++; + + /* point to NIC specific routines */ + pcnet32_reset(nic); + if (mii) { + int tmp; + int phy, phy_idx = 0; + u16 mii_lpa; + lp->phys[0] = 1; /* Default Setting */ + for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) { + int mii_status = mdio_read(nic, phy, MII_BMSR); + if (mii_status != 0xffff && mii_status != 0x0000) { + lp->phys[phy_idx++] = phy; + lp->mii_if.advertising = + mdio_read(nic, phy, MII_ADVERTISE); + if ((mii_status & 0x0040) == 0) { + tmp = phy; + dprintf (("MII PHY found at address %d, status " + "%hX advertising %hX\n", phy, mii_status, + lp->mii_if.advertising)); + } + } + } + if (phy_idx == 0) + printf("No MII transceiver found!\n"); + lp->mii_if.phy_id = lp->phys[0]; + + lp->mii_if.advertising = + mdio_read(nic, lp->phys[0], MII_ADVERTISE); + + mii_lpa = mdio_read(nic, lp->phys[0], MII_LPA); + lp->mii_if.advertising &= mii_lpa; + if (lp->mii_if.advertising & ADVERTISE_100FULL) + printf("100Mbps Full-Duplex\n"); + else if (lp->mii_if.advertising & ADVERTISE_100HALF) + printf("100Mbps Half-Duplex\n"); + else if (lp->mii_if.advertising & ADVERTISE_10FULL) + printf("10Mbps Full-Duplex\n"); + else if (lp->mii_if.advertising & ADVERTISE_10HALF) + printf("10Mbps Half-Duplex\n"); + else + printf("\n"); + } else { + /* The older chips are fixed 10Mbps, and some support full duplex, + * although not via autonegotiation, but only via configuration. */ + if (fdx) + printf("10Mbps Full-Duplex\n"); + else + printf("10Mbps Half-Duplex\n"); + } + + nic->nic_op = &pcnet32_operations; + + return 1; +} +static int mdio_read(struct nic *nic __unused, int phy_id, int reg_num) +{ + u16 val_out; + int phyaddr; + + if (!lp->mii) + return 0; + + phyaddr = lp->a.read_bcr(ioaddr, 33); + + lp->a.write_bcr(ioaddr, 33, + ((phy_id & 0x1f) << 5) | (reg_num & 0x1f)); + val_out = lp->a.read_bcr(ioaddr, 34); + lp->a.write_bcr(ioaddr, 33, phyaddr); + + return val_out; +} + +#if 0 +static void mdio_write(struct nic *nic __unused, int phy_id, int reg_num, + int val) +{ + int phyaddr; + + if (!lp->mii) + return; + + phyaddr = lp->a.read_bcr(ioaddr, 33); + + lp->a.write_bcr(ioaddr, 33, + ((phy_id & 0x1f) << 5) | (reg_num & 0x1f)); + lp->a.write_bcr(ioaddr, 34, val); + lp->a.write_bcr(ioaddr, 33, phyaddr); +} +#endif + +static struct nic_operations pcnet32_operations = { + .connect = dummy_connect, + .poll = pcnet32_poll, + .transmit = pcnet32_transmit, + .irq = pcnet32_irq, + +}; + +static struct pci_device_id pcnet32_nics[] = { + PCI_ROM(0x1022, 0x2000, "pcnet32", "AMD PCnet/PCI"), + PCI_ROM(0x1022, 0x2625, "pcnetfastiii", "AMD PCNet FAST III"), + PCI_ROM(0x1022, 0x2001, "amdhomepna", "AMD PCnet/HomePNA"), +}; + +PCI_DRIVER ( pcnet32_driver, pcnet32_nics, PCI_NO_CLASS ); + +DRIVER ( "PCNET32/PCI", nic_driver, pci_driver, pcnet32_driver, + pcnet32_probe, pcnet32_disable ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/phantom/nx_bitops.h b/gpxe/src/drivers/net/phantom/nx_bitops.h new file mode 100644 index 0000000..33c8fba --- /dev/null +++ b/gpxe/src/drivers/net/phantom/nx_bitops.h @@ -0,0 +1,192 @@ +#ifndef _NX_BITOPS_H +#define _NX_BITOPS_H + +/* + * Copyright (C) 2008 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. + */ + +/** + * @file + * + * NetXen bit operations + * + */ + +/** Datatype used to represent a bit in the pseudo-structures */ +typedef unsigned char pseudo_bit_t; + +/** + * Wrapper structure for pseudo_bit_t structures + * + * This structure provides a wrapper around pseudo_bit_t structures. + * It has the correct size, and also encapsulates type information + * about the underlying pseudo_bit_t-based structure, which allows the + * NX_FILL etc. macros to work without requiring explicit type + * information. + */ +#define NX_PSEUDO_BIT_STRUCT( _structure ) \ + union { \ + uint8_t bytes[ sizeof ( _structure ) / 8 ]; \ + uint64_t qwords[ sizeof ( _structure ) / 64 ]; \ + _structure *dummy[0]; \ + } u; + +/** Get pseudo_bit_t structure type from wrapper structure pointer */ +#define NX_PSEUDO_STRUCT( _ptr ) \ + typeof ( *((_ptr)->u.dummy[0]) ) + +/** Bit offset of a field within a pseudo_bit_t structure */ +#define NX_BIT_OFFSET( _ptr, _field ) \ + offsetof ( NX_PSEUDO_STRUCT ( _ptr ), _field ) + +/** Bit width of a field within a pseudo_bit_t structure */ +#define NX_BIT_WIDTH( _ptr, _field ) \ + sizeof ( ( ( NX_PSEUDO_STRUCT ( _ptr ) * ) NULL )->_field ) + +/** Qword offset of a field within a pseudo_bit_t structure */ +#define NX_QWORD_OFFSET( _ptr, _field ) \ + ( NX_BIT_OFFSET ( _ptr, _field ) / 64 ) + +/** Qword bit offset of a field within a pseudo_bit_t structure + * + * Yes, using mod-64 would work, but would lose the check for the + * error of specifying a mismatched field name and qword index. + */ +#define NX_QWORD_BIT_OFFSET( _ptr, _index, _field ) \ + ( NX_BIT_OFFSET ( _ptr, _field ) - ( 64 * (_index) ) ) + +/** Bit mask for a field within a pseudo_bit_t structure */ +#define NX_BIT_MASK( _ptr, _field ) \ + ( ( ~( ( uint64_t ) 0 ) ) >> \ + ( 64 - NX_BIT_WIDTH ( _ptr, _field ) ) ) + +/* + * Assemble native-endian qword from named fields and values + * + */ + +#define NX_ASSEMBLE_1( _ptr, _index, _field, _value ) \ + ( ( ( uint64_t) (_value) ) << \ + NX_QWORD_BIT_OFFSET ( _ptr, _index, _field ) ) + +#define NX_ASSEMBLE_2( _ptr, _index, _field, _value, ... ) \ + ( NX_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + NX_ASSEMBLE_1 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_ASSEMBLE_3( _ptr, _index, _field, _value, ... ) \ + ( NX_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + NX_ASSEMBLE_2 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_ASSEMBLE_4( _ptr, _index, _field, _value, ... ) \ + ( NX_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + NX_ASSEMBLE_3 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_ASSEMBLE_5( _ptr, _index, _field, _value, ... ) \ + ( NX_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + NX_ASSEMBLE_4 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_ASSEMBLE_6( _ptr, _index, _field, _value, ... ) \ + ( NX_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + NX_ASSEMBLE_5 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_ASSEMBLE_7( _ptr, _index, _field, _value, ... ) \ + ( NX_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + NX_ASSEMBLE_6 ( _ptr, _index, __VA_ARGS__ ) ) + +/* + * Build native-endian (positive) qword bitmasks from named fields + * + */ + +#define NX_MASK_1( _ptr, _index, _field ) \ + ( NX_BIT_MASK ( _ptr, _field ) << \ + NX_QWORD_BIT_OFFSET ( _ptr, _index, _field ) ) + +#define NX_MASK_2( _ptr, _index, _field, ... ) \ + ( NX_MASK_1 ( _ptr, _index, _field ) | \ + NX_MASK_1 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_MASK_3( _ptr, _index, _field, ... ) \ + ( NX_MASK_1 ( _ptr, _index, _field ) | \ + NX_MASK_2 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_MASK_4( _ptr, _index, _field, ... ) \ + ( NX_MASK_1 ( _ptr, _index, _field ) | \ + NX_MASK_3 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_MASK_5( _ptr, _index, _field, ... ) \ + ( NX_MASK_1 ( _ptr, _index, _field ) | \ + NX_MASK_4 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_MASK_6( _ptr, _index, _field, ... ) \ + ( NX_MASK_1 ( _ptr, _index, _field ) | \ + NX_MASK_5 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_MASK_7( _ptr, _index, _field, ... ) \ + ( NX_MASK_1 ( _ptr, _index, _field ) | \ + NX_MASK_6 ( _ptr, _index, __VA_ARGS__ ) ) + +/* + * Populate big-endian qwords from named fields and values + * + */ + +#define NX_FILL( _ptr, _index, _assembled ) \ + do { \ + uint64_t *__ptr = &(_ptr)->u.qwords[(_index)]; \ + uint64_t __assembled = (_assembled); \ + *__ptr = cpu_to_le64 ( __assembled ); \ + } while ( 0 ) + +#define NX_FILL_1( _ptr, _index, ... ) \ + NX_FILL ( _ptr, _index, NX_ASSEMBLE_1 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_FILL_2( _ptr, _index, ... ) \ + NX_FILL ( _ptr, _index, NX_ASSEMBLE_2 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_FILL_3( _ptr, _index, ... ) \ + NX_FILL ( _ptr, _index, NX_ASSEMBLE_3 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_FILL_4( _ptr, _index, ... ) \ + NX_FILL ( _ptr, _index, NX_ASSEMBLE_4 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_FILL_5( _ptr, _index, ... ) \ + NX_FILL ( _ptr, _index, NX_ASSEMBLE_5 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_FILL_6( _ptr, _index, ... ) \ + NX_FILL ( _ptr, _index, NX_ASSEMBLE_6 ( _ptr, _index, __VA_ARGS__ ) ) + +#define NX_FILL_7( _ptr, _index, ... ) \ + NX_FILL ( _ptr, _index, NX_ASSEMBLE_7 ( _ptr, _index, __VA_ARGS__ ) ) + +/** Extract value of named field */ +#define NX_GET64( _ptr, _field ) \ + ( { \ + unsigned int __index = NX_QWORD_OFFSET ( _ptr, _field ); \ + uint64_t *__ptr = &(_ptr)->u.qwords[__index]; \ + uint64_t __value = le64_to_cpu ( *__ptr ); \ + __value >>= \ + NX_QWORD_BIT_OFFSET ( _ptr, __index, _field ); \ + __value &= NX_BIT_MASK ( _ptr, _field ); \ + __value; \ + } ) + +/** Extract value of named field (for fields up to the size of a long) */ +#define NX_GET( _ptr, _field ) \ + ( ( unsigned long ) NX_GET64 ( _ptr, _field ) ) + +#endif /* _NX_BITOPS_H */ diff --git a/gpxe/src/drivers/net/phantom/nxhal_nic_interface.h b/gpxe/src/drivers/net/phantom/nxhal_nic_interface.h new file mode 100644 index 0000000..aa05c72 --- /dev/null +++ b/gpxe/src/drivers/net/phantom/nxhal_nic_interface.h @@ -0,0 +1,499 @@ +/* + * Data types and structure for HAL - NIC interface. + * + */ + +#ifndef _NXHAL_NIC_INTERFACE_H_ +#define _NXHAL_NIC_INTERFACE_H_ + +/***************************************************************************** + * Simple Types + *****************************************************************************/ + +typedef U32 nx_reg_addr_t; + +/***************************************************************************** + * Root crb-based firmware commands + *****************************************************************************/ + +/* CRB Root Command + + A single set of crbs is used across all physical/virtual + functions for capability queries, initialization, and + context creation/destruction. + + There are 4 CRBS: + Command/Response CRB + Argument1 CRB + Argument2 CRB + Argument3 CRB + Signature CRB + + The cmd/rsp crb is always intiated by the host via + a command code and always responded by the card with + a response code. The cmd and rsp codes are disjoint. + The sequence of use is always CMD, RSP, CLEAR CMD. + + The arguments are for passing in command specific + and response specific parameters/data. + + The signature is composed of a magic value, the + pci function id, and a command sequence id: + [7:0] = pci function + [15:8] = version + [31:16] = magic of 0xcafe + + The pci function allows the card to take correct + action for the given particular commands. + The firmware will attempt to detect + an errant driver that has died while holding + the root crb hardware lock. Such an error condition + shows up as the cmd/rsp crb stuck in a non-clear state. + + Interface Sequence: + Host always makes requests and firmware always responds. + Note that data field is always set prior to command field. + + [READ] CMD/RSP CRB ARGUMENT FIELD + Host grab lock + Host -> CMD optional parameter + FW <- (Good) RSP-OK DATA + FW <- (Fail) RSP-FAIL optional failure code + Host -> CLEAR + Host release lock + + [WRITE] CMD/RSP CRB ARGUMENT FIELD + Host grab lock + Host -> CMD DATA + FW <- (Good) RSP-OK optional write status + FW <- (Write) RSP-FAIL optional failure code + Host -> CLEAR + Host release lock + +*/ + + +/***************************************************************************** + * CMD/RSP + *****************************************************************************/ + +#define NX_CDRP_SIGNATURE_TO_PCIFN(sign) ((sign) & 0xff) +#define NX_CDRP_SIGNATURE_TO_VERSION(sign) (((sign)>>8) & 0xff) +#define NX_CDRP_SIGNATURE_TO_MAGIC(sign) (((sign)>>16) & 0xffff) +#define NX_CDRP_SIGNATURE_VALID(sign) \ + ( NX_CDRP_SIGNATURE_TO_MAGIC(sign) == 0xcafe && \ + NX_CDRP_SIGNATURE_TO_PCIFN(sign) < 8) +#define NX_CDRP_SIGNATURE_MAKE(pcifn,version) \ + ( ((pcifn) & 0xff) | \ + (((version) & 0xff) << 8) | \ + (0xcafe << 16) ) + +#define NX_CDRP_CLEAR 0x00000000 +#define NX_CDRP_CMD_BIT 0x80000000 + +/* All responses must have the NX_CDRP_CMD_BIT cleared + * in the crb NX_CDRP_CRB_OFFSET. */ +#define NX_CDRP_FORM_RSP(rsp) (rsp) +#define NX_CDRP_IS_RSP(rsp) (((rsp) & NX_CDRP_CMD_BIT) == 0) + +#define NX_CDRP_RSP_OK 0x00000001 +#define NX_CDRP_RSP_FAIL 0x00000002 +#define NX_CDRP_RSP_TIMEOUT 0x00000003 + +/* All commands must have the NX_CDRP_CMD_BIT set in + * the crb NX_CDRP_CRB_OFFSET. + * The macros below do not have it explicitly set to + * allow their use in lookup tables */ +#define NX_CDRP_FORM_CMD(cmd) (NX_CDRP_CMD_BIT | (cmd)) +#define NX_CDRP_IS_CMD(cmd) (((cmd) & NX_CDRP_CMD_BIT) != 0) + +/* [CMD] Capability Vector [RSP] Capability Vector */ +#define NX_CDRP_CMD_SUBMIT_CAPABILITIES 0x00000001 + +/* [CMD] - [RSP] Query Value */ +#define NX_CDRP_CMD_READ_MAX_RDS_PER_CTX 0x00000002 + +/* [CMD] - [RSP] Query Value */ +#define NX_CDRP_CMD_READ_MAX_SDS_PER_CTX 0x00000003 + +/* [CMD] - [RSP] Query Value */ +#define NX_CDRP_CMD_READ_MAX_RULES_PER_CTX 0x00000004 + +/* [CMD] - [RSP] Query Value */ +#define NX_CDRP_CMD_READ_MAX_RX_CTX 0x00000005 + +/* [CMD] - [RSP] Query Value */ +#define NX_CDRP_CMD_READ_MAX_TX_CTX 0x00000006 + +/* [CMD] Rx Config DMA Addr [RSP] rcode */ +#define NX_CDRP_CMD_CREATE_RX_CTX 0x00000007 + +/* [CMD] Rx Context Handle, Reset Kind [RSP] rcode */ +#define NX_CDRP_CMD_DESTROY_RX_CTX 0x00000008 + +/* [CMD] Tx Config DMA Addr [RSP] rcode */ +#define NX_CDRP_CMD_CREATE_TX_CTX 0x00000009 + +/* [CMD] Tx Context Handle, Reset Kind [RSP] rcode */ +#define NX_CDRP_CMD_DESTROY_TX_CTX 0x0000000a + +/* [CMD] Stat setup dma addr - [RSP] Handle, rcode */ +#define NX_CDRP_CMD_SETUP_STATISTICS 0x0000000e + +/* [CMD] Handle - [RSP] rcode */ +#define NX_CDRP_CMD_GET_STATISTICS 0x0000000f + +/* [CMD] Handle - [RSP] rcode */ +#define NX_CDRP_CMD_DELETE_STATISTICS 0x00000010 + +#define NX_CDRP_CMD_MAX 0x00000011 + +/***************************************************************************** + * Capabilities + *****************************************************************************/ + +#define NX_CAP_BIT(class, bit) (1 << bit) + +/* Class 0 (i.e. ARGS 1) + */ +#define NX_CAP0_LEGACY_CONTEXT NX_CAP_BIT(0, 0) +#define NX_CAP0_MULTI_CONTEXT NX_CAP_BIT(0, 1) +#define NX_CAP0_LEGACY_MN NX_CAP_BIT(0, 2) +#define NX_CAP0_LEGACY_MS NX_CAP_BIT(0, 3) +#define NX_CAP0_CUT_THROUGH NX_CAP_BIT(0, 4) +#define NX_CAP0_LRO NX_CAP_BIT(0, 5) +#define NX_CAP0_LSO NX_CAP_BIT(0, 6) + +/* Class 1 (i.e. ARGS 2) + */ +#define NX_CAP1_NIC NX_CAP_BIT(1, 0) +#define NX_CAP1_PXE NX_CAP_BIT(1, 1) +#define NX_CAP1_CHIMNEY NX_CAP_BIT(1, 2) +#define NX_CAP1_LSA NX_CAP_BIT(1, 3) +#define NX_CAP1_RDMA NX_CAP_BIT(1, 4) +#define NX_CAP1_ISCSI NX_CAP_BIT(1, 5) +#define NX_CAP1_FCOE NX_CAP_BIT(1, 6) + +/* Class 2 (i.e. ARGS 3) + */ + +/***************************************************************************** + * Rules + *****************************************************************************/ + +typedef U32 nx_rx_rule_type_t; + +#define NX_RX_RULETYPE_DEFAULT 0 +#define NX_RX_RULETYPE_MAC 1 +#define NX_RX_RULETYPE_MAC_VLAN 2 +#define NX_RX_RULETYPE_MAC_RSS 3 +#define NX_RX_RULETYPE_MAC_VLAN_RSS 4 +#define NX_RX_RULETYPE_MAX 5 + +typedef U32 nx_rx_rule_cmd_t; + +#define NX_RX_RULECMD_ADD 0 +#define NX_RX_RULECMD_REMOVE 1 +#define NX_RX_RULECMD_MAX 2 + +typedef struct nx_rx_rule_arg_s { + union { + struct { + char mac[6]; + } m; + struct { + char mac[6]; + char vlan; + } mv; + struct { + char mac[6]; + } mr; + struct { + char mac[6]; + char vlan; + } mvr; + }; + /* will be union of all the different args for rules */ + U64 data; +} nx_rx_rule_arg_t; + +typedef struct nx_rx_rule_s { + U32 id; + U32 active; + nx_rx_rule_arg_t arg; + nx_rx_rule_type_t type; +} nx_rx_rule_t; + +/* MSG - REQUIRES TX CONTEXT */ + +/* The rules can be added/deleted from both the + * host and card sides so rq/rsp are similar. + */ +typedef struct nx_hostmsg_rx_rule_s { + nx_rx_rule_cmd_t cmd; + nx_rx_rule_t rule; +} nx_hostmsg_rx_rule_t; + +typedef struct nx_cardmsg_rx_rule_s { + nx_rcode_t rcode; + nx_rx_rule_cmd_t cmd; + nx_rx_rule_t rule; +} nx_cardmsg_rx_rule_t; + + +/***************************************************************************** + * Common to Rx/Tx contexts + *****************************************************************************/ + +/* + * Context states + */ + +typedef U32 nx_host_ctx_state_t; + +#define NX_HOST_CTX_STATE_FREED 0 /* Invalid state */ +#define NX_HOST_CTX_STATE_ALLOCATED 1 /* Not committed */ +/* The following states imply FW is aware of context */ +#define NX_HOST_CTX_STATE_ACTIVE 2 +#define NX_HOST_CTX_STATE_DISABLED 3 +#define NX_HOST_CTX_STATE_QUIESCED 4 +#define NX_HOST_CTX_STATE_MAX 5 + +/* + * Interrupt mask crb use must be set identically on the Tx + * and Rx context configs across a pci function + */ + +/* Rx and Tx have unique interrupt/crb */ +#define NX_HOST_INT_CRB_MODE_UNIQUE 0 +/* Rx and Tx share a common interrupt/crb */ +#define NX_HOST_INT_CRB_MODE_SHARED 1 /* <= LEGACY */ +/* Rx does not use a crb */ +#define NX_HOST_INT_CRB_MODE_NORX 2 +/* Tx does not use a crb */ +#define NX_HOST_INT_CRB_MODE_NOTX 3 +/* Neither Rx nor Tx use a crb */ +#define NX_HOST_INT_CRB_MODE_NORXTX 4 + +/* + * Destroy Rx/Tx + */ + +#define NX_DESTROY_CTX_RESET 0 +#define NX_DESTROY_CTX_D3_RESET 1 +#define NX_DESTROY_CTX_MAX 2 + + +/***************************************************************************** + * Tx + *****************************************************************************/ + +/* + * Components of the host-request for Tx context creation. + * CRB - DOES NOT REQUIRE Rx/TX CONTEXT + */ + +typedef struct nx_hostrq_cds_ring_s { + U64 host_phys_addr; /* Ring base addr */ + U32 ring_size; /* Ring entries */ + U32 rsvd; /* Padding */ +} nx_hostrq_cds_ring_t; + +typedef struct nx_hostrq_tx_ctx_s { + U64 host_rsp_dma_addr; /* Response dma'd here */ + U64 cmd_cons_dma_addr; /* */ + U64 dummy_dma_addr; /* */ + U32 capabilities[4]; /* Flag bit vector */ + U32 host_int_crb_mode; /* Interrupt crb usage */ + U32 rsvd1; /* Padding */ + U16 rsvd2; /* Padding */ + U16 interrupt_ctl; + U16 msi_index; + U16 rsvd3; /* Padding */ + nx_hostrq_cds_ring_t cds_ring; /* Desc of cds ring */ + U8 reserved[128]; /* future expansion */ +} nx_hostrq_tx_ctx_t; + +typedef struct nx_cardrsp_cds_ring_s { + U32 host_producer_crb; /* Crb to use */ + U32 interrupt_crb; /* Crb to use */ +} nx_cardrsp_cds_ring_t; + +typedef struct nx_cardrsp_tx_ctx_s { + U32 host_ctx_state; /* Starting state */ + U16 context_id; /* Handle for context */ + U8 phys_port; /* Physical id of port */ + U8 virt_port; /* Virtual/Logical id of port */ + nx_cardrsp_cds_ring_t cds_ring; /* Card cds settings */ + U8 reserved[128]; /* future expansion */ +} nx_cardrsp_tx_ctx_t; + +#define SIZEOF_HOSTRQ_TX(HOSTRQ_TX) \ + ( sizeof(HOSTRQ_TX)) + +#define SIZEOF_CARDRSP_TX(CARDRSP_TX) \ + ( sizeof(CARDRSP_TX)) + +/***************************************************************************** + * Rx + *****************************************************************************/ + +/* + * RDS ring mapping to producer crbs + */ + +/* Each ring has a unique crb */ +#define NX_HOST_RDS_CRB_MODE_UNIQUE 0 /* <= LEGACY */ + +/* All configured RDS Rings share common crb: + 1 Ring - same as unique + 2 Rings - 16, 16 + 3 Rings - 10, 10, 10 */ +#define NX_HOST_RDS_CRB_MODE_SHARED 1 + +/* Bit usage is specified per-ring using the + ring's size. Sum of bit lengths must be <= 32. + Packing is [Ring N] ... [Ring 1][Ring 0] */ +#define NX_HOST_RDS_CRB_MODE_CUSTOM 2 +#define NX_HOST_RDS_CRB_MODE_MAX 3 + + +/* + * RDS Ting Types + */ + +#define NX_RDS_RING_TYPE_NORMAL 0 +#define NX_RDS_RING_TYPE_JUMBO 1 +#define NX_RDS_RING_TYPE_LRO 2 +#define NX_RDS_RING_TYPE_MAX 3 + +/* + * Components of the host-request for Rx context creation. + * CRB - DOES NOT REQUIRE Rx/TX CONTEXT + */ + +typedef struct nx_hostrq_sds_ring_s { + U64 host_phys_addr; /* Ring base addr */ + U32 ring_size; /* Ring entries */ + U16 msi_index; + U16 rsvd; /* Padding */ +} nx_hostrq_sds_ring_t; + +typedef struct nx_hostrq_rds_ring_s { + U64 host_phys_addr; /* Ring base addr */ + U64 buff_size; /* Packet buffer size */ + U32 ring_size; /* Ring entries */ + U32 ring_kind; /* Class of ring */ +} nx_hostrq_rds_ring_t; + +typedef struct nx_hostrq_rx_ctx_s { + U64 host_rsp_dma_addr; /* Response dma'd here */ + U32 capabilities[4]; /* Flag bit vector */ + U32 host_int_crb_mode; /* Interrupt crb usage */ + U32 host_rds_crb_mode; /* RDS crb usage */ + /* These ring offsets are relative to data[0] below */ + U32 rds_ring_offset; /* Offset to RDS config */ + U32 sds_ring_offset; /* Offset to SDS config */ + U16 num_rds_rings; /* Count of RDS rings */ + U16 num_sds_rings; /* Count of SDS rings */ + U16 rsvd1; /* Padding */ + U16 rsvd2; /* Padding */ + U8 reserved[128]; /* reserve space for future expansion*/ + /* MUST BE 64-bit aligned. + The following is packed: + - N hostrq_rds_rings + - N hostrq_sds_rings */ + char data[0]; +} nx_hostrq_rx_ctx_t; + +typedef struct nx_cardrsp_rds_ring_s { + U32 host_producer_crb; /* Crb to use */ + U32 rsvd1; /* Padding */ +} nx_cardrsp_rds_ring_t; + +typedef struct nx_cardrsp_sds_ring_s { + U32 host_consumer_crb; /* Crb to use */ + U32 interrupt_crb; /* Crb to use */ +} nx_cardrsp_sds_ring_t; + +typedef struct nx_cardrsp_rx_ctx_s { + /* These ring offsets are relative to data[0] below */ + U32 rds_ring_offset; /* Offset to RDS config */ + U32 sds_ring_offset; /* Offset to SDS config */ + U32 host_ctx_state; /* Starting State */ + U32 num_fn_per_port; /* How many PCI fn share the port */ + U16 num_rds_rings; /* Count of RDS rings */ + U16 num_sds_rings; /* Count of SDS rings */ + U16 context_id; /* Handle for context */ + U8 phys_port; /* Physical id of port */ + U8 virt_port; /* Virtual/Logical id of port */ + U8 reserved[128]; /* save space for future expansion */ + /* MUST BE 64-bit aligned. + The following is packed: + - N cardrsp_rds_rings + - N cardrs_sds_rings */ + char data[0]; +} nx_cardrsp_rx_ctx_t; + +#define SIZEOF_HOSTRQ_RX(HOSTRQ_RX, rds_rings, sds_rings) \ + ( sizeof(HOSTRQ_RX) + \ + (rds_rings)*(sizeof (nx_hostrq_rds_ring_t)) + \ + (sds_rings)*(sizeof (nx_hostrq_sds_ring_t)) ) + +#define SIZEOF_CARDRSP_RX(CARDRSP_RX, rds_rings, sds_rings) \ + ( sizeof(CARDRSP_RX) + \ + (rds_rings)*(sizeof (nx_cardrsp_rds_ring_t)) + \ + (sds_rings)*(sizeof (nx_cardrsp_sds_ring_t)) ) + + +/***************************************************************************** + * Statistics + *****************************************************************************/ + +/* + * The model of statistics update to use + */ + +#define NX_STATISTICS_MODE_INVALID 0 + +/* Permanent setup; Updates are only sent on explicit request + (NX_CDRP_CMD_GET_STATISTICS) */ +#define NX_STATISTICS_MODE_PULL 1 + +/* Permanent setup; Updates are sent automatically and on + explicit request (NX_CDRP_CMD_GET_STATISTICS) */ +#define NX_STATISTICS_MODE_PUSH 2 + +/* One time stat update. */ +#define NX_STATISTICS_MODE_SINGLE_SHOT 3 + +#define NX_STATISTICS_MODE_MAX 4 + +/* + * What set of stats + */ +#define NX_STATISTICS_TYPE_INVALID 0 +#define NX_STATISTICS_TYPE_NIC_RX_CORE 1 +#define NX_STATISTICS_TYPE_NIC_TX_CORE 2 +#define NX_STATISTICS_TYPE_NIC_RX_ALL 3 +#define NX_STATISTICS_TYPE_NIC_TX_ALL 4 +#define NX_STATISTICS_TYPE_MAX 5 + + +/* + * Request to setup statistics gathering. + * CRB - DOES NOT REQUIRE Rx/TX CONTEXT + */ + +typedef struct nx_hostrq_stat_setup_s { + U64 host_stat_buffer; /* Where to dma stats */ + U32 host_stat_size; /* Size of stat buffer */ + U16 context_id; /* Which context */ + U16 stat_type; /* What class of stats */ + U16 stat_mode; /* When to update */ + U16 stat_interval; /* Frequency of update */ +} nx_hostrq_stat_setup_t; + + + +#endif /* _NXHAL_NIC_INTERFACE_H_ */ diff --git a/gpxe/src/drivers/net/phantom/phantom.c b/gpxe/src/drivers/net/phantom/phantom.c new file mode 100644 index 0000000..1d1637d --- /dev/null +++ b/gpxe/src/drivers/net/phantom/phantom.c @@ -0,0 +1,2112 @@ +/* + * Copyright (C) 2008 Michael Brown . + * Copyright (C) 2008 NetXen, 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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "phantom.h" + +/** + * @file + * + * NetXen Phantom NICs + * + */ + +/** Maximum number of ports */ +#define PHN_MAX_NUM_PORTS 4 + +/** Maximum time to wait for command PEG to initialise + * + * BUGxxxx + * + * The command PEG will currently report initialisation complete only + * when at least one PHY has detected a link (so that the global PHY + * clock can be set to 10G/1G as appropriate). This can take a very, + * very long time. + * + * A future firmware revision should decouple PHY initialisation from + * firmware initialisation, at which point the command PEG will report + * initialisation complete much earlier, and this timeout can be + * reduced. + */ +#define PHN_CMDPEG_INIT_TIMEOUT_SEC 50 + +/** Maximum time to wait for receive PEG to initialise */ +#define PHN_RCVPEG_INIT_TIMEOUT_SEC 2 + +/** Maximum time to wait for firmware to accept a command */ +#define PHN_ISSUE_CMD_TIMEOUT_MS 2000 + +/** Maximum time to wait for test memory */ +#define PHN_TEST_MEM_TIMEOUT_MS 100 + +/** Maximum time to wait for CLP command to be issued */ +#define PHN_CLP_CMD_TIMEOUT_MS 500 + +/** Link state poll frequency + * + * The link state will be checked once in every N calls to poll(). + */ +#define PHN_LINK_POLL_FREQUENCY 4096 + +/** Number of RX descriptors */ +#define PHN_NUM_RDS 32 + +/** RX maximum fill level. Must be strictly less than PHN_NUM_RDS. */ +#define PHN_RDS_MAX_FILL 16 + +/** RX buffer size */ +#define PHN_RX_BUFSIZE ( 32 /* max LL padding added by card */ + \ + ETH_FRAME_LEN ) + +/** Number of RX status descriptors */ +#define PHN_NUM_SDS 32 + +/** Number of TX descriptors */ +#define PHN_NUM_CDS 8 + +/** A Phantom descriptor ring set */ +struct phantom_descriptor_rings { + /** RX descriptors */ + struct phantom_rds rds[PHN_NUM_RDS]; + /** RX status descriptors */ + struct phantom_sds sds[PHN_NUM_SDS]; + /** TX descriptors */ + union phantom_cds cds[PHN_NUM_CDS]; + /** TX consumer index */ + volatile uint32_t cmd_cons; +}; + +/** RX context creation request and response buffers */ +struct phantom_create_rx_ctx_rqrsp { + struct { + struct nx_hostrq_rx_ctx_s rx_ctx; + struct nx_hostrq_rds_ring_s rds; + struct nx_hostrq_sds_ring_s sds; + } __unm_dma_aligned hostrq; + struct { + struct nx_cardrsp_rx_ctx_s rx_ctx; + struct nx_cardrsp_rds_ring_s rds; + struct nx_cardrsp_sds_ring_s sds; + } __unm_dma_aligned cardrsp; +}; + +/** TX context creation request and response buffers */ +struct phantom_create_tx_ctx_rqrsp { + struct { + struct nx_hostrq_tx_ctx_s tx_ctx; + } __unm_dma_aligned hostrq; + struct { + struct nx_cardrsp_tx_ctx_s tx_ctx; + } __unm_dma_aligned cardrsp; +}; + +/** A Phantom NIC */ +struct phantom_nic { + /** BAR 0 */ + void *bar0; + /** Current CRB window */ + unsigned long crb_window; + /** CRB window access method */ + unsigned long ( *crb_access ) ( struct phantom_nic *phantom, + unsigned long reg ); + + + /** Port number */ + unsigned int port; + + + /** RX context ID */ + uint16_t rx_context_id; + /** RX descriptor producer CRB offset */ + unsigned long rds_producer_crb; + /** RX status descriptor consumer CRB offset */ + unsigned long sds_consumer_crb; + + /** RX producer index */ + unsigned int rds_producer_idx; + /** RX consumer index */ + unsigned int rds_consumer_idx; + /** RX status consumer index */ + unsigned int sds_consumer_idx; + /** RX I/O buffers */ + struct io_buffer *rds_iobuf[PHN_RDS_MAX_FILL]; + + + /** TX context ID */ + uint16_t tx_context_id; + /** TX descriptor producer CRB offset */ + unsigned long cds_producer_crb; + + /** TX producer index */ + unsigned int cds_producer_idx; + /** TX consumer index */ + unsigned int cds_consumer_idx; + /** TX I/O buffers */ + struct io_buffer *cds_iobuf[PHN_NUM_CDS]; + + + /** Descriptor rings */ + struct phantom_descriptor_rings *desc; + + + /** Last known link state */ + uint32_t link_state; + /** Link state poll timer */ + unsigned long link_poll_timer; + + + /** Non-volatile settings */ + struct settings settings; +}; + +/*************************************************************************** + * + * CRB register access + * + */ + +/** + * Prepare for access to CRB register via 128MB BAR + * + * @v phantom Phantom NIC + * @v reg Register offset within abstract address space + * @ret offset Register offset within PCI BAR0 + */ +static unsigned long phantom_crb_access_128m ( struct phantom_nic *phantom, + unsigned long reg ) { + unsigned long offset = ( 0x6000000 + ( reg & 0x1ffffff ) ); + uint32_t window = ( reg & 0x2000000 ); + uint32_t verify_window; + + if ( phantom->crb_window != window ) { + + /* Write to the CRB window register */ + writel ( window, phantom->bar0 + UNM_128M_CRB_WINDOW ); + + /* Ensure that the write has reached the card */ + verify_window = readl ( phantom->bar0 + UNM_128M_CRB_WINDOW ); + assert ( verify_window == window ); + + /* Record new window */ + phantom->crb_window = window; + } + + return offset; +} + +/** + * Prepare for access to CRB register via 32MB BAR + * + * @v phantom Phantom NIC + * @v reg Register offset within abstract address space + * @ret offset Register offset within PCI BAR0 + */ +static unsigned long phantom_crb_access_32m ( struct phantom_nic *phantom, + unsigned long reg ) { + unsigned long offset = ( reg & 0x1ffffff ); + uint32_t window = ( reg & 0x2000000 ); + uint32_t verify_window; + + if ( phantom->crb_window != window ) { + + /* Write to the CRB window register */ + writel ( window, phantom->bar0 + UNM_32M_CRB_WINDOW ); + + /* Ensure that the write has reached the card */ + verify_window = readl ( phantom->bar0 + UNM_32M_CRB_WINDOW ); + assert ( verify_window == window ); + + /* Record new window */ + phantom->crb_window = window; + } + + return offset; +} + +/** + * Prepare for access to CRB register via 2MB BAR + * + * @v phantom Phantom NIC + * @v reg Register offset within abstract address space + * @ret offset Register offset within PCI BAR0 + */ +static unsigned long phantom_crb_access_2m ( struct phantom_nic *phantom, + unsigned long reg ) { + static const struct { + uint8_t block; + uint16_t window_hi; + } reg_window_hi[] = { + { UNM_CRB_BLK_PCIE, 0x773 }, + { UNM_CRB_BLK_CAM, 0x416 }, + { UNM_CRB_BLK_ROMUSB, 0x421 }, + { UNM_CRB_BLK_TEST, 0x295 }, + { UNM_CRB_BLK_PEG_0, 0x340 }, + { UNM_CRB_BLK_PEG_1, 0x341 }, + { UNM_CRB_BLK_PEG_2, 0x342 }, + { UNM_CRB_BLK_PEG_3, 0x343 }, + { UNM_CRB_BLK_PEG_4, 0x34b }, + }; + unsigned int block = UNM_CRB_BLK ( reg ); + unsigned long offset = UNM_CRB_OFFSET ( reg ); + uint32_t window; + uint32_t verify_window; + unsigned int i; + + for ( i = 0 ; i < ( sizeof ( reg_window_hi ) / + sizeof ( reg_window_hi[0] ) ) ; i++ ) { + + if ( reg_window_hi[i].block != block ) + continue; + + window = ( ( reg_window_hi[i].window_hi << 20 ) | + ( offset & 0x000f0000 ) ); + + if ( phantom->crb_window != window ) { + + /* Write to the CRB window register */ + writel ( window, phantom->bar0 + UNM_2M_CRB_WINDOW ); + + /* Ensure that the write has reached the card */ + verify_window = readl ( phantom->bar0 + + UNM_2M_CRB_WINDOW ); + assert ( verify_window == window ); + + /* Record new window */ + phantom->crb_window = window; + } + + return ( 0x1e0000 + ( offset & 0xffff ) ); + } + + assert ( 0 ); + return 0; +} + +/** + * Read from Phantom CRB register + * + * @v phantom Phantom NIC + * @v reg Register offset within abstract address space + * @ret value Register value + */ +static uint32_t phantom_readl ( struct phantom_nic *phantom, + unsigned long reg ) { + unsigned long offset; + + offset = phantom->crb_access ( phantom, reg ); + return readl ( phantom->bar0 + offset ); +} + +/** + * Write to Phantom CRB register + * + * @v phantom Phantom NIC + * @v value Register value + * @v reg Register offset within abstract address space + */ +static void phantom_writel ( struct phantom_nic *phantom, uint32_t value, + unsigned long reg ) { + unsigned long offset; + + offset = phantom->crb_access ( phantom, reg ); + writel ( value, phantom->bar0 + offset ); +} + +/** + * Write to Phantom CRB HI/LO register pair + * + * @v phantom Phantom NIC + * @v value Register value + * @v lo_offset LO register offset within CRB + * @v hi_offset HI register offset within CRB + */ +static inline void phantom_write_hilo ( struct phantom_nic *phantom, + uint64_t value, + unsigned long lo_offset, + unsigned long hi_offset ) { + uint32_t lo = ( value & 0xffffffffUL ); + uint32_t hi = ( value >> 32 ); + + phantom_writel ( phantom, lo, lo_offset ); + phantom_writel ( phantom, hi, hi_offset ); +} + +/*************************************************************************** + * + * Firmware message buffer access (for debug) + * + */ + +/** + * Read from Phantom test memory + * + * @v phantom Phantom NIC + * @v offset Offset within test memory + * @v buf 8-byte buffer to fill + * @ret rc Return status code + */ +static int phantom_read_test_mem_block ( struct phantom_nic *phantom, + unsigned long offset, + uint32_t buf[2] ) { + unsigned int retries; + uint32_t test_control; + + phantom_write_hilo ( phantom, offset, UNM_TEST_ADDR_LO, + UNM_TEST_ADDR_HI ); + phantom_writel ( phantom, UNM_TEST_CONTROL_ENABLE, UNM_TEST_CONTROL ); + phantom_writel ( phantom, + ( UNM_TEST_CONTROL_ENABLE | UNM_TEST_CONTROL_START ), + UNM_TEST_CONTROL ); + + for ( retries = 0 ; retries < PHN_TEST_MEM_TIMEOUT_MS ; retries++ ) { + test_control = phantom_readl ( phantom, UNM_TEST_CONTROL ); + if ( ( test_control & UNM_TEST_CONTROL_BUSY ) == 0 ) { + buf[0] = phantom_readl ( phantom, UNM_TEST_RDDATA_LO ); + buf[1] = phantom_readl ( phantom, UNM_TEST_RDDATA_HI ); + return 0; + } + mdelay ( 1 ); + } + + DBGC ( phantom, "Phantom %p timed out waiting for test memory\n", + phantom ); + return -ETIMEDOUT; +} + +/** + * Read single byte from Phantom test memory + * + * @v phantom Phantom NIC + * @v offset Offset within test memory + * @ret byte Byte read, or negative error + */ +static int phantom_read_test_mem ( struct phantom_nic *phantom, + unsigned long offset ) { + static union { + uint8_t bytes[8]; + uint32_t dwords[2]; + } cache; + static unsigned long cache_offset = -1UL; + unsigned long sub_offset; + int rc; + + sub_offset = ( offset & ( sizeof ( cache ) - 1 ) ); + offset = ( offset & ~( sizeof ( cache ) - 1 ) ); + + if ( cache_offset != offset ) { + if ( ( rc = phantom_read_test_mem_block ( phantom, offset, + cache.dwords )) !=0 ) + return rc; + cache_offset = offset; + } + + return cache.bytes[sub_offset]; +} + +/** + * Dump Phantom firmware dmesg log + * + * @v phantom Phantom NIC + * @v log Log number + * @v max_lines Maximum number of lines to show, or -1 to show all + * @ret rc Return status code + */ +static int phantom_dmesg ( struct phantom_nic *phantom, unsigned int log, + unsigned int max_lines ) { + uint32_t head; + uint32_t tail; + uint32_t len; + uint32_t sig; + uint32_t offset; + int byte; + + /* Optimise out for non-debug builds */ + if ( ! DBG_LOG ) + return 0; + + /* Locate log */ + head = phantom_readl ( phantom, UNM_CAM_RAM_DMESG_HEAD ( log ) ); + len = phantom_readl ( phantom, UNM_CAM_RAM_DMESG_LEN ( log ) ); + tail = phantom_readl ( phantom, UNM_CAM_RAM_DMESG_TAIL ( log ) ); + sig = phantom_readl ( phantom, UNM_CAM_RAM_DMESG_SIG ( log ) ); + DBGC ( phantom, "Phantom %p firmware dmesg buffer %d (%08x-%08x)\n", + phantom, log, head, tail ); + assert ( ( head & 0x07 ) == 0 ); + if ( sig != UNM_CAM_RAM_DMESG_SIG_MAGIC ) { + DBGC ( phantom, "Warning: bad signature %08x (want %08lx)\n", + sig, UNM_CAM_RAM_DMESG_SIG_MAGIC ); + } + + /* Locate start of last (max_lines) lines */ + for ( offset = tail ; offset > head ; offset-- ) { + if ( ( byte = phantom_read_test_mem ( phantom, + ( offset - 1 ) ) ) < 0 ) + return byte; + if ( ( byte == '\n' ) && ( max_lines-- == 0 ) ) + break; + } + + /* Print lines */ + for ( ; offset < tail ; offset++ ) { + if ( ( byte = phantom_read_test_mem ( phantom, offset ) ) < 0 ) + return byte; + DBG ( "%c", byte ); + } + DBG ( "\n" ); + return 0; +} + +/** + * Dump Phantom firmware dmesg logs + * + * @v phantom Phantom NIC + * @v max_lines Maximum number of lines to show, or -1 to show all + */ +static void __attribute__ (( unused )) +phantom_dmesg_all ( struct phantom_nic *phantom, unsigned int max_lines ) { + unsigned int i; + + for ( i = 0 ; i < UNM_CAM_RAM_NUM_DMESG_BUFFERS ; i++ ) + phantom_dmesg ( phantom, i, max_lines ); +} + +/*************************************************************************** + * + * Firmware interface + * + */ + +/** + * Wait for firmware to accept command + * + * @v phantom Phantom NIC + * @ret rc Return status code + */ +static int phantom_wait_for_cmd ( struct phantom_nic *phantom ) { + unsigned int retries; + uint32_t cdrp; + + for ( retries = 0 ; retries < PHN_ISSUE_CMD_TIMEOUT_MS ; retries++ ) { + mdelay ( 1 ); + cdrp = phantom_readl ( phantom, UNM_NIC_REG_NX_CDRP ); + if ( NX_CDRP_IS_RSP ( cdrp ) ) { + switch ( NX_CDRP_FORM_RSP ( cdrp ) ) { + case NX_CDRP_RSP_OK: + return 0; + case NX_CDRP_RSP_FAIL: + return -EIO; + case NX_CDRP_RSP_TIMEOUT: + return -ETIMEDOUT; + default: + return -EPROTO; + } + } + } + + DBGC ( phantom, "Phantom %p timed out waiting for firmware to accept " + "command\n", phantom ); + return -ETIMEDOUT; +} + +/** + * Issue command to firmware + * + * @v phantom Phantom NIC + * @v command Firmware command + * @v arg1 Argument 1 + * @v arg2 Argument 2 + * @v arg3 Argument 3 + * @ret rc Return status code + */ +static int phantom_issue_cmd ( struct phantom_nic *phantom, + uint32_t command, uint32_t arg1, uint32_t arg2, + uint32_t arg3 ) { + uint32_t signature; + int rc; + + /* Issue command */ + signature = NX_CDRP_SIGNATURE_MAKE ( phantom->port, + NXHAL_VERSION ); + DBGC2 ( phantom, "Phantom %p issuing command %08x (%08x, %08x, " + "%08x)\n", phantom, command, arg1, arg2, arg3 ); + phantom_writel ( phantom, signature, UNM_NIC_REG_NX_SIGN ); + phantom_writel ( phantom, arg1, UNM_NIC_REG_NX_ARG1 ); + phantom_writel ( phantom, arg2, UNM_NIC_REG_NX_ARG2 ); + phantom_writel ( phantom, arg3, UNM_NIC_REG_NX_ARG3 ); + phantom_writel ( phantom, NX_CDRP_FORM_CMD ( command ), + UNM_NIC_REG_NX_CDRP ); + + /* Wait for command to be accepted */ + if ( ( rc = phantom_wait_for_cmd ( phantom ) ) != 0 ) { + DBGC ( phantom, "Phantom %p could not issue command: %s\n", + phantom, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Issue buffer-format command to firmware + * + * @v phantom Phantom NIC + * @v command Firmware command + * @v buffer Buffer to pass to firmware + * @v len Length of buffer + * @ret rc Return status code + */ +static int phantom_issue_buf_cmd ( struct phantom_nic *phantom, + uint32_t command, void *buffer, + size_t len ) { + uint64_t physaddr; + + physaddr = virt_to_bus ( buffer ); + return phantom_issue_cmd ( phantom, command, ( physaddr >> 32 ), + ( physaddr & 0xffffffffUL ), len ); +} + +/** + * Create Phantom RX context + * + * @v phantom Phantom NIC + * @ret rc Return status code + */ +static int phantom_create_rx_ctx ( struct phantom_nic *phantom ) { + struct phantom_create_rx_ctx_rqrsp *buf; + int rc; + + /* Allocate context creation buffer */ + buf = malloc_dma ( sizeof ( *buf ), UNM_DMA_BUFFER_ALIGN ); + if ( ! buf ) { + rc = -ENOMEM; + goto out; + } + memset ( buf, 0, sizeof ( *buf ) ); + + /* Prepare request */ + buf->hostrq.rx_ctx.host_rsp_dma_addr = + cpu_to_le64 ( virt_to_bus ( &buf->cardrsp ) ); + buf->hostrq.rx_ctx.capabilities[0] = + cpu_to_le32 ( NX_CAP0_LEGACY_CONTEXT | NX_CAP0_LEGACY_MN ); + buf->hostrq.rx_ctx.host_int_crb_mode = + cpu_to_le32 ( NX_HOST_INT_CRB_MODE_SHARED ); + buf->hostrq.rx_ctx.host_rds_crb_mode = + cpu_to_le32 ( NX_HOST_RDS_CRB_MODE_UNIQUE ); + buf->hostrq.rx_ctx.rds_ring_offset = cpu_to_le32 ( 0 ); + buf->hostrq.rx_ctx.sds_ring_offset = + cpu_to_le32 ( sizeof ( buf->hostrq.rds ) ); + buf->hostrq.rx_ctx.num_rds_rings = cpu_to_le16 ( 1 ); + buf->hostrq.rx_ctx.num_sds_rings = cpu_to_le16 ( 1 ); + buf->hostrq.rds.host_phys_addr = + cpu_to_le64 ( virt_to_bus ( phantom->desc->rds ) ); + buf->hostrq.rds.buff_size = cpu_to_le64 ( PHN_RX_BUFSIZE ); + buf->hostrq.rds.ring_size = cpu_to_le32 ( PHN_NUM_RDS ); + buf->hostrq.rds.ring_kind = cpu_to_le32 ( NX_RDS_RING_TYPE_NORMAL ); + buf->hostrq.sds.host_phys_addr = + cpu_to_le64 ( virt_to_bus ( phantom->desc->sds ) ); + buf->hostrq.sds.ring_size = cpu_to_le32 ( PHN_NUM_SDS ); + + DBGC ( phantom, "Phantom %p creating RX context\n", phantom ); + DBGC2_HDA ( phantom, virt_to_bus ( &buf->hostrq ), + &buf->hostrq, sizeof ( buf->hostrq ) ); + + /* Issue request */ + if ( ( rc = phantom_issue_buf_cmd ( phantom, + NX_CDRP_CMD_CREATE_RX_CTX, + &buf->hostrq, + sizeof ( buf->hostrq ) ) ) != 0 ) { + DBGC ( phantom, "Phantom %p could not create RX context: " + "%s\n", phantom, strerror ( rc ) ); + DBGC ( phantom, "Request:\n" ); + DBGC_HDA ( phantom, virt_to_bus ( &buf->hostrq ), + &buf->hostrq, sizeof ( buf->hostrq ) ); + DBGC ( phantom, "Response:\n" ); + DBGC_HDA ( phantom, virt_to_bus ( &buf->cardrsp ), + &buf->cardrsp, sizeof ( buf->cardrsp ) ); + goto out; + } + + /* Retrieve context parameters */ + phantom->rx_context_id = + le16_to_cpu ( buf->cardrsp.rx_ctx.context_id ); + phantom->rds_producer_crb = + ( UNM_CAM_RAM + + le32_to_cpu ( buf->cardrsp.rds.host_producer_crb )); + phantom->sds_consumer_crb = + ( UNM_CAM_RAM + + le32_to_cpu ( buf->cardrsp.sds.host_consumer_crb )); + + DBGC ( phantom, "Phantom %p created RX context (id %04x, port phys " + "%02x virt %02x)\n", phantom, phantom->rx_context_id, + buf->cardrsp.rx_ctx.phys_port, buf->cardrsp.rx_ctx.virt_port ); + DBGC2_HDA ( phantom, virt_to_bus ( &buf->cardrsp ), + &buf->cardrsp, sizeof ( buf->cardrsp ) ); + DBGC ( phantom, "Phantom %p RDS producer CRB is %08lx\n", + phantom, phantom->rds_producer_crb ); + DBGC ( phantom, "Phantom %p SDS consumer CRB is %08lx\n", + phantom, phantom->sds_consumer_crb ); + + out: + free_dma ( buf, sizeof ( *buf ) ); + return rc; +} + +/** + * Destroy Phantom RX context + * + * @v phantom Phantom NIC + * @ret rc Return status code + */ +static void phantom_destroy_rx_ctx ( struct phantom_nic *phantom ) { + int rc; + + DBGC ( phantom, "Phantom %p destroying RX context (id %04x)\n", + phantom, phantom->rx_context_id ); + + /* Issue request */ + if ( ( rc = phantom_issue_cmd ( phantom, + NX_CDRP_CMD_DESTROY_RX_CTX, + phantom->rx_context_id, + NX_DESTROY_CTX_RESET, 0 ) ) != 0 ) { + DBGC ( phantom, "Phantom %p could not destroy RX context: " + "%s\n", phantom, strerror ( rc ) ); + /* We're probably screwed */ + return; + } + + /* Clear context parameters */ + phantom->rx_context_id = 0; + phantom->rds_producer_crb = 0; + phantom->sds_consumer_crb = 0; + + /* Reset software counters */ + phantom->rds_producer_idx = 0; + phantom->rds_consumer_idx = 0; + phantom->sds_consumer_idx = 0; +} + +/** + * Create Phantom TX context + * + * @v phantom Phantom NIC + * @ret rc Return status code + */ +static int phantom_create_tx_ctx ( struct phantom_nic *phantom ) { + struct phantom_create_tx_ctx_rqrsp *buf; + int rc; + + /* Allocate context creation buffer */ + buf = malloc_dma ( sizeof ( *buf ), UNM_DMA_BUFFER_ALIGN ); + if ( ! buf ) { + rc = -ENOMEM; + goto out; + } + memset ( buf, 0, sizeof ( *buf ) ); + + /* Prepare request */ + buf->hostrq.tx_ctx.host_rsp_dma_addr = + cpu_to_le64 ( virt_to_bus ( &buf->cardrsp ) ); + buf->hostrq.tx_ctx.cmd_cons_dma_addr = + cpu_to_le64 ( virt_to_bus ( &phantom->desc->cmd_cons ) ); + buf->hostrq.tx_ctx.capabilities[0] = + cpu_to_le32 ( NX_CAP0_LEGACY_CONTEXT | NX_CAP0_LEGACY_MN ); + buf->hostrq.tx_ctx.host_int_crb_mode = + cpu_to_le32 ( NX_HOST_INT_CRB_MODE_SHARED ); + buf->hostrq.tx_ctx.cds_ring.host_phys_addr = + cpu_to_le64 ( virt_to_bus ( phantom->desc->cds ) ); + buf->hostrq.tx_ctx.cds_ring.ring_size = cpu_to_le32 ( PHN_NUM_CDS ); + + DBGC ( phantom, "Phantom %p creating TX context\n", phantom ); + DBGC2_HDA ( phantom, virt_to_bus ( &buf->hostrq ), + &buf->hostrq, sizeof ( buf->hostrq ) ); + + /* Issue request */ + if ( ( rc = phantom_issue_buf_cmd ( phantom, + NX_CDRP_CMD_CREATE_TX_CTX, + &buf->hostrq, + sizeof ( buf->hostrq ) ) ) != 0 ) { + DBGC ( phantom, "Phantom %p could not create TX context: " + "%s\n", phantom, strerror ( rc ) ); + DBGC ( phantom, "Request:\n" ); + DBGC_HDA ( phantom, virt_to_bus ( &buf->hostrq ), + &buf->hostrq, sizeof ( buf->hostrq ) ); + DBGC ( phantom, "Response:\n" ); + DBGC_HDA ( phantom, virt_to_bus ( &buf->cardrsp ), + &buf->cardrsp, sizeof ( buf->cardrsp ) ); + goto out; + } + + /* Retrieve context parameters */ + phantom->tx_context_id = + le16_to_cpu ( buf->cardrsp.tx_ctx.context_id ); + phantom->cds_producer_crb = + ( UNM_CAM_RAM + + le32_to_cpu(buf->cardrsp.tx_ctx.cds_ring.host_producer_crb)); + + DBGC ( phantom, "Phantom %p created TX context (id %04x, port phys " + "%02x virt %02x)\n", phantom, phantom->tx_context_id, + buf->cardrsp.tx_ctx.phys_port, buf->cardrsp.tx_ctx.virt_port ); + DBGC2_HDA ( phantom, virt_to_bus ( &buf->cardrsp ), + &buf->cardrsp, sizeof ( buf->cardrsp ) ); + DBGC ( phantom, "Phantom %p CDS producer CRB is %08lx\n", + phantom, phantom->cds_producer_crb ); + + out: + free_dma ( buf, sizeof ( *buf ) ); + return rc; +} + +/** + * Destroy Phantom TX context + * + * @v phantom Phantom NIC + * @ret rc Return status code + */ +static void phantom_destroy_tx_ctx ( struct phantom_nic *phantom ) { + int rc; + + DBGC ( phantom, "Phantom %p destroying TX context (id %04x)\n", + phantom, phantom->tx_context_id ); + + /* Issue request */ + if ( ( rc = phantom_issue_cmd ( phantom, + NX_CDRP_CMD_DESTROY_TX_CTX, + phantom->tx_context_id, + NX_DESTROY_CTX_RESET, 0 ) ) != 0 ) { + DBGC ( phantom, "Phantom %p could not destroy TX context: " + "%s\n", phantom, strerror ( rc ) ); + /* We're probably screwed */ + return; + } + + /* Clear context parameters */ + phantom->tx_context_id = 0; + phantom->cds_producer_crb = 0; + + /* Reset software counters */ + phantom->cds_producer_idx = 0; + phantom->cds_consumer_idx = 0; +} + +/*************************************************************************** + * + * Descriptor ring management + * + */ + +/** + * Allocate Phantom RX descriptor + * + * @v phantom Phantom NIC + * @ret index RX descriptor index, or negative error + */ +static int phantom_alloc_rds ( struct phantom_nic *phantom ) { + unsigned int rds_producer_idx; + unsigned int next_rds_producer_idx; + + /* Check for space in the ring. RX descriptors are consumed + * out of order, but they are *read* by the hardware in strict + * order. We maintain a pessimistic consumer index, which is + * guaranteed never to be an overestimate of the number of + * descriptors read by the hardware. + */ + rds_producer_idx = phantom->rds_producer_idx; + next_rds_producer_idx = ( ( rds_producer_idx + 1 ) % PHN_NUM_RDS ); + if ( next_rds_producer_idx == phantom->rds_consumer_idx ) { + DBGC ( phantom, "Phantom %p RDS ring full (index %d not " + "consumed)\n", phantom, next_rds_producer_idx ); + return -ENOBUFS; + } + + return rds_producer_idx; +} + +/** + * Post Phantom RX descriptor + * + * @v phantom Phantom NIC + * @v rds RX descriptor + */ +static void phantom_post_rds ( struct phantom_nic *phantom, + struct phantom_rds *rds ) { + unsigned int rds_producer_idx; + unsigned int next_rds_producer_idx; + struct phantom_rds *entry; + + /* Copy descriptor to ring */ + rds_producer_idx = phantom->rds_producer_idx; + entry = &phantom->desc->rds[rds_producer_idx]; + memcpy ( entry, rds, sizeof ( *entry ) ); + DBGC2 ( phantom, "Phantom %p posting RDS %ld (slot %d):\n", + phantom, NX_GET ( rds, handle ), rds_producer_idx ); + DBGC2_HDA ( phantom, virt_to_bus ( entry ), entry, sizeof ( *entry ) ); + + /* Update producer index */ + next_rds_producer_idx = ( ( rds_producer_idx + 1 ) % PHN_NUM_RDS ); + phantom->rds_producer_idx = next_rds_producer_idx; + wmb(); + phantom_writel ( phantom, phantom->rds_producer_idx, + phantom->rds_producer_crb ); +} + +/** + * Allocate Phantom TX descriptor + * + * @v phantom Phantom NIC + * @ret index TX descriptor index, or negative error + */ +static int phantom_alloc_cds ( struct phantom_nic *phantom ) { + unsigned int cds_producer_idx; + unsigned int next_cds_producer_idx; + + /* Check for space in the ring. TX descriptors are consumed + * in strict order, so we just check for a collision against + * the consumer index. + */ + cds_producer_idx = phantom->cds_producer_idx; + next_cds_producer_idx = ( ( cds_producer_idx + 1 ) % PHN_NUM_CDS ); + if ( next_cds_producer_idx == phantom->cds_consumer_idx ) { + DBGC ( phantom, "Phantom %p CDS ring full (index %d not " + "consumed)\n", phantom, next_cds_producer_idx ); + return -ENOBUFS; + } + + return cds_producer_idx; +} + +/** + * Post Phantom TX descriptor + * + * @v phantom Phantom NIC + * @v cds TX descriptor + */ +static void phantom_post_cds ( struct phantom_nic *phantom, + union phantom_cds *cds ) { + unsigned int cds_producer_idx; + unsigned int next_cds_producer_idx; + union phantom_cds *entry; + + /* Copy descriptor to ring */ + cds_producer_idx = phantom->cds_producer_idx; + entry = &phantom->desc->cds[cds_producer_idx]; + memcpy ( entry, cds, sizeof ( *entry ) ); + DBGC2 ( phantom, "Phantom %p posting CDS %d:\n", + phantom, cds_producer_idx ); + DBGC2_HDA ( phantom, virt_to_bus ( entry ), entry, sizeof ( *entry ) ); + + /* Update producer index */ + next_cds_producer_idx = ( ( cds_producer_idx + 1 ) % PHN_NUM_CDS ); + phantom->cds_producer_idx = next_cds_producer_idx; + wmb(); + phantom_writel ( phantom, phantom->cds_producer_idx, + phantom->cds_producer_crb ); +} + +/*************************************************************************** + * + * MAC address management + * + */ + +/** + * Add/remove MAC address + * + * @v phantom Phantom NIC + * @v ll_addr MAC address to add or remove + * @v opcode MAC request opcode + * @ret rc Return status code + */ +static int phantom_update_macaddr ( struct phantom_nic *phantom, + const uint8_t *ll_addr, + unsigned int opcode ) { + union phantom_cds cds; + int index; + + /* Get descriptor ring entry */ + index = phantom_alloc_cds ( phantom ); + if ( index < 0 ) + return index; + + /* Fill descriptor ring entry */ + memset ( &cds, 0, sizeof ( cds ) ); + NX_FILL_1 ( &cds, 0, + nic_request.common.opcode, UNM_NIC_REQUEST ); + NX_FILL_2 ( &cds, 1, + nic_request.header.opcode, UNM_MAC_EVENT, + nic_request.header.context_id, phantom->port ); + NX_FILL_7 ( &cds, 2, + nic_request.body.mac_request.opcode, opcode, + nic_request.body.mac_request.mac_addr_0, ll_addr[0], + nic_request.body.mac_request.mac_addr_1, ll_addr[1], + nic_request.body.mac_request.mac_addr_2, ll_addr[2], + nic_request.body.mac_request.mac_addr_3, ll_addr[3], + nic_request.body.mac_request.mac_addr_4, ll_addr[4], + nic_request.body.mac_request.mac_addr_5, ll_addr[5] ); + + /* Post descriptor */ + phantom_post_cds ( phantom, &cds ); + + return 0; +} + +/** + * Add MAC address + * + * @v phantom Phantom NIC + * @v ll_addr MAC address to add or remove + * @ret rc Return status code + */ +static inline int phantom_add_macaddr ( struct phantom_nic *phantom, + const uint8_t *ll_addr ) { + + DBGC ( phantom, "Phantom %p adding MAC address %s\n", + phantom, eth_ntoa ( ll_addr ) ); + + return phantom_update_macaddr ( phantom, ll_addr, UNM_MAC_ADD ); +} + +/** + * Remove MAC address + * + * @v phantom Phantom NIC + * @v ll_addr MAC address to add or remove + * @ret rc Return status code + */ +static inline int phantom_del_macaddr ( struct phantom_nic *phantom, + const uint8_t *ll_addr ) { + + DBGC ( phantom, "Phantom %p removing MAC address %s\n", + phantom, eth_ntoa ( ll_addr ) ); + + return phantom_update_macaddr ( phantom, ll_addr, UNM_MAC_DEL ); +} + +/*************************************************************************** + * + * Link state detection + * + */ + +/** + * Poll link state + * + * @v netdev Network device + */ +static void phantom_poll_link_state ( struct net_device *netdev ) { + struct phantom_nic *phantom = netdev_priv ( netdev ); + uint32_t xg_state_p3; + unsigned int link; + + /* Read link state */ + xg_state_p3 = phantom_readl ( phantom, UNM_NIC_REG_XG_STATE_P3 ); + + /* If there is no change, do nothing */ + if ( phantom->link_state == xg_state_p3 ) + return; + + /* Record new link state */ + DBGC ( phantom, "Phantom %p new link state %08x (was %08x)\n", + phantom, xg_state_p3, phantom->link_state ); + phantom->link_state = xg_state_p3; + + /* Indicate link state to gPXE */ + link = UNM_NIC_REG_XG_STATE_P3_LINK ( phantom->port, + phantom->link_state ); + switch ( link ) { + case UNM_NIC_REG_XG_STATE_P3_LINK_UP: + DBGC ( phantom, "Phantom %p link is up\n", phantom ); + netdev_link_up ( netdev ); + break; + case UNM_NIC_REG_XG_STATE_P3_LINK_DOWN: + DBGC ( phantom, "Phantom %p link is down\n", phantom ); + netdev_link_down ( netdev ); + break; + default: + DBGC ( phantom, "Phantom %p bad link state %d\n", + phantom, link ); + break; + } +} + +/*************************************************************************** + * + * Main driver body + * + */ + +/** + * Refill descriptor ring + * + * @v netdev Net device + */ +static void phantom_refill_rx_ring ( struct net_device *netdev ) { + struct phantom_nic *phantom = netdev_priv ( netdev ); + struct io_buffer *iobuf; + struct phantom_rds rds; + unsigned int handle; + int index; + + for ( handle = 0 ; handle < PHN_RDS_MAX_FILL ; handle++ ) { + + /* Skip this index if the descriptor has not yet been + * consumed. + */ + if ( phantom->rds_iobuf[handle] != NULL ) + continue; + + /* Allocate descriptor ring entry */ + index = phantom_alloc_rds ( phantom ); + assert ( PHN_RDS_MAX_FILL < PHN_NUM_RDS ); + assert ( index >= 0 ); /* Guaranteed by MAX_FILL < NUM_RDS ) */ + + /* Try to allocate an I/O buffer */ + iobuf = alloc_iob ( PHN_RX_BUFSIZE ); + if ( ! iobuf ) { + /* Failure is non-fatal; we will retry later */ + netdev_rx_err ( netdev, NULL, -ENOMEM ); + break; + } + + /* Fill descriptor ring entry */ + memset ( &rds, 0, sizeof ( rds ) ); + NX_FILL_2 ( &rds, 0, + handle, handle, + length, iob_len ( iobuf ) ); + NX_FILL_1 ( &rds, 1, + dma_addr, virt_to_bus ( iobuf->data ) ); + + /* Record I/O buffer */ + assert ( phantom->rds_iobuf[handle] == NULL ); + phantom->rds_iobuf[handle] = iobuf; + + /* Post descriptor */ + phantom_post_rds ( phantom, &rds ); + } +} + +/** + * Open NIC + * + * @v netdev Net device + * @ret rc Return status code + */ +static int phantom_open ( struct net_device *netdev ) { + struct phantom_nic *phantom = netdev_priv ( netdev ); + int rc; + + /* Allocate and zero descriptor rings */ + phantom->desc = malloc_dma ( sizeof ( *(phantom->desc) ), + UNM_DMA_BUFFER_ALIGN ); + if ( ! phantom->desc ) { + rc = -ENOMEM; + goto err_alloc_desc; + } + memset ( phantom->desc, 0, sizeof ( *(phantom->desc) ) ); + + /* Create RX context */ + if ( ( rc = phantom_create_rx_ctx ( phantom ) ) != 0 ) + goto err_create_rx_ctx; + + /* Create TX context */ + if ( ( rc = phantom_create_tx_ctx ( phantom ) ) != 0 ) + goto err_create_tx_ctx; + + /* Fill the RX descriptor ring */ + phantom_refill_rx_ring ( netdev ); + + /* Add MAC addresses + * + * BUG5583 + * + * We would like to be able to enable receiving all multicast + * packets (or, failing that, promiscuous mode), but the + * firmware doesn't currently support this. + */ + if ( ( rc = phantom_add_macaddr ( phantom, + netdev->ll_protocol->ll_broadcast ) ) != 0 ) + goto err_add_macaddr_broadcast; + if ( ( rc = phantom_add_macaddr ( phantom, + netdev->ll_addr ) ) != 0 ) + goto err_add_macaddr_unicast; + + return 0; + + phantom_del_macaddr ( phantom, netdev->ll_addr ); + err_add_macaddr_unicast: + phantom_del_macaddr ( phantom, + netdev->ll_protocol->ll_broadcast ); + err_add_macaddr_broadcast: + phantom_destroy_tx_ctx ( phantom ); + err_create_tx_ctx: + phantom_destroy_rx_ctx ( phantom ); + err_create_rx_ctx: + free_dma ( phantom->desc, sizeof ( *(phantom->desc) ) ); + phantom->desc = NULL; + err_alloc_desc: + return rc; +} + +/** + * Close NIC + * + * @v netdev Net device + */ +static void phantom_close ( struct net_device *netdev ) { + struct phantom_nic *phantom = netdev_priv ( netdev ); + struct io_buffer *iobuf; + unsigned int i; + + /* Shut down the port */ + phantom_del_macaddr ( phantom, netdev->ll_addr ); + phantom_del_macaddr ( phantom, + netdev->ll_protocol->ll_broadcast ); + phantom_destroy_tx_ctx ( phantom ); + phantom_destroy_rx_ctx ( phantom ); + free_dma ( phantom->desc, sizeof ( *(phantom->desc) ) ); + phantom->desc = NULL; + + /* Flush any uncompleted descriptors */ + for ( i = 0 ; i < PHN_RDS_MAX_FILL ; i++ ) { + iobuf = phantom->rds_iobuf[i]; + if ( iobuf ) { + free_iob ( iobuf ); + phantom->rds_iobuf[i] = NULL; + } + } + for ( i = 0 ; i < PHN_NUM_CDS ; i++ ) { + iobuf = phantom->cds_iobuf[i]; + if ( iobuf ) { + netdev_tx_complete_err ( netdev, iobuf, -ECANCELED ); + phantom->cds_iobuf[i] = NULL; + } + } +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int phantom_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct phantom_nic *phantom = netdev_priv ( netdev ); + union phantom_cds cds; + int index; + + /* Get descriptor ring entry */ + index = phantom_alloc_cds ( phantom ); + if ( index < 0 ) + return index; + + /* Fill descriptor ring entry */ + memset ( &cds, 0, sizeof ( cds ) ); + NX_FILL_3 ( &cds, 0, + tx.opcode, UNM_TX_ETHER_PKT, + tx.num_buffers, 1, + tx.length, iob_len ( iobuf ) ); + NX_FILL_2 ( &cds, 2, + tx.port, phantom->port, + tx.context_id, phantom->port ); + NX_FILL_1 ( &cds, 4, + tx.buffer1_dma_addr, virt_to_bus ( iobuf->data ) ); + NX_FILL_1 ( &cds, 5, + tx.buffer1_length, iob_len ( iobuf ) ); + + /* Record I/O buffer */ + assert ( phantom->cds_iobuf[index] == NULL ); + phantom->cds_iobuf[index] = iobuf; + + /* Post descriptor */ + phantom_post_cds ( phantom, &cds ); + + return 0; +} + +/** + * Poll for received packets + * + * @v netdev Network device + */ +static void phantom_poll ( struct net_device *netdev ) { + struct phantom_nic *phantom = netdev_priv ( netdev ); + struct io_buffer *iobuf; + unsigned int cds_consumer_idx; + unsigned int raw_new_cds_consumer_idx; + unsigned int new_cds_consumer_idx; + unsigned int rds_consumer_idx; + unsigned int sds_consumer_idx; + struct phantom_sds *sds; + unsigned int sds_handle; + unsigned int sds_opcode; + + /* Check for TX completions */ + cds_consumer_idx = phantom->cds_consumer_idx; + raw_new_cds_consumer_idx = phantom->desc->cmd_cons; + new_cds_consumer_idx = le32_to_cpu ( raw_new_cds_consumer_idx ); + while ( cds_consumer_idx != new_cds_consumer_idx ) { + DBGC2 ( phantom, "Phantom %p CDS %d complete\n", + phantom, cds_consumer_idx ); + /* Completions may be for commands other than TX, so + * there may not always be an associated I/O buffer. + */ + if ( ( iobuf = phantom->cds_iobuf[cds_consumer_idx] ) ) { + netdev_tx_complete ( netdev, iobuf ); + phantom->cds_iobuf[cds_consumer_idx] = NULL; + } + cds_consumer_idx = ( ( cds_consumer_idx + 1 ) % PHN_NUM_CDS ); + phantom->cds_consumer_idx = cds_consumer_idx; + } + + /* Check for received packets */ + rds_consumer_idx = phantom->rds_consumer_idx; + sds_consumer_idx = phantom->sds_consumer_idx; + while ( 1 ) { + sds = &phantom->desc->sds[sds_consumer_idx]; + if ( NX_GET ( sds, owner ) == 0 ) + break; + + DBGC2 ( phantom, "Phantom %p SDS %d status:\n", + phantom, sds_consumer_idx ); + DBGC2_HDA ( phantom, virt_to_bus ( sds ), sds, sizeof (*sds) ); + + /* Check received opcode */ + sds_opcode = NX_GET ( sds, opcode ); + if ( ( sds_opcode == UNM_RXPKT_DESC ) || + ( sds_opcode == UNM_SYN_OFFLOAD ) ) { + + /* Sanity check: ensure that all of the SDS + * descriptor has been written. + */ + if ( NX_GET ( sds, total_length ) == 0 ) { + DBGC ( phantom, "Phantom %p SDS %d " + "incomplete; deferring\n", + phantom, sds_consumer_idx ); + /* Leave for next poll() */ + break; + } + + /* Process received packet */ + sds_handle = NX_GET ( sds, handle ); + iobuf = phantom->rds_iobuf[sds_handle]; + assert ( iobuf != NULL ); + iob_put ( iobuf, NX_GET ( sds, total_length ) ); + iob_pull ( iobuf, NX_GET ( sds, pkt_offset ) ); + DBGC2 ( phantom, "Phantom %p RDS %d complete\n", + phantom, sds_handle ); + netdev_rx ( netdev, iobuf ); + phantom->rds_iobuf[sds_handle] = NULL; + + /* Update RDS consumer counter. This is a + * lower bound for the number of descriptors + * that have been read by the hardware, since + * the hardware must have read at least one + * descriptor for each completion that we + * receive. + */ + rds_consumer_idx = + ( ( rds_consumer_idx + 1 ) % PHN_NUM_RDS ); + phantom->rds_consumer_idx = rds_consumer_idx; + + } else { + + DBGC ( phantom, "Phantom %p unexpected SDS opcode " + "%02x\n", phantom, sds_opcode ); + DBGC_HDA ( phantom, virt_to_bus ( sds ), + sds, sizeof ( *sds ) ); + } + + /* Clear status descriptor */ + memset ( sds, 0, sizeof ( *sds ) ); + + /* Update SDS consumer index */ + sds_consumer_idx = ( ( sds_consumer_idx + 1 ) % PHN_NUM_SDS ); + phantom->sds_consumer_idx = sds_consumer_idx; + wmb(); + phantom_writel ( phantom, phantom->sds_consumer_idx, + phantom->sds_consumer_crb ); + } + + /* Refill the RX descriptor ring */ + phantom_refill_rx_ring ( netdev ); + + /* Occasionally poll the link state */ + if ( phantom->link_poll_timer-- == 0 ) { + phantom_poll_link_state ( netdev ); + /* Reset the link poll timer */ + phantom->link_poll_timer = PHN_LINK_POLL_FREQUENCY; + } +} + +/** + * Enable/disable interrupts + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ +static void phantom_irq ( struct net_device *netdev, int enable ) { + struct phantom_nic *phantom = netdev_priv ( netdev ); + static const unsigned long sw_int_mask_reg[PHN_MAX_NUM_PORTS] = { + UNM_NIC_REG_SW_INT_MASK_0, + UNM_NIC_REG_SW_INT_MASK_1, + UNM_NIC_REG_SW_INT_MASK_2, + UNM_NIC_REG_SW_INT_MASK_3 + }; + + phantom_writel ( phantom, + ( enable ? 1 : 0 ), + sw_int_mask_reg[phantom->port] ); +} + +/** Phantom net device operations */ +static struct net_device_operations phantom_operations = { + .open = phantom_open, + .close = phantom_close, + .transmit = phantom_transmit, + .poll = phantom_poll, + .irq = phantom_irq, +}; + +/*************************************************************************** + * + * CLP settings + * + */ + +/** Phantom CLP settings tag magic */ +#define PHN_CLP_TAG_MAGIC 0xc19c1900UL + +/** Phantom CLP settings tag magic mask */ +#define PHN_CLP_TAG_MAGIC_MASK 0xffffff00UL + +/** Phantom CLP data + * + */ +union phantom_clp_data { + /** Data bytes + * + * This field is right-aligned; if only N bytes are present + * then bytes[0]..bytes[7-N] should be zero, and the data + * should be in bytes[7-N+1] to bytes[7]; + */ + uint8_t bytes[8]; + /** Dwords for the CLP interface */ + struct { + /** High dword, in network byte order */ + uint32_t hi; + /** Low dword, in network byte order */ + uint32_t lo; + } dwords; +}; +#define PHN_CLP_BLKSIZE ( sizeof ( union phantom_clp_data ) ) + +/** + * Wait for Phantom CLP command to complete + * + * @v phantom Phantom NIC + * @ret rc Return status code + */ +static int phantom_clp_wait ( struct phantom_nic *phantom ) { + unsigned int retries; + uint32_t status; + + for ( retries = 0 ; retries < PHN_CLP_CMD_TIMEOUT_MS ; retries++ ) { + status = phantom_readl ( phantom, UNM_CAM_RAM_CLP_STATUS ); + if ( status & UNM_CAM_RAM_CLP_STATUS_DONE ) + return 0; + mdelay ( 1 ); + } + + DBGC ( phantom, "Phantom %p timed out waiting for CLP command\n", + phantom ); + return -ETIMEDOUT; +} + +/** + * Issue Phantom CLP command + * + * @v phantom Phantom NIC + * @v port Virtual port number + * @v opcode Opcode + * @v data_in Data in, or NULL + * @v data_out Data out, or NULL + * @v offset Offset within data + * @v len Data buffer length + * @ret len Total transfer length (for reads), or negative error + */ +static int phantom_clp_cmd ( struct phantom_nic *phantom, unsigned int port, + unsigned int opcode, const void *data_in, + void *data_out, size_t offset, size_t len ) { + union phantom_clp_data data; + unsigned int index = ( offset / sizeof ( data ) ); + unsigned int last = 0; + size_t in_frag_len; + uint8_t *in_frag; + uint32_t command; + uint32_t status; + size_t read_len; + unsigned int error; + size_t out_frag_len; + uint8_t *out_frag; + int rc; + + /* Sanity checks */ + assert ( ( offset % sizeof ( data ) ) == 0 ); + if ( len > 255 ) { + DBGC ( phantom, "Phantom %p invalid CLP length %zd\n", + phantom, len ); + return -EINVAL; + } + + /* Check that CLP interface is ready */ + if ( ( rc = phantom_clp_wait ( phantom ) ) != 0 ) + return rc; + + /* Copy data in */ + memset ( &data, 0, sizeof ( data ) ); + if ( data_in ) { + assert ( offset < len ); + in_frag_len = ( len - offset ); + if ( in_frag_len > sizeof ( data ) ) { + in_frag_len = sizeof ( data ); + } else { + last = 1; + } + in_frag = &data.bytes[ sizeof ( data ) - in_frag_len ]; + memcpy ( in_frag, ( data_in + offset ), in_frag_len ); + phantom_writel ( phantom, be32_to_cpu ( data.dwords.lo ), + UNM_CAM_RAM_CLP_DATA_LO ); + phantom_writel ( phantom, be32_to_cpu ( data.dwords.hi ), + UNM_CAM_RAM_CLP_DATA_HI ); + } + + /* Issue CLP command */ + command = ( ( index << 24 ) | ( ( data_in ? len : 0 ) << 16 ) | + ( port << 8 ) | ( last << 7 ) | ( opcode << 0 ) ); + phantom_writel ( phantom, command, UNM_CAM_RAM_CLP_COMMAND ); + mb(); + phantom_writel ( phantom, UNM_CAM_RAM_CLP_STATUS_START, + UNM_CAM_RAM_CLP_STATUS ); + + /* Wait for command to complete */ + if ( ( rc = phantom_clp_wait ( phantom ) ) != 0 ) + return rc; + + /* Get command status */ + status = phantom_readl ( phantom, UNM_CAM_RAM_CLP_STATUS ); + read_len = ( ( status >> 16 ) & 0xff ); + error = ( ( status >> 8 ) & 0xff ); + if ( error ) { + DBGC ( phantom, "Phantom %p CLP command error %02x\n", + phantom, error ); + return -EIO; + } + + /* Copy data out */ + if ( data_out ) { + data.dwords.lo = cpu_to_be32 ( phantom_readl ( phantom, + UNM_CAM_RAM_CLP_DATA_LO ) ); + data.dwords.hi = cpu_to_be32 ( phantom_readl ( phantom, + UNM_CAM_RAM_CLP_DATA_HI ) ); + out_frag_len = ( read_len - offset ); + if ( out_frag_len > sizeof ( data ) ) + out_frag_len = sizeof ( data ); + out_frag = &data.bytes[ sizeof ( data ) - out_frag_len ]; + if ( out_frag_len > ( len - offset ) ) + out_frag_len = ( len - offset ); + memcpy ( ( data_out + offset ), out_frag, out_frag_len ); + } + + return read_len; +} + +/** + * Store Phantom CLP setting + * + * @v phantom Phantom NIC + * @v port Virtual port number + * @v setting Setting number + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +static int phantom_clp_store ( struct phantom_nic *phantom, unsigned int port, + unsigned int setting, const void *data, + size_t len ) { + unsigned int opcode = setting; + size_t offset; + int rc; + + for ( offset = 0 ; offset < len ; offset += PHN_CLP_BLKSIZE ) { + if ( ( rc = phantom_clp_cmd ( phantom, port, opcode, data, + NULL, offset, len ) ) < 0 ) + return rc; + } + return 0; +} + +/** + * Fetch Phantom CLP setting + * + * @v phantom Phantom NIC + * @v port Virtual port number + * @v setting Setting number + * @v data Data buffer + * @v len Length of data buffer + * @ret len Length of setting, or negative error + */ +static int phantom_clp_fetch ( struct phantom_nic *phantom, unsigned int port, + unsigned int setting, void *data, size_t len ) { + unsigned int opcode = ( setting + 1 ); + size_t offset = 0; + int read_len; + + while ( 1 ) { + read_len = phantom_clp_cmd ( phantom, port, opcode, NULL, + data, offset, len ); + if ( read_len < 0 ) + return read_len; + offset += PHN_CLP_BLKSIZE; + if ( offset >= ( unsigned ) read_len ) + break; + if ( offset >= len ) + break; + } + return read_len; +} + +/** A Phantom CLP setting */ +struct phantom_clp_setting { + /** gPXE setting */ + struct setting *setting; + /** Setting number */ + unsigned int clp_setting; +}; + +/** Phantom CLP settings */ +static struct phantom_clp_setting clp_settings[] = { + { &mac_setting, 0x01 }, +}; + +/** + * Find Phantom CLP setting + * + * @v setting gPXE setting + * @v clp_setting Setting number, or 0 if not found + */ +static unsigned int +phantom_clp_setting ( struct phantom_nic *phantom, struct setting *setting ) { + struct phantom_clp_setting *clp_setting; + unsigned int i; + + /* Search the list of explicitly-defined settings */ + for ( i = 0 ; i < ( sizeof ( clp_settings ) / + sizeof ( clp_settings[0] ) ) ; i++ ) { + clp_setting = &clp_settings[i]; + if ( setting_cmp ( setting, clp_setting->setting ) == 0 ) + return clp_setting->clp_setting; + } + + /* Allow for use of numbered settings */ + if ( ( setting->tag & PHN_CLP_TAG_MAGIC_MASK ) == PHN_CLP_TAG_MAGIC ) + return ( setting->tag & ~PHN_CLP_TAG_MAGIC_MASK ); + + DBGC2 ( phantom, "Phantom %p has no \"%s\" setting\n", + phantom, setting->name ); + + return 0; +} + +/** + * Store Phantom CLP setting + * + * @v settings Settings block + * @v setting Setting to store + * @v data Setting data, or NULL to clear setting + * @v len Length of setting data + * @ret rc Return status code + */ +static int phantom_store_setting ( struct settings *settings, + struct setting *setting, + const void *data, size_t len ) { + struct phantom_nic *phantom = + container_of ( settings, struct phantom_nic, settings ); + unsigned int clp_setting; + int rc; + + /* Find Phantom setting equivalent to gPXE setting */ + clp_setting = phantom_clp_setting ( phantom, setting ); + if ( ! clp_setting ) + return -ENOTSUP; + + /* Store setting */ + if ( ( rc = phantom_clp_store ( phantom, phantom->port, + clp_setting, data, len ) ) != 0 ) { + DBGC ( phantom, "Phantom %p could not store setting \"%s\": " + "%s\n", phantom, setting->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Fetch Phantom CLP setting + * + * @v settings Settings block + * @v setting Setting to fetch + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int phantom_fetch_setting ( struct settings *settings, + struct setting *setting, + void *data, size_t len ) { + struct phantom_nic *phantom = + container_of ( settings, struct phantom_nic, settings ); + unsigned int clp_setting; + int read_len; + int rc; + + /* Find Phantom setting equivalent to gPXE setting */ + clp_setting = phantom_clp_setting ( phantom, setting ); + if ( ! clp_setting ) + return -ENOTSUP; + + /* Fetch setting */ + if ( ( read_len = phantom_clp_fetch ( phantom, phantom->port, + clp_setting, data, len ) ) < 0 ){ + rc = read_len; + DBGC ( phantom, "Phantom %p could not fetch setting \"%s\": " + "%s\n", phantom, setting->name, strerror ( rc ) ); + return rc; + } + + return read_len; +} + +/** Phantom CLP settings operations */ +static struct settings_operations phantom_settings_operations = { + .store = phantom_store_setting, + .fetch = phantom_fetch_setting, +}; + +/*************************************************************************** + * + * Initialisation + * + */ + +/** + * Map Phantom CRB window + * + * @v phantom Phantom NIC + * @ret rc Return status code + */ +static int phantom_map_crb ( struct phantom_nic *phantom, + struct pci_device *pci ) { + unsigned long bar0_start; + unsigned long bar0_size; + + bar0_start = pci_bar_start ( pci, PCI_BASE_ADDRESS_0 ); + bar0_size = pci_bar_size ( pci, PCI_BASE_ADDRESS_0 ); + DBGC ( phantom, "Phantom %p is PCI %02x:%02x.%x with BAR0 at " + "%08lx+%lx\n", phantom, pci->bus, PCI_SLOT ( pci->devfn ), + PCI_FUNC ( pci->devfn ), bar0_start, bar0_size ); + + if ( ! bar0_start ) { + DBGC ( phantom, "Phantom %p BAR not assigned; ignoring\n", + phantom ); + return -EINVAL; + } + + switch ( bar0_size ) { + case ( 128 * 1024 * 1024 ) : + DBGC ( phantom, "Phantom %p has 128MB BAR\n", phantom ); + phantom->crb_access = phantom_crb_access_128m; + break; + case ( 32 * 1024 * 1024 ) : + DBGC ( phantom, "Phantom %p has 32MB BAR\n", phantom ); + phantom->crb_access = phantom_crb_access_32m; + break; + case ( 2 * 1024 * 1024 ) : + DBGC ( phantom, "Phantom %p has 2MB BAR\n", phantom ); + phantom->crb_access = phantom_crb_access_2m; + break; + default: + DBGC ( phantom, "Phantom %p has bad BAR size\n", phantom ); + return -EINVAL; + } + + phantom->bar0 = ioremap ( bar0_start, bar0_size ); + if ( ! phantom->bar0 ) { + DBGC ( phantom, "Phantom %p could not map BAR0\n", phantom ); + return -EIO; + } + + /* Mark current CRB window as invalid, so that the first + * read/write will set the current window. + */ + phantom->crb_window = -1UL; + + return 0; +} + +/** + * Unhalt all PEGs + * + * @v phantom Phantom NIC + */ +static void phantom_unhalt_pegs ( struct phantom_nic *phantom ) { + uint32_t halt_status; + + halt_status = phantom_readl ( phantom, UNM_PEG_0_HALT_STATUS ); + phantom_writel ( phantom, halt_status, UNM_PEG_0_HALT_STATUS ); + halt_status = phantom_readl ( phantom, UNM_PEG_1_HALT_STATUS ); + phantom_writel ( phantom, halt_status, UNM_PEG_1_HALT_STATUS ); + halt_status = phantom_readl ( phantom, UNM_PEG_2_HALT_STATUS ); + phantom_writel ( phantom, halt_status, UNM_PEG_2_HALT_STATUS ); + halt_status = phantom_readl ( phantom, UNM_PEG_3_HALT_STATUS ); + phantom_writel ( phantom, halt_status, UNM_PEG_3_HALT_STATUS ); + halt_status = phantom_readl ( phantom, UNM_PEG_4_HALT_STATUS ); + phantom_writel ( phantom, halt_status, UNM_PEG_4_HALT_STATUS ); +} + +/** + * Initialise the Phantom command PEG + * + * @v phantom Phantom NIC + * @ret rc Return status code + */ +static int phantom_init_cmdpeg ( struct phantom_nic *phantom ) { + uint32_t cold_boot; + uint32_t sw_reset; + unsigned int retries; + uint32_t cmdpeg_state; + uint32_t last_cmdpeg_state = 0; + + /* Check for a previous initialisation. This could have + * happened if, for example, the BIOS used the UNDI API to + * drive the NIC prior to a full PXE boot. + */ + cmdpeg_state = phantom_readl ( phantom, UNM_NIC_REG_CMDPEG_STATE ); + if ( cmdpeg_state == UNM_NIC_REG_CMDPEG_STATE_INITIALIZE_ACK ) { + DBGC ( phantom, "Phantom %p command PEG already initialized\n", + phantom ); + /* Unhalt the PEGs. Previous firmware (e.g. BOFM) may + * have halted the PEGs to prevent internal bus + * collisions when the BIOS re-reads the expansion ROM. + */ + phantom_unhalt_pegs ( phantom ); + return 0; + } + + /* If this was a cold boot, check that the hardware came up ok */ + cold_boot = phantom_readl ( phantom, UNM_CAM_RAM_COLD_BOOT ); + if ( cold_boot == UNM_CAM_RAM_COLD_BOOT_MAGIC ) { + DBGC ( phantom, "Phantom %p coming up from cold boot\n", + phantom ); + sw_reset = phantom_readl ( phantom, UNM_ROMUSB_GLB_SW_RESET ); + if ( sw_reset != UNM_ROMUSB_GLB_SW_RESET_MAGIC ) { + DBGC ( phantom, "Phantom %p reset failed: %08x\n", + phantom, sw_reset ); + return -EIO; + } + } else { + DBGC ( phantom, "Phantom %p coming up from warm boot " + "(%08x)\n", phantom, cold_boot ); + } + /* Clear cold-boot flag */ + phantom_writel ( phantom, 0, UNM_CAM_RAM_COLD_BOOT ); + + /* Set port modes */ + phantom_writel ( phantom, UNM_CAM_RAM_PORT_MODE_AUTO_NEG_1G, + UNM_CAM_RAM_WOL_PORT_MODE ); + + /* Pass dummy DMA area to card */ + phantom_write_hilo ( phantom, 0, + UNM_NIC_REG_DUMMY_BUF_ADDR_LO, + UNM_NIC_REG_DUMMY_BUF_ADDR_HI ); + phantom_writel ( phantom, UNM_NIC_REG_DUMMY_BUF_INIT, + UNM_NIC_REG_DUMMY_BUF ); + + /* Tell the hardware that tuning is complete */ + phantom_writel ( phantom, UNM_ROMUSB_GLB_PEGTUNE_DONE_MAGIC, + UNM_ROMUSB_GLB_PEGTUNE_DONE ); + + /* Wait for command PEG to finish initialising */ + DBGC ( phantom, "Phantom %p initialising command PEG (will take up to " + "%d seconds)...\n", phantom, PHN_CMDPEG_INIT_TIMEOUT_SEC ); + for ( retries = 0; retries < PHN_CMDPEG_INIT_TIMEOUT_SEC; retries++ ) { + cmdpeg_state = phantom_readl ( phantom, + UNM_NIC_REG_CMDPEG_STATE ); + if ( cmdpeg_state != last_cmdpeg_state ) { + DBGC ( phantom, "Phantom %p command PEG state is " + "%08x after %d seconds...\n", + phantom, cmdpeg_state, retries ); + last_cmdpeg_state = cmdpeg_state; + } + if ( cmdpeg_state == UNM_NIC_REG_CMDPEG_STATE_INITIALIZED ) { + /* Acknowledge the PEG initialisation */ + phantom_writel ( phantom, + UNM_NIC_REG_CMDPEG_STATE_INITIALIZE_ACK, + UNM_NIC_REG_CMDPEG_STATE ); + return 0; + } + mdelay ( 1000 ); + } + + DBGC ( phantom, "Phantom %p timed out waiting for command PEG to " + "initialise (status %08x)\n", phantom, cmdpeg_state ); + return -ETIMEDOUT; +} + +/** + * Read Phantom MAC address + * + * @v phanton_port Phantom NIC + * @v ll_addr Buffer to fill with MAC address + */ +static void phantom_get_macaddr ( struct phantom_nic *phantom, + uint8_t *ll_addr ) { + union { + uint8_t mac_addr[2][ETH_ALEN]; + uint32_t dwords[3]; + } u; + unsigned long offset; + int i; + + /* Read the three dwords that include this MAC address and one other */ + offset = ( UNM_CAM_RAM_MAC_ADDRS + + ( 12 * ( phantom->port / 2 ) ) ); + for ( i = 0 ; i < 3 ; i++, offset += 4 ) { + u.dwords[i] = phantom_readl ( phantom, offset ); + } + + /* Copy out the relevant MAC address */ + for ( i = 0 ; i < ETH_ALEN ; i++ ) { + ll_addr[ ETH_ALEN - i - 1 ] = + u.mac_addr[ phantom->port & 1 ][i]; + } + DBGC ( phantom, "Phantom %p MAC address is %s\n", + phantom, eth_ntoa ( ll_addr ) ); +} + +/** + * Check Phantom is enabled for boot + * + * @v phanton_port Phantom NIC + * @ret rc Return status code + * + * This is something of an ugly hack to accommodate an OEM + * requirement. The NIC has only one expansion ROM BAR, rather than + * one per port. To allow individual ports to be selectively + * enabled/disabled for PXE boot (as required), we must therefore + * leave the expansion ROM always enabled, and place the per-port + * enable/disable logic within the gPXE driver. + */ +static int phantom_check_boot_enable ( struct phantom_nic *phantom ) { + unsigned long boot_enable; + + boot_enable = phantom_readl ( phantom, UNM_CAM_RAM_BOOT_ENABLE ); + if ( ! ( boot_enable & ( 1 << phantom->port ) ) ) { + DBGC ( phantom, "Phantom %p PXE boot is disabled\n", + phantom ); + return -ENOTSUP; + } + + return 0; +} + +/** + * Initialise Phantom receive PEG + * + * @v phantom Phantom NIC + * @ret rc Return status code + */ +static int phantom_init_rcvpeg ( struct phantom_nic *phantom ) { + unsigned int retries; + uint32_t rcvpeg_state; + uint32_t last_rcvpeg_state = 0; + + DBGC ( phantom, "Phantom %p initialising receive PEG (will take up to " + "%d seconds)...\n", phantom, PHN_RCVPEG_INIT_TIMEOUT_SEC ); + for ( retries = 0; retries < PHN_RCVPEG_INIT_TIMEOUT_SEC; retries++ ) { + rcvpeg_state = phantom_readl ( phantom, + UNM_NIC_REG_RCVPEG_STATE ); + if ( rcvpeg_state != last_rcvpeg_state ) { + DBGC ( phantom, "Phantom %p receive PEG state is " + "%08x after %d seconds...\n", + phantom, rcvpeg_state, retries ); + last_rcvpeg_state = rcvpeg_state; + } + if ( rcvpeg_state == UNM_NIC_REG_RCVPEG_STATE_INITIALIZED ) + return 0; + mdelay ( 1000 ); + } + + DBGC ( phantom, "Phantom %p timed out waiting for receive PEG to " + "initialise (status %08x)\n", phantom, rcvpeg_state ); + return -ETIMEDOUT; +} + +/** + * Probe PCI device + * + * @v pci PCI device + * @v id PCI ID + * @ret rc Return status code + */ +static int phantom_probe ( struct pci_device *pci, + const struct pci_device_id *id __unused ) { + struct net_device *netdev; + struct phantom_nic *phantom; + struct settings *parent_settings; + int rc; + + /* Allocate Phantom device */ + netdev = alloc_etherdev ( sizeof ( *phantom ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc_etherdev; + } + netdev_init ( netdev, &phantom_operations ); + phantom = netdev_priv ( netdev ); + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + memset ( phantom, 0, sizeof ( *phantom ) ); + phantom->port = PCI_FUNC ( pci->devfn ); + assert ( phantom->port < PHN_MAX_NUM_PORTS ); + settings_init ( &phantom->settings, + &phantom_settings_operations, + &netdev->refcnt, "clp", PHN_CLP_TAG_MAGIC ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map CRB */ + if ( ( rc = phantom_map_crb ( phantom, pci ) ) != 0 ) + goto err_map_crb; + + /* BUG5945 - need to hack PCI config space on P3 B1 silicon. + * B2 will have this fixed; remove this hack when B1 is no + * longer in use. + */ + if ( PCI_FUNC ( pci->devfn ) == 0 ) { + unsigned int i; + for ( i = 0 ; i < 8 ; i++ ) { + uint32_t temp; + pci->devfn = PCI_DEVFN ( PCI_SLOT ( pci->devfn ), i ); + pci_read_config_dword ( pci, 0xc8, &temp ); + pci_read_config_dword ( pci, 0xc8, &temp ); + pci_write_config_dword ( pci, 0xc8, 0xf1000 ); + } + pci->devfn = PCI_DEVFN ( PCI_SLOT ( pci->devfn ), 0 ); + } + + /* Initialise the command PEG */ + if ( ( rc = phantom_init_cmdpeg ( phantom ) ) != 0 ) + goto err_init_cmdpeg; + + /* Initialise the receive PEG */ + if ( ( rc = phantom_init_rcvpeg ( phantom ) ) != 0 ) + goto err_init_rcvpeg; + + /* Read MAC addresses */ + phantom_get_macaddr ( phantom, netdev->ll_addr ); + + /* Skip if boot disabled on NIC */ + if ( ( rc = phantom_check_boot_enable ( phantom ) ) != 0 ) + goto err_check_boot_enable; + + /* Register network devices */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) { + DBGC ( phantom, "Phantom %p could not register net device: " + "%s\n", phantom, strerror ( rc ) ); + goto err_register_netdev; + } + + /* Register settings blocks */ + parent_settings = netdev_settings ( netdev ); + if ( ( rc = register_settings ( &phantom->settings, + parent_settings ) ) != 0 ) { + DBGC ( phantom, "Phantom %p could not register settings: " + "%s\n", phantom, strerror ( rc ) ); + goto err_register_settings; + } + + return 0; + + unregister_settings ( &phantom->settings ); + err_register_settings: + unregister_netdev ( netdev ); + err_register_netdev: + err_check_boot_enable: + err_init_rcvpeg: + err_init_cmdpeg: + err_map_crb: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc_etherdev: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void phantom_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct phantom_nic *phantom = netdev_priv ( netdev ); + + unregister_settings ( &phantom->settings ); + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** Phantom PCI IDs */ +static struct pci_device_id phantom_nics[] = { + PCI_ROM ( 0x4040, 0x0100, "nx", "NX" ), +}; + +/** Phantom PCI driver */ +struct pci_driver phantom_driver __pci_driver = { + .ids = phantom_nics, + .id_count = ( sizeof ( phantom_nics ) / sizeof ( phantom_nics[0] ) ), + .probe = phantom_probe, + .remove = phantom_remove, +}; diff --git a/gpxe/src/drivers/net/phantom/phantom.h b/gpxe/src/drivers/net/phantom/phantom.h new file mode 100644 index 0000000..974eeca --- /dev/null +++ b/gpxe/src/drivers/net/phantom/phantom.h @@ -0,0 +1,192 @@ +#ifndef _PHANTOM_H +#define _PHANTOM_H + +/* + * Copyright (C) 2008 Michael Brown . + * Copyright (C) 2008 NetXen, 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 any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * @file + * + * NetXen Phantom NICs + * + */ + +#include + +/* Drag in hardware definitions */ +#include "nx_bitops.h" +#include "phantom_hw.h" +struct phantom_rds { NX_PSEUDO_BIT_STRUCT ( struct phantom_rds_pb ) }; +struct phantom_sds { NX_PSEUDO_BIT_STRUCT ( struct phantom_sds_pb ) }; +union phantom_cds { NX_PSEUDO_BIT_STRUCT ( union phantom_cds_pb ) }; + +/* Drag in firmware interface definitions */ +typedef uint8_t U8; +typedef uint16_t U16; +typedef uint32_t U32; +typedef uint64_t U64; +typedef uint32_t nx_rcode_t; +#define NXHAL_VERSION 1 +#include "nxhal_nic_interface.h" + +/** DMA buffer alignment */ +#define UNM_DMA_BUFFER_ALIGN 16 + +/** Mark structure as DMA-aligned */ +#define __unm_dma_aligned __attribute__ (( aligned ( UNM_DMA_BUFFER_ALIGN ) )) + +/****************************************************************************** + * + * Register definitions + * + */ + +#define UNM_128M_CRB_WINDOW 0x6110210UL +#define UNM_32M_CRB_WINDOW 0x0110210UL +#define UNM_2M_CRB_WINDOW 0x0130060UL + +/** + * Phantom register blocks + * + * The upper address bits vary between cards. We define an abstract + * address space in which the upper 8 bits of the 32-bit register + * address encode the register block. This gets translated to a bus + * address by the phantom_crb_access_xxx() methods. + */ +enum unm_reg_blocks { + UNM_CRB_BLK_PCIE = 0x01, + UNM_CRB_BLK_CAM = 0x22, + UNM_CRB_BLK_ROMUSB = 0x33, + UNM_CRB_BLK_TEST = 0x02, + UNM_CRB_BLK_PEG_0 = 0x11, + UNM_CRB_BLK_PEG_1 = 0x12, + UNM_CRB_BLK_PEG_2 = 0x13, + UNM_CRB_BLK_PEG_3 = 0x14, + UNM_CRB_BLK_PEG_4 = 0x0f, +}; +#define UNM_CRB_BASE(blk) ( (blk) << 20 ) +#define UNM_CRB_BLK(reg) ( (reg) >> 20 ) +#define UNM_CRB_OFFSET(reg) ( (reg) & 0x000fffff ) + +#define UNM_CRB_PCIE UNM_CRB_BASE ( UNM_CRB_BLK_PCIE ) +#define UNM_PCIE_SEM2_LOCK ( UNM_CRB_PCIE + 0x1c010 ) +#define UNM_PCIE_SEM2_UNLOCK ( UNM_CRB_PCIE + 0x1c014 ) + +#define UNM_CRB_CAM UNM_CRB_BASE ( UNM_CRB_BLK_CAM ) + +#define UNM_CAM_RAM ( UNM_CRB_CAM + 0x02000 ) +#define UNM_CAM_RAM_PORT_MODE ( UNM_CAM_RAM + 0x00024 ) +#define UNM_CAM_RAM_PORT_MODE_AUTO_NEG 4 +#define UNM_CAM_RAM_PORT_MODE_AUTO_NEG_1G 5 +#define UNM_CAM_RAM_DMESG_HEAD(n) ( UNM_CAM_RAM + 0x00030 + (n) * 0x10 ) +#define UNM_CAM_RAM_DMESG_LEN(n) ( UNM_CAM_RAM + 0x00034 + (n) * 0x10 ) +#define UNM_CAM_RAM_DMESG_TAIL(n) ( UNM_CAM_RAM + 0x00038 + (n) * 0x10 ) +#define UNM_CAM_RAM_DMESG_SIG(n) ( UNM_CAM_RAM + 0x0003c + (n) * 0x10 ) +#define UNM_CAM_RAM_DMESG_SIG_MAGIC 0xcafebabeUL +#define UNM_CAM_RAM_NUM_DMESG_BUFFERS 5 +#define UNM_CAM_RAM_CLP_COMMAND ( UNM_CAM_RAM + 0x000c0 ) +#define UNM_CAM_RAM_CLP_COMMAND_LAST 0x00000080UL +#define UNM_CAM_RAM_CLP_DATA_LO ( UNM_CAM_RAM + 0x000c4 ) +#define UNM_CAM_RAM_CLP_DATA_HI ( UNM_CAM_RAM + 0x000c8 ) +#define UNM_CAM_RAM_CLP_STATUS ( UNM_CAM_RAM + 0x000cc ) +#define UNM_CAM_RAM_CLP_STATUS_START 0x00000001UL +#define UNM_CAM_RAM_CLP_STATUS_DONE 0x00000002UL +#define UNM_CAM_RAM_CLP_STATUS_ERROR 0x0000ff00UL +#define UNM_CAM_RAM_CLP_STATUS_UNINITIALISED 0xffffffffUL +#define UNM_CAM_RAM_BOOT_ENABLE ( UNM_CAM_RAM + 0x000fc ) +#define UNM_CAM_RAM_WOL_PORT_MODE ( UNM_CAM_RAM + 0x00198 ) +#define UNM_CAM_RAM_MAC_ADDRS ( UNM_CAM_RAM + 0x001c0 ) +#define UNM_CAM_RAM_COLD_BOOT ( UNM_CAM_RAM + 0x001fc ) +#define UNM_CAM_RAM_COLD_BOOT_MAGIC 0x55555555UL + +#define UNM_NIC_REG ( UNM_CRB_CAM + 0x02200 ) +#define UNM_NIC_REG_NX_CDRP ( UNM_NIC_REG + 0x00018 ) +#define UNM_NIC_REG_NX_ARG1 ( UNM_NIC_REG + 0x0001c ) +#define UNM_NIC_REG_NX_ARG2 ( UNM_NIC_REG + 0x00020 ) +#define UNM_NIC_REG_NX_ARG3 ( UNM_NIC_REG + 0x00024 ) +#define UNM_NIC_REG_NX_SIGN ( UNM_NIC_REG + 0x00028 ) +#define UNM_NIC_REG_DUMMY_BUF_ADDR_HI ( UNM_NIC_REG + 0x0003c ) +#define UNM_NIC_REG_DUMMY_BUF_ADDR_LO ( UNM_NIC_REG + 0x00040 ) +#define UNM_NIC_REG_CMDPEG_STATE ( UNM_NIC_REG + 0x00050 ) +#define UNM_NIC_REG_CMDPEG_STATE_INITIALIZED 0xff01 +#define UNM_NIC_REG_CMDPEG_STATE_INITIALIZE_ACK 0xf00f +#define UNM_NIC_REG_DUMMY_BUF ( UNM_NIC_REG + 0x000fc ) +#define UNM_NIC_REG_DUMMY_BUF_INIT 0 +#define UNM_NIC_REG_XG_STATE_P3 ( UNM_NIC_REG + 0x00098 ) +#define UNM_NIC_REG_XG_STATE_P3_LINK( port, state_p3 ) \ + ( ( (state_p3) >> ( (port) * 4 ) ) & 0x0f ) +#define UNM_NIC_REG_XG_STATE_P3_LINK_UP 0x01 +#define UNM_NIC_REG_XG_STATE_P3_LINK_DOWN 0x02 +#define UNM_NIC_REG_RCVPEG_STATE ( UNM_NIC_REG + 0x0013c ) +#define UNM_NIC_REG_RCVPEG_STATE_INITIALIZED 0xff01 +#define UNM_NIC_REG_SW_INT_MASK_0 ( UNM_NIC_REG + 0x001d8 ) +#define UNM_NIC_REG_SW_INT_MASK_1 ( UNM_NIC_REG + 0x001e0 ) +#define UNM_NIC_REG_SW_INT_MASK_2 ( UNM_NIC_REG + 0x001e4 ) +#define UNM_NIC_REG_SW_INT_MASK_3 ( UNM_NIC_REG + 0x001e8 ) + +#define UNM_CRB_ROMUSB UNM_CRB_BASE ( UNM_CRB_BLK_ROMUSB ) + +#define UNM_ROMUSB_GLB ( UNM_CRB_ROMUSB + 0x00000 ) +#define UNM_ROMUSB_GLB_STATUS ( UNM_ROMUSB_GLB + 0x00004 ) +#define UNM_ROMUSB_GLB_STATUS_ROM_DONE ( 1 << 1 ) +#define UNM_ROMUSB_GLB_SW_RESET ( UNM_ROMUSB_GLB + 0x00008 ) +#define UNM_ROMUSB_GLB_SW_RESET_MAGIC 0x0080000fUL +#define UNM_ROMUSB_GLB_PEGTUNE_DONE ( UNM_ROMUSB_GLB + 0x0005c ) +#define UNM_ROMUSB_GLB_PEGTUNE_DONE_MAGIC 0x31 + +#define UNM_ROMUSB_ROM ( UNM_CRB_ROMUSB + 0x10000 ) +#define UNM_ROMUSB_ROM_INSTR_OPCODE ( UNM_ROMUSB_ROM + 0x00004 ) +#define UNM_ROMUSB_ROM_ADDRESS ( UNM_ROMUSB_ROM + 0x00008 ) +#define UNM_ROMUSB_ROM_WDATA ( UNM_ROMUSB_ROM + 0x0000c ) +#define UNM_ROMUSB_ROM_ABYTE_CNT ( UNM_ROMUSB_ROM + 0x00010 ) +#define UNM_ROMUSB_ROM_DUMMY_BYTE_CNT ( UNM_ROMUSB_ROM + 0x00014 ) +#define UNM_ROMUSB_ROM_RDATA ( UNM_ROMUSB_ROM + 0x00018 ) + +#define UNM_CRB_TEST UNM_CRB_BASE ( UNM_CRB_BLK_TEST ) + +#define UNM_TEST_CONTROL ( UNM_CRB_TEST + 0x00090 ) +#define UNM_TEST_CONTROL_START 0x01 +#define UNM_TEST_CONTROL_ENABLE 0x02 +#define UNM_TEST_CONTROL_BUSY 0x08 +#define UNM_TEST_ADDR_LO ( UNM_CRB_TEST + 0x00094 ) +#define UNM_TEST_ADDR_HI ( UNM_CRB_TEST + 0x00098 ) +#define UNM_TEST_RDDATA_LO ( UNM_CRB_TEST + 0x000a8 ) +#define UNM_TEST_RDDATA_HI ( UNM_CRB_TEST + 0x000ac ) + +#define UNM_CRB_PEG_0 UNM_CRB_BASE ( UNM_CRB_BLK_PEG_0 ) +#define UNM_PEG_0_HALT_STATUS ( UNM_CRB_PEG_0 + 0x00030 ) +#define UNM_PEG_0_HALT ( UNM_CRB_PEG_0 + 0x0003c ) + +#define UNM_CRB_PEG_1 UNM_CRB_BASE ( UNM_CRB_BLK_PEG_1 ) +#define UNM_PEG_1_HALT_STATUS ( UNM_CRB_PEG_1 + 0x00030 ) +#define UNM_PEG_1_HALT ( UNM_CRB_PEG_1 + 0x0003c ) + +#define UNM_CRB_PEG_2 UNM_CRB_BASE ( UNM_CRB_BLK_PEG_2 ) +#define UNM_PEG_2_HALT_STATUS ( UNM_CRB_PEG_2 + 0x00030 ) +#define UNM_PEG_2_HALT ( UNM_CRB_PEG_2 + 0x0003c ) + +#define UNM_CRB_PEG_3 UNM_CRB_BASE ( UNM_CRB_BLK_PEG_3 ) +#define UNM_PEG_3_HALT_STATUS ( UNM_CRB_PEG_3 + 0x00030 ) +#define UNM_PEG_3_HALT ( UNM_CRB_PEG_3 + 0x0003c ) + +#define UNM_CRB_PEG_4 UNM_CRB_BASE ( UNM_CRB_BLK_PEG_4 ) +#define UNM_PEG_4_HALT_STATUS ( UNM_CRB_PEG_4 + 0x00030 ) +#define UNM_PEG_4_HALT ( UNM_CRB_PEG_4 + 0x0003c ) + +#endif /* _PHANTOM_H */ diff --git a/gpxe/src/drivers/net/phantom/phantom_hw.h b/gpxe/src/drivers/net/phantom/phantom_hw.h new file mode 100644 index 0000000..e2c3e53 --- /dev/null +++ b/gpxe/src/drivers/net/phantom/phantom_hw.h @@ -0,0 +1,182 @@ +#ifndef _PHANTOM_HW_H +#define _PHANTOM_HW_H + +/* + * Copyright (C) 2008 Michael Brown . + * Copyright (C) 2008 NetXen, 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 any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * @file + * + * Phantom hardware definitions + * + */ + +/** A Phantom RX descriptor */ +struct phantom_rds_pb { + pseudo_bit_t handle[16]; /**< Reference handle */ + pseudo_bit_t flags[16]; /**< Flags */ + pseudo_bit_t length[32]; /**< Buffer length */ + + /* --------------------------------------------------------------- */ + + pseudo_bit_t dma_addr[64]; /**< Buffer DMA address */ + +}; + +/** A Phantom RX status descriptor */ +struct phantom_sds_pb { + pseudo_bit_t port[4]; /**< Port number */ + pseudo_bit_t status[4]; /**< Checksum status */ + pseudo_bit_t type[4]; /**< Type */ + pseudo_bit_t total_length[16]; /**< Total packet length */ + pseudo_bit_t handle[16]; /**< Reference handle */ + pseudo_bit_t protocol[4]; /**< Protocol */ + pseudo_bit_t pkt_offset[5]; /**< Offset to packet start */ + pseudo_bit_t desc_cnt[3]; /**< Descriptor count */ + pseudo_bit_t owner[2]; /**< Owner */ + pseudo_bit_t opcode[6]; /**< Opcode */ + + /* --------------------------------------------------------------- */ + + pseudo_bit_t hash_value[32]; /**< RSS hash value */ + pseudo_bit_t hash_type[8]; /**< RSS hash type */ + pseudo_bit_t lro[8]; /**< LRO data */ +}; + +/** Phantom RX status opcodes */ +enum phantom_sds_opcode { + UNM_SYN_OFFLOAD = 0x03, + UNM_RXPKT_DESC = 0x04, +}; + +/** A Phantom TX descriptor */ +struct phantom_tx_cds_pb { + pseudo_bit_t tcp_hdr_offset[8]; /**< TCP header offset (LSO) */ + pseudo_bit_t ip_hdr_offset[8]; /**< IP header offset (LSO) */ + pseudo_bit_t flags[7]; /**< Flags */ + pseudo_bit_t opcode[6]; /**< Opcode */ + pseudo_bit_t hw_rsvd_0[3]; /**< (Reserved) */ + pseudo_bit_t num_buffers[8]; /**< Total number of buffers */ + pseudo_bit_t length[24]; /**< Total length */ + + /* --------------------------------------------------------------- */ + + pseudo_bit_t buffer2_dma_addr[64]; /**< Buffer 2 DMA address */ + + /* --------------------------------------------------------------- */ + + pseudo_bit_t handle[16]; /**< Reference handle (n/a) */ + pseudo_bit_t port_mss[16]; /**< TCP MSS (LSO) */ + pseudo_bit_t port[4]; /**< Port */ + pseudo_bit_t context_id[4]; /**< Context ID */ + pseudo_bit_t total_hdr_length[8]; /**< MAC+IP+TCP header (LSO) */ + pseudo_bit_t conn_id[16]; /**< IPSec connection ID */ + + /* --------------------------------------------------------------- */ + + pseudo_bit_t buffer3_dma_addr[64]; /**< Buffer 3 DMA address */ + + /* --------------------------------------------------------------- */ + + pseudo_bit_t buffer1_dma_addr[64]; /**< Buffer 1 DMA address */ + + /* --------------------------------------------------------------- */ + + pseudo_bit_t buffer1_length[16]; /**< Buffer 1 length */ + pseudo_bit_t buffer2_length[16]; /**< Buffer 2 length */ + pseudo_bit_t buffer3_length[16]; /**< Buffer 3 length */ + pseudo_bit_t buffer4_length[16]; /**< Buffer 4 length */ + + /* --------------------------------------------------------------- */ + + pseudo_bit_t buffer4_dma_addr[64]; /**< Buffer 4 DMA address */ + + /* --------------------------------------------------------------- */ + + pseudo_bit_t hw_rsvd_1[64]; /**< (Reserved) */ +}; + +/** A Phantom MAC address request body */ +struct phantom_nic_request_body_mac_request_pb { + pseudo_bit_t opcode[8]; /**< Opcode */ + pseudo_bit_t tag[8]; /**< Tag */ + pseudo_bit_t mac_addr_0[8]; /**< MAC address byte 0 */ + pseudo_bit_t mac_addr_1[8]; /**< MAC address byte 1 */ + pseudo_bit_t mac_addr_2[8]; /**< MAC address byte 2 */ + pseudo_bit_t mac_addr_3[8]; /**< MAC address byte 3 */ + pseudo_bit_t mac_addr_4[8]; /**< MAC address byte 4 */ + pseudo_bit_t mac_addr_5[8]; /**< MAC address byte 5 */ +}; + +/** Phantom MAC request opcodes */ +enum phantom_mac_request_opcode { + UNM_MAC_ADD = 0x01, /**< Add MAC address */ + UNM_MAC_DEL = 0x02, /**< Delete MAC address */ +}; + +/** A Phantom NIC request command descriptor */ +struct phantom_nic_request_cds_pb { + struct { + pseudo_bit_t dst_minor[18]; + pseudo_bit_t dst_subq[1]; + pseudo_bit_t dst_major[4]; + pseudo_bit_t opcode[6]; + pseudo_bit_t hw_rsvd_0[3]; + pseudo_bit_t msginfo[24]; + pseudo_bit_t hw_rsvd_1[2]; + pseudo_bit_t qmsg_type[6]; + } common; + + /* --------------------------------------------------------------- */ + + struct { + pseudo_bit_t opcode[8]; + pseudo_bit_t comp_id [8]; + pseudo_bit_t context_id[16]; + pseudo_bit_t need_completion[1]; + pseudo_bit_t hw_rsvd_0[23]; + pseudo_bit_t sub_opcode[8]; + } header; + + /* --------------------------------------------------------------- */ + + union { + struct phantom_nic_request_body_mac_request_pb mac_request; + pseudo_bit_t padding[384]; + } body; +}; + +/** Phantom NIC request opcodes */ +enum phantom_nic_request_opcode { + UNM_MAC_EVENT = 0x01, /**< Add/delete MAC address */ +}; + +/** A Phantom command descriptor */ +union phantom_cds_pb { + struct phantom_tx_cds_pb tx; + struct phantom_nic_request_cds_pb nic_request; +}; + +/** Phantom command descriptor opcodes */ +enum phantom_cds_opcode { + UNM_TX_ETHER_PKT = 0x01, /**< Transmit raw Ethernet */ + UNM_NIC_REQUEST = 0x14, /**< NIC request */ +}; + +#endif /* _PHANTOM_HW_H */ diff --git a/gpxe/src/drivers/net/pnic.c b/gpxe/src/drivers/net/pnic.c new file mode 100644 index 0000000..3a4af96 --- /dev/null +++ b/gpxe/src/drivers/net/pnic.c @@ -0,0 +1,279 @@ +/************************************************************************** +Etherboot - BOOTP/TFTP Bootstrap Program +Bochs Pseudo NIC driver for Etherboot +***************************************************************************/ + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, or (at + * your option) any later version. + * + * See pnic_api.h for an explanation of the Bochs Pseudo NIC. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pnic_api.h" + +struct pnic { + unsigned short ioaddr; +}; + +/* + * Utility functions: issue a PNIC command, retrieve result. Use + * pnic_command_quiet if you don't want failure codes to be + * automatically printed. Returns the PNIC status code. + * + * Set output_length to NULL only if you expect to receive exactly + * output_max_length bytes, otherwise it'll complain that you didn't + * get enough data (on the assumption that if you not interested in + * discovering the output length then you're expecting a fixed amount + * of data). + */ + +static uint16_t pnic_command_quiet ( struct pnic *pnic, uint16_t command, + const void *input, uint16_t input_length, + void *output, uint16_t output_max_length, + uint16_t *output_length ) { + uint16_t status; + uint16_t _output_length; + + if ( input != NULL ) { + /* Write input length */ + outw ( input_length, pnic->ioaddr + PNIC_REG_LEN ); + /* Write input data */ + outsb ( pnic->ioaddr + PNIC_REG_DATA, input, input_length ); + } + /* Write command */ + outw ( command, pnic->ioaddr + PNIC_REG_CMD ); + /* Retrieve status */ + status = inw ( pnic->ioaddr + PNIC_REG_STAT ); + /* Retrieve output length */ + _output_length = inw ( pnic->ioaddr + PNIC_REG_LEN ); + if ( output_length == NULL ) { + if ( _output_length != output_max_length ) { + printf ( "pnic_command %#hx: wrong data length " + "returned (expected %d, got %d)\n", command, + output_max_length, _output_length ); + } + } else { + *output_length = _output_length; + } + if ( output != NULL ) { + if ( _output_length > output_max_length ) { + printf ( "pnic_command %#hx: output buffer too small " + "(have %d, need %d)\n", command, + output_max_length, _output_length ); + _output_length = output_max_length; + } + /* Retrieve output data */ + insb ( pnic->ioaddr + PNIC_REG_DATA, output, _output_length ); + } + return status; +} + +static uint16_t pnic_command ( struct pnic *pnic, uint16_t command, + const void *input, uint16_t input_length, + void *output, uint16_t output_max_length, + uint16_t *output_length ) { + uint16_t status = pnic_command_quiet ( pnic, command, + input, input_length, + output, output_max_length, + output_length ); + if ( status == PNIC_STATUS_OK ) return status; + printf ( "PNIC command %#hx (len %#hx) failed with status %#hx\n", + command, input_length, status ); + return status; +} + +/* Check API version matches that of NIC */ +static int pnic_api_check ( uint16_t api_version ) { + if ( api_version != PNIC_API_VERSION ) { + printf ( "Warning: API version mismatch! " + "(NIC's is %d.%d, ours is %d.%d)\n", + api_version >> 8, api_version & 0xff, + PNIC_API_VERSION >> 8, PNIC_API_VERSION & 0xff ); + } + if ( api_version < PNIC_API_VERSION ) { + printf ( "** You may need to update your copy of Bochs **\n" ); + } + return ( api_version == PNIC_API_VERSION ); +} + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static void pnic_poll ( struct net_device *netdev ) { + struct pnic *pnic = netdev->priv; + struct io_buffer *iobuf; + uint16_t length; + uint16_t qlen; + + /* Fetch all available packets */ + while ( 1 ) { + if ( pnic_command ( pnic, PNIC_CMD_RECV_QLEN, NULL, 0, + &qlen, sizeof ( qlen ), NULL ) + != PNIC_STATUS_OK ) + return; + if ( qlen == 0 ) + return; + iobuf = alloc_iob ( ETH_FRAME_LEN ); + if ( ! iobuf ) { + DBG ( "could not allocate buffer\n" ); + netdev_rx_err ( netdev, NULL, -ENOMEM ); + return; + } + if ( pnic_command ( pnic, PNIC_CMD_RECV, NULL, 0, + iobuf->data, ETH_FRAME_LEN, &length ) + != PNIC_STATUS_OK ) { + netdev_rx_err ( netdev, iobuf, -EIO ); + return; + } + iob_put ( iobuf, length ); + netdev_rx ( netdev, iobuf ); + } +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static int pnic_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { + struct pnic *pnic = netdev->priv; + + /* Pad the packet */ + iob_pad ( iobuf, ETH_ZLEN ); + + /* Send packet */ + pnic_command ( pnic, PNIC_CMD_XMIT, iobuf->data, iob_len ( iobuf ), + NULL, 0, NULL ); + + netdev_tx_complete ( netdev, iobuf ); + return 0; +} + +/************************************************************************** +OPEN - Open network device +***************************************************************************/ +static int pnic_open ( struct net_device *netdev __unused ) { + /* Nothing to do */ + return 0; +} + +/************************************************************************** +CLOSE - Close network device +***************************************************************************/ +static void pnic_close ( struct net_device *netdev __unused ) { + /* Nothing to do */ +} + +/************************************************************************** +IRQ - Enable/disable interrupts +***************************************************************************/ +static void pnic_irq ( struct net_device *netdev, int enable ) { + struct pnic *pnic = netdev->priv; + uint8_t mask = ( enable ? 1 : 0 ); + + pnic_command ( pnic, PNIC_CMD_MASK_IRQ, &mask, sizeof ( mask ), + NULL, 0, NULL ); +} + +/************************************************************************** +OPERATIONS TABLE +***************************************************************************/ +static struct net_device_operations pnic_operations = { + .open = pnic_open, + .close = pnic_close, + .transmit = pnic_transmit, + .poll = pnic_poll, + .irq = pnic_irq, +}; + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void pnic_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct pnic *pnic = netdev->priv; + + unregister_netdev ( netdev ); + pnic_command ( pnic, PNIC_CMD_RESET, NULL, 0, NULL, 0, NULL ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ +static int pnic_probe ( struct pci_device *pci, + const struct pci_device_id *id __unused ) { + struct net_device *netdev; + struct pnic *pnic; + uint16_t api_version; + uint16_t status; + int rc; + + /* Allocate net device */ + netdev = alloc_etherdev ( sizeof ( *pnic ) ); + if ( ! netdev ) + return -ENOMEM; + netdev_init ( netdev, &pnic_operations ); + pnic = netdev->priv; + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + pnic->ioaddr = pci->ioaddr; + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* API version check */ + status = pnic_command_quiet ( pnic, PNIC_CMD_API_VER, NULL, 0, + &api_version, + sizeof ( api_version ), NULL ); + if ( status != PNIC_STATUS_OK ) { + printf ( "PNIC failed installation check, code %#hx\n", + status ); + rc = -EIO; + goto err; + } + pnic_api_check ( api_version ); + + /* Get MAC address */ + status = pnic_command ( pnic, PNIC_CMD_READ_MAC, NULL, 0, + netdev->ll_addr, ETH_ALEN, NULL ); + + /* Mark as link up; PNIC has no concept of link state */ + netdev_link_up ( netdev ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err; + + return 0; + + err: + /* Free net device */ + netdev_nullify ( netdev ); + netdev_put ( netdev ); + return rc; +} + +static struct pci_device_id pnic_nics[] = { +/* genrules.pl doesn't let us use macros for PCI IDs...*/ +PCI_ROM ( 0xfefe, 0xefef, "pnic", "Bochs Pseudo NIC Adaptor" ), +}; + +struct pci_driver pnic_driver __pci_driver = { + .ids = pnic_nics, + .id_count = ( sizeof ( pnic_nics ) / sizeof ( pnic_nics[0] ) ), + .probe = pnic_probe, + .remove = pnic_remove, +}; diff --git a/gpxe/src/drivers/net/pnic_api.h b/gpxe/src/drivers/net/pnic_api.h new file mode 100644 index 0000000..6d117fa --- /dev/null +++ b/gpxe/src/drivers/net/pnic_api.h @@ -0,0 +1,59 @@ +/* + * Constants etc. for the Bochs/Etherboot pseudo-NIC + * + * This header file must be valid C and C++. + * + * Operation of the pseudo-NIC (PNIC) is pretty simple. To write a + * command plus data, first write the length of the data to + * PNIC_REG_LEN, then write the data a byte at a type to + * PNIC_REG_DATA, then write the command code to PNIC_REG_CMD. The + * status will be available from PNIC_REG_STAT. The length of any + * data returned will be in PNIC_REG_LEN and can be read a byte at a + * time from PNIC_REG_DATA. + */ + +/* + * PCI parameters + */ +#define PNIC_PCI_VENDOR 0xfefe /* Hopefully these won't clash with */ +#define PNIC_PCI_DEVICE 0xefef /* any real PCI device IDs. */ + +/* + * 'Hardware' register addresses, offset from io_base + */ +#define PNIC_REG_CMD 0x00 /* Command register, 2 bytes, write only */ +#define PNIC_REG_STAT 0x00 /* Status register, 2 bytes, read only */ +#define PNIC_REG_LEN 0x02 /* Length register, 2 bytes, read-write */ +#define PNIC_REG_DATA 0x04 /* Data port, 1 byte, read-write */ +/* + * PNIC_MAX_REG used in Bochs to claim i/o space + */ +#define PNIC_MAX_REG 0x04 + +/* + * Command code definitions: write these into PNIC_REG_CMD + */ +#define PNIC_CMD_NOOP 0x0000 +#define PNIC_CMD_API_VER 0x0001 +#define PNIC_CMD_READ_MAC 0x0002 +#define PNIC_CMD_RESET 0x0003 +#define PNIC_CMD_XMIT 0x0004 +#define PNIC_CMD_RECV 0x0005 +#define PNIC_CMD_RECV_QLEN 0x0006 +#define PNIC_CMD_MASK_IRQ 0x0007 +#define PNIC_CMD_FORCE_IRQ 0x0008 + +/* + * Status code definitions: read these from PNIC_REG_STAT + * + * We avoid using status codes that might be confused with + * randomly-read data (e.g. 0x0000, 0xffff etc.) + */ +#define PNIC_STATUS_OK 0x4f4b /* 'OK' */ +#define PNIC_STATUS_UNKNOWN_CMD 0x3f3f /* '??' */ + +/* + * Other miscellaneous information + */ + +#define PNIC_API_VERSION 0x0101 /* 1.1 */ diff --git a/gpxe/src/drivers/net/prism2.c b/gpxe/src/drivers/net/prism2.c new file mode 100644 index 0000000..c54dba5 --- /dev/null +++ b/gpxe/src/drivers/net/prism2.c @@ -0,0 +1,855 @@ +/************************************************************************** +Etherboot - BOOTP/TFTP Bootstrap Program +Prism2 NIC driver for Etherboot + +Written by Michael Brown of Fen Systems Ltd +$Id$ +***************************************************************************/ + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, or (at + * your option) any later version. + */ + +#include +#include +#include +#include + +/* + * 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 +#define __le16_to_cpu(x) (x) +#define __le32_to_cpu(x) (x) +#define __cpu_to_le16(x) (x) +#define __cpu_to_le32(x) (x) + +#define hfa384x2host_16(n) (__le16_to_cpu((UINT16)(n))) +#define hfa384x2host_32(n) (__le32_to_cpu((UINT32)(n))) +#define host2hfa384x_16(n) (__cpu_to_le16((UINT16)(n))) +#define host2hfa384x_32(n) (__cpu_to_le32((UINT32)(n))) + +/* + * PLX9052 PCI register offsets + * Taken from PLX9052 datasheet available from http://www.plxtech.com/download/9052/databook/9052db-20.pdf + */ + +#define PLX_LOCAL_CONFIG_REGISTER_BASE ( PCI_BASE_ADDRESS_1 ) +#define PLX_LOCAL_ADDRESS_SPACE_0_BASE ( PCI_BASE_ADDRESS_2 ) +#define PLX_LOCAL_ADDRESS_SPACE_1_BASE ( PCI_BASE_ADDRESS_3 ) +#define PLX_LOCAL_ADDRESS_SPACE_2_BASE ( PCI_BASE_ADDRESS_4 ) +#define PLX_LOCAL_ADDRESS_SPACE_3_BASE ( PCI_BASE_ADDRESS_5 ) + +#define PRISM2_PLX_ATTR_MEM_BASE ( PLX_LOCAL_ADDRESS_SPACE_0_BASE ) +#define PRISM2_PLX_IO_BASE ( PLX_LOCAL_ADDRESS_SPACE_1_BASE ) + +#define PRISM2_PCI_MEM_BASE ( PCI_BASE_ADDRESS_0 ) + +/* + * PCMCIA CIS types + * Taken from cistpl.h in pcmcia-cs + */ + +#define CISTPL_VERS_1 ( 0x15 ) +#define CISTPL_END ( 0xff ) + +#define CIS_STEP ( 2 ) +#define CISTPL_HEADER_LEN ( 2 * CIS_STEP ) +#define CISTPL_LEN_OFF ( 1 * CIS_STEP ) +#define CISTPL_VERS_1_STR_OFF ( 4 * CIS_STEP ) + +/* + * Prism2 constants + * Taken from prism2sta.c in linux-wlan-ng + */ + +#define COR_OFFSET ( 0x3e0 ) /* COR attribute offset of Prism2 PC card */ +#define COR_VALUE ( 0x41 ) /* Enable PC card with irq in level trigger (but interrupts disabled) */ + +/* NIC specific static variables */ + +/* The hfa384x_t structure is used extensively in the Linux driver but is ifdef'd out in our include since __KERNEL__ is not defined. + * This is a dummy version that contains only the fields we are interested in. + */ + +typedef struct hfa384x +{ + UINT32 iobase; + void *membase; + UINT16 lastcmd; + UINT16 status; /* in host order */ + UINT16 resp0; /* in host order */ + UINT16 resp1; /* in host order */ + UINT16 resp2; /* in host order */ + UINT8 bssid[WLAN_BSSID_LEN]; +} hfa384x_t; + +/* The global instance of the hardware (i.e. where we store iobase and membase, in the absence of anywhere better to put them */ +static hfa384x_t hw_global = { + 0, 0, 0, 0, 0, 0, 0, {0,0,0,0,0,0} +}; + +/* + * 802.11 headers in addition to those in hfa384x_tx_frame_t (LLC and SNAP) + * Taken from p80211conv.h + */ + +typedef struct wlan_llc +{ + UINT8 dsap; + UINT8 ssap; + UINT8 ctl; +} wlan_llc_t; + +static const wlan_llc_t wlan_llc_snap = { 0xaa, 0xaa, 0x03 }; /* LLC header indicating SNAP (?) */ + +#define WLAN_IEEE_OUI_LEN 3 +typedef struct wlan_snap +{ + UINT8 oui[WLAN_IEEE_OUI_LEN]; + UINT16 type; +} wlan_snap_t; + +typedef struct wlan_80211hdr +{ + wlan_llc_t llc; + wlan_snap_t snap; +} wlan_80211hdr_t; + +/* + * Function prototypes + */ + +/* + * Hardware-level hfa384x functions + * These are based on the ones in hfa384x.h (which are ifdef'd out since __KERNEL__ is not defined). + * Basically, these functions are the result of hand-evaluating all the ifdefs and defines in the hfa384x.h versions. + */ + +/* Retrieve the value of one of the MAC registers. */ +static inline UINT16 hfa384x_getreg( hfa384x_t *hw, UINT reg ) +{ +#if (WLAN_HOSTIF == WLAN_PLX) + return inw ( hw->iobase + reg ); +#elif (WLAN_HOSTIF == WLAN_PCI) + return readw ( hw->membase + reg ); +#endif +} + +/* Set the value of one of the MAC registers. */ +static inline void hfa384x_setreg( hfa384x_t *hw, UINT16 val, UINT reg ) +{ +#if (WLAN_HOSTIF == WLAN_PLX) + outw ( val, hw->iobase + reg ); +#elif (WLAN_HOSTIF == WLAN_PCI) + writew ( val, hw->membase + reg ); +#endif + return; +} + +/* + * Noswap versions + * Etherboot is i386 only, so swap and noswap are the same... + */ +static inline UINT16 hfa384x_getreg_noswap( hfa384x_t *hw, UINT reg ) +{ + return hfa384x_getreg ( hw, reg ); +} +static inline void hfa384x_setreg_noswap( hfa384x_t *hw, UINT16 val, UINT reg ) +{ + hfa384x_setreg ( hw, val, reg ); +} + +/* + * Low-level hfa384x functions + * These are based on the ones in hfa384x.c, modified to work in the Etherboot environment. + */ + +/* + * hfa384x_docmd_wait + * + * Waits for availability of the Command register, then + * issues the given command. Then polls the Evstat register + * waiting for command completion. + * Arguments: + * hw device structure + * cmd Command in host order + * parm0 Parameter0 in host order + * parm1 Parameter1 in host order + * parm2 Parameter2 in host order + * Returns: + * 0 success + * >0 command indicated error, Status and Resp0-2 are + * in hw structure. + */ +static int hfa384x_docmd_wait( hfa384x_t *hw, UINT16 cmd, UINT16 parm0, UINT16 parm1, UINT16 parm2) +{ + UINT16 reg = 0; + UINT16 counter = 0; + + /* wait for the busy bit to clear */ + counter = 0; + reg = hfa384x_getreg(hw, HFA384x_CMD); + while ( HFA384x_CMD_ISBUSY(reg) && (counter < 10) ) { + reg = hfa384x_getreg(hw, HFA384x_CMD); + counter++; + udelay(10); + } + if (HFA384x_CMD_ISBUSY(reg)) { + printf("hfa384x_cmd timeout(1), reg=0x%0hx.\n", reg); + return -ETIMEDOUT; + } + + /* busy bit clear, write command */ + hfa384x_setreg(hw, parm0, HFA384x_PARAM0); + hfa384x_setreg(hw, parm1, HFA384x_PARAM1); + hfa384x_setreg(hw, parm2, HFA384x_PARAM2); + hw->lastcmd = cmd; + hfa384x_setreg(hw, cmd, HFA384x_CMD); + + /* Now wait for completion */ + counter = 0; + reg = hfa384x_getreg(hw, HFA384x_EVSTAT); + /* Initialization is the problem. It takes about + 100ms. "normal" commands are typically is about + 200-400 us (I've never seen less than 200). Longer + is better so that we're not hammering the bus. */ + while ( !HFA384x_EVSTAT_ISCMD(reg) && (counter < 5000)) { + reg = hfa384x_getreg(hw, HFA384x_EVSTAT); + counter++; + udelay(200); + } + if ( ! HFA384x_EVSTAT_ISCMD(reg) ) { + printf("hfa384x_cmd timeout(2), reg=0x%0hx.\n", reg); + return -ETIMEDOUT; + } + + /* Read status and response */ + hw->status = hfa384x_getreg(hw, HFA384x_STATUS); + hw->resp0 = hfa384x_getreg(hw, HFA384x_RESP0); + hw->resp1 = hfa384x_getreg(hw, HFA384x_RESP1); + hw->resp2 = hfa384x_getreg(hw, HFA384x_RESP2); + hfa384x_setreg(hw, HFA384x_EVACK_CMD, HFA384x_EVACK); + return HFA384x_STATUS_RESULT_GET(hw->status); +} + +/* + * Prepare BAP for access. Assigns FID and RID, sets offset register + * and waits for BAP to become available. + * + * Arguments: + * hw device structure + * id FID or RID, destined for the select register (host order) + * offset An _even_ offset into the buffer for the given FID/RID. + * Returns: + * 0 success + */ +static int hfa384x_prepare_bap(hfa384x_t *hw, UINT16 id, UINT16 offset) +{ + int result = 0; + UINT16 reg; + UINT16 i; + + /* Validate offset, buf, and len */ + if ( (offset > HFA384x_BAP_OFFSET_MAX) || (offset % 2) ) { + result = -EINVAL; + } else { + /* Write fid/rid and offset */ + hfa384x_setreg(hw, id, HFA384x_SELECT0); + udelay(10); + hfa384x_setreg(hw, offset, HFA384x_OFFSET0); + /* Wait for offset[busy] to clear (see BAP_TIMEOUT) */ + i = 0; + do { + reg = hfa384x_getreg(hw, HFA384x_OFFSET0); + if ( i > 0 ) udelay(2); + i++; + } while ( i < BAP_TIMEOUT && HFA384x_OFFSET_ISBUSY(reg)); + if ( i >= BAP_TIMEOUT ) { + /* failure */ + result = reg; + } else if ( HFA384x_OFFSET_ISERR(reg) ){ + /* failure */ + result = reg; + } + } + return result; +} + +/* + * Copy data from BAP to memory. + * + * Arguments: + * hw device structure + * id FID or RID, destined for the select register (host order) + * offset An _even_ offset into the buffer for the given FID/RID. + * buf ptr to array of bytes + * len length of data to transfer in bytes + * Returns: + * 0 success + */ +static int hfa384x_copy_from_bap(hfa384x_t *hw, UINT16 id, UINT16 offset, + void *buf, UINT len) +{ + int result = 0; + UINT8 *d = (UINT8*)buf; + UINT16 i; + UINT16 reg = 0; + + /* Prepare BAP */ + result = hfa384x_prepare_bap ( hw, id, offset ); + if ( result == 0 ) { + /* Read even(len) buf contents from data reg */ + for ( i = 0; i < (len & 0xfffe); i+=2 ) { + *(UINT16*)(&(d[i])) = hfa384x_getreg_noswap(hw, HFA384x_DATA0); + } + /* If len odd, handle last byte */ + if ( len % 2 ){ + reg = hfa384x_getreg_noswap(hw, HFA384x_DATA0); + d[len-1] = ((UINT8*)(®))[0]; + } + } + if (result) { + printf ( "copy_from_bap(%#hx, %#hx, %d) failed, result=%#hx\n", id, offset, len, result); + } + return result; +} + +/* + * Copy data from memory to BAP. + * + * Arguments: + * hw device structure + * id FID or RID, destined for the select register (host order) + * offset An _even_ offset into the buffer for the given FID/RID. + * buf ptr to array of bytes + * len length of data to transfer in bytes + * Returns: + * 0 success + */ +static int hfa384x_copy_to_bap(hfa384x_t *hw, UINT16 id, UINT16 offset, + void *buf, UINT len) +{ + int result = 0; + UINT8 *d = (UINT8*)buf; + UINT16 i; + UINT16 savereg; + + /* Prepare BAP */ + result = hfa384x_prepare_bap ( hw, id, offset ); + if ( result == 0 ) { + /* Write even(len) buf contents to data reg */ + for ( i = 0; i < (len & 0xfffe); i+=2 ) { + hfa384x_setreg_noswap(hw, *(UINT16*)(&(d[i])), HFA384x_DATA0); + } + /* If len odd, handle last byte */ + if ( len % 2 ){ + savereg = hfa384x_getreg_noswap(hw, HFA384x_DATA0); + result = hfa384x_prepare_bap ( hw, id, offset + (len & 0xfffe) ); + if ( result == 0 ) { + ((UINT8*)(&savereg))[0] = d[len-1]; + hfa384x_setreg_noswap(hw, savereg, HFA384x_DATA0); + } + } + } + if (result) { + printf ( "copy_to_bap(%#hx, %#hx, %d) failed, result=%#hx\n", id, offset, len, result); + } + return result; +} + +/* + * Request a given record to be copied to/from the record buffer. + * + * Arguments: + * hw device structure + * write [0|1] copy the record buffer to the given + * configuration record. (host order) + * rid RID of the record to read/write. (host order) + * + * Returns: + * 0 success + */ +static inline int hfa384x_cmd_access(hfa384x_t *hw, UINT16 write, UINT16 rid) +{ + return hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ACCESS) | HFA384x_CMD_WRITE_SET(write), rid, 0, 0); +} + +/* + * Performs the sequence necessary to read a config/info item. + * + * Arguments: + * hw device structure + * rid config/info record id (host order) + * buf host side record buffer. Upon return it will + * contain the body portion of the record (minus the + * RID and len). + * len buffer length (in bytes, should match record length) + * + * Returns: + * 0 success + */ +static int hfa384x_drvr_getconfig(hfa384x_t *hw, UINT16 rid, void *buf, UINT16 len) +{ + int result = 0; + hfa384x_rec_t rec; + + /* Request read of RID */ + result = hfa384x_cmd_access( hw, 0, rid); + if ( result ) { + printf("Call to hfa384x_cmd_access failed\n"); + return -1; + } + /* Copy out record length */ + result = hfa384x_copy_from_bap( hw, rid, 0, &rec, sizeof(rec)); + if ( result ) { + return -1; + } + /* Validate the record length */ + if ( ((hfa384x2host_16(rec.reclen)-1)*2) != len ) { /* note body len calculation in bytes */ + printf ( "RID len mismatch, rid=%#hx hlen=%d fwlen=%d\n", rid, len, (hfa384x2host_16(rec.reclen)-1)*2); + return -1; + } + /* Copy out record data */ + result = hfa384x_copy_from_bap( hw, rid, sizeof(rec), buf, len); + return result; +} + +/* + * Performs the sequence necessary to read a 16/32 bit config/info item + * and convert it to host order. + * + * Arguments: + * hw device structure + * rid config/info record id (in host order) + * val ptr to 16/32 bit buffer to receive value (in host order) + * + * Returns: + * 0 success + */ +#if 0 /* Not actually used anywhere */ +static int hfa384x_drvr_getconfig16(hfa384x_t *hw, UINT16 rid, void *val) +{ + int result = 0; + result = hfa384x_drvr_getconfig(hw, rid, val, sizeof(UINT16)); + if ( result == 0 ) { + *((UINT16*)val) = hfa384x2host_16(*((UINT16*)val)); + } + return result; +} +#endif +#if 0 /* Not actually used anywhere */ +static int hfa384x_drvr_getconfig32(hfa384x_t *hw, UINT16 rid, void *val) +{ + int result = 0; + result = hfa384x_drvr_getconfig(hw, rid, val, sizeof(UINT32)); + if ( result == 0 ) { + *((UINT32*)val) = hfa384x2host_32(*((UINT32*)val)); + } + return result; +} +#endif + +/* + * Performs the sequence necessary to write a config/info item. + * + * Arguments: + * hw device structure + * rid config/info record id (in host order) + * buf host side record buffer + * len buffer length (in bytes) + * + * Returns: + * 0 success + */ +static int hfa384x_drvr_setconfig(hfa384x_t *hw, UINT16 rid, void *buf, UINT16 len) +{ + int result = 0; + hfa384x_rec_t rec; + + rec.rid = host2hfa384x_16(rid); + rec.reclen = host2hfa384x_16((len/2) + 1); /* note conversion to words, +1 for rid field */ + /* write the record header */ + result = hfa384x_copy_to_bap( hw, rid, 0, &rec, sizeof(rec)); + if ( result ) { + printf("Failure writing record header\n"); + return -1; + } + /* write the record data (if there is any) */ + if ( len > 0 ) { + result = hfa384x_copy_to_bap( hw, rid, sizeof(rec), buf, len); + if ( result ) { + printf("Failure writing record data\n"); + return -1; + } + } + /* Trigger setting of record */ + result = hfa384x_cmd_access( hw, 1, rid); + return result; +} + +/* + * Performs the sequence necessary to write a 16/32 bit config/info item. + * + * Arguments: + * hw device structure + * rid config/info record id (in host order) + * val 16/32 bit value to store (in host order) + * + * Returns: + * 0 success + */ +static int hfa384x_drvr_setconfig16(hfa384x_t *hw, UINT16 rid, UINT16 *val) +{ + UINT16 value; + value = host2hfa384x_16(*val); + return hfa384x_drvr_setconfig(hw, rid, &value, sizeof(UINT16)); +} +#if 0 /* Not actually used anywhere */ +static int hfa384x_drvr_setconfig32(hfa384x_t *hw, UINT16 rid, UINT32 *val) +{ + UINT32 value; + value = host2hfa384x_32(*val); + return hfa384x_drvr_setconfig(hw, rid, &value, sizeof(UINT32)); +} +#endif + +/* + * Wait for an event, with specified checking interval and timeout. + * Automatically acknolwedges events. + * + * Arguments: + * hw device structure + * event_mask EVSTAT register mask of events to wait for + * event_ack EVACK register set of events to be acknowledged if they happen (can be + * used to acknowledge "ignorable" events in addition to the "main" event) + * wait Time (in us) to wait between each poll of the register + * timeout Maximum number of polls before timing out + * descr Descriptive text string of what is being waited for + * (will be printed out if a timeout happens) + * + * Returns: + * value of EVSTAT register, or 0 on failure + */ +static int hfa384x_wait_for_event(hfa384x_t *hw, UINT16 event_mask, UINT16 event_ack, int wait, int timeout, const char *descr) +{ + UINT16 reg; + int count = 0; + + do { + reg = hfa384x_getreg(hw, HFA384x_EVSTAT); + if ( count > 0 ) udelay(wait); + count++; + } while ( !(reg & event_mask) && count < timeout); + if ( count >= timeout ) { + printf("hfa384x: Timed out waiting for %s\n", descr); + return 0; /* Return failure */ + } + /* Acknowledge all events that we were waiting on */ + hfa384x_setreg(hw, reg & ( event_mask | event_ack ), HFA384x_EVACK); + return reg; +} + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int prism2_poll(struct nic *nic, int retrieve) +{ + UINT16 reg; + UINT16 rxfid; + UINT16 result; + hfa384x_rx_frame_t rxdesc; + hfa384x_t *hw = &hw_global; + + /* Check for received packet */ + reg = hfa384x_getreg(hw, HFA384x_EVSTAT); + if ( ! HFA384x_EVSTAT_ISRX(reg) ) { + /* No packet received - return 0 */ + return 0; + } + + if ( ! retrieve ) return 1; + + /* Acknowledge RX event */ + hfa384x_setreg(hw, HFA384x_EVACK_RX_SET(1), HFA384x_EVACK); + /* Get RX FID */ + rxfid = hfa384x_getreg(hw, HFA384x_RXFID); + /* Get the descriptor (including headers) */ + result = hfa384x_copy_from_bap(hw, rxfid, 0, &rxdesc, sizeof(rxdesc)); + if ( result ) { + return 0; /* fail */ + } + /* Byte order convert once up front. */ + rxdesc.status = hfa384x2host_16(rxdesc.status); + rxdesc.time = hfa384x2host_32(rxdesc.time); + rxdesc.data_len = hfa384x2host_16(rxdesc.data_len); + + /* Fill in nic->packetlen */ + nic->packetlen = rxdesc.data_len; + if ( nic->packetlen > 0 ) { + /* Fill in nic->packet */ + /* + * NOTE: Packets as received have an 8-byte header (LLC+SNAP(?)) terminating with the packet type. + * Etherboot expects a 14-byte header terminating with the packet type (it ignores the rest of the + * header), so we use a quick hack to achieve this. + */ + result = hfa384x_copy_from_bap(hw, rxfid, HFA384x_RX_DATA_OFF, + nic->packet + ETH_HLEN - sizeof(wlan_80211hdr_t), nic->packetlen); + if ( result ) { + return 0; /* fail */ + } + } + return 1; /* Packet successfully received */ +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static void prism2_transmit( + struct nic *nic, + const char *d, /* Destination */ + unsigned int t, /* Type */ + unsigned int s, /* size */ + const char *p) /* Packet */ +{ + hfa384x_t *hw = &hw_global; + hfa384x_tx_frame_t txdesc; + wlan_80211hdr_t p80211hdr = { wlan_llc_snap, {{0,0,0},0} }; + UINT16 fid; + UINT16 status; + int result; + + // Request FID allocation + result = hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ALLOC), HFA384x_DRVR_TXBUF_MAX, 0, 0); + if (result != 0) { + printf("hfa384x: Tx FID allocate command failed: Aborting transmit..\n"); + return; + } + if ( !hfa384x_wait_for_event(hw, HFA384x_EVSTAT_ALLOC, HFA384x_EVACK_INFO, 10, 50, "Tx FID to be allocated\n" ) ) return; + fid = hfa384x_getreg(hw, HFA384x_ALLOCFID); + + /* Build Tx frame structure */ + memset(&txdesc, 0, sizeof(txdesc)); + txdesc.tx_control = host2hfa384x_16( HFA384x_TX_MACPORT_SET(0) | HFA384x_TX_STRUCTYPE_SET(1) | + HFA384x_TX_TXEX_SET(1) | HFA384x_TX_TXOK_SET(1) ); + txdesc.frame_control = host2ieee16( WLAN_SET_FC_FTYPE(WLAN_FTYPE_DATA) | + WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_DATAONLY) | + WLAN_SET_FC_TODS(1) ); + memcpy(txdesc.address1, hw->bssid, WLAN_ADDR_LEN); + memcpy(txdesc.address2, nic->node_addr, WLAN_ADDR_LEN); + memcpy(txdesc.address3, d, WLAN_ADDR_LEN); + txdesc.data_len = host2hfa384x_16( sizeof(txdesc) + sizeof(p80211hdr) + s ); + /* Set up SNAP header */ + /* Let OUI default to RFC1042 (0x000000) */ + p80211hdr.snap.type = htons(t); + + /* Copy txdesc, p80211hdr and payload parts to FID */ + result = hfa384x_copy_to_bap(hw, fid, 0, &txdesc, sizeof(txdesc)); + if ( result ) return; /* fail */ + result = hfa384x_copy_to_bap( hw, fid, sizeof(txdesc), &p80211hdr, sizeof(p80211hdr) ); + if ( result ) return; /* fail */ + result = hfa384x_copy_to_bap( hw, fid, sizeof(txdesc) + sizeof(p80211hdr), (UINT8*)p, s ); + if ( result ) return; /* fail */ + + /* Issue Tx command */ + result = hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_TX), fid, 0, 0); + if ( result != 0 ) { + printf("hfa384x: Transmit failed with result %#hx.\n", result); + return; + } + + /* Wait for transmit completion (or exception) */ + result = hfa384x_wait_for_event(hw, HFA384x_EVSTAT_TXEXC | HFA384x_EVSTAT_TX, HFA384x_EVACK_INFO, + 200, 500, "Tx to complete\n" ); + if ( !result ) return; /* timeout failure */ + if ( HFA384x_EVSTAT_ISTXEXC(result) ) { + fid = hfa384x_getreg(hw, HFA384x_TXCOMPLFID); + printf ( "Tx exception occurred with fid %#hx\n", fid ); + result = hfa384x_copy_from_bap(hw, fid, 0, &status, sizeof(status)); + if ( result ) return; /* fail */ + printf("hfa384x: Tx error occurred (status %#hx):\n", status); + if ( HFA384x_TXSTATUS_ISACKERR(status) ) { printf(" ...acknowledgement error\n"); } + if ( HFA384x_TXSTATUS_ISFORMERR(status) ) { printf(" ...format error\n"); } + if ( HFA384x_TXSTATUS_ISDISCON(status) ) { printf(" ...disconnected error\n"); } + if ( HFA384x_TXSTATUS_ISAGEDERR(status) ) { printf(" ...AGED error\n"); } + if ( HFA384x_TXSTATUS_ISRETRYERR(status) ) { printf(" ...retry error\n"); } + return; /* fail */ + } +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void prism2_disable ( struct nic *nic __unused ) { + /* put the card in its initial state */ +} + +/************************************************************************** +IRQ - Enable, Disable, or Force interrupts +***************************************************************************/ +static void prism2_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + +/************************************************************************** +Operations table +***************************************************************************/ +static struct nic_operations prism2_operations = { + .connect = dummy_connect, + .poll = prism2_poll, + .transmit = prism2_transmit, + .irq = prism2_irq, +}; + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +You should omit the last argument struct pci_device * for a non-PCI NIC +***************************************************************************/ +static int prism2_probe ( struct nic *nic, hfa384x_t *hw ) { + int result; + UINT16 tmp16 = 0; + UINT16 infofid; + hfa384x_InfFrame_t inf; + char ssid[HFA384x_RID_CNFDESIREDSSID_LEN]; + int info_count = 0; + + nic->irqno = 0; + + /* Initialize card */ + result = hfa384x_docmd_wait(hw, HFA384x_CMDCODE_INIT, 0,0,0); /* Send initialize command */ + if ( result ) printf ( "Initialize command returned %#hx\n", result ); + hfa384x_setreg(hw, 0, HFA384x_INTEN); /* Disable interrupts */ + hfa384x_setreg(hw, 0xffff, HFA384x_EVACK); /* Acknowledge any spurious events */ + + DBG ( "MAC address %s\n", eth_ntoa ( nic->node_addr ) ); + + /* Retrieve MAC address (and fill out nic->node_addr) */ + hfa384x_drvr_getconfig ( hw, HFA384x_RID_CNFOWNMACADDR, nic->node_addr, HFA384x_RID_CNFOWNMACADDR_LEN ); + + /* Prepare card for autojoin */ + /* This procedure is reverse-engineered from a register-level trace of the Linux driver's join process */ + tmp16 = WLAN_DATA_MAXLEN; /* Set maximum data length */ + result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFMAXDATALEN, &tmp16); + if ( result ) printf ( "Set Max Data Length command returned %#hx\n", result ); + tmp16 = 0x000f; /* Set transmit rate(?) */ + result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_TXRATECNTL, &tmp16); + if ( result ) printf ( "Set Transmit Rate command returned %#hx\n", result ); + tmp16 = HFA384x_CNFAUTHENTICATION_OPENSYSTEM; /* Set authentication type to OpenSystem */ + result = hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFAUTHENTICATION, &tmp16); + if ( result ) printf ( "Set Authentication Type command returned %#hx\n", result ); + /* Set SSID */ + memset(ssid, 0, HFA384x_RID_CNFDESIREDSSID_LEN); + for ( tmp16=0; tmp16 MAX_JOIN_INFO_COUNT ) { + printf ( "Too many failed attempts - aborting\n" ); + return 0; + } + + /* Wait for info frame to indicate link status */ + if ( sizeof(hardcoded_ssid) == 1 ) { + /* Empty SSID => join to any SSID */ + printf ( "Attempting to autojoin to any available access point (attempt %d)...", info_count ); + } else { + printf ( "Attempting to autojoin to SSID %s (attempt %d)...", &ssid[2], info_count ); + } + + if ( !hfa384x_wait_for_event(hw, HFA384x_EVSTAT_INFO, 0, 1000, 2000, "Info event" ) ) return 0; + printf("done\n"); + infofid = hfa384x_getreg(hw, HFA384x_INFOFID); + /* Retrieve the length */ + result = hfa384x_copy_from_bap( hw, infofid, 0, &inf.framelen, sizeof(UINT16)); + if ( result ) return 0; /* fail */ + inf.framelen = hfa384x2host_16(inf.framelen); + /* Retrieve the rest */ + result = hfa384x_copy_from_bap( hw, infofid, sizeof(UINT16), + &(inf.infotype), inf.framelen * sizeof(UINT16)); + if ( result ) return 0; /* fail */ + if ( inf.infotype != HFA384x_IT_LINKSTATUS ) { + /* Not a Link Status info frame: die */ + printf ( "Unexpected info frame type %#hx (not LinkStatus type)\n", inf.infotype ); + return 0; + } + inf.info.linkstatus.linkstatus = hfa384x2host_16(inf.info.linkstatus.linkstatus); + if ( inf.info.linkstatus.linkstatus != HFA384x_LINK_CONNECTED ) { + /* Link not connected - retry */ + printf ( "Link not connected (status %#hx)\n", inf.info.linkstatus.linkstatus ); + } + } while ( inf.info.linkstatus.linkstatus != HFA384x_LINK_CONNECTED ); + + /* Retrieve BSSID and print Connected message */ + result = hfa384x_drvr_getconfig(hw, HFA384x_RID_CURRENTBSSID, hw->bssid, WLAN_BSSID_LEN); + + DBG ( "Link connected (BSSID %s - ", eth_ntoa ( hw->bssid ) ); + DBG ( " MAC address %s)\n", eth_ntoa (nic->node_addr ) ); + + /* point to NIC specific routines */ + nic->nic_op = &prism2_operations; + return 1; +} + diff --git a/gpxe/src/drivers/net/prism2_pci.c b/gpxe/src/drivers/net/prism2_pci.c new file mode 100644 index 0000000..eda7bf5 --- /dev/null +++ b/gpxe/src/drivers/net/prism2_pci.c @@ -0,0 +1,56 @@ +/************************************************************************** +Etherboot - BOOTP/TFTP Bootstrap Program +Prism2 NIC driver for Etherboot +Wrapper for prism2_pci + +Written by Michael Brown of Fen Systems Ltd +$Id$ +***************************************************************************/ + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, or (at + * your option) any later version. + */ + +#include +#include + +#define WLAN_HOSTIF WLAN_PCI +#include "prism2.c" + +static int prism2_pci_probe ( struct nic *nic, struct pci_device *pci ) { + hfa384x_t *hw = &hw_global; + + printf ( "Prism2.5 has registers at %#lx\n", pci->membase ); + hw->membase = ioremap ( pci->membase, 0x100 ); + + nic->ioaddr = pci->membase; + nic->irqno = 0; + + return prism2_probe ( nic, hw ); +} + +static void prism2_pci_disable ( struct nic *nic ) { + prism2_disable ( nic ); +} + +static struct pci_device_id prism2_pci_nics[] = { +PCI_ROM(0x1260, 0x3873, "prism2_pci", "Harris Semiconductor Prism2.5 clone"), +PCI_ROM(0x1260, 0x3873, "hwp01170", "ActionTec HWP01170"), +PCI_ROM(0x1260, 0x3873, "dwl520", "DLink DWL-520"), +}; + +PCI_DRIVER ( prism2_pci_driver, prism2_pci_nics, PCI_NO_CLASS ); + +DRIVER ( "Prism2/PCI", nic_driver, pci_driver, prism2_pci_driver, + prism2_pci_probe, prism2_pci_disable ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/prism2_plx.c b/gpxe/src/drivers/net/prism2_plx.c new file mode 100644 index 0000000..a10eef2 --- /dev/null +++ b/gpxe/src/drivers/net/prism2_plx.c @@ -0,0 +1,120 @@ +/************************************************************************** +Etherboot - BOOTP/TFTP Bootstrap Program +Prism2 NIC driver for Etherboot +Wrapper for prism2_plx + +Written by Michael Brown of Fen Systems Ltd +$Id$ +***************************************************************************/ + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, or (at + * your option) any later version. + */ + +#include +#include + +#define WLAN_HOSTIF WLAN_PLX +#include "prism2.c" + +/* + * Find PLX card. Prints out information strings from PCMCIA CIS as visual + * confirmation of presence of card. + * + * Arguments: + * hw device structure to be filled in + * p PCI device structure + * + * Returns: + * 1 Success + */ +static int prism2_find_plx ( hfa384x_t *hw, struct pci_device *p ) +{ + int found = 0; + uint32_t plx_lcr = 0; /* PLX9052 Local Configuration Register Base (I/O) */ + uint32_t attr_mem = 0; /* Prism2 Attribute Memory Base */ + uint32_t iobase = 0; /* Prism2 I/O Base */ + unsigned char *cis_tpl = NULL; + unsigned char *cis_string; + + /* Obtain all memory and IO base addresses */ + pci_read_config_dword( p, PLX_LOCAL_CONFIG_REGISTER_BASE, &plx_lcr); + plx_lcr &= PCI_BASE_ADDRESS_IO_MASK; + pci_read_config_dword( p, PRISM2_PLX_ATTR_MEM_BASE, &attr_mem); + pci_read_config_dword( p, PRISM2_PLX_IO_BASE, &iobase); + iobase &= PCI_BASE_ADDRESS_IO_MASK; + + /* Fill out hw structure */ + hw->iobase = iobase; + printf ( "PLX9052 has local config registers at %#x\n", plx_lcr ); + printf ( "Prism2 has attribute memory at %#x and I/O base at %#x\n", attr_mem, iobase ); + + /* Search for CIS strings */ + printf ( "Searching for PCMCIA card...\n" ); + cis_tpl = bus_to_virt(attr_mem); + while ( *cis_tpl != CISTPL_END ) { + if ( *cis_tpl == CISTPL_VERS_1 ) { + /* CISTPL_VERS_1 contains some nice text strings */ + printf ( "...found " ); + found = 1; + cis_string = cis_tpl + CISTPL_VERS_1_STR_OFF; + while ( ! ( ( *cis_string == 0 ) && ( *(cis_string+CIS_STEP) == 0 ) ) ) { + printf ( "%c", *cis_string == 0 ? ' ' : *cis_string ); + cis_string += CIS_STEP; + } + printf ( "\n" ); + } + /* printf ( "CIS tuple type %#hhx, length %#hhx\n", *cis_tpl, *(cis_tpl+CISTPL_LEN_OFF) ); */ + cis_tpl += CISTPL_HEADER_LEN + CIS_STEP * ( *(cis_tpl+CISTPL_LEN_OFF) ); + } + if ( found == 0 ) { + printf ( "...nothing found\n" ); + } + ((unsigned char *)bus_to_virt(attr_mem))[COR_OFFSET] = COR_VALUE; /* Write COR to enable PC card */ + return found; +} + +static int prism2_plx_probe ( struct nic *nic, struct pci_device *pci ) { + hfa384x_t *hw = &hw_global; + + /* Find and intialise PLX Prism2 card */ + if ( ! prism2_find_plx ( hw, pci ) ) return 0; + nic->ioaddr = hw->iobase; + nic->irqno = 0; + return prism2_probe ( nic, hw ); +} + +static void prism2_plx_disable ( struct nic *nic ) { + prism2_disable ( nic ); +} + +static struct pci_device_id prism2_plx_nics[] = { +PCI_ROM(0x1385, 0x4100, "ma301", "Netgear MA301"), +PCI_ROM(0x10b7, 0x7770, "3c-airconnect", "3Com AirConnect"), +PCI_ROM(0x111a, 0x1023, "ss1023", "Siemens SpeedStream SS1023"), +PCI_ROM(0x15e8, 0x0130, "correga", "Correga"), +PCI_ROM(0x1638, 0x1100, "smc2602w", "SMC EZConnect SMC2602W"), /* or Eumitcom PCI WL11000, Addtron AWA-100 */ +PCI_ROM(0x16ab, 0x1100, "gl24110p", "Global Sun Tech GL24110P"), +PCI_ROM(0x16ab, 0x1101, "16ab-1101", "Unknown"), +PCI_ROM(0x16ab, 0x1102, "wdt11", "Linksys WDT11"), +PCI_ROM(0x16ec, 0x3685, "usr2415", "USR 2415"), +PCI_ROM(0xec80, 0xec00, "f5d6000", "Belkin F5D6000"), +PCI_ROM(0x126c, 0x8030, "emobility", "Nortel emobility"), +}; + +PCI_DRIVER ( prism2_plx_driver, prism2_plx_nics, PCI_NO_CLASS ); + + +DRIVER ( "Prism2/PLX", nic_driver, pci_driver, prism2_plx_driver, + prism2_plx_probe, prism2_plx_disable ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/r8169.c b/gpxe/src/drivers/net/r8169.c new file mode 100644 index 0000000..4315f47 --- /dev/null +++ b/gpxe/src/drivers/net/r8169.c @@ -0,0 +1,2282 @@ +/* + * Copyright (c) 2008 Marty Connor + * Copyright (c) 2008 Entity Cyber, 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 any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This driver is based on rtl8169 data sheets and work by: + * + * Copyright (c) 2002 ShuChen + * Copyright (c) 2003 - 2007 Francois Romieu + * Copyright (c) a lot of people too. Please respect their work. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "r8169.h" + +/*** Low level hardware routines ***/ + +static void mdio_write(void *ioaddr, int reg_addr, int value) +{ + int i; + + DBGP ( "mdio_write\n" ); + + RTL_W32(PHYAR, 0x80000000 | (reg_addr & 0x1f) << 16 | (value & 0xffff)); + + for (i = 20; i > 0; i--) { + /* + * Check if the RTL8169 has completed writing to the specified + * MII register. + */ + if (!(RTL_R32(PHYAR) & 0x80000000)) + break; + udelay(25); + } +} + +static int mdio_read(void *ioaddr, int reg_addr) +{ + int i, value = -1; + + DBGP ( "mdio_read\n" ); + + RTL_W32(PHYAR, 0x0 | (reg_addr & 0x1f) << 16); + + for (i = 20; i > 0; i--) { + /* + * Check if the RTL8169 has completed retrieving data from + * the specified MII register. + */ + if (RTL_R32(PHYAR) & 0x80000000) { + value = RTL_R32(PHYAR) & 0xffff; + break; + } + udelay(25); + } + return value; +} + +static void mdio_patch(void *ioaddr, int reg_addr, int value) +{ + DBGP ( "mdio_patch\n" ); + + mdio_write(ioaddr, reg_addr, mdio_read(ioaddr, reg_addr) | value); +} + +static void rtl_ephy_write(void *ioaddr, int reg_addr, int value) +{ + unsigned int i; + + DBGP ( "rtl_ephy_write\n" ); + + RTL_W32(EPHYAR, EPHYAR_WRITE_CMD | (value & EPHYAR_DATA_MASK) | + (reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT); + + for (i = 0; i < 100; i++) { + if (!(RTL_R32(EPHYAR) & EPHYAR_FLAG)) + break; + udelay(10); + } +} + +static u16 rtl_ephy_read(void *ioaddr, int reg_addr) +{ + u16 value = 0xffff; + unsigned int i; + + DBGP ( "rtl_ephy_read\n" ); + + RTL_W32(EPHYAR, (reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT); + + for (i = 0; i < 100; i++) { + if (RTL_R32(EPHYAR) & EPHYAR_FLAG) { + value = RTL_R32(EPHYAR) & EPHYAR_DATA_MASK; + break; + } + udelay(10); + } + + return value; +} + +static void rtl_csi_write(void *ioaddr, int addr, int value) +{ + unsigned int i; + + DBGP ( "rtl_csi_write\n" ); + + RTL_W32(CSIDR, value); + RTL_W32(CSIAR, CSIAR_WRITE_CMD | (addr & CSIAR_ADDR_MASK) | + CSIAR_BYTE_ENABLE << CSIAR_BYTE_ENABLE_SHIFT); + + for (i = 0; i < 100; i++) { + if (!(RTL_R32(CSIAR) & CSIAR_FLAG)) + break; + udelay(10); + } +} + +static u32 rtl_csi_read(void *ioaddr, int addr) +{ + u32 value = ~0x00; + unsigned int i; + + DBGP ( "rtl_csi_read\n" ); + + RTL_W32(CSIAR, (addr & CSIAR_ADDR_MASK) | + CSIAR_BYTE_ENABLE << CSIAR_BYTE_ENABLE_SHIFT); + + for (i = 0; i < 100; i++) { + if (RTL_R32(CSIAR) & CSIAR_FLAG) { + value = RTL_R32(CSIDR); + break; + } + udelay(10); + } + + return value; +} + +static void rtl8169_irq_mask_and_ack(void *ioaddr) +{ + DBGP ( "rtl8169_irq_mask_and_ack\n" ); + + RTL_W16(IntrMask, 0x0000); + + RTL_W16(IntrStatus, 0xffff); +} + +static unsigned int rtl8169_tbi_reset_pending(void *ioaddr) +{ + DBGP ( "rtl8169_tbi_reset_pending\n" ); + + return RTL_R32(TBICSR) & TBIReset; +} + +static unsigned int rtl8169_xmii_reset_pending(void *ioaddr) +{ + DBGP ( "rtl8169_xmii_reset_pending\n" ); + + return mdio_read(ioaddr, MII_BMCR) & BMCR_RESET; +} + +static unsigned int rtl8169_tbi_link_ok(void *ioaddr) +{ + DBGP ( "rtl8169_tbi_link_ok\n" ); + + return RTL_R32(TBICSR) & TBILinkOk; +} + +static unsigned int rtl8169_xmii_link_ok(void *ioaddr) +{ + DBGP ( "rtl8169_xmii_link_ok\n" ); + + return RTL_R8(PHYstatus) & LinkStatus; +} + +static void rtl8169_tbi_reset_enable(void *ioaddr) +{ + DBGP ( "rtl8169_tbi_reset_enable\n" ); + + RTL_W32(TBICSR, RTL_R32(TBICSR) | TBIReset); +} + +static void rtl8169_xmii_reset_enable(void *ioaddr) +{ + unsigned int val; + + DBGP ( "rtl8169_xmii_reset_enable\n" ); + + val = mdio_read(ioaddr, MII_BMCR) | BMCR_RESET; + mdio_write(ioaddr, MII_BMCR, val & 0xffff); +} + +static int rtl8169_set_speed_tbi(struct net_device *dev, + u8 autoneg, u16 speed, u8 duplex) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void *ioaddr = tp->mmio_addr; + int ret = 0; + u32 reg; + + DBGP ( "rtl8169_set_speed_tbi\n" ); + + reg = RTL_R32(TBICSR); + if ((autoneg == AUTONEG_DISABLE) && (speed == SPEED_1000) && + (duplex == DUPLEX_FULL)) { + RTL_W32(TBICSR, reg & ~(TBINwEnable | TBINwRestart)); + } else if (autoneg == AUTONEG_ENABLE) + RTL_W32(TBICSR, reg | TBINwEnable | TBINwRestart); + else { + DBG ( "incorrect speed setting refused in TBI mode\n" ); + ret = -EOPNOTSUPP; + } + return ret; +} + +static int rtl8169_set_speed_xmii(struct net_device *dev, + u8 autoneg, u16 speed, u8 duplex) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void *ioaddr = tp->mmio_addr; + int auto_nego, giga_ctrl; + + DBGP ( "rtl8169_set_speed_xmii\n" ); + + auto_nego = mdio_read(ioaddr, MII_ADVERTISE); + auto_nego &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL | + ADVERTISE_100HALF | ADVERTISE_100FULL); + giga_ctrl = mdio_read(ioaddr, MII_CTRL1000); + giga_ctrl &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); + + if (autoneg == AUTONEG_ENABLE) { + auto_nego |= (ADVERTISE_10HALF | ADVERTISE_10FULL | + ADVERTISE_100HALF | ADVERTISE_100FULL); + giga_ctrl |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; + } else { + if (speed == SPEED_10) + auto_nego |= ADVERTISE_10HALF | ADVERTISE_10FULL; + else if (speed == SPEED_100) + auto_nego |= ADVERTISE_100HALF | ADVERTISE_100FULL; + else if (speed == SPEED_1000) + giga_ctrl |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; + + if (duplex == DUPLEX_HALF) + auto_nego &= ~(ADVERTISE_10FULL | ADVERTISE_100FULL); + + if (duplex == DUPLEX_FULL) + auto_nego &= ~(ADVERTISE_10HALF | ADVERTISE_100HALF); + + /* This tweak comes straight from Realtek's driver. */ + if ((speed == SPEED_100) && (duplex == DUPLEX_HALF) && + ((tp->mac_version == RTL_GIGA_MAC_VER_13) || + (tp->mac_version == RTL_GIGA_MAC_VER_16))) { + auto_nego = ADVERTISE_100HALF | ADVERTISE_CSMA; + } + } + + /* The 8100e/8101e/8102e do Fast Ethernet only. */ + if ((tp->mac_version == RTL_GIGA_MAC_VER_07) || + (tp->mac_version == RTL_GIGA_MAC_VER_08) || + (tp->mac_version == RTL_GIGA_MAC_VER_09) || + (tp->mac_version == RTL_GIGA_MAC_VER_10) || + (tp->mac_version == RTL_GIGA_MAC_VER_13) || + (tp->mac_version == RTL_GIGA_MAC_VER_14) || + (tp->mac_version == RTL_GIGA_MAC_VER_15) || + (tp->mac_version == RTL_GIGA_MAC_VER_16)) { + if ((giga_ctrl & (ADVERTISE_1000FULL | ADVERTISE_1000HALF))) { + DBG ( "PHY does not support 1000Mbps.\n" ); + } + giga_ctrl &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); + } + + auto_nego |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; + + if ((tp->mac_version == RTL_GIGA_MAC_VER_11) || + (tp->mac_version == RTL_GIGA_MAC_VER_12) || + (tp->mac_version >= RTL_GIGA_MAC_VER_17)) { + /* + * Wake up the PHY. + * Vendor specific (0x1f) and reserved (0x0e) MII registers. + */ + mdio_write(ioaddr, 0x1f, 0x0000); + mdio_write(ioaddr, 0x0e, 0x0000); + } + + tp->phy_auto_nego_reg = auto_nego; + tp->phy_1000_ctrl_reg = giga_ctrl; + + mdio_write(ioaddr, MII_ADVERTISE, auto_nego); + mdio_write(ioaddr, MII_CTRL1000, giga_ctrl); + mdio_write(ioaddr, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART); + return 0; +} + +static int rtl8169_set_speed(struct net_device *dev, + u8 autoneg, u16 speed, u8 duplex) +{ + struct rtl8169_private *tp = netdev_priv(dev); + int ret; + + DBGP ( "rtl8169_set_speed\n" ); + + ret = tp->set_speed(dev, autoneg, speed, duplex); + + return ret; +} + +static void rtl8169_write_gmii_reg_bit(void *ioaddr, int reg, + int bitnum, int bitval) +{ + int val; + + DBGP ( "rtl8169_write_gmii_reg_bit\n" ); + + val = mdio_read(ioaddr, reg); + val = (bitval == 1) ? + val | (bitval << bitnum) : val & ~(0x0001 << bitnum); + mdio_write(ioaddr, reg, val & 0xffff); +} + +static void rtl8169_get_mac_version(struct rtl8169_private *tp, + void *ioaddr) +{ + /* + * The driver currently handles the 8168Bf and the 8168Be identically + * but they can be identified more specifically through the test below + * if needed: + * + * (RTL_R32(TxConfig) & 0x700000) == 0x500000 ? 8168Bf : 8168Be + * + * Same thing for the 8101Eb and the 8101Ec: + * + * (RTL_R32(TxConfig) & 0x700000) == 0x200000 ? 8101Eb : 8101Ec + */ + const struct { + u32 mask; + u32 val; + int mac_version; + } mac_info[] = { + /* 8168D family. */ + { 0x7c800000, 0x28000000, RTL_GIGA_MAC_VER_25 }, + + /* 8168C family. */ + { 0x7cf00000, 0x3ca00000, RTL_GIGA_MAC_VER_24 }, + { 0x7cf00000, 0x3c900000, RTL_GIGA_MAC_VER_23 }, + { 0x7cf00000, 0x3c800000, RTL_GIGA_MAC_VER_18 }, + { 0x7c800000, 0x3c800000, RTL_GIGA_MAC_VER_24 }, + { 0x7cf00000, 0x3c000000, RTL_GIGA_MAC_VER_19 }, + { 0x7cf00000, 0x3c200000, RTL_GIGA_MAC_VER_20 }, + { 0x7cf00000, 0x3c300000, RTL_GIGA_MAC_VER_21 }, + { 0x7cf00000, 0x3c400000, RTL_GIGA_MAC_VER_22 }, + { 0x7c800000, 0x3c000000, RTL_GIGA_MAC_VER_22 }, + + /* 8168B family. */ + { 0x7cf00000, 0x38000000, RTL_GIGA_MAC_VER_12 }, + { 0x7cf00000, 0x38500000, RTL_GIGA_MAC_VER_17 }, + { 0x7c800000, 0x38000000, RTL_GIGA_MAC_VER_17 }, + { 0x7c800000, 0x30000000, RTL_GIGA_MAC_VER_11 }, + + /* 8101 family. */ + { 0x7cf00000, 0x34a00000, RTL_GIGA_MAC_VER_09 }, + { 0x7cf00000, 0x24a00000, RTL_GIGA_MAC_VER_09 }, + { 0x7cf00000, 0x34900000, RTL_GIGA_MAC_VER_08 }, + { 0x7cf00000, 0x24900000, RTL_GIGA_MAC_VER_08 }, + { 0x7cf00000, 0x34800000, RTL_GIGA_MAC_VER_07 }, + { 0x7cf00000, 0x24800000, RTL_GIGA_MAC_VER_07 }, + { 0x7cf00000, 0x34000000, RTL_GIGA_MAC_VER_13 }, + { 0x7cf00000, 0x34300000, RTL_GIGA_MAC_VER_10 }, + { 0x7cf00000, 0x34200000, RTL_GIGA_MAC_VER_16 }, + { 0x7c800000, 0x34800000, RTL_GIGA_MAC_VER_09 }, + { 0x7c800000, 0x24800000, RTL_GIGA_MAC_VER_09 }, + { 0x7c800000, 0x34000000, RTL_GIGA_MAC_VER_16 }, + /* FIXME: where did these entries come from ? -- FR */ + { 0xfc800000, 0x38800000, RTL_GIGA_MAC_VER_15 }, + { 0xfc800000, 0x30800000, RTL_GIGA_MAC_VER_14 }, + + /* 8110 family. */ + { 0xfc800000, 0x98000000, RTL_GIGA_MAC_VER_06 }, + { 0xfc800000, 0x18000000, RTL_GIGA_MAC_VER_05 }, + { 0xfc800000, 0x10000000, RTL_GIGA_MAC_VER_04 }, + { 0xfc800000, 0x04000000, RTL_GIGA_MAC_VER_03 }, + { 0xfc800000, 0x00800000, RTL_GIGA_MAC_VER_02 }, + { 0xfc800000, 0x00000000, RTL_GIGA_MAC_VER_01 }, + + { 0x00000000, 0x00000000, RTL_GIGA_MAC_VER_01 } /* Catch-all */ + }, *p = mac_info; + u32 reg; + + DBGP ( "rtl8169_get_mac_version\n" ); + + reg = RTL_R32(TxConfig); + while ((reg & p->mask) != p->val) + p++; + tp->mac_version = p->mac_version; + + DBG ( "tp->mac_version = %d\n", tp->mac_version ); + + if (p->mask == 0x00000000) { + DBG ( "unknown MAC (%08x)\n", reg ); + } +} + +struct phy_reg { + u16 reg; + u16 val; +}; + +static void rtl_phy_write(void *ioaddr, struct phy_reg *regs, int len) +{ + DBGP ( "rtl_phy_write\n" ); + + while (len-- > 0) { + mdio_write(ioaddr, regs->reg, regs->val); + regs++; + } +} + +static void rtl8169s_hw_phy_config(void *ioaddr) +{ + struct { + u16 regs[5]; /* Beware of bit-sign propagation */ + } phy_magic[5] = { { + { 0x0000, //w 4 15 12 0 + 0x00a1, //w 3 15 0 00a1 + 0x0008, //w 2 15 0 0008 + 0x1020, //w 1 15 0 1020 + 0x1000 } },{ //w 0 15 0 1000 + { 0x7000, //w 4 15 12 7 + 0xff41, //w 3 15 0 ff41 + 0xde60, //w 2 15 0 de60 + 0x0140, //w 1 15 0 0140 + 0x0077 } },{ //w 0 15 0 0077 + { 0xa000, //w 4 15 12 a + 0xdf01, //w 3 15 0 df01 + 0xdf20, //w 2 15 0 df20 + 0xff95, //w 1 15 0 ff95 + 0xfa00 } },{ //w 0 15 0 fa00 + { 0xb000, //w 4 15 12 b + 0xff41, //w 3 15 0 ff41 + 0xde20, //w 2 15 0 de20 + 0x0140, //w 1 15 0 0140 + 0x00bb } },{ //w 0 15 0 00bb + { 0xf000, //w 4 15 12 f + 0xdf01, //w 3 15 0 df01 + 0xdf20, //w 2 15 0 df20 + 0xff95, //w 1 15 0 ff95 + 0xbf00 } //w 0 15 0 bf00 + } + }, *p = phy_magic; + unsigned int i; + + DBGP ( "rtl8169s_hw_phy_config\n" ); + + mdio_write(ioaddr, 0x1f, 0x0001); //w 31 2 0 1 + mdio_write(ioaddr, 0x15, 0x1000); //w 21 15 0 1000 + mdio_write(ioaddr, 0x18, 0x65c7); //w 24 15 0 65c7 + rtl8169_write_gmii_reg_bit(ioaddr, 4, 11, 0); //w 4 11 11 0 + + for (i = 0; i < ARRAY_SIZE(phy_magic); i++, p++) { + int val, pos = 4; + + val = (mdio_read(ioaddr, pos) & 0x0fff) | (p->regs[0] & 0xffff); + mdio_write(ioaddr, pos, val); + while (--pos >= 0) + mdio_write(ioaddr, pos, p->regs[4 - pos] & 0xffff); + rtl8169_write_gmii_reg_bit(ioaddr, 4, 11, 1); //w 4 11 11 1 + rtl8169_write_gmii_reg_bit(ioaddr, 4, 11, 0); //w 4 11 11 0 + } + mdio_write(ioaddr, 0x1f, 0x0000); //w 31 2 0 0 +} + +static void rtl8169sb_hw_phy_config(void *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0002 }, + { 0x01, 0x90d0 }, + { 0x1f, 0x0000 } + }; + + DBGP ( "rtl8169sb_hw_phy_config\n" ); + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); +} + +static void rtl8168bb_hw_phy_config(void *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x10, 0xf41b }, + { 0x1f, 0x0000 } + }; + + mdio_write(ioaddr, 0x1f, 0x0001); + mdio_patch(ioaddr, 0x16, 1 << 0); + + DBGP ( "rtl8168bb_hw_phy_config\n" ); + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); +} + +static void rtl8168bef_hw_phy_config(void *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x10, 0xf41b }, + { 0x1f, 0x0000 } + }; + + DBGP ( "rtl8168bef_hw_phy_config\n" ); + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); +} + +static void rtl8168cp_1_hw_phy_config(void *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0000 }, + { 0x1d, 0x0f00 }, + { 0x1f, 0x0002 }, + { 0x0c, 0x1ec8 }, + { 0x1f, 0x0000 } + }; + + DBGP ( "rtl8168cp_1_hw_phy_config\n" ); + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); +} + +static void rtl8168cp_2_hw_phy_config(void *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x1d, 0x3d98 }, + { 0x1f, 0x0000 } + }; + + DBGP ( "rtl8168cp_2_hw_phy_config\n" ); + + mdio_write(ioaddr, 0x1f, 0x0000); + mdio_patch(ioaddr, 0x14, 1 << 5); + mdio_patch(ioaddr, 0x0d, 1 << 5); + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); +} + +static void rtl8168c_1_hw_phy_config(void *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x12, 0x2300 }, + { 0x1f, 0x0002 }, + { 0x00, 0x88d4 }, + { 0x01, 0x82b1 }, + { 0x03, 0x7002 }, + { 0x08, 0x9e30 }, + { 0x09, 0x01f0 }, + { 0x0a, 0x5500 }, + { 0x0c, 0x00c8 }, + { 0x1f, 0x0003 }, + { 0x12, 0xc096 }, + { 0x16, 0x000a }, + { 0x1f, 0x0000 }, + { 0x1f, 0x0000 }, + { 0x09, 0x2000 }, + { 0x09, 0x0000 } + }; + + DBGP ( "rtl8168c_1_hw_phy_config\n" ); + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); + + mdio_patch(ioaddr, 0x14, 1 << 5); + mdio_patch(ioaddr, 0x0d, 1 << 5); + mdio_write(ioaddr, 0x1f, 0x0000); +} + +static void rtl8168c_2_hw_phy_config(void *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x12, 0x2300 }, + { 0x03, 0x802f }, + { 0x02, 0x4f02 }, + { 0x01, 0x0409 }, + { 0x00, 0xf099 }, + { 0x04, 0x9800 }, + { 0x04, 0x9000 }, + { 0x1d, 0x3d98 }, + { 0x1f, 0x0002 }, + { 0x0c, 0x7eb8 }, + { 0x06, 0x0761 }, + { 0x1f, 0x0003 }, + { 0x16, 0x0f0a }, + { 0x1f, 0x0000 } + }; + + DBGP ( "rtl8168c_2_hw_phy_config\n" ); + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); + + mdio_patch(ioaddr, 0x16, 1 << 0); + mdio_patch(ioaddr, 0x14, 1 << 5); + mdio_patch(ioaddr, 0x0d, 1 << 5); + mdio_write(ioaddr, 0x1f, 0x0000); +} + +static void rtl8168c_3_hw_phy_config(void *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x12, 0x2300 }, + { 0x1d, 0x3d98 }, + { 0x1f, 0x0002 }, + { 0x0c, 0x7eb8 }, + { 0x06, 0x5461 }, + { 0x1f, 0x0003 }, + { 0x16, 0x0f0a }, + { 0x1f, 0x0000 } + }; + + DBGP ( "rtl8168c_3_hw_phy_config\n" ); + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); + + mdio_patch(ioaddr, 0x16, 1 << 0); + mdio_patch(ioaddr, 0x14, 1 << 5); + mdio_patch(ioaddr, 0x0d, 1 << 5); + mdio_write(ioaddr, 0x1f, 0x0000); +} + +static void rtl8168c_4_hw_phy_config(void *ioaddr) +{ + DBGP ( "rtl8168c_4_hw_phy_config\n" ); + + rtl8168c_3_hw_phy_config(ioaddr); +} + +static void rtl8168d_hw_phy_config(void *ioaddr) +{ + struct phy_reg phy_reg_init_0[] = { + { 0x1f, 0x0001 }, + { 0x09, 0x2770 }, + { 0x08, 0x04d0 }, + { 0x0b, 0xad15 }, + { 0x0c, 0x5bf0 }, + { 0x1c, 0xf101 }, + { 0x1f, 0x0003 }, + { 0x14, 0x94d7 }, + { 0x12, 0xf4d6 }, + { 0x09, 0xca0f }, + { 0x1f, 0x0002 }, + { 0x0b, 0x0b10 }, + { 0x0c, 0xd1f7 }, + { 0x1f, 0x0002 }, + { 0x06, 0x5461 }, + { 0x1f, 0x0002 }, + { 0x05, 0x6662 }, + { 0x1f, 0x0000 }, + { 0x14, 0x0060 }, + { 0x1f, 0x0000 }, + { 0x0d, 0xf8a0 }, + { 0x1f, 0x0005 }, + { 0x05, 0xffc2 } + }; + + DBGP ( "rtl8168d_hw_phy_config\n" ); + + rtl_phy_write(ioaddr, phy_reg_init_0, ARRAY_SIZE(phy_reg_init_0)); + + if (mdio_read(ioaddr, 0x06) == 0xc400) { + struct phy_reg phy_reg_init_1[] = { + { 0x1f, 0x0005 }, + { 0x01, 0x0300 }, + { 0x1f, 0x0000 }, + { 0x11, 0x401c }, + { 0x16, 0x4100 }, + { 0x1f, 0x0005 }, + { 0x07, 0x0010 }, + { 0x05, 0x83dc }, + { 0x06, 0x087d }, + { 0x05, 0x8300 }, + { 0x06, 0x0101 }, + { 0x06, 0x05f8 }, + { 0x06, 0xf9fa }, + { 0x06, 0xfbef }, + { 0x06, 0x79e2 }, + { 0x06, 0x835f }, + { 0x06, 0xe0f8 }, + { 0x06, 0x9ae1 }, + { 0x06, 0xf89b }, + { 0x06, 0xef31 }, + { 0x06, 0x3b65 }, + { 0x06, 0xaa07 }, + { 0x06, 0x81e4 }, + { 0x06, 0xf89a }, + { 0x06, 0xe5f8 }, + { 0x06, 0x9baf }, + { 0x06, 0x06ae }, + { 0x05, 0x83dc }, + { 0x06, 0x8300 }, + }; + + rtl_phy_write(ioaddr, phy_reg_init_1, + ARRAY_SIZE(phy_reg_init_1)); + } + + mdio_write(ioaddr, 0x1f, 0x0000); +} + +static void rtl8102e_hw_phy_config(void *ioaddr) +{ + struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0003 }, + { 0x08, 0x441d }, + { 0x01, 0x9100 }, + { 0x1f, 0x0000 } + }; + + DBGP ( "rtl8102e_hw_phy_config\n" ); + + mdio_write(ioaddr, 0x1f, 0x0000); + mdio_patch(ioaddr, 0x11, 1 << 12); + mdio_patch(ioaddr, 0x19, 1 << 13); + + rtl_phy_write(ioaddr, phy_reg_init, ARRAY_SIZE(phy_reg_init)); +} + +static void rtl_hw_phy_config(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void *ioaddr = tp->mmio_addr; + + DBGP ( "rtl_hw_phy_config\n" ); + + DBG ( "mac_version = 0x%02x\n", tp->mac_version ); + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_01: + break; + case RTL_GIGA_MAC_VER_02: + case RTL_GIGA_MAC_VER_03: + rtl8169s_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_04: + rtl8169sb_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_07: + case RTL_GIGA_MAC_VER_08: + case RTL_GIGA_MAC_VER_09: + rtl8102e_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_11: + rtl8168bb_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_12: + rtl8168bef_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_17: + rtl8168bef_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_18: + rtl8168cp_1_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_19: + rtl8168c_1_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_20: + rtl8168c_2_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_21: + rtl8168c_3_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_22: + rtl8168c_4_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_23: + case RTL_GIGA_MAC_VER_24: + rtl8168cp_2_hw_phy_config(ioaddr); + break; + case RTL_GIGA_MAC_VER_25: + rtl8168d_hw_phy_config(ioaddr); + break; + + default: + break; + } +} + +static void rtl8169_phy_reset(struct net_device *dev __unused, + struct rtl8169_private *tp) +{ + void *ioaddr = tp->mmio_addr; + unsigned int i; + + DBGP ( "rtl8169_phy_reset\n" ); + + tp->phy_reset_enable(ioaddr); + for (i = 0; i < 100; i++) { + if (!tp->phy_reset_pending(ioaddr)) + return; + mdelay ( 1 ); + } + DBG ( "PHY reset failed.\n" ); +} + +static void rtl8169_init_phy(struct net_device *dev, struct rtl8169_private *tp) +{ + void *ioaddr = tp->mmio_addr; + + DBGP ( "rtl8169_init_phy\n" ); + + rtl_hw_phy_config(dev); + + if (tp->mac_version <= RTL_GIGA_MAC_VER_06) { + DBG ( "Set MAC Reg C+CR Offset 0x82h = 0x01h\n" ); + RTL_W8(0x82, 0x01); + } + + pci_write_config_byte(tp->pci_dev, PCI_LATENCY_TIMER, 0x40); + + if (tp->mac_version <= RTL_GIGA_MAC_VER_06) + pci_write_config_byte(tp->pci_dev, PCI_CACHE_LINE_SIZE, 0x08); + + if (tp->mac_version == RTL_GIGA_MAC_VER_02) { + DBG ( "Set MAC Reg C+CR Offset 0x82h = 0x01h\n" ); + RTL_W8(0x82, 0x01); + DBG ( "Set PHY Reg 0x0bh = 0x00h\n" ); + mdio_write(ioaddr, 0x0b, 0x0000); //w 0x0b 15 0 0 + } + + rtl8169_phy_reset(dev, tp); + + /* + * rtl8169_set_speed_xmii takes good care of the Fast Ethernet + * only 8101. Don't panic. + */ + rtl8169_set_speed(dev, AUTONEG_ENABLE, SPEED_1000, DUPLEX_FULL); + + if ((RTL_R8(PHYstatus) & TBI_Enable)) + DBG ( "TBI auto-negotiating\n" ); +} + +static const struct rtl_cfg_info { + void (*hw_start)(struct net_device *); + unsigned int region; + unsigned int align; + u16 intr_event; + u16 napi_event; + unsigned features; +} rtl_cfg_infos [] = { + [RTL_CFG_0] = { + .hw_start = rtl_hw_start_8169, + .region = 1, + .align = 0, + .intr_event = SYSErr | LinkChg | RxOverflow | + RxFIFOOver | TxErr | TxOK | RxOK | RxErr, + .napi_event = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow, + .features = RTL_FEATURE_GMII + }, + [RTL_CFG_1] = { + .hw_start = rtl_hw_start_8168, + .region = 2, + .align = 8, + .intr_event = SYSErr | LinkChg | RxOverflow | + TxErr | TxOK | RxOK | RxErr, + .napi_event = TxErr | TxOK | RxOK | RxOverflow, + .features = RTL_FEATURE_GMII + }, + [RTL_CFG_2] = { + .hw_start = rtl_hw_start_8101, + .region = 2, + .align = 8, + .intr_event = SYSErr | LinkChg | RxOverflow | PCSTimeout | + RxFIFOOver | TxErr | TxOK | RxOK | RxErr, + .napi_event = RxFIFOOver | TxErr | TxOK | RxOK | RxOverflow, + } +}; + +static void rtl8169_hw_reset(void *ioaddr) +{ + DBGP ( "rtl8169_hw_reset\n" ); + + /* Disable interrupts */ + rtl8169_irq_mask_and_ack(ioaddr); + + /* Reset the chipset */ + RTL_W8(ChipCmd, CmdReset); + + /* PCI commit */ + RTL_R8(ChipCmd); +} + +static void rtl_set_rx_tx_config_registers(struct rtl8169_private *tp) +{ + void *ioaddr = tp->mmio_addr; + u32 cfg = rtl8169_rx_config; + + DBGP ( "rtl_set_rx_tx_config_registers\n" ); + + cfg |= (RTL_R32(RxConfig) & rtl_chip_info[tp->chipset].RxConfigMask); + RTL_W32(RxConfig, cfg); + + /* Set DMA burst size and Interframe Gap Time */ + RTL_W32(TxConfig, (TX_DMA_BURST << TxDMAShift) | + (InterFrameGap << TxInterFrameGapShift)); +} + +static void rtl_soft_reset ( struct net_device *dev ) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void *ioaddr = tp->mmio_addr; + unsigned int i; + + DBGP ( "rtl_hw_soft_reset\n" ); + + /* Soft reset the chip. */ + RTL_W8(ChipCmd, CmdReset); + + /* Check that the chip has finished the reset. */ + for (i = 0; i < 100; i++) { + if ((RTL_R8(ChipCmd) & CmdReset) == 0) + break; + mdelay ( 1 ); + } + + if ( i == 100 ) { + DBG ( "Reset Failed! (> 100 iterations)\n" ); + } +} + +static void rtl_hw_start ( struct net_device *dev ) +{ + struct rtl8169_private *tp = netdev_priv ( dev ); + + DBGP ( "rtl_hw_start\n" ); + + /* Soft reset NIC */ + rtl_soft_reset ( dev ); + + tp->hw_start ( dev ); +} + +static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp, + void *ioaddr) +{ + DBGP ( "rtl_set_rx_tx_desc_registers\n" ); + + /* + * Magic spell: some iop3xx ARM board needs the TxDescAddrHigh + * register to be written before TxDescAddrLow to work. + * Switching from MMIO to I/O access fixes the issue as well. + */ + RTL_W32 ( TxDescStartAddrHigh, 0 ); + RTL_W32 ( TxDescStartAddrLow, virt_to_bus ( tp->tx_base ) ); + RTL_W32 ( RxDescAddrHigh, 0 ); + RTL_W32 ( RxDescAddrLow, virt_to_bus ( tp->rx_base ) ); +} + +static u16 rtl_rw_cpluscmd(void *ioaddr) +{ + u16 cmd; + + DBGP ( "rtl_rw_cpluscmd\n" ); + + cmd = RTL_R16(CPlusCmd); + RTL_W16(CPlusCmd, cmd); + return cmd; +} + +static void rtl_set_rx_max_size(void *ioaddr) +{ + DBGP ( "rtl_set_rx_max_size\n" ); + + RTL_W16 ( RxMaxSize, RX_BUF_SIZE ); +} + +static void rtl8169_set_magic_reg(void *ioaddr, unsigned mac_version) +{ + struct { + u32 mac_version; + u32 clk; + u32 val; + } cfg2_info [] = { + { RTL_GIGA_MAC_VER_05, PCI_Clock_33MHz, 0x000fff00 }, // 8110SCd + { RTL_GIGA_MAC_VER_05, PCI_Clock_66MHz, 0x000fffff }, + { RTL_GIGA_MAC_VER_06, PCI_Clock_33MHz, 0x00ffff00 }, // 8110SCe + { RTL_GIGA_MAC_VER_06, PCI_Clock_66MHz, 0x00ffffff } + }, *p = cfg2_info; + unsigned int i; + u32 clk; + + DBGP ( "rtl8169_set_magic_reg\n" ); + + clk = RTL_R8(Config2) & PCI_Clock_66MHz; + for (i = 0; i < ARRAY_SIZE(cfg2_info); i++, p++) { + if ((p->mac_version == mac_version) && (p->clk == clk)) { + RTL_W32(0x7c, p->val); + break; + } + } +} + +static void rtl_set_rx_mode ( struct net_device *netdev ) +{ + struct rtl8169_private *tp = netdev_priv ( netdev ); + void *ioaddr = tp->mmio_addr; + u32 tmp; + + DBGP ( "rtl_set_rx_mode\n" ); + + /* Accept all Multicast Packets */ + + RTL_W32 ( MAR0 + 0, 0xffffffff ); + RTL_W32 ( MAR0 + 4, 0xffffffff ); + + tmp = rtl8169_rx_config | AcceptBroadcast | AcceptMulticast | AcceptMyPhys | + ( RTL_R32 ( RxConfig ) & rtl_chip_info[tp->chipset].RxConfigMask ); + + RTL_W32 ( RxConfig, tmp ); +} + +static void rtl_hw_start_8169(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void *ioaddr = tp->mmio_addr; + struct pci_device *pdev = tp->pci_dev; + + DBGP ( "rtl_hw_start_8169\n" ); + + if (tp->mac_version == RTL_GIGA_MAC_VER_05) { + RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) | PCIMulRW); + pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x08); + } + + RTL_W8(Cfg9346, Cfg9346_Unlock); + + if ((tp->mac_version == RTL_GIGA_MAC_VER_01) || + (tp->mac_version == RTL_GIGA_MAC_VER_02) || + (tp->mac_version == RTL_GIGA_MAC_VER_03) || + (tp->mac_version == RTL_GIGA_MAC_VER_04)) + RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb); + + RTL_W8(EarlyTxThres, EarlyTxThld); + + rtl_set_rx_max_size(ioaddr); + + if ((tp->mac_version == RTL_GIGA_MAC_VER_01) || + (tp->mac_version == RTL_GIGA_MAC_VER_02) || + (tp->mac_version == RTL_GIGA_MAC_VER_03) || + (tp->mac_version == RTL_GIGA_MAC_VER_04)) + rtl_set_rx_tx_config_registers(tp); + + tp->cp_cmd |= rtl_rw_cpluscmd(ioaddr) | PCIMulRW; + + if ((tp->mac_version == RTL_GIGA_MAC_VER_02) || + (tp->mac_version == RTL_GIGA_MAC_VER_03)) { + DBG ( "Set MAC Reg C+CR Offset 0xE0. " + "Bit-3 and bit-14 MUST be 1\n" ); + tp->cp_cmd |= (1 << 14); + } + + RTL_W16(CPlusCmd, tp->cp_cmd); + + rtl8169_set_magic_reg(ioaddr, tp->mac_version); + + /* + * Undocumented corner. Supposedly: + * (TxTimer << 12) | (TxPackets << 8) | (RxTimer << 4) | RxPackets + */ + RTL_W16(IntrMitigate, 0x0000); + + rtl_set_rx_tx_desc_registers(tp, ioaddr); + + if ((tp->mac_version != RTL_GIGA_MAC_VER_01) && + (tp->mac_version != RTL_GIGA_MAC_VER_02) && + (tp->mac_version != RTL_GIGA_MAC_VER_03) && + (tp->mac_version != RTL_GIGA_MAC_VER_04)) { + RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb); + rtl_set_rx_tx_config_registers(tp); + } + + RTL_W8(Cfg9346, Cfg9346_Lock); + + /* Initially a 10 us delay. Turned it into a PCI commit. - FR */ + RTL_R8(IntrMask); + + RTL_W32(RxMissed, 0); + + rtl_set_rx_mode(dev); + + /* no early-rx interrupts */ + RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000); + + // RTL_W16(IntrMask, tp->intr_event); +} + +static void rtl_tx_performance_tweak(struct pci_device *pdev, u16 force) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8169_private *tp = netdev_priv(dev); + int cap = tp->pcie_cap; + + DBGP ( "rtl_tx_performance_tweak\n" ); + + if (cap) { + u16 ctl; + + pci_read_config_word(pdev, cap + PCI_EXP_DEVCTL, &ctl); + ctl = (ctl & ~PCI_EXP_DEVCTL_READRQ) | force; + pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL, ctl); + } +} + +static void rtl_csi_access_enable(void *ioaddr) +{ + u32 csi; + + DBGP ( "rtl_csi_access_enable\n" ); + + csi = rtl_csi_read(ioaddr, 0x070c) & 0x00ffffff; + rtl_csi_write(ioaddr, 0x070c, csi | 0x27000000); +} + +struct ephy_info { + unsigned int offset; + u16 mask; + u16 bits; +}; + +static void rtl_ephy_init(void *ioaddr, struct ephy_info *e, int len) +{ + u16 w; + + DBGP ( "rtl_ephy_init\n" ); + + while (len-- > 0) { + w = (rtl_ephy_read(ioaddr, e->offset) & ~e->mask) | e->bits; + rtl_ephy_write(ioaddr, e->offset, w); + e++; + } +} + +static void rtl_disable_clock_request(struct pci_device *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8169_private *tp = netdev_priv(dev); + int cap = tp->pcie_cap; + + DBGP ( "rtl_disable_clock_request\n" ); + + if (cap) { + u16 ctl; + + pci_read_config_word(pdev, cap + PCI_EXP_LNKCTL, &ctl); + ctl &= ~PCI_EXP_LNKCTL_CLKREQ_EN; + pci_write_config_word(pdev, cap + PCI_EXP_LNKCTL, ctl); + } +} + +#define R8168_CPCMD_QUIRK_MASK (\ + EnableBist | \ + Mac_dbgo_oe | \ + Force_half_dup | \ + Force_rxflow_en | \ + Force_txflow_en | \ + Cxpl_dbg_sel | \ + ASF | \ + PktCntrDisable | \ + Mac_dbgo_sel) + +static void rtl_hw_start_8168bb(void *ioaddr, struct pci_device *pdev) +{ + DBGP ( "rtl_hw_start_8168bb\n" ); + + RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en); + + RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK); + + rtl_tx_performance_tweak(pdev, + (0x5 << MAX_READ_REQUEST_SHIFT) | PCI_EXP_DEVCTL_NOSNOOP_EN); +} + +static void rtl_hw_start_8168bef(void *ioaddr, struct pci_device *pdev) +{ + DBGP ( "rtl_hw_start_8168bef\n" ); + + rtl_hw_start_8168bb(ioaddr, pdev); + + RTL_W8(EarlyTxThres, EarlyTxThld); + + RTL_W8(Config4, RTL_R8(Config4) & ~(1 << 0)); +} + +static void __rtl_hw_start_8168cp(void *ioaddr, struct pci_device *pdev) +{ + DBGP ( "__rtl_hw_start_8168cp\n" ); + + RTL_W8(Config1, RTL_R8(Config1) | Speed_down); + + RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en); + + rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT); + + rtl_disable_clock_request(pdev); + + RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK); +} + +static void rtl_hw_start_8168cp_1(void *ioaddr, struct pci_device *pdev) +{ + static struct ephy_info e_info_8168cp[] = { + { 0x01, 0, 0x0001 }, + { 0x02, 0x0800, 0x1000 }, + { 0x03, 0, 0x0042 }, + { 0x06, 0x0080, 0x0000 }, + { 0x07, 0, 0x2000 } + }; + + DBGP ( "rtl_hw_start_8168cp_1\n" ); + + rtl_csi_access_enable(ioaddr); + + rtl_ephy_init(ioaddr, e_info_8168cp, ARRAY_SIZE(e_info_8168cp)); + + __rtl_hw_start_8168cp(ioaddr, pdev); +} + +static void rtl_hw_start_8168cp_2(void *ioaddr, struct pci_device *pdev) +{ + DBGP ( "rtl_hw_start_8168cp_2\n" ); + + rtl_csi_access_enable(ioaddr); + + RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en); + + rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT); + + RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK); +} + +static void rtl_hw_start_8168cp_3(void *ioaddr, struct pci_device *pdev) +{ + DBGP ( "rtl_hw_start_8168cp_3\n" ); + + rtl_csi_access_enable(ioaddr); + + RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en); + + /* Magic. */ + RTL_W8(DBG_REG, 0x20); + + RTL_W8(EarlyTxThres, EarlyTxThld); + + rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT); + + RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK); +} + +static void rtl_hw_start_8168c_1(void *ioaddr, struct pci_device *pdev) +{ + static struct ephy_info e_info_8168c_1[] = { + { 0x02, 0x0800, 0x1000 }, + { 0x03, 0, 0x0002 }, + { 0x06, 0x0080, 0x0000 } + }; + + DBGP ( "rtl_hw_start_8168c_1\n" ); + + rtl_csi_access_enable(ioaddr); + + RTL_W8(DBG_REG, 0x06 | FIX_NAK_1 | FIX_NAK_2); + + rtl_ephy_init(ioaddr, e_info_8168c_1, ARRAY_SIZE(e_info_8168c_1)); + + __rtl_hw_start_8168cp(ioaddr, pdev); +} + +static void rtl_hw_start_8168c_2(void *ioaddr, struct pci_device *pdev) +{ + static struct ephy_info e_info_8168c_2[] = { + { 0x01, 0, 0x0001 }, + { 0x03, 0x0400, 0x0220 } + }; + + DBGP ( "rtl_hw_start_8168c_2\n" ); + + rtl_csi_access_enable(ioaddr); + + rtl_ephy_init(ioaddr, e_info_8168c_2, ARRAY_SIZE(e_info_8168c_2)); + + __rtl_hw_start_8168cp(ioaddr, pdev); +} + +static void rtl_hw_start_8168c_3(void *ioaddr, struct pci_device *pdev) +{ + DBGP ( "rtl_hw_start_8168c_3\n" ); + + rtl_hw_start_8168c_2(ioaddr, pdev); +} + +static void rtl_hw_start_8168c_4(void *ioaddr, struct pci_device *pdev) +{ + DBGP ( "rtl_hw_start_8168c_4\n" ); + + rtl_csi_access_enable(ioaddr); + + __rtl_hw_start_8168cp(ioaddr, pdev); +} + +static void rtl_hw_start_8168d(void *ioaddr, struct pci_device *pdev) +{ + DBGP ( "rtl_hw_start_8168d\n" ); + + rtl_csi_access_enable(ioaddr); + + rtl_disable_clock_request(pdev); + + RTL_W8(EarlyTxThres, EarlyTxThld); + + rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT); + + RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R8168_CPCMD_QUIRK_MASK); +} + +static void rtl_hw_start_8168(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void *ioaddr = tp->mmio_addr; + struct pci_device *pdev = tp->pci_dev; + + DBGP ( "rtl_hw_start_8168\n" ); + + RTL_W8(Cfg9346, Cfg9346_Unlock); + + RTL_W8(EarlyTxThres, EarlyTxThld); + + rtl_set_rx_max_size(ioaddr); + + tp->cp_cmd |= RTL_R16(CPlusCmd) | PktCntrDisable | INTT_1; + + RTL_W16(CPlusCmd, tp->cp_cmd); + + RTL_W16(IntrMitigate, 0x5151); + + /* Work around for RxFIFO overflow. */ + if (tp->mac_version == RTL_GIGA_MAC_VER_11) { + tp->intr_event |= RxFIFOOver | PCSTimeout; + tp->intr_event &= ~RxOverflow; + } + + rtl_set_rx_tx_desc_registers(tp, ioaddr); + + rtl_set_rx_mode(dev); + + RTL_W32(TxConfig, (TX_DMA_BURST << TxDMAShift) | + (InterFrameGap << TxInterFrameGapShift)); + + RTL_R8(IntrMask); + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_11: + rtl_hw_start_8168bb(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_12: + case RTL_GIGA_MAC_VER_17: + rtl_hw_start_8168bef(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_18: + rtl_hw_start_8168cp_1(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_19: + rtl_hw_start_8168c_1(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_20: + rtl_hw_start_8168c_2(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_21: + rtl_hw_start_8168c_3(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_22: + rtl_hw_start_8168c_4(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_23: + rtl_hw_start_8168cp_2(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_24: + rtl_hw_start_8168cp_3(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_25: + rtl_hw_start_8168d(ioaddr, pdev); + break; + + default: + DBG ( "Unknown chipset (mac_version = %d).\n", + tp->mac_version ); + break; + } + + RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb); + + RTL_W8(Cfg9346, Cfg9346_Lock); + + RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000); + + // RTL_W16(IntrMask, tp->intr_event); +} + +#define R810X_CPCMD_QUIRK_MASK (\ + EnableBist | \ + Mac_dbgo_oe | \ + Force_half_dup | \ + Force_half_dup | \ + Force_txflow_en | \ + Cxpl_dbg_sel | \ + ASF | \ + PktCntrDisable | \ + PCIDAC | \ + PCIMulRW) + +static void rtl_hw_start_8102e_1(void *ioaddr, struct pci_device *pdev) +{ + static struct ephy_info e_info_8102e_1[] = { + { 0x01, 0, 0x6e65 }, + { 0x02, 0, 0x091f }, + { 0x03, 0, 0xc2f9 }, + { 0x06, 0, 0xafb5 }, + { 0x07, 0, 0x0e00 }, + { 0x19, 0, 0xec80 }, + { 0x01, 0, 0x2e65 }, + { 0x01, 0, 0x6e65 } + }; + u8 cfg1; + + DBGP ( "rtl_hw_start_8102e_1\n" ); + + rtl_csi_access_enable(ioaddr); + + RTL_W8(DBG_REG, FIX_NAK_1); + + rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT); + + RTL_W8(Config1, + LEDS1 | LEDS0 | Speed_down | MEMMAP | IOMAP | VPD | PMEnable); + RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en); + + cfg1 = RTL_R8(Config1); + if ((cfg1 & LEDS0) && (cfg1 & LEDS1)) + RTL_W8(Config1, cfg1 & ~LEDS0); + + RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R810X_CPCMD_QUIRK_MASK); + + rtl_ephy_init(ioaddr, e_info_8102e_1, ARRAY_SIZE(e_info_8102e_1)); +} + +static void rtl_hw_start_8102e_2(void *ioaddr, struct pci_device *pdev) +{ + DBGP ( "rtl_hw_start_8102e_2\n" ); + + rtl_csi_access_enable(ioaddr); + + rtl_tx_performance_tweak(pdev, 0x5 << MAX_READ_REQUEST_SHIFT); + + RTL_W8(Config1, MEMMAP | IOMAP | VPD | PMEnable); + RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en); + + RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R810X_CPCMD_QUIRK_MASK); +} + +static void rtl_hw_start_8102e_3(void *ioaddr, struct pci_device *pdev) +{ + DBGP ( "rtl_hw_start_8102e_3\n" ); + + rtl_hw_start_8102e_2(ioaddr, pdev); + + rtl_ephy_write(ioaddr, 0x03, 0xc2f9); +} + +static void rtl_hw_start_8101(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void *ioaddr = tp->mmio_addr; + struct pci_device *pdev = tp->pci_dev; + + DBGP ( "rtl_hw_start_8101\n" ); + + if ((tp->mac_version == RTL_GIGA_MAC_VER_13) || + (tp->mac_version == RTL_GIGA_MAC_VER_16)) { + int cap = tp->pcie_cap; + + if (cap) { + pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_NOSNOOP_EN); + } + } + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_07: + rtl_hw_start_8102e_1(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_08: + rtl_hw_start_8102e_3(ioaddr, pdev); + break; + + case RTL_GIGA_MAC_VER_09: + rtl_hw_start_8102e_2(ioaddr, pdev); + break; + } + + RTL_W8(Cfg9346, Cfg9346_Unlock); + + RTL_W8(EarlyTxThres, EarlyTxThld); + + rtl_set_rx_max_size(ioaddr); + + tp->cp_cmd |= rtl_rw_cpluscmd(ioaddr) | PCIMulRW; + + RTL_W16(CPlusCmd, tp->cp_cmd); + + RTL_W16(IntrMitigate, 0x0000); + + rtl_set_rx_tx_desc_registers(tp, ioaddr); + + RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb); + rtl_set_rx_tx_config_registers(tp); + + RTL_W8(Cfg9346, Cfg9346_Lock); + + RTL_R8(IntrMask); + + rtl_set_rx_mode(dev); + + RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb); + + RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xf000); + + // RTL_W16(IntrMask, tp->intr_event); +} + +/*** gPXE API Support Routines ***/ + +/** + * setup_tx_resources - allocate tx resources (descriptors) + * + * @v tp Driver private storage + * + * @ret rc Returns 0 on success, negative on failure + **/ +static int +rtl8169_setup_tx_resources ( struct rtl8169_private *tp ) +{ + DBGP ( "rtl8169_setup_tx_resources\n" ); + + tp->tx_base = malloc_dma ( R8169_TX_RING_BYTES, TX_RING_ALIGN ); + + if ( ! tp->tx_base ) { + return -ENOMEM; + } + + memset ( tp->tx_base, 0, R8169_TX_RING_BYTES ); + + DBG ( "tp->tx_base = %#08lx\n", virt_to_bus ( tp->tx_base ) ); + + tp->tx_fill_ctr = 0; + tp->tx_curr = 0; + tp->tx_tail = 0; + + return 0; +} + +static void +rtl8169_process_tx_packets ( struct net_device *netdev ) +{ + struct rtl8169_private *tp = netdev_priv ( netdev ); + + uint32_t tx_status; + struct TxDesc *tx_curr_desc; + + DBGP ( "rtl8169_process_tx_packets\n" ); + + while ( tp->tx_tail != tp->tx_curr ) { + + tx_curr_desc = tp->tx_base + tp->tx_tail; + + tx_status = tx_curr_desc->opts1; + + DBG2 ( "Before DescOwn check tx_status: %#08x\n", tx_status ); + + /* if the packet at tx_tail is not owned by hardware it is for us */ + if ( tx_status & DescOwn ) + break; + + DBG ( "Transmitted packet.\n" ); + DBG ( "tp->tx_fill_ctr = %d\n", tp->tx_fill_ctr ); + DBG ( "tp->tx_tail = %d\n", tp->tx_tail ); + DBG ( "tp->tx_curr = %d\n", tp->tx_curr ); + DBG ( "tx_status = %d\n", tx_status ); + DBG ( "tx_curr_desc = %#08lx\n", virt_to_bus ( tx_curr_desc ) ); + + /* Pass packet to core for processing */ + netdev_tx_complete ( netdev, tp->tx_iobuf[tp->tx_tail] ); + + memset ( tx_curr_desc, 0, sizeof ( *tx_curr_desc ) ); + + /* Decrement count of used descriptors */ + tp->tx_fill_ctr--; + + /* Increment sent packets index */ + tp->tx_tail = ( tp->tx_tail + 1 ) % NUM_TX_DESC; + } +} + +static void +rtl8169_free_tx_resources ( struct rtl8169_private *tp ) +{ + DBGP ( "rtl8169_free_tx_resources\n" ); + + free_dma ( tp->tx_base, R8169_TX_RING_BYTES ); +} + +static void +rtl8169_populate_rx_descriptor ( struct rtl8169_private *tp, struct RxDesc *rx_desc, uint32_t index ) +{ + DBGP ( "rtl8169_populate_rx_descriptor\n" ); + + DBG ( "Populating rx descriptor %d\n", index ); + + memset ( rx_desc, 0, sizeof ( *rx_desc ) ); + + rx_desc->addr_hi = 0; + rx_desc->addr_lo = virt_to_bus ( tp->rx_iobuf[index]->data ); + rx_desc->opts2 = 0; + rx_desc->opts1 = ( index == ( NUM_RX_DESC - 1 ) ? RingEnd : 0 ) | + RX_BUF_SIZE; + rx_desc->opts1 |= DescOwn; +} + +/** + * Refill descriptor ring + * + * @v netdev Net device + */ +static void rtl8169_refill_rx_ring ( struct rtl8169_private *tp ) +{ + struct RxDesc *rx_curr_desc; + int i; + + DBGP ( "rtl8169_refill_rx_ring\n" ); + + for ( i = 0; i < NUM_RX_DESC; i++ ) { + + rx_curr_desc = ( tp->rx_base ) + i; + + /* Don't touch descriptors owned by the NIC */ + if ( rx_curr_desc->opts1 & DescOwn ) + continue; + + /* Don't touch descriptors with iobufs, they still need to be + processed by the poll routine */ + if ( tp->rx_iobuf[tp->rx_curr] != NULL ) + continue; + + /** If we can't get an iobuf for this descriptor + try again later (next poll). + */ + if ( ! ( tp->rx_iobuf[i] = alloc_iob ( RX_BUF_SIZE ) ) ) { + DBG ( "Refill rx ring failed!!\n" ); + break; + } + + rtl8169_populate_rx_descriptor ( tp, rx_curr_desc, i ); + } +} + +/** + * setup_rx_resources - allocate Rx resources (Descriptors) + * + * @v tp: Driver private structure + * + * @ret rc Returns 0 on success, negative on failure + * + **/ +static int +rtl8169_setup_rx_resources ( struct rtl8169_private *tp ) +{ + DBGP ( "rtl8169_setup_rx_resources\n" ); + + tp->rx_base = malloc_dma ( R8169_RX_RING_BYTES, RX_RING_ALIGN ); + + DBG ( "tp->rx_base = %#08lx\n", virt_to_bus ( tp->rx_base ) ); + + if ( ! tp->rx_base ) { + return -ENOMEM; + } + memset ( tp->rx_base, 0, R8169_RX_RING_BYTES ); + + rtl8169_refill_rx_ring ( tp ); + + tp->rx_curr = 0; + + return 0; +} + +static void +rtl8169_process_rx_packets ( struct net_device *netdev ) +{ + struct rtl8169_private *tp = netdev_priv ( netdev ); + uint32_t rx_status; + uint16_t rx_len; + struct RxDesc *rx_curr_desc; + int i; + + DBGP ( "rtl8169_process_rx_packets\n" ); + + for ( i = 0; i < NUM_RX_DESC; i++ ) { + + rx_curr_desc = tp->rx_base + tp->rx_curr; + + rx_status = rx_curr_desc->opts1; + + DBG2 ( "Before DescOwn check rx_status: %#08x\n", rx_status ); + + /* Hardware still owns the descriptor */ + if ( rx_status & DescOwn ) + break; + + /* We own the descriptor, but it has not been refilled yet */ + if ( tp->rx_iobuf[tp->rx_curr] == NULL ) + break; + + rx_len = rx_status & 0x3fff; + + DBG ( "Received packet.\n" ); + DBG ( "tp->rx_curr = %d\n", tp->rx_curr ); + DBG ( "rx_len = %d\n", rx_len ); + DBG ( "rx_status = %#08x\n", rx_status ); + DBG ( "rx_curr_desc = %#08lx\n", virt_to_bus ( rx_curr_desc ) ); + + if ( rx_status & RxRES ) { + + netdev_rx_err ( netdev, tp->rx_iobuf[tp->rx_curr], -EINVAL ); + + DBG ( "rtl8169_poll: Corrupted packet received!\n" + " rx_status: %#08x\n", rx_status ); + + } else { + + /* Adjust size of the iobuf to reflect received data */ + iob_put ( tp->rx_iobuf[tp->rx_curr], rx_len ); + + /* Add this packet to the receive queue. */ + netdev_rx ( netdev, tp->rx_iobuf[tp->rx_curr] ); + } + + /* Invalidate this iobuf and descriptor */ + tp->rx_iobuf[tp->rx_curr] = NULL; + memset ( rx_curr_desc, 0, sizeof ( *rx_curr_desc ) ); + + /* Update pointer to next available rx descriptor */ + tp->rx_curr = ( tp->rx_curr + 1 ) % NUM_RX_DESC; + } + rtl8169_refill_rx_ring ( tp ); +} + +static void +rtl8169_free_rx_resources ( struct rtl8169_private *tp ) +{ + int i; + + DBGP ( "rtl8169_free_rx_resources\n" ); + + free_dma ( tp->rx_base, R8169_RX_RING_BYTES ); + + for ( i = 0; i < NUM_RX_DESC; i++ ) { + free_iob ( tp->rx_iobuf[i] ); + tp->rx_iobuf[i] = NULL; + } +} + +/** + FIXME: Because gPXE's pci_device_id structure does not contain a + field to contain arbitrary data, we need the following table to + associate PCI IDs with nic variants, because a lot of driver + routines depend on knowing which kind of variant they are dealing + with. --mdc + **/ + +#define _R(VENDOR,DEVICE,INDEX) \ + { .vendor = VENDOR, .device = DEVICE, .index = INDEX } + +static const struct { + uint16_t vendor; + uint16_t device; + int index; +} nic_variant_table[] = { + _R(0x10ec, 0x8129, RTL_CFG_0), + _R(0x10ec, 0x8136, RTL_CFG_2), + _R(0x10ec, 0x8167, RTL_CFG_0), + _R(0x10ec, 0x8168, RTL_CFG_1), + _R(0x10ec, 0x8169, RTL_CFG_0), + _R(0x1186, 0x4300, RTL_CFG_0), + _R(0x1259, 0xc107, RTL_CFG_0), + _R(0x16ec, 0x0116, RTL_CFG_0), + _R(0x1737, 0x1032, RTL_CFG_0), + _R(0x0001, 0x8168, RTL_CFG_2), +}; +#undef _R + +static int +rtl8169_get_nic_variant ( uint16_t vendor, uint16_t device ) +{ + u32 i; + + DBGP ( "rtl8169_get_nic_variant\n" ); + + for (i = 0; i < ARRAY_SIZE(nic_variant_table); i++) { + if ( ( nic_variant_table[i].vendor == vendor ) && + ( nic_variant_table[i].device == device ) ) { + return ( nic_variant_table[i].index ); + } + } + DBG ( "No matching NIC variant found!\n" ); + return ( RTL_CFG_0 ); +} + +static void rtl8169_irq_enable ( struct rtl8169_private *tp ) +{ + void *ioaddr = tp->mmio_addr; + + DBGP ( "rtl8169_irq_enable\n" ); + + RTL_W16 ( IntrMask, tp->intr_event ); +} + +static void rtl8169_irq_disable ( struct rtl8169_private *tp ) +{ + void *ioaddr = tp->mmio_addr; + + DBGP ( "rtl8169_irq_disable\n" ); + + rtl8169_irq_mask_and_ack ( ioaddr ); +} + +/*** gPXE Core API Routines ***/ + +/** + * open - Called when a network interface is made active + * + * @v netdev network interface device structure + * @ret rc Return status code, 0 on success, negative value on failure + * + **/ +static int +rtl8169_open ( struct net_device *netdev ) +{ + struct rtl8169_private *tp = netdev_priv ( netdev ); + void *ioaddr = tp->mmio_addr; + int rc; + + DBGP ( "rtl8169_open\n" ); + + /* allocate transmit descriptors */ + rc = rtl8169_setup_tx_resources ( tp ); + if ( rc ) { + DBG ( "Error setting up TX resources!\n" ); + goto err_setup_tx; + } + + /* allocate receive descriptors */ + rc = rtl8169_setup_rx_resources ( tp ); + if ( rc ) { + DBG ( "Error setting up RX resources!\n" ); + goto err_setup_rx; + } + + rtl_hw_start ( netdev ); + + DBG ( "TxDescStartAddrHigh = %#08lx\n", RTL_R32 ( TxDescStartAddrHigh ) ); + DBG ( "TxDescStartAddrLow = %#08lx\n", RTL_R32 ( TxDescStartAddrLow ) ); + DBG ( "RxDescAddrHigh = %#08lx\n", RTL_R32 ( RxDescAddrHigh ) ); + DBG ( "RxDescAddrLow = %#08lx\n", RTL_R32 ( RxDescAddrLow ) ); + + return 0; + +err_setup_rx: + rtl8169_free_tx_resources ( tp ); +err_setup_tx: + rtl8169_hw_reset ( ioaddr ); + + return rc; +} + +/** + * transmit - Transmit a packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * + * @ret rc Returns 0 on success, negative on failure + */ +static int +rtl8169_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) +{ + struct rtl8169_private *tp = netdev_priv ( netdev ); + void *ioaddr = tp->mmio_addr; + uint32_t tx_len = iob_len ( iobuf ); + + struct TxDesc *tx_curr_desc; + + DBGP ("rtl8169_transmit\n"); + + if ( tp->tx_fill_ctr == NUM_TX_DESC ) { + DBG ("TX overflow\n"); + return -ENOBUFS; + } + + /** + * The rtl8169 family automatically pads short packets to a + * minimum size, but if it did not, like some older cards, + * we could do: + * iob_pad ( iobuf, ETH_ZLEN ); + */ + + /* Save pointer to this iobuf we have been given to transmit so + we can pass it to netdev_tx_complete() later */ + tp->tx_iobuf[tp->tx_curr] = iobuf; + + tx_curr_desc = tp->tx_base + tp->tx_curr; + + DBG ( "tp->tx_fill_ctr = %d\n", tp->tx_fill_ctr ); + DBG ( "tp->tx_curr = %d\n", tp->tx_curr ); + DBG ( "tx_curr_desc = %#08lx\n", virt_to_bus ( tx_curr_desc ) ); + DBG ( "iobuf->data = %#08lx\n", virt_to_bus ( iobuf->data ) ); + DBG ( "tx_len = %d\n", tx_len ); + + /* Configure current descriptor to transmit supplied packet */ + tx_curr_desc->addr_hi = 0; + tx_curr_desc->addr_lo = virt_to_bus ( iobuf->data ); + tx_curr_desc->opts2 = 0; + tx_curr_desc->opts1 = FirstFrag | LastFrag | + ( tp->tx_curr == ( NUM_TX_DESC - 1 ) ? RingEnd : 0 ) | + tx_len; + + /* Mark descriptor as owned by NIC */ + tx_curr_desc->opts1 |= DescOwn; + + DBG ( "tx_curr_desc->opts1 = %#08x\n", tx_curr_desc->opts1 ); + DBG ( "tx_curr_desc->opts2 = %#08x\n", tx_curr_desc->opts2 ); + DBG ( "tx_curr_desc->addr_hi = %#08x\n", tx_curr_desc->addr_hi ); + DBG ( "tx_curr_desc->addr_lo = %#08x\n", tx_curr_desc->addr_lo ); + + RTL_W8 ( TxPoll, NPQ ); /* set polling bit */ + + /* Point to next free descriptor */ + tp->tx_curr = ( tp->tx_curr + 1 ) % NUM_TX_DESC; + + /* Increment number of tx descriptors in use */ + tp->tx_fill_ctr++; + + return 0; +} + +/** + * poll - Poll for received packets + * + * @v netdev Network device + */ +static void +rtl8169_poll ( struct net_device *netdev ) +{ + struct rtl8169_private *tp = netdev_priv ( netdev ); + void *ioaddr = tp->mmio_addr; + + uint16_t intr_status; + uint16_t intr_mask; + + DBGP ( "rtl8169_poll\n" ); + + intr_status = RTL_R16 ( IntrStatus ); + intr_mask = RTL_R16 ( IntrMask ); + + DBG2 ( "rtl8169_poll (before): intr_mask = %#04x intr_status = %#04x\n", + intr_mask, intr_status ); + + RTL_W16 ( IntrStatus, 0xffff ); + + /* hotplug / major error / no more work / shared irq */ + if ( intr_status == 0xffff ) + return; + + /* Process transmitted packets */ + rtl8169_process_tx_packets ( netdev ); + + /* Process received packets */ + rtl8169_process_rx_packets ( netdev ); +} + +/** + * close - Disable network interface + * + * @v netdev network interface device structure + * + **/ +static void +rtl8169_close ( struct net_device *netdev ) +{ + struct rtl8169_private *tp = netdev_priv ( netdev ); + void *ioaddr = tp->mmio_addr; + + DBGP ( "r8169_close\n" ); + + rtl8169_hw_reset ( ioaddr ); + + rtl8169_free_tx_resources ( tp ); + rtl8169_free_rx_resources ( tp ); +} + +/** + * irq - enable or Disable interrupts + * + * @v netdev network adapter + * @v action requested interrupt action + * + **/ +static void +rtl8169_irq ( struct net_device *netdev, int action ) +{ + struct rtl8169_private *tp = netdev_priv ( netdev ); + + DBGP ( "rtl8169_irq\n" ); + + switch ( action ) { + case 0 : + rtl8169_irq_disable ( tp ); + break; + default : + rtl8169_irq_enable ( tp ); + break; + } +} + +static struct net_device_operations rtl8169_operations = { + .open = rtl8169_open, + .transmit = rtl8169_transmit, + .poll = rtl8169_poll, + .close = rtl8169_close, + .irq = rtl8169_irq, +}; + +/** + * probe - Initial configuration of NIC + * + * @v pci PCI device + * @v id PCI IDs + * + * @ret rc Return status code + **/ +static int +rtl8169_probe ( struct pci_device *pdev, const struct pci_device_id *ent ) +{ + int i, rc; + struct net_device *netdev; + struct rtl8169_private *tp; + void *ioaddr; + + /** FIXME: This lookup is necessary because gPXE does not have a "data" + element in the structure pci_device_id which can pass an arbitrary + piece of data to the driver. It might be useful to add it. Then we + could just use ent->data instead of having to look up cfg_index. + **/ + int cfg_index = rtl8169_get_nic_variant ( ent->vendor, ent->device ); + const struct rtl_cfg_info *cfg = rtl_cfg_infos + cfg_index; + + DBGP ( "rtl8169_probe\n" ); + + DBG ( "ent->vendor = %#04x, ent->device = %#04x\n", ent->vendor, ent->device ); + + DBG ( "cfg_index = %d\n", cfg_index ); + DBG ( "cfg->intr_event = %#04x\n", cfg->intr_event ); + + rc = -ENOMEM; + + /* Allocate net device ( also allocates memory for netdev->priv + and makes netdev-priv point to it ) + */ + netdev = alloc_etherdev ( sizeof ( *tp ) ); + + if ( ! netdev ) + goto err_alloc_etherdev; + + /* Associate driver-specific network operations with + generic network device layer + */ + netdev_init ( netdev, &rtl8169_operations ); + + /* Associate this network device with the given PCI device */ + pci_set_drvdata ( pdev, netdev ); + netdev->dev = &pdev->dev; + + /* Initialize driver private storage */ + tp = netdev_priv ( netdev ); + memset ( tp, 0, ( sizeof ( *tp ) ) ); + + tp->pci_dev = pdev; + tp->irqno = pdev->irq; + tp->netdev = netdev; + tp->cfg_index = cfg_index; + tp->intr_event = cfg->intr_event; + tp->cp_cmd = PCIMulRW; + + tp->hw_start = cfg->hw_start; + + rc = -EIO; + + adjust_pci_device ( pdev ); + + /* ioremap MMIO region */ + ioaddr = ioremap ( pdev->membase, R8169_REGS_SIZE ); + + if ( ! ioaddr ) { + DBG ( "cannot remap MMIO\n" ); + rc = -EIO; + goto err_ioremap; + } + + tp->mmio_addr = ioaddr; + + tp->pcie_cap = pci_find_capability ( pdev, PCI_CAP_ID_EXP ); + if ( tp->pcie_cap ) { + DBG ( "PCI Express capability\n" ); + } else { + DBG ( "No PCI Express capability\n" ); + } + + /* Mask interrupts just in case */ + rtl8169_irq_mask_and_ack ( ioaddr ); + + /* Soft reset NIC */ + rtl_soft_reset ( netdev ); + + /* Identify chip attached to board */ + rtl8169_get_mac_version ( tp, ioaddr ); + + for ( i = 0; (u32) i < ARRAY_SIZE ( rtl_chip_info ); i++ ) { + if ( tp->mac_version == rtl_chip_info[i].mac_version ) + break; + } + if ( i == ARRAY_SIZE(rtl_chip_info ) ) { + /* Unknown chip: assume array element #0, original RTL-8169 */ + DBG ( "Unknown chip version, assuming %s\n", rtl_chip_info[0].name ); + i = 0; + } + tp->chipset = i; + + if ((tp->mac_version <= RTL_GIGA_MAC_VER_06) && + (RTL_R8(PHYstatus) & TBI_Enable)) { + tp->set_speed = rtl8169_set_speed_tbi; + tp->phy_reset_enable = rtl8169_tbi_reset_enable; + tp->phy_reset_pending = rtl8169_tbi_reset_pending; + tp->link_ok = rtl8169_tbi_link_ok; + + tp->phy_1000_ctrl_reg = ADVERTISE_1000FULL; /* Implied by TBI */ + } else { + tp->set_speed = rtl8169_set_speed_xmii; + tp->phy_reset_enable = rtl8169_xmii_reset_enable; + tp->phy_reset_pending = rtl8169_xmii_reset_pending; + tp->link_ok = rtl8169_xmii_link_ok; + } + + /* Get MAC address */ + for ( i = 0; i < MAC_ADDR_LEN; i++ ) + netdev->ll_addr[i] = RTL_R8 ( MAC0 + i ); + + DBG ( "%s\n", eth_ntoa ( netdev->ll_addr ) ); + + rtl8169_init_phy ( netdev, tp ); + + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + /* Mark as link up; we don't yet handle link state */ + netdev_link_up ( netdev ); + + DBG ( "rtl8169_probe succeeded!\n" ); + + /* No errors, return success */ + return 0; + +/* Error return paths */ +err_register: +err_ioremap: + netdev_put ( netdev ); +err_alloc_etherdev: + return rc; +} + +/** + * remove - Device Removal Routine + * + * @v pdev PCI device information struct + * + **/ +static void +rtl8169_remove ( struct pci_device *pdev ) +{ + struct net_device *netdev = pci_get_drvdata ( pdev ); + struct rtl8169_private *tp = netdev_priv ( netdev ); + void *ioaddr = tp->mmio_addr; + + DBGP ( "rtl8169_remove\n" ); + + rtl8169_hw_reset ( ioaddr ); + + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +static struct pci_device_id rtl8169_nics[] = { + PCI_ROM(0x10ec, 0x8129, "rtl8169-0x8129", "rtl8169-0x8129"), + PCI_ROM(0x10ec, 0x8136, "rtl8169-0x8136", "rtl8169-0x8136"), + PCI_ROM(0x10ec, 0x8167, "rtl8169-0x8167", "rtl8169-0x8167"), + PCI_ROM(0x10ec, 0x8168, "rtl8169-0x8168", "rtl8169-0x8168"), + PCI_ROM(0x10ec, 0x8169, "rtl8169-0x8169", "rtl8169-0x8169"), + PCI_ROM(0x1186, 0x4300, "rtl8169-0x4300", "rtl8169-0x4300"), + PCI_ROM(0x1259, 0xc107, "rtl8169-0xc107", "rtl8169-0xc107"), + PCI_ROM(0x16ec, 0x0116, "rtl8169-0x0116", "rtl8169-0x0116"), + PCI_ROM(0x1737, 0x1032, "rtl8169-0x1032", "rtl8169-0x1032"), + PCI_ROM(0x0001, 0x8168, "rtl8169-0x8168", "rtl8169-0x8168"), +}; + +struct pci_driver rtl8169_driver __pci_driver = { + .ids = rtl8169_nics, + .id_count = ( sizeof ( rtl8169_nics ) / sizeof ( rtl8169_nics[0] ) ), + .probe = rtl8169_probe, + .remove = rtl8169_remove, +}; + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/r8169.h b/gpxe/src/drivers/net/r8169.h new file mode 100644 index 0000000..d353632 --- /dev/null +++ b/gpxe/src/drivers/net/r8169.h @@ -0,0 +1,566 @@ +/* + * Copyright (c) 2008 Marty Connor + * Copyright (c) 2008 Entity Cyber, 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 any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This driver is based on rtl8169 data sheets and work by: + * + * Copyright (c) 2002 ShuChen + * Copyright (c) 2003 - 2007 Francois Romieu + * Copyright (c) a lot of people too. Please respect their work. + * + */ + +#ifndef _R8169_H_ +#define _R8169_H_ + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +/** FIXME: include/linux/pci_regs.h has these PCI regs, maybe + we need such a file in gPXE? +**/ +#define PCI_EXP_DEVCTL 8 /* Device Control */ +#define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */ +#define PCI_EXP_LNKCTL 16 /* Link Control */ +#define PCI_EXP_LNKCTL_CLKREQ_EN 0x100 /* Enable clkreq */ +#define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */ + +/** FIXME: update mii.h in src/include/mii.h from Linux sources + so we don't have to include these definitiions. +**/ +/* The forced speed, 10Mb, 100Mb, gigabit, 2.5Gb, 10GbE. */ +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 +#define SPEED_2500 2500 +#define SPEED_10000 10000 + +/* Duplex, half or full. */ +#define DUPLEX_HALF 0x00 +#define DUPLEX_FULL 0x01 + +/* Generic MII registers. */ + +#define MII_BMCR 0x00 /* Basic mode control register */ +#define MII_BMSR 0x01 /* Basic mode status register */ +#define MII_PHYSID1 0x02 /* PHYS ID 1 */ +#define MII_PHYSID2 0x03 /* PHYS ID 2 */ +#define MII_ADVERTISE 0x04 /* Advertisement control reg */ +#define MII_LPA 0x05 /* Link partner ability reg */ +#define MII_EXPANSION 0x06 /* Expansion register */ +#define MII_CTRL1000 0x09 /* 1000BASE-T control */ +#define MII_STAT1000 0x0a /* 1000BASE-T status */ +#define MII_ESTATUS 0x0f /* Extended Status */ +#define MII_DCOUNTER 0x12 /* Disconnect counter */ +#define MII_FCSCOUNTER 0x13 /* False carrier counter */ +#define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */ +#define MII_RERRCOUNTER 0x15 /* Receive error counter */ +#define MII_SREVISION 0x16 /* Silicon revision */ +#define MII_RESV1 0x17 /* Reserved... */ +#define MII_LBRERROR 0x18 /* Lpback, rx, bypass error */ +#define MII_PHYADDR 0x19 /* PHY address */ +#define MII_RESV2 0x1a /* Reserved... */ +#define MII_TPISTATUS 0x1b /* TPI status for 10mbps */ +#define MII_NCONFIG 0x1c /* Network interface config */ + +/* Basic mode control register. */ +#define BMCR_RESV 0x003f /* Unused... */ +#define BMCR_SPEED1000 0x0040 /* MSB of Speed (1000) */ +#define BMCR_CTST 0x0080 /* Collision test */ +#define BMCR_FULLDPLX 0x0100 /* Full duplex */ +#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */ +#define BMCR_ISOLATE 0x0400 /* Disconnect DP83840 from MII */ +#define BMCR_PDOWN 0x0800 /* Powerdown the DP83840 */ +#define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */ +#define BMCR_SPEED100 0x2000 /* Select 100Mbps */ +#define BMCR_LOOPBACK 0x4000 /* TXD loopback bits */ +#define BMCR_RESET 0x8000 /* Reset the DP83840 */ + +/* Basic mode status register. */ +#define BMSR_ERCAP 0x0001 /* Ext-reg capability */ +#define BMSR_JCD 0x0002 /* Jabber detected */ +#define BMSR_LSTATUS 0x0004 /* Link status */ +#define BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */ +#define BMSR_RFAULT 0x0010 /* Remote fault detected */ +#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */ +#define BMSR_RESV 0x00c0 /* Unused... */ +#define BMSR_ESTATEN 0x0100 /* Extended Status in R15 */ +#define BMSR_100HALF2 0x0200 /* Can do 100BASE-T2 HDX */ +#define BMSR_100FULL2 0x0400 /* Can do 100BASE-T2 FDX */ +#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 */ + +#define AUTONEG_DISABLE 0x00 +#define AUTONEG_ENABLE 0x01 + +#define MII_ADVERTISE 0x04 /* Advertisement control reg */ +#define ADVERTISE_SLCT 0x001f /* Selector bits */ +#define ADVERTISE_CSMA 0x0001 /* Only selector supported */ +#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ +#define ADVERTISE_1000XFULL 0x0020 /* Try for 1000BASE-X full-duplex */ +#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ +#define ADVERTISE_1000XHALF 0x0040 /* Try for 1000BASE-X half-duplex */ +#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ +#define ADVERTISE_1000XPAUSE 0x0080 /* Try for 1000BASE-X pause */ +#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ +#define ADVERTISE_1000XPSE_ASYM 0x0100 /* Try for 1000BASE-X asym pause */ +#define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */ +#define ADVERTISE_PAUSE_CAP 0x0400 /* Try for pause */ +#define ADVERTISE_PAUSE_ASYM 0x0800 /* Try for asymetric pause */ +#define ADVERTISE_RESV 0x1000 /* 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) + +/* 1000BASE-T Control register */ +#define ADVERTISE_1000FULL 0x0200 /* Advertise 1000BASE-T full duplex */ +#define ADVERTISE_1000HALF 0x0100 /* Advertise 1000BASE-T half duplex */ + +/* MAC address length */ +#define MAC_ADDR_LEN 6 + +#define MAX_READ_REQUEST_SHIFT 12 +#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 0x3FE8 /* 16K - 1 - ETH_HLEN - VLAN - CRC... */ +#define SafeMtu 0x1c20 /* ... actually life sucks beyond ~7k */ +#define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */ + +#define R8169_REGS_SIZE 256 +#define R8169_NAPI_WEIGHT 64 +#define NUM_TX_DESC 8 /* Number of Tx descriptor registers */ +#define NUM_RX_DESC 8 /* Number of Rx descriptor registers */ +#define RX_BUF_SIZE 1536 /* Rx Buffer size */ +#define R8169_TX_RING_BYTES (NUM_TX_DESC * sizeof(struct TxDesc)) +#define R8169_RX_RING_BYTES (NUM_RX_DESC * sizeof(struct RxDesc)) + +#define TX_RING_ALIGN 256 +#define RX_RING_ALIGN 256 + +#define RTL8169_TX_TIMEOUT (6*HZ) +#define RTL8169_PHY_TIMEOUT (10*HZ) + +#define RTL_EEPROM_SIG cpu_to_le32(0x8129) +#define RTL_EEPROM_SIG_MASK cpu_to_le32(0xffff) +#define RTL_EEPROM_SIG_ADDR 0x0000 + +/* 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 mac_version { + RTL_GIGA_MAC_VER_01 = 0x01, // 8169 + RTL_GIGA_MAC_VER_02 = 0x02, // 8169S + RTL_GIGA_MAC_VER_03 = 0x03, // 8110S + RTL_GIGA_MAC_VER_04 = 0x04, // 8169SB + RTL_GIGA_MAC_VER_05 = 0x05, // 8110SCd + RTL_GIGA_MAC_VER_06 = 0x06, // 8110SCe + RTL_GIGA_MAC_VER_07 = 0x07, // 8102e + RTL_GIGA_MAC_VER_08 = 0x08, // 8102e + RTL_GIGA_MAC_VER_09 = 0x09, // 8102e + RTL_GIGA_MAC_VER_10 = 0x0a, // 8101e + RTL_GIGA_MAC_VER_11 = 0x0b, // 8168Bb + RTL_GIGA_MAC_VER_12 = 0x0c, // 8168Be + RTL_GIGA_MAC_VER_13 = 0x0d, // 8101Eb + RTL_GIGA_MAC_VER_14 = 0x0e, // 8101 ? + RTL_GIGA_MAC_VER_15 = 0x0f, // 8101 ? + RTL_GIGA_MAC_VER_16 = 0x11, // 8101Ec + RTL_GIGA_MAC_VER_17 = 0x10, // 8168Bf + RTL_GIGA_MAC_VER_18 = 0x12, // 8168CP + RTL_GIGA_MAC_VER_19 = 0x13, // 8168C + RTL_GIGA_MAC_VER_20 = 0x14, // 8168C + RTL_GIGA_MAC_VER_21 = 0x15, // 8168C + RTL_GIGA_MAC_VER_22 = 0x16, // 8168C + RTL_GIGA_MAC_VER_23 = 0x17, // 8168CP + RTL_GIGA_MAC_VER_24 = 0x18, // 8168CP + RTL_GIGA_MAC_VER_25 = 0x19, // 8168D +}; + +#define _R(NAME,MAC,MASK) \ + { .name = NAME, .mac_version = MAC, .RxConfigMask = MASK } + +static const struct { + const char *name; + u8 mac_version; + u32 RxConfigMask; /* Clears the bits supported by this chip */ +} rtl_chip_info[] = { + _R("RTL8169", RTL_GIGA_MAC_VER_01, 0xff7e1880), // 8169 + _R("RTL8169s", RTL_GIGA_MAC_VER_02, 0xff7e1880), // 8169S + _R("RTL8110s", RTL_GIGA_MAC_VER_03, 0xff7e1880), // 8110S + _R("RTL8169sb/8110sb", RTL_GIGA_MAC_VER_04, 0xff7e1880), // 8169SB + _R("RTL8169sc/8110sc", RTL_GIGA_MAC_VER_05, 0xff7e1880), // 8110SCd + _R("RTL8169sc/8110sc", RTL_GIGA_MAC_VER_06, 0xff7e1880), // 8110SCe + _R("RTL8102e", RTL_GIGA_MAC_VER_07, 0xff7e1880), // PCI-E + _R("RTL8102e", RTL_GIGA_MAC_VER_08, 0xff7e1880), // PCI-E + _R("RTL8102e", RTL_GIGA_MAC_VER_09, 0xff7e1880), // PCI-E + _R("RTL8101e", RTL_GIGA_MAC_VER_10, 0xff7e1880), // PCI-E + _R("RTL8168b/8111b", RTL_GIGA_MAC_VER_11, 0xff7e1880), // PCI-E + _R("RTL8168b/8111b", RTL_GIGA_MAC_VER_12, 0xff7e1880), // PCI-E + _R("RTL8101e", RTL_GIGA_MAC_VER_13, 0xff7e1880), // PCI-E 8139 + _R("RTL8100e", RTL_GIGA_MAC_VER_14, 0xff7e1880), // PCI-E 8139 + _R("RTL8100e", RTL_GIGA_MAC_VER_15, 0xff7e1880), // PCI-E 8139 + _R("RTL8168b/8111b", RTL_GIGA_MAC_VER_17, 0xff7e1880), // PCI-E + _R("RTL8101e", RTL_GIGA_MAC_VER_16, 0xff7e1880), // PCI-E + _R("RTL8168cp/8111cp", RTL_GIGA_MAC_VER_18, 0xff7e1880), // PCI-E + _R("RTL8168c/8111c", RTL_GIGA_MAC_VER_19, 0xff7e1880), // PCI-E + _R("RTL8168c/8111c", RTL_GIGA_MAC_VER_20, 0xff7e1880), // PCI-E + _R("RTL8168c/8111c", RTL_GIGA_MAC_VER_21, 0xff7e1880), // PCI-E + _R("RTL8168c/8111c", RTL_GIGA_MAC_VER_22, 0xff7e1880), // PCI-E + _R("RTL8168cp/8111cp", RTL_GIGA_MAC_VER_23, 0xff7e1880), // PCI-E + _R("RTL8168cp/8111cp", RTL_GIGA_MAC_VER_24, 0xff7e1880), // PCI-E + _R("RTL8168d/8111d", RTL_GIGA_MAC_VER_25, 0xff7e1880) // PCI-E +}; +#undef _R + +enum cfg_version { + RTL_CFG_0 = 0x00, + RTL_CFG_1, + RTL_CFG_2 +}; + +#if 0 +/** Device Table from Linux Driver **/ +static struct pci_device_id rtl8169_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8129), 0, 0, RTL_CFG_0 }, + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8136), 0, 0, RTL_CFG_2 }, + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8167), 0, 0, RTL_CFG_0 }, + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8168), 0, 0, RTL_CFG_1 }, + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8169), 0, 0, RTL_CFG_0 }, + { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4300), 0, 0, RTL_CFG_0 }, + { PCI_DEVICE(PCI_VENDOR_ID_AT, 0xc107), 0, 0, RTL_CFG_0 }, + { PCI_DEVICE(0x16ec, 0x0116), 0, 0, RTL_CFG_0 }, + { PCI_VENDOR_ID_LINKSYS, 0x1032, + PCI_ANY_ID, 0x0024, 0, 0, RTL_CFG_0 }, + { 0x0001, 0x8168, + PCI_ANY_ID, 0x2410, 0, 0, RTL_CFG_2 }, + {0,}, +}; +#endif + +enum rtl_registers { + MAC0 = 0, /* Ethernet hardware address. */ + MAC4 = 4, + MAR0 = 8, /* Multicast filter. */ + CounterAddrLow = 0x10, + CounterAddrHigh = 0x14, + TxDescStartAddrLow = 0x20, + TxDescStartAddrHigh = 0x24, + TxHDescStartAddrLow = 0x28, + TxHDescStartAddrHigh = 0x2c, + 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, + PHYstatus = 0x6c, + RxMaxSize = 0xda, + CPlusCmd = 0xe0, + IntrMitigate = 0xe2, + RxDescAddrLow = 0xe4, + RxDescAddrHigh = 0xe8, + EarlyTxThres = 0xec, + FuncEvent = 0xf0, + FuncEventMask = 0xf4, + FuncPresetState = 0xf8, + FuncForceEvent = 0xfc, +}; + +enum rtl8110_registers { + TBICSR = 0x64, + TBI_ANAR = 0x68, + TBI_LPAR = 0x6a, +}; + +enum rtl8168_8101_registers { + CSIDR = 0x64, + CSIAR = 0x68, +#define CSIAR_FLAG 0x80000000 +#define CSIAR_WRITE_CMD 0x80000000 +#define CSIAR_BYTE_ENABLE 0x0f +#define CSIAR_BYTE_ENABLE_SHIFT 12 +#define CSIAR_ADDR_MASK 0x0fff + + EPHYAR = 0x80, +#define EPHYAR_FLAG 0x80000000 +#define EPHYAR_WRITE_CMD 0x80000000 +#define EPHYAR_REG_MASK 0x1f +#define EPHYAR_REG_SHIFT 16 +#define EPHYAR_DATA_MASK 0xffff + DBG_REG = 0xd1, +#define FIX_NAK_1 (1 << 4) +#define FIX_NAK_2 (1 << 3) +}; + +enum rtl_register_content { + /* InterruptStatusBits */ + SYSErr = 0x8000, + PCSTimeout = 0x4000, + SWInt = 0x0100, + TxDescUnavail = 0x0080, + RxFIFOOver = 0x0040, + LinkChg = 0x0020, + RxOverflow = 0x0010, + TxErr = 0x0008, + TxOK = 0x0004, + RxErr = 0x0002, + RxOK = 0x0001, + + /* RxStatusDesc */ + RxFOVF = (1 << 23), + RxRWT = (1 << 22), + RxRES = (1 << 21), + RxRUNT = (1 << 20), + RxCRC = (1 << 19), + + /* ChipCmdBits */ + CmdReset = 0x10, + CmdRxEnb = 0x08, + CmdTxEnb = 0x04, + RxBufEmpty = 0x01, + + /* TXPoll register p.5 */ + HPQ = 0x80, /* Poll cmd on the high prio queue */ + NPQ = 0x40, /* Poll cmd on the low prio queue */ + FSWInt = 0x01, /* Forced software interrupt */ + + /* 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 */ + + /* Config1 register p.24 */ + LEDS1 = (1 << 7), + LEDS0 = (1 << 6), + MSIEnable = (1 << 5), /* Enable Message Signaled Interrupt */ + Speed_down = (1 << 4), + MEMMAP = (1 << 3), + IOMAP = (1 << 2), + VPD = (1 << 1), + PMEnable = (1 << 0), /* Power Management Enable */ + + /* Config2 register p. 25 */ + PCI_Clock_66MHz = 0x01, + PCI_Clock_33MHz = 0x00, + + /* Config3 register p.25 */ + MagicPacket = (1 << 5), /* Wake up when receives a Magic Packet */ + LinkUp = (1 << 4), /* Wake up when the cable connection is re-established */ + Beacon_en = (1 << 0), /* 8168 only. Reserved in the 8168b */ + + /* Config5 register p.27 */ + BWF = (1 << 6), /* Accept Broadcast wakeup frame */ + MWF = (1 << 5), /* Accept Multicast wakeup frame */ + UWF = (1 << 4), /* Accept Unicast wakeup frame */ + LanWake = (1 << 1), /* LanWake enable/disable */ + PMEStatus = (1 << 0), /* PME status can be reset by PCI RST# */ + + /* TBICSR p.28 */ + TBIReset = 0x80000000, + TBILoopback = 0x40000000, + TBINwEnable = 0x20000000, + TBINwRestart = 0x10000000, + TBILinkOk = 0x02000000, + TBINwComplete = 0x01000000, + + /* CPlusCmd p.31 */ + EnableBist = (1 << 15), // 8168 8101 + Mac_dbgo_oe = (1 << 14), // 8168 8101 + Normal_mode = (1 << 13), // unused + Force_half_dup = (1 << 12), // 8168 8101 + Force_rxflow_en = (1 << 11), // 8168 8101 + Force_txflow_en = (1 << 10), // 8168 8101 + Cxpl_dbg_sel = (1 << 9), // 8168 8101 + ASF = (1 << 8), // 8168 8101 + PktCntrDisable = (1 << 7), // 8168 8101 + Mac_dbgo_sel = 0x001c, // 8168 + RxVlan = (1 << 6), + RxChkSum = (1 << 5), + PCIDAC = (1 << 4), + PCIMulRW = (1 << 3), + INTT_0 = 0x0000, // 8168 + INTT_1 = 0x0001, // 8168 + INTT_2 = 0x0002, // 8168 + INTT_3 = 0x0003, // 8168 + + /* rtl8169_PHYstatus */ + TBI_Enable = 0x80, + TxFlowCtrl = 0x40, + RxFlowCtrl = 0x20, + _1000bpsF = 0x10, + _100bps = 0x08, + _10bps = 0x04, + LinkStatus = 0x02, + FullDup = 0x01, + + /* _TBICSRBit */ + TBILinkOK = 0x02000000, + + /* DumpCounterCommand */ + CounterDump = 0x8, +}; + +enum desc_status_bit { + DescOwn = (1 << 31), /* Descriptor is owned by NIC */ + RingEnd = (1 << 30), /* End of descriptor ring */ + FirstFrag = (1 << 29), /* First segment of a packet */ + LastFrag = (1 << 28), /* Final segment of a packet */ + + /* Tx private */ + LargeSend = (1 << 27), /* TCP Large Send Offload (TSO) */ + MSSShift = 16, /* MSS value position */ + MSSMask = 0xfff, /* MSS value + LargeSend bit: 12 bits */ + IPCS = (1 << 18), /* Calculate IP checksum */ + UDPCS = (1 << 17), /* Calculate UDP/IP checksum */ + TCPCS = (1 << 16), /* Calculate TCP/IP checksum */ + TxVlanTag = (1 << 17), /* Add VLAN tag */ + + /* Rx private */ + PID1 = (1 << 18), /* Protocol ID bit 1/2 */ + PID0 = (1 << 17), /* Protocol ID bit 2/2 */ + +#define RxProtoUDP (PID1) +#define RxProtoTCP (PID0) +#define RxProtoIP (PID1 | PID0) +#define RxProtoMask RxProtoIP + + IPFail = (1 << 16), /* IP checksum failed */ + UDPFail = (1 << 15), /* UDP/IP checksum failed */ + TCPFail = (1 << 14), /* TCP/IP checksum failed */ + RxVlanTag = (1 << 16), /* VLAN tag available */ +}; + +#define RsvdMask 0x3fffc000 + +struct TxDesc { + volatile uint32_t opts1; + volatile uint32_t opts2; + volatile uint32_t addr_lo; + volatile uint32_t addr_hi; +}; + +struct RxDesc { + volatile uint32_t opts1; + volatile uint32_t opts2; + volatile uint32_t addr_lo; + volatile uint32_t addr_hi; +}; + +enum features { + RTL_FEATURE_WOL = (1 << 0), + RTL_FEATURE_MSI = (1 << 1), + RTL_FEATURE_GMII = (1 << 2), +}; + +static void rtl_hw_start_8169(struct net_device *); +static void rtl_hw_start_8168(struct net_device *); +static void rtl_hw_start_8101(struct net_device *); + +struct rtl8169_private { + + struct pci_device *pci_dev; + struct net_device *netdev; + uint8_t *hw_addr; + void *mmio_addr; + uint32_t irqno; + + int chipset; + int mac_version; + int cfg_index; + u16 intr_event; + + struct io_buffer *tx_iobuf[NUM_TX_DESC]; + struct io_buffer *rx_iobuf[NUM_RX_DESC]; + + struct TxDesc *tx_base; + struct RxDesc *rx_base; + + uint32_t tx_curr; + uint32_t rx_curr; + + uint32_t tx_tail; + + uint32_t tx_fill_ctr; + + u16 cp_cmd; + + int phy_auto_nego_reg; + int phy_1000_ctrl_reg; + + int ( *set_speed ) (struct net_device *, u8 autoneg, u16 speed, u8 duplex ); + void ( *phy_reset_enable ) ( void *ioaddr ); + void ( *hw_start ) ( struct net_device * ); + unsigned int ( *phy_reset_pending ) ( void *ioaddr ); + unsigned int ( *link_ok ) ( void *ioaddr ); + + int pcie_cap; + + unsigned features; + +}; + +static const unsigned int rtl8169_rx_config = + (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift); + +#endif /* _R8169_H_ */ + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/rtl8139.c b/gpxe/src/drivers/net/rtl8139.c new file mode 100644 index 0000000..2dff324 --- /dev/null +++ b/gpxe/src/drivers/net/rtl8139.c @@ -0,0 +1,587 @@ +/* rtl8139.c - etherboot driver for the Realtek 8139 chipset + + ported from the linux driver written by Donald Becker + by Rainer Bawidamann (Rainer.Bawidamann@informatik.uni-ulm.de) 1999 + + This software may be used and distributed according to the terms + of the GNU Public License, incorporated herein by reference. + + changes to the original driver: + - removed support for interrupts, switching to polling mode (yuck!) + - removed support for the 8129 chip (external MII) + +*/ + +/*********************************************************************/ +/* Revision History */ +/*********************************************************************/ + +/* + 27 May 2006 mcb30@users.sourceforge.net (Michael Brown) + Rewrote to use the new net driver API, the updated PCI API, and + the generic three-wire serial device support for EEPROM access. + + 28 Dec 2002 ken_yap@users.sourceforge.net (Ken Yap) + Put in virt_to_bus calls to allow Etherboot relocation. + + 06 Apr 2001 ken_yap@users.sourceforge.net (Ken Yap) + Following email from Hyun-Joon Cha, added a disable routine, otherwise + NIC remains live and can crash the kernel later. + + 4 Feb 2000 espenlaub@informatik.uni-ulm.de (Klaus Espenlaub) + Shuffled things around, removed the leftovers from the 8129 support + that was in the Linux driver and added a bit more 8139 definitions. + Moved the 8K receive buffer to a fixed, available address outside the + 0x98000-0x9ffff range. This is a bit of a hack, but currently the only + way to make room for the Etherboot features that need substantial amounts + of code like the ANSI console support. Currently the buffer is just below + 0x10000, so this even conforms to the tagged boot image specification, + which reserves the ranges 0x00000-0x10000 and 0x98000-0xA0000. My + interpretation of this "reserved" is that Etherboot may do whatever it + likes, as long as its environment is kept intact (like the BIOS + variables). Hopefully fixed rtl_poll() once and for all. The symptoms + were that if Etherboot was left at the boot menu for several minutes, the + first eth_poll failed. Seems like I am the only person who does this. + First of all I fixed the debugging code and then set out for a long bug + hunting session. It took me about a week full time work - poking around + various places in the driver, reading Don Becker's and Jeff Garzik's Linux + driver and even the FreeBSD driver (what a piece of crap!) - and + eventually spotted the nasty thing: the transmit routine was acknowledging + each and every interrupt pending, including the RxOverrun and RxFIFIOver + interrupts. This confused the RTL8139 thoroughly. It destroyed the + Rx ring contents by dumping the 2K FIFO contents right where we wanted to + get the next packet. Oh well, what fun. + + 18 Jan 2000 mdc@etherboot.org (Marty Connor) + Drastically simplified error handling. Basically, if any error + in transmission or reception occurs, the card is reset. + Also, pointed all transmit descriptors to the same buffer to + save buffer space. This should decrease driver size and avoid + corruption because of exceeding 32K during runtime. + + 28 Jul 1999 (Matthias Meixner - meixner@rbg.informatik.tu-darmstadt.de) + rtl_poll was quite broken: it used the RxOK interrupt flag instead + of the RxBufferEmpty flag which often resulted in very bad + transmission performace - below 1kBytes/s. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TX_RING_SIZE 4 + +struct rtl8139_tx { + unsigned int next; + struct io_buffer *iobuf[TX_RING_SIZE]; +}; + +struct rtl8139_rx { + void *ring; + unsigned int offset; +}; + +struct rtl8139_nic { + unsigned short ioaddr; + struct rtl8139_tx tx; + struct rtl8139_rx rx; + struct spi_bit_basher spibit; + struct spi_device eeprom; + struct nvo_block nvo; +}; + +/* Tuning Parameters */ +#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */ +#define RX_FIFO_THRESH 4 /* Rx buffer level before first PCI xfer. */ +#define RX_DMA_BURST 4 /* Maximum PCI burst, '4' is 256 bytes */ +#define TX_DMA_BURST 4 /* Calculate as 16<ioaddr + Cfg9346 ); + return ( eereg & mask ); +} + +static void rtl_spi_write_bit ( struct bit_basher *basher, + unsigned int bit_id, unsigned long data ) { + struct rtl8139_nic *rtl = container_of ( basher, struct rtl8139_nic, + spibit.basher ); + uint8_t mask = rtl_ee_bits[bit_id]; + uint8_t eereg; + + eereg = inb ( rtl->ioaddr + Cfg9346 ); + eereg &= ~mask; + eereg |= ( data & mask ); + outb ( eereg, rtl->ioaddr + Cfg9346 ); +} + +static struct bit_basher_operations rtl_basher_ops = { + .read = rtl_spi_read_bit, + .write = rtl_spi_write_bit, +}; + +/** Portion of EEPROM available for non-volatile stored options + * + * We use offset 0x40 (i.e. address 0x20), length 0x40. This block is + * marked as VPD in the rtl8139 datasheets, so we use it only if we + * detect that the card is not supporting VPD. + */ +static struct nvo_fragment rtl_nvo_fragments[] = { + { 0x20, 0x40 }, + { 0, 0 } +}; + +/** + * Set up for EEPROM access + * + * @v netdev Net device + */ +static void rtl_init_eeprom ( struct net_device *netdev ) { + struct rtl8139_nic *rtl = netdev->priv; + int ee9356; + int vpd; + + /* Initialise three-wire bus */ + rtl->spibit.basher.op = &rtl_basher_ops; + rtl->spibit.bus.mode = SPI_MODE_THREEWIRE; + init_spi_bit_basher ( &rtl->spibit ); + + /* Detect EEPROM type and initialise three-wire device */ + ee9356 = ( inw ( rtl->ioaddr + RxConfig ) & Eeprom9356 ); + if ( ee9356 ) { + DBG ( "EEPROM is an AT93C56\n" ); + init_at93c56 ( &rtl->eeprom, 16 ); + } else { + DBG ( "EEPROM is an AT93C46\n" ); + init_at93c46 ( &rtl->eeprom, 16 ); + } + rtl->eeprom.bus = &rtl->spibit.bus; + + /* Initialise space for non-volatile options, if available */ + vpd = ( inw ( rtl->ioaddr + Config1 ) & VPDEnable ); + if ( vpd ) { + DBG ( "EEPROM in use for VPD; cannot use for options\n" ); + } else { + nvo_init ( &rtl->nvo, &rtl->eeprom.nvs, rtl_nvo_fragments, + &netdev->refcnt ); + } +} + +/** + * Reset NIC + * + * @v netdev Net device + * + * Issues a hardware reset and waits for the reset to complete. + */ +static void rtl_reset ( struct net_device *netdev ) { + struct rtl8139_nic *rtl = netdev->priv; + + /* Reset chip */ + outb ( CmdReset, rtl->ioaddr + ChipCmd ); + mdelay ( 10 ); + memset ( &rtl->tx, 0, sizeof ( rtl->tx ) ); + rtl->rx.offset = 0; +} + +/** + * Open NIC + * + * @v netdev Net device + * @ret rc Return status code + */ +static int rtl_open ( struct net_device *netdev ) { + struct rtl8139_nic *rtl = netdev->priv; + int i; + + /* Program the MAC address */ + for ( i = 0 ; i < ETH_ALEN ; i++ ) + outb ( netdev->ll_addr[i], rtl->ioaddr + MAC0 + i ); + + /* Set up RX ring */ + rtl->rx.ring = malloc ( RX_BUF_LEN + RX_BUF_PAD ); + if ( ! rtl->rx.ring ) + return -ENOMEM; + outl ( virt_to_bus ( rtl->rx.ring ), rtl->ioaddr + RxBuf ); + DBG ( "RX ring at %lx\n", virt_to_bus ( rtl->rx.ring ) ); + + /* Enable TX and RX */ + outb ( ( CmdRxEnb | CmdTxEnb ), rtl->ioaddr + ChipCmd ); + outl ( ( ( RX_FIFO_THRESH << 13 ) | ( RX_BUF_LEN_IDX << 11 ) | + ( RX_DMA_BURST << 8 ) | AcceptBroadcast | AcceptMulticast | + AcceptMyPhys ), rtl->ioaddr + RxConfig ); + outl ( 0xffffffffUL, rtl->ioaddr + MAR0 + 0 ); + outl ( 0xffffffffUL, rtl->ioaddr + MAR0 + 4 ); + outl ( ( ( TX_DMA_BURST << 8 ) | ( TX_IPG << 24 ) ), + rtl->ioaddr + TxConfig ); + + return 0; +} + +/** + * Close NIC + * + * @v netdev Net device + */ +static void rtl_close ( struct net_device *netdev ) { + struct rtl8139_nic *rtl = netdev->priv; + + /* Reset the hardware to disable everything in one go */ + rtl_reset ( netdev ); + + /* Free RX ring */ + free ( rtl->rx.ring ); + rtl->rx.ring = NULL; +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int rtl_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct rtl8139_nic *rtl = netdev->priv; + + /* Check for space in TX ring */ + if ( rtl->tx.iobuf[rtl->tx.next] != NULL ) { + DBG ( "TX overflow\n" ); + return -ENOBUFS; + } + + /* Pad and align packet */ + iob_pad ( iobuf, ETH_ZLEN ); + + /* Add to TX ring */ + DBG ( "TX id %d at %lx+%zx\n", rtl->tx.next, + virt_to_bus ( iobuf->data ), iob_len ( iobuf ) ); + rtl->tx.iobuf[rtl->tx.next] = iobuf; + outl ( virt_to_bus ( iobuf->data ), + rtl->ioaddr + TxAddr0 + 4 * rtl->tx.next ); + outl ( ( ( ( TX_FIFO_THRESH & 0x7e0 ) << 11 ) | iob_len ( iobuf ) ), + rtl->ioaddr + TxStatus0 + 4 * rtl->tx.next ); + rtl->tx.next = ( rtl->tx.next + 1 ) % TX_RING_SIZE; + + return 0; +} + +/** + * Poll for received packets + * + * @v netdev Network device + */ +static void rtl_poll ( struct net_device *netdev ) { + struct rtl8139_nic *rtl = netdev->priv; + unsigned int status; + unsigned int tsad; + unsigned int rx_status; + unsigned int rx_len; + struct io_buffer *rx_iob; + int wrapped_len; + int i; + + /* Acknowledge interrupts */ + status = inw ( rtl->ioaddr + IntrStatus ); + if ( ! status ) + return; + outw ( status, rtl->ioaddr + IntrStatus ); + + /* Handle TX completions */ + tsad = inw ( rtl->ioaddr + TxSummary ); + for ( i = 0 ; i < TX_RING_SIZE ; i++ ) { + if ( ( rtl->tx.iobuf[i] != NULL ) && ( tsad & ( 1 << i ) ) ) { + DBG ( "TX id %d complete\n", i ); + netdev_tx_complete ( netdev, rtl->tx.iobuf[i] ); + rtl->tx.iobuf[i] = NULL; + } + } + + /* Handle received packets */ + while ( ! ( inw ( rtl->ioaddr + ChipCmd ) & RxBufEmpty ) ) { + rx_status = * ( ( uint16_t * ) + ( rtl->rx.ring + rtl->rx.offset ) ); + rx_len = * ( ( uint16_t * ) + ( rtl->rx.ring + rtl->rx.offset + 2 ) ); + if ( rx_status & RxOK ) { + DBG ( "RX packet at offset %x+%x\n", rtl->rx.offset, + rx_len ); + + rx_iob = alloc_iob ( rx_len ); + if ( ! rx_iob ) { + netdev_rx_err ( netdev, NULL, -ENOMEM ); + /* Leave packet for next call to poll() */ + break; + } + + wrapped_len = ( ( rtl->rx.offset + 4 + rx_len ) + - RX_BUF_LEN ); + if ( wrapped_len < 0 ) + wrapped_len = 0; + + memcpy ( iob_put ( rx_iob, rx_len - wrapped_len ), + rtl->rx.ring + rtl->rx.offset + 4, + rx_len - wrapped_len ); + memcpy ( iob_put ( rx_iob, wrapped_len ), + rtl->rx.ring, wrapped_len ); + + netdev_rx ( netdev, rx_iob ); + } else { + DBG ( "RX bad packet (status %#04x len %d)\n", + rx_status, rx_len ); + netdev_rx_err ( netdev, NULL, -EINVAL ); + } + rtl->rx.offset = ( ( ( rtl->rx.offset + 4 + rx_len + 3 ) & ~3 ) + % RX_BUF_LEN ); + outw ( rtl->rx.offset - 16, rtl->ioaddr + RxBufPtr ); + } +} + +/** + * Enable/disable interrupts + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ +static void rtl_irq ( struct net_device *netdev, int enable ) { + struct rtl8139_nic *rtl = netdev->priv; + + outw ( ( enable ? ( ROK | RER | TOK | TER ) : 0 ), + rtl->ioaddr + IntrMask ); +} + +/** RTL8139 net device operations */ +static struct net_device_operations rtl_operations = { + .open = rtl_open, + .close = rtl_close, + .transmit = rtl_transmit, + .poll = rtl_poll, + .irq = rtl_irq, +}; + +/** + * Probe PCI device + * + * @v pci PCI device + * @v id PCI ID + * @ret rc Return status code + */ +static int rtl_probe ( struct pci_device *pci, + const struct pci_device_id *id __unused ) { + struct net_device *netdev; + struct rtl8139_nic *rtl; + int rc; + + /* Allocate net device */ + netdev = alloc_etherdev ( sizeof ( *rtl ) ); + if ( ! netdev ) + return -ENOMEM; + netdev_init ( netdev, &rtl_operations ); + rtl = netdev->priv; + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + memset ( rtl, 0, sizeof ( *rtl ) ); + rtl->ioaddr = pci->ioaddr; + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Reset the NIC, set up EEPROM access and read MAC address */ + rtl_reset ( netdev ); + rtl_init_eeprom ( netdev ); + nvs_read ( &rtl->eeprom.nvs, EE_MAC, netdev->ll_addr, ETH_ALEN ); + + /* Mark as link up; we don't yet handle link state */ + netdev_link_up ( netdev ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + /* Register non-volatile storage */ + if ( rtl->nvo.nvs ) { + if ( ( rc = register_nvo ( &rtl->nvo, + netdev_settings ( netdev ) ) ) != 0) + goto err_register_nvo; + } + + return 0; + + err_register_nvo: + unregister_netdev ( netdev ); + err_register_netdev: + rtl_reset ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void rtl_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct rtl8139_nic *rtl = netdev->priv; + + if ( rtl->nvo.nvs ) + unregister_nvo ( &rtl->nvo ); + unregister_netdev ( netdev ); + rtl_reset ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +static struct pci_device_id rtl8139_nics[] = { +PCI_ROM(0x10ec, 0x8129, "rtl8129", "Realtek 8129"), +PCI_ROM(0x10ec, 0x8139, "rtl8139", "Realtek 8139"), +PCI_ROM(0x10ec, 0x8138, "rtl8139b", "Realtek 8139B"), +PCI_ROM(0x1186, 0x1300, "dfe538", "DFE530TX+/DFE538TX"), +PCI_ROM(0x1113, 0x1211, "smc1211-1", "SMC EZ10/100"), +PCI_ROM(0x1112, 0x1211, "smc1211", "SMC EZ10/100"), +PCI_ROM(0x1500, 0x1360, "delta8139", "Delta Electronics 8139"), +PCI_ROM(0x4033, 0x1360, "addtron8139", "Addtron Technology 8139"), +PCI_ROM(0x1186, 0x1340, "dfe690txd", "D-Link DFE690TXD"), +PCI_ROM(0x13d1, 0xab06, "fe2000vx", "AboCom FE2000VX"), +PCI_ROM(0x1259, 0xa117, "allied8139", "Allied Telesyn 8139"), +PCI_ROM(0x14ea, 0xab06, "fnw3603tx", "Planex FNW-3603-TX"), +PCI_ROM(0x14ea, 0xab07, "fnw3800tx", "Planex FNW-3800-TX"), +PCI_ROM(0xffff, 0x8139, "clone-rtl8139", "Cloned 8139"), +}; + +struct pci_driver rtl8139_driver __pci_driver = { + .ids = rtl8139_nics, + .id_count = ( sizeof ( rtl8139_nics ) / sizeof ( rtl8139_nics[0] ) ), + .probe = rtl_probe, + .remove = rtl_remove, +}; diff --git a/gpxe/src/drivers/net/sis900.c b/gpxe/src/drivers/net/sis900.c new file mode 100644 index 0000000..008b9cf --- /dev/null +++ b/gpxe/src/drivers/net/sis900.c @@ -0,0 +1,1296 @@ +/* -*- Mode:C; c-basic-offset:4; -*- */ + +/* + sis900.c: An SiS 900/7016 PCI Fast Ethernet driver for Etherboot + Copyright (C) 2001 Entity Cyber, Inc. + + Revision: 1.0 March 1, 2001 + + Author: Marty Connor (mdc@etherboot.org) + + Adapted from a Linux driver which was written by Donald Becker + and modified by Ollie Lho and Chin-Shan Li of SiS Corporation. + Rewritten for Etherboot by Marty Connor. + + This software may be used and distributed according to the terms + of the GNU Public License (GPL), incorporated herein by reference. + + References: + SiS 7016 Fast Ethernet PCI Bus 10/100 Mbps LAN Controller with OnNow Support, + preliminary Rev. 1.0 Jan. 14, 1998 + SiS 900 Fast Ethernet PCI Bus 10/100 Mbps LAN Single Chip with OnNow Support, + preliminary Rev. 1.0 Nov. 10, 1998 + SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution, + preliminary Rev. 1.0 Jan. 18, 1998 + http://www.sis.com.tw/support/databook.htm */ + +/* Revision History */ + +/* + 07 Dec 2003 timlegge - Enabled Multicast Support + 06 Dec 2003 timlegge - Fixed relocation issue in 5.2 + 04 Jan 2002 Chien-Yu Chen, Doug Ambrisko, Marty Connor Patch to Etherboot 5.0.5 + Added support for the SiS 630ET plus various bug fixes from linux kernel + source 2.4.17. + 01 March 2001 mdc 1.0 + Initial Release. Tested with PCI based sis900 card and ThinkNIC + computer. + 20 March 2001 P.Koegel + added support for sis630e and PHY ICS1893 and RTL8201 + Testet with SIS730S chipset + ICS1893 +*/ + + +/* Includes */ + +#include "etherboot.h" +#include +#include "nic.h" + +#include "sis900.h" + +/* Globals */ + +static struct nic_operations sis900_operations; + +static int sis900_debug = 0; + +static unsigned short vendor, dev_id; +static unsigned long ioaddr; +static u8 pci_revision; + +static unsigned int cur_phy; + +static unsigned int cur_rx; + +struct { + BufferDesc txd; + BufferDesc rxd[NUM_RX_DESC]; + unsigned char txb[TX_BUF_SIZE]; + unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE]; +} sis900_bufs __shared; +#define txd sis900_bufs.txd +#define rxd sis900_bufs.rxd +#define txb sis900_bufs.txb +#define rxb sis900_bufs.rxb + +#if 0 +static struct mac_chip_info { + const char *name; + u16 vendor_id, device_id, flags; + int io_size; +} mac_chip_table[] = { + { "SiS 900 PCI Fast Ethernet", PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS900, + PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE}, + { "SiS 7016 PCI Fast Ethernet",PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS7016, + PCI_COMMAND_IO|PCI_COMMAND_MASTER, SIS900_TOTAL_SIZE}, + {0,0,0,0,0} /* 0 terminated list. */ +}; +#endif + +static void sis900_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex); +static void amd79c901_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex); +static void ics1893_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex); +static void rtl8201_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex); +static void vt6103_read_mode(struct nic *nic, int phy_addr, int *speed, int *duplex); + +static struct mii_chip_info { + const char * name; + u16 phy_id0; + u16 phy_id1; + void (*read_mode) (struct nic *nic, int phy_addr, int *speed, int *duplex); +} mii_chip_table[] = { + {"SiS 900 Internal MII PHY", 0x001d, 0x8000, sis900_read_mode}, + {"SiS 7014 Physical Layer Solution", 0x0016, 0xf830,sis900_read_mode}, + {"SiS 900 on Foxconn 661 7MI", 0x0143, 0xBC70, sis900_read_mode}, + {"AMD 79C901 10BASE-T PHY", 0x0000, 0x6B70, amd79c901_read_mode}, + {"AMD 79C901 HomePNA PHY", 0x0000, 0x6B90, amd79c901_read_mode}, + {"ICS 1893 Integrated PHYceiver" , 0x0015, 0xf440,ics1893_read_mode}, +// {"NS 83851 PHY",0x2000, 0x5C20, MIX }, + {"RTL 8201 10/100Mbps Phyceiver" , 0x0000, 0x8200,rtl8201_read_mode}, + {"VIA 6103 10/100Mbps Phyceiver", 0x0101, 0x8f20,vt6103_read_mode}, + {0,0,0,0} +}; + +static struct mii_phy { + struct mii_phy * next; + struct mii_chip_info * chip_info; + int phy_addr; + u16 status; +} mii; + + + +#if 0 +// PCI to ISA bridge for SIS640E access +static struct pci_device_id pci_isa_bridge_list[] = { + { .vendor = 0x1039, .device = 0x0008, + .name = "SIS 85C503/5513 PCI to ISA bridge"}, +}; + +PCI_DRIVER( sis_bridge_pci_driver, pci_isa_bridge_list, PCI_NO_CLASS ); + +static struct device_driver sis_bridge_driver = { + .name = "SIS ISA bridge", + .bus_driver = &pci_driver, + .bus_driver_info = ( struct bus_driver_info * ) &sis_bridge_pci_driver, +}; +#endif + +/* Function Prototypes */ + +static int sis900_probe(struct nic *nic,struct pci_device *pci); + +static u16 sis900_read_eeprom(int location); +static void sis900_mdio_reset(long mdio_addr); +static void sis900_mdio_idle(long mdio_addr); +static u16 sis900_mdio_read(int phy_id, int location); +#if 0 +static void sis900_mdio_write(int phy_id, int location, int val); +#endif +static void sis900_init(struct nic *nic); + +static void sis900_reset(struct nic *nic); + +static void sis900_init_rxfilter(struct nic *nic); +static void sis900_init_txd(struct nic *nic); +static void sis900_init_rxd(struct nic *nic); +static void sis900_set_rx_mode(struct nic *nic); +static void sis900_check_mode(struct nic *nic); + +static void sis900_transmit(struct nic *nic, const char *d, + unsigned int t, unsigned int s, const char *p); +static int sis900_poll(struct nic *nic, int retrieve); + +static void sis900_disable(struct nic *nic); + +static void sis900_irq(struct nic *nic, irq_action_t action); + +/** + * sis900_get_mac_addr: - Get MAC address for stand alone SiS900 model + * @pci_dev: the sis900 pci device + * @net_dev: the net device to get address for + * + * Older SiS900 and friends, use EEPROM to store MAC address. + * MAC address is read from read_eeprom() into @net_dev->dev_addr. + */ + +static int sis900_get_mac_addr(struct pci_device * pci_dev __unused, struct nic *nic) +{ + u16 signature; + int i; + + /* check to see if we have sane EEPROM */ + signature = (u16) sis900_read_eeprom( EEPROMSignature); + if (signature == 0xffff || signature == 0x0000) { + printf ("sis900_probe: Error EERPOM read %hX\n", signature); + return 0; + } + + /* get MAC address from EEPROM */ + for (i = 0; i < 3; i++) + ((u16 *)(nic->node_addr))[i] = sis900_read_eeprom(i+EEPROMMACAddr); + return 1; +} + +/** + * sis96x_get_mac_addr: - Get MAC address for SiS962 or SiS963 model + * @pci_dev: the sis900 pci device + * @net_dev: the net device to get address for + * + * SiS962 or SiS963 model, use EEPROM to store MAC address. And EEPROM + * is shared by + * LAN and 1394. When access EEPROM, send EEREQ signal to hardware first + * and wait for EEGNT. If EEGNT is ON, EEPROM is permitted to be access + * by LAN, otherwise is not. After MAC address is read from EEPROM, send + * EEDONE signal to refuse EEPROM access by LAN. + * The EEPROM map of SiS962 or SiS963 is different to SiS900. + * The signature field in SiS962 or SiS963 spec is meaningless. + * MAC address is read into @net_dev->dev_addr. + */ + +static int sis96x_get_mac_addr(struct pci_device * pci_dev __unused, struct nic *nic) +{ +/* long ioaddr = net_dev->base_addr; */ + long ee_addr = ioaddr + mear; + u32 waittime = 0; + int i; + + printf("Alternate function\n"); + + outl(EEREQ, ee_addr); + while(waittime < 2000) { + if(inl(ee_addr) & EEGNT) { + + /* get MAC address from EEPROM */ + for (i = 0; i < 3; i++) + ((u16 *)(nic->node_addr))[i] = sis900_read_eeprom(i+EEPROMMACAddr); + + outl(EEDONE, ee_addr); + return 1; + } else { + udelay(1); + waittime ++; + } + } + outl(EEDONE, ee_addr); + return 0; +} + +/** + * sis630e_get_mac_addr: - Get MAC address for SiS630E model + * @pci_dev: the sis900 pci device + * @net_dev: the net device to get address for + * + * SiS630E model, use APC CMOS RAM to store MAC address. + * APC CMOS RAM is accessed through ISA bridge. + * MAC address is read into @net_dev->dev_addr. + */ + +static int sis630e_get_mac_addr(struct pci_device * pci_dev __unused, struct nic *nic) +{ +#if 0 + u8 reg; + int i; + struct bus_loc bus_loc; + union { + struct bus_dev bus_dev; + struct pci_device isa_bridge; + } u; + + /* find PCI to ISA bridge */ + memset(&bus_loc, 0, sizeof(bus_loc)); + if ( ! find_by_driver ( &bus_loc, &u.bus_dev, &sis_bridge_driver, 0 ) ) + return 0; + + pci_read_config_byte(&u.isa_bridge, 0x48, ®); + pci_write_config_byte(&u.isa_bridge, 0x48, reg | 0x40); + + for (i = 0; i < ETH_ALEN; i++) + { + outb(0x09 + i, 0x70); + ((u8 *)(nic->node_addr))[i] = inb(0x71); + } + pci_write_config_byte(&u.isa_bridge, 0x48, reg & ~0x40); + + return 1; +#endif + + /* Does not work with current bus/device model */ + memset ( nic->node_addr, 0, sizeof ( nic->node_addr ) ); + return 0; +} + +/** + * sis630e_get_mac_addr: - Get MAC address for SiS630E model + * @pci_dev: the sis900 pci device + * @net_dev: the net device to get address for + * + * SiS630E model, use APC CMOS RAM to store MAC address. + * APC CMOS RAM is accessed through ISA bridge. + * MAC address is read into @net_dev->dev_addr. + */ + +static int sis635_get_mac_addr(struct pci_device * pci_dev __unused, struct nic *nic) +{ + u32 rfcrSave; + u32 i; + + + rfcrSave = inl(rfcr + ioaddr); + + outl(rfcrSave | RELOAD, ioaddr + cr); + outl(0, ioaddr + cr); + + /* disable packet filtering before setting filter */ + outl(rfcrSave & ~RFEN, rfcr + ioaddr); + + /* load MAC addr to filter data register */ + for (i = 0 ; i < 3 ; i++) { + outl((i << RFADDR_shift), ioaddr + rfcr); + *( ((u16 *)nic->node_addr) + i) = inw(ioaddr + rfdr); + } + + /* enable packet filitering */ + outl(rfcrSave | RFEN, rfcr + ioaddr); + + return 1; +} + +/* + * Function: sis900_probe + * + * Description: initializes initializes the NIC, retrieves the + * MAC address of the card, and sets up some globals required by + * other routines. + * + * Side effects: + * leaves the ioaddress of the sis900 chip in the variable ioaddr. + * leaves the sis900 initialized, and ready to recieve packets. + * + * Returns: struct nic *: pointer to NIC data structure + */ + +static int sis900_probe ( struct nic *nic, struct pci_device *pci ) { + + int i; + int found=0; + int phy_addr; + u8 revision; + int ret; + + if (pci->ioaddr == 0) + return 0; + + nic->irqno = 0; + nic->ioaddr = pci->ioaddr; + + ioaddr = pci->ioaddr; + vendor = pci->vendor; + dev_id = pci->device; + + /* wakeup chip */ + pci_write_config_dword(pci, 0x40, 0x00000000); + + adjust_pci_device(pci); + + /* get MAC address */ + ret = 0; + pci_read_config_byte(pci, PCI_REVISION, &revision); + + /* save for use later in sis900_reset() */ + pci_revision = revision; + + if (revision == SIS630E_900_REV) + ret = sis630e_get_mac_addr(pci, nic); + else if ((revision > 0x81) && (revision <= 0x90)) + ret = sis635_get_mac_addr(pci, nic); + else if (revision == SIS96x_900_REV) + ret = sis96x_get_mac_addr(pci, nic); + else + ret = sis900_get_mac_addr(pci, nic); + + if (ret == 0) + { + printf ("sis900_probe: Error MAC address not found\n"); + return 0; + } + + /* 630ET : set the mii access mode as software-mode */ + if (revision == SIS630ET_900_REV) + outl(ACCESSMODE | inl(ioaddr + cr), ioaddr + cr); + + DBG( "sis900_probe: Vendor:%#hX Device:%#hX\n", vendor, dev_id ); + + /* probe for mii transceiver */ + /* search for total of 32 possible mii phy addresses */ + + found = 0; + for (phy_addr = 0; phy_addr < 32; phy_addr++) { + u16 mii_status; + u16 phy_id0, phy_id1; + + mii_status = sis900_mdio_read(phy_addr, MII_STATUS); + if (mii_status == 0xffff || mii_status == 0x0000) + /* the mii is not accessable, try next one */ + continue; + + phy_id0 = sis900_mdio_read(phy_addr, MII_PHY_ID0); + phy_id1 = sis900_mdio_read(phy_addr, MII_PHY_ID1); + + /* search our mii table for the current mii */ + for (i = 0; mii_chip_table[i].phy_id1; i++) { + + if ((phy_id0 == mii_chip_table[i].phy_id0) && + ((phy_id1 & 0xFFF0) == mii_chip_table[i].phy_id1)){ + + printf("sis900_probe: %s transceiver found at address %d.\n", + mii_chip_table[i].name, phy_addr); + + mii.chip_info = &mii_chip_table[i]; + mii.phy_addr = phy_addr; + mii.status = sis900_mdio_read(phy_addr, MII_STATUS); + mii.next = NULL; + + found=1; + break; + } + } + } + + if (found == 0) { + printf("sis900_probe: No MII transceivers found!\n"); + return 0; + } + + /* Arbitrarily select the last PHY found as current PHY */ + cur_phy = mii.phy_addr; + printf("sis900_probe: Using %s as default\n", mii.chip_info->name); + + /* initialize device */ + sis900_init(nic); + nic->nic_op = &sis900_operations; + + return 1; +} + + + + +/* + * EEPROM Routines: These functions read and write to EEPROM for + * retrieving the MAC address and other configuration information about + * the card. + */ + +/* Delay between EEPROM clock transitions. */ +#define eeprom_delay() inl(ee_addr) + + +/* Function: sis900_read_eeprom + * + * Description: reads and returns a given location from EEPROM + * + * Arguments: int location: requested EEPROM location + * + * Returns: u16: contents of requested EEPROM location + * + */ + +/* Read Serial EEPROM through EEPROM Access Register, Note that location is + in word (16 bits) unit */ +static u16 sis900_read_eeprom(int location) +{ + int i; + u16 retval = 0; + long ee_addr = ioaddr + mear; + u32 read_cmd = location | EEread; + + outl(0, ee_addr); + eeprom_delay(); + outl(EECS, ee_addr); + eeprom_delay(); + + /* Shift the read command (9) bits out. */ + for (i = 8; i >= 0; i--) { + u32 dataval = (read_cmd & (1 << i)) ? EEDI | EECS : EECS; + outl(dataval, ee_addr); + eeprom_delay(); + outl(dataval | EECLK, ee_addr); + eeprom_delay(); + } + outl(EECS, ee_addr); + eeprom_delay(); + + /* read the 16-bits data in */ + for (i = 16; i > 0; i--) { + outl(EECS, ee_addr); + eeprom_delay(); + outl(EECS | EECLK, ee_addr); + eeprom_delay(); + retval = (retval << 1) | ((inl(ee_addr) & EEDO) ? 1 : 0); + eeprom_delay(); + } + + /* Terminate the EEPROM access. */ + outl(0, ee_addr); + eeprom_delay(); +// outl(EECLK, ee_addr); + + return (retval); +} + +#define sis900_mdio_delay() inl(mdio_addr) + + +/* + Read and write the MII management registers using software-generated + serial MDIO protocol. Note that the command bits and data bits are + send out seperately +*/ + +static void sis900_mdio_idle(long mdio_addr) +{ + outl(MDIO | MDDIR, mdio_addr); + sis900_mdio_delay(); + outl(MDIO | MDDIR | MDC, mdio_addr); +} + +/* Syncronize the MII management interface by shifting 32 one bits out. */ +static void sis900_mdio_reset(long mdio_addr) +{ + int i; + + for (i = 31; i >= 0; i--) { + outl(MDDIR | MDIO, mdio_addr); + sis900_mdio_delay(); + outl(MDDIR | MDIO | MDC, mdio_addr); + sis900_mdio_delay(); + } + return; +} + +static u16 sis900_mdio_read(int phy_id, int location) +{ + long mdio_addr = ioaddr + mear; + int mii_cmd = MIIread|(phy_id<= 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]=%p link=%X cmdsts=%X bufptr=%X\n", + i, &rxd[i], (unsigned int) rxd[i].link, (unsigned int) rxd[i].cmdsts, + (unsigned int) rxd[i].bufptr); + } + + /* load Receive Descriptor Register */ + outl(virt_to_bus(&rxd[0]), ioaddr + rxdp); + + if (sis900_debug > 0) + printf("sis900_init_rxd: RX descriptor register loaded with: %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 */ + do { + status = sis900_mdio_read(phy_addr, MII_STSOUT); + } while (i++ < 2); + + *speed = HW_SPEED_10_MBPS; + *duplex = FDX_CAPABLE_HALF_SELECTED; + + if (status & (MII_NWAY_TX | MII_NWAY_TX_FDX)) + *speed = HW_SPEED_100_MBPS; + if (status & ( MII_NWAY_TX_FDX | MII_NWAY_T_FDX)) + *duplex = FDX_CAPABLE_FULL_SELECTED; + + /* Workaround for Realtek RTL8201 PHY issue */ + phy_id0 = sis900_mdio_read(phy_addr, MII_PHY_ID0); + phy_id1 = sis900_mdio_read(phy_addr, MII_PHY_ID1); + if((phy_id0 == 0x0000) && ((phy_id1 & 0xFFF0) == 0x8200)){ + if(sis900_mdio_read(phy_addr, MII_CONTROL) & MII_CNTL_FDX) + *duplex = FDX_CAPABLE_FULL_SELECTED; + if(sis900_mdio_read(phy_addr, 0x0019) & 0x01) + *speed = HW_SPEED_100_MBPS; + } + + if (status & MII_STSOUT_LINK_FAIL) + printf("sis900_read_mode: Media Link Off\n"); + else + printf("sis900_read_mode: Media Link On %s %s-duplex \n", + *speed == HW_SPEED_100_MBPS ? + "100mbps" : "10mbps", + *duplex == FDX_CAPABLE_FULL_SELECTED ? + "full" : "half"); +} + + +/* Function: amd79c901_read_mode + * + * Description: retrieves and displays speed and duplex + * parameters from the NIC + * + * Arguments: struct nic *nic: NIC data structure + * + * Returns: void. + */ + +static void +amd79c901_read_mode(struct nic *nic __unused, int phy_addr, int *speed, int *duplex) +{ + int i; + u16 status; + + for (i = 0; i < 2; i++) + status = sis900_mdio_read(phy_addr, MII_STATUS); + + if (status & MII_STAT_CAN_AUTO) { + /* 10BASE-T PHY */ + for (i = 0; i < 2; i++) + status = sis900_mdio_read(phy_addr, MII_STATUS_SUMMARY); + if (status & MII_STSSUM_SPD) + *speed = HW_SPEED_100_MBPS; + else + *speed = HW_SPEED_10_MBPS; + if (status & MII_STSSUM_DPLX) + *duplex = FDX_CAPABLE_FULL_SELECTED; + else + *duplex = FDX_CAPABLE_HALF_SELECTED; + + if (status & MII_STSSUM_LINK) + printf("amd79c901_read_mode: Media Link On %s %s-duplex \n", + *speed == HW_SPEED_100_MBPS ? + "100mbps" : "10mbps", + *duplex == FDX_CAPABLE_FULL_SELECTED ? + "full" : "half"); + else + printf("amd79c901_read_mode: Media Link Off\n"); + } + else { + /* HomePNA */ + *speed = HW_SPEED_HOME; + *duplex = FDX_CAPABLE_HALF_SELECTED; + if (status & MII_STAT_LINK) + printf("amd79c901_read_mode:Media Link On 1mbps half-duplex \n"); + else + printf("amd79c901_read_mode: Media Link Off\n"); + } +} + + +/** + * ics1893_read_mode: - read media mode for ICS1893 PHY + * @net_dev: the net device to read mode for + * @phy_addr: mii phy address + * @speed: the transmit speed to be determined + * @duplex: the duplex mode to be determined + * + * ICS1893 PHY use Quick Poll Detailed Status register + * to determine the speed and duplex mode for sis900 + */ + +static void ics1893_read_mode(struct nic *nic __unused, int phy_addr, int *speed, int *duplex) +{ + int i = 0; + u32 status; + + /* MII_QPDSTS is Latched, read twice in succession will reflect the current state */ + for (i = 0; i < 2; i++) + status = sis900_mdio_read(phy_addr, MII_QPDSTS); + + if (status & MII_STSICS_SPD) + *speed = HW_SPEED_100_MBPS; + else + *speed = HW_SPEED_10_MBPS; + + if (status & MII_STSICS_DPLX) + *duplex = FDX_CAPABLE_FULL_SELECTED; + else + *duplex = FDX_CAPABLE_HALF_SELECTED; + + if (status & MII_STSICS_LINKSTS) + printf("ics1893_read_mode: Media Link On %s %s-duplex \n", + *speed == HW_SPEED_100_MBPS ? + "100mbps" : "10mbps", + *duplex == FDX_CAPABLE_FULL_SELECTED ? + "full" : "half"); + else + printf("ics1893_read_mode: Media Link Off\n"); +} + +/** + * rtl8201_read_mode: - read media mode for rtl8201 phy + * @nic: the net device to read mode for + * @phy_addr: mii phy address + * @speed: the transmit speed to be determined + * @duplex: the duplex mode to be determined + * + * read MII_STATUS register from rtl8201 phy + * to determine the speed and duplex mode for sis900 + */ + +static void rtl8201_read_mode(struct nic *nic __unused, int phy_addr, int *speed, int *duplex) +{ + u32 status; + + status = sis900_mdio_read(phy_addr, MII_STATUS); + + if (status & MII_STAT_CAN_TX_FDX) { + *speed = HW_SPEED_100_MBPS; + *duplex = FDX_CAPABLE_FULL_SELECTED; + } + else if (status & MII_STAT_CAN_TX) { + *speed = HW_SPEED_100_MBPS; + *duplex = FDX_CAPABLE_HALF_SELECTED; + } + else if (status & MII_STAT_CAN_T_FDX) { + *speed = HW_SPEED_10_MBPS; + *duplex = FDX_CAPABLE_FULL_SELECTED; + } + else if (status & MII_STAT_CAN_T) { + *speed = HW_SPEED_10_MBPS; + *duplex = FDX_CAPABLE_HALF_SELECTED; + } + + if (status & MII_STAT_LINK) + printf("rtl8201_read_mode: Media Link On %s %s-duplex \n", + *speed == HW_SPEED_100_MBPS ? + "100mbps" : "10mbps", + *duplex == FDX_CAPABLE_FULL_SELECTED ? + "full" : "half"); + else + printf("rtl8201_read_config_mode: Media Link Off\n"); +} + +/** + * vt6103_read_mode: - read media mode for vt6103 phy + * @nic: the net device to read mode for + * @phy_addr: mii phy address + * @speed: the transmit speed to be determined + * @duplex: the duplex mode to be determined + * + * read MII_STATUS register from rtl8201 phy + * to determine the speed and duplex mode for sis900 + */ + +static void vt6103_read_mode(struct nic *nic __unused, int phy_addr, int *speed, int *duplex) +{ + u32 status; + + status = sis900_mdio_read(phy_addr, MII_STATUS); + + if (status & MII_STAT_CAN_TX_FDX) { + *speed = HW_SPEED_100_MBPS; + *duplex = FDX_CAPABLE_FULL_SELECTED; + } + else if (status & MII_STAT_CAN_TX) { + *speed = HW_SPEED_100_MBPS; + *duplex = FDX_CAPABLE_HALF_SELECTED; + } + else if (status & MII_STAT_CAN_T_FDX) { + *speed = HW_SPEED_10_MBPS; + *duplex = FDX_CAPABLE_FULL_SELECTED; + } + else if (status & MII_STAT_CAN_T) { + *speed = HW_SPEED_10_MBPS; + *duplex = FDX_CAPABLE_HALF_SELECTED; + } + + if (status & MII_STAT_LINK) + printf("vt6103_read_mode: Media Link On %s %s-duplex \n", + *speed == HW_SPEED_100_MBPS ? + "100mbps" : "10mbps", + *duplex == FDX_CAPABLE_FULL_SELECTED ? + "full" : "half"); + else + printf("vt6103_read_config_mode: Media Link Off\n"); +} + +/* Function: sis900_transmit + * + * Description: transmits a packet and waits for completion or timeout. + * + * Arguments: char d[6]: destination ethernet address. + * unsigned short t: ethernet protocol type. + * unsigned short s: size of the data-part of the packet. + * char *p: the data for the packet. + * + * Returns: void. + */ + +static void +sis900_transmit(struct nic *nic, + const char *d, /* Destination */ + unsigned int t, /* Type */ + unsigned int s, /* size */ + const char *p) /* Packet */ +{ + u32 to, nstype; + volatile u32 tx_status; + + /* Stop the transmitter */ + outl(TxDIS | inl(ioaddr + cr), ioaddr + cr); + + /* load Transmit Descriptor Register */ + outl(virt_to_bus(&txd), ioaddr + txdp); + if (sis900_debug > 1) + printf("sis900_transmit: TX descriptor register loaded with: %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 (((tx_status=txd.cmdsts) & OWN) && (currticks() < to)) + /* wait */ ; + + if (currticks() >= to) { + printf("sis900_transmit: TX Timeout! Tx status %X.\n", + (unsigned int) tx_status); + } + + if (tx_status & (ABORT | UNDERRUN | OWCOLL)) { + /* packet unsuccessfully transmited */ + printf("sis900_transmit: Transmit error, Tx status %X.\n", + (unsigned int) tx_status); + } + /* Disable interrupts by clearing the interrupt mask. */ + outl(0, ioaddr + imr); +} + + +/* Function: sis900_poll + * + * Description: checks for a received packet and returns it if found. + * + * Arguments: struct nic *nic: NIC data structure + * + * Returns: 1 if a packet was recieved. + * 0 if no pacet was recieved. + * + * Side effects: + * Returns (copies) the packet to the array nic->packet. + * Returns the length of the packet in nic->packetlen. + */ + +static int +sis900_poll(struct nic *nic, int retrieve) +{ + u32 rx_status = rxd[cur_rx].cmdsts; + int retstat = 0; + + if (sis900_debug > 2) + printf("sis900_poll: cur_rx:%d, status:%X\n", cur_rx, + (unsigned int) rx_status); + + if (!(rx_status & OWN)) + return retstat; + + if (sis900_debug > 1) + printf("sis900_poll: got a packet: cur_rx:%d, status:%X\n", + cur_rx, (unsigned int) rx_status); + + if ( ! retrieve ) return 1; + + nic->packetlen = (rx_status & DSIZE) - CRC_SIZE; + + if (rx_status & (ABORT|OVERRUN|TOOLONG|RUNT|RXISERR|CRCERR|FAERR)) { + /* corrupted packet received */ + printf("sis900_poll: Corrupted packet received, buffer status = %X\n", + (unsigned int) rx_status); + retstat = 0; + } else { + /* give packet to higher level routine */ + memcpy(nic->packet, (rxb + cur_rx*RX_BUF_SIZE), nic->packetlen); + retstat = 1; + } + + /* return the descriptor and buffer to receive ring */ + rxd[cur_rx].cmdsts = RX_BUF_SIZE; + rxd[cur_rx].bufptr = virt_to_bus(&rxb[cur_rx*RX_BUF_SIZE]); + + if (++cur_rx == NUM_RX_DESC) + cur_rx = 0; + + /* re-enable the potentially idle receive state machine */ + outl(RxENA | inl(ioaddr + cr), ioaddr + cr); + + return retstat; + +} + + +/* Function: sis900_disable + * + * Description: Turns off interrupts and stops Tx and Rx engines + * + * Arguments: struct nic *nic: NIC data structure + * + * Returns: void. + */ + +static void +sis900_disable ( struct nic *nic ) { + + sis900_init(nic); + + /* Disable interrupts by clearing the interrupt mask. */ + outl(0, ioaddr + imr); + outl(0, ioaddr + ier); + + /* Stop the chip's Tx and Rx Status Machine */ + outl(RxDIS | TxDIS | inl(ioaddr + cr), ioaddr + cr); +} + + +/* Function: sis900_irq + * + * Description: Enable, Disable, or Force, interrupts + * + * Arguments: struct nic *nic: NIC data structure + * irq_action_t action: Requested action + * + * Returns: void. + */ + +static void +sis900_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + +static struct nic_operations sis900_operations = { + .connect = dummy_connect, + .poll = sis900_poll, + .transmit = sis900_transmit, + .irq = sis900_irq, +}; + +static struct pci_device_id sis900_nics[] = { +PCI_ROM(0x1039, 0x0900, "sis900", "SIS900"), +PCI_ROM(0x1039, 0x7016, "sis7016", "SIS7016"), +}; + +PCI_DRIVER ( sis900_driver, sis900_nics, PCI_NO_CLASS ); + +DRIVER ( "SIS900", nic_driver, pci_driver, sis900_driver, + sis900_probe, sis900_disable ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/sis900.h b/gpxe/src/drivers/net/sis900.h new file mode 100644 index 0000000..e88e111 --- /dev/null +++ b/gpxe/src/drivers/net/sis900.h @@ -0,0 +1,373 @@ +/* -*- Mode:C; c-basic-offset:4; -*- */ + +/* Definitions for SiS ethernet controllers including 7014/7016 and 900 + * References: + * SiS 7016 Fast Ethernet PCI Bus 10/100 Mbps LAN Controller with OnNow Support, + * preliminary Rev. 1.0 Jan. 14, 1998 + * SiS 900 Fast Ethernet PCI Bus 10/100 Mbps LAN Single Chip with OnNow Support, + * preliminary Rev. 1.0 Nov. 10, 1998 + * SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution, + * preliminary Rev. 1.0 Jan. 18, 1998 + * http://www.sis.com.tw/support/databook.htm + */ + +/* MAC operationl registers of SiS 7016 and SiS 900 ethernet controller */ +/* The I/O extent, SiS 900 needs 256 bytes of io address */ +#define SIS900_TOTAL_SIZE 0x100 + +/* Symbolic offsets to registers. */ +enum sis900_registers { + cr=0x0, /* Command Register */ + cfg=0x4, /* Configuration Register */ + mear=0x8, /* EEPROM Access Register */ + ptscr=0xc, /* PCI Test Control Register */ + isr=0x10, /* Interrupt Status Register */ + imr=0x14, /* Interrupt Mask Register */ + ier=0x18, /* Interrupt Enable Register */ + epar=0x18, /* Enhanced PHY Access Register */ + txdp=0x20, /* Transmit Descriptor Pointer Register */ + txcfg=0x24, /* Transmit Configuration Register */ + rxdp=0x30, /* Receive Descriptor Pointer Register */ + rxcfg=0x34, /* Receive Configuration Register */ + flctrl=0x38, /* Flow Control Register */ + rxlen=0x3c, /* Receive Packet Length Register */ + rfcr=0x48, /* Receive Filter Control Register */ + rfdr=0x4C, /* Receive Filter Data Register */ + pmctrl=0xB0, /* Power Management Control Register */ + pmer=0xB4 /* Power Management Wake-up Event Register */ +}; + +/* Symbolic names for bits in various registers */ +enum sis900_command_register_bits { + RELOAD = 0x00000400, + ACCESSMODE = 0x00000200, + RESET = 0x00000100, + SWI = 0x00000080, + RxRESET = 0x00000020, + TxRESET = 0x00000010, + RxDIS = 0x00000008, + RxENA = 0x00000004, + TxDIS = 0x00000002, + TxENA = 0x00000001 +}; + +enum sis900_configuration_register_bits { + DESCRFMT = 0x00000100, /* 7016 specific */ + REQALG = 0x00000080, + SB = 0x00000040, + POW = 0x00000020, + EXD = 0x00000010, + PESEL = 0x00000008, + LPM = 0x00000004, + BEM = 0x00000001, + RND_CNT = 0x00000400, + FAIR_BACKOFF = 0x00000200, + EDB_MASTER_EN = 0x00002000 +}; + +enum sis900_eeprom_access_reigster_bits { + MDC = 0x00000040, + MDDIR = 0x00000020, + MDIO = 0x00000010, /* 7016 specific */ + EECS = 0x00000008, + EECLK = 0x00000004, + EEDO = 0x00000002, + EEDI = 0x00000001 +}; + +enum sis900_interrupt_register_bits { + WKEVT = 0x10000000, + TxPAUSEEND = 0x08000000, + TxPAUSE = 0x04000000, + TxRCMP = 0x02000000, + RxRCMP = 0x01000000, + DPERR = 0x00800000, + SSERR = 0x00400000, + RMABT = 0x00200000, + RTABT = 0x00100000, + RxSOVR = 0x00010000, + HIBERR = 0x00008000, + SWINT = 0x00001000, + MIBINT = 0x00000800, + TxURN = 0x00000400, + TxIDLE = 0x00000200, + TxERR = 0x00000100, + TxDESC = 0x00000080, + TxOK = 0x00000040, + RxORN = 0x00000020, + RxIDLE = 0x00000010, + RxEARLY = 0x00000008, + RxERR = 0x00000004, + RxDESC = 0x00000002, + RxOK = 0x00000001 +}; + +enum sis900_interrupt_enable_reigster_bits { + IE = 0x00000001 +}; + +/* maximum dma burst fro transmission and receive*/ +#define MAX_DMA_RANGE 7 /* actually 0 means MAXIMUM !! */ +#define TxMXDMA_shift 20 +#define RxMXDMA_shift 20 +#define TX_DMA_BURST 0 +#define RX_DMA_BURST 0 + +enum sis900_tx_rx_dma{ + DMA_BURST_512 = 0, DMA_BURST_64 = 5 +}; + +/* transmit FIFO threshholds */ +#define TX_FILL_THRESH 16 /* 1/4 FIFO size */ +#define TxFILLT_shift 8 +#define TxDRNT_shift 0 +#define TxDRNT_100 48 /* 3/4 FIFO size */ +#define TxDRNT_10 16 /* 1/2 FIFO size */ + +enum sis900_transmit_config_register_bits { + TxCSI = 0x80000000, + TxHBI = 0x40000000, + TxMLB = 0x20000000, + TxATP = 0x10000000, + TxIFG = 0x0C000000, + TxFILLT = 0x00003F00, + TxDRNT = 0x0000003F +}; + +/* recevie FIFO thresholds */ +#define RxDRNT_shift 1 +#define RxDRNT_100 16 /* 1/2 FIFO size */ +#define RxDRNT_10 24 /* 3/4 FIFO size */ + +enum sis900_reveive_config_register_bits { + RxAEP = 0x80000000, + RxARP = 0x40000000, + RxATX = 0x10000000, + RxAJAB = 0x08000000, + RxDRNT = 0x0000007F +}; + +#define RFAA_shift 28 +#define RFADDR_shift 16 + +enum sis900_receive_filter_control_register_bits { + RFEN = 0x80000000, + RFAAB = 0x40000000, + RFAAM = 0x20000000, + RFAAP = 0x10000000, + RFPromiscuous = (RFAAB|RFAAM|RFAAP) +}; + +enum sis900_reveive_filter_data_mask { + RFDAT = 0x0000FFFF +}; + +/* EEPROM Addresses */ +enum sis900_eeprom_address { + EEPROMSignature = 0x00, + EEPROMVendorID = 0x02, + EEPROMDeviceID = 0x03, + EEPROMMACAddr = 0x08, + EEPROMChecksum = 0x0b +}; + +/* The EEPROM commands include the alway-set leading bit. Refer to NM93Cxx datasheet */ +enum sis900_eeprom_command { + EEread = 0x0180, + EEwrite = 0x0140, + EEerase = 0x01C0, + EEwriteEnable = 0x0130, + EEwriteDisable = 0x0100, + EEeraseAll = 0x0120, + EEwriteAll = 0x0110, + EEaddrMask = 0x013F, + EEcmdShift = 16 +}; +/* For SiS962 or SiS963, request the eeprom software access */ +enum sis96x_eeprom_command { + EEREQ = 0x00000400, EEDONE = 0x00000200, EEGNT = 0x00000100 +}; + +/* Manamgement Data I/O (mdio) frame */ +#define MIIread 0x6000 +#define MIIwrite 0x5002 +#define MIIpmdShift 7 +#define MIIregShift 2 +#define MIIcmdLen 16 +#define MIIcmdShift 16 + +/* Buffer Descriptor Status*/ +enum sis900_buffer_status { + OWN = 0x80000000, + MORE = 0x40000000, + INTR = 0x20000000, + SUPCRC = 0x10000000, + INCCRC = 0x10000000, + OK = 0x08000000, + DSIZE = 0x00000FFF +}; + +/* Status for TX Buffers */ +enum sis900_tx_buffer_status { + ABORT = 0x04000000, + UNDERRUN = 0x02000000, + NOCARRIER = 0x01000000, + DEFERD = 0x00800000, + EXCDEFER = 0x00400000, + OWCOLL = 0x00200000, + EXCCOLL = 0x00100000, + COLCNT = 0x000F0000 +}; + +enum sis900_rx_bufer_status { + OVERRUN = 0x02000000, + DEST = 0x00800000, + BCAST = 0x01800000, + MCAST = 0x01000000, + UNIMATCH = 0x00800000, + TOOLONG = 0x00400000, + RUNT = 0x00200000, + RXISERR = 0x00100000, + CRCERR = 0x00080000, + FAERR = 0x00040000, + LOOPBK = 0x00020000, + RXCOL = 0x00010000 +}; + +/* MII register offsets */ +enum mii_registers { + MII_CONTROL = 0x0000, + MII_STATUS = 0x0001, + MII_PHY_ID0 = 0x0002, + MII_PHY_ID1 = 0x0003, + MII_ANADV = 0x0004, + MII_ANLPAR = 0x0005, + MII_ANEXT = 0x0006 +}; + +/* mii registers specific to SiS 900 */ +enum sis_mii_registers { + MII_CONFIG1 = 0x0010, + MII_CONFIG2 = 0x0011, + MII_STSOUT = 0x0012, + MII_MASK = 0x0013, + MII_RESV = 0x0014 +}; + +/* mii registers specific to AMD 79C901 */ +enum amd_mii_registers { + MII_STATUS_SUMMARY = 0x0018 +}; + +/* mii registers specific to ICS 1893 */ +enum ics_mii_registers { + MII_EXTCTRL = 0x0010, MII_QPDSTS = 0x0011, MII_10BTOP = 0x0012, + MII_EXTCTRL2 = 0x0013 +}; + + + +/* MII Control register bit definitions. */ +enum mii_control_register_bits { + MII_CNTL_FDX = 0x0100, + MII_CNTL_RST_AUTO = 0x0200, + MII_CNTL_ISOLATE = 0x0400, + MII_CNTL_PWRDWN = 0x0800, + MII_CNTL_AUTO = 0x1000, + MII_CNTL_SPEED = 0x2000, + MII_CNTL_LPBK = 0x4000, + MII_CNTL_RESET = 0x8000 +}; + +/* MII Status register bit */ +enum mii_status_register_bits { + MII_STAT_EXT = 0x0001, + MII_STAT_JAB = 0x0002, + MII_STAT_LINK = 0x0004, + MII_STAT_CAN_AUTO = 0x0008, + MII_STAT_FAULT = 0x0010, + MII_STAT_AUTO_DONE = 0x0020, + MII_STAT_CAN_T = 0x0800, + MII_STAT_CAN_T_FDX = 0x1000, + MII_STAT_CAN_TX = 0x2000, + MII_STAT_CAN_TX_FDX = 0x4000, + MII_STAT_CAN_T4 = 0x8000 +}; + +#define MII_ID1_OUI_LO 0xFC00 /* low bits of OUI mask */ +#define MII_ID1_MODEL 0x03F0 /* model number */ +#define MII_ID1_REV 0x000F /* model number */ + +/* MII NWAY Register Bits ... + valid for the ANAR (Auto-Negotiation Advertisement) and + ANLPAR (Auto-Negotiation Link Partner) registers */ +enum mii_nway_register_bits { + MII_NWAY_NODE_SEL = 0x001f, + MII_NWAY_CSMA_CD = 0x0001, + MII_NWAY_T = 0x0020, + MII_NWAY_T_FDX = 0x0040, + MII_NWAY_TX = 0x0080, + MII_NWAY_TX_FDX = 0x0100, + MII_NWAY_T4 = 0x0200, + MII_NWAY_PAUSE = 0x0400, + MII_NWAY_RF = 0x2000, + MII_NWAY_ACK = 0x4000, + MII_NWAY_NP = 0x8000 +}; + +enum mii_stsout_register_bits { + MII_STSOUT_LINK_FAIL = 0x4000, + MII_STSOUT_SPD = 0x0080, + MII_STSOUT_DPLX = 0x0040 +}; + +enum mii_stsics_register_bits { + MII_STSICS_SPD = 0x8000, MII_STSICS_DPLX = 0x4000, + MII_STSICS_LINKSTS = 0x0001 +}; + +enum mii_stssum_register_bits { + MII_STSSUM_LINK = 0x0008, + MII_STSSUM_DPLX = 0x0004, + MII_STSSUM_AUTO = 0x0002, + MII_STSSUM_SPD = 0x0001 +}; + +enum sis900_revision_id { + SIS630A_900_REV = 0x80, SIS630E_900_REV = 0x81, + SIS630S_900_REV = 0x82, SIS630EA1_900_REV = 0x83, + SIS630ET_900_REV = 0x84, SIS635A_900_REV = 0x90, + SIS96x_900_REV = 0X91, SIS900B_900_REV = 0x03 +}; + +enum sis630_revision_id { + SIS630A0 = 0x00, SIS630A1 = 0x01, + SIS630B0 = 0x10, SIS630B1 = 0x11 +}; + +#define FDX_CAPABLE_DUPLEX_UNKNOWN 0 +#define FDX_CAPABLE_HALF_SELECTED 1 +#define FDX_CAPABLE_FULL_SELECTED 2 + +#define HW_SPEED_UNCONFIG 0 +#define HW_SPEED_HOME 1 +#define HW_SPEED_10_MBPS 10 +#define HW_SPEED_100_MBPS 100 +#define HW_SPEED_DEFAULT (HW_SPEED_100_MBPS) + +#define CRC_SIZE 4 +#define MAC_HEADER_SIZE 14 + +#define TX_BUF_SIZE 1536 +#define RX_BUF_SIZE 1536 + +#define NUM_RX_DESC 4 /* Number of Rx descriptor registers. */ + +/* Time in ticks before concluding the transmitter is hung. */ +#define TX_TIMEOUT (4*TICKS_PER_SEC) + +typedef struct _BufferDesc { + u32 link; + volatile u32 cmdsts; + u32 bufptr; +} BufferDesc; diff --git a/gpxe/src/drivers/net/smc9000.c b/gpxe/src/drivers/net/smc9000.c new file mode 100644 index 0000000..31a1e1b --- /dev/null +++ b/gpxe/src/drivers/net/smc9000.c @@ -0,0 +1,952 @@ +#ifdef ALLMULTI +#error multicast support is not yet implemented +#endif + /*------------------------------------------------------------------------ + * smc9000.c + * This is a Etherboot driver for SMC's 9000 series of Ethernet cards. + * + * Copyright (C) 1998 Daniel Engström + * 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_DEBUG 0 + +#if SMC9000_DEBUG > 1 +#define PRINTK2 printf +#else +#define PRINTK2(args...) +#endif + +#include +#include +#include "etherboot.h" +#include "nic.h" +#include +#include "smc9000.h" + +# define _outb outb +# define _outw outw + +static const char smc9000_version[] = "Version 0.99 98-09-30"; +static const char *interfaces[ 2 ] = { "TP", "AUI" }; +static const char *chip_ids[ 15 ] = { + NULL, NULL, NULL, + /* 3 */ "SMC91C90/91C92", + /* 4 */ "SMC91C94", + /* 5 */ "SMC91C95", + NULL, + /* 7 */ "SMC91C100", + /* 8 */ "SMC91C100FD", + /* 9 */ "SMC91C11xFD", + NULL, NULL, + NULL, NULL, NULL +}; +static const char smc91c96_id[] = "SMC91C96"; + +/*------------------------------------------------------------ + . Reads a register from the MII Management serial interface + .-------------------------------------------------------------*/ +static word smc_read_phy_register(int ioaddr, byte phyaddr, byte phyreg) +{ + int oldBank; + unsigned int i; + byte mask; + word mii_reg; + byte bits[64]; + int clk_idx = 0; + int input_idx; + word phydata; + + // 32 consecutive ones on MDO to establish sync + for (i = 0; i < 32; ++i) + bits[clk_idx++] = MII_MDOE | MII_MDO; + + // Start code <01> + bits[clk_idx++] = MII_MDOE; + bits[clk_idx++] = MII_MDOE | MII_MDO; + + // Read command <10> + bits[clk_idx++] = MII_MDOE | MII_MDO; + bits[clk_idx++] = MII_MDOE; + + // Output the PHY address, msb first + mask = (byte)0x10; + for (i = 0; i < 5; ++i) + { + if (phyaddr & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + // Shift to next lowest bit + mask >>= 1; + } + + // Output the phy register number, msb first + mask = (byte)0x10; + for (i = 0; i < 5; ++i) + { + if (phyreg & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + // Shift to next lowest bit + mask >>= 1; + } + + // Tristate and turnaround (2 bit times) + bits[clk_idx++] = 0; + //bits[clk_idx++] = 0; + + // Input starts at this bit time + input_idx = clk_idx; + + // Will input 16 bits + for (i = 0; i < 16; ++i) + bits[clk_idx++] = 0; + + // Final clock bit + bits[clk_idx++] = 0; + + // Save the current bank + oldBank = inw( ioaddr+BANK_SELECT ); + + // Select bank 3 + SMC_SELECT_BANK(ioaddr, 3); + + // Get the current MII register value + mii_reg = inw( ioaddr+MII_REG ); + + // Turn off all MII Interface bits + mii_reg &= ~(MII_MDOE|MII_MCLK|MII_MDI|MII_MDO); + + // Clock all 64 cycles + for (i = 0; i < sizeof(bits); ++i) + { + // Clock Low - output data + outw( mii_reg | bits[i], ioaddr+MII_REG ); + udelay(50); + + + // Clock Hi - input data + outw( mii_reg | bits[i] | MII_MCLK, ioaddr+MII_REG ); + udelay(50); + bits[i] |= inw( ioaddr+MII_REG ) & MII_MDI; + } + + // Return to idle state + // Set clock to low, data to low, and output tristated + outw( mii_reg, ioaddr+MII_REG ); + udelay(50); + + // Restore original bank select + SMC_SELECT_BANK(ioaddr, oldBank); + + // Recover input data + phydata = 0; + for (i = 0; i < 16; ++i) + { + phydata <<= 1; + + if (bits[input_idx++] & MII_MDI) + phydata |= 0x0001; + } + +#if (SMC_DEBUG > 2 ) + printf("smc_read_phy_register(): phyaddr=%x,phyreg=%x,phydata=%x\n", + phyaddr, phyreg, phydata); +#endif + + return(phydata); +} + + +/*------------------------------------------------------------ + . Writes a register to the MII Management serial interface + .-------------------------------------------------------------*/ +static void smc_write_phy_register(int ioaddr, + byte phyaddr, byte phyreg, word phydata) +{ + int oldBank; + unsigned int i; + word mask; + word mii_reg; + byte bits[65]; + int clk_idx = 0; + + // 32 consecutive ones on MDO to establish sync + for (i = 0; i < 32; ++i) + bits[clk_idx++] = MII_MDOE | MII_MDO; + + // Start code <01> + bits[clk_idx++] = MII_MDOE; + bits[clk_idx++] = MII_MDOE | MII_MDO; + + // Write command <01> + bits[clk_idx++] = MII_MDOE; + bits[clk_idx++] = MII_MDOE | MII_MDO; + + // Output the PHY address, msb first + mask = (byte)0x10; + for (i = 0; i < 5; ++i) + { + if (phyaddr & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + // Shift to next lowest bit + mask >>= 1; + } + + // Output the phy register number, msb first + mask = (byte)0x10; + for (i = 0; i < 5; ++i) + { + if (phyreg & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + // Shift to next lowest bit + mask >>= 1; + } + + // Tristate and turnaround (2 bit times) + bits[clk_idx++] = 0; + bits[clk_idx++] = 0; + + // Write out 16 bits of data, msb first + mask = 0x8000; + for (i = 0; i < 16; ++i) + { + if (phydata & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + // Shift to next lowest bit + mask >>= 1; + } + + // Final clock bit (tristate) + bits[clk_idx++] = 0; + + // Save the current bank + oldBank = inw( ioaddr+BANK_SELECT ); + + // Select bank 3 + SMC_SELECT_BANK(ioaddr, 3); + + // Get the current MII register value + mii_reg = inw( ioaddr+MII_REG ); + + // Turn off all MII Interface bits + mii_reg &= ~(MII_MDOE|MII_MCLK|MII_MDI|MII_MDO); + + // Clock all cycles + for (i = 0; i < sizeof(bits); ++i) + { + // Clock Low - output data + outw( mii_reg | bits[i], ioaddr+MII_REG ); + udelay(50); + + + // Clock Hi - input data + outw( mii_reg | bits[i] | MII_MCLK, ioaddr+MII_REG ); + udelay(50); + bits[i] |= inw( ioaddr+MII_REG ) & MII_MDI; + } + + // Return to idle state + // Set clock to low, data to low, and output tristated + outw( mii_reg, ioaddr+MII_REG ); + udelay(50); + + // Restore original bank select + SMC_SELECT_BANK(ioaddr, oldBank); + +#if (SMC_DEBUG > 2 ) + printf("smc_write_phy_register(): phyaddr=%x,phyreg=%x,phydata=%x\n", + phyaddr, phyreg, phydata); +#endif +} + + +/*------------------------------------------------------------ + . Finds and reports the PHY address + .-------------------------------------------------------------*/ +static int smc_detect_phy(int ioaddr, byte *pphyaddr) +{ + word phy_id1; + word phy_id2; + int phyaddr; + int found = 0; + + // Scan all 32 PHY addresses if necessary + for (phyaddr = 0; phyaddr < 32; ++phyaddr) + { + // Read the PHY identifiers + phy_id1 = smc_read_phy_register(ioaddr, phyaddr, PHY_ID1_REG); + phy_id2 = smc_read_phy_register(ioaddr, phyaddr, PHY_ID2_REG); + + // Make sure it is a valid identifier + if ((phy_id2 > 0x0000) && (phy_id2 < 0xffff) && + (phy_id1 > 0x0000) && (phy_id1 < 0xffff)) + { + if ((phy_id1 != 0x8000) && (phy_id2 != 0x8000)) + { + // Save the PHY's address + *pphyaddr = phyaddr; + found = 1; + break; + } + } + } + + if (!found) + { + printf("No PHY found\n"); + return(0); + } + + // Set the PHY type + if ( (phy_id1 == 0x0016) && ((phy_id2 & 0xFFF0) == 0xF840 ) ) + { + printf("PHY=LAN83C183 (LAN91C111 Internal)\n"); + } + + if ( (phy_id1 == 0x0282) && ((phy_id2 & 0xFFF0) == 0x1C50) ) + { + printf("PHY=LAN83C180\n"); + } + + return(1); +} + +/*------------------------------------------------------------ + . Configures the specified PHY using Autonegotiation. Calls + . smc_phy_fixed() if the user has requested a certain config. + .-------------------------------------------------------------*/ +static void smc_phy_configure(int ioaddr) +{ + int timeout; + byte phyaddr; + word my_phy_caps; // My PHY capabilities + word my_ad_caps; // My Advertised capabilities + word status; + int failed = 0; + int rpc_cur_mode = RPC_DEFAULT; + int lastPhy18; + + // Find the address and type of our phy + if (!smc_detect_phy(ioaddr, &phyaddr)) + { + return; + } + + // Reset the PHY, setting all other bits to zero + smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG, PHY_CNTL_RST); + + // Wait for the reset to complete, or time out + timeout = 6; // Wait up to 3 seconds + while (timeout--) + { + if (!(smc_read_phy_register(ioaddr, phyaddr, PHY_CNTL_REG) + & PHY_CNTL_RST)) + { + // reset complete + break; + } + + mdelay(500); // wait 500 millisecs + } + + if (timeout < 1) + { + PRINTK2("PHY reset timed out\n"); + return; + } + + // Read PHY Register 18, Status Output + lastPhy18 = smc_read_phy_register(ioaddr, phyaddr, PHY_INT_REG); + + // Enable PHY Interrupts (for register 18) + // Interrupts listed here are disabled + smc_write_phy_register(ioaddr, phyaddr, PHY_MASK_REG, + PHY_INT_LOSSSYNC | PHY_INT_CWRD | PHY_INT_SSD | + PHY_INT_ESD | PHY_INT_RPOL | PHY_INT_JAB | + PHY_INT_SPDDET | PHY_INT_DPLXDET); + + /* Configure the Receive/Phy Control register */ + SMC_SELECT_BANK(ioaddr, 0); + outw( rpc_cur_mode, ioaddr + RPC_REG ); + + // Copy our capabilities from PHY_STAT_REG to PHY_AD_REG + my_phy_caps = smc_read_phy_register(ioaddr, phyaddr, PHY_STAT_REG); + my_ad_caps = PHY_AD_CSMA; // I am CSMA capable + + if (my_phy_caps & PHY_STAT_CAP_T4) + my_ad_caps |= PHY_AD_T4; + + if (my_phy_caps & PHY_STAT_CAP_TXF) + my_ad_caps |= PHY_AD_TX_FDX; + + if (my_phy_caps & PHY_STAT_CAP_TXH) + my_ad_caps |= PHY_AD_TX_HDX; + + if (my_phy_caps & PHY_STAT_CAP_TF) + my_ad_caps |= PHY_AD_10_FDX; + + if (my_phy_caps & PHY_STAT_CAP_TH) + my_ad_caps |= PHY_AD_10_HDX; + + // Update our Auto-Neg Advertisement Register + smc_write_phy_register(ioaddr, phyaddr, PHY_AD_REG, my_ad_caps); + + PRINTK2("phy caps=%x\n", my_phy_caps); + PRINTK2("phy advertised caps=%x\n", my_ad_caps); + + // Restart auto-negotiation process in order to advertise my caps + smc_write_phy_register( ioaddr, phyaddr, PHY_CNTL_REG, + PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST ); + + // Wait for the auto-negotiation to complete. This may take from + // 2 to 3 seconds. + // Wait for the reset to complete, or time out + timeout = 20; // Wait up to 10 seconds + while (timeout--) + { + status = smc_read_phy_register(ioaddr, phyaddr, PHY_STAT_REG); + if (status & PHY_STAT_ANEG_ACK) + { + // auto-negotiate complete + break; + } + + mdelay(500); // wait 500 millisecs + + // Restart auto-negotiation if remote fault + if (status & PHY_STAT_REM_FLT) + { + PRINTK2("PHY remote fault detected\n"); + + // Restart auto-negotiation + PRINTK2("PHY restarting auto-negotiation\n"); + smc_write_phy_register( ioaddr, phyaddr, PHY_CNTL_REG, + PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST | + PHY_CNTL_SPEED | PHY_CNTL_DPLX); + } + } + + if (timeout < 1) + { + PRINTK2("PHY auto-negotiate timed out\n"); + failed = 1; + } + + // Fail if we detected an auto-negotiate remote fault + if (status & PHY_STAT_REM_FLT) + { + PRINTK2("PHY remote fault detected\n"); + failed = 1; + } + + // Set our sysctl parameters to match auto-negotiation results + if ( lastPhy18 & PHY_INT_SPDDET ) + { + PRINTK2("PHY 100BaseT\n"); + rpc_cur_mode |= RPC_SPEED; + } + else + { + PRINTK2("PHY 10BaseT\n"); + rpc_cur_mode &= ~RPC_SPEED; + } + + if ( lastPhy18 & PHY_INT_DPLXDET ) + { + PRINTK2("PHY Full Duplex\n"); + rpc_cur_mode |= RPC_DPLX; + } + else + { + PRINTK2("PHY Half Duplex\n"); + rpc_cur_mode &= ~RPC_DPLX; + } + + // Re-Configure the Receive/Phy Control register + outw( rpc_cur_mode, ioaddr + RPC_REG ); +} + +/* + * Function: smc_reset( int ioaddr ) + * Purpose: + * This sets the SMC91xx chip to its normal state, hopefully from whatever + * mess that any other DOS driver has put it in. + * + * Maybe I should reset more registers to defaults in here? SOFTRESET should + * do that for me. + * + * Method: + * 1. send a SOFT RESET + * 2. wait for it to finish + * 3. reset the memory management unit + * 4. clear all interrupts + * +*/ +static void smc_reset(int ioaddr) +{ + /* This resets the registers mostly to defaults, but doesn't + * affect EEPROM. That seems unnecessary */ + SMC_SELECT_BANK(ioaddr, 0); + _outw( RCR_SOFTRESET, ioaddr + RCR ); + + /* this should pause enough for the chip to be happy */ + SMC_DELAY(ioaddr); + + /* Set the transmit and receive configuration registers to + * default values */ + _outw(RCR_CLEAR, ioaddr + RCR); + _outw(TCR_CLEAR, ioaddr + TCR); + + /* Reset the MMU */ + SMC_SELECT_BANK(ioaddr, 2); + _outw( MC_RESET, ioaddr + MMU_CMD ); + + /* Note: It doesn't seem that waiting for the MMU busy is needed here, + * but this is a place where future chipsets _COULD_ break. Be wary + * of issuing another MMU command right after this */ + _outb(0, ioaddr + INT_MASK); +} + + +/*---------------------------------------------------------------------- + * Function: smc9000_probe_addr( int ioaddr ) + * + * Purpose: + * Tests to see if a given ioaddr points to an SMC9xxx chip. + * Returns a 1 on success + * + * Algorithm: + * (1) see if the high byte of BANK_SELECT is 0x33 + * (2) compare the ioaddr with the base register's address + * (3) see if I recognize the chip ID in the appropriate register + * + * --------------------------------------------------------------------- + */ +static int smc9000_probe_addr( isa_probe_addr_t ioaddr ) +{ + word bank; + word revision_register; + word base_address_register; + + /* First, see if the high byte is 0x33 */ + bank = inw(ioaddr + BANK_SELECT); + if ((bank & 0xFF00) != 0x3300) { + return 0; + } + /* The above MIGHT indicate a device, but I need to write to further + * test this. */ + _outw(0x0, ioaddr + BANK_SELECT); + bank = inw(ioaddr + BANK_SELECT); + if ((bank & 0xFF00) != 0x3300) { + return 0; + } + + /* well, we've already written once, so hopefully another time won't + * hurt. This time, I need to switch the bank register to bank 1, + * so I can access the base address register */ + SMC_SELECT_BANK(ioaddr, 1); + base_address_register = inw(ioaddr + BASE); + + if (ioaddr != (base_address_register >> 3 & 0x3E0)) { + DBG("SMC9000: IOADDR %hX doesn't match configuration (%hX)." + "Probably not a SMC chip\n", + ioaddr, base_address_register >> 3 & 0x3E0); + /* well, the base address register didn't match. Must not have + * been a SMC chip after all. */ + return 0; + } + + + /* check if the revision register is something that I recognize. + * These might need to be added to later, as future revisions + * could be added. */ + SMC_SELECT_BANK(ioaddr, 3); + revision_register = inw(ioaddr + REVISION); + if (!chip_ids[(revision_register >> 4) & 0xF]) { + /* I don't recognize this chip, so... */ + DBG( "SMC9000: IO %hX: Unrecognized revision register:" + " %hX, Contact author.\n", ioaddr, revision_register ); + return 0; + } + + /* at this point I'll assume that the chip is an SMC9xxx. + * It might be prudent to check a listing of MAC addresses + * against the hardware address, or do some other tests. */ + return 1; +} + + +/************************************************************************** + * ETH_TRANSMIT - Transmit a frame + ***************************************************************************/ +static void smc9000_transmit( + struct nic *nic, + const char *d, /* Destination */ + unsigned int t, /* Type */ + unsigned int s, /* size */ + const char *p) /* Packet */ +{ + word length; /* real, length incl. header */ + word numPages; + unsigned long time_out; + byte packet_no; + word status; + int i; + + /* We dont pad here since we can have the hardware doing it for us */ + length = (s + ETH_HLEN + 1)&~1; + + /* convert to MMU pages */ + numPages = length / 256; + + if (numPages > 7 ) { + DBG("SMC9000: Far too big packet error. \n"); + return; + } + + /* dont try more than, say 30 times */ + for (i=0;i<30;i++) { + /* now, try to allocate the memory */ + SMC_SELECT_BANK(nic->ioaddr, 2); + _outw(MC_ALLOC | numPages, nic->ioaddr + MMU_CMD); + + status = 0; + /* wait for the memory allocation to finnish */ + for (time_out = currticks() + 5*TICKS_PER_SEC; currticks() < time_out; ) { + status = inb(nic->ioaddr + INTERRUPT); + if ( status & IM_ALLOC_INT ) { + /* acknowledge the interrupt */ + _outb(IM_ALLOC_INT, nic->ioaddr + INTERRUPT); + break; + } + } + + if ((status & IM_ALLOC_INT) != 0 ) { + /* We've got the memory */ + break; + } else { + printf("SMC9000: Memory allocation timed out, resetting MMU.\n"); + _outw(MC_RESET, nic->ioaddr + MMU_CMD); + } + } + + /* If I get here, I _know_ there is a packet slot waiting for me */ + packet_no = inb(nic->ioaddr + PNR_ARR + 1); + if (packet_no & 0x80) { + /* or isn't there? BAD CHIP! */ + printf("SMC9000: Memory allocation failed. \n"); + return; + } + + /* we have a packet address, so tell the card to use it */ + _outb(packet_no, nic->ioaddr + PNR_ARR); + + /* point to the beginning of the packet */ + _outw(PTR_AUTOINC, nic->ioaddr + POINTER); + +#if SMC9000_DEBUG > 2 + printf("Trying to xmit packet of length %hX\n", length ); +#endif + + /* send the packet length ( +6 for status, length and ctl byte ) + * and the status word ( set to zeros ) */ + _outw(0, nic->ioaddr + DATA_1 ); + + /* send the packet length ( +6 for status words, length, and ctl) */ + _outb((length+6) & 0xFF, nic->ioaddr + DATA_1); + _outb((length+6) >> 8 , nic->ioaddr + DATA_1); + + /* Write the contents of the packet */ + + /* The ethernet header first... */ + outsw(nic->ioaddr + DATA_1, d, ETH_ALEN >> 1); + outsw(nic->ioaddr + DATA_1, nic->node_addr, ETH_ALEN >> 1); + _outw(htons(t), nic->ioaddr + DATA_1); + + /* ... the data ... */ + outsw(nic->ioaddr + DATA_1 , p, s >> 1); + + /* ... and the last byte, if there is one. */ + if ((s & 1) == 0) { + _outw(0, nic->ioaddr + DATA_1); + } else { + _outb(p[s-1], nic->ioaddr + DATA_1); + _outb(0x20, nic->ioaddr + DATA_1); + } + + /* and let the chipset deal with it */ + _outw(MC_ENQUEUE , nic->ioaddr + MMU_CMD); + + status = 0; time_out = currticks() + 5*TICKS_PER_SEC; + do { + status = inb(nic->ioaddr + INTERRUPT); + + if ((status & IM_TX_INT ) != 0) { + word tx_status; + + /* ack interrupt */ + _outb(IM_TX_INT, nic->ioaddr + INTERRUPT); + + packet_no = inw(nic->ioaddr + FIFO_PORTS); + packet_no &= 0x7F; + + /* select this as the packet to read from */ + _outb( packet_no, nic->ioaddr + PNR_ARR ); + + /* read the first word from this packet */ + _outw( PTR_AUTOINC | PTR_READ, nic->ioaddr + POINTER ); + + tx_status = inw( nic->ioaddr + DATA_1 ); + + if (0 == (tx_status & TS_SUCCESS)) { + DBG("SMC9000: TX FAIL STATUS: %hX \n", tx_status); + /* re-enable transmit */ + SMC_SELECT_BANK(nic->ioaddr, 0); + _outw(inw(nic->ioaddr + TCR ) | TCR_ENABLE, nic->ioaddr + TCR ); + } + + /* kill the packet */ + SMC_SELECT_BANK(nic->ioaddr, 2); + _outw(MC_FREEPKT, nic->ioaddr + MMU_CMD); + + return; + } + }while(currticks() < time_out); + + printf("SMC9000: TX timed out, resetting board\n"); + smc_reset(nic->ioaddr); + return; +} + +/************************************************************************** + * ETH_POLL - Wait for a frame + ***************************************************************************/ +static int smc9000_poll(struct nic *nic, int retrieve) +{ + SMC_SELECT_BANK(nic->ioaddr, 2); + if (inw(nic->ioaddr + FIFO_PORTS) & FP_RXEMPTY) + return 0; + + if ( ! retrieve ) return 1; + + /* start reading from the start of the packet */ + _outw(PTR_READ | PTR_RCV | PTR_AUTOINC, nic->ioaddr + POINTER); + + /* First read the status and check that we're ok */ + if (!(inw(nic->ioaddr + DATA_1) & RS_ERRORS)) { + /* Next: read the packet length and mask off the top bits */ + nic->packetlen = (inw(nic->ioaddr + DATA_1) & 0x07ff); + + /* the packet length includes the 3 extra words */ + nic->packetlen -= 6; +#if SMC9000_DEBUG > 2 + printf(" Reading %d words (and %d byte(s))\n", + (nic->packetlen >> 1), nic->packetlen & 1); +#endif + /* read the packet (and the last "extra" word) */ + insw(nic->ioaddr + DATA_1, nic->packet, (nic->packetlen+2) >> 1); + /* is there an odd last byte ? */ + if (nic->packet[nic->packetlen+1] & 0x20) + nic->packetlen++; + + /* error or good, tell the card to get rid of this packet */ + _outw(MC_RELEASE, nic->ioaddr + MMU_CMD); + return 1; + } + + printf("SMC9000: RX error\n"); + /* error or good, tell the card to get rid of this packet */ + _outw(MC_RELEASE, nic->ioaddr + MMU_CMD); + return 0; +} + +static void smc9000_disable ( struct nic *nic, struct isa_device *isa __unused ) { + + smc_reset(nic->ioaddr); + + /* no more interrupts for me */ + SMC_SELECT_BANK(nic->ioaddr, 2); + _outb( 0, nic->ioaddr + INT_MASK); + + /* and tell the card to stay away from that nasty outside world */ + SMC_SELECT_BANK(nic->ioaddr, 0); + _outb( RCR_CLEAR, nic->ioaddr + RCR ); + _outb( TCR_CLEAR, nic->ioaddr + TCR ); +} + +static void smc9000_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + +static struct nic_operations smc9000_operations = { + .connect = dummy_connect, + .poll = smc9000_poll, + .transmit = smc9000_transmit, + .irq = smc9000_irq, + +}; + +/************************************************************************** + * ETH_PROBE - Look for an adapter + ***************************************************************************/ + +static int smc9000_probe ( struct nic *nic, struct isa_device *isa ) { + + unsigned short revision; + int memory; + int media; + const char * version_string; + const char * if_string; + int i; + + nic->irqno = 0; + nic->ioaddr = isa->ioaddr; + + /* + * Get the MAC address ( bank 1, regs 4 - 9 ) + */ + SMC_SELECT_BANK(nic->ioaddr, 1); + for ( i = 0; i < 6; i += 2 ) { + word address; + + address = inw(nic->ioaddr + ADDR0 + i); + nic->node_addr[i+1] = address >> 8; + nic->node_addr[i] = address & 0xFF; + } + + /* get the memory information */ + SMC_SELECT_BANK(nic->ioaddr, 0); + memory = ( inw(nic->ioaddr + MCR) >> 9 ) & 0x7; /* multiplier */ + memory *= 256 * (inw(nic->ioaddr + MIR) & 0xFF); + + /* + * Now, I want to find out more about the chip. This is sort of + * redundant, but it's cleaner to have it in both, rather than having + * one VERY long probe procedure. + */ + SMC_SELECT_BANK(nic->ioaddr, 3); + revision = inw(nic->ioaddr + REVISION); + version_string = chip_ids[(revision >> 4) & 0xF]; + + if (((revision & 0xF0) >> 4 == CHIP_9196) && + ((revision & 0x0F) >= REV_9196)) { + /* This is a 91c96. 'c96 has the same chip id as 'c94 (4) but + * a revision starting at 6 */ + version_string = smc91c96_id; + } + + if ( !version_string ) { + /* I shouldn't get here because this call was done before.... */ + return 0; + } + + /* is it using AUI or 10BaseT ? */ + SMC_SELECT_BANK(nic->ioaddr, 1); + if (inw(nic->ioaddr + CONFIG) & CFG_AUI_SELECT) + media = 2; + else + media = 1; + + if_string = interfaces[media - 1]; + + /* now, reset the chip, and put it into a known state */ + smc_reset(nic->ioaddr); + + printf("SMC9000 %s\n", smc9000_version); + DBG("Copyright (C) 1998 Daniel Engstr\x94m\n"); + DBG("Copyright (C) 1996 Eric Stahlman\n"); + + printf("%s rev:%d I/O port:%hX Interface:%s RAM:%d bytes \n", + version_string, revision & 0xF, + nic->ioaddr, if_string, memory ); + + DBG ( "Ethernet MAC address: %s\n", eth_ntoa ( nic->node_addr ) ); + + SMC_SELECT_BANK(nic->ioaddr, 0); + + /* see the header file for options in TCR/RCR NORMAL*/ + _outw(TCR_NORMAL, nic->ioaddr + TCR); + _outw(RCR_NORMAL, nic->ioaddr + RCR); + + /* Select which interface to use */ + SMC_SELECT_BANK(nic->ioaddr, 1); + if ( media == 1 ) { + _outw( inw( nic->ioaddr + CONFIG ) & ~CFG_AUI_SELECT, + nic->ioaddr + CONFIG ); + } + else if ( media == 2 ) { + _outw( inw( nic->ioaddr + CONFIG ) | CFG_AUI_SELECT, + nic->ioaddr + CONFIG ); + } + + smc_phy_configure(nic->ioaddr); + + nic->nic_op = &smc9000_operations; + return 1; +} + +/* + * The SMC9000 can be at any of the following port addresses. To + * change for a slightly different card, you can add it to the array. + * + */ +static isa_probe_addr_t smc9000_probe_addrs[] = { + 0x200, 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x2E0, + 0x300, 0x320, 0x340, 0x360, 0x380, 0x3A0, 0x3C0, 0x3E0, +}; + +ISA_DRIVER ( smc9000_driver, smc9000_probe_addrs, smc9000_probe_addr, + GENERIC_ISAPNP_VENDOR, 0x8228 ); + +DRIVER ( "SMC9000", nic_driver, isa_driver, smc9000_driver, + smc9000_probe, smc9000_disable ); + +ISA_ROM ( "smc9000", "SMC9000" ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/smc9000.h b/gpxe/src/drivers/net/smc9000.h new file mode 100644 index 0000000..318b779 --- /dev/null +++ b/gpxe/src/drivers/net/smc9000.h @@ -0,0 +1,433 @@ +/*------------------------------------------------------------------------ + * smc9000.h + * + * Copyright (C) 1998 by Daniel Engström + * Copyright (C) 1996 by Erik Stahlman + * + * This software may be used and distributed according to the terms + * of the GNU Public License, incorporated herein by reference. + * + * This file contains register information and access macros for + * the SMC91xxx chipset. + * + * Information contained in this file was obtained from the SMC91C94 + * manual from SMC. To get a copy, if you really want one, you can find + * information under www.smsc.com in the components division. + * ( this thanks to advice from Donald Becker ). + * + * Authors + * Daniel Engström + * 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 */ + +// Receive/Phy Control Register +/* BANK 0 */ +#define RPC_REG 0x000A +#define RPC_SPEED 0x2000 // When 1 PHY is in 100Mbps mode. +#define RPC_DPLX 0x1000 // When 1 PHY is in Full-Duplex Mode +#define RPC_ANEG 0x0800 // When 1 PHY is in Auto-Negotiate Mode +#define RPC_LSXA_SHFT 5 // Bits to shift LS2A,LS1A,LS0A to lsb +#define RPC_LSXB_SHFT 2 // Bits to get LS2B,LS1B,LS0B to lsb +#define RPC_LED_100_10 (0x00) // LED = 100Mbps OR's with 10Mbps link detect +#define RPC_LED_RES (0x01) // LED = Reserved +#define RPC_LED_10 (0x02) // LED = 10Mbps link detect +#define RPC_LED_FD (0x03) // LED = Full Duplex Mode +#define RPC_LED_TX_RX (0x04) // LED = TX or RX packet occurred +#define RPC_LED_100 (0x05) // LED = 100Mbps link dectect +#define RPC_LED_TX (0x06) // LED = TX packet occurred +#define RPC_LED_RX (0x07) // LED = RX packet occurred +#define RPC_DEFAULT (RPC_ANEG | (RPC_LED_100 << RPC_LSXA_SHFT) | (RPC_LED_FD << RPC_LSXB_SHFT) | RPC_SPEED | RPC_DPLX) + +// Receive/Phy Control Register +/* BANK 0 */ +#define RPC_REG 0x000A +#define RPC_SPEED 0x2000 // When 1 PHY is in 100Mbps mode. +#define RPC_DPLX 0x1000 // When 1 PHY is in Full-Duplex Mode +#define RPC_ANEG 0x0800 // When 1 PHY is in Auto-Negotiate Mode +#define RPC_LSXA_SHFT 5 // Bits to shift LS2A,LS1A,LS0A to lsb +#define RPC_LSXB_SHFT 2 // Bits to get LS2B,LS1B,LS0B to lsb +#define RPC_LED_100_10 (0x00) // LED = 100Mbps OR's with 10Mbps link detect +#define RPC_LED_RES (0x01) // LED = Reserved +#define RPC_LED_10 (0x02) // LED = 10Mbps link detect +#define RPC_LED_FD (0x03) // LED = Full Duplex Mode +#define RPC_LED_TX_RX (0x04) // LED = TX or RX packet occurred +#define RPC_LED_100 (0x05) // LED = 100Mbps link dectect +#define RPC_LED_TX (0x06) // LED = TX packet occurred +#define RPC_LED_RX (0x07) // LED = RX packet occurred +#define RPC_DEFAULT (RPC_ANEG | (RPC_LED_100 << RPC_LSXA_SHFT) | (RPC_LED_FD << RPC_LSXB_SHFT) | RPC_SPEED | RPC_DPLX) + +/* BANK 1 */ +#define CONFIG 0 +#define CFG_AUI_SELECT 0x100 +#define BASE 2 +#define ADDR0 4 +#define ADDR1 6 +#define ADDR2 8 +#define GENERAL 10 +#define CONTROL 12 +#define CTL_POWERDOWN 0x2000 +#define CTL_LE_ENABLE 0x80 +#define CTL_CR_ENABLE 0x40 +#define CTL_TE_ENABLE 0x0020 +#define CTL_AUTO_RELEASE 0x0800 +#define CTL_EPROM_ACCESS 0x0003 /* high if Eprom is being read */ + +/* BANK 2 */ +#define MMU_CMD 0 +#define MC_BUSY 1 /* only readable bit in the register */ +#define MC_NOP 0 +#define MC_ALLOC 0x20 /* or with number of 256 byte packets */ +#define MC_RESET 0x40 +#define MC_REMOVE 0x60 /* remove the current rx packet */ +#define MC_RELEASE 0x80 /* remove and release the current rx packet */ +#define MC_FREEPKT 0xA0 /* Release packet in PNR register */ +#define MC_ENQUEUE 0xC0 /* Enqueue the packet for transmit */ + +#define PNR_ARR 2 +#define FIFO_PORTS 4 + +#define FP_RXEMPTY 0x8000 +#define FP_TXEMPTY 0x80 + +#define POINTER 6 +#define PTR_READ 0x2000 +#define PTR_RCV 0x8000 +#define PTR_AUTOINC 0x4000 +#define PTR_AUTO_INC 0x0040 + +#define DATA_1 8 +#define DATA_2 10 +#define INTERRUPT 12 + +#define INT_MASK 13 +#define IM_RCV_INT 0x1 +#define IM_TX_INT 0x2 +#define IM_TX_EMPTY_INT 0x4 +#define IM_ALLOC_INT 0x8 +#define IM_RX_OVRN_INT 0x10 +#define IM_EPH_INT 0x20 +#define IM_ERCV_INT 0x40 /* not on SMC9192 */ + +/* BANK 3 */ +#define MULTICAST1 0 +#define MULTICAST2 2 +#define MULTICAST3 4 +#define MULTICAST4 6 +#define MGMT 8 +#define REVISION 10 /* ( hi: chip id low: rev # ) */ + +// Management Interface Register (MII) +#define MII_REG 0x0008 +#define MII_MSK_CRS100 0x4000 // Disables CRS100 detection during tx half dup +#define MII_MDOE 0x0008 // MII Output Enable +#define MII_MCLK 0x0004 // MII Clock, pin MDCLK +#define MII_MDI 0x0002 // MII Input, pin MDI +#define MII_MDO 0x0001 // MII Output, pin MDO + +/* this is NOT on SMC9192 */ +#define ERCV 12 + +/* Note that 9194 and 9196 have the smame chip id, + * the 9196 will have revisions starting at 6 */ +#define CHIP_9190 3 +#define CHIP_9194 4 +#define CHIP_9195 5 +#define CHIP_9196 4 +#define CHIP_91100 7 +#define CHIP_91100FD 8 + +#define REV_9196 6 + +/* + * Transmit status bits + */ +#define TS_SUCCESS 0x0001 +#define TS_LOSTCAR 0x0400 +#define TS_LATCOL 0x0200 +#define TS_16COL 0x0010 + +/* + * Receive status bits + */ +#define RS_ALGNERR 0x8000 +#define RS_BADCRC 0x2000 +#define RS_ODDFRAME 0x1000 +#define RS_TOOLONG 0x0800 +#define RS_TOOSHORT 0x0400 +#define RS_MULTICAST 0x0001 +#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT) + +// Management Interface Register (MII) +#define MII_REG 0x0008 +#define MII_MSK_CRS100 0x4000 // Disables CRS100 detection during tx half dup +#define MII_MDOE 0x0008 // MII Output Enable +#define MII_MCLK 0x0004 // MII Clock, pin MDCLK +#define MII_MDI 0x0002 // MII Input, pin MDI +#define MII_MDO 0x0001 // MII Output, pin MDO + +// PHY Register Addresses (LAN91C111 Internal PHY) + +// PHY Control Register +#define PHY_CNTL_REG 0x00 +#define PHY_CNTL_RST 0x8000 // 1=PHY Reset +#define PHY_CNTL_LPBK 0x4000 // 1=PHY Loopback +#define PHY_CNTL_SPEED 0x2000 // 1=100Mbps, 0=10Mpbs +#define PHY_CNTL_ANEG_EN 0x1000 // 1=Enable Auto negotiation +#define PHY_CNTL_PDN 0x0800 // 1=PHY Power Down mode +#define PHY_CNTL_MII_DIS 0x0400 // 1=MII 4 bit interface disabled +#define PHY_CNTL_ANEG_RST 0x0200 // 1=Reset Auto negotiate +#define PHY_CNTL_DPLX 0x0100 // 1=Full Duplex, 0=Half Duplex +#define PHY_CNTL_COLTST 0x0080 // 1= MII Colision Test + +// PHY Status Register +#define PHY_STAT_REG 0x01 +#define PHY_STAT_CAP_T4 0x8000 // 1=100Base-T4 capable +#define PHY_STAT_CAP_TXF 0x4000 // 1=100Base-X full duplex capable +#define PHY_STAT_CAP_TXH 0x2000 // 1=100Base-X half duplex capable +#define PHY_STAT_CAP_TF 0x1000 // 1=10Mbps full duplex capable +#define PHY_STAT_CAP_TH 0x0800 // 1=10Mbps half duplex capable +#define PHY_STAT_CAP_SUPR 0x0040 // 1=recv mgmt frames with not preamble +#define PHY_STAT_ANEG_ACK 0x0020 // 1=ANEG has completed +#define PHY_STAT_REM_FLT 0x0010 // 1=Remote Fault detected +#define PHY_STAT_CAP_ANEG 0x0008 // 1=Auto negotiate capable +#define PHY_STAT_LINK 0x0004 // 1=valid link +#define PHY_STAT_JAB 0x0002 // 1=10Mbps jabber condition +#define PHY_STAT_EXREG 0x0001 // 1=extended registers implemented + +// PHY Identifier Registers +#define PHY_ID1_REG 0x02 // PHY Identifier 1 +#define PHY_ID2_REG 0x03 // PHY Identifier 2 + +// PHY Auto-Negotiation Advertisement Register +#define PHY_AD_REG 0x04 +#define PHY_AD_NP 0x8000 // 1=PHY requests exchange of Next Page +#define PHY_AD_ACK 0x4000 // 1=got link code word from remote +#define PHY_AD_RF 0x2000 // 1=advertise remote fault +#define PHY_AD_T4 0x0200 // 1=PHY is capable of 100Base-T4 +#define PHY_AD_TX_FDX 0x0100 // 1=PHY is capable of 100Base-TX FDPLX +#define PHY_AD_TX_HDX 0x0080 // 1=PHY is capable of 100Base-TX HDPLX +#define PHY_AD_10_FDX 0x0040 // 1=PHY is capable of 10Base-T FDPLX +#define PHY_AD_10_HDX 0x0020 // 1=PHY is capable of 10Base-T HDPLX +#define PHY_AD_CSMA 0x0001 // 1=PHY is capable of 802.3 CMSA + +// PHY Auto-negotiation Remote End Capability Register +#define PHY_RMT_REG 0x05 +// Uses same bit definitions as PHY_AD_REG + +// PHY Configuration Register 1 +#define PHY_CFG1_REG 0x10 +#define PHY_CFG1_LNKDIS 0x8000 // 1=Rx Link Detect Function disabled +#define PHY_CFG1_XMTDIS 0x4000 // 1=TP Transmitter Disabled +#define PHY_CFG1_XMTPDN 0x2000 // 1=TP Transmitter Powered Down +#define PHY_CFG1_BYPSCR 0x0400 // 1=Bypass scrambler/descrambler +#define PHY_CFG1_UNSCDS 0x0200 // 1=Unscramble Idle Reception Disable +#define PHY_CFG1_EQLZR 0x0100 // 1=Rx Equalizer Disabled +#define PHY_CFG1_CABLE 0x0080 // 1=STP(150ohm), 0=UTP(100ohm) +#define PHY_CFG1_RLVL0 0x0040 // 1=Rx Squelch level reduced by 4.5db +#define PHY_CFG1_TLVL_SHIFT 2 // Transmit Output Level Adjust +#define PHY_CFG1_TLVL_MASK 0x003C +#define PHY_CFG1_TRF_MASK 0x0003 // Transmitter Rise/Fall time + + +// PHY Configuration Register 2 +#define PHY_CFG2_REG 0x11 +#define PHY_CFG2_APOLDIS 0x0020 // 1=Auto Polarity Correction disabled +#define PHY_CFG2_JABDIS 0x0010 // 1=Jabber disabled +#define PHY_CFG2_MREG 0x0008 // 1=Multiple register access (MII mgt) +#define PHY_CFG2_INTMDIO 0x0004 // 1=Interrupt signaled with MDIO pulseo + +// PHY Status Output (and Interrupt status) Register +#define PHY_INT_REG 0x12 // Status Output (Interrupt Status) +#define PHY_INT_INT 0x8000 // 1=bits have changed since last read +#define PHY_INT_LNKFAIL 0x4000 // 1=Link Not detected +#define PHY_INT_LOSSSYNC 0x2000 // 1=Descrambler has lost sync +#define PHY_INT_CWRD 0x1000 // 1=Invalid 4B5B code detected on rx +#define PHY_INT_SSD 0x0800 // 1=No Start Of Stream detected on rx +#define PHY_INT_ESD 0x0400 // 1=No End Of Stream detected on rx +#define PHY_INT_RPOL 0x0200 // 1=Reverse Polarity detected +#define PHY_INT_JAB 0x0100 // 1=Jabber detected +#define PHY_INT_SPDDET 0x0080 // 1=100Base-TX mode, 0=10Base-T mode +#define PHY_INT_DPLXDET 0x0040 // 1=Device in Full Duplex + +// PHY Interrupt/Status Mask Register +#define PHY_MASK_REG 0x13 // Interrupt Mask +// Uses the same bit definitions as PHY_INT_REG + + +// PHY Register Addresses (LAN91C111 Internal PHY) + +// PHY Control Register +#define PHY_CNTL_REG 0x00 +#define PHY_CNTL_RST 0x8000 // 1=PHY Reset +#define PHY_CNTL_LPBK 0x4000 // 1=PHY Loopback +#define PHY_CNTL_SPEED 0x2000 // 1=100Mbps, 0=10Mpbs +#define PHY_CNTL_ANEG_EN 0x1000 // 1=Enable Auto negotiation +#define PHY_CNTL_PDN 0x0800 // 1=PHY Power Down mode +#define PHY_CNTL_MII_DIS 0x0400 // 1=MII 4 bit interface disabled +#define PHY_CNTL_ANEG_RST 0x0200 // 1=Reset Auto negotiate +#define PHY_CNTL_DPLX 0x0100 // 1=Full Duplex, 0=Half Duplex +#define PHY_CNTL_COLTST 0x0080 // 1= MII Colision Test + +// PHY Status Register +#define PHY_STAT_REG 0x01 +#define PHY_STAT_CAP_T4 0x8000 // 1=100Base-T4 capable +#define PHY_STAT_CAP_TXF 0x4000 // 1=100Base-X full duplex capable +#define PHY_STAT_CAP_TXH 0x2000 // 1=100Base-X half duplex capable +#define PHY_STAT_CAP_TF 0x1000 // 1=10Mbps full duplex capable +#define PHY_STAT_CAP_TH 0x0800 // 1=10Mbps half duplex capable +#define PHY_STAT_CAP_SUPR 0x0040 // 1=recv mgmt frames with not preamble +#define PHY_STAT_ANEG_ACK 0x0020 // 1=ANEG has completed +#define PHY_STAT_REM_FLT 0x0010 // 1=Remote Fault detected +#define PHY_STAT_CAP_ANEG 0x0008 // 1=Auto negotiate capable +#define PHY_STAT_LINK 0x0004 // 1=valid link +#define PHY_STAT_JAB 0x0002 // 1=10Mbps jabber condition +#define PHY_STAT_EXREG 0x0001 // 1=extended registers implemented + +// PHY Identifier Registers +#define PHY_ID1_REG 0x02 // PHY Identifier 1 +#define PHY_ID2_REG 0x03 // PHY Identifier 2 + +// PHY Auto-Negotiation Advertisement Register +#define PHY_AD_REG 0x04 +#define PHY_AD_NP 0x8000 // 1=PHY requests exchange of Next Page +#define PHY_AD_ACK 0x4000 // 1=got link code word from remote +#define PHY_AD_RF 0x2000 // 1=advertise remote fault +#define PHY_AD_T4 0x0200 // 1=PHY is capable of 100Base-T4 +#define PHY_AD_TX_FDX 0x0100 // 1=PHY is capable of 100Base-TX FDPLX +#define PHY_AD_TX_HDX 0x0080 // 1=PHY is capable of 100Base-TX HDPLX +#define PHY_AD_10_FDX 0x0040 // 1=PHY is capable of 10Base-T FDPLX +#define PHY_AD_10_HDX 0x0020 // 1=PHY is capable of 10Base-T HDPLX +#define PHY_AD_CSMA 0x0001 // 1=PHY is capable of 802.3 CMSA + +// PHY Auto-negotiation Remote End Capability Register +#define PHY_RMT_REG 0x05 +// Uses same bit definitions as PHY_AD_REG + +// PHY Configuration Register 1 +#define PHY_CFG1_REG 0x10 +#define PHY_CFG1_LNKDIS 0x8000 // 1=Rx Link Detect Function disabled +#define PHY_CFG1_XMTDIS 0x4000 // 1=TP Transmitter Disabled +#define PHY_CFG1_XMTPDN 0x2000 // 1=TP Transmitter Powered Down +#define PHY_CFG1_BYPSCR 0x0400 // 1=Bypass scrambler/descrambler +#define PHY_CFG1_UNSCDS 0x0200 // 1=Unscramble Idle Reception Disable +#define PHY_CFG1_EQLZR 0x0100 // 1=Rx Equalizer Disabled +#define PHY_CFG1_CABLE 0x0080 // 1=STP(150ohm), 0=UTP(100ohm) +#define PHY_CFG1_RLVL0 0x0040 // 1=Rx Squelch level reduced by 4.5db +#define PHY_CFG1_TLVL_SHIFT 2 // Transmit Output Level Adjust +#define PHY_CFG1_TLVL_MASK 0x003C +#define PHY_CFG1_TRF_MASK 0x0003 // Transmitter Rise/Fall time + + +// PHY Configuration Register 2 +#define PHY_CFG2_REG 0x11 +#define PHY_CFG2_APOLDIS 0x0020 // 1=Auto Polarity Correction disabled +#define PHY_CFG2_JABDIS 0x0010 // 1=Jabber disabled +#define PHY_CFG2_MREG 0x0008 // 1=Multiple register access (MII mgt) +#define PHY_CFG2_INTMDIO 0x0004 // 1=Interrupt signaled with MDIO pulseo + +// PHY Status Output (and Interrupt status) Register +#define PHY_INT_REG 0x12 // Status Output (Interrupt Status) +#define PHY_INT_INT 0x8000 // 1=bits have changed since last read +#define PHY_INT_LNKFAIL 0x4000 // 1=Link Not detected +#define PHY_INT_LOSSSYNC 0x2000 // 1=Descrambler has lost sync +#define PHY_INT_CWRD 0x1000 // 1=Invalid 4B5B code detected on rx +#define PHY_INT_SSD 0x0800 // 1=No Start Of Stream detected on rx +#define PHY_INT_ESD 0x0400 // 1=No End Of Stream detected on rx +#define PHY_INT_RPOL 0x0200 // 1=Reverse Polarity detected +#define PHY_INT_JAB 0x0100 // 1=Jabber detected +#define PHY_INT_SPDDET 0x0080 // 1=100Base-TX mode, 0=10Base-T mode +#define PHY_INT_DPLXDET 0x0040 // 1=Device in Full Duplex + +// PHY Interrupt/Status Mask Register +#define PHY_MASK_REG 0x13 // Interrupt Mask +// Uses the same bit definitions as PHY_INT_REG + + +/*------------------------------------------------------------------------- + * I define some macros to make it easier to do somewhat common + * or slightly complicated, repeated tasks. + --------------------------------------------------------------------------*/ + +/* select a register bank, 0 to 3 */ + +#define SMC_SELECT_BANK(x, y) { _outw( y, x + BANK_SELECT ); } + +/* define a small delay for the reset */ +#define SMC_DELAY(x) { inw( x + RCR );\ + inw( x + RCR );\ + inw( x + RCR ); } + + +#endif /* _SMC_9000_H_ */ + diff --git a/gpxe/src/drivers/net/sundance.c b/gpxe/src/drivers/net/sundance.c new file mode 100644 index 0000000..119c977 --- /dev/null +++ b/gpxe/src/drivers/net/sundance.c @@ -0,0 +1,902 @@ +/************************************************************************** +* +* 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 +#include "mii.h" + +#define drv_version "v1.12" +#define drv_date "2004-03-21" + +#define HZ 100 + +/* Condensed operations for readability. */ +#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr)) + +/* May need to be moved to mii.h */ +struct mii_if_info { + int phy_id; + int advertising; + unsigned int full_duplex:1; /* is full duplex? */ +}; + +/* Set the mtu */ +static int mtu = 1514; + +/* Maximum number of multicast addresses to filter (vs. rx-all-multicast). + The sundance uses a 64 element hash table based on the Ethernet CRC. */ +// static int multicast_filter_limit = 32; + +/* Set the copy breakpoint for the copy-only-tiny-frames scheme. + Setting to > 1518 effectively disables this feature. + This chip can receive into any byte alignment buffers, so word-oriented + archs do not need a copy-align of the IP header. */ +static int rx_copybreak = 0; +static int flowctrl = 1; + +/* Allow forcing the media type */ +/* media[] specifies the media type the NIC operates at. + autosense Autosensing active media. + 10mbps_hd 10Mbps half duplex. + 10mbps_fd 10Mbps full duplex. + 100mbps_hd 100Mbps half duplex. + 100mbps_fd 100Mbps full duplex. +*/ +static char media[] = "autosense"; + +/* Operational parameters that are set at compile time. */ + +/* As Etherboot uses a Polling driver we can keep the number of rings +to the minimum number required. In general that is 1 transmit and 4 receive receive rings. However some cards require that +there be a minimum of 2 rings */ +#define TX_RING_SIZE 2 +#define TX_QUEUE_LEN 10 /* Limit ring entries actually used. */ +#define RX_RING_SIZE 4 + + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIME_OUT (4*HZ) +#define PKT_BUF_SZ 1536 + +/* Offsets to the device registers. + Unlike software-only systems, device drivers interact with complex hardware. + It's not useful to define symbolic names for every register bit in the + device. The name can only partially document the semantics and make + the driver longer and more difficult to read. + In general, only the important configuration values or bits changed + multiple times should be defined symbolically. +*/ +enum alta_offsets { + DMACtrl = 0x00, + TxListPtr = 0x04, + TxDMABurstThresh = 0x08, + TxDMAUrgentThresh = 0x09, + TxDMAPollPeriod = 0x0a, + RxDMAStatus = 0x0c, + RxListPtr = 0x10, + DebugCtrl0 = 0x1a, + DebugCtrl1 = 0x1c, + RxDMABurstThresh = 0x14, + RxDMAUrgentThresh = 0x15, + RxDMAPollPeriod = 0x16, + LEDCtrl = 0x1a, + ASICCtrl = 0x30, + EEData = 0x34, + EECtrl = 0x36, + TxStartThresh = 0x3c, + RxEarlyThresh = 0x3e, + FlashAddr = 0x40, + FlashData = 0x44, + TxStatus = 0x46, + TxFrameId = 0x47, + DownCounter = 0x18, + IntrClear = 0x4a, + IntrEnable = 0x4c, + IntrStatus = 0x4e, + MACCtrl0 = 0x50, + MACCtrl1 = 0x52, + StationAddr = 0x54, + MaxFrameSize = 0x5A, + RxMode = 0x5c, + MIICtrl = 0x5e, + MulticastFilter0 = 0x60, + MulticastFilter1 = 0x64, + RxOctetsLow = 0x68, + RxOctetsHigh = 0x6a, + TxOctetsLow = 0x6c, + TxOctetsHigh = 0x6e, + TxFramesOK = 0x70, + RxFramesOK = 0x72, + StatsCarrierError = 0x74, + StatsLateColl = 0x75, + StatsMultiColl = 0x76, + StatsOneColl = 0x77, + StatsTxDefer = 0x78, + RxMissed = 0x79, + StatsTxXSDefer = 0x7a, + StatsTxAbort = 0x7b, + StatsBcastTx = 0x7c, + StatsBcastRx = 0x7d, + StatsMcastTx = 0x7e, + StatsMcastRx = 0x7f, + /* Aliased and bogus values! */ + RxStatus = 0x0c, +}; +enum ASICCtrl_HiWord_bit { + GlobalReset = 0x0001, + RxReset = 0x0002, + TxReset = 0x0004, + DMAReset = 0x0008, + FIFOReset = 0x0010, + NetworkReset = 0x0020, + HostReset = 0x0040, + ResetBusy = 0x0400, +}; + +/* Bits in the interrupt status/mask registers. */ +enum intr_status_bits { + IntrSummary = 0x0001, IntrPCIErr = 0x0002, IntrMACCtrl = 0x0008, + IntrTxDone = 0x0004, IntrRxDone = 0x0010, IntrRxStart = 0x0020, + IntrDrvRqst = 0x0040, + StatsMax = 0x0080, LinkChange = 0x0100, + IntrTxDMADone = 0x0200, IntrRxDMADone = 0x0400, +}; + +/* Bits in the RxMode register. */ +enum rx_mode_bits { + AcceptAllIPMulti = 0x20, AcceptMultiHash = 0x10, AcceptAll = 0x08, + AcceptBroadcast = 0x04, AcceptMulticast = 0x02, AcceptMyPhys = + 0x01, +}; +/* Bits in MACCtrl. */ +enum mac_ctrl0_bits { + EnbFullDuplex = 0x20, EnbRcvLargeFrame = 0x40, + EnbFlowCtrl = 0x100, EnbPassRxCRC = 0x200, +}; +enum mac_ctrl1_bits { + StatsEnable = 0x0020, StatsDisable = 0x0040, StatsEnabled = 0x0080, + TxEnable = 0x0100, TxDisable = 0x0200, TxEnabled = 0x0400, + RxEnable = 0x0800, RxDisable = 0x1000, RxEnabled = 0x2000, +}; + +/* The Rx and Tx buffer descriptors. + Using only 32 bit fields simplifies software endian correction. + This structure must be aligned, and should avoid spanning cache lines. +*/ +struct netdev_desc { + u32 next_desc; + u32 status; + u32 addr; + u32 length; +}; + +/* Bits in netdev_desc.status */ +enum desc_status_bits { + DescOwn = 0x8000, + DescEndPacket = 0x4000, + DescEndRing = 0x2000, + LastFrag = 0x80000000, + DescIntrOnTx = 0x8000, + DescIntrOnDMADone = 0x80000000, + DisableAlign = 0x00000001, +}; + +/********************************************** +* Descriptor Ring and Buffer defination +***********************************************/ +/* Define the TX Descriptor */ +static struct netdev_desc tx_ring[TX_RING_SIZE]; + +/* Define the RX Descriptor */ +static struct netdev_desc rx_ring[RX_RING_SIZE]; + +/* Create a static buffer of size PKT_BUF_SZ for each RX and TX descriptor. + All descriptors point to a part of this buffer */ +struct { + unsigned char txb[PKT_BUF_SZ * TX_RING_SIZE]; + unsigned char rxb[RX_RING_SIZE * PKT_BUF_SZ]; +} rx_tx_buf __shared; +#define rxb rx_tx_buf.rxb +#define txb rx_tx_buf.txb + +/* FIXME: Move BASE to the private structure */ +static u32 BASE; +#define EEPROM_SIZE 128 + +enum pci_id_flags_bits { + PCI_USES_IO = 1, PCI_USES_MEM = 2, PCI_USES_MASTER = 4, + PCI_ADDR0 = 0 << 4, PCI_ADDR1 = 1 << 4, PCI_ADDR2 = + 2 << 4, PCI_ADDR3 = 3 << 4, +}; + +enum chip_capability_flags { CanHaveMII = 1, KendinPktDropBug = 2, }; +#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_IO | PCI_ADDR0) + +#define MII_CNT 4 +static struct sundance_private { + const char *nic_name; + /* Frequently used values */ + + unsigned int cur_rx; /* Producer/consumer ring indicies */ + unsigned int mtu; + + /* These values keep track of the tranceiver/media in use */ + unsigned int flowctrl:1; + unsigned int an_enable:1; + + unsigned int speed; + + /* MII tranceiver section */ + struct mii_if_info mii_if; + int mii_preamble_required; + unsigned char phys[MII_CNT]; + unsigned char pci_rev_id; +} sdx; + +static struct sundance_private *sdc; + +/* Station Address location within the EEPROM */ +#define EEPROM_SA_OFFSET 0x10 +#define DEFAULT_INTR (IntrRxDMADone | IntrPCIErr | \ + IntrDrvRqst | IntrTxDone | StatsMax | \ + LinkChange) + +static int eeprom_read(long ioaddr, int location); +static int mdio_read(struct nic *nic, int phy_id, unsigned int location); +static void mdio_write(struct nic *nic, int phy_id, unsigned int location, + int value); +static void set_rx_mode(struct nic *nic); + +static void check_duplex(struct nic *nic) +{ + int mii_lpa = mdio_read(nic, sdc->phys[0], MII_LPA); + int negotiated = mii_lpa & sdc->mii_if.advertising; + int duplex; + + /* Force media */ + if (!sdc->an_enable || mii_lpa == 0xffff) { + if (sdc->mii_if.full_duplex) + outw(inw(BASE + MACCtrl0) | EnbFullDuplex, + BASE + MACCtrl0); + return; + } + + /* Autonegotiation */ + duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040; + if (sdc->mii_if.full_duplex != duplex) { + sdc->mii_if.full_duplex = duplex; + DBG ("%s: Setting %s-duplex based on MII #%d " + "negotiated capability %4.4x.\n", sdc->nic_name, + duplex ? "full" : "half", sdc->phys[0], + negotiated ); + outw(inw(BASE + MACCtrl0) | duplex ? 0x20 : 0, + BASE + MACCtrl0); + } +} + + +/************************************************************************** + * init_ring - setup the tx and rx descriptors + *************************************************************************/ +static void init_ring(struct nic *nic __unused) +{ + int i; + + sdc->cur_rx = 0; + + /* Initialize all the Rx descriptors */ + for (i = 0; i < RX_RING_SIZE; i++) { + rx_ring[i].next_desc = virt_to_le32desc(&rx_ring[i + 1]); + rx_ring[i].status = 0; + rx_ring[i].length = 0; + rx_ring[i].addr = 0; + } + + /* Mark the last entry as wrapping the ring */ + rx_ring[i - 1].next_desc = virt_to_le32desc(&rx_ring[0]); + + for (i = 0; i < RX_RING_SIZE; i++) { + rx_ring[i].addr = virt_to_le32desc(&rxb[i * PKT_BUF_SZ]); + rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ | LastFrag); + } + + /* We only use one transmit buffer, but two + * descriptors so transmit engines have somewhere + * to point should they feel the need */ + tx_ring[0].status = 0x00000000; + tx_ring[0].addr = virt_to_bus(&txb[0]); + tx_ring[0].next_desc = 0; /* virt_to_bus(&tx_ring[1]); */ + + /* This descriptor is never used */ + tx_ring[1].status = 0x00000000; + tx_ring[1].addr = 0; /*virt_to_bus(&txb[0]); */ + tx_ring[1].next_desc = 0; + + /* Mark the last entry as wrapping the ring, + * though this should never happen */ + tx_ring[1].length = cpu_to_le32(LastFrag | PKT_BUF_SZ); +} + +/************************************************************************** + * RESET - Reset Adapter + * ***********************************************************************/ +static void sundance_reset(struct nic *nic) +{ + int i; + + init_ring(nic); + + outl(virt_to_le32desc(&rx_ring[0]), BASE + RxListPtr); + /* The Tx List Pointer is written as packets are queued */ + + /* Initialize other registers. */ + /* __set_mac_addr(dev); */ + { + u16 addr16; + + addr16 = (nic->node_addr[0] | (nic->node_addr[1] << 8)); + outw(addr16, BASE + StationAddr); + addr16 = (nic->node_addr[2] | (nic->node_addr[3] << 8)); + outw(addr16, BASE + StationAddr + 2); + addr16 = (nic->node_addr[4] | (nic->node_addr[5] << 8)); + outw(addr16, BASE + StationAddr + 4); + } + + outw(sdc->mtu + 14, BASE + MaxFrameSize); + if (sdc->mtu > 2047) /* this will never happen with default options */ + outl(inl(BASE + ASICCtrl) | 0x0c, BASE + ASICCtrl); + + set_rx_mode(nic); + + outw(0, BASE + DownCounter); + /* Set the chip to poll every N*30nsec */ + outb(100, BASE + RxDMAPollPeriod); + + /* Fix DFE-580TX packet drop issue */ + if (sdc->pci_rev_id >= 0x14) + writeb(0x01, BASE + DebugCtrl1); + + outw(RxEnable | TxEnable, BASE + MACCtrl1); + + /* Construct a perfect filter frame with the mac address as first match + * and broadcast for all others */ + for (i = 0; i < 192; i++) + txb[i] = 0xFF; + + txb[0] = nic->node_addr[0]; + txb[1] = nic->node_addr[1]; + txb[2] = nic->node_addr[2]; + txb[3] = nic->node_addr[3]; + txb[4] = nic->node_addr[4]; + txb[5] = nic->node_addr[5]; + + DBG ( "%s: Done sundance_reset, status: Rx %hX Tx %hX " + "MAC Control %hX, %hX %hX\n", + sdc->nic_name, (int) inl(BASE + RxStatus), + (int) inw(BASE + TxStatus), (int) inl(BASE + MACCtrl0), + (int) inw(BASE + MACCtrl1), (int) inw(BASE + MACCtrl0) ); +} + +/************************************************************************** +IRQ - Wait for a frame +***************************************************************************/ +static void sundance_irq ( struct nic *nic, irq_action_t action ) { + unsigned int intr_status; + + switch ( action ) { + case DISABLE : + case ENABLE : + intr_status = inw(nic->ioaddr + IntrStatus); + intr_status = intr_status & ~DEFAULT_INTR; + if ( action == ENABLE ) + intr_status = intr_status | DEFAULT_INTR; + outw(intr_status, nic->ioaddr + IntrEnable); + break; + case FORCE : + outw(0x0200, BASE + ASICCtrl); + break; + } +} +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int sundance_poll(struct nic *nic, int retreive) +{ + /* return true if there's an ethernet packet ready to read */ + /* nic->packet should contain data on return */ + /* nic->packetlen should contain length of data */ + int entry = sdc->cur_rx % RX_RING_SIZE; + u32 frame_status = le32_to_cpu(rx_ring[entry].status); + int intr_status; + int pkt_len = 0; + + if (!(frame_status & DescOwn)) + return 0; + + /* There is a packet ready */ + if(!retreive) + return 1; + + intr_status = inw(nic->ioaddr + IntrStatus); + outw(intr_status, nic->ioaddr + IntrStatus); + + pkt_len = frame_status & 0x1fff; + + if (frame_status & 0x001f4000) { + DBG ( "Polling frame_status error\n" ); /* Do we really care about this */ + } else { + if (pkt_len < rx_copybreak) { + /* FIXME: What should happen Will this ever occur */ + printf("Poll Error: pkt_len < rx_copybreak"); + } else { + nic->packetlen = pkt_len; + memcpy(nic->packet, rxb + + (sdc->cur_rx * PKT_BUF_SZ), nic->packetlen); + + } + } + rx_ring[entry].length = cpu_to_le32(PKT_BUF_SZ | LastFrag); + rx_ring[entry].status = 0; + entry++; + sdc->cur_rx = entry % RX_RING_SIZE; + outw(DEFAULT_INTR & ~(IntrRxDone|IntrRxDMADone), + nic->ioaddr + IntrStatus); + return 1; +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static void sundance_transmit(struct nic *nic, const char *d, /* Destination */ + unsigned int t, /* Type */ + unsigned int s, /* size */ + const char *p) +{ /* Packet */ + u16 nstype; + u32 to; + + /* Disable the Tx */ + outw(TxDisable, BASE + MACCtrl1); + + memcpy(txb, d, ETH_ALEN); + memcpy(txb + ETH_ALEN, nic->node_addr, ETH_ALEN); + nstype = htons((u16) t); + memcpy(txb + 2 * ETH_ALEN, (u8 *) & nstype, 2); + memcpy(txb + ETH_HLEN, p, s); + + s += ETH_HLEN; + s &= 0x0FFF; + while (s < ETH_ZLEN) + txb[s++] = '\0'; + + /* Setup the transmit descriptor */ + tx_ring[0].length = cpu_to_le32(s | LastFrag); + tx_ring[0].status = cpu_to_le32(0x00000001); + + /* Point to transmit descriptor */ + outl(virt_to_le32desc(&tx_ring[0]), BASE + TxListPtr); + + /* Enable Tx */ + outw(TxEnable, BASE + MACCtrl1); + /* Trigger an immediate send */ + outw(0, BASE + TxStatus); + + to = currticks() + TX_TIME_OUT; + while (!(tx_ring[0].status & 0x00010000) && (currticks() < to)); /* wait */ + + if (currticks() >= to) { + printf("TX Time Out"); + } + /* Disable Tx */ + outw(TxDisable, BASE + MACCtrl1); + +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void sundance_disable ( struct nic *nic __unused ) { + /* put the card in its initial state */ + /* This function serves 3 purposes. + * This disables DMA and interrupts so we don't receive + * unexpected packets or interrupts from the card after + * etherboot has finished. + * This frees resources so etherboot may use + * this driver on another interface + * This allows etherboot to reinitialize the interface + * if something is something goes wrong. + */ + outw(0x0000, BASE + IntrEnable); + /* Stop the Chipchips Tx and Rx Status */ + outw(TxDisable | RxDisable | StatsDisable, BASE + MACCtrl1); +} + +static struct nic_operations sundance_operations = { + .connect = dummy_connect, + .poll = sundance_poll, + .transmit = sundance_transmit, + .irq = sundance_irq, + +}; + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ +static int sundance_probe ( struct nic *nic, struct pci_device *pci ) { + + u8 ee_data[EEPROM_SIZE]; + u16 mii_ctl; + int i; + int speed; + + if (pci->ioaddr == 0) + return 0; + + /* BASE is used throughout to address the card */ + BASE = pci->ioaddr; + printf(" sundance.c: Found %s Vendor=0x%hX Device=0x%hX\n", + pci->driver_name, pci->vendor, pci->device); + + /* Get the MAC Address by reading the EEPROM */ + for (i = 0; i < 3; i++) { + ((u16 *) ee_data)[i] = + le16_to_cpu(eeprom_read(BASE, i + EEPROM_SA_OFFSET)); + } + /* Update the nic structure with the MAC Address */ + for (i = 0; i < ETH_ALEN; i++) { + nic->node_addr[i] = ee_data[i]; + } + + /* Set the card as PCI Bus Master */ + adjust_pci_device(pci); + +// sdc->mii_if.dev = pci; +// sdc->mii_if.phy_id_mask = 0x1f; +// sdc->mii_if.reg_num_mask = 0x1f; + + /* point to private storage */ + sdc = &sdx; + + sdc->nic_name = pci->driver_name; + sdc->mtu = mtu; + + pci_read_config_byte(pci, PCI_REVISION_ID, &sdc->pci_rev_id); + + DBG ( "Device revision id: %hx\n", sdc->pci_rev_id ); + + /* Print out some hardware info */ + DBG ( "%s: %s at ioaddr %hX, ", pci->driver_name, nic->node_addr, (unsigned int) BASE); + + sdc->mii_preamble_required = 0; + if (1) { + int phy, phy_idx = 0; + sdc->phys[0] = 1; /* Default Setting */ + sdc->mii_preamble_required++; + for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) { + int mii_status = mdio_read(nic, phy, MII_BMSR); + if (mii_status != 0xffff && mii_status != 0x0000) { + sdc->phys[phy_idx++] = phy; + sdc->mii_if.advertising = + mdio_read(nic, phy, MII_ADVERTISE); + if ((mii_status & 0x0040) == 0) + sdc->mii_preamble_required++; + DBG + ( "%s: MII PHY found at address %d, status " "%hX advertising %hX\n", sdc->nic_name, phy, mii_status, sdc->mii_if.advertising ); + } + } + sdc->mii_preamble_required--; + if (phy_idx == 0) + printf("%s: No MII transceiver found!\n", + sdc->nic_name); + sdc->mii_if.phy_id = sdc->phys[0]; + } + + /* Parse override configuration */ + sdc->an_enable = 1; + if (strcasecmp(media, "autosense") != 0) { + sdc->an_enable = 0; + if (strcasecmp(media, "100mbps_fd") == 0 || + strcasecmp(media, "4") == 0) { + sdc->speed = 100; + sdc->mii_if.full_duplex = 1; + } else if (strcasecmp(media, "100mbps_hd") == 0 + || strcasecmp(media, "3") == 0) { + sdc->speed = 100; + sdc->mii_if.full_duplex = 0; + } else if (strcasecmp(media, "10mbps_fd") == 0 || + strcasecmp(media, "2") == 0) { + sdc->speed = 10; + sdc->mii_if.full_duplex = 1; + } else if (strcasecmp(media, "10mbps_hd") == 0 || + strcasecmp(media, "1") == 0) { + sdc->speed = 10; + sdc->mii_if.full_duplex = 0; + } else { + sdc->an_enable = 1; + } + } + if (flowctrl == 1) + sdc->flowctrl = 1; + + /* Fibre PHY? */ + if (inl(BASE + ASICCtrl) & 0x80) { + /* Default 100Mbps Full */ + if (sdc->an_enable) { + sdc->speed = 100; + sdc->mii_if.full_duplex = 1; + sdc->an_enable = 0; + } + } + + /* The Linux driver uses flow control and resets the link here. This means the + mii section from above would need to be re done I believe. Since it serves + no real purpose leave it out. */ + + /* Force media type */ + if (!sdc->an_enable) { + mii_ctl = 0; + mii_ctl |= (sdc->speed == 100) ? BMCR_SPEED100 : 0; + mii_ctl |= (sdc->mii_if.full_duplex) ? BMCR_FULLDPLX : 0; + mdio_write(nic, sdc->phys[0], MII_BMCR, mii_ctl); + printf("Override speed=%d, %s duplex\n", + sdc->speed, + sdc->mii_if.full_duplex ? "Full" : "Half"); + } + + /* Reset the chip to erase previous misconfiguration */ + DBG ( "ASIC Control is %#x\n", inl(BASE + ASICCtrl) ); + outw(0x007f, BASE + ASICCtrl + 2); + + /* + * wait for reset to complete + * this is heavily inspired by the linux sundance driver + * according to the linux driver it can take up to 1ms for the reset + * to complete + */ + i = 0; + while(inl(BASE + ASICCtrl) & (ResetBusy << 16)) { + if(i++ >= 10) { + DBG("sundance: NIC reset did not complete.\n"); + break; + } + udelay(100); + } + + DBG ( "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 */ + nic->nic_op = &sundance_operations; + + nic->irqno = pci->irq; + nic->ioaddr = BASE; + + return 1; +} + + +/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. */ +static int eeprom_read(long ioaddr, int location) +{ + int boguscnt = 10000; /* Typical 1900 ticks */ + outw(0x0200 | (location & 0xff), ioaddr + EECtrl); + do { + if (!(inw(ioaddr + EECtrl) & 0x8000)) { + return inw(ioaddr + EEData); + } + } + while (--boguscnt > 0); + return 0; +} + +/* MII transceiver control section. + Read and write the MII registers using software-generated serial + MDIO protocol. See the MII specifications or DP83840A data sheet + for details. + + The maximum data clock rate is 2.5 Mhz. + The timing is decoupled from the processor clock by flushing the write + from the CPU write buffer with a following read, and using PCI + transaction time. */ + +#define mdio_in(mdio_addr) inb(mdio_addr) +#define mdio_out(value, mdio_addr) outb(value, mdio_addr) +#define mdio_delay(mdio_addr) inb(mdio_addr) + +enum mii_reg_bits { + MDIO_ShiftClk = 0x0001, MDIO_Data = 0x0002, MDIO_EnbOutput = + 0x0004, +}; +#define MDIO_EnbIn (0) +#define MDIO_WRITE0 (MDIO_EnbOutput) +#define MDIO_WRITE1 (MDIO_Data | MDIO_EnbOutput) + +/* Generate the preamble required for initial synchronization and + a few older transceivers. */ +static void mdio_sync(long mdio_addr) +{ + int bits = 32; + + /* Establish sync by sending at least 32 logic ones. */ + while (--bits >= 0) { + mdio_out(MDIO_WRITE1, mdio_addr); + mdio_delay(mdio_addr); + mdio_out(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } +} + +static int +mdio_read(struct nic *nic __unused, int phy_id, unsigned int location) +{ + long mdio_addr = BASE + MIICtrl; + int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; + int i, retval = 0; + + if (sdc->mii_preamble_required) + mdio_sync(mdio_addr); + + /* Shift the read command bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = + (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + + mdio_out(dataval, mdio_addr); + mdio_delay(mdio_addr); + mdio_out(dataval | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 19; i > 0; i--) { + mdio_out(MDIO_EnbIn, mdio_addr); + mdio_delay(mdio_addr); + retval = (retval << 1) | ((mdio_in(mdio_addr) & MDIO_Data) + ? 1 : 0); + mdio_out(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } + return (retval >> 1) & 0xffff; +} + +static void +mdio_write(struct nic *nic __unused, int phy_id, + unsigned int location, int value) +{ + long mdio_addr = BASE + MIICtrl; + int mii_cmd = + (0x5002 << 16) | (phy_id << 23) | (location << 18) | value; + int i; + + if (sdc->mii_preamble_required) + mdio_sync(mdio_addr); + + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = + (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + mdio_out(dataval, mdio_addr); + mdio_delay(mdio_addr); + mdio_out(dataval | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + mdio_out(MDIO_EnbIn, mdio_addr); + mdio_delay(mdio_addr); + mdio_out(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } + return; +} + +static void set_rx_mode(struct nic *nic __unused) +{ + int i; + u16 mc_filter[4]; /* Multicast hash filter */ + u32 rx_mode; + + memset(mc_filter, 0xff, sizeof(mc_filter)); + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + + if (sdc->mii_if.full_duplex && sdc->flowctrl) + mc_filter[3] |= 0x0200; + for (i = 0; i < 4; i++) + outw(mc_filter[i], BASE + MulticastFilter0 + i * 2); + outb(rx_mode, BASE + RxMode); + return; +} + +static struct pci_device_id sundance_nics[] = { + PCI_ROM(0x13f0, 0x0201, "sundance", "ST201 Sundance 'Alta' based Adaptor"), + PCI_ROM(0x1186, 0x1002, "dfe530txs", "D-Link DFE530TXS (Sundance ST201 Alta)"), + PCI_ROM(0x13f0, 0x0200, "ip100a", "IC+ IP100A"), +}; + +PCI_DRIVER ( sundance_driver, sundance_nics, PCI_NO_CLASS ); + +DRIVER ( "SUNDANCE/PCI", nic_driver, pci_driver, sundance_driver, + sundance_probe, sundance_disable ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/tg3.c b/gpxe/src/drivers/net/tg3.c new file mode 100644 index 0000000..14093bb --- /dev/null +++ b/gpxe/src/drivers/net/tg3.c @@ -0,0 +1,3432 @@ +/* $Id$ + * tg3.c: Broadcom Tigon3 ethernet driver. + * + * Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com) + * Copyright (C) 2001, 2002 Jeff Garzik (jgarzik@mandrakesoft.com) + * Copyright (C) 2003 Eric Biederman (ebiederman@lnxi.com) [etherboot port] + */ + +/* 11-13-2003 timlegge Fix Issue with NetGear GA302T + * 11-18-2003 ebiederm Generalize NetGear Fix to what the code was supposed to be. + * 01-06-2005 Alf (Frederic Olivie) Add Dell bcm 5751 (0x1677) support + * 04-15-2005 Martin Vogt Add Fujitsu Siemens Computer (FSC) 0x1734 bcm 5751 0x105d support + */ + +#include "etherboot.h" +#include "nic.h" +#include +#include +#include +#include "string.h" +#include "tg3.h" + +#define SUPPORT_COPPER_PHY 1 +#define SUPPORT_FIBER_PHY 1 +#define SUPPORT_LINK_REPORT 1 +#define SUPPORT_PARTNO_STR 1 +#define SUPPORT_PHY_STR 1 + +static struct tg3 tg3; + +/* These numbers seem to be hard coded in the NIC firmware somehow. + * You can't change the ring sizes, but you can change where you place + * them in the NIC onboard memory. + */ +#define TG3_RX_RING_SIZE 512 +#define TG3_DEF_RX_RING_PENDING 20 /* RX_RING_PENDING seems to be o.k. at 20 and 200 */ +#define TG3_RX_RCB_RING_SIZE 1024 + +/* (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 ? \ + 512 : 1024) */ +#define TG3_TX_RING_SIZE 512 +#define TG3_DEF_TX_RING_PENDING (TG3_TX_RING_SIZE - 1) + +#define TG3_RX_RING_BYTES (sizeof(struct tg3_rx_buffer_desc) * TG3_RX_RING_SIZE) +#define TG3_RX_RCB_RING_BYTES (sizeof(struct tg3_rx_buffer_desc) * TG3_RX_RCB_RING_SIZE) + +#define TG3_TX_RING_BYTES (sizeof(struct tg3_tx_buffer_desc) * TG3_TX_RING_SIZE) +#define NEXT_TX(N) (((N) + 1) & (TG3_TX_RING_SIZE - 1)) +#define PREV_TX(N) (((N) - 1) & (TG3_TX_RING_SIZE - 1)) + +#define RX_PKT_BUF_SZ (1536 + 2 + 64) + +struct eth_frame { + uint8_t dst_addr[ETH_ALEN]; + uint8_t src_addr[ETH_ALEN]; + uint16_t type; + uint8_t data [ETH_FRAME_LEN - ETH_HLEN]; +}; + +struct bss { + struct tg3_rx_buffer_desc rx_std[TG3_RX_RING_SIZE]; + struct tg3_rx_buffer_desc rx_rcb[TG3_RX_RCB_RING_SIZE]; + struct tg3_tx_buffer_desc tx_ring[TG3_TX_RING_SIZE]; + struct tg3_hw_status hw_status; + struct tg3_hw_stats hw_stats; + unsigned char rx_bufs[TG3_DEF_RX_RING_PENDING][RX_PKT_BUF_SZ]; + struct eth_frame tx_frame[2]; +} tg3_bss __shared; + +/** + * pci_save_state - save the PCI configuration space of a device before suspending + * @dev: - PCI device that we're dealing with + * @buffer: - buffer to hold config space context + * + * @buffer must be large enough to hold the entire PCI 2.2 config space + * (>= 64 bytes). + */ +static int pci_save_state(struct pci_device *dev, uint32_t *buffer) +{ + int i; + for (i = 0; i < 16; i++) + pci_read_config_dword(dev, i * 4,&buffer[i]); + return 0; +} + +/** + * pci_restore_state - Restore the saved state of a PCI device + * @dev: - PCI device that we're dealing with + * @buffer: - saved PCI config space + * + */ +static int pci_restore_state(struct pci_device *dev, uint32_t *buffer) +{ + int i; + + for (i = 0; i < 16; i++) + pci_write_config_dword(dev,i * 4, buffer[i]); + return 0; +} + +static void tg3_write_indirect_reg32(uint32_t off, uint32_t val) +{ + pci_write_config_dword(tg3.pdev, TG3PCI_REG_BASE_ADDR, off); + pci_write_config_dword(tg3.pdev, TG3PCI_REG_DATA, val); +} + +#define tw32(reg,val) tg3_write_indirect_reg32((reg),(val)) +#define tw32_mailbox(reg, val) writel(((val) & 0xffffffff), tg3.regs + (reg)) +#define tw16(reg,val) writew(((val) & 0xffff), tg3.regs + (reg)) +#define tw8(reg,val) writeb(((val) & 0xff), tg3.regs + (reg)) +#define tr32(reg) readl(tg3.regs + (reg)) +#define tr16(reg) readw(tg3.regs + (reg)) +#define tr8(reg) readb(tg3.regs + (reg)) + +static void tw32_carefully(uint32_t reg, uint32_t val) +{ + tw32(reg, val); + tr32(reg); + udelay(100); +} + +static void tw32_mailbox2(uint32_t reg, uint32_t val) +{ + tw32_mailbox(reg, val); + tr32(reg); +} + +static void tg3_write_mem(uint32_t off, uint32_t val) +{ + pci_write_config_dword(tg3.pdev, TG3PCI_MEM_WIN_BASE_ADDR, off); + pci_write_config_dword(tg3.pdev, TG3PCI_MEM_WIN_DATA, val); + + /* Always leave this as zero. */ + pci_write_config_dword(tg3.pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0); +} + +static void tg3_read_mem(uint32_t off, uint32_t *val) +{ + pci_write_config_dword(tg3.pdev, TG3PCI_MEM_WIN_BASE_ADDR, off); + pci_read_config_dword(tg3.pdev, TG3PCI_MEM_WIN_DATA, val); + + /* Always leave this as zero. */ + pci_write_config_dword(tg3.pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0); +} + +static void tg3_disable_ints(struct tg3 *tp) +{ + tw32(TG3PCI_MISC_HOST_CTRL, + (tp->misc_host_ctrl | MISC_HOST_CTRL_MASK_PCI_INT)); + tw32_mailbox2(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001); +} + +static void tg3_switch_clocks(struct tg3 *tp) +{ + uint32_t orig_clock_ctrl, clock_ctrl; + + clock_ctrl = tr32(TG3PCI_CLOCK_CTRL); + + orig_clock_ctrl = clock_ctrl; + clock_ctrl &= (CLOCK_CTRL_FORCE_CLKRUN | CLOCK_CTRL_CLKRUN_OENABLE | 0x1f); + tp->pci_clock_ctrl = clock_ctrl; + + if ((GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) && + (!((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) + && (tp->tg3_flags & TG3_FLAG_ENABLE_ASF))) && + (orig_clock_ctrl & CLOCK_CTRL_44MHZ_CORE)!=0) { + tw32_carefully(TG3PCI_CLOCK_CTRL, + clock_ctrl | (CLOCK_CTRL_44MHZ_CORE | CLOCK_CTRL_ALTCLK)); + tw32_carefully(TG3PCI_CLOCK_CTRL, + clock_ctrl | (CLOCK_CTRL_ALTCLK)); + } + tw32_carefully(TG3PCI_CLOCK_CTRL, clock_ctrl); +} + +#define PHY_BUSY_LOOPS 5000 + +static int tg3_readphy(struct tg3 *tp, int reg, uint32_t *val) +{ + uint32_t frame_val; + int loops, ret; + + tw32_carefully(MAC_MI_MODE, tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL); + + *val = 0xffffffff; + + frame_val = ((PHY_ADDR << MI_COM_PHY_ADDR_SHIFT) & + MI_COM_PHY_ADDR_MASK); + frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) & + MI_COM_REG_ADDR_MASK); + frame_val |= (MI_COM_CMD_READ | MI_COM_START); + + tw32_carefully(MAC_MI_COM, frame_val); + + loops = PHY_BUSY_LOOPS; + while (loops-- > 0) { + udelay(10); + frame_val = tr32(MAC_MI_COM); + + if ((frame_val & MI_COM_BUSY) == 0) { + udelay(5); + frame_val = tr32(MAC_MI_COM); + break; + } + } + + ret = -EBUSY; + if (loops > 0) { + *val = frame_val & MI_COM_DATA_MASK; + ret = 0; + } + + tw32_carefully(MAC_MI_MODE, tp->mi_mode); + + return ret; +} + +static int tg3_writephy(struct tg3 *tp, int reg, uint32_t val) +{ + uint32_t frame_val; + int loops, ret; + + tw32_carefully(MAC_MI_MODE, tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL); + + frame_val = ((PHY_ADDR << MI_COM_PHY_ADDR_SHIFT) & + MI_COM_PHY_ADDR_MASK); + frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) & + MI_COM_REG_ADDR_MASK); + frame_val |= (val & MI_COM_DATA_MASK); + frame_val |= (MI_COM_CMD_WRITE | MI_COM_START); + + tw32_carefully(MAC_MI_COM, frame_val); + + loops = PHY_BUSY_LOOPS; + while (loops-- > 0) { + udelay(10); + frame_val = tr32(MAC_MI_COM); + if ((frame_val & MI_COM_BUSY) == 0) { + udelay(5); + frame_val = tr32(MAC_MI_COM); + break; + } + } + + ret = -EBUSY; + if (loops > 0) + ret = 0; + + tw32_carefully(MAC_MI_MODE, tp->mi_mode); + + return ret; +} + +static int tg3_writedsp(struct tg3 *tp, uint16_t addr, uint16_t val) +{ + int err; + err = tg3_writephy(tp, MII_TG3_DSP_ADDRESS, addr); + err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, val); + return err; +} + + +static void tg3_phy_set_wirespeed(struct tg3 *tp) +{ + uint32_t val; + + if (tp->tg3_flags2 & TG3_FLG2_NO_ETH_WIRE_SPEED) + return; + + tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x7007); + tg3_readphy(tp, MII_TG3_AUX_CTRL, &val); + tg3_writephy(tp, MII_TG3_AUX_CTRL, (val | (1 << 15) | (1 << 4))); +} + +static int tg3_bmcr_reset(struct tg3 *tp) +{ + uint32_t phy_control; + int limit, err; + + /* OK, reset it, and poll the BMCR_RESET bit until it + * clears or we time out. + */ + phy_control = BMCR_RESET; + err = tg3_writephy(tp, MII_BMCR, phy_control); + if (err != 0) + return -EBUSY; + + limit = 5000; + while (limit--) { + err = tg3_readphy(tp, MII_BMCR, &phy_control); + if (err != 0) + return -EBUSY; + + if ((phy_control & BMCR_RESET) == 0) { + udelay(40); + break; + } + udelay(10); + } + if (limit <= 0) + return -EBUSY; + + return 0; +} + +static int tg3_wait_macro_done(struct tg3 *tp) +{ + int limit = 100; + + while (limit--) { + uint32_t tmp32; + + tg3_readphy(tp, 0x16, &tmp32); + if ((tmp32 & 0x1000) == 0) + break; + } + if (limit <= 0) + return -EBUSY; + + return 0; +} + +static int tg3_phy_write_and_check_testpat(struct tg3 *tp, int *resetp) +{ + static const uint32_t test_pat[4][6] = { + { 0x00005555, 0x00000005, 0x00002aaa, 0x0000000a, 0x00003456, 0x00000003 }, + { 0x00002aaa, 0x0000000a, 0x00003333, 0x00000003, 0x0000789a, 0x00000005 }, + { 0x00005a5a, 0x00000005, 0x00002a6a, 0x0000000a, 0x00001bcd, 0x00000003 }, + { 0x00002a5a, 0x0000000a, 0x000033c3, 0x00000003, 0x00002ef1, 0x00000005 } + }; + int chan; + + for (chan = 0; chan < 4; chan++) { + int i; + + tg3_writephy(tp, MII_TG3_DSP_ADDRESS, + (chan * 0x2000) | 0x0200); + tg3_writephy(tp, 0x16, 0x0002); + + for (i = 0; i < 6; i++) + tg3_writephy(tp, MII_TG3_DSP_RW_PORT, + test_pat[chan][i]); + + tg3_writephy(tp, 0x16, 0x0202); + if (tg3_wait_macro_done(tp)) { + *resetp = 1; + return -EBUSY; + } + + tg3_writephy(tp, MII_TG3_DSP_ADDRESS, + (chan * 0x2000) | 0x0200); + tg3_writephy(tp, 0x16, 0x0082); + if (tg3_wait_macro_done(tp)) { + *resetp = 1; + return -EBUSY; + } + + tg3_writephy(tp, 0x16, 0x0802); + if (tg3_wait_macro_done(tp)) { + *resetp = 1; + return -EBUSY; + } + + for (i = 0; i < 6; i += 2) { + uint32_t low, high; + + tg3_readphy(tp, MII_TG3_DSP_RW_PORT, &low); + tg3_readphy(tp, MII_TG3_DSP_RW_PORT, &high); + if (tg3_wait_macro_done(tp)) { + *resetp = 1; + return -EBUSY; + } + low &= 0x7fff; + high &= 0x000f; + if (low != test_pat[chan][i] || + high != test_pat[chan][i+1]) { + tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x000b); + tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x4001); + tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x4005); + + return -EBUSY; + } + } + } + + return 0; +} + +static int tg3_phy_reset_chanpat(struct tg3 *tp) +{ + int chan; + + for (chan = 0; chan < 4; chan++) { + int i; + + tg3_writephy(tp, MII_TG3_DSP_ADDRESS, + (chan * 0x2000) | 0x0200); + tg3_writephy(tp, 0x16, 0x0002); + for (i = 0; i < 6; i++) + tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x000); + tg3_writephy(tp, 0x16, 0x0202); + if (tg3_wait_macro_done(tp)) + return -EBUSY; + } + + return 0; +} + +static int tg3_phy_reset_5703_4_5(struct tg3 *tp) +{ + uint32_t reg32, phy9_orig; + int retries, do_phy_reset, err; + + retries = 10; + do_phy_reset = 1; + do { + if (do_phy_reset) { + err = tg3_bmcr_reset(tp); + if (err) + return err; + do_phy_reset = 0; + } + + /* Disable transmitter and interrupt. */ + tg3_readphy(tp, MII_TG3_EXT_CTRL, ®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 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) { + 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 or 5787, 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=%3x\n", + ofs, enable_bit ); + return -ENODEV; + } + + return 0; +} + +static int tg3_abort_hw(struct tg3 *tp) +{ + int i, err; + uint32_t val; + + tg3_disable_ints(tp); + + tp->rx_mode &= ~RX_MODE_ENABLE; + tw32_carefully(MAC_RX_MODE, tp->rx_mode); + + err = tg3_stop_block(tp, RCVBDI_MODE, RCVBDI_MODE_ENABLE); + err |= tg3_stop_block(tp, RCVLPC_MODE, RCVLPC_MODE_ENABLE); + err |= tg3_stop_block(tp, RCVLSC_MODE, RCVLSC_MODE_ENABLE); + err |= tg3_stop_block(tp, RCVDBDI_MODE, RCVDBDI_MODE_ENABLE); + err |= tg3_stop_block(tp, RCVDCC_MODE, RCVDCC_MODE_ENABLE); + err |= tg3_stop_block(tp, RCVCC_MODE, RCVCC_MODE_ENABLE); + + err |= tg3_stop_block(tp, SNDBDS_MODE, SNDBDS_MODE_ENABLE); + err |= tg3_stop_block(tp, SNDBDI_MODE, SNDBDI_MODE_ENABLE); + err |= tg3_stop_block(tp, SNDDATAI_MODE, SNDDATAI_MODE_ENABLE); + err |= tg3_stop_block(tp, RDMAC_MODE, RDMAC_MODE_ENABLE); + err |= tg3_stop_block(tp, SNDDATAC_MODE, SNDDATAC_MODE_ENABLE); + err |= tg3_stop_block(tp, SNDBDC_MODE, SNDBDC_MODE_ENABLE); + if (err) + goto out; + + tp->mac_mode &= ~MAC_MODE_TDE_ENABLE; + tw32_carefully(MAC_MODE, tp->mac_mode); + + tp->tx_mode &= ~TX_MODE_ENABLE; + tw32_carefully(MAC_TX_MODE, tp->tx_mode); + + for (i = 0; i < MAX_WAIT_CNT; i++) { + udelay(100); + if (!(tr32(MAC_TX_MODE) & TX_MODE_ENABLE)) + break; + } + if (i >= MAX_WAIT_CNT) { + printf("tg3_abort_hw timed out TX_MODE_ENABLE will not clear MAC_TX_MODE=%x\n", + (unsigned int) tr32(MAC_TX_MODE)); + return -ENODEV; + } + + err = tg3_stop_block(tp, HOSTCC_MODE, HOSTCC_MODE_ENABLE); + err |= tg3_stop_block(tp, WDMAC_MODE, WDMAC_MODE_ENABLE); + err |= tg3_stop_block(tp, MBFREE_MODE, MBFREE_MODE_ENABLE); + + val = tr32(FTQ_RESET); + val |= FTQ_RESET_DMA_READ_QUEUE | FTQ_RESET_DMA_HIGH_PRI_READ | + FTQ_RESET_SEND_BD_COMPLETION | FTQ_RESET_DMA_WRITE | + FTQ_RESET_DMA_HIGH_PRI_WRITE | FTQ_RESET_SEND_DATA_COMPLETION | + FTQ_RESET_HOST_COALESCING | FTQ_RESET_MAC_TX | + FTQ_RESET_RX_BD_COMPLETE | FTQ_RESET_RX_LIST_PLCMT | + FTQ_RESET_RX_DATA_COMPLETION; + tw32(FTQ_RESET, val); + + 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 (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) { + if (tr32(0x7e2c) == 0x60) { + tw32(0x7e2c, 0x20); + } + if (tp->pci_chip_rev_id != CHIPREV_ID_5750_A0) { + tw32(GRC_MISC_CFG, (1 << 29)); + val |= (1 << 29); + } + } + + if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) + || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) + || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787)) { + 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) && + !(GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787)) { + 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_5787) { + /* Do nothing. */ + } else if ((GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) && + (tp->pci_chip_rev_id != CHIPREV_ID_5721)) { + 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 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) { + 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 and 5787, no need to explicitly + * disable the others. + */ + if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5787) { + /* 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 and 5787, no need to + * explicitly disable the others. + */ + if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5787) { + 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; + } + + /* Host coalescing bug fix */ + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) + val |= (1 << 29); + + 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 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5787) { + TG3_WRITE_SETTINGS(table_not_5705); + } + } + + tp->tx_mode = TX_MODE_ENABLE; + tw32_carefully(MAC_TX_MODE, tp->tx_mode); + + tp->rx_mode = RX_MODE_ENABLE; + tw32_carefully(MAC_RX_MODE, tp->rx_mode); + + tp->mi_mode = MAC_MI_MODE_BASE; + tw32_carefully(MAC_MI_MODE, tp->mi_mode); + + tw32(MAC_LED_CTRL, 0); + tw32(MAC_MI_STAT, MAC_MI_STAT_LNKSTAT_ATTN_ENAB); + if (tp->phy_id == PHY_ID_SERDES) { + tw32_carefully(MAC_RX_MODE, RX_MODE_RESET); + } + tp->rx_mode |= RX_MODE_KEEP_VLAN_TAG; /* drop tagged vlan packets */ + tw32_carefully(MAC_RX_MODE, tp->rx_mode); + + if (tp->pci_chip_rev_id == CHIPREV_ID_5703_A1) + tw32(MAC_SERDES_CFG, 0x616000); + + /* Prevent chip from dropping frames when flow control + * is enabled. + */ + tw32(MAC_LOW_WMARK_MAX_RX_FRAME, 2); + tr32(MAC_LOW_WMARK_MAX_RX_FRAME); + + err = tg3_setup_phy(tp); + + /* Ignore CRC stats */ + + /* Initialize receive rules. */ + tw32(MAC_RCV_RULE_0, 0xc2000000 & RCV_RULE_DISABLE_MASK); + tw32(MAC_RCV_VALUE_0, 0xffffffff & RCV_RULE_DISABLE_MASK); + tw32(MAC_RCV_RULE_1, 0x86000004 & RCV_RULE_DISABLE_MASK); + tw32(MAC_RCV_VALUE_1, 0xffffffff & RCV_RULE_DISABLE_MASK); + + if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) + || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750)) + limit = 8; + else + limit = 16; + if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) + limit -= 4; + switch (limit) { + case 16: tw32(MAC_RCV_RULE_15, 0); tw32(MAC_RCV_VALUE_15, 0); + case 15: tw32(MAC_RCV_RULE_14, 0); tw32(MAC_RCV_VALUE_14, 0); + case 14: tw32(MAC_RCV_RULE_13, 0); tw32(MAC_RCV_VALUE_13, 0); + case 13: tw32(MAC_RCV_RULE_12, 0); tw32(MAC_RCV_VALUE_12, 0); + case 12: tw32(MAC_RCV_RULE_11, 0); tw32(MAC_RCV_VALUE_11, 0); + case 11: tw32(MAC_RCV_RULE_10, 0); tw32(MAC_RCV_VALUE_10, 0); + case 10: tw32(MAC_RCV_RULE_9, 0); tw32(MAC_RCV_VALUE_9, 0); + case 9: tw32(MAC_RCV_RULE_8, 0); tw32(MAC_RCV_VALUE_8, 0); + case 8: tw32(MAC_RCV_RULE_7, 0); tw32(MAC_RCV_VALUE_7, 0); + case 7: tw32(MAC_RCV_RULE_6, 0); tw32(MAC_RCV_VALUE_6, 0); + case 6: tw32(MAC_RCV_RULE_5, 0); tw32(MAC_RCV_VALUE_5, 0); + case 5: tw32(MAC_RCV_RULE_4, 0); tw32(MAC_RCV_VALUE_4, 0); + case 4: /* tw32(MAC_RCV_RULE_3, 0); tw32(MAC_RCV_VALUE_3, 0); */ + case 3: /* tw32(MAC_RCV_RULE_2, 0); tw32(MAC_RCV_VALUE_2, 0); */ + case 2: + case 1: + default: + break; + }; + + return err; +} + + + +/* Chips other than 5700/5701 use the NVRAM for fetching info. */ +static void tg3_nvram_init(struct tg3 *tp) +{ + tw32(GRC_EEPROM_ADDR, + (EEPROM_ADDR_FSM_RESET | + (EEPROM_DEFAULT_CLOCK_PERIOD << + EEPROM_ADDR_CLKPERD_SHIFT))); + + mdelay(1); + + /* Enable seeprom accesses. */ + tw32_carefully(GRC_LOCAL_CTRL, + tr32(GRC_LOCAL_CTRL) | GRC_LCLCTRL_AUTO_SEEPROM); + + if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701) { + uint32_t nvcfg1 = tr32(NVRAM_CFG1); + + tp->tg3_flags |= TG3_FLAG_NVRAM; + if (nvcfg1 & NVRAM_CFG1_FLASHIF_ENAB) { + if (nvcfg1 & NVRAM_CFG1_BUFFERED_MODE) + tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED; + } else { + nvcfg1 &= ~NVRAM_CFG1_COMPAT_BYPASS; + tw32(NVRAM_CFG1, nvcfg1); + } + + } else { + tp->tg3_flags &= ~(TG3_FLAG_NVRAM | TG3_FLAG_NVRAM_BUFFERED); + } +} + + +static int tg3_nvram_read_using_eeprom( + struct tg3 *tp __unused, uint32_t offset, uint32_t *val) +{ + uint32_t tmp; + int i; + + if (offset > EEPROM_ADDR_ADDR_MASK || + (offset % 4) != 0) { + return -EINVAL; + } + + tmp = tr32(GRC_EEPROM_ADDR) & ~(EEPROM_ADDR_ADDR_MASK | + EEPROM_ADDR_DEVID_MASK | + EEPROM_ADDR_READ); + tw32(GRC_EEPROM_ADDR, + tmp | + (0 << EEPROM_ADDR_DEVID_SHIFT) | + ((offset << EEPROM_ADDR_ADDR_SHIFT) & + EEPROM_ADDR_ADDR_MASK) | + EEPROM_ADDR_READ | EEPROM_ADDR_START); + + for (i = 0; i < 10000; i++) { + tmp = tr32(GRC_EEPROM_ADDR); + + if (tmp & EEPROM_ADDR_COMPLETE) + break; + udelay(100); + } + if (!(tmp & EEPROM_ADDR_COMPLETE)) { + return -EBUSY; + } + + *val = tr32(GRC_EEPROM_DATA); + return 0; +} + +static int tg3_nvram_read(struct tg3 *tp, uint32_t offset, uint32_t *val) +{ + int i, saw_done_clear; + + if (!(tp->tg3_flags & TG3_FLAG_NVRAM)) + return tg3_nvram_read_using_eeprom(tp, offset, val); + + if (tp->tg3_flags & TG3_FLAG_NVRAM_BUFFERED) + offset = ((offset / NVRAM_BUFFERED_PAGE_SIZE) << + NVRAM_BUFFERED_PAGE_POS) + + (offset % NVRAM_BUFFERED_PAGE_SIZE); + + if (offset > NVRAM_ADDR_MSK) + return -EINVAL; + + tw32(NVRAM_SWARB, SWARB_REQ_SET1); + for (i = 0; i < 1000; i++) { + if (tr32(NVRAM_SWARB) & SWARB_GNT1) + break; + udelay(20); + } + + tw32(NVRAM_ADDR, offset); + tw32(NVRAM_CMD, + NVRAM_CMD_RD | NVRAM_CMD_GO | + NVRAM_CMD_FIRST | NVRAM_CMD_LAST | NVRAM_CMD_DONE); + + /* Wait for done bit to clear then set again. */ + saw_done_clear = 0; + for (i = 0; i < 1000; i++) { + udelay(10); + if (!saw_done_clear && + !(tr32(NVRAM_CMD) & NVRAM_CMD_DONE)) + saw_done_clear = 1; + else if (saw_done_clear && + (tr32(NVRAM_CMD) & NVRAM_CMD_DONE)) + break; + } + if (i >= 1000) { + tw32(NVRAM_SWARB, SWARB_REQ_CLR1); + return -EBUSY; + } + + *val = bswap_32(tr32(NVRAM_RDDATA)); + tw32(NVRAM_SWARB, 0x20); + + return 0; +} + +struct subsys_tbl_ent { + uint16_t subsys_vendor, subsys_devid; + uint32_t phy_id; +}; + +static struct subsys_tbl_ent subsys_id_to_phy_id[] = { + /* Broadcom boards. */ + { 0x14e4, 0x1644, PHY_ID_BCM5401 }, /* BCM95700A6 */ + { 0x14e4, 0x0001, PHY_ID_BCM5701 }, /* BCM95701A5 */ + { 0x14e4, 0x0002, PHY_ID_BCM8002 }, /* BCM95700T6 */ + { 0x14e4, 0x0003, PHY_ID_SERDES }, /* BCM95700A9 */ + { 0x14e4, 0x0005, PHY_ID_BCM5701 }, /* BCM95701T1 */ + { 0x14e4, 0x0006, PHY_ID_BCM5701 }, /* BCM95701T8 */ + { 0x14e4, 0x0007, PHY_ID_SERDES }, /* BCM95701A7 */ + { 0x14e4, 0x0008, PHY_ID_BCM5701 }, /* BCM95701A10 */ + { 0x14e4, 0x8008, PHY_ID_BCM5701 }, /* BCM95701A12 */ + { 0x14e4, 0x0009, PHY_ID_BCM5701 }, /* BCM95703Ax1 */ + { 0x14e4, 0x8009, PHY_ID_BCM5701 }, /* BCM95703Ax2 */ + + /* 3com boards. */ + { PCI_VENDOR_ID_3COM, 0x1000, PHY_ID_BCM5401 }, /* 3C996T */ + { PCI_VENDOR_ID_3COM, 0x1006, PHY_ID_BCM5701 }, /* 3C996BT */ + /* { PCI_VENDOR_ID_3COM, 0x1002, PHY_ID_XXX }, 3C996CT */ + /* { PCI_VENDOR_ID_3COM, 0x1003, PHY_ID_XXX }, 3C997T */ + { PCI_VENDOR_ID_3COM, 0x1004, PHY_ID_SERDES }, /* 3C996SX */ + /* { PCI_VENDOR_ID_3COM, 0x1005, PHY_ID_XXX }, 3C997SZ */ + { PCI_VENDOR_ID_3COM, 0x1007, PHY_ID_BCM5701 }, /* 3C1000T */ + { PCI_VENDOR_ID_3COM, 0x1008, PHY_ID_BCM5701 }, /* 3C940BR01 */ + + /* DELL boards. */ + { PCI_VENDOR_ID_DELL, 0x00d1, PHY_ID_BCM5401 }, /* VIPER */ + { PCI_VENDOR_ID_DELL, 0x0106, PHY_ID_BCM5401 }, /* JAGUAR */ + { PCI_VENDOR_ID_DELL, 0x0109, PHY_ID_BCM5411 }, /* MERLOT */ + { PCI_VENDOR_ID_DELL, 0x010a, PHY_ID_BCM5411 }, /* SLIM_MERLOT */ + { PCI_VENDOR_ID_DELL, 0x0179, PHY_ID_BCM5751 }, /* EtherXpress */ + + /* Fujitsu Siemens Computer */ + { PCI_VENDOR_ID_FSC, 0x105d, PHY_ID_BCM5751 }, /* Futro C200 */ + + /* Compaq boards. */ + { PCI_VENDOR_ID_COMPAQ, 0x007c, PHY_ID_BCM5701 }, /* BANSHEE */ + { PCI_VENDOR_ID_COMPAQ, 0x009a, PHY_ID_BCM5701 }, /* BANSHEE_2 */ + { PCI_VENDOR_ID_COMPAQ, 0x007d, PHY_ID_SERDES }, /* CHANGELING */ + { PCI_VENDOR_ID_COMPAQ, 0x0085, PHY_ID_BCM5701 }, /* NC7780 */ + { PCI_VENDOR_ID_COMPAQ, 0x0099, PHY_ID_BCM5701 } /* NC7780_2 */ +}; + +static int tg3_phy_probe(struct tg3 *tp) +{ + uint32_t eeprom_phy_id, hw_phy_id_1, hw_phy_id_2; + uint32_t hw_phy_id, hw_phy_id_masked; + enum phy_led_mode eeprom_led_mode; + uint32_t val; + unsigned i; + int eeprom_signature_found, err; + + tp->phy_id = PHY_ID_INVALID; + + for (i = 0; i < sizeof(subsys_id_to_phy_id)/sizeof(subsys_id_to_phy_id[0]); i++) { + if ((subsys_id_to_phy_id[i].subsys_vendor == tp->subsystem_vendor) && + (subsys_id_to_phy_id[i].subsys_devid == tp->subsystem_device)) { + tp->phy_id = subsys_id_to_phy_id[i].phy_id; + break; + } + } + + eeprom_phy_id = PHY_ID_INVALID; + eeprom_led_mode = led_mode_auto; + eeprom_signature_found = 0; + tg3_read_mem(NIC_SRAM_DATA_SIG, &val); + if (val == NIC_SRAM_DATA_SIG_MAGIC) { + uint32_t nic_cfg; + + tg3_read_mem(NIC_SRAM_DATA_CFG, &nic_cfg); + tp->nic_sram_data_cfg = nic_cfg; + + eeprom_signature_found = 1; + + if ((nic_cfg & NIC_SRAM_DATA_CFG_PHY_TYPE_MASK) == + NIC_SRAM_DATA_CFG_PHY_TYPE_FIBER) { + eeprom_phy_id = PHY_ID_SERDES; + } else { + uint32_t nic_phy_id; + + tg3_read_mem(NIC_SRAM_DATA_PHY_ID, &nic_phy_id); + if (nic_phy_id != 0) { + uint32_t id1 = nic_phy_id & NIC_SRAM_DATA_PHY_ID1_MASK; + uint32_t id2 = nic_phy_id & NIC_SRAM_DATA_PHY_ID2_MASK; + + eeprom_phy_id = (id1 >> 16) << 10; + eeprom_phy_id |= (id2 & 0xfc00) << 16; + eeprom_phy_id |= (id2 & 0x03ff) << 0; + } + } + + switch (nic_cfg & NIC_SRAM_DATA_CFG_LED_MODE_MASK) { + case NIC_SRAM_DATA_CFG_LED_TRIPLE_SPD: + eeprom_led_mode = led_mode_three_link; + break; + + case NIC_SRAM_DATA_CFG_LED_LINK_SPD: + eeprom_led_mode = led_mode_link10; + break; + + default: + eeprom_led_mode = led_mode_auto; + break; + }; + if (((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) || + (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) || + (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705)) && + (nic_cfg & NIC_SRAM_DATA_CFG_EEPROM_WP)) { + tp->tg3_flags |= TG3_FLAG_EEPROM_WRITE_PROT; + } + + if (nic_cfg & NIC_SRAM_DATA_CFG_ASF_ENABLE) + tp->tg3_flags |= TG3_FLAG_ENABLE_ASF; + if (nic_cfg & NIC_SRAM_DATA_CFG_FIBER_WOL) + tp->tg3_flags |= TG3_FLAG_SERDES_WOL_CAP; + } + + /* Now read the physical PHY_ID from the chip and verify + * that it is sane. If it doesn't look good, we fall back + * to either the hard-coded table based PHY_ID and failing + * that the value found in the eeprom area. + */ + err = tg3_readphy(tp, MII_PHYSID1, &hw_phy_id_1); + err |= tg3_readphy(tp, MII_PHYSID2, &hw_phy_id_2); + + hw_phy_id = (hw_phy_id_1 & 0xffff) << 10; + hw_phy_id |= (hw_phy_id_2 & 0xfc00) << 16; + hw_phy_id |= (hw_phy_id_2 & 0x03ff) << 0; + + hw_phy_id_masked = hw_phy_id & PHY_ID_MASK; + + if (!err && KNOWN_PHY_ID(hw_phy_id_masked)) { + tp->phy_id = hw_phy_id; + } else { + /* phy_id currently holds the value found in the + * subsys_id_to_phy_id[] table or PHY_ID_INVALID + * if a match was not found there. + */ + if (tp->phy_id == PHY_ID_INVALID) { + if (!eeprom_signature_found || + !KNOWN_PHY_ID(eeprom_phy_id & PHY_ID_MASK)) + return -ENODEV; + tp->phy_id = eeprom_phy_id; + } + } + + err = tg3_phy_reset(tp); + if (err) + return err; + + if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || + tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) { + uint32_t mii_tg3_ctrl; + + /* These chips, when reset, only advertise 10Mb + * capabilities. Fix that. + */ + err = tg3_writephy(tp, MII_ADVERTISE, + (ADVERTISE_CSMA | + ADVERTISE_PAUSE_CAP | + ADVERTISE_10HALF | + ADVERTISE_10FULL | + ADVERTISE_100HALF | + ADVERTISE_100FULL)); + mii_tg3_ctrl = (MII_TG3_CTRL_ADV_1000_HALF | + MII_TG3_CTRL_ADV_1000_FULL | + MII_TG3_CTRL_AS_MASTER | + MII_TG3_CTRL_ENABLE_AS_MASTER); + if (tp->tg3_flags & TG3_FLAG_10_100_ONLY) + mii_tg3_ctrl = 0; + + err |= tg3_writephy(tp, MII_TG3_CTRL, mii_tg3_ctrl); + err |= tg3_writephy(tp, MII_BMCR, + (BMCR_ANRESTART | BMCR_ANENABLE)); + } + + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) { + tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00); + tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x201f); + tg3_writedsp(tp, MII_TG3_DSP_RW_PORT, 0x2aaa); + } + + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) { + tg3_writephy(tp, 0x1c, 0x8d68); + tg3_writephy(tp, 0x1c, 0x8d68); + } + + /* Enable Ethernet@WireSpeed */ + tg3_phy_set_wirespeed(tp); + + if (!err && ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401)) { + err = tg3_init_5401phy_dsp(tp); + } + + /* Determine the PHY led mode. + * Be careful if this gets set wrong it can result in an inability to + * establish a link. + */ + if (tp->phy_id == PHY_ID_SERDES) { + tp->led_mode = led_mode_three_link; + } + else if (tp->subsystem_vendor == PCI_VENDOR_ID_DELL) { + tp->led_mode = led_mode_link10; + } else { + tp->led_mode = led_mode_three_link; + if (eeprom_signature_found && + eeprom_led_mode != led_mode_auto) + tp->led_mode = eeprom_led_mode; + } + + if (tp->phy_id == PHY_ID_SERDES) + tp->link_config.advertising = + (ADVERTISED_1000baseT_Half | + ADVERTISED_1000baseT_Full | + ADVERTISED_Autoneg | + ADVERTISED_FIBRE); + if (tp->tg3_flags & TG3_FLAG_10_100_ONLY) + tp->link_config.advertising &= + ~(ADVERTISED_1000baseT_Half | + ADVERTISED_1000baseT_Full); + + return err; +} + +#if SUPPORT_PARTNO_STR +static void tg3_read_partno(struct tg3 *tp) +{ + unsigned char vpd_data[256]; + int i; + + for (i = 0; i < 256; i += 4) { + uint32_t tmp; + + if (tg3_nvram_read(tp, 0x100 + i, &tmp)) + goto out_not_found; + + vpd_data[i + 0] = ((tmp >> 0) & 0xff); + vpd_data[i + 1] = ((tmp >> 8) & 0xff); + vpd_data[i + 2] = ((tmp >> 16) & 0xff); + vpd_data[i + 3] = ((tmp >> 24) & 0xff); + } + + /* Now parse and find the part number. */ + for (i = 0; i < 256; ) { + unsigned char val = vpd_data[i]; + int block_end; + + if (val == 0x82 || val == 0x91) { + i = (i + 3 + + (vpd_data[i + 1] + + (vpd_data[i + 2] << 8))); + continue; + } + + if (val != 0x90) + goto out_not_found; + + block_end = (i + 3 + + (vpd_data[i + 1] + + (vpd_data[i + 2] << 8))); + i += 3; + while (i < block_end) { + if (vpd_data[i + 0] == 'P' && + vpd_data[i + 1] == 'N') { + int partno_len = vpd_data[i + 2]; + + if (partno_len > 24) + goto out_not_found; + + memcpy(tp->board_part_number, + &vpd_data[i + 3], + partno_len); + + /* Success. */ + return; + } + } + + /* Part number not found. */ + goto out_not_found; + } + +out_not_found: + memcpy(tp->board_part_number, "none", sizeof("none")); +} +#else +#define tg3_read_partno(TP) ((TP)->board_part_number[0] = '\0') +#endif + +static int tg3_get_invariants(struct tg3 *tp) +{ + uint32_t misc_ctrl_reg; + uint32_t pci_state_reg, grc_misc_cfg; + uint16_t pci_cmd; + uint8_t pci_latency; + uint32_t val ; + int err; + + /* Read the subsystem vendor and device ids */ + pci_read_config_word(tp->pdev, PCI_SUBSYSTEM_VENDOR_ID, &tp->subsystem_vendor); + pci_read_config_word(tp->pdev, PCI_SUBSYSTEM_ID, &tp->subsystem_device); + + /* The sun_5704 code needs infrastructure etherboot does have + * ignore it for now. + */ + + /* If we have an AMD 762 or Intel ICH/ICH0 chipset, write + * reordering to the mailbox registers done by the host + * controller can cause major troubles. We read back from + * every mailbox register write to force the writes to be + * posted to the chip in order. + * + * TG3_FLAG_MBOX_WRITE_REORDER has been forced on. + */ + + /* Force memory write invalidate off. If we leave it on, + * then on 5700_BX chips we have to enable a workaround. + * The workaround is to set the TG3PCI_DMA_RW_CTRL boundry + * to match the cacheline size. The Broadcom driver have this + * workaround but turns MWI off all the times so never uses + * it. This seems to suggest that the workaround is insufficient. + */ + pci_read_config_word(tp->pdev, PCI_COMMAND, &pci_cmd); + pci_cmd &= ~PCI_COMMAND_INVALIDATE; + /* Also, force SERR#/PERR# in PCI command. */ + pci_cmd |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR; + pci_write_config_word(tp->pdev, PCI_COMMAND, pci_cmd); + + /* It is absolutely critical that TG3PCI_MISC_HOST_CTRL + * has the register indirect write enable bit set before + * we try to access any of the MMIO registers. It is also + * critical that the PCI-X hw workaround situation is decided + * before that as well. + */ + pci_read_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL, &misc_ctrl_reg); + + tp->pci_chip_rev_id = (misc_ctrl_reg >> MISC_HOST_CTRL_CHIPREV_SHIFT); + + /* Initialize misc host control in PCI block. */ + tp->misc_host_ctrl |= (misc_ctrl_reg & + MISC_HOST_CTRL_CHIPREV); + pci_write_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL, + tp->misc_host_ctrl); + + pci_read_config_byte(tp->pdev, PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency < 64) { + pci_write_config_byte(tp->pdev, PCI_LATENCY_TIMER, 64); + } + + pci_read_config_dword(tp->pdev, TG3PCI_PCISTATE, &pci_state_reg); + + /* If this is a 5700 BX chipset, and we are in PCI-X + * mode, enable register write workaround. + * + * The workaround is to use indirect register accesses + * for all chip writes not to mailbox registers. + * + * In etherboot to simplify things we just always use this work around. + */ + if ((pci_state_reg & PCISTATE_CONV_PCI_MODE) == 0) { + tp->tg3_flags |= TG3_FLAG_PCIX_MODE; + } + /* Back to back register writes can cause problems on the 5701, + * the workaround is to read back all reg writes except those to + * mailbox regs. + * In etherboot we always use indirect register accesses so + * we don't see this. + */ + + if ((pci_state_reg & PCISTATE_BUS_SPEED_HIGH) != 0) + tp->tg3_flags |= TG3_FLAG_PCI_HIGH_SPEED; + if ((pci_state_reg & PCISTATE_BUS_32BIT) != 0) + tp->tg3_flags |= TG3_FLAG_PCI_32BIT; + + /* Chip-specific fixup from Broadcom driver */ + if ((tp->pci_chip_rev_id == CHIPREV_ID_5704_A0) && + (!(pci_state_reg & PCISTATE_RETRY_SAME_DMA))) { + pci_state_reg |= PCISTATE_RETRY_SAME_DMA; + pci_write_config_dword(tp->pdev, TG3PCI_PCISTATE, pci_state_reg); + } + + /* determine if it is PCIE system */ + // Alf : I have no idea what this is about... + // But it's definitely usefull + val = pci_find_capability(tp->pdev, PCI_CAP_ID_EXP); + if (val) + tp->tg3_flags2 |= TG3_FLG2_PCI_EXPRESS; + + /* Force the chip into D0. */ + tg3_set_power_state_0(tp); + + /* Etherboot does not ask the tg3 to do checksums */ + /* Etherboot does not ask the tg3 to do jumbo frames */ + /* Ehterboot does not ask the tg3 to use WakeOnLan. */ + + /* A few boards don't want Ethernet@WireSpeed phy feature */ + if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) || + (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) || + ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) && + (tp->pci_chip_rev_id != CHIPREV_ID_5705_A0) && + (tp->pci_chip_rev_id != CHIPREV_ID_5705_A1))) { + tp->tg3_flags2 |= TG3_FLG2_NO_ETH_WIRE_SPEED; + } + + /* Avoid tagged irq status etherboot does not use irqs */ + + /* Only 5701 and later support tagged irq status mode. + * Also, 5788 chips cannot use tagged irq status. + * + * However, since etherboot does not use irqs avoid tagged irqs + * status because the interrupt condition is more difficult to + * fully clear in that mode. + */ + + /* Since some 5700_AX && 5700_BX have problems with 32BYTE + * coalesce_mode, and the rest work fine anything set. + * Don't enable HOST_CC_MODE_32BYTE in etherboot. + */ + + /* Initialize MAC MI mode, polling disabled. */ + tw32_carefully(MAC_MI_MODE, tp->mi_mode); + + /* Initialize data/descriptor byte/word swapping. */ + tw32(GRC_MODE, tp->grc_mode); + + tg3_switch_clocks(tp); + + /* Clear this out for sanity. */ + tw32(TG3PCI_MEM_WIN_BASE_ADDR, 0); + + /* Etherboot does not need to check if the PCIX_TARGET_HWBUG + * is needed. It always uses it. + */ + + udelay(50); + tg3_nvram_init(tp); + + /* The TX descriptors will reside in main memory. + */ + + /* See which board we are using. + */ + grc_misc_cfg = tr32(GRC_MISC_CFG); + grc_misc_cfg &= GRC_MISC_CFG_BOARD_ID_MASK; + + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 && + grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5704CIOBE) { + tp->tg3_flags |= TG3_FLAG_SPLIT_MODE; + tp->split_mode_max_reqs = SPLIT_MODE_5704_MAX_REQ; + } + + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 && + (grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5788 || + grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5788M)) + tp->tg3_flags2 |= TG3_FLG2_IS_5788; + +#define PCI_DEVICE_ID_TIGON3_5901 0x170d +#define PCI_DEVICE_ID_TIGON3_5901_2 0x170e + + /* these are limited to 10/100 only */ + if (((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) && + ((grc_misc_cfg == 0x8000) || (grc_misc_cfg == 0x4000))) || + ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) && + (tp->pdev->vendor == PCI_VENDOR_ID_BROADCOM) && + ((tp->pdev->device == PCI_DEVICE_ID_TIGON3_5901) || + (tp->pdev->device == PCI_DEVICE_ID_TIGON3_5901_2)))) { + tp->tg3_flags |= TG3_FLAG_10_100_ONLY; + } + + err = tg3_phy_probe(tp); + if (err) { + printf("phy probe failed, err %d\n", err); + } + + tg3_read_partno(tp); + + + /* 5700 BX chips need to have their TX producer index mailboxes + * written twice to workaround a bug. + * In etherboot we do this unconditionally to simplify things. + */ + + /* 5700 chips can get confused if TX buffers straddle the + * 4GB address boundary in some cases. + * + * In etherboot we can ignore the problem as etherboot lives below 4GB. + */ + + /* In etherboot wake-on-lan is unconditionally disabled */ + return err; +} + +static int tg3_get_device_address(struct tg3 *tp) +{ + struct nic *nic = tp->nic; + uint32_t hi, lo, mac_offset; + + if (PCI_FUNC(tp->pdev->devfn) == 0) + mac_offset = 0x7c; + else + mac_offset = 0xcc; + + /* First try to get it from MAC address mailbox. */ + tg3_read_mem(NIC_SRAM_MAC_ADDR_HIGH_MBOX, &hi); + if ((hi >> 16) == 0x484b) { + nic->node_addr[0] = (hi >> 8) & 0xff; + nic->node_addr[1] = (hi >> 0) & 0xff; + + tg3_read_mem(NIC_SRAM_MAC_ADDR_LOW_MBOX, &lo); + nic->node_addr[2] = (lo >> 24) & 0xff; + nic->node_addr[3] = (lo >> 16) & 0xff; + nic->node_addr[4] = (lo >> 8) & 0xff; + nic->node_addr[5] = (lo >> 0) & 0xff; + } + /* Next, try NVRAM. */ + else if (!tg3_nvram_read(tp, mac_offset + 0, &hi) && + !tg3_nvram_read(tp, mac_offset + 4, &lo)) { + nic->node_addr[0] = ((hi >> 16) & 0xff); + nic->node_addr[1] = ((hi >> 24) & 0xff); + nic->node_addr[2] = ((lo >> 0) & 0xff); + nic->node_addr[3] = ((lo >> 8) & 0xff); + nic->node_addr[4] = ((lo >> 16) & 0xff); + nic->node_addr[5] = ((lo >> 24) & 0xff); + } + /* Finally just fetch it out of the MAC control regs. */ + else { + hi = tr32(MAC_ADDR_0_HIGH); + lo = tr32(MAC_ADDR_0_LOW); + + nic->node_addr[5] = lo & 0xff; + nic->node_addr[4] = (lo >> 8) & 0xff; + nic->node_addr[3] = (lo >> 16) & 0xff; + nic->node_addr[2] = (lo >> 24) & 0xff; + nic->node_addr[1] = hi & 0xff; + nic->node_addr[0] = (hi >> 8) & 0xff; + } + + return 0; +} + + +static int tg3_setup_dma(struct tg3 *tp) +{ + tw32(TG3PCI_CLOCK_CTRL, 0); + + if ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) == 0) { + tp->dma_rwctrl = + (0x7 << DMA_RWCTRL_PCI_WRITE_CMD_SHIFT) | + (0x6 << DMA_RWCTRL_PCI_READ_CMD_SHIFT) | + (0x7 << DMA_RWCTRL_WRITE_WATER_SHIFT) | + (0x7 << DMA_RWCTRL_READ_WATER_SHIFT) | + (0x0f << DMA_RWCTRL_MIN_DMA_SHIFT); + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) { + tp->dma_rwctrl &= ~(DMA_RWCTRL_MIN_DMA << DMA_RWCTRL_MIN_DMA_SHIFT); + } + } else { + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) + tp->dma_rwctrl = + (0x7 << DMA_RWCTRL_PCI_WRITE_CMD_SHIFT) | + (0x6 << DMA_RWCTRL_PCI_READ_CMD_SHIFT) | + (0x3 << DMA_RWCTRL_WRITE_WATER_SHIFT) | + (0x7 << DMA_RWCTRL_READ_WATER_SHIFT) | + (0x00 << DMA_RWCTRL_MIN_DMA_SHIFT); + else + tp->dma_rwctrl = + (0x7 << DMA_RWCTRL_PCI_WRITE_CMD_SHIFT) | + (0x6 << DMA_RWCTRL_PCI_READ_CMD_SHIFT) | + (0x3 << DMA_RWCTRL_WRITE_WATER_SHIFT) | + (0x3 << DMA_RWCTRL_READ_WATER_SHIFT) | + (0x0f << DMA_RWCTRL_MIN_DMA_SHIFT); + + /* Wheee, some more chip bugs... */ + if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) || + (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704)) { + uint32_t ccval = tr32(TG3PCI_CLOCK_CTRL) & 0x1f; + + if ((ccval == 0x6) || (ccval == 0x7)) { + tp->dma_rwctrl |= DMA_RWCTRL_ONE_DMA; + } + } + } + + if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) || + (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704)) { + tp->dma_rwctrl &= ~(DMA_RWCTRL_MIN_DMA << DMA_RWCTRL_MIN_DMA_SHIFT); + } + + /* + Alf : Tried that, but it does not work. Should be this way though :-( + if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) { + tp->dma_rwctrl |= 0x001f0000; + } + */ + tp->dma_rwctrl |= DMA_RWCTRL_ASSERT_ALL_BE; + + tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl); + + return 0; +} + +static void tg3_init_link_config(struct tg3 *tp) +{ + tp->link_config.advertising = + (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full | + ADVERTISED_Autoneg | ADVERTISED_MII); + tp->carrier_ok = 0; + tp->link_config.active_speed = SPEED_INVALID; + tp->link_config.active_duplex = DUPLEX_INVALID; +} + + +#if SUPPORT_PHY_STR +static const char * tg3_phy_string(struct tg3 *tp) +{ + switch (tp->phy_id & PHY_ID_MASK) { + case PHY_ID_BCM5400: return "5400"; + case PHY_ID_BCM5401: return "5401"; + case PHY_ID_BCM5411: return "5411"; + case PHY_ID_BCM5701: return "5701"; + case PHY_ID_BCM5703: return "5703"; + case PHY_ID_BCM5704: return "5704"; + case PHY_ID_BCM5705: return "5705"; + case PHY_ID_BCM5750: return "5750"; + case PHY_ID_BCM5751: return "5751"; + case PHY_ID_BCM5787: return "5787"; + case PHY_ID_BCM8002: return "8002/serdes"; + case PHY_ID_SERDES: return "serdes"; + default: return "unknown"; + }; +} +#else +#define tg3_phy_string(TP) "?" +#endif + + +static void tg3_poll_link(struct tg3 *tp) +{ + uint32_t mac_stat; + + mac_stat = tr32(MAC_STATUS); + if (tp->phy_id == PHY_ID_SERDES) { + if (tp->carrier_ok? + (mac_stat & MAC_STATUS_LNKSTATE_CHANGED): + (mac_stat & MAC_STATUS_PCS_SYNCED)) { + tw32_carefully(MAC_MODE, tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK); + tw32_carefully(MAC_MODE, tp->mac_mode); + + tg3_setup_phy(tp); + } + } + else { + if (mac_stat & MAC_STATUS_LNKSTATE_CHANGED) { + tg3_setup_phy(tp); + } + } +} + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static void tg3_ack_irqs(struct tg3 *tp) +{ + if (tp->hw_status->status & SD_STATUS_UPDATED) { + /* + * writing any value to intr-mbox-0 clears PCI INTA# and + * chip-internal interrupt pending events. + * writing non-zero to intr-mbox-0 additional tells the + * NIC to stop sending us irqs, engaging "in-intr-handler" + * event coalescing. + */ + tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, + 0x00000001); + /* + * Flush PCI write. This also guarantees that our + * status block has been flushed to host memory. + */ + tr32(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW); + tp->hw_status->status &= ~SD_STATUS_UPDATED; + } +} + +static int tg3_poll(struct nic *nic, int retrieve) +{ + /* return true if there's an ethernet packet ready to read */ + /* nic->packet should contain data on return */ + /* nic->packetlen should contain length of data */ + + struct tg3 *tp = &tg3; + int result; + + result = 0; + + if ( (tp->hw_status->idx[0].rx_producer != tp->rx_rcb_ptr) && !retrieve ) + return 1; + + tg3_ack_irqs(tp); + + if (tp->hw_status->idx[0].rx_producer != tp->rx_rcb_ptr) { + struct tg3_rx_buffer_desc *desc; + unsigned int len; + desc = &tp->rx_rcb[tp->rx_rcb_ptr]; + if ((desc->opaque & RXD_OPAQUE_RING_MASK) == RXD_OPAQUE_RING_STD) { + len = ((desc->idx_len & RXD_LEN_MASK) >> RXD_LEN_SHIFT) - 4; /* omit crc */ + + nic->packetlen = len; + memcpy(nic->packet, bus_to_virt(desc->addr_lo), len); + result = 1; + } + tp->rx_rcb_ptr = (tp->rx_rcb_ptr + 1) % TG3_RX_RCB_RING_SIZE; + + /* ACK the status ring */ + tw32_mailbox2(MAILBOX_RCVRET_CON_IDX_0 + TG3_64BIT_REG_LOW, tp->rx_rcb_ptr); + + /* Refill RX ring. */ + if (result) { + tp->rx_std_ptr = (tp->rx_std_ptr + 1) % TG3_RX_RING_SIZE; + tw32_mailbox2(MAILBOX_RCV_STD_PROD_IDX + TG3_64BIT_REG_LOW, tp->rx_std_ptr); + } + } + tg3_poll_link(tp); + return result; +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +#if 0 +static void tg3_set_txd(struct tg3 *tp, int entry, + dma_addr_t mapping, int len, uint32_t flags, + uint32_t mss_and_is_end) +{ + struct tg3_tx_buffer_desc *txd = &tp->tx_ring[entry]; + int is_end = (mss_and_is_end & 0x1); + if (is_end) { + flags |= TXD_FLAG_END; + } + + txd->addr_hi = 0; + txd->addr_lo = mapping & 0xffffffff; + txd->len_flags = (len << TXD_LEN_SHIFT) | flags; + txd->vlan_tag = 0 << TXD_VLAN_TAG_SHIFT; +} +#endif + +static void tg3_transmit(struct nic *nic, const char *dst_addr, + unsigned int type, unsigned int size, const char *packet) +{ + static int frame_idx; + struct eth_frame *frame; + + /* send the packet to destination */ + struct tg3_tx_buffer_desc *txd; + struct tg3 *tp; + uint32_t entry; + int i; + + /* Wait until there is a free packet frame */ + tp = &tg3; + i = 0; + entry = tp->tx_prod; + while((tp->hw_status->idx[0].tx_consumer != entry) && + (tp->hw_status->idx[0].tx_consumer != PREV_TX(entry))) { + mdelay(10); /* give the nick a chance */ + if (++i > 500) { /* timeout 5s for transmit */ + printf("transmit timed out\n"); + tg3_halt(tp); + tg3_setup_hw(tp); + return; + } + } + if (i != 0) { + printf("#"); + } + + /* Copy the packet to the our local buffer */ + frame = &tg3_bss.tx_frame[frame_idx]; + memcpy(frame->dst_addr, dst_addr, ETH_ALEN); + memcpy(frame->src_addr, nic->node_addr, ETH_ALEN); + frame->type = htons(type); + memset(frame->data, 0, sizeof(frame->data)); + memcpy(frame->data, packet, size); + + /* Setup the ring buffer entry to transmit */ + txd = &tp->tx_ring[entry]; + txd->addr_hi = 0; /* Etherboot runs under 4GB */ + txd->addr_lo = virt_to_bus(frame); + txd->len_flags = ((size + ETH_HLEN) << TXD_LEN_SHIFT) | TXD_FLAG_END; + txd->vlan_tag = 0 << TXD_VLAN_TAG_SHIFT; + + /* Advance to the next entry */ + entry = NEXT_TX(entry); + frame_idx ^= 1; + + /* Packets are ready, update Tx producer idx local and on card */ + tw32_mailbox((MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW), entry); + tw32_mailbox2((MAILBOX_SNDHOST_PROD_IDX_0 + TG3_64BIT_REG_LOW), entry); + tp->tx_prod = entry; +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void tg3_disable ( struct nic *nic __unused ) { + struct tg3 *tp = &tg3; + /* put the card in its initial state */ + /* This function serves 3 purposes. + * This disables DMA and interrupts so we don't receive + * unexpected packets or interrupts from the card after + * etherboot has finished. + * This frees resources so etherboot may use + * this driver on another interface + * This allows etherboot to reinitialize the interface + * if something is something goes wrong. + */ + tg3_halt(tp); + tp->tg3_flags &= ~(TG3_FLAG_INIT_COMPLETE|TG3_FLAG_GOT_SERDES_FLOWCTL); + tp->carrier_ok = 0; + iounmap((void *)tp->regs); +} + +/************************************************************************** +IRQ - Enable, Disable, or Force interrupts +***************************************************************************/ +static void tg3_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + +static struct nic_operations tg3_operations = { + .connect = dummy_connect, + .poll = tg3_poll, + .transmit = tg3_transmit, + .irq = tg3_irq, + +}; + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +You should omit the last argument struct pci_device * for a non-PCI NIC +***************************************************************************/ +static int tg3_probe ( struct nic *nic, struct pci_device *pdev ) { + + struct tg3 *tp = &tg3; + unsigned long tg3reg_base, tg3reg_len; + int i, err, pm_cap; + + memset(tp, 0, sizeof(*tp)); + + adjust_pci_device(pdev); + + nic->irqno = 0; + nic->ioaddr = pdev->ioaddr; + + /* Find power-management capability. */ + pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); + if (pm_cap == 0) { + printf("Cannot find PowerManagement capability, aborting.\n"); + return 0; + } + tg3reg_base = pci_bar_start(pdev, PCI_BASE_ADDRESS_0); + if (tg3reg_base == -1UL) { + printf("Unuseable bar\n"); + return 0; + } + tg3reg_len = pci_bar_size(pdev, PCI_BASE_ADDRESS_0); + + tp->pdev = pdev; + tp->nic = nic; + tp->pm_cap = pm_cap; + tp->rx_mode = 0; + tp->tx_mode = 0; + tp->mi_mode = MAC_MI_MODE_BASE; + tp->tg3_flags = 0 & ~TG3_FLAG_INIT_COMPLETE; + + /* The word/byte swap controls here control register access byte + * swapping. DMA data byte swapping is controlled in the GRC_MODE + * setting below. + */ + tp->misc_host_ctrl = + MISC_HOST_CTRL_MASK_PCI_INT | + MISC_HOST_CTRL_WORD_SWAP | + MISC_HOST_CTRL_INDIR_ACCESS | + MISC_HOST_CTRL_PCISTATE_RW; + + /* The NONFRM (non-frame) byte/word swap controls take effect + * on descriptor entries, anything which isn't packet data. + * + * The StrongARM chips on the board (one for tx, one for rx) + * are running in big-endian mode. + */ + tp->grc_mode = (GRC_MODE_WSWAP_DATA | GRC_MODE_BSWAP_DATA | + GRC_MODE_WSWAP_NONFRM_DATA); +#if __BYTE_ORDER == __BIG_ENDIAN + tp->grc_mode |= GRC_MODE_BSWAP_NONFRM_DATA; +#endif + tp->regs = (unsigned long) ioremap(tg3reg_base, tg3reg_len); + if (tp->regs == 0UL) { + printf("Cannot map device registers, aborting\n"); + return 0; + } + + tg3_init_link_config(tp); + + err = tg3_get_invariants(tp); + if (err) { + printf("Problem fetching invariants of chip, aborting.\n"); + goto err_out_iounmap; + } + + err = tg3_get_device_address(tp); + if (err) { + printf("Could not obtain valid ethernet address, aborting.\n"); + goto err_out_iounmap; + } + + DBG ( "Ethernet addr: %s\n", eth_ntoa ( nic->node_addr ) ); + + tg3_setup_dma(tp); + + /* Now that we have fully setup the chip, save away a snapshot + * of the PCI config space. We need to restore this after + * GRC_MISC_CFG core clock resets and some resume events. + */ + pci_save_state(tp->pdev, tp->pci_cfg_state); + + printf("Tigon3 [partno(%s) rev %hx PHY(%s)] (PCI%s:%s:%s)\n", + tp->board_part_number, + tp->pci_chip_rev_id, + tg3_phy_string(tp), + ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) ? "X" : ""), + ((tp->tg3_flags & TG3_FLAG_PCI_HIGH_SPEED) ? + ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) ? "133MHz" : "66MHz") : + ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) ? "100MHz" : "33MHz")), + ((tp->tg3_flags & TG3_FLAG_PCI_32BIT) ? "32-bit" : "64-bit")); + + + err = tg3_setup_hw(tp); + if (err) { + goto err_out_disable; + } + tp->tg3_flags |= TG3_FLAG_INIT_COMPLETE; + + /* Wait for a reasonable time for the link to come up */ + tg3_poll_link(tp); + for(i = 0; !tp->carrier_ok && (i < VALID_LINK_TIMEOUT*100); i++) { + mdelay(1); + tg3_poll_link(tp); + } + if (!tp->carrier_ok){ + printf("Valid link not established\n"); + goto err_out_disable; + } + + nic->nic_op = &tg3_operations; + return 1; + + err_out_iounmap: + iounmap((void *)tp->regs); + return 0; + err_out_disable: + tg3_disable(nic); + return 0; +} + + +static struct pci_device_id tg3_nics[] = { +PCI_ROM(0x14e4, 0x1644, "tg3-5700", "Broadcom Tigon 3 5700"), +PCI_ROM(0x14e4, 0x1645, "tg3-5701", "Broadcom Tigon 3 5701"), +PCI_ROM(0x14e4, 0x1646, "tg3-5702", "Broadcom Tigon 3 5702"), +PCI_ROM(0x14e4, 0x1647, "tg3-5703", "Broadcom Tigon 3 5703"), +PCI_ROM(0x14e4, 0x1648, "tg3-5704", "Broadcom Tigon 3 5704"), +PCI_ROM(0x14e4, 0x164d, "tg3-5702FE", "Broadcom Tigon 3 5702FE"), +PCI_ROM(0x14e4, 0x1653, "tg3-5705", "Broadcom Tigon 3 5705"), +PCI_ROM(0x14e4, 0x1654, "tg3-5705_2", "Broadcom Tigon 3 5705_2"), +PCI_ROM(0x14e4, 0x1659, "tg3-5721", "Broadcom Tigon 3 5721"), +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, 0x167a, "tg3-5754", "Broadcom Tigon 3 5754"), +PCI_ROM(0x14e4, 0x1693, "tg3-5787", "Broadcom Tigon 3 5787"), +PCI_ROM(0x14e4, 0x1696, "tg3-5782", "Broadcom Tigon 3 5782"), +PCI_ROM(0x14e4, 0x169a, "tg3-5786", "Broadcom Tigon 3 5786"), +PCI_ROM(0x14e4, 0x169c, "tg3-5788", "Broadcom Tigon 3 5788"), +PCI_ROM(0x14e4, 0x169d, "tg3-5789", "Broadcom Tigon 3 5789"), +PCI_ROM(0x14e4, 0x16a6, "tg3-5702X", "Broadcom Tigon 3 5702X"), +PCI_ROM(0x14e4, 0x16a7, "tg3-5703X", "Broadcom Tigon 3 5703X"), +PCI_ROM(0x14e4, 0x16a8, "tg3-5704S", "Broadcom Tigon 3 5704S"), +PCI_ROM(0x14e4, 0x16c6, "tg3-5702A3", "Broadcom Tigon 3 5702A3"), +PCI_ROM(0x14e4, 0x16c7, "tg3-5703A3", "Broadcom Tigon 3 5703A3"), +PCI_ROM(0x14e4, 0x170d, "tg3-5901", "Broadcom Tigon 3 5901"), +PCI_ROM(0x14e4, 0x170e, "tg3-5901_2", "Broadcom Tigon 3 5901_2"), +PCI_ROM(0x1148, 0x4400, "tg3-9DXX", "Syskonnect 9DXX"), +PCI_ROM(0x1148, 0x4500, "tg3-9MXX", "Syskonnect 9MXX"), +PCI_ROM(0x173b, 0x03e8, "tg3-ac1000", "Altima AC1000"), +PCI_ROM(0x173b, 0x03e9, "tg3-ac1001", "Altima AC1001"), +PCI_ROM(0x173b, 0x03ea, "tg3-ac9100", "Altima AC9100"), +PCI_ROM(0x173b, 0x03eb, "tg3-ac1003", "Altima AC1003"), +PCI_ROM(0x0e11, 0x00ca, "tg3-hp", "HP Tigon 3"), +}; + +PCI_DRIVER ( tg3_driver, tg3_nics, PCI_NO_CLASS ); + +DRIVER ( "TG3", nic_driver, pci_driver, tg3_driver, + tg3_probe, tg3_disable ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/tg3.h b/gpxe/src/drivers/net/tg3.h new file mode 100644 index 0000000..d1c09e0 --- /dev/null +++ b/gpxe/src/drivers/net/tg3.h @@ -0,0 +1,2226 @@ +/* $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_5721 0x4101 +#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 ASIC_REV_5787 0x0b +#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 +#define FTQ_RESET_DMA_READ_QUEUE (1 << 1) +#define FTQ_RESET_DMA_HIGH_PRI_READ (1 << 2) +#define FTQ_RESET_SEND_BD_COMPLETION (1 << 4) +#define FTQ_RESET_DMA_WRITE (1 << 6) +#define FTQ_RESET_DMA_HIGH_PRI_WRITE (1 << 7) +#define FTQ_RESET_SEND_DATA_COMPLETION (1 << 9) +#define FTQ_RESET_HOST_COALESCING (1 << 10) +#define FTQ_RESET_MAC_TX (1 << 11) +#define FTQ_RESET_RX_BD_COMPLETE (1 << 13) +#define FTQ_RESET_RX_LIST_PLCMT (1 << 14) +#define FTQ_RESET_RX_DATA_COMPLETION (1 << 16) +/* 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_BCM5787 0xbc050ce0 +#define PHY_ID_BCM8002 0x60010140 +#define PHY_ID_BCM5751 0x00206180 +#define PHY_ID_SERDES 0xfeedbee0 +#define PHY_ID_INVALID 0xffffffff +#define PHY_ID_REV_MASK 0x0000000f +#define PHY_REV_BCM5401_B0 0x1 +#define PHY_REV_BCM5401_B2 0x3 +#define PHY_REV_BCM5401_C0 0x6 +#define PHY_REV_BCM5411_X0 0x1 /* Found on Netgear GA302T */ + + enum phy_led_mode led_mode; + + char board_part_number[24]; + uint32_t nic_sram_data_cfg; + uint32_t pci_clock_ctrl; +#if 0 + struct pci_device *pdev_peer; +#endif + + /* This macro assumes the passed PHY ID is already masked + * with PHY_ID_MASK. + */ +#define KNOWN_PHY_ID(X) \ + ((X) == PHY_ID_BCM5400 || (X) == PHY_ID_BCM5401 || \ + (X) == PHY_ID_BCM5411 || (X) == PHY_ID_BCM5701 || \ + (X) == PHY_ID_BCM5703 || (X) == PHY_ID_BCM5704 || \ + (X) == PHY_ID_BCM5705 || (X) == PHY_ID_BCM5750 || \ + (X) == PHY_ID_BCM5751 || (X) == PHY_ID_BCM5787 || \ + (X) == PHY_ID_BCM8002 || (X) == PHY_ID_SERDES) + + unsigned long regs; + struct pci_device *pdev; + struct nic *nic; +#if 0 + struct net_device *dev; +#endif +#if TG3_VLAN_TAG_USED + struct vlan_group *vlgrp; +#endif + + struct tg3_rx_buffer_desc *rx_std; +#if 0 + struct ring_info *rx_std_buffers; + dma_addr_t rx_std_mapping; + struct tg3_rx_buffer_desc *rx_jumbo; + struct ring_info *rx_jumbo_buffers; + dma_addr_t rx_jumbo_mapping; +#endif + + struct tg3_rx_buffer_desc *rx_rcb; +#if 0 + dma_addr_t rx_rcb_mapping; +#endif + + /* TX descs are only used if TG3_FLAG_HOST_TXDS is set. */ + struct tg3_tx_buffer_desc *tx_ring; +#if 0 + struct tx_ring_info *tx_buffers; + dma_addr_t tx_desc_mapping; +#endif + + struct tg3_hw_status *hw_status; +#if 0 + dma_addr_t status_mapping; +#endif +#if 0 + uint32_t msg_enable; +#endif + + struct tg3_hw_stats *hw_stats; +#if 0 + dma_addr_t stats_mapping; +#endif + + int carrier_ok; + uint16_t subsystem_vendor; + uint16_t subsystem_device; +}; + +#endif /* !(_T3_H) */ diff --git a/gpxe/src/drivers/net/tlan.c b/gpxe/src/drivers/net/tlan.c new file mode 100644 index 0000000..4fae017 --- /dev/null +++ b/gpxe/src/drivers/net/tlan.c @@ -0,0 +1,1720 @@ +/************************************************************************** +* +* tlan.c -- Etherboot device driver for the Texas Instruments ThunderLAN +* Written 2003-2003 by Timothy Legge +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Portions of this code based on: +* lan.c: Linux ThunderLan Driver: +* +* by James Banks +* +* (C) 1997-1998 Caldera, Inc. +* (C) 1998 James Banks +* (C) 1999-2001 Torben Mathiasen +* (C) 2002 Samuel Chessman +* +* REVISION HISTORY: +* ================ +* v1.0 07-08-2003 timlegge Initial not quite working version +* v1.1 07-27-2003 timlegge Sync 5.0 and 5.1 versions +* v1.2 08-19-2003 timlegge Implement Multicast Support +* v1.3 08-23-2003 timlegge Fix the transmit Function +* v1.4 01-17-2004 timlegge Initial driver output cleanup +* +* Indent Options: indent -kr -i8 +***************************************************************************/ + +#include "etherboot.h" +#include "nic.h" +#include +#include +#include "tlan.h" + +#define drv_version "v1.4" +#define drv_date "01-17-2004" + +/* NIC specific static variables go here */ +#define HZ 100 +#define TX_TIME_OUT (6*HZ) + +/* Condensed operations for readability. */ +#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr)) + +static void TLan_ResetLists(struct nic *nic __unused); +static void TLan_ResetAdapter(struct nic *nic __unused); +static void TLan_FinishReset(struct nic *nic __unused); + +static void TLan_EeSendStart(u16); +static int TLan_EeSendByte(u16, u8, int); +static void TLan_EeReceiveByte(u16, u8 *, int); +static int TLan_EeReadByte(u16 io_base, u8, u8 *); + +static void TLan_PhyDetect(struct nic *nic); +static void TLan_PhyPowerDown(struct nic *nic); +static void TLan_PhyPowerUp(struct nic *nic); + + +static void TLan_SetMac(struct nic *nic __unused, int areg, unsigned char *mac); + +static void TLan_PhyReset(struct nic *nic); +static void TLan_PhyStartLink(struct nic *nic); +static void TLan_PhyFinishAutoNeg(struct nic *nic); + +#ifdef MONITOR +static void TLan_PhyMonitor(struct nic *nic); +#endif + + +static void refill_rx(struct nic *nic __unused); + +static int TLan_MiiReadReg(struct nic *nic __unused, u16, u16, u16 *); +static void TLan_MiiSendData(u16, u32, unsigned); +static void TLan_MiiSync(u16); +static void TLan_MiiWriteReg(struct nic *nic __unused, u16, u16, u16); + + +static const char *media[] = { + "10BaseT-HD ", "10BaseT-FD ", "100baseTx-HD ", + "100baseTx-FD", "100baseT4", 0 +}; + +/* This much match tlan_pci_tbl[]! */ +enum tlan_nics { + NETEL10 = 0, NETEL100 = 1, NETFLEX3I = 2, THUNDER = 3, NETFLEX3B = + 4, NETEL100PI = 5, + NETEL100D = 6, NETEL100I = 7, OC2183 = 8, OC2325 = 9, OC2326 = + 10, NETELLIGENT_10_100_WS_5100 = 11, + NETELLIGENT_10_T2 = 12 +}; + +struct pci_id_info { + const char *name; + int nic_id; + struct match_info { + u32 pci, pci_mask, subsystem, subsystem_mask; + u32 revision, revision_mask; /* Only 8 bits. */ + } id; + u32 flags; + u16 addrOfs; /* Address Offset */ +}; + +static const struct pci_id_info tlan_pci_tbl[] = { + {"Compaq Netelligent 10 T PCI UTP", NETEL10, + {0xae340e11, 0xffffffff, 0, 0, 0, 0}, + TLAN_ADAPTER_ACTIVITY_LED, 0x83}, + {"Compaq Netelligent 10/100 TX PCI UTP", NETEL100, + {0xae320e11, 0xffffffff, 0, 0, 0, 0}, + TLAN_ADAPTER_ACTIVITY_LED, 0x83}, + {"Compaq Integrated NetFlex-3/P", NETFLEX3I, + {0xae350e11, 0xffffffff, 0, 0, 0, 0}, + TLAN_ADAPTER_NONE, 0x83}, + {"Compaq NetFlex-3/P", THUNDER, + {0xf1300e11, 0xffffffff, 0, 0, 0, 0}, + TLAN_ADAPTER_UNMANAGED_PHY | TLAN_ADAPTER_BIT_RATE_PHY, 0x83}, + {"Compaq NetFlex-3/P", NETFLEX3B, + {0xf1500e11, 0xffffffff, 0, 0, 0, 0}, + TLAN_ADAPTER_NONE, 0x83}, + {"Compaq Netelligent Integrated 10/100 TX UTP", NETEL100PI, + {0xae430e11, 0xffffffff, 0, 0, 0, 0}, + TLAN_ADAPTER_ACTIVITY_LED, 0x83}, + {"Compaq Netelligent Dual 10/100 TX PCI UTP", NETEL100D, + {0xae400e11, 0xffffffff, 0, 0, 0, 0}, + TLAN_ADAPTER_NONE, 0x83}, + {"Compaq Netelligent 10/100 TX Embedded UTP", NETEL100I, + {0xb0110e11, 0xffffffff, 0, 0, 0, 0}, + TLAN_ADAPTER_NONE, 0x83}, + {"Olicom OC-2183/2185", OC2183, + {0x0013108d, 0xffffffff, 0, 0, 0, 0}, + TLAN_ADAPTER_USE_INTERN_10, 0x83}, + {"Olicom OC-2325", OC2325, + {0x0012108d, 0xffffffff, 0, 0, 0, 0}, + TLAN_ADAPTER_UNMANAGED_PHY, 0xF8}, + {"Olicom OC-2326", OC2326, + {0x0014108d, 0xffffffff, 0, 0, 0, 0}, + TLAN_ADAPTER_USE_INTERN_10, 0xF8}, + {"Compaq Netelligent 10/100 TX UTP", NETELLIGENT_10_100_WS_5100, + {0xb0300e11, 0xffffffff, 0, 0, 0, 0}, + TLAN_ADAPTER_ACTIVITY_LED, 0x83}, + {"Compaq Netelligent 10 T/2 PCI UTP/Coax", NETELLIGENT_10_T2, + {0xb0120e11, 0xffffffff, 0, 0, 0, 0}, + TLAN_ADAPTER_NONE, 0x83}, + {"Compaq NetFlex-3/E", 0, /* EISA card */ + {0, 0, 0, 0, 0, 0}, + TLAN_ADAPTER_ACTIVITY_LED | TLAN_ADAPTER_UNMANAGED_PHY | + TLAN_ADAPTER_BIT_RATE_PHY, 0x83}, + {"Compaq NetFlex-3/E", 0, /* EISA card */ + {0, 0, 0, 0, 0, 0}, + TLAN_ADAPTER_ACTIVITY_LED, 0x83}, + {0, 0, + {0, 0, 0, 0, 0, 0}, + 0, 0}, +}; + +struct TLanList { + u32 forward; + u16 cStat; + u16 frameSize; + struct { + u32 count; + u32 address; + } buffer[TLAN_BUFFERS_PER_LIST]; +}; + +struct { + struct TLanList tx_ring[TLAN_NUM_TX_LISTS]; + unsigned char txb[TLAN_MAX_FRAME_SIZE * TLAN_NUM_TX_LISTS]; + struct TLanList rx_ring[TLAN_NUM_RX_LISTS]; + unsigned char rxb[TLAN_MAX_FRAME_SIZE * TLAN_NUM_RX_LISTS]; +} tlan_buffers __shared; +#define tx_ring tlan_buffers.tx_ring +#define txb tlan_buffers.txb +#define rx_ring tlan_buffers.rx_ring +#define rxb tlan_buffers.rxb + +typedef u8 TLanBuffer[TLAN_MAX_FRAME_SIZE]; + +static int chip_idx; + +/***************************************************************** +* TLAN Private Information Structure +* +****************************************************************/ +static struct tlan_private { + unsigned short vendor_id; /* PCI Vendor code */ + unsigned short dev_id; /* PCI Device code */ + const char *nic_name; + unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indicies */ + unsigned rx_buf_sz; /* Based on mtu + Slack */ + struct TLanList *txList; + u32 txHead; + u32 txInProgress; + u32 txTail; + int eoc; + u32 phyOnline; + u32 aui; + u32 duplex; + u32 phy[2]; + u32 phyNum; + u32 speed; + u8 tlanRev; + u8 tlanFullDuplex; + u8 link; + u8 neg_be_verbose; +} TLanPrivateInfo; + +static struct tlan_private *priv; + +static u32 BASE; + +/*************************************************************** +* TLan_ResetLists +* +* Returns: +* Nothing +* Parms: +* dev The device structure with the list +* stuctures to be reset. +* +* This routine sets the variables associated with managing +* the TLAN lists to their initial values. +* +**************************************************************/ + +static void TLan_ResetLists(struct nic *nic __unused) +{ + + int i; + struct TLanList *list; + priv->txHead = 0; + priv->txTail = 0; + + for (i = 0; i < TLAN_NUM_TX_LISTS; i++) { + list = &tx_ring[i]; + list->cStat = TLAN_CSTAT_UNUSED; + list->buffer[0].address = virt_to_bus(txb + + (i * TLAN_MAX_FRAME_SIZE)); + list->buffer[2].count = 0; + list->buffer[2].address = 0; + list->buffer[9].address = 0; + } + + priv->cur_rx = 0; + priv->rx_buf_sz = (TLAN_MAX_FRAME_SIZE); +// priv->rx_head_desc = &rx_ring[0]; + + /* Initialize all the Rx descriptors */ + for (i = 0; i < TLAN_NUM_RX_LISTS; i++) { + rx_ring[i].forward = virt_to_le32desc(&rx_ring[i + 1]); + rx_ring[i].cStat = TLAN_CSTAT_READY; + rx_ring[i].frameSize = TLAN_MAX_FRAME_SIZE; + rx_ring[i].buffer[0].count = + TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER; + rx_ring[i].buffer[0].address = + virt_to_le32desc(&rxb[i * TLAN_MAX_FRAME_SIZE]); + rx_ring[i].buffer[1].count = 0; + rx_ring[i].buffer[1].address = 0; + } + + /* Mark the last entry as wrapping the ring */ + rx_ring[i - 1].forward = virt_to_le32desc(&rx_ring[0]); + priv->dirty_rx = (unsigned int) (i - TLAN_NUM_RX_LISTS); + +} /* TLan_ResetLists */ + +/*************************************************************** +* TLan_Reset +* +* Returns: +* 0 +* Parms: +* dev Pointer to device structure of adapter +* to be reset. +* +* This function resets the adapter and it's physical +* device. See Chap. 3, pp. 9-10 of the "ThunderLAN +* Programmer's Guide" for details. The routine tries to +* implement what is detailed there, though adjustments +* have been made. +* +**************************************************************/ + +void TLan_ResetAdapter(struct nic *nic __unused) +{ + int i; + u32 addr; + u32 data; + u8 data8; + + priv->tlanFullDuplex = FALSE; + priv->phyOnline = 0; +/* 1. Assert reset bit. */ + + data = inl(BASE + TLAN_HOST_CMD); + data |= TLAN_HC_AD_RST; + outl(data, BASE + TLAN_HOST_CMD); + + udelay(1000); + +/* 2. Turn off interrupts. ( Probably isn't necessary ) */ + + data = inl(BASE + TLAN_HOST_CMD); + data |= TLAN_HC_INT_OFF; + outl(data, BASE + TLAN_HOST_CMD); +/* 3. Clear AREGs and HASHs. */ + + for (i = TLAN_AREG_0; i <= TLAN_HASH_2; i += 4) { + TLan_DioWrite32(BASE, (u16) i, 0); + } + +/* 4. Setup NetConfig register. */ + + data = + TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN; + TLan_DioWrite16(BASE, TLAN_NET_CONFIG, (u16) data); + +/* 5. Load Ld_Tmr and Ld_Thr in HOST_CMD. */ + + outl(TLAN_HC_LD_TMR | 0x3f, BASE + TLAN_HOST_CMD); + outl(TLAN_HC_LD_THR | 0x0, BASE + TLAN_HOST_CMD); + +/* 6. Unreset the MII by setting NMRST (in NetSio) to 1. */ + + outw(TLAN_NET_SIO, BASE + TLAN_DIO_ADR); + addr = BASE + TLAN_DIO_DATA + TLAN_NET_SIO; + TLan_SetBit(TLAN_NET_SIO_NMRST, addr); + +/* 7. Setup the remaining registers. */ + + if (priv->tlanRev >= 0x30) { + data8 = TLAN_ID_TX_EOC | TLAN_ID_RX_EOC; + TLan_DioWrite8(BASE, TLAN_INT_DIS, data8); + } + TLan_PhyDetect(nic); + data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN; + + if (tlan_pci_tbl[chip_idx].flags & TLAN_ADAPTER_BIT_RATE_PHY) { + data |= TLAN_NET_CFG_BIT; + if (priv->aui == 1) { + TLan_DioWrite8(BASE, TLAN_ACOMMIT, 0x0a); + } else if (priv->duplex == TLAN_DUPLEX_FULL) { + TLan_DioWrite8(BASE, TLAN_ACOMMIT, 0x00); + priv->tlanFullDuplex = TRUE; + } else { + TLan_DioWrite8(BASE, TLAN_ACOMMIT, 0x08); + } + } + + if (priv->phyNum == 0) { + data |= TLAN_NET_CFG_PHY_EN; + } + TLan_DioWrite16(BASE, TLAN_NET_CONFIG, (u16) data); + + if (tlan_pci_tbl[chip_idx].flags & TLAN_ADAPTER_UNMANAGED_PHY) { + TLan_FinishReset(nic); + } else { + TLan_PhyPowerDown(nic); + } + +} /* TLan_ResetAdapter */ + +void TLan_FinishReset(struct nic *nic) +{ + + u8 data; + u32 phy; + u8 sio; + u16 status; + u16 partner; + u16 tlphy_ctl; + u16 tlphy_par; + u16 tlphy_id1, tlphy_id2; + int i; + + phy = priv->phy[priv->phyNum]; + + data = TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP; + if (priv->tlanFullDuplex) { + data |= TLAN_NET_CMD_DUPLEX; + } + TLan_DioWrite8(BASE, TLAN_NET_CMD, data); + data = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5; + if (priv->phyNum == 0) { + data |= TLAN_NET_MASK_MASK7; + } + TLan_DioWrite8(BASE, TLAN_NET_MASK, data); + TLan_DioWrite16(BASE, TLAN_MAX_RX, ((1536) + 7) & ~7); + TLan_MiiReadReg(nic, phy, MII_GEN_ID_HI, &tlphy_id1); + TLan_MiiReadReg(nic, phy, MII_GEN_ID_LO, &tlphy_id2); + + if ((tlan_pci_tbl[chip_idx].flags & TLAN_ADAPTER_UNMANAGED_PHY) + || (priv->aui)) { + status = MII_GS_LINK; + DBG ( "TLAN: %s: Link forced.\n", priv->nic_name ); + } else { + TLan_MiiReadReg(nic, phy, MII_GEN_STS, &status); + udelay(1000); + TLan_MiiReadReg(nic, phy, MII_GEN_STS, &status); + if ((status & MII_GS_LINK) && /* We only support link info on Nat.Sem. PHY's */ + (tlphy_id1 == NAT_SEM_ID1) + && (tlphy_id2 == NAT_SEM_ID2)) { + TLan_MiiReadReg(nic, phy, MII_AN_LPA, &partner); + TLan_MiiReadReg(nic, phy, TLAN_TLPHY_PAR, + &tlphy_par); + + DBG ( "TLAN: %s: Link active with ", + priv->nic_name ); + if (!(tlphy_par & TLAN_PHY_AN_EN_STAT)) { + DBG ( "forced 10%sMbps %s-Duplex\n", + tlphy_par & TLAN_PHY_SPEED_100 ? "" + : "0", + tlphy_par & TLAN_PHY_DUPLEX_FULL ? + "Full" : "Half" ); + } else { + DBG + ( "AutoNegotiation enabled, at 10%sMbps %s-Duplex\n", + tlphy_par & TLAN_PHY_SPEED_100 ? "" : + "0", + tlphy_par & TLAN_PHY_DUPLEX_FULL ? + "Full" : "Half" ); + DBG ( "TLAN: Partner capability: " ); + for (i = 5; i <= 10; i++) + if (partner & (1 << i)) { + DBG ( "%s", media[i - 5] ); + } + DBG ( "\n" ); + } + + TLan_DioWrite8(BASE, TLAN_LED_REG, TLAN_LED_LINK); +#ifdef MONITOR + /* We have link beat..for now anyway */ + priv->link = 1; + /*Enabling link beat monitoring */ + /* TLan_SetTimer( nic, (10*HZ), TLAN_TIMER_LINK_BEAT ); */ + mdelay(10000); + TLan_PhyMonitor(nic); +#endif + } else if (status & MII_GS_LINK) { + DBG ( "TLAN: %s: Link active\n", priv->nic_name ); + TLan_DioWrite8(BASE, TLAN_LED_REG, TLAN_LED_LINK); + } + } + + if (priv->phyNum == 0) { + TLan_MiiReadReg(nic, phy, TLAN_TLPHY_CTL, &tlphy_ctl); + tlphy_ctl |= TLAN_TC_INTEN; + TLan_MiiWriteReg(nic, phy, TLAN_TLPHY_CTL, tlphy_ctl); + sio = TLan_DioRead8(BASE, TLAN_NET_SIO); + sio |= TLAN_NET_SIO_MINTEN; + TLan_DioWrite8(BASE, TLAN_NET_SIO, sio); + } + + if (status & MII_GS_LINK) { + TLan_SetMac(nic, 0, nic->node_addr); + priv->phyOnline = 1; + outb((TLAN_HC_INT_ON >> 8), BASE + TLAN_HOST_CMD + 1); + outl(virt_to_bus(&rx_ring), BASE + TLAN_CH_PARM); + outl(TLAN_HC_GO | TLAN_HC_RT, BASE + TLAN_HOST_CMD); + } else { + DBG + ( "TLAN: %s: Link inactive, will retry in 10 secs...\n", + priv->nic_name ); + /* TLan_SetTimer( nic, (10*HZ), TLAN_TIMER_FINISH_RESET ); */ + mdelay(10000); + TLan_FinishReset(nic); + return; + + } + +} /* TLan_FinishReset */ + +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int tlan_poll(struct nic *nic, int retrieve) +{ + /* return true if there's an ethernet packet ready to read */ + /* nic->packet should contain data on return */ + /* nic->packetlen should contain length of data */ + u32 framesize; + u32 host_cmd = 0; + u32 ack = 1; + int eoc = 0; + int entry = priv->cur_rx % TLAN_NUM_RX_LISTS; + u16 tmpCStat = le32_to_cpu(rx_ring[entry].cStat); + u16 host_int = inw(BASE + TLAN_HOST_INT); + + if ((tmpCStat & TLAN_CSTAT_FRM_CMP) && !retrieve) + return 1; + + outw(host_int, BASE + TLAN_HOST_INT); + + if (!(tmpCStat & TLAN_CSTAT_FRM_CMP)) + return 0; + + /* printf("PI-1: 0x%hX\n", host_int); */ + if (tmpCStat & TLAN_CSTAT_EOC) + eoc = 1; + + framesize = rx_ring[entry].frameSize; + + nic->packetlen = framesize; + + DBG ( ".%d.", (unsigned int) framesize ); + + memcpy(nic->packet, rxb + + (priv->cur_rx * TLAN_MAX_FRAME_SIZE), nic->packetlen); + + rx_ring[entry].cStat = 0; + + DBG ( "%d", entry ); + + entry = (entry + 1) % TLAN_NUM_RX_LISTS; + priv->cur_rx = entry; + if (eoc) { + if ((rx_ring[entry].cStat & TLAN_CSTAT_READY) == + TLAN_CSTAT_READY) { + ack |= TLAN_HC_GO | TLAN_HC_RT; + host_cmd = TLAN_HC_ACK | ack | 0x001C0000; + outl(host_cmd, BASE + TLAN_HOST_CMD); + } + } else { + host_cmd = TLAN_HC_ACK | ack | (0x000C0000); + outl(host_cmd, BASE + TLAN_HOST_CMD); + + DBG ( "AC: 0x%hX\n", inw(BASE + TLAN_CH_PARM) ); + DBG ( "PI-2: 0x%hX\n", inw(BASE + TLAN_HOST_INT) ); + } + refill_rx(nic); + return (1); /* initially as this is called to flush the input */ +} + +static void refill_rx(struct nic *nic __unused) +{ + int entry = 0; + + for (; + (priv->cur_rx - priv->dirty_rx + + TLAN_NUM_RX_LISTS) % TLAN_NUM_RX_LISTS > 0; + priv->dirty_rx = (priv->dirty_rx + 1) % TLAN_NUM_RX_LISTS) { + entry = priv->dirty_rx % TLAN_NUM_TX_LISTS; + rx_ring[entry].frameSize = TLAN_MAX_FRAME_SIZE; + rx_ring[entry].cStat = TLAN_CSTAT_READY; + } + +} + +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static void tlan_transmit(struct nic *nic, const char *d, /* Destination */ + unsigned int t, /* Type */ + unsigned int s, /* size */ + const char *p) +{ /* Packet */ + u16 nstype; + u32 to; + struct TLanList *tail_list; + struct TLanList *head_list; + u8 *tail_buffer; + u32 ack = 0; + u32 host_cmd; + int eoc = 0; + u16 tmpCStat; + u16 host_int = inw(BASE + TLAN_HOST_INT); + + int entry = 0; + + DBG ( "INT0-0x%hX\n", host_int ); + + if (!priv->phyOnline) { + printf("TRANSMIT: %s PHY is not ready\n", priv->nic_name); + return; + } + + tail_list = priv->txList + priv->txTail; + + if (tail_list->cStat != TLAN_CSTAT_UNUSED) { + printf("TRANSMIT: %s is busy (Head=%p Tail=%x)\n", + priv->nic_name, priv->txList, (unsigned int) priv->txTail); + tx_ring[entry].cStat = TLAN_CSTAT_UNUSED; +// priv->txBusyCount++; + return; + } + + tail_list->forward = 0; + + tail_buffer = txb + (priv->txTail * TLAN_MAX_FRAME_SIZE); + + /* send the packet to destination */ + memcpy(tail_buffer, d, ETH_ALEN); + memcpy(tail_buffer + ETH_ALEN, nic->node_addr, ETH_ALEN); + nstype = htons((u16) t); + memcpy(tail_buffer + 2 * ETH_ALEN, (u8 *) & nstype, 2); + memcpy(tail_buffer + ETH_HLEN, p, s); + + s += ETH_HLEN; + s &= 0x0FFF; + while (s < ETH_ZLEN) + tail_buffer[s++] = '\0'; + + /*=====================================================*/ + /* Receive + * 0000 0000 0001 1100 + * 0000 0000 0000 1100 + * 0000 0000 0000 0011 = 0x0003 + * + * 0000 0000 0000 0000 0000 0000 0000 0011 + * 0000 0000 0000 1100 0000 0000 0000 0000 = 0x000C0000 + * + * Transmit + * 0000 0000 0001 1100 + * 0000 0000 0000 0100 + * 0000 0000 0000 0001 = 0x0001 + * + * 0000 0000 0000 0000 0000 0000 0000 0001 + * 0000 0000 0000 0100 0000 0000 0000 0000 = 0x00040000 + * */ + + /* Setup the transmit descriptor */ + tail_list->frameSize = (u16) s; + tail_list->buffer[0].count = TLAN_LAST_BUFFER | (u32) s; + tail_list->buffer[1].count = 0; + tail_list->buffer[1].address = 0; + + tail_list->cStat = TLAN_CSTAT_READY; + + DBG ( "INT1-0x%hX\n", inw(BASE + TLAN_HOST_INT) ); + + if (!priv->txInProgress) { + priv->txInProgress = 1; + outl(virt_to_le32desc(tail_list), BASE + TLAN_CH_PARM); + outl(TLAN_HC_GO, BASE + TLAN_HOST_CMD); + } else { + if (priv->txTail == 0) { + DBG ( "Out buffer\n" ); + (priv->txList + (TLAN_NUM_TX_LISTS - 1))->forward = + virt_to_le32desc(tail_list); + } else { + DBG ( "Fix this \n" ); + (priv->txList + (priv->txTail - 1))->forward = + virt_to_le32desc(tail_list); + } + } + + CIRC_INC(priv->txTail, TLAN_NUM_TX_LISTS); + + DBG ( "INT2-0x%hX\n", inw(BASE + TLAN_HOST_INT) ); + + to = currticks() + TX_TIME_OUT; + while ((tail_list->cStat == TLAN_CSTAT_READY) && currticks() < to); + + head_list = priv->txList + priv->txHead; + while (((tmpCStat = head_list->cStat) & TLAN_CSTAT_FRM_CMP) + && (ack < 255)) { + ack++; + if(tmpCStat & TLAN_CSTAT_EOC) + eoc =1; + head_list->cStat = TLAN_CSTAT_UNUSED; + CIRC_INC(priv->txHead, TLAN_NUM_TX_LISTS); + head_list = priv->txList + priv->txHead; + + } + if(!ack) + printf("Incomplete TX Frame\n"); + + if(eoc) { + head_list = priv->txList + priv->txHead; + if ((head_list->cStat & TLAN_CSTAT_READY) == TLAN_CSTAT_READY) { + outl(virt_to_le32desc(head_list), BASE + TLAN_CH_PARM); + ack |= TLAN_HC_GO; + } else { + priv->txInProgress = 0; + } + } + if(ack) { + host_cmd = TLAN_HC_ACK | ack; + outl(host_cmd, BASE + TLAN_HOST_CMD); + } + + if(priv->tlanRev < 0x30 ) { + ack = 1; + head_list = priv->txList + priv->txHead; + if ((head_list->cStat & TLAN_CSTAT_READY) == TLAN_CSTAT_READY) { + outl(virt_to_le32desc(head_list), BASE + TLAN_CH_PARM); + ack |= TLAN_HC_GO; + } else { + priv->txInProgress = 0; + } + host_cmd = TLAN_HC_ACK | ack | 0x00140000; + outl(host_cmd, BASE + TLAN_HOST_CMD); + + } + + if (currticks() >= to) { + printf("TX Time Out"); + } +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void tlan_disable ( struct nic *nic __unused ) { + /* put the card in its initial state */ + /* This function serves 3 purposes. + * This disables DMA and interrupts so we don't receive + * unexpected packets or interrupts from the card after + * etherboot has finished. + * This frees resources so etherboot may use + * this driver on another interface + * This allows etherboot to reinitialize the interface + * if something is something goes wrong. + * + */ + outl(TLAN_HC_AD_RST, BASE + TLAN_HOST_CMD); +} + +/************************************************************************** +IRQ - Enable, Disable, or Force interrupts +***************************************************************************/ +static void tlan_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + +static struct nic_operations tlan_operations = { + .connect = dummy_connect, + .poll = tlan_poll, + .transmit = tlan_transmit, + .irq = tlan_irq, + +}; + +static void TLan_SetMulticastList(struct nic *nic) { + int i; + u8 tmp; + + /* !IFF_PROMISC */ + tmp = TLan_DioRead8(BASE, TLAN_NET_CMD); + TLan_DioWrite8(BASE, TLAN_NET_CMD, tmp & ~TLAN_NET_CMD_CAF); + + /* IFF_ALLMULTI */ + for(i = 0; i< 3; i++) + TLan_SetMac(nic, i + 1, NULL); + TLan_DioWrite32(BASE, TLAN_HASH_1, 0xFFFFFFFF); + TLan_DioWrite32(BASE, TLAN_HASH_2, 0xFFFFFFFF); + + +} +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ + +#define board_found 1 +#define valid_link 0 +static int tlan_probe ( struct nic *nic, struct pci_device *pci ) { + + u16 data = 0; + int err; + int i; + + if (pci->ioaddr == 0) + return 0; + + nic->irqno = 0; + nic->ioaddr = pci->ioaddr; + + BASE = pci->ioaddr; + + /* Set nic as PCI bus master */ + adjust_pci_device(pci); + + /* Point to private storage */ + priv = &TLanPrivateInfo; + + /* Figure out which chip we're dealing with */ + i = 0; + chip_idx = -1; + while (tlan_pci_tbl[i].name) { + if ((((u32) pci->device << 16) | pci->vendor) == + (tlan_pci_tbl[i].id.pci & 0xffffffff)) { + chip_idx = i; + break; + } + i++; + } + + priv->vendor_id = pci->vendor; + priv->dev_id = pci->device; + priv->nic_name = pci->driver_name; + priv->eoc = 0; + + err = 0; + for (i = 0; i < 6; i++) + err |= TLan_EeReadByte(BASE, + (u8) tlan_pci_tbl[chip_idx]. + addrOfs + i, + (u8 *) & nic->node_addr[i]); + if (err) { + printf ( "TLAN: %s: Error reading MAC from eeprom: %d\n", + pci->driver_name, err); + } else { + DBG ( "%s: %s at ioaddr %#lX, ", + pci->driver_name, eth_ntoa ( nic->node_addr ), pci->ioaddr ); + } + + priv->tlanRev = TLan_DioRead8(BASE, TLAN_DEF_REVISION); + printf("revision: 0x%hX\n", priv->tlanRev); + + TLan_ResetLists(nic); + TLan_ResetAdapter(nic); + + data = inl(BASE + TLAN_HOST_CMD); + data |= TLAN_HC_INT_OFF; + outw(data, BASE + TLAN_HOST_CMD); + + TLan_SetMulticastList(nic); + udelay(100); + priv->txList = tx_ring; + +/* if (board_found && valid_link) + {*/ + /* point to NIC specific routines */ + nic->nic_op = &tlan_operations; + return 1; +} + + +/***************************************************************************** +****************************************************************************** + + ThunderLAN Driver Eeprom routines + + The Compaq Netelligent 10 and 10/100 cards use a Microchip 24C02A + EEPROM. These functions are based on information in Microchip's + data sheet. I don't know how well this functions will work with + other EEPROMs. + +****************************************************************************** +*****************************************************************************/ + + +/*************************************************************** +* TLan_EeSendStart +* +* Returns: +* Nothing +* Parms: +* io_base The IO port base address for the +* TLAN device with the EEPROM to +* use. +* +* This function sends a start cycle to an EEPROM attached +* to a TLAN chip. +* +**************************************************************/ + +void TLan_EeSendStart(u16 io_base) +{ + u16 sio; + + outw(TLAN_NET_SIO, io_base + TLAN_DIO_ADR); + sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; + + TLan_SetBit(TLAN_NET_SIO_ECLOK, sio); + TLan_SetBit(TLAN_NET_SIO_EDATA, sio); + TLan_SetBit(TLAN_NET_SIO_ETXEN, sio); + TLan_ClearBit(TLAN_NET_SIO_EDATA, sio); + TLan_ClearBit(TLAN_NET_SIO_ECLOK, sio); + +} /* TLan_EeSendStart */ + +/*************************************************************** +* TLan_EeSendByte +* +* Returns: +* If the correct ack was received, 0, otherwise 1 +* Parms: io_base The IO port base address for the +* TLAN device with the EEPROM to +* use. +* data The 8 bits of information to +* send to the EEPROM. +* stop If TLAN_EEPROM_STOP is passed, a +* stop cycle is sent after the +* byte is sent after the ack is +* read. +* +* This function sends a byte on the serial EEPROM line, +* driving the clock to send each bit. The function then +* reverses transmission direction and reads an acknowledge +* bit. +* +**************************************************************/ + +int TLan_EeSendByte(u16 io_base, u8 data, int stop) +{ + int err; + u8 place; + u16 sio; + + outw(TLAN_NET_SIO, io_base + TLAN_DIO_ADR); + sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; + + /* Assume clock is low, tx is enabled; */ + for (place = 0x80; place != 0; place >>= 1) { + if (place & data) + TLan_SetBit(TLAN_NET_SIO_EDATA, sio); + else + TLan_ClearBit(TLAN_NET_SIO_EDATA, sio); + TLan_SetBit(TLAN_NET_SIO_ECLOK, sio); + TLan_ClearBit(TLAN_NET_SIO_ECLOK, sio); + } + TLan_ClearBit(TLAN_NET_SIO_ETXEN, sio); + TLan_SetBit(TLAN_NET_SIO_ECLOK, sio); + err = TLan_GetBit(TLAN_NET_SIO_EDATA, sio); + TLan_ClearBit(TLAN_NET_SIO_ECLOK, sio); + TLan_SetBit(TLAN_NET_SIO_ETXEN, sio); + + if ((!err) && stop) { + TLan_ClearBit(TLAN_NET_SIO_EDATA, sio); /* STOP, raise data while clock is high */ + TLan_SetBit(TLAN_NET_SIO_ECLOK, sio); + TLan_SetBit(TLAN_NET_SIO_EDATA, sio); + } + + return (err); + +} /* TLan_EeSendByte */ + +/*************************************************************** +* TLan_EeReceiveByte +* +* Returns: +* Nothing +* Parms: +* io_base The IO port base address for the +* TLAN device with the EEPROM to +* use. +* data An address to a char to hold the +* data sent from the EEPROM. +* stop If TLAN_EEPROM_STOP is passed, a +* stop cycle is sent after the +* byte is received, and no ack is +* sent. +* +* This function receives 8 bits of data from the EEPROM +* over the serial link. It then sends and ack bit, or no +* ack and a stop bit. This function is used to retrieve +* data after the address of a byte in the EEPROM has been +* sent. +* +**************************************************************/ + +void TLan_EeReceiveByte(u16 io_base, u8 * data, int stop) +{ + u8 place; + u16 sio; + + outw(TLAN_NET_SIO, io_base + TLAN_DIO_ADR); + sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO; + *data = 0; + + /* Assume clock is low, tx is enabled; */ + TLan_ClearBit(TLAN_NET_SIO_ETXEN, sio); + for (place = 0x80; place; place >>= 1) { + TLan_SetBit(TLAN_NET_SIO_ECLOK, sio); + if (TLan_GetBit(TLAN_NET_SIO_EDATA, sio)) + *data |= place; + TLan_ClearBit(TLAN_NET_SIO_ECLOK, sio); + } + + TLan_SetBit(TLAN_NET_SIO_ETXEN, sio); + if (!stop) { + TLan_ClearBit(TLAN_NET_SIO_EDATA, sio); /* Ack = 0 */ + TLan_SetBit(TLAN_NET_SIO_ECLOK, sio); + TLan_ClearBit(TLAN_NET_SIO_ECLOK, sio); + } else { + TLan_SetBit(TLAN_NET_SIO_EDATA, sio); /* No ack = 1 (?) */ + TLan_SetBit(TLAN_NET_SIO_ECLOK, sio); + TLan_ClearBit(TLAN_NET_SIO_ECLOK, sio); + TLan_ClearBit(TLAN_NET_SIO_EDATA, sio); /* STOP, raise data while clock is high */ + TLan_SetBit(TLAN_NET_SIO_ECLOK, sio); + TLan_SetBit(TLAN_NET_SIO_EDATA, sio); + } + +} /* TLan_EeReceiveByte */ + +/*************************************************************** +* TLan_EeReadByte +* +* Returns: +* No error = 0, else, the stage at which the error +* occurred. +* Parms: +* io_base The IO port base address for the +* TLAN device with the EEPROM to +* use. +* ee_addr The address of the byte in the +* EEPROM whose contents are to be +* retrieved. +* data An address to a char to hold the +* data obtained from the EEPROM. +* +* This function reads a byte of information from an byte +* cell in the EEPROM. +* +**************************************************************/ + +int TLan_EeReadByte(u16 io_base, u8 ee_addr, u8 * data) +{ + int err; + int ret = 0; + + + TLan_EeSendStart(io_base); + err = TLan_EeSendByte(io_base, 0xA0, TLAN_EEPROM_ACK); + if (err) { + ret = 1; + goto fail; + } + err = TLan_EeSendByte(io_base, ee_addr, TLAN_EEPROM_ACK); + if (err) { + ret = 2; + goto fail; + } + TLan_EeSendStart(io_base); + err = TLan_EeSendByte(io_base, 0xA1, TLAN_EEPROM_ACK); + if (err) { + ret = 3; + goto fail; + } + TLan_EeReceiveByte(io_base, data, TLAN_EEPROM_STOP); + fail: + + return ret; + +} /* TLan_EeReadByte */ + + +/***************************************************************************** +****************************************************************************** + +ThunderLAN Driver MII Routines + +These routines are based on the information in Chap. 2 of the +"ThunderLAN Programmer's Guide", pp. 15-24. + +****************************************************************************** +*****************************************************************************/ + + +/*************************************************************** +* TLan_MiiReadReg +* +* Returns: +* 0 if ack received ok +* 1 otherwise. +* +* Parms: +* dev The device structure containing +* The io address and interrupt count +* for this device. +* phy The address of the PHY to be queried. +* reg The register whose contents are to be +* retreived. +* val A pointer to a variable to store the +* retrieved value. +* +* This function uses the TLAN's MII bus to retreive the contents +* of a given register on a PHY. It sends the appropriate info +* and then reads the 16-bit register value from the MII bus via +* the TLAN SIO register. +* +**************************************************************/ + +int TLan_MiiReadReg(struct nic *nic __unused, u16 phy, u16 reg, u16 * val) +{ + u8 nack; + u16 sio, tmp; + u32 i; + int err; + int minten; + + err = FALSE; + outw(TLAN_NET_SIO, BASE + TLAN_DIO_ADR); + sio = BASE + TLAN_DIO_DATA + TLAN_NET_SIO; + + TLan_MiiSync(BASE); + + minten = TLan_GetBit(TLAN_NET_SIO_MINTEN, sio); + if (minten) + TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio); + + TLan_MiiSendData(BASE, 0x1, 2); /* Start ( 01b ) */ + TLan_MiiSendData(BASE, 0x2, 2); /* Read ( 10b ) */ + TLan_MiiSendData(BASE, phy, 5); /* Device # */ + TLan_MiiSendData(BASE, reg, 5); /* Register # */ + + + TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio); /* Change direction */ + + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Clock Idle bit */ + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Wait 300ns */ + + nack = TLan_GetBit(TLAN_NET_SIO_MDATA, sio); /* Check for ACK */ + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); /* Finish ACK */ + if (nack) { /* No ACK, so fake it */ + for (i = 0; i < 16; i++) { + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + } + tmp = 0xffff; + err = TRUE; + } else { /* ACK, so read data */ + for (tmp = 0, i = 0x8000; i; i >>= 1) { + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); + if (TLan_GetBit(TLAN_NET_SIO_MDATA, sio)) + tmp |= i; + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + } + } + + + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Idle cycle */ + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + + if (minten) + TLan_SetBit(TLAN_NET_SIO_MINTEN, sio); + + *val = tmp; + + return err; + +} /* TLan_MiiReadReg */ + +/*************************************************************** +* TLan_MiiSendData +* +* Returns: +* Nothing +* Parms: +* base_port The base IO port of the adapter in +* question. +* dev The address of the PHY to be queried. +* data The value to be placed on the MII bus. +* num_bits The number of bits in data that are to +* be placed on the MII bus. +* +* This function sends on sequence of bits on the MII +* configuration bus. +* +**************************************************************/ + +void TLan_MiiSendData(u16 base_port, u32 data, unsigned num_bits) +{ + u16 sio; + u32 i; + + if (num_bits == 0) + return; + + outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR); + sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; + TLan_SetBit(TLAN_NET_SIO_MTXEN, sio); + + for (i = (0x1 << (num_bits - 1)); i; i >>= 1) { + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); + (void) TLan_GetBit(TLAN_NET_SIO_MCLK, sio); + if (data & i) + TLan_SetBit(TLAN_NET_SIO_MDATA, sio); + else + TLan_ClearBit(TLAN_NET_SIO_MDATA, sio); + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + (void) TLan_GetBit(TLAN_NET_SIO_MCLK, sio); + } + +} /* TLan_MiiSendData */ + +/*************************************************************** +* TLan_MiiSync +* +* Returns: +* Nothing +* Parms: +* base_port The base IO port of the adapter in +* question. +* +* This functions syncs all PHYs in terms of the MII configuration +* bus. +* +**************************************************************/ + +void TLan_MiiSync(u16 base_port) +{ + int i; + u16 sio; + + outw(TLAN_NET_SIO, base_port + TLAN_DIO_ADR); + sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO; + + TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio); + for (i = 0; i < 32; i++) { + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + } + +} /* TLan_MiiSync */ + +/*************************************************************** +* TLan_MiiWriteReg +* +* Returns: +* Nothing +* Parms: +* dev The device structure for the device +* to write to. +* phy The address of the PHY to be written to. +* reg The register whose contents are to be +* written. +* val The value to be written to the register. +* +* This function uses the TLAN's MII bus to write the contents of a +* given register on a PHY. It sends the appropriate info and then +* writes the 16-bit register value from the MII configuration bus +* via the TLAN SIO register. +* +**************************************************************/ + +void TLan_MiiWriteReg(struct nic *nic __unused, u16 phy, u16 reg, u16 val) +{ + u16 sio; + int minten; + + outw(TLAN_NET_SIO, BASE + TLAN_DIO_ADR); + sio = BASE + TLAN_DIO_DATA + TLAN_NET_SIO; + + TLan_MiiSync(BASE); + + minten = TLan_GetBit(TLAN_NET_SIO_MINTEN, sio); + if (minten) + TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio); + + TLan_MiiSendData(BASE, 0x1, 2); /* Start ( 01b ) */ + TLan_MiiSendData(BASE, 0x1, 2); /* Write ( 01b ) */ + TLan_MiiSendData(BASE, phy, 5); /* Device # */ + TLan_MiiSendData(BASE, reg, 5); /* Register # */ + + TLan_MiiSendData(BASE, 0x2, 2); /* Send ACK */ + TLan_MiiSendData(BASE, val, 16); /* Send Data */ + + TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Idle cycle */ + TLan_SetBit(TLAN_NET_SIO_MCLK, sio); + + if (minten) + TLan_SetBit(TLAN_NET_SIO_MINTEN, sio); + + +} /* TLan_MiiWriteReg */ + +/*************************************************************** +* TLan_SetMac +* +* Returns: +* Nothing +* Parms: +* dev Pointer to device structure of adapter +* on which to change the AREG. +* areg The AREG to set the address in (0 - 3). +* mac A pointer to an array of chars. Each +* element stores one byte of the address. +* IE, it isn't in ascii. +* +* This function transfers a MAC address to one of the +* TLAN AREGs (address registers). The TLAN chip locks +* the register on writing to offset 0 and unlocks the +* register after writing to offset 5. If NULL is passed +* in mac, then the AREG is filled with 0's. +* +**************************************************************/ + +void TLan_SetMac(struct nic *nic __unused, int areg, unsigned char *mac) +{ + int i; + + areg *= 6; + + if (mac != NULL) { + for (i = 0; i < 6; i++) + TLan_DioWrite8(BASE, TLAN_AREG_0 + areg + i, + mac[i]); + } else { + for (i = 0; i < 6; i++) + TLan_DioWrite8(BASE, TLAN_AREG_0 + areg + i, 0); + } + +} /* TLan_SetMac */ + +/********************************************************************* +* TLan_PhyDetect +* +* Returns: +* Nothing +* Parms: +* dev A pointer to the device structure of the adapter +* for which the PHY needs determined. +* +* So far I've found that adapters which have external PHYs +* may also use the internal PHY for part of the functionality. +* (eg, AUI/Thinnet). This function finds out if this TLAN +* chip has an internal PHY, and then finds the first external +* PHY (starting from address 0) if it exists). +* +********************************************************************/ + +void TLan_PhyDetect(struct nic *nic) +{ + u16 control; + u16 hi; + u16 lo; + u32 phy; + + if (tlan_pci_tbl[chip_idx].flags & TLAN_ADAPTER_UNMANAGED_PHY) { + priv->phyNum = 0xFFFF; + return; + } + + TLan_MiiReadReg(nic, TLAN_PHY_MAX_ADDR, MII_GEN_ID_HI, &hi); + + if (hi != 0xFFFF) { + priv->phy[0] = TLAN_PHY_MAX_ADDR; + } else { + priv->phy[0] = TLAN_PHY_NONE; + } + + priv->phy[1] = TLAN_PHY_NONE; + for (phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++) { + TLan_MiiReadReg(nic, phy, MII_GEN_CTL, &control); + TLan_MiiReadReg(nic, phy, MII_GEN_ID_HI, &hi); + TLan_MiiReadReg(nic, phy, MII_GEN_ID_LO, &lo); + if ((control != 0xFFFF) || (hi != 0xFFFF) + || (lo != 0xFFFF)) { + printf("PHY found at %hX %hX %hX %hX\n", + (unsigned int) phy, control, hi, lo); + if ((priv->phy[1] == TLAN_PHY_NONE) + && (phy != TLAN_PHY_MAX_ADDR)) { + priv->phy[1] = phy; + } + } + } + + if (priv->phy[1] != TLAN_PHY_NONE) { + priv->phyNum = 1; + } else if (priv->phy[0] != TLAN_PHY_NONE) { + priv->phyNum = 0; + } else { + printf + ("TLAN: Cannot initialize device, no PHY was found!\n"); + } + +} /* TLan_PhyDetect */ + +void TLan_PhyPowerDown(struct nic *nic) +{ + + u16 value; + DBG ( "%s: Powering down PHY(s).\n", priv->nic_name ); + value = MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE; + TLan_MiiSync(BASE); + TLan_MiiWriteReg(nic, priv->phy[priv->phyNum], MII_GEN_CTL, value); + if ((priv->phyNum == 0) && (priv->phy[1] != TLAN_PHY_NONE) + && + (!(tlan_pci_tbl[chip_idx]. + flags & TLAN_ADAPTER_USE_INTERN_10))) { + TLan_MiiSync(BASE); + TLan_MiiWriteReg(nic, priv->phy[1], MII_GEN_CTL, value); + } + + /* Wait for 50 ms and powerup + * This is abitrary. It is intended to make sure the + * tranceiver settles. + */ + /* TLan_SetTimer( dev, (HZ/20), TLAN_TIMER_PHY_PUP ); */ + mdelay(50); + TLan_PhyPowerUp(nic); + +} /* TLan_PhyPowerDown */ + + +void TLan_PhyPowerUp(struct nic *nic) +{ + u16 value; + + DBG ( "%s: Powering up PHY.\n", priv->nic_name ); + TLan_MiiSync(BASE); + value = MII_GC_LOOPBK; + TLan_MiiWriteReg(nic, priv->phy[priv->phyNum], MII_GEN_CTL, value); + TLan_MiiSync(BASE); + /* Wait for 500 ms and reset the + * tranceiver. The TLAN docs say both 50 ms and + * 500 ms, so do the longer, just in case. + */ + mdelay(500); + TLan_PhyReset(nic); + /* TLan_SetTimer( dev, (HZ/20), TLAN_TIMER_PHY_RESET ); */ + +} /* TLan_PhyPowerUp */ + +void TLan_PhyReset(struct nic *nic) +{ + u16 phy; + u16 value; + + phy = priv->phy[priv->phyNum]; + + DBG ( "%s: Reseting PHY.\n", priv->nic_name ); + TLan_MiiSync(BASE); + value = MII_GC_LOOPBK | MII_GC_RESET; + TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, value); + TLan_MiiReadReg(nic, phy, MII_GEN_CTL, &value); + while (value & MII_GC_RESET) { + TLan_MiiReadReg(nic, phy, MII_GEN_CTL, &value); + } + + /* Wait for 500 ms and initialize. + * I don't remember why I wait this long. + * I've changed this to 50ms, as it seems long enough. + */ + /* TLan_SetTimer( dev, (HZ/20), TLAN_TIMER_PHY_START_LINK ); */ + mdelay(50); + TLan_PhyStartLink(nic); + +} /* TLan_PhyReset */ + + +void TLan_PhyStartLink(struct nic *nic) +{ + + u16 ability; + u16 control; + u16 data; + u16 phy; + u16 status; + u16 tctl; + + phy = priv->phy[priv->phyNum]; + DBG ( "%s: Trying to activate link.\n", priv->nic_name ); + TLan_MiiReadReg(nic, phy, MII_GEN_STS, &status); + TLan_MiiReadReg(nic, phy, MII_GEN_STS, &ability); + + if ((status & MII_GS_AUTONEG) && (!priv->aui)) { + ability = status >> 11; + if (priv->speed == TLAN_SPEED_10 && + priv->duplex == TLAN_DUPLEX_HALF) { + TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x0000); + } else if (priv->speed == TLAN_SPEED_10 && + priv->duplex == TLAN_DUPLEX_FULL) { + priv->tlanFullDuplex = TRUE; + TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x0100); + } else if (priv->speed == TLAN_SPEED_100 && + priv->duplex == TLAN_DUPLEX_HALF) { + TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x2000); + } else if (priv->speed == TLAN_SPEED_100 && + priv->duplex == TLAN_DUPLEX_FULL) { + priv->tlanFullDuplex = TRUE; + TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x2100); + } else { + + /* Set Auto-Neg advertisement */ + TLan_MiiWriteReg(nic, phy, MII_AN_ADV, + (ability << 5) | 1); + /* Enablee Auto-Neg */ + TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x1000); + /* Restart Auto-Neg */ + TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, 0x1200); + /* Wait for 4 sec for autonegotiation + * to complete. The max spec time is less than this + * but the card need additional time to start AN. + * .5 sec should be plenty extra. + */ + DBG ( "TLAN: %s: Starting autonegotiation.\n", + priv->nic_name ); + mdelay(4000); + TLan_PhyFinishAutoNeg(nic); + /* TLan_SetTimer( dev, (2*HZ), TLAN_TIMER_PHY_FINISH_AN ); */ + return; + } + + } + + if ((priv->aui) && (priv->phyNum != 0)) { + priv->phyNum = 0; + data = + TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | + TLAN_NET_CFG_PHY_EN; + TLan_DioWrite16(BASE, TLAN_NET_CONFIG, data); + mdelay(50); + /* TLan_SetTimer( dev, (40*HZ/1000), TLAN_TIMER_PHY_PDOWN ); */ + TLan_PhyPowerDown(nic); + return; + } else if (priv->phyNum == 0) { + control = 0; + TLan_MiiReadReg(nic, phy, TLAN_TLPHY_CTL, &tctl); + if (priv->aui) { + tctl |= TLAN_TC_AUISEL; + } else { + tctl &= ~TLAN_TC_AUISEL; + if (priv->duplex == TLAN_DUPLEX_FULL) { + control |= MII_GC_DUPLEX; + priv->tlanFullDuplex = TRUE; + } + if (priv->speed == TLAN_SPEED_100) { + control |= MII_GC_SPEEDSEL; + } + } + TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, control); + TLan_MiiWriteReg(nic, phy, TLAN_TLPHY_CTL, tctl); + } + + /* Wait for 2 sec to give the tranceiver time + * to establish link. + */ + /* TLan_SetTimer( dev, (4*HZ), TLAN_TIMER_FINISH_RESET ); */ + mdelay(2000); + TLan_FinishReset(nic); + +} /* TLan_PhyStartLink */ + +void TLan_PhyFinishAutoNeg(struct nic *nic) +{ + + u16 an_adv; + u16 an_lpa; + u16 data; + u16 mode; + u16 phy; + u16 status; + + phy = priv->phy[priv->phyNum]; + + TLan_MiiReadReg(nic, phy, MII_GEN_STS, &status); + udelay(1000); + TLan_MiiReadReg(nic, phy, MII_GEN_STS, &status); + + if (!(status & MII_GS_AUTOCMPLT)) { + /* Wait for 8 sec to give the process + * more time. Perhaps we should fail after a while. + */ + if (!priv->neg_be_verbose++) { + printf + ("TLAN: Giving autonegotiation more time.\n"); + printf + ("TLAN: Please check that your adapter has\n"); + printf + ("TLAN: been properly connected to a HUB or Switch.\n"); + printf + ("TLAN: Trying to establish link in the background...\n"); + } + mdelay(8000); + TLan_PhyFinishAutoNeg(nic); + /* TLan_SetTimer( dev, (8*HZ), TLAN_TIMER_PHY_FINISH_AN ); */ + return; + } + + DBG ( "TLAN: %s: Autonegotiation complete.\n", priv->nic_name ); + TLan_MiiReadReg(nic, phy, MII_AN_ADV, &an_adv); + TLan_MiiReadReg(nic, phy, MII_AN_LPA, &an_lpa); + mode = an_adv & an_lpa & 0x03E0; + if (mode & 0x0100) { + printf("Full Duplex\n"); + priv->tlanFullDuplex = TRUE; + } else if (!(mode & 0x0080) && (mode & 0x0040)) { + priv->tlanFullDuplex = TRUE; + printf("Full Duplex\n"); + } + + if ((!(mode & 0x0180)) + && (tlan_pci_tbl[chip_idx].flags & TLAN_ADAPTER_USE_INTERN_10) + && (priv->phyNum != 0)) { + priv->phyNum = 0; + data = + TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | + TLAN_NET_CFG_PHY_EN; + TLan_DioWrite16(BASE, TLAN_NET_CONFIG, data); + /* TLan_SetTimer( nic, (400*HZ/1000), TLAN_TIMER_PHY_PDOWN ); */ + mdelay(400); + TLan_PhyPowerDown(nic); + return; + } + + if (priv->phyNum == 0) { + if ((priv->duplex == TLAN_DUPLEX_FULL) + || (an_adv & an_lpa & 0x0040)) { + TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, + MII_GC_AUTOENB | MII_GC_DUPLEX); + DBG + ( "TLAN: Starting internal PHY with FULL-DUPLEX\n" ); + } else { + TLan_MiiWriteReg(nic, phy, MII_GEN_CTL, + MII_GC_AUTOENB); + DBG + ( "TLAN: Starting internal PHY with HALF-DUPLEX\n" ); + } + } + + /* Wait for 100 ms. No reason in partiticular. + */ + /* TLan_SetTimer( dev, (HZ/10), TLAN_TIMER_FINISH_RESET ); */ + mdelay(100); + TLan_FinishReset(nic); + +} /* TLan_PhyFinishAutoNeg */ + +#ifdef MONITOR + +/********************************************************************* +* +* TLan_phyMonitor +* +* Returns: +* None +* +* Params: +* dev The device structure of this device. +* +* +* This function monitors PHY condition by reading the status +* register via the MII bus. This can be used to give info +* about link changes (up/down), and possible switch to alternate +* media. +* +********************************************************************/ + +void TLan_PhyMonitor(struct net_device *dev) +{ + TLanPrivateInfo *priv = dev->priv; + u16 phy; + u16 phy_status; + + phy = priv->phy[priv->phyNum]; + + /* Get PHY status register */ + TLan_MiiReadReg(nic, phy, MII_GEN_STS, &phy_status); + + /* Check if link has been lost */ + if (!(phy_status & MII_GS_LINK)) { + if (priv->link) { + priv->link = 0; + printf("TLAN: %s has lost link\n", priv->nic_name); + priv->flags &= ~IFF_RUNNING; + mdelay(2000); + TLan_PhyMonitor(nic); + /* TLan_SetTimer( dev, (2*HZ), TLAN_TIMER_LINK_BEAT ); */ + return; + } + } + + /* Link restablished? */ + if ((phy_status & MII_GS_LINK) && !priv->link) { + priv->link = 1; + printf("TLAN: %s has reestablished link\n", + priv->nic_name); + priv->flags |= IFF_RUNNING; + } + + /* Setup a new monitor */ + /* TLan_SetTimer( dev, (2*HZ), TLAN_TIMER_LINK_BEAT ); */ + mdelay(2000); + TLan_PhyMonitor(nic); +} + +#endif /* MONITOR */ + +static struct pci_device_id tlan_nics[] = { + PCI_ROM(0x0e11, 0xae34, "netel10", "Compaq Netelligent 10 T PCI UTP"), + PCI_ROM(0x0e11, 0xae32, "netel100","Compaq Netelligent 10/100 TX PCI UTP"), + PCI_ROM(0x0e11, 0xae35, "netflex3i", "Compaq Integrated NetFlex-3/P"), + PCI_ROM(0x0e11, 0xf130, "thunder", "Compaq NetFlex-3/P"), + PCI_ROM(0x0e11, 0xf150, "netflex3b", "Compaq NetFlex-3/P"), + PCI_ROM(0x0e11, 0xae43, "netel100pi", "Compaq Netelligent Integrated 10/100 TX UTP"), + PCI_ROM(0x0e11, 0xae40, "netel100d", "Compaq Netelligent Dual 10/100 TX PCI UTP"), + PCI_ROM(0x0e11, 0xb011, "netel100i", "Compaq Netelligent 10/100 TX Embedded UTP"), + PCI_ROM(0x108d, 0x0013, "oc2183", "Olicom OC-2183/2185"), + PCI_ROM(0x108d, 0x0012, "oc2325", "Olicom OC-2325"), + PCI_ROM(0x108d, 0x0014, "oc2326", "Olicom OC-2326"), + PCI_ROM(0x0e11, 0xb030, "netelligent_10_100_ws_5100", "Compaq Netelligent 10/100 TX UTP"), + PCI_ROM(0x0e11, 0xb012, "netelligent_10_t2", "Compaq Netelligent 10 T/2 PCI UTP/Coax"), +}; + +PCI_DRIVER ( tlan_driver, tlan_nics, PCI_NO_CLASS ); + +DRIVER ( "TLAN/PCI", nic_driver, pci_driver, tlan_driver, + tlan_probe, tlan_disable ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/tlan.h b/gpxe/src/drivers/net/tlan.h new file mode 100644 index 0000000..de57d59 --- /dev/null +++ b/gpxe/src/drivers/net/tlan.h @@ -0,0 +1,524 @@ +/************************************************************************** +* +* tlan.c -- Etherboot device driver for the Texas Instruments ThunderLAN +* Written 2003-2003 by Timothy Legge +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Portions of this code (almost all) based on: +* tlan.c: Linux ThunderLan Driver: +* +* by James Banks +* +* (C) 1997-1998 Caldera, Inc. +* (C) 1998 James Banks +* (C) 1999-2001 Torben Mathiasen +* (C) 2002 Samuel Chessman +* +* REVISION HISTORY: +* ================ +* v1.0 07-08-2003 timlegge Initial not quite working version +* +* Indent Style: indent -kr -i8 +***************************************************************************/ + +/***************************************************************** +* TLan Definitions +* +****************************************************************/ + +#define FALSE 0 +#define TRUE 1 + +#define TLAN_MIN_FRAME_SIZE 64 +#define TLAN_MAX_FRAME_SIZE 1600 + +#define TLAN_NUM_RX_LISTS 4 +#define TLAN_NUM_TX_LISTS 2 + +#define TLAN_IGNORE 0 +#define TLAN_RECORD 1 +/* +#define TLAN_DBG(lvl, format, args...) if (debug&lvl) printf("TLAN: " format, ##args ); +*/ +#define TLAN_DEBUG_GNRL 0x0001 +#define TLAN_DEBUG_TX 0x0002 +#define TLAN_DEBUG_RX 0x0004 +#define TLAN_DEBUG_LIST 0x0008 +#define TLAN_DEBUG_PROBE 0x0010 + +#define TX_TIMEOUT (10*HZ) /* We need time for auto-neg */ +#define MAX_TLAN_BOARDS 8 /* Max number of boards installed at a time */ + + + /***************************************************************** + * Device Identification Definitions + * + ****************************************************************/ + +#define PCI_DEVICE_ID_NETELLIGENT_10_T2 0xB012 +#define PCI_DEVICE_ID_NETELLIGENT_10_100_WS_5100 0xB030 +#ifndef PCI_DEVICE_ID_OLICOM_OC2183 +#define PCI_DEVICE_ID_OLICOM_OC2183 0x0013 +#endif +#ifndef PCI_DEVICE_ID_OLICOM_OC2325 +#define PCI_DEVICE_ID_OLICOM_OC2325 0x0012 +#endif +#ifndef PCI_DEVICE_ID_OLICOM_OC2326 +#define PCI_DEVICE_ID_OLICOM_OC2326 0x0014 +#endif + +typedef struct tlan_adapter_entry { + u16 vendorId; + u16 deviceId; + char *deviceLabel; + u32 flags; + u16 addrOfs; +} TLanAdapterEntry; + +#define TLAN_ADAPTER_NONE 0x00000000 +#define TLAN_ADAPTER_UNMANAGED_PHY 0x00000001 +#define TLAN_ADAPTER_BIT_RATE_PHY 0x00000002 +#define TLAN_ADAPTER_USE_INTERN_10 0x00000004 +#define TLAN_ADAPTER_ACTIVITY_LED 0x00000008 + +#define TLAN_SPEED_DEFAULT 0 +#define TLAN_SPEED_10 10 +#define TLAN_SPEED_100 100 + +#define TLAN_DUPLEX_DEFAULT 0 +#define TLAN_DUPLEX_HALF 1 +#define TLAN_DUPLEX_FULL 2 + + + + /***************************************************************** + * EISA Definitions + * + ****************************************************************/ + +#define EISA_ID 0xc80 /* EISA ID Registers */ +#define EISA_ID0 0xc80 /* EISA ID Register 0 */ +#define EISA_ID1 0xc81 /* EISA ID Register 1 */ +#define EISA_ID2 0xc82 /* EISA ID Register 2 */ +#define EISA_ID3 0xc83 /* EISA ID Register 3 */ +#define EISA_CR 0xc84 /* EISA Control Register */ +#define EISA_REG0 0xc88 /* EISA Configuration Register 0 */ +#define EISA_REG1 0xc89 /* EISA Configuration Register 1 */ +#define EISA_REG2 0xc8a /* EISA Configuration Register 2 */ +#define EISA_REG3 0xc8f /* EISA Configuration Register 3 */ +#define EISA_APROM 0xc90 /* Ethernet Address PROM */ + + + + /***************************************************************** + * Rx/Tx List Definitions + * + ****************************************************************/ + +#define TLAN_BUFFERS_PER_LIST 10 +#define TLAN_LAST_BUFFER 0x80000000 +#define TLAN_CSTAT_UNUSED 0x8000 +#define TLAN_CSTAT_FRM_CMP 0x4000 +#define TLAN_CSTAT_READY 0x3000 +#define TLAN_CSTAT_EOC 0x0800 +#define TLAN_CSTAT_RX_ERROR 0x0400 +#define TLAN_CSTAT_PASS_CRC 0x0200 +#define TLAN_CSTAT_DP_PR 0x0100 + + + + + + + /***************************************************************** + * PHY definitions + * + ****************************************************************/ + +#define TLAN_PHY_MAX_ADDR 0x1F +#define TLAN_PHY_NONE 0x20 + + + + /***************************************************************** + * TLan Driver Timer Definitions + * + ****************************************************************/ + +#define TLAN_TIMER_LINK_BEAT 1 +#define TLAN_TIMER_ACTIVITY 2 +#define TLAN_TIMER_PHY_PDOWN 3 +#define TLAN_TIMER_PHY_PUP 4 +#define TLAN_TIMER_PHY_RESET 5 +#define TLAN_TIMER_PHY_START_LINK 6 +#define TLAN_TIMER_PHY_FINISH_AN 7 +#define TLAN_TIMER_FINISH_RESET 8 + +#define TLAN_TIMER_ACT_DELAY (HZ/10) + + + + + /***************************************************************** + * TLan Driver Eeprom Definitions + * + ****************************************************************/ + +#define TLAN_EEPROM_ACK 0 +#define TLAN_EEPROM_STOP 1 + + + + + /***************************************************************** + * Host Register Offsets and Contents + * + ****************************************************************/ + +#define TLAN_HOST_CMD 0x00 +#define TLAN_HC_GO 0x80000000 +#define TLAN_HC_STOP 0x40000000 +#define TLAN_HC_ACK 0x20000000 +#define TLAN_HC_CS_MASK 0x1FE00000 +#define TLAN_HC_EOC 0x00100000 +#define TLAN_HC_RT 0x00080000 +#define TLAN_HC_NES 0x00040000 +#define TLAN_HC_AD_RST 0x00008000 +#define TLAN_HC_LD_TMR 0x00004000 +#define TLAN_HC_LD_THR 0x00002000 +#define TLAN_HC_REQ_INT 0x00001000 +#define TLAN_HC_INT_OFF 0x00000800 +#define TLAN_HC_INT_ON 0x00000400 +#define TLAN_HC_AC_MASK 0x000000FF +#define TLAN_CH_PARM 0x04 +#define TLAN_DIO_ADR 0x08 +#define TLAN_DA_ADR_INC 0x8000 +#define TLAN_DA_RAM_ADR 0x4000 +#define TLAN_HOST_INT 0x0A +#define TLAN_HI_IV_MASK 0x1FE0 +#define TLAN_HI_IT_MASK 0x001C +#define TLAN_DIO_DATA 0x0C + + +/* ThunderLAN Internal Register DIO Offsets */ + +#define TLAN_NET_CMD 0x00 +#define TLAN_NET_CMD_NRESET 0x80 +#define TLAN_NET_CMD_NWRAP 0x40 +#define TLAN_NET_CMD_CSF 0x20 +#define TLAN_NET_CMD_CAF 0x10 +#define TLAN_NET_CMD_NOBRX 0x08 +#define TLAN_NET_CMD_DUPLEX 0x04 +#define TLAN_NET_CMD_TRFRAM 0x02 +#define TLAN_NET_CMD_TXPACE 0x01 +#define TLAN_NET_SIO 0x01 +#define TLAN_NET_SIO_MINTEN 0x80 +#define TLAN_NET_SIO_ECLOK 0x40 +#define TLAN_NET_SIO_ETXEN 0x20 +#define TLAN_NET_SIO_EDATA 0x10 +#define TLAN_NET_SIO_NMRST 0x08 +#define TLAN_NET_SIO_MCLK 0x04 +#define TLAN_NET_SIO_MTXEN 0x02 +#define TLAN_NET_SIO_MDATA 0x01 +#define TLAN_NET_STS 0x02 +#define TLAN_NET_STS_MIRQ 0x80 +#define TLAN_NET_STS_HBEAT 0x40 +#define TLAN_NET_STS_TXSTOP 0x20 +#define TLAN_NET_STS_RXSTOP 0x10 +#define TLAN_NET_STS_RSRVD 0x0F +#define TLAN_NET_MASK 0x03 +#define TLAN_NET_MASK_MASK7 0x80 +#define TLAN_NET_MASK_MASK6 0x40 +#define TLAN_NET_MASK_MASK5 0x20 +#define TLAN_NET_MASK_MASK4 0x10 +#define TLAN_NET_MASK_RSRVD 0x0F +#define TLAN_NET_CONFIG 0x04 +#define TLAN_NET_CFG_RCLK 0x8000 +#define TLAN_NET_CFG_TCLK 0x4000 +#define TLAN_NET_CFG_BIT 0x2000 +#define TLAN_NET_CFG_RXCRC 0x1000 +#define TLAN_NET_CFG_PEF 0x0800 +#define TLAN_NET_CFG_1FRAG 0x0400 +#define TLAN_NET_CFG_1CHAN 0x0200 +#define TLAN_NET_CFG_MTEST 0x0100 +#define TLAN_NET_CFG_PHY_EN 0x0080 +#define TLAN_NET_CFG_MSMASK 0x007F +#define TLAN_MAN_TEST 0x06 +#define TLAN_DEF_VENDOR_ID 0x08 +#define TLAN_DEF_DEVICE_ID 0x0A +#define TLAN_DEF_REVISION 0x0C +#define TLAN_DEF_SUBCLASS 0x0D +#define TLAN_DEF_MIN_LAT 0x0E +#define TLAN_DEF_MAX_LAT 0x0F +#define TLAN_AREG_0 0x10 +#define TLAN_AREG_1 0x16 +#define TLAN_AREG_2 0x1C +#define TLAN_AREG_3 0x22 +#define TLAN_HASH_1 0x28 +#define TLAN_HASH_2 0x2C +#define TLAN_GOOD_TX_FRMS 0x30 +#define TLAN_TX_UNDERUNS 0x33 +#define TLAN_GOOD_RX_FRMS 0x34 +#define TLAN_RX_OVERRUNS 0x37 +#define TLAN_DEFERRED_TX 0x38 +#define TLAN_CRC_ERRORS 0x3A +#define TLAN_CODE_ERRORS 0x3B +#define TLAN_MULTICOL_FRMS 0x3C +#define TLAN_SINGLECOL_FRMS 0x3E +#define TLAN_EXCESSCOL_FRMS 0x40 +#define TLAN_LATE_COLS 0x41 +#define TLAN_CARRIER_LOSS 0x42 +#define TLAN_ACOMMIT 0x43 +#define TLAN_LED_REG 0x44 +#define TLAN_LED_ACT 0x10 +#define TLAN_LED_LINK 0x01 +#define TLAN_BSIZE_REG 0x45 +#define TLAN_MAX_RX 0x46 +#define TLAN_INT_DIS 0x48 +#define TLAN_ID_TX_EOC 0x04 +#define TLAN_ID_RX_EOF 0x02 +#define TLAN_ID_RX_EOC 0x01 + + + +/* ThunderLAN Interrupt Codes */ + +#define TLAN_INT_NUMBER_OF_INTS 8 + +#define TLAN_INT_NONE 0x0000 +#define TLAN_INT_TX_EOF 0x0001 +#define TLAN_INT_STAT_OVERFLOW 0x0002 +#define TLAN_INT_RX_EOF 0x0003 +#define TLAN_INT_DUMMY 0x0004 +#define TLAN_INT_TX_EOC 0x0005 +#define TLAN_INT_STATUS_CHECK 0x0006 +#define TLAN_INT_RX_EOC 0x0007 + + + +/* ThunderLAN MII Registers */ + +/* Generic MII/PHY Registers */ + +#define MII_GEN_CTL 0x00 +#define MII_GC_RESET 0x8000 +#define MII_GC_LOOPBK 0x4000 +#define MII_GC_SPEEDSEL 0x2000 +#define MII_GC_AUTOENB 0x1000 +#define MII_GC_PDOWN 0x0800 +#define MII_GC_ISOLATE 0x0400 +#define MII_GC_AUTORSRT 0x0200 +#define MII_GC_DUPLEX 0x0100 +#define MII_GC_COLTEST 0x0080 +#define MII_GC_RESERVED 0x007F +#define MII_GEN_STS 0x01 +#define MII_GS_100BT4 0x8000 +#define MII_GS_100BTXFD 0x4000 +#define MII_GS_100BTXHD 0x2000 +#define MII_GS_10BTFD 0x1000 +#define MII_GS_10BTHD 0x0800 +#define MII_GS_RESERVED 0x07C0 +#define MII_GS_AUTOCMPLT 0x0020 +#define MII_GS_RFLT 0x0010 +#define MII_GS_AUTONEG 0x0008 +#define MII_GS_LINK 0x0004 +#define MII_GS_JABBER 0x0002 +#define MII_GS_EXTCAP 0x0001 +#define MII_GEN_ID_HI 0x02 +#define MII_GEN_ID_LO 0x03 +#define MII_GIL_OUI 0xFC00 +#define MII_GIL_MODEL 0x03F0 +#define MII_GIL_REVISION 0x000F +#define MII_AN_ADV 0x04 +#define MII_AN_LPA 0x05 +#define MII_AN_EXP 0x06 + +/* ThunderLAN Specific MII/PHY Registers */ + +#define TLAN_TLPHY_ID 0x10 +#define TLAN_TLPHY_CTL 0x11 +#define TLAN_TC_IGLINK 0x8000 +#define TLAN_TC_SWAPOL 0x4000 +#define TLAN_TC_AUISEL 0x2000 +#define TLAN_TC_SQEEN 0x1000 +#define TLAN_TC_MTEST 0x0800 +#define TLAN_TC_RESERVED 0x07F8 +#define TLAN_TC_NFEW 0x0004 +#define TLAN_TC_INTEN 0x0002 +#define TLAN_TC_TINT 0x0001 +#define TLAN_TLPHY_STS 0x12 +#define TLAN_TS_MINT 0x8000 +#define TLAN_TS_PHOK 0x4000 +#define TLAN_TS_POLOK 0x2000 +#define TLAN_TS_TPENERGY 0x1000 +#define TLAN_TS_RESERVED 0x0FFF +#define TLAN_TLPHY_PAR 0x19 +#define TLAN_PHY_CIM_STAT 0x0020 +#define TLAN_PHY_SPEED_100 0x0040 +#define TLAN_PHY_DUPLEX_FULL 0x0080 +#define TLAN_PHY_AN_EN_STAT 0x0400 + +/* National Sem. & Level1 PHY id's */ +#define NAT_SEM_ID1 0x2000 +#define NAT_SEM_ID2 0x5C01 +#define LEVEL1_ID1 0x7810 +#define LEVEL1_ID2 0x0000 + +#define CIRC_INC( a, b ) if ( ++a >= b ) a = 0 + +/* Routines to access internal registers. */ + +static inline u8 TLan_DioRead8(u16 base_addr, u16 internal_addr) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + return (inb((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x3))); + +} /* TLan_DioRead8 */ + + + + +static inline u16 TLan_DioRead16(u16 base_addr, u16 internal_addr) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + return (inw((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x2))); + +} /* TLan_DioRead16 */ + + + + +static inline u32 TLan_DioRead32(u16 base_addr, u16 internal_addr) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + return (inl(base_addr + TLAN_DIO_DATA)); + +} /* TLan_DioRead32 */ + + + + +static inline void TLan_DioWrite8(u16 base_addr, u16 internal_addr, u8 data) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + outb(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x3)); + +} + + + + +static inline void TLan_DioWrite16(u16 base_addr, u16 internal_addr, u16 data) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + outw(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2)); + +} + + + + +static inline void TLan_DioWrite32(u16 base_addr, u16 internal_addr, u32 data) +{ + outw(internal_addr, base_addr + TLAN_DIO_ADR); + outl(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2)); + +} + + + +#if 0 +static inline void TLan_ClearBit(u8 bit, u16 port) +{ + outb_p(inb_p(port) & ~bit, port); +} + + + + +static inline int TLan_GetBit(u8 bit, u16 port) +{ + return ((int) (inb_p(port) & bit)); +} + + + + +static inline void TLan_SetBit(u8 bit, u16 port) +{ + outb_p(inb_p(port) | bit, port); +} +#endif + +#define TLan_ClearBit( bit, port ) outb_p(inb_p(port) & ~bit, port) +#define TLan_GetBit( bit, port ) ((int) (inb_p(port) & bit)) +#define TLan_SetBit( bit, port ) outb_p(inb_p(port) | bit, port) + +#ifdef I_LIKE_A_FAST_HASH_FUNCTION +/* given 6 bytes, view them as 8 6-bit numbers and return the XOR of those */ +/* the code below is about seven times as fast as the original code */ +static inline u32 TLan_HashFunc(u8 * a) +{ + u8 hash; + + hash = (a[0] ^ a[3]); /* & 077 */ + hash ^= ((a[0] ^ a[3]) >> 6); /* & 003 */ + hash ^= ((a[1] ^ a[4]) << 2); /* & 074 */ + hash ^= ((a[1] ^ a[4]) >> 4); /* & 017 */ + hash ^= ((a[2] ^ a[5]) << 4); /* & 060 */ + hash ^= ((a[2] ^ a[5]) >> 2); /* & 077 */ + + return (hash & 077); +} + +#else /* original code */ + +static inline u32 xor(u32 a, u32 b) +{ + return ((a && !b) || (!a && b)); +} + +#define XOR8( a, b, c, d, e, f, g, h ) xor( a, xor( b, xor( c, xor( d, xor( e, xor( f, xor( g, h ) ) ) ) ) ) ) +#define DA( a, bit ) ( ( (u8) a[bit/8] ) & ( (u8) ( 1 << bit%8 ) ) ) + +static inline u32 TLan_HashFunc(u8 * a) +{ + u32 hash; + + hash = + XOR8(DA(a, 0), DA(a, 6), DA(a, 12), DA(a, 18), DA(a, 24), + DA(a, 30), DA(a, 36), DA(a, 42)); + hash |= + XOR8(DA(a, 1), DA(a, 7), DA(a, 13), DA(a, 19), DA(a, 25), + DA(a, 31), DA(a, 37), DA(a, 43)) << 1; + hash |= + XOR8(DA(a, 2), DA(a, 8), DA(a, 14), DA(a, 20), DA(a, 26), + DA(a, 32), DA(a, 38), DA(a, 44)) << 2; + hash |= + XOR8(DA(a, 3), DA(a, 9), DA(a, 15), DA(a, 21), DA(a, 27), + DA(a, 33), DA(a, 39), DA(a, 45)) << 3; + hash |= + XOR8(DA(a, 4), DA(a, 10), DA(a, 16), DA(a, 22), DA(a, 28), + DA(a, 34), DA(a, 40), DA(a, 46)) << 4; + hash |= + XOR8(DA(a, 5), DA(a, 11), DA(a, 17), DA(a, 23), DA(a, 29), + DA(a, 35), DA(a, 41), DA(a, 47)) << 5; + + return hash; + +} + +#endif /* I_LIKE_A_FAST_HASH_FUNCTION */ diff --git a/gpxe/src/drivers/net/tulip.c b/gpxe/src/drivers/net/tulip.c new file mode 100644 index 0000000..afd7d45 --- /dev/null +++ b/gpxe/src/drivers/net/tulip.c @@ -0,0 +1,2097 @@ +/* -*- Mode:C; c-basic-offset:4; -*- */ + +/* + Tulip and clone Etherboot Driver + + By Marty Connor (mdc@etherboot.org) + Copyright (C) 2001 Entity Cyber, Inc. + + This software may be used and distributed according to the terms + of the GNU Public License, incorporated herein by reference. + + As of April 2001 this driver should support most tulip cards that + the Linux tulip driver supports because Donald Becker's Linux media + detection code is now included. + + Based on Ken Yap's Tulip Etherboot Driver and Donald Becker's + Linux Tulip Driver. Supports N-Way speed auto-configuration on + MX98715, MX98715A and MX98725. Support inexpensive PCI 10/100 cards + based on the Macronix MX987x5 chip, such as the SOHOware Fast + model SFA110A, and the LinkSYS model LNE100TX. The NetGear + model FA310X, based on the LC82C168 chip is supported. + The TRENDnet TE100-PCIA NIC which uses a genuine Intel 21143-PD + chipset is supported. Also, Davicom DM9102's. + + Documentation and source code used: + Source for Etherboot driver at + http://etherboot.sourceforge.net/ + MX98715A Data Sheet and MX98715A Application Note + on http://www.macronix.com/ (PDF format files) + Source for Linux tulip driver at + http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html + + Adapted by Ken Yap from + FreeBSD netboot DEC 21143 driver + Author: David Sharp + date: Nov/98 + + Some code fragments were taken from verious places, Ken Yap's + etherboot, FreeBSD's if_de.c, and various Linux related files. + DEC's manuals for the 21143 and SROM format were very helpful. + The Linux de driver development page has a number of links to + useful related information. Have a look at: + ftp://cesdis.gsfc.nasa.gov/pub/linux/drivers/tulip-devel.html +*/ + +/*********************************************************************/ +/* Revision History */ +/*********************************************************************/ + +/* + 08 Feb 2005 Ramesh Chander chhabaramesh at yahoo.co.in added table entries + for SGThomson STE10/100A + 07 Sep 2003 timlegge Multicast Support Added + 11 Apr 2001 mdc [patch to etherboot 4.7.24] + Major rewrite to include Linux tulip driver media detection + code. This driver should support a lot more cards now. + 16 Jul 2000 mdc 0.75b11 + Added support for ADMtek 0985 Centaur-P, a "Comet" tulip clone + which is used on the LinkSYS LNE100TX v4.x cards. We already + support LNE100TX v2.0 cards, which use a different controller. + 04 Jul 2000 jam ? + Added test of status after receiving a packet from the card. + Also uncommented the tulip_disable routine. Stray packets + seemed to be causing problems. + 27 Apr 2000 njl ? + 29 Feb 2000 mdc 0.75b7 + Increased reset delay to 3 seconds because Macronix cards seem to + need more reset time before card comes back to a usable state. + 26 Feb 2000 mdc 0.75b6 + Added a 1 second delay after initializing the transmitter because + some cards seem to need the time or they drop the first packet + transmitted. + 23 Feb 2000 mdc 0.75b5 + removed udelay code and used currticks() for more reliable delay + code in reset pause and sanity timeouts. Added function prototypes + and TX debugging code. + 21 Feb 2000 mdc patch to Etherboot 4.4.3 + Incorporated patches from Bob Edwards and Paul Mackerras of + Linuxcare's OZLabs to deal with inefficiencies in tulip_transmit + and udelay. We now wait for packet transmission to complete + (or sanity timeout). + 04 Feb 2000 Robert.Edwards@anu.edu.au patch to Etherboot 4.4.2 + patch to tulip.c that implements the automatic selection of the MII + interface on cards using the Intel/DEC 21143 reference design, in + particular, the TRENDnet TE100-PCIA NIC which uses a genuine Intel + 21143-PD chipset. + 11 Jan 2000 mdc 0.75b4 + Added support for NetGear FA310TX card based on the LC82C168 + chip. This should also support Lite-On LC82C168 boards. + Added simple MII support. Re-arranged code to better modularize + initializations. + 04 Dec 1999 mdc 0.75b3 + Added preliminary support for LNE100TX PCI cards. Should work for + PNIC2 cards. No MII support, but single interface (RJ45) tulip + cards seem to not care. + 03 Dec 1999 mdc 0.75b2 + Renamed from mx987x5 to tulip, merged in original tulip init code + from tulip.c to support other tulip compatible cards. + 02 Dec 1999 mdc 0.75b1 + Released Beta MX987x5 Driver for code review and testing to netboot + and thinguin mailing lists. +*/ + + +/*********************************************************************/ +/* Declarations */ +/*********************************************************************/ + +#include "etherboot.h" +#include "nic.h" + +#include +#include + +/* User settable parameters */ + +#undef TULIP_DEBUG +#undef TULIP_DEBUG_WHERE +#ifdef TULIP_DEBUG +static int tulip_debug = 2; /* 1 normal messages, 0 quiet .. 7 verbose. */ +#endif + +#define TX_TIME_OUT 2*TICKS_PER_SEC + +/* helpful macros if on a big_endian machine for changing byte order. + not strictly needed on Intel */ +#define get_unaligned(ptr) (*(ptr)) +#define put_unaligned(val, ptr) ((void)( *(ptr) = (val) )) +#define get_u16(ptr) (*(u16 *)(ptr)) +#define virt_to_le32desc(addr) virt_to_bus(addr) + +#define TULIP_IOTYPE PCI_USES_MASTER | PCI_USES_IO | PCI_ADDR0 +#define TULIP_SIZE 0x80 + +/* This is a mysterious value that can be written to CSR11 in the 21040 (only) + to support a pre-NWay full-duplex signaling mechanism using short frames. + No one knows what it should be, but if left at its default value some + 10base2(!) packets trigger a full-duplex-request interrupt. */ +#define FULL_DUPLEX_MAGIC 0x6969 + +static const int csr0 = 0x01A00000 | 0x8000; + +/* The possible media types that can be set in options[] are: */ +#define MEDIA_MASK 31 +static const char * const medianame[32] = { + "10baseT", "10base2", "AUI", "100baseTx", + "10baseT-FDX", "100baseTx-FDX", "100baseT4", "100baseFx", + "100baseFx-FDX", "MII 10baseT", "MII 10baseT-FDX", "MII", + "10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FDX", "MII 100baseT4", + "MII 100baseFx-HDX", "MII 100baseFx-FDX", "Home-PNA 1Mbps", "Invalid-19", +}; + +/* This much match tulip_tbl[]! Note 21142 == 21143. */ +enum tulip_chips { + DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3, + LC82C168, MX98713, MX98715, MX98725, AX88141, AX88140, PNIC2, COMET, + COMPEX9881, I21145, XIRCOM, SGThomson, /*Ramesh Chander*/ +}; + +enum pci_id_flags_bits { + /* Set PCI command register bits before calling probe1(). */ + PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, + /* Read and map the single following PCI BAR. */ + PCI_ADDR0=0<<4, PCI_ADDR1=1<<4, PCI_ADDR2=2<<4, PCI_ADDR3=3<<4, + PCI_ADDR_64BITS=0x100, PCI_NO_ACPI_WAKE=0x200, PCI_NO_MIN_LATENCY=0x400, + PCI_UNUSED_IRQ=0x800, +}; + +struct pci_id_info { + char *name; + struct match_info { + u32 pci, pci_mask, subsystem, subsystem_mask; + u32 revision, revision_mask; /* Only 8 bits. */ + } id; + enum pci_id_flags_bits pci_flags; + int io_size; /* Needed for I/O region check or ioremap(). */ + int drv_flags; /* Driver use, intended as capability flags. */ +}; + +static const struct pci_id_info pci_id_tbl[] = { + { "Digital DC21040 Tulip", { 0x00021011, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, 0x80, DC21040 }, + { "Digital DC21041 Tulip", { 0x00141011, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, 0x80, DC21041 }, + { "Digital DS21140A Tulip", { 0x00091011, 0xffffffff, 0,0, 0x20,0xf0 }, + TULIP_IOTYPE, 0x80, DC21140 }, + { "Digital DS21140 Tulip", { 0x00091011, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, 0x80, DC21140 }, + { "Digital DS21143 Tulip", { 0x00191011, 0xffffffff, 0,0, 65,0xff }, + TULIP_IOTYPE, TULIP_SIZE, DC21142 }, + { "Digital DS21142 Tulip", { 0x00191011, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, TULIP_SIZE, DC21142 }, + { "Kingston KNE110tx (PNIC)", { 0x000211AD, 0xffffffff, 0xf0022646, 0xffffffff, 0, 0 }, + TULIP_IOTYPE, 256, LC82C168 }, + { "Lite-On 82c168 PNIC", { 0x000211AD, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, 256, LC82C168 }, + { "Macronix 98713 PMAC", { 0x051210d9, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, 256, MX98713 }, + { "Macronix 98715 PMAC", { 0x053110d9, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, 256, MX98715 }, + { "Macronix 98725 PMAC", { 0x053110d9, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, 256, MX98725 }, + { "ASIX AX88141", { 0x1400125B, 0xffffffff, 0,0, 0x10, 0xf0 }, + TULIP_IOTYPE, 128, AX88141 }, + { "ASIX AX88140", { 0x1400125B, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, 128, AX88140 }, + { "Lite-On LC82C115 PNIC-II", { 0xc11511AD, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, 256, PNIC2 }, + { "ADMtek AN981 Comet", { 0x09811317, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, 256, COMET }, + { "ADMTek AN983 Comet", { 0x12161113, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, 256, COMET }, + { "ADMTek Comet AN983b", { 0x95111317, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, 256, COMET }, + { "ADMtek Centaur-P", { 0x09851317, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, 256, COMET }, + { "ADMtek Centaur-C", { 0x19851317, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, 256, COMET }, + { "Compex RL100-TX", { 0x988111F6, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, 128, COMPEX9881 }, + { "Intel 21145 Tulip", { 0x00398086, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, 128, I21145 }, + { "Xircom Tulip clone", { 0x0003115d, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, 128, XIRCOM }, + { "Davicom DM9102", { 0x91021282, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, 0x80, DC21140 }, + { "Davicom DM9100", { 0x91001282, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, 0x80, DC21140 }, + { "Macronix mxic-98715 (EN1217)", { 0x12171113, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, 256, MX98715 }, + { "3Com 3cSOHO100B-TX (ADMtek Centuar)", { 0x930010b7, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, TULIP_SIZE, COMET }, + { "SG Thomson STE10/100A", { 0x2774104a, 0xffffffff, 0, 0, 0, 0 }, + TULIP_IOTYPE, 256, COMET }, /*Ramesh Chander*/ + { 0, { 0, 0, 0, 0, 0, 0 }, 0, 0, 0 }, +}; + +enum tbl_flag { + HAS_MII=1, HAS_MEDIA_TABLE=2, CSR12_IN_SROM=4, ALWAYS_CHECK_MII=8, + HAS_PWRDWN=0x10, MC_HASH_ONLY=0x20, /* Hash-only multicast filter. */ + HAS_PNICNWAY=0x80, HAS_NWAY=0x40, /* Uses internal NWay xcvr. */ + HAS_INTR_MITIGATION=0x100, IS_ASIX=0x200, HAS_8023X=0x400, +}; + +/* Note: this table must match enum tulip_chips above. */ +static struct tulip_chip_table { + char *chip_name; + int flags; +} tulip_tbl[] = { + { "Digital DC21040 Tulip", 0}, + { "Digital DC21041 Tulip", HAS_MEDIA_TABLE | HAS_NWAY }, + { "Digital DS21140 Tulip", HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM }, + { "Digital DS21143 Tulip", HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII + | HAS_PWRDWN | HAS_NWAY | HAS_INTR_MITIGATION }, + { "Lite-On 82c168 PNIC", HAS_MII | HAS_PNICNWAY }, + { "Macronix 98713 PMAC", HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM }, + { "Macronix 98715 PMAC", HAS_MEDIA_TABLE }, + { "Macronix 98725 PMAC", HAS_MEDIA_TABLE }, + { "ASIX AX88140", HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM + | MC_HASH_ONLY | IS_ASIX }, + { "ASIX AX88141", HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM | MC_HASH_ONLY + | IS_ASIX }, + { "Lite-On PNIC-II", HAS_MII | HAS_NWAY | HAS_8023X }, + { "ADMtek Comet", HAS_MII | MC_HASH_ONLY }, + { "Compex 9881 PMAC", HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM }, + { "Intel DS21145 Tulip", HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII + | HAS_PWRDWN | HAS_NWAY }, + { "Xircom tulip work-alike", HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII + | HAS_PWRDWN | HAS_NWAY }, + { "SGThomson STE10/100A", HAS_MII | MC_HASH_ONLY }, /*Ramesh Chander*/ + { 0, 0 }, +}; + +/* A full-duplex map for media types. */ +enum MediaIs { + MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8, + MediaIs100=16}; + +static const char media_cap[32] = +{0,0,0,16, 3,19,16,24, 27,4,7,5, 0,20,23,20, 20,31,0,0, }; +static u8 t21040_csr13[] = {2,0x0C,8,4, 4,0,0,0, 0,0,0,0, 4,0,0,0}; + +/* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD */ +static u16 t21041_csr13[] = { 0xEF01, 0xEF09, 0xEF09, 0xEF01, 0xEF09, }; +static u16 t21041_csr14[] = { 0xFFFF, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, }; +static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; + +/* not used +static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, }; +*/ +static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x0000, 0x7F3D, }; +/* not used +static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, }; +*/ + +/* Offsets to the Command and Status Registers, "CSRs". All accesses + must be longword instructions and quadword aligned. */ +enum tulip_offsets { + CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28, + CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58, + CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78, CSR16=0x80, CSR20=0xA0 +}; + +/* The bits in the CSR5 status registers, mostly interrupt sources. */ +enum status_bits { + TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10, + NormalIntr=0x10000, AbnormalIntr=0x8000, + RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40, + TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01, +}; + +/* The configuration bits in CSR6. */ +enum csr6_mode_bits { + TxOn=0x2000, RxOn=0x0002, FullDuplex=0x0200, + AcceptBroadcast=0x0100, AcceptAllMulticast=0x0080, + AcceptAllPhys=0x0040, AcceptRunt=0x0008, +}; + + +enum desc_status_bits { + DescOwnded=0x80000000, RxDescFatalErr=0x8000, RxWholePkt=0x0300, +}; + +struct medialeaf { + u8 type; + u8 media; + unsigned char *leafdata; +}; + +struct mediatable { + u16 defaultmedia; + u8 leafcount, csr12dir; /* General purpose pin directions. */ + unsigned has_mii:1, has_nonmii:1, has_reset:6; + u32 csr15dir, csr15val; /* 21143 NWay setting. */ + struct medialeaf mleaf[0]; +}; + +struct mediainfo { + struct mediainfo *next; + int info_type; + int index; + unsigned char *info; +}; + +/* EEPROM Address width definitions */ +#define EEPROM_ADDRLEN 6 +#define EEPROM_SIZE 128 /* 2 << EEPROM_ADDRLEN */ + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD (5 << addr_len) +#define EE_READ_CMD (6 << addr_len) +#define EE_ERASE_CMD (7 << addr_len) + +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */ +#define EE_CS 0x01 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x01 +#define EE_WRITE_1 0x05 +#define EE_DATA_READ 0x08 /* EEPROM chip data out. */ +#define EE_ENB (0x4800 | EE_CS) + +/* Delay between EEPROM clock transitions. Even at 33Mhz current PCI + implementations don't overrun the EEPROM clock. We add a bus + turn-around to insure that this remains true. */ +#define eeprom_delay() inl(ee_addr) + +/* Size of transmit and receive buffers */ +#define BUFLEN 1536 + +/* Ring-wrap flag in length field, use for last ring entry. + 0x01000000 means chain on buffer2 address, + 0x02000000 means use the ring start address in CSR2/3. + Note: Some work-alike chips do not function correctly in chained mode. + The ASIX chip works only in chained mode. + Thus we indicate ring mode, but always write the 'next' field for + chained mode as well. */ +#define DESC_RING_WRAP 0x02000000 + +/* transmit and receive descriptor format */ +struct tulip_rx_desc { + volatile u32 status; + u32 length; + u32 buffer1, buffer2; +}; + +struct tulip_tx_desc { + volatile u32 status; + u32 length; + u32 buffer1, buffer2; +}; + +/*********************************************************************/ +/* Global Storage */ +/*********************************************************************/ + +static u32 ioaddr; + +struct tulip_private { + int cur_rx; + int chip_id; /* index into tulip_tbl[] */ + int pci_id_idx; /* index into pci_id_tbl[] */ + int revision; + int flags; + unsigned short vendor_id; /* PCI card vendor code */ + unsigned short dev_id; /* PCI card device code */ + unsigned char ehdr[ETH_HLEN]; /* buffer for ethernet header */ + const char *nic_name; + unsigned int csr0, csr6; /* Current CSR0, CSR6 settings. */ + unsigned int if_port; + unsigned int full_duplex; /* Full-duplex operation requested. */ + unsigned int full_duplex_lock; + unsigned int medialock; /* Do not sense media type. */ + unsigned int mediasense; /* Media sensing in progress. */ + unsigned int nway, nwayset; /* 21143 internal NWay. */ + unsigned int default_port; + unsigned char eeprom[EEPROM_SIZE]; /* Serial EEPROM contents. */ + u8 media_table_storage[(sizeof(struct mediatable) + 32*sizeof(struct medialeaf))]; + u16 sym_advertise, mii_advertise; /* NWay to-advertise. */ + struct mediatable *mtable; + u16 lpar; /* 21143 Link partner ability. */ + u16 advertising[4]; /* MII advertise, from SROM table. */ + signed char phys[4], mii_cnt; /* MII device addresses. */ + int cur_index; /* Current media index. */ + int saved_if_port; +}; + +/* Note: transmit and receive buffers must be longword aligned and + longword divisable */ + +#define TX_RING_SIZE 2 +#define RX_RING_SIZE 4 +struct { + struct tulip_tx_desc tx_ring[TX_RING_SIZE]; + unsigned char txb[BUFLEN]; + struct tulip_rx_desc rx_ring[RX_RING_SIZE]; + unsigned char rxb[RX_RING_SIZE * BUFLEN]; + struct tulip_private tpx; +} tulip_bss __shared __attribute__ ((aligned(4))); +#define tx_ring tulip_bss.tx_ring +#define txb tulip_bss.txb +#define rx_ring tulip_bss.rx_ring +#define rxb tulip_bss.rxb + +static struct tulip_private *tp; + +/* Known cards that have old-style EEPROMs. + Writing this table is described at + http://cesdis.gsfc.nasa.gov/linux/drivers/tulip-drivers/tulip-media.html */ +static struct fixups { + char *name; + unsigned char addr0, addr1, addr2; + u16 newtable[32]; /* Max length below. */ +} eeprom_fixups[] = { + {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c, + 0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }}, + {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x041f, + 0x0000, 0x009E, /* 10baseT */ + 0x0004, 0x009E, /* 10baseT-FD */ + 0x0903, 0x006D, /* 100baseTx */ + 0x0905, 0x006D, /* 100baseTx-FD */ }}, + {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x063f, + 0x0107, 0x8021, /* 100baseFx */ + 0x0108, 0x8021, /* 100baseFx-FD */ + 0x0100, 0x009E, /* 10baseT */ + 0x0104, 0x009E, /* 10baseT-FD */ + 0x0103, 0x006D, /* 100baseTx */ + 0x0105, 0x006D, /* 100baseTx-FD */ }}, + {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0513, + 0x1001, 0x009E, /* 10base2, CSR12 0x10*/ + 0x0000, 0x009E, /* 10baseT */ + 0x0004, 0x009E, /* 10baseT-FD */ + 0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ + 0x0305, 0x006D, /* 100baseTx-FD CSR12 0x03 */}}, + {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x051F, + 0x1B01, 0x0000, /* 10base2, CSR12 0x1B */ + 0x0B00, 0x009E, /* 10baseT, CSR12 0x0B */ + 0x0B04, 0x009E, /* 10baseT-FD,CSR12 0x0B */ + 0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ + 0x1B05, 0x006D, /* 100baseTx-FD CSR12 0x1B */ + }}, + {0, 0, 0, 0, {}}}; + +static const char * block_name[] = {"21140 non-MII", "21140 MII PHY", + "21142 Serial PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"}; + + +/*********************************************************************/ +/* Function Prototypes */ +/*********************************************************************/ +static int mdio_read(struct nic *nic, int phy_id, int location); +static void mdio_write(struct nic *nic, int phy_id, int location, int value); +static int read_eeprom(unsigned long ioaddr, int location, int addr_len); +static void parse_eeprom(struct nic *nic); +static int tulip_probe(struct nic *nic,struct pci_device *pci); +static void tulip_init_ring(struct nic *nic); +static void tulip_reset(struct nic *nic); +static void tulip_transmit(struct nic *nic, const char *d, unsigned int t, + unsigned int s, const char *p); +static int tulip_poll(struct nic *nic, int retrieve); +static void tulip_disable(struct nic *nic); +static void nway_start(struct nic *nic); +static void pnic_do_nway(struct nic *nic); +static void select_media(struct nic *nic, int startup); +static void init_media(struct nic *nic); +static void start_link(struct nic *nic); +static int tulip_check_duplex(struct nic *nic); + +static void tulip_wait(unsigned int nticks); + +#ifdef TULIP_DEBUG_WHERE +static void whereami(const char *str); +#endif + +#ifdef TULIP_DEBUG +static void tulip_more(void); +#endif + + +/*********************************************************************/ +/* Utility Routines */ +/*********************************************************************/ + +#ifdef TULIP_DEBUG_WHERE +static void whereami (const char *str, struct pci_device *pci) +{ + printf("%s: %s\n", tp->nic_name, str); + /* sleep(2); */ +} +#endif + +#ifdef TULIP_DEBUG +static void tulip_more(void) +{ + printf("\n\n-- more --"); + while (!iskey()) + /* wait */; + getchar(); + printf("\n\n"); +} +#endif /* TULIP_DEBUG */ + +static void tulip_wait(unsigned int nticks) +{ + unsigned int to = currticks() + nticks; + while (currticks() < to) + /* wait */ ; +} + + +/*********************************************************************/ +/* Media Descriptor Code */ +/*********************************************************************/ + +/* MII transceiver control section. + Read and write the MII registers using software-generated serial + MDIO protocol. See the MII specifications or DP83840A data sheet + for details. */ + +/* The maximum data clock rate is 2.5 Mhz. The minimum timing is usually + met by back-to-back PCI I/O cycles, but we insert a delay to avoid + "overclocking" issues or future 66Mhz PCI. */ +#define mdio_delay() inl(mdio_addr) + +/* Read and write the MII registers using software-generated serial + MDIO protocol. It is just different enough from the EEPROM protocol + to not share code. The maxium data clock rate is 2.5 Mhz. */ +#define MDIO_SHIFT_CLK 0x10000 +#define MDIO_DATA_WRITE0 0x00000 +#define MDIO_DATA_WRITE1 0x20000 +#define MDIO_ENB 0x00000 /* Ignore the 0x02000 databook setting. */ +#define MDIO_ENB_IN 0x40000 +#define MDIO_DATA_READ 0x80000 + +/* MII transceiver control section. + Read and write the MII registers using software-generated serial + MDIO protocol. See the MII specifications or DP83840A data sheet + for details. */ + +int mdio_read(struct nic *nic __unused, int phy_id, int location) +{ + int i; + int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; + int retval = 0; + long mdio_addr = ioaddr + CSR9; + +#ifdef TULIP_DEBUG_WHERE + whereami("mdio_read\n"); +#endif + + if (tp->chip_id == LC82C168) { + int i = 1000; + outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0); + inl(ioaddr + 0xA0); + inl(ioaddr + 0xA0); + while (--i > 0) + if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000)) + return retval & 0xffff; + return 0xffff; + } + + if (tp->chip_id == COMET) { + if (phy_id == 1) { + if (location < 7) + return inl(ioaddr + 0xB4 + (location<<2)); + else if (location == 17) + return inl(ioaddr + 0xD0); + else if (location >= 29 && location <= 31) + return inl(ioaddr + 0xD4 + ((location-29)<<2)); + } + return 0xffff; + } + + /* Establish sync by sending at least 32 logic ones. */ + for (i = 32; i >= 0; i--) { + outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Shift the read command bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; + + outl(MDIO_ENB | dataval, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 19; i > 0; i--) { + outl(MDIO_ENB_IN, mdio_addr); + mdio_delay(); + retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); + outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + return (retval>>1) & 0xffff; +} + +void mdio_write(struct nic *nic __unused, int phy_id, int location, int value) +{ + int i; + int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; + long mdio_addr = ioaddr + CSR9; + +#ifdef TULIP_DEBUG_WHERE + whereami("mdio_write\n"); +#endif + + if (tp->chip_id == LC82C168) { + int i = 1000; + outl(cmd, ioaddr + 0xA0); + do + if ( ! (inl(ioaddr + 0xA0) & 0x80000000)) + break; + while (--i > 0); + return; + } + + if (tp->chip_id == COMET) { + if (phy_id != 1) + return; + if (location < 7) + outl(value, ioaddr + 0xB4 + (location<<2)); + else if (location == 17) + outl(value, ioaddr + 0xD0); + else if (location >= 29 && location <= 31) + outl(value, ioaddr + 0xD4 + ((location-29)<<2)); + return; + } + + /* Establish sync by sending 32 logic ones. */ + for (i = 32; i >= 0; i--) { + outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; + outl(MDIO_ENB | dataval, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + outl(MDIO_ENB_IN, mdio_addr); + mdio_delay(); + outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } +} + + +/*********************************************************************/ +/* EEPROM Reading Code */ +/*********************************************************************/ +/* EEPROM routines adapted from the Linux Tulip Code */ +/* Reading a serial EEPROM is a "bit" grungy, but we work our way + through:->. +*/ +static int read_eeprom(unsigned long ioaddr, int location, int addr_len) +{ + int i; + unsigned short retval = 0; + long ee_addr = ioaddr + CSR9; + int read_cmd = location | EE_READ_CMD; + +#ifdef TULIP_DEBUG_WHERE + whereami("read_eeprom\n"); +#endif + + outl(EE_ENB & ~EE_CS, ee_addr); + outl(EE_ENB, ee_addr); + + /* Shift the read command bits out. */ + for (i = 4 + addr_len; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + outl(EE_ENB | dataval, ee_addr); + eeprom_delay(); + outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(); + } + outl(EE_ENB, ee_addr); + + for (i = 16; i > 0; i--) { + outl(EE_ENB | EE_SHIFT_CLK, ee_addr); + eeprom_delay(); + retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0); + outl(EE_ENB, ee_addr); + eeprom_delay(); + } + + /* Terminate the EEPROM access. */ + outl(EE_ENB & ~EE_CS, ee_addr); + return retval; +} + + +/*********************************************************************/ +/* EEPROM Parsing Code */ +/*********************************************************************/ +static void parse_eeprom(struct nic *nic) +{ + unsigned char *p, *ee_data = tp->eeprom; + int new_advertise = 0; + int i; + +#ifdef TULIP_DEBUG_WHERE + whereami("parse_eeprom\n"); +#endif + + tp->mtable = 0; + /* Detect an old-style (SA only) EEPROM layout: + memcmp(ee_data, ee_data+16, 8). */ + for (i = 0; i < 8; i ++) + if (ee_data[i] != ee_data[16+i]) + break; + if (i >= 8) { + /* Do a fix-up based on the vendor half of the station address. */ + for (i = 0; eeprom_fixups[i].name; i++) { + if (nic->node_addr[0] == eeprom_fixups[i].addr0 + && nic->node_addr[1] == eeprom_fixups[i].addr1 + && nic->node_addr[2] == eeprom_fixups[i].addr2) { + if (nic->node_addr[2] == 0xE8 && ee_data[0x1a] == 0x55) + i++; /* An Accton EN1207, not an outlaw Maxtech. */ + memcpy(ee_data + 26, eeprom_fixups[i].newtable, + sizeof(eeprom_fixups[i].newtable)); +#ifdef TULIP_DEBUG + printf("%s: Old format EEPROM on '%s' board.\n%s: Using substitute media control info.\n", + tp->nic_name, eeprom_fixups[i].name, tp->nic_name); +#endif + break; + } + } + if (eeprom_fixups[i].name == NULL) { /* No fixup found. */ +#ifdef TULIP_DEBUG + printf("%s: Old style EEPROM with no media selection information.\n", + tp->nic_name); +#endif + return; + } + } + + if (ee_data[19] > 1) { +#ifdef TULIP_DEBUG + printf("%s: Multiport cards (%d ports) may not work correctly.\n", + tp->nic_name, ee_data[19]); +#endif + } + + p = (void *)ee_data + ee_data[27]; + + if (ee_data[27] == 0) { /* No valid media table. */ +#ifdef TULIP_DEBUG + if (tulip_debug > 1) { + printf("%s: No Valid Media Table. ee_data[27] = %hhX\n", + tp->nic_name, ee_data[27]); + } +#endif + } else if (tp->chip_id == DC21041) { + int media = get_u16(p); + int count = p[2]; + p += 3; + + printf("%s: 21041 Media table, default media %hX (%s).\n", + tp->nic_name, media, + media & 0x0800 ? "Autosense" : medianame[media & 15]); + for (i = 0; i < count; i++) { + unsigned char media_block = *p++; + int media_code = media_block & MEDIA_MASK; + if (media_block & 0x40) + p += 6; + switch(media_code) { + case 0: new_advertise |= 0x0020; break; + case 4: new_advertise |= 0x0040; break; + } + printf("%s: 21041 media #%d, %s.\n", + tp->nic_name, media_code, medianame[media_code]); + } + } else { + unsigned char csr12dir = 0; + int count; + struct mediatable *mtable; + u16 media = get_u16(p); + + p += 2; + if (tp->flags & CSR12_IN_SROM) + csr12dir = *p++; + count = *p++; + + tp->mtable = mtable = (struct mediatable *)&tp->media_table_storage[0]; + + mtable->defaultmedia = media; + mtable->leafcount = count; + mtable->csr12dir = csr12dir; + mtable->has_nonmii = mtable->has_mii = mtable->has_reset = 0; + mtable->csr15dir = mtable->csr15val = 0; + + printf("%s: EEPROM default media type %s.\n", tp->nic_name, + media & 0x0800 ? "Autosense" : medianame[media & MEDIA_MASK]); + + for (i = 0; i < count; i++) { + struct medialeaf *leaf = &mtable->mleaf[i]; + + if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */ + leaf->type = 0; + leaf->media = p[0] & 0x3f; + leaf->leafdata = p; + if ((p[2] & 0x61) == 0x01) /* Bogus, but Znyx boards do it. */ + mtable->has_mii = 1; + p += 4; + } else { + switch(leaf->type = p[1]) { + case 5: + mtable->has_reset = i; + leaf->media = p[2] & 0x0f; + break; + case 1: case 3: + mtable->has_mii = 1; + leaf->media = 11; + break; + case 2: + if ((p[2] & 0x3f) == 0) { + u32 base15 = (p[2] & 0x40) ? get_u16(p + 7) : 0x0008; + u16 *p1 = (u16 *)(p + (p[2] & 0x40 ? 9 : 3)); + mtable->csr15dir = (get_unaligned(p1 + 0)<<16) + base15; + mtable->csr15val = (get_unaligned(p1 + 1)<<16) + base15; + } + /* Fall through. */ + case 0: case 4: + mtable->has_nonmii = 1; + leaf->media = p[2] & MEDIA_MASK; + switch (leaf->media) { + case 0: new_advertise |= 0x0020; break; + case 4: new_advertise |= 0x0040; break; + case 3: new_advertise |= 0x0080; break; + case 5: new_advertise |= 0x0100; break; + case 6: new_advertise |= 0x0200; break; + } + break; + default: + leaf->media = 19; + } + leaf->leafdata = p + 2; + p += (p[0] & 0x3f) + 1; + } +#ifdef TULIP_DEBUG + if (tulip_debug > 1 && leaf->media == 11) { + unsigned char *bp = leaf->leafdata; + printf("%s: MII interface PHY %d, setup/reset sequences %d/%d long, capabilities %hhX %hhX.\n", + tp->nic_name, bp[0], bp[1], bp[2 + bp[1]*2], + bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]); + } +#endif + printf("%s: Index #%d - Media %s (#%d) described " + "by a %s (%d) block.\n", + tp->nic_name, i, medianame[leaf->media], leaf->media, + leaf->type < 6 ? block_name[leaf->type] : "UNKNOWN", + leaf->type); + } + if (new_advertise) + tp->sym_advertise = new_advertise; + } +} + + +/*********************************************************************/ +/* tulip_init_ring - setup the tx and rx descriptors */ +/*********************************************************************/ +static void tulip_init_ring(struct nic *nic __unused) +{ + int i; + +#ifdef TULIP_DEBUG_WHERE + whereami("tulip_init_ring\n"); +#endif + + tp->cur_rx = 0; + + for (i = 0; i < RX_RING_SIZE; i++) { + rx_ring[i].status = cpu_to_le32(0x80000000); + rx_ring[i].length = cpu_to_le32(BUFLEN); + rx_ring[i].buffer1 = virt_to_le32desc(&rxb[i * BUFLEN]); + rx_ring[i].buffer2 = virt_to_le32desc(&rx_ring[i+1]); + } + /* Mark the last entry as wrapping the ring. */ + rx_ring[i-1].length = cpu_to_le32(DESC_RING_WRAP | BUFLEN); + rx_ring[i-1].buffer2 = virt_to_le32desc(&rx_ring[0]); + + /* We only use 1 transmit buffer, but we use 2 descriptors so + transmit engines have somewhere to point to if they feel the need */ + + tx_ring[0].status = 0x00000000; + tx_ring[0].buffer1 = virt_to_le32desc(&txb[0]); + tx_ring[0].buffer2 = virt_to_le32desc(&tx_ring[1]); + + /* this descriptor should never get used, since it will never be owned + by the machine (status will always == 0) */ + tx_ring[1].status = 0x00000000; + tx_ring[1].buffer1 = virt_to_le32desc(&txb[0]); + tx_ring[1].buffer2 = virt_to_le32desc(&tx_ring[0]); + + /* Mark the last entry as wrapping the ring, though this should never happen */ + tx_ring[1].length = cpu_to_le32(DESC_RING_WRAP | BUFLEN); +} + + +static void set_rx_mode(struct nic *nic __unused) { + int csr6 = inl(ioaddr + CSR6) & ~0x00D5; + + tp->csr6 &= ~0x00D5; + + /* !IFF_PROMISC */ + tp->csr6 |= AcceptAllMulticast; + csr6 |= AcceptAllMulticast; + + outl(csr6, ioaddr + CSR6); + + + +} + +/*********************************************************************/ +/* eth_reset - Reset adapter */ +/*********************************************************************/ +static void tulip_reset(struct nic *nic) +{ + int i; + unsigned long to; + +#ifdef TULIP_DEBUG_WHERE + whereami("tulip_reset\n"); +#endif + + /* Stop Tx and RX */ + outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6); + + /* On some chip revs we must set the MII/SYM port before the reset!? */ + if (tp->mii_cnt || (tp->mtable && tp->mtable->has_mii)) { + outl(0x814C0000, ioaddr + CSR6); + } + + /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */ + outl(0x00000001, ioaddr + CSR0); + tulip_wait(1); + + /* turn off reset and set cache align=16lword, burst=unlimit */ + outl(tp->csr0, ioaddr + CSR0); + + /* Wait the specified 50 PCI cycles after a reset */ + tulip_wait(1); + + /* set up transmit and receive descriptors */ + tulip_init_ring(nic); + + if (tp->chip_id == PNIC2) { + u32 addr_high = (nic->node_addr[1]<<8) + (nic->node_addr[0]<<0); + /* This address setting does not appear to impact chip operation?? */ + outl((nic->node_addr[5]<<8) + nic->node_addr[4] + + (nic->node_addr[3]<<24) + (nic->node_addr[2]<<16), + ioaddr + 0xB0); + outl(addr_high + (addr_high<<16), ioaddr + 0xB8); + } + + /* MC_HASH_ONLY boards don't support setup packets */ + if (tp->flags & MC_HASH_ONLY) { + u32 addr_low = cpu_to_le32(get_unaligned((u32 *)nic->node_addr)); + u32 addr_high = cpu_to_le32(get_unaligned((u16 *)(nic->node_addr+4))); + + /* clear multicast hash filters and setup MAC address filters */ + if (tp->flags & IS_ASIX) { + outl(0, ioaddr + CSR13); + outl(addr_low, ioaddr + CSR14); + outl(1, ioaddr + CSR13); + outl(addr_high, ioaddr + CSR14); + outl(2, ioaddr + CSR13); + outl(0, ioaddr + CSR14); + outl(3, ioaddr + CSR13); + outl(0, ioaddr + CSR14); + } else if (tp->chip_id == COMET) { + outl(addr_low, ioaddr + 0xA4); + outl(addr_high, ioaddr + 0xA8); + outl(0, ioaddr + 0xAC); + outl(0, ioaddr + 0xB0); + } + } else { + /* for other boards we send a setup packet to initialize + the filters */ + u32 tx_flags = 0x08000000 | 192; + + /* construct perfect filter frame with mac address as first match + and broadcast address for all others */ + for (i=0; i<192; i++) + txb[i] = 0xFF; + txb[0] = nic->node_addr[0]; + txb[1] = nic->node_addr[1]; + txb[4] = nic->node_addr[2]; + txb[5] = nic->node_addr[3]; + txb[8] = nic->node_addr[4]; + txb[9] = nic->node_addr[5]; + + tx_ring[0].length = cpu_to_le32(tx_flags); + tx_ring[0].buffer1 = virt_to_le32desc(&txb[0]); + tx_ring[0].status = cpu_to_le32(0x80000000); + } + + /* Point to rx and tx descriptors */ + outl(virt_to_le32desc(&rx_ring[0]), ioaddr + CSR3); + outl(virt_to_le32desc(&tx_ring[0]), ioaddr + CSR4); + + init_media(nic); + + /* set the chip's operating mode (but don't turn on xmit and recv yet) */ + outl((tp->csr6 & ~0x00002002), ioaddr + CSR6); + + /* send setup packet for cards that support it */ + if (!(tp->flags & MC_HASH_ONLY)) { + /* enable transmit wait for completion */ + outl(tp->csr6 | 0x00002000, ioaddr + CSR6); + /* immediate transmit demand */ + outl(0, ioaddr + CSR1); + + to = currticks() + TX_TIME_OUT; + while ((tx_ring[0].status & 0x80000000) && (currticks() < to)) + /* wait */ ; + + if (currticks() >= to) { + printf ("%s: TX Setup Timeout.\n", tp->nic_name); + } + } + + if (tp->chip_id == LC82C168) + tulip_check_duplex(nic); + + set_rx_mode(nic); + + /* enable transmit and receive */ + outl(tp->csr6 | 0x00002002, ioaddr + CSR6); +} + + +/*********************************************************************/ +/* eth_transmit - Transmit a frame */ +/*********************************************************************/ +static void tulip_transmit(struct nic *nic, const char *d, unsigned int t, + unsigned int s, const char *p) +{ + u16 nstype; + u32 to; + u32 csr6 = inl(ioaddr + CSR6); + +#ifdef TULIP_DEBUG_WHERE + whereami("tulip_transmit\n"); +#endif + + /* Disable Tx */ + outl(csr6 & ~0x00002000, ioaddr + CSR6); + + memcpy(txb, d, ETH_ALEN); + memcpy(txb + ETH_ALEN, nic->node_addr, ETH_ALEN); + nstype = htons((u16) t); + memcpy(txb + 2 * ETH_ALEN, (u8 *)&nstype, 2); + memcpy(txb + ETH_HLEN, p, s); + + s += ETH_HLEN; + s &= 0x0FFF; + + /* pad to minimum packet size */ + while (s < ETH_ZLEN) + txb[s++] = '\0'; + +#ifdef TULIP_DEBUG + if (tulip_debug > 1) + printf("%s: sending %d bytes ethtype %hX\n", tp->nic_name, s, t); +#endif + + /* setup the transmit descriptor */ + /* 0x60000000 = no interrupt on completion */ + tx_ring[0].length = cpu_to_le32(0x60000000 | s); + tx_ring[0].status = cpu_to_le32(0x80000000); + + /* Point to transmit descriptor */ + outl(virt_to_le32desc(&tx_ring[0]), ioaddr + CSR4); + + /* Enable Tx */ + outl(csr6 | 0x00002000, ioaddr + CSR6); + /* immediate transmit demand */ + outl(0, ioaddr + CSR1); + + to = currticks() + TX_TIME_OUT; + while ((tx_ring[0].status & 0x80000000) && (currticks() < to)) + /* wait */ ; + + if (currticks() >= to) { + printf ("TX Timeout!\n"); + } + + /* Disable Tx */ + outl(csr6 & ~0x00002000, ioaddr + CSR6); +} + +/*********************************************************************/ +/* eth_poll - Wait for a frame */ +/*********************************************************************/ +static int tulip_poll(struct nic *nic, int retrieve) +{ + +#ifdef TULIP_DEBUG_WHERE + whereami("tulip_poll\n"); +#endif + + /* no packet waiting. packet still owned by NIC */ + if (rx_ring[tp->cur_rx].status & 0x80000000) + return 0; + + if ( ! retrieve ) return 1; + +#ifdef TULIP_DEBUG_WHERE + whereami("tulip_poll got one\n"); +#endif + + nic->packetlen = (rx_ring[tp->cur_rx].status & 0x3FFF0000) >> 16; + + /* if we get a corrupted packet. throw it away and move on */ + if (rx_ring[tp->cur_rx].status & 0x00008000) { + /* return the descriptor and buffer to receive ring */ + rx_ring[tp->cur_rx].status = 0x80000000; + tp->cur_rx = (++tp->cur_rx) % RX_RING_SIZE; + return 0; + } + + /* copy packet to working buffer */ + memcpy(nic->packet, rxb + tp->cur_rx * BUFLEN, nic->packetlen); + + /* return the descriptor and buffer to receive ring */ + rx_ring[tp->cur_rx].status = 0x80000000; + tp->cur_rx = (++tp->cur_rx) % RX_RING_SIZE; + + return 1; +} + +/*********************************************************************/ +/* eth_disable - Disable the interface */ +/*********************************************************************/ +static void tulip_disable ( struct nic *nic ) { + +#ifdef TULIP_DEBUG_WHERE + whereami("tulip_disable\n"); +#endif + + tulip_reset(nic); + + /* disable interrupts */ + outl(0x00000000, ioaddr + CSR7); + + /* Stop the chip's Tx and Rx processes. */ + outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6); + + /* Clear the missed-packet counter. */ + inl(ioaddr + CSR8); +} + +/*********************************************************************/ +/*IRQ - Enable, Disable, or Force interrupts */ +/*********************************************************************/ +static void tulip_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + +static struct nic_operations tulip_operations = { + .connect = dummy_connect, + .poll = tulip_poll, + .transmit = tulip_transmit, + .irq = tulip_irq, + +}; + +/*********************************************************************/ +/* eth_probe - Look for an adapter */ +/*********************************************************************/ +static int tulip_probe ( struct nic *nic, struct pci_device *pci ) { + + u32 i; + u8 chip_rev; + u8 ee_data[EEPROM_SIZE]; + unsigned short sum; + int chip_idx; + static unsigned char last_phys_addr[ETH_ALEN] = {0x00, 'L', 'i', 'n', 'u', 'x'}; + + if (pci->ioaddr == 0) + return 0; + + ioaddr = pci->ioaddr; + nic->ioaddr = pci->ioaddr & ~3; + nic->irqno = 0; + + /* point to private storage */ + tp = &tulip_bss.tpx; + + tp->vendor_id = pci->vendor; + tp->dev_id = pci->device; + tp->nic_name = pci->driver_name; + + tp->if_port = 0; + tp->default_port = 0; + + adjust_pci_device(pci); + + /* disable interrupts */ + outl(0x00000000, ioaddr + CSR7); + + /* Stop the chip's Tx and Rx processes. */ + outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6); + + /* Clear the missed-packet counter. */ + inl(ioaddr + CSR8); + + printf("\n"); /* so we start on a fresh line */ +#ifdef TULIP_DEBUG_WHERE + whereami("tulip_probe\n"); +#endif + +#ifdef TULIP_DEBUG + if (tulip_debug > 1) + printf ("%s: Looking for Tulip Chip: Vendor=%hX Device=%hX\n", tp->nic_name, + tp->vendor, tp->dev_id); +#endif + + /* Figure out which chip we're dealing with */ + i = 0; + chip_idx = -1; + + while (pci_id_tbl[i].name) { + if ( (((u32) tp->dev_id << 16) | tp->vendor_id) == + (pci_id_tbl[i].id.pci & pci_id_tbl[i].id.pci_mask) ) { + chip_idx = pci_id_tbl[i].drv_flags; + break; + } + i++; + } + + if (chip_idx == -1) { + printf ("%s: Unknown Tulip Chip: Vendor=%hX Device=%hX\n", tp->nic_name, + tp->vendor_id, tp->dev_id); + return 0; + } + + tp->pci_id_idx = i; + tp->flags = tulip_tbl[chip_idx].flags; + +#ifdef TULIP_DEBUG + if (tulip_debug > 1) { + printf ("%s: tp->pci_id_idx == %d, name == %s\n", tp->nic_name, + tp->pci_id_idx, pci_id_tbl[tp->pci_id_idx].name); + printf ("%s: chip_idx == %d, name == %s\n", tp->nic_name, chip_idx, + tulip_tbl[chip_idx].chip_name); + } +#endif + + /* Bring the 21041/21143 out of sleep mode. + Caution: Snooze mode does not work with some boards! */ + if (tp->flags & HAS_PWRDWN) + pci_write_config_dword(pci, 0x40, 0x00000000); + + if (inl(ioaddr + CSR5) == 0xFFFFFFFF) { + printf("%s: The Tulip chip at %X is not functioning.\n", + tp->nic_name, (unsigned int) ioaddr); + return 0; + } + + pci_read_config_byte(pci, PCI_REVISION, &chip_rev); + + printf("%s: [chip: %s] rev %d at %hX\n", tp->nic_name, + tulip_tbl[chip_idx].chip_name, chip_rev, (unsigned int) ioaddr); + printf("%s: Vendor=%hX Device=%hX", tp->nic_name, tp->vendor_id, tp->dev_id); + + if (chip_idx == DC21041 && inl(ioaddr + CSR9) & 0x8000) { + printf(" 21040 compatible mode."); + chip_idx = DC21040; + } + + printf("\n"); + + /* The SROM/EEPROM interface varies dramatically. */ + sum = 0; + if (chip_idx == DC21040) { + outl(0, ioaddr + CSR9); /* Reset the pointer with a dummy write. */ + for (i = 0; i < ETH_ALEN; i++) { + int value, boguscnt = 100000; + do + value = inl(ioaddr + CSR9); + while (value < 0 && --boguscnt > 0); + nic->node_addr[i] = value; + sum += value & 0xff; + } + } else if (chip_idx == LC82C168) { + for (i = 0; i < 3; i++) { + int value, boguscnt = 100000; + outl(0x600 | i, ioaddr + 0x98); + do + value = inl(ioaddr + CSR9); + while (value < 0 && --boguscnt > 0); + put_unaligned(le16_to_cpu(value), ((u16*)nic->node_addr) + i); + sum += value & 0xffff; + } + } else if (chip_idx == COMET) { + /* No need to read the EEPROM. */ + put_unaligned(inl(ioaddr + 0xA4), (u32 *)nic->node_addr); + put_unaligned(inl(ioaddr + 0xA8), (u16 *)(nic->node_addr + 4)); + for (i = 0; i < ETH_ALEN; i ++) + sum += nic->node_addr[i]; + } else { + /* A serial EEPROM interface, we read now and sort it out later. */ + int sa_offset = 0; + int ee_addr_size = read_eeprom(ioaddr, 0xff, 8) & 0x40000 ? 8 : 6; + + for (i = 0; i < sizeof(ee_data)/2; i++) + ((u16 *)ee_data)[i] = + le16_to_cpu(read_eeprom(ioaddr, i, ee_addr_size)); + + /* DEC now has a specification (see Notes) but early board makers + just put the address in the first EEPROM locations. */ + /* This does memcmp(eedata, eedata+16, 8) */ + for (i = 0; i < 8; i ++) + if (ee_data[i] != ee_data[16+i]) + sa_offset = 20; + if (ee_data[0] == 0xff && ee_data[1] == 0xff && ee_data[2] == 0) { + sa_offset = 2; /* Grrr, damn Matrox boards. */ + } + for (i = 0; i < ETH_ALEN; i ++) { + nic->node_addr[i] = ee_data[i + sa_offset]; + sum += ee_data[i + sa_offset]; + } + } + /* Lite-On boards have the address byte-swapped. */ + if ((nic->node_addr[0] == 0xA0 || nic->node_addr[0] == 0xC0) + && nic->node_addr[1] == 0x00) + for (i = 0; i < ETH_ALEN; i+=2) { + char tmp = nic->node_addr[i]; + nic->node_addr[i] = nic->node_addr[i+1]; + nic->node_addr[i+1] = tmp; + } + + if (sum == 0 || sum == ETH_ALEN*0xff) { + printf("%s: EEPROM not present!\n", tp->nic_name); + for (i = 0; i < ETH_ALEN-1; i++) + nic->node_addr[i] = last_phys_addr[i]; + nic->node_addr[i] = last_phys_addr[i] + 1; + } + + for (i = 0; i < ETH_ALEN; i++) + last_phys_addr[i] = nic->node_addr[i]; + + DBG ( "%s: %s at ioaddr %hX\n", tp->nic_name, eth_ntoa ( nic->node_addr ), + (unsigned int) ioaddr ); + + tp->chip_id = chip_idx; + tp->revision = chip_rev; + tp->csr0 = csr0; + + /* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles. + And the ASIX must have a burst limit or horrible things happen. */ + if (chip_idx == DC21143 && chip_rev == 65) + tp->csr0 &= ~0x01000000; + else if (tp->flags & IS_ASIX) + tp->csr0 |= 0x2000; + + if (media_cap[tp->default_port] & MediaIsMII) { + static const u16 media2advert[] = { 0x20, 0x40, 0x03e0, 0x60, + 0x80, 0x100, 0x200 }; + tp->mii_advertise = media2advert[tp->default_port - 9]; + tp->mii_advertise |= (tp->flags & HAS_8023X); /* Matching bits! */ + } + + /* This is logically part of the probe routine, but too complex + to write inline. */ + if (tp->flags & HAS_MEDIA_TABLE) { + memcpy(tp->eeprom, ee_data, sizeof(tp->eeprom)); + parse_eeprom(nic); + } + + start_link(nic); + + /* reset the device and make ready for tx and rx of packets */ + tulip_reset(nic); + nic->nic_op = &tulip_operations; + + /* give the board a chance to reset before returning */ + tulip_wait(4*TICKS_PER_SEC); + + return 1; +} + +static void start_link(struct nic *nic) +{ + int i; + +#ifdef TULIP_DEBUG_WHERE + whereami("start_link\n"); +#endif + + if ((tp->flags & ALWAYS_CHECK_MII) || + (tp->mtable && tp->mtable->has_mii) || + ( ! tp->mtable && (tp->flags & HAS_MII))) { + unsigned int phy, phy_idx; + if (tp->mtable && tp->mtable->has_mii) { + for (i = 0; i < tp->mtable->leafcount; i++) + if (tp->mtable->mleaf[i].media == 11) { + tp->cur_index = i; + tp->saved_if_port = tp->if_port; + select_media(nic, 2); + tp->if_port = tp->saved_if_port; + break; + } + } + + /* Find the connected MII xcvrs. */ + for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys); + phy++) { + int mii_status = mdio_read(nic, phy, 1); + if ((mii_status & 0x8301) == 0x8001 || + ((mii_status & 0x8000) == 0 && (mii_status & 0x7800) != 0)) { + int mii_reg0 = mdio_read(nic, phy, 0); + int mii_advert = mdio_read(nic, phy, 4); + int to_advert; + + if (tp->mii_advertise) + to_advert = tp->mii_advertise; + else if (tp->advertising[phy_idx]) + to_advert = tp->advertising[phy_idx]; + else /* Leave unchanged. */ + tp->mii_advertise = to_advert = mii_advert; + + tp->phys[phy_idx++] = phy; + printf("%s: MII transceiver %d config %hX status %hX advertising %hX.\n", + tp->nic_name, phy, mii_reg0, mii_status, mii_advert); + /* Fixup for DLink with miswired PHY. */ + if (mii_advert != to_advert) { + printf("%s: Advertising %hX on PHY %d previously advertising %hX.\n", + tp->nic_name, to_advert, phy, mii_advert); + mdio_write(nic, phy, 4, to_advert); + } + /* Enable autonegotiation: some boards default to off. */ + mdio_write(nic, phy, 0, mii_reg0 | + (tp->full_duplex ? 0x1100 : 0x1000) | + (media_cap[tp->default_port]&MediaIs100 ? 0x2000:0)); + } + } + tp->mii_cnt = phy_idx; + if (tp->mtable && tp->mtable->has_mii && phy_idx == 0) { + printf("%s: ***WARNING***: No MII transceiver found!\n", + tp->nic_name); + tp->phys[0] = 1; + } + } + + /* Reset the xcvr interface and turn on heartbeat. */ + switch (tp->chip_id) { + case DC21040: + outl(0x00000000, ioaddr + CSR13); + outl(0x00000004, ioaddr + CSR13); + break; + case DC21041: + /* This is nway_start(). */ + if (tp->sym_advertise == 0) + tp->sym_advertise = 0x0061; + outl(0x00000000, ioaddr + CSR13); + outl(0xFFFFFFFF, ioaddr + CSR14); + outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */ + outl(inl(ioaddr + CSR6) | 0x0200, ioaddr + CSR6); + outl(0x0000EF01, ioaddr + CSR13); + break; + case DC21140: default: + if (tp->mtable) + outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12); + break; + case DC21142: + case PNIC2: + if (tp->mii_cnt || media_cap[tp->if_port] & MediaIsMII) { + outl(0x82020000, ioaddr + CSR6); + outl(0x0000, ioaddr + CSR13); + outl(0x0000, ioaddr + CSR14); + outl(0x820E0000, ioaddr + CSR6); + } else + nway_start(nic); + break; + case LC82C168: + if ( ! tp->mii_cnt) { + tp->nway = 1; + tp->nwayset = 0; + outl(0x00420000, ioaddr + CSR6); + outl(0x30, ioaddr + CSR12); + outl(0x0001F078, ioaddr + 0xB8); + outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */ + } + break; + case MX98713: case COMPEX9881: + outl(0x00000000, ioaddr + CSR6); + outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */ + outl(0x00000001, ioaddr + CSR13); + break; + case MX98715: case MX98725: + outl(0x01a80000, ioaddr + CSR6); + outl(0xFFFFFFFF, ioaddr + CSR14); + outl(0x00001000, ioaddr + CSR12); + break; + case COMET: + /* No initialization necessary. */ + break; + } +} + +static void nway_start(struct nic *nic __unused) +{ + int csr14 = ((tp->sym_advertise & 0x0780) << 9) | + ((tp->sym_advertise&0x0020)<<1) | 0xffbf; + +#ifdef TULIP_DEBUG_WHERE + whereami("nway_start\n"); +#endif + + tp->if_port = 0; + tp->nway = tp->mediasense = 1; + tp->nwayset = tp->lpar = 0; + if (tp->chip_id == PNIC2) { + tp->csr6 = 0x01000000 | (tp->sym_advertise & 0x0040 ? 0x0200 : 0); + return; + } +#ifdef TULIP_DEBUG + if (tulip_debug > 1) + printf("%s: Restarting internal NWay autonegotiation, %X.\n", + tp->nic_name, csr14); +#endif + outl(0x0001, ioaddr + CSR13); + outl(csr14, ioaddr + CSR14); + tp->csr6 = 0x82420000 | (tp->sym_advertise & 0x0040 ? 0x0200 : 0); + outl(tp->csr6, ioaddr + CSR6); + if (tp->mtable && tp->mtable->csr15dir) { + outl(tp->mtable->csr15dir, ioaddr + CSR15); + outl(tp->mtable->csr15val, ioaddr + CSR15); + } else if (tp->chip_id != PNIC2) + outw(0x0008, ioaddr + CSR15); + if (tp->chip_id == DC21041) /* Trigger NWAY. */ + outl(0xEF01, ioaddr + CSR12); + else + outl(0x1301, ioaddr + CSR12); +} + +static void init_media(struct nic *nic) +{ + int i; + +#ifdef TULIP_DEBUG_WHERE + whereami("init_media\n"); +#endif + + tp->saved_if_port = tp->if_port; + if (tp->if_port == 0) + tp->if_port = tp->default_port; + + /* Allow selecting a default media. */ + i = 0; + if (tp->mtable == NULL) + goto media_picked; + if (tp->if_port) { + int looking_for = media_cap[tp->if_port] & MediaIsMII ? 11 : + (tp->if_port == 12 ? 0 : tp->if_port); + for (i = 0; i < tp->mtable->leafcount; i++) + if (tp->mtable->mleaf[i].media == looking_for) { + printf("%s: Using user-specified media %s.\n", + tp->nic_name, medianame[tp->if_port]); + goto media_picked; + } + } + if ((tp->mtable->defaultmedia & 0x0800) == 0) { + int looking_for = tp->mtable->defaultmedia & 15; + for (i = 0; i < tp->mtable->leafcount; i++) + if (tp->mtable->mleaf[i].media == looking_for) { + printf("%s: Using EEPROM-set media %s.\n", + tp->nic_name, medianame[looking_for]); + goto media_picked; + } + } + /* Start sensing first non-full-duplex media. */ + for (i = tp->mtable->leafcount - 1; + (media_cap[tp->mtable->mleaf[i].media] & MediaAlwaysFD) && i > 0; i--) + ; + media_picked: + + tp->csr6 = 0; + tp->cur_index = i; + tp->nwayset = 0; + + if (tp->if_port) { + if (tp->chip_id == DC21143 && media_cap[tp->if_port] & MediaIsMII) { + /* We must reset the media CSRs when we force-select MII mode. */ + outl(0x0000, ioaddr + CSR13); + outl(0x0000, ioaddr + CSR14); + outl(0x0008, ioaddr + CSR15); + } + select_media(nic, 1); + return; + } + switch(tp->chip_id) { + case DC21041: + /* tp->nway = 1;*/ + nway_start(nic); + break; + case DC21142: + if (tp->mii_cnt) { + select_media(nic, 1); +#ifdef TULIP_DEBUG + if (tulip_debug > 1) + printf("%s: Using MII transceiver %d, status %hX.\n", + tp->nic_name, tp->phys[0], mdio_read(nic, tp->phys[0], 1)); +#endif + outl(0x82020000, ioaddr + CSR6); + tp->csr6 = 0x820E0000; + tp->if_port = 11; + outl(0x0000, ioaddr + CSR13); + outl(0x0000, ioaddr + CSR14); + } else + nway_start(nic); + break; + case PNIC2: + nway_start(nic); + break; + case LC82C168: + if (tp->mii_cnt) { + tp->if_port = 11; + tp->csr6 = 0x814C0000 | (tp->full_duplex ? 0x0200 : 0); + outl(0x0001, ioaddr + CSR15); + } else if (inl(ioaddr + CSR5) & TPLnkPass) + pnic_do_nway(nic); + else { + /* Start with 10mbps to do autonegotiation. */ + outl(0x32, ioaddr + CSR12); + tp->csr6 = 0x00420000; + outl(0x0001B078, ioaddr + 0xB8); + outl(0x0201B078, ioaddr + 0xB8); + } + break; + case MX98713: case COMPEX9881: + tp->if_port = 0; + tp->csr6 = 0x01880000 | (tp->full_duplex ? 0x0200 : 0); + outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80); + break; + case MX98715: case MX98725: + /* Provided by BOLO, Macronix - 12/10/1998. */ + tp->if_port = 0; + tp->csr6 = 0x01a80200; + outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80); + outl(0x11000 | inw(ioaddr + 0xa0), ioaddr + 0xa0); + break; + case COMET: + /* Enable automatic Tx underrun recovery */ + outl(inl(ioaddr + 0x88) | 1, ioaddr + 0x88); + tp->if_port = 0; + tp->csr6 = 0x00040000; + break; + case AX88140: case AX88141: + tp->csr6 = tp->mii_cnt ? 0x00040100 : 0x00000100; + break; + default: + select_media(nic, 1); + } +} + +static void pnic_do_nway(struct nic *nic __unused) +{ + u32 phy_reg = inl(ioaddr + 0xB8); + u32 new_csr6 = tp->csr6 & ~0x40C40200; + +#ifdef TULIP_DEBUG_WHERE + whereami("pnic_do_nway\n"); +#endif + + if (phy_reg & 0x78000000) { /* Ignore baseT4 */ + if (phy_reg & 0x20000000) tp->if_port = 5; + else if (phy_reg & 0x40000000) tp->if_port = 3; + else if (phy_reg & 0x10000000) tp->if_port = 4; + else if (phy_reg & 0x08000000) tp->if_port = 0; + tp->nwayset = 1; + new_csr6 = (tp->if_port & 1) ? 0x01860000 : 0x00420000; + outl(0x32 | (tp->if_port & 1), ioaddr + CSR12); + if (tp->if_port & 1) + outl(0x1F868, ioaddr + 0xB8); + if (phy_reg & 0x30000000) { + tp->full_duplex = 1; + new_csr6 |= 0x00000200; + } +#ifdef TULIP_DEBUG + if (tulip_debug > 1) + printf("%s: PNIC autonegotiated status %X, %s.\n", + tp->nic_name, phy_reg, medianame[tp->if_port]); +#endif + if (tp->csr6 != new_csr6) { + tp->csr6 = new_csr6; + outl(tp->csr6 | 0x0002, ioaddr + CSR6); /* Restart Tx */ + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + } + } +} + +/* Set up the transceiver control registers for the selected media type. */ +static void select_media(struct nic *nic, int startup) +{ + struct mediatable *mtable = tp->mtable; + u32 new_csr6; + int i; + +#ifdef TULIP_DEBUG_WHERE + whereami("select_media\n"); +#endif + + if (mtable) { + struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index]; + unsigned char *p = mleaf->leafdata; + switch (mleaf->type) { + case 0: /* 21140 non-MII xcvr. */ +#ifdef TULIP_DEBUG + if (tulip_debug > 1) + printf("%s: Using a 21140 non-MII transceiver" + " with control setting %hhX.\n", + tp->nic_name, p[1]); +#endif + tp->if_port = p[0]; + if (startup) + outl(mtable->csr12dir | 0x100, ioaddr + CSR12); + outl(p[1], ioaddr + CSR12); + new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18); + break; + case 2: case 4: { + u16 setup[5]; + u32 csr13val, csr14val, csr15dir, csr15val; + for (i = 0; i < 5; i++) + setup[i] = get_u16(&p[i*2 + 1]); + + tp->if_port = p[0] & 15; + if (media_cap[tp->if_port] & MediaAlwaysFD) + tp->full_duplex = 1; + + if (startup && mtable->has_reset) { + struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset]; + unsigned char *rst = rleaf->leafdata; +#ifdef TULIP_DEBUG + if (tulip_debug > 1) + printf("%s: Resetting the transceiver.\n", + tp->nic_name); +#endif + for (i = 0; i < rst[0]; i++) + outl(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15); + } +#ifdef TULIP_DEBUG + if (tulip_debug > 1) + printf("%s: 21143 non-MII %s transceiver control " + "%hX/%hX.\n", + tp->nic_name, medianame[tp->if_port], setup[0], setup[1]); +#endif + if (p[0] & 0x40) { /* SIA (CSR13-15) setup values are provided. */ + csr13val = setup[0]; + csr14val = setup[1]; + csr15dir = (setup[3]<<16) | setup[2]; + csr15val = (setup[4]<<16) | setup[2]; + outl(0, ioaddr + CSR13); + outl(csr14val, ioaddr + CSR14); + outl(csr15dir, ioaddr + CSR15); /* Direction */ + outl(csr15val, ioaddr + CSR15); /* Data */ + outl(csr13val, ioaddr + CSR13); + } else { + csr13val = 1; + csr14val = 0x0003FF7F; + csr15dir = (setup[0]<<16) | 0x0008; + csr15val = (setup[1]<<16) | 0x0008; + if (tp->if_port <= 4) + csr14val = t21142_csr14[tp->if_port]; + if (startup) { + outl(0, ioaddr + CSR13); + outl(csr14val, ioaddr + CSR14); + } + outl(csr15dir, ioaddr + CSR15); /* Direction */ + outl(csr15val, ioaddr + CSR15); /* Data */ + if (startup) outl(csr13val, ioaddr + CSR13); + } +#ifdef TULIP_DEBUG + if (tulip_debug > 1) + printf("%s: Setting CSR15 to %X/%X.\n", + tp->nic_name, csr15dir, csr15val); +#endif + if (mleaf->type == 4) + new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18); + else + new_csr6 = 0x82420000; + break; + } + case 1: case 3: { + int phy_num = p[0]; + int init_length = p[1]; + u16 *misc_info; + + tp->if_port = 11; + new_csr6 = 0x020E0000; + if (mleaf->type == 3) { /* 21142 */ + u16 *init_sequence = (u16*)(p+2); + u16 *reset_sequence = &((u16*)(p+3))[init_length]; + int reset_length = p[2 + init_length*2]; + misc_info = reset_sequence + reset_length; + if (startup) + for (i = 0; i < reset_length; i++) + outl(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15); + for (i = 0; i < init_length; i++) + outl(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15); + } else { + u8 *init_sequence = p + 2; + u8 *reset_sequence = p + 3 + init_length; + int reset_length = p[2 + init_length]; + misc_info = (u16*)(reset_sequence + reset_length); + if (startup) { + outl(mtable->csr12dir | 0x100, ioaddr + CSR12); + for (i = 0; i < reset_length; i++) + outl(reset_sequence[i], ioaddr + CSR12); + } + for (i = 0; i < init_length; i++) + outl(init_sequence[i], ioaddr + CSR12); + } + tp->advertising[phy_num] = get_u16(&misc_info[1]) | 1; + if (startup < 2) { + if (tp->mii_advertise == 0) + tp->mii_advertise = tp->advertising[phy_num]; +#ifdef TULIP_DEBUG + if (tulip_debug > 1) + printf("%s: Advertising %hX on MII %d.\n", + tp->nic_name, tp->mii_advertise, tp->phys[phy_num]); +#endif + mdio_write(nic, tp->phys[phy_num], 4, tp->mii_advertise); + } + break; + } + default: + printf("%s: Invalid media table selection %d.\n", + tp->nic_name, mleaf->type); + new_csr6 = 0x020E0000; + } +#ifdef TULIP_DEBUG + if (tulip_debug > 1) + printf("%s: Using media type %s, CSR12 is %hhX.\n", + tp->nic_name, medianame[tp->if_port], + inl(ioaddr + CSR12) & 0xff); +#endif + } else if (tp->chip_id == DC21041) { + int port = tp->if_port <= 4 ? tp->if_port : 0; +#ifdef TULIP_DEBUG + if (tulip_debug > 1) + printf("%s: 21041 using media %s, CSR12 is %hX.\n", + tp->nic_name, medianame[port == 3 ? 12: port], + inl(ioaddr + CSR12)); +#endif + outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */ + outl(t21041_csr14[port], ioaddr + CSR14); + outl(t21041_csr15[port], ioaddr + CSR15); + outl(t21041_csr13[port], ioaddr + CSR13); + new_csr6 = 0x80020000; + } else if (tp->chip_id == LC82C168) { + if (startup && ! tp->medialock) + tp->if_port = tp->mii_cnt ? 11 : 0; +#ifdef TULIP_DEBUG + if (tulip_debug > 1) + printf("%s: PNIC PHY status is %hX, media %s.\n", + tp->nic_name, inl(ioaddr + 0xB8), medianame[tp->if_port]); +#endif + if (tp->mii_cnt) { + new_csr6 = 0x810C0000; + outl(0x0001, ioaddr + CSR15); + outl(0x0201B07A, ioaddr + 0xB8); + } else if (startup) { + /* Start with 10mbps to do autonegotiation. */ + outl(0x32, ioaddr + CSR12); + new_csr6 = 0x00420000; + outl(0x0001B078, ioaddr + 0xB8); + outl(0x0201B078, ioaddr + 0xB8); + } else if (tp->if_port == 3 || tp->if_port == 5) { + outl(0x33, ioaddr + CSR12); + new_csr6 = 0x01860000; + /* Trigger autonegotiation. */ + outl(startup ? 0x0201F868 : 0x0001F868, ioaddr + 0xB8); + } else { + outl(0x32, ioaddr + CSR12); + new_csr6 = 0x00420000; + outl(0x1F078, ioaddr + 0xB8); + } + } else if (tp->chip_id == DC21040) { /* 21040 */ + /* Turn on the xcvr interface. */ +#ifdef TULIP_DEBUG + int csr12 = inl(ioaddr + CSR12); + if (tulip_debug > 1) + printf("%s: 21040 media type is %s, CSR12 is %hhX.\n", + tp->nic_name, medianame[tp->if_port], csr12); +#endif + if (media_cap[tp->if_port] & MediaAlwaysFD) + tp->full_duplex = 1; + new_csr6 = 0x20000; + /* Set the full duplux match frame. */ + outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11); + outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */ + if (t21040_csr13[tp->if_port] & 8) { + outl(0x0705, ioaddr + CSR14); + outl(0x0006, ioaddr + CSR15); + } else { + outl(0xffff, ioaddr + CSR14); + outl(0x0000, ioaddr + CSR15); + } + outl(0x8f01 | t21040_csr13[tp->if_port], ioaddr + CSR13); + } else { /* Unknown chip type with no media table. */ + if (tp->default_port == 0) + tp->if_port = tp->mii_cnt ? 11 : 3; + if (media_cap[tp->if_port] & MediaIsMII) { + new_csr6 = 0x020E0000; + } else if (media_cap[tp->if_port] & MediaIsFx) { + new_csr6 = 0x028600000; + } else + new_csr6 = 0x038600000; +#ifdef TULIP_DEBUG + if (tulip_debug > 1) + printf("%s: No media description table, assuming " + "%s transceiver, CSR12 %hhX.\n", + tp->nic_name, medianame[tp->if_port], + inl(ioaddr + CSR12)); +#endif + } + + tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0); + return; +} + +/* + Check the MII negotiated duplex and change the CSR6 setting if + required. + Return 0 if everything is OK. + Return < 0 if the transceiver is missing or has no link beat. +*/ +static int tulip_check_duplex(struct nic *nic) +{ + unsigned int bmsr, lpa, negotiated, new_csr6; + + bmsr = mdio_read(nic, tp->phys[0], 1); + lpa = mdio_read(nic, tp->phys[0], 5); + +#ifdef TULIP_DEBUG + if (tulip_debug > 1) + printf("%s: MII status %#x, Link partner report " + "%#x.\n", tp->nic_name, bmsr, lpa); +#endif + + if (bmsr == 0xffff) + return -2; + if ((bmsr & 4) == 0) { + int new_bmsr = mdio_read(nic, tp->phys[0], 1); + if ((new_bmsr & 4) == 0) { +#ifdef TULIP_DEBUG + if (tulip_debug > 1) + printf("%s: No link beat on the MII interface," + " status %#x.\n", tp->nic_name, + new_bmsr); +#endif + return -1; + } + } + tp->full_duplex = lpa & 0x140; + + new_csr6 = tp->csr6; + negotiated = lpa & tp->advertising[0]; + + if(negotiated & 0x380) new_csr6 &= ~0x400000; + else new_csr6 |= 0x400000; + if (tp->full_duplex) new_csr6 |= 0x200; + else new_csr6 &= ~0x200; + + if (new_csr6 != tp->csr6) { + tp->csr6 = new_csr6; + +#ifdef TULIP_DEBUG + if (tulip_debug > 0) + printf("%s: Setting %s-duplex based on MII" + "#%d link partner capability of %#x.\n", + tp->nic_name, + tp->full_duplex ? "full" : "half", + tp->phys[0], lpa); +#endif + return 1; + } + + return 0; +} + +static struct pci_device_id tulip_nics[] = { +PCI_ROM(0x1011, 0x0002, "dc21040", "Digital Tulip"), +PCI_ROM(0x1011, 0x0009, "ds21140", "Digital Tulip Fast"), +PCI_ROM(0x1011, 0x0014, "dc21041", "Digital Tulip+"), +PCI_ROM(0x1011, 0x0019, "ds21142", "Digital Tulip 21142"), +PCI_ROM(0x10b7, 0x9300, "3csoho100b-tx","3ComSOHO100B-TX"), +PCI_ROM(0x10b9, 0x5261, "ali1563", "ALi 1563 integrated ethernet"), +PCI_ROM(0x10d9, 0x0512, "mx98713", "Macronix MX987x3"), +PCI_ROM(0x10d9, 0x0531, "mx98715", "Macronix MX987x5"), +PCI_ROM(0x1113, 0x1217, "mxic-98715", "Macronix MX987x5"), +PCI_ROM(0x11ad, 0xc115, "lc82c115", "LinkSys LNE100TX"), +PCI_ROM(0x11ad, 0x0002, "82c168", "Netgear FA310TX"), +PCI_ROM(0x1282, 0x9100, "dm9100", "Davicom 9100"), +PCI_ROM(0x1282, 0x9102, "dm9102", "Davicom 9102"), +PCI_ROM(0x1282, 0x9009, "dm9009", "Davicom 9009"), +PCI_ROM(0x1282, 0x9132, "dm9132", "Davicom 9132"), +PCI_ROM(0x1317, 0x0985, "centaur-p", "ADMtek Centaur-P"), +PCI_ROM(0x1317, 0x0981, "an981", "ADMtek AN981 Comet"), /* ADMTek Centaur-P (stmicro) */ +PCI_ROM(0x1113, 0x1216, "an983", "ADMTek AN983 Comet"), +PCI_ROM(0x1317, 0x9511, "an983b", "ADMTek Comet 983b"), +PCI_ROM(0x1317, 0x1985, "centaur-c", "ADMTek Centaur-C"), +PCI_ROM(0x8086, 0x0039, "intel21145", "Intel Tulip"), +PCI_ROM(0x125b, 0x1400, "ax88140", "ASIX AX88140"), +PCI_ROM(0x11f6, 0x9881, "rl100tx", "Compex RL100-TX"), +PCI_ROM(0x115d, 0x0003, "xircomtulip", "Xircom Tulip"), +PCI_ROM(0x104a, 0x0981, "tulip-0981", "Tulip 0x104a 0x0981"), +PCI_ROM(0x104a, 0x2774, "SGThomson-STE10100A", "Tulip 0x104a 0x2774"), /*Modified by Ramesh Chander*/ +PCI_ROM(0x1113, 0x9511, "tulip-9511", "Tulip 0x1113 0x9511"), +PCI_ROM(0x1186, 0x1561, "tulip-1561", "Tulip 0x1186 0x1561"), +PCI_ROM(0x1259, 0xa120, "tulip-a120", "Tulip 0x1259 0xa120"), +PCI_ROM(0x13d1, 0xab02, "tulip-ab02", "Tulip 0x13d1 0xab02"), +PCI_ROM(0x13d1, 0xab03, "tulip-ab03", "Tulip 0x13d1 0xab03"), +PCI_ROM(0x13d1, 0xab08, "tulip-ab08", "Tulip 0x13d1 0xab08"), +PCI_ROM(0x14f1, 0x1803, "lanfinity", "Conexant LANfinity"), +PCI_ROM(0x1626, 0x8410, "tulip-8410", "Tulip 0x1626 0x8410"), +PCI_ROM(0x1737, 0xab08, "tulip-1737-ab08","Tulip 0x1737 0xab08"), +PCI_ROM(0x1737, 0xab09, "tulip-ab09", "Tulip 0x1737 0xab09"), +}; + +PCI_DRIVER ( tulip_driver, tulip_nics, PCI_NO_CLASS ); + +DRIVER ( "Tulip", nic_driver, pci_driver, tulip_driver, + tulip_probe, tulip_disable ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/tulip.txt b/gpxe/src/drivers/net/tulip.txt new file mode 100644 index 0000000..b4f6756 --- /dev/null +++ b/gpxe/src/drivers/net/tulip.txt @@ -0,0 +1,54 @@ +This software may be used and distributed according to the terms of +the GNU Public License, incorporated herein by reference. + +This is a tulip and clone driver for Etherboot. See the revision +history in the tulip.c file for information on changes. This version +of the driver incorporates changes from Bob Edwards and Paul Mackerras +who cantributed changes to support the TRENDnet TE100-PCIA NIC which +uses a genuine Intel 21143-PD chipset. There are also various code +cleanups to make time-based activities more reliable. + +Of course you have to have all the usual Etherboot environment +(bootp/dhcp/NFS) set up, and you need a Linux kernel with v0.91g +(7.16.99) or later of the tulip.c driver compiled in to support some +MX98715 based cards. That file is available at: + + http://cesdis.gsfc.nasa.gov/linux/drivers/test/tulip.c + +NOTES + +I've tested this driver with a SOHOware Fast 10/100 Model SDA110A, +a Linksys LNE100TX v2.0, and a Netgear FA310TX card, and it worked at +both 10 and 100 mbits. Other cards based on the tulip family may work as +well. + +These cards are about 20$US, are supported by Linux and now Etherboot, +and being PCI, they auto-configure IRQ and IOADDR and auto-negotiate +10/100 half/full duplex. It seems like a pretty good value compared to +some of the pricier cards, and can lower the cost of building/adapting +thin client workstations substantially while giving a considerable +performance increase. + +On some PCI tulip clone chipsets (MX987x5, LC82C115, LC82C168) this driver +lets the card choose the fastest speed it can negotiate with the peer +device. On other cards, it chooses 10mbit half-duplex. + +I burned an AM27C256 (32KByte) EPROM with mx987x5.lzrom and it worked. +According to the data sheet the MX98715A supports up to 64K (27C512) +EPROMs, + +I've liberally commented the code and header files in the hope that it +will help the next person who hacks the code or needs to support some +tulip clone card, or wishes to add functionality. + +Anyway, please test this if you can on your tulip based card, and let +me (mdc@etherboot.org) and the Etherboot-Discuss list +(etherboot-discuss@lists.sourceforge.net) know how things go. I also +would appreciate code review by people who program. I'm a strong +believer in "another set of eyes". + +Regards, + +Marty Connor +mdc@etherboot.org +http://www.etherboot.org/ diff --git a/gpxe/src/drivers/net/via-rhine.c b/gpxe/src/drivers/net/via-rhine.c new file mode 100644 index 0000000..d9880c3 --- /dev/null +++ b/gpxe/src/drivers/net/via-rhine.c @@ -0,0 +1,1447 @@ +/* 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 +#include + +/* define all ioaddr */ + +#define byPAR0 ioaddr +#define byRCR ioaddr + 6 +#define byTCR ioaddr + 7 +#define byCR0 ioaddr + 8 +#define byCR1 ioaddr + 9 +#define byISR0 ioaddr + 0x0c +#define byISR1 ioaddr + 0x0d +#define byIMR0 ioaddr + 0x0e +#define byIMR1 ioaddr + 0x0f +#define byMAR0 ioaddr + 0x10 +#define byMAR1 ioaddr + 0x11 +#define byMAR2 ioaddr + 0x12 +#define byMAR3 ioaddr + 0x13 +#define byMAR4 ioaddr + 0x14 +#define byMAR5 ioaddr + 0x15 +#define byMAR6 ioaddr + 0x16 +#define byMAR7 ioaddr + 0x17 +#define dwCurrentRxDescAddr ioaddr + 0x18 +#define dwCurrentTxDescAddr ioaddr + 0x1c +#define dwCurrentRDSE0 ioaddr + 0x20 +#define dwCurrentRDSE1 ioaddr + 0x24 +#define dwCurrentRDSE2 ioaddr + 0x28 +#define dwCurrentRDSE3 ioaddr + 0x2c +#define dwNextRDSE0 ioaddr + 0x30 +#define dwNextRDSE1 ioaddr + 0x34 +#define dwNextRDSE2 ioaddr + 0x38 +#define dwNextRDSE3 ioaddr + 0x3c +#define dwCurrentTDSE0 ioaddr + 0x40 +#define dwCurrentTDSE1 ioaddr + 0x44 +#define dwCurrentTDSE2 ioaddr + 0x48 +#define dwCurrentTDSE3 ioaddr + 0x4c +#define dwNextTDSE0 ioaddr + 0x50 +#define dwNextTDSE1 ioaddr + 0x54 +#define dwNextTDSE2 ioaddr + 0x58 +#define dwNextTDSE3 ioaddr + 0x5c +#define dwCurrRxDMAPtr ioaddr + 0x60 +#define dwCurrTxDMAPtr ioaddr + 0x64 +#define byMPHY ioaddr + 0x6c +#define byMIISR ioaddr + 0x6d +#define byBCR0 ioaddr + 0x6e +#define byBCR1 ioaddr + 0x6f +#define byMIICR ioaddr + 0x70 +#define byMIIAD ioaddr + 0x71 +#define wMIIDATA ioaddr + 0x72 +#define byEECSR ioaddr + 0x74 +#define byTEST ioaddr + 0x75 +#define byGPIO ioaddr + 0x76 +#define byCFGA ioaddr + 0x78 +#define byCFGB ioaddr + 0x79 +#define byCFGC ioaddr + 0x7a +#define byCFGD ioaddr + 0x7b +#define wTallyCntMPA ioaddr + 0x7c +#define wTallyCntCRC ioaddr + 0x7d +#define bySTICKHW ioaddr + 0x83 +#define byWOLcrClr ioaddr + 0xA4 +#define byWOLcgClr ioaddr + 0xA7 +#define byPwrcsrClr ioaddr + 0xAC + +/*--------------------- Exioaddr Definitions -------------------------*/ + +/* + * Bits in the RCR register + */ + +#define RCR_RRFT2 0x80 +#define RCR_RRFT1 0x40 +#define RCR_RRFT0 0x20 +#define RCR_PROM 0x10 +#define RCR_AB 0x08 +#define RCR_AM 0x04 +#define RCR_AR 0x02 +#define RCR_SEP 0x01 + +/* + * Bits in the TCR register + */ + +#define TCR_RTSF 0x80 +#define TCR_RTFT1 0x40 +#define TCR_RTFT0 0x20 +#define TCR_OFSET 0x08 +#define TCR_LB1 0x04 /* loopback[1] */ +#define TCR_LB0 0x02 /* loopback[0] */ + +/* + * Bits in the CR0 register + */ + +#define CR0_RDMD 0x40 /* rx descriptor polling demand */ +#define CR0_TDMD 0x20 /* tx descriptor polling demand */ +#define CR0_TXON 0x10 +#define CR0_RXON 0x08 +#define CR0_STOP 0x04 /* stop NIC, default = 1 */ +#define CR0_STRT 0x02 /* start NIC */ +#define CR0_INIT 0x01 /* start init process */ + + +/* + * Bits in the CR1 register + */ + +#define CR1_SFRST 0x80 /* software reset */ +#define CR1_RDMD1 0x40 /* RDMD1 */ +#define CR1_TDMD1 0x20 /* TDMD1 */ +#define CR1_KEYPAG 0x10 /* turn on par/key */ +#define CR1_DPOLL 0x08 /* disable rx/tx auto polling */ +#define CR1_FDX 0x04 /* full duplex mode */ +#define CR1_ETEN 0x02 /* early tx mode */ +#define CR1_EREN 0x01 /* early rx mode */ + +/* + * Bits in the CR register + */ + +#define CR_RDMD 0x0040 /* rx descriptor polling demand */ +#define CR_TDMD 0x0020 /* tx descriptor polling demand */ +#define CR_TXON 0x0010 +#define CR_RXON 0x0008 +#define CR_STOP 0x0004 /* stop NIC, default = 1 */ +#define CR_STRT 0x0002 /* start NIC */ +#define CR_INIT 0x0001 /* start init process */ +#define CR_SFRST 0x8000 /* software reset */ +#define CR_RDMD1 0x4000 /* RDMD1 */ +#define CR_TDMD1 0x2000 /* TDMD1 */ +#define CR_KEYPAG 0x1000 /* turn on par/key */ +#define CR_DPOLL 0x0800 /* disable rx/tx auto polling */ +#define CR_FDX 0x0400 /* full duplex mode */ +#define CR_ETEN 0x0200 /* early tx mode */ +#define CR_EREN 0x0100 /* early rx mode */ + +/* + * Bits in the IMR0 register + */ + +#define IMR0_CNTM 0x80 +#define IMR0_BEM 0x40 +#define IMR0_RUM 0x20 +#define IMR0_TUM 0x10 +#define IMR0_TXEM 0x08 +#define IMR0_RXEM 0x04 +#define IMR0_PTXM 0x02 +#define IMR0_PRXM 0x01 + +/* define imrshadow */ + +#define IMRShadow 0x5AFF + +/* + * Bits in the IMR1 register + */ + +#define IMR1_INITM 0x80 +#define IMR1_SRCM 0x40 +#define IMR1_NBFM 0x10 +#define IMR1_PRAIM 0x08 +#define IMR1_RES0M 0x04 +#define IMR1_ETM 0x02 +#define IMR1_ERM 0x01 + +/* + * Bits in the ISR register + */ + +#define ISR_INITI 0x8000 +#define ISR_SRCI 0x4000 +#define ISR_ABTI 0x2000 +#define ISR_NORBF 0x1000 +#define ISR_PKTRA 0x0800 +#define ISR_RES0 0x0400 +#define ISR_ETI 0x0200 +#define ISR_ERI 0x0100 +#define ISR_CNT 0x0080 +#define ISR_BE 0x0040 +#define ISR_RU 0x0020 +#define ISR_TU 0x0010 +#define ISR_TXE 0x0008 +#define ISR_RXE 0x0004 +#define ISR_PTX 0x0002 +#define ISR_PRX 0x0001 + +/* + * Bits in the ISR0 register + */ + +#define ISR0_CNT 0x80 +#define ISR0_BE 0x40 +#define ISR0_RU 0x20 +#define ISR0_TU 0x10 +#define ISR0_TXE 0x08 +#define ISR0_RXE 0x04 +#define ISR0_PTX 0x02 +#define ISR0_PRX 0x01 + +/* + * Bits in the ISR1 register + */ + +#define ISR1_INITI 0x80 +#define ISR1_SRCI 0x40 +#define ISR1_NORBF 0x10 +#define ISR1_PKTRA 0x08 +#define ISR1_ETI 0x02 +#define ISR1_ERI 0x01 + +/* ISR ABNORMAL CONDITION */ + +#define ISR_ABNORMAL ISR_BE+ISR_RU+ISR_TU+ISR_CNT+ISR_NORBF+ISR_PKTRA + +/* + * Bits in the MIISR register + */ + +#define MIISR_MIIERR 0x08 +#define MIISR_MRERR 0x04 +#define MIISR_LNKFL 0x02 +#define MIISR_SPEED 0x01 + +/* + * Bits in the MIICR register + */ + +#define MIICR_MAUTO 0x80 +#define MIICR_RCMD 0x40 +#define MIICR_WCMD 0x20 +#define MIICR_MDPM 0x10 +#define MIICR_MOUT 0x08 +#define MIICR_MDO 0x04 +#define MIICR_MDI 0x02 +#define MIICR_MDC 0x01 + +/* + * Bits in the EECSR register + */ + +#define EECSR_EEPR 0x80 /* eeprom programed status, 73h means programed */ +#define EECSR_EMBP 0x40 /* eeprom embeded programming */ +#define EECSR_AUTOLD 0x20 /* eeprom content reload */ +#define EECSR_DPM 0x10 /* eeprom direct programming */ +#define EECSR_CS 0x08 /* eeprom CS pin */ +#define EECSR_SK 0x04 /* eeprom SK pin */ +#define EECSR_DI 0x02 /* eeprom DI pin */ +#define EECSR_DO 0x01 /* eeprom DO pin */ + +/* + * Bits in the BCR0 register + */ + +#define BCR0_CRFT2 0x20 +#define BCR0_CRFT1 0x10 +#define BCR0_CRFT0 0x08 +#define BCR0_DMAL2 0x04 +#define BCR0_DMAL1 0x02 +#define BCR0_DMAL0 0x01 + +/* + * Bits in the BCR1 register + */ + +#define BCR1_CTSF 0x20 +#define BCR1_CTFT1 0x10 +#define BCR1_CTFT0 0x08 +#define BCR1_POT2 0x04 +#define BCR1_POT1 0x02 +#define BCR1_POT0 0x01 + +/* + * Bits in the CFGA register + */ + +#define CFGA_EELOAD 0x80 /* enable eeprom embeded and direct programming */ +#define CFGA_JUMPER 0x40 +#define CFGA_MTGPIO 0x08 +#define CFGA_T10EN 0x02 +#define CFGA_AUTO 0x01 + +/* + * Bits in the CFGB register + */ + +#define CFGB_PD 0x80 +#define CFGB_POLEN 0x02 +#define CFGB_LNKEN 0x01 + +/* + * Bits in the CFGC register + */ + +#define CFGC_M10TIO 0x80 +#define CFGC_M10POL 0x40 +#define CFGC_PHY1 0x20 +#define CFGC_PHY0 0x10 +#define CFGC_BTSEL 0x08 +#define CFGC_BPS2 0x04 /* bootrom select[2] */ +#define CFGC_BPS1 0x02 /* bootrom select[1] */ +#define CFGC_BPS0 0x01 /* bootrom select[0] */ + +/* + * Bits in the CFGD register + */ + +#define CFGD_GPIOEN 0x80 +#define CFGD_DIAG 0x40 +#define CFGD_MAGIC 0x10 +#define CFGD_RANDOM 0x08 +#define CFGD_CFDX 0x04 +#define CFGD_CEREN 0x02 +#define CFGD_CETEN 0x01 + +/* Bits in RSR */ +#define RSR_RERR 0x00000001 +#define RSR_CRC 0x00000002 +#define RSR_FAE 0x00000004 +#define RSR_FOV 0x00000008 +#define RSR_LONG 0x00000010 +#define RSR_RUNT 0x00000020 +#define RSR_SERR 0x00000040 +#define RSR_BUFF 0x00000080 +#define RSR_EDP 0x00000100 +#define RSR_STP 0x00000200 +#define RSR_CHN 0x00000400 +#define RSR_PHY 0x00000800 +#define RSR_BAR 0x00001000 +#define RSR_MAR 0x00002000 +#define RSR_RXOK 0x00008000 +#define RSR_ABNORMAL RSR_RERR+RSR_LONG+RSR_RUNT + +/* Bits in TSR */ +#define TSR_NCR0 0x00000001 +#define TSR_NCR1 0x00000002 +#define TSR_NCR2 0x00000004 +#define TSR_NCR3 0x00000008 +#define TSR_COLS 0x00000010 +#define TSR_CDH 0x00000080 +#define TSR_ABT 0x00000100 +#define TSR_OWC 0x00000200 +#define TSR_CRS 0x00000400 +#define TSR_UDF 0x00000800 +#define TSR_TBUFF 0x00001000 +#define TSR_SERR 0x00002000 +#define TSR_JAB 0x00004000 +#define TSR_TERR 0x00008000 +#define TSR_ABNORMAL TSR_TERR+TSR_OWC+TSR_ABT+TSR_JAB+TSR_CRS +#define TSR_OWN_BIT 0x80000000 + +#define CB_DELAY_LOOP_WAIT 10 /* 10ms */ +/* enabled mask value of irq */ + +#define W_IMR_MASK_VALUE 0x1BFF /* initial value of IMR */ + +/* Ethernet address filter type */ +#define PKT_TYPE_DIRECTED 0x0001 /* obsolete, directed address is always accepted */ +#define PKT_TYPE_MULTICAST 0x0002 +#define PKT_TYPE_ALL_MULTICAST 0x0004 +#define PKT_TYPE_BROADCAST 0x0008 +#define PKT_TYPE_PROMISCUOUS 0x0020 +#define PKT_TYPE_LONG 0x2000 +#define PKT_TYPE_RUNT 0x4000 +#define PKT_TYPE_ERROR 0x8000 /* accept error packets, e.g. CRC error */ + +/* Loopback mode */ + +#define NIC_LB_NONE 0x00 +#define NIC_LB_INTERNAL 0x01 +#define NIC_LB_PHY 0x02 /* MII or Internal-10BaseT loopback */ + +#define TX_RING_SIZE 2 +#define RX_RING_SIZE 2 +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */ + +#define PCI_REG_MODE3 0x53 +#define MODE3_MIION 0x04 /* in PCI_REG_MOD3 OF PCI space */ + +enum rhine_revs { + VT86C100A = 0x00, + VTunknown0 = 0x20, + VT6102 = 0x40, + VT8231 = 0x50, /* Integrated MAC */ + VT8233 = 0x60, /* Integrated MAC */ + VT8235 = 0x74, /* Integrated MAC */ + VT8237 = 0x78, /* Integrated MAC */ + VTunknown1 = 0x7C, + VT6105 = 0x80, + VT6105_B0 = 0x83, + VT6105L = 0x8A, + VT6107 = 0x8C, + VTunknown2 = 0x8E, + VT6105M = 0x90, +}; + +/* Transmit and receive descriptors definition */ + +struct rhine_tx_desc +{ + union VTC_tx_status_tag + { + struct + { + unsigned long ncro:1; + unsigned long ncr1:1; + unsigned long ncr2:1; + unsigned long ncr3:1; + unsigned long cols:1; + unsigned long reserve_1:2; + unsigned long cdh:1; + unsigned long abt:1; + unsigned long owc:1; + unsigned long crs:1; + unsigned long udf:1; + unsigned long tbuff:1; + unsigned long serr:1; + unsigned long jab:1; + unsigned long terr:1; + unsigned long reserve_2:15; + unsigned long own_bit:1; + } + bits; + unsigned long lw; + } + tx_status; + + union VTC_tx_ctrl_tag + { + struct + { + unsigned long tx_buf_size:11; + unsigned long extend_tx_buf_size:4; + unsigned long chn:1; + unsigned long crc:1; + unsigned long reserve_1:4; + unsigned long stp:1; + unsigned long edp:1; + unsigned long ic:1; + unsigned long reserve_2:8; + } + bits; + unsigned long lw; + } + tx_ctrl; + + unsigned long buf_addr_1:32; + unsigned long buf_addr_2:32; + +}; + +struct rhine_rx_desc +{ + union VTC_rx_status_tag + { + struct + { + unsigned long rerr:1; + unsigned long crc_error:1; + unsigned long fae:1; + unsigned long fov:1; + unsigned long toolong:1; + unsigned long runt:1; + unsigned long serr:1; + unsigned long buff:1; + unsigned long edp:1; + unsigned long stp:1; + unsigned long chn:1; + unsigned long phy:1; + unsigned long bar:1; + unsigned long mar:1; + unsigned long reserve_1:1; + unsigned long rxok:1; + unsigned long frame_length:11; + unsigned long reverve_2:4; + unsigned long own_bit:1; + } + bits; + unsigned long lw; + } + rx_status; + + union VTC_rx_ctrl_tag + { + struct + { + unsigned long rx_buf_size:11; + unsigned long extend_rx_buf_size:4; + unsigned long reserved_1:17; + } + bits; + unsigned long lw; + } + rx_ctrl; + + unsigned long buf_addr_1:32; + unsigned long buf_addr_2:32; + +}; + +struct { + char txbuf[TX_RING_SIZE * PKT_BUF_SZ + 32]; + char rxbuf[RX_RING_SIZE * PKT_BUF_SZ + 32]; + char txdesc[TX_RING_SIZE * sizeof (struct rhine_tx_desc) + 32]; + char rxdesc[RX_RING_SIZE * sizeof (struct rhine_rx_desc) + 32]; +} rhine_buffers __shared; + +/* The I/O extent. */ +#define rhine_TOTAL_SIZE 0x80 + +#ifdef HAVE_DEVLIST +struct netdev_entry rhine_drv = + { "rhine", rhine_probe, rhine_TOTAL_SIZE, NULL }; +#endif + +static int rhine_debug = 1; + +/* + Theory of Operation + +I. Board Compatibility + +This driver is designed for the VIA 86c100A Rhine-II PCI Fast Ethernet +controller. + +II. Board-specific settings + +Boards with this chip are functional only in a bus-master PCI slot. + +Many operational settings are loaded from the EEPROM to the Config word at +offset 0x78. This driver assumes that they are correct. +If this driver is compiled to use PCI memory space operations the EEPROM +must be configured to enable memory ops. + +III. Driver operation + +IIIa. Ring buffers + +This driver uses two statically allocated fixed-size descriptor lists +formed into rings by a branch from the final descriptor to the beginning of +the list. The ring sizes are set at compile time by RX/TX_RING_SIZE. + +IIIb/c. Transmit/Receive Structure + +This driver attempts to use a zero-copy receive and transmit scheme. + +Alas, all data buffers are required to start on a 32 bit boundary, so +the driver must often copy transmit packets into bounce buffers. + +The driver allocates full frame size skbuffs for the Rx ring buffers at +open() time and passes the skb->data field to the chip as receive data +buffers. When an incoming frame is less than RX_COPYBREAK bytes long, +a fresh skbuff is allocated and the frame is copied to the new skbuff. +When the incoming frame is larger, the skbuff is passed directly up the +protocol stack. Buffers consumed this way are replaced by newly allocated +skbuffs in the last phase of netdev_rx(). + +The RX_COPYBREAK value is chosen to trade-off the memory wasted by +using a full-sized skbuff for small frames vs. the copying costs of larger +frames. New boards are typically used in generously configured machines +and the underfilled buffers have negligible impact compared to the benefit of +a single allocation size, so the default value of zero results in never +copying packets. When copying is done, the cost is usually mitigated by using +a combined copy/checksum routine. Copying also preloads the cache, which is +most useful with small frames. + +Since the VIA chips are only able to transfer data to buffers on 32 bit +boundaries, the the IP header at offset 14 in an ethernet frame isn't +longword aligned for further processing. Copying these unaligned buffers +has the beneficial effect of 16-byte aligning the IP header. + +IIId. Synchronization + +The driver runs as two independent, single-threaded flows of control. One +is the send-packet routine, which enforces single-threaded use by the +dev->tbusy flag. The other thread is the interrupt handler, which is single +threaded by the hardware and interrupt handling software. + +The send packet thread has partial control over the Tx ring and 'dev->tbusy' +flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next +queue slot is empty, it clears the tbusy flag when finished otherwise it sets +the 'lp->tx_full' flag. + +The interrupt handler has exclusive control over the Rx ring and records stats +from the Tx ring. After reaping the stats, it marks the Tx queue entry as +empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it +clears both the tx_full and tbusy flags. + +IV. Notes + +IVb. References + +Preliminary VT86C100A manual from http://www.via.com.tw/ +http://cesdis.gsfc.nasa.gov/linux/misc/100mbps.html +http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html + +IVc. Errata + +The VT86C100A manual is not reliable information. +The chip does not handle unaligned transmit or receive buffers, resulting +in significant performance degradation for bounce buffer copies on transmit +and unaligned IP headers on receive. +The chip does not pad to minimum transmit length. + +*/ + +/* The rest of these values should never change. */ +#define NUM_TX_DESC 2 /* Number of Tx descriptor registers. */ + +static struct rhine_private +{ + char devname[8]; /* Used only for kernel debugging. */ + const char *product_name; + struct rhine_rx_desc *rx_ring; + struct rhine_tx_desc *tx_ring; + char *rx_buffs[RX_RING_SIZE]; + char *tx_buffs[TX_RING_SIZE]; + + /* temporary Rx buffers. */ + + int chip_id; + int chip_revision; + unsigned short ioaddr; + unsigned int cur_rx, cur_tx; /* The next free and used entries */ + unsigned int dirty_rx, dirty_tx; + /* The saved address of a sent-in-place packet/buffer, for skfree(). */ + struct sk_buff *tx_skbuff[TX_RING_SIZE]; + unsigned char mc_filter[8]; /* Current multicast filter. */ + char phys[4]; /* MII device addresses. */ + unsigned int tx_full:1; /* The Tx queue is full. */ + unsigned int full_duplex:1; /* Full-duplex operation requested. */ + unsigned int default_port:4; /* Last dev->if_port value. */ + unsigned int media2:4; /* Secondary monitored media port. */ + unsigned int medialock:1; /* Don't sense media type. */ + unsigned int mediasense:1; /* Media sensing in progress. */ +} +rhine; + +static void rhine_probe1 (struct nic *nic, struct pci_device *pci, int ioaddr, + int chip_id, int options); +static int QueryAuto (int); +static int ReadMII (int byMIIIndex, int); +static void WriteMII (char, char, char, int); +static void MIIDelay (void); +static void rhine_init_ring (struct nic *dev); +static void rhine_disable (struct nic *nic); +static void rhine_reset (struct nic *nic); +static int rhine_poll (struct nic *nic, int retreive); +static void rhine_transmit (struct nic *nic, const char *d, unsigned int t, + unsigned int s, const char *p); +static void reload_eeprom(int ioaddr); + + +static void reload_eeprom(int ioaddr) +{ + int i; + outb(0x20, byEECSR); + /* Typically 2 cycles to reload. */ + for (i = 0; i < 150; i++) + if (! (inb(byEECSR) & 0x20)) + break; +} +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void +rhine_init_ring (struct nic *nic) +{ + struct rhine_private *tp = (struct rhine_private *) nic->priv_data; + int i; + + tp->tx_full = 0; + tp->cur_rx = tp->cur_tx = 0; + tp->dirty_rx = tp->dirty_tx = 0; + + for (i = 0; i < RX_RING_SIZE; i++) + { + + tp->rx_ring[i].rx_status.bits.own_bit = 1; + tp->rx_ring[i].rx_ctrl.bits.rx_buf_size = 1536; + + tp->rx_ring[i].buf_addr_1 = virt_to_bus (tp->rx_buffs[i]); + tp->rx_ring[i].buf_addr_2 = virt_to_bus (&tp->rx_ring[i + 1]); + /* printf("[%d]buf1=%hX,buf2=%hX",i,tp->rx_ring[i].buf_addr_1,tp->rx_ring[i].buf_addr_2); */ + } + /* Mark the last entry as wrapping the ring. */ + /* tp->rx_ring[i-1].rx_ctrl.bits.rx_buf_size =1518; */ + tp->rx_ring[i - 1].buf_addr_2 = virt_to_bus (&tp->rx_ring[0]); + /*printf("[%d]buf1=%hX,buf2=%hX",i-1,tp->rx_ring[i-1].buf_addr_1,tp->rx_ring[i-1].buf_addr_2); */ + + /* The Tx buffer descriptor is filled in as needed, but we + do need to clear the ownership bit. */ + + for (i = 0; i < TX_RING_SIZE; i++) + { + + tp->tx_ring[i].tx_status.lw = 0; + tp->tx_ring[i].tx_ctrl.lw = 0x00e08000; + tp->tx_ring[i].buf_addr_1 = virt_to_bus (tp->tx_buffs[i]); + tp->tx_ring[i].buf_addr_2 = virt_to_bus (&tp->tx_ring[i + 1]); + /* printf("[%d]buf1=%hX,buf2=%hX",i,tp->tx_ring[i].buf_addr_1,tp->tx_ring[i].buf_addr_2); */ + } + + tp->tx_ring[i - 1].buf_addr_2 = virt_to_bus (&tp->tx_ring[0]); + /* printf("[%d]buf1=%hX,buf2=%hX",i,tp->tx_ring[i-1].buf_addr_1,tp->tx_ring[i-1].buf_addr_2); */ +} + +int +QueryAuto (int ioaddr) +{ + int byMIIIndex; + int MIIReturn; + + int advertising,mii_reg5; + int negociated; + + byMIIIndex = 0x04; + MIIReturn = ReadMII (byMIIIndex, ioaddr); + advertising=MIIReturn; + + byMIIIndex = 0x05; + MIIReturn = ReadMII (byMIIIndex, ioaddr); + mii_reg5=MIIReturn; + + negociated=mii_reg5 & advertising; + + if ( (negociated & 0x100) || (negociated & 0x1C0) == 0x40 ) + return 1; + else + return 0; + +} + +int +ReadMII (int byMIIIndex, int ioaddr) +{ + int ReturnMII; + char byMIIAdrbak; + char byMIICRbak; + char byMIItemp; + unsigned long ct; + + byMIIAdrbak = inb (byMIIAD); + byMIICRbak = inb (byMIICR); + outb (byMIICRbak & 0x7f, byMIICR); + MIIDelay (); + + outb (byMIIIndex, byMIIAD); + MIIDelay (); + + outb (inb (byMIICR) | 0x40, byMIICR); + + byMIItemp = inb (byMIICR); + byMIItemp = byMIItemp & 0x40; + + ct = currticks(); + while (byMIItemp != 0 && ct + 2*1000 < currticks()) + { + byMIItemp = inb (byMIICR); + byMIItemp = byMIItemp & 0x40; + } + MIIDelay (); + + ReturnMII = inw (wMIIDATA); + + outb (byMIIAdrbak, byMIIAD); + outb (byMIICRbak, byMIICR); + MIIDelay (); + + return (ReturnMII); + +} + +void +WriteMII (char byMIISetByte, char byMIISetBit, char byMIIOP, int ioaddr) +{ + int ReadMIItmp; + int MIIMask; + char byMIIAdrbak; + char byMIICRbak; + char byMIItemp; + unsigned long ct; + + + byMIIAdrbak = inb (byMIIAD); + + byMIICRbak = inb (byMIICR); + outb (byMIICRbak & 0x7f, byMIICR); + MIIDelay (); + outb (byMIISetByte, byMIIAD); + MIIDelay (); + + outb (inb (byMIICR) | 0x40, byMIICR); + + byMIItemp = inb (byMIICR); + byMIItemp = byMIItemp & 0x40; + + ct = currticks(); + while (byMIItemp != 0 && ct + 2*1000 < currticks()) + { + byMIItemp = inb (byMIICR); + byMIItemp = byMIItemp & 0x40; + } + MIIDelay (); + + ReadMIItmp = inw (wMIIDATA); + MIIMask = 0x0001; + MIIMask = MIIMask << byMIISetBit; + + + if (byMIIOP == 0) + { + MIIMask = ~MIIMask; + ReadMIItmp = ReadMIItmp & MIIMask; + } + else + { + ReadMIItmp = ReadMIItmp | MIIMask; + + } + outw (ReadMIItmp, wMIIDATA); + MIIDelay (); + + outb (inb (byMIICR) | 0x20, byMIICR); + byMIItemp = inb (byMIICR); + byMIItemp = byMIItemp & 0x20; + + ct = currticks(); + while (byMIItemp != 0 && ct + 2*1000 < currticks()) + { + byMIItemp = inb (byMIICR); + byMIItemp = byMIItemp & 0x20; + } + MIIDelay (); + + outb (byMIIAdrbak & 0x7f, byMIIAD); + outb (byMIICRbak, byMIICR); + MIIDelay (); + +} + +void +MIIDelay (void) +{ + int i; + for (i = 0; i < 0x7fff; i++) + { + ( void ) inb (0x61); + ( void ) inb (0x61); + ( void ) inb (0x61); + ( void ) inb (0x61); + } +} + +/* Offsets to the device registers. */ +enum register_offsets { + StationAddr=0x00, RxConfig=0x06, TxConfig=0x07, ChipCmd=0x08, + IntrStatus=0x0C, IntrEnable=0x0E, + MulticastFilter0=0x10, MulticastFilter1=0x14, + RxRingPtr=0x18, TxRingPtr=0x1C, GFIFOTest=0x54, + MIIPhyAddr=0x6C, MIIStatus=0x6D, PCIBusConfig=0x6E, + MIICmd=0x70, MIIRegAddr=0x71, MIIData=0x72, MACRegEEcsr=0x74, + ConfigA=0x78, ConfigB=0x79, ConfigC=0x7A, ConfigD=0x7B, + RxMissed=0x7C, RxCRCErrs=0x7E, MiscCmd=0x81, + StickyHW=0x83, IntrStatus2=0x84, WOLcrClr=0xA4, WOLcgClr=0xA7, + PwrcsrClr=0xAC, +}; + +/* Bits in the interrupt status/mask registers. */ +enum intr_status_bits { + IntrRxDone=0x0001, IntrRxErr=0x0004, IntrRxEmpty=0x0020, + IntrTxDone=0x0002, IntrTxError=0x0008, IntrTxUnderrun=0x0210, + IntrPCIErr=0x0040, + IntrStatsMax=0x0080, IntrRxEarly=0x0100, + IntrRxOverflow=0x0400, IntrRxDropped=0x0800, IntrRxNoBuf=0x1000, + IntrTxAborted=0x2000, IntrLinkChange=0x4000, + IntrRxWakeUp=0x8000, + IntrNormalSummary=0x0003, IntrAbnormalSummary=0xC260, + IntrTxDescRace=0x080000, /* mapped from IntrStatus2 */ + IntrTxErrSummary=0x082218, +}; +#define DEFAULT_INTR (IntrRxDone | IntrRxErr | IntrRxEmpty| IntrRxOverflow | \ + IntrRxDropped | IntrRxNoBuf) + +/*************************************************************************** + IRQ - PXE IRQ Handler +***************************************************************************/ +void rhine_irq ( struct nic *nic, irq_action_t action ) { + struct rhine_private *tp = (struct rhine_private *) nic->priv_data; + /* Enable interrupts by setting the interrupt mask. */ + unsigned int intr_status; + + switch ( action ) { + case DISABLE : + case ENABLE : + intr_status = inw(nic->ioaddr + IntrStatus); + /* On Rhine-II, Bit 3 indicates Tx descriptor write-back race. */ + + /* added comment by guard */ + /* For supporting VT6107, please use revision id to recognize different chips in driver */ + // if (tp->chip_id == 0x3065) + if( tp->chip_revision < 0x80 && tp->chip_revision >=0x40 ) + intr_status |= inb(nic->ioaddr + IntrStatus2) << 16; + intr_status = (intr_status & ~DEFAULT_INTR); + if ( action == ENABLE ) + intr_status = intr_status | DEFAULT_INTR; + outw(intr_status, nic->ioaddr + IntrEnable); + break; + case FORCE : + outw(0x0010, nic->ioaddr + 0x84); + break; + } +} + +static struct nic_operations rhine_operations; + +static int +rhine_probe ( struct nic *nic, struct pci_device *pci ) { + + struct rhine_private *tp = (struct rhine_private *) nic->priv_data; + + if (!pci->ioaddr) + return 0; + + rhine_probe1 (nic, pci, pci->ioaddr, pci->device, -1); + + adjust_pci_device ( pci ); + + rhine_reset (nic); + + nic->nic_op = &rhine_operations; + + nic->irqno = pci->irq; + nic->ioaddr = tp->ioaddr; + + return 1; +} + +static void set_rx_mode(struct nic *nic __unused) { + struct rhine_private *tp = (struct rhine_private *) nic->priv_data; + unsigned char rx_mode; + int ioaddr = tp->ioaddr; + + /* ! IFF_PROMISC */ + outl(0xffffffff, byMAR0); + outl(0xffffffff, byMAR4); + rx_mode = 0x0C; + + outb(0x60 /* thresh */ | rx_mode, byRCR ); +} + +static void +rhine_probe1 (struct nic *nic, struct pci_device *pci, int ioaddr, int chip_id, int options) +{ + struct rhine_private *tp; + static int did_version = 0; /* Already printed version info. */ + unsigned int i, ww; + unsigned int timeout; + int FDXFlag; + int byMIIvalue, LineSpeed, MIICRbak; + uint8_t revision_id; + unsigned char mode3_reg; + + if (rhine_debug > 0 && did_version++ == 0) + printf ("%s",version); + + // get revision id. + pci_read_config_byte(pci, PCI_REVISION, &revision_id); + + /* D-Link provided reset code (with comment additions) */ + if (revision_id >= 0x40) { + unsigned char byOrgValue; + + if(rhine_debug > 0) + printf("Enabling Sticky Bit Workaround for Chip_id: 0x%hX\n" + , chip_id); + /* clear sticky bit before reset & read ethernet address */ + byOrgValue = inb(bySTICKHW); + byOrgValue = byOrgValue & 0xFC; + outb(byOrgValue, bySTICKHW); + + /* (bits written are cleared?) */ + /* disable force PME-enable */ + outb(0x80, byWOLcgClr); + /* disable power-event config bit */ + outb(0xFF, byWOLcrClr); + /* clear power status (undocumented in vt6102 docs?) */ + outb(0xFF, byPwrcsrClr); + + } + + /* Reset the chip to erase previous misconfiguration. */ + outw(CR_SFRST, byCR0); + // if vt3043 delay after reset + if (revision_id <0x40) { + udelay(10000); + } + // polling till software reset complete + // W_MAX_TIMEOUT is the timeout period + for(ww = 0; ww < W_MAX_TIMEOUT; ww++) { + if ((inw(byCR0) & CR_SFRST) == 0) + break; + } + + // issue AUTOLoad in EECSR to reload eeprom + outb(0x20, byEECSR ); + + // if vt3065 delay after reset + if (revision_id >=0x40) { + // delay 8ms to let MAC stable + mdelay(8); + /* + * for 3065D, EEPROM reloaded will cause bit 0 in MAC_REG_CFGA + * turned on. it makes MAC receive magic packet + * automatically. So, we turn it off. (D-Link) + */ + outb(inb(byCFGA) & 0xFE, byCFGA); + } + + /* turn on bit2 in PCI configuration register 0x53 , only for 3065*/ + if (revision_id >= 0x40) { + pci_read_config_byte(pci, PCI_REG_MODE3, &mode3_reg); + pci_write_config_byte(pci, PCI_REG_MODE3, mode3_reg|MODE3_MIION); + } + + + /* back off algorithm ,disable the right-most 4-bit off CFGD*/ + outb(inb(byCFGD) & (~(CFGD_RANDOM | CFGD_CFDX | CFGD_CEREN | CFGD_CETEN)), byCFGD); + + /* reload eeprom */ + reload_eeprom(ioaddr); + + /* Perhaps this should be read from the EEPROM? */ + for (i = 0; i < ETH_ALEN; i++) + nic->node_addr[i] = inb (byPAR0 + i); + + DBG ( "IO address %#hX Ethernet Address: %s\n", ioaddr, eth_ntoa ( nic->node_addr ) ); + + /* restart MII auto-negotiation */ + WriteMII (0, 9, 1, ioaddr); + printf ("Analyzing Media type,this may take several seconds... "); + for (i = 0; i < 5; i++) + { + /* need to wait 1 millisecond - we will round it up to 50-100ms */ + timeout = currticks() + 2; + for (timeout = currticks() + 2; currticks() < timeout;) + /* nothing */; + if (ReadMII (1, ioaddr) & 0x0020) + break; + } + printf ("OK.\n"); + +#if 0 + /* JJM : for Debug */ + printf("MII : Address %hhX ",inb(ioaddr+0x6c)); + { + unsigned char st1,st2,adv1,adv2,l1,l2; + + st1=ReadMII(1,ioaddr)>>8; + st2=ReadMII(1,ioaddr)&0xFF; + adv1=ReadMII(4,ioaddr)>>8; + adv2=ReadMII(4,ioaddr)&0xFF; + l1=ReadMII(5,ioaddr)>>8; + l2=ReadMII(5,ioaddr)&0xFF; + printf(" status 0x%hhX%hhX, advertising 0x%hhX%hhX, link 0x%hhX%hhX\n", st1,st2,adv1,adv2,l1,l2); + } +#endif + + + /* query MII to know LineSpeed,duplex mode */ + byMIIvalue = inb (ioaddr + 0x6d); + LineSpeed = byMIIvalue & MIISR_SPEED; + if (LineSpeed != 0) //JJM + { + printf ("Linespeed=10Mbs"); + } + else + { + printf ("Linespeed=100Mbs"); + } + + FDXFlag = QueryAuto (ioaddr); + if (FDXFlag == 1) + { + printf (" Fullduplex\n"); + outw (CR_FDX, byCR0); + } + else + { + printf (" Halfduplex\n"); + } + + + /* set MII 10 FULL ON, only apply in vt3043 */ + if(chip_id == 0x3043) + WriteMII (0x17, 1, 1, ioaddr); + + /* turn on MII link change */ + MIICRbak = inb (byMIICR); + outb (MIICRbak & 0x7F, byMIICR); + MIIDelay (); + outb (0x41, byMIIAD); + MIIDelay (); + + /* while((inb(byMIIAD)&0x20)==0) ; */ + outb (MIICRbak | 0x80, byMIICR); + + nic->priv_data = &rhine; + tp = &rhine; + tp->chip_id = chip_id; + tp->ioaddr = ioaddr; + tp->phys[0] = -1; + tp->chip_revision = revision_id; + + /* The lower four bits are the media type. */ + if (options > 0) + { + tp->full_duplex = (options & 16) ? 1 : 0; + tp->default_port = options & 15; + if (tp->default_port) + tp->medialock = 1; + } + return; +} + +static void +rhine_disable ( struct nic *nic ) { + + struct rhine_private *tp = (struct rhine_private *) nic->priv_data; + int ioaddr = tp->ioaddr; + + rhine_reset(nic); + + printf ("rhine disable\n"); + /* Switch to loopback mode to avoid hardware races. */ + outb(0x60 | 0x01, byTCR); + /* Stop the chip's Tx and Rx processes. */ + outw(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; + void *rx_ring_tmp; + void *tx_ring_tmp; + void *rx_bufs_tmp; + void *tx_bufs_tmp; + unsigned long rx_ring_tmp1; + unsigned long tx_ring_tmp1; + unsigned long rx_bufs_tmp1; + unsigned long tx_bufs_tmp1; + + /* printf ("rhine_reset\n"); */ + /* Soft reset the chip. */ + /*outb(CmdReset, ioaddr + ChipCmd); */ + + tx_bufs_tmp = rhine_buffers.txbuf; + tx_ring_tmp = rhine_buffers.txdesc; + rx_bufs_tmp = rhine_buffers.rxbuf; + rx_ring_tmp = rhine_buffers.rxdesc; + + /* tune RD TD 32 byte alignment */ + rx_ring_tmp1 = virt_to_bus ( 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 = virt_to_bus ( 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 = virt_to_bus ( tx_bufs_tmp ); + j = (int) (tx_bufs_tmp1 + 32) & (~0x1f); + tx_bufs_tmp = bus_to_virt (j); + /* printf ("txb[%X]", j); */ + + rx_bufs_tmp1 = virt_to_bus ( rx_bufs_tmp ); + j = (int) (rx_bufs_tmp1 + 32) & (~0x1f); + rx_bufs_tmp = 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 { inb(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; + unsigned long ct; + + + /*printf ("rhine_transmit\n"); */ + /* setup ethernet header */ + + + /* Calculate the next Tx descriptor entry. */ + entry = tp->cur_tx % TX_RING_SIZE; + + memcpy (tp->tx_buffs[entry], d, ETH_ALEN); /* dst */ + memcpy (tp->tx_buffs[entry] + ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */ + + nstype=htons(t); + memcpy(tp->tx_buffs[entry] + 2 * ETH_ALEN, (char*)&nstype, 2); + + memcpy (tp->tx_buffs[entry] + ETH_HLEN, p, s); + s += ETH_HLEN; + while (s < ETH_ZLEN) + *((char *) tp->tx_buffs[entry] + (s++)) = 0; + + tp->tx_ring[entry].tx_ctrl.bits.tx_buf_size = s; + + tp->tx_ring[entry].tx_status.bits.own_bit = 1; + + + CR1bak = inb (byCR1); + + CR1bak = CR1bak | CR1_TDMD1; + /*printf("tdsw=[%X]",tp->tx_ring[entry].tx_status.lw); */ + /*printf("tdcw=[%X]",tp->tx_ring[entry].tx_ctrl.lw); */ + /*printf("tdbuf1=[%X]",tp->tx_ring[entry].buf_addr_1); */ + /*printf("tdbuf2=[%X]",tp->tx_ring[entry].buf_addr_2); */ + /*printf("td1=[%X]",inl(dwCurrentTDSE0)); */ + /*printf("td2=[%X]",inl(dwCurrentTDSE1)); */ + /*printf("td3=[%X]",inl(dwCurrentTDSE2)); */ + /*printf("td4=[%X]",inl(dwCurrentTDSE3)); */ + + outb (CR1bak, byCR1); + do + { + ct = currticks(); + /* Wait until transmit is finished or timeout*/ + while((tp->tx_ring[entry].tx_status.bits.own_bit !=0) && + ct + 10*1000 < currticks()) + ; + + if(tp->tx_ring[entry].tx_status.bits.terr == 0) + break; + + if(tp->tx_ring[entry].tx_status.bits.abt == 1) + { + // turn on TX + CR0bak = inb(byCR0); + CR0bak = CR0bak|CR_TXON; + outb(CR0bak,byCR0); + } + }while(0); + tp->cur_tx++; + + /*outw(IMRShadow,byIMR0); */ + /*dev_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE); */ + /*tp->tx_skbuff[entry] = 0; */ +} + +static struct nic_operations rhine_operations = { + .connect = dummy_connect, + .poll = rhine_poll, + .transmit = rhine_transmit, + .irq = rhine_irq, + +}; + +static struct pci_device_id rhine_nics[] = { +PCI_ROM(0x1106, 0x3065, "dlink-530tx", "VIA 6102"), +PCI_ROM(0x1106, 0x3106, "via-rhine-6105", "VIA 6105"), +PCI_ROM(0x1106, 0x3043, "dlink-530tx-old", "VIA 3043"), /* Rhine-I 86c100a */ +PCI_ROM(0x1106, 0x3053, "via6105m", "VIA 6105M"), +PCI_ROM(0x1106, 0x6100, "via-rhine-old", "VIA 86C100A"), /* Rhine-II */ +}; + +PCI_DRIVER ( rhine_driver, rhine_nics, PCI_NO_CLASS ); + +DRIVER ( "VIA 86C100", nic_driver, pci_driver, rhine_driver, + rhine_probe, rhine_disable ); + +/* EOF via-rhine.c */ + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/via-velocity.c b/gpxe/src/drivers/net/via-velocity.c new file mode 100644 index 0000000..4473ed1 --- /dev/null +++ b/gpxe/src/drivers/net/via-velocity.c @@ -0,0 +1,1939 @@ +/************************************************************************** +* via-velocity.c: Etherboot device driver for the VIA 6120 Gigabit +* Changes for Etherboot port: +* Copyright (c) 2006 by Timothy Legge +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* This driver is based on: +* via-velocity.c: VIA Velocity VT6120, VT6122 Ethernet driver +* The changes are (c) Copyright 2004, Red Hat Inc. +* +* Additional fixes and clean up: Francois Romieu +* +* Original code: +* Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. +* All rights reserved. +* Author: Chuang Liang-Shing, AJ Jiang +* +* Linux Driver Version 2.6.15.4 +* +* REVISION HISTORY: +* ================ +* +* v1.0 03-06-2006 timlegge Initial port of Linux driver +* +* Indent Options: indent -kr -i8 +*************************************************************************/ + +#include "etherboot.h" +#include "nic.h" +#include +#include + +#include "via-velocity.h" + +typedef int pci_power_t; + +#define PCI_D0 ((int) 0) +#define PCI_D1 ((int) 1) +#define PCI_D2 ((int) 2) +#define PCI_D3hot ((int) 3) +#define PCI_D3cold ((int) 4) +#define PCI_POWER_ERROR ((int) -1) + + +/* Condensed operations for readability. */ +#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr)) + +//FIXME: Move to pci.c +int pci_set_power_state(struct pci_device *dev, int state); + +/* FIXME: Move BASE to the private structure */ +static u32 BASE; + +/* NIC specific static variables go here */ +#define VELOCITY_PARAM(N,D) \ + static const int N[MAX_UNITS]=OPTION_DEFAULT; +/* MODULE_PARM(N, "1-" __MODULE_STRING(MAX_UNITS) "i");\ + MODULE_PARM_DESC(N, D); */ + +VELOCITY_PARAM(RxDescriptors, "Number of receive descriptors"); +VELOCITY_PARAM(TxDescriptors, "Number of transmit descriptors"); + + +#define VLAN_ID_MIN 0 +#define VLAN_ID_MAX 4095 +#define VLAN_ID_DEF 0 +/* VID_setting[] is used for setting the VID of NIC. + 0: default VID. + 1-4094: other VIDs. +*/ +VELOCITY_PARAM(VID_setting, "802.1Q VLAN ID"); + +#define RX_THRESH_MIN 0 +#define RX_THRESH_MAX 3 +#define RX_THRESH_DEF 0 +/* rx_thresh[] is used for controlling the receive fifo threshold. + 0: indicate the rxfifo threshold is 128 bytes. + 1: indicate the rxfifo threshold is 512 bytes. + 2: indicate the rxfifo threshold is 1024 bytes. + 3: indicate the rxfifo threshold is store & forward. +*/ +VELOCITY_PARAM(rx_thresh, "Receive fifo threshold"); + +#define DMA_LENGTH_MIN 0 +#define DMA_LENGTH_MAX 7 +#define DMA_LENGTH_DEF 0 + +/* DMA_length[] is used for controlling the DMA length + 0: 8 DWORDs + 1: 16 DWORDs + 2: 32 DWORDs + 3: 64 DWORDs + 4: 128 DWORDs + 5: 256 DWORDs + 6: SF(flush till emply) + 7: SF(flush till emply) +*/ +VELOCITY_PARAM(DMA_length, "DMA length"); + +#define TAGGING_DEF 0 +/* enable_tagging[] is used for enabling 802.1Q VID tagging. + 0: disable VID seeting(default). + 1: enable VID setting. +*/ +VELOCITY_PARAM(enable_tagging, "Enable 802.1Q tagging"); + +#define IP_ALIG_DEF 0 +/* IP_byte_align[] is used for IP header DWORD byte aligned + 0: indicate the IP header won't be DWORD byte aligned.(Default) . + 1: indicate the IP header will be DWORD byte aligned. + In some enviroment, the IP header should be DWORD byte aligned, + or the packet will be droped when we receive it. (eg: IPVS) +*/ +VELOCITY_PARAM(IP_byte_align, "Enable IP header dword aligned"); + +#define TX_CSUM_DEF 1 +/* txcsum_offload[] is used for setting the checksum offload ability of NIC. + (We only support RX checksum offload now) + 0: disable csum_offload[checksum offload + 1: enable checksum offload. (Default) +*/ +VELOCITY_PARAM(txcsum_offload, "Enable transmit packet checksum offload"); + +#define FLOW_CNTL_DEF 1 +#define FLOW_CNTL_MIN 1 +#define FLOW_CNTL_MAX 5 + +/* flow_control[] is used for setting the flow control ability of NIC. + 1: hardware deafult - AUTO (default). Use Hardware default value in ANAR. + 2: enable TX flow control. + 3: enable RX flow control. + 4: enable RX/TX flow control. + 5: disable +*/ +VELOCITY_PARAM(flow_control, "Enable flow control ability"); + +#define MED_LNK_DEF 0 +#define MED_LNK_MIN 0 +#define MED_LNK_MAX 4 +/* speed_duplex[] is used for setting the speed and duplex mode of NIC. + 0: indicate autonegotiation for both speed and duplex mode + 1: indicate 100Mbps half duplex mode + 2: indicate 100Mbps full duplex mode + 3: indicate 10Mbps half duplex mode + 4: indicate 10Mbps full duplex mode + + Note: + if EEPROM have been set to the force mode, this option is ignored + by driver. +*/ +VELOCITY_PARAM(speed_duplex, "Setting the speed and duplex mode"); + +#define VAL_PKT_LEN_DEF 0 +/* ValPktLen[] is used for setting the checksum offload ability of NIC. + 0: Receive frame with invalid layer 2 length (Default) + 1: Drop frame with invalid layer 2 length +*/ +VELOCITY_PARAM(ValPktLen, "Receiving or Drop invalid 802.3 frame"); + +#define WOL_OPT_DEF 0 +#define WOL_OPT_MIN 0 +#define WOL_OPT_MAX 7 +/* wol_opts[] is used for controlling wake on lan behavior. + 0: Wake up if recevied a magic packet. (Default) + 1: Wake up if link status is on/off. + 2: Wake up if recevied an arp packet. + 4: Wake up if recevied any unicast packet. + Those value can be sumed up to support more than one option. +*/ +VELOCITY_PARAM(wol_opts, "Wake On Lan options"); + +#define INT_WORKS_DEF 20 +#define INT_WORKS_MIN 10 +#define INT_WORKS_MAX 64 + +VELOCITY_PARAM(int_works, "Number of packets per interrupt services"); + +/* The descriptors for this card are required to be aligned on +64 byte boundaries. As the align attribute does not guarantee alignment +greater than the alignment of the start address (which for Etherboot +is 16 bytes of alignment) it requires some extra steps. Add 64 to the +size of the array and the init_ring adjusts the alignment */ + +/* Define the TX Descriptor */ +static u8 tx_ring[TX_DESC_DEF * sizeof(struct tx_desc) + 64]; + +/* Create a static buffer of size PKT_BUF_SZ for each TX Descriptor. +All descriptors point to a part of this buffer */ +static u8 txb[(TX_DESC_DEF * PKT_BUF_SZ) + 64]; + +/* Define the RX Descriptor */ +static u8 rx_ring[RX_DESC_DEF * sizeof(struct rx_desc) + 64]; + +/* Create a static buffer of size PKT_BUF_SZ for each RX Descriptor + All descriptors point to a part of this buffer */ +static u8 rxb[(RX_DESC_DEF * PKT_BUF_SZ) + 64]; + +static void velocity_init_info(struct pci_device *pdev, + struct velocity_info *vptr, + struct velocity_info_tbl *info); +static int velocity_get_pci_info(struct velocity_info *, + struct pci_device *pdev); +static int velocity_open(struct nic *nic, struct pci_device *pci); + +static int velocity_soft_reset(struct velocity_info *vptr); +static void velocity_init_cam_filter(struct velocity_info *vptr); +static void mii_init(struct velocity_info *vptr, u32 mii_status); +static u32 velocity_get_opt_media_mode(struct velocity_info *vptr); +static void velocity_print_link_status(struct velocity_info *vptr); +static void safe_disable_mii_autopoll(struct mac_regs *regs); +static void enable_flow_control_ability(struct velocity_info *vptr); +static void enable_mii_autopoll(struct mac_regs *regs); +static int velocity_mii_read(struct mac_regs *, u8 byIdx, u16 * pdata); +static int velocity_mii_write(struct mac_regs *, u8 byMiiAddr, u16 data); +static u32 mii_check_media_mode(struct mac_regs *regs); +static u32 check_connection_type(struct mac_regs *regs); +static int velocity_set_media_mode(struct velocity_info *vptr, + u32 mii_status); + + +/* + * Internal board variants. At the moment we have only one + */ + +static struct velocity_info_tbl chip_info_table[] = { + {CHIP_TYPE_VT6110, + "VIA Networking Velocity Family Gigabit Ethernet Adapter", 256, 1, + 0x00FFFFFFUL}, + {0, NULL, 0, 0, 0} +}; + +/** + * velocity_set_int_opt - parser for integer options + * @opt: pointer to option value + * @val: value the user requested (or -1 for default) + * @min: lowest value allowed + * @max: highest value allowed + * @def: default value + * @name: property name + * @dev: device name + * + * Set an integer property in the module options. This function does + * all the verification and checking as well as reporting so that + * we don't duplicate code for each option. + */ + +static void velocity_set_int_opt(int *opt, int val, int min, int max, + int def, char *name, const char *devname) +{ + if (val == -1) { + printf("%s: set value of parameter %s to %d\n", + devname, name, def); + *opt = def; + } else if (val < min || val > max) { + printf + ("%s: the value of parameter %s is invalid, the valid range is (%d-%d)\n", + devname, name, min, max); + *opt = def; + } else { + printf("%s: set value of parameter %s to %d\n", + devname, name, val); + *opt = val; + } +} + +/** + * velocity_set_bool_opt - parser for boolean options + * @opt: pointer to option value + * @val: value the user requested (or -1 for default) + * @def: default value (yes/no) + * @flag: numeric value to set for true. + * @name: property name + * @dev: device name + * + * Set a boolean property in the module options. This function does + * all the verification and checking as well as reporting so that + * we don't duplicate code for each option. + */ + +static void velocity_set_bool_opt(u32 * opt, int val, int def, u32 flag, + char *name, const char *devname) +{ + (*opt) &= (~flag); + if (val == -1) { + printf("%s: set parameter %s to %s\n", + devname, name, def ? "TRUE" : "FALSE"); + *opt |= (def ? flag : 0); + } else if (val < 0 || val > 1) { + printf + ("%s: the value of parameter %s is invalid, the valid range is (0-1)\n", + devname, name); + *opt |= (def ? flag : 0); + } else { + printf("%s: set parameter %s to %s\n", + devname, name, val ? "TRUE" : "FALSE"); + *opt |= (val ? flag : 0); + } +} + +/** + * velocity_get_options - set options on device + * @opts: option structure for the device + * @index: index of option to use in module options array + * @devname: device name + * + * Turn the module and command options into a single structure + * for the current device + */ + +static void velocity_get_options(struct velocity_opt *opts, int index, + const char *devname) +{ + + /* FIXME Do the options need to be configurable */ + velocity_set_int_opt(&opts->rx_thresh, -1, RX_THRESH_MIN, + RX_THRESH_MAX, RX_THRESH_DEF, "rx_thresh", + devname); + velocity_set_int_opt(&opts->DMA_length, DMA_length[index], + DMA_LENGTH_MIN, DMA_LENGTH_MAX, + DMA_LENGTH_DEF, "DMA_length", devname); + velocity_set_int_opt(&opts->numrx, RxDescriptors[index], + RX_DESC_MIN, RX_DESC_MAX, RX_DESC_DEF, + "RxDescriptors", devname); + velocity_set_int_opt(&opts->numtx, TxDescriptors[index], + TX_DESC_MIN, TX_DESC_MAX, TX_DESC_DEF, + "TxDescriptors", devname); + velocity_set_int_opt(&opts->vid, VID_setting[index], VLAN_ID_MIN, + VLAN_ID_MAX, VLAN_ID_DEF, "VID_setting", + devname); + velocity_set_bool_opt(&opts->flags, enable_tagging[index], + TAGGING_DEF, VELOCITY_FLAGS_TAGGING, + "enable_tagging", devname); + velocity_set_bool_opt(&opts->flags, txcsum_offload[index], + TX_CSUM_DEF, VELOCITY_FLAGS_TX_CSUM, + "txcsum_offload", devname); + velocity_set_int_opt(&opts->flow_cntl, flow_control[index], + FLOW_CNTL_MIN, FLOW_CNTL_MAX, FLOW_CNTL_DEF, + "flow_control", devname); + velocity_set_bool_opt(&opts->flags, IP_byte_align[index], + IP_ALIG_DEF, VELOCITY_FLAGS_IP_ALIGN, + "IP_byte_align", devname); + velocity_set_bool_opt(&opts->flags, ValPktLen[index], + VAL_PKT_LEN_DEF, VELOCITY_FLAGS_VAL_PKT_LEN, + "ValPktLen", devname); + velocity_set_int_opt((void *) &opts->spd_dpx, speed_duplex[index], + MED_LNK_MIN, MED_LNK_MAX, MED_LNK_DEF, + "Media link mode", devname); + velocity_set_int_opt((int *) &opts->wol_opts, wol_opts[index], + WOL_OPT_MIN, WOL_OPT_MAX, WOL_OPT_DEF, + "Wake On Lan options", devname); + velocity_set_int_opt((int *) &opts->int_works, int_works[index], + INT_WORKS_MIN, INT_WORKS_MAX, INT_WORKS_DEF, + "Interrupt service works", devname); + opts->numrx = (opts->numrx & ~3); +} + +/** + * velocity_init_cam_filter - initialise CAM + * @vptr: velocity to program + * + * Initialize the content addressable memory used for filters. Load + * appropriately according to the presence of VLAN + */ + +static void velocity_init_cam_filter(struct velocity_info *vptr) +{ + struct mac_regs *regs = vptr->mac_regs; + + /* Turn on MCFG_PQEN, turn off MCFG_RTGOPT */ + WORD_REG_BITS_SET(MCFG_PQEN, MCFG_RTGOPT, ®s->MCFG); + WORD_REG_BITS_ON(MCFG_VIDFR, ®s->MCFG); + + /* Disable all CAMs */ + memset(vptr->vCAMmask, 0, sizeof(u8) * 8); + memset(vptr->mCAMmask, 0, sizeof(u8) * 8); + mac_set_cam_mask(regs, vptr->vCAMmask, VELOCITY_VLAN_ID_CAM); + mac_set_cam_mask(regs, vptr->mCAMmask, VELOCITY_MULTICAST_CAM); + + /* Enable first VCAM */ + if (vptr->flags & VELOCITY_FLAGS_TAGGING) { + /* If Tagging option is enabled and VLAN ID is not zero, then + turn on MCFG_RTGOPT also */ + if (vptr->options.vid != 0) + WORD_REG_BITS_ON(MCFG_RTGOPT, ®s->MCFG); + + mac_set_cam(regs, 0, (u8 *) & (vptr->options.vid), + VELOCITY_VLAN_ID_CAM); + vptr->vCAMmask[0] |= 1; + mac_set_cam_mask(regs, vptr->vCAMmask, + VELOCITY_VLAN_ID_CAM); + } else { + u16 temp = 0; + mac_set_cam(regs, 0, (u8 *) & temp, VELOCITY_VLAN_ID_CAM); + temp = 1; + mac_set_cam_mask(regs, (u8 *) & temp, + VELOCITY_VLAN_ID_CAM); + } +} + +static inline void velocity_give_many_rx_descs(struct velocity_info *vptr) +{ + struct mac_regs *regs = vptr->mac_regs; + int avail, dirty, unusable; + + /* + * RD number must be equal to 4X per hardware spec + * (programming guide rev 1.20, p.13) + */ + if (vptr->rd_filled < 4) + return; + + wmb(); + + unusable = vptr->rd_filled & 0x0003; + dirty = vptr->rd_dirty - unusable; + for (avail = vptr->rd_filled & 0xfffc; avail; avail--) { + dirty = (dirty > 0) ? dirty - 1 : vptr->options.numrx - 1; +// printf("return dirty: %d\n", dirty); + vptr->rd_ring[dirty].rdesc0.owner = OWNED_BY_NIC; + } + + writew(vptr->rd_filled & 0xfffc, ®s->RBRDU); + vptr->rd_filled = unusable; +} + +static int velocity_rx_refill(struct velocity_info *vptr) +{ + int dirty = vptr->rd_dirty, done = 0, ret = 0; + +// printf("rx_refill - rd_curr = %d, dirty = %d\n", vptr->rd_curr, dirty); + do { + struct rx_desc *rd = vptr->rd_ring + dirty; + + /* Fine for an all zero Rx desc at init time as well */ + if (rd->rdesc0.owner == OWNED_BY_NIC) + break; +// printf("rx_refill - after owner %d\n", dirty); + + rd->inten = 1; + rd->pa_high = 0; + rd->rdesc0.len = cpu_to_le32(vptr->rx_buf_sz);; + + done++; + dirty = (dirty < vptr->options.numrx - 1) ? dirty + 1 : 0; + } while (dirty != vptr->rd_curr); + + if (done) { +// printf("\nGive Back Desc\n"); + vptr->rd_dirty = dirty; + vptr->rd_filled += done; + velocity_give_many_rx_descs(vptr); + } + + return ret; +} + +extern void hex_dump(const char *data, const unsigned int len); +/************************************************************************** +POLL - Wait for a frame +***************************************************************************/ +static int velocity_poll(struct nic *nic, int retrieve) +{ + /* Work out whether or not there's an ethernet packet ready to + * read. Return 0 if not. + */ + + int rd_curr = vptr->rd_curr % RX_DESC_DEF; + struct rx_desc *rd = &(vptr->rd_ring[rd_curr]); + + if (rd->rdesc0.owner == OWNED_BY_NIC) + return 0; + rmb(); + + if ( ! retrieve ) return 1; + + /* + * Don't drop CE or RL error frame although RXOK is off + */ + if ((rd->rdesc0.RSR & RSR_RXOK) + || (!(rd->rdesc0.RSR & RSR_RXOK) + && (rd->rdesc0.RSR & (RSR_CE | RSR_RL)))) { + + nic->packetlen = rd->rdesc0.len; + // ptr->rxb + (rd_curr * PKT_BUF_SZ) + memcpy(nic->packet, bus_to_virt(rd->pa_low), + nic->packetlen - 4); + + vptr->rd_curr++; + vptr->rd_curr = vptr->rd_curr % RX_DESC_DEF; + velocity_rx_refill(vptr); + return 1; /* Remove this line once this method is implemented */ + } + return 0; +} + +#define TX_TIMEOUT (1000); +/************************************************************************** +TRANSMIT - Transmit a frame +***************************************************************************/ +static void velocity_transmit(struct nic *nic, const char *dest, /* Destination */ + unsigned int type, /* Type */ + unsigned int size, /* size */ + const char *packet) +{ /* Packet */ + u16 nstype; + u32 to; + u8 *ptxb; + unsigned int pktlen; + struct tx_desc *td_ptr; + + int entry = vptr->td_curr % TX_DESC_DEF; + td_ptr = &(vptr->td_rings[entry]); + + /* point to the current txb incase multiple tx_rings are used */ + ptxb = vptr->txb + (entry * PKT_BUF_SZ); + memcpy(ptxb, dest, ETH_ALEN); /* Destination */ + memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN); /* Source */ + nstype = htons((u16) type); /* Type */ + memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2); /* Type */ + memcpy(ptxb + ETH_HLEN, packet, size); + + td_ptr->tdesc1.TCPLS = TCPLS_NORMAL; + td_ptr->tdesc1.TCR = TCR0_TIC; + td_ptr->td_buf[0].queue = 0; + + size += ETH_HLEN; + while (size < ETH_ZLEN) /* pad to min length */ + ptxb[size++] = '\0'; + + if (size < ETH_ZLEN) { +// printf("Padd that packet\n"); + pktlen = ETH_ZLEN; +// memcpy(ptxb, skb->data, skb->len); + memset(ptxb + size, 0, ETH_ZLEN - size); + + vptr->td_rings[entry].tdesc0.pktsize = pktlen; + vptr->td_rings[entry].td_buf[0].pa_low = virt_to_bus(ptxb); + vptr->td_rings[entry].td_buf[0].pa_high &= + cpu_to_le32(0xffff0000UL); + vptr->td_rings[entry].td_buf[0].bufsize = + vptr->td_rings[entry].tdesc0.pktsize; + vptr->td_rings[entry].tdesc1.CMDZ = 2; + } else { +// printf("Correct size packet\n"); + td_ptr->tdesc0.pktsize = size; + td_ptr->td_buf[0].pa_low = virt_to_bus(ptxb); + td_ptr->td_buf[0].pa_high = 0; + td_ptr->td_buf[0].bufsize = td_ptr->tdesc0.pktsize; +// tdinfo->nskb_dma = 1; + td_ptr->tdesc1.CMDZ = 2; + } + + if (vptr->flags & VELOCITY_FLAGS_TAGGING) { + td_ptr->tdesc1.pqinf.VID = (vptr->options.vid & 0xfff); + td_ptr->tdesc1.pqinf.priority = 0; + td_ptr->tdesc1.pqinf.CFI = 0; + td_ptr->tdesc1.TCR |= TCR0_VETAG; + } + + vptr->td_curr = (entry + 1); + + { + + int prev = entry - 1; + + if (prev < 0) + prev = TX_DESC_DEF - 1; + td_ptr->tdesc0.owner |= OWNED_BY_NIC; + td_ptr = &(vptr->td_rings[prev]); + td_ptr->td_buf[0].queue = 1; + mac_tx_queue_wake(vptr->mac_regs, 0); + + } + + to = currticks() + TX_TIMEOUT; + while ((td_ptr->tdesc0.owner & OWNED_BY_NIC) && (currticks() < to)); /* wait */ + + if (currticks() >= to) { + printf("TX Time Out"); + } + +} + +/************************************************************************** +DISABLE - Turn off ethernet interface +***************************************************************************/ +static void velocity_disable(struct nic *nic __unused) +{ + /* put the card in its initial state */ + /* This function serves 3 purposes. + * This disables DMA and interrupts so we don't receive + * unexpected packets or interrupts from the card after + * etherboot has finished. + * This frees resources so etherboot may use + * this driver on another interface + * This allows etherboot to reinitialize the interface + * if something is something goes wrong. + */ + struct mac_regs *regs = vptr->mac_regs; + mac_disable_int(regs); + writel(CR0_STOP, ®s->CR0Set); + writew(0xFFFF, ®s->TDCSRClr); + writeb(0xFF, ®s->RDCSRClr); + safe_disable_mii_autopoll(regs); + mac_clear_isr(regs); + + /* Power down the chip */ +// pci_set_power_state(vptr->pdev, PCI_D3hot); + + vptr->flags &= (~VELOCITY_FLAGS_OPENED); +} + +/************************************************************************** +IRQ - handle interrupts +***************************************************************************/ +static void velocity_irq(struct nic *nic __unused, irq_action_t action) +{ + /* This routine is somewhat optional. Etherboot itself + * doesn't use interrupts, but they are required under some + * circumstances when we're acting as a PXE stack. + * + * If you don't implement this routine, the only effect will + * be that your driver cannot be used via Etherboot's UNDI + * API. This won't affect programs that use only the UDP + * portion of the PXE API, such as pxelinux. + */ + + switch (action) { + case DISABLE: + case ENABLE: + /* Set receive interrupt enabled/disabled state */ + /* + outb ( action == ENABLE ? IntrMaskEnabled : IntrMaskDisabled, + nic->ioaddr + IntrMaskRegister ); + */ + break; + case FORCE: + /* Force NIC to generate a receive interrupt */ + /* + outb ( ForceInterrupt, nic->ioaddr + IntrForceRegister ); + */ + break; + } +} + +static struct nic_operations velocity_operations = { + .connect = dummy_connect, + .poll = velocity_poll, + .transmit = velocity_transmit, + .irq = velocity_irq, +}; + +/************************************************************************** +PROBE - Look for an adapter, this routine's visible to the outside +***************************************************************************/ +static int velocity_probe( struct nic *nic, struct pci_device *pci) +{ + int ret, i; + struct mac_regs *regs; + + printf("via-velocity.c: Found %s Vendor=0x%hX Device=0x%hX\n", + pci->driver_name, pci->vendor, pci->device); + + /* point to private storage */ + vptr = &vptx; + info = chip_info_table; + + velocity_init_info(pci, vptr, info); + +//FIXME: pci_enable_device(pci); +//FIXME: pci_set_power_state(pci, PCI_D0); + + ret = velocity_get_pci_info(vptr, pci); + if (ret < 0) { + printf("Failed to find PCI device.\n"); + return 0; + } + + regs = ioremap(vptr->memaddr, vptr->io_size); + if (regs == NULL) { + printf("Unable to remap io\n"); + return 0; + } + + vptr->mac_regs = regs; + + BASE = vptr->ioaddr; + + printf("Chip ID: %hX\n", vptr->chip_id); + + for (i = 0; i < 6; i++) + nic->node_addr[i] = readb(®s->PAR[i]); + + DBG ( "%s: %s at ioaddr %#hX\n", pci->driver_name, eth_ntoa ( nic->node_addr ), + (unsigned int) BASE ); + + velocity_get_options(&vptr->options, 0, pci->driver_name); + + /* + * Mask out the options cannot be set to the chip + */ + vptr->options.flags &= 0x00FFFFFFUL; //info->flags = 0x00FFFFFFUL; + + /* + * Enable the chip specified capbilities + */ + + vptr->flags = + vptr->options. + flags | (0x00FFFFFFUL /*info->flags */ & 0xFF000000UL); + + vptr->wol_opts = vptr->options.wol_opts; + vptr->flags |= VELOCITY_FLAGS_WOL_ENABLED; + + vptr->phy_id = MII_GET_PHY_ID(vptr->mac_regs); + + if (vptr->flags & VELOCITY_FLAGS_TX_CSUM) { + printf("features missing\n"); + } + + /* and leave the chip powered down */ +// FIXME: pci_set_power_state(pci, PCI_D3hot); + + check_connection_type(vptr->mac_regs); + velocity_open(nic, pci); + + /* store NIC parameters */ + nic->nic_op = &velocity_operations; + return 1; +} + +//#define IORESOURCE_IO 0x00000100 /* Resource type */ + +/** + * velocity_init_info - init private data + * @pdev: PCI device + * @vptr: Velocity info + * @info: Board type + * + * Set up the initial velocity_info struct for the device that has been + * discovered. + */ + +static void velocity_init_info(struct pci_device *pdev, + struct velocity_info *vptr, + struct velocity_info_tbl *info) +{ + memset(vptr, 0, sizeof(struct velocity_info)); + + vptr->pdev = pdev; + vptr->chip_id = info->chip_id; + vptr->io_size = info->io_size; + vptr->num_txq = info->txqueue; + vptr->multicast_limit = MCAM_SIZE; + + printf + ("chip_id: 0x%hX, io_size: %d, num_txq %d, multicast_limit: %d\n", + vptr->chip_id, (unsigned int) vptr->io_size, vptr->num_txq, + vptr->multicast_limit); + printf("Name: %s\n", info->name); + +// spin_lock_init(&vptr->lock); +// INIT_LIST_HEAD(&vptr->list); +} + +/** + * velocity_get_pci_info - retrieve PCI info for device + * @vptr: velocity device + * @pdev: PCI device it matches + * + * Retrieve the PCI configuration space data that interests us from + * the kernel PCI layer + */ + +#define IORESOURCE_IO 0x00000100 /* Resource type */ +#define IORESOURCE_PREFETCH 0x00001000 /* No side effects */ + +#define IORESOURCE_MEM 0x00000200 +#define BAR_0 0 +#define BAR_1 1 +#define BAR_5 5 +#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ +#define PCI_BASE_ADDRESS_SPACE_IO 0x01 +#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 +#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06 +#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ +#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ +#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ +#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */ +//#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL) +// #define PCI_BASE_ADDRESS_IO_MASK (~0x03UL) + +unsigned long pci_resource_flags(struct pci_device *pdev, unsigned int bar) +{ + uint32_t l, sz; + unsigned long flags = 0; + + pci_read_config_dword(pdev, bar, &l); + pci_write_config_dword(pdev, bar, ~0); + pci_read_config_dword(pdev, bar, &sz); + pci_write_config_dword(pdev, bar, l); + + if (!sz || sz == 0xffffffff) + printf("Weird size\n"); + if (l == 0xffffffff) + l = 0; + if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) { + /* sz = pci_size(l, sz, PCI_BASE_ADDRESS_MEM_MASK); + if (!sz) + continue; + res->start = l & PCI_BASE_ADDRESS_MEM_MASK; + */ flags |= l & ~PCI_BASE_ADDRESS_MEM_MASK; + printf("Memory Resource\n"); + } else { + // sz = pci_size(l, sz, PCI_BASE_ADDRESS_IO_MASK & 0xffff); + /// if (!sz) + /// continue; +// res->start = l & PCI_BASE_ADDRESS_IO_MASK; + flags |= l & ~PCI_BASE_ADDRESS_IO_MASK; + printf("I/O Resource\n"); + } + if (flags & PCI_BASE_ADDRESS_SPACE_IO) { + printf("Why is it here\n"); + flags |= IORESOURCE_IO; + } else { + printf("here\n"); +//flags &= ~IORESOURCE_IO; + } + + + if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH) + flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH; + + + return flags; +} +static int velocity_get_pci_info(struct velocity_info *vptr, + struct pci_device *pdev) +{ + if (pci_read_config_byte(pdev, PCI_REVISION_ID, &vptr->rev_id) < 0) { + printf("DEBUG: pci_read_config_byte failed\n"); + return -1; + } + + adjust_pci_device(pdev); + + vptr->ioaddr = pci_bar_start(pdev, PCI_BASE_ADDRESS_0); + vptr->memaddr = pci_bar_start(pdev, PCI_BASE_ADDRESS_1); + + printf("Looking for I/O Resource - Found:"); + if (! + (pci_resource_flags(pdev, PCI_BASE_ADDRESS_0) & IORESOURCE_IO)) + { + printf + ("DEBUG: region #0 is not an I/O resource, aborting.\n"); + return -1; + } + + printf("Looking for Memory Resource - Found:"); + if ((pci_resource_flags(pdev, PCI_BASE_ADDRESS_1) & IORESOURCE_IO)) { + printf("DEBUG: region #1 is an I/O resource, aborting.\n"); + return -1; + } + + if (pci_bar_size(pdev, PCI_BASE_ADDRESS_1) < 256) { + printf("DEBUG: region #1 is too small.\n"); + return -1; + } + vptr->pdev = pdev; + + return 0; +} + +/** + * velocity_print_link_status - link status reporting + * @vptr: velocity to report on + * + * Turn the link status of the velocity card into a kernel log + * description of the new link state, detailing speed and duplex + * status + */ + +static void velocity_print_link_status(struct velocity_info *vptr) +{ + + if (vptr->mii_status & VELOCITY_LINK_FAIL) { + printf("failed to detect cable link\n"); + } else if (vptr->options.spd_dpx == SPD_DPX_AUTO) { + printf("Link autonegation"); + + if (vptr->mii_status & VELOCITY_SPEED_1000) + printf(" speed 1000M bps"); + else if (vptr->mii_status & VELOCITY_SPEED_100) + printf(" speed 100M bps"); + else + printf(" speed 10M bps"); + + if (vptr->mii_status & VELOCITY_DUPLEX_FULL) + printf(" full duplex\n"); + else + printf(" half duplex\n"); + } else { + printf("Link forced"); + switch (vptr->options.spd_dpx) { + case SPD_DPX_100_HALF: + printf(" speed 100M bps half duplex\n"); + break; + case SPD_DPX_100_FULL: + printf(" speed 100M bps full duplex\n"); + break; + case SPD_DPX_10_HALF: + printf(" speed 10M bps half duplex\n"); + break; + case SPD_DPX_10_FULL: + printf(" speed 10M bps full duplex\n"); + break; + default: + break; + } + } +} + +/** + * velocity_rx_reset - handle a receive reset + * @vptr: velocity we are resetting + * + * Reset the ownership and status for the receive ring side. + * Hand all the receive queue to the NIC. + */ + +static void velocity_rx_reset(struct velocity_info *vptr) +{ + + struct mac_regs *regs = vptr->mac_regs; + int i; + +//ptr->rd_dirty = vptr->rd_filled = vptr->rd_curr = 0; + + /* + * Init state, all RD entries belong to the NIC + */ + for (i = 0; i < vptr->options.numrx; ++i) + vptr->rd_ring[i].rdesc0.owner = OWNED_BY_NIC; + + writew(RX_DESC_DEF, ®s->RBRDU); + writel(virt_to_le32desc(vptr->rd_ring), ®s->RDBaseLo); + writew(0, ®s->RDIdx); + writew(RX_DESC_DEF - 1, ®s->RDCSize); +} + +/** + * velocity_init_registers - initialise MAC registers + * @vptr: velocity to init + * @type: type of initialisation (hot or cold) + * + * Initialise the MAC on a reset or on first set up on the + * hardware. + */ + +static void velocity_init_registers(struct nic *nic, + struct velocity_info *vptr, + enum velocity_init_type type) +{ + struct mac_regs *regs = vptr->mac_regs; + int i, mii_status; + + mac_wol_reset(regs); + + switch (type) { + case VELOCITY_INIT_RESET: + case VELOCITY_INIT_WOL: + +//netif_stop_queue(vptr->dev); + + /* + * Reset RX to prevent RX pointer not on the 4X location + */ + velocity_rx_reset(vptr); + mac_rx_queue_run(regs); + mac_rx_queue_wake(regs); + + mii_status = velocity_get_opt_media_mode(vptr); + + if (velocity_set_media_mode(vptr, mii_status) != + VELOCITY_LINK_CHANGE) { + velocity_print_link_status(vptr); + if (!(vptr->mii_status & VELOCITY_LINK_FAIL)) + printf("Link Failed\n"); +// netif_wake_queue(vptr->dev); + } + + enable_flow_control_ability(vptr); + + mac_clear_isr(regs); + writel(CR0_STOP, ®s->CR0Clr); + //writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), + writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), + ®s->CR0Set); + break; + + case VELOCITY_INIT_COLD: + default: + /* + * Do reset + */ + velocity_soft_reset(vptr); + mdelay(5); + + mac_eeprom_reload(regs); + for (i = 0; i < 6; i++) { + writeb(nic->node_addr[i], &(regs->PAR[i])); + } + /* + * clear Pre_ACPI bit. + */ + BYTE_REG_BITS_OFF(CFGA_PACPI, &(regs->CFGA)); + mac_set_rx_thresh(regs, vptr->options.rx_thresh); + mac_set_dma_length(regs, vptr->options.DMA_length); + + writeb(WOLCFG_SAM | WOLCFG_SAB, ®s->WOLCFGSet); + /* + * Back off algorithm use original IEEE standard + */ + BYTE_REG_BITS_SET(CFGB_OFSET, + (CFGB_CRANDOM | CFGB_CAP | CFGB_MBA | + CFGB_BAKOPT), ®s->CFGB); + + /* + * Init CAM filter + */ + velocity_init_cam_filter(vptr); + + /* + * Set packet filter: Receive directed and broadcast address + */ +//FIXME Multicast velocity_set_multi(nic); + + /* + * Enable MII auto-polling + */ + enable_mii_autopoll(regs); + + vptr->int_mask = INT_MASK_DEF; + + writel(virt_to_le32desc(vptr->rd_ring), ®s->RDBaseLo); + writew(vptr->options.numrx - 1, ®s->RDCSize); + mac_rx_queue_run(regs); + mac_rx_queue_wake(regs); + + writew(vptr->options.numtx - 1, ®s->TDCSize); + +// for (i = 0; i < vptr->num_txq; i++) { + writel(virt_to_le32desc(vptr->td_rings), + &(regs->TDBaseLo[0])); + mac_tx_queue_run(regs, 0); +// } + + init_flow_control_register(vptr); + + writel(CR0_STOP, ®s->CR0Clr); + writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), + ®s->CR0Set); + + mii_status = velocity_get_opt_media_mode(vptr); +// netif_stop_queue(vptr->dev); + + mii_init(vptr, mii_status); + + if (velocity_set_media_mode(vptr, mii_status) != + VELOCITY_LINK_CHANGE) { + velocity_print_link_status(vptr); + if (!(vptr->mii_status & VELOCITY_LINK_FAIL)) + printf("Link Faaailll\n"); +// netif_wake_queue(vptr->dev); + } + + enable_flow_control_ability(vptr); + mac_hw_mibs_init(regs); + mac_write_int_mask(vptr->int_mask, regs); + mac_clear_isr(regs); + + + } + velocity_print_link_status(vptr); +} + +/** + * velocity_soft_reset - soft reset + * @vptr: velocity to reset + * + * Kick off a soft reset of the velocity adapter and then poll + * until the reset sequence has completed before returning. + */ + +static int velocity_soft_reset(struct velocity_info *vptr) +{ + struct mac_regs *regs = vptr->mac_regs; + unsigned int i = 0; + + writel(CR0_SFRST, ®s->CR0Set); + + for (i = 0; i < W_MAX_TIMEOUT; i++) { + udelay(5); + if (!DWORD_REG_BITS_IS_ON(CR0_SFRST, ®s->CR0Set)) + break; + } + + if (i == W_MAX_TIMEOUT) { + writel(CR0_FORSRST, ®s->CR0Set); + /* FIXME: PCI POSTING */ + /* delay 2ms */ + mdelay(2); + } + return 0; +} + +/** + * velocity_init_rings - set up DMA rings + * @vptr: Velocity to set up + * + * Allocate PCI mapped DMA rings for the receive and transmit layer + * to use. + */ + +static int velocity_init_rings(struct velocity_info *vptr) +{ + + int idx; + + vptr->rd_curr = 0; + vptr->td_curr = 0; + memset(vptr->td_rings, 0, TX_DESC_DEF * sizeof(struct tx_desc)); + memset(vptr->rd_ring, 0, RX_DESC_DEF * sizeof(struct rx_desc)); +// memset(vptr->tx_buffs, 0, TX_DESC_DEF * PKT_BUF_SZ); + + + for (idx = 0; idx < RX_DESC_DEF; idx++) { + vptr->rd_ring[idx].rdesc0.RSR = 0; + vptr->rd_ring[idx].rdesc0.len = 0; + vptr->rd_ring[idx].rdesc0.reserved = 0; + vptr->rd_ring[idx].rdesc0.owner = 0; + vptr->rd_ring[idx].len = cpu_to_le32(vptr->rx_buf_sz); + vptr->rd_ring[idx].inten = 1; + vptr->rd_ring[idx].pa_low = + virt_to_bus(vptr->rxb + (RX_DESC_DEF * idx)); + vptr->rd_ring[idx].pa_high = 0; + vptr->rd_ring[idx].rdesc0.owner = OWNED_BY_NIC; + } + +/* for (i = 0; idx < TX_DESC_DEF; idx++ ) { + vptr->td_rings[idx].tdesc1.TCPLS = TCPLS_NORMAL; + vptr->td_rings[idx].tdesc1.TCR = TCR0_TIC; + vptr->td_rings[idx].td_buf[0].queue = 0; + vptr->td_rings[idx].tdesc0.owner = ~OWNED_BY_NIC; + vptr->td_rings[idx].tdesc0.pktsize = 0; + vptr->td_rings[idx].td_buf[0].pa_low = cpu_to_le32(virt_to_bus(vptr->txb + (idx * PKT_BUF_SZ))); + vptr->td_rings[idx].td_buf[0].pa_high = 0; + vptr->td_rings[idx].td_buf[0].bufsize = 0; + vptr->td_rings[idx].tdesc1.CMDZ = 2; + } +*/ + return 0; +} + +/** + * velocity_open - interface activation callback + * @dev: network layer device to open + * + * Called when the network layer brings the interface up. Returns + * a negative posix error code on failure, or zero on success. + * + * All the ring allocation and set up is done on open for this + * adapter to minimise memory usage when inactive + */ + +#define PCI_BYTE_REG_BITS_ON(x,i,p) do{\ + u8 byReg;\ + pci_read_config_byte((p), (i), &(byReg));\ + (byReg) |= (x);\ + pci_write_config_byte((p), (i), (byReg));\ +} while (0) + +// +// Registers in the PCI configuration space +// +#define PCI_REG_COMMAND 0x04 // +#define PCI_REG_MODE0 0x60 // +#define PCI_REG_MODE1 0x61 // +#define PCI_REG_MODE2 0x62 // +#define PCI_REG_MODE3 0x63 // +#define PCI_REG_DELAY_TIMER 0x64 // + +// Bits in the (MODE2, 0x62) register +// +#define MODE2_PCEROPT 0x80 // take PCI bus ERror as a fatal and shutdown from software control +#define MODE2_TXQ16 0x40 // TX write-back Queue control. 0->32 entries available in Tx write-back queue, 1->16 entries +#define MODE2_TXPOST 0x08 // (Not support in VT3119) +#define MODE2_AUTOOPT 0x04 // (VT3119 GHCI without such behavior) +#define MODE2_MODE10T 0x02 // used to control tx Threshold for 10M case +#define MODE2_TCPLSOPT 0x01 // TCP large send field update disable, hardware will not update related fields, leave it to software. + +// +// Bits in the MODE3 register +// +#define MODE3_MIION 0x04 // MII symbol codine error detect enable ?? + +// Bits in the (COMMAND, 0x04) register +#define COMMAND_BUSM 0x04 +#define COMMAND_WAIT 0x80 +static int velocity_open(struct nic *nic, struct pci_device *pci __unused) +{ + int ret; + + u8 diff; + u32 TxPhyAddr, RxPhyAddr; + u32 TxBufPhyAddr, RxBufPhyAddr; + vptr->TxDescArrays = tx_ring; + if (vptr->TxDescArrays == 0) + printf("Allot Error"); + + /* Tx Descriptor needs 64 bytes alignment; */ + TxPhyAddr = virt_to_bus(vptr->TxDescArrays); + printf("Unaligned Address : %X\n", TxPhyAddr); + diff = 64 - (TxPhyAddr - ((TxPhyAddr >> 6) << 6)); + TxPhyAddr += diff; + vptr->td_rings = (struct tx_desc *) (vptr->TxDescArrays + diff); + + printf("Aligned Address: %lX\n", virt_to_bus(vptr->td_rings)); + vptr->tx_buffs = txb; + /* Rx Buffer needs 64 bytes alignment; */ + TxBufPhyAddr = virt_to_bus(vptr->tx_buffs); + diff = 64 - (TxBufPhyAddr - ((TxBufPhyAddr >> 6) << 6)); + TxBufPhyAddr += diff; + vptr->txb = (unsigned char *) (vptr->tx_buffs + diff); + + vptr->RxDescArrays = rx_ring; + /* Rx Descriptor needs 64 bytes alignment; */ + RxPhyAddr = virt_to_bus(vptr->RxDescArrays); + diff = 64 - (RxPhyAddr - ((RxPhyAddr >> 6) << 6)); + RxPhyAddr += diff; + vptr->rd_ring = (struct rx_desc *) (vptr->RxDescArrays + diff); + + vptr->rx_buffs = rxb; + /* Rx Buffer needs 64 bytes alignment; */ + RxBufPhyAddr = virt_to_bus(vptr->rx_buffs); + diff = 64 - (RxBufPhyAddr - ((RxBufPhyAddr >> 6) << 6)); + RxBufPhyAddr += diff; + vptr->rxb = (unsigned char *) (vptr->rx_buffs + diff); + + if (vptr->RxDescArrays == NULL || vptr->RxDescArrays == NULL) { + printf("Allocate tx_ring or rd_ring failed\n"); + return 0; + } + + vptr->rx_buf_sz = PKT_BUF_SZ; +/* + // turn this on to avoid retry forever + PCI_BYTE_REG_BITS_ON(MODE2_PCEROPT, PCI_REG_MODE2, pci); + // for some legacy BIOS and OS don't open BusM + // bit in PCI configuration space. So, turn it on. + PCI_BYTE_REG_BITS_ON(COMMAND_BUSM, PCI_REG_COMMAND, pci); + // turn this on to detect MII coding error + PCI_BYTE_REG_BITS_ON(MODE3_MIION, PCI_REG_MODE3, pci); + */ + ret = velocity_init_rings(vptr); + + /* Ensure chip is running */ +//FIXME: pci_set_power_state(vptr->pdev, PCI_D0); + + velocity_init_registers(nic, vptr, VELOCITY_INIT_COLD); + mac_write_int_mask(0, vptr->mac_regs); +// _int(vptr->mac_regs); + //mac_enable_int(vptr->mac_regs); + + vptr->flags |= VELOCITY_FLAGS_OPENED; + return 1; + +} + +/* + * MII access , media link mode setting functions + */ + + +/** + * mii_init - set up MII + * @vptr: velocity adapter + * @mii_status: links tatus + * + * Set up the PHY for the current link state. + */ + +static void mii_init(struct velocity_info *vptr, u32 mii_status __unused) +{ + u16 BMCR; + + switch (PHYID_GET_PHY_ID(vptr->phy_id)) { + case PHYID_CICADA_CS8201: + /* + * Reset to hardware default + */ + MII_REG_BITS_OFF((ANAR_ASMDIR | ANAR_PAUSE), MII_REG_ANAR, + vptr->mac_regs); + /* + * Turn on ECHODIS bit in NWay-forced full mode and turn it + * off it in NWay-forced half mode for NWay-forced v.s. + * legacy-forced issue. + */ + if (vptr->mii_status & VELOCITY_DUPLEX_FULL) + MII_REG_BITS_ON(TCSR_ECHODIS, MII_REG_TCSR, + vptr->mac_regs); + else + MII_REG_BITS_OFF(TCSR_ECHODIS, MII_REG_TCSR, + vptr->mac_regs); + /* + * Turn on Link/Activity LED enable bit for CIS8201 + */ + MII_REG_BITS_ON(PLED_LALBE, MII_REG_PLED, vptr->mac_regs); + break; + case PHYID_VT3216_32BIT: + case PHYID_VT3216_64BIT: + /* + * Reset to hardware default + */ + MII_REG_BITS_ON((ANAR_ASMDIR | ANAR_PAUSE), MII_REG_ANAR, + vptr->mac_regs); + /* + * Turn on ECHODIS bit in NWay-forced full mode and turn it + * off it in NWay-forced half mode for NWay-forced v.s. + * legacy-forced issue + */ + if (vptr->mii_status & VELOCITY_DUPLEX_FULL) + MII_REG_BITS_ON(TCSR_ECHODIS, MII_REG_TCSR, + vptr->mac_regs); + else + MII_REG_BITS_OFF(TCSR_ECHODIS, MII_REG_TCSR, + vptr->mac_regs); + break; + + case PHYID_MARVELL_1000: + case PHYID_MARVELL_1000S: + /* + * Assert CRS on Transmit + */ + MII_REG_BITS_ON(PSCR_ACRSTX, MII_REG_PSCR, vptr->mac_regs); + /* + * Reset to hardware default + */ + MII_REG_BITS_ON((ANAR_ASMDIR | ANAR_PAUSE), MII_REG_ANAR, + vptr->mac_regs); + break; + default: + ; + } + velocity_mii_read(vptr->mac_regs, MII_REG_BMCR, &BMCR); + if (BMCR & BMCR_ISO) { + BMCR &= ~BMCR_ISO; + velocity_mii_write(vptr->mac_regs, MII_REG_BMCR, BMCR); + } +} + +/** + * safe_disable_mii_autopoll - autopoll off + * @regs: velocity registers + * + * Turn off the autopoll and wait for it to disable on the chip + */ + +static void safe_disable_mii_autopoll(struct mac_regs *regs) +{ + u16 ww; + + /* turn off MAUTO */ + writeb(0, ®s->MIICR); + for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { + udelay(1); + if (BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) + break; + } +} + +/** + * enable_mii_autopoll - turn on autopolling + * @regs: velocity registers + * + * Enable the MII link status autopoll feature on the Velocity + * hardware. Wait for it to enable. + */ + +static void enable_mii_autopoll(struct mac_regs *regs) +{ + unsigned int ii; + + writeb(0, &(regs->MIICR)); + writeb(MIIADR_SWMPL, ®s->MIIADR); + + for (ii = 0; ii < W_MAX_TIMEOUT; ii++) { + udelay(1); + if (BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) + break; + } + + writeb(MIICR_MAUTO, ®s->MIICR); + + for (ii = 0; ii < W_MAX_TIMEOUT; ii++) { + udelay(1); + if (!BYTE_REG_BITS_IS_ON(MIISR_MIDLE, ®s->MIISR)) + break; + } + +} + +/** + * velocity_mii_read - read MII data + * @regs: velocity registers + * @index: MII register index + * @data: buffer for received data + * + * Perform a single read of an MII 16bit register. Returns zero + * on success or -ETIMEDOUT if the PHY did not respond. + */ + +static int velocity_mii_read(struct mac_regs *regs, u8 index, u16 * data) +{ + u16 ww; + + /* + * Disable MIICR_MAUTO, so that mii addr can be set normally + */ + safe_disable_mii_autopoll(regs); + + writeb(index, ®s->MIIADR); + + BYTE_REG_BITS_ON(MIICR_RCMD, ®s->MIICR); + + for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { + if (!(readb(®s->MIICR) & MIICR_RCMD)) + break; + } + + *data = readw(®s->MIIDATA); + + enable_mii_autopoll(regs); + if (ww == W_MAX_TIMEOUT) + return -1; + return 0; +} + +/** + * velocity_mii_write - write MII data + * @regs: velocity registers + * @index: MII register index + * @data: 16bit data for the MII register + * + * Perform a single write to an MII 16bit register. Returns zero + * on success or -ETIMEDOUT if the PHY did not respond. + */ + +static int velocity_mii_write(struct mac_regs *regs, u8 mii_addr, u16 data) +{ + u16 ww; + + /* + * Disable MIICR_MAUTO, so that mii addr can be set normally + */ + safe_disable_mii_autopoll(regs); + + /* MII reg offset */ + writeb(mii_addr, ®s->MIIADR); + /* set MII data */ + writew(data, ®s->MIIDATA); + + /* turn on MIICR_WCMD */ + BYTE_REG_BITS_ON(MIICR_WCMD, ®s->MIICR); + + /* W_MAX_TIMEOUT is the timeout period */ + for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { + udelay(5); + if (!(readb(®s->MIICR) & MIICR_WCMD)) + break; + } + enable_mii_autopoll(regs); + + if (ww == W_MAX_TIMEOUT) + return -1; + return 0; +} + +/** + * velocity_get_opt_media_mode - get media selection + * @vptr: velocity adapter + * + * Get the media mode stored in EEPROM or module options and load + * mii_status accordingly. The requested link state information + * is also returned. + */ + +static u32 velocity_get_opt_media_mode(struct velocity_info *vptr) +{ + u32 status = 0; + + switch (vptr->options.spd_dpx) { + case SPD_DPX_AUTO: + status = VELOCITY_AUTONEG_ENABLE; + break; + case SPD_DPX_100_FULL: + status = VELOCITY_SPEED_100 | VELOCITY_DUPLEX_FULL; + break; + case SPD_DPX_10_FULL: + status = VELOCITY_SPEED_10 | VELOCITY_DUPLEX_FULL; + break; + case SPD_DPX_100_HALF: + status = VELOCITY_SPEED_100; + break; + case SPD_DPX_10_HALF: + status = VELOCITY_SPEED_10; + break; + } + vptr->mii_status = status; + return status; +} + +/** + * mii_set_auto_on - autonegotiate on + * @vptr: velocity + * + * Enable autonegotation on this interface + */ + +static void mii_set_auto_on(struct velocity_info *vptr) +{ + if (MII_REG_BITS_IS_ON(BMCR_AUTO, MII_REG_BMCR, vptr->mac_regs)) + MII_REG_BITS_ON(BMCR_REAUTO, MII_REG_BMCR, vptr->mac_regs); + else + MII_REG_BITS_ON(BMCR_AUTO, MII_REG_BMCR, vptr->mac_regs); +} + + +/* +static void mii_set_auto_off(struct velocity_info * vptr) +{ + MII_REG_BITS_OFF(BMCR_AUTO, MII_REG_BMCR, vptr->mac_regs); +} +*/ + +/** + * set_mii_flow_control - flow control setup + * @vptr: velocity interface + * + * Set up the flow control on this interface according to + * the supplied user/eeprom options. + */ + +static void set_mii_flow_control(struct velocity_info *vptr) +{ + /*Enable or Disable PAUSE in ANAR */ + switch (vptr->options.flow_cntl) { + case FLOW_CNTL_TX: + MII_REG_BITS_OFF(ANAR_PAUSE, MII_REG_ANAR, vptr->mac_regs); + MII_REG_BITS_ON(ANAR_ASMDIR, MII_REG_ANAR, vptr->mac_regs); + break; + + case FLOW_CNTL_RX: + MII_REG_BITS_ON(ANAR_PAUSE, MII_REG_ANAR, vptr->mac_regs); + MII_REG_BITS_ON(ANAR_ASMDIR, MII_REG_ANAR, vptr->mac_regs); + break; + + case FLOW_CNTL_TX_RX: + MII_REG_BITS_ON(ANAR_PAUSE, MII_REG_ANAR, vptr->mac_regs); + MII_REG_BITS_ON(ANAR_ASMDIR, MII_REG_ANAR, vptr->mac_regs); + break; + + case FLOW_CNTL_DISABLE: + MII_REG_BITS_OFF(ANAR_PAUSE, MII_REG_ANAR, vptr->mac_regs); + MII_REG_BITS_OFF(ANAR_ASMDIR, MII_REG_ANAR, + vptr->mac_regs); + break; + default: + break; + } +} + +/** + * velocity_set_media_mode - set media mode + * @mii_status: old MII link state + * + * Check the media link state and configure the flow control + * PHY and also velocity hardware setup accordingly. In particular + * we need to set up CD polling and frame bursting. + */ + +static int velocity_set_media_mode(struct velocity_info *vptr, + u32 mii_status) +{ + u32 curr_status; + struct mac_regs *regs = vptr->mac_regs; + + vptr->mii_status = mii_check_media_mode(vptr->mac_regs); + curr_status = vptr->mii_status & (~VELOCITY_LINK_FAIL); + + /* Set mii link status */ + set_mii_flow_control(vptr); + + /* + Check if new status is consisent with current status + if (((mii_status & curr_status) & VELOCITY_AUTONEG_ENABLE) + || (mii_status==curr_status)) { + vptr->mii_status=mii_check_media_mode(vptr->mac_regs); + vptr->mii_status=check_connection_type(vptr->mac_regs); + printf(MSG_LEVEL_INFO, "Velocity link no change\n"); + return 0; + } + */ + + if (PHYID_GET_PHY_ID(vptr->phy_id) == PHYID_CICADA_CS8201) { + MII_REG_BITS_ON(AUXCR_MDPPS, MII_REG_AUXCR, + vptr->mac_regs); + } + + /* + * If connection type is AUTO + */ + if (mii_status & VELOCITY_AUTONEG_ENABLE) { + printf("Velocity is AUTO mode\n"); + /* clear force MAC mode bit */ + BYTE_REG_BITS_OFF(CHIPGCR_FCMODE, ®s->CHIPGCR); + /* set duplex mode of MAC according to duplex mode of MII */ + MII_REG_BITS_ON(ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10, + MII_REG_ANAR, vptr->mac_regs); + MII_REG_BITS_ON(G1000CR_1000FD | G1000CR_1000, + MII_REG_G1000CR, vptr->mac_regs); + MII_REG_BITS_ON(BMCR_SPEED1G, MII_REG_BMCR, + vptr->mac_regs); + + /* enable AUTO-NEGO mode */ + mii_set_auto_on(vptr); + } else { + u16 ANAR; + u8 CHIPGCR; + + /* + * 1. if it's 3119, disable frame bursting in halfduplex mode + * and enable it in fullduplex mode + * 2. set correct MII/GMII and half/full duplex mode in CHIPGCR + * 3. only enable CD heart beat counter in 10HD mode + */ + + /* set force MAC mode bit */ + BYTE_REG_BITS_ON(CHIPGCR_FCMODE, ®s->CHIPGCR); + + CHIPGCR = readb(®s->CHIPGCR); + CHIPGCR &= ~CHIPGCR_FCGMII; + + if (mii_status & VELOCITY_DUPLEX_FULL) { + CHIPGCR |= CHIPGCR_FCFDX; + writeb(CHIPGCR, ®s->CHIPGCR); + printf + ("DEBUG: set Velocity to forced full mode\n"); + if (vptr->rev_id < REV_ID_VT3216_A0) + BYTE_REG_BITS_OFF(TCR_TB2BDIS, ®s->TCR); + } else { + CHIPGCR &= ~CHIPGCR_FCFDX; + printf + ("DEBUG: set Velocity to forced half mode\n"); + writeb(CHIPGCR, ®s->CHIPGCR); + if (vptr->rev_id < REV_ID_VT3216_A0) + BYTE_REG_BITS_ON(TCR_TB2BDIS, ®s->TCR); + } + + MII_REG_BITS_OFF(G1000CR_1000FD | G1000CR_1000, + MII_REG_G1000CR, vptr->mac_regs); + + if (!(mii_status & VELOCITY_DUPLEX_FULL) + && (mii_status & VELOCITY_SPEED_10)) { + BYTE_REG_BITS_OFF(TESTCFG_HBDIS, ®s->TESTCFG); + } else { + BYTE_REG_BITS_ON(TESTCFG_HBDIS, ®s->TESTCFG); + } + /* MII_REG_BITS_OFF(BMCR_SPEED1G, MII_REG_BMCR, vptr->mac_regs); */ + velocity_mii_read(vptr->mac_regs, MII_REG_ANAR, &ANAR); + ANAR &= (~(ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)); + if (mii_status & VELOCITY_SPEED_100) { + if (mii_status & VELOCITY_DUPLEX_FULL) + ANAR |= ANAR_TXFD; + else + ANAR |= ANAR_TX; + } else { + if (mii_status & VELOCITY_DUPLEX_FULL) + ANAR |= ANAR_10FD; + else + ANAR |= ANAR_10; + } + velocity_mii_write(vptr->mac_regs, MII_REG_ANAR, ANAR); + /* enable AUTO-NEGO mode */ + mii_set_auto_on(vptr); + /* MII_REG_BITS_ON(BMCR_AUTO, MII_REG_BMCR, vptr->mac_regs); */ + } + /* vptr->mii_status=mii_check_media_mode(vptr->mac_regs); */ + /* vptr->mii_status=check_connection_type(vptr->mac_regs); */ + return VELOCITY_LINK_CHANGE; +} + +/** + * mii_check_media_mode - check media state + * @regs: velocity registers + * + * Check the current MII status and determine the link status + * accordingly + */ + +static u32 mii_check_media_mode(struct mac_regs *regs) +{ + u32 status = 0; + u16 ANAR; + + if (!MII_REG_BITS_IS_ON(BMSR_LNK, MII_REG_BMSR, regs)) + status |= VELOCITY_LINK_FAIL; + + if (MII_REG_BITS_IS_ON(G1000CR_1000FD, MII_REG_G1000CR, regs)) + status |= VELOCITY_SPEED_1000 | VELOCITY_DUPLEX_FULL; + else if (MII_REG_BITS_IS_ON(G1000CR_1000, MII_REG_G1000CR, regs)) + status |= (VELOCITY_SPEED_1000); + else { + velocity_mii_read(regs, MII_REG_ANAR, &ANAR); + if (ANAR & ANAR_TXFD) + status |= + (VELOCITY_SPEED_100 | VELOCITY_DUPLEX_FULL); + else if (ANAR & ANAR_TX) + status |= VELOCITY_SPEED_100; + else if (ANAR & ANAR_10FD) + status |= + (VELOCITY_SPEED_10 | VELOCITY_DUPLEX_FULL); + else + status |= (VELOCITY_SPEED_10); + } + + if (MII_REG_BITS_IS_ON(BMCR_AUTO, MII_REG_BMCR, regs)) { + velocity_mii_read(regs, MII_REG_ANAR, &ANAR); + if ((ANAR & (ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)) + == (ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)) { + if (MII_REG_BITS_IS_ON + (G1000CR_1000 | G1000CR_1000FD, + MII_REG_G1000CR, regs)) + status |= VELOCITY_AUTONEG_ENABLE; + } + } + + return status; +} + +static u32 check_connection_type(struct mac_regs *regs) +{ + u32 status = 0; + u8 PHYSR0; + u16 ANAR; + PHYSR0 = readb(®s->PHYSR0); + + /* + if (!(PHYSR0 & PHYSR0_LINKGD)) + status|=VELOCITY_LINK_FAIL; + */ + + if (PHYSR0 & PHYSR0_FDPX) + status |= VELOCITY_DUPLEX_FULL; + + if (PHYSR0 & PHYSR0_SPDG) + status |= VELOCITY_SPEED_1000; + if (PHYSR0 & PHYSR0_SPD10) + status |= VELOCITY_SPEED_10; + else + status |= VELOCITY_SPEED_100; + + if (MII_REG_BITS_IS_ON(BMCR_AUTO, MII_REG_BMCR, regs)) { + velocity_mii_read(regs, MII_REG_ANAR, &ANAR); + if ((ANAR & (ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)) + == (ANAR_TXFD | ANAR_TX | ANAR_10FD | ANAR_10)) { + if (MII_REG_BITS_IS_ON + (G1000CR_1000 | G1000CR_1000FD, + MII_REG_G1000CR, regs)) + status |= VELOCITY_AUTONEG_ENABLE; + } + } + + return status; +} + +/** + * enable_flow_control_ability - flow control + * @vptr: veloity to configure + * + * Set up flow control according to the flow control options + * determined by the eeprom/configuration. + */ + +static void enable_flow_control_ability(struct velocity_info *vptr) +{ + + struct mac_regs *regs = vptr->mac_regs; + + switch (vptr->options.flow_cntl) { + + case FLOW_CNTL_DEFAULT: + if (BYTE_REG_BITS_IS_ON(PHYSR0_RXFLC, ®s->PHYSR0)) + writel(CR0_FDXRFCEN, ®s->CR0Set); + else + writel(CR0_FDXRFCEN, ®s->CR0Clr); + + if (BYTE_REG_BITS_IS_ON(PHYSR0_TXFLC, ®s->PHYSR0)) + writel(CR0_FDXTFCEN, ®s->CR0Set); + else + writel(CR0_FDXTFCEN, ®s->CR0Clr); + break; + + case FLOW_CNTL_TX: + writel(CR0_FDXTFCEN, ®s->CR0Set); + writel(CR0_FDXRFCEN, ®s->CR0Clr); + break; + + case FLOW_CNTL_RX: + writel(CR0_FDXRFCEN, ®s->CR0Set); + writel(CR0_FDXTFCEN, ®s->CR0Clr); + break; + + case FLOW_CNTL_TX_RX: + writel(CR0_FDXTFCEN, ®s->CR0Set); + writel(CR0_FDXRFCEN, ®s->CR0Set); + break; + + case FLOW_CNTL_DISABLE: + writel(CR0_FDXRFCEN, ®s->CR0Clr); + writel(CR0_FDXTFCEN, ®s->CR0Clr); + break; + + default: + break; + } + +} + +/* FIXME: Move to pci.c */ +/** + * pci_set_power_state - Set the power state of a PCI device + * @dev: PCI device to be suspended + * @state: Power state we're entering + * + * Transition a device to a new power state, using the Power Management + * Capabilities in the device's config space. + * + * RETURN VALUE: + * -EINVAL if trying to enter a lower state than we're already in. + * 0 if we're already in the requested state. + * -EIO if device does not support PCI PM. + * 0 if we can successfully change the power state. + */ + +int pci_set_power_state(struct pci_device *dev, int state) +{ + int pm; + u16 pmcsr; + int current_state = 0; + + /* bound the state we're entering */ + if (state > 3) + state = 3; + + /* Validate current state: + * Can enter D0 from any state, but if we can only go deeper + * to sleep if we're already in a low power state + */ + if (state > 0 && current_state > state) + return -1; + else if (current_state == state) + return 0; /* we're already there */ + + /* find PCI PM capability in list */ + pm = pci_find_capability(dev, PCI_CAP_ID_PM); + + /* abort if the device doesn't support PM capabilities */ + if (!pm) + return -2; + + /* check if this device supports the desired state */ + if (state == 1 || state == 2) { + u16 pmc; + pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc); + if (state == 1 && !(pmc & PCI_PM_CAP_D1)) + return -2; + else if (state == 2 && !(pmc & PCI_PM_CAP_D2)) + return -2; + } + + /* If we're in D3, force entire word to 0. + * This doesn't affect PME_Status, disables PME_En, and + * sets PowerState to 0. + */ + if (current_state >= 3) + pmcsr = 0; + else { + pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr); + pmcsr &= ~PCI_PM_CTRL_STATE_MASK; + pmcsr |= state; + } + + /* enter specified state */ + pci_write_config_word(dev, pm + PCI_PM_CTRL, pmcsr); + + /* Mandatory power management transition delays */ + /* see PCI PM 1.1 5.6.1 table 18 */ + if (state == 3 || current_state == 3) + mdelay(10); + else if (state == 2 || current_state == 2) + udelay(200); + current_state = state; + + return 0; +} + +static struct pci_device_id velocity_nics[] = { + PCI_ROM(0x1106, 0x3119, "via-velocity", "VIA Networking Velocity Family Gigabit Ethernet Adapter"), +}; + +PCI_DRIVER ( velocity_driver, velocity_nics, PCI_NO_CLASS ); + +DRIVER ( "VIA-VELOCITY/PCI", nic_driver, pci_driver, velocity_driver, + velocity_probe, velocity_disable ); diff --git a/gpxe/src/drivers/net/via-velocity.h b/gpxe/src/drivers/net/via-velocity.h new file mode 100644 index 0000000..a43028b --- /dev/null +++ b/gpxe/src/drivers/net/via-velocity.h @@ -0,0 +1,1930 @@ +/* + * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. + * All rights reserved. + * + * This software may be redistributed and/or modified under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * File: via-velocity.h + * + * Purpose: Header file to define driver's private structures. + * + * Author: Chuang Liang-Shing, AJ Jiang + * + * Date: Jan 24, 2003 + * + * Changes for Etherboot Port: + * Copyright (c) 2006 by Timothy Legge + */ + +#ifndef VELOCITY_H +#define VELOCITY_H + +#define VELOCITY_TX_CSUM_SUPPORT + +#define VELOCITY_NAME "via-velocity" +#define VELOCITY_FULL_DRV_NAM "VIA Networking Velocity Family Gigabit Ethernet Adapter Driver" +#define VELOCITY_VERSION "1.13" + +#define PKT_BUF_SZ 1564 + +#define MAX_UNITS 8 +#define OPTION_DEFAULT { [0 ... MAX_UNITS-1] = -1} + +#define REV_ID_VT6110 (0) + +#define BYTE_REG_BITS_ON(x,p) do { writeb(readb((p))|(x),(p));} while (0) +#define WORD_REG_BITS_ON(x,p) do { writew(readw((p))|(x),(p));} while (0) +#define DWORD_REG_BITS_ON(x,p) do { writel(readl((p))|(x),(p));} while (0) + +#define BYTE_REG_BITS_IS_ON(x,p) (readb((p)) & (x)) +#define WORD_REG_BITS_IS_ON(x,p) (readw((p)) & (x)) +#define DWORD_REG_BITS_IS_ON(x,p) (readl((p)) & (x)) + +#define BYTE_REG_BITS_OFF(x,p) do { writeb(readb((p)) & (~(x)),(p));} while (0) +#define WORD_REG_BITS_OFF(x,p) do { writew(readw((p)) & (~(x)),(p));} while (0) +#define DWORD_REG_BITS_OFF(x,p) do { writel(readl((p)) & (~(x)),(p));} while (0) + +#define BYTE_REG_BITS_SET(x,m,p) do { writeb( (readb((p)) & (~(m))) |(x),(p));} while (0) +#define WORD_REG_BITS_SET(x,m,p) do { writew( (readw((p)) & (~(m))) |(x),(p));} while (0) +#define DWORD_REG_BITS_SET(x,m,p) do { writel( (readl((p)) & (~(m)))|(x),(p));} while (0) + +#define VAR_USED(p) do {(p)=(p);} while (0) + +/* + * Purpose: Structures for MAX RX/TX descriptors. + */ + + +#define B_OWNED_BY_CHIP 1 +#define B_OWNED_BY_HOST 0 + +/* + * Bits in the RSR0 register + */ + +#define RSR_DETAG 0x0080 +#define RSR_SNTAG 0x0040 +#define RSR_RXER 0x0020 +#define RSR_RL 0x0010 +#define RSR_CE 0x0008 +#define RSR_FAE 0x0004 +#define RSR_CRC 0x0002 +#define RSR_VIDM 0x0001 + +/* + * Bits in the RSR1 register + */ + +#define RSR_RXOK 0x8000 // rx OK +#define RSR_PFT 0x4000 // Perfect filtering address match +#define RSR_MAR 0x2000 // MAC accept multicast address packet +#define RSR_BAR 0x1000 // MAC accept broadcast address packet +#define RSR_PHY 0x0800 // MAC accept physical address packet +#define RSR_VTAG 0x0400 // 802.1p/1q tagging packet indicator +#define RSR_STP 0x0200 // start of packet +#define RSR_EDP 0x0100 // end of packet + +/* + * Bits in the RSR1 register + */ + +#define RSR1_RXOK 0x80 // rx OK +#define RSR1_PFT 0x40 // Perfect filtering address match +#define RSR1_MAR 0x20 // MAC accept multicast address packet +#define RSR1_BAR 0x10 // MAC accept broadcast address packet +#define RSR1_PHY 0x08 // MAC accept physical address packet +#define RSR1_VTAG 0x04 // 802.1p/1q tagging packet indicator +#define RSR1_STP 0x02 // start of packet +#define RSR1_EDP 0x01 // end of packet + +/* + * Bits in the CSM register + */ + +#define CSM_IPOK 0x40 //IP Checkusm validatiaon ok +#define CSM_TUPOK 0x20 //TCP/UDP Checkusm validatiaon ok +#define CSM_FRAG 0x10 //Fragment IP datagram +#define CSM_IPKT 0x04 //Received an IP packet +#define CSM_TCPKT 0x02 //Received a TCP packet +#define CSM_UDPKT 0x01 //Received a UDP packet + +/* + * Bits in the TSR0 register + */ + +#define TSR0_ABT 0x0080 // Tx abort because of excessive collision +#define TSR0_OWT 0x0040 // Jumbo frame Tx abort +#define TSR0_OWC 0x0020 // Out of window collision +#define TSR0_COLS 0x0010 // experience collision in this transmit event +#define TSR0_NCR3 0x0008 // collision retry counter[3] +#define TSR0_NCR2 0x0004 // collision retry counter[2] +#define TSR0_NCR1 0x0002 // collision retry counter[1] +#define TSR0_NCR0 0x0001 // collision retry counter[0] +#define TSR0_TERR 0x8000 // +#define TSR0_FDX 0x4000 // current transaction is serviced by full duplex mode +#define TSR0_GMII 0x2000 // current transaction is serviced by GMII mode +#define TSR0_LNKFL 0x1000 // packet serviced during link down +#define TSR0_SHDN 0x0400 // shutdown case +#define TSR0_CRS 0x0200 // carrier sense lost +#define TSR0_CDH 0x0100 // AQE test fail (CD heartbeat) + +/* + * Bits in the TSR1 register + */ + +#define TSR1_TERR 0x80 // +#define TSR1_FDX 0x40 // current transaction is serviced by full duplex mode +#define TSR1_GMII 0x20 // current transaction is serviced by GMII mode +#define TSR1_LNKFL 0x10 // packet serviced during link down +#define TSR1_SHDN 0x04 // shutdown case +#define TSR1_CRS 0x02 // carrier sense lost +#define TSR1_CDH 0x01 // AQE test fail (CD heartbeat) + +// +// Bits in the TCR0 register +// +#define TCR0_TIC 0x80 // assert interrupt immediately while descriptor has been send complete +#define TCR0_PIC 0x40 // priority interrupt request, INA# is issued over adaptive interrupt scheme +#define TCR0_VETAG 0x20 // enable VLAN tag +#define TCR0_IPCK 0x10 // request IP checksum calculation. +#define TCR0_UDPCK 0x08 // request UDP checksum calculation. +#define TCR0_TCPCK 0x04 // request TCP checksum calculation. +#define TCR0_JMBO 0x02 // indicate a jumbo packet in GMAC side +#define TCR0_CRC 0x01 // disable CRC generation + +#define TCPLS_NORMAL 3 +#define TCPLS_START 2 +#define TCPLS_END 1 +#define TCPLS_MED 0 + + +// max transmit or receive buffer size +#define CB_RX_BUF_SIZE 2048UL // max buffer size + // NOTE: must be multiple of 4 + +#define CB_MAX_RD_NUM 512 // MAX # of RD +#define CB_MAX_TD_NUM 256 // MAX # of TD + +#define CB_INIT_RD_NUM_3119 128 // init # of RD, for setup VT3119 +#define CB_INIT_TD_NUM_3119 64 // init # of TD, for setup VT3119 + +#define CB_INIT_RD_NUM 128 // init # of RD, for setup default +#define CB_INIT_TD_NUM 64 // init # of TD, for setup default + +// for 3119 +#define CB_TD_RING_NUM 4 // # of TD rings. +#define CB_MAX_SEG_PER_PKT 7 // max data seg per packet (Tx) + + +/* + * If collisions excess 15 times , tx will abort, and + * if tx fifo underflow, tx will fail + * we should try to resend it + */ + +#define CB_MAX_TX_ABORT_RETRY 3 + +/* + * Receive descriptor + */ + +struct rdesc0 { + u16 RSR; /* Receive status */ + u16 len:14; /* Received packet length */ + u16 reserved:1; + u16 owner:1; /* Who owns this buffer ? */ +}; + +struct rdesc1 { + u16 PQTAG; + u8 CSM; + u8 IPKT; +}; + +struct rx_desc { + struct rdesc0 rdesc0; + struct rdesc1 rdesc1; + u32 pa_low; /* Low 32 bit PCI address */ + u16 pa_high; /* Next 16 bit PCI address (48 total) */ + u16 len:15; /* Frame size */ + u16 inten:1; /* Enable interrupt */ +} __attribute__ ((__packed__)); + +/* + * Transmit descriptor + */ + +struct tdesc0 { + u16 TSR; /* Transmit status register */ + u16 pktsize:14; /* Size of frame */ + u16 reserved:1; + u16 owner:1; /* Who owns the buffer */ +}; + +struct pqinf { /* Priority queue info */ + u16 VID:12; + u16 CFI:1; + u16 priority:3; +} __attribute__ ((__packed__)); + +struct tdesc1 { + struct pqinf pqinf; + u8 TCR; + u8 TCPLS:2; + u8 reserved:2; + u8 CMDZ:4; +} __attribute__ ((__packed__)); + +struct td_buf { + u32 pa_low; + u16 pa_high; + u16 bufsize:14; + u16 reserved:1; + u16 queue:1; +} __attribute__ ((__packed__)); + +struct tx_desc { + struct tdesc0 tdesc0; + struct tdesc1 tdesc1; + struct td_buf td_buf[7]; +}; + +#ifdef LINUX +struct velocity_rd_info { + struct sk_buff *skb; + dma_addr_t skb_dma; +}; + + +/** + * alloc_rd_info - allocate an rd info block + * + * Alocate and initialize a receive info structure used for keeping + * track of kernel side information related to each receive + * descriptor we are using + */ + +static inline struct velocity_rd_info *alloc_rd_info(void) +{ + struct velocity_rd_info *ptr; + if ((ptr = + kmalloc(sizeof(struct velocity_rd_info), GFP_ATOMIC)) == NULL) + return NULL; + else { + memset(ptr, 0, sizeof(struct velocity_rd_info)); + return ptr; + } +} + +/* + * Used to track transmit side buffers. + */ + +struct velocity_td_info { + struct sk_buff *skb; + u8 *buf; + int nskb_dma; + dma_addr_t skb_dma[7]; + dma_addr_t buf_dma; +}; + +#endif +enum { + OWNED_BY_HOST = 0, + OWNED_BY_NIC = 1 +} velocity_owner; + + +/* + * MAC registers and macros. + */ + + +#define MCAM_SIZE 64 +#define VCAM_SIZE 64 +#define TX_QUEUE_NO 4 + +#define MAX_HW_MIB_COUNTER 32 +#define VELOCITY_MIN_MTU (1514-14) +#define VELOCITY_MAX_MTU (9000) + +/* + * Registers in the MAC + */ + +#define MAC_REG_PAR 0x00 // physical address +#define MAC_REG_RCR 0x06 +#define MAC_REG_TCR 0x07 +#define MAC_REG_CR0_SET 0x08 +#define MAC_REG_CR1_SET 0x09 +#define MAC_REG_CR2_SET 0x0A +#define MAC_REG_CR3_SET 0x0B +#define MAC_REG_CR0_CLR 0x0C +#define MAC_REG_CR1_CLR 0x0D +#define MAC_REG_CR2_CLR 0x0E +#define MAC_REG_CR3_CLR 0x0F +#define MAC_REG_MAR 0x10 +#define MAC_REG_CAM 0x10 +#define MAC_REG_DEC_BASE_HI 0x18 +#define MAC_REG_DBF_BASE_HI 0x1C +#define MAC_REG_ISR_CTL 0x20 +#define MAC_REG_ISR_HOTMR 0x20 +#define MAC_REG_ISR_TSUPTHR 0x20 +#define MAC_REG_ISR_RSUPTHR 0x20 +#define MAC_REG_ISR_CTL1 0x21 +#define MAC_REG_TXE_SR 0x22 +#define MAC_REG_RXE_SR 0x23 +#define MAC_REG_ISR 0x24 +#define MAC_REG_ISR0 0x24 +#define MAC_REG_ISR1 0x25 +#define MAC_REG_ISR2 0x26 +#define MAC_REG_ISR3 0x27 +#define MAC_REG_IMR 0x28 +#define MAC_REG_IMR0 0x28 +#define MAC_REG_IMR1 0x29 +#define MAC_REG_IMR2 0x2A +#define MAC_REG_IMR3 0x2B +#define MAC_REG_TDCSR_SET 0x30 +#define MAC_REG_RDCSR_SET 0x32 +#define MAC_REG_TDCSR_CLR 0x34 +#define MAC_REG_RDCSR_CLR 0x36 +#define MAC_REG_RDBASE_LO 0x38 +#define MAC_REG_RDINDX 0x3C +#define MAC_REG_TDBASE_LO 0x40 +#define MAC_REG_RDCSIZE 0x50 +#define MAC_REG_TDCSIZE 0x52 +#define MAC_REG_TDINDX 0x54 +#define MAC_REG_TDIDX0 0x54 +#define MAC_REG_TDIDX1 0x56 +#define MAC_REG_TDIDX2 0x58 +#define MAC_REG_TDIDX3 0x5A +#define MAC_REG_PAUSE_TIMER 0x5C +#define MAC_REG_RBRDU 0x5E +#define MAC_REG_FIFO_TEST0 0x60 +#define MAC_REG_FIFO_TEST1 0x64 +#define MAC_REG_CAMADDR 0x68 +#define MAC_REG_CAMCR 0x69 +#define MAC_REG_GFTEST 0x6A +#define MAC_REG_FTSTCMD 0x6B +#define MAC_REG_MIICFG 0x6C +#define MAC_REG_MIISR 0x6D +#define MAC_REG_PHYSR0 0x6E +#define MAC_REG_PHYSR1 0x6F +#define MAC_REG_MIICR 0x70 +#define MAC_REG_MIIADR 0x71 +#define MAC_REG_MIIDATA 0x72 +#define MAC_REG_SOFT_TIMER0 0x74 +#define MAC_REG_SOFT_TIMER1 0x76 +#define MAC_REG_CFGA 0x78 +#define MAC_REG_CFGB 0x79 +#define MAC_REG_CFGC 0x7A +#define MAC_REG_CFGD 0x7B +#define MAC_REG_DCFG0 0x7C +#define MAC_REG_DCFG1 0x7D +#define MAC_REG_MCFG0 0x7E +#define MAC_REG_MCFG1 0x7F + +#define MAC_REG_TBIST 0x80 +#define MAC_REG_RBIST 0x81 +#define MAC_REG_PMCC 0x82 +#define MAC_REG_STICKHW 0x83 +#define MAC_REG_MIBCR 0x84 +#define MAC_REG_EERSV 0x85 +#define MAC_REG_REVID 0x86 +#define MAC_REG_MIBREAD 0x88 +#define MAC_REG_BPMA 0x8C +#define MAC_REG_EEWR_DATA 0x8C +#define MAC_REG_BPMD_WR 0x8F +#define MAC_REG_BPCMD 0x90 +#define MAC_REG_BPMD_RD 0x91 +#define MAC_REG_EECHKSUM 0x92 +#define MAC_REG_EECSR 0x93 +#define MAC_REG_EERD_DATA 0x94 +#define MAC_REG_EADDR 0x96 +#define MAC_REG_EMBCMD 0x97 +#define MAC_REG_JMPSR0 0x98 +#define MAC_REG_JMPSR1 0x99 +#define MAC_REG_JMPSR2 0x9A +#define MAC_REG_JMPSR3 0x9B +#define MAC_REG_CHIPGSR 0x9C +#define MAC_REG_TESTCFG 0x9D +#define MAC_REG_DEBUG 0x9E +#define MAC_REG_CHIPGCR 0x9F +#define MAC_REG_WOLCR0_SET 0xA0 +#define MAC_REG_WOLCR1_SET 0xA1 +#define MAC_REG_PWCFG_SET 0xA2 +#define MAC_REG_WOLCFG_SET 0xA3 +#define MAC_REG_WOLCR0_CLR 0xA4 +#define MAC_REG_WOLCR1_CLR 0xA5 +#define MAC_REG_PWCFG_CLR 0xA6 +#define MAC_REG_WOLCFG_CLR 0xA7 +#define MAC_REG_WOLSR0_SET 0xA8 +#define MAC_REG_WOLSR1_SET 0xA9 +#define MAC_REG_WOLSR0_CLR 0xAC +#define MAC_REG_WOLSR1_CLR 0xAD +#define MAC_REG_PATRN_CRC0 0xB0 +#define MAC_REG_PATRN_CRC1 0xB2 +#define MAC_REG_PATRN_CRC2 0xB4 +#define MAC_REG_PATRN_CRC3 0xB6 +#define MAC_REG_PATRN_CRC4 0xB8 +#define MAC_REG_PATRN_CRC5 0xBA +#define MAC_REG_PATRN_CRC6 0xBC +#define MAC_REG_PATRN_CRC7 0xBE +#define MAC_REG_BYTEMSK0_0 0xC0 +#define MAC_REG_BYTEMSK0_1 0xC4 +#define MAC_REG_BYTEMSK0_2 0xC8 +#define MAC_REG_BYTEMSK0_3 0xCC +#define MAC_REG_BYTEMSK1_0 0xD0 +#define MAC_REG_BYTEMSK1_1 0xD4 +#define MAC_REG_BYTEMSK1_2 0xD8 +#define MAC_REG_BYTEMSK1_3 0xDC +#define MAC_REG_BYTEMSK2_0 0xE0 +#define MAC_REG_BYTEMSK2_1 0xE4 +#define MAC_REG_BYTEMSK2_2 0xE8 +#define MAC_REG_BYTEMSK2_3 0xEC +#define MAC_REG_BYTEMSK3_0 0xF0 +#define MAC_REG_BYTEMSK3_1 0xF4 +#define MAC_REG_BYTEMSK3_2 0xF8 +#define MAC_REG_BYTEMSK3_3 0xFC + +/* + * Bits in the RCR register + */ + +#define RCR_AS 0x80 +#define RCR_AP 0x40 +#define RCR_AL 0x20 +#define RCR_PROM 0x10 +#define RCR_AB 0x08 +#define RCR_AM 0x04 +#define RCR_AR 0x02 +#define RCR_SEP 0x01 + +/* + * Bits in the TCR register + */ + +#define TCR_TB2BDIS 0x80 +#define TCR_COLTMC1 0x08 +#define TCR_COLTMC0 0x04 +#define TCR_LB1 0x02 /* loopback[1] */ +#define TCR_LB0 0x01 /* loopback[0] */ + +/* + * Bits in the CR0 register + */ + +#define CR0_TXON 0x00000008UL +#define CR0_RXON 0x00000004UL +#define CR0_STOP 0x00000002UL /* stop MAC, default = 1 */ +#define CR0_STRT 0x00000001UL /* start MAC */ +#define CR0_SFRST 0x00008000UL /* software reset */ +#define CR0_TM1EN 0x00004000UL +#define CR0_TM0EN 0x00002000UL +#define CR0_DPOLL 0x00000800UL /* disable rx/tx auto polling */ +#define CR0_DISAU 0x00000100UL +#define CR0_XONEN 0x00800000UL +#define CR0_FDXTFCEN 0x00400000UL /* full-duplex TX flow control enable */ +#define CR0_FDXRFCEN 0x00200000UL /* full-duplex RX flow control enable */ +#define CR0_HDXFCEN 0x00100000UL /* half-duplex flow control enable */ +#define CR0_XHITH1 0x00080000UL /* TX XON high threshold 1 */ +#define CR0_XHITH0 0x00040000UL /* TX XON high threshold 0 */ +#define CR0_XLTH1 0x00020000UL /* TX pause frame low threshold 1 */ +#define CR0_XLTH0 0x00010000UL /* TX pause frame low threshold 0 */ +#define CR0_GSPRST 0x80000000UL +#define CR0_FORSRST 0x40000000UL +#define CR0_FPHYRST 0x20000000UL +#define CR0_DIAG 0x10000000UL +#define CR0_INTPCTL 0x04000000UL +#define CR0_GINTMSK1 0x02000000UL +#define CR0_GINTMSK0 0x01000000UL + +/* + * Bits in the CR1 register + */ + +#define CR1_SFRST 0x80 /* software reset */ +#define CR1_TM1EN 0x40 +#define CR1_TM0EN 0x20 +#define CR1_DPOLL 0x08 /* disable rx/tx auto polling */ +#define CR1_DISAU 0x01 + +/* + * Bits in the CR2 register + */ + +#define CR2_XONEN 0x80 +#define CR2_FDXTFCEN 0x40 /* full-duplex TX flow control enable */ +#define CR2_FDXRFCEN 0x20 /* full-duplex RX flow control enable */ +#define CR2_HDXFCEN 0x10 /* half-duplex flow control enable */ +#define CR2_XHITH1 0x08 /* TX XON high threshold 1 */ +#define CR2_XHITH0 0x04 /* TX XON high threshold 0 */ +#define CR2_XLTH1 0x02 /* TX pause frame low threshold 1 */ +#define CR2_XLTH0 0x01 /* TX pause frame low threshold 0 */ + +/* + * Bits in the CR3 register + */ + +#define CR3_GSPRST 0x80 +#define CR3_FORSRST 0x40 +#define CR3_FPHYRST 0x20 +#define CR3_DIAG 0x10 +#define CR3_INTPCTL 0x04 +#define CR3_GINTMSK1 0x02 +#define CR3_GINTMSK0 0x01 + +#define ISRCTL_UDPINT 0x8000 +#define ISRCTL_TSUPDIS 0x4000 +#define ISRCTL_RSUPDIS 0x2000 +#define ISRCTL_PMSK1 0x1000 +#define ISRCTL_PMSK0 0x0800 +#define ISRCTL_INTPD 0x0400 +#define ISRCTL_HCRLD 0x0200 +#define ISRCTL_SCRLD 0x0100 + +/* + * Bits in the ISR_CTL1 register + */ + +#define ISRCTL1_UDPINT 0x80 +#define ISRCTL1_TSUPDIS 0x40 +#define ISRCTL1_RSUPDIS 0x20 +#define ISRCTL1_PMSK1 0x10 +#define ISRCTL1_PMSK0 0x08 +#define ISRCTL1_INTPD 0x04 +#define ISRCTL1_HCRLD 0x02 +#define ISRCTL1_SCRLD 0x01 + +/* + * Bits in the TXE_SR register + */ + +#define TXESR_TFDBS 0x08 +#define TXESR_TDWBS 0x04 +#define TXESR_TDRBS 0x02 +#define TXESR_TDSTR 0x01 + +/* + * Bits in the RXE_SR register + */ + +#define RXESR_RFDBS 0x08 +#define RXESR_RDWBS 0x04 +#define RXESR_RDRBS 0x02 +#define RXESR_RDSTR 0x01 + +/* + * Bits in the ISR register + */ + +#define ISR_ISR3 0x80000000UL +#define ISR_ISR2 0x40000000UL +#define ISR_ISR1 0x20000000UL +#define ISR_ISR0 0x10000000UL +#define ISR_TXSTLI 0x02000000UL +#define ISR_RXSTLI 0x01000000UL +#define ISR_HFLD 0x00800000UL +#define ISR_UDPI 0x00400000UL +#define ISR_MIBFI 0x00200000UL +#define ISR_SHDNI 0x00100000UL +#define ISR_PHYI 0x00080000UL +#define ISR_PWEI 0x00040000UL +#define ISR_TMR1I 0x00020000UL +#define ISR_TMR0I 0x00010000UL +#define ISR_SRCI 0x00008000UL +#define ISR_LSTPEI 0x00004000UL +#define ISR_LSTEI 0x00002000UL +#define ISR_OVFI 0x00001000UL +#define ISR_FLONI 0x00000800UL +#define ISR_RACEI 0x00000400UL +#define ISR_TXWB1I 0x00000200UL +#define ISR_TXWB0I 0x00000100UL +#define ISR_PTX3I 0x00000080UL +#define ISR_PTX2I 0x00000040UL +#define ISR_PTX1I 0x00000020UL +#define ISR_PTX0I 0x00000010UL +#define ISR_PTXI 0x00000008UL +#define ISR_PRXI 0x00000004UL +#define ISR_PPTXI 0x00000002UL +#define ISR_PPRXI 0x00000001UL + +/* + * Bits in the IMR register + */ + +#define IMR_TXSTLM 0x02000000UL +#define IMR_UDPIM 0x00400000UL +#define IMR_MIBFIM 0x00200000UL +#define IMR_SHDNIM 0x00100000UL +#define IMR_PHYIM 0x00080000UL +#define IMR_PWEIM 0x00040000UL +#define IMR_TMR1IM 0x00020000UL +#define IMR_TMR0IM 0x00010000UL + +#define IMR_SRCIM 0x00008000UL +#define IMR_LSTPEIM 0x00004000UL +#define IMR_LSTEIM 0x00002000UL +#define IMR_OVFIM 0x00001000UL +#define IMR_FLONIM 0x00000800UL +#define IMR_RACEIM 0x00000400UL +#define IMR_TXWB1IM 0x00000200UL +#define IMR_TXWB0IM 0x00000100UL + +#define IMR_PTX3IM 0x00000080UL +#define IMR_PTX2IM 0x00000040UL +#define IMR_PTX1IM 0x00000020UL +#define IMR_PTX0IM 0x00000010UL +#define IMR_PTXIM 0x00000008UL +#define IMR_PRXIM 0x00000004UL +#define IMR_PPTXIM 0x00000002UL +#define IMR_PPRXIM 0x00000001UL + +/* 0x0013FB0FUL = initial value of IMR */ + +#define INT_MASK_DEF ( IMR_PPTXIM|IMR_PPRXIM| IMR_PTXIM|IMR_PRXIM | \ + IMR_PWEIM|IMR_TXWB0IM|IMR_TXWB1IM|IMR_FLONIM| \ + IMR_OVFIM|IMR_LSTEIM|IMR_LSTPEIM|IMR_SRCIM|IMR_MIBFIM|\ + IMR_SHDNIM |IMR_TMR1IM|IMR_TMR0IM|IMR_TXSTLM ) + +/* + * Bits in the TDCSR0/1, RDCSR0 register + */ + +#define TRDCSR_DEAD 0x0008 +#define TRDCSR_WAK 0x0004 +#define TRDCSR_ACT 0x0002 +#define TRDCSR_RUN 0x0001 + +/* + * Bits in the CAMADDR register + */ + +#define CAMADDR_CAMEN 0x80 +#define CAMADDR_VCAMSL 0x40 + +/* + * Bits in the CAMCR register + */ + +#define CAMCR_PS1 0x80 +#define CAMCR_PS0 0x40 +#define CAMCR_AITRPKT 0x20 +#define CAMCR_AITR16 0x10 +#define CAMCR_CAMRD 0x08 +#define CAMCR_CAMWR 0x04 +#define CAMCR_PS_CAM_MASK 0x40 +#define CAMCR_PS_CAM_DATA 0x80 +#define CAMCR_PS_MAR 0x00 + +/* + * Bits in the MIICFG register + */ + +#define MIICFG_MPO1 0x80 +#define MIICFG_MPO0 0x40 +#define MIICFG_MFDC 0x20 + +/* + * Bits in the MIISR register + */ + +#define MIISR_MIDLE 0x80 + +/* + * Bits in the PHYSR0 register + */ + +#define PHYSR0_PHYRST 0x80 +#define PHYSR0_LINKGD 0x40 +#define PHYSR0_FDPX 0x10 +#define PHYSR0_SPDG 0x08 +#define PHYSR0_SPD10 0x04 +#define PHYSR0_RXFLC 0x02 +#define PHYSR0_TXFLC 0x01 + +/* + * Bits in the PHYSR1 register + */ + +#define PHYSR1_PHYTBI 0x01 + +/* + * Bits in the MIICR register + */ + +#define MIICR_MAUTO 0x80 +#define MIICR_RCMD 0x40 +#define MIICR_WCMD 0x20 +#define MIICR_MDPM 0x10 +#define MIICR_MOUT 0x08 +#define MIICR_MDO 0x04 +#define MIICR_MDI 0x02 +#define MIICR_MDC 0x01 + +/* + * Bits in the MIIADR register + */ + +#define MIIADR_SWMPL 0x80 + +/* + * Bits in the CFGA register + */ + +#define CFGA_PMHCTG 0x08 +#define CFGA_GPIO1PD 0x04 +#define CFGA_ABSHDN 0x02 +#define CFGA_PACPI 0x01 + +/* + * Bits in the CFGB register + */ + +#define CFGB_GTCKOPT 0x80 +#define CFGB_MIIOPT 0x40 +#define CFGB_CRSEOPT 0x20 +#define CFGB_OFSET 0x10 +#define CFGB_CRANDOM 0x08 +#define CFGB_CAP 0x04 +#define CFGB_MBA 0x02 +#define CFGB_BAKOPT 0x01 + +/* + * Bits in the CFGC register + */ + +#define CFGC_EELOAD 0x80 +#define CFGC_BROPT 0x40 +#define CFGC_DLYEN 0x20 +#define CFGC_DTSEL 0x10 +#define CFGC_BTSEL 0x08 +#define CFGC_BPS2 0x04 /* bootrom select[2] */ +#define CFGC_BPS1 0x02 /* bootrom select[1] */ +#define CFGC_BPS0 0x01 /* bootrom select[0] */ + +/* + * Bits in the CFGD register + */ + +#define CFGD_IODIS 0x80 +#define CFGD_MSLVDACEN 0x40 +#define CFGD_CFGDACEN 0x20 +#define CFGD_PCI64EN 0x10 +#define CFGD_HTMRL4 0x08 + +/* + * Bits in the DCFG1 register + */ + +#define DCFG_XMWI 0x8000 +#define DCFG_XMRM 0x4000 +#define DCFG_XMRL 0x2000 +#define DCFG_PERDIS 0x1000 +#define DCFG_MRWAIT 0x0400 +#define DCFG_MWWAIT 0x0200 +#define DCFG_LATMEN 0x0100 + +/* + * Bits in the MCFG0 register + */ + +#define MCFG_RXARB 0x0080 +#define MCFG_RFT1 0x0020 +#define MCFG_RFT0 0x0010 +#define MCFG_LOWTHOPT 0x0008 +#define MCFG_PQEN 0x0004 +#define MCFG_RTGOPT 0x0002 +#define MCFG_VIDFR 0x0001 + +/* + * Bits in the MCFG1 register + */ + +#define MCFG_TXARB 0x8000 +#define MCFG_TXQBK1 0x0800 +#define MCFG_TXQBK0 0x0400 +#define MCFG_TXQNOBK 0x0200 +#define MCFG_SNAPOPT 0x0100 + +/* + * Bits in the PMCC register + */ + +#define PMCC_DSI 0x80 +#define PMCC_D2_DIS 0x40 +#define PMCC_D1_DIS 0x20 +#define PMCC_D3C_EN 0x10 +#define PMCC_D3H_EN 0x08 +#define PMCC_D2_EN 0x04 +#define PMCC_D1_EN 0x02 +#define PMCC_D0_EN 0x01 + +/* + * Bits in STICKHW + */ + +#define STICKHW_SWPTAG 0x10 +#define STICKHW_WOLSR 0x08 +#define STICKHW_WOLEN 0x04 +#define STICKHW_DS1 0x02 /* R/W by software/cfg cycle */ +#define STICKHW_DS0 0x01 /* suspend well DS write port */ + +/* + * Bits in the MIBCR register + */ + +#define MIBCR_MIBISTOK 0x80 +#define MIBCR_MIBISTGO 0x40 +#define MIBCR_MIBINC 0x20 +#define MIBCR_MIBHI 0x10 +#define MIBCR_MIBFRZ 0x08 +#define MIBCR_MIBFLSH 0x04 +#define MIBCR_MPTRINI 0x02 +#define MIBCR_MIBCLR 0x01 + +/* + * Bits in the EERSV register + */ + +#define EERSV_BOOT_RPL ((u8) 0x01) /* Boot method selection for VT6110 */ + +#define EERSV_BOOT_MASK ((u8) 0x06) +#define EERSV_BOOT_INT19 ((u8) 0x00) +#define EERSV_BOOT_INT18 ((u8) 0x02) +#define EERSV_BOOT_LOCAL ((u8) 0x04) +#define EERSV_BOOT_BEV ((u8) 0x06) + + +/* + * Bits in BPCMD + */ + +#define BPCMD_BPDNE 0x80 +#define BPCMD_EBPWR 0x02 +#define BPCMD_EBPRD 0x01 + +/* + * Bits in the EECSR register + */ + +#define EECSR_EMBP 0x40 /* eeprom embeded programming */ +#define EECSR_RELOAD 0x20 /* eeprom content reload */ +#define EECSR_DPM 0x10 /* eeprom direct programming */ +#define EECSR_ECS 0x08 /* eeprom CS pin */ +#define EECSR_ECK 0x04 /* eeprom CK pin */ +#define EECSR_EDI 0x02 /* eeprom DI pin */ +#define EECSR_EDO 0x01 /* eeprom DO pin */ + +/* + * Bits in the EMBCMD register + */ + +#define EMBCMD_EDONE 0x80 +#define EMBCMD_EWDIS 0x08 +#define EMBCMD_EWEN 0x04 +#define EMBCMD_EWR 0x02 +#define EMBCMD_ERD 0x01 + +/* + * Bits in TESTCFG register + */ + +#define TESTCFG_HBDIS 0x80 + +/* + * Bits in CHIPGCR register + */ + +#define CHIPGCR_FCGMII 0x80 +#define CHIPGCR_FCFDX 0x40 +#define CHIPGCR_FCRESV 0x20 +#define CHIPGCR_FCMODE 0x10 +#define CHIPGCR_LPSOPT 0x08 +#define CHIPGCR_TM1US 0x04 +#define CHIPGCR_TM0US 0x02 +#define CHIPGCR_PHYINTEN 0x01 + +/* + * Bits in WOLCR0 + */ + +#define WOLCR_MSWOLEN7 0x0080 /* enable pattern match filtering */ +#define WOLCR_MSWOLEN6 0x0040 +#define WOLCR_MSWOLEN5 0x0020 +#define WOLCR_MSWOLEN4 0x0010 +#define WOLCR_MSWOLEN3 0x0008 +#define WOLCR_MSWOLEN2 0x0004 +#define WOLCR_MSWOLEN1 0x0002 +#define WOLCR_MSWOLEN0 0x0001 +#define WOLCR_ARP_EN 0x0001 + +/* + * Bits in WOLCR1 + */ + +#define WOLCR_LINKOFF_EN 0x0800 /* link off detected enable */ +#define WOLCR_LINKON_EN 0x0400 /* link on detected enable */ +#define WOLCR_MAGIC_EN 0x0200 /* magic packet filter enable */ +#define WOLCR_UNICAST_EN 0x0100 /* unicast filter enable */ + + +/* + * Bits in PWCFG + */ + +#define PWCFG_PHYPWOPT 0x80 /* internal MII I/F timing */ +#define PWCFG_PCISTICK 0x40 /* PCI sticky R/W enable */ +#define PWCFG_WOLTYPE 0x20 /* pulse(1) or button (0) */ +#define PWCFG_LEGCY_WOL 0x10 +#define PWCFG_PMCSR_PME_SR 0x08 +#define PWCFG_PMCSR_PME_EN 0x04 /* control by PCISTICK */ +#define PWCFG_LEGACY_WOLSR 0x02 /* Legacy WOL_SR shadow */ +#define PWCFG_LEGACY_WOLEN 0x01 /* Legacy WOL_EN shadow */ + +/* + * Bits in WOLCFG + */ + +#define WOLCFG_PMEOVR 0x80 /* for legacy use, force PMEEN always */ +#define WOLCFG_SAM 0x20 /* accept multicast case reset, default=0 */ +#define WOLCFG_SAB 0x10 /* accept broadcast case reset, default=0 */ +#define WOLCFG_SMIIACC 0x08 /* ?? */ +#define WOLCFG_SGENWH 0x02 +#define WOLCFG_PHYINTEN 0x01 /* 0:PHYINT trigger enable, 1:use internal MII + to report status change */ +/* + * Bits in WOLSR1 + */ + +#define WOLSR_LINKOFF_INT 0x0800 +#define WOLSR_LINKON_INT 0x0400 +#define WOLSR_MAGIC_INT 0x0200 +#define WOLSR_UNICAST_INT 0x0100 + +/* + * Ethernet address filter type + */ + +#define PKT_TYPE_NONE 0x0000 /* Turn off receiver */ +#define PKT_TYPE_DIRECTED 0x0001 /* obselete, directed address is always accepted */ +#define PKT_TYPE_MULTICAST 0x0002 +#define PKT_TYPE_ALL_MULTICAST 0x0004 +#define PKT_TYPE_BROADCAST 0x0008 +#define PKT_TYPE_PROMISCUOUS 0x0020 +#define PKT_TYPE_LONG 0x2000 /* NOTE.... the definition of LONG is >2048 bytes in our chip */ +#define PKT_TYPE_RUNT 0x4000 +#define PKT_TYPE_ERROR 0x8000 /* Accept error packets, e.g. CRC error */ + +/* + * Loopback mode + */ + +#define MAC_LB_NONE 0x00 +#define MAC_LB_INTERNAL 0x01 +#define MAC_LB_EXTERNAL 0x02 + +/* + * Enabled mask value of irq + */ + +#if defined(_SIM) +#define IMR_MASK_VALUE 0x0033FF0FUL /* initial value of IMR + set IMR0 to 0x0F according to spec */ + +#else +#define IMR_MASK_VALUE 0x0013FB0FUL /* initial value of IMR + ignore MIBFI,RACEI to + reduce intr. frequency + NOTE.... do not enable NoBuf int mask at driver driver + when (1) NoBuf -> RxThreshold = SF + (2) OK -> RxThreshold = original value + */ +#endif + +/* + * Revision id + */ + +#define REV_ID_VT3119_A0 0x00 +#define REV_ID_VT3119_A1 0x01 +#define REV_ID_VT3216_A0 0x10 + +/* + * Max time out delay time + */ + +#define W_MAX_TIMEOUT 0x0FFFU + + +/* + * MAC registers as a structure. Cannot be directly accessed this + * way but generates offsets for readl/writel() calls + */ + +struct mac_regs { + volatile u8 PAR[6]; /* 0x00 */ + volatile u8 RCR; + volatile u8 TCR; + + volatile u32 CR0Set; /* 0x08 */ + volatile u32 CR0Clr; /* 0x0C */ + + volatile u8 MARCAM[8]; /* 0x10 */ + + volatile u32 DecBaseHi; /* 0x18 */ + volatile u16 DbfBaseHi; /* 0x1C */ + volatile u16 reserved_1E; + + volatile u16 ISRCTL; /* 0x20 */ + volatile u8 TXESR; + volatile u8 RXESR; + + volatile u32 ISR; /* 0x24 */ + volatile u32 IMR; + + volatile u32 TDStatusPort; /* 0x2C */ + + volatile u16 TDCSRSet; /* 0x30 */ + volatile u8 RDCSRSet; + volatile u8 reserved_33; + volatile u16 TDCSRClr; + volatile u8 RDCSRClr; + volatile u8 reserved_37; + + volatile u32 RDBaseLo; /* 0x38 */ + volatile u16 RDIdx; /* 0x3C */ + volatile u16 reserved_3E; + + volatile u32 TDBaseLo[4]; /* 0x40 */ + + volatile u16 RDCSize; /* 0x50 */ + volatile u16 TDCSize; /* 0x52 */ + volatile u16 TDIdx[4]; /* 0x54 */ + volatile u16 tx_pause_timer; /* 0x5C */ + volatile u16 RBRDU; /* 0x5E */ + + volatile u32 FIFOTest0; /* 0x60 */ + volatile u32 FIFOTest1; /* 0x64 */ + + volatile u8 CAMADDR; /* 0x68 */ + volatile u8 CAMCR; /* 0x69 */ + volatile u8 GFTEST; /* 0x6A */ + volatile u8 FTSTCMD; /* 0x6B */ + + volatile u8 MIICFG; /* 0x6C */ + volatile u8 MIISR; + volatile u8 PHYSR0; + volatile u8 PHYSR1; + volatile u8 MIICR; + volatile u8 MIIADR; + volatile u16 MIIDATA; + + volatile u16 SoftTimer0; /* 0x74 */ + volatile u16 SoftTimer1; + + volatile u8 CFGA; /* 0x78 */ + volatile u8 CFGB; + volatile u8 CFGC; + volatile u8 CFGD; + + volatile u16 DCFG; /* 0x7C */ + volatile u16 MCFG; + + volatile u8 TBIST; /* 0x80 */ + volatile u8 RBIST; + volatile u8 PMCPORT; + volatile u8 STICKHW; + + volatile u8 MIBCR; /* 0x84 */ + volatile u8 reserved_85; + volatile u8 rev_id; + volatile u8 PORSTS; + + volatile u32 MIBData; /* 0x88 */ + + volatile u16 EEWrData; + + volatile u8 reserved_8E; + volatile u8 BPMDWr; + volatile u8 BPCMD; + volatile u8 BPMDRd; + + volatile u8 EECHKSUM; /* 0x92 */ + volatile u8 EECSR; + + volatile u16 EERdData; /* 0x94 */ + volatile u8 EADDR; + volatile u8 EMBCMD; + + + volatile u8 JMPSR0; /* 0x98 */ + volatile u8 JMPSR1; + volatile u8 JMPSR2; + volatile u8 JMPSR3; + volatile u8 CHIPGSR; /* 0x9C */ + volatile u8 TESTCFG; + volatile u8 DEBUG; + volatile u8 CHIPGCR; + + volatile u16 WOLCRSet; /* 0xA0 */ + volatile u8 PWCFGSet; + volatile u8 WOLCFGSet; + + volatile u16 WOLCRClr; /* 0xA4 */ + volatile u8 PWCFGCLR; + volatile u8 WOLCFGClr; + + volatile u16 WOLSRSet; /* 0xA8 */ + volatile u16 reserved_AA; + + volatile u16 WOLSRClr; /* 0xAC */ + volatile u16 reserved_AE; + + volatile u16 PatternCRC[8]; /* 0xB0 */ + volatile u32 ByteMask[4][4]; /* 0xC0 */ +} __attribute__ ((__packed__)); + + +enum hw_mib { + HW_MIB_ifRxAllPkts = 0, + HW_MIB_ifRxOkPkts, + HW_MIB_ifTxOkPkts, + HW_MIB_ifRxErrorPkts, + HW_MIB_ifRxRuntOkPkt, + HW_MIB_ifRxRuntErrPkt, + HW_MIB_ifRx64Pkts, + HW_MIB_ifTx64Pkts, + HW_MIB_ifRx65To127Pkts, + HW_MIB_ifTx65To127Pkts, + HW_MIB_ifRx128To255Pkts, + HW_MIB_ifTx128To255Pkts, + HW_MIB_ifRx256To511Pkts, + HW_MIB_ifTx256To511Pkts, + HW_MIB_ifRx512To1023Pkts, + HW_MIB_ifTx512To1023Pkts, + HW_MIB_ifRx1024To1518Pkts, + HW_MIB_ifTx1024To1518Pkts, + HW_MIB_ifTxEtherCollisions, + HW_MIB_ifRxPktCRCE, + HW_MIB_ifRxJumboPkts, + HW_MIB_ifTxJumboPkts, + HW_MIB_ifRxMacControlFrames, + HW_MIB_ifTxMacControlFrames, + HW_MIB_ifRxPktFAE, + HW_MIB_ifRxLongOkPkt, + HW_MIB_ifRxLongPktErrPkt, + HW_MIB_ifTXSQEErrors, + HW_MIB_ifRxNobuf, + HW_MIB_ifRxSymbolErrors, + HW_MIB_ifInRangeLengthErrors, + HW_MIB_ifLateCollisions, + HW_MIB_SIZE +}; + +enum chip_type { + CHIP_TYPE_VT6110 = 1, +}; + +struct velocity_info_tbl { + enum chip_type chip_id; + char *name; + int io_size; + int txqueue; + u32 flags; +}; + +static struct velocity_info_tbl *info; + +#define mac_hw_mibs_init(regs) {\ + BYTE_REG_BITS_ON(MIBCR_MIBFRZ,&((regs)->MIBCR));\ + BYTE_REG_BITS_ON(MIBCR_MIBCLR,&((regs)->MIBCR));\ + do {}\ + while (BYTE_REG_BITS_IS_ON(MIBCR_MIBCLR,&((regs)->MIBCR)));\ + BYTE_REG_BITS_OFF(MIBCR_MIBFRZ,&((regs)->MIBCR));\ +} + +#define mac_read_isr(regs) readl(&((regs)->ISR)) +#define mac_write_isr(regs, x) writel((x),&((regs)->ISR)) +#define mac_clear_isr(regs) writel(0xffffffffL,&((regs)->ISR)) + +#define mac_write_int_mask(mask, regs) writel((mask),&((regs)->IMR)); +#define mac_disable_int(regs) writel(CR0_GINTMSK1,&((regs)->CR0Clr)) +#define mac_enable_int(regs) writel(CR0_GINTMSK1,&((regs)->CR0Set)) + +#define mac_hw_mibs_read(regs, MIBs) {\ + int i;\ + BYTE_REG_BITS_ON(MIBCR_MPTRINI,&((regs)->MIBCR));\ + for (i=0;iMIBData));\ + }\ +} + +#define mac_set_dma_length(regs, n) {\ + BYTE_REG_BITS_SET((n),0x07,&((regs)->DCFG));\ +} + +#define mac_set_rx_thresh(regs, n) {\ + BYTE_REG_BITS_SET((n),(MCFG_RFT0|MCFG_RFT1),&((regs)->MCFG));\ +} + +#define mac_rx_queue_run(regs) {\ + writeb(TRDCSR_RUN, &((regs)->RDCSRSet));\ +} + +#define mac_rx_queue_wake(regs) {\ + writeb(TRDCSR_WAK, &((regs)->RDCSRSet));\ +} + +#define mac_tx_queue_run(regs, n) {\ + writew(TRDCSR_RUN<<((n)*4),&((regs)->TDCSRSet));\ +} + +#define mac_tx_queue_wake(regs, n) {\ + writew(TRDCSR_WAK<<(n*4),&((regs)->TDCSRSet));\ +} + +#define mac_eeprom_reload(regs) {\ + int i=0;\ + BYTE_REG_BITS_ON(EECSR_RELOAD,&((regs)->EECSR));\ + do {\ + udelay(10);\ + if (i++>0x1000) {\ + break;\ + }\ + }while (BYTE_REG_BITS_IS_ON(EECSR_RELOAD,&((regs)->EECSR)));\ +} + +enum velocity_cam_type { + VELOCITY_VLAN_ID_CAM = 0, + VELOCITY_MULTICAST_CAM +}; + +/** + * mac_get_cam_mask - Read a CAM mask + * @regs: register block for this velocity + * @mask: buffer to store mask + * @cam_type: CAM to fetch + * + * Fetch the mask bits of the selected CAM and store them into the + * provided mask buffer. + */ + +static inline void mac_get_cam_mask(struct mac_regs *regs, u8 * mask, + enum velocity_cam_type cam_type) +{ + int i; + /* Select CAM mask */ + BYTE_REG_BITS_SET(CAMCR_PS_CAM_MASK, CAMCR_PS1 | CAMCR_PS0, + ®s->CAMCR); + + if (cam_type == VELOCITY_VLAN_ID_CAM) + writeb(CAMADDR_VCAMSL, ®s->CAMADDR); + else + writeb(0, ®s->CAMADDR); + + /* read mask */ + for (i = 0; i < 8; i++) + *mask++ = readb(&(regs->MARCAM[i])); + + /* disable CAMEN */ + writeb(0, ®s->CAMADDR); + + /* Select mar */ + BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, + ®s->CAMCR); + +} + +/** + * mac_set_cam_mask - Set a CAM mask + * @regs: register block for this velocity + * @mask: CAM mask to load + * @cam_type: CAM to store + * + * Store a new mask into a CAM + */ + +static inline void mac_set_cam_mask(struct mac_regs *regs, u8 * mask, + enum velocity_cam_type cam_type) +{ + int i; + /* Select CAM mask */ + BYTE_REG_BITS_SET(CAMCR_PS_CAM_MASK, CAMCR_PS1 | CAMCR_PS0, + ®s->CAMCR); + + if (cam_type == VELOCITY_VLAN_ID_CAM) + writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL, ®s->CAMADDR); + else + writeb(CAMADDR_CAMEN, ®s->CAMADDR); + + for (i = 0; i < 8; i++) { + writeb(*mask++, &(regs->MARCAM[i])); + } + /* disable CAMEN */ + writeb(0, ®s->CAMADDR); + + /* Select mar */ + BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, + ®s->CAMCR); +} + +/** + * mac_set_cam - set CAM data + * @regs: register block of this velocity + * @idx: Cam index + * @addr: 2 or 6 bytes of CAM data + * @cam_type: CAM to load + * + * Load an address or vlan tag into a CAM + */ + +static inline void mac_set_cam(struct mac_regs *regs, int idx, u8 * addr, + enum velocity_cam_type cam_type) +{ + int i; + + /* Select CAM mask */ + BYTE_REG_BITS_SET(CAMCR_PS_CAM_DATA, CAMCR_PS1 | CAMCR_PS0, + ®s->CAMCR); + + idx &= (64 - 1); + + if (cam_type == VELOCITY_VLAN_ID_CAM) + writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL | idx, + ®s->CAMADDR); + else + writeb(CAMADDR_CAMEN | idx, ®s->CAMADDR); + + if (cam_type == VELOCITY_VLAN_ID_CAM) + writew(*((u16 *) addr), ®s->MARCAM[0]); + else { + for (i = 0; i < 6; i++) { + writeb(*addr++, &(regs->MARCAM[i])); + } + } + BYTE_REG_BITS_ON(CAMCR_CAMWR, ®s->CAMCR); + + udelay(10); + + writeb(0, ®s->CAMADDR); + + /* Select mar */ + BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, + ®s->CAMCR); +} + +/** + * mac_get_cam - fetch CAM data + * @regs: register block of this velocity + * @idx: Cam index + * @addr: buffer to hold up to 6 bytes of CAM data + * @cam_type: CAM to load + * + * Load an address or vlan tag from a CAM into the buffer provided by + * the caller. VLAN tags are 2 bytes the address cam entries are 6. + */ + +static inline void mac_get_cam(struct mac_regs *regs, int idx, u8 * addr, + enum velocity_cam_type cam_type) +{ + int i; + + /* Select CAM mask */ + BYTE_REG_BITS_SET(CAMCR_PS_CAM_DATA, CAMCR_PS1 | CAMCR_PS0, + ®s->CAMCR); + + idx &= (64 - 1); + + if (cam_type == VELOCITY_VLAN_ID_CAM) + writeb(CAMADDR_CAMEN | CAMADDR_VCAMSL | idx, + ®s->CAMADDR); + else + writeb(CAMADDR_CAMEN | idx, ®s->CAMADDR); + + BYTE_REG_BITS_ON(CAMCR_CAMRD, ®s->CAMCR); + + udelay(10); + + if (cam_type == VELOCITY_VLAN_ID_CAM) + *((u16 *) addr) = readw(&(regs->MARCAM[0])); + else + for (i = 0; i < 6; i++, addr++) + *((u8 *) addr) = readb(&(regs->MARCAM[i])); + + writeb(0, ®s->CAMADDR); + + /* Select mar */ + BYTE_REG_BITS_SET(CAMCR_PS_MAR, CAMCR_PS1 | CAMCR_PS0, + ®s->CAMCR); +} + +/** + * mac_wol_reset - reset WOL after exiting low power + * @regs: register block of this velocity + * + * Called after we drop out of wake on lan mode in order to + * reset the Wake on lan features. This function doesn't restore + * the rest of the logic from the result of sleep/wakeup + */ + +inline static void mac_wol_reset(struct mac_regs *regs) +{ + + /* Turn off SWPTAG right after leaving power mode */ + BYTE_REG_BITS_OFF(STICKHW_SWPTAG, ®s->STICKHW); + /* clear sticky bits */ + BYTE_REG_BITS_OFF((STICKHW_DS1 | STICKHW_DS0), ®s->STICKHW); + + BYTE_REG_BITS_OFF(CHIPGCR_FCGMII, ®s->CHIPGCR); + BYTE_REG_BITS_OFF(CHIPGCR_FCMODE, ®s->CHIPGCR); + /* disable force PME-enable */ + writeb(WOLCFG_PMEOVR, ®s->WOLCFGClr); + /* disable power-event config bit */ + writew(0xFFFF, ®s->WOLCRClr); + /* clear power status */ + writew(0xFFFF, ®s->WOLSRClr); +} + + +/* + * Header for WOL definitions. Used to compute hashes + */ + +typedef u8 MCAM_ADDR[ETH_ALEN]; + +struct arp_packet { + u8 dest_mac[ETH_ALEN]; + u8 src_mac[ETH_ALEN]; + u16 type; + u16 ar_hrd; + u16 ar_pro; + u8 ar_hln; + u8 ar_pln; + u16 ar_op; + u8 ar_sha[ETH_ALEN]; + u8 ar_sip[4]; + u8 ar_tha[ETH_ALEN]; + u8 ar_tip[4]; +} __attribute__ ((__packed__)); + +struct _magic_packet { + u8 dest_mac[6]; + u8 src_mac[6]; + u16 type; + u8 MAC[16][6]; + u8 password[6]; +} __attribute__ ((__packed__)); + +/* + * Store for chip context when saving and restoring status. Not + * all fields are saved/restored currently. + */ + +struct velocity_context { + u8 mac_reg[256]; + MCAM_ADDR cam_addr[MCAM_SIZE]; + u16 vcam[VCAM_SIZE]; + u32 cammask[2]; + u32 patcrc[2]; + u32 pattern[8]; +}; + + +/* + * MII registers. + */ + + +/* + * Registers in the MII (offset unit is WORD) + */ + +#define MII_REG_BMCR 0x00 // physical address +#define MII_REG_BMSR 0x01 // +#define MII_REG_PHYID1 0x02 // OUI +#define MII_REG_PHYID2 0x03 // OUI + Module ID + REV ID +#define MII_REG_ANAR 0x04 // +#define MII_REG_ANLPAR 0x05 // +#define MII_REG_G1000CR 0x09 // +#define MII_REG_G1000SR 0x0A // +#define MII_REG_MODCFG 0x10 // +#define MII_REG_TCSR 0x16 // +#define MII_REG_PLED 0x1B // +// NS, MYSON only +#define MII_REG_PCR 0x17 // +// ESI only +#define MII_REG_PCSR 0x17 // +#define MII_REG_AUXCR 0x1C // + +// Marvell 88E1000/88E1000S +#define MII_REG_PSCR 0x10 // PHY specific control register + +// +// Bits in the BMCR register +// +#define BMCR_RESET 0x8000 // +#define BMCR_LBK 0x4000 // +#define BMCR_SPEED100 0x2000 // +#define BMCR_AUTO 0x1000 // +#define BMCR_PD 0x0800 // +#define BMCR_ISO 0x0400 // +#define BMCR_REAUTO 0x0200 // +#define BMCR_FDX 0x0100 // +#define BMCR_SPEED1G 0x0040 // +// +// Bits in the BMSR register +// +#define BMSR_AUTOCM 0x0020 // +#define BMSR_LNK 0x0004 // + +// +// Bits in the ANAR register +// +#define ANAR_ASMDIR 0x0800 // Asymmetric PAUSE support +#define ANAR_PAUSE 0x0400 // Symmetric PAUSE Support +#define ANAR_T4 0x0200 // +#define ANAR_TXFD 0x0100 // +#define ANAR_TX 0x0080 // +#define ANAR_10FD 0x0040 // +#define ANAR_10 0x0020 // +// +// Bits in the ANLPAR register +// +#define ANLPAR_ASMDIR 0x0800 // Asymmetric PAUSE support +#define ANLPAR_PAUSE 0x0400 // Symmetric PAUSE Support +#define ANLPAR_T4 0x0200 // +#define ANLPAR_TXFD 0x0100 // +#define ANLPAR_TX 0x0080 // +#define ANLPAR_10FD 0x0040 // +#define ANLPAR_10 0x0020 // + +// +// Bits in the G1000CR register +// +#define G1000CR_1000FD 0x0200 // PHY is 1000-T Full-duplex capable +#define G1000CR_1000 0x0100 // PHY is 1000-T Half-duplex capable + +// +// Bits in the G1000SR register +// +#define G1000SR_1000FD 0x0800 // LP PHY is 1000-T Full-duplex capable +#define G1000SR_1000 0x0400 // LP PHY is 1000-T Half-duplex capable + +#define TCSR_ECHODIS 0x2000 // +#define AUXCR_MDPPS 0x0004 // + +// Bits in the PLED register +#define PLED_LALBE 0x0004 // + +// Marvell 88E1000/88E1000S Bits in the PHY specific control register (10h) +#define PSCR_ACRSTX 0x0800 // Assert CRS on Transmit + +#define PHYID_CICADA_CS8201 0x000FC410UL +#define PHYID_VT3216_32BIT 0x000FC610UL +#define PHYID_VT3216_64BIT 0x000FC600UL +#define PHYID_MARVELL_1000 0x01410C50UL +#define PHYID_MARVELL_1000S 0x01410C40UL + +#define PHYID_REV_ID_MASK 0x0000000FUL + +#define PHYID_GET_PHY_REV_ID(i) ((i) & PHYID_REV_ID_MASK) +#define PHYID_GET_PHY_ID(i) ((i) & ~PHYID_REV_ID_MASK) + +#define MII_REG_BITS_ON(x,i,p) do {\ + u16 w;\ + velocity_mii_read((p),(i),&(w));\ + (w)|=(x);\ + velocity_mii_write((p),(i),(w));\ +} while (0) + +#define MII_REG_BITS_OFF(x,i,p) do {\ + u16 w;\ + velocity_mii_read((p),(i),&(w));\ + (w)&=(~(x));\ + velocity_mii_write((p),(i),(w));\ +} while (0) + +#define MII_REG_BITS_IS_ON(x,i,p) ({\ + u16 w;\ + velocity_mii_read((p),(i),&(w));\ + ((int) ((w) & (x)));}) + +#define MII_GET_PHY_ID(p) ({\ + u32 id; \ + u16 id2; \ + u16 id1; \ + velocity_mii_read((p),MII_REG_PHYID2, &id2);\ + velocity_mii_read((p),MII_REG_PHYID1, &id1);\ + id = ( ( (u32)id2 ) << 16 ) | id1; \ + (id);}) + +#ifdef LINUX +/* + * Inline debug routine + */ + + +enum velocity_msg_level { + MSG_LEVEL_ERR = 0, //Errors that will cause abnormal operation. + MSG_LEVEL_NOTICE = 1, //Some errors need users to be notified. + MSG_LEVEL_INFO = 2, //Normal message. + MSG_LEVEL_VERBOSE = 3, //Will report all trival errors. + MSG_LEVEL_DEBUG = 4 //Only for debug purpose. +}; + +#ifdef VELOCITY_DEBUG +#define ASSERT(x) { \ + if (!(x)) { \ + printk(KERN_ERR "assertion %s failed: file %s line %d\n", #x,\ + __FUNCTION__, __LINE__);\ + BUG(); \ + }\ +} +#define VELOCITY_DBG(p,args...) printk(p, ##args) +#else +#define ASSERT(x) +#define VELOCITY_DBG(x) +#endif + +#define VELOCITY_PRT(l, p, args...) do {if (l<=msglevel) printf( p ,##args);} while (0) + +#define VELOCITY_PRT_CAMMASK(p,t) {\ + int i;\ + if ((t)==VELOCITY_MULTICAST_CAM) {\ + for (i=0;i<(MCAM_SIZE/8);i++)\ + printk("%02X",(p)->mCAMmask[i]);\ + }\ + else {\ + for (i=0;i<(VCAM_SIZE/8);i++)\ + printk("%02X",(p)->vCAMmask[i]);\ + }\ + printk("\n");\ +} + +#endif + +#define VELOCITY_WOL_MAGIC 0x00000000UL +#define VELOCITY_WOL_PHY 0x00000001UL +#define VELOCITY_WOL_ARP 0x00000002UL +#define VELOCITY_WOL_UCAST 0x00000004UL +#define VELOCITY_WOL_BCAST 0x00000010UL +#define VELOCITY_WOL_MCAST 0x00000020UL +#define VELOCITY_WOL_MAGIC_SEC 0x00000040UL + +/* + * Flags for options + */ + +#define VELOCITY_FLAGS_TAGGING 0x00000001UL +#define VELOCITY_FLAGS_TX_CSUM 0x00000002UL +#define VELOCITY_FLAGS_RX_CSUM 0x00000004UL +#define VELOCITY_FLAGS_IP_ALIGN 0x00000008UL +#define VELOCITY_FLAGS_VAL_PKT_LEN 0x00000010UL + +#define VELOCITY_FLAGS_FLOW_CTRL 0x01000000UL + +/* + * Flags for driver status + */ + +#define VELOCITY_FLAGS_OPENED 0x00010000UL +#define VELOCITY_FLAGS_VMNS_CONNECTED 0x00020000UL +#define VELOCITY_FLAGS_VMNS_COMMITTED 0x00040000UL +#define VELOCITY_FLAGS_WOL_ENABLED 0x00080000UL + +/* + * Flags for MII status + */ + +#define VELOCITY_LINK_FAIL 0x00000001UL +#define VELOCITY_SPEED_10 0x00000002UL +#define VELOCITY_SPEED_100 0x00000004UL +#define VELOCITY_SPEED_1000 0x00000008UL +#define VELOCITY_DUPLEX_FULL 0x00000010UL +#define VELOCITY_AUTONEG_ENABLE 0x00000020UL +#define VELOCITY_FORCED_BY_EEPROM 0x00000040UL + +/* + * For velocity_set_media_duplex + */ + +#define VELOCITY_LINK_CHANGE 0x00000001UL + +enum speed_opt { + SPD_DPX_AUTO = 0, + SPD_DPX_100_HALF = 1, + SPD_DPX_100_FULL = 2, + SPD_DPX_10_HALF = 3, + SPD_DPX_10_FULL = 4 +}; + +enum velocity_init_type { + VELOCITY_INIT_COLD = 0, + VELOCITY_INIT_RESET, + VELOCITY_INIT_WOL +}; + +enum velocity_flow_cntl_type { + FLOW_CNTL_DEFAULT = 1, + FLOW_CNTL_TX, + FLOW_CNTL_RX, + FLOW_CNTL_TX_RX, + FLOW_CNTL_DISABLE, +}; + +struct velocity_opt { + int numrx; /* Number of RX descriptors */ + int numtx; /* Number of TX descriptors */ + enum speed_opt spd_dpx; /* Media link mode */ + int vid; /* vlan id */ + int DMA_length; /* DMA length */ + int rx_thresh; /* RX_THRESH */ + int flow_cntl; + int wol_opts; /* Wake on lan options */ + int td_int_count; + int int_works; + int rx_bandwidth_hi; + int rx_bandwidth_lo; + int rx_bandwidth_en; + u32 flags; +}; + +#define RX_DESC_MIN 4 +#define RX_DESC_MAX 255 +#define RX_DESC_DEF RX_DESC_MIN + +#define TX_DESC_MIN 1 +#define TX_DESC_MAX 256 +#define TX_DESC_DEF TX_DESC_MIN + +static struct velocity_info { +// struct list_head list; + + struct pci_device *pdev; +// struct net_device *dev; +// struct net_device_stats stats; + +#ifdef CONFIG_PM + u32 pci_state[16]; +#endif + +// dma_addr_t rd_pool_dma; +// dma_addr_t td_pool_dma[TX_QUEUE_NO]; + +// dma_addr_t tx_bufs_dma; + u8 *tx_bufs; + + u8 ip_addr[4]; + enum chip_type chip_id; + + struct mac_regs *mac_regs; + unsigned long memaddr; + unsigned long ioaddr; + u32 io_size; + + u8 rev_id; + +#define AVAIL_TD(p,q) ((p)->options.numtx-((p)->td_used[(q)])) + + int num_txq; + + volatile int td_used[TX_QUEUE_NO]; + int td_curr; + int td_tail[TX_QUEUE_NO]; + unsigned char *TxDescArrays; /* Index of Tx Descriptor buffer */ + unsigned char *RxDescArrays; /* Index of Rx Descriptor buffer */ + unsigned char *tx_buffs; + unsigned char *rx_buffs; + + unsigned char *txb; + unsigned char *rxb; + struct tx_desc *td_rings; + struct velocity_td_info *td_infos[TX_QUEUE_NO]; + + int rd_curr; + int rd_dirty; + u32 rd_filled; + struct rx_desc *rd_ring; + struct velocity_rd_info *rd_info; /* It's an array */ + +#define GET_RD_BY_IDX(vptr, idx) (vptr->rd_ring[idx]) + u32 mib_counter[MAX_HW_MIB_COUNTER]; + struct velocity_opt options; + + u32 int_mask; + + u32 flags; + + int rx_buf_sz; + u32 mii_status; + u32 phy_id; + int multicast_limit; + + u8 vCAMmask[(VCAM_SIZE / 8)]; + u8 mCAMmask[(MCAM_SIZE / 8)]; + +// spinlock_t lock; + + int wol_opts; + u8 wol_passwd[6]; + + struct velocity_context context; + + u32 ticks; + u32 rx_bytes; + +} vptx; + +static struct velocity_info *vptr; + +#ifdef LINUX +/** + * velocity_get_ip - find an IP address for the device + * @vptr: Velocity to query + * + * Dig out an IP address for this interface so that we can + * configure wakeup with WOL for ARP. If there are multiple IP + * addresses on this chain then we use the first - multi-IP WOL is not + * supported. + * + * CHECK ME: locking + */ + +inline static int velocity_get_ip(struct velocity_info *vptr) +{ + struct in_device *in_dev = (struct in_device *) vptr->dev->ip_ptr; + struct in_ifaddr *ifa; + + if (in_dev != NULL) { + ifa = (struct in_ifaddr *) in_dev->ifa_list; + if (ifa != NULL) { + memcpy(vptr->ip_addr, &ifa->ifa_address, 4); + return 0; + } + } + return -ENOENT; +} + +/** + * velocity_update_hw_mibs - fetch MIB counters from chip + * @vptr: velocity to update + * + * The velocity hardware keeps certain counters in the hardware + * side. We need to read these when the user asks for statistics + * or when they overflow (causing an interrupt). The read of the + * statistic clears it, so we keep running master counters in user + * space. + */ + +static inline void velocity_update_hw_mibs(struct velocity_info *vptr) +{ + u32 tmp; + int i; + BYTE_REG_BITS_ON(MIBCR_MIBFLSH, &(vptr->mac_regs->MIBCR)); + + while (BYTE_REG_BITS_IS_ON + (MIBCR_MIBFLSH, &(vptr->mac_regs->MIBCR))); + + BYTE_REG_BITS_ON(MIBCR_MPTRINI, &(vptr->mac_regs->MIBCR)); + for (i = 0; i < HW_MIB_SIZE; i++) { + tmp = readl(&(vptr->mac_regs->MIBData)) & 0x00FFFFFFUL; + vptr->mib_counter[i] += tmp; + } +} +#endif +/** + * init_flow_control_register - set up flow control + * @vptr: velocity to configure + * + * Configure the flow control registers for this velocity device. + */ + +static inline void init_flow_control_register(struct velocity_info *vptr) +{ + struct mac_regs *regs = vptr->mac_regs; + + /* Set {XHITH1, XHITH0, XLTH1, XLTH0} in FlowCR1 to {1, 0, 1, 1} + depend on RD=64, and Turn on XNOEN in FlowCR1 */ + writel((CR0_XONEN | CR0_XHITH1 | CR0_XLTH1 | CR0_XLTH0), + ®s->CR0Set); + writel((CR0_FDXTFCEN | CR0_FDXRFCEN | CR0_HDXFCEN | CR0_XHITH0), + ®s->CR0Clr); + + /* Set TxPauseTimer to 0xFFFF */ + writew(0xFFFF, ®s->tx_pause_timer); + + /* Initialize RBRDU to Rx buffer count. */ + writew(vptr->options.numrx, ®s->RBRDU); +} + + +#endif diff --git a/gpxe/src/drivers/net/virtio-net.c b/gpxe/src/drivers/net/virtio-net.c new file mode 100644 index 0000000..f0afd3f --- /dev/null +++ b/gpxe/src/drivers/net/virtio-net.c @@ -0,0 +1,307 @@ +/* virtio-net.c - etherboot driver for virtio network interface + * + * (c) Copyright 2008 Bull S.A.S. + * + * Author: Laurent Vivier + * + * some parts from Linux Virtio PCI driver + * + * Copyright IBM Corp. 2007 + * Authors: Anthony Liguori + * + * some parts from Linux Virtio Ring + * + * Copyright Rusty Russell IBM Corporation 2007 + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * + */ + +#include "etherboot.h" +#include "nic.h" +#include "gpxe/virtio-ring.h" +#include "gpxe/virtio-pci.h" +#include "virtio-net.h" + +#define BUG() do { \ + printf("BUG: failure at %s:%d/%s()!\n", \ + __FILE__, __LINE__, __FUNCTION__); \ + while(1); \ +} while (0) +#define BUG_ON(condition) do { if (condition) BUG(); } while (0) + +/* Ethernet header */ + +struct eth_hdr { + unsigned char dst_addr[ETH_ALEN]; + unsigned char src_addr[ETH_ALEN]; + unsigned short type; +}; + +struct eth_frame { + struct eth_hdr hdr; + unsigned char data[ETH_FRAME_LEN]; +}; + +/* TX: virtio header and eth buffer */ + +static struct virtio_net_hdr tx_virtio_hdr; +static struct eth_frame tx_eth_frame; + +/* RX: virtio headers and buffers */ + +#define RX_BUF_NB 6 +static struct virtio_net_hdr rx_hdr[RX_BUF_NB]; +static unsigned char rx_buffer[RX_BUF_NB][ETH_FRAME_LEN]; + +/* virtio queues and vrings */ + +enum { + RX_INDEX = 0, + TX_INDEX, + QUEUE_NB +}; + +static struct vring_virtqueue virtqueue[QUEUE_NB]; + +/* + * virtnet_disable + * + * Turn off ethernet interface + * + */ + +static void virtnet_disable(struct nic *nic) +{ + int i; + + for (i = 0; i < QUEUE_NB; i++) { + vring_disable_cb(&virtqueue[i]); + vp_del_vq(nic->ioaddr, i); + } + vp_reset(nic->ioaddr); +} + +/* + * virtnet_poll + * + * Wait for a frame + * + * return true if there is a packet ready to read + * + * nic->packet should contain data on return + * nic->packetlen should contain length of data + * + */ +static int virtnet_poll(struct nic *nic, int retrieve) +{ + unsigned int len; + u16 token; + struct virtio_net_hdr *hdr; + struct vring_list list[2]; + + if (!vring_more_used(&virtqueue[RX_INDEX])) + return 0; + + if (!retrieve) + return 1; + + token = vring_get_buf(&virtqueue[RX_INDEX], &len); + + BUG_ON(len > sizeof(struct virtio_net_hdr) + ETH_FRAME_LEN); + + hdr = &rx_hdr[token]; /* FIXME: check flags */ + len -= sizeof(struct virtio_net_hdr); + + nic->packetlen = len; + memcpy(nic->packet, (char *)rx_buffer[token], nic->packetlen); + + /* add buffer to desc */ + + list[0].addr = (char*)&rx_hdr[token]; + list[0].length = sizeof(struct virtio_net_hdr); + list[1].addr = (char*)&rx_buffer[token]; + list[1].length = ETH_FRAME_LEN; + + vring_add_buf(&virtqueue[RX_INDEX], list, 0, 2, token, 0); + vring_kick(nic->ioaddr, &virtqueue[RX_INDEX], 1); + + return 1; +} + +/* + * + * virtnet_transmit + * + * Transmit a frame + * + */ + +static void virtnet_transmit(struct nic *nic, const char *destaddr, + unsigned int type, unsigned int len, const char *data) +{ + struct vring_list list[2]; + + /* + * from http://www.etherboot.org/wiki/dev/devmanual : + * "You do not need more than one transmit buffer." + */ + + /* FIXME: initialize header according to vp_get_features() */ + + tx_virtio_hdr.flags = 0; + tx_virtio_hdr.csum_offset = 0; + tx_virtio_hdr.csum_start = 0; + tx_virtio_hdr.gso_type = VIRTIO_NET_HDR_GSO_NONE; + tx_virtio_hdr.gso_size = 0; + tx_virtio_hdr.hdr_len = 0; + + /* add ethernet frame into vring */ + + BUG_ON(len > sizeof(tx_eth_frame.data)); + + memcpy(tx_eth_frame.hdr.dst_addr, destaddr, ETH_ALEN); + memcpy(tx_eth_frame.hdr.src_addr, nic->node_addr, ETH_ALEN); + tx_eth_frame.hdr.type = htons(type); + memcpy(tx_eth_frame.data, data, len); + + list[0].addr = (char*)&tx_virtio_hdr; + list[0].length = sizeof(struct virtio_net_hdr); + list[1].addr = (char*)&tx_eth_frame; + list[1].length = ETH_FRAME_LEN; + + vring_add_buf(&virtqueue[TX_INDEX], list, 2, 0, 0, 0); + + vring_kick(nic->ioaddr, &virtqueue[TX_INDEX], 1); + + /* + * http://www.etherboot.org/wiki/dev/devmanual + * + * "You should ensure the packet is fully transmitted + * before returning from this routine" + */ + + while (!vring_more_used(&virtqueue[TX_INDEX])) { + mb(); + udelay(10); + } + + /* free desc */ + + (void)vring_get_buf(&virtqueue[TX_INDEX], NULL); +} + +static void virtnet_irq(struct nic *nic __unused, irq_action_t action) +{ + switch ( action ) { + case DISABLE : + vring_disable_cb(&virtqueue[RX_INDEX]); + vring_disable_cb(&virtqueue[TX_INDEX]); + break; + case ENABLE : + vring_enable_cb(&virtqueue[RX_INDEX]); + vring_enable_cb(&virtqueue[TX_INDEX]); + break; + case FORCE : + break; + } +} + +static void provide_buffers(struct nic *nic) +{ + int i; + struct vring_list list[2]; + + for (i = 0; i < RX_BUF_NB; i++) { + list[0].addr = (char*)&rx_hdr[i]; + list[0].length = sizeof(struct virtio_net_hdr); + list[1].addr = (char*)&rx_buffer[i]; + list[1].length = ETH_FRAME_LEN; + vring_add_buf(&virtqueue[RX_INDEX], list, 0, 2, i, i); + } + + /* nofify */ + + vring_kick(nic->ioaddr, &virtqueue[RX_INDEX], i); +} + +static struct nic_operations virtnet_operations = { + .connect = dummy_connect, + .poll = virtnet_poll, + .transmit = virtnet_transmit, + .irq = virtnet_irq, +}; + +/* + * virtnet_probe + * + * Look for a virtio network adapter + * + */ + +static int virtnet_probe(struct nic *nic, struct pci_device *pci) +{ + u32 features; + int i; + + /* Mask the bit that says "this is an io addr" */ + + nic->ioaddr = pci->ioaddr & ~3; + + /* Copy IRQ from PCI information */ + + nic->irqno = pci->irq; + + printf("I/O address 0x%08x, IRQ #%d\n", nic->ioaddr, nic->irqno); + + adjust_pci_device(pci); + + vp_reset(nic->ioaddr); + + features = vp_get_features(nic->ioaddr); + if (features & (1 << VIRTIO_NET_F_MAC)) { + vp_get(nic->ioaddr, offsetof(struct virtio_net_config, mac), + nic->node_addr, ETH_ALEN); + printf("MAC address "); + for (i = 0; i < ETH_ALEN; i++) { + printf("%02x%c", nic->node_addr[i], + (i == ETH_ALEN - 1) ? '\n' : ':'); + } + } + + /* initialize emit/receive queue */ + + for (i = 0; i < QUEUE_NB; i++) { + virtqueue[i].free_head = 0; + virtqueue[i].last_used_idx = 0; + memset((char*)&virtqueue[i].queue, 0, sizeof(virtqueue[i].queue)); + if (vp_find_vq(nic->ioaddr, i, &virtqueue[i]) == -1) + printf("Cannot register queue #%d\n", i); + } + + /* provide some receive buffers */ + + provide_buffers(nic); + + /* define NIC interface */ + + nic->nic_op = &virtnet_operations; + + /* driver is ready */ + + vp_set_features(nic->ioaddr, features & (1 << VIRTIO_NET_F_MAC)); + vp_set_status(nic->ioaddr, VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK); + + return 1; +} + +static struct pci_device_id virtnet_nics[] = { +PCI_ROM(0x1af4, 0x1000, "virtio-net", "Virtio Network Interface"), +}; + +PCI_DRIVER ( virtnet_driver, virtnet_nics, PCI_NO_CLASS ); + +DRIVER ( "VIRTIO-NET", nic_driver, pci_driver, virtnet_driver, + virtnet_probe, virtnet_disable ); diff --git a/gpxe/src/drivers/net/virtio-net.h b/gpxe/src/drivers/net/virtio-net.h new file mode 100644 index 0000000..3abef28 --- /dev/null +++ b/gpxe/src/drivers/net/virtio-net.h @@ -0,0 +1,44 @@ +#ifndef _VIRTIO_NET_H_ +# define _VIRTIO_NET_H_ + +/* The feature bitmap for virtio net */ +#define VIRTIO_NET_F_CSUM 0 /* Host handles pkts w/ partial csum */ +#define VIRTIO_NET_F_GUEST_CSUM 1 /* Guest handles pkts w/ partial csum */ +#define VIRTIO_NET_F_MAC 5 /* Host has given MAC address. */ +#define VIRTIO_NET_F_GSO 6 /* Host handles pkts w/ any GSO type */ +#define VIRTIO_NET_F_GUEST_TSO4 7 /* Guest can handle TSOv4 in. */ +#define VIRTIO_NET_F_GUEST_TSO6 8 /* Guest can handle TSOv6 in. */ +#define VIRTIO_NET_F_GUEST_ECN 9 /* Guest can handle TSO[6] w/ ECN in. */ +#define VIRTIO_NET_F_GUEST_UFO 10 /* Guest can handle UFO in. */ +#define VIRTIO_NET_F_HOST_TSO4 11 /* Host can handle TSOv4 in. */ +#define VIRTIO_NET_F_HOST_TSO6 12 /* Host can handle TSOv6 in. */ +#define VIRTIO_NET_F_HOST_ECN 13 /* Host can handle TSO[6] w/ ECN in. */ +#define VIRTIO_NET_F_HOST_UFO 14 /* Host can handle UFO in. */ + +struct virtio_net_config +{ + /* The config defining mac address (if VIRTIO_NET_F_MAC) */ + u8 mac[6]; +} __attribute__((packed)); + +/* This is the first element of the scatter-gather list. If you don't + * specify GSO or CSUM features, you can simply ignore the header. */ + +struct virtio_net_hdr +{ +#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 // Use csum_start, csum_offset + uint8_t flags; +#define VIRTIO_NET_HDR_GSO_NONE 0 // Not a GSO frame +#define VIRTIO_NET_HDR_GSO_TCPV4 1 // GSO frame, IPv4 TCP (TSO) +/* FIXME: Do we need this? If they said they can handle ECN, do they care? */ +#define VIRTIO_NET_HDR_GSO_TCPV4_ECN 2 // GSO frame, IPv4 TCP w/ ECN +#define VIRTIO_NET_HDR_GSO_UDP 3 // GSO frame, IPv4 UDP (UFO) +#define VIRTIO_NET_HDR_GSO_TCPV6 4 // GSO frame, IPv6 TCP +#define VIRTIO_NET_HDR_GSO_ECN 0x80 // TCP has ECN set + uint8_t gso_type; + uint16_t hdr_len; + uint16_t gso_size; + uint16_t csum_start; + uint16_t csum_offset; +}; +#endif /* _VIRTIO_NET_H_ */ diff --git a/gpxe/src/drivers/net/w89c840.c b/gpxe/src/drivers/net/w89c840.c new file mode 100644 index 0000000..5abc0b3 --- /dev/null +++ b/gpxe/src/drivers/net/w89c840.c @@ -0,0 +1,962 @@ +/* + * Etherboot - BOOTP/TFTP Bootstrap Program + * + * w89c840.c -- This file implements the winbond-840 driver for etherboot. + * + */ + +/* + * Adapted by Igor V. Kovalenko + * -- + * 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 +#include + +static const char *w89c840_version = "driver Version 0.94 - December 12, 2003"; + +/* Linux support functions */ +#define virt_to_le32desc(addr) virt_to_bus(addr) +#define le32desc_to_virt(addr) bus_to_virt(addr) + +/* +#define cpu_to_le32(val) (val) +#define le32_to_cpu(val) (val) +*/ + +/* Operational parameters that are set at compile time. */ + +/* Keep the ring sizes a power of two for compile efficiency. + The compiler will convert '%'<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*1000) + +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ + +/* + * Used to be this much CPU loops on Celeron@400 (?), + * now using real timer and TX_TIMEOUT! + * #define TX_LOOP_COUNT 10000000 + */ + +#if !defined(__OPTIMIZE__) +#warning You must compile this file with the correct options! +#warning See the last lines of the source file. +#error You must compile this driver with "-O". +#endif + +enum chip_capability_flags {CanHaveMII=1, HasBrokenTx=2}; + +#ifdef USE_IO_OPS +#define W840_FLAGS (PCI_USES_IO | PCI_ADDR0 | PCI_USES_MASTER) +#else +#define W840_FLAGS (PCI_USES_MEM | PCI_ADDR1 | PCI_USES_MASTER) +#endif + +static u32 driver_flags = CanHaveMII | HasBrokenTx; + +/* This driver was written to use PCI memory space, however some x86 systems + work only with I/O space accesses. Pass -DUSE_IO_OPS to use PCI I/O space + accesses instead of memory space. */ + +#ifdef USE_IO_OPS +#undef readb +#undef readw +#undef readl +#undef writeb +#undef writew +#undef writel +#define readb inb +#define readw inw +#define readl inl +#define writeb outb +#define writew outw +#define writel outl +#endif + +/* Offsets to the Command and Status Registers, "CSRs". + While similar to the Tulip, these registers are longword aligned. + Note: It's not useful to define symbolic names for every register bit in + the device. The name can only partially document the semantics and make + the driver longer and more difficult to read. +*/ +enum w840_offsets { + PCIBusCfg=0x00, TxStartDemand=0x04, RxStartDemand=0x08, + RxRingPtr=0x0C, TxRingPtr=0x10, + IntrStatus=0x14, NetworkConfig=0x18, IntrEnable=0x1C, + RxMissed=0x20, EECtrl=0x24, MIICtrl=0x24, BootRom=0x28, GPTimer=0x2C, + CurRxDescAddr=0x30, CurRxBufAddr=0x34, /* Debug use */ + MulticastFilter0=0x38, MulticastFilter1=0x3C, StationAddr=0x40, + CurTxDescAddr=0x4C, CurTxBufAddr=0x50, +}; + +/* Bits in the interrupt status/enable registers. */ +/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */ +enum intr_status_bits { + NormalIntr=0x10000, AbnormalIntr=0x8000, + IntrPCIErr=0x2000, TimerInt=0x800, + IntrRxDied=0x100, RxNoBuf=0x80, IntrRxDone=0x40, + TxFIFOUnderflow=0x20, RxErrIntr=0x10, + TxIdle=0x04, IntrTxStopped=0x02, IntrTxDone=0x01, +}; + +/* Bits in the NetworkConfig register. */ +enum rx_mode_bits { + AcceptErr=0x80, AcceptRunt=0x40, + AcceptBroadcast=0x20, AcceptMulticast=0x10, + AcceptAllPhys=0x08, AcceptMyPhys=0x02, +}; + +enum mii_reg_bits { + MDIO_ShiftClk=0x10000, MDIO_DataIn=0x80000, MDIO_DataOut=0x20000, + MDIO_EnbOutput=0x40000, MDIO_EnbIn = 0x00000, +}; + +/* The Tulip Rx and Tx buffer descriptors. */ +struct w840_rx_desc { + s32 status; + s32 length; + u32 buffer1; + u32 next_desc; +}; + +struct w840_tx_desc { + s32 status; + s32 length; + u32 buffer1, buffer2; /* We use only buffer 1. */ +}; + +/* Bits in network_desc.status */ +enum desc_status_bits { + DescOwn=0x80000000, DescEndRing=0x02000000, DescUseLink=0x01000000, + DescWholePkt=0x60000000, DescStartPkt=0x20000000, DescEndPkt=0x40000000, + DescIntr=0x80000000, +}; +#define PRIV_ALIGN 15 /* Required alignment mask */ +#define PRIV_ALIGN_BYTES 32 + +static struct winbond_private +{ + /* Descriptor rings first for alignment. */ + struct w840_rx_desc rx_ring[RX_RING_SIZE]; + struct w840_tx_desc tx_ring[TX_RING_SIZE]; + struct net_device *next_module; /* Link for devices of this type. */ + void *priv_addr; /* Unaligned address for kfree */ + const char *product_name; + /* Frequently used values: keep some adjacent for cache effect. */ + int chip_id, drv_flags; + struct pci_dev *pci_dev; + int csr6; + struct w840_rx_desc *rx_head_desc; + unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */ + unsigned int rx_buf_sz; /* Based on MTU+slack. */ + unsigned int cur_tx, dirty_tx; + int tx_q_bytes; + unsigned int tx_full:1; /* The Tx queue is full. */ + /* These values are keep track of the transceiver/media in use. */ + unsigned int full_duplex:1; /* Full-duplex operation requested. */ + unsigned int duplex_lock:1; + unsigned int medialock:1; /* Do not sense media. */ + unsigned int default_port:4; /* Last dev->if_port value. */ + /* MII transceiver section. */ + int mii_cnt; /* MII device addresses. */ + u16 advertising; /* NWay media advertisement */ + unsigned char phys[2]; /* MII device addresses. */ +} w840private __attribute__ ((aligned (PRIV_ALIGN_BYTES))); + +/* NIC specific static variables go here */ + +static int ioaddr; +static unsigned short eeprom [0x40]; +struct { + char rx_packet[PKT_BUF_SZ * RX_RING_SIZE]; + char tx_packet[PKT_BUF_SZ * TX_RING_SIZE]; +} w89c840_buf __shared; + +static int eeprom_read(long ioaddr, int location); +static int mdio_read(int base_address, int phy_id, int location); +#if 0 +static void mdio_write(int base_address, int phy_id, int location, int value); +#endif + +static void check_duplex(void); +static void set_rx_mode(void); +static void init_ring(void); + +#if defined(W89C840_DEBUG) +static void decode_interrupt(u32 intr_status) +{ + printf("Interrupt status: "); + +#define TRACE_INTR(_intr_) \ + if (intr_status & (_intr_)) { printf (" " #_intr_); } + + TRACE_INTR(NormalIntr); + TRACE_INTR(AbnormalIntr); + TRACE_INTR(IntrPCIErr); + TRACE_INTR(TimerInt); + TRACE_INTR(IntrRxDied); + TRACE_INTR(RxNoBuf); + TRACE_INTR(IntrRxDone); + TRACE_INTR(TxFIFOUnderflow); + TRACE_INTR(RxErrIntr); + TRACE_INTR(TxIdle); + TRACE_INTR(IntrTxStopped); + TRACE_INTR(IntrTxDone); + + printf("\n"); + /*sleep(1);*/ +} +#endif + +/************************************************************************** +w89c840_reset - Reset adapter +***************************************************************************/ +static void w89c840_reset(struct nic *nic) +{ + int i; + + /* Reset the chip to erase previous misconfiguration. + No hold time required! */ + writel(0x00000001, ioaddr + PCIBusCfg); + + init_ring(); + + writel(virt_to_bus(w840private.rx_ring), ioaddr + RxRingPtr); + writel(virt_to_bus(w840private.tx_ring), ioaddr + TxRingPtr); + + for (i = 0; i < ETH_ALEN; i++) + writeb(nic->node_addr[i], ioaddr + StationAddr + i); + + /* Initialize other registers. */ + /* Configure the PCI bus bursts and FIFO thresholds. + 486: Set 8 longword cache alignment, 8 longword burst. + 586: Set 16 longword cache alignment, no burst limit. + Cache alignment bits 15:14 Burst length 13:8 + 0000 0000 align to cache 0800 8 longwords + 4000 8 longwords 0100 1 longword 1000 16 longwords + 8000 16 longwords 0200 2 longwords 2000 32 longwords + C000 32 longwords 0400 4 longwords + Wait the specified 50 PCI cycles after a reset by initializing + Tx and Rx queues and the address filter list. */ + + writel(0xE010, ioaddr + PCIBusCfg); + + writel(0, ioaddr + RxStartDemand); + w840private.csr6 = 0x20022002; + check_duplex(); + set_rx_mode(); + + /* Do not enable the interrupts Etherboot doesn't need them */ +/* + writel(0x1A0F5, ioaddr + IntrStatus); + writel(0x1A0F5, ioaddr + IntrEnable); +*/ +#if defined(W89C840_DEBUG) + printf("winbond-840 : Done reset.\n"); +#endif +} + +#if 0 +static void handle_intr(u32 intr_stat) +{ + if ((intr_stat & (NormalIntr|AbnormalIntr)) == 0) { + /* we are polling, do not return now */ + /*return 0;*/ + } else { + /* Acknowledge all of the current interrupt sources ASAP. */ + writel(intr_stat & 0x001ffff, ioaddr + IntrStatus); + } + + if (intr_stat & AbnormalIntr) { + /* There was an abnormal interrupt */ + printf("\n-=- Abnormal interrupt.\n"); + +#if defined(W89C840_DEBUG) + decode_interrupt(intr_stat); +#endif + + if (intr_stat & RxNoBuf) { + /* There was an interrupt */ + printf("-=- <=> No receive buffers available.\n"); + writel(0, ioaddr + RxStartDemand); + } + } +} +#endif + +/************************************************************************** +w89c840_poll - Wait for a frame +***************************************************************************/ +static int w89c840_poll(struct nic *nic, int retrieve) +{ + /* return true if there's an ethernet packet ready to read */ + /* nic->packet should contain data on return */ + /* nic->packetlen should contain length of data */ + int packet_received = 0; + +#if defined(W89C840_DEBUG) + u32 intr_status = readl(ioaddr + IntrStatus); +#endif + + do { + /* Code from netdev_rx(dev) */ + + int entry = w840private.cur_rx % RX_RING_SIZE; + + struct w840_rx_desc *desc = w840private.rx_head_desc; + s32 status = desc->status; + + if (status & DescOwn) { + /* DescOwn bit is still set, we should wait for RX to complete */ + packet_received = 0; + break; + } + + if ( !retrieve ) { + packet_received = 1; + break; + } + + if ((status & 0x38008300) != 0x0300) { + if ((status & 0x38000300) != 0x0300) { + /* Ingore earlier buffers. */ + if ((status & 0xffff) != 0x7fff) { + printf("winbond-840 : Oversized Ethernet frame spanned " + "multiple buffers, entry %d status %X !\n", + w840private.cur_rx, (unsigned int) status); + } + } else if (status & 0x8000) { + /* There was a fatal error. */ +#if defined(W89C840_DEBUG) + printf("winbond-840 : Receive error, Rx status %X :", status); + if (status & 0x0890) { + printf(" RXLEN_ERROR"); + } + if (status & 0x004C) { + printf(", FRAME_ERROR"); + } + if (status & 0x0002) { + printf(", CRC_ERROR"); + } + printf("\n"); +#endif + + /* Simpy do a reset now... */ + w89c840_reset(nic); + + packet_received = 0; + break; + } + } else { + /* Omit the four octet CRC from the length. */ + int pkt_len = ((status >> 16) & 0x7ff) - 4; + +#if defined(W89C840_DEBUG) + printf(" netdev_rx() normal Rx pkt ring %d length %d status %X\n", entry, pkt_len, status); +#endif + + nic->packetlen = pkt_len; + + /* Check if the packet is long enough to accept without copying + to a minimally-sized skbuff. */ + + memcpy(nic->packet, le32desc_to_virt(w840private.rx_ring[entry].buffer1), pkt_len); + packet_received = 1; + + /* Release buffer to NIC */ + w840private.rx_ring[entry].status = DescOwn; + +#if defined(W89C840_DEBUG) + /* You will want this info for the initial debug. */ + printf(" Rx data %hhX:%hhX:%hhX:%hhX:%hhX:" + "%hhX %hhX:%hhX:%hhX:%hhX:%hhX:%hhX %hhX%hhX " + "%hhX.%hhX.%hhX.%hhX.\n", + nic->packet[0], nic->packet[1], nic->packet[2], nic->packet[3], + nic->packet[4], nic->packet[5], nic->packet[6], nic->packet[7], + nic->packet[8], nic->packet[9], nic->packet[10], + nic->packet[11], nic->packet[12], nic->packet[13], + nic->packet[14], nic->packet[15], nic->packet[16], + nic->packet[17]); +#endif + + } + + entry = (++w840private.cur_rx) % RX_RING_SIZE; + w840private.rx_head_desc = &w840private.rx_ring[entry]; + } while (0); + + return packet_received; +} + +/************************************************************************** +w89c840_transmit - Transmit a frame +***************************************************************************/ + +static void w89c840_transmit( + struct nic *nic, + const char *d, /* Destination */ + unsigned int t, /* Type */ + unsigned int s, /* size */ + const char *p) /* Packet */ +{ + /* send the packet to destination */ + unsigned entry; + int transmit_status; + unsigned long ct; + + /* Caution: the write order is important here, set the field + with the "ownership" bits last. */ + + /* Fill in our transmit buffer */ + entry = w840private.cur_tx % TX_RING_SIZE; + + memcpy (w89c840_buf.tx_packet, d, ETH_ALEN); /* dst */ + memcpy (w89c840_buf.tx_packet + ETH_ALEN, nic->node_addr, ETH_ALEN);/*src*/ + + *((char *) w89c840_buf.tx_packet + 12) = t >> 8; /* type */ + *((char *) w89c840_buf.tx_packet + 13) = t; + + memcpy (w89c840_buf.tx_packet + ETH_HLEN, p, s); + s += ETH_HLEN; + + while (s < ETH_ZLEN) + *((char *) w89c840_buf.tx_packet + ETH_HLEN + (s++)) = 0; + + w840private.tx_ring[entry].buffer1 + = virt_to_le32desc(w89c840_buf.tx_packet); + + w840private.tx_ring[entry].length = (DescWholePkt | (u32) s); + if (entry >= TX_RING_SIZE-1) /* Wrap ring */ + w840private.tx_ring[entry].length |= (DescIntr | DescEndRing); + w840private.tx_ring[entry].status = (DescOwn); + w840private.cur_tx++; + + w840private.tx_q_bytes = (u16) s; + writel(0, ioaddr + TxStartDemand); + + /* Work around horrible bug in the chip by marking the queue as full + when we do not have FIFO room for a maximum sized packet. */ + + if ((w840private.drv_flags & HasBrokenTx) && w840private.tx_q_bytes > TX_BUG_FIFO_LIMIT) { + /* Actually this is left to help finding error tails later in debugging... + * See Linux kernel driver in winbond-840.c for details. + */ + w840private.tx_full = 1; + } + +#if defined(W89C840_DEBUG) + printf("winbond-840 : Transmit frame # %d size %d queued in slot %d.\n", w840private.cur_tx, s, entry); +#endif + + /* Now wait for TX to complete. */ + transmit_status = w840private.tx_ring[entry].status; + + ct = currticks(); + { +#if defined W89C840_DEBUG + u32 intr_stat = 0; +#endif + while (1) { + +#if defined(W89C840_DEBUG) + decode_interrupt(intr_stat); +#endif + + while ( (transmit_status & DescOwn) && ct + TX_TIMEOUT < currticks()) { + + transmit_status = w840private.tx_ring[entry].status; + } + + break; + } + } + + if ((transmit_status & DescOwn) == 0) { + +#if defined(W89C840_DEBUG) + printf("winbond-840 : transmission complete after wait loop iterations, status %X\n", + w840private.tx_ring[entry].status); +#endif + + return; + } + + /* Transmit timed out... */ + + printf("winbond-840 : transmission TIMEOUT : status %X\n", + (unsigned int) w840private.tx_ring[entry].status); + + return; +} + +/************************************************************************** +w89c840_disable - Turn off ethernet interface +***************************************************************************/ +static void w89c840_disable ( struct nic *nic ) { + + w89c840_reset(nic); + + /* Don't know what to do to disable the board. Is this needed at all? */ + /* Yes, a live NIC can corrupt the loaded memory later [Ken] */ + /* Stop the chip's Tx and Rx processes. */ + writel(w840private.csr6 &= ~0x20FA, ioaddr + NetworkConfig); +} + +/************************************************************************** +w89c840_irq - Enable, Disable, or Force interrupts +***************************************************************************/ +static void w89c840_irq(struct nic *nic __unused, irq_action_t action __unused) +{ + switch ( action ) { + case DISABLE : + break; + case ENABLE : + break; + case FORCE : + break; + } +} + +static struct nic_operations w89c840_operations = { + .connect = dummy_connect, + .poll = w89c840_poll, + .transmit = w89c840_transmit, + .irq = w89c840_irq, + +}; + +static struct pci_device_id w89c840_nics[] = { +PCI_ROM(0x1050, 0x0840, "winbond840", "Winbond W89C840F"), +PCI_ROM(0x11f6, 0x2011, "compexrl100atx", "Compex RL100ATX"), +}; + +PCI_DRIVER ( w89c840_driver, w89c840_nics, PCI_NO_CLASS ); + +/************************************************************************** +w89c840_probe - Look for an adapter, this routine's visible to the outside +***************************************************************************/ +static int w89c840_probe ( struct nic *nic, struct pci_device *p ) { + + + u16 sum = 0; + int i, j; + unsigned short value; + + if (p->ioaddr == 0) + return 0; + + nic->ioaddr = p->ioaddr; + nic->irqno = 0; + +#if defined(W89C840_DEBUG) + printf("winbond-840: PCI bus %hhX device function %hhX: I/O address: %hX\n", p->bus, p->devfn, ioaddr); +#endif + + ioaddr = ioaddr & ~3; /* Mask the bit that says "this is an io addr" */ + +#define PCI_DEVICE_ID_WINBOND2_89C840 0x0840 +#define PCI_DEVICE_ID_COMPEX_RL100ATX 0x2011 + + /* From Matt Hortman */ + if (p->vendor == PCI_VENDOR_ID_WINBOND2 + && p->device == PCI_DEVICE_ID_WINBOND2_89C840) { + + /* detected "Winbond W89c840 Fast Ethernet PCI NIC" */ + + } else if ( p->vendor == PCI_VENDOR_ID_COMPEX + && p->device == PCI_DEVICE_ID_COMPEX_RL100ATX) { + + /* detected "Compex RL100ATX Fast Ethernet PCI NIC" */ + + } else { + /* Gee, guess what? They missed again. */ + printf("device ID : %X - is not a Compex RL100ATX NIC.\n", + p->device); + return 0; + } + + printf(" %s\n", w89c840_version); + + adjust_pci_device(p); + + /* Ok. Got one. Read the eeprom. */ + for (j = 0, i = 0; i < 0x40; i++) { + value = eeprom_read(ioaddr, i); + eeprom[i] = value; + sum += value; + } + + for (i=0;inode_addr[i] = (eeprom[i/2] >> (8*(i&1))) & 0xff; + } + + DBG ( "Ethernet addr: %s\n", eth_ntoa ( nic->node_addr ) ); + +#if defined(W89C840_DEBUG) + printf("winbond-840: EEPROM checksum %hX, got eeprom", sum); +#endif + + /* Reset the chip to erase previous misconfiguration. + No hold time required! */ + writel(0x00000001, ioaddr + PCIBusCfg); + + if (driver_flags & CanHaveMII) { + int phy, phy_idx = 0; + for (phy = 1; phy < 32 && phy_idx < 4; phy++) { + int mii_status = mdio_read(ioaddr, phy, 1); + if (mii_status != 0xffff && mii_status != 0x0000) { + w840private.phys[phy_idx++] = phy; + w840private.advertising = mdio_read(ioaddr, phy, 4); + +#if defined(W89C840_DEBUG) + printf("winbond-840 : MII PHY found at address %d, status " + "%X advertising %hX.\n", phy, mii_status, w840private.advertising); +#endif + + } + } + + w840private.mii_cnt = phy_idx; + + if (phy_idx == 0) { + printf("winbond-840 : MII PHY not found -- this device may not operate correctly.\n"); + } + } + + /* point to NIC specific routines */ + nic->nic_op = &w89c840_operations; + + w89c840_reset(nic); + + return 1; +} + +/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. These are + often serial bit streams generated by the host processor. + The example below is for the common 93c46 EEPROM, 64 16 bit words. */ + +/* Delay between EEPROM clock transitions. + No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need + a delay. Note that pre-2.0.34 kernels had a cache-alignment bug that + made udelay() unreliable. + The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is + depricated. +*/ +#define eeprom_delay(ee_addr) readl(ee_addr) + +enum EEPROM_Ctrl_Bits { + EE_ShiftClk=0x02, EE_Write0=0x801, EE_Write1=0x805, + EE_ChipSelect=0x801, EE_DataIn=0x08, +}; + +/* The EEPROM commands include the alway-set leading bit. */ +enum EEPROM_Cmds { + EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6), +}; + +static int eeprom_read(long addr, int location) +{ + int i; + int retval = 0; + int ee_addr = addr + EECtrl; + int read_cmd = location | EE_ReadCmd; + writel(EE_ChipSelect, ee_addr); + + /* Shift the read command bits out. */ + for (i = 10; i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0; + writel(dataval, ee_addr); + eeprom_delay(ee_addr); + writel(dataval | EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + } + writel(EE_ChipSelect, ee_addr); + + for (i = 16; i > 0; i--) { + writel(EE_ChipSelect | EE_ShiftClk, ee_addr); + eeprom_delay(ee_addr); + retval = (retval << 1) | ((readl(ee_addr) & EE_DataIn) ? 1 : 0); + writel(EE_ChipSelect, ee_addr); + eeprom_delay(ee_addr); + } + + /* Terminate the EEPROM access. */ + writel(0, ee_addr); + return retval; +} + +/* MII transceiver control section. + Read and write the MII registers using software-generated serial + MDIO protocol. See the MII specifications or DP83840A data sheet + for details. + + The maximum data clock rate is 2.5 Mhz. The minimum timing is usually + met by back-to-back 33Mhz PCI cycles. */ +#define mdio_delay(mdio_addr) readl(mdio_addr) + +/* Set iff a MII transceiver on any interface requires mdio preamble. + This only set with older tranceivers, so the extra + code size of a per-interface flag is not worthwhile. */ +static char mii_preamble_required = 1; + +#define MDIO_WRITE0 (MDIO_EnbOutput) +#define MDIO_WRITE1 (MDIO_DataOut | MDIO_EnbOutput) + +/* Generate the preamble required for initial synchronization and + a few older transceivers. */ +static void mdio_sync(long mdio_addr) +{ + int bits = 32; + + /* Establish sync by sending at least 32 logic ones. */ + while (--bits >= 0) { + writel(MDIO_WRITE1, mdio_addr); + mdio_delay(mdio_addr); + writel(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } +} + +static int mdio_read(int base_address, int phy_id, int location) +{ + long mdio_addr = base_address + MIICtrl; + int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; + int i, retval = 0; + + if (mii_preamble_required) + mdio_sync(mdio_addr); + + /* Shift the read command bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + + writel(dataval, mdio_addr); + mdio_delay(mdio_addr); + writel(dataval | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 20; i > 0; i--) { + writel(MDIO_EnbIn, mdio_addr); + mdio_delay(mdio_addr); + retval = (retval << 1) | ((readl(mdio_addr) & MDIO_DataIn) ? 1 : 0); + writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } + return (retval>>1) & 0xffff; +} + +#if 0 +static void mdio_write(int base_address, int phy_id, int location, int value) +{ + long mdio_addr = base_address + MIICtrl; + int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; + int i; + + if (location == 4 && phy_id == w840private.phys[0]) + w840private.advertising = value; + + if (mii_preamble_required) + mdio_sync(mdio_addr); + + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + + writel(dataval, mdio_addr); + mdio_delay(mdio_addr); + writel(dataval | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + writel(MDIO_EnbIn, mdio_addr); + mdio_delay(mdio_addr); + writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); + mdio_delay(mdio_addr); + } + return; +} +#endif + +static void check_duplex(void) +{ + int mii_reg5 = mdio_read(ioaddr, w840private.phys[0], 5); + int negotiated = mii_reg5 & w840private.advertising; + int duplex; + + if (w840private.duplex_lock || mii_reg5 == 0xffff) + return; + + duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040; + if (w840private.full_duplex != duplex) { + w840private.full_duplex = duplex; + +#if defined(W89C840_DEBUG) + printf("winbond-840 : Setting %s-duplex based on MII # %d negotiated capability %X\n", + duplex ? "full" : "half", w840private.phys[0], negotiated); +#endif + + w840private.csr6 &= ~0x200; + w840private.csr6 |= duplex ? 0x200 : 0; + } +} + +static void set_rx_mode(void) +{ + u32 mc_filter[2]; /* Multicast hash filter */ + u32 rx_mode; + + /* Accept all multicasts from now on. */ + memset(mc_filter, 0xff, sizeof(mc_filter)); + +/* + * works OK with multicast enabled. + */ + + rx_mode = AcceptBroadcast | AcceptMyPhys | AcceptMulticast; + + writel(mc_filter[0], ioaddr + MulticastFilter0); + writel(mc_filter[1], ioaddr + MulticastFilter1); + w840private.csr6 &= ~0x00F8; + w840private.csr6 |= rx_mode; + writel(w840private.csr6, ioaddr + NetworkConfig); + +#if defined(W89C840_DEBUG) + printf("winbond-840 : Done setting RX mode.\n"); +#endif +} + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void init_ring(void) +{ + int i; + char * p; + + w840private.tx_full = 0; + w840private.tx_q_bytes = w840private.cur_rx = w840private.cur_tx = 0; + w840private.dirty_rx = w840private.dirty_tx = 0; + + w840private.rx_buf_sz = PKT_BUF_SZ; + w840private.rx_head_desc = &w840private.rx_ring[0]; + + /* Initial all Rx descriptors. Fill in the Rx buffers. */ + + p = &w89c840_buf.rx_packet[0]; + + for (i = 0; i < RX_RING_SIZE; i++) { + w840private.rx_ring[i].length = w840private.rx_buf_sz; + w840private.rx_ring[i].status = 0; + w840private.rx_ring[i].next_desc = virt_to_le32desc(&w840private.rx_ring[i+1]); + + w840private.rx_ring[i].buffer1 = virt_to_le32desc(p + (PKT_BUF_SZ * i)); + w840private.rx_ring[i].status = DescOwn | DescIntr; + } + + /* Mark the last entry as wrapping the ring. */ + w840private.rx_ring[i-1].length |= DescEndRing; + w840private.rx_ring[i-1].next_desc = virt_to_le32desc(&w840private.rx_ring[0]); + + w840private.dirty_rx = (unsigned int)(i - RX_RING_SIZE); + + for (i = 0; i < TX_RING_SIZE; i++) { + w840private.tx_ring[i].status = 0; + } + return; +} + + +DRIVER ( "W89C840F", nic_driver, pci_driver, w89c840_driver, + w89c840_probe, w89c840_disable ); + +/* + * Local variables: + * c-basic-offset: 8 + * c-indent-level: 8 + * tab-width: 8 + * End: + */ diff --git a/gpxe/src/drivers/net/wd.c b/gpxe/src/drivers/net/wd.c new file mode 100644 index 0000000..9939aa0 --- /dev/null +++ b/gpxe/src/drivers/net/wd.c @@ -0,0 +1,6 @@ +/* ISA memory-mapped NS8390-based cards, including WD80x3 */ +#if 0 /* Currently broken! */ +#define INCLUDE_WD +#define WD_DEFAULT_MEM 0xCC000 +#include "ns8390.c" +#endif diff --git a/gpxe/src/drivers/net/wlan_compat.h b/gpxe/src/drivers/net/wlan_compat.h new file mode 100644 index 0000000..a4f75e3 --- /dev/null +++ b/gpxe/src/drivers/net/wlan_compat.h @@ -0,0 +1,575 @@ +/* src/include/wlan/wlan_compat.h +* +* Types and macros to aid in portability +* +* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. +* -------------------------------------------------------------------- +* +* linux-wlan +* +* The contents of this file are subject to the Mozilla Public +* License Version 1.1 (the "License"); you may not use this file +* except in compliance with the License. You may obtain a copy of +* the License at http://www.mozilla.org/MPL/ +* +* Software distributed under the License is distributed on an "AS +* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +* implied. See the License for the specific language governing +* rights and limitations under the License. +* +* Alternatively, the contents of this file may be used under the +* terms of the GNU Public License version 2 (the "GPL"), in which +* case the provisions of the GPL are applicable instead of the +* above. If you wish to allow the use of your version of this file +* only under the terms of the GPL and not to allow others to use +* your version of this file under the MPL, indicate your decision +* by deleting the provisions above and replace them with the notice +* and other provisions required by the GPL. If you do not delete +* the provisions above, a recipient may use your version of this +* file under either the MPL or the GPL. +* +* -------------------------------------------------------------------- +* +* Inquiries regarding the linux-wlan Open Source project can be +* made directly to: +* +* AbsoluteValue Systems Inc. +* info@linux-wlan.com +* http://www.linux-wlan.com +* +* -------------------------------------------------------------------- +* +* Portions of the development of this software were funded by +* Intersil Corporation as part of PRISM(R) chipset product development. +* +* -------------------------------------------------------------------- +*/ + +#ifndef _WLAN_COMPAT_H +#define _WLAN_COMPAT_H + +/*=============================================================*/ +/*------ Establish Platform Identity --------------------------*/ +/*=============================================================*/ +/* Key macros: */ +/* WLAN_CPU_FAMILY */ + #define WLAN_Ix86 1 + #define WLAN_PPC 2 + #define WLAN_Ix96 3 + #define WLAN_ARM 4 + #define WLAN_ALPHA 5 + #define WLAN_MIPS 6 + #define WLAN_HPPA 7 +/* WLAN_CPU_CORE */ + #define WLAN_I386CORE 1 + #define WLAN_PPCCORE 2 + #define WLAN_I296 3 + #define WLAN_ARMCORE 4 + #define WLAN_ALPHACORE 5 + #define WLAN_MIPSCORE 6 + #define WLAN_HPPACORE 7 +/* WLAN_CPU_PART */ + #define WLAN_I386PART 1 + #define WLAN_MPC860 2 + #define WLAN_MPC823 3 + #define WLAN_I296SA 4 + #define WLAN_PPCPART 5 + #define WLAN_ARMPART 6 + #define WLAN_ALPHAPART 7 + #define WLAN_MIPSPART 8 + #define WLAN_HPPAPART 9 +/* WLAN_SYSARCH */ + #define WLAN_PCAT 1 + #define WLAN_MBX 2 + #define WLAN_RPX 3 + #define WLAN_LWARCH 4 + #define WLAN_PMAC 5 + #define WLAN_SKIFF 6 + #define WLAN_BITSY 7 + #define WLAN_ALPHAARCH 7 + #define WLAN_MIPSARCH 9 + #define WLAN_HPPAARCH 10 +/* WLAN_OS */ + #define WLAN_LINUX_KERNEL 1 + #define WLAN_LINUX_USER 2 +/* WLAN_HOSTIF (generally set on the command line, not detected) */ + #define WLAN_PCMCIA 1 + #define WLAN_ISA 2 + #define WLAN_PCI 3 + #define WLAN_USB 4 + #define WLAN_PLX 5 + +/* Note: the PLX HOSTIF above refers to some vendors implementations for */ +/* PCI. It's a PLX chip that is a PCI to PCMCIA adapter, but it */ +/* isn't a real PCMCIA host interface adapter providing all the */ +/* card&socket services. */ + +/* Lets try to figure out what we've got. Kernel mode or User mode? */ +#if defined(__KERNEL__) + #define WLAN_OS WLAN_LINUX_KERNEL +#else + #define WLAN_OS WLAN_LINUX_USER +#endif + +#ifdef __powerpc__ +#ifndef __ppc__ +#define __ppc__ +#endif +#endif + +#if (defined(CONFIG_PPC) || defined(CONFIG_8xx)) +#ifndef __ppc__ +#define __ppc__ +#endif +#endif + +#if defined(__KERNEL__) +#if defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) + #define WLAN_CPU_FAMILY WLAN_Ix86 + #define WLAN_CPU_CORE WLAN_I386CORE + #define WLAN_CPU_PART WLAN_I386PART + #define WLAN_SYSARCH WLAN_PCAT +#elif defined(__ppc__) + #define WLAN_CPU_FAMILY WLAN_PPC + #define WLAN_CPU_CORE WLAN_PPCCORE + #if defined(CONFIG_MBX) + #define WLAN_CPU_PART WLAN_MPC860 + #define WLAN_SYSARCH WLAN_MBX + #elif defined(CONFIG_RPXLITE) + #define WLAN_CPU_PART WLAN_MPC823 + #define WLAN_SYSARCH WLAN_RPX + #elif defined(CONFIG_RPXCLASSIC) + #define WLAN_CPU_PART WLAN_MPC860 + #define WLAN_SYSARCH WLAN_RPX + #else + #define WLAN_CPU_PART WLAN_PPCPART + #define WLAN_SYSARCH WLAN_PMAC + #endif +#elif defined(__arm__) + #define WLAN_CPU_FAMILY WLAN_ARM + #define WLAN_CPU_CORE WLAN_ARMCORE + #define WLAN_CPU_PART WLAN_ARM_PART + #define WLAN_SYSARCH WLAN_SKIFF +#elif defined(__alpha__) + #define WLAN_CPU_FAMILY WLAN_ALPHA + #define WLAN_CPU_CORE WLAN_ALPHACORE + #define WLAN_CPU_PART WLAN_ALPHAPART + #define WLAN_SYSARCH WLAN_ALPHAARCH +#elif defined(__mips__) + #define WLAN_CPU_FAMILY WLAN_MIPS + #define WLAN_CPU_CORE WLAN_MIPSCORE + #define WLAN_CPU_PART WLAN_MIPSPART + #define WLAN_SYSARCH WLAN_MIPSARCH +#elif defined(__hppa__) + #define WLAN_CPU_FAMILY WLAN_HPPA + #define WLAN_CPU_CORE WLAN_HPPACORE + #define WLAN_CPU_PART WLAN_HPPAPART + #define WLAN_SYSARCH WLAN_HPPAARCH +#else + #error "No CPU identified!" +#endif +#endif /* __KERNEL__ */ + +/* + Some big endian machines implicitly do all I/O in little endian mode. + + In particular: + Linux/PPC on PowerMacs (PCI) + Arm/Intel Xscale (PCI) + + This may also affect PLX boards and other BE &| PPC platforms; + as new ones are discovered, add them below. +*/ + +#if (WLAN_HOSTIF == WLAN_PCI) +#if ((WLAN_SYSARCH == WLAN_SKIFF) || (WLAN_SYSARCH == WLAN_PMAC)) +#define REVERSE_ENDIAN +#endif +#endif + +/*=============================================================*/ +/*------ Bit settings -----------------------------------------*/ +/*=============================================================*/ + +#define BIT0 0x00000001 +#define BIT1 0x00000002 +#define BIT2 0x00000004 +#define BIT3 0x00000008 +#define BIT4 0x00000010 +#define BIT5 0x00000020 +#define BIT6 0x00000040 +#define BIT7 0x00000080 +#define BIT8 0x00000100 +#define BIT9 0x00000200 +#define BIT10 0x00000400 +#define BIT11 0x00000800 +#define BIT12 0x00001000 +#define BIT13 0x00002000 +#define BIT14 0x00004000 +#define BIT15 0x00008000 +#define BIT16 0x00010000 +#define BIT17 0x00020000 +#define BIT18 0x00040000 +#define BIT19 0x00080000 +#define BIT20 0x00100000 +#define BIT21 0x00200000 +#define BIT22 0x00400000 +#define BIT23 0x00800000 +#define BIT24 0x01000000 +#define BIT25 0x02000000 +#define BIT26 0x04000000 +#define BIT27 0x08000000 +#define BIT28 0x10000000 +#define BIT29 0x20000000 +#define BIT30 0x40000000 +#define BIT31 0x80000000 + +typedef unsigned char UINT8; +typedef unsigned short UINT16; +typedef unsigned long UINT32; + +typedef signed char INT8; +typedef signed short INT16; +typedef signed long INT32; + +typedef unsigned int UINT; +typedef signed int INT; + +typedef unsigned long long UINT64; +typedef signed long long INT64; + +#define UINT8_MAX (0xffUL) +#define UINT16_MAX (0xffffUL) +#define UINT32_MAX (0xffffffffUL) + +#define INT8_MAX (0x7fL) +#define INT16_MAX (0x7fffL) +#define INT32_MAX (0x7fffffffL) + +/*=============================================================*/ +/*------ Compiler Portability Macros --------------------------*/ +/*=============================================================*/ +#define __WLAN_ATTRIB_PACK__ __attribute__ ((packed)) +#define __WLAN_PRAGMA_PACK1__ +#define __WLAN_PRAGMA_PACKDFLT__ +#define __WLAN_INLINE__ inline +#define WLAN_MIN_ARRAY 0 + +/*=============================================================*/ +/*------ OS Portability Macros --------------------------------*/ +/*=============================================================*/ + +#ifndef WLAN_DBVAR +#define WLAN_DBVAR wlan_debug +#endif + +#if (WLAN_OS == WLAN_LINUX_KERNEL) + #define WLAN_LOG_ERROR0(x) printk(KERN_ERR "%s: " x , __FUNCTION__ ); + #define WLAN_LOG_ERROR1(x,n) printk(KERN_ERR "%s: " x , __FUNCTION__ , (n)); + #define WLAN_LOG_ERROR2(x,n1,n2) printk(KERN_ERR "%s: " x , __FUNCTION__ , (n1), (n2)); + #define WLAN_LOG_ERROR3(x,n1,n2,n3) printk(KERN_ERR "%s: " x , __FUNCTION__, (n1), (n2), (n3)); + #define WLAN_LOG_ERROR4(x,n1,n2,n3,n4) printk(KERN_ERR "%s: " x , __FUNCTION__, (n1), (n2), (n3), (n4)); + + #define WLAN_LOG_WARNING0(x) printk(KERN_WARNING "%s: " x , __FUNCTION__); + #define WLAN_LOG_WARNING1(x,n) printk(KERN_WARNING "%s: " x , __FUNCTION__, (n)); + #define WLAN_LOG_WARNING2(x,n1,n2) printk(KERN_WARNING "%s: " x , __FUNCTION__, (n1), (n2)); + #define WLAN_LOG_WARNING3(x,n1,n2,n3) printk(KERN_WARNING "%s: " x , __FUNCTION__, (n1), (n2), (n3)); + #define WLAN_LOG_WARNING4(x,n1,n2,n3,n4) printk(KERN_WARNING "%s: " x , __FUNCTION__ , (n1), (n2), (n3), (n4)); + + #define WLAN_LOG_NOTICE0(x) printk(KERN_NOTICE "%s: " x , __FUNCTION__); + #define WLAN_LOG_NOTICE1(x,n) printk(KERN_NOTICE "%s: " x , __FUNCTION__, (n)); + #define WLAN_LOG_NOTICE2(x,n1,n2) printk(KERN_NOTICE "%s: " x , __FUNCTION__, (n1), (n2)); + #define WLAN_LOG_NOTICE3(x,n1,n2,n3) printk(KERN_NOTICE "%s: " x , __FUNCTION__, (n1), (n2), (n3)); + #define WLAN_LOG_NOTICE4(x,n1,n2,n3,n4) printk(KERN_NOTICE "%s: " x , __FUNCTION__, (n1), (n2), (n3), (n4)); + + #define WLAN_LOG_INFO0(x) printk(KERN_INFO x); + #define WLAN_LOG_INFO1(x,n) printk(KERN_INFO x, (n)); + #define WLAN_LOG_INFO2(x,n1,n2) printk(KERN_INFO x, (n1), (n2)); + #define WLAN_LOG_INFO3(x,n1,n2,n3) printk(KERN_INFO x, (n1), (n2), (n3)); + #define WLAN_LOG_INFO4(x,n1,n2,n3,n4) printk(KERN_INFO x, (n1), (n2), (n3), (n4)); + #define WLAN_LOG_INFO5(x,n1,n2,n3,n4,n5) printk(KERN_INFO x, (n1), (n2), (n3), (n4), (n5)); + + #if defined(WLAN_INCLUDE_DEBUG) + #define WLAN_ASSERT(c) if ((!(c)) && WLAN_DBVAR >= 1) { \ + WLAN_LOG_DEBUG0(1, "Assertion failure!\n"); } + #define WLAN_HEX_DUMP( l, x, p, n) if( WLAN_DBVAR >= (l) ){ \ + int __i__; \ + printk(KERN_DEBUG x ":"); \ + for( __i__=0; __i__ < (n); __i__++) \ + printk( " %02x", ((UINT8*)(p))[__i__]); \ + printk("\n"); } + + #define DBFENTER { if ( WLAN_DBVAR >= 4 ){ WLAN_LOG_DEBUG0(3,"Enter\n"); } } + #define DBFEXIT { if ( WLAN_DBVAR >= 4 ){ WLAN_LOG_DEBUG0(3,"Exit\n"); } } + + #define WLAN_LOG_DEBUG0(l,x) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ ); + #define WLAN_LOG_DEBUG1(l,x,n) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n)); + #define WLAN_LOG_DEBUG2(l,x,n1,n2) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n1), (n2)); + #define WLAN_LOG_DEBUG3(l,x,n1,n2,n3) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n1), (n2), (n3)); + #define WLAN_LOG_DEBUG4(l,x,n1,n2,n3,n4) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n1), (n2), (n3), (n4)); + #define WLAN_LOG_DEBUG5(l,x,n1,n2,n3,n4,n5) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n1), (n2), (n3), (n4), (n5)); + #define WLAN_LOG_DEBUG6(l,x,n1,n2,n3,n4,n5,n6) if ( WLAN_DBVAR >= (l)) printk(KERN_DEBUG "%s: " x , __FUNCTION__ , (n1), (n2), (n3), (n4), (n5), (n6)); + #else + #define WLAN_ASSERT(c) + #define WLAN_HEX_DUMP( l, s, p, n) + + #define DBFENTER + #define DBFEXIT + + #define WLAN_LOG_DEBUG0(l, s) + #define WLAN_LOG_DEBUG1(l, s,n) + #define WLAN_LOG_DEBUG2(l, s,n1,n2) + #define WLAN_LOG_DEBUG3(l, s,n1,n2,n3) + #define WLAN_LOG_DEBUG4(l, s,n1,n2,n3,n4) + #define WLAN_LOG_DEBUG5(l, s,n1,n2,n3,n4,n5) + #endif +#else + #define WLAN_LOG_ERROR0(s) + #define WLAN_LOG_ERROR1(s,n) + #define WLAN_LOG_ERROR2(s,n1,n2) + #define WLAN_LOG_ERROR3(s,n1,n2,n3) + #define WLAN_LOG_ERROR4(s,n1,n2,n3,n4) + + #define WLAN_LOG_WARNING0(s) + #define WLAN_LOG_WARNING1(s,n) + #define WLAN_LOG_WARNING2(s,n1,n2) + #define WLAN_LOG_WARNING3(s,n1,n2,n3) + #define WLAN_LOG_WARNING4(s,n1,n2,n3,n4) + + #define WLAN_LOG_NOTICE0(s) + #define WLAN_LOG_NOTICE1(s,n) + #define WLAN_LOG_NOTICE2(s,n1,n2) + #define WLAN_LOG_NOTICE3(s,n1,n2,n3) + #define WLAN_LOG_NOTICE4(s,n1,n2,n3,n4) + + #define WLAN_ASSERT(c) + #define WLAN_HEX_DUMP( l, s, p, n) + + #define DBFENTER + #define DBFEXIT + + #define WLAN_LOG_INFO0(s) + #define WLAN_LOG_INFO1(s,n) + #define WLAN_LOG_INFO2(s,n1,n2) + #define WLAN_LOG_INFO3(s,n1,n2,n3) + #define WLAN_LOG_INFO4(s,n1,n2,n3,n4) + #define WLAN_LOG_INFO5(s,n1,n2,n3,n4,n5) + + #define WLAN_LOG_DEBUG0(l, s) + #define WLAN_LOG_DEBUG1(l, s,n) + #define WLAN_LOG_DEBUG2(l, s,n1,n2) + #define WLAN_LOG_DEBUG3(l, s,n1,n2,n3) + #define WLAN_LOG_DEBUG4(l, s,n1,n2,n3,n4) + #define WLAN_LOG_DEBUG5(l, s,n1,n2,n3,n4,n5) +#endif + +#define wlan_ms_per_tick (1000UL / (wlan_ticks_per_sec)) +#define wlan_ms_to_ticks(n) ( (n) / (wlan_ms_per_tick)) +#define wlan_tu2ticks(n) ( (n) / (wlan_ms_per_tick)) +#define WLAN_INT_DISABLE(n) { save_flags((n)); cli(); } +#define WLAN_INT_ENABLE(n) { sti(); restore_flags((n)); } + +#ifdef CONFIG_MODVERSIONS +#define MODVERSIONS 1 +#include +#endif + +#ifdef CONFIG_SMP +#define __SMP__ 1 +#endif + +#ifndef KERNEL_VERSION +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17)) +#define CONFIG_NETLINK 1 +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)) +#define kfree_s(a, b) kfree((a)) +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) +#ifndef init_waitqueue_head +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,0,16)) +#define init_waitqueue_head(p) (*(p) = NULL) +#else +#define init_waitqueue_head(p) init_waitqueue(p) +#endif +typedef struct wait_queue *wait_queue_head_t; +typedef struct wait_queue wait_queue_t; +#define set_current_state(b) { current->state = (b); mb(); } +#define init_waitqueue_entry(a, b) { (a)->task = current; } +#endif +#endif + +#ifndef wait_event_interruptible_timeout +// retval == 0; signal met; we're good. +// retval < 0; interrupted by signal. +// retval > 0; timed out. +#define __wait_event_interruptible_timeout(wq, condition, timeout, ret) \ +do { \ + int __ret = 0; \ + if (!(condition)) { \ + wait_queue_t __wait; \ + unsigned long expire; \ + init_waitqueue_entry(&__wait, current); \ + \ + expire = timeout + jiffies; \ + add_wait_queue(&wq, &__wait); \ + for (;;) { \ + set_current_state(TASK_INTERRUPTIBLE); \ + if (condition) \ + break; \ + if (jiffies > expire) { \ + ret = jiffies - expire; \ + break; \ + } \ + if (!signal_pending(current)) { \ + schedule_timeout(timeout); \ + continue; \ + } \ + ret = -ERESTARTSYS; \ + break; \ + } \ + set_current_state(TASK_RUNNING); \ + remove_wait_queue(&wq, &__wait); \ + } \ +} while (0) + +#define wait_event_interruptible_timeout(wq, condition, timeout) \ +({ \ + int __ret = 0; \ + if (!(condition)) \ + __wait_event_interruptible_timeout(wq, condition, \ + timeout, __ret); \ + __ret; \ +}) + +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,90)) +#define spin_lock(l) do { } while (0) +#define spin_unlock(l) do { } while (0) +#define spin_lock_irqsave(l,f) do { save_flags(f); cli(); } while (0) +#define spin_unlock_irqrestore(l,f) do { restore_flags(f); } while (0) +#define spin_lock_init(s) do { } while (0) +#define spin_trylock(l) (1) +typedef int spinlock_t; +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)) +#ifdef CONFIG_SMP +#define spin_is_locked(x) (*(volatile char *)(&(x)->lock) <= 0) +#else +#define spin_is_locked(l) (0) +#endif +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,38)) +typedef struct device netdevice_t; +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)) +typedef struct net_device netdevice_t; +#else +#undef netdevice_t +typedef struct net_device netdevice_t; +#endif + +#ifdef WIRELESS_EXT +#if (WIRELESS_EXT < 13) +struct iw_request_info +{ + __u16 cmd; /* Wireless Extension command */ + __u16 flags; /* More to come ;-) */ +}; +#endif +#endif + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,18)) +#define MODULE_PARM(a,b) extern int __bogus_decl +#define MODULE_AUTHOR(a) extern int __bogus_decl +#define MODULE_DESCRIPTION(a) extern int __bogus_decl +#define MODULE_SUPPORTED_DEVICE(a) extern int __bogus_decl +#undef GET_USE_COUNT +#define GET_USE_COUNT(m) mod_use_count_ +#endif + +#ifndef MODULE_LICENSE +#define MODULE_LICENSE(m) extern int __bogus_decl +#endif + +/* TODO: Do we care about this? */ +#ifndef MODULE_DEVICE_TABLE +#define MODULE_DEVICE_TABLE(foo,bar) +#endif + +#define wlan_minutes2ticks(a) ((a)*(wlan_ticks_per_sec * 60)) +#define wlan_seconds2ticks(a) ((a)*(wlan_ticks_per_sec)) + +/*=============================================================*/ +/*------ Hardware Portability Macros --------------------------*/ +/*=============================================================*/ + +#define ieee2host16(n) __le16_to_cpu(n) +#define ieee2host32(n) __le32_to_cpu(n) +#define host2ieee16(n) __cpu_to_le16(n) +#define host2ieee32(n) __cpu_to_le32(n) + +#if (WLAN_CPU_FAMILY == WLAN_PPC) + #define wlan_inw(a) in_be16((unsigned short *)((a)+_IO_BASE)) + #define wlan_inw_le16_to_cpu(a) inw((a)) + #define wlan_outw(v,a) out_be16((unsigned short *)((a)+_IO_BASE), (v)) + #define wlan_outw_cpu_to_le16(v,a) outw((v),(a)) +#else + #define wlan_inw(a) inw((a)) + #define wlan_inw_le16_to_cpu(a) __cpu_to_le16(inw((a))) + #define wlan_outw(v,a) outw((v),(a)) + #define wlan_outw_cpu_to_le16(v,a) outw(__cpu_to_le16((v)),(a)) +#endif + +/*=============================================================*/ +/*--- General Macros ------------------------------------------*/ +/*=============================================================*/ + +#define wlan_max(a, b) (((a) > (b)) ? (a) : (b)) +#define wlan_min(a, b) (((a) < (b)) ? (a) : (b)) + +#define wlan_isprint(c) (((c) > (0x19)) && ((c) < (0x7f))) + +#define wlan_hexchar(x) (((x) < 0x0a) ? ('0' + (x)) : ('a' + ((x) - 0x0a))) + +/* Create a string of printable chars from something that might not be */ +/* It's recommended that the str be 4*len + 1 bytes long */ +#define wlan_mkprintstr(buf, buflen, str, strlen) \ +{ \ + int i = 0; \ + int j = 0; \ + memset(str, 0, (strlen)); \ + for (i = 0; i < (buflen); i++) { \ + if ( wlan_isprint((buf)[i]) ) { \ + (str)[j] = (buf)[i]; \ + j++; \ + } else { \ + (str)[j] = '\\'; \ + (str)[j+1] = 'x'; \ + (str)[j+2] = wlan_hexchar(((buf)[i] & 0xf0) >> 4); \ + (str)[j+3] = wlan_hexchar(((buf)[i] & 0x0f)); \ + j += 4; \ + } \ + } \ +} + +/*=============================================================*/ +/*--- Variables -----------------------------------------------*/ +/*=============================================================*/ + +extern int wlan_debug; +extern int wlan_ethconv; /* What's the default ethconv? */ + +/*=============================================================*/ +/*--- Functions -----------------------------------------------*/ +/*=============================================================*/ +#endif /* _WLAN_COMPAT_H */ + diff --git a/gpxe/src/drivers/nvs/nvs.c b/gpxe/src/drivers/nvs/nvs.c new file mode 100644 index 0000000..8e94b87 --- /dev/null +++ b/gpxe/src/drivers/nvs/nvs.c @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include + +/** @file + * + * Non-volatile storage + * + */ + +/** + * Read from non-volatile storage device + * + * @v nvs NVS device + * @v address Address from which to read + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +int nvs_read ( struct nvs_device *nvs, unsigned int address, + void *data, size_t len ) { + size_t frag_len; + int rc; + + /* We don't even attempt to handle buffer lengths that aren't + * an integral number of words. + */ + assert ( ( len & ( ( 1 << nvs->word_len_log2 ) - 1 ) ) == 0 ); + + while ( len ) { + + /* Calculate space remaining up to next block boundary */ + frag_len = ( ( nvs->block_size - + ( address & ( nvs->block_size - 1 ) ) ) + << nvs->word_len_log2 ); + + /* Limit to space remaining in buffer */ + if ( frag_len > len ) + frag_len = len; + + /* Read this portion of the buffer from the device */ + if ( ( rc = nvs->read ( nvs, address, data, frag_len ) ) != 0 ) + return rc; + + /* Update parameters */ + data += frag_len; + address += ( frag_len >> nvs->word_len_log2 ); + len -= frag_len; + } + + return 0; +} + +/** + * Verify content of non-volatile storage device + * + * @v nvs NVS device + * @v address Address from which to read + * @v data Data to compare against + * @v len Length of data buffer + * @ret rc Return status code + */ +static int nvs_verify ( struct nvs_device *nvs, unsigned int address, + const void *data, size_t len ) { + uint8_t read_data[len]; + int rc; + + /* Read data into temporary buffer */ + if ( ( rc = nvs_read ( nvs, address, read_data, len ) ) != 0 ) + return rc; + + /* Compare data */ + if ( memcmp ( data, read_data, len ) != 0 ) { + DBG ( "NVS %p verification failed at %#04x+%zd\n", + nvs, address, len ); + return -EIO; + } + + return 0; +} + +/** + * Write to non-volatile storage device + * + * @v nvs NVS device + * @v address Address to which to write + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +int nvs_write ( struct nvs_device *nvs, unsigned int address, + const void *data, size_t len ) { + size_t frag_len; + int rc; + + /* We don't even attempt to handle buffer lengths that aren't + * an integral number of words. + */ + assert ( ( len & ( ( 1 << nvs->word_len_log2 ) - 1 ) ) == 0 ); + + while ( len ) { + + /* Calculate space remaining up to next block boundary */ + frag_len = ( ( nvs->block_size - + ( address & ( nvs->block_size - 1 ) ) ) + << nvs->word_len_log2 ); + + /* Limit to space remaining in buffer */ + if ( frag_len > len ) + frag_len = len; + + /* Write this portion of the buffer to the device */ + if ( ( rc = nvs->write ( nvs, address, data, frag_len ) ) != 0) + return rc; + + /* Read back and verify data */ + if ( ( rc = nvs_verify ( nvs, address, data, frag_len ) ) != 0) + return rc; + + /* Update parameters */ + data += frag_len; + address += ( frag_len >> nvs->word_len_log2 ); + len -= frag_len; + } + + return 0; +} diff --git a/gpxe/src/drivers/nvs/spi.c b/gpxe/src/drivers/nvs/spi.c new file mode 100644 index 0000000..dbdc32f --- /dev/null +++ b/gpxe/src/drivers/nvs/spi.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include + +/** @file + * + * SPI devices + * + */ + +/** + * Munge SPI device address into command + * + * @v command SPI command + * @v address Address + * @v munge_address Device requires address munging + * @ret command Actual SPI command to use + * + * Some devices with 9-bit addresses (e.g. AT25040A EEPROM) use bit 3 + * of the command byte as address bit A8, rather than having a + * two-byte address. This function takes care of generating the + * appropriate command. + */ +static inline unsigned int spi_command ( unsigned int command, + unsigned int address, + int munge_address ) { + return ( command | ( ( ( address >> 8 ) & munge_address ) << 3 ) ); +} + +/** + * Wait for SPI device to complete operation + * + * @v device SPI device + * @ret rc Return status code + */ +static int spi_wait ( struct spi_device *device ) { + struct spi_bus *bus = device->bus; + uint8_t status; + int i; + int rc; + + for ( i = 0 ; i < 50 ; i++ ) { + udelay ( 20 ); + if ( ( rc = bus->rw ( bus, device, SPI_RDSR, -1, NULL, + &status, sizeof ( status ) ) ) != 0 ) + return rc; + if ( ! ( status & SPI_STATUS_NRDY ) ) + return 0; + } + DBG ( "SPI %p timed out\n", device ); + return -ETIMEDOUT; +} + +/** + * Read data from SPI device + * + * @v nvs NVS device + * @v address Address from which to read + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +int spi_read ( struct nvs_device *nvs, unsigned int address, + void *data, size_t len ) { + struct spi_device *device = nvs_to_spi ( nvs ); + struct spi_bus *bus = device->bus; + unsigned int command = spi_command ( SPI_READ, address, + device->munge_address ); + int rc; + + DBG ( "SPI %p reading %zd bytes from %#04x\n", device, len, address ); + if ( ( rc = bus->rw ( bus, device, command, address, + NULL, data, len ) ) != 0 ) { + DBG ( "SPI %p failed to read data from device\n", device ); + return rc; + } + + return 0; +} + +/** + * Write data to SPI device + * + * @v nvs NVS device + * @v address Address from which to read + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +int spi_write ( struct nvs_device *nvs, unsigned int address, + const void *data, size_t len ) { + struct spi_device *device = nvs_to_spi ( nvs ); + struct spi_bus *bus = device->bus; + unsigned int command = spi_command ( SPI_WRITE, address, + device->munge_address ); + int rc; + + DBG ( "SPI %p writing %zd bytes to %#04x\n", device, len, address ); + + if ( ( rc = bus->rw ( bus, device, SPI_WREN, -1, + NULL, NULL, 0 ) ) != 0 ) { + DBG ( "SPI %p failed to write-enable device\n", device ); + return rc; + } + + if ( ( rc = bus->rw ( bus, device, command, address, + data, NULL, len ) ) != 0 ) { + DBG ( "SPI %p failed to write data to device\n", device ); + return rc; + } + + if ( ( rc = spi_wait ( device ) ) != 0 ) { + DBG ( "SPI %p failed to complete write operation\n", device ); + return rc; + } + + return 0; +} + diff --git a/gpxe/src/drivers/nvs/threewire.c b/gpxe/src/drivers/nvs/threewire.c new file mode 100644 index 0000000..dbecf90 --- /dev/null +++ b/gpxe/src/drivers/nvs/threewire.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include + +/** @file + * + * Three-wire serial devices + * + */ + +/** + * Read data from three-wire device + * + * @v nvs NVS device + * @v address Address from which to read + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +int threewire_read ( struct nvs_device *nvs, unsigned int address, + void *data, size_t len ) { + struct spi_device *device = nvs_to_spi ( nvs ); + struct spi_bus *bus = device->bus; + + assert ( bus->mode == SPI_MODE_THREEWIRE ); + + DBG ( "3wire %p reading %zd bytes at %04x\n", device, len, address ); + + return bus->rw ( bus, device, THREEWIRE_READ, address, + NULL, data, len ); +} + +/** + * Write data to three-wire device + * + * @v nvs NVS device + * @v address Address from which to read + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +int threewire_write ( struct nvs_device *nvs, unsigned int address, + const void *data, size_t len ) { + struct spi_device *device = nvs_to_spi ( nvs ); + struct spi_bus *bus = device->bus; + int rc; + + assert ( bus->mode == SPI_MODE_THREEWIRE ); + + DBG ( "3wire %p writing %zd bytes at %04x\n", device, len, address ); + + /* Enable device for writing */ + if ( ( rc = bus->rw ( bus, device, THREEWIRE_EWEN, + THREEWIRE_EWEN_ADDRESS, NULL, NULL, 0 ) ) != 0 ) + return rc; + + /* Write data */ + if ( ( rc = bus->rw ( bus, device, THREEWIRE_WRITE, address, + data, NULL, len ) ) != 0 ) + return rc; + + /* Our model of an SPI bus doesn't provide a mechanism for + * "assert CS, wait for MISO to become high, so just wait for + * long enough to ensure that the write has completed. + */ + mdelay ( THREEWIRE_WRITE_MDELAY ); + + return 0; +} diff --git a/gpxe/src/hci/commands/autoboot_cmd.c b/gpxe/src/hci/commands/autoboot_cmd.c new file mode 100644 index 0000000..0e6f294 --- /dev/null +++ b/gpxe/src/hci/commands/autoboot_cmd.c @@ -0,0 +1,25 @@ +#include +#include +#include + +static int autoboot_exec ( int argc, char **argv ) { + + if ( argc != 1 ) { + printf ( "Usage:\n" + " %s\n" + "\n" + "Attempts to boot the system\n", + argv[0] ); + return 1; + } + + autoboot(); + + /* Can never return success by definition */ + return 1; +} + +struct command autoboot_command __command = { + .name = "autoboot", + .exec = autoboot_exec, +}; diff --git a/gpxe/src/hci/commands/config_cmd.c b/gpxe/src/hci/commands/config_cmd.c new file mode 100644 index 0000000..87abb05 --- /dev/null +++ b/gpxe/src/hci/commands/config_cmd.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include + +static int config_exec ( int argc, char **argv ) { + char *settings_name; + struct settings *settings; + int rc; + + if ( argc > 2 ) { + printf ( "Usage: %s [scope]\n" + "Opens the option configuration console\n", argv[0] ); + return 1; + } + + settings_name = ( ( argc == 2 ) ? argv[1] : "" ); + settings = find_settings ( settings_name ); + if ( ! settings ) { + printf ( "No such scope \"%s\"\n", settings_name ); + return 1; + } + + if ( ( rc = settings_ui ( settings ) ) != 0 ) { + printf ( "Could not save settings: %s\n", + strerror ( rc ) ); + return 1; + } + + return 0; +} + +struct command config_command __command = { + .name = "config", + .exec = config_exec, +}; diff --git a/gpxe/src/hci/commands/dhcp_cmd.c b/gpxe/src/hci/commands/dhcp_cmd.c new file mode 100644 index 0000000..9b577c8 --- /dev/null +++ b/gpxe/src/hci/commands/dhcp_cmd.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * DHCP management commands + * + */ + +/** + * "dhcp" command syntax message + * + * @v argv Argument list + */ +static void dhcp_syntax ( char **argv ) { + printf ( "Usage:\n" + " %s \n" + "\n" + "Configure a network interface using DHCP\n", + argv[0] ); +} + +/** + * The "dhcp" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Exit code + */ +static int dhcp_exec ( int argc, char **argv ) { + static struct option longopts[] = { + { "help", 0, NULL, 'h' }, + { NULL, 0, NULL, 0 }, + }; + const char *netdev_txt; + struct net_device *netdev; + int c; + int rc; + + /* Parse options */ + while ( ( c = getopt_long ( argc, argv, "h", longopts, NULL ) ) >= 0 ){ + switch ( c ) { + case 'h': + /* Display help text */ + default: + /* Unrecognised/invalid option */ + dhcp_syntax ( argv ); + return 1; + } + } + + /* Need exactly one interface name remaining after the options */ + if ( optind != ( argc - 1 ) ) { + dhcp_syntax ( argv ); + return 1; + } + netdev_txt = argv[optind]; + + /* Parse arguments */ + netdev = find_netdev ( netdev_txt ); + if ( ! netdev ) { + printf ( "No such interface: %s\n", netdev_txt ); + return 1; + } + + /* Perform DHCP */ + if ( ( rc = dhcp ( netdev ) ) != 0 ) { + printf ( "Could not configure %s: %s\n", netdev->name, + strerror ( rc ) ); + return 1; + } + + return 0; +} + +/** + * "pxebs" command syntax message + * + * @v argv Argument list + */ +static void pxebs_syntax ( char **argv ) { + printf ( "Usage:\n" + " %s \n" + "\n" + "Perform PXE Boot Server discovery\n", + argv[0] ); +} + +/** + * The "pxebs" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Exit code + */ +static int pxebs_exec ( int argc, char **argv ) { + static struct option longopts[] = { + { "help", 0, NULL, 'h' }, + { NULL, 0, NULL, 0 }, + }; + const char *netdev_txt; + const char *pxe_type_txt; + struct net_device *netdev; + unsigned int pxe_type; + char *end; + int c; + int rc; + + /* Parse options */ + while ( ( c = getopt_long ( argc, argv, "h", longopts, NULL ) ) >= 0 ){ + switch ( c ) { + case 'h': + /* Display help text */ + default: + /* Unrecognised/invalid option */ + pxebs_syntax ( argv ); + return 1; + } + } + if ( optind != ( argc - 2 ) ) { + pxebs_syntax ( argv ); + return 1; + } + netdev_txt = argv[optind]; + pxe_type_txt = argv[ optind + 1 ]; + + /* Parse arguments */ + netdev = find_netdev ( netdev_txt ); + if ( ! netdev ) { + printf ( "No such interface: %s\n", netdev_txt ); + return 1; + } + pxe_type = strtoul ( pxe_type_txt, &end, 0 ); + if ( *end ) { + printf ( "Bad server type: %s\n", pxe_type_txt ); + return 1; + } + + /* Perform Boot Server Discovery */ + if ( ( rc = pxebs ( netdev, pxe_type ) ) != 0 ) { + printf ( "Could not discover boot server on %s: %s\n", + netdev->name, strerror ( rc ) ); + return 1; + } + + return 0; +} + +/** DHCP management commands */ +struct command dhcp_commands[] __command = { + { + .name = "dhcp", + .exec = dhcp_exec, + }, + { + .name = "pxebs", + .exec = pxebs_exec, + }, +}; diff --git a/gpxe/src/hci/commands/gdbstub_cmd.c b/gpxe/src/hci/commands/gdbstub_cmd.c new file mode 100644 index 0000000..7416752 --- /dev/null +++ b/gpxe/src/hci/commands/gdbstub_cmd.c @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2008 Stefan Hajnoczi . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +/** @file + * + * GDB stub command + * + */ + +/** + * "gdbstub" command syntax message + * + * @v argv Argument list + */ +static void gdbstub_syntax ( char **argv ) { + printf ( "Usage:\n" + " %s [...]\n" + "\n" + "Start remote debugging using one of the following transports:\n" + " serial use serial port (if compiled in)\n" + " udp use UDP over network interface (if compiled in)\n", + argv[0] ); +} + +/** + * The "gdbstub" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Exit code + */ +static int gdbstub_exec ( int argc, char **argv ) { + static struct option longopts[] = { + { "help", 0, NULL, 'h' }, + { NULL, 0, NULL, 0 }, + }; + const char *trans_name; + struct gdb_transport *trans; + int c; + + /* Parse options */ + while ( ( c = getopt_long ( argc, argv, "h", longopts, NULL ) ) >= 0 ){ + switch ( c ) { + case 'h': + /* Display help text */ + default: + /* Unrecognised/invalid option */ + gdbstub_syntax ( argv ); + return 1; + } + } + + /* At least one argument */ + if ( optind == argc ) { + gdbstub_syntax ( argv ); + return 1; + } + + trans_name = argv[optind++]; + + /* Initialise transport */ + trans = find_gdb_transport ( trans_name ); + if ( !trans ) { + printf ( "%s: no such transport (is it compiled in?)\n", trans_name ); + return 1; + } + + if ( trans->init ) { + if ( trans->init ( argc - optind, &argv[optind] ) != 0 ) { + return 1; + } + } + + /* Enter GDB stub */ + gdbstub_start ( trans ); + return 0; +} + +/** GDB stub commands */ +struct command gdbstub_commands[] __command = { + { + .name = "gdbstub", + .exec = gdbstub_exec, + }, +}; diff --git a/gpxe/src/hci/commands/ifmgmt_cmd.c b/gpxe/src/hci/commands/ifmgmt_cmd.c new file mode 100644 index 0000000..f2508e5 --- /dev/null +++ b/gpxe/src/hci/commands/ifmgmt_cmd.c @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include + +/** @file + * + * Network interface management commands + * + */ + +/** Options shared by all if commands */ +static struct option ifcommon_longopts[] = { + { "help", 0, NULL, 'h' }, + { NULL, 0, NULL, 0 }, +}; + +/** + * Print syntax of if command + * + * @v argv Command arguments + * @v verb Verb describing the action of the command + */ +static void ifcommon_syntax ( char **argv, const char *verb ) { + printf ( "Usage:\n" + " %s [] [...]\n" + "\n" + "%s the specified network interfaces\n", + argv[0], verb ); +} + +/** + * Execute if command over all network devices + * + * @v payload Command to execute + * @ret rc Exit code + */ +static int ifcommon_do_all ( int ( * payload ) ( struct net_device * ) ) { + struct net_device *netdev; + int rc = 0; + + /* Execute payload for each network device */ + for_each_netdev ( netdev ) { + if ( payload ( netdev ) != 0 ) + rc = 1; + } + return rc; +} + +/** + * Execute if command over list of network devices + * + * @v payload Command to execute + * @ret rc Exit code + */ +static int ifcommon_do_list ( int ( * payload ) ( struct net_device * ), + char **list, unsigned int count ) { + const char *netdev_name; + struct net_device *netdev; + int rc = 0; + + while ( count-- ) { + netdev_name = *(list++); + netdev = find_netdev ( netdev_name ); + if ( ! netdev ) { + printf ( "%s: no such interface\n", netdev_name ); + rc = 1; + continue; + } + if ( payload ( netdev ) != 0 ) + rc = 1; + } + return rc; +} + +/** + * Execute if command + * + * @v payload Command to execute + * @v verb Verb describing the action of the command + * @v argc Argument count + * @v argv Argument list + * @ret rc Exit code + */ +static __attribute__ (( regparm ( 2 ) )) int +ifcommon_exec ( int ( * payload ) ( struct net_device * ), + const char *verb, int argc, char **argv ) { + int c; + + /* Parse options */ + while ( ( c = getopt_long ( argc, argv, "h", ifcommon_longopts, + NULL ) ) >= 0 ) { + switch ( c ) { + case 'h': + /* Display help text */ + default: + /* Unrecognised/invalid option */ + ifcommon_syntax ( argv, verb ); + return 1; + } + } + + if ( optind == argc ) { + return ifcommon_do_all ( payload ); + } else { + return ifcommon_do_list ( payload, &argv[optind], + ( argc - optind ) ); + } +} + +/* "ifopen" command */ + +static int ifopen_payload ( struct net_device *netdev ) { + return ifopen ( netdev ); +} + +static int ifopen_exec ( int argc, char **argv ) { + return ifcommon_exec ( ifopen_payload, "Open", argc, argv ); +} + +/* "ifclose" command */ + +static int ifclose_payload ( struct net_device *netdev ) { + ifclose ( netdev ); + return 0; +} + +static int ifclose_exec ( int argc, char **argv ) { + return ifcommon_exec ( ifclose_payload, "Close", argc, argv ); +} + +/* "ifstat" command */ + +static int ifstat_payload ( struct net_device *netdev ) { + ifstat ( netdev ); + return 0; +} + +static int ifstat_exec ( int argc, char **argv ) { + return ifcommon_exec ( ifstat_payload, "Display status of", + argc, argv ); +} + +/** Interface management commands */ +struct command ifmgmt_commands[] __command = { + { + .name = "ifopen", + .exec = ifopen_exec, + }, + { + .name = "ifclose", + .exec = ifclose_exec, + }, + { + .name = "ifstat", + .exec = ifstat_exec, + }, +}; diff --git a/gpxe/src/hci/commands/image_cmd.c b/gpxe/src/hci/commands/image_cmd.c new file mode 100644 index 0000000..6d8b590 --- /dev/null +++ b/gpxe/src/hci/commands/image_cmd.c @@ -0,0 +1,593 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Image management commands + * + */ + +enum image_action { + IMG_FETCH = 0, + IMG_LOAD, + IMG_EXEC, +}; + +/** + * Fill in image command line + * + * @v image Image + * @v nargs Argument count + * @v args Argument list + * @ret rc Return status code + */ +static int imgfill_cmdline ( struct image *image, unsigned int nargs, + char **args ) { + size_t len; + unsigned int i; + + /* Determine total length of command line */ + len = 1; /* NUL */ + for ( i = 0 ; i < nargs ; i++ ) + len += ( 1 /* possible space */ + strlen ( args[i] ) ); + + { + char buf[len]; + char *ptr = buf; + + /* Assemble command line */ + buf[0] = '\0'; + for ( i = 0 ; i < nargs ; i++ ) { + ptr += sprintf ( ptr, "%s%s", ( i ? " " : "" ), + args[i] ); + } + assert ( ptr < ( buf + len ) ); + + return image_set_cmdline ( image, buf ); + } +} + +/** + * "imgfetch"/"module"/"kernel" command syntax message + * + * @v argv Argument list + */ +static void imgfetch_core_syntax ( char **argv, enum image_action action ) { + static const char *actions[] = { + [IMG_FETCH] = "Fetch", + [IMG_LOAD] = "Fetch and load", + [IMG_EXEC] = "Fetch and execute", + }; + + printf ( "Usage:\n" + " %s [-n|--name ] filename [arguments...]\n" + "\n" + "%s executable/loadable image\n", + argv[0], actions[action] ); +} + +/** + * The "imgfetch"/"module"/"kernel" command body + * + * @v image_type Image type to assign (or NULL) + * @v load Image will be automatically loaded after fetching + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int imgfetch_core_exec ( struct image_type *image_type, + enum image_action action, + int argc, char **argv ) { + static struct option longopts[] = { + { "help", 0, NULL, 'h' }, + { "name", required_argument, NULL, 'n' }, + { NULL, 0, NULL, 0 }, + }; + struct image *image; + const char *name = NULL; + char *filename; + int ( * image_register ) ( struct image *image ); + int c; + int rc; + + /* Parse options */ + while ( ( c = getopt_long ( argc, argv, "hn:", + longopts, NULL ) ) >= 0 ) { + switch ( c ) { + case 'n': + /* Set image name */ + name = optarg; + break; + case 'h': + /* Display help text */ + default: + /* Unrecognised/invalid option */ + imgfetch_core_syntax ( argv, action ); + return -EINVAL; + } + } + + /* Need at least a filename remaining after the options */ + if ( optind == argc ) { + imgfetch_core_syntax ( argv, action ); + return -EINVAL; + } + filename = argv[optind++]; + if ( ! name ) + name = basename ( filename ); + + /* Allocate image */ + image = alloc_image(); + if ( ! image ) { + printf ( "%s\n", strerror ( -ENOMEM ) ); + return -ENOMEM; + } + + /* Fill in image name */ + if ( name ) { + if ( ( rc = image_set_name ( image, name ) ) != 0 ) + return rc; + } + + /* Set image type (if specified) */ + image->type = image_type; + + /* Fill in command line */ + if ( ( rc = imgfill_cmdline ( image, ( argc - optind ), + &argv[optind] ) ) != 0 ) + return rc; + + /* Fetch the image */ + switch ( action ) { + case IMG_FETCH: + image_register = register_image; + break; + case IMG_LOAD: + image_register = register_and_autoload_image; + break; + case IMG_EXEC: + image_register = register_and_autoexec_image; + break; + default: + assert ( 0 ); + return -EINVAL; + } + if ( ( rc = imgfetch ( image, filename, image_register ) ) != 0 ) { + printf ( "Could not fetch %s: %s\n", + filename, strerror ( rc ) ); + image_put ( image ); + return rc; + } + + image_put ( image ); + return 0; +} + +/** + * The "imgfetch"/"module" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Exit code + */ +static int imgfetch_exec ( int argc, char **argv ) { + int rc; + + if ( ( rc = imgfetch_core_exec ( NULL, IMG_FETCH, + argc, argv ) ) != 0 ) + return rc; + + return 0; +} + +/** + * The "kernel" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Exit code + */ +static int kernel_exec ( int argc, char **argv ) { + int rc; + + if ( ( rc = imgfetch_core_exec ( NULL, IMG_LOAD, argc, argv ) ) != 0 ) + return rc; + + return 0; +} + +/** + * The "chain" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Exit code + */ +static int chain_exec ( int argc, char **argv) { + int rc; + + if ( ( rc = imgfetch_core_exec ( NULL, IMG_EXEC, argc, argv ) ) != 0 ) + return rc; + + return 0; +} + +/** + * "imgload" command syntax message + * + * @v argv Argument list + */ +static void imgload_syntax ( char **argv ) { + printf ( "Usage:\n" + " %s \n" + "\n" + "Load executable/loadable image\n", + argv[0] ); +} + +/** + * The "imgload" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Exit code + */ +static int imgload_exec ( int argc, char **argv ) { + static struct option longopts[] = { + { "help", 0, NULL, 'h' }, + { NULL, 0, NULL, 0 }, + }; + struct image *image; + const char *name; + int c; + int rc; + + /* Parse options */ + while ( ( c = getopt_long ( argc, argv, "h", longopts, NULL ) ) >= 0 ){ + switch ( c ) { + case 'h': + /* Display help text */ + default: + /* Unrecognised/invalid option */ + imgload_syntax ( argv ); + return 1; + } + } + + /* Need exactly one image name remaining after the options */ + if ( optind != ( argc - 1 ) ) { + imgload_syntax ( argv ); + return 1; + } + name = argv[optind]; + + /* Load all specified images */ + image = find_image ( name ); + if ( ! image ) { + printf ( "No such image: %s\n", name ); + return 1; + } + if ( ( rc = imgload ( image ) ) != 0 ) { + printf ( "Could not load %s: %s\n", name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * "imgargs" command syntax message + * + * @v argv Argument list + */ +static void imgargs_syntax ( char **argv ) { + printf ( "Usage:\n" + " %s [...]\n" + "\n" + "Set arguments for executable/loadable image\n", + argv[0] ); +} + +/** + * The "imgargs" command body + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Exit code + */ +static int imgargs_exec ( int argc, char **argv ) { + static struct option longopts[] = { + { "help", 0, NULL, 'h' }, + { NULL, 0, NULL, 0 }, + }; + struct image *image; + const char *name; + int c; + int rc; + + /* Parse options */ + while ( ( c = getopt_long ( argc, argv, "h", longopts, NULL ) ) >= 0 ){ + switch ( c ) { + case 'h': + /* Display help text */ + default: + /* Unrecognised/invalid option */ + imgargs_syntax ( argv ); + return 1; + } + } + + /* Need at least an image name remaining after the options */ + if ( optind == argc ) { + imgargs_syntax ( argv ); + return 1; + } + name = argv[optind++]; + + /* Fill in command line */ + image = find_image ( name ); + if ( ! image ) { + printf ( "No such image: %s\n", name ); + return 1; + } + if ( ( rc = imgfill_cmdline ( image, ( argc - optind ), + &argv[optind] ) ) != 0 ) + return rc; + + + return 0; +} + +/** + * "imgexec" command syntax message + * + * @v argv Argument list + */ +static void imgexec_syntax ( char **argv ) { + printf ( "Usage:\n" + " %s \n" + "\n" + "Execute executable/loadable image\n", + argv[0] ); +} + +/** + * The "imgexec" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Exit code + */ +static int imgexec_exec ( int argc, char **argv ) { + static struct option longopts[] = { + { "help", 0, NULL, 'h' }, + { NULL, 0, NULL, 0 }, + }; + struct image *image; + const char *name = NULL; + int c; + int rc; + + /* Parse options */ + while ( ( c = getopt_long ( argc, argv, "h", longopts, NULL ) ) >= 0 ){ + switch ( c ) { + case 'h': + /* Display help text */ + default: + /* Unrecognised/invalid option */ + imgexec_syntax ( argv ); + return 1; + } + } + + /* Need no more than one image name */ + if ( optind != argc ) + name = argv[optind++]; + if ( optind != argc ) { + imgexec_syntax ( argv ); + return 1; + } + + /* Execute specified image */ + if ( name ) { + image = find_image ( name ); + if ( ! image ) { + printf ( "No such image: %s\n", name ); + return 1; + } + } else { + image = imgautoselect(); + if ( ! image ) { + printf ( "No (unique) loaded image\n" ); + return 1; + } + } + + if ( ( rc = imgexec ( image ) ) != 0 ) { + printf ( "Could not execute %s: %s\n", + image->name, strerror ( rc ) ); + return 1; + } + + return 0; +} + +/** + * "imgstat" command syntax message + * + * @v argv Argument list + */ +static void imgstat_syntax ( char **argv ) { + printf ( "Usage:\n" + " %s\n" + "\n" + "List executable/loadable images\n", + argv[0] ); +} + +/** + * The "imgstat" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Exit code + */ +static int imgstat_exec ( int argc, char **argv ) { + static struct option longopts[] = { + { "help", 0, NULL, 'h' }, + { NULL, 0, NULL, 0 }, + }; + struct image *image; + int c; + + /* Parse options */ + while ( ( c = getopt_long ( argc, argv, "h", longopts, NULL ) ) >= 0 ){ + switch ( c ) { + case 'h': + /* Display help text */ + default: + /* Unrecognised/invalid option */ + imgstat_syntax ( argv ); + return 1; + } + } + + /* No arguments */ + if ( optind != argc ) { + imgstat_syntax ( argv ); + return 1; + } + + /* Show status of all images */ + for_each_image ( image ) { + imgstat ( image ); + } + return 0; +} + +/** + * "imgstat" command syntax message + * + * @v argv Argument list + */ +static void imgfree_syntax ( char **argv ) { + printf ( "Usage:\n" + " %s\n" + "\n" + "Free all executable/loadable images\n", + argv[0] ); +} + +/** + * The "imgfree" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Exit code + */ +static int imgfree_exec ( int argc, char **argv ) { + static struct option longopts[] = { + { "help", 0, NULL, 'h' }, + { NULL, 0, NULL, 0 }, + }; + struct image *image; + struct image *tmp; + int c; + + /* Parse options */ + while ( ( c = getopt_long ( argc, argv, "h", longopts, NULL ) ) >= 0 ){ + switch ( c ) { + case 'h': + /* Display help text */ + default: + /* Unrecognised/invalid option */ + imgfree_syntax ( argv ); + return 1; + } + } + + /* No arguments */ + if ( optind != argc ) { + imgfree_syntax ( argv ); + return 1; + } + + /* Free all images */ + list_for_each_entry_safe ( image, tmp, &images, list ) { + imgfree ( image ); + } + return 0; +} + +/** Image management commands */ +struct command image_commands[] __command = { + { + .name = "imgfetch", + .exec = imgfetch_exec, + }, + { + .name = "module", + .exec = imgfetch_exec, /* synonym for "imgfetch" */ + }, + { + .name = "initrd", + .exec = imgfetch_exec, /* synonym for "imgfetch" */ + }, + { + .name = "kernel", + .exec = kernel_exec, + }, + { + .name = "chain", + .exec = chain_exec, + }, + { + .name = "imgload", + .exec = imgload_exec, + }, + { + .name = "imgargs", + .exec = imgargs_exec, + }, + { + .name = "imgexec", + .exec = imgexec_exec, + }, + { + .name = "boot", /* synonym for "imgexec" */ + .exec = imgexec_exec, + }, + { + .name = "imgstat", + .exec = imgstat_exec, + }, + { + .name = "imgfree", + .exec = imgfree_exec, + }, +}; diff --git a/gpxe/src/hci/commands/login_cmd.c b/gpxe/src/hci/commands/login_cmd.c new file mode 100644 index 0000000..e425247 --- /dev/null +++ b/gpxe/src/hci/commands/login_cmd.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +static int login_exec ( int argc, char **argv ) { + int rc; + + if ( argc > 1 ) { + printf ( "Usage: %s\n" + "Prompt for login credentials\n", argv[0] ); + return 1; + } + + if ( ( rc = login_ui() ) != 0 ) { + printf ( "Could not set credentials: %s\n", + strerror ( rc ) ); + return 1; + } + + return 0; +} + +struct command login_command __command = { + .name = "login", + .exec = login_exec, +}; diff --git a/gpxe/src/hci/commands/nvo_cmd.c b/gpxe/src/hci/commands/nvo_cmd.c new file mode 100644 index 0000000..c0c0728 --- /dev/null +++ b/gpxe/src/hci/commands/nvo_cmd.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static int show_exec ( int argc, char **argv ) { + char buf[256]; + int rc; + + if ( argc != 2 ) { + printf ( "Syntax: %s \n", argv[0] ); + return 1; + } + + if ( ( rc = fetchf_named_setting ( argv[1], buf, + sizeof ( buf ) ) ) < 0 ){ + printf ( "Could not find \"%s\": %s\n", + argv[1], strerror ( rc ) ); + return 1; + } + + printf ( "%s = %s\n", argv[1], buf ); + return 0; +} + +static int set_exec ( int argc, char **argv ) { + int rc; + + if ( argc != 3 ) { + printf ( "Syntax: %s \n", argv[0] ); + return 1; + } + + if ( ( rc = storef_named_setting ( argv[1], argv[2] ) ) != 0 ) { + printf ( "Could not set \"%s\"=\"%s\": %s\n", + argv[1], argv[2], strerror ( rc ) ); + return 1; + } + + return 0; +} + +static int clear_exec ( int argc, char **argv ) { + int rc; + + if ( argc != 2 ) { + printf ( "Syntax: %s \n", argv[0] ); + return 1; + } + + if ( ( rc = delete_named_setting ( argv[1] ) ) != 0 ) { + printf ( "Could not clear \"%s\": %s\n", + argv[1], strerror ( rc ) ); + return 1; + } + + return 0; +} + +struct command nvo_commands[] __command = { + { + .name = "show", + .exec = show_exec, + }, + { + .name = "set", + .exec = set_exec, + }, + { + .name = "clear", + .exec = clear_exec, + }, +}; diff --git a/gpxe/src/hci/commands/route_cmd.c b/gpxe/src/hci/commands/route_cmd.c new file mode 100644 index 0000000..227682c --- /dev/null +++ b/gpxe/src/hci/commands/route_cmd.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include + +/** @file + * + * Routing table management commands + * + */ + +/** + * "route" command syntax message + * + * @v argv Argument list + */ +static void route_syntax ( char **argv ) { + printf ( "Usage:\n" + " %s\n" + "\n" + "Displays the routing table\n", + argv[0] ); +} + +/** + * The "route" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Exit code + */ +static int route_exec ( int argc, char **argv ) { + static struct option longopts[] = { + { "help", 0, NULL, 'h' }, + { NULL, 0, NULL, 0 }, + }; + + int c; + + /* Parse options */ + while ( ( c = getopt_long ( argc, argv, "h", longopts, NULL ) ) >= 0 ){ + switch ( c ) { + case 'h': + /* Display help text */ + default: + /* Unrecognised/invalid option */ + route_syntax ( argv ); + return 1; + } + } + + if ( optind != argc ) { + route_syntax ( argv ); + return 1; + } + + route(); + return 0; +} + +/** Routing table management commands */ +struct command route_commands[] __command = { + { + .name = "route", + .exec = route_exec, + }, +}; diff --git a/gpxe/src/hci/commands/sanboot_cmd.c b/gpxe/src/hci/commands/sanboot_cmd.c new file mode 100644 index 0000000..d5bbfb8 --- /dev/null +++ b/gpxe/src/hci/commands/sanboot_cmd.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include + +/** + * "sanboot" command syntax message + * + * @v argv Argument list + */ +static void sanboot_syntax ( char **argv ) { + printf ( "Usage:\n" + " %s \n" + "\n" + "Boot from SAN target\n", + argv[0] ); +} + +/** + * The "sanboot" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Exit code + */ +static int sanboot_exec ( int argc, char **argv ) { + static struct option longopts[] = { + { "help", 0, NULL, 'h' }, + { NULL, 0, NULL, 0 }, + }; + const char *root_path = NULL; + int c; + int rc; + + /* Parse options */ + while ( ( c = getopt_long ( argc, argv, "h", longopts, NULL ) ) >= 0 ){ + switch ( c ) { + case 'h': + /* Display help text */ + default: + /* Unrecognised/invalid option */ + sanboot_syntax ( argv ); + return 1; + } + } + + /* Need exactly one image name remaining after the options */ + if ( optind != ( argc - 1 ) ) { + sanboot_syntax ( argv ); + return 1; + } + root_path = argv[optind]; + + /* Boot from root path */ + if ( ( rc = boot_root_path ( root_path ) ) != 0 ) { + printf ( "Could not boot from %s: %s\n", + root_path, strerror ( rc ) ); + return 1; + } + + return 0; +} + +struct command sanboot_command __command = { + .name = "sanboot", + .exec = sanboot_exec, +}; diff --git a/gpxe/src/hci/editstring.c b/gpxe/src/hci/editstring.c new file mode 100644 index 0000000..347249f --- /dev/null +++ b/gpxe/src/hci/editstring.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include + +/** @file + * + * Editable strings + * + */ + +static void insert_delete ( struct edit_string *string, size_t delete_len, + const char *insert_text ) + __attribute__ (( nonnull (1) )); +static void insert_character ( struct edit_string *string, + unsigned int character ) __nonnull; +static void delete_character ( struct edit_string *string ) __nonnull; +static void backspace ( struct edit_string *string ) __nonnull; +static void kill_eol ( struct edit_string *string ) __nonnull; + +/** + * Insert and/or delete text within an editable string + * + * @v string Editable string + * @v delete_len Length of text to delete from current cursor position + * @v insert_text Text to insert at current cursor position, or NULL + */ +static void insert_delete ( struct edit_string *string, size_t delete_len, + const char *insert_text ) { + size_t old_len, max_delete_len, insert_len, max_insert_len, new_len; + + /* Calculate lengths */ + old_len = strlen ( string->buf ); + assert ( string->cursor <= old_len ); + max_delete_len = ( old_len - string->cursor ); + if ( delete_len > max_delete_len ) + delete_len = max_delete_len; + insert_len = ( insert_text ? strlen ( insert_text ) : 0 ); + max_insert_len = ( ( string->len - 1 ) - ( old_len - delete_len ) ); + if ( insert_len > max_insert_len ) + insert_len = max_insert_len; + new_len = ( old_len - delete_len + insert_len ); + + /* Fill in edit history */ + string->mod_start = string->cursor; + string->mod_end = ( ( new_len > old_len ) ? new_len : old_len ); + + /* Move data following the cursor */ + memmove ( ( string->buf + string->cursor + insert_len ), + ( string->buf + string->cursor + delete_len ), + ( max_delete_len + 1 - delete_len ) ); + + /* Copy inserted text to cursor position */ + memcpy ( ( string->buf + string->cursor ), insert_text, insert_len ); + string->cursor += insert_len; +} + +/** + * Insert character at current cursor position + * + * @v string Editable string + * @v character Character to insert + */ +static void insert_character ( struct edit_string *string, + unsigned int character ) { + char insert_text[2] = { character, '\0' }; + insert_delete ( string, 0, insert_text ); +} + +/** + * Delete character at current cursor position + * + * @v string Editable string + */ +static void delete_character ( struct edit_string *string ) { + insert_delete ( string, 1, NULL ); +} + +/** + * Delete character to left of current cursor position + * + * @v string Editable string + */ +static void backspace ( struct edit_string *string ) { + if ( string->cursor > 0 ) { + string->cursor--; + delete_character ( string ); + } +} + +/** + * Delete to end of line + * + * @v string Editable string + */ +static void kill_eol ( struct edit_string *string ) { + insert_delete ( string, ~( ( size_t ) 0 ), NULL ); +} + +/** + * Edit editable string + * + * @v string Editable string + * @v key Key pressed by user + * @ret key Key returned to application, or zero + * + * Handles keypresses and updates the content of the editable string. + * Basic line editing facilities (delete/insert/cursor) are supported. + * If edit_string() understands and uses the keypress it will return + * zero, otherwise it will return the original key. + * + * This function does not update the display in any way. + * + * The string's edit history will be updated to allow the caller to + * efficiently bring the display into sync with the string content. + */ +int edit_string ( struct edit_string *string, int key ) { + int retval = 0; + size_t len = strlen ( string->buf ); + + /* Prepare edit history */ + string->last_cursor = string->cursor; + string->mod_start = string->cursor; + string->mod_end = string->cursor; + + /* Interpret key */ + if ( ( key >= 0x20 ) && ( key <= 0x7e ) ) { + /* Printable character; insert at current position */ + insert_character ( string, key ); + } else switch ( key ) { + case KEY_BACKSPACE: + /* Backspace */ + backspace ( string ); + break; + case KEY_DC: + case CTRL_D: + /* Delete character */ + delete_character ( string ); + break; + case CTRL_K: + /* Delete to end of line */ + kill_eol ( string ); + break; + case KEY_HOME: + case CTRL_A: + /* Start of line */ + string->cursor = 0; + break; + case KEY_END: + case CTRL_E: + /* End of line */ + string->cursor = len; + break; + case KEY_LEFT: + case CTRL_B: + /* Cursor left */ + if ( string->cursor > 0 ) + string->cursor--; + break; + case KEY_RIGHT: + case CTRL_F: + /* Cursor right */ + if ( string->cursor < len ) + string->cursor++; + break; + default: + retval = key; + break; + } + + return retval; +} diff --git a/gpxe/src/hci/mucurses/alert.c b/gpxe/src/hci/mucurses/alert.c new file mode 100644 index 0000000..00e959a --- /dev/null +++ b/gpxe/src/hci/mucurses/alert.c @@ -0,0 +1,18 @@ +#include +#include + +/** @file + * + * MuCurses alert functions + * + */ + +/** + * Audible signal + * + * @ret rc return status code + */ +int beep ( void ) { + printf("\a"); + return OK; +} diff --git a/gpxe/src/hci/mucurses/ansi_screen.c b/gpxe/src/hci/mucurses/ansi_screen.c new file mode 100644 index 0000000..468bac0 --- /dev/null +++ b/gpxe/src/hci/mucurses/ansi_screen.c @@ -0,0 +1,72 @@ +#include +#include +#include + +static void ansiscr_reset(struct _curses_screen *scr) __nonnull; +static void ansiscr_movetoyx(struct _curses_screen *scr, + unsigned int y, unsigned int x) __nonnull; +static void ansiscr_putc(struct _curses_screen *scr, chtype c) __nonnull; + +unsigned short _COLS = 80; +unsigned short _LINES = 24; + +static void ansiscr_reset ( struct _curses_screen *scr ) { + /* Reset terminal attributes and clear screen */ + scr->attrs = 0; + scr->curs_x = 0; + scr->curs_y = 0; + printf ( "\033[0m" ); +} + +static void ansiscr_movetoyx ( struct _curses_screen *scr, + unsigned int y, unsigned int x ) { + if ( ( x != scr->curs_x ) || ( y != scr->curs_y ) ) { + /* ANSI escape sequence to update cursor position */ + printf ( "\033[%d;%dH", ( y + 1 ), ( x + 1 ) ); + scr->curs_x = x; + scr->curs_y = y; + } +} + +static void ansiscr_putc ( struct _curses_screen *scr, chtype c ) { + unsigned int character = ( c & A_CHARTEXT ); + attr_t attrs = ( c & ( A_ATTRIBUTES | A_COLOR ) ); + int bold = ( attrs & A_BOLD ); + attr_t cpair = PAIR_NUMBER ( attrs ); + short fcol; + short bcol; + + /* Update attributes if changed */ + if ( attrs != scr->attrs ) { + scr->attrs = attrs; + pair_content ( cpair, &fcol, &bcol ); + /* ANSI escape sequence to update character attributes */ + printf ( "\033[0;%d;3%d;4%dm", ( bold ? 1 : 22 ), fcol, bcol ); + } + + /* Print the actual character */ + putchar ( character ); + + /* Update expected cursor position */ + if ( ++(scr->curs_x) == _COLS ) { + scr->curs_x = 0; + ++scr->curs_y; + } +} + +static int ansiscr_getc ( struct _curses_screen *scr __unused ) { + return getchar(); +} + +static bool ansiscr_peek ( struct _curses_screen *scr __unused ) { + return iskey(); +} + +SCREEN _ansi_screen = { + .init = ansiscr_reset, + .exit = ansiscr_reset, + .movetoyx = ansiscr_movetoyx, + .putc = ansiscr_putc, + .getc = ansiscr_getc, + .peek = ansiscr_peek, +}; diff --git a/gpxe/src/hci/mucurses/clear.c b/gpxe/src/hci/mucurses/clear.c new file mode 100644 index 0000000..1813939 --- /dev/null +++ b/gpxe/src/hci/mucurses/clear.c @@ -0,0 +1,88 @@ +#include +#include "mucurses.h" +#include "cursor.h" + +/** @file + * + * MuCurses clearing functions + * + */ + +/** + * Clear a window to the bottom from current cursor position + * + * @v *win subject window + * @ret rc return status code + */ +int wclrtobot ( WINDOW *win ) { + struct cursor_pos pos; + + _store_curs_pos( win, &pos ); + do { + _wputc( win, ' ', WRAP ); + } while ( win->curs_y + win->curs_x ); + _restore_curs_pos( win, &pos ); + + return OK; +} + +/** + * Clear a window to the end of the current line + * + * @v *win subject window + * @ret rc return status code + */ +int wclrtoeol ( WINDOW *win ) { + struct cursor_pos pos; + + _store_curs_pos( win, &pos ); + while ( ( win->curs_y - pos.y ) == 0 ) { + _wputc( win, ' ', WRAP ); + } + _restore_curs_pos( win, &pos ); + + return OK; +} + +/** + * Delete character under the cursor in a window + * + * @v *win subject window + * @ret rc return status code + */ +int wdelch ( WINDOW *win ) { + _wputc( win, ' ', NOWRAP ); + _wcursback( win ); + + return OK; +} + +/** + * Delete line under a window's cursor + * + * @v *win subject window + * @ret rc return status code + */ +int wdeleteln ( WINDOW *win ) { + struct cursor_pos pos; + + _store_curs_pos( win, &pos ); + /* let's just set the cursor to the beginning of the line and + let wclrtoeol do the work :) */ + wmove( win, win->curs_y, 0 ); + wclrtoeol( win ); + _restore_curs_pos( win, &pos ); + return OK; +} + +/** + * Completely clear a window + * + * @v *win subject window + * @ret rc return status code + */ +int werase ( WINDOW *win ) { + wmove( win, 0, 0 ); + wclrtobot( win ); + return OK; +} diff --git a/gpxe/src/hci/mucurses/colour.c b/gpxe/src/hci/mucurses/colour.c new file mode 100644 index 0000000..2310641 --- /dev/null +++ b/gpxe/src/hci/mucurses/colour.c @@ -0,0 +1,64 @@ +#include + +struct colour_pair { + short fcol; + short bcol; +}; + +static struct colour_pair cpairs[COLOUR_PAIRS] = { + [0] = { COLOUR_WHITE, COLOUR_BLACK }, +}; + +/** + * Identify the RGB components of a given colour value + * + * @v colour colour value + * @v *red address to store red component + * @v *green address to store green component + * @v *blue address to store blue component + * @ret rc return status code + */ +int colour_content ( short colour, short *red, short *green, short *blue ) { + *red = ( ( colour & COLOUR_RED ) ? 1 : 0 ); + *green = ( ( colour & COLOUR_GREEN ) ? 1 : 0 ); + *blue = ( ( colour & COLOUR_BLUE ) ? 1 : 0 ); + return OK; +} + +/** + * Initialise colour pair + * + * @v pair colour pair number + * @v fcol foreground colour + * @v bcol background colour + */ +int init_pair ( short pair, short fcol, short bcol ) { + struct colour_pair *cpair; + + if ( ( pair < 1 ) || ( pair >= COLOUR_PAIRS ) ) + return ERR; + + cpair = &cpairs[pair]; + cpair->fcol = fcol; + cpair->bcol = bcol; + return OK; +} + +/** + * Get colours of colour pair + * + * @v pair colour pair number + * @ret fcol foreground colour + * @ret bcol background colour + */ +int pair_content ( short pair, short *fcol, short *bcol ) { + struct colour_pair *cpair; + + if ( ( pair < 0 ) || ( pair >= COLOUR_PAIRS ) ) + return ERR; + + cpair = &cpairs[pair]; + *fcol = cpair->fcol; + *bcol = cpair->bcol; + return OK; +} diff --git a/gpxe/src/hci/mucurses/cursor.h b/gpxe/src/hci/mucurses/cursor.h new file mode 100644 index 0000000..af86519 --- /dev/null +++ b/gpxe/src/hci/mucurses/cursor.h @@ -0,0 +1,35 @@ +#ifndef CURSOR_H +#define CURSOR_H + +/** @file + * + * MuCurses cursor implementation specific header file + * + */ + +struct cursor_pos { + unsigned int y, x; +}; + +/** + * Restore cursor position from encoded backup variable + * + * @v *win window on which to operate + * @v *pos pointer to struct in which original cursor position is stored + */ +static inline void _restore_curs_pos ( WINDOW *win, struct cursor_pos *pos ) { + wmove ( win, pos->y, pos->x ); +} + +/** + * Store cursor position for later restoration + * + * @v *win window on which to operate + * @v *pos pointer to struct in which to store cursor position + */ +static inline void _store_curs_pos ( WINDOW *win, struct cursor_pos *pos ) { + pos->y = win->curs_y; + pos->x = win->curs_x; +} + +#endif /* CURSOR_H */ diff --git a/gpxe/src/hci/mucurses/edging.c b/gpxe/src/hci/mucurses/edging.c new file mode 100644 index 0000000..eccd324 --- /dev/null +++ b/gpxe/src/hci/mucurses/edging.c @@ -0,0 +1,111 @@ +#include +#include "mucurses.h" +#include "cursor.h" + +/** @file + * + * MuCurses edging functions + * + */ + +/** + * Draw borders from single-byte characters and renditions around a + * window + * + * @v *win window to be bordered + * @v verch vertical chtype + * @v horch horizontal chtype + * @ret rc return status code + */ +int box ( WINDOW *win, chtype verch, chtype horch ) { + chtype corner = '+' | win->attrs; /* default corner character */ + return wborder( win, verch, verch, horch, horch, + corner, corner, corner, corner ); +} + +/** + * Draw borders from single-byte characters and renditions around a + * window + * + * @v *win window to be bordered + * @v ls left side + * @v rs right side + * @v ts top + * @v bs bottom + * @v tl top left corner + * @v tr top right corner + * @v bl bottom left corner + * @v br bottom right corner + * @ret rc return status code + */ +int wborder ( WINDOW *win, chtype ls, chtype rs, + chtype ts, chtype bs, chtype tl, + chtype tr, chtype bl, chtype br ) { + struct cursor_pos pos; + + _store_curs_pos( win, &pos ); + wmove(win,0,0); + + _wputch(win,tl,WRAP); + while ( ( win->width - 1 ) - win->curs_x ) { + _wputch(win,ts,WRAP); + } + _wputch(win,tr,WRAP); + + while ( ( win->height - 1 ) - win->curs_y ) { + _wputch(win,ls,WRAP); + wmove(win,win->curs_y,(win->width)-1); + _wputch(win,rs,WRAP); + } + + _wputch(win,bl,WRAP); + while ( ( win->width -1 ) - win->curs_x ) { + _wputch(win,bs,WRAP); + } + _wputch(win,br,NOWRAP); /* do not wrap last char to leave + cursor in last position */ + _restore_curs_pos( win, &pos ); + + return OK; +} + +/** + * Create a horizontal line in a window + * + * @v *win subject window + * @v ch rendition and character + * @v n max number of chars (wide) to render + * @ret rc return status code + */ +int whline ( WINDOW *win, chtype ch, int n ) { + struct cursor_pos pos; + + _store_curs_pos ( win, &pos ); + while ( ( win->curs_x - win->width ) && n-- ) { + _wputch ( win, ch, NOWRAP ); + } + _restore_curs_pos ( win, &pos ); + + return OK; +} + +/** + * Create a vertical line in a window + * + * @v *win subject window + * @v ch rendition and character + * @v n max number of chars (high) to render + * @ret rc return status code + */ +int wvline ( WINDOW *win, chtype ch, int n ) { + struct cursor_pos pos; + + _store_curs_pos ( win, &pos ); + while ( ( win->curs_y - win->height ) && n-- ) { + _wputch ( win, ch, NOWRAP ); + wmove( win, ++(win->curs_y), pos.x); + } + _restore_curs_pos ( win, &pos ); + + return OK; +} diff --git a/gpxe/src/hci/mucurses/kb.c b/gpxe/src/hci/mucurses/kb.c new file mode 100644 index 0000000..cada729 --- /dev/null +++ b/gpxe/src/hci/mucurses/kb.c @@ -0,0 +1,143 @@ +#include +#include +#include +#include "mucurses.h" + +/** @file + * + * MuCurses keyboard input handling functions + */ + +#define INPUT_DELAY 200 // half-blocking delay timer resolution (ms) +#define INPUT_DELAY_TIMEOUT 1000 // half-blocking delay timeout + +int m_delay; /* + < 0 : blocking read + 0 : non-blocking read + > 0 : timed blocking read + */ +bool m_echo; +bool m_cbreak; + +static int _wgetc ( WINDOW *win ) { + int timer, c; + + if ( win == NULL ) + return ERR; + + timer = INPUT_DELAY_TIMEOUT; + while ( ! win->scr->peek( win->scr ) ) { + if ( m_delay == 0 ) // non-blocking read + return ERR; + if ( timer > 0 ) { // time-limited blocking read + if ( m_delay > 0 ) + timer -= INPUT_DELAY; + mdelay( INPUT_DELAY ); + } else { return ERR; } // non-blocking read + } + + c = win->scr->getc( win->scr ); + + if ( m_echo && ( c >= 32 && c <= 126 ) ) // printable ASCII characters + _wputch( win, (chtype) ( c | win->attrs ), WRAP ); + + return c; +} + +/** + * Pop a character from the FIFO into a window + * + * @v *win window in which to echo input + * @ret c char from input stream + */ +int wgetch ( WINDOW *win ) { + int c; + + c = _wgetc( win ); + + if ( m_echo ) { + if ( c >= KEY_MIN ) { + switch(c) { + case KEY_LEFT : + case KEY_BACKSPACE : + _wcursback( win ); + wdelch( win ); + break; + default : + beep(); + break; + } + } else { + _wputch( win, (chtype)( c | win->attrs ), WRAP ); + } + } + + return c; +} + +/** + * Read at most n characters from the FIFO into a window + * + * @v *win window in which to echo input + * @v *str pointer to string in which to store result + * @v n maximum number of characters to read into string (inc. NUL) + * @ret rc return status code + */ +int wgetnstr ( WINDOW *win, char *str, int n ) { + char *_str; + int c; + + if ( n == 0 ) { + str = '\0'; + return OK; + } + + _str = str; + + while ( ( c = _wgetc( win ) ) != ERR ) { + /* termination enforcement - don't let us go past the + end of the allocated buffer... */ + if ( n == 0 && ( c >= 32 && c <= 126 ) ) { + _wcursback( win ); + wdelch( win ); + } else { + if ( c >= KEY_MIN ) { + switch(c) { + case KEY_LEFT : + case KEY_BACKSPACE : + _wcursback( win ); + wdelch( win ); + break; + case KEY_ENTER : + *_str = '\0'; + return OK; + default : + beep(); + break; + } + } + if ( c >= 32 && c <= 126 ) { + *(_str++) = c; n--; + } + } + } + + return ERR; +} + + +/** + * + */ +int echo ( void ) { + m_echo = TRUE; + return OK; +} + +/** + * + */ +int noecho ( void ) { + m_echo = FALSE; + return OK; +} diff --git a/gpxe/src/hci/mucurses/mucurses.c b/gpxe/src/hci/mucurses/mucurses.c new file mode 100644 index 0000000..3620d08 --- /dev/null +++ b/gpxe/src/hci/mucurses/mucurses.c @@ -0,0 +1,145 @@ +#include +#include +#include "mucurses.h" + +/** @file + * + * MuCurses core functions + * + */ + +static void _wupdcurs ( WINDOW *win ) __nonnull; +void _wputch ( WINDOW *win, chtype ch, int wrap ) __nonnull; +void _wputc ( WINDOW *win, char c, int wrap ) __nonnull; +void _wcursback ( WINDOW *win ) __nonnull; +void _wputchstr ( WINDOW *win, const chtype *chstr, int wrap, int n ) __nonnull; +void _wputstr ( WINDOW *win, const char *str, int wrap, int n ) __nonnull; +int wmove ( WINDOW *win, int y, int x ) __nonnull; + +WINDOW _stdscr = { + .attrs = A_DEFAULT, + .ori_y = 0, + .ori_x = 0, + .curs_y = 0, + .curs_x = 0, + .scr = &_ansi_screen, +}; + +/* + * Primitives + */ + +/** + * Update cursor position + * + * @v *win window in which to update position + */ +static void _wupdcurs ( WINDOW *win ) { + win->scr->movetoyx ( win->scr, win->ori_y + win->curs_y, + win->ori_x + win->curs_x ); +} + +/** + * Write a single character rendition to a window + * + * @v *win window in which to write + * @v ch character rendition to write + * @v wrap wrap "switch" + */ +void _wputch ( WINDOW *win, chtype ch, int wrap ) { + /* make sure we set the screen cursor to the right position + first! */ + _wupdcurs(win); + win->scr->putc(win->scr, ch); + if ( ++(win->curs_x) - win->width == 0 ) { + if ( wrap == WRAP ) { + win->curs_x = 0; + /* specification says we should really scroll, + but we have no buffer to scroll with, so we + can only overwrite back at the beginning of + the window */ + if ( ++(win->curs_y) - win->height == 0 ) + win->curs_y = 0; + } else { + (win->curs_x)--; + } + } +} + +/** + * Write a single character to a window + * + * @v *win window in which to write + * @v c character rendition to write + * @v wrap wrap "switch" + */ +void _wputc ( WINDOW *win, char c, int wrap ) { + _wputch ( win, ( c | win->attrs ), wrap ); +} + +/** + * Retreat the cursor back one position (useful for a whole host of + * ops) + * + * @v *win window in which to retreat + */ +void _wcursback ( WINDOW *win ) { + if ( win->curs_x == 0 ) { + if ( win->curs_y == 0 ) + win->curs_y = win->height - 1; + win->curs_x = win->width = 1; + } else { + win->curs_x--; + } + + _wupdcurs(win); +} + +/** + * Write a chtype string to a window + * + * @v *win window in which to write + * @v *chstr chtype string + * @v wrap wrap "switch" + * @v n write at most n chtypes + */ +void _wputchstr ( WINDOW *win, const chtype *chstr, int wrap, int n ) { + for ( ; *chstr && n-- ; chstr++ ) { + _wputch(win,*chstr,wrap); + } +} + +/** + * Write a standard c-style string to a window + * + * @v *win window in which to write + * @v *str string + * @v wrap wrap "switch" + * @v n write at most n chars from *str + */ +void _wputstr ( WINDOW *win, const char *str, int wrap, int n ) { + for ( ; *str && n-- ; str++ ) { + _wputc ( win, *str, wrap ); + } +} + +/** + * Move a window's cursor to the specified position + * + * @v *win window to be operated on + * @v y Y position + * @v x X position + * @ret rc return status code + */ +int wmove ( WINDOW *win, int y, int x ) { + /* chech for out-of-bounds errors */ + if ( ( (unsigned)y >= win->height ) || + ( (unsigned)x >= win->width ) ) { + return ERR; + } + + win->curs_y = y; + win->curs_x = x; + _wupdcurs(win); + return OK; +} diff --git a/gpxe/src/hci/mucurses/mucurses.h b/gpxe/src/hci/mucurses/mucurses.h new file mode 100644 index 0000000..1476733 --- /dev/null +++ b/gpxe/src/hci/mucurses/mucurses.h @@ -0,0 +1,21 @@ +#ifndef _MUCURSES_H +#define _MUCURSES_H + +/** @file + * + * MuCurses core implementation specific header file + * + */ + +#define WRAP 0 +#define NOWRAP 1 + +extern SCREEN _ansi_screen; + +extern void _wputch ( WINDOW *win, chtype ch, int wrap ) __nonnull; +extern void _wputc ( WINDOW *win, char c, int wrap ) __nonnull; +extern void _wputchstr ( WINDOW *win, const chtype *chstr, int wrap, int n ) __nonnull; +extern void _wputstr ( WINDOW *win, const char *str, int wrap, int n ) __nonnull; +extern void _wcursback ( WINDOW *win ) __nonnull; + +#endif /* _MUCURSES_H */ diff --git a/gpxe/src/hci/mucurses/print.c b/gpxe/src/hci/mucurses/print.c new file mode 100644 index 0000000..9fca308 --- /dev/null +++ b/gpxe/src/hci/mucurses/print.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include "mucurses.h" + +/** @file + * + * MuCurses printing functions + * + */ + +/** + * Add a single-byte character and rendition to a window and advance + * the cursor + * + * @v *win window to be rendered in + * @v ch character to be added at cursor + * @ret rc return status code + */ +int waddch ( WINDOW *win, const chtype ch ) { + _wputch( win, ch, WRAP ); + return OK; +} + +/** + * Add string of single-byte characters to a window + * + * @v *win window to be rendered in + * @v *str standard c-style string + * @v n max number of chars from string to render + * @ret rc return status code + */ +int waddnstr ( WINDOW *win, const char *str, int n ) { + _wputstr( win, str, WRAP, n ); + return OK; +} + +struct printw_context { + struct printf_context ctx; + WINDOW *win; +}; + +static void _printw_handler ( struct printf_context *ctx, unsigned int c ) { + struct printw_context *wctx = + container_of ( ctx, struct printw_context, ctx ); + + _wputch( wctx->win, c | wctx->win->attrs, WRAP ); +} + +/** + * Print formatted output in a window + * + * @v *win subject window + * @v *fmt formatted string + * @v varglist argument list + * @ret rc return status code + */ +int vw_printw ( WINDOW *win, const char *fmt, va_list varglist ) { + struct printw_context wctx; + + wctx.win = win; + wctx.ctx.handler = _printw_handler; + vcprintf ( &(wctx.ctx), fmt, varglist ); + return OK; +} + +/** + * Print formatted output to a window + * + * @v *win subject window + * @v *fmt formatted string + * @v ... string arguments + * @ret rc return status code + */ +int wprintw ( WINDOW *win, const char *fmt, ... ) { + va_list args; + int i; + + va_start ( args, fmt ); + i = vw_printw ( win, fmt, args ); + va_end ( args ); + return i; +} diff --git a/gpxe/src/hci/mucurses/print_nadv.c b/gpxe/src/hci/mucurses/print_nadv.c new file mode 100644 index 0000000..ee472e6 --- /dev/null +++ b/gpxe/src/hci/mucurses/print_nadv.c @@ -0,0 +1,26 @@ +#include +#include "mucurses.h" +#include "cursor.h" + +/** @file + * + * MuCurses printing functions (no cursor advance) + * + */ + +/** + * Add string of single-byte characters and renditions to a window + * + * @v *win window to be rendered in + * @v *chstr pointer to first chtype in "string" + * @v n max number of chars from chstr to render + * @ret rc return status code + */ +int waddchnstr ( WINDOW *win, const chtype *chstr, int n ) { + struct cursor_pos pos; + + _store_curs_pos( win, &pos ); + _wputchstr( win, chstr, NOWRAP, n ); + _restore_curs_pos( win, &pos ); + return OK; +} diff --git a/gpxe/src/hci/mucurses/slk.c b/gpxe/src/hci/mucurses/slk.c new file mode 100644 index 0000000..600658e --- /dev/null +++ b/gpxe/src/hci/mucurses/slk.c @@ -0,0 +1,363 @@ +#include +#include +#include +#include +#include +#include "mucurses.h" +#include "cursor.h" + +/** @file + * + * Soft label key functions + */ + +#define MIN_SPACE_SIZE 2 + +#define SLK_MAX_LABEL_LEN 8 + +#define SLK_MAX_NUM_LABELS 12 + +#define SLK_MAX_NUM_SPACES 2 + +struct _softlabel { + // label string + char label[SLK_MAX_LABEL_LEN]; + /* Format of soft label + 0: left justify + 1: centre justify + 2: right justify + */ + unsigned int fmt; +}; + +struct _softlabelkeys { + struct _softlabel fkeys[SLK_MAX_NUM_LABELS]; + attr_t attrs; + /* Soft label layout format + 0: 3-2-3 + 1: 4-4 + 2: 4-4-4 + 3: 4-4-4 with index line + */ + unsigned int fmt; + unsigned int max_label_len; + unsigned int maj_space_len; + unsigned int num_labels; + unsigned int num_spaces; + unsigned int spaces[SLK_MAX_NUM_SPACES]; + struct cursor_pos saved_cursor; + attr_t saved_attrs; + short saved_pair; +}; + +static struct _softlabelkeys *slks; + +/* + I either need to break the primitives here, or write a collection of + functions specifically for SLKs that directly access the screen + functions - since this technically isn't part of stdscr, I think + this should be ok... + */ + +static void _enter_slk ( void ) { + _store_curs_pos ( stdscr, &slks->saved_cursor ); + wattr_get ( stdscr, &slks->saved_attrs, &slks->saved_pair, NULL ); + LINES++; + wmove ( stdscr, LINES, 0 ); + wattrset ( stdscr, slks->attrs ); +} + +static void _leave_slk ( void ) { + LINES--; + wattr_set ( stdscr, slks->saved_attrs, slks->saved_pair, NULL ); + _restore_curs_pos ( stdscr, &slks->saved_cursor ); +} + +static void _print_label ( struct _softlabel sl ) { + int space_ch; + char str[SLK_MAX_LABEL_LEN + 1]; + + assert ( slks->max_label_len <= SLK_MAX_LABEL_LEN ); + space_ch = ' '; + + // protect against gaps in the soft label keys array + if ( sl.label == NULL ) { + memset( str, space_ch, (size_t)(slks->max_label_len) ); + } else { + /* we need to pad the label with varying amounts of leading + pad depending on the format of the label */ + if ( sl.fmt == 1 ) { + memset( str, space_ch, + (size_t)(slks->max_label_len + - strlen(sl.label)) / 2 ); + } + if ( sl.fmt == 2 ) { + memset( str, space_ch, + (size_t)(slks->max_label_len + - strlen(sl.label)) ); + } + strcat(str,sl.label); + + // post-padding + memset(str+strlen(str), space_ch, + (size_t)(slks->max_label_len - strlen(str)) ); + } + + // print the formatted label + _wputstr ( stdscr, str, NOWRAP, slks->max_label_len ); +} + +/** + * Return the attribute used for the soft function keys + * + * @ret attrs the current attributes of the soft function keys + */ +attr_t slk_attr ( void ) { + return ( slks == NULL ? 0 : slks->attrs ); +} + +/** + * Turn off soft function key attributes + * + * @v attrs attribute bit mask + * @ret rc return status code + */ +int slk_attroff ( const chtype attrs ) { + if ( slks == NULL ) + return ERR; + slks->attrs &= ~( attrs & A_ATTRIBUTES ); + return OK; +} + +/** + * Turn on soft function key attributes + * + * @v attrs attribute bit mask + * @ret rc return status code + */ +int slk_attron ( const chtype attrs ) { + if ( slks == NULL ) + return ERR; + slks->attrs |= ( attrs & A_ATTRIBUTES ); + return OK; +} + +/** + * Set soft function key attributes + * + * @v attrs attribute bit mask + * @ret rc return status code + */ +int slk_attrset ( const chtype attrs ) { + if ( slks == NULL ) + return ERR; + slks->attrs = ( attrs & A_ATTRIBUTES ); + return OK; +} + +/** + * Turn off soft function key attributes + * + * @v attrs attribute bit mask + * @v *opts undefined (for future implementation) + * @ret rc return status code + */ +int slk_attr_off ( const attr_t attrs, void *opts __unused ) { + return slk_attroff( attrs ); +} + +/** + * Turn on soft function key attributes + * + * @v attrs attribute bit mask + * @v *opts undefined (for future implementation) + * @ret rc return status code + */ +int slk_attr_on ( attr_t attrs, void *opts __unused ) { + return slk_attron( attrs ); +} + +/** + * Set soft function key attributes + * + * @v attrs attribute bit mask + * @v colour_pair_number colour pair integer + * @v *opts undefined (for future implementation) + * @ret rc return status code + */ +int slk_attr_set ( const attr_t attrs, short colour_pair_number, + void *opts __unused ) { + if ( slks == NULL ) + return ERR; + + if ( ( unsigned short )colour_pair_number > COLORS ) + return ERR; + + slks->attrs = ( (unsigned short)colour_pair_number << CPAIR_SHIFT ) | + ( attrs & A_ATTRIBUTES ); + return OK; +} + +/** + * Clear the soft function key labels from the screen + * + * @ret rc return status code + */ +int slk_clear ( void ) { + if ( slks == NULL ) + return ERR; + + _enter_slk(); + wclrtoeol ( stdscr ); + _leave_slk(); + + return OK; +} + +/** + * Set soft label colour pair + */ +int slk_colour ( short colour_pair_number ) { + if ( slks == NULL ) + return ERR; + if ( ( unsigned short )colour_pair_number > COLORS ) + return ERR; + + slks->attrs = ( (unsigned short)colour_pair_number << CPAIR_SHIFT ) + | ( slks->attrs & A_ATTRIBUTES ); + + return OK; +} + +/** + * Initialise the soft function keys + * + * @v fmt format of keys + * @ret rc return status code + */ +int slk_init ( int fmt ) { + unsigned short nmaj, nmin, nblocks, available_width; + + if ( (unsigned)fmt > 3 ) { + return ERR; + } + + /* There seems to be no API call to free this data structure... */ + if ( ! slks ) + slks = calloc(1,sizeof(*slks)); + if ( ! slks ) + return ERR; + + slks->attrs = A_DEFAULT; + slks->fmt = fmt; + switch(fmt) { + case 0: + nblocks = 8; nmaj = 2; nmin = 5; + slks->spaces[0] = 2; slks->spaces[1] = 4; + break; + case 1: + nblocks = 8; nmaj = 1; nmin = 6; + slks->spaces[0] = 3; + break; + case 2: + // same allocations as format 3 + case 3: + nblocks = 12; nmaj = 2; nmin = 9; + slks->spaces[0] = 3; slks->spaces[1] = 7; + break; + default: + nblocks = 0; nmaj = 0; nmin = 0; + break; + } + + // determine maximum label length and major space size + available_width = COLS - ( ( MIN_SPACE_SIZE * nmaj ) + nmin ); + slks->max_label_len = available_width / nblocks; + slks->maj_space_len = MIN_SPACE_SIZE + + ( available_width % nblocks ) / nmaj; + slks->num_spaces = nmaj; + slks->num_labels = nblocks; + + // strip a line from the screen + LINES -= 1; + + return OK; +} + +/** + * Return the label for the specified soft key + * + * @v labnum soft key identifier + * @ret label return label + */ +char* slk_label ( int labnum ) { + if ( slks == NULL ) + return NULL; + + return slks->fkeys[labnum].label; +} + +/** + * Restore soft function key labels to the screen + * + * @ret rc return status code + */ +int slk_restore ( void ) { + unsigned int i, j, pos_x, + *next_space, *last_space; + chtype space_ch; + + if ( slks == NULL ) + return ERR; + + pos_x = 0; + + _enter_slk(); + + space_ch = (chtype)' ' | slks->attrs; + next_space = &(slks->spaces[0]); + last_space = &(slks->spaces[slks->num_spaces-1]); + + for ( i = 0; i < slks->num_labels ; i++ ) { + _print_label( slks->fkeys[i] ); + pos_x += slks->max_label_len; + + if ( i == *next_space ) { + for ( j = 0; j < slks->maj_space_len; j++, pos_x++ ) + _wputch ( stdscr, space_ch, NOWRAP ); + if ( next_space < last_space ) + next_space++; + } else { + if ( pos_x < COLS ) + _wputch ( stdscr, space_ch, NOWRAP ); + pos_x++; + } + } + + _leave_slk(); + + return OK; +} + +/** + * Configure specified soft key + * + * @v labnum soft label position to configure + * @v *label string to use as soft key label + * @v fmt justification format of label + * @ret rc return status code + */ +int slk_set ( int labnum, const char *label, int fmt ) { + if ( slks == NULL ) + return ERR; + if ( (unsigned short)labnum >= slks->num_labels ) + return ERR; + if ( (unsigned short)fmt >= 3 ) + return ERR; + + strncpy(slks->fkeys[labnum].label, label, + sizeof(slks->fkeys[labnum].label)); + slks->fkeys[labnum].fmt = fmt; + + return OK; +} diff --git a/gpxe/src/hci/mucurses/widgets/editbox.c b/gpxe/src/hci/mucurses/widgets/editbox.c new file mode 100644 index 0000000..a52089c --- /dev/null +++ b/gpxe/src/hci/mucurses/widgets/editbox.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include + +/** @file + * + * Editable text box widget + * + */ + +#define EDITBOX_MIN_CHARS 3 + +/** + * Initialise text box widget + * + * @v box Editable text box widget + * @v buf Text buffer + * @v len Size of text buffer + * @v win Containing window + * @v row Row + * @v col Starting column + * @v width Width + * @v flags Flags + */ +void init_editbox ( struct edit_box *box, char *buf, size_t len, + WINDOW *win, unsigned int row, unsigned int col, + unsigned int width, unsigned int flags ) { + memset ( box, 0, sizeof ( *box ) ); + box->string.buf = buf; + box->string.len = len; + box->string.cursor = strlen ( buf ); + box->win = ( win ? win : stdscr ); + box->row = row; + box->col = col; + box->width = width; + box->flags = flags; +} + +/** + * Draw text box widget + * + * @v box Editable text box widget + * + */ +void draw_editbox ( struct edit_box *box ) { + size_t width = box->width; + char buf[ width + 1 ]; + signed int cursor_offset, underflow, overflow, first; + size_t len; + + /* Adjust starting offset so that cursor remains within box */ + cursor_offset = ( box->string.cursor - box->first ); + underflow = ( EDITBOX_MIN_CHARS - cursor_offset ); + overflow = ( cursor_offset - ( width - 1 ) ); + first = box->first; + if ( underflow > 0 ) { + first -= underflow; + if ( first < 0 ) + first = 0; + } else if ( overflow > 0 ) { + first += overflow; + } + box->first = first; + cursor_offset = ( box->string.cursor - first ); + + /* Construct underscore-padded string portion */ + memset ( buf, '_', width ); + buf[width] = '\0'; + len = ( strlen ( box->string.buf ) - first ); + if ( len > width ) + len = width; + if ( box->flags & EDITBOX_STARS ) { + memset ( buf, '*', len ); + } else { + memcpy ( buf, ( box->string.buf + first ), len ); + } + + /* Print box content and move cursor */ + if ( ! box->win ) + box->win = stdscr; + mvwprintw ( box->win, box->row, box->col, "%s", buf ); + wmove ( box->win, box->row, ( box->col + cursor_offset ) ); +} diff --git a/gpxe/src/hci/mucurses/winattrs.c b/gpxe/src/hci/mucurses/winattrs.c new file mode 100644 index 0000000..15f9732 --- /dev/null +++ b/gpxe/src/hci/mucurses/winattrs.c @@ -0,0 +1,131 @@ +#include + +/** @file + * + * MuCurses window attribute functions + * + */ + +/** + * Get the background rendition attributes for a window + * + * @v *win subject window + * @ret ch chtype rendition representation + */ +inline chtype getbkgd ( WINDOW *win ) { + return win->attrs; +} + +/** + * Turn off attributes in a window + * + * @v win subject window + * @v attrs attributes to enable + * @ret rc return status code + */ +int wattroff ( WINDOW *win, int attrs ) { + win->attrs &= ~attrs; + return OK; +} + +/** + * Turn on attributes in a window + * + * @v win subject window + * @v attrs attributes to enable + * @ret rc return status code + */ +int wattron ( WINDOW *win, int attrs ) { + win->attrs |= attrs; + return OK; +} + +/** + * Set attributes in a window + * + * @v win subject window + * @v attrs attributes to enable + * @ret rc return status code + */ +int wattrset ( WINDOW *win, int attrs ) { + win->attrs = ( attrs | ( win->attrs & A_COLOR ) ); + return OK; +} + +/** + * Get attributes and colour pair information + * + * @v *win window to obtain information from + * @v *attrs address in which to store attributes + * @v *pair address in which to store colour pair + * @v *opts undefined (for future implementation) + * @ret rc return status cude + */ +int wattr_get ( WINDOW *win, attr_t *attrs, short *pair, + void *opts __unused ) { + *attrs = win->attrs & A_ATTRIBUTES; + *pair = PAIR_NUMBER ( win->attrs ); + return OK; +} + +/** + * Turn off attributes in a window + * + * @v *win subject window + * @v attrs attributes to toggle + * @v *opts undefined (for future implementation) + * @ret rc return status code + */ +int wattr_off ( WINDOW *win, attr_t attrs, + void *opts __unused ) { + wattroff( win, attrs ); + return OK; +} + +/** + * Turn on attributes in a window + * + * @v *win subject window + * @v attrs attributes to toggle + * @v *opts undefined (for future implementation) + * @ret rc return status code + */ +int wattr_on ( WINDOW *win, attr_t attrs, + void *opts __unused ) { + wattron( win, attrs ); + return OK; +} + +/** + * Set attributes and colour pair information in a window + * + * @v *win subject window + * @v attrs attributes to set + * @v cpair colour pair to set + * @v *opts undefined (for future implementation) + * @ret rc return status code + */ +int wattr_set ( WINDOW *win, attr_t attrs, short cpair, + void *opts __unused ) { + wattrset( win, attrs | COLOUR_PAIR ( cpair ) ); + return OK; +} + +/** + * Set colour pair for a window + * + * @v *win subject window + * @v colour_pair_number colour pair integer + * @v *opts undefined (for future implementation) + * @ret rc return status code + */ +int wcolour_set ( WINDOW *win, short colour_pair_number, + void *opts __unused ) { + if ( ( unsigned short )colour_pair_number > COLOUR_PAIRS ) + return ERR; + + win->attrs = ( ( win->attrs & A_ATTRIBUTES ) | + COLOUR_PAIR ( colour_pair_number ) ); + return OK; +} + diff --git a/gpxe/src/hci/mucurses/windows.c b/gpxe/src/hci/mucurses/windows.c new file mode 100644 index 0000000..63d0af0 --- /dev/null +++ b/gpxe/src/hci/mucurses/windows.c @@ -0,0 +1,158 @@ +#include +#include +#include +#include "mucurses.h" + +/** @file + * + * MuCurses windows instance functions + * + */ + +/** + * Delete a window + * + * @v *win pointer to window being deleted + * @ret rc return status code + */ +int delwin ( WINDOW *win ) { + if ( win == NULL ) + return ERR; + + /* I think we should blank the region covered by the window - + ncurses doesn't do this, but they have a buffer, so they + may just be deleting from an offscreen context whereas we + are guaranteed to be deleting something onscreen */ + wmove( win, 0, 0 ); + chtype killch = (chtype)' '; + do { + _wputch( win, killch, WRAP ); + } while ( win->curs_x + win->curs_y ); + + free( win ); + + wmove ( stdscr, 0, 0 ); + + return OK; +} + +/** + * Create a new derived window + * + * @v parent parent window + * @v nlines window height + * @v ncols window width + * @v begin_y window y origin (relative to parent) + * @v begin_x window x origin (relative to parent) + * @ret ptr return pointer to child window + */ +WINDOW *derwin ( WINDOW *parent, int nlines, int ncols, + int begin_y, int begin_x ) { + WINDOW *child; + if ( parent == NULL ) + return NULL; + if ( ( child = malloc( sizeof( WINDOW ) ) ) == NULL ) + return NULL; + if ( ( (unsigned)ncols > parent->width ) || + ( (unsigned)nlines > parent->height ) ) + return NULL; + child->ori_y = parent->ori_y + begin_y; + child->ori_x = parent->ori_x + begin_x; + child->height = nlines; + child->width = ncols; + child->parent = parent; + child->scr = parent->scr; + return child; +} + +/** + * Create a duplicate of the specified window + * + * @v orig original window + * @ret ptr pointer to duplicate window + */ +WINDOW *dupwin ( WINDOW *orig ) { + WINDOW *copy; + if ( orig == NULL ) + return NULL; + if ( ( copy = malloc( sizeof( WINDOW ) ) ) == NULL ) + return NULL; + copy->scr = orig->scr; + copy->attrs = orig->attrs; + copy->ori_y = orig->ori_y; + copy->ori_x = orig->ori_x; + copy->curs_y = orig->curs_y; + copy->curs_x = orig->curs_x; + copy->height = orig->height; + copy->width = orig->width; + return copy; +} + +/** + * Move window origin to specified coordinates + * + * @v *win window to move + * @v y Y position + * @v x X position + * @ret rc return status code + */ +int mvwin ( WINDOW *win, int y, int x ) { + if ( win == NULL ) + return ERR; + if ( ( ( (unsigned)y + win->height ) > LINES ) || + ( ( (unsigned)x + win->width ) > COLS ) ) + return ERR; + + win->ori_y = y; + win->ori_x = x; + + return OK; +} + +/** + * Create new WINDOW + * + * @v nlines number of lines + * @v ncols number of columns + * @v begin_y column origin + * @v begin_x line origin + * @ret *win return pointer to new window + */ +WINDOW *newwin ( int nlines, int ncols, int begin_y, int begin_x ) { + WINDOW *win; + if ( ( win = malloc( sizeof(WINDOW) ) ) == NULL ) + return NULL; + if ( ( (unsigned)( begin_y + nlines ) > stdscr->height ) && + ( (unsigned)( begin_x + ncols ) > stdscr->width ) ) + return NULL; + win->ori_y = begin_y; + win->ori_x = begin_x; + win->height = nlines; + win->width = ncols; + win->scr = stdscr->scr; + win->parent = stdscr; + return win; +} + +/** + * Create a new sub-window + * + * @v orig parent window + * @v nlines window height + * @v ncols window width + * @v begin_y window y origin (absolute) + * @v begin_x window x origin (absolute) + * @ret ptr return pointer to child window + */ +WINDOW *subwin ( WINDOW *parent, int nlines, int ncols, + int begin_y, int begin_x ) { + WINDOW *child; + if ( parent == NULL ) + return NULL; + if ( ( child = malloc( sizeof( WINDOW ) ) ) == NULL ) + return NULL; + child = newwin( nlines, ncols, begin_y, begin_x ); + child->parent = parent; + child->scr = parent->scr; + return child; +} diff --git a/gpxe/src/hci/mucurses/wininit.c b/gpxe/src/hci/mucurses/wininit.c new file mode 100644 index 0000000..cd27f9f --- /dev/null +++ b/gpxe/src/hci/mucurses/wininit.c @@ -0,0 +1,35 @@ +#include +#include + +/** @file + * + * MuCurses initialisation functions + * + */ + +/** + * Initialise console environment + * + * @ret *win return pointer to stdscr + */ +WINDOW *initscr ( void ) { + /* determine console size */ + /* initialise screen */ + stdscr->scr->init( stdscr->scr ); + stdscr->height = LINES; + stdscr->width = COLS; + move ( 0, 0 ); + return stdscr; +} + +/** + * Finalise console environment + * + */ +int endwin ( void ) { + attrset ( 0 ); + color_set ( 0, NULL ); + mvprintw ( ( LINES - 1 ), 0, "\n" ); + stdscr->scr->exit( stdscr->scr ); + return OK; +} diff --git a/gpxe/src/hci/readline.c b/gpxe/src/hci/readline.c new file mode 100644 index 0000000..ff7a767 --- /dev/null +++ b/gpxe/src/hci/readline.c @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Minimal readline + * + */ + +#define READLINE_MAX 256 + +static void sync_console ( struct edit_string *string ) __nonnull; + +/** + * Synchronise console with edited string + * + * @v string Editable string + */ +static void sync_console ( struct edit_string *string ) { + unsigned int mod_start = string->mod_start; + unsigned int mod_end = string->mod_end; + unsigned int cursor = string->last_cursor; + size_t len = strlen ( string->buf ); + + /* Expand region back to old cursor position if applicable */ + if ( mod_start > string->last_cursor ) + mod_start = string->last_cursor; + + /* Expand region forward to new cursor position if applicable */ + if ( mod_end < string->cursor ) + mod_end = string->cursor; + + /* Backspace to start of region */ + while ( cursor > mod_start ) { + putchar ( '\b' ); + cursor--; + } + + /* Print modified region */ + while ( cursor < mod_end ) { + putchar ( ( cursor >= len ) ? ' ' : string->buf[cursor] ); + cursor++; + } + + /* Backspace to new cursor position */ + while ( cursor > string->cursor ) { + putchar ( '\b' ); + cursor--; + } +} + +/** + * Read line from console + * + * @v prompt Prompt string + * @ret line Line read from console (excluding terminating newline) + * + * The returned line is allocated with malloc(); the caller must + * eventually call free() to release the storage. + */ +char * readline ( const char *prompt ) { + char buf[READLINE_MAX]; + struct edit_string string; + int key; + char *line; + + if ( prompt ) + printf ( "%s", prompt ); + + memset ( &string, 0, sizeof ( string ) ); + string.buf = buf; + string.len = sizeof ( buf ); + buf[0] = '\0'; + + while ( 1 ) { + key = edit_string ( &string, getkey() ); + sync_console ( &string ); + switch ( key ) { + case CR: + case LF: + putchar ( '\n' ); + line = strdup ( buf ); + if ( ! line ) + printf ( "Out of memory\n" ); + return line; + case CTRL_C: + putchar ( '\n' ); + return NULL; + default: + /* Do nothing */ + break; + } + } +} diff --git a/gpxe/src/hci/shell.c b/gpxe/src/hci/shell.c new file mode 100644 index 0000000..18b1068 --- /dev/null +++ b/gpxe/src/hci/shell.c @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include + +/** @file + * + * Minimal command shell + * + */ + +static struct command commands[0] + __table_start ( struct command, commands ); +static struct command commands_end[0] + __table_end ( struct command, commands ); + +/** The shell prompt string */ +static const char shell_prompt[] = "gPXE> "; + +/** Flag set in order to exit shell */ +static int exit_flag = 0; + +/** "exit" command body */ +static int exit_exec ( int argc, char **argv __unused ) { + + if ( argc == 1 ) { + exit_flag = 1; + } else { + printf ( "Usage: exit\n" + "Exits the command shell\n" ); + } + + return 0; +} + +/** "exit" command definition */ +struct command exit_command __command = { + .name = "exit", + .exec = exit_exec, +}; + +/** "help" command body */ +static int help_exec ( int argc __unused, char **argv __unused ) { + struct command *command; + unsigned int hpos = 0; + + printf ( "\nAvailable commands:\n\n" ); + for ( command = commands ; command < commands_end ; command++ ) { + hpos += printf ( " %s", command->name ); + if ( hpos > ( 16 * 4 ) ) { + printf ( "\n" ); + hpos = 0; + } else { + while ( hpos % 16 ) { + printf ( " " ); + hpos++; + } + } + } + printf ( "\n\nType \" --help\" for further information\n\n" ); + return 0; +} + +/** "help" command definition */ +struct command help_command __command = { + .name = "help", + .exec = help_exec, +}; + +/** + * Start command shell + * + */ +void shell ( void ) { + char *line; + + exit_flag = 0; + while ( ! exit_flag ) { + line = readline ( shell_prompt ); + if ( line ) { + system ( line ); + free ( line ); + } + } +} diff --git a/gpxe/src/hci/shell_banner.c b/gpxe/src/hci/shell_banner.c new file mode 100644 index 0000000..3271c48 --- /dev/null +++ b/gpxe/src/hci/shell_banner.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include + +/** @file + * + * Shell startup banner + * + */ + +/** + * Print shell banner and prompt for shell entry + * + * @ret enter_shell User wants to enter shell + */ +int shell_banner ( void ) { + int enter_shell = 0; + int wait_count; + int key; + + printf ( "\nPress Ctrl-B for the gPXE command line..." ); + + /* Wait for key */ + for ( wait_count = 0 ; wait_count < BANNER_TIMEOUT ; wait_count++ ) { + if ( iskey() ) { + key = getchar(); + if ( key == CTRL_B ) + enter_shell = 1; + break; + } + mdelay(100); + } + + /* Clear the "Press Ctrl-B" line */ + printf ( "\r \r" ); + + return enter_shell; +} diff --git a/gpxe/src/hci/strerror.c b/gpxe/src/hci/strerror.c new file mode 100644 index 0000000..74995e8 --- /dev/null +++ b/gpxe/src/hci/strerror.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include + +/** @file + * + * Error descriptions. + * + * The error numbers used by Etherboot are a superset of those defined + * by the PXE specification version 2.1. See errno.h for a listing of + * the error values. + * + * To save space in ROM images, error string tables are optional. Use + * the ERRORMSG_XXX options in config.h to select which error string + * tables you want to include. If an error string table is omitted, + * strerror() will simply return the text "Error 0x". + * + */ + +static struct errortab errortab_start[0] + __table_start ( struct errortab, errortab ); +static struct errortab errortab_end[0] + __table_end ( struct errortab, errortab ); + +/** + * Find error description + * + * @v errno Error number + * @v mask Mask of bits that we care about + * @ret errortab Error description, or NULL + */ +static struct errortab * find_error ( int errno, int mask ) { + struct errortab *errortab; + + for ( errortab = errortab_start ; errortab < errortab_end ; + errortab++ ) { + if ( ( ( errortab->errno ^ errno ) & mask ) == 0 ) + return errortab; + } + + return NULL; +} + +/** + * Find closest error description + * + * @v errno Error number + * @ret errortab Error description, or NULL + * + * + */ +static struct errortab * find_closest_error ( int errno ) { + struct errortab *errortab; + + /* First, look for an exact match */ + if ( ( errortab = find_error ( errno, 0x7fffffff ) ) != NULL ) + return errortab; + + /* Second, try masking off the gPXE-specific bit and seeing if + * we have an entry for the generic POSIX error message. + */ + if ( ( errortab = find_error ( errno, 0x4f0000ff ) ) != NULL ) + return errortab; + + return NULL; +} + +/** + * Retrieve string representation of error number. + * + * @v errno/rc Error number or return status code + * @ret strerror Pointer to error text + * + * If the error is not found in the linked-in error tables, generates + * a generic "Error 0x" message. + * + * The pointer returned by strerror() is valid only until the next + * call to strerror(). + * + */ +const char * strerror ( int errno ) { + static char errbuf[64]; + struct errortab *errortab; + + /* Allow for strerror(rc) as well as strerror(errno) */ + if ( errno < 0 ) + errno = -errno; + + /* Find the error description, if one exists */ + errortab = find_closest_error ( errno ); + + /* Construct the error message */ + if ( errortab ) { + snprintf ( errbuf, sizeof ( errbuf ), "%s (%#08x)", + errortab->text, errno ); + } else { + snprintf ( errbuf, sizeof ( errbuf ), "Error %#08x", errno ); + } + + return errbuf; +} + +/* Do not include ERRFILE portion in the numbers in the error table */ +#undef ERRFILE +#define ERRFILE 0 + +/** The most common errors */ +struct errortab common_errors[] __errortab = { + { 0, "No error" }, + { EACCES, "Permission denied" }, + { ECANCELED, "Operation cancelled" }, + { ECONNRESET, "Connection reset" }, + { EINVAL, "Invalid argument" }, + { EIO, "Input/output error" }, + { ENETUNREACH, "Network unreachable" }, + { ENODEV, "No such device" }, + { ENOENT, "File not found" }, + { ENOEXEC, "Not an executable image" }, + { ENOMEM, "Out of memory" }, + { ENOSPC, "No space left on device" }, + { ENOTSUP, "Not supported" }, + { EPERM, "Operation not permitted" }, + { ETIMEDOUT, "Connection timed out" }, +}; diff --git a/gpxe/src/hci/tui/login_ui.c b/gpxe/src/hci/tui/login_ui.c new file mode 100644 index 0000000..c14a2c0 --- /dev/null +++ b/gpxe/src/hci/tui/login_ui.c @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2009 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. + */ + +/** @file + * + * Login UI + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Colour pairs */ +#define CPAIR_NORMAL 1 +#define CPAIR_LABEL 2 +#define CPAIR_EDITBOX 3 + +/* Screen layout */ +#define USERNAME_LABEL_ROW 8 +#define USERNAME_ROW 10 +#define PASSWORD_LABEL_ROW 14 +#define PASSWORD_ROW 16 +#define LABEL_COL 36 +#define EDITBOX_COL 30 +#define EDITBOX_WIDTH 20 + +int login_ui ( void ) { + char username[64]; + char password[64]; + struct edit_box username_box; + struct edit_box password_box; + struct edit_box *current_box = &username_box; + int key; + int rc = -EINPROGRESS; + + /* Fetch current setting values */ + fetch_string_setting ( NULL, &username_setting, username, + sizeof ( username ) ); + fetch_string_setting ( NULL, &password_setting, password, + sizeof ( password ) ); + + /* Initialise UI */ + initscr(); + start_color(); + init_pair ( CPAIR_NORMAL, COLOR_WHITE, COLOR_BLACK ); + init_pair ( CPAIR_LABEL, COLOR_WHITE, COLOR_BLACK ); + init_pair ( CPAIR_EDITBOX, COLOR_WHITE, COLOR_BLUE ); + init_editbox ( &username_box, username, sizeof ( username ), NULL, + USERNAME_ROW, EDITBOX_COL, EDITBOX_WIDTH, 0 ); + init_editbox ( &password_box, password, sizeof ( password ), NULL, + PASSWORD_ROW, EDITBOX_COL, EDITBOX_WIDTH, + EDITBOX_STARS ); + + /* Draw initial UI */ + erase(); + color_set ( CPAIR_LABEL, NULL ); + mvprintw ( USERNAME_LABEL_ROW, LABEL_COL, "Username:" ); + mvprintw ( PASSWORD_LABEL_ROW, LABEL_COL, "Password:" ); + color_set ( CPAIR_EDITBOX, NULL ); + draw_editbox ( &username_box ); + draw_editbox ( &password_box ); + + /* Main loop */ + while ( rc == -EINPROGRESS ) { + + draw_editbox ( current_box ); + + key = getkey(); + switch ( key ) { + case KEY_DOWN: + current_box = &password_box; + break; + case KEY_UP: + current_box = &username_box; + break; + case TAB: + current_box = ( ( current_box == &username_box ) ? + &password_box : &username_box ); + break; + case KEY_ENTER: + if ( current_box == &username_box ) { + current_box = &password_box; + } else { + rc = 0; + } + break; + case CTRL_C: + case ESC: + rc = -ECANCELED; + break; + default: + edit_editbox ( current_box, key ); + break; + } + } + + /* Terminate UI */ + color_set ( CPAIR_NORMAL, NULL ); + erase(); + endwin(); + + if ( rc != 0 ) + return rc; + + /* Store settings */ + if ( ( rc = store_setting ( NULL, &username_setting, username, + strlen ( username ) ) ) != 0 ) + return rc; + if ( ( rc = store_setting ( NULL, &password_setting, password, + strlen ( password ) ) ) != 0 ) + return rc; + + return 0; +} diff --git a/gpxe/src/hci/tui/settings_ui.c b/gpxe/src/hci/tui/settings_ui.c new file mode 100644 index 0000000..4ab3827 --- /dev/null +++ b/gpxe/src/hci/tui/settings_ui.c @@ -0,0 +1,427 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Option configuration console + * + */ + +/* Colour pairs */ +#define CPAIR_NORMAL 1 +#define CPAIR_SELECT 2 +#define CPAIR_EDIT 3 +#define CPAIR_ALERT 4 + +/* Screen layout */ +#define TITLE_ROW 1 +#define SETTINGS_LIST_ROW 3 +#define SETTINGS_LIST_COL 1 +#define INFO_ROW 20 +#define ALERT_ROW 20 +#define INSTRUCTION_ROW 22 +#define INSTRUCTION_PAD " " + +/** Layout of text within a setting widget */ +struct setting_row { + char start[0]; + char pad1[1]; + char name[15]; + char pad2[1]; + char value[60]; + char pad3[1]; + char nul; +} __attribute__ (( packed )); + +/** A setting widget */ +struct setting_widget { + /** Settings block */ + struct settings *settings; + /** Configuration setting */ + struct setting *setting; + /** Screen row */ + unsigned int row; + /** Screen column */ + unsigned int col; + /** Edit box widget used for editing setting */ + struct edit_box editbox; + /** Editing in progress flag */ + int editing; + /** Buffer for setting's value */ + char value[256]; /* enough size for a DHCP string */ +}; + +/** Registered configuration settings */ +static struct setting all_settings[0] + __table_start ( struct setting, settings ); +static struct setting all_settings_end[0] + __table_end ( struct setting, settings ); +#define NUM_SETTINGS ( ( unsigned ) ( all_settings_end - all_settings ) ) + +static void load_setting ( struct setting_widget *widget ) __nonnull; +static int save_setting ( struct setting_widget *widget ) __nonnull; +static void init_setting ( struct setting_widget *widget, + struct settings *settings, + struct setting *setting, + unsigned int row, unsigned int col ) __nonnull; +static void draw_setting ( struct setting_widget *widget ) __nonnull; +static int edit_setting ( struct setting_widget *widget, int key ) __nonnull; +static void init_setting_index ( struct setting_widget *widget, + struct settings *settings, + unsigned int index ) __nonnull; +static void vmsg ( unsigned int row, const char *fmt, va_list args ) __nonnull; +static void msg ( unsigned int row, const char *fmt, ... ) __nonnull; +static void valert ( const char *fmt, va_list args ) __nonnull; +static void alert ( const char *fmt, ... ) __nonnull; +static void draw_info_row ( struct setting *setting ) __nonnull; +static int main_loop ( struct settings *settings ) __nonnull; + +/** + * Load setting widget value from configuration settings + * + * @v widget Setting widget + * + */ +static void load_setting ( struct setting_widget *widget ) { + + /* Mark as not editing */ + widget->editing = 0; + + /* Read current setting value */ + if ( fetchf_setting ( widget->settings, widget->setting, + widget->value, sizeof ( widget->value ) ) < 0 ) { + widget->value[0] = '\0'; + } + + /* Initialise edit box */ + init_editbox ( &widget->editbox, widget->value, + sizeof ( widget->value ), NULL, widget->row, + ( widget->col + offsetof ( struct setting_row, value )), + sizeof ( ( ( struct setting_row * ) NULL )->value ), 0); +} + +/** + * Save setting widget value back to configuration settings + * + * @v widget Setting widget + */ +static int save_setting ( struct setting_widget *widget ) { + return storef_setting ( widget->settings, widget->setting, + widget->value ); +} + +/** + * Initialise setting widget + * + * @v widget Setting widget + * @v settings Settings block + * @v setting Configuration setting + * @v row Screen row + * @v col Screen column + */ +static void init_setting ( struct setting_widget *widget, + struct settings *settings, + struct setting *setting, + unsigned int row, unsigned int col ) { + + /* Initialise widget structure */ + memset ( widget, 0, sizeof ( *widget ) ); + widget->settings = settings; + widget->setting = setting; + widget->row = row; + widget->col = col; + + /* Read current setting value */ + load_setting ( widget ); +} + +/** + * Draw setting widget + * + * @v widget Setting widget + */ +static void draw_setting ( struct setting_widget *widget ) { + struct setting_row row; + unsigned int len; + unsigned int curs_col; + char *value; + + /* Fill row with spaces */ + memset ( &row, ' ', sizeof ( row ) ); + row.nul = '\0'; + + /* Construct dot-padded name */ + memset ( row.name, '.', sizeof ( row.name ) ); + len = strlen ( widget->setting->name ); + if ( len > sizeof ( row.name ) ) + len = sizeof ( row.name ); + memcpy ( row.name, widget->setting->name, len ); + + /* Construct space-padded value */ + value = widget->value; + if ( ! *value ) + value = ""; + len = strlen ( value ); + if ( len > sizeof ( row.value ) ) + len = sizeof ( row.value ); + memcpy ( row.value, value, len ); + curs_col = ( widget->col + offsetof ( typeof ( row ), value ) + + len ); + + /* Print row */ + mvprintw ( widget->row, widget->col, "%s", row.start ); + move ( widget->row, curs_col ); + if ( widget->editing ) + draw_editbox ( &widget->editbox ); +} + +/** + * Edit setting widget + * + * @v widget Setting widget + * @v key Key pressed by user + * @ret key Key returned to application, or zero + */ +static int edit_setting ( struct setting_widget *widget, int key ) { + widget->editing = 1; + return edit_editbox ( &widget->editbox, key ); +} + +/** + * Initialise setting widget by index + * + * @v widget Setting widget + * @v settings Settings block + * @v index Index of setting with settings list + */ +static void init_setting_index ( struct setting_widget *widget, + struct settings *settings, + unsigned int index ) { + init_setting ( widget, settings, &all_settings[index], + ( SETTINGS_LIST_ROW + index ), SETTINGS_LIST_COL ); +} + +/** + * Print message centred on specified row + * + * @v row Row + * @v fmt printf() format string + * @v args printf() argument list + */ +static void vmsg ( unsigned int row, const char *fmt, va_list args ) { + char buf[COLS]; + size_t len; + + len = vsnprintf ( buf, sizeof ( buf ), fmt, args ); + mvprintw ( row, ( ( COLS - len ) / 2 ), "%s", buf ); +} + +/** + * Print message centred on specified row + * + * @v row Row + * @v fmt printf() format string + * @v .. printf() arguments + */ +static void msg ( unsigned int row, const char *fmt, ... ) { + va_list args; + + va_start ( args, fmt ); + vmsg ( row, fmt, args ); + va_end ( args ); +} + +/** + * Clear message on specified row + * + * @v row Row + */ +static void clearmsg ( unsigned int row ) { + move ( row, 0 ); + clrtoeol(); +} + +/** + * Print alert message + * + * @v fmt printf() format string + * @v args printf() argument list + */ +static void valert ( const char *fmt, va_list args ) { + clearmsg ( ALERT_ROW ); + color_set ( CPAIR_ALERT, NULL ); + vmsg ( ALERT_ROW, fmt, args ); + sleep ( 2 ); + color_set ( CPAIR_NORMAL, NULL ); + clearmsg ( ALERT_ROW ); +} + +/** + * Print alert message + * + * @v fmt printf() format string + * @v ... printf() arguments + */ +static void alert ( const char *fmt, ... ) { + va_list args; + + va_start ( args, fmt ); + valert ( fmt, args ); + va_end ( args ); +} + +/** + * Draw title row + */ +static void draw_title_row ( void ) { + attron ( A_BOLD ); + msg ( TITLE_ROW, "gPXE option configuration console" ); + attroff ( A_BOLD ); +} + +/** + * Draw information row + * + * @v setting Current configuration setting + */ +static void draw_info_row ( struct setting *setting ) { + clearmsg ( INFO_ROW ); + attron ( A_BOLD ); + msg ( INFO_ROW, "%s - %s", setting->name, setting->description ); + attroff ( A_BOLD ); +} + +/** + * Draw instruction row + * + * @v editing Editing in progress flag + */ +static void draw_instruction_row ( int editing ) { + clearmsg ( INSTRUCTION_ROW ); + if ( editing ) { + msg ( INSTRUCTION_ROW, + "Enter - accept changes" INSTRUCTION_PAD + "Ctrl-C - discard changes" ); + } else { + msg ( INSTRUCTION_ROW, + "Ctrl-X - exit configuration utility" ); + } +} + +static int main_loop ( struct settings *settings ) { + struct setting_widget widget; + unsigned int current = 0; + unsigned int next; + int i; + int key; + int rc; + + /* Print initial screen content */ + draw_title_row(); + color_set ( CPAIR_NORMAL, NULL ); + for ( i = ( NUM_SETTINGS - 1 ) ; i >= 0 ; i-- ) { + init_setting_index ( &widget, settings, i ); + draw_setting ( &widget ); + } + + while ( 1 ) { + /* Redraw information and instruction rows */ + draw_info_row ( widget.setting ); + draw_instruction_row ( widget.editing ); + + /* Redraw current setting */ + color_set ( ( widget.editing ? CPAIR_EDIT : CPAIR_SELECT ), + NULL ); + draw_setting ( &widget ); + color_set ( CPAIR_NORMAL, NULL ); + + key = getkey(); + if ( widget.editing ) { + key = edit_setting ( &widget, key ); + switch ( key ) { + case CR: + case LF: + if ( ( rc = save_setting ( &widget ) ) != 0 ) { + alert ( " Could not set %s: %s ", + widget.setting->name, + strerror ( rc ) ); + } + /* Fall through */ + case CTRL_C: + load_setting ( &widget ); + break; + default: + /* Do nothing */ + break; + } + } else { + next = current; + switch ( key ) { + case KEY_DOWN: + if ( next < ( NUM_SETTINGS - 1 ) ) + next++; + break; + case KEY_UP: + if ( next > 0 ) + next--; + break; + case CTRL_X: + return 0; + default: + edit_setting ( &widget, key ); + break; + } + if ( next != current ) { + draw_setting ( &widget ); + init_setting_index ( &widget, settings, next ); + current = next; + } + } + } + +} + +int settings_ui ( struct settings *settings ) { + int rc; + + initscr(); + start_color(); + init_pair ( CPAIR_NORMAL, COLOR_WHITE, COLOR_BLUE ); + init_pair ( CPAIR_SELECT, COLOR_WHITE, COLOR_RED ); + init_pair ( CPAIR_EDIT, COLOR_BLACK, COLOR_CYAN ); + init_pair ( CPAIR_ALERT, COLOR_WHITE, COLOR_RED ); + color_set ( CPAIR_NORMAL, NULL ); + erase(); + + rc = main_loop ( settings ); + + endwin(); + + return rc; +} diff --git a/gpxe/src/image/efi_image.c b/gpxe/src/image/efi_image.c new file mode 100644 index 0000000..ae95deb --- /dev/null +++ b/gpxe/src/image/efi_image.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include +#include + +FEATURE ( FEATURE_IMAGE, "EFI", DHCP_EB_FEATURE_EFI, 1 ); + +struct image_type efi_image_type __image_type ( PROBE_NORMAL ); + +/** + * Execute EFI image + * + * @v image EFI image + * @ret rc Return status code + */ +static int efi_image_exec ( struct image *image ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE handle; + UINTN exit_data_size; + CHAR16 *exit_data; + EFI_STATUS efirc; + + /* Attempt loading image */ + if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, NULL, + user_to_virt ( image->data, 0 ), + image->len, &handle ) ) != 0 ) { + /* Not an EFI image */ + DBGC ( image, "EFIIMAGE %p could not load: %s\n", + image, efi_strerror ( efirc ) ); + return -ENOEXEC; + } + + /* Start the image */ + if ( ( efirc = bs->StartImage ( handle, &exit_data_size, + &exit_data ) ) != 0 ) { + DBGC ( image, "EFIIMAGE %p returned with status %s\n", + image, efi_strerror ( efirc ) ); + goto done; + } + + done: + /* Unload the image. We can't leave it loaded, because we + * have no "unload" operation. + */ + bs->UnloadImage ( handle ); + + return EFIRC_TO_RC ( efirc ); +} + +/** + * Load EFI image into memory + * + * @v image EFI file + * @ret rc Return status code + */ +static int efi_image_load ( struct image *image ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE handle; + EFI_STATUS efirc; + + /* Attempt loading image */ + if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, NULL, + user_to_virt ( image->data, 0 ), + image->len, &handle ) ) != 0 ) { + /* Not an EFI image */ + DBGC ( image, "EFIIMAGE %p could not load: %s\n", + image, efi_strerror ( efirc ) ); + return -ENOEXEC; + } + + /* This is an EFI image */ + if ( ! image->type ) + image->type = &efi_image_type; + + /* Unload the image. We can't leave it loaded, because we + * have no "unload" operation. + */ + bs->UnloadImage ( handle ); + + return 0; +} + +/** EFI image type */ +struct image_type efi_image_type __image_type ( PROBE_NORMAL ) = { + .name = "EFI", + .load = efi_image_load, + .exec = efi_image_exec, +}; diff --git a/gpxe/src/image/elf.c b/gpxe/src/image/elf.c new file mode 100644 index 0000000..cb2b0f5 --- /dev/null +++ b/gpxe/src/image/elf.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2007 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. + */ + +/** + * @file + * + * ELF image format + * + * A "pure" ELF image is not a bootable image. There are various + * bootable formats based upon ELF (e.g. Multiboot), which share + * common ELF-related functionality. + */ + +#include +#include +#include +#include +#include +#include + +typedef Elf32_Ehdr Elf_Ehdr; +typedef Elf32_Phdr Elf_Phdr; +typedef Elf32_Off Elf_Off; + +/** + * Load ELF segment into memory + * + * @v image ELF file + * @v phdr ELF program header + * @ret rc Return status code + */ +static int elf_load_segment ( struct image *image, Elf_Phdr *phdr ) { + physaddr_t dest; + userptr_t buffer; + int rc; + + /* Do nothing for non-PT_LOAD segments */ + if ( phdr->p_type != PT_LOAD ) + return 0; + + /* Check segment lies within image */ + if ( ( phdr->p_offset + phdr->p_filesz ) > image->len ) { + DBG ( "ELF segment outside ELF file\n" ); + return -ENOEXEC; + } + + /* Find start address: use physical address for preference, + * fall back to virtual address if no physical address + * supplied. + */ + dest = phdr->p_paddr; + if ( ! dest ) + dest = phdr->p_vaddr; + if ( ! dest ) { + DBG ( "ELF segment loads to physical address 0\n" ); + return -ENOEXEC; + } + buffer = phys_to_user ( dest ); + + DBG ( "ELF loading segment [%x,%x) to [%x,%x,%x)\n", + phdr->p_offset, ( phdr->p_offset + phdr->p_filesz ), + phdr->p_paddr, ( phdr->p_paddr + phdr->p_filesz ), + ( phdr->p_paddr + phdr->p_memsz ) ); + + /* Verify and prepare segment */ + if ( ( rc = prep_segment ( buffer, phdr->p_filesz, + phdr->p_memsz ) ) != 0 ) { + DBG ( "ELF could not prepare segment: %s\n", strerror ( rc ) ); + return rc; + } + + /* Copy image to segment */ + memcpy_user ( buffer, 0, image->data, phdr->p_offset, phdr->p_filesz ); + + return 0; +} + +/** + * Load ELF image into memory + * + * @v image ELF file + * @ret rc Return status code + */ +int elf_load ( struct image *image ) { + Elf_Ehdr ehdr; + Elf_Phdr phdr; + Elf_Off phoff; + unsigned int phnum; + int rc; + + /* Image type must already have been set by caller */ + assert ( image->type != NULL ); + + /* Read ELF header */ + copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) ); + if ( memcmp ( &ehdr.e_ident[EI_MAG0], ELFMAG, SELFMAG ) != 0 ) { + DBG ( "Invalid ELF signature\n" ); + return -ENOEXEC; + } + + /* Read ELF program headers */ + for ( phoff = ehdr.e_phoff , phnum = ehdr.e_phnum ; phnum ; + phoff += ehdr.e_phentsize, phnum-- ) { + if ( phoff > image->len ) { + DBG ( "ELF program header %d outside ELF image\n", + phnum ); + return -ENOEXEC; + } + copy_from_user ( &phdr, image->data, phoff, sizeof ( phdr ) ); + if ( ( rc = elf_load_segment ( image, &phdr ) ) != 0 ) + return rc; + } + + /* Record execution entry point in image private data field */ + image->priv.phys = ehdr.e_entry; + + return 0; +} diff --git a/gpxe/src/image/embedded.c b/gpxe/src/image/embedded.c new file mode 100644 index 0000000..4f76357 --- /dev/null +++ b/gpxe/src/image/embedded.c @@ -0,0 +1,99 @@ +/** @file + * + * Embedded image support + * + * Embedded images are images built into the gPXE binary and do not require + * fetching over the network. + */ + +#include +#include +#include +#include + +/** + * Free embedded image + * + * @v refcnt Reference counter + */ +static void __attribute__ (( unused )) +embedded_image_free ( struct refcnt *refcnt __unused ) { + /* Do nothing */ +} + +/* Raw image data for all embedded images */ +#undef EMBED +#define EMBED( _index, _path, _name ) \ + extern char embedded_image_ ## _index ## _data[]; \ + extern char embedded_image_ ## _index ## _len[]; \ + __asm__ ( ".section \".rodata\", \"a\", @progbits\n\t" \ + "\nembedded_image_" #_index "_data:\n\t" \ + ".incbin \"" _path "\"\n\t" \ + "\nembedded_image_" #_index "_end:\n\t" \ + ".equ embedded_image_" #_index "_len, " \ + "( embedded_image_" #_index "_end - " \ + " embedded_image_" #_index "_data )\n\t" \ + ".previous\n\t" ); +EMBED_ALL + +/* Image structures for all embedded images */ +#undef EMBED +#define EMBED( _index, _path, _name ) { \ + .refcnt = { .free = embedded_image_free, }, \ + .name = _name, \ + .data = ( userptr_t ) ( embedded_image_ ## _index ## _data ), \ + .len = ( size_t ) embedded_image_ ## _index ## _len, \ +}, +static struct image embedded_images[] = { + EMBED_ALL +}; + +/** + * Register all embedded images + */ +static void embedded_init ( void ) { + int i; + struct image *image; + void *data; + int rc; + + /* Skip if we have no embedded images */ + if ( ! sizeof ( embedded_images ) ) + return; + + /* Fix up data pointers and register images */ + for ( i = 0 ; i < ( int ) ( sizeof ( embedded_images ) / + sizeof ( embedded_images[0] ) ) ; i++ ) { + image = &embedded_images[i]; + + /* virt_to_user() cannot be used in a static + * initialiser, so we cast the pointer to a userptr_t + * in the initialiser and fix it up here. (This will + * actually be a no-op on most platforms.) + */ + data = ( ( void * ) image->data ); + image->data = virt_to_user ( data ); + + DBG ( "Embedded image \"%s\": %zd bytes at %p\n", + image->name, image->len, data ); + + if ( ( rc = register_image ( image ) ) != 0 ) { + DBG ( "Could not register embedded image \"%s\": " + "%s\n", image->name, strerror ( rc ) ); + return; + } + } + + /* Load the first image */ + image = &embedded_images[0]; + if ( ( rc = image_autoload ( image ) ) != 0 ) { + DBG ( "Could not load embedded image \"%s\": %s\n", + image->name, strerror ( rc ) ); + return; + } +} + +/** Embedded image initialisation function */ +struct init_fn embedded_init_fn __init_fn ( INIT_NORMAL ) = { + .initialise = embedded_init, +}; diff --git a/gpxe/src/image/script.c b/gpxe/src/image/script.c new file mode 100644 index 0000000..2d24274 --- /dev/null +++ b/gpxe/src/image/script.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2007 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. + */ + +/** + * @file + * + * gPXE scripts + * + */ + +#include +#include +#include +#include + +struct image_type script_image_type __image_type ( PROBE_NORMAL ); + +/** + * Execute script + * + * @v image Script + * @ret rc Return status code + */ +static int script_exec ( struct image *image ) { + size_t offset = 0; + off_t eol; + size_t len; + int rc; + + /* Temporarily de-register image, so that a "boot" command + * doesn't throw us into an execution loop. + */ + unregister_image ( image ); + + while ( offset < image->len ) { + + /* Find length of next line, excluding any terminating '\n' */ + eol = memchr_user ( image->data, offset, '\n', + ( image->len - offset ) ); + if ( eol < 0 ) + eol = image->len; + len = ( eol - offset ); + + /* Copy line, terminate with NUL, and execute command */ + { + char cmdbuf[ len + 1 ]; + + copy_from_user ( cmdbuf, image->data, offset, len ); + cmdbuf[len] = '\0'; + DBG ( "$ %s\n", cmdbuf ); + if ( ( rc = system ( cmdbuf ) ) != 0 ) { + DBG ( "Command \"%s\" failed: %s\n", + cmdbuf, strerror ( rc ) ); + goto done; + } + } + + /* Move to next line */ + offset += ( len + 1 ); + } + + rc = 0; + done: + /* Re-register image and return */ + register_image ( image ); + return rc; +} + +/** + * Load script into memory + * + * @v image Script + * @ret rc Return status code + */ +static int script_load ( struct image *image ) { + static const char magic[] = "#!gpxe\n"; + char test[ sizeof ( magic ) - 1 ]; + + /* Sanity check */ + if ( image->len < sizeof ( test ) ) { + DBG ( "Too short to be a script\n" ); + return -ENOEXEC; + } + + /* Check for magic signature */ + copy_from_user ( test, image->data, 0, sizeof ( test ) ); + if ( memcmp ( test, magic, sizeof ( test ) ) != 0 ) { + DBG ( "Invalid magic signature\n" ); + return -ENOEXEC; + } + + /* This is a script */ + image->type = &script_image_type; + + /* We don't actually load it anywhere; we will pick the lines + * out of the image as we need them. + */ + + return 0; +} + +/** Script image type */ +struct image_type script_image_type __image_type ( PROBE_NORMAL ) = { + .name = "script", + .load = script_load, + .exec = script_exec, +}; diff --git a/gpxe/src/image/segment.c b/gpxe/src/image/segment.c new file mode 100644 index 0000000..9bd60f9 --- /dev/null +++ b/gpxe/src/image/segment.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2007 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. + */ + +/** + * @file + * + * Executable image segments + * + */ + +#include +#include +#include +#include + +/** + * Prepare segment for loading + * + * @v segment Segment start + * @v filesz Size of the "allocated bytes" portion of the segment + * @v memsz Size of the segment + * @ret rc Return status code + */ +int prep_segment ( userptr_t segment, size_t filesz, size_t memsz ) { + struct memory_map memmap; + physaddr_t start = user_to_phys ( segment, 0 ); + physaddr_t mid = user_to_phys ( segment, filesz ); + physaddr_t end = user_to_phys ( segment, memsz ); + unsigned int i; + + DBG ( "Preparing segment [%lx,%lx,%lx)\n", start, mid, end ); + + /* Sanity check */ + if ( filesz > memsz ) { + DBG ( "Insane segment [%lx,%lx,%lx)\n", start, mid, end ); + return -EINVAL; + } + + /* Get a fresh memory map. This allows us to automatically + * avoid treading on any regions that Etherboot is currently + * editing out of the memory map. + */ + get_memmap ( &memmap ); + + /* Look for a suitable memory region */ + for ( i = 0 ; i < memmap.count ; i++ ) { + if ( ( start >= memmap.regions[i].start ) && + ( end <= memmap.regions[i].end ) ) { + /* Found valid region: zero bss and return */ + memset_user ( segment, filesz, 0, ( memsz - filesz ) ); + return 0; + } + } + + /* No suitable memory region found */ + DBG ( "Segment [%lx,%lx,%lx) does not fit into available memory\n", + start, mid, end ); + return -ERANGE; +} diff --git a/gpxe/src/include/.gitignore b/gpxe/src/include/.gitignore new file mode 100644 index 0000000..de1598e --- /dev/null +++ b/gpxe/src/include/.gitignore @@ -0,0 +1 @@ +.buildserial.h diff --git a/gpxe/src/include/alloca.h b/gpxe/src/include/alloca.h new file mode 100644 index 0000000..08398fb --- /dev/null +++ b/gpxe/src/include/alloca.h @@ -0,0 +1,25 @@ +#ifndef _ALLOCA_H +#define _ALLOCA_H + +/** + * @file + * + * Temporary memory allocation + * + */ + +#include + +/** + * Allocate temporary memory from the stack + * + * @v size Size to allocate + * @ret ptr Allocated memory + * + * This memory will be freed automatically when the containing + * function returns. There are several caveats regarding use of + * alloca(); use it only if you already know what they are. + */ +#define alloca(size) __builtin_alloca ( size ) + +#endif /* _ALLOCA_H */ diff --git a/gpxe/src/include/assert.h b/gpxe/src/include/assert.h new file mode 100644 index 0000000..93750a1 --- /dev/null +++ b/gpxe/src/include/assert.h @@ -0,0 +1,65 @@ +#ifndef _ASSERT_H +#define _ASSERT_H + +/** @file + * + * Assertions + * + * This file provides two assertion macros: assert() (for run-time + * assertions) and linker_assert() (for link-time assertions). + * + */ + +#ifdef NDEBUG +#define ASSERTING 0 +#else +#define ASSERTING 1 +#endif + +/** printf() for assertions + * + * This function exists so that the assert() macro can expand to + * printf() calls without dragging the printf() prototype into scope. + * + * As far as the compiler is concerned, assert_printf() and printf() are + * completely unrelated calls; it's only at the assembly stage that + * references to the assert_printf symbol are collapsed into references + * to the printf symbol. + */ +extern int __attribute__ (( format ( printf, 1, 2 ) )) +assert_printf ( const char *fmt, ... ) asm ( "printf" ); + +/** + * Assert a condition at run-time. + * + * If the condition is not true, a debug message will be printed. + * Assertions only take effect in debug-enabled builds (see DBG()). + * + * @todo Make an assertion failure abort the program + * + */ +#define assert( condition ) \ + do { \ + if ( ASSERTING && ! (condition) ) { \ + assert_printf ( "assert(%s) failed at %s line %d\n", \ + #condition, __FILE__, __LINE__ ); \ + } \ + } while ( 0 ) + +/** + * Assert a condition at link-time. + * + * If the condition is not true, the link will fail with an unresolved + * symbol (error_symbol). + * + * This macro is gPXE-specific. Do not use this macro in code + * intended to be portable. + * + */ +#define linker_assert( condition, error_symbol ) \ + if ( ! (condition) ) { \ + extern void error_symbol ( void ); \ + error_symbol(); \ + } + +#endif /* _ASSERT_H */ diff --git a/gpxe/src/include/big_bswap.h b/gpxe/src/include/big_bswap.h new file mode 100644 index 0000000..3775fac --- /dev/null +++ b/gpxe/src/include/big_bswap.h @@ -0,0 +1,33 @@ +#ifndef ETHERBOOT_BIG_BSWAP_H +#define ETHERBOOT_BIG_BSWAP_H + +#define ntohl(x) (x) +#define htonl(x) (x) +#define ntohs(x) (x) +#define htons(x) (x) +#define cpu_to_le64(x) __bswap_64(x) +#define cpu_to_le32(x) __bswap_32(x) +#define cpu_to_le16(x) __bswap_16(x) +#define cpu_to_be64(x) (x) +#define cpu_to_be32(x) (x) +#define cpu_to_be16(x) (x) +#define le64_to_cpu(x) __bswap_64(x) +#define le32_to_cpu(x) __bswap_32(x) +#define le16_to_cpu(x) __bswap_16(x) +#define be64_to_cpu(x) (x) +#define be32_to_cpu(x) (x) +#define be16_to_cpu(x) (x) +#define cpu_to_le64s(x) __bswap_64s(x) +#define cpu_to_le32s(x) __bswap_32s(x) +#define cpu_to_le16s(x) __bswap_16s(x) +#define cpu_to_be64s(x) do {} while (0) +#define cpu_to_be32s(x) do {} while (0) +#define cpu_to_be16s(x) do {} while (0) +#define le64_to_cpus(x) __bswap_64s(x) +#define le32_to_cpus(x) __bswap_32s(x) +#define le16_to_cpus(x) __bswap_16s(x) +#define be64_to_cpus(x) do {} while (0) +#define be32_to_cpus(x) do {} while (0) +#define be16_to_cpus(x) do {} while (0) + +#endif /* ETHERBOOT_BIG_BSWAP_H */ diff --git a/gpxe/src/include/bootp.h b/gpxe/src/include/bootp.h new file mode 100644 index 0000000..0e65477 --- /dev/null +++ b/gpxe/src/include/bootp.h @@ -0,0 +1,230 @@ +#ifndef _BOOTP_H +#define _BOOTP_H + +#ifdef ALTERNATE_DHCP_PORTS_1067_1068 +#undef NON_STANDARD_BOOTP_SERVER +#define NON_STANDARD_BOOTP_SERVER 1067 +#undef NON_STANDARD_BOOTP_CLIENT +#define NON_STANDARD_BOOTP_CLIENT 1068 +#endif + +#ifdef NON_STANDARD_BOOTP_SERVER +#define BOOTP_SERVER NON_STANDARD_BOOTP_SERVER +#else +#define BOOTP_SERVER 67 +#endif +#ifdef NON_STANDARD_BOOTP_CLIENT +#define BOOTP_CLIENT NON_STANDARD_BOOTP_CLIENT +#else +#define BOOTP_CLIENT 68 +#endif +#define PROXYDHCP_SERVER 4011 /* For PXE */ + +#define BOOTP_REQUEST 1 +#define BOOTP_REPLY 2 + +#define TAG_LEN(p) (*((p)+1)) +#define RFC1533_COOKIE 99, 130, 83, 99 +#define RFC1533_PAD 0 +#define RFC1533_NETMASK 1 +#define RFC1533_TIMEOFFSET 2 +#define RFC1533_GATEWAY 3 +#define RFC1533_TIMESERVER 4 +#define RFC1533_IEN116NS 5 +#define RFC1533_DNS 6 +#define RFC1533_LOGSERVER 7 +#define RFC1533_COOKIESERVER 8 +#define RFC1533_LPRSERVER 9 +#define RFC1533_IMPRESSSERVER 10 +#define RFC1533_RESOURCESERVER 11 +#define RFC1533_HOSTNAME 12 +#define RFC1533_BOOTFILESIZE 13 +#define RFC1533_MERITDUMPFILE 14 +#define RFC1533_DOMAINNAME 15 +#define RFC1533_SWAPSERVER 16 +#define RFC1533_ROOTPATH 17 +#define RFC1533_EXTENSIONPATH 18 +#define RFC1533_IPFORWARDING 19 +#define RFC1533_IPSOURCEROUTING 20 +#define RFC1533_IPPOLICYFILTER 21 +#define RFC1533_IPMAXREASSEMBLY 22 +#define RFC1533_IPTTL 23 +#define RFC1533_IPMTU 24 +#define RFC1533_IPMTUPLATEAU 25 +#define RFC1533_INTMTU 26 +#define RFC1533_INTLOCALSUBNETS 27 +#define RFC1533_INTBROADCAST 28 +#define RFC1533_INTICMPDISCOVER 29 +#define RFC1533_INTICMPRESPOND 30 +#define RFC1533_INTROUTEDISCOVER 31 +#define RFC1533_INTROUTESOLICIT 32 +#define RFC1533_INTSTATICROUTES 33 +#define RFC1533_LLTRAILERENCAP 34 +#define RFC1533_LLARPCACHETMO 35 +#define RFC1533_LLETHERNETENCAP 36 +#define RFC1533_TCPTTL 37 +#define RFC1533_TCPKEEPALIVETMO 38 +#define RFC1533_TCPKEEPALIVEGB 39 +#define RFC1533_NISDOMAIN 40 +#define RFC1533_NISSERVER 41 +#define RFC1533_NTPSERVER 42 +#define RFC1533_VENDOR 43 +#define RFC1533_NBNS 44 +#define RFC1533_NBDD 45 +#define RFC1533_NBNT 46 +#define RFC1533_NBSCOPE 47 +#define RFC1533_XFS 48 +#define RFC1533_XDM 49 +#ifndef NO_DHCP_SUPPORT +#define RFC2132_REQ_ADDR 50 +#define RFC2132_MSG_TYPE 53 +#define RFC2132_SRV_ID 54 +#define RFC2132_PARAM_LIST 55 +#define RFC2132_MAX_SIZE 57 +#define RFC2132_VENDOR_CLASS_ID 60 +#define RFC2132_CLIENT_ID 61 +#define RFC2132_TFTP_SERVER_NAME 66 +#define RFC2132_BOOTFILE_NAME 67 +#define RFC3004_USER_CLASS 77 + +#ifdef PXE_DHCP_STRICT +/* + * The following options are acknowledged in RFC3679 because they are + * widely used by PXE implementations, but have never been properly + * allocated. Despite other PXE options being correctly packed in a + * vendor encapsulated field, these are exposed. Sigh. Note that the + * client UUID (option 97) is also noted in the PXE spec as using + * option 61. + */ +#define RFC3679_PXE_CLIENT_ARCH 93 +#define RFC3679_PXE_CLIENT_NDI 94 +#define RFC3679_PXE_CLIENT_UUID 97 + +/* The lengths are fixed. */ +#define RFC3679_PXE_CLIENT_ARCH_LENGTH 2 +#define RFC3679_PXE_CLIENT_NDI_LENGTH 3 +#define RFC3679_PXE_CLIENT_UUID_LENGTH 17 + +/* + * Values of RFC3679_PXE_CLIENT_ARCH can apparently be one of the + * following, according to the PXE spec. The spec only actually + * described the 2nd octet, not the first. Duh... assume 0. + */ +#define RFC3679_PXE_CLIENT_ARCH_IAX86PC 0,0 +#define RFC3679_PXE_CLIENT_ARCH_NECPC98 0,1 +#define RFC3679_PXE_CLIENT_ARCH_IA64PC 0,2 +#define RFC3679_PXE_CLIENT_ARCH_DECALPHA 0,3 +#define RFC3679_PXE_CLIENT_ARCH_ARCX86 0,4 +#define RFC3679_PXE_CLIENT_ARCH_INTELLEAN 0,5 + +/* + * Only one valid value of NDI type (must be 1) and UNDI version (must + * be 2.1) + */ +#define RFC3679_PXE_CLIENT_NDI_21 1,2,1 + +/* + * UUID - type must be 1 and then 16 octets of UID, as with the client ID. + * The value is a default for testing only + */ +#define RFC3679_PXE_CLIENT_UUID_TYPE 0 +#warning "UUID is a default for testing ONLY!" +#define RFC3679_PXE_CLIENT_UUID_DEFAULT \ + RFC3679_PXE_CLIENT_UUID_TYPE, \ + 0xDE,0xAD,0xBE,0xEF, \ + 0xDE,0xAD,0xBE,0xEF, \ + 0xDE,0xAD,0xBE,0xEF, \ + 0xDE,0xAD,0xBE,0xEF +/* + * The Vendor Class ID. Note that the Arch and UNDI version numbers + * are fixed and must be same as the ARCH and NDI above. + */ +#define RFC2132_VENDOR_CLASS_ID_PXE_LENGTH 32 +#define RFC2132_VENDOR_CLASS_ID_PXE \ + 'P','X','E','C','l','i','e','n','t',':', \ + 'A','r','c','h',':','0','0','0','0','0',':', \ + 'U','N','D','I',':','0','0','2','0','0','1' + +/* + * The following vendor options are required in the PXE spec to pull + * options for the *next* image. The PXE spec doesn't help us with + * this (like explaining why). + */ +#define RFC1533_VENDOR_PXE_OPT128 128 +#define RFC1533_VENDOR_PXE_OPT129 129 +#define RFC1533_VENDOR_PXE_OPT130 130 +#define RFC1533_VENDOR_PXE_OPT131 131 +#define RFC1533_VENDOR_PXE_OPT132 132 +#define RFC1533_VENDOR_PXE_OPT133 133 +#define RFC1533_VENDOR_PXE_OPT134 134 +#define RFC1533_VENDOR_PXE_OPT135 135 + +#endif /* PXE_DHCP_STRICT */ + +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPACK 5 +#endif /* NO_DHCP_SUPPORT */ + +#define RFC1533_VENDOR_MAJOR 0 +#define RFC1533_VENDOR_MINOR 0 + +#define RFC1533_VENDOR_MAGIC 128 +#define RFC1533_VENDOR_ADDPARM 129 +#define RFC1533_VENDOR_ETHDEV 130 +/* We should really apply for an official Etherboot encap option */ +#define RFC1533_VENDOR_ETHERBOOT_ENCAP 150 +/* I'll leave it to FREEBSD to decide if they want to renumber */ +#ifdef IMAGE_FREEBSD +#define RFC1533_VENDOR_HOWTO 132 +#define RFC1533_VENDOR_KERNEL_ENV 133 +#endif +#define RFC1533_VENDOR_NIC_DEV_ID 175 +#define RFC1533_VENDOR_ARCH 177 + +#define RFC1533_END 255 + +#define BOOTP_VENDOR_LEN 64 +#ifndef NO_DHCP_SUPPORT +#define DHCP_OPT_LEN 312 +#endif /* NO_DHCP_SUPPORT */ + +/* Format of a bootp packet */ +struct bootp_t { + uint8_t bp_op; + uint8_t bp_htype; + uint8_t bp_hlen; + uint8_t bp_hops; + uint32_t bp_xid; + uint16_t bp_secs; + uint16_t unused; + in_addr bp_ciaddr; + in_addr bp_yiaddr; + in_addr bp_siaddr; + in_addr bp_giaddr; + uint8_t bp_hwaddr[16]; + uint8_t bp_sname[64]; + char bp_file[128]; +#ifdef NO_DHCP_SUPPORT + uint8_t bp_vend[BOOTP_VENDOR_LEN]; +#else + uint8_t bp_vend[DHCP_OPT_LEN]; +#endif /* NO_DHCP_SUPPORT */ +}; + +/* Format of a bootp IP packet */ +struct bootpip_t +{ + struct iphdr ip; + struct udphdr udp; + struct bootp_t bp; +}; + +/* Format of bootp packet with extensions */ +struct bootpd_t { + struct bootp_t bootp_reply; + uint8_t bootp_extension[MAX_BOOTP_EXTLEN]; +}; + +#endif /* _BOOTP_H */ diff --git a/gpxe/src/include/btext.h b/gpxe/src/include/btext.h new file mode 100644 index 0000000..1d3f9e5 --- /dev/null +++ b/gpxe/src/include/btext.h @@ -0,0 +1,62 @@ +/* + * This file describes the structure passed from the BootX application + * (for MacOS) when it is used to boot Linux. + * + * Written by Benjamin Herrenschmidt. + * + * Move to LinuxBIOS by LYH yhlu@tyan.com + * + */ + + +#ifndef _BTEXT_H__ +#define _BTEXT_H__ + +#if 1 +#define u32 unsigned int +#define u16 unsigned short +#define u8 unsigned char +#endif + +/* Here are the boot informations that are passed to the bootstrap + * Note that the kernel arguments and the device tree are appended + * at the end of this structure. */ +typedef struct boot_infos +{ + + /* NEW (vers. 2) this holds the current _logical_ base addr of + the frame buffer (for use by early boot message) */ + u8* logicalDisplayBase; + + + /* Some infos about the current MacOS display */ + u32 dispDeviceRect[4]; /* left,top,right,bottom */ + u32 dispDeviceDepth; /* (8, 16 or 32) */ + u32 dispDeviceBase; /* base address (physical) */ + u32 dispDeviceRowBytes; /* rowbytes (in bytes) */ + u32 dispDeviceColorsOffset; /* Colormap (8 bits only) or 0 (*) */ + + + /* The framebuffer size (optional, currently 0) */ + u32 frameBufferSize; /* Represents a max size, can be 0. */ + + +} boot_infos_t; + +/* (*) The format of the colormap is 256 * 3 * 2 bytes. Each color index is represented + * by 3 short words containing a 16 bits (unsigned) color component. + * Later versions may contain the gamma table for direct-color devices here. + */ +#define BOOTX_COLORTABLE_SIZE (256UL*3UL*2UL) + + +/* + * Definitions for using the procedures in btext.c. + * + * Benjamin Herrenschmidt + */ + +extern boot_infos_t disp_bi; +extern u32 boot_text_mapped; + +#endif /* _BTEXT_H */ diff --git a/gpxe/src/include/byteswap.h b/gpxe/src/include/byteswap.h new file mode 100644 index 0000000..6c3ced2 --- /dev/null +++ b/gpxe/src/include/byteswap.h @@ -0,0 +1,57 @@ +#ifndef ETHERBOOT_BYTESWAP_H +#define ETHERBOOT_BYTESWAP_H + +#include "endian.h" +#include "bits/byteswap.h" + +#define __bswap_constant_16(x) \ + ((uint16_t)((((uint16_t)(x) & 0x00ff) << 8) | \ + (((uint16_t)(x) & 0xff00) >> 8))) + +#define __bswap_constant_32(x) \ + ((uint32_t)((((uint32_t)(x) & 0x000000ffU) << 24) | \ + (((uint32_t)(x) & 0x0000ff00U) << 8) | \ + (((uint32_t)(x) & 0x00ff0000U) >> 8) | \ + (((uint32_t)(x) & 0xff000000U) >> 24))) + +#define __bswap_constant_64(x) \ + ((uint64_t)((((uint64_t)(x) & 0x00000000000000ffULL) << 56) | \ + (((uint64_t)(x) & 0x000000000000ff00ULL) << 40) | \ + (((uint64_t)(x) & 0x0000000000ff0000ULL) << 24) | \ + (((uint64_t)(x) & 0x00000000ff000000ULL) << 8) | \ + (((uint64_t)(x) & 0x000000ff00000000ULL) >> 8) | \ + (((uint64_t)(x) & 0x0000ff0000000000ULL) >> 24) | \ + (((uint64_t)(x) & 0x00ff000000000000ULL) >> 40) | \ + (((uint64_t)(x) & 0xff00000000000000ULL) >> 56))) + +#define __bswap_16(x) \ + ((uint16_t)(__builtin_constant_p(x) ? \ + __bswap_constant_16(x) : \ + __bswap_variable_16(x))) + +#define __bswap_32(x) \ + ((uint32_t)(__builtin_constant_p(x) ? \ + __bswap_constant_32(x) : \ + __bswap_variable_32(x))) + +#define __bswap_64(x) \ + ((uint64_t)(__builtin_constant_p(x) ? \ + __bswap_constant_64(x) : \ + __bswap_variable_64(x))) + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#include "little_bswap.h" +#endif +#if __BYTE_ORDER == __BIG_ENDIAN +#include "big_bswap.h" +#endif + +/* Make routines available to all */ +#define swap64(x) __bswap_64(x) +#define swap32(x) __bswap_32(x) +#define swap16(x) __bswap_16(x) +#define bswap_64(x) __bswap_64(x) +#define bswap_32(x) __bswap_32(x) +#define bswap_16(x) __bswap_16(x) + +#endif /* ETHERBOOT_BYTESWAP_H */ diff --git a/gpxe/src/include/cmdline.h b/gpxe/src/include/cmdline.h new file mode 100644 index 0000000..9ab29c5 --- /dev/null +++ b/gpxe/src/include/cmdline.h @@ -0,0 +1,8 @@ +#ifndef CMDLINE_H +#define CMDLINE_H + +/* Command line external functions */ + +void cmdl_start(); + +#endif diff --git a/gpxe/src/include/cmdlinelib.h b/gpxe/src/include/cmdlinelib.h new file mode 100644 index 0000000..1eb6899 --- /dev/null +++ b/gpxe/src/include/cmdlinelib.h @@ -0,0 +1,99 @@ +/* Command line library */ +#ifndef CMDLINELIB_H +#define CMDLINELIB_H + +#define CMDL_BUFFER_SIZE 256 +//#define CMDL_OUTPUT_SIZE 256 +#define CMDL_PROMPT_SIZE 8 +#define CMDL_MAX_TAB_COMPLETE_RESULT 256 + +typedef int (*cmdl_putchar_t)(int); +typedef int (*cmdl_printf_t)( const char *format, ... ); +typedef int (*cmdl_getchar_t)(); + +#ifndef NULL +#define NULL ((void *)0) +#endif + +enum{ + CMDL_LEFT, + CMDL_RIGHT +}; + +enum{ + CMDLK_FW=6, + CMDLK_BW=2, + CMDLK_BS=8, + CMDLK_HOME=2, + CMDLK_END=5, + CMDLK_DELTOEND=11, + CMDLK_DELARG=23, + CMDLK_ENTER=0x0d, + CMDLK_RETURN=0x0a, + CMDLK_TAB=9 +}; + +typedef struct{ + + // buffers + + //char* output; + char* buffer; + char* prompt; + + // options and values + + int cursor; + //int has_output; + int exit; + int refresh; + int tabstate; + int insert; + + // callbacks + + cmdl_putchar_t putchar; + cmdl_getchar_t getchar; + cmdl_printf_t printf; + +}cmd_line; + +typedef struct{ + int argc; + char **argv; +}cmdl_param_list; + +void cmdl_setputchar(cmd_line* cmd, cmdl_putchar_t in); +void cmdl_setgetchar(cmd_line* cmd, cmdl_getchar_t in); +void cmdl_setprintf(cmd_line* cmd, cmdl_printf_t in); + +//void cmdl_builtin_help(cmd_line* cmd, char* command); + +void cmdl_parsechar(cmd_line* cmd, char in); + +void cmdl_addreplace(cmd_line* cmd, char in); +void cmdl_addinsert(cmd_line* cmd, char in); +void cmdl_enterloop(cmd_line* cmd); +void cmdl_exec(cmd_line* cmd); +void cmdl_setexit(cmd_line* cmd, int exit); +int cmdl_getexit(cmd_line* cmd); +void cmdl_clearoutput(cmd_line* cmd); +void cmdl_clearbuffer(cmd_line* cmd); +int cmdl_printf(cmd_line* cmd, const char *format, ...); +char* cmdl_getoutput(cmd_line* cmd); +//void cmdl_addoutput_str(cmd_line* cmd, char output[CMDL_OUTPUT_SIZE]); +void cmdl_addstr(cmd_line* cmd, char* str); +int cmdl_movecursor(cmd_line* cmd, int direction); +char* cmdl_getbuffer(cmd_line* cmd); +void cmdl_addchar(cmd_line* cmd, char in); +int cmdl_check(cmd_line* cmd); +void cmdl_del(cmd_line* cmd); +cmd_line* cmdl_create(); +void cmdl_free(cmd_line* cmd); +char *cmdl_getprompt(cmd_line* cmd); +void cmdl_setpropmt(cmd_line* cmd, char prompt[CMDL_PROMPT_SIZE]); +cmdl_param_list* cmdl_getparams(const char* command); +void cmdl_tabcomplete(cmd_line *cmd); + +#endif + diff --git a/gpxe/src/include/cmdlist.h b/gpxe/src/include/cmdlist.h new file mode 100644 index 0000000..33cd349 --- /dev/null +++ b/gpxe/src/include/cmdlist.h @@ -0,0 +1,18 @@ +#ifndef COMMANDLIST_H +#define COMMANDLIST_H + +void test_req(); +void test2_req(); +void help_req(); +void nvo_cmd_req(); + +void commandlist() +{ + // test_req(); + // test2_req(); + help_req(); + nvo_cmd_req(); +} + +#endif + diff --git a/gpxe/src/include/coff.h b/gpxe/src/include/coff.h new file mode 100644 index 0000000..a73fda5 --- /dev/null +++ b/gpxe/src/include/coff.h @@ -0,0 +1,73 @@ +#ifndef COFF_H +#define COFF_H +/* Based on the elf.h file + * Changed accordingly to support COFF file support + */ + + +/* Values for f_flags. */ +#define F_RELFLG 0x0001 /* If set, not reloc. info. Clear for executables */ +#define F_EXEC 0x0002 /* No unresolved symbols. Executable file ! */ +#define F_LNNO 0x0004 /* If set, line information numbers removed */ +#define F_LSYMS 0x0008 /* If set, local symbols removed */ +#define F_AR32WR 0x0100 /* Indicates little endian file */ + +/* Values for e_machine (architecute). */ +#define EM_E1 0x17a /* Magic number for Hyperstone. Big endian format */ + +/* Values for f_flags. */ +#define O_MAGIC 0x017c /* Optional's header magic number for Hyperstone */ + +/* Values for s_flags. */ +#define S_TYPE_TEXT 0x0020 /* If set, the section contains only executable */ +#define S_TYPE_DATA 0x0040 /* If set, the section contains only initialized data */ +#define S_TYPE_BSS 0x0080 /* If set, the section is BSS no data stored */ + + +typedef struct +{ + unsigned short f_magic; /* magic number */ + unsigned short f_nscns; /* number of sections */ + unsigned long f_timdat; /* time & date stamp */ + unsigned long f_symptr; /* file pointer to symtab */ + unsigned long f_nsyms; /* number of symtab entries */ + unsigned short f_opthdr; /* sizeof(optional hdr) */ + unsigned short f_flags; /* flags */ +} +COFF_filehdr; + +/* + * Optional header. + */ +typedef struct +{ + unsigned short magic; /* type of file */ + unsigned short vstamp; /* version stamp */ + unsigned long tsize; /* text size in bytes, padded to FW bdry*/ + unsigned long dsize; /* initialized data " " */ + unsigned long bsize; /* uninitialized data " " */ + unsigned long entry; /* entry pt. */ + unsigned long text_start; /* base of text used for this file */ + unsigned long data_start; /* base of data used for this file */ +} +COFF_opthdr; + +/* + * Section header. + */ +typedef struct +{ + char s_name[8]; /* section name */ + unsigned long s_paddr; /* physical address, aliased s_nlib */ + unsigned long s_vaddr; /* virtual address */ + unsigned long s_size; /* section size */ + unsigned long s_scnptr; /* file ptr to raw data for section */ + unsigned long s_relptr; /* file ptr to relocation */ + unsigned long s_lnnoptr; /* file ptr to line numbers */ + unsigned short s_nreloc; /* number of relocation entries */ + unsigned short s_nlnno; /* number of line number entries*/ + unsigned long s_flags; /* flags */ +} +COFF_scnhdr; + +#endif /* COFF_H */ diff --git a/gpxe/src/include/compiler.h b/gpxe/src/include/compiler.h new file mode 100644 index 0000000..889c240 --- /dev/null +++ b/gpxe/src/include/compiler.h @@ -0,0 +1,385 @@ +#ifndef COMPILER_H +#define COMPILER_H + +/* + * Doxygen can't cope with some of the more esoteric areas of C, so we + * make its life simpler. + * + */ +#ifdef DOXYGEN +#define __attribute__(x) +#endif + +/** @file + * + * Global compiler definitions. + * + * This file is implicitly included by every @c .c file in Etherboot. + * It defines global macros such as DBG(). + * + * We arrange for each object to export the symbol @c obj_OBJECT + * (where @c OBJECT is the object name, e.g. @c rtl8139) as a global + * symbol, so that the linker can drag in selected object files from + * the library using -u obj_OBJECT . + * + */ + +/* Not quite sure why cpp requires two levels of macro call in order + * to actually expand OBJECT... + */ +#undef _H1 +#define _H1( x, y ) x ## y +#undef _H2 +#define _H2( x, y ) _H1 ( x, y ) +#define PREFIX_OBJECT(prefix) _H2 ( prefix, OBJECT ) +#define OBJECT_SYMBOL PREFIX_OBJECT(obj_) +#undef _STR +#define _STR(s) #s +#undef _XSTR +#define _XSTR(s) _STR(s) +#define OBJECT_SYMBOL_STR _XSTR ( OBJECT_SYMBOL ) + +#ifdef ASSEMBLY + + .globl OBJECT_SYMBOL + .equ OBJECT_SYMBOL, 0 + +#else /* ASSEMBLY */ + +__asm__ ( ".globl\t" OBJECT_SYMBOL_STR ); +__asm__ ( ".equ\t" OBJECT_SYMBOL_STR ", 0" ); + +/** + * Drag in an object by object name. + * + * Macro to allow objects to explicitly drag in other objects by + * object name. Used by config.c. + * + */ +#define REQUIRE_OBJECT(object) \ + __asm__ ( ".equ\tneed_" #object ", obj_" #object ); + +/* Force visibility of all symbols to "hidden", i.e. inform gcc that + * all symbol references resolve strictly within our final binary. + * This avoids unnecessary PLT/GOT entries on x86_64. + * + * This is a stronger claim than specifying "-fvisibility=hidden", + * since it also affects symbols marked with "extern". + */ +#if __GNUC__ >= 4 +#pragma GCC visibility push(hidden) +#endif + +/** @def DBG + * + * Print a debugging message. + * + * The debug level is set at build time by specifying the @c DEBUG= + * parameter on the @c make command line. For example, to enable + * debugging for the PCI bus functions (in pci.c) in a @c .dsk image + * for the @c rtl8139 card, you could use the command line + * + * @code + * + * make bin/rtl8139.dsk DEBUG=pci + * + * @endcode + * + * This will enable the debugging statements (DBG()) in pci.c. If + * debugging is not enabled, DBG() statements will be ignored. + * + * You can enable debugging in several objects simultaneously by + * separating them with commas, as in + * + * @code + * + * make bin/rtl8139.dsk DEBUG=pci,buffer,heap + * + * @endcode + * + * You can increase the debugging level for an object by specifying it + * with @c :N, where @c N is the level, as in + * + * @code + * + * make bin/rtl8139.dsk DEBUG=pci,buffer:2,heap + * + * @endcode + * + * which would enable debugging for the PCI, buffer-handling and + * heap-allocation code, with the buffer-handling code at level 2. + * + */ + +/* + * If debug_OBJECT is set to a true value, the macro DBG(...) will + * expand to printf(...) when compiling OBJECT, and the symbol + * DEBUG_LEVEL will be inserted into the object file. + * + */ +#define DEBUG_SYMBOL PREFIX_OBJECT(debug_) + +#if DEBUG_SYMBOL +#define DEBUG_SYMBOL_STR _XSTR ( DEBUG_SYMBOL ) +__asm__ ( ".equ\tDBGLVL, " DEBUG_SYMBOL_STR ); +#endif + +/** printf() for debugging + * + * This function exists so that the DBG() macros can expand to + * printf() calls without dragging the printf() prototype into scope. + * + * As far as the compiler is concerned, dbg_printf() and printf() are + * completely unrelated calls; it's only at the assembly stage that + * references to the dbg_printf symbol are collapsed into references + * to the printf symbol. + */ +extern int __attribute__ (( format ( printf, 1, 2 ) )) +dbg_printf ( const char *fmt, ... ) asm ( "printf" ); + +extern void dbg_autocolourise ( unsigned long id ); +extern void dbg_decolourise ( void ); +extern void dbg_hex_dump_da ( unsigned long dispaddr, + const void *data, unsigned long len ); + +#if DEBUG_SYMBOL +#define DBGLVL_MAX DEBUG_SYMBOL +#else +#define DBGLVL_MAX 0 +#endif + +/* Allow for selective disabling of enabled debug levels */ +#if DBGLVL_MAX +int __debug_disable; +#define DBGLVL ( DBGLVL_MAX & ~__debug_disable ) +#define DBG_DISABLE( level ) do { \ + __debug_disable |= ( (level) & DBGLVL_MAX ); \ + } while ( 0 ) +#define DBG_ENABLE( level ) do { \ + __debug_disable &= ~( (level) & DBGLVL_MAX ); \ + } while ( 0 ) +#else +#define DBGLVL 0 +#define DBG_DISABLE( level ) do { } while ( 0 ) +#define DBG_ENABLE( level ) do { } while ( 0 ) +#endif + +#define DBGLVL_LOG 1 +#define DBG_LOG ( DBGLVL & DBGLVL_LOG ) +#define DBGLVL_EXTRA 2 +#define DBG_EXTRA ( DBGLVL & DBGLVL_EXTRA ) +#define DBGLVL_PROFILE 4 +#define DBG_PROFILE ( DBGLVL & DBGLVL_PROFILE ) +#define DBGLVL_IO 8 +#define DBG_IO ( DBGLVL & DBGLVL_IO ) + +/** + * Print debugging message if we are at a certain debug level + * + * @v level Debug level + * @v ... printf() argument list + */ +#define DBG_IF( level, ... ) do { \ + if ( DBG_ ## level ) { \ + dbg_printf ( __VA_ARGS__ ); \ + } \ + } while ( 0 ) + +/** + * Print a hex dump if we are at a certain debug level + * + * @v level Debug level + * @v dispaddr Display address + * @v data Data to print + * @v len Length of data + */ +#define DBG_HDA_IF( level, dispaddr, data, len ) do { \ + if ( DBG_ ## level ) { \ + union { \ + unsigned long ul; \ + typeof ( dispaddr ) raw; \ + } da; \ + da.raw = dispaddr; \ + dbg_hex_dump_da ( da.ul, data, len ); \ + } \ + } while ( 0 ) + +/** + * Print a hex dump if we are at a certain debug level + * + * @v level Debug level + * @v data Data to print + * @v len Length of data + */ +#define DBG_HD_IF( level, data, len ) do { \ + DBG_HDA_IF ( level, data, data, len ); \ + } while ( 0 ) + +/** + * Select colour for debug messages if we are at a certain debug level + * + * @v level Debug level + * @v id Message stream ID + */ +#define DBG_AC_IF( level, id ) do { \ + if ( DBG_ ## level ) { \ + union { \ + unsigned long ul; \ + typeof ( id ) raw; \ + } dbg_stream; \ + dbg_stream.raw = id; \ + dbg_autocolourise ( dbg_stream.ul ); \ + } \ + } while ( 0 ) + +/** + * Revert colour for debug messages if we are at a certain debug level + * + * @v level Debug level + */ +#define DBG_DC_IF( level ) do { \ + if ( DBG_ ## level ) { \ + dbg_decolourise(); \ + } \ + } while ( 0 ) + +/* Autocolourising versions of the DBGxxx_IF() macros */ + +#define DBGC_IF( level, id, ... ) do { \ + DBG_AC_IF ( level, id ); \ + DBG_IF ( level, __VA_ARGS__ ); \ + DBG_DC_IF ( level ); \ + } while ( 0 ) + +#define DBGC_HDA_IF( level, id, ... ) do { \ + DBG_AC_IF ( level, id ); \ + DBG_HDA_IF ( level, __VA_ARGS__ ); \ + DBG_DC_IF ( level ); \ + } while ( 0 ) + +#define DBGC_HD_IF( level, id, ... ) do { \ + DBG_AC_IF ( level, id ); \ + DBG_HD_IF ( level, __VA_ARGS__ ); \ + DBG_DC_IF ( level ); \ + } while ( 0 ) + +/* Versions of the DBGxxx_IF() macros that imply DBGxxx_IF( LOG, ... )*/ + +#define DBG( ... ) DBG_IF ( LOG, __VA_ARGS__ ) +#define DBG_HDA( ... ) DBG_HDA_IF ( LOG, __VA_ARGS__ ) +#define DBG_HD( ... ) DBG_HD_IF ( LOG, __VA_ARGS__ ) +#define DBGC( ... ) DBGC_IF ( LOG, __VA_ARGS__ ) +#define DBGC_HDA( ... ) DBGC_HDA_IF ( LOG, __VA_ARGS__ ) +#define DBGC_HD( ... ) DBGC_HD_IF ( LOG, __VA_ARGS__ ) + +/* Versions of the DBGxxx_IF() macros that imply DBGxxx_IF( EXTRA, ... )*/ + +#define DBG2( ... ) DBG_IF ( EXTRA, __VA_ARGS__ ) +#define DBG2_HDA( ... ) DBG_HDA_IF ( EXTRA, __VA_ARGS__ ) +#define DBG2_HD( ... ) DBG_HD_IF ( EXTRA, __VA_ARGS__ ) +#define DBGC2( ... ) DBGC_IF ( EXTRA, __VA_ARGS__ ) +#define DBGC2_HDA( ... ) DBGC_HDA_IF ( EXTRA, __VA_ARGS__ ) +#define DBGC2_HD( ... ) DBGC_HD_IF ( EXTRA, __VA_ARGS__ ) + +/* Versions of the DBGxxx_IF() macros that imply DBGxxx_IF( PROFILE, ... )*/ + +#define DBGP( ... ) DBG_IF ( PROFILE, __VA_ARGS__ ) +#define DBGP_HDA( ... ) DBG_HDA_IF ( PROFILE, __VA_ARGS__ ) +#define DBGP_HD( ... ) DBG_HD_IF ( PROFILE, __VA_ARGS__ ) +#define DBGCP( ... ) DBGC_IF ( PROFILE, __VA_ARGS__ ) +#define DBGCP_HDA( ... ) DBGC_HDA_IF ( PROFILE, __VA_ARGS__ ) +#define DBGCP_HD( ... ) DBGC_HD_IF ( PROFILE, __VA_ARGS__ ) + +/* Versions of the DBGxxx_IF() macros that imply DBGxxx_IF( IO, ... )*/ + +#define DBGIO( ... ) DBG_IF ( IO, __VA_ARGS__ ) +#define DBGIO_HDA( ... ) DBG_HDA_IF ( IO, __VA_ARGS__ ) +#define DBGIO_HD( ... ) DBG_HD_IF ( IO, __VA_ARGS__ ) +#define DBGCIO( ... ) DBGC_IF ( IO, __VA_ARGS__ ) +#define DBGCIO_HDA( ... ) DBGC_HDA_IF ( IO, __VA_ARGS__ ) +#define DBGCIO_HD( ... ) DBGC_HD_IF ( IO, __VA_ARGS__ ) + + +#if DEBUG_SYMBOL == 0 +#define NDEBUG +#endif + +/** Select file identifier for errno.h (if used) */ +#define ERRFILE PREFIX_OBJECT ( ERRFILE_ ) + +/** Declare a data structure as packed. */ +#define PACKED __attribute__ (( packed )) + +/** Declare a variable or data structure as unused. */ +#define __unused __attribute__ (( unused )) + +/** + * Declare a function as pure - i.e. without side effects + */ +#define __pure __attribute__ (( pure )) + +/** + * Declare a function as const - i.e. it does not access global memory + * (including dereferencing pointers passed to it) at all. + * Must also not call any non-const functions. + */ +#define __const __attribute__ (( const )) + +/** + * Declare a function's pointer parameters as non-null - i.e. force + * compiler to check pointers at compile time and enable possible + * optimizations based on that fact + */ +#define __nonnull __attribute__ (( nonnull )) + +/** + * Declare a pointer returned by a function as a unique memory address + * as returned by malloc-type functions. + */ +#define __malloc __attribute__ (( malloc )) + +/** + * Declare a function as used. + * + * Necessary only if the function is called only from assembler code. + */ +#define __used __attribute__ (( used )) + +/** Declare a data structure to be aligned with 16-byte alignment */ +#define __aligned __attribute__ (( aligned ( 16 ) )) + +/** Declare a function to be always inline */ +#define __always_inline __attribute__ (( always_inline )) + +/** + * Shared data. + * + * To save space in the binary when multiple-driver images are + * compiled, uninitialised data areas can be shared between drivers. + * This will typically be used to share statically-allocated receive + * and transmit buffers between drivers. + * + * Use as e.g. + * + * @code + * + * struct { + * char rx_buf[NUM_RX_BUF][RX_BUF_SIZE]; + * char tx_buf[TX_BUF_SIZE]; + * } my_static_data __shared; + * + * @endcode + * + */ +#define __shared __asm__ ( "_shared_bss" ) __aligned + +/** + * Optimisation barrier + */ +#define barrier() __asm__ __volatile__ ( "" : : : "memory" ) + +#endif /* ASSEMBLY */ + +#include + +#endif /* COMPILER_H */ diff --git a/gpxe/src/include/console.h b/gpxe/src/include/console.h new file mode 100644 index 0000000..9addd52 --- /dev/null +++ b/gpxe/src/include/console.h @@ -0,0 +1,114 @@ +#ifndef CONSOLE_H +#define CONSOLE_H + +#include + +/** @file + * + * User interaction. + * + * Various console devices can be selected via the build options + * CONSOLE_FIRMWARE, CONSOLE_SERIAL etc. The console functions + * putchar(), getchar() and iskey() delegate to the individual console + * drivers. + * + */ + +/** + * A console driver + * + * Defines the functions that implement a particular console type. + * Must be made part of the console drivers table by using + * #__console_driver. + * + * @note Consoles that cannot be used before their initialisation + * function has completed should set #disabled=1 initially. This + * allows other console devices to still be used to print out early + * debugging messages. + * + */ +struct console_driver { + /** Console is disabled. + * + * The console's putchar(), putline(), getchar() and iskey() + * methods will not be called while #disabled==1. Typically + * the console's initialisation functions will set #disabled=0 + * upon completion. + * + */ + int disabled; + + /** Write a character to the console. + * + * @v character Character to be written + * @ret None - + * @err None - + * + */ + void ( *putchar ) ( int character ); + + /** Write an entire line to the console. + * This is intended to be used by line-oriented output media, + * like system logging facilities or line printers. + * Line output will not contain non-printable characters. + * + * @v linebuffer Pointer to the \0-terminated line + * @ret None - + * @err None - + */ + void ( * putline ) ( unsigned char * linebuffer ); + + /** Read a character from the console. + * + * @v None - + * @ret character Character read + * @err None - + * + * If no character is available to be read, this method will + * block. The character read should not be echoed back to the + * console. + * + */ + int ( *getchar ) ( void ); + + /** Check for available input. + * + * @v None - + * @ret True Input is available + * @ret False Input is not available + * @err None - + * + * This should return True if a subsequent call to getchar() + * will not block. + * + */ + int ( *iskey ) ( void ); +}; + +/** + * Mark a struct console_driver as being part of the + * console drivers table. + * + * Use as e.g. + * + * @code + * + * struct console_driver my_console __console_driver = { + * .putchar = my_putchar, + * .getchar = my_getchar, + * .iskey = my_iskey, + * }; + * + * @endcode + * + */ +#define __console_driver __table ( struct console_driver, console, 01 ) + +/* Function prototypes */ + +extern void putchar ( int character ); +extern int getchar ( void ); +extern int iskey ( void ); +extern int getkey ( void ); + +#endif /* CONSOLE_H */ diff --git a/gpxe/src/include/cpu.h b/gpxe/src/include/cpu.h new file mode 100644 index 0000000..b2c428f --- /dev/null +++ b/gpxe/src/include/cpu.h @@ -0,0 +1,6 @@ +#ifndef CPU_H +#define CPU_H + +#include "bits/cpu.h" + +#endif /* CPU_H */ diff --git a/gpxe/src/include/ctype.h b/gpxe/src/include/ctype.h new file mode 100644 index 0000000..7740443 --- /dev/null +++ b/gpxe/src/include/ctype.h @@ -0,0 +1,27 @@ +#ifndef _CTYPE_H +#define _CTYPE_H + +/** @file + * + * Character types + */ + +#define isdigit(c) ((c) >= '0' && (c) <= '9') +#define islower(c) ((c) >= 'a' && (c) <= 'z') +#define isupper(c) ((c) >= 'A' && (c) <= 'Z') + +static inline unsigned char tolower(unsigned char c) +{ + if (isupper(c)) + c -= 'A'-'a'; + return c; +} + +static inline unsigned char toupper(unsigned char c) +{ + if (islower(c)) + c -= 'a'-'A'; + return c; +} + +#endif /* _CTYPE_H */ diff --git a/gpxe/src/include/curses.h b/gpxe/src/include/curses.h new file mode 100644 index 0000000..6b1c42d --- /dev/null +++ b/gpxe/src/include/curses.h @@ -0,0 +1,753 @@ +#ifndef CURSES_H +#define CURSES_H + +#include +#include + +/** @file + * + * MuCurses header file + * + */ + +#undef ERR +#define ERR (-1) + +#undef FALSE +#define FALSE (0) + +#undef OK +#define OK (0) + +#undef TRUE +#define TRUE (1) + +typedef int bool; +typedef uint32_t chtype; +typedef uint32_t attr_t; + +/** Curses SCREEN object */ +typedef struct _curses_screen { + /** Current cursor position */ + unsigned int curs_x, curs_y; + /** Current attribute */ + attr_t attrs; + + void ( *init ) ( struct _curses_screen *scr ); + void ( *exit ) ( struct _curses_screen *scr ); + /** + * Move cursor to position specified by x,y coords + * + * @v scr screen on which to operate + * @v y Y position + * @v x X position + */ + void ( * movetoyx ) ( struct _curses_screen *scr, + unsigned int y, unsigned int x ); + /** + * Write character to current cursor position + * + * @v scr screen on which to operate + * @v c character to be written + */ + void ( * putc ) ( struct _curses_screen *scr, chtype c ); + /** + * Pop a character from the keyboard input stream + * + * @v scr screen on which to operate + * @ret c popped character + */ + int ( * getc ) ( struct _curses_screen *scr ); + /** + * Checks to see whether a character is waiting in the input stream + * + * @v scr screen on which to operate + * @ret TRUE character waiting in stream + * @ret FALSE no character waiting in stream + */ + bool ( *peek ) ( struct _curses_screen *scr ); +} SCREEN; + +/** Curses Window struct */ +typedef struct _curses_window { + /** screen with which window associates */ + SCREEN *scr; + /** window attributes */ + attr_t attrs; + /** window origin coordinates */ + unsigned int ori_x, ori_y; + /** window cursor position */ + unsigned int curs_x, curs_y; + /** window dimensions */ + unsigned int width, height; + /** parent window */ + struct _curses_window *parent; + /** windows that share the same parent as this one */ + //struct list_head siblings; + /** windows der'd or sub'd from this one */ + //struct list_head children; +} WINDOW; + +extern WINDOW _stdscr; +extern unsigned short _COLS; +extern unsigned short _LINES; + +#define stdscr ( &_stdscr ) +#define COLS _COLS +#define LINES _LINES + +#define MUCURSES_BITS( mask, shift ) (( mask ) << (shift)) +#define CPAIR_SHIFT 8 +#define ATTRS_SHIFT 16 + +#define WA_DEFAULT ( 0x0000 << ATTRS_SHIFT ) +#define WA_ALTCHARSET ( 0x0001 << ATTRS_SHIFT ) +#define WA_BLINK ( 0x0002 << ATTRS_SHIFT ) +#define WA_BOLD ( 0x0004 << ATTRS_SHIFT ) +#define WA_DIM ( 0x0008 << ATTRS_SHIFT ) +#define WA_INVIS ( 0x0010 << ATTRS_SHIFT ) +#define WA_PROTECT ( 0x0020 << ATTRS_SHIFT ) +#define WA_REVERSE ( 0x0040 << ATTRS_SHIFT ) +#define WA_STANDOUT ( 0x0080 << ATTRS_SHIFT ) +#define WA_UNDERLINE ( 0x0100 << ATTRS_SHIFT ) +#define WA_HORIZONTAL ( 0x0200 << ATTRS_SHIFT ) +#define WA_VERTICAL ( 0x0400 << ATTRS_SHIFT ) +#define WA_LEFT ( 0x0800 << ATTRS_SHIFT ) +#define WA_RIGHT ( 0x1000 << ATTRS_SHIFT ) +#define WA_LOW ( 0x2000 << ATTRS_SHIFT ) +#define WA_TOP ( 0x4000 << ATTRS_SHIFT ) + +#define A_DEFAULT WA_DEFAULT +#define A_ALTCHARSET WA_ALTCHARSET +#define A_BLINK WA_BLINK +#define A_BOLD WA_BOLD +#define A_DIM WA_DIM +#define A_INVIS WA_INVIS +#define A_PROTECT WA_PROTECT +#define A_REVERSE WA_REVERSE +#define A_STANDOUT WA_STANDOUT +#define A_UNDERLINE WA_UNDERLINE + +#define A_ATTRIBUTES ( 0xffff << ATTRS_SHIFT ) +#define A_CHARTEXT ( 0xff ) +#define A_COLOUR ( 0xff << CPAIR_SHIFT ) +#define A_COLOR A_COLOUR + +#define COLOUR_PAIR(n) ( (n) << CPAIR_SHIFT ) +#define COLOR_PAIR(n) COLOUR_PAIR(n) +#define PAIR_NUMBER(attrs) ( ( (attrs) & A_COLOUR ) >> CPAIR_SHIFT ) + +#define COLOUR_PAIRS 8 /* Arbitrary limit */ +#define COLOR_PAIRS COLOUR_PAIRS + +#define ACS_ULCORNER '+' +#define ACS_LLCORNER '+' +#define ACS_URCORNER '+' +#define ACS_LRCORNER '+' +#define ACS_RTEE '+' +#define ACS_LTEE '+' +#define ACS_BTEE '+' +#define ACS_TTEE '+' +#define ACS_HLINE '-' +#define ACS_VLINE '|' +#define ACS_PLUS '+' +#define ACS_S1 '-' +#define ACS_S9 '_' +#define ACS_DIAMOND '+' +#define ACS_CKBOARD ':' +#define ACS_DEGREE '\'' +#define ACS_PLMINUS '#' +#define ACS_BULLET 'o' +#define ACS_LARROW '<' +#define ACS_RARROW '>' +#define ACS_DARROW 'v' +#define ACS_UARROW '^' +#define ACS_BOARD '#' +#define ACS_LANTERN '#' +#define ACS_BLOCK '#' + +#define COLOUR_BLACK 0 +#define COLOUR_RED 1 +#define COLOUR_GREEN 2 +#define COLOUR_YELLOW 3 +#define COLOUR_BLUE 4 +#define COLOUR_MAGENTA 5 +#define COLOUR_CYAN 6 +#define COLOUR_WHITE 7 +#define COLOURS 7 + +#define COLOUR_FG 30 +#define COLOUR_BG 40 +#define COLOR_FG COLOUR_FG +#define COLOR_BG COLOUR_BG + +#define COLOR_BLACK COLOUR_BLACK +#define COLOR_BLUE COLOUR_BLUE +#define COLOR_GREEN COLOUR_GREEN +#define COLOR_CYAN COLOUR_CYAN +#define COLOR_RED COLOUR_RED +#define COLOR_MAGENTA COLOUR_MAGENTA +#define COLOR_YELLOW COLOUR_YELLOW +#define COLOR_WHITE COLOUR_WHITE +#define COLORS COLOURS + +/* + * KEY code constants are define in gpxe/keys.h + */ +#include + +//extern int addch ( const chtype * ); +//extern int addchnstr ( const chtype *, int ); +//extern int addchstr ( const chtype * ); +//extern int addnstr ( const char *, int ); +//extern int addstr ( const char * ); +//extern int attroff ( int ); +//extern int attron ( int ); +//extern int attrset ( int ); +//extern int attr_get ( attr_t *, short *, void * ); +//extern int attr_off ( attr_t, void * ); +//extern int attr_on ( attr_t, void * ); +//extern int attr_set ( attr_t, short, void * ); +extern int baudrate ( void ); +extern int beep ( void ); +//extern void bkgdset ( chtype ); +/*extern int border ( chtype, chtype, chtype, chtype, chtype, chtype, chtype, + chtype );*/ +extern int box ( WINDOW *, chtype, chtype ) __nonnull; +//extern bool can_change_colour ( void ); +#define can_change_color() can_change_colour() +extern int cbreak ( void ); +//extern int clrtobot ( void ); +//extern int clrtoeol ( void ); +extern int colour_content ( short, short *, short *, short * ) __nonnull; +#define color_content( c, r, g, b ) colour_content( (c), (r), (g), (b) ) +//extern int colour_set ( short, void * ); +#define color_set( cpno, opts ) colour_set( (cpno), (opts) ) +extern int copywin ( const WINDOW *, WINDOW *, int, int, int, + int, int, int, int ); +extern int curs_set ( int ); +extern int def_prog_mode ( void ); +extern int def_shell_mode ( void ); +extern int delay_output ( int ); +//extern int delch ( void ); +//extern int deleteln ( void ); +extern void delscreen ( SCREEN * ); +extern int delwin ( WINDOW * ) __nonnull; +extern WINDOW *derwin ( WINDOW *, int, int, int, int ) __nonnull; +//extern int doupdate ( void ); +extern WINDOW *dupwin ( WINDOW * ) __nonnull; +extern int echo ( void ); +extern int echochar ( const chtype ); +extern int endwin ( void ); +extern char erasechar ( void ); +//extern int erase ( void ); +extern void filter ( void ); +extern int flash ( void ); +extern int flushinp ( void ); +extern __pure chtype getbkgd ( WINDOW * ) __nonnull; +//extern int getch ( void ); +//extern int getnstr ( char *, int ); +//extern int getstr ( char * ); +extern int halfdelay ( int ); +//extern bool has_colors ( void ); +extern bool has_ic ( void ); +extern bool has_il ( void ); +//extern int hline ( chtype, int ); +extern void idcok ( WINDOW *, bool ); +extern int idlok ( WINDOW *, bool ); +//extern void immedok ( WINDOW *, bool ); +//extern chtype inch ( void ); +//extern int inchnstr ( chtype *, int ); +//extern int inchstr ( chtype * ); +extern WINDOW *initscr ( void ); +extern int init_colour ( short, short, short, short ); +#define init_color ( c, r, g, b ) init_colour ( (c), (r), (g), (b) ) +extern int init_pair ( short, short, short ); +//extern int innstr ( char *, int ); +//extern int insch ( chtype ); +//extern int insnstr ( const char *, int ); +//extern int insstr ( const char * ); +//extern int instr ( char * ); +extern int intrflush ( WINDOW *, bool ); +extern bool isendwin ( void ); +//extern bool is_linetouched ( WINDOW *, int ); +//extern bool is_wintouched ( WINDOW * ); +extern char *keyname ( int ); +extern int keypad ( WINDOW *, bool ); +extern char killchar ( void ); +extern int leaveok ( WINDOW *, bool ); +extern char *longname ( void ); +extern int meta ( WINDOW *, bool ); +//extern int move ( int, int ); +//extern int mvaddch ( int, int, const chtype ); +//extern int mvaddchnstr ( int, int, const chtype *, int ); +//extern int mvaddchstr ( int, int, const chtype * ); +//extern int mvaddnstr ( int, int, const char *, int ); +//extern int mvaddstr ( int, int, const char * ); +extern int mvcur ( int, int, int, int ); +//extern int mvdelch ( int, int ); +extern int mvderwin ( WINDOW *, int, int ); +//extern int mvgetch ( int, int ); +//extern int mvgetnstr ( int, int, char *, int ); +//extern int mvgetstr ( int, int, char * ); +//extern int mvhline ( int, int, chtype, int ); +//extern chtype mvinch ( int, int ); +//extern int mvinchnstr ( int, int, chtype *, int ); +//extern int mvinchstr ( int, int, chtype * ); +//extern int mvinnstr ( int, int, char *, int ); +//extern int mvinsch ( int, int, chtype ); +//extern int mvinsnstr ( int, int, const char *, int ); +//extern int mvinsstr ( int, int, const char * ); +//extern int mvinstr ( int, int, char * ); +//extern int mvprintw ( int, int, char *, ... ); +//extern int mvscanw ( int, int, char *, ... ); +//extern int mvvline ( int, int, chtype, int ); +//extern int mvwaddch ( WINDOW *, int, int, const chtype ); +//extern int mvwaddchnstr ( WINDOW *, int, int, const chtype *, int ); +//extern int mvwaddchstr ( WINDOW *, int, int, const chtype * ); +//extern int mvwaddnstr ( WINDOW *, int, int, const char *, int ); +//extern int mvwaddstr ( WINDOW *, int, int, const char * ); +//extern int mvwdelch ( WINDOW *, int, int ); +//extern int mvwgetch ( WINDOW *, int, int ); +//extern int mvwgetnstr ( WINDOW *, int, int, char *, int ); +//extern int mvwgetstr ( WINDOW *, int, int, char * ); +//extern int mvwhline ( WINDOW *, int, int, chtype, int ); +extern int mvwin ( WINDOW *, int, int ) __nonnull; +//extern chtype mvwinch ( WINDOW *, int, int ); +//extern int mvwinchnstr ( WINDOW *, int, int, chtype *, int ); +//extern int mvwinchstr ( WINDOW *, int, int, chtype * ); +//extern int mvwinnstr ( WINDOW *, int, int, char *, int ); +//extern int mvwinsch ( WINDOW *, int, int, chtype ); +//extern int mvwinsnstr ( WINDOW *, int, int, const char *, int ); +//extern int mvwinsstr ( WINDOW *, int, int, const char * ); +//extern int mvwinstr ( WINDOW *, int, int, char * ); +//extern int mvwprintw ( WINDOW *, int, int, char *, ... ); +//extern int mvwscanw ( WINDOW *, int, int, char *, ... ); +//extern int mvwvline ( WINDOW *, int, int, chtype, int ); +extern int napms ( int ); +//extern WINDOW *newpad ( int, int ); +extern WINDOW *newwin ( int, int, int, int ); +extern int nl ( void ); +extern int nocbreak ( void ); +extern int nodelay ( WINDOW *, bool ); +extern int noecho ( void ); +extern int nonl ( void ); +extern void noqiflush ( void ); +extern int noraw ( void ); +extern int notimeout ( WINDOW *, bool ); +extern int overlay ( const WINDOW *, WINDOW * ); +extern int overwrite ( const WINDOW *, WINDOW * ); +extern int pair_content ( short, short *, short * ) __nonnull; +//extern int pechochar ( WINDOW *, chtype ); +//extern int pnoutrefresh ( WINDOW *, int, int, int, int, int, int ); +//extern int prefresh ( WINDOW *, int, int, int, int, int, int ); +extern int printw ( char *, ... ); +extern int putp ( const char * ); +extern void qiflush ( void ); +extern int raw ( void ); +//extern int redrawwin ( WINDOW * ); +//extern int refresh ( void ); +extern int reset_prog_mode ( void ); +extern int reset_shell_mode ( void ); +extern int resetty ( void ); +extern int ripoffline ( int, int (*) ( WINDOW *, int) ); +extern int savetty ( void ); +//extern int scanw ( char *, ... ); +//extern int scrl ( int ); +//extern int scroll ( WINDOW * ); +//extern int scrollok ( WINDOW *, bool ); +//extern int setscrreg ( int, int ); +extern SCREEN *set_term ( SCREEN * ); +extern int setupterm ( char *, int, int * ); +extern int slk_attr_off ( const attr_t, void * ); +extern int slk_attroff ( const chtype ); +extern int slk_attr_on ( const attr_t, void * ); +extern int slk_attron ( const chtype ); +extern int slk_attr_set ( const attr_t, short, void * ); +extern int slk_attrset ( const chtype ); +extern int slk_clear ( void ); +extern int slk_colour ( short ); +#define slk_color( c ) slk_colour( (c) ) +extern int slk_init ( int ); +extern char *slk_label ( int ); +extern int slk_noutrefresh ( void ); +//extern int slk_refresh ( void ); +extern int slk_restore ( void ); +extern int slk_set ( int, const char *, int ) __nonnull; +extern int slk_touch ( void ); +extern int standend ( void ); +extern int standout ( void ); +//extern int start_colour ( void ); +#define start_color() start_colour() +//extern WINDOW *subpad ( WINDOW *, int, int, int, int ); +extern WINDOW *subwin ( WINDOW *, int, int, int, int ) __nonnull; +extern int syncok ( WINDOW *, bool ); +extern chtype termattrs ( void ); +extern attr_t term_attrs ( void ); +extern char *termname ( void ); +extern int tigetflag ( char * ); +extern int tigetnum ( char * ); +extern char *tigetstr ( char * ); +extern void timeout ( int ); +//extern int touchline ( WINDOW *, int, int ); +//extern int touchwin ( WINDOW * ); +extern char *tparm ( char *, long, long, long, long, long, long, long, long, + long ); +extern int typeahead ( int ); +//extern int ungetch ( int ); +//extern int untouchwin ( WINDOW * ); +extern void use_env ( bool ); +extern int vid_attr ( attr_t, short, void * ); +extern int vidattr ( chtype ); +extern int vid_puts ( attr_t, short, void *, int ( *) ( int) ); +extern int vidputs ( chtype, int ( *) ( int) ); +//extern int vline ( chtype, int ); +//extern int vwprintw ( WINDOW *, const char *, va_list ); +extern int vw_printw ( WINDOW *, const char *, va_list ) __nonnull; +//extern int vwscanw ( WINDOW *, char *, va_list ); +//extern int vw_scanw ( WINDOW *, char *, va_list ); +extern int waddch ( WINDOW *, const chtype ) __nonnull; +extern int waddchnstr ( WINDOW *, const chtype *, int ) __nonnull; +//extern int waddchstr ( WINDOW *, const chtype * ); +extern int waddnstr ( WINDOW *, const char *, int ) __nonnull; +//extern int waddstr ( WINDOW *, const char * ); +extern int wattroff ( WINDOW *, int ) __nonnull; +extern int wattron ( WINDOW *, int ) __nonnull; +extern int wattrset ( WINDOW *, int ) __nonnull; +extern int wattr_get ( WINDOW *, attr_t *, short *, void * ) + __attribute__ (( nonnull (1, 2, 3))); +extern int wattr_off ( WINDOW *, attr_t, void * ) + __attribute__ (( nonnull (1))); +extern int wattr_on ( WINDOW *, attr_t, void * ) + __attribute__ (( nonnull (1))); +extern int wattr_set ( WINDOW *, attr_t, short, void * ) + __attribute__ (( nonnull (1))); +//extern void wbkgdset ( WINDOW *, chtype ); +extern int wborder ( WINDOW *, chtype, chtype, chtype, chtype, chtype, chtype, + chtype, chtype ) __nonnull; +extern int wclrtobot ( WINDOW * ) __nonnull; +extern int wclrtoeol ( WINDOW * ) __nonnull; +extern void wcursyncup ( WINDOW * ); +extern int wcolour_set ( WINDOW *, short, void * ) __nonnull; +#define wcolor_set(w,s,v) wcolour_set((w),(s),(v)) +extern int wdelch ( WINDOW * ) __nonnull; +extern int wdeleteln ( WINDOW * ) __nonnull; +extern int wechochar ( WINDOW *, const chtype ); +extern int werase ( WINDOW * ) __nonnull; +extern int wgetch ( WINDOW * ); +extern int wgetnstr ( WINDOW *, char *, int ); +//extern int wgetstr ( WINDOW *, char * ); +extern int whline ( WINDOW *, chtype, int ) __nonnull; +//extern chtype winch ( WINDOW * ); +//extern int winchnstr ( WINDOW *, chtype *, int ); +//extern int winchstr ( WINDOW *, chtype * ); +//extern int winnstr ( WINDOW *, char *, int ); +//extern int winsch ( WINDOW *, chtype ); +//extern int winsnstr ( WINDOW *, const char *, int ); +//extern int winsstr ( WINDOW *, const char * ); +//extern int winstr ( WINDOW *, char * ); +extern int wmove ( WINDOW *, int, int ); +//extern int wnoutrefresh ( WINDOW * ); +extern int wprintw ( WINDOW *, const char *, ... ) __nonnull; +//extern int wredrawln ( WINDOW *, int, int ); +//extern int wrefresh ( WINDOW * ); +//extern int wscanw ( WINDOW *, char *, ... ); +//extern int wscrl ( WINDOW *, int ); +//extern int wsetscrreg ( WINDOW *, int, int ); +//extern int wstandend ( WINDOW * ); +//extern int wstandout ( WINDOW * ); +extern void wsyncup ( WINDOW * ); +extern void wsyncdown ( WINDOW * ); +extern void wtimeout ( WINDOW *, int ); +//extern int wtouchln ( WINDOW *, int, int, int ); +extern int wvline ( WINDOW *, chtype, int ) __nonnull; + +/* + * There is frankly a ridiculous amount of redundancy within the + * curses API - ncurses decided to get around this by using #define + * macros, but I've decided to be type-safe and implement them all as + * static inlines instead... + */ + +static inline int addch ( const chtype ch ) { + return waddch( stdscr, ch ); +} + +static inline int addchnstr ( const chtype *chstr, int n ) { + return waddchnstr ( stdscr, chstr, n ); +} + +static inline int addchstr ( const chtype *chstr ) { + return waddchnstr ( stdscr, chstr, -1 ); +} + +static inline int addnstr ( const char *str, int n ) { + return waddnstr ( stdscr, str, n ); +} + +static inline int addstr ( const char *str ) { + return waddnstr ( stdscr, str, -1 ); +} + +static inline int attroff ( int attrs ) { + return wattroff ( stdscr, attrs ); +} + +static inline int attron ( int attrs ) { + return wattron ( stdscr, attrs ); +} + +static inline int attrset ( int attrs ) { + return wattrset ( stdscr, attrs ); +} + +static inline int attr_get ( attr_t *attrs, short *pair, void *opts ) { + return wattr_get ( stdscr, attrs, pair, opts ); +} + +static inline int attr_off ( attr_t attrs, void *opts ) { + return wattr_off ( stdscr, attrs, opts ); +} + +static inline int attr_on ( attr_t attrs, void *opts ) { + return wattr_on ( stdscr, attrs, opts ); +} + +static inline int attr_set ( attr_t attrs, short cpair, void *opts ) { + return wattr_set ( stdscr, attrs, cpair, opts ); +} + +static inline void bkgdset ( chtype ch ) { + wattrset ( stdscr, ch ); +} + +static inline int border ( chtype ls, chtype rs, chtype ts, chtype bs, + chtype tl, chtype tr, chtype bl, chtype br ) { + return wborder ( stdscr, ls, rs, ts, bs, tl, tr, bl, br ); +} + +static inline bool can_change_colour ( void ) { + return FALSE; +} + +static inline int clrtobot ( void ) { + return wclrtobot( stdscr ); +} + +static inline int clrtoeol ( void ) { + return wclrtoeol( stdscr ); +} + +static inline int colour_set ( short colour_pair_number, void *opts ) { + return wcolour_set ( stdscr, colour_pair_number, opts ); +} + +static inline int delch ( void ) { + return wdelch ( stdscr ); +} + +static inline int deleteln ( void ) { + return wdeleteln( stdscr ); +} + +static inline int erase ( void ) { + return werase ( stdscr ); +} + +static inline int getch ( void ) { + return wgetch ( stdscr ); +} + +static inline int getnstr ( char *str, int n ) { + return wgetnstr ( stdscr, str, n ); +} + +static inline int getstr ( char *str ) { + return wgetnstr ( stdscr, str, -1 ); +} + +static inline bool has_colors ( void ) { + return TRUE; +} + +static inline int has_key ( int kc __unused ) { + return TRUE; +} + +static inline int hline ( chtype ch, int n ) { + return whline ( stdscr, ch, n ); +} + +static inline int move ( int y, int x ) { + return wmove ( stdscr, y, x ); +} + +static inline int mvaddch ( int y, int x, const chtype ch ) { + return ( wmove ( stdscr, y, x ) == OK + ? waddch( stdscr, ch ) : ERR ); +} + +static inline int mvaddchnstr ( int y, int x, const chtype *chstr, int n ) { + return ( wmove ( stdscr, y, x ) == OK + ? waddchnstr ( stdscr, chstr, n ) : ERR ); +} + +static inline int mvaddchstr ( int y, int x, const chtype *chstr ) { + return ( wmove ( stdscr, y, x ) == OK + ? waddchnstr ( stdscr, chstr, -1 ) : ERR ); +} + +static inline int mvaddnstr ( int y, int x, const char *str, int n ) { + return ( wmove ( stdscr, y, x ) == OK + ? waddnstr ( stdscr, str, n ) : ERR ); +} + +static inline int mvaddstr ( int y, int x, const char *str ) { + return ( wmove ( stdscr, y, x ) == OK + ? waddnstr ( stdscr, str, -1 ) : ERR ); +} + +static inline int mvdelch ( int y, int x ) { + return ( wmove ( stdscr, y, x ) == OK + ? wdelch ( stdscr ) : ERR ); +} + +static inline int mvgetch ( int y, int x ) { + return ( wmove ( stdscr, y, x ) == OK + ? wgetch ( stdscr ) : ERR ); +} + +static inline int mvgetnstr ( int y, int x, char *str, int n ) { + return ( wmove ( stdscr, y, x ) == OK + ? wgetnstr ( stdscr, str, n ) : ERR ); +} + +static inline int mvgetstr ( int y, int x, char *str ) { + return ( wmove ( stdscr, y, x ) == OK + ? wgetnstr ( stdscr, str, -1 ) : ERR ); +} + +static inline int mvhline ( int y, int x, chtype ch, int n ) { + return ( wmove ( stdscr, y, x ) == OK + ? whline ( stdscr, ch, n ) : ERR ); +} + +// OK, so maybe a few I did with macros... +#define mvprintw( y, x, fmt, ... ) \ + ( wmove(stdscr,(y),(x)) == OK \ + ? wprintw( stdscr,(fmt), ## __VA_ARGS__ ) : ERR ) + +static inline int mvvline ( int y, int x, chtype ch, int n ) { + return ( wmove ( stdscr, y, x ) == OK + ? wvline ( stdscr, ch, n ) : ERR ); +} + +static inline int mvwaddch ( WINDOW *win, int y, int x, const chtype ch ) { + return ( wmove( win, y, x ) == OK + ? waddch ( win, ch ) : ERR ); +} + +static inline int mvwaddchnstr ( WINDOW *win, int y, int x, const chtype *chstr, int n ) { + return ( wmove ( win, y, x ) == OK + ? waddchnstr ( win, chstr, n ) : ERR ); +} + +static inline int mvwaddchstr ( WINDOW *win, int y, int x, const chtype *chstr ) { + return ( wmove ( win, y, x ) == OK + ? waddchnstr ( win, chstr, -1 ) : ERR ); +} + +static inline int mvwaddnstr ( WINDOW *win, int y, int x, const char *str, int n ) { + return ( wmove ( win, y, x ) == OK + ? waddnstr ( win, str, n ) : ERR ); +} + +static inline int mvwaddstr ( WINDOW *win, int y, int x, const char *str ) { + return ( wmove ( win, y, x ) == OK + ? waddnstr ( win, str, -1 ) : ERR ); +} + +static inline int mvwdelch ( WINDOW *win, int y, int x ) { + return ( wmove ( win, y, x ) == OK + ? wdelch ( win ) : ERR ); +} + +static inline int mvwgetch ( WINDOW *win, int y, int x ) { + return ( wmove ( win, y, x ) == OK + ? wgetch ( win ) : ERR ); +} + +static inline int mvwgetnstr ( WINDOW *win, int y, int x, char *str, int n ) { + return ( wmove ( win, y, x ) == OK + ? wgetnstr ( win, str, n ) : ERR ); +} + +static inline int mvwgetstr ( WINDOW *win, int y, int x, char *str ) { + return ( wmove ( win, y, x ) == OK + ? wgetnstr ( win, str, -1 ) : ERR ); +} + +static inline int mvwhline ( WINDOW *win, int y, int x, chtype ch, int n ) { + return ( wmove ( win, y, x ) == OK + ? whline ( win, ch, n ) : ERR ); +} + +#define mvwprintw( win, y, x, fmt, ... ) \ + ( wmove((win),(y),(x)) == OK \ + ? wprintw((win),(fmt), ## __VA_ARGS__) : ERR ) + +static inline int mvwvline ( WINDOW *win, int y, int x, chtype ch, int n ) { + return ( wmove ( win, y, x ) == OK + ? wvline ( win, ch, n ) : ERR ); +} + +#define printw( fmt, ... ) wprintw(stdscr,(fmt), ## __VA_ARGS__ ) + +static inline int slk_refresh ( void ) { + if ( slk_clear() == OK ) + return slk_restore(); + else + return ERR; +} + +#define standend() wstandend( stdscr ) +#define standout() wstandout( stdscr ) + +static inline int start_colour ( void ) { + return OK; +} + +static inline int vline ( chtype ch, int n ) { + return wvline ( stdscr, ch, n ); +} + +// marked for removal +static inline int vwprintw ( WINDOW *win, const char *fmt, va_list varglist ) { + return vw_printw ( win, fmt, varglist ); +} + +static inline int waddchstr ( WINDOW *win, const chtype *chstr ) { + return waddchnstr ( win, chstr, -1 ); +} + +static inline int waddstr ( WINDOW *win, const char *str ) { + return waddnstr ( win, str, -1 ); +} + +static inline int wbkgdset ( WINDOW *win, chtype ch ) { + return wattrset( win, ch ); +} + +static inline int wgetstr ( WINDOW *win, char *str ) { + return wgetnstr ( win, str, -1 ); +} + +static inline int wstandend ( WINDOW *win ) { + return wattrset ( win, A_DEFAULT ); +} + +static inline int wstandout ( WINDOW *win ) { + return wattrset ( win, A_STANDOUT ); +} + +#endif /* CURSES_H */ diff --git a/gpxe/src/include/debug.h b/gpxe/src/include/debug.h new file mode 100644 index 0000000..bb5d33f --- /dev/null +++ b/gpxe/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/gpxe/src/include/dhcp.h b/gpxe/src/include/dhcp.h new file mode 100644 index 0000000..deba219 --- /dev/null +++ b/gpxe/src/include/dhcp.h @@ -0,0 +1,12 @@ +#ifndef DHCP_H +#define DHCP_H + +#include "stdint.h" + +struct dhcp_dev_id { + uint8_t bus_type; + uint16_t vendor_id; + uint16_t device_id; +} __attribute__ (( packed )); + +#endif /* DHCP_H */ diff --git a/gpxe/src/include/elf.h b/gpxe/src/include/elf.h new file mode 100644 index 0000000..fadc0bd --- /dev/null +++ b/gpxe/src/include/elf.h @@ -0,0 +1,232 @@ +#ifndef ELF_H +#define ELF_H + +#define EI_NIDENT 16 /* Size of e_ident array. */ + +/* Values for e_type. */ +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* Relocatable file */ +#define ET_EXEC 2 /* Executable file */ +#define ET_DYN 3 /* Shared object file */ +#define ET_CORE 4 /* Core file */ + +/* Values for e_machine (architecute). */ +#define EM_NONE 0 /* No machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SUN SPARC */ +#define EM_386 3 /* Intel 80386+ */ +#define EM_68K 4 /* Motorola m68k family */ +#define EM_88K 5 /* Motorola m88k family */ +#define EM_486 6 /* Perhaps disused */ +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS R3000 big-endian */ +#define EM_S370 9 /* IBM System/370 */ +#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ + +#define EM_PARISC 15 /* HPPA */ +#define EM_VPP500 17 /* Fujitsu VPP500 */ +#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ +#define EM_960 19 /* Intel 80960 */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* PowerPC 64-bit */ +#define EM_S390 22 /* IBM S390 */ + +#define EM_V800 36 /* NEC V800 series */ +#define EM_FR20 37 /* Fujitsu FR20 */ +#define EM_RH32 38 /* TRW RH-32 */ +#define EM_RCE 39 /* Motorola RCE */ +#define EM_ARM 40 /* ARM */ +#define EM_FAKE_ALPHA 41 /* Digital Alpha */ +#define EM_SH 42 /* Hitachi SH */ +#define EM_SPARCV9 43 /* SPARC v9 64-bit */ +#define EM_TRICORE 44 /* Siemens Tricore */ +#define EM_ARC 45 /* Argonaut RISC Core */ +#define EM_H8_300 46 /* Hitachi H8/300 */ +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ +#define EM_H8_500 49 /* Hitachi H8/500 */ +#define EM_IA_64 50 /* Intel Merced */ +#define EM_MIPS_X 51 /* Stanford MIPS-X */ +#define EM_COLDFIRE 52 /* Motorola Coldfire */ +#define EM_68HC12 53 /* Motorola M68HC12 */ +#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ +#define EM_PCP 55 /* Siemens PCP */ +#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ +#define EM_NDR1 57 /* Denso NDR1 microprocessor */ +#define EM_STARCORE 58 /* Motorola Start*Core processor */ +#define EM_ME16 59 /* Toyota ME16 processor */ +#define EM_ST100 60 /* STMicroelectronic ST100 processor */ +#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ +#define EM_X86_64 62 /* AMD x86-64 architecture */ +#define EM_PDSP 63 /* Sony DSP Processor */ + +#define EM_FX66 66 /* Siemens FX66 microcontroller */ +#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ +#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ +#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ +#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ +#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ +#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ +#define EM_SVX 73 /* Silicon Graphics SVx */ +#define EM_AT19 74 /* STMicroelectronics ST19 8 bit mc */ +#define EM_VAX 75 /* Digital VAX */ +#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ +#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ +#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ +#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ +#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ +#define EM_HUANY 81 /* Harvard University machine-independent object files */ +#define EM_PRISM 82 /* SiTera Prism */ +#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ +#define EM_FR30 84 /* Fujitsu FR30 */ +#define EM_D10V 85 /* Mitsubishi D10V */ +#define EM_D30V 86 /* Mitsubishi D30V */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Mitsubishi M32R */ +#define EM_MN10300 89 /* Matsushita MN10300 */ +#define EM_MN10200 90 /* Matsushita MN10200 */ +#define EM_PJ 91 /* picoJava */ +#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ +#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ +#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define EM_NUM 95 + +/* Values for p_type. */ +#define PT_NULL 0 /* Unused entry. */ +#define PT_LOAD 1 /* Loadable segment. */ +#define PT_DYNAMIC 2 /* Dynamic linking information segment. */ +#define PT_INTERP 3 /* Pathname of interpreter. */ +#define PT_NOTE 4 /* Auxiliary information. */ +#define PT_SHLIB 5 /* Reserved (not used). */ +#define PT_PHDR 6 /* Location of program header itself. */ + +/* Values for p_flags. */ +#define PF_X 0x1 /* Executable. */ +#define PF_W 0x2 /* Writable. */ +#define PF_R 0x4 /* Readable. */ + + +#define ELF_PROGRAM_RETURNS_BIT 0x8000000 /* e_flags bit 31 */ + +#define EI_MAG0 0 +#define ELFMAG0 0x7f + +#define EI_MAG1 1 +#define ELFMAG1 'E' + +#define EI_MAG2 2 +#define ELFMAG2 'L' + +#define EI_MAG3 3 +#define ELFMAG3 'F' + +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASSNONE 0 /* Invalid class */ +#define ELFCLASS32 1 /* 32-bit objects */ +#define ELFCLASS64 2 /* 64-bit objects */ + +#define EI_DATA 5 /* Data encodeing byte index */ +#define ELFDATANONE 0 /* Invalid data encoding */ +#define ELFDATA2LSB 1 /* 2's complement little endian */ +#define ELFDATA2MSB 2 /* 2's complement big endian */ + +#define EI_VERSION 6 /* File version byte index */ + /* Value must be EV_CURRENT */ + +#define EV_NONE 0 /* Invalid ELF Version */ +#define EV_CURRENT 1 /* Current version */ + +#define ELF32_PHDR_SIZE (8*4) /* Size of an elf program header */ + +#ifndef ASSEMBLY + +#include + +/* + * 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 */ + +#endif /* ASSEMBLY */ + +#endif /* ELF_H */ diff --git a/gpxe/src/include/endian.h b/gpxe/src/include/endian.h new file mode 100644 index 0000000..3200622 --- /dev/null +++ b/gpxe/src/include/endian.h @@ -0,0 +1,19 @@ +#ifndef ETHERBOOT_ENDIAN_H +#define ETHERBOOT_ENDIAN_H + +/* Definitions for byte order, according to significance of bytes, + from low addresses to high addresses. The value is what you get by + putting '4' in the most significant byte, '3' in the second most + significant byte, '2' in the second least significant byte, and '1' + in the least significant byte, and then writing down one digit for + each byte, starting with the byte at the lowest address at the left, + and proceeding to the byte with the highest address at the right. */ + +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 +#define __PDP_ENDIAN 3412 + +#include "bits/endian.h" + + +#endif /* ETHERBOOT_ENDIAN_H */ diff --git a/gpxe/src/include/errno.h b/gpxe/src/include/errno.h new file mode 100644 index 0000000..58dff1f --- /dev/null +++ b/gpxe/src/include/errno.h @@ -0,0 +1,508 @@ +#ifndef ERRNO_H +#define ERRNO_H + +/** @file + * + * Error codes + * + * Return status codes as used within gPXE are designed to allow for + * maximum visibility into the source of an error even in an end-user + * build with no debugging. They are constructed as follows: + * + * Bits 7-0 : PXE error code + * + * This is the closest equivalent PXE error code + * (e.g. PXENV_STATUS_OUT_OF_RESOURCES), and is the only part of the + * error that will be returned via the PXE API, since PXE has + * predefined error codes. + * + * Bits 12-8 : Per-file disambiguator + * + * When the same error number can be generated from multiple points + * within a file, this field can be used to identify the unique + * instance. + * + * Bits 23-13 : File identifier + * + * This is a unique identifier for the file generating the error + * (e.g. ERRFILE_tcp for tcp.c). + * + * Bits 30-24 : POSIX error code + * + * This is the closest equivalent POSIX error code (e.g. ENOMEM). + * + * Bit 31 : Reserved + * + * Errors are usually return as negative error numbers (e.g. -EINVAL); + * bit 31 is therefore unusable. + * + * + * The convention within the code is that errors are negative and + * expressed using the POSIX error code and (optionally) a per-file + * disambiguator, e.g. + * + * return -EINVAL; + * + * or + * + * #define ETCP_BAD_CHECKSUM EUNIQ_02 + * return -( EINVAL | ETCP_BAD_CHECKSUM ) + * + * By various bits of preprocessor magic, the PXE error code and file + * identifier are already incorporated into the definition of the + * POSIX error code, which keeps the code relatively clean. + * + * + * Functions that wish to return failures should be declared as + * returning an integer @c rc "Return status code". A return value of + * zero indicates success, a non-zero value indicates failure. The + * return value can be passed directly to strerror() in order to + * generate a human-readable error message, e.g. + * + * if ( ( rc = some_function ( ... ) ) != 0 ) { + * DBG ( "Whatever I was trying to do failed: %s\n", strerror ( rc ) ); + * return rc; + * } + * + * As illustrated in the above example, error returns should generally + * be directly propagated upward to the calling function. + * + */ + +/* Get definitions for file identifiers */ +#include + +/* If we do not have a valid file identifier, generate a compiler + * warning upon usage of any error codes. (Don't just use a #warning, + * because some files include errno.h but don't ever actually use any + * error codes.) + */ +#if ! ERRFILE +extern char missing_errfile_declaration[] __attribute__ (( deprecated )); +#undef ERRFILE +#define ERRFILE ( 0 * ( ( int ) missing_errfile_declaration ) ) +#endif + +/** Derive PXENV_STATUS code from gPXE error number */ +#define PXENV_STATUS( rc ) ( (-(rc)) & 0x00ff ) + +/** + * @defgroup pxeerrors PXE error codes + * + * The names, meanings and values of these error codes are defined by + * the PXE specification. + * + * @{ + */ + +/* Generic errors */ +#define PXENV_STATUS_SUCCESS 0x0000 +#define PXENV_STATUS_FAILURE 0x0001 +#define PXENV_STATUS_BAD_FUNC 0x0002 +#define PXENV_STATUS_UNSUPPORTED 0x0003 +#define PXENV_STATUS_KEEP_UNDI 0x0004 +#define PXENV_STATUS_KEEP_ALL 0x0005 +#define PXENV_STATUS_OUT_OF_RESOURCES 0x0006 + +/* ARP errors (0x0010 to 0x001f) */ +#define PXENV_STATUS_ARP_TIMEOUT 0x0011 + +/* Base-Code state errors */ +#define PXENV_STATUS_UDP_CLOSED 0x0018 +#define PXENV_STATUS_UDP_OPEN 0x0019 +#define PXENV_STATUS_TFTP_CLOSED 0x001a +#define PXENV_STATUS_TFTP_OPEN 0x001b + +/* BIOS/system errors (0x0020 to 0x002f) */ +#define PXENV_STATUS_MCOPY_PROBLEM 0x0020 +#define PXENV_STATUS_BIS_INTEGRITY_FAILURE 0x0021 +#define PXENV_STATUS_BIS_VALIDATE_FAILURE 0x0022 +#define PXENV_STATUS_BIS_INIT_FAILURE 0x0023 +#define PXENV_STATUS_BIS_SHUTDOWN_FAILURE 0x0024 +#define PXENV_STATUS_BIS_GBOA_FAILURE 0x0025 +#define PXENV_STATUS_BIS_FREE_FAILURE 0x0026 +#define PXENV_STATUS_BIS_GSI_FAILURE 0x0027 +#define PXENV_STATUS_BIS_BAD_CKSUM 0x0028 + +/* TFTP/MTFTP errors (0x0030 to 0x003f) */ +#define PXENV_STATUS_TFTP_CANNOT_ARP_ADDRESS 0x0030 +#define PXENV_STATUS_TFTP_OPEN_TIMEOUT 0x0032 +#define PXENV_STATUS_TFTP_UNKNOWN_OPCODE 0x0033 +#define PXENV_STATUS_TFTP_READ_TIMEOUT 0x0035 +#define PXENV_STATUS_TFTP_ERROR_OPCODE 0x0036 +#define PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION 0x0038 +#define PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION 0x0039 +#define PXENV_STATUS_TFTP_TOO_MANY_PACKAGES 0x003a +#define PXENV_STATUS_TFTP_FILE_NOT_FOUND 0x003b +#define PXENV_STATUS_TFTP_ACCESS_VIOLATION 0x003c +#define PXENV_STATUS_TFTP_NO_MCAST_ADDRESS 0x003d +#define PXENV_STATUS_TFTP_NO_FILESIZE 0x003e +#define PXENV_STATUS_TFTP_INVALID_PACKET_SIZE 0x003f + +/* Reserved errors 0x0040 to 0x004f) */ + +/* DHCP/BOOTP errors (0x0050 to 0x005f) */ +#define PXENV_STATUS_DHCP_TIMEOUT 0x0051 +#define PXENV_STATUS_DHCP_NO_IP_ADDRESS 0x0052 +#define PXENV_STATUS_DHCP_NO_BOOTFILE_NAME 0x0053 +#define PXENV_STATUS_DHCP_BAD_IP_ADDRESS 0x0054 + +/* Driver errors (0x0060 to 0x006f) */ +#define PXENV_STATUS_UNDI_INVALID_FUNCTION 0x0060 +#define PXENV_STATUS_UNDI_MEDIATEST_FAILED 0x0061 +#define PXENV_STATUS_UNDI_CANNOT_INIT_NIC_FOR_MCAST 0x0062 +#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC 0x0063 +#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_PHY 0x0064 +#define PXENV_STATUS_UNDI_CANNOT_READ_CONFIG_DATA 0x0065 +#define PXENV_STATUS_UNDI_CANNOT_READ_INIT_DATA 0x0066 +#define PXENV_STATUS_UNDI_BAD_MAC_ADDRESS 0x0067 +#define PXENV_STATUS_UNDI_BAD_EEPROM_CHECKSUM 0x0068 +#define PXENV_STATUS_UNDI_ERROR_SETTING_ISR 0x0069 +#define PXENV_STATUS_UNDI_INVALID_STATE 0x006a +#define PXENV_STATUS_UNDI_TRANSMIT_ERROR 0x006b +#define PXENV_STATUS_UNDI_INVALID_PARAMETER 0x006c + +/* ROM and NBP bootstrap errors (0x0070 to 0x007f) */ +#define PXENV_STATUS_BSTRAP_PROMPT_MENU 0x0074 +#define PXENV_STATUS_BSTRAP_MCAST_ADDR 0x0076 +#define PXENV_STATUS_BSTRAP_MISSING_LIST 0x0077 +#define PXENV_STATUS_BSTRAP_NO_RESPONSE 0x0078 +#define PXENV_STATUS_BSTRAP_FILE_TOO_BIG 0x0079 + +/* Environment NBP errors (0x0080 to 0x008f) */ + +/* Reserved errors (0x0090 to 0x009f) */ + +/* Miscellaneous errors (0x00a0 to 0x00af) */ +#define PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE 0x00a0 +#define PXENV_STATUS_BINL_NO_PXE_SERVER 0x00a1 +#define PXENV_STATUS_NOT_AVAILABLE_IN_PMODE 0x00a2 +#define PXENV_STATUS_NOT_AVAILABLE_IN_RMODE 0x00a3 + +/* BUSD errors (0x00b0 to 0x00bf) */ +#define PXENV_STATUS_BUSD_DEVICE_NOT_SUPPORTED 0x00b0 + +/* Loader errors (0x00c0 to 0x00cf) */ +#define PXENV_STATUS_LOADER_NO_FREE_BASE_MEMORY 0x00c0 +#define PXENV_STATUS_LOADER_NO_BC_ROMID 0x00c1 +#define PXENV_STATUS_LOADER_BAD_BC_ROMID 0x00c2 +#define PXENV_STATUS_LOADER_BAD_BC_RUNTIME_IMAGE 0x00c3 +#define PXENV_STATUS_LOADER_NO_UNDI_ROMID 0x00c4 +#define PXENV_STATUS_LOADER_BAD_UNDI_ROMID 0x00c5 +#define PXENV_STATUS_LOADER_BAD_UNDI_DRIVER_IMAGE 0x00c6 +#define PXENV_STATUS_LOADER_NO_PXE_STRUCT 0x00c8 +#define PXENV_STATUS_LOADER_NO_PXENV_STRUCT 0x00c9 +#define PXENV_STATUS_LOADER_UNDI_START 0x00ca +#define PXENV_STATUS_LOADER_BC_START 0x00cb + +/** @} */ + +/** + * @defgroup posixerrors POSIX error codes + * + * The names and meanings (but not the values) of these error codes + * are defined by POSIX. We choose to assign unique values which + * incorporate the closest equivalent PXE error code, so that code may + * simply use ENOMEM, rather than having to use the cumbersome + * (ENOMEM|PXENV_STATUS_OUT_OF_RESOURCES). + * + * @{ + */ + +/** Operation completed successfully */ +#define ENOERR ( ERRFILE | PXENV_STATUS_SUCCESS | 0x00000000 ) + +/** Arg list too long */ +#define E2BIG ( ERRFILE | PXENV_STATUS_BAD_FUNC | 0x01000000 ) + +/** Permission denied */ +#define EACCES ( ERRFILE | PXENV_STATUS_TFTP_ACCESS_VIOLATION | 0x02000000 ) + +/** Address in use */ +#define EADDRINUSE ( ERRFILE | PXENV_STATUS_UDP_OPEN | 0x03000000 ) + +/** Address not available */ +#define EADDRNOTAVAIL ( ERRFILE | PXENV_STATUS_UDP_OPEN | 0x04000000 ) + +/** Address family not supported */ +#define EAFNOSUPPORT ( ERRFILE | PXENV_STATUS_UNSUPPORTED | 0x05000000 ) + +/** Resource temporarily unavailable */ +#define EAGAIN ( ERRFILE | PXENV_STATUS_FAILURE | 0x06000000 ) + +/** Connection already in progress */ +#define EALREADY ( ERRFILE | PXENV_STATUS_UDP_OPEN | 0x07000000 ) + +/** Bad file descriptor */ +#define EBADF ( ERRFILE | PXENV_STATUS_TFTP_CLOSED | 0x08000000 ) + +/** Bad message */ +#define EBADMSG ( ERRFILE | PXENV_STATUS_FAILURE | 0x09000000 ) + +/** Resource busy */ +#define EBUSY ( ERRFILE | PXENV_STATUS_OUT_OF_RESOURCES | 0x0a000000 ) + +/** Operation canceled */ +#define ECANCELED \ + ( ERRFILE | PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE | 0x0b000000 ) + +/** No child processes */ +#define ECHILD ( ERRFILE | PXENV_STATUS_TFTP_FILE_NOT_FOUND | 0x0c000000 ) + +/** Connection aborted */ +#define ECONNABORTED \ + ( ERRFILE | PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION | 0x0d000000 ) + +/** Connection refused */ +#define ECONNREFUSED \ + ( ERRFILE | PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION | 0x0e000000 ) + +/** Connection reset */ +#define ECONNRESET \ + ( ERRFILE | PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION | 0x0f000000 ) + +/** Resource deadlock avoided */ +#define EDEADLK ( ERRFILE | PXENV_STATUS_FAILURE | 0x10000000 ) + +/** Destination address required */ +#define EDESTADDRREQ ( ERRFILE | PXENV_STATUS_BAD_FUNC | 0x11000000 ) + +/** Domain error */ +#define EDOM ( ERRFILE | PXENV_STATUS_FAILURE | 0x12000000 ) + +/** Reserved */ +#define EDQUOT ( ERRFILE | PXENV_STATUS_FAILURE | 0x13000000 ) + +/** File exists */ +#define EEXIST ( ERRFILE | PXENV_STATUS_FAILURE | 0x14000000 ) + +/** Bad address */ +#define EFAULT ( ERRFILE | PXENV_STATUS_MCOPY_PROBLEM | 0x15000000 ) + +/** File too large */ +#define EFBIG ( ERRFILE | PXENV_STATUS_MCOPY_PROBLEM | 0x16000000 ) + +/** Host is unreachable */ +#define EHOSTUNREACH ( ERRFILE | PXENV_STATUS_ARP_TIMEOUT | 0x17000000 ) + +/** Identifier removed */ +#define EIDRM ( ERRFILE | PXENV_STATUS_FAILURE | 0x18000000 ) + +/** Illegal byte sequence */ +#define EILSEQ ( ERRFILE | PXENV_STATUS_FAILURE | 0x19000000 ) + +/** Operation in progress */ +#define EINPROGRESS ( ERRFILE | PXENV_STATUS_FAILURE | 0x1a000000 ) + +/** Interrupted function call */ +#define EINTR ( ERRFILE | PXENV_STATUS_FAILURE | 0x1b000000 ) + +/** Invalid argument */ +#define EINVAL ( ERRFILE | PXENV_STATUS_BAD_FUNC | 0x1c000000 ) + +/** Input/output error */ +#define EIO \ + ( ERRFILE | PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION | 0x1d000000 ) + +/** Socket is connected */ +#define EISCONN ( ERRFILE | PXENV_STATUS_UDP_OPEN | 0x1e000000 ) + +/** Is a directory */ +#define EISDIR ( ERRFILE | PXENV_STATUS_FAILURE | 0x1f000000 ) + +/** Too many levels of symbolic links */ +#define ELOOP ( ERRFILE | PXENV_STATUS_FAILURE | 0x20000000 ) + +/** Too many open files */ +#define EMFILE ( ERRFILE | PXENV_STATUS_OUT_OF_RESOURCES | 0x21000000 ) + +/** Too many links */ +#define EMLINK ( ERRFILE | PXENV_STATUS_FAILURE | 0x22000000 ) + +/** Inappropriate message buffer length */ +#define EMSGSIZE ( ERRFILE | PXENV_STATUS_BAD_FUNC | 0x23000000 ) + +/** Reserved */ +#define EMULTIHOP ( ERRFILE | PXENV_STATUS_FAILURE | 0x24000000 ) + +/** Filename too long */ +#define ENAMETOOLONG ( ERRFILE | PXENV_STATUS_FAILURE | 0x25000000 ) + +/** Network is down */ +#define ENETDOWN ( ERRFILE | PXENV_STATUS_ARP_TIMEOUT | 0x26000000 ) + +/** Connection aborted by network */ +#define ENETRESET ( ERRFILE | PXENV_STATUS_FAILURE | 0x27000000 ) + +/** Network unreachable */ +#define ENETUNREACH ( ERRFILE | PXENV_STATUS_ARP_TIMEOUT | 0x28000000 ) + +/** Too many open files in system */ +#define ENFILE ( ERRFILE | PXENV_STATUS_OUT_OF_RESOURCES | 0x29000000 ) + +/** No buffer space available */ +#define ENOBUFS ( ERRFILE | PXENV_STATUS_OUT_OF_RESOURCES | 0x2a000000 ) + +/** No message is available on the STREAM head read queue */ +#define ENODATA ( ERRFILE | PXENV_STATUS_FAILURE | 0x2b000000 ) + +/** No such device */ +#define ENODEV ( ERRFILE | PXENV_STATUS_TFTP_FILE_NOT_FOUND | 0x2c000000 ) + +/** No such file or directory */ +#define ENOENT ( ERRFILE | PXENV_STATUS_TFTP_FILE_NOT_FOUND | 0x2d000000 ) + +/** Exec format error */ +#define ENOEXEC ( ERRFILE | PXENV_STATUS_FAILURE | 0x2e000000 ) + +/** No locks available */ +#define ENOLCK ( ERRFILE | PXENV_STATUS_FAILURE | 0x2f000000 ) + +/** Reserved */ +#define ENOLINK ( ERRFILE | PXENV_STATUS_FAILURE | 0x30000000 ) + +/** Not enough space */ +#define ENOMEM ( ERRFILE | PXENV_STATUS_OUT_OF_RESOURCES | 0x31000000 ) + +/** No message of the desired type */ +#define ENOMSG ( ERRFILE | PXENV_STATUS_FAILURE | 0x32000000 ) + +/** Protocol not available */ +#define ENOPROTOOPT ( ERRFILE | PXENV_STATUS_UNSUPPORTED | 0x33000000 ) + +/** No space left on device */ +#define ENOSPC ( ERRFILE | PXENV_STATUS_OUT_OF_RESOURCES | 0x34000000 ) + +/** No STREAM resources */ +#define ENOSR ( ERRFILE | PXENV_STATUS_OUT_OF_RESOURCES | 0x35000000 ) + +/** Not a STREAM */ +#define ENOSTR ( ERRFILE | PXENV_STATUS_FAILURE | 0x36000000 ) + +/** Function not implemented */ +#define ENOSYS ( ERRFILE | PXENV_STATUS_UNSUPPORTED | 0x37000000 ) + +/** The socket is not connected */ +#define ENOTCONN ( ERRFILE | PXENV_STATUS_FAILURE | 0x38000000 ) + +/** Not a directory */ +#define ENOTDIR ( ERRFILE | PXENV_STATUS_FAILURE | 0x39000000 ) + +/** Directory not empty */ +#define ENOTEMPTY ( ERRFILE | PXENV_STATUS_FAILURE | 0x3a000000 ) + +/** Not a socket */ +#define ENOTSOCK ( ERRFILE | PXENV_STATUS_FAILURE | 0x3b000000 ) + +/** Not supported */ +#define ENOTSUP ( ERRFILE | PXENV_STATUS_UNSUPPORTED | 0x3c000000 ) + +/** Inappropriate I/O control operation */ +#define ENOTTY ( ERRFILE | PXENV_STATUS_FAILURE | 0x3d000000 ) + +/** No such device or address */ +#define ENXIO ( ERRFILE | PXENV_STATUS_TFTP_FILE_NOT_FOUND | 0x3e000000 ) + +/** Operation not supported on socket */ +#define EOPNOTSUPP ( ERRFILE | PXENV_STATUS_UNSUPPORTED | 0x3f000000 ) + +/** Value too large to be stored in data type */ +#define EOVERFLOW ( ERRFILE | PXENV_STATUS_FAILURE | 0x40000000 ) + +/** Operation not permitted */ +#define EPERM ( ERRFILE | PXENV_STATUS_TFTP_ACCESS_VIOLATION | 0x41000000 ) + +/** Broken pipe */ +#define EPIPE ( ERRFILE | PXENV_STATUS_FAILURE | 0x42000000 ) + +/** Protocol error */ +#define EPROTO ( ERRFILE | PXENV_STATUS_FAILURE | 0x43000000 ) + +/** Protocol not supported */ +#define EPROTONOSUPPORT ( ERRFILE | PXENV_STATUS_UNSUPPORTED | 0x44000000 ) + +/** Protocol wrong type for socket */ +#define EPROTOTYPE ( ERRFILE | PXENV_STATUS_FAILURE | 0x45000000 ) + +/** Result too large */ +#define ERANGE ( ERRFILE | PXENV_STATUS_FAILURE | 0x46000000 ) + +/** Read-only file system */ +#define EROFS ( ERRFILE | PXENV_STATUS_FAILURE | 0x47000000 ) + +/** Invalid seek */ +#define ESPIPE ( ERRFILE | PXENV_STATUS_FAILURE | 0x48000000 ) + +/** No such process */ +#define ESRCH ( ERRFILE | PXENV_STATUS_TFTP_FILE_NOT_FOUND | 0x49000000 ) + +/** Stale file handle */ +#define ESTALE ( ERRFILE | PXENV_STATUS_FAILURE | 0x4a000000 ) + +/** STREAM ioctl() timeout */ +#define ETIME ( ERRFILE | PXENV_STATUS_FAILURE | 0x4b000000 ) + +/** Operation timed out */ +#define ETIMEDOUT ( ERRFILE | PXENV_STATUS_TFTP_READ_TIMEOUT | 0x4c000000 ) + +/** Text file busy */ +#define ETXTBSY ( ERRFILE | PXENV_STATUS_FAILURE | 0x4d000000 ) + +/** Operation would block (different from EAGAIN!) */ +#define EWOULDBLOCK ( ERRFILE | PXENV_STATUS_TFTP_OPEN | 0x4e000000 ) + +/** Improper link */ +#define EXDEV ( ERRFILE | PXENV_STATUS_FAILURE | 0x4f000000 ) + +/** @} */ + +/** + * @defgroup euniq Per-file error disambiguators + * + * Files which use the same error number multiple times should + * probably define their own error subspace using these + * disambiguators. For example: + * + * #define ETCP_HEADER_TOO_SHORT EUNIQ_01 + * #define ETCP_BAD_CHECKSUM EUNIQ_02 + * + * @{ + */ + +#define EUNIQ_01 0x00000100 +#define EUNIQ_02 0x00000200 +#define EUNIQ_03 0x00000300 +#define EUNIQ_04 0x00000400 +#define EUNIQ_05 0x00000500 +#define EUNIQ_06 0x00000600 +#define EUNIQ_07 0x00000700 +#define EUNIQ_08 0x00000800 +#define EUNIQ_09 0x00000900 +#define EUNIQ_0A 0x00000a00 +#define EUNIQ_0B 0x00000b00 +#define EUNIQ_0C 0x00000c00 +#define EUNIQ_0D 0x00000d00 +#define EUNIQ_0E 0x00000e00 +#define EUNIQ_0F 0x00000f00 +#define EUNIQ_10 0x00001000 +#define EUNIQ_11 0x00001100 +#define EUNIQ_12 0x00001200 +#define EUNIQ_13 0x00001300 +#define EUNIQ_14 0x00001400 +#define EUNIQ_15 0x00001500 +#define EUNIQ_16 0x00001600 +#define EUNIQ_17 0x00001700 +#define EUNIQ_18 0x00001800 +#define EUNIQ_19 0x00001900 +#define EUNIQ_1A 0x00001a00 +#define EUNIQ_1B 0x00001b00 +#define EUNIQ_1C 0x00001c00 +#define EUNIQ_1D 0x00001d00 +#define EUNIQ_1E 0x00001e00 +#define EUNIQ_1F 0x00001f00 + +/** @} */ + +extern int errno; + +#endif /* ERRNO_H */ diff --git a/gpxe/src/include/etherboot.h b/gpxe/src/include/etherboot.h new file mode 100644 index 0000000..2a46595 --- /dev/null +++ b/gpxe/src/include/etherboot.h @@ -0,0 +1,42 @@ +#ifndef ETHERBOOT_H +#define ETHERBOOT_H + +/* + * Standard includes that we always want + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef unsigned long Address; + +/* + * IMPORTANT!!!!!!!!!!!!!! + * + * Everything below this point is cruft left over from older versions + * of Etherboot. Do not add *anything* below this point. Things are + * gradually being moved to individual header files. + * + */ + +/* Link configuration time in tenths of a second */ +#ifndef VALID_LINK_TIMEOUT +#define VALID_LINK_TIMEOUT 100 /* 10.0 seconds */ +#endif + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ + +#endif /* ETHERBOOT_H */ diff --git a/gpxe/src/include/fs.h b/gpxe/src/include/fs.h new file mode 100644 index 0000000..1dfe8fd --- /dev/null +++ b/gpxe/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/gpxe/src/include/getopt.h b/gpxe/src/include/getopt.h new file mode 100644 index 0000000..2505223 --- /dev/null +++ b/gpxe/src/include/getopt.h @@ -0,0 +1,92 @@ +#ifndef _GETOPT_H +#define _GETOPT_H + +/** @file + * + * Parse command-line options + * + */ + +#include + +enum getopt_argument_requirement { + /** Option does not take an argument */ + no_argument = 0, + /** Option requires an argument */ + required_argument = 1, + /** Option may have an argument */ + optional_argument = 2, +}; + +/** A long option, as used for getopt_long() */ +struct option { + /** Long name of this option */ + const char *name; + /** Option takes an argument + * + * Must be one of @c no_argument, @c required_argument, or @c + * optional_argument. + */ + int has_arg; + /** Location into which to store @c val, or NULL. + * + * See the description for @c val for more details. + */ + int *flag; + /** Value to return + * + * If @c flag is NULL, then this is the value that will be + * returned by getopt_long() when this option is found, and + * should therefore be set to the equivalent short option + * character. + * + * If @c flag is non-NULL, then this value will be written to + * the location pointed to by @flag, and getopt_long() will + * return 0. + */ + int val; +}; + +extern char *optarg; +extern int optind; +extern int nextchar; +extern int optopt; + +extern int getopt_long ( int argc, char * const argv[], const char *optstring, + const struct option *longopts, int *longindex ); + +/** + * Parse command-line options + * + * @v argv Argument count + * @v argv Argument list + * @v optstring Option specification string + * @ret option Option found, or -1 for no more options + * + * See getopt_long() for full details. + */ +static inline int getopt ( int argc, char * const argv[], + const char *optstring ) { + static const struct option no_options[] = { + { NULL, 0, NULL, 0 } + }; + return getopt_long ( argc, argv, optstring, no_options, NULL ); +} + +/** + * Reset getopt() internal state + * + * Due to a limitation of the POSIX getopt() API, it is necessary to + * add a call to reset_getopt() before each set of calls to getopt() + * or getopt_long(). This arises because POSIX assumes that each + * process will parse command line arguments no more than once; this + * assumption is not valid within Etherboot. We work around the + * limitation by arranging for execv() to call reset_getopt() before + * executing the command. + */ +static inline void reset_getopt ( void ) { + optind = 1; + nextchar = 0; +} + +#endif /* _GETOPT_H */ diff --git a/gpxe/src/include/gpxe/acpi.h b/gpxe/src/include/gpxe/acpi.h new file mode 100644 index 0000000..33b1b2b --- /dev/null +++ b/gpxe/src/include/gpxe/acpi.h @@ -0,0 +1,41 @@ +#ifndef _GPXE_ACPI_H +#define _GPXE_ACPI_H + +/** @file + * + * ACPI data structures + * + */ + +#include + +/** + * An ACPI description header + * + * This is the structure common to the start of all ACPI system + * description tables. + */ +struct acpi_description_header { + /** ACPI signature (4 ASCII characters) */ + char signature[4]; + /** Length of table, in bytes, including header */ + uint32_t length; + /** ACPI Specification minor version number */ + uint8_t revision; + /** To make sum of entire table == 0 */ + uint8_t checksum; + /** OEM identification */ + char oem_id[6]; + /** OEM table identification */ + char oem_table_id[8]; + /** OEM revision number */ + uint32_t oem_revision; + /** ASL compiler vendor ID */ + char asl_compiler_id[4]; + /** ASL compiler revision number */ + uint32_t asl_compiler_revision; +} __attribute__ (( packed )); + +extern void acpi_fix_checksum ( struct acpi_description_header *acpi ); + +#endif /* _GPXE_ACPI_H */ diff --git a/gpxe/src/include/gpxe/aes.h b/gpxe/src/include/gpxe/aes.h new file mode 100644 index 0000000..bdb4b35 --- /dev/null +++ b/gpxe/src/include/gpxe/aes.h @@ -0,0 +1,8 @@ +#ifndef _GPXE_AES_H +#define _GPXE_AES_H + +struct cipher_algorithm; + +extern struct cipher_algorithm aes_cbc_algorithm; + +#endif /* _GPXE_AES_H */ diff --git a/gpxe/src/include/gpxe/ansiesc.h b/gpxe/src/include/gpxe/ansiesc.h new file mode 100644 index 0000000..ccc4ca6 --- /dev/null +++ b/gpxe/src/include/gpxe/ansiesc.h @@ -0,0 +1,118 @@ +#ifndef _GPXE_ANSIESC_H +#define _GPXE_ANSIESC_H + +/** @file + * + * ANSI escape sequences + * + * ANSI X3.64 (aka ECMA-48 or ISO/IEC 6429, available from + * http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf) + * defines escape sequences consisting of: + * + * A Control Sequence Introducer (CSI) + * + * Zero or more Parameter Bytes (P) + * + * Zero or more Intermediate Bytes (I) + * + * A Final Byte (F) + * + * The CSI consists of ESC (0x1b) followed by "[" (0x5b). The + * Parameter Bytes, for a standardised (i.e. not private or + * experimental) sequence, consist of a list of ASCII decimal integers + * separated by semicolons. The Intermediate Bytes (in the range 0x20 + * to 0x2f) and the Final Byte (in the range 0x40 to 0x4f) determine + * the control function. + * + */ + +/** A handler for an escape sequence */ +struct ansiesc_handler { + /** The control function identifier + * + * The control function identifier consists of the + * Intermediate Bytes (if any) and the Final Byte. In + * practice, no more than one immediate byte is ever used, so + * the byte combination can be efficiently expressed as a + * single integer, in the obvious way (with the Final Byte + * being the least significant byte). + */ + unsigned int function; + /** Handle escape sequence + * + * @v count Parameter count + * @v params Parameter list + * + * A negative parameter value indicates that the parameter was + * omitted and that the default value for this control + * function should be used. + * + * Since all parameters are optional, there is no way to + * distinguish between "zero parameters" and "single parameter + * omitted". Consequently, the parameter list will always + * contain at least one item. + */ + void ( * handle ) ( unsigned int count, int params[] ); +}; + +/** Maximum number of parameters within a single escape sequence */ +#define ANSIESC_MAX_PARAMS 4 + +/** + * ANSI escape sequence context + * + * This provides temporary storage for processing escape sequences, + * and points to the list of escape sequence handlers. + */ +struct ansiesc_context { + /** Array of handlers + * + * Must be terminated by a handler with @c function set to + * zero. + */ + struct ansiesc_handler *handlers; + /** Parameter count + * + * Will be zero when not currently in an escape sequence. + */ + unsigned int count; + /** Parameter list */ + int params[ANSIESC_MAX_PARAMS]; + /** Control function identifier */ + unsigned int function; +}; + +/** Escape character */ +#define ESC 0x1b + +/** Control Sequence Introducer */ +#define CSI "\033[" + +/** + * @defgroup ansifuncs ANSI escape sequence function identifiers + * @{ + */ + +/** Cursor position */ +#define ANSIESC_CUP 'H' + +/** Erase in page */ +#define ANSIESC_ED 'J' + +/** Erase from cursor to end of page */ +#define ANSIESC_ED_TO_END 0 + +/** Erase from start of page to cursor */ +#define ANSIESC_ED_FROM_START 1 + +/** Erase whole page */ +#define ANSIESC_ED_ALL 2 + +/** Select graphic rendition */ +#define ANSIESC_SGR 'm' + +/** @} */ + +extern int ansiesc_process ( struct ansiesc_context *ctx, int c ); + +#endif /* _GPXE_ANSIESC_H */ diff --git a/gpxe/src/include/gpxe/aoe.h b/gpxe/src/include/gpxe/aoe.h new file mode 100644 index 0000000..6de6b96 --- /dev/null +++ b/gpxe/src/include/gpxe/aoe.h @@ -0,0 +1,148 @@ +#ifndef _GPXE_AOE_H +#define _GPXE_AOE_H + +/** @file + * + * AoE protocol + * + */ + +#include +#include +#include +#include +#include + +/** An AoE config command */ +struct aoecfg { + /** AoE Queue depth */ + uint16_t bufcnt; + /** ATA target firmware version */ + uint16_t fwver; + /** ATA target sector count */ + uint8_t scnt; + /** AoE config string subcommand */ + uint8_t aoeccmd; + /** AoE config string length */ + uint16_t cfglen; + /** AoE config string */ + uint8_t data[0]; +} __attribute__ (( packed )); + +/** An AoE ATA command */ +struct aoeata { + /** AoE command flags */ + uint8_t aflags; + /** ATA error/feature register */ + uint8_t err_feat; + /** ATA sector count register */ + uint8_t count; + /** ATA command/status register */ + uint8_t cmd_stat; + /** Logical block address, in little-endian order */ + union { + uint64_t u64; + uint8_t bytes[6]; + } lba; + /** Data payload */ + uint8_t data[0]; +} __attribute__ (( packed )); + +#define AOE_FL_EXTENDED 0x40 /**< LBA48 extended addressing */ +#define AOE_FL_DEV_HEAD 0x10 /**< Device/head flag */ +#define AOE_FL_ASYNC 0x02 /**< Asynchronous write */ +#define AOE_FL_WRITE 0x01 /**< Write command */ + +/** An AoE command */ +union aoecmd { + /** Config command */ + struct aoecfg cfg; + /** ATA command */ + struct aoeata ata; +}; + +/** An AoE header */ +struct aoehdr { + /** Protocol version number and flags */ + uint8_t ver_flags; + /** Error code */ + uint8_t error; + /** Major device number, in network byte order */ + uint16_t major; + /** Minor device number */ + uint8_t minor; + /** Command number */ + uint8_t command; + /** Tag, in network byte order */ + uint32_t tag; + /** Payload */ + union aoecmd cmd[0]; +} __attribute__ (( packed )); + +#define AOE_VERSION 0x10 /**< Version 1 */ +#define AOE_VERSION_MASK 0xf0 /**< Version part of ver_flags field */ + +#define AOE_FL_RESPONSE 0x08 /**< Message is a response */ +#define AOE_FL_ERROR 0x04 /**< Command generated an error */ + +#define AOE_MAJOR_BROADCAST 0xffff +#define AOE_MINOR_BROADCAST 0xff + +#define AOE_CMD_ATA 0x00 /**< Issue ATA command */ +#define AOE_CMD_CONFIG 0x01 /**< Query Config Information */ + +#define AOE_TAG_MAGIC 0xebeb0000 + +#define AOE_ERR_BAD_COMMAND 1 /**< Unrecognised command code */ +#define AOE_ERR_BAD_PARAMETER 2 /**< Bad argument parameter */ +#define AOE_ERR_UNAVAILABLE 3 /**< Device unavailable */ +#define AOE_ERR_CONFIG_EXISTS 4 /**< Config string present */ +#define AOE_ERR_BAD_VERSION 5 /**< Unsupported version */ + +/** An AoE session */ +struct aoe_session { + /** Reference counter */ + struct refcnt refcnt; + + /** List of all AoE sessions */ + struct list_head list; + + /** Network device */ + struct net_device *netdev; + + /** Major number */ + uint16_t major; + /** Minor number */ + uint8_t minor; + /** Target MAC address */ + uint8_t target[ETH_ALEN]; + + /** Tag for current AoE command */ + uint32_t tag; + + /** Current AOE command */ + uint8_t aoe_cmd_type; + /** Current ATA command */ + struct ata_command *command; + /** Overall status of current ATA command */ + unsigned int status; + /** Byte offset within command's data buffer */ + unsigned int command_offset; + /** Return status code for command */ + int rc; + + /** Retransmission timer */ + struct retry_timer timer; +}; + +#define AOE_STATUS_ERR_MASK 0x0f /**< Error portion of status code */ +#define AOE_STATUS_PENDING 0x80 /**< Command pending */ + +/** Maximum number of sectors per packet */ +#define AOE_MAX_COUNT 2 + +extern void aoe_detach ( struct ata_device *ata ); +extern int aoe_attach ( struct ata_device *ata, struct net_device *netdev, + const char *root_path ); + +#endif /* _GPXE_AOE_H */ diff --git a/gpxe/src/include/gpxe/api.h b/gpxe/src/include/gpxe/api.h new file mode 100644 index 0000000..df5d1ae --- /dev/null +++ b/gpxe/src/include/gpxe/api.h @@ -0,0 +1,82 @@ +#ifndef _GPXE_API_H +#define _GPXE_API_H + +/** @file + * + * gPXE internal APIs + * + * There are various formally-defined APIs internal to gPXE, with + * several differing implementations specific to particular execution + * environments (e.g. PC BIOS, EFI, LinuxBIOS). + * + */ + +/** @defgroup Single-implementation APIs + * + * These are APIs for which only a single implementation may be + * compiled in at any given time. + * + * @{ + */ + +/** + * Calculate function implementation name + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @ret _subsys_func Subsystem API function + * + * The subsystem prefix should be an empty string for the currently + * selected subsystem, and should be a subsystem-unique string for all + * other subsystems. + */ +#define SINGLE_API_NAME( _prefix, _api_func ) _prefix ## _api_func + +/** + * Calculate static inline function name + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @ret _subsys_func Subsystem API function + */ +#define SINGLE_API_INLINE( _prefix, _api_func ) \ + SINGLE_API_NAME ( _prefix, _api_func ) + +/** + * Provide an API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @v _func Implementing function + */ +#define PROVIDE_SINGLE_API( _prefix, _api_func, _func ) \ + /* Ensure that _api_func exists */ \ + typeof ( _api_func ) _api_func; \ + /* Ensure that _func exists */ \ + typeof ( _func ) _func; \ + /* Ensure that _func is type-compatible with _api_func */ \ + typeof ( _api_func ) _func; \ + /* Ensure that _subsys_func is non-static */ \ + extern typeof ( _api_func ) SINGLE_API_NAME ( _prefix, _api_func ); \ + /* Provide symbol alias from _subsys_func to _func */ \ + typeof ( _api_func ) SINGLE_API_NAME ( _prefix, _api_func ) \ + __attribute__ (( alias ( #_func ) )); + +/** + * Provide a static inline API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + */ +#define PROVIDE_SINGLE_API_INLINE( _prefix, _api_func ) \ + /* Ensure that _api_func exists */ \ + typeof ( _api_func ) _api_func; \ + /* Ensure that _subsys_func exists and is static */ \ + static typeof ( SINGLE_API_INLINE ( _prefix, _api_func ) ) \ + SINGLE_API_INLINE ( _prefix, _api_func ); \ + /* Ensure that _subsys_func is type-compatible with _api_func */ \ + typeof ( _api_func ) SINGLE_API_INLINE ( _prefix, _api_func ); + +/** @} */ + +#endif /* _GPXE_API_H */ diff --git a/gpxe/src/include/gpxe/arp.h b/gpxe/src/include/gpxe/arp.h new file mode 100644 index 0000000..6464ce0 --- /dev/null +++ b/gpxe/src/include/gpxe/arp.h @@ -0,0 +1,41 @@ +#ifndef _GPXE_ARP_H +#define _GPXE_ARP_H + +/** @file + * + * Address Resolution Protocol + * + */ + +#include + +struct net_device; +struct net_protocol; + +/** A network-layer protocol that relies upon ARP */ +struct arp_net_protocol { + /** Network-layer protocol */ + struct net_protocol *net_protocol; + /** Check existence of address + * + * @v netdev Network device + * @v net_addr Network-layer address + * @ret rc Return status code + */ + int ( * check ) ( struct net_device *netdev, + const void *net_addr ); +}; + +/** Declare an ARP protocol */ +#define __arp_net_protocol \ + __table ( struct arp_net_protocol, arp_net_protocols, 01 ) + +extern struct net_protocol arp_protocol; + +extern int arp_resolve ( struct net_device *netdev, + struct net_protocol *net_protocol, + const void *dest_net_addr, + const void *source_net_addr, + void *dest_ll_addr ); + +#endif /* _GPXE_ARP_H */ diff --git a/gpxe/src/include/gpxe/asn1.h b/gpxe/src/include/gpxe/asn1.h new file mode 100644 index 0000000..5440c48 --- /dev/null +++ b/gpxe/src/include/gpxe/asn1.h @@ -0,0 +1,32 @@ +#ifndef _GPXE_ASN1_H +#define _GPXE_ASN1_H + +/** @file + * + * ASN.1 encoding + * + */ + +#define ASN1_INTEGER 0x02 +#define ASN1_BIT_STRING 0x03 +#define ASN1_OCTET_STRING 0x04 +#define ASN1_NULL 0x05 +#define ASN1_OID 0x06 +#define ASN1_SEQUENCE 0x30 +#define ASN1_IP_ADDRESS 0x40 +#define ASN1_EXPLICIT_TAG 0xa0 + +/** + * A DER-encoded ASN.1 object cursor + */ +struct asn1_cursor { + /** Start of data */ + void *data; + /** Length of data */ + size_t len; +}; + +extern int asn1_enter ( struct asn1_cursor *cursor, unsigned int type ); +extern int asn1_skip ( struct asn1_cursor *cursor, unsigned int type ); + +#endif /* _GPXE_ASN1_H */ diff --git a/gpxe/src/include/gpxe/ata.h b/gpxe/src/include/gpxe/ata.h new file mode 100644 index 0000000..b6da393 --- /dev/null +++ b/gpxe/src/include/gpxe/ata.h @@ -0,0 +1,205 @@ +#ifndef _GPXE_ATA_H +#define _GPXE_ATA_H + +#include +#include +#include +#include + +/** @file + * + * ATA devices + * + */ + +/** + * An ATA Logical Block Address + * + * ATA controllers have three byte-wide registers for specifying the + * block address: LBA Low, LBA Mid and LBA High. This allows for a + * 24-bit address. Some devices support the "48-bit address feature + * set" (LBA48), in which case each of these byte-wide registers is + * actually a two-entry FIFO, and the "previous" byte pushed into the + * FIFO is used as the corresponding high-order byte. So, to set up + * the 48-bit address 0x123456abcdef, you would issue + * + * 0x56 -> LBA Low register + * 0xef -> LBA Low register + * 0x34 -> LBA Mid register + * 0xcd -> LBA Mid register + * 0x12 -> LBA High register + * 0xab -> LBA High register + * + * This structure encapsulates this information by providing a single + * 64-bit integer in native byte order, unioned with bytes named so + * that the sequence becomes + * + * low_prev -> LBA Low register + * low_cur -> LBA Low register + * mid_prev -> LBA Mid register + * mid_cur -> LBA Mid register + * high_prev -> LBA High register + * high_cur -> LBA High register + * + * Just to complicate matters further, in non-LBA48 mode it is + * possible to have a 28-bit address, in which case bits 27:24 must be + * written into the low four bits of the Device register. + */ +union ata_lba { + /** LBA as a 64-bit integer in native-endian order */ + uint64_t native; + /** ATA registers */ + struct { +#if __BYTE_ORDER == __LITTLE_ENDIAN + uint8_t low_cur; + uint8_t mid_cur; + uint8_t high_cur; + uint8_t low_prev; + uint8_t mid_prev; + uint8_t high_prev; + uint16_t pad; +#elif __BYTE_ORDER == __BIG_ENDIAN + uint16_t pad; + uint8_t high_prev; + uint8_t mid_prev; + uint8_t low_prev; + uint8_t high_cur; + uint8_t mid_cur; + uint8_t low_cur; +#else +#error "I need a byte order" +#endif + } bytes; +}; + +/** An ATA 2-byte FIFO register */ +union ata_fifo { + /** Value in native-endian order */ + uint16_t native; + /** ATA registers */ + struct { +#if __BYTE_ORDER == __LITTLE_ENDIAN + uint8_t cur; + uint8_t prev; +#elif __BYTE_ORDER == __BIG_ENDIAN + uint8_t prev; + uint8_t cur; +#else +#error "I need a byte order" +#endif + } bytes; +}; + +/** ATA command block */ +struct ata_cb { + /** Logical block address */ + union ata_lba lba; + /** Sector count */ + union ata_fifo count; + /** Error/feature register */ + union ata_fifo err_feat; + /** Device register */ + uint8_t device; + /** Command/status register */ + uint8_t cmd_stat; + /** LBA48 addressing flag */ + int lba48; +}; + +/** Obsolete bits in the ATA device register */ +#define ATA_DEV_OBSOLETE 0xa0 + +/** LBA flag in the ATA device register */ +#define ATA_DEV_LBA 0x40 + +/** Slave ("device 1") flag in the ATA device register */ +#define ATA_DEV_SLAVE 0x10 + +/** Master ("device 0") flag in the ATA device register */ +#define ATA_DEV_MASTER 0x00 + +/** Mask of non-LBA portion of device register */ +#define ATA_DEV_MASK 0xf0 + +/** "Read sectors" command */ +#define ATA_CMD_READ 0x20 + +/** "Read sectors (ext)" command */ +#define ATA_CMD_READ_EXT 0x24 + +/** "Write sectors" command */ +#define ATA_CMD_WRITE 0x30 + +/** "Write sectors (ext)" command */ +#define ATA_CMD_WRITE_EXT 0x34 + +/** "Identify" command */ +#define ATA_CMD_IDENTIFY 0xec + +/** An ATA command */ +struct ata_command { + /** ATA command block */ + struct ata_cb cb; + /** Data-out buffer (may be NULL) + * + * If non-NULL, this buffer must be ata_command::cb::count + * sectors in size. + */ + userptr_t data_out; + /** Data-in buffer (may be NULL) + * + * If non-NULL, this buffer must be ata_command::cb::count + * sectors in size. + */ + userptr_t data_in; +}; + +/** + * Structure returned by ATA IDENTIFY command + * + * This is a huge structure with many fields that we don't care about, + * so we implement only a few fields. + */ +struct ata_identity { + uint16_t ignore_a[60]; /* words 0-59 */ + uint32_t lba_sectors; /* words 60-61 */ + uint16_t ignore_b[21]; /* words 62-82 */ + uint16_t supports_lba48; /* word 83 */ + uint16_t ignore_c[16]; /* words 84-99 */ + uint64_t lba48_sectors; /* words 100-103 */ + uint16_t ignore_d[152]; /* words 104-255 */ +}; + +/** Supports LBA48 flag */ +#define ATA_SUPPORTS_LBA48 ( 1 << 10 ) + +/** ATA sector size */ +#define ATA_SECTOR_SIZE 512 + +/** An ATA device */ +struct ata_device { + /** Block device interface */ + struct block_device blockdev; + /** Device number + * + * Must be ATA_DEV_MASTER or ATA_DEV_SLAVE. + */ + int device; + /** LBA48 extended addressing */ + int lba48; + /** + * Issue ATA command + * + * @v ata ATA device + * @v command ATA command + * @ret rc Return status code + */ + int ( * command ) ( struct ata_device *ata, + struct ata_command *command ); + /** Backing device */ + struct refcnt *backend; +}; + +extern int init_atadev ( struct ata_device *ata ); + +#endif /* _GPXE_ATA_H */ diff --git a/gpxe/src/include/gpxe/base64.h b/gpxe/src/include/gpxe/base64.h new file mode 100644 index 0000000..3321971 --- /dev/null +++ b/gpxe/src/include/gpxe/base64.h @@ -0,0 +1,24 @@ +#ifndef _GPXE_BASE64_H +#define _GPXE_BASE64_H + +/** @file + * + * Base64 encoding + * + */ + +#include + +/** + * Calculate length of base64-encoded string + * + * @v raw_len Raw string length (excluding NUL) + * @ret encoded_len Encoded string length (excluding NUL) + */ +static inline size_t base64_encoded_len ( size_t raw_len ) { + return ( ( ( raw_len + 3 - 1 ) / 3 ) * 4 ); +} + +extern void base64_encode ( const char *raw, char *encoded ); + +#endif /* _GPXE_BASE64_H */ diff --git a/gpxe/src/include/gpxe/bitbash.h b/gpxe/src/include/gpxe/bitbash.h new file mode 100644 index 0000000..62bdce0 --- /dev/null +++ b/gpxe/src/include/gpxe/bitbash.h @@ -0,0 +1,50 @@ +#ifndef _GPXE_BITBASH_H +#define _GPXE_BITBASH_H + +/** @file + * + * Bit-bashing interfaces + * + */ + +struct bit_basher; + +/** Bit-bashing operations */ +struct bit_basher_operations { + /** + * Set/clear output bit + * + * @v basher Bit-bashing interface + * @v bit_id Bit number + * @v data Value to write + * + * @c data will be 0 if a logic 0 should be written (i.e. the + * bit should be cleared), or -1UL if a logic 1 should be + * written (i.e. the bit should be set). This is done so that + * the method may simply binary-AND @c data with the + * appropriate bit mask. + */ + void ( * write ) ( struct bit_basher *basher, unsigned int bit_id, + unsigned long data ); + /** + * Read input bit + * + * @v basher Bit-bashing interface + * @v bit_id Bit number + * @ret zero Input is a logic 0 + * @ret non-zero Input is a logic 1 + */ + int ( * read ) ( struct bit_basher *basher, unsigned int bit_id ); +}; + +/** A bit-bashing interface */ +struct bit_basher { + /** Bit-bashing operations */ + struct bit_basher_operations *op; +}; + +extern void write_bit ( struct bit_basher *basher, unsigned int bit_id, + unsigned long data ); +extern int read_bit ( struct bit_basher *basher, unsigned int bit_id ); + +#endif /* _GPXE_BITBASH_H */ diff --git a/gpxe/src/include/gpxe/bitmap.h b/gpxe/src/include/gpxe/bitmap.h new file mode 100644 index 0000000..0c2f53c --- /dev/null +++ b/gpxe/src/include/gpxe/bitmap.h @@ -0,0 +1,83 @@ +#ifndef _GPXE_BITMAP_H +#define _GPXE_BITMAP_H + +/** @file + * + * Bitmaps for multicast downloads + * + */ + +#include +#include +#include + +/** A single block of bits within a bitmap */ +typedef unsigned long bitmap_block_t; + +/** Size of a block of bits (in bits) */ +#define BITMAP_BLKSIZE ( sizeof ( bitmap_block_t ) * 8 ) + +/** + * Block index within bitmap + * + * @v bit Bit index + * @ret index Block index + */ +#define BITMAP_INDEX( bit ) ( (bit) / BITMAP_BLKSIZE ) + +/** + * Block mask within bitmap + * + * @v bit Bit index + * @ret mask Block mask + */ +#define BITMAP_MASK( bit ) ( 1 << ( (bit) % BITMAP_BLKSIZE ) ) + +/** A bitmap */ +struct bitmap { + /** Bitmap data */ + bitmap_block_t *blocks; + /** Length of the bitmap, in bits */ + unsigned int length; + /** Index of first gap in the bitmap */ + unsigned int first_gap; +}; + +extern int bitmap_resize ( struct bitmap *bitmap, unsigned int new_length ); +extern int bitmap_test ( struct bitmap *bitmap, unsigned int bit ); +extern void bitmap_set ( struct bitmap *bitmap, unsigned int bit ); + +/** + * Free bitmap resources + * + * @v bitmap Bitmap + */ +static inline void bitmap_free ( struct bitmap *bitmap ) { + free ( bitmap->blocks ); +} + +/** + * Get first gap within bitmap + * + * @v bitmap Bitmap + * @ret first_gap First gap + * + * The first gap is the first unset bit within the bitmap. + */ +static inline unsigned int bitmap_first_gap ( struct bitmap *bitmap ) { + return bitmap->first_gap; +} + +/** + * Check to see if bitmap is full + * + * @v bitmap Bitmap + * @ret is_full Bitmap is full + * + * The bitmap is full if it has no gaps (i.e. no unset bits). + */ +static inline int bitmap_full ( struct bitmap *bitmap ) { + return ( bitmap->first_gap == bitmap->length ); +} + +#endif /* _GPXE_BITMAP_H */ diff --git a/gpxe/src/include/gpxe/bitops.h b/gpxe/src/include/gpxe/bitops.h new file mode 100644 index 0000000..5405c85 --- /dev/null +++ b/gpxe/src/include/gpxe/bitops.h @@ -0,0 +1,228 @@ +#ifndef _GPXE_BITOPS_H +#define _GPXE_BITOPS_H + +/* + * Copyright (C) 2008 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. + */ + +/** + * @file + * + * Bit operations + * + */ + +#include +#include + +/* Endianness selection. + * + * This is a property of the NIC, not a property of the host CPU. + */ +#ifdef BITOPS_LITTLE_ENDIAN +#define cpu_to_BIT64 cpu_to_le64 +#define cpu_to_BIT32 cpu_to_le32 +#define BIT64_to_cpu le64_to_cpu +#define BIT32_to_cpu le32_to_cpu +#endif +#ifdef BITOPS_BIG_ENDIAN +#define cpu_to_BIT64 cpu_to_be64 +#define cpu_to_BIT32 cpu_to_be32 +#define BIT64_to_cpu be64_to_cpu +#define BIT32_to_cpu be32_to_cpu +#endif + +/** Datatype used to represent a bit in the pseudo-structures */ +typedef unsigned char pseudo_bit_t; + +/** + * Wrapper structure for pseudo_bit_t structures + * + * This structure provides a wrapper around pseudo_bit_t structures. + * It has the correct size, and also encapsulates type information + * about the underlying pseudo_bit_t-based structure, which allows the + * BIT_FILL() etc. macros to work without requiring explicit type + * information. + */ +#define PSEUDO_BIT_STRUCT( _structure ) \ + union { \ + uint8_t bytes[ sizeof ( _structure ) / 8 ]; \ + uint32_t dwords[ sizeof ( _structure ) / 32 ]; \ + uint64_t qwords[ sizeof ( _structure ) / 64 ]; \ + _structure *dummy[0]; \ + } u + +/** Get pseudo_bit_t structure type from wrapper structure pointer */ +#define PSEUDO_BIT_STRUCT_TYPE( _ptr ) \ + typeof ( *((_ptr)->u.dummy[0]) ) + +/** Bit offset of a field within a pseudo_bit_t structure */ +#define BIT_OFFSET( _ptr, _field ) \ + offsetof ( PSEUDO_BIT_STRUCT_TYPE ( _ptr ), _field ) + +/** Bit width of a field within a pseudo_bit_t structure */ +#define BIT_WIDTH( _ptr, _field ) \ + sizeof ( ( ( PSEUDO_BIT_STRUCT_TYPE ( _ptr ) * ) NULL )->_field ) + +/** Qword offset of a field within a pseudo_bit_t structure */ +#define QWORD_OFFSET( _ptr, _field ) \ + ( BIT_OFFSET ( _ptr, _field ) / 64 ) + +/** Qword bit offset of a field within a pseudo_bit_t structure */ +#define QWORD_BIT_OFFSET( _ptr, _index, _field ) \ + ( BIT_OFFSET ( _ptr, _field ) - ( 64 * (_index) ) ) + +/** Bit mask for a field within a pseudo_bit_t structure */ +#define BIT_MASK( _ptr, _field ) \ + ( ( ~( ( uint64_t ) 0 ) ) >> \ + ( 64 - BIT_WIDTH ( _ptr, _field ) ) ) + +/* + * Assemble native-endian qword from named fields and values + * + */ + +#define BIT_ASSEMBLE_1( _ptr, _index, _field, _value ) \ + ( ( ( uint64_t) (_value) ) << \ + QWORD_BIT_OFFSET ( _ptr, _index, _field ) ) + +#define BIT_ASSEMBLE_2( _ptr, _index, _field, _value, ... ) \ + ( BIT_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + BIT_ASSEMBLE_1 ( _ptr, _index, __VA_ARGS__ ) ) + +#define BIT_ASSEMBLE_3( _ptr, _index, _field, _value, ... ) \ + ( BIT_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + BIT_ASSEMBLE_2 ( _ptr, _index, __VA_ARGS__ ) ) + +#define BIT_ASSEMBLE_4( _ptr, _index, _field, _value, ... ) \ + ( BIT_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + BIT_ASSEMBLE_3 ( _ptr, _index, __VA_ARGS__ ) ) + +#define BIT_ASSEMBLE_5( _ptr, _index, _field, _value, ... ) \ + ( BIT_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + BIT_ASSEMBLE_4 ( _ptr, _index, __VA_ARGS__ ) ) + +#define BIT_ASSEMBLE_6( _ptr, _index, _field, _value, ... ) \ + ( BIT_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + BIT_ASSEMBLE_5 ( _ptr, _index, __VA_ARGS__ ) ) + +#define BIT_ASSEMBLE_7( _ptr, _index, _field, _value, ... ) \ + ( BIT_ASSEMBLE_1 ( _ptr, _index, _field, _value ) | \ + BIT_ASSEMBLE_6 ( _ptr, _index, __VA_ARGS__ ) ) + +/* + * Build native-endian (positive) qword bitmasks from named fields + * + */ + +#define BIT_MASK_1( _ptr, _index, _field ) \ + ( BIT_MASK ( _ptr, _field ) << \ + QWORD_BIT_OFFSET ( _ptr, _index, _field ) ) + +#define BIT_MASK_2( _ptr, _index, _field, ... ) \ + ( BIT_MASK_1 ( _ptr, _index, _field ) | \ + BIT_MASK_1 ( _ptr, _index, __VA_ARGS__ ) ) + +#define BIT_MASK_3( _ptr, _index, _field, ... ) \ + ( BIT_MASK_1 ( _ptr, _index, _field ) | \ + BIT_MASK_2 ( _ptr, _index, __VA_ARGS__ ) ) + +#define BIT_MASK_4( _ptr, _index, _field, ... ) \ + ( BIT_MASK_1 ( _ptr, _index, _field ) | \ + BIT_MASK_3 ( _ptr, _index, __VA_ARGS__ ) ) + +#define BIT_MASK_5( _ptr, _index, _field, ... ) \ + ( BIT_MASK_1 ( _ptr, _index, _field ) | \ + BIT_MASK_4 ( _ptr, _index, __VA_ARGS__ ) ) + +#define BIT_MASK_6( _ptr, _index, _field, ... ) \ + ( BIT_MASK_1 ( _ptr, _index, _field ) | \ + BIT_MASK_5 ( _ptr, _index, __VA_ARGS__ ) ) + +#define BIT_MASK_7( _ptr, _index, _field, ... ) \ + ( BIT_MASK_1 ( _ptr, _index, _field ) | \ + BIT_MASK_6 ( _ptr, _index, __VA_ARGS__ ) ) + +/* + * Populate little-endian qwords from named fields and values + * + */ + +#define BIT_FILL( _ptr, _index, _assembled ) do { \ + uint64_t *__ptr = &(_ptr)->u.qwords[(_index)]; \ + uint64_t __assembled = (_assembled); \ + *__ptr = cpu_to_BIT64 ( __assembled ); \ + } while ( 0 ) + +#define BIT_FILL_1( _ptr, _field1, ... ) \ + BIT_FILL ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + BIT_ASSEMBLE_1 ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + _field1, __VA_ARGS__ ) ) + +#define BIT_FILL_2( _ptr, _field1, ... ) \ + BIT_FILL ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + BIT_ASSEMBLE_2 ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + _field1, __VA_ARGS__ ) ) + +#define BIT_FILL_3( _ptr, _field1, ... ) \ + BIT_FILL ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + BIT_ASSEMBLE_3 ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + _field1, __VA_ARGS__ ) ) + +#define BIT_FILL_4( _ptr, _field1, ... ) \ + BIT_FILL ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + BIT_ASSEMBLE_4 ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + _field1, __VA_ARGS__ ) ) + +#define BIT_FILL_5( _ptr, _field1, ... ) \ + BIT_FILL ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + BIT_ASSEMBLE_5 ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + _field1, __VA_ARGS__ ) ) + +#define BIT_FILL_6( _ptr, _field1, ... ) \ + BIT_FILL ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + BIT_ASSEMBLE_6 ( _ptr, QWORD_OFFSET ( _ptr, _field1 ), \ + _field1, __VA_ARGS__ ) ) + +/** Extract value of named field */ +#define BIT_GET64( _ptr, _field ) \ + ( { \ + unsigned int __index = QWORD_OFFSET ( _ptr, _field ); \ + uint64_t *__ptr = &(_ptr)->u.qwords[__index]; \ + uint64_t __value = BIT64_to_cpu ( *__ptr ); \ + __value >>= \ + QWORD_BIT_OFFSET ( _ptr, __index, _field ); \ + __value &= BIT_MASK ( _ptr, _field ); \ + __value; \ + } ) + +/** Extract value of named field (for fields up to the size of a long) */ +#define BIT_GET( _ptr, _field ) \ + ( ( unsigned long ) BIT_GET64 ( _ptr, _field ) ) + +#define BIT_SET( _ptr, _field, _value ) do { \ + unsigned int __index = QWORD_OFFSET ( _ptr, _field ); \ + uint64_t *__ptr = &(_ptr)->u.qwords[__index]; \ + unsigned int __shift = \ + QWORD_BIT_OFFSET ( _ptr, __index, _field ); \ + uint64_t __value = (_value); \ + *__ptr &= cpu_to_BIT64 ( ~( BIT_MASK ( _ptr, _field ) << \ + __shift ) ); \ + *__ptr |= cpu_to_BIT64 ( __value << __shift ); \ + } while ( 0 ) + +#endif /* _GPXE_BITOPS_H */ diff --git a/gpxe/src/include/gpxe/blockdev.h b/gpxe/src/include/gpxe/blockdev.h new file mode 100644 index 0000000..8222984 --- /dev/null +++ b/gpxe/src/include/gpxe/blockdev.h @@ -0,0 +1,51 @@ +#ifndef _GPXE_BLOCKDEV_H +#define _GPXE_BLOCKDEV_H + +/** + * @file + * + * Block devices + * + */ + +#include + +struct block_device; + +/** Block device operations */ +struct block_device_operations { + /** + * Read block + * + * @v blockdev Block device + * @v block Block number + * @v count Block count + * @v buffer Data buffer + * @ret rc Return status code + */ + int ( * read ) ( struct block_device *blockdev, uint64_t block, + unsigned long count, userptr_t buffer ); + /** + * Write block + * + * @v blockdev Block device + * @v block Block number + * @v count Block count + * @v buffer Data buffer + * @ret rc Return status code + */ + int ( * write ) ( struct block_device *blockdev, uint64_t block, + unsigned long count, userptr_t buffer ); +}; + +/** A block device */ +struct block_device { + /** Block device operations */ + struct block_device_operations *op; + /** Block size */ + size_t blksize; + /** Total number of blocks */ + uint64_t blocks; +}; + +#endif /* _GPXE_BLOCKDEV_H */ diff --git a/gpxe/src/include/gpxe/cbc.h b/gpxe/src/include/gpxe/cbc.h new file mode 100644 index 0000000..fcc115e --- /dev/null +++ b/gpxe/src/include/gpxe/cbc.h @@ -0,0 +1,98 @@ +#ifndef _GPXE_CBC_H +#define _GPXE_CBC_H + +/** @file + * + * Cipher-block chaining + * + */ + +#include + +/** + * Set key + * + * @v ctx Context + * @v key Key + * @v keylen Key length + * @v raw_cipher Underlying cipher algorithm + * @v cbc_ctx CBC context + * @ret rc Return status code + */ +static inline int cbc_setkey ( void *ctx, const void *key, size_t keylen, + struct cipher_algorithm *raw_cipher, + void *cbc_ctx __unused ) { + + return cipher_setkey ( raw_cipher, ctx, key, keylen ); +} + +/** + * Set initialisation vector + * + * @v ctx Context + * @v iv Initialisation vector + * @v raw_cipher Underlying cipher algorithm + * @v cbc_ctx CBC context + */ +static inline void cbc_setiv ( void *ctx __unused, const void *iv, + struct cipher_algorithm *raw_cipher, + void *cbc_ctx ) { + memcpy ( cbc_ctx, iv, raw_cipher->blocksize ); +} + +extern void cbc_encrypt ( void *ctx, const void *src, void *dst, + size_t len, struct cipher_algorithm *raw_cipher, + void *cbc_ctx ); +extern void cbc_decrypt ( void *ctx, const void *src, void *dst, + size_t len, struct cipher_algorithm *raw_cipher, + void *cbc_ctx ); + +/** + * Create a cipher-block chaining mode of behaviour of an existing cipher + * + * @v _cbc_name Name for the new CBC cipher + * @v _cbc_cipher New cipher algorithm + * @v _raw_cipher Underlying cipher algorithm + * @v _raw_context Context structure for the underlying cipher + * @v _blocksize Cipher block size + */ +#define CBC_CIPHER( _cbc_name, _cbc_cipher, _raw_cipher, _raw_context, \ + _blocksize ) \ +struct _cbc_name ## _context { \ + _raw_context raw_ctx; \ + uint8_t cbc_ctx[_blocksize]; \ +}; \ +static int _cbc_name ## _setkey ( void *ctx, const void *key, \ + size_t keylen ) { \ + struct _cbc_name ## _context * _cbc_name ## _ctx = ctx; \ + return cbc_setkey ( &_cbc_name ## _ctx->raw_ctx, key, keylen, \ + &_raw_cipher, &_cbc_name ## _ctx->cbc_ctx );\ +} \ +static void _cbc_name ## _setiv ( void *ctx, const void *iv ) { \ + struct _cbc_name ## _context * _cbc_name ## _ctx = ctx; \ + cbc_setiv ( &_cbc_name ## _ctx->raw_ctx, iv, \ + &_raw_cipher, &aes_cbc_ctx->cbc_ctx ); \ +} \ +static void _cbc_name ## _encrypt ( void *ctx, const void *src, \ + void *dst, size_t len ) { \ + struct _cbc_name ## _context * _cbc_name ## _ctx = ctx; \ + cbc_encrypt ( &_cbc_name ## _ctx->raw_ctx, src, dst, len, \ + &_raw_cipher, &aes_cbc_ctx->cbc_ctx ); \ +} \ +static void _cbc_name ## _decrypt ( void *ctx, const void *src, \ + void *dst, size_t len ) { \ + struct _cbc_name ## _context * _cbc_name ## _ctx = ctx; \ + cbc_decrypt ( &_cbc_name ## _ctx->raw_ctx, src, dst, len, \ + &_raw_cipher, &aes_cbc_ctx->cbc_ctx ); \ +} \ +struct cipher_algorithm _cbc_cipher = { \ + .name = #_cbc_name, \ + .ctxsize = sizeof ( struct _cbc_name ## _context ), \ + .blocksize = _blocksize, \ + .setkey = _cbc_name ## _setkey, \ + .setiv = _cbc_name ## _setiv, \ + .encrypt = _cbc_name ## _encrypt, \ + .decrypt = _cbc_name ## _decrypt, \ +}; + +#endif /* _GPXE_CBC_H */ diff --git a/gpxe/src/include/gpxe/chap.h b/gpxe/src/include/gpxe/chap.h new file mode 100644 index 0000000..87e5484 --- /dev/null +++ b/gpxe/src/include/gpxe/chap.h @@ -0,0 +1,51 @@ +#ifndef _GPXE_CHAP_H +#define _GPXE_CHAP_H + +/** @file + * + * CHAP protocol + * + */ + +#include +#include + +struct digest_algorithm; + +/** A CHAP response */ +struct chap_response { + /** Digest algorithm used for the response */ + struct digest_algorithm *digest; + /** Context used by the digest algorithm */ + uint8_t *digest_context; + /** CHAP response */ + uint8_t *response; + /** Length of CHAP response */ + size_t response_len; +}; + +extern int chap_init ( struct chap_response *chap, + struct digest_algorithm *digest ); +extern void chap_update ( struct chap_response *chap, const void *data, + size_t len ); +extern void chap_respond ( struct chap_response *chap ); +extern void chap_finish ( struct chap_response *chap ); + +/** + * Add identifier data to the CHAP challenge + * + * @v chap CHAP response + * @v identifier CHAP identifier + * + * The CHAP identifier is the first byte of the CHAP challenge. This + * function is a notational convenience for calling chap_update() for + * the identifier byte. + */ +static inline void chap_set_identifier ( struct chap_response *chap, + unsigned int identifier ) { + uint8_t ident_byte = identifier; + + chap_update ( chap, &ident_byte, sizeof ( ident_byte ) ); +} + +#endif /* _GPXE_CHAP_H */ diff --git a/gpxe/src/include/gpxe/command.h b/gpxe/src/include/gpxe/command.h new file mode 100644 index 0000000..5d8057a --- /dev/null +++ b/gpxe/src/include/gpxe/command.h @@ -0,0 +1,22 @@ +#ifndef _GPXE_COMMAND_H +#define _GPXE_COMMAND_H + +#include + +/** A command-line command */ +struct command { + /** Name of the command */ + const char *name; + /** + * Function implementing the command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ + int ( * exec ) ( int argc, char **argv ); +}; + +#define __command __table ( struct command, commands, 01 ) + +#endif /* _GPXE_COMMAND_H */ diff --git a/gpxe/src/include/gpxe/cpio.h b/gpxe/src/include/gpxe/cpio.h new file mode 100644 index 0000000..ba6f844 --- /dev/null +++ b/gpxe/src/include/gpxe/cpio.h @@ -0,0 +1,51 @@ +#ifndef _GPXE_CPIO_H +#define _GPXE_CPIO_H + +/** @file + * + * CPIO archives + * + */ + +/** A CPIO archive header + * + * All field are hexadecimal ASCII numbers padded with '0' on the + * left to the full width of the field. + */ +struct cpio_header { + /** The string "070701" or "070702" */ + char c_magic[6]; + /** File inode number */ + char c_ino[8]; + /** File mode and permissions */ + char c_mode[8]; + /** File uid */ + char c_uid[8]; + /** File gid */ + char c_gid[8]; + /** Number of links */ + char c_nlink[8]; + /** Modification time */ + char c_mtime[8]; + /** Size of data field */ + char c_filesize[8]; + /** Major part of file device number */ + char c_maj[8]; + /** Minor part of file device number */ + char c_min[8]; + /** Major part of device node reference */ + char c_rmaj[8]; + /** Minor part of device node reference */ + char c_rmin[8]; + /** Length of filename, including final NUL */ + char c_namesize[8]; + /** Checksum of data field if c_magic is 070702, othersize zero */ + char c_chksum[8]; +} __attribute__ (( packed )); + +/** CPIO magic */ +#define CPIO_MAGIC "070701" + +extern void cpio_set_field ( char *field, unsigned long value ); + +#endif /* _GPXE_CPIO_H */ diff --git a/gpxe/src/include/gpxe/crypto.h b/gpxe/src/include/gpxe/crypto.h new file mode 100644 index 0000000..10882d3 --- /dev/null +++ b/gpxe/src/include/gpxe/crypto.h @@ -0,0 +1,152 @@ +#ifndef _GPXE_CRYPTO_H +#define _GPXE_CRYPTO_H + +/** @file + * + * Cryptographic API + * + */ + +#include +#include + +/** A message digest algorithm */ +struct digest_algorithm { + /** Algorithm name */ + const char *name; + /** Context size */ + size_t ctxsize; + /** Block size */ + size_t blocksize; + /** Digest size */ + size_t digestsize; + /** Initialise digest + * + * @v ctx Context + */ + void ( * init ) ( void *ctx ); + /** Update digest with new data + * + * @v ctx Context + * @v src Data to digest + * @v len Length of data + * + * @v len is not necessarily a multiple of @c blocksize. + */ + void ( * update ) ( void *ctx, const void *src, size_t len ); + /** Finalise digest + * + * @v ctx Context + * @v out Buffer for digest output + */ + void ( * final ) ( void *ctx, void *out ); +}; + +/** A cipher algorithm */ +struct cipher_algorithm { + /** Algorithm name */ + const char *name; + /** Context size */ + size_t ctxsize; + /** Block size */ + size_t blocksize; + /** Set key + * + * @v ctx Context + * @v key Key + * @v keylen Key length + * @ret rc Return status code + */ + int ( * setkey ) ( void *ctx, const void *key, size_t keylen ); + /** Set initialisation vector + * + * @v ctx Context + * @v iv Initialisation vector + */ + void ( * setiv ) ( void *ctx, const void *iv ); + /** Encrypt data + * + * @v ctx Context + * @v src Data to encrypt + * @v dst Buffer for encrypted data + * @v len Length of data + * + * @v len is guaranteed to be a multiple of @c blocksize. + */ + void ( * encrypt ) ( void *ctx, const void *src, void *dst, + size_t len ); + /** Decrypt data + * + * @v ctx Context + * @v src Data to decrypt + * @v dst Buffer for decrypted data + * @v len Length of data + * + * @v len is guaranteed to be a multiple of @c blocksize. + */ + void ( * decrypt ) ( void *ctx, const void *src, void *dst, + size_t len ); +}; + +/** A public key algorithm */ +struct pubkey_algorithm { + /** Algorithm name */ + const char *name; + /** Context size */ + size_t ctxsize; +}; + +static inline void digest_init ( struct digest_algorithm *digest, + void *ctx ) { + digest->init ( ctx ); +} + +static inline void digest_update ( struct digest_algorithm *digest, + void *ctx, const void *data, size_t len ) { + digest->update ( ctx, data, len ); +} + +static inline void digest_final ( struct digest_algorithm *digest, + void *ctx, void *out ) { + digest->final ( ctx, out ); +} + +static inline int cipher_setkey ( struct cipher_algorithm *cipher, + void *ctx, const void *key, size_t keylen ) { + return cipher->setkey ( ctx, key, keylen ); +} + +static inline void cipher_setiv ( struct cipher_algorithm *cipher, + void *ctx, const void *iv ) { + cipher->setiv ( ctx, iv ); +} + +static inline void cipher_encrypt ( struct cipher_algorithm *cipher, + void *ctx, const void *src, void *dst, + size_t len ) { + cipher->encrypt ( ctx, src, dst, len ); +} +#define cipher_encrypt( cipher, ctx, src, dst, len ) do { \ + assert ( ( len & ( (cipher)->blocksize - 1 ) ) == 0 ); \ + cipher_encrypt ( (cipher), (ctx), (src), (dst), (len) ); \ + } while ( 0 ) + +static inline void cipher_decrypt ( struct cipher_algorithm *cipher, + void *ctx, const void *src, void *dst, + size_t len ) { + cipher->decrypt ( ctx, src, dst, len ); +} +#define cipher_decrypt( cipher, ctx, src, dst, len ) do { \ + assert ( ( len & ( (cipher)->blocksize - 1 ) ) == 0 ); \ + cipher_decrypt ( (cipher), (ctx), (src), (dst), (len) ); \ + } while ( 0 ) + +static inline int is_stream_cipher ( struct cipher_algorithm *cipher ) { + return ( cipher->blocksize == 1 ); +} + +extern struct digest_algorithm digest_null; +extern struct cipher_algorithm cipher_null; +extern struct pubkey_algorithm pubkey_null; + +#endif /* _GPXE_CRYPTO_H */ diff --git a/gpxe/src/include/gpxe/device.h b/gpxe/src/include/gpxe/device.h new file mode 100644 index 0000000..f40cc95 --- /dev/null +++ b/gpxe/src/include/gpxe/device.h @@ -0,0 +1,108 @@ +#ifndef _GPXE_DEVICE_H +#define _GPXE_DEVICE_H + +/** + * @file + * + * Device model + * + */ + +#include +#include + +/** A hardware device description */ +struct device_description { + /** Bus type + * + * This must be a BUS_TYPE_XXX constant. + */ + unsigned int bus_type; + /** Location + * + * The interpretation of this field is bus-type-specific. + */ + unsigned int location; + /** Vendor ID */ + unsigned int vendor; + /** Device ID */ + unsigned int device; + /** Device class */ + unsigned long class; + /** I/O address */ + unsigned long ioaddr; + /** IRQ */ + unsigned int irq; +}; + +/** PCI bus type */ +#define BUS_TYPE_PCI 1 + +/** ISAPnP bus type */ +#define BUS_TYPE_ISAPNP 2 + +/** EISA bus type */ +#define BUS_TYPE_EISA 3 + +/** MCA bus type */ +#define BUS_TYPE_MCA 4 + +/** ISA bus type */ +#define BUS_TYPE_ISA 5 + +/** A hardware device */ +struct device { + /** Name */ + char name[16]; + /** Device description */ + struct device_description desc; + /** Devices on the same bus */ + struct list_head siblings; + /** Devices attached to this device */ + struct list_head children; + /** Bus device */ + struct device *parent; +}; + +/** + * A root device + * + * Root devices are system buses such as PCI, EISA, etc. + * + */ +struct root_device { + /** Device chain + * + * A root device has a NULL parent field. + */ + struct device dev; + /** Root device driver */ + struct root_driver *driver; +}; + +/** A root device driver */ +struct root_driver { + /** + * Add root device + * + * @v rootdev Root device + * @ret rc Return status code + * + * Called from probe_devices() for all root devices in the build. + */ + int ( * probe ) ( struct root_device *rootdev ); + /** + * Remove root device + * + * @v rootdev Root device + * + * Called from remove_device() for all successfully-probed + * root devices. + */ + void ( * remove ) ( struct root_device *rootdev ); +}; + +/** Declare a root device */ +#define __root_device __table ( struct root_device, root_devices, 01 ) + +#endif /* _GPXE_DEVICE_H */ diff --git a/gpxe/src/include/gpxe/dhcp.h b/gpxe/src/include/gpxe/dhcp.h new file mode 100644 index 0000000..33e0c5d --- /dev/null +++ b/gpxe/src/include/gpxe/dhcp.h @@ -0,0 +1,614 @@ +#ifndef _GPXE_DHCP_H +#define _GPXE_DHCP_H + +/** @file + * + * Dynamic Host Configuration Protocol + * + */ + +#include +#include +#include +#include +#include +#include +#include + +struct job_interface; +struct dhcp_options; +struct dhcp_packet; + +/** BOOTP/DHCP server port */ +#define BOOTPS_PORT 67 + +/** BOOTP/DHCP client port */ +#define BOOTPC_PORT 68 + +/** PXE server port */ +#define PXE_PORT 4011 + +/** Construct a tag value for an encapsulated option + * + * This tag value can be passed to Etherboot functions when searching + * for DHCP options in order to search for a tag within an + * encapsulated options block. + */ +#define DHCP_ENCAP_OPT( encapsulator, encapsulated ) \ + ( ( (encapsulator) << 8 ) | (encapsulated) ) +/** Extract encapsulating option block tag from encapsulated tag value */ +#define DHCP_ENCAPSULATOR( encap_opt ) ( (encap_opt) >> 8 ) +/** Extract encapsulated option tag from encapsulated tag value */ +#define DHCP_ENCAPSULATED( encap_opt ) ( (encap_opt) & 0xff ) +/** Option is encapsulated */ +#define DHCP_IS_ENCAP_OPT( opt ) DHCP_ENCAPSULATOR( opt ) + +/** + * @defgroup dhcpopts DHCP option tags + * @{ + */ + +/** Padding + * + * This tag does not have a length field; it is always only a single + * byte in length. + */ +#define DHCP_PAD 0 + +/** Minimum normal DHCP option */ +#define DHCP_MIN_OPTION 1 + +/** Subnet mask */ +#define DHCP_SUBNET_MASK 1 + +/** Routers */ +#define DHCP_ROUTERS 3 + +/** DNS servers */ +#define DHCP_DNS_SERVERS 6 + +/** Syslog servers */ +#define DHCP_LOG_SERVERS 7 + +/** Host name */ +#define DHCP_HOST_NAME 12 + +/** Domain name */ +#define DHCP_DOMAIN_NAME 15 + +/** Root path */ +#define DHCP_ROOT_PATH 17 + +/** Vendor encapsulated options */ +#define DHCP_VENDOR_ENCAP 43 + +/** PXE boot server discovery control */ +#define DHCP_PXE_DISCOVERY_CONTROL DHCP_ENCAP_OPT ( DHCP_VENDOR_ENCAP, 6 ) + +/** PXE boot server discovery control bits */ +enum dhcp_pxe_discovery_control { + /** Inhibit broadcast discovery */ + PXEBS_NO_BROADCAST = 1, + /** Inhibit multicast discovery */ + PXEBS_NO_MULTICAST = 2, + /** Accept only servers in DHCP_PXE_BOOT_SERVERS list */ + PXEBS_NO_UNKNOWN_SERVERS = 4, + /** Skip discovery if filename present */ + PXEBS_SKIP = 8, +}; + +/** PXE boot server multicast address */ +#define DHCP_PXE_BOOT_SERVER_MCAST DHCP_ENCAP_OPT ( DHCP_VENDOR_ENCAP, 7 ) + +/** PXE boot servers */ +#define DHCP_PXE_BOOT_SERVERS DHCP_ENCAP_OPT ( DHCP_VENDOR_ENCAP, 8 ) + +/** PXE boot server */ +struct dhcp_pxe_boot_server { + /** "Type" */ + uint16_t type; + /** Number of IPv4 addresses */ + uint8_t num_ip; + /** IPv4 addresses */ + struct in_addr ip[0]; +} __attribute__ (( packed )); + +/** PXE boot menu */ +#define DHCP_PXE_BOOT_MENU DHCP_ENCAP_OPT ( DHCP_VENDOR_ENCAP, 9 ) + +/** PXE boot menu */ +struct dhcp_pxe_boot_menu { + /** "Type" */ + uint16_t type; + /** Description length */ + uint8_t desc_len; + /** Description */ + char desc[0]; +} __attribute__ (( packed )); + +/** PXE boot menu prompt */ +#define DHCP_PXE_BOOT_MENU_PROMPT DHCP_ENCAP_OPT ( DHCP_VENDOR_ENCAP, 10 ) + +/** PXE boot menu prompt */ +struct dhcp_pxe_boot_menu_prompt { + /** Timeout + * + * A value of 0 means "time out immediately and select first + * boot item, without displaying the prompt". A value of 255 + * means "display menu immediately with no timeout". Any + * other value means "display prompt, wait this many seconds + * for keypress, if key is F8, display menu, otherwise select + * first boot item". + */ + uint8_t timeout; + /** Prompt to press F8 */ + char prompt[0]; +} __attribute__ (( packed )); + +/** PXE boot menu item */ +#define DHCP_PXE_BOOT_MENU_ITEM DHCP_ENCAP_OPT ( DHCP_VENDOR_ENCAP, 71 ) + +/** PXE boot menu item */ +struct dhcp_pxe_boot_menu_item { + /** "Type" + * + * This field actually identifies the specific boot server (or + * cluster of boot servers offering identical boot files). + */ + uint16_t type; + /** "Layer" + * + * Just don't ask. + */ + uint16_t layer; +} __attribute__ (( packed )); + +/** Requested IP address */ +#define DHCP_REQUESTED_ADDRESS 50 + +/** Lease time */ +#define DHCP_LEASE_TIME 51 + +/** Option overloading + * + * The value of this option is the bitwise-OR of zero or more + * DHCP_OPTION_OVERLOAD_XXX constants. + */ +#define DHCP_OPTION_OVERLOAD 52 + +/** The "file" field is overloaded to contain extra DHCP options */ +#define DHCP_OPTION_OVERLOAD_FILE 1 + +/** The "sname" field is overloaded to contain extra DHCP options */ +#define DHCP_OPTION_OVERLOAD_SNAME 2 + +/** DHCP message type */ +#define DHCP_MESSAGE_TYPE 53 +#define DHCPNONE 0 +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 +#define DHCPINFORM 8 + +/** DHCP server identifier */ +#define DHCP_SERVER_IDENTIFIER 54 + +/** Parameter request list */ +#define DHCP_PARAMETER_REQUEST_LIST 55 + +/** Maximum DHCP message size */ +#define DHCP_MAX_MESSAGE_SIZE 57 + +/** Vendor class identifier */ +#define DHCP_VENDOR_CLASS_ID 60 + +/** Client identifier */ +#define DHCP_CLIENT_ID 61 + +/** Client identifier */ +struct dhcp_client_id { + /** Link-layer protocol */ + uint8_t ll_proto; + /** Link-layer address */ + uint8_t ll_addr[MAX_LL_ADDR_LEN]; +} __attribute__ (( packed )); + +/** TFTP server name + * + * This option replaces the fixed "sname" field, when that field is + * used to contain overloaded options. + */ +#define DHCP_TFTP_SERVER_NAME 66 + +/** Bootfile name + * + * This option replaces the fixed "file" field, when that field is + * used to contain overloaded options. + */ +#define DHCP_BOOTFILE_NAME 67 + +/** User class identifier */ +#define DHCP_USER_CLASS_ID 77 + +/** Client system architecture */ +#define DHCP_CLIENT_ARCHITECTURE 93 + +/** Client network device interface */ +#define DHCP_CLIENT_NDI 94 + +/** UUID client identifier */ +#define DHCP_CLIENT_UUID 97 + +/** UUID client identifier */ +struct dhcp_client_uuid { + /** Identifier type */ + uint8_t type; + /** UUID */ + union uuid uuid; +} __attribute__ (( packed )); + +#define DHCP_CLIENT_UUID_TYPE 0 + +/** Etherboot-specific encapsulated options + * + * This encapsulated options field is used to contain all options + * specific to Etherboot (i.e. not assigned by IANA or other standards + * bodies). + */ +#define DHCP_EB_ENCAP 175 + +/** Priority of this options block + * + * This is a signed 8-bit integer field indicating the priority of + * this block of options. It can be used to specify the relative + * priority of multiple option blocks (e.g. options from non-volatile + * storage versus options from a DHCP server). + */ +#define DHCP_EB_PRIORITY DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0x01 ) + +/** "Your" IP address + * + * This option is used internally to contain the value of the "yiaddr" + * field, in order to provide a consistent approach to storing and + * processing options. It should never be present in a DHCP packet. + */ +#define DHCP_EB_YIADDR DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0x02 ) + +/** "Server" IP address + * + * This option is used internally to contain the value of the "siaddr" + * field, in order to provide a consistent approach to storing and + * processing options. It should never be present in a DHCP packet. + */ +#define DHCP_EB_SIADDR DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0x03 ) + +/** Keep SAN drive registered + * + * If set to a non-zero value, gPXE will not detach any SAN drive + * after failing to boot from it. (This option is required in order + * to perform a Windows Server 2008 installation direct to an iSCSI + * target.) + */ +#define DHCP_EB_KEEP_SAN DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0x08 ) + +/* + * Tags in the range 0x10-0x7f are reserved for feature markers + * + */ + +/** Skip PXE DHCP protocol extensions such as ProxyDHCP + * + * If set to a non-zero value, gPXE will not wait for ProxyDHCP offers + * and will ignore any PXE-specific DHCP options that it receives. + */ +#define DHCP_EB_NO_PXEDHCP DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xb0 ) + +/** Network device descriptor + * + * Byte 0 is the bus type ID; remaining bytes depend on the bus type. + * + * PCI devices: + * Byte 0 : 1 (PCI) + * Byte 1 : PCI vendor ID MSB + * Byte 2 : PCI vendor ID LSB + * Byte 3 : PCI device ID MSB + * Byte 4 : PCI device ID LSB + */ +#define DHCP_EB_BUS_ID DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xb1 ) + +/** Network device descriptor */ +struct dhcp_netdev_desc { + /** Bus type ID */ + uint8_t type; + /** Vendor ID */ + uint16_t vendor; + /** Device ID */ + uint16_t device; +} __attribute__ (( packed )); + +/** BIOS drive number + * + * This is the drive number for a drive emulated via INT 13. 0x80 is + * the first hard disk, 0x81 is the second hard disk, etc. + */ +#define DHCP_EB_BIOS_DRIVE DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xbd ) + +/** Username + * + * This will be used as the username for any required authentication. + * It is expected that this option's value will be held in + * non-volatile storage, rather than transmitted as part of a DHCP + * packet. + */ +#define DHCP_EB_USERNAME DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xbe ) + +/** Password + * + * This will be used as the password for any required authentication. + * It is expected that this option's value will be held in + * non-volatile storage, rather than transmitted as part of a DHCP + * packet. + */ +#define DHCP_EB_PASSWORD DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xbf ) + +/** Reverse username + * + * This will be used as the reverse username (i.e. the username + * provided by the server) for any required authentication. It is + * expected that this option's value will be held in non-volatile + * storage, rather than transmitted as part of a DHCP packet. + */ +#define DHCP_EB_REVERSE_USERNAME DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xc0 ) + +/** Reverse password + * + * This will be used as the reverse password (i.e. the password + * provided by the server) for any required authentication. It is + * expected that this option's value will be held in non-volatile + * storage, rather than transmitted as part of a DHCP packet. + */ +#define DHCP_EB_REVERSE_PASSWORD DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xc1 ) + +/** gPXE version number */ +#define DHCP_EB_VERSION DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xeb ) + +/** iSCSI primary target IQN */ +#define DHCP_ISCSI_PRIMARY_TARGET_IQN 201 + +/** iSCSI secondary target IQN */ +#define DHCP_ISCSI_SECONDARY_TARGET_IQN 202 + +/** iSCSI initiator IQN */ +#define DHCP_ISCSI_INITIATOR_IQN 203 + +/** Maximum normal DHCP option */ +#define DHCP_MAX_OPTION 254 + +/** End of options + * + * This tag does not have a length field; it is always only a single + * byte in length. + */ +#define DHCP_END 255 + +/** @} */ + +/** + * Count number of arguments to a variadic macro + * + * This rather neat, non-iterative solution is courtesy of Laurent + * Deniau. + * + */ +#define _VA_ARG_COUNT( _1, _2, _3, _4, _5, _6, _7, _8, \ + _9, _10, _11, _12, _13, _14, _15, _16, \ + _17, _18, _19, _20, _21, _22, _23, _24, \ + _25, _26, _27, _28, _29, _30, _31, _32, \ + _33, _34, _35, _36, _37, _38, _39, _40, \ + _41, _42, _43, _44, _45, _46, _47, _48, \ + _49, _50, _51, _52, _53, _54, _55, _56, \ + _57, _58, _59, _60, _61, _62, _63, N, ... ) N +#define VA_ARG_COUNT( ... ) \ + _VA_ARG_COUNT ( __VA_ARGS__, \ + 63, 62, 61, 60, 59, 58, 57, 56, \ + 55, 54, 53, 52, 51, 50, 49, 48, \ + 47, 46, 45, 44, 43, 42, 41, 40, \ + 39, 38, 37, 36, 35, 34, 33, 32, \ + 31, 30, 29, 28, 27, 26, 25, 24, \ + 23, 22, 21, 20, 19, 18, 17, 16, \ + 15, 14, 13, 12, 11, 10, 9, 8, \ + 7, 6, 5, 4, 3, 2, 1, 0 ) + +/** Construct a DHCP option from a list of bytes */ +#define DHCP_OPTION( ... ) VA_ARG_COUNT ( __VA_ARGS__ ), __VA_ARGS__ + +/** Construct a DHCP option from a list of characters */ +#define DHCP_STRING( ... ) DHCP_OPTION ( __VA_ARGS__ ) + +/** Construct a byte-valued DHCP option */ +#define DHCP_BYTE( value ) DHCP_OPTION ( value ) + +/** Construct a word-valued DHCP option */ +#define DHCP_WORD( value ) DHCP_OPTION ( ( ( (value) >> 8 ) & 0xff ), \ + ( ( (value) >> 0 ) & 0xff ) ) + +/** Construct a dword-valued DHCP option */ +#define DHCP_DWORD( value ) DHCP_OPTION ( ( ( (value) >> 24 ) & 0xff ), \ + ( ( (value) >> 16 ) & 0xff ), \ + ( ( (value) >> 8 ) & 0xff ), \ + ( ( (value) >> 0 ) & 0xff ) ) + +/** Construct a DHCP encapsulated options field */ +#define DHCP_ENCAP( ... ) DHCP_OPTION ( __VA_ARGS__, DHCP_END ) + +/** + * A DHCP option + * + * DHCP options consist of a mandatory tag, a length field that is + * mandatory for all options except @c DHCP_PAD and @c DHCP_END, and a + * payload. + */ +struct dhcp_option { + /** Tag + * + * Must be a @c DHCP_XXX value. + */ + uint8_t tag; + /** Length + * + * This is the length of the data field (i.e. excluding the + * tag and length fields). For the two tags @c DHCP_PAD and + * @c DHCP_END, the length field is implicitly zero and is + * also missing, i.e. these DHCP options are only a single + * byte in length. + */ + uint8_t len; + /** Option data */ + uint8_t data[0]; +} __attribute__ (( packed )); + +/** + * Length of a DHCP option header + * + * The header is the portion excluding the data, i.e. the tag and the + * length. + */ +#define DHCP_OPTION_HEADER_LEN ( offsetof ( struct dhcp_option, data ) ) + +/** Maximum length for a single DHCP option */ +#define DHCP_MAX_LEN 0xff + +/** + * A DHCP header + * + */ +struct dhcphdr { + /** Operation + * + * This must be either @c BOOTP_REQUEST or @c BOOTP_REPLY. + */ + uint8_t op; + /** Hardware address type + * + * This is an ARPHRD_XXX constant. Note that ARPHRD_XXX + * constants are nominally 16 bits wide; this could be + * considered to be a bug in the BOOTP/DHCP specification. + */ + uint8_t htype; + /** Hardware address length */ + uint8_t hlen; + /** Number of hops from server */ + uint8_t hops; + /** Transaction ID */ + uint32_t xid; + /** Seconds since start of acquisition */ + uint16_t secs; + /** Flags */ + uint16_t flags; + /** "Client" IP address + * + * This is filled in if the client already has an IP address + * assigned and can respond to ARP requests. + */ + struct in_addr ciaddr; + /** "Your" IP address + * + * This is the IP address assigned by the server to the client. + */ + struct in_addr yiaddr; + /** "Server" IP address + * + * This is the IP address of the next server to be used in the + * boot process. + */ + struct in_addr siaddr; + /** "Gateway" IP address + * + * This is the IP address of the DHCP relay agent, if any. + */ + struct in_addr giaddr; + /** Client hardware address */ + uint8_t chaddr[16]; + /** Server host name (null terminated) + * + * This field may be overridden and contain DHCP options + */ + char sname[64]; + /** Boot file name (null terminated) + * + * This field may be overridden and contain DHCP options + */ + char file[128]; + /** DHCP magic cookie + * + * Must have the value @c DHCP_MAGIC_COOKIE. + */ + uint32_t magic; + /** DHCP options + * + * Variable length; extends to the end of the packet. Minimum + * length (for the sake of sanity) is 1, to allow for a single + * @c DHCP_END tag. + */ + uint8_t options[0]; +}; + +/** Opcode for a request from client to server */ +#define BOOTP_REQUEST 1 + +/** Opcode for a reply from server to client */ +#define BOOTP_REPLY 2 + +/** BOOTP reply must be broadcast + * + * Clients that cannot accept unicast BOOTP replies must set this + * flag. + */ +#define BOOTP_FL_BROADCAST 0x8000 + +/** DHCP magic cookie */ +#define DHCP_MAGIC_COOKIE 0x63825363UL + +/** DHCP minimum packet length + * + * This is the mandated minimum packet length that a DHCP participant + * must be prepared to receive. + */ +#define DHCP_MIN_LEN 552 + +/** Timeouts for sending DHCP packets */ +#define DHCP_MIN_TIMEOUT ( 1 * TICKS_PER_SEC ) +#define DHCP_MAX_TIMEOUT ( 10 * TICKS_PER_SEC ) + +/** Maximum time that we will wait for ProxyDHCP responses */ +#define PROXYDHCP_MAX_TIMEOUT ( 2 * TICKS_PER_SEC ) + +/** Maximum time that we will wait for Boot Server responses */ +#define PXEBS_MAX_TIMEOUT ( 3 * TICKS_PER_SEC ) + +/** Settings block name used for DHCP responses */ +#define DHCP_SETTINGS_NAME "dhcp" + +/** Settings block name used for ProxyDHCP responses */ +#define PROXYDHCP_SETTINGS_NAME "proxydhcp" + +/** Setting block name used for BootServerDHCP responses */ +#define PXEBS_SETTINGS_NAME "pxebs" + +extern int dhcp_create_packet ( struct dhcp_packet *dhcppkt, + struct net_device *netdev, uint8_t msgtype, + const void *options, size_t options_len, + void *data, size_t max_len ); +extern int dhcp_create_request ( struct dhcp_packet *dhcppkt, + struct net_device *netdev, + unsigned int msgtype, struct in_addr ciaddr, + void *data, size_t max_len ); +extern int start_dhcp ( struct job_interface *job, struct net_device *netdev ); +extern int start_pxebs ( struct job_interface *job, struct net_device *netdev, + unsigned int pxe_type ); + +#endif /* _GPXE_DHCP_H */ diff --git a/gpxe/src/include/gpxe/dhcpopts.h b/gpxe/src/include/gpxe/dhcpopts.h new file mode 100644 index 0000000..8391a9d --- /dev/null +++ b/gpxe/src/include/gpxe/dhcpopts.h @@ -0,0 +1,32 @@ +#ifndef _GPXE_DHCPOPTS_H +#define _GPXE_DHCPOPTS_H + +/** @file + * + * DHCP options + * + */ + +#include + +/** A DHCP options block */ +struct dhcp_options { + /** Option block raw data */ + void *data; + /** Option block length */ + size_t len; + /** Option block maximum length */ + size_t max_len; +}; + +extern int dhcpopt_store ( struct dhcp_options *options, unsigned int tag, + const void *data, size_t len ); +extern int dhcpopt_extensible_store ( struct dhcp_options *options, + unsigned int tag, + const void *data, size_t len ); +extern int dhcpopt_fetch ( struct dhcp_options *options, unsigned int tag, + void *data, size_t len ); +extern void dhcpopt_init ( struct dhcp_options *options, + void *data, size_t max_len ); + +#endif /* _GPXE_DHCPOPTS_H */ diff --git a/gpxe/src/include/gpxe/dhcppkt.h b/gpxe/src/include/gpxe/dhcppkt.h new file mode 100644 index 0000000..e8f8faf --- /dev/null +++ b/gpxe/src/include/gpxe/dhcppkt.h @@ -0,0 +1,62 @@ +#ifndef _GPXE_DHCPPKT_H +#define _GPXE_DHCPPKT_H + +/** @file + * + * DHCP packets + * + */ + +#include +#include +#include + +/** + * A DHCP packet + * + */ +struct dhcp_packet { + /** Reference counter */ + struct refcnt refcnt; + /** The DHCP packet contents */ + struct dhcphdr *dhcphdr; + /** Maximum length of the DHCP packet buffer */ + size_t max_len; + /** Used length of the DHCP packet buffer */ + size_t len; + /** DHCP options */ + struct dhcp_options options; + /** Settings interface */ + struct settings settings; +}; + +/** + * Increment reference count on DHCP packet + * + * @v dhcppkt DHCP packet + * @ret dhcppkt DHCP packet + */ +static inline __attribute__ (( always_inline )) struct dhcp_packet * +dhcppkt_get ( struct dhcp_packet *dhcppkt ) { + ref_get ( &dhcppkt->refcnt ); + return dhcppkt; +} + +/** + * Decrement reference count on DHCP packet + * + * @v dhcppkt DHCP packet + */ +static inline __attribute__ (( always_inline )) void +dhcppkt_put ( struct dhcp_packet *dhcppkt ) { + ref_put ( &dhcppkt->refcnt ); +} + +extern int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag, + const void *data, size_t len ); +extern int dhcppkt_fetch ( struct dhcp_packet *dhcppkt, unsigned int tag, + void *data, size_t len ); +extern void dhcppkt_init ( struct dhcp_packet *dhcppkt, + struct dhcphdr *data, size_t len ); + +#endif /* _GPXE_DHCPPKT_H */ diff --git a/gpxe/src/include/gpxe/dns.h b/gpxe/src/include/gpxe/dns.h new file mode 100644 index 0000000..3e3cff1 --- /dev/null +++ b/gpxe/src/include/gpxe/dns.h @@ -0,0 +1,90 @@ +#ifndef _GPXE_DNS_H +#define _GPXE_DNS_H + +/** @file + * + * DNS protocol + * + */ + +#include +#include + +/* + * Constants + * + */ + +#define DNS_TYPE_A 1 +#define DNS_TYPE_CNAME 5 +#define DNS_TYPE_ANY 255 + +#define DNS_CLASS_IN 1 +#define DNS_CLASS_CS 2 +#define DNS_CLASS_CH 3 +#define DNS_CLASS_HS 4 + +#define DNS_FLAG_QUERY ( 0x00 << 15 ) +#define DNS_FLAG_RESPONSE ( 0x01 << 15 ) +#define DNS_FLAG_QR(flags) ( (flags) & ( 0x01 << 15 ) ) +#define DNS_FLAG_OPCODE_QUERY ( 0x00 << 11 ) +#define DNS_FLAG_OPCODE_IQUERY ( 0x01 << 11 ) +#define DNS_FLAG_OPCODE_STATUS ( 0x02 << 11 ) +#define DNS_FLAG_OPCODE(flags) ( (flags) & ( 0x0f << 11 ) ) +#define DNS_FLAG_RD ( 0x01 << 8 ) +#define DNS_FLAG_RA ( 0x01 << 7 ) +#define DNS_FLAG_RCODE_OK ( 0x00 << 0 ) +#define DNS_FLAG_RCODE_NX ( 0x03 << 0 ) +#define DNS_FLAG_RCODE(flags) ( (flags) & ( 0x0f << 0 ) ) + +#define DNS_PORT 53 +#define DNS_MAX_RETRIES 3 +#define DNS_MAX_CNAME_RECURSION 0x30 + +/* + * DNS protocol structures + * + */ +struct dns_header { + uint16_t id; + uint16_t flags; + uint16_t qdcount; + uint16_t ancount; + uint16_t nscount; + uint16_t arcount; +} __attribute__ (( packed )); + +struct dns_query_info { + uint16_t qtype; + uint16_t qclass; +} __attribute__ (( packed )); + +struct dns_query { + struct dns_header dns; + char payload[ 256 + sizeof ( struct dns_query_info ) ]; +} __attribute__ (( packed )); + +struct dns_rr_info_common { + uint16_t type; + uint16_t class; + uint32_t ttl; + uint16_t rdlength; +} __attribute__ (( packed )); + +struct dns_rr_info_a { + struct dns_rr_info_common common; + struct in_addr in_addr; +} __attribute__ (( packed )); + +struct dns_rr_info_cname { + struct dns_rr_info_common common; + char cname[0]; +} __attribute__ (( packed )); + +union dns_rr_info { + struct dns_rr_info_common common; + struct dns_rr_info_a a; + struct dns_rr_info_cname cname; +}; + +#endif /* _GPXE_DNS_H */ diff --git a/gpxe/src/include/gpxe/downloader.h b/gpxe/src/include/gpxe/downloader.h new file mode 100644 index 0000000..33aa769 --- /dev/null +++ b/gpxe/src/include/gpxe/downloader.h @@ -0,0 +1,17 @@ +#ifndef _GPXE_DOWNLOADER_H +#define _GPXE_DOWNLOADER_H + +/** @file + * + * Image downloader + * + */ + +struct job_interface; +struct image; + +extern int create_downloader ( struct job_interface *job, struct image *image, + int ( * register_image ) ( struct image *image ), + int type, ... ); + +#endif /* _GPXE_DOWNLOADER_H */ diff --git a/gpxe/src/include/gpxe/editbox.h b/gpxe/src/include/gpxe/editbox.h new file mode 100644 index 0000000..007b042 --- /dev/null +++ b/gpxe/src/include/gpxe/editbox.h @@ -0,0 +1,59 @@ +#ifndef _GPXE_EDITBOX_H +#define _GPXE_EDITBOX_H + +/** @file + * + * Editable text box widget + * + */ + +#include +#include + +/** An editable text box widget */ +struct edit_box { + /** Editable string */ + struct edit_string string; + /** Containing window */ + WINDOW *win; + /** Row */ + unsigned int row; + /** Starting column */ + unsigned int col; + /** Width */ + unsigned int width; + /** First displayed character */ + unsigned int first; + /** Flags */ + unsigned int flags; +}; + +/** Editable text box widget flags */ +enum edit_box_flags { + /** Show stars instead of contents (for password widgets) */ + EDITBOX_STARS = 0x0001, +}; + +extern void init_editbox ( struct edit_box *box, char *buf, size_t len, + WINDOW *win, unsigned int row, unsigned int col, + unsigned int width, unsigned int flags ) + __attribute__ (( nonnull (1, 2) )); +extern void draw_editbox ( struct edit_box *box ) __nonnull; +static inline int edit_editbox ( struct edit_box *box, int key ) __nonnull; + +/** + * Edit text box widget + * + * @v box Editable text box widget + * @v key Key pressed by user + * @ret key Key returned to application, or zero + * + * You must call draw_editbox() to update the display after calling + * edit_editbox(). + * + */ +static inline int edit_editbox ( struct edit_box *box, int key ) { + return edit_string ( &box->string, key ); +} + +#endif /* _GPXE_EDITBOX_H */ diff --git a/gpxe/src/include/gpxe/editstring.h b/gpxe/src/include/gpxe/editstring.h new file mode 100644 index 0000000..fad8bd5 --- /dev/null +++ b/gpxe/src/include/gpxe/editstring.h @@ -0,0 +1,31 @@ +#ifndef _GPXE_EDITSTRING_H +#define _GPXE_EDITSTRING_H + +/** @file + * + * Editable strings + * + */ + +/** An editable string */ +struct edit_string { + /** Buffer for string */ + char *buf; + /** Size of buffer (including terminating NUL) */ + size_t len; + /** Cursor position */ + unsigned int cursor; + + /* The following items are the edit history */ + + /** Last cursor position */ + unsigned int last_cursor; + /** Start of modified portion of string */ + unsigned int mod_start; + /** End of modified portion of string */ + unsigned int mod_end; +}; + +extern int edit_string ( struct edit_string *string, int key ) __nonnull; + +#endif /* _GPXE_EDITSTRING_H */ diff --git a/gpxe/src/include/gpxe/efi/Base.h b/gpxe/src/include/gpxe/efi/Base.h new file mode 100644 index 0000000..b2552d8 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Base.h @@ -0,0 +1,329 @@ +/** @file + Root include file for Mde Package Base type modules + + This is the include file for any module of type base. Base modules only use + types defined via this include file and can be ported easily to any + environment. There are a set of base libraries in the Mde Package that can + be used to implement base modules. + +Copyright (c) 2006 - 2008, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + + +#ifndef __BASE_H__ +#define __BASE_H__ + +// +// Include processor specific binding +// +#include + +typedef struct { + UINT32 Data1; + UINT16 Data2; + UINT16 Data3; + UINT8 Data4[8]; +} GUID; + +typedef UINT64 PHYSICAL_ADDRESS; + +/// +/// LIST_ENTRY definition +/// +typedef struct _LIST_ENTRY LIST_ENTRY; + +struct _LIST_ENTRY { + LIST_ENTRY *ForwardLink; + LIST_ENTRY *BackLink; +}; + +// +// Modifiers to absract standard types to aid in debug of problems +// +#define CONST const +#define STATIC static +#define VOID void + +// +// Modifiers for Data Types used to self document code. +// This concept is borrowed for UEFI specification. +// +#define IN +#define OUT +#define OPTIONAL + +// +// UEFI specification claims 1 and 0. We are concerned about the +// complier portability so we did it this way. +// +#define TRUE ((BOOLEAN)(1==1)) +#define FALSE ((BOOLEAN)(0==1)) + +#define NULL ((VOID *) 0) + +#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 +#define BIT32 0x0000000100000000UL +#define BIT33 0x0000000200000000UL +#define BIT34 0x0000000400000000UL +#define BIT35 0x0000000800000000UL +#define BIT36 0x0000001000000000UL +#define BIT37 0x0000002000000000UL +#define BIT38 0x0000004000000000UL +#define BIT39 0x0000008000000000UL +#define BIT40 0x0000010000000000UL +#define BIT41 0x0000020000000000UL +#define BIT42 0x0000040000000000UL +#define BIT43 0x0000080000000000UL +#define BIT44 0x0000100000000000UL +#define BIT45 0x0000200000000000UL +#define BIT46 0x0000400000000000UL +#define BIT47 0x0000800000000000UL +#define BIT48 0x0001000000000000UL +#define BIT49 0x0002000000000000UL +#define BIT50 0x0004000000000000UL +#define BIT51 0x0008000000000000UL +#define BIT52 0x0010000000000000UL +#define BIT53 0x0020000000000000UL +#define BIT54 0x0040000000000000UL +#define BIT55 0x0080000000000000UL +#define BIT56 0x0100000000000000UL +#define BIT57 0x0200000000000000UL +#define BIT58 0x0400000000000000UL +#define BIT59 0x0800000000000000UL +#define BIT60 0x1000000000000000UL +#define BIT61 0x2000000000000000UL +#define BIT62 0x4000000000000000UL +#define BIT63 0x8000000000000000UL + +// +// Support for variable length argument lists using the ANSI standard. +// +// Since we are using the ANSI standard we used the standard nameing and +// did not folow the coding convention +// +// VA_LIST - typedef for argument list. +// VA_START (VA_LIST Marker, argument before the ...) - Init Marker for use. +// VA_END (VA_LIST Marker) - Clear Marker +// VA_ARG (VA_LIST Marker, var arg size) - Use Marker to get an argumnet from +// the ... list. You must know the size and pass it in this macro. +// +// example: +// +// UINTN +// ExampleVarArg ( +// IN UINTN NumberOfArgs, +// ... +// ) +// { +// VA_LIST Marker; +// UINTN Index; +// UINTN Result; +// +// // +// // Initialize the Marker +// // +// VA_START (Marker, NumberOfArgs); +// for (Index = 0, Result = 0; Index < NumberOfArgs; Index++) { +// // +// // The ... list is a series of UINTN values, so average them up. +// // +// Result += VA_ARG (Marker, UINTN); +// } +// +// VA_END (Marker); +// return Result +// } +// + +#define _INT_SIZE_OF(n) ((sizeof (n) + sizeof (UINTN) - 1) &~(sizeof (UINTN) - 1)) + +// +// Also support coding convention rules for var arg macros +// +typedef CHAR8 *VA_LIST; +#define VA_START(ap, v) (ap = (VA_LIST) & (v) + _INT_SIZE_OF (v)) +#define VA_ARG(ap, t) (*(t *) ((ap += _INT_SIZE_OF (t)) - _INT_SIZE_OF (t))) +#define VA_END(ap) (ap = (VA_LIST) 0) + +// +// Macro that returns the byte offset of a field in a data structure. +// +#define OFFSET_OF(TYPE, Field) ((UINTN) &(((TYPE *)0)->Field)) + +/// +/// CONTAINING_RECORD - returns a pointer to the structure +/// from one of it's elements. +/// +#define _CR(Record, TYPE, Field) ((TYPE *) ((CHAR8 *) (Record) - (CHAR8 *) &(((TYPE *) 0)->Field))) + +/// +/// ALIGN_VALUE - aligns a value up to the next boundary of the given alignment. +/// +#define ALIGN_VALUE(Value, Alignment) ((Value) + (((Alignment) - (Value)) & ((Alignment) - 1))) + +/// +/// ALIGN_POINTER - aligns a pointer to the lowest boundry +/// +#define ALIGN_POINTER(Pointer, Alignment) ((VOID *) (ALIGN_VALUE ((UINTN)(Pointer), (Alignment)))) + +/// +/// ALIGN_VARIABLE - aligns a variable up to the next natural boundry for int size of a processor +/// +#define ALIGN_VARIABLE(Value) ALIGN_VALUE ((Value), sizeof (UINTN)) + + +// +// Return the maximum of two operands. +// This macro returns the maximum of two operand specified by a and b. +// Both a and b must be the same numerical types, signed or unsigned. +// +#define MAX(a, b) \ + (((a) > (b)) ? (a) : (b)) + + +// +// Return the minimum of two operands. +// This macro returns the minimal of two operand specified by a and b. +// Both a and b must be the same numerical types, signed or unsigned. +// +#define MIN(a, b) \ + (((a) < (b)) ? (a) : (b)) + + +// +// EFI Error Codes common to all execution phases +// + +typedef INTN RETURN_STATUS; + +/// +/// Set the upper bit to indicate EFI Error. +/// +#define ENCODE_ERROR(a) (MAX_BIT | (a)) + +#define ENCODE_WARNING(a) (a) +#define RETURN_ERROR(a) ((INTN) (a) < 0) + +#define RETURN_SUCCESS 0 +#define RETURN_LOAD_ERROR ENCODE_ERROR (1) +#define RETURN_INVALID_PARAMETER ENCODE_ERROR (2) +#define RETURN_UNSUPPORTED ENCODE_ERROR (3) +#define RETURN_BAD_BUFFER_SIZE ENCODE_ERROR (4) +#define RETURN_BUFFER_TOO_SMALL ENCODE_ERROR (5) +#define RETURN_NOT_READY ENCODE_ERROR (6) +#define RETURN_DEVICE_ERROR ENCODE_ERROR (7) +#define RETURN_WRITE_PROTECTED ENCODE_ERROR (8) +#define RETURN_OUT_OF_RESOURCES ENCODE_ERROR (9) +#define RETURN_VOLUME_CORRUPTED ENCODE_ERROR (10) +#define RETURN_VOLUME_FULL ENCODE_ERROR (11) +#define RETURN_NO_MEDIA ENCODE_ERROR (12) +#define RETURN_MEDIA_CHANGED ENCODE_ERROR (13) +#define RETURN_NOT_FOUND ENCODE_ERROR (14) +#define RETURN_ACCESS_DENIED ENCODE_ERROR (15) +#define RETURN_NO_RESPONSE ENCODE_ERROR (16) +#define RETURN_NO_MAPPING ENCODE_ERROR (17) +#define RETURN_TIMEOUT ENCODE_ERROR (18) +#define RETURN_NOT_STARTED ENCODE_ERROR (19) +#define RETURN_ALREADY_STARTED ENCODE_ERROR (20) +#define RETURN_ABORTED ENCODE_ERROR (21) +#define RETURN_ICMP_ERROR ENCODE_ERROR (22) +#define RETURN_TFTP_ERROR ENCODE_ERROR (23) +#define RETURN_PROTOCOL_ERROR ENCODE_ERROR (24) +#define RETURN_INCOMPATIBLE_VERSION ENCODE_ERROR (25) +#define RETURN_SECURITY_VIOLATION ENCODE_ERROR (26) +#define RETURN_CRC_ERROR ENCODE_ERROR (27) +#define RETURN_END_OF_MEDIA ENCODE_ERROR (28) +#define RETURN_END_OF_FILE ENCODE_ERROR (31) +#define RETURN_INVALID_LANGUAGE ENCODE_ERROR (32) + + +#define RETURN_WARN_UNKNOWN_GLYPH ENCODE_WARNING (1) +#define RETURN_WARN_DELETE_FAILURE ENCODE_WARNING (2) +#define RETURN_WARN_WRITE_FAILURE ENCODE_WARNING (3) +#define RETURN_WARN_BUFFER_TOO_SMALL ENCODE_WARNING (4) + +/** + Returns a 16-bit signature built from 2 ASCII characters. + + @param A The first ASCII character. + @param B The second ASCII character. + + @return A 16-bit value built from the two ASCII characters specified by A and B. + +**/ +#define SIGNATURE_16(A, B) ((A) | (B << 8)) + +/** + Returns a 32-bit signature built from 4 ASCII characters. + + @param A The first ASCII character. + @param B The second ASCII character. + @param C The third ASCII character. + @param D The fourth ASCII character. + + @return A 32-bit value built from the two ASCII characters specified by A, B, + C and D. + +**/ +#define SIGNATURE_32(A, B, C, D) (SIGNATURE_16 (A, B) | (SIGNATURE_16 (C, D) << 16)) + +/** + Returns a 64-bit signature built from 8 ASCII characters. + + @param A The first ASCII character. + @param B The second ASCII character. + @param C The third ASCII character. + @param D The fourth ASCII character. + @param E The fifth ASCII character. + @param F The sixth ASCII character. + @param G The seventh ASCII character. + @param H The eighth ASCII character. + + @return A 64-bit value built from the two ASCII characters specified by A, B, + C, D, E, F, G and H. + +**/ +#define SIGNATURE_64(A, B, C, D, E, F, G, H) \ + (SIGNATURE_32 (A, B, C, D) | ((UINT64) (SIGNATURE_32 (E, F, G, H)) << 32)) + +#endif + diff --git a/gpxe/src/include/gpxe/efi/Guid/PcAnsi.h b/gpxe/src/include/gpxe/efi/Guid/PcAnsi.h new file mode 100644 index 0000000..1dab310 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Guid/PcAnsi.h @@ -0,0 +1,58 @@ +/** @file + Terminal Device Path Vendor Guid. + + Copyright (c) 2006, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + GUIDs defined in UEFI 2.0 spec. + +**/ + +#ifndef __PC_ANSI_H__ +#define __PC_ANSI_H__ + +#define EFI_PC_ANSI_GUID \ + { \ + 0xe0c14753, 0xf9be, 0x11d2, {0x9a, 0x0c, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \ + } + +#define EFI_VT_100_GUID \ + { \ + 0xdfa66065, 0xb419, 0x11d3, {0x9a, 0x2d, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \ + } + +#define EFI_VT_100_PLUS_GUID \ + { \ + 0x7baec70b, 0x57e0, 0x4c76, {0x8e, 0x87, 0x2f, 0x9e, 0x28, 0x08, 0x83, 0x43 } \ + } + +#define EFI_VT_UTF8_GUID \ + { \ + 0xad15a0d6, 0x8bec, 0x4acf, {0xa0, 0x73, 0xd0, 0x1d, 0xe7, 0x7e, 0x2d, 0x88 } \ + } + +#define EFI_UART_DEVICE_PATH_GUID \ + { \ + 0x37499a9d, 0x542f, 0x4c89, {0xa0, 0x26, 0x35, 0xda, 0x14, 0x20, 0x94, 0xe4 } \ + } + +#define EFI_SAS_DEVICE_PATH_GUID \ + { \ + 0xd487ddb4, 0x008b, 0x11d9, {0xaf, 0xdc, 0x00, 0x10, 0x83, 0xff, 0xca, 0x4d } \ + } + +extern EFI_GUID gEfiPcAnsiGuid; +extern EFI_GUID gEfiVT100Guid; +extern EFI_GUID gEfiVT100PlusGuid; +extern EFI_GUID gEfiVTUTF8Guid; +extern EFI_GUID gEfiUartDevicePathGuid; +extern EFI_GUID gEfiSasDevicePathGuid; + +#endif diff --git a/gpxe/src/include/gpxe/efi/Guid/SmBios.h b/gpxe/src/include/gpxe/efi/Guid/SmBios.h new file mode 100644 index 0000000..c6c7f77 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Guid/SmBios.h @@ -0,0 +1,34 @@ +/** @file + GUIDs used to locate the SMBIOS tables in the UEFI 2.0 system table. + + This GUID in the system table is the only legal way to search for and + locate the SMBIOS tables. Do not search the 0xF0000 segment to find SMBIOS + tables. + + Copyright (c) 2006, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + GUIDs defined in UEFI 2.0 spec. + +**/ + +#ifndef __SMBIOS_GUID_H__ +#define __SMBIOS_GUID_H__ + +#define EFI_SMBIOS_TABLE_GUID \ + { \ + 0xeb9d2d31, 0x2d88, 0x11d3, {0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \ + } + +#define SMBIOS_TABLE_GUID EFI_SMBIOS_TABLE_GUID + +extern EFI_GUID gEfiSmbiosTableGuid; + +#endif diff --git a/gpxe/src/include/gpxe/efi/Ia32/ProcessorBind.h b/gpxe/src/include/gpxe/efi/Ia32/ProcessorBind.h new file mode 100644 index 0000000..5a3a918 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Ia32/ProcessorBind.h @@ -0,0 +1,215 @@ +/** @file + Processor or Compiler specific defines and types for Ia32 architecture. + + Copyright (c) 2006, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __PROCESSOR_BIND_H__ +#define __PROCESSOR_BIND_H__ + +/// +/// Define the processor type so other code can make processor based choices +/// +#define MDE_CPU_IA32 + +// +// Make sure we are useing the correct packing rules per EFI specification +// +#ifndef __GNUC__ +#pragma pack() +#endif + +#if __INTEL_COMPILER +// +// Disable ICC's remark #869: "Parameter" was never referenced warning. +// This is legal ANSI C code so we disable the remark that is turned on with -Wall +// +#pragma warning ( disable : 869 ) + +// +// Disable ICC's remark #1418: external function definition with no prior declaration. +// This is legal ANSI C code so we disable the remark that is turned on with /W4 +// +#pragma warning ( disable : 1418 ) + +// +// Disable ICC's remark #1419: external declaration in primary source file +// This is legal ANSI C code so we disable the remark that is turned on with /W4 +// +#pragma warning ( disable : 1419 ) + +#endif + + +#if _MSC_EXTENSIONS + +// +// Disable warning that make it impossible to compile at /W4 +// This only works for Microsoft* tools +// + +// +// Disabling bitfield type checking warnings. +// +#pragma warning ( disable : 4214 ) + +// +// Disabling the unreferenced formal parameter warnings. +// +#pragma warning ( disable : 4100 ) + +// +// Disable slightly different base types warning as CHAR8 * can not be set +// to a constant string. +// +#pragma warning ( disable : 4057 ) + +// +// ASSERT(FALSE) or while (TRUE) are legal constructes so supress this warning +// +#pragma warning ( disable : 4127 ) + +// +// This warning is caused by functions defined but not used. For precompiled header only. +// +#pragma warning ( disable : 4505 ) + +// +// This warning is caused by empty (after preprocessing) souce file. For precompiled header only. +// +#pragma warning ( disable : 4206 ) + +#endif + + +#if !defined(__GNUC__) && (__STDC_VERSION__ < 199901L) + // + // No ANSI C 2000 stdint.h integer width declarations, so define equivalents + // + + #if _MSC_EXTENSIONS + + // + // use Microsoft* C complier dependent interger width types + // + typedef unsigned __int64 UINT64; + typedef __int64 INT64; + typedef unsigned __int32 UINT32; + typedef __int32 INT32; + typedef unsigned short UINT16; + typedef unsigned short CHAR16; + typedef short INT16; + typedef unsigned char BOOLEAN; + typedef unsigned char UINT8; + typedef char CHAR8; + typedef char INT8; + #else + + // + // Assume standard IA-32 alignment. + // Need to check portability of long long + // + typedef unsigned long long UINT64; + typedef long long INT64; + typedef unsigned int UINT32; + typedef int INT32; + typedef unsigned short UINT16; + typedef unsigned short CHAR16; + typedef short INT16; + typedef unsigned char BOOLEAN; + typedef unsigned char UINT8; + typedef char CHAR8; + typedef char INT8; + #endif + + #define UINT8_MAX 0xff + +#else + // + // Use ANSI C 2000 stdint.h integer width declarations + // + #include "stdint.h" + typedef uint8_t BOOLEAN; + typedef int8_t INT8; + typedef uint8_t UINT8; + typedef int16_t INT16; + typedef uint16_t UINT16; + typedef int32_t INT32; + typedef uint32_t UINT32; + typedef int64_t INT64; + typedef uint64_t UINT64; + typedef char CHAR8; + typedef uint16_t CHAR16; + +#endif + +typedef UINT32 UINTN; +typedef INT32 INTN; + + +/// +/// Processor specific defines +/// +#define MAX_BIT 0x80000000 +#define MAX_2_BITS 0xC0000000 + +/// +/// Maximum legal IA-32 address +/// +#define MAX_ADDRESS 0xFFFFFFFF + +/// +/// The stack alignment required for IA-32 +/// +#define CPU_STACK_ALIGNMENT sizeof(UINTN) + +// +// Modifier to ensure that all protocol member functions and EFI intrinsics +// use the correct C calling convention. All protocol member functions and +// EFI intrinsics are required to modify thier member functions with EFIAPI. +// +#if _MSC_EXTENSIONS + /// + /// Microsoft* compiler requires _EFIAPI useage, __cdecl is Microsoft* specific C. + /// + #define EFIAPI __cdecl +#else + #if __GNUC__ + #define EFIAPI __attribute__((cdecl,regparm(0))) + #endif +#endif + +// +// The Microsoft* C compiler can removed references to unreferenced data items +// if the /OPT:REF linker option is used. We defined a macro as this is a +// a non standard extension +// +#if _MSC_EXTENSIONS + #define GLOBAL_REMOVE_IF_UNREFERENCED __declspec(selectany) +#else + #define GLOBAL_REMOVE_IF_UNREFERENCED +#endif + +// +// For symbol name in GNU assembly code, an extra "_" is necessary +// +#if __GNUC__ + #if defined(linux) + #define ASM_PFX(name) name + #else + #define ASM_PFX(name) _##name + #endif +#endif + +#define FUNCTION_ENTRY_POINT(p) (p) + +#endif + diff --git a/gpxe/src/include/gpxe/efi/IndustryStandard/Pci22.h b/gpxe/src/include/gpxe/efi/IndustryStandard/Pci22.h new file mode 100644 index 0000000..5b96a56 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/IndustryStandard/Pci22.h @@ -0,0 +1,601 @@ +/** @file + Support for PCI 2.2 standard. + + This file includes the definitions in the following specifications, + PCI Local Bus Specification, 2.0 + PCI-to-PCI Bridge Architecture Specification, + PC Card Standard, 8.0 + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _PCI22_H_ +#define _PCI22_H_ + +#define PCI_MAX_SEGMENT 0 +#define PCI_MAX_BUS 255 +#define PCI_MAX_DEVICE 31 +#define PCI_MAX_FUNC 7 + + +#pragma pack(1) +typedef struct { + UINT16 VendorId; + UINT16 DeviceId; + UINT16 Command; + UINT16 Status; + UINT8 RevisionID; + UINT8 ClassCode[3]; + UINT8 CacheLineSize; + UINT8 LatencyTimer; + UINT8 HeaderType; + UINT8 BIST; +} PCI_DEVICE_INDEPENDENT_REGION; + +typedef struct { + UINT32 Bar[6]; + UINT32 CISPtr; + UINT16 SubsystemVendorID; + UINT16 SubsystemID; + UINT32 ExpansionRomBar; + UINT8 CapabilityPtr; + UINT8 Reserved1[3]; + UINT32 Reserved2; + UINT8 InterruptLine; + UINT8 InterruptPin; + UINT8 MinGnt; + UINT8 MaxLat; +} PCI_DEVICE_HEADER_TYPE_REGION; + +typedef struct { + PCI_DEVICE_INDEPENDENT_REGION Hdr; + PCI_DEVICE_HEADER_TYPE_REGION Device; +} PCI_TYPE00; + +/// +/// defined in PCI-to-PCI Bridge Architecture Specification +/// +typedef struct { + UINT32 Bar[2]; + UINT8 PrimaryBus; + UINT8 SecondaryBus; + UINT8 SubordinateBus; + UINT8 SecondaryLatencyTimer; + UINT8 IoBase; + UINT8 IoLimit; + UINT16 SecondaryStatus; + UINT16 MemoryBase; + UINT16 MemoryLimit; + UINT16 PrefetchableMemoryBase; + UINT16 PrefetchableMemoryLimit; + UINT32 PrefetchableBaseUpper32; + UINT32 PrefetchableLimitUpper32; + UINT16 IoBaseUpper16; + UINT16 IoLimitUpper16; + UINT8 CapabilityPtr; + UINT8 Reserved[3]; + UINT32 ExpansionRomBAR; + UINT8 InterruptLine; + UINT8 InterruptPin; + UINT16 BridgeControl; +} PCI_BRIDGE_CONTROL_REGISTER; + +typedef struct { + PCI_DEVICE_INDEPENDENT_REGION Hdr; + PCI_BRIDGE_CONTROL_REGISTER Bridge; +} PCI_TYPE01; + +typedef union { + PCI_TYPE00 Device; + PCI_TYPE01 Bridge; +} PCI_TYPE_GENERIC; + +/// +/// CardBus Conroller Configuration Space, defined in PC Card Standard. 8.0 +/// +typedef struct { + UINT32 CardBusSocketReg; ///< Cardus Socket/ExCA Base + UINT8 Cap_Ptr; + UINT8 Reserved; + UINT16 SecondaryStatus; ///< Secondary Status + UINT8 PciBusNumber; ///< PCI Bus Number + UINT8 CardBusBusNumber; ///< CardBus Bus Number + UINT8 SubordinateBusNumber; ///< Subordinate Bus Number + UINT8 CardBusLatencyTimer; ///< CardBus Latency Timer + UINT32 MemoryBase0; ///< Memory Base Register 0 + UINT32 MemoryLimit0; ///< Memory Limit Register 0 + UINT32 MemoryBase1; + UINT32 MemoryLimit1; + UINT32 IoBase0; + UINT32 IoLimit0; ///< I/O Base Register 0 + UINT32 IoBase1; ///< I/O Limit Register 0 + UINT32 IoLimit1; + UINT8 InterruptLine; ///< Interrupt Line + UINT8 InterruptPin; ///< Interrupt Pin + UINT16 BridgeControl; ///< Bridge Control +} PCI_CARDBUS_CONTROL_REGISTER; + +/// +/// Definitions of PCI class bytes and manipulation macros. +/// +#define PCI_CLASS_OLD 0x00 +#define PCI_CLASS_OLD_OTHER 0x00 +#define PCI_CLASS_OLD_VGA 0x01 + +#define PCI_CLASS_MASS_STORAGE 0x01 +#define PCI_CLASS_MASS_STORAGE_SCSI 0x00 +#define PCI_CLASS_MASS_STORAGE_IDE 0x01 +#define PCI_CLASS_MASS_STORAGE_FLOPPY 0x02 +#define PCI_CLASS_MASS_STORAGE_IPI 0x03 +#define PCI_CLASS_MASS_STORAGE_RAID 0x04 +#define PCI_CLASS_MASS_STORAGE_OTHER 0x80 + +#define PCI_CLASS_NETWORK 0x02 +#define PCI_CLASS_NETWORK_ETHERNET 0x00 +#define PCI_CLASS_NETWORK_TOKENRING 0x01 +#define PCI_CLASS_NETWORK_FDDI 0x02 +#define PCI_CLASS_NETWORK_ATM 0x03 +#define PCI_CLASS_NETWORK_ISDN 0x04 +#define PCI_CLASS_NETWORK_OTHER 0x80 + +#define PCI_CLASS_DISPLAY 0x03 +#define PCI_CLASS_DISPLAY_VGA 0x00 +#define PCI_IF_VGA_VGA 0x00 +#define PCI_IF_VGA_8514 0x01 +#define PCI_CLASS_DISPLAY_XGA 0x01 +#define PCI_CLASS_DISPLAY_3D 0x02 +#define PCI_CLASS_DISPLAY_OTHER 0x80 +#define PCI_CLASS_DISPLAY_GFX 0x80 + +#define PCI_CLASS_MEDIA 0x04 +#define PCI_CLASS_MEDIA_VIDEO 0x00 +#define PCI_CLASS_MEDIA_AUDIO 0x01 +#define PCI_CLASS_MEDIA_TELEPHONE 0x02 +#define PCI_CLASS_MEDIA_OTHER 0x80 + +#define PCI_CLASS_MEMORY_CONTROLLER 0x05 +#define PCI_CLASS_MEMORY_RAM 0x00 +#define PCI_CLASS_MEMORY_FLASH 0x01 +#define PCI_CLASS_MEMORY_OTHER 0x80 + +#define PCI_CLASS_BRIDGE 0x06 +#define PCI_CLASS_BRIDGE_HOST 0x00 +#define PCI_CLASS_BRIDGE_ISA 0x01 +#define PCI_CLASS_BRIDGE_EISA 0x02 +#define PCI_CLASS_BRIDGE_MCA 0x03 +#define PCI_CLASS_BRIDGE_P2P 0x04 +#define PCI_IF_BRIDGE_P2P 0x00 +#define PCI_IF_BRIDGE_P2P_SUBTRACTIVE 0x01 +#define PCI_CLASS_BRIDGE_PCMCIA 0x05 +#define PCI_CLASS_BRIDGE_NUBUS 0x06 +#define PCI_CLASS_BRIDGE_CARDBUS 0x07 +#define PCI_CLASS_BRIDGE_RACEWAY 0x08 +#define PCI_CLASS_BRIDGE_OTHER 0x80 +#define PCI_CLASS_BRIDGE_ISA_PDECODE 0x80 + +#define PCI_CLASS_SCC 0x07 ///< Simple communications controllers +#define PCI_SUBCLASS_SERIAL 0x00 +#define PCI_IF_GENERIC_XT 0x00 +#define PCI_IF_16450 0x01 +#define PCI_IF_16550 0x02 +#define PCI_IF_16650 0x03 +#define PCI_IF_16750 0x04 +#define PCI_IF_16850 0x05 +#define PCI_IF_16950 0x06 +#define PCI_SUBCLASS_PARALLEL 0x01 +#define PCI_IF_PARALLEL_PORT 0x00 +#define PCI_IF_BI_DIR_PARALLEL_PORT 0x01 +#define PCI_IF_ECP_PARALLEL_PORT 0x02 +#define PCI_IF_1284_CONTROLLER 0x03 +#define PCI_IF_1284_DEVICE 0xFE +#define PCI_SUBCLASS_MULTIPORT_SERIAL 0x02 +#define PCI_SUBCLASS_MODEM 0x03 +#define PCI_IF_GENERIC_MODEM 0x00 +#define PCI_IF_16450_MODEM 0x01 +#define PCI_IF_16550_MODEM 0x02 +#define PCI_IF_16650_MODEM 0x03 +#define PCI_IF_16750_MODEM 0x04 +#define PCI_SUBCLASS_SCC_OTHER 0x80 + +#define PCI_CLASS_SYSTEM_PERIPHERAL 0x08 +#define PCI_SUBCLASS_PIC 0x00 +#define PCI_IF_8259_PIC 0x00 +#define PCI_IF_ISA_PIC 0x01 +#define PCI_IF_EISA_PIC 0x02 +#define PCI_IF_APIC_CONTROLLER 0x10 ///< I/O APIC interrupt controller , 32 bye none-prefectable memory. +#define PCI_IF_APIC_CONTROLLER2 0x20 +#define PCI_SUBCLASS_DMA 0x01 +#define PCI_IF_8237_DMA 0x00 +#define PCI_IF_ISA_DMA 0x01 +#define PCI_IF_EISA_DMA 0x02 +#define PCI_SUBCLASS_TIMER 0x02 +#define PCI_IF_8254_TIMER 0x00 +#define PCI_IF_ISA_TIMER 0x01 +#define PCI_IF_EISA_TIMER 0x02 +#define PCI_SUBCLASS_RTC 0x03 +#define PCI_IF_GENERIC_RTC 0x00 +#define PCI_IF_ISA_RTC 0x00 +#define PCI_SUBCLASS_PNP_CONTROLLER 0x04 ///< HotPlug Controller +#define PCI_SUBCLASS_PERIPHERAL_OTHER 0x80 + +#define PCI_CLASS_INPUT_DEVICE 0x09 +#define PCI_SUBCLASS_KEYBOARD 0x00 +#define PCI_SUBCLASS_PEN 0x01 +#define PCI_SUBCLASS_MOUSE_CONTROLLER 0x02 +#define PCI_SUBCLASS_SCAN_CONTROLLER 0x03 +#define PCI_SUBCLASS_GAMEPORT 0x04 +#define PCI_IF_GAMEPORT 0x00 +#define PCI_IF_GAMEPORT1 0x01 +#define PCI_SUBCLASS_INPUT_OTHER 0x80 + +#define PCI_CLASS_DOCKING_STATION 0x0A + +#define PCI_CLASS_PROCESSOR 0x0B +#define PCI_SUBCLASS_PROC_386 0x00 +#define PCI_SUBCLASS_PROC_486 0x01 +#define PCI_SUBCLASS_PROC_PENTIUM 0x02 +#define PCI_SUBCLASS_PROC_ALPHA 0x10 +#define PCI_SUBCLASS_PROC_POWERPC 0x20 +#define PCI_SUBCLASS_PROC_MIPS 0x30 +#define PCI_SUBCLASS_PROC_CO_PORC 0x40 ///< Co-Processor + +#define PCI_CLASS_SERIAL 0x0C +#define PCI_CLASS_SERIAL_FIREWIRE 0x00 +#define PCI_IF_1394 0x00 +#define PCI_IF_1394_OPEN_HCI 0x10 +#define PCI_CLASS_SERIAL_ACCESS_BUS 0x01 +#define PCI_CLASS_SERIAL_SSA 0x02 +#define PCI_CLASS_SERIAL_USB 0x03 +#define PCI_IF_UHCI 0x00 +#define PCI_IF_OHCI 0x10 +#define PCI_IF_USB_OTHER 0x80 +#define PCI_IF_USB_DEVICE 0xFE +#define PCI_CLASS_SERIAL_FIBRECHANNEL 0x04 +#define PCI_CLASS_SERIAL_SMB 0x05 + +#define PCI_CLASS_WIRELESS 0x0D +#define PCI_SUBCLASS_IRDA 0x00 +#define PCI_SUBCLASS_IR 0x01 +#define PCI_SUBCLASS_RF 0x02 +#define PCI_SUBCLASS_WIRELESS_OTHER 0x80 + +#define PCI_CLASS_INTELLIGENT_IO 0x0E + +#define PCI_CLASS_SATELLITE 0x0F +#define PCI_SUBCLASS_TV 0x01 +#define PCI_SUBCLASS_AUDIO 0x02 +#define PCI_SUBCLASS_VOICE 0x03 +#define PCI_SUBCLASS_DATA 0x04 + +#define PCI_SECURITY_CONTROLLER 0x10 ///< Encryption and decryption controller +#define PCI_SUBCLASS_NET_COMPUT 0x00 +#define PCI_SUBCLASS_ENTERTAINMENT 0x10 +#define PCI_SUBCLASS_SECURITY_OTHER 0x80 + +#define PCI_CLASS_DPIO 0x11 +#define PCI_SUBCLASS_DPIO 0x00 +#define PCI_SUBCLASS_DPIO_OTHER 0x80 + +#define IS_CLASS1(_p, c) ((_p)->Hdr.ClassCode[2] == (c)) +#define IS_CLASS2(_p, c, s) (IS_CLASS1 (_p, c) && ((_p)->Hdr.ClassCode[1] == (s))) +#define IS_CLASS3(_p, c, s, p) (IS_CLASS2 (_p, c, s) && ((_p)->Hdr.ClassCode[0] == (p))) + +#define IS_PCI_DISPLAY(_p) IS_CLASS1 (_p, PCI_CLASS_DISPLAY) +#define IS_PCI_VGA(_p) IS_CLASS3 (_p, PCI_CLASS_DISPLAY, PCI_CLASS_DISPLAY_VGA, 0) +#define IS_PCI_8514(_p) IS_CLASS3 (_p, PCI_CLASS_DISPLAY, PCI_CLASS_DISPLAY_VGA, 1) +#define IS_PCI_GFX(_p) IS_CLASS3 (_p, PCI_CLASS_DISPLAY, PCI_CLASS_DISPLAY_GFX, 0) +#define IS_PCI_OLD(_p) IS_CLASS1 (_p, PCI_CLASS_OLD) +#define IS_PCI_OLD_VGA(_p) IS_CLASS2 (_p, PCI_CLASS_OLD, PCI_CLASS_OLD_VGA) +#define IS_PCI_IDE(_p) IS_CLASS2 (_p, PCI_CLASS_MASS_STORAGE, PCI_CLASS_MASS_STORAGE_IDE) +#define IS_PCI_SCSI(_p) IS_CLASS3 (_p, PCI_CLASS_MASS_STORAGE, PCI_CLASS_MASS_STORAGE_SCSI, 0) +#define IS_PCI_RAID(_p) IS_CLASS3 (_p, PCI_CLASS_MASS_STORAGE, PCI_CLASS_MASS_STORAGE_RAID, 0) +#define IS_PCI_LPC(_p) IS_CLASS3 (_p, PCI_CLASS_BRIDGE, PCI_CLASS_BRIDGE_ISA, 0) +#define IS_PCI_P2P(_p) IS_CLASS3 (_p, PCI_CLASS_BRIDGE, PCI_CLASS_BRIDGE_P2P, 0) +#define IS_PCI_P2P_SUB(_p) IS_CLASS3 (_p, PCI_CLASS_BRIDGE, PCI_CLASS_BRIDGE_P2P, 1) +#define IS_PCI_16550_SERIAL(_p) IS_CLASS3 (_p, PCI_CLASS_SCC, PCI_SUBCLASS_SERIAL, PCI_IF_16550) +#define IS_PCI_USB(_p) IS_CLASS2 (_p, PCI_CLASS_SERIAL, PCI_CLASS_SERIAL_USB) + +// +// the definition of Header Type +// +#define HEADER_TYPE_DEVICE 0x00 +#define HEADER_TYPE_PCI_TO_PCI_BRIDGE 0x01 +#define HEADER_TYPE_CARDBUS_BRIDGE 0x02 +#define HEADER_TYPE_MULTI_FUNCTION 0x80 +// +// Mask of Header type +// +#define HEADER_LAYOUT_CODE 0x7f + +#define IS_PCI_BRIDGE(_p) (((_p)->Hdr.HeaderType & HEADER_LAYOUT_CODE) == (HEADER_TYPE_PCI_TO_PCI_BRIDGE)) +#define IS_CARDBUS_BRIDGE(_p) (((_p)->Hdr.HeaderType & HEADER_LAYOUT_CODE) == (HEADER_TYPE_CARDBUS_BRIDGE)) +#define IS_PCI_MULTI_FUNC(_p) ((_p)->Hdr.HeaderType & HEADER_TYPE_MULTI_FUNCTION) + +/// +/// Rom Base Address in Bridge, defined in PCI-to-PCI Bridge Architecure Specification, +/// +#define PCI_BRIDGE_ROMBAR 0x38 + +#define PCI_MAX_BAR 0x0006 +#define PCI_MAX_CONFIG_OFFSET 0x0100 + +#define PCI_VENDOR_ID_OFFSET 0x00 +#define PCI_DEVICE_ID_OFFSET 0x02 +#define PCI_COMMAND_OFFSET 0x04 +#define PCI_PRIMARY_STATUS_OFFSET 0x06 +#define PCI_REVISION_ID_OFFSET 0x08 +#define PCI_CLASSCODE_OFFSET 0x09 +#define PCI_CACHELINE_SIZE_OFFSET 0x0C +#define PCI_LATENCY_TIMER_OFFSET 0x0D +#define PCI_HEADER_TYPE_OFFSET 0x0E +#define PCI_BIST_OFFSET 0x0F +#define PCI_BASE_ADDRESSREG_OFFSET 0x10 +#define PCI_CARDBUS_CIS_OFFSET 0x28 +#define PCI_SVID_OFFSET 0x2C ///< SubSystem Vendor id +#define PCI_SUBSYSTEM_VENDOR_ID_OFFSET 0x2C +#define PCI_SID_OFFSET 0x2E ///< SubSystem ID +#define PCI_SUBSYSTEM_ID_OFFSET 0x2E +#define PCI_EXPANSION_ROM_BASE 0x30 +#define PCI_CAPBILITY_POINTER_OFFSET 0x34 +#define PCI_INT_LINE_OFFSET 0x3C ///< Interrupt Line Register +#define PCI_INT_PIN_OFFSET 0x3D ///< Interrupt Pin Register +#define PCI_MAXGNT_OFFSET 0x3E ///< Max Grant Register +#define PCI_MAXLAT_OFFSET 0x3F ///< Max Latency Register + +/// +/// defined in PCI-to-PCI Bridge Architecture Specification +/// +#define PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET 0x18 +#define PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET 0x19 +#define PCI_BRIDGE_SUBORDINATE_BUS_REGISTER_OFFSET 0x1a +#define PCI_BRIDGE_STATUS_REGISTER_OFFSET 0x1E +#define PCI_BRIDGE_CONTROL_REGISTER_OFFSET 0x3E + +/// +/// Interrupt Line "Unknown" or "No connection" value defined for x86 based system +/// +#define PCI_INT_LINE_UNKNOWN 0xFF + +typedef union { + struct { + UINT32 Reg : 8; + UINT32 Func : 3; + UINT32 Dev : 5; + UINT32 Bus : 8; + UINT32 Reserved : 7; + UINT32 Enable : 1; + } Bits; + UINT32 Uint32; +} PCI_CONFIG_ACCESS_CF8; + +#pragma pack() + +#define EFI_PCI_COMMAND_IO_SPACE BIT0 ///< 0x0001 +#define EFI_PCI_COMMAND_MEMORY_SPACE BIT1 ///< 0x0002 +#define EFI_PCI_COMMAND_BUS_MASTER BIT2 ///< 0x0004 +#define EFI_PCI_COMMAND_SPECIAL_CYCLE BIT3 ///< 0x0008 +#define EFI_PCI_COMMAND_MEMORY_WRITE_AND_INVALIDATE BIT4 ///< 0x0010 +#define EFI_PCI_COMMAND_VGA_PALETTE_SNOOP BIT5 ///< 0x0020 +#define EFI_PCI_COMMAND_PARITY_ERROR_RESPOND BIT6 ///< 0x0040 +#define EFI_PCI_COMMAND_STEPPING_CONTROL BIT7 ///< 0x0080 +#define EFI_PCI_COMMAND_SERR BIT8 ///< 0x0100 +#define EFI_PCI_COMMAND_FAST_BACK_TO_BACK BIT9 ///< 0x0200 + +/// +/// defined in PCI-to-PCI Bridge Architecture Specification +/// +#define EFI_PCI_BRIDGE_CONTROL_PARITY_ERROR_RESPONSE BIT0 ///< 0x0001 +#define EFI_PCI_BRIDGE_CONTROL_SERR BIT1 ///< 0x0002 +#define EFI_PCI_BRIDGE_CONTROL_ISA BIT2 ///< 0x0004 +#define EFI_PCI_BRIDGE_CONTROL_VGA BIT3 ///< 0x0008 +#define EFI_PCI_BRIDGE_CONTROL_VGA_16 BIT4 ///< 0x0010 +#define EFI_PCI_BRIDGE_CONTROL_MASTER_ABORT BIT5 ///< 0x0020 +#define EFI_PCI_BRIDGE_CONTROL_RESET_SECONDARY_BUS BIT6 ///< 0x0040 +#define EFI_PCI_BRIDGE_CONTROL_FAST_BACK_TO_BACK BIT7 ///< 0x0080 +#define EFI_PCI_BRIDGE_CONTROL_PRIMARY_DISCARD_TIMER BIT8 ///< 0x0100 +#define EFI_PCI_BRIDGE_CONTROL_SECONDARY_DISCARD_TIMER BIT9 ///< 0x0200 +#define EFI_PCI_BRIDGE_CONTROL_TIMER_STATUS BIT10 ///< 0x0400 +#define EFI_PCI_BRIDGE_CONTROL_DISCARD_TIMER_SERR BIT11 ///< 0x0800 + +/// +/// Following are the PCI-CARDBUS bridge control bit, defined in PC Card Standard +/// +#define EFI_PCI_BRIDGE_CONTROL_IREQINT_ENABLE BIT7 ///< 0x0080 +#define EFI_PCI_BRIDGE_CONTROL_RANGE0_MEMORY_TYPE BIT8 ///< 0x0100 +#define EFI_PCI_BRIDGE_CONTROL_RANGE1_MEMORY_TYPE BIT9 ///< 0x0200 +#define EFI_PCI_BRIDGE_CONTROL_WRITE_POSTING_ENABLE BIT10 ///< 0x0400 + +// +// Following are the PCI status control bit +// +#define EFI_PCI_STATUS_CAPABILITY BIT4 ///< 0x0010 +#define EFI_PCI_STATUS_66MZ_CAPABLE BIT5 ///< 0x0020 +#define EFI_PCI_FAST_BACK_TO_BACK_CAPABLE BIT7 ///< 0x0080 +#define EFI_PCI_MASTER_DATA_PARITY_ERROR BIT8 ///< 0x0100 + +/// +/// defined in PC Card Standard +/// +#define EFI_PCI_CARDBUS_BRIDGE_CAPABILITY_PTR 0x14 + +#pragma pack(1) +// +// PCI Capability List IDs and records +// +#define EFI_PCI_CAPABILITY_ID_PMI 0x01 +#define EFI_PCI_CAPABILITY_ID_AGP 0x02 +#define EFI_PCI_CAPABILITY_ID_VPD 0x03 +#define EFI_PCI_CAPABILITY_ID_SLOTID 0x04 +#define EFI_PCI_CAPABILITY_ID_MSI 0x05 +#define EFI_PCI_CAPABILITY_ID_HOTPLUG 0x06 +typedef struct { + UINT8 CapabilityID; + UINT8 NextItemPtr; +} EFI_PCI_CAPABILITY_HDR; + +/// +/// Capability EFI_PCI_CAPABILITY_ID_PMI, defined in PCI Power Management Interface Specifiction +/// +typedef struct { + EFI_PCI_CAPABILITY_HDR Hdr; + UINT16 PMC; + UINT16 PMCSR; + UINT8 BridgeExtention; + UINT8 Data; +} EFI_PCI_CAPABILITY_PMI; + +/// +/// Capability EFI_PCI_CAPABILITY_ID_AGP, defined in Accelerated Graphics Port Interface Specification +/// +typedef struct { + EFI_PCI_CAPABILITY_HDR Hdr; + UINT8 Rev; + UINT8 Reserved; + UINT32 Status; + UINT32 Command; +} EFI_PCI_CAPABILITY_AGP; + +/// +/// Capability EFI_PCI_CAPABILITY_ID_VPD, in PCI2.2 Spec. +/// +typedef struct { + EFI_PCI_CAPABILITY_HDR Hdr; + UINT16 AddrReg; + UINT32 DataReg; +} EFI_PCI_CAPABILITY_VPD; + +/// +/// Capability EFI_PCI_CAPABILITY_ID_SLOTID, defined in PCI-to-PCI Bridge Architeture Specification +/// +typedef struct { + EFI_PCI_CAPABILITY_HDR Hdr; + UINT8 ExpnsSlotReg; + UINT8 ChassisNo; +} EFI_PCI_CAPABILITY_SLOTID; + +/// +/// Capability EFI_PCI_CAPABILITY_ID_MSI, defined in PCI2.2 +/// +typedef struct { + EFI_PCI_CAPABILITY_HDR Hdr; + UINT16 MsgCtrlReg; + UINT32 MsgAddrReg; + UINT16 MsgDataReg; +} EFI_PCI_CAPABILITY_MSI32; + +typedef struct { + EFI_PCI_CAPABILITY_HDR Hdr; + UINT16 MsgCtrlReg; + UINT32 MsgAddrRegLsdw; + UINT32 MsgAddrRegMsdw; + UINT16 MsgDataReg; +} EFI_PCI_CAPABILITY_MSI64; + +/// +/// Capability EFI_PCI_CAPABILITY_ID_HOTPLUG, defined in CompactPCI Hot Swap Specification PICMG 2.1, R1.0 +/// +typedef struct { + EFI_PCI_CAPABILITY_HDR Hdr; + /// + /// not finished - fields need to go here + /// +} EFI_PCI_CAPABILITY_HOTPLUG; + +#define DEVICE_ID_NOCARE 0xFFFF + +#define PCI_ACPI_UNUSED 0 +#define PCI_BAR_NOCHANGE 0 +#define PCI_BAR_OLD_ALIGN 0xFFFFFFFFFFFFFFFFULL +#define PCI_BAR_EVEN_ALIGN 0xFFFFFFFFFFFFFFFEULL +#define PCI_BAR_SQUAD_ALIGN 0xFFFFFFFFFFFFFFFDULL +#define PCI_BAR_DQUAD_ALIGN 0xFFFFFFFFFFFFFFFCULL + +#define PCI_BAR_IDX0 0x00 +#define PCI_BAR_IDX1 0x01 +#define PCI_BAR_IDX2 0x02 +#define PCI_BAR_IDX3 0x03 +#define PCI_BAR_IDX4 0x04 +#define PCI_BAR_IDX5 0x05 +#define PCI_BAR_ALL 0xFF + +/// +/// EFI PCI Option ROM definitions +/// +#define EFI_ROOT_BRIDGE_LIST 'eprb' +#define EFI_PCI_EXPANSION_ROM_HEADER_EFISIGNATURE 0x0EF1 ///< defined in UEFI Spec. + +typedef struct { + UINT8 Register; + UINT8 Function; + UINT8 Device; + UINT8 Bus; + UINT8 Reserved[4]; +} DEFIO_PCI_ADDR; + +#define PCI_EXPANSION_ROM_HEADER_SIGNATURE 0xaa55 +#define PCI_DATA_STRUCTURE_SIGNATURE SIGNATURE_32 ('P', 'C', 'I', 'R') +#define PCI_CODE_TYPE_PCAT_IMAGE 0x00 +#define EFI_PCI_EXPANSION_ROM_HEADER_COMPRESSED 0x0001 ///FileHeader.SizeOfOptionalHeader \ + ) \ + ) + +/// +/// Other Windows Subsystem Values +/// +#define EFI_IMAGE_SUBSYSTEM_UNKNOWN 0 +#define EFI_IMAGE_SUBSYSTEM_NATIVE 1 +#define EFI_IMAGE_SUBSYSTEM_WINDOWS_GUI 2 +#define EFI_IMAGE_SUBSYSTEM_WINDOWS_CUI 3 +#define EFI_IMAGE_SUBSYSTEM_OS2_CUI 5 +#define EFI_IMAGE_SUBSYSTEM_POSIX_CUI 7 + +/// +/// Section header format. +/// +#define EFI_IMAGE_SIZEOF_SHORT_NAME 8 + +typedef struct { + UINT8 Name[EFI_IMAGE_SIZEOF_SHORT_NAME]; + union { + UINT32 PhysicalAddress; + UINT32 VirtualSize; + } Misc; + UINT32 VirtualAddress; + UINT32 SizeOfRawData; + UINT32 PointerToRawData; + UINT32 PointerToRelocations; + UINT32 PointerToLinenumbers; + UINT16 NumberOfRelocations; + UINT16 NumberOfLinenumbers; + UINT32 Characteristics; +} EFI_IMAGE_SECTION_HEADER; + +/// +/// Size of EFI_IMAGE_SECTION_HEADER +/// +#define EFI_IMAGE_SIZEOF_SECTION_HEADER 40 + +/// +/// Section Flags Values +/// +#define EFI_IMAGE_SCN_TYPE_NO_PAD BIT3 ///< 0x00000008 ///< Reserved. +#define EFI_IMAGE_SCN_CNT_CODE BIT5 ///< 0x00000020 +#define EFI_IMAGE_SCN_CNT_INITIALIZED_DATA BIT6 ///< 0x00000040 +#define EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA BIT7 ///< 0x00000080 + +#define EFI_IMAGE_SCN_LNK_OTHER BIT8 ///< 0x00000100 ///< Reserved. +#define EFI_IMAGE_SCN_LNK_INFO BIT9 ///< 0x00000200 ///< Section contains comments or some other type of information. +#define EFI_IMAGE_SCN_LNK_REMOVE BIT10 ///< 0x00000800 ///< Section contents will not become part of image. +#define EFI_IMAGE_SCN_LNK_COMDAT BIT12 ///< 0x00001000 + +#define EFI_IMAGE_SCN_ALIGN_1BYTES BIT20 ///< 0x00100000 +#define EFI_IMAGE_SCN_ALIGN_2BYTES BIT21 ///< 0x00200000 +#define EFI_IMAGE_SCN_ALIGN_4BYTES (BIT20|BIT21) ///< 0x00300000 +#define EFI_IMAGE_SCN_ALIGN_8BYTES BIT22 ///< 0x00400000 +#define EFI_IMAGE_SCN_ALIGN_16BYTES (BIT20|BIT22) ///< 0x00500000 +#define EFI_IMAGE_SCN_ALIGN_32BYTES (BIT21|BIT22) ///< 0x00600000 +#define EFI_IMAGE_SCN_ALIGN_64BYTES (BIT20|BIT21|BIT22) ///< 0x00700000 + +#define EFI_IMAGE_SCN_MEM_DISCARDABLE BIT25 ///< 0x02000000 +#define EFI_IMAGE_SCN_MEM_NOT_CACHED BIT26 ///< 0x04000000 +#define EFI_IMAGE_SCN_MEM_NOT_PAGED BIT27 ///< 0x08000000 +#define EFI_IMAGE_SCN_MEM_SHARED BIT28 ///< 0x10000000 +#define EFI_IMAGE_SCN_MEM_EXECUTE BIT29 ///< 0x20000000 +#define EFI_IMAGE_SCN_MEM_READ BIT30 ///< 0x40000000 +#define EFI_IMAGE_SCN_MEM_WRITE BIT31 ///< 0x80000000 + +/// +/// Size of a Symbol Table Record +/// +#define EFI_IMAGE_SIZEOF_SYMBOL 18 + +/// +/// Symbols have a section number of the section in which they are +/// defined. Otherwise, section numbers have the following meanings: +/// +#define EFI_IMAGE_SYM_UNDEFINED (UINT16) 0 ///< Symbol is undefined or is common. +#define EFI_IMAGE_SYM_ABSOLUTE (UINT16) -1 ///< Symbol is an absolute value. +#define EFI_IMAGE_SYM_DEBUG (UINT16) -2 ///< Symbol is a special debug item. + +/// +/// Symbol Type (fundamental) values. +/// +#define EFI_IMAGE_SYM_TYPE_NULL 0 ///< no type. +#define EFI_IMAGE_SYM_TYPE_VOID 1 ///< no valid type. +#define EFI_IMAGE_SYM_TYPE_CHAR 2 ///< type character. +#define EFI_IMAGE_SYM_TYPE_SHORT 3 ///< type short integer. +#define EFI_IMAGE_SYM_TYPE_INT 4 +#define EFI_IMAGE_SYM_TYPE_LONG 5 +#define EFI_IMAGE_SYM_TYPE_FLOAT 6 +#define EFI_IMAGE_SYM_TYPE_DOUBLE 7 +#define EFI_IMAGE_SYM_TYPE_STRUCT 8 +#define EFI_IMAGE_SYM_TYPE_UNION 9 +#define EFI_IMAGE_SYM_TYPE_ENUM 10 ///< enumeration. +#define EFI_IMAGE_SYM_TYPE_MOE 11 ///< member of enumeration. +#define EFI_IMAGE_SYM_TYPE_BYTE 12 +#define EFI_IMAGE_SYM_TYPE_WORD 13 +#define EFI_IMAGE_SYM_TYPE_UINT 14 +#define EFI_IMAGE_SYM_TYPE_DWORD 15 + +/// +/// Symbol Type (derived) values. +/// +#define EFI_IMAGE_SYM_DTYPE_NULL 0 ///< no derived type. +#define EFI_IMAGE_SYM_DTYPE_POINTER 1 +#define EFI_IMAGE_SYM_DTYPE_FUNCTION 2 +#define EFI_IMAGE_SYM_DTYPE_ARRAY 3 + +/// +/// Storage classes. +/// +#define EFI_IMAGE_SYM_CLASS_END_OF_FUNCTION ((UINT8) -1) +#define EFI_IMAGE_SYM_CLASS_NULL 0 +#define EFI_IMAGE_SYM_CLASS_AUTOMATIC 1 +#define EFI_IMAGE_SYM_CLASS_EXTERNAL 2 +#define EFI_IMAGE_SYM_CLASS_STATIC 3 +#define EFI_IMAGE_SYM_CLASS_REGISTER 4 +#define EFI_IMAGE_SYM_CLASS_EXTERNAL_DEF 5 +#define EFI_IMAGE_SYM_CLASS_LABEL 6 +#define EFI_IMAGE_SYM_CLASS_UNDEFINED_LABEL 7 +#define EFI_IMAGE_SYM_CLASS_MEMBER_OF_STRUCT 8 +#define EFI_IMAGE_SYM_CLASS_ARGUMENT 9 +#define EFI_IMAGE_SYM_CLASS_STRUCT_TAG 10 +#define EFI_IMAGE_SYM_CLASS_MEMBER_OF_UNION 11 +#define EFI_IMAGE_SYM_CLASS_UNION_TAG 12 +#define EFI_IMAGE_SYM_CLASS_TYPE_DEFINITION 13 +#define EFI_IMAGE_SYM_CLASS_UNDEFINED_STATIC 14 +#define EFI_IMAGE_SYM_CLASS_ENUM_TAG 15 +#define EFI_IMAGE_SYM_CLASS_MEMBER_OF_ENUM 16 +#define EFI_IMAGE_SYM_CLASS_REGISTER_PARAM 17 +#define EFI_IMAGE_SYM_CLASS_BIT_FIELD 18 +#define EFI_IMAGE_SYM_CLASS_BLOCK 100 +#define EFI_IMAGE_SYM_CLASS_FUNCTION 101 +#define EFI_IMAGE_SYM_CLASS_END_OF_STRUCT 102 +#define EFI_IMAGE_SYM_CLASS_FILE 103 +#define EFI_IMAGE_SYM_CLASS_SECTION 104 +#define EFI_IMAGE_SYM_CLASS_WEAK_EXTERNAL 105 + +// +// type packing constants +// +#define EFI_IMAGE_N_BTMASK 017 +#define EFI_IMAGE_N_TMASK 060 +#define EFI_IMAGE_N_TMASK1 0300 +#define EFI_IMAGE_N_TMASK2 0360 +#define EFI_IMAGE_N_BTSHFT 4 +#define EFI_IMAGE_N_TSHIFT 2 + +/// +/// Communal selection types. +/// +#define EFI_IMAGE_COMDAT_SELECT_NODUPLICATES 1 +#define EFI_IMAGE_COMDAT_SELECT_ANY 2 +#define EFI_IMAGE_COMDAT_SELECT_SAME_SIZE 3 +#define EFI_IMAGE_COMDAT_SELECT_EXACT_MATCH 4 +#define EFI_IMAGE_COMDAT_SELECT_ASSOCIATIVE 5 + +/// +/// the following values only be referred in PeCoff, not defined in PECOFF. +/// +#define EFI_IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY 1 +#define EFI_IMAGE_WEAK_EXTERN_SEARCH_LIBRARY 2 +#define EFI_IMAGE_WEAK_EXTERN_SEARCH_ALIAS 3 + +/// +/// Relocation format. +/// +typedef struct { + UINT32 VirtualAddress; + UINT32 SymbolTableIndex; + UINT16 Type; +} EFI_IMAGE_RELOCATION; + +/// +/// Size of EFI_IMAGE_RELOCATION +/// +#define EFI_IMAGE_SIZEOF_RELOCATION 10 + +/// +/// I386 relocation types. +/// +#define EFI_IMAGE_REL_I386_ABSOLUTE 0x0000 ///< Reference is absolute, no relocation is necessary +#define EFI_IMAGE_REL_I386_DIR16 0x0001 ///< Direct 16-bit reference to the symbols virtual address +#define EFI_IMAGE_REL_I386_REL16 0x0002 ///< PC-relative 16-bit reference to the symbols virtual address +#define EFI_IMAGE_REL_I386_DIR32 0x0006 ///< Direct 32-bit reference to the symbols virtual address +#define EFI_IMAGE_REL_I386_DIR32NB 0x0007 ///< Direct 32-bit reference to the symbols virtual address, base not included +#define EFI_IMAGE_REL_I386_SEG12 0x0009 ///< Direct 16-bit reference to the segment-selector bits of a 32-bit virtual address +#define EFI_IMAGE_REL_I386_SECTION 0x000A +#define EFI_IMAGE_REL_I386_SECREL 0x000B +#define EFI_IMAGE_REL_I386_REL32 0x0014 ///< PC-relative 32-bit reference to the symbols virtual address + +/// +/// x64 processor relocation types. +/// +#define IMAGE_REL_AMD64_ABSOLUTE 0x0000 +#define IMAGE_REL_AMD64_ADDR64 0x0001 +#define IMAGE_REL_AMD64_ADDR32 0x0002 +#define IMAGE_REL_AMD64_ADDR32NB 0x0003 +#define IMAGE_REL_AMD64_REL32 0x0004 +#define IMAGE_REL_AMD64_REL32_1 0x0005 +#define IMAGE_REL_AMD64_REL32_2 0x0006 +#define IMAGE_REL_AMD64_REL32_3 0x0007 +#define IMAGE_REL_AMD64_REL32_4 0x0008 +#define IMAGE_REL_AMD64_REL32_5 0x0009 +#define IMAGE_REL_AMD64_SECTION 0x000A +#define IMAGE_REL_AMD64_SECREL 0x000B +#define IMAGE_REL_AMD64_SECREL7 0x000C +#define IMAGE_REL_AMD64_TOKEN 0x000D +#define IMAGE_REL_AMD64_SREL32 0x000E +#define IMAGE_REL_AMD64_PAIR 0x000F +#define IMAGE_REL_AMD64_SSPAN32 0x0010 + +/// +/// Based relocation format. +/// +typedef struct { + UINT32 VirtualAddress; + UINT32 SizeOfBlock; +} EFI_IMAGE_BASE_RELOCATION; + +/// +/// Size of EFI_IMAGE_BASE_RELOCATION +/// +#define EFI_IMAGE_SIZEOF_BASE_RELOCATION 8 + +/// +/// Based relocation types. +/// +#define EFI_IMAGE_REL_BASED_ABSOLUTE 0 +#define EFI_IMAGE_REL_BASED_HIGH 1 +#define EFI_IMAGE_REL_BASED_LOW 2 +#define EFI_IMAGE_REL_BASED_HIGHLOW 3 +#define EFI_IMAGE_REL_BASED_HIGHADJ 4 +#define EFI_IMAGE_REL_BASED_MIPS_JMPADDR 5 +#define EFI_IMAGE_REL_BASED_IA64_IMM64 9 +#define IMAGE_REL_BASED_MIPS_JMPADDR16 9 +#define EFI_IMAGE_REL_BASED_DIR64 10 + +/// +/// Line number format. +/// +typedef struct { + union { + UINT32 SymbolTableIndex; // Symbol table index of function name if Linenumber is 0. + UINT32 VirtualAddress; // Virtual address of line number. + } Type; + UINT16 Linenumber; // Line number. +} EFI_IMAGE_LINENUMBER; + +/// +/// Size of EFI_IMAGE_LINENUMBER +/// +#define EFI_IMAGE_SIZEOF_LINENUMBER 6 + +/// +/// Archive format. +/// +#define EFI_IMAGE_ARCHIVE_START_SIZE 8 +#define EFI_IMAGE_ARCHIVE_START "!\n" +#define EFI_IMAGE_ARCHIVE_END "`\n" +#define EFI_IMAGE_ARCHIVE_PAD "\n" +#define EFI_IMAGE_ARCHIVE_LINKER_MEMBER "/ " +#define EFI_IMAGE_ARCHIVE_LONGNAMES_MEMBER "// " + +typedef struct { + UINT8 Name[16]; ///< File member name - `/' terminated. + UINT8 Date[12]; ///< File member date - decimal. + UINT8 UserID[6]; ///< File member user id - decimal. + UINT8 GroupID[6]; ///< File member group id - decimal. + UINT8 Mode[8]; ///< File member mode - octal. + UINT8 Size[10]; ///< File member size - decimal. + UINT8 EndHeader[2]; ///< String to end header. (0x60 0x0A) +} EFI_IMAGE_ARCHIVE_MEMBER_HEADER; + +/// +/// Size of EFI_IMAGE_ARCHIVE_MEMBER_HEADER +/// +#define EFI_IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR 60 + + +/// +/// DLL Support +/// + +/// +/// Export Directory Table +/// +typedef struct { + UINT32 Characteristics; + UINT32 TimeDateStamp; + UINT16 MajorVersion; + UINT16 MinorVersion; + UINT32 Name; + UINT32 Base; + UINT32 NumberOfFunctions; + UINT32 NumberOfNames; + UINT32 AddressOfFunctions; + UINT32 AddressOfNames; + UINT32 AddressOfNameOrdinals; +} EFI_IMAGE_EXPORT_DIRECTORY; + +/// +/// Hint/Name Table +/// +typedef struct { + UINT16 Hint; + UINT8 Name[1]; +} EFI_IMAGE_IMPORT_BY_NAME; + +typedef struct { + union { + UINT32 Function; + UINT32 Ordinal; + EFI_IMAGE_IMPORT_BY_NAME *AddressOfData; + } u1; +} EFI_IMAGE_THUNK_DATA; + +#define EFI_IMAGE_ORDINAL_FLAG BIT31 ///< Flag for PE32 +#define EFI_IMAGE_SNAP_BY_ORDINAL(Ordinal) ((Ordinal & EFI_IMAGE_ORDINAL_FLAG) != 0) +#define EFI_IMAGE_ORDINAL(Ordinal) (Ordinal & 0xffff) + +/// +/// Import Directory Table +/// +typedef struct { + UINT32 Characteristics; + UINT32 TimeDateStamp; + UINT32 ForwarderChain; + UINT32 Name; + EFI_IMAGE_THUNK_DATA *FirstThunk; +} EFI_IMAGE_IMPORT_DESCRIPTOR; + + +/// +/// Debug Direcotry Format +/// +typedef struct { + UINT32 Characteristics; + UINT32 TimeDateStamp; + UINT16 MajorVersion; + UINT16 MinorVersion; + UINT32 Type; + UINT32 SizeOfData; + UINT32 RVA; ///< The address of the debug data when loaded, relative to the image base + UINT32 FileOffset; ///< The file pointer to the debug data +} EFI_IMAGE_DEBUG_DIRECTORY_ENTRY; + +#define EFI_IMAGE_DEBUG_TYPE_CODEVIEW 2 ///< The Visual C++ debug information + +/// +/// Debug Data Structure defined in Microsoft C++ +/// +#define CODEVIEW_SIGNATURE_NB10 SIGNATURE_32('N', 'B', '1', '0') +typedef struct { + UINT32 Signature; ///< "NB10" + UINT32 Unknown; + UINT32 Unknown2; + UINT32 Unknown3; + // + // Filename of .PDB goes here + // +} EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY; + +/// +/// Debug Data Structure defined in Microsoft C++ +/// +#define CODEVIEW_SIGNATURE_RSDS SIGNATURE_32('R', 'S', 'D', 'S') +typedef struct { + UINT32 Signature; ///< "RSDS" + UINT32 Unknown; + UINT32 Unknown2; + UINT32 Unknown3; + UINT32 Unknown4; + UINT32 Unknown5; + // + // Filename of .PDB goes here + // +} EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY; + +/// +/// Header format for TE images, defined in PI Specification, 1.0 +/// +typedef struct { + UINT16 Signature; ///< signature for TE format = "VZ" + UINT16 Machine; ///< from the original file header + UINT8 NumberOfSections; ///< from the original file header + UINT8 Subsystem; ///< from original optional header + UINT16 StrippedSize; ///< how many bytes we removed from the header + UINT32 AddressOfEntryPoint; ///< offset to entry point -- from original optional header + UINT32 BaseOfCode; ///< from original image -- required for ITP debug + UINT64 ImageBase; ///< from original file header + EFI_IMAGE_DATA_DIRECTORY DataDirectory[2]; ///< only base relocation and debug directory +} EFI_TE_IMAGE_HEADER; + + +#define EFI_TE_IMAGE_HEADER_SIGNATURE SIGNATURE_16('V', 'Z') + +// +// Data directory indexes in our TE image header +// +#define EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC 0 +#define EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG 1 + + +/// +/// Union of PE32, PE32+, and TE headers +/// +typedef union { + EFI_IMAGE_NT_HEADERS32 Pe32; + EFI_IMAGE_NT_HEADERS64 Pe32Plus; + EFI_TE_IMAGE_HEADER Te; +} EFI_IMAGE_OPTIONAL_HEADER_UNION; + +typedef union { + EFI_IMAGE_NT_HEADERS32 *Pe32; + EFI_IMAGE_NT_HEADERS64 *Pe32Plus; + EFI_TE_IMAGE_HEADER *Te; + EFI_IMAGE_OPTIONAL_HEADER_UNION *Union; +} EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION; + +#endif diff --git a/gpxe/src/include/gpxe/efi/LICENCE b/gpxe/src/include/gpxe/efi/LICENCE new file mode 100644 index 0000000..6aa2b2f --- /dev/null +++ b/gpxe/src/include/gpxe/efi/LICENCE @@ -0,0 +1,40 @@ +The EFI headers contained herein are copied from the EFI Development +Kit, available from http://www.tianocore.org and published under the +following licence: + + BSD License from Intel + Copyright (c) 2004, Intel Corporation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + Neither the name of the Intel Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + COPYRIGHT OWNER 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. + + +This licence applies only to files that are part of the EFI +Development Kit. Other files may contain their own licence terms, or +may fall under the standard gPXE GPL licence. diff --git a/gpxe/src/include/gpxe/efi/Pi/PiBootMode.h b/gpxe/src/include/gpxe/efi/Pi/PiBootMode.h new file mode 100644 index 0000000..bebca22 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Pi/PiBootMode.h @@ -0,0 +1,43 @@ +/** @file + Present the boot mode values in PI. + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + PI Version 1.0 + +**/ + +#ifndef __PI_BOOT_MODE_H__ +#define __PI_BOOT_MODE_H__ + +#include + +/// +/// EFI boot mode +/// +typedef UINT32 EFI_BOOT_MODE; + +// +// 0x21 - 0xf..f are reserved. +// +#define BOOT_WITH_FULL_CONFIGURATION 0x00 +#define BOOT_WITH_MINIMAL_CONFIGURATION 0x01 +#define BOOT_ASSUMING_NO_CONFIGURATION_CHANGES 0x02 +#define BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS 0x03 +#define BOOT_WITH_DEFAULT_SETTINGS 0x04 +#define BOOT_ON_S4_RESUME 0x05 +#define BOOT_ON_S5_RESUME 0x06 +#define BOOT_ON_S2_RESUME 0x10 +#define BOOT_ON_S3_RESUME 0x11 +#define BOOT_ON_FLASH_UPDATE 0x12 +#define BOOT_IN_RECOVERY_MODE 0x20 + +#endif diff --git a/gpxe/src/include/gpxe/efi/Pi/PiDependency.h b/gpxe/src/include/gpxe/efi/Pi/PiDependency.h new file mode 100644 index 0000000..73f6027 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Pi/PiDependency.h @@ -0,0 +1,47 @@ +/** @file + Present the dependency expression values in PI. + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + PI Version 1.0 + +**/ +#ifndef __PI_DEPENDENCY_H__ +#define __PI_DEPENDENCY_H__ + +/// +/// If present, this must be the first and only opcode, +/// EFI_DEP_BEFORE is only used by DXE driver. +/// +#define EFI_DEP_BEFORE 0x00 + +/// +/// If present, this must be the first and only opcode, +/// EFI_DEP_AFTER is only used by DXE driver. +/// +#define EFI_DEP_AFTER 0x01 + +#define EFI_DEP_PUSH 0x02 +#define EFI_DEP_AND 0x03 +#define EFI_DEP_OR 0x04 +#define EFI_DEP_NOT 0x05 +#define EFI_DEP_TRUE 0x06 +#define EFI_DEP_FALSE 0x07 +#define EFI_DEP_END 0x08 + + +/// +/// If present, this must be the first opcode, +/// EFI_DEP_SOR is only used by DXE driver. +/// +#define EFI_DEP_SOR 0x09 + +#endif diff --git a/gpxe/src/include/gpxe/efi/Pi/PiDxeCis.h b/gpxe/src/include/gpxe/efi/Pi/PiDxeCis.h new file mode 100644 index 0000000..a924888 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Pi/PiDxeCis.h @@ -0,0 +1,642 @@ +/** @file + Include file matches things in PI. + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + PI Version 1.0 + +**/ + +#ifndef __PI_DXECIS_H__ +#define __PI_DXECIS_H__ + +#include + +/// +/// Global Coherencey Domain types - Memory type +/// +typedef enum { + EfiGcdMemoryTypeNonExistent, + EfiGcdMemoryTypeReserved, + EfiGcdMemoryTypeSystemMemory, + EfiGcdMemoryTypeMemoryMappedIo, + EfiGcdMemoryTypeMaximum +} EFI_GCD_MEMORY_TYPE; + +/// +/// Global Coherencey Domain types - IO type +/// +typedef enum { + EfiGcdIoTypeNonExistent, + EfiGcdIoTypeReserved, + EfiGcdIoTypeIo, + EfiGcdIoTypeMaximum +} EFI_GCD_IO_TYPE; + +/// +/// The type of allocation to perform. +/// +typedef enum { + EfiGcdAllocateAnySearchBottomUp, + EfiGcdAllocateMaxAddressSearchBottomUp, + EfiGcdAllocateAddress, + EfiGcdAllocateAnySearchTopDown, + EfiGcdAllocateMaxAddressSearchTopDown, + EfiGcdMaxAllocateType +} EFI_GCD_ALLOCATE_TYPE; + +/// +/// EFI_GCD_MEMORY_SPACE_DESCRIPTOR +/// +typedef struct { + /// + /// The physical address of the first byte in the memory region. Type + /// EFI_PHYSICAL_ADDRESS is defined in the AllocatePages() function + /// description in the UEFI 2.0 specification + /// + EFI_PHYSICAL_ADDRESS BaseAddress; + + /// + /// The number of bytes in the memory region. + /// + UINT64 Length; + + /// + /// The bit mask of attributes that the memory region is capable of supporting. The bit + /// mask of available attributes is defined in the GetMemoryMap() function description + /// in the UEFI 2.0 specification. + /// + UINT64 Capabilities; + /// + /// The bit mask of attributes that the memory region is currently using. The bit mask of + /// available attributes is defined in GetMemoryMap(). + /// + UINT64 Attributes; + /// + /// Type of the memory region. Type EFI_GCD_MEMORY_TYPE is defined in the + /// AddMemorySpace() function description + /// + EFI_GCD_MEMORY_TYPE GcdMemoryType; + + /// + /// The image handle of the agent that allocated the memory resource described by + /// PhysicalStart and NumberOfBytes. If this field is NULL, then the memory + /// resource is not currently allocated. Type EFI_HANDLE is defined in + /// InstallProtocolInterface() in the UEFI 2.0 specification. + /// + EFI_HANDLE ImageHandle; + + /// + /// The device handle for which the memory resource has been allocated. If + /// ImageHandle is NULL, then the memory resource is not currently allocated. If this + /// field is NULL, then the memory resource is not associated with a device that is + /// described by a device handle. Type EFI_HANDLE is defined in + /// InstallProtocolInterface() in the UEFI 2.0 specification. + /// + EFI_HANDLE DeviceHandle; +} EFI_GCD_MEMORY_SPACE_DESCRIPTOR; + +/// +/// EFI_GCD_IO_SPACE_DESCRIPTOR +/// +typedef struct { + /// + /// Physical address of the first byte in the I/O region. Type + /// EFI_PHYSICAL_ADDRESS is defined in the AllocatePages() function + /// description in the UEFI 2.0 specification. + /// + EFI_PHYSICAL_ADDRESS BaseAddress; + + /// + /// Number of bytes in the I/O region. + /// + UINT64 Length; + + /// + /// Type of the I/O region. Type EFI_GCD_IO_TYPE is defined in the + /// AddIoSpace() function description. + /// + EFI_GCD_IO_TYPE GcdIoType; + + /// + /// The image handle of the agent that allocated the I/O resource described by + /// PhysicalStart and NumberOfBytes. If this field is NULL, then the I/O + /// resource is not currently allocated. Type EFI_HANDLE is defined in + /// InstallProtocolInterface() in the UEFI 2.0 specification. + /// + EFI_HANDLE ImageHandle; + + /// + /// The device handle for which the I/O resource has been allocated. If ImageHandle + /// is NULL, then the I/O resource is not currently allocated. If this field is NULL, then + /// the I/O resource is not associated with a device that is described by a device handle. + /// Type EFI_HANDLE is defined in InstallProtocolInterface() in the UEFI + /// 2.0 specification. + /// + EFI_HANDLE DeviceHandle; +} EFI_GCD_IO_SPACE_DESCRIPTOR; + + +/** + Adds reserved memory, system memory, or memory-mapped I/O resources to the + global coherency domain of the processor. + + @param GcdMemoryType The type of memory resource being added. + @param BaseAddress The physical address that is the start address + of the memory resource being added. + @param Length The size, in bytes, of the memory resource that + is being added. + @param Capabilities The bit mask of attributes that the memory + resource region supports. + + @retval EFI_SUCCESS The memory resource was added to the global + coherency domain of the processor. + @retval EFI_INVALID_PARAMETER GcdMemoryType is invalid. + @retval EFI_INVALID_PARAMETER Length is zero. + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to add + the memory resource to the global coherency + domain of the processor. + @retval EFI_UNSUPPORTED The processor does not support one or more bytes + of the memory resource range specified by + BaseAddress and Length. + @retval EFI_ACCESS_DENIED One or more bytes of the memory resource range + specified by BaseAddress and Length conflicts + with a memory resource range that was previously + added to the global coherency domain of the processor. + @retval EFI_ACCESS_DENIED One or more bytes of the memory resource range + specified by BaseAddress and Length was allocated + in a prior call to AllocateMemorySpace().. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ADD_MEMORY_SPACE)( + IN EFI_GCD_MEMORY_TYPE GcdMemoryType, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Capabilities + ); + +/** + Allocates nonexistent memory, reserved memory, system memory, or memorymapped + I/O resources from the global coherency domain of the processor. + + @param GcdAllocateType The type of allocation to perform. + @param GcdMemoryType The type of memory resource being allocated. + @param Alignment The log base 2 of the boundary that BaseAddress must + be aligned on output. Align with 2^Alignment. + @param Length The size in bytes of the memory resource range that + is being allocated. + @param BaseAddress A pointer to a physical address to allocate. + @param Imagehandle The image handle of the agent that is allocating + the memory resource. + @param DeviceHandle The device handle for which the memory resource + is being allocated. + + @retval EFI_INVALID_PARAMETER GcdAllocateType is invalid. + @retval EFI_INVALID_PARAMETER GcdMemoryType is invalid. + @retval EFI_INVALID_PARAMETER Length is zero. + @retval EFI_INVALID_PARAMETER BaseAddress is NULL. + @retval EFI_INVALID_PARAMETER ImageHandle is NULL. + @retval EFI_NOT_FOUND The memory resource request could not be satisfied. + No descriptor contains the desired space. + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to allocate the memory + resource from the global coherency domain of the processor. + @retval EFI_SUCCESS The memory resource was allocated from the global coherency + domain of the processor. + + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ALLOCATE_MEMORY_SPACE)( + IN EFI_GCD_ALLOCATE_TYPE GcdAllocateType, + IN EFI_GCD_MEMORY_TYPE GcdMemoryType, + IN UINTN Alignment, + IN UINT64 Length, + IN OUT EFI_PHYSICAL_ADDRESS *BaseAddress, + IN EFI_HANDLE ImageHandle, + IN EFI_HANDLE DeviceHandle OPTIONAL + ); + +/** + Frees nonexistent memory, reserved memory, system memory, or memory-mapped + I/O resources from the global coherency domain of the processor. + + @param BaseAddress The physical address that is the start address of the memory resource being freed. + @param Length The size in bytes of the memory resource range that is being freed. + + @retval EFI_SUCCESS The memory resource was freed from the global coherency domain of + the processor. + @retval EFI_INVALID_PARAMETER Length is zero. + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory + resource range specified by BaseAddress and Length. + @retval EFI_NOT_FOUND The memory resource range specified by BaseAddress and + Length was not allocated with previous calls to AllocateMemorySpace(). + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to free the memory resource + from the global coherency domain of the processor. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_FREE_MEMORY_SPACE)( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ); + +/** + Removes reserved memory, system memory, or memory-mapped I/O resources from + the global coherency domain of the processor. + + @param BaseAddress The physical address that is the start address of the memory resource being removed. + @param Length The size in bytes of the memory resource that is being removed. + + @retval EFI_SUCCESS The memory resource was removed from the global coherency + domain of the processor. + @retval EFI_INVALID_PARAMETER Length is zero. + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory + resource range specified by BaseAddress and Length. + @retval EFI_NOT_FOUND One or more bytes of the memory resource range specified by + BaseAddress and Length was not added with previous calls to + AddMemorySpace(). + @retval EFI_ACCESS_DEFINED One or more bytes of the memory resource range specified by + BaseAddress and Length has been allocated with AllocateMemorySpace(). + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to remove the memory + resource from the global coherency domain of the processor. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_REMOVE_MEMORY_SPACE)( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ); + +/** + Retrieves the descriptor for a memory region containing a specified address. + + @param BaseAddress The physical address that is the start address of a memory region. + @param Descriptor A pointer to a caller allocated descriptor. + + @retval EFI_SUCCESS The descriptor for the memory resource region containing + BaseAddress was returned in Descriptor. + @retval EFI_INVALID_PARAMETER Descriptor is NULL. + @retval EFI_NOT_FOUND A memory resource range containing BaseAddress was not found. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_GET_MEMORY_SPACE_DESCRIPTOR)( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + OUT EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Descriptor + ); + +/** + Modifies the attributes for a memory region in the global coherency domain of the + processor. + + @param BaseAddress The physical address that is the start address of a memory region. + @param Length The size in bytes of the memory region. + @param Attributes The bit mask of attributes to set for the memory region. + + @retval EFI_SUCCESS The attributes were set for the memory region. + @retval EFI_INVALID_PARAMETER Length is zero. + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory + resource range specified by BaseAddress and Length. + @retval EFI_UNSUPPORTED The bit mask of attributes is not support for the memory resource + range specified by BaseAddress and Length. + @retval EFI_ACCESS_DEFINED The attributes for the memory resource range specified by + BaseAddress and Length cannot be modified. + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of + the memory resource range. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SET_MEMORY_SPACE_ATTRIBUTES)( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ); + +/** + Returns a map of the memory resources in the global coherency domain of the + processor. + + @param NumberOfDescriptors A pointer to number of descriptors returned in the MemorySpaceMap buffer. + @param MemorySpaceMap A pointer to the array of EFI_GCD_MEMORY_SPACE_DESCRIPTORs. + + @retval EFI_SUCCESS The memory space map was returned in the MemorySpaceMap + buffer, and the number of descriptors in MemorySpaceMap was + returned in NumberOfDescriptors. + @retval EFI_INVALID_PARAMETER NumberOfDescriptors is NULL. + @retval EFI_INVALID_PARAMETER MemorySpaceMap is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate MemorySpaceMap. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_GET_MEMORY_SPACE_MAP)( + OUT UINTN *NumberOfDescriptors, + OUT EFI_GCD_MEMORY_SPACE_DESCRIPTOR **MemorySpaceMap + ); + +/** + Adds reserved I/O or I/O resources to the global coherency domain of the processor. + + @param GcdIoType The type of I/O resource being added. + @param BaseAddress The physical address that is the start address of the I/O resource being added. + @param Length The size in bytes of the I/O resource that is being added. + + @retval EFI_SUCCESS The I/O resource was added to the global coherency domain of + the processor. + @retval EFI_INVALID_PARAMETER GcdIoType is invalid. + @retval EFI_INVALID_PARAMETER Length is zero. + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to add the I/O resource to + the global coherency domain of the processor. + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the I/O + resource range specified by BaseAddress and Length. + @retval EFI_ACCESS_DENIED One or more bytes of the I/O resource range specified by + BaseAddress and Length conflicts with an I/O resource + range that was previously added to the global coherency domain + of the processor. + @retval EFI_ACCESS_DENIED One or more bytes of the I/O resource range specified by + BaseAddress and Length was allocated in a prior call to + AllocateIoSpace(). + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ADD_IO_SPACE)( + IN EFI_GCD_IO_TYPE GcdIoType, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ); + +/** + Allocates nonexistent I/O, reserved I/O, or I/O resources from the global coherency + domain of the processor. + + @param GcdAllocateType The type of allocation to perform. + @param GcdIoType The type of I/O resource being allocated. + @param Alignment The log base 2 of the boundary that BaseAddress must be aligned on output. + @param Length The size in bytes of the I/O resource range that is being allocated. + @param BaseAddress A pointer to a physical address. + @param Imagehandle The image handle of the agent that is allocating the I/O resource. + @param DeviceHandle The device handle for which the I/O resource is being allocated. + + @retval EFI_SUCCESS The I/O resource was allocated from the global coherency domain + of the processor. + @retval EFI_INVALID_PARAMETER GcdAllocateType is invalid. + @retval EFI_INVALID_PARAMETER GcdIoType is invalid. + @retval EFI_INVALID_PARAMETER Length is zero. + @retval EFI_INVALID_PARAMETER BaseAddress is NULL. + @retval EFI_INVALID_PARAMETER ImageHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to allocate the I/O + resource from the global coherency domain of the processor. + @retval EFI_NOT_FOUND The I/O resource request could not be satisfied. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ALLOCATE_IO_SPACE)( + IN EFI_GCD_ALLOCATE_TYPE GcdAllocateType, + IN EFI_GCD_IO_TYPE GcdIoType, + IN UINTN Alignment, + IN UINT64 Length, + IN OUT EFI_PHYSICAL_ADDRESS *BaseAddress, + IN EFI_HANDLE ImageHandle, + IN EFI_HANDLE DeviceHandle OPTIONAL + ); + +/** + Frees nonexistent I/O, reserved I/O, or I/O resources from the global coherency + domain of the processor. + + @param BaseAddress The physical address that is the start address of the I/O resource being freed. + @param Length The size in bytes of the I/O resource range that is being freed. + + @retval EFI_SUCCESS The I/O resource was freed from the global coherency domain of the + processor. + @retval EFI_INVALID_PARAMETER Length is zero. + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the I/O resource + range specified by BaseAddress and Length. + @retval EFI_NOT_FOUND The I/O resource range specified by BaseAddress and Length + was not allocated with previous calls to AllocateIoSpace(). + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to free the I/O resource from + the global coherency domain of the processor. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_FREE_IO_SPACE)( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ); + +/** + Removes reserved I/O or I/O resources from the global coherency domain of the + processor. + + @param BaseAddress A pointer to a physical address that is the start address of the I/O resource being + removed. + @param Length The size in bytes of the I/O resource that is being removed. + + @retval EFI_SUCCESS The I/O resource was removed from the global coherency domain + of the processor. + @retval EFI_INVALID_PARAMETER Length is zero. + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the I/O + resource range specified by BaseAddress and Length. + @retval EFI_NOT_FOUND One or more bytes of the I/O resource range specified by + BaseAddress and Length was not added with previous + calls to AddIoSpace(). + @retval EFI_ACCESS_DENIED One or more bytes of the I/O resource range specified by + BaseAddress and Length has been allocated with + AllocateIoSpace(). + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to remove the I/O + resource from the global coherency domain of the processor. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_REMOVE_IO_SPACE)( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ); + +/** + Retrieves the descriptor for an I/O region containing a specified address. + + @param BaseAddress The physical address that is the start address of an I/O region. + @param Descriptor A pointer to a caller allocated descriptor. + + @retval EFI_SUCCESS The descriptor for the I/O resource region containing + BaseAddress was returned in Descriptor. + @retval EFI_INVALID_PARAMETER Descriptor is NULL. + @retval EFI_NOT_FOUND An I/O resource range containing BaseAddress was not found. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_GET_IO_SPACE_DESCRIPTOR)( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + OUT EFI_GCD_IO_SPACE_DESCRIPTOR *Descriptor + ); + +/** + Returns a map of the I/O resources in the global coherency domain of the processor. + + @param NumberOfDescriptors A pointer to number of descriptors returned in the IoSpaceMap buffer. + @param MemorySpaceMap A pointer to the array of EFI_GCD_IO_SPACE_DESCRIPTORs. + + @retval EFI_SUCCESS The I/O space map was returned in the IoSpaceMap buffer, and + the number of descriptors in IoSpaceMap was returned in + NumberOfDescriptors. + @retval EFI_INVALID_PARAMETER NumberOfDescriptors is NULL. + @retval EFI_INVALID_PARAMETER IoSpaceMap is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate IoSpaceMap. + + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_GET_IO_SPACE_MAP)( + OUT UINTN *NumberOfDescriptors, + OUT EFI_GCD_IO_SPACE_DESCRIPTOR **IoSpaceMap + ); + + + +/** + Loads and executed DXE drivers from firmware volumes. + + The Dispatch() function searches for DXE drivers in firmware volumes that have been + installed since the last time the Dispatch() service was called. It then evaluates + the dependency expressions of all the DXE drivers and loads and executes those DXE + drivers whose dependency expression evaluate to TRUE. This service must interact with + the Security Architectural Protocol to authenticate DXE drivers before they are executed. + This process is continued until no more DXE drivers can be executed. + + @retval EFI_SUCCESS One or more DXE driver were dispatched. + @retval EFI_NOT_FOUND No DXE drivers were dispatched. + @retval EFI_ALREADY_STARTED An attempt is being made to start the DXE Dispatcher recursively. + Thus no action was taken. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_DISPATCH)( + VOID + ); + +/** + Clears the Schedule on Request (SOR) flag for a component that is stored in a firmware volume. + + @param FirmwareVolumeHandle The handle of the firmware volume that contains the file specified by FileName. + @param FileName A pointer to the name of the file in a firmware volume. + + @retval EFI_SUCCESS The DXE driver was found and its SOR bit was cleared. + @retval EFI_NOT_FOUND The DXE driver does not exist, or the DXE driver exists and its SOR + bit is not set. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SCHEDULE)( + IN EFI_HANDLE FirmwareVolumeHandle, + IN CONST EFI_GUID *FileName + ); + +/** + Promotes a file stored in a firmware volume from the untrusted to the trusted state. + + @param FirmwareVolumeHandle The handle of the firmware volume that contains the file specified by FileName. + @param DriverName A pointer to the name of the file in a firmware volume. + + @return Status of promoting FFS from untrusted to trusted + state. + @retval EFI_NOT_FOUND The file was not found in the untrusted state. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TRUST)( + IN EFI_HANDLE FirmwareVolumeHandle, + IN CONST EFI_GUID *FileName + ); + +/** + Creates a firmware volume handle for a firmware volume that is present in system memory. + + @param FirmwareVolumeHeader A pointer to the header of the firmware volume. + @param Size The size, in bytes, of the firmware volume. + @param FirmwareVolumeHandle On output, a pointer to the created handle. + + @retval EFI_SUCCESS The EFI_FIRMWARE_VOLUME_PROTOCOL and + EFI_DEVICE_PATH_PROTOCOL were installed onto + FirmwareVolumeHandle for the firmware volume described + by FirmwareVolumeHeader and Size. + @retval EFI_VOLUME_CORRUPTED The firmware volume described by FirmwareVolumeHeader + and Size is corrupted. + @retval EFI_OUT_OF_RESOURCES There are not enough system resources available to produce the + EFI_FIRMWARE_VOLUME_PROTOCOL and EFI_DEVICE_PATH_PROTOCOL + for the firmware volume described by FirmwareVolumeHeader and Size. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PROCESS_FIRMWARE_VOLUME)( + IN CONST VOID *FirmwareVolumeHeader, + IN UINTN Size, + OUT EFI_HANDLE *FirmwareVolumeHandle + ); + +// +// DXE Services Table +// +#define DXE_SERVICES_SIGNATURE 0x565245535f455844ULL +#define DXE_SERVICES_REVISION ((1<<16) | (00)) + +typedef struct { + EFI_TABLE_HEADER Hdr; + + // + // Global Coherency Domain Services + // + EFI_ADD_MEMORY_SPACE AddMemorySpace; + EFI_ALLOCATE_MEMORY_SPACE AllocateMemorySpace; + EFI_FREE_MEMORY_SPACE FreeMemorySpace; + EFI_REMOVE_MEMORY_SPACE RemoveMemorySpace; + EFI_GET_MEMORY_SPACE_DESCRIPTOR GetMemorySpaceDescriptor; + EFI_SET_MEMORY_SPACE_ATTRIBUTES SetMemorySpaceAttributes; + EFI_GET_MEMORY_SPACE_MAP GetMemorySpaceMap; + EFI_ADD_IO_SPACE AddIoSpace; + EFI_ALLOCATE_IO_SPACE AllocateIoSpace; + EFI_FREE_IO_SPACE FreeIoSpace; + EFI_REMOVE_IO_SPACE RemoveIoSpace; + EFI_GET_IO_SPACE_DESCRIPTOR GetIoSpaceDescriptor; + EFI_GET_IO_SPACE_MAP GetIoSpaceMap; + + // + // Dispatcher Services + // + EFI_DISPATCH Dispatch; + EFI_SCHEDULE Schedule; + EFI_TRUST Trust; + // + // Service to process a single firmware volume found in a capsule + // + EFI_PROCESS_FIRMWARE_VOLUME ProcessFirmwareVolume; +} DXE_SERVICES; + +typedef DXE_SERVICES EFI_DXE_SERVICES; + +#endif diff --git a/gpxe/src/include/gpxe/efi/Pi/PiFirmwareFile.h b/gpxe/src/include/gpxe/efi/Pi/PiFirmwareFile.h new file mode 100644 index 0000000..be6f8bd --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Pi/PiFirmwareFile.h @@ -0,0 +1,242 @@ +/** @file + The firmware file related definitions in PI. + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + PI Version 1.0 + +**/ + + +#ifndef __PI_FIRMWARE_FILE_H__ +#define __PI_FIRMWARE_FILE_H__ + +#include + +#pragma pack(1) +/// +/// Used to verify the integrity of the file. +/// +typedef union { + struct { + UINT8 Header; + UINT8 File; + } Checksum; + UINT16 Checksum16; +} EFI_FFS_INTEGRITY_CHECK; + +typedef UINT8 EFI_FV_FILETYPE; +typedef UINT8 EFI_FFS_FILE_ATTRIBUTES; +typedef UINT8 EFI_FFS_FILE_STATE; + +/// +/// File Types Definitions +/// +#define EFI_FV_FILETYPE_ALL 0x00 +#define EFI_FV_FILETYPE_RAW 0x01 +#define EFI_FV_FILETYPE_FREEFORM 0x02 +#define EFI_FV_FILETYPE_SECURITY_CORE 0x03 +#define EFI_FV_FILETYPE_PEI_CORE 0x04 +#define EFI_FV_FILETYPE_DXE_CORE 0x05 +#define EFI_FV_FILETYPE_PEIM 0x06 +#define EFI_FV_FILETYPE_DRIVER 0x07 +#define EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER 0x08 +#define EFI_FV_FILETYPE_APPLICATION 0x09 +#define EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE 0x0B +#define EFI_FV_FILETYPE_OEM_MIN 0xc0 +#define EFI_FV_FILETYPE_OEM_MAX 0xdf +#define EFI_FV_FILETYPE_DEBUG_MIN 0xe0 +#define EFI_FV_FILETYPE_DEBUG_MAX 0xef +#define EFI_FV_FILETYPE_FFS_MIN 0xf0 +#define EFI_FV_FILETYPE_FFS_MAX 0xff +#define EFI_FV_FILETYPE_FFS_PAD 0xf0 +/// +/// FFS File Attributes. +/// +#define FFS_ATTRIB_FIXED 0x04 +#define FFS_ATTRIB_DATA_ALIGNMENT 0x38 +#define FFS_ATTRIB_CHECKSUM 0x40 + +/// +/// FFS File State Bits. +/// +#define EFI_FILE_HEADER_CONSTRUCTION 0x01 +#define EFI_FILE_HEADER_VALID 0x02 +#define EFI_FILE_DATA_VALID 0x04 +#define EFI_FILE_MARKED_FOR_UPDATE 0x08 +#define EFI_FILE_DELETED 0x10 +#define EFI_FILE_HEADER_INVALID 0x20 + + +/// +/// Each file begins with the header that describe the +/// contents and state of the files. +/// +typedef struct { + EFI_GUID Name; + EFI_FFS_INTEGRITY_CHECK IntegrityCheck; + EFI_FV_FILETYPE Type; + EFI_FFS_FILE_ATTRIBUTES Attributes; + UINT8 Size[3]; + EFI_FFS_FILE_STATE State; +} EFI_FFS_FILE_HEADER; + + +typedef UINT8 EFI_SECTION_TYPE; + +/// +/// Pseudo type. It is +/// used as a wild card when retrieving sections. The section +/// type EFI_SECTION_ALL matches all section types. +/// +#define EFI_SECTION_ALL 0x00 + +/// +/// Encapsulation section Type values +/// +#define EFI_SECTION_COMPRESSION 0x01 + +#define EFI_SECTION_GUID_DEFINED 0x02 + +/// +/// Leaf section Type values +/// +#define EFI_SECTION_PE32 0x10 +#define EFI_SECTION_PIC 0x11 +#define EFI_SECTION_TE 0x12 +#define EFI_SECTION_DXE_DEPEX 0x13 +#define EFI_SECTION_VERSION 0x14 +#define EFI_SECTION_USER_INTERFACE 0x15 +#define EFI_SECTION_COMPATIBILITY16 0x16 +#define EFI_SECTION_FIRMWARE_VOLUME_IMAGE 0x17 +#define EFI_SECTION_FREEFORM_SUBTYPE_GUID 0x18 +#define EFI_SECTION_RAW 0x19 +#define EFI_SECTION_PEI_DEPEX 0x1B + +/// +/// Common section header +/// +typedef struct { + UINT8 Size[3]; + EFI_SECTION_TYPE Type; +} EFI_COMMON_SECTION_HEADER; + +/// +/// Leaf section type that contains an +/// IA-32 16-bit executable image. +/// +typedef EFI_COMMON_SECTION_HEADER EFI_COMPATIBILITY16_SECTION; + +/// +/// CompressionType of EFI_COMPRESSION_SECTION. +/// +#define EFI_NOT_COMPRESSED 0x00 +#define EFI_STANDARD_COMPRESSION 0x01 +/// +/// An encapsulation section type in which the +/// section data is compressed. +/// +typedef struct { + EFI_COMMON_SECTION_HEADER CommonHeader; + UINT32 UncompressedLength; + UINT8 CompressionType; +} EFI_COMPRESSION_SECTION; + +/// +/// Leaf section which could be used to determine the dispatch order of DXEs. +/// +typedef EFI_COMMON_SECTION_HEADER EFI_DXE_DEPEX_SECTION; + +/// +/// Leaf section which contains a PI FV. +/// +typedef EFI_COMMON_SECTION_HEADER EFI_FIRMWARE_VOLUME_IMAGE_SECTION; + +/// +/// Leaf section which contains a single GUID. +/// +typedef struct { + EFI_COMMON_SECTION_HEADER CommonHeader; + EFI_GUID SubTypeGuid; +} EFI_FREEFORM_SUBTYPE_GUID_SECTION; + +/// +/// Attributes of EFI_GUID_DEFINED_SECTION +/// +#define EFI_GUIDED_SECTION_PROCESSING_REQUIRED 0x01 +#define EFI_GUIDED_SECTION_AUTH_STATUS_VALID 0x02 +/// +/// Leaf section which is encapsulation defined by specific GUID +/// +typedef struct { + EFI_COMMON_SECTION_HEADER CommonHeader; + EFI_GUID SectionDefinitionGuid; + UINT16 DataOffset; + UINT16 Attributes; +} EFI_GUID_DEFINED_SECTION; + +/// +/// Leaf section which contains PE32+ image. +/// +typedef EFI_COMMON_SECTION_HEADER EFI_PE32_SECTION; + + +/// +/// Leaf section which used to determine the dispatch order of PEIMs. +/// +typedef EFI_COMMON_SECTION_HEADER EFI_PEI_DEPEX_SECTION; + +/// +/// Leaf section which constains the position-independent-code image. +/// +typedef EFI_COMMON_SECTION_HEADER EFI_TE_SECTION; + +/// +/// Leaf section which contains an array of zero or more bytes. +/// +typedef EFI_COMMON_SECTION_HEADER EFI_RAW_SECTION; + +/// +/// Leaf section which contains a unicode string that +/// is human readable file name. +/// +typedef struct { + EFI_COMMON_SECTION_HEADER CommonHeader; + + /// + /// Array of unicode string. + /// + CHAR16 FileNameString[1]; +} EFI_USER_INTERFACE_SECTION; + + +/// +/// Leaf section which contains a numeric build number and +/// an optional unicode string that represents the file revision. +/// +typedef struct { + EFI_COMMON_SECTION_HEADER CommonHeader; + UINT16 BuildNumber; + + /// + /// Array of unicode string. + /// + CHAR16 VersionString[1]; +} EFI_VERSION_SECTION; + + +#define SECTION_SIZE(SectionHeaderPtr) \ + ((UINT32) (*((UINT32 *) ((EFI_COMMON_SECTION_HEADER *) SectionHeaderPtr)->Size) & 0x00ffffff)) + +#pragma pack() + +#endif + diff --git a/gpxe/src/include/gpxe/efi/Pi/PiFirmwareVolume.h b/gpxe/src/include/gpxe/efi/Pi/PiFirmwareVolume.h new file mode 100644 index 0000000..277b704 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Pi/PiFirmwareVolume.h @@ -0,0 +1,154 @@ +/** @file + The firmware volume related definitions in PI. + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + PI Version 1.0 + +**/ + +#ifndef __PI_FIRMWAREVOLUME_H__ +#define __PI_FIRMWAREVOLUME_H__ + +#include + +/// +/// EFI_FV_FILE_ATTRIBUTES +/// +typedef UINT32 EFI_FV_FILE_ATTRIBUTES; + +// +// Value of EFI_FV_FILE_ATTRIBUTES. +// +#define EFI_FV_FILE_ATTRIB_ALIGNMENT 0x0000001F +#define EFI_FV_FILE_ATTRIB_FIXED 0x00000100 +#define EFI_FV_FILE_ATTRIB_MEMORY_MAPPED 0x00000200 + +/// +/// type of EFI FVB attribute +/// +typedef UINT32 EFI_FVB_ATTRIBUTES_2; + +// +// Attributes bit definitions +// +#define EFI_FVB2_READ_DISABLED_CAP 0x00000001 +#define EFI_FVB2_READ_ENABLED_CAP 0x00000002 +#define EFI_FVB2_READ_STATUS 0x00000004 +#define EFI_FVB2_WRITE_DISABLED_CAP 0x00000008 +#define EFI_FVB2_WRITE_ENABLED_CAP 0x00000010 +#define EFI_FVB2_WRITE_STATUS 0x00000020 +#define EFI_FVB2_LOCK_CAP 0x00000040 +#define EFI_FVB2_LOCK_STATUS 0x00000080 +#define EFI_FVB2_STICKY_WRITE 0x00000200 +#define EFI_FVB2_MEMORY_MAPPED 0x00000400 +#define EFI_FVB2_ERASE_POLARITY 0x00000800 +#define EFI_FVB2_READ_LOCK_CAP 0x00001000 +#define EFI_FVB2_READ_LOCK_STATUS 0x00002000 +#define EFI_FVB2_WRITE_LOCK_CAP 0x00004000 +#define EFI_FVB2_WRITE_LOCK_STATUS 0x00008000 +#define EFI_FVB2_ALIGNMENT 0x001F0000 +#define EFI_FVB2_ALIGNMENT_1 0x00000000 +#define EFI_FVB2_ALIGNMENT_2 0x00010000 +#define EFI_FVB2_ALIGNMENT_4 0x00020000 +#define EFI_FVB2_ALIGNMENT_8 0x00030000 +#define EFI_FVB2_ALIGNMENT_16 0x00040000 +#define EFI_FVB2_ALIGNMENT_32 0x00050000 +#define EFI_FVB2_ALIGNMENT_64 0x00060000 +#define EFI_FVB2_ALIGNMENT_128 0x00070000 +#define EFI_FVB2_ALIGNMENT_256 0x00080000 +#define EFI_FVB2_ALIGNMENT_512 0x00090000 +#define EFI_FVB2_ALIGNMENT_1K 0x000A0000 +#define EFI_FVB2_ALIGNMENT_2K 0x000B0000 +#define EFI_FVB2_ALIGNMENT_4K 0x000C0000 +#define EFI_FVB2_ALIGNMENT_8K 0x000D0000 +#define EFI_FVB2_ALIGNMENT_16K 0x000E0000 +#define EFI_FVB2_ALIGNMENT_32K 0x000F0000 +#define EFI_FVB2_ALIGNMENT_64K 0x00100000 +#define EFI_FVB2_ALIGNMENT_128K 0x00110000 +#define EFI_FVB2_ALIGNMENT_256K 0x00120000 +#define EFI_FVB2_ALIGNMNET_512K 0x00130000 +#define EFI_FVB2_ALIGNMENT_1M 0x00140000 +#define EFI_FVB2_ALIGNMENT_2M 0x00150000 +#define EFI_FVB2_ALIGNMENT_4M 0x00160000 +#define EFI_FVB2_ALIGNMENT_8M 0x00170000 +#define EFI_FVB2_ALIGNMENT_16M 0x00180000 +#define EFI_FVB2_ALIGNMENT_32M 0x00190000 +#define EFI_FVB2_ALIGNMENT_64M 0x001A0000 +#define EFI_FVB2_ALIGNMENT_128M 0x001B0000 +#define EFI_FVB2_ALIGNMENT_256M 0x001C0000 +#define EFI_FVB2_ALIGNMENT_512M 0x001D0000 +#define EFI_FVB2_ALIGNMENT_1G 0x001E0000 +#define EFI_FVB2_ALIGNMENT_2G 0x001F0000 + + +typedef struct { + UINT32 NumBlocks; + UINT32 Length; +} EFI_FV_BLOCK_MAP_ENTRY; + +/// +/// Describes the features and layout of the firmware volume. +/// +typedef struct { + UINT8 ZeroVector[16]; + EFI_GUID FileSystemGuid; + UINT64 FvLength; + UINT32 Signature; + EFI_FVB_ATTRIBUTES_2 Attributes; + UINT16 HeaderLength; + UINT16 Checksum; + UINT16 ExtHeaderOffset; + UINT8 Reserved[1]; + UINT8 Revision; + EFI_FV_BLOCK_MAP_ENTRY BlockMap[1]; +} EFI_FIRMWARE_VOLUME_HEADER; + +#define EFI_FVH_SIGNATURE EFI_SIGNATURE_32 ('_', 'F', 'V', 'H') + +/// +/// Firmware Volume Header Revision definition +/// +#define EFI_FVH_REVISION 0x02 + +/// +/// Extension header pointed by ExtHeaderOffset of volume header. +/// +typedef struct { + EFI_GUID FvName; + UINT32 ExtHeaderSize; +} EFI_FIRMWARE_VOLUME_EXT_HEADER; + +/// +/// Entry struture for describing FV extension header +/// +typedef struct { + UINT16 ExtEntrySize; + UINT16 ExtEntryType; +} EFI_FIRMWARE_VOLUME_EXT_ENTRY; + +#define EFI_FV_EXT_TYPE_OEM_TYPE 0x01 +/// +/// This extension header provides a mapping between a GUID and an OEM file type. +/// +typedef struct { + EFI_FIRMWARE_VOLUME_EXT_ENTRY Hdr; + UINT32 TypeMask; + + // + // Array of GUIDs. + // Each GUID represents an OEM file type. + // + EFI_GUID Types[1]; +} EFI_FIRMWARE_VOLUME_EXT_ENTRY_OEM_TYPE; + + +#endif diff --git a/gpxe/src/include/gpxe/efi/Pi/PiHob.h b/gpxe/src/include/gpxe/efi/Pi/PiHob.h new file mode 100644 index 0000000..0c8df1f --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Pi/PiHob.h @@ -0,0 +1,295 @@ +/** @file + HOB related definitions in PI. + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + PI Version 1.0 + +**/ + +#ifndef __PI_HOB_H__ +#define __PI_HOB_H__ + +#include +#include +#include +#include + +// +// HobType of EFI_HOB_GENERIC_HEADER. +// +#define EFI_HOB_TYPE_HANDOFF 0x0001 +#define EFI_HOB_TYPE_MEMORY_ALLOCATION 0x0002 +#define EFI_HOB_TYPE_RESOURCE_DESCRIPTOR 0x0003 +#define EFI_HOB_TYPE_GUID_EXTENSION 0x0004 +#define EFI_HOB_TYPE_FV 0x0005 +#define EFI_HOB_TYPE_CPU 0x0006 +#define EFI_HOB_TYPE_MEMORY_POOL 0x0007 +#define EFI_HOB_TYPE_FV2 0x0009 +#define EFI_HOB_TYPE_LOAD_PEIM 0x000A +#define EFI_HOB_TYPE_UNUSED 0xFFFE +#define EFI_HOB_TYPE_END_OF_HOB_LIST 0xFFFF + +/// +/// Describes the format and size of the data inside the HOB. +/// All HOBs must contain this generic HOB header. +/// +typedef struct { + UINT16 HobType; + UINT16 HobLength; + UINT32 Reserved; +} EFI_HOB_GENERIC_HEADER; + + +/// +/// Value of version ofinEFI_HOB_HANDOFF_INFO_TABLE. +/// +#define EFI_HOB_HANDOFF_TABLE_VERSION 0x0009 + +/// +/// Contains general state information used by the HOB producer phase. +/// This HOB must be the first one in the HOB list. +/// +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + UINT32 Version; + EFI_BOOT_MODE BootMode; + EFI_PHYSICAL_ADDRESS EfiMemoryTop; + EFI_PHYSICAL_ADDRESS EfiMemoryBottom; + EFI_PHYSICAL_ADDRESS EfiFreeMemoryTop; + EFI_PHYSICAL_ADDRESS EfiFreeMemoryBottom; + EFI_PHYSICAL_ADDRESS EfiEndOfHobList; +} EFI_HOB_HANDOFF_INFO_TABLE; + +/// +/// EFI_HOB_MEMORY_ALLOCATION_HEADER describes the +/// various attributes of the logical memory allocation. The type field will be used for +/// subsequent inclusion in the UEFI memory map. +/// +typedef struct { + /// + /// A GUID that defines the memory allocation region's type and purpose, as well as + /// other fields within the memory allocation HOB. This GUID is used to define the + /// additional data within the HOB that may be present for the memory allocation HOB. + /// Type EFI_GUID is defined in InstallProtocolInterface() in the UEFI 2.0 + /// specification. + /// + EFI_GUID Name; + + /// + /// The base address of memory allocated by this HOB. Type + /// EFI_PHYSICAL_ADDRESS is defined in AllocatePages() in the UEFI 2.0 + /// specification. + /// + EFI_PHYSICAL_ADDRESS MemoryBaseAddress; + + /// + /// The length in bytes of memory allocated by this HOB. + /// + UINT64 MemoryLength; + + /// + /// Defines the type of memory allocated by this HOB. The memory type definition + /// follows the EFI_MEMORY_TYPE definition. Type EFI_MEMORY_TYPE is defined + /// in AllocatePages() in the UEFI 2.0 specification. + /// + EFI_MEMORY_TYPE MemoryType; + + /// + /// Padding for Itanium processor family + /// + UINT8 Reserved[4]; +} EFI_HOB_MEMORY_ALLOCATION_HEADER; + +/// +/// Describes all memory ranges used during the HOB producer +/// phase that exist outside the HOB list. This HOB type +/// describes how memory is used, +/// not the physical attributes of memory. +/// +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + EFI_HOB_MEMORY_ALLOCATION_HEADER AllocDescriptor; + // + // Additional data pertaining to the "Name" Guid memory + // may go here. + // +} EFI_HOB_MEMORY_ALLOCATION; + + +/// +/// Describes the memory stack that is produced by the HOB producer +/// phase and upon which all postmemory-installed executable +/// content in the HOB producer phase is executing. +/// +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + EFI_HOB_MEMORY_ALLOCATION_HEADER AllocDescriptor; +} EFI_HOB_MEMORY_ALLOCATION_STACK; + +/// +/// Defines the location of the boot-strap +/// processor (BSP) BSPStore ("Backing Store Pointer Store"). +/// This HOB is valid for the Itanium processor family only +/// register overflow store. +/// +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + EFI_HOB_MEMORY_ALLOCATION_HEADER AllocDescriptor; +} EFI_HOB_MEMORY_ALLOCATION_BSP_STORE; + +/// +/// Defines the location and entry point of the HOB consumer phase. +/// +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + EFI_HOB_MEMORY_ALLOCATION_HEADER MemoryAllocationHeader; + EFI_GUID ModuleName; + EFI_PHYSICAL_ADDRESS EntryPoint; +} EFI_HOB_MEMORY_ALLOCATION_MODULE; + +/// +/// Resource type +/// +typedef UINT32 EFI_RESOURCE_TYPE; + +// +// Value of ResourceType in EFI_HOB_RESOURCE_DESCRIPTOR. +// +#define EFI_RESOURCE_SYSTEM_MEMORY 0x00000000 +#define EFI_RESOURCE_MEMORY_MAPPED_IO 0x00000001 +#define EFI_RESOURCE_IO 0x00000002 +#define EFI_RESOURCE_FIRMWARE_DEVICE 0x00000003 +#define EFI_RESOURCE_MEMORY_MAPPED_IO_PORT 0x00000004 +#define EFI_RESOURCE_MEMORY_RESERVED 0x00000005 +#define EFI_RESOURCE_IO_RESERVED 0x00000006 +#define EFI_RESOURCE_MAX_MEMORY_TYPE 0x00000007 + +/// +/// type of recount attribute type +/// +typedef UINT32 EFI_RESOURCE_ATTRIBUTE_TYPE; + +// +// These types can be ORed together as needed. +// +// The first three enumerations describe settings +// +#define EFI_RESOURCE_ATTRIBUTE_PRESENT 0x00000001 +#define EFI_RESOURCE_ATTRIBUTE_INITIALIZED 0x00000002 +#define EFI_RESOURCE_ATTRIBUTE_TESTED 0x00000004 +// +// The rest of the settings describe capabilities +// +#define EFI_RESOURCE_ATTRIBUTE_SINGLE_BIT_ECC 0x00000008 +#define EFI_RESOURCE_ATTRIBUTE_MULTIPLE_BIT_ECC 0x00000010 +#define EFI_RESOURCE_ATTRIBUTE_ECC_RESERVED_1 0x00000020 +#define EFI_RESOURCE_ATTRIBUTE_ECC_RESERVED_2 0x00000040 +#define EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED 0x00000080 +#define EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED 0x00000100 +#define EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED 0x00000200 +#define EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE 0x00000400 +#define EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE 0x00000800 +#define EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE 0x00001000 +#define EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE 0x00002000 +#define EFI_RESOURCE_ATTRIBUTE_16_BIT_IO 0x00004000 +#define EFI_RESOURCE_ATTRIBUTE_32_BIT_IO 0x00008000 +#define EFI_RESOURCE_ATTRIBUTE_64_BIT_IO 0x00010000 +#define EFI_RESOURCE_ATTRIBUTE_UNCACHED_EXPORTED 0x00020000 + +/// +/// Describes the resource properties of all fixed, +/// nonrelocatable resource ranges found on the processor +/// host bus during the HOB producer phase. +/// +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + EFI_GUID Owner; + EFI_RESOURCE_TYPE ResourceType; + EFI_RESOURCE_ATTRIBUTE_TYPE ResourceAttribute; + EFI_PHYSICAL_ADDRESS PhysicalStart; + UINT64 ResourceLength; +} EFI_HOB_RESOURCE_DESCRIPTOR; + +/// +/// Allows writers of executable content in the HOB producer phase to +/// maintain and manage HOBs with specific GUID. +/// +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + EFI_GUID Name; + + /// + /// Guid specific data goes here + /// +} EFI_HOB_GUID_TYPE; + +/// +/// Details the location of firmware volumes that contain firmware files. +/// +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + EFI_PHYSICAL_ADDRESS BaseAddress; + UINT64 Length; +} EFI_HOB_FIRMWARE_VOLUME; + +/// +/// Details the location of a firmware volume which was extracted +/// from a file within another firmware volume. +/// +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + EFI_PHYSICAL_ADDRESS BaseAddress; + UINT64 Length; + EFI_GUID FvName; + EFI_GUID FileName; +} EFI_HOB_FIRMWARE_VOLUME2; + + +/// +/// Describes processor information, such as address space and I/O space capabilities. +/// +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + UINT8 SizeOfMemorySpace; + UINT8 SizeOfIoSpace; + UINT8 Reserved[6]; +} EFI_HOB_CPU; + + +/// +/// Describes pool memory allocations. +/// +typedef struct { + EFI_HOB_GENERIC_HEADER Header; +} EFI_HOB_MEMORY_POOL; + +/// +/// Union of all the possible HOB Types +/// +typedef union { + EFI_HOB_GENERIC_HEADER *Header; + EFI_HOB_HANDOFF_INFO_TABLE *HandoffInformationTable; + EFI_HOB_MEMORY_ALLOCATION *MemoryAllocation; + EFI_HOB_MEMORY_ALLOCATION_BSP_STORE *MemoryAllocationBspStore; + EFI_HOB_MEMORY_ALLOCATION_STACK *MemoryAllocationStack; + EFI_HOB_MEMORY_ALLOCATION_MODULE *MemoryAllocationModule; + EFI_HOB_RESOURCE_DESCRIPTOR *ResourceDescriptor; + EFI_HOB_GUID_TYPE *Guid; + EFI_HOB_FIRMWARE_VOLUME *FirmwareVolume; + EFI_HOB_FIRMWARE_VOLUME2 *FirmwareVolume2; + EFI_HOB_CPU *Cpu; + EFI_HOB_MEMORY_POOL *Pool; + UINT8 *Raw; +} EFI_PEI_HOB_POINTERS; + + +#endif diff --git a/gpxe/src/include/gpxe/efi/Pi/PiMultiPhase.h b/gpxe/src/include/gpxe/efi/Pi/PiMultiPhase.h new file mode 100644 index 0000000..3ab6421 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Pi/PiMultiPhase.h @@ -0,0 +1,104 @@ +/** @file + Include file matches things in PI for multiple module types. + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + PI Version 1.0 + +**/ + +#ifndef __PI_MULTIPHASE_H__ +#define __PI_MULTIPHASE_H__ + +#include + +#include +#include +#include + +#include +#include + + +#define EFI_NOT_AVAILABLE_YET EFIERR (32) + +/// +/// Status Code Type Definition +/// +typedef UINT32 EFI_STATUS_CODE_TYPE; + +// +// A Status Code Type is made up of the code type and severity +// All values masked by EFI_STATUS_CODE_RESERVED_MASK are +// reserved for use by this specification. +// +#define EFI_STATUS_CODE_TYPE_MASK 0x000000FF +#define EFI_STATUS_CODE_SEVERITY_MASK 0xFF000000 +#define EFI_STATUS_CODE_RESERVED_MASK 0x00FFFF00 + +// +// Definition of code types, all other values masked by +// EFI_STATUS_CODE_TYPE_MASK are reserved for use by +// this specification. +// +#define EFI_PROGRESS_CODE 0x00000001 +#define EFI_ERROR_CODE 0x00000002 +#define EFI_DEBUG_CODE 0x00000003 + +// +// Definitions of severities, all other values masked by +// EFI_STATUS_CODE_SEVERITY_MASK are reserved for use by +// this specification. +// Uncontained errors are major errors that could not contained +// to the specific component that is reporting the error +// For example, if a memory error was not detected early enough, +// the bad data could be consumed by other drivers. +// +#define EFI_ERROR_MINOR 0x40000000 +#define EFI_ERROR_MAJOR 0x80000000 +#define EFI_ERROR_UNRECOVERED 0x90000000 +#define EFI_ERROR_UNCONTAINED 0xa0000000 + +/// +/// Status Code Value Definition +/// +typedef UINT32 EFI_STATUS_CODE_VALUE; + +// +// A Status Code Value is made up of the class, subclass, and +// an operation. +// +#define EFI_STATUS_CODE_CLASS_MASK 0xFF000000 +#define EFI_STATUS_CODE_SUBCLASS_MASK 0x00FF0000 +#define EFI_STATUS_CODE_OPERATION_MASK 0x0000FFFF + +/// +/// Definition of Status Code extended data header. +/// The data will follow HeaderSize bytes from the beginning of +/// the structure and is Size bytes long. +/// +typedef struct { + UINT16 HeaderSize; + UINT16 Size; + EFI_GUID Type; +} EFI_STATUS_CODE_DATA; + + +// +// Bit values for Authentication Status +// +#define EFI_AUTH_STATUS_PLATFORM_OVERRIDE 0x01 +#define EFI_AUTH_STATUS_IMAGE_SIGNED 0x02 +#define EFI_AUTH_STATUS_NOT_TESTED 0x04 +#define EFI_AUTH_STATUS_TEST_FAILED 0x08 +#define EFI_AUTH_STATUS_ALL 0x0f + +#endif diff --git a/gpxe/src/include/gpxe/efi/PiDxe.h b/gpxe/src/include/gpxe/efi/PiDxe.h new file mode 100644 index 0000000..c326302 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/PiDxe.h @@ -0,0 +1,25 @@ +/** @file + + Root include file for Mde Package DXE_CORE, DXE, SMM, SAL type modules. + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __PI_DXE_H__ +#define __PI_DXE_H__ + +#include +#include + +#include + +#endif + diff --git a/gpxe/src/include/gpxe/efi/ProcessorBind.h b/gpxe/src/include/gpxe/efi/ProcessorBind.h new file mode 100644 index 0000000..3dbaf1b --- /dev/null +++ b/gpxe/src/include/gpxe/efi/ProcessorBind.h @@ -0,0 +1,14 @@ +/* + * EFI header files rely on having the CPU architecture directory + * present in the search path in order to pick up ProcessorBind.h. We + * use this header file as a quick indirection layer. + * - mcb30 + */ + +#if __i386__ +#include +#endif + +#if __x86_64__ +#include +#endif diff --git a/gpxe/src/include/gpxe/efi/Protocol/ComponentName2.h b/gpxe/src/include/gpxe/efi/Protocol/ComponentName2.h new file mode 100644 index 0000000..0f01014 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Protocol/ComponentName2.h @@ -0,0 +1,174 @@ +/** @file + UEFI Component Name 2 Protocol as defined in the UEFI 2.1 specification. + This protocol is used to retrieve user readable names of drivers + and controllers managed by UEFI Drivers. + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_COMPONENT_NAME2_H__ +#define __EFI_COMPONENT_NAME2_H__ + +/// +/// Global ID for the Component Name Protocol +/// +#define EFI_COMPONENT_NAME2_PROTOCOL_GUID \ + {0x6a7a5cff, 0xe8d9, 0x4f70, { 0xba, 0xda, 0x75, 0xab, 0x30, 0x25, 0xce, 0x14 } } + +typedef struct _EFI_COMPONENT_NAME2_PROTOCOL EFI_COMPONENT_NAME2_PROTOCOL; + + +/** + Retrieves a Unicode string that is the user readable name of + the EFI Driver. + + @param This A pointer to the + EFI_COMPONENT_NAME2_PROTOCOL instance. + + @param Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller + is requesting, and it must match one of the + languages specified in SupportedLanguages. + The number of languages supported by a + driver is up to the driver writer. Language + is specified in RFC 3066 language code + format. + + @param DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the + Driver specified by This and the + language specified by Language + was returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This + does not support the language + specified by Language. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_COMPONENT_NAME2_GET_DRIVER_NAME)( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of + the controller that is being managed by an EFI Driver. + + @param This A pointer to the + EFI_COMPONENT_NAME2_PROTOCOL instance. + + @param ControllerHandle The handle of a controller that the + driver specified by This is managing. + This handle specifies the controller + whose name is to be returned. + + @param ChildHandle The handle of the child controller to + retrieve the name of. This is an + optional parameter that may be NULL. + It will be NULL for device drivers. + It will also be NULL for a bus + drivers that wish to retrieve the + name of the bus controller. It will + not be NULL for a bus driver that + wishes to retrieve the name of a + child controller. + + @param Language A pointer to a Null-terminated ASCII + string array indicating the language. + This is the language of the driver + name that the caller is requesting, + and it must match one of the + languages specified in + SupportedLanguages. The number of + languages supported by a driver is up + to the driver writer. Language is + specified in RFC 3066 language code + format. + + @param ControllerName A pointer to the Unicode string to + return. This Unicode string is the + name of the controller specified by + ControllerHandle and ChildHandle in + the language specified by Language + from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user + readable name in the language + specified by Language for the + driver specified by This was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it + is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is + not currently managing the + controller specified by + ControllerHandle and + ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This + does not support the language + specified by Language. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +/// +/// This protocol is used to retrieve user readable names of drivers +/// and controllers managed by UEFI Drivers. +/// +struct _EFI_COMPONENT_NAME2_PROTOCOL { + EFI_COMPONENT_NAME2_GET_DRIVER_NAME GetDriverName; + EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME GetControllerName; + + /// + /// A Null-terminated ASCII string array that contains one or more + /// supported language codes. This is the list of language codes that + /// this protocol supports. The number of languages supported by a + /// driver is up to the driver writer. SupportedLanguages is + /// specified in RFC 3066 format. + /// + CHAR8 *SupportedLanguages; +}; + +extern EFI_GUID gEfiComponentName2ProtocolGuid; + +#endif diff --git a/gpxe/src/include/gpxe/efi/Protocol/Cpu.h b/gpxe/src/include/gpxe/efi/Protocol/Cpu.h new file mode 100644 index 0000000..da3d0fd --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Protocol/Cpu.h @@ -0,0 +1,291 @@ +/** @file + CPU Architectural Protocol as defined in PI spec Volume 2 DXE + + This code abstracts the DXE core from processor implementation details. + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __ARCH_PROTOCOL_CPU_H__ +#define __ARCH_PROTOCOL_CPU_H__ + +#include + +#define EFI_CPU_ARCH_PROTOCOL_GUID \ + { 0x26baccb1, 0x6f42, 0x11d4, {0xbc, 0xe7, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81 } } + +typedef struct _EFI_CPU_ARCH_PROTOCOL EFI_CPU_ARCH_PROTOCOL; + +typedef enum { + EfiCpuFlushTypeWriteBackInvalidate, + EfiCpuFlushTypeWriteBack, + EfiCpuFlushTypeInvalidate, + EfiCpuMaxFlushType +} EFI_CPU_FLUSH_TYPE; + +typedef enum { + EfiCpuInit, + EfiCpuMaxInitType +} EFI_CPU_INIT_TYPE; + +/** + EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs. + + @param InterruptType Defines the type of interrupt or exception that + occurred on the processor.This parameter is processor architecture specific. + @param SystemContext A pointer to the processor context when + the interrupt occurred on the processor. + + @return None + +**/ +typedef +VOID +(EFIAPI *EFI_CPU_INTERRUPT_HANDLER)( + IN CONST EFI_EXCEPTION_TYPE InterruptType, + IN CONST EFI_SYSTEM_CONTEXT SystemContext + ); + +/** + This function flushes the range of addresses from Start to Start+Length + from the processor's data cache. If Start is not aligned to a cache line + boundary, then the bytes before Start to the preceding cache line boundary + are also flushed. If Start+Length is not aligned to a cache line boundary, + then the bytes past Start+Length to the end of the next cache line boundary + are also flushed. The FlushType of EfiCpuFlushTypeWriteBackInvalidate must be + supported. If the data cache is fully coherent with all DMA operations, then + this function can just return EFI_SUCCESS. If the processor does not support + flushing a range of the data cache, then the entire data cache can be flushed. + + @param This The EFI_CPU_ARCH_PROTOCOL instance. + @param Start The beginning physical address to flush from the processor's data + cache. + @param Length The number of bytes to flush from the processor's data cache. This + function may flush more bytes than Length specifies depending upon + the granularity of the flush operation that the processor supports. + @param FlushType Specifies the type of flush operation to perform. + + @retval EFI_SUCCESS The address range from Start to Start+Length was flushed from + the processor's data cache. + @retval EFI_UNSUPPORTEDT The processor does not support the cache flush type specified + by FlushType. + @retval EFI_DEVICE_ERROR The address range from Start to Start+Length could not be flushed + from the processor's data cache. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_CPU_FLUSH_DATA_CACHE)( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS Start, + IN UINT64 Length, + IN EFI_CPU_FLUSH_TYPE FlushType + ); + + +/** + This function enables interrupt processing by the processor. + + @param This The EFI_CPU_ARCH_PROTOCOL instance. + + @retval EFI_SUCCESS Interrupts are enabled on the processor. + @retval EFI_DEVICE_ERROR Interrupts could not be enabled on the processor. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_CPU_ENABLE_INTERRUPT)( + IN EFI_CPU_ARCH_PROTOCOL *This + ); + + +/** + This function disables interrupt processing by the processor. + + @param This The EFI_CPU_ARCH_PROTOCOL instance. + + @retval EFI_SUCCESS Interrupts are disabled on the processor. + @retval EFI_DEVICE_ERROR Interrupts could not be disabled on the processor. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_CPU_DISABLE_INTERRUPT)( + IN EFI_CPU_ARCH_PROTOCOL *This + ); + + +/** + This function retrieves the processor's current interrupt state a returns it in + State. If interrupts are currently enabled, then TRUE is returned. If interrupts + are currently disabled, then FALSE is returned. + + @param This The EFI_CPU_ARCH_PROTOCOL instance. + @param State A pointer to the processor's current interrupt state. Set to TRUE if + interrupts are enabled and FALSE if interrupts are disabled. + + @retval EFI_SUCCESS The processor's current interrupt state was returned in State. + @retval EFI_INVALID_PARAMETER State is NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_CPU_GET_INTERRUPT_STATE)( + IN EFI_CPU_ARCH_PROTOCOL *This, + OUT BOOLEAN *State + ); + + +/** + This function generates an INIT on the processor. If this function succeeds, then the + processor will be reset, and control will not be returned to the caller. If InitType is + not supported by this processor, or the processor cannot programmatically generate an + INIT without help from external hardware, then EFI_UNSUPPORTED is returned. If an error + occurs attempting to generate an INIT, then EFI_DEVICE_ERROR is returned. + + @param This The EFI_CPU_ARCH_PROTOCOL instance. + @param InitType The type of processor INIT to perform. + + @retval EFI_SUCCESS The processor INIT was performed. This return code should never be seen. + @retval EFI_UNSUPPORTED The processor INIT operation specified by InitType is not supported + by this processor. + @retval EFI_DEVICE_ERROR The processor INIT failed. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_CPU_INIT)( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_CPU_INIT_TYPE InitType + ); + + +/** + This function registers and enables the handler specified by InterruptHandler for a processor + interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the + handler for the processor interrupt or exception type specified by InterruptType is uninstalled. + The installed handler is called once for each processor interrupt or exception. + + @param This The EFI_CPU_ARCH_PROTOCOL instance. + @param InterruptType A pointer to the processor's current interrupt state. Set to TRUE if interrupts + are enabled and FALSE if interrupts are disabled. + @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called + when a processor interrupt occurs. If this parameter is NULL, then the handler + will be uninstalled. + + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled. + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was + previously installed. + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not + previously installed. + @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_CPU_REGISTER_INTERRUPT_HANDLER)( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler + ); + + +/** + This function reads the processor timer specified by TimerIndex and returns it in TimerValue. + + @param This The EFI_CPU_ARCH_PROTOCOL instance. + @param TimerIndex Specifies which processor timer is to be returned in TimerValue. This parameter + must be between 0 and NumberOfTimers-1. + @param TimerValue Pointer to the returned timer value. + @param TimerPeriod A pointer to the amount of time that passes in femtoseconds for each increment + of TimerValue. + + @retval EFI_SUCCESS The processor timer value specified by TimerIndex was returned in TimerValue. + @retval EFI_DEVICE_ERROR An error occurred attempting to read one of the processor's timers. + @retval EFI_INVALID_PARAMETER TimerValue is NULL or TimerIndex is not valid. + @retval EFI_UNSUPPORTED The processor does not have any readable timers. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_CPU_GET_TIMER_VALUE)( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN UINT32 TimerIndex, + OUT UINT64 *TimerValue, + OUT UINT64 *TimerPeriod OPTIONAL + ); + + +/** + This function modifies the attributes for the memory region specified by BaseAddress and + Length from their current attributes to the attributes specified by Attributes. + + @param This The EFI_CPU_ARCH_PROTOCOL instance. + @param BaseAddress The physical address that is the start address of a memory region. + @param Length The size in bytes of the memory region. + @param Attributes The bit mask of attributes to set for the memory region. + + @retval EFI_SUCCESS The attributes were set for the memory region. + @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by + BaseAddress and Length cannot be modified. + @retval EFI_INVALID_PARAMETER Length is zero. + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of + the memory resource range. + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory + resource range specified by BaseAddress and Length. + The bit mask of attributes is not support for the memory resource + range specified by BaseAddress and Length. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_CPU_SET_MEMORY_ATTRIBUTES)( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ); + + +/// +/// The EFI_CPU_ARCH_PROTOCOL is used to abstract processor-specific functions from the DXE +/// Foundation. This includes flushing caches, enabling and disabling interrupts, hooking interrupt +/// vectors and exception vectors, reading internal processor timers, resetting the processor, and +/// determining the processor frequency. +/// +struct _EFI_CPU_ARCH_PROTOCOL { + EFI_CPU_FLUSH_DATA_CACHE FlushDataCache; + EFI_CPU_ENABLE_INTERRUPT EnableInterrupt; + EFI_CPU_DISABLE_INTERRUPT DisableInterrupt; + EFI_CPU_GET_INTERRUPT_STATE GetInterruptState; + EFI_CPU_INIT Init; + EFI_CPU_REGISTER_INTERRUPT_HANDLER RegisterInterruptHandler; + EFI_CPU_GET_TIMER_VALUE GetTimerValue; + EFI_CPU_SET_MEMORY_ATTRIBUTES SetMemoryAttributes; + /// + /// The number of timers that are available in a processor. The value in this + /// field is a constant that must not be modified after the CPU Architectural + /// Protocol is installed. All consumers must treat this as a read-only field. + /// + UINT32 NumberOfTimers; + /// + /// The size, in bytes, of the alignment required for DMA buffer allocations. + /// This is typically the size of the largest data cache line in the platform. + /// The value in this field is a constant that must not be modified after the + /// CPU Architectural Protocol is installed. All consumers must treat this as + /// a read-only field. + /// + UINT32 DmaBufferAlignment; +}; + +extern EFI_GUID gEfiCpuArchProtocolGuid; + +#endif diff --git a/gpxe/src/include/gpxe/efi/Protocol/CpuIo.h b/gpxe/src/include/gpxe/efi/Protocol/CpuIo.h new file mode 100644 index 0000000..8d35c6c --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Protocol/CpuIo.h @@ -0,0 +1,128 @@ +/** @file + This code abstracts the CPU IO Protocol which installed by some platform or chipset-specific + PEIM that abstracts the processor-visible I/O operations. + + Copyright (c) 2007, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + Module Name: CpuIO.h + + @par Revision Reference: + CPU IO Protocol is defined in Framework of EFI CPU IO Protocol Spec + Version 0.9 + +**/ + +#ifndef _CPUIO_H_ +#define _CPUIO_H_ + +#include + +#define EFI_CPU_IO_PROTOCOL_GUID \ + { \ + 0xB0732526, 0x38C8, 0x4b40, {0x88, 0x77, 0x61, 0xC7, 0xB0, 0x6A, 0xAC, 0x45 } \ + } + +typedef struct _EFI_CPU_IO_PROTOCOL EFI_CPU_IO_PROTOCOL; + +// +// ******************************************************* +// EFI_CPU_IO_PROTOCOL_WIDTH +// ******************************************************* +// +typedef enum { + EfiCpuIoWidthUint8, + EfiCpuIoWidthUint16, + EfiCpuIoWidthUint32, + EfiCpuIoWidthUint64, + EfiCpuIoWidthFifoUint8, + EfiCpuIoWidthFifoUint16, + EfiCpuIoWidthFifoUint32, + EfiCpuIoWidthFifoUint64, + EfiCpuIoWidthFillUint8, + EfiCpuIoWidthFillUint16, + EfiCpuIoWidthFillUint32, + EfiCpuIoWidthFillUint64, + EfiCpuIoWidthMaximum +} EFI_CPU_IO_PROTOCOL_WIDTH; + +// +// ******************************************************* +// EFI_CPU_IO_PROTOCOL_IO_MEM +// ******************************************************* +// +/** + Enables a driver to access memory-mapped registers in the EFI system memory space. + Or, Enables a driver to access registers in the EFI CPU I/O space. + + @param This A pointer to the EFI_CPU_IO_PROTOCOL instance. + @param Width Signifies the width of the I/O or Memory operation. + @param Address The base address of the I/O or Memoryoperation. + @param Count The number of I/O or Memory operations to perform. + The number of bytes moved is Width size * Count, starting at Address. + @param Buffer For read operations, the destination buffer to store the results. + For write operations, the source buffer from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the EFI system. + @retval EFI_INVALID_PARAMETER Width is invalid for this EFI system.Or Buffer is NULL. + @retval EFI_UNSUPPORTED The Buffer is not aligned for the given Width. + Or,The address range specified by Address, Width, and Count is not valid for this EFI system. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_CPU_IO_PROTOCOL_IO_MEM)( + IN EFI_CPU_IO_PROTOCOL *This, + IN EFI_CPU_IO_PROTOCOL_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN OUT VOID *Buffer + ); + +// +// ******************************************************* +// EFI_CPU_IO_PROTOCOL_ACCESS +// ******************************************************* +// +typedef struct { + EFI_CPU_IO_PROTOCOL_IO_MEM Read; + EFI_CPU_IO_PROTOCOL_IO_MEM Write; +} EFI_CPU_IO_PROTOCOL_ACCESS; + +// +// ******************************************************* +// EFI_CPU_IO_PROTOCOL +// ******************************************************* +// +/** + @par Protocol Description: + Provides the basic memory and I/O interfaces that are used to abstract + accesses to devices in a system. + + @param Mem.Read + Allows reads from memory-mapped I/O space. + + @param Mem.Write + Allows writes to memory-mapped I/O space. + + @param Io.Read + Allows reads from I/O space. + + @param Io.Write + Allows writes to I/O space. + +**/ +struct _EFI_CPU_IO_PROTOCOL { + EFI_CPU_IO_PROTOCOL_ACCESS Mem; + EFI_CPU_IO_PROTOCOL_ACCESS Io; +}; + +extern EFI_GUID gEfiCpuIoProtocolGuid; + +#endif diff --git a/gpxe/src/include/gpxe/efi/Protocol/DebugSupport.h b/gpxe/src/include/gpxe/efi/Protocol/DebugSupport.h new file mode 100644 index 0000000..c8db249 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Protocol/DebugSupport.h @@ -0,0 +1,634 @@ +/** @file + DebugSupport protocol and supporting definitions as defined in the UEFI2.0 + specification. + + The DebugSupport protocol is used by source level debuggers to abstract the + processor and handle context save and restore operations. + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __DEBUG_SUPPORT_H__ +#define __DEBUG_SUPPORT_H__ + +#include +#include + +typedef struct _EFI_DEBUG_SUPPORT_PROTOCOL EFI_DEBUG_SUPPORT_PROTOCOL; + +/// +/// Debug Support protocol {2755590C-6F3C-42FA-9EA4-A3BA543CDA25} +/// +#define EFI_DEBUG_SUPPORT_PROTOCOL_GUID \ + { \ + 0x2755590C, 0x6F3C, 0x42FA, {0x9E, 0xA4, 0xA3, 0xBA, 0x54, 0x3C, 0xDA, 0x25 } \ + } + +/// +/// Debug Support definitions +/// +typedef INTN EFI_EXCEPTION_TYPE; + +// +// IA-32 processor exception types +// +#define EXCEPT_IA32_DIVIDE_ERROR 0 +#define EXCEPT_IA32_DEBUG 1 +#define EXCEPT_IA32_NMI 2 +#define EXCEPT_IA32_BREAKPOINT 3 +#define EXCEPT_IA32_OVERFLOW 4 +#define EXCEPT_IA32_BOUND 5 +#define EXCEPT_IA32_INVALID_OPCODE 6 +#define EXCEPT_IA32_DOUBLE_FAULT 8 +#define EXCEPT_IA32_INVALID_TSS 10 +#define EXCEPT_IA32_SEG_NOT_PRESENT 11 +#define EXCEPT_IA32_STACK_FAULT 12 +#define EXCEPT_IA32_GP_FAULT 13 +#define EXCEPT_IA32_PAGE_FAULT 14 +#define EXCEPT_IA32_FP_ERROR 16 +#define EXCEPT_IA32_ALIGNMENT_CHECK 17 +#define EXCEPT_IA32_MACHINE_CHECK 18 +#define EXCEPT_IA32_SIMD 19 + +/// +/// IA-32 processor context definition +/// +/// +/// FXSAVE_STATE +/// FP / MMX / XMM registers (see fxrstor instruction definition) +/// +typedef struct { + UINT16 Fcw; + UINT16 Fsw; + UINT16 Ftw; + UINT16 Opcode; + UINT32 Eip; + UINT16 Cs; + UINT16 Reserved1; + UINT32 DataOffset; + UINT16 Ds; + UINT8 Reserved2[10]; + UINT8 St0Mm0[10], Reserved3[6]; + UINT8 St1Mm1[10], Reserved4[6]; + UINT8 St2Mm2[10], Reserved5[6]; + UINT8 St3Mm3[10], Reserved6[6]; + UINT8 St4Mm4[10], Reserved7[6]; + UINT8 St5Mm5[10], Reserved8[6]; + UINT8 St6Mm6[10], Reserved9[6]; + UINT8 St7Mm7[10], Reserved10[6]; + UINT8 Xmm0[16]; + UINT8 Xmm1[16]; + UINT8 Xmm2[16]; + UINT8 Xmm3[16]; + UINT8 Xmm4[16]; + UINT8 Xmm5[16]; + UINT8 Xmm6[16]; + UINT8 Xmm7[16]; + UINT8 Reserved11[14 * 16]; +} EFI_FX_SAVE_STATE_IA32; + +typedef struct { + UINT32 ExceptionData; + EFI_FX_SAVE_STATE_IA32 FxSaveState; + UINT32 Dr0; + UINT32 Dr1; + UINT32 Dr2; + UINT32 Dr3; + UINT32 Dr6; + UINT32 Dr7; + UINT32 Cr0; + UINT32 Cr1; /* Reserved */ + UINT32 Cr2; + UINT32 Cr3; + UINT32 Cr4; + UINT32 Eflags; + UINT32 Ldtr; + UINT32 Tr; + UINT32 Gdtr[2]; + UINT32 Idtr[2]; + UINT32 Eip; + UINT32 Gs; + UINT32 Fs; + UINT32 Es; + UINT32 Ds; + UINT32 Cs; + UINT32 Ss; + UINT32 Edi; + UINT32 Esi; + UINT32 Ebp; + UINT32 Esp; + UINT32 Ebx; + UINT32 Edx; + UINT32 Ecx; + UINT32 Eax; +} EFI_SYSTEM_CONTEXT_IA32; + +// +// X64 processor exception types +// +#define EXCEPT_X64_DIVIDE_ERROR 0 +#define EXCEPT_X64_DEBUG 1 +#define EXCEPT_X64_NMI 2 +#define EXCEPT_X64_BREAKPOINT 3 +#define EXCEPT_X64_OVERFLOW 4 +#define EXCEPT_X64_BOUND 5 +#define EXCEPT_X64_INVALID_OPCODE 6 +#define EXCEPT_X64_DOUBLE_FAULT 8 +#define EXCEPT_X64_INVALID_TSS 10 +#define EXCEPT_X64_SEG_NOT_PRESENT 11 +#define EXCEPT_X64_STACK_FAULT 12 +#define EXCEPT_X64_GP_FAULT 13 +#define EXCEPT_X64_PAGE_FAULT 14 +#define EXCEPT_X64_FP_ERROR 16 +#define EXCEPT_X64_ALIGNMENT_CHECK 17 +#define EXCEPT_X64_MACHINE_CHECK 18 +#define EXCEPT_X64_SIMD 19 + +/// +/// X64 processor context definition +/// +/// FXSAVE_STATE +/// FP / MMX / XMM registers (see fxrstor instruction definition) +/// +typedef struct { + UINT16 Fcw; + UINT16 Fsw; + UINT16 Ftw; + UINT16 Opcode; + UINT64 Rip; + UINT64 DataOffset; + UINT8 Reserved1[8]; + UINT8 St0Mm0[10], Reserved2[6]; + UINT8 St1Mm1[10], Reserved3[6]; + UINT8 St2Mm2[10], Reserved4[6]; + UINT8 St3Mm3[10], Reserved5[6]; + UINT8 St4Mm4[10], Reserved6[6]; + UINT8 St5Mm5[10], Reserved7[6]; + UINT8 St6Mm6[10], Reserved8[6]; + UINT8 St7Mm7[10], Reserved9[6]; + UINT8 Xmm0[16]; + UINT8 Xmm1[16]; + UINT8 Xmm2[16]; + UINT8 Xmm3[16]; + UINT8 Xmm4[16]; + UINT8 Xmm5[16]; + UINT8 Xmm6[16]; + UINT8 Xmm7[16]; + // + // NOTE: UEFI 2.0 spec definition as follows. + // + UINT8 Reserved11[14 * 16]; +} EFI_FX_SAVE_STATE_X64; + +typedef struct { + UINT64 ExceptionData; + EFI_FX_SAVE_STATE_X64 FxSaveState; + UINT64 Dr0; + UINT64 Dr1; + UINT64 Dr2; + UINT64 Dr3; + UINT64 Dr6; + UINT64 Dr7; + UINT64 Cr0; + UINT64 Cr1; /* Reserved */ + UINT64 Cr2; + UINT64 Cr3; + UINT64 Cr4; + UINT64 Cr8; + UINT64 Rflags; + UINT64 Ldtr; + UINT64 Tr; + UINT64 Gdtr[2]; + UINT64 Idtr[2]; + UINT64 Rip; + UINT64 Gs; + UINT64 Fs; + UINT64 Es; + UINT64 Ds; + UINT64 Cs; + UINT64 Ss; + UINT64 Rdi; + UINT64 Rsi; + UINT64 Rbp; + UINT64 Rsp; + UINT64 Rbx; + UINT64 Rdx; + UINT64 Rcx; + UINT64 Rax; + UINT64 R8; + UINT64 R9; + UINT64 R10; + UINT64 R11; + UINT64 R12; + UINT64 R13; + UINT64 R14; + UINT64 R15; +} EFI_SYSTEM_CONTEXT_X64; + +// +// IPF processor exception types +// +#define EXCEPT_IPF_VHTP_TRANSLATION 0 +#define EXCEPT_IPF_INSTRUCTION_TLB 1 +#define EXCEPT_IPF_DATA_TLB 2 +#define EXCEPT_IPF_ALT_INSTRUCTION_TLB 3 +#define EXCEPT_IPF_ALT_DATA_TLB 4 +#define EXCEPT_IPF_DATA_NESTED_TLB 5 +#define EXCEPT_IPF_INSTRUCTION_KEY_MISSED 6 +#define EXCEPT_IPF_DATA_KEY_MISSED 7 +#define EXCEPT_IPF_DIRTY_BIT 8 +#define EXCEPT_IPF_INSTRUCTION_ACCESS_BIT 9 +#define EXCEPT_IPF_DATA_ACCESS_BIT 10 +#define EXCEPT_IPF_BREAKPOINT 11 +#define EXCEPT_IPF_EXTERNAL_INTERRUPT 12 +// +// 13 - 19 reserved +// +#define EXCEPT_IPF_PAGE_NOT_PRESENT 20 +#define EXCEPT_IPF_KEY_PERMISSION 21 +#define EXCEPT_IPF_INSTRUCTION_ACCESS_RIGHTS 22 +#define EXCEPT_IPF_DATA_ACCESS_RIGHTS 23 +#define EXCEPT_IPF_GENERAL_EXCEPTION 24 +#define EXCEPT_IPF_DISABLED_FP_REGISTER 25 +#define EXCEPT_IPF_NAT_CONSUMPTION 26 +#define EXCEPT_IPF_SPECULATION 27 +// +// 28 reserved +// +#define EXCEPT_IPF_DEBUG 29 +#define EXCEPT_IPF_UNALIGNED_REFERENCE 30 +#define EXCEPT_IPF_UNSUPPORTED_DATA_REFERENCE 31 +#define EXCEPT_IPF_FP_FAULT 32 +#define EXCEPT_IPF_FP_TRAP 33 +#define EXCEPT_IPF_LOWER_PRIVILEGE_TRANSFER_TRAP 34 +#define EXCEPT_IPF_TAKEN_BRANCH 35 +#define EXCEPT_IPF_SINGLE_STEP 36 +// +// 37 - 44 reserved +// +#define EXCEPT_IPF_IA32_EXCEPTION 45 +#define EXCEPT_IPF_IA32_INTERCEPT 46 +#define EXCEPT_IPF_IA32_INTERRUPT 47 + +/// +/// IPF processor context definition +/// +typedef struct { + // + // The first reserved field is necessary to preserve alignment for the correct + // bits in UNAT and to insure F2 is 16 byte aligned.. + // + UINT64 Reserved; + UINT64 R1; + UINT64 R2; + UINT64 R3; + UINT64 R4; + UINT64 R5; + UINT64 R6; + UINT64 R7; + UINT64 R8; + UINT64 R9; + UINT64 R10; + UINT64 R11; + UINT64 R12; + UINT64 R13; + UINT64 R14; + UINT64 R15; + UINT64 R16; + UINT64 R17; + UINT64 R18; + UINT64 R19; + UINT64 R20; + UINT64 R21; + UINT64 R22; + UINT64 R23; + UINT64 R24; + UINT64 R25; + UINT64 R26; + UINT64 R27; + UINT64 R28; + UINT64 R29; + UINT64 R30; + UINT64 R31; + + UINT64 F2[2]; + UINT64 F3[2]; + UINT64 F4[2]; + UINT64 F5[2]; + UINT64 F6[2]; + UINT64 F7[2]; + UINT64 F8[2]; + UINT64 F9[2]; + UINT64 F10[2]; + UINT64 F11[2]; + UINT64 F12[2]; + UINT64 F13[2]; + UINT64 F14[2]; + UINT64 F15[2]; + UINT64 F16[2]; + UINT64 F17[2]; + UINT64 F18[2]; + UINT64 F19[2]; + UINT64 F20[2]; + UINT64 F21[2]; + UINT64 F22[2]; + UINT64 F23[2]; + UINT64 F24[2]; + UINT64 F25[2]; + UINT64 F26[2]; + UINT64 F27[2]; + UINT64 F28[2]; + UINT64 F29[2]; + UINT64 F30[2]; + UINT64 F31[2]; + + UINT64 Pr; + + UINT64 B0; + UINT64 B1; + UINT64 B2; + UINT64 B3; + UINT64 B4; + UINT64 B5; + UINT64 B6; + UINT64 B7; + + // + // application registers + // + UINT64 ArRsc; + UINT64 ArBsp; + UINT64 ArBspstore; + UINT64 ArRnat; + + UINT64 ArFcr; + + UINT64 ArEflag; + UINT64 ArCsd; + UINT64 ArSsd; + UINT64 ArCflg; + UINT64 ArFsr; + UINT64 ArFir; + UINT64 ArFdr; + + UINT64 ArCcv; + + UINT64 ArUnat; + + UINT64 ArFpsr; + + UINT64 ArPfs; + UINT64 ArLc; + UINT64 ArEc; + + // + // control registers + // + UINT64 CrDcr; + UINT64 CrItm; + UINT64 CrIva; + UINT64 CrPta; + UINT64 CrIpsr; + UINT64 CrIsr; + UINT64 CrIip; + UINT64 CrIfa; + UINT64 CrItir; + UINT64 CrIipa; + UINT64 CrIfs; + UINT64 CrIim; + UINT64 CrIha; + + // + // debug registers + // + UINT64 Dbr0; + UINT64 Dbr1; + UINT64 Dbr2; + UINT64 Dbr3; + UINT64 Dbr4; + UINT64 Dbr5; + UINT64 Dbr6; + UINT64 Dbr7; + + UINT64 Ibr0; + UINT64 Ibr1; + UINT64 Ibr2; + UINT64 Ibr3; + UINT64 Ibr4; + UINT64 Ibr5; + UINT64 Ibr6; + UINT64 Ibr7; + + // + // virtual registers - nat bits for R1-R31 + // + UINT64 IntNat; + +} EFI_SYSTEM_CONTEXT_IPF; + +// +// EBC processor exception types +// +#define EXCEPT_EBC_UNDEFINED 0 +#define EXCEPT_EBC_DIVIDE_ERROR 1 +#define EXCEPT_EBC_DEBUG 2 +#define EXCEPT_EBC_BREAKPOINT 3 +#define EXCEPT_EBC_OVERFLOW 4 +#define EXCEPT_EBC_INVALID_OPCODE 5 // opcode out of range +#define EXCEPT_EBC_STACK_FAULT 6 +#define EXCEPT_EBC_ALIGNMENT_CHECK 7 +#define EXCEPT_EBC_INSTRUCTION_ENCODING 8 // malformed instruction +#define EXCEPT_EBC_BAD_BREAK 9 // BREAK 0 or undefined BREAK +#define EXCEPT_EBC_STEP 10 // to support debug stepping +/// +/// For coding convenience, define the maximum valid EBC exception. +/// +#define MAX_EBC_EXCEPTION EXCEPT_EBC_STEP + +/// +/// EBC processor context definition +/// +typedef struct { + UINT64 R0; + UINT64 R1; + UINT64 R2; + UINT64 R3; + UINT64 R4; + UINT64 R5; + UINT64 R6; + UINT64 R7; + UINT64 Flags; + UINT64 ControlFlags; + UINT64 Ip; +} EFI_SYSTEM_CONTEXT_EBC; + +/// +/// Universal EFI_SYSTEM_CONTEXT definition +/// +typedef union { + EFI_SYSTEM_CONTEXT_EBC *SystemContextEbc; + EFI_SYSTEM_CONTEXT_IA32 *SystemContextIa32; + EFI_SYSTEM_CONTEXT_X64 *SystemContextX64; + EFI_SYSTEM_CONTEXT_IPF *SystemContextIpf; +} EFI_SYSTEM_CONTEXT; + +// +// DebugSupport callback function prototypes +// + +/** + Registers and enables an exception callback function for the specified exception. + + @param ExceptionType Exception types in EBC, IA-32, X64, or IPF + @param SystemContext Exception content. + +**/ +typedef +VOID +(*EFI_EXCEPTION_CALLBACK)( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ); + +/** + Registers and enables the on-target debug agent's periodic entry point. + + @param SystemContext Exception content. + +**/ +typedef +VOID +(*EFI_PERIODIC_CALLBACK)( + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ); + +/// +/// Machine type definition +/// +typedef enum { + IsaIa32 = IMAGE_FILE_MACHINE_I386, ///< 0x014C + IsaX64 = IMAGE_FILE_MACHINE_X64, ///< 0x8664 + IsaIpf = IMAGE_FILE_MACHINE_IA64, ///< 0x0200 + IsaEbc = IMAGE_FILE_MACHINE_EBC ///< 0x0EBC +} EFI_INSTRUCTION_SET_ARCHITECTURE; + + +// +// DebugSupport member function definitions +// + +/** + Returns the maximum value that may be used for the ProcessorIndex parameter in + RegisterPeriodicCallback() and RegisterExceptionCallback(). + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param MaxProcessorIndex Pointer to a caller-allocated UINTN in which the maximum supported + processor index is returned. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_GET_MAXIMUM_PROCESSOR_INDEX)( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + OUT UINTN *MaxProcessorIndex + ); + +/** + Registers a function to be called back periodically in interrupt context. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param ProcessorIndex Specifies which processor the callback function applies to. + @param PeriodicCallback A pointer to a function of type PERIODIC_CALLBACK that is the main + periodic entry point of the debug agent. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ALREADY_STARTED Non-NULL PeriodicCallback parameter when a callback + function was previously registered. + @retval EFI_OUT_OF_RESOURCES System has insufficient memory resources to register new callback + function. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_REGISTER_PERIODIC_CALLBACK)( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_PERIODIC_CALLBACK PeriodicCallback + ); + +/** + Registers a function to be called when a given processor exception occurs. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param ProcessorIndex Specifies which processor the callback function applies to. + @param PeriodicCallback A pointer to a function of type EXCEPTION_CALLBACK that is called + when the processor exception specified by ExceptionType occurs. + @param ExceptionType Specifies which processor exception to hook. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_ALREADY_STARTED Non-NULL PeriodicCallback parameter when a callback + function was previously registered. + @retval EFI_OUT_OF_RESOURCES System has insufficient memory resources to register new callback + function. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_REGISTER_EXCEPTION_CALLBACK)( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN EFI_EXCEPTION_CALLBACK ExceptionCallback, + IN EFI_EXCEPTION_TYPE ExceptionType + ); + +/** + Invalidates processor instruction cache for a memory range. Subsequent execution in this range + causes a fresh memory fetch to retrieve code to be executed. + + @param This A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance. + @param ProcessorIndex Specifies which processor's instruction cache is to be invalidated. + @param Start Specifies the physical base of the memory range to be invalidated. + @param Length Specifies the minimum number of bytes in the processor's instruction + cache to invalidate. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_INVALIDATE_INSTRUCTION_CACHE)( + IN EFI_DEBUG_SUPPORT_PROTOCOL *This, + IN UINTN ProcessorIndex, + IN VOID *Start, + IN UINT64 Length + ); + +/// +/// This protocol provides the services to allow the debug agent to register +/// callback functions that are called either periodically or when specific +/// processor exceptions occur. +/// +struct _EFI_DEBUG_SUPPORT_PROTOCOL { + /// + /// Declares the processor architecture for this instance of the EFI Debug Support protocol. + /// + EFI_INSTRUCTION_SET_ARCHITECTURE Isa; + EFI_GET_MAXIMUM_PROCESSOR_INDEX GetMaximumProcessorIndex; + EFI_REGISTER_PERIODIC_CALLBACK RegisterPeriodicCallback; + EFI_REGISTER_EXCEPTION_CALLBACK RegisterExceptionCallback; + EFI_INVALIDATE_INSTRUCTION_CACHE InvalidateInstructionCache; +}; + +extern EFI_GUID gEfiDebugSupportProtocolGuid; + +#endif diff --git a/gpxe/src/include/gpxe/efi/Protocol/DevicePath.h b/gpxe/src/include/gpxe/efi/Protocol/DevicePath.h new file mode 100644 index 0000000..e847668 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Protocol/DevicePath.h @@ -0,0 +1,535 @@ +/** @file + The device path protocol as defined in UEFI 2.0. + + The device path represents a programatic path to a device. It's the view + from a software point of view. It also must persist from boot to boot, so + it can not contain things like PCI bus numbers that change from boot to boot. + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_DEVICE_PATH_PROTOCOL_H__ +#define __EFI_DEVICE_PATH_PROTOCOL_H__ + +#include + +/// +/// Device Path protocol +/// +#define EFI_DEVICE_PATH_PROTOCOL_GUID \ + { \ + 0x9576e91, 0x6d3f, 0x11d2, {0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \ + } + +// +// Protocol GUID defined in EFI1.1. +// + +/// +/// Device Path information +/// +#define DEVICE_PATH_PROTOCOL EFI_DEVICE_PATH_PROTOCOL_GUID + +#pragma pack(1) + +/** + This protocol can be used on any device handle to obtain generic path/location + information concerning the physical device or logical device. If the handle does + not logically map to a physical device, the handle may not necessarily support + the device path protocol. The device path describes the location of the device + the handle is for. The size of the Device Path can be determined from the structures + that make up the Device Path. +**/ +typedef struct { + UINT8 Type; ///< 0x01 Hardware Device Path + ///< 0x02 ACPI Device Path + ///< 0x03 Messaging Device Path + ///< 0x04 Media Device Path + ///< 0x05 BIOS Boot Specification Device Path + ///< 0x7F End of Hardware Device Path + + UINT8 SubType; ///< Varies by Type + ///< 0xFF End Entire Device Path, or + ///< 0x01 End This Instance of a Device Path and start a new + ///< Device Path + + UINT8 Length[2]; ///< Specific Device Path data. Type and Sub-Type define + ///< type of data. Size of data is included in Length. + +} EFI_DEVICE_PATH_PROTOCOL; + +/// +/// For backward-compatible with EFI1.1. +/// +typedef EFI_DEVICE_PATH_PROTOCOL EFI_DEVICE_PATH; + +/// +/// Hardware Device Paths +/// +#define HARDWARE_DEVICE_PATH 0x01 + +#define HW_PCI_DP 0x01 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT8 Function; + UINT8 Device; +} PCI_DEVICE_PATH; + +#define HW_PCCARD_DP 0x02 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT8 FunctionNumber; +} PCCARD_DEVICE_PATH; + +#define HW_MEMMAP_DP 0x03 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT32 MemoryType; + EFI_PHYSICAL_ADDRESS StartingAddress; + EFI_PHYSICAL_ADDRESS EndingAddress; +} MEMMAP_DEVICE_PATH; + +#define HW_VENDOR_DP 0x04 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + EFI_GUID Guid; +} VENDOR_DEVICE_PATH; + +#define HW_CONTROLLER_DP 0x05 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT32 ControllerNumber; +} CONTROLLER_DEVICE_PATH; + +/// +/// ACPI Device Paths +/// +#define ACPI_DEVICE_PATH 0x02 + +#define ACPI_DP 0x01 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT32 HID; + UINT32 UID; +} ACPI_HID_DEVICE_PATH; + +#define ACPI_EXTENDED_DP 0x02 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT32 HID; + UINT32 UID; + UINT32 CID; + /// + /// Optional variable length _HIDSTR + /// Optional variable length _UIDSTR + /// Optional variable length _CIDSTR + /// +} ACPI_EXTENDED_HID_DEVICE_PATH; + +// +// EISA ID Macro +// EISA ID Definition 32-bits +// bits[15:0] - three character compressed ASCII EISA ID. +// bits[31:16] - binary number +// Compressed ASCII is 5 bits per character 0b00001 = 'A' 0b11010 = 'Z' +// +#define PNP_EISA_ID_CONST 0x41d0 +#define EISA_ID(_Name, _Num) ((UINT32)((_Name) | (_Num) << 16)) +#define EISA_PNP_ID(_PNPId) (EISA_ID(PNP_EISA_ID_CONST, (_PNPId))) +#define EFI_PNP_ID(_PNPId) (EISA_ID(PNP_EISA_ID_CONST, (_PNPId))) + +#define PNP_EISA_ID_MASK 0xffff +#define EISA_ID_TO_NUM(_Id) ((_Id) >> 16) + + +#define ACPI_ADR_DP 0x03 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT32 ADR; +} ACPI_ADR_DEVICE_PATH; + +#define ACPI_ADR_DISPLAY_TYPE_OTHER 0 +#define ACPI_ADR_DISPLAY_TYPE_VGA 1 +#define ACPI_ADR_DISPLAY_TYPE_TV 2 +#define ACPI_ADR_DISPLAY_TYPE_EXTERNAL_DIGITAL 3 +#define ACPI_ADR_DISPLAY_TYPE_INTERNAL_DIGITAL 4 + +#define ACPI_DISPLAY_ADR(_DeviceIdScheme, _HeadId, _NonVgaOutput, _BiosCanDetect, _VendorInfo, _Type, _Port, _Index) \ + ((UINT32)( (((_DeviceIdScheme) & 0x1) << 31) | \ + (((_HeadId) & 0x7) << 18) | \ + (((_NonVgaOutput) & 0x1) << 17) | \ + (((_BiosCanDetect) & 0x1) << 16) | \ + (((_VendorInfo) & 0xf) << 12) | \ + (((_Type) & 0xf) << 8) | \ + (((_Port) & 0xf) << 4) | \ + ((_Index) & 0xf) )) + +/// +/// Messaging Device Paths +/// +#define MESSAGING_DEVICE_PATH 0x03 + +#define MSG_ATAPI_DP 0x01 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT8 PrimarySecondary; + UINT8 SlaveMaster; + UINT16 Lun; +} ATAPI_DEVICE_PATH; + +#define MSG_SCSI_DP 0x02 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT16 Pun; + UINT16 Lun; +} SCSI_DEVICE_PATH; + +#define MSG_FIBRECHANNEL_DP 0x03 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT32 Reserved; + UINT64 WWN; + UINT64 Lun; +} FIBRECHANNEL_DEVICE_PATH; + +#define MSG_1394_DP 0x04 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT32 Reserved; + UINT64 Guid; +} F1394_DEVICE_PATH; + +#define MSG_USB_DP 0x05 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT8 ParentPortNumber; + UINT8 InterfaceNumber; +} USB_DEVICE_PATH; + +#define MSG_USB_CLASS_DP 0x0f +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT16 VendorId; + UINT16 ProductId; + UINT8 DeviceClass; + UINT8 DeviceSubClass; + UINT8 DeviceProtocol; +} USB_CLASS_DEVICE_PATH; + +#define MSG_USB_WWID_DP 0x10 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT16 InterfaceNumber; + UINT16 VendorId; + UINT16 ProductId; + // CHAR16 SerialNumber[...]; +} USB_WWID_DEVICE_PATH; + + +#define MSG_DEVICE_LOGICAL_UNIT_DP 0x11 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT8 Lun; +} DEVICE_LOGICAL_UNIT_DEVICE_PATH; + +#define MSG_SATA_DP 0x12 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT16 HBAPortNumber; + UINT16 PortMultiplierPortNumber; + UINT16 Lun; +} SATA_DEVICE_PATH; + +#define MSG_I2O_DP 0x06 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT32 Tid; +} I2O_DEVICE_PATH; + +#define MSG_MAC_ADDR_DP 0x0b +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + EFI_MAC_ADDRESS MacAddress; + UINT8 IfType; +} MAC_ADDR_DEVICE_PATH; + +#define MSG_IPv4_DP 0x0c +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + EFI_IPv4_ADDRESS LocalIpAddress; + EFI_IPv4_ADDRESS RemoteIpAddress; + UINT16 LocalPort; + UINT16 RemotePort; + UINT16 Protocol; + BOOLEAN StaticIpAddress; +} IPv4_DEVICE_PATH; + +#define MSG_IPv6_DP 0x0d +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + EFI_IPv6_ADDRESS LocalIpAddress; + EFI_IPv6_ADDRESS RemoteIpAddress; + UINT16 LocalPort; + UINT16 RemotePort; + UINT16 Protocol; + BOOLEAN StaticIpAddress; +} IPv6_DEVICE_PATH; + +#define MSG_INFINIBAND_DP 0x09 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT32 ResourceFlags; + UINT8 PortGid[16]; + UINT64 ServiceId; + UINT64 TargetPortId; + UINT64 DeviceId; +} INFINIBAND_DEVICE_PATH; + +#define INFINIBAND_RESOURCE_FLAG_IOC_SERVICE 0x01 +#define INFINIBAND_RESOURCE_FLAG_EXTENDED_BOOT_ENVIRONMENT 0x02 +#define INFINIBAND_RESOURCE_FLAG_CONSOLE_PROTOCOL 0x04 +#define INFINIBAND_RESOURCE_FLAG_STORAGE_PROTOCOL 0x08 +#define INFINIBAND_RESOURCE_FLAG_NETWORK_PROTOCOL 0x10 + +#define MSG_UART_DP 0x0e +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT32 Reserved; + UINT64 BaudRate; + UINT8 DataBits; + UINT8 Parity; + UINT8 StopBits; +} UART_DEVICE_PATH; + +// +// Use VENDOR_DEVICE_PATH struct +// +#define MSG_VENDOR_DP 0x0a +typedef VENDOR_DEVICE_PATH VENDOR_DEFINED_DEVICE_PATH; + +#define DEVICE_PATH_MESSAGING_PC_ANSI EFI_PC_ANSI_GUID +#define DEVICE_PATH_MESSAGING_VT_100 EFI_VT_100_GUID +#define DEVICE_PATH_MESSAGING_VT_100_PLUS EFI_VT_100_PLUS_GUID +#define DEVICE_PATH_MESSAGING_VT_UTF8 EFI_VT_UTF8_GUID + +#define DEVICE_PATH_MESSAGING_UART_FLOW_CONTROL EFI_UART_DEVICE_PATH_GUID + +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + EFI_GUID Guid; + UINT32 FlowControlMap; +} UART_FLOW_CONTROL_DEVICE_PATH; + +#define DEVICE_PATH_MESSAGING_SAS EFI_SAS_DEVICE_PATH_GUID + +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + EFI_GUID Guid; + UINT32 Reserved; + UINT64 SasAddress; + UINT64 Lun; + UINT16 DeviceTopology; + UINT16 RelativeTargetPort; +} SAS_DEVICE_PATH; + +#define MSG_ISCSI_DP 0x13 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT16 NetworkProtocol; + UINT16 LoginOption; + UINT64 Lun; + UINT16 TargetPortalGroupTag; + // CHAR8 iSCSI Target Name +} ISCSI_DEVICE_PATH; + +#define ISCSI_LOGIN_OPTION_NO_HEADER_DIGEST 0x0000 +#define ISCSI_LOGIN_OPTION_HEADER_DIGEST_USING_CRC32C 0x0002 +#define ISCSI_LOGIN_OPTION_NO_DATA_DIGEST 0x0000 +#define ISCSI_LOGIN_OPTION_DATA_DIGEST_USING_CRC32C 0x0008 +#define ISCSI_LOGIN_OPTION_AUTHMETHOD_CHAP 0x0000 +#define ISCSI_LOGIN_OPTION_AUTHMETHOD_NON 0x1000 +#define ISCSI_LOGIN_OPTION_CHAP_BI 0x0000 +#define ISCSI_LOGIN_OPTION_CHAP_UNI 0x2000 + +// +// Media Device Path +// +#define MEDIA_DEVICE_PATH 0x04 + +#define MEDIA_HARDDRIVE_DP 0x01 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT32 PartitionNumber; + UINT64 PartitionStart; + UINT64 PartitionSize; + UINT8 Signature[16]; + UINT8 MBRType; + UINT8 SignatureType; +} HARDDRIVE_DEVICE_PATH; + +#define MBR_TYPE_PCAT 0x01 +#define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02 + +#define SIGNATURE_TYPE_MBR 0x01 +#define SIGNATURE_TYPE_GUID 0x02 + +#define MEDIA_CDROM_DP 0x02 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT32 BootEntry; + UINT64 PartitionStart; + UINT64 PartitionSize; +} CDROM_DEVICE_PATH; + +// +// Use VENDOR_DEVICE_PATH struct +// +#define MEDIA_VENDOR_DP 0x03 + +#define MEDIA_FILEPATH_DP 0x04 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + CHAR16 PathName[1]; +} FILEPATH_DEVICE_PATH; + +#define SIZE_OF_FILEPATH_DEVICE_PATH EFI_FIELD_OFFSET(FILEPATH_DEVICE_PATH,PathName) + +#define MEDIA_PROTOCOL_DP 0x05 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + EFI_GUID Protocol; +} MEDIA_PROTOCOL_DEVICE_PATH; + + +#define MEDIA_PIWG_FW_VOL_DP 0x7 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + EFI_GUID FvName; +} MEDIA_FW_VOL_DEVICE_PATH; + + +#define MEDIA_PIWG_FW_FILE_DP 0x6 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + EFI_GUID FvFileName; +} MEDIA_FW_VOL_FILEPATH_DEVICE_PATH; + +// +// BBS Device Path +// +#define BBS_DEVICE_PATH 0x05 +#define BBS_BBS_DP 0x01 +typedef struct { + EFI_DEVICE_PATH_PROTOCOL Header; + UINT16 DeviceType; + UINT16 StatusFlag; + CHAR8 String[1]; +} BBS_BBS_DEVICE_PATH; + +// +// DeviceType definitions - from BBS specification +// +#define BBS_TYPE_FLOPPY 0x01 +#define BBS_TYPE_HARDDRIVE 0x02 +#define BBS_TYPE_CDROM 0x03 +#define BBS_TYPE_PCMCIA 0x04 +#define BBS_TYPE_USB 0x05 +#define BBS_TYPE_EMBEDDED_NETWORK 0x06 +#define BBS_TYPE_BEV 0x80 +#define BBS_TYPE_UNKNOWN 0xFF + + +/// +/// Union of all possible Device Paths and pointers to Device Paths +/// + +typedef union { + EFI_DEVICE_PATH_PROTOCOL DevPath; + PCI_DEVICE_PATH Pci; + PCCARD_DEVICE_PATH PcCard; + MEMMAP_DEVICE_PATH MemMap; + VENDOR_DEVICE_PATH Vendor; + + CONTROLLER_DEVICE_PATH Controller; + ACPI_HID_DEVICE_PATH Acpi; + + ATAPI_DEVICE_PATH Atapi; + SCSI_DEVICE_PATH Scsi; + ISCSI_DEVICE_PATH Iscsi; + FIBRECHANNEL_DEVICE_PATH FibreChannel; + + F1394_DEVICE_PATH F1394; + USB_DEVICE_PATH Usb; + SATA_DEVICE_PATH Sata; + USB_CLASS_DEVICE_PATH UsbClass; + I2O_DEVICE_PATH I2O; + MAC_ADDR_DEVICE_PATH MacAddr; + IPv4_DEVICE_PATH Ipv4; + IPv6_DEVICE_PATH Ipv6; + INFINIBAND_DEVICE_PATH InfiniBand; + UART_DEVICE_PATH Uart; + + HARDDRIVE_DEVICE_PATH HardDrive; + CDROM_DEVICE_PATH CD; + + FILEPATH_DEVICE_PATH FilePath; + MEDIA_PROTOCOL_DEVICE_PATH MediaProtocol; + + BBS_BBS_DEVICE_PATH Bbs; +} EFI_DEV_PATH; + + + +typedef union { + EFI_DEVICE_PATH_PROTOCOL *DevPath; + PCI_DEVICE_PATH *Pci; + PCCARD_DEVICE_PATH *PcCard; + MEMMAP_DEVICE_PATH *MemMap; + VENDOR_DEVICE_PATH *Vendor; + + CONTROLLER_DEVICE_PATH *Controller; + ACPI_HID_DEVICE_PATH *Acpi; + ACPI_EXTENDED_HID_DEVICE_PATH *ExtendedAcpi; + + ATAPI_DEVICE_PATH *Atapi; + SCSI_DEVICE_PATH *Scsi; + FIBRECHANNEL_DEVICE_PATH *FibreChannel; + + F1394_DEVICE_PATH *F1394; + USB_DEVICE_PATH *Usb; + SATA_DEVICE_PATH *Sata; + USB_CLASS_DEVICE_PATH *UsbClass; + I2O_DEVICE_PATH *I2O; + MAC_ADDR_DEVICE_PATH *MacAddr; + IPv4_DEVICE_PATH *Ipv4; + IPv6_DEVICE_PATH *Ipv6; + INFINIBAND_DEVICE_PATH *InfiniBand; + UART_DEVICE_PATH *Uart; + + HARDDRIVE_DEVICE_PATH *HardDrive; + CDROM_DEVICE_PATH *CD; + + FILEPATH_DEVICE_PATH *FilePath; + MEDIA_PROTOCOL_DEVICE_PATH *MediaProtocol; + + BBS_BBS_DEVICE_PATH *Bbs; + UINT8 *Raw; +} EFI_DEV_PATH_PTR; + +#pragma pack() + +#define END_DEVICE_PATH_TYPE 0x7f +#define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xFF +#define END_INSTANCE_DEVICE_PATH_SUBTYPE 0x01 + +extern EFI_GUID gEfiDevicePathProtocolGuid; + +#endif diff --git a/gpxe/src/include/gpxe/efi/Protocol/DriverBinding.h b/gpxe/src/include/gpxe/efi/Protocol/DriverBinding.h new file mode 100644 index 0000000..510cb7c --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Protocol/DriverBinding.h @@ -0,0 +1,153 @@ +/** @file + UEFI DriverBinding Protocol is defined in UEFI specification. + + This protocol is produced by every driver that follows the UEFI Driver Model, + and it is the central component that allows drivers and controllers to be managed. + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_DRIVER_BINDING_H__ +#define __EFI_DRIVER_BINDING_H__ + +#include +/// +/// Global ID for the ControllerHandle Driver Protocol +/// +#define EFI_DRIVER_BINDING_PROTOCOL_GUID \ + { \ + 0x18a031ab, 0xb443, 0x4d1a, {0xa5, 0xc0, 0xc, 0x9, 0x26, 0x1e, 0x9f, 0x71 } \ + } + +typedef struct _EFI_DRIVER_BINDING_PROTOCOL EFI_DRIVER_BINDING_PROTOCOL; + +/** + Test to see if this driver supports ControllerHandle. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are a few calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported() it must also follow these calling restrictions. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device + @retval EFI_ALREADY_STARTED This driver is already running on this device + @retval other This driver does not support this device + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_DRIVER_BINDING_SUPPORTED)( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Start this driver on ControllerHandle. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_DRIVER_BINDING_START)( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stop this driver on ControllerHandle. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop() it must also follow these calling restrictions. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_DRIVER_BINDING_STOP)( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +/// +/// This protocol provides the services required to determine if a driver supports a given controller. +/// If a controller is supported, then it also provides routines to start and stop the controller. +/// +struct _EFI_DRIVER_BINDING_PROTOCOL { + EFI_DRIVER_BINDING_SUPPORTED Supported; + EFI_DRIVER_BINDING_START Start; + EFI_DRIVER_BINDING_STOP Stop; + + /// + /// The version number of the UEFI driver that produced the + /// EFI_DRIVER_BINDING_PROTOCOL. This field is used by + /// the EFI boot service ConnectController() to determine + /// the order that driver's Supported() service will be used when + /// a controller needs to be started. EFI Driver Binding Protocol + /// instances with higher Version values will be used before ones + /// with lower Version values. The Version values of 0x0- + /// 0x0f and 0xfffffff0-0xffffffff are reserved for + /// platform/OEM specific drivers. The Version values of 0x10- + /// 0xffffffef are reserved for IHV-developed drivers. + /// + UINT32 Version; + + /// + /// The image handle of the UEFI driver that produced this instance + /// of the EFI_DRIVER_BINDING_PROTOCOL. + /// + EFI_HANDLE ImageHandle; + + /// + /// The handle on which this instance of the + /// EFI_DRIVER_BINDING_PROTOCOL is installed. In most + /// cases, this is the same handle as ImageHandle. However, for + /// UEFI drivers that produce more than one instance of the + /// EFI_DRIVER_BINDING_PROTOCOL, this value may not be + /// the same as ImageHandle. + /// + EFI_HANDLE DriverBindingHandle; +}; + +extern EFI_GUID gEfiDriverBindingProtocolGuid; + +#endif diff --git a/gpxe/src/include/gpxe/efi/Protocol/NetworkInterfaceIdentifier.h b/gpxe/src/include/gpxe/efi/Protocol/NetworkInterfaceIdentifier.h new file mode 100644 index 0000000..e99ec38 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Protocol/NetworkInterfaceIdentifier.h @@ -0,0 +1,74 @@ +/** @file + EFI Network Interface Identifier Protocol + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + +**/ + +#ifndef __EFI_NETWORK_INTERFACE_IDENTIFER_H__ +#define __EFI_NETWORK_INTERFACE_IDENTIFER_H__ + + +#define EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_GUID \ + { \ + 0xE18541CD, 0xF755, 0x4f73, {0x92, 0x8D, 0x64, 0x3C, 0x8A, 0x79, 0xB2, 0x29 } \ + } + +#define EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION 0x00010000 + +/// +/// Revision defined in EFI1.1. +/// +#define EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE_REVISION EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION + +/// +/// Forward reference for pure ANSI compatability +/// +typedef struct _EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL; + +/// +/// Protocol defined in EFI1.1. +/// +typedef EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL EFI_NETWORK_INTERFACE_IDENTIFIER_INTERFACE; + +typedef enum { + EfiNetworkInterfaceUndi = 1 +} EFI_NETWORK_PROTOCOL_TYPE; + +/// +/// An optional protocol that is used to describe details about the software +/// layer that is used to produce the Simple Network Protocol. +/// +struct _EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL { + UINT64 Revision; ///< The revision of the EFI_NETWORK_INTERFACE_IDENTIFIER protocol. + UINT64 ID; ///< Address of the first byte of the identifying structure for this network + ///< interface. This is only valid when the network interface is started + ///< (see Start()). When the network interface is not started, this field is set to zero. + UINT64 ImageAddr; ///< Address of the first byte of the identifying structure for this + ///< network interface. This is set to zero if there is no structure. + UINT32 ImageSize; ///< Size of unrelocated network interface image. + CHAR8 StringId[4];///< A four-character ASCII string that is sent in the class identifier field of + ///< option 60 in DHCP. For a Type of EfiNetworkInterfaceUndi, this field is UNDI. + UINT8 Type; ///< Network interface type. This will be set to one of the values + ///< in EFI_NETWORK_INTERFACE_TYPE. + UINT8 MajorVer; ///< Major version number. + UINT8 MinorVer; ///< Minor version number. + BOOLEAN Ipv6Supported; ///< TRUE if the network interface supports IPv6; otherwise FALSE. + UINT8 IfNum; ///< The network interface number that is being identified by this Network + ///< Interface Identifier Protocol. This field must be less than or equal + ///< to the IFcnt field in the !PXE structure. + +}; + +extern EFI_GUID gEfiNetworkInterfaceIdentifierProtocolGuid; +extern EFI_GUID gEfiNetworkInterfaceIdentifierProtocolGuid_31; + +#endif // _EFI_NII_H diff --git a/gpxe/src/include/gpxe/efi/Protocol/PciIo.h b/gpxe/src/include/gpxe/efi/Protocol/PciIo.h new file mode 100644 index 0000000..5aea5b4 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Protocol/PciIo.h @@ -0,0 +1,519 @@ +/** @file + EFI PCI I/O Protocol provides the basic Memory, I/O, PCI configuration, + and DMA interfaces that a driver uses to access its PCI controller. + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __PCI_IO_H__ +#define __PCI_IO_H__ + +/// +/// Global ID for the PCI I/O Protocol +/// +#define EFI_PCI_IO_PROTOCOL_GUID \ + { \ + 0x4cf5b200, 0x68b8, 0x4ca5, {0x9e, 0xec, 0xb2, 0x3e, 0x3f, 0x50, 0x2, 0x9a } \ + } + +typedef struct _EFI_PCI_IO_PROTOCOL EFI_PCI_IO_PROTOCOL; + +/// +/// Prototypes for the PCI I/O Protocol +/// +typedef enum { + EfiPciIoWidthUint8 = 0, + EfiPciIoWidthUint16, + EfiPciIoWidthUint32, + EfiPciIoWidthUint64, + EfiPciIoWidthFifoUint8, + EfiPciIoWidthFifoUint16, + EfiPciIoWidthFifoUint32, + EfiPciIoWidthFifoUint64, + EfiPciIoWidthFillUint8, + EfiPciIoWidthFillUint16, + EfiPciIoWidthFillUint32, + EfiPciIoWidthFillUint64, + EfiPciIoWidthMaximum +} EFI_PCI_IO_PROTOCOL_WIDTH; + +// +// Complete PCI address generater +// +#define EFI_PCI_IO_PASS_THROUGH_BAR 0xff ///< Special BAR that passes a memory or I/O cycle through unchanged +#define EFI_PCI_IO_ATTRIBUTE_MASK 0x077f ///< All the following I/O and Memory cycles +#define EFI_PCI_IO_ATTRIBUTE_ISA_MOTHERBOARD_IO 0x0001 ///< I/O cycles 0x0000-0x00FF (10 bit decode) +#define EFI_PCI_IO_ATTRIBUTE_ISA_IO 0x0002 ///< I/O cycles 0x0100-0x03FF or greater (10 bit decode) +#define EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO 0x0004 ///< I/O cycles 0x3C6, 0x3C8, 0x3C9 (10 bit decode) +#define EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY 0x0008 ///< MEM cycles 0xA0000-0xBFFFF (24 bit decode) +#define EFI_PCI_IO_ATTRIBUTE_VGA_IO 0x0010 ///< I/O cycles 0x3B0-0x3BB and 0x3C0-0x3DF (10 bit decode) +#define EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO 0x0020 ///< I/O cycles 0x1F0-0x1F7, 0x3F6, 0x3F7 (10 bit decode) +#define EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO 0x0040 ///< I/O cycles 0x170-0x177, 0x376, 0x377 (10 bit decode) +#define EFI_PCI_IO_ATTRIBUTE_MEMORY_WRITE_COMBINE 0x0080 ///< Map a memory range so write are combined +#define EFI_PCI_IO_ATTRIBUTE_IO 0x0100 ///< Enable the I/O decode bit in the PCI Config Header +#define EFI_PCI_IO_ATTRIBUTE_MEMORY 0x0200 ///< Enable the Memory decode bit in the PCI Config Header +#define EFI_PCI_IO_ATTRIBUTE_BUS_MASTER 0x0400 ///< Enable the DMA bit in the PCI Config Header +#define EFI_PCI_IO_ATTRIBUTE_MEMORY_CACHED 0x0800 ///< Map a memory range so all r/w accesses are cached +#define EFI_PCI_IO_ATTRIBUTE_MEMORY_DISABLE 0x1000 ///< Disable a memory range +#define EFI_PCI_IO_ATTRIBUTE_EMBEDDED_DEVICE 0x2000 ///< Clear for an add-in PCI Device +#define EFI_PCI_IO_ATTRIBUTE_EMBEDDED_ROM 0x4000 ///< Clear for a physical PCI Option ROM accessed through ROM BAR +#define EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE 0x8000 ///< Clear for PCI controllers that can not genrate a DAC +#define EFI_PCI_IO_ATTRIBUTE_ISA_IO_16 0x10000 ///< I/O cycles 0x0100-0x03FF or greater (16 bit decode) +#define EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO_16 0x20000 ///< I/O cycles 0x3C6, 0x3C8, 0x3C9 (16 bit decode) +#define EFI_PCI_IO_ATTRIBUTE_VGA_IO_16 0x30000 ///< I/O cycles 0x3B0-0x3BB and 0x3C0-0x3DF (16 bit decode) + +#define EFI_PCI_DEVICE_ENABLE (EFI_PCI_IO_ATTRIBUTE_IO | EFI_PCI_IO_ATTRIBUTE_MEMORY | EFI_PCI_IO_ATTRIBUTE_BUS_MASTER) +#define EFI_VGA_DEVICE_ENABLE (EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO | EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY | EFI_PCI_IO_ATTRIBUTE_VGA_IO | EFI_PCI_IO_ATTRIBUTE_IO) + +/// +/// ******************************************************* +/// EFI_PCI_IO_PROTOCOL_OPERATION +/// ******************************************************* +/// +typedef enum { + EfiPciIoOperationBusMasterRead, + EfiPciIoOperationBusMasterWrite, + EfiPciIoOperationBusMasterCommonBuffer, + EfiPciIoOperationMaximum +} EFI_PCI_IO_PROTOCOL_OPERATION; + +/// +/// ******************************************************* +/// EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION +/// ******************************************************* +/// +typedef enum { + EfiPciIoAttributeOperationGet, + EfiPciIoAttributeOperationSet, + EfiPciIoAttributeOperationEnable, + EfiPciIoAttributeOperationDisable, + EfiPciIoAttributeOperationSupported, + EfiPciIoAttributeOperationMaximum +} EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION; + +/** + Reads from the memory space of a PCI controller. Returns when either the polling exit criteria is + satisfied or after a defined duration. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory or I/O operations. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for the memory operation to perform. + @param Offset The offset within the selected BAR to start the memory operation. + @param Mask Mask used for the polling criteria. + @param Value The comparison value used for the polling exit criteria. + @param Delay The number of 100 ns units to poll. + @param Result Pointer to the last value read from the memory location. + + @retval EFI_SUCCESS The last data returned from the access matched the poll exit criteria. + @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. + @retval EFI_UNSUPPORTED Offset is not valid for the BarIndex of this PCI controller. + @retval EFI_TIMEOUT Delay expired before a match occurred. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_IO_PROTOCOL_POLL_IO_MEM)( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT8 BarIndex, + IN UINT64 Offset, + IN UINT64 Mask, + IN UINT64 Value, + IN UINT64 Delay, + OUT UINT64 *Result + ); + +/** + Enable a PCI driver to access PCI controller registers in the PCI memory or I/O space. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory or I/O operations. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for the memory or I/O operation to perform. + @param Offset The offset within the selected BAR to start the memory or I/O operation. + @param Count The number of memory or I/O operations to perform. + @param Buffer For read operations, the destination buffer to store the results. For write + operations, the source buffer to write data from. + + @retval EFI_SUCCESS The data was read from or written to the PCI controller. + @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. + @retval EFI_UNSUPPORTED The address range specified by Offset, Width, and Count is not + valid for the PCI BAR specified by BarIndex. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_IO_PROTOCOL_IO_MEM)( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT8 BarIndex, + IN UINT64 Offset, + IN UINTN Count, + IN OUT VOID *Buffer + ); + +typedef struct { + EFI_PCI_IO_PROTOCOL_IO_MEM Read; + EFI_PCI_IO_PROTOCOL_IO_MEM Write; +} EFI_PCI_IO_PROTOCOL_ACCESS; + +/** + Enable a PCI driver to access PCI controller registers in PCI configuration space. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory operations. + @param Offset The offset within the PCI configuration space for the PCI controller. + @param Count The number of PCI configuration operations to perform. + @param Buffer For read operations, the destination buffer to store the results. For write + operations, the source buffer to write data from. + + + @retval EFI_SUCCESS The data was read from or written to the PCI controller. + @retval EFI_UNSUPPORTED The address range specified by Offset, Width, and Count is not + valid for the PCI configuration header of the PCI controller. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER Buffer is NULL or Width is invalid. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_IO_PROTOCOL_CONFIG)( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT32 Offset, + IN UINTN Count, + IN OUT VOID *Buffer + ); + +typedef struct { + EFI_PCI_IO_PROTOCOL_CONFIG Read; + EFI_PCI_IO_PROTOCOL_CONFIG Write; +} EFI_PCI_IO_PROTOCOL_CONFIG_ACCESS; + +/** + Enables a PCI driver to copy one region of PCI memory space to another region of PCI + memory space. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory operations. + @param DestBarIndex The BAR index in the standard PCI Configuration header to use as the + base address for the memory operation to perform. + @param DestOffset The destination offset within the BAR specified by DestBarIndex to + start the memory writes for the copy operation. + @param SrcBarIndex The BAR index in the standard PCI Configuration header to use as the + base address for the memory operation to perform. + @param SrcOffset The source offset within the BAR specified by SrcBarIndex to start + the memory reads for the copy operation. + @param Count The number of memory operations to perform. Bytes moved is Width + size * Count, starting at DestOffset and SrcOffset. + + @retval EFI_SUCCESS The data was copied from one memory region to another memory region. + @retval EFI_UNSUPPORTED DestBarIndex not valid for this PCI controller. + @retval EFI_UNSUPPORTED SrcBarIndex not valid for this PCI controller. + @retval EFI_UNSUPPORTED The address range specified by DestOffset, Width, and Count + is not valid for the PCI BAR specified by DestBarIndex. + @retval EFI_UNSUPPORTED The address range specified by SrcOffset, Width, and Count is + not valid for the PCI BAR specified by SrcBarIndex. + @retval EFI_INVALID_PARAMETER Width is invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_IO_PROTOCOL_COPY_MEM)( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT8 DestBarIndex, + IN UINT64 DestOffset, + IN UINT8 SrcBarIndex, + IN UINT64 SrcOffset, + IN UINTN Count + ); + +/** + Provides the PCI controller-Cspecific addresses needed to access system memory. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Operation Indicates if the bus master is going to read or write to system memory. + @param HostAddress The system memory address to map to the PCI controller. + @param NumberOfBytes On input the number of bytes to map. On output the number of bytes + that were mapped. + @param DeviceAddress The resulting map address for the bus master PCI controller to use to + access the hosts HostAddress. + @param Mapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes. + @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_DEVICE_ERROR The system hardware could not map the requested address. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_IO_PROTOCOL_MAP)( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_OPERATION Operation, + IN VOID *HostAddress, + IN OUT UINTN *NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ); + +/** + Completes the Map() operation and releases any corresponding resources. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The range was unmapped. + @retval EFI_DEVICE_ERROR The data was not committed to the target system memory. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_IO_PROTOCOL_UNMAP)( + IN EFI_PCI_IO_PROTOCOL *This, + IN VOID *Mapping + ); + +/** + Allocates pages that are suitable for an EfiPciIoOperationBusMasterCommonBuffer + mapping. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Type This parameter is not used and must be ignored. + @param MemoryType The type of memory to allocate, EfiBootServicesData or + EfiRuntimeServicesData. + @param Pages The number of pages to allocate. + @param HostAddress A pointer to store the base system memory address of the + allocated range. + @param Attributes The requested bit mask of attributes for the allocated range. + + @retval EFI_SUCCESS The requested memory pages were allocated. + @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are + MEMORY_WRITE_COMBINE and MEMORY_CACHED. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_IO_PROTOCOL_ALLOCATE_BUFFER)( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Pages, + OUT VOID **HostAddress, + IN UINT64 Attributes + ); + +/** + Frees memory that was allocated with AllocateBuffer(). + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Pages The number of pages to free. + @param HostAddress The base system memory address of the allocated range. + + @retval EFI_SUCCESS The requested memory pages were freed. + @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages + was not allocated with AllocateBuffer(). + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_IO_PROTOCOL_FREE_BUFFER)( + IN EFI_PCI_IO_PROTOCOL *This, + IN UINTN Pages, + IN VOID *HostAddress + ); + +/** + Flushes all PCI posted write transactions from a PCI host bridge to system memory. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + + @retval EFI_SUCCESS The PCI posted write transactions were flushed from the PCI host + bridge to system memory. + @retval EFI_DEVICE_ERROR The PCI posted write transactions were not flushed from the PCI + host bridge due to a hardware error. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_IO_PROTOCOL_FLUSH)( + IN EFI_PCI_IO_PROTOCOL *This + ); + +/** + Retrieves this PCI controller's current PCI bus number, device number, and function number. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param SegmentNumber The PCI controller's current PCI segment number. + @param BusNumber The PCI controller's current PCI bus number. + @param DeviceNumber The PCI controller's current PCI device number. + @param FunctionNumber The PCI controller's current PCI function number. + + @retval EFI_SUCCESS The PCI controller location was returned. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_IO_PROTOCOL_GET_LOCATION)( + IN EFI_PCI_IO_PROTOCOL *This, + OUT UINTN *SegmentNumber, + OUT UINTN *BusNumber, + OUT UINTN *DeviceNumber, + OUT UINTN *FunctionNumber + ); + +/** + Performs an operation on the attributes that this PCI controller supports. The operations include + getting the set of supported attributes, retrieving the current attributes, setting the current + attributes, enabling attributes, and disabling attributes. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Operation The operation to perform on the attributes for this PCI controller. + @param Attributes The mask of attributes that are used for Set, Enable, and Disable + operations. + @param Result A pointer to the result mask of attributes that are returned for the Get + and Supported operations. + + @retval EFI_SUCCESS The operation on the PCI controller's attributes was completed. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_UNSUPPORTED one or more of the bits set in + Attributes are not supported by this PCI controller or one of + its parent bridges when Operation is Set, Enable or Disable. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_IO_PROTOCOL_ATTRIBUTES)( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION Operation, + IN UINT64 Attributes, + OUT UINT64 *Result OPTIONAL + ); + +/** + Gets the attributes that this PCI controller supports setting on a BAR using + SetBarAttributes(), and retrieves the list of resource descriptors for a BAR. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for resource range. The legal range for this field is 0..5. + @param Supports A pointer to the mask of attributes that this PCI controller supports + setting for this BAR with SetBarAttributes(). + @param Resources A pointer to the ACPI 2.0 resource descriptors that describe the current + configuration of this BAR of the PCI controller. + + @retval EFI_SUCCESS If Supports is not NULL, then the attributes that the PCI + controller supports are returned in Supports. If Resources + is not NULL, then the ACPI 2.0 resource descriptors that the PCI + controller is currently using are returned in Resources. + @retval EFI_INVALID_PARAMETER Both Supports and Attributes are NULL. + @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to allocate + Resources. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_IO_PROTOCOL_GET_BAR_ATTRIBUTES)( + IN EFI_PCI_IO_PROTOCOL *This, + IN UINT8 BarIndex, + OUT UINT64 *Supports, OPTIONAL + OUT VOID **Resources OPTIONAL + ); + +/** + Sets the attributes for a range of a BAR on a PCI controller. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Attributes The mask of attributes to set for the resource range specified by + BarIndex, Offset, and Length. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for resource range. The legal range for this field is 0..5. + @param Offset A pointer to the BAR relative base address of the resource range to be + modified by the attributes specified by Attributes. + @param Length A pointer to the length of the resource range to be modified by the + attributes specified by Attributes. + + @retval EFI_SUCCESS The set of attributes specified by Attributes for the resource + range specified by BarIndex, Offset, and Length were + set on the PCI controller, and the actual resource range is returned + in Offset and Length. + @retval EFI_INVALID_PARAMETER Offset or Length is NULL. + @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the attributes on the + resource range specified by BarIndex, Offset, and + Length. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_IO_PROTOCOL_SET_BAR_ATTRIBUTES)( + IN EFI_PCI_IO_PROTOCOL *This, + IN UINT64 Attributes, + IN UINT8 BarIndex, + IN OUT UINT64 *Offset, + IN OUT UINT64 *Length + ); + +/// +/// The EFI_PCI_IO_PROTOCOL provides the basic Memory, I/O, PCI configuration, +/// and DMA interfaces that are used to abstract accesses to PCI controllers. +/// There is one EFI_PCI_IO_PROTOCOL instance for each PCI controller on a PCI bus. +/// A device driver that wishes to manage a PCI controller in a system will have to +/// retrieve the EFI_PCI_IO_PROTOCOL instance that is associated with the PCI controller. +/// +struct _EFI_PCI_IO_PROTOCOL { + EFI_PCI_IO_PROTOCOL_POLL_IO_MEM PollMem; + EFI_PCI_IO_PROTOCOL_POLL_IO_MEM PollIo; + EFI_PCI_IO_PROTOCOL_ACCESS Mem; + EFI_PCI_IO_PROTOCOL_ACCESS Io; + EFI_PCI_IO_PROTOCOL_CONFIG_ACCESS Pci; + EFI_PCI_IO_PROTOCOL_COPY_MEM CopyMem; + EFI_PCI_IO_PROTOCOL_MAP Map; + EFI_PCI_IO_PROTOCOL_UNMAP Unmap; + EFI_PCI_IO_PROTOCOL_ALLOCATE_BUFFER AllocateBuffer; + EFI_PCI_IO_PROTOCOL_FREE_BUFFER FreeBuffer; + EFI_PCI_IO_PROTOCOL_FLUSH Flush; + EFI_PCI_IO_PROTOCOL_GET_LOCATION GetLocation; + EFI_PCI_IO_PROTOCOL_ATTRIBUTES Attributes; + EFI_PCI_IO_PROTOCOL_GET_BAR_ATTRIBUTES GetBarAttributes; + EFI_PCI_IO_PROTOCOL_SET_BAR_ATTRIBUTES SetBarAttributes; + + /// + /// The size, in bytes, of the ROM image. + /// + UINT64 RomSize; + + /// + /// A pointer to the in memory copy of the ROM image. The PCI Bus Driver is responsible + /// for allocating memory for the ROM image, and copying the contents of the ROM to memory. + /// The contents of this buffer are either from the PCI option ROM that can be accessed + /// through the ROM BAR of the PCI controller, or it is from a platform-specific location. + /// The Attributes() function can be used to determine from which of these two sources + /// the RomImage buffer was initialized. + /// + VOID *RomImage; +}; + +extern EFI_GUID gEfiPciIoProtocolGuid; + +#endif diff --git a/gpxe/src/include/gpxe/efi/Protocol/PciRootBridgeIo.h b/gpxe/src/include/gpxe/efi/Protocol/PciRootBridgeIo.h new file mode 100644 index 0000000..a1150f0 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Protocol/PciRootBridgeIo.h @@ -0,0 +1,393 @@ +/** @file + PCI Root Bridge I/O protocol as defined in the UEFI 2.0 specification. + + PCI Root Bridge I/O protocol is used by PCI Bus Driver to perform PCI Memory, PCI I/O, + and PCI Configuration cycles on a PCI Root Bridge. It also provides services to perform + defferent types of bus mastering DMA + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __PCI_ROOT_BRIDGE_IO_H__ +#define __PCI_ROOT_BRIDGE_IO_H__ + +#define EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GUID \ + { \ + 0x2f707ebb, 0x4a1a, 0x11d4, {0x9a, 0x38, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \ + } + +typedef struct _EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL; + +typedef enum { + EfiPciWidthUint8, + EfiPciWidthUint16, + EfiPciWidthUint32, + EfiPciWidthUint64, + EfiPciWidthFifoUint8, + EfiPciWidthFifoUint16, + EfiPciWidthFifoUint32, + EfiPciWidthFifoUint64, + EfiPciWidthFillUint8, + EfiPciWidthFillUint16, + EfiPciWidthFillUint32, + EfiPciWidthFillUint64, + EfiPciWidthMaximum +} EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH; + +typedef enum { + EfiPciOperationBusMasterRead, + EfiPciOperationBusMasterWrite, + EfiPciOperationBusMasterCommonBuffer, + EfiPciOperationBusMasterRead64, + EfiPciOperationBusMasterWrite64, + EfiPciOperationBusMasterCommonBuffer64, + EfiPciOperationMaximum +} EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_OPERATION; + +#define EFI_PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO 0x0001 +#define EFI_PCI_ATTRIBUTE_ISA_IO 0x0002 +#define EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO 0x0004 +#define EFI_PCI_ATTRIBUTE_VGA_MEMORY 0x0008 +#define EFI_PCI_ATTRIBUTE_VGA_IO 0x0010 +#define EFI_PCI_ATTRIBUTE_IDE_PRIMARY_IO 0x0020 +#define EFI_PCI_ATTRIBUTE_IDE_SECONDARY_IO 0x0040 +#define EFI_PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE 0x0080 +#define EFI_PCI_ATTRIBUTE_MEMORY_CACHED 0x0800 +#define EFI_PCI_ATTRIBUTE_MEMORY_DISABLE 0x1000 +#define EFI_PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE 0x8000 + +#define EFI_PCI_ATTRIBUTE_VALID_FOR_ALLOCATE_BUFFER (EFI_PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE | EFI_PCI_ATTRIBUTE_MEMORY_CACHED | EFI_PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE) + +#define EFI_PCI_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER (~EFI_PCI_ATTRIBUTE_VALID_FOR_ALLOCATE_BUFFER) + +#define EFI_PCI_ADDRESS(bus, dev, func, reg) \ + ((UINT64) ((((UINTN) bus) << 24) + (((UINTN) dev) << 16) + (((UINTN) func) << 8) + ((UINTN) reg))) + +typedef struct { + UINT8 Register; + UINT8 Function; + UINT8 Device; + UINT8 Bus; + UINT32 ExtendedRegister; +} EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS; + +/** + Reads from the I/O space of a PCI Root Bridge. Returns when either the polling exit criteria is + satisfied or after a defined duration. + + @param This A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL. + @param Width Signifies the width of the memory or I/O operations. + @param Address The base address of the memory or I/O operations. + @param Mask Mask used for the polling criteria. + @param Value The comparison value used for the polling exit criteria. + @param Delay The number of 100 ns units to poll. + @param Result Pointer to the last value read from the memory location. + + @retval EFI_SUCCESS The last data returned from the access matched the poll exit criteria. + @retval EFI_TIMEOUT Delay expired before a match occurred. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_POLL_IO_MEM)( + IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *This, + IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH Width, + IN UINT64 Address, + IN UINT64 Mask, + IN UINT64 Value, + IN UINT64 Delay, + OUT UINT64 *Result + ); + +/** + Enables a PCI driver to access PCI controller registers in the PCI root bridge memory space. + + @param This A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL. + @param Width Signifies the width of the memory operations. + @param Address The base address of the memory operations. + @param Count The number of memory operations to perform. + @param Buffer For read operations, the destination buffer to store the results. For write + operations, the source buffer to write data from. + + @retval EFI_SUCCESS The data was read from or written to the PCI root bridge. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_IO_MEM)( + IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *This, + IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN OUT VOID *Buffer + ); + +typedef struct { + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_IO_MEM Read; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_IO_MEM Write; +} EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS; + +/** + Enables a PCI driver to copy one region of PCI root bridge memory space to another region of PCI + root bridge memory space. + + @param This A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL instance. + @param Width Signifies the width of the memory operations. + @param DestAddress The destination address of the memory operation. + @param SrcAddress The source address of the memory operation. + @param Count The number of memory operations to perform. + + @retval EFI_SUCCESS The data was copied from one memory region to another memory region. + @retval EFI_INVALID_PARAMETER Width is invalid for this PCI root bridge. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_COPY_MEM)( + IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *This, + IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH Width, + IN UINT64 DestAddress, + IN UINT64 SrcAddress, + IN UINTN Count + ); + +/** + Provides the PCI controller-Cspecific addresses required to access system memory from a + DMA bus master. + + @param This A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL. + @param Operation Indicates if the bus master is going to read or write to system memory. + @param HostAddress The system memory address to map to the PCI controller. + @param NumberOfBytes On input the number of bytes to map. On output the number of bytes + that were mapped. + @param DeviceAddress The resulting map address for the bus master PCI controller to use to + access the hosts HostAddress. + @param Mapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes. + @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_DEVICE_ERROR The system hardware could not map the requested address. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_MAP)( + IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *This, + IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_OPERATION Operation, + IN VOID *HostAddress, + IN OUT UINTN *NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ); + +/** + Completes the Map() operation and releases any corresponding resources. + + @param This A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL. + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The range was unmapped. + @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by Map(). + @retval EFI_DEVICE_ERROR The data was not committed to the target system memory. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_UNMAP)( + IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *This, + IN VOID *Mapping + ); + +/** + Allocates pages that are suitable for an EfiPciOperationBusMasterCommonBuffer or + EfiPciOperationBusMasterCommonBuffer64 mapping. + + @param This A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL. + @param Type This parameter is not used and must be ignored. + @param MemoryType The type of memory to allocate, EfiBootServicesData or + EfiRuntimeServicesData. + @param Pages The number of pages to allocate. + @param HostAddress A pointer to store the base system memory address of the + allocated range. + @param Attributes The requested bit mask of attributes for the allocated range. + + @retval EFI_SUCCESS The requested memory pages were allocated. + @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are + MEMORY_WRITE_COMBINE and MEMORY_CACHED. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ALLOCATE_BUFFER)( + IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *This, + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Pages, + IN OUT VOID **HostAddress, + IN UINT64 Attributes + ); + +/** + Frees memory that was allocated with AllocateBuffer(). + + @param This A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL. + @param Pages The number of pages to free. + @param HostAddress The base system memory address of the allocated range. + + @retval EFI_SUCCESS The requested memory pages were freed. + @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages + was not allocated with AllocateBuffer(). + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_FREE_BUFFER)( + IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *This, + IN UINTN Pages, + IN VOID *HostAddress + ); + +/** + Flushes all PCI posted write transactions from a PCI host bridge to system memory. + + @param This A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL. + + @retval EFI_SUCCESS The PCI posted write transactions were flushed from the PCI host + bridge to system memory. + @retval EFI_DEVICE_ERROR The PCI posted write transactions were not flushed from the PCI + host bridge due to a hardware error. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_FLUSH)( + IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *This + ); + +/** + Gets the attributes that a PCI root bridge supports setting with SetAttributes(), and the + attributes that a PCI root bridge is currently using. + + @param This A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL. + @param Supports A pointer to the mask of attributes that this PCI root bridge supports + setting with SetAttributes(). + @param Attributes A pointer to the mask of attributes that this PCI root bridge is currently + using. + + @retval EFI_SUCCESS If Supports is not NULL, then the attributes that the PCI root + bridge supports is returned in Supports. If Attributes is + not NULL, then the attributes that the PCI root bridge is currently + using is returned in Attributes. + @retval EFI_INVALID_PARAMETER Both Supports and Attributes are NULL. + + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GET_ATTRIBUTES)( + IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *This, + OUT UINT64 *Supports, + OUT UINT64 *Attributes + ); + +/** + Sets attributes for a resource range on a PCI root bridge. + + @param This A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL. + @param Attributes The mask of attributes to set. + @param ResourceBase A pointer to the base address of the resource range to be modified by the + attributes specified by Attributes. + @param ResourceLength A pointer to the length of the resource range to be modified by the + attributes specified by Attributes. + + @retval EFI_SUCCESS The set of attributes specified by Attributes for the resource + range specified by ResourceBase and ResourceLength + were set on the PCI root bridge, and the actual resource range is + returned in ResuourceBase and ResourceLength. + @retval EFI_UNSUPPORTED A bit is set in Attributes that is not supported by the PCI Root + Bridge. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the attributes on the + resource range specified by BaseAddress and Length. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_SET_ATTRIBUTES)( + IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *This, + IN UINT64 Attributes, + IN OUT UINT64 *ResourceBase, + IN OUT UINT64 *ResourceLength + ); + +/** + Retrieves the current resource settings of this PCI root bridge in the form of a set of ACPI 2.0 + resource descriptors. + + @param This A pointer to the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL. + @param Resources A pointer to the ACPI 2.0 resource descriptors that describe the current + configuration of this PCI root bridge. + + @retval EFI_SUCCESS The current configuration of this PCI root bridge was returned in + Resources. + @retval EFI_UNSUPPORTED The current configuration of this PCI root bridge could not be + retrieved. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_CONFIGURATION)( + IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *This, + OUT VOID **Resources + ); + +/// +/// Provides the basic Memory, I/O, PCI configuration, and DMA interfaces that are +/// used to abstract accesses to PCI controllers behind a PCI Root Bridge Controller. +/// +struct _EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL { + /// + /// The EFI_HANDLE of the PCI Host Bridge of which this PCI Root Bridge is a member. + /// + EFI_HANDLE ParentHandle; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_POLL_IO_MEM PollMem; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_POLL_IO_MEM PollIo; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS Mem; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS Io; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ACCESS Pci; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_COPY_MEM CopyMem; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_MAP Map; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_UNMAP Unmap; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_ALLOCATE_BUFFER AllocateBuffer; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_FREE_BUFFER FreeBuffer; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_FLUSH Flush; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GET_ATTRIBUTES GetAttributes; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_SET_ATTRIBUTES SetAttributes; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_CONFIGURATION Configuration; + + /// + /// The segment number that this PCI root bridge resides. + /// + UINT32 SegmentNumber; +}; + +extern EFI_GUID gEfiPciRootBridgeIoProtocolGuid; + +#endif diff --git a/gpxe/src/include/gpxe/efi/Protocol/SimpleNetwork.h b/gpxe/src/include/gpxe/efi/Protocol/SimpleNetwork.h new file mode 100644 index 0000000..d3d2afc --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Protocol/SimpleNetwork.h @@ -0,0 +1,593 @@ +/** @file + Simple Network protocol as defined in the UEFI 2.0 specification. + + Basic network device abstraction. + + Rx - Received + Tx - Transmit + MCast - MultiCast + ... + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __SIMPLE_NETWORK_H__ +#define __SIMPLE_NETWORK_H__ + +#define EFI_SIMPLE_NETWORK_PROTOCOL_GUID \ + { \ + 0xA19832B9, 0xAC25, 0x11D3, {0x9A, 0x2D, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D } \ + } + +typedef struct _EFI_SIMPLE_NETWORK_PROTOCOL EFI_SIMPLE_NETWORK_PROTOCOL; + + +/// +/// Protocol defined in EFI1.1. +/// +typedef EFI_SIMPLE_NETWORK_PROTOCOL EFI_SIMPLE_NETWORK; + +/// +/// Simple Network Protocol data structures +/// +typedef struct { + /// + /// Total number of frames received. Includes frames with errors and + /// dropped frames. + /// + UINT64 RxTotalFrames; + + /// + /// Number of valid frames received and copied into receive buffers. + /// + UINT64 RxGoodFrames; + + /// + /// Number of frames below the minimum length for the media. + /// This would be <64 for ethernet. + /// + UINT64 RxUndersizeFrames; + + /// + /// Number of frames longer than the maxminum length for the + /// media. This would be >1500 for ethernet. + /// + UINT64 RxOversizeFrames; + + /// + /// Valid frames that were dropped because receive buffers were full. + /// + UINT64 RxDroppedFrames; + + /// + /// Number of valid unicast frames received and not dropped. + /// + UINT64 RxUnicastFrames; + + /// + /// Number of valid broadcast frames received and not dropped. + /// + UINT64 RxBroadcastFrames; + + /// + /// Number of valid mutlicast frames received and not dropped. + /// + UINT64 RxMulticastFrames; + + /// + /// Number of frames w/ CRC or alignment errors. + /// + UINT64 RxCrcErrorFrames; + + /// + /// Total number of bytes received. Includes frames with errors + /// and dropped frames. + // + UINT64 RxTotalBytes; + + /// + /// Transmit statistics. + /// + UINT64 TxTotalFrames; + UINT64 TxGoodFrames; + UINT64 TxUndersizeFrames; + UINT64 TxOversizeFrames; + UINT64 TxDroppedFrames; + UINT64 TxUnicastFrames; + UINT64 TxBroadcastFrames; + UINT64 TxMulticastFrames; + UINT64 TxCrcErrorFrames; + UINT64 TxTotalBytes; + + /// + /// Number of collisions detection on this subnet. + /// + UINT64 Collisions; + + /// + /// Number of frames destined for unsupported protocol. + /// + UINT64 UnsupportedProtocol; + +} EFI_NETWORK_STATISTICS; + +typedef enum { + EfiSimpleNetworkStopped, + EfiSimpleNetworkStarted, + EfiSimpleNetworkInitialized, + EfiSimpleNetworkMaxState +} EFI_SIMPLE_NETWORK_STATE; + +#define EFI_SIMPLE_NETWORK_RECEIVE_UNICAST 0x01 +#define EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST 0x02 +#define EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST 0x04 +#define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS 0x08 +#define EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST 0x10 + +#define EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT 0x01 +#define EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT 0x02 +#define EFI_SIMPLE_NETWORK_COMMAND_INTERRUPT 0x04 +#define EFI_SIMPLE_NETWORK_SOFTWARE_INTERRUPT 0x08 + +#define MAX_MCAST_FILTER_CNT 16 +typedef struct { + UINT32 State; + UINT32 HwAddressSize; + UINT32 MediaHeaderSize; + UINT32 MaxPacketSize; + UINT32 NvRamSize; + UINT32 NvRamAccessSize; + UINT32 ReceiveFilterMask; + UINT32 ReceiveFilterSetting; + UINT32 MaxMCastFilterCount; + UINT32 MCastFilterCount; + EFI_MAC_ADDRESS MCastFilter[MAX_MCAST_FILTER_CNT]; + EFI_MAC_ADDRESS CurrentAddress; + EFI_MAC_ADDRESS BroadcastAddress; + EFI_MAC_ADDRESS PermanentAddress; + UINT8 IfType; + BOOLEAN MacAddressChangeable; + BOOLEAN MultipleTxSupported; + BOOLEAN MediaPresentSupported; + BOOLEAN MediaPresent; +} EFI_SIMPLE_NETWORK_MODE; + +// +// Protocol Member Functions +// +/** + Changes the state of a network interface from "stopped" to "started". + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The network interface was started. + @retval EFI_ALREADY_STARTED The network interface is already in the started state. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SIMPLE_NETWORK_START)( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ); + +/** + Changes the state of a network interface from "started" to "stopped". + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The network interface was stopped. + @retval EFI_ALREADY_STARTED The network interface is already in the stopped state. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SIMPLE_NETWORK_STOP)( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ); + +/** + Resets a network adapter and allocates the transmit and receive buffers + required by the network interface; optionally, also requests allocation + of additional transmit and receive buffers. + + @param This Protocol instance pointer. + @param ExtraRxBufferSize The size, in bytes, of the extra receive buffer space + that the driver should allocate for the network interface. + Some network interfaces will not be able to use the extra + buffer, and the caller will not know if it is actually + being used. + @param ExtraTxBufferSize The size, in bytes, of the extra transmit buffer space + that the driver should allocate for the network interface. + Some network interfaces will not be able to use the extra + buffer, and the caller will not know if it is actually + being used. + + @retval EFI_SUCCESS The network interface was initialized. + @retval EFI_NOT_STARTED The network interface has not been started + @retval EFI_OUT_OF_RESOURCES There was not enough memory for the transmit and + receive buffers. . + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SIMPLE_NETWORK_INITIALIZE)( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN UINTN ExtraRxBufferSize OPTIONAL, + IN UINTN ExtraTxBufferSize OPTIONAL + ); + +/** + Resets a network adapter and re-initializes it with the parameters that were + provided in the previous call to Initialize(). + + @param This Protocol instance pointer. + @param ExtendedVerification Indicates that the driver may perform a more + exhaustive verification operation of the device + during reset. + + @retval EFI_SUCCESS The network interface was reset. + @retval EFI_NOT_STARTED The network interface has not been started + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SIMPLE_NETWORK_RESET)( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Resets a network adapter and leaves it in a state that is safe for + another driver to initialize. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The network interface was shutdown. + @retval EFI_NOT_STARTED The network interface has not been started + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SIMPLE_NETWORK_SHUTDOWN)( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This + ); + +/** + Manages the multicast receive filters of a network interface. + + @param This Protocol instance pointer. + @param Enable A bit mask of receive filters to enable on the network interface. + @param Disable A bit mask of receive filters to disable on the network interface. + @param ResetMCastFilter Set to TRUE to reset the contents of the multicast receive + filters on the network interface to their default values. + @param McastFilterCnt Number of multicast HW MAC addresses in the new + MCastFilter list. This value must be less than or equal to + the MCastFilterCnt field of EFI_SIMPLE_NETWORK_MODE. This + field is optional if ResetMCastFilter is TRUE. + @param MCastFilter A pointer to a list of new multicast receive filter HW MAC + addresses. This list will replace any existing multicast + HW MAC address list. This field is optional if + ResetMCastFilter is TRUE. + + @retval EFI_SUCCESS The multicast receive filter list was updated. + @retval EFI_NOT_STARTED The network interface has not been started + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SIMPLE_NETWORK_RECEIVE_FILTERS)( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN UINT32 Enable, + IN UINT32 Disable, + IN BOOLEAN ResetMCastFilter, + IN UINTN MCastFilterCnt OPTIONAL, + IN EFI_MAC_ADDRESS *MCastFilter OPTIONAL + ); + +/** + Modifies or resets the current station address, if supported. + + @param This Protocol instance pointer. + @param Reset Flag used to reset the station address to the network interfaces + permanent address. + @param New New station address to be used for the network interface. + + @retval EFI_SUCCESS The network interfaces station address was updated. + @retval EFI_NOT_STARTED The network interface has not been started + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SIMPLE_NETWORK_STATION_ADDRESS)( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN BOOLEAN Reset, + IN EFI_MAC_ADDRESS *New OPTIONAL + ); + +/** + Resets or collects the statistics on a network interface. + + @param This Protocol instance pointer. + @param Reset Set to TRUE to reset the statistics for the network interface. + @param StatisticsSize On input the size, in bytes, of StatisticsTable. On + output the size, in bytes, of the resulting table of + statistics. + @param StatisticsTable A pointer to the EFI_NETWORK_STATISTICS structure that + contains the statistics. + + @retval EFI_SUCCESS The statistics were collected from the network interface. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_BUFFER_TOO_SMALL The Statistics buffer was too small. The current buffer + size needed to hold the statistics is returned in + StatisticsSize. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SIMPLE_NETWORK_STATISTICS)( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN BOOLEAN Reset, + IN OUT UINTN *StatisticsSize OPTIONAL, + OUT EFI_NETWORK_STATISTICS *StatisticsTable OPTIONAL + ); + +/** + Converts a multicast IP address to a multicast HW MAC address. + + @param This Protocol instance pointer. + @param IPv6 Set to TRUE if the multicast IP address is IPv6 [RFC 2460]. Set + to FALSE if the multicast IP address is IPv4 [RFC 791]. + @param IP The multicast IP address that is to be converted to a multicast + HW MAC address. + @param MAC The multicast HW MAC address that is to be generated from IP. + + @retval EFI_SUCCESS The multicast IP address was mapped to the multicast + HW MAC address. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_BUFFER_TOO_SMALL The Statistics buffer was too small. The current buffer + size needed to hold the statistics is returned in + StatisticsSize. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SIMPLE_NETWORK_MCAST_IP_TO_MAC)( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN BOOLEAN IPv6, + IN EFI_IP_ADDRESS *IP, + OUT EFI_MAC_ADDRESS *MAC + ); + +/** + Performs read and write operations on the NVRAM device attached to a + network interface. + + @param This Protocol instance pointer. + @param ReadWrite TRUE for read operations, FALSE for write operations. + @param Offset Byte offset in the NVRAM device at which to start the read or + write operation. This must be a multiple of NvRamAccessSize and + less than NvRamSize. + @param BufferSize The number of bytes to read or write from the NVRAM device. + This must also be a multiple of NvramAccessSize. + @param Buffer A pointer to the data buffer. + + @retval EFI_SUCCESS The NVRAM access was performed. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SIMPLE_NETWORK_NVDATA)( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN BOOLEAN ReadWrite, + IN UINTN Offset, + IN UINTN BufferSize, + IN OUT VOID *Buffer + ); + +/** + Reads the current interrupt status and recycled transmit buffer status from + a network interface. + + @param This Protocol instance pointer. + @param InterruptStatus A pointer to the bit mask of the currently active interrupts + If this is NULL, the interrupt status will not be read from + the device. If this is not NULL, the interrupt status will + be read from the device. When the interrupt status is read, + it will also be cleared. Clearing the transmit interrupt + does not empty the recycled transmit buffer array. + @param TxBuf Recycled transmit buffer address. The network interface will + not transmit if its internal recycled transmit buffer array + is full. Reading the transmit buffer does not clear the + transmit interrupt. If this is NULL, then the transmit buffer + status will not be read. If there are no transmit buffers to + recycle and TxBuf is not NULL, * TxBuf will be set to NULL. + + @retval EFI_SUCCESS The status of the network interface was retrieved. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SIMPLE_NETWORK_GET_STATUS)( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + OUT UINT32 *InterruptStatus OPTIONAL, + OUT VOID **TxBuf OPTIONAL + ); + +/** + Places a packet in the transmit queue of a network interface. + + @param This Protocol instance pointer. + @param HeaderSize The size, in bytes, of the media header to be filled in by + the Transmit() function. If HeaderSize is non-zero, then it + must be equal to This->Mode->MediaHeaderSize and the DestAddr + and Protocol parameters must not be NULL. + @param BufferSize The size, in bytes, of the entire packet (media header and + data) to be transmitted through the network interface. + @param Buffer A pointer to the packet (media header followed by data) to be + transmitted. This parameter cannot be NULL. If HeaderSize is zero, + then the media header in Buffer must already be filled in by the + caller. If HeaderSize is non-zero, then the media header will be + filled in by the Transmit() function. + @param SrcAddr The source HW MAC address. If HeaderSize is zero, then this parameter + is ignored. If HeaderSize is non-zero and SrcAddr is NULL, then + This->Mode->CurrentAddress is used for the source HW MAC address. + @param DsetAddr The destination HW MAC address. If HeaderSize is zero, then this + parameter is ignored. + @param Protocol The type of header to build. If HeaderSize is zero, then this + parameter is ignored. See RFC 1700, section "Ether Types", for + examples. + + @retval EFI_SUCCESS The packet was placed on the transmit queue. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_NOT_READY The network interface is too busy to accept this transmit request. + @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SIMPLE_NETWORK_TRANSMIT)( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + IN UINTN HeaderSize, + IN UINTN BufferSize, + IN VOID *Buffer, + IN EFI_MAC_ADDRESS *SrcAddr OPTIONAL, + IN EFI_MAC_ADDRESS *DestAddr OPTIONAL, + IN UINT16 *Protocol OPTIONAL + ); + +/** + Receives a packet from a network interface. + + @param This Protocol instance pointer. + @param HeaderSize The size, in bytes, of the media header received on the network + interface. If this parameter is NULL, then the media header size + will not be returned. + @param BufferSize On entry, the size, in bytes, of Buffer. On exit, the size, in + bytes, of the packet that was received on the network interface. + @param Buffer A pointer to the data buffer to receive both the media header and + the data. + @param SrcAddr The source HW MAC address. If this parameter is NULL, the + HW MAC source address will not be extracted from the media + header. + @param DsetAddr The destination HW MAC address. If this parameter is NULL, + the HW MAC destination address will not be extracted from the + media header. + @param Protocol The media header type. If this parameter is NULL, then the + protocol will not be extracted from the media header. See + RFC 1700 section "Ether Types" for examples. + + @retval EFI_SUCCESS The received data was stored in Buffer, and BufferSize has + been updated to the number of bytes received. + @retval EFI_NOT_STARTED The network interface has not been started. + @retval EFI_NOT_READY The network interface is too busy to accept this transmit + request. + @retval EFI_BUFFER_TOO_SMALL The BufferSize parameter is too small. + @retval EFI_INVALID_PARAMETER One or more of the parameters has an unsupported value. + @retval EFI_DEVICE_ERROR The command could not be sent to the network interface. + @retval EFI_UNSUPPORTED This function is not supported by the network interface. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SIMPLE_NETWORK_RECEIVE)( + IN EFI_SIMPLE_NETWORK_PROTOCOL *This, + OUT UINTN *HeaderSize OPTIONAL, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer, + OUT EFI_MAC_ADDRESS *SrcAddr OPTIONAL, + OUT EFI_MAC_ADDRESS *DestAddr OPTIONAL, + OUT UINT16 *Protocol OPTIONAL + ); + +#define EFI_SIMPLE_NETWORK_PROTOCOL_REVISION 0x00010000 + +// +// Revision defined in EFI1.1 +// +#define EFI_SIMPLE_NETWORK_INTERFACE_REVISION EFI_SIMPLE_NETWORK_PROTOCOL_REVISION + +/// +/// The EFI_SIMPLE_NETWORK_PROTOCOL protocol is used to initialize access +/// to a network adapter. Once the network adapter initializes, +/// the EFI_SIMPLE_NETWORK_PROTOCOL protocol provides services that +/// allow packets to be transmitted and received. +/// +struct _EFI_SIMPLE_NETWORK_PROTOCOL { + /// + /// Revision of the EFI_SIMPLE_NETWORK_PROTOCOL. All future revisions must + /// be backwards compatible. If a future version is not backwards compatible + /// it is not the same GUID. + /// + UINT64 Revision; + EFI_SIMPLE_NETWORK_START Start; + EFI_SIMPLE_NETWORK_STOP Stop; + EFI_SIMPLE_NETWORK_INITIALIZE Initialize; + EFI_SIMPLE_NETWORK_RESET Reset; + EFI_SIMPLE_NETWORK_SHUTDOWN Shutdown; + EFI_SIMPLE_NETWORK_RECEIVE_FILTERS ReceiveFilters; + EFI_SIMPLE_NETWORK_STATION_ADDRESS StationAddress; + EFI_SIMPLE_NETWORK_STATISTICS Statistics; + EFI_SIMPLE_NETWORK_MCAST_IP_TO_MAC MCastIpToMac; + EFI_SIMPLE_NETWORK_NVDATA NvData; + EFI_SIMPLE_NETWORK_GET_STATUS GetStatus; + EFI_SIMPLE_NETWORK_TRANSMIT Transmit; + EFI_SIMPLE_NETWORK_RECEIVE Receive; + /// + /// Event used with WaitForEvent() to wait for a packet to be received. + /// + EFI_EVENT WaitForPacket; + /// + /// Pointer to the EFI_SIMPLE_NETWORK_MODE data for the device. + /// + EFI_SIMPLE_NETWORK_MODE *Mode; +}; + +extern EFI_GUID gEfiSimpleNetworkProtocolGuid; + +#endif diff --git a/gpxe/src/include/gpxe/efi/Protocol/SimpleTextIn.h b/gpxe/src/include/gpxe/efi/Protocol/SimpleTextIn.h new file mode 100644 index 0000000..f14f62d --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Protocol/SimpleTextIn.h @@ -0,0 +1,136 @@ +/** @file + Simple Text In protocol from the UEFI 2.0 specification. + + Abstraction of a very simple input device like a keyboard or serial + terminal. + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __SIMPLE_TEXT_IN_PROTOCOL_H__ +#define __SIMPLE_TEXT_IN_PROTOCOL_H__ + +#include + +#define EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID \ + { \ + 0x387477c1, 0x69c7, 0x11d2, {0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \ + } + +/// +/// Protocol GUID defined in EFI1.1. +/// +#define SIMPLE_INPUT_PROTOCOL EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID + +typedef struct _EFI_SIMPLE_TEXT_INPUT_PROTOCOL EFI_SIMPLE_TEXT_INPUT_PROTOCOL; + +/// +/// Backward-compatible with EFI1.1. +/// +typedef struct _EFI_SIMPLE_TEXT_INPUT_PROTOCOL SIMPLE_INPUT_INTERFACE; +// +// Data structures +// +typedef struct { + UINT16 ScanCode; + CHAR16 UnicodeChar; +} EFI_INPUT_KEY; + +// +// Required unicode control chars +// +#define CHAR_NULL 0x0000 +#define CHAR_BACKSPACE 0x0008 +#define CHAR_TAB 0x0009 +#define CHAR_LINEFEED 0x000A +#define CHAR_CARRIAGE_RETURN 0x000D + +// +// EFI Scan codes +// +#define SCAN_NULL 0x0000 +#define SCAN_UP 0x0001 +#define SCAN_DOWN 0x0002 +#define SCAN_RIGHT 0x0003 +#define SCAN_LEFT 0x0004 +#define SCAN_HOME 0x0005 +#define SCAN_END 0x0006 +#define SCAN_INSERT 0x0007 +#define SCAN_DELETE 0x0008 +#define SCAN_PAGE_UP 0x0009 +#define SCAN_PAGE_DOWN 0x000A +#define SCAN_F1 0x000B +#define SCAN_F2 0x000C +#define SCAN_F3 0x000D +#define SCAN_F4 0x000E +#define SCAN_F5 0x000F +#define SCAN_F6 0x0010 +#define SCAN_F7 0x0011 +#define SCAN_F8 0x0012 +#define SCAN_F9 0x0013 +#define SCAN_F10 0x0014 +#define SCAN_F11 0x0015 +#define SCAN_F12 0x0016 +#define SCAN_ESC 0x0017 + +/** + Reset the input device and optionaly run diagnostics + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The device was reset. + @retval EFI_DEVICE_ERROR The device is not functioning properly and could not be reset. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_INPUT_RESET)( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Reads the next keystroke from the input device. The WaitForKey Event can + be used to test for existance of a keystroke via WaitForEvent () call. + + @param This Protocol instance pointer. + @param Key Driver may perform diagnostics on reset. + + @retval EFI_SUCCESS The keystroke information was returned. + @retval EFI_NOT_READY There was no keystroke data availiable. + @retval EFI_DEVICE_ERROR The keydtroke information was not returned due to + hardware errors. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_INPUT_READ_KEY)( + IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, + OUT EFI_INPUT_KEY *Key + ); + +/// +/// The EFI_SIMPLE_TEXT_INPUT_PROTOCOL is used on the ConsoleIn device. +/// It is the minimum required protocol for ConsoleIn. +/// +struct _EFI_SIMPLE_TEXT_INPUT_PROTOCOL { + EFI_INPUT_RESET Reset; + EFI_INPUT_READ_KEY ReadKeyStroke; + /// + /// Event to use with WaitForEvent() to wait for a key to be available + /// + EFI_EVENT WaitForKey; +}; + +extern EFI_GUID gEfiSimpleTextInProtocolGuid; + +#endif diff --git a/gpxe/src/include/gpxe/efi/Protocol/SimpleTextOut.h b/gpxe/src/include/gpxe/efi/Protocol/SimpleTextOut.h new file mode 100644 index 0000000..426ce81 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Protocol/SimpleTextOut.h @@ -0,0 +1,404 @@ +/** @file + Simple Text Out protocol from the UEFI 2.0 specification. + + Abstraction of a very simple text based output device like VGA text mode or + a serial terminal. The Simple Text Out protocol instance can represent + a single hardware device or a virtual device that is an agregation + of multiple physical devices. + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __SIMPLE_TEXT_OUT_H__ +#define __SIMPLE_TEXT_OUT_H__ + +#define EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID \ + { \ + 0x387477c2, 0x69c7, 0x11d2, {0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \ + } + +/// +/// Protocol GUID defined in EFI1.1. +/// +#define SIMPLE_TEXT_OUTPUT_PROTOCOL EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID + +typedef struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL; + +/// +/// Backward-compatible with EFI1.1. +/// +typedef EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL SIMPLE_TEXT_OUTPUT_INTERFACE; + +// +// Define's for required EFI Unicode Box Draw characters +// +#define BOXDRAW_HORIZONTAL 0x2500 +#define BOXDRAW_VERTICAL 0x2502 +#define BOXDRAW_DOWN_RIGHT 0x250c +#define BOXDRAW_DOWN_LEFT 0x2510 +#define BOXDRAW_UP_RIGHT 0x2514 +#define BOXDRAW_UP_LEFT 0x2518 +#define BOXDRAW_VERTICAL_RIGHT 0x251c +#define BOXDRAW_VERTICAL_LEFT 0x2524 +#define BOXDRAW_DOWN_HORIZONTAL 0x252c +#define BOXDRAW_UP_HORIZONTAL 0x2534 +#define BOXDRAW_VERTICAL_HORIZONTAL 0x253c +#define BOXDRAW_DOUBLE_HORIZONTAL 0x2550 +#define BOXDRAW_DOUBLE_VERTICAL 0x2551 +#define BOXDRAW_DOWN_RIGHT_DOUBLE 0x2552 +#define BOXDRAW_DOWN_DOUBLE_RIGHT 0x2553 +#define BOXDRAW_DOUBLE_DOWN_RIGHT 0x2554 +#define BOXDRAW_DOWN_LEFT_DOUBLE 0x2555 +#define BOXDRAW_DOWN_DOUBLE_LEFT 0x2556 +#define BOXDRAW_DOUBLE_DOWN_LEFT 0x2557 +#define BOXDRAW_UP_RIGHT_DOUBLE 0x2558 +#define BOXDRAW_UP_DOUBLE_RIGHT 0x2559 +#define BOXDRAW_DOUBLE_UP_RIGHT 0x255a +#define BOXDRAW_UP_LEFT_DOUBLE 0x255b +#define BOXDRAW_UP_DOUBLE_LEFT 0x255c +#define BOXDRAW_DOUBLE_UP_LEFT 0x255d +#define BOXDRAW_VERTICAL_RIGHT_DOUBLE 0x255e +#define BOXDRAW_VERTICAL_DOUBLE_RIGHT 0x255f +#define BOXDRAW_DOUBLE_VERTICAL_RIGHT 0x2560 +#define BOXDRAW_VERTICAL_LEFT_DOUBLE 0x2561 +#define BOXDRAW_VERTICAL_DOUBLE_LEFT 0x2562 +#define BOXDRAW_DOUBLE_VERTICAL_LEFT 0x2563 +#define BOXDRAW_DOWN_HORIZONTAL_DOUBLE 0x2564 +#define BOXDRAW_DOWN_DOUBLE_HORIZONTAL 0x2565 +#define BOXDRAW_DOUBLE_DOWN_HORIZONTAL 0x2566 +#define BOXDRAW_UP_HORIZONTAL_DOUBLE 0x2567 +#define BOXDRAW_UP_DOUBLE_HORIZONTAL 0x2568 +#define BOXDRAW_DOUBLE_UP_HORIZONTAL 0x2569 +#define BOXDRAW_VERTICAL_HORIZONTAL_DOUBLE 0x256a +#define BOXDRAW_VERTICAL_DOUBLE_HORIZONTAL 0x256b +#define BOXDRAW_DOUBLE_VERTICAL_HORIZONTAL 0x256c + +// +// EFI Required Block Elements Code Chart +// +#define BLOCKELEMENT_FULL_BLOCK 0x2588 +#define BLOCKELEMENT_LIGHT_SHADE 0x2591 + +// +// EFI Required Geometric Shapes Code Chart +// +#define GEOMETRICSHAPE_UP_TRIANGLE 0x25b2 +#define GEOMETRICSHAPE_RIGHT_TRIANGLE 0x25ba +#define GEOMETRICSHAPE_DOWN_TRIANGLE 0x25bc +#define GEOMETRICSHAPE_LEFT_TRIANGLE 0x25c4 + +// +// EFI Required Arrow shapes +// +#define ARROW_LEFT 0x2190 +#define ARROW_UP 0x2191 +#define ARROW_RIGHT 0x2192 +#define ARROW_DOWN 0x2193 + +// +// EFI Console Colours +// +#define EFI_BLACK 0x00 +#define EFI_BLUE 0x01 +#define EFI_GREEN 0x02 +#define EFI_CYAN (EFI_BLUE | EFI_GREEN) +#define EFI_RED 0x04 +#define EFI_MAGENTA (EFI_BLUE | EFI_RED) +#define EFI_BROWN (EFI_GREEN | EFI_RED) +#define EFI_LIGHTGRAY (EFI_BLUE | EFI_GREEN | EFI_RED) +#define EFI_BRIGHT 0x08 +#define EFI_DARKGRAY (EFI_BRIGHT) +#define EFI_LIGHTBLUE (EFI_BLUE | EFI_BRIGHT) +#define EFI_LIGHTGREEN (EFI_GREEN | EFI_BRIGHT) +#define EFI_LIGHTCYAN (EFI_CYAN | EFI_BRIGHT) +#define EFI_LIGHTRED (EFI_RED | EFI_BRIGHT) +#define EFI_LIGHTMAGENTA (EFI_MAGENTA | EFI_BRIGHT) +#define EFI_YELLOW (EFI_BROWN | EFI_BRIGHT) +#define EFI_WHITE (EFI_BLUE | EFI_GREEN | EFI_RED | EFI_BRIGHT) + +#define EFI_TEXT_ATTR(f, b) ((f) | ((b) << 4)) + +#define EFI_BACKGROUND_BLACK 0x00 +#define EFI_BACKGROUND_BLUE 0x10 +#define EFI_BACKGROUND_GREEN 0x20 +#define EFI_BACKGROUND_CYAN (EFI_BACKGROUND_BLUE | EFI_BACKGROUND_GREEN) +#define EFI_BACKGROUND_RED 0x40 +#define EFI_BACKGROUND_MAGENTA (EFI_BACKGROUND_BLUE | EFI_BACKGROUND_RED) +#define EFI_BACKGROUND_BROWN (EFI_BACKGROUND_GREEN | EFI_BACKGROUND_RED) +#define EFI_BACKGROUND_LIGHTGRAY (EFI_BACKGROUND_BLUE | EFI_BACKGROUND_GREEN | EFI_BACKGROUND_RED) + +// +// We currently define attributes from 0 - 7F for color manipulations +// To internally handle the local display characteristics for a particular character, we are defining +// Bit 7 to signify the local glyph representation for a character. If turned on, glyphs will be +// pulled from the wide glyph database and will display locally as a wide character (16 X 19 versus 8 X 19) +// If bit 7 is off, the narrow glyph database will be used. This does NOT affect information that is sent to +// non-local displays (e.g. serial or LAN consoles). +// +#define EFI_WIDE_ATTRIBUTE 0x80 + +/** + Reset the text output device hardware and optionaly run diagnostics + + @param This Protocol instance pointer. + @param ExtendedVerification Driver may perform more exhaustive verfication + operation of the device during reset. + + @retval EFI_SUCCESS The text output device was reset. + @retval EFI_DEVICE_ERROR The text output device is not functioning correctly and + could not be reset. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TEXT_RESET)( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN BOOLEAN ExtendedVerification + ); + +/** + Write a Unicode string to the output device. + + @param This Protocol instance pointer. + @param String The NULL-terminated Unicode string to be displayed on the output + device(s). All output devices must also support the Unicode + drawing defined in this file. + + @retval EFI_SUCCESS The string was output to the device. + @retval EFI_DEVICE_ERROR The device reported an error while attempting to output + the text. + @retval EFI_UNSUPPORTED The output device's mode is not currently in a + defined text mode. + @retval EFI_WARN_UNKNOWN_GLYPH This warning code indicates that some of the + characters in the Unicode string could not be + rendered and were skipped. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TEXT_STRING)( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *String + ); + +/** + Verifies that all characters in a Unicode string can be output to the + target device. + + @param This Protocol instance pointer. + @param String The NULL-terminated Unicode string to be examined for the output + device(s). + + @retval EFI_SUCCESS The device(s) are capable of rendering the output string. + @retval EFI_UNSUPPORTED Some of the characters in the Unicode string cannot be + rendered by one or more of the output devices mapped + by the EFI handle. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TEXT_TEST_STRING)( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN CHAR16 *String + ); + +/** + Returns information for an available text mode that the output device(s) + supports. + + @param This Protocol instance pointer. + @param ModeNumber The mode number to return information on. + @param Columns Returns the geometry of the text output device for the + requested ModeNumber. + @param Rows Returns the geometry of the text output device for the + requested ModeNumber. + + @retval EFI_SUCCESS The requested mode information was returned. + @retval EFI_DEVICE_ERROR The device had an error and could not complete the request. + @retval EFI_UNSUPPORTED The mode number was not valid. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TEXT_QUERY_MODE)( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN ModeNumber, + OUT UINTN *Columns, + OUT UINTN *Rows + ); + +/** + Sets the output device(s) to a specified mode. + + @param This Protocol instance pointer. + @param ModeNumber The mode number to set. + + @retval EFI_SUCCESS The requested text mode was set. + @retval EFI_DEVICE_ERROR The device had an error and could not complete the request. + @retval EFI_UNSUPPORTED The mode number was not valid. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TEXT_SET_MODE)( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN ModeNumber + ); + +/** + Sets the background and foreground colors for the OutputString () and + ClearScreen () functions. + + @param This Protocol instance pointer. + @param Attribute The attribute to set. Bits 0..3 are the foreground color, and + bits 4..6 are the background color. All other bits are undefined + and must be zero. The valid Attributes are defined in this file. + + @retval EFI_SUCCESS The attribute was set. + @retval EFI_DEVICE_ ERROR The device had an error and could not complete the request. + @retval EFI_UNSUPPORTED The attribute requested is not defined. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TEXT_SET_ATTRIBUTE)( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN Attribute + ); + +/** + Clears the output device(s) display to the currently selected background + color. + + @param This Protocol instance pointer. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The device had an error and could not complete the request. + @retval EFI_UNSUPPORTED The output device is not in a valid text mode. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TEXT_CLEAR_SCREEN)( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This + ); + +/** + Sets the current coordinates of the cursor position + + @param This Protocol instance pointer. + @param Column The position to set the cursor to. Must be greater than or + equal to zero and less than the number of columns and rows + by QueryMode (). + @param Row The position to set the cursor to. Must be greater than or + equal to zero and less than the number of columns and rows + by QueryMode (). + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The device had an error and could not complete the request. + @retval EFI_UNSUPPORTED The output device is not in a valid text mode, or the + cursor position is invalid for the current mode. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TEXT_SET_CURSOR_POSITION)( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN UINTN Column, + IN UINTN Row + ); + +/** + Makes the cursor visible or invisible + + @param This Protocol instance pointer. + @param Visible If TRUE, the cursor is set to be visible. If FALSE, the cursor is + set to be invisible. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_DEVICE_ERROR The device had an error and could not complete the + request, or the device does not support changing + the cursor mode. + @retval EFI_UNSUPPORTED The output device is not in a valid text mode. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_TEXT_ENABLE_CURSOR)( + IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, + IN BOOLEAN Visible + ); + +/** + @par Data Structure Description: + Mode Structure pointed to by Simple Text Out protocol. + + @param MaxMode + The number of modes supported by QueryMode () and SetMode (). + + @param Mode + The text mode of the output device(s). + + @param Attribute + The current character output attribute + + @param CursorColumn + The cursor's column. + + @param CursorRow + The cursor's row. + + @param CursorVisible + The cursor is currently visbile or not. + +**/ +typedef struct { + INT32 MaxMode; + + // + // current settings + // + INT32 Mode; + INT32 Attribute; + INT32 CursorColumn; + INT32 CursorRow; + BOOLEAN CursorVisible; +} EFI_SIMPLE_TEXT_OUTPUT_MODE; + +/// +/// The SIMPLE_TEXT_OUTPUT protocol is used to control text-based output devices. +/// It is the minimum required protocol for any handle supplied as the ConsoleOut +/// or StandardError device. In addition, the minimum supported text mode of such +/// devices is at least 80 x 25 characters. +/// +struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL { + EFI_TEXT_RESET Reset; + + EFI_TEXT_STRING OutputString; + EFI_TEXT_TEST_STRING TestString; + + EFI_TEXT_QUERY_MODE QueryMode; + EFI_TEXT_SET_MODE SetMode; + EFI_TEXT_SET_ATTRIBUTE SetAttribute; + + EFI_TEXT_CLEAR_SCREEN ClearScreen; + EFI_TEXT_SET_CURSOR_POSITION SetCursorPosition; + EFI_TEXT_ENABLE_CURSOR EnableCursor; + + /// + /// Pointer to SIMPLE_TEXT_OUTPUT_MODE data. + /// + EFI_SIMPLE_TEXT_OUTPUT_MODE *Mode; +}; + +extern EFI_GUID gEfiSimpleTextOutProtocolGuid; + +#endif diff --git a/gpxe/src/include/gpxe/efi/Uefi.h b/gpxe/src/include/gpxe/efi/Uefi.h new file mode 100644 index 0000000..58ddb11 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Uefi.h @@ -0,0 +1,27 @@ +/** @file + + Root include file for Mde Package UEFI, UEFI_APPLICATION type modules. + + This is the include file for any module of type UEFI and UEFI_APPLICATION. Uefi modules only use + types defined via this include file and can be ported easily to any + environment. + +Copyright (c) 2006 - 2007, Intel Corporation +All rights reserved. This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __PI_UEFI_H__ +#define __PI_UEFI_H__ + +#include +#include + +#endif + diff --git a/gpxe/src/include/gpxe/efi/Uefi/UefiBaseType.h b/gpxe/src/include/gpxe/efi/Uefi/UefiBaseType.h new file mode 100644 index 0000000..475ac01 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Uefi/UefiBaseType.h @@ -0,0 +1,200 @@ +/** @file + Defines data types and constants introduced in UEFI. + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __UEFI_BASETYPE_H__ +#define __UEFI_BASETYPE_H__ + +#include + +// +// Basical data type definitions introduced in UEFI. +// + +/// +/// 128-bit buffer containing a unique identifier value. +/// +typedef GUID EFI_GUID; +/// +/// Function return status for EFI API +/// +typedef RETURN_STATUS EFI_STATUS; +/// +/// A collection of related interfaces. +/// +typedef VOID *EFI_HANDLE; +/// +/// Handle to an event structure. +/// +typedef VOID *EFI_EVENT; +/// +/// Task priority level. +/// +typedef UINTN EFI_TPL; +/// +/// Logical block address. +/// +typedef UINT64 EFI_LBA; +typedef UINT64 EFI_PHYSICAL_ADDRESS; +typedef UINT64 EFI_VIRTUAL_ADDRESS; + +/// +/// EFI Time Abstraction: +/// Year: 1998 - 20XX +/// Month: 1 - 12 +/// Day: 1 - 31 +/// Hour: 0 - 23 +/// Minute: 0 - 59 +/// Second: 0 - 59 +/// Nanosecond: 0 - 999,999,999 +/// TimeZone: -1440 to 1440 or 2047 +/// +typedef struct { + UINT16 Year; + UINT8 Month; + UINT8 Day; + UINT8 Hour; + UINT8 Minute; + UINT8 Second; + UINT8 Pad1; + UINT32 Nanosecond; + INT16 TimeZone; + UINT8 Daylight; + UINT8 Pad2; +} EFI_TIME; + + +/// +/// 4-byte buffer. An IPv4 internet protocol address. +/// +typedef struct { + UINT8 Addr[4]; +} EFI_IPv4_ADDRESS; + +/// +/// 16-byte buffer. An IPv6 internet protocol address +/// +typedef struct { + UINT8 Addr[16]; +} EFI_IPv6_ADDRESS; + +/// +/// 32-byte buffer containing a network Media Access Control address. +/// +typedef struct { + UINT8 Addr[32]; +} EFI_MAC_ADDRESS; + +/// +/// 16-byte buffer aligned on a 4-byte boundary. +/// An IPv4 or IPv6 internet protocol address. +/// +typedef union { + UINT32 Addr[4]; + EFI_IPv4_ADDRESS v4; + EFI_IPv6_ADDRESS v6; +} EFI_IP_ADDRESS; + + +// +// Enumeration of EFI_STATUS. +// +#define EFI_SUCCESS RETURN_SUCCESS +#define EFI_LOAD_ERROR RETURN_LOAD_ERROR +#define EFI_INVALID_PARAMETER RETURN_INVALID_PARAMETER +#define EFI_UNSUPPORTED RETURN_UNSUPPORTED +#define EFI_BAD_BUFFER_SIZE RETURN_BAD_BUFFER_SIZE +#define EFI_BUFFER_TOO_SMALL RETURN_BUFFER_TOO_SMALL +#define EFI_NOT_READY RETURN_NOT_READY +#define EFI_DEVICE_ERROR RETURN_DEVICE_ERROR +#define EFI_WRITE_PROTECTED RETURN_WRITE_PROTECTED +#define EFI_OUT_OF_RESOURCES RETURN_OUT_OF_RESOURCES +#define EFI_VOLUME_CORRUPTED RETURN_VOLUME_CORRUPTED +#define EFI_VOLUME_FULL RETURN_VOLUME_FULL +#define EFI_NO_MEDIA RETURN_NO_MEDIA +#define EFI_MEDIA_CHANGED RETURN_MEDIA_CHANGED +#define EFI_NOT_FOUND RETURN_NOT_FOUND +#define EFI_ACCESS_DENIED RETURN_ACCESS_DENIED +#define EFI_NO_RESPONSE RETURN_NO_RESPONSE +#define EFI_NO_MAPPING RETURN_NO_MAPPING +#define EFI_TIMEOUT RETURN_TIMEOUT +#define EFI_NOT_STARTED RETURN_NOT_STARTED +#define EFI_ALREADY_STARTED RETURN_ALREADY_STARTED +#define EFI_ABORTED RETURN_ABORTED +#define EFI_ICMP_ERROR RETURN_ICMP_ERROR +#define EFI_TFTP_ERROR RETURN_TFTP_ERROR +#define EFI_PROTOCOL_ERROR RETURN_PROTOCOL_ERROR +#define EFI_INCOMPATIBLE_VERSION RETURN_INCOMPATIBLE_VERSION +#define EFI_SECURITY_VIOLATION RETURN_SECURITY_VIOLATION +#define EFI_CRC_ERROR RETURN_CRC_ERROR +#define EFI_END_OF_MEDIA RETURN_END_OF_MEDIA +#define EFI_END_OF_FILE RETURN_END_OF_FILE +#define EFI_INVALID_LANGUAGE RETURN_INVALID_LANGUAGE + +#define EFI_WARN_UNKNOWN_GLYPH RETURN_WARN_UNKNOWN_GLYPH +#define EFI_WARN_DELETE_FAILURE RETURN_WARN_DELETE_FAILURE +#define EFI_WARN_WRITE_FAILURE RETURN_WARN_WRITE_FAILURE +#define EFI_WARN_BUFFER_TOO_SMALL RETURN_WARN_BUFFER_TOO_SMALL + + +// +// Define macro to encode the status code. +// +#define EFIERR(_a) ENCODE_ERROR(_a) + +#define EFI_ERROR(A) RETURN_ERROR(A) + +// +// Define macros to build data structure signatures from characters. +// +#define EFI_SIGNATURE_16(A, B) SIGNATURE_16 (A, B) +#define EFI_SIGNATURE_32(A, B, C, D) SIGNATURE_32 (A, B, C, D) +#define EFI_SIGNATURE_64(A, B, C, D, E, F, G, H) SIGNATURE_64 (A, B, C, D, E, F, G, H) + + +/// +/// Returns the byte offset to a field within a structure +/// +#define EFI_FIELD_OFFSET(TYPE,Field) ((UINTN)(&(((TYPE *) 0)->Field))) + +// +// The EFI memory allocation functions work in units of EFI_PAGEs that are +// 4K. This should in no way be confused with the page size of the processor. +// An EFI_PAGE is just the quanta of memory in EFI. +// +#define EFI_PAGE_SIZE 0x1000 +#define EFI_PAGE_MASK 0xFFF +#define EFI_PAGE_SHIFT 12 + +#define EFI_SIZE_TO_PAGES(a) (((a) >> EFI_PAGE_SHIFT) + (((a) & EFI_PAGE_MASK) ? 1 : 0)) + +#define EFI_PAGES_TO_SIZE(a) ( (a) << EFI_PAGE_SHIFT) + + +#define EFI_MAX_BIT MAX_BIT +#define EFI_MAX_ADDRESS MAX_ADDRESS + + +/// +/// Limited buffer size for a language code recommended by RFC3066 +/// (42 characters plus a NULL terminator) +/// +#define RFC_3066_ENTRY_SIZE (42 + 1) + +/// +/// The size of a 3 character ISO639 language code. +/// +#define ISO_639_2_ENTRY_SIZE 3 + + +#endif diff --git a/gpxe/src/include/gpxe/efi/Uefi/UefiGpt.h b/gpxe/src/include/gpxe/efi/Uefi/UefiGpt.h new file mode 100644 index 0000000..92c3035 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Uefi/UefiGpt.h @@ -0,0 +1,62 @@ +/** @file + EFI Guid Partition Table Format Definition. + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __UEFI_GPT_H__ +#define __UEFI_GPT_H__ + +/// +/// The primary GUID Partition Table Header must be +/// located in LBA 1 (i.e., the second logical block). +/// +#define PRIMARY_PART_HEADER_LBA 1 + +/// +/// EFI Partition Table Signature: "EFI PART" +/// +#define EFI_PTAB_HEADER_ID 0x5452415020494645ULL + +#pragma pack(1) + +/// +/// GPT Partition Table Header +/// +typedef struct { + EFI_TABLE_HEADER Header; + EFI_LBA MyLBA; + EFI_LBA AlternateLBA; + EFI_LBA FirstUsableLBA; + EFI_LBA LastUsableLBA; + EFI_GUID DiskGUID; + EFI_LBA PartitionEntryLBA; + UINT32 NumberOfPartitionEntries; + UINT32 SizeOfPartitionEntry; + UINT32 PartitionEntryArrayCRC32; +} EFI_PARTITION_TABLE_HEADER; + +/// +/// GPT Partition Entry +/// +typedef struct { + EFI_GUID PartitionTypeGUID; + EFI_GUID UniquePartitionGUID; + EFI_LBA StartingLBA; + EFI_LBA EndingLBA; + UINT64 Attributes; + CHAR16 PartitionName[36]; +} EFI_PARTITION_ENTRY; + +#pragma pack() +#endif + + diff --git a/gpxe/src/include/gpxe/efi/Uefi/UefiInternalFormRepresentation.h b/gpxe/src/include/gpxe/efi/Uefi/UefiInternalFormRepresentation.h new file mode 100644 index 0000000..c675517 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Uefi/UefiInternalFormRepresentation.h @@ -0,0 +1,1590 @@ +/** @file + This file defines the encoding for the VFR (Visual Form Representation) language. + IFR is primarily consumed by the EFI presentation engine, and produced by EFI + internal application and drivers as well as all add-in card option-ROM drivers + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + These definitions are from UEFI2.1. + +**/ + +#ifndef __UEFI_INTERNAL_FORMREPRESENTATION_H__ +#define __UEFI_INTERNAL_FORMREPRESENTATION_H__ + +/// +/// The following types are currently defined: +/// +typedef VOID* EFI_HII_HANDLE; +typedef CHAR16* EFI_STRING; +typedef UINT16 EFI_IMAGE_ID; +typedef UINT16 EFI_QUESTION_ID; +typedef UINT16 EFI_STRING_ID; +typedef UINT16 EFI_FORM_ID; +typedef UINT16 EFI_VARSTORE_ID; + +typedef UINT16 EFI_DEFAULT_ID; + +typedef UINT32 EFI_HII_FONT_STYLE; + + + +#pragma pack(1) + +// +// Definitions for Package Lists and Package Headers +// Section 27.3.1 +// + +/// +/// The header found at the start of each package list. +/// +typedef struct { + EFI_GUID PackageListGuid; + UINT32 PackageLength; +} EFI_HII_PACKAGE_LIST_HEADER; + +/// +/// The header found at the start of each package. +/// +typedef struct { + UINT32 Length:24; + UINT32 Type:8; + // UINT8 Data[...]; +} EFI_HII_PACKAGE_HEADER; + +// +// Value of HII package type +// +#define EFI_HII_PACKAGE_TYPE_ALL 0x00 +#define EFI_HII_PACKAGE_TYPE_GUID 0x01 +#define EFI_HII_PACKAGE_FORMS 0x02 +#define EFI_HII_PACKAGE_STRINGS 0x04 +#define EFI_HII_PACKAGE_FONTS 0x05 +#define EFI_HII_PACKAGE_IMAGES 0x06 +#define EFI_HII_PACKAGE_SIMPLE_FONTS 0x07 +#define EFI_HII_PACKAGE_DEVICE_PATH 0x08 +#define EFI_HII_PACKAGE_KEYBOARD_LAYOUT 0x09 +#define EFI_HII_PACKAGE_END 0xDF +#define EFI_HII_PACKAGE_TYPE_SYSTEM_BEGIN 0xE0 +#define EFI_HII_PACKAGE_TYPE_SYSTEM_END 0xFF + +// +// Definitions for Simplified Font Package +// Section 27.3.2 +// + +// +// Contents of EFI_NARROW_GLYPH.Attributes +// +#define EFI_GLYPH_NON_SPACING 0x01 +#define EFI_GLYPH_WIDE 0x02 +#define EFI_GLYPH_HEIGHT 19 +#define EFI_GLYPH_WIDTH 8 + +typedef struct { + CHAR16 UnicodeWeight; + UINT8 Attributes; + UINT8 GlyphCol1[EFI_GLYPH_HEIGHT]; +} EFI_NARROW_GLYPH; + +typedef struct { + CHAR16 UnicodeWeight; + UINT8 Attributes; + UINT8 GlyphCol1[EFI_GLYPH_HEIGHT]; + UINT8 GlyphCol2[EFI_GLYPH_HEIGHT]; + UINT8 Pad[3]; +} EFI_WIDE_GLYPH; + +/// +/// A simplified font package consists of a font header +/// followed by a series of glyph structures. +/// +typedef struct _EFI_HII_SIMPLE_FONT_PACKAGE_HDR { + EFI_HII_PACKAGE_HEADER Header; + UINT16 NumberOfNarrowGlyphs; + UINT16 NumberOfWideGlyphs; + // EFI_NARROW_GLYPH NarrowGlyphs[]; + // EFI_WIDE_GLYPH WideGlyphs[]; +} EFI_HII_SIMPLE_FONT_PACKAGE_HDR; + +// +// Definitions for Font Package +// Section 27.3.3 +// + +// +// Value for font style +// +#define EFI_HII_FONT_STYLE_NORMAL 0x00000000 +#define EFI_HII_FONT_STYLE_BOLD 0x00000001 +#define EFI_HII_FONT_STYLE_ITALIC 0x00000002 +#define EFI_HII_FONT_STYLE_EMBOSS 0x00010000 +#define EFI_HII_FONT_STYLE_OUTLINE 0x00020000 +#define EFI_HII_FONT_STYLE_SHADOW 0x00040000 +#define EFI_HII_FONT_STYLE_UNDERLINE 0x00080000 +#define EFI_HII_FONT_STYLE_DBL_UNDER 0x00100000 + +typedef struct _EFI_HII_GLYPH_INFO { + UINT16 Width; + UINT16 Height; + INT16 OffsetX; + INT16 OffsetY; + INT16 AdvanceX; +} EFI_HII_GLYPH_INFO; + +/// +/// The fixed header consists of a standard record header and +/// then the character values in this section, the flags +/// (including the encoding method) and the offsets of the glyph +/// information, the glyph bitmaps and the character map. +/// +typedef struct _EFI_HII_FONT_PACKAGE_HDR { + EFI_HII_PACKAGE_HEADER Header; + UINT32 HdrSize; + UINT32 GlyphBlockOffset; + EFI_HII_GLYPH_INFO Cell; + EFI_HII_FONT_STYLE FontStyle; + CHAR16 FontFamily[1]; +} EFI_HII_FONT_PACKAGE_HDR; + +// +// Value of different glyph info block types +// +#define EFI_HII_GIBT_END 0x00 +#define EFI_HII_GIBT_GLYPH 0x10 +#define EFI_HII_GIBT_GLYPHS 0x11 +#define EFI_HII_GIBT_GLYPH_DEFAULT 0x12 +#define EFI_HII_GIBT_GLYPHS_DEFAULT 0x13 +#define EFI_HII_GIBT_DUPLICATE 0x20 +#define EFI_HII_GIBT_SKIP2 0x21 +#define EFI_HII_GIBT_SKIP1 0x22 +#define EFI_HII_GIBT_DEFAULTS 0x23 +#define EFI_HII_GIBT_EXT1 0x30 +#define EFI_HII_GIBT_EXT2 0x31 +#define EFI_HII_GIBT_EXT4 0x32 + +typedef struct _EFI_HII_GLYPH_BLOCK { + UINT8 BlockType; +} EFI_HII_GLYPH_BLOCK; + +// +// Definition of different glyph info block types +// + +typedef struct _EFI_HII_GIBT_DEFAULTS_BLOCK { + EFI_HII_GLYPH_BLOCK Header; + EFI_HII_GLYPH_INFO Cell; +} EFI_HII_GIBT_DEFAULTS_BLOCK; + +typedef struct _EFI_HII_GIBT_DUPLICATE_BLOCK { + EFI_HII_GLYPH_BLOCK Header; + CHAR16 CharValue; +} EFI_HII_GIBT_DUPLICATE_BLOCK; + +typedef struct _EFI_GLYPH_GIBT_END_BLOCK { + EFI_HII_GLYPH_BLOCK Header; +} EFI_GLYPH_GIBT_END_BLOCK; + +typedef struct _EFI_HII_GIBT_EXT1_BLOCK { + EFI_HII_GLYPH_BLOCK Header; + UINT8 BlockType2; + UINT8 Length; +} EFI_HII_GIBT_EXT1_BLOCK; + +typedef struct _EFI_HII_GIBT_EXT2_BLOCK { + EFI_HII_GLYPH_BLOCK Header; + UINT8 BlockType2; + UINT16 Length; +} EFI_HII_GIBT_EXT2_BLOCK; + +typedef struct _EFI_HII_GIBT_EXT4_BLOCK { + EFI_HII_GLYPH_BLOCK Header; + UINT8 BlockType2; + UINT32 Length; +} EFI_HII_GIBT_EXT4_BLOCK; + +typedef struct _EFI_HII_GIBT_GLYPH_BLOCK { + EFI_HII_GLYPH_BLOCK Header; + EFI_HII_GLYPH_INFO Cell; + UINT8 BitmapData[1]; +} EFI_HII_GIBT_GLYPH_BLOCK; + +typedef struct _EFI_HII_GIBT_GLYPHS_BLOCK { + EFI_HII_GLYPH_BLOCK Header; + EFI_HII_GLYPH_INFO Cell; + UINT16 Count; + UINT8 BitmapData[1]; +} EFI_HII_GIBT_GLYPHS_BLOCK; + +typedef struct _EFI_HII_GIBT_GLYPH_DEFAULT_BLOCK { + EFI_HII_GLYPH_BLOCK Header; + UINT8 BitmapData[1]; +} EFI_HII_GIBT_GLYPH_DEFAULT_BLOCK; + +typedef struct _EFI_HII_GIBT_GLYPHS_DEFAULT_BLOCK { + EFI_HII_GLYPH_BLOCK Header; + UINT16 Count; + UINT8 BitmapData[1]; +} EFI_HII_GIBT_GLYPHS_DEFAULT_BLOCK; + +typedef struct _EFI_HII_GIBT_SKIP1_BLOCK { + EFI_HII_GLYPH_BLOCK Header; + UINT8 SkipCount; +} EFI_HII_GIBT_SKIP1_BLOCK; + +typedef struct _EFI_HII_GIBT_SKIP2_BLOCK { + EFI_HII_GLYPH_BLOCK Header; + UINT16 SkipCount; +} EFI_HII_GIBT_SKIP2_BLOCK; + +// +// Definitions for Device Path Package +// Section 27.3.4 +// + +/// +/// The device path package is used to carry a device path +/// associated with the package list. +/// +typedef struct _EFI_HII_DEVICE_PATH_PACKAGE { + EFI_HII_PACKAGE_HEADER Header; + // EFI_DEVICE_PATH_PROTOCOL DevicePath[]; +} EFI_HII_DEVICE_PATH_PACKAGE; + +// +// Definitions for GUID Package +// Section 27.3.5 +// + +/// +/// The GUID package is used to carry data where the format is defined by a GUID. +/// +typedef struct _EFI_HII_GUID_PACKAGE_HDR { + EFI_HII_PACKAGE_HEADER Header; + EFI_GUID Guid; + // Data per GUID definition may follow +} EFI_HII_GUID_PACKAGE_HDR; + +// +// Definitions for String Package +// Section 27.3.6 +// + +#define UEFI_CONFIG_LANG L"x-UEFI" +#define UEFI_CONFIG_LANG2 L"x-i-UEFI" + +/// +/// The fixed header consists of a standard record header and then the string identifiers +/// contained in this section and the offsets of the string and language information. +/// +typedef struct _EFI_HII_STRING_PACKAGE_HDR { + EFI_HII_PACKAGE_HEADER Header; + UINT32 HdrSize; + UINT32 StringInfoOffset; + CHAR16 LanguageWindow[16]; + EFI_STRING_ID LanguageName; + CHAR8 Language[1]; +} EFI_HII_STRING_PACKAGE_HDR; + +typedef struct { + UINT8 BlockType; +} EFI_HII_STRING_BLOCK; + +// +// Value of different string information block types +// +#define EFI_HII_SIBT_END 0x00 +#define EFI_HII_SIBT_STRING_SCSU 0x10 +#define EFI_HII_SIBT_STRING_SCSU_FONT 0x11 +#define EFI_HII_SIBT_STRINGS_SCSU 0x12 +#define EFI_HII_SIBT_STRINGS_SCSU_FONT 0x13 +#define EFI_HII_SIBT_STRING_UCS2 0x14 +#define EFI_HII_SIBT_STRING_UCS2_FONT 0x15 +#define EFI_HII_SIBT_STRINGS_UCS2 0x16 +#define EFI_HII_SIBT_STRINGS_UCS2_FONT 0x17 +#define EFI_HII_SIBT_DUPLICATE 0x20 +#define EFI_HII_SIBT_SKIP2 0x21 +#define EFI_HII_SIBT_SKIP1 0x22 +#define EFI_HII_SIBT_EXT1 0x30 +#define EFI_HII_SIBT_EXT2 0x31 +#define EFI_HII_SIBT_EXT4 0x32 +#define EFI_HII_SIBT_FONT 0x40 + +// +// Definition of different string information block types +// + +typedef struct _EFI_HII_SIBT_DUPLICATE_BLOCK { + EFI_HII_STRING_BLOCK Header; + EFI_STRING_ID StringId; +} EFI_HII_SIBT_DUPLICATE_BLOCK; + +typedef struct _EFI_HII_SIBT_END_BLOCK { + EFI_HII_STRING_BLOCK Header; +} EFI_HII_SIBT_END_BLOCK; + +typedef struct _EFI_HII_SIBT_EXT1_BLOCK { + EFI_HII_STRING_BLOCK Header; + UINT8 BlockType2; + UINT8 Length; +} EFI_HII_SIBT_EXT1_BLOCK; + +typedef struct _EFI_HII_SIBT_EXT2_BLOCK { + EFI_HII_STRING_BLOCK Header; + UINT8 BlockType2; + UINT16 Length; +} EFI_HII_SIBT_EXT2_BLOCK; + +typedef struct _EFI_HII_SIBT_EXT4_BLOCK { + EFI_HII_STRING_BLOCK Header; + UINT8 BlockType2; + UINT32 Length; +} EFI_HII_SIBT_EXT4_BLOCK; + +typedef struct _EFI_HII_SIBT_FONT_BLOCK { + EFI_HII_SIBT_EXT2_BLOCK Header; + UINT8 FontId; + UINT16 FontSize; + EFI_HII_FONT_STYLE FontStyle; + CHAR16 FontName[1]; +} EFI_HII_SIBT_FONT_BLOCK; + +typedef struct _EFI_HII_SIBT_SKIP1_BLOCK { + EFI_HII_STRING_BLOCK Header; + UINT8 SkipCount; +} EFI_HII_SIBT_SKIP1_BLOCK; + +typedef struct _EFI_HII_SIBT_SKIP2_BLOCK { + EFI_HII_STRING_BLOCK Header; + UINT16 SkipCount; +} EFI_HII_SIBT_SKIP2_BLOCK; + +typedef struct _EFI_HII_SIBT_STRING_SCSU_BLOCK { + EFI_HII_STRING_BLOCK Header; + UINT8 StringText[1]; +} EFI_HII_SIBT_STRING_SCSU_BLOCK; + +typedef struct _EFI_HII_SIBT_STRING_SCSU_FONT_BLOCK { + EFI_HII_STRING_BLOCK Header; + UINT8 FontIdentifier; + UINT8 StringText[1]; +} EFI_HII_SIBT_STRING_SCSU_FONT_BLOCK; + +typedef struct _EFI_HII_SIBT_STRINGS_SCSU_BLOCK { + EFI_HII_STRING_BLOCK Header; + UINT16 StringCount; + UINT8 StringText[1]; +} EFI_HII_SIBT_STRINGS_SCSU_BLOCK; + +typedef struct _EFI_HII_SIBT_STRINGS_SCSU_FONT_BLOCK { + EFI_HII_STRING_BLOCK Header; + UINT8 FontIdentifier; + UINT16 StringCount; + UINT8 StringText[1]; +} EFI_HII_SIBT_STRINGS_SCSU_FONT_BLOCK; + +typedef struct _EFI_HII_SIBT_STRING_UCS2_BLOCK { + EFI_HII_STRING_BLOCK Header; + CHAR16 StringText[1]; +} EFI_HII_SIBT_STRING_UCS2_BLOCK; + +typedef struct _EFI_HII_SIBT_STRING_UCS2_FONT_BLOCK { + EFI_HII_STRING_BLOCK Header; + UINT8 FontIdentifier; + CHAR16 StringText[1]; +} EFI_HII_SIBT_STRING_UCS2_FONT_BLOCK; + +typedef struct _EFI_HII_SIBT_STRINGS_UCS2_BLOCK { + EFI_HII_STRING_BLOCK Header; + UINT16 StringCount; + CHAR16 StringText[1]; +} EFI_HII_SIBT_STRINGS_UCS2_BLOCK; + +typedef struct _EFI_HII_SIBT_STRINGS_UCS2_FONT_BLOCK { + EFI_HII_STRING_BLOCK Header; + UINT8 FontIdentifier; + UINT16 StringCount; + CHAR16 StringText[1]; +} EFI_HII_SIBT_STRINGS_UCS2_FONT_BLOCK; + +// +// Definitions for Image Package +// Section 27.3.7 +// + +typedef struct _EFI_HII_IMAGE_PACKAGE_HDR { + EFI_HII_PACKAGE_HEADER Header; + UINT32 ImageInfoOffset; + UINT32 PaletteInfoOffset; +} EFI_HII_IMAGE_PACKAGE_HDR; + +typedef struct _EFI_HII_IMAGE_BLOCK { + UINT8 BlockType; +} EFI_HII_IMAGE_BLOCK; + +// +// Value of different image information block types +// +#define EFI_HII_IIBT_END 0x00 +#define EFI_HII_IIBT_IMAGE_1BIT 0x10 +#define EFI_HII_IIBT_IMAGE_1BIT_TRANS 0x11 +#define EFI_HII_IIBT_IMAGE_4BIT 0x12 +#define EFI_HII_IIBT_IMAGE_4BIT_TRANS 0x13 +#define EFI_HII_IIBT_IMAGE_8BIT 0x14 +#define EFI_HII_IIBT_IMAGE_8BIT_TRANS 0x15 +#define EFI_HII_IIBT_IMAGE_24BIT 0x16 +#define EFI_HII_IIBT_IMAGE_24BIT_TRANS 0x17 +#define EFI_HII_IIBT_IMAGE_JPEG 0x18 +#define EFI_HII_IIBT_DUPLICATE 0x20 +#define EFI_HII_IIBT_SKIP2 0x21 +#define EFI_HII_IIBT_SKIP1 0x22 +#define EFI_HII_IIBT_EXT1 0x30 +#define EFI_HII_IIBT_EXT2 0x31 +#define EFI_HII_IIBT_EXT4 0x32 + +// +// Definition of different image information block types +// + +typedef struct _EFI_HII_IIBT_END_BLOCK { + EFI_HII_IMAGE_BLOCK Header; +} EFI_HII_IIBT_END_BLOCK; + +typedef struct _EFI_HII_IIBT_EXT1_BLOCK { + EFI_HII_IMAGE_BLOCK Header; + UINT8 BlockType2; + UINT8 Length; +} EFI_HII_IIBT_EXT1_BLOCK; + +typedef struct _EFI_HII_IIBT_EXT2_BLOCK { + EFI_HII_IMAGE_BLOCK Header; + UINT8 BlockType2; + UINT16 Length; +} EFI_HII_IIBT_EXT2_BLOCK; + +typedef struct _EFI_HII_IIBT_EXT4_BLOCK { + EFI_HII_IMAGE_BLOCK Header; + UINT8 BlockType2; + UINT32 Length; +} EFI_HII_IIBT_EXT4_BLOCK; + +typedef struct _EFI_HII_IIBT_IMAGE_1BIT_BASE { + UINT16 Width; + UINT16 Height; + UINT8 Data[1]; +} EFI_HII_IIBT_IMAGE_1BIT_BASE; + +typedef struct _EFI_HII_IIBT_IMAGE_1BIT_BLOCK { + EFI_HII_IMAGE_BLOCK Header; + UINT8 PaletteIndex; + EFI_HII_IIBT_IMAGE_1BIT_BASE Bitmap; +} EFI_HII_IIBT_IMAGE_1BIT_BLOCK; + +typedef struct _EFI_HII_IIBT_IMAGE_1BIT_TRANS_BLOCK { + EFI_HII_IMAGE_BLOCK Header; + UINT8 PaletteIndex; + EFI_HII_IIBT_IMAGE_1BIT_BASE Bitmap; +} EFI_HII_IIBT_IMAGE_1BIT_TRANS_BLOCK; + +typedef struct _EFI_HII_RGB_PIXEL { + UINT8 b; + UINT8 g; + UINT8 r; +} EFI_HII_RGB_PIXEL; + +typedef struct _EFI_HII_IIBT_IMAGE_24BIT_BASE { + UINT16 Width; + UINT16 Height; + EFI_HII_RGB_PIXEL Bitmap[1]; +} EFI_HII_IIBT_IMAGE_24BIT_BASE; + +typedef struct _EFI_HII_IIBT_IMAGE_24BIT_BLOCK { + EFI_HII_IMAGE_BLOCK Header; + EFI_HII_IIBT_IMAGE_24BIT_BASE Bitmap; +} EFI_HII_IIBT_IMAGE_24BIT_BLOCK; + +typedef struct _EFI_HII_IIBT_IMAGE_24BIT_TRANS_BLOCK { + EFI_HII_IMAGE_BLOCK Header; + EFI_HII_IIBT_IMAGE_24BIT_BASE Bitmap; +} EFI_HII_IIBT_IMAGE_24BIT_TRANS_BLOCK; + +typedef struct _EFI_HII_IIBT_IMAGE_4BIT_BASE { + UINT16 Width; + UINT16 Height; + UINT8 Data[1]; +} EFI_HII_IIBT_IMAGE_4BIT_BASE; + +typedef struct _EFI_HII_IIBT_IMAGE_4BIT_BLOCK { + EFI_HII_IMAGE_BLOCK Header; + UINT8 PaletteIndex; + EFI_HII_IIBT_IMAGE_4BIT_BASE Bitmap; +} EFI_HII_IIBT_IMAGE_4BIT_BLOCK; + +typedef struct _EFI_HII_IIBT_IMAGE_4BIT_TRANS_BLOCK { + EFI_HII_IMAGE_BLOCK Header; + UINT8 PaletteIndex; + EFI_HII_IIBT_IMAGE_4BIT_BASE Bitmap; +} EFI_HII_IIBT_IMAGE_4BIT_TRANS_BLOCK; + +typedef struct _EFI_HII_IIBT_IMAGE_8BIT_BASE { + UINT16 Width; + UINT16 Height; + UINT8 Data[1]; +} EFI_HII_IIBT_IMAGE_8BIT_BASE; + +typedef struct _EFI_HII_IIBT_IMAGE_8BIT_PALETTE_BLOCK { + EFI_HII_IMAGE_BLOCK Header; + UINT8 PaletteIndex; + EFI_HII_IIBT_IMAGE_8BIT_BASE Bitmap; +} EFI_HII_IIBT_IMAGE_8BIT_BLOCK; + +typedef struct _EFI_HII_IIBT_IMAGE_8BIT_TRANS_BLOCK { + EFI_HII_IMAGE_BLOCK Header; + UINT8 PaletteIndex; + EFI_HII_IIBT_IMAGE_8BIT_BASE Bitmap; +} EFI_HII_IIBT_IMAGE_8BIT_TRAN_BLOCK; + +typedef struct _EFI_HII_IIBT_DUPLICATE_BLOCK { + EFI_HII_IMAGE_BLOCK Header; + EFI_IMAGE_ID ImageId; +} EFI_HII_IIBT_DUPLICATE_BLOCK; + +typedef struct _EFI_HII_IIBT_JPEG_BLOCK { + EFI_HII_IMAGE_BLOCK Header; + UINT32 Size; + UINT8 Data[1]; +} EFI_HII_IIBT_JPEG_BLOCK; + +typedef struct _EFI_HII_IIBT_SKIP1_BLOCK { + EFI_HII_IMAGE_BLOCK Header; + UINT8 SkipCount; +} EFI_HII_IIBT_SKIP1_BLOCK; + +typedef struct _EFI_HII_IIBT_SKIP2_BLOCK { + EFI_HII_IMAGE_BLOCK Header; + UINT16 SkipCount; +} EFI_HII_IIBT_SKIP2_BLOCK; + +// +// Definitions for Palette Information +// + +typedef struct _EFI_HII_IMAGE_PALETTE_INFO_HEADER { + UINT16 PaletteCount; +} EFI_HII_IMAGE_PALETTE_INFO_HEADER; + +typedef struct _EFI_HII_IMAGE_PALETTE_INFO { + UINT16 PaletteSize; + EFI_HII_RGB_PIXEL PaletteValue[1]; +} EFI_HII_IMAGE_PALETTE_INFO; + +// +// Definitions for Forms Package +// Section 27.3.8 +// + +/// +/// The Forms package is used to carry forms-based encoding data. +/// +typedef struct _EFI_HII_FORM_PACKAGE { + EFI_HII_PACKAGE_HEADER Header; + // EFI_IFR_OP_HEADER OpCodeHeader; + // More op-codes follow +} EFI_HII_FORM_PACKAGE; + +typedef struct { + UINT8 Hour; + UINT8 Minute; + UINT8 Second; +} EFI_HII_TIME; + +typedef struct { + UINT16 Year; + UINT8 Month; + UINT8 Day; +} EFI_HII_DATE; + +typedef union { + UINT8 u8; + UINT16 u16; + UINT32 u32; + UINT64 u64; + BOOLEAN b; + EFI_HII_TIME time; + EFI_HII_DATE date; + EFI_STRING_ID string; +} EFI_IFR_TYPE_VALUE; + +// +// IFR Opcodes +// +#define EFI_IFR_FORM_OP 0x01 +#define EFI_IFR_SUBTITLE_OP 0x02 +#define EFI_IFR_TEXT_OP 0x03 +#define EFI_IFR_IMAGE_OP 0x04 +#define EFI_IFR_ONE_OF_OP 0x05 +#define EFI_IFR_CHECKBOX_OP 0x06 +#define EFI_IFR_NUMERIC_OP 0x07 +#define EFI_IFR_PASSWORD_OP 0x08 +#define EFI_IFR_ONE_OF_OPTION_OP 0x09 +#define EFI_IFR_SUPPRESS_IF_OP 0x0A +#define EFI_IFR_LOCKED_OP 0x0B +#define EFI_IFR_ACTION_OP 0x0C +#define EFI_IFR_RESET_BUTTON_OP 0x0D +#define EFI_IFR_FORM_SET_OP 0x0E +#define EFI_IFR_REF_OP 0x0F +#define EFI_IFR_NO_SUBMIT_IF_OP 0x10 +#define EFI_IFR_INCONSISTENT_IF_OP 0x11 +#define EFI_IFR_EQ_ID_VAL_OP 0x12 +#define EFI_IFR_EQ_ID_ID_OP 0x13 +#define EFI_IFR_EQ_ID_LIST_OP 0x14 +#define EFI_IFR_AND_OP 0x15 +#define EFI_IFR_OR_OP 0x16 +#define EFI_IFR_NOT_OP 0x17 +#define EFI_IFR_RULE_OP 0x18 +#define EFI_IFR_GRAY_OUT_IF_OP 0x19 +#define EFI_IFR_DATE_OP 0x1A +#define EFI_IFR_TIME_OP 0x1B +#define EFI_IFR_STRING_OP 0x1C +#define EFI_IFR_REFRESH_OP 0x1D +#define EFI_IFR_DISABLE_IF_OP 0x1E +#define EFI_IFR_TO_LOWER_OP 0x20 +#define EFI_IFR_TO_UPPER_OP 0x21 +#define EFI_IFR_ORDERED_LIST_OP 0x23 +#define EFI_IFR_VARSTORE_OP 0x24 +#define EFI_IFR_VARSTORE_NAME_VALUE_OP 0x25 +#define EFI_IFR_VARSTORE_EFI_OP 0x26 +#define EFI_IFR_VARSTORE_DEVICE_OP 0x27 +#define EFI_IFR_VERSION_OP 0x28 +#define EFI_IFR_END_OP 0x29 +#define EFI_IFR_MATCH_OP 0x2A +#define EFI_IFR_EQUAL_OP 0x2F +#define EFI_IFR_NOT_EQUAL_OP 0x30 +#define EFI_IFR_GREATER_THAN_OP 0x31 +#define EFI_IFR_GREATER_EQUAL_OP 0x32 +#define EFI_IFR_LESS_THAN_OP 0x33 +#define EFI_IFR_LESS_EQUAL_OP 0x34 +#define EFI_IFR_BITWISE_AND_OP 0x35 +#define EFI_IFR_BITWISE_OR_OP 0x36 +#define EFI_IFR_BITWISE_NOT_OP 0x37 +#define EFI_IFR_SHIFT_LEFT_OP 0x38 +#define EFI_IFR_SHIFT_RIGHT_OP 0x39 +#define EFI_IFR_ADD_OP 0x3A +#define EFI_IFR_SUBTRACT_OP 0x3B +#define EFI_IFR_MULTIPLY_OP 0x3C +#define EFI_IFR_DIVIDE_OP 0x3D +#define EFI_IFR_MODULO_OP 0x3E +#define EFI_IFR_RULE_REF_OP 0x3F +#define EFI_IFR_QUESTION_REF1_OP 0x40 +#define EFI_IFR_QUESTION_REF2_OP 0x41 +#define EFI_IFR_UINT8_OP 0x42 +#define EFI_IFR_UINT16_OP 0x43 +#define EFI_IFR_UINT32_OP 0x44 +#define EFI_IFR_UINT64_OP 0x45 +#define EFI_IFR_TRUE_OP 0x46 +#define EFI_IFR_FALSE_OP 0x47 +#define EFI_IFR_TO_UINT_OP 0x48 +#define EFI_IFR_TO_STRING_OP 0x49 +#define EFI_IFR_TO_BOOLEAN_OP 0x4A +#define EFI_IFR_MID_OP 0x4B +#define EFI_IFR_FIND_OP 0x4C +#define EFI_IFR_TOKEN_OP 0x4D +#define EFI_IFR_STRING_REF1_OP 0x4E +#define EFI_IFR_STRING_REF2_OP 0x4F +#define EFI_IFR_CONDITIONAL_OP 0x50 +#define EFI_IFR_QUESTION_REF3_OP 0x51 +#define EFI_IFR_ZERO_OP 0x52 +#define EFI_IFR_ONE_OP 0x53 +#define EFI_IFR_ONES_OP 0x54 +#define EFI_IFR_UNDEFINED_OP 0x55 +#define EFI_IFR_LENGTH_OP 0x56 +#define EFI_IFR_DUP_OP 0x57 +#define EFI_IFR_THIS_OP 0x58 +#define EFI_IFR_SPAN_OP 0x59 +#define EFI_IFR_VALUE_OP 0x5A +#define EFI_IFR_DEFAULT_OP 0x5B +#define EFI_IFR_DEFAULTSTORE_OP 0x5C +#define EFI_IFR_CATENATE_OP 0x5E +#define EFI_IFR_GUID_OP 0x5F + +// +// Definitions of IFR Standard Headers +// Section 27.3.8.2 +// + +typedef struct _EFI_IFR_OP_HEADER { + UINT8 OpCode; + UINT8 Length:7; + UINT8 Scope:1; +} EFI_IFR_OP_HEADER; + +typedef struct _EFI_IFR_STATEMENT_HEADER { + EFI_STRING_ID Prompt; + EFI_STRING_ID Help; +} EFI_IFR_STATEMENT_HEADER; + +typedef struct _EFI_IFR_QUESTION_HEADER { + EFI_IFR_STATEMENT_HEADER Header; + EFI_QUESTION_ID QuestionId; + EFI_VARSTORE_ID VarStoreId; + union { + EFI_STRING_ID VarName; + UINT16 VarOffset; + } VarStoreInfo; + UINT8 Flags; +} EFI_IFR_QUESTION_HEADER; + +// +// Flag values of EFI_IFR_QUESTION_HEADER +// +#define EFI_IFR_FLAG_READ_ONLY 0x01 +#define EFI_IFR_FLAG_CALLBACK 0x04 +#define EFI_IFR_FLAG_RESET_REQUIRED 0x10 +#define EFI_IFR_FLAG_OPTIONS_ONLY 0x80 + +// +// Definition for Opcode Reference +// Section 27.3.8.3 +// +typedef struct _EFI_IFR_DEFAULTSTORE { + EFI_IFR_OP_HEADER Header; + EFI_STRING_ID DefaultName; + UINT16 DefaultId; +} EFI_IFR_DEFAULTSTORE; + +// +// Default Identifier of default store +// +#define EFI_HII_DEFAULT_CLASS_STANDARD 0x0000 +#define EFI_HII_DEFAULT_CLASS_MANUFACTURING 0x0001 +#define EFI_HII_DEFAULT_CLASS_SAFE 0x0002 +#define EFI_HII_DEFAULT_CLASS_PLATFORM_BEGIN 0x4000 +#define EFI_HII_DEFAULT_CLASS_PLATFORM_END 0x7fff +#define EFI_HII_DEFAULT_CLASS_HARDWARE_BEGIN 0x8000 +#define EFI_HII_DEFAULT_CLASS_HARDWARE_END 0xbfff +#define EFI_HII_DEFAULT_CLASS_FIRMWARE_BEGIN 0xc000 +#define EFI_HII_DEFAULT_CLASS_FIRMWARE_END 0xffff + +typedef struct _EFI_IFR_VARSTORE { + EFI_IFR_OP_HEADER Header; + EFI_GUID Guid; + EFI_VARSTORE_ID VarStoreId; + UINT16 Size; + UINT8 Name[1]; +} EFI_IFR_VARSTORE; + +typedef struct _EFI_IFR_VARSTORE_EFI { + EFI_IFR_OP_HEADER Header; + EFI_VARSTORE_ID VarStoreId; + EFI_GUID Guid; + UINT32 Attributes; +} EFI_IFR_VARSTORE_EFI; + +typedef struct _EFI_IFR_VARSTORE_NAME_VALUE { + EFI_IFR_OP_HEADER Header; + EFI_VARSTORE_ID VarStoreId; + EFI_GUID Guid; +} EFI_IFR_VARSTORE_NAME_VALUE; + +typedef struct _EFI_IFR_FORM_SET { + EFI_IFR_OP_HEADER Header; + EFI_GUID Guid; + EFI_STRING_ID FormSetTitle; + EFI_STRING_ID Help; +} EFI_IFR_FORM_SET; + +typedef struct _EFI_IFR_END { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_END; + +typedef struct _EFI_IFR_FORM { + EFI_IFR_OP_HEADER Header; + UINT16 FormId; + EFI_STRING_ID FormTitle; +} EFI_IFR_FORM; + +typedef struct _EFI_IFR_IMAGE { + EFI_IFR_OP_HEADER Header; + EFI_IMAGE_ID Id; +} EFI_IFR_IMAGE; + +typedef struct _EFI_IFR_LOCKED { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_LOCKED; + +typedef struct _EFI_IFR_RULE { + EFI_IFR_OP_HEADER Header; + UINT8 RuleId; +} EFI_IFR_RULE; + +typedef struct _EFI_IFR_DEFAULT { + EFI_IFR_OP_HEADER Header; + UINT16 DefaultId; + UINT8 Type; + EFI_IFR_TYPE_VALUE Value; +} EFI_IFR_DEFAULT; + +typedef struct _EFI_IFR_VALUE { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_VALUE; + +typedef struct _EFI_IFR_SUBTITLE { + EFI_IFR_OP_HEADER Header; + EFI_IFR_STATEMENT_HEADER Statement; + UINT8 Flags; +} EFI_IFR_SUBTITLE; + +#define EFI_IFR_FLAGS_HORIZONTAL 0x01 + +typedef struct _EFI_IFR_CHECKBOX { + EFI_IFR_OP_HEADER Header; + EFI_IFR_QUESTION_HEADER Question; + UINT8 Flags; +} EFI_IFR_CHECKBOX; + +#define EFI_IFR_CHECKBOX_DEFAULT 0x01 +#define EFI_IFR_CHECKBOX_DEFAULT_MFG 0x02 + +typedef struct _EFI_IFR_TEXT { + EFI_IFR_OP_HEADER Header; + EFI_IFR_STATEMENT_HEADER Statement; + EFI_STRING_ID TextTwo; +} EFI_IFR_TEXT; + +typedef struct _EFI_IFR_REF { + EFI_IFR_OP_HEADER Header; + EFI_IFR_QUESTION_HEADER Question; + EFI_FORM_ID FormId; +} EFI_IFR_REF; + +typedef struct _EFI_IFR_REF2 { + EFI_IFR_OP_HEADER Header; + EFI_IFR_QUESTION_HEADER Question; + EFI_FORM_ID FormId; + EFI_QUESTION_ID QuestionId; +} EFI_IFR_REF2; + +typedef struct _EFI_IFR_REF3 { + EFI_IFR_OP_HEADER Header; + EFI_IFR_QUESTION_HEADER Question; + EFI_FORM_ID FormId; + EFI_QUESTION_ID QuestionId; + EFI_GUID FormSetId; +} EFI_IFR_REF3; + +typedef struct _EFI_IFR_REF4 { + EFI_IFR_OP_HEADER Header; + EFI_IFR_QUESTION_HEADER Question; + EFI_FORM_ID FormId; + EFI_QUESTION_ID QuestionId; + EFI_GUID FormSetId; + EFI_STRING_ID DevicePath; +} EFI_IFR_REF4; + +typedef struct _EFI_IFR_RESET_BUTTON { + EFI_IFR_OP_HEADER Header; + EFI_IFR_QUESTION_HEADER Question; + EFI_DEFAULT_ID DefaultId; +} EFI_IFR_RESET_BUTTON; + +typedef struct _EFI_IFR_ACTION { + EFI_IFR_OP_HEADER Header; + EFI_IFR_QUESTION_HEADER Question; + EFI_STRING_ID QuestionConfig; +} EFI_IFR_ACTION; + +typedef struct _EFI_IFR_ACTION_1 { + EFI_IFR_OP_HEADER Header; + EFI_IFR_QUESTION_HEADER Question; +} EFI_IFR_ACTION_1; + +typedef struct _EFI_IFR_DATE { + EFI_IFR_OP_HEADER Header; + EFI_IFR_QUESTION_HEADER Question; + UINT8 Flags; +} EFI_IFR_DATE; + +// +// Flags that describe the behavior of the question. +// +#define EFI_QF_DATE_YEAR_SUPPRESS 0x01 +#define EFI_QF_DATE_MONTH_SUPPRESS 0x02 +#define EFI_QF_DATE_DAY_SUPPRESS 0x04 + +#define EFI_QF_DATE_STORAGE 0x30 +#define QF_DATE_STORAGE_NORMAL 0x00 +#define QF_DATE_STORAGE_TIME 0x10 +#define QF_DATE_STORAGE_WAKEUP 0x20 + +typedef union { + struct { + UINT8 MinValue; + UINT8 MaxValue; + UINT8 Step; + } u8; + struct { + UINT16 MinValue; + UINT16 MaxValue; + UINT16 Step; + } u16; + struct { + UINT32 MinValue; + UINT32 MaxValue; + UINT32 Step; + } u32; + struct { + UINT64 MinValue; + UINT64 MaxValue; + UINT64 Step; + } u64; +} MINMAXSTEP_DATA; + +typedef struct _EFI_IFR_NUMERIC { + EFI_IFR_OP_HEADER Header; + EFI_IFR_QUESTION_HEADER Question; + UINT8 Flags; + MINMAXSTEP_DATA data; +} EFI_IFR_NUMERIC; + +// +// Flags related to the numeric question +// +#define EFI_IFR_NUMERIC_SIZE 0x03 +#define EFI_IFR_NUMERIC_SIZE_1 0x00 +#define EFI_IFR_NUMERIC_SIZE_2 0x01 +#define EFI_IFR_NUMERIC_SIZE_4 0x02 +#define EFI_IFR_NUMERIC_SIZE_8 0x03 + +#define EFI_IFR_DISPLAY 0x30 +#define EFI_IFR_DISPLAY_INT_DEC 0x00 +#define EFI_IFR_DISPLAY_UINT_DEC 0x10 +#define EFI_IFR_DISPLAY_UINT_HEX 0x20 + +typedef struct _EFI_IFR_ONE_OF { + EFI_IFR_OP_HEADER Header; + EFI_IFR_QUESTION_HEADER Question; + UINT8 Flags; + MINMAXSTEP_DATA data; +} EFI_IFR_ONE_OF; + +typedef struct _EFI_IFR_STRING { + EFI_IFR_OP_HEADER Header; + EFI_IFR_QUESTION_HEADER Question; + UINT8 MinSize; + UINT8 MaxSize; + UINT8 Flags; +} EFI_IFR_STRING; + +#define EFI_IFR_STRING_MULTI_LINE 0x01 + +typedef struct _EFI_IFR_PASSWORD { + EFI_IFR_OP_HEADER Header; + EFI_IFR_QUESTION_HEADER Question; + UINT16 MinSize; + UINT16 MaxSize; +} EFI_IFR_PASSWORD; + +typedef struct _EFI_IFR_ORDERED_LIST { + EFI_IFR_OP_HEADER Header; + EFI_IFR_QUESTION_HEADER Question; + UINT8 MaxContainers; + UINT8 Flags; +} EFI_IFR_ORDERED_LIST; + +#define EFI_IFR_UNIQUE_SET 0x01 +#define EFI_IFR_NO_EMPTY_SET 0x02 + +typedef struct _EFI_IFR_TIME { + EFI_IFR_OP_HEADER Header; + EFI_IFR_QUESTION_HEADER Question; + UINT8 Flags; +} EFI_IFR_TIME; + +// +// A bit-mask that determines which unique settings are active for this opcode. +// +#define QF_TIME_HOUR_SUPPRESS 0x01 +#define QF_TIME_MINUTE_SUPPRESS 0x02 +#define QF_TIME_SECOND_SUPPRESS 0x04 + +#define QF_TIME_STORAGE 0x30 +#define QF_TIME_STORAGE_NORMAL 0x00 +#define QF_TIME_STORAGE_TIME 0x10 +#define QF_TIME_STORAGE_WAKEUP 0x20 + +typedef struct _EFI_IFR_DISABLE_IF { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_DISABLE_IF; + +typedef struct _EFI_IFR_SUPPRESS_IF { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_SUPPRESS_IF; + +typedef struct _EFI_IFR_GRAY_OUT_IF { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_GRAY_OUT_IF; + +typedef struct _EFI_IFR_INCONSISTENT_IF { + EFI_IFR_OP_HEADER Header; + EFI_STRING_ID Error; +} EFI_IFR_INCONSISTENT_IF; + +typedef struct _EFI_IFR_NO_SUBMIT_IF { + EFI_IFR_OP_HEADER Header; + EFI_STRING_ID Error; +} EFI_IFR_NO_SUBMIT_IF; + +typedef struct _EFI_IFR_REFRESH { + EFI_IFR_OP_HEADER Header; + UINT8 RefreshInterval; +} EFI_IFR_REFRESH; + +typedef struct _EFI_IFR_VARSTORE_DEVICE { + EFI_IFR_OP_HEADER Header; + EFI_STRING_ID DevicePath; +} EFI_IFR_VARSTORE_DEVICE; + +typedef struct _EFI_IFR_ONE_OF_OPTION { + EFI_IFR_OP_HEADER Header; + EFI_STRING_ID Option; + UINT8 Flags; + UINT8 Type; + EFI_IFR_TYPE_VALUE Value; +} EFI_IFR_ONE_OF_OPTION; + +// +// Types of the option's value. +// +#define EFI_IFR_TYPE_NUM_SIZE_8 0x00 +#define EFI_IFR_TYPE_NUM_SIZE_16 0x01 +#define EFI_IFR_TYPE_NUM_SIZE_32 0x02 +#define EFI_IFR_TYPE_NUM_SIZE_64 0x03 +#define EFI_IFR_TYPE_BOOLEAN 0x04 +#define EFI_IFR_TYPE_TIME 0x05 +#define EFI_IFR_TYPE_DATE 0x06 +#define EFI_IFR_TYPE_STRING 0x07 +#define EFI_IFR_TYPE_OTHER 0x08 + +#define EFI_IFR_OPTION_DEFAULT 0x10 +#define EFI_IFR_OPTION_DEFAULT_MFG 0x20 + +typedef struct _EFI_IFR_GUID { + EFI_IFR_OP_HEADER Header; + EFI_GUID Guid; + //Optional Data Follows +} EFI_IFR_GUID; + +typedef struct _EFI_IFR_DUP { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_DUP; + +typedef struct _EFI_IFR_EQ_ID_ID { + EFI_IFR_OP_HEADER Header; + EFI_QUESTION_ID QuestionId1; + EFI_QUESTION_ID QuestionId2; +} EFI_IFR_EQ_ID_ID; + +typedef struct _EFI_IFR_EQ_ID_VAL { + EFI_IFR_OP_HEADER Header; + EFI_QUESTION_ID QuestionId; + UINT16 Value; +} EFI_IFR_EQ_ID_VAL; + +typedef struct _EFI_IFR_EQ_ID_LIST { + EFI_IFR_OP_HEADER Header; + EFI_QUESTION_ID QuestionId; + UINT16 ListLength; + UINT16 ValueList[1]; +} EFI_IFR_EQ_ID_LIST; + +typedef struct _EFI_IFR_UINT8 { + EFI_IFR_OP_HEADER Header; + UINT8 Value; +} EFI_IFR_UINT8; + +typedef struct _EFI_IFR_UINT16 { + EFI_IFR_OP_HEADER Header; + UINT16 Value; +} EFI_IFR_UINT16; + +typedef struct _EFI_IFR_UINT32 { + EFI_IFR_OP_HEADER Header; + UINT32 Value; +} EFI_IFR_UINT32; + +typedef struct _EFI_IFR_UINT64 { + EFI_IFR_OP_HEADER Header; + UINT64 Value; +} EFI_IFR_UINT64; + +typedef struct _EFI_IFR_QUESTION_REF1 { + EFI_IFR_OP_HEADER Header; + EFI_QUESTION_ID QuestionId; +} EFI_IFR_QUESTION_REF1; + +typedef struct _EFI_IFR_QUESTION_REF2 { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_QUESTION_REF2; + +typedef struct _EFI_IFR_QUESTION_REF3 { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_QUESTION_REF3; + +typedef struct _EFI_IFR_QUESTION_REF3_2 { + EFI_IFR_OP_HEADER Header; + EFI_STRING_ID DevicePath; +} EFI_IFR_QUESTION_REF3_2; + +typedef struct _EFI_IFR_QUESTION_REF3_3 { + EFI_IFR_OP_HEADER Header; + EFI_STRING_ID DevicePath; + EFI_GUID Guid; +} EFI_IFR_QUESTION_REF3_3; + +typedef struct _EFI_IFR_RULE_REF { + EFI_IFR_OP_HEADER Header; + UINT8 RuleId; +} EFI_IFR_RULE_REF; + +typedef struct _EFI_IFR_STRING_REF1 { + EFI_IFR_OP_HEADER Header; + EFI_STRING_ID StringId; +} EFI_IFR_STRING_REF1; + +typedef struct _EFI_IFR_STRING_REF2 { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_STRING_REF2; + +typedef struct _EFI_IFR_THIS { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_THIS; + +typedef struct _EFI_IFR_TRUE { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_TRUE; + +typedef struct _EFI_IFR_FALSE { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_FALSE; + +typedef struct _EFI_IFR_ONE { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_ONE; + +typedef struct _EFI_IFR_ONES { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_ONES; + +typedef struct _EFI_IFR_ZERO { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_ZERO; + +typedef struct _EFI_IFR_UNDEFINED { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_UNDEFINED; + +typedef struct _EFI_IFR_VERSION { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_VERSION; + +typedef struct _EFI_IFR_LENGTH { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_LENGTH; + +typedef struct _EFI_IFR_NOT { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_NOT; + +typedef struct _EFI_IFR_BITWISE_NOT { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_BITWISE_NOT; + +typedef struct _EFI_IFR_TO_BOOLEAN { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_TO_BOOLEAN; + +// +// For EFI_IFR_TO_STRING, when converting from +// unsigned integers, these flags control the format: +// 0 = unsigned decimal +// 1 = signed decimal +// 2 = hexadecimal (lower-case alpha) +// 3 = hexadecimal (upper-case alpha) +// +#define EFI_IFR_STRING_UNSIGNED_DEC 0 +#define EFI_IFR_STRING_SIGNED_DEC 1 +#define EFI_IFR_STRING_LOWERCASE_HEX 2 +#define EFI_IFR_STRING_UPPERCASE_HEX 3 +// +// When converting from a buffer, these flags control the format: +// 0 = ASCII +// 8 = Unicode +// +#define EFI_IFR_STRING_ASCII 0 +#define EFI_IFR_STRING_UNICODE 8 + +typedef struct _EFI_IFR_TO_STRING { + EFI_IFR_OP_HEADER Header; + UINT8 Format; +} EFI_IFR_TO_STRING; + +typedef struct _EFI_IFR_TO_UINT { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_TO_UINT; + +typedef struct _EFI_IFR_TO_UPPER { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_TO_UPPER; + +typedef struct _EFI_IFR_TO_LOWER { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_TO_LOWER; + +typedef struct _EFI_IFR_ADD { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_ADD; + +typedef struct _EFI_IFR_AND { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_AND; + +typedef struct _EFI_IFR_BITWISE_AND { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_BITWISE_AND; + +typedef struct _EFI_IFR_BITWISE_OR { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_BITWISE_OR; + +typedef struct _EFI_IFR_CATENATE { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_CATENATE; + +typedef struct _EFI_IFR_DIVIDE { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_DIVIDE; + +typedef struct _EFI_IFR_EQUAL { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_EQUAL; + +typedef struct _EFI_IFR_GREATER_EQUAL { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_GREATER_EQUAL; + +typedef struct _EFI_IFR_GREATER_THAN { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_GREATER_THAN; + +typedef struct _EFI_IFR_LESS_EQUAL { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_LESS_EQUAL; + +typedef struct _EFI_IFR_LESS_THAN { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_LESS_THAN; + +typedef struct _EFI_IFR_MATCH { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_MATCH; + +typedef struct _EFI_IFR_MULTIPLY { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_MULTIPLY; + +typedef struct _EFI_IFR_MODULO { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_MODULO; + +typedef struct _EFI_IFR_NOT_EQUAL { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_NOT_EQUAL; + +typedef struct _EFI_IFR_OR { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_OR; + +typedef struct _EFI_IFR_SHIFT_LEFT { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_SHIFT_LEFT; + +typedef struct _EFI_IFR_SHIFT_RIGHT { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_SHIFT_RIGHT; + +typedef struct _EFI_IFR_SUBTRACT { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_SUBTRACT; + +typedef struct _EFI_IFR_CONDITIONAL { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_CONDITIONAL; + +// +// Flags governing the matching criteria of EFI_IFR_FIND +// +#define EFI_IFR_FF_CASE_SENSITIVE 0x00 +#define EFI_IFR_FF_CASE_INSENSITIVE 0x01 + +typedef struct _EFI_IFR_FIND { + EFI_IFR_OP_HEADER Header; + UINT8 Format; +} EFI_IFR_FIND; + +typedef struct _EFI_IFR_MID { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_MID; + +typedef struct _EFI_IFR_TOKEN { + EFI_IFR_OP_HEADER Header; +} EFI_IFR_TOKEN; + +// +// Flags specifying whether to find the first matching string +// or the first non-matching string. +// +#define EFI_IFR_FLAGS_FIRST_MATCHING 0x00 +#define EFI_IFR_FLAGS_FIRST_NON_MATCHING 0x01 + +typedef struct _EFI_IFR_SPAN { + EFI_IFR_OP_HEADER Header; + UINT8 Flags; +} EFI_IFR_SPAN; + +// +// Definitions for Keyboard Package +// Section 27.3.9 +// Releated definitions are in Section of EFI_HII_DATABASE_PROTOCOL +// + +typedef enum { + EfiKeyLCtrl, + EfiKeyA0, + EfiKeyLAlt, + EfiKeySpaceBar, + EfiKeyA2, + EfiKeyA3, + EfiKeyA4, + EfiKeyRCtrl, + EfiKeyLeftArrow, + EfiKeyDownArrow, + EfiKeyRightArrow, + EfiKeyZero, + EfiKeyPeriod, + EfiKeyEnter, + EfiKeyLShift, + EfiKeyB0, + EfiKeyB1, + EfiKeyB2, + EfiKeyB3, + EfiKeyB4, + EfiKeyB5, + EfiKeyB6, + EfiKeyB7, + EfiKeyB8, + EfiKeyB9, + EfiKeyB10, + EfiKeyRShift, + EfiKeyUpArrow, + EfiKeyOne, + EfiKeyTwo, + EfiKeyThree, + EfiKeyCapsLock, + EfiKeyC1, + EfiKeyC2, + EfiKeyC3, + EfiKeyC4, + EfiKeyC5, + EfiKeyC6, + EfiKeyC7, + EfiKeyC8, + EfiKeyC9, + EfiKeyC10, + EfiKeyC11, + EfiKeyC12, + EfiKeyFour, + EfiKeyFive, + EfiKeySix, + EfiKeyPlus, + EfiKeyTab, + EfiKeyD1, + EfiKeyD2, + EfiKeyD3, + EfiKeyD4, + EfiKeyD5, + EfiKeyD6, + EfiKeyD7, + EfiKeyD8, + EfiKeyD9, + EfiKeyD10, + EfiKeyD11, + EfiKeyD12, + EfiKeyD13, + EfiKeyDel, + EfiKeyEnd, + EfiKeyPgDn, + EfiKeySeven, + EfiKeyEight, + EfiKeyNine, + EfiKeyE0, + EfiKeyE1, + EfiKeyE2, + EfiKeyE3, + EfiKeyE4, + EfiKeyE5, + EfiKeyE6, + EfiKeyE7, + EfiKeyE8, + EfiKeyE9, + EfiKeyE10, + EfiKeyE11, + EfiKeyE12, + EfiKeyBackSpace, + EfiKeyIns, + EfiKeyHome, + EfiKeyPgUp, + EfiKeyNLck, + EfiKeySlash, + EfiKeyAsterisk, + EfiKeyMinus, + EfiKeyEsc, + EfiKeyF1, + EfiKeyF2, + EfiKeyF3, + EfiKeyF4, + EfiKeyF5, + EfiKeyF6, + EfiKeyF7, + EfiKeyF8, + EfiKeyF9, + EfiKeyF10, + EfiKeyF11, + EfiKeyF12, + EfiKeyPrint, + EfiKeySLck, + EfiKeyPause +} EFI_KEY; + +typedef struct { + EFI_KEY Key; + CHAR16 Unicode; + CHAR16 ShiftedUnicode; + CHAR16 AltGrUnicode; + CHAR16 ShiftedAltGrUnicode; + UINT16 Modifier; + UINT16 AffectedAttribute; +} EFI_KEY_DESCRIPTOR; + +/// +/// A key which is affected by all the standard shift modifiers. +/// Most keys would be expected to have this bit active. +/// +#define EFI_AFFECTED_BY_STANDARD_SHIFT 0x0001 + +/// +/// This key is affected by the caps lock so that if a keyboard driver +/// would need to disambiguate between a key which had a "1" defined +/// versus a "a" character. Having this bit turned on would tell +/// the keyboard driver to use the appropriate shifted state or not. +/// +#define EFI_AFFECTED_BY_CAPS_LOCK 0x0002 + +/// +/// Similar to the case of CAPS lock, if this bit is active, the key +/// is affected by the num lock being turned on. +/// +#define EFI_AFFECTED_BY_NUM_LOCK 0x0004 + +typedef struct { + UINT16 LayoutLength; + EFI_GUID Guid; + UINT32 LayoutDescriptorStringOffset; + UINT8 DescriptorCount; + // EFI_KEY_DESCRIPTOR Descriptors[]; +} EFI_HII_KEYBOARD_LAYOUT; + +typedef struct { + EFI_HII_PACKAGE_HEADER Header; + UINT16 LayoutCount; + // EFI_HII_KEYBOARD_LAYOUT Layout[]; +} EFI_HII_KEYBOARD_PACKAGE_HDR; + +// +// Modifier values +// +#define EFI_NULL_MODIFIER 0x0000 +#define EFI_LEFT_CONTROL_MODIFIER 0x0001 +#define EFI_RIGHT_CONTROL_MODIFIER 0x0002 +#define EFI_LEFT_ALT_MODIFIER 0x0003 +#define EFI_RIGHT_ALT_MODIFIER 0x0004 +#define EFI_ALT_GR_MODIFIER 0x0005 +#define EFI_INSERT_MODIFIER 0x0006 +#define EFI_DELETE_MODIFIER 0x0007 +#define EFI_PAGE_DOWN_MODIFIER 0x0008 +#define EFI_PAGE_UP_MODIFIER 0x0009 +#define EFI_HOME_MODIFIER 0x000A +#define EFI_END_MODIFIER 0x000B +#define EFI_LEFT_SHIFT_MODIFIER 0x000C +#define EFI_RIGHT_SHIFT_MODIFIER 0x000D +#define EFI_CAPS_LOCK_MODIFIER 0x000E +#define EFI_NUM_LOCK_MODIFIER 0x000F +#define EFI_LEFT_ARROW_MODIFIER 0x0010 +#define EFI_RIGHT_ARROW_MODIFIER 0x0011 +#define EFI_DOWN_ARROW_MODIFIER 0x0012 +#define EFI_UP_ARROW_MODIFIER 0x0013 +#define EFI_NS_KEY_MODIFIER 0x0014 +#define EFI_NS_KEY_DEPENDENCY_MODIFIER 0x0015 +#define EFI_FUNCTION_KEY_ONE_MODIFIER 0x0016 +#define EFI_FUNCTION_KEY_TWO_MODIFIER 0x0017 +#define EFI_FUNCTION_KEY_THREE_MODIFIER 0x0018 +#define EFI_FUNCTION_KEY_FOUR_MODIFIER 0x0019 +#define EFI_FUNCTION_KEY_FIVE_MODIFIER 0x001A +#define EFI_FUNCTION_KEY_SIX_MODIFIER 0x001B +#define EFI_FUNCTION_KEY_SEVEN_MODIFIER 0x001C +#define EFI_FUNCTION_KEY_EIGHT_MODIFIER 0x001D +#define EFI_FUNCTION_KEY_NINE_MODIFIER 0x001E +#define EFI_FUNCTION_KEY_TEN_MODIFIER 0x001F +#define EFI_FUNCTION_KEY_ELEVEN_MODIFIER 0x0020 +#define EFI_FUNCTION_KEY_TWELVE_MODIFIER 0x0021 + +// +// Keys that have multiple control functions based on modifier +// settings are handled in the keyboard driver implementation. +// For instance PRINT_KEY might have a modifier held down and +// is still a nonprinting character, but might have an alternate +// control function like SYSREQUEST +// +#define EFI_PRINT_MODIFIER 0x0022 +#define EFI_SYS_REQUEST_MODIFIER 0x0023 +#define EFI_SCROLL_LOCK_MODIFIER 0x0024 +#define EFI_PAUSE_MODIFIER 0x0025 +#define EFI_BREAK_MODIFIER 0x0026 + +#define EFI_LEFT_LOGO_MODIFIER 0x0027 +#define EFI_RIGHT_LOGO_MODIFIER 0x0028 +#define EFI_MENU_MODIFIER 0x0029 + +#pragma pack() + + + +/// +/// References to string tokens must use this macro to enable scanning for +/// token usages. +/// +/// +/// STRING_TOKEN is not defined in UEFI specification. But it is placed +/// here for the easy access by C files and VFR source files. +/// +#define STRING_TOKEN(t) t + +#endif diff --git a/gpxe/src/include/gpxe/efi/Uefi/UefiMultiPhase.h b/gpxe/src/include/gpxe/efi/Uefi/UefiMultiPhase.h new file mode 100644 index 0000000..2d1af5e --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Uefi/UefiMultiPhase.h @@ -0,0 +1,210 @@ +/** @file + This includes some definitions introduced in UEFI that will be used in both PEI and DXE phases. + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __UEFI_MULTIPHASE_H__ +#define __UEFI_MULTIPHASE_H__ + +#include + +/// +/// Enumeration of memory types introduced in UEFI. +/// +typedef enum { + EfiReservedMemoryType, + EfiLoaderCode, + EfiLoaderData, + EfiBootServicesCode, + EfiBootServicesData, + EfiRuntimeServicesCode, + EfiRuntimeServicesData, + EfiConventionalMemory, + EfiUnusableMemory, + EfiACPIReclaimMemory, + EfiACPIMemoryNVS, + EfiMemoryMappedIO, + EfiMemoryMappedIOPortSpace, + EfiPalCode, + EfiMaxMemoryType +} EFI_MEMORY_TYPE; + + +/// +/// Data structure that precedes all of the standard EFI table types. +/// +typedef struct { + UINT64 Signature; + UINT32 Revision; + UINT32 HeaderSize; + UINT32 CRC32; + UINT32 Reserved; +} EFI_TABLE_HEADER; + +/// +/// Attributes of variable. +/// +#define EFI_VARIABLE_NON_VOLATILE 0x00000001 +#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002 +#define EFI_VARIABLE_RUNTIME_ACCESS 0x00000004 +#define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x00000008 + +/// +/// This attribute is identified by the mnemonic 'HR' +/// elsewhere in this specification. +/// +#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x00000010 + +// +// _WIN_CERTIFICATE.wCertificateType +// +#define WIN_CERT_TYPE_EFI_PKCS115 0x0EF0 +#define WIN_CERT_TYPE_EFI_GUID 0x0EF1 + +/// +/// The WIN_CERTIFICATE structure is part of the PE/COFF specification. +/// +typedef struct _WIN_CERTIFICATE { + /// + /// The length of the entire certificate, + /// including the length of the header, in bytes. + /// + UINT32 dwLength; + /// + /// The revision level of the WIN_CERTIFICATE + /// structure. The current revision level is 0x0200. + /// + UINT16 wRevision; + /// + /// The certificate type. See WIN_CERT_TYPE_xxx for the UEFI + /// certificate types. The UEFI specification reserves the range of + /// certificate type values from 0x0EF0 to 0x0EFF. + /// + UINT16 wCertificateType; + /// + /// The following is the actual certificate. The format of + /// the certificate depends on wCertificateType. + /// + /// UINT8 bCertificate[ANYSIZE_ARRAY]; + /// +} WIN_CERTIFICATE; + +/// +/// WIN_CERTIFICATE_UEFI_GUID.CertType +/// +#define EFI_CERT_TYPE_RSA2048_SHA256_GUID \ + {0xa7717414, 0xc616, 0x4977, {0x94, 0x20, 0x84, 0x47, 0x12, 0xa7, 0x35, 0xbf } } + +// +// WIN_CERTIFICATE_UEFI_GUID.CertData +// +typedef struct _EFI_CERT_BLOCK_RSA_2048_SHA256 { + UINT32 HashType; + UINT8 PublicKey[256]; + UINT8 Signature[256]; +} EFI_CERT_BLOCK_RSA_2048_SHA256; + + +/// +/// Certificate which encapsulates a GUID-specific digital signature +/// +typedef struct _WIN_CERTIFICATE_UEFI_GUID { + /// + /// This is the standard WIN_CERTIFICATE header, where + /// wCertificateType is set to WIN_CERT_TYPE_UEFI_GUID. + /// + WIN_CERTIFICATE Hdr; + /// + /// This is the unique id which determines the + /// format of the CertData. In this case, the + /// value is EFI_CERT_TYPE_RSA2048_SHA256_GUID. + /// + EFI_GUID CertType; + /// + /// The following is the certificate data. The format of + /// the data is determined by the CertType. In this case the value is + /// EFI_CERT_BLOCK_RSA_2048_SHA256. + /// + /// UINT8 CertData[ANYSIZE_ARRAY]; + /// +} WIN_CERTIFICATE_UEFI_GUID; + + +/// +/// Certificate which encapsulates the RSASSA_PKCS1-v1_5 digital signature. +/// +/// The WIN_CERTIFICATE_UEFI_PKCS1_15 structure is derived from +/// WIN_CERTIFICATE and encapsulate the information needed to +/// implement the RSASSA-PKCS1-v1_5 digital signature algorithm as +/// specified in RFC2437. +/// +typedef struct _WIN_CERTIFICATE_EFI_PKCS1_15 { + /// + /// This is the standard WIN_CERTIFICATE header, where + /// wCertificateType is set to WIN_CERT_TYPE_UEFI_PKCS1_15. + /// + WIN_CERTIFICATE Hdr; + /// + /// This is the hashing algorithm which was performed on the + /// UEFI executable when creating the digital signature. + /// + EFI_GUID HashAlgorithm; + /// + /// The following is the actual digital signature. The + /// size of the signature is the same size as the key + /// (1024-bit key is 128 bytes) and can be determined by + /// subtracting the length of the other parts of this header + /// from the total length of the certificate as found in + /// Hdr.dwLength. + /// + /// UINT8 Signature[ANYSIZE_ARRAY]; + /// +} WIN_CERTIFICATE_EFI_PKCS1_15; + + + +/// +/// AuthInfo is a WIN_CERTIFICATE using the wCertificateType +/// WIN_CERTIFICATE_UEFI_GUID and the CertType +/// EFI_CERT_TYPE_RSA2048_SHA256. If the attribute specifies +/// authenticated access, then the Data buffer should begin with an +/// authentication descriptor prior to the data payload and DataSize +/// should reflect the the data.and descriptor size. The caller +/// shall digest the Monotonic Count value and the associated data +/// for the variable update using the SHA-256 1-way hash algorithm. +/// The ensuing the 32-byte digest will be signed using the private +/// key associated w/ the public/private 2048-bit RSA key-pair. The +/// WIN_CERTIFICATE shall be used to describe the signature of the +/// Variable data *Data. In addition, the signature will also +/// include the MonotonicCount value to guard against replay attacks +/// +typedef struct { + /// + /// Included in the signature of + /// AuthInfo.Used to ensure freshness/no + /// replay. Incremented during each + /// "Write" access. + /// + UINT64 MonotonicCount; + /// + /// Provides the authorization for the variable + /// access. It is a signature across the + /// variable data and the Monotonic Count + /// value. Caller uses Private key that is + /// associated with a public key that has been + /// provisioned via the key exchange. + /// + WIN_CERTIFICATE_UEFI_GUID AuthInfo; +} EFI_VARIABLE_AUTHENTICATION; + +#endif + diff --git a/gpxe/src/include/gpxe/efi/Uefi/UefiPxe.h b/gpxe/src/include/gpxe/efi/Uefi/UefiPxe.h new file mode 100644 index 0000000..bb8ab41 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Uefi/UefiPxe.h @@ -0,0 +1,1756 @@ +/** @file + This header file contains all of the PXE type definitions, + structure prototypes, global variables and constants that + are needed for porting PXE to EFI. + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + + @par Revision Reference: + 32/64-bit PXE specification: + alpha-4, 99-Dec-17 + +**/ + +#ifndef __EFI_PXE_H__ +#define __EFI_PXE_H__ + +#pragma pack(1) + +#define PXE_BUSTYPE(a, b, c, d) \ + ( \ + (((PXE_UINT32) (d) & 0xFF) << 24) | (((PXE_UINT32) (c) & 0xFF) << 16) | (((PXE_UINT32) (b) & 0xFF) << 8) | \ + ((PXE_UINT32) (a) & 0xFF) \ + ) + +/// +/// UNDI ROM ID and devive ID signature +/// +#define PXE_BUSTYPE_PXE PXE_BUSTYPE ('!', 'P', 'X', 'E') + +/// +/// BUS ROM ID signatures +/// +#define PXE_BUSTYPE_PCI PXE_BUSTYPE ('P', 'C', 'I', 'R') +#define PXE_BUSTYPE_PC_CARD PXE_BUSTYPE ('P', 'C', 'C', 'R') +#define PXE_BUSTYPE_USB PXE_BUSTYPE ('U', 'S', 'B', 'R') +#define PXE_BUSTYPE_1394 PXE_BUSTYPE ('1', '3', '9', '4') + +#define PXE_SWAP_UINT16(n) ((((PXE_UINT16) (n) & 0x00FF) << 8) | (((PXE_UINT16) (n) & 0xFF00) >> 8)) + +#define PXE_SWAP_UINT32(n) \ + ((((PXE_UINT32)(n) & 0x000000FF) << 24) | \ + (((PXE_UINT32)(n) & 0x0000FF00) << 8) | \ + (((PXE_UINT32)(n) & 0x00FF0000) >> 8) | \ + (((PXE_UINT32)(n) & 0xFF000000) >> 24)) + +#define PXE_SWAP_UINT64(n) \ + ((((PXE_UINT64)(n) & 0x00000000000000FFULL) << 56) | \ + (((PXE_UINT64)(n) & 0x000000000000FF00ULL) << 40) | \ + (((PXE_UINT64)(n) & 0x0000000000FF0000ULL) << 24) | \ + (((PXE_UINT64)(n) & 0x00000000FF000000ULL) << 8) | \ + (((PXE_UINT64)(n) & 0x000000FF00000000ULL) >> 8) | \ + (((PXE_UINT64)(n) & 0x0000FF0000000000ULL) >> 24) | \ + (((PXE_UINT64)(n) & 0x00FF000000000000ULL) >> 40) | \ + (((PXE_UINT64)(n) & 0xFF00000000000000ULL) >> 56)) + + +#define PXE_CPBSIZE_NOT_USED 0 ///< zero +#define PXE_DBSIZE_NOT_USED 0 ///< zero +#define PXE_CPBADDR_NOT_USED (PXE_UINT64) 0 ///< zero +#define PXE_DBADDR_NOT_USED (PXE_UINT64) 0 ///< zero +#define PXE_CONST CONST + +#define PXE_VOLATILE volatile + +typedef VOID PXE_VOID; +typedef UINT8 PXE_UINT8; +typedef UINT16 PXE_UINT16; +typedef UINT32 PXE_UINT32; +typedef UINTN PXE_UINTN; + +/// +/// typedef unsigned long PXE_UINT64; +/// +typedef UINT64 PXE_UINT64; + +typedef PXE_UINT8 PXE_BOOL; +#define PXE_FALSE 0 ///< zero +#define PXE_TRUE (!PXE_FALSE) + +typedef PXE_UINT16 PXE_OPCODE; + +/// +/// Return UNDI operational state. +/// +#define PXE_OPCODE_GET_STATE 0x0000 + +/// +/// Change UNDI operational state from Stopped to Started. +/// +#define PXE_OPCODE_START 0x0001 + +/// +/// Change UNDI operational state from Started to Stopped. +/// +#define PXE_OPCODE_STOP 0x0002 + +/// +/// Get UNDI initialization information. +/// +#define PXE_OPCODE_GET_INIT_INFO 0x0003 + +/// +/// Get NIC configuration information. +/// +#define PXE_OPCODE_GET_CONFIG_INFO 0x0004 + +/// +/// Changed UNDI operational state from Started to Initialized. +/// +#define PXE_OPCODE_INITIALIZE 0x0005 + +/// +/// Re-initialize the NIC H/W. +/// +#define PXE_OPCODE_RESET 0x0006 + +/// +/// Change the UNDI operational state from Initialized to Started. +/// +#define PXE_OPCODE_SHUTDOWN 0x0007 + +/// +/// Read & change state of external interrupt enables. +/// +#define PXE_OPCODE_INTERRUPT_ENABLES 0x0008 + +/// +/// Read & change state of packet receive filters. +/// +#define PXE_OPCODE_RECEIVE_FILTERS 0x0009 + +/// +/// Read & change station MAC address. +/// +#define PXE_OPCODE_STATION_ADDRESS 0x000A + +/// +/// Read traffic statistics. +/// +#define PXE_OPCODE_STATISTICS 0x000B + +/// +/// Convert multicast IP address to multicast MAC address. +/// +#define PXE_OPCODE_MCAST_IP_TO_MAC 0x000C + +/// +/// Read or change non-volatile storage on the NIC. +/// +#define PXE_OPCODE_NVDATA 0x000D + +/// +/// Get & clear interrupt status. +/// +#define PXE_OPCODE_GET_STATUS 0x000E + +/// +/// Fill media header in packet for transmit. +/// +#define PXE_OPCODE_FILL_HEADER 0x000F + +/// +/// Transmit packet(s). +/// +#define PXE_OPCODE_TRANSMIT 0x0010 + +/// +/// Receive packet. +/// +#define PXE_OPCODE_RECEIVE 0x0011 + +/// +/// Last valid PXE UNDI OpCode number. +/// +#define PXE_OPCODE_LAST_VALID 0x0011 + +typedef PXE_UINT16 PXE_OPFLAGS; + +#define PXE_OPFLAGS_NOT_USED 0x0000 + +// +// ////////////////////////////////////// +// UNDI Get State +// +// No OpFlags + +//////////////////////////////////////// +// UNDI Start +// +// No OpFlags + +//////////////////////////////////////// +// UNDI Stop +// +// No OpFlags + +//////////////////////////////////////// +// UNDI Get Init Info +// +// No Opflags + +//////////////////////////////////////// +// UNDI Get Config Info +// +// No Opflags + +/// +/// UNDI Initialize +/// +#define PXE_OPFLAGS_INITIALIZE_CABLE_DETECT_MASK 0x0001 +#define PXE_OPFLAGS_INITIALIZE_DETECT_CABLE 0x0000 +#define PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE 0x0001 + +/// +/// +/// UNDI Reset +/// +#define PXE_OPFLAGS_RESET_DISABLE_INTERRUPTS 0x0001 +#define PXE_OPFLAGS_RESET_DISABLE_FILTERS 0x0002 + +/// +/// UNDI Shutdown +/// +/// No OpFlags + +/// +/// UNDI Interrupt Enables +/// +/// +/// Select whether to enable or disable external interrupt signals. +/// Setting both enable and disable will return PXE_STATCODE_INVALID_OPFLAGS. +/// +#define PXE_OPFLAGS_INTERRUPT_OPMASK 0xC000 +#define PXE_OPFLAGS_INTERRUPT_ENABLE 0x8000 +#define PXE_OPFLAGS_INTERRUPT_DISABLE 0x4000 +#define PXE_OPFLAGS_INTERRUPT_READ 0x0000 + +/// +/// Enable receive interrupts. An external interrupt will be generated +/// after a complete non-error packet has been received. +/// +#define PXE_OPFLAGS_INTERRUPT_RECEIVE 0x0001 + +/// +/// Enable transmit interrupts. An external interrupt will be generated +/// after a complete non-error packet has been transmitted. +/// +#define PXE_OPFLAGS_INTERRUPT_TRANSMIT 0x0002 + +/// +/// Enable command interrupts. An external interrupt will be generated +/// when command execution stops. +/// +#define PXE_OPFLAGS_INTERRUPT_COMMAND 0x0004 + +/// +/// Generate software interrupt. Setting this bit generates an external +/// interrupt, if it is supported by the hardware. +/// +#define PXE_OPFLAGS_INTERRUPT_SOFTWARE 0x0008 + +/// +/// UNDI Receive Filters +/// +/// +/// Select whether to enable or disable receive filters. +/// Setting both enable and disable will return PXE_STATCODE_INVALID_OPCODE. +/// +#define PXE_OPFLAGS_RECEIVE_FILTER_OPMASK 0xC000 +#define PXE_OPFLAGS_RECEIVE_FILTER_ENABLE 0x8000 +#define PXE_OPFLAGS_RECEIVE_FILTER_DISABLE 0x4000 +#define PXE_OPFLAGS_RECEIVE_FILTER_READ 0x0000 + +/// +/// To reset the contents of the multicast MAC address filter list, +/// set this OpFlag: +/// +#define PXE_OPFLAGS_RECEIVE_FILTER_RESET_MCAST_LIST 0x2000 + +/// +/// Enable unicast packet receiving. Packets sent to the current station +/// MAC address will be received. +/// +#define PXE_OPFLAGS_RECEIVE_FILTER_UNICAST 0x0001 + +/// +/// Enable broadcast packet receiving. Packets sent to the broadcast +/// MAC address will be received. +/// +#define PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST 0x0002 + +/// +/// Enable filtered multicast packet receiving. Packets sent to any +/// of the multicast MAC addresses in the multicast MAC address filter +/// list will be received. If the filter list is empty, no multicast +/// +#define PXE_OPFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST 0x0004 + +/// +/// Enable promiscuous packet receiving. All packets will be received. +/// +#define PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS 0x0008 + +/// +/// Enable promiscuous multicast packet receiving. All multicast +/// packets will be received. +/// +#define PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST 0x0010 + +/// +/// UNDI Station Address +/// +#define PXE_OPFLAGS_STATION_ADDRESS_READ 0x0000 +#define PXE_OPFLAGS_STATION_ADDRESS_WRITE 0x0000 +#define PXE_OPFLAGS_STATION_ADDRESS_RESET 0x0001 + +/// +/// UNDI Statistics +/// +#define PXE_OPFLAGS_STATISTICS_READ 0x0000 +#define PXE_OPFLAGS_STATISTICS_RESET 0x0001 + +/// +/// UNDI MCast IP to MAC +/// +/// +/// Identify the type of IP address in the CPB. +/// +#define PXE_OPFLAGS_MCAST_IP_TO_MAC_OPMASK 0x0003 +#define PXE_OPFLAGS_MCAST_IPV4_TO_MAC 0x0000 +#define PXE_OPFLAGS_MCAST_IPV6_TO_MAC 0x0001 + +/// +/// UNDI NvData +/// +/// +/// Select the type of non-volatile data operation. +/// +#define PXE_OPFLAGS_NVDATA_OPMASK 0x0001 +#define PXE_OPFLAGS_NVDATA_READ 0x0000 +#define PXE_OPFLAGS_NVDATA_WRITE 0x0001 + +/// +/// UNDI Get Status +/// +/// +/// Return current interrupt status. This will also clear any interrupts +/// that are currently set. This can be used in a polling routine. The +/// interrupt flags are still set and cleared even when the interrupts +/// are disabled. +/// +#define PXE_OPFLAGS_GET_INTERRUPT_STATUS 0x0001 + +/// +/// Return list of transmitted buffers for recycling. Transmit buffers +/// must not be changed or unallocated until they have recycled. After +/// issuing a transmit command, wait for a transmit complete interrupt. +/// When a transmit complete interrupt is received, read the transmitted +/// buffers. Do not plan on getting one buffer per interrupt. Some +/// NICs and UNDIs may transmit multiple buffers per interrupt. +/// +#define PXE_OPFLAGS_GET_TRANSMITTED_BUFFERS 0x0002 + +/// +/// UNDI Fill Header +/// +#define PXE_OPFLAGS_FILL_HEADER_OPMASK 0x0001 +#define PXE_OPFLAGS_FILL_HEADER_FRAGMENTED 0x0001 +#define PXE_OPFLAGS_FILL_HEADER_WHOLE 0x0000 + +/// +/// UNDI Transmit +/// +/// +/// S/W UNDI only. Return after the packet has been transmitted. A +/// transmit complete interrupt will still be generated and the transmit +/// buffer will have to be recycled. +/// +#define PXE_OPFLAGS_SWUNDI_TRANSMIT_OPMASK 0x0001 +#define PXE_OPFLAGS_TRANSMIT_BLOCK 0x0001 +#define PXE_OPFLAGS_TRANSMIT_DONT_BLOCK 0x0000 + +#define PXE_OPFLAGS_TRANSMIT_OPMASK 0x0002 +#define PXE_OPFLAGS_TRANSMIT_FRAGMENTED 0x0002 +#define PXE_OPFLAGS_TRANSMIT_WHOLE 0x0000 + +/// +/// UNDI Receive +/// +/// No OpFlags +/// + +/// +/// PXE STATFLAGS +/// +typedef PXE_UINT16 PXE_STATFLAGS; + +#define PXE_STATFLAGS_INITIALIZE 0x0000 + +/// +/// Common StatFlags that can be returned by all commands. +/// +/// +/// The COMMAND_COMPLETE and COMMAND_FAILED status flags must be +/// implemented by all UNDIs. COMMAND_QUEUED is only needed by UNDIs +/// that support command queuing. +/// +#define PXE_STATFLAGS_STATUS_MASK 0xC000 +#define PXE_STATFLAGS_COMMAND_COMPLETE 0xC000 +#define PXE_STATFLAGS_COMMAND_FAILED 0x8000 +#define PXE_STATFLAGS_COMMAND_QUEUED 0x4000 + +/// +/// UNDI Get State +/// +#define PXE_STATFLAGS_GET_STATE_MASK 0x0003 +#define PXE_STATFLAGS_GET_STATE_INITIALIZED 0x0002 +#define PXE_STATFLAGS_GET_STATE_STARTED 0x0001 +#define PXE_STATFLAGS_GET_STATE_STOPPED 0x0000 + +/// +/// UNDI Start +/// +/// No additional StatFlags +/// + +/// +/// UNDI Get Init Info +/// +#define PXE_STATFLAGS_CABLE_DETECT_MASK 0x0001 +#define PXE_STATFLAGS_CABLE_DETECT_NOT_SUPPORTED 0x0000 +#define PXE_STATFLAGS_CABLE_DETECT_SUPPORTED 0x0001 + +/// +/// UNDI Initialize +/// +#define PXE_STATFLAGS_INITIALIZED_NO_MEDIA 0x0001 + +/// +/// UNDI Reset +/// +#define PXE_STATFLAGS_RESET_NO_MEDIA 0x0001 + +/// +/// UNDI Shutdown +/// +/// No additional StatFlags + +/// +/// UNDI Interrupt Enables +/// +/// +/// If set, receive interrupts are enabled. +/// +#define PXE_STATFLAGS_INTERRUPT_RECEIVE 0x0001 + +/// +/// If set, transmit interrupts are enabled. +/// +#define PXE_STATFLAGS_INTERRUPT_TRANSMIT 0x0002 + +/// +/// If set, command interrupts are enabled. +/// +#define PXE_STATFLAGS_INTERRUPT_COMMAND 0x0004 + +/// +/// UNDI Receive Filters +/// + +/// +/// If set, unicast packets will be received. +/// +#define PXE_STATFLAGS_RECEIVE_FILTER_UNICAST 0x0001 + +/// +/// If set, broadcast packets will be received. +/// +#define PXE_STATFLAGS_RECEIVE_FILTER_BROADCAST 0x0002 + +/// +/// If set, multicast packets that match up with the multicast address +/// filter list will be received. +/// +#define PXE_STATFLAGS_RECEIVE_FILTER_FILTERED_MULTICAST 0x0004 + +/// +/// If set, all packets will be received. +/// +#define PXE_STATFLAGS_RECEIVE_FILTER_PROMISCUOUS 0x0008 + +/// +/// If set, all multicast packets will be received. +/// +#define PXE_STATFLAGS_RECEIVE_FILTER_ALL_MULTICAST 0x0010 + +/// +/// UNDI Station Address +/// +/// No additional StatFlags +/// + +/// +/// UNDI Statistics +/// +/// No additional StatFlags +/// + +/// +//// UNDI MCast IP to MAC +//// +//// No additional StatFlags + +/// +/// UNDI NvData +/// +/// No additional StatFlags +/// + +/// +/// UNDI Get Status +/// + +/// +/// Use to determine if an interrupt has occurred. +/// +#define PXE_STATFLAGS_GET_STATUS_INTERRUPT_MASK 0x000F +#define PXE_STATFLAGS_GET_STATUS_NO_INTERRUPTS 0x0000 + +/// +/// If set, at least one receive interrupt occurred. +/// +#define PXE_STATFLAGS_GET_STATUS_RECEIVE 0x0001 + +/// +/// If set, at least one transmit interrupt occurred. +/// +#define PXE_STATFLAGS_GET_STATUS_TRANSMIT 0x0002 + +/// +/// If set, at least one command interrupt occurred. +/// +#define PXE_STATFLAGS_GET_STATUS_COMMAND 0x0004 + +/// +/// If set, at least one software interrupt occurred. +/// +#define PXE_STATFLAGS_GET_STATUS_SOFTWARE 0x0008 + +/// +/// This flag is set if the transmitted buffer queue is empty. This flag +/// will be set if all transmitted buffer addresses get written into the DB. +/// +#define PXE_STATFLAGS_GET_STATUS_TXBUF_QUEUE_EMPTY 0x0010 + +/// +/// This flag is set if no transmitted buffer addresses were written +/// into the DB. (This could be because DBsize was too small.) +/// +#define PXE_STATFLAGS_GET_STATUS_NO_TXBUFS_WRITTEN 0x0020 + +/// +/// UNDI Fill Header +/// +/// No additional StatFlags +/// + +/// +/// UNDI Transmit +/// +/// No additional StatFlags. + +/// +/// UNDI Receive +/// + +/// +/// No additional StatFlags. +/// +typedef PXE_UINT16 PXE_STATCODE; + +#define PXE_STATCODE_INITIALIZE 0x0000 + +/// +/// Common StatCodes returned by all UNDI commands, UNDI protocol functions +/// and BC protocol functions. +/// +#define PXE_STATCODE_SUCCESS 0x0000 + +#define PXE_STATCODE_INVALID_CDB 0x0001 +#define PXE_STATCODE_INVALID_CPB 0x0002 +#define PXE_STATCODE_BUSY 0x0003 +#define PXE_STATCODE_QUEUE_FULL 0x0004 +#define PXE_STATCODE_ALREADY_STARTED 0x0005 +#define PXE_STATCODE_NOT_STARTED 0x0006 +#define PXE_STATCODE_NOT_SHUTDOWN 0x0007 +#define PXE_STATCODE_ALREADY_INITIALIZED 0x0008 +#define PXE_STATCODE_NOT_INITIALIZED 0x0009 +#define PXE_STATCODE_DEVICE_FAILURE 0x000A +#define PXE_STATCODE_NVDATA_FAILURE 0x000B +#define PXE_STATCODE_UNSUPPORTED 0x000C +#define PXE_STATCODE_BUFFER_FULL 0x000D +#define PXE_STATCODE_INVALID_PARAMETER 0x000E +#define PXE_STATCODE_INVALID_UNDI 0x000F +#define PXE_STATCODE_IPV4_NOT_SUPPORTED 0x0010 +#define PXE_STATCODE_IPV6_NOT_SUPPORTED 0x0011 +#define PXE_STATCODE_NOT_ENOUGH_MEMORY 0x0012 +#define PXE_STATCODE_NO_DATA 0x0013 + +typedef PXE_UINT16 PXE_IFNUM; + +/// +/// This interface number must be passed to the S/W UNDI Start command. +/// +#define PXE_IFNUM_START 0x0000 + +/// +/// This interface number is returned by the S/W UNDI Get State and +/// Start commands if information in the CDB, CPB or DB is invalid. +/// +#define PXE_IFNUM_INVALID 0x0000 + +typedef PXE_UINT16 PXE_CONTROL; + +/// +/// Setting this flag directs the UNDI to queue this command for later +/// execution if the UNDI is busy and it supports command queuing. +/// If queuing is not supported, a PXE_STATCODE_INVALID_CONTROL error +/// is returned. If the queue is full, a PXE_STATCODE_CDB_QUEUE_FULL +/// error is returned. +/// +#define PXE_CONTROL_QUEUE_IF_BUSY 0x0002 + +/// +/// These two bit values are used to determine if there are more UNDI +/// CDB structures following this one. If the link bit is set, there +/// must be a CDB structure following this one. Execution will start +/// on the next CDB structure as soon as this one completes successfully. +/// If an error is generated by this command, execution will stop. +/// +#define PXE_CONTROL_LINK 0x0001 +#define PXE_CONTROL_LAST_CDB_IN_LIST 0x0000 + +typedef PXE_UINT8 PXE_FRAME_TYPE; + +#define PXE_FRAME_TYPE_NONE 0x00 +#define PXE_FRAME_TYPE_UNICAST 0x01 +#define PXE_FRAME_TYPE_BROADCAST 0x02 +#define PXE_FRAME_TYPE_FILTERED_MULTICAST 0x03 +#define PXE_FRAME_TYPE_PROMISCUOUS 0x04 +#define PXE_FRAME_TYPE_PROMISCUOUS_MULTICAST 0x05 + +#define PXE_FRAME_TYPE_MULTICAST PXE_FRAME_TYPE_FILTERED_MULTICAST + +typedef PXE_UINT32 PXE_IPV4; + +typedef PXE_UINT32 PXE_IPV6[4]; +#define PXE_MAC_LENGTH 32 + +typedef PXE_UINT8 PXE_MAC_ADDR[PXE_MAC_LENGTH]; + +typedef PXE_UINT8 PXE_IFTYPE; +typedef UINT16 PXE_MEDIA_PROTOCOL; + +/// +/// This information is from the ARP section of RFC 1700. +/// +/// 1 Ethernet (10Mb) [JBP] +/// 2 Experimental Ethernet (3Mb) [JBP] +/// 3 Amateur Radio AX.25 [PXK] +/// 4 Proteon ProNET Token Ring [JBP] +/// 5 Chaos [GXP] +/// 6 IEEE 802 Networks [JBP] +/// 7 ARCNET [JBP] +/// 8 Hyperchannel [JBP] +/// 9 Lanstar [TU] +/// 10 Autonet Short Address [MXB1] +/// 11 LocalTalk [JKR1] +/// 12 LocalNet (IBM* PCNet or SYTEK* LocalNET) [JXM] +/// 13 Ultra link [RXD2] +/// 14 SMDS [GXC1] +/// 15 Frame Relay [AGM] +/// 16 Asynchronous Transmission Mode (ATM) [JXB2] +/// 17 HDLC [JBP] +/// 18 Fibre Channel [Yakov Rekhter] +/// 19 Asynchronous Transmission Mode (ATM) [Mark Laubach] +/// 20 Serial Line [JBP] +/// 21 Asynchronous Transmission Mode (ATM) [MXB1] +/// +/// * Other names and brands may be claimed as the property of others. +/// +#define PXE_IFTYPE_ETHERNET 0x01 +#define PXE_IFTYPE_TOKENRING 0x04 +#define PXE_IFTYPE_FIBRE_CHANNEL 0x12 + +typedef struct s_pxe_hw_undi { + PXE_UINT32 Signature; ///< PXE_ROMID_SIGNATURE + PXE_UINT8 Len; ///< sizeof(PXE_HW_UNDI) + PXE_UINT8 Fudge; ///< makes 8-bit cksum equal zero + PXE_UINT8 Rev; ///< PXE_ROMID_REV + PXE_UINT8 IFcnt; ///< physical connector count + PXE_UINT8 MajorVer; ///< PXE_ROMID_MAJORVER + PXE_UINT8 MinorVer; ///< PXE_ROMID_MINORVER + PXE_UINT16 reserved; ///< zero, not used + PXE_UINT32 Implementation; ///< implementation flags + ///< reserved ///< vendor use + ///< UINT32 Status; ///< status port + ///< UINT32 Command; ///< command port + ///< UINT64 CDBaddr; ///< CDB address port + ///< +} PXE_HW_UNDI; + +/// +/// Status port bit definitions +/// + +/// +/// UNDI operation state +/// +#define PXE_HWSTAT_STATE_MASK 0xC0000000 +#define PXE_HWSTAT_BUSY 0xC0000000 +#define PXE_HWSTAT_INITIALIZED 0x80000000 +#define PXE_HWSTAT_STARTED 0x40000000 +#define PXE_HWSTAT_STOPPED 0x00000000 + +/// +/// If set, last command failed +/// +#define PXE_HWSTAT_COMMAND_FAILED 0x20000000 + +/// +/// If set, identifies enabled receive filters +/// +#define PXE_HWSTAT_PROMISCUOUS_MULTICAST_RX_ENABLED 0x00001000 +#define PXE_HWSTAT_PROMISCUOUS_RX_ENABLED 0x00000800 +#define PXE_HWSTAT_BROADCAST_RX_ENABLED 0x00000400 +#define PXE_HWSTAT_MULTICAST_RX_ENABLED 0x00000200 +#define PXE_HWSTAT_UNICAST_RX_ENABLED 0x00000100 + +/// +/// If set, identifies enabled external interrupts +/// +#define PXE_HWSTAT_SOFTWARE_INT_ENABLED 0x00000080 +#define PXE_HWSTAT_TX_COMPLETE_INT_ENABLED 0x00000040 +#define PXE_HWSTAT_PACKET_RX_INT_ENABLED 0x00000020 +#define PXE_HWSTAT_CMD_COMPLETE_INT_ENABLED 0x00000010 + +/// +/// If set, identifies pending interrupts +/// +#define PXE_HWSTAT_SOFTWARE_INT_PENDING 0x00000008 +#define PXE_HWSTAT_TX_COMPLETE_INT_PENDING 0x00000004 +#define PXE_HWSTAT_PACKET_RX_INT_PENDING 0x00000002 +#define PXE_HWSTAT_CMD_COMPLETE_INT_PENDING 0x00000001 + +/// +/// Command port definitions +/// + +/// +/// If set, CDB identified in CDBaddr port is given to UNDI. +/// If not set, other bits in this word will be processed. +/// +#define PXE_HWCMD_ISSUE_COMMAND 0x80000000 +#define PXE_HWCMD_INTS_AND_FILTS 0x00000000 + +/// +/// Use these to enable/disable receive filters. +/// +#define PXE_HWCMD_PROMISCUOUS_MULTICAST_RX_ENABLE 0x00001000 +#define PXE_HWCMD_PROMISCUOUS_RX_ENABLE 0x00000800 +#define PXE_HWCMD_BROADCAST_RX_ENABLE 0x00000400 +#define PXE_HWCMD_MULTICAST_RX_ENABLE 0x00000200 +#define PXE_HWCMD_UNICAST_RX_ENABLE 0x00000100 + +/// +/// Use these to enable/disable external interrupts +/// +#define PXE_HWCMD_SOFTWARE_INT_ENABLE 0x00000080 +#define PXE_HWCMD_TX_COMPLETE_INT_ENABLE 0x00000040 +#define PXE_HWCMD_PACKET_RX_INT_ENABLE 0x00000020 +#define PXE_HWCMD_CMD_COMPLETE_INT_ENABLE 0x00000010 + +/// +/// Use these to clear pending external interrupts +/// +#define PXE_HWCMD_CLEAR_SOFTWARE_INT 0x00000008 +#define PXE_HWCMD_CLEAR_TX_COMPLETE_INT 0x00000004 +#define PXE_HWCMD_CLEAR_PACKET_RX_INT 0x00000002 +#define PXE_HWCMD_CLEAR_CMD_COMPLETE_INT 0x00000001 + +typedef struct s_pxe_sw_undi { + PXE_UINT32 Signature; ///< PXE_ROMID_SIGNATURE + PXE_UINT8 Len; ///< sizeof(PXE_SW_UNDI) + PXE_UINT8 Fudge; ///< makes 8-bit cksum zero + PXE_UINT8 Rev; ///< PXE_ROMID_REV + PXE_UINT8 IFcnt; ///< physical connector count + PXE_UINT8 MajorVer; ///< PXE_ROMID_MAJORVER + PXE_UINT8 MinorVer; ///< PXE_ROMID_MINORVER + PXE_UINT16 reserved1; ///< zero, not used + PXE_UINT32 Implementation; ///< Implementation flags + PXE_UINT64 EntryPoint; ///< API entry point + PXE_UINT8 reserved2[3]; ///< zero, not used + PXE_UINT8 BusCnt; ///< number of bustypes supported + PXE_UINT32 BusType[1]; ///< list of supported bustypes +} PXE_SW_UNDI; + +typedef union u_pxe_undi { + PXE_HW_UNDI hw; + PXE_SW_UNDI sw; +} PXE_UNDI; + +/// +/// Signature of !PXE structure +/// +#define PXE_ROMID_SIGNATURE PXE_BUSTYPE ('!', 'P', 'X', 'E') + +/// +/// !PXE structure format revision +/// +#define PXE_ROMID_REV 0x02 + +/// +/// UNDI command interface revision. These are the values that get sent +/// in option 94 (Client Network Interface Identifier) in the DHCP Discover +/// and PXE Boot Server Request packets. +/// +#define PXE_ROMID_MAJORVER 0x03 +#define PXE_ROMID_MINORVER 0x01 + +/// +/// Implementation flags +/// +#define PXE_ROMID_IMP_HW_UNDI 0x80000000 +#define PXE_ROMID_IMP_SW_VIRT_ADDR 0x40000000 +#define PXE_ROMID_IMP_64BIT_DEVICE 0x00010000 +#define PXE_ROMID_IMP_FRAG_SUPPORTED 0x00008000 +#define PXE_ROMID_IMP_CMD_LINK_SUPPORTED 0x00004000 +#define PXE_ROMID_IMP_CMD_QUEUE_SUPPORTED 0x00002000 +#define PXE_ROMID_IMP_MULTI_FRAME_SUPPORTED 0x00001000 +#define PXE_ROMID_IMP_NVDATA_SUPPORT_MASK 0x00000C00 +#define PXE_ROMID_IMP_NVDATA_BULK_WRITABLE 0x00000C00 +#define PXE_ROMID_IMP_NVDATA_SPARSE_WRITABLE 0x00000800 +#define PXE_ROMID_IMP_NVDATA_READ_ONLY 0x00000400 +#define PXE_ROMID_IMP_NVDATA_NOT_AVAILABLE 0x00000000 +#define PXE_ROMID_IMP_STATISTICS_SUPPORTED 0x00000200 +#define PXE_ROMID_IMP_STATION_ADDR_SETTABLE 0x00000100 +#define PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED 0x00000080 +#define PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED 0x00000040 +#define PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED 0x00000020 +#define PXE_ROMID_IMP_FILTERED_MULTICAST_RX_SUPPORTED 0x00000010 +#define PXE_ROMID_IMP_SOFTWARE_INT_SUPPORTED 0x00000008 +#define PXE_ROMID_IMP_TX_COMPLETE_INT_SUPPORTED 0x00000004 +#define PXE_ROMID_IMP_PACKET_RX_INT_SUPPORTED 0x00000002 +#define PXE_ROMID_IMP_CMD_COMPLETE_INT_SUPPORTED 0x00000001 + +typedef struct s_pxe_cdb { + PXE_OPCODE OpCode; + PXE_OPFLAGS OpFlags; + PXE_UINT16 CPBsize; + PXE_UINT16 DBsize; + PXE_UINT64 CPBaddr; + PXE_UINT64 DBaddr; + PXE_STATCODE StatCode; + PXE_STATFLAGS StatFlags; + PXE_UINT16 IFnum; + PXE_CONTROL Control; +} PXE_CDB; + +typedef union u_pxe_ip_addr { + PXE_IPV6 IPv6; + PXE_IPV4 IPv4; +} PXE_IP_ADDR; + +typedef union pxe_device { + /// + /// PCI and PC Card NICs are both identified using bus, device + /// and function numbers. For PC Card, this may require PC + /// Card services to be loaded in the BIOS or preboot + /// environment. + /// + struct { + /// + /// See S/W UNDI ROMID structure definition for PCI and + /// PCC BusType definitions. + /// + PXE_UINT32 BusType; + + /// + /// Bus, device & function numbers that locate this device. + /// + PXE_UINT16 Bus; + PXE_UINT8 Device; + PXE_UINT8 Function; + } + PCI, PCC; + +} PXE_DEVICE; + +/// +/// cpb and db definitions +/// +#define MAX_PCI_CONFIG_LEN 64 ///< # of dwords +#define MAX_EEPROM_LEN 128 ///< # of dwords +#define MAX_XMIT_BUFFERS 32 ///< recycling Q length for xmit_done +#define MAX_MCAST_ADDRESS_CNT 8 + +typedef struct s_pxe_cpb_start_30 { + /// + /// PXE_VOID Delay(UINTN microseconds); + /// + /// UNDI will never request a delay smaller than 10 microseconds + /// and will always request delays in increments of 10 microseconds. + /// The Delay() CallBack routine must delay between n and n + 10 + /// microseconds before returning control to the UNDI. + /// + /// This field cannot be set to zero. + /// + UINT64 Delay; + + /// + /// PXE_VOID Block(UINT32 enable); + /// + /// UNDI may need to block multi-threaded/multi-processor access to + /// critical code sections when programming or accessing the network + /// device. To this end, a blocking service is needed by the UNDI. + /// When UNDI needs a block, it will call Block() passing a non-zero + /// value. When UNDI no longer needs a block, it will call Block() + /// with a zero value. When called, if the Block() is already enabled, + /// do not return control to the UNDI until the previous Block() is + /// disabled. + /// + /// This field cannot be set to zero. + /// + UINT64 Block; + + /// + /// PXE_VOID Virt2Phys(UINT64 virtual, UINT64 physical_ptr); + /// + /// UNDI will pass the virtual address of a buffer and the virtual + /// address of a 64-bit physical buffer. Convert the virtual address + /// to a physical address and write the result to the physical address + /// buffer. If virtual and physical addresses are the same, just + /// copy the virtual address to the physical address buffer. + /// + /// This field can be set to zero if virtual and physical addresses + /// are equal. + /// + UINT64 Virt2Phys; + /// + /// PXE_VOID Mem_IO(UINT8 read_write, UINT8 len, UINT64 port, + /// UINT64 buf_addr); + /// + /// UNDI will read or write the device io space using this call back + /// function. It passes the number of bytes as the len parameter and it + /// will be either 1,2,4 or 8. + /// + /// This field can not be set to zero. + /// + UINT64 Mem_IO; +} PXE_CPB_START_30; + +typedef struct s_pxe_cpb_start_31 { + /// + /// PXE_VOID Delay(UINT64 UnqId, UINTN microseconds); + /// + /// UNDI will never request a delay smaller than 10 microseconds + /// and will always request delays in increments of 10 microseconds. + /// The Delay() CallBack routine must delay between n and n + 10 + /// microseconds before returning control to the UNDI. + /// + /// This field cannot be set to zero. + /// + UINT64 Delay; + + /// + /// PXE_VOID Block(UINT64 unq_id, UINT32 enable); + /// + /// UNDI may need to block multi-threaded/multi-processor access to + /// critical code sections when programming or accessing the network + /// device. To this end, a blocking service is needed by the UNDI. + /// When UNDI needs a block, it will call Block() passing a non-zero + /// value. When UNDI no longer needs a block, it will call Block() + /// with a zero value. When called, if the Block() is already enabled, + /// do not return control to the UNDI until the previous Block() is + /// disabled. + /// + /// This field cannot be set to zero. + /// + UINT64 Block; + + /// + /// PXE_VOID Virt2Phys(UINT64 UnqId, UINT64 virtual, UINT64 physical_ptr); + /// + /// UNDI will pass the virtual address of a buffer and the virtual + /// address of a 64-bit physical buffer. Convert the virtual address + /// to a physical address and write the result to the physical address + /// buffer. If virtual and physical addresses are the same, just + /// copy the virtual address to the physical address buffer. + /// + /// This field can be set to zero if virtual and physical addresses + /// are equal. + /// + UINT64 Virt2Phys; + /// + /// PXE_VOID Mem_IO(UINT64 UnqId, UINT8 read_write, UINT8 len, UINT64 port, + /// UINT64 buf_addr); + /// + /// UNDI will read or write the device io space using this call back + /// function. It passes the number of bytes as the len parameter and it + /// will be either 1,2,4 or 8. + /// + /// This field can not be set to zero. + /// + UINT64 Mem_IO; + /// + /// PXE_VOID Map_Mem(UINT64 unq_id, UINT64 virtual_addr, UINT32 size, + /// UINT32 Direction, UINT64 mapped_addr); + /// + /// UNDI will pass the virtual address of a buffer, direction of the data + /// flow from/to the mapped buffer (the constants are defined below) + /// and a place holder (pointer) for the mapped address. + /// This call will Map the given address to a physical DMA address and write + /// the result to the mapped_addr pointer. If there is no need to + /// map the given address to a lower address (i.e. the given address is + /// associated with a physical address that is already compatible to be + /// used with the DMA, it converts the given virtual address to it's + /// physical address and write that in the mapped address pointer. + /// + /// This field can be set to zero if there is no mapping service available + /// + UINT64 Map_Mem; + + /// + /// PXE_VOID UnMap_Mem(UINT64 unq_id, UINT64 virtual_addr, UINT32 size, + /// UINT32 Direction, UINT64 mapped_addr); + /// + /// UNDI will pass the virtual and mapped addresses of a buffer + /// This call will un map the given address + /// + /// This field can be set to zero if there is no unmapping service available + /// + UINT64 UnMap_Mem; + + /// + /// PXE_VOID Sync_Mem(UINT64 unq_id, UINT64 virtual, + /// UINT32 size, UINT32 Direction, UINT64 mapped_addr); + /// + /// UNDI will pass the virtual and mapped addresses of a buffer + /// This call will synchronize the contents of both the virtual and mapped + /// buffers for the given Direction. + /// + /// This field can be set to zero if there is no service available + /// + UINT64 Sync_Mem; + + /// + /// protocol driver can provide anything for this Unique_ID, UNDI remembers + /// that as just a 64bit value assocaited to the interface specified by + /// the ifnum and gives it back as a parameter to all the call-back routines + /// when calling for that interface! + /// + UINT64 Unique_ID; +} PXE_CPB_START_31; + +#define TO_AND_FROM_DEVICE 0 +#define FROM_DEVICE 1 +#define TO_DEVICE 2 + +#define PXE_DELAY_MILLISECOND 1000 +#define PXE_DELAY_SECOND 1000000 +#define PXE_IO_READ 0 +#define PXE_IO_WRITE 1 +#define PXE_MEM_READ 2 +#define PXE_MEM_WRITE 4 + +typedef struct s_pxe_db_get_init_info { + /// + /// Minimum length of locked memory buffer that must be given to + /// the Initialize command. Giving UNDI more memory will generally + /// give better performance. + /// + /// If MemoryRequired is zero, the UNDI does not need and will not + /// use system memory to receive and transmit packets. + /// + PXE_UINT32 MemoryRequired; + + /// + /// Maximum frame data length for Tx/Rx excluding the media header. + /// + PXE_UINT32 FrameDataLen; + + /// + /// Supported link speeds are in units of mega bits. Common ethernet + /// values are 10, 100 and 1000. Unused LinkSpeeds[] entries are zero + /// filled. + /// + PXE_UINT32 LinkSpeeds[4]; + + /// + /// Number of non-volatile storage items. + /// + PXE_UINT32 NvCount; + + /// + /// Width of non-volatile storage item in bytes. 0, 1, 2 or 4 + /// + PXE_UINT16 NvWidth; + + /// + /// Media header length. This is the typical media header length for + /// this UNDI. This information is needed when allocating receive + /// and transmit buffers. + /// + PXE_UINT16 MediaHeaderLen; + + /// + /// Number of bytes in the NIC hardware (MAC) address. + /// + PXE_UINT16 HWaddrLen; + + /// + /// Maximum number of multicast MAC addresses in the multicast + /// MAC address filter list. + /// + PXE_UINT16 MCastFilterCnt; + + /// + /// Default number and size of transmit and receive buffers that will + /// be allocated by the UNDI. If MemoryRequired is non-zero, this + /// allocation will come out of the memory buffer given to the Initialize + /// command. If MemoryRequired is zero, this allocation will come out of + /// memory on the NIC. + /// + PXE_UINT16 TxBufCnt; + PXE_UINT16 TxBufSize; + PXE_UINT16 RxBufCnt; + PXE_UINT16 RxBufSize; + + /// + /// Hardware interface types defined in the Assigned Numbers RFC + /// and used in DHCP and ARP packets. + /// See the PXE_IFTYPE typedef and PXE_IFTYPE_xxx macros. + /// + PXE_UINT8 IFtype; + + /// + /// Supported duplex. See PXE_DUPLEX_xxxxx #defines below. + /// + PXE_UINT8 SupportedDuplexModes; + + /// + /// Supported loopback options. See PXE_LOOPBACK_xxxxx #defines below. + /// + PXE_UINT8 SupportedLoopBackModes; +} PXE_DB_GET_INIT_INFO; + +#define PXE_MAX_TXRX_UNIT_ETHER 1500 + +#define PXE_HWADDR_LEN_ETHER 0x0006 +#define PXE_MAC_HEADER_LEN_ETHER 0x000E + +#define PXE_DUPLEX_ENABLE_FULL_SUPPORTED 1 +#define PXE_DUPLEX_FORCE_FULL_SUPPORTED 2 + +#define PXE_LOOPBACK_INTERNAL_SUPPORTED 1 +#define PXE_LOOPBACK_EXTERNAL_SUPPORTED 2 + +typedef struct s_pxe_pci_config_info { + /// + /// This is the flag field for the PXE_DB_GET_CONFIG_INFO union. + /// For PCI bus devices, this field is set to PXE_BUSTYPE_PCI. + /// + UINT32 BusType; + + /// + /// This identifies the PCI network device that this UNDI interface + /// is bound to. + /// + UINT16 Bus; + UINT8 Device; + UINT8 Function; + + /// + /// This is a copy of the PCI configuration space for this + /// network device. + /// + union { + UINT8 Byte[256]; + UINT16 Word[128]; + UINT32 Dword[64]; + } Config; +} PXE_PCI_CONFIG_INFO; + +typedef struct s_pxe_pcc_config_info { + /// + /// This is the flag field for the PXE_DB_GET_CONFIG_INFO union. + /// For PCC bus devices, this field is set to PXE_BUSTYPE_PCC. + /// + PXE_UINT32 BusType; + + /// + /// This identifies the PCC network device that this UNDI interface + /// is bound to. + /// + PXE_UINT16 Bus; + PXE_UINT8 Device; + PXE_UINT8 Function; + + /// + /// This is a copy of the PCC configuration space for this + /// network device. + /// + union { + PXE_UINT8 Byte[256]; + PXE_UINT16 Word[128]; + PXE_UINT32 Dword[64]; + } Config; +} PXE_PCC_CONFIG_INFO; + +typedef union u_pxe_db_get_config_info { + PXE_PCI_CONFIG_INFO pci; + PXE_PCC_CONFIG_INFO pcc; +} PXE_DB_GET_CONFIG_INFO; + +typedef struct s_pxe_cpb_initialize { + /// + /// Address of first (lowest) byte of the memory buffer. This buffer must + /// be in contiguous physical memory and cannot be swapped out. The UNDI + /// will be using this for transmit and receive buffering. + /// + PXE_UINT64 MemoryAddr; + + /// + /// MemoryLength must be greater than or equal to MemoryRequired + /// returned by the Get Init Info command. + /// + PXE_UINT32 MemoryLength; + + /// + /// Desired link speed in Mbit/sec. Common ethernet values are 10, 100 + /// and 1000. Setting a value of zero will auto-detect and/or use the + /// default link speed (operation depends on UNDI/NIC functionality). + /// + PXE_UINT32 LinkSpeed; + + /// + /// Suggested number and size of receive and transmit buffers to + /// allocate. If MemoryAddr and MemoryLength are non-zero, this + /// allocation comes out of the supplied memory buffer. If MemoryAddr + /// and MemoryLength are zero, this allocation comes out of memory + /// on the NIC. + /// + /// If these fields are set to zero, the UNDI will allocate buffer + /// counts and sizes as it sees fit. + /// + PXE_UINT16 TxBufCnt; + PXE_UINT16 TxBufSize; + PXE_UINT16 RxBufCnt; + PXE_UINT16 RxBufSize; + + /// + /// The following configuration parameters are optional and must be zero + /// to use the default values. + /// + PXE_UINT8 DuplexMode; + + PXE_UINT8 LoopBackMode; +} PXE_CPB_INITIALIZE; + +#define PXE_DUPLEX_DEFAULT 0x00 +#define PXE_FORCE_FULL_DUPLEX 0x01 +#define PXE_ENABLE_FULL_DUPLEX 0x02 +#define PXE_FORCE_HALF_DUPLEX 0x04 +#define PXE_DISABLE_FULL_DUPLEX 0x08 + +#define LOOPBACK_NORMAL 0 +#define LOOPBACK_INTERNAL 1 +#define LOOPBACK_EXTERNAL 2 + +typedef struct s_pxe_db_initialize { + /// + /// Actual amount of memory used from the supplied memory buffer. This + /// may be less that the amount of memory suppllied and may be zero if + /// the UNDI and network device do not use external memory buffers. + /// + /// Memory used by the UNDI and network device is allocated from the + /// lowest memory buffer address. + /// + PXE_UINT32 MemoryUsed; + + /// + /// Actual number and size of receive and transmit buffers that were + /// allocated. + /// + PXE_UINT16 TxBufCnt; + PXE_UINT16 TxBufSize; + PXE_UINT16 RxBufCnt; + PXE_UINT16 RxBufSize; +} PXE_DB_INITIALIZE; + +typedef struct s_pxe_cpb_receive_filters { + /// + /// List of multicast MAC addresses. This list, if present, will + /// replace the existing multicast MAC address filter list. + /// + PXE_MAC_ADDR MCastList[MAX_MCAST_ADDRESS_CNT]; +} PXE_CPB_RECEIVE_FILTERS; + +typedef struct s_pxe_db_receive_filters { + /// + /// Filtered multicast MAC address list. + /// + PXE_MAC_ADDR MCastList[MAX_MCAST_ADDRESS_CNT]; +} PXE_DB_RECEIVE_FILTERS; + +typedef struct s_pxe_cpb_station_address { + /// + /// If supplied and supported, the current station MAC address + /// will be changed. + /// + PXE_MAC_ADDR StationAddr; +} PXE_CPB_STATION_ADDRESS; + +typedef struct s_pxe_dpb_station_address { + /// + /// Current station MAC address. + /// + PXE_MAC_ADDR StationAddr; + + /// + /// Station broadcast MAC address. + /// + PXE_MAC_ADDR BroadcastAddr; + + /// + /// Permanent station MAC address. + /// + PXE_MAC_ADDR PermanentAddr; +} PXE_DB_STATION_ADDRESS; + +typedef struct s_pxe_db_statistics { + /// + /// Bit field identifying what statistic data is collected by the + /// UNDI/NIC. + /// If bit 0x00 is set, Data[0x00] is collected. + /// If bit 0x01 is set, Data[0x01] is collected. + /// If bit 0x20 is set, Data[0x20] is collected. + /// If bit 0x21 is set, Data[0x21] is collected. + /// Etc. + /// + PXE_UINT64 Supported; + + /// + /// Statistic data. + /// + PXE_UINT64 Data[64]; +} PXE_DB_STATISTICS; + +/// +/// Total number of frames received. Includes frames with errors and +/// dropped frames. +/// +#define PXE_STATISTICS_RX_TOTAL_FRAMES 0x00 + +/// +/// Number of valid frames received and copied into receive buffers. +/// +#define PXE_STATISTICS_RX_GOOD_FRAMES 0x01 + +/// +/// Number of frames below the minimum length for the media. +/// This would be <64 for ethernet. +/// +#define PXE_STATISTICS_RX_UNDERSIZE_FRAMES 0x02 + +/// +/// Number of frames longer than the maxminum length for the +/// media. This would be >1500 for ethernet. +/// +#define PXE_STATISTICS_RX_OVERSIZE_FRAMES 0x03 + +/// +/// Valid frames that were dropped because receive buffers were full. +/// +#define PXE_STATISTICS_RX_DROPPED_FRAMES 0x04 + +/// +/// Number of valid unicast frames received and not dropped. +/// +#define PXE_STATISTICS_RX_UNICAST_FRAMES 0x05 + +/// +/// Number of valid broadcast frames received and not dropped. +/// +#define PXE_STATISTICS_RX_BROADCAST_FRAMES 0x06 + +/// +/// Number of valid mutlicast frames received and not dropped. +/// +#define PXE_STATISTICS_RX_MULTICAST_FRAMES 0x07 + +/// +/// Number of frames w/ CRC or alignment errors. +/// +#define PXE_STATISTICS_RX_CRC_ERROR_FRAMES 0x08 + +/// +/// Total number of bytes received. Includes frames with errors +/// and dropped frames. +/// +#define PXE_STATISTICS_RX_TOTAL_BYTES 0x09 + +/// +/// Transmit statistics. +/// +#define PXE_STATISTICS_TX_TOTAL_FRAMES 0x0A +#define PXE_STATISTICS_TX_GOOD_FRAMES 0x0B +#define PXE_STATISTICS_TX_UNDERSIZE_FRAMES 0x0C +#define PXE_STATISTICS_TX_OVERSIZE_FRAMES 0x0D +#define PXE_STATISTICS_TX_DROPPED_FRAMES 0x0E +#define PXE_STATISTICS_TX_UNICAST_FRAMES 0x0F +#define PXE_STATISTICS_TX_BROADCAST_FRAMES 0x10 +#define PXE_STATISTICS_TX_MULTICAST_FRAMES 0x11 +#define PXE_STATISTICS_TX_CRC_ERROR_FRAMES 0x12 +#define PXE_STATISTICS_TX_TOTAL_BYTES 0x13 + +/// +/// Number of collisions detection on this subnet. +/// +#define PXE_STATISTICS_COLLISIONS 0x14 + +/// +/// Number of frames destined for unsupported protocol. +/// +#define PXE_STATISTICS_UNSUPPORTED_PROTOCOL 0x15 + +typedef struct s_pxe_cpb_mcast_ip_to_mac { + /// + /// Multicast IP address to be converted to multicast MAC address. + /// + PXE_IP_ADDR IP; +} PXE_CPB_MCAST_IP_TO_MAC; + +typedef struct s_pxe_db_mcast_ip_to_mac { + /// + /// Multicast MAC address. + /// + PXE_MAC_ADDR MAC; +} PXE_DB_MCAST_IP_TO_MAC; + +typedef struct s_pxe_cpb_nvdata_sparse { + /// + /// NvData item list. Only items in this list will be updated. + /// + struct { + /// + /// Non-volatile storage address to be changed. + /// + PXE_UINT32 Addr; + + /// + /// Data item to write into above storage address. + /// + union { + PXE_UINT8 Byte; + PXE_UINT16 Word; + PXE_UINT32 Dword; + } Data; + } Item[MAX_EEPROM_LEN]; +} PXE_CPB_NVDATA_SPARSE; + +/// +/// When using bulk update, the size of the CPB structure must be +/// the same size as the non-volatile NIC storage. +/// +typedef union u_pxe_cpb_nvdata_bulk { + /// + /// Array of byte-wide data items. + /// + PXE_UINT8 Byte[MAX_EEPROM_LEN << 2]; + + /// + /// Array of word-wide data items. + /// + PXE_UINT16 Word[MAX_EEPROM_LEN << 1]; + + /// + /// Array of dword-wide data items. + /// + PXE_UINT32 Dword[MAX_EEPROM_LEN]; +} PXE_CPB_NVDATA_BULK; + +typedef struct s_pxe_db_nvdata { + /// + /// Arrays of data items from non-volatile storage. + /// + union { + /// + /// Array of byte-wide data items. + /// + PXE_UINT8 Byte[MAX_EEPROM_LEN << 2]; + + /// + /// Array of word-wide data items. + /// + PXE_UINT16 Word[MAX_EEPROM_LEN << 1]; + + /// + /// Array of dword-wide data items. + /// + PXE_UINT32 Dword[MAX_EEPROM_LEN]; + } Data; +} PXE_DB_NVDATA; + +typedef struct s_pxe_db_get_status { + /// + /// Length of next receive frame (header + data). If this is zero, + /// there is no next receive frame available. + /// + PXE_UINT32 RxFrameLen; + + /// + /// Reserved, set to zero. + /// + PXE_UINT32 reserved; + + /// + /// Addresses of transmitted buffers that need to be recycled. + /// + PXE_UINT64 TxBuffer[MAX_XMIT_BUFFERS]; +} PXE_DB_GET_STATUS; + +typedef struct s_pxe_cpb_fill_header { + /// + /// Source and destination MAC addresses. These will be copied into + /// the media header without doing byte swapping. + /// + PXE_MAC_ADDR SrcAddr; + PXE_MAC_ADDR DestAddr; + + /// + /// Address of first byte of media header. The first byte of packet data + /// follows the last byte of the media header. + /// + PXE_UINT64 MediaHeader; + + /// + /// Length of packet data in bytes (not including the media header). + /// + PXE_UINT32 PacketLen; + + /// + /// Protocol type. This will be copied into the media header without + /// doing byte swapping. Protocol type numbers can be obtained from + /// the Assigned Numbers RFC 1700. + /// + PXE_UINT16 Protocol; + + /// + /// Length of the media header in bytes. + /// + PXE_UINT16 MediaHeaderLen; +} PXE_CPB_FILL_HEADER; + +#define PXE_PROTOCOL_ETHERNET_IP 0x0800 +#define PXE_PROTOCOL_ETHERNET_ARP 0x0806 +#define MAX_XMIT_FRAGMENTS 16 + +typedef struct s_pxe_cpb_fill_header_fragmented { + /// + /// Source and destination MAC addresses. These will be copied into + /// the media header without doing byte swapping. + /// + PXE_MAC_ADDR SrcAddr; + PXE_MAC_ADDR DestAddr; + + /// + /// Length of packet data in bytes (not including the media header). + /// + PXE_UINT32 PacketLen; + + /// + /// Protocol type. This will be copied into the media header without + /// doing byte swapping. Protocol type numbers can be obtained from + /// the Assigned Numbers RFC 1700. + /// + PXE_MEDIA_PROTOCOL Protocol; + + /// + /// Length of the media header in bytes. + /// + PXE_UINT16 MediaHeaderLen; + + /// + /// Number of packet fragment descriptors. + /// + PXE_UINT16 FragCnt; + + /// + /// Reserved, must be set to zero. + /// + PXE_UINT16 reserved; + + /// + /// Array of packet fragment descriptors. The first byte of the media + /// header is the first byte of the first fragment. + /// + struct { + /// + /// Address of this packet fragment. + /// + PXE_UINT64 FragAddr; + + /// + /// Length of this packet fragment. + /// + PXE_UINT32 FragLen; + + /// + /// Reserved, must be set to zero. + /// + PXE_UINT32 reserved; + } FragDesc[MAX_XMIT_FRAGMENTS]; +} +PXE_CPB_FILL_HEADER_FRAGMENTED; + +typedef struct s_pxe_cpb_transmit { + /// + /// Address of first byte of frame buffer. This is also the first byte + /// of the media header. + /// + PXE_UINT64 FrameAddr; + + /// + /// Length of the data portion of the frame buffer in bytes. Do not + /// include the length of the media header. + /// + PXE_UINT32 DataLen; + + /// + /// Length of the media header in bytes. + /// + PXE_UINT16 MediaheaderLen; + + /// + /// Reserved, must be zero. + /// + PXE_UINT16 reserved; +} PXE_CPB_TRANSMIT; + +typedef struct s_pxe_cpb_transmit_fragments { + /// + /// Length of packet data in bytes (not including the media header). + /// + PXE_UINT32 FrameLen; + + /// + /// Length of the media header in bytes. + /// + PXE_UINT16 MediaheaderLen; + + /// + /// Number of packet fragment descriptors. + /// + PXE_UINT16 FragCnt; + + /// + /// Array of frame fragment descriptors. The first byte of the first + /// fragment is also the first byte of the media header. + /// + struct { + /// + /// Address of this frame fragment. + /// + PXE_UINT64 FragAddr; + + /// + /// Length of this frame fragment. + /// + PXE_UINT32 FragLen; + + /// + /// Reserved, must be set to zero. + /// + PXE_UINT32 reserved; + } FragDesc[MAX_XMIT_FRAGMENTS]; +} +PXE_CPB_TRANSMIT_FRAGMENTS; + +typedef struct s_pxe_cpb_receive { + /// + /// Address of first byte of receive buffer. This is also the first byte + /// of the frame header. + /// + PXE_UINT64 BufferAddr; + + /// + /// Length of receive buffer. This must be large enough to hold the + /// received frame (media header + data). If the length of smaller than + /// the received frame, data will be lost. + /// + PXE_UINT32 BufferLen; + + /// + /// Reserved, must be set to zero. + /// + PXE_UINT32 reserved; +} PXE_CPB_RECEIVE; + +typedef struct s_pxe_db_receive { + /// + /// Source and destination MAC addresses from media header. + /// + PXE_MAC_ADDR SrcAddr; + PXE_MAC_ADDR DestAddr; + + /// + /// Length of received frame. May be larger than receive buffer size. + /// The receive buffer will not be overwritten. This is how to tell + /// if data was lost because the receive buffer was too small. + /// + PXE_UINT32 FrameLen; + + /// + /// Protocol type from media header. + /// + PXE_MEDIA_PROTOCOL Protocol; + + /// + /// Length of media header in received frame. + /// + PXE_UINT16 MediaHeaderLen; + + /// + /// Type of receive frame. + /// + PXE_FRAME_TYPE Type; + + /// + /// Reserved, must be zero. + /// + PXE_UINT8 reserved[7]; + +} PXE_DB_RECEIVE; + +#pragma pack() + +#endif diff --git a/gpxe/src/include/gpxe/efi/Uefi/UefiSpec.h b/gpxe/src/include/gpxe/efi/Uefi/UefiSpec.h new file mode 100644 index 0000000..03e7b6c --- /dev/null +++ b/gpxe/src/include/gpxe/efi/Uefi/UefiSpec.h @@ -0,0 +1,1916 @@ +/** @file + Include file that supports UEFI. + + This include file must only contain things defined in the UEFI 2.1 specification. + If a code construct is defined in the UEFI 2.1 specification it must be included + by this include file. + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __UEFI_SPEC_H__ +#define __UEFI_SPEC_H__ + +#include + +#include +#include +#include + +/// +/// Enumeration of memory allocation. +/// +typedef enum { + AllocateAnyPages, + AllocateMaxAddress, + AllocateAddress, + MaxAllocateType +} EFI_ALLOCATE_TYPE; + +// +// Bit definitions for EFI_TIME.Daylight +// +#define EFI_TIME_ADJUST_DAYLIGHT 0x01 +#define EFI_TIME_IN_DAYLIGHT 0x02 + +/// +/// Value definition for EFI_TIME.TimeZone +/// +#define EFI_UNSPECIFIED_TIMEZONE 0x07FF + +// +// Memory cacheability attributes +// +#define EFI_MEMORY_UC 0x0000000000000001ULL +#define EFI_MEMORY_WC 0x0000000000000002ULL +#define EFI_MEMORY_WT 0x0000000000000004ULL +#define EFI_MEMORY_WB 0x0000000000000008ULL +#define EFI_MEMORY_UCE 0x0000000000000010ULL +// +// Physical memory protection attributes +// +#define EFI_MEMORY_WP 0x0000000000001000ULL +#define EFI_MEMORY_RP 0x0000000000002000ULL +#define EFI_MEMORY_XP 0x0000000000004000ULL +// +// Runtime memory attribute +// +#define EFI_MEMORY_RUNTIME 0x8000000000000000ULL + +/// +/// Memory descriptor version number +/// +#define EFI_MEMORY_DESCRIPTOR_VERSION 1 + +/// +/// Definition of memory descriptor +/// +typedef struct { + UINT32 Type; + EFI_PHYSICAL_ADDRESS PhysicalStart; + EFI_VIRTUAL_ADDRESS VirtualStart; + UINT64 NumberOfPages; + UINT64 Attribute; +} EFI_MEMORY_DESCRIPTOR; + +/// +/// Build macros to find next EFI_MEMORY_DESCRIPTOR. +/// +#define NEXT_MEMORY_DESCRIPTOR(_Ptr, _Size) ((EFI_MEMORY_DESCRIPTOR *) (((UINT8 *) (_Ptr)) + (_Size))) + +/// +/// Declare forward referenced data structures +/// +typedef struct _EFI_SYSTEM_TABLE EFI_SYSTEM_TABLE; + +/** + Allocates memory pages from the system. + + @param Type The type of allocation to perform. + @param MemoryType The type of memory to allocate. + @param Pages The number of contiguous 4 KB pages to allocate. + @param Memory Pointer to a physical address. On input, the way in which the address is + used depends on the value of Type. + + @retval EFI_SUCCESS The requested pages were allocated. + @retval EFI_INVALID_PARAMETER 1) Type is not AllocateAnyPages or + AllocateMaxAddress or AllocateAddress. + 2) MemoryType is in the range + EfiMaxMemoryType..0x7FFFFFFF. + @retval EFI_OUT_OF_RESOURCES The pages could not be allocated. + @retval EFI_NOT_FOUND The requested pages could not be found. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ALLOCATE_PAGES)( + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Pages, + IN OUT EFI_PHYSICAL_ADDRESS *Memory + ); + +/** + Frees memory pages. + + @param Memory The base physical address of the pages to be freed. + @param Pages The number of contiguous 4 KB pages to free. + + @retval EFI_SUCCESS The requested pages were freed. + @retval EFI_INVALID_PARAMETER Memory is not a page-aligned address or Pages is invalid. + @retval EFI_NOT_FOUND The requested memory pages were not allocated with + AllocatePages(). + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_FREE_PAGES)( + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN Pages + ); + +/** + Returns the current memory map. + + @param MemoryMapSize A pointer to the size, in bytes, of the MemoryMap buffer. + On input, this is the size of the buffer allocated by the caller. + On output, it is the size of the buffer returned by the firmware if + the buffer was large enough, or the size of the buffer needed to contain + the map if the buffer was too small. + @param MemoryMap A pointer to the buffer in which firmware places the current memory + map. + @param MapKey A pointer to the location in which firmware returns the key for the + current memory map. + @param DescriptorSize A pointer to the location in which firmware returns the size, in bytes, of + an individual EFI_MEMORY_DESCRIPTOR. + @param DescriptorVersion A pointer to the location in which firmware returns the version number + associated with the EFI_MEMORY_DESCRIPTOR. + + @retval EFI_SUCCESS The memory map was returned in the MemoryMap buffer. + @retval EFI_BUFFER_TOO_SMALL The MemoryMap buffer was too small. The current buffer size + needed to hold the memory map is returned in MemoryMapSize. + @retval EFI_INVALID_PARAMETER 1) MemoryMapSize is NULL. + 2) The MemoryMap buffer is not too small and MemoryMap is + NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_GET_MEMORY_MAP)( + IN OUT UINTN *MemoryMapSize, + IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, + OUT UINTN *MapKey, + OUT UINTN *DescriptorSize, + OUT UINT32 *DescriptorVersion + ); + +/** + Allocates pool memory. + + @param PoolType The type of pool to allocate. + @param Size The number of bytes to allocate from the pool. + @param Buffer A pointer to a pointer to the allocated buffer if the call succeeds; + undefined otherwise. + + @retval EFI_SUCCESS The requested number of bytes was allocated. + @retval EFI_OUT_OF_RESOURCES The pool requested could not be allocated. + @retval EFI_INVALID_PARAMETER PoolType was invalid. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_ALLOCATE_POOL)( + IN EFI_MEMORY_TYPE PoolType, + IN UINTN Size, + OUT VOID **Buffer + ); + +/** + Returns pool memory to the system. + + @param Buffer Pointer to the buffer to free. + + @retval EFI_SUCCESS The memory was returned to the system. + @retval EFI_INVALID_PARAMETER Buffer was invalid. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_FREE_POOL)( + IN VOID *Buffer + ); + +/** + Changes the runtime addressing mode of EFI firmware from physical to virtual. + + @param MemoryMapSize The size in bytes of VirtualMap. + @param DescriptorSize The size in bytes of an entry in the VirtualMap. + @param DescriptorVersion The version of the structure entries in VirtualMap. + @param VirtualMap An array of memory descriptors which contain new virtual + address mapping information for all runtime ranges. + + @retval EFI_SUCCESS The virtual address map has been applied. + @retval EFI_UNSUPPORTED EFI firmware is not at runtime, or the EFI firmware is already in + virtual address mapped mode. + @retval EFI_INVALID_PARAMETER DescriptorSize or DescriptorVersion is invalid. + @retval EFI_NO_MAPPING A virtual address was not supplied for a range in the memory + map that requires a mapping. + @retval EFI_NOT_FOUND A virtual address was supplied for an address that is not found + in the memory map. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SET_VIRTUAL_ADDRESS_MAP)( + IN UINTN MemoryMapSize, + IN UINTN DescriptorSize, + IN UINT32 DescriptorVersion, + IN EFI_MEMORY_DESCRIPTOR *VirtualMap + ); + +/** + Connects one or more drivers to a controller. + + @param ControllerHandle The handle of the controller to which driver(s) are to be connected. + @param DriverImageHandle A pointer to an ordered list handles that support the + EFI_DRIVER_BINDING_PROTOCOL. + @param RemainingDevicePath A pointer to the device path that specifies a child of the + controller specified by ControllerHandle. + @param Recursive If TRUE, then ConnectController() is called recursively + until the entire tree of controllers below the controller specified + by ControllerHandle have been created. If FALSE, then + the tree of controllers is only expanded one level. + + @retval EFI_SUCCESS 1) One or more drivers were connected to ControllerHandle. + 2) No drivers were connected to ControllerHandle, but + RemainingDevicePath is not NULL, and it is an End Device + Path Node. + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + @retval EFI_NOT_FOUND 1) There are no EFI_DRIVER_BINDING_PROTOCOL instances + present in the system. + 2) No drivers were connected to ControllerHandle. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_CONNECT_CONTROLLER)( + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE *DriverImageHandle, OPTIONAL + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath, OPTIONAL + IN BOOLEAN Recursive + ); + +/** + Disconnects one or more drivers from a controller. + + @param ControllerHandle The handle of the controller from which driver(s) are to be disconnected. + @param DriverImageHandle The driver to disconnect from ControllerHandle. + If DriverImageHandle is NULL, then all the drivers currently managing + ControllerHandle are disconnected from ControllerHandle. + @param ChildHandle The handle of the child to destroy. + If ChildHandle is NULL, then all the children of ControllerHandle are + destroyed before the drivers are disconnected from ControllerHandle. + + @retval EFI_SUCCESS 1) One or more drivers were disconnected from the controller. + 2) On entry, no drivers are managing ControllerHandle. + 3) DriverImageHandle is not NULL, and on entry + DriverImageHandle is not managing ControllerHandle. + @retval EFI_INVALID_PARAMETER 1) ControllerHandle is not a valid EFI_HANDLE. + 2) DriverImageHandle is not NULL, and it is not a valid EFI_HANDLE. + 3) ChildHandle is not NULL, and it is not a valid EFI_HANDLE. + 4) DriverImageHandle does not support the EFI_DRIVER_BINDING_PROTOCOL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to disconnect any drivers from + ControllerHandle. + @retval EFI_DEVICE_ERROR The controller could not be disconnected because of a device error. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_DISCONNECT_CONTROLLER)( + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE DriverImageHandle, OPTIONAL + IN EFI_HANDLE ChildHandle OPTIONAL + ); + + + +// +// ConvertPointer DebugDisposition type. +// +#define EFI_OPTIONAL_PTR 0x00000001 +#define EFI_OPTIONAL_POINTER EFI_OPTIONAL_PTR + +/** + Determines the new virtual address that is to be used on subsequent memory accesses. + + @param DebugDisposition Supplies type information for the pointer being converted. + @param Address A pointer to a pointer that is to be fixed to be the value needed + for the new virtual address mappings being applied. + + @retval EFI_SUCCESS The pointer pointed to by Address was modified. + @retval EFI_INVALID_PARAMETER 1) Address is NULL. + 2) *Address is NULL and DebugDisposition does + not have the EFI_OPTIONAL_PTR bit set. + @retval EFI_NOT_FOUND The pointer pointed to by Address was not found to be part + of the current memory map. This is normally fatal. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_CONVERT_POINTER)( + IN UINTN DebugDisposition, + IN OUT VOID **Address + ); + + +// +// These types can be ORed together as needed - for example, +// EVT_TIMER might be Ored with EVT_NOTIFY_WAIT or +// EVT_NOTIFY_SIGNAL. +// +#define EVT_TIMER 0x80000000 +#define EVT_RUNTIME 0x40000000 +#define EVT_NOTIFY_WAIT 0x00000100 +#define EVT_NOTIFY_SIGNAL 0x00000200 + +#define EVT_SIGNAL_EXIT_BOOT_SERVICES 0x00000201 +#define EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE 0x60000202 + +// +// The event's NotifyContext pointer points to a runtime memory +// address. +// The event is deprecated in UEFI2.0 and later specifications. +// +#define EVT_RUNTIME_CONTEXT 0x20000000 + + +/** + Invoke a notification event + + @param Event Event whose notification function is being invoked. + @param Context Pointer to the notification function's context, + which is implementation-dependent. + +**/ +typedef +VOID +(EFIAPI *EFI_EVENT_NOTIFY)( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Creates an event. + + @param Type The type of event to create and its mode and attributes. + @param NotifyTpl The task priority level of event notifications, if needed. + @param NotifyFunction Pointer to the event's notification function, if any. + @param NotifyContext Pointer to the notification function's context; corresponds to parameter + Context in the notification function. + @param Event Pointer to the newly created event if the call succeeds; undefined + otherwise. + + @retval EFI_SUCCESS The event structure was created. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The event could not be allocated. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_CREATE_EVENT)( + IN UINT32 Type, + IN EFI_TPL NotifyTpl, + IN EFI_EVENT_NOTIFY NotifyFunction, + IN VOID *NotifyContext, + OUT EFI_EVENT *Event + ); + +/** + Creates an event in a group. + + @param Type The type of event to create and its mode and attributes. + @param NotifyTpl The task priority level of event notifications,if needed. + @param NotifyFunction Pointer to the event's notification function, if any. + @param NotifyContext Pointer to the notification function's context; corresponds to parameter + Context in the notification function. + @param EventGroup Pointer to the unique identifier of the group to which this event belongs. + If this is NULL, then the function behaves as if the parameters were passed + to CreateEvent. + @param Event Pointer to the newly created event if the call succeeds; undefined + otherwise. + + @retval EFI_SUCCESS The event structure was created. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The event could not be allocated. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_CREATE_EVENT_EX)( + IN UINT32 Type, + IN EFI_TPL NotifyTpl, + IN EFI_EVENT_NOTIFY NotifyFunction OPTIONAL, + IN CONST VOID *NotifyContext OPTIONAL, + IN CONST EFI_GUID *EventGroup OPTIONAL, + OUT EFI_EVENT *Event + ); + +/// +/// Timer delay types +/// +typedef enum { + TimerCancel, + TimerPeriodic, + TimerRelative +} EFI_TIMER_DELAY; + +/** + Sets the type of timer and the trigger time for a timer event. + + @param Event The timer event that is to be signaled at the specified time. + @param Type The type of time that is specified in TriggerTime. + @param TriggerTime The number of 100ns units until the timer expires. + A TriggerTime of 0 is legal. + If Type is TimerRelative and TriggerTime is 0, then the timer + event will be signaled on the next timer tick. + If Type is TimerPeriodic and TriggerTime is 0, then the timer + event will be signaled on every timer tick. + + @retval EFI_SUCCESS The event has been set to be signaled at the requested time. + @retval EFI_INVALID_PARAMETER Event or Type is not valid. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SET_TIMER)( + IN EFI_EVENT Event, + IN EFI_TIMER_DELAY Type, + IN UINT64 TriggerTime + ); + +/** + Signals an event. + + @param Event The event to signal. + + @retval EFI_SUCCESS The event has been signaled. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SIGNAL_EVENT)( + IN EFI_EVENT Event + ); + +/** + Stops execution until an event is signaled. + + @param NumberOfEvents The number of events in the Event array. + @param Event An array of EFI_EVENT. + @param Index Pointer to the index of the event which satisfied the wait condition. + + @retval EFI_SUCCESS The event indicated by Index was signaled. + @retval EFI_INVALID_PARAMETER 1) NumberOfEvents is 0. + 2) The event indicated by Index is of type + EVT_NOTIFY_SIGNAL. + @retval EFI_UNSUPPORTED The current TPL is not TPL_APPLICATION. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_WAIT_FOR_EVENT)( + IN UINTN NumberOfEvents, + IN EFI_EVENT *Event, + OUT UINTN *Index + ); + +/** + Closes an event. + + @param Event The event to close. + + @retval EFI_SUCCESS The event has been closed. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_CLOSE_EVENT)( + IN EFI_EVENT Event + ); + +/** + Checks whether an event is in the signaled state. + + @param Event The event to check. + + @retval EFI_SUCCESS The event is in the signaled state. + @retval EFI_NOT_READY The event is not in the signaled state. + @retval EFI_INVALID_PARAMETER Event is of type EVT_NOTIFY_SIGNAL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_CHECK_EVENT)( + IN EFI_EVENT Event + ); + + +// +// Task priority level +// +#define TPL_APPLICATION 4 +#define TPL_CALLBACK 8 +#define TPL_NOTIFY 16 +#define TPL_HIGH_LEVEL 31 + + +/** + Raises a task's priority level and returns its previous level. + + @param NewTpl The new task priority level. + + @return Previous task priority level + +**/ +typedef +EFI_TPL +(EFIAPI *EFI_RAISE_TPL)( + IN EFI_TPL NewTpl + ); + +/** + Restores a task's priority level to its previous value. + + @param OldTpl The previous task priority level to restore. + +**/ +typedef +VOID +(EFIAPI *EFI_RESTORE_TPL)( + IN EFI_TPL OldTpl + ); + +/** + Returns the value of a variable. + + @param VariableName A Null-terminated Unicode string that is the name of the + vendor's variable. + @param VendorGuid A unique identifier for the vendor. + @param Attributes If not NULL, a pointer to the memory location to return the + attributes bitmask for the variable. + @param DataSize On input, the size in bytes of the return Data buffer. + On output the size of data returned in Data. + @param Data The buffer to return the contents of the variable. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_NOT_FOUND The variable was not found. + @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the result. + @retval EFI_INVALID_PARAMETER VariableName is NULL. + @retval EFI_INVALID_PARAMETER VendorGuid is NULL. + @retval EFI_INVALID_PARAMETER DataSize is NULL. + @retval EFI_INVALID_PARAMETER The DataSize is not too small and Data is NULL. + @retval EFI_DEVICE_ERROR The variable could not be retrieved due to a hardware error. + @retval EFI_SECURITY_VIOLATION The variable could not be retrieved due to an authentication failure. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_GET_VARIABLE)( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT UINT32 *Attributes, OPTIONAL + IN OUT UINTN *DataSize, + OUT VOID *Data + ); + +/** + Enumerates the current variable names. + + @param VariableNameSize The size of the VariableName buffer. + @param VariableName On input, supplies the last VariableName that was returned + by GetNextVariableName(). On output, returns the Nullterminated + Unicode string of the current variable. + @param VendorGuid On input, supplies the last VendorGuid that was returned by + GetNextVariableName(). On output, returns the + VendorGuid of the current variable. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_NOT_FOUND The next variable was not found. + @retval EFI_BUFFER_TOO_SMALL The VariableNameSize is too small for the result. + @retval EFI_INVALID_PARAMETER VariableNameSize is NULL. + @retval EFI_INVALID_PARAMETER VariableName is NULL. + @retval EFI_INVALID_PARAMETER VendorGuid is NULL. + @retval EFI_DEVICE_ERROR The variable could not be retrieved due to a hardware error. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_GET_NEXT_VARIABLE_NAME)( + IN OUT UINTN *VariableNameSize, + IN OUT CHAR16 *VariableName, + IN OUT EFI_GUID *VendorGuid + ); + +/** + Sets the value of a variable. + + @param VariableName A Null-terminated Unicode string that is the name of the + vendor's variable. + @param VendorGuid A unique identifier for the vendor. + @param Attributes Attributes bitmask to set for the variable. + @param DataSize The size in bytes of the Data buffer. + @param Data The contents for the variable. + + @retval EFI_SUCCESS The firmware has successfully stored the variable and its data as + defined by the Attributes. + @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits was supplied, or the + DataSize exceeds the maximum allowed. + @retval EFI_INVALID_PARAMETER VariableName is an empty Unicode string. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data. + @retval EFI_DEVICE_ERROR The variable could not be retrieved due to a hardware error. + @retval EFI_WRITE_PROTECTED The variable in question is read-only. + @retval EFI_WRITE_PROTECTED The variable in question cannot be deleted. + @retval EFI_SECURITY_VIOLATION The variable could not be retrieved due to an authentication failure. + @retval EFI_NOT_FOUND The variable trying to be updated or deleted was not found. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SET_VARIABLE)( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN UINTN DataSize, + IN VOID *Data + ); + + +/// +/// This provides the capabilities of the +/// real time clock device as exposed through the EFI interfaces. +/// +typedef struct { + UINT32 Resolution; + UINT32 Accuracy; + BOOLEAN SetsToZero; +} EFI_TIME_CAPABILITIES; + +/** + Returns the current time and date information, and the time-keeping capabilities + of the hardware platform. + + @param Time A pointer to storage to receive a snapshot of the current time. + @param Capabilities An optional pointer to a buffer to receive the real time clock + device's capabilities. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER Time is NULL. + @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_GET_TIME)( + OUT EFI_TIME *Time, + OUT EFI_TIME_CAPABILITIES *Capabilities OPTIONAL + ); + +/** + Sets the current local time and date information. + + @param Time A pointer to the current time. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER A time field is out of range. + @retval EFI_DEVICE_ERROR The time could not be set due due to hardware error. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SET_TIME)( + IN EFI_TIME *Time + ); + +/** + Returns the current wakeup alarm clock setting. + + @param Enabled Indicates if the alarm is currently enabled or disabled. + @param Pending Indicates if the alarm signal is pending and requires acknowledgement. + @param Time The current alarm setting. + + @retval EFI_SUCCESS The alarm settings were returned. + @retval EFI_INVALID_PARAMETER Enabled is NULL. + @retval EFI_INVALID_PARAMETER Pending is NULL. + @retval EFI_INVALID_PARAMETER Time is NULL. + @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a hardware error. + @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_GET_WAKEUP_TIME)( + OUT BOOLEAN *Enabled, + OUT BOOLEAN *Pending, + OUT EFI_TIME *Time + ); + +/** + Sets the system wakeup alarm clock time. + + @param Enabled Enable or disable the wakeup alarm. + @param Time If Enable is TRUE, the time to set the wakeup alarm for. + If Enable is FALSE, then this parameter is optional, and may be NULL. + + @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was enabled. If + Enable is FALSE, then the wakeup alarm was disabled. + @retval EFI_INVALID_PARAMETER A time field is out of range. + @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a hardware error. + @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SET_WAKEUP_TIME)( + IN BOOLEAN Enable, + IN EFI_TIME *Time OPTIONAL + ); + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including + both device drivers and bus drivers. + + @param ImageHandle The firmware allocated handle for the UEFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_IMAGE_ENTRY_POINT)( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +/** + Loads an EFI image into memory. + + @param BootPolicy If TRUE, indicates that the request originates from the boot + manager, and that the boot manager is attempting to load + FilePath as a boot selection. Ignored if SourceBuffer is + not NULL. + @param ParentImageHandle The caller's image handle. + @param DevicePath The DeviceHandle specific file path from which the image is + loaded. + @param SourceBuffer If not NULL, a pointer to the memory location containing a copy + of the image to be loaded. + @param SourceSize The size in bytes of SourceBuffer. Ignored if SourceBuffer is NULL. + @param ImageHandle Pointer to the returned image handle that is created when the + image is successfully loaded. + + @retval EFI_SUCCESS Image was loaded into memory correctly. + @retval EFI_NOT_FOUND Both SourceBuffer and DevicePath are NULL. + @retval EFI_INVALID_PARAMETER One or more parametes are invalid. + @retval EFI_UNSUPPORTED The image type is not supported. + @retval EFI_OUT_OF_RESOURCES Image was not loaded due to insufficient resources. + @retval EFI_LOAD_ERROR Image was not loaded because the image format was corrupt or not + understood. + @retval EFI_DEVICE_ERROR Image was not loaded because the device returned a read error. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_IMAGE_LOAD)( + IN BOOLEAN BootPolicy, + IN EFI_HANDLE ParentImageHandle, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN VOID *SourceBuffer OPTIONAL, + IN UINTN SourceSize, + OUT EFI_HANDLE *ImageHandle + ); + +/** + Transfers control to a loaded image's entry point. + + @param ImageHandle Handle of image to be started. + @param ExitDataSize Pointer to the size, in bytes, of ExitData. + @param ExitData Pointer to a pointer to a data buffer that includes a Null-terminated + Unicode string, optionally followed by additional binary data. + + @retval EFI_INVALID_PARAMETER ImageHandle is either an invalid image handle or the image + has already been initialized with StartImage + @return Exit code from image + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_IMAGE_START)( + IN EFI_HANDLE ImageHandle, + OUT UINTN *ExitDataSize, + OUT CHAR16 **ExitData OPTIONAL + ); + +/** + Terminates a loaded EFI image and returns control to boot services. + + @param ImageHandle Handle that identifies the image. + @param ExitStatus The image's exit code. + @param ExitDataSize The size, in bytes, of ExitData. + @param ExitData Pointer to a data buffer that includes a Null-terminated Unicode string, + optionally followed by additional binary data. + + @retval EFI_SUCCESS The image specified by ImageHandle was unloaded. + @retval EFI_INVALID_PARAMETER The image specified by ImageHandle has been loaded and + started with LoadImage() and StartImage(), but the + image is not the currently executing image. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXIT)( + IN EFI_HANDLE ImageHandle, + IN EFI_STATUS ExitStatus, + IN UINTN ExitDataSize, + IN CHAR16 *ExitData OPTIONAL + ); + +/** + Unloads an image. + + @param ImageHandle Handle that identifies the image to be unloaded. + + @retval EFI_SUCCESS The image has been unloaded. + @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle. + @retval EFI_UNSUPPORTED The image has been started, and does not support unload. + @return Exit code from the image's unload handler + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_IMAGE_UNLOAD)( + IN EFI_HANDLE ImageHandle + ); + +/** + Terminates all boot services. + + @param ImageHandle Handle that identifies the exiting image. + @param MapKey Key to the latest memory map. + + @retval EFI_SUCCESS Boot services have been terminated. + @retval EFI_INVALID_PARAMETER MapKey is incorrect. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_EXIT_BOOT_SERVICES)( + IN EFI_HANDLE ImageHandle, + IN UINTN MapKey + ); + +/** + Induces a fine-grained stall. + + @param Microseconds The number of microseconds to stall execution. + + @retval EFI_SUCCESS Execution was stalled at least the requested number of + Microseconds. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_STALL)( + IN UINTN Microseconds + ); + +/** + Sets the system's watchdog timer. + + @param Timeout The number of seconds to set the watchdog timer to. + @param WatchdogCode The numeric code to log on a watchdog timer timeout event. + @param DataSize The size, in bytes, of WatchdogData. + @param WatchdogData A data buffer that includes a Null-terminated Unicode string, optionally + followed by additional binary data. + + @retval EFI_SUCCESS The timeout has been set. + @retval EFI_INVALID_PARAMETER The supplied WatchdogCode is invalid. + @retval EFI_UNSUPPORTED The system does not have a watchdog timer. + @retval EFI_DEVICE_ERROR The watch dog timer could not be programmed due to a hardware + error. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_SET_WATCHDOG_TIMER)( + IN UINTN Timeout, + IN UINT64 WatchdogCode, + IN UINTN DataSize, + IN CHAR16 *WatchdogData OPTIONAL + ); + +/// +/// Enumeration of reset types. +/// +typedef enum { + EfiResetCold, + EfiResetWarm, + EfiResetShutdown, + EfiResetUpdate +} EFI_RESET_TYPE; + +/** + Resets the entire platform. + + @param ResetType The type of reset to perform. + @param ResetStatus The status code for the reset. + @param DataSize The size, in bytes, of WatchdogData. + @param ResetData For a ResetType of EfiResetCold, EfiResetWarm, or + EfiResetShutdown the data buffer starts with a Null-terminated + Unicode string, optionally followed by additional binary data. + +**/ +typedef +VOID +(EFIAPI *EFI_RESET_SYSTEM)( + IN EFI_RESET_TYPE ResetType, + IN EFI_STATUS ResetStatus, + IN UINTN DataSize, + IN CHAR16 *ResetData OPTIONAL + ); + +/** + Returns a monotonically increasing count for the platform. + + @param Count Pointer to returned value. + + @retval EFI_SUCCESS The next monotonic count was returned. + @retval EFI_INVALID_PARAMETER Count is NULL. + @retval EFI_DEVICE_ERROR The device is not functioning properly. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_GET_NEXT_MONOTONIC_COUNT)( + OUT UINT64 *Count + ); + +/** + Returns the next high 32 bits of the platform's monotonic counter. + + @param HighCount Pointer to returned value. + + @retval EFI_SUCCESS The next high monotonic count was returned. + @retval EFI_INVALID_PARAMETER HighCount is NULL. + @retval EFI_DEVICE_ERROR The device is not functioning properly. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_GET_NEXT_HIGH_MONO_COUNT)( + OUT UINT32 *HighCount + ); + +/** + Computes and returns a 32-bit CRC for a data buffer. + + @param Data A pointer to the buffer on which the 32-bit CRC is to be computed. + @param DataSize The number of bytes in the buffer Data. + @param Crc32 The 32-bit CRC that was computed for the data buffer specified by Data + and DataSize. + + @retval EFI_SUCCESS The 32-bit CRC was computed for the data buffer and returned in + Crc32. + @retval EFI_INVALID_PARAMETER Data is NULL. + @retval EFI_INVALID_PARAMETER Crc32 is NULL. + @retval EFI_INVALID_PARAMETER DataSize is 0. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_CALCULATE_CRC32)( + IN VOID *Data, + IN UINTN DataSize, + OUT UINT32 *Crc32 + ); + +/** + Copies the contents of one buffer to another buffer. + + @param Destination Pointer to the destination buffer of the memory copy. + @param Source Pointer to the source buffer of the memory copy. + @param Length Number of bytes to copy from Source to Destination. + +**/ +typedef +VOID +(EFIAPI *EFI_COPY_MEM)( + IN VOID *Destination, + IN VOID *Source, + IN UINTN Length + ); + +/** + The SetMem() function fills a buffer with a specified value. + + @param Buffer Pointer to the buffer to fill. + @param Size Number of bytes in Buffer to fill. + @param Value Value to fill Buffer with. + +**/ +typedef +VOID +(EFIAPI *EFI_SET_MEM)( + IN VOID *Buffer, + IN UINTN Size, + IN UINT8 Value + ); + + +// +// Protocol handler functions +// +typedef enum { + EFI_NATIVE_INTERFACE +} EFI_INTERFACE_TYPE; + +/** + Installs a protocol interface on a device handle. If the handle does not exist, it is created and added + to the list of handles in the system. InstallMultipleProtocolInterfaces() performs + more error checking than InstallProtocolInterface(), so it is recommended that + InstallMultipleProtocolInterfaces() be used in place of + InstallProtocolInterface() + + @param Handle A pointer to the EFI_HANDLE on which the interface is to be installed. + @param Protocol The numeric ID of the protocol interface. + @param InterfaceType Indicates whether Interface is supplied in native form. + @param Interface A pointer to the protocol interface. + + @retval EFI_SUCCESS The protocol interface was installed. + @retval EFI_OUT_OF_RESOURCES Space for a new handle could not be allocated. + @retval EFI_INVALID_PARAMETER Handle is NULL. + @retval EFI_INVALID_PARAMETER Protocol is NULL. + @retval EFI_INVALID_PARAMETER InterfaceType is not EFI_NATIVE_INTERFACE. + @retval EFI_INVALID_PARAMETER Protocol is already installed on the handle specified by Handle. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_INSTALL_PROTOCOL_INTERFACE)( + IN OUT EFI_HANDLE *Handle, + IN EFI_GUID *Protocol, + IN EFI_INTERFACE_TYPE InterfaceType, + IN VOID *Interface + ); + +/** + Installs one or more protocol interfaces into the boot services environment. + + @param Handle The handle to install the new protocol interfaces on, or NULL if a new + handle is to be allocated. + @param ... A variable argument list containing pairs of protocol GUIDs and protocol + interfaces. + + @retval EFI_SUCCESS All the protocol interface was installed. + @retval EFI_OUT_OF_RESOURCES There was not enough memory in pool to install all the protocols. + @retval EFI_ALREADY_STARTED A Device Path Protocol instance was passed in that is already present in + the handle database. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_INSTALL_MULTIPLE_PROTOCOL_INTERFACES)( + IN OUT EFI_HANDLE *Handle, + ... + ); + +/** + Reinstalls a protocol interface on a device handle. + + @param Handle Handle on which the interface is to be reinstalled. + @param Protocol The numeric ID of the interface. + @param OldInterface A pointer to the old interface. NULL can be used if a structure is not + associated with Protocol. + @param NewInterface A pointer to the new interface. + + @retval EFI_SUCCESS The protocol interface was reinstalled. + @retval EFI_NOT_FOUND The OldInterface on the handle was not found. + @retval EFI_ACCESS_DENIED The protocol interface could not be reinstalled, + because OldInterface is still being used by a + driver that will not release it. + @retval EFI_INVALID_PARAMETER Handle is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Protocol is NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_REINSTALL_PROTOCOL_INTERFACE)( + IN EFI_HANDLE Handle, + IN EFI_GUID *Protocol, + IN VOID *OldInterface, + IN VOID *NewInterface + ); + +/** + Removes a protocol interface from a device handle. It is recommended that + UninstallMultipleProtocolInterfaces() be used in place of + UninstallProtocolInterface(). + + @param Handle The handle on which the interface was installed. + @param Protocol The numeric ID of the interface. + @param Interface A pointer to the interface. + + @retval EFI_SUCCESS The interface was removed. + @retval EFI_NOT_FOUND The interface was not found. + @retval EFI_ACCESS_DENIED The interface was not removed because the interface + is still being used by a driver. + @retval EFI_INVALID_PARAMETER Handle is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Protocol is NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_UNINSTALL_PROTOCOL_INTERFACE)( + IN EFI_HANDLE Handle, + IN EFI_GUID *Protocol, + IN VOID *Interface + ); + +/** + Removes one or more protocol interfaces into the boot services environment. + + @param Handle The handle to remove the protocol interfaces from. + @param ... A variable argument list containing pairs of protocol GUIDs and + protocol interfaces. + + @retval EFI_SUCCESS All the protocol interfaces were removed. + @retval EFI_INVALID_PARAMETER One of the protocol interfaces was not previously installed on Handle. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_UNINSTALL_MULTIPLE_PROTOCOL_INTERFACES)( + IN EFI_HANDLE Handle, + ... + ); + +/** + Queries a handle to determine if it supports a specified protocol. + + @param Handle The handle being queried. + @param Protocol The published unique identifier of the protocol. + @param Interface Supplies the address where a pointer to the corresponding Protocol + Interface is returned. + + @retval EFI_SUCCESS The interface information for the specified protocol was returned. + @retval EFI_UNSUPPORTED The device does not support the specified protocol. + @retval EFI_INVALID_PARAMETER Handle is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER Protocol is NULL. + @retval EFI_INVALID_PARAMETER Interface is NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_HANDLE_PROTOCOL)( + IN EFI_HANDLE Handle, + IN EFI_GUID *Protocol, + OUT VOID **Interface + ); + +#define EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL 0x00000001 +#define EFI_OPEN_PROTOCOL_GET_PROTOCOL 0x00000002 +#define EFI_OPEN_PROTOCOL_TEST_PROTOCOL 0x00000004 +#define EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER 0x00000008 +#define EFI_OPEN_PROTOCOL_BY_DRIVER 0x00000010 +#define EFI_OPEN_PROTOCOL_EXCLUSIVE 0x00000020 + +/** + Queries a handle to determine if it supports a specified protocol. If the protocol is supported by the + handle, it opens the protocol on behalf of the calling agent. + + @param Handle The handle for the protocol interface that is being opened. + @param Protocol The published unique identifier of the protocol. + @param Interface Supplies the address where a pointer to the corresponding Protocol + Interface is returned. + @param AgentHandle The handle of the agent that is opening the protocol interface + specified by Protocol and Interface. + @param ControllerHandle If the agent that is opening a protocol is a driver that follows the + UEFI Driver Model, then this parameter is the controller handle + that requires the protocol interface. If the agent does not follow + the UEFI Driver Model, then this parameter is optional and may + be NULL. + @param Attributes The open mode of the protocol interface specified by Handle + and Protocol. + + @retval EFI_SUCCESS An item was added to the open list for the protocol interface, and the + protocol interface was returned in Interface. + @retval EFI_UNSUPPORTED Handle does not support Protocol. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_ACCESS_DENIED Required attributes can't be supported in current environment. + @retval EFI_ALREADY_STARTED Item on the open list already has requierd attributes whose agent + handle is the same as AgentHandle. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_OPEN_PROTOCOL)( + IN EFI_HANDLE Handle, + IN EFI_GUID *Protocol, + OUT VOID **Interface, OPTIONAL + IN EFI_HANDLE AgentHandle, + IN EFI_HANDLE ControllerHandle, + IN UINT32 Attributes + ); + + +/** + Closes a protocol on a handle that was opened using OpenProtocol(). + + @param Handle The handle for the protocol interface that was previously opened + with OpenProtocol(), and is now being closed. + @param Protocol The published unique identifier of the protocol. + @param AgentHandle The handle of the agent that is closing the protocol interface. + @param ControllerHandle If the agent that opened a protocol is a driver that follows the + UEFI Driver Model, then this parameter is the controller handle + that required the protocol interface. + + @retval EFI_SUCCESS The protocol instance was closed. + @retval EFI_INVALID_PARAMETER 1) Handle is not a valid EFI_HANDLE. + 2) AgentHandle is not a valid EFI_HANDLE. + 3) ControllerHandle is not NULL and ControllerHandle is not a valid EFI_HANDLE. + 4) Protocol is NULL. + @retval EFI_NOT_FOUND 1) Handle does not support the protocol specified by Protocol. + 2) The protocol interface specified by Handle and Protocol is not + currently open by AgentHandle and ControllerHandle. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_CLOSE_PROTOCOL)( + IN EFI_HANDLE Handle, + IN EFI_GUID *Protocol, + IN EFI_HANDLE AgentHandle, + IN EFI_HANDLE ControllerHandle + ); + + +typedef struct { + EFI_HANDLE AgentHandle; + EFI_HANDLE ControllerHandle; + UINT32 Attributes; + UINT32 OpenCount; +} EFI_OPEN_PROTOCOL_INFORMATION_ENTRY; + +/** + Retrieves the list of agents that currently have a protocol interface opened. + + @param Handle The handle for the protocol interface that is being queried. + @param Protocol The published unique identifier of the protocol. + @param EntryBuffer A pointer to a buffer of open protocol information in the form of + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY structures. + @param EntryCount A pointer to the number of entries in EntryBuffer. + + @retval EFI_SUCCESS The open protocol information was returned in EntryBuffer, and the + number of entries was returned EntryCount. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to allocate EntryBuffer. + @retval EFI_NOT_FOUND Handle does not support the protocol specified by Protocol. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_OPEN_PROTOCOL_INFORMATION)( + IN EFI_HANDLE Handle, + IN EFI_GUID *Protocol, + OUT EFI_OPEN_PROTOCOL_INFORMATION_ENTRY **EntryBuffer, + OUT UINTN *EntryCount + ); + +/** + Retrieves the list of protocol interface GUIDs that are installed on a handle in a buffer allocated + from pool. + + @param Handle The handle from which to retrieve the list of protocol interface + GUIDs. + @param ProtocolBuffer A pointer to the list of protocol interface GUID pointers that are + installed on Handle. + @param ProtocolBufferCount A pointer to the number of GUID pointers present in + ProtocolBuffer. + + @retval EFI_SUCCESS The list of protocol interface GUIDs installed on Handle was returned in + ProtocolBuffer. The number of protocol interface GUIDs was + returned in ProtocolBufferCount. + @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the results. + @retval EFI_INVALID_PARAMETER Handle is NULL. + @retval EFI_INVALID_PARAMETER Handle is not a valid EFI_HANDLE. + @retval EFI_INVALID_PARAMETER ProtocolBuffer is NULL. + @retval EFI_INVALID_PARAMETER ProtocolBufferCount is NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_PROTOCOLS_PER_HANDLE)( + IN EFI_HANDLE Handle, + OUT EFI_GUID ***ProtocolBuffer, + OUT UINTN *ProtocolBufferCount + ); + +/** + Creates an event that is to be signaled whenever an interface is installed for a specified protocol. + + @param Protocol The numeric ID of the protocol for which the event is to be registered. + @param Event Event that is to be signaled whenever a protocol interface is registered + for Protocol. + @param Registration A pointer to a memory location to receive the registration value. + + @retval EFI_SUCCESS The notification event has been registered. + @retval EFI_OUT_OF_RESOURCES Space for the notification event could not be allocated. + @retval EFI_INVALID_PARAMETER Protocol is NULL. + @retval EFI_INVALID_PARAMETER Event is NULL. + @retval EFI_INVALID_PARAMETER Registration is NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_REGISTER_PROTOCOL_NOTIFY)( + IN EFI_GUID *Protocol, + IN EFI_EVENT Event, + OUT VOID **Registration + ); + + +typedef enum { + AllHandles, + ByRegisterNotify, + ByProtocol +} EFI_LOCATE_SEARCH_TYPE; + +/** + Returns an array of handles that support a specified protocol. + + @param SearchType Specifies which handle(s) are to be returned. + @param Protocol Specifies the protocol to search by. + @param SearchKey Specifies the search key. + @param BufferSize On input, the size in bytes of Buffer. On output, the size in bytes of + the array returned in Buffer (if the buffer was large enough) or the + size, in bytes, of the buffer needed to obtain the array (if the buffer was + not large enough). + @param Buffer The buffer in which the array is returned. + + @retval EFI_SUCCESS The array of handles was returned. + @retval EFI_NOT_FOUND No handles match the search. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small for the result. + @retval EFI_INVALID_PARAMETER SearchType is not a member of EFI_LOCATE_SEARCH_TYPE. + @retval EFI_INVALID_PARAMETER SearchType is ByRegisterNotify and SearchKey is NULL. + @retval EFI_INVALID_PARAMETER SearchType is ByProtocol and Protocol is NULL. + @retval EFI_INVALID_PARAMETER One or more matches are found and BufferSize is NULL. + @retval EFI_INVALID_PARAMETER BufferSize is large enough for the result and Buffer is NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_LOCATE_HANDLE)( + IN EFI_LOCATE_SEARCH_TYPE SearchType, + IN EFI_GUID *Protocol, OPTIONAL + IN VOID *SearchKey, OPTIONAL + IN OUT UINTN *BufferSize, + OUT EFI_HANDLE *Buffer + ); + +/** + Locates the handle to a device on the device path that supports the specified protocol. + + @param Protocol Specifies the protocol to search for. + @param DevicePath On input, a pointer to a pointer to the device path. On output, the device + path pointer is modified to point to the remaining part of the device + path. + @param Device A pointer to the returned device handle. + + @retval EFI_SUCCESS The resulting handle was returned. + @retval EFI_NOT_FOUND No handles match the search. + @retval EFI_INVALID_PARAMETER Protocol is NULL. + @retval EFI_INVALID_PARAMETER DevicePath is NULL. + @retval EFI_INVALID_PARAMETER A handle matched the search and Device is NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_LOCATE_DEVICE_PATH)( + IN EFI_GUID *Protocol, + IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath, + OUT EFI_HANDLE *Device + ); + +/** + Adds, updates, or removes a configuration table entry from the EFI System Table. + + @param Guid A pointer to the GUID for the entry to add, update, or remove. + @param Table A pointer to the configuration table for the entry to add, update, or + remove. May be NULL. + + @retval EFI_SUCCESS The (Guid, Table) pair was added, updated, or removed. + @retval EFI_NOT_FOUND An attempt was made to delete a nonexistent entry. + @retval EFI_INVALID_PARAMETER Guid is not valid. + @retval EFI_OUT_OF_RESOURCES There is not enough memory available to complete the operation. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_INSTALL_CONFIGURATION_TABLE)( + IN EFI_GUID *Guid, + IN VOID *Table + ); + + +/** + Returns an array of handles that support the requested protocol in a buffer allocated from pool. + + @param SearchType Specifies which handle(s) are to be returned. + @param Protocol Provides the protocol to search by. + This parameter is only valid for a SearchType of ByProtocol. + @param SearchKey Supplies the search key depending on the SearchType. + @param NoHandles The number of handles returned in Buffer. + @param Buffer A pointer to the buffer to return the requested array of handles that + support Protocol. + + @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of + handles in Buffer was returned in NoHandles. + @retval EFI_NOT_FOUND No handles match the search. + @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results. + @retval EFI_INVALID_PARAMETER NoHandles is NULL. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_LOCATE_HANDLE_BUFFER)( + IN EFI_LOCATE_SEARCH_TYPE SearchType, + IN EFI_GUID *Protocol, OPTIONAL + IN VOID *SearchKey, OPTIONAL + IN OUT UINTN *NoHandles, + OUT EFI_HANDLE **Buffer + ); + +/** + Returns the first protocol instance that matches the given protocol. + + @param Protocol Provides the protocol to search for. + @param Registration Optional registration key returned from + RegisterProtocolNotify(). + @param Interface On return, a pointer to the first interface that matches Protocol and + Registration. + + @retval EFI_SUCCESS A protocol instance matching Protocol was found and returned in + Interface. + @retval EFI_NOT_FOUND No protocol instances were found that match Protocol and + Registration. + @retval EFI_INVALID_PARAMETER Interface is NULL. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_LOCATE_PROTOCOL)( + IN EFI_GUID *Protocol, + IN VOID *Registration, OPTIONAL + OUT VOID **Interface + ); + +typedef struct { + UINT64 Length; + union { + EFI_PHYSICAL_ADDRESS DataBlock; + EFI_PHYSICAL_ADDRESS ContinuationPointer; + } Union; +} EFI_CAPSULE_BLOCK_DESCRIPTOR; + +typedef struct { + EFI_GUID CapsuleGuid; + UINT32 HeaderSize; + UINT32 Flags; + UINT32 CapsuleImageSize; +} EFI_CAPSULE_HEADER; + +// +// The EFI System Table entry must point to an array of capsules +// that contain the same CapsuleGuid value. The array must be +// prefixed by a UINT32 that represents the size of the array of capsules. +// +typedef struct { + UINT32 CapsuleArrayNumber; + VOID* CapsulePtr[1]; +} EFI_CAPSULE_TABLE; + +#define CAPSULE_FLAGS_PERSIST_ACROSS_RESET 0x00010000 +#define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000 + +/** + Passes capsules to the firmware with both virtual and physical mapping. Depending on the intended + consumption, the firmware may process the capsule immediately. If the payload should persist + across a system reset, the reset value returned from EFI_QueryCapsuleCapabilities must + be passed into ResetSystem() and will cause the capsule to be processed by the firmware as + part of the reset process. + + @param CapsuleHeaderArray Virtual pointer to an array of virtual pointers to the capsules + being passed into update capsule. + @param CapsuleCount Number of pointers to EFI_CAPSULE_HEADER in + CaspuleHeaderArray. + @param ScatterGatherList Physical pointer to a set of + EFI_CAPSULE_BLOCK_DESCRIPTOR that describes the + location in physical memory of a set of capsules. + + @retval EFI_SUCCESS Valid capsule was passed. If + CAPSULE_FLAGS_PERSIT_ACROSS_RESET is not set, the + capsule has been successfully processed by the firmware. + @retval EFI_DEVICE_ERROR The capsule update was started, but failed due to a device error. + @retval EFI_INVALID_PARAMETER CapsuleSize is NULL. + @retval EFI_UNSUPPORTED The capsule type is not supported on this platform. + @retval EFI_OUT_OF_RESOURCES There were insufficient resources to process the capsule. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_UPDATE_CAPSULE)( + IN EFI_CAPSULE_HEADER **CapsuleHeaderArray, + IN UINTN CapsuleCount, + IN EFI_PHYSICAL_ADDRESS ScatterGatherList OPTIONAL + ); + +/** + Returns if the capsule can be supported via UpdateCapsule(). + + @param CapsuleHeaderArray Virtual pointer to an array of virtual pointers to the capsules + being passed into update capsule. + @param CapsuleCount Number of pointers to EFI_CAPSULE_HEADER in + CaspuleHeaderArray. + @param MaxiumCapsuleSize On output the maximum size that UpdateCapsule() can + support as an argument to UpdateCapsule() via + CapsuleHeaderArray and ScatterGatherList. + @param ResetType Returns the type of reset required for the capsule update. + + @retval EFI_SUCCESS Valid answer returned. + @retval EFI_UNSUPPORTED The capsule type is not supported on this platform, and + MaximumCapsuleSize and ResetType are undefined. + @retval EFI_INVALID_PARAMETER MaximumCapsuleSize is NULL. + @retval EFI_OUT_OF_RESOURCES There were insufficient resources to process the query request. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_QUERY_CAPSULE_CAPABILITIES)( + IN EFI_CAPSULE_HEADER **CapsuleHeaderArray, + IN UINTN CapsuleCount, + OUT UINT64 *MaximumCapsuleSize, + OUT EFI_RESET_TYPE *ResetType + ); + +/** + Returns information about the EFI variables. + + @param Attributes Attributes bitmask to specify the type of variables on + which to return information. + @param MaximumVariableStorageSize On output the maximum size of the storage space + available for the EFI variables associated with the + attributes specified. + @param RemainingVariableStorageSize Returns the remaining size of the storage space + available for the EFI variables associated with the + attributes specified. + @param MaximumVariableSize Returns the maximum size of the individual EFI + variables associated with the attributes specified. + + @retval EFI_SUCCESS Valid answer returned. + @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits was supplied + @retval EFI_UNSUPPORTED The attribute is not supported on this platform, and the + MaximumVariableStorageSize, + RemainingVariableStorageSize, MaximumVariableSize + are undefined. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_QUERY_VARIABLE_INFO)( + IN UINT32 Attributes, + OUT UINT64 *MaximumVariableStorageSize, + OUT UINT64 *RemainingVariableStorageSize, + OUT UINT64 *MaximumVariableSize + ); + + +// +// EFI Runtime Services Table +// +#define EFI_SYSTEM_TABLE_SIGNATURE 0x5453595320494249ULL +#define EFI_SYSTEM_TABLE_REVISION ((2<<16) | (10)) +#define EFI_2_10_SYSTEM_TABLE_REVISION ((2<<16) | (10)) +#define EFI_2_00_SYSTEM_TABLE_REVISION ((2<<16) | (00)) +#define EFI_1_10_SYSTEM_TABLE_REVISION ((1<<16) | (10)) +#define EFI_1_02_SYSTEM_TABLE_REVISION ((1<<16) | (02)) + +#define EFI_RUNTIME_SERVICES_SIGNATURE 0x56524553544e5552ULL +#define EFI_RUNTIME_SERVICES_REVISION EFI_2_10_SYSTEM_TABLE_REVISION + +/// +/// EFI Runtime Services Table +/// +typedef struct { + /// + /// The table header for the EFI Runtime Services Table. + /// + EFI_TABLE_HEADER Hdr; + + // + // Time Services + // + EFI_GET_TIME GetTime; + EFI_SET_TIME SetTime; + EFI_GET_WAKEUP_TIME GetWakeupTime; + EFI_SET_WAKEUP_TIME SetWakeupTime; + + // + // Virtual Memory Services + // + EFI_SET_VIRTUAL_ADDRESS_MAP SetVirtualAddressMap; + EFI_CONVERT_POINTER ConvertPointer; + + // + // Variable Services + // + EFI_GET_VARIABLE GetVariable; + EFI_GET_NEXT_VARIABLE_NAME GetNextVariableName; + EFI_SET_VARIABLE SetVariable; + + // + // Miscellaneous Services + // + EFI_GET_NEXT_HIGH_MONO_COUNT GetNextHighMonotonicCount; + EFI_RESET_SYSTEM ResetSystem; + + // + // UEFI 2.0 Capsule Services + // + EFI_UPDATE_CAPSULE UpdateCapsule; + EFI_QUERY_CAPSULE_CAPABILITIES QueryCapsuleCapabilities; + + // + // Miscellaneous UEFI 2.0 Service + // + EFI_QUERY_VARIABLE_INFO QueryVariableInfo; +} EFI_RUNTIME_SERVICES; + + +#define EFI_BOOT_SERVICES_SIGNATURE 0x56524553544f4f42ULL +#define EFI_BOOT_SERVICES_REVISION EFI_2_10_SYSTEM_TABLE_REVISION + +/// +/// EFI Boot Services Table +/// +typedef struct { + /// + /// The table header for the EFI Boot Services Table. + /// + EFI_TABLE_HEADER Hdr; + + // + // Task Priority Services + // + EFI_RAISE_TPL RaiseTPL; + EFI_RESTORE_TPL RestoreTPL; + + // + // Memory Services + // + EFI_ALLOCATE_PAGES AllocatePages; + EFI_FREE_PAGES FreePages; + EFI_GET_MEMORY_MAP GetMemoryMap; + EFI_ALLOCATE_POOL AllocatePool; + EFI_FREE_POOL FreePool; + + // + // Event & Timer Services + // + EFI_CREATE_EVENT CreateEvent; + EFI_SET_TIMER SetTimer; + EFI_WAIT_FOR_EVENT WaitForEvent; + EFI_SIGNAL_EVENT SignalEvent; + EFI_CLOSE_EVENT CloseEvent; + EFI_CHECK_EVENT CheckEvent; + + // + // Protocol Handler Services + // + EFI_INSTALL_PROTOCOL_INTERFACE InstallProtocolInterface; + EFI_REINSTALL_PROTOCOL_INTERFACE ReinstallProtocolInterface; + EFI_UNINSTALL_PROTOCOL_INTERFACE UninstallProtocolInterface; + EFI_HANDLE_PROTOCOL HandleProtocol; + VOID *Reserved; + EFI_REGISTER_PROTOCOL_NOTIFY RegisterProtocolNotify; + EFI_LOCATE_HANDLE LocateHandle; + EFI_LOCATE_DEVICE_PATH LocateDevicePath; + EFI_INSTALL_CONFIGURATION_TABLE InstallConfigurationTable; + + // + // Image Services + // + EFI_IMAGE_LOAD LoadImage; + EFI_IMAGE_START StartImage; + EFI_EXIT Exit; + EFI_IMAGE_UNLOAD UnloadImage; + EFI_EXIT_BOOT_SERVICES ExitBootServices; + + // + // Miscellaneous Services + // + EFI_GET_NEXT_MONOTONIC_COUNT GetNextMonotonicCount; + EFI_STALL Stall; + EFI_SET_WATCHDOG_TIMER SetWatchdogTimer; + + // + // DriverSupport Services + // + EFI_CONNECT_CONTROLLER ConnectController; + EFI_DISCONNECT_CONTROLLER DisconnectController; + + // + // Open and Close Protocol Services + // + EFI_OPEN_PROTOCOL OpenProtocol; + EFI_CLOSE_PROTOCOL CloseProtocol; + EFI_OPEN_PROTOCOL_INFORMATION OpenProtocolInformation; + + // + // Library Services + // + EFI_PROTOCOLS_PER_HANDLE ProtocolsPerHandle; + EFI_LOCATE_HANDLE_BUFFER LocateHandleBuffer; + EFI_LOCATE_PROTOCOL LocateProtocol; + EFI_INSTALL_MULTIPLE_PROTOCOL_INTERFACES InstallMultipleProtocolInterfaces; + EFI_UNINSTALL_MULTIPLE_PROTOCOL_INTERFACES UninstallMultipleProtocolInterfaces; + + // + // 32-bit CRC Services + // + EFI_CALCULATE_CRC32 CalculateCrc32; + + // + // Miscellaneous Services + // + EFI_COPY_MEM CopyMem; + EFI_SET_MEM SetMem; + EFI_CREATE_EVENT_EX CreateEventEx; +} EFI_BOOT_SERVICES; + +/// +/// Contains a set of GUID/pointer pairs comprised of the ConfigurationTable field in the +/// EFI System Table. +/// +typedef struct{ + /// + /// The 128-bit GUID value that uniquely identifies the system configuration table. + /// + EFI_GUID VendorGuid; + /// + /// A pointer to the table associated with VendorGuid. + /// + VOID *VendorTable; +} EFI_CONFIGURATION_TABLE; + +/// +/// EFI System Table +/// +struct _EFI_SYSTEM_TABLE { + /// + /// The table header for the EFI System Table. + /// + EFI_TABLE_HEADER Hdr; + /// + /// A pointer to a null terminated Unicode string that identifies + /// the vendor that produces the system firmware for the platform. + /// + CHAR16 *FirmwareVendor; + /// + /// A firmware vendor specific value that identifies the revision + /// of the system firmware for the platform. + /// + UINT32 FirmwareRevision; + /// + /// The handle for the active console input device. + /// + EFI_HANDLE ConsoleInHandle; + /// + /// A pointer to the SIMPLE_INPUT_PROTOCOL interface that is + /// associated with ConsoleInHandle. + /// + EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn; + /// + /// The handle for the active console output device. + /// + EFI_HANDLE ConsoleOutHandle; + /// + /// A pointer to the SIMPLE_TEXT_OUTPUT_PROTOCOL interface + /// that is associated with ConsoleOutHandle. + /// + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut; + /// + /// The handle for the active standard error console device. + /// + EFI_HANDLE StandardErrorHandle; + /// + /// A pointer to the SIMPLE_TEXT_OUTPUT_PROTOCOL interface + /// that is associated with StandardErrorHandle. + /// + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *StdErr; + /// + /// A pointer to the EFI Runtime Services Table. + /// + EFI_RUNTIME_SERVICES *RuntimeServices; + /// + /// A pointer to the EFI Boot Services Table. + /// + EFI_BOOT_SERVICES *BootServices; + /// + /// The number of system configuration tables in the buffer ConfigurationTable. + /// + UINTN NumberOfTableEntries; + /// + /// A pointer to the system configuration tables. + /// The number of entries in the table is NumberOfTableEntries. + /// + EFI_CONFIGURATION_TABLE *ConfigurationTable; +}; + +// +// EFI Load Options Attributes +// +#define LOAD_OPTION_ACTIVE 0x00000001 +#define LOAD_OPTION_FORCE_RECONNECT 0x00000002 +#define LOAD_OPTION_HIDDEN 0x00000008 +#define LOAD_OPTION_CATEGORY 0x00001F00 + +#define LOAD_OPTION_CATEGORY_BOOT 0x00000000 +#define LOAD_OPTION_CATEGORY_APP 0x00000100 + +#define EFI_BOOT_OPTION_SUPPORT_KEY 0x00000001 +#define EFI_BOOT_OPTION_SUPPORT_APP 0x00000002 +#define EFI_BOOT_OPTION_SUPPORT_COUNT 0x00000300 + +typedef union { + struct { + UINT32 Revision : 8; + UINT32 ShiftPressed : 1; + UINT32 ControlPressed : 1; + UINT32 AltPressed : 1; + UINT32 LogoPressed : 1; + UINT32 MenuPressed : 1; + UINT32 SysReqPessed : 1; + UINT32 Reserved : 16; + UINT32 InputKeyCount : 2; + } Options; + UINT32 PackedValue; +} HOT_KEY_EFI_KEY_DATA; + +typedef struct { + HOT_KEY_EFI_KEY_DATA KeyOptions; + UINT32 BootOptionCrc; + UINT16 BootOption; +//EFI_INPUT_KEY Keys[]; +} EFI_KEY_OPTION; + +#define EFI_KEY_OPTION_SHIFT 0x00000001 +#define EFI_KEY_OPTION_CONTROL 0x00000002 +#define EFI_KEY_OPTION_ALT 0x00000004 +#define EFI_KEY_OPTION_LOGO 0x00000008 +#define EFI_KEY_OPTION_MENU 0x00000010 +#define EFI_KEY_OPTION_SYSREQ 0x00000020 +#define EFI_KEY_CODE_COUNT 0x00000300 + + +// +// EFI File location to boot from on removable media devices +// +#define EFI_REMOVABLE_MEDIA_FILE_NAME_IA32 L"\\EFI\\BOOT\\BOOTIA32.EFI" +#define EFI_REMOVABLE_MEDIA_FILE_NAME_IA64 L"\\EFI\\BOOT\\BOOTIA64.EFI" +#define EFI_REMOVABLE_MEDIA_FILE_NAME_X64 L"\\EFI\\BOOT\\BOOTX64.EFI" + +#if defined (MDE_CPU_IA32) + #define EFI_REMOVABLE_MEDIA_FILE_NAME EFI_REMOVABLE_MEDIA_FILE_NAME_IA32 +#elif defined (MDE_CPU_IPF) + #define EFI_REMOVABLE_MEDIA_FILE_NAME EFI_REMOVABLE_MEDIA_FILE_NAME_IA64 +#elif defined (MDE_CPU_X64) + #define EFI_REMOVABLE_MEDIA_FILE_NAME EFI_REMOVABLE_MEDIA_FILE_NAME_X64 +#elif defined (MDE_CPU_EBC) +#else + #error Unknown Processor Type +#endif + +#include +#include +#include + +#endif diff --git a/gpxe/src/include/gpxe/efi/X64/ProcessorBind.h b/gpxe/src/include/gpxe/efi/X64/ProcessorBind.h new file mode 100644 index 0000000..fe6c413 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/X64/ProcessorBind.h @@ -0,0 +1,247 @@ +/** @file + Processor or Compiler specific defines and types x64 (Intel(r) EM64T, AMD64). + + Copyright (c) 2006, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __PROCESSOR_BIND_H__ +#define __PROCESSOR_BIND_H__ + +/// +/// Define the processor type so other code can make processor based choices +/// +#define MDE_CPU_X64 + + +// +// Make sure we are useing the correct packing rules per EFI specification +// +#ifndef __GNUC__ +#pragma pack() +#endif + + +#if __INTEL_COMPILER +// +// Disable ICC's remark #869: "Parameter" was never referenced warning. +// This is legal ANSI C code so we disable the remark that is turned on with -Wall +// +#pragma warning ( disable : 869 ) + +// +// Disable ICC's remark #1418: external function definition with no prior declaration. +// This is legal ANSI C code so we disable the remark that is turned on with /W4 +// +#pragma warning ( disable : 1418 ) + +// +// Disable ICC's remark #1419: external declaration in primary source file +// This is legal ANSI C code so we disable the remark that is turned on with /W4 +// +#pragma warning ( disable : 1419 ) + +#endif + + +#if _MSC_EXTENSIONS + +// +// Disable warning that make it impossible to compile at /W4 +// This only works for Microsoft* tools +// + +// +// Disabling bitfield type checking warnings. +// +#pragma warning ( disable : 4214 ) + +// +// Disabling the unreferenced formal parameter warnings. +// +#pragma warning ( disable : 4100 ) + +// +// Disable slightly different base types warning as CHAR8 * can not be set +// to a constant string. +// +#pragma warning ( disable : 4057 ) + +// +// ASSERT(FALSE) or while (TRUE) are legal constructes so supress this warning +// +#pragma warning ( disable : 4127 ) + +// +// This warning is caused by functions defined but not used. For precompiled header only. +// +#pragma warning ( disable : 4505 ) + +// +// This warning is caused by empty (after preprocessing) souce file. For precompiled header only. +// +#pragma warning ( disable : 4206 ) + +#endif + + +#if !defined(__GNUC__) && (__STDC_VERSION__ < 199901L) + // + // No ANSI C 2000 stdint.h integer width declarations, so define equivalents + // + + #if _MSC_EXTENSIONS + + + // + // use Microsoft C complier dependent interger width types + // + typedef unsigned __int64 UINT64; + typedef __int64 INT64; + typedef unsigned __int32 UINT32; + typedef __int32 INT32; + typedef unsigned short UINT16; + typedef unsigned short CHAR16; + typedef short INT16; + typedef unsigned char BOOLEAN; + typedef unsigned char UINT8; + typedef char CHAR8; + typedef char INT8; + #else + #ifdef _EFI_P64 + // + // P64 - is Intel Itanium(TM) speak for pointers being 64-bit and longs and ints + // are 32-bits + // + typedef unsigned long long UINT64; + typedef long long INT64; + typedef unsigned int UINT32; + typedef int INT32; + typedef unsigned short CHAR16; + typedef unsigned short UINT16; + typedef short INT16; + typedef unsigned char BOOLEAN; + typedef unsigned char UINT8; + typedef char CHAR8; + typedef char INT8; + #else + // + // Assume LP64 - longs and pointers are 64-bit. Ints are 32-bit. + // + typedef unsigned long UINT64; + typedef long INT64; + typedef unsigned int UINT32; + typedef int INT32; + typedef unsigned short UINT16; + typedef unsigned short CHAR16; + typedef short INT16; + typedef unsigned char BOOLEAN; + typedef unsigned char UINT8; + typedef char CHAR8; + typedef char INT8; + #endif + #endif + + #define UINT8_MAX 0xff + +#else + // + // Use ANSI C 2000 stdint.h integer width declarations + // + #include + typedef uint8_t BOOLEAN; + typedef int8_t INT8; + typedef uint8_t UINT8; + typedef int16_t INT16; + typedef uint16_t UINT16; + typedef int32_t INT32; + typedef uint32_t UINT32; + typedef int64_t INT64; + typedef uint64_t UINT64; + typedef char CHAR8; + typedef uint16_t CHAR16; + +#endif + +typedef UINT64 UINTN; +typedef INT64 INTN; + + +// +// Processor specific defines +// +#define MAX_BIT 0x8000000000000000ULL +#define MAX_2_BITS 0xC000000000000000ULL + +// +// Maximum legal X64 address +// +#define MAX_ADDRESS 0xFFFFFFFFFFFFFFFFULL + +// +// The stack alignment required for X64 +// +#define CPU_STACK_ALIGNMENT 16 + +// +// Modifier to ensure that all protocol member functions and EFI intrinsics +// use the correct C calling convention. All protocol member functions and +// EFI intrinsics are required to modify thier member functions with EFIAPI. +// +#if _MSC_EXTENSIONS + /// + /// Define the standard calling convention reguardless of optimization level. + /// __cdecl is Microsoft* specific C extension. + /// + #define EFIAPI __cdecl +#elif __GNUC__ + /// + /// Define the standard calling convention reguardless of optimization level. + /// The GCC support assumes a GCC compiler that supports the EFI ABI. The EFI + /// ABI is much closer to the x64 Microsoft* ABI than standard x64 (x86-64) + /// GCC ABI. Thus a standard x64 (x86-64) GCC compiler can not be used for + /// x64. Warning the assembly code in the MDE x64 does not follow the correct + /// ABI for the standard x64 (x86-64) GCC. + /// + #define EFIAPI __attribute__((ms_abi)) +#else + /// + /// The default for a non Microsoft* or GCC compiler is to assume the EFI ABI + /// is the standard. + /// + #define EFIAPI +#endif + +// +// The Microsoft* C compiler can removed references to unreferenced data items +// if the /OPT:REF linker option is used. We defined a macro as this is a +// a non standard extension +// +#if _MSC_EXTENSIONS + #define GLOBAL_REMOVE_IF_UNREFERENCED __declspec(selectany) +#else + #define GLOBAL_REMOVE_IF_UNREFERENCED +#endif + +// +// For symbol name in GNU assembly code, an extra "_" is necessary +// +#if __GNUC__ + #if defined(linux) + #define ASM_PFX(name) name + #else + #define ASM_PFX(name) _##name + #endif +#endif + +#define FUNCTION_ENTRY_POINT(p) (p) + +#endif + diff --git a/gpxe/src/include/gpxe/efi/efi.h b/gpxe/src/include/gpxe/efi/efi.h new file mode 100644 index 0000000..c7f63b6 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/efi.h @@ -0,0 +1,131 @@ +#ifndef _EFI_H +#define _EFI_H + +/** @file + * + * EFI API + * + * The intention is to include near-verbatim copies of the EFI headers + * required by gPXE. This is achieved using the import.pl script in + * this directory. Run the import script to update the local copies + * of the headers: + * + * ./import.pl /path/to/edk2/edk2 + * + * where /path/to/edk2/edk2 is the path to your local checkout of the + * EFI Development Kit. + * + * Note that import.pl will modify any #include lines in each imported + * header to reflect its new location within the gPXE tree. It will + * also tidy up the file by removing carriage return characters and + * trailing whitespace. + * + * + * At the time of writing, there are a few other modifications to + * these headers that are present in my personal edk2 tree, that are + * not yet committed back to the main edk2 repository. These + * modifications are fixes for compilation on case-dependent + * filesystems, compilation under -mrtd and -mregparm=3, etc. + */ + +/* EFI headers rudely redefine NULL */ +#undef NULL + +/* Include the top-level EFI header files */ +#include +#include + +/* Reset any trailing #pragma pack directives */ +#pragma pack() + +#include +#include + +/** An EFI protocol used by gPXE */ +struct efi_protocol { + /** GUID */ + union { + /** EFI protocol GUID */ + EFI_GUID guid; + /** UUID structure understood by gPXE */ + union uuid uuid; + } u; + /** Variable containing pointer to protocol structure */ + void **protocol; +}; + +/** Declare an EFI protocol used by gPXE */ +#define __efi_protocol \ + __table ( struct efi_protocol, efi_protocols, 01 ) + +/** Declare an EFI protocol to be required by gPXE + * + * @v _protocol EFI protocol name + * @v _ptr Pointer to protocol instance + */ +#define EFI_REQUIRE_PROTOCOL( _protocol, _ptr ) \ + struct efi_protocol __ ## _protocol __efi_protocol = { \ + .u.guid = _protocol ## _GUID, \ + .protocol = ( ( void ** ) ( void * ) \ + ( ( (_ptr) == ( ( _protocol ** ) NULL ) ) ? \ + (_ptr) : (_ptr) ) ), \ + } + +/** An EFI configuration table used by gPXE */ +struct efi_config_table { + /** GUID */ + union { + /** EFI configuration table GUID */ + EFI_GUID guid; + /** UUID structure understood by gPXE */ + union uuid uuid; + } u; + /** Variable containing pointer to configuration table */ + void **table; + /** Table is required for operation */ + int required; +}; + +/** Declare an EFI configuration table used by gPXE */ +#define __efi_config_table \ + __table ( struct efi_config_table, efi_config_tables, 01 ) + +/** Declare an EFI configuration table to be used by gPXE + * + * @v _table EFI configuration table name + * @v _ptr Pointer to configuration table + * @v _required Table is required for operation + */ +#define EFI_USE_TABLE( _table, _ptr, _required ) \ + struct efi_config_table __ ## _table __efi_config_table = { \ + .u.guid = _table ## _GUID, \ + .table = ( ( void ** ) ( void * ) (_ptr) ), \ + .required = (_required), \ + } + +/** Convert a gPXE status code to an EFI status code + * + * FIXME: actually perform some kind of conversion. gPXE error codes + * will be detected as EFI error codes; both have the top bit set, and + * the success return code is zero for both. Anything that just + * reports a numerical error will be OK, anything attempting to + * interpret the value or to display a text equivalent will be + * screwed. + */ +#define RC_TO_EFIRC( rc ) (rc) + +/** Convert an EFI status code to a gPXE status code + * + * FIXME: as above + */ +#define EFIRC_TO_RC( efirc ) (efirc) + +extern EFI_HANDLE efi_image_handle; +extern EFI_SYSTEM_TABLE *efi_systab; + +extern const char * efi_strerror ( EFI_STATUS efirc ); +extern EFI_STATUS efi_init ( EFI_HANDLE image_handle, + EFI_SYSTEM_TABLE *systab ); +extern int efi_snp_install ( void ); + +#endif /* _EFI_H */ diff --git a/gpxe/src/include/gpxe/efi/efi_io.h b/gpxe/src/include/gpxe/efi/efi_io.h new file mode 100644 index 0000000..93f559d --- /dev/null +++ b/gpxe/src/include/gpxe/efi/efi_io.h @@ -0,0 +1,178 @@ +#ifndef _GPXE_EFI_IO_H +#define _GPXE_EFI_IO_H + +/** @file + * + * gPXE I/O API for EFI + * + * EFI runs with flat physical addressing, so the various mappings + * between virtual addresses, I/O addresses and bus addresses are all + * no-ops. I/O is handled using the EFI_CPU_IO_PROTOCOL. + */ + +#ifdef IOAPI_EFI +#define IOAPI_PREFIX_efi +#else +#define IOAPI_PREFIX_efi __efi_ +#endif + +extern unsigned long long efi_ioread ( volatile void *io_addr, + size_t size ); +extern void efi_iowrite ( unsigned long long data, volatile void *io_addr, + size_t size ); +extern void efi_ioreads ( volatile void *io_addr, void *data, + size_t size, unsigned int count ); +extern void efi_iowrites ( volatile void *io_addr, const void *data, + size_t size, unsigned int count ); + +/* + * Physical<->Bus and Bus<->I/O address mappings + * + * EFI runs with flat physical addressing, so these are all no-ops. + * + */ + +static inline __always_inline unsigned long +IOAPI_INLINE ( efi, phys_to_bus ) ( unsigned long phys_addr ) { + return phys_addr; +} + +static inline __always_inline unsigned long +IOAPI_INLINE ( efi, bus_to_phys ) ( unsigned long bus_addr ) { + return bus_addr; +} + +static inline __always_inline void * +IOAPI_INLINE ( efi, ioremap ) ( unsigned long bus_addr, size_t len __unused ) { + return ( ( void * ) bus_addr ); +} + +static inline __always_inline void +IOAPI_INLINE ( efi, iounmap ) ( volatile const void *io_addr __unused ) { + /* Nothing to do */ +} + +static inline __always_inline unsigned long +IOAPI_INLINE ( efi, io_to_bus ) ( volatile const void *io_addr ) { + return ( ( unsigned long ) io_addr ); +} + +/* + * I/O functions + * + */ + +static inline __always_inline uint8_t +IOAPI_INLINE ( efi, readb ) ( volatile uint8_t *io_addr ) { + return efi_ioread ( io_addr, sizeof ( *io_addr ) ); +} + +static inline __always_inline uint16_t +IOAPI_INLINE ( efi, readw ) ( volatile uint16_t *io_addr ) { + return efi_ioread ( io_addr, sizeof ( *io_addr ) ); +} + +static inline __always_inline uint32_t +IOAPI_INLINE ( efi, readl ) ( volatile uint32_t *io_addr ) { + return efi_ioread ( io_addr, sizeof ( *io_addr ) ); +} + +static inline __always_inline uint64_t +IOAPI_INLINE ( efi, readq ) ( volatile uint64_t *io_addr ) { + return efi_ioread ( io_addr, sizeof ( *io_addr ) ); +} + +static inline __always_inline void +IOAPI_INLINE ( efi, writeb ) ( uint8_t data, volatile uint8_t *io_addr ) { + efi_iowrite ( data, io_addr, sizeof ( *io_addr ) ); +} + +static inline __always_inline void +IOAPI_INLINE ( efi, writew ) ( uint16_t data, volatile uint16_t *io_addr ) { + efi_iowrite ( data, io_addr, sizeof ( *io_addr ) ); +} + +static inline __always_inline void +IOAPI_INLINE ( efi, writel ) ( uint32_t data, volatile uint32_t *io_addr ) { + efi_iowrite ( data, io_addr, sizeof ( *io_addr ) ); +} + +static inline __always_inline void +IOAPI_INLINE ( efi, writeq ) ( uint64_t data, volatile uint64_t *io_addr ) { + efi_iowrite ( data, io_addr, sizeof ( *io_addr ) ); +} + +static inline __always_inline uint8_t +IOAPI_INLINE ( efi, inb ) ( volatile uint8_t *io_addr ) { + return efi_ioread ( io_addr, sizeof ( *io_addr ) ); +} + +static inline __always_inline uint16_t +IOAPI_INLINE ( efi, inw ) ( volatile uint16_t *io_addr ) { + return efi_ioread ( io_addr, sizeof ( *io_addr ) ); +} + +static inline __always_inline uint32_t +IOAPI_INLINE ( efi, inl ) ( volatile uint32_t *io_addr ) { + return efi_ioread ( io_addr, sizeof ( *io_addr ) ); +} + +static inline __always_inline void +IOAPI_INLINE ( efi, outb ) ( uint8_t data, volatile uint8_t *io_addr ) { + efi_iowrite ( data, io_addr, sizeof ( *io_addr ) ); +} + +static inline __always_inline void +IOAPI_INLINE ( efi, outw ) ( uint16_t data, volatile uint16_t *io_addr ) { + efi_iowrite ( data, io_addr, sizeof ( *io_addr ) ); +} + +static inline __always_inline void +IOAPI_INLINE ( efi, outl ) ( uint32_t data, volatile uint32_t *io_addr ) { + efi_iowrite ( data, io_addr, sizeof ( *io_addr ) ); +} + +static inline __always_inline void +IOAPI_INLINE ( efi, insb ) ( volatile uint8_t *io_addr, uint8_t *data, + unsigned int count ) { + efi_ioreads ( io_addr, data, sizeof ( *io_addr ), count ); +} + +static inline __always_inline void +IOAPI_INLINE ( efi, insw ) ( volatile uint16_t *io_addr, uint16_t *data, + unsigned int count ) { + efi_ioreads ( io_addr, data, sizeof ( *io_addr ), count ); +} + +static inline __always_inline void +IOAPI_INLINE ( efi, insl ) ( volatile uint32_t *io_addr, uint32_t *data, + unsigned int count ) { + efi_ioreads ( io_addr, data, sizeof ( *io_addr ), count ); +} + +static inline __always_inline void +IOAPI_INLINE ( efi, outsb ) ( volatile uint8_t *io_addr, const uint8_t *data, + unsigned int count ) { + efi_iowrites ( io_addr, data, sizeof ( *io_addr ), count ); +} + +static inline __always_inline void +IOAPI_INLINE ( efi, outsw ) ( volatile uint16_t *io_addr, const uint16_t *data, + unsigned int count ) { + efi_iowrites ( io_addr, data, sizeof ( *io_addr ), count ); +} + +static inline __always_inline void +IOAPI_INLINE ( efi, outsl ) ( volatile uint32_t *io_addr, const uint32_t *data, + unsigned int count ) { + efi_iowrites ( io_addr, data, sizeof ( *io_addr ), count ); +} + +static inline __always_inline void +IOAPI_INLINE ( efi, mb ) ( void ) { + /* Do nothing; EFI readl()/writel() calls already act as + * memory barriers. + */ +} + +#endif /* _GPXE_EFI_IO_H */ diff --git a/gpxe/src/include/gpxe/efi/efi_pci.h b/gpxe/src/include/gpxe/efi/efi_pci.h new file mode 100644 index 0000000..8be331a --- /dev/null +++ b/gpxe/src/include/gpxe/efi/efi_pci.h @@ -0,0 +1,146 @@ +#ifndef _GPXE_EFI_PCI_H +#define _GPXE_EFI_PCI_H + +/** @file + * + * gPXE PCI I/O API for EFI + * + */ + +#ifdef PCIAPI_EFI +#define PCIAPI_PREFIX_efi +#else +#define PCIAPI_PREFIX_efi __efi_ +#endif + +/* EFI PCI width codes defined by EFI spec */ +#define EFIPCI_WIDTH_BYTE 0 +#define EFIPCI_WIDTH_WORD 1 +#define EFIPCI_WIDTH_DWORD 2 + +#define EFIPCI_LOCATION( _offset, _width ) \ + ( (_offset) | ( (_width) << 16 ) ) +#define EFIPCI_OFFSET( _location ) ( (_location) & 0xffff ) +#define EFIPCI_WIDTH( _location ) ( (_location) >> 16 ) + +struct pci_device; + +extern int efipci_read ( struct pci_device *pci, unsigned long location, + void *value ); +extern int efipci_write ( struct pci_device *pci, unsigned long location, + unsigned long value ); + +/** + * Determine maximum PCI bus number within system + * + * @ret max_bus Maximum bus number + */ +static inline __always_inline int +PCIAPI_INLINE ( efi, pci_max_bus ) ( void ) { + /* No way to work this out via EFI */ + return 0xff; +} + +/** + * Read byte from PCI configuration space via EFI + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( efi, pci_read_config_byte ) ( struct pci_device *pci, + unsigned int where, + uint8_t *value ) { + return efipci_read ( pci, + EFIPCI_LOCATION ( where, EFIPCI_WIDTH_BYTE ), + value ); +} + +/** + * Read word from PCI configuration space via EFI + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( efi, pci_read_config_word ) ( struct pci_device *pci, + unsigned int where, + uint16_t *value ) { + return efipci_read ( pci, + EFIPCI_LOCATION ( where, EFIPCI_WIDTH_WORD ), + value ); +} + +/** + * Read dword from PCI configuration space via EFI + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( efi, pci_read_config_dword ) ( struct pci_device *pci, + unsigned int where, + uint32_t *value ) { + return efipci_read ( pci, + EFIPCI_LOCATION ( where, EFIPCI_WIDTH_DWORD ), + value ); +} + +/** + * Write byte to PCI configuration space via EFI + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( efi, pci_write_config_byte ) ( struct pci_device *pci, + unsigned int where, + uint8_t value ) { + return efipci_write ( pci, + EFIPCI_LOCATION ( where, EFIPCI_WIDTH_BYTE ), + value ); +} + +/** + * Write word to PCI configuration space via EFI + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( efi, pci_write_config_word ) ( struct pci_device *pci, + unsigned int where, + uint16_t value ) { + return efipci_write ( pci, + EFIPCI_LOCATION ( where, EFIPCI_WIDTH_WORD ), + value ); +} + +/** + * Write dword to PCI configuration space via EFI + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +static inline __always_inline int +PCIAPI_INLINE ( efi, pci_write_config_dword ) ( struct pci_device *pci, + unsigned int where, + uint32_t value ) { + return efipci_write ( pci, + EFIPCI_LOCATION ( where, EFIPCI_WIDTH_DWORD ), + value ); +} + +#endif /* _GPXE_EFI_PCI_H */ diff --git a/gpxe/src/include/gpxe/efi/efi_smbios.h b/gpxe/src/include/gpxe/efi/efi_smbios.h new file mode 100644 index 0000000..df947de --- /dev/null +++ b/gpxe/src/include/gpxe/efi/efi_smbios.h @@ -0,0 +1,16 @@ +#ifndef _GPXE_EFI_SMBIOS_H +#define _GPXE_EFI_SMBIOS_H + +/** @file + * + * gPXE SMBIOS API for EFI + * + */ + +#ifdef SMBIOS_EFI +#define SMBIOS_PREFIX_efi +#else +#define SMBIOS_PREFIX_efi __efi_ +#endif + +#endif /* _GPXE_EFI_SMBIOS_H */ diff --git a/gpxe/src/include/gpxe/efi/efi_timer.h b/gpxe/src/include/gpxe/efi/efi_timer.h new file mode 100644 index 0000000..c332c9d --- /dev/null +++ b/gpxe/src/include/gpxe/efi/efi_timer.h @@ -0,0 +1,16 @@ +#ifndef _GPXE_EFI_TIMER_H +#define _GPXE_EFI_TIMER_H + +/** @file + * + * gPXE timer API for EFI + * + */ + +#ifdef TIMER_EFI +#define TIMER_PREFIX_efi +#else +#define TIMER_PREFIX_efi __efi_ +#endif + +#endif /* _GPXE_EFI_TIMER_H */ diff --git a/gpxe/src/include/gpxe/efi/efi_uaccess.h b/gpxe/src/include/gpxe/efi/efi_uaccess.h new file mode 100644 index 0000000..bae5fb4 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/efi_uaccess.h @@ -0,0 +1,88 @@ +#ifndef _GPXE_EFI_UACCESS_H +#define _GPXE_EFI_UACCESS_H + +/** @file + * + * gPXE user access API for EFI + * + * EFI runs with flat physical addressing, so the various mappings + * between virtual addresses, I/O addresses and bus addresses are all + * no-ops. + */ + +#ifdef UACCESS_EFI +#define UACCESS_PREFIX_efi +#else +#define UACCESS_PREFIX_efi __efi_ +#endif + +/** + * Convert physical address to user pointer + * + * @v phys_addr Physical address + * @ret userptr User pointer + */ +static inline __always_inline userptr_t +UACCESS_INLINE ( efi, phys_to_user ) ( unsigned long phys_addr ) { + return phys_addr; +} + +/** + * Convert user buffer to physical address + * + * @v userptr User pointer + * @v offset Offset from user pointer + * @ret phys_addr Physical address + */ +static inline __always_inline unsigned long +UACCESS_INLINE ( efi, user_to_phys ) ( userptr_t userptr, off_t offset ) { + return ( userptr + offset ); +} + +static inline __always_inline userptr_t +UACCESS_INLINE ( efi, virt_to_user ) ( volatile const void *addr ) { + return trivial_virt_to_user ( addr ); +} + +static inline __always_inline void * +UACCESS_INLINE ( efi, user_to_virt ) ( userptr_t userptr, off_t offset ) { + return trivial_user_to_virt ( userptr, offset ); +} + +static inline __always_inline userptr_t +UACCESS_INLINE ( efi, userptr_add ) ( userptr_t userptr, off_t offset ) { + return trivial_userptr_add ( userptr, offset ); +} + +static inline __always_inline void +UACCESS_INLINE ( efi, memcpy_user ) ( userptr_t dest, off_t dest_off, + userptr_t src, off_t src_off, + size_t len ) { + trivial_memcpy_user ( dest, dest_off, src, src_off, len ); +} + +static inline __always_inline void +UACCESS_INLINE ( efi, memmove_user ) ( userptr_t dest, off_t dest_off, + userptr_t src, off_t src_off, + size_t len ) { + trivial_memmove_user ( dest, dest_off, src, src_off, len ); +} + +static inline __always_inline void +UACCESS_INLINE ( efi, memset_user ) ( userptr_t buffer, off_t offset, + int c, size_t len ) { + trivial_memset_user ( buffer, offset, c, len ); +} + +static inline __always_inline size_t +UACCESS_INLINE ( efi, strlen_user ) ( userptr_t buffer, off_t offset ) { + return trivial_strlen_user ( buffer, offset ); +} + +static inline __always_inline off_t +UACCESS_INLINE ( efi, memchr_user ) ( userptr_t buffer, off_t offset, + int c, size_t len ) { + return trivial_memchr_user ( buffer, offset, c, len ); +} + +#endif /* _GPXE_EFI_UACCESS_H */ diff --git a/gpxe/src/include/gpxe/efi/efi_umalloc.h b/gpxe/src/include/gpxe/efi/efi_umalloc.h new file mode 100644 index 0000000..def17b2 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/efi_umalloc.h @@ -0,0 +1,16 @@ +#ifndef _GPXE_EFI_UMALLOC_H +#define _GPXE_EFI_UMALLOC_H + +/** @file + * + * gPXE user memory allocation API for EFI + * + */ + +#ifdef UMALLOC_EFI +#define UMALLOC_PREFIX_efi +#else +#define UMALLOC_PREFIX_efi __efi_ +#endif + +#endif /* _GPXE_EFI_UMALLOC_H */ diff --git a/gpxe/src/include/gpxe/efi/import.pl b/gpxe/src/include/gpxe/efi/import.pl new file mode 100755 index 0000000..1699394 --- /dev/null +++ b/gpxe/src/include/gpxe/efi/import.pl @@ -0,0 +1,75 @@ +#!/usr/bin/perl -w + +use File::Spec::Functions qw ( :ALL ); +use File::Find; +use File::Path; +use FindBin; +use strict; +use warnings; + +sub try_import_file { + my $gpxedir = shift; + my $edkdirs = shift; + my $filename = shift; + + # Skip everything except headers + return unless $filename =~ /\.h$/; + print "$filename..."; + + my $outfile = catfile ( $gpxedir, $filename ); + foreach my $edkdir ( @$edkdirs ) { + my $infile = catfile ( $edkdir, $filename ); + if ( -e $infile ) { + # We have found a matching source file - import it + print "$infile\n"; + open my $infh, "<$infile" or die "Could not open $infile: $!\n"; + ( undef, my $outdir, undef ) = splitpath ( $outfile ); + mkpath ( $outdir ); + open my $outfh, ">$outfile" or die "Could not open $outfile: $!\n"; + my @dependencies = (); + while ( <$infh> ) { + # Strip CR and trailing whitespace + s/\r//g; + s/\s*$//g; + chomp; + # Update include lines, and record included files + if ( s/^\#include\s+[<\"](\S+)[>\"]/\#include / ) { + push @dependencies, $1; + } + print $outfh "$_\n"; + } + close $outfh; + close $infh; + # Recurse to handle any included files that we don't already have + foreach my $dependency ( @dependencies ) { + if ( ! -e catfile ( $gpxedir, $dependency ) ) { + print "...following dependency on $dependency\n"; + try_import_file ( $gpxedir, $edkdirs, $dependency ); + } + } + return; + } + } + print "no equivalent found\n"; +} + +# Identify edk import directories +die "Syntax $0 /path/to/edk2/edk2\n" unless @ARGV == 1; +my $edktop = shift; +die "Directory \"$edktop\" does not appear to contain the EFI EDK2\n" + unless -e catfile ( $edktop, "MdePkg" ); +my $edkdirs = [ catfile ( $edktop, "MdePkg/Include" ), + catfile ( $edktop, "IntelFrameworkPkg/Include" ) ]; + +# Identify gPXE EFI includes directory +my $gpxedir = $FindBin::Bin; +die "Directory \"$gpxedir\" does not appear to contain the gPXE EFI includes\n" + unless -e catfile ( $gpxedir, "../../../include/gpxe/efi" ); + +print "Importing EFI headers into $gpxedir\nfrom "; +print join ( "\n and ", @$edkdirs )."\n"; + +# Import headers +find ( { wanted => sub { + try_import_file ( $gpxedir, $edkdirs, abs2rel ( $_, $gpxedir ) ); +}, no_chdir => 1 }, $gpxedir ); diff --git a/gpxe/src/include/gpxe/eisa.h b/gpxe/src/include/gpxe/eisa.h new file mode 100644 index 0000000..e9d890e --- /dev/null +++ b/gpxe/src/include/gpxe/eisa.h @@ -0,0 +1,125 @@ +#ifndef EISA_H +#define EISA_H + +#include +#include +#include +#include + +/* + * EISA constants + * + */ + +#define EISA_MIN_SLOT (0x1) +#define EISA_MAX_SLOT (0xf) /* Must be 2^n - 1 */ +#define EISA_SLOT_BASE( n ) ( 0x1000 * (n) ) + +#define EISA_VENDOR_ID ( 0xc80 ) +#define EISA_PROD_ID ( 0xc82 ) +#define EISA_GLOBAL_CONFIG ( 0xc84 ) + +#define EISA_CMD_RESET ( 1 << 2 ) +#define EISA_CMD_ENABLE ( 1 << 0 ) + +/** An EISA device ID list entry */ +struct eisa_device_id { + /** Name */ + const char *name; + /** Manufacturer ID */ + uint16_t vendor_id; + /** Product ID */ + uint16_t prod_id; +}; + +/** An EISA device */ +struct eisa_device { + /** Generic device */ + struct device dev; + /** Slot number */ + unsigned int slot; + /** I/O address */ + uint16_t ioaddr; + /** Manufacturer ID */ + uint16_t vendor_id; + /** Product ID */ + uint16_t prod_id; + /** Driver for this device */ + struct eisa_driver *driver; + /** Driver-private data + * + * Use eisa_set_drvdata() and eisa_get_drvdata() to access + * this field. + */ + void *priv; + /** Driver name */ + const char *driver_name; +}; + +/** An EISA driver */ +struct eisa_driver { + /** EISA ID table */ + struct eisa_device_id *ids; + /** Number of entries in EISA ID table */ + unsigned int id_count; + /** + * Probe device + * + * @v eisa EISA device + * @v id Matching entry in ID table + * @ret rc Return status code + */ + int ( * probe ) ( struct eisa_device *eisa, + const struct eisa_device_id *id ); + /** + * Remove device + * + * @v eisa EISA device + */ + void ( * remove ) ( struct eisa_device *eisa ); +}; + +/** Declare an EISA driver */ +#define __eisa_driver __table ( struct eisa_driver, eisa_drivers, 01 ) + +extern void eisa_device_enabled ( struct eisa_device *eisa, int enabled ); + +/** + * Enable EISA device + * + * @v eisa EISA device + */ +static inline void enable_eisa_device ( struct eisa_device *eisa ) { + eisa_device_enabled ( eisa, 1 ); +} + +/** + * Disable EISA device + * + * @v eisa EISA device + */ +static inline void disable_eisa_device ( struct eisa_device *eisa ) { + eisa_device_enabled ( eisa, 0 ); +} + +/** + * Set EISA driver-private data + * + * @v eisa EISA device + * @v priv Private data + */ +static inline void eisa_set_drvdata ( struct eisa_device *eisa, void *priv ) { + eisa->priv = priv; +} + +/** + * Get EISA driver-private data + * + * @v eisa EISA device + * @ret priv Private data + */ +static inline void * eisa_get_drvdata ( struct eisa_device *eisa ) { + return eisa->priv; +} + +#endif /* EISA_H */ diff --git a/gpxe/src/include/gpxe/elf.h b/gpxe/src/include/gpxe/elf.h new file mode 100644 index 0000000..db28a60 --- /dev/null +++ b/gpxe/src/include/gpxe/elf.h @@ -0,0 +1,15 @@ +#ifndef _GPXE_ELF_H +#define _GPXE_ELF_H + +/** + * @file + * + * ELF image format + * + */ + +#include + +extern int elf_load ( struct image *image ); + +#endif /* _GPXE_ELF_H */ diff --git a/gpxe/src/include/gpxe/errfile.h b/gpxe/src/include/gpxe/errfile.h new file mode 100644 index 0000000..dcfd4e4 --- /dev/null +++ b/gpxe/src/include/gpxe/errfile.h @@ -0,0 +1,178 @@ +#ifndef _GPXE_ERRFILE_H +#define _GPXE_ERRFILE_H + +/** @file + * + * Error file identifiers + * + */ + +#include + +/** + * @defgroup errfilecat Error file identifier categories + * + * @{ + */ + +#define ERRFILE_CORE 0x00002000 /**< Core code */ +#define ERRFILE_DRIVER 0x00004000 /**< Driver code */ +#define ERRFILE_NET 0x00006000 /**< Networking code */ +#define ERRFILE_IMAGE 0x00008000 /**< Image code */ +#define ERRFILE_OTHER 0x0000e000 /**< Any other code */ + +/** @} */ + +/** Flag for architecture-dependent error files */ +#define ERRFILE_ARCH 0x00800000 + +/** + * @defgroup errfile Error file identifiers + * + * These values are automatically incorporated into the definitions + * for error numbers such as EINVAL. + * + * @{ + */ + +#define ERRFILE_asprintf ( ERRFILE_CORE | 0x00000000 ) +#define ERRFILE_downloader ( ERRFILE_CORE | 0x00010000 ) +#define ERRFILE_exec ( ERRFILE_CORE | 0x00020000 ) +#define ERRFILE_hw ( ERRFILE_CORE | 0x00030000 ) +#define ERRFILE_iobuf ( ERRFILE_CORE | 0x00040000 ) +#define ERRFILE_job ( ERRFILE_CORE | 0x00050000 ) +#define ERRFILE_linebuf ( ERRFILE_CORE | 0x00060000 ) +#define ERRFILE_monojob ( ERRFILE_CORE | 0x00070000 ) +#define ERRFILE_nvo ( ERRFILE_CORE | 0x00080000 ) +#define ERRFILE_open ( ERRFILE_CORE | 0x00090000 ) +#define ERRFILE_posix_io ( ERRFILE_CORE | 0x000a0000 ) +#define ERRFILE_resolv ( ERRFILE_CORE | 0x000b0000 ) +#define ERRFILE_settings ( ERRFILE_CORE | 0x000c0000 ) +#define ERRFILE_vsprintf ( ERRFILE_CORE | 0x000d0000 ) +#define ERRFILE_xfer ( ERRFILE_CORE | 0x000e0000 ) +#define ERRFILE_bitmap ( ERRFILE_CORE | 0x000f0000 ) + +#define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 ) +#define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 ) +#define ERRFILE_isapnp ( ERRFILE_DRIVER | 0x00020000 ) +#define ERRFILE_mca ( ERRFILE_DRIVER | 0x00030000 ) +#define ERRFILE_pci ( ERRFILE_DRIVER | 0x00040000 ) + +#define ERRFILE_nvs ( ERRFILE_DRIVER | 0x00100000 ) +#define ERRFILE_spi ( ERRFILE_DRIVER | 0x00110000 ) +#define ERRFILE_i2c_bit ( ERRFILE_DRIVER | 0x00120000 ) +#define ERRFILE_spi_bit ( ERRFILE_DRIVER | 0x00130000 ) + +#define ERRFILE_3c509 ( ERRFILE_DRIVER | 0x00200000 ) +#define ERRFILE_bnx2 ( ERRFILE_DRIVER | 0x00210000 ) +#define ERRFILE_cs89x0 ( ERRFILE_DRIVER | 0x00220000 ) +#define ERRFILE_eepro ( ERRFILE_DRIVER | 0x00230000 ) +#define ERRFILE_etherfabric ( ERRFILE_DRIVER | 0x00240000 ) +#define ERRFILE_legacy ( ERRFILE_DRIVER | 0x00250000 ) +#define ERRFILE_natsemi ( ERRFILE_DRIVER | 0x00260000 ) +#define ERRFILE_pnic ( ERRFILE_DRIVER | 0x00270000 ) +#define ERRFILE_prism2_pci ( ERRFILE_DRIVER | 0x00280000 ) +#define ERRFILE_prism2_plx ( ERRFILE_DRIVER | 0x00290000 ) +#define ERRFILE_rtl8139 ( ERRFILE_DRIVER | 0x002a0000 ) +#define ERRFILE_smc9000 ( ERRFILE_DRIVER | 0x002b0000 ) +#define ERRFILE_tg3 ( ERRFILE_DRIVER | 0x002c0000 ) +#define ERRFILE_3c509_eisa ( ERRFILE_DRIVER | 0x002d0000 ) +#define ERRFILE_3c515 ( ERRFILE_DRIVER | 0x002e0000 ) +#define ERRFILE_3c529 ( ERRFILE_DRIVER | 0x002f0000 ) +#define ERRFILE_3c595 ( ERRFILE_DRIVER | 0x00300000 ) +#define ERRFILE_3c5x9 ( ERRFILE_DRIVER | 0x00310000 ) +#define ERRFILE_3c90x ( ERRFILE_DRIVER | 0x00320000 ) +#define ERRFILE_amd8111e ( ERRFILE_DRIVER | 0x00330000 ) +#define ERRFILE_davicom ( ERRFILE_DRIVER | 0x00340000 ) +#define ERRFILE_depca ( ERRFILE_DRIVER | 0x00350000 ) +#define ERRFILE_dmfe ( ERRFILE_DRIVER | 0x00360000 ) +#define ERRFILE_eepro100 ( ERRFILE_DRIVER | 0x00380000 ) +#define ERRFILE_epic100 ( ERRFILE_DRIVER | 0x00390000 ) +#define ERRFILE_forcedeth ( ERRFILE_DRIVER | 0x003a0000 ) +#define ERRFILE_mtd80x ( ERRFILE_DRIVER | 0x003b0000 ) +#define ERRFILE_ns83820 ( ERRFILE_DRIVER | 0x003c0000 ) +#define ERRFILE_ns8390 ( ERRFILE_DRIVER | 0x003d0000 ) +#define ERRFILE_pcnet32 ( ERRFILE_DRIVER | 0x003e0000 ) +#define ERRFILE_r8169 ( ERRFILE_DRIVER | 0x003f0000 ) +#define ERRFILE_sis900 ( ERRFILE_DRIVER | 0x00400000 ) +#define ERRFILE_sundance ( ERRFILE_DRIVER | 0x00410000 ) +#define ERRFILE_tlan ( ERRFILE_DRIVER | 0x00420000 ) +#define ERRFILE_tulip ( ERRFILE_DRIVER | 0x00430000 ) +#define ERRFILE_via_rhine ( ERRFILE_DRIVER | 0x00440000 ) +#define ERRFILE_via_velocity ( ERRFILE_DRIVER | 0x00450000 ) +#define ERRFILE_w89c840 ( ERRFILE_DRIVER | 0x00460000 ) +#define ERRFILE_ipoib ( ERRFILE_DRIVER | 0x00470000 ) +#define ERRFILE_e1000 ( ERRFILE_DRIVER | 0x00480000 ) +#define ERRFILE_e1000_hw ( ERRFILE_DRIVER | 0x00490000 ) +#define ERRFILE_mtnic ( ERRFILE_DRIVER | 0x004a0000 ) +#define ERRFILE_phantom ( ERRFILE_DRIVER | 0x004b0000 ) +#define ERRFILE_ne2k_isa ( ERRFILE_DRIVER | 0x004c0000 ) +#define ERRFILE_b44 ( ERRFILE_DRIVER | 0x004d0000 ) + +#define ERRFILE_scsi ( ERRFILE_DRIVER | 0x00700000 ) +#define ERRFILE_arbel ( ERRFILE_DRIVER | 0x00710000 ) +#define ERRFILE_hermon ( ERRFILE_DRIVER | 0x00720000 ) +#define ERRFILE_linda ( ERRFILE_DRIVER | 0x00730000 ) + +#define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) +#define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) +#define ERRFILE_dhcpopts ( ERRFILE_NET | 0x00020000 ) +#define ERRFILE_ethernet ( ERRFILE_NET | 0x00030000 ) +#define ERRFILE_icmpv6 ( ERRFILE_NET | 0x00040000 ) +#define ERRFILE_ipv4 ( ERRFILE_NET | 0x00050000 ) +#define ERRFILE_ipv6 ( ERRFILE_NET | 0x00060000 ) +#define ERRFILE_ndp ( ERRFILE_NET | 0x00070000 ) +#define ERRFILE_netdevice ( ERRFILE_NET | 0x00080000 ) +#define ERRFILE_nullnet ( ERRFILE_NET | 0x00090000 ) +#define ERRFILE_tcp ( ERRFILE_NET | 0x000a0000 ) +#define ERRFILE_ftp ( ERRFILE_NET | 0x000b0000 ) +#define ERRFILE_http ( ERRFILE_NET | 0x000c0000 ) +#define ERRFILE_iscsi ( ERRFILE_NET | 0x000d0000 ) +#define ERRFILE_tcpip ( ERRFILE_NET | 0x000e0000 ) +#define ERRFILE_udp ( ERRFILE_NET | 0x000f0000 ) +#define ERRFILE_dhcp ( ERRFILE_NET | 0x00100000 ) +#define ERRFILE_dns ( ERRFILE_NET | 0x00110000 ) +#define ERRFILE_tftp ( ERRFILE_NET | 0x00120000 ) +#define ERRFILE_infiniband ( ERRFILE_NET | 0x00130000 ) +#define ERRFILE_netdev_settings ( ERRFILE_NET | 0x00140000 ) +#define ERRFILE_dhcppkt ( ERRFILE_NET | 0x00150000 ) +#define ERRFILE_slam ( ERRFILE_NET | 0x00160000 ) +#define ERRFILE_ib_sma ( ERRFILE_NET | 0x00170000 ) +#define ERRFILE_ib_packet ( ERRFILE_NET | 0x00180000 ) +#define ERRFILE_icmp ( ERRFILE_NET | 0x00190000 ) + +#define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 ) +#define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 ) +#define ERRFILE_script ( ERRFILE_IMAGE | 0x00020000 ) +#define ERRFILE_segment ( ERRFILE_IMAGE | 0x00030000 ) +#define ERRFILE_efi_image ( ERRFILE_IMAGE | 0x00040000 ) +#define ERRFILE_embedded ( ERRFILE_IMAGE | 0x00050000 ) + +#define ERRFILE_asn1 ( ERRFILE_OTHER | 0x00000000 ) +#define ERRFILE_chap ( ERRFILE_OTHER | 0x00010000 ) +#define ERRFILE_aoeboot ( ERRFILE_OTHER | 0x00020000 ) +#define ERRFILE_autoboot ( ERRFILE_OTHER | 0x00030000 ) +#define ERRFILE_dhcpmgmt ( ERRFILE_OTHER | 0x00040000 ) +#define ERRFILE_imgmgmt ( ERRFILE_OTHER | 0x00050000 ) +#define ERRFILE_pxe_tftp ( ERRFILE_OTHER | 0x00060000 ) +#define ERRFILE_pxe_udp ( ERRFILE_OTHER | 0x00070000 ) +#define ERRFILE_axtls_aes ( ERRFILE_OTHER | 0x00080000 ) +#define ERRFILE_cipher ( ERRFILE_OTHER | 0x00090000 ) +#define ERRFILE_image_cmd ( ERRFILE_OTHER | 0x000a0000 ) +#define ERRFILE_uri_test ( ERRFILE_OTHER | 0x000b0000 ) +#define ERRFILE_ibft ( ERRFILE_OTHER | 0x000c0000 ) +#define ERRFILE_tls ( ERRFILE_OTHER | 0x000d0000 ) +#define ERRFILE_ifmgmt ( ERRFILE_OTHER | 0x000e0000 ) +#define ERRFILE_iscsiboot ( ERRFILE_OTHER | 0x000f0000 ) +#define ERRFILE_efi_pci ( ERRFILE_OTHER | 0x00100000 ) +#define ERRFILE_efi_snp ( ERRFILE_OTHER | 0x00110000 ) +#define ERRFILE_smbios ( ERRFILE_OTHER | 0x00120000 ) +#define ERRFILE_smbios_settings ( ERRFILE_OTHER | 0x00130000 ) +#define ERRFILE_efi_smbios ( ERRFILE_OTHER | 0x00140000 ) +#define ERRFILE_pxemenu ( ERRFILE_OTHER | 0x00150000 ) +#define ERRFILE_x509 ( ERRFILE_OTHER | 0x00160000 ) +#define ERRFILE_login_ui ( ERRFILE_OTHER | 0x00170000 ) + +/** @} */ + +#endif /* _GPXE_ERRFILE_H */ diff --git a/gpxe/src/include/gpxe/errortab.h b/gpxe/src/include/gpxe/errortab.h new file mode 100644 index 0000000..e9a5676 --- /dev/null +++ b/gpxe/src/include/gpxe/errortab.h @@ -0,0 +1,19 @@ +#ifndef _GPXE_ERRORTAB_H +#define _GPXE_ERRORTAB_H + +/** @file + * + * Error message tables + * + */ + +#include + +struct errortab { + int errno; + const char *text; +}; + +#define __errortab __table ( struct errortab, errortab, 01 ) + +#endif /* _GPXE_ERRORTAB_H */ diff --git a/gpxe/src/include/gpxe/ethernet.h b/gpxe/src/include/gpxe/ethernet.h new file mode 100644 index 0000000..ff0fd6c --- /dev/null +++ b/gpxe/src/include/gpxe/ethernet.h @@ -0,0 +1,35 @@ +#ifndef _GPXE_ETHERNET_H +#define _GPXE_ETHERNET_H + +/** @file + * + * Ethernet protocol + * + */ + +#include +#include +#include + +extern struct ll_protocol ethernet_protocol; + +extern const char * eth_ntoa ( const void *ll_addr ); + +/** + * Allocate Ethernet device + * + * @v priv_size Size of driver private data + * @ret netdev Network device, or NULL + */ +static inline struct net_device * alloc_etherdev ( size_t priv_size ) { + struct net_device *netdev; + + netdev = alloc_netdev ( priv_size ); + if ( netdev ) { + netdev->ll_protocol = ðernet_protocol; + netdev->max_pkt_len = ETH_FRAME_LEN; + } + return netdev; +} + +#endif /* _GPXE_ETHERNET_H */ diff --git a/gpxe/src/include/gpxe/fakedhcp.h b/gpxe/src/include/gpxe/fakedhcp.h new file mode 100644 index 0000000..550b74f --- /dev/null +++ b/gpxe/src/include/gpxe/fakedhcp.h @@ -0,0 +1,21 @@ +#ifndef _GPXE_FAKEDHCP_H +#define _GPXE_FAKEDHCP_H + +/** @file + * + * Fake DHCP packets + * + */ + +#include + +struct net_device; + +extern int create_fakedhcpdiscover ( struct net_device *netdev, + void *data, size_t max_len ); +extern int create_fakedhcpack ( struct net_device *netdev, + void *data, size_t max_len ); +extern int create_fakepxebsack ( struct net_device *netdev, + void *data, size_t max_len ); + +#endif /* _GPXE_FAKEDHCP_H */ diff --git a/gpxe/src/include/gpxe/features.h b/gpxe/src/include/gpxe/features.h new file mode 100644 index 0000000..32c3169 --- /dev/null +++ b/gpxe/src/include/gpxe/features.h @@ -0,0 +1,95 @@ +#ifndef _GPXE_FEATURES_H +#define _GPXE_FEATURES_H + +#include +#include +#include + +/** @file + * + * Feature list + * + */ + +/** + * @defgroup featurecat Feature categories + * @{ + */ + +#define FEATURE_PROTOCOL 01 /**< Network protocols */ +#define FEATURE_IMAGE 02 /**< Image formats */ +#define FEATURE_MISC 03 /**< Miscellaneous */ + +/** @} */ + +/** + * @defgroup dhcpfeatures DHCP feature option tags + * + * DHCP feature option tags are Etherboot encapsulated options in the + * range 0x10-0x7f. + * + * @{ + */ + +#define DHCP_EB_FEATURE_PXE_EXT 0x10 /**< PXE API extensions */ +#define DHCP_EB_FEATURE_ISCSI 0x11 /**< iSCSI protocol */ +#define DHCP_EB_FEATURE_AOE 0x12 /**< AoE protocol */ +#define DHCP_EB_FEATURE_HTTP 0x13 /**< HTTP protocol */ +#define DHCP_EB_FEATURE_HTTPS 0x14 /**< HTTPS protocol */ +#define DHCP_EB_FEATURE_TFTP 0x15 /**< TFTP protocol */ +#define DHCP_EB_FEATURE_FTP 0x16 /**< FTP protocol */ +#define DHCP_EB_FEATURE_DNS 0x17 /**< DNS protocol */ +#define DHCP_EB_FEATURE_BZIMAGE 0x18 /**< bzImage format */ +#define DHCP_EB_FEATURE_MULTIBOOT 0x19 /**< Multiboot format */ +#define DHCP_EB_FEATURE_SLAM 0x1a /**< SLAM protocol */ +#define DHCP_EB_FEATURE_NBI 0x20 /**< NBI format */ +#define DHCP_EB_FEATURE_PXE 0x21 /**< PXE format */ +#define DHCP_EB_FEATURE_ELF 0x22 /**< ELF format */ +#define DHCP_EB_FEATURE_COMBOOT 0x23 /**< COMBOOT format */ +#define DHCP_EB_FEATURE_EFI 0x24 /**< EFI format */ + +/** @} */ + +/** Declare a feature code for DHCP */ +#define __dhcp_feature __table ( uint8_t, dhcp_features, 01 ) + +/** Construct a DHCP feature table entry */ +#define DHCP_FEATURE( feature_opt, ... ) \ + _DHCP_FEATURE ( OBJECT, feature_opt, __VA_ARGS__ ) +#define _DHCP_FEATURE( _name, feature_opt, ... ) \ + __DHCP_FEATURE ( _name, feature_opt, __VA_ARGS__ ) +#define __DHCP_FEATURE( _name, feature_opt, ... ) \ + uint8_t __dhcp_feature_ ## _name [] __dhcp_feature = { \ + feature_opt, DHCP_OPTION ( __VA_ARGS__ ) \ + }; + +/** A named feature */ +struct feature { + /** Feature name */ + char *name; +}; + +/** Declare a named feature */ +#define __feature_name( category ) \ + __table ( struct feature, features, category ) + +/** Construct a named feature */ +#define FEATURE_NAME( category, text ) \ + _FEATURE_NAME ( category, OBJECT, text ) +#define _FEATURE_NAME( category, _name, text ) \ + __FEATURE_NAME ( category, _name, text ) +#define __FEATURE_NAME( category, _name, text ) \ + struct feature __feature_ ## _name __feature_name ( category ) = { \ + .name = text, \ + }; + +/** Declare a feature */ +#define FEATURE( category, text, feature_opt, version ) \ + FEATURE_NAME ( category, text ); \ + DHCP_FEATURE ( feature_opt, version ); + +/** Declare the version number feature */ +#define FEATURE_VERSION( ... ) \ + DHCP_FEATURE ( DHCP_ENCAPSULATED ( DHCP_EB_VERSION ), __VA_ARGS__ ) + +#endif /* _GPXE_FEATURES_H */ diff --git a/gpxe/src/include/gpxe/filter.h b/gpxe/src/include/gpxe/filter.h new file mode 100644 index 0000000..126f634 --- /dev/null +++ b/gpxe/src/include/gpxe/filter.h @@ -0,0 +1,73 @@ +#ifndef _GPXE_FILTER_H +#define _GPXE_FILTER_H + +/** @file + * + * Data transfer filters + * + */ + +#include +#include + +/** + * Half of a data transfer filter + * + * Embed two of these structures within a structure implementing a + * data transfer filter, and intialise with filter_init(). You can + * then use the filter_xxx() methods as the data transfer interface + * methods as required. + */ +struct xfer_filter_half { + /** Data transfer interface */ + struct xfer_interface xfer; + /** Other half of the data transfer filter */ + struct xfer_filter_half *other; +}; + +/** + * Get data transfer interface for the other half of a data transfer filter + * + * @v xfer Data transfer interface + * @ret other Other half's data transfer interface + */ +static inline __attribute__ (( always_inline )) struct xfer_interface * +filter_other_half ( struct xfer_interface *xfer ) { + struct xfer_filter_half *half = + container_of ( xfer, struct xfer_filter_half, xfer ); + return &half->other->xfer; +} + +extern void filter_close ( struct xfer_interface *xfer, int rc ); +extern int filter_vredirect ( struct xfer_interface *xfer, int type, + va_list args ); +extern size_t filter_window ( struct xfer_interface *xfer ); +extern struct io_buffer * filter_alloc_iob ( struct xfer_interface *xfer, + size_t len ); +extern int filter_deliver_iob ( struct xfer_interface *xfer, + struct io_buffer *iobuf, + struct xfer_metadata *meta ); +extern int filter_deliver_raw ( struct xfer_interface *xfer, const void *data, + size_t len ); + +/** + * Initialise a data transfer filter + * + * @v left "Left" half of the filter + * @v left_op Data transfer interface operations for "left" half + * @v right "Right" half of the filter + * @v right_op Data transfer interface operations for "right" half + * @v refcnt Containing object reference counter, or NULL + */ +static inline void filter_init ( struct xfer_filter_half *left, + struct xfer_interface_operations *left_op, + struct xfer_filter_half *right, + struct xfer_interface_operations *right_op, + struct refcnt *refcnt ) { + xfer_init ( &left->xfer, left_op, refcnt ); + xfer_init ( &right->xfer, right_op, refcnt ); + left->other = right; + right->other = left; +} + +#endif /* _GPXE_FILTER_H */ diff --git a/gpxe/src/include/gpxe/ftp.h b/gpxe/src/include/gpxe/ftp.h new file mode 100644 index 0000000..370285c --- /dev/null +++ b/gpxe/src/include/gpxe/ftp.h @@ -0,0 +1,13 @@ +#ifndef _GPXE_FTP_H +#define _GPXE_FTP_H + +/** @file + * + * File transfer protocol + * + */ + +/** FTP default port */ +#define FTP_PORT 21 + +#endif /* _GPXE_FTP_H */ diff --git a/gpxe/src/include/gpxe/gdbserial.h b/gpxe/src/include/gpxe/gdbserial.h new file mode 100644 index 0000000..1863e90 --- /dev/null +++ b/gpxe/src/include/gpxe/gdbserial.h @@ -0,0 +1,19 @@ +#ifndef _GPXE_GDBSERIAL_H +#define _GPXE_GDBSERIAL_H + +/** @file + * + * GDB remote debugging over serial + * + */ + +struct gdb_transport; + +/** + * Set up the serial transport + * + * @ret transport suitable for starting the GDB stub or NULL on error + */ +struct gdb_transport *gdbserial_configure ( void ); + +#endif /* _GPXE_GDBSERIAL_H */ diff --git a/gpxe/src/include/gpxe/gdbstub.h b/gpxe/src/include/gpxe/gdbstub.h new file mode 100644 index 0000000..bf5d24d --- /dev/null +++ b/gpxe/src/include/gpxe/gdbstub.h @@ -0,0 +1,73 @@ +#ifndef _GPXE_GDBSTUB_H +#define _GPXE_GDBSTUB_H + +/** @file + * + * GDB remote debugging + * + */ + +#include +#include +#include + +/** + * A transport mechanism for the GDB protocol + * + */ +struct gdb_transport { + /** Transport name */ + const char *name; + /** + * Set up the transport given a list of arguments + * + * @v argc Number of arguments + * @v argv Argument list + * @ret Return status code + * + * Note that arguments start at argv[0]. + */ + int ( * init ) ( int argc, char **argv ); + /** + * Perform a blocking read + * + * @v buf Buffer + * @v len Size of buffer + * @ret Number of bytes read into buffer + */ + size_t ( * recv ) ( char *buf, size_t len ); + /** + * Write, may block + * + * @v buf Buffer + * @v len Size of buffer + */ + void ( * send ) ( const char *buf, size_t len ); +}; + +#define __gdb_transport __table ( struct gdb_transport, gdb_transports, 01 ) + +/** + * Look up GDB transport by name + * + * @v name Name of transport + * @ret GDB transport or NULL + */ +extern struct gdb_transport *find_gdb_transport ( const char *name ); + +/** + * Break into the debugger using the given transport + * + * @v trans GDB transport + */ +extern void gdbstub_start ( struct gdb_transport *trans ); + +/** + * Interrupt handler + * + * @signo POSIX signal number + * @regs CPU register snapshot + **/ +extern void gdbstub_handler ( int signo, gdbreg_t *regs ); + +#endif /* _GPXE_GDBSTUB_H */ diff --git a/gpxe/src/include/gpxe/gdbudp.h b/gpxe/src/include/gpxe/gdbudp.h new file mode 100644 index 0000000..1a99093 --- /dev/null +++ b/gpxe/src/include/gpxe/gdbudp.h @@ -0,0 +1,22 @@ +#ifndef _GPXE_GDBUDP_H +#define _GPXE_GDBUDP_H + +/** @file + * + * GDB remote debugging over UDP + * + */ + +struct sockaddr_in; +struct gdb_transport; + +/** + * Set up the UDP transport with network address + * + * @name network device name + * @addr IP address and UDP listen port, may be NULL and fields may be zero + * @ret transport suitable for starting the GDB stub or NULL on error + */ +struct gdb_transport *gdbudp_configure ( const char *name, struct sockaddr_in *addr ); + +#endif /* _GPXE_GDBUDP_H */ diff --git a/gpxe/src/include/gpxe/hidemem.h b/gpxe/src/include/gpxe/hidemem.h new file mode 100644 index 0000000..010fdb5 --- /dev/null +++ b/gpxe/src/include/gpxe/hidemem.h @@ -0,0 +1,15 @@ +#ifndef _GPXE_HIDEMEM_H +#define _GPXE_HIDEMEM_H + +/** + * @file + * + * Hidden memory regions + * + */ + +#include + +extern void hide_umalloc ( physaddr_t start, physaddr_t end ); + +#endif /* _GPXE_HIDEMEM_H */ diff --git a/gpxe/src/include/gpxe/hmac.h b/gpxe/src/include/gpxe/hmac.h new file mode 100644 index 0000000..67aefdc --- /dev/null +++ b/gpxe/src/include/gpxe/hmac.h @@ -0,0 +1,30 @@ +#ifndef _GPXE_HMAC_H +#define _GPXE_HMAC_H + +/** @file + * + * Keyed-Hashing for Message Authentication + */ + +#include + +/** + * Update HMAC + * + * @v digest Digest algorithm to use + * @v digest_ctx Digest context + * @v data Data + * @v len Length of data + */ +static inline void hmac_update ( struct digest_algorithm *digest, + void *digest_ctx, const void *data, + size_t len ) { + digest_update ( digest, digest_ctx, data, len ); +} + +extern void hmac_init ( struct digest_algorithm *digest, void *digest_ctx, + void *key, size_t *key_len ); +extern void hmac_final ( struct digest_algorithm *digest, void *digest_ctx, + void *key, size_t *key_len, void *hmac ); + +#endif /* _GPXE_HMAC_H */ diff --git a/gpxe/src/include/gpxe/http.h b/gpxe/src/include/gpxe/http.h new file mode 100644 index 0000000..fa92a95 --- /dev/null +++ b/gpxe/src/include/gpxe/http.h @@ -0,0 +1,21 @@ +#ifndef _GPXE_HTTP_H +#define _GPXE_HTTP_H + +/** @file + * + * Hyper Text Transport Protocol + * + */ + +/** HTTP default port */ +#define HTTP_PORT 80 + +/** HTTPS default port */ +#define HTTPS_PORT 443 + +extern int http_open_filter ( struct xfer_interface *xfer, struct uri *uri, + unsigned int default_port, + int ( * filter ) ( struct xfer_interface *, + struct xfer_interface ** ) ); + +#endif /* _GPXE_HTTP_H */ diff --git a/gpxe/src/include/gpxe/i2c.h b/gpxe/src/include/gpxe/i2c.h new file mode 100644 index 0000000..9d22954 --- /dev/null +++ b/gpxe/src/include/gpxe/i2c.h @@ -0,0 +1,169 @@ +#ifndef _GPXE_I2C_H +#define _GPXE_I2C_H + +/** @file + * + * I2C interface + * + */ + +#include +#include + +/** An I2C device + * + * An I2C device represents a specific slave device on an I2C bus. It + * is accessed via an I2C interface. + */ +struct i2c_device { + /** Address of this device + * + * The actual address sent on the bus will look like + * + * + * + * The "word address overflow" is any excess bits from the + * word address, i.e. any portion that does not fit within the + * defined word address length. + */ + unsigned int dev_addr; + /** Device address length, in bytes + * + * This is the number of bytes that comprise the device + * address, defined to be the portion that terminates with the + * read/write bit. + */ + unsigned int dev_addr_len; + /** Word adddress length, in bytes + * + * This is the number of bytes that comprise the word address, + * defined to be the portion that starts after the read/write + * bit and ends before the first data byte. + * + * For some devices, this length will be zero (i.e. the word + * address is contained entirely within the "word address + * overflow"). + */ + unsigned int word_addr_len; +}; + +/** An I2C interface + * + * An I2C interface provides access to an I2C bus, via which I2C + * devices may be reached. + */ +struct i2c_interface { + /** + * Read data from I2C device + * + * @v i2c I2C interface + * @v i2cdev I2C device + * @v offset Starting offset within the device + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ + int ( * read ) ( struct i2c_interface *i2c, struct i2c_device *i2cdev, + unsigned int offset, uint8_t *data, + unsigned int len ); + /** + * Write data to I2C device + * + * @v i2c I2C interface + * @v i2cdev I2C device + * @v offset Starting offset within the device + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ + int ( * write ) ( struct i2c_interface *i2c, struct i2c_device *i2cdev, + unsigned int offset, const uint8_t *data, + unsigned int len ); +}; + +/** A bit-bashing I2C interface + * + * This provides a standardised way to construct I2C buses via a + * bit-bashing interface. + */ +struct i2c_bit_basher { + /** I2C interface */ + struct i2c_interface i2c; + /** Bit-bashing interface */ + struct bit_basher basher; +}; + +/** Ten-bit address marker + * + * This value is ORed with the I2C device address to indicate a + * ten-bit address format on the bus. + */ +#define I2C_TENBIT_ADDRESS 0x7800 + +/** An I2C write command */ +#define I2C_WRITE 0 + +/** An I2C read command */ +#define I2C_READ 1 + +/** Bit indices used for I2C bit-bashing interface */ +enum { + /** Serial clock */ + I2C_BIT_SCL = 0, + /** Serial data */ + I2C_BIT_SDA, +}; + +/** Delay required for bit-bashing operation */ +#define I2C_UDELAY 5 + +/** Maximum number of cycles to use when attempting a bus reset */ +#define I2C_RESET_MAX_CYCLES 32 + +/** + * Check presence of I2C device + * + * @v i2c I2C interface + * @v i2cdev I2C device + * @ret rc Return status code + * + * Checks for the presence of the device on the I2C bus by attempting + * a zero-length write. + */ +static inline int i2c_check_presence ( struct i2c_interface *i2c, + struct i2c_device *i2cdev ) { + return i2c->write ( i2c, i2cdev, 0, NULL, 0 ); +} + +extern int init_i2c_bit_basher ( struct i2c_bit_basher *i2cbit, + struct bit_basher_operations *bash_op ); + +/** + * Initialise generic I2C EEPROM device + * + * @v i2cdev I2C device + */ +static inline __always_inline void +init_i2c_eeprom ( struct i2c_device *i2cdev, unsigned int dev_addr ) { + i2cdev->dev_addr = dev_addr; + i2cdev->dev_addr_len = 1; + i2cdev->word_addr_len = 1; +} + +/** + * Initialise Atmel AT24C11 + * + * @v i2cdev I2C device + */ +static inline __always_inline void +init_at24c11 ( struct i2c_device *i2cdev ) { + /* This chip has no device address; it must be the only chip + * on the bus. The word address is contained entirely within + * the device address field. + */ + i2cdev->dev_addr = 0; + i2cdev->dev_addr_len = 1; + i2cdev->word_addr_len = 0; +} + +#endif /* _GPXE_I2C_H */ diff --git a/gpxe/src/include/gpxe/ib_mad.h b/gpxe/src/include/gpxe/ib_mad.h new file mode 100644 index 0000000..6c4e95b --- /dev/null +++ b/gpxe/src/include/gpxe/ib_mad.h @@ -0,0 +1,364 @@ +#ifndef _GPXE_IB_MAD_H +#define _GPXE_IB_MAD_H + +/** @file + * + * Infiniband management datagrams + * + */ + +#include +#include + +/***************************************************************************** + * + * Subnet management MADs + * + ***************************************************************************** + */ + +/** A subnet management header + * + * Defined in sections 14.2.1.1 and 14.2.1.2 of the IBA. + */ +struct ib_smp_hdr { + uint64_t mkey; + uint16_t slid; + uint16_t dlid; + uint8_t reserved[28]; +} __attribute__ (( packed )); + +/** Subnet management class version */ +#define IB_SMP_CLASS_VERSION 1 + +/** Subnet management direction bit + * + * This bit resides in the "status" field in the MAD header. + */ +#define IB_SMP_STATUS_D_INBOUND 0x8000 + +/* Subnet management attributes */ +#define IB_SMP_ATTR_NOTICE 0x0002 +#define IB_SMP_ATTR_NODE_DESC 0x0010 +#define IB_SMP_ATTR_NODE_INFO 0x0011 +#define IB_SMP_ATTR_SWITCH_INFO 0x0012 +#define IB_SMP_ATTR_GUID_INFO 0x0014 +#define IB_SMP_ATTR_PORT_INFO 0x0015 +#define IB_SMP_ATTR_PKEY_TABLE 0x0016 +#define IB_SMP_ATTR_SL_TO_VL_TABLE 0x0017 +#define IB_SMP_ATTR_VL_ARB_TABLE 0x0018 +#define IB_SMP_ATTR_LINEAR_FORWARD_TABLE 0x0019 +#define IB_SMP_ATTR_RANDOM_FORWARD_TABLE 0x001A +#define IB_SMP_ATTR_MCAST_FORWARD_TABLE 0x001B +#define IB_SMP_ATTR_SM_INFO 0x0020 +#define IB_SMP_ATTR_VENDOR_DIAG 0x0030 +#define IB_SMP_ATTR_LED_INFO 0x0031 +#define IB_SMP_ATTR_VENDOR_MASK 0xFF00 + +/** + * A Node Description attribute + * + * Defined in section 14.2.5.2 of the IBA + */ +struct ib_node_desc { + char node_string[64]; +} __attribute__ (( packed )); + +/** A Node Information attribute + * + * Defined in section 14.2.5.3 of the IBA. + */ +struct ib_node_info { + uint8_t base_version; + uint8_t class_version; + uint8_t node_type; + uint8_t num_ports; + uint8_t sys_guid[8]; + uint8_t node_guid[8]; + uint8_t port_guid[8]; + uint16_t partition_cap; + uint16_t device_id; + uint32_t revision; + uint8_t local_port_num; + uint8_t vendor_id[3]; +} __attribute__ ((packed)); + +#define IB_NODE_TYPE_HCA 0x01 +#define IB_NODE_TYPE_SWITCH 0x02 +#define IB_NODE_TYPE_ROUTER 0x03 + +/** A GUID Information attribute + * + * Defined in section 14.2.5.5 of the IBA. + */ +struct ib_guid_info { + uint8_t guid[8][8]; +} __attribute__ (( packed )); + +/** A Port Information attribute + * + * Defined in section 14.2.5.6 of the IBA. + */ +struct ib_port_info { + uint64_t mkey; + uint8_t gid_prefix[8]; + uint16_t lid; + uint16_t mastersm_lid; + uint32_t cap_mask; + uint16_t diag_code; + uint16_t mkey_lease_period; + uint8_t local_port_num; + uint8_t link_width_enabled; + uint8_t link_width_supported; + uint8_t link_width_active; + uint8_t link_speed_supported__port_state; + uint8_t port_phys_state__link_down_def_state; + uint8_t mkey_prot_bits__lmc; + uint8_t link_speed_active__link_speed_enabled; + uint8_t neighbour_mtu__mastersm_sl; + uint8_t vl_cap__init_type; + uint8_t vl_high_limit; + uint8_t vl_arbitration_high_cap; + uint8_t vl_arbitration_low_cap; + uint8_t init_type_reply__mtu_cap; + uint8_t vl_stall_count__hoq_life; + uint8_t operational_vls__enforcement; + uint16_t mkey_violations; + uint16_t pkey_violations; + uint16_t qkey_violations; + uint8_t guid_cap; + uint8_t client_reregister__subnet_timeout; + uint8_t resp_time_value; + uint8_t local_phy_errors__overrun_errors; + uint16_t max_credit_hint; + uint32_t link_round_trip_latency; +} __attribute__ (( packed )); + +#define IB_LINK_WIDTH_1X 0x01 +#define IB_LINK_WIDTH_4X 0x02 +#define IB_LINK_WIDTH_8X 0x04 +#define IB_LINK_WIDTH_12X 0x08 + +#define IB_LINK_SPEED_SDR 0x01 +#define IB_LINK_SPEED_DDR 0x02 +#define IB_LINK_SPEED_QDR 0x04 + +#define IB_PORT_STATE_DOWN 0x01 +#define IB_PORT_STATE_INIT 0x02 +#define IB_PORT_STATE_ARMED 0x03 +#define IB_PORT_STATE_ACTIVE 0x04 + +#define IB_PORT_PHYS_STATE_SLEEP 0x01 +#define IB_PORT_PHYS_STATE_POLLING 0x02 + +#define IB_MTU_256 0x01 +#define IB_MTU_512 0x02 +#define IB_MTU_1024 0x03 +#define IB_MTU_2048 0x04 +#define IB_MTU_4096 0x05 + +#define IB_VL_0 0x01 +#define IB_VL_0_1 0x02 +#define IB_VL_0_3 0x03 +#define IB_VL_0_7 0x04 +#define IB_VL_0_14 0x05 + +/** A Partition Key Table attribute + * + * Defined in section 14.2.5.7 of the IBA. + */ +struct ib_pkey_table { + uint16_t pkey[32]; +} __attribute__ (( packed )); + +/** A subnet management attribute */ +union ib_smp_data { + struct ib_node_desc node_desc; + struct ib_node_info node_info; + struct ib_guid_info guid_info; + struct ib_port_info port_info; + struct ib_pkey_table pkey_table; + uint8_t bytes[64]; +} __attribute__ (( packed )); + +/** A subnet management directed route path */ +struct ib_smp_dr_path { + uint8_t hops[64]; +} __attribute__ (( packed )); + +/** Subnet management MAD class-specific data */ +struct ib_smp_class_specific { + uint8_t hop_pointer; + uint8_t hop_count; +} __attribute__ (( packed )); + +/***************************************************************************** + * + * Subnet administration MADs + * + ***************************************************************************** + */ + +struct ib_rmpp_hdr { + uint32_t raw[3]; +} __attribute__ (( packed )); + +struct ib_sa_hdr { + uint32_t sm_key[2]; + uint16_t reserved; + uint16_t attrib_offset; + uint32_t comp_mask[2]; +} __attribute__ (( packed )); + +#define IB_SA_ATTR_MC_MEMBER_REC 0x38 +#define IB_SA_ATTR_PATH_REC 0x35 + +struct ib_path_record { + uint32_t reserved0[2]; + struct ib_gid dgid; + struct ib_gid sgid; + uint16_t dlid; + uint16_t slid; + uint32_t hop_limit__flow_label__raw_traffic; + uint32_t pkey__numb_path__reversible__tclass; + uint8_t reserved1; + uint8_t reserved__sl; + uint8_t mtu_selector__mtu; + uint8_t rate_selector__rate; + uint32_t preference__packet_lifetime__packet_lifetime_selector; + uint32_t reserved2[35]; +} __attribute__ (( packed )); + +#define IB_SA_PATH_REC_DGID (1<<2) +#define IB_SA_PATH_REC_SGID (1<<3) + +struct ib_mc_member_record { + struct ib_gid mgid; + struct ib_gid port_gid; + uint32_t qkey; + uint16_t mlid; + uint8_t mtu_selector__mtu; + uint8_t tclass; + uint16_t pkey; + uint8_t rate_selector__rate; + uint8_t packet_lifetime_selector__packet_lifetime; + uint32_t sl__flow_label__hop_limit; + uint8_t scope__join_state; + uint8_t proxy_join__reserved; + uint16_t reserved0; + uint32_t reserved1[37]; +} __attribute__ (( packed )); + +#define IB_SA_MCMEMBER_REC_MGID (1<<0) +#define IB_SA_MCMEMBER_REC_PORT_GID (1<<1) +#define IB_SA_MCMEMBER_REC_QKEY (1<<2) +#define IB_SA_MCMEMBER_REC_MLID (1<<3) +#define IB_SA_MCMEMBER_REC_MTU_SELECTOR (1<<4) +#define IB_SA_MCMEMBER_REC_MTU (1<<5) +#define IB_SA_MCMEMBER_REC_TRAFFIC_CLASS (1<<6) +#define IB_SA_MCMEMBER_REC_PKEY (1<<7) +#define IB_SA_MCMEMBER_REC_RATE_SELECTOR (1<<8) +#define IB_SA_MCMEMBER_REC_RATE (1<<9) +#define IB_SA_MCMEMBER_REC_PACKET_LIFE_TIME_SELECTOR (1<<10) +#define IB_SA_MCMEMBER_REC_PACKET_LIFE_TIME (1<<11) +#define IB_SA_MCMEMBER_REC_SL (1<<12) +#define IB_SA_MCMEMBER_REC_FLOW_LABEL (1<<13) +#define IB_SA_MCMEMBER_REC_HOP_LIMIT (1<<14) +#define IB_SA_MCMEMBER_REC_SCOPE (1<<15) +#define IB_SA_MCMEMBER_REC_JOIN_STATE (1<<16) +#define IB_SA_MCMEMBER_REC_PROXY_JOIN (1<<17) + +union ib_sa_data { + struct ib_path_record path_record; + struct ib_mc_member_record mc_member_record; +} __attribute__ (( packed )); + +/***************************************************************************** + * + * MADs + * + ***************************************************************************** + */ + +/** Management datagram class_specific data */ +union ib_mad_class_specific { + uint16_t raw; + struct ib_smp_class_specific smp; +} __attribute__ (( packed )); + +/** A management datagram common header + * + * Defined in section 13.4.2 of the IBA. + */ +struct ib_mad_hdr { + uint8_t base_version; + uint8_t mgmt_class; + uint8_t class_version; + uint8_t method; + uint16_t status; + union ib_mad_class_specific class_specific; + uint32_t tid[2]; + uint16_t attr_id; + uint8_t reserved[2]; + uint32_t attr_mod; +} __attribute__ (( packed )); + +/* Management base version */ +#define IB_MGMT_BASE_VERSION 1 + +/* Management classes */ +#define IB_MGMT_CLASS_SUBN_LID_ROUTED 0x01 +#define IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE 0x81 +#define IB_MGMT_CLASS_SUBN_ADM 0x03 +#define IB_MGMT_CLASS_PERF_MGMT 0x04 +#define IB_MGMT_CLASS_BM 0x05 +#define IB_MGMT_CLASS_DEVICE_MGMT 0x06 +#define IB_MGMT_CLASS_CM 0x07 +#define IB_MGMT_CLASS_SNMP 0x08 +#define IB_MGMT_CLASS_VENDOR_RANGE2_START 0x30 +#define IB_MGMT_CLASS_VENDOR_RANGE2_END 0x4F + +/* Management methods */ +#define IB_MGMT_METHOD_GET 0x01 +#define IB_MGMT_METHOD_SET 0x02 +#define IB_MGMT_METHOD_GET_RESP 0x81 +#define IB_MGMT_METHOD_SEND 0x03 +#define IB_MGMT_METHOD_TRAP 0x05 +#define IB_MGMT_METHOD_REPORT 0x06 +#define IB_MGMT_METHOD_REPORT_RESP 0x86 +#define IB_MGMT_METHOD_TRAP_REPRESS 0x07 +#define IB_MGMT_METHOD_DELETE 0x15 + +/* Status codes */ +#define IB_MGMT_STATUS_OK 0x0000 +#define IB_MGMT_STATUS_BAD_VERSION 0x0001 +#define IB_MGMT_STATUS_UNSUPPORTED_METHOD 0x0002 +#define IB_MGMT_STATUS_UNSUPPORTED_METHOD_ATTR 0x0003 +#define IB_MGMT_STATUS_INVALID_VALUE 0x0004 + +/** A subnet management MAD */ +struct ib_mad_smp { + struct ib_mad_hdr mad_hdr; + struct ib_smp_hdr smp_hdr; + union ib_smp_data smp_data; + struct ib_smp_dr_path initial_path; + struct ib_smp_dr_path return_path; +} __attribute__ (( packed )); + +/** A subnet administration MAD */ +struct ib_mad_sa { + struct ib_mad_hdr mad_hdr; + struct ib_rmpp_hdr rmpp_hdr; + struct ib_sa_hdr sa_hdr; + union ib_sa_data sa_data; +} __attribute__ (( packed )); + +/** A management datagram */ +union ib_mad { + struct ib_mad_hdr hdr; + struct ib_mad_smp smp; + struct ib_mad_sa sa; + uint8_t bytes[256]; +} __attribute__ (( packed )); + +#endif /* _GPXE_IB_MAD_H */ diff --git a/gpxe/src/include/gpxe/ib_packet.h b/gpxe/src/include/gpxe/ib_packet.h new file mode 100644 index 0000000..5374802 --- /dev/null +++ b/gpxe/src/include/gpxe/ib_packet.h @@ -0,0 +1,147 @@ +#ifndef _GPXE_IB_PACKET_H +#define _GPXE_IB_PACKET_H + +/** @file + * + * Infiniband packet format + * + */ + +struct ib_device; +struct ib_queue_pair; +struct ib_address_vector; +struct io_buffer; + +/** Half of an Infiniband Global Identifier */ +struct ib_gid_half { + uint8_t bytes[8]; +}; + +/** An Infiniband Global Identifier */ +struct ib_gid { + union { + uint8_t bytes[16]; + uint16_t words[8]; + uint32_t dwords[4]; + struct ib_gid_half half[2]; + } u; +}; + +/** An Infiniband Local Route Header */ +struct ib_local_route_header { + /** Virtual lane and link version */ + uint8_t vl__lver; + /** Service level and next link header */ + uint8_t sl__lnh; + /** Destination LID */ + uint16_t dlid; + /** Packet length */ + uint16_t length; + /** Source LID */ + uint16_t slid; +} __attribute__ (( packed )); + +/** Infiniband virtual lanes */ +enum ib_vl { + IB_VL_DEFAULT = 0, + IB_VL_SMP = 15, +}; + +/** An Infiniband Link Next Header value */ +enum ib_lnh { + IB_LNH_RAW = 0, + IB_LNH_IPv6 = 1, + IB_LNH_BTH = 2, + IB_LNH_GRH = 3 +}; + +/** Default Infiniband LID */ +#define IB_LID_NONE 0xffff + +/** Test for multicast LID */ +#define IB_LID_MULTICAST( lid ) ( ( (lid) >= 0xc000 ) && ( (lid) <= 0xfffe ) ) + +/** An Infiniband Global Route Header */ +struct ib_global_route_header { + /** IP version, traffic class, and flow label + * + * 4 bits : Version of the GRH + * 8 bits : Traffic class + * 20 bits : Flow label + */ + uint32_t ipver__tclass__flowlabel; + /** Payload length */ + uint16_t paylen; + /** Next header */ + uint8_t nxthdr; + /** Hop limit */ + uint8_t hoplmt; + /** Source GID */ + struct ib_gid sgid; + /** Destiniation GID */ + struct ib_gid dgid; +} __attribute__ (( packed )); + +#define IB_GRH_IPVER_IPv6 0x06 +#define IB_GRH_NXTHDR_IBA 0x1b + +/** An Infiniband Base Transport Header */ +struct ib_base_transport_header { + /** Opcode */ + uint8_t opcode; + /** Transport header version, pad count, migration and solicitation */ + uint8_t se__m__padcnt__tver; + /** Partition key */ + uint16_t pkey; + /** Destination queue pair */ + uint32_t dest_qp; + /** Packet sequence number and acknowledge request */ + uint32_t ack__psn; +} __attribute__ (( packed )); + +/** An Infiniband BTH opcode */ +enum ib_bth_opcode { + BTH_OPCODE_UD_SEND = 0x64, +}; + +/** Default Infiniband partition key */ +#define IB_PKEY_NONE 0xffff + +/** Subnet management queue pair number */ +#define IB_QPN_SMP 0 + +/** An Infiniband Datagram Extended Transport Header */ +struct ib_datagram_extended_transport_header { + /** Queue key */ + uint32_t qkey; + /** Source queue pair */ + uint32_t src_qp; +} __attribute__ (( packed )); + +/** All known IB header formats */ +union ib_headers { + struct ib_local_route_header lrh; + struct { + struct ib_local_route_header lrh; + struct ib_global_route_header grh; + struct ib_base_transport_header bth; + struct ib_datagram_extended_transport_header deth; + } __attribute__ (( packed )) lrh__grh__bth__deth; + struct { + struct ib_local_route_header lrh; + struct ib_base_transport_header bth; + struct ib_datagram_extended_transport_header deth; + } __attribute__ (( packed )) lrh__bth__deth; +} __attribute__ (( packed )); + +/** Maximum size required for IB headers */ +#define IB_MAX_HEADER_SIZE sizeof ( union ib_headers ) + +extern int ib_push ( struct ib_device *ibdev, struct io_buffer *iobuf, + struct ib_queue_pair *qp, size_t payload_len, + const struct ib_address_vector *av ); +extern int ib_pull ( struct ib_device *ibdev, struct io_buffer *iobuf, + struct ib_queue_pair **qp, size_t *payload_len, + struct ib_address_vector *av ); + +#endif /* _GPXE_IB_PACKET_H */ diff --git a/gpxe/src/include/gpxe/ib_sma.h b/gpxe/src/include/gpxe/ib_sma.h new file mode 100644 index 0000000..835ed4e --- /dev/null +++ b/gpxe/src/include/gpxe/ib_sma.h @@ -0,0 +1,63 @@ +#ifndef _GPXE_IB_SMA_H +#define _GPXE_IB_SMA_H + +/** @file + * + * Infiniband Subnet Management Agent + * + */ + +#include +#include + +/** Infiniband Subnet Management Agent operations */ +struct ib_sma_operations { + /** Set port information + * + * @v ibdev Infiniband device + * @v port_info New port information + */ + int ( * set_port_info ) ( struct ib_device *ibdev, + const struct ib_port_info *port_info ); +}; + +/** An Infiniband Subnet Management Agent */ +struct ib_sma { + /** Infiniband device */ + struct ib_device *ibdev; + /** SMA operations */ + struct ib_sma_operations *op; + /** SMA completion queue */ + struct ib_completion_queue *cq; + /** SMA queue pair */ + struct ib_queue_pair *qp; + /** Poll process */ + struct process poll; +}; + +/** SMA payload size allocated for received packets */ +#define IB_SMA_PAYLOAD_LEN 2048 + +/** SMA number of send WQEs + * + * This is a policy decision. + */ +#define IB_SMA_NUM_SEND_WQES 4 + +/** SMA number of receive WQEs + * + * This is a policy decision. + */ +#define IB_SMA_NUM_RECV_WQES 2 + +/** SMA number of completion queue entries + * + * This is a policy decision + */ +#define IB_SMA_NUM_CQES 8 + +extern int ib_create_sma ( struct ib_sma *sma, struct ib_device *ibdev, + struct ib_sma_operations *op ); +extern void ib_destroy_sma ( struct ib_sma *sma ); + +#endif /* _GPXE_IB_SMA_H */ diff --git a/gpxe/src/include/gpxe/ib_smc.h b/gpxe/src/include/gpxe/ib_smc.h new file mode 100644 index 0000000..bb9020b --- /dev/null +++ b/gpxe/src/include/gpxe/ib_smc.h @@ -0,0 +1,18 @@ +#ifndef _GPXE_IB_SMC_H +#define _GPXE_IB_SMC_H + +/** @file + * + * Infiniband Subnet Management Client + * + */ + +#include + +typedef int ( * ib_local_mad_t ) ( struct ib_device *ibdev, + union ib_mad *mad ); + +extern int ib_smc_update ( struct ib_device *ibdev, + ib_local_mad_t local_mad ); + +#endif /* _GPXE_IB_SMC_H */ diff --git a/gpxe/src/include/gpxe/icmp.h b/gpxe/src/include/gpxe/icmp.h new file mode 100644 index 0000000..49ade2f --- /dev/null +++ b/gpxe/src/include/gpxe/icmp.h @@ -0,0 +1,23 @@ +#ifndef _GPXE_ICMP_H +#define _GPXE_ICMP_H + +/** @file + * + * ICMP protocol + * + */ + +/** An ICMP header */ +struct icmp_header { + /** Type */ + uint8_t type; + /** Code */ + uint8_t code; + /** Checksum */ + uint16_t chksum; +} __attribute__ (( packed )); + +#define ICMP_ECHO_RESPONSE 0 +#define ICMP_ECHO_REQUEST 8 + +#endif /* _GPXE_ICMP_H */ diff --git a/gpxe/src/include/gpxe/icmp6.h b/gpxe/src/include/gpxe/icmp6.h new file mode 100644 index 0000000..3409361 --- /dev/null +++ b/gpxe/src/include/gpxe/icmp6.h @@ -0,0 +1,57 @@ +#ifndef _GPXE_ICMP6_H +#define _GPXE_ICMP6_H + +/** @file + * + * ICMP6 protocol + * + */ + +#include +#include + +#define ICMP6_NSOLICIT 135 +#define ICMP6_NADVERT 136 + +extern struct tcpip_protocol icmp6_protocol; + +struct icmp6_header { + uint8_t type; + uint8_t code; + uint16_t csum; + /* Message body */ +}; + +struct neighbour_solicit { + uint8_t type; + uint8_t code; + uint16_t csum; + uint32_t reserved; + struct in6_addr target; + /* "Compulsory" options */ + uint8_t opt_type; + uint8_t opt_len; + /* FIXME: hack alert */ + uint8_t opt_ll_addr[6]; +}; + +struct neighbour_advert { + uint8_t type; + uint8_t code; + uint16_t csum; + uint8_t flags; + uint8_t reserved; + struct in6_addr target; + uint8_t opt_type; + uint8_t opt_len; + /* FIXME: hack alert */ + uint8_t opt_ll_addr[6]; +}; + +#define ICMP6_FLAGS_ROUTER 0x80 +#define ICMP6_FLAGS_SOLICITED 0x40 +#define ICMP6_FLAGS_OVERRIDE 0x20 + +int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src, struct in6_addr *dest ); + +#endif /* _GPXE_ICMP6_H */ diff --git a/gpxe/src/include/gpxe/if_arp.h b/gpxe/src/include/gpxe/if_arp.h new file mode 100644 index 0000000..5b25033 --- /dev/null +++ b/gpxe/src/include/gpxe/if_arp.h @@ -0,0 +1,100 @@ +#ifndef _GPXE_IF_ARP_H +#define _GPXE_IF_ARP_H + +/** @file + * + * Address Resolution Protocol constants and types + * + */ + +#include + +/* ARP protocol HARDWARE identifiers. */ +#define ARPHRD_NETROM 0 /**< from KA9Q: NET/ROM pseudo */ +#define ARPHRD_ETHER 1 /**< Ethernet 10Mbps */ +#define ARPHRD_EETHER 2 /**< Experimental Ethernet */ +#define ARPHRD_AX25 3 /**< AX.25 Level 2 */ +#define ARPHRD_PRONET 4 /**< PROnet token ring */ +#define ARPHRD_CHAOS 5 /**< Chaosnet */ +#define ARPHRD_IEEE802 6 /**< IEEE 802.2 Ethernet/TR/TB */ +#define ARPHRD_ARCNET 7 /**< ARCnet */ +#define ARPHRD_APPLETLK 8 /**< APPLEtalk */ +#define ARPHRD_DLCI 15 /**< Frame Relay DLCI */ +#define ARPHRD_ATM 19 /**< ATM */ +#define ARPHRD_METRICOM 23 /**< Metricom STRIP (new IANA id) */ +#define ARPHRD_IEEE1394 24 /**< IEEE 1394 IPv4 - RFC 2734 */ +#define ARPHRD_EUI64 27 /**< EUI-64 */ +#define ARPHRD_INFINIBAND 32 /**< InfiniBand */ + +/* ARP protocol opcodes. */ +#define ARPOP_REQUEST 1 /**< ARP request */ +#define ARPOP_REPLY 2 /**< ARP reply */ +#define ARPOP_RREQUEST 3 /**< RARP request */ +#define ARPOP_RREPLY 4 /**< RARP reply */ +#define ARPOP_InREQUEST 8 /**< InARP request */ +#define ARPOP_InREPLY 9 /**< InARP reply */ +#define ARPOP_NAK 10 /**< (ATM)ARP NAK */ + +/** + * An ARP header + * + * This contains only the fixed-size portions of an ARP header; for + * other fields use the arp_{sender,target}_{ha,pa} family of + * functions. + */ +struct arphdr { + /** Link-layer protocol + * + * This is an ARPHRD_XXX constant + */ + uint16_t ar_hrd; + /** Network-layer protocol + * + * This is, for Ethernet, an ETH_P_XXX constant. + */ + uint16_t ar_pro; + /** Link-layer address length */ + uint8_t ar_hln; + /** Network-layer address length */ + uint8_t ar_pln; + /** ARP opcode */ + uint16_t ar_op; +} __attribute__ (( packed )); + +/** ARP packet sender hardware address + * + * @v arphdr ARP header + * @ret ar_sha Sender hardware address + */ +static inline void * arp_sender_ha ( struct arphdr *arphdr ) { + return ( ( ( void * ) arphdr ) + sizeof ( *arphdr ) ); +} + +/** ARP packet sender protocol address + * + * @v arphdr ARP header + * @ret ar_spa Sender protocol address + */ +static inline void * arp_sender_pa ( struct arphdr *arphdr ) { + return ( arp_sender_ha ( arphdr ) + arphdr->ar_hln ); +} + +/** ARP packet target hardware address + * + * @v arphdr ARP header + * @ret ar_tha Target hardware address + */ +static inline void * arp_target_ha ( struct arphdr *arphdr ) { + return ( arp_sender_pa ( arphdr ) + arphdr->ar_pln ); +} + +/** ARP packet target protocol address + * + * @v arphdr ARP header + * @ret ar_tpa Target protocol address + */ +static inline void * arp_target_pa ( struct arphdr *arphdr ) { + return ( arp_target_ha ( arphdr ) + arphdr->ar_hln ); +} + +#endif /* _GPXE_IF_ARP_H */ diff --git a/gpxe/src/include/gpxe/if_ether.h b/gpxe/src/include/gpxe/if_ether.h new file mode 100644 index 0000000..2f3f33d --- /dev/null +++ b/gpxe/src/include/gpxe/if_ether.h @@ -0,0 +1,33 @@ +#ifndef _GPXE_IF_ETHER_H +#define _GPXE_IF_ETHER_H + +#include + +#define ETH_ALEN 6 /* Size of Ethernet address */ +#define ETH_HLEN 14 /* Size of ethernet header */ +#define ETH_ZLEN 60 /* Minimum packet */ +#define ETH_FRAME_LEN 1514 /* Maximum packet */ +#define ETH_DATA_ALIGN 2 /* Amount needed to align the data after an ethernet header */ +#ifndef ETH_MAX_MTU +#define ETH_MAX_MTU (ETH_FRAME_LEN-ETH_HLEN) +#endif + +#define ETH_P_RAW 0x0000 /* Raw packet */ +#define ETH_P_IP 0x0800 /* Internet Protocl Packet */ +#define ETH_P_ARP 0x0806 /* Address Resolution Protocol */ +#define ETH_P_RARP 0x8035 /* Reverse Address resolution Protocol */ +#define ETH_P_IPV6 0x86DD /* IPv6 over blueblook */ +#define ETH_P_SLOW 0x8809 /* Ethernet slow protocols */ +#define ETH_P_AOE 0x88A2 /* ATA over Ethernet */ + +/** An Ethernet link-layer header */ +struct ethhdr { + /** Destination MAC address */ + uint8_t h_dest[ETH_ALEN]; + /** Source MAC address */ + uint8_t h_source[ETH_ALEN]; + /** Protocol ID */ + uint16_t h_protocol; +} __attribute__ ((packed)); + +#endif /* _GPXE_IF_ETHER_H */ diff --git a/gpxe/src/include/gpxe/image.h b/gpxe/src/include/gpxe/image.h new file mode 100644 index 0000000..b953e15 --- /dev/null +++ b/gpxe/src/include/gpxe/image.h @@ -0,0 +1,190 @@ +#ifndef _GPXE_IMAGE_H +#define _GPXE_IMAGE_H + +/** + * @file + * + * Executable/loadable images + * + */ + +#include +#include +#include +#include + +struct uri; +struct image_type; + +/** An executable or loadable image */ +struct image { + /** Reference count */ + struct refcnt refcnt; + + /** List of registered images */ + struct list_head list; + + /** URI of image */ + struct uri *uri; + /** Name */ + char name[16]; + /** Flags */ + unsigned int flags; + + /** Command line to pass to image */ + char *cmdline; + /** Raw file image */ + userptr_t data; + /** Length of raw file image */ + size_t len; + + /** Image type, if known */ + struct image_type *type; + /** Image type private data */ + union { + physaddr_t phys; + userptr_t user; + unsigned long ul; + } priv; + + /** Replacement image + * + * An image wishing to replace itself with another image (in a + * style similar to a Unix exec() call) should return from its + * exec() method with the replacement image set to point to + * the new image. The new image must already be in a suitable + * state for execution (i.e. loaded). + * + * If an image unregisters itself as a result of being + * executed, it must make sure that its replacement image (if + * any) is registered, otherwise the replacement is likely to + * be freed before it can be executed. + */ + struct image *replacement; +}; + +/** Image is loaded */ +#define IMAGE_LOADED 0x0001 + +/** An executable or loadable image type */ +struct image_type { + /** Name of this image type */ + char *name; + /** + * Load image into memory + * + * @v image Executable/loadable image + * @ret rc Return status code + * + * Load the image into memory at the correct location as + * determined by the file format. + * + * If the file image is in the correct format, the method must + * update @c image->type to point to its own type (unless @c + * image->type is already set). This allows the autoloading + * code to disambiguate between "this is not my image format" + * and "there is something wrong with this image". In + * particular, setting @c image->type and then returning an + * error will cause image_autoload() to abort and return an + * error, rather than continuing to the next image type. + */ + int ( * load ) ( struct image *image ); + /** + * Execute loaded image + * + * @v image Loaded image + * @ret rc Return status code + * + * Note that the image may be invalidated by the act of + * execution, i.e. an image is allowed to choose to unregister + * (and so potentially free) itself. + */ + int ( * exec ) ( struct image *image ); +}; + +/** + * Multiboot image probe priority + * + * Multiboot images are also valid executables in another format + * (e.g. ELF), so we must perform the multiboot probe first. + */ +#define PROBE_MULTIBOOT 01 + +/** + * Normal image probe priority + */ +#define PROBE_NORMAL 02 + +/** + * PXE image probe priority + * + * PXE images have no signature checks, so will claim all image files. + * They must therefore be tried last in the probe order list. + */ +#define PROBE_PXE 03 + +/** An executable or loadable image type */ +#define __image_type( probe_order ) \ + __table ( struct image_type, image_types, probe_order ) + +extern struct list_head images; + +/** Iterate over all registered images */ +#define for_each_image( image ) \ + list_for_each_entry ( (image), &images, list ) + +/** + * Test for existence of images + * + * @ret existence Some images exist + */ +static inline int have_images ( void ) { + return ( ! list_empty ( &images ) ); +} + +extern struct image * alloc_image ( void ); +extern int image_set_uri ( struct image *image, struct uri *uri ); +extern int image_set_cmdline ( struct image *image, const char *cmdline ); +extern int register_image ( struct image *image ); +extern void unregister_image ( struct image *image ); +extern void promote_image ( struct image *image ); +struct image * find_image ( const char *name ); +extern int image_load ( struct image *image ); +extern int image_autoload ( struct image *image ); +extern int image_exec ( struct image *image ); +extern int register_and_autoload_image ( struct image *image ); +extern int register_and_autoexec_image ( struct image *image ); + +/** + * Increment reference count on an image + * + * @v image Image + * @ret image Image + */ +static inline struct image * image_get ( struct image *image ) { + ref_get ( &image->refcnt ); + return image; +} + +/** + * Decrement reference count on an image + * + * @v image Image + */ +static inline void image_put ( struct image *image ) { + ref_put ( &image->refcnt ); +} + +/** + * Set image name + * + * @v image Image + * @v name New image name + * @ret rc Return status code + */ +static inline int image_set_name ( struct image *image, const char *name ) { + strncpy ( image->name, name, ( sizeof ( image->name ) - 1 ) ); + return 0; +} + +#endif /* _GPXE_IMAGE_H */ diff --git a/gpxe/src/include/gpxe/in.h b/gpxe/src/include/gpxe/in.h new file mode 100644 index 0000000..831a611 --- /dev/null +++ b/gpxe/src/include/gpxe/in.h @@ -0,0 +1,103 @@ +#ifndef _GPXE_IN_H +#define _GPXE_IN_H + +#include +#include + +/* Protocol numbers */ + +#define IP_ICMP 1 +#define IP_IGMP 2 +#define IP_TCP 6 +#define IP_UDP 17 +#define IP_ICMP6 58 + +/* IP address constants */ + +#define INADDR_NONE 0xffffffff + +#define INADDR_BROADCAST 0xffffffff + +#define IN_CLASSA(addr) ( ( (addr) & 0x80000000 ) == 0x00000000 ) +#define IN_CLASSA_NET 0xff000000 +#define IN_CLASSB(addr) ( ( (addr) & 0xc0000000 ) == 0x80000000 ) +#define IN_CLASSB_NET 0xffff0000 +#define IN_CLASSC(addr) ( ( (addr) & 0xe0000000 ) == 0xc0000000 ) +#define IN_CLASSC_NET 0xffffff00 +#define IN_MULTICAST(addr) ( ( (addr) & 0xf0000000 ) == 0xe0000000 ) + +/** + * IP address structure + */ +struct in_addr { + uint32_t s_addr; +}; + +typedef struct in_addr in_addr; + +/** + * IP6 address structure + */ +struct in6_addr { + union { + uint8_t u6_addr8[16]; + uint16_t u6_addr16[8]; + uint32_t u6_addr32[4]; + } in6_u; +#define s6_addr in6_u.u6_addr8 +#define s6_addr16 in6_u.u6_addr16 +#define s6_addr32 in6_u.u6_addr32 +}; + +/** + * IPv4 socket address + */ +struct sockaddr_in { + /** Socket address family (part of struct @c sockaddr) + * + * Always set to @c AF_INET for IPv4 addresses + */ + sa_family_t sin_family; + /** TCP/IP port (part of struct @c sockaddr_tcpip) */ + uint16_t sin_port; + /** IPv4 address */ + struct in_addr sin_addr; + /** Padding + * + * This ensures that a struct @c sockaddr_tcpip is large + * enough to hold a socket address for any TCP/IP address + * family. + */ + char pad[ sizeof ( struct sockaddr ) - sizeof ( sa_family_t ) + - sizeof ( uint16_t ) + - sizeof ( struct in_addr ) ]; +} __attribute__ (( may_alias )); + +/** + * IPv6 socket address + */ +struct sockaddr_in6 { + /** Socket address family (part of struct @c sockaddr) + * + * Always set to @c AF_INET6 for IPv6 addresses + */ + sa_family_t sin_family; + /** TCP/IP port (part of struct @c sockaddr_tcpip) */ + uint16_t sin_port; + uint32_t sin6_flowinfo; /* Flow number */ + struct in6_addr sin6_addr; /* 128-bit destination address */ + uint32_t sin6_scope_id; /* Scope ID */ +} __attribute__ (( may_alias )); + +extern int inet_aton ( const char *cp, struct in_addr *inp ); +extern char * inet_ntoa ( struct in_addr in ); + +/* Adding the following for IP6 support + * + +extern int inet6_aton ( const char *cp, struct in6_addr *inp ); +extern char * inet6_ntoa ( struct in_addr in ); + + */ + +#endif /* _GPXE_IN_H */ diff --git a/gpxe/src/include/gpxe/infiniband.h b/gpxe/src/include/gpxe/infiniband.h new file mode 100644 index 0000000..196b595 --- /dev/null +++ b/gpxe/src/include/gpxe/infiniband.h @@ -0,0 +1,561 @@ +#ifndef _GPXE_INFINIBAND_H +#define _GPXE_INFINIBAND_H + +/** @file + * + * Infiniband protocol + * + */ + +#include +#include +#include +#include +#include + +/** Subnet administrator QPN */ +#define IB_SA_QPN 1 + +/** Broadcast QPN */ +#define IB_BROADCAST_QPN 0xffffffUL + +/** Subnet administrator queue key */ +#define IB_GLOBAL_QKEY 0x80010000UL + +struct ib_device; +struct ib_queue_pair; +struct ib_address_vector; +struct ib_completion_queue; + +/** An Infiniband Work Queue */ +struct ib_work_queue { + /** Containing queue pair */ + struct ib_queue_pair *qp; + /** "Is a send queue" flag */ + int is_send; + /** Associated completion queue */ + struct ib_completion_queue *cq; + /** List of work queues on this completion queue */ + struct list_head list; + /** Number of work queue entries */ + unsigned int num_wqes; + /** Number of occupied work queue entries */ + unsigned int fill; + /** Next work queue entry index + * + * This is the index of the next entry to be filled (i.e. the + * first empty entry). This value is not bounded by num_wqes; + * users must logical-AND with (num_wqes-1) to generate an + * array index. + */ + unsigned long next_idx; + /** I/O buffers assigned to work queue */ + struct io_buffer **iobufs; + /** Driver private data */ + void *drv_priv; +}; + +/** An Infiniband multicast GID */ +struct ib_multicast_gid { + /** List of multicast GIDs on this QP */ + struct list_head list; + /** Multicast GID */ + struct ib_gid gid; +}; + +/** An Infiniband Queue Pair */ +struct ib_queue_pair { + /** Containing Infiniband device */ + struct ib_device *ibdev; + /** List of queue pairs on this Infiniband device */ + struct list_head list; + /** Queue Pair Number */ + unsigned long qpn; + /** Queue key */ + unsigned long qkey; + /** Send queue */ + struct ib_work_queue send; + /** Receive queue */ + struct ib_work_queue recv; + /** List of multicast GIDs */ + struct list_head mgids; + /** Driver private data */ + void *drv_priv; + /** Queue owner private data */ + void *owner_priv; +}; + +/** Infiniband queue pair modification flags */ +enum ib_queue_pair_mods { + IB_MODIFY_QKEY = 0x0001, +}; + +/** An Infiniband Address Vector */ +struct ib_address_vector { + /** Queue Pair Number */ + unsigned long qpn; + /** Queue key + * + * Not specified for received packets. + */ + unsigned long qkey; + /** Local ID */ + unsigned int lid; + /** Rate + * + * Not specified for received packets. + */ + unsigned int rate; + /** Service level */ + unsigned int sl; + /** GID is present */ + unsigned int gid_present; + /** GID, if present */ + struct ib_gid gid; +}; + +/** Infiniband completion queue operations */ +struct ib_completion_queue_operations { + /** + * Complete Send WQE + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v iobuf I/O buffer + * @v rc Completion status code + */ + void ( * complete_send ) ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct io_buffer *iobuf, int rc ); + /** + * Complete Receive WQE + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v av Address vector, or NULL + * @v iobuf I/O buffer + * @v rc Completion status code + */ + void ( * complete_recv ) ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct ib_address_vector *av, + struct io_buffer *iobuf, int rc ); +}; + +/** An Infiniband Completion Queue */ +struct ib_completion_queue { + /** Completion queue number */ + unsigned long cqn; + /** Number of completion queue entries */ + unsigned int num_cqes; + /** Next completion queue entry index + * + * This is the index of the next entry to be filled (i.e. the + * first empty entry). This value is not bounded by num_wqes; + * users must logical-AND with (num_wqes-1) to generate an + * array index. + */ + unsigned long next_idx; + /** List of work queues completing to this queue */ + struct list_head work_queues; + /** Completion queue operations */ + struct ib_completion_queue_operations *op; + /** Driver private data */ + void *drv_priv; +}; + +/** + * Infiniband device operations + * + * These represent a subset of the Infiniband Verbs. + */ +struct ib_device_operations { + /** Create completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + * @ret rc Return status code + */ + int ( * create_cq ) ( struct ib_device *ibdev, + struct ib_completion_queue *cq ); + /** Destroy completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + */ + void ( * destroy_cq ) ( struct ib_device *ibdev, + struct ib_completion_queue *cq ); + /** Create queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @ret rc Return status code + */ + int ( * create_qp ) ( struct ib_device *ibdev, + struct ib_queue_pair *qp ); + /** Modify queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v mod_list Modification list + * @ret rc Return status code + */ + int ( * modify_qp ) ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + unsigned long mod_list ); + /** Destroy queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + */ + void ( * destroy_qp ) ( struct ib_device *ibdev, + struct ib_queue_pair *qp ); + /** Post send work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v av Address vector + * @v iobuf I/O buffer + * @ret rc Return status code + * + * If this method returns success, the I/O buffer remains + * owned by the queue pair. If this method returns failure, + * the I/O buffer is immediately released; the failure is + * interpreted as "failure to enqueue buffer". + */ + int ( * post_send ) ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct ib_address_vector *av, + struct io_buffer *iobuf ); + /** Post receive work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v iobuf I/O buffer + * @ret rc Return status code + * + * If this method returns success, the I/O buffer remains + * owned by the queue pair. If this method returns failure, + * the I/O buffer is immediately released; the failure is + * interpreted as "failure to enqueue buffer". + */ + int ( * post_recv ) ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct io_buffer *iobuf ); + /** Poll completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + * + * The relevant completion handler (specified at completion + * queue creation time) takes ownership of the I/O buffer. + */ + void ( * poll_cq ) ( struct ib_device *ibdev, + struct ib_completion_queue *cq ); + /** + * Poll event queue + * + * @v ibdev Infiniband device + */ + void ( * poll_eq ) ( struct ib_device *ibdev ); + /** + * Open port + * + * @v ibdev Infiniband device + * @ret rc Return status code + */ + int ( * open ) ( struct ib_device *ibdev ); + /** + * Close port + * + * @v ibdev Infiniband device + */ + void ( * close ) ( struct ib_device *ibdev ); + /** Attach to multicast group + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v gid Multicast GID + * @ret rc Return status code + */ + int ( * mcast_attach ) ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct ib_gid *gid ); + /** Detach from multicast group + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v gid Multicast GID + */ + void ( * mcast_detach ) ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct ib_gid *gid ); +}; + +/** An Infiniband device */ +struct ib_device { + /** Reference counter */ + struct refcnt refcnt; + /** List of Infiniband devices */ + struct list_head list; + /** Underlying device */ + struct device *dev; + /** List of queue pairs */ + struct list_head qps; + /** Infiniband operations */ + struct ib_device_operations *op; + /** Port number */ + unsigned int port; + /** Port open request counter */ + unsigned int open_count; + + /** Port state */ + uint8_t port_state; + /** Link width */ + uint8_t link_width; + /** Link speed */ + uint8_t link_speed; + /** Port GID */ + struct ib_gid gid; + /** Port LID */ + uint16_t lid; + /** Subnet manager LID */ + uint16_t sm_lid; + /** Subnet manager SL */ + uint8_t sm_sl; + /** Partition key */ + uint16_t pkey; + + /** Outbound packet sequence number */ + uint32_t psn; + + /** Driver private data */ + void *drv_priv; + /** Owner private data */ + void *owner_priv; +}; + +extern struct ib_completion_queue * +ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes, + struct ib_completion_queue_operations *op ); +extern void ib_destroy_cq ( struct ib_device *ibdev, + struct ib_completion_queue *cq ); +extern struct ib_queue_pair * +ib_create_qp ( struct ib_device *ibdev, unsigned int num_send_wqes, + struct ib_completion_queue *send_cq, unsigned int num_recv_wqes, + struct ib_completion_queue *recv_cq, unsigned long qkey ); +extern int ib_modify_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp, + unsigned long mod_list, unsigned long qkey ); +extern void ib_destroy_qp ( struct ib_device *ibdev, + struct ib_queue_pair *qp ); +extern struct ib_queue_pair * ib_find_qp_qpn ( struct ib_device *ibdev, + unsigned long qpn ); +extern struct ib_queue_pair * ib_find_qp_mgid ( struct ib_device *ibdev, + struct ib_gid *gid ); +extern struct ib_work_queue * ib_find_wq ( struct ib_completion_queue *cq, + unsigned long qpn, int is_send ); +extern int ib_post_send ( struct ib_device *ibdev, struct ib_queue_pair *qp, + struct ib_address_vector *av, + struct io_buffer *iobuf ); +extern int ib_post_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp, + struct io_buffer *iobuf ); +extern void ib_complete_send ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct io_buffer *iobuf, int rc ); +extern void ib_complete_recv ( struct ib_device *ibdev, + struct ib_queue_pair *qp, + struct ib_address_vector *av, + struct io_buffer *iobuf, int rc ); +extern int ib_open ( struct ib_device *ibdev ); +extern void ib_close ( struct ib_device *ibdev ); +extern int ib_mcast_attach ( struct ib_device *ibdev, struct ib_queue_pair *qp, + struct ib_gid *gid ); +extern void ib_mcast_detach ( struct ib_device *ibdev, + struct ib_queue_pair *qp, struct ib_gid *gid ); +extern struct ib_device * alloc_ibdev ( size_t priv_size ); +extern int register_ibdev ( struct ib_device *ibdev ); +extern void unregister_ibdev ( struct ib_device *ibdev ); +extern void ib_link_state_changed ( struct ib_device *ibdev ); +extern struct list_head ib_devices; + +/** Iterate over all network devices */ +#define for_each_ibdev( ibdev ) \ + list_for_each_entry ( (ibdev), &ib_devices, list ) + +/** + * Poll completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + */ +static inline __always_inline void +ib_poll_cq ( struct ib_device *ibdev, struct ib_completion_queue *cq ) { + ibdev->op->poll_cq ( ibdev, cq ); +} + +/** + * Check link state + * + * @v ibdev Infiniband device + * @ret link_up Link is up + */ +static inline __always_inline int +ib_link_ok ( struct ib_device *ibdev ) { + return ( ibdev->port_state == IB_PORT_STATE_ACTIVE ); +} + +/** + * Get reference to Infiniband device + * + * @v ibdev Infiniband device + * @ret ibdev Infiniband device + */ +static inline __always_inline struct ib_device * +ibdev_get ( struct ib_device *ibdev ) { + ref_get ( &ibdev->refcnt ); + return ibdev; +} + +/** + * Drop reference to Infiniband device + * + * @v ibdev Infiniband device + */ +static inline __always_inline void +ibdev_put ( struct ib_device *ibdev ) { + ref_put ( &ibdev->refcnt ); +} + +/** + * Set Infiniband work queue driver-private data + * + * @v wq Work queue + * @v priv Private data + */ +static inline __always_inline void +ib_wq_set_drvdata ( struct ib_work_queue *wq, void *priv ) { + wq->drv_priv = priv; +} + +/** + * Get Infiniband work queue driver-private data + * + * @v wq Work queue + * @ret priv Private data + */ +static inline __always_inline void * +ib_wq_get_drvdata ( struct ib_work_queue *wq ) { + return wq->drv_priv; +} + +/** + * Set Infiniband queue pair driver-private data + * + * @v qp Queue pair + * @v priv Private data + */ +static inline __always_inline void +ib_qp_set_drvdata ( struct ib_queue_pair *qp, void *priv ) { + qp->drv_priv = priv; +} + +/** + * Get Infiniband queue pair driver-private data + * + * @v qp Queue pair + * @ret priv Private data + */ +static inline __always_inline void * +ib_qp_get_drvdata ( struct ib_queue_pair *qp ) { + return qp->drv_priv; +} + +/** + * Set Infiniband queue pair owner-private data + * + * @v qp Queue pair + * @v priv Private data + */ +static inline __always_inline void +ib_qp_set_ownerdata ( struct ib_queue_pair *qp, void *priv ) { + qp->owner_priv = priv; +} + +/** + * Get Infiniband queue pair owner-private data + * + * @v qp Queue pair + * @ret priv Private data + */ +static inline __always_inline void * +ib_qp_get_ownerdata ( struct ib_queue_pair *qp ) { + return qp->owner_priv; +} + +/** + * Set Infiniband completion queue driver-private data + * + * @v cq Completion queue + * @v priv Private data + */ +static inline __always_inline void +ib_cq_set_drvdata ( struct ib_completion_queue *cq, void *priv ) { + cq->drv_priv = priv; +} + +/** + * Get Infiniband completion queue driver-private data + * + * @v cq Completion queue + * @ret priv Private data + */ +static inline __always_inline void * +ib_cq_get_drvdata ( struct ib_completion_queue *cq ) { + return cq->drv_priv; +} + +/** + * Set Infiniband device driver-private data + * + * @v ibdev Infiniband device + * @v priv Private data + */ +static inline __always_inline void +ib_set_drvdata ( struct ib_device *ibdev, void *priv ) { + ibdev->drv_priv = priv; +} + +/** + * Get Infiniband device driver-private data + * + * @v ibdev Infiniband device + * @ret priv Private data + */ +static inline __always_inline void * +ib_get_drvdata ( struct ib_device *ibdev ) { + return ibdev->drv_priv; +} + +/** + * Set Infiniband device owner-private data + * + * @v ibdev Infiniband device + * @v priv Private data + */ +static inline __always_inline void +ib_set_ownerdata ( struct ib_device *ibdev, void *priv ) { + ibdev->owner_priv = priv; +} + +/** + * Get Infiniband device owner-private data + * + * @v ibdev Infiniband device + * @ret priv Private data + */ +static inline __always_inline void * +ib_get_ownerdata ( struct ib_device *ibdev ) { + return ibdev->owner_priv; +} + +#endif /* _GPXE_INFINIBAND_H */ diff --git a/gpxe/src/include/gpxe/init.h b/gpxe/src/include/gpxe/init.h new file mode 100644 index 0000000..e0e9f9c --- /dev/null +++ b/gpxe/src/include/gpxe/init.h @@ -0,0 +1,74 @@ +#ifndef _GPXE_INIT_H +#define _GPXE_INIT_H + +#include + +/** + * An initialisation function + * + * Initialisation functions are called exactly once, as part of the + * call to initialise(). + */ +struct init_fn { + void ( * initialise ) ( void ); +}; + +/** Declare an initialisation functon */ +#define __init_fn( init_order ) \ + __table ( struct init_fn, init_fns, init_order ) + +/** @defgroup initfn_order Initialisation function ordering + * @{ + */ + +#define INIT_EARLY 01 /**< Early initialisation */ +#define INIT_SERIAL 02 /**< Serial driver initialisation */ +#define INIT_CONSOLE 03 /**< Console initialisation */ +#define INIT_NORMAL 04 /**< Normal initialisation */ + +/** @} */ + +/** Shutdown flags */ +enum shutdown_flags { + /** Shutdown is in order to exit (return to gPXE's caller) */ + SHUTDOWN_EXIT = 0x0001, + /** Shutdown is in order to boot an OS */ + SHUTDOWN_BOOT = 0x0002, + /** Do not remove devices */ + SHUTDOWN_KEEP_DEVICES = 0x0004, +}; + +/** + * A startup/shutdown function + * + * Startup and shutdown functions may be called multiple times, as + * part of the calls to startup() and shutdown(). + */ +struct startup_fn { + void ( * startup ) ( void ); + void ( * shutdown ) ( int flags ); +}; + +/** Declare a startup/shutdown function */ +#define __startup_fn( startup_order ) \ + __table ( struct startup_fn, startup_fns, startup_order ) + +/** @defgroup startfn_order Startup/shutdown function ordering + * + * Shutdown functions are called in the reverse order to startup + * functions. + * + * @{ + */ + +#define STARTUP_EARLY 01 /**< Early startup */ +#define STARTUP_NORMAL 02 /**< Normal startup */ +#define STARTUP_LATE 03 /**< Late startup */ + +/** @} */ + +extern void initialise ( void ); +extern void startup ( void ); +extern void shutdown ( int flags ); + +#endif /* _GPXE_INIT_H */ diff --git a/gpxe/src/include/gpxe/interface.h b/gpxe/src/include/gpxe/interface.h new file mode 100644 index 0000000..94c711a --- /dev/null +++ b/gpxe/src/include/gpxe/interface.h @@ -0,0 +1,56 @@ +#ifndef _GPXE_INTERFACE_H +#define _GPXE_INTERFACE_H + +/** @file + * + * Object communication interfaces + * + */ + +#include + +/** An object communication interface */ +struct interface { + /** Destination interface + * + * When messages are sent via this interface, they will be + * delivered to the destination interface. + * + * This pointer may never be NULL. When the interface is + * unplugged, it should point to a null interface. + */ + struct interface *dest; + /** Reference counter + * + * If this interface is not part of a reference-counted + * object, this field may be NULL. + */ + struct refcnt *refcnt; +}; + +/** + * Increment reference count on an interface + * + * @v intf Interface + * @ret intf Interface + */ +static inline __attribute__ (( always_inline )) struct interface * +intf_get ( struct interface *intf ) { + ref_get ( intf->refcnt ); + return intf; +} + +/** + * Decrement reference count on an interface + * + * @v intf Interface + */ +static inline __attribute__ (( always_inline )) void +intf_put ( struct interface *intf ) { + ref_put ( intf->refcnt ); +} + +extern void plug ( struct interface *intf, struct interface *dest ); +extern void plug_plug ( struct interface *a, struct interface *b ); + +#endif /* _GPXE_INTERFACE_H */ diff --git a/gpxe/src/include/gpxe/io.h b/gpxe/src/include/gpxe/io.h new file mode 100644 index 0000000..ebb8ba3 --- /dev/null +++ b/gpxe/src/include/gpxe/io.h @@ -0,0 +1,504 @@ +#ifndef _GPXE_IO_H +#define _GPXE_IO_H + +/** @file + * + * gPXE I/O API + * + * The I/O API provides methods for reading from and writing to + * memory-mapped and I/O-mapped devices. + * + * The standard methods (readl()/writel() etc.) do not strictly check + * the type of the address parameter; this is because traditional + * usage does not necessarily provide the correct pointer type. For + * example, code written for ISA devices at fixed I/O addresses (such + * as the keyboard controller) tend to use plain integer constants for + * the address parameter. + */ + +#include +#include +#include +#include + +/** + * Calculate static inline I/O API function name + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @ret _subsys_func Subsystem API function + */ +#define IOAPI_INLINE( _subsys, _api_func ) \ + SINGLE_API_INLINE ( IOAPI_PREFIX_ ## _subsys, _api_func ) + +/** + * Provide an I/O API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @v _func Implementing function + */ +#define PROVIDE_IOAPI( _subsys, _api_func, _func ) \ + PROVIDE_SINGLE_API ( IOAPI_PREFIX_ ## _subsys, _api_func, _func ) + +/** + * Provide a static inline I/O API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + */ +#define PROVIDE_IOAPI_INLINE( _subsys, _api_func ) \ + PROVIDE_SINGLE_API_INLINE ( IOAPI_PREFIX_ ## _subsys, _api_func ) + +/* Include all architecture-independent I/O API headers */ +#include + +/* Include all architecture-dependent I/O API headers */ +#include + +/** + * Wrap an I/O read + * + * @v _func I/O API function + * @v _type Data type + * @v io_addr I/O address + * @v _prefix Prefix for address in debug message + * @v _ndigits Number of hex digits for this data type + */ +#define IOAPI_READ( _func, _type, io_addr, _prefix, _ndigits ) ( { \ + volatile _type *_io_addr = \ + ( ( volatile _type * ) ( intptr_t ) (io_addr) ); \ + _type _data = _func ( _io_addr ); \ + DBGIO ( "[" _prefix " %08lx] => %0" #_ndigits "llx\n", \ + io_to_bus ( _io_addr ), ( unsigned long long ) _data ); \ + _data; } ) + +/** + * Wrap an I/O write + * + * @v _func I/O API function + * @v _type Data type + * @v data Value to write + * @v io_addr I/O address + * @v _prefix Prefix for address in debug message + * @v _ndigits Number of hex digits for this data type + */ +#define IOAPI_WRITE( _func, _type, data, io_addr, _prefix, _ndigits ) do { \ + volatile _type *_io_addr = \ + ( ( volatile _type * ) ( intptr_t ) (io_addr) ); \ + _type _data = (data); \ + DBGIO ( "[" _prefix " %08lx] <= %0" #_ndigits "llx\n", \ + io_to_bus ( _io_addr ), ( unsigned long long ) _data ); \ + _func ( _data, _io_addr ); \ + } while ( 0 ) + +/** + * Wrap an I/O string read + * + * @v _func I/O API function + * @v _type Data type + * @v io_addr I/O address + * @v data Data buffer + * @v count Number of elements to read + * @v _prefix Prefix for address in debug message + * @v _ndigits Number of hex digits for this data type + */ +#define IOAPI_READS( _func, _type, io_addr, data, count, _prefix, _ndigits ) \ + do { \ + volatile _type *_io_addr = \ + ( ( volatile _type * ) ( intptr_t ) (io_addr) ); \ + void *_data_void = (data); /* Check data is a pointer */ \ + _type * _data = ( ( _type * ) _data_void ); \ + const _type * _dbg_data = _data; \ + unsigned int _count = (count); \ + unsigned int _dbg_count = _count; \ + _func ( _io_addr, _data, _count ); \ + DBGIO ( "[" _prefix " %08lx] =>", io_to_bus ( _io_addr ) ); \ + while ( _dbg_count-- ) { \ + DBGIO ( " %0" #_ndigits "llx", \ + ( ( unsigned long long ) *(_dbg_data++) ) ); \ + } \ + DBGIO ( "\n" ); \ + } while ( 0 ) + +/** + * Wrap an I/O string write + * + * @v _func I/O API function + * @v _type Data type + * @v io_addr I/O address + * @v data Data buffer + * @v count Number of elements to write + * @v _prefix Prefix for address in debug message + * @v _ndigits Number of hex digits for this data type + */ +#define IOAPI_WRITES( _func, _type, io_addr, data, count, _prefix, _ndigits ) \ + do { \ + volatile _type *_io_addr = \ + ( ( volatile _type * ) ( intptr_t ) (io_addr) ); \ + const void *_data_void = (data); /* Check data is a pointer */ \ + const _type * _data = ( ( const _type * ) _data_void ); \ + const _type * _dbg_data = _data; \ + unsigned int _count = (count); \ + unsigned int _dbg_count = _count; \ + DBGIO ( "[" _prefix " %08lx] <=", io_to_bus ( _io_addr ) ); \ + while ( _dbg_count-- ) { \ + DBGIO ( " %0" #_ndigits "llx", \ + ( ( unsigned long long ) *(_dbg_data++) ) ); \ + } \ + DBGIO ( "\n" ); \ + _func ( _io_addr, _data, _count ); \ + } while ( 0 ) + +/** + * Convert physical address to a bus address + * + * @v phys_addr Physical address + * @ret bus_addr Bus address + */ +unsigned long phys_to_bus ( unsigned long phys_addr ); + +/** + * Convert bus address to a physical address + * + * @v bus_addr Bus address + * @ret phys_addr Physical address + */ +unsigned long bus_to_phys ( unsigned long bus_addr ); + +/** + * Convert virtual address to a bus address + * + * @v addr Virtual address + * @ret bus_addr Bus address + */ +static inline __always_inline unsigned long +virt_to_bus ( volatile const void *addr ) { + return phys_to_bus ( virt_to_phys ( addr ) ); +} + +/** + * Convert bus address to a virtual address + * + * @v bus_addr Bus address + * @ret addr Virtual address + * + * This operation is not available under all memory models. + */ +static inline __always_inline void * bus_to_virt ( unsigned long bus_addr ) { + return phys_to_virt ( bus_to_phys ( bus_addr ) ); +} + +/** + * Map bus address as an I/O address + * + * @v bus_addr Bus address + * @v len Length of region + * @ret io_addr I/O address + */ +void * ioremap ( unsigned long bus_addr, size_t len ); + +/** + * Unmap I/O address + * + * @v io_addr I/O address + */ +void iounmap ( volatile const void *io_addr ); + +/** + * Convert I/O address to bus address (for debug only) + * + * @v io_addr I/O address + * @ret bus_addr Bus address + */ +unsigned long io_to_bus ( volatile const void *io_addr ); + +/** + * Read byte from memory-mapped device + * + * @v io_addr I/O address + * @ret data Value read + */ +uint8_t readb ( volatile uint8_t *io_addr ); +#define readb( io_addr ) IOAPI_READ ( readb, uint8_t, io_addr, "MEM", 2 ) + +/** + * Read 16-bit word from memory-mapped device + * + * @v io_addr I/O address + * @ret data Value read + */ +uint16_t readw ( volatile uint16_t *io_addr ); +#define readw( io_addr ) IOAPI_READ ( readw, uint16_t, io_addr, "MEM", 4 ) + +/** + * Read 32-bit dword from memory-mapped device + * + * @v io_addr I/O address + * @ret data Value read + */ +uint32_t readl ( volatile uint32_t *io_addr ); +#define readl( io_addr ) IOAPI_READ ( readl, uint32_t, io_addr, "MEM", 8 ) + +/** + * Read 64-bit qword from memory-mapped device + * + * @v io_addr I/O address + * @ret data Value read + */ +uint64_t readq ( volatile uint64_t *io_addr ); +#define readq( io_addr ) IOAPI_READ ( readq, uint64_t, io_addr, "MEM", 16 ) + +/** + * Write byte to memory-mapped device + * + * @v data Value to write + * @v io_addr I/O address + */ +void writeb ( uint8_t data, volatile uint8_t *io_addr ); +#define writeb( data, io_addr ) \ + IOAPI_WRITE ( writeb, uint8_t, data, io_addr, "MEM", 2 ) + +/** + * Write 16-bit word to memory-mapped device + * + * @v data Value to write + * @v io_addr I/O address + */ +void writew ( uint16_t data, volatile uint16_t *io_addr ); +#define writew( data, io_addr ) \ + IOAPI_WRITE ( writew, uint16_t, data, io_addr, "MEM", 4 ) + +/** + * Write 32-bit dword to memory-mapped device + * + * @v data Value to write + * @v io_addr I/O address + */ +void writel ( uint32_t data, volatile uint32_t *io_addr ); +#define writel( data, io_addr ) \ + IOAPI_WRITE ( writel, uint32_t, data, io_addr, "MEM", 8 ) + +/** + * Write 64-bit qword to memory-mapped device + * + * @v data Value to write + * @v io_addr I/O address + */ +void writeq ( uint64_t data, volatile uint64_t *io_addr ); +#define writeq( data, io_addr ) \ + IOAPI_WRITE ( writeq, uint64_t, data, io_addr, "MEM", 16 ) + +/** + * Read byte from I/O-mapped device + * + * @v io_addr I/O address + * @ret data Value read + */ +uint8_t inb ( volatile uint8_t *io_addr ); +#define inb( io_addr ) IOAPI_READ ( inb, uint8_t, io_addr, "IO", 2 ) + +/** + * Read 16-bit word from I/O-mapped device + * + * @v io_addr I/O address + * @ret data Value read + */ +uint16_t inw ( volatile uint16_t *io_addr ); +#define inw( io_addr ) IOAPI_READ ( inw, uint16_t, io_addr, "IO", 4 ) + +/** + * Read 32-bit dword from I/O-mapped device + * + * @v io_addr I/O address + * @ret data Value read + */ +uint32_t inl ( volatile uint32_t *io_addr ); +#define inl( io_addr ) IOAPI_READ ( inl, uint32_t, io_addr, "IO", 8 ) + +/** + * Write byte to I/O-mapped device + * + * @v data Value to write + * @v io_addr I/O address + */ +void outb ( uint8_t data, volatile uint8_t *io_addr ); +#define outb( data, io_addr ) \ + IOAPI_WRITE ( outb, uint8_t, data, io_addr, "IO", 2 ) + +/** + * Write 16-bit word to I/O-mapped device + * + * @v data Value to write + * @v io_addr I/O address + */ +void outw ( uint16_t data, volatile uint16_t *io_addr ); +#define outw( data, io_addr ) \ + IOAPI_WRITE ( outw, uint16_t, data, io_addr, "IO", 4 ) + +/** + * Write 32-bit dword to I/O-mapped device + * + * @v data Value to write + * @v io_addr I/O address + */ +void outl ( uint32_t data, volatile uint32_t *io_addr ); +#define outl( data, io_addr ) \ + IOAPI_WRITE ( outl, uint32_t, data, io_addr, "IO", 8 ) + +/** + * Read bytes from I/O-mapped device + * + * @v io_addr I/O address + * @v data Data buffer + * @v count Number of bytes to read + */ +void insb ( volatile uint8_t *io_addr, uint8_t *data, unsigned int count ); +#define insb( io_addr, data, count ) \ + IOAPI_READS ( insb, uint8_t, io_addr, data, count, "IO", 2 ) + +/** + * Read 16-bit words from I/O-mapped device + * + * @v io_addr I/O address + * @v data Data buffer + * @v count Number of words to read + */ +void insw ( volatile uint16_t *io_addr, uint16_t *data, unsigned int count ); +#define insw( io_addr, data, count ) \ + IOAPI_READS ( insw, uint16_t, io_addr, data, count, "IO", 4 ) + +/** + * Read 32-bit words from I/O-mapped device + * + * @v io_addr I/O address + * @v data Data buffer + * @v count Number of words to read + */ +void insl ( volatile uint32_t *io_addr, uint32_t *data, unsigned int count ); +#define insl( io_addr, data, count ) \ + IOAPI_READS ( insl, uint32_t, io_addr, data, count, "IO", 8 ) + +/** + * Write bytes to I/O-mapped device + * + * @v io_addr I/O address + * @v data Data buffer + * @v count Number of bytes to write + */ +void outsb ( volatile uint8_t *io_addr, const uint8_t *data, + unsigned int count ); +#define outsb( io_addr, data, count ) \ + IOAPI_WRITES ( outsb, uint8_t, io_addr, data, count, "IO", 2 ) + +/** + * Write 16-bit words to I/O-mapped device + * + * @v io_addr I/O address + * @v data Data buffer + * @v count Number of words to write + */ +void outsw ( volatile uint16_t *io_addr, const uint16_t *data, + unsigned int count ); +#define outsw( io_addr, data, count ) \ + IOAPI_WRITES ( outsw, uint16_t, io_addr, data, count, "IO", 4 ) + +/** + * Write 32-bit words to I/O-mapped device + * + * @v io_addr I/O address + * @v data Data buffer + * @v count Number of words to write + */ +void outsl ( volatile uint32_t *io_addr, const uint32_t *data, + unsigned int count ); +#define outsl( io_addr, data, count ) \ + IOAPI_WRITES ( outsl, uint32_t, io_addr, data, count, "IO", 8 ) + +/** + * Slow down I/O + * + */ +void iodelay ( void ); + +/** + * Read value from I/O-mapped device, slowly + * + * @v _func Function to use to read value + * @v data Value to write + * @v io_addr I/O address + */ +#define INX_P( _func, _type, io_addr ) ( { \ + _type _data = _func ( (io_addr) ); \ + iodelay(); \ + _data; } ) + +/** + * Read byte from I/O-mapped device + * + * @v io_addr I/O address + * @ret data Value read + */ +#define inb_p( io_addr ) INX_P ( inb, uint8_t, io_addr ) + +/** + * Read 16-bit word from I/O-mapped device + * + * @v io_addr I/O address + * @ret data Value read + */ +#define inw_p( io_addr ) INX_P ( inw, uint16_t, io_addr ) + +/** + * Read 32-bit dword from I/O-mapped device + * + * @v io_addr I/O address + * @ret data Value read + */ +#define inl_p( io_addr ) INX_P ( inl, uint32_t, io_addr ) + +/** + * Write value to I/O-mapped device, slowly + * + * @v _func Function to use to write value + * @v data Value to write + * @v io_addr I/O address + */ +#define OUTX_P( _func, data, io_addr ) do { \ + _func ( (data), (io_addr) ); \ + iodelay(); \ + } while ( 0 ) + +/** + * Write byte to I/O-mapped device, slowly + * + * @v data Value to write + * @v io_addr I/O address + */ +#define outb_p( data, io_addr ) OUTX_P ( outb, data, io_addr ) + +/** + * Write 16-bit word to I/O-mapped device, slowly + * + * @v data Value to write + * @v io_addr I/O address + */ +#define outw_p( data, io_addr ) OUTX_P ( outw, data, io_addr ) + +/** + * Write 32-bit dword to I/O-mapped device, slowly + * + * @v data Value to write + * @v io_addr I/O address + */ +#define outl_p( data, io_addr ) OUTX_P ( outl, data, io_addr ) + +/** + * Memory barrier + * + */ +void mb ( void ); +#define rmb() mb() +#define wmb() mb() + +#endif /* _GPXE_IO_H */ diff --git a/gpxe/src/include/gpxe/iobuf.h b/gpxe/src/include/gpxe/iobuf.h new file mode 100644 index 0000000..6d1a58a --- /dev/null +++ b/gpxe/src/include/gpxe/iobuf.h @@ -0,0 +1,227 @@ +#ifndef _GPXE_IOBUF_H +#define _GPXE_IOBUF_H + +/** @file + * + * I/O buffers + * + */ + +#include +#include +#include + +/** + * I/O buffer alignment + * + * I/O buffers allocated via alloc_iob() are guaranteed to be + * physically aligned to this boundary. Some cards cannot DMA across + * a 4kB boundary. With a standard Ethernet MTU, aligning to a 2kB + * boundary is sufficient to guarantee no 4kB boundary crossings. For + * a jumbo Ethernet MTU, a packet may be larger than 4kB anyway. + */ +#define IOB_ALIGN 2048 + +/** + * Minimum I/O buffer length + * + * alloc_iob() will round up the allocated length to this size if + * necessary. This is used on behalf of hardware that is not capable + * of auto-padding. + */ +#define IOB_ZLEN 64 + +/** + * A persistent I/O buffer + * + * This data structure encapsulates a long-lived I/O buffer. The + * buffer may be passed between multiple owners, queued for possible + * retransmission, etc. + */ +struct io_buffer { + /** List of which this buffer is a member + * + * The list must belong to the current owner of the buffer. + * Different owners may maintain different lists (e.g. a + * retransmission list for TCP). + */ + struct list_head list; + + /** Start of the buffer */ + void *head; + /** Start of data */ + void *data; + /** End of data */ + void *tail; + /** End of the buffer */ + void *end; +}; + +/** + * Reserve space at start of I/O buffer + * + * @v iobuf I/O buffer + * @v len Length to reserve + * @ret data Pointer to new start of buffer + */ +static inline void * iob_reserve ( struct io_buffer *iobuf, size_t len ) { + iobuf->data += len; + iobuf->tail += len; + return iobuf->data; +} +#define iob_reserve( iobuf, len ) ( { \ + void *__result; \ + __result = iob_reserve ( (iobuf), (len) ); \ + assert ( (iobuf)->tail <= (iobuf)->end ); \ + __result; } ) + +/** + * Add data to start of I/O buffer + * + * @v iobuf I/O buffer + * @v len Length to add + * @ret data Pointer to new start of buffer + */ +static inline void * iob_push ( struct io_buffer *iobuf, size_t len ) { + iobuf->data -= len; + return iobuf->data; +} +#define iob_push( iobuf, len ) ( { \ + void *__result; \ + __result = iob_push ( (iobuf), (len) ); \ + assert ( (iobuf)->data >= (iobuf)->head ); \ + __result; } ) + +/** + * Remove data from start of I/O buffer + * + * @v iobuf I/O buffer + * @v len Length to remove + * @ret data Pointer to new start of buffer + */ +static inline void * iob_pull ( struct io_buffer *iobuf, size_t len ) { + iobuf->data += len; + assert ( iobuf->data <= iobuf->tail ); + return iobuf->data; +} +#define iob_pull( iobuf, len ) ( { \ + void *__result; \ + __result = iob_pull ( (iobuf), (len) ); \ + assert ( (iobuf)->data <= (iobuf)->tail ); \ + __result; } ) + +/** + * Add data to end of I/O buffer + * + * @v iobuf I/O buffer + * @v len Length to add + * @ret data Pointer to newly added space + */ +static inline void * iob_put ( struct io_buffer *iobuf, size_t len ) { + void *old_tail = iobuf->tail; + iobuf->tail += len; + return old_tail; +} +#define iob_put( iobuf, len ) ( { \ + void *__result; \ + __result = iob_put ( (iobuf), (len) ); \ + assert ( (iobuf)->tail <= (iobuf)->end ); \ + __result; } ) + +/** + * Remove data from end of I/O buffer + * + * @v iobuf I/O buffer + * @v len Length to remove + */ +static inline void iob_unput ( struct io_buffer *iobuf, size_t len ) { + iobuf->tail -= len; +} +#define iob_unput( iobuf, len ) do { \ + iob_unput ( (iobuf), (len) ); \ + assert ( (iobuf)->tail >= (iobuf)->data ); \ + } while ( 0 ) + +/** + * Empty an I/O buffer + * + * @v iobuf I/O buffer + */ +static inline void iob_empty ( struct io_buffer *iobuf ) { + iobuf->tail = iobuf->data; +} + +/** + * Calculate length of data in an I/O buffer + * + * @v iobuf I/O buffer + * @ret len Length of data in buffer + */ +static inline size_t iob_len ( struct io_buffer *iobuf ) { + return ( iobuf->tail - iobuf->data ); +} + +/** + * Calculate available space at start of an I/O buffer + * + * @v iobuf I/O buffer + * @ret len Length of data available at start of buffer + */ +static inline size_t iob_headroom ( struct io_buffer *iobuf ) { + return ( iobuf->data - iobuf->head ); +} + +/** + * Calculate available space at end of an I/O buffer + * + * @v iobuf I/O buffer + * @ret len Length of data available at end of buffer + */ +static inline size_t iob_tailroom ( struct io_buffer *iobuf ) { + return ( iobuf->end - iobuf->tail ); +} + +/** + * Create a temporary I/O buffer + * + * @v iobuf I/O buffer + * @v data Data buffer + * @v len Length of data + * @v max_len Length of buffer + * + * It is sometimes useful to use the iob_xxx() methods on temporary + * data buffers. + */ +static inline void iob_populate ( struct io_buffer *iobuf, + void *data, size_t len, size_t max_len ) { + iobuf->head = iobuf->data = data; + iobuf->tail = ( data + len ); + iobuf->end = ( data + max_len ); +} + +/** + * Disown an I/O buffer + * + * @v iobuf I/O buffer + * + * There are many functions that take ownership of the I/O buffer they + * are passed as a parameter. The caller should not retain a pointer + * to the I/O buffer. Use iob_disown() to automatically nullify the + * caller's pointer, e.g.: + * + * xfer_deliver_iob ( xfer, iob_disown ( iobuf ) ); + * + * This will ensure that iobuf is set to NULL for any code after the + * call to xfer_deliver_iob(). + */ +#define iob_disown( iobuf ) ( { \ + struct io_buffer *__iobuf = (iobuf); \ + (iobuf) = NULL; \ + __iobuf; } ) + +extern struct io_buffer * __malloc alloc_iob ( size_t len ); +extern void free_iob ( struct io_buffer *iobuf ); +extern void iob_pad ( struct io_buffer *iobuf, size_t min_len ); +extern int iob_ensure_headroom ( struct io_buffer *iobuf, size_t len ); + +#endif /* _GPXE_IOBUF_H */ diff --git a/gpxe/src/include/gpxe/ip.h b/gpxe/src/include/gpxe/ip.h new file mode 100644 index 0000000..b1b0c48 --- /dev/null +++ b/gpxe/src/include/gpxe/ip.h @@ -0,0 +1,95 @@ +#ifndef _GPXE_IP_H +#define _GPXE_IP_H + +/** @file + * + * IP protocol + * + */ + +#include +#include +#include +#include + +struct io_buffer; +struct net_device; +struct net_protocol; + +/* IP constants */ + +#define IP_VER 0x40U +#define IP_MASK_VER 0xf0U +#define IP_MASK_HLEN 0x0fU +#define IP_MASK_OFFSET 0x1fffU +#define IP_MASK_DONOTFRAG 0x4000U +#define IP_MASK_MOREFRAGS 0x2000U +#define IP_PSHLEN 12 + +/* IP header defaults */ +#define IP_TOS 0 +#define IP_TTL 64 + +#define IP_FRAG_IOB_SIZE 1500 +#define IP_FRAG_TIMEOUT 50 + +/** An IPv4 packet header */ +struct iphdr { + uint8_t verhdrlen; + uint8_t service; + uint16_t len; + uint16_t ident; + uint16_t frags; + uint8_t ttl; + uint8_t protocol; + uint16_t chksum; + struct in_addr src; + struct in_addr dest; +} __attribute__ (( packed )); + +/** An IPv4 pseudo header */ +struct ipv4_pseudo_header { + struct in_addr src; + struct in_addr dest; + uint8_t zero_padding; + uint8_t protocol; + uint16_t len; +}; + +/** An IPv4 address/routing table entry */ +struct ipv4_miniroute { + /** List of miniroutes */ + struct list_head list; + + /** Network device */ + struct net_device *netdev; + + /** IPv4 address */ + struct in_addr address; + /** Subnet mask */ + struct in_addr netmask; + /** Gateway address */ + struct in_addr gateway; +}; + +/* Fragment reassembly buffer */ +struct frag_buffer { + /* Identification number */ + uint16_t ident; + /* Source network address */ + struct in_addr src; + /* Destination network address */ + struct in_addr dest; + /* Reassembled I/O buffer */ + struct io_buffer *frag_iob; + /* Reassembly timer */ + struct retry_timer frag_timer; + /* List of fragment reassembly buffers */ + struct list_head list; +}; + +extern struct list_head ipv4_miniroutes; + +extern struct net_protocol ipv4_protocol; + +#endif /* _GPXE_IP_H */ diff --git a/gpxe/src/include/gpxe/ip6.h b/gpxe/src/include/gpxe/ip6.h new file mode 100644 index 0000000..dc0ae31 --- /dev/null +++ b/gpxe/src/include/gpxe/ip6.h @@ -0,0 +1,78 @@ +#ifndef _GPXE_IP6_H +#define _GPXE_IP6_H + +/** @file + * + * IP6 protocol + * + */ + +#include +#include + +/* IP6 constants */ + +#define IP6_VERSION 0x6 +#define IP6_HOP_LIMIT 255 + +/** + * I/O buffer contents + * This is duplicated in tcp.h and here. Ideally it should go into iobuf.h + */ +#define MAX_HDR_LEN 100 +#define MAX_IOB_LEN 1500 +#define MIN_IOB_LEN MAX_HDR_LEN + 100 /* To account for padding by LL */ + +#define IP6_EQUAL( in6_addr1, in6_addr2 ) \ + ( memcmp ( ( char* ) &( in6_addr1 ), ( char* ) &( in6_addr2 ),\ + sizeof ( struct in6_addr ) ) == 0 ) + +#define IS_UNSPECIFIED( addr ) \ + ( ( (addr).in6_u.u6_addr32[0] == 0x00000000 ) && \ + ( (addr).in6_u.u6_addr32[1] == 0x00000000 ) && \ + ( (addr).in6_u.u6_addr32[2] == 0x00000000 ) && \ + ( (addr).in6_u.u6_addr32[3] == 0x00000000 ) ) +/* IP6 header */ +struct ip6_header { + uint32_t ver_traffic_class_flow_label; + uint16_t payload_len; + uint8_t nxt_hdr; + uint8_t hop_limit; + struct in6_addr src; + struct in6_addr dest; +}; + +/* IP6 pseudo header */ +struct ipv6_pseudo_header { + struct in6_addr src; + struct in6_addr dest; + uint8_t zero_padding; + uint8_t nxt_hdr; + uint16_t len; +}; + +/* Next header numbers */ +#define IP6_HOPBYHOP 0x00 +#define IP6_ROUTING 0x43 +#define IP6_FRAGMENT 0x44 +#define IP6_AUTHENTICATION 0x51 +#define IP6_DEST_OPTS 0x60 +#define IP6_ESP 0x50 +#define IP6_ICMP6 0x58 +#define IP6_NO_HEADER 0x59 + +struct io_buffer; +struct net_device; +struct net_protocol; + +extern struct net_protocol ipv6_protocol; +extern struct tcpip_net_protocol ipv6_tcpip_protocol; +extern char * inet6_ntoa ( struct in6_addr in6 ); + +extern int add_ipv6_address ( struct net_device *netdev, + struct in6_addr prefix, int prefix_len, + struct in6_addr address, + struct in6_addr gateway ); +extern void del_ipv6_address ( struct net_device *netdev ); + +#endif /* _GPXE_IP6_H */ diff --git a/gpxe/src/include/gpxe/ipoib.h b/gpxe/src/include/gpxe/ipoib.h new file mode 100644 index 0000000..80adee5 --- /dev/null +++ b/gpxe/src/include/gpxe/ipoib.h @@ -0,0 +1,80 @@ +#ifndef _GPXE_IPOIB_H +#define _GPXE_IPOIB_H + +/** @file + * + * IP over Infiniband + */ + +#include + +/** IPoIB packet length */ +#define IPOIB_PKT_LEN 2048 + +/** IPoIB MAC address length */ +#define IPOIB_ALEN 20 + +/** An IPoIB MAC address */ +struct ipoib_mac { + /** Queue pair number + * + * MSB must be zero; QPNs are only 24-bit. + */ + uint32_t qpn; + /** Port GID */ + struct ib_gid gid; +} __attribute__ (( packed )); + +/** IPoIB link-layer header length */ +#define IPOIB_HLEN 4 + +/** IPoIB link-layer header */ +struct ipoib_hdr { + /** Network-layer protocol */ + uint16_t proto; + /** Reserved, must be zero */ + union { + /** Reserved, must be zero */ + uint16_t reserved; + /** Peer addresses + * + * We use these fields internally to represent the + * peer addresses using a lookup key. There simply + * isn't enough room in the IPoIB header to store + * literal source or destination MAC addresses. + */ + struct { + /** Destination address key */ + uint8_t dest; + /** Source address key */ + uint8_t src; + } __attribute__ (( packed )) peer; + } __attribute__ (( packed )) u; +} __attribute__ (( packed )); + +extern struct ll_protocol ipoib_protocol; + +extern const char * ipoib_ntoa ( const void *ll_addr ); + +/** + * Allocate IPoIB device + * + * @v priv_size Size of driver private data + * @ret netdev Network device, or NULL + */ +static inline struct net_device * alloc_ipoibdev ( size_t priv_size ) { + struct net_device *netdev; + + netdev = alloc_netdev ( priv_size ); + if ( netdev ) { + netdev->ll_protocol = &ipoib_protocol; + netdev->max_pkt_len = IPOIB_PKT_LEN; + } + return netdev; +} + +extern void ipoib_link_state_changed ( struct ib_device *ibdev ); +extern int ipoib_probe ( struct ib_device *ibdev ); +extern void ipoib_remove ( struct ib_device *ibdev ); + +#endif /* _GPXE_IPOIB_H */ diff --git a/gpxe/src/include/gpxe/isa.h b/gpxe/src/include/gpxe/isa.h new file mode 100644 index 0000000..bb25dbc --- /dev/null +++ b/gpxe/src/include/gpxe/isa.h @@ -0,0 +1,92 @@ +#ifndef ISA_H +#define ISA_H + +#include +#include +#include +#include + +/** An ISA device */ +struct isa_device { + /** Generic device */ + struct device dev; + /** I/O address */ + uint16_t ioaddr; + /** Driver for this device */ + struct isa_driver *driver; + /** Driver-private data + * + * Use isa_set_drvdata() and isa_get_drvdata() to access + * this field. + */ + void *priv; + /** Driver name */ + const char *driver_name; +}; + +/* + * An individual ISA device, identified by probe address + * + */ +typedef uint16_t isa_probe_addr_t; + +/** An ISA driver */ +struct isa_driver { + /** Name */ + const char *name; + /** Probe address list */ + isa_probe_addr_t *probe_addrs; + /** Number of entries in probe address list */ + unsigned int addr_count; + /** Manufacturer ID to be assumed for this device */ + uint16_t vendor_id; + /** Product ID to be assumed for this device */ + uint16_t prod_id; + /** + * Probe device + * + * @v isa ISA device + * @v id Matching entry in ID table + * @ret rc Return status code + */ + int ( * probe ) ( struct isa_device *isa ); + /** + * Remove device + * + * @v isa ISA device + */ + void ( * remove ) ( struct isa_device *isa ); +}; + +/** Declare an ISA driver */ +#define __isa_driver __table ( struct isa_driver, isa_drivers, 01 ) + +/** + * Set ISA driver-private data + * + * @v isa ISA device + * @v priv Private data + */ +static inline void isa_set_drvdata ( struct isa_device *isa, void *priv ) { + isa->priv = priv; +} + +/** + * Get ISA driver-private data + * + * @v isa ISA device + * @ret priv Private data + */ +static inline void * isa_get_drvdata ( struct isa_device *isa ) { + return isa->priv; +} + +/* + * ISA_ROM is parsed by parserom.pl to generate Makefile rules and + * files for rom-o-matic. + * + */ +#define ISA_ROM( IMAGE, DESCRIPTION ) + +#endif /* ISA_H */ + diff --git a/gpxe/src/include/gpxe/isa_ids.h b/gpxe/src/include/gpxe/isa_ids.h new file mode 100644 index 0000000..bf3f101 --- /dev/null +++ b/gpxe/src/include/gpxe/isa_ids.h @@ -0,0 +1,49 @@ +#ifndef ISA_IDS_H +#define ISA_IDS_H + +/* + * This file defines IDs as used by ISAPnP and EISA devices. These + * IDs have the format: + * + * vendor byte 0 bit 7 must be zero + * bits 6-2 first vendor char in compressed ASCII + * bits 1-0 second vendor char in compressed ASCII (bits 4-3) + * byte 1 bits 7-5 second vendor char in compressed ASCII (bits 2-0) + * bits 4-0 third vendor char in compressed ASCII + * product byte 0 bits 7-4 first hex digit of product number + * bits 3-0 second hex digit of product number + * byte 1 bits 7-4 third hex digit of product number + * bits 3-0 hex digit of revision level + * + * ISA IDs are always expressed in little-endian order, even though + * the underlying "meaning" is big-endian. + */ + +#include + +/* + * Construct a vendor ID from three ASCII characters + * + */ +#define ISA_VENDOR( a, b, c ) \ + bswap_16 ( ( ( ( (a) - 'A' + 1 ) & 0x1f ) << 10 ) | \ + ( ( ( (b) - 'A' + 1 ) & 0x1f ) << 5 ) | \ + ( ( ( (c) - 'A' + 1 ) & 0x1f ) << 0 ) ) + +#define ISAPNP_VENDOR( a, b, c ) ISA_VENDOR ( a, b, c ) +#define EISA_VENDOR( a, b, c ) ISA_VENDOR ( a, b, c ) + +#define GENERIC_ISAPNP_VENDOR ISAPNP_VENDOR ( 'P','N','P' ) + +/* + * Extract product ID and revision from combined product field + * + */ +#define ISA_PROD_ID_MASK ( 0xf0ff ) +#define ISA_PROD_ID(product) ( (product) & ISA_PROD_ID_MASK ) +#define ISA_PROD_REV(product) ( ( (product) & ~ISA_PROD_ID_MASK ) >> 8 ) + +/* Functions in isa_ids.c */ +extern char * isa_id_string ( unsigned int vendor, unsigned int product ); + +#endif /* ISA_IDS_H */ diff --git a/gpxe/src/include/gpxe/isapnp.h b/gpxe/src/include/gpxe/isapnp.h new file mode 100644 index 0000000..07797a9 --- /dev/null +++ b/gpxe/src/include/gpxe/isapnp.h @@ -0,0 +1,273 @@ +/************************************************************************** +* +* isapnp.h -- Etherboot isapnp support for the 3Com 3c515 +* Written 2002-2003 by Timothy Legge +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Portions of this code: +* Copyright (C) 2001 P.J.H.Fox (fox@roestock.demon.co.uk) +* +* +* +* REVISION HISTORY: +* ================ +* Version 0.1 April 26, 2002 TJL +* Version 0.2 01/08/2003 TJL Renamed from 3c515_isapnp.h +* +* +* Generalised into an ISAPnP bus that can be used by more than just +* the 3c515 by Michael Brown +* +***************************************************************************/ + +#ifndef ISAPNP_H +#define ISAPNP_H + +#include +#include +#include +#include + +/* + * ISAPnP constants + * + */ + +/* Port addresses */ +#define ISAPNP_ADDRESS 0x279 +#define ISAPNP_WRITE_DATA 0xa79 +#define ISAPNP_READ_PORT_MIN 0x203 +#define ISAPNP_READ_PORT_START 0x213 /* ISAPnP spec says 0x203, but + * Linux ISAPnP starts at + * 0x213 with no explanatory + * comment. 0x203 probably + * clashes with something. */ +#define ISAPNP_READ_PORT_MAX 0x3ff +#define ISAPNP_READ_PORT_STEP 0x10 /* Can be any multiple of 4 + * according to the spec, but + * since ISA I/O addresses are + * allocated in blocks of 16, + * it makes no sense to use + * any value less than 16. + */ + +/* Card select numbers */ +#define ISAPNP_CSN_MIN 0x01 +#define ISAPNP_CSN_MAX 0x0f + +/* Registers */ +#define ISAPNP_READPORT 0x00 +#define ISAPNP_SERIALISOLATION 0x01 +#define ISAPNP_CONFIGCONTROL 0x02 +#define ISAPNP_WAKE 0x03 +#define ISAPNP_RESOURCEDATA 0x04 +#define ISAPNP_STATUS 0x05 +#define ISAPNP_CARDSELECTNUMBER 0x06 +#define ISAPNP_LOGICALDEVICENUMBER 0x07 +#define ISAPNP_ACTIVATE 0x30 +#define ISAPNP_IORANGECHECK 0x31 +#define ISAPNP_IOBASE(n) ( 0x60 + ( (n) * 2 ) ) +#define ISAPNP_IRQNO(n) ( 0x70 + ( (n) * 2 ) ) +#define ISAPNP_IRQTYPE(n) ( 0x71 + ( (n) * 2 ) ) + +/* Bits in the CONFIGCONTROL register */ +#define ISAPNP_CONFIG_RESET ( 1 << 0 ) +#define ISAPNP_CONFIG_WAIT_FOR_KEY ( 1 << 1 ) +#define ISAPNP_CONFIG_RESET_CSN ( 1 << 2 ) +#define ISAPNP_CONFIG_RESET_DRV ( ISAPNP_CONFIG_RESET | \ + ISAPNP_CONFIG_WAIT_FOR_KEY | \ + ISAPNP_CONFIG_RESET_CSN ) + +/* The LFSR used for the initiation key and for checksumming */ +#define ISAPNP_LFSR_SEED 0x6a + +/* Small tags */ +#define ISAPNP_IS_SMALL_TAG(tag) ( ! ( (tag) & 0x80 ) ) +#define ISAPNP_SMALL_TAG_NAME(tag) ( ( (tag) >> 3 ) & 0xf ) +#define ISAPNP_SMALL_TAG_LEN(tag) ( ( (tag) & 0x7 ) ) +#define ISAPNP_TAG_PNPVERNO 0x01 +#define ISAPNP_TAG_LOGDEVID 0x02 +#define ISAPNP_TAG_COMPATDEVID 0x03 +#define ISAPNP_TAG_IRQ 0x04 +#define ISAPNP_TAG_DMA 0x05 +#define ISAPNP_TAG_STARTDEP 0x06 +#define ISAPNP_TAG_ENDDEP 0x07 +#define ISAPNP_TAG_IOPORT 0x08 +#define ISAPNP_TAG_FIXEDIO 0x09 +#define ISAPNP_TAG_RSVDSHORTA 0x0A +#define ISAPNP_TAG_RSVDSHORTB 0x0B +#define ISAPNP_TAG_RSVDSHORTC 0x0C +#define ISAPNP_TAG_RSVDSHORTD 0x0D +#define ISAPNP_TAG_VENDORSHORT 0x0E +#define ISAPNP_TAG_END 0x0F +/* Large tags */ +#define ISAPNP_IS_LARGE_TAG(tag) ( ( (tag) & 0x80 ) ) +#define ISAPNP_LARGE_TAG_NAME(tag) (tag) +#define ISAPNP_TAG_MEMRANGE 0x81 +#define ISAPNP_TAG_ANSISTR 0x82 +#define ISAPNP_TAG_UNICODESTR 0x83 +#define ISAPNP_TAG_VENDORLONG 0x84 +#define ISAPNP_TAG_MEM32RANGE 0x85 +#define ISAPNP_TAG_FIXEDMEM32RANGE 0x86 +#define ISAPNP_TAG_RSVDLONG0 0xF0 +#define ISAPNP_TAG_RSVDLONG1 0xF1 +#define ISAPNP_TAG_RSVDLONG2 0xF2 +#define ISAPNP_TAG_RSVDLONG3 0xF3 +#define ISAPNP_TAG_RSVDLONG4 0xF4 +#define ISAPNP_TAG_RSVDLONG5 0xF5 +#define ISAPNP_TAG_RSVDLONG6 0xF6 +#define ISAPNP_TAG_RSVDLONG7 0xF7 +#define ISAPNP_TAG_RSVDLONG8 0xF8 +#define ISAPNP_TAG_RSVDLONG9 0xF9 +#define ISAPNP_TAG_RSVDLONGA 0xFA +#define ISAPNP_TAG_RSVDLONGB 0xFB +#define ISAPNP_TAG_RSVDLONGC 0xFC +#define ISAPNP_TAG_RSVDLONGD 0xFD +#define ISAPNP_TAG_RSVDLONGE 0xFE +#define ISAPNP_TAG_RSVDLONGF 0xFF +#define ISAPNP_TAG_PSEUDO_NEWBOARD 0x100 + +/** An ISAPnP serial identifier */ +struct isapnp_identifier { + /** Vendor ID */ + uint16_t vendor_id; + /** Product ID */ + uint16_t prod_id; + /** Serial number */ + uint32_t serial; + /** Checksum */ + uint8_t checksum; +} __attribute__ (( packed )); + +/** An ISAPnP logical device ID structure */ +struct isapnp_logdevid { + /** Vendor ID */ + uint16_t vendor_id; + /** Product ID */ + uint16_t prod_id; + /** Flags */ + uint16_t flags; +} __attribute__ (( packed )); + +/** An ISAPnP device ID list entry */ +struct isapnp_device_id { + /** Name */ + const char *name; + /** Vendor ID */ + uint16_t vendor_id; + /** Product ID */ + uint16_t prod_id; +}; + +/** An ISAPnP device */ +struct isapnp_device { + /** Generic device */ + struct device dev; + /** Vendor ID */ + uint16_t vendor_id; + /** Product ID */ + uint16_t prod_id; + /** I/O address */ + uint16_t ioaddr; + /** Interrupt number */ + uint8_t irqno; + /** Card Select Number */ + uint8_t csn; + /** Logical Device ID */ + uint8_t logdev; + /** Driver for this device */ + struct isapnp_driver *driver; + /** Driver-private data + * + * Use isapnp_set_drvdata() and isapnp_get_drvdata() to access + * this field. + */ + void *priv; + /** Driver name */ + const char *driver_name; +}; + +/** An ISAPnP driver */ +struct isapnp_driver { + /** ISAPnP ID table */ + struct isapnp_device_id *ids; + /** Number of entries in ISAPnP ID table */ + unsigned int id_count; + /** + * Probe device + * + * @v isapnp ISAPnP device + * @v id Matching entry in ID table + * @ret rc Return status code + */ + int ( * probe ) ( struct isapnp_device *isapnp, + const struct isapnp_device_id *id ); + /** + * Remove device + * + * @v isapnp ISAPnP device + */ + void ( * remove ) ( struct isapnp_device *isapnp ); +}; + +/** Declare an ISAPnP driver */ +#define __isapnp_driver __table ( struct isapnp_driver, isapnp_drivers, 01 ) + +extern uint16_t isapnp_read_port; + +extern void isapnp_device_activation ( struct isapnp_device *isapnp, + int activation ); + +/** + * Activate ISAPnP device + * + * @v isapnp ISAPnP device + */ +static inline void activate_isapnp_device ( struct isapnp_device *isapnp ) { + isapnp_device_activation ( isapnp, 1 ); +} + +/** + * Deactivate ISAPnP device + * + * @v isapnp ISAPnP device + */ +static inline void deactivate_isapnp_device ( struct isapnp_device *isapnp ) { + isapnp_device_activation ( isapnp, 0 ); +} + +/** + * Set ISAPnP driver-private data + * + * @v isapnp ISAPnP device + * @v priv Private data + */ +static inline void isapnp_set_drvdata ( struct isapnp_device *isapnp, + void *priv ) { + isapnp->priv = priv; +} + +/** + * Get ISAPnP driver-private data + * + * @v isapnp ISAPnP device + * @ret priv Private data + */ +static inline void * isapnp_get_drvdata ( struct isapnp_device *isapnp ) { + return isapnp->priv; +} + +#endif /* ISAPNP_H */ diff --git a/gpxe/src/include/gpxe/iscsi.h b/gpxe/src/include/gpxe/iscsi.h new file mode 100644 index 0000000..0510974 --- /dev/null +++ b/gpxe/src/include/gpxe/iscsi.h @@ -0,0 +1,681 @@ +#ifndef _GPXE_ISCSI_H +#define _GPXE_ISCSI_H + +/** @file + * + * iSCSI protocol + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/** Default iSCSI port */ +#define ISCSI_PORT 3260 + +/** + * iSCSI segment lengths + * + * iSCSI uses an icky structure with one one-byte field (a dword + * count) and one three-byte field (a byte count). This structure, + * and the accompanying macros, relieve some of the pain. + */ +union iscsi_segment_lengths { + struct { + /** The AHS length (measured in dwords) */ + uint8_t ahs_len; + /** The data length (measured in bytes), in network + * byte order + */ + uint8_t data_len[3]; + } bytes; + /** Ths data length (measured in bytes), in network byte + * order, with ahs_len as the first byte. + */ + uint32_t ahs_and_data_len; +}; + +/** The length of the additional header segment, in dwords */ +#define ISCSI_AHS_LEN( segment_lengths ) \ + ( (segment_lengths).bytes.ahs_len ) + +/** The length of the data segment, in bytes, excluding any padding */ +#define ISCSI_DATA_LEN( segment_lengths ) \ + ( ntohl ( (segment_lengths).ahs_and_data_len ) & 0xffffff ) + +/** The padding of the data segment, in bytes */ +#define ISCSI_DATA_PAD_LEN( segment_lengths ) \ + ( ( 0 - (segment_lengths).bytes.data_len[2] ) & 0x03 ) + +/** Set additional header and data segment lengths */ +#define ISCSI_SET_LENGTHS( segment_lengths, ahs_len, data_len ) do { \ + (segment_lengths).ahs_and_data_len = \ + htonl ( data_len | ( ahs_len << 24 ) ); \ + } while ( 0 ) + +/** + * iSCSI basic header segment common fields + * + */ +struct iscsi_bhs_common { + /** Opcode */ + uint8_t opcode; + /** Flags */ + uint8_t flags; + /** Fields specific to the PDU type */ + uint8_t other_a[2]; + /** Segment lengths */ + union iscsi_segment_lengths lengths; + /** Fields specific to the PDU type */ + uint8_t other_b[8]; + /** Initiator Task Tag */ + uint32_t itt; + /** Fields specific to the PDU type */ + uint8_t other_c[28]; +}; + +/** Opcode mask */ +#define ISCSI_OPCODE_MASK 0x3f + +/** Immediate delivery */ +#define ISCSI_FLAG_IMMEDIATE 0x40 + +/** Final PDU of a sequence */ +#define ISCSI_FLAG_FINAL 0x80 + +/** + * iSCSI basic header segment common request fields + * + */ +struct iscsi_bhs_common_response { + /** Opcode */ + uint8_t opcode; + /** Flags */ + uint8_t flags; + /** Fields specific to the PDU type */ + uint8_t other_a[2]; + /** Segment lengths */ + union iscsi_segment_lengths lengths; + /** Fields specific to the PDU type */ + uint8_t other_b[8]; + /** Initiator Task Tag */ + uint32_t itt; + /** Fields specific to the PDU type */ + uint8_t other_c[4]; + /** Status sequence number */ + uint32_t statsn; + /** Expected command sequence number */ + uint32_t expcmdsn; + /** Fields specific to the PDU type */ + uint8_t other_d[16]; +}; + +/** + * iSCSI login request basic header segment + * + */ +struct iscsi_bhs_login_request { + /** Opcode */ + uint8_t opcode; + /** Flags */ + uint8_t flags; + /** Maximum supported version number */ + uint8_t version_max; + /** Minimum supported version number */ + uint8_t version_min; + /** Segment lengths */ + union iscsi_segment_lengths lengths; + /** Initiator session ID (IANA format) enterprise number and flags */ + uint32_t isid_iana_en; + /** Initiator session ID (IANA format) qualifier */ + uint16_t isid_iana_qual; + /** Target session identifying handle */ + uint16_t tsih; + /** Initiator Task Tag */ + uint32_t itt; + /** Connection ID */ + uint16_t cid; + /** Reserved */ + uint16_t reserved_a; + /** Command sequence number */ + uint32_t cmdsn; + /** Expected status sequence number */ + uint32_t expstatsn; + /** Reserved */ + uint8_t reserved_b[16]; +}; + +/** Login request opcode */ +#define ISCSI_OPCODE_LOGIN_REQUEST 0x03 + +/** Willingness to transition to next stage */ +#define ISCSI_LOGIN_FLAG_TRANSITION 0x80 + +/** Key=value pairs continued in subsequent request */ +#define ISCSI_LOGIN_FLAG_CONTINUE 0x40 + +/* Current stage values and mask */ +#define ISCSI_LOGIN_CSG_MASK 0x0c +#define ISCSI_LOGIN_CSG_SECURITY_NEGOTIATION 0x00 +#define ISCSI_LOGIN_CSG_OPERATIONAL_NEGOTIATION 0x04 +#define ISCSI_LOGIN_CSG_FULL_FEATURE_PHASE 0x0c + +/* Next stage values and mask */ +#define ISCSI_LOGIN_NSG_MASK 0x03 +#define ISCSI_LOGIN_NSG_SECURITY_NEGOTIATION 0x00 +#define ISCSI_LOGIN_NSG_OPERATIONAL_NEGOTIATION 0x01 +#define ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE 0x03 + +/** ISID IANA format marker */ +#define ISCSI_ISID_IANA 0x40000000 + +/** Fen Systems Ltd. IANA enterprise number + * + * Permission is hereby granted to use Fen Systems Ltd.'s IANA + * enterprise number with this iSCSI implementation. + */ +#define IANA_EN_FEN_SYSTEMS 10019 + +/** + * iSCSI login response basic header segment + * + */ +struct iscsi_bhs_login_response { + /** Opcode */ + uint8_t opcode; + /** Flags */ + uint8_t flags; + /** Maximum supported version number */ + uint8_t version_max; + /** Minimum supported version number */ + uint8_t version_min; + /** Segment lengths */ + union iscsi_segment_lengths lengths; + /** Initiator session ID (IANA format) enterprise number and flags */ + uint32_t isid_iana_en; + /** Initiator session ID (IANA format) qualifier */ + uint16_t isid_iana_qual; + /** Target session identifying handle */ + uint16_t tsih; + /** Initiator Task Tag */ + uint32_t itt; + /** Reserved */ + uint32_t reserved_a; + /** Status sequence number */ + uint32_t statsn; + /** Expected command sequence number */ + uint32_t expcmdsn; + /** Maximum command sequence number */ + uint32_t maxcmdsn; + /** Status class */ + uint8_t status_class; + /** Status detail */ + uint8_t status_detail; + /** Reserved */ + uint8_t reserved_b[10]; +}; + +/** Login response opcode */ +#define ISCSI_OPCODE_LOGIN_RESPONSE 0x23 + +/* Login response status codes */ +#define ISCSI_STATUS_SUCCESS 0x00 +#define ISCSI_STATUS_REDIRECT 0x01 +#define ISCSI_STATUS_INITIATOR_ERROR 0x02 +#define ISCSI_STATUS_INITIATOR_ERROR_AUTHENTICATION 0x01 +#define ISCSI_STATUS_INITIATOR_ERROR_AUTHORISATION 0x02 +#define ISCSI_STATUS_INITIATOR_ERROR_NOT_FOUND 0x03 +#define ISCSI_STATUS_INITIATOR_ERROR_REMOVED 0x04 +#define ISCSI_STATUS_TARGET_ERROR 0x03 + +/** + * iSCSI SCSI command basic header segment + * + */ +struct iscsi_bhs_scsi_command { + /** Opcode */ + uint8_t opcode; + /** Flags */ + uint8_t flags; + /** Reserved */ + uint16_t reserved_a; + /** Segment lengths */ + union iscsi_segment_lengths lengths; + /** SCSI Logical Unit Number */ + uint64_t lun; + /** Initiator Task Tag */ + uint32_t itt; + /** Expected data transfer length */ + uint32_t exp_len; + /** Command sequence number */ + uint32_t cmdsn; + /** Expected status sequence number */ + uint32_t expstatsn; + /** SCSI Command Descriptor Block (CDB) */ + union scsi_cdb cdb; +}; + +/** SCSI command opcode */ +#define ISCSI_OPCODE_SCSI_COMMAND 0x01 + +/** Command will read data */ +#define ISCSI_COMMAND_FLAG_READ 0x40 + +/** Command will write data */ +#define ISCSI_COMMAND_FLAG_WRITE 0x20 + +/* Task attributes */ +#define ISCSI_COMMAND_ATTR_UNTAGGED 0x00 +#define ISCSI_COMMAND_ATTR_SIMPLE 0x01 +#define ISCSI_COMMAND_ATTR_ORDERED 0x02 +#define ISCSI_COMMAND_ATTR_HEAD_OF_QUEUE 0x03 +#define ISCSI_COMMAND_ATTR_ACA 0x04 + +/** + * iSCSI SCSI response basic header segment + * + */ +struct iscsi_bhs_scsi_response { + /** Opcode */ + uint8_t opcode; + /** Flags */ + uint8_t flags; + /** Response code */ + uint8_t response; + /** SCSI status code */ + uint8_t status; + /** Segment lengths */ + union iscsi_segment_lengths lengths; + /** Reserved */ + uint8_t reserved_a[8]; + /** Initiator Task Tag */ + uint32_t itt; + /** SNACK tag */ + uint32_t snack; + /** Status sequence number */ + uint32_t statsn; + /** Expected command sequence number */ + uint32_t expcmdsn; + /** Maximum command sequence number */ + uint32_t maxcmdsn; + /** Expected data sequence number */ + uint32_t expdatasn; + /** Reserved */ + uint8_t reserved_b[8]; +}; + +/** SCSI response opcode */ +#define ISCSI_OPCODE_SCSI_RESPONSE 0x21 + +/** SCSI command completed at target */ +#define ISCSI_RESPONSE_COMMAND_COMPLETE 0x00 + +/** SCSI target failure */ +#define ISCSI_RESPONSE_TARGET_FAILURE 0x01 + +/** SCSI sense response code offset + * + * The SCSI response may contain unsolicited sense data in the data + * segment. If it does, this is the offset to the sense response code + * byte, which is the only byte we care about. + */ +#define ISCSI_SENSE_RESPONSE_CODE_OFFSET 2 + +/** + * iSCSI data-in basic header segment + * + */ +struct iscsi_bhs_data_in { + /** Opcode */ + uint8_t opcode; + /** Flags */ + uint8_t flags; + /** Reserved */ + uint8_t reserved_a; + /** SCSI status code */ + uint8_t status; + /** Segment lengths */ + union iscsi_segment_lengths lengths; + /** Logical Unit Number */ + uint64_t lun; + /** Initiator Task Tag */ + uint32_t itt; + /** Target Transfer Tag */ + uint32_t ttt; + /** Status sequence number */ + uint32_t statsn; + /** Expected command sequence number */ + uint32_t expcmdsn; + /** Maximum command sequence number */ + uint32_t maxcmdsn; + /** Data sequence number */ + uint32_t datasn; + /** Buffer offset */ + uint32_t offset; + /** Residual count */ + uint32_t residual_count; +}; + +/** Data-in opcode */ +#define ISCSI_OPCODE_DATA_IN 0x25 + +/** Data requires acknowledgement */ +#define ISCSI_DATA_FLAG_ACKNOWLEDGE 0x40 + +/** Data overflow occurred */ +#define ISCSI_DATA_FLAG_OVERFLOW 0x04 + +/** Data underflow occurred */ +#define ISCSI_DATA_FLAG_UNDERFLOW 0x02 + +/** SCSI status code and overflow/underflow flags are valid */ +#define ISCSI_DATA_FLAG_STATUS 0x01 + +/** + * iSCSI data-out basic header segment + * + */ +struct iscsi_bhs_data_out { + /** Opcode */ + uint8_t opcode; + /** Flags */ + uint8_t flags; + /** Reserved */ + uint16_t reserved_a; + /** Segment lengths */ + union iscsi_segment_lengths lengths; + /** Logical Unit Number */ + uint64_t lun; + /** Initiator Task Tag */ + uint32_t itt; + /** Target Transfer Tag */ + uint32_t ttt; + /** Reserved */ + uint32_t reserved_b; + /** Expected status sequence number */ + uint32_t expstatsn; + /** Reserved */ + uint32_t reserved_c; + /** Data sequence number */ + uint32_t datasn; + /** Buffer offset */ + uint32_t offset; + /** Reserved */ + uint32_t reserved_d; +}; + +/** Data-out opcode */ +#define ISCSI_OPCODE_DATA_OUT 0x05 + +/** + * iSCSI request to transfer basic header segment + * + */ +struct iscsi_bhs_r2t { + /** Opcode */ + uint8_t opcode; + /** Flags */ + uint8_t flags; + /** Reserved */ + uint16_t reserved_a; + /** Segment lengths */ + union iscsi_segment_lengths lengths; + /** Logical Unit Number */ + uint64_t lun; + /** Initiator Task Tag */ + uint32_t itt; + /** Target Transfer Tag */ + uint32_t ttt; + /** Status sequence number */ + uint32_t statsn; + /** Expected command sequence number */ + uint32_t expcmdsn; + /** Maximum command sequence number */ + uint32_t maxcmdsn; + /** R2T sequence number */ + uint32_t r2tsn; + /** Buffer offset */ + uint32_t offset; + /** Desired data transfer length */ + uint32_t len; +}; + +/** R2T opcode */ +#define ISCSI_OPCODE_R2T 0x31 + +/** + * An iSCSI basic header segment + */ +union iscsi_bhs { + struct iscsi_bhs_common common; + struct iscsi_bhs_common_response common_response; + struct iscsi_bhs_login_request login_request; + struct iscsi_bhs_login_response login_response; + struct iscsi_bhs_scsi_command scsi_command; + struct iscsi_bhs_scsi_response scsi_response; + struct iscsi_bhs_data_in data_in; + struct iscsi_bhs_data_out data_out; + struct iscsi_bhs_r2t r2t; + unsigned char bytes[ sizeof ( struct iscsi_bhs_common ) ]; +}; + +/** State of an iSCSI TX engine */ +enum iscsi_tx_state { + /** Nothing to send */ + ISCSI_TX_IDLE = 0, + /** Sending the basic header segment */ + ISCSI_TX_BHS, + /** Sending the additional header segment */ + ISCSI_TX_AHS, + /** Sending the data segment */ + ISCSI_TX_DATA, + /** Sending the data segment padding */ + ISCSI_TX_DATA_PADDING, +}; + +/** State of an iSCSI RX engine */ +enum iscsi_rx_state { + /** Receiving the basic header segment */ + ISCSI_RX_BHS = 0, + /** Receiving the additional header segment */ + ISCSI_RX_AHS, + /** Receiving the data segment */ + ISCSI_RX_DATA, + /** Receiving the data segment padding */ + ISCSI_RX_DATA_PADDING, +}; + +/** An iSCSI session */ +struct iscsi_session { + /** Reference counter */ + struct refcnt refcnt; + + /** Transport-layer socket */ + struct xfer_interface socket; + + /** Target address */ + char *target_address; + /** Target port */ + unsigned int target_port; + /** Target IQN */ + char *target_iqn; + /** Logical Unit Number (LUN) */ + uint64_t lun; + /** Target socket address (recorded only for iBFT) */ + struct sockaddr target_sockaddr; + + /** Session status + * + * This is the bitwise-OR of zero or more ISCSI_STATUS_XXX + * constants. + */ + int status; + /** Retry count + * + * Number of times that the connection has been retried. + * Reset upon a successful connection. + */ + int retry_count; + + /** Initiator username (if any) */ + char *initiator_username; + /** Initiator password (if any) */ + char *initiator_password; + /** Target username (if any) */ + char *target_username; + /** Target password (if any) */ + char *target_password; + /** CHAP challenge (for target auth only) + * + * This is a block of random data; the first byte is used as + * the CHAP identifier (CHAP_I) and the remainder as the CHAP + * challenge (CHAP_C). + */ + unsigned char chap_challenge[17]; + /** CHAP response (used for both initiator and target auth) */ + struct chap_response chap; + + /** Target session identifying handle + * + * This is assigned by the target when we first log in, and + * must be reused on subsequent login attempts. + */ + uint16_t tsih; + /** Initiator task tag + * + * This is the tag of the current command. It is incremented + * whenever a new command is started. + */ + uint32_t itt; + /** Target transfer tag + * + * This is the tag attached to a sequence of data-out PDUs in + * response to an R2T. + */ + uint32_t ttt; + /** + * Transfer offset + * + * This is the offset for an in-progress sequence of data-out + * PDUs in response to an R2T. + */ + uint32_t transfer_offset; + /** + * Transfer length + * + * This is the length for an in-progress sequence of data-out + * PDUs in response to an R2T. + */ + uint32_t transfer_len; + /** Command sequence number + * + * This is the sequence number of the current command, used to + * fill out the CmdSN field in iSCSI request PDUs. It is + * updated with the value of the ExpCmdSN field whenever we + * receive an iSCSI response PDU containing such a field. + */ + uint32_t cmdsn; + /** Status sequence number + * + * This is the most recent status sequence number present in + * the StatSN field of an iSCSI response PDU containing such a + * field. Whenever we send an iSCSI request PDU, we fill out + * the ExpStatSN field with this value plus one. + */ + uint32_t statsn; + + /** Basic header segment for current TX PDU */ + union iscsi_bhs tx_bhs; + /** State of the TX engine */ + enum iscsi_tx_state tx_state; + /** TX process */ + struct process process; + + /** Basic header segment for current RX PDU */ + union iscsi_bhs rx_bhs; + /** State of the RX engine */ + enum iscsi_rx_state rx_state; + /** Byte offset within the current RX state */ + size_t rx_offset; + /** Length of the current RX state */ + size_t rx_len; + /** Buffer for received data (not always used) */ + void *rx_buffer; + + /** Current SCSI command + * + * Set to NULL when command is complete. + */ + struct scsi_command *command; + /** SCSI command return code + * + * Set to -EINPROGRESS while command is processing. + */ + int rc; + /** Instant return code + * + * Set to a non-zero value if all requests should return + * immediately. This can be used to e.g. avoid retrying + * logins that are doomed to fail authentication. + */ + int instant_rc; +}; + +/** iSCSI session is currently in the security negotiation phase */ +#define ISCSI_STATUS_SECURITY_NEGOTIATION_PHASE \ + ( ISCSI_LOGIN_CSG_SECURITY_NEGOTIATION | \ + ISCSI_LOGIN_NSG_OPERATIONAL_NEGOTIATION ) + +/** iSCSI session is currently in the operational parameter + * negotiation phase + */ +#define ISCSI_STATUS_OPERATIONAL_NEGOTIATION_PHASE \ + ( ISCSI_LOGIN_CSG_OPERATIONAL_NEGOTIATION | \ + ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE ) + +/** iSCSI session is currently in the full feature phase */ +#define ISCSI_STATUS_FULL_FEATURE_PHASE ISCSI_LOGIN_CSG_FULL_FEATURE_PHASE + +/** Mask for all iSCSI session phases */ +#define ISCSI_STATUS_PHASE_MASK ( ISCSI_LOGIN_CSG_MASK | ISCSI_LOGIN_NSG_MASK ) + +/** iSCSI session needs to send the initial security negotiation strings */ +#define ISCSI_STATUS_STRINGS_SECURITY 0x0100 + +/** iSCSI session needs to send the CHAP_A string */ +#define ISCSI_STATUS_STRINGS_CHAP_ALGORITHM 0x0200 + +/** iSCSI session needs to send the CHAP response */ +#define ISCSI_STATUS_STRINGS_CHAP_RESPONSE 0x0400 + +/** iSCSI session needs to send the mutual CHAP challenge */ +#define ISCSI_STATUS_STRINGS_CHAP_CHALLENGE 0x0800 + +/** iSCSI session needs to send the operational negotiation strings */ +#define ISCSI_STATUS_STRINGS_OPERATIONAL 0x1000 + +/** Mask for all iSCSI "needs to send" flags */ +#define ISCSI_STATUS_STRINGS_MASK 0xff00 + +/** Target has requested forward (initiator) authentication */ +#define ISCSI_STATUS_AUTH_FORWARD_REQUIRED 0x00010000 + +/** Initiator requires target (reverse) authentication */ +#define ISCSI_STATUS_AUTH_REVERSE_REQUIRED 0x00020000 + +/** Target authenticated itself correctly */ +#define ISCSI_STATUS_AUTH_REVERSE_OK 0x00040000 + +/** Maximum number of retries at connecting */ +#define ISCSI_MAX_RETRIES 2 + +extern int iscsi_attach ( struct scsi_device *scsi, const char *root_path ); +extern void iscsi_detach ( struct scsi_device *scsi ); +extern const char * iscsi_initiator_iqn ( void ); + +#endif /* _GPXE_ISCSI_H */ diff --git a/gpxe/src/include/gpxe/job.h b/gpxe/src/include/gpxe/job.h new file mode 100644 index 0000000..8e11aa3 --- /dev/null +++ b/gpxe/src/include/gpxe/job.h @@ -0,0 +1,165 @@ +#ifndef _GPXE_JOB_H +#define _GPXE_JOB_H + +/** @file + * + * Job control interfaces + * + */ + +#include +#include + +/** Job progress */ +struct job_progress { + /** Amount of operation completed so far + * + * The units for this quantity are arbitrary. @c completed + * divded by @total should give something which approximately + * represents the progress through the operation. For a + * download operation, using byte counts would make sense. + */ + unsigned long completed; + /** Total operation size + * + * See @c completed. A zero value means "total size unknown" + * and is explcitly permitted; users should take this into + * account before calculating @c completed/total. + */ + unsigned long total; +}; + +struct job_interface; + +/** Job control interface operations */ +struct job_interface_operations { + /** Job completed + * + * @v job Job control interface + * @v rc Overall job status code + */ + void ( * done ) ( struct job_interface *job, int rc ); + /** Abort job + * + * @v job Job control interface + */ + void ( * kill ) ( struct job_interface *job ); + /** Get job progress + * + * @v job Job control interface + * @v progress Progress data to fill in + */ + void ( * progress ) ( struct job_interface *job, + struct job_progress *progress ); +}; + +/** A job control interface */ +struct job_interface { + /** Generic object communication interface */ + struct interface intf; + /** Operations for received messages */ + struct job_interface_operations *op; +}; + +extern struct job_interface null_job; +extern struct job_interface_operations null_job_ops; + +extern void job_done ( struct job_interface *job, int rc ); +extern void job_kill ( struct job_interface *job ); + +extern void ignore_job_done ( struct job_interface *job, int rc ); +extern void ignore_job_kill ( struct job_interface *job ); +extern void ignore_job_progress ( struct job_interface *job, + struct job_progress *progress ); + +/** + * Initialise a job control interface + * + * @v job Job control interface + * @v op Job control interface operations + * @v refcnt Containing object reference counter, or NULL + */ +static inline void job_init ( struct job_interface *job, + struct job_interface_operations *op, + struct refcnt *refcnt ) { + job->intf.dest = &null_job.intf; + job->intf.refcnt = refcnt; + job->op = op; +} + +/** + * Get job control interface from generic object communication interface + * + * @v intf Generic object communication interface + * @ret job Job control interface + */ +static inline __attribute__ (( always_inline )) struct job_interface * +intf_to_job ( struct interface *intf ) { + return container_of ( intf, struct job_interface, intf ); +} + +/** + * Get reference to destination job control interface + * + * @v job Job control interface + * @ret dest Destination interface + */ +static inline __attribute__ (( always_inline )) struct job_interface * +job_get_dest ( struct job_interface *job ) { + return intf_to_job ( intf_get ( job->intf.dest ) ); +} + +/** + * Drop reference to job control interface + * + * @v job Job control interface + */ +static inline __attribute__ (( always_inline )) void +job_put ( struct job_interface *job ) { + intf_put ( &job->intf ); +} + +/** + * Plug a job control interface into a new destination interface + * + * @v job Job control interface + * @v dest New destination interface + */ +static inline void job_plug ( struct job_interface *job, + struct job_interface *dest ) { + plug ( &job->intf, &dest->intf ); +} + +/** + * Plug two job control interfaces together + * + * @v a Job control interface A + * @v b Job control interface B + */ +static inline void job_plug_plug ( struct job_interface *a, + struct job_interface *b ) { + plug_plug ( &a->intf, &b->intf ); +} + +/** + * Unplug a job control interface + * + * @v job Job control interface + */ +static inline void job_unplug ( struct job_interface *job ) { + plug ( &job->intf, &null_job.intf ); +} + +/** + * Stop using a job control interface + * + * @v job Job control interface + * + * After calling this method, no further messages will be received via + * the interface. + */ +static inline void job_nullify ( struct job_interface *job ) { + job->op = &null_job_ops; +}; + +#endif /* _GPXE_JOB_H */ diff --git a/gpxe/src/include/gpxe/keys.h b/gpxe/src/include/gpxe/keys.h new file mode 100644 index 0000000..3da8a1f --- /dev/null +++ b/gpxe/src/include/gpxe/keys.h @@ -0,0 +1,79 @@ +#ifndef _GPXE_KEYS_H +#define _GPXE_KEYS_H + +/** @file + * + * Key definitions + * + */ + +/* + * Symbolic names for some standard ASCII characters + * + */ + +#define NUL 0x00 +#define CTRL_A 0x01 +#define CTRL_B 0x02 +#define CTRL_C 0x03 +#define CTRL_D 0x04 +#define CTRL_E 0x05 +#define CTRL_F 0x06 +#define CTRL_G 0x07 +#define CTRL_H 0x08 +#define CTRL_I 0x09 +#define CTRL_J 0x0a +#define CTRL_K 0x0b +#define CTRL_L 0x0c +#define CTRL_M 0x0d +#define CTRL_N 0x0e +#define CTRL_O 0x0f +#define CTRL_P 0x10 +#define CTRL_Q 0x11 +#define CTRL_R 0x12 +#define CTRL_S 0x13 +#define CTRL_T 0x14 +#define CTRL_U 0x15 +#define CTRL_V 0x16 +#define CTRL_W 0x17 +#define CTRL_X 0x18 +#define CTRL_Y 0x19 +#define CTRL_Z 0x1a + +#define BACKSPACE CTRL_H +#define TAB CTRL_I +#define LF CTRL_J +#define CR CTRL_M +#define ESC 0x1b + +/* + * Special keys outside the normal ASCII range + * + * + * The names are chosen to match those used by curses. The values are + * chosen to facilitate easy conversion from a received ANSI escape + * sequence to a KEY_XXX constant. + */ + +#define KEY_ANSI( n, terminator ) ( 0x100 * ( (n) + 1 ) + (terminator) ) + +#define KEY_MIN 0x101 +#define KEY_UP KEY_ANSI ( 0, 'A' ) /**< Up arrow */ +#define KEY_DOWN KEY_ANSI ( 0, 'B' ) /**< Down arrow */ +#define KEY_RIGHT KEY_ANSI ( 0, 'C' ) /**< Right arrow */ +#define KEY_LEFT KEY_ANSI ( 0, 'D' ) /**< Left arrow */ +#define KEY_END KEY_ANSI ( 0, 'F' ) /**< End */ +#define KEY_HOME KEY_ANSI ( 0, 'H' ) /**< Home */ +#define KEY_IC KEY_ANSI ( 2, '~' ) /**< Insert */ +#define KEY_DC KEY_ANSI ( 3, '~' ) /**< Delete */ +#define KEY_PPAGE KEY_ANSI ( 5, '~' ) /**< Page up */ +#define KEY_NPAGE KEY_ANSI ( 6, '~' ) /**< Page down */ +#define KEY_F8 KEY_ANSI ( 19, '~' ) /**< F8 (for PXE) */ + +/* Not in the [KEY_MIN,KEY_MAX] range; terminals seem to send these as + * normal ASCII values. + */ +#define KEY_BACKSPACE BACKSPACE +#define KEY_ENTER LF + +#endif /* _GPXE_KEYS_H */ diff --git a/gpxe/src/include/gpxe/linebuf.h b/gpxe/src/include/gpxe/linebuf.h new file mode 100644 index 0000000..676731a --- /dev/null +++ b/gpxe/src/include/gpxe/linebuf.h @@ -0,0 +1,28 @@ +#ifndef _GPXE_LINEBUF_H +#define _GPXE_LINEBUF_H + +/** @file + * + * Line buffering + * + */ + +#include +#include + +/** A line buffer */ +struct line_buffer { + /** Current string in the buffer */ + char *data; + /** Length of current string, excluding the terminating NUL */ + size_t len; + /** String is ready to read */ + int ready; +}; + +extern char * buffered_line ( struct line_buffer *linebuf ); +extern ssize_t line_buffer ( struct line_buffer *linebuf, + const char *data, size_t len ); +extern void empty_line_buffer ( struct line_buffer *linebuf ); + +#endif /* _GPXE_LINEBUF_H */ diff --git a/gpxe/src/include/gpxe/linux_compat.h b/gpxe/src/include/gpxe/linux_compat.h new file mode 100644 index 0000000..2c3cbbd --- /dev/null +++ b/gpxe/src/include/gpxe/linux_compat.h @@ -0,0 +1,25 @@ +#ifndef _GPXE_LINUX_COMPAT_H +#define _GPXE_LINUX_COMPAT_H + +/** @file + * + * Linux code compatibility + * + * This file exists to ease the building of Linux source code within + * gPXE. This is intended to facilitate quick testing; it is not + * intended to be a substitute for proper porting. + */ + +#include +#include +#include +#include +#include + +#define __init +#define __exit +#define __initdata +#define __exitdata +#define printk printf + +#endif /* _GPXE_LINUX_COMPAT_H */ diff --git a/gpxe/src/include/gpxe/list.h b/gpxe/src/include/gpxe/list.h new file mode 100644 index 0000000..602382b --- /dev/null +++ b/gpxe/src/include/gpxe/list.h @@ -0,0 +1,178 @@ +#ifndef _GPXE_LIST_H +#define _GPXE_LIST_H + +/** @file + * + * Linked lists + * + * This linked list handling code is based on the Linux kernel's + * list.h. + */ + +#include +#include + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next; + struct list_head *prev; +}; + +#define LIST_HEAD_INIT( name ) { &(name), &(name) } + +#define LIST_HEAD( name ) \ + struct list_head name = LIST_HEAD_INIT ( name ) + +#define INIT_LIST_HEAD( ptr ) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while ( 0 ) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add ( struct list_head *new, + struct list_head *prev, + struct list_head *next ) { + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * Add a new entry to the head of a list + * + * @v new New entry to be added + * @v head List head to add it after + * + * Insert a new entry after the specified head. This is good for + * implementing stacks. + */ +static inline void list_add ( struct list_head *new, struct list_head *head ) { + __list_add ( new, head, head->next ); +} +#define list_add( new, head ) do { \ + assert ( (head)->next->prev == (head) ); \ + assert ( (head)->prev->next == (head) ); \ + list_add ( (new), (head) ); \ + } while ( 0 ) + +/** + * Add a new entry to the tail of a list + * + * @v new New entry to be added + * @v head List head to add it before + * + * Insert a new entry before the specified head. This is useful for + * implementing queues. + */ +static inline void list_add_tail ( struct list_head *new, + struct list_head *head ) { + __list_add ( new, head->prev, head ); +} +#define list_add_tail( new, head ) do { \ + assert ( (head)->next->prev == (head) ); \ + assert ( (head)->prev->next == (head) ); \ + list_add_tail ( (new), (head) ); \ + } while ( 0 ) + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del ( struct list_head * prev, + struct list_head * next ) { + next->prev = prev; + prev->next = next; +} + +/** + * Delete an entry from a list + * + * @v entry Element to delete from the list + * + * Note that list_empty() on entry does not return true after this; + * the entry is in an undefined state. + */ +static inline void list_del ( struct list_head *entry ) { + __list_del ( entry->prev, entry->next ); +} +#define list_del( entry ) do { \ + assert ( (entry)->prev != NULL ); \ + assert ( (entry)->next != NULL ); \ + assert ( (entry)->next->prev == (entry) ); \ + assert ( (entry)->prev->next == (entry) ); \ + list_del ( (entry) ); \ + } while ( 0 ) + +/** + * Test whether a list is empty + * + * @v head List to test. + */ +static inline int list_empty ( const struct list_head *head ) { + return head->next == head; +} + +/** + * Get the containing struct for this entry + * + * @v ptr The struct list_head pointer + * @v type The type of the struct this is embedded in + * @v member The name of the list_struct within the struct + */ +#define list_entry( ptr, type, member ) \ + container_of ( ptr, type, member ) + +/** + * Iterate over a list + * + * @v pos The &struct list_head to use as a loop counter + * @v head The head for your list + */ +#define list_for_each( pos, head ) \ + for ( pos = (head)->next; pos != (head); pos = pos->next ) + +/** + * Iterate over entries in a list + * + * @v pos The type * to use as a loop counter + * @v head The head for your list + * @v member The name of the list_struct within the struct + */ +#define list_for_each_entry( pos, head, member ) \ + for ( pos = list_entry ( (head)->next, typeof ( *pos ), member ); \ + &pos->member != (head); \ + pos = list_entry ( pos->member.next, typeof ( *pos ), member ) ) + +/** + * Iterate over entries in a list, safe against deletion of entries + * + * @v pos The type * to use as a loop counter + * @v tmp Another type * to use for temporary storage + * @v head The head for your list + * @v member The name of the list_struct within the struct + */ +#define list_for_each_entry_safe( pos, tmp, head, member ) \ + for ( pos = list_entry ( (head)->next, typeof ( *pos ), member ), \ + tmp = list_entry ( pos->member.next, typeof ( *tmp ), member ); \ + &pos->member != (head); \ + pos = tmp, \ + tmp = list_entry ( tmp->member.next, typeof ( *tmp ), member ) ) + +#endif /* _GPXE_LIST_H */ diff --git a/gpxe/src/include/gpxe/login_ui.h b/gpxe/src/include/gpxe/login_ui.h new file mode 100644 index 0000000..d92ba8f --- /dev/null +++ b/gpxe/src/include/gpxe/login_ui.h @@ -0,0 +1,12 @@ +#ifndef _GPXE_LOGIN_UI_H +#define _GPXE_LOGIN_UI_H + +/** @file + * + * Login UI + * + */ + +extern int login_ui ( void ); + +#endif /* _GPXE_LOGIN_UI_H */ diff --git a/gpxe/src/include/gpxe/malloc.h b/gpxe/src/include/gpxe/malloc.h new file mode 100644 index 0000000..cce5d1d --- /dev/null +++ b/gpxe/src/include/gpxe/malloc.h @@ -0,0 +1,57 @@ +#ifndef _GPXE_MALLOC_H +#define _GPXE_MALLOC_H + +#include + +/** @file + * + * Dynamic memory allocation + * + */ + +/* + * Prototypes for the standard functions (malloc() et al) are in + * stdlib.h. Include only if you need the + * non-standard functions, such as malloc_dma(). + * + */ +#include + +extern size_t freemem; + +extern void * __malloc alloc_memblock ( size_t size, size_t align ); +extern void free_memblock ( void *ptr, size_t size ); +extern void mpopulate ( void *start, size_t len ); +extern void mdumpfree ( void ); + +/** + * Allocate memory for DMA + * + * @v size Requested size + * @v align Physical alignment + * @ret ptr Memory, or NULL + * + * Allocates physically-aligned memory for DMA. + * + * @c align must be a power of two. @c size may not be zero. + */ +static inline void * __malloc malloc_dma ( size_t size, size_t phys_align ) { + return alloc_memblock ( size, phys_align ); +} + +/** + * Free memory allocated with malloc_dma() + * + * @v ptr Memory allocated by malloc_dma(), or NULL + * @v size Size of memory, as passed to malloc_dma() + * + * Memory allocated with malloc_dma() can only be freed with + * free_dma(); it cannot be freed with the standard free(). + * + * If @c ptr is NULL, no action is taken. + */ +static inline void free_dma ( void *ptr, size_t size ) { + free_memblock ( ptr, size ); +} + +#endif /* _GPXE_MALLOC_H */ diff --git a/gpxe/src/include/gpxe/mca.h b/gpxe/src/include/gpxe/mca.h new file mode 100644 index 0000000..21f9e74 --- /dev/null +++ b/gpxe/src/include/gpxe/mca.h @@ -0,0 +1,103 @@ +/* + * MCA bus driver code + * + * Abstracted from 3c509.c. + * + */ + +#ifndef MCA_H +#define MCA_H + +#include +#include +#include + +/* + * MCA constants + * + */ +#define MCA_MOTHERBOARD_SETUP_REG 0x94 +#define MCA_ADAPTER_SETUP_REG 0x96 +#define MCA_MAX_SLOT_NR 0x07 /* Must be 2^n - 1 */ +#define MCA_POS_REG(n) (0x100+(n)) + +/* Is there a standard that would define this? */ +#define GENERIC_MCA_VENDOR ISA_VENDOR ( 'M', 'C', 'A' ) + +/** An MCA device ID list entry */ +struct mca_device_id { + /** Name */ + const char *name; + /** Device ID */ + uint16_t id; +}; + +/** An MCA device */ +struct mca_device { + /** Generic device */ + struct device dev; + /** Slot number */ + unsigned int slot; + /** POS register values */ + unsigned char pos[8]; + /** Driver for this device */ + struct mca_driver *driver; + /** Driver-private data + * + * Use mca_set_drvdata() and mca_get_drvdata() to access + * this field. + */ + void *priv; + /** Driver name */ + const char *driver_name; +}; + +#define MCA_ID(mca) ( ( (mca)->pos[1] << 8 ) + (mca)->pos[0] ) + +/** An MCA driver */ +struct mca_driver { + /** MCA ID table */ + struct mca_device_id *ids; + /** Number of entries in MCA ID table */ + unsigned int id_count; + /** + * Probe device + * + * @v mca MCA device + * @v id Matching entry in ID table + * @ret rc Return status code + */ + int ( * probe ) ( struct mca_device *mca, + const struct mca_device_id *id ); + /** + * Remove device + * + * @v mca MCA device + */ + void ( * remove ) ( struct mca_device *mca ); +}; + +/** Declare an MCA driver */ +#define __mca_driver __table ( struct mca_driver, mca_drivers, 01 ) + +/** + * Set MCA driver-private data + * + * @v mca MCA device + * @v priv Private data + */ +static inline void mca_set_drvdata ( struct mca_device *mca, void *priv ) { + mca->priv = priv; +} + +/** + * Get MCA driver-private data + * + * @v mca MCA device + * @ret priv Private data + */ +static inline void * mca_get_drvdata ( struct mca_device *mca ) { + return mca->priv; +} + +#endif diff --git a/gpxe/src/include/gpxe/md5.h b/gpxe/src/include/gpxe/md5.h new file mode 100644 index 0000000..f8976a1 --- /dev/null +++ b/gpxe/src/include/gpxe/md5.h @@ -0,0 +1,22 @@ +#ifndef _GPXE_MD5_H +#define _GPXE_MD5_H + +struct digest_algorithm; + +#include + +#define MD5_DIGEST_SIZE 16 +#define MD5_BLOCK_WORDS 16 +#define MD5_HASH_WORDS 4 + +struct md5_ctx { + u32 hash[MD5_HASH_WORDS]; + u32 block[MD5_BLOCK_WORDS]; + u64 byte_count; +}; + +#define MD5_CTX_SIZE sizeof ( struct md5_ctx ) + +extern struct digest_algorithm md5_algorithm; + +#endif /* _GPXE_MD5_H */ diff --git a/gpxe/src/include/gpxe/memmap.h b/gpxe/src/include/gpxe/memmap.h new file mode 100644 index 0000000..836a1b9 --- /dev/null +++ b/gpxe/src/include/gpxe/memmap.h @@ -0,0 +1,34 @@ +#ifndef _GPXE_MEMMAP_H +#define _GPXE_MEMMAP_H + +#include + +/** + * @file + * + * Memory mapping + * + */ + +/** A usable memory region */ +struct memory_region { + /** Physical start address */ + uint64_t start; + /** Physical end address */ + uint64_t end; +}; + +/** Maximum number of memory regions we expect to encounter */ +#define MAX_MEMORY_REGIONS 8 + +/** A memory map */ +struct memory_map { + /** Memory regions */ + struct memory_region regions[MAX_MEMORY_REGIONS]; + /** Number of used regions */ + unsigned int count; +}; + +extern void get_memmap ( struct memory_map *memmap ); + +#endif /* _GPXE_MEMMAP_H */ diff --git a/gpxe/src/include/gpxe/monojob.h b/gpxe/src/include/gpxe/monojob.h new file mode 100644 index 0000000..aaa38d0 --- /dev/null +++ b/gpxe/src/include/gpxe/monojob.h @@ -0,0 +1,15 @@ +#ifndef _GPXE_MONOJOB_H +#define _GPXE_MONOJOB_H + +/** @file + * + * Single foreground job + * + */ + +struct job_interface; + +extern struct job_interface monojob; +extern int monojob_wait ( const char *string ); + +#endif /* _GPXE_MONOJOB_H */ diff --git a/gpxe/src/include/gpxe/nap.h b/gpxe/src/include/gpxe/nap.h new file mode 100644 index 0000000..f9ae3cf --- /dev/null +++ b/gpxe/src/include/gpxe/nap.h @@ -0,0 +1,54 @@ +#ifndef _GPXE_NAP_H +#define _GPXE_NAP_H + +/** @file + * + * CPU sleeping + * + */ + +#include +#include + +/** + * Calculate static inline CPU sleeping API function name + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @ret _subsys_func Subsystem API function + */ +#define NAP_INLINE( _subsys, _api_func ) \ + SINGLE_API_INLINE ( NAP_PREFIX_ ## _subsys, _api_func ) + +/** + * Provide an CPU sleeping API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @v _func Implementing function + */ +#define PROVIDE_NAP( _subsys, _api_func, _func ) \ + PROVIDE_SINGLE_API ( NAP_PREFIX_ ## _subsys, _api_func, _func ) + +/** + * Provide a static inline CPU sleeping API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + */ +#define PROVIDE_NAP_INLINE( _subsys, _api_func ) \ + PROVIDE_SINGLE_API_INLINE ( NAP_PREFIX_ ## _subsys, _api_func ) + +/* Include all architecture-independent I/O API headers */ +#include + +/* Include all architecture-dependent I/O API headers */ +#include + +/** + * Sleep until next CPU interrupt + * + */ +void cpu_nap ( void ); + +#endif /* _GPXE_NAP_H */ diff --git a/gpxe/src/include/gpxe/ndp.h b/gpxe/src/include/gpxe/ndp.h new file mode 100644 index 0000000..db32b0c --- /dev/null +++ b/gpxe/src/include/gpxe/ndp.h @@ -0,0 +1,21 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NDP_STATE_INVALID 0 +#define NDP_STATE_INCOMPLETE 1 +#define NDP_STATE_REACHABLE 2 +#define NDP_STATE_DELAY 3 +#define NDP_STATE_PROBE 4 +#define NDP_STATE_STALE 5 + +int ndp_resolve ( struct net_device *netdev, struct in6_addr *src, + struct in6_addr *dest, void *dest_ll_addr ); +int ndp_process_advert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src, + struct sockaddr_tcpip *st_dest ); diff --git a/gpxe/src/include/gpxe/netdevice.h b/gpxe/src/include/gpxe/netdevice.h new file mode 100644 index 0000000..f1585de --- /dev/null +++ b/gpxe/src/include/gpxe/netdevice.h @@ -0,0 +1,461 @@ +#ifndef _GPXE_NETDEVICE_H +#define _GPXE_NETDEVICE_H + +/** @file + * + * Network device management + * + */ + +#include +#include +#include +#include +#include + +struct io_buffer; +struct net_device; +struct net_protocol; +struct ll_protocol; +struct device; + +/** Maximum length of a link-layer address */ +#define MAX_LL_ADDR_LEN 20 + +/** Maximum length of a link-layer header */ +#define MAX_LL_HEADER_LEN 6 + +/** Maximum length of a network-layer address */ +#define MAX_NET_ADDR_LEN 4 + +/** + * A network-layer protocol + * + */ +struct net_protocol { + /** Protocol name */ + const char *name; + /** + * Process received packet + * + * @v iobuf I/O buffer + * @v netdev Network device + * @v ll_source Link-layer source address + * + * This method takes ownership of the I/O buffer. + */ + int ( * rx ) ( struct io_buffer *iobuf, struct net_device *netdev, + const void *ll_source ); + /** + * Transcribe network-layer address + * + * @v net_addr Network-layer address + * @ret string Human-readable transcription of address + * + * This method should convert the network-layer address into a + * human-readable format (e.g. dotted quad notation for IPv4). + * + * The buffer used to hold the transcription is statically + * allocated. + */ + const char * ( *ntoa ) ( const void * net_addr ); + /** Network-layer protocol + * + * This is an ETH_P_XXX constant, in network-byte order + */ + uint16_t net_proto; + /** Network-layer address length */ + uint8_t net_addr_len; +}; + +/** + * A link-layer protocol + * + */ +struct ll_protocol { + /** Protocol name */ + const char *name; + /** + * Add link-layer header + * + * @v iobuf I/O buffer + * @v ll_dest Link-layer destination address + * @v ll_source Source link-layer address + * @v net_proto Network-layer protocol, in network-byte order + * @ret rc Return status code + */ + int ( * push ) ( struct io_buffer *iobuf, const void *ll_dest, + const void *ll_source, uint16_t net_proto ); + /** + * Remove link-layer header + * + * @v iobuf I/O buffer + * @ret ll_dest Link-layer destination address + * @ret ll_source Source link-layer address + * @ret net_proto Network-layer protocol, in network-byte order + * @ret rc Return status code + */ + int ( * pull ) ( struct io_buffer *iobuf, const void **ll_dest, + const void **ll_source, uint16_t *net_proto ); + /** + * Transcribe link-layer address + * + * @v ll_addr Link-layer address + * @ret string Human-readable transcription of address + * + * This method should convert the link-layer address into a + * human-readable format. + * + * The buffer used to hold the transcription is statically + * allocated. + */ + const char * ( * ntoa ) ( const void * ll_addr ); + /** + * Hash multicast address + * + * @v af Address family + * @v net_addr Network-layer address + * @v ll_addr Link-layer address to fill in + * @ret rc Return status code + */ + int ( * mc_hash ) ( unsigned int af, const void *net_addr, + void *ll_addr ); + /** Link-layer protocol + * + * This is an ARPHRD_XXX constant, in network byte order. + */ + uint16_t ll_proto; + /** Link-layer address length */ + uint8_t ll_addr_len; + /** Link-layer header length */ + uint8_t ll_header_len; + /** Link-layer broadcast address */ + const uint8_t *ll_broadcast; +}; + +/** Network device operations */ +struct net_device_operations { + /** Open network device + * + * @v netdev Network device + * @ret rc Return status code + * + * This method should allocate RX I/O buffers and enable + * the hardware to start transmitting and receiving packets. + */ + int ( * open ) ( struct net_device *netdev ); + /** Close network device + * + * @v netdev Network device + * + * This method should stop the flow of packets, and free up + * any packets that are currently in the device's TX queue. + */ + void ( * close ) ( struct net_device *netdev ); + /** Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + * + * This method should cause the hardware to initiate + * transmission of the I/O buffer. + * + * If this method returns success, the I/O buffer remains + * owned by the net device's TX queue, and the net device must + * eventually call netdev_tx_complete() to free the buffer. + * If this method returns failure, the I/O buffer is + * immediately released; the failure is interpreted as + * "failure to enqueue buffer". + * + * This method is guaranteed to be called only when the device + * is open. + */ + int ( * transmit ) ( struct net_device *netdev, + struct io_buffer *iobuf ); + /** Poll for completed and received packets + * + * @v netdev Network device + * + * This method should cause the hardware to check for + * completed transmissions and received packets. Any received + * packets should be delivered via netdev_rx(). + * + * This method is guaranteed to be called only when the device + * is open. + */ + void ( * poll ) ( struct net_device *netdev ); + /** Enable or disable interrupts + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ + void ( * irq ) ( struct net_device *netdev, int enable ); +}; + +/** Network device error */ +struct net_device_error { + /** Error status code */ + int rc; + /** Error count */ + unsigned int count; +}; + +/** Maximum number of unique errors that we will keep track of */ +#define NETDEV_MAX_UNIQUE_ERRORS 4 + +/** Network device statistics */ +struct net_device_stats { + /** Count of successful completions */ + unsigned int good; + /** Count of error completions */ + unsigned int bad; + /** Error breakdowns */ + struct net_device_error errors[NETDEV_MAX_UNIQUE_ERRORS]; +}; + +/** + * A network device + * + * This structure represents a piece of networking hardware. It has + * properties such as a link-layer address and methods for + * transmitting and receiving raw packets. + * + * Note that this structure must represent a generic network device, + * not just an Ethernet device. + */ +struct net_device { + /** Reference counter */ + struct refcnt refcnt; + /** List of network devices */ + struct list_head list; + /** List of open network devices */ + struct list_head open_list; + /** Name of this network device */ + char name[8]; + /** Underlying hardware device */ + struct device *dev; + + /** Network device operations */ + struct net_device_operations *op; + + /** Link-layer protocol */ + struct ll_protocol *ll_protocol; + /** Link-layer address + * + * For Ethernet, this is the MAC address. + */ + uint8_t ll_addr[MAX_LL_ADDR_LEN]; + + /** Current device state + * + * This is the bitwise-OR of zero or more NETDEV_XXX constants. + */ + unsigned int state; + /** Maximum packet length + * + * This length includes any link-layer headers. + */ + size_t max_pkt_len; + /** TX packet queue */ + struct list_head tx_queue; + /** RX packet queue */ + struct list_head rx_queue; + /** TX statistics */ + struct net_device_stats tx_stats; + /** RX statistics */ + struct net_device_stats rx_stats; + + /** Configuration settings applicable to this device */ + struct simple_settings settings; + + /** Driver private data */ + void *priv; +}; + +/** Network device is open */ +#define NETDEV_OPEN 0x0001 + +/** Network device has link */ +#define NETDEV_LINK_UP 0x0002 + +/** Declare a link-layer protocol */ +#define __ll_protocol __table ( struct ll_protocol, ll_protocols, 01 ) + +/** Declare a network-layer protocol */ +#define __net_protocol __table ( struct net_protocol, net_protocols, 01 ) + +extern struct list_head net_devices; +extern struct net_device_operations null_netdev_operations; + +/** + * Initialise a network device + * + * @v netdev Network device + * @v op Network device operations + */ +static inline void netdev_init ( struct net_device *netdev, + struct net_device_operations *op ) { + netdev->op = op; +} + +/** + * Stop using a network device + * + * @v netdev Network device + * + * Drivers should call this method immediately before the final call + * to netdev_put(). + */ +static inline void netdev_nullify ( struct net_device *netdev ) { + netdev->op = &null_netdev_operations; +} + +/** + * Get printable network device hardware address + * + * @v netdev Network device + * @ret name Hardware address + */ +static inline const char * netdev_hwaddr ( struct net_device *netdev ) { + return netdev->ll_protocol->ntoa ( netdev->ll_addr ); +} + +/** Iterate over all network devices */ +#define for_each_netdev( netdev ) \ + list_for_each_entry ( (netdev), &net_devices, list ) + +/** There exist some network devices + * + * @ret existence Existence of network devices + */ +static inline int have_netdevs ( void ) { + return ( ! list_empty ( &net_devices ) ); +} + +/** + * Get reference to network device + * + * @v netdev Network device + * @ret netdev Network device + */ +static inline __attribute__ (( always_inline )) struct net_device * +netdev_get ( struct net_device *netdev ) { + ref_get ( &netdev->refcnt ); + return netdev; +} + +/** + * Drop reference to network device + * + * @v netdev Network device + */ +static inline __attribute__ (( always_inline )) void +netdev_put ( struct net_device *netdev ) { + ref_put ( &netdev->refcnt ); +} + +/** + * Get driver private area for this network device + * + * @v netdev Network device + * @ret priv Driver private area for this network device + */ +static inline __attribute__ (( always_inline )) void * +netdev_priv ( struct net_device *netdev ) { + return netdev->priv; +} + +/** + * Get per-netdevice configuration settings block + * + * @v netdev Network device + * @ret settings Settings block + */ +static inline __attribute__ (( always_inline )) struct settings * +netdev_settings ( struct net_device *netdev ) { + return &netdev->settings.settings; +} + +/** + * Mark network device as having link up + * + * @v netdev Network device + */ +static inline __attribute__ (( always_inline )) void +netdev_link_up ( struct net_device *netdev ) { + netdev->state |= NETDEV_LINK_UP; +} + +/** + * Mark network device as having link down + * + * @v netdev Network device + */ +static inline __attribute__ (( always_inline )) void +netdev_link_down ( struct net_device *netdev ) { + netdev->state &= ~NETDEV_LINK_UP; +} + +/** + * Check link state of network device + * + * @v netdev Network device + * @ret link_up Link is up + */ +static inline __attribute__ (( always_inline )) int +netdev_link_ok ( struct net_device *netdev ) { + return ( netdev->state & NETDEV_LINK_UP ); +} + +extern int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf ); +extern void netdev_tx_complete_err ( struct net_device *netdev, + struct io_buffer *iobuf, int rc ); +extern void netdev_tx_complete_next_err ( struct net_device *netdev, int rc ); +extern void netdev_rx ( struct net_device *netdev, struct io_buffer *iobuf ); +extern void netdev_rx_err ( struct net_device *netdev, + struct io_buffer *iobuf, int rc ); +extern void netdev_poll ( struct net_device *netdev ); +extern struct io_buffer * netdev_rx_dequeue ( struct net_device *netdev ); +extern struct net_device * alloc_netdev ( size_t priv_size ); +extern int register_netdev ( struct net_device *netdev ); +extern int netdev_open ( struct net_device *netdev ); +extern void netdev_close ( struct net_device *netdev ); +extern void unregister_netdev ( struct net_device *netdev ); +extern void netdev_irq ( struct net_device *netdev, int enable ); +extern struct net_device * find_netdev ( const char *name ); +extern struct net_device * find_netdev_by_location ( unsigned int bus_type, + unsigned int location ); +extern struct net_device * last_opened_netdev ( void ); +extern int net_tx ( struct io_buffer *iobuf, struct net_device *netdev, + struct net_protocol *net_protocol, const void *ll_dest ); +extern int net_rx ( struct io_buffer *iobuf, struct net_device *netdev, + uint16_t net_proto, const void *ll_source ); + +extern struct settings_operations netdev_settings_operations; + +/** + * Complete network transmission + * + * @v netdev Network device + * @v iobuf I/O buffer + * + * The packet must currently be in the network device's TX queue. + */ +static inline void netdev_tx_complete ( struct net_device *netdev, + struct io_buffer *iobuf ) { + netdev_tx_complete_err ( netdev, iobuf, 0 ); +} + +/** + * Complete network transmission + * + * @v netdev Network device + * + * Completes the oldest outstanding packet in the TX queue. + */ +static inline void netdev_tx_complete_next ( struct net_device *netdev ) { + netdev_tx_complete_next_err ( netdev, 0 ); +} + +#endif /* _GPXE_NETDEVICE_H */ diff --git a/gpxe/src/include/gpxe/null_nap.h b/gpxe/src/include/gpxe/null_nap.h new file mode 100644 index 0000000..6dd0cda --- /dev/null +++ b/gpxe/src/include/gpxe/null_nap.h @@ -0,0 +1,21 @@ +#ifndef _GPXE_NULL_NAP_H +#define _GPXE_NULL_NAP_H + +/** @file + * + * Null CPU sleeping + * + */ + +#ifdef NAP_NULL +#define NAP_PREFIX_null +#else +#define NAP_PREFIX_null __null_ +#endif + +static inline __always_inline void +NAP_INLINE ( null, cpu_nap ) ( void ) { + /* Do nothing */ +} + +#endif /* _GPXE_NULL_NAP_H */ diff --git a/gpxe/src/include/gpxe/nvo.h b/gpxe/src/include/gpxe/nvo.h new file mode 100644 index 0000000..28068f4 --- /dev/null +++ b/gpxe/src/include/gpxe/nvo.h @@ -0,0 +1,53 @@ +#ifndef _GPXE_NVO_H +#define _GPXE_NVO_H + +/** @file + * + * Non-volatile stored options + * + */ + +#include +#include +#include + +struct nvs_device; +struct refcnt; + +/** + * A fragment of a non-volatile storage device used for stored options + */ +struct nvo_fragment { + /** Starting address of fragment within NVS device */ + unsigned int address; + /** Length of fragment */ + size_t len; +}; + +/** + * A block of non-volatile stored options + */ +struct nvo_block { + /** Settings block */ + struct settings settings; + /** Underlying non-volatile storage device */ + struct nvs_device *nvs; + /** List of option-containing fragments + * + * The list is terminated by a fragment with a length of zero. + */ + struct nvo_fragment *fragments; + /** Total length of option-containing fragments */ + size_t total_len; + /** Option-containing data */ + void *data; + /** DHCP options block */ + struct dhcp_options dhcpopts; +}; + +extern void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs, + struct nvo_fragment *fragments, struct refcnt *refcnt ); +extern int register_nvo ( struct nvo_block *nvo, struct settings *parent ); +extern void unregister_nvo ( struct nvo_block *nvo ); + +#endif /* _GPXE_NVO_H */ diff --git a/gpxe/src/include/gpxe/nvs.h b/gpxe/src/include/gpxe/nvs.h new file mode 100644 index 0000000..b026dd4 --- /dev/null +++ b/gpxe/src/include/gpxe/nvs.h @@ -0,0 +1,66 @@ +#ifndef _GPXE_NVS_H +#define _GPXE_NVS_H + +/** @file + * + * Non-volatile storage + * + */ + +#include + +/** A non-volatile storage device */ +struct nvs_device { + /** Word length + * + * This is expressed as the base-2 logarithm of the word + * length in bytes. A value of 0 therefore translates as + * 8-bit words, and a value of 1 translates as 16-bit words. + */ + unsigned int word_len_log2; + /** Device size (in words) */ + unsigned int size; + /** Data block size (in words) + * + * This is the block size used by the device. It must be a + * power of two. Data reads and writes must not cross a block + * boundary. + * + * Many devices allow reads to cross a block boundary, and + * restrict only writes. For the sake of simplicity, we + * assume that the same restriction applies to both reads and + * writes. + */ + unsigned int block_size; + /** Read data from device + * + * @v nvs NVS device + * @v address Address from which to read + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + * + * Reads may not cross a block boundary. + */ + int ( * read ) ( struct nvs_device *nvs, unsigned int address, + void *data, size_t len ); + /** Write data to device + * + * @v nvs NVS device + * @v address Address to which to write + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + * + * Writes may not cross a block boundary. + */ + int ( * write ) ( struct nvs_device *nvs, unsigned int address, + const void *data, size_t len ); +}; + +extern int nvs_read ( struct nvs_device *nvs, unsigned int address, + void *data, size_t len ); +extern int nvs_write ( struct nvs_device *nvs, unsigned int address, + const void *data, size_t len ); + +#endif /* _GPXE_NVS_H */ diff --git a/gpxe/src/include/gpxe/open.h b/gpxe/src/include/gpxe/open.h new file mode 100644 index 0000000..81d5fc2 --- /dev/null +++ b/gpxe/src/include/gpxe/open.h @@ -0,0 +1,95 @@ +#ifndef _GPXE_OPEN_H +#define _GPXE_OPEN_H + +/** @file + * + * Data transfer interface opening + * + */ + +#include +#include +#include + +struct xfer_interface; +struct uri; + +/** Location types */ +enum { + /** Location is a URI + * + * Parameter list for open() is: + * + * struct uri *uri; + */ + LOCATION_URI = 1, + /** Location is a URI string + * + * Parameter list for open() is: + * + * const char *uri_string; + */ + LOCATION_URI_STRING, + /** Location is a socket + * + * Parameter list for open() is: + * + * int semantics; + * struct sockaddr *peer; + * struct sockaddr *local; + */ + LOCATION_SOCKET, +}; + +/** A URI opener */ +struct uri_opener { + /** URI protocol name + * + * This is the "scheme" portion of the URI, e.g. "http" or + * "file". + */ + const char *scheme; + /** Open URI + * + * @v xfer Data transfer interface + * @v uri URI + * @ret rc Return status code + */ + int ( * open ) ( struct xfer_interface *xfer, struct uri *uri ); +}; + +/** Register a URI opener */ +#define __uri_opener __table ( struct uri_opener, uri_openers, 01 ) + +/** A socket opener */ +struct socket_opener { + /** Communication semantics (e.g. SOCK_STREAM) */ + int semantics; + /** Address family (e.g. AF_INET) */ + int family; + /** Open socket + * + * @v xfer Data transfer interface + * @v peer Peer socket address + * @v local Local socket address, or NULL + * @ret rc Return status code + */ + int ( * open ) ( struct xfer_interface *xfer, struct sockaddr *peer, + struct sockaddr *local ); +}; + +/** Register a socket opener */ +#define __socket_opener __table ( struct socket_opener, socket_openers, 01 ) + +extern int xfer_open_uri ( struct xfer_interface *xfer, struct uri *uri ); +extern int xfer_open_uri_string ( struct xfer_interface *xfer, + const char *uri_string ); +extern int xfer_open_named_socket ( struct xfer_interface *xfer, + int semantics, struct sockaddr *peer, + const char *name, struct sockaddr *local ); +extern int xfer_open_socket ( struct xfer_interface *xfer, int semantics, + struct sockaddr *peer, struct sockaddr *local ); +extern int xfer_vopen ( struct xfer_interface *xfer, int type, va_list args ); +extern int xfer_open ( struct xfer_interface *xfer, int type, ... ); + +#endif /* _GPXE_OPEN_H */ diff --git a/gpxe/src/include/gpxe/pci.h b/gpxe/src/include/gpxe/pci.h new file mode 100644 index 0000000..1ccdb10 --- /dev/null +++ b/gpxe/src/include/gpxe/pci.h @@ -0,0 +1,360 @@ +#ifndef _GPXE_PCI_H +#define _GPXE_PCI_H + +/* + * Support for NE2000 PCI clones added David Monro June 1997 + * Generalised for other PCI NICs by Ken Yap July 1997 + * PCI support rewritten by Michael Brown 2006 + * + * Most of this is taken from /usr/src/linux/include/linux/pci.h. + */ + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, or (at + * your option) any later version. + */ + +#include +#include +#include +#include +#include "pci_ids.h" + +/* + * PCI constants + * + */ + +#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ +#define PCI_COMMAND_MEM 0x2 /* Enable response in mem space */ +#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */ + +#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ +#define PCI_LATENCY_TIMER 0x0d /* 8 bits */ + +#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */ +#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */ +#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */ +#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */ +#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */ +#define PCI_COMMAND_SERR 0x100 /* Enable SERR */ +#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */ + + +#define PCI_VENDOR_ID 0x00 /* 16 bits */ +#define PCI_DEVICE_ID 0x02 /* 16 bits */ +#define PCI_COMMAND 0x04 /* 16 bits */ + +#define PCI_STATUS 0x06 /* 16 bits */ +#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ +#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */ +#define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */ +#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */ +#define PCI_STATUS_PARITY 0x100 /* Detected parity error */ +#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */ +#define PCI_STATUS_DEVSEL_FAST 0x000 +#define PCI_STATUS_DEVSEL_MEDIUM 0x200 +#define PCI_STATUS_DEVSEL_SLOW 0x400 +#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */ +#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */ +#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */ +#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */ +#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */ + +#define PCI_REVISION 0x08 /* 8 bits */ +#define PCI_REVISION_ID 0x08 /* 8 bits */ +#define PCI_CLASS_REVISION 0x08 /* 32 bits */ +#define PCI_CLASS_CODE 0x0b /* 8 bits */ +#define PCI_SUBCLASS_CODE 0x0a /* 8 bits */ +#define PCI_HEADER_TYPE 0x0e /* 8 bits */ +#define PCI_HEADER_TYPE_NORMAL 0 +#define PCI_HEADER_TYPE_BRIDGE 1 +#define PCI_HEADER_TYPE_CARDBUS 2 + + +/* Header type 0 (normal devices) */ +#define PCI_CARDBUS_CIS 0x28 +#define PCI_SUBSYSTEM_VENDOR_ID 0x2c +#define PCI_SUBSYSTEM_ID 0x2e + +#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */ +#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits */ +#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits */ +#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */ +#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */ +#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */ + +#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ +#define PCI_BASE_ADDRESS_SPACE_IO 0x01 +#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 + +#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06 +#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ +#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ +#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ +#define PCI_BASE_ADDRESS_MEM_MASK (~0x0f) +#define PCI_BASE_ADDRESS_IO_MASK (~0x03) +#define PCI_ROM_ADDRESS 0x30 /* 32 bits */ +#define PCI_ROM_ADDRESS_ENABLE 0x01 /* Write 1 to enable ROM, + bits 31..11 are address, + 10..2 are reserved */ + +#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */ + +#define PCI_INTERRUPT_LINE 0x3c /* IRQ number (0-15) */ +#define PCI_INTERRUPT_PIN 0x3d /* IRQ pin on PCI bus (A-D) */ + +/* Header type 1 (PCI-to-PCI bridges) */ +#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */ +#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */ +#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */ +#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */ +#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */ +#define PCI_IO_LIMIT 0x1d +#define PCI_IO_RANGE_TYPE_MASK 0x0f /* I/O bridging type */ +#define PCI_IO_RANGE_TYPE_16 0x00 +#define PCI_IO_RANGE_TYPE_32 0x01 +#define PCI_IO_RANGE_MASK ~0x0f +#define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */ +#define PCI_MEMORY_BASE 0x20 /* Memory range behind */ +#define PCI_MEMORY_LIMIT 0x22 +#define PCI_MEMORY_RANGE_TYPE_MASK 0x0f +#define PCI_MEMORY_RANGE_MASK ~0x0f +#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */ +#define PCI_PREF_MEMORY_LIMIT 0x26 +#define PCI_PREF_RANGE_TYPE_MASK 0x0f +#define PCI_PREF_RANGE_TYPE_32 0x00 +#define PCI_PREF_RANGE_TYPE_64 0x01 +#define PCI_PREF_RANGE_MASK ~0x0f +#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */ +#define PCI_PREF_LIMIT_UPPER32 0x2c +#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */ +#define PCI_IO_LIMIT_UPPER16 0x32 +/* 0x34 same as for htype 0 */ +/* 0x35-0x3b is reserved */ +#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */ +/* 0x3c-0x3d are same as for htype 0 */ +#define PCI_BRIDGE_CONTROL 0x3e +#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */ +#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */ +#define PCI_BRIDGE_CTL_NO_ISA 0x04 /* Disable bridging of ISA ports */ +#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */ +#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */ +#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */ +#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */ + +#define PCI_CB_CAPABILITY_LIST 0x14 + +/* Capability lists */ + +#define PCI_CAP_LIST_ID 0 /* Capability ID */ +#define PCI_CAP_ID_PM 0x01 /* Power Management */ +#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */ +#define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */ +#define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */ +#define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */ +#define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */ +#define PCI_CAP_ID_EXP 0x10 /* PCI Express */ +#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ +#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */ +#define PCI_CAP_SIZEOF 4 + +/* Power Management Registers */ + +#define PCI_PM_PMC 2 /* PM Capabilities Register */ +#define PCI_PM_CAP_VER_MASK 0x0007 /* Version */ +#define PCI_PM_CAP_PME_CLOCK 0x0008 /* PME clock required */ +#define PCI_PM_CAP_RESERVED 0x0010 /* Reserved field */ +#define PCI_PM_CAP_DSI 0x0020 /* Device specific initialization */ +#define PCI_PM_CAP_AUX_POWER 0x01C0 /* Auxilliary power support mask */ +#define PCI_PM_CAP_D1 0x0200 /* D1 power state support */ +#define PCI_PM_CAP_D2 0x0400 /* D2 power state support */ +#define PCI_PM_CAP_PME 0x0800 /* PME pin supported */ +#define PCI_PM_CAP_PME_MASK 0xF800 /* PME Mask of all supported states */ +#define PCI_PM_CAP_PME_D0 0x0800 /* PME# from D0 */ +#define PCI_PM_CAP_PME_D1 0x1000 /* PME# from D1 */ +#define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */ +#define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */ +#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */ +#define PCI_PM_CTRL 4 /* PM control and status register */ +#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */ +#define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */ +#define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* Data select (??) */ +#define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* Data scale (??) */ +#define PCI_PM_CTRL_PME_STATUS 0x8000 /* PME pin status */ +#define PCI_PM_PPB_EXTENSIONS 6 /* PPB support extensions (??) */ +#define PCI_PM_PPB_B2_B3 0x40 /* Stop clock when in D3hot (??) */ +#define PCI_PM_BPCC_ENABLE 0x80 /* Bus power/clock control enable (??) */ +#define PCI_PM_DATA_REGISTER 7 /* (??) */ +#define PCI_PM_SIZEOF 8 + +/* AGP registers */ + +#define PCI_AGP_VERSION 2 /* BCD version number */ +#define PCI_AGP_RFU 3 /* Rest of capability flags */ +#define PCI_AGP_STATUS 4 /* Status register */ +#define PCI_AGP_STATUS_RQ_MASK 0xff000000 /* Maximum number of requests - 1 */ +#define PCI_AGP_STATUS_SBA 0x0200 /* Sideband addressing supported */ +#define PCI_AGP_STATUS_64BIT 0x0020 /* 64-bit addressing supported */ +#define PCI_AGP_STATUS_FW 0x0010 /* FW transfers supported */ +#define PCI_AGP_STATUS_RATE4 0x0004 /* 4x transfer rate supported */ +#define PCI_AGP_STATUS_RATE2 0x0002 /* 2x transfer rate supported */ +#define PCI_AGP_STATUS_RATE1 0x0001 /* 1x transfer rate supported */ +#define PCI_AGP_COMMAND 8 /* Control register */ +#define PCI_AGP_COMMAND_RQ_MASK 0xff000000 /* Master: Maximum number of requests */ +#define PCI_AGP_COMMAND_SBA 0x0200 /* Sideband addressing enabled */ +#define PCI_AGP_COMMAND_AGP 0x0100 /* Allow processing of AGP transactions */ +#define PCI_AGP_COMMAND_64BIT 0x0020 /* Allow processing of 64-bit addresses */ +#define PCI_AGP_COMMAND_FW 0x0010 /* Force FW transfers */ +#define PCI_AGP_COMMAND_RATE4 0x0004 /* Use 4x rate */ +#define PCI_AGP_COMMAND_RATE2 0x0002 /* Use 2x rate */ +#define PCI_AGP_COMMAND_RATE1 0x0001 /* Use 1x rate */ +#define PCI_AGP_SIZEOF 12 + +/* Slot Identification */ + +#define PCI_SID_ESR 2 /* Expansion Slot Register */ +#define PCI_SID_ESR_NSLOTS 0x1f /* Number of expansion slots available */ +#define PCI_SID_ESR_FIC 0x20 /* First In Chassis Flag */ +#define PCI_SID_CHASSIS_NR 3 /* Chassis Number */ + +/* Message Signalled Interrupts registers */ + +#define PCI_MSI_FLAGS 2 /* Various flags */ +#define PCI_MSI_FLAGS_64BIT 0x80 /* 64-bit addresses allowed */ +#define PCI_MSI_FLAGS_QSIZE 0x70 /* Message queue size configured */ +#define PCI_MSI_FLAGS_QMASK 0x0e /* Maximum queue size available */ +#define PCI_MSI_FLAGS_ENABLE 0x01 /* MSI feature enabled */ +#define PCI_MSI_RFU 3 /* Rest of capability flags */ +#define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */ +#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */ +#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */ +#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ + +/** A PCI device ID list entry */ +struct pci_device_id { + /** Name */ + const char *name; + /** PCI vendor ID */ + uint16_t vendor; + /** PCI device ID */ + uint16_t device; +}; + +/** Match-anything ID */ +#define PCI_ANY_ID 0xffff + +/** A PCI device */ +struct pci_device { + /** Generic device */ + struct device dev; + /** Memory base + * + * This is the physical address of the first valid memory BAR. + */ + unsigned long membase; + /** + * I/O address + * + * This is the physical address of the first valid I/O BAR. + */ + unsigned long ioaddr; + /** Vendor ID */ + uint16_t vendor; + /** Device ID */ + uint16_t device; + /** Device class */ + uint32_t class; + /** Interrupt number */ + uint8_t irq; + /** Bus number */ + uint8_t bus; + /** Device and function number */ + uint8_t devfn; + /** Driver for this device */ + struct pci_driver *driver; + /** Driver-private data + * + * Use pci_set_drvdata() and pci_get_drvdata() to access this + * field. + */ + void *priv; + /** Driver name */ + const char *driver_name; +}; + +/** A PCI driver */ +struct pci_driver { + /** PCI ID table */ + struct pci_device_id *ids; + /** Number of entries in PCI ID table */ + unsigned int id_count; + /** + * Probe device + * + * @v pci PCI device + * @v id Matching entry in ID table + * @ret rc Return status code + */ + int ( * probe ) ( struct pci_device *pci, + const struct pci_device_id *id ); + /** + * Remove device + * + * @v pci PCI device + */ + void ( * remove ) ( struct pci_device *pci ); +}; + +/** Declare a PCI driver */ +#define __pci_driver __table ( struct pci_driver, pci_drivers, 01 ) + +#define PCI_DEVFN( slot, func ) ( ( (slot) << 3 ) | (func) ) +#define PCI_SLOT( devfn ) ( ( (devfn) >> 3 ) & 0x1f ) +#define PCI_FUNC( devfn ) ( (devfn) & 0x07 ) +#define PCI_BUSDEVFN( bus, devfn ) ( ( (bus) << 8 ) | (devfn) ) + +#define PCI_BASE_CLASS( class ) ( (class) >> 16 ) +#define PCI_SUB_CLASS( class ) ( ( (class) >> 8 ) & 0xff ) +#define PCI_PROG_INTF( class ) ( (class) & 0xff ) + +/* + * PCI_ROM is used to build up entries in a struct pci_id array. It + * is also parsed by parserom.pl to generate Makefile rules and files + * for rom-o-matic. + */ +#define PCI_ROM( _vendor, _device, _name, _description ) { \ + .vendor = _vendor, \ + .device = _device, \ + .name = _name, \ +} + +extern void adjust_pci_device ( struct pci_device *pci ); +extern unsigned long pci_bar_start ( struct pci_device *pci, + unsigned int reg ); +extern int pci_find_capability ( struct pci_device *pci, int capability ); +extern unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ); + +/** + * Set PCI driver-private data + * + * @v pci PCI device + * @v priv Private data + */ +static inline void pci_set_drvdata ( struct pci_device *pci, void *priv ) { + pci->priv = priv; +} + +/** + * Get PCI driver-private data + * + * @v pci PCI device + * @ret priv Private data + */ +static inline void * pci_get_drvdata ( struct pci_device *pci ) { + return pci->priv; +} + +#endif /* _GPXE_PCI_H */ diff --git a/gpxe/src/include/gpxe/pci_ids.h b/gpxe/src/include/gpxe/pci_ids.h new file mode 100644 index 0000000..075ff96 --- /dev/null +++ b/gpxe/src/include/gpxe/pci_ids.h @@ -0,0 +1,348 @@ +#ifndef _GPXE_PCI_IDS_H +#define _GPXE_PCI_IDS_H + +/* + * PCI Class, Vendor and Device IDs + * + * Please keep sorted. + */ + +/* Device classes and subclasses */ + +#define PCI_CLASS_NOT_DEFINED 0x0000 +#define PCI_CLASS_NOT_DEFINED_VGA 0x0001 + +#define PCI_BASE_CLASS_STORAGE 0x01 +#define PCI_CLASS_STORAGE_SCSI 0x0100 +#define PCI_CLASS_STORAGE_IDE 0x0101 +#define PCI_CLASS_STORAGE_FLOPPY 0x0102 +#define PCI_CLASS_STORAGE_IPI 0x0103 +#define PCI_CLASS_STORAGE_RAID 0x0104 +#define PCI_CLASS_STORAGE_OTHER 0x0180 + +#define PCI_BASE_CLASS_NETWORK 0x02 +#define PCI_CLASS_NETWORK_ETHERNET 0x0200 +#define PCI_CLASS_NETWORK_TOKEN_RING 0x0201 +#define PCI_CLASS_NETWORK_FDDI 0x0202 +#define PCI_CLASS_NETWORK_ATM 0x0203 +#define PCI_CLASS_NETWORK_OTHER 0x0280 + +#define PCI_BASE_CLASS_DISPLAY 0x03 +#define PCI_CLASS_DISPLAY_VGA 0x0300 +#define PCI_CLASS_DISPLAY_XGA 0x0301 +#define PCI_CLASS_DISPLAY_3D 0x0302 +#define PCI_CLASS_DISPLAY_OTHER 0x0380 + +#define PCI_BASE_CLASS_MULTIMEDIA 0x04 +#define PCI_CLASS_MULTIMEDIA_VIDEO 0x0400 +#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401 +#define PCI_CLASS_MULTIMEDIA_PHONE 0x0402 +#define PCI_CLASS_MULTIMEDIA_OTHER 0x0480 + +#define PCI_BASE_CLASS_MEMORY 0x05 +#define PCI_CLASS_MEMORY_RAM 0x0500 +#define PCI_CLASS_MEMORY_FLASH 0x0501 +#define PCI_CLASS_MEMORY_OTHER 0x0580 + +#define PCI_BASE_CLASS_BRIDGE 0x06 +#define PCI_CLASS_BRIDGE_HOST 0x0600 +#define PCI_CLASS_BRIDGE_ISA 0x0601 +#define PCI_CLASS_BRIDGE_EISA 0x0602 +#define PCI_CLASS_BRIDGE_MC 0x0603 +#define PCI_CLASS_BRIDGE_PCI 0x0604 +#define PCI_CLASS_BRIDGE_PCMCIA 0x0605 +#define PCI_CLASS_BRIDGE_NUBUS 0x0606 +#define PCI_CLASS_BRIDGE_CARDBUS 0x0607 +#define PCI_CLASS_BRIDGE_RACEWAY 0x0608 +#define PCI_CLASS_BRIDGE_OTHER 0x0680 + +#define PCI_BASE_CLASS_COMMUNICATION 0x07 +#define PCI_CLASS_COMMUNICATION_SERIAL 0x0700 +#define PCI_CLASS_COMMUNICATION_PARALLEL 0x0701 +#define PCI_CLASS_COMMUNICATION_MULTISERIAL 0x0702 +#define PCI_CLASS_COMMUNICATION_MODEM 0x0703 +#define PCI_CLASS_COMMUNICATION_OTHER 0x0780 + +#define PCI_BASE_CLASS_SYSTEM 0x08 +#define PCI_CLASS_SYSTEM_PIC 0x0800 +#define PCI_CLASS_SYSTEM_DMA 0x0801 +#define PCI_CLASS_SYSTEM_TIMER 0x0802 +#define PCI_CLASS_SYSTEM_RTC 0x0803 +#define PCI_CLASS_SYSTEM_PCI_HOTPLUG 0x0804 +#define PCI_CLASS_SYSTEM_OTHER 0x0880 + +#define PCI_BASE_CLASS_INPUT 0x09 +#define PCI_CLASS_INPUT_KEYBOARD 0x0900 +#define PCI_CLASS_INPUT_PEN 0x0901 +#define PCI_CLASS_INPUT_MOUSE 0x0902 +#define PCI_CLASS_INPUT_SCANNER 0x0903 +#define PCI_CLASS_INPUT_GAMEPORT 0x0904 +#define PCI_CLASS_INPUT_OTHER 0x0980 + +#define PCI_BASE_CLASS_DOCKING 0x0a +#define PCI_CLASS_DOCKING_GENERIC 0x0a00 +#define PCI_CLASS_DOCKING_OTHER 0x0a80 + +#define PCI_BASE_CLASS_PROCESSOR 0x0b +#define PCI_CLASS_PROCESSOR_386 0x0b00 +#define PCI_CLASS_PROCESSOR_486 0x0b01 +#define PCI_CLASS_PROCESSOR_PENTIUM 0x0b02 +#define PCI_CLASS_PROCESSOR_ALPHA 0x0b10 +#define PCI_CLASS_PROCESSOR_POWERPC 0x0b20 +#define PCI_CLASS_PROCESSOR_MIPS 0x0b30 +#define PCI_CLASS_PROCESSOR_CO 0x0b40 + +#define PCI_BASE_CLASS_SERIAL 0x0c +#define PCI_CLASS_SERIAL_FIREWIRE 0x0c00 +#define PCI_CLASS_SERIAL_ACCESS 0x0c01 +#define PCI_CLASS_SERIAL_SSA 0x0c02 +#define PCI_CLASS_SERIAL_USB 0x0c03 +#define PCI_CLASS_SERIAL_FIBER 0x0c04 +#define PCI_CLASS_SERIAL_SMBUS 0x0c05 + +#define PCI_BASE_CLASS_INTELLIGENT 0x0e +#define PCI_CLASS_INTELLIGENT_I2O 0x0e00 + +#define PCI_BASE_CLASS_SATELLITE 0x0f +#define PCI_CLASS_SATELLITE_TV 0x0f00 +#define PCI_CLASS_SATELLITE_AUDIO 0x0f01 +#define PCI_CLASS_SATELLITE_VOICE 0x0f03 +#define PCI_CLASS_SATELLITE_DATA 0x0f04 + +#define PCI_BASE_CLASS_CRYPT 0x10 +#define PCI_CLASS_CRYPT_NETWORK 0x1000 +#define PCI_CLASS_CRYPT_ENTERTAINMENT 0x1001 +#define PCI_CLASS_CRYPT_OTHER 0x1080 + +#define PCI_BASE_CLASS_SIGNAL_PROCESSING 0x11 +#define PCI_CLASS_SP_DPIO 0x1100 +#define PCI_CLASS_SP_OTHER 0x1180 + +#define PCI_CLASS_OTHERS 0xff + +/* Vendors */ + +#define PCI_VENDOR_ID_DYNALINK 0x0675 +#define PCI_VENDOR_ID_BERKOM 0x0871 +#define PCI_VENDOR_ID_COMPAQ 0x0e11 +#define PCI_VENDOR_ID_NCR 0x1000 +#define PCI_VENDOR_ID_LSI_LOGIC 0x1000 +#define PCI_VENDOR_ID_ATI 0x1002 +#define PCI_VENDOR_ID_VLSI 0x1004 +#define PCI_VENDOR_ID_ADL 0x1005 +#define PCI_VENDOR_ID_NS 0x100b +#define PCI_VENDOR_ID_TSENG 0x100c +#define PCI_VENDOR_ID_WEITEK 0x100e +#define PCI_VENDOR_ID_DEC 0x1011 +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#define PCI_VENDOR_ID_IBM 0x1014 +#define PCI_VENDOR_ID_COMPEX2 0x101a +/* pci.ids says "AT&T GIS (NCR)" */ +#define PCI_VENDOR_ID_WD 0x101c +#define PCI_VENDOR_ID_AMI 0x101e +#define PCI_VENDOR_ID_AMD 0x1022 +#define PCI_VENDOR_ID_TRIDENT 0x1023 +#define PCI_VENDOR_ID_AI 0x1025 +#define PCI_VENDOR_ID_DELL 0x1028 +#define PCI_VENDOR_ID_MATROX 0x102B +#define PCI_VENDOR_ID_CT 0x102c +#define PCI_VENDOR_ID_MIRO 0x1031 +#define PCI_VENDOR_ID_NEC 0x1033 +#define PCI_VENDOR_ID_FD 0x1036 +#define PCI_VENDOR_ID_SIS 0x1039 +#define PCI_VENDOR_ID_SI 0x1039 +#define PCI_VENDOR_ID_HP 0x103c +#define PCI_VENDOR_ID_PCTECH 0x1042 +#define PCI_VENDOR_ID_ASUSTEK 0x1043 +#define PCI_VENDOR_ID_DPT 0x1044 +#define PCI_VENDOR_ID_OPTI 0x1045 +#define PCI_VENDOR_ID_ELSA 0x1048 +#define PCI_VENDOR_ID_ELSA 0x1048 +#define PCI_VENDOR_ID_SGS 0x104a +#define PCI_VENDOR_ID_BUSLOGIC 0x104B +#define PCI_VENDOR_ID_TI 0x104c +#define PCI_VENDOR_ID_SONY 0x104d +#define PCI_VENDOR_ID_OAK 0x104e +/* Winbond have two vendor IDs! See 0x10ad as well */ +#define PCI_VENDOR_ID_WINBOND2 0x1050 +#define PCI_VENDOR_ID_ANIGMA 0x1051 +#define PCI_VENDOR_ID_EFAR 0x1055 +#define PCI_VENDOR_ID_MOTOROLA 0x1057 +#define PCI_VENDOR_ID_MOTOROLA_OOPS 0x1507 +#define PCI_VENDOR_ID_PROMISE 0x105a +#define PCI_VENDOR_ID_N9 0x105d +#define PCI_VENDOR_ID_UMC 0x1060 +#define PCI_VENDOR_ID_X 0x1061 +#define PCI_VENDOR_ID_MYLEX 0x1069 +#define PCI_VENDOR_ID_PICOP 0x1066 +#define PCI_VENDOR_ID_APPLE 0x106b +#define PCI_VENDOR_ID_YAMAHA 0x1073 +#define PCI_VENDOR_ID_NEXGEN 0x1074 +#define PCI_VENDOR_ID_QLOGIC 0x1077 +#define PCI_VENDOR_ID_CYRIX 0x1078 +#define PCI_VENDOR_ID_LEADTEK 0x107d +#define PCI_VENDOR_ID_INTERPHASE 0x107e +#define PCI_VENDOR_ID_CONTAQ 0x1080 +#define PCI_VENDOR_ID_FOREX 0x1083 +#define PCI_VENDOR_ID_OLICOM 0x108d +#define PCI_VENDOR_ID_SUN 0x108e +#define PCI_VENDOR_ID_CMD 0x1095 +#define PCI_VENDOR_ID_VISION 0x1098 +#define PCI_VENDOR_ID_BROOKTREE 0x109e +#define PCI_VENDOR_ID_SIERRA 0x10a8 +#define PCI_VENDOR_ID_SGI 0x10a9 +#define PCI_VENDOR_ID_ACC 0x10aa +#define PCI_VENDOR_ID_WINBOND 0x10ad +#define PCI_VENDOR_ID_DATABOOK 0x10b3 +#define PCI_VENDOR_ID_PLX 0x10b5 +#define PCI_VENDOR_ID_MADGE 0x10b6 +#define PCI_VENDOR_ID_3COM 0x10b7 +#define PCI_VENDOR_ID_SMC 0x10b8 +#define PCI_VENDOR_ID_SUNDANCE 0x13F0 +#define PCI_VENDOR_ID_AL 0x10b9 +#define PCI_VENDOR_ID_MITSUBISHI 0x10ba +#define PCI_VENDOR_ID_SURECOM 0x10bd +#define PCI_VENDOR_ID_NEOMAGIC 0x10c8 +#define PCI_VENDOR_ID_ASP 0x10cd +#define PCI_VENDOR_ID_MACRONIX 0x10d9 +#define PCI_VENDOR_ID_TCONRAD 0x10da +#define PCI_VENDOR_ID_CERN 0x10dc +#define PCI_VENDOR_ID_NVIDIA 0x10de +#define PCI_VENDOR_ID_IMS 0x10e0 +#define PCI_VENDOR_ID_TEKRAM2 0x10e1 +#define PCI_VENDOR_ID_TUNDRA 0x10e3 +#define PCI_VENDOR_ID_AMCC 0x10e8 +#define PCI_VENDOR_ID_INTERG 0x10ea +#define PCI_VENDOR_ID_REALTEK 0x10ec +#define PCI_VENDOR_ID_XILINX 0x10ee +#define PCI_VENDOR_ID_TRUEVISION 0x10fa +#define PCI_VENDOR_ID_INIT 0x1101 +#define PCI_VENDOR_ID_CREATIVE 0x1102 +/* duplicate: ECTIVA */ +#define PCI_VENDOR_ID_ECTIVA 0x1102 +/* duplicate: CREATIVE */ +#define PCI_VENDOR_ID_TTI 0x1103 +#define PCI_VENDOR_ID_VIA 0x1106 +#define PCI_VENDOR_ID_VIATEC 0x1106 +#define PCI_VENDOR_ID_SIEMENS 0x110A +#define PCI_VENDOR_ID_SMC2 0x1113 +#define PCI_VENDOR_ID_VORTEX 0x1119 +#define PCI_VENDOR_ID_EF 0x111a +#define PCI_VENDOR_ID_IDT 0x111d +#define PCI_VENDOR_ID_FORE 0x1127 +#define PCI_VENDOR_ID_IMAGINGTECH 0x112f +#define PCI_VENDOR_ID_PHILIPS 0x1131 +#define PCI_VENDOR_ID_EICON 0x1133 +#define PCI_VENDOR_ID_CYCLONE 0x113c +#define PCI_VENDOR_ID_ALLIANCE 0x1142 +#define PCI_VENDOR_ID_SYSKONNECT 0x1148 +#define PCI_VENDOR_ID_VMIC 0x114a +#define PCI_VENDOR_ID_DIGI 0x114f +#define PCI_VENDOR_ID_MUTECH 0x1159 +#define PCI_VENDOR_ID_XIRCOM 0x115d +#define PCI_VENDOR_ID_RENDITION 0x1163 +#define PCI_VENDOR_ID_SERVERWORKS 0x1166 +#define PCI_VENDOR_ID_SBE 0x1176 +#define PCI_VENDOR_ID_TOSHIBA 0x1179 +#define PCI_VENDOR_ID_RICOH 0x1180 +#define PCI_VENDOR_ID_DLINK 0x1186 +#define PCI_VENDOR_ID_ARTOP 0x1191 +#define PCI_VENDOR_ID_ZEITNET 0x1193 +#define PCI_VENDOR_ID_OMEGA 0x119b +#define PCI_VENDOR_ID_FUJITSU_ME 0x119e +#define PCI_SUBVENDOR_ID_KEYSPAN 0x11a9 +#define PCI_VENDOR_ID_GALILEO 0x11ab +#define PCI_VENDOR_ID_LINKSYS 0x11ad +#define PCI_VENDOR_ID_LITEON 0x11ad +#define PCI_VENDOR_ID_V3 0x11b0 +#define PCI_VENDOR_ID_NP 0x11bc +#define PCI_VENDOR_ID_ATT 0x11c1 +#define PCI_VENDOR_ID_SPECIALIX 0x11cb +#define PCI_VENDOR_ID_AURAVISION 0x11d1 +#define PCI_VENDOR_ID_ANALOG_DEVICES 0x11d4 +#define PCI_VENDOR_ID_IKON 0x11d5 +#define PCI_VENDOR_ID_ZORAN 0x11de +#define PCI_VENDOR_ID_KINETIC 0x11f4 +#define PCI_VENDOR_ID_COMPEX 0x11f6 +#define PCI_VENDOR_ID_RP 0x11fe +#define PCI_VENDOR_ID_CYCLADES 0x120e +#define PCI_VENDOR_ID_ESSENTIAL 0x120f +#define PCI_VENDOR_ID_O2 0x1217 +#define PCI_VENDOR_ID_3DFX 0x121a +#define PCI_VENDOR_ID_SIGMADES 0x1236 +#define PCI_VENDOR_ID_CCUBE 0x123f +#define PCI_VENDOR_ID_AVM 0x1244 +#define PCI_VENDOR_ID_DIPIX 0x1246 +#define PCI_VENDOR_ID_STALLION 0x124d +#define PCI_VENDOR_ID_OPTIBASE 0x1255 +#define PCI_VENDOR_ID_ESS 0x125d +#define PCI_VENDOR_ID_HARRIS 0x1260 +#define PCI_VENDOR_ID_SATSAGEM 0x1267 +#define PCI_VENDOR_ID_HUGHES 0x1273 +#define PCI_VENDOR_ID_ENSONIQ 0x1274 +#define PCI_VENDOR_ID_ROCKWELL 0x127A +#define PCI_VENDOR_ID_DAVICOM 0x1282 +#define PCI_VENDOR_ID_ITE 0x1283 +/* formerly Platform Tech */ +#define PCI_VENDOR_ID_ESS_OLD 0x1285 +#define PCI_VENDOR_ID_ALTEON 0x12ae +#define PCI_VENDOR_ID_USR 0x12B9 +#define PCI_VENDOR_ID_HOLTEK 0x12c3 +#define PCI_SUBVENDOR_ID_CONNECT_TECH 0x12c4 +#define PCI_VENDOR_ID_PICTUREL 0x12c5 +#define PCI_VENDOR_ID_NVIDIA_SGS 0x12d2 +#define PCI_SUBVENDOR_ID_CHASE_PCIFAST 0x12E0 +#define PCI_SUBVENDOR_ID_CHASE_PCIRAS 0x124D +#define PCI_VENDOR_ID_AUREAL 0x12eb +#define PCI_VENDOR_ID_CBOARDS 0x1307 +#define PCI_VENDOR_ID_SIIG 0x131f +#define PCI_VENDOR_ID_ADMTEK 0x1317 +#define PCI_VENDOR_ID_DOMEX 0x134a +#define PCI_VENDOR_ID_QUATECH 0x135C +#define PCI_VENDOR_ID_SEALEVEL 0x135e +#define PCI_VENDOR_ID_HYPERCOPE 0x1365 +#define PCI_VENDOR_ID_KAWASAKI 0x136b +#define PCI_VENDOR_ID_LMC 0x1376 +#define PCI_VENDOR_ID_NETGEAR 0x1385 +#define PCI_VENDOR_ID_APPLICOM 0x1389 +#define PCI_VENDOR_ID_MOXA 0x1393 +#define PCI_VENDOR_ID_CCD 0x1397 +#define PCI_VENDOR_ID_MICROGATE 0x13c0 +#define PCI_VENDOR_ID_3WARE 0x13C1 +#define PCI_VENDOR_ID_ABOCOM 0x13D1 +#define PCI_VENDOR_ID_CMEDIA 0x13f6 +#define PCI_VENDOR_ID_LAVA 0x1407 +#define PCI_VENDOR_ID_TIMEDIA 0x1409 +#define PCI_VENDOR_ID_OXSEMI 0x1415 +#define PCI_VENDOR_ID_AIRONET 0x14b9 +#define PCI_VENDOR_ID_TITAN 0x14D2 +#define PCI_VENDOR_ID_PANACOM 0x14d4 +#define PCI_VENDOR_ID_BROADCOM 0x14e4 +#define PCI_VENDOR_ID_SYBA 0x1592 +#define PCI_VENDOR_ID_MORETON 0x15aa +#define PCI_VENDOR_ID_ZOLTRIX 0x15b0 +#define PCI_VENDOR_ID_PDC 0x15e9 +#define PCI_VENDOR_ID_FSC 0x1734 +#define PCI_VENDOR_ID_SYMPHONY 0x1c1c +#define PCI_VENDOR_ID_TEKRAM 0x1de1 +#define PCI_VENDOR_ID_3DLABS 0x3d3d +#define PCI_VENDOR_ID_AVANCE 0x4005 +#define PCI_VENDOR_ID_AKS 0x416c +#define PCI_VENDOR_ID_NETVIN 0x4a14 +#define PCI_VENDOR_ID_S3 0x5333 +#define PCI_VENDOR_ID_DCI 0x6666 +#define PCI_VENDOR_ID_GENROCO 0x5555 +#define PCI_VENDOR_ID_INTEL 0x8086 +#define PCI_VENDOR_ID_COMPUTONE 0x8e0e +#define PCI_SUBVENDOR_ID_COMPUTONE 0x8e0e +#define PCI_VENDOR_ID_KTI 0x8e2e +#define PCI_VENDOR_ID_ADAPTEC 0x9004 +#define PCI_VENDOR_ID_ADAPTEC2 0x9005 +#define PCI_VENDOR_ID_ATRONICS 0x907f +#define PCI_VENDOR_ID_HOLTEK2 0x9412 +#define PCI_VENDOR_ID_NETMOS 0x9710 +#define PCI_SUBVENDOR_ID_EXSYS 0xd84d +#define PCI_VENDOR_ID_TIGERJET 0xe159 +#define PCI_VENDOR_ID_ARK 0xedd8 + +#endif /* _GPXE_PCI_IDS_H */ diff --git a/gpxe/src/include/gpxe/pci_io.h b/gpxe/src/include/gpxe/pci_io.h new file mode 100644 index 0000000..365166c --- /dev/null +++ b/gpxe/src/include/gpxe/pci_io.h @@ -0,0 +1,122 @@ +#ifndef _GPXE_PCI_IO_H +#define _GPXE_PCI_IO_H + +/** @file + * + * PCI I/O API + * + */ + +#include +#include +#include + +/** + * Calculate static inline PCI I/O API function name + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @ret _subsys_func Subsystem API function + */ +#define PCIAPI_INLINE( _subsys, _api_func ) \ + SINGLE_API_INLINE ( PCIAPI_PREFIX_ ## _subsys, _api_func ) + +/** + * Provide a PCI I/O API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @v _func Implementing function + */ +#define PROVIDE_PCIAPI( _subsys, _api_func, _func ) \ + PROVIDE_SINGLE_API ( PCIAPI_PREFIX_ ## _subsys, _api_func, _func ) + +/** + * Provide a static inline PCI I/O API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + */ +#define PROVIDE_PCIAPI_INLINE( _subsys, _api_func ) \ + PROVIDE_SINGLE_API_INLINE ( PCIAPI_PREFIX_ ## _subsys, _api_func ) + +/* Include all architecture-independent I/O API headers */ +#include + +/* Include all architecture-dependent I/O API headers */ +#include + +/** + * Determine maximum PCI bus number within system + * + * @ret max_bus Maximum bus number + */ +int pci_max_bus ( void ); + +/** + * Read byte from PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +int pci_read_config_byte ( struct pci_device *pci, unsigned int where, + uint8_t *value ); + +/** + * Read 16-bit word from PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +int pci_read_config_word ( struct pci_device *pci, unsigned int where, + uint16_t *value ); + +/** + * Read 32-bit dword from PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value read + * @ret rc Return status code + */ +int pci_read_config_dword ( struct pci_device *pci, unsigned int where, + uint32_t *value ); + +/** + * Write byte to PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +int pci_write_config_byte ( struct pci_device *pci, unsigned int where, + uint8_t value ); + +/** + * Write 16-bit word to PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +int pci_write_config_word ( struct pci_device *pci, unsigned int where, + uint16_t value ); + +/** + * Write 32-bit dword to PCI configuration space + * + * @v pci PCI device + * @v where Location within PCI configuration space + * @v value Value to be written + * @ret rc Return status code + */ +int pci_write_config_dword ( struct pci_device *pci, unsigned int where, + uint32_t value ); + +#endif /* _GPXE_PCI_IO_H */ diff --git a/gpxe/src/include/gpxe/posix_io.h b/gpxe/src/include/gpxe/posix_io.h new file mode 100644 index 0000000..9984db0 --- /dev/null +++ b/gpxe/src/include/gpxe/posix_io.h @@ -0,0 +1,85 @@ +#ifndef _GPXE_POSIX_IO_H +#define _GPXE_POSIX_IO_H + +/** @file + * + * POSIX-like I/O + * + */ + +#include +#include + +/** Minimum file descriptor that will ever be allocated */ +#define POSIX_FD_MIN ( 1 ) + +/** Maximum file descriptor that will ever be allocated */ +#define POSIX_FD_MAX ( 31 ) + +/** File descriptor set as used for select() */ +typedef uint32_t fd_set; + +extern int open ( const char *uri_string ); +extern ssize_t read_user ( int fd, userptr_t buffer, + off_t offset, size_t len ); +extern int select ( fd_set *readfds, int wait ); +extern ssize_t fsize ( int fd ); +extern int close ( int fd ); + +/** + * Zero a file descriptor set + * + * @v set File descriptor set + */ +static inline __attribute__ (( always_inline )) void +FD_ZERO ( fd_set *set ) { + *set = 0; +} + +/** + * Set a bit within a file descriptor set + * + * @v fd File descriptor + * @v set File descriptor set + */ +static inline __attribute__ (( always_inline )) void +FD_SET ( int fd, fd_set *set ) { + *set |= ( 1 << fd ); +} + +/** + * Clear a bit within a file descriptor set + * + * @v fd File descriptor + * @v set File descriptor set + */ +static inline __attribute__ (( always_inline )) void +FD_CLR ( int fd, fd_set *set ) { + *set &= ~( 1 << fd ); +} + +/** + * Test a bit within a file descriptor set + * + * @v fd File descriptor + * @v set File descriptor set + * @ret is_set Corresponding bit is set + */ +static inline __attribute__ (( always_inline )) int +FD_ISSET ( int fd, fd_set *set ) { + return ( *set & ( 1 << fd ) ); +} + +/** + * Read data from file + * + * @v fd File descriptor + * @v buf Data buffer + * @v len Maximum length to read + * @ret len Actual length read, or negative error number + */ +static inline ssize_t read ( int fd, void *buf, size_t len ) { + return read_user ( fd, virt_to_user ( buf ), 0, len ); +} + +#endif /* _GPXE_POSIX_IO_H */ diff --git a/gpxe/src/include/gpxe/process.h b/gpxe/src/include/gpxe/process.h new file mode 100644 index 0000000..8d9b109 --- /dev/null +++ b/gpxe/src/include/gpxe/process.h @@ -0,0 +1,75 @@ +#ifndef _GPXE_PROCESS_H +#define _GPXE_PROCESS_H + +/** @file + * + * Processes + * + */ + +#include +#include +#include + +/** A process */ +struct process { + /** List of processes */ + struct list_head list; + /** + * Single-step the process + * + * This method should execute a single step of the process. + * Returning from this method is isomorphic to yielding the + * CPU to another process. + */ + void ( * step ) ( struct process *process ); + /** Reference counter + * + * If this interface is not part of a reference-counted + * object, this field may be NULL. + */ + struct refcnt *refcnt; +}; + +extern void process_add ( struct process *process ); +extern void process_del ( struct process *process ); +extern void step ( void ); + +/** + * Initialise process without adding to process list + * + * @v process Process + * @v step Process' step() method + */ +static inline __attribute__ (( always_inline )) void +process_init_stopped ( struct process *process, + void ( * step ) ( struct process *process ), + struct refcnt *refcnt ) { + process->step = step; + process->refcnt = refcnt; +} + +/** + * Initialise process and add to process list + * + * @v process Process + * @v step Process' step() method + */ +static inline __attribute__ (( always_inline )) void +process_init ( struct process *process, + void ( * step ) ( struct process *process ), + struct refcnt *refcnt ) { + process_init_stopped ( process, step, refcnt ); + process_add ( process ); +} + +/** + * Declare a permanent process + * + * Permanent processes will be automatically added to the process list + * at initialisation time. + */ +#define __permanent_process \ + __table ( struct process, processes, 01 ) + +#endif /* _GPXE_PROCESS_H */ diff --git a/gpxe/src/include/gpxe/profile.h b/gpxe/src/include/gpxe/profile.h new file mode 100644 index 0000000..d46ca05 --- /dev/null +++ b/gpxe/src/include/gpxe/profile.h @@ -0,0 +1,78 @@ +#ifndef _GPXE_PROFILE_H +#define _GPXE_PROFILE_H + +/** @file + * + * Profiling + * + */ + +#include + +/** + * A data structure for storing profiling information + */ +union profiler { + /** Timestamp (in CPU-specific "ticks") */ + uint64_t timestamp; + /** Registers returned by rdtsc. + * + * This part should really be architecture-specific code. + */ + struct { + uint32_t eax; + uint32_t edx; + } rdtsc; +}; + +/** + * Static per-object profiler, for use with simple_profile() + */ +static union profiler simple_profiler; + +/** + * Perform profiling + * + * @v profiler Profiler data structure + * @ret delta Elapsed ticks since last call to profile(). + * + * Call profile() both before and after the code you wish to measure. + * The "after" call will return the measurement. For example: + * + * @code + * + * profile ( &profiler ); + * ... do something here ... + * printf ( "It took %ld ticks to execute\n", profile ( &profiler ) ); + * + * @endcode + */ +static inline __attribute__ (( always_inline )) unsigned long +profile ( union profiler *profiler ) { + uint64_t last_timestamp = profiler->timestamp; + + __asm__ __volatile__ ( "rdtsc" : + "=a" ( profiler->rdtsc.eax ), + "=d" ( profiler->rdtsc.edx ) ); + return ( profiler->timestamp - last_timestamp ); +} + +/** + * Perform profiling + * + * @ret delta Elapsed ticks since last call to profile(). + * + * When you only need one profiler, you can avoid the hassle of + * creating your own @c profiler data structure by using + * simple_profile() instead. + * + * simple_profile() is equivalent to profile(&simple_profiler), where + * @c simple_profiler is a @c profiler data structure that is static + * to each object which includes @c profile.h. + */ +static inline __attribute__ (( always_inline )) unsigned long +simple_profile ( void ) { + return profile ( &simple_profiler ); +} + +#endif /* _GPXE_PROFILE_H */ diff --git a/gpxe/src/include/gpxe/ramdisk.h b/gpxe/src/include/gpxe/ramdisk.h new file mode 100644 index 0000000..4a77f05 --- /dev/null +++ b/gpxe/src/include/gpxe/ramdisk.h @@ -0,0 +1,22 @@ +#ifndef _GPXE_RAMDISK_H +#define _GPXE_RAMDISK_H + +/** + * @file + * + * RAM disks + * + */ + +#include +#include + +struct ramdisk { + struct block_device blockdev; + userptr_t data; +}; + +extern int init_ramdisk ( struct ramdisk *ramdisk, userptr_t data, size_t len, + unsigned int blksize ); + +#endif /* _GPXE_RAMDISK_H */ diff --git a/gpxe/src/include/gpxe/rarp.h b/gpxe/src/include/gpxe/rarp.h new file mode 100644 index 0000000..81e03bd --- /dev/null +++ b/gpxe/src/include/gpxe/rarp.h @@ -0,0 +1,14 @@ +#ifndef _GPXE_RARP_H +#define _GPXE_RARP_H + +/** @file + * + * Reverse Address Resolution Protocol + * + */ + +struct net_protocol; + +extern struct net_protocol rarp_protocol; + +#endif /* _GPXE_RARP_H */ diff --git a/gpxe/src/include/gpxe/refcnt.h b/gpxe/src/include/gpxe/refcnt.h new file mode 100644 index 0000000..68e0fd4 --- /dev/null +++ b/gpxe/src/include/gpxe/refcnt.h @@ -0,0 +1,44 @@ +#ifndef _GPXE_REFCNT_H +#define _GPXE_REFCNT_H + +/** @file + * + * Reference counting + * + */ + +/** + * A reference counter + * + * This data structure is designed to be embedded within a + * reference-counted object. + * + * Reference-counted objects are freed when their reference count + * drops below zero. This means that a freshly allocated-and-zeroed + * reference-counted object will be freed on the first call to + * ref_put(). + */ +struct refcnt { + /** Current reference count + * + * When this count is decremented below zero, the free() + * method will be called. + */ + int refcnt; + /** Free containing object + * + * This method is called when the reference count is + * decremented below zero. + * + * If this method is left NULL, the standard library free() + * function will be called. The upshot of this is that you + * may omit the free() method if the @c refcnt object is the + * first element of your reference-counted struct. + */ + void ( * free ) ( struct refcnt *refcnt ); +}; + +extern struct refcnt * ref_get ( struct refcnt *refcnt ); +extern void ref_put ( struct refcnt *refcnt ); + +#endif /* _GPXE_REFCNT_H */ diff --git a/gpxe/src/include/gpxe/resolv.h b/gpxe/src/include/gpxe/resolv.h new file mode 100644 index 0000000..e73c820 --- /dev/null +++ b/gpxe/src/include/gpxe/resolv.h @@ -0,0 +1,166 @@ +#ifndef _GPXE_RESOLV_H +#define _GPXE_RESOLV_H + +/** @file + * + * Name resolution + * + */ + +#include +#include +#include +#include + +struct resolv_interface; + +/** Name resolution interface operations */ +struct resolv_interface_operations { + /** Name resolution completed + * + * @v resolv Name resolution interface + * @v sa Completed socket address (if successful) + * @v rc Final status code + */ + void ( * done ) ( struct resolv_interface *resolv, + struct sockaddr *sa, int rc ); +}; + +/** A name resolution interface */ +struct resolv_interface { + /** Generic object communication interface */ + struct interface intf; + /** Operations for received messages */ + struct resolv_interface_operations *op; +}; + +extern struct resolv_interface null_resolv; +extern struct resolv_interface_operations null_resolv_ops; + +/** + * Initialise a name resolution interface + * + * @v resolv Name resolution interface + * @v op Name resolution interface operations + * @v refcnt Containing object reference counter, or NULL + */ +static inline void resolv_init ( struct resolv_interface *resolv, + struct resolv_interface_operations *op, + struct refcnt *refcnt ) { + resolv->intf.dest = &null_resolv.intf; + resolv->intf.refcnt = refcnt; + resolv->op = op; +} + +/** + * Get name resolution interface from generic object communication interface + * + * @v intf Generic object communication interface + * @ret resolv Name resolution interface + */ +static inline __attribute__ (( always_inline )) struct resolv_interface * +intf_to_resolv ( struct interface *intf ) { + return container_of ( intf, struct resolv_interface, intf ); +} + +/** + * Get reference to destination name resolution interface + * + * @v resolv Name resolution interface + * @ret dest Destination interface + */ +static inline __attribute__ (( always_inline )) struct resolv_interface * +resolv_get_dest ( struct resolv_interface *resolv ) { + return intf_to_resolv ( intf_get ( resolv->intf.dest ) ); +} + +/** + * Drop reference to name resolution interface + * + * @v resolv name resolution interface + */ +static inline __attribute__ (( always_inline )) void +resolv_put ( struct resolv_interface *resolv ) { + intf_put ( &resolv->intf ); +} + +/** + * Plug a name resolution interface into a new destination interface + * + * @v resolv Name resolution interface + * @v dest New destination interface + */ +static inline __attribute__ (( always_inline )) void +resolv_plug ( struct resolv_interface *resolv, struct resolv_interface *dest ) { + plug ( &resolv->intf, &dest->intf ); +} + +/** + * Plug two name resolution interfaces together + * + * @v a Name resolution interface A + * @v b Name resolution interface B + */ +static inline __attribute__ (( always_inline )) void +resolv_plug_plug ( struct resolv_interface *a, struct resolv_interface *b ) { + plug_plug ( &a->intf, &b->intf ); +} + +/** + * Unplug a name resolution interface + * + * @v resolv Name resolution interface + */ +static inline __attribute__ (( always_inline )) void +resolv_unplug ( struct resolv_interface *resolv ) { + plug ( &resolv->intf, &null_resolv.intf ); +} + +/** + * Stop using a name resolution interface + * + * @v resolv Name resolution interface + * + * After calling this method, no further messages will be received via + * the interface. + */ +static inline void resolv_nullify ( struct resolv_interface *resolv ) { + resolv->op = &null_resolv_ops; +}; + +/** A name resolver */ +struct resolver { + /** Name of this resolver (e.g. "DNS") */ + const char *name; + /** Start name resolution + * + * @v resolv Name resolution interface + * @v name Name to resolve + * @v sa Socket address to complete + * @ret rc Return status code + */ + int ( * resolv ) ( struct resolv_interface *resolv, const char *name, + struct sockaddr *sa ); +}; + +/** Numeric resolver priority */ +#define RESOLV_NUMERIC 01 + +/** Normal resolver priority */ +#define RESOLV_NORMAL 02 + +/** Register as a name resolver */ +#define __resolver( resolv_order ) \ + __table ( struct resolver, resolvers, resolv_order ) + +extern void resolv_done ( struct resolv_interface *resolv, + struct sockaddr *sa, int rc ); +extern void ignore_resolv_done ( struct resolv_interface *resolv, + struct sockaddr *sa, int rc ); +extern struct resolv_interface_operations null_resolv_ops; +extern struct resolv_interface null_resolv; + +extern int resolv ( struct resolv_interface *resolv, const char *name, + struct sockaddr *sa ); + +#endif /* _GPXE_RESOLV_H */ diff --git a/gpxe/src/include/gpxe/retry.h b/gpxe/src/include/gpxe/retry.h new file mode 100644 index 0000000..1e7fde4 --- /dev/null +++ b/gpxe/src/include/gpxe/retry.h @@ -0,0 +1,79 @@ +#ifndef _GPXE_RETRY_H +#define _GPXE_RETRY_H + +/** @file + * + * Retry timers + * + */ + +#include + +/** Default timeout value */ +#define DEFAULT_MIN_TIMEOUT ( TICKS_PER_SEC / 4 ) + +/** Limit after which the timeout will be deemed permanent */ +#define DEFAULT_MAX_TIMEOUT ( 10 * TICKS_PER_SEC ) + +/** A retry timer */ +struct retry_timer { + /** List of active timers */ + struct list_head list; + /** Timer is currently running */ + unsigned int running; + /** Timeout value (in ticks) */ + unsigned long timeout; + /** Minimum timeout value (in ticks) + * + * A value of zero means "use default timeout." + */ + unsigned long min_timeout; + /** Maximum timeout value before failure (in ticks) + * + * A value of zero means "use default timeout." + */ + unsigned long max_timeout; + /** Start time (in ticks) */ + unsigned long start; + /** Retry count */ + unsigned int count; + /** Timer expired callback + * + * @v timer Retry timer + * @v fail Failure indicator + * + * The timer will already be stopped when this method is + * called. The failure indicator will be True if the retry + * timeout has already exceeded @c MAX_TIMEOUT. + */ + void ( * expired ) ( struct retry_timer *timer, int over ); +}; + +extern void start_timer ( struct retry_timer *timer ); +extern void start_timer_fixed ( struct retry_timer *timer, + unsigned long timeout ); +extern void stop_timer ( struct retry_timer *timer ); + +/** + * Start timer with no delay + * + * @v timer Retry timer + * + * This starts the timer running with a zero timeout value. + */ +static inline void start_timer_nodelay ( struct retry_timer *timer ) { + start_timer_fixed ( timer, 0 ); +} + +/** + * Test to see if timer is currently running + * + * @v timer Retry timer + * @ret running Non-zero if timer is running + */ +static inline __attribute__ (( always_inline )) unsigned long +timer_running ( struct retry_timer *timer ) { + return ( timer->running ); +} + +#endif /* _GPXE_RETRY_H */ diff --git a/gpxe/src/include/gpxe/rotate.h b/gpxe/src/include/gpxe/rotate.h new file mode 100644 index 0000000..42ec719 --- /dev/null +++ b/gpxe/src/include/gpxe/rotate.h @@ -0,0 +1,27 @@ +#ifndef _GPXE_ROTATE_H +#define _GPXE_ROTATE_H + +/** @file + * + * Bit operations + */ + +#include + +static inline uint32_t rol32 ( uint32_t data, unsigned int rotation ) { + return ( ( data << rotation ) | ( data >> ( 32 - rotation ) ) ); +} + +static inline uint32_t ror32 ( uint32_t data, unsigned int rotation ) { + return ( ( data >> rotation ) | ( data << ( 32 - rotation ) ) ); +} + +static inline uint64_t rol64 ( uint64_t data, unsigned int rotation ) { + return ( ( data << rotation ) | ( data >> ( 64 - rotation ) ) ); +} + +static inline uint64_t ror64 ( uint64_t data, unsigned int rotation ) { + return ( ( data >> rotation ) | ( data << ( 64 - rotation ) ) ); +} + +#endif /* _GPXE_ROTATE_H */ diff --git a/gpxe/src/include/gpxe/rsa.h b/gpxe/src/include/gpxe/rsa.h new file mode 100644 index 0000000..e30e1a5 --- /dev/null +++ b/gpxe/src/include/gpxe/rsa.h @@ -0,0 +1,10 @@ +#ifndef _GPXE_RSA_H +#define _GPXE_RSA_H + +struct pubkey_algorithm; + +extern struct pubkey_algorithm rsa_algorithm; + +#include "crypto/axtls/crypto.h" + +#endif /* _GPXE_RSA_H */ diff --git a/gpxe/src/include/gpxe/sanboot.h b/gpxe/src/include/gpxe/sanboot.h new file mode 100644 index 0000000..ea26a35 --- /dev/null +++ b/gpxe/src/include/gpxe/sanboot.h @@ -0,0 +1,14 @@ +#ifndef _GPXE_SANBOOT_H +#define _GPXE_SANBOOT_H + +#include + +struct sanboot_protocol { + const char *prefix; + int ( * boot ) ( const char *root_path ); +}; + +#define __sanboot_protocol \ + __table ( struct sanboot_protocol, sanboot_protocols, 01 ) + +#endif /* _GPXE_SANBOOT_H */ diff --git a/gpxe/src/include/gpxe/scsi.h b/gpxe/src/include/gpxe/scsi.h new file mode 100644 index 0000000..e820117 --- /dev/null +++ b/gpxe/src/include/gpxe/scsi.h @@ -0,0 +1,270 @@ +#ifndef _GPXE_SCSI_H +#define _GPXE_SCSI_H + +#include +#include +#include +#include + +/** @file + * + * SCSI devices + * + */ + +/** + * @defgroup scsiops SCSI operation codes + * @{ + */ + +#define SCSI_OPCODE_READ_10 0x28 /**< READ (10) */ +#define SCSI_OPCODE_READ_16 0x88 /**< READ (16) */ +#define SCSI_OPCODE_WRITE_10 0x2a /**< WRITE (10) */ +#define SCSI_OPCODE_WRITE_16 0x8a /**< WRITE (16) */ +#define SCSI_OPCODE_READ_CAPACITY_10 0x25 /**< READ CAPACITY (10) */ +#define SCSI_OPCODE_SERVICE_ACTION_IN 0x9e /**< SERVICE ACTION IN */ +#define SCSI_SERVICE_ACTION_READ_CAPACITY_16 0x10 /**< READ CAPACITY (16) */ + +/** @} */ + +/** + * @defgroup scsiflags SCSI flags + * @{ + */ + +#define SCSI_FL_FUA_NV 0x02 /**< Force unit access to NVS */ +#define SCSI_FL_FUA 0x08 /**< Force unit access */ +#define SCSI_FL_DPO 0x10 /**< Disable cache page out */ + +/** @} */ + +/** + * @defgroup scsicdbs SCSI command data blocks + * @{ + */ + +/** A SCSI "READ (10)" CDB */ +struct scsi_cdb_read_10 { + /** Opcode (0x28) */ + uint8_t opcode; + /** Flags */ + uint8_t flags; + /** Start address + * + * This is a logical block number, in big-endian order. + */ + uint32_t lba; + /** Group number */ + uint8_t group; + /** Transfer length + * + * This is a logical block count, in big-endian order. + */ + uint16_t len; + /** Control byte */ + uint8_t control; +} __attribute__ (( packed )); + +/** A SCSI "READ (16)" CDB */ +struct scsi_cdb_read_16 { + /** Opcode (0x88) */ + uint8_t opcode; + /** Flags */ + uint8_t flags; + /** Start address + * + * This is a logical block number, in big-endian order. + */ + uint64_t lba; + /** Transfer length + * + * This is a logical block count, in big-endian order. + */ + uint32_t len; + /** Group number */ + uint8_t group; + /** Control byte */ + uint8_t control; +} __attribute__ (( packed )); + +/** A SCSI "WRITE (10)" CDB */ +struct scsi_cdb_write_10 { + /** Opcode (0x2a) */ + uint8_t opcode; + /** Flags */ + uint8_t flags; + /** Start address + * + * This is a logical block number, in big-endian order. + */ + uint32_t lba; + /** Group number */ + uint8_t group; + /** Transfer length + * + * This is a logical block count, in big-endian order. + */ + uint16_t len; + /** Control byte */ + uint8_t control; +} __attribute__ (( packed )); + +/** A SCSI "WRITE (16)" CDB */ +struct scsi_cdb_write_16 { + /** Opcode (0x8a) */ + uint8_t opcode; + /** Flags */ + uint8_t flags; + /** Start address + * + * This is a logical block number, in big-endian order. + */ + uint64_t lba; + /** Transfer length + * + * This is a logical block count, in big-endian order. + */ + uint32_t len; + /** Group number */ + uint8_t group; + /** Control byte */ + uint8_t control; +} __attribute__ (( packed )); + +/** A SCSI "READ CAPACITY (10)" CDB */ +struct scsi_cdb_read_capacity_10 { + /** Opcode (0x25) */ + uint8_t opcode; + /** Reserved */ + uint8_t reserved_a; + /** Logical block address + * + * Applicable only if the PMI bit is set. + */ + uint32_t lba; + /** Reserved */ + uint8_t reserved_b[3]; + /** Control byte */ + uint8_t control; +} __attribute__ (( packed )); + +/** SCSI "READ CAPACITY (10)" parameter data */ +struct scsi_capacity_10 { + /** Maximum logical block number */ + uint32_t lba; + /** Block length in bytes */ + uint32_t blksize; +} __attribute__ (( packed )); + +/** A SCSI "READ CAPACITY (16)" CDB */ +struct scsi_cdb_read_capacity_16 { + /** Opcode (0x9e) */ + uint8_t opcode; + /** Service action */ + uint8_t service_action; + /** Logical block address + * + * Applicable only if the PMI bit is set. + */ + uint64_t lba; + /** Transfer length + * + * This is the size of the data-in buffer, in bytes. + */ + uint32_t len; + /** Reserved */ + uint8_t reserved; + /** Control byte */ + uint8_t control; +} __attribute__ (( packed )); + +/** SCSI "READ CAPACITY (16)" parameter data */ +struct scsi_capacity_16 { + /** Maximum logical block number */ + uint64_t lba; + /** Block length in bytes */ + uint32_t blksize; + /** Reserved */ + uint8_t reserved[20]; +} __attribute__ (( packed )); + +/** A SCSI Command Data Block */ +union scsi_cdb { + struct scsi_cdb_read_10 read10; + struct scsi_cdb_read_16 read16; + struct scsi_cdb_write_10 write10; + struct scsi_cdb_write_16 write16; + struct scsi_cdb_read_capacity_10 readcap10; + struct scsi_cdb_read_capacity_16 readcap16; + unsigned char bytes[16]; +}; + +/** printf() format for dumping a scsi_cdb */ +#define SCSI_CDB_FORMAT "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:" \ + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x" + +/** printf() parameters for dumping a scsi_cdb */ +#define SCSI_CDB_DATA(cdb) \ + (cdb).bytes[0], (cdb).bytes[1], (cdb).bytes[2], (cdb).bytes[3], \ + (cdb).bytes[4], (cdb).bytes[5], (cdb).bytes[6], (cdb).bytes[7], \ + (cdb).bytes[8], (cdb).bytes[9], (cdb).bytes[10], (cdb).bytes[11], \ + (cdb).bytes[12], (cdb).bytes[13], (cdb).bytes[14], (cdb).bytes[15] + +/** @} */ + +/** A SCSI command */ +struct scsi_command { + /** CDB for this command */ + union scsi_cdb cdb; + /** Data-out buffer (may be NULL) */ + userptr_t data_out; + /** Data-out buffer length + * + * Must be zero if @c data_out is NULL + */ + size_t data_out_len; + /** Data-in buffer (may be NULL) */ + userptr_t data_in; + /** Data-in buffer length + * + * Must be zero if @c data_in is NULL + */ + size_t data_in_len; + /** SCSI status code */ + uint8_t status; + /** SCSI sense response code */ + uint8_t sense_response; +}; + +/** A SCSI device */ +struct scsi_device { + /** Block device interface */ + struct block_device blockdev; + /** Logical unit number (LUN) + * + * This is a four-level LUN as specified by SAM-2, in + * big-endian order. + */ + uint64_t lun; + /** + * Issue SCSI command + * + * @v scsi SCSI device + * @v command SCSI command + * @ret rc Return status code + * + * Note that a successful return status code indicates only + * that the SCSI command completed. The caller must check the + * status field in the command structure to see if, for + * example, the device returned CHECK CONDITION or some other + * non-success status code. + */ + int ( * command ) ( struct scsi_device *scsi, + struct scsi_command *command ); + /** Backing device */ + struct refcnt *backend; +}; + +extern int init_scsidev ( struct scsi_device *scsi ); + +#endif /* _GPXE_SCSI_H */ diff --git a/gpxe/src/include/gpxe/segment.h b/gpxe/src/include/gpxe/segment.h new file mode 100644 index 0000000..5ab9169 --- /dev/null +++ b/gpxe/src/include/gpxe/segment.h @@ -0,0 +1,15 @@ +#ifndef _GPXE_SEGMENT_H +#define _GPXE_SEGMENT_H + +/** + * @file + * + * Executable image segments + * + */ + +#include + +extern int prep_segment ( userptr_t segment, size_t filesz, size_t memsz ); + +#endif /* _GPXE_SEGMENT_H */ diff --git a/gpxe/src/include/gpxe/serial.h b/gpxe/src/include/gpxe/serial.h new file mode 100644 index 0000000..2825b93 --- /dev/null +++ b/gpxe/src/include/gpxe/serial.h @@ -0,0 +1,14 @@ +#ifndef _GPXE_SERIAL_H +#define _GPXE_SERIAL_H + +/** @file + * + * Serial driver functions + * + */ + +extern void serial_putc ( int ch ); +extern int serial_getc ( void ); +extern int serial_ischar ( void ); + +#endif /* _GPXE_SERIAL_H */ diff --git a/gpxe/src/include/gpxe/settings.h b/gpxe/src/include/gpxe/settings.h new file mode 100644 index 0000000..9e62cde --- /dev/null +++ b/gpxe/src/include/gpxe/settings.h @@ -0,0 +1,319 @@ +#ifndef _GPXE_SETTINGS_H +#define _GPXE_SETTINGS_H + +/** @file + * + * Configuration settings + * + */ + +#include +#include +#include +#include +#include + +struct settings; +struct in_addr; +union uuid; + +/** A setting */ +struct setting { + /** Name + * + * This is the human-readable name for the setting. + */ + const char *name; + /** Description */ + const char *description; + /** Setting type + * + * This identifies the type of setting (e.g. string, IPv4 + * address, etc.). + */ + struct setting_type *type; + /** DHCP option number, if applicable */ + unsigned int tag; +}; + +/** Declare a configuration setting */ +#define __setting __table ( struct setting, settings, 01 ) + +/** Settings block operations */ +struct settings_operations { + /** Store value of setting + * + * @v settings Settings block + * @v setting Setting to store + * @v data Setting data, or NULL to clear setting + * @v len Length of setting data + * @ret rc Return status code + */ + int ( * store ) ( struct settings *settings, struct setting *setting, + const void *data, size_t len ); + /** Fetch value of setting + * + * @v settings Settings block + * @v setting Setting to fetch + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + * + * The actual length of the setting will be returned even if + * the buffer was too small. + */ + int ( * fetch ) ( struct settings *settings, struct setting *setting, + void *data, size_t len ); +}; + +/** A settings block */ +struct settings { + /** Reference counter */ + struct refcnt *refcnt; + /** Name */ + const char *name; + /** Tag magic + * + * This value will be ORed in to any numerical tags + * constructed by parse_setting_name(), and can be used to + * avoid e.g. attempting to retrieve the subnet mask from + * SMBIOS, or the system UUID from DHCP. + */ + unsigned int tag_magic; + /** Parent settings block */ + struct settings *parent; + /** Sibling settings blocks */ + struct list_head siblings; + /** Child settings blocks */ + struct list_head children; + /** Settings block operations */ + struct settings_operations *op; +}; + +/** + * A setting type + * + * This represents a type of setting (e.g. string, IPv4 address, + * etc.). + */ +struct setting_type { + /** Name + * + * This is the name exposed to the user (e.g. "string"). + */ + const char *name; + /** Parse and set value of setting + * + * @v settings Settings block + * @v setting Setting to store + * @v value Formatted setting data + * @ret rc Return status code + */ + int ( * storef ) ( struct settings *settings, struct setting *setting, + const char *value ); + /** Fetch and format value of setting + * + * @v settings Settings block + * @v setting Setting to fetch + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ + int ( * fetchf ) ( struct settings *settings, struct setting *setting, + char *buf, size_t len ); +}; + +/** Declare a configuration setting type */ +#define __setting_type \ + __table ( struct setting_type, setting_types, 01 ) + +/** + * A settings applicator + * + */ +struct settings_applicator { + /** Apply updated settings + * + * @ret rc Return status code + */ + int ( * apply ) ( void ); +}; + +/** Declare a settings applicator */ +#define __settings_applicator \ + __table ( struct settings_applicator, settings_applicators, 01 ) + +/** + * A simple settings block + * + */ +struct simple_settings { + /** Settings block */ + struct settings settings; + /** DHCP options */ + struct dhcp_options dhcpopts; +}; + +extern struct settings_operations simple_settings_operations; +extern int simple_settings_store ( struct settings *settings, + struct setting *setting, + const void *data, size_t len ); +extern int simple_settings_fetch ( struct settings *settings, + struct setting *setting, + void *data, size_t len ); + +extern int register_settings ( struct settings *settings, + struct settings *parent ); +extern void unregister_settings ( struct settings *settings ); + +extern int store_setting ( struct settings *settings, struct setting *setting, + const void *data, size_t len ); +extern int fetch_setting ( struct settings *settings, struct setting *setting, + void *data, size_t len ); +extern int fetch_setting_len ( struct settings *settings, + struct setting *setting ); +extern int fetch_string_setting ( struct settings *settings, + struct setting *setting, + char *data, size_t len ); +extern int fetch_string_setting_copy ( struct settings *settings, + struct setting *setting, + char **data ); +extern int fetch_ipv4_setting ( struct settings *settings, + struct setting *setting, struct in_addr *inp ); +extern int fetch_int_setting ( struct settings *settings, + struct setting *setting, long *value ); +extern int fetch_uint_setting ( struct settings *settings, + struct setting *setting, + unsigned long *value ); +extern long fetch_intz_setting ( struct settings *settings, + struct setting *setting ); +extern unsigned long fetch_uintz_setting ( struct settings *settings, + struct setting *setting ); +extern int fetch_uuid_setting ( struct settings *settings, + struct setting *setting, union uuid *uuid ); +extern int setting_cmp ( struct setting *a, struct setting *b ); + +extern struct settings * find_child_settings ( struct settings *parent, + const char *name ); +extern struct settings * find_settings ( const char *name ); + +extern int storef_setting ( struct settings *settings, + struct setting *setting, + const char *value ); +extern int storef_named_setting ( const char *name, const char *value ); +extern int fetchf_named_setting ( const char *name, char *buf, size_t len ); + +extern struct setting_type setting_type_string __setting_type; +extern struct setting_type setting_type_ipv4 __setting_type; +extern struct setting_type setting_type_int8 __setting_type; +extern struct setting_type setting_type_int16 __setting_type; +extern struct setting_type setting_type_int32 __setting_type; +extern struct setting_type setting_type_uint8 __setting_type; +extern struct setting_type setting_type_uint16 __setting_type; +extern struct setting_type setting_type_uint32 __setting_type; +extern struct setting_type setting_type_hex __setting_type; +extern struct setting_type setting_type_uuid __setting_type; + +extern struct setting ip_setting __setting; +extern struct setting netmask_setting __setting; +extern struct setting gateway_setting __setting; +extern struct setting dns_setting __setting; +extern struct setting domain_setting __setting; +extern struct setting hostname_setting __setting; +extern struct setting filename_setting __setting; +extern struct setting root_path_setting __setting; +extern struct setting username_setting __setting; +extern struct setting password_setting __setting; +extern struct setting priority_setting __setting; +extern struct setting uuid_setting __setting; +extern struct setting next_server_setting __setting; +extern struct setting mac_setting __setting; +extern struct setting user_class_setting __setting; + +/** + * Initialise a settings block + * + * @v settings Settings block + * @v op Settings block operations + * @v refcnt Containing object reference counter, or NULL + * @v name Settings block name + * @v tag_magic Tag magic + */ +static inline void settings_init ( struct settings *settings, + struct settings_operations *op, + struct refcnt *refcnt, + const char *name, + unsigned int tag_magic ) { + INIT_LIST_HEAD ( &settings->siblings ); + INIT_LIST_HEAD ( &settings->children ); + settings->op = op; + settings->refcnt = refcnt; + settings->name = name; + settings->tag_magic = tag_magic; +} + +/** + * Initialise a settings block + * + * @v simple Simple settings block + * @v refcnt Containing object reference counter, or NULL + * @v name Settings block name + */ +static inline void simple_settings_init ( struct simple_settings *simple, + struct refcnt *refcnt, + const char *name ) { + settings_init ( &simple->settings, &simple_settings_operations, + refcnt, name, 0 ); +} + +/** + * Delete setting + * + * @v settings Settings block + * @v setting Setting to delete + * @ret rc Return status code + */ +static inline int delete_setting ( struct settings *settings, + struct setting *setting ) { + return store_setting ( settings, setting, NULL, 0 ); +} + +/** + * Fetch and format value of setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v type Settings type + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static inline int fetchf_setting ( struct settings *settings, + struct setting *setting, + char *buf, size_t len ) { + return setting->type->fetchf ( settings, setting, buf, len ); +} + +/** + * Delete named setting + * + * @v name Name of setting + * @ret rc Return status code + */ +static inline int delete_named_setting ( const char *name ) { + return storef_named_setting ( name, NULL ); +} + +/** + * Check existence of setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @ret exists Setting exists + */ +static inline int setting_exists ( struct settings *settings, + struct setting *setting ) { + return ( fetch_setting_len ( settings, setting ) >= 0 ); +} + +#endif /* _GPXE_SETTINGS_H */ diff --git a/gpxe/src/include/gpxe/settings_ui.h b/gpxe/src/include/gpxe/settings_ui.h new file mode 100644 index 0000000..48548fd --- /dev/null +++ b/gpxe/src/include/gpxe/settings_ui.h @@ -0,0 +1,14 @@ +#ifndef _GPXE_SETTINGS_UI_H +#define _GPXE_SETTINGS_UI_H + +/** @file + * + * Option configuration console + * + */ + +struct settings; + +extern int settings_ui ( struct settings *settings ) __nonnull; + +#endif /* _GPXE_SETTINGS_UI_H */ diff --git a/gpxe/src/include/gpxe/sha1.h b/gpxe/src/include/gpxe/sha1.h new file mode 100644 index 0000000..66370d4 --- /dev/null +++ b/gpxe/src/include/gpxe/sha1.h @@ -0,0 +1,13 @@ +#ifndef _GPXE_SHA1_H +#define _GPXE_SHA1_H + +#include "crypto/axtls/crypto.h" + +struct digest_algorithm; + +#define SHA1_CTX_SIZE sizeof ( SHA1_CTX ) +#define SHA1_DIGEST_SIZE SHA1_SIZE + +extern struct digest_algorithm sha1_algorithm; + +#endif /* _GPXE_SHA1_H */ diff --git a/gpxe/src/include/gpxe/shell.h b/gpxe/src/include/gpxe/shell.h new file mode 100644 index 0000000..c353fc4 --- /dev/null +++ b/gpxe/src/include/gpxe/shell.h @@ -0,0 +1,12 @@ +#ifndef _GPXE_SHELL_H +#define _GPXE_SHELL_H + +/** @file + * + * Minimal command shell + * + */ + +extern void shell ( void ); + +#endif /* _GPXE_SHELL_H */ diff --git a/gpxe/src/include/gpxe/shell_banner.h b/gpxe/src/include/gpxe/shell_banner.h new file mode 100644 index 0000000..f8e92a4 --- /dev/null +++ b/gpxe/src/include/gpxe/shell_banner.h @@ -0,0 +1,12 @@ +#ifndef _GPXE_SHELL_BANNER_H +#define _GPXE_SHELL_BANNER_H + +/** @file + * + * Shell startup banner + * + */ + +extern int shell_banner ( void ); + +#endif /* _GPXE_SHELL_BANNER_H */ diff --git a/gpxe/src/include/gpxe/smbios.h b/gpxe/src/include/gpxe/smbios.h new file mode 100644 index 0000000..2b0fcbd --- /dev/null +++ b/gpxe/src/include/gpxe/smbios.h @@ -0,0 +1,140 @@ +#ifndef _GPXE_SMBIOS_H +#define _GPXE_SMBIOS_H + +/** @file + * + * System Management BIOS + * + */ + +#include +#include +#include +#include + +/** + * Provide an SMBIOS API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @v _func Implementing function + */ +#define PROVIDE_SMBIOS( _subsys, _api_func, _func ) \ + PROVIDE_SINGLE_API ( SMBIOS_PREFIX_ ## _subsys, _api_func, _func ) + +/* Include all architecture-independent SMBIOS API headers */ +#include + +/* Include all architecture-dependent SMBIOS API headers */ +#include + +/** Signature for SMBIOS entry point */ +#define SMBIOS_SIGNATURE \ + ( ( '_' << 0 ) + ( 'S' << 8 ) + ( 'M' << 16 ) + ( '_' << 24 ) ) + +/** + * SMBIOS entry point + * + * This is the single table which describes the list of SMBIOS + * structures. It is located by scanning through the BIOS segment. + */ +struct smbios_entry { + /** Signature + * + * Must be equal to SMBIOS_SIGNATURE + */ + uint32_t signature; + /** Checksum */ + uint8_t checksum; + /** Length */ + uint8_t len; + /** Major version */ + uint8_t major; + /** Minor version */ + uint8_t minor; + /** Maximum structure size */ + uint16_t max; + /** Entry point revision */ + uint8_t revision; + /** Formatted area */ + uint8_t formatted[5]; + /** DMI Signature */ + uint8_t dmi_signature[5]; + /** DMI checksum */ + uint8_t dmi_checksum; + /** Structure table length */ + uint16_t smbios_len; + /** Structure table address */ + uint32_t smbios_address; + /** Number of SMBIOS structures */ + uint16_t smbios_count; + /** BCD revision */ + uint8_t bcd_revision; +} __attribute__ (( packed )); + +/** An SMBIOS structure header */ +struct smbios_header { + /** Type */ + uint8_t type; + /** Length */ + uint8_t len; + /** Handle */ + uint16_t handle; +} __attribute__ (( packed )); + +/** SMBIOS structure descriptor */ +struct smbios_structure { + /** Copy of SMBIOS structure header */ + struct smbios_header header; + /** Offset of structure within SMBIOS */ + size_t offset; + /** Length of strings section */ + size_t strings_len; +}; + +/** SMBIOS system information structure */ +struct smbios_system_information { + /** SMBIOS structure header */ + struct smbios_header header; + /** Manufacturer string */ + uint8_t manufacturer; + /** Product string */ + uint8_t product; + /** Version string */ + uint8_t version; + /** Serial number string */ + uint8_t serial; + /** UUID */ + uint8_t uuid[16]; + /** Wake-up type */ + uint8_t wakeup; +} __attribute__ (( packed )); + +/** SMBIOS system information structure type */ +#define SMBIOS_TYPE_SYSTEM_INFORMATION 1 + +/** + * SMBIOS entry point descriptor + * + * This contains the information from the SMBIOS entry point that we + * care about. + */ +struct smbios { + /** Start of SMBIOS structures */ + userptr_t address; + /** Length of SMBIOS structures */ + size_t len; + /** Number of SMBIOS structures */ + unsigned int count; +}; + +extern int find_smbios ( struct smbios *smbios ); +extern int find_smbios_structure ( unsigned int type, + struct smbios_structure *structure ); +extern int read_smbios_structure ( struct smbios_structure *structure, + void *data, size_t len ); +extern int read_smbios_string ( struct smbios_structure *structure, + unsigned int index, + void *data, size_t len ); + +#endif /* _GPXE_SMBIOS_H */ diff --git a/gpxe/src/include/gpxe/socket.h b/gpxe/src/include/gpxe/socket.h new file mode 100644 index 0000000..b683bed --- /dev/null +++ b/gpxe/src/include/gpxe/socket.h @@ -0,0 +1,99 @@ +#ifndef _GPXE_SOCKET_H +#define _GPXE_SOCKET_H + +/** @file + * + * Socket addresses + * + */ + +#include + +/** + * @defgroup commtypes Communication semantics + * + * @{ + */ + +/** Connection-based, reliable streams */ +extern int tcp_sock_stream; +#define TCP_SOCK_STREAM 0x1 +#define SOCK_STREAM tcp_sock_stream + +/** Connectionless, unreliable streams */ +extern int udp_sock_dgram; +#define UDP_SOCK_DGRAM 0x2 +#define SOCK_DGRAM udp_sock_dgram + +/** @} */ + +/** + * Name communication semantics + * + * @v semantics Communication semantics (e.g. SOCK_STREAM) + * @ret name Name of communication semantics + */ +static inline __attribute__ (( always_inline )) const char * +socket_semantics_name ( int semantics ) { + /* Cannot use a switch() because of the {TCP_UDP}_SOCK_XXX hack */ + if ( semantics == SOCK_STREAM ) { + return "SOCK_STREAM"; + } else if ( semantics == SOCK_DGRAM ) { + return "SOCK_DGRAM"; + } else { + return "SOCK_UNKNOWN"; + } +} + +/** + * @defgroup addrfam Address families + * + * @{ + */ +#define AF_INET 1 /**< IPv4 Internet addresses */ +#define AF_INET6 2 /**< IPv6 Internet addresses */ +/** @} */ + +/** + * Name address family + * + * @v family Address family (e.g. AF_INET) + * @ret name Name of address family + */ +static inline __attribute__ (( always_inline )) const char * +socket_family_name ( int family ) { + switch ( family ) { + case AF_INET: return "AF_INET"; + case AF_INET6: return "AF_INET6"; + default: return "AF_UNKNOWN"; + } +} + +/** A socket address family */ +typedef uint16_t sa_family_t; + +/** Length of a @c struct @c sockaddr */ +#define SA_LEN 32 + +/** + * Generalized socket address structure + * + * This contains the fields common to socket addresses for all address + * families. + */ +struct sockaddr { + /** Socket address family + * + * This is an AF_XXX constant. + */ + sa_family_t sa_family; + /** Padding + * + * This ensures that a struct @c sockaddr_tcpip is large + * enough to hold a socket address for any TCP/IP address + * family. + */ + char pad[ SA_LEN - sizeof ( sa_family_t ) ]; +} __attribute__ (( may_alias )); + +#endif /* _GPXE_SOCKET_H */ diff --git a/gpxe/src/include/gpxe/spi.h b/gpxe/src/include/gpxe/spi.h new file mode 100644 index 0000000..8f90e3d --- /dev/null +++ b/gpxe/src/include/gpxe/spi.h @@ -0,0 +1,248 @@ +#ifndef _GPXE_SPI_H +#define _GPXE_SPI_H + +/** @file + * + * SPI interface + * + */ + +#include + +/** + * @defgroup spicmds SPI commands + * @{ + */ + +/** Write status register */ +#define SPI_WRSR 0x01 + +/** Write data to memory array */ +#define SPI_WRITE 0x02 + +/** Read data from memory array */ +#define SPI_READ 0x03 + +/** Reset write enable latch */ +#define SPI_WRDI 0x04 + +/** Read status register */ +#define SPI_RDSR 0x05 + +/** Set write enable latch */ +#define SPI_WREN 0x06 + +/** + * @defgroup atmelcmds Atmel-specific SPI commands + * @{ + */ + +/** Erase one sector in memory array (Not supported on all devices) */ +#define ATMEL_SECTOR_ERASE 0x52 + +/** Erase all sections in memory array (Not supported on all devices) */ +#define ATMEL_CHIP_ERASE 0x62 + +/** Read manufacturer and product ID (Not supported on all devices) */ +#define ATMEL_RDID 0x15 + +/** @} */ + +/** @} */ + +/** + * @defgroup spistatus SPI status register bits (not present on all devices) + * @{ + */ + +/** Write-protect pin enabled */ +#define SPI_STATUS_WPEN 0x80 + +/** Block protection bit 2 */ +#define SPI_STATUS_BP2 0x10 + +/** Block protection bit 1 */ +#define SPI_STATUS_BP1 0x08 + +/** Block protection bit 0 */ +#define SPI_STATUS_BP0 0x04 + +/** State of the write enable latch */ +#define SPI_STATUS_WEN 0x02 + +/** Device busy flag */ +#define SPI_STATUS_NRDY 0x01 + +/** @} */ + +/** + * An SPI device + * + * This data structure represents a physical SPI device attached to an + * SPI bus. + */ +struct spi_device { + /** NVS device */ + struct nvs_device nvs; + /** SPI bus to which device is attached */ + struct spi_bus *bus; + /** Slave number */ + unsigned int slave; + /** Command length, in bits */ + unsigned int command_len; + /** Address length, in bits */ + unsigned int address_len; + /** Address is munged + * + * Some devices with 9-bit addresses (e.g. AT25040A EEPROM) + * use bit 3 of the command byte as address bit A8, rather + * than having a two-byte address. If this flag is set, then + * commands should be munged in this way. + */ + unsigned int munge_address : 1; +}; + +static inline __attribute__ (( always_inline )) struct spi_device * +nvs_to_spi ( struct nvs_device *nvs ) { + return container_of ( nvs, struct spi_device, nvs ); +} + +/** + * An SPI bus + * + * This data structure represents an SPI bus controller capable of + * issuing commands to attached SPI devices. + */ +struct spi_bus { + /** SPI interface mode + * + * This is the bitwise OR of zero or more of @c SPI_MODE_CPHA + * and @c SPI_MODE_CPOL. It is also the number conventionally + * used to describe the SPI interface mode. For example, SPI + * mode 1 is the mode in which CPOL=0 and CPHA=1, which + * therefore corresponds to a mode value of (0|SPI_MODE_CPHA) + * which, happily, equals 1. + */ + unsigned int mode; + /** + * Read/write data via SPI bus + * + * @v bus SPI bus + * @v device SPI device + * @v command Command + * @v address Address to read/write (<0 for no address) + * @v data_out TX data buffer (or NULL) + * @v data_in RX data buffer (or NULL) + * @v len Length of data buffer(s) + * + * This issues the specified command and optional address to + * the SPI device, then reads and/or writes data to/from the + * data buffers. + */ + int ( * rw ) ( struct spi_bus *bus, struct spi_device *device, + unsigned int command, int address, + const void *data_out, void *data_in, size_t len ); +}; + +/** Clock phase (CPHA) mode bit + * + * Phase 0 is sample on rising edge, shift data on falling edge. + * + * Phase 1 is shift data on rising edge, sample data on falling edge. + */ +#define SPI_MODE_CPHA 0x01 + +/** Clock polarity (CPOL) mode bit + * + * This bit reflects the idle state of the clock line (SCLK). + */ +#define SPI_MODE_CPOL 0x02 + +/** Slave select polarity mode bit + * + * This bit reflects that active state of the slave select lines. It + * is not part of the normal SPI mode number (which covers only @c + * SPI_MODE_CPOL and @c SPI_MODE_CPHA), but is included here for + * convenience. + */ +#define SPI_MODE_SSPOL 0x10 + +/** Microwire-compatible mode + * + * This is SPI mode 1 (i.e. CPOL=0, CPHA=1), and is compatible with + * the original Microwire protocol. + */ +#define SPI_MODE_MICROWIRE 1 + +/** Microwire/Plus-compatible mode + * + * This is SPI mode 0 (i.e. CPOL=0, CPHA=0), and is compatible with + * the Microwire/Plus protocol + */ +#define SPI_MODE_MICROWIRE_PLUS 0 + +/** Threewire-compatible mode + * + * This mode is compatible with Atmel's series of "three-wire" + * interfaces. + */ +#define SPI_MODE_THREEWIRE ( SPI_MODE_MICROWIRE_PLUS | SPI_MODE_SSPOL ) + +extern int spi_read ( struct nvs_device *nvs, unsigned int address, + void *data, size_t len ); +extern int spi_write ( struct nvs_device *nvs, unsigned int address, + const void *data, size_t len ); + +/** + * @defgroup spidevs SPI device types + * @{ + */ + +static inline __attribute__ (( always_inline )) void +init_spi ( struct spi_device *device ) { + device->nvs.word_len_log2 = 0; + device->command_len = 8, + device->nvs.read = spi_read; + device->nvs.write = spi_write; +} + +/** Atmel AT25F1024 serial flash */ +static inline __attribute__ (( always_inline )) void +init_at25f1024 ( struct spi_device *device ) { + device->address_len = 24; + device->nvs.size = ( 128 * 1024 ); + device->nvs.block_size = 256; + init_spi ( device ); +} + +/** Atmel 25040 serial EEPROM */ +static inline __attribute__ (( always_inline )) void +init_at25040 ( struct spi_device *device ) { + device->address_len = 8; + device->munge_address = 1; + device->nvs.size = 512; + device->nvs.block_size = 8; + init_spi ( device ); +} + +/** ST M25P32 serial flash */ +static inline __attribute__ (( always_inline )) void +init_m25p32 ( struct spi_device *device ) { + device->address_len = 24; + device->nvs.size = ( 4 * 1024 * 1024 ); + device->nvs.block_size = 256; + init_spi ( device ); +} + +/** Microchip 25XX640 serial EEPROM */ +static inline __attribute__ (( always_inline )) void +init_mc25xx640 ( struct spi_device *device ) { + device->address_len = 16; + device->nvs.size = ( 8 * 1024 ); + device->nvs.block_size = 32; + init_spi ( device ); +} + +/** @} */ + +#endif /* _GPXE_SPI_H */ diff --git a/gpxe/src/include/gpxe/spi_bit.h b/gpxe/src/include/gpxe/spi_bit.h new file mode 100644 index 0000000..ced85ce --- /dev/null +++ b/gpxe/src/include/gpxe/spi_bit.h @@ -0,0 +1,61 @@ +#ifndef _GPXE_SPI_BIT_H +#define _GPXE_SPI_BIT_H + +/** @file + * + * SPI bit-bashing interface + * + */ + +#include +#include + +/** A bit-bashing SPI bus */ +struct spi_bit_basher { + /** SPI bus */ + struct spi_bus bus; + /** Bit-bashing interface */ + struct bit_basher basher; + /** Endianness of data + * + * SPI commands and addresses are always big-endian (i.e. MSB + * transmitted first on the wire), but some cards + * (e.g. natsemi) choose to regard the data stored in the + * EEPROM as little-endian (i.e. LSB transmitted first on the + * wire). + */ + int endianness; +}; + +/** Bit indices used for SPI bit-bashing interface */ +enum { + /** Serial clock */ + SPI_BIT_SCLK = 0, + /** Master Out Slave In */ + SPI_BIT_MOSI, + /** Master In Slave Out */ + SPI_BIT_MISO, + /** Slave 0 select */ + SPI_BIT_SS0, +}; + +/** + * Determine bit index for a particular slave + * + * @v slave Slave number + * @ret index Bit index (i.e. SPI_BIT_SSN, where N=slave) + */ +#define SPI_BIT_SS( slave ) ( SPI_BIT_SS0 + (slave) ) + +/** Delay between SCLK transitions */ +#define SPI_BIT_UDELAY 1 + +/** SPI bit basher treats data as big-endian */ +#define SPI_BIT_BIG_ENDIAN 0 + +/** SPI bit basher treats data as little-endian */ +#define SPI_BIT_LITTLE_ENDIAN 1 + +extern void init_spi_bit_basher ( struct spi_bit_basher *spibit ); + +#endif /* _GPXE_SPI_BIT_H */ diff --git a/gpxe/src/include/gpxe/tables.h b/gpxe/src/include/gpxe/tables.h new file mode 100644 index 0000000..b2c56ab --- /dev/null +++ b/gpxe/src/include/gpxe/tables.h @@ -0,0 +1,229 @@ +#ifndef _GPXE_TABLES_H +#define _GPXE_TABLES_H + +/** @page ifdef_harmful #ifdef considered harmful + * + * Overuse of @c #ifdef has long been a problem in Etherboot. + * Etherboot provides a rich array of features, but all these features + * take up valuable space in a ROM image. The traditional solution to + * this problem has been for each feature to have its own @c #ifdef + * option, allowing the feature to be compiled in only if desired. + * + * The problem with this is that it becomes impossible to compile, let + * alone test, all possible versions of Etherboot. Code that is not + * typically used tends to suffer from bit-rot over time. It becomes + * extremely difficult to predict which combinations of compile-time + * options will result in code that can even compile and link + * correctly. + * + * To solve this problem, we have adopted a new approach from + * Etherboot 5.5 onwards. @c #ifdef is now "considered harmful", and + * its use should be minimised. Separate features should be + * implemented in separate @c .c files, and should \b always be + * compiled (i.e. they should \b not be guarded with a @c #ifdef @c + * MY_PET_FEATURE statement). By making (almost) all code always + * compile, we avoid the problem of bit-rot in rarely-used code. + * + * The file config.h, in combination with the @c make command line, + * specifies the objects that will be included in any particular build + * of Etherboot. For example, suppose that config.h includes the line + * + * @code + * + * #define CONSOLE_SERIAL + * #define DOWNLOAD_PROTO_TFTP + * + * @endcode + * + * When a particular Etherboot image (e.g. @c bin/rtl8139.zdsk) is + * built, the options specified in config.h are used to drag in the + * relevant objects at link-time. For the above example, serial.o and + * tftp.o would be linked in. + * + * There remains one problem to solve: how do these objects get used? + * Traditionally, we had code such as + * + * @code + * + * #ifdef CONSOLE_SERIAL + * serial_init(); + * #endif + * + * @endcode + * + * in main.c, but this reintroduces @c #ifdef and so is a Bad Idea. + * We cannot simply remove the @c #ifdef and make it + * + * @code + * + * serial_init(); + * + * @endcode + * + * because then serial.o would end up always being linked in. + * + * The solution is to use @link tables.h linker tables @endlink. + * + */ + +/** @file + * + * Linker tables + * + * Read @ref ifdef_harmful first for some background on the motivation + * for using linker tables. + * + * This file provides macros for dealing with linker-generated tables + * of fixed-size symbols. We make fairly extensive use of these in + * order to avoid @c #ifdef spaghetti and/or linker symbol pollution. + * For example, instead of having code such as + * + * @code + * + * #ifdef CONSOLE_SERIAL + * serial_init(); + * #endif + * + * @endcode + * + * we make serial.c generate an entry in the initialisation function + * table, and then have a function call_init_fns() that simply calls + * all functions present in this table. If and only if serial.o gets + * linked in, then its initialisation function will be called. We + * avoid linker symbol pollution (i.e. always dragging in serial.o + * just because of a call to serial_init()) and we also avoid @c + * #ifdef spaghetti (having to conditionalise every reference to + * functions in serial.c). + * + * The linker script takes care of assembling the tables for us. All + * our table sections have names of the format @c .tbl.NAME.NN where + * @c NAME designates the data structure stored in the table (e.g. @c + * init_fn) and @c NN is a two-digit decimal number used to impose an + * ordering upon the tables if required. @c NN=00 is reserved for the + * symbol indicating "table start", and @c NN=99 is reserved for the + * symbol indicating "table end". + * + * As an example, suppose that we want to create a "frobnicator" + * feature framework, and allow for several independent modules to + * provide frobnicating services. Then we would create a frob.h + * header file containing e.g. + * + * @code + * + * struct frobnicator { + * const char *name; // Name of the frobnicator + * void ( *frob ) ( void ); // The frobnicating function itself + * }; + * + * #define __frobnicator __table ( frobnicators, 01 ) + * + * @endcode + * + * Any module providing frobnicating services would look something + * like + * + * @code + * + * #include "frob.h" + * + * static void my_frob ( void ) { + * // Do my frobnicating + * ... + * } + * + * struct frob my_frobnicator __frobnicator = { + * .name = "my_frob", + * .frob = my_frob, + * }; + * + * @endcode + * + * The central frobnicator code (frob.c) would use the frobnicating + * modules as follows + * + * @code + * + * #include "frob.h" + * + * static struct frob frob_start[0] __table_start ( frobnicators ); + * static struct frob frob_end[0] __table_end ( frobnicators ); + * + * // Call all linked-in frobnicators + * void frob_all ( void ) { + * struct frob *frob; + * + * for ( frob = frob_start ; frob < frob_end ; frob++ ) { + * printf ( "Calling frobnicator \"%s\"\n", frob->name ); + * frob->frob (); + * } + * } + * + * @endcode + * + * See init.h and init.c for a real-life example. + * + */ + +#ifdef DOXYGEN +#define __attribute__( x ) +#endif + +#define __table_str( x ) #x +#define __table_section( table, idx ) \ + __section__ ( ".tbl." __table_str ( table ) "." __table_str ( idx ) ) + +#define __table_section_start( table ) __table_section ( table, 00 ) +#define __table_section_end( table ) __table_section ( table, 99 ) + +#define __natural_alignment( type ) __aligned__ ( __alignof__ ( type ) ) + +/** + * Linker table entry. + * + * Declares a data structure to be part of a linker table. Use as + * e.g. + * + * @code + * + * struct my_foo __table ( foo, 01 ) = { + * ... + * }; + * + * @endcode + * + */ +#define __table( type, table, idx ) \ + __attribute__ (( __table_section ( table, idx ), \ + __natural_alignment ( type ) )) + +/** + * Linker table start marker. + * + * Declares a data structure (usually an empty data structure) to be + * the start of a linker table. Use as e.g. + * + * @code + * + * static struct foo_start[0] __table_start ( foo ); + * + * @endcode + * + */ +#define __table_start( type, table ) __table ( type, table, 00 ) + +/** + * Linker table end marker. + * + * Declares a data structure (usually an empty data structure) to be + * the end of a linker table. Use as e.g. + * + * @code + * + * static struct foo_end[0] __table_end ( foo ); + * + * @endcode + * + */ +#define __table_end( type, table ) __table ( type, table, 99 ) + +#endif /* _GPXE_TABLES_H */ diff --git a/gpxe/src/include/gpxe/tcp.h b/gpxe/src/include/gpxe/tcp.h new file mode 100644 index 0000000..264ec29 --- /dev/null +++ b/gpxe/src/include/gpxe/tcp.h @@ -0,0 +1,306 @@ +#ifndef _GPXE_TCP_H +#define _GPXE_TCP_H + +/** @file + * + * TCP protocol + * + * This file defines the gPXE TCP API. + * + */ + +#include + +/** + * A TCP header + */ +struct tcp_header { + uint16_t src; /* Source port */ + uint16_t dest; /* Destination port */ + uint32_t seq; /* Sequence number */ + uint32_t ack; /* Acknowledgement number */ + uint8_t hlen; /* Header length (4), Reserved (4) */ + uint8_t flags; /* Reserved (2), Flags (6) */ + uint16_t win; /* Advertised window */ + uint16_t csum; /* Checksum */ + uint16_t urg; /* Urgent pointer */ +}; + +/** @defgroup tcpopts TCP options + * @{ + */ + +/** End of TCP options list */ +#define TCP_OPTION_END 0 + +/** TCP option pad */ +#define TCP_OPTION_NOP 1 + +/** Generic TCP option */ +struct tcp_option { + uint8_t kind; + uint8_t length; +} __attribute__ (( packed )); + +/** TCP MSS option */ +struct tcp_mss_option { + uint8_t kind; + uint8_t length; + uint16_t mss; +} __attribute__ (( packed )); + +/** Code for the TCP MSS option */ +#define TCP_OPTION_MSS 2 + +/** TCP timestamp option */ +struct tcp_timestamp_option { + uint8_t kind; + uint8_t length; + uint32_t tsval; + uint32_t tsecr; +} __attribute__ (( packed )); + +/** Padded TCP timestamp option (used for sending) */ +struct tcp_timestamp_padded_option { + uint8_t nop[2]; + struct tcp_timestamp_option tsopt; +} __attribute__ (( packed )); + +/** Code for the TCP timestamp option */ +#define TCP_OPTION_TS 8 + +/** Parsed TCP options */ +struct tcp_options { + /** MSS option, if present */ + const struct tcp_mss_option *mssopt; + /** Timestampe option, if present */ + const struct tcp_timestamp_option *tsopt; +}; + +/** @} */ + +/* + * TCP flags + */ +#define TCP_CWR 0x80 +#define TCP_ECE 0x40 +#define TCP_URG 0x20 +#define TCP_ACK 0x10 +#define TCP_PSH 0x08 +#define TCP_RST 0x04 +#define TCP_SYN 0x02 +#define TCP_FIN 0x01 + +/** +* @defgroup tcpstates TCP states +* +* The TCP state is defined by a combination of the flags that have +* been sent to the peer, the flags that have been acknowledged by the +* peer, and the flags that have been received from the peer. +* +* @{ +*/ + +/** TCP flags that have been sent in outgoing packets */ +#define TCP_STATE_SENT(flags) ( (flags) << 0 ) +#define TCP_FLAGS_SENT(state) ( ( (state) >> 0 ) & 0xff ) + +/** TCP flags that have been acknowledged by the peer + * + * Note that this applies only to SYN and FIN. + */ +#define TCP_STATE_ACKED(flags) ( (flags) << 8 ) +#define TCP_FLAGS_ACKED(state) ( ( (state) >> 8 ) & 0xff ) + +/** TCP flags that have been received from the peer + * + * Note that this applies only to SYN and FIN, and that once SYN has + * been received, we should always be sending ACK. + */ +#define TCP_STATE_RCVD(flags) ( (flags) << 16 ) +#define TCP_FLAGS_RCVD(state) ( ( (state) >> 16 ) & 0xff ) + +/** TCP flags that are currently being sent in outgoing packets */ +#define TCP_FLAGS_SENDING(state) \ + ( TCP_FLAGS_SENT ( state ) & ~TCP_FLAGS_ACKED ( state ) ) + +/** CLOSED + * + * The connection has not yet been used for anything. + */ +#define TCP_CLOSED TCP_RST + +/** LISTEN + * + * Not currently used as a state; we have no support for listening + * connections. Given a unique value to avoid compiler warnings. + */ +#define TCP_LISTEN 0 + +/** SYN_SENT + * + * SYN has been sent, nothing has yet been received or acknowledged. + */ +#define TCP_SYN_SENT ( TCP_STATE_SENT ( TCP_SYN ) ) + +/** SYN_RCVD + * + * SYN has been sent but not acknowledged, SYN has been received. + */ +#define TCP_SYN_RCVD ( TCP_STATE_SENT ( TCP_SYN | TCP_ACK ) | \ + TCP_STATE_RCVD ( TCP_SYN ) ) + +/** ESTABLISHED + * + * SYN has been sent and acknowledged, SYN has been received. + */ +#define TCP_ESTABLISHED ( TCP_STATE_SENT ( TCP_SYN | TCP_ACK ) | \ + TCP_STATE_ACKED ( TCP_SYN ) | \ + TCP_STATE_RCVD ( TCP_SYN ) ) + +/** FIN_WAIT_1 + * + * SYN has been sent and acknowledged, SYN has been received, FIN has + * been sent but not acknowledged, FIN has not been received. + * + * RFC 793 shows that we can enter FIN_WAIT_1 without have had SYN + * acknowledged, i.e. if the application closes the connection after + * sending and receiving SYN, but before having had SYN acknowledged. + * However, we have to *pretend* that SYN has been acknowledged + * anyway, otherwise we end up sending SYN and FIN in the same + * sequence number slot. Therefore, when we transition from SYN_RCVD + * to FIN_WAIT_1, we have to remember to set TCP_STATE_ACKED(TCP_SYN) + * and increment our sequence number. + */ +#define TCP_FIN_WAIT_1 ( TCP_STATE_SENT ( TCP_SYN | TCP_ACK | TCP_FIN ) | \ + TCP_STATE_ACKED ( TCP_SYN ) | \ + TCP_STATE_RCVD ( TCP_SYN ) ) + +/** FIN_WAIT_2 + * + * SYN has been sent and acknowledged, SYN has been received, FIN has + * been sent and acknowledged, FIN ha not been received. + */ +#define TCP_FIN_WAIT_2 ( TCP_STATE_SENT ( TCP_SYN | TCP_ACK | TCP_FIN ) | \ + TCP_STATE_ACKED ( TCP_SYN | TCP_FIN ) | \ + TCP_STATE_RCVD ( TCP_SYN ) ) + +/** CLOSING / LAST_ACK + * + * SYN has been sent and acknowledged, SYN has been received, FIN has + * been sent but not acknowledged, FIN has been received. + * + * This state actually encompasses both CLOSING and LAST_ACK; they are + * identical with the definition of state that we use. I don't + * *believe* that they need to be distinguished. + */ +#define TCP_CLOSING_OR_LAST_ACK \ + ( TCP_STATE_SENT ( TCP_SYN | TCP_ACK | TCP_FIN ) | \ + TCP_STATE_ACKED ( TCP_SYN ) | \ + TCP_STATE_RCVD ( TCP_SYN | TCP_FIN ) ) + +/** TIME_WAIT + * + * SYN has been sent and acknowledged, SYN has been received, FIN has + * been sent and acknowledged, FIN has been received. + */ +#define TCP_TIME_WAIT ( TCP_STATE_SENT ( TCP_SYN | TCP_ACK | TCP_FIN ) | \ + TCP_STATE_ACKED ( TCP_SYN | TCP_FIN ) | \ + TCP_STATE_RCVD ( TCP_SYN | TCP_FIN ) ) + +/** CLOSE_WAIT + * + * SYN has been sent and acknowledged, SYN has been received, FIN has + * been received. + */ +#define TCP_CLOSE_WAIT ( TCP_STATE_SENT ( TCP_SYN | TCP_ACK ) | \ + TCP_STATE_ACKED ( TCP_SYN ) | \ + TCP_STATE_RCVD ( TCP_SYN | TCP_FIN ) ) + +/** Can send data in current state + * + * We can send data if and only if we have had our SYN acked and we + * have not yet sent our FIN. + */ +#define TCP_CAN_SEND_DATA(state) \ + ( ( (state) & ( TCP_STATE_ACKED ( TCP_SYN ) | \ + TCP_STATE_SENT ( TCP_FIN ) ) ) \ + == TCP_STATE_ACKED ( TCP_SYN ) ) + +/** Have closed gracefully + * + * We have closed gracefully if we have both received a FIN and had + * our own FIN acked. + */ +#define TCP_CLOSED_GRACEFULLY(state) \ + ( ( (state) & ( TCP_STATE_ACKED ( TCP_FIN ) | \ + TCP_STATE_RCVD ( TCP_FIN ) ) ) \ + == ( TCP_STATE_ACKED ( TCP_FIN ) | TCP_STATE_RCVD ( TCP_FIN ) ) ) + +/** @} */ + +/** Mask for TCP header length field */ +#define TCP_MASK_HLEN 0xf0 + +/** Smallest port number on which a TCP connection can listen */ +#define TCP_MIN_PORT 1 + +/* Some IOB constants */ +#define MAX_HDR_LEN 100 +#define MAX_IOB_LEN 1500 +#define MIN_IOB_LEN MAX_HDR_LEN + 100 /* To account for padding by LL */ + +/** + * Maxmimum advertised TCP window size + * + * We estimate the TCP window size as the amount of free memory we + * have. This is not strictly accurate (since it ignores any space + * already allocated as RX buffers), but it will do for now. + * + * Since we don't store out-of-order received packets, the + * retransmission penalty is that the whole window contents must be + * resent. This suggests keeping the window size small, but bear in + * mind that the maximum bandwidth on any link is limited to + * + * max_bandwidth = ( tcp_window / round_trip_time ) + * + * With a 48kB window, which probably accurately reflects our amount + * of free memory, and a WAN RTT of say 200ms, this gives a maximum + * bandwidth of 240kB/s. This is sufficiently close to realistic that + * we will need to be careful that our advertised window doesn't end + * up limiting WAN download speeds. + * + * Finally, since the window goes into a 16-bit field and we cannot + * actually use 65536, we use a window size of (65536-4) to ensure + * that payloads remain dword-aligned. + */ +//#define TCP_MAX_WINDOW_SIZE ( 65536 - 4 ) +#define TCP_MAX_WINDOW_SIZE 4096 + +/** + * Path MTU + * + * We really ought to implement Path MTU discovery. Until we do, + * anything with a path MTU greater than this may fail. + */ +#define TCP_PATH_MTU 1460 + +/** + * Advertised TCP MSS + * + * We currently hardcode this to a reasonable value and hope that the + * sender uses path MTU discovery. The alternative is breaking the + * abstraction layer so that we can find out the MTU from the IP layer + * (which would have to find out from the net device layer). + */ +#define TCP_MSS 1460 + +/** TCP maximum segment lifetime + * + * Currently set to 2 minutes, as per RFC 793. + */ +#define TCP_MSL ( 2 * 60 * TICKS_PER_SEC ) + +extern struct tcpip_protocol tcp_protocol; + +#endif /* _GPXE_TCP_H */ diff --git a/gpxe/src/include/gpxe/tcpip.h b/gpxe/src/include/gpxe/tcpip.h new file mode 100644 index 0000000..da89530 --- /dev/null +++ b/gpxe/src/include/gpxe/tcpip.h @@ -0,0 +1,121 @@ +#ifndef _GPXE_TCPIP_H +#define _GPXE_TCPIP_H + +/** @file + * + * Transport-network layer interface + * + */ + +#include +#include +#include +#include + +struct io_buffer; +struct net_device; + +/** Empty checksum value + * + * This is the TCP/IP checksum over a zero-length block of data. + */ +#define TCPIP_EMPTY_CSUM 0xffff + +/** + * TCP/IP socket address + * + * This contains the fields common to socket addresses for all TCP/IP + * address families. + */ +struct sockaddr_tcpip { + /** Socket address family (part of struct @c sockaddr) */ + sa_family_t st_family; + /** TCP/IP port */ + uint16_t st_port; + /** Padding + * + * This ensures that a struct @c sockaddr_tcpip is large + * enough to hold a socket address for any TCP/IP address + * family. + */ + char pad[ sizeof ( struct sockaddr ) - + ( sizeof ( sa_family_t ) + sizeof ( uint16_t ) ) ]; +} __attribute__ (( may_alias )); + +/** + * A transport-layer protocol of the TCP/IP stack (eg. UDP, TCP, etc) + */ +struct tcpip_protocol { + /** Protocol name */ + const char *name; + /** + * Process received packet + * + * @v iobuf I/O buffer + * @v st_src Partially-filled source address + * @v st_dest Partially-filled destination address + * @v pshdr_csum Pseudo-header checksum + * @ret rc Return status code + * + * This method takes ownership of the I/O buffer. + */ + int ( * rx ) ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src, + struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ); + /** + * Transport-layer protocol number + * + * This is a constant of the type IP_XXX + */ + uint8_t tcpip_proto; +}; + +/** + * A network-layer protocol of the TCP/IP stack (eg. IPV4, IPv6, etc) + */ +struct tcpip_net_protocol { + /** Protocol name */ + const char *name; + /** Network address family */ + sa_family_t sa_family; + /** + * Transmit packet + * + * @v iobuf I/O buffer + * @v tcpip_protocol Transport-layer protocol + * @v st_src Source address, or NULL to use default + * @v st_dest Destination address + * @v netdev Network device (or NULL to route automatically) + * @v trans_csum Transport-layer checksum to complete, or NULL + * @ret rc Return status code + * + * This function takes ownership of the I/O buffer. + */ + int ( * tx ) ( struct io_buffer *iobuf, + struct tcpip_protocol *tcpip_protocol, + struct sockaddr_tcpip *st_src, + struct sockaddr_tcpip *st_dest, + struct net_device *netdev, + uint16_t *trans_csum ); +}; + +/** Declare a TCP/IP transport-layer protocol */ +#define __tcpip_protocol \ + __table ( struct tcpip_protocol, tcpip_protocols, 01 ) + +/** Declare a TCP/IP network-layer protocol */ +#define __tcpip_net_protocol \ + __table ( struct tcpip_net_protocol, tcpip_net_protocols, 01 ) + +extern int tcpip_rx ( struct io_buffer *iobuf, uint8_t tcpip_proto, + struct sockaddr_tcpip *st_src, + struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ); +extern int tcpip_tx ( struct io_buffer *iobuf, struct tcpip_protocol *tcpip, + struct sockaddr_tcpip *st_src, + struct sockaddr_tcpip *st_dest, + struct net_device *netdev, + uint16_t *trans_csum ); +extern uint16_t tcpip_continue_chksum ( uint16_t partial, + const void *data, size_t len ); +extern uint16_t tcpip_chksum ( const void *data, size_t len ); + +#endif /* _GPXE_TCPIP_H */ diff --git a/gpxe/src/include/gpxe/tftp.h b/gpxe/src/include/gpxe/tftp.h new file mode 100644 index 0000000..0177a95 --- /dev/null +++ b/gpxe/src/include/gpxe/tftp.h @@ -0,0 +1,83 @@ +#ifndef _GPXE_TFTP_H +#define _GPXE_TFTP_H + +/** @file + * + * TFTP protocol + * + */ + +#include + +#define TFTP_PORT 69 /**< Default TFTP server port */ +#define TFTP_DEFAULT_BLKSIZE 512 /**< Default TFTP data block size */ +#define TFTP_MAX_BLKSIZE 1432 + +#define TFTP_RRQ 1 /**< Read request opcode */ +#define TFTP_WRQ 2 /**< Write request opcode */ +#define TFTP_DATA 3 /**< Data block opcode */ +#define TFTP_ACK 4 /**< Data block acknowledgement opcode */ +#define TFTP_ERROR 5 /**< Error opcode */ +#define TFTP_OACK 6 /**< Options acknowledgement opcode */ + +#define TFTP_ERR_FILE_NOT_FOUND 1 /**< File not found */ +#define TFTP_ERR_ACCESS_DENIED 2 /**< Access violation */ +#define TFTP_ERR_DISK_FULL 3 /**< Disk full or allocation exceeded */ +#define TFTP_ERR_ILLEGAL_OP 4 /**< Illegal TFTP operation */ +#define TFTP_ERR_UNKNOWN_TID 5 /**< Unknown transfer ID */ +#define TFTP_ERR_FILE_EXISTS 6 /**< File already exists */ +#define TFTP_ERR_UNKNOWN_USER 7 /**< No such user */ +#define TFTP_ERR_BAD_OPTS 8 /**< Option negotiation failed */ + +#define MTFTP_PORT 1759 /**< Default MTFTP server port */ + +/** A TFTP read request (RRQ) packet */ +struct tftp_rrq { + uint16_t opcode; + char data[0]; +} __attribute__ (( packed )); + +/** A TFTP data (DATA) packet */ +struct tftp_data { + uint16_t opcode; + uint16_t block; + uint8_t data[0]; +} __attribute__ (( packed )); + +/** A TFTP acknowledgement (ACK) packet */ +struct tftp_ack { + uint16_t opcode; + uint16_t block; +} __attribute__ (( packed )); + +/** A TFTP error (ERROR) packet */ +struct tftp_error { + uint16_t opcode; + uint16_t errcode; + char errmsg[0]; +} __attribute__ (( packed )); + +/** A TFTP options acknowledgement (OACK) packet */ +struct tftp_oack { + uint16_t opcode; + char data[0]; +} __attribute__ (( packed )); + +/** The common header of all TFTP packets */ +struct tftp_common { + uint16_t opcode; +} __attribute__ (( packed )); + +/** A union encapsulating all TFTP packet types */ +union tftp_any { + struct tftp_common common; + struct tftp_rrq rrq; + struct tftp_data data; + struct tftp_ack ack; + struct tftp_error error; + struct tftp_oack oack; +}; + +extern void tftp_set_request_blksize ( unsigned int blksize ); + +#endif /* _GPXE_TFTP_H */ diff --git a/gpxe/src/include/gpxe/threewire.h b/gpxe/src/include/gpxe/threewire.h new file mode 100644 index 0000000..865fc25 --- /dev/null +++ b/gpxe/src/include/gpxe/threewire.h @@ -0,0 +1,89 @@ +#ifndef _GPXE_THREEWIRE_H +#define _GPXE_THREEWIRE_H + +/** @file + * + * Three-wire serial interface + * + * The Atmel three-wire interface is a subset of the (newer) SPI + * interface, and is implemented here as a layer on top of the SPI + * support. + */ + +#include +#include + +/** + * @defgroup tcmds Three-wire commands + * @{ + */ + +/** Read data from memory array */ +#define THREEWIRE_READ 0x6 + +/** Write data to memory array */ +#define THREEWIRE_WRITE 0x5 + +/** Write enable */ +#define THREEWIRE_EWEN 0x4 + +/** Address to be used for write enable command */ +#define THREEWIRE_EWEN_ADDRESS INT_MAX + +/** Time to wait for write cycles to complete + * + * This is sufficient for AT93C46/AT93C56 devices, but may need to be + * increased in future when other devices are added. + */ +#define THREEWIRE_WRITE_MDELAY 10 + +/** @} */ + +extern int threewire_read ( struct nvs_device *nvs, unsigned int address, + void *data, size_t len ); +extern int threewire_write ( struct nvs_device *nvs, unsigned int address, + const void *data, size_t len ); + +/** + * @defgroup tdevs Three-wire device types + * @{ + */ + +static inline __attribute__ (( always_inline )) void +init_at93cx6 ( struct spi_device *device, unsigned int organisation ) { + device->nvs.word_len_log2 = ( ( organisation == 8 ) ? 0 : 1 ); + device->nvs.block_size = 1; + device->command_len = 3, + device->nvs.read = threewire_read; + device->nvs.write = threewire_write; +} + +/** + * Initialise Atmel AT93C46 serial EEPROM + * + * @v device SPI device + * @v organisation Word organisation (8 or 16) + */ +static inline __attribute__ (( always_inline )) void +init_at93c46 ( struct spi_device *device, unsigned int organisation ) { + device->nvs.size = ( 1024 / organisation ); + device->address_len = ( ( organisation == 8 ) ? 7 : 6 ); + init_at93cx6 ( device, organisation ); +} + +/** + * Initialise Atmel AT93C56 serial EEPROM + * + * @v device SPI device + * @v organisation Word organisation (8 or 16) + */ +static inline __attribute__ (( always_inline )) void +init_at93c56 ( struct spi_device *device, unsigned int organisation ) { + device->nvs.size = ( 2048 / organisation ); + device->address_len = ( ( organisation == 8 ) ? 9 : 8 ); + init_at93cx6 ( device, organisation ); +} + +/** @} */ + +#endif /* _GPXE_THREEWIRE_H */ diff --git a/gpxe/src/include/gpxe/timer.h b/gpxe/src/include/gpxe/timer.h new file mode 100644 index 0000000..862d87b --- /dev/null +++ b/gpxe/src/include/gpxe/timer.h @@ -0,0 +1,74 @@ +#ifndef _GPXE_TIMER_H +#define _GPXE_TIMER_H + +/** @file + * + * gPXE timer API + * + * The timer API provides udelay() for fixed delays, and currticks() + * for a monotonically increasing tick counter. + */ + +#include +#include + +/** + * Calculate static inline timer API function name + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @ret _subsys_func Subsystem API function + */ +#define TIMER_INLINE( _subsys, _api_func ) \ + SINGLE_API_INLINE ( TIMER_PREFIX_ ## _subsys, _api_func ) + +/** + * Provide a timer API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @v _func Implementing function + */ +#define PROVIDE_TIMER( _subsys, _api_func, _func ) \ + PROVIDE_SINGLE_API ( TIMER_PREFIX_ ## _subsys, _api_func, _func ) + +/** + * Provide a static inline timer API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + */ +#define PROVIDE_TIMER_INLINE( _subsys, _api_func ) \ + PROVIDE_SINGLE_API_INLINE ( TIMER_PREFIX_ ## _subsys, _api_func ) + +/* Include all architecture-independent I/O API headers */ +#include + +/* Include all architecture-dependent I/O API headers */ +#include + +/** + * Delay for a fixed number of microseconds + * + * @v usecs Number of microseconds for which to delay + */ +void udelay ( unsigned long usecs ); + +/** + * Get current system time in ticks + * + * @ret ticks Current time, in ticks + */ +unsigned long currticks ( void ); + +/** + * Get number of ticks per second + * + * @ret ticks_per_sec Number of ticks per second + */ +unsigned long ticks_per_sec ( void ); + +/** Number of ticks per second */ +#define TICKS_PER_SEC ( ticks_per_sec() ) + +#endif /* _GPXE_TIMER_H */ diff --git a/gpxe/src/include/gpxe/tls.h b/gpxe/src/include/gpxe/tls.h new file mode 100644 index 0000000..ddec7be --- /dev/null +++ b/gpxe/src/include/gpxe/tls.h @@ -0,0 +1,185 @@ +#ifndef _GPXE_TLS_H +#define _GPXE_TLS_H + +/** + * @file + * + * Transport Layer Security Protocol + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** A TLS header */ +struct tls_header { + /** Content type + * + * This is a TLS_TYPE_XXX constant + */ + uint8_t type; + /** Protocol version + * + * This is a TLS_VERSION_XXX constant + */ + uint16_t version; + /** Length of payload */ + uint16_t length; +} __attribute__ (( packed )); + +/** TLS version 1.0 */ +#define TLS_VERSION_TLS_1_0 0x0301 + +/** TLS version 1.1 */ +#define TLS_VERSION_TLS_1_1 0x0302 + +/** Change cipher content type */ +#define TLS_TYPE_CHANGE_CIPHER 20 + +/** Alert content type */ +#define TLS_TYPE_ALERT 21 + +/** Handshake content type */ +#define TLS_TYPE_HANDSHAKE 22 + +/** Application data content type */ +#define TLS_TYPE_DATA 23 + +/* Handshake message types */ +#define TLS_HELLO_REQUEST 0 +#define TLS_CLIENT_HELLO 1 +#define TLS_SERVER_HELLO 2 +#define TLS_CERTIFICATE 11 +#define TLS_SERVER_KEY_EXCHANGE 12 +#define TLS_CERTIFICATE_REQUEST 13 +#define TLS_SERVER_HELLO_DONE 14 +#define TLS_CERTIFICATE_VERIFY 15 +#define TLS_CLIENT_KEY_EXCHANGE 16 +#define TLS_FINISHED 20 + +/* TLS alert levels */ +#define TLS_ALERT_WARNING 1 +#define TLS_ALERT_FATAL 2 + +/* TLS cipher specifications */ +#define TLS_RSA_WITH_NULL_MD5 0x0001 +#define TLS_RSA_WITH_NULL_SHA 0x0002 +#define TLS_RSA_WITH_AES_128_CBC_SHA 0x002f +#define TLS_RSA_WITH_AES_256_CBC_SHA 0x0035 + +/** TLS RX state machine state */ +enum tls_rx_state { + TLS_RX_HEADER = 0, + TLS_RX_DATA, +}; + +/** TLS TX state machine state */ +enum tls_tx_state { + TLS_TX_NONE = 0, + TLS_TX_CLIENT_HELLO, + TLS_TX_CLIENT_KEY_EXCHANGE, + TLS_TX_CHANGE_CIPHER, + TLS_TX_FINISHED, + TLS_TX_DATA +}; + +/** A TLS cipher specification */ +struct tls_cipherspec { + /** Public-key encryption algorithm */ + struct pubkey_algorithm *pubkey; + /** Bulk encryption cipher algorithm */ + struct cipher_algorithm *cipher; + /** MAC digest algorithm */ + struct digest_algorithm *digest; + /** Key length */ + size_t key_len; + /** Dynamically-allocated storage */ + void *dynamic; + /** Public key encryption context */ + void *pubkey_ctx; + /** Bulk encryption cipher context */ + void *cipher_ctx; + /** Next bulk encryption cipher context (TX only) */ + void *cipher_next_ctx; + /** MAC secret */ + void *mac_secret; +}; + +/** TLS pre-master secret */ +struct tls_pre_master_secret { + /** TLS version */ + uint16_t version; + /** Random data */ + uint8_t random[46]; +} __attribute__ (( packed )); + +/** TLS client random data */ +struct tls_client_random { + /** GMT Unix time */ + uint32_t gmt_unix_time; + /** Random data */ + uint8_t random[28]; +} __attribute__ (( packed )); + +/** A TLS session */ +struct tls_session { + /** Reference counter */ + struct refcnt refcnt; + + /** Plaintext stream */ + struct xfer_filter_half plainstream; + /** Ciphertext stream */ + struct xfer_filter_half cipherstream; + + /** Current TX cipher specification */ + struct tls_cipherspec tx_cipherspec; + /** Next TX cipher specification */ + struct tls_cipherspec tx_cipherspec_pending; + /** Current RX cipher specification */ + struct tls_cipherspec rx_cipherspec; + /** Next RX cipher specification */ + struct tls_cipherspec rx_cipherspec_pending; + /** Premaster secret */ + struct tls_pre_master_secret pre_master_secret; + /** Master secret */ + uint8_t master_secret[48]; + /** Server random bytes */ + uint8_t server_random[32]; + /** Client random bytes */ + struct tls_client_random client_random; + /** MD5 context for handshake verification */ + uint8_t handshake_md5_ctx[MD5_CTX_SIZE]; + /** SHA1 context for handshake verification */ + uint8_t handshake_sha1_ctx[SHA1_CTX_SIZE]; + + /** Hack: server RSA public key */ + struct x509_rsa_public_key rsa; + + /** TX sequence number */ + uint64_t tx_seq; + /** TX state */ + enum tls_tx_state tx_state; + /** TX process */ + struct process process; + + /** RX sequence number */ + uint64_t rx_seq; + /** RX state */ + enum tls_rx_state rx_state; + /** Offset within current RX state */ + size_t rx_rcvd; + /** Current received record header */ + struct tls_header rx_header; + /** Current received raw data buffer */ + void *rx_data; +}; + +extern int add_tls ( struct xfer_interface *xfer, + struct xfer_interface **next ); + +#endif /* _GPXE_TLS_H */ diff --git a/gpxe/src/include/gpxe/uaccess.h b/gpxe/src/include/gpxe/uaccess.h new file mode 100644 index 0000000..33aaed1 --- /dev/null +++ b/gpxe/src/include/gpxe/uaccess.h @@ -0,0 +1,342 @@ +#ifndef _GPXE_UACCESS_H +#define _GPXE_UACCESS_H + +/** + * @file + * + * Access to external ("user") memory + * + * gPXE often needs to transfer data between internal and external + * buffers. On i386, the external buffers may require access via a + * different segment, and the buffer address cannot be encoded into a + * simple void * pointer. The @c userptr_t type encapsulates the + * information needed to identify an external buffer, and the + * copy_to_user() and copy_from_user() functions provide methods for + * transferring data between internal and external buffers. + * + * Note that userptr_t is an opaque type; in particular, performing + * arithmetic upon a userptr_t is not allowed. + * + */ + +#include +#include +#include +#include + +/** + * A pointer to a user buffer + * + */ +typedef unsigned long userptr_t; + +/** Equivalent of NULL for user pointers */ +#define UNULL ( ( userptr_t ) 0 ) + +/** + * @defgroup uaccess_trivial Trivial user access API implementations + * + * User access API implementations that can be used by environments in + * which virtual addresses allow access to all of memory. + * + * @{ + * + */ + +/** + * Convert virtual address to user pointer + * + * @v addr Virtual address + * @ret userptr User pointer + */ +static inline __always_inline userptr_t +trivial_virt_to_user ( volatile const void *addr ) { + return ( ( userptr_t ) addr ); +} + +/** + * Convert user pointer to virtual address + * + * @v userptr User pointer + * @v offset Offset from user pointer + * @ret addr Virtual address + * + * This operation is not available under all memory models. + */ +static inline __always_inline void * +trivial_user_to_virt ( userptr_t userptr, off_t offset ) { + return ( ( void * ) userptr + offset ); +} + +/** + * Add offset to user pointer + * + * @v userptr User pointer + * @v offset Offset + * @ret userptr New pointer value + */ +static inline __always_inline userptr_t +trivial_userptr_add ( userptr_t userptr, off_t offset ) { + return ( userptr + offset ); +} + +/** + * Copy data between user buffers + * + * @v dest Destination + * @v dest_off Destination offset + * @v src Source + * @v src_off Source offset + * @v len Length + */ +static inline __always_inline void +trivial_memcpy_user ( userptr_t dest, off_t dest_off, + userptr_t src, off_t src_off, size_t len ) { + memcpy ( ( ( void * ) dest + dest_off ), + ( ( void * ) src + src_off ), len ); +} + +/** + * Copy data between user buffers, allowing for overlap + * + * @v dest Destination + * @v dest_off Destination offset + * @v src Source + * @v src_off Source offset + * @v len Length + */ +static inline __always_inline void +trivial_memmove_user ( userptr_t dest, off_t dest_off, + userptr_t src, off_t src_off, size_t len ) { + memmove ( ( ( void * ) dest + dest_off ), + ( ( void * ) src + src_off ), len ); +} + +/** + * Fill user buffer with a constant byte + * + * @v buffer User buffer + * @v offset Offset within buffer + * @v c Constant byte with which to fill + * @v len Length + */ +static inline __always_inline void +trivial_memset_user ( userptr_t buffer, off_t offset, int c, size_t len ) { + memset ( ( ( void * ) buffer + offset ), c, len ); +} + +/** + * Find length of NUL-terminated string in user buffer + * + * @v buffer User buffer + * @v offset Offset within buffer + * @ret len Length of string (excluding NUL) + */ +static inline __always_inline size_t +trivial_strlen_user ( userptr_t buffer, off_t offset ) { + return strlen ( ( void * ) buffer + offset ); +} + +/** + * Find character in user buffer + * + * @v buffer User buffer + * @v offset Starting offset within buffer + * @v c Character to search for + * @v len Length of user buffer + * @ret offset Offset of character, or <0 if not found + */ +static inline __always_inline off_t +trivial_memchr_user ( userptr_t buffer, off_t offset, int c, size_t len ) { + void *found; + + found = memchr ( ( ( void * ) buffer + offset ), c, len ); + return ( found ? ( found - ( void * ) buffer ) : -1 ); +} + +/** @} */ + +/** + * Calculate static inline user access API function name + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @ret _subsys_func Subsystem API function + */ +#define UACCESS_INLINE( _subsys, _api_func ) \ + SINGLE_API_INLINE ( UACCESS_PREFIX_ ## _subsys, _api_func ) + +/** + * Provide an user access API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @v _func Implementing function + */ +#define PROVIDE_UACCESS( _subsys, _api_func, _func ) \ + PROVIDE_SINGLE_API ( UACCESS_PREFIX_ ## _subsys, _api_func, _func ) + +/** + * Provide a static inline user access API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + */ +#define PROVIDE_UACCESS_INLINE( _subsys, _api_func ) \ + PROVIDE_SINGLE_API_INLINE ( UACCESS_PREFIX_ ## _subsys, _api_func ) + +/* Include all architecture-independent user access API headers */ +#include + +/* Include all architecture-dependent user access API headers */ +#include + +/** + * Convert physical address to user pointer + * + * @v phys_addr Physical address + * @ret userptr User pointer + */ +userptr_t phys_to_user ( unsigned long phys_addr ); + +/** + * Convert user pointer to physical address + * + * @v userptr User pointer + * @v offset Offset from user pointer + * @ret phys_addr Physical address + */ +unsigned long user_to_phys ( userptr_t userptr, off_t offset ); + +/** + * Convert virtual address to user pointer + * + * @v addr Virtual address + * @ret userptr User pointer + */ +userptr_t virt_to_user ( volatile const void *addr ); + +/** + * Convert user pointer to virtual address + * + * @v userptr User pointer + * @v offset Offset from user pointer + * @ret addr Virtual address + * + * This operation is not available under all memory models. + */ +void * user_to_virt ( userptr_t userptr, off_t offset ); + +/** + * Add offset to user pointer + * + * @v userptr User pointer + * @v offset Offset + * @ret userptr New pointer value + */ +userptr_t userptr_add ( userptr_t userptr, off_t offset ); + +/** + * Convert virtual address to a physical address + * + * @v addr Virtual address + * @ret phys_addr Physical address + */ +static inline __always_inline unsigned long +virt_to_phys ( volatile const void *addr ) { + return user_to_phys ( virt_to_user ( addr ), 0 ); +} + +/** + * Convert physical address to a virtual address + * + * @v addr Virtual address + * @ret phys_addr Physical address + * + * This operation is not available under all memory models. + */ +static inline __always_inline void * phys_to_virt ( unsigned long phys_addr ) { + return user_to_virt ( phys_to_user ( phys_addr ), 0 ); +} + +/** + * Copy data between user buffers + * + * @v dest Destination + * @v dest_off Destination offset + * @v src Source + * @v src_off Source offset + * @v len Length + */ +void memcpy_user ( userptr_t dest, off_t dest_off, + userptr_t src, off_t src_off, size_t len ); + +/** + * Copy data to user buffer + * + * @v dest Destination + * @v dest_off Destination offset + * @v src Source + * @v len Length + */ +static inline __always_inline void +copy_to_user ( userptr_t dest, off_t dest_off, const void *src, size_t len ) { + memcpy_user ( dest, dest_off, virt_to_user ( src ), 0, len ); +} + +/** + * Copy data from user buffer + * + * @v dest Destination + * @v src Source + * @v src_off Source offset + * @v len Length + */ +static inline __always_inline void +copy_from_user ( void *dest, userptr_t src, off_t src_off, size_t len ) { + memcpy_user ( virt_to_user ( dest ), 0, src, src_off, len ); +} + +/** + * Copy data between user buffers, allowing for overlap + * + * @v dest Destination + * @v dest_off Destination offset + * @v src Source + * @v src_off Source offset + * @v len Length + */ +void memmove_user ( userptr_t dest, off_t dest_off, + userptr_t src, off_t src_off, size_t len ); + +/** + * Fill user buffer with a constant byte + * + * @v userptr User buffer + * @v offset Offset within buffer + * @v c Constant byte with which to fill + * @v len Length + */ +void memset_user ( userptr_t userptr, off_t offset, int c, size_t len ); + +/** + * Find length of NUL-terminated string in user buffer + * + * @v userptr User buffer + * @v offset Offset within buffer + * @ret len Length of string (excluding NUL) + */ +size_t strlen_user ( userptr_t userptr, off_t offset ); + +/** + * Find character in user buffer + * + * @v userptr User buffer + * @v offset Starting offset within buffer + * @v c Character to search for + * @v len Length of user buffer + * @ret offset Offset of character, or <0 if not found + */ +off_t memchr_user ( userptr_t userptr, off_t offset, int c, size_t len ); + +#endif /* _GPXE_UACCESS_H */ diff --git a/gpxe/src/include/gpxe/udp.h b/gpxe/src/include/gpxe/udp.h new file mode 100644 index 0000000..e515f65 --- /dev/null +++ b/gpxe/src/include/gpxe/udp.h @@ -0,0 +1,46 @@ +#ifndef _GPXE_UDP_H +#define _GPXE_UDP_H + +/** @file + * + * UDP protocol + * + * This file defines the gPXE UDP API. + * + */ + +#include +#include +#include +#include + +struct xfer_interface; + +/** + * UDP constants + */ + +#define UDP_MAX_HLEN 72 +#define UDP_MAX_TXIOB ETH_MAX_MTU +#define UDP_MIN_TXIOB ETH_ZLEN + +/** + * A UDP header + */ +struct udp_header { + /** Source port */ + uint16_t src; + /** Destination port */ + uint16_t dest; + /** Length */ + uint16_t len; + /** Checksum */ + uint16_t chksum; +}; + +extern int udp_open_promisc ( struct xfer_interface *xfer ); +extern int udp_open ( struct xfer_interface *xfer, struct sockaddr *peer, + struct sockaddr *local ); + +#endif /* _GPXE_UDP_H */ + diff --git a/gpxe/src/include/gpxe/umalloc.h b/gpxe/src/include/gpxe/umalloc.h new file mode 100644 index 0000000..e6fc7bf --- /dev/null +++ b/gpxe/src/include/gpxe/umalloc.h @@ -0,0 +1,66 @@ +#ifndef _GPXE_UMALLOC_H +#define _GPXE_UMALLOC_H + +/** + * @file + * + * User memory allocation + * + */ + +#include +#include +#include + +/** + * Provide a user memory allocation API implementation + * + * @v _prefix Subsystem prefix + * @v _api_func API function + * @v _func Implementing function + */ +#define PROVIDE_UMALLOC( _subsys, _api_func, _func ) \ + PROVIDE_SINGLE_API ( UMALLOC_PREFIX_ ## _subsys, _api_func, _func ) + +/* Include all architecture-independent I/O API headers */ +#include + +/* Include all architecture-dependent I/O API headers */ +#include + +/** + * Reallocate external memory + * + * @v userptr Memory previously allocated by umalloc(), or UNULL + * @v new_size Requested size + * @ret userptr Allocated memory, or UNULL + * + * Calling realloc() with a new size of zero is a valid way to free a + * memory block. + */ +userptr_t urealloc ( userptr_t userptr, size_t new_size ); + +/** + * Allocate external memory + * + * @v size Requested size + * @ret userptr Memory, or UNULL + * + * Memory is guaranteed to be aligned to a page boundary. + */ +static inline __always_inline userptr_t umalloc ( size_t size ) { + return urealloc ( UNULL, size ); +} + +/** + * Free external memory + * + * @v userptr Memory allocated by umalloc(), or UNULL + * + * If @c ptr is UNULL, no action is taken. + */ +static inline __always_inline void ufree ( userptr_t userptr ) { + urealloc ( userptr, 0 ); +} + +#endif /* _GPXE_UMALLOC_H */ diff --git a/gpxe/src/include/gpxe/uri.h b/gpxe/src/include/gpxe/uri.h new file mode 100644 index 0000000..3803868 --- /dev/null +++ b/gpxe/src/include/gpxe/uri.h @@ -0,0 +1,142 @@ +#ifndef _GPXE_URI_H +#define _GPXE_URI_H + +/** @file + * + * Uniform Resource Identifiers + * + */ + +#include +#include +#include + +/** A Uniform Resource Identifier + * + * Terminology for this data structure is as per uri(7), except that + * "path" is defined to include the leading '/' for an absolute path. + * + * Note that all fields within a URI are optional and may be NULL. + * + * Some examples are probably helpful: + * + * http://www.etherboot.org/wiki : + * + * scheme = "http", host = "www.etherboot.org", path = "/wiki" + * + * /var/lib/tftpboot : + * + * path = "/var/lib/tftpboot" + * + * mailto:bob@nowhere.com : + * + * scheme = "mailto", opaque = "bob@nowhere.com" + * + * ftp://joe:secret@insecure.org:8081/hidden/path/to?what=is#this + * + * scheme = "ftp", user = "joe", password = "secret", + * host = "insecure.org", port = "8081", path = "/hidden/path/to", + * query = "what=is", fragment = "this" + */ +struct uri { + /** Reference count */ + struct refcnt refcnt; + /** Scheme */ + const char *scheme; + /** Opaque part */ + const char *opaque; + /** User name */ + const char *user; + /** Password */ + const char *password; + /** Host name */ + const char *host; + /** Port number */ + const char *port; + /** Path */ + const char *path; + /** Query */ + const char *query; + /** Fragment */ + const char *fragment; +}; + +/** + * URI is an absolute URI + * + * @v uri URI + * @ret is_absolute URI is absolute + * + * An absolute URI begins with a scheme, e.g. "http:" or "mailto:". + * Note that this is a separate concept from a URI with an absolute + * path. + */ +static inline int uri_is_absolute ( struct uri *uri ) { + return ( uri->scheme != NULL ); +} + +/** + * URI has an absolute path + * + * @v uri URI + * @ret has_absolute_path URI has an absolute path + * + * An absolute path begins with a '/'. Note that this is a separate + * concept from an absolute URI. Note also that a URI may not have a + * path at all. + */ +static inline int uri_has_absolute_path ( struct uri *uri ) { + return ( uri->path && ( uri->path[0] == '/' ) ); +} + +/** + * URI has a relative path + * + * @v uri URI + * @ret has_relative_path URI has a relative path + * + * A relative path begins with something other than a '/'. Note that + * this is a separate concept from a relative URI. Note also that a + * URI may not have a path at all. + */ +static inline int uri_has_relative_path ( struct uri *uri ) { + return ( uri->path && ( uri->path[0] != '/' ) ); +} + +/** + * Increment URI reference count + * + * @v uri URI, or NULL + * @ret uri URI as passed in + */ +static inline __attribute__ (( always_inline )) struct uri * +uri_get ( struct uri *uri ) { + ref_get ( &uri->refcnt ); + return uri; +} + +/** + * Decrement URI reference count + * + * @v uri URI, or NULL + */ +static inline __attribute__ (( always_inline )) void +uri_put ( struct uri *uri ) { + ref_put ( &uri->refcnt ); +} + +extern struct uri *cwuri; + +extern struct uri * parse_uri ( const char *uri_string ); +extern unsigned int uri_port ( struct uri *uri, unsigned int default_port ); +extern int unparse_uri ( char *buf, size_t size, struct uri *uri ); +extern struct uri * uri_dup ( struct uri *uri ); +extern char * resolve_path ( const char *base_path, + const char *relative_path ); +extern struct uri * resolve_uri ( struct uri *base_uri, + struct uri *relative_uri ); +extern void churi ( struct uri *uri ); +extern size_t uri_encode ( const char *raw_string, char *buf, size_t len ); +extern size_t uri_decode ( const char *encoded_string, char *buf, size_t len ); + +#endif /* _GPXE_URI_H */ diff --git a/gpxe/src/include/gpxe/uuid.h b/gpxe/src/include/gpxe/uuid.h new file mode 100644 index 0000000..18d1f14 --- /dev/null +++ b/gpxe/src/include/gpxe/uuid.h @@ -0,0 +1,31 @@ +#ifndef _GPXE_UUID_H +#define _GPXE_UUID_H + +/** @file + * + * Universally unique IDs + */ + +#include + +/** A universally unique ID */ +union uuid { + /** Canonical form (00000000-0000-0000-0000-000000000000) */ + struct { + /** 8 hex digits, big-endian */ + uint32_t a; + /** 2 hex digits, big-endian */ + uint16_t b; + /** 2 hex digits, big-endian */ + uint16_t c; + /** 2 hex digits, big-endian */ + uint16_t d; + /** 12 hex digits, big-endian */ + uint8_t e[6]; + } canonical; + uint8_t raw[16]; +}; + +extern char * uuid_ntoa ( union uuid *uuid ); + +#endif /* _GPXE_UUID_H */ diff --git a/gpxe/src/include/gpxe/virtio-pci.h b/gpxe/src/include/gpxe/virtio-pci.h new file mode 100644 index 0000000..f0c17e8 --- /dev/null +++ b/gpxe/src/include/gpxe/virtio-pci.h @@ -0,0 +1,97 @@ +#ifndef _VIRTIO_PCI_H_ +# define _VIRTIO_PCI_H_ + +/* A 32-bit r/o bitmask of the features supported by the host */ +#define VIRTIO_PCI_HOST_FEATURES 0 + +/* A 32-bit r/w bitmask of features activated by the guest */ +#define VIRTIO_PCI_GUEST_FEATURES 4 + +/* A 32-bit r/w PFN for the currently selected queue */ +#define VIRTIO_PCI_QUEUE_PFN 8 + +/* A 16-bit r/o queue size for the currently selected queue */ +#define VIRTIO_PCI_QUEUE_NUM 12 + +/* A 16-bit r/w queue selector */ +#define VIRTIO_PCI_QUEUE_SEL 14 + +/* A 16-bit r/w queue notifier */ +#define VIRTIO_PCI_QUEUE_NOTIFY 16 + +/* An 8-bit device status register. */ +#define VIRTIO_PCI_STATUS 18 + +/* An 8-bit r/o interrupt status register. Reading the value will return the + * current contents of the ISR and will also clear it. This is effectively + * a read-and-acknowledge. */ +#define VIRTIO_PCI_ISR 19 + +/* The bit of the ISR which indicates a device configuration change. */ +#define VIRTIO_PCI_ISR_CONFIG 0x2 + +/* The remaining space is defined by each driver as the per-driver + * configuration space */ +#define VIRTIO_PCI_CONFIG 20 + +/* Virtio ABI version, this must match exactly */ +#define VIRTIO_PCI_ABI_VERSION 0 + +static inline u32 vp_get_features(unsigned int ioaddr) +{ + return inl(ioaddr + VIRTIO_PCI_HOST_FEATURES); +} + +static inline void vp_set_features(unsigned int ioaddr, u32 features) +{ + outl(features, ioaddr + VIRTIO_PCI_GUEST_FEATURES); +} + +static inline void vp_get(unsigned int ioaddr, unsigned offset, + void *buf, unsigned len) +{ + u8 *ptr = buf; + unsigned i; + + for (i = 0; i < len; i++) + ptr[i] = inb(ioaddr + VIRTIO_PCI_CONFIG + offset + i); +} + +static inline u8 vp_get_status(unsigned int ioaddr) +{ + return inb(ioaddr + VIRTIO_PCI_STATUS); +} + +static inline void vp_set_status(unsigned int ioaddr, u8 status) +{ + if (status == 0) /* reset */ + return; + outb(status, ioaddr + VIRTIO_PCI_STATUS); +} + + +static inline void vp_reset(unsigned int ioaddr) +{ + outb(0, ioaddr + VIRTIO_PCI_STATUS); + (void)inb(ioaddr + VIRTIO_PCI_ISR); +} + +static inline void vp_notify(unsigned int ioaddr, int queue_index) +{ + outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_NOTIFY); +} + +static inline void vp_del_vq(unsigned int ioaddr, int queue_index) +{ + /* select the queue */ + + outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_SEL); + + /* deactivate the queue */ + + outl(0, ioaddr + VIRTIO_PCI_QUEUE_PFN); +} + +int vp_find_vq(unsigned int ioaddr, int queue_index, + struct vring_virtqueue *vq); +#endif /* _VIRTIO_PCI_H_ */ diff --git a/gpxe/src/include/gpxe/virtio-ring.h b/gpxe/src/include/gpxe/virtio-ring.h new file mode 100644 index 0000000..e96dd37 --- /dev/null +++ b/gpxe/src/include/gpxe/virtio-ring.h @@ -0,0 +1,142 @@ +#ifndef _VIRTIO_RING_H_ +# define _VIRTIO_RING_H_ +#define PAGE_SHIFT (12) +#define PAGE_SIZE (1<num = num; + + /* physical address of desc must be page aligned */ + + pa = virt_to_phys(queue); + pa = (pa + PAGE_MASK) & ~PAGE_MASK; + vr->desc = phys_to_virt(pa); + + vr->avail = (struct vring_avail *)&vr->desc[num]; + + /* physical address of used must be page aligned */ + + pa = virt_to_phys(&vr->avail->ring[num]); + pa = (pa + PAGE_MASK) & ~PAGE_MASK; + vr->used = phys_to_virt(pa); + + for (i = 0; i < num - 1; i++) + vr->desc[i].next = i + 1; + vr->desc[i].next = 0; +} + +static inline void vring_enable_cb(struct vring_virtqueue *vq) +{ + vq->vring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT; +} + +static inline void vring_disable_cb(struct vring_virtqueue *vq) +{ + vq->vring.avail->flags |= VRING_AVAIL_F_NO_INTERRUPT; +} + + +/* + * vring_more_used + * + * is there some used buffers ? + * + */ + +static inline int vring_more_used(struct vring_virtqueue *vq) +{ + wmb(); + return vq->last_used_idx != vq->vring.used->idx; +} + +void vring_detach(struct vring_virtqueue *vq, unsigned int head); +int vring_get_buf(struct vring_virtqueue *vq, unsigned int *len); +void vring_add_buf(struct vring_virtqueue *vq, struct vring_list list[], + unsigned int out, unsigned int in, + int index, int num_added); +void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int num_added); + +#endif /* _VIRTIO_RING_H_ */ diff --git a/gpxe/src/include/gpxe/vsprintf.h b/gpxe/src/include/gpxe/vsprintf.h new file mode 100644 index 0000000..aa8f8a3 --- /dev/null +++ b/gpxe/src/include/gpxe/vsprintf.h @@ -0,0 +1,72 @@ +#ifndef _GPXE_VSPRINTF_H +#define _GPXE_VSPRINTF_H + +/** @file + * + * printf() and friends + * + * Etherboot's printf() functions understand the following subset of + * the standard C printf()'s format specifiers: + * + * - Flag characters + * - '#' - Alternate form (i.e. "0x" prefix) + * - '0' - Zero-pad + * - Field widths + * - Length modifiers + * - 'hh' - Signed / unsigned char + * - 'h' - Signed / unsigned short + * - 'l' - Signed / unsigned long + * - 'll' - Signed / unsigned long long + * - 'z' - Signed / unsigned size_t + * - Conversion specifiers + * - 'd' - Signed decimal + * - 'x','X' - Unsigned hexadecimal + * - 'c' - Character + * - 's' - String + * - 'p' - Pointer + * + * Hexadecimal numbers are always zero-padded to the specified field + * width (if any); decimal numbers are always space-padded. Decimal + * long longs are not supported. + * + */ + +#include +#include +#include + +/** + * A printf context + * + * Contexts are used in order to be able to share code between + * vprintf() and vsnprintf(), without requiring the allocation of a + * buffer for vprintf(). + */ +struct printf_context { + /** + * Character handler + * + * @v ctx Context + * @v c Character + * + * This method is called for each character written to the + * formatted string. + */ + void ( * handler ) ( struct printf_context *ctx, unsigned int c ); + /** Length of formatted string + * + * When handler() is called, @len will be set to the number of + * characters written so far (i.e. zero for the first call to + * handler()). + */ + size_t len; +}; + +extern size_t vcprintf ( struct printf_context *ctx, const char *fmt, + va_list args ); +extern int vssnprintf ( char *buf, ssize_t ssize, const char *fmt, + va_list args ); +extern int __attribute__ (( format ( printf, 3, 4 ) )) +ssnprintf ( char *buf, ssize_t ssize, const char *fmt, ... ); + +#endif /* _GPXE_VSPRINTF_H */ diff --git a/gpxe/src/include/gpxe/x509.h b/gpxe/src/include/gpxe/x509.h new file mode 100644 index 0000000..071e1de --- /dev/null +++ b/gpxe/src/include/gpxe/x509.h @@ -0,0 +1,39 @@ +#ifndef _GPXE_X509_H +#define _GPXE_X509_H + +/** @file + * + * X.509 certificates + * + */ + +#include + +struct asn1_cursor; + +/** An X.509 RSA public key */ +struct x509_rsa_public_key { + /** Modulus */ + uint8_t *modulus; + /** Modulus length */ + size_t modulus_len; + /** Exponent */ + uint8_t *exponent; + /** Exponent length */ + size_t exponent_len; +}; + +/** + * Free X.509 RSA public key + * + * @v rsa_pubkey RSA public key + */ +static inline void +x509_free_rsa_public_key ( struct x509_rsa_public_key *rsa_pubkey ) { + free ( rsa_pubkey->modulus ); +} + +extern int x509_rsa_public_key ( const struct asn1_cursor *certificate, + struct x509_rsa_public_key *rsa_pubkey ); + +#endif /* _GPXE_X509_H */ diff --git a/gpxe/src/include/gpxe/xfer.h b/gpxe/src/include/gpxe/xfer.h new file mode 100644 index 0000000..e592fa3 --- /dev/null +++ b/gpxe/src/include/gpxe/xfer.h @@ -0,0 +1,275 @@ +#ifndef _GPXE_XFER_H +#define _GPXE_XFER_H + +/** @file + * + * Data transfer interfaces + * + */ + +#include +#include +#include +#include + +struct xfer_interface; +struct xfer_metadata; + +/** Data transfer interface operations */ +struct xfer_interface_operations { + /** Close interface + * + * @v xfer Data transfer interface + * @v rc Reason for close + */ + void ( * close ) ( struct xfer_interface *xfer, int rc ); + /** Redirect to new location + * + * @v xfer Data transfer interface + * @v type New location type + * @v args Remaining arguments depend upon location type + * @ret rc Return status code + */ + int ( * vredirect ) ( struct xfer_interface *xfer, int type, + va_list args ); + /** Check flow control window + * + * @v xfer Data transfer interface + * @ret len Length of window + * + * Flow control is regarded as advisory but not mandatory. + * Users who have control over their own rate of data + * generation should perform a flow control check before + * generating new data. Users who have no control (such as + * NIC drivers or filter layers) are not obliged to check. + * + * Data transfer interfaces must be prepared to accept + * datagrams even if they are advertising a window of zero + * bytes. + */ + size_t ( * window ) ( struct xfer_interface *xfer ); + /** Allocate I/O buffer + * + * @v xfer Data transfer interface + * @v len I/O buffer payload length + * @ret iobuf I/O buffer + */ + struct io_buffer * ( * alloc_iob ) ( struct xfer_interface *xfer, + size_t len ); + /** Deliver datagram as I/O buffer with metadata + * + * @v xfer Data transfer interface + * @v iobuf Datagram I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + * + * A data transfer interface that wishes to support only raw + * data delivery should set this method to + * xfer_deliver_as_raw(). + */ + int ( * deliver_iob ) ( struct xfer_interface *xfer, + struct io_buffer *iobuf, + struct xfer_metadata *meta ); + /** Deliver datagram as raw data + * + * @v xfer Data transfer interface + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + * + * A data transfer interface that wishes to support only I/O + * buffer delivery should set this method to + * xfer_deliver_as_iob(). + */ + int ( * deliver_raw ) ( struct xfer_interface *xfer, + const void *data, size_t len ); +}; + +/** A data transfer interface */ +struct xfer_interface { + /** Generic object communication interface */ + struct interface intf; + /** Operations for received messages */ + struct xfer_interface_operations *op; +}; + +/** Basis positions for seek() events */ +enum seek_whence { + SEEK_CUR = 0, + SEEK_SET, +}; + +/** Data transfer metadata */ +struct xfer_metadata { + /** Position of data within stream */ + off_t offset; + /** Basis for data position + * + * Must be one of @c SEEK_CUR or @c SEEK_SET. + */ + int whence; + /** Source socket address, or NULL */ + struct sockaddr *src; + /** Destination socket address, or NULL */ + struct sockaddr *dest; + /** Network device, or NULL */ + struct net_device *netdev; +}; + +/** + * Describe seek basis + * + * @v whence Basis for new position + */ +static inline __attribute__ (( always_inline )) const char * +whence_text ( int whence ) { + switch ( whence ) { + case SEEK_CUR: return "CUR"; + case SEEK_SET: return "SET"; + default: return "INVALID"; + } +} + +extern struct xfer_interface null_xfer; +extern struct xfer_interface_operations null_xfer_ops; + +extern void xfer_close ( struct xfer_interface *xfer, int rc ); +extern int xfer_vredirect ( struct xfer_interface *xfer, int type, + va_list args ); +extern int xfer_redirect ( struct xfer_interface *xfer, int type, ... ); +extern size_t xfer_window ( struct xfer_interface *xfer ); +extern struct io_buffer * xfer_alloc_iob ( struct xfer_interface *xfer, + size_t len ); +extern int xfer_deliver_iob ( struct xfer_interface *xfer, + struct io_buffer *iobuf ); +extern int xfer_deliver_iob_meta ( struct xfer_interface *xfer, + struct io_buffer *iobuf, + struct xfer_metadata *meta ); +extern int xfer_deliver_raw ( struct xfer_interface *xfer, + const void *data, size_t len ); +extern int xfer_vprintf ( struct xfer_interface *xfer, + const char *format, va_list args ); +extern int __attribute__ (( format ( printf, 2, 3 ) )) +xfer_printf ( struct xfer_interface *xfer, const char *format, ... ); +extern int xfer_seek ( struct xfer_interface *xfer, off_t offset, int whence ); + +extern void ignore_xfer_close ( struct xfer_interface *xfer, int rc ); +extern int ignore_xfer_vredirect ( struct xfer_interface *xfer, + int type, va_list args ); +extern size_t unlimited_xfer_window ( struct xfer_interface *xfer ); +extern size_t no_xfer_window ( struct xfer_interface *xfer ); +extern struct io_buffer * default_xfer_alloc_iob ( struct xfer_interface *xfer, + size_t len ); +extern int xfer_deliver_as_raw ( struct xfer_interface *xfer, + struct io_buffer *iobuf, + struct xfer_metadata *meta ); +extern int xfer_deliver_as_iob ( struct xfer_interface *xfer, + const void *data, size_t len ); +extern int ignore_xfer_deliver_raw ( struct xfer_interface *xfer, + const void *data __unused, size_t len ); + +/** + * Initialise a data transfer interface + * + * @v xfer Data transfer interface + * @v op Data transfer interface operations + * @v refcnt Containing object reference counter, or NULL + */ +static inline void xfer_init ( struct xfer_interface *xfer, + struct xfer_interface_operations *op, + struct refcnt *refcnt ) { + xfer->intf.dest = &null_xfer.intf; + xfer->intf.refcnt = refcnt; + xfer->op = op; +} + +/** + * Initialise a static data transfer interface + * + * @v operations Data transfer interface operations + */ +#define XFER_INIT( operations ) { \ + .intf = { \ + .dest = &null_xfer.intf, \ + .refcnt = NULL, \ + }, \ + .op = operations, \ + } + +/** + * Get data transfer interface from generic object communication interface + * + * @v intf Generic object communication interface + * @ret xfer Data transfer interface + */ +static inline __attribute__ (( always_inline )) struct xfer_interface * +intf_to_xfer ( struct interface *intf ) { + return container_of ( intf, struct xfer_interface, intf ); +} + +/** + * Get reference to destination data transfer interface + * + * @v xfer Data transfer interface + * @ret dest Destination interface + */ +static inline __attribute__ (( always_inline )) struct xfer_interface * +xfer_get_dest ( struct xfer_interface *xfer ) { + return intf_to_xfer ( intf_get ( xfer->intf.dest ) ); +} + +/** + * Drop reference to data transfer interface + * + * @v xfer Data transfer interface + */ +static inline __attribute__ (( always_inline )) void +xfer_put ( struct xfer_interface *xfer ) { + intf_put ( &xfer->intf ); +} + +/** + * Plug a data transfer interface into a new destination interface + * + * @v xfer Data transfer interface + * @v dest New destination interface + */ +static inline __attribute__ (( always_inline )) void +xfer_plug ( struct xfer_interface *xfer, struct xfer_interface *dest ) { + plug ( &xfer->intf, &dest->intf ); +} + +/** + * Plug two data transfer interfaces together + * + * @v a Data transfer interface A + * @v b Data transfer interface B + */ +static inline __attribute__ (( always_inline )) void +xfer_plug_plug ( struct xfer_interface *a, struct xfer_interface *b ) { + plug_plug ( &a->intf, &b->intf ); +} + +/** + * Unplug a data transfer interface + * + * @v xfer Data transfer interface + */ +static inline __attribute__ (( always_inline )) void +xfer_unplug ( struct xfer_interface *xfer ) { + plug ( &xfer->intf, &null_xfer.intf ); +} + +/** + * Stop using a data transfer interface + * + * @v xfer Data transfer interface + * + * After calling this method, no further messages will be received via + * the interface. + */ +static inline void xfer_nullify ( struct xfer_interface *xfer ) { + xfer->op = &null_xfer_ops; +}; + +#endif /* _GPXE_XFER_H */ diff --git a/gpxe/src/include/i82365.h b/gpxe/src/include/i82365.h new file mode 100644 index 0000000..3b0e00c --- /dev/null +++ b/gpxe/src/include/i82365.h @@ -0,0 +1,450 @@ +/* + * i82365.h 1.15 1999/10/25 20:03:34 + * + * The contents of this file may be used under the + * terms of the GNU General Public License version 2 (the "GPL"). + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and + * limitations under the License. + * + * The initial developer of the original code is David A. Hinds + * . Portions created by David A. Hinds + * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + */ + +#ifndef _LINUX_I82365_H +#define _LINUX_I82365_H + +/* register definitions for the Intel 82365SL PCMCIA controller */ + +/* Offsets for PCIC registers */ +#define I365_IDENT 0x00 /* Identification and revision */ +#define I365_STATUS 0x01 /* Interface status */ +#define I365_POWER 0x02 /* Power and RESETDRV control */ +#define I365_INTCTL 0x03 /* Interrupt and general control */ +#define I365_CSC 0x04 /* Card status change */ +#define I365_CSCINT 0x05 /* Card status change interrupt control */ +#define I365_ADDRWIN 0x06 /* Address window enable */ +#define I365_IOCTL 0x07 /* I/O control */ +#define I365_GENCTL 0x16 /* Card detect and general control */ +#define I365_GBLCTL 0x1E /* Global control register */ + +/* Offsets for I/O and memory window registers */ +#define I365_IO(map) (0x08+((map)<<2)) +#define I365_MEM(map) (0x10+((map)<<3)) +#define I365_W_START 0 +#define I365_W_STOP 2 +#define I365_W_OFF 4 + +/* Flags for I365_STATUS */ +#define I365_CS_BVD1 0x01 +#define I365_CS_STSCHG 0x01 +#define I365_CS_BVD2 0x02 +#define I365_CS_SPKR 0x02 +#define I365_CS_DETECT 0x0C +#define I365_CS_WRPROT 0x10 +#define I365_CS_READY 0x20 /* Inverted */ +#define I365_CS_POWERON 0x40 +#define I365_CS_GPI 0x80 + +/* Flags for I365_POWER */ +#define I365_PWR_OFF 0x00 /* Turn off the socket */ +#define I365_PWR_OUT 0x80 /* Output enable */ +#define I365_PWR_NORESET 0x40 /* Disable RESETDRV on resume */ +#define I365_PWR_AUTO 0x20 /* Auto pwr switch enable */ +#define I365_VCC_MASK 0x18 /* Mask for turning off Vcc */ +/* There are different layouts for B-step and DF-step chips: the B + step has independent Vpp1/Vpp2 control, and the DF step has only + Vpp1 control, plus 3V control */ +#define I365_VCC_5V 0x10 /* Vcc = 5.0v */ +#define I365_VCC_3V 0x18 /* Vcc = 3.3v */ +#define I365_VPP2_MASK 0x0c /* Mask for turning off Vpp2 */ +#define I365_VPP2_5V 0x04 /* Vpp2 = 5.0v */ +#define I365_VPP2_12V 0x08 /* Vpp2 = 12.0v */ +#define I365_VPP1_MASK 0x03 /* Mask for turning off Vpp1 */ +#define I365_VPP1_5V 0x01 /* Vpp2 = 5.0v */ +#define I365_VPP1_12V 0x02 /* Vpp2 = 12.0v */ + +/* Flags for I365_INTCTL */ +#define I365_RING_ENA 0x80 +#define I365_PC_RESET 0x40 +#define I365_PC_IOCARD 0x20 +#define I365_INTR_ENA 0x10 +#define I365_IRQ_MASK 0x0F + +/* Flags for I365_CSC and I365_CSCINT*/ +#define I365_CSC_BVD1 0x01 +#define I365_CSC_STSCHG 0x01 +#define I365_CSC_BVD2 0x02 +#define I365_CSC_READY 0x04 +#define I365_CSC_DETECT 0x08 +#define I365_CSC_ANY 0x0F +#define I365_CSC_GPI 0x10 + +/* Flags for I365_ADDRWIN */ +#define I365_ENA_IO(map) (0x40 << (map)) +#define I365_ENA_MEM(map) (0x01 << (map)) + +/* Flags for I365_IOCTL */ +#define I365_IOCTL_MASK(map) (0x0F << (map<<2)) +#define I365_IOCTL_WAIT(map) (0x08 << (map<<2)) +#define I365_IOCTL_0WS(map) (0x04 << (map<<2)) +#define I365_IOCTL_IOCS16(map) (0x02 << (map<<2)) +#define I365_IOCTL_16BIT(map) (0x01 << (map<<2)) + +/* Flags for I365_GENCTL */ +#define I365_CTL_16DELAY 0x01 +#define I365_CTL_RESET 0x02 +#define I365_CTL_GPI_ENA 0x04 +#define I365_CTL_GPI_CTL 0x08 +#define I365_CTL_RESUME 0x10 +#define I365_CTL_SW_IRQ 0x20 + +/* Flags for I365_GBLCTL */ +#define I365_GBL_PWRDOWN 0x01 +#define I365_GBL_CSC_LEV 0x02 +#define I365_GBL_WRBACK 0x04 +#define I365_GBL_IRQ_0_LEV 0x08 +#define I365_GBL_IRQ_1_LEV 0x10 + +/* Flags for memory window registers */ +#define I365_MEM_16BIT 0x8000 /* In memory start high byte */ +#define I365_MEM_0WS 0x4000 +#define I365_MEM_WS1 0x8000 /* In memory stop high byte */ +#define I365_MEM_WS0 0x4000 +#define I365_MEM_WRPROT 0x8000 /* In offset high byte */ +#define I365_MEM_REG 0x4000 + +#define I365_REG(slot, reg) (((slot) << 6) + reg) + +#endif /* _LINUX_I82365_H */ + +//***************************************************************************** +//***************************************************************************** +//***************************************************************************** +//***************************************************************************** +//***************************************************************************** +// Beginning vg468.h (for VADEM chipset) + +#ifndef _LINUX_VG468_H +#define _LINUX_VG468_H + +/* Special bit in I365_IDENT used for Vadem chip detection */ +#define I365_IDENT_VADEM 0x08 + +/* Special definitions in I365_POWER */ +#define VG468_VPP2_MASK 0x0c +#define VG468_VPP2_5V 0x04 +#define VG468_VPP2_12V 0x08 + +/* Unique Vadem registers */ +#define VG469_VSENSE 0x1f /* Card voltage sense */ +#define VG469_VSELECT 0x2f /* Card voltage select */ +#define VG468_CTL 0x38 /* Control register */ +#define VG468_TIMER 0x39 /* Timer control */ +#define VG468_MISC 0x3a /* Miscellaneous */ +#define VG468_GPIO_CFG 0x3b /* GPIO configuration */ +#define VG469_EXT_MODE 0x3c /* Extended mode register */ +#define VG468_SELECT 0x3d /* Programmable chip select */ +#define VG468_SELECT_CFG 0x3e /* Chip select configuration */ +#define VG468_ATA 0x3f /* ATA control */ + +/* Flags for VG469_VSENSE */ +#define VG469_VSENSE_A_VS1 0x01 +#define VG469_VSENSE_A_VS2 0x02 +#define VG469_VSENSE_B_VS1 0x04 +#define VG469_VSENSE_B_VS2 0x08 + +/* Flags for VG469_VSELECT */ +#define VG469_VSEL_VCC 0x03 +#define VG469_VSEL_5V 0x00 +#define VG469_VSEL_3V 0x03 +#define VG469_VSEL_MAX 0x0c +#define VG469_VSEL_EXT_STAT 0x10 +#define VG469_VSEL_EXT_BUS 0x20 +#define VG469_VSEL_MIXED 0x40 +#define VG469_VSEL_ISA 0x80 + +/* Flags for VG468_CTL */ +#define VG468_CTL_SLOW 0x01 /* 600ns memory timing */ +#define VG468_CTL_ASYNC 0x02 /* Asynchronous bus clocking */ +#define VG468_CTL_TSSI 0x08 /* Tri-state some outputs */ +#define VG468_CTL_DELAY 0x10 /* Card detect debounce */ +#define VG468_CTL_INPACK 0x20 /* Obey INPACK signal? */ +#define VG468_CTL_POLARITY 0x40 /* VCCEN polarity */ +#define VG468_CTL_COMPAT 0x80 /* Compatibility stuff */ + +#define VG469_CTL_WS_COMPAT 0x04 /* Wait state compatibility */ +#define VG469_CTL_STRETCH 0x10 /* LED stretch */ + +/* Flags for VG468_TIMER */ +#define VG468_TIMER_ZEROPWR 0x10 /* Zero power control */ +#define VG468_TIMER_SIGEN 0x20 /* Power up */ +#define VG468_TIMER_STATUS 0x40 /* Activity timer status */ +#define VG468_TIMER_RES 0x80 /* Timer resolution */ +#define VG468_TIMER_MASK 0x0f /* Activity timer timeout */ + +/* Flags for VG468_MISC */ +#define VG468_MISC_GPIO 0x04 /* General-purpose IO */ +#define VG468_MISC_DMAWSB 0x08 /* DMA wait state control */ +#define VG469_MISC_LEDENA 0x10 /* LED enable */ +#define VG468_MISC_VADEMREV 0x40 /* Vadem revision control */ +#define VG468_MISC_UNLOCK 0x80 /* Unique register lock */ + +/* Flags for VG469_EXT_MODE_A */ +#define VG469_MODE_VPPST 0x03 /* Vpp steering control */ +#define VG469_MODE_INT_SENSE 0x04 /* Internal voltage sense */ +#define VG469_MODE_CABLE 0x08 +#define VG469_MODE_COMPAT 0x10 /* i82365sl B or DF step */ +#define VG469_MODE_TEST 0x20 +#define VG469_MODE_RIO 0x40 /* Steer RIO to INTR? */ + +/* Flags for VG469_EXT_MODE_B */ +#define VG469_MODE_B_3V 0x01 /* 3.3v for socket B */ + +#endif /* _LINUX_VG468_H */ + + +//***************************************************************************** +//***************************************************************************** +//***************************************************************************** +//***************************************************************************** +//***************************************************************************** +// Beginning ricoh.h (RICOH chipsets) + +#ifndef _LINUX_RICOH_H +#define _LINUX_RICOH_H + + +#define RF5C_MODE_CTL 0x1f /* Mode control */ +#define RF5C_PWR_CTL 0x2f /* Mixed voltage control */ +#define RF5C_CHIP_ID 0x3a /* Chip identification */ +#define RF5C_MODE_CTL_3 0x3b /* Mode control 3 */ + +/* I/O window address offset */ +#define RF5C_IO_OFF(w) (0x36+((w)<<1)) + +/* Flags for RF5C_MODE_CTL */ +#define RF5C_MODE_ATA 0x01 /* ATA mode */ +#define RF5C_MODE_LED_ENA 0x02 /* IRQ 12 is LED */ +#define RF5C_MODE_CA21 0x04 +#define RF5C_MODE_CA22 0x08 +#define RF5C_MODE_CA23 0x10 +#define RF5C_MODE_CA24 0x20 +#define RF5C_MODE_CA25 0x40 +#define RF5C_MODE_3STATE_BIT7 0x80 + +/* Flags for RF5C_PWR_CTL */ +#define RF5C_PWR_VCC_3V 0x01 +#define RF5C_PWR_IREQ_HIGH 0x02 +#define RF5C_PWR_INPACK_ENA 0x04 +#define RF5C_PWR_5V_DET 0x08 +#define RF5C_PWR_TC_SEL 0x10 /* Terminal Count: irq 11 or 15 */ +#define RF5C_PWR_DREQ_LOW 0x20 +#define RF5C_PWR_DREQ_OFF 0x00 /* DREQ steering control */ +#define RF5C_PWR_DREQ_INPACK 0x40 +#define RF5C_PWR_DREQ_SPKR 0x80 +#define RF5C_PWR_DREQ_IOIS16 0xc0 + +/* Values for RF5C_CHIP_ID */ +#define RF5C_CHIP_RF5C296 0x32 +#define RF5C_CHIP_RF5C396 0xb2 + +/* Flags for RF5C_MODE_CTL_3 */ +#define RF5C_MCTL3_DISABLE 0x01 /* Disable PCMCIA interface */ +#define RF5C_MCTL3_DMA_ENA 0x02 + +/* Register definitions for Ricoh PCI-to-CardBus bridges */ + +/* Extra bits in CB_BRIDGE_CONTROL */ +#define RL5C46X_BCR_3E0_ENA 0x0800 +#define RL5C46X_BCR_3E2_ENA 0x1000 + +/* Bridge Configuration Register */ +#define RL5C4XX_CONFIG 0x80 /* 16 bit */ +#define RL5C4XX_CONFIG_IO_1_MODE 0x0200 +#define RL5C4XX_CONFIG_IO_0_MODE 0x0100 +#define RL5C4XX_CONFIG_PREFETCH 0x0001 + + +/* Misc Control Register */ +#define RL5C4XX_MISC 0x0082 /* 16 bit */ +#define RL5C4XX_MISC_HW_SUSPEND_ENA 0x0002 +#define RL5C4XX_MISC_VCCEN_POL 0x0100 +#define RL5C4XX_MISC_VPPEN_POL 0x0200 +#define RL5C46X_MISC_SUSPEND 0x0001 +#define RL5C46X_MISC_PWR_SAVE_2 0x0004 +#define RL5C46X_MISC_IFACE_BUSY 0x0008 +#define RL5C46X_MISC_B_LOCK 0x0010 +#define RL5C46X_MISC_A_LOCK 0x0020 +#define RL5C46X_MISC_PCI_LOCK 0x0040 +#define RL5C47X_MISC_IFACE_BUSY 0x0004 +#define RL5C47X_MISC_PCI_INT_MASK 0x0018 +#define RL5C47X_MISC_PCI_INT_DIS 0x0020 +#define RL5C47X_MISC_SUBSYS_WR 0x0040 +#define RL5C47X_MISC_SRIRQ_ENA 0x0080 +#define RL5C47X_MISC_5V_DISABLE 0x0400 +#define RL5C47X_MISC_LED_POL 0x0800 + +/* 16-bit Interface Control Register */ +#define RL5C4XX_16BIT_CTL 0x0084 /* 16 bit */ +#define RL5C4XX_16CTL_IO_TIMING 0x0100 +#define RL5C4XX_16CTL_MEM_TIMING 0x0200 +#define RL5C46X_16CTL_LEVEL_1 0x0010 +#define RL5C46X_16CTL_LEVEL_2 0x0020 + +/* 16-bit IO and memory timing registers */ +#define RL5C4XX_16BIT_IO_0 0x0088 /* 16 bit */ +#define RL5C4XX_16BIT_MEM_0 0x0088 /* 16 bit */ +#define RL5C4XX_SETUP_MASK 0x0007 +#define RL5C4XX_SETUP_SHIFT 0 +#define RL5C4XX_CMD_MASK 0x01f0 +#define RL5C4XX_CMD_SHIFT 4 +#define RL5C4XX_HOLD_MASK 0x1c00 +#define RL5C4XX_HOLD_SHIFT 10 +#define RL5C4XX_MISC_CONTROL 0x2F /* 8 bit */ +#define RL5C4XX_ZV_ENABLE 0x08 + +#endif /* _LINUX_RICOH_H */ + + +//***************************************************************************** +//***************************************************************************** +//***************************************************************************** +//***************************************************************************** +//***************************************************************************** +// Beginning cirrus.h (CIRRUS chipsets) + +#ifndef _LINUX_CIRRUS_H +#define _LINUX_CIRRUS_H + +#ifndef PCI_VENDOR_ID_CIRRUS +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_6729 +#define PCI_DEVICE_ID_CIRRUS_6729 0x1100 +#endif +#ifndef PCI_DEVICE_ID_CIRRUS_6832 +#define PCI_DEVICE_ID_CIRRUS_6832 0x1110 +#endif + +#define PD67_MISC_CTL_1 0x16 /* Misc control 1 */ +#define PD67_FIFO_CTL 0x17 /* FIFO control */ +#define PD67_MISC_CTL_2 0x1E /* Misc control 2 */ +#define PD67_CHIP_INFO 0x1f /* Chip information */ +#define PD67_ATA_CTL 0x026 /* 6730: ATA control */ +#define PD67_EXT_INDEX 0x2e /* Extension index */ +#define PD67_EXT_DATA 0x2f /* Extension data */ + +/* PD6722 extension registers -- indexed in PD67_EXT_INDEX */ +#define PD67_DATA_MASK0 0x01 /* Data mask 0 */ +#define PD67_DATA_MASK1 0x02 /* Data mask 1 */ +#define PD67_DMA_CTL 0x03 /* DMA control */ + +/* PD6730 extension registers -- indexed in PD67_EXT_INDEX */ +#define PD67_EXT_CTL_1 0x03 /* Extension control 1 */ +#define PD67_MEM_PAGE(n) ((n)+5) /* PCI window bits 31:24 */ +#define PD67_EXTERN_DATA 0x0a +#define PD67_MISC_CTL_3 0x25 +#define PD67_SMB_PWR_CTL 0x26 + +/* I/O window address offset */ +#define PD67_IO_OFF(w) (0x36+((w)<<1)) + +/* Timing register sets */ +#define PD67_TIME_SETUP(n) (0x3a + 3*(n)) +#define PD67_TIME_CMD(n) (0x3b + 3*(n)) +#define PD67_TIME_RECOV(n) (0x3c + 3*(n)) + +/* Flags for PD67_MISC_CTL_1 */ +#define PD67_MC1_5V_DET 0x01 /* 5v detect */ +#define PD67_MC1_MEDIA_ENA 0x01 /* 6730: Multimedia enable */ +#define PD67_MC1_VCC_3V 0x02 /* 3.3v Vcc */ +#define PD67_MC1_PULSE_MGMT 0x04 +#define PD67_MC1_PULSE_IRQ 0x08 +#define PD67_MC1_SPKR_ENA 0x10 +#define PD67_MC1_INPACK_ENA 0x80 + +/* Flags for PD67_FIFO_CTL */ +#define PD67_FIFO_EMPTY 0x80 + +/* Flags for PD67_MISC_CTL_2 */ +#define PD67_MC2_FREQ_BYPASS 0x01 +#define PD67_MC2_DYNAMIC_MODE 0x02 +#define PD67_MC2_SUSPEND 0x04 +#define PD67_MC2_5V_CORE 0x08 +#define PD67_MC2_LED_ENA 0x10 /* IRQ 12 is LED enable */ +#define PD67_MC2_FAST_PCI 0x10 /* 6729: PCI bus > 25 MHz */ +#define PD67_MC2_3STATE_BIT7 0x20 /* Floppy change bit */ +#define PD67_MC2_DMA_MODE 0x40 +#define PD67_MC2_IRQ15_RI 0x80 /* IRQ 15 is ring enable */ + +/* Flags for PD67_CHIP_INFO */ +#define PD67_INFO_SLOTS 0x20 /* 0 = 1 slot, 1 = 2 slots */ +#define PD67_INFO_CHIP_ID 0xc0 +#define PD67_INFO_REV 0x1c + +/* Fields in PD67_TIME_* registers */ +#define PD67_TIME_SCALE 0xc0 +#define PD67_TIME_SCALE_1 0x00 +#define PD67_TIME_SCALE_16 0x40 +#define PD67_TIME_SCALE_256 0x80 +#define PD67_TIME_SCALE_4096 0xc0 +#define PD67_TIME_MULT 0x3f + +/* Fields in PD67_DMA_CTL */ +#define PD67_DMA_MODE 0xc0 +#define PD67_DMA_OFF 0x00 +#define PD67_DMA_DREQ_INPACK 0x40 +#define PD67_DMA_DREQ_WP 0x80 +#define PD67_DMA_DREQ_BVD2 0xc0 +#define PD67_DMA_PULLUP 0x20 /* Disable socket pullups? */ + +/* Fields in PD67_EXT_CTL_1 */ +#define PD67_EC1_VCC_PWR_LOCK 0x01 +#define PD67_EC1_AUTO_PWR_CLEAR 0x02 +#define PD67_EC1_LED_ENA 0x04 +#define PD67_EC1_INV_CARD_IRQ 0x08 +#define PD67_EC1_INV_MGMT_IRQ 0x10 +#define PD67_EC1_PULLUP_CTL 0x20 + +/* Fields in PD67_MISC_CTL_3 */ +#define PD67_MC3_IRQ_MASK 0x03 +#define PD67_MC3_IRQ_PCPCI 0x00 +#define PD67_MC3_IRQ_EXTERN 0x01 +#define PD67_MC3_IRQ_PCIWAY 0x02 +#define PD67_MC3_IRQ_PCI 0x03 +#define PD67_MC3_PWR_MASK 0x0c +#define PD67_MC3_PWR_SERIAL 0x00 +#define PD67_MC3_PWR_TI2202 0x08 +#define PD67_MC3_PWR_SMB 0x0c + +/* Register definitions for Cirrus PD6832 PCI-to-CardBus bridge */ + +/* PD6832 extension registers -- indexed in PD67_EXT_INDEX */ +#define PD68_EXT_CTL_2 0x0b +#define PD68_PCI_SPACE 0x22 +#define PD68_PCCARD_SPACE 0x23 +#define PD68_WINDOW_TYPE 0x24 +#define PD68_EXT_CSC 0x2e +#define PD68_MISC_CTL_4 0x2f +#define PD68_MISC_CTL_5 0x30 +#define PD68_MISC_CTL_6 0x31 + +/* Extra flags in PD67_MISC_CTL_3 */ +#define PD68_MC3_HW_SUSP 0x10 +#define PD68_MC3_MM_EXPAND 0x40 +#define PD68_MC3_MM_ARM 0x80 + +/* Bridge Control Register */ +#define PD6832_BCR_MGMT_IRQ_ENA 0x0800 + +/* Socket Number Register */ +#define PD6832_SOCKET_NUMBER 0x004c /* 8 bit */ + +#endif /* _LINUX_CIRRUS_H */ + + + diff --git a/gpxe/src/include/igmp.h b/gpxe/src/include/igmp.h new file mode 100644 index 0000000..8b3292f --- /dev/null +++ b/gpxe/src/include/igmp.h @@ -0,0 +1,42 @@ +#ifndef IGMP_H +#define IGMP_H + +#include "stdint.h" +#include + +#define IGMP_QUERY 0x11 +#define IGMPv1_REPORT 0x12 +#define IGMPv2_REPORT 0x16 +#define IGMP_LEAVE 0x17 +#define GROUP_ALL_HOSTS 0xe0000001 /* 224.0.0.1 Host byte order */ + +#define MULTICAST_MASK 0xf0000000 +#define MULTICAST_NETWORK 0xe0000000 + +enum { + IGMP_SERVER, + MAX_IGMP +}; + +struct igmp { + uint8_t type; + uint8_t response_time; + uint16_t chksum; + struct in_addr group; +} PACKED; + +struct igmp_ip_t { /* Format of an igmp ip packet */ + struct iphdr ip; + uint8_t router_alert[4]; /* Router alert option */ + struct igmp igmp; +} PACKED; + +struct igmptable_t { + struct in_addr group; + unsigned long time; +} PACKED; + +extern void join_group ( int slot, unsigned long group ); +extern void leave_group ( int slot ); + +#endif /* IGMP_H */ diff --git a/gpxe/src/include/lib.h b/gpxe/src/include/lib.h new file mode 100644 index 0000000..400ea46 --- /dev/null +++ b/gpxe/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/gpxe/src/include/libgen.h b/gpxe/src/include/libgen.h new file mode 100644 index 0000000..56a2f76 --- /dev/null +++ b/gpxe/src/include/libgen.h @@ -0,0 +1,7 @@ +#ifndef _LIBGEN_H +#define _LIBGEN_H + +extern char * basename ( char *path ); +extern char * dirname ( char *path ); + +#endif /* _LIBGEN_H */ diff --git a/gpxe/src/include/little_bswap.h b/gpxe/src/include/little_bswap.h new file mode 100644 index 0000000..e4f8375 --- /dev/null +++ b/gpxe/src/include/little_bswap.h @@ -0,0 +1,33 @@ +#ifndef ETHERBOOT_LITTLE_BSWAP_H +#define ETHERBOOT_LITTLE_BSWAP_H + +#define ntohl(x) __bswap_32(x) +#define htonl(x) __bswap_32(x) +#define ntohs(x) __bswap_16(x) +#define htons(x) __bswap_16(x) +#define cpu_to_le64(x) (x) +#define cpu_to_le32(x) (x) +#define cpu_to_le16(x) (x) +#define cpu_to_be64(x) __bswap_64(x) +#define cpu_to_be32(x) __bswap_32(x) +#define cpu_to_be16(x) __bswap_16(x) +#define le64_to_cpu(x) (x) +#define le32_to_cpu(x) (x) +#define le16_to_cpu(x) (x) +#define be64_to_cpu(x) __bswap_64(x) +#define be32_to_cpu(x) __bswap_32(x) +#define be16_to_cpu(x) __bswap_16(x) +#define cpu_to_le64s(x) do {} while (0) +#define cpu_to_le32s(x) do {} while (0) +#define cpu_to_le16s(x) do {} while (0) +#define cpu_to_be64s(x) __bswap_64s(x) +#define cpu_to_be32s(x) __bswap_32s(x) +#define cpu_to_be16s(x) __bswap_16s(x) +#define le64_to_cpus(x) do {} while (0) +#define le32_to_cpus(x) do {} while (0) +#define le16_to_cpus(x) do {} while (0) +#define be64_to_cpus(x) __bswap_64s(x) +#define be32_to_cpus(x) __bswap_32s(x) +#define be16_to_cpus(x) __bswap_16s(x) + +#endif /* ETHERBOOT_LITTLE_BSWAP_H */ diff --git a/gpxe/src/include/mii.h b/gpxe/src/include/mii.h new file mode 100644 index 0000000..34c1ca9 --- /dev/null +++ b/gpxe/src/include/mii.h @@ -0,0 +1,105 @@ +/* + * linux/mii.h: definitions for MII-compatible transceivers + * Originally drivers/net/sunhme.h. + * + * Copyright (C) 1996, 1999, 2001 David S. Miller (davem@redhat.com) + * + * Copied Form Linux 2.4.25 an unneeded items removed by: + * Timothy Legge (timlegge at etherboot dot org) + * + * 03/26/2004 + */ + +/* Generic MII registers. */ + +#define MII_BMCR 0x00 /* Basic mode control register */ +#define MII_BMSR 0x01 /* Basic mode status register */ +#define MII_PHYSID1 0x02 /* PHYS ID 1 */ +#define MII_PHYSID2 0x03 /* PHYS ID 2 */ +#define MII_ADVERTISE 0x04 /* Advertisement control reg */ +#define MII_LPA 0x05 /* Link partner ability reg */ +#define MII_EXPANSION 0x06 /* Expansion register */ +#define MII_DCOUNTER 0x12 /* Disconnect counter */ +#define MII_FCSCOUNTER 0x13 /* False carrier counter */ +#define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */ +#define MII_RERRCOUNTER 0x15 /* Receive error counter */ +#define MII_SREVISION 0x16 /* Silicon revision */ +#define MII_RESV1 0x17 /* Reserved... */ +#define MII_LBRERROR 0x18 /* Lpback, rx, bypass error */ +#define MII_PHYADDR 0x19 /* PHY address */ +#define MII_RESV2 0x1a /* Reserved... */ +#define MII_TPISTATUS 0x1b /* TPI status for 10mbps */ +#define MII_NCONFIG 0x1c /* Network interface config */ + +/* Basic mode control register. */ +#define BMCR_RESV 0x007f /* Unused... */ +#define BMCR_CTST 0x0080 /* Collision test */ +#define BMCR_FULLDPLX 0x0100 /* Full duplex */ +#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */ +#define BMCR_ISOLATE 0x0400 /* Disconnect DP83840 from MII */ +#define BMCR_PDOWN 0x0800 /* Powerdown the DP83840 */ +#define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */ +#define BMCR_SPEED100 0x2000 /* Select 100Mbps */ +#define BMCR_LOOPBACK 0x4000 /* TXD loopback bits */ +#define BMCR_RESET 0x8000 /* Reset the DP83840 */ + +/* Basic mode status register. */ +#define BMSR_ERCAP 0x0001 /* Ext-reg capability */ +#define BMSR_JCD 0x0002 /* Jabber detected */ +#define BMSR_LSTATUS 0x0004 /* Link status */ +#define BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */ +#define BMSR_RFAULT 0x0010 /* Remote fault detected */ +#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */ +#define BMSR_RESV 0x07c0 /* Unused... */ +#define BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */ +#define BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */ +#define BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */ +#define BMSR_100FULL 0x4000 /* Can do 100mbps, full-duplex */ +#define BMSR_100BASE4 0x8000 /* Can do 100mbps, 4k packets */ + +/* Advertisement control register. */ +#define ADVERTISE_SLCT 0x001f /* Selector bits */ +#define ADVERTISE_CSMA 0x0001 /* Only selector supported */ +#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ +#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ +#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ +#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ +#define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */ +#define ADVERTISE_RESV 0x1c00 /* Unused... */ +#define ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */ +#define ADVERTISE_LPACK 0x4000 /* Ack link partners response */ +#define ADVERTISE_NPAGE 0x8000 /* Next page bit */ + +#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | \ + ADVERTISE_CSMA) +#define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \ + ADVERTISE_100HALF | ADVERTISE_100FULL) + +/* Link partner ability register. */ +#define LPA_SLCT 0x001f /* Same as advertise selector */ +#define LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */ +#define LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */ +#define LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */ +#define LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */ +#define LPA_100BASE4 0x0200 /* Can do 100mbps 4k packets */ +#define LPA_RESV 0x1c00 /* Unused... */ +#define LPA_RFAULT 0x2000 /* Link partner faulted */ +#define LPA_LPACK 0x4000 /* Link partner acked us */ +#define LPA_NPAGE 0x8000 /* Next page bit */ + +#define LPA_DUPLEX (LPA_10FULL | LPA_100FULL) +#define LPA_100 (LPA_100FULL | LPA_100HALF | LPA_100BASE4) + +/* Expansion register for auto-negotiation. */ +#define EXPANSION_NWAY 0x0001 /* Can do N-way auto-nego */ +#define EXPANSION_LCWP 0x0002 /* Got new RX page code word */ +#define EXPANSION_ENABLENPAGE 0x0004 /* This enables npage words */ +#define EXPANSION_NPCAPABLE 0x0008 /* Link partner supports npage */ +#define EXPANSION_MFAULTS 0x0010 /* Multiple faults detected */ +#define EXPANSION_RESV 0xffe0 /* Unused... */ + +/* N-way test register. */ +#define NWAYTEST_RESV1 0x00ff /* Unused... */ +#define NWAYTEST_LOOPBACK 0x0100 /* Enable loopback for N-way */ +#define NWAYTEST_RESV2 0xfe00 /* Unused... */ + diff --git a/gpxe/src/include/nfs.h b/gpxe/src/include/nfs.h new file mode 100644 index 0000000..0877bb6 --- /dev/null +++ b/gpxe/src/include/nfs.h @@ -0,0 +1,63 @@ +#ifndef _NFS_H +#define _NFS_H + +#define SUNRPC_PORT 111 + +#define PROG_PORTMAP 100000 +#define PROG_NFS 100003 +#define PROG_MOUNT 100005 + +#define MSG_CALL 0 +#define MSG_REPLY 1 + +#define PORTMAP_GETPORT 3 + +#define MOUNT_ADDENTRY 1 +#define MOUNT_UMOUNTALL 4 + +#define NFS_LOOKUP 4 +#define NFS_READLINK 5 +#define NFS_READ 6 + +#define NFS_FHSIZE 32 + +#define NFSERR_PERM 1 +#define NFSERR_NOENT 2 +#define NFSERR_ACCES 13 +#define NFSERR_ISDIR 21 +#define NFSERR_INVAL 22 + +/* Block size used for NFS read accesses. A RPC reply packet (including all + * headers) must fit within a single Ethernet frame to avoid fragmentation. + * Chosen to be a power of two, as most NFS servers are optimized for this. */ +#define NFS_READ_SIZE 1024 + +#define NFS_MAXLINKDEPTH 16 + +struct rpc_t { + struct iphdr ip; + struct udphdr udp; + union { + uint8_t data[300]; /* longest RPC call must fit!!!! */ + struct { + uint32_t id; + uint32_t type; + uint32_t rpcvers; + uint32_t prog; + uint32_t vers; + uint32_t proc; + uint32_t data[1]; + } call; + struct { + uint32_t id; + uint32_t type; + uint32_t rstatus; + uint32_t verifier; + uint32_t v2; + uint32_t astatus; + uint32_t data[1]; + } reply; + } u; +} PACKED; + +#endif /* _NFS_H */ diff --git a/gpxe/src/include/nic.h b/gpxe/src/include/nic.h new file mode 100644 index 0000000..186b2ea --- /dev/null +++ b/gpxe/src/include/nic.h @@ -0,0 +1,273 @@ + /* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dhcp.h" + +typedef enum { + DISABLE = 0, + ENABLE, + FORCE +} irq_action_t; + +typedef enum duplex { + HALF_DUPLEX = 1, + FULL_DUPLEX +} duplex_t; + +/* + * Structure returned from eth_probe and passed to other driver + * functions. + */ +struct nic { + struct nic_operations *nic_op; + int flags; /* driver specific flags */ + unsigned char *node_addr; + unsigned char *packet; + unsigned int packetlen; + unsigned int ioaddr; + unsigned char irqno; + unsigned int mbps; + duplex_t duplex; + struct dhcp_dev_id dhcp_dev_id; + void *priv_data; /* driver private data */ +}; + +struct nic_operations { + int ( *connect ) ( struct nic * ); + int ( *poll ) ( struct nic *, int retrieve ); + void ( *transmit ) ( struct nic *, const char *, + unsigned int, unsigned int, const char * ); + void ( *irq ) ( struct nic *, irq_action_t ); +}; + +extern struct nic nic; + +static inline int eth_poll ( int retrieve ) { + return nic.nic_op->poll ( &nic, retrieve ); +} + +static inline void eth_transmit ( const char *dest, unsigned int type, + unsigned int size, const void *packet ) { + nic.nic_op->transmit ( &nic, dest, type, size, packet ); +} + +/* + * Function prototypes + * + */ +extern int dummy_connect ( struct nic *nic ); +extern void dummy_irq ( struct nic *nic, irq_action_t irq_action ); +extern int legacy_probe ( void *hwdev, + void ( * set_drvdata ) ( void *hwdev, void *priv ), + struct device *dev, + int ( * probe ) ( struct nic *nic, void *hwdev ), + void ( * disable ) ( struct nic *nic, void *hwdev )); +void legacy_remove ( void *hwdev, + void * ( * get_drvdata ) ( void *hwdev ), + void ( * disable ) ( struct nic *nic, void *hwdev ) ); + +#define PCI_DRIVER(_name,_ids,_class) \ + static inline int \ + _name ## _pci_legacy_probe ( struct pci_device *pci, \ + const struct pci_device_id *id ); \ + static inline void \ + _name ## _pci_legacy_remove ( struct pci_device *pci ); \ + struct pci_driver _name __pci_driver = { \ + .ids = _ids, \ + .id_count = sizeof ( _ids ) / sizeof ( _ids[0] ), \ + .probe = _name ## _pci_legacy_probe, \ + .remove = _name ## _pci_legacy_remove, \ + }; \ + REQUIRE_OBJECT ( pci ); + +static inline void legacy_pci_set_drvdata ( void *hwdev, void *priv ) { + pci_set_drvdata ( hwdev, priv ); +} +static inline void * legacy_pci_get_drvdata ( void *hwdev ) { + return pci_get_drvdata ( hwdev ); +} + +#define ISAPNP_DRIVER(_name,_ids) \ + static inline int \ + _name ## _isapnp_legacy_probe ( struct isapnp_device *isapnp, \ + const struct isapnp_device_id *id ); \ + static inline void \ + _name ## _isapnp_legacy_remove ( struct isapnp_device *isapnp ); \ + struct isapnp_driver _name __isapnp_driver = { \ + .ids = _ids, \ + .id_count = sizeof ( _ids ) / sizeof ( _ids[0] ), \ + .probe = _name ## _isapnp_legacy_probe, \ + .remove = _name ## _isapnp_legacy_remove, \ + }; \ + REQUIRE_OBJECT ( isapnp ); + +static inline void legacy_isapnp_set_drvdata ( void *hwdev, void *priv ) { + isapnp_set_drvdata ( hwdev, priv ); +} +static inline void * legacy_isapnp_get_drvdata ( void *hwdev ) { + return isapnp_get_drvdata ( hwdev ); +} + +#define EISA_DRIVER(_name,_ids) \ + static inline int \ + _name ## _eisa_legacy_probe ( struct eisa_device *eisa, \ + const struct eisa_device_id *id ); \ + static inline void \ + _name ## _eisa_legacy_remove ( struct eisa_device *eisa ); \ + struct eisa_driver _name __eisa_driver = { \ + .ids = _ids, \ + .id_count = sizeof ( _ids ) / sizeof ( _ids[0] ), \ + .probe = _name ## _eisa_legacy_probe, \ + .remove = _name ## _eisa_legacy_remove, \ + }; \ + REQUIRE_OBJECT ( eisa ); + +static inline void legacy_eisa_set_drvdata ( void *hwdev, void *priv ) { + eisa_set_drvdata ( hwdev, priv ); +} +static inline void * legacy_eisa_get_drvdata ( void *hwdev ) { + return eisa_get_drvdata ( hwdev ); +} + +#define MCA_DRIVER(_name,_ids) \ + static inline int \ + _name ## _mca_legacy_probe ( struct mca_device *mca, \ + const struct mca_device_id *id ); \ + static inline void \ + _name ## _mca_legacy_remove ( struct mca_device *mca ); \ + struct mca_driver _name __mca_driver = { \ + .ids = _ids, \ + .id_count = sizeof ( _ids ) / sizeof ( _ids[0] ), \ + .probe = _name ## _mca_legacy_probe, \ + .remove = _name ## _mca_legacy_remove, \ + }; \ + REQUIRE_OBJECT ( mca ); + +static inline void legacy_mca_set_drvdata ( void *hwdev, void *priv ) { + mca_set_drvdata ( hwdev, priv ); +} +static inline void * legacy_mca_get_drvdata ( void *hwdev ) { + return mca_get_drvdata ( hwdev ); +} + +#define ISA_DRIVER(_name,_probe_addrs,_probe_addr,_vendor_id,_prod_id) \ + static inline int \ + _name ## _isa_legacy_probe ( struct isa_device *isa ); \ + static inline int \ + _name ## _isa_legacy_probe_at_addr ( struct isa_device *isa ) { \ + if ( ! _probe_addr ( isa->ioaddr ) ) \ + return -ENODEV; \ + return _name ## _isa_legacy_probe ( isa ); \ + } \ + static inline void \ + _name ## _isa_legacy_remove ( struct isa_device *isa ); \ + static const char _name ## _text[]; \ + struct isa_driver _name __isa_driver = { \ + .name = _name ## _text, \ + .probe_addrs = _probe_addrs, \ + .addr_count = ( sizeof ( _probe_addrs ) / \ + sizeof ( _probe_addrs[0] ) ), \ + .vendor_id = _vendor_id, \ + .prod_id = _prod_id, \ + .probe = _name ## _isa_legacy_probe_at_addr, \ + .remove = _name ## _isa_legacy_remove, \ + }; \ + REQUIRE_OBJECT ( isa ); + +static inline void legacy_isa_set_drvdata ( void *hwdev, void *priv ) { + isa_set_drvdata ( hwdev, priv ); +} +static inline void * legacy_isa_get_drvdata ( void *hwdev ) { + return isa_get_drvdata ( hwdev ); +} + +#undef DRIVER +#define DRIVER(_name_text,_unused2,_unused3,_name,_probe,_disable) \ + static const char _name ## _text[] = _name_text; \ + static inline int \ + _name ## _probe ( struct nic *nic, void *hwdev ) { \ + return _probe ( nic, hwdev ); \ + } \ + static inline void \ + _name ## _disable ( struct nic *nic, void *hwdev ) { \ + void ( * _unsafe_disable ) () = _disable; \ + _unsafe_disable ( nic, hwdev ); \ + } \ + static inline int \ + _name ## _pci_legacy_probe ( struct pci_device *pci, \ + const struct pci_device_id *id __unused ) { \ + return legacy_probe ( pci, legacy_pci_set_drvdata, \ + &pci->dev, _name ## _probe, \ + _name ## _disable ); \ + } \ + static inline void \ + _name ## _pci_legacy_remove ( struct pci_device *pci ) { \ + return legacy_remove ( pci, legacy_pci_get_drvdata, \ + _name ## _disable ); \ + } \ + static inline int \ + _name ## _isapnp_legacy_probe ( struct isapnp_device *isapnp, \ + const struct isapnp_device_id *id __unused ) { \ + return legacy_probe ( isapnp, legacy_isapnp_set_drvdata, \ + &isapnp->dev, _name ## _probe, \ + _name ## _disable ); \ + } \ + static inline void \ + _name ## _isapnp_legacy_remove ( struct isapnp_device *isapnp ) { \ + return legacy_remove ( isapnp, legacy_isapnp_get_drvdata, \ + _name ## _disable ); \ + } \ + static inline int \ + _name ## _eisa_legacy_probe ( struct eisa_device *eisa, \ + const struct eisa_device_id *id __unused ) { \ + return legacy_probe ( eisa, legacy_eisa_set_drvdata, \ + &eisa->dev, _name ## _probe, \ + _name ## _disable ); \ + } \ + static inline void \ + _name ## _eisa_legacy_remove ( struct eisa_device *eisa ) { \ + return legacy_remove ( eisa, legacy_eisa_get_drvdata, \ + _name ## _disable ); \ + } \ + static inline int \ + _name ## _mca_legacy_probe ( struct mca_device *mca, \ + const struct mca_device_id *id __unused ) { \ + return legacy_probe ( mca, legacy_mca_set_drvdata, \ + &mca->dev, _name ## _probe, \ + _name ## _disable ); \ + } \ + static inline void \ + _name ## _mca_legacy_remove ( struct mca_device *mca ) { \ + return legacy_remove ( mca, legacy_mca_get_drvdata, \ + _name ## _disable ); \ + } \ + static inline int \ + _name ## _isa_legacy_probe ( struct isa_device *isa ) { \ + return legacy_probe ( isa, legacy_isa_set_drvdata, \ + &isa->dev, _name ## _probe, \ + _name ## _disable ); \ + } \ + static inline void \ + _name ## _isa_legacy_remove ( struct isa_device *isa ) { \ + return legacy_remove ( isa, legacy_isa_get_drvdata, \ + _name ## _disable ); \ + } + +#endif /* NIC_H */ diff --git a/gpxe/src/include/nmb.h b/gpxe/src/include/nmb.h new file mode 100644 index 0000000..3e551ff --- /dev/null +++ b/gpxe/src/include/nmb.h @@ -0,0 +1,22 @@ +#ifndef NMB_H +#define NMB_H + +#include + +/* + * NetBIOS name query packets are basically the same as DNS packets, + * though the resource record format is different. + * + */ + +#define DNS_TYPE_NB 0x20 +#define DNS_FLAG_BROADCAST ( 0x01 << 4 ) +#define NBNS_UDP_PORT 137 + +struct dns_rr_info_nb { + struct dns_rr_info info; + uint16_t nb_flags; + struct in_addr nb_address; +} __attribute__ (( packed )); + +#endif /* NMB_H */ diff --git a/gpxe/src/include/old_tcp.h b/gpxe/src/include/old_tcp.h new file mode 100644 index 0000000..93e1485 --- /dev/null +++ b/gpxe/src/include/old_tcp.h @@ -0,0 +1,37 @@ +#ifndef _TCP_H +#define _TCP_H + +#define TCP_INITIAL_TIMEOUT (3*TICKS_PER_SEC) +#define TCP_MAX_TIMEOUT (60*TICKS_PER_SEC) +#define TCP_MIN_TIMEOUT (TICKS_PER_SEC) +#define TCP_MAX_RETRY 10 +#define TCP_MAX_HEADER ((int)sizeof(struct iphdr)+64) +#define TCP_MIN_WINDOW (1500-TCP_MAX_HEADER) +#define TCP_MAX_WINDOW (65535-TCP_MAX_HEADER) + +#define FIN 1 +#define SYN 2 +#define RST 4 +#define PSH 8 +#define ACK 16 +#define URG 32 + + +struct tcphdr { + uint16_t src; + uint16_t dst; + int32_t seq; + int32_t ack; + uint16_t ctrl; + uint16_t window; + uint16_t chksum; + uint16_t urgent; +}; + +extern int tcp_transaction ( unsigned long destip, unsigned int destsock, + void *ptr, + int (*send)(int len, void *buf, void *ptr), + int (*recv)(int len, const void *buf, void *ptr)); + + +#endif /* _TCP_H */ diff --git a/gpxe/src/include/pc_kbd.h b/gpxe/src/include/pc_kbd.h new file mode 100644 index 0000000..c125efa --- /dev/null +++ b/gpxe/src/include/pc_kbd.h @@ -0,0 +1,7 @@ +#ifndef _PC_KBD_H +#define _PC_KBD_H + +int kbd_ischar(void); + +int kbd_getc(void); +#endif diff --git a/gpxe/src/include/pcmcia-opts.h b/gpxe/src/include/pcmcia-opts.h new file mode 100644 index 0000000..70dc092 --- /dev/null +++ b/gpxe/src/include/pcmcia-opts.h @@ -0,0 +1,23 @@ +// pcmcia-opts.h +// special options file for development time. Later this could end in Config(?) +#ifndef __pcmciaopts +#define __pcmciaopts + + #define _yes_ 1 + #define _no_ 0 + + #define SUPPORT_I82365 (_yes_) +// #define SUPPORT_YENTA (_no_) +// #define SUPPORT_SOME_DRIVER (_no_) + + #define PCMCIA_SHUTDOWN (_yes_) + #define MAP_ATTRMEM_TO 0xd0000 + #define MAP_ATTRMEM_LEN 0x02000 + + #define PDEBUG 3 + // The higher the more output you get, 0..3 + // Not fully implemented though, but for the future... + + #undef _yes_ + #undef _no_ +#endif diff --git a/gpxe/src/include/pcmcia.h b/gpxe/src/include/pcmcia.h new file mode 100644 index 0000000..d528bea --- /dev/null +++ b/gpxe/src/include/pcmcia.h @@ -0,0 +1,156 @@ +// pcmcia.h - Header file for PCMCIA support + +#ifndef PCMCIA_H +#define PCMCIA_H + +typedef unsigned char u_char; +typedef unsigned short u_short; +typedef unsigned int u_int; +typedef unsigned long u_long; + +typedef u_short ioaddr_t; +extern int sockets; + +#define MAXPCCSOCKS 8 +#define MAXPCCCONFIGS 8 + +typedef enum ebpdriver_t { I82365, SOMEDRIVER } ebpdriver_t; +typedef enum interface_func_t { INIT, SHUTDOWN, MAPATTRMEM, UNMAPATTRMEM, SELECTCONFIG } interface_func_t; +typedef enum ebpstatus_t { EMPTY, HASCARD, INITIALIZED, SUSPENDED, OTHERDEVICE, UNKNOWN } ebpstatus_t; + +struct driver_interact_t { + ebpdriver_t id; + int (*f)(interface_func_t,int,int,int,int); + char *name; +}; +struct pccsock_t { + ebpdriver_t device; + int drivernum; + ebpstatus_t status; + // Internal usage of the drivers: + int internalid; + int flags; + int ioaddr; + int type; + int configoffset; + int possibleconfignum; + int stringoffset; + u_int stringlength; + int rmask0; +}; + +extern struct pccsock_t pccsock[MAXPCCSOCKS]; +extern u_int pccsocks; + +struct pcc_config_t { + u_char index; + u_char irq; + int iowin; + int iolen; +}; + + +int i82365_interfacer(interface_func_t,int,int,int,void *); +void sleepticks(int); + +#define EINVAL 22 + + +//*********************************************************** cc.h: +/* Definitions for card status flags for GetStatus */ +#define SS_WRPROT 0x0001 +#define SS_CARDLOCK 0x0002 +#define SS_EJECTION 0x0004 +#define SS_INSERTION 0x0008 +#define SS_BATDEAD 0x0010 +#define SS_BATWARN 0x0020 +#define SS_READY 0x0040 +#define SS_DETECT 0x0080 +#define SS_POWERON 0x0100 +#define SS_GPI 0x0200 +#define SS_STSCHG 0x0400 +#define SS_CARDBUS 0x0800 +#define SS_3VCARD 0x1000 +#define SS_XVCARD 0x2000 +#define SS_PENDING 0x4000 + +/* cc.h: for InquireSocket */ +typedef struct socket_cap_t { + u_int features; + u_int irq_mask; + u_int map_size; + ioaddr_t io_offset; + u_char pci_irq; + //struct pci_dev *cb_dev; + //struct bus_operations *bus; + void *cb_dev; + void *bus; +} socket_cap_t; +/* InquireSocket capabilities */ +#define SS_CAP_PAGE_REGS 0x0001 +#define SS_CAP_VIRTUAL_BUS 0x0002 +#define SS_CAP_MEM_ALIGN 0x0004 +#define SS_CAP_STATIC_MAP 0x0008 +#define SS_CAP_PCCARD 0x4000 +#define SS_CAP_CARDBUS 0x8000 + +/* for GetSocket, SetSocket */ +typedef struct socket_state_t { + u_int flags; + u_int csc_mask; + u_char Vcc, Vpp; + u_char io_irq; +} socket_state_t; + +extern socket_state_t dead_socket; + +/* Socket configuration flags */ +#define SS_PWR_AUTO 0x0010 +#define SS_IOCARD 0x0020 +#define SS_RESET 0x0040 +#define SS_DMA_MODE 0x0080 +#define SS_SPKR_ENA 0x0100 +#define SS_OUTPUT_ENA 0x0200 +#define SS_DEBOUNCED 0x0400 /* Tell driver that the debounce delay has ended */ +#define SS_ZVCARD 0x0800 + +/* Flags for I/O port and memory windows */ +#define MAP_ACTIVE 0x01 +#define MAP_16BIT 0x02 +#define MAP_AUTOSZ 0x04 +#define MAP_0WS 0x08 +#define MAP_WRPROT 0x10 +#define MAP_ATTRIB 0x20 +#define MAP_USE_WAIT 0x40 +#define MAP_PREFETCH 0x80 + +/* Use this just for bridge windows */ +#define MAP_IOSPACE 0x20 + +typedef struct pccard_io_map { + u_char map; + u_char flags; + u_short speed; + ioaddr_t start, stop; +} pccard_io_map; + + +typedef struct pccard_mem_map { + u_char map; + u_char flags; + u_short speed; + u_long sys_start, sys_stop; + u_int card_start; +} pccard_mem_map; + +typedef struct cb_bridge_map { + u_char map; + u_char flags; + u_int start, stop; +} cb_bridge_map; +// need the global function pointer struct? *TODO* +//************************************* end cc.h + + + +#endif /* PCMCIA_H */ diff --git a/gpxe/src/include/readline/readline.h b/gpxe/src/include/readline/readline.h new file mode 100644 index 0000000..1a03b48 --- /dev/null +++ b/gpxe/src/include/readline/readline.h @@ -0,0 +1,12 @@ +#ifndef _READLINE_H +#define _READLINE_H + +/** @file + * + * Minmal readline + * + */ + +extern char * __malloc readline ( const char *prompt ); + +#endif /* _READLINE_H */ diff --git a/gpxe/src/include/stdarg.h b/gpxe/src/include/stdarg.h new file mode 100644 index 0000000..a4eb711 --- /dev/null +++ b/gpxe/src/include/stdarg.h @@ -0,0 +1,10 @@ +#ifndef _STDARG_H +#define _STDARG_H + +typedef __builtin_va_list va_list; +#define va_start( ap, last ) __builtin_va_start ( ap, last ) +#define va_arg( ap, type ) __builtin_va_arg ( ap, type ) +#define va_end( ap ) __builtin_va_end ( ap ) +#define va_copy( dest, src ) __builtin_va_copy ( dest, src ) + +#endif /* _STDARG_H */ diff --git a/gpxe/src/include/stddef.h b/gpxe/src/include/stddef.h new file mode 100644 index 0000000..11ea934 --- /dev/null +++ b/gpxe/src/include/stddef.h @@ -0,0 +1,24 @@ +#ifndef STDDEF_H +#define STDDEF_H + +/* for size_t */ +#include + +#undef NULL +#define NULL ((void *)0) + +#undef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +#undef container_of +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +/* __WCHAR_TYPE__ is defined by gcc and will change if -fshort-wchar is used */ +#ifndef __WCHAR_TYPE__ +#define __WCHAR_TYPE__ long int +#endif +typedef __WCHAR_TYPE__ wchar_t; + +#endif /* STDDEF_H */ diff --git a/gpxe/src/include/stdint.h b/gpxe/src/include/stdint.h new file mode 100644 index 0000000..4b0e44f --- /dev/null +++ b/gpxe/src/include/stdint.h @@ -0,0 +1,24 @@ +#ifndef _STDINT_H +#define _STDINT_H + +#include + +typedef int8_t s8; +typedef uint8_t u8; +typedef int16_t s16; +typedef uint16_t u16; +typedef int32_t s32; +typedef uint32_t u32; +typedef int64_t s64; +typedef uint64_t u64; + +typedef int8_t int8; +typedef uint8_t uint8; +typedef int16_t int16; +typedef uint16_t uint16; +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; + +#endif /* _STDINT_H */ diff --git a/gpxe/src/include/stdio.h b/gpxe/src/include/stdio.h new file mode 100644 index 0000000..82077e2 --- /dev/null +++ b/gpxe/src/include/stdio.h @@ -0,0 +1,45 @@ +#ifndef _STDIO_H +#define _STDIO_H + +#include +#include + +extern int __attribute__ (( format ( printf, 1, 2 ) )) +printf ( const char *fmt, ... ); + +extern int __attribute__ (( format ( printf, 3, 4 ) )) +snprintf ( char *buf, size_t size, const char *fmt, ... ); + +extern int __attribute__ (( format ( printf, 2, 3 ) )) +asprintf ( char **strp, const char *fmt, ... ); + +extern int vprintf ( const char *fmt, va_list args ); + +extern int vsnprintf ( char *buf, size_t size, const char *fmt, va_list args ); + +extern int vasprintf ( char **strp, const char *fmt, va_list args ); + +/** + * Write a formatted string to a buffer + * + * @v buf Buffer into which to write the string + * @v fmt Format string + * @v ... Arguments corresponding to the format string + * @ret len Length of formatted string + */ +#define sprintf( buf, fmt, ... ) \ + snprintf ( (buf), ~( ( size_t ) 0 ), (fmt), ## __VA_ARGS__ ) + +/** + * Write a formatted string to a buffer + * + * @v buf Buffer into which to write the string + * @v fmt Format string + * @v args Arguments corresponding to the format string + * @ret len Length of formatted string + */ +static inline int vsprintf ( char *buf, const char *fmt, va_list args ) { + return vsnprintf ( buf, ~( ( size_t ) 0 ), fmt, args ); +} + +#endif /* _STDIO_H */ diff --git a/gpxe/src/include/stdlib.h b/gpxe/src/include/stdlib.h new file mode 100644 index 0000000..838a22a --- /dev/null +++ b/gpxe/src/include/stdlib.h @@ -0,0 +1,73 @@ +#ifndef STDLIB_H +#define STDLIB_H + +#include +#include + +/***************************************************************************** + * + * Numeric parsing + * + **************************************************************************** + */ + +extern unsigned long strtoul ( const char *p, char **endp, int base ); + +/***************************************************************************** + * + * Memory allocation + * + **************************************************************************** + */ + +extern void * __malloc malloc ( size_t size ); +extern void * realloc ( void *old_ptr, size_t new_size ); +extern void free ( void *ptr ); +extern void * __malloc zalloc ( size_t len ); + +/** + * Allocate cleared memory + * + * @v nmemb Number of members + * @v size Size of each member + * @ret ptr Allocated memory + * + * Allocate memory as per malloc(), and zero it. + * + * This is implemented as a static inline, with the body of the + * function in zalloc(), since in most cases @c nmemb will be 1 and + * doing the multiply is just wasteful. + */ +static inline void * __malloc calloc ( size_t nmemb, size_t size ) { + return zalloc ( nmemb * size ); +} + +/***************************************************************************** + * + * Random number generation + * + **************************************************************************** + */ + +extern long int random ( void ); +extern void srandom ( unsigned int seed ); + +static inline int rand ( void ) { + return random(); +} + +static inline void srand ( unsigned int seed ) { + srandom ( seed ); +} + +/***************************************************************************** + * + * Miscellaneous + * + **************************************************************************** + */ + +extern int system ( const char *command ); +extern __asmcall int main ( void ); + +#endif /* STDLIB_H */ diff --git a/gpxe/src/include/string.h b/gpxe/src/include/string.h new file mode 100644 index 0000000..a2894a3 --- /dev/null +++ b/gpxe/src/include/string.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 2004 Tobias Lorenz + * + * string handling functions + * based on linux/include/linux/ctype.h + * and linux/include/linux/string.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef ETHERBOOT_STRING_H +#define ETHERBOOT_STRING_H + +#include +#include + +int __pure strnicmp(const char *s1, const char *s2, size_t len) __nonnull; +char * strcpy(char * dest,const char *src) __nonnull; +char * strncpy(char * dest,const char *src,size_t count) __nonnull; +char * strcat(char * dest, const char * src) __nonnull; +char * strncat(char *dest, const char *src, size_t count) __nonnull; +int __pure strcmp(const char * cs,const char * ct) __nonnull; +int __pure strncmp(const char * cs,const char * ct, + size_t count) __nonnull; +char * __pure strchr(const char * s, int c) __nonnull; +char * __pure strrchr(const char * s, int c) __nonnull; +size_t __pure strlen(const char * s) __nonnull; +size_t __pure strnlen(const char * s, size_t count) __nonnull; +size_t __pure strspn(const char *s, const char *accept) __nonnull; +size_t __pure strcspn(const char *s, const char *reject) __nonnull; +char * __pure strpbrk(const char * cs,const char * ct) __nonnull; +char * strtok(char * s,const char * ct) __nonnull; +char * strsep(char **s, const char *ct) __nonnull; +void * memset(void * s,int c,size_t count) __nonnull; +void * memmove(void * dest,const void *src,size_t count) __nonnull; +int __pure memcmp(const void * cs,const void * ct, + size_t count) __nonnull; +void * __pure memscan(const void * addr, int c, size_t size) __nonnull; +char * __pure strstr(const char * s1,const char * s2) __nonnull; +void * __pure memchr(const void *s, int c, size_t n) __nonnull; +char * __malloc strdup(const char *s) __nonnull; +char * __malloc strndup(const char *s, size_t n) __nonnull; + +extern const char * __pure strerror ( int errno ); + +#endif /* ETHERBOOT_STRING */ diff --git a/gpxe/src/include/strings.h b/gpxe/src/include/strings.h new file mode 100644 index 0000000..968a7c1 --- /dev/null +++ b/gpxe/src/include/strings.h @@ -0,0 +1,63 @@ +#ifndef _STRINGS_H +#define _STRINGS_H + +#include +#include + +static inline __attribute__ (( always_inline )) int +__constant_flsl ( unsigned long x ) { + int r = 0; + +#if ULONG_MAX > 0xffffffff + if ( x & 0xffffffff00000000UL ) { + x >>= 32; + r += 32; + } +#endif + if ( x & 0xffff0000UL ) { + x >>= 16; + r += 16; + } + if ( x & 0xff00 ) { + x >>= 8; + r += 8; + } + if ( x & 0xf0 ) { + x >>= 4; + r += 4; + } + if ( x & 0xc ) { + x >>= 2; + r += 2; + } + if ( x & 0x2 ) { + x >>= 1; + r += 1; + } + if ( x & 0x1 ) { + r += 1; + } + return r; +} + +/* We don't actually have these functions yet */ +extern int __flsl ( long x ); + +#define flsl( x ) \ + ( __builtin_constant_p ( x ) ? __constant_flsl ( x ) : __flsl ( x ) ) + +#define fls( x ) flsl ( x ) + +extern int strcasecmp ( const char *s1, const char *s2 ); + +static inline __attribute__ (( always_inline )) void +bcopy ( const void *src, void *dest, size_t n ) { + memmove ( dest, src, n ); +} + +static inline __attribute__ (( always_inline )) void +bzero ( void *s, size_t n ) { + memset ( s, 0, n ); +} + +#endif /* _STRINGS_H */ diff --git a/gpxe/src/include/sys/time.h b/gpxe/src/include/sys/time.h new file mode 100644 index 0000000..21fb7e9 --- /dev/null +++ b/gpxe/src/include/sys/time.h @@ -0,0 +1,20 @@ +#ifndef _SYS_TIME_H +#define _SYS_TIME_H + +#include + +typedef unsigned long suseconds_t; + +struct timeval { + time_t tv_sec; /* seconds */ + suseconds_t tv_usec; /* microseconds */ +}; + +struct timezone { + int tz_minuteswest; /* minutes W of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; + +extern int gettimeofday ( struct timeval *tv, struct timezone *tz ); + +#endif /* _SYS_TIME_H */ diff --git a/gpxe/src/include/sys_info.h b/gpxe/src/include/sys_info.h new file mode 100644 index 0000000..7127c64 --- /dev/null +++ b/gpxe/src/include/sys_info.h @@ -0,0 +1,33 @@ +#ifndef SYS_INFO_H +#define SYS_INFO_H + +/* Information collected from firmware/bootloader */ + +struct sys_info { + /* Values passed by bootloader */ + unsigned long boot_type; + unsigned long boot_data; + unsigned long boot_arg; + + char *firmware; /* "PCBIOS", "LinuxBIOS", etc. */ + char *command_line; /* command line given to us */ +#if 0 +//By LYH +//Will use meminfo in Etherboot + /* memory map */ + int n_memranges; + struct memrange { + unsigned long long base; + unsigned long long size; + } *memrange; +#endif +}; + +void collect_sys_info(struct sys_info *info); +void collect_elfboot_info(struct sys_info *info); +void collect_linuxbios_info(struct sys_info *info); + +/* Our name and version. I want to see single instance of these in the image */ +extern const char *program_name, *program_version; + +#endif /* SYS_INFO_H */ diff --git a/gpxe/src/include/time.h b/gpxe/src/include/time.h new file mode 100644 index 0000000..6ea927c --- /dev/null +++ b/gpxe/src/include/time.h @@ -0,0 +1,22 @@ +#ifndef _TIME_H +#define _TIME_H + +typedef unsigned long time_t; + +struct tm { + int tm_sec; /* seconds */ + int tm_min; /* minutes */ + int tm_hour; /* hours */ + int tm_mday; /* day of the month */ + int tm_mon; /* month */ + int tm_year; /* year */ + int tm_wday; /* day of the week */ + int tm_yday; /* day in the year */ + int tm_isdst; /* daylight saving time */ +}; + +extern time_t time ( time_t *t ); + +extern time_t mktime ( struct tm *tm ); + +#endif /* _TIME_H */ diff --git a/gpxe/src/include/unistd.h b/gpxe/src/include/unistd.h new file mode 100644 index 0000000..dc1f67f --- /dev/null +++ b/gpxe/src/include/unistd.h @@ -0,0 +1,41 @@ +#ifndef _UNISTD_H +#define _UNISTD_H + +#include +#include + +extern int execv ( const char *command, char * const argv[] ); + +/** + * Execute command + * + * @v command Command name + * @v arg ... Argument list (starting with argv[0]) + * @ret rc Command exit status + * + * This is a front end to execv(). + */ +#define execl( command, arg, ... ) ( { \ + char * const argv[] = { (arg), ## __VA_ARGS__ }; \ + int rc = execv ( (command), argv ); \ + rc; \ + } ) + +/* Pick up udelay() */ +#include + +/* + * sleep() prototype is defined by POSIX.1. usleep() prototype is + * defined by 4.3BSD. udelay() and mdelay() prototypes are chosen to + * be reasonably sensible. + * + */ + +extern unsigned int sleep ( unsigned int seconds ); +extern void mdelay ( unsigned long msecs ); + +static inline __always_inline void usleep ( unsigned long usecs ) { + udelay ( usecs ); +} + +#endif /* _UNISTD_H */ diff --git a/gpxe/src/include/usr/autoboot.h b/gpxe/src/include/usr/autoboot.h new file mode 100644 index 0000000..1e9647c --- /dev/null +++ b/gpxe/src/include/usr/autoboot.h @@ -0,0 +1,21 @@ +#ifndef _USR_AUTOBOOT_H +#define _USR_AUTOBOOT_H + +/** @file + * + * Automatic booting + * + */ + +#include +struct net_device; + +extern int shutdown_exit_flags; + +extern void autoboot ( void ); +extern int boot_next_server_and_filename ( struct in_addr next_server, + const char *filename ); +extern int boot_root_path ( const char *root_path ); +extern int pxe_menu_boot ( struct net_device *netdev ); + +#endif /* _USR_AUTOBOOT_H */ diff --git a/gpxe/src/include/usr/dhcpmgmt.h b/gpxe/src/include/usr/dhcpmgmt.h new file mode 100644 index 0000000..0f27560 --- /dev/null +++ b/gpxe/src/include/usr/dhcpmgmt.h @@ -0,0 +1,15 @@ +#ifndef _USR_DHCPMGMT_H +#define _USR_DHCPMGMT_H + +/** @file + * + * DHCP management + * + */ + +struct net_device; + +extern int dhcp ( struct net_device *netdev ); +extern int pxebs ( struct net_device *netdev, unsigned int pxe_type ); + +#endif /* _USR_DHCPMGMT_H */ diff --git a/gpxe/src/include/usr/ifmgmt.h b/gpxe/src/include/usr/ifmgmt.h new file mode 100644 index 0000000..7b49d34 --- /dev/null +++ b/gpxe/src/include/usr/ifmgmt.h @@ -0,0 +1,17 @@ +#ifndef _USR_IFMGMT_H +#define _USR_IFMGMT_H + +/** @file + * + * Network interface management + * + */ + +struct net_device; + +extern int ifopen ( struct net_device *netdev ); +extern void ifclose ( struct net_device *netdev ); +extern void ifstat ( struct net_device *netdev ); +extern int iflinkwait ( struct net_device *netdev, unsigned int max_wait_ms ); + +#endif /* _USR_IFMGMT_H */ diff --git a/gpxe/src/include/usr/imgmgmt.h b/gpxe/src/include/usr/imgmgmt.h new file mode 100644 index 0000000..438af00 --- /dev/null +++ b/gpxe/src/include/usr/imgmgmt.h @@ -0,0 +1,20 @@ +#ifndef _USR_IMGMGMT_H +#define _USR_IMGMGMT_H + +/** @file + * + * Image management + * + */ + +struct image; + +extern int imgfetch ( struct image *image, const char *uri_string, + int ( * image_register ) ( struct image *image ) ); +extern int imgload ( struct image *image ); +extern int imgexec ( struct image *image ); +extern struct image * imgautoselect ( void ); +extern void imgstat ( struct image *image ); +extern void imgfree ( struct image *image ); + +#endif /* _USR_IMGMGMT_H */ diff --git a/gpxe/src/include/usr/route.h b/gpxe/src/include/usr/route.h new file mode 100644 index 0000000..fc85589 --- /dev/null +++ b/gpxe/src/include/usr/route.h @@ -0,0 +1,12 @@ +#ifndef _USR_ROUTE_H +#define _USR_ROUTE_H + +/** @file + * + * Routing table management + * + */ + +extern void route ( void ); + +#endif /* _USR_ROUTE_H */ diff --git a/gpxe/src/interface/efi/efi_console.c b/gpxe/src/interface/efi/efi_console.c new file mode 100644 index 0000000..b78de61 --- /dev/null +++ b/gpxe/src/interface/efi/efi_console.c @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include +#include +#include + +#define ATTR_BOLD 0x08 + +#define ATTR_FCOL_MASK 0x07 +#define ATTR_FCOL_BLACK 0x00 +#define ATTR_FCOL_BLUE 0x01 +#define ATTR_FCOL_GREEN 0x02 +#define ATTR_FCOL_CYAN 0x03 +#define ATTR_FCOL_RED 0x04 +#define ATTR_FCOL_MAGENTA 0x05 +#define ATTR_FCOL_YELLOW 0x06 +#define ATTR_FCOL_WHITE 0x07 + +#define ATTR_BCOL_MASK 0x70 +#define ATTR_BCOL_BLACK 0x00 +#define ATTR_BCOL_BLUE 0x10 +#define ATTR_BCOL_GREEN 0x20 +#define ATTR_BCOL_CYAN 0x30 +#define ATTR_BCOL_RED 0x40 +#define ATTR_BCOL_MAGENTA 0x50 +#define ATTR_BCOL_YELLOW 0x60 +#define ATTR_BCOL_WHITE 0x70 + +#define ATTR_DEFAULT ATTR_FCOL_WHITE + +/** Current character attribute */ +static unsigned int efi_attr = ATTR_DEFAULT; + +/** + * Handle ANSI CUP (cursor position) + * + * @v count Parameter count + * @v params[0] Row (1 is top) + * @v params[1] Column (1 is left) + */ +static void efi_handle_cup ( unsigned int count __unused, int params[] ) { + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut; + int cx = ( params[1] - 1 ); + int cy = ( params[0] - 1 ); + + if ( cx < 0 ) + cx = 0; + if ( cy < 0 ) + cy = 0; + + conout->SetCursorPosition ( conout, cx, cy ); +} + +/** + * Handle ANSI ED (erase in page) + * + * @v count Parameter count + * @v params[0] Region to erase + */ +static void efi_handle_ed ( unsigned int count __unused, + int params[] __unused ) { + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut; + + /* We assume that we always clear the whole screen */ + assert ( params[0] == ANSIESC_ED_ALL ); + + conout->ClearScreen ( conout ); +} + +/** + * Handle ANSI SGR (set graphics rendition) + * + * @v count Parameter count + * @v params List of graphic rendition aspects + */ +static void efi_handle_sgr ( unsigned int count, int params[] ) { + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut; + static const uint8_t efi_attr_fcols[10] = { + ATTR_FCOL_BLACK, ATTR_FCOL_RED, ATTR_FCOL_GREEN, + ATTR_FCOL_YELLOW, ATTR_FCOL_BLUE, ATTR_FCOL_MAGENTA, + ATTR_FCOL_CYAN, ATTR_FCOL_WHITE, + ATTR_FCOL_WHITE, ATTR_FCOL_WHITE /* defaults */ + }; + static const uint8_t efi_attr_bcols[10] = { + ATTR_BCOL_BLACK, ATTR_BCOL_RED, ATTR_BCOL_GREEN, + ATTR_BCOL_YELLOW, ATTR_BCOL_BLUE, ATTR_BCOL_MAGENTA, + ATTR_BCOL_CYAN, ATTR_BCOL_WHITE, + ATTR_BCOL_BLACK, ATTR_BCOL_BLACK /* defaults */ + }; + unsigned int i; + int aspect; + + for ( i = 0 ; i < count ; i++ ) { + aspect = params[i]; + if ( aspect == 0 ) { + efi_attr = ATTR_DEFAULT; + } else if ( aspect == 1 ) { + efi_attr |= ATTR_BOLD; + } else if ( aspect == 22 ) { + efi_attr &= ~ATTR_BOLD; + } else if ( ( aspect >= 30 ) && ( aspect <= 39 ) ) { + efi_attr &= ~ATTR_FCOL_MASK; + efi_attr |= efi_attr_fcols[ aspect - 30 ]; + } else if ( ( aspect >= 40 ) && ( aspect <= 49 ) ) { + efi_attr &= ~ATTR_BCOL_MASK; + efi_attr |= efi_attr_bcols[ aspect - 40 ]; + } + } + + conout->SetAttribute ( conout, efi_attr ); +} + +/** EFI console ANSI escape sequence handlers */ +static struct ansiesc_handler efi_ansiesc_handlers[] = { + { ANSIESC_CUP, efi_handle_cup }, + { ANSIESC_ED, efi_handle_ed }, + { ANSIESC_SGR, efi_handle_sgr }, + { 0, NULL } +}; + +/** EFI console ANSI escape sequence context */ +static struct ansiesc_context efi_ansiesc_ctx = { + .handlers = efi_ansiesc_handlers, +}; + +/** + * Print a character to EFI console + * + * @v character Character to be printed + */ +static void efi_putchar ( int character ) { + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut; + wchar_t wstr[] = { character, 0 }; + + /* Intercept ANSI escape sequences */ + character = ansiesc_process ( &efi_ansiesc_ctx, character ); + if ( character < 0 ) + return; + + conout->OutputString ( conout, wstr ); +} + +/** + * Pointer to current ANSI output sequence + * + * While we are in the middle of returning an ANSI sequence for a + * special key, this will point to the next character to return. When + * not in the middle of such a sequence, this will point to a NUL + * (note: not "will be NULL"). + */ +static const char *ansi_input = ""; + +/** Mapping from EFI scan codes to ANSI escape sequences */ +static const char *ansi_sequences[] = { + [SCAN_UP] = "[A", + [SCAN_DOWN] = "[B", + [SCAN_RIGHT] = "[C", + [SCAN_LEFT] = "[D", + [SCAN_HOME] = "[H", + [SCAN_END] = "[F", + [SCAN_INSERT] = "[2~", + /* EFI translates an incoming backspace via the serial console + * into a SCAN_DELETE. There's not much we can do about this. + */ + [SCAN_DELETE] = "[3~", + [SCAN_PAGE_UP] = "[5~", + [SCAN_PAGE_DOWN] = "[6~", + /* EFI translates some (but not all) incoming escape sequences + * via the serial console into equivalent scancodes. When it + * doesn't recognise a sequence, it helpfully(!) translates + * the initial ESC and passes the remainder through verbatim. + * Treating SCAN_ESC as equivalent to an empty escape sequence + * works around this bug. + */ + [SCAN_ESC] = "", +}; + +/** + * Get ANSI escape sequence corresponding to EFI scancode + * + * @v scancode EFI scancode + * @ret ansi_seq ANSI escape sequence, if any, otherwise NULL + */ +static const char * scancode_to_ansi_seq ( unsigned int scancode ) { + if ( scancode < ( sizeof ( ansi_sequences ) / + sizeof ( ansi_sequences[0] ) ) ) { + return ansi_sequences[scancode]; + } + return NULL; +} + +/** + * Get character from EFI console + * + * @ret character Character read from console + */ +static int efi_getchar ( void ) { + EFI_SIMPLE_TEXT_INPUT_PROTOCOL *conin = efi_systab->ConIn; + const char *ansi_seq; + EFI_INPUT_KEY key; + EFI_STATUS efirc; + + /* If we are mid-sequence, pass out the next byte */ + if ( *ansi_input ) + return *(ansi_input++); + + /* Read key from real EFI console */ + if ( ( efirc = conin->ReadKeyStroke ( conin, &key ) ) != 0 ) { + DBG ( "EFI could not read keystroke: %s\n", + efi_strerror ( efirc ) ); + return 0; + } + DBG2 ( "EFI read key stroke with unicode %04x scancode %04x\n", + key.UnicodeChar, key.ScanCode ); + + /* If key has a Unicode representation, return it */ + if ( key.UnicodeChar ) + return key.UnicodeChar; + + /* Otherwise, check for a special key that we know about */ + if ( ( ansi_seq = scancode_to_ansi_seq ( key.ScanCode ) ) ) { + /* Start of escape sequence: return ESC (0x1b) */ + ansi_input = ansi_seq; + return 0x1b; + } + + return 0; +} + +/** + * Check for character ready to read from EFI console + * + * @ret True Character available to read + * @ret False No character available to read + */ +static int efi_iskey ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_SIMPLE_TEXT_INPUT_PROTOCOL *conin = efi_systab->ConIn; + EFI_STATUS efirc; + + /* If we are mid-sequence, we are always ready */ + if ( *ansi_input ) + return 1; + + /* Check to see if the WaitForKey event has fired */ + if ( ( efirc = bs->CheckEvent ( conin->WaitForKey ) ) == 0 ) + return 1; + + return 0; +} + +struct console_driver efi_console __console_driver = { + .putchar = efi_putchar, + .getchar = efi_getchar, + .iskey = efi_iskey, +}; diff --git a/gpxe/src/interface/efi/efi_init.c b/gpxe/src/interface/efi/efi_init.c new file mode 100644 index 0000000..6e54cf7 --- /dev/null +++ b/gpxe/src/interface/efi/efi_init.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include + +/** Image handle passed to entry point */ +EFI_HANDLE efi_image_handle; + +/** System table passed to entry point */ +EFI_SYSTEM_TABLE *efi_systab; + +/** Declared used EFI protocols */ +static struct efi_protocol efi_protocols[0] \ + __table_start ( struct efi_protocol, efi_protocols ); +static struct efi_protocol efi_protocols_end[0] \ + __table_end ( struct efi_protocol, efi_protocols ); + +/** Declared used EFI configuration tables */ +static struct efi_config_table efi_config_tables[0] \ + __table_start ( struct efi_config_table, efi_config_tables ); +static struct efi_config_table efi_config_tables_end[0] \ + __table_end ( struct efi_config_table, efi_config_tables ); + +/** + * Look up EFI configuration table + * + * @v guid Configuration table GUID + * @ret table Configuration table, or NULL + */ +static void * efi_find_table ( EFI_GUID *guid ) { + unsigned int i; + + for ( i = 0 ; i < efi_systab->NumberOfTableEntries ; i++ ) { + if ( memcmp ( &efi_systab->ConfigurationTable[i].VendorGuid, + guid, sizeof ( *guid ) ) == 0 ) + return efi_systab->ConfigurationTable[i].VendorTable; + } + + return NULL; +} + +/** + * Initialise EFI environment + * + * @v image_handle Image handle + * @v systab System table + * @ret efirc EFI return status code + */ +EFI_STATUS efi_init ( EFI_HANDLE image_handle, + EFI_SYSTEM_TABLE *systab ) { + EFI_BOOT_SERVICES *bs; + struct efi_protocol *prot; + struct efi_config_table *tab; + EFI_STATUS efirc; + + /* Store image handle and system table pointer for future use */ + efi_image_handle = image_handle; + efi_systab = systab; + + /* Sanity checks */ + if ( ! systab ) + return EFI_NOT_AVAILABLE_YET; + if ( ! systab->ConOut ) + return EFI_NOT_AVAILABLE_YET; + if ( ! systab->BootServices ) { + DBGC ( systab, "EFI provided no BootServices entry point\n" ); + return EFI_NOT_AVAILABLE_YET; + } + if ( ! systab->RuntimeServices ) { + DBGC ( systab, "EFI provided no RuntimeServices entry " + "point\n" ); + return EFI_NOT_AVAILABLE_YET; + } + DBGC ( systab, "EFI handle %p systab %p\n", image_handle, systab ); + + /* Look up used protocols */ + bs = systab->BootServices; + for ( prot = efi_protocols ; prot < efi_protocols_end ; prot++ ) { + if ( ( efirc = bs->LocateProtocol ( &prot->u.guid, NULL, + prot->protocol ) ) == 0 ) { + DBGC ( systab, "EFI protocol %s is at %p\n", + uuid_ntoa ( &prot->u.uuid ), *(prot->protocol)); + } else { + DBGC ( systab, "EFI does not provide protocol %s\n", + uuid_ntoa ( &prot->u.uuid ) ); + /* All protocols are required */ + return efirc; + } + } + + /* Look up used configuration tables */ + for ( tab = efi_config_tables ; tab < efi_config_tables_end ; tab++ ) { + if ( ( *(tab->table) = efi_find_table ( &tab->u.guid ) ) ) { + DBGC ( systab, "EFI configuration table %s is at %p\n", + uuid_ntoa ( &tab->u.uuid ), *(tab->table) ); + } else { + DBGC ( systab, "EFI does not provide configuration " + "table %s\n", uuid_ntoa ( &tab->u.uuid ) ); + if ( tab->required ) + return EFI_NOT_AVAILABLE_YET; + } + } + + return 0; +} diff --git a/gpxe/src/interface/efi/efi_io.c b/gpxe/src/interface/efi/efi_io.c new file mode 100644 index 0000000..e11f9bf --- /dev/null +++ b/gpxe/src/interface/efi/efi_io.c @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include +#include +#include + +/** @file + * + * gPXE I/O API for EFI + * + */ + +/** CPU I/O protocol */ +static EFI_CPU_IO_PROTOCOL *cpu_io; +EFI_REQUIRE_PROTOCOL ( EFI_CPU_IO_PROTOCOL, &cpu_io ); + +/** Maximum address that can be used for port I/O */ +#define MAX_PORT_ADDRESS 0xffff + +/** + * Determine whether or not address is a port I/O address + * + * @v io_addr I/O address + * @v is_port I/O address is a port I/O address + */ +#define IS_PORT_ADDRESS(io_addr) \ + ( ( ( intptr_t ) (io_addr) ) <= MAX_PORT_ADDRESS ) + +/** + * Determine EFI CPU I/O width code + * + * @v size Size of value + * @ret width EFI width code + * + * Someone at Intel clearly gets paid by the number of lines of code + * they write. No-one should ever be able to make I/O this + * convoluted. The EFI_CPU_IO_PROTOCOL_WIDTH enum is my favourite + * idiocy. + */ +static EFI_CPU_IO_PROTOCOL_WIDTH efi_width ( size_t size ) { + switch ( size ) { + case 1 : return EfiCpuIoWidthFifoUint8; + case 2 : return EfiCpuIoWidthFifoUint16; + case 4 : return EfiCpuIoWidthFifoUint32; + case 8 : return EfiCpuIoWidthFifoUint64; + default : + assert ( 0 ); + /* I wonder what this will actually do... */ + return EfiCpuIoWidthMaximum; + } +} + +/** + * Read from device + * + * @v io_addr I/O address + * @v size Size of value + * @ret data Value read + */ +unsigned long long efi_ioread ( volatile void *io_addr, size_t size ) { + EFI_CPU_IO_PROTOCOL_IO_MEM read; + unsigned long long data = 0; + EFI_STATUS efirc; + + read = ( IS_PORT_ADDRESS ( io_addr ) ? + cpu_io->Io.Read : cpu_io->Mem.Read ); + + if ( ( efirc = read ( cpu_io, efi_width ( size ), + ( intptr_t ) io_addr, 1, + ( void * ) &data ) ) != 0 ) { + DBG ( "EFI I/O read at %p failed: %s\n", + io_addr, efi_strerror ( efirc ) ); + return -1ULL; + } + + return data; +} + +/** + * Write to device + * + * @v data Value to write + * @v io_addr I/O address + * @v size Size of value + */ +void efi_iowrite ( unsigned long long data, volatile void *io_addr, + size_t size ) { + EFI_CPU_IO_PROTOCOL_IO_MEM write; + EFI_STATUS efirc; + + write = ( IS_PORT_ADDRESS ( io_addr ) ? + cpu_io->Io.Write : cpu_io->Mem.Write ); + + if ( ( efirc = write ( cpu_io, efi_width ( size ), + ( intptr_t ) io_addr, 1, + ( void * ) &data ) ) != 0 ) { + DBG ( "EFI I/O write at %p failed: %s\n", + io_addr, efi_strerror ( efirc ) ); + } +} + +/** + * String read from device + * + * @v io_addr I/O address + * @v data Data buffer + * @v size Size of values + * @v count Number of values to read + */ +void efi_ioreads ( volatile void *io_addr, void *data, + size_t size, unsigned int count ) { + EFI_CPU_IO_PROTOCOL_IO_MEM read; + EFI_STATUS efirc; + + read = ( IS_PORT_ADDRESS ( io_addr ) ? + cpu_io->Io.Read : cpu_io->Mem.Read ); + + if ( ( efirc = read ( cpu_io, efi_width ( size ), + ( intptr_t ) io_addr, count, + ( void * ) data ) ) != 0 ) { + DBG ( "EFI I/O string read at %p failed: %s\n", + io_addr, efi_strerror ( efirc ) ); + } +} + +/** + * String write to device + * + * @v io_addr I/O address + * @v data Data buffer + * @v size Size of values + * @v count Number of values to write + */ +void efi_iowrites ( volatile void *io_addr, const void *data, + size_t size, unsigned int count ) { + EFI_CPU_IO_PROTOCOL_IO_MEM write; + EFI_STATUS efirc; + + write = ( IS_PORT_ADDRESS ( io_addr ) ? + cpu_io->Io.Write : cpu_io->Mem.Write ); + + if ( ( efirc = write ( cpu_io, efi_width ( size ), + ( intptr_t ) io_addr, count, + ( void * ) data ) ) != 0 ) { + DBG ( "EFI I/O write at %p failed: %s\n", + io_addr, efi_strerror ( efirc ) ); + } +} + +/** + * Wait for I/O-mapped operation to complete + * + */ +static void efi_iodelay ( void ) { + /* Write to non-existent port. Probably x86-only. */ + outb ( 0, 0x80 ); +} + +PROVIDE_IOAPI_INLINE ( efi, phys_to_bus ); +PROVIDE_IOAPI_INLINE ( efi, bus_to_phys ); +PROVIDE_IOAPI_INLINE ( efi, ioremap ); +PROVIDE_IOAPI_INLINE ( efi, iounmap ); +PROVIDE_IOAPI_INLINE ( efi, io_to_bus ); +PROVIDE_IOAPI_INLINE ( efi, readb ); +PROVIDE_IOAPI_INLINE ( efi, readw ); +PROVIDE_IOAPI_INLINE ( efi, readl ); +PROVIDE_IOAPI_INLINE ( efi, readq ); +PROVIDE_IOAPI_INLINE ( efi, writeb ); +PROVIDE_IOAPI_INLINE ( efi, writew ); +PROVIDE_IOAPI_INLINE ( efi, writel ); +PROVIDE_IOAPI_INLINE ( efi, writeq ); +PROVIDE_IOAPI_INLINE ( efi, inb ); +PROVIDE_IOAPI_INLINE ( efi, inw ); +PROVIDE_IOAPI_INLINE ( efi, inl ); +PROVIDE_IOAPI_INLINE ( efi, outb ); +PROVIDE_IOAPI_INLINE ( efi, outw ); +PROVIDE_IOAPI_INLINE ( efi, outl ); +PROVIDE_IOAPI_INLINE ( efi, insb ); +PROVIDE_IOAPI_INLINE ( efi, insw ); +PROVIDE_IOAPI_INLINE ( efi, insl ); +PROVIDE_IOAPI_INLINE ( efi, outsb ); +PROVIDE_IOAPI_INLINE ( efi, outsw ); +PROVIDE_IOAPI_INLINE ( efi, outsl ); +PROVIDE_IOAPI ( efi, iodelay, efi_iodelay ); +PROVIDE_IOAPI_INLINE ( efi, mb ); diff --git a/gpxe/src/interface/efi/efi_pci.c b/gpxe/src/interface/efi/efi_pci.c new file mode 100644 index 0000000..f87b540 --- /dev/null +++ b/gpxe/src/interface/efi/efi_pci.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include +#include + +/** @file + * + * gPXE PCI I/O API for EFI + * + */ + +/** PCI root bridge I/O protocol */ +static EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *efipci; +EFI_REQUIRE_PROTOCOL ( EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL, &efipci ); + +static unsigned long efipci_address ( struct pci_device *pci, + unsigned long location ) { + return EFI_PCI_ADDRESS ( pci->bus, PCI_SLOT ( pci->devfn ), + PCI_FUNC ( pci->devfn ), + EFIPCI_OFFSET ( location ) ); +} + +int efipci_read ( struct pci_device *pci, unsigned long location, + void *value ) { + EFI_STATUS efirc; + + if ( ( efirc = efipci->Pci.Read ( efipci, EFIPCI_WIDTH ( location ), + efipci_address ( pci, location ), 1, + value ) ) != 0 ) { + DBG ( "EFIPCI config read from %02x:%02x.%x offset %02lx " + "failed: %s\n", pci->bus, PCI_SLOT ( pci->devfn ), + PCI_FUNC ( pci->devfn ), EFIPCI_OFFSET ( location ), + efi_strerror ( efirc ) ); + return -EIO; + } + + return 0; +} + +int efipci_write ( struct pci_device *pci, unsigned long location, + unsigned long value ) { + EFI_STATUS efirc; + + if ( ( efirc = efipci->Pci.Write ( efipci, EFIPCI_WIDTH ( location ), + efipci_address ( pci, location ), 1, + &value ) ) != 0 ) { + DBG ( "EFIPCI config write to %02x:%02x.%x offset %02lx " + "failed: %s\n", pci->bus, PCI_SLOT ( pci->devfn ), + PCI_FUNC ( pci->devfn ), EFIPCI_OFFSET ( location ), + efi_strerror ( efirc ) ); + return -EIO; + } + + return 0; +} + +PROVIDE_PCIAPI_INLINE ( efi, pci_max_bus ); +PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_byte ); +PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_word ); +PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_dword ); +PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_byte ); +PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_word ); +PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_dword ); diff --git a/gpxe/src/interface/efi/efi_smbios.c b/gpxe/src/interface/efi/efi_smbios.c new file mode 100644 index 0000000..5888f2f --- /dev/null +++ b/gpxe/src/interface/efi/efi_smbios.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include +#include + +/** @file + * + * gPXE SMBIOS API for EFI + * + */ + +/** SMBIOS configuration table */ +static struct smbios_entry *smbios_entry; +EFI_USE_TABLE ( EFI_SMBIOS_TABLE, &smbios_entry, 0 ); + +/** + * Find SMBIOS + * + * @v smbios SMBIOS entry point descriptor structure to fill in + * @ret rc Return status code + */ +static int efi_find_smbios ( struct smbios *smbios ) { + + if ( ! smbios_entry ) { + DBG ( "No SMBIOS table provided\n" ); + return -ENODEV; + } + + if ( smbios_entry->signature != SMBIOS_SIGNATURE ) { + DBG ( "Invalid SMBIOS signature\n" ); + return -ENODEV; + } + + smbios->address = phys_to_user ( smbios_entry->smbios_address ); + smbios->len = smbios_entry->smbios_len; + smbios->count = smbios_entry->smbios_count; + DBG ( "Found SMBIOS v%d.%d entry point at %p (%x+%zx)\n", + smbios_entry->major, smbios_entry->minor, smbios_entry, + smbios_entry->smbios_address, smbios->len ); + + return 0; +} + +PROVIDE_SMBIOS ( efi, find_smbios, efi_find_smbios ); diff --git a/gpxe/src/interface/efi/efi_snp.c b/gpxe/src/interface/efi/efi_snp.c new file mode 100644 index 0000000..771b917 --- /dev/null +++ b/gpxe/src/interface/efi/efi_snp.c @@ -0,0 +1,1145 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * gPXE EFI SNP interface + * + */ + +/** An SNP device */ +struct efi_snp_device { + /** The underlying gPXE network device */ + struct net_device *netdev; + /** EFI device handle */ + EFI_HANDLE handle; + /** The SNP structure itself */ + EFI_SIMPLE_NETWORK_PROTOCOL snp; + /** The SNP "mode" (parameters) */ + EFI_SIMPLE_NETWORK_MODE mode; + /** Outstanding TX packet count (via "interrupt status") + * + * Used in order to generate TX completions. + */ + unsigned int tx_count_interrupts; + /** Outstanding TX packet count (via "recycled tx buffers") + * + * Used in order to generate TX completions. + */ + unsigned int tx_count_txbufs; + /** Outstanding RX packet count (via "interrupt status") */ + unsigned int rx_count_interrupts; + /** Outstanding RX packet count (via WaitForPacket event) */ + unsigned int rx_count_events; + /** The network interface identifier */ + EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL nii; + /** Device name */ + wchar_t name[ sizeof ( ( ( struct net_device * ) NULL )->name ) ]; + /** The device path + * + * This field is variable in size and must appear at the end + * of the structure. + */ + EFI_DEVICE_PATH_PROTOCOL path; +}; + +/** EFI simple network protocol GUID */ +static EFI_GUID efi_simple_network_protocol_guid + = EFI_SIMPLE_NETWORK_PROTOCOL_GUID; + +/** EFI driver binding protocol GUID */ +static EFI_GUID efi_driver_binding_protocol_guid + = EFI_DRIVER_BINDING_PROTOCOL_GUID; + +/** EFI component name protocol GUID */ +static EFI_GUID efi_component_name2_protocol_guid + = EFI_COMPONENT_NAME2_PROTOCOL_GUID; + +/** EFI device path protocol GUID */ +static EFI_GUID efi_device_path_protocol_guid + = EFI_DEVICE_PATH_PROTOCOL_GUID; + +/** EFI network interface identifier GUID */ +static EFI_GUID efi_nii_protocol_guid + = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_GUID; + +/** EFI network interface identifier GUID (extra special version) */ +static EFI_GUID efi_nii31_protocol_guid = { + /* At some point, it seems that someone decided to change the + * GUID. Current EFI builds ignore the older GUID, older EFI + * builds ignore the newer GUID, so we have to expose both. + */ + 0x1ACED566, 0x76ED, 0x4218, + { 0xBC, 0x81, 0x76, 0x7F, 0x1F, 0x97, 0x7A, 0x89 } +}; + +/** EFI PCI I/O protocol GUID */ +static EFI_GUID efi_pci_io_protocol_guid + = EFI_PCI_IO_PROTOCOL_GUID; + +/** + * Set EFI SNP mode based on gPXE net device parameters + * + * @v snp SNP interface + */ +static void efi_snp_set_mode ( struct efi_snp_device *snpdev ) { + struct net_device *netdev = snpdev->netdev; + EFI_SIMPLE_NETWORK_MODE *mode = &snpdev->mode; + unsigned int ll_addr_len = netdev->ll_protocol->ll_addr_len; + + mode->HwAddressSize = ll_addr_len; + mode->MediaHeaderSize = netdev->ll_protocol->ll_header_len; + mode->MaxPacketSize = netdev->max_pkt_len; + mode->ReceiveFilterMask = ( EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | + EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST | + EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST ); + assert ( ll_addr_len <= sizeof ( mode->CurrentAddress ) ); + memcpy ( &mode->CurrentAddress, netdev->ll_addr, ll_addr_len ); + memcpy ( &mode->BroadcastAddress, netdev->ll_protocol->ll_broadcast, + ll_addr_len ); + memcpy ( &mode->PermanentAddress, netdev->ll_addr, ll_addr_len ); + mode->IfType = ntohs ( netdev->ll_protocol->ll_proto ); + mode->MacAddressChangeable = TRUE; + mode->MediaPresentSupported = TRUE; + mode->MediaPresent = ( netdev_link_ok ( netdev ) ? TRUE : FALSE ); +} + +/** + * Poll net device and count received packets + * + * @v snpdev SNP device + */ +static void efi_snp_poll ( struct efi_snp_device *snpdev ) { + struct io_buffer *iobuf; + unsigned int before = 0; + unsigned int after = 0; + unsigned int arrived; + + /* We have to report packet arrivals, and this is the easiest + * way to fake it. + */ + list_for_each_entry ( iobuf, &snpdev->netdev->rx_queue, list ) + before++; + netdev_poll ( snpdev->netdev ); + list_for_each_entry ( iobuf, &snpdev->netdev->rx_queue, list ) + after++; + arrived = ( after - before ); + + snpdev->rx_count_interrupts += arrived; + snpdev->rx_count_events += arrived; +} + +/** + * Change SNP state from "stopped" to "started" + * + * @v snp SNP interface + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_snp_start ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { + struct efi_snp_device *snpdev = + container_of ( snp, struct efi_snp_device, snp ); + + DBGC2 ( snpdev, "SNPDEV %p START\n", snpdev ); + + snpdev->mode.State = EfiSimpleNetworkStarted; + return 0; +} + +/** + * Change SNP state from "started" to "stopped" + * + * @v snp SNP interface + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_snp_stop ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { + struct efi_snp_device *snpdev = + container_of ( snp, struct efi_snp_device, snp ); + + DBGC2 ( snpdev, "SNPDEV %p STOP\n", snpdev ); + + snpdev->mode.State = EfiSimpleNetworkStopped; + return 0; +} + +/** + * Open the network device + * + * @v snp SNP interface + * @v extra_rx_bufsize Extra RX buffer size, in bytes + * @v extra_tx_bufsize Extra TX buffer size, in bytes + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, + UINTN extra_rx_bufsize, UINTN extra_tx_bufsize ) { + struct efi_snp_device *snpdev = + container_of ( snp, struct efi_snp_device, snp ); + int rc; + + DBGC2 ( snpdev, "SNPDEV %p INITIALIZE (%ld extra RX, %ld extra TX)\n", + snpdev, ( ( unsigned long ) extra_rx_bufsize ), + ( ( unsigned long ) extra_tx_bufsize ) ); + + if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) { + DBGC ( snpdev, "SNPDEV %p could not open %s: %s\n", + snpdev, snpdev->netdev->name, strerror ( rc ) ); + return RC_TO_EFIRC ( rc ); + } + + snpdev->mode.State = EfiSimpleNetworkInitialized; + return 0; +} + +/** + * Reset the network device + * + * @v snp SNP interface + * @v ext_verify Extended verification required + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) { + struct efi_snp_device *snpdev = + container_of ( snp, struct efi_snp_device, snp ); + int rc; + + DBGC2 ( snpdev, "SNPDEV %p RESET (%s extended verification)\n", + snpdev, ( ext_verify ? "with" : "without" ) ); + + netdev_close ( snpdev->netdev ); + snpdev->mode.State = EfiSimpleNetworkStarted; + + if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) { + DBGC ( snpdev, "SNPDEV %p could not reopen %s: %s\n", + snpdev, snpdev->netdev->name, strerror ( rc ) ); + return RC_TO_EFIRC ( rc ); + } + + snpdev->mode.State = EfiSimpleNetworkInitialized; + return 0; +} + +/** + * Shut down the network device + * + * @v snp SNP interface + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { + struct efi_snp_device *snpdev = + container_of ( snp, struct efi_snp_device, snp ); + + DBGC2 ( snpdev, "SNPDEV %p SHUTDOWN\n", snpdev ); + + netdev_close ( snpdev->netdev ); + snpdev->mode.State = EfiSimpleNetworkStarted; + return 0; +} + +/** + * Manage receive filters + * + * @v snp SNP interface + * @v enable Receive filters to enable + * @v disable Receive filters to disable + * @v mcast_reset Reset multicast filters + * @v mcast_count Number of multicast filters + * @v mcast Multicast filters + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_snp_receive_filters ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINT32 enable, + UINT32 disable, BOOLEAN mcast_reset, + UINTN mcast_count, EFI_MAC_ADDRESS *mcast ) { + struct efi_snp_device *snpdev = + container_of ( snp, struct efi_snp_device, snp ); + unsigned int i; + + DBGC2 ( snpdev, "SNPDEV %p RECEIVE_FILTERS %08x&~%08x%s %ld mcast\n", + snpdev, enable, disable, ( mcast_reset ? " reset" : "" ), + ( ( unsigned long ) mcast_count ) ); + for ( i = 0 ; i < mcast_count ; i++ ) { + DBGC2_HDA ( snpdev, i, &mcast[i], + snpdev->netdev->ll_protocol->ll_addr_len ); + } + + /* Lie through our teeth, otherwise MNP refuses to accept us */ + return 0; +} + +/** + * Set station address + * + * @v snp SNP interface + * @v reset Reset to permanent address + * @v new New station address + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_snp_station_address ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset, + EFI_MAC_ADDRESS *new ) { + struct efi_snp_device *snpdev = + container_of ( snp, struct efi_snp_device, snp ); + struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol; + + DBGC2 ( snpdev, "SNPDEV %p STATION_ADDRESS %s\n", snpdev, + ( reset ? "reset" : ll_protocol->ntoa ( new ) ) ); + + /* Set the MAC address */ + if ( reset ) + new = &snpdev->mode.PermanentAddress; + memcpy ( snpdev->netdev->ll_addr, new, ll_protocol->ll_addr_len ); + + /* MAC address changes take effect only on netdev_open() */ + if ( snpdev->netdev->state & NETDEV_OPEN ) { + DBGC ( snpdev, "SNPDEV %p MAC address changed while net " + "devive open\n", snpdev ); + } + + return 0; +} + +/** + * Get (or reset) statistics + * + * @v snp SNP interface + * @v reset Reset statistics + * @v stats_len Size of statistics table + * @v stats Statistics table + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_snp_statistics ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset, + UINTN *stats_len, EFI_NETWORK_STATISTICS *stats ) { + struct efi_snp_device *snpdev = + container_of ( snp, struct efi_snp_device, snp ); + EFI_NETWORK_STATISTICS stats_buf; + + DBGC2 ( snpdev, "SNPDEV %p STATISTICS%s", snpdev, + ( reset ? " reset" : "" ) ); + + /* Gather statistics */ + memset ( &stats_buf, 0, sizeof ( stats_buf ) ); + stats_buf.TxGoodFrames = snpdev->netdev->tx_stats.good; + stats_buf.TxDroppedFrames = snpdev->netdev->tx_stats.bad; + stats_buf.TxTotalFrames = ( snpdev->netdev->tx_stats.good + + snpdev->netdev->tx_stats.bad ); + stats_buf.RxGoodFrames = snpdev->netdev->rx_stats.good; + stats_buf.RxDroppedFrames = snpdev->netdev->rx_stats.bad; + stats_buf.RxTotalFrames = ( snpdev->netdev->rx_stats.good + + snpdev->netdev->rx_stats.bad ); + if ( *stats_len > sizeof ( stats_buf ) ) + *stats_len = sizeof ( stats_buf ); + if ( stats ) + memcpy ( stats, &stats_buf, *stats_len ); + + /* Reset statistics if requested to do so */ + if ( reset ) { + memset ( &snpdev->netdev->tx_stats, 0, + sizeof ( snpdev->netdev->tx_stats ) ); + memset ( &snpdev->netdev->rx_stats, 0, + sizeof ( snpdev->netdev->rx_stats ) ); + } + + return 0; +} + +/** + * Convert multicast IP address to MAC address + * + * @v snp SNP interface + * @v ipv6 Address is IPv6 + * @v ip IP address + * @v mac MAC address + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_snp_mcast_ip_to_mac ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ipv6, + EFI_IP_ADDRESS *ip, EFI_MAC_ADDRESS *mac ) { + struct efi_snp_device *snpdev = + container_of ( snp, struct efi_snp_device, snp ); + struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol; + const char *ip_str; + int rc; + + ip_str = ( ipv6 ? "(IPv6)" /* FIXME when we have inet6_ntoa() */ : + inet_ntoa ( *( ( struct in_addr * ) ip ) ) ); + DBGC2 ( snpdev, "SNPDEV %p MCAST_IP_TO_MAC %s\n", snpdev, ip_str ); + + /* Try to hash the address */ + if ( ( rc = ll_protocol->mc_hash ( ( ipv6 ? AF_INET6 : AF_INET ), + ip, mac ) ) != 0 ) { + DBGC ( snpdev, "SNPDEV %p could not hash %s: %s\n", + snpdev, ip_str, strerror ( rc ) ); + return RC_TO_EFIRC ( rc ); + } + + return 0; +} + +/** + * Read or write non-volatile storage + * + * @v snp SNP interface + * @v read Operation is a read + * @v offset Starting offset within NVRAM + * @v len Length of data buffer + * @v data Data buffer + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_snp_nvdata ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN read, + UINTN offset, UINTN len, VOID *data ) { + struct efi_snp_device *snpdev = + container_of ( snp, struct efi_snp_device, snp ); + + DBGC2 ( snpdev, "SNPDEV %p NVDATA %s %lx+%lx\n", snpdev, + ( read ? "read" : "write" ), ( ( unsigned long ) offset ), + ( ( unsigned long ) len ) ); + if ( ! read ) + DBGC2_HDA ( snpdev, offset, data, len ); + + return EFI_UNSUPPORTED; +} + +/** + * Read interrupt status and TX recycled buffer status + * + * @v snp SNP interface + * @v interrupts Interrupt status, or NULL + * @v txbufs Recycled transmit buffer address, or NULL + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, + UINT32 *interrupts, VOID **txbufs ) { + struct efi_snp_device *snpdev = + container_of ( snp, struct efi_snp_device, snp ); + + DBGC2 ( snpdev, "SNPDEV %p GET_STATUS", snpdev ); + + /* Poll the network device */ + efi_snp_poll ( snpdev ); + + /* Interrupt status. In practice, this seems to be used only + * to detect TX completions. + */ + if ( interrupts ) { + *interrupts = 0; + /* Report TX completions once queue is empty; this + * avoids having to add hooks in the net device layer. + */ + if ( snpdev->tx_count_interrupts && + list_empty ( &snpdev->netdev->tx_queue ) ) { + *interrupts |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; + snpdev->tx_count_interrupts--; + } + /* Report RX */ + if ( snpdev->rx_count_interrupts ) { + *interrupts |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; + snpdev->rx_count_interrupts--; + } + DBGC2 ( snpdev, " INTS:%02x", *interrupts ); + } + + /* TX completions. It would be possible to design a more + * idiotic scheme for this, but it would be a challenge. + * According to the UEFI header file, txbufs will be filled in + * with a list of "recycled transmit buffers" (i.e. completed + * TX buffers). Observant readers may care to note that + * *txbufs is a void pointer. Precisely how a list of + * completed transmit buffers is meant to be represented as an + * array of voids is left as an exercise for the reader. + * + * The only users of this interface (MnpDxe/MnpIo.c and + * PxeBcDxe/Bc.c within the EFI dev kit) both just poll until + * seeing a non-NULL result return in txbufs. This is valid + * provided that they do not ever attempt to transmit more + * than one packet concurrently (and that TX never times out). + */ + if ( txbufs ) { + if ( snpdev->tx_count_txbufs && + list_empty ( &snpdev->netdev->tx_queue ) ) { + *txbufs = "Which idiot designed this API?"; + snpdev->tx_count_txbufs--; + } else { + *txbufs = NULL; + } + DBGC2 ( snpdev, " TX:%s", ( *txbufs ? "some" : "none" ) ); + } + + DBGC2 ( snpdev, "\n" ); + return 0; +} + +/** + * Start packet transmission + * + * @v snp SNP interface + * @v ll_header_len Link-layer header length, if to be filled in + * @v len Length of data buffer + * @v data Data buffer + * @v ll_src Link-layer source address, if specified + * @v ll_dest Link-layer destination address, if specified + * @v net_proto Network-layer protocol (in host order) + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, + UINTN ll_header_len, UINTN len, VOID *data, + EFI_MAC_ADDRESS *ll_src, EFI_MAC_ADDRESS *ll_dest, + UINT16 *net_proto ) { + struct efi_snp_device *snpdev = + container_of ( snp, struct efi_snp_device, snp ); + struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol; + struct io_buffer *iobuf; + int rc; + EFI_STATUS efirc; + + DBGC2 ( snpdev, "SNPDEV %p TRANSMIT %p+%lx", snpdev, data, + ( ( unsigned long ) len ) ); + if ( ll_header_len ) { + if ( ll_src ) { + DBGC2 ( snpdev, " src %s", + ll_protocol->ntoa ( ll_src ) ); + } + if ( ll_dest ) { + DBGC2 ( snpdev, " dest %s", + ll_protocol->ntoa ( ll_dest ) ); + } + if ( net_proto ) { + DBGC2 ( snpdev, " proto %04x", *net_proto ); + } + } + DBGC2 ( snpdev, "\n" ); + + /* Sanity checks */ + if ( ll_header_len ) { + if ( ll_header_len != ll_protocol->ll_header_len ) { + DBGC ( snpdev, "SNPDEV %p TX invalid header length " + "%ld\n", snpdev, + ( ( unsigned long ) ll_header_len ) ); + efirc = EFI_INVALID_PARAMETER; + goto err_sanity; + } + if ( len < ll_header_len ) { + DBGC ( snpdev, "SNPDEV %p invalid packet length %ld\n", + snpdev, ( ( unsigned long ) len ) ); + efirc = EFI_BUFFER_TOO_SMALL; + goto err_sanity; + } + if ( ! ll_dest ) { + DBGC ( snpdev, "SNPDEV %p TX missing destination " + "address\n", snpdev ); + efirc = EFI_INVALID_PARAMETER; + goto err_sanity; + } + if ( ! net_proto ) { + DBGC ( snpdev, "SNPDEV %p TX missing network " + "protocol\n", snpdev ); + efirc = EFI_INVALID_PARAMETER; + goto err_sanity; + } + if ( ! ll_src ) + ll_src = &snpdev->mode.CurrentAddress; + } + + /* Allocate buffer */ + iobuf = alloc_iob ( len ); + if ( ! iobuf ) { + DBGC ( snpdev, "SNPDEV %p TX could not allocate %ld-byte " + "buffer\n", snpdev, ( ( unsigned long ) len ) ); + efirc = EFI_DEVICE_ERROR; + goto err_alloc_iob; + } + memcpy ( iob_put ( iobuf, len ), data, len ); + + /* Create link-layer header, if specified */ + if ( ll_header_len ) { + iob_pull ( iobuf, ll_header_len ); + if ( ( rc = ll_protocol->push ( iobuf, ll_dest, ll_src, + htons ( *net_proto ) )) != 0 ){ + DBGC ( snpdev, "SNPDEV %p TX could not construct " + "header: %s\n", snpdev, strerror ( rc ) ); + efirc = RC_TO_EFIRC ( rc ); + goto err_ll_push; + } + } + + /* Transmit packet */ + if ( ( rc = netdev_tx ( snpdev->netdev, iob_disown ( iobuf ) ) ) != 0){ + DBGC ( snpdev, "SNPDEV %p TX could not transmit: %s\n", + snpdev, strerror ( rc ) ); + efirc = RC_TO_EFIRC ( rc ); + goto err_tx; + } + + /* Record transmission as outstanding */ + snpdev->tx_count_interrupts++; + snpdev->tx_count_txbufs++; + + return 0; + + err_tx: + err_ll_push: + free_iob ( iobuf ); + err_alloc_iob: + err_sanity: + return efirc; +} + +/** + * Receive packet + * + * @v snp SNP interface + * @v ll_header_len Link-layer header length, if to be filled in + * @v len Length of data buffer + * @v data Data buffer + * @v ll_src Link-layer source address, if specified + * @v ll_dest Link-layer destination address, if specified + * @v net_proto Network-layer protocol (in host order) + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, + UINTN *ll_header_len, UINTN *len, VOID *data, + EFI_MAC_ADDRESS *ll_src, EFI_MAC_ADDRESS *ll_dest, + UINT16 *net_proto ) { + struct efi_snp_device *snpdev = + container_of ( snp, struct efi_snp_device, snp ); + struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol; + struct io_buffer *iobuf; + const void *iob_ll_dest; + const void *iob_ll_src; + uint16_t iob_net_proto; + int rc; + EFI_STATUS efirc; + + DBGC2 ( snpdev, "SNPDEV %p RECEIVE %p(+%lx)", snpdev, data, + ( ( unsigned long ) *len ) ); + + /* Poll the network device */ + efi_snp_poll ( snpdev ); + + /* Dequeue a packet, if one is available */ + iobuf = netdev_rx_dequeue ( snpdev->netdev ); + if ( ! iobuf ) { + DBGC2 ( snpdev, "\n" ); + efirc = EFI_NOT_READY; + goto out_no_packet; + } + DBGC2 ( snpdev, "+%zx\n", iob_len ( iobuf ) ); + + /* Return packet to caller */ + memcpy ( data, iobuf->data, iob_len ( iobuf ) ); + *len = iob_len ( iobuf ); + + /* Attempt to decode link-layer header */ + if ( ( rc = ll_protocol->pull ( iobuf, &iob_ll_dest, &iob_ll_src, + &iob_net_proto ) ) != 0 ) { + DBGC ( snpdev, "SNPDEV %p could not parse header: %s\n", + snpdev, strerror ( rc ) ); + efirc = RC_TO_EFIRC ( rc ); + goto out_bad_ll_header; + } + + /* Return link-layer header parameters to caller, if required */ + if ( ll_header_len ) + *ll_header_len = ll_protocol->ll_header_len; + if ( ll_src ) + memcpy ( ll_src, iob_ll_src, ll_protocol->ll_addr_len ); + if ( ll_dest ) + memcpy ( ll_dest, iob_ll_dest, ll_protocol->ll_addr_len ); + if ( net_proto ) + *net_proto = ntohs ( iob_net_proto ); + + efirc = 0; + + out_bad_ll_header: + free_iob ( iobuf ); +out_no_packet: + return efirc; +} + +/** + * Poll event + * + * @v event Event + * @v context Event context + */ +static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event, + VOID *context ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_snp_device *snpdev = context; + + DBGCP ( snpdev, "SNPDEV %p WAIT_FOR_PACKET\n", snpdev ); + + /* Do nothing unless the net device is open */ + if ( ! ( snpdev->netdev->state & NETDEV_OPEN ) ) + return; + + /* Poll the network device */ + efi_snp_poll ( snpdev ); + + /* Fire event if packets have been received */ + if ( snpdev->rx_count_events != 0 ) { + DBGC2 ( snpdev, "SNPDEV %p firing WaitForPacket event\n", + snpdev ); + bs->SignalEvent ( event ); + snpdev->rx_count_events--; + } +} + +/** SNP interface */ +static EFI_SIMPLE_NETWORK_PROTOCOL efi_snp_device_snp = { + .Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION, + .Start = efi_snp_start, + .Stop = efi_snp_stop, + .Initialize = efi_snp_initialize, + .Reset = efi_snp_reset, + .Shutdown = efi_snp_shutdown, + .ReceiveFilters = efi_snp_receive_filters, + .StationAddress = efi_snp_station_address, + .Statistics = efi_snp_statistics, + .MCastIpToMac = efi_snp_mcast_ip_to_mac, + .NvData = efi_snp_nvdata, + .GetStatus = efi_snp_get_status, + .Transmit = efi_snp_transmit, + .Receive = efi_snp_receive, +}; + +/** + * Locate net device corresponding to EFI device + * + * @v driver EFI driver + * @v device EFI device + * @ret netdev Net device, or NULL if not found + */ +static struct net_device * +efi_snp_netdev ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + EFI_PCI_IO_PROTOCOL *pci; + void *interface; + } u; + UINTN pci_segment, pci_bus, pci_dev, pci_fn; + unsigned int pci_busdevfn; + struct net_device *netdev = NULL; + EFI_STATUS efirc; + + /* See if device is a PCI device */ + if ( ( efirc = bs->OpenProtocol ( device, + &efi_pci_io_protocol_guid, + &u.interface, + driver->DriverBindingHandle, + device, + EFI_OPEN_PROTOCOL_BY_DRIVER )) !=0 ){ + DBGCP ( driver, "SNPDRV %p device %p is not a PCI device\n", + driver, device ); + goto out_no_pci_io; + } + + /* Get PCI bus:dev.fn address */ + if ( ( efirc = u.pci->GetLocation ( u.pci, &pci_segment, &pci_bus, + &pci_dev, &pci_fn ) ) != 0 ) { + DBGC ( driver, "SNPDRV %p device %p could not get PCI " + "location: %s\n", + driver, device, efi_strerror ( efirc ) ); + goto out_no_pci_location; + } + DBGCP ( driver, "SNPDRV %p device %p is PCI %04lx:%02lx:%02lx.%lx\n", + driver, device, ( ( unsigned long ) pci_segment ), + ( ( unsigned long ) pci_bus ), ( ( unsigned long ) pci_dev ), + ( ( unsigned long ) pci_fn ) ); + + /* Look up corresponding network device */ + pci_busdevfn = PCI_BUSDEVFN ( pci_bus, PCI_DEVFN ( pci_dev, pci_fn ) ); + if ( ( netdev = find_netdev_by_location ( BUS_TYPE_PCI, + pci_busdevfn ) ) == NULL ) { + DBGCP ( driver, "SNPDRV %p device %p is not a gPXE network " + "device\n", driver, device ); + goto out_no_netdev; + } + DBGC ( driver, "SNPDRV %p device %p is %s\n", + driver, device, netdev->name ); + + out_no_netdev: + out_no_pci_location: + bs->CloseProtocol ( device, &efi_pci_io_protocol_guid, + driver->DriverBindingHandle, device ); + out_no_pci_io: + return netdev; +} + +/** + * Locate SNP corresponding to EFI device + * + * @v driver EFI driver + * @v device EFI device + * @ret snp EFI SNP, or NULL if not found + */ +static struct efi_snp_device * +efi_snp_snpdev ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + EFI_SIMPLE_NETWORK_PROTOCOL *snp; + void *interface; + } u; + struct efi_snp_device *snpdev = NULL; + EFI_STATUS efirc; + + if ( ( efirc = bs->OpenProtocol ( device, + &efi_simple_network_protocol_guid, + &u.interface, + driver->DriverBindingHandle, + device, + EFI_OPEN_PROTOCOL_GET_PROTOCOL))!=0){ + DBGC ( driver, "SNPDRV %p device %p could not locate SNP: " + "%s\n", driver, device, efi_strerror ( efirc ) ); + goto err_no_snp; + } + + snpdev = container_of ( u.snp, struct efi_snp_device, snp ); + DBGCP ( driver, "SNPDRV %p device %p is SNPDEV %p\n", + driver, device, snpdev ); + + bs->CloseProtocol ( device, &efi_simple_network_protocol_guid, + driver->DriverBindingHandle, device ); + err_no_snp: + return snpdev; +} + +/** + * Check to see if driver supports a device + * + * @v driver EFI driver + * @v device EFI device + * @v child Path to child device, if any + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_snp_driver_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver, + EFI_HANDLE device, + EFI_DEVICE_PATH_PROTOCOL *child ) { + struct net_device *netdev; + + DBGCP ( driver, "SNPDRV %p DRIVER_SUPPORTED %p (%p)\n", + driver, device, child ); + + netdev = efi_snp_netdev ( driver, device ); + return ( netdev ? 0 : EFI_UNSUPPORTED ); +} + +/** + * Attach driver to device + * + * @v driver EFI driver + * @v device EFI device + * @v child Path to child device, if any + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_snp_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, + EFI_HANDLE device, + EFI_DEVICE_PATH_PROTOCOL *child ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_DEVICE_PATH_PROTOCOL *path; + EFI_DEVICE_PATH_PROTOCOL *subpath; + MAC_ADDR_DEVICE_PATH *macpath; + struct efi_snp_device *snpdev; + struct net_device *netdev; + size_t subpath_len; + size_t path_prefix_len = 0; + unsigned int i; + EFI_STATUS efirc; + + DBGCP ( driver, "SNPDRV %p DRIVER_START %p (%p)\n", + driver, device, child ); + + /* Determine device path prefix length */ + if ( ( efirc = bs->OpenProtocol ( device, + &efi_device_path_protocol_guid, + ( void * ) &path, + driver->DriverBindingHandle, + device, + EFI_OPEN_PROTOCOL_BY_DRIVER )) !=0 ){ + DBGCP ( driver, "SNPDRV %p device %p has no device path\n", + driver, device ); + goto err_no_device_path; + } + subpath = path; + while ( subpath->Type != END_DEVICE_PATH_TYPE ) { + subpath_len = ( ( subpath->Length[1] << 8 ) | + subpath->Length[0] ); + path_prefix_len += subpath_len; + subpath = ( ( ( void * ) subpath ) + subpath_len ); + } + + /* Allocate the SNP device */ + snpdev = zalloc ( sizeof ( *snpdev ) + path_prefix_len + + sizeof ( *macpath ) ); + if ( ! snpdev ) { + efirc = EFI_OUT_OF_RESOURCES; + goto err_alloc_snp; + } + + /* Identify the net device */ + netdev = efi_snp_netdev ( driver, device ); + if ( ! netdev ) { + DBGC ( snpdev, "SNPDEV %p cannot find netdev for device %p\n", + snpdev, device ); + efirc = EFI_UNSUPPORTED; + goto err_no_netdev; + } + snpdev->netdev = netdev_get ( netdev ); + + /* Sanity check */ + if ( netdev->ll_protocol->ll_addr_len > sizeof ( EFI_MAC_ADDRESS ) ) { + DBGC ( snpdev, "SNPDEV %p cannot support link-layer address " + "length %d for %s\n", snpdev, + netdev->ll_protocol->ll_addr_len, netdev->name ); + efirc = EFI_INVALID_PARAMETER; + goto err_ll_addr_len; + } + + /* Populate the SNP structure */ + memcpy ( &snpdev->snp, &efi_snp_device_snp, sizeof ( snpdev->snp ) ); + snpdev->snp.Mode = &snpdev->mode; + if ( ( efirc = bs->CreateEvent ( EVT_NOTIFY_WAIT, TPL_NOTIFY, + efi_snp_wait_for_packet, snpdev, + &snpdev->snp.WaitForPacket ) ) != 0 ){ + DBGC ( snpdev, "SNPDEV %p could not create event: %s\n", + snpdev, efi_strerror ( efirc ) ); + goto err_create_event; + } + + /* Populate the SNP mode structure */ + snpdev->mode.State = EfiSimpleNetworkStopped; + efi_snp_set_mode ( snpdev ); + + /* Populate the NII structure */ + snpdev->nii.Revision = + EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION; + strncpy ( snpdev->nii.StringId, "gPXE", + sizeof ( snpdev->nii.StringId ) ); + + /* Populate the device name */ + for ( i = 0 ; i < sizeof ( netdev->name ) ; i++ ) { + /* Damn Unicode names */ + assert ( i < ( sizeof ( snpdev->name ) / + sizeof ( snpdev->name[0] ) ) ); + snpdev->name[i] = netdev->name[i]; + } + + /* Populate the device path */ + memcpy ( &snpdev->path, path, path_prefix_len ); + macpath = ( ( ( void * ) &snpdev->path ) + path_prefix_len ); + subpath = ( ( void * ) ( macpath + 1 ) ); + memset ( macpath, 0, sizeof ( *macpath ) ); + macpath->Header.Type = MESSAGING_DEVICE_PATH; + macpath->Header.SubType = MSG_MAC_ADDR_DP; + macpath->Header.Length[0] = sizeof ( *macpath ); + memcpy ( &macpath->MacAddress, netdev->ll_addr, + sizeof ( macpath->MacAddress ) ); + macpath->IfType = ntohs ( netdev->ll_protocol->ll_proto ); + memset ( subpath, 0, sizeof ( *subpath ) ); + subpath->Type = END_DEVICE_PATH_TYPE; + subpath->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + subpath->Length[0] = sizeof ( *subpath ); + + /* Install the SNP */ + if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( + &snpdev->handle, + &efi_simple_network_protocol_guid, &snpdev->snp, + &efi_device_path_protocol_guid, &snpdev->path, + &efi_nii_protocol_guid, &snpdev->nii, + &efi_nii31_protocol_guid, &snpdev->nii, + NULL ) ) != 0 ) { + DBGC ( snpdev, "SNPDEV %p could not install protocols: " + "%s\n", snpdev, efi_strerror ( efirc ) ); + goto err_install_protocol_interface; + } + + DBGC ( snpdev, "SNPDEV %p installed for %s as device %p\n", + snpdev, netdev->name, snpdev->handle ); + return 0; + + bs->UninstallMultipleProtocolInterfaces ( + snpdev->handle, + &efi_simple_network_protocol_guid, &snpdev->snp, + &efi_device_path_protocol_guid, &snpdev->path, + &efi_nii_protocol_guid, &snpdev->nii, + &efi_nii31_protocol_guid, &snpdev->nii, + NULL ); + err_install_protocol_interface: + bs->CloseEvent ( snpdev->snp.WaitForPacket ); + err_create_event: + err_ll_addr_len: + netdev_put ( netdev ); + err_no_netdev: + free ( snpdev ); + err_alloc_snp: + bs->CloseProtocol ( device, &efi_device_path_protocol_guid, + driver->DriverBindingHandle, device ); + err_no_device_path: + return efirc; +} + +/** + * Detach driver from device + * + * @v driver EFI driver + * @v device EFI device + * @v num_children Number of child devices + * @v children List of child devices + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_snp_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver, + EFI_HANDLE device, + UINTN num_children, + EFI_HANDLE *children ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_snp_device *snpdev; + + DBGCP ( driver, "SNPDRV %p DRIVER_STOP %p (%ld %p)\n", + driver, device, ( ( unsigned long ) num_children ), children ); + + /* Locate SNP device */ + snpdev = efi_snp_snpdev ( driver, device ); + if ( ! snpdev ) { + DBGC ( driver, "SNPDRV %p device %p could not find SNPDEV\n", + driver, device ); + return EFI_DEVICE_ERROR; + } + + /* Uninstall the SNP */ + bs->UninstallMultipleProtocolInterfaces ( + snpdev->handle, + &efi_simple_network_protocol_guid, &snpdev->snp, + &efi_device_path_protocol_guid, &snpdev->path, + &efi_nii_protocol_guid, &snpdev->nii, + &efi_nii31_protocol_guid, &snpdev->nii, + NULL ); + bs->CloseEvent ( snpdev->snp.WaitForPacket ); + netdev_put ( snpdev->netdev ); + free ( snpdev ); + bs->CloseProtocol ( device, &efi_device_path_protocol_guid, + driver->DriverBindingHandle, device ); + return 0; +} + +/** EFI SNP driver binding */ +static EFI_DRIVER_BINDING_PROTOCOL efi_snp_binding = { + efi_snp_driver_supported, + efi_snp_driver_start, + efi_snp_driver_stop, + 0x10, + NULL, + NULL +}; + +/** + * Look up driver name + * + * @v wtf Component name protocol + * @v language Language to use + * @v driver_name Driver name to fill in + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_snp_get_driver_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused, + CHAR8 *language __unused, CHAR16 **driver_name ) { + + *driver_name = L"" PRODUCT_SHORT_NAME " Driver"; + return 0; +} + +/** + * Look up controller name + * + * @v wtf Component name protocol + * @v device Device + * @v child Child device, or NULL + * @v language Language to use + * @v driver_name Device name to fill in + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_snp_get_controller_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused, + EFI_HANDLE device __unused, + EFI_HANDLE child __unused, + CHAR8 *language __unused, + CHAR16 **controller_name __unused ) { + + /* Just let EFI use the default Device Path Name */ + return EFI_UNSUPPORTED; +} + +/** EFI SNP component name protocol */ +static EFI_COMPONENT_NAME2_PROTOCOL efi_snp_name = { + efi_snp_get_driver_name, + efi_snp_get_controller_name, + "en" +}; + +/** + * Install EFI SNP driver + * + * @ret rc Return status code + */ +int efi_snp_install ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_DRIVER_BINDING_PROTOCOL *driver = &efi_snp_binding; + EFI_STATUS efirc; + + driver->ImageHandle = efi_image_handle; + if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( + &driver->DriverBindingHandle, + &efi_driver_binding_protocol_guid, driver, + &efi_component_name2_protocol_guid, &efi_snp_name, + NULL ) ) != 0 ) { + DBGC ( driver, "SNPDRV %p could not install protocols: " + "%s\n", driver, efi_strerror ( efirc ) ); + return EFIRC_TO_RC ( efirc ); + } + + DBGC ( driver, "SNPDRV %p driver binding installed as %p\n", + driver, driver->DriverBindingHandle ); + return 0; +} diff --git a/gpxe/src/interface/efi/efi_strerror.c b/gpxe/src/interface/efi/efi_strerror.c new file mode 100644 index 0000000..adfeaed --- /dev/null +++ b/gpxe/src/interface/efi/efi_strerror.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include + +/** @file + * + * gPXE error message formatting for EFI + * + */ + +/** + * Format EFI status code + * + * @v efirc EFI status code + * @v efi_strerror EFI status code string + */ +const char * efi_strerror ( EFI_STATUS efirc ) { + static char errbuf[32]; + + if ( ! efirc ) + return "No error"; + + snprintf ( errbuf, sizeof ( errbuf ), "Error %lld", + ( unsigned long long ) ( efirc ^ MAX_BIT ) ); + return errbuf; +} diff --git a/gpxe/src/interface/efi/efi_timer.c b/gpxe/src/interface/efi/efi_timer.c new file mode 100644 index 0000000..d1ba43a --- /dev/null +++ b/gpxe/src/interface/efi/efi_timer.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include +#include +#include +#include + +/** @file + * + * gPXE timer API for EFI + * + */ + +/** Scale factor to apply to CPU timer 0 + * + * The timer is scaled down in order to ensure that reasonable values + * for "number of ticks" don't exceed the size of an unsigned long. + */ +#define EFI_TIMER0_SHIFT 12 + +/** Calibration time */ +#define EFI_CALIBRATE_DELAY_MS 1 + +/** CPU protocol */ +static EFI_CPU_ARCH_PROTOCOL *cpu_arch; +EFI_REQUIRE_PROTOCOL ( EFI_CPU_ARCH_PROTOCOL, &cpu_arch ); + +/** + * Delay for a fixed number of microseconds + * + * @v usecs Number of microseconds for which to delay + */ +static void efi_udelay ( unsigned long usecs ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + + if ( ( efirc = bs->Stall ( usecs ) ) != 0 ) { + DBG ( "EFI could not delay for %ldus: %s\n", + usecs, efi_strerror ( efirc ) ); + /* Probably screwed */ + } +} + +/** + * Get current system time in ticks + * + * @ret ticks Current time, in ticks + */ +static unsigned long efi_currticks ( void ) { + UINT64 time; + EFI_STATUS efirc; + + /* Read CPU timer 0 (TSC) */ + if ( ( efirc = cpu_arch->GetTimerValue ( cpu_arch, 0, &time, + NULL ) ) != 0 ) { + DBG ( "EFI could not read CPU timer: %s\n", + efi_strerror ( efirc ) ); + /* Probably screwed */ + return -1UL; + } + + return ( time >> EFI_TIMER0_SHIFT ); +} + +/** + * Get number of ticks per second + * + * @ret ticks_per_sec Number of ticks per second + */ +static unsigned long efi_ticks_per_sec ( void ) { + static unsigned long ticks_per_sec = 0; + + /* Calibrate timer, if necessary. EFI does nominally provide + * the timer speed via the (optional) TimerPeriod parameter to + * the GetTimerValue() call, but it gets the speed slightly + * wrong. By up to three orders of magnitude. Not helpful. + */ + if ( ! ticks_per_sec ) { + unsigned long start; + unsigned long elapsed; + + DBG ( "Calibrating EFI timer with a %d ms delay\n", + EFI_CALIBRATE_DELAY_MS ); + start = currticks(); + mdelay ( EFI_CALIBRATE_DELAY_MS ); + elapsed = ( currticks() - start ); + ticks_per_sec = ( elapsed * ( 1000 / EFI_CALIBRATE_DELAY_MS )); + DBG ( "EFI CPU timer calibrated at %ld ticks in %d ms (%ld " + "ticks/sec)\n", elapsed, EFI_CALIBRATE_DELAY_MS, + ticks_per_sec ); + } + + return ticks_per_sec; +} + +PROVIDE_TIMER ( efi, udelay, efi_udelay ); +PROVIDE_TIMER ( efi, currticks, efi_currticks ); +PROVIDE_TIMER ( efi, ticks_per_sec, efi_ticks_per_sec ); diff --git a/gpxe/src/interface/efi/efi_uaccess.c b/gpxe/src/interface/efi/efi_uaccess.c new file mode 100644 index 0000000..1c54c03 --- /dev/null +++ b/gpxe/src/interface/efi/efi_uaccess.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include + +/** @file + * + * gPXE user access API for EFI + * + */ + +PROVIDE_UACCESS_INLINE ( efi, phys_to_user ); +PROVIDE_UACCESS_INLINE ( efi, user_to_phys ); +PROVIDE_UACCESS_INLINE ( efi, virt_to_user ); +PROVIDE_UACCESS_INLINE ( efi, user_to_virt ); +PROVIDE_UACCESS_INLINE ( efi, userptr_add ); +PROVIDE_UACCESS_INLINE ( efi, memcpy_user ); +PROVIDE_UACCESS_INLINE ( efi, memmove_user ); +PROVIDE_UACCESS_INLINE ( efi, memset_user ); +PROVIDE_UACCESS_INLINE ( efi, strlen_user ); +PROVIDE_UACCESS_INLINE ( efi, memchr_user ); diff --git a/gpxe/src/interface/efi/efi_umalloc.c b/gpxe/src/interface/efi/efi_umalloc.c new file mode 100644 index 0000000..4de3789 --- /dev/null +++ b/gpxe/src/interface/efi/efi_umalloc.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include + +/** @file + * + * gPXE user memory allocation API for EFI + * + */ + +/** Equivalent of NOWHERE for user pointers */ +#define UNOWHERE ( ~UNULL ) + +/** + * Reallocate external memory + * + * @v old_ptr Memory previously allocated by umalloc(), or UNULL + * @v new_size Requested size + * @ret new_ptr Allocated memory, or UNULL + * + * Calling realloc() with a new size of zero is a valid way to free a + * memory block. + */ +static userptr_t efi_urealloc ( userptr_t old_ptr, size_t new_size ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_PHYSICAL_ADDRESS phys_addr; + unsigned int new_pages, old_pages; + userptr_t new_ptr = UNOWHERE; + size_t old_size; + EFI_STATUS efirc; + + /* Allocate new memory if necessary. If allocation fails, + * return without touching the old block. + */ + if ( new_size ) { + new_pages = ( EFI_SIZE_TO_PAGES ( new_size ) + 1 ); + if ( ( efirc = bs->AllocatePages ( AllocateAnyPages, + EfiBootServicesData, + new_pages, + &phys_addr ) ) != 0 ) { + DBG ( "EFI could not allocate %d pages: %s\n", + new_pages, efi_strerror ( efirc ) ); + return UNULL; + } + assert ( phys_addr != 0 ); + new_ptr = phys_to_user ( phys_addr + EFI_PAGE_SIZE ); + copy_to_user ( new_ptr, -EFI_PAGE_SIZE, + &new_size, sizeof ( new_size ) ); + DBG ( "EFI allocated %d pages at %llx\n", + new_pages, phys_addr ); + } + + /* Copy across relevant part of the old data region (if any), + * then free it. Note that at this point either (a) new_ptr + * is valid, or (b) new_size is 0; either way, the memcpy() is + * valid. + */ + if ( old_ptr && ( old_ptr != UNOWHERE ) ) { + copy_from_user ( &old_size, old_ptr, -EFI_PAGE_SIZE, + sizeof ( old_size ) ); + memcpy_user ( new_ptr, 0, old_ptr, 0, + ( (old_size < new_size) ? old_size : new_size )); + old_pages = ( EFI_SIZE_TO_PAGES ( old_size ) + 1 ); + phys_addr = user_to_phys ( old_ptr, -EFI_PAGE_SIZE ); + if ( ( efirc = bs->FreePages ( phys_addr, old_pages ) ) != 0 ){ + DBG ( "EFI could not free %d pages at %llx: %s\n", + old_pages, phys_addr, efi_strerror ( efirc ) ); + /* Not fatal; we have leaked memory but successfully + * allocated (if asked to do so). + */ + } + DBG ( "EFI freed %d pages at %llx\n", old_pages, phys_addr ); + } + + return new_ptr; +} + +PROVIDE_UMALLOC ( efi, urealloc, efi_urealloc ); diff --git a/gpxe/src/interface/smbios/smbios.c b/gpxe/src/interface/smbios/smbios.c new file mode 100644 index 0000000..8207c1f --- /dev/null +++ b/gpxe/src/interface/smbios/smbios.c @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include +#include + +/** @file + * + * System Management BIOS + * + */ + +/** SMBIOS entry point descriptor */ +static struct smbios smbios = { + .address = UNULL, +}; + +/** + * Find SMBIOS strings terminator + * + * @v offset Offset to start of strings + * @ret offset Offset to strings terminator, or 0 if not found + */ +static size_t find_strings_terminator ( size_t offset ) { + size_t max_offset = ( smbios.len - 2 ); + uint16_t nulnul; + + for ( ; offset <= max_offset ; offset++ ) { + copy_from_user ( &nulnul, smbios.address, offset, 2 ); + if ( nulnul == 0 ) + return ( offset + 1 ); + } + return 0; +} + +/** + * Find specific structure type within SMBIOS + * + * @v type Structure type to search for + * @v structure SMBIOS structure descriptor to fill in + * @ret rc Return status code + */ +int find_smbios_structure ( unsigned int type, + struct smbios_structure *structure ) { + unsigned int count = 0; + size_t offset = 0; + size_t strings_offset; + size_t terminator_offset; + int rc; + + /* Find SMBIOS */ + if ( ( smbios.address == UNULL ) && + ( ( rc = find_smbios ( &smbios ) ) != 0 ) ) + return rc; + assert ( smbios.address != UNULL ); + + /* Scan through list of structures */ + while ( ( ( offset + sizeof ( structure->header ) ) < smbios.len ) + && ( count < smbios.count ) ) { + + /* Read next SMBIOS structure header */ + copy_from_user ( &structure->header, smbios.address, offset, + sizeof ( structure->header ) ); + + /* Determine start and extent of strings block */ + strings_offset = ( offset + structure->header.len ); + if ( strings_offset > smbios.len ) { + DBG ( "SMBIOS structure at offset %zx with length " + "%x extends beyond SMBIOS\n", offset, + structure->header.len ); + return -ENOENT; + } + terminator_offset = find_strings_terminator ( strings_offset ); + if ( ! terminator_offset ) { + DBG ( "SMBIOS structure at offset %zx has " + "unterminated strings section\n", offset ); + return -ENOENT; + } + structure->strings_len = ( terminator_offset - strings_offset); + + DBG ( "SMBIOS structure at offset %zx has type %d, length %x, " + "strings length %zx\n", offset, structure->header.type, + structure->header.len, structure->strings_len ); + + /* If this is the structure we want, return */ + if ( structure->header.type == type ) { + structure->offset = offset; + return 0; + } + + /* Move to next SMBIOS structure */ + offset = ( terminator_offset + 1 ); + count++; + } + + DBG ( "SMBIOS structure type %d not found\n", type ); + return -ENOENT; +} + +/** + * Copy SMBIOS structure + * + * @v structure SMBIOS structure descriptor + * @v data Buffer to hold SMBIOS structure + * @v len Length of buffer + * @ret rc Return status code + */ +int read_smbios_structure ( struct smbios_structure *structure, + void *data, size_t len ) { + + assert ( smbios.address != UNULL ); + + if ( len > structure->header.len ) + len = structure->header.len; + copy_from_user ( data, smbios.address, structure->offset, len ); + return 0; +} + +/** + * Find indexed string within SMBIOS structure + * + * @v structure SMBIOS structure descriptor + * @v index String index + * @v data Buffer for string + * @v len Length of string buffer + * @ret rc Length of string, or negative error + */ +int read_smbios_string ( struct smbios_structure *structure, + unsigned int index, void *data, size_t len ) { + size_t strings_start = ( structure->offset + structure->header.len ); + size_t strings_end = ( strings_start + structure->strings_len ); + size_t offset; + size_t string_len; + + assert ( smbios.address != UNULL ); + + /* String numbers start at 1 (0 is used to indicate "no string") */ + if ( ! index ) + return -ENOENT; + + for ( offset = strings_start ; offset < strings_end ; + offset += ( string_len + 1 ) ) { + /* Get string length. This is known safe, since the + * smbios_strings struct is constructed so as to + * always end on a string boundary. + */ + string_len = strlen_user ( smbios.address, offset ); + if ( --index == 0 ) { + /* Copy string, truncating as necessary. */ + if ( len > string_len ) + len = string_len; + copy_from_user ( data, smbios.address, offset, len ); + return string_len; + } + } + + DBG ( "SMBIOS string index %d not found\n", index ); + return -ENOENT; +} diff --git a/gpxe/src/interface/smbios/smbios_settings.c b/gpxe/src/interface/smbios/smbios_settings.c new file mode 100644 index 0000000..61c2d91 --- /dev/null +++ b/gpxe/src/interface/smbios/smbios_settings.c @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +/** SMBIOS settings tag magic number */ +#define SMBIOS_TAG_MAGIC 0x5B /* "SmBios" */ + +/** + * Construct SMBIOS empty tag + * + * @ret tag SMBIOS setting tag + */ +#define SMBIOS_EMPTY_TAG ( SMBIOS_TAG_MAGIC << 24 ) + +/** + * Construct SMBIOS raw-data tag + * + * @v _type SMBIOS structure type number + * @v _structure SMBIOS structure data type + * @v _field Field within SMBIOS structure data type + * @ret tag SMBIOS setting tag + */ +#define SMBIOS_RAW_TAG( _type, _structure, _field ) \ + ( ( SMBIOS_TAG_MAGIC << 24 ) | \ + ( (_type) << 16 ) | \ + ( offsetof ( _structure, _field ) << 8 ) | \ + ( sizeof ( ( ( _structure * ) 0 )->_field ) ) ) + +/** + * Construct SMBIOS string tag + * + * @v _type SMBIOS structure type number + * @v _structure SMBIOS structure data type + * @v _field Field within SMBIOS structure data type + * @ret tag SMBIOS setting tag + */ +#define SMBIOS_STRING_TAG( _type, _structure, _field ) \ + ( ( SMBIOS_TAG_MAGIC << 24 ) | \ + ( (_type) << 16 ) | \ + ( offsetof ( _structure, _field ) << 8 ) ) + +/** + * Store value of SMBIOS setting + * + * @v settings Settings block + * @v setting Setting to store + * @v data Setting data, or NULL to clear setting + * @v len Length of setting data + * @ret rc Return status code + */ +static int smbios_store ( struct settings *settings __unused, + struct setting *setting __unused, + const void *data __unused, size_t len __unused ) { + /* Cannot write data into SMBIOS */ + return -ENOTSUP; +} + +/** + * Fetch value of SMBIOS setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int smbios_fetch ( struct settings *settings __unused, + struct setting *setting, + void *data, size_t len ) { + struct smbios_structure structure; + unsigned int tag_magic; + unsigned int tag_type; + unsigned int tag_offset; + unsigned int tag_len; + int rc; + + /* Split tag into type, offset and length */ + tag_magic = ( setting->tag >> 24 ); + tag_type = ( ( setting->tag >> 16 ) & 0xff ); + tag_offset = ( ( setting->tag >> 8 ) & 0xff ); + tag_len = ( setting->tag & 0xff ); + if ( tag_magic != SMBIOS_TAG_MAGIC ) + return -ENOENT; + + /* Find SMBIOS structure */ + if ( ( rc = find_smbios_structure ( tag_type, &structure ) ) != 0 ) + return rc; + + { + uint8_t buf[structure.header.len]; + + /* Read SMBIOS structure */ + if ( ( rc = read_smbios_structure ( &structure, buf, + sizeof ( buf ) ) ) != 0 ) + return rc; + + if ( tag_len == 0 ) { + /* String */ + return read_smbios_string ( &structure, + buf[tag_offset], + data, len ); + } else { + /* Raw data */ + if ( len > tag_len ) + len = tag_len; + memcpy ( data, &buf[tag_offset], len ); + return tag_len; + } + } +} + +/** SMBIOS settings operations */ +static struct settings_operations smbios_settings_operations = { + .store = smbios_store, + .fetch = smbios_fetch, +}; + +/** SMBIOS settings */ +static struct settings smbios_settings = { + .refcnt = NULL, + .name = "smbios", + .tag_magic = SMBIOS_EMPTY_TAG, + .siblings = LIST_HEAD_INIT ( smbios_settings.siblings ), + .children = LIST_HEAD_INIT ( smbios_settings.children ), + .op = &smbios_settings_operations, +}; + +/** Initialise SMBIOS settings */ +static void smbios_init ( void ) { + int rc; + + if ( ( rc = register_settings ( &smbios_settings, NULL ) ) != 0 ) { + DBG ( "SMBIOS could not register settings: %s\n", + strerror ( rc ) ); + return; + } +} + +/** SMBIOS settings initialiser */ +struct init_fn smbios_init_fn __init_fn ( INIT_NORMAL ) = { + .initialise = smbios_init, +}; + +/** UUID setting obtained via SMBIOS */ +struct setting uuid_setting __setting = { + .name = "uuid", + .description = "UUID", + .tag = SMBIOS_RAW_TAG ( SMBIOS_TYPE_SYSTEM_INFORMATION, + struct smbios_system_information, uuid ), + .type = &setting_type_uuid, +}; + +/** Other SMBIOS named settings */ +struct setting smbios_named_settings[] __setting = { + { + .name = "manufacturer", + .description = "Manufacturer", + .tag = SMBIOS_STRING_TAG ( SMBIOS_TYPE_SYSTEM_INFORMATION, + struct smbios_system_information, + manufacturer ), + .type = &setting_type_string, + }, + { + .name = "product", + .description = "Product name", + .tag = SMBIOS_STRING_TAG ( SMBIOS_TYPE_SYSTEM_INFORMATION, + struct smbios_system_information, + product ), + .type = &setting_type_string, + }, + { + .name = "serial", + .description = "Serial number", + .tag = SMBIOS_STRING_TAG ( SMBIOS_TYPE_SYSTEM_INFORMATION, + struct smbios_system_information, + serial ), + .type = &setting_type_string, + }, +}; diff --git a/gpxe/src/libgcc/__divdi3.c b/gpxe/src/libgcc/__divdi3.c new file mode 100644 index 0000000..7097b11 --- /dev/null +++ b/gpxe/src/libgcc/__divdi3.c @@ -0,0 +1,26 @@ +/* + * arch/i386/libgcc/__divdi3.c + */ + +#include "libgcc.h" + +__libgcc int64_t __divdi3(int64_t num, int64_t den) +{ + int minus = 0; + int64_t v; + + if ( num < 0 ) { + num = -num; + minus = 1; + } + if ( den < 0 ) { + den = -den; + minus ^= 1; + } + + v = __udivmoddi4(num, den, NULL); + if ( minus ) + v = -v; + + return v; +} diff --git a/gpxe/src/libgcc/__moddi3.c b/gpxe/src/libgcc/__moddi3.c new file mode 100644 index 0000000..d671bbc --- /dev/null +++ b/gpxe/src/libgcc/__moddi3.c @@ -0,0 +1,26 @@ +/* + * arch/i386/libgcc/__moddi3.c + */ + +#include "libgcc.h" + +__libgcc int64_t __moddi3(int64_t num, int64_t den) +{ + int minus = 0; + int64_t v; + + if ( num < 0 ) { + num = -num; + minus = 1; + } + if ( den < 0 ) { + den = -den; + minus ^= 1; + } + + (void) __udivmoddi4(num, den, (uint64_t *)&v); + if ( minus ) + v = -v; + + return v; +} diff --git a/gpxe/src/libgcc/__udivdi3.c b/gpxe/src/libgcc/__udivdi3.c new file mode 100644 index 0000000..f5a14de --- /dev/null +++ b/gpxe/src/libgcc/__udivdi3.c @@ -0,0 +1,10 @@ +/* + * arch/i386/libgcc/__divdi3.c + */ + +#include "libgcc.h" + +__libgcc uint64_t __udivdi3(uint64_t num, uint64_t den) +{ + return __udivmoddi4(num, den, NULL); +} diff --git a/gpxe/src/libgcc/__udivmoddi4.c b/gpxe/src/libgcc/__udivmoddi4.c new file mode 100644 index 0000000..21e0d51 --- /dev/null +++ b/gpxe/src/libgcc/__udivmoddi4.c @@ -0,0 +1,32 @@ +#include "libgcc.h" + +__libgcc uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t *rem_p) +{ + uint64_t quot = 0, qbit = 1; + + if ( den == 0 ) { + return 1/((unsigned)den); /* Intentional divide by zero, without + triggering a compiler warning which + would abort the build */ + } + + /* Left-justify denominator and count shift */ + while ( (int64_t)den >= 0 ) { + den <<= 1; + qbit <<= 1; + } + + while ( qbit ) { + if ( den <= num ) { + num -= den; + quot += qbit; + } + den >>= 1; + qbit >>= 1; + } + + if ( rem_p ) + *rem_p = num; + + return quot; +} diff --git a/gpxe/src/libgcc/__umoddi3.c b/gpxe/src/libgcc/__umoddi3.c new file mode 100644 index 0000000..fb4da99 --- /dev/null +++ b/gpxe/src/libgcc/__umoddi3.c @@ -0,0 +1,13 @@ +/* + * arch/i386/libgcc/__umoddi3.c + */ + +#include "libgcc.h" + +__libgcc uint64_t __umoddi3(uint64_t num, uint64_t den) +{ + uint64_t v; + + (void) __udivmoddi4(num, den, &v); + return v; +} diff --git a/gpxe/src/libgcc/libgcc.h b/gpxe/src/libgcc/libgcc.h new file mode 100644 index 0000000..d3e9bdd --- /dev/null +++ b/gpxe/src/libgcc/libgcc.h @@ -0,0 +1,14 @@ +#ifndef _LIBGCC_H +#define _LIBGCC_H + +#include +#include + +extern __libgcc uint64_t __udivmoddi4 ( uint64_t num, uint64_t den, + uint64_t *rem ); +extern __libgcc uint64_t __udivdi3 (uint64_t num, uint64_t den ); +extern __libgcc uint64_t __umoddi3 ( uint64_t num, uint64_t den ); +extern __libgcc int64_t __divdi3 ( int64_t num, int64_t den ); +extern __libgcc int64_t __moddi3 ( int64_t num, int64_t den ); + +#endif /* _LIBGCC_H */ diff --git a/gpxe/src/libgcc/memcpy.c b/gpxe/src/libgcc/memcpy.c new file mode 100644 index 0000000..e98b783 --- /dev/null +++ b/gpxe/src/libgcc/memcpy.c @@ -0,0 +1,18 @@ +/** @file + * + * gcc sometimes likes to insert implicit calls to memcpy(). + * Unfortunately, there doesn't seem to be any way to prevent it from + * doing this, or to force it to use the optimised memcpy() as seen by + * C code; it insists on inserting a symbol reference to "memcpy". We + * therefore include wrapper functions just to keep gcc happy. + * + */ + +#include + +void * gcc_implicit_memcpy ( void *dest, const void *src, + size_t len ) asm ( "memcpy" ); + +void * gcc_implicit_memcpy ( void *dest, const void *src, size_t len ) { + return memcpy ( dest, src, len ); +} diff --git a/gpxe/src/net/aoe.c b/gpxe/src/net/aoe.c new file mode 100644 index 0000000..08887fe --- /dev/null +++ b/gpxe/src/net/aoe.c @@ -0,0 +1,476 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * AoE protocol + * + */ + +FEATURE ( FEATURE_PROTOCOL, "AoE", DHCP_EB_FEATURE_AOE, 1 ); + +struct net_protocol aoe_protocol; + +/** List of all AoE sessions */ +static LIST_HEAD ( aoe_sessions ); + +static void aoe_free ( struct refcnt *refcnt ) { + struct aoe_session *aoe = + container_of ( refcnt, struct aoe_session, refcnt ); + + netdev_put ( aoe->netdev ); + free ( aoe ); +} + +/** + * Mark current AoE command complete + * + * @v aoe AoE session + * @v rc Return status code + */ +static void aoe_done ( struct aoe_session *aoe, int rc ) { + + /* Record overall command status */ + if ( aoe->command ) { + aoe->command->cb.cmd_stat = aoe->status; + aoe->command = NULL; + } + + /* Stop retransmission timer */ + stop_timer ( &aoe->timer ); + + /* Mark operation as complete */ + aoe->rc = rc; +} + +/** + * Send AoE command + * + * @v aoe AoE session + * @ret rc Return status code + * + * This transmits an AoE command packet. It does not wait for a + * response. + */ +static int aoe_send_command ( struct aoe_session *aoe ) { + struct ata_command *command = aoe->command; + struct io_buffer *iobuf; + struct aoehdr *aoehdr; + union aoecmd *aoecmd; + struct aoeata *aoeata; + unsigned int count; + unsigned int data_out_len; + unsigned int aoecmdlen; + + /* Fail immediately if we have no netdev to send on */ + if ( ! aoe->netdev ) { + aoe_done ( aoe, -ENETUNREACH ); + return -ENETUNREACH; + } + + /* If we are transmitting anything that requires a response, + * start the retransmission timer. Do this before attempting + * to allocate the I/O buffer, in case allocation itself + * fails. + */ + start_timer ( &aoe->timer ); + + /* Calculate count and data_out_len for this subcommand */ + switch ( aoe->aoe_cmd_type ) { + case AOE_CMD_ATA: + count = command->cb.count.native; + if ( count > AOE_MAX_COUNT ) + count = AOE_MAX_COUNT; + data_out_len = ( command->data_out ? + ( count * ATA_SECTOR_SIZE ) : 0 ); + aoecmdlen = sizeof ( aoecmd->ata ); + break; + case AOE_CMD_CONFIG: + count = 0; + data_out_len = 0; + aoecmdlen = sizeof ( aoecmd->cfg ); + break; + default: + return -ENOTSUP; + } + + /* Create outgoing I/O buffer */ + iobuf = alloc_iob ( ETH_HLEN + sizeof ( *aoehdr ) + + aoecmdlen + data_out_len ); + + if ( ! iobuf ) + return -ENOMEM; + iob_reserve ( iobuf, ETH_HLEN ); + aoehdr = iob_put ( iobuf, sizeof ( *aoehdr ) ); + aoecmd = iob_put ( iobuf, aoecmdlen ); + memset ( aoehdr, 0, ( sizeof ( *aoehdr ) + aoecmdlen ) ); + + /* Fill AoE header */ + aoehdr->ver_flags = AOE_VERSION; + aoehdr->major = htons ( aoe->major ); + aoehdr->minor = aoe->minor; + aoehdr->command = aoe->aoe_cmd_type; + aoehdr->tag = htonl ( ++aoe->tag ); + + /* Fill AoE payload */ + switch ( aoe->aoe_cmd_type ) { + case AOE_CMD_ATA: + /* Fill AoE command */ + aoeata = &aoecmd->ata; + linker_assert ( AOE_FL_DEV_HEAD == ATA_DEV_SLAVE, + __fix_ata_h__ ); + aoeata->aflags = ( ( command->cb.lba48 ? AOE_FL_EXTENDED : 0 )| + ( command->cb.device & ATA_DEV_SLAVE ) | + ( data_out_len ? AOE_FL_WRITE : 0 ) ); + aoeata->err_feat = command->cb.err_feat.bytes.cur; + aoeata->count = count; + aoeata->cmd_stat = command->cb.cmd_stat; + aoeata->lba.u64 = cpu_to_le64 ( command->cb.lba.native ); + if ( ! command->cb.lba48 ) + aoeata->lba.bytes[3] |= + ( command->cb.device & ATA_DEV_MASK ); + + /* Fill data payload */ + copy_from_user ( iob_put ( iobuf, data_out_len ), + command->data_out, aoe->command_offset, + data_out_len ); + break; + case AOE_CMD_CONFIG: + /* Nothing to do */ + break; + default: + assert ( 0 ); + } + + /* Send packet */ + return net_tx ( iobuf, aoe->netdev, &aoe_protocol, aoe->target ); +} + +/** + * Handle AoE retry timer expiry + * + * @v timer AoE retry timer + * @v fail Failure indicator + */ +static void aoe_timer_expired ( struct retry_timer *timer, int fail ) { + struct aoe_session *aoe = + container_of ( timer, struct aoe_session, timer ); + + if ( fail ) { + aoe_done ( aoe, -ETIMEDOUT ); + } else { + aoe_send_command ( aoe ); + } +} + +/** + * Handle AoE configuration command response + * + * @v aoe AoE session + * @v ll_source Link-layer source address + * @ret rc Return status code + */ +static int aoe_rx_cfg ( struct aoe_session *aoe, const void *ll_source ) { + + /* Record target MAC address */ + memcpy ( aoe->target, ll_source, sizeof ( aoe->target ) ); + DBGC ( aoe, "AoE %p target MAC address %s\n", + aoe, eth_ntoa ( aoe->target ) ); + + /* Mark config request as complete */ + aoe_done ( aoe, 0 ); + + return 0; +} + +/** + * Handle AoE ATA command response + * + * @v aoe AoE session + * @v aoeata AoE ATA command + * @v len Length of AoE ATA command + * @ret rc Return status code + */ +static int aoe_rx_ata ( struct aoe_session *aoe, struct aoeata *aoeata, + size_t len ) { + struct ata_command *command = aoe->command; + unsigned int rx_data_len; + unsigned int count; + unsigned int data_len; + + /* Sanity check */ + if ( len < sizeof ( *aoeata ) ) { + /* Ignore packet; allow timer to trigger retransmit */ + return -EINVAL; + } + rx_data_len = ( len - sizeof ( *aoeata ) ); + + /* Calculate count and data_len for this subcommand */ + count = command->cb.count.native; + if ( count > AOE_MAX_COUNT ) + count = AOE_MAX_COUNT; + data_len = count * ATA_SECTOR_SIZE; + + /* Merge into overall ATA status */ + aoe->status |= aoeata->cmd_stat; + + /* Copy data payload */ + if ( command->data_in ) { + if ( rx_data_len > data_len ) + rx_data_len = data_len; + copy_to_user ( command->data_in, aoe->command_offset, + aoeata->data, rx_data_len ); + } + + /* Update ATA command and offset */ + aoe->command_offset += data_len; + command->cb.lba.native += count; + command->cb.count.native -= count; + + /* Check for operation complete */ + if ( ! command->cb.count.native ) { + aoe_done ( aoe, 0 ); + return 0; + } + + /* Transmit next portion of request */ + stop_timer ( &aoe->timer ); + aoe_send_command ( aoe ); + + return 0; +} + +/** + * Process incoming AoE packets + * + * @v iobuf I/O buffer + * @v netdev Network device + * @v ll_source Link-layer source address + * @ret rc Return status code + * + */ +static int aoe_rx ( struct io_buffer *iobuf, + struct net_device *netdev __unused, + const void *ll_source ) { + struct aoehdr *aoehdr = iobuf->data; + struct aoe_session *aoe; + int rc = 0; + + /* Sanity checks */ + if ( iob_len ( iobuf ) < sizeof ( *aoehdr ) ) { + rc = -EINVAL; + goto done; + } + if ( ( aoehdr->ver_flags & AOE_VERSION_MASK ) != AOE_VERSION ) { + rc = -EPROTONOSUPPORT; + goto done; + } + if ( ! ( aoehdr->ver_flags & AOE_FL_RESPONSE ) ) { + /* Ignore AoE requests that we happen to see */ + goto done; + } + iob_pull ( iobuf, sizeof ( *aoehdr ) ); + + /* Demultiplex amongst active AoE sessions */ + list_for_each_entry ( aoe, &aoe_sessions, list ) { + if ( ntohs ( aoehdr->major ) != aoe->major ) + continue; + if ( aoehdr->minor != aoe->minor ) + continue; + if ( ntohl ( aoehdr->tag ) != aoe->tag ) + continue; + if ( aoehdr->ver_flags & AOE_FL_ERROR ) { + aoe_done ( aoe, -EIO ); + break; + } + switch ( aoehdr->command ) { + case AOE_CMD_ATA: + rc = aoe_rx_ata ( aoe, iobuf->data, iob_len ( iobuf )); + break; + case AOE_CMD_CONFIG: + rc = aoe_rx_cfg ( aoe, ll_source ); + break; + default: + DBGC ( aoe, "AoE %p ignoring command %02x\n", + aoe, aoehdr->command ); + break; + } + break; + } + + done: + free_iob ( iobuf ); + return rc; +} + +/** AoE protocol */ +struct net_protocol aoe_protocol __net_protocol = { + .name = "AoE", + .net_proto = htons ( ETH_P_AOE ), + .rx = aoe_rx, +}; + +/** + * Issue ATA command via an open AoE session + * + * @v ata ATA device + * @v command ATA command + * @ret rc Return status code + */ +static int aoe_command ( struct ata_device *ata, + struct ata_command *command ) { + struct aoe_session *aoe = + container_of ( ata->backend, struct aoe_session, refcnt ); + int rc; + + aoe->command = command; + aoe->status = 0; + aoe->command_offset = 0; + aoe->aoe_cmd_type = AOE_CMD_ATA; + + aoe_send_command ( aoe ); + + aoe->rc = -EINPROGRESS; + while ( aoe->rc == -EINPROGRESS ) + step(); + rc = aoe->rc; + + return rc; +} + + +/** + * Issue AoE config query for AoE target discovery + * + * @v aoe AoE session + * @ret rc Return status code + */ +static int aoe_discover ( struct aoe_session *aoe ) { + int rc; + + aoe->status = 0; + aoe->aoe_cmd_type = AOE_CMD_CONFIG; + aoe->command = NULL; + + aoe_send_command ( aoe ); + + aoe->rc = -EINPROGRESS; + while ( aoe->rc == -EINPROGRESS ) + step(); + rc = aoe->rc; + + return rc; +} + +static int aoe_detached_command ( struct ata_device *ata __unused, + struct ata_command *command __unused ) { + return -ENODEV; +} + +void aoe_detach ( struct ata_device *ata ) { + struct aoe_session *aoe = + container_of ( ata->backend, struct aoe_session, refcnt ); + + stop_timer ( &aoe->timer ); + ata->command = aoe_detached_command; + list_del ( &aoe->list ); + ref_put ( ata->backend ); + ata->backend = NULL; +} + +static int aoe_parse_root_path ( struct aoe_session *aoe, + const char *root_path ) { + char *ptr; + + if ( strncmp ( root_path, "aoe:", 4 ) != 0 ) + return -EINVAL; + ptr = ( ( char * ) root_path + 4 ); + + if ( *ptr++ != 'e' ) + return -EINVAL; + + aoe->major = strtoul ( ptr, &ptr, 10 ); + if ( *ptr++ != '.' ) + return -EINVAL; + + aoe->minor = strtoul ( ptr, &ptr, 10 ); + if ( *ptr ) + return -EINVAL; + + return 0; +} + +int aoe_attach ( struct ata_device *ata, struct net_device *netdev, + const char *root_path ) { + struct aoe_session *aoe; + int rc; + + /* Allocate and initialise structure */ + aoe = zalloc ( sizeof ( *aoe ) ); + if ( ! aoe ) + return -ENOMEM; + aoe->refcnt.free = aoe_free; + aoe->netdev = netdev_get ( netdev ); + memcpy ( aoe->target, ethernet_protocol.ll_broadcast, + sizeof ( aoe->target ) ); + aoe->tag = AOE_TAG_MAGIC; + aoe->timer.expired = aoe_timer_expired; + + /* Parse root path */ + if ( ( rc = aoe_parse_root_path ( aoe, root_path ) ) != 0 ) + goto err; + + /* Attach parent interface, transfer reference to connection + * list, and return + */ + ata->backend = ref_get ( &aoe->refcnt ); + ata->command = aoe_command; + list_add ( &aoe->list, &aoe_sessions ); + + /* Send discovery packet to find the target MAC address. + * Ideally, this ought to be done asynchronously, but the + * block device interface does not yet support asynchronous + * operation. + */ + if ( ( rc = aoe_discover( aoe ) ) != 0 ) + goto err; + + return 0; + + err: + ref_put ( &aoe->refcnt ); + return rc; +} diff --git a/gpxe/src/net/arp.c b/gpxe/src/net/arp.c new file mode 100644 index 0000000..ba9ebf4 --- /dev/null +++ b/gpxe/src/net/arp.c @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Address Resolution Protocol + * + * This file implements the address resolution protocol as defined in + * RFC826. The implementation is media-independent and + * protocol-independent; it is not limited to Ethernet or to IPv4. + * + */ + +/** Registered ARP protocols */ +static struct arp_net_protocol arp_net_protocols[0] + __table_start ( struct arp_net_protocol, arp_net_protocols ); +static struct arp_net_protocol arp_net_protocols_end[0] + __table_end ( struct arp_net_protocol, arp_net_protocols ); + +/** An ARP cache entry */ +struct arp_entry { + /** Network-layer protocol */ + struct net_protocol *net_protocol; + /** Link-layer protocol */ + struct ll_protocol *ll_protocol; + /** Network-layer address */ + uint8_t net_addr[MAX_NET_ADDR_LEN]; + /** Link-layer address */ + uint8_t ll_addr[MAX_LL_ADDR_LEN]; +}; + +/** Number of entries in the ARP cache + * + * This is a global cache, covering all network interfaces, + * network-layer protocols and link-layer protocols. + */ +#define NUM_ARP_ENTRIES 4 + +/** The ARP cache */ +static struct arp_entry arp_table[NUM_ARP_ENTRIES]; +#define arp_table_end &arp_table[NUM_ARP_ENTRIES] + +static unsigned int next_new_arp_entry = 0; + +struct net_protocol arp_protocol; + +/** + * Find entry in the ARP cache + * + * @v ll_protocol Link-layer protocol + * @v net_protocol Network-layer protocol + * @v net_addr Network-layer address + * @ret arp ARP cache entry, or NULL if not found + * + */ +static struct arp_entry * +arp_find_entry ( struct ll_protocol *ll_protocol, + struct net_protocol *net_protocol, + const void *net_addr ) { + struct arp_entry *arp; + + for ( arp = arp_table ; arp < arp_table_end ; arp++ ) { + if ( ( arp->ll_protocol == ll_protocol ) && + ( arp->net_protocol == net_protocol ) && + ( memcmp ( arp->net_addr, net_addr, + net_protocol->net_addr_len ) == 0 ) ) + return arp; + } + return NULL; +} + +/** + * Look up media-specific link-layer address in the ARP cache + * + * @v netdev Network device + * @v net_protocol Network-layer protocol + * @v dest_net_addr Destination network-layer address + * @v source_net_addr Source network-layer address + * @ret dest_ll_addr Destination link layer address + * @ret rc Return status code + * + * This function will use the ARP cache to look up the link-layer + * address for the link-layer protocol associated with the network + * device and the given network-layer protocol and addresses. If + * found, the destination link-layer address will be filled in in @c + * dest_ll_addr. + * + * If no address is found in the ARP cache, an ARP request will be + * transmitted on the specified network device and -ENOENT will be + * returned. + */ +int arp_resolve ( struct net_device *netdev, struct net_protocol *net_protocol, + const void *dest_net_addr, const void *source_net_addr, + void *dest_ll_addr ) { + struct ll_protocol *ll_protocol = netdev->ll_protocol; + const struct arp_entry *arp; + struct io_buffer *iobuf; + struct arphdr *arphdr; + int rc; + + /* Look for existing entry in ARP table */ + arp = arp_find_entry ( ll_protocol, net_protocol, dest_net_addr ); + if ( arp ) { + DBG ( "ARP cache hit: %s %s => %s %s\n", + net_protocol->name, net_protocol->ntoa ( arp->net_addr ), + ll_protocol->name, ll_protocol->ntoa ( arp->ll_addr ) ); + memcpy ( dest_ll_addr, arp->ll_addr, ll_protocol->ll_addr_len); + return 0; + } + DBG ( "ARP cache miss: %s %s\n", net_protocol->name, + net_protocol->ntoa ( dest_net_addr ) ); + + /* Allocate ARP packet */ + iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( *arphdr ) + + 2 * ( MAX_LL_ADDR_LEN + MAX_NET_ADDR_LEN ) ); + if ( ! iobuf ) + return -ENOMEM; + iob_reserve ( iobuf, MAX_LL_HEADER_LEN ); + + /* Build up ARP request */ + arphdr = iob_put ( iobuf, sizeof ( *arphdr ) ); + arphdr->ar_hrd = ll_protocol->ll_proto; + arphdr->ar_hln = ll_protocol->ll_addr_len; + arphdr->ar_pro = net_protocol->net_proto; + arphdr->ar_pln = net_protocol->net_addr_len; + arphdr->ar_op = htons ( ARPOP_REQUEST ); + memcpy ( iob_put ( iobuf, ll_protocol->ll_addr_len ), + netdev->ll_addr, ll_protocol->ll_addr_len ); + memcpy ( iob_put ( iobuf, net_protocol->net_addr_len ), + source_net_addr, net_protocol->net_addr_len ); + memset ( iob_put ( iobuf, ll_protocol->ll_addr_len ), + 0, ll_protocol->ll_addr_len ); + memcpy ( iob_put ( iobuf, net_protocol->net_addr_len ), + dest_net_addr, net_protocol->net_addr_len ); + + /* Transmit ARP request */ + if ( ( rc = net_tx ( iobuf, netdev, &arp_protocol, + ll_protocol->ll_broadcast ) ) != 0 ) + return rc; + + return -ENOENT; +} + +/** + * Identify ARP protocol + * + * @v net_proto Network-layer protocol, in network-endian order + * @ret arp_net_protocol ARP protocol, or NULL + * + */ +static struct arp_net_protocol * arp_find_protocol ( uint16_t net_proto ) { + struct arp_net_protocol *arp_net_protocol; + + for ( arp_net_protocol = arp_net_protocols ; + arp_net_protocol < arp_net_protocols_end ; arp_net_protocol++ ) { + if ( arp_net_protocol->net_protocol->net_proto == net_proto ) { + return arp_net_protocol; + } + } + return NULL; +} + +/** + * Process incoming ARP packets + * + * @v iobuf I/O buffer + * @v netdev Network device + * @v ll_source Link-layer source address + * @ret rc Return status code + * + * This handles ARP requests and responses as detailed in RFC826. The + * method detailed within the RFC is pretty optimised, handling + * requests and responses with basically a single code path and + * avoiding the need for extraneous ARP requests; read the RFC for + * details. + */ +static int arp_rx ( struct io_buffer *iobuf, struct net_device *netdev, + const void *ll_source __unused ) { + struct arphdr *arphdr = iobuf->data; + struct arp_net_protocol *arp_net_protocol; + struct net_protocol *net_protocol; + struct ll_protocol *ll_protocol; + struct arp_entry *arp; + int merge = 0; + + /* Identify network-layer and link-layer protocols */ + arp_net_protocol = arp_find_protocol ( arphdr->ar_pro ); + if ( ! arp_net_protocol ) + goto done; + net_protocol = arp_net_protocol->net_protocol; + ll_protocol = netdev->ll_protocol; + + /* Sanity checks */ + if ( ( arphdr->ar_hrd != ll_protocol->ll_proto ) || + ( arphdr->ar_hln != ll_protocol->ll_addr_len ) || + ( arphdr->ar_pln != net_protocol->net_addr_len ) ) + goto done; + + /* See if we have an entry for this sender, and update it if so */ + arp = arp_find_entry ( ll_protocol, net_protocol, + arp_sender_pa ( arphdr ) ); + if ( arp ) { + memcpy ( arp->ll_addr, arp_sender_ha ( arphdr ), + arphdr->ar_hln ); + merge = 1; + DBG ( "ARP cache update: %s %s => %s %s\n", + net_protocol->name, net_protocol->ntoa ( arp->net_addr ), + ll_protocol->name, ll_protocol->ntoa ( arp->ll_addr ) ); + } + + /* See if we own the target protocol address */ + if ( arp_net_protocol->check ( netdev, arp_target_pa ( arphdr ) ) != 0) + goto done; + + /* Create new ARP table entry if necessary */ + if ( ! merge ) { + arp = &arp_table[next_new_arp_entry++ % NUM_ARP_ENTRIES]; + arp->ll_protocol = ll_protocol; + arp->net_protocol = net_protocol; + memcpy ( arp->ll_addr, arp_sender_ha ( arphdr ), + arphdr->ar_hln ); + memcpy ( arp->net_addr, arp_sender_pa ( arphdr ), + arphdr->ar_pln); + DBG ( "ARP cache add: %s %s => %s %s\n", + net_protocol->name, net_protocol->ntoa ( arp->net_addr ), + ll_protocol->name, ll_protocol->ntoa ( arp->ll_addr ) ); + } + + /* If it's not a request, there's nothing more to do */ + if ( arphdr->ar_op != htons ( ARPOP_REQUEST ) ) + goto done; + + /* Change request to a reply */ + DBG ( "ARP reply: %s %s => %s %s\n", net_protocol->name, + net_protocol->ntoa ( arp_target_pa ( arphdr ) ), + ll_protocol->name, ll_protocol->ntoa ( netdev->ll_addr ) ); + arphdr->ar_op = htons ( ARPOP_REPLY ); + memswap ( arp_sender_ha ( arphdr ), arp_target_ha ( arphdr ), + arphdr->ar_hln + arphdr->ar_pln ); + memcpy ( arp_sender_ha ( arphdr ), netdev->ll_addr, arphdr->ar_hln ); + + /* Send reply */ + net_tx ( iob_disown ( iobuf ), netdev, &arp_protocol, + arp_target_ha ( arphdr ) ); + + done: + free_iob ( iobuf ); + return 0; +} + +/** + * Transcribe ARP address + * + * @v net_addr ARP address + * @ret string "" + * + * This operation is meaningless for the ARP protocol. + */ +static const char * arp_ntoa ( const void *net_addr __unused ) { + return ""; +} + +/** ARP protocol */ +struct net_protocol arp_protocol __net_protocol = { + .name = "ARP", + .net_proto = htons ( ETH_P_ARP ), + .rx = arp_rx, + .ntoa = arp_ntoa, +}; diff --git a/gpxe/src/net/dhcpopts.c b/gpxe/src/net/dhcpopts.c new file mode 100644 index 0000000..1898011 --- /dev/null +++ b/gpxe/src/net/dhcpopts.c @@ -0,0 +1,434 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * DHCP options + * + */ + +/** + * Obtain printable version of a DHCP option tag + * + * @v tag DHCP option tag + * @ret name String representation of the tag + * + */ +static inline char * dhcp_tag_name ( unsigned int tag ) { + static char name[8]; + + if ( DHCP_IS_ENCAP_OPT ( tag ) ) { + snprintf ( name, sizeof ( name ), "%d.%d", + DHCP_ENCAPSULATOR ( tag ), + DHCP_ENCAPSULATED ( tag ) ); + } else { + snprintf ( name, sizeof ( name ), "%d", tag ); + } + return name; +} + +/** + * Get pointer to DHCP option + * + * @v options DHCP options block + * @v offset Offset within options block + * @ret option DHCP option + */ +static inline __attribute__ (( always_inline )) struct dhcp_option * +dhcp_option ( struct dhcp_options *options, unsigned int offset ) { + return ( ( struct dhcp_option * ) ( options->data + offset ) ); +} + +/** + * Get offset of a DHCP option + * + * @v options DHCP options block + * @v option DHCP option + * @ret offset Offset within options block + */ +static inline __attribute__ (( always_inline )) int +dhcp_option_offset ( struct dhcp_options *options, + struct dhcp_option *option ) { + return ( ( ( void * ) option ) - options->data ); +} + +/** + * Calculate length of any DHCP option + * + * @v option DHCP option + * @ret len Length (including tag and length field) + */ +static unsigned int dhcp_option_len ( struct dhcp_option *option ) { + if ( ( option->tag == DHCP_END ) || ( option->tag == DHCP_PAD ) ) { + return 1; + } else { + return ( option->len + DHCP_OPTION_HEADER_LEN ); + } +} + +/** + * Find DHCP option within DHCP options block, and its encapsulator (if any) + * + * @v options DHCP options block + * @v tag DHCP option tag to search for + * @ret encap_offset Offset of encapsulating DHCP option + * @ret offset Offset of DHCP option, or negative error + * + * Searches for the DHCP option matching the specified tag within the + * DHCP option block. Encapsulated options may be searched for by + * using DHCP_ENCAP_OPT() to construct the tag value. + * + * If the option is encapsulated, and @c encapsulator is non-NULL, it + * will be filled in with the offset of the encapsulating option. + * + * This routine is designed to be paranoid. It does not assume that + * the option data is well-formatted, and so must guard against flaws + * such as options missing a @c DHCP_END terminator, or options whose + * length would take them beyond the end of the data block. + */ +static int find_dhcp_option_with_encap ( struct dhcp_options *options, + unsigned int tag, + int *encap_offset ) { + unsigned int original_tag __attribute__ (( unused )) = tag; + struct dhcp_option *option; + int offset = 0; + ssize_t remaining = options->len; + unsigned int option_len; + + /* Sanity check */ + if ( tag == DHCP_PAD ) + return -ENOENT; + + /* Search for option */ + while ( remaining ) { + /* Calculate length of this option. Abort processing + * if the length is malformed (i.e. takes us beyond + * the end of the data block). + */ + option = dhcp_option ( options, offset ); + option_len = dhcp_option_len ( option ); + remaining -= option_len; + if ( remaining < 0 ) + break; + /* Check for explicit end marker */ + if ( option->tag == DHCP_END ) + break; + /* Check for matching tag */ + if ( option->tag == tag ) { + DBGC ( options, "DHCPOPT %p found %s (length %d)\n", + options, dhcp_tag_name ( original_tag ), + option_len ); + return offset; + } + /* Check for start of matching encapsulation block */ + if ( DHCP_IS_ENCAP_OPT ( tag ) && + ( option->tag == DHCP_ENCAPSULATOR ( tag ) ) ) { + if ( encap_offset ) + *encap_offset = offset; + /* Continue search within encapsulated option block */ + tag = DHCP_ENCAPSULATED ( tag ); + remaining = option_len; + offset += DHCP_OPTION_HEADER_LEN; + continue; + } + offset += option_len; + } + + return -ENOENT; +} + +/** + * Resize a DHCP option + * + * @v options DHCP option block + * @v offset Offset of option to resize + * @v encap_offset Offset of encapsulating offset (or -ve for none) + * @v old_len Old length (including header) + * @v new_len New length (including header) + * @v can_realloc Can reallocate options data if necessary + * @ret rc Return status code + */ +static int resize_dhcp_option ( struct dhcp_options *options, + int offset, int encap_offset, + size_t old_len, size_t new_len, + int can_realloc ) { + struct dhcp_option *encapsulator; + struct dhcp_option *option; + ssize_t delta = ( new_len - old_len ); + size_t new_options_len; + size_t new_encapsulator_len; + void *new_data; + void *source; + void *dest; + void *end; + + /* Check for sufficient space, and update length fields */ + if ( new_len > DHCP_MAX_LEN ) { + DBGC ( options, "DHCPOPT %p overlength option\n", options ); + return -ENOSPC; + } + new_options_len = ( options->len + delta ); + if ( new_options_len > options->max_len ) { + /* Reallocate options block if allowed to do so. */ + if ( can_realloc ) { + new_data = realloc ( options->data, new_options_len ); + if ( ! new_data ) { + DBGC ( options, "DHCPOPT %p could not " + "reallocate to %zd bytes\n", options, + new_options_len ); + return -ENOMEM; + } + options->data = new_data; + options->max_len = new_options_len; + } else { + DBGC ( options, "DHCPOPT %p out of space\n", options ); + return -ENOMEM; + } + } + if ( encap_offset >= 0 ) { + encapsulator = dhcp_option ( options, encap_offset ); + new_encapsulator_len = ( encapsulator->len + delta ); + if ( new_encapsulator_len > DHCP_MAX_LEN ) { + DBGC ( options, "DHCPOPT %p overlength encapsulator\n", + options ); + return -ENOSPC; + } + encapsulator->len = new_encapsulator_len; + } + options->len = new_options_len; + + /* Move remainder of option data */ + option = dhcp_option ( options, offset ); + source = ( ( ( void * ) option ) + old_len ); + dest = ( ( ( void * ) option ) + new_len ); + end = ( options->data + options->max_len ); + memmove ( dest, source, ( end - dest ) ); + + return 0; +} + +/** + * Set value of DHCP option + * + * @v options DHCP option block + * @v tag DHCP option tag + * @v data New value for DHCP option + * @v len Length of value, in bytes + * @v can_realloc Can reallocate options data if necessary + * @ret offset Offset of DHCP option, or negative error + * + * Sets the value of a DHCP option within the options block. The + * option may or may not already exist. Encapsulators will be created + * (and deleted) as necessary. + * + * This call may fail due to insufficient space in the options block. + * If it does fail, and the option existed previously, the option will + * be left with its original value. + */ +static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag, + const void *data, size_t len, + int can_realloc ) { + static const uint8_t empty_encapsulator[] = { DHCP_END }; + int offset; + int encap_offset = -1; + int creation_offset = 0; + struct dhcp_option *option; + unsigned int encap_tag = DHCP_ENCAPSULATOR ( tag ); + size_t old_len = 0; + size_t new_len = ( len ? ( len + DHCP_OPTION_HEADER_LEN ) : 0 ); + int rc; + + /* Sanity check */ + if ( tag == DHCP_PAD ) + return -ENOTTY; + + /* Find old instance of this option, if any */ + offset = find_dhcp_option_with_encap ( options, tag, &encap_offset ); + if ( offset >= 0 ) { + old_len = dhcp_option_len ( dhcp_option ( options, offset ) ); + DBGC ( options, "DHCPOPT %p resizing %s from %zd to %zd\n", + options, dhcp_tag_name ( tag ), old_len, new_len ); + } else { + DBGC ( options, "DHCPOPT %p creating %s (length %zd)\n", + options, dhcp_tag_name ( tag ), new_len ); + } + + /* Ensure that encapsulator exists, if required */ + if ( encap_tag ) { + if ( encap_offset < 0 ) + encap_offset = set_dhcp_option ( options, encap_tag, + empty_encapsulator, 1, + can_realloc ); + if ( encap_offset < 0 ) + return encap_offset; + creation_offset = ( encap_offset + DHCP_OPTION_HEADER_LEN ); + } + + /* Create new option if necessary */ + if ( offset < 0 ) + offset = creation_offset; + + /* Resize option to fit new data */ + if ( ( rc = resize_dhcp_option ( options, offset, encap_offset, + old_len, new_len, + can_realloc ) ) != 0 ) + return rc; + + /* Copy new data into option, if applicable */ + if ( len ) { + option = dhcp_option ( options, offset ); + option->tag = tag; + option->len = len; + memcpy ( &option->data, data, len ); + } + + /* Delete encapsulator if there's nothing else left in it */ + if ( encap_offset >= 0 ) { + option = dhcp_option ( options, encap_offset ); + if ( option->len <= 1 ) + set_dhcp_option ( options, encap_tag, NULL, 0, 0 ); + } + + return offset; +} + +/** + * Store value of DHCP option setting + * + * @v options DHCP option block + * @v tag Setting tag number + * @v data Setting data, or NULL to clear setting + * @v len Length of setting data + * @ret rc Return status code + */ +int dhcpopt_store ( struct dhcp_options *options, unsigned int tag, + const void *data, size_t len ) { + int offset; + + offset = set_dhcp_option ( options, tag, data, len, 0 ); + if ( offset < 0 ) + return offset; + return 0; +} + +/** + * Store value of DHCP option setting, extending options block if necessary + * + * @v options DHCP option block + * @v tag Setting tag number + * @v data Setting data, or NULL to clear setting + * @v len Length of setting data + * @ret rc Return status code + */ +int dhcpopt_extensible_store ( struct dhcp_options *options, unsigned int tag, + const void *data, size_t len ) { + int offset; + + offset = set_dhcp_option ( options, tag, data, len, 1 ); + if ( offset < 0 ) + return offset; + return 0; +} + +/** + * Fetch value of DHCP option setting + * + * @v options DHCP option block + * @v tag Setting tag number + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +int dhcpopt_fetch ( struct dhcp_options *options, unsigned int tag, + void *data, size_t len ) { + int offset; + struct dhcp_option *option; + size_t option_len; + + offset = find_dhcp_option_with_encap ( options, tag, NULL ); + if ( offset < 0 ) + return offset; + + option = dhcp_option ( options, offset ); + option_len = option->len; + if ( len > option_len ) + len = option_len; + memcpy ( data, option->data, len ); + + return option_len; +} + +/** + * Recalculate length of DHCP options block + * + * @v options Uninitialised DHCP option block + * + * The "used length" field will be updated based on scanning through + * the block to find the end of the options. + */ +static void dhcpopt_update_len ( struct dhcp_options *options ) { + struct dhcp_option *option; + int offset = 0; + ssize_t remaining = options->max_len; + unsigned int option_len; + + /* Find last non-pad option */ + options->len = 0; + while ( remaining ) { + option = dhcp_option ( options, offset ); + option_len = dhcp_option_len ( option ); + remaining -= option_len; + if ( remaining < 0 ) + break; + offset += option_len; + if ( option->tag != DHCP_PAD ) + options->len = offset; + } +} + +/** + * Initialise prepopulated block of DHCP options + * + * @v options Uninitialised DHCP option block + * @v data Memory for DHCP option data + * @v max_len Length of memory for DHCP option data + * + * The memory content must already be filled with valid DHCP options. + * A zeroed block counts as a block of valid DHCP options. + */ +void dhcpopt_init ( struct dhcp_options *options, void *data, + size_t max_len ) { + + /* Fill in fields */ + options->data = data; + options->max_len = max_len; + + /* Update length */ + dhcpopt_update_len ( options ); + + DBGC ( options, "DHCPOPT %p created (data %p len %#zx max_len %#zx)\n", + options, options->data, options->len, options->max_len ); +} diff --git a/gpxe/src/net/dhcppkt.c b/gpxe/src/net/dhcppkt.c new file mode 100644 index 0000000..1f2d373 --- /dev/null +++ b/gpxe/src/net/dhcppkt.c @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * DHCP packets + * + */ + +/**************************************************************************** + * + * DHCP packet raw interface + * + */ + +/** + * Calculate used length of an IPv4 field within a DHCP packet + * + * @v data Field data + * @v len Length of field + * @ret used Used length of field + */ +static size_t used_len_ipv4 ( const void *data, size_t len __unused ) { + const struct in_addr *in = data; + + return ( in->s_addr ? sizeof ( *in ) : 0 ); +} + +/** + * Calculate used length of a string field within a DHCP packet + * + * @v data Field data + * @v len Length of field + * @ret used Used length of field + */ +static size_t used_len_string ( const void *data, size_t len ) { + return strnlen ( data, len ); +} + +/** A dedicated field within a DHCP packet */ +struct dhcp_packet_field { + /** Settings tag number */ + unsigned int tag; + /** Offset within DHCP packet */ + uint16_t offset; + /** Length of field */ + uint16_t len; + /** Calculate used length of field + * + * @v data Field data + * @v len Length of field + * @ret used Used length of field + */ + size_t ( * used_len ) ( const void *data, size_t len ); +}; + +/** Declare a dedicated field within a DHCP packet + * + * @v _tag Settings tag number + * @v _field Field name + * @v _used_len Function to calculate used length of field + */ +#define DHCP_PACKET_FIELD( _tag, _field, _used_len ) { \ + .tag = (_tag), \ + .offset = offsetof ( struct dhcphdr, _field ), \ + .len = sizeof ( ( ( struct dhcphdr * ) 0 )->_field ), \ + .used_len = _used_len, \ + } + +/** Dedicated fields within a DHCP packet */ +static struct dhcp_packet_field dhcp_packet_fields[] = { + DHCP_PACKET_FIELD ( DHCP_EB_YIADDR, yiaddr, used_len_ipv4 ), + DHCP_PACKET_FIELD ( DHCP_EB_SIADDR, siaddr, used_len_ipv4 ), + DHCP_PACKET_FIELD ( DHCP_TFTP_SERVER_NAME, sname, used_len_string ), + DHCP_PACKET_FIELD ( DHCP_BOOTFILE_NAME, file, used_len_string ), +}; + +/** + * Get address of a DHCP packet field + * + * @v dhcphdr DHCP packet header + * @v field DHCP packet field + * @ret data Packet field data + */ +static inline void * dhcp_packet_field ( struct dhcphdr *dhcphdr, + struct dhcp_packet_field *field ) { + return ( ( ( void * ) dhcphdr ) + field->offset ); +} + +/** + * Find DHCP packet field corresponding to settings tag number + * + * @v tag Settings tag number + * @ret field DHCP packet field, or NULL + */ +static struct dhcp_packet_field * +find_dhcp_packet_field ( unsigned int tag ) { + struct dhcp_packet_field *field; + unsigned int i; + + for ( i = 0 ; i < ( sizeof ( dhcp_packet_fields ) / + sizeof ( dhcp_packet_fields[0] ) ) ; i++ ) { + field = &dhcp_packet_fields[i]; + if ( field->tag == tag ) + return field; + } + return NULL; +} + +/** + * Store value of DHCP packet setting + * + * @v dhcppkt DHCP packet + * @v tag Setting tag number + * @v data Setting data, or NULL to clear setting + * @v len Length of setting data + * @ret rc Return status code + */ +int dhcppkt_store ( struct dhcp_packet *dhcppkt, unsigned int tag, + const void *data, size_t len ) { + struct dhcp_packet_field *field; + void *field_data; + int rc; + + /* If this is a special field, fill it in */ + if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) { + if ( len > field->len ) + return -ENOSPC; + field_data = dhcp_packet_field ( dhcppkt->dhcphdr, field ); + memset ( field_data, 0, field->len ); + memcpy ( dhcp_packet_field ( dhcppkt->dhcphdr, field ), + data, len ); + return 0; + } + + /* Otherwise, use the generic options block */ + rc = dhcpopt_store ( &dhcppkt->options, tag, data, len ); + + /* Update our used-length field */ + dhcppkt->len = ( offsetof ( struct dhcphdr, options ) + + dhcppkt->options.len ); + + return rc; +} + +/** + * Fetch value of DHCP packet setting + * + * @v dhcppkt DHCP packet + * @v tag Setting tag number + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +int dhcppkt_fetch ( struct dhcp_packet *dhcppkt, unsigned int tag, + void *data, size_t len ) { + struct dhcp_packet_field *field; + void *field_data; + size_t field_len; + + /* If this is a special field, return it */ + if ( ( field = find_dhcp_packet_field ( tag ) ) != NULL ) { + field_data = dhcp_packet_field ( dhcppkt->dhcphdr, field ); + field_len = field->used_len ( field_data, field->len ); + if ( ! field_len ) + return -ENOENT; + if ( len > field_len ) + len = field_len; + memcpy ( data, field_data, len ); + return field_len; + } + + /* Otherwise, use the generic options block */ + return dhcpopt_fetch ( &dhcppkt->options, tag, data, len ); +} + +/**************************************************************************** + * + * DHCP packet settings interface + * + */ + +/** + * Store value of DHCP setting + * + * @v settings Settings block + * @v setting Setting to store + * @v data Setting data, or NULL to clear setting + * @v len Length of setting data + * @ret rc Return status code + */ +static int dhcppkt_settings_store ( struct settings *settings, + struct setting *setting, + const void *data, size_t len ) { + struct dhcp_packet *dhcppkt = + container_of ( settings, struct dhcp_packet, settings ); + + return dhcppkt_store ( dhcppkt, setting->tag, data, len ); +} + +/** + * Fetch value of DHCP setting + * + * @v settings Settings block, or NULL to search all blocks + * @v setting Setting to fetch + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int dhcppkt_settings_fetch ( struct settings *settings, + struct setting *setting, + void *data, size_t len ) { + struct dhcp_packet *dhcppkt = + container_of ( settings, struct dhcp_packet, settings ); + + return dhcppkt_fetch ( dhcppkt, setting->tag, data, len ); +} + +/** DHCP settings operations */ +static struct settings_operations dhcppkt_settings_operations = { + .store = dhcppkt_settings_store, + .fetch = dhcppkt_settings_fetch, +}; + +/**************************************************************************** + * + * Constructor + * + */ + +/** + * Initialise DHCP packet + * + * @v dhcppkt DHCP packet structure to fill in + * @v data DHCP packet raw data + * @v max_len Length of raw data buffer + * + * Initialise a DHCP packet structure from a data buffer containing a + * DHCP packet. + */ +void dhcppkt_init ( struct dhcp_packet *dhcppkt, struct dhcphdr *data, + size_t len ) { + dhcppkt->dhcphdr = data; + dhcppkt->max_len = len; + dhcpopt_init ( &dhcppkt->options, &dhcppkt->dhcphdr->options, + ( len - offsetof ( struct dhcphdr, options ) ) ); + dhcppkt->len = ( offsetof ( struct dhcphdr, options ) + + dhcppkt->options.len ); + settings_init ( &dhcppkt->settings, + &dhcppkt_settings_operations, &dhcppkt->refcnt, + DHCP_SETTINGS_NAME, 0 ); +} diff --git a/gpxe/src/net/ethernet.c b/gpxe/src/net/ethernet.c new file mode 100644 index 0000000..b16135a --- /dev/null +++ b/gpxe/src/net/ethernet.c @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Ethernet protocol + * + */ + +/** Ethernet broadcast MAC address */ +static uint8_t eth_broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +/** + * Add Ethernet link-layer header + * + * @v iobuf I/O buffer + * @v ll_dest Link-layer destination address + * @v ll_source Source link-layer address + * @v net_proto Network-layer protocol, in network-byte order + * @ret rc Return status code + */ +static int eth_push ( struct io_buffer *iobuf, const void *ll_dest, + const void *ll_source, uint16_t net_proto ) { + struct ethhdr *ethhdr = iob_push ( iobuf, sizeof ( *ethhdr ) ); + + /* Build Ethernet header */ + memcpy ( ethhdr->h_dest, ll_dest, ETH_ALEN ); + memcpy ( ethhdr->h_source, ll_source, ETH_ALEN ); + ethhdr->h_protocol = net_proto; + + return 0; +} + +/** + * Remove Ethernet link-layer header + * + * @v iobuf I/O buffer + * @ret ll_dest Link-layer destination address + * @ret ll_source Source link-layer address + * @ret net_proto Network-layer protocol, in network-byte order + * @ret rc Return status code + */ +static int eth_pull ( struct io_buffer *iobuf, const void **ll_dest, + const void **ll_source, uint16_t *net_proto ) { + struct ethhdr *ethhdr = iobuf->data; + + /* Sanity check */ + if ( iob_len ( iobuf ) < sizeof ( *ethhdr ) ) { + DBG ( "Ethernet packet too short (%zd bytes)\n", + iob_len ( iobuf ) ); + return -EINVAL; + } + + /* Strip off Ethernet header */ + iob_pull ( iobuf, sizeof ( *ethhdr ) ); + + /* Fill in required fields */ + *ll_dest = ethhdr->h_dest; + *ll_source = ethhdr->h_source; + *net_proto = ethhdr->h_protocol; + + return 0; +} + +/** + * Transcribe Ethernet address + * + * @v ll_addr Link-layer address + * @ret string Link-layer address in human-readable format + */ +const char * eth_ntoa ( const void *ll_addr ) { + static char buf[18]; /* "00:00:00:00:00:00" */ + const uint8_t *eth_addr = ll_addr; + + sprintf ( buf, "%02x:%02x:%02x:%02x:%02x:%02x", + eth_addr[0], eth_addr[1], eth_addr[2], + eth_addr[3], eth_addr[4], eth_addr[5] ); + return buf; +} + +/** + * Hash multicast address + * + * @v af Address family + * @v net_addr Network-layer address + * @v ll_addr Link-layer address to fill in + * @ret rc Return status code + */ +static int eth_mc_hash ( unsigned int af, const void *net_addr, + void *ll_addr ) { + const uint8_t *net_addr_bytes = net_addr; + uint8_t *ll_addr_bytes = ll_addr; + + switch ( af ) { + case AF_INET: + ll_addr_bytes[0] = 0x01; + ll_addr_bytes[1] = 0x00; + ll_addr_bytes[2] = 0x5e; + ll_addr_bytes[3] = net_addr_bytes[1] & 0x7f; + ll_addr_bytes[4] = net_addr_bytes[2]; + ll_addr_bytes[5] = net_addr_bytes[3]; + return 0; + default: + return -ENOTSUP; + } +} + +/** Ethernet protocol */ +struct ll_protocol ethernet_protocol __ll_protocol = { + .name = "Ethernet", + .ll_proto = htons ( ARPHRD_ETHER ), + .ll_addr_len = ETH_ALEN, + .ll_header_len = ETH_HLEN, + .ll_broadcast = eth_broadcast, + .push = eth_push, + .pull = eth_pull, + .ntoa = eth_ntoa, + .mc_hash = eth_mc_hash, +}; diff --git a/gpxe/src/net/fakedhcp.c b/gpxe/src/net/fakedhcp.c new file mode 100644 index 0000000..0518789 --- /dev/null +++ b/gpxe/src/net/fakedhcp.c @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Fake DHCP packets + * + */ + +/** + * Copy settings to DHCP packet + * + * @v dest Destination DHCP packet + * @v source Source settings block + * @v encapsulator Encapsulating setting tag number, or zero + * @ret rc Return status code + */ +static int copy_encap_settings ( struct dhcp_packet *dest, + struct settings *source, + unsigned int encapsulator ) { + struct setting setting = { .name = "" }; + unsigned int subtag; + unsigned int tag; + int len; + int check_len; + int rc; + + for ( subtag = DHCP_MIN_OPTION; subtag <= DHCP_MAX_OPTION; subtag++ ) { + tag = DHCP_ENCAP_OPT ( encapsulator, subtag ); + switch ( tag ) { + case DHCP_EB_ENCAP: + case DHCP_VENDOR_ENCAP: + /* Process encapsulated settings */ + if ( ( rc = copy_encap_settings ( dest, source, + tag ) ) != 0 ) + return rc; + break; + default: + /* Copy setting, if present */ + setting.tag = tag; + len = fetch_setting_len ( source, &setting ); + if ( len < 0 ) + break; + { + char buf[len]; + + check_len = fetch_setting ( source, &setting, + buf, sizeof (buf)); + assert ( check_len == len ); + if ( ( rc = dhcppkt_store ( dest, tag, buf, + sizeof(buf) )) !=0) + return rc; + } + break; + } + } + + return 0; +} + +/** + * Copy settings to DHCP packet + * + * @v dest Destination DHCP packet + * @v source Source settings block + * @ret rc Return status code + */ +static int copy_settings ( struct dhcp_packet *dest, + struct settings *source ) { + return copy_encap_settings ( dest, source, 0 ); +} + +/** + * Create fake DHCPDISCOVER packet + * + * @v netdev Network device + * @v data Buffer for DHCP packet + * @v max_len Size of DHCP packet buffer + * @ret rc Return status code + * + * Used by external code. + */ +int create_fakedhcpdiscover ( struct net_device *netdev, + void *data, size_t max_len ) { + struct dhcp_packet dhcppkt; + struct in_addr ciaddr = { 0 }; + int rc; + + if ( ( rc = dhcp_create_request ( &dhcppkt, netdev, DHCPDISCOVER, + ciaddr, data, max_len ) ) != 0 ) { + DBG ( "Could not create DHCPDISCOVER: %s\n", + strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Create fake DHCPACK packet + * + * @v netdev Network device + * @v data Buffer for DHCP packet + * @v max_len Size of DHCP packet buffer + * @ret rc Return status code + * + * Used by external code. + */ +int create_fakedhcpack ( struct net_device *netdev, + void *data, size_t max_len ) { + struct dhcp_packet dhcppkt; + int rc; + + /* Create base DHCPACK packet */ + if ( ( rc = dhcp_create_packet ( &dhcppkt, netdev, DHCPACK, NULL, 0, + data, max_len ) ) != 0 ) { + DBG ( "Could not create DHCPACK: %s\n", strerror ( rc ) ); + return rc; + } + + /* Merge in globally-scoped settings, then netdev-specific + * settings. Do it in this order so that netdev-specific + * settings take precedence regardless of stated priorities. + */ + if ( ( rc = copy_settings ( &dhcppkt, NULL ) ) != 0 ) { + DBG ( "Could not set DHCPACK global settings: %s\n", + strerror ( rc ) ); + return rc; + } + if ( ( rc = copy_settings ( &dhcppkt, + netdev_settings ( netdev ) ) ) != 0 ) { + DBG ( "Could not set DHCPACK netdev settings: %s\n", + strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Create fake PXE Boot Server ACK packet + * + * @v netdev Network device + * @v data Buffer for DHCP packet + * @v max_len Size of DHCP packet buffer + * @ret rc Return status code + * + * Used by external code. + */ +int create_fakepxebsack ( struct net_device *netdev, + void *data, size_t max_len ) { + struct dhcp_packet dhcppkt; + struct settings *proxy_settings; + struct settings *pxebs_settings; + int rc; + + /* Identify available settings */ + proxy_settings = find_settings ( PROXYDHCP_SETTINGS_NAME ); + pxebs_settings = find_settings ( PXEBS_SETTINGS_NAME ); + if ( ( ! proxy_settings ) && ( ! pxebs_settings ) ) { + /* No PXE boot server; return the regular DHCPACK */ + return create_fakedhcpack ( netdev, data, max_len ); + } + + /* Create base DHCPACK packet */ + if ( ( rc = dhcp_create_packet ( &dhcppkt, netdev, DHCPACK, NULL, 0, + data, max_len ) ) != 0 ) { + DBG ( "Could not create PXE BS ACK: %s\n", + strerror ( rc ) ); + return rc; + } + + /* Merge in ProxyDHCP options */ + if ( proxy_settings && + ( ( rc = copy_settings ( &dhcppkt, proxy_settings ) ) != 0 ) ) { + DBG ( "Could not copy ProxyDHCP settings: %s\n", + strerror ( rc ) ); + return rc; + } + + /* Merge in BootServerDHCP options, if present */ + if ( pxebs_settings && + ( ( rc = copy_settings ( &dhcppkt, pxebs_settings ) ) != 0 ) ) { + DBG ( "Could not copy PXE BS settings: %s\n", + strerror ( rc ) ); + return rc; + } + + return 0; +} diff --git a/gpxe/src/net/icmp.c b/gpxe/src/net/icmp.c new file mode 100644 index 0000000..3e45c1f --- /dev/null +++ b/gpxe/src/net/icmp.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2009 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. + */ + +#include +#include +#include +#include +#include +#include + +/** @file + * + * ICMP protocol + * + */ + +struct tcpip_protocol icmp_protocol __tcpip_protocol; + +/** + * Process a received packet + * + * @v iobuf I/O buffer + * @v st_src Partially-filled source address + * @v st_dest Partially-filled destination address + * @v pshdr_csum Pseudo-header checksum + * @ret rc Return status code + */ +static int icmp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src, + struct sockaddr_tcpip *st_dest, + uint16_t pshdr_csum __unused ) { + struct icmp_header *icmp = iobuf->data; + size_t len = iob_len ( iobuf ); + unsigned int csum; + int rc; + + /* Sanity check */ + if ( len < sizeof ( *icmp ) ) { + DBG ( "ICMP packet too short at %zd bytes (min %zd bytes)\n", + len, sizeof ( *icmp ) ); + rc = -EINVAL; + goto done; + } + + /* Verify checksum */ + csum = tcpip_chksum ( icmp, len ); + if ( csum != 0 ) { + DBG ( "ICMP checksum incorrect (is %04x, should be 0000)\n", + csum ); + DBG_HD ( icmp, len ); + rc = -EINVAL; + goto done; + } + + /* We respond only to pings */ + if ( icmp->type != ICMP_ECHO_REQUEST ) { + DBG ( "ICMP ignoring type %d\n", icmp->type ); + rc = 0; + goto done; + } + + DBG ( "ICMP responding to ping\n" ); + + /* Change type to response and recalculate checksum */ + icmp->type = ICMP_ECHO_RESPONSE; + icmp->chksum = 0; + icmp->chksum = tcpip_chksum ( icmp, len ); + + /* Transmit the response */ + if ( ( rc = tcpip_tx ( iob_disown ( iobuf ), &icmp_protocol, st_dest, + st_src, NULL, NULL ) ) != 0 ) { + DBG ( "ICMP could not transmit ping response: %s\n", + strerror ( rc ) ); + goto done; + } + + done: + free_iob ( iobuf ); + return rc; +} + +/** ICMP TCP/IP protocol */ +struct tcpip_protocol icmp_protocol __tcpip_protocol = { + .name = "ICMP", + .rx = icmp_rx, + .tcpip_proto = IP_ICMP, +}; diff --git a/gpxe/src/net/icmpv6.c b/gpxe/src/net/icmpv6.c new file mode 100644 index 0000000..237fc4a --- /dev/null +++ b/gpxe/src/net/icmpv6.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct tcpip_protocol icmp6_protocol; + +/** + * Send neighbour solicitation packet + * + * @v netdev Network device + * @v src Source address + * @v dest Destination address + * + * This function prepares a neighbour solicitation packet and sends it to the + * network layer. + */ +int icmp6_send_solicit ( struct net_device *netdev, struct in6_addr *src __unused, + struct in6_addr *dest ) { + union { + struct sockaddr_in6 sin6; + struct sockaddr_tcpip st; + } st_dest; + struct ll_protocol *ll_protocol = netdev->ll_protocol; + struct neighbour_solicit *nsolicit; + struct io_buffer *iobuf = alloc_iob ( sizeof ( *nsolicit ) + MIN_IOB_LEN ); + iob_reserve ( iobuf, MAX_HDR_LEN ); + nsolicit = iob_put ( iobuf, sizeof ( *nsolicit ) ); + + /* Fill up the headers */ + memset ( nsolicit, 0, sizeof ( *nsolicit ) ); + nsolicit->type = ICMP6_NSOLICIT; + nsolicit->code = 0; + nsolicit->target = *dest; + nsolicit->opt_type = 1; + nsolicit->opt_len = ( 2 + ll_protocol->ll_addr_len ) / 8; + memcpy ( nsolicit->opt_ll_addr, netdev->ll_addr, + netdev->ll_protocol->ll_addr_len ); + /* Partial checksum */ + nsolicit->csum = 0; + nsolicit->csum = tcpip_chksum ( nsolicit, sizeof ( *nsolicit ) ); + + /* Solicited multicast address */ + st_dest.sin6.sin_family = AF_INET6; + st_dest.sin6.sin6_addr.in6_u.u6_addr8[0] = 0xff; + st_dest.sin6.sin6_addr.in6_u.u6_addr8[2] = 0x02; + st_dest.sin6.sin6_addr.in6_u.u6_addr16[1] = 0x0000; + st_dest.sin6.sin6_addr.in6_u.u6_addr32[1] = 0x00000000; + st_dest.sin6.sin6_addr.in6_u.u6_addr16[4] = 0x0000; + st_dest.sin6.sin6_addr.in6_u.u6_addr16[5] = 0x0001; + st_dest.sin6.sin6_addr.in6_u.u6_addr32[3] = dest->in6_u.u6_addr32[3]; + st_dest.sin6.sin6_addr.in6_u.u6_addr8[13] = 0xff; + + /* Send packet over IP6 */ + return tcpip_tx ( iobuf, &icmp6_protocol, NULL, &st_dest.st, + NULL, &nsolicit->csum ); +} + +/** + * Process ICMP6 headers + * + * @v iobuf I/O buffer + * @v st_src Source address + * @v st_dest Destination address + */ +static int icmp6_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src, + struct sockaddr_tcpip *st_dest, __unused uint16_t pshdr_csum ) { + struct icmp6_header *icmp6hdr = iobuf->data; + + /* Sanity check */ + if ( iob_len ( iobuf ) < sizeof ( *icmp6hdr ) ) { + DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) ); + free_iob ( iobuf ); + return -EINVAL; + } + + /* TODO: Verify checksum */ + + /* Process the ICMP header */ + switch ( icmp6hdr->type ) { + case ICMP6_NADVERT: + return ndp_process_advert ( iobuf, st_src, st_dest ); + } + return -ENOSYS; +} + +#if 0 +void icmp6_test_nadvert (struct net_device *netdev, struct sockaddr_in6 *server_p, char *ll_addr) { + + struct sockaddr_in6 server; + memcpy ( &server, server_p, sizeof ( server ) ); + struct io_buffer *rxiobuf = alloc_iob ( 500 ); + iob_reserve ( rxiobuf, MAX_HDR_LEN ); + struct neighbour_advert *nadvert = iob_put ( rxiobuf, sizeof ( *nadvert ) ); + nadvert->type = 136; + nadvert->code = 0; + nadvert->flags = ICMP6_FLAGS_SOLICITED; + nadvert->csum = 0xffff; + nadvert->target = server.sin6_addr; + nadvert->opt_type = 2; + nadvert->opt_len = 1; + memcpy ( nadvert->opt_ll_addr, ll_addr, 6 ); + struct ip6_header *ip6hdr = iob_push ( rxiobuf, sizeof ( *ip6hdr ) ); + ip6hdr->ver_traffic_class_flow_label = htonl ( 0x60000000 ); + ip6hdr->hop_limit = 255; + ip6hdr->nxt_hdr = 58; + ip6hdr->payload_len = htons ( sizeof ( *nadvert ) ); + ip6hdr->src = server.sin6_addr; + ip6hdr->dest = server.sin6_addr; + hex_dump ( rxiobuf->data, iob_len ( rxiobuf ) ); + net_rx ( rxiobuf, netdev, htons ( ETH_P_IPV6 ), ll_addr ); +} +#endif + +/** ICMP6 protocol */ +struct tcpip_protocol icmp6_protocol __tcpip_protocol = { + .name = "ICMP6", + .rx = icmp6_rx, + .tcpip_proto = IP_ICMP6, // 58 +}; diff --git a/gpxe/src/net/infiniband.c b/gpxe/src/net/infiniband.c new file mode 100644 index 0000000..d79bdc2 --- /dev/null +++ b/gpxe/src/net/infiniband.c @@ -0,0 +1,600 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Infiniband protocol + * + */ + +/** List of Infiniband devices */ +struct list_head ib_devices = LIST_HEAD_INIT ( ib_devices ); + +/** + * Create completion queue + * + * @v ibdev Infiniband device + * @v num_cqes Number of completion queue entries + * @v op Completion queue operations + * @ret cq New completion queue + */ +struct ib_completion_queue * +ib_create_cq ( struct ib_device *ibdev, unsigned int num_cqes, + struct ib_completion_queue_operations *op ) { + struct ib_completion_queue *cq; + int rc; + + DBGC ( ibdev, "IBDEV %p creating completion queue\n", ibdev ); + + /* Allocate and initialise data structure */ + cq = zalloc ( sizeof ( *cq ) ); + if ( ! cq ) + goto err_alloc_cq; + cq->num_cqes = num_cqes; + INIT_LIST_HEAD ( &cq->work_queues ); + cq->op = op; + + /* Perform device-specific initialisation and get CQN */ + if ( ( rc = ibdev->op->create_cq ( ibdev, cq ) ) != 0 ) { + DBGC ( ibdev, "IBDEV %p could not initialise completion " + "queue: %s\n", ibdev, strerror ( rc ) ); + goto err_dev_create_cq; + } + + DBGC ( ibdev, "IBDEV %p created %d-entry completion queue %p (%p) " + "with CQN %#lx\n", ibdev, num_cqes, cq, + ib_cq_get_drvdata ( cq ), cq->cqn ); + return cq; + + ibdev->op->destroy_cq ( ibdev, cq ); + err_dev_create_cq: + free ( cq ); + err_alloc_cq: + return NULL; +} + +/** + * Destroy completion queue + * + * @v ibdev Infiniband device + * @v cq Completion queue + */ +void ib_destroy_cq ( struct ib_device *ibdev, + struct ib_completion_queue *cq ) { + DBGC ( ibdev, "IBDEV %p destroying completion queue %#lx\n", + ibdev, cq->cqn ); + assert ( list_empty ( &cq->work_queues ) ); + ibdev->op->destroy_cq ( ibdev, cq ); + free ( cq ); +} + +/** + * Create queue pair + * + * @v ibdev Infiniband device + * @v num_send_wqes Number of send work queue entries + * @v send_cq Send completion queue + * @v num_recv_wqes Number of receive work queue entries + * @v recv_cq Receive completion queue + * @v qkey Queue key + * @ret qp Queue pair + */ +struct ib_queue_pair * ib_create_qp ( struct ib_device *ibdev, + unsigned int num_send_wqes, + struct ib_completion_queue *send_cq, + unsigned int num_recv_wqes, + struct ib_completion_queue *recv_cq, + unsigned long qkey ) { + struct ib_queue_pair *qp; + size_t total_size; + int rc; + + DBGC ( ibdev, "IBDEV %p creating queue pair\n", ibdev ); + + /* Allocate and initialise data structure */ + total_size = ( sizeof ( *qp ) + + ( num_send_wqes * sizeof ( qp->send.iobufs[0] ) ) + + ( num_recv_wqes * sizeof ( qp->recv.iobufs[0] ) ) ); + qp = zalloc ( total_size ); + if ( ! qp ) + goto err_alloc_qp; + qp->ibdev = ibdev; + list_add ( &qp->list, &ibdev->qps ); + qp->qkey = qkey; + qp->send.qp = qp; + qp->send.is_send = 1; + qp->send.cq = send_cq; + list_add ( &qp->send.list, &send_cq->work_queues ); + qp->send.num_wqes = num_send_wqes; + qp->send.iobufs = ( ( ( void * ) qp ) + sizeof ( *qp ) ); + qp->recv.qp = qp; + qp->recv.cq = recv_cq; + list_add ( &qp->recv.list, &recv_cq->work_queues ); + qp->recv.num_wqes = num_recv_wqes; + qp->recv.iobufs = ( ( ( void * ) qp ) + sizeof ( *qp ) + + ( num_send_wqes * sizeof ( qp->send.iobufs[0] ) )); + INIT_LIST_HEAD ( &qp->mgids ); + + /* Perform device-specific initialisation and get QPN */ + if ( ( rc = ibdev->op->create_qp ( ibdev, qp ) ) != 0 ) { + DBGC ( ibdev, "IBDEV %p could not initialise queue pair: " + "%s\n", ibdev, strerror ( rc ) ); + goto err_dev_create_qp; + } + + DBGC ( ibdev, "IBDEV %p created queue pair %p (%p) with QPN %#lx\n", + ibdev, qp, ib_qp_get_drvdata ( qp ), qp->qpn ); + DBGC ( ibdev, "IBDEV %p QPN %#lx has %d send entries at [%p,%p)\n", + ibdev, qp->qpn, num_send_wqes, qp->send.iobufs, + qp->recv.iobufs ); + DBGC ( ibdev, "IBDEV %p QPN %#lx has %d receive entries at [%p,%p)\n", + ibdev, qp->qpn, num_recv_wqes, qp->recv.iobufs, + ( ( ( void * ) qp ) + total_size ) ); + return qp; + + ibdev->op->destroy_qp ( ibdev, qp ); + err_dev_create_qp: + list_del ( &qp->send.list ); + list_del ( &qp->recv.list ); + list_del ( &qp->list ); + free ( qp ); + err_alloc_qp: + return NULL; +} + +/** + * Modify queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v mod_list Modification list + * @v qkey New queue key, if applicable + * @ret rc Return status code + */ +int ib_modify_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp, + unsigned long mod_list, unsigned long qkey ) { + int rc; + + DBGC ( ibdev, "IBDEV %p modifying QPN %#lx\n", ibdev, qp->qpn ); + + if ( mod_list & IB_MODIFY_QKEY ) + qp->qkey = qkey; + + if ( ( rc = ibdev->op->modify_qp ( ibdev, qp, mod_list ) ) != 0 ) { + DBGC ( ibdev, "IBDEV %p could not modify QPN %#lx: %s\n", + ibdev, qp->qpn, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Destroy queue pair + * + * @v ibdev Infiniband device + * @v qp Queue pair + */ +void ib_destroy_qp ( struct ib_device *ibdev, struct ib_queue_pair *qp ) { + struct io_buffer *iobuf; + unsigned int i; + + DBGC ( ibdev, "IBDEV %p destroying QPN %#lx\n", + ibdev, qp->qpn ); + + assert ( list_empty ( &qp->mgids ) ); + + /* Perform device-specific destruction */ + ibdev->op->destroy_qp ( ibdev, qp ); + + /* Complete any remaining I/O buffers with errors */ + for ( i = 0 ; i < qp->send.num_wqes ; i++ ) { + if ( ( iobuf = qp->send.iobufs[i] ) != NULL ) + ib_complete_send ( ibdev, qp, iobuf, -ECANCELED ); + } + for ( i = 0 ; i < qp->recv.num_wqes ; i++ ) { + if ( ( iobuf = qp->recv.iobufs[i] ) != NULL ) { + ib_complete_recv ( ibdev, qp, NULL, iobuf, + -ECANCELED ); + } + } + + /* Remove work queues from completion queue */ + list_del ( &qp->send.list ); + list_del ( &qp->recv.list ); + + /* Free QP */ + list_del ( &qp->list ); + free ( qp ); +} + +/** + * Find queue pair by QPN + * + * @v ibdev Infiniband device + * @v qpn Queue pair number + * @ret qp Queue pair, or NULL + */ +struct ib_queue_pair * ib_find_qp_qpn ( struct ib_device *ibdev, + unsigned long qpn ) { + struct ib_queue_pair *qp; + + list_for_each_entry ( qp, &ibdev->qps, list ) { + if ( qp->qpn == qpn ) + return qp; + } + return NULL; +} + +/** + * Find queue pair by multicast GID + * + * @v ibdev Infiniband device + * @v gid Multicast GID + * @ret qp Queue pair, or NULL + */ +struct ib_queue_pair * ib_find_qp_mgid ( struct ib_device *ibdev, + struct ib_gid *gid ) { + struct ib_queue_pair *qp; + struct ib_multicast_gid *mgid; + + list_for_each_entry ( qp, &ibdev->qps, list ) { + list_for_each_entry ( mgid, &qp->mgids, list ) { + if ( memcmp ( &mgid->gid, gid, + sizeof ( mgid->gid ) ) == 0 ) { + return qp; + } + } + } + return NULL; +} + +/** + * Find work queue belonging to completion queue + * + * @v cq Completion queue + * @v qpn Queue pair number + * @v is_send Find send work queue (rather than receive) + * @ret wq Work queue, or NULL if not found + */ +struct ib_work_queue * ib_find_wq ( struct ib_completion_queue *cq, + unsigned long qpn, int is_send ) { + struct ib_work_queue *wq; + + list_for_each_entry ( wq, &cq->work_queues, list ) { + if ( ( wq->qp->qpn == qpn ) && ( wq->is_send == is_send ) ) + return wq; + } + return NULL; +} + +/** + * Post send work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v av Address vector + * @v iobuf I/O buffer + * @ret rc Return status code + */ +int ib_post_send ( struct ib_device *ibdev, struct ib_queue_pair *qp, + struct ib_address_vector *av, + struct io_buffer *iobuf ) { + int rc; + + /* Check queue fill level */ + if ( qp->send.fill >= qp->send.num_wqes ) { + DBGC ( ibdev, "IBDEV %p QPN %#lx send queue full\n", + ibdev, qp->qpn ); + return -ENOBUFS; + } + + /* Post to hardware */ + if ( ( rc = ibdev->op->post_send ( ibdev, qp, av, iobuf ) ) != 0 ) { + DBGC ( ibdev, "IBDEV %p QPN %#lx could not post send WQE: " + "%s\n", ibdev, qp->qpn, strerror ( rc ) ); + return rc; + } + + qp->send.fill++; + return 0; +} + +/** + * Post receive work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v iobuf I/O buffer + * @ret rc Return status code + */ +int ib_post_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp, + struct io_buffer *iobuf ) { + int rc; + + /* Check queue fill level */ + if ( qp->recv.fill >= qp->recv.num_wqes ) { + DBGC ( ibdev, "IBDEV %p QPN %#lx receive queue full\n", + ibdev, qp->qpn ); + return -ENOBUFS; + } + + /* Post to hardware */ + if ( ( rc = ibdev->op->post_recv ( ibdev, qp, iobuf ) ) != 0 ) { + DBGC ( ibdev, "IBDEV %p QPN %#lx could not post receive WQE: " + "%s\n", ibdev, qp->qpn, strerror ( rc ) ); + return rc; + } + + qp->recv.fill++; + return 0; +} + +/** + * Complete send work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v iobuf I/O buffer + * @v rc Completion status code + */ +void ib_complete_send ( struct ib_device *ibdev, struct ib_queue_pair *qp, + struct io_buffer *iobuf, int rc ) { + qp->send.cq->op->complete_send ( ibdev, qp, iobuf, rc ); + qp->send.fill--; +} + +/** + * Complete receive work queue entry + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v av Address vector + * @v iobuf I/O buffer + * @v rc Completion status code + */ +void ib_complete_recv ( struct ib_device *ibdev, struct ib_queue_pair *qp, + struct ib_address_vector *av, + struct io_buffer *iobuf, int rc ) { + qp->recv.cq->op->complete_recv ( ibdev, qp, av, iobuf, rc ); + qp->recv.fill--; +} + +/** + * Open port + * + * @v ibdev Infiniband device + * @ret rc Return status code + */ +int ib_open ( struct ib_device *ibdev ) { + int rc; + + /* Open device if this is the first requested opening */ + if ( ibdev->open_count == 0 ) { + if ( ( rc = ibdev->op->open ( ibdev ) ) != 0 ) + return rc; + } + + /* Increment device open request counter */ + ibdev->open_count++; + + return 0; +} + +/** + * Close port + * + * @v ibdev Infiniband device + */ +void ib_close ( struct ib_device *ibdev ) { + + /* Decrement device open request counter */ + ibdev->open_count--; + + /* Close device if this was the last remaining requested opening */ + if ( ibdev->open_count == 0 ) + ibdev->op->close ( ibdev ); +} + +/** + * Attach to multicast group + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v gid Multicast GID + * @ret rc Return status code + */ +int ib_mcast_attach ( struct ib_device *ibdev, struct ib_queue_pair *qp, + struct ib_gid *gid ) { + struct ib_multicast_gid *mgid; + int rc; + + /* Add to software multicast GID list */ + mgid = zalloc ( sizeof ( *mgid ) ); + if ( ! mgid ) { + rc = -ENOMEM; + goto err_alloc_mgid; + } + memcpy ( &mgid->gid, gid, sizeof ( mgid->gid ) ); + list_add ( &mgid->list, &qp->mgids ); + + /* Add to hardware multicast GID list */ + if ( ( rc = ibdev->op->mcast_attach ( ibdev, qp, gid ) ) != 0 ) + goto err_dev_mcast_attach; + + return 0; + + err_dev_mcast_attach: + list_del ( &mgid->list ); + free ( mgid ); + err_alloc_mgid: + return rc; +} + +/** + * Detach from multicast group + * + * @v ibdev Infiniband device + * @v qp Queue pair + * @v gid Multicast GID + */ +void ib_mcast_detach ( struct ib_device *ibdev, struct ib_queue_pair *qp, + struct ib_gid *gid ) { + struct ib_multicast_gid *mgid; + + /* Remove from hardware multicast GID list */ + ibdev->op->mcast_detach ( ibdev, qp, gid ); + + /* Remove from software multicast GID list */ + list_for_each_entry ( mgid, &qp->mgids, list ) { + if ( memcmp ( &mgid->gid, gid, sizeof ( mgid->gid ) ) == 0 ) { + list_del ( &mgid->list ); + free ( mgid ); + break; + } + } +} + + +/*************************************************************************** + * + * Event queues + * + *************************************************************************** + */ + +/** + * Handle Infiniband link state change + * + * @v ibdev Infiniband device + */ +void ib_link_state_changed ( struct ib_device *ibdev ) { + + /* Notify IPoIB of link state change */ + ipoib_link_state_changed ( ibdev ); +} + +/** + * Single-step the Infiniband event queue + * + * @v process Infiniband event queue process + */ +static void ib_step ( struct process *process __unused ) { + struct ib_device *ibdev; + + list_for_each_entry ( ibdev, &ib_devices, list ) { + ibdev->op->poll_eq ( ibdev ); + } +} + +/** Infiniband event queue process */ +struct process ib_process __permanent_process = { + .step = ib_step, +}; + +/*************************************************************************** + * + * Infiniband device creation/destruction + * + *************************************************************************** + */ + +/** + * Allocate Infiniband device + * + * @v priv_size Size of driver private data area + * @ret ibdev Infiniband device, or NULL + */ +struct ib_device * alloc_ibdev ( size_t priv_size ) { + struct ib_device *ibdev; + void *drv_priv; + size_t total_len; + + total_len = ( sizeof ( *ibdev ) + priv_size ); + ibdev = zalloc ( total_len ); + if ( ibdev ) { + drv_priv = ( ( ( void * ) ibdev ) + sizeof ( *ibdev ) ); + ib_set_drvdata ( ibdev, drv_priv ); + INIT_LIST_HEAD ( &ibdev->qps ); + ibdev->lid = IB_LID_NONE; + ibdev->pkey = IB_PKEY_NONE; + } + return ibdev; +} + +/** + * Register Infiniband device + * + * @v ibdev Infiniband device + * @ret rc Return status code + */ +int register_ibdev ( struct ib_device *ibdev ) { + int rc; + + /* Add to device list */ + ibdev_get ( ibdev ); + list_add_tail ( &ibdev->list, &ib_devices ); + + /* Add IPoIB device */ + if ( ( rc = ipoib_probe ( ibdev ) ) != 0 ) { + DBGC ( ibdev, "IBDEV %p could not add IPoIB device: %s\n", + ibdev, strerror ( rc ) ); + goto err_ipoib_probe; + } + + DBGC ( ibdev, "IBDEV %p registered (phys %s)\n", ibdev, + ibdev->dev->name ); + return 0; + + err_ipoib_probe: + list_del ( &ibdev->list ); + ibdev_put ( ibdev ); + return rc; +} + +/** + * Unregister Infiniband device + * + * @v ibdev Infiniband device + */ +void unregister_ibdev ( struct ib_device *ibdev ) { + + /* Close device */ + ipoib_remove ( ibdev ); + + /* Remove from device list */ + list_del ( &ibdev->list ); + ibdev_put ( ibdev ); + DBGC ( ibdev, "IBDEV %p unregistered\n", ibdev ); +} diff --git a/gpxe/src/net/iobpad.c b/gpxe/src/net/iobpad.c new file mode 100644 index 0000000..9961edc --- /dev/null +++ b/gpxe/src/net/iobpad.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2007 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. + */ + +/** + * @file + * + * I/O buffer padding + * + */ + +#include +#include + +/** + * Pad I/O buffer + * + * @v iobuf I/O buffer + * @v min_len Minimum length + * + * This function pads and aligns I/O buffers, for devices that + * aren't capable of padding in hardware, or that require specific + * alignment in TX buffers. The packet data will end up aligned to a + * multiple of @c IOB_ALIGN. + * + * @c min_len must not exceed @v IOB_ZLEN. + */ +void iob_pad ( struct io_buffer *iobuf, size_t min_len ) { + void *data; + size_t len; + size_t headroom; + signed int pad_len; + + assert ( min_len <= IOB_ZLEN ); + + /* Move packet data to start of I/O buffer. This will both + * align the data (since I/O buffers are aligned to + * IOB_ALIGN) and give us sufficient space for the + * zero-padding + */ + data = iobuf->data; + len = iob_len ( iobuf ); + headroom = iob_headroom ( iobuf ); + iob_push ( iobuf, headroom ); + memmove ( iobuf->data, data, len ); + iob_unput ( iobuf, headroom ); + + /* Pad to minimum packet length */ + pad_len = ( min_len - iob_len ( iobuf ) ); + if ( pad_len > 0 ) + memset ( iob_put ( iobuf, pad_len ), 0, pad_len ); +} diff --git a/gpxe/src/net/ipv4.c b/gpxe/src/net/ipv4.c new file mode 100644 index 0000000..8668d44 --- /dev/null +++ b/gpxe/src/net/ipv4.c @@ -0,0 +1,632 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * IPv4 protocol + * + */ + +/* Unique IP datagram identification number */ +static uint16_t next_ident = 0; + +struct net_protocol ipv4_protocol; + +/** List of IPv4 miniroutes */ +struct list_head ipv4_miniroutes = LIST_HEAD_INIT ( ipv4_miniroutes ); + +/** List of fragment reassembly buffers */ +static LIST_HEAD ( frag_buffers ); + +/** + * Add IPv4 minirouting table entry + * + * @v netdev Network device + * @v address IPv4 address + * @v netmask Subnet mask + * @v gateway Gateway address (or @c INADDR_NONE for no gateway) + * @ret miniroute Routing table entry, or NULL + */ +static struct ipv4_miniroute * __malloc +add_ipv4_miniroute ( struct net_device *netdev, struct in_addr address, + struct in_addr netmask, struct in_addr gateway ) { + struct ipv4_miniroute *miniroute; + + DBG ( "IPv4 add %s", inet_ntoa ( address ) ); + DBG ( "/%s ", inet_ntoa ( netmask ) ); + if ( gateway.s_addr != INADDR_NONE ) + DBG ( "gw %s ", inet_ntoa ( gateway ) ); + DBG ( "via %s\n", netdev->name ); + + /* Allocate and populate miniroute structure */ + miniroute = malloc ( sizeof ( *miniroute ) ); + if ( ! miniroute ) { + DBG ( "IPv4 could not add miniroute\n" ); + return NULL; + } + + /* Record routing information */ + miniroute->netdev = netdev_get ( netdev ); + miniroute->address = address; + miniroute->netmask = netmask; + miniroute->gateway = gateway; + + /* Add to end of list if we have a gateway, otherwise + * to start of list. + */ + if ( gateway.s_addr != INADDR_NONE ) { + list_add_tail ( &miniroute->list, &ipv4_miniroutes ); + } else { + list_add ( &miniroute->list, &ipv4_miniroutes ); + } + + return miniroute; +} + +/** + * Delete IPv4 minirouting table entry + * + * @v miniroute Routing table entry + */ +static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) { + + DBG ( "IPv4 del %s", inet_ntoa ( miniroute->address ) ); + DBG ( "/%s ", inet_ntoa ( miniroute->netmask ) ); + if ( miniroute->gateway.s_addr != INADDR_NONE ) + DBG ( "gw %s ", inet_ntoa ( miniroute->gateway ) ); + DBG ( "via %s\n", miniroute->netdev->name ); + + netdev_put ( miniroute->netdev ); + list_del ( &miniroute->list ); + free ( miniroute ); +} + +/** + * Perform IPv4 routing + * + * @v dest Final destination address + * @ret dest Next hop destination address + * @ret miniroute Routing table entry to use, or NULL if no route + * + * If the route requires use of a gateway, the next hop destination + * address will be overwritten with the gateway address. + */ +static struct ipv4_miniroute * ipv4_route ( struct in_addr *dest ) { + struct ipv4_miniroute *miniroute; + int local; + int has_gw; + + /* Never attempt to route the broadcast address */ + if ( dest->s_addr == INADDR_BROADCAST ) + return NULL; + + /* Find first usable route in routing table */ + list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) { + local = ( ( ( dest->s_addr ^ miniroute->address.s_addr ) + & miniroute->netmask.s_addr ) == 0 ); + has_gw = ( miniroute->gateway.s_addr != INADDR_NONE ); + if ( local || has_gw ) { + if ( ! local ) + *dest = miniroute->gateway; + return miniroute; + } + } + + return NULL; +} + +/** + * Fragment reassembly counter timeout + * + * @v timer Retry timer + * @v over If asserted, the timer is greater than @c MAX_TIMEOUT + */ +static void ipv4_frag_expired ( struct retry_timer *timer __unused, + int over ) { + if ( over ) { + DBG ( "Fragment reassembly timeout" ); + /* Free the fragment buffer */ + } +} + +/** + * Free fragment buffer + * + * @v fragbug Fragment buffer + */ +static void free_fragbuf ( struct frag_buffer *fragbuf ) { + free ( fragbuf ); +} + +/** + * Fragment reassembler + * + * @v iobuf I/O buffer, fragment of the datagram + * @ret frag_iob Reassembled packet, or NULL + */ +static struct io_buffer * ipv4_reassemble ( struct io_buffer * iobuf ) { + struct iphdr *iphdr = iobuf->data; + struct frag_buffer *fragbuf; + + /** + * Check if the fragment belongs to any fragment series + */ + list_for_each_entry ( fragbuf, &frag_buffers, list ) { + if ( fragbuf->ident == iphdr->ident && + fragbuf->src.s_addr == iphdr->src.s_addr ) { + /** + * Check if the packet is the expected fragment + * + * The offset of the new packet must be equal to the + * length of the data accumulated so far (the length of + * the reassembled I/O buffer + */ + if ( iob_len ( fragbuf->frag_iob ) == + ( iphdr->frags & IP_MASK_OFFSET ) ) { + /** + * Append the contents of the fragment to the + * reassembled I/O buffer + */ + iob_pull ( iobuf, sizeof ( *iphdr ) ); + memcpy ( iob_put ( fragbuf->frag_iob, + iob_len ( iobuf ) ), + iobuf->data, iob_len ( iobuf ) ); + free_iob ( iobuf ); + + /** Check if the fragment series is over */ + if ( ! ( iphdr->frags & IP_MASK_MOREFRAGS ) ) { + iobuf = fragbuf->frag_iob; + free_fragbuf ( fragbuf ); + return iobuf; + } + + } else { + /* Discard the fragment series */ + free_fragbuf ( fragbuf ); + free_iob ( iobuf ); + } + return NULL; + } + } + + /** Check if the fragment is the first in the fragment series */ + if ( iphdr->frags & IP_MASK_MOREFRAGS && + ( ( iphdr->frags & IP_MASK_OFFSET ) == 0 ) ) { + + /** Create a new fragment buffer */ + fragbuf = ( struct frag_buffer* ) malloc ( sizeof( *fragbuf ) ); + fragbuf->ident = iphdr->ident; + fragbuf->src = iphdr->src; + + /* Set up the reassembly I/O buffer */ + fragbuf->frag_iob = alloc_iob ( IP_FRAG_IOB_SIZE ); + iob_pull ( iobuf, sizeof ( *iphdr ) ); + memcpy ( iob_put ( fragbuf->frag_iob, iob_len ( iobuf ) ), + iobuf->data, iob_len ( iobuf ) ); + free_iob ( iobuf ); + + /* Set the reassembly timer */ + fragbuf->frag_timer.timeout = IP_FRAG_TIMEOUT; + fragbuf->frag_timer.expired = ipv4_frag_expired; + start_timer ( &fragbuf->frag_timer ); + + /* Add the fragment buffer to the list of fragment buffers */ + list_add ( &fragbuf->list, &frag_buffers ); + } + + return NULL; +} + +/** + * Add IPv4 pseudo-header checksum to existing checksum + * + * @v iobuf I/O buffer + * @v csum Existing checksum + * @ret csum Updated checksum + */ +static uint16_t ipv4_pshdr_chksum ( struct io_buffer *iobuf, uint16_t csum ) { + struct ipv4_pseudo_header pshdr; + struct iphdr *iphdr = iobuf->data; + size_t hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 ); + + /* Build pseudo-header */ + pshdr.src = iphdr->src; + pshdr.dest = iphdr->dest; + pshdr.zero_padding = 0x00; + pshdr.protocol = iphdr->protocol; + pshdr.len = htons ( iob_len ( iobuf ) - hdrlen ); + + /* Update the checksum value */ + return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) ); +} + +/** + * Determine link-layer address + * + * @v dest IPv4 destination address + * @v src IPv4 source address + * @v netdev Network device + * @v ll_dest Link-layer destination address buffer + * @ret rc Return status code + */ +static int ipv4_ll_addr ( struct in_addr dest, struct in_addr src, + struct net_device *netdev, uint8_t *ll_dest ) { + struct ll_protocol *ll_protocol = netdev->ll_protocol; + + if ( dest.s_addr == INADDR_BROADCAST ) { + /* Broadcast address */ + memcpy ( ll_dest, ll_protocol->ll_broadcast, + ll_protocol->ll_addr_len ); + return 0; + } else if ( IN_MULTICAST ( ntohl ( dest.s_addr ) ) ) { + return ll_protocol->mc_hash ( AF_INET, &dest, ll_dest ); + } else { + /* Unicast address: resolve via ARP */ + return arp_resolve ( netdev, &ipv4_protocol, &dest, + &src, ll_dest ); + } +} + +/** + * Transmit IP packet + * + * @v iobuf I/O buffer + * @v tcpip Transport-layer protocol + * @v st_src Source network-layer address + * @v st_dest Destination network-layer address + * @v netdev Network device to use if no route found, or NULL + * @v trans_csum Transport-layer checksum to complete, or NULL + * @ret rc Status + * + * This function expects a transport-layer segment and prepends the IP header + */ +static int ipv4_tx ( struct io_buffer *iobuf, + struct tcpip_protocol *tcpip_protocol, + struct sockaddr_tcpip *st_src, + struct sockaddr_tcpip *st_dest, + struct net_device *netdev, + uint16_t *trans_csum ) { + struct iphdr *iphdr = iob_push ( iobuf, sizeof ( *iphdr ) ); + struct sockaddr_in *sin_src = ( ( struct sockaddr_in * ) st_src ); + struct sockaddr_in *sin_dest = ( ( struct sockaddr_in * ) st_dest ); + struct ipv4_miniroute *miniroute; + struct in_addr next_hop; + uint8_t ll_dest[MAX_LL_ADDR_LEN]; + int rc; + + /* Fill up the IP header, except source address */ + memset ( iphdr, 0, sizeof ( *iphdr ) ); + iphdr->verhdrlen = ( IP_VER | ( sizeof ( *iphdr ) / 4 ) ); + iphdr->service = IP_TOS; + iphdr->len = htons ( iob_len ( iobuf ) ); + iphdr->ident = htons ( ++next_ident ); + iphdr->ttl = IP_TTL; + iphdr->protocol = tcpip_protocol->tcpip_proto; + iphdr->dest = sin_dest->sin_addr; + + /* Use routing table to identify next hop and transmitting netdev */ + next_hop = iphdr->dest; + if ( sin_src ) + iphdr->src = sin_src->sin_addr; + if ( ( next_hop.s_addr != INADDR_BROADCAST ) && + ( ! IN_MULTICAST ( ntohl ( next_hop.s_addr ) ) ) && + ( ( miniroute = ipv4_route ( &next_hop ) ) != NULL ) ) { + iphdr->src = miniroute->address; + netdev = miniroute->netdev; + } + if ( ! netdev ) { + DBG ( "IPv4 has no route to %s\n", inet_ntoa ( iphdr->dest ) ); + rc = -ENETUNREACH; + goto err; + } + + /* Determine link-layer destination address */ + if ( ( rc = ipv4_ll_addr ( next_hop, iphdr->src, netdev, + ll_dest ) ) != 0 ) { + DBG ( "IPv4 has no link-layer address for %s: %s\n", + inet_ntoa ( next_hop ), strerror ( rc ) ); + goto err; + } + + /* Fix up checksums */ + if ( trans_csum ) + *trans_csum = ipv4_pshdr_chksum ( iobuf, *trans_csum ); + iphdr->chksum = tcpip_chksum ( iphdr, sizeof ( *iphdr ) ); + + /* Print IP4 header for debugging */ + DBG ( "IPv4 TX %s->", inet_ntoa ( iphdr->src ) ); + DBG ( "%s len %d proto %d id %04x csum %04x\n", + inet_ntoa ( iphdr->dest ), ntohs ( iphdr->len ), iphdr->protocol, + ntohs ( iphdr->ident ), ntohs ( iphdr->chksum ) ); + + /* Hand off to link layer */ + if ( ( rc = net_tx ( iobuf, netdev, &ipv4_protocol, ll_dest ) ) != 0 ) { + DBG ( "IPv4 could not transmit packet via %s: %s\n", + netdev->name, strerror ( rc ) ); + return rc; + } + + return 0; + + err: + free_iob ( iobuf ); + return rc; +} + +/** + * Process incoming packets + * + * @v iobuf I/O buffer + * @v netdev Network device + * @v ll_source Link-layer destination source + * + * This function expects an IP4 network datagram. It processes the headers + * and sends it to the transport layer. + */ +static int ipv4_rx ( struct io_buffer *iobuf, struct net_device *netdev __unused, + const void *ll_source __unused ) { + struct iphdr *iphdr = iobuf->data; + size_t hdrlen; + size_t len; + union { + struct sockaddr_in sin; + struct sockaddr_tcpip st; + } src, dest; + uint16_t csum; + uint16_t pshdr_csum; + int rc; + + /* Sanity check the IPv4 header */ + if ( iob_len ( iobuf ) < sizeof ( *iphdr ) ) { + DBG ( "IPv4 packet too short at %zd bytes (min %zd bytes)\n", + iob_len ( iobuf ), sizeof ( *iphdr ) ); + goto err; + } + if ( ( iphdr->verhdrlen & IP_MASK_VER ) != IP_VER ) { + DBG ( "IPv4 version %#02x not supported\n", iphdr->verhdrlen ); + goto err; + } + hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 ); + if ( hdrlen < sizeof ( *iphdr ) ) { + DBG ( "IPv4 header too short at %zd bytes (min %zd bytes)\n", + hdrlen, sizeof ( *iphdr ) ); + goto err; + } + if ( hdrlen > iob_len ( iobuf ) ) { + DBG ( "IPv4 header too long at %zd bytes " + "(packet is %zd bytes)\n", hdrlen, iob_len ( iobuf ) ); + goto err; + } + if ( ( csum = tcpip_chksum ( iphdr, hdrlen ) ) != 0 ) { + DBG ( "IPv4 checksum incorrect (is %04x including checksum " + "field, should be 0000)\n", csum ); + goto err; + } + len = ntohs ( iphdr->len ); + if ( len < hdrlen ) { + DBG ( "IPv4 length too short at %zd bytes " + "(header is %zd bytes)\n", len, hdrlen ); + goto err; + } + if ( len > iob_len ( iobuf ) ) { + DBG ( "IPv4 length too long at %zd bytes " + "(packet is %zd bytes)\n", len, iob_len ( iobuf ) ); + goto err; + } + + /* Print IPv4 header for debugging */ + DBG ( "IPv4 RX %s<-", inet_ntoa ( iphdr->dest ) ); + DBG ( "%s len %d proto %d id %04x csum %04x\n", + inet_ntoa ( iphdr->src ), ntohs ( iphdr->len ), iphdr->protocol, + ntohs ( iphdr->ident ), ntohs ( iphdr->chksum ) ); + + /* Truncate packet to correct length, calculate pseudo-header + * checksum and then strip off the IPv4 header. + */ + iob_unput ( iobuf, ( iob_len ( iobuf ) - len ) ); + pshdr_csum = ipv4_pshdr_chksum ( iobuf, TCPIP_EMPTY_CSUM ); + iob_pull ( iobuf, hdrlen ); + + /* Fragment reassembly */ + if ( ( iphdr->frags & htons ( IP_MASK_MOREFRAGS ) ) || + ( ( iphdr->frags & htons ( IP_MASK_OFFSET ) ) != 0 ) ) { + /* Pass the fragment to ipv4_reassemble() which either + * returns a fully reassembled I/O buffer or NULL. + */ + iobuf = ipv4_reassemble ( iobuf ); + if ( ! iobuf ) + return 0; + } + + /* Construct socket addresses and hand off to transport layer */ + memset ( &src, 0, sizeof ( src ) ); + src.sin.sin_family = AF_INET; + src.sin.sin_addr = iphdr->src; + memset ( &dest, 0, sizeof ( dest ) ); + dest.sin.sin_family = AF_INET; + dest.sin.sin_addr = iphdr->dest; + if ( ( rc = tcpip_rx ( iobuf, iphdr->protocol, &src.st, + &dest.st, pshdr_csum ) ) != 0 ) { + DBG ( "IPv4 received packet rejected by stack: %s\n", + strerror ( rc ) ); + return rc; + } + + return 0; + + err: + free_iob ( iobuf ); + return -EINVAL; +} + +/** + * Check existence of IPv4 address for ARP + * + * @v netdev Network device + * @v net_addr Network-layer address + * @ret rc Return status code + */ +static int ipv4_arp_check ( struct net_device *netdev, const void *net_addr ) { + const struct in_addr *address = net_addr; + struct ipv4_miniroute *miniroute; + + list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) { + if ( ( miniroute->netdev == netdev ) && + ( miniroute->address.s_addr == address->s_addr ) ) { + /* Found matching address */ + return 0; + } + } + return -ENOENT; +} + +/** + * Convert IPv4 address to dotted-quad notation + * + * @v in IP address + * @ret string IP address in dotted-quad notation + */ +char * inet_ntoa ( struct in_addr in ) { + static char buf[16]; /* "xxx.xxx.xxx.xxx" */ + uint8_t *bytes = ( uint8_t * ) ∈ + + sprintf ( buf, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3] ); + return buf; +} + +/** + * Transcribe IP address + * + * @v net_addr IP address + * @ret string IP address in dotted-quad notation + * + */ +static const char * ipv4_ntoa ( const void *net_addr ) { + return inet_ntoa ( * ( ( struct in_addr * ) net_addr ) ); +} + +/** IPv4 protocol */ +struct net_protocol ipv4_protocol __net_protocol = { + .name = "IP", + .net_proto = htons ( ETH_P_IP ), + .net_addr_len = sizeof ( struct in_addr ), + .rx = ipv4_rx, + .ntoa = ipv4_ntoa, +}; + +/** IPv4 TCPIP net protocol */ +struct tcpip_net_protocol ipv4_tcpip_protocol __tcpip_net_protocol = { + .name = "IPv4", + .sa_family = AF_INET, + .tx = ipv4_tx, +}; + +/** IPv4 ARP protocol */ +struct arp_net_protocol ipv4_arp_protocol __arp_net_protocol = { + .net_protocol = &ipv4_protocol, + .check = ipv4_arp_check, +}; + +/****************************************************************************** + * + * Settings + * + ****************************************************************************** + */ + +/** IPv4 address setting */ +struct setting ip_setting __setting = { + .name = "ip", + .description = "IPv4 address", + .tag = DHCP_EB_YIADDR, + .type = &setting_type_ipv4, +}; + +/** IPv4 subnet mask setting */ +struct setting netmask_setting __setting = { + .name = "netmask", + .description = "IPv4 subnet mask", + .tag = DHCP_SUBNET_MASK, + .type = &setting_type_ipv4, +}; + +/** Default gateway setting */ +struct setting gateway_setting __setting = { + .name = "gateway", + .description = "Default gateway", + .tag = DHCP_ROUTERS, + .type = &setting_type_ipv4, +}; + +/** + * Create IPv4 routing table based on configured settings + * + * @ret rc Return status code + */ +static int ipv4_create_routes ( void ) { + struct ipv4_miniroute *miniroute; + struct ipv4_miniroute *tmp; + struct net_device *netdev; + struct settings *settings; + struct in_addr address = { 0 }; + struct in_addr netmask = { 0 }; + struct in_addr gateway = { INADDR_NONE }; + + /* Delete all existing routes */ + list_for_each_entry_safe ( miniroute, tmp, &ipv4_miniroutes, list ) + del_ipv4_miniroute ( miniroute ); + + /* Create a route for each configured network device */ + for_each_netdev ( netdev ) { + settings = netdev_settings ( netdev ); + /* Get IPv4 address */ + address.s_addr = 0; + fetch_ipv4_setting ( settings, &ip_setting, &address ); + if ( ! address.s_addr ) + continue; + /* Calculate default netmask */ + if ( IN_CLASSA ( ntohl ( address.s_addr ) ) ) { + netmask.s_addr = htonl ( IN_CLASSA_NET ); + } else if ( IN_CLASSB ( ntohl ( address.s_addr ) ) ) { + netmask.s_addr = htonl ( IN_CLASSB_NET ); + } else if ( IN_CLASSC ( ntohl ( address.s_addr ) ) ) { + netmask.s_addr = htonl ( IN_CLASSC_NET ); + } else { + netmask.s_addr = 0; + } + /* Override with subnet mask, if present */ + fetch_ipv4_setting ( settings, &netmask_setting, &netmask ); + /* Get default gateway, if present */ + gateway.s_addr = INADDR_NONE; + fetch_ipv4_setting ( settings, &gateway_setting, &gateway ); + /* Configure route */ + miniroute = add_ipv4_miniroute ( netdev, address, + netmask, gateway ); + if ( ! miniroute ) + return -ENOMEM; + } + + return 0; +} + +/** IPv4 settings applicator */ +struct settings_applicator ipv4_settings_applicator __settings_applicator = { + .apply = ipv4_create_routes, +}; + +/* Drag in ICMP */ +REQUIRE_OBJECT ( icmp ); diff --git a/gpxe/src/net/ipv6.c b/gpxe/src/net/ipv6.c new file mode 100644 index 0000000..f7308bb --- /dev/null +++ b/gpxe/src/net/ipv6.c @@ -0,0 +1,381 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct net_protocol ipv6_protocol; + +/* Unspecified IP6 address */ +static struct in6_addr ip6_none = { + .in6_u.u6_addr32 = { 0,0,0,0 } +}; + +/** An IPv6 routing table entry */ +struct ipv6_miniroute { + /* List of miniroutes */ + struct list_head list; + + /* Network device */ + struct net_device *netdev; + + /* Destination prefix */ + struct in6_addr prefix; + /* Prefix length */ + int prefix_len; + /* IPv6 address of interface */ + struct in6_addr address; + /* Gateway address */ + struct in6_addr gateway; +}; + +/** List of IPv6 miniroutes */ +static LIST_HEAD ( miniroutes ); + +/** + * Add IPv6 minirouting table entry + * + * @v netdev Network device + * @v prefix Destination prefix + * @v address Address of the interface + * @v gateway Gateway address (or ::0 for no gateway) + * @ret miniroute Routing table entry, or NULL + */ +static struct ipv6_miniroute * __malloc +add_ipv6_miniroute ( struct net_device *netdev, struct in6_addr prefix, + int prefix_len, struct in6_addr address, + struct in6_addr gateway ) { + struct ipv6_miniroute *miniroute; + + miniroute = malloc ( sizeof ( *miniroute ) ); + if ( miniroute ) { + /* Record routing information */ + miniroute->netdev = netdev_get ( netdev ); + miniroute->prefix = prefix; + miniroute->prefix_len = prefix_len; + miniroute->address = address; + miniroute->gateway = gateway; + + /* Add miniroute to list of miniroutes */ + if ( !IP6_EQUAL ( gateway, ip6_none ) ) { + list_add_tail ( &miniroute->list, &miniroutes ); + } else { + list_add ( &miniroute->list, &miniroutes ); + } + } + + return miniroute; +} + +/** + * Delete IPv6 minirouting table entry + * + * @v miniroute Routing table entry + */ +static void del_ipv6_miniroute ( struct ipv6_miniroute *miniroute ) { + netdev_put ( miniroute->netdev ); + list_del ( &miniroute->list ); + free ( miniroute ); +} + +/** + * Add IPv6 interface + * + * @v netdev Network device + * @v prefix Destination prefix + * @v address Address of the interface + * @v gateway Gateway address (or ::0 for no gateway) + */ +int add_ipv6_address ( struct net_device *netdev, struct in6_addr prefix, + int prefix_len, struct in6_addr address, + struct in6_addr gateway ) { + struct ipv6_miniroute *miniroute; + + /* Clear any existing address for this net device */ + del_ipv6_address ( netdev ); + + /* Add new miniroute */ + miniroute = add_ipv6_miniroute ( netdev, prefix, prefix_len, address, + gateway ); + if ( ! miniroute ) + return -ENOMEM; + + return 0; +} + +/** + * Remove IPv6 interface + * + * @v netdev Network device + */ +void del_ipv6_address ( struct net_device *netdev ) { + struct ipv6_miniroute *miniroute; + + list_for_each_entry ( miniroute, &miniroutes, list ) { + if ( miniroute->netdev == netdev ) { + del_ipv6_miniroute ( miniroute ); + break; + } + } +} + +/** + * Calculate TCPIP checksum + * + * @v iobuf I/O buffer + * @v tcpip TCP/IP protocol + * + * This function constructs the pseudo header and completes the checksum in the + * upper layer header. + */ +static uint16_t ipv6_tx_csum ( struct io_buffer *iobuf, uint16_t csum ) { + struct ip6_header *ip6hdr = iobuf->data; + struct ipv6_pseudo_header pshdr; + + /* Calculate pseudo header */ + memset ( &pshdr, 0, sizeof ( pshdr ) ); + pshdr.src = ip6hdr->src; + pshdr.dest = ip6hdr->dest; + pshdr.len = htons ( iob_len ( iobuf ) - sizeof ( *ip6hdr ) ); + pshdr.nxt_hdr = ip6hdr->nxt_hdr; + + /* Update checksum value */ + return tcpip_continue_chksum ( csum, &pshdr, sizeof ( pshdr ) ); +} + +/** + * Dump IP6 header for debugging + * + * ip6hdr IPv6 header + */ +void ipv6_dump ( struct ip6_header *ip6hdr ) { + DBG ( "IP6 %p src %s dest %s nxt_hdr %d len %d\n", ip6hdr, + inet6_ntoa ( ip6hdr->src ), inet6_ntoa ( ip6hdr->dest ), + ip6hdr->nxt_hdr, ntohs ( ip6hdr->payload_len ) ); +} + +/** + * Transmit IP6 packet + * + * iobuf I/O buffer + * tcpip TCP/IP protocol + * st_dest Destination socket address + * + * This function prepends the IPv6 headers to the payload an transmits it. + */ +static int ipv6_tx ( struct io_buffer *iobuf, + struct tcpip_protocol *tcpip, + struct sockaddr_tcpip *st_src __unused, + struct sockaddr_tcpip *st_dest, + struct net_device *netdev, + uint16_t *trans_csum ) { + struct sockaddr_in6 *dest = ( struct sockaddr_in6* ) st_dest; + struct in6_addr next_hop; + struct ipv6_miniroute *miniroute; + uint8_t ll_dest_buf[MAX_LL_ADDR_LEN]; + const uint8_t *ll_dest = ll_dest_buf; + int rc; + + /* Construct the IPv6 packet */ + struct ip6_header *ip6hdr = iob_push ( iobuf, sizeof ( *ip6hdr ) ); + memset ( ip6hdr, 0, sizeof ( *ip6hdr) ); + ip6hdr->ver_traffic_class_flow_label = htonl ( 0x60000000 );//IP6_VERSION; + ip6hdr->payload_len = htons ( iob_len ( iobuf ) - sizeof ( *ip6hdr ) ); + ip6hdr->nxt_hdr = tcpip->tcpip_proto; + ip6hdr->hop_limit = IP6_HOP_LIMIT; // 255 + + /* Determine the next hop address and interface + * + * TODO: Implement the routing table. + */ + next_hop = dest->sin6_addr; + list_for_each_entry ( miniroute, &miniroutes, list ) { + if ( ( memcmp ( &ip6hdr->dest, &miniroute->prefix, + miniroute->prefix_len ) == 0 ) || + ( IP6_EQUAL ( miniroute->gateway, ip6_none ) ) ) { + netdev = miniroute->netdev; + ip6hdr->src = miniroute->address; + if ( ! ( IS_UNSPECIFIED ( miniroute->gateway ) ) ) { + next_hop = miniroute->gateway; + } + break; + } + } + /* No network interface identified */ + if ( !netdev ) { + DBG ( "No route to host %s\n", inet6_ntoa ( ip6hdr->dest ) ); + rc = -ENETUNREACH; + goto err; + } + + /* Complete the transport layer checksum */ + if ( trans_csum ) + *trans_csum = ipv6_tx_csum ( iobuf, *trans_csum ); + + /* Print IPv6 header */ + ipv6_dump ( ip6hdr ); + + /* Resolve link layer address */ + if ( next_hop.in6_u.u6_addr8[0] == 0xff ) { + ll_dest_buf[0] = 0x33; + ll_dest_buf[1] = 0x33; + ll_dest_buf[2] = next_hop.in6_u.u6_addr8[12]; + ll_dest_buf[3] = next_hop.in6_u.u6_addr8[13]; + ll_dest_buf[4] = next_hop.in6_u.u6_addr8[14]; + ll_dest_buf[5] = next_hop.in6_u.u6_addr8[15]; + } else { + /* Unicast address needs to be resolved by NDP */ + if ( ( rc = ndp_resolve ( netdev, &next_hop, &ip6hdr->src, + ll_dest_buf ) ) != 0 ) { + DBG ( "No entry for %s\n", inet6_ntoa ( next_hop ) ); + goto err; + } + } + + /* Transmit packet */ + return net_tx ( iobuf, netdev, &ipv6_protocol, ll_dest ); + + err: + free_iob ( iobuf ); + return rc; +} + +/** + * Process next IP6 header + * + * @v iobuf I/O buffer + * @v nxt_hdr Next header number + * @v src Source socket address + * @v dest Destination socket address + * + * Refer http://www.iana.org/assignments/ipv6-parameters for the numbers + */ +static int ipv6_process_nxt_hdr ( struct io_buffer *iobuf, uint8_t nxt_hdr, + struct sockaddr_tcpip *src, struct sockaddr_tcpip *dest ) { + switch ( nxt_hdr ) { + case IP6_HOPBYHOP: + case IP6_ROUTING: + case IP6_FRAGMENT: + case IP6_AUTHENTICATION: + case IP6_DEST_OPTS: + case IP6_ESP: + DBG ( "Function not implemented for header %d\n", nxt_hdr ); + return -ENOSYS; + case IP6_ICMP6: + break; + case IP6_NO_HEADER: + DBG ( "No next header\n" ); + return 0; + } + /* Next header is not a IPv6 extension header */ + return tcpip_rx ( iobuf, nxt_hdr, src, dest, 0 /* fixme */ ); +} + +/** + * Process incoming IP6 packets + * + * @v iobuf I/O buffer + * @v netdev Network device + * @v ll_source Link-layer source address + * + * This function processes a IPv6 packet + */ +static int ipv6_rx ( struct io_buffer *iobuf, + __unused struct net_device *netdev, + __unused const void *ll_source ) { + + struct ip6_header *ip6hdr = iobuf->data; + union { + struct sockaddr_in6 sin6; + struct sockaddr_tcpip st; + } src, dest; + + /* Sanity check */ + if ( iob_len ( iobuf ) < sizeof ( *ip6hdr ) ) { + DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) ); + goto drop; + } + + /* TODO: Verify checksum */ + + /* Print IP6 header for debugging */ + ipv6_dump ( ip6hdr ); + + /* Check header version */ + if ( ( ip6hdr->ver_traffic_class_flow_label & 0xf0000000 ) != 0x60000000 ) { + DBG ( "Invalid protocol version\n" ); + goto drop; + } + + /* Check the payload length */ + if ( ntohs ( ip6hdr->payload_len ) > iob_len ( iobuf ) ) { + DBG ( "Inconsistent packet length (%d bytes)\n", + ip6hdr->payload_len ); + goto drop; + } + + /* Ignore the traffic class and flow control values */ + + /* Construct socket address */ + memset ( &src, 0, sizeof ( src ) ); + src.sin6.sin_family = AF_INET6; + src.sin6.sin6_addr = ip6hdr->src; + memset ( &dest, 0, sizeof ( dest ) ); + dest.sin6.sin_family = AF_INET6; + dest.sin6.sin6_addr = ip6hdr->dest; + + /* Strip header */ + iob_unput ( iobuf, iob_len ( iobuf ) - ntohs ( ip6hdr->payload_len ) - + sizeof ( *ip6hdr ) ); + iob_pull ( iobuf, sizeof ( *ip6hdr ) ); + + /* Send it to the transport layer */ + return ipv6_process_nxt_hdr ( iobuf, ip6hdr->nxt_hdr, &src.st, &dest.st ); + + drop: + DBG ( "Packet dropped\n" ); + free_iob ( iobuf ); + return -1; +} + +/** + * Print a IP6 address as xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx + */ +char * inet6_ntoa ( struct in6_addr in6 ) { + static char buf[40]; + uint16_t *bytes = ( uint16_t* ) &in6; + sprintf ( buf, "%x:%x:%x:%x:%x:%x:%x:%x", bytes[0], bytes[1], bytes[2], + bytes[3], bytes[4], bytes[5], bytes[6], bytes[7] ); + return buf; +} + +static const char * ipv6_ntoa ( const void *net_addr ) { + return inet6_ntoa ( * ( ( struct in6_addr * ) net_addr ) ); +} + +/** IPv6 protocol */ +struct net_protocol ipv6_protocol __net_protocol = { + .name = "IPv6", + .net_proto = htons ( ETH_P_IPV6 ), + .net_addr_len = sizeof ( struct in6_addr ), + .rx = ipv6_rx, + .ntoa = ipv6_ntoa, +}; + +/** IPv6 TCPIP net protocol */ +struct tcpip_net_protocol ipv6_tcpip_protocol __tcpip_net_protocol = { + .name = "IPv6", + .sa_family = AF_INET6, + .tx = ipv6_tx, +}; diff --git a/gpxe/src/net/ndp.c b/gpxe/src/net/ndp.c new file mode 100644 index 0000000..8bea8b3 --- /dev/null +++ b/gpxe/src/net/ndp.c @@ -0,0 +1,180 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Neighbour Discovery Protocol + * + * This file implements address resolution as specified by the neighbour + * discovery protocol in RFC2461. This protocol is part of the IPv6 protocol + * family. + */ + +/* A neighbour entry */ +struct ndp_entry { + /** Target IP6 address */ + struct in6_addr in6; + /** Link layer protocol */ + struct ll_protocol *ll_protocol; + /** Link-layer address */ + uint8_t ll_addr[MAX_LL_ADDR_LEN]; + /** State of the neighbour entry */ + int state; +}; + +/** Number of entries in the neighbour cache table */ +#define NUM_NDP_ENTRIES 4 + +/** The neighbour cache table */ +static struct ndp_entry ndp_table[NUM_NDP_ENTRIES]; +#define ndp_table_end &ndp_table[NUM_NDP_ENTRIES] + +static unsigned int next_new_ndp_entry = 0; + +/** + * Find entry in the neighbour cache + * + * @v in6 IP6 address + */ +static struct ndp_entry * +ndp_find_entry ( struct in6_addr *in6 ) { + struct ndp_entry *ndp; + + for ( ndp = ndp_table ; ndp < ndp_table_end ; ndp++ ) { + if ( IP6_EQUAL ( ( *in6 ), ndp->in6 ) && + ( ndp->state != NDP_STATE_INVALID ) ) { + return ndp; + } + } + return NULL; +} + +/** + * Add NDP entry + * + * @v netdev Network device + * @v in6 IP6 address + * @v ll_addr Link-layer address + * @v state State of the entry - one of the NDP_STATE_XXX values + */ +static void +add_ndp_entry ( struct net_device *netdev, struct in6_addr *in6, + void *ll_addr, int state ) { + struct ndp_entry *ndp; + ndp = &ndp_table[next_new_ndp_entry++ % NUM_NDP_ENTRIES]; + + /* Fill up entry */ + ndp->ll_protocol = netdev->ll_protocol; + memcpy ( &ndp->in6, &( *in6 ), sizeof ( *in6 ) ); + if ( ll_addr ) { + memcpy ( ndp->ll_addr, ll_addr, netdev->ll_protocol->ll_addr_len ); + } else { + memset ( ndp->ll_addr, 0, netdev->ll_protocol->ll_addr_len ); + } + ndp->state = state; + DBG ( "New neighbour cache entry: IP6 %s => %s %s\n", + inet6_ntoa ( ndp->in6 ), netdev->ll_protocol->name, + netdev->ll_protocol->ntoa ( ndp->ll_addr ) ); +} + +/** + * Resolve the link-layer address + * + * @v netdev Network device + * @v dest Destination address + * @v src Source address + * @ret dest_ll_addr Destination link-layer address or NULL + * @ret rc Status + * + * This function looks up the neighbour cache for an entry corresponding to the + * destination address. If it finds a valid entry, it fills up dest_ll_addr and + * returns 0. Otherwise it sends a neighbour solicitation to the solicited + * multicast address. + */ +int ndp_resolve ( struct net_device *netdev, struct in6_addr *dest, + struct in6_addr *src, void *dest_ll_addr ) { + struct ll_protocol *ll_protocol = netdev->ll_protocol; + struct ndp_entry *ndp; + int rc; + + ndp = ndp_find_entry ( dest ); + /* Check if the entry is valid */ + if ( ndp && ndp->state == NDP_STATE_REACHABLE ) { + DBG ( "Neighbour cache hit: IP6 %s => %s %s\n", + inet6_ntoa ( *dest ), ll_protocol->name, + ll_protocol->ntoa ( ndp->ll_addr ) ); + memcpy ( dest_ll_addr, ndp->ll_addr, ll_protocol->ll_addr_len ); + return 0; + } + + /* Check if the entry was already created */ + if ( ndp ) { + DBG ( "Awaiting neighbour advertisement\n" ); + /* For test */ +// ndp->state = NDP_STATE_REACHABLE; +// memcpy ( ndp->ll_addr, netdev->ll_addr, 6 ); +// assert ( ndp->ll_protocol->ll_addr_len == 6 ); +// icmp6_test_nadvert ( netdev, dest, ndp->ll_addr ); +// assert ( ndp->state == NDP_STATE_REACHABLE ); + /* Take it out till here */ + return -ENOENT; + } + DBG ( "Neighbour cache miss: IP6 %s\n", inet6_ntoa ( *dest ) ); + + /* Add entry in the neighbour cache */ + add_ndp_entry ( netdev, dest, NULL, NDP_STATE_INCOMPLETE ); + + /* Send neighbour solicitation */ + if ( ( rc = icmp6_send_solicit ( netdev, src, dest ) ) != 0 ) { + return rc; + } + return -ENOENT; +} + +/** + * Process neighbour advertisement + * + * @v iobuf I/O buffer + * @v st_src Source address + * @v st_dest Destination address + */ +int ndp_process_advert ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src __unused, + struct sockaddr_tcpip *st_dest __unused ) { + struct neighbour_advert *nadvert = iobuf->data; + struct ndp_entry *ndp; + + /* Sanity check */ + if ( iob_len ( iobuf ) < sizeof ( *nadvert ) ) { + DBG ( "Packet too short (%zd bytes)\n", iob_len ( iobuf ) ); + return -EINVAL; + } + + assert ( nadvert->code == 0 ); + assert ( nadvert->flags & ICMP6_FLAGS_SOLICITED ); + assert ( nadvert->opt_type == 2 ); + + /* Update the neighbour cache, if entry is present */ + ndp = ndp_find_entry ( &nadvert->target ); + if ( ndp ) { + + assert ( nadvert->opt_len == + ( ( 2 + ndp->ll_protocol->ll_addr_len ) / 8 ) ); + + if ( IP6_EQUAL ( ndp->in6, nadvert->target ) ) { + memcpy ( ndp->ll_addr, nadvert->opt_ll_addr, + ndp->ll_protocol->ll_addr_len ); + ndp->state = NDP_STATE_REACHABLE; + return 0; + } + } + DBG ( "Unsolicited advertisement (dropping packet)\n" ); + return 0; +} diff --git a/gpxe/src/net/netdev_settings.c b/gpxe/src/net/netdev_settings.c new file mode 100644 index 0000000..44aca7d --- /dev/null +++ b/gpxe/src/net/netdev_settings.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include +#include +#include + +/** @file + * + * Network device configuration settings + * + */ + +/** Network device named settings */ +struct setting mac_setting __setting = { + .name = "mac", + .description = "MAC address", + .type = &setting_type_hex, +}; + +/** + * Store value of network device setting + * + * @v settings Settings block + * @v setting Setting to store + * @v data Setting data, or NULL to clear setting + * @v len Length of setting data + * @ret rc Return status code + */ +static int netdev_store ( struct settings *settings, struct setting *setting, + const void *data, size_t len ) { + struct net_device *netdev = container_of ( settings, struct net_device, + settings.settings ); + + if ( setting_cmp ( setting, &mac_setting ) == 0 ) { + if ( len != netdev->ll_protocol->ll_addr_len ) + return -EINVAL; + memcpy ( netdev->ll_addr, data, len ); + return 0; + } else { + return simple_settings_store ( settings, setting, data, len ); + } +} + +/** + * Fetch value of network device setting + * + * @v settings Settings block + * @v setting Setting to fetch + * @v data Setting data, or NULL to clear setting + * @v len Length of setting data + * @ret rc Return status code + */ +static int netdev_fetch ( struct settings *settings, struct setting *setting, + void *data, size_t len ) { + struct net_device *netdev = container_of ( settings, struct net_device, + settings.settings ); + + if ( setting_cmp ( setting, &mac_setting ) == 0 ) { + if ( len > netdev->ll_protocol->ll_addr_len ) + len = netdev->ll_protocol->ll_addr_len; + memcpy ( data, netdev->ll_addr, len ); + return netdev->ll_protocol->ll_addr_len; + } else { + return simple_settings_fetch ( settings, setting, data, len ); + } +} + +/** Network device configuration settings operations */ +struct settings_operations netdev_settings_operations = { + .store = netdev_store, + .fetch = netdev_fetch, +}; diff --git a/gpxe/src/net/netdevice.c b/gpxe/src/net/netdevice.c new file mode 100644 index 0000000..9e142d2 --- /dev/null +++ b/gpxe/src/net/netdevice.c @@ -0,0 +1,603 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Network device management + * + */ + +/** Registered network-layer protocols */ +static struct net_protocol net_protocols[0] + __table_start ( struct net_protocol, net_protocols ); +static struct net_protocol net_protocols_end[0] + __table_end ( struct net_protocol, net_protocols ); + +/** List of network devices */ +struct list_head net_devices = LIST_HEAD_INIT ( net_devices ); + +/** List of open network devices, in reverse order of opening */ +struct list_head open_net_devices = LIST_HEAD_INIT ( open_net_devices ); + +/** + * Record network device statistic + * + * @v stats Network device statistics + * @v rc Status code + */ +static void netdev_record_stat ( struct net_device_stats *stats, int rc ) { + struct net_device_error *error; + struct net_device_error *least_common_error; + unsigned int i; + + /* If this is not an error, just update the good counter */ + if ( rc == 0 ) { + stats->good++; + return; + } + + /* Update the bad counter */ + stats->bad++; + + /* Locate the appropriate error record */ + least_common_error = &stats->errors[0]; + for ( i = 0 ; i < ( sizeof ( stats->errors ) / + sizeof ( stats->errors[0] ) ) ; i++ ) { + error = &stats->errors[i]; + /* Update matching record, if found */ + if ( error->rc == rc ) { + error->count++; + return; + } + if ( error->count < least_common_error->count ) + least_common_error = error; + } + + /* Overwrite the least common error record */ + least_common_error->rc = rc; + least_common_error->count = 1; +} + +/** + * Transmit raw packet via network device + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + * + * Transmits the packet via the specified network device. This + * function takes ownership of the I/O buffer. + */ +int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf ) { + int rc; + + DBGC ( netdev, "NETDEV %p transmitting %p (%p+%zx)\n", + netdev, iobuf, iobuf->data, iob_len ( iobuf ) ); + + list_add_tail ( &iobuf->list, &netdev->tx_queue ); + + if ( ! ( netdev->state & NETDEV_OPEN ) ) { + rc = -ENETUNREACH; + goto err; + } + + if ( ( rc = netdev->op->transmit ( netdev, iobuf ) ) != 0 ) + goto err; + + return 0; + + err: + netdev_tx_complete_err ( netdev, iobuf, rc ); + return rc; +} + +/** + * Complete network transmission + * + * @v netdev Network device + * @v iobuf I/O buffer + * @v rc Packet status code + * + * The packet must currently be in the network device's TX queue. + */ +void netdev_tx_complete_err ( struct net_device *netdev, + struct io_buffer *iobuf, int rc ) { + + /* Update statistics counter */ + netdev_record_stat ( &netdev->tx_stats, rc ); + if ( rc == 0 ) { + DBGC ( netdev, "NETDEV %p transmission %p complete\n", + netdev, iobuf ); + } else { + DBGC ( netdev, "NETDEV %p transmission %p failed: %s\n", + netdev, iobuf, strerror ( rc ) ); + } + + /* Catch data corruption as early as possible */ + assert ( iobuf->list.next != NULL ); + assert ( iobuf->list.prev != NULL ); + + /* Dequeue and free I/O buffer */ + list_del ( &iobuf->list ); + free_iob ( iobuf ); +} + +/** + * Complete network transmission + * + * @v netdev Network device + * @v rc Packet status code + * + * Completes the oldest outstanding packet in the TX queue. + */ +void netdev_tx_complete_next_err ( struct net_device *netdev, int rc ) { + struct io_buffer *iobuf; + + list_for_each_entry ( iobuf, &netdev->tx_queue, list ) { + netdev_tx_complete_err ( netdev, iobuf, rc ); + return; + } +} + +/** + * Flush device's transmit queue + * + * @v netdev Network device + */ +static void netdev_tx_flush ( struct net_device *netdev ) { + + /* Discard any packets in the TX queue */ + while ( ! list_empty ( &netdev->tx_queue ) ) { + netdev_tx_complete_next_err ( netdev, -ECANCELED ); + } +} + +/** + * Add packet to receive queue + * + * @v netdev Network device + * @v iobuf I/O buffer, or NULL + * + * The packet is added to the network device's RX queue. This + * function takes ownership of the I/O buffer. + */ +void netdev_rx ( struct net_device *netdev, struct io_buffer *iobuf ) { + + DBGC ( netdev, "NETDEV %p received %p (%p+%zx)\n", + netdev, iobuf, iobuf->data, iob_len ( iobuf ) ); + + /* Enqueue packet */ + list_add_tail ( &iobuf->list, &netdev->rx_queue ); + + /* Update statistics counter */ + netdev_record_stat ( &netdev->rx_stats, 0 ); +} + +/** + * Discard received packet + * + * @v netdev Network device + * @v iobuf I/O buffer, or NULL + * @v rc Packet status code + * + * The packet is discarded and an RX error is recorded. This function + * takes ownership of the I/O buffer. @c iobuf may be NULL if, for + * example, the net device wishes to report an error due to being + * unable to allocate an I/O buffer. + */ +void netdev_rx_err ( struct net_device *netdev, + struct io_buffer *iobuf, int rc ) { + + DBGC ( netdev, "NETDEV %p failed to receive %p: %s\n", + netdev, iobuf, strerror ( rc ) ); + + /* Discard packet */ + free_iob ( iobuf ); + + /* Update statistics counter */ + netdev_record_stat ( &netdev->rx_stats, rc ); +} + +/** + * Poll for completed and received packets on network device + * + * @v netdev Network device + * + * Polls the network device for completed transmissions and received + * packets. Any received packets will be added to the RX packet queue + * via netdev_rx(). + */ +void netdev_poll ( struct net_device *netdev ) { + + if ( netdev->state & NETDEV_OPEN ) + netdev->op->poll ( netdev ); +} + +/** + * Remove packet from device's receive queue + * + * @v netdev Network device + * @ret iobuf I/O buffer, or NULL + * + * Removes the first packet from the device's RX queue and returns it. + * Ownership of the packet is transferred to the caller. + */ +struct io_buffer * netdev_rx_dequeue ( struct net_device *netdev ) { + struct io_buffer *iobuf; + + list_for_each_entry ( iobuf, &netdev->rx_queue, list ) { + list_del ( &iobuf->list ); + return iobuf; + } + return NULL; +} + +/** + * Flush device's receive queue + * + * @v netdev Network device + */ +static void netdev_rx_flush ( struct net_device *netdev ) { + struct io_buffer *iobuf; + + /* Discard any packets in the RX queue */ + while ( ( iobuf = netdev_rx_dequeue ( netdev ) ) ) { + netdev_rx_err ( netdev, iobuf, -ECANCELED ); + } +} + +/** + * Free network device + * + * @v refcnt Network device reference counter + */ +static void free_netdev ( struct refcnt *refcnt ) { + struct net_device *netdev = + container_of ( refcnt, struct net_device, refcnt ); + + netdev_tx_flush ( netdev ); + netdev_rx_flush ( netdev ); + free ( netdev ); +} + +/** + * Allocate network device + * + * @v priv_size Size of private data area (net_device::priv) + * @ret netdev Network device, or NULL + * + * Allocates space for a network device and its private data area. + */ +struct net_device * alloc_netdev ( size_t priv_size ) { + struct net_device *netdev; + size_t total_len; + + total_len = ( sizeof ( *netdev ) + priv_size ); + netdev = zalloc ( total_len ); + if ( netdev ) { + netdev->refcnt.free = free_netdev; + INIT_LIST_HEAD ( &netdev->tx_queue ); + INIT_LIST_HEAD ( &netdev->rx_queue ); + settings_init ( netdev_settings ( netdev ), + &netdev_settings_operations, &netdev->refcnt, + netdev->name, 0 ); + netdev->priv = ( ( ( void * ) netdev ) + sizeof ( *netdev ) ); + } + return netdev; +} + +/** + * Register network device + * + * @v netdev Network device + * @ret rc Return status code + * + * Gives the network device a name and adds it to the list of network + * devices. + */ +int register_netdev ( struct net_device *netdev ) { + static unsigned int ifindex = 0; + int rc; + + /* Create device name */ + snprintf ( netdev->name, sizeof ( netdev->name ), "net%d", + ifindex++ ); + + /* Register per-netdev configuration settings */ + if ( ( rc = register_settings ( netdev_settings ( netdev ), + NULL ) ) != 0 ) { + DBGC ( netdev, "NETDEV %p could not register settings: %s\n", + netdev, strerror ( rc ) ); + return rc; + } + + /* Add to device list */ + netdev_get ( netdev ); + list_add_tail ( &netdev->list, &net_devices ); + DBGC ( netdev, "NETDEV %p registered as %s (phys %s hwaddr %s)\n", + netdev, netdev->name, netdev->dev->name, + netdev_hwaddr ( netdev ) ); + + return 0; +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +int netdev_open ( struct net_device *netdev ) { + int rc; + + /* Do nothing if device is already open */ + if ( netdev->state & NETDEV_OPEN ) + return 0; + + DBGC ( netdev, "NETDEV %p opening\n", netdev ); + + /* Open the device */ + if ( ( rc = netdev->op->open ( netdev ) ) != 0 ) + return rc; + + /* Mark as opened */ + netdev->state |= NETDEV_OPEN; + + /* Add to head of open devices list */ + list_add ( &netdev->open_list, &open_net_devices ); + + return 0; +} + +/** + * Close network device + * + * @v netdev Network device + */ +void netdev_close ( struct net_device *netdev ) { + + /* Do nothing if device is already closed */ + if ( ! ( netdev->state & NETDEV_OPEN ) ) + return; + + DBGC ( netdev, "NETDEV %p closing\n", netdev ); + + /* Close the device */ + netdev->op->close ( netdev ); + + /* Flush TX and RX queues */ + netdev_tx_flush ( netdev ); + netdev_rx_flush ( netdev ); + + /* Mark as closed */ + netdev->state &= ~NETDEV_OPEN; + + /* Remove from open devices list */ + list_del ( &netdev->open_list ); +} + +/** + * Unregister network device + * + * @v netdev Network device + * + * Removes the network device from the list of network devices. + */ +void unregister_netdev ( struct net_device *netdev ) { + + /* Ensure device is closed */ + netdev_close ( netdev ); + + /* Unregister per-netdev configuration settings */ + unregister_settings ( netdev_settings ( netdev ) ); + + /* Remove from device list */ + list_del ( &netdev->list ); + netdev_put ( netdev ); + DBGC ( netdev, "NETDEV %p unregistered\n", netdev ); +} + +/** Enable or disable interrupts + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ +void netdev_irq ( struct net_device *netdev, int enable ) { + netdev->op->irq ( netdev, enable ); +} + +/** + * Get network device by name + * + * @v name Network device name + * @ret netdev Network device, or NULL + */ +struct net_device * find_netdev ( const char *name ) { + struct net_device *netdev; + + list_for_each_entry ( netdev, &net_devices, list ) { + if ( strcmp ( netdev->name, name ) == 0 ) + return netdev; + } + + return NULL; +} + +/** + * Get network device by PCI bus:dev.fn address + * + * @v bus_type Bus type + * @v location Bus location + * @ret netdev Network device, or NULL + */ +struct net_device * find_netdev_by_location ( unsigned int bus_type, + unsigned int location ) { + struct net_device *netdev; + + list_for_each_entry ( netdev, &net_devices, list ) { + if ( ( netdev->dev->desc.bus_type == bus_type ) && + ( netdev->dev->desc.location == location ) ) + return netdev; + } + + return NULL; +} + +/** + * Get most recently opened network device + * + * @ret netdev Most recently opened network device, or NULL + */ +struct net_device * last_opened_netdev ( void ) { + struct net_device *netdev; + + list_for_each_entry ( netdev, &open_net_devices, open_list ) { + assert ( netdev->state & NETDEV_OPEN ); + return netdev; + } + + return NULL; +} + +/** + * Transmit network-layer packet + * + * @v iobuf I/O buffer + * @v netdev Network device + * @v net_protocol Network-layer protocol + * @v ll_dest Destination link-layer address + * @ret rc Return status code + * + * Prepends link-layer headers to the I/O buffer and transmits the + * packet via the specified network device. This function takes + * ownership of the I/O buffer. + */ +int net_tx ( struct io_buffer *iobuf, struct net_device *netdev, + struct net_protocol *net_protocol, const void *ll_dest ) { + struct ll_protocol *ll_protocol = netdev->ll_protocol; + int rc; + + /* Force a poll on the netdevice to (potentially) clear any + * backed-up TX completions. This is needed on some network + * devices to avoid excessive losses due to small TX ring + * sizes. + */ + netdev_poll ( netdev ); + + /* Add link-layer header */ + if ( ( rc = ll_protocol->push ( iobuf, ll_dest, netdev->ll_addr, + net_protocol->net_proto ) ) != 0 ) { + free_iob ( iobuf ); + return rc; + } + + /* Transmit packet */ + return netdev_tx ( netdev, iobuf ); +} + +/** + * Process received network-layer packet + * + * @v iobuf I/O buffer + * @v netdev Network device + * @v net_proto Network-layer protocol, in network-byte order + * @v ll_source Source link-layer address + * @ret rc Return status code + */ +int net_rx ( struct io_buffer *iobuf, struct net_device *netdev, + uint16_t net_proto, const void *ll_source ) { + struct net_protocol *net_protocol; + + /* Hand off to network-layer protocol, if any */ + for ( net_protocol = net_protocols ; net_protocol < net_protocols_end ; + net_protocol++ ) { + if ( net_protocol->net_proto == net_proto ) { + return net_protocol->rx ( iobuf, netdev, ll_source ); + } + } + free_iob ( iobuf ); + return 0; +} + +/** + * Single-step the network stack + * + * @v process Network stack process + * + * This polls all interfaces for received packets, and processes + * packets from the RX queue. + */ +static void net_step ( struct process *process __unused ) { + struct net_device *netdev; + struct io_buffer *iobuf; + struct ll_protocol *ll_protocol; + const void *ll_dest; + const void *ll_source; + uint16_t net_proto; + int rc; + + /* Poll and process each network device */ + list_for_each_entry ( netdev, &net_devices, list ) { + + /* Poll for new packets */ + netdev_poll ( netdev ); + + /* Process at most one received packet. Give priority + * to getting packets out of the NIC over processing + * the received packets, because we advertise a window + * that assumes that we can receive packets from the + * NIC faster than they arrive. + */ + if ( ( iobuf = netdev_rx_dequeue ( netdev ) ) ) { + + DBGC ( netdev, "NETDEV %p processing %p (%p+%zx)\n", + netdev, iobuf, iobuf->data, + iob_len ( iobuf ) ); + + /* Remove link-layer header */ + ll_protocol = netdev->ll_protocol; + if ( ( rc = ll_protocol->pull ( iobuf, &ll_dest, + &ll_source, + &net_proto ) ) != 0 ) { + free_iob ( iobuf ); + continue; + } + + net_rx ( iobuf, netdev, net_proto, ll_source ); + } + } +} + +/** Networking stack process */ +struct process net_process __permanent_process = { + .step = net_step, +}; diff --git a/gpxe/src/net/nullnet.c b/gpxe/src/net/nullnet.c new file mode 100644 index 0000000..7e199ce --- /dev/null +++ b/gpxe/src/net/nullnet.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include + +/** @file + * + * Null network device + * + */ + +static int null_open ( struct net_device *netdev __unused ) { + return -ENODEV; +}; + +static void null_close ( struct net_device *netdev __unused ) { + /* Do nothing */ +}; + +static int null_transmit ( struct net_device *netdev __unused, + struct io_buffer *iobuf __unused ) { + return -ENODEV; +}; + +static void null_poll ( struct net_device *netdev __unused ) { + /* Do nothing */ +} + +static void null_irq ( struct net_device *netdev __unused, + int enable __unused ) { + /* Do nothing */ +} + +struct net_device_operations null_netdev_operations = { + .open = null_open, + .close = null_close, + .transmit = null_transmit, + .poll = null_poll, + .irq = null_irq, +}; diff --git a/gpxe/src/net/rarp.c b/gpxe/src/net/rarp.c new file mode 100644 index 0000000..bb5e6ad --- /dev/null +++ b/gpxe/src/net/rarp.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include +#include + +/** @file + * + * Reverse Address Resolution Protocol + * + */ + +/** + * Process incoming ARP packets + * + * @v iobuf I/O buffer + * @v netdev Network device + * @v ll_source Link-layer source address + * @ret rc Return status code + * + * This is a dummy method which simply discards RARP packets. + */ +static int rarp_rx ( struct io_buffer *iobuf, + struct net_device *netdev __unused, + const void *ll_source __unused ) { + free_iob ( iobuf ); + return 0; +} + + +/** + * Transcribe RARP address + * + * @v net_addr RARP address + * @ret string "" + * + * This operation is meaningless for the RARP protocol. + */ +static const char * rarp_ntoa ( const void *net_addr __unused ) { + return ""; +} + +/** RARP protocol */ +struct net_protocol rarp_protocol __net_protocol = { + .name = "RARP", + .net_proto = htons ( ETH_P_RARP ), + .rx = rarp_rx, + .ntoa = rarp_ntoa, +}; diff --git a/gpxe/src/net/retry.c b/gpxe/src/net/retry.c new file mode 100644 index 0000000..cd793a7 --- /dev/null +++ b/gpxe/src/net/retry.c @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include + +/** @file + * + * Retry timers + * + * A retry timer is a binary exponential backoff timer. It can be + * used to build automatic retransmission into network protocols. + * + * This implementation of the timer is designed to satisfy RFC 2988 + * and therefore be usable as a TCP retransmission timer. + * + * + */ + +/* The theoretical minimum that the algorithm in stop_timer() can + * adjust the timeout back down to is seven ticks, so set the minimum + * timeout to at least that value for the sake of consistency. + */ +#define MIN_TIMEOUT 7 + +/** List of running timers */ +static LIST_HEAD ( timers ); + +/** + * Start timer + * + * @v timer Retry timer + * + * This starts the timer running with the current timeout value. If + * stop_timer() is not called before the timer expires, the timer will + * be stopped and the timer's callback function will be called. + */ +void start_timer ( struct retry_timer *timer ) { + if ( ! timer->running ) + list_add ( &timer->list, &timers ); + timer->start = currticks(); + timer->running = 1; + + /* 0 means "use default timeout" */ + if ( timer->min_timeout == 0 ) + timer->min_timeout = DEFAULT_MIN_TIMEOUT; + /* We must never be less than MIN_TIMEOUT under any circumstances */ + if ( timer->min_timeout < MIN_TIMEOUT ) + timer->min_timeout = MIN_TIMEOUT; + /* Honor user-specified minimum timeout */ + if ( timer->timeout < timer->min_timeout ) + timer->timeout = timer->min_timeout; + + DBG2 ( "Timer %p started at time %ld (expires at %ld)\n", + timer, timer->start, ( timer->start + timer->timeout ) ); +} + +/** + * Start timer with a specified fixed timeout + * + * @v timer Retry timer + * @v timeout Timeout, in ticks + */ +void start_timer_fixed ( struct retry_timer *timer, unsigned long timeout ) { + start_timer ( timer ); + timer->timeout = timeout; + DBG2 ( "Timer %p expiry time changed to %ld\n", + timer, ( timer->start + timer->timeout ) ); +} + +/** + * Stop timer + * + * @v timer Retry timer + * + * This stops the timer and updates the timer's timeout value. + */ +void stop_timer ( struct retry_timer *timer ) { + unsigned long old_timeout = timer->timeout; + unsigned long now = currticks(); + unsigned long runtime; + + /* If timer was already stopped, do nothing */ + if ( ! timer->running ) + return; + + list_del ( &timer->list ); + runtime = ( now - timer->start ); + timer->running = 0; + DBG2 ( "Timer %p stopped at time %ld (ran for %ld)\n", + timer, now, runtime ); + + /* Update timer. Variables are: + * + * r = round-trip time estimate (i.e. runtime) + * t = timeout value (i.e. timer->timeout) + * s = smoothed round-trip time + * + * By choice, we set t = 4s, i.e. allow for four times the + * normal round-trip time to pass before retransmitting. + * + * We want to smooth according to s := ( 7 s + r ) / 8 + * + * Since we don't actually store s, this reduces to + * t := ( 7 t / 8 ) + ( r / 2 ) + * + */ + if ( timer->count ) { + timer->count--; + } else { + timer->timeout -= ( timer->timeout >> 3 ); + timer->timeout += ( runtime >> 1 ); + if ( timer->timeout != old_timeout ) { + DBG ( "Timer %p timeout updated to %ld\n", + timer, timer->timeout ); + } + } +} + +/** + * Handle expired timer + * + * @v timer Retry timer + */ +static void timer_expired ( struct retry_timer *timer ) { + int fail; + + /* Stop timer without performing RTT calculations */ + DBG2 ( "Timer %p stopped at time %ld on expiry\n", + timer, currticks() ); + assert ( timer->running ); + list_del ( &timer->list ); + timer->running = 0; + timer->count++; + + /* Back off the timeout value */ + timer->timeout <<= 1; + if ( timer->max_timeout == 0 ) /* 0 means "use default timeout" */ + timer->max_timeout = DEFAULT_MAX_TIMEOUT; + if ( ( fail = ( timer->timeout > timer->max_timeout ) ) ) + timer->timeout = timer->max_timeout; + DBG ( "Timer %p timeout backed off to %ld\n", + timer, timer->timeout ); + + /* Call expiry callback */ + timer->expired ( timer, fail ); +} + +/** + * Single-step the retry timer list + * + * @v process Retry timer process + */ +static void retry_step ( struct process *process __unused ) { + struct retry_timer *timer; + struct retry_timer *tmp; + unsigned long now = currticks(); + unsigned long used; + + list_for_each_entry_safe ( timer, tmp, &timers, list ) { + used = ( now - timer->start ); + if ( used >= timer->timeout ) + timer_expired ( timer ); + } +} + +/** Retry timer process */ +struct process retry_process __permanent_process = { + .step = retry_step, +}; diff --git a/gpxe/src/net/tcp.c b/gpxe/src/net/tcp.c new file mode 100644 index 0000000..6bcd193 --- /dev/null +++ b/gpxe/src/net/tcp.c @@ -0,0 +1,1107 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * TCP protocol + * + */ + +/** A TCP connection */ +struct tcp_connection { + /** Reference counter */ + struct refcnt refcnt; + /** List of TCP connections */ + struct list_head list; + + /** Data transfer interface */ + struct xfer_interface xfer; + /** Data transfer interface closed flag */ + int xfer_closed; + + /** Remote socket address */ + struct sockaddr_tcpip peer; + /** Local port, in network byte order */ + unsigned int local_port; + + /** Current TCP state */ + unsigned int tcp_state; + /** Previous TCP state + * + * Maintained only for debug messages + */ + unsigned int prev_tcp_state; + /** Current sequence number + * + * Equivalent to SND.UNA in RFC 793 terminology. + */ + uint32_t snd_seq; + /** Unacknowledged sequence count + * + * Equivalent to (SND.NXT-SND.UNA) in RFC 793 terminology. + */ + uint32_t snd_sent; + /** Send window + * + * Equivalent to SND.WND in RFC 793 terminology + */ + uint32_t snd_win; + /** Current acknowledgement number + * + * Equivalent to RCV.NXT in RFC 793 terminology. + */ + uint32_t rcv_ack; + /** Receive window + * + * Equivalent to RCV.WND in RFC 793 terminology. + */ + uint32_t rcv_win; + /** Most recent received timestamp + * + * Equivalent to TS.Recent in RFC 1323 terminology. + */ + uint32_t ts_recent; + /** Timestamps enabled */ + int timestamps; + + /** Transmit queue */ + struct list_head queue; + /** Retransmission timer */ + struct retry_timer timer; +}; + +/** + * List of registered TCP connections + */ +static LIST_HEAD ( tcp_conns ); + +/* Forward declarations */ +static struct xfer_interface_operations tcp_xfer_operations; +static void tcp_expired ( struct retry_timer *timer, int over ); +static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack, + uint32_t win ); + +/** + * Name TCP state + * + * @v state TCP state + * @ret name Name of TCP state + */ +static inline __attribute__ (( always_inline )) const char * +tcp_state ( int state ) { + switch ( state ) { + case TCP_CLOSED: return "CLOSED"; + case TCP_LISTEN: return "LISTEN"; + case TCP_SYN_SENT: return "SYN_SENT"; + case TCP_SYN_RCVD: return "SYN_RCVD"; + case TCP_ESTABLISHED: return "ESTABLISHED"; + case TCP_FIN_WAIT_1: return "FIN_WAIT_1"; + case TCP_FIN_WAIT_2: return "FIN_WAIT_2"; + case TCP_CLOSING_OR_LAST_ACK: return "CLOSING/LAST_ACK"; + case TCP_TIME_WAIT: return "TIME_WAIT"; + case TCP_CLOSE_WAIT: return "CLOSE_WAIT"; + default: return "INVALID"; + } +} + +/** + * Dump TCP state transition + * + * @v tcp TCP connection + */ +static inline __attribute__ (( always_inline )) void +tcp_dump_state ( struct tcp_connection *tcp ) { + + if ( tcp->tcp_state != tcp->prev_tcp_state ) { + DBGC ( tcp, "TCP %p transitioned from %s to %s\n", tcp, + tcp_state ( tcp->prev_tcp_state ), + tcp_state ( tcp->tcp_state ) ); + } + tcp->prev_tcp_state = tcp->tcp_state; +} + +/** + * Dump TCP flags + * + * @v flags TCP flags + */ +static inline __attribute__ (( always_inline )) void +tcp_dump_flags ( struct tcp_connection *tcp, unsigned int flags ) { + if ( flags & TCP_RST ) + DBGC ( tcp, " RST" ); + if ( flags & TCP_SYN ) + DBGC ( tcp, " SYN" ); + if ( flags & TCP_PSH ) + DBGC ( tcp, " PSH" ); + if ( flags & TCP_FIN ) + DBGC ( tcp, " FIN" ); + if ( flags & TCP_ACK ) + DBGC ( tcp, " ACK" ); +} + +/*************************************************************************** + * + * Open and close + * + *************************************************************************** + */ + +/** + * Bind TCP connection to local port + * + * @v tcp TCP connection + * @v port Local port number, in network-endian order + * @ret rc Return status code + * + * If the port is 0, the connection is assigned an available port + * between 1024 and 65535. + */ +static int tcp_bind ( struct tcp_connection *tcp, unsigned int port ) { + struct tcp_connection *existing; + static uint16_t try_port = 1023; + + /* If no port specified, find the first available port */ + if ( ! port ) { + while ( try_port ) { + try_port++; + if ( try_port < 1024 ) + continue; + if ( tcp_bind ( tcp, htons ( try_port ) ) == 0 ) + return 0; + } + DBGC ( tcp, "TCP %p could not bind: no free ports\n", tcp ); + return -EADDRINUSE; + } + + /* Attempt bind to local port */ + list_for_each_entry ( existing, &tcp_conns, list ) { + if ( existing->local_port == port ) { + DBGC ( tcp, "TCP %p could not bind: port %d in use\n", + tcp, ntohs ( port ) ); + return -EADDRINUSE; + } + } + tcp->local_port = port; + + DBGC ( tcp, "TCP %p bound to port %d\n", tcp, ntohs ( port ) ); + return 0; +} + +/** + * Open a TCP connection + * + * @v xfer Data transfer interface + * @v peer Peer socket address + * @v local Local socket address, or NULL + * @ret rc Return status code + */ +static int tcp_open ( struct xfer_interface *xfer, struct sockaddr *peer, + struct sockaddr *local ) { + struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer; + struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local; + struct tcp_connection *tcp; + unsigned int bind_port; + int rc; + + /* Allocate and initialise structure */ + tcp = zalloc ( sizeof ( *tcp ) ); + if ( ! tcp ) + return -ENOMEM; + DBGC ( tcp, "TCP %p allocated\n", tcp ); + xfer_init ( &tcp->xfer, &tcp_xfer_operations, &tcp->refcnt ); + tcp->prev_tcp_state = TCP_CLOSED; + tcp->tcp_state = TCP_STATE_SENT ( TCP_SYN ); + tcp_dump_state ( tcp ); + tcp->snd_seq = random(); + INIT_LIST_HEAD ( &tcp->queue ); + tcp->timer.expired = tcp_expired; + memcpy ( &tcp->peer, st_peer, sizeof ( tcp->peer ) ); + + /* Bind to local port */ + bind_port = ( st_local ? st_local->st_port : 0 ); + if ( ( rc = tcp_bind ( tcp, bind_port ) ) != 0 ) + goto err; + + /* Start timer to initiate SYN */ + start_timer_nodelay ( &tcp->timer ); + + /* Attach parent interface, transfer reference to connection + * list and return + */ + xfer_plug_plug ( &tcp->xfer, xfer ); + list_add ( &tcp->list, &tcp_conns ); + return 0; + + err: + ref_put ( &tcp->refcnt ); + return rc; +} + +/** + * Close TCP connection + * + * @v tcp TCP connection + * @v rc Reason for close + * + * Closes the data transfer interface. If the TCP state machine is in + * a suitable state, the connection will be deleted. + */ +static void tcp_close ( struct tcp_connection *tcp, int rc ) { + struct io_buffer *iobuf; + struct io_buffer *tmp; + + /* Close data transfer interface */ + xfer_nullify ( &tcp->xfer ); + xfer_close ( &tcp->xfer, rc ); + tcp->xfer_closed = 1; + + /* If we are in CLOSED, or have otherwise not yet received a + * SYN (i.e. we are in LISTEN or SYN_SENT), just delete the + * connection. + */ + if ( ! ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) ) { + + /* Transition to CLOSED for the sake of debugging messages */ + tcp->tcp_state = TCP_CLOSED; + tcp_dump_state ( tcp ); + + /* Free any unsent I/O buffers */ + list_for_each_entry_safe ( iobuf, tmp, &tcp->queue, list ) { + list_del ( &iobuf->list ); + free_iob ( iobuf ); + } + + /* Remove from list and drop reference */ + stop_timer ( &tcp->timer ); + list_del ( &tcp->list ); + ref_put ( &tcp->refcnt ); + DBGC ( tcp, "TCP %p connection deleted\n", tcp ); + return; + } + + /* If we have not had our SYN acknowledged (i.e. we are in + * SYN_RCVD), pretend that it has been acknowledged so that we + * can send a FIN without breaking things. + */ + if ( ! ( tcp->tcp_state & TCP_STATE_ACKED ( TCP_SYN ) ) ) + tcp_rx_ack ( tcp, ( tcp->snd_seq + 1 ), 0 ); + + /* If we have no data remaining to send, start sending FIN */ + if ( list_empty ( &tcp->queue ) ) { + tcp->tcp_state |= TCP_STATE_SENT ( TCP_FIN ); + tcp_dump_state ( tcp ); + } +} + +/*************************************************************************** + * + * Transmit data path + * + *************************************************************************** + */ + +/** + * Calculate transmission window + * + * @v tcp TCP connection + * @ret len Maximum length that can be sent in a single packet + */ +static size_t tcp_xmit_win ( struct tcp_connection *tcp ) { + size_t len; + + /* Not ready if we're not in a suitable connection state */ + if ( ! TCP_CAN_SEND_DATA ( tcp->tcp_state ) ) + return 0; + + /* Length is the minimum of the receiver's window and the path MTU */ + len = tcp->snd_win; + if ( len > TCP_PATH_MTU ) + len = TCP_PATH_MTU; + + return len; +} + +/** + * Process TCP transmit queue + * + * @v tcp TCP connection + * @v max_len Maximum length to process + * @v dest I/O buffer to fill with data, or NULL + * @v remove Remove data from queue + * @ret len Length of data processed + * + * This processes at most @c max_len bytes from the TCP connection's + * transmit queue. Data will be copied into the @c dest I/O buffer + * (if provided) and, if @c remove is true, removed from the transmit + * queue. + */ +static size_t tcp_process_queue ( struct tcp_connection *tcp, size_t max_len, + struct io_buffer *dest, int remove ) { + struct io_buffer *iobuf; + struct io_buffer *tmp; + size_t frag_len; + size_t len = 0; + + list_for_each_entry_safe ( iobuf, tmp, &tcp->queue, list ) { + frag_len = iob_len ( iobuf ); + if ( frag_len > max_len ) + frag_len = max_len; + if ( dest ) { + memcpy ( iob_put ( dest, frag_len ), iobuf->data, + frag_len ); + } + if ( remove ) { + iob_pull ( iobuf, frag_len ); + if ( ! iob_len ( iobuf ) ) { + list_del ( &iobuf->list ); + free_iob ( iobuf ); + } + } + len += frag_len; + max_len -= frag_len; + } + return len; +} + +/** + * Transmit any outstanding data + * + * @v tcp TCP connection + * @v force_send Force sending of packet + * + * Transmits any outstanding data on the connection. + * + * Note that even if an error is returned, the retransmission timer + * will have been started if necessary, and so the stack will + * eventually attempt to retransmit the failed packet. + */ +static int tcp_xmit ( struct tcp_connection *tcp, int force_send ) { + struct io_buffer *iobuf; + struct tcp_header *tcphdr; + struct tcp_mss_option *mssopt; + struct tcp_timestamp_padded_option *tsopt; + void *payload; + unsigned int flags; + size_t len = 0; + size_t seq_len; + size_t app_win; + size_t max_rcv_win; + + /* If retransmission timer is already running, do nothing */ + if ( timer_running ( &tcp->timer ) ) + return 0; + + /* Calculate both the actual (payload) and sequence space + * lengths that we wish to transmit. + */ + if ( TCP_CAN_SEND_DATA ( tcp->tcp_state ) ) { + len = tcp_process_queue ( tcp, tcp_xmit_win ( tcp ), + NULL, 0 ); + } + seq_len = len; + flags = TCP_FLAGS_SENDING ( tcp->tcp_state ); + if ( flags & ( TCP_SYN | TCP_FIN ) ) { + /* SYN or FIN consume one byte, and we can never send both */ + assert ( ! ( ( flags & TCP_SYN ) && ( flags & TCP_FIN ) ) ); + seq_len++; + } + tcp->snd_sent = seq_len; + + /* If we have nothing to transmit, stop now */ + if ( ( seq_len == 0 ) && ! force_send ) + return 0; + + /* If we are transmitting anything that requires + * acknowledgement (i.e. consumes sequence space), start the + * retransmission timer. Do this before attempting to + * allocate the I/O buffer, in case allocation itself fails. + */ + if ( seq_len ) + start_timer ( &tcp->timer ); + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( len + MAX_HDR_LEN ); + if ( ! iobuf ) { + DBGC ( tcp, "TCP %p could not allocate data buffer\n", tcp ); + return -ENOMEM; + } + iob_reserve ( iobuf, MAX_HDR_LEN ); + + /* Fill data payload from transmit queue */ + tcp_process_queue ( tcp, len, iobuf, 0 ); + + /* Expand receive window if possible */ + max_rcv_win = ( ( freemem * 3 ) / 4 ); + if ( max_rcv_win > TCP_MAX_WINDOW_SIZE ) + max_rcv_win = TCP_MAX_WINDOW_SIZE; + app_win = xfer_window ( &tcp->xfer ); + if ( max_rcv_win > app_win ) + max_rcv_win = app_win; + max_rcv_win &= ~0x03; /* Keep everything dword-aligned */ + if ( tcp->rcv_win < max_rcv_win ) + tcp->rcv_win = max_rcv_win; + + /* Fill up the TCP header */ + payload = iobuf->data; + if ( flags & TCP_SYN ) { + mssopt = iob_push ( iobuf, sizeof ( *mssopt ) ); + mssopt->kind = TCP_OPTION_MSS; + mssopt->length = sizeof ( *mssopt ); + mssopt->mss = htons ( TCP_MSS ); + } + if ( ( flags & TCP_SYN ) || tcp->timestamps ) { + tsopt = iob_push ( iobuf, sizeof ( *tsopt ) ); + memset ( tsopt->nop, TCP_OPTION_NOP, sizeof ( tsopt->nop ) ); + tsopt->tsopt.kind = TCP_OPTION_TS; + tsopt->tsopt.length = sizeof ( tsopt->tsopt ); + tsopt->tsopt.tsval = ntohl ( currticks() ); + tsopt->tsopt.tsecr = ntohl ( tcp->ts_recent ); + } + tcphdr = iob_push ( iobuf, sizeof ( *tcphdr ) ); + memset ( tcphdr, 0, sizeof ( *tcphdr ) ); + tcphdr->src = tcp->local_port; + tcphdr->dest = tcp->peer.st_port; + tcphdr->seq = htonl ( tcp->snd_seq ); + tcphdr->ack = htonl ( tcp->rcv_ack ); + tcphdr->hlen = ( ( payload - iobuf->data ) << 2 ); + tcphdr->flags = ( flags | TCP_PSH ); + tcphdr->win = htons ( tcp->rcv_win ); + tcphdr->csum = tcpip_chksum ( iobuf->data, iob_len ( iobuf ) ); + + /* Dump header */ + DBGC ( tcp, "TCP %p TX %d->%d %08x..%08zx %08x %4zd", + tcp, ntohs ( tcphdr->src ), ntohs ( tcphdr->dest ), + ntohl ( tcphdr->seq ), ( ntohl ( tcphdr->seq ) + seq_len ), + ntohl ( tcphdr->ack ), len ); + tcp_dump_flags ( tcp, tcphdr->flags ); + DBGC ( tcp, "\n" ); + + /* Transmit packet */ + return tcpip_tx ( iobuf, &tcp_protocol, NULL, &tcp->peer, NULL, + &tcphdr->csum ); +} + +/** + * Retransmission timer expired + * + * @v timer Retry timer + * @v over Failure indicator + */ +static void tcp_expired ( struct retry_timer *timer, int over ) { + struct tcp_connection *tcp = + container_of ( timer, struct tcp_connection, timer ); + int graceful_close = TCP_CLOSED_GRACEFULLY ( tcp->tcp_state ); + + DBGC ( tcp, "TCP %p timer %s in %s\n", tcp, + ( over ? "expired" : "fired" ), tcp_state ( tcp->tcp_state ) ); + + assert ( ( tcp->tcp_state == TCP_SYN_SENT ) || + ( tcp->tcp_state == TCP_SYN_RCVD ) || + ( tcp->tcp_state == TCP_ESTABLISHED ) || + ( tcp->tcp_state == TCP_FIN_WAIT_1 ) || + ( tcp->tcp_state == TCP_TIME_WAIT ) || + ( tcp->tcp_state == TCP_CLOSE_WAIT ) || + ( tcp->tcp_state == TCP_CLOSING_OR_LAST_ACK ) ); + + if ( over || graceful_close ) { + /* If we have finally timed out and given up, or if + * this is the result of a graceful close, terminate + * the connection + */ + tcp->tcp_state = TCP_CLOSED; + tcp_dump_state ( tcp ); + tcp_close ( tcp, -ETIMEDOUT ); + } else { + /* Otherwise, retransmit the packet */ + tcp_xmit ( tcp, 0 ); + } +} + +/** + * Send RST response to incoming packet + * + * @v in_tcphdr TCP header of incoming packet + * @ret rc Return status code + */ +static int tcp_xmit_reset ( struct tcp_connection *tcp, + struct sockaddr_tcpip *st_dest, + struct tcp_header *in_tcphdr ) { + struct io_buffer *iobuf; + struct tcp_header *tcphdr; + + /* Allocate space for dataless TX buffer */ + iobuf = alloc_iob ( MAX_HDR_LEN ); + if ( ! iobuf ) { + DBGC ( tcp, "TCP %p could not allocate data buffer\n", tcp ); + return -ENOMEM; + } + iob_reserve ( iobuf, MAX_HDR_LEN ); + + /* Construct RST response */ + tcphdr = iob_push ( iobuf, sizeof ( *tcphdr ) ); + memset ( tcphdr, 0, sizeof ( *tcphdr ) ); + tcphdr->src = in_tcphdr->dest; + tcphdr->dest = in_tcphdr->src; + tcphdr->seq = in_tcphdr->ack; + tcphdr->ack = in_tcphdr->seq; + tcphdr->hlen = ( ( sizeof ( *tcphdr ) / 4 ) << 4 ); + tcphdr->flags = ( TCP_RST | TCP_ACK ); + tcphdr->win = htons ( TCP_MAX_WINDOW_SIZE ); + tcphdr->csum = tcpip_chksum ( iobuf->data, iob_len ( iobuf ) ); + + /* Dump header */ + DBGC ( tcp, "TCP %p TX %d->%d %08x..%08x %08x %4d", + tcp, ntohs ( tcphdr->src ), ntohs ( tcphdr->dest ), + ntohl ( tcphdr->seq ), ( ntohl ( tcphdr->seq ) ), + ntohl ( tcphdr->ack ), 0 ); + tcp_dump_flags ( tcp, tcphdr->flags ); + DBGC ( tcp, "\n" ); + + /* Transmit packet */ + return tcpip_tx ( iobuf, &tcp_protocol, NULL, st_dest, + NULL, &tcphdr->csum ); +} + +/*************************************************************************** + * + * Receive data path + * + *************************************************************************** + */ + +/** + * Identify TCP connection by local port number + * + * @v local_port Local port (in network-endian order) + * @ret tcp TCP connection, or NULL + */ +static struct tcp_connection * tcp_demux ( unsigned int local_port ) { + struct tcp_connection *tcp; + + list_for_each_entry ( tcp, &tcp_conns, list ) { + if ( tcp->local_port == local_port ) + return tcp; + } + return NULL; +} + +/** + * Parse TCP received options + * + * @v tcp TCP connection + * @v data Raw options data + * @v len Raw options length + * @v options Options structure to fill in + */ +static void tcp_rx_opts ( struct tcp_connection *tcp, const void *data, + size_t len, struct tcp_options *options ) { + const void *end = ( data + len ); + const struct tcp_option *option; + unsigned int kind; + + memset ( options, 0, sizeof ( *options ) ); + while ( data < end ) { + option = data; + kind = option->kind; + if ( kind == TCP_OPTION_END ) + return; + if ( kind == TCP_OPTION_NOP ) { + data++; + continue; + } + switch ( kind ) { + case TCP_OPTION_MSS: + options->mssopt = data; + break; + case TCP_OPTION_TS: + options->tsopt = data; + break; + default: + DBGC ( tcp, "TCP %p received unknown option %d\n", + tcp, kind ); + break; + } + data += option->length; + } +} + +/** + * Consume received sequence space + * + * @v tcp TCP connection + * @v seq_len Sequence space length to consume + */ +static void tcp_rx_seq ( struct tcp_connection *tcp, size_t seq_len ) { + tcp->rcv_ack += seq_len; + if ( tcp->rcv_win > seq_len ) { + tcp->rcv_win -= seq_len; + } else { + tcp->rcv_win = 0; + } +} + +/** + * Handle TCP received SYN + * + * @v tcp TCP connection + * @v seq SEQ value (in host-endian order) + * @v options TCP options + * @ret rc Return status code + */ +static int tcp_rx_syn ( struct tcp_connection *tcp, uint32_t seq, + struct tcp_options *options ) { + + /* Synchronise sequence numbers on first SYN */ + if ( ! ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) ) { + tcp->rcv_ack = seq; + if ( options->tsopt ) + tcp->timestamps = 1; + } + + /* Ignore duplicate SYN */ + if ( ( tcp->rcv_ack - seq ) > 0 ) + return 0; + + /* Mark SYN as received and start sending ACKs with each packet */ + tcp->tcp_state |= ( TCP_STATE_SENT ( TCP_ACK ) | + TCP_STATE_RCVD ( TCP_SYN ) ); + + /* Acknowledge SYN */ + tcp_rx_seq ( tcp, 1 ); + + return 0; +} + +/** + * Handle TCP received ACK + * + * @v tcp TCP connection + * @v ack ACK value (in host-endian order) + * @v win WIN value (in host-endian order) + * @ret rc Return status code + */ +static int tcp_rx_ack ( struct tcp_connection *tcp, uint32_t ack, + uint32_t win ) { + size_t ack_len = ( ack - tcp->snd_seq ); + size_t len; + unsigned int acked_flags; + + /* Ignore duplicate or out-of-range ACK */ + if ( ack_len > tcp->snd_sent ) { + DBGC ( tcp, "TCP %p received ACK for [%08x,%08zx), " + "sent only [%08x,%08x)\n", tcp, tcp->snd_seq, + ( tcp->snd_seq + ack_len ), tcp->snd_seq, + ( tcp->snd_seq + tcp->snd_sent ) ); + return -EINVAL; + } + + /* Acknowledge any flags being sent */ + len = ack_len; + acked_flags = ( TCP_FLAGS_SENDING ( tcp->tcp_state ) & + ( TCP_SYN | TCP_FIN ) ); + if ( acked_flags ) + len--; + + /* Update SEQ and sent counters, and window size */ + tcp->snd_seq = ack; + tcp->snd_sent = 0; + tcp->snd_win = win; + + /* Stop the retransmission timer */ + stop_timer ( &tcp->timer ); + + /* Remove any acknowledged data from transmit queue */ + tcp_process_queue ( tcp, len, NULL, 1 ); + + /* Mark SYN/FIN as acknowledged if applicable. */ + if ( acked_flags ) + tcp->tcp_state |= TCP_STATE_ACKED ( acked_flags ); + + /* Start sending FIN if we've had all possible data ACKed */ + if ( list_empty ( &tcp->queue ) && tcp->xfer_closed ) + tcp->tcp_state |= TCP_STATE_SENT ( TCP_FIN ); + + return 0; +} + +/** + * Handle TCP received data + * + * @v tcp TCP connection + * @v seq SEQ value (in host-endian order) + * @v iobuf I/O buffer + * @ret rc Return status code + * + * This function takes ownership of the I/O buffer. + */ +static int tcp_rx_data ( struct tcp_connection *tcp, uint32_t seq, + struct io_buffer *iobuf ) { + size_t already_rcvd; + size_t len; + int rc; + + /* Ignore duplicate or out-of-order data */ + already_rcvd = ( tcp->rcv_ack - seq ); + len = iob_len ( iobuf ); + if ( already_rcvd >= len ) { + free_iob ( iobuf ); + return 0; + } + iob_pull ( iobuf, already_rcvd ); + len -= already_rcvd; + + /* Deliver data to application */ + if ( ( rc = xfer_deliver_iob ( &tcp->xfer, iobuf ) ) != 0 ) + return rc; + + /* Acknowledge new data */ + tcp_rx_seq ( tcp, len ); + + return 0; +} + +/** + * Handle TCP received FIN + * + * @v tcp TCP connection + * @v seq SEQ value (in host-endian order) + * @ret rc Return status code + */ +static int tcp_rx_fin ( struct tcp_connection *tcp, uint32_t seq ) { + + /* Ignore duplicate or out-of-order FIN */ + if ( ( tcp->rcv_ack - seq ) > 0 ) + return 0; + + /* Mark FIN as received and acknowledge it */ + tcp->tcp_state |= TCP_STATE_RCVD ( TCP_FIN ); + tcp_rx_seq ( tcp, 1 ); + + /* Close connection */ + tcp_close ( tcp, 0 ); + + return 0; +} + +/** + * Handle TCP received RST + * + * @v tcp TCP connection + * @v seq SEQ value (in host-endian order) + * @ret rc Return status code + */ +static int tcp_rx_rst ( struct tcp_connection *tcp, uint32_t seq ) { + + /* Accept RST only if it falls within the window. If we have + * not yet received a SYN, then we have no window to test + * against, so fall back to checking that our SYN has been + * ACKed. + */ + if ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) { + if ( ( seq - tcp->rcv_ack ) >= tcp->rcv_win ) + return 0; + } else { + if ( ! ( tcp->tcp_state & TCP_STATE_ACKED ( TCP_SYN ) ) ) + return 0; + } + + /* Abort connection */ + tcp->tcp_state = TCP_CLOSED; + tcp_dump_state ( tcp ); + tcp_close ( tcp, -ECONNRESET ); + + return -ECONNRESET; +} + +/** + * Process received packet + * + * @v iobuf I/O buffer + * @v st_src Partially-filled source address + * @v st_dest Partially-filled destination address + * @v pshdr_csum Pseudo-header checksum + * @ret rc Return status code + */ +static int tcp_rx ( struct io_buffer *iobuf, + struct sockaddr_tcpip *st_src, + struct sockaddr_tcpip *st_dest __unused, + uint16_t pshdr_csum ) { + struct tcp_header *tcphdr = iobuf->data; + struct tcp_connection *tcp; + struct tcp_options options; + size_t hlen; + uint16_t csum; + uint32_t start_seq; + uint32_t seq; + uint32_t ack; + uint32_t win; + unsigned int flags; + size_t len; + int rc; + + /* Sanity check packet */ + if ( iob_len ( iobuf ) < sizeof ( *tcphdr ) ) { + DBG ( "TCP packet too short at %zd bytes (min %zd bytes)\n", + iob_len ( iobuf ), sizeof ( *tcphdr ) ); + rc = -EINVAL; + goto discard; + } + hlen = ( ( tcphdr->hlen & TCP_MASK_HLEN ) / 16 ) * 4; + if ( hlen < sizeof ( *tcphdr ) ) { + DBG ( "TCP header too short at %zd bytes (min %zd bytes)\n", + hlen, sizeof ( *tcphdr ) ); + rc = -EINVAL; + goto discard; + } + if ( hlen > iob_len ( iobuf ) ) { + DBG ( "TCP header too long at %zd bytes (max %zd bytes)\n", + hlen, iob_len ( iobuf ) ); + rc = -EINVAL; + goto discard; + } + csum = tcpip_continue_chksum ( pshdr_csum, iobuf->data, + iob_len ( iobuf ) ); + if ( csum != 0 ) { + DBG ( "TCP checksum incorrect (is %04x including checksum " + "field, should be 0000)\n", csum ); + rc = -EINVAL; + goto discard; + } + + /* Parse parameters from header and strip header */ + tcp = tcp_demux ( tcphdr->dest ); + start_seq = seq = ntohl ( tcphdr->seq ); + ack = ntohl ( tcphdr->ack ); + win = ntohs ( tcphdr->win ); + flags = tcphdr->flags; + tcp_rx_opts ( tcp, ( ( ( void * ) tcphdr ) + sizeof ( *tcphdr ) ), + ( hlen - sizeof ( *tcphdr ) ), &options ); + iob_pull ( iobuf, hlen ); + len = iob_len ( iobuf ); + + /* Dump header */ + DBGC ( tcp, "TCP %p RX %d<-%d %08x %08x..%08zx %4zd", + tcp, ntohs ( tcphdr->dest ), ntohs ( tcphdr->src ), + ntohl ( tcphdr->ack ), ntohl ( tcphdr->seq ), + ( ntohl ( tcphdr->seq ) + len + + ( ( tcphdr->flags & ( TCP_SYN | TCP_FIN ) ) ? 1 : 0 ) ), len); + tcp_dump_flags ( tcp, tcphdr->flags ); + DBGC ( tcp, "\n" ); + + /* If no connection was found, send RST */ + if ( ! tcp ) { + tcp_xmit_reset ( tcp, st_src, tcphdr ); + rc = -ENOTCONN; + goto discard; + } + + /* Handle ACK, if present */ + if ( flags & TCP_ACK ) { + if ( ( rc = tcp_rx_ack ( tcp, ack, win ) ) != 0 ) { + tcp_xmit_reset ( tcp, st_src, tcphdr ); + goto discard; + } + } + + /* Handle SYN, if present */ + if ( flags & TCP_SYN ) { + tcp_rx_syn ( tcp, seq, &options ); + seq++; + } + + /* Handle RST, if present */ + if ( flags & TCP_RST ) { + if ( ( rc = tcp_rx_rst ( tcp, seq ) ) != 0 ) + goto discard; + } + + /* Handle new data, if any */ + tcp_rx_data ( tcp, seq, iobuf ); + seq += len; + + /* Handle FIN, if present */ + if ( flags & TCP_FIN ) { + tcp_rx_fin ( tcp, seq ); + seq++; + } + + /* Update timestamp, if present and applicable */ + if ( ( seq == tcp->rcv_ack ) && options.tsopt ) + tcp->ts_recent = ntohl ( options.tsopt->tsval ); + + /* Dump out any state change as a result of the received packet */ + tcp_dump_state ( tcp ); + + /* Send out any pending data. We force sending a reply if either + * + * a) the peer is expecting an ACK (i.e. consumed sequence space), or + * b) either end of the packet was outside the receive window + * + * Case (b) enables us to support TCP keepalives using + * zero-length packets, which we would otherwise ignore. Note + * that for case (b), we need *only* consider zero-length + * packets, since non-zero-length packets will already be + * caught by case (a). + */ + tcp_xmit ( tcp, ( ( start_seq != seq ) || + ( ( seq - tcp->rcv_ack ) > tcp->rcv_win ) ) ); + + /* If this packet was the last we expect to receive, set up + * timer to expire and cause the connection to be freed. + */ + if ( TCP_CLOSED_GRACEFULLY ( tcp->tcp_state ) ) { + tcp->timer.timeout = ( 2 * TCP_MSL ); + start_timer ( &tcp->timer ); + } + + return 0; + + discard: + /* Free received packet */ + free_iob ( iobuf ); + return rc; +} + +/** TCP protocol */ +struct tcpip_protocol tcp_protocol __tcpip_protocol = { + .name = "TCP", + .rx = tcp_rx, + .tcpip_proto = IP_TCP, +}; + +/*************************************************************************** + * + * Data transfer interface + * + *************************************************************************** + */ + +/** + * Close interface + * + * @v xfer Data transfer interface + * @v rc Reason for close + */ +static void tcp_xfer_close ( struct xfer_interface *xfer, int rc ) { + struct tcp_connection *tcp = + container_of ( xfer, struct tcp_connection, xfer ); + + /* Close data transfer interface */ + tcp_close ( tcp, rc ); + + /* Transmit FIN, if possible */ + tcp_xmit ( tcp, 0 ); +} + +/** + * Check flow control window + * + * @v xfer Data transfer interface + * @ret len Length of window + */ +static size_t tcp_xfer_window ( struct xfer_interface *xfer ) { + struct tcp_connection *tcp = + container_of ( xfer, struct tcp_connection, xfer ); + + /* Not ready if data queue is non-empty. This imposes a limit + * of only one unACKed packet in the TX queue at any time; we + * do this to conserve memory usage. + */ + if ( ! list_empty ( &tcp->queue ) ) + return 0; + + /* Return TCP window length */ + return tcp_xmit_win ( tcp ); +} + +/** + * Deliver datagram as I/O buffer + * + * @v xfer Data transfer interface + * @v iobuf Datagram I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int tcp_xfer_deliver_iob ( struct xfer_interface *xfer, + struct io_buffer *iobuf, + struct xfer_metadata *meta __unused ) { + struct tcp_connection *tcp = + container_of ( xfer, struct tcp_connection, xfer ); + + /* Enqueue packet */ + list_add_tail ( &iobuf->list, &tcp->queue ); + + /* Transmit data, if possible */ + tcp_xmit ( tcp, 0 ); + + return 0; +} + +/** TCP data transfer interface operations */ +static struct xfer_interface_operations tcp_xfer_operations = { + .close = tcp_xfer_close, + .vredirect = ignore_xfer_vredirect, + .window = tcp_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = tcp_xfer_deliver_iob, + .deliver_raw = xfer_deliver_as_iob, +}; + +/*************************************************************************** + * + * Openers + * + *************************************************************************** + */ + +/** TCP socket opener */ +struct socket_opener tcp_socket_opener __socket_opener = { + .semantics = TCP_SOCK_STREAM, + .family = AF_INET, + .open = tcp_open, +}; + +/** Linkage hack */ +int tcp_sock_stream = TCP_SOCK_STREAM; + +/** + * Open TCP URI + * + * @v xfer Data transfer interface + * @v uri URI + * @ret rc Return status code + */ +static int tcp_open_uri ( struct xfer_interface *xfer, struct uri *uri ) { + struct sockaddr_tcpip peer; + + /* Sanity check */ + if ( ! uri->host ) + return -EINVAL; + + memset ( &peer, 0, sizeof ( peer ) ); + peer.st_port = htons ( uri_port ( uri, 0 ) ); + return xfer_open_named_socket ( xfer, SOCK_STREAM, + ( struct sockaddr * ) &peer, + uri->host, NULL ); +} + +/** TCP URI opener */ +struct uri_opener tcp_uri_opener __uri_opener = { + .scheme = "tcp", + .open = tcp_open_uri, +}; + diff --git a/gpxe/src/net/tcp/ftp.c b/gpxe/src/net/tcp/ftp.c new file mode 100644 index 0000000..445e32b --- /dev/null +++ b/gpxe/src/net/tcp/ftp.c @@ -0,0 +1,507 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * File transfer protocol + * + */ + +FEATURE ( FEATURE_PROTOCOL, "FTP", DHCP_EB_FEATURE_FTP, 1 ); + +/** + * FTP states + * + * These @b must be sequential, i.e. a successful FTP session must + * pass through each of these states in order. + */ +enum ftp_state { + FTP_CONNECT = 0, + FTP_USER, + FTP_PASS, + FTP_TYPE, + FTP_PASV, + FTP_RETR, + FTP_WAIT, + FTP_QUIT, + FTP_DONE, +}; + +/** + * An FTP request + * + */ +struct ftp_request { + /** Reference counter */ + struct refcnt refcnt; + /** Data transfer interface */ + struct xfer_interface xfer; + + /** URI being fetched */ + struct uri *uri; + /** FTP control channel interface */ + struct xfer_interface control; + /** FTP data channel interface */ + struct xfer_interface data; + + /** Current state */ + enum ftp_state state; + /** Buffer to be filled with data received via the control channel */ + char *recvbuf; + /** Remaining size of recvbuf */ + size_t recvsize; + /** FTP status code, as text */ + char status_text[5]; + /** Passive-mode parameters, as text */ + char passive_text[24]; /* "aaa,bbb,ccc,ddd,eee,fff" */ +}; + +/** + * Free FTP request + * + * @v refcnt Reference counter + */ +static void ftp_free ( struct refcnt *refcnt ) { + struct ftp_request *ftp = + container_of ( refcnt, struct ftp_request, refcnt ); + + DBGC ( ftp, "FTP %p freed\n", ftp ); + + uri_put ( ftp->uri ); + free ( ftp ); +} + +/** + * Mark FTP operation as complete + * + * @v ftp FTP request + * @v rc Return status code + */ +static void ftp_done ( struct ftp_request *ftp, int rc ) { + + DBGC ( ftp, "FTP %p completed (%s)\n", ftp, strerror ( rc ) ); + + /* Close all data transfer interfaces */ + xfer_nullify ( &ftp->xfer ); + xfer_close ( &ftp->xfer, rc ); + xfer_nullify ( &ftp->control ); + xfer_close ( &ftp->control, rc ); + xfer_nullify ( &ftp->data ); + xfer_close ( &ftp->data, rc ); +} + +/***************************************************************************** + * + * FTP control channel + * + */ + +/** An FTP control channel string */ +struct ftp_control_string { + /** Literal portion */ + const char *literal; + /** Variable portion + * + * @v ftp FTP request + * @ret string Variable portion of string + */ + const char * ( *variable ) ( struct ftp_request *ftp ); +}; + +/** + * Retrieve FTP pathname + * + * @v ftp FTP request + * @ret path FTP pathname + */ +static const char * ftp_uri_path ( struct ftp_request *ftp ) { + return ftp->uri->path; +} + +/** FTP control channel strings */ +static struct ftp_control_string ftp_strings[] = { + [FTP_CONNECT] = { NULL, NULL }, + [FTP_USER] = { "USER anonymous", NULL }, + [FTP_PASS] = { "PASS etherboot@etherboot.org", NULL }, + [FTP_TYPE] = { "TYPE I", NULL }, + [FTP_PASV] = { "PASV", NULL }, + [FTP_RETR] = { "RETR ", ftp_uri_path }, + [FTP_WAIT] = { NULL, NULL }, + [FTP_QUIT] = { "QUIT", NULL }, + [FTP_DONE] = { NULL, NULL }, +}; + +/** + * Handle control channel being closed + * + * @v control FTP control channel interface + * @v rc Reason for close + * + * When the control channel is closed, the data channel must also be + * closed, if it is currently open. + */ +static void ftp_control_close ( struct xfer_interface *control, int rc ) { + struct ftp_request *ftp = + container_of ( control, struct ftp_request, control ); + + DBGC ( ftp, "FTP %p control connection closed: %s\n", + ftp, strerror ( rc ) ); + + /* Complete FTP operation */ + ftp_done ( ftp, rc ); +} + +/** + * Parse FTP byte sequence value + * + * @v text Text string + * @v value Value buffer + * @v len Length of value buffer + * + * This parses an FTP byte sequence value (e.g. the "aaa,bbb,ccc,ddd" + * form for IP addresses in PORT commands) into a byte sequence. @c + * *text will be updated to point beyond the end of the parsed byte + * sequence. + * + * This function is safe in the presence of malformed data, though the + * output is undefined. + */ +static void ftp_parse_value ( char **text, uint8_t *value, size_t len ) { + do { + *(value++) = strtoul ( *text, text, 10 ); + if ( **text ) + (*text)++; + } while ( --len ); +} + +/** + * Move to next state and send the appropriate FTP control string + * + * @v ftp FTP request + * + */ +static void ftp_next_state ( struct ftp_request *ftp ) { + struct ftp_control_string *ftp_string; + const char *literal; + const char *variable; + + /* Move to next state */ + if ( ftp->state < FTP_DONE ) + ftp->state++; + + /* Send control string if needed */ + ftp_string = &ftp_strings[ftp->state]; + literal = ftp_string->literal; + variable = ( ftp_string->variable ? + ftp_string->variable ( ftp ) : "" ); + if ( literal ) { + DBGC ( ftp, "FTP %p sending %s%s\n", ftp, literal, variable ); + xfer_printf ( &ftp->control, "%s%s\r\n", literal, variable ); + } +} + +/** + * Handle an FTP control channel response + * + * @v ftp FTP request + * + * This is called once we have received a complete response line. + */ +static void ftp_reply ( struct ftp_request *ftp ) { + char status_major = ftp->status_text[0]; + char separator = ftp->status_text[3]; + + DBGC ( ftp, "FTP %p received status %s\n", ftp, ftp->status_text ); + + /* Ignore malformed lines */ + if ( separator != ' ' ) + return; + + /* Ignore "intermediate" responses (1xx codes) */ + if ( status_major == '1' ) + return; + + /* Anything other than success (2xx) or, in the case of a + * repsonse to a "USER" command, a password prompt (3xx), is a + * fatal error. + */ + if ( ! ( ( status_major == '2' ) || + ( ( status_major == '3' ) && ( ftp->state == FTP_USER ) ) ) ){ + /* Flag protocol error and close connections */ + ftp_done ( ftp, -EPROTO ); + return; + } + + /* Open passive connection when we get "PASV" response */ + if ( ftp->state == FTP_PASV ) { + char *ptr = ftp->passive_text; + union { + struct sockaddr_in sin; + struct sockaddr sa; + } sa; + int rc; + + sa.sin.sin_family = AF_INET; + ftp_parse_value ( &ptr, ( uint8_t * ) &sa.sin.sin_addr, + sizeof ( sa.sin.sin_addr ) ); + ftp_parse_value ( &ptr, ( uint8_t * ) &sa.sin.sin_port, + sizeof ( sa.sin.sin_port ) ); + if ( ( rc = xfer_open_socket ( &ftp->data, SOCK_STREAM, + &sa.sa, NULL ) ) != 0 ) { + DBGC ( ftp, "FTP %p could not open data connection\n", + ftp ); + ftp_done ( ftp, rc ); + return; + } + } + + /* Move to next state and send control string */ + ftp_next_state ( ftp ); + +} + +/** + * Handle new data arriving on FTP control channel + * + * @v control FTP control channel interface + * @v data New data + * @v len Length of new data + * + * Data is collected until a complete line is received, at which point + * its information is passed to ftp_reply(). + */ +static int ftp_control_deliver_raw ( struct xfer_interface *control, + const void *data, size_t len ) { + struct ftp_request *ftp = + container_of ( control, struct ftp_request, control ); + char *recvbuf = ftp->recvbuf; + size_t recvsize = ftp->recvsize; + char c; + + while ( len-- ) { + c = * ( ( char * ) data++ ); + switch ( c ) { + case '\r' : + case '\n' : + /* End of line: call ftp_reply() to handle + * completed reply. Avoid calling ftp_reply() + * twice if we receive both \r and \n. + */ + if ( recvsize == 0 ) + ftp_reply ( ftp ); + /* Start filling up the status code buffer */ + recvbuf = ftp->status_text; + recvsize = sizeof ( ftp->status_text ) - 1; + break; + case '(' : + /* Start filling up the passive parameter buffer */ + recvbuf = ftp->passive_text; + recvsize = sizeof ( ftp->passive_text ) - 1; + break; + case ')' : + /* Stop filling the passive parameter buffer */ + recvsize = 0; + break; + default : + /* Fill up buffer if applicable */ + if ( recvsize > 0 ) { + *(recvbuf++) = c; + recvsize--; + } + break; + } + } + + /* Store for next invocation */ + ftp->recvbuf = recvbuf; + ftp->recvsize = recvsize; + + return 0; +} + +/** FTP control channel operations */ +static struct xfer_interface_operations ftp_control_operations = { + .close = ftp_control_close, + .vredirect = xfer_vopen, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = xfer_deliver_as_raw, + .deliver_raw = ftp_control_deliver_raw, +}; + +/***************************************************************************** + * + * FTP data channel + * + */ + +/** + * Handle FTP data channel being closed + * + * @v data FTP data channel interface + * @v rc Reason for closure + * + * When the data channel is closed, the control channel should be left + * alone; the server will send a completion message via the control + * channel which we'll pick up. + * + * If the data channel is closed due to an error, we abort the request. + */ +static void ftp_data_closed ( struct xfer_interface *data, int rc ) { + struct ftp_request *ftp = + container_of ( data, struct ftp_request, data ); + + DBGC ( ftp, "FTP %p data connection closed: %s\n", + ftp, strerror ( rc ) ); + + /* If there was an error, close control channel and record status */ + if ( rc ) { + ftp_done ( ftp, rc ); + } else { + ftp_next_state ( ftp ); + } +} + +/** + * Handle data delivery via FTP data channel + * + * @v xfer FTP data channel interface + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int ftp_data_deliver_iob ( struct xfer_interface *data, + struct io_buffer *iobuf, + struct xfer_metadata *meta __unused ) { + struct ftp_request *ftp = + container_of ( data, struct ftp_request, data ); + int rc; + + if ( ( rc = xfer_deliver_iob ( &ftp->xfer, iobuf ) ) != 0 ) { + DBGC ( ftp, "FTP %p failed to deliver data: %s\n", + ftp, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** FTP data channel operations */ +static struct xfer_interface_operations ftp_data_operations = { + .close = ftp_data_closed, + .vredirect = xfer_vopen, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = ftp_data_deliver_iob, + .deliver_raw = xfer_deliver_as_iob, +}; + +/***************************************************************************** + * + * Data transfer interface + * + */ + +/** + * Close FTP data transfer interface + * + * @v xfer FTP data transfer interface + * @v rc Reason for close + */ +static void ftp_xfer_closed ( struct xfer_interface *xfer, int rc ) { + struct ftp_request *ftp = + container_of ( xfer, struct ftp_request, xfer ); + + DBGC ( ftp, "FTP %p data transfer interface closed: %s\n", + ftp, strerror ( rc ) ); + + ftp_done ( ftp, rc ); +} + +/** FTP data transfer interface operations */ +static struct xfer_interface_operations ftp_xfer_operations = { + .close = ftp_xfer_closed, + .vredirect = ignore_xfer_vredirect, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = xfer_deliver_as_raw, + .deliver_raw = ignore_xfer_deliver_raw, +}; + +/***************************************************************************** + * + * URI opener + * + */ + +/** + * Initiate an FTP connection + * + * @v xfer Data transfer interface + * @v uri Uniform Resource Identifier + * @ret rc Return status code + */ +static int ftp_open ( struct xfer_interface *xfer, struct uri *uri ) { + struct ftp_request *ftp; + struct sockaddr_tcpip server; + int rc; + + /* Sanity checks */ + if ( ! uri->path ) + return -EINVAL; + if ( ! uri->host ) + return -EINVAL; + + /* Allocate and populate structure */ + ftp = zalloc ( sizeof ( *ftp ) ); + if ( ! ftp ) + return -ENOMEM; + ftp->refcnt.free = ftp_free; + xfer_init ( &ftp->xfer, &ftp_xfer_operations, &ftp->refcnt ); + ftp->uri = uri_get ( uri ); + xfer_init ( &ftp->control, &ftp_control_operations, &ftp->refcnt ); + xfer_init ( &ftp->data, &ftp_data_operations, &ftp->refcnt ); + ftp->recvbuf = ftp->status_text; + ftp->recvsize = sizeof ( ftp->status_text ) - 1; + + DBGC ( ftp, "FTP %p fetching %s\n", ftp, ftp->uri->path ); + + /* Open control connection */ + memset ( &server, 0, sizeof ( server ) ); + server.st_port = htons ( uri_port ( uri, FTP_PORT ) ); + if ( ( rc = xfer_open_named_socket ( &ftp->control, SOCK_STREAM, + ( struct sockaddr * ) &server, + uri->host, NULL ) ) != 0 ) + goto err; + + /* Attach to parent interface, mortalise self, and return */ + xfer_plug_plug ( &ftp->xfer, xfer ); + ref_put ( &ftp->refcnt ); + return 0; + + err: + DBGC ( ftp, "FTP %p could not create request: %s\n", + ftp, strerror ( rc ) ); + ftp_done ( ftp, rc ); + ref_put ( &ftp->refcnt ); + return rc; +} + +/** FTP URI opener */ +struct uri_opener ftp_uri_opener __uri_opener = { + .scheme = "ftp", + .open = ftp_open, +}; diff --git a/gpxe/src/net/tcp/http.c b/gpxe/src/net/tcp/http.c new file mode 100644 index 0000000..93ccfd3 --- /dev/null +++ b/gpxe/src/net/tcp/http.c @@ -0,0 +1,573 @@ +/* + * Copyright (C) 2007 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. + */ + +/** + * @file + * + * Hyper Text Transfer Protocol (HTTP) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +FEATURE ( FEATURE_PROTOCOL, "HTTP", DHCP_EB_FEATURE_HTTP, 1 ); + +/** HTTP receive state */ +enum http_rx_state { + HTTP_RX_RESPONSE = 0, + HTTP_RX_HEADER, + HTTP_RX_DATA, + HTTP_RX_DEAD, +}; + +/** + * An HTTP request + * + */ +struct http_request { + /** Reference count */ + struct refcnt refcnt; + /** Data transfer interface */ + struct xfer_interface xfer; + + /** URI being fetched */ + struct uri *uri; + /** Transport layer interface */ + struct xfer_interface socket; + + /** TX process */ + struct process process; + + /** HTTP response code */ + unsigned int response; + /** HTTP Content-Length */ + size_t content_length; + /** Received length */ + size_t rx_len; + /** RX state */ + enum http_rx_state rx_state; + /** Line buffer for received header lines */ + struct line_buffer linebuf; +}; + +/** + * Free HTTP request + * + * @v refcnt Reference counter + */ +static void http_free ( struct refcnt *refcnt ) { + struct http_request *http = + container_of ( refcnt, struct http_request, refcnt ); + + uri_put ( http->uri ); + empty_line_buffer ( &http->linebuf ); + free ( http ); +}; + +/** + * Mark HTTP request as complete + * + * @v http HTTP request + * @v rc Return status code + */ +static void http_done ( struct http_request *http, int rc ) { + + /* Prevent further processing of any current packet */ + http->rx_state = HTTP_RX_DEAD; + + /* If we had a Content-Length, and the received content length + * isn't correct, flag an error + */ + if ( http->content_length && + ( http->content_length != http->rx_len ) ) { + DBGC ( http, "HTTP %p incorrect length %zd, should be %zd\n", + http, http->rx_len, http->content_length ); + rc = -EIO; + } + + /* Remove process */ + process_del ( &http->process ); + + /* Close all data transfer interfaces */ + xfer_nullify ( &http->socket ); + xfer_close ( &http->socket, rc ); + xfer_nullify ( &http->xfer ); + xfer_close ( &http->xfer, rc ); +} + +/** + * Convert HTTP response code to return status code + * + * @v response HTTP response code + * @ret rc Return status code + */ +static int http_response_to_rc ( unsigned int response ) { + switch ( response ) { + case 200: + return 0; + case 404: + return -ENOENT; + case 403: + return -EPERM; + case 401: + return -EACCES; + default: + return -EIO; + } +} + +/** + * Handle HTTP response + * + * @v http HTTP request + * @v response HTTP response + * @ret rc Return status code + */ +static int http_rx_response ( struct http_request *http, char *response ) { + char *spc; + int rc; + + DBGC ( http, "HTTP %p response \"%s\"\n", http, response ); + + /* Check response starts with "HTTP/" */ + if ( strncmp ( response, "HTTP/", 5 ) != 0 ) + return -EIO; + + /* Locate and check response code */ + spc = strchr ( response, ' ' ); + if ( ! spc ) + return -EIO; + http->response = strtoul ( spc, NULL, 10 ); + if ( ( rc = http_response_to_rc ( http->response ) ) != 0 ) + return rc; + + /* Move to received headers */ + http->rx_state = HTTP_RX_HEADER; + return 0; +} + +/** + * Handle HTTP Content-Length header + * + * @v http HTTP request + * @v value HTTP header value + * @ret rc Return status code + */ +static int http_rx_content_length ( struct http_request *http, + const char *value ) { + char *endp; + + http->content_length = strtoul ( value, &endp, 10 ); + if ( *endp != '\0' ) { + DBGC ( http, "HTTP %p invalid Content-Length \"%s\"\n", + http, value ); + return -EIO; + } + + /* Use seek() to notify recipient of filesize */ + xfer_seek ( &http->xfer, http->content_length, SEEK_SET ); + xfer_seek ( &http->xfer, 0, SEEK_SET ); + + return 0; +} + +/** An HTTP header handler */ +struct http_header_handler { + /** Name (e.g. "Content-Length") */ + const char *header; + /** Handle received header + * + * @v http HTTP request + * @v value HTTP header value + * @ret rc Return status code + * + * If an error is returned, the download will be aborted. + */ + int ( * rx ) ( struct http_request *http, const char *value ); +}; + +/** List of HTTP header handlers */ +static struct http_header_handler http_header_handlers[] = { + { + .header = "Content-Length", + .rx = http_rx_content_length, + }, + { NULL, NULL } +}; + +/** + * Handle HTTP header + * + * @v http HTTP request + * @v header HTTP header + * @ret rc Return status code + */ +static int http_rx_header ( struct http_request *http, char *header ) { + struct http_header_handler *handler; + char *separator; + char *value; + int rc; + + /* An empty header line marks the transition to the data phase */ + if ( ! header[0] ) { + DBGC ( http, "HTTP %p start of data\n", http ); + empty_line_buffer ( &http->linebuf ); + http->rx_state = HTTP_RX_DATA; + return 0; + } + + DBGC ( http, "HTTP %p header \"%s\"\n", http, header ); + + /* Split header at the ": " */ + separator = strstr ( header, ": " ); + if ( ! separator ) { + DBGC ( http, "HTTP %p malformed header\n", http ); + return -EIO; + } + *separator = '\0'; + value = ( separator + 2 ); + + /* Hand off to header handler, if one exists */ + for ( handler = http_header_handlers ; handler->header ; handler++ ) { + if ( strcasecmp ( header, handler->header ) == 0 ) { + if ( ( rc = handler->rx ( http, value ) ) != 0 ) + return rc; + break; + } + } + return 0; +} + +/** An HTTP line-based data handler */ +struct http_line_handler { + /** Handle line + * + * @v http HTTP request + * @v line Line to handle + * @ret rc Return status code + */ + int ( * rx ) ( struct http_request *http, char *line ); +}; + +/** List of HTTP line-based data handlers */ +static struct http_line_handler http_line_handlers[] = { + [HTTP_RX_RESPONSE] = { .rx = http_rx_response }, + [HTTP_RX_HEADER] = { .rx = http_rx_header }, +}; + +/** + * Handle new data arriving via HTTP connection in the data phase + * + * @v http HTTP request + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int http_rx_data ( struct http_request *http, + struct io_buffer *iobuf ) { + int rc; + + /* Update received length */ + http->rx_len += iob_len ( iobuf ); + + /* Hand off data buffer */ + if ( ( rc = xfer_deliver_iob ( &http->xfer, iobuf ) ) != 0 ) + return rc; + + /* If we have reached the content-length, stop now */ + if ( http->content_length && + ( http->rx_len >= http->content_length ) ) { + http_done ( http, 0 ); + } + + return 0; +} + +/** + * Handle new data arriving via HTTP connection + * + * @v socket Transport layer interface + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int http_socket_deliver_iob ( struct xfer_interface *socket, + struct io_buffer *iobuf, + struct xfer_metadata *meta __unused ) { + struct http_request *http = + container_of ( socket, struct http_request, socket ); + struct http_line_handler *lh; + char *line; + ssize_t len; + int rc = 0; + + while ( iob_len ( iobuf ) ) { + switch ( http->rx_state ) { + case HTTP_RX_DEAD: + /* Do no further processing */ + goto done; + case HTTP_RX_DATA: + /* Once we're into the data phase, just fill + * the data buffer + */ + rc = http_rx_data ( http, iob_disown ( iobuf ) ); + goto done; + case HTTP_RX_RESPONSE: + case HTTP_RX_HEADER: + /* In the other phases, buffer and process a + * line at a time + */ + len = line_buffer ( &http->linebuf, iobuf->data, + iob_len ( iobuf ) ); + if ( len < 0 ) { + rc = len; + DBGC ( http, "HTTP %p could not buffer line: " + "%s\n", http, strerror ( rc ) ); + goto done; + } + iob_pull ( iobuf, len ); + line = buffered_line ( &http->linebuf ); + if ( line ) { + lh = &http_line_handlers[http->rx_state]; + if ( ( rc = lh->rx ( http, line ) ) != 0 ) + goto done; + } + break; + default: + assert ( 0 ); + break; + } + } + + done: + if ( rc ) + http_done ( http, rc ); + free_iob ( iobuf ); + return rc; +} + +/** + * HTTP process + * + * @v process Process + */ +static void http_step ( struct process *process ) { + struct http_request *http = + container_of ( process, struct http_request, process ); + const char *path = http->uri->path; + const char *host = http->uri->host; + const char *query = http->uri->query; + const char *user = http->uri->user; + const char *password = + ( http->uri->password ? http->uri->password : "" ); + size_t user_pw_len = ( user ? ( strlen ( user ) + 1 /* ":" */ + + strlen ( password ) ) : 0 ); + size_t user_pw_base64_len = base64_encoded_len ( user_pw_len ); + char user_pw[ user_pw_len + 1 /* NUL */ ]; + char user_pw_base64[ user_pw_base64_len + 1 /* NUL */ ]; + int rc; + + if ( xfer_window ( &http->socket ) ) { + + /* We want to execute only once */ + process_del ( &http->process ); + + /* Construct authorisation, if applicable */ + if ( user ) { + char *buf = user_pw; + ssize_t remaining = sizeof ( user_pw ); + size_t len; + + /* URI-decode the username and password */ + len = uri_decode ( user, buf, remaining ); + buf += len; + remaining -= len; + *(remaining--, buf++) = ':'; + len = uri_decode ( password, buf, remaining ); + buf += len; + remaining -= len; + assert ( remaining >= 0 ); + + /* Base64-encode the "user:password" string */ + base64_encode ( user_pw, user_pw_base64 ); + } + + /* Send GET request */ + if ( ( rc = xfer_printf ( &http->socket, + "GET %s%s%s HTTP/1.0\r\n" + "User-Agent: gPXE/" VERSION "\r\n" + "%s%s%s" + "Host: %s\r\n" + "\r\n", + ( path ? path : "/" ), + ( query ? "?" : "" ), + ( query ? query : "" ), + ( user ? + "Authorization: Basic " : "" ), + ( user ? user_pw_base64 : "" ), + ( user ? "\r\n" : "" ), + host ) ) != 0 ) { + http_done ( http, rc ); + } + } +} + +/** + * HTTP connection closed by network stack + * + * @v socket Transport layer interface + * @v rc Reason for close + */ +static void http_socket_close ( struct xfer_interface *socket, int rc ) { + struct http_request *http = + container_of ( socket, struct http_request, socket ); + + DBGC ( http, "HTTP %p socket closed: %s\n", + http, strerror ( rc ) ); + + http_done ( http, rc ); +} + +/** HTTP socket operations */ +static struct xfer_interface_operations http_socket_operations = { + .close = http_socket_close, + .vredirect = xfer_vopen, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = http_socket_deliver_iob, + .deliver_raw = xfer_deliver_as_iob, +}; + +/** + * Close HTTP data transfer interface + * + * @v xfer Data transfer interface + * @v rc Reason for close + */ +static void http_xfer_close ( struct xfer_interface *xfer, int rc ) { + struct http_request *http = + container_of ( xfer, struct http_request, xfer ); + + DBGC ( http, "HTTP %p interface closed: %s\n", + http, strerror ( rc ) ); + + http_done ( http, rc ); +} + +/** HTTP data transfer interface operations */ +static struct xfer_interface_operations http_xfer_operations = { + .close = http_xfer_close, + .vredirect = ignore_xfer_vredirect, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = xfer_deliver_as_raw, + .deliver_raw = ignore_xfer_deliver_raw, +}; + +/** + * Initiate an HTTP connection, with optional filter + * + * @v xfer Data transfer interface + * @v uri Uniform Resource Identifier + * @v default_port Default port number + * @v filter Filter to apply to socket, or NULL + * @ret rc Return status code + */ +int http_open_filter ( struct xfer_interface *xfer, struct uri *uri, + unsigned int default_port, + int ( * filter ) ( struct xfer_interface *xfer, + struct xfer_interface **next ) ) { + struct http_request *http; + struct sockaddr_tcpip server; + struct xfer_interface *socket; + int rc; + + /* Sanity checks */ + if ( ! uri->host ) + return -EINVAL; + + /* Allocate and populate HTTP structure */ + http = zalloc ( sizeof ( *http ) ); + if ( ! http ) + return -ENOMEM; + http->refcnt.free = http_free; + xfer_init ( &http->xfer, &http_xfer_operations, &http->refcnt ); + http->uri = uri_get ( uri ); + xfer_init ( &http->socket, &http_socket_operations, &http->refcnt ); + process_init ( &http->process, http_step, &http->refcnt ); + + /* Open socket */ + memset ( &server, 0, sizeof ( server ) ); + server.st_port = htons ( uri_port ( http->uri, default_port ) ); + socket = &http->socket; + if ( filter ) { + if ( ( rc = filter ( socket, &socket ) ) != 0 ) + goto err; + } + if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM, + ( struct sockaddr * ) &server, + uri->host, NULL ) ) != 0 ) + goto err; + + /* Attach to parent interface, mortalise self, and return */ + xfer_plug_plug ( &http->xfer, xfer ); + ref_put ( &http->refcnt ); + return 0; + + err: + DBGC ( http, "HTTP %p could not create request: %s\n", + http, strerror ( rc ) ); + http_done ( http, rc ); + ref_put ( &http->refcnt ); + return rc; +} + +/** + * Initiate an HTTP connection + * + * @v xfer Data transfer interface + * @v uri Uniform Resource Identifier + * @ret rc Return status code + */ +static int http_open ( struct xfer_interface *xfer, struct uri *uri ) { + return http_open_filter ( xfer, uri, HTTP_PORT, NULL ); +} + +/** HTTP URI opener */ +struct uri_opener http_uri_opener __uri_opener = { + .scheme = "http", + .open = http_open, +}; diff --git a/gpxe/src/net/tcp/https.c b/gpxe/src/net/tcp/https.c new file mode 100644 index 0000000..15ab32e --- /dev/null +++ b/gpxe/src/net/tcp/https.c @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2007 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. + */ + +/** + * @file + * + * Secure Hyper Text Transfer Protocol (HTTPS) + * + */ + +#include +#include +#include +#include +#include + +FEATURE ( FEATURE_PROTOCOL, "HTTPS", DHCP_EB_FEATURE_HTTPS, 1 ); + +/** + * Initiate an HTTPS connection + * + * @v xfer Data transfer interface + * @v uri Uniform Resource Identifier + * @ret rc Return status code + */ +static int https_open ( struct xfer_interface *xfer, struct uri *uri ) { + return http_open_filter ( xfer, uri, HTTPS_PORT, add_tls ); +} + +/** HTTPS URI opener */ +struct uri_opener https_uri_opener __uri_opener = { + .scheme = "https", + .open = https_open, +}; diff --git a/gpxe/src/net/tcp/iscsi.c b/gpxe/src/net/tcp/iscsi.c new file mode 100644 index 0000000..45519e6 --- /dev/null +++ b/gpxe/src/net/tcp/iscsi.c @@ -0,0 +1,1981 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * iSCSI protocol + * + */ + +FEATURE ( FEATURE_PROTOCOL, "iSCSI", DHCP_EB_FEATURE_ISCSI, 1 ); + +/** iSCSI initiator name (explicitly specified) */ +static char *iscsi_explicit_initiator_iqn; + +/** Default iSCSI initiator name (constructed from hostname) */ +static char *iscsi_default_initiator_iqn; + +/** iSCSI initiator username */ +static char *iscsi_initiator_username; + +/** iSCSI initiator password */ +static char *iscsi_initiator_password; + +/** iSCSI target username */ +static char *iscsi_target_username; + +/** iSCSI target password */ +static char *iscsi_target_password; + +static void iscsi_start_tx ( struct iscsi_session *iscsi ); +static void iscsi_start_login ( struct iscsi_session *iscsi ); +static void iscsi_start_data_out ( struct iscsi_session *iscsi, + unsigned int datasn ); + +/** + * Finish receiving PDU data into buffer + * + * @v iscsi iSCSI session + */ +static void iscsi_rx_buffered_data_done ( struct iscsi_session *iscsi ) { + free ( iscsi->rx_buffer ); + iscsi->rx_buffer = NULL; +} + +/** + * Free iSCSI session + * + * @v refcnt Reference counter + */ +static void iscsi_free ( struct refcnt *refcnt ) { + struct iscsi_session *iscsi = + container_of ( refcnt, struct iscsi_session, refcnt ); + + free ( iscsi->target_address ); + free ( iscsi->target_iqn ); + free ( iscsi->initiator_username ); + free ( iscsi->initiator_password ); + free ( iscsi->target_username ); + free ( iscsi->target_password ); + chap_finish ( &iscsi->chap ); + iscsi_rx_buffered_data_done ( iscsi ); + free ( iscsi ); +} + +/** + * Open iSCSI transport-layer connection + * + * @v iscsi iSCSI session + * @ret rc Return status code + */ +static int iscsi_open_connection ( struct iscsi_session *iscsi ) { + struct sockaddr_tcpip target; + int rc; + + assert ( iscsi->tx_state == ISCSI_TX_IDLE ); + assert ( iscsi->rx_state == ISCSI_RX_BHS ); + assert ( iscsi->rx_offset == 0 ); + + /* Open socket */ + memset ( &target, 0, sizeof ( target ) ); + target.st_port = htons ( iscsi->target_port ); + if ( ( rc = xfer_open_named_socket ( &iscsi->socket, SOCK_STREAM, + ( struct sockaddr * ) &target, + iscsi->target_address, + NULL ) ) != 0 ) { + DBGC ( iscsi, "iSCSI %p could not open socket: %s\n", + iscsi, strerror ( rc ) ); + return rc; + } + + /* Enter security negotiation phase */ + iscsi->status = ( ISCSI_STATUS_SECURITY_NEGOTIATION_PHASE | + ISCSI_STATUS_STRINGS_SECURITY ); + if ( iscsi->target_username ) + iscsi->status |= ISCSI_STATUS_AUTH_REVERSE_REQUIRED; + + /* Assign fresh initiator task tag */ + iscsi->itt++; + + /* Initiate login */ + iscsi_start_login ( iscsi ); + + return 0; +} + +/** + * Close iSCSI transport-layer connection + * + * @v iscsi iSCSI session + * @v rc Reason for close + * + * Closes the transport-layer connection and resets the session state + * ready to attempt a fresh login. + */ +static void iscsi_close_connection ( struct iscsi_session *iscsi, int rc ) { + + /* Close all data transfer interfaces */ + xfer_close ( &iscsi->socket, rc ); + + /* Clear connection status */ + iscsi->status = 0; + + /* Reset TX and RX state machines */ + iscsi->tx_state = ISCSI_TX_IDLE; + iscsi->rx_state = ISCSI_RX_BHS; + iscsi->rx_offset = 0; + + /* Free any temporary dynamically allocated memory */ + chap_finish ( &iscsi->chap ); + iscsi_rx_buffered_data_done ( iscsi ); +} + +/** + * Mark iSCSI SCSI operation as complete + * + * @v iscsi iSCSI session + * @v rc Return status code + * + * Note that iscsi_scsi_done() will not close the connection, and must + * therefore be called only when the internal state machines are in an + * appropriate state, otherwise bad things may happen on the next call + * to iscsi_issue(). The general rule is to call iscsi_scsi_done() + * only at the end of receiving a PDU; at this point the TX and RX + * engines should both be idle. + */ +static void iscsi_scsi_done ( struct iscsi_session *iscsi, int rc ) { + + assert ( iscsi->tx_state == ISCSI_TX_IDLE ); + + iscsi->command = NULL; + iscsi->rc = rc; +} + +/**************************************************************************** + * + * iSCSI SCSI command issuing + * + */ + +/** + * Build iSCSI SCSI command BHS + * + * @v iscsi iSCSI session + * + * We don't currently support bidirectional commands (i.e. with both + * Data-In and Data-Out segments); these would require providing code + * to generate an AHS, and there doesn't seem to be any need for it at + * the moment. + */ +static void iscsi_start_command ( struct iscsi_session *iscsi ) { + struct iscsi_bhs_scsi_command *command = &iscsi->tx_bhs.scsi_command; + + assert ( ! ( iscsi->command->data_in && iscsi->command->data_out ) ); + + /* Construct BHS and initiate transmission */ + iscsi_start_tx ( iscsi ); + command->opcode = ISCSI_OPCODE_SCSI_COMMAND; + command->flags = ( ISCSI_FLAG_FINAL | + ISCSI_COMMAND_ATTR_SIMPLE ); + if ( iscsi->command->data_in ) + command->flags |= ISCSI_COMMAND_FLAG_READ; + if ( iscsi->command->data_out ) + command->flags |= ISCSI_COMMAND_FLAG_WRITE; + /* lengths left as zero */ + command->lun = iscsi->lun; + command->itt = htonl ( ++iscsi->itt ); + command->exp_len = htonl ( iscsi->command->data_in_len | + iscsi->command->data_out_len ); + command->cmdsn = htonl ( iscsi->cmdsn ); + command->expstatsn = htonl ( iscsi->statsn + 1 ); + memcpy ( &command->cdb, &iscsi->command->cdb, sizeof ( command->cdb )); + DBGC2 ( iscsi, "iSCSI %p start " SCSI_CDB_FORMAT " %s %#zx\n", + iscsi, SCSI_CDB_DATA ( command->cdb ), + ( iscsi->command->data_in ? "in" : "out" ), + ( iscsi->command->data_in ? + iscsi->command->data_in_len : + iscsi->command->data_out_len ) ); +} + +/** + * Receive data segment of an iSCSI SCSI response PDU + * + * @v iscsi iSCSI session + * @v data Received data + * @v len Length of received data + * @v remaining Data remaining after this data + * @ret rc Return status code + */ +static int iscsi_rx_scsi_response ( struct iscsi_session *iscsi, + const void *data, size_t len, + size_t remaining ) { + struct iscsi_bhs_scsi_response *response + = &iscsi->rx_bhs.scsi_response; + int sense_offset; + + /* Capture the sense response code as it floats past, if present */ + sense_offset = ISCSI_SENSE_RESPONSE_CODE_OFFSET - iscsi->rx_offset; + if ( ( sense_offset >= 0 ) && len ) { + iscsi->command->sense_response = + * ( ( char * ) data + sense_offset ); + } + + /* Wait for whole SCSI response to arrive */ + if ( remaining ) + return 0; + + /* Record SCSI status code */ + iscsi->command->status = response->status; + + /* Check for errors */ + if ( response->response != ISCSI_RESPONSE_COMMAND_COMPLETE ) + return -EIO; + + /* Mark as completed */ + iscsi_scsi_done ( iscsi, 0 ); + return 0; +} + +/** + * Receive data segment of an iSCSI data-in PDU + * + * @v iscsi iSCSI session + * @v data Received data + * @v len Length of received data + * @v remaining Data remaining after this data + * @ret rc Return status code + */ +static int iscsi_rx_data_in ( struct iscsi_session *iscsi, + const void *data, size_t len, + size_t remaining ) { + struct iscsi_bhs_data_in *data_in = &iscsi->rx_bhs.data_in; + unsigned long offset; + + /* Copy data to data-in buffer */ + offset = ntohl ( data_in->offset ) + iscsi->rx_offset; + assert ( iscsi->command != NULL ); + assert ( iscsi->command->data_in ); + assert ( ( offset + len ) <= iscsi->command->data_in_len ); + copy_to_user ( iscsi->command->data_in, offset, data, len ); + + /* Wait for whole SCSI response to arrive */ + if ( remaining ) + return 0; + + /* Mark as completed if status is present */ + if ( data_in->flags & ISCSI_DATA_FLAG_STATUS ) { + assert ( ( offset + len ) == iscsi->command->data_in_len ); + assert ( data_in->flags & ISCSI_FLAG_FINAL ); + iscsi->command->status = data_in->status; + /* iSCSI cannot return an error status via a data-in */ + iscsi_scsi_done ( iscsi, 0 ); + } + + return 0; +} + +/** + * Receive data segment of an iSCSI R2T PDU + * + * @v iscsi iSCSI session + * @v data Received data + * @v len Length of received data + * @v remaining Data remaining after this data + * @ret rc Return status code + */ +static int iscsi_rx_r2t ( struct iscsi_session *iscsi, + const void *data __unused, size_t len __unused, + size_t remaining __unused ) { + struct iscsi_bhs_r2t *r2t = &iscsi->rx_bhs.r2t; + + /* Record transfer parameters and trigger first data-out */ + iscsi->ttt = ntohl ( r2t->ttt ); + iscsi->transfer_offset = ntohl ( r2t->offset ); + iscsi->transfer_len = ntohl ( r2t->len ); + iscsi_start_data_out ( iscsi, 0 ); + + return 0; +} + +/** + * Build iSCSI data-out BHS + * + * @v iscsi iSCSI session + * @v datasn Data sequence number within the transfer + * + */ +static void iscsi_start_data_out ( struct iscsi_session *iscsi, + unsigned int datasn ) { + struct iscsi_bhs_data_out *data_out = &iscsi->tx_bhs.data_out; + unsigned long offset; + unsigned long remaining; + unsigned long len; + + /* We always send 512-byte Data-Out PDUs; this removes the + * need to worry about the target's MaxRecvDataSegmentLength. + */ + offset = datasn * 512; + remaining = iscsi->transfer_len - offset; + len = remaining; + if ( len > 512 ) + len = 512; + + /* Construct BHS and initiate transmission */ + iscsi_start_tx ( iscsi ); + data_out->opcode = ISCSI_OPCODE_DATA_OUT; + if ( len == remaining ) + data_out->flags = ( ISCSI_FLAG_FINAL ); + ISCSI_SET_LENGTHS ( data_out->lengths, 0, len ); + data_out->lun = iscsi->lun; + data_out->itt = htonl ( iscsi->itt ); + data_out->ttt = htonl ( iscsi->ttt ); + data_out->expstatsn = htonl ( iscsi->statsn + 1 ); + data_out->datasn = htonl ( datasn ); + data_out->offset = htonl ( iscsi->transfer_offset + offset ); + DBGC ( iscsi, "iSCSI %p start data out DataSN %#x len %#lx\n", + iscsi, datasn, len ); +} + +/** + * Complete iSCSI data-out PDU transmission + * + * @v iscsi iSCSI session + * + */ +static void iscsi_data_out_done ( struct iscsi_session *iscsi ) { + struct iscsi_bhs_data_out *data_out = &iscsi->tx_bhs.data_out; + + /* If we haven't reached the end of the sequence, start + * sending the next data-out PDU. + */ + if ( ! ( data_out->flags & ISCSI_FLAG_FINAL ) ) + iscsi_start_data_out ( iscsi, ntohl ( data_out->datasn ) + 1 ); +} + +/** + * Send iSCSI data-out data segment + * + * @v iscsi iSCSI session + * @ret rc Return status code + */ +static int iscsi_tx_data_out ( struct iscsi_session *iscsi ) { + struct iscsi_bhs_data_out *data_out = &iscsi->tx_bhs.data_out; + struct io_buffer *iobuf; + unsigned long offset; + size_t len; + + offset = ntohl ( data_out->offset ); + len = ISCSI_DATA_LEN ( data_out->lengths ); + + assert ( iscsi->command != NULL ); + assert ( iscsi->command->data_out ); + assert ( ( offset + len ) <= iscsi->command->data_out_len ); + + iobuf = xfer_alloc_iob ( &iscsi->socket, len ); + if ( ! iobuf ) + return -ENOMEM; + + copy_from_user ( iob_put ( iobuf, len ), + iscsi->command->data_out, offset, len ); + + return xfer_deliver_iob ( &iscsi->socket, iobuf ); +} + +/**************************************************************************** + * + * iSCSI login + * + */ + +/** + * Build iSCSI login request strings + * + * @v iscsi iSCSI session + * + * These are the initial set of strings sent in the first login + * request PDU. We want the following settings: + * + * HeaderDigest=None + * DataDigest=None + * MaxConnections is irrelevant; we make only one connection anyway + * InitialR2T=Yes [1] + * ImmediateData is irrelevant; we never send immediate data + * MaxRecvDataSegmentLength=8192 (default; we don't care) [3] + * MaxBurstLength=262144 (default; we don't care) [3] + * FirstBurstLength=262144 (default; we don't care) + * DefaultTime2Wait=0 [2] + * DefaultTime2Retain=0 [2] + * MaxOutstandingR2T=1 + * DataPDUInOrder=Yes + * DataSequenceInOrder=Yes + * ErrorRecoveryLevel=0 + * + * [1] InitialR2T has an OR resolution function, so the target may + * force us to use it. We therefore simplify our logic by always + * using it. + * + * [2] These ensure that we can safely start a new task once we have + * reconnected after a failure, without having to manually tidy up + * after the old one. + * + * [3] We are quite happy to use the RFC-defined default values for + * these parameters, but some targets (notably OpenSolaris) + * incorrectly assume a default value of zero, so we explicitly + * specify the default values. + */ +static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi, + void *data, size_t len ) { + unsigned int used = 0; + unsigned int i; + const char *auth_method; + + if ( iscsi->status & ISCSI_STATUS_STRINGS_SECURITY ) { + /* Default to allowing no authentication */ + auth_method = "None"; + /* If we have a credential to supply, permit CHAP */ + if ( iscsi->initiator_username ) + auth_method = "CHAP,None"; + /* If we have a credential to check, force CHAP */ + if ( iscsi->target_username ) + auth_method = "CHAP"; + used += ssnprintf ( data + used, len - used, + "InitiatorName=%s%c" + "TargetName=%s%c" + "SessionType=Normal%c" + "AuthMethod=%s%c", + iscsi_initiator_iqn(), 0, + iscsi->target_iqn, 0, 0, + auth_method, 0 ); + } + + if ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_ALGORITHM ) { + used += ssnprintf ( data + used, len - used, "CHAP_A=5%c", 0 ); + } + + if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_RESPONSE ) ) { + assert ( iscsi->initiator_username != NULL ); + used += ssnprintf ( data + used, len - used, + "CHAP_N=%s%cCHAP_R=0x", + iscsi->initiator_username, 0 ); + for ( i = 0 ; i < iscsi->chap.response_len ; i++ ) { + used += ssnprintf ( data + used, len - used, "%02x", + iscsi->chap.response[i] ); + } + used += ssnprintf ( data + used, len - used, "%c", 0 ); + } + + if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_CHALLENGE ) ) { + used += ssnprintf ( data + used, len - used, + "CHAP_I=%d%cCHAP_C=0x", + iscsi->chap_challenge[0], 0 ); + for ( i = 1 ; i < sizeof ( iscsi->chap_challenge ) ; i++ ) { + used += ssnprintf ( data + used, len - used, "%02x", + iscsi->chap_challenge[i] ); + } + used += ssnprintf ( data + used, len - used, "%c", 0 ); + } + + if ( iscsi->status & ISCSI_STATUS_STRINGS_OPERATIONAL ) { + used += ssnprintf ( data + used, len - used, + "HeaderDigest=None%c" + "DataDigest=None%c" + "InitialR2T=Yes%c" + "MaxRecvDataSegmentLength=8192%c" + "MaxBurstLength=262144%c" + "DefaultTime2Wait=0%c" + "DefaultTime2Retain=0%c" + "MaxOutstandingR2T=1%c" + "DataPDUInOrder=Yes%c" + "DataSequenceInOrder=Yes%c" + "ErrorRecoveryLevel=0%c", + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + } + + return used; +} + +/** + * Build iSCSI login request BHS + * + * @v iscsi iSCSI session + */ +static void iscsi_start_login ( struct iscsi_session *iscsi ) { + struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request; + int len; + + /* Construct BHS and initiate transmission */ + iscsi_start_tx ( iscsi ); + request->opcode = ( ISCSI_OPCODE_LOGIN_REQUEST | + ISCSI_FLAG_IMMEDIATE ); + request->flags = ( ( iscsi->status & ISCSI_STATUS_PHASE_MASK ) | + ISCSI_LOGIN_FLAG_TRANSITION ); + /* version_max and version_min left as zero */ + len = iscsi_build_login_request_strings ( iscsi, NULL, 0 ); + ISCSI_SET_LENGTHS ( request->lengths, 0, len ); + request->isid_iana_en = htonl ( ISCSI_ISID_IANA | + IANA_EN_FEN_SYSTEMS ); + /* isid_iana_qual left as zero */ + request->tsih = htons ( iscsi->tsih ); + request->itt = htonl ( iscsi->itt ); + /* cid left as zero */ + request->cmdsn = htonl ( iscsi->cmdsn ); + request->expstatsn = htonl ( iscsi->statsn + 1 ); +} + +/** + * Complete iSCSI login request PDU transmission + * + * @v iscsi iSCSI session + * + */ +static void iscsi_login_request_done ( struct iscsi_session *iscsi ) { + + /* Clear any "strings to send" flags */ + iscsi->status &= ~ISCSI_STATUS_STRINGS_MASK; + + /* Free any dynamically allocated storage used for login */ + chap_finish ( &iscsi->chap ); +} + +/** + * Transmit data segment of an iSCSI login request PDU + * + * @v iscsi iSCSI session + * @ret rc Return status code + * + * For login requests, the data segment consists of the login strings. + */ +static int iscsi_tx_login_request ( struct iscsi_session *iscsi ) { + struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request; + struct io_buffer *iobuf; + size_t len; + + len = ISCSI_DATA_LEN ( request->lengths ); + iobuf = xfer_alloc_iob ( &iscsi->socket, len ); + if ( ! iobuf ) + return -ENOMEM; + iob_put ( iobuf, len ); + iscsi_build_login_request_strings ( iscsi, iobuf->data, len ); + return xfer_deliver_iob ( &iscsi->socket, iobuf ); +} + +/** + * Handle iSCSI TargetAddress text value + * + * @v iscsi iSCSI session + * @v value TargetAddress value + * @ret rc Return status code + */ +static int iscsi_handle_targetaddress_value ( struct iscsi_session *iscsi, + const char *value ) { + char *separator; + + DBGC ( iscsi, "iSCSI %p will redirect to %s\n", iscsi, value ); + + /* Replace target address */ + free ( iscsi->target_address ); + iscsi->target_address = strdup ( value ); + if ( ! iscsi->target_address ) + return -ENOMEM; + + /* Replace target port */ + iscsi->target_port = htons ( ISCSI_PORT ); + separator = strchr ( iscsi->target_address, ':' ); + if ( separator ) { + *separator = '\0'; + iscsi->target_port = strtoul ( ( separator + 1 ), NULL, 0 ); + } + + return 0; +} + +/** + * Handle iSCSI AuthMethod text value + * + * @v iscsi iSCSI session + * @v value AuthMethod value + * @ret rc Return status code + */ +static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi, + const char *value ) { + + /* If server requests CHAP, send the CHAP_A string */ + if ( strcmp ( value, "CHAP" ) == 0 ) { + DBGC ( iscsi, "iSCSI %p initiating CHAP authentication\n", + iscsi ); + iscsi->status |= ( ISCSI_STATUS_STRINGS_CHAP_ALGORITHM | + ISCSI_STATUS_AUTH_FORWARD_REQUIRED ); + } + + return 0; +} + +/** + * Handle iSCSI CHAP_A text value + * + * @v iscsi iSCSI session + * @v value CHAP_A value + * @ret rc Return status code + */ +static int iscsi_handle_chap_a_value ( struct iscsi_session *iscsi, + const char *value ) { + + /* We only ever offer "5" (i.e. MD5) as an algorithm, so if + * the server responds with anything else it is a protocol + * violation. + */ + if ( strcmp ( value, "5" ) != 0 ) { + DBGC ( iscsi, "iSCSI %p got invalid CHAP algorithm \"%s\"\n", + iscsi, value ); + return -EPROTO; + } + + return 0; +} + +/** + * Handle iSCSI CHAP_I text value + * + * @v iscsi iSCSI session + * @v value CHAP_I value + * @ret rc Return status code + */ +static int iscsi_handle_chap_i_value ( struct iscsi_session *iscsi, + const char *value ) { + unsigned int identifier; + char *endp; + int rc; + + /* The CHAP identifier is an integer value */ + identifier = strtoul ( value, &endp, 0 ); + if ( *endp != '\0' ) { + DBGC ( iscsi, "iSCSI %p saw invalid CHAP identifier \"%s\"\n", + iscsi, value ); + return -EPROTO; + } + + /* Prepare for CHAP with MD5 */ + chap_finish ( &iscsi->chap ); + if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) { + DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n", + iscsi, strerror ( rc ) ); + return rc; + } + + /* Identifier and secret are the first two components of the + * challenge. + */ + chap_set_identifier ( &iscsi->chap, identifier ); + if ( iscsi->initiator_password ) { + chap_update ( &iscsi->chap, iscsi->initiator_password, + strlen ( iscsi->initiator_password ) ); + } + + return 0; +} + +/** + * Handle iSCSI CHAP_C text value + * + * @v iscsi iSCSI session + * @v value CHAP_C value + * @ret rc Return status code + */ +static int iscsi_handle_chap_c_value ( struct iscsi_session *iscsi, + const char *value ) { + char buf[3]; + char *endp; + uint8_t byte; + unsigned int i; + + /* Check and strip leading "0x" */ + if ( ( value[0] != '0' ) || ( value[1] != 'x' ) ) { + DBGC ( iscsi, "iSCSI %p saw invalid CHAP challenge \"%s\"\n", + iscsi, value ); + return -EPROTO; + } + value += 2; + + /* Process challenge an octet at a time */ + for ( ; ( value[0] && value[1] ) ; value += 2 ) { + memcpy ( buf, value, 2 ); + buf[2] = 0; + byte = strtoul ( buf, &endp, 16 ); + if ( *endp != '\0' ) { + DBGC ( iscsi, "iSCSI %p saw invalid CHAP challenge " + "byte \"%s\"\n", iscsi, buf ); + return -EPROTO; + } + chap_update ( &iscsi->chap, &byte, sizeof ( byte ) ); + } + + /* Build CHAP response */ + DBGC ( iscsi, "iSCSI %p sending CHAP response\n", iscsi ); + chap_respond ( &iscsi->chap ); + iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_RESPONSE; + + /* Send CHAP challenge, if applicable */ + if ( iscsi->target_username ) { + iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_CHALLENGE; + /* Generate CHAP challenge data */ + for ( i = 0 ; i < sizeof ( iscsi->chap_challenge ) ; i++ ) { + iscsi->chap_challenge[i] = random(); + } + } + + return 0; +} + +/** + * Handle iSCSI CHAP_N text value + * + * @v iscsi iSCSI session + * @v value CHAP_N value + * @ret rc Return status code + */ +static int iscsi_handle_chap_n_value ( struct iscsi_session *iscsi, + const char *value ) { + + /* The target username isn't actually involved at any point in + * the authentication process; it merely serves to identify + * which password the target is using to generate the CHAP + * response. We unnecessarily verify that the username is as + * expected, in order to provide mildly helpful diagnostics if + * the target is supplying the wrong username/password + * combination. + */ + if ( iscsi->target_username && + ( strcmp ( iscsi->target_username, value ) != 0 ) ) { + DBGC ( iscsi, "iSCSI %p target username \"%s\" incorrect " + "(wanted \"%s\")\n", + iscsi, value, iscsi->target_username ); + return -EACCES; + } + + return 0; +} + +/** + * Handle iSCSI CHAP_R text value + * + * @v iscsi iSCSI session + * @v value CHAP_R value + * @ret rc Return status code + */ +static int iscsi_handle_chap_r_value ( struct iscsi_session *iscsi, + const char *value ) { + char buf[3]; + char *endp; + uint8_t byte; + unsigned int i; + int rc; + + /* Generate CHAP response for verification */ + chap_finish ( &iscsi->chap ); + if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) { + DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n", + iscsi, strerror ( rc ) ); + return rc; + } + chap_set_identifier ( &iscsi->chap, iscsi->chap_challenge[0] ); + if ( iscsi->target_password ) { + chap_update ( &iscsi->chap, iscsi->target_password, + strlen ( iscsi->target_password ) ); + } + chap_update ( &iscsi->chap, &iscsi->chap_challenge[1], + ( sizeof ( iscsi->chap_challenge ) - 1 ) ); + chap_respond ( &iscsi->chap ); + + /* Check and strip leading "0x" */ + if ( ( value[0] != '0' ) || ( value[1] != 'x' ) ) { + DBGC ( iscsi, "iSCSI %p saw invalid CHAP response \"%s\"\n", + iscsi, value ); + return -EPROTO; + } + value += 2; + + /* Check CHAP response length */ + if ( strlen ( value ) != ( 2 * iscsi->chap.response_len ) ) { + DBGC ( iscsi, "iSCSI %p invalid CHAP response length\n", + iscsi ); + return -EPROTO; + } + + /* Process response an octet at a time */ + for ( i = 0 ; ( value[0] && value[1] ) ; value += 2, i++ ) { + memcpy ( buf, value, 2 ); + buf[2] = 0; + byte = strtoul ( buf, &endp, 16 ); + if ( *endp != '\0' ) { + DBGC ( iscsi, "iSCSI %p saw invalid CHAP response " + "byte \"%s\"\n", iscsi, buf ); + return -EPROTO; + } + if ( byte != iscsi->chap.response[i] ) { + DBGC ( iscsi, "iSCSI %p saw incorrect CHAP " + "response\n", iscsi ); + return -EACCES; + } + } + assert ( i == iscsi->chap.response_len ); + + /* Mark session as authenticated */ + iscsi->status |= ISCSI_STATUS_AUTH_REVERSE_OK; + + return 0; +} + +/** An iSCSI text string that we want to handle */ +struct iscsi_string_type { + /** String key + * + * This is the portion up to and including the "=" sign, + * e.g. "InitiatorName=", "CHAP_A=", etc. + */ + const char *key; + /** Handle iSCSI string value + * + * @v iscsi iSCSI session + * @v value iSCSI string value + * @ret rc Return status code + */ + int ( * handle ) ( struct iscsi_session *iscsi, const char *value ); +}; + +/** iSCSI text strings that we want to handle */ +static struct iscsi_string_type iscsi_string_types[] = { + { "TargetAddress=", iscsi_handle_targetaddress_value }, + { "AuthMethod=", iscsi_handle_authmethod_value }, + { "CHAP_A=", iscsi_handle_chap_a_value }, + { "CHAP_I=", iscsi_handle_chap_i_value }, + { "CHAP_C=", iscsi_handle_chap_c_value }, + { "CHAP_N=", iscsi_handle_chap_n_value }, + { "CHAP_R=", iscsi_handle_chap_r_value }, + { NULL, NULL } +}; + +/** + * Handle iSCSI string + * + * @v iscsi iSCSI session + * @v string iSCSI string (in "key=value" format) + * @ret rc Return status code + */ +static int iscsi_handle_string ( struct iscsi_session *iscsi, + const char *string ) { + struct iscsi_string_type *type; + size_t key_len; + int rc; + + for ( type = iscsi_string_types ; type->key ; type++ ) { + key_len = strlen ( type->key ); + if ( strncmp ( string, type->key, key_len ) != 0 ) + continue; + DBGC ( iscsi, "iSCSI %p handling %s\n", iscsi, string ); + if ( ( rc = type->handle ( iscsi, + ( string + key_len ) ) ) != 0 ) { + DBGC ( iscsi, "iSCSI %p could not handle %s: %s\n", + iscsi, string, strerror ( rc ) ); + return rc; + } + return 0; + } + DBGC ( iscsi, "iSCSI %p ignoring %s\n", iscsi, string ); + return 0; +} + +/** + * Handle iSCSI strings + * + * @v iscsi iSCSI session + * @v string iSCSI string buffer + * @v len Length of string buffer + * @ret rc Return status code + */ +static int iscsi_handle_strings ( struct iscsi_session *iscsi, + const char *strings, size_t len ) { + size_t string_len; + int rc; + + /* Handle each string in turn, taking care not to overrun the + * data buffer in case of badly-terminated data. + */ + while ( 1 ) { + string_len = ( strnlen ( strings, len ) + 1 ); + if ( string_len > len ) + break; + if ( ( rc = iscsi_handle_string ( iscsi, strings ) ) != 0 ) + return rc; + strings += string_len; + len -= string_len; + } + return 0; +} + +/** + * Receive PDU data into buffer + * + * @v iscsi iSCSI session + * @v data Data to receive + * @v len Length of data + * @ret rc Return status code + * + * This can be used when the RX PDU type handler wishes to buffer up + * all received data and process the PDU as a single unit. The caller + * is repsonsible for calling iscsi_rx_buffered_data_done() after + * processing the data. + */ +static int iscsi_rx_buffered_data ( struct iscsi_session *iscsi, + const void *data, size_t len ) { + + /* Allocate buffer on first call */ + if ( ! iscsi->rx_buffer ) { + iscsi->rx_buffer = malloc ( iscsi->rx_len ); + if ( ! iscsi->rx_buffer ) + return -ENOMEM; + } + + /* Copy data to buffer */ + assert ( ( iscsi->rx_offset + len ) <= iscsi->rx_len ); + memcpy ( ( iscsi->rx_buffer + iscsi->rx_offset ), data, len ); + + return 0; +} + +/** + * Convert iSCSI response status to return status code + * + * @v status_class iSCSI status class + * @v status_detail iSCSI status detail + * @ret rc Return status code + */ +static int iscsi_status_to_rc ( unsigned int status_class, + unsigned int status_detail ) { + switch ( status_class ) { + case ISCSI_STATUS_INITIATOR_ERROR : + switch ( status_detail ) { + case ISCSI_STATUS_INITIATOR_ERROR_AUTHENTICATION : + return -EACCES; + case ISCSI_STATUS_INITIATOR_ERROR_AUTHORISATION : + return -EPERM; + case ISCSI_STATUS_INITIATOR_ERROR_NOT_FOUND : + case ISCSI_STATUS_INITIATOR_ERROR_REMOVED : + return -ENODEV; + default : + return -ENOTSUP; + } + case ISCSI_STATUS_TARGET_ERROR : + return -EIO; + default : + return -EINVAL; + } +} + +/** + * Receive data segment of an iSCSI login response PDU + * + * @v iscsi iSCSI session + * @v data Received data + * @v len Length of received data + * @v remaining Data remaining after this data + * @ret rc Return status code + */ +static int iscsi_rx_login_response ( struct iscsi_session *iscsi, + const void *data, size_t len, + size_t remaining ) { + struct iscsi_bhs_login_response *response + = &iscsi->rx_bhs.login_response; + int rc; + + /* Buffer up the PDU data */ + if ( ( rc = iscsi_rx_buffered_data ( iscsi, data, len ) ) != 0 ) { + DBGC ( iscsi, "iSCSI %p could not buffer login response: %s\n", + iscsi, strerror ( rc ) ); + return rc; + } + if ( remaining ) + return 0; + + /* Process string data and discard string buffer */ + if ( ( rc = iscsi_handle_strings ( iscsi, iscsi->rx_buffer, + iscsi->rx_len ) ) != 0 ) + return rc; + iscsi_rx_buffered_data_done ( iscsi ); + + /* Check for login redirection */ + if ( response->status_class == ISCSI_STATUS_REDIRECT ) { + DBGC ( iscsi, "iSCSI %p redirecting to new server\n", iscsi ); + iscsi_close_connection ( iscsi, 0 ); + if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 ) { + DBGC ( iscsi, "iSCSI %p could not redirect: %s\n ", + iscsi, strerror ( rc ) ); + return rc; + } + return 0; + } + + /* Check for fatal errors */ + if ( response->status_class != 0 ) { + DBGC ( iscsi, "iSCSI login failure: class %02x detail %02x\n", + response->status_class, response->status_detail ); + rc = iscsi_status_to_rc ( response->status_class, + response->status_detail ); + iscsi->instant_rc = rc; + return rc; + } + + /* Handle login transitions */ + if ( response->flags & ISCSI_LOGIN_FLAG_TRANSITION ) { + iscsi->status &= ~( ISCSI_STATUS_PHASE_MASK | + ISCSI_STATUS_STRINGS_MASK ); + switch ( response->flags & ISCSI_LOGIN_NSG_MASK ) { + case ISCSI_LOGIN_NSG_OPERATIONAL_NEGOTIATION: + iscsi->status |= + ( ISCSI_STATUS_OPERATIONAL_NEGOTIATION_PHASE | + ISCSI_STATUS_STRINGS_OPERATIONAL ); + break; + case ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE: + iscsi->status |= ISCSI_STATUS_FULL_FEATURE_PHASE; + break; + default: + DBGC ( iscsi, "iSCSI %p got invalid response flags " + "%02x\n", iscsi, response->flags ); + return -EIO; + } + } + + /* Send next login request PDU if we haven't reached the full + * feature phase yet. + */ + if ( ( iscsi->status & ISCSI_STATUS_PHASE_MASK ) != + ISCSI_STATUS_FULL_FEATURE_PHASE ) { + iscsi_start_login ( iscsi ); + return 0; + } + + /* Check that target authentication was successful (if required) */ + if ( ( iscsi->status & ISCSI_STATUS_AUTH_REVERSE_REQUIRED ) && + ! ( iscsi->status & ISCSI_STATUS_AUTH_REVERSE_OK ) ) { + DBGC ( iscsi, "iSCSI %p nefarious target tried to bypass " + "authentication\n", iscsi ); + return -EPROTO; + } + + /* Reset retry count */ + iscsi->retry_count = 0; + + /* Record TSIH for future reference */ + iscsi->tsih = ntohl ( response->tsih ); + + /* Send the actual SCSI command */ + iscsi_start_command ( iscsi ); + + return 0; +} + +/**************************************************************************** + * + * iSCSI to socket interface + * + */ + +/** + * Start up a new TX PDU + * + * @v iscsi iSCSI session + * + * This initiates the process of sending a new PDU. Only one PDU may + * be in transit at any one time. + */ +static void iscsi_start_tx ( struct iscsi_session *iscsi ) { + assert ( iscsi->tx_state == ISCSI_TX_IDLE ); + + /* Initialise TX BHS */ + memset ( &iscsi->tx_bhs, 0, sizeof ( iscsi->tx_bhs ) ); + + /* Flag TX engine to start transmitting */ + iscsi->tx_state = ISCSI_TX_BHS; +} + +/** + * Transmit nothing + * + * @v iscsi iSCSI session + * @ret rc Return status code + */ +static int iscsi_tx_nothing ( struct iscsi_session *iscsi __unused ) { + return 0; +} + +/** + * Transmit basic header segment of an iSCSI PDU + * + * @v iscsi iSCSI session + * @ret rc Return status code + */ +static int iscsi_tx_bhs ( struct iscsi_session *iscsi ) { + return xfer_deliver_raw ( &iscsi->socket, &iscsi->tx_bhs, + sizeof ( iscsi->tx_bhs ) ); +} + +/** + * Transmit data segment of an iSCSI PDU + * + * @v iscsi iSCSI session + * @ret rc Return status code + * + * Handle transmission of part of a PDU data segment. iscsi::tx_bhs + * will be valid when this is called. + */ +static int iscsi_tx_data ( struct iscsi_session *iscsi ) { + struct iscsi_bhs_common *common = &iscsi->tx_bhs.common; + + switch ( common->opcode & ISCSI_OPCODE_MASK ) { + case ISCSI_OPCODE_DATA_OUT: + return iscsi_tx_data_out ( iscsi ); + case ISCSI_OPCODE_LOGIN_REQUEST: + return iscsi_tx_login_request ( iscsi ); + default: + /* Nothing to send in other states */ + return 0; + } +} + +/** + * Transmit data padding of an iSCSI PDU + * + * @v iscsi iSCSI session + * @ret rc Return status code + * + * Handle transmission of any data padding in a PDU data segment. + * iscsi::tx_bhs will be valid when this is called. + */ +static int iscsi_tx_data_padding ( struct iscsi_session *iscsi ) { + static const char pad[] = { '\0', '\0', '\0' }; + struct iscsi_bhs_common *common = &iscsi->tx_bhs.common; + size_t pad_len; + + pad_len = ISCSI_DATA_PAD_LEN ( common->lengths ); + if ( ! pad_len ) + return 0; + + return xfer_deliver_raw ( &iscsi->socket, pad, pad_len ); +} + +/** + * Complete iSCSI PDU transmission + * + * @v iscsi iSCSI session + * + * Called when a PDU has been completely transmitted and the TX state + * machine is about to enter the idle state. iscsi::tx_bhs will be + * valid for the just-completed PDU when this is called. + */ +static void iscsi_tx_done ( struct iscsi_session *iscsi ) { + struct iscsi_bhs_common *common = &iscsi->tx_bhs.common; + + switch ( common->opcode & ISCSI_OPCODE_MASK ) { + case ISCSI_OPCODE_DATA_OUT: + iscsi_data_out_done ( iscsi ); + case ISCSI_OPCODE_LOGIN_REQUEST: + iscsi_login_request_done ( iscsi ); + default: + /* No action */ + break; + } +} + +/** + * Transmit iSCSI PDU + * + * @v iscsi iSCSI session + * @v buf Temporary data buffer + * @v len Length of temporary data buffer + * + * Constructs data to be sent for the current TX state + */ +static void iscsi_tx_step ( struct process *process ) { + struct iscsi_session *iscsi = + container_of ( process, struct iscsi_session, process ); + struct iscsi_bhs_common *common = &iscsi->tx_bhs.common; + int ( * tx ) ( struct iscsi_session *iscsi ); + enum iscsi_tx_state next_state; + size_t tx_len; + int rc; + + /* Select fragment to transmit */ + while ( 1 ) { + switch ( iscsi->tx_state ) { + case ISCSI_TX_IDLE: + /* Stop processing */ + return; + case ISCSI_TX_BHS: + tx = iscsi_tx_bhs; + tx_len = sizeof ( iscsi->tx_bhs ); + next_state = ISCSI_TX_AHS; + break; + case ISCSI_TX_AHS: + tx = iscsi_tx_nothing; + tx_len = 0; + next_state = ISCSI_TX_DATA; + break; + case ISCSI_TX_DATA: + tx = iscsi_tx_data; + tx_len = ISCSI_DATA_LEN ( common->lengths ); + next_state = ISCSI_TX_DATA_PADDING; + break; + case ISCSI_TX_DATA_PADDING: + tx = iscsi_tx_data_padding; + tx_len = ISCSI_DATA_PAD_LEN ( common->lengths ); + next_state = ISCSI_TX_IDLE; + break; + default: + assert ( 0 ); + return; + } + + /* Check for window availability, if needed */ + if ( tx_len && ( xfer_window ( &iscsi->socket ) == 0 ) ) { + /* Cannot transmit at this point; stop processing */ + return; + } + + /* Transmit data */ + if ( ( rc = tx ( iscsi ) ) != 0 ) { + DBGC ( iscsi, "iSCSI %p could not transmit: %s\n", + iscsi, strerror ( rc ) ); + return; + } + + /* Move to next state */ + iscsi->tx_state = next_state; + if ( next_state == ISCSI_TX_IDLE ) + iscsi_tx_done ( iscsi ); + } +} + +/** + * Receive basic header segment of an iSCSI PDU + * + * @v iscsi iSCSI session + * @v data Received data + * @v len Length of received data + * @v remaining Data remaining after this data + * @ret rc Return status code + * + * This fills in iscsi::rx_bhs with the data from the BHS portion of + * the received PDU. + */ +static int iscsi_rx_bhs ( struct iscsi_session *iscsi, const void *data, + size_t len, size_t remaining __unused ) { + memcpy ( &iscsi->rx_bhs.bytes[iscsi->rx_offset], data, len ); + if ( ( iscsi->rx_offset + len ) >= sizeof ( iscsi->rx_bhs ) ) { + DBGC2 ( iscsi, "iSCSI %p received PDU opcode %#x len %#x\n", + iscsi, iscsi->rx_bhs.common.opcode, + ISCSI_DATA_LEN ( iscsi->rx_bhs.common.lengths ) ); + } + return 0; +} + +/** + * Discard portion of an iSCSI PDU. + * + * @v iscsi iSCSI session + * @v data Received data + * @v len Length of received data + * @v remaining Data remaining after this data + * @ret rc Return status code + * + * This discards data from a portion of a received PDU. + */ +static int iscsi_rx_discard ( struct iscsi_session *iscsi __unused, + const void *data __unused, size_t len __unused, + size_t remaining __unused ) { + /* Do nothing */ + return 0; +} + +/** + * Receive data segment of an iSCSI PDU + * + * @v iscsi iSCSI session + * @v data Received data + * @v len Length of received data + * @v remaining Data remaining after this data + * @ret rc Return status code + * + * Handle processing of part of a PDU data segment. iscsi::rx_bhs + * will be valid when this is called. + */ +static int iscsi_rx_data ( struct iscsi_session *iscsi, const void *data, + size_t len, size_t remaining ) { + struct iscsi_bhs_common_response *response + = &iscsi->rx_bhs.common_response; + + /* Update cmdsn and statsn */ + iscsi->cmdsn = ntohl ( response->expcmdsn ); + iscsi->statsn = ntohl ( response->statsn ); + + switch ( response->opcode & ISCSI_OPCODE_MASK ) { + case ISCSI_OPCODE_LOGIN_RESPONSE: + return iscsi_rx_login_response ( iscsi, data, len, remaining ); + case ISCSI_OPCODE_SCSI_RESPONSE: + return iscsi_rx_scsi_response ( iscsi, data, len, remaining ); + case ISCSI_OPCODE_DATA_IN: + return iscsi_rx_data_in ( iscsi, data, len, remaining ); + case ISCSI_OPCODE_R2T: + return iscsi_rx_r2t ( iscsi, data, len, remaining ); + default: + if ( remaining ) + return 0; + DBGC ( iscsi, "iSCSI %p unknown opcode %02x\n", iscsi, + response->opcode ); + return -ENOTSUP; + } +} + +/** + * Receive new data + * + * @v socket Transport layer interface + * @v data Received data + * @v len Length of received data + * @ret rc Return status code + * + * This handles received PDUs. The receive strategy is to fill in + * iscsi::rx_bhs with the contents of the BHS portion of the PDU, + * throw away any AHS portion, and then process each part of the data + * portion as it arrives. The data processing routine therefore + * always has a full copy of the BHS available, even for portions of + * the data in different packets to the BHS. + */ +static int iscsi_socket_deliver_raw ( struct xfer_interface *socket, + const void *data, size_t len ) { + struct iscsi_session *iscsi = + container_of ( socket, struct iscsi_session, socket ); + struct iscsi_bhs_common *common = &iscsi->rx_bhs.common; + int ( * rx ) ( struct iscsi_session *iscsi, const void *data, + size_t len, size_t remaining ); + enum iscsi_rx_state next_state; + size_t frag_len; + size_t remaining; + int rc; + + while ( 1 ) { + switch ( iscsi->rx_state ) { + case ISCSI_RX_BHS: + rx = iscsi_rx_bhs; + iscsi->rx_len = sizeof ( iscsi->rx_bhs ); + next_state = ISCSI_RX_AHS; + break; + case ISCSI_RX_AHS: + rx = iscsi_rx_discard; + iscsi->rx_len = 4 * ISCSI_AHS_LEN ( common->lengths ); + next_state = ISCSI_RX_DATA; + break; + case ISCSI_RX_DATA: + rx = iscsi_rx_data; + iscsi->rx_len = ISCSI_DATA_LEN ( common->lengths ); + next_state = ISCSI_RX_DATA_PADDING; + break; + case ISCSI_RX_DATA_PADDING: + rx = iscsi_rx_discard; + iscsi->rx_len = ISCSI_DATA_PAD_LEN ( common->lengths ); + next_state = ISCSI_RX_BHS; + break; + default: + assert ( 0 ); + return -EINVAL; + } + + frag_len = iscsi->rx_len - iscsi->rx_offset; + if ( frag_len > len ) + frag_len = len; + remaining = iscsi->rx_len - iscsi->rx_offset - frag_len; + if ( ( rc = rx ( iscsi, data, frag_len, remaining ) ) != 0 ) { + DBGC ( iscsi, "iSCSI %p could not process received " + "data: %s\n", iscsi, strerror ( rc ) ); + iscsi_close_connection ( iscsi, rc ); + iscsi_scsi_done ( iscsi, rc ); + return rc; + } + + iscsi->rx_offset += frag_len; + data += frag_len; + len -= frag_len; + + /* If all the data for this state has not yet been + * received, stay in this state for now. + */ + if ( iscsi->rx_offset != iscsi->rx_len ) + return 0; + + iscsi->rx_state = next_state; + iscsi->rx_offset = 0; + } + + return 0; +} + +/** + * Handle stream connection closure + * + * @v socket Transport layer interface + * @v rc Reason for close + * + */ +static void iscsi_socket_close ( struct xfer_interface *socket, int rc ) { + struct iscsi_session *iscsi = + container_of ( socket, struct iscsi_session, socket ); + + /* Even a graceful close counts as an error for iSCSI */ + if ( ! rc ) + rc = -ECONNRESET; + + /* Close session cleanly */ + iscsi_close_connection ( iscsi, rc ); + + /* Retry connection if within the retry limit, otherwise fail */ + if ( ++iscsi->retry_count <= ISCSI_MAX_RETRIES ) { + DBGC ( iscsi, "iSCSI %p retrying connection (retry #%d)\n", + iscsi, iscsi->retry_count ); + if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 ) { + DBGC ( iscsi, "iSCSI %p could not reconnect: %s\n", + iscsi, strerror ( rc ) ); + iscsi_scsi_done ( iscsi, rc ); + } + } else { + DBGC ( iscsi, "iSCSI %p retry count exceeded\n", iscsi ); + iscsi->instant_rc = rc; + iscsi_scsi_done ( iscsi, rc ); + } +} + +/** + * Handle redirection event + * + * @v socket Transport layer interface + * @v type Location type + * @v args Remaining arguments depend upon location type + * @ret rc Return status code + */ +static int iscsi_vredirect ( struct xfer_interface *socket, int type, + va_list args ) { + struct iscsi_session *iscsi = + container_of ( socket, struct iscsi_session, socket ); + va_list tmp; + struct sockaddr *peer; + + /* Intercept redirects to a LOCATION_SOCKET and record the IP + * address for the iBFT. This is a bit of a hack, but avoids + * inventing an ioctl()-style call to retrieve the socket + * address from a data-xfer interface. + */ + if ( type == LOCATION_SOCKET ) { + va_copy ( tmp, args ); + ( void ) va_arg ( tmp, int ); /* Discard "semantics" */ + peer = va_arg ( tmp, struct sockaddr * ); + memcpy ( &iscsi->target_sockaddr, peer, + sizeof ( iscsi->target_sockaddr ) ); + va_end ( tmp ); + } + + return xfer_vopen ( socket, type, args ); +} + + +/** iSCSI socket operations */ +static struct xfer_interface_operations iscsi_socket_operations = { + .close = iscsi_socket_close, + .vredirect = iscsi_vredirect, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = xfer_deliver_as_raw, + .deliver_raw = iscsi_socket_deliver_raw, +}; + + +/**************************************************************************** + * + * iSCSI command issuing + * + */ + +/** + * Issue SCSI command + * + * @v scsi SCSI device + * @v command SCSI command + * @ret rc Return status code + */ +static int iscsi_command ( struct scsi_device *scsi, + struct scsi_command *command ) { + struct iscsi_session *iscsi = + container_of ( scsi->backend, struct iscsi_session, refcnt ); + int rc; + + /* Record SCSI command */ + iscsi->command = command; + + /* Abort immediately if we have a recorded permanent failure */ + if ( iscsi->instant_rc ) { + rc = iscsi->instant_rc; + goto done; + } + + /* Issue command or open connection as appropriate */ + if ( iscsi->status ) { + iscsi_start_command ( iscsi ); + } else { + if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 ) + goto done; + } + + /* Wait for command to complete */ + iscsi->rc = -EINPROGRESS; + while ( iscsi->rc == -EINPROGRESS ) + step(); + rc = iscsi->rc; + + done: + iscsi->command = NULL; + return rc; +} + +static int iscsi_detached_command ( struct scsi_device *scsi __unused, + struct scsi_command *command __unused ) { + return -ENODEV; +} + +/** + * Shut down iSCSI interface + * + * @v scsi SCSI device + */ +void iscsi_detach ( struct scsi_device *scsi ) { + struct iscsi_session *iscsi = + container_of ( scsi->backend, struct iscsi_session, refcnt ); + + xfer_nullify ( &iscsi->socket ); + iscsi_close_connection ( iscsi, 0 ); + process_del ( &iscsi->process ); + scsi->command = iscsi_detached_command; + ref_put ( scsi->backend ); + scsi->backend = NULL; +} + +/**************************************************************************** + * + * Instantiator + * + */ + +/** iSCSI root path components (as per RFC4173) */ +enum iscsi_root_path_component { + RP_LITERAL = 0, + RP_SERVERNAME, + RP_PROTOCOL, + RP_PORT, + RP_LUN, + RP_TARGETNAME, + NUM_RP_COMPONENTS +}; + +/** + * Parse iSCSI LUN + * + * @v iscsi iSCSI session + * @v lun_string LUN string representation (as per RFC4173) + * @ret rc Return status code + */ +static int iscsi_parse_lun ( struct iscsi_session *iscsi, + const char *lun_string ) { + union { + uint64_t u64; + uint16_t u16[4]; + } lun; + char *p; + int i; + + memset ( &lun, 0, sizeof ( lun ) ); + if ( lun_string ) { + p = ( char * ) lun_string; + + for ( i = 0 ; i < 4 ; i++ ) { + lun.u16[i] = htons ( strtoul ( p, &p, 16 ) ); + if ( *p == '\0' ) + break; + if ( *p != '-' ) + return -EINVAL; + p++; + } + if ( *p ) + return -EINVAL; + } + + iscsi->lun = lun.u64; + return 0; +} + +/** + * Parse iSCSI root path + * + * @v iscsi iSCSI session + * @v root_path iSCSI root path (as per RFC4173) + * @ret rc Return status code + */ +static int iscsi_parse_root_path ( struct iscsi_session *iscsi, + const char *root_path ) { + char rp_copy[ strlen ( root_path ) + 1 ]; + char *rp_comp[NUM_RP_COMPONENTS]; + char *rp = rp_copy; + int i = 0; + int rc; + + /* Split root path into component parts */ + strcpy ( rp_copy, root_path ); + while ( 1 ) { + rp_comp[i++] = rp; + if ( i == NUM_RP_COMPONENTS ) + break; + for ( ; *rp != ':' ; rp++ ) { + if ( ! *rp ) { + DBGC ( iscsi, "iSCSI %p root path \"%s\" " + "too short\n", iscsi, root_path ); + return -EINVAL; + } + } + *(rp++) = '\0'; + } + + /* Use root path components to configure iSCSI session */ + iscsi->target_address = strdup ( rp_comp[RP_SERVERNAME] ); + if ( ! iscsi->target_address ) + return -ENOMEM; + iscsi->target_port = strtoul ( rp_comp[RP_PORT], NULL, 10 ); + if ( ! iscsi->target_port ) + iscsi->target_port = ISCSI_PORT; + if ( ( rc = iscsi_parse_lun ( iscsi, rp_comp[RP_LUN] ) ) != 0 ) { + DBGC ( iscsi, "iSCSI %p invalid LUN \"%s\"\n", + iscsi, rp_comp[RP_LUN] ); + return rc; + } + iscsi->target_iqn = strdup ( rp_comp[RP_TARGETNAME] ); + if ( ! iscsi->target_iqn ) + return -ENOMEM; + + return 0; +} + +/** + * Set iSCSI authentication details + * + * @v iscsi iSCSI session + * @v initiator_username Initiator username, if any + * @v initiator_password Initiator password, if any + * @v target_username Target username, if any + * @v target_password Target password, if any + * @ret rc Return status code + */ +static int iscsi_set_auth ( struct iscsi_session *iscsi, + const char *initiator_username, + const char *initiator_password, + const char *target_username, + const char *target_password ) { + + /* Check for initiator or target credentials */ + if ( initiator_username || initiator_password || + target_username || target_password ) { + + /* We must have at least an initiator username+password */ + if ( ! ( initiator_username && initiator_password ) ) + goto invalid_auth; + + /* Store initiator credentials */ + iscsi->initiator_username = strdup ( initiator_username ); + if ( ! iscsi->initiator_username ) + return -ENOMEM; + iscsi->initiator_password = strdup ( initiator_password ); + if ( ! iscsi->initiator_password ) + return -ENOMEM; + + /* Check for target credentials */ + if ( target_username || target_password ) { + + /* We must have target username+password */ + if ( ! ( target_username && target_password ) ) + goto invalid_auth; + + /* Store target credentials */ + iscsi->target_username = strdup ( target_username ); + if ( ! iscsi->target_username ) + return -ENOMEM; + iscsi->target_password = strdup ( target_password ); + if ( ! iscsi->target_password ) + return -ENOMEM; + } + } + + return 0; + + invalid_auth: + DBGC ( iscsi, "iSCSI %p invalid credentials: initiator " + "%sname,%spw, target %sname,%spw\n", iscsi, + ( initiator_username ? "" : "no " ), + ( initiator_password ? "" : "no " ), + ( target_username ? "" : "no " ), + ( target_password ? "" : "no " ) ); + return -EINVAL; +} + +/** + * Attach iSCSI interface + * + * @v scsi SCSI device + * @v root_path iSCSI root path (as per RFC4173) + * @ret rc Return status code + */ +int iscsi_attach ( struct scsi_device *scsi, const char *root_path ) { + struct iscsi_session *iscsi; + int rc; + + /* Allocate and initialise structure */ + iscsi = zalloc ( sizeof ( *iscsi ) ); + if ( ! iscsi ) + return -ENOMEM; + iscsi->refcnt.free = iscsi_free; + xfer_init ( &iscsi->socket, &iscsi_socket_operations, &iscsi->refcnt ); + process_init ( &iscsi->process, iscsi_tx_step, &iscsi->refcnt ); + + /* Parse root path */ + if ( ( rc = iscsi_parse_root_path ( iscsi, root_path ) ) != 0 ) + goto err; + /* Set fields not specified by root path */ + if ( ( rc = iscsi_set_auth ( iscsi, + iscsi_initiator_username, + iscsi_initiator_password, + iscsi_target_username, + iscsi_target_password ) ) != 0 ) + goto err; + + /* Sanity checks */ + if ( ! iscsi->target_address ) { + DBGC ( iscsi, "iSCSI %p does not yet support discovery\n", + iscsi ); + rc = -ENOTSUP; + goto err; + } + if ( ! iscsi->target_iqn ) { + DBGC ( iscsi, "iSCSI %p no target address supplied in %s\n", + iscsi, root_path ); + rc = -EINVAL; + goto err; + } + + /* Attach parent interface, mortalise self, and return */ + scsi->backend = ref_get ( &iscsi->refcnt ); + scsi->command = iscsi_command; + scsi->lun = iscsi->lun; + ref_put ( &iscsi->refcnt ); + return 0; + + err: + ref_put ( &iscsi->refcnt ); + return rc; +} + +/**************************************************************************** + * + * Settings + * + */ + +/** iSCSI initiator IQN setting */ +struct setting initiator_iqn_setting __setting = { + .name = "initiator-iqn", + .description = "iSCSI initiator name", + .tag = DHCP_ISCSI_INITIATOR_IQN, + .type = &setting_type_string, +}; + +/** iSCSI reverse username setting */ +struct setting reverse_username_setting __setting = { + .name = "reverse-username", + .description = "Reverse user name", + .tag = DHCP_EB_REVERSE_USERNAME, + .type = &setting_type_string, +}; + +/** iSCSI reverse password setting */ +struct setting reverse_password_setting __setting = { + .name = "reverse-password", + .description = "Reverse password", + .tag = DHCP_EB_REVERSE_PASSWORD, + .type = &setting_type_string, +}; + +/** An iSCSI string setting */ +struct iscsi_string_setting { + /** Setting */ + struct setting *setting; + /** String to update */ + char **string; + /** String prefix */ + const char *prefix; +}; + +/** iSCSI string settings */ +static struct iscsi_string_setting iscsi_string_settings[] = { + { + .setting = &initiator_iqn_setting, + .string = &iscsi_explicit_initiator_iqn, + .prefix = "", + }, + { + .setting = &username_setting, + .string = &iscsi_initiator_username, + .prefix = "", + }, + { + .setting = &password_setting, + .string = &iscsi_initiator_password, + .prefix = "", + }, + { + .setting = &reverse_username_setting, + .string = &iscsi_target_username, + .prefix = "", + }, + { + .setting = &reverse_password_setting, + .string = &iscsi_target_password, + .prefix = "", + }, + { + .setting = &hostname_setting, + .string = &iscsi_default_initiator_iqn, + .prefix = "iqn.2000-01.org.etherboot:", + }, +}; + +/** + * Apply iSCSI setting + * + * @v setting iSCSI string setting + * @ret rc Return status code + */ +static int apply_iscsi_string_setting ( struct iscsi_string_setting *setting ){ + size_t prefix_len; + int setting_len; + size_t len; + int check_len; + char *p; + + /* Free old string */ + free ( *setting->string ); + *setting->string = NULL; + + /* Allocate new string */ + prefix_len = strlen ( setting->prefix ); + setting_len = fetch_setting_len ( NULL, setting->setting ); + if ( setting_len < 0 ) { + /* Missing settings are not errors; leave strings as NULL */ + return 0; + } + len = ( prefix_len + setting_len + 1 ); + p = *setting->string = malloc ( len ); + if ( ! p ) + return -ENOMEM; + + /* Fill new string */ + strcpy ( p, setting->prefix ); + check_len = fetch_string_setting ( NULL, setting->setting, + ( p + prefix_len ), + ( len - prefix_len ) ); + assert ( check_len == setting_len ); + + return 0; +} + +/** + * Apply iSCSI settings + * + * @ret rc Return status code + */ +static int apply_iscsi_settings ( void ) { + struct iscsi_string_setting *setting; + unsigned int i; + int rc; + + for ( i = 0 ; i < ( sizeof ( iscsi_string_settings ) / + sizeof ( iscsi_string_settings[0] ) ) ; i++ ) { + setting = &iscsi_string_settings[i]; + if ( ( rc = apply_iscsi_string_setting ( setting ) ) != 0 ) { + DBG ( "iSCSI could not apply setting %s\n", + setting->setting->name ); + return rc; + } + } + + return 0; +} + +/** iSCSI settings applicator */ +struct settings_applicator iscsi_settings_applicator __settings_applicator = { + .apply = apply_iscsi_settings, +}; + +/**************************************************************************** + * + * Initiator name + * + */ + +/** + * Get iSCSI initiator IQN + * + * @v iscsi iSCSI session + * @ret rc Return status code + */ +const char * iscsi_initiator_iqn ( void ) { + + if ( iscsi_explicit_initiator_iqn ) + return iscsi_explicit_initiator_iqn; + if ( iscsi_default_initiator_iqn ) + return iscsi_default_initiator_iqn; + return "iqn.2000-09.org.etherboot:UNKNOWN"; +} diff --git a/gpxe/src/net/tcpip.c b/gpxe/src/net/tcpip.c new file mode 100644 index 0000000..d4542b0 --- /dev/null +++ b/gpxe/src/net/tcpip.c @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Transport-network layer interface + * + * This file contains functions and utilities for the + * TCP/IP transport-network layer interface + */ + +/** Registered network-layer protocols that support TCP/IP */ +static struct tcpip_net_protocol tcpip_net_protocols[0] + __table_start ( struct tcpip_net_protocol, tcpip_net_protocols ); +static struct tcpip_net_protocol tcpip_net_protocols_end[0] + __table_end ( struct tcpip_net_protocol, tcpip_net_protocols ); + +/** Registered transport-layer protocols that support TCP/IP */ +static struct tcpip_protocol tcpip_protocols[0] + __table_start ( struct tcpip_protocol, tcpip_protocols ); +static struct tcpip_protocol tcpip_protocols_end[0] + __table_end ( struct tcpip_protocol, tcpip_protocols ); + +/** Process a received TCP/IP packet + * + * @v iobuf I/O buffer + * @v tcpip_proto Transport-layer protocol number + * @v st_src Partially-filled source address + * @v st_dest Partially-filled destination address + * @v pshdr_csum Pseudo-header checksum + * @ret rc Return status code + * + * This function expects a transport-layer segment from the network + * layer. The network layer should fill in as much as it can of the + * source and destination addresses (i.e. it should fill in the + * address family and the network-layer addresses, but leave the ports + * and the rest of the structures as zero). + */ +int tcpip_rx ( struct io_buffer *iobuf, uint8_t tcpip_proto, + struct sockaddr_tcpip *st_src, + struct sockaddr_tcpip *st_dest, + uint16_t pshdr_csum ) { + struct tcpip_protocol *tcpip; + + /* Hand off packet to the appropriate transport-layer protocol */ + for ( tcpip = tcpip_protocols; tcpip < tcpip_protocols_end; tcpip++ ) { + if ( tcpip->tcpip_proto == tcpip_proto ) { + DBG ( "TCP/IP received %s packet\n", tcpip->name ); + return tcpip->rx ( iobuf, st_src, st_dest, pshdr_csum ); + } + } + + DBG ( "Unrecognised TCP/IP protocol %d\n", tcpip_proto ); + free_iob ( iobuf ); + return -EPROTONOSUPPORT; +} + +/** Transmit a TCP/IP packet + * + * @v iobuf I/O buffer + * @v tcpip_protocol Transport-layer protocol + * @v st_src Source address, or NULL to use route default + * @v st_dest Destination address + * @v netdev Network device to use if no route found, or NULL + * @v trans_csum Transport-layer checksum to complete, or NULL + * @ret rc Return status code + */ +int tcpip_tx ( struct io_buffer *iobuf, struct tcpip_protocol *tcpip_protocol, + struct sockaddr_tcpip *st_src, struct sockaddr_tcpip *st_dest, + struct net_device *netdev, uint16_t *trans_csum ) { + struct tcpip_net_protocol *tcpip_net; + + /* Hand off packet to the appropriate network-layer protocol */ + for ( tcpip_net = tcpip_net_protocols ; + tcpip_net < tcpip_net_protocols_end ; tcpip_net++ ) { + if ( tcpip_net->sa_family == st_dest->st_family ) { + DBG ( "TCP/IP sending %s packet\n", tcpip_net->name ); + return tcpip_net->tx ( iobuf, tcpip_protocol, st_src, + st_dest, netdev, trans_csum ); + } + } + + DBG ( "Unrecognised TCP/IP address family %d\n", st_dest->st_family ); + free_iob ( iobuf ); + return -EAFNOSUPPORT; +} + +/** + * Calculate continued TCP/IP checkum + * + * @v partial Checksum of already-summed data, in network byte order + * @v data Data buffer + * @v len Length of data buffer + * @ret cksum Updated checksum, in network byte order + * + * Calculates a TCP/IP-style 16-bit checksum over the data block. The + * checksum is returned in network byte order. + * + * This function may be used to add new data to an existing checksum. + * The function assumes that both the old data and the new data start + * on even byte offsets; if this is not the case then you will need to + * byte-swap either the input partial checksum, the output checksum, + * or both. Deciding which to swap is left as an exercise for the + * interested reader. + */ +uint16_t tcpip_continue_chksum ( uint16_t partial, const void *data, + size_t len ) { + unsigned int cksum = ( ( ~partial ) & 0xffff ); + unsigned int value; + unsigned int i; + + for ( i = 0 ; i < len ; i++ ) { + value = * ( ( uint8_t * ) data + i ); + if ( i & 1 ) { + /* Odd bytes: swap on little-endian systems */ + value = be16_to_cpu ( value ); + } else { + /* Even bytes: swap on big-endian systems */ + value = le16_to_cpu ( value ); + } + cksum += value; + if ( cksum > 0xffff ) + cksum -= 0xffff; + } + + return ( ~cksum ); +} + +/** + * Calculate TCP/IP checkum + * + * @v data Data buffer + * @v len Length of data buffer + * @ret cksum Checksum, in network byte order + * + * Calculates a TCP/IP-style 16-bit checksum over the data block. The + * checksum is returned in network byte order. + */ +uint16_t tcpip_chksum ( const void *data, size_t len ) { + return tcpip_continue_chksum ( TCPIP_EMPTY_CSUM, data, len ); +} diff --git a/gpxe/src/net/tls.c b/gpxe/src/net/tls.c new file mode 100644 index 0000000..73f9ad0 --- /dev/null +++ b/gpxe/src/net/tls.c @@ -0,0 +1,1757 @@ +/* + * Copyright (C) 2007 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. + */ + +/** + * @file + * + * Transport Layer Security Protocol + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int tls_send_plaintext ( struct tls_session *tls, unsigned int type, + const void *data, size_t len ); +static void tls_clear_cipher ( struct tls_session *tls, + struct tls_cipherspec *cipherspec ); + +/****************************************************************************** + * + * Utility functions + * + ****************************************************************************** + */ + +/** + * Extract 24-bit field value + * + * @v field24 24-bit field + * @ret value Field value + * + * TLS uses 24-bit integers in several places, which are awkward to + * parse in C. + */ +static unsigned long tls_uint24 ( uint8_t field24[3] ) { + return ( ( field24[0] << 16 ) + ( field24[1] << 8 ) + field24[2] ); +} + +/****************************************************************************** + * + * Cleanup functions + * + ****************************************************************************** + */ + +/** + * Free TLS session + * + * @v refcnt Reference counter + */ +static void free_tls ( struct refcnt *refcnt ) { + struct tls_session *tls = + container_of ( refcnt, struct tls_session, refcnt ); + + /* Free dynamically-allocated resources */ + tls_clear_cipher ( tls, &tls->tx_cipherspec ); + tls_clear_cipher ( tls, &tls->tx_cipherspec_pending ); + tls_clear_cipher ( tls, &tls->rx_cipherspec ); + tls_clear_cipher ( tls, &tls->rx_cipherspec_pending ); + x509_free_rsa_public_key ( &tls->rsa ); + free ( tls->rx_data ); + + /* Free TLS structure itself */ + free ( tls ); +} + +/** + * Finish with TLS session + * + * @v tls TLS session + * @v rc Status code + */ +static void tls_close ( struct tls_session *tls, int rc ) { + + /* Remove process */ + process_del ( &tls->process ); + + /* Close ciphertext and plaintext streams */ + xfer_nullify ( &tls->cipherstream.xfer ); + xfer_close ( &tls->cipherstream.xfer, rc ); + xfer_nullify ( &tls->plainstream.xfer ); + xfer_close ( &tls->plainstream.xfer, rc ); +} + +/****************************************************************************** + * + * Random number generation + * + ****************************************************************************** + */ + +/** + * Generate random data + * + * @v data Buffer to fill + * @v len Length of buffer + */ +static void tls_generate_random ( void *data, size_t len ) { + /* FIXME: Some real random data source would be nice... */ + memset ( data, 0x01, len ); +} + +/** + * Update HMAC with a list of ( data, len ) pairs + * + * @v digest Hash function to use + * @v digest_ctx Digest context + * @v args ( data, len ) pairs of data, terminated by NULL + */ +static void tls_hmac_update_va ( struct digest_algorithm *digest, + void *digest_ctx, va_list args ) { + void *data; + size_t len; + + while ( ( data = va_arg ( args, void * ) ) ) { + len = va_arg ( args, size_t ); + hmac_update ( digest, digest_ctx, data, len ); + } +} + +/** + * Generate secure pseudo-random data using a single hash function + * + * @v tls TLS session + * @v digest Hash function to use + * @v secret Secret + * @v secret_len Length of secret + * @v out Output buffer + * @v out_len Length of output buffer + * @v seeds ( data, len ) pairs of seed data, terminated by NULL + */ +static void tls_p_hash_va ( struct tls_session *tls, + struct digest_algorithm *digest, + void *secret, size_t secret_len, + void *out, size_t out_len, + va_list seeds ) { + uint8_t secret_copy[secret_len]; + uint8_t digest_ctx[digest->ctxsize]; + uint8_t digest_ctx_partial[digest->ctxsize]; + uint8_t a[digest->digestsize]; + uint8_t out_tmp[digest->digestsize]; + size_t frag_len = digest->digestsize; + va_list tmp; + + /* Copy the secret, in case HMAC modifies it */ + memcpy ( secret_copy, secret, secret_len ); + secret = secret_copy; + DBGC2 ( tls, "TLS %p %s secret:\n", tls, digest->name ); + DBGC2_HD ( tls, secret, secret_len ); + + /* Calculate A(1) */ + hmac_init ( digest, digest_ctx, secret, &secret_len ); + va_copy ( tmp, seeds ); + tls_hmac_update_va ( digest, digest_ctx, tmp ); + va_end ( tmp ); + hmac_final ( digest, digest_ctx, secret, &secret_len, a ); + DBGC2 ( tls, "TLS %p %s A(1):\n", tls, digest->name ); + DBGC2_HD ( tls, &a, sizeof ( a ) ); + + /* Generate as much data as required */ + while ( out_len ) { + /* Calculate output portion */ + hmac_init ( digest, digest_ctx, secret, &secret_len ); + hmac_update ( digest, digest_ctx, a, sizeof ( a ) ); + memcpy ( digest_ctx_partial, digest_ctx, digest->ctxsize ); + va_copy ( tmp, seeds ); + tls_hmac_update_va ( digest, digest_ctx, tmp ); + va_end ( tmp ); + hmac_final ( digest, digest_ctx, + secret, &secret_len, out_tmp ); + + /* Copy output */ + if ( frag_len > out_len ) + frag_len = out_len; + memcpy ( out, out_tmp, frag_len ); + DBGC2 ( tls, "TLS %p %s output:\n", tls, digest->name ); + DBGC2_HD ( tls, out, frag_len ); + + /* Calculate A(i) */ + hmac_final ( digest, digest_ctx_partial, + secret, &secret_len, a ); + DBGC2 ( tls, "TLS %p %s A(n):\n", tls, digest->name ); + DBGC2_HD ( tls, &a, sizeof ( a ) ); + + out += frag_len; + out_len -= frag_len; + } +} + +/** + * Generate secure pseudo-random data + * + * @v tls TLS session + * @v secret Secret + * @v secret_len Length of secret + * @v out Output buffer + * @v out_len Length of output buffer + * @v ... ( data, len ) pairs of seed data, terminated by NULL + */ +static void tls_prf ( struct tls_session *tls, void *secret, size_t secret_len, + void *out, size_t out_len, ... ) { + va_list seeds; + va_list tmp; + size_t subsecret_len; + void *md5_secret; + void *sha1_secret; + uint8_t out_md5[out_len]; + uint8_t out_sha1[out_len]; + unsigned int i; + + va_start ( seeds, out_len ); + + /* Split secret into two, with an overlap of up to one byte */ + subsecret_len = ( ( secret_len + 1 ) / 2 ); + md5_secret = secret; + sha1_secret = ( secret + secret_len - subsecret_len ); + + /* Calculate MD5 portion */ + va_copy ( tmp, seeds ); + tls_p_hash_va ( tls, &md5_algorithm, md5_secret, subsecret_len, + out_md5, out_len, seeds ); + va_end ( tmp ); + + /* Calculate SHA1 portion */ + va_copy ( tmp, seeds ); + tls_p_hash_va ( tls, &sha1_algorithm, sha1_secret, subsecret_len, + out_sha1, out_len, seeds ); + va_end ( tmp ); + + /* XOR the two portions together into the final output buffer */ + for ( i = 0 ; i < out_len ; i++ ) { + *( ( uint8_t * ) out + i ) = ( out_md5[i] ^ out_sha1[i] ); + } + + va_end ( seeds ); +} + +/** + * Generate secure pseudo-random data + * + * @v secret Secret + * @v secret_len Length of secret + * @v out Output buffer + * @v out_len Length of output buffer + * @v label String literal label + * @v ... ( data, len ) pairs of seed data + */ +#define tls_prf_label( tls, secret, secret_len, out, out_len, label, ... ) \ + tls_prf ( (tls), (secret), (secret_len), (out), (out_len), \ + label, ( sizeof ( label ) - 1 ), __VA_ARGS__, NULL ) + +/****************************************************************************** + * + * Secret management + * + ****************************************************************************** + */ + +/** + * Generate master secret + * + * @v tls TLS session + * + * The pre-master secret and the client and server random values must + * already be known. + */ +static void tls_generate_master_secret ( struct tls_session *tls ) { + DBGC ( tls, "TLS %p pre-master-secret:\n", tls ); + DBGC_HD ( tls, &tls->pre_master_secret, + sizeof ( tls->pre_master_secret ) ); + DBGC ( tls, "TLS %p client random bytes:\n", tls ); + DBGC_HD ( tls, &tls->client_random, sizeof ( tls->client_random ) ); + DBGC ( tls, "TLS %p server random bytes:\n", tls ); + DBGC_HD ( tls, &tls->server_random, sizeof ( tls->server_random ) ); + + tls_prf_label ( tls, &tls->pre_master_secret, + sizeof ( tls->pre_master_secret ), + &tls->master_secret, sizeof ( tls->master_secret ), + "master secret", + &tls->client_random, sizeof ( tls->client_random ), + &tls->server_random, sizeof ( tls->server_random ) ); + + DBGC ( tls, "TLS %p generated master secret:\n", tls ); + DBGC_HD ( tls, &tls->master_secret, sizeof ( tls->master_secret ) ); +} + +/** + * Generate key material + * + * @v tls TLS session + * + * The master secret must already be known. + */ +static int tls_generate_keys ( struct tls_session *tls ) { + struct tls_cipherspec *tx_cipherspec = &tls->tx_cipherspec_pending; + struct tls_cipherspec *rx_cipherspec = &tls->rx_cipherspec_pending; + size_t hash_size = tx_cipherspec->digest->digestsize; + size_t key_size = tx_cipherspec->key_len; + size_t iv_size = tx_cipherspec->cipher->blocksize; + size_t total = ( 2 * ( hash_size + key_size + iv_size ) ); + uint8_t key_block[total]; + uint8_t *key; + int rc; + + /* Generate key block */ + tls_prf_label ( tls, &tls->master_secret, sizeof ( tls->master_secret ), + key_block, sizeof ( key_block ), "key expansion", + &tls->server_random, sizeof ( tls->server_random ), + &tls->client_random, sizeof ( tls->client_random ) ); + + /* Split key block into portions */ + key = key_block; + + /* TX MAC secret */ + memcpy ( tx_cipherspec->mac_secret, key, hash_size ); + DBGC ( tls, "TLS %p TX MAC secret:\n", tls ); + DBGC_HD ( tls, key, hash_size ); + key += hash_size; + + /* RX MAC secret */ + memcpy ( rx_cipherspec->mac_secret, key, hash_size ); + DBGC ( tls, "TLS %p RX MAC secret:\n", tls ); + DBGC_HD ( tls, key, hash_size ); + key += hash_size; + + /* TX key */ + if ( ( rc = cipher_setkey ( tx_cipherspec->cipher, + tx_cipherspec->cipher_ctx, + key, key_size ) ) != 0 ) { + DBGC ( tls, "TLS %p could not set TX key: %s\n", + tls, strerror ( rc ) ); + return rc; + } + DBGC ( tls, "TLS %p TX key:\n", tls ); + DBGC_HD ( tls, key, key_size ); + key += key_size; + + /* RX key */ + if ( ( rc = cipher_setkey ( rx_cipherspec->cipher, + rx_cipherspec->cipher_ctx, + key, key_size ) ) != 0 ) { + DBGC ( tls, "TLS %p could not set TX key: %s\n", + tls, strerror ( rc ) ); + return rc; + } + DBGC ( tls, "TLS %p RX key:\n", tls ); + DBGC_HD ( tls, key, key_size ); + key += key_size; + + /* TX initialisation vector */ + cipher_setiv ( tx_cipherspec->cipher, tx_cipherspec->cipher_ctx, key ); + DBGC ( tls, "TLS %p TX IV:\n", tls ); + DBGC_HD ( tls, key, iv_size ); + key += iv_size; + + /* RX initialisation vector */ + cipher_setiv ( rx_cipherspec->cipher, rx_cipherspec->cipher_ctx, key ); + DBGC ( tls, "TLS %p RX IV:\n", tls ); + DBGC_HD ( tls, key, iv_size ); + key += iv_size; + + assert ( ( key_block + total ) == key ); + + return 0; +} + +/****************************************************************************** + * + * Cipher suite management + * + ****************************************************************************** + */ + +/** + * Clear cipher suite + * + * @v cipherspec TLS cipher specification + */ +static void tls_clear_cipher ( struct tls_session *tls __unused, + struct tls_cipherspec *cipherspec ) { + free ( cipherspec->dynamic ); + memset ( cipherspec, 0, sizeof ( cipherspec ) ); + cipherspec->pubkey = &pubkey_null; + cipherspec->cipher = &cipher_null; + cipherspec->digest = &digest_null; +} + +/** + * Set cipher suite + * + * @v tls TLS session + * @v cipherspec TLS cipher specification + * @v pubkey Public-key encryption elgorithm + * @v cipher Bulk encryption cipher algorithm + * @v digest MAC digest algorithm + * @v key_len Key length + * @ret rc Return status code + */ +static int tls_set_cipher ( struct tls_session *tls, + struct tls_cipherspec *cipherspec, + struct pubkey_algorithm *pubkey, + struct cipher_algorithm *cipher, + struct digest_algorithm *digest, + size_t key_len ) { + size_t total; + void *dynamic; + + /* Clear out old cipher contents, if any */ + tls_clear_cipher ( tls, cipherspec ); + + /* Allocate dynamic storage */ + total = ( pubkey->ctxsize + 2 * cipher->ctxsize + digest->digestsize ); + dynamic = malloc ( total ); + if ( ! dynamic ) { + DBGC ( tls, "TLS %p could not allocate %zd bytes for crypto " + "context\n", tls, total ); + return -ENOMEM; + } + memset ( dynamic, 0, total ); + + /* Assign storage */ + cipherspec->dynamic = dynamic; + cipherspec->pubkey_ctx = dynamic; dynamic += pubkey->ctxsize; + cipherspec->cipher_ctx = dynamic; dynamic += cipher->ctxsize; + cipherspec->cipher_next_ctx = dynamic; dynamic += cipher->ctxsize; + cipherspec->mac_secret = dynamic; dynamic += digest->digestsize; + assert ( ( cipherspec->dynamic + total ) == dynamic ); + + /* Store parameters */ + cipherspec->pubkey = pubkey; + cipherspec->cipher = cipher; + cipherspec->digest = digest; + cipherspec->key_len = key_len; + + return 0; +} + +/** + * Select next cipher suite + * + * @v tls TLS session + * @v cipher_suite Cipher suite specification + * @ret rc Return status code + */ +static int tls_select_cipher ( struct tls_session *tls, + unsigned int cipher_suite ) { + struct pubkey_algorithm *pubkey = &pubkey_null; + struct cipher_algorithm *cipher = &cipher_null; + struct digest_algorithm *digest = &digest_null; + unsigned int key_len = 0; + int rc; + + switch ( cipher_suite ) { + case htons ( TLS_RSA_WITH_AES_128_CBC_SHA ): + key_len = ( 128 / 8 ); + cipher = &aes_cbc_algorithm; + digest = &sha1_algorithm; + break; + case htons ( TLS_RSA_WITH_AES_256_CBC_SHA ): + key_len = ( 256 / 8 ); + cipher = &aes_cbc_algorithm; + digest = &sha1_algorithm; + break; + default: + DBGC ( tls, "TLS %p does not support cipher %04x\n", + tls, ntohs ( cipher_suite ) ); + return -ENOTSUP; + } + + /* Set ciphers */ + if ( ( rc = tls_set_cipher ( tls, &tls->tx_cipherspec_pending, pubkey, + cipher, digest, key_len ) ) != 0 ) + return rc; + if ( ( rc = tls_set_cipher ( tls, &tls->rx_cipherspec_pending, pubkey, + cipher, digest, key_len ) ) != 0 ) + return rc; + + DBGC ( tls, "TLS %p selected %s-%s-%d-%s\n", tls, + pubkey->name, cipher->name, ( key_len * 8 ), digest->name ); + + return 0; +} + +/** + * Activate next cipher suite + * + * @v tls TLS session + * @v pending Pending cipher specification + * @v active Active cipher specification to replace + * @ret rc Return status code + */ +static int tls_change_cipher ( struct tls_session *tls, + struct tls_cipherspec *pending, + struct tls_cipherspec *active ) { + + /* Sanity check */ + if ( /* FIXME (when pubkey is not hard-coded to RSA): + * ( pending->pubkey == &pubkey_null ) || */ + ( pending->cipher == &cipher_null ) || + ( pending->digest == &digest_null ) ) { + DBGC ( tls, "TLS %p refusing to use null cipher\n", tls ); + return -ENOTSUP; + } + + tls_clear_cipher ( tls, active ); + memswap ( active, pending, sizeof ( *active ) ); + return 0; +} + +/****************************************************************************** + * + * Handshake verification + * + ****************************************************************************** + */ + +/** + * Add handshake record to verification hash + * + * @v tls TLS session + * @v data Handshake record + * @v len Length of handshake record + */ +static void tls_add_handshake ( struct tls_session *tls, + const void *data, size_t len ) { + + digest_update ( &md5_algorithm, tls->handshake_md5_ctx, data, len ); + digest_update ( &sha1_algorithm, tls->handshake_sha1_ctx, data, len ); +} + +/** + * Calculate handshake verification hash + * + * @v tls TLS session + * @v out Output buffer + * + * Calculates the MD5+SHA1 digest over all handshake messages seen so + * far. + */ +static void tls_verify_handshake ( struct tls_session *tls, void *out ) { + struct digest_algorithm *md5 = &md5_algorithm; + struct digest_algorithm *sha1 = &sha1_algorithm; + uint8_t md5_ctx[md5->ctxsize]; + uint8_t sha1_ctx[sha1->ctxsize]; + void *md5_digest = out; + void *sha1_digest = ( out + md5->digestsize ); + + memcpy ( md5_ctx, tls->handshake_md5_ctx, sizeof ( md5_ctx ) ); + memcpy ( sha1_ctx, tls->handshake_sha1_ctx, sizeof ( sha1_ctx ) ); + digest_final ( md5, md5_ctx, md5_digest ); + digest_final ( sha1, sha1_ctx, sha1_digest ); +} + +/****************************************************************************** + * + * Record handling + * + ****************************************************************************** + */ + +/** + * Transmit Handshake record + * + * @v tls TLS session + * @v data Plaintext record + * @v len Length of plaintext record + * @ret rc Return status code + */ +static int tls_send_handshake ( struct tls_session *tls, + void *data, size_t len ) { + + /* Add to handshake digest */ + tls_add_handshake ( tls, data, len ); + + /* Send record */ + return tls_send_plaintext ( tls, TLS_TYPE_HANDSHAKE, data, len ); +} + +/** + * Transmit Client Hello record + * + * @v tls TLS session + * @ret rc Return status code + */ +static int tls_send_client_hello ( struct tls_session *tls ) { + struct { + uint32_t type_length; + uint16_t version; + uint8_t random[32]; + uint8_t session_id_len; + uint16_t cipher_suite_len; + uint16_t cipher_suites[2]; + uint8_t compression_methods_len; + uint8_t compression_methods[1]; + } __attribute__ (( packed )) hello; + + memset ( &hello, 0, sizeof ( hello ) ); + hello.type_length = ( cpu_to_le32 ( TLS_CLIENT_HELLO ) | + htonl ( sizeof ( hello ) - + sizeof ( hello.type_length ) ) ); + hello.version = htons ( TLS_VERSION_TLS_1_0 ); + memcpy ( &hello.random, &tls->client_random, sizeof ( hello.random ) ); + hello.cipher_suite_len = htons ( sizeof ( hello.cipher_suites ) ); + hello.cipher_suites[0] = htons ( TLS_RSA_WITH_AES_128_CBC_SHA ); + hello.cipher_suites[1] = htons ( TLS_RSA_WITH_AES_256_CBC_SHA ); + hello.compression_methods_len = sizeof ( hello.compression_methods ); + + return tls_send_handshake ( tls, &hello, sizeof ( hello ) ); +} + +/** + * Transmit Client Key Exchange record + * + * @v tls TLS session + * @ret rc Return status code + */ +static int tls_send_client_key_exchange ( struct tls_session *tls ) { + /* FIXME: Hack alert */ + RSA_CTX *rsa_ctx; + RSA_pub_key_new ( &rsa_ctx, tls->rsa.modulus, tls->rsa.modulus_len, + tls->rsa.exponent, tls->rsa.exponent_len ); + struct { + uint32_t type_length; + uint16_t encrypted_pre_master_secret_len; + uint8_t encrypted_pre_master_secret[rsa_ctx->num_octets]; + } __attribute__ (( packed )) key_xchg; + + memset ( &key_xchg, 0, sizeof ( key_xchg ) ); + key_xchg.type_length = ( cpu_to_le32 ( TLS_CLIENT_KEY_EXCHANGE ) | + htonl ( sizeof ( key_xchg ) - + sizeof ( key_xchg.type_length ) ) ); + key_xchg.encrypted_pre_master_secret_len + = htons ( sizeof ( key_xchg.encrypted_pre_master_secret ) ); + + /* FIXME: Hack alert */ + DBGC ( tls, "RSA encrypting plaintext, modulus, exponent:\n" ); + DBGC_HD ( tls, &tls->pre_master_secret, + sizeof ( tls->pre_master_secret ) ); + DBGC_HD ( tls, tls->rsa.modulus, tls->rsa.modulus_len ); + DBGC_HD ( tls, tls->rsa.exponent, tls->rsa.exponent_len ); + RSA_encrypt ( rsa_ctx, ( const uint8_t * ) &tls->pre_master_secret, + sizeof ( tls->pre_master_secret ), + key_xchg.encrypted_pre_master_secret, 0 ); + DBGC ( tls, "RSA encrypt done. Ciphertext:\n" ); + DBGC_HD ( tls, &key_xchg.encrypted_pre_master_secret, + sizeof ( key_xchg.encrypted_pre_master_secret ) ); + RSA_free ( rsa_ctx ); + + + return tls_send_handshake ( tls, &key_xchg, sizeof ( key_xchg ) ); +} + +/** + * Transmit Change Cipher record + * + * @v tls TLS session + * @ret rc Return status code + */ +static int tls_send_change_cipher ( struct tls_session *tls ) { + static const uint8_t change_cipher[1] = { 1 }; + return tls_send_plaintext ( tls, TLS_TYPE_CHANGE_CIPHER, + change_cipher, sizeof ( change_cipher ) ); +} + +/** + * Transmit Finished record + * + * @v tls TLS session + * @ret rc Return status code + */ +static int tls_send_finished ( struct tls_session *tls ) { + struct { + uint32_t type_length; + uint8_t verify_data[12]; + } __attribute__ (( packed )) finished; + uint8_t digest[MD5_DIGEST_SIZE + SHA1_DIGEST_SIZE]; + + memset ( &finished, 0, sizeof ( finished ) ); + finished.type_length = ( cpu_to_le32 ( TLS_FINISHED ) | + htonl ( sizeof ( finished ) - + sizeof ( finished.type_length ) ) ); + tls_verify_handshake ( tls, digest ); + tls_prf_label ( tls, &tls->master_secret, sizeof ( tls->master_secret ), + finished.verify_data, sizeof ( finished.verify_data ), + "client finished", digest, sizeof ( digest ) ); + + return tls_send_handshake ( tls, &finished, sizeof ( finished ) ); +} + +/** + * Receive new Change Cipher record + * + * @v tls TLS session + * @v data Plaintext record + * @v len Length of plaintext record + * @ret rc Return status code + */ +static int tls_new_change_cipher ( struct tls_session *tls, + void *data, size_t len ) { + int rc; + + if ( ( len != 1 ) || ( *( ( uint8_t * ) data ) != 1 ) ) { + DBGC ( tls, "TLS %p received invalid Change Cipher\n", tls ); + DBGC_HD ( tls, data, len ); + return -EINVAL; + } + + if ( ( rc = tls_change_cipher ( tls, &tls->rx_cipherspec_pending, + &tls->rx_cipherspec ) ) != 0 ) { + DBGC ( tls, "TLS %p could not activate RX cipher: %s\n", + tls, strerror ( rc ) ); + return rc; + } + tls->rx_seq = ~( ( uint64_t ) 0 ); + + return 0; +} + +/** + * Receive new Alert record + * + * @v tls TLS session + * @v data Plaintext record + * @v len Length of plaintext record + * @ret rc Return status code + */ +static int tls_new_alert ( struct tls_session *tls, void *data, size_t len ) { + struct { + uint8_t level; + uint8_t description; + char next[0]; + } __attribute__ (( packed )) *alert = data; + void *end = alert->next; + + /* Sanity check */ + if ( end != ( data + len ) ) { + DBGC ( tls, "TLS %p received overlength Alert\n", tls ); + DBGC_HD ( tls, data, len ); + return -EINVAL; + } + + switch ( alert->level ) { + case TLS_ALERT_WARNING: + DBGC ( tls, "TLS %p received warning alert %d\n", + tls, alert->description ); + return 0; + case TLS_ALERT_FATAL: + DBGC ( tls, "TLS %p received fatal alert %d\n", + tls, alert->description ); + return -EPERM; + default: + DBGC ( tls, "TLS %p received unknown alert level %d" + "(alert %d)\n", tls, alert->level, alert->description ); + return -EIO; + } +} + +/** + * Receive new Server Hello handshake record + * + * @v tls TLS session + * @v data Plaintext handshake record + * @v len Length of plaintext handshake record + * @ret rc Return status code + */ +static int tls_new_server_hello ( struct tls_session *tls, + void *data, size_t len ) { + struct { + uint16_t version; + uint8_t random[32]; + uint8_t session_id_len; + char next[0]; + } __attribute__ (( packed )) *hello_a = data; + struct { + uint8_t session_id[hello_a->session_id_len]; + uint16_t cipher_suite; + uint8_t compression_method; + char next[0]; + } __attribute__ (( packed )) *hello_b = ( void * ) &hello_a->next; + void *end = hello_b->next; + int rc; + + /* Sanity check */ + if ( end != ( data + len ) ) { + DBGC ( tls, "TLS %p received overlength Server Hello\n", tls ); + DBGC_HD ( tls, data, len ); + return -EINVAL; + } + + /* Check protocol version */ + if ( ntohs ( hello_a->version ) < TLS_VERSION_TLS_1_0 ) { + DBGC ( tls, "TLS %p does not support protocol version %d.%d\n", + tls, ( ntohs ( hello_a->version ) >> 8 ), + ( ntohs ( hello_a->version ) & 0xff ) ); + return -ENOTSUP; + } + + /* Copy out server random bytes */ + memcpy ( &tls->server_random, &hello_a->random, + sizeof ( tls->server_random ) ); + + /* Select cipher suite */ + if ( ( rc = tls_select_cipher ( tls, hello_b->cipher_suite ) ) != 0 ) + return rc; + + /* Generate secrets */ + tls_generate_master_secret ( tls ); + if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Receive new Certificate handshake record + * + * @v tls TLS session + * @v data Plaintext handshake record + * @v len Length of plaintext handshake record + * @ret rc Return status code + */ +static int tls_new_certificate ( struct tls_session *tls, + void *data, size_t len ) { + struct { + uint8_t length[3]; + uint8_t certificates[0]; + } __attribute__ (( packed )) *certificate = data; + struct { + uint8_t length[3]; + uint8_t certificate[0]; + } __attribute__ (( packed )) *element = + ( ( void * ) certificate->certificates ); + size_t elements_len = tls_uint24 ( certificate->length ); + void *end = ( certificate->certificates + elements_len ); + struct asn1_cursor cursor; + int rc; + + /* Sanity check */ + if ( end != ( data + len ) ) { + DBGC ( tls, "TLS %p received overlength Server Certificate\n", + tls ); + DBGC_HD ( tls, data, len ); + return -EINVAL; + } + + /* Traverse certificate chain */ + do { + cursor.data = element->certificate; + cursor.len = tls_uint24 ( element->length ); + if ( ( cursor.data + cursor.len ) > end ) { + DBGC ( tls, "TLS %p received corrupt Server " + "Certificate\n", tls ); + DBGC_HD ( tls, data, len ); + return -EINVAL; + } + + // HACK + if ( ( rc = x509_rsa_public_key ( &cursor, + &tls->rsa ) ) != 0 ) { + DBGC ( tls, "TLS %p cannot determine RSA public key: " + "%s\n", tls, strerror ( rc ) ); + return rc; + } + return 0; + + element = ( cursor.data + cursor.len ); + } while ( element != end ); + + return -EINVAL; +} + +/** + * Receive new Server Hello Done handshake record + * + * @v tls TLS session + * @v data Plaintext handshake record + * @v len Length of plaintext handshake record + * @ret rc Return status code + */ +static int tls_new_server_hello_done ( struct tls_session *tls, + void *data, size_t len ) { + struct { + char next[0]; + } __attribute__ (( packed )) *hello_done = data; + void *end = hello_done->next; + + /* Sanity check */ + if ( end != ( data + len ) ) { + DBGC ( tls, "TLS %p received overlength Server Hello Done\n", + tls ); + DBGC_HD ( tls, data, len ); + return -EINVAL; + } + + /* Check that we are ready to send the Client Key Exchange */ + if ( tls->tx_state != TLS_TX_NONE ) { + DBGC ( tls, "TLS %p received Server Hello Done while in " + "TX state %d\n", tls, tls->tx_state ); + return -EIO; + } + + /* Start sending the Client Key Exchange */ + tls->tx_state = TLS_TX_CLIENT_KEY_EXCHANGE; + + return 0; +} + +/** + * Receive new Finished handshake record + * + * @v tls TLS session + * @v data Plaintext handshake record + * @v len Length of plaintext handshake record + * @ret rc Return status code + */ +static int tls_new_finished ( struct tls_session *tls, + void *data, size_t len ) { + + /* FIXME: Handle this properly */ + tls->tx_state = TLS_TX_DATA; + ( void ) data; + ( void ) len; + return 0; +} + +/** + * Receive new Handshake record + * + * @v tls TLS session + * @v data Plaintext record + * @v len Length of plaintext record + * @ret rc Return status code + */ +static int tls_new_handshake ( struct tls_session *tls, + void *data, size_t len ) { + struct { + uint8_t type; + uint8_t length[3]; + uint8_t payload[0]; + } __attribute__ (( packed )) *handshake = data; + void *payload = &handshake->payload; + size_t payload_len = tls_uint24 ( handshake->length ); + void *end = ( payload + payload_len ); + int rc; + + /* Sanity check */ + if ( end != ( data + len ) ) { + DBGC ( tls, "TLS %p received overlength Handshake\n", tls ); + DBGC_HD ( tls, data, len ); + return -EINVAL; + } + + switch ( handshake->type ) { + case TLS_SERVER_HELLO: + rc = tls_new_server_hello ( tls, payload, payload_len ); + break; + case TLS_CERTIFICATE: + rc = tls_new_certificate ( tls, payload, payload_len ); + break; + case TLS_SERVER_HELLO_DONE: + rc = tls_new_server_hello_done ( tls, payload, payload_len ); + break; + case TLS_FINISHED: + rc = tls_new_finished ( tls, payload, payload_len ); + break; + default: + DBGC ( tls, "TLS %p ignoring handshake type %d\n", + tls, handshake->type ); + rc = 0; + break; + } + + /* Add to handshake digest (except for Hello Requests, which + * are explicitly excluded). + */ + if ( handshake->type != TLS_HELLO_REQUEST ) + tls_add_handshake ( tls, data, len ); + + return rc; +} + +/** + * Receive new record + * + * @v tls TLS session + * @v type Record type + * @v data Plaintext record + * @v len Length of plaintext record + * @ret rc Return status code + */ +static int tls_new_record ( struct tls_session *tls, + unsigned int type, void *data, size_t len ) { + + switch ( type ) { + case TLS_TYPE_CHANGE_CIPHER: + return tls_new_change_cipher ( tls, data, len ); + case TLS_TYPE_ALERT: + return tls_new_alert ( tls, data, len ); + case TLS_TYPE_HANDSHAKE: + return tls_new_handshake ( tls, data, len ); + case TLS_TYPE_DATA: + return xfer_deliver_raw ( &tls->plainstream.xfer, data, len ); + default: + /* RFC4346 says that we should just ignore unknown + * record types. + */ + DBGC ( tls, "TLS %p ignoring record type %d\n", tls, type ); + return 0; + } +} + +/****************************************************************************** + * + * Record encryption/decryption + * + ****************************************************************************** + */ + +/** + * Calculate HMAC + * + * @v tls TLS session + * @v cipherspec Cipher specification + * @v seq Sequence number + * @v tlshdr TLS header + * @v data Data + * @v len Length of data + * @v mac HMAC to fill in + */ +static void tls_hmac ( struct tls_session *tls __unused, + struct tls_cipherspec *cipherspec, + uint64_t seq, struct tls_header *tlshdr, + const void *data, size_t len, void *hmac ) { + struct digest_algorithm *digest = cipherspec->digest; + uint8_t digest_ctx[digest->ctxsize]; + + hmac_init ( digest, digest_ctx, cipherspec->mac_secret, + &digest->digestsize ); + seq = cpu_to_be64 ( seq ); + hmac_update ( digest, digest_ctx, &seq, sizeof ( seq ) ); + hmac_update ( digest, digest_ctx, tlshdr, sizeof ( *tlshdr ) ); + hmac_update ( digest, digest_ctx, data, len ); + hmac_final ( digest, digest_ctx, cipherspec->mac_secret, + &digest->digestsize, hmac ); +} + +/** + * Allocate and assemble stream-ciphered record from data and MAC portions + * + * @v tls TLS session + * @ret data Data + * @ret len Length of data + * @ret digest MAC digest + * @ret plaintext_len Length of plaintext record + * @ret plaintext Allocated plaintext record + */ +static void * __malloc tls_assemble_stream ( struct tls_session *tls, + const void *data, size_t len, + void *digest, size_t *plaintext_len ) { + size_t mac_len = tls->tx_cipherspec.digest->digestsize; + void *plaintext; + void *content; + void *mac; + + /* Calculate stream-ciphered struct length */ + *plaintext_len = ( len + mac_len ); + + /* Allocate stream-ciphered struct */ + plaintext = malloc ( *plaintext_len ); + if ( ! plaintext ) + return NULL; + content = plaintext; + mac = ( content + len ); + + /* Fill in stream-ciphered struct */ + memcpy ( content, data, len ); + memcpy ( mac, digest, mac_len ); + + return plaintext; +} + +/** + * Allocate and assemble block-ciphered record from data and MAC portions + * + * @v tls TLS session + * @ret data Data + * @ret len Length of data + * @ret digest MAC digest + * @ret plaintext_len Length of plaintext record + * @ret plaintext Allocated plaintext record + */ +static void * tls_assemble_block ( struct tls_session *tls, + const void *data, size_t len, + void *digest, size_t *plaintext_len ) { + size_t blocksize = tls->tx_cipherspec.cipher->blocksize; + size_t iv_len = blocksize; + size_t mac_len = tls->tx_cipherspec.digest->digestsize; + size_t padding_len; + void *plaintext; + void *iv; + void *content; + void *mac; + void *padding; + + /* FIXME: TLSv1.1 has an explicit IV */ + iv_len = 0; + + /* Calculate block-ciphered struct length */ + padding_len = ( ( blocksize - 1 ) & -( iv_len + len + mac_len + 1 ) ); + *plaintext_len = ( iv_len + len + mac_len + padding_len + 1 ); + + /* Allocate block-ciphered struct */ + plaintext = malloc ( *plaintext_len ); + if ( ! plaintext ) + return NULL; + iv = plaintext; + content = ( iv + iv_len ); + mac = ( content + len ); + padding = ( mac + mac_len ); + + /* Fill in block-ciphered struct */ + memset ( iv, 0, iv_len ); + memcpy ( content, data, len ); + memcpy ( mac, digest, mac_len ); + memset ( padding, padding_len, ( padding_len + 1 ) ); + + return plaintext; +} + +/** + * Send plaintext record + * + * @v tls TLS session + * @v type Record type + * @v data Plaintext record + * @v len Length of plaintext record + * @ret rc Return status code + */ +static int tls_send_plaintext ( struct tls_session *tls, unsigned int type, + const void *data, size_t len ) { + struct tls_header plaintext_tlshdr; + struct tls_header *tlshdr; + struct tls_cipherspec *cipherspec = &tls->tx_cipherspec; + void *plaintext = NULL; + size_t plaintext_len; + struct io_buffer *ciphertext = NULL; + size_t ciphertext_len; + size_t mac_len = cipherspec->digest->digestsize; + uint8_t mac[mac_len]; + int rc; + + /* Construct header */ + plaintext_tlshdr.type = type; + plaintext_tlshdr.version = htons ( TLS_VERSION_TLS_1_0 ); + plaintext_tlshdr.length = htons ( len ); + + /* Calculate MAC */ + tls_hmac ( tls, cipherspec, tls->tx_seq, &plaintext_tlshdr, + data, len, mac ); + + /* Allocate and assemble plaintext struct */ + if ( is_stream_cipher ( cipherspec->cipher ) ) { + plaintext = tls_assemble_stream ( tls, data, len, mac, + &plaintext_len ); + } else { + plaintext = tls_assemble_block ( tls, data, len, mac, + &plaintext_len ); + } + if ( ! plaintext ) { + DBGC ( tls, "TLS %p could not allocate %zd bytes for " + "plaintext\n", tls, plaintext_len ); + rc = -ENOMEM; + goto done; + } + + DBGC2 ( tls, "Sending plaintext data:\n" ); + DBGC2_HD ( tls, plaintext, plaintext_len ); + + /* Allocate ciphertext */ + ciphertext_len = ( sizeof ( *tlshdr ) + plaintext_len ); + ciphertext = xfer_alloc_iob ( &tls->cipherstream.xfer, + ciphertext_len ); + if ( ! ciphertext ) { + DBGC ( tls, "TLS %p could not allocate %zd bytes for " + "ciphertext\n", tls, ciphertext_len ); + rc = -ENOMEM; + goto done; + } + + /* Assemble ciphertext */ + tlshdr = iob_put ( ciphertext, sizeof ( *tlshdr ) ); + tlshdr->type = type; + tlshdr->version = htons ( TLS_VERSION_TLS_1_0 ); + tlshdr->length = htons ( plaintext_len ); + memcpy ( cipherspec->cipher_next_ctx, cipherspec->cipher_ctx, + cipherspec->cipher->ctxsize ); + cipher_encrypt ( cipherspec->cipher, cipherspec->cipher_next_ctx, + plaintext, iob_put ( ciphertext, plaintext_len ), + plaintext_len ); + + /* Free plaintext as soon as possible to conserve memory */ + free ( plaintext ); + plaintext = NULL; + + /* Send ciphertext */ + rc = xfer_deliver_iob ( &tls->cipherstream.xfer, ciphertext ); + ciphertext = NULL; + if ( rc != 0 ) { + DBGC ( tls, "TLS %p could not deliver ciphertext: %s\n", + tls, strerror ( rc ) ); + goto done; + } + + /* Update TX state machine to next record */ + tls->tx_seq += 1; + memcpy ( tls->tx_cipherspec.cipher_ctx, + tls->tx_cipherspec.cipher_next_ctx, + tls->tx_cipherspec.cipher->ctxsize ); + + done: + free ( plaintext ); + free_iob ( ciphertext ); + return rc; +} + +/** + * Split stream-ciphered record into data and MAC portions + * + * @v tls TLS session + * @v plaintext Plaintext record + * @v plaintext_len Length of record + * @ret data Data + * @ret len Length of data + * @ret digest MAC digest + * @ret rc Return status code + */ +static int tls_split_stream ( struct tls_session *tls, + void *plaintext, size_t plaintext_len, + void **data, size_t *len, void **digest ) { + void *content; + size_t content_len; + void *mac; + size_t mac_len; + + /* Decompose stream-ciphered data */ + mac_len = tls->rx_cipherspec.digest->digestsize; + if ( plaintext_len < mac_len ) { + DBGC ( tls, "TLS %p received underlength record\n", tls ); + DBGC_HD ( tls, plaintext, plaintext_len ); + return -EINVAL; + } + content_len = ( plaintext_len - mac_len ); + content = plaintext; + mac = ( content + content_len ); + + /* Fill in return values */ + *data = content; + *len = content_len; + *digest = mac; + + return 0; +} + +/** + * Split block-ciphered record into data and MAC portions + * + * @v tls TLS session + * @v plaintext Plaintext record + * @v plaintext_len Length of record + * @ret data Data + * @ret len Length of data + * @ret digest MAC digest + * @ret rc Return status code + */ +static int tls_split_block ( struct tls_session *tls, + void *plaintext, size_t plaintext_len, + void **data, size_t *len, + void **digest ) { + void *iv; + size_t iv_len; + void *content; + size_t content_len; + void *mac; + size_t mac_len; + void *padding; + size_t padding_len; + unsigned int i; + + /* Decompose block-ciphered data */ + if ( plaintext_len < 1 ) { + DBGC ( tls, "TLS %p received underlength record\n", tls ); + DBGC_HD ( tls, plaintext, plaintext_len ); + return -EINVAL; + } + iv_len = tls->rx_cipherspec.cipher->blocksize; + + /* FIXME: TLSv1.1 uses an explicit IV */ + iv_len = 0; + + mac_len = tls->rx_cipherspec.digest->digestsize; + padding_len = *( ( uint8_t * ) ( plaintext + plaintext_len - 1 ) ); + if ( plaintext_len < ( iv_len + mac_len + padding_len + 1 ) ) { + DBGC ( tls, "TLS %p received underlength record\n", tls ); + DBGC_HD ( tls, plaintext, plaintext_len ); + return -EINVAL; + } + content_len = ( plaintext_len - iv_len - mac_len - padding_len - 1 ); + iv = plaintext; + content = ( iv + iv_len ); + mac = ( content + content_len ); + padding = ( mac + mac_len ); + + /* Verify padding bytes */ + for ( i = 0 ; i < padding_len ; i++ ) { + if ( *( ( uint8_t * ) ( padding + i ) ) != padding_len ) { + DBGC ( tls, "TLS %p received bad padding\n", tls ); + DBGC_HD ( tls, plaintext, plaintext_len ); + return -EINVAL; + } + } + + /* Fill in return values */ + *data = content; + *len = content_len; + *digest = mac; + + return 0; +} + +/** + * Receive new ciphertext record + * + * @v tls TLS session + * @v tlshdr Record header + * @v ciphertext Ciphertext record + * @ret rc Return status code + */ +static int tls_new_ciphertext ( struct tls_session *tls, + struct tls_header *tlshdr, void *ciphertext ) { + struct tls_header plaintext_tlshdr; + struct tls_cipherspec *cipherspec = &tls->rx_cipherspec; + size_t record_len = ntohs ( tlshdr->length ); + void *plaintext = NULL; + void *data; + size_t len; + void *mac; + size_t mac_len = cipherspec->digest->digestsize; + uint8_t verify_mac[mac_len]; + int rc; + + /* Allocate buffer for plaintext */ + plaintext = malloc ( record_len ); + if ( ! plaintext ) { + DBGC ( tls, "TLS %p could not allocate %zd bytes for " + "decryption buffer\n", tls, record_len ); + rc = -ENOMEM; + goto done; + } + + /* Decrypt the record */ + cipher_decrypt ( cipherspec->cipher, cipherspec->cipher_ctx, + ciphertext, plaintext, record_len ); + + /* Split record into content and MAC */ + if ( is_stream_cipher ( cipherspec->cipher ) ) { + if ( ( rc = tls_split_stream ( tls, plaintext, record_len, + &data, &len, &mac ) ) != 0 ) + goto done; + } else { + if ( ( rc = tls_split_block ( tls, plaintext, record_len, + &data, &len, &mac ) ) != 0 ) + goto done; + } + + /* Verify MAC */ + plaintext_tlshdr.type = tlshdr->type; + plaintext_tlshdr.version = tlshdr->version; + plaintext_tlshdr.length = htons ( len ); + tls_hmac ( tls, cipherspec, tls->rx_seq, &plaintext_tlshdr, + data, len, verify_mac); + if ( memcmp ( mac, verify_mac, mac_len ) != 0 ) { + DBGC ( tls, "TLS %p failed MAC verification\n", tls ); + DBGC_HD ( tls, plaintext, record_len ); + goto done; + } + + DBGC2 ( tls, "Received plaintext data:\n" ); + DBGC2_HD ( tls, data, len ); + + /* Process plaintext record */ + if ( ( rc = tls_new_record ( tls, tlshdr->type, data, len ) ) != 0 ) + goto done; + + rc = 0; + done: + free ( plaintext ); + return rc; +} + +/****************************************************************************** + * + * Plaintext stream operations + * + ****************************************************************************** + */ + +/** + * Close interface + * + * @v xfer Plainstream data transfer interface + * @v rc Reason for close + */ +static void tls_plainstream_close ( struct xfer_interface *xfer, int rc ) { + struct tls_session *tls = + container_of ( xfer, struct tls_session, plainstream.xfer ); + + tls_close ( tls, rc ); +} + +/** + * Check flow control window + * + * @v xfer Plainstream data transfer interface + * @ret len Length of window + */ +static size_t tls_plainstream_window ( struct xfer_interface *xfer ) { + struct tls_session *tls = + container_of ( xfer, struct tls_session, plainstream.xfer ); + + /* Block window unless we are ready to accept data */ + if ( tls->tx_state != TLS_TX_DATA ) + return 0; + + return filter_window ( xfer ); +} + +/** + * Deliver datagram as raw data + * + * @v xfer Plainstream data transfer interface + * @v data Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +static int tls_plainstream_deliver_raw ( struct xfer_interface *xfer, + const void *data, size_t len ) { + struct tls_session *tls = + container_of ( xfer, struct tls_session, plainstream.xfer ); + + /* Refuse unless we are ready to accept data */ + if ( tls->tx_state != TLS_TX_DATA ) + return -ENOTCONN; + + return tls_send_plaintext ( tls, TLS_TYPE_DATA, data, len ); +} + +/** TLS plaintext stream operations */ +static struct xfer_interface_operations tls_plainstream_operations = { + .close = tls_plainstream_close, + .vredirect = ignore_xfer_vredirect, + .window = tls_plainstream_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = xfer_deliver_as_raw, + .deliver_raw = tls_plainstream_deliver_raw, +}; + +/****************************************************************************** + * + * Ciphertext stream operations + * + ****************************************************************************** + */ + +/** + * Close interface + * + * @v xfer Plainstream data transfer interface + * @v rc Reason for close + */ +static void tls_cipherstream_close ( struct xfer_interface *xfer, int rc ) { + struct tls_session *tls = + container_of ( xfer, struct tls_session, cipherstream.xfer ); + + tls_close ( tls, rc ); +} + +/** + * Handle received TLS header + * + * @v tls TLS session + * @ret rc Returned status code + */ +static int tls_newdata_process_header ( struct tls_session *tls ) { + size_t data_len = ntohs ( tls->rx_header.length ); + + /* Allocate data buffer now that we know the length */ + assert ( tls->rx_data == NULL ); + tls->rx_data = malloc ( data_len ); + if ( ! tls->rx_data ) { + DBGC ( tls, "TLS %p could not allocate %zd bytes " + "for receive buffer\n", tls, data_len ); + return -ENOMEM; + } + + /* Move to data state */ + tls->rx_state = TLS_RX_DATA; + + return 0; +} + +/** + * Handle received TLS data payload + * + * @v tls TLS session + * @ret rc Returned status code + */ +static int tls_newdata_process_data ( struct tls_session *tls ) { + int rc; + + /* Process record */ + if ( ( rc = tls_new_ciphertext ( tls, &tls->rx_header, + tls->rx_data ) ) != 0 ) + return rc; + + /* Increment RX sequence number */ + tls->rx_seq += 1; + + /* Free data buffer */ + free ( tls->rx_data ); + tls->rx_data = NULL; + + /* Return to header state */ + tls->rx_state = TLS_RX_HEADER; + + return 0; +} + +/** + * Receive new ciphertext + * + * @v app Stream application + * @v data Data received + * @v len Length of received data + * @ret rc Return status code + */ +static int tls_cipherstream_deliver_raw ( struct xfer_interface *xfer, + const void *data, size_t len ) { + struct tls_session *tls = + container_of ( xfer, struct tls_session, cipherstream.xfer ); + size_t frag_len; + void *buf; + size_t buf_len; + int ( * process ) ( struct tls_session *tls ); + int rc; + + while ( len ) { + /* Select buffer according to current state */ + switch ( tls->rx_state ) { + case TLS_RX_HEADER: + buf = &tls->rx_header; + buf_len = sizeof ( tls->rx_header ); + process = tls_newdata_process_header; + break; + case TLS_RX_DATA: + buf = tls->rx_data; + buf_len = ntohs ( tls->rx_header.length ); + process = tls_newdata_process_data; + break; + default: + assert ( 0 ); + return -EINVAL; + } + + /* Copy data portion to buffer */ + frag_len = ( buf_len - tls->rx_rcvd ); + if ( frag_len > len ) + frag_len = len; + memcpy ( ( buf + tls->rx_rcvd ), data, frag_len ); + tls->rx_rcvd += frag_len; + data += frag_len; + len -= frag_len; + + /* Process data if buffer is now full */ + if ( tls->rx_rcvd == buf_len ) { + if ( ( rc = process ( tls ) ) != 0 ) { + tls_close ( tls, rc ); + return rc; + } + tls->rx_rcvd = 0; + } + } + + return 0; +} + +/** TLS ciphertext stream operations */ +static struct xfer_interface_operations tls_cipherstream_operations = { + .close = tls_cipherstream_close, + .vredirect = xfer_vopen, + .window = filter_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = xfer_deliver_as_raw, + .deliver_raw = tls_cipherstream_deliver_raw, +}; + +/****************************************************************************** + * + * Controlling process + * + ****************************************************************************** + */ + +/** + * TLS TX state machine + * + * @v process TLS process + */ +static void tls_step ( struct process *process ) { + struct tls_session *tls = + container_of ( process, struct tls_session, process ); + int rc; + + /* Wait for cipherstream to become ready */ + if ( ! xfer_window ( &tls->cipherstream.xfer ) ) + return; + + switch ( tls->tx_state ) { + case TLS_TX_NONE: + /* Nothing to do */ + break; + case TLS_TX_CLIENT_HELLO: + /* Send Client Hello */ + if ( ( rc = tls_send_client_hello ( tls ) ) != 0 ) { + DBGC ( tls, "TLS %p could not send Client Hello: %s\n", + tls, strerror ( rc ) ); + goto err; + } + tls->tx_state = TLS_TX_NONE; + break; + case TLS_TX_CLIENT_KEY_EXCHANGE: + /* Send Client Key Exchange */ + if ( ( rc = tls_send_client_key_exchange ( tls ) ) != 0 ) { + DBGC ( tls, "TLS %p could send Client Key Exchange: " + "%s\n", tls, strerror ( rc ) ); + goto err; + } + tls->tx_state = TLS_TX_CHANGE_CIPHER; + break; + case TLS_TX_CHANGE_CIPHER: + /* Send Change Cipher, and then change the cipher in use */ + if ( ( rc = tls_send_change_cipher ( tls ) ) != 0 ) { + DBGC ( tls, "TLS %p could not send Change Cipher: " + "%s\n", tls, strerror ( rc ) ); + goto err; + } + if ( ( rc = tls_change_cipher ( tls, + &tls->tx_cipherspec_pending, + &tls->tx_cipherspec )) != 0 ){ + DBGC ( tls, "TLS %p could not activate TX cipher: " + "%s\n", tls, strerror ( rc ) ); + goto err; + } + tls->tx_seq = 0; + tls->tx_state = TLS_TX_FINISHED; + break; + case TLS_TX_FINISHED: + /* Send Finished */ + if ( ( rc = tls_send_finished ( tls ) ) != 0 ) { + DBGC ( tls, "TLS %p could not send Finished: %s\n", + tls, strerror ( rc ) ); + goto err; + } + tls->tx_state = TLS_TX_NONE; + break; + case TLS_TX_DATA: + /* Nothing to do */ + break; + default: + assert ( 0 ); + } + + return; + + err: + tls_close ( tls, rc ); +} + +/****************************************************************************** + * + * Instantiator + * + ****************************************************************************** + */ + +int add_tls ( struct xfer_interface *xfer, struct xfer_interface **next ) { + struct tls_session *tls; + + /* Allocate and initialise TLS structure */ + tls = malloc ( sizeof ( *tls ) ); + if ( ! tls ) + return -ENOMEM; + memset ( tls, 0, sizeof ( *tls ) ); + tls->refcnt.free = free_tls; + filter_init ( &tls->plainstream, &tls_plainstream_operations, + &tls->cipherstream, &tls_cipherstream_operations, + &tls->refcnt ); + tls_clear_cipher ( tls, &tls->tx_cipherspec ); + tls_clear_cipher ( tls, &tls->tx_cipherspec_pending ); + tls_clear_cipher ( tls, &tls->rx_cipherspec ); + tls_clear_cipher ( tls, &tls->rx_cipherspec_pending ); + tls->client_random.gmt_unix_time = 0; + tls_generate_random ( &tls->client_random.random, + ( sizeof ( tls->client_random.random ) ) ); + tls->pre_master_secret.version = htons ( TLS_VERSION_TLS_1_0 ); + tls_generate_random ( &tls->pre_master_secret.random, + ( sizeof ( tls->pre_master_secret.random ) ) ); + digest_init ( &md5_algorithm, tls->handshake_md5_ctx ); + digest_init ( &sha1_algorithm, tls->handshake_sha1_ctx ); + tls->tx_state = TLS_TX_CLIENT_HELLO; + process_init ( &tls->process, tls_step, &tls->refcnt ); + + /* Attach to parent interface, mortalise self, and return */ + xfer_plug_plug ( &tls->plainstream.xfer, xfer ); + *next = &tls->cipherstream.xfer; + ref_put ( &tls->refcnt ); + return 0; +} + diff --git a/gpxe/src/net/udp.c b/gpxe/src/net/udp.c new file mode 100644 index 0000000..c3a1eba --- /dev/null +++ b/gpxe/src/net/udp.c @@ -0,0 +1,461 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * UDP protocol + */ + +/** + * A UDP connection + * + */ +struct udp_connection { + /** Reference counter */ + struct refcnt refcnt; + /** List of UDP connections */ + struct list_head list; + + /** Data transfer interface */ + struct xfer_interface xfer; + + /** Local socket address */ + struct sockaddr_tcpip local; + /** Remote socket address */ + struct sockaddr_tcpip peer; +}; + +/** + * List of registered UDP connections + */ +static LIST_HEAD ( udp_conns ); + +/* Forward declatations */ +static struct xfer_interface_operations udp_xfer_operations; +struct tcpip_protocol udp_protocol; + +/** + * Bind UDP connection to local port + * + * @v udp UDP connection + * @ret rc Return status code + * + * Opens the UDP connection and binds to the specified local port. If + * no local port is specified, the first available port will be used. + */ +static int udp_bind ( struct udp_connection *udp ) { + struct udp_connection *existing; + static uint16_t try_port = 1023; + + /* If no port specified, find the first available port */ + if ( ! udp->local.st_port ) { + while ( try_port ) { + try_port++; + if ( try_port < 1024 ) + continue; + udp->local.st_port = htons ( try_port ); + if ( udp_bind ( udp ) == 0 ) + return 0; + } + return -EADDRINUSE; + } + + /* Attempt bind to local port */ + list_for_each_entry ( existing, &udp_conns, list ) { + if ( existing->local.st_port == udp->local.st_port ) { + DBGC ( udp, "UDP %p could not bind: port %d in use\n", + udp, ntohs ( udp->local.st_port ) ); + return -EADDRINUSE; + } + } + + /* Add to UDP connection list */ + DBGC ( udp, "UDP %p bound to port %d\n", + udp, ntohs ( udp->local.st_port ) ); + + return 0; +} + +/** + * Open a UDP connection + * + * @v xfer Data transfer interface + * @v peer Peer socket address, or NULL + * @v local Local socket address, or NULL + * @v promisc Socket is promiscuous + * @ret rc Return status code + */ +static int udp_open_common ( struct xfer_interface *xfer, + struct sockaddr *peer, struct sockaddr *local, + int promisc ) { + struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer; + struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local; + struct udp_connection *udp; + int rc; + + /* Allocate and initialise structure */ + udp = zalloc ( sizeof ( *udp ) ); + if ( ! udp ) + return -ENOMEM; + DBGC ( udp, "UDP %p allocated\n", udp ); + xfer_init ( &udp->xfer, &udp_xfer_operations, &udp->refcnt ); + if ( st_peer ) + memcpy ( &udp->peer, st_peer, sizeof ( udp->peer ) ); + if ( st_local ) + memcpy ( &udp->local, st_local, sizeof ( udp->local ) ); + + /* Bind to local port */ + if ( ! promisc ) { + if ( ( rc = udp_bind ( udp ) ) != 0 ) + goto err; + } + + /* Attach parent interface, transfer reference to connection + * list and return + */ + xfer_plug_plug ( &udp->xfer, xfer ); + list_add ( &udp->list, &udp_conns ); + return 0; + + err: + ref_put ( &udp->refcnt ); + return rc; +} + +/** + * Open a UDP connection + * + * @v xfer Data transfer interface + * @v peer Peer socket address + * @v local Local socket address, or NULL + * @ret rc Return status code + */ +int udp_open ( struct xfer_interface *xfer, struct sockaddr *peer, + struct sockaddr *local ) { + return udp_open_common ( xfer, peer, local, 0 ); +} + +/** + * Open a promiscuous UDP connection + * + * @v xfer Data transfer interface + * @ret rc Return status code + * + * Promiscuous UDP connections are required in order to support the + * PXE API. + */ +int udp_open_promisc ( struct xfer_interface *xfer ) { + return udp_open_common ( xfer, NULL, NULL, 1 ); +} + +/** + * Close a UDP connection + * + * @v udp UDP connection + * @v rc Reason for close + */ +static void udp_close ( struct udp_connection *udp, int rc ) { + + /* Close data transfer interface */ + xfer_nullify ( &udp->xfer ); + xfer_close ( &udp->xfer, rc ); + + /* Remove from list of connections and drop list's reference */ + list_del ( &udp->list ); + ref_put ( &udp->refcnt ); + + DBGC ( udp, "UDP %p closed\n", udp ); +} + +/** + * Transmit data via a UDP connection to a specified address + * + * @v udp UDP connection + * @v iobuf I/O buffer + * @v src Source address, or NULL to use default + * @v dest Destination address, or NULL to use default + * @v netdev Network device, or NULL to use default + * @ret rc Return status code + */ +static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf, + struct sockaddr_tcpip *src, struct sockaddr_tcpip *dest, + struct net_device *netdev ) { + struct udp_header *udphdr; + size_t len; + int rc; + + /* Check we can accommodate the header */ + if ( ( rc = iob_ensure_headroom ( iobuf, UDP_MAX_HLEN ) ) != 0 ) { + free_iob ( iobuf ); + return rc; + } + + /* Fill in default values if not explicitly provided */ + if ( ! src ) + src = &udp->local; + if ( ! dest ) + dest = &udp->peer; + + /* Add the UDP header */ + udphdr = iob_push ( iobuf, sizeof ( *udphdr ) ); + len = iob_len ( iobuf ); + udphdr->dest = dest->st_port; + udphdr->src = src->st_port; + udphdr->len = htons ( len ); + udphdr->chksum = 0; + udphdr->chksum = tcpip_chksum ( udphdr, len ); + + /* Dump debugging information */ + DBGC ( udp, "UDP %p TX %d->%d len %d\n", udp, + ntohs ( udphdr->src ), ntohs ( udphdr->dest ), + ntohs ( udphdr->len ) ); + + /* Send it to the next layer for processing */ + if ( ( rc = tcpip_tx ( iobuf, &udp_protocol, src, dest, netdev, + &udphdr->chksum ) ) != 0 ) { + DBGC ( udp, "UDP %p could not transmit packet: %s\n", + udp, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Identify UDP connection by local address + * + * @v local Local address + * @ret udp UDP connection, or NULL + */ +static struct udp_connection * udp_demux ( struct sockaddr_tcpip *local ) { + static const struct sockaddr_tcpip empty_sockaddr; + struct udp_connection *udp; + + list_for_each_entry ( udp, &udp_conns, list ) { + if ( ( ( udp->local.st_family == local->st_family ) || + ( udp->local.st_family == 0 ) ) && + ( ( udp->local.st_port == local->st_port ) || + ( udp->local.st_port == 0 ) ) && + ( ( memcmp ( udp->local.pad, local->pad, + sizeof ( udp->local.pad ) ) == 0 ) || + ( memcmp ( udp->local.pad, empty_sockaddr.pad, + sizeof ( udp->local.pad ) ) == 0 ) ) ) { + return udp; + } + } + return NULL; +} + +/** + * Process a received packet + * + * @v iobuf I/O buffer + * @v st_src Partially-filled source address + * @v st_dest Partially-filled destination address + * @v pshdr_csum Pseudo-header checksum + * @ret rc Return status code + */ +static int udp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src, + struct sockaddr_tcpip *st_dest, uint16_t pshdr_csum ) { + struct udp_header *udphdr = iobuf->data; + struct udp_connection *udp; + struct xfer_metadata meta; + size_t ulen; + unsigned int csum; + int rc = 0; + + /* Sanity check packet */ + if ( iob_len ( iobuf ) < sizeof ( *udphdr ) ) { + DBG ( "UDP packet too short at %zd bytes (min %zd bytes)\n", + iob_len ( iobuf ), sizeof ( *udphdr ) ); + + rc = -EINVAL; + goto done; + } + ulen = ntohs ( udphdr->len ); + if ( ulen < sizeof ( *udphdr ) ) { + DBG ( "UDP length too short at %zd bytes " + "(header is %zd bytes)\n", ulen, sizeof ( *udphdr ) ); + rc = -EINVAL; + goto done; + } + if ( ulen > iob_len ( iobuf ) ) { + DBG ( "UDP length too long at %zd bytes (packet is %zd " + "bytes)\n", ulen, iob_len ( iobuf ) ); + rc = -EINVAL; + goto done; + } + if ( udphdr->chksum ) { + csum = tcpip_continue_chksum ( pshdr_csum, iobuf->data, ulen ); + if ( csum != 0 ) { + DBG ( "UDP checksum incorrect (is %04x including " + "checksum field, should be 0000)\n", csum ); + rc = -EINVAL; + goto done; + } + } + + /* Parse parameters from header and strip header */ + st_src->st_port = udphdr->src; + st_dest->st_port = udphdr->dest; + udp = udp_demux ( st_dest ); + iob_unput ( iobuf, ( iob_len ( iobuf ) - ulen ) ); + iob_pull ( iobuf, sizeof ( *udphdr ) ); + + /* Dump debugging information */ + DBGC ( udp, "UDP %p RX %d<-%d len %zd\n", udp, + ntohs ( udphdr->dest ), ntohs ( udphdr->src ), ulen ); + + /* Ignore if no matching connection found */ + if ( ! udp ) { + DBG ( "No UDP connection listening on port %d\n", + ntohs ( udphdr->dest ) ); + rc = -ENOTCONN; + goto done; + } + + /* Pass data to application */ + memset ( &meta, 0, sizeof ( meta ) ); + meta.src = ( struct sockaddr * ) st_src; + meta.dest = ( struct sockaddr * ) st_dest; + rc = xfer_deliver_iob_meta ( &udp->xfer, iob_disown ( iobuf ), &meta ); + + done: + free_iob ( iobuf ); + return rc; +} + +struct tcpip_protocol udp_protocol __tcpip_protocol = { + .name = "UDP", + .rx = udp_rx, + .tcpip_proto = IP_UDP, +}; + +/*************************************************************************** + * + * Data transfer interface + * + *************************************************************************** + */ + +/** + * Close interface + * + * @v xfer Data transfer interface + * @v rc Reason for close + */ +static void udp_xfer_close ( struct xfer_interface *xfer, int rc ) { + struct udp_connection *udp = + container_of ( xfer, struct udp_connection, xfer ); + + /* Close connection */ + udp_close ( udp, rc ); +} + +/** + * Allocate I/O buffer for UDP + * + * @v xfer Data transfer interface + * @v len Payload size + * @ret iobuf I/O buffer, or NULL + */ +static struct io_buffer * udp_alloc_iob ( struct xfer_interface *xfer, + size_t len ) { + struct udp_connection *udp = + container_of ( xfer, struct udp_connection, xfer ); + struct io_buffer *iobuf; + + iobuf = alloc_iob ( UDP_MAX_HLEN + len ); + if ( ! iobuf ) { + DBGC ( udp, "UDP %p cannot allocate buffer of length %zd\n", + udp, len ); + return NULL; + } + iob_reserve ( iobuf, UDP_MAX_HLEN ); + return iobuf; +} + +/** + * Deliver datagram as I/O buffer + * + * @v xfer Data transfer interface + * @v iobuf Datagram I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int udp_xfer_deliver_iob ( struct xfer_interface *xfer, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct udp_connection *udp = + container_of ( xfer, struct udp_connection, xfer ); + + /* Transmit data, if possible */ + udp_tx ( udp, iobuf, ( ( struct sockaddr_tcpip * ) meta->src ), + ( ( struct sockaddr_tcpip * ) meta->dest ), meta->netdev ); + + return 0; +} + +/** UDP data transfer interface operations */ +static struct xfer_interface_operations udp_xfer_operations = { + .close = udp_xfer_close, + .vredirect = ignore_xfer_vredirect, + .window = unlimited_xfer_window, + .alloc_iob = udp_alloc_iob, + .deliver_iob = udp_xfer_deliver_iob, + .deliver_raw = xfer_deliver_as_iob, +}; + +/*************************************************************************** + * + * Openers + * + *************************************************************************** + */ + +/** UDP socket opener */ +struct socket_opener udp_socket_opener __socket_opener = { + .semantics = UDP_SOCK_DGRAM, + .family = AF_INET, + .open = udp_open, +}; + +/** Linkage hack */ +int udp_sock_dgram = UDP_SOCK_DGRAM; + +/** + * Open UDP URI + * + * @v xfer Data transfer interface + * @v uri URI + * @ret rc Return status code + */ +static int udp_open_uri ( struct xfer_interface *xfer, struct uri *uri ) { + struct sockaddr_tcpip peer; + + /* Sanity check */ + if ( ! uri->host ) + return -EINVAL; + + memset ( &peer, 0, sizeof ( peer ) ); + peer.st_port = htons ( uri_port ( uri, 0 ) ); + return xfer_open_named_socket ( xfer, SOCK_DGRAM, + ( struct sockaddr * ) &peer, + uri->host, NULL ); +} + +/** UDP URI opener */ +struct uri_opener udp_uri_opener __uri_opener = { + .scheme = "udp", + .open = udp_open_uri, +}; diff --git a/gpxe/src/net/udp/dhcp.c b/gpxe/src/net/udp/dhcp.c new file mode 100644 index 0000000..ab843ce --- /dev/null +++ b/gpxe/src/net/udp/dhcp.c @@ -0,0 +1,1372 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Dynamic Host Configuration Protocol + * + */ + +struct dhcp_session; +static int dhcp_tx ( struct dhcp_session *dhcp ); + +/** + * DHCP operation types + * + * This table maps from DHCP message types (i.e. values of the @c + * DHCP_MESSAGE_TYPE option) to values of the "op" field within a DHCP + * packet. + */ +static const uint8_t dhcp_op[] = { + [DHCPDISCOVER] = BOOTP_REQUEST, + [DHCPOFFER] = BOOTP_REPLY, + [DHCPREQUEST] = BOOTP_REQUEST, + [DHCPDECLINE] = BOOTP_REQUEST, + [DHCPACK] = BOOTP_REPLY, + [DHCPNAK] = BOOTP_REPLY, + [DHCPRELEASE] = BOOTP_REQUEST, + [DHCPINFORM] = BOOTP_REQUEST, +}; + +/** Raw option data for options common to all DHCP requests */ +static uint8_t dhcp_request_options_data[] = { + DHCP_MAX_MESSAGE_SIZE, + DHCP_WORD ( ETH_MAX_MTU - 20 /* IP header */ - 8 /* UDP header */ ), + DHCP_CLIENT_ARCHITECTURE, DHCP_WORD ( 0 ), + DHCP_CLIENT_NDI, DHCP_OPTION ( 1 /* UNDI */ , 2, 1 /* v2.1 */ ), + DHCP_VENDOR_CLASS_ID, + DHCP_STRING ( 'P', 'X', 'E', 'C', 'l', 'i', 'e', 'n', 't', ':', + 'A', 'r', 'c', 'h', ':', '0', '0', '0', '0', '0', ':', + 'U', 'N', 'D', 'I', ':', '0', '0', '2', '0', '0', '1' ), + DHCP_USER_CLASS_ID, + DHCP_STRING ( 'g', 'P', 'X', 'E' ), + DHCP_PARAMETER_REQUEST_LIST, + DHCP_OPTION ( DHCP_SUBNET_MASK, DHCP_ROUTERS, DHCP_DNS_SERVERS, + DHCP_LOG_SERVERS, DHCP_HOST_NAME, DHCP_DOMAIN_NAME, + DHCP_ROOT_PATH, DHCP_VENDOR_ENCAP, DHCP_VENDOR_CLASS_ID, + DHCP_TFTP_SERVER_NAME, DHCP_BOOTFILE_NAME, + DHCP_EB_ENCAP, DHCP_ISCSI_INITIATOR_IQN ), + DHCP_END +}; + +/** DHCP feature codes */ +static uint8_t dhcp_features[0] __table_start ( uint8_t, dhcp_features ); +static uint8_t dhcp_features_end[0] __table_end ( uint8_t, dhcp_features ); + +/** Version number feature */ +FEATURE_VERSION ( VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH ); + +/** DHCP server address setting */ +struct setting dhcp_server_setting __setting = { + .name = "dhcp-server", + .description = "DHCP server address", + .tag = DHCP_SERVER_IDENTIFIER, + .type = &setting_type_ipv4, +}; + +/** DHCP user class setting */ +struct setting user_class_setting __setting = { + .name = "user-class", + .description = "User class identifier", + .tag = DHCP_USER_CLASS_ID, + .type = &setting_type_string, +}; + +/** + * Name a DHCP packet type + * + * @v msgtype DHCP message type + * @ret string DHCP mesasge type name + */ +static inline const char * dhcp_msgtype_name ( unsigned int msgtype ) { + switch ( msgtype ) { + case DHCPNONE: return "BOOTP"; /* Non-DHCP packet */ + case DHCPDISCOVER: return "DHCPDISCOVER"; + case DHCPOFFER: return "DHCPOFFER"; + case DHCPREQUEST: return "DHCPREQUEST"; + case DHCPDECLINE: return "DHCPDECLINE"; + case DHCPACK: return "DHCPACK"; + case DHCPNAK: return "DHCPNAK"; + case DHCPRELEASE: return "DHCPRELEASE"; + case DHCPINFORM: return "DHCPINFORM"; + default: return "DHCP"; + } +} + +/** + * Calculate DHCP transaction ID for a network device + * + * @v netdev Network device + * @ret xid DHCP XID + * + * Extract the least significant bits of the hardware address for use + * as the transaction ID. + */ +static uint32_t dhcp_xid ( struct net_device *netdev ) { + uint32_t xid; + + memcpy ( &xid, ( netdev->ll_addr + netdev->ll_protocol->ll_addr_len + - sizeof ( xid ) ), sizeof ( xid ) ); + return xid; +} + +/**************************************************************************** + * + * DHCP session + * + */ + +struct dhcp_session; + +/** DHCP session state operations */ +struct dhcp_session_state { + /** State name */ + const char *name; + /** + * Construct transmitted packet + * + * @v dhcp DHCP session + * @v dhcppkt DHCP packet + * @v peer Destination address + */ + int ( * tx ) ( struct dhcp_session *dhcp, + struct dhcp_packet *dhcppkt, + struct sockaddr_in *peer ); + /** Handle received packet + * + * @v dhcp DHCP session + * @v dhcppkt DHCP packet + * @v peer DHCP server address + * @v msgtype DHCP message type + * @v server_id DHCP server ID + */ + void ( * rx ) ( struct dhcp_session *dhcp, + struct dhcp_packet *dhcppkt, + struct sockaddr_in *peer, + uint8_t msgtype, struct in_addr server_id ); + /** Handle timer expiry + * + * @v dhcp DHCP session + */ + void ( * expired ) ( struct dhcp_session *dhcp ); + /** Transmitted message type */ + uint8_t tx_msgtype; + /** Apply minimum timeout */ + uint8_t apply_min_timeout; +}; + +static struct dhcp_session_state dhcp_state_discover; +static struct dhcp_session_state dhcp_state_request; +static struct dhcp_session_state dhcp_state_proxy; +static struct dhcp_session_state dhcp_state_pxebs; + +/** A DHCP session */ +struct dhcp_session { + /** Reference counter */ + struct refcnt refcnt; + /** Job control interface */ + struct job_interface job; + /** Data transfer interface */ + struct xfer_interface xfer; + + /** Network device being configured */ + struct net_device *netdev; + /** Local socket address */ + struct sockaddr_in local; + /** State of the session */ + struct dhcp_session_state *state; + + /** Offered IP address */ + struct in_addr offer; + /** DHCP server */ + struct in_addr server; + /** DHCP offer priority */ + int priority; + + /** ProxyDHCP protocol extensions should be ignored */ + int no_pxedhcp; + /** ProxyDHCP server */ + struct in_addr proxy_server; + /** ProxyDHCP server priority */ + int proxy_priority; + + /** PXE Boot Server type */ + uint16_t pxe_type; + /** List of PXE Boot Servers to attempt */ + struct in_addr *pxe_attempt; + /** List of PXE Boot Servers to accept */ + struct in_addr *pxe_accept; + + /** Retransmission timer */ + struct retry_timer timer; + /** Start time of the current state (in ticks) */ + unsigned long start; +}; + +/** + * Free DHCP session + * + * @v refcnt Reference counter + */ +static void dhcp_free ( struct refcnt *refcnt ) { + struct dhcp_session *dhcp = + container_of ( refcnt, struct dhcp_session, refcnt ); + + netdev_put ( dhcp->netdev ); + free ( dhcp ); +} + +/** + * Mark DHCP session as complete + * + * @v dhcp DHCP session + * @v rc Return status code + */ +static void dhcp_finished ( struct dhcp_session *dhcp, int rc ) { + + /* Block futher incoming messages */ + job_nullify ( &dhcp->job ); + xfer_nullify ( &dhcp->xfer ); + + /* Stop retry timer */ + stop_timer ( &dhcp->timer ); + + /* Free resources and close interfaces */ + xfer_close ( &dhcp->xfer, rc ); + job_done ( &dhcp->job, rc ); +} + +/** + * Transition to new DHCP session state + * + * @v dhcp DHCP session + * @v state New session state + */ +static void dhcp_set_state ( struct dhcp_session *dhcp, + struct dhcp_session_state *state ) { + + DBGC ( dhcp, "DHCP %p entering %s state\n", dhcp, state->name ); + dhcp->state = state; + dhcp->start = currticks(); + stop_timer ( &dhcp->timer ); + dhcp->timer.min_timeout = + ( state->apply_min_timeout ? DHCP_MIN_TIMEOUT : 0 ); + dhcp->timer.max_timeout = DHCP_MAX_TIMEOUT; + start_timer_nodelay ( &dhcp->timer ); +} + +/**************************************************************************** + * + * DHCP state machine + * + */ + +/** + * Construct transmitted packet for DHCP discovery + * + * @v dhcp DHCP session + * @v dhcppkt DHCP packet + * @v peer Destination address + */ +static int dhcp_discovery_tx ( struct dhcp_session *dhcp, + struct dhcp_packet *dhcppkt __unused, + struct sockaddr_in *peer ) { + + DBGC ( dhcp, "DHCP %p DHCPDISCOVER\n", dhcp ); + + /* Set server address */ + peer->sin_addr.s_addr = INADDR_BROADCAST; + peer->sin_port = htons ( BOOTPS_PORT ); + + return 0; +} + +/** + * Handle received packet during DHCP discovery + * + * @v dhcp DHCP session + * @v dhcppkt DHCP packet + * @v peer DHCP server address + * @v msgtype DHCP message type + * @v server_id DHCP server ID + */ +static void dhcp_discovery_rx ( struct dhcp_session *dhcp, + struct dhcp_packet *dhcppkt, + struct sockaddr_in *peer, uint8_t msgtype, + struct in_addr server_id ) { + struct in_addr ip; + char vci[9]; /* "PXEClient" */ + int vci_len; + int has_pxeclient; + int8_t priority = 0; + uint8_t no_pxedhcp = 0; + unsigned long elapsed; + + DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp, + dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ), + ntohs ( peer->sin_port ) ); + if ( server_id.s_addr != peer->sin_addr.s_addr ) + DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) ); + + /* Identify offered IP address */ + ip = dhcppkt->dhcphdr->yiaddr; + if ( ip.s_addr ) + DBGC ( dhcp, " for %s", inet_ntoa ( ip ) ); + + /* Identify "PXEClient" vendor class */ + vci_len = dhcppkt_fetch ( dhcppkt, DHCP_VENDOR_CLASS_ID, + vci, sizeof ( vci ) ); + has_pxeclient = ( ( vci_len >= ( int ) sizeof ( vci ) ) && + ( strncmp ( "PXEClient", vci, sizeof (vci) ) == 0 )); + if ( has_pxeclient ) + DBGC ( dhcp, " pxe" ); + + /* Identify priority */ + dhcppkt_fetch ( dhcppkt, DHCP_EB_PRIORITY, &priority, + sizeof ( priority ) ); + if ( priority ) + DBGC ( dhcp, " pri %d", priority ); + + /* Identify ignore-PXE flag */ + dhcppkt_fetch ( dhcppkt, DHCP_EB_NO_PXEDHCP, &no_pxedhcp, + sizeof ( no_pxedhcp ) ); + if ( no_pxedhcp ) + DBGC ( dhcp, " nopxe" ); + DBGC ( dhcp, "\n" ); + + /* Select as DHCP offer, if applicable */ + if ( ip.s_addr && ( peer->sin_port == htons ( BOOTPS_PORT ) ) && + ( ( msgtype == DHCPOFFER ) || ( ! msgtype /* BOOTP */ ) ) && + ( priority >= dhcp->priority ) ) { + dhcp->offer = ip; + dhcp->server = server_id; + dhcp->priority = priority; + dhcp->no_pxedhcp = no_pxedhcp; + } + + /* Select as ProxyDHCP offer, if applicable */ + if ( has_pxeclient && ( msgtype == DHCPOFFER ) && + ( priority >= dhcp->proxy_priority ) ) { + dhcp->proxy_server = server_id; + dhcp->proxy_priority = priority; + } + + /* We can exit the discovery state when we have a valid + * DHCPOFFER, and either: + * + * o The DHCPOFFER instructs us to ignore ProxyDHCPOFFERs, or + * o We have a valid ProxyDHCPOFFER, or + * o We have allowed sufficient time for ProxyDHCPOFFERs. + */ + + /* If we don't yet have a DHCPOFFER, do nothing */ + if ( ! dhcp->offer.s_addr ) + return; + + /* If we can't yet transition to DHCPREQUEST, do nothing */ + elapsed = ( currticks() - dhcp->start ); + if ( ! ( dhcp->no_pxedhcp || dhcp->proxy_server.s_addr || + ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) ) + return; + + /* Transition to DHCPREQUEST */ + dhcp_set_state ( dhcp, &dhcp_state_request ); +} + +/** + * Handle timer expiry during DHCP discovery + * + * @v dhcp DHCP session + */ +static void dhcp_discovery_expired ( struct dhcp_session *dhcp ) { + unsigned long elapsed = ( currticks() - dhcp->start ); + + /* Give up waiting for ProxyDHCP before we reach the failure point */ + if ( dhcp->offer.s_addr && ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) { + dhcp_set_state ( dhcp, &dhcp_state_request ); + return; + } + + /* Otherwise, retransmit current packet */ + dhcp_tx ( dhcp ); +} + +/** DHCP discovery state operations */ +static struct dhcp_session_state dhcp_state_discover = { + .name = "discovery", + .tx = dhcp_discovery_tx, + .rx = dhcp_discovery_rx, + .expired = dhcp_discovery_expired, + .tx_msgtype = DHCPDISCOVER, + .apply_min_timeout = 1, +}; + +/** + * Construct transmitted packet for DHCP request + * + * @v dhcp DHCP session + * @v dhcppkt DHCP packet + * @v peer Destination address + */ +static int dhcp_request_tx ( struct dhcp_session *dhcp, + struct dhcp_packet *dhcppkt, + struct sockaddr_in *peer ) { + int rc; + + DBGC ( dhcp, "DHCP %p DHCPREQUEST to %s:%d", + dhcp, inet_ntoa ( dhcp->server ), BOOTPS_PORT ); + DBGC ( dhcp, " for %s\n", inet_ntoa ( dhcp->offer ) ); + + /* Set server ID */ + if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER, + &dhcp->server, + sizeof ( dhcp->server ) ) ) != 0 ) + return rc; + + /* Set requested IP address */ + if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_REQUESTED_ADDRESS, + &dhcp->offer, + sizeof ( dhcp->offer ) ) ) != 0 ) + return rc; + + /* Set server address */ + peer->sin_addr.s_addr = INADDR_BROADCAST; + peer->sin_port = htons ( BOOTPS_PORT ); + + return 0; +} + +/** + * Handle received packet during DHCP request + * + * @v dhcp DHCP session + * @v dhcppkt DHCP packet + * @v peer DHCP server address + * @v msgtype DHCP message type + * @v server_id DHCP server ID + */ +static void dhcp_request_rx ( struct dhcp_session *dhcp, + struct dhcp_packet *dhcppkt, + struct sockaddr_in *peer, uint8_t msgtype, + struct in_addr server_id ) { + struct in_addr ip; + struct settings *parent; + int rc; + + DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp, + dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ), + ntohs ( peer->sin_port ) ); + if ( server_id.s_addr != peer->sin_addr.s_addr ) + DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) ); + + /* Identify leased IP address */ + ip = dhcppkt->dhcphdr->yiaddr; + if ( ip.s_addr ) + DBGC ( dhcp, " for %s", inet_ntoa ( ip ) ); + DBGC ( dhcp, "\n" ); + + /* Filter out unacceptable responses */ + if ( peer->sin_port != htons ( BOOTPS_PORT ) ) + return; + if ( msgtype /* BOOTP */ && ( msgtype != DHCPACK ) ) + return; + if ( server_id.s_addr != dhcp->server.s_addr ) + return; + + /* Record assigned address */ + dhcp->local.sin_addr = ip; + + /* Register settings */ + parent = netdev_settings ( dhcp->netdev ); + if ( ( rc = register_settings ( &dhcppkt->settings, parent ) ) != 0 ){ + DBGC ( dhcp, "DHCP %p could not register settings: %s\n", + dhcp, strerror ( rc ) ); + dhcp_finished ( dhcp, rc ); + return; + } + + /* Start ProxyDHCPREQUEST if applicable */ + if ( dhcp->proxy_server.s_addr && ( ! dhcp->no_pxedhcp ) ) { + dhcp_set_state ( dhcp, &dhcp_state_proxy ); + return; + } + + /* Terminate DHCP */ + dhcp_finished ( dhcp, 0 ); +} + +/** + * Handle timer expiry during DHCP discovery + * + * @v dhcp DHCP session + */ +static void dhcp_request_expired ( struct dhcp_session *dhcp ) { + + /* Retransmit current packet */ + dhcp_tx ( dhcp ); +} + +/** DHCP request state operations */ +static struct dhcp_session_state dhcp_state_request = { + .name = "request", + .tx = dhcp_request_tx, + .rx = dhcp_request_rx, + .expired = dhcp_request_expired, + .tx_msgtype = DHCPREQUEST, + .apply_min_timeout = 0, +}; + +/** + * Construct transmitted packet for ProxyDHCP request + * + * @v dhcp DHCP session + * @v dhcppkt DHCP packet + * @v peer Destination address + */ +static int dhcp_proxy_tx ( struct dhcp_session *dhcp, + struct dhcp_packet *dhcppkt, + struct sockaddr_in *peer ) { + int rc; + + DBGC ( dhcp, "DHCP %p ProxyDHCP REQUEST to %s:%d\n", + dhcp, inet_ntoa ( dhcp->proxy_server ), PXE_PORT ); + + /* Set server ID */ + if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_SERVER_IDENTIFIER, + &dhcp->proxy_server, + sizeof ( dhcp->proxy_server ) ) ) != 0 ) + return rc; + + /* Set server address */ + peer->sin_addr = dhcp->proxy_server; + peer->sin_port = htons ( PXE_PORT ); + + return 0; +} + +/** + * Handle received packet during ProxyDHCP request + * + * @v dhcp DHCP session + * @v dhcppkt DHCP packet + * @v peer DHCP server address + * @v msgtype DHCP message type + * @v server_id DHCP server ID + */ +static void dhcp_proxy_rx ( struct dhcp_session *dhcp, + struct dhcp_packet *dhcppkt, + struct sockaddr_in *peer, uint8_t msgtype, + struct in_addr server_id ) { + int rc; + + DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp, + dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ), + ntohs ( peer->sin_port ) ); + if ( server_id.s_addr != peer->sin_addr.s_addr ) + DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) ); + DBGC ( dhcp, "\n" ); + + /* Filter out unacceptable responses */ + if ( peer->sin_port != htons ( PXE_PORT ) ) + return; + if ( msgtype != DHCPACK ) + return; + if ( server_id.s_addr /* Linux PXE server omits server ID */ && + ( server_id.s_addr != dhcp->proxy_server.s_addr ) ) + return; + + /* Register settings */ + dhcppkt->settings.name = PROXYDHCP_SETTINGS_NAME; + if ( ( rc = register_settings ( &dhcppkt->settings, NULL ) ) != 0 ) { + DBGC ( dhcp, "DHCP %p could not register settings: %s\n", + dhcp, strerror ( rc ) ); + dhcp_finished ( dhcp, rc ); + return; + } + + /* Terminate DHCP */ + dhcp_finished ( dhcp, 0 ); +} + +/** + * Handle timer expiry during ProxyDHCP request + * + * @v dhcp DHCP session + */ +static void dhcp_proxy_expired ( struct dhcp_session *dhcp ) { + unsigned long elapsed = ( currticks() - dhcp->start ); + + /* Give up waiting for ProxyDHCP before we reach the failure point */ + if ( elapsed > PROXYDHCP_MAX_TIMEOUT ) { + dhcp_finished ( dhcp, 0 ); + return; + } + + /* Retransmit current packet */ + dhcp_tx ( dhcp ); +} + +/** ProxyDHCP request state operations */ +static struct dhcp_session_state dhcp_state_proxy = { + .name = "ProxyDHCP", + .tx = dhcp_proxy_tx, + .rx = dhcp_proxy_rx, + .expired = dhcp_proxy_expired, + .tx_msgtype = DHCPREQUEST, + .apply_min_timeout = 0, +}; + +/** + * Construct transmitted packet for PXE Boot Server Discovery + * + * @v dhcp DHCP session + * @v dhcppkt DHCP packet + * @v peer Destination address + */ +static int dhcp_pxebs_tx ( struct dhcp_session *dhcp, + struct dhcp_packet *dhcppkt, + struct sockaddr_in *peer ) { + struct dhcp_pxe_boot_menu_item menu_item = { 0, 0 }; + int rc; + + DBGC ( dhcp, "DHCP %p PXEBS REQUEST to %s:%d for type %d\n", + dhcp, inet_ntoa ( *(dhcp->pxe_attempt) ), PXE_PORT, + ntohs ( dhcp->pxe_type ) ); + + /* Set boot menu item */ + menu_item.type = dhcp->pxe_type; + if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_PXE_BOOT_MENU_ITEM, + &menu_item, sizeof ( menu_item ) ) ) != 0 ) + return rc; + + /* Set server address */ + peer->sin_addr = *(dhcp->pxe_attempt); + peer->sin_port = htons ( PXE_PORT ); + + return 0; +} + +/** + * Check to see if PXE Boot Server address is acceptable + * + * @v dhcp DHCP session + * @v bs Boot Server address + * @ret accept Boot Server is acceptable + */ +static int dhcp_pxebs_accept ( struct dhcp_session *dhcp, + struct in_addr bs ) { + struct in_addr *accept; + + /* Accept if we have no acceptance filter */ + if ( ! dhcp->pxe_accept ) + return 1; + + /* Scan through acceptance list */ + for ( accept = dhcp->pxe_accept ; accept->s_addr ; accept++ ) { + if ( accept->s_addr == bs.s_addr ) + return 1; + } + + DBGC ( dhcp, "DHCP %p rejecting server %s\n", + dhcp, inet_ntoa ( bs ) ); + return 0; +} + +/** + * Handle received packet during PXE Boot Server Discovery + * + * @v dhcp DHCP session + * @v dhcppkt DHCP packet + * @v peer DHCP server address + * @v msgtype DHCP message type + * @v server_id DHCP server ID + */ +static void dhcp_pxebs_rx ( struct dhcp_session *dhcp, + struct dhcp_packet *dhcppkt, + struct sockaddr_in *peer, uint8_t msgtype, + struct in_addr server_id ) { + struct dhcp_pxe_boot_menu_item menu_item = { 0, 0 }; + int rc; + + DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp, + dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ), + ntohs ( peer->sin_port ) ); + if ( server_id.s_addr != peer->sin_addr.s_addr ) + DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) ); + + /* Identify boot menu item */ + dhcppkt_fetch ( dhcppkt, DHCP_PXE_BOOT_MENU_ITEM, + &menu_item, sizeof ( menu_item ) ); + if ( menu_item.type ) + DBGC ( dhcp, " for type %d", ntohs ( menu_item.type ) ); + DBGC ( dhcp, "\n" ); + + /* Filter out unacceptable responses */ + if ( peer->sin_port != htons ( PXE_PORT ) ) + return; + if ( msgtype != DHCPACK ) + return; + if ( menu_item.type != dhcp->pxe_type ) + return; + if ( ! dhcp_pxebs_accept ( dhcp, ( server_id.s_addr ? + server_id : peer->sin_addr ) ) ) + return; + + /* Register settings */ + dhcppkt->settings.name = PXEBS_SETTINGS_NAME; + if ( ( rc = register_settings ( &dhcppkt->settings, NULL ) ) != 0 ) { + DBGC ( dhcp, "DHCP %p could not register settings: %s\n", + dhcp, strerror ( rc ) ); + dhcp_finished ( dhcp, rc ); + return; + } + + /* Terminate DHCP */ + dhcp_finished ( dhcp, 0 ); +} + +/** + * Handle timer expiry during PXE Boot Server Discovery + * + * @v dhcp DHCP session + */ +static void dhcp_pxebs_expired ( struct dhcp_session *dhcp ) { + unsigned long elapsed = ( currticks() - dhcp->start ); + + /* Give up waiting before we reach the failure point, and fail + * over to the next server in the attempt list + */ + if ( elapsed > PXEBS_MAX_TIMEOUT ) { + dhcp->pxe_attempt++; + if ( dhcp->pxe_attempt->s_addr ) { + dhcp_set_state ( dhcp, &dhcp_state_pxebs ); + return; + } else { + dhcp_finished ( dhcp, -ETIMEDOUT ); + return; + } + } + + /* Retransmit current packet */ + dhcp_tx ( dhcp ); +} + +/** PXE Boot Server Discovery state operations */ +static struct dhcp_session_state dhcp_state_pxebs = { + .name = "PXEBS", + .tx = dhcp_pxebs_tx, + .rx = dhcp_pxebs_rx, + .expired = dhcp_pxebs_expired, + .tx_msgtype = DHCPREQUEST, + .apply_min_timeout = 1, +}; + +/**************************************************************************** + * + * Packet construction + * + */ + +/** + * Create a DHCP packet + * + * @v dhcppkt DHCP packet structure to fill in + * @v netdev Network device + * @v msgtype DHCP message type + * @v options Initial options to include (or NULL) + * @v options_len Length of initial options + * @v data Buffer for DHCP packet + * @v max_len Size of DHCP packet buffer + * @ret rc Return status code + * + * Creates a DHCP packet in the specified buffer, and initialise a + * DHCP packet structure. + */ +int dhcp_create_packet ( struct dhcp_packet *dhcppkt, + struct net_device *netdev, uint8_t msgtype, + const void *options, size_t options_len, + void *data, size_t max_len ) { + struct dhcphdr *dhcphdr = data; + unsigned int hlen; + int rc; + + /* Sanity check */ + if ( max_len < ( sizeof ( *dhcphdr ) + options_len ) ) + return -ENOSPC; + + /* Initialise DHCP packet content */ + memset ( dhcphdr, 0, max_len ); + dhcphdr->xid = dhcp_xid ( netdev ); + dhcphdr->magic = htonl ( DHCP_MAGIC_COOKIE ); + dhcphdr->htype = ntohs ( netdev->ll_protocol->ll_proto ); + dhcphdr->op = dhcp_op[msgtype]; + /* If hardware length exceeds the chaddr field length, don't + * use the chaddr field. This is as per RFC4390. + */ + hlen = netdev->ll_protocol->ll_addr_len; + if ( hlen > sizeof ( dhcphdr->chaddr ) ) { + hlen = 0; + dhcphdr->flags = htons ( BOOTP_FL_BROADCAST ); + } + dhcphdr->hlen = hlen; + memcpy ( dhcphdr->chaddr, netdev->ll_addr, hlen ); + memcpy ( dhcphdr->options, options, options_len ); + + /* Initialise DHCP packet structure */ + memset ( dhcppkt, 0, sizeof ( *dhcppkt ) ); + dhcppkt_init ( dhcppkt, data, max_len ); + + /* Set DHCP_MESSAGE_TYPE option */ + if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_MESSAGE_TYPE, + &msgtype, sizeof ( msgtype ) ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Create DHCP request packet + * + * @v dhcppkt DHCP packet structure to fill in + * @v netdev Network device + * @v msgtype DHCP message type + * @v ciaddr Client IP address + * @v data Buffer for DHCP packet + * @v max_len Size of DHCP packet buffer + * @ret rc Return status code + * + * Creates a DHCP request packet in the specified buffer, and + * initialise a DHCP packet structure. + */ +int dhcp_create_request ( struct dhcp_packet *dhcppkt, + struct net_device *netdev, unsigned int msgtype, + struct in_addr ciaddr, void *data, size_t max_len ) { + struct device_description *desc = &netdev->dev->desc; + struct dhcp_netdev_desc dhcp_desc; + struct dhcp_client_id client_id; + struct dhcp_client_uuid client_uuid; + size_t dhcp_features_len; + size_t ll_addr_len; + ssize_t len; + int rc; + + /* Create DHCP packet */ + if ( ( rc = dhcp_create_packet ( dhcppkt, netdev, msgtype, + dhcp_request_options_data, + sizeof ( dhcp_request_options_data ), + data, max_len ) ) != 0 ) { + DBG ( "DHCP could not create DHCP packet: %s\n", + strerror ( rc ) ); + return rc; + } + + /* Set client IP address */ + dhcppkt->dhcphdr->ciaddr = ciaddr; + + /* Add options to identify the feature list */ + dhcp_features_len = ( dhcp_features_end - dhcp_features ); + if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_ENCAP, dhcp_features, + dhcp_features_len ) ) != 0 ) { + DBG ( "DHCP could not set features list option: %s\n", + strerror ( rc ) ); + return rc; + } + + /* Add options to identify the network device */ + dhcp_desc.type = desc->bus_type; + dhcp_desc.vendor = htons ( desc->vendor ); + dhcp_desc.device = htons ( desc->device ); + if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_EB_BUS_ID, &dhcp_desc, + sizeof ( dhcp_desc ) ) ) != 0 ) { + DBG ( "DHCP could not set bus ID option: %s\n", + strerror ( rc ) ); + return rc; + } + + /* Add DHCP client identifier. Required for Infiniband, and + * doesn't hurt other link layers. + */ + client_id.ll_proto = ntohs ( netdev->ll_protocol->ll_proto ); + ll_addr_len = netdev->ll_protocol->ll_addr_len; + assert ( ll_addr_len <= sizeof ( client_id.ll_addr ) ); + memcpy ( client_id.ll_addr, netdev->ll_addr, ll_addr_len ); + if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_ID, &client_id, + ( ll_addr_len + 1 ) ) ) != 0 ) { + DBG ( "DHCP could not set client ID: %s\n", + strerror ( rc ) ); + return rc; + } + + /* Add client UUID, if we have one. Required for PXE. */ + client_uuid.type = DHCP_CLIENT_UUID_TYPE; + if ( ( len = fetch_uuid_setting ( NULL, &uuid_setting, + &client_uuid.uuid ) ) >= 0 ) { + if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_CLIENT_UUID, + &client_uuid, + sizeof ( client_uuid ) ) ) != 0 ) { + DBG ( "DHCP could not set client UUID: %s\n", + strerror ( rc ) ); + return rc; + } + } + + /* Add user class, if we have one. */ + if ( ( len = fetch_setting_len ( NULL, &user_class_setting ) ) >= 0 ) { + char user_class[len]; + fetch_setting ( NULL, &user_class_setting, user_class, + sizeof ( user_class ) ); + if ( ( rc = dhcppkt_store ( dhcppkt, DHCP_USER_CLASS_ID, + &user_class, + sizeof ( user_class ) ) ) != 0 ) { + DBG ( "DHCP could not set user class: %s\n", + strerror ( rc ) ); + return rc; + } + } + + return 0; +} + +/**************************************************************************** + * + * Data transfer interface + * + */ + +/** + * Transmit DHCP request + * + * @v dhcp DHCP session + * @ret rc Return status code + */ +static int dhcp_tx ( struct dhcp_session *dhcp ) { + static struct sockaddr_in peer = { + .sin_family = AF_INET, + }; + struct xfer_metadata meta = { + .netdev = dhcp->netdev, + .src = ( struct sockaddr * ) &dhcp->local, + .dest = ( struct sockaddr * ) &peer, + }; + struct io_buffer *iobuf; + uint8_t msgtype = dhcp->state->tx_msgtype; + struct dhcp_packet dhcppkt; + int rc; + + /* Start retry timer. Do this first so that failures to + * transmit will be retried. + */ + start_timer ( &dhcp->timer ); + + /* Allocate buffer for packet */ + iobuf = xfer_alloc_iob ( &dhcp->xfer, DHCP_MIN_LEN ); + if ( ! iobuf ) + return -ENOMEM; + + /* Create basic DHCP packet in temporary buffer */ + if ( ( rc = dhcp_create_request ( &dhcppkt, dhcp->netdev, msgtype, + dhcp->local.sin_addr, iobuf->data, + iob_tailroom ( iobuf ) ) ) != 0 ) { + DBGC ( dhcp, "DHCP %p could not construct DHCP request: %s\n", + dhcp, strerror ( rc ) ); + goto done; + } + + /* Fill in packet based on current state */ + if ( ( rc = dhcp->state->tx ( dhcp, &dhcppkt, &peer ) ) != 0 ) { + DBGC ( dhcp, "DHCP %p could not fill DHCP request: %s\n", + dhcp, strerror ( rc ) ); + goto done; + } + + /* Transmit the packet */ + iob_put ( iobuf, dhcppkt.len ); + if ( ( rc = xfer_deliver_iob_meta ( &dhcp->xfer, iob_disown ( iobuf ), + &meta ) ) != 0 ) { + DBGC ( dhcp, "DHCP %p could not transmit UDP packet: %s\n", + dhcp, strerror ( rc ) ); + goto done; + } + + done: + free_iob ( iobuf ); + return rc; +} + +/** + * Receive new data + * + * @v xfer Data transfer interface + * @v iobuf I/O buffer + * @v meta Transfer metadata + * @ret rc Return status code + */ +static int dhcp_deliver_iob ( struct xfer_interface *xfer, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct dhcp_session *dhcp = + container_of ( xfer, struct dhcp_session, xfer ); + struct sockaddr_in *peer; + size_t data_len; + struct dhcp_packet *dhcppkt; + struct dhcphdr *dhcphdr; + uint8_t msgtype = 0; + struct in_addr server_id = { 0 }; + int rc = 0; + + /* Sanity checks */ + if ( ! meta->src ) { + DBGC ( dhcp, "DHCP %p received packet without source port\n", + dhcp ); + rc = -EINVAL; + goto err_no_src; + } + peer = ( struct sockaddr_in * ) meta->src; + + /* Create a DHCP packet containing the I/O buffer contents. + * Whilst we could just use the original buffer in situ, that + * would waste the unused space in the packet buffer, and also + * waste a relatively scarce fully-aligned I/O buffer. + */ + data_len = iob_len ( iobuf ); + dhcppkt = zalloc ( sizeof ( *dhcppkt ) + data_len ); + if ( ! dhcppkt ) { + rc = -ENOMEM; + goto err_alloc_dhcppkt; + } + dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) ); + memcpy ( dhcphdr, iobuf->data, data_len ); + dhcppkt_init ( dhcppkt, dhcphdr, data_len ); + + /* Identify message type */ + dhcppkt_fetch ( dhcppkt, DHCP_MESSAGE_TYPE, &msgtype, + sizeof ( msgtype ) ); + + /* Identify server ID */ + dhcppkt_fetch ( dhcppkt, DHCP_SERVER_IDENTIFIER, + &server_id, sizeof ( server_id ) ); + + /* Check for matching transaction ID */ + if ( dhcphdr->xid != dhcp_xid ( dhcp->netdev ) ) { + DBGC ( dhcp, "DHCP %p %s from %s:%d has bad transaction " + "ID\n", dhcp, dhcp_msgtype_name ( msgtype ), + inet_ntoa ( peer->sin_addr ), + ntohs ( peer->sin_port ) ); + rc = -EINVAL; + goto err_xid; + }; + + /* Handle packet based on current state */ + dhcp->state->rx ( dhcp, dhcppkt, peer, msgtype, server_id ); + + err_xid: + dhcppkt_put ( dhcppkt ); + err_alloc_dhcppkt: + err_no_src: + free_iob ( iobuf ); + return rc; +} + +/** DHCP data transfer interface operations */ +static struct xfer_interface_operations dhcp_xfer_operations = { + .close = ignore_xfer_close, + .vredirect = xfer_vopen, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = dhcp_deliver_iob, + .deliver_raw = xfer_deliver_as_iob, +}; + +/** + * Handle DHCP retry timer expiry + * + * @v timer DHCP retry timer + * @v fail Failure indicator + */ +static void dhcp_timer_expired ( struct retry_timer *timer, int fail ) { + struct dhcp_session *dhcp = + container_of ( timer, struct dhcp_session, timer ); + + /* If we have failed, terminate DHCP */ + if ( fail ) { + dhcp_finished ( dhcp, -ETIMEDOUT ); + return; + } + + /* Handle timer expiry based on current state */ + dhcp->state->expired ( dhcp ); +} + +/**************************************************************************** + * + * Job control interface + * + */ + +/** + * Handle kill() event received via job control interface + * + * @v job DHCP job control interface + */ +static void dhcp_job_kill ( struct job_interface *job ) { + struct dhcp_session *dhcp = + container_of ( job, struct dhcp_session, job ); + + /* Terminate DHCP session */ + dhcp_finished ( dhcp, -ECANCELED ); +} + +/** DHCP job control interface operations */ +static struct job_interface_operations dhcp_job_operations = { + .done = ignore_job_done, + .kill = dhcp_job_kill, + .progress = ignore_job_progress, +}; + +/**************************************************************************** + * + * Instantiators + * + */ + +/** + * DHCP peer address for socket opening + * + * This is a dummy address; the only useful portion is the socket + * family (so that we get a UDP connection). The DHCP client will set + * the IP address and source port explicitly on each transmission. + */ +static struct sockaddr dhcp_peer = { + .sa_family = AF_INET, +}; + +/** + * Start DHCP state machine on a network device + * + * @v job Job control interface + * @v netdev Network device + * @ret rc Return status code + * + * Starts DHCP on the specified network device. If successful, the + * DHCPACK (and ProxyDHCPACK, if applicable) will be registered as + * option sources. + */ +int start_dhcp ( struct job_interface *job, struct net_device *netdev ) { + struct dhcp_session *dhcp; + int rc; + + /* Allocate and initialise structure */ + dhcp = zalloc ( sizeof ( *dhcp ) ); + if ( ! dhcp ) + return -ENOMEM; + dhcp->refcnt.free = dhcp_free; + job_init ( &dhcp->job, &dhcp_job_operations, &dhcp->refcnt ); + xfer_init ( &dhcp->xfer, &dhcp_xfer_operations, &dhcp->refcnt ); + dhcp->netdev = netdev_get ( netdev ); + dhcp->local.sin_family = AF_INET; + dhcp->local.sin_port = htons ( BOOTPC_PORT ); + dhcp->timer.expired = dhcp_timer_expired; + + /* Instantiate child objects and attach to our interfaces */ + if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM, &dhcp_peer, + ( struct sockaddr * ) &dhcp->local ) ) != 0 ) + goto err; + + /* Enter DHCPDISCOVER state */ + dhcp_set_state ( dhcp, &dhcp_state_discover ); + + /* Attach parent interface, mortalise self, and return */ + job_plug_plug ( &dhcp->job, job ); + ref_put ( &dhcp->refcnt ); + return 0; + + err: + dhcp_finished ( dhcp, rc ); + ref_put ( &dhcp->refcnt ); + return rc; +} + +/** + * Retrieve list of PXE boot servers for a given server type + * + * @v dhcp DHCP session + * @v raw DHCP PXE boot server list + * @v raw_len Length of DHCP PXE boot server list + * @v ip IP address list to fill in + * + * The caller must ensure that the IP address list has sufficient + * space. + */ +static void pxebs_list ( struct dhcp_session *dhcp, void *raw, + size_t raw_len, struct in_addr *ip ) { + struct dhcp_pxe_boot_server *server = raw; + size_t server_len; + unsigned int i; + + while ( raw_len ) { + if ( raw_len < sizeof ( *server ) ) { + DBGC ( dhcp, "DHCP %p malformed PXE server list\n", + dhcp ); + break; + } + server_len = offsetof ( typeof ( *server ), + ip[ server->num_ip ] ); + if ( raw_len < server_len ) { + DBGC ( dhcp, "DHCP %p malformed PXE server list\n", + dhcp ); + break; + } + if ( server->type == dhcp->pxe_type ) { + for ( i = 0 ; i < server->num_ip ; i++ ) + *(ip++) = server->ip[i]; + } + server = ( ( ( void * ) server ) + server_len ); + raw_len -= server_len; + } +} + +/** + * Start PXE Boot Server Discovery on a network device + * + * @v job Job control interface + * @v netdev Network device + * @v pxe_type PXE server type + * @ret rc Return status code + * + * Starts PXE Boot Server Discovery on the specified network device. + * If successful, the Boot Server ACK will be registered as an option + * source. + */ +int start_pxebs ( struct job_interface *job, struct net_device *netdev, + unsigned int pxe_type ) { + struct setting pxe_discovery_control_setting = + { .tag = DHCP_PXE_DISCOVERY_CONTROL }; + struct setting pxe_boot_servers_setting = + { .tag = DHCP_PXE_BOOT_SERVERS }; + struct setting pxe_boot_server_mcast_setting = + { .tag = DHCP_PXE_BOOT_SERVER_MCAST }; + ssize_t pxebs_list_len; + struct dhcp_session *dhcp; + struct in_addr *ip; + unsigned int pxe_discovery_control; + int rc; + + /* Get upper bound for PXE boot server IP address list */ + pxebs_list_len = fetch_setting_len ( NULL, &pxe_boot_servers_setting ); + if ( pxebs_list_len < 0 ) + pxebs_list_len = 0; + + /* Allocate and initialise structure */ + dhcp = zalloc ( sizeof ( *dhcp ) + sizeof ( *ip ) /* mcast */ + + sizeof ( *ip ) /* bcast */ + pxebs_list_len + + sizeof ( *ip ) /* terminator */ ); + if ( ! dhcp ) + return -ENOMEM; + dhcp->refcnt.free = dhcp_free; + job_init ( &dhcp->job, &dhcp_job_operations, &dhcp->refcnt ); + xfer_init ( &dhcp->xfer, &dhcp_xfer_operations, &dhcp->refcnt ); + dhcp->netdev = netdev_get ( netdev ); + dhcp->local.sin_family = AF_INET; + fetch_ipv4_setting ( netdev_settings ( netdev ), &ip_setting, + &dhcp->local.sin_addr ); + dhcp->local.sin_port = htons ( BOOTPC_PORT ); + dhcp->pxe_type = htons ( pxe_type ); + dhcp->timer.expired = dhcp_timer_expired; + + /* Construct PXE boot server IP address lists */ + pxe_discovery_control = + fetch_uintz_setting ( NULL, &pxe_discovery_control_setting ); + ip = ( ( ( void * ) dhcp ) + sizeof ( *dhcp ) ); + dhcp->pxe_attempt = ip; + if ( ! ( pxe_discovery_control & PXEBS_NO_MULTICAST ) ) { + fetch_ipv4_setting ( NULL, &pxe_boot_server_mcast_setting, ip); + if ( ip->s_addr ) + ip++; + } + if ( ! ( pxe_discovery_control & PXEBS_NO_BROADCAST ) ) + (ip++)->s_addr = INADDR_BROADCAST; + if ( pxe_discovery_control & PXEBS_NO_UNKNOWN_SERVERS ) + dhcp->pxe_accept = ip; + if ( pxebs_list_len ) { + uint8_t buf[pxebs_list_len]; + + fetch_setting ( NULL, &pxe_boot_servers_setting, + buf, sizeof ( buf ) ); + pxebs_list ( dhcp, buf, sizeof ( buf ), ip ); + } + if ( ! dhcp->pxe_attempt->s_addr ) { + DBGC ( dhcp, "DHCP %p has no PXE boot servers for type %04x\n", + dhcp, pxe_type ); + rc = -EINVAL; + goto err; + } + + /* Dump out PXE server lists */ + DBGC ( dhcp, "DHCP %p attempting", dhcp ); + for ( ip = dhcp->pxe_attempt ; ip->s_addr ; ip++ ) + DBGC ( dhcp, " %s", inet_ntoa ( *ip ) ); + DBGC ( dhcp, "\n" ); + if ( dhcp->pxe_accept ) { + DBGC ( dhcp, "DHCP %p accepting", dhcp ); + for ( ip = dhcp->pxe_accept ; ip->s_addr ; ip++ ) + DBGC ( dhcp, " %s", inet_ntoa ( *ip ) ); + DBGC ( dhcp, "\n" ); + } + + /* Instantiate child objects and attach to our interfaces */ + if ( ( rc = xfer_open_socket ( &dhcp->xfer, SOCK_DGRAM, &dhcp_peer, + ( struct sockaddr * ) &dhcp->local ) ) != 0 ) + goto err; + + /* Enter PXEBS state */ + dhcp_set_state ( dhcp, &dhcp_state_pxebs ); + + /* Attach parent interface, mortalise self, and return */ + job_plug_plug ( &dhcp->job, job ); + ref_put ( &dhcp->refcnt ); + return 0; + + err: + dhcp_finished ( dhcp, rc ); + ref_put ( &dhcp->refcnt ); + return rc; +} diff --git a/gpxe/src/net/udp/dns.c b/gpxe/src/net/udp/dns.c new file mode 100644 index 0000000..a498aef --- /dev/null +++ b/gpxe/src/net/udp/dns.c @@ -0,0 +1,601 @@ +/* + * Copyright (C) 2006 Michael Brown . + * + * Portions copyright (C) 2004 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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * DNS protocol + * + */ + +FEATURE ( FEATURE_PROTOCOL, "DNS", DHCP_EB_FEATURE_DNS, 1 ); + +/** The DNS server */ +static struct sockaddr_tcpip nameserver = { + .st_port = htons ( DNS_PORT ), +}; + +/** The local domain */ +static char *localdomain; + +/** A DNS request */ +struct dns_request { + /** Reference counter */ + struct refcnt refcnt; + /** Name resolution interface */ + struct resolv_interface resolv; + /** Data transfer interface */ + struct xfer_interface socket; + /** Retry timer */ + struct retry_timer timer; + + /** Socket address to fill in with resolved address */ + struct sockaddr sa; + /** Current query packet */ + struct dns_query query; + /** Location of query info structure within current packet + * + * The query info structure is located immediately after the + * compressed name. + */ + struct dns_query_info *qinfo; + /** Recursion counter */ + unsigned int recursion; +}; + +/** + * Mark DNS request as complete + * + * @v dns DNS request + * @v rc Return status code + */ +static void dns_done ( struct dns_request *dns, int rc ) { + + /* Stop the retry timer */ + stop_timer ( &dns->timer ); + + /* Close data transfer interface */ + xfer_nullify ( &dns->socket ); + xfer_close ( &dns->socket, rc ); + + /* Mark name resolution as complete */ + resolv_done ( &dns->resolv, &dns->sa, rc ); +} + +/** + * Compare DNS reply name against the query name from the original request + * + * @v dns DNS request + * @v reply DNS reply + * @v rname Reply name + * @ret zero Names match + * @ret non-zero Names do not match + */ +static int dns_name_cmp ( struct dns_request *dns, + const struct dns_header *reply, + const char *rname ) { + const char *qname = dns->query.payload; + int i; + + while ( 1 ) { + /* Obtain next section of rname */ + while ( ( *rname ) & 0xc0 ) { + rname = ( ( ( char * ) reply ) + + ( ntohs( *((uint16_t *)rname) ) & ~0xc000 )); + } + /* Check that lengths match */ + if ( *rname != *qname ) + return -1; + /* If length is zero, we have reached the end */ + if ( ! *qname ) + return 0; + /* Check that data matches */ + for ( i = *qname + 1; i > 0 ; i-- ) { + if ( *(rname++) != *(qname++) ) + return -1; + } + } +} + +/** + * Skip over a (possibly compressed) DNS name + * + * @v name DNS name + * @ret name Next DNS name + */ +static const char * dns_skip_name ( const char *name ) { + while ( 1 ) { + if ( ! *name ) { + /* End of name */ + return ( name + 1); + } + if ( *name & 0xc0 ) { + /* Start of a compressed name */ + return ( name + 2 ); + } + /* Uncompressed name portion */ + name += *name + 1; + } +} + +/** + * Find an RR in a reply packet corresponding to our query + * + * @v dns DNS request + * @v reply DNS reply + * @ret rr DNS RR, or NULL if not found + */ +static union dns_rr_info * dns_find_rr ( struct dns_request *dns, + const struct dns_header *reply ) { + int i, cmp; + const char *p = ( ( char * ) reply ) + sizeof ( struct dns_header ); + union dns_rr_info *rr_info; + + /* Skip over the questions section */ + for ( i = ntohs ( reply->qdcount ) ; i > 0 ; i-- ) { + p = dns_skip_name ( p ) + sizeof ( struct dns_query_info ); + } + + /* Process the answers section */ + for ( i = ntohs ( reply->ancount ) ; i > 0 ; i-- ) { + cmp = dns_name_cmp ( dns, reply, p ); + p = dns_skip_name ( p ); + rr_info = ( ( union dns_rr_info * ) p ); + if ( cmp == 0 ) + return rr_info; + p += ( sizeof ( rr_info->common ) + + ntohs ( rr_info->common.rdlength ) ); + } + + return NULL; +} + +/** + * Append DHCP domain name if available and name is not fully qualified + * + * @v string Name as a NUL-terminated string + * @ret fqdn Fully-qualified domain name, malloc'd copy + * + * The caller must free fqdn which is allocated even if the name is already + * fully qualified. + */ +static char * dns_qualify_name ( const char *string ) { + char *fqdn; + + /* Leave unchanged if already fully-qualified or no local domain */ + if ( ( ! localdomain ) || ( strchr ( string, '.' ) != 0 ) ) + return strdup ( string ); + + /* Append local domain to name */ + asprintf ( &fqdn, "%s.%s", string, localdomain ); + return fqdn; +} + +/** + * Convert a standard NUL-terminated string to a DNS name + * + * @v string Name as a NUL-terminated string + * @v buf Buffer in which to place DNS name + * @ret next Byte following constructed DNS name + * + * DNS names consist of "element" pairs. + */ +static char * dns_make_name ( const char *string, char *buf ) { + char *length_byte = buf++; + char c; + + while ( ( c = *(string++) ) ) { + if ( c == '.' ) { + *length_byte = buf - length_byte - 1; + length_byte = buf; + } + *(buf++) = c; + } + *length_byte = buf - length_byte - 1; + *(buf++) = '\0'; + return buf; +} + +/** + * Convert an uncompressed DNS name to a NUL-terminated string + * + * @v name DNS name + * @ret string NUL-terminated string + * + * Produce a printable version of a DNS name. Used only for debugging. + */ +static inline char * dns_unmake_name ( char *name ) { + char *p; + unsigned int len; + + p = name; + while ( ( len = *p ) ) { + *(p++) = '.'; + p += len; + } + + return name + 1; +} + +/** + * Decompress a DNS name + * + * @v reply DNS replay + * @v name DNS name + * @v buf Buffer into which to decompress DNS name + * @ret next Byte following decompressed DNS name + */ +static char * dns_decompress_name ( const struct dns_header *reply, + const char *name, char *buf ) { + int i, len; + + do { + /* Obtain next section of name */ + while ( ( *name ) & 0xc0 ) { + name = ( ( char * ) reply + + ( ntohs ( *((uint16_t *)name) ) & ~0xc000 ) ); + } + /* Copy data */ + len = *name; + for ( i = len + 1 ; i > 0 ; i-- ) { + *(buf++) = *(name++); + } + } while ( len ); + return buf; +} + +/** + * Send next packet in DNS request + * + * @v dns DNS request + */ +static int dns_send_packet ( struct dns_request *dns ) { + static unsigned int qid = 0; + size_t qlen; + + /* Increment query ID */ + dns->query.dns.id = htons ( ++qid ); + + DBGC ( dns, "DNS %p sending query ID %d\n", dns, qid ); + + /* Start retransmission timer */ + start_timer ( &dns->timer ); + + /* Send the data */ + qlen = ( ( ( void * ) dns->qinfo ) - ( ( void * ) &dns->query ) + + sizeof ( dns->qinfo ) ); + return xfer_deliver_raw ( &dns->socket, &dns->query, qlen ); +} + +/** + * Handle DNS retransmission timer expiry + * + * @v timer Retry timer + * @v fail Failure indicator + */ +static void dns_timer_expired ( struct retry_timer *timer, int fail ) { + struct dns_request *dns = + container_of ( timer, struct dns_request, timer ); + + if ( fail ) { + dns_done ( dns, -ETIMEDOUT ); + } else { + dns_send_packet ( dns ); + } +} + +/** + * Receive new data + * + * @v socket UDP socket + * @v data DNS reply + * @v len Length of DNS reply + * @ret rc Return status code + */ +static int dns_xfer_deliver_raw ( struct xfer_interface *socket, + const void *data, size_t len ) { + struct dns_request *dns = + container_of ( socket, struct dns_request, socket ); + const struct dns_header *reply = data; + union dns_rr_info *rr_info; + struct sockaddr_in *sin; + unsigned int qtype = dns->qinfo->qtype; + + /* Sanity check */ + if ( len < sizeof ( *reply ) ) { + DBGC ( dns, "DNS %p received underlength packet length %zd\n", + dns, len ); + return -EINVAL; + } + + /* Check reply ID matches query ID */ + if ( reply->id != dns->query.dns.id ) { + DBGC ( dns, "DNS %p received unexpected reply ID %d " + "(wanted %d)\n", dns, ntohs ( reply->id ), + ntohs ( dns->query.dns.id ) ); + return -EINVAL; + } + + DBGC ( dns, "DNS %p received reply ID %d\n", dns, ntohs ( reply->id )); + + /* Stop the retry timer. After this point, each code path + * must either restart the timer by calling dns_send_packet(), + * or mark the DNS operation as complete by calling + * dns_done() + */ + stop_timer ( &dns->timer ); + + /* Search through response for useful answers. Do this + * multiple times, to take advantage of useful nameservers + * which send us e.g. the CNAME *and* the A record for the + * pointed-to name. + */ + while ( ( rr_info = dns_find_rr ( dns, reply ) ) ) { + switch ( rr_info->common.type ) { + + case htons ( DNS_TYPE_A ): + + /* Found the target A record */ + DBGC ( dns, "DNS %p found address %s\n", + dns, inet_ntoa ( rr_info->a.in_addr ) ); + sin = ( struct sockaddr_in * ) &dns->sa; + sin->sin_family = AF_INET; + sin->sin_addr = rr_info->a.in_addr; + + /* Mark operation as complete */ + dns_done ( dns, 0 ); + return 0; + + case htons ( DNS_TYPE_CNAME ): + + /* Found a CNAME record; update query and recurse */ + DBGC ( dns, "DNS %p found CNAME\n", dns ); + dns->qinfo = ( void * ) dns_decompress_name ( reply, + rr_info->cname.cname, + dns->query.payload ); + dns->qinfo->qtype = htons ( DNS_TYPE_A ); + dns->qinfo->qclass = htons ( DNS_CLASS_IN ); + + /* Terminate the operation if we recurse too far */ + if ( ++dns->recursion > DNS_MAX_CNAME_RECURSION ) { + DBGC ( dns, "DNS %p recursion exceeded\n", + dns ); + dns_done ( dns, -ELOOP ); + return 0; + } + break; + + default: + DBGC ( dns, "DNS %p got unknown record type %d\n", + dns, ntohs ( rr_info->common.type ) ); + break; + } + } + + /* Determine what to do next based on the type of query we + * issued and the reponse we received + */ + switch ( qtype ) { + + case htons ( DNS_TYPE_A ): + /* We asked for an A record and got nothing; + * try the CNAME. + */ + DBGC ( dns, "DNS %p found no A record; trying CNAME\n", dns ); + dns->qinfo->qtype = htons ( DNS_TYPE_CNAME ); + dns_send_packet ( dns ); + return 0; + + case htons ( DNS_TYPE_CNAME ): + /* We asked for a CNAME record. If we got a response + * (i.e. if the next A query is already set up), then + * issue it, otherwise abort. + */ + if ( dns->qinfo->qtype == htons ( DNS_TYPE_A ) ) { + dns_send_packet ( dns ); + return 0; + } else { + DBGC ( dns, "DNS %p found no CNAME record\n", dns ); + dns_done ( dns, -ENXIO ); + return 0; + } + + default: + assert ( 0 ); + dns_done ( dns, -EINVAL ); + return 0; + } +} + +/** + * Receive new data + * + * @v socket UDP socket + * @v rc Reason for close + */ +static void dns_xfer_close ( struct xfer_interface *socket, int rc ) { + struct dns_request *dns = + container_of ( socket, struct dns_request, socket ); + + if ( ! rc ) + rc = -ECONNABORTED; + + dns_done ( dns, rc ); +} + +/** DNS socket operations */ +static struct xfer_interface_operations dns_socket_operations = { + .close = dns_xfer_close, + .vredirect = xfer_vopen, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = xfer_deliver_as_raw, + .deliver_raw = dns_xfer_deliver_raw, +}; + +/** + * Resolve name using DNS + * + * @v resolv Name resolution interface + * @v name Name to resolve + * @v sa Socket address to fill in + * @ret rc Return status code + */ +static int dns_resolv ( struct resolv_interface *resolv, + const char *name, struct sockaddr *sa ) { + struct dns_request *dns; + char *fqdn; + int rc; + + /* Fail immediately if no DNS servers */ + if ( ! nameserver.st_family ) { + DBG ( "DNS not attempting to resolve \"%s\": " + "no DNS servers\n", name ); + rc = -ENXIO; + goto err_no_nameserver; + } + + /* Ensure fully-qualified domain name if DHCP option was given */ + fqdn = dns_qualify_name ( name ); + if ( ! fqdn ) { + rc = -ENOMEM; + goto err_qualify_name; + } + + /* Allocate DNS structure */ + dns = zalloc ( sizeof ( *dns ) ); + if ( ! dns ) { + rc = -ENOMEM; + goto err_alloc_dns; + } + resolv_init ( &dns->resolv, &null_resolv_ops, &dns->refcnt ); + xfer_init ( &dns->socket, &dns_socket_operations, &dns->refcnt ); + dns->timer.expired = dns_timer_expired; + memcpy ( &dns->sa, sa, sizeof ( dns->sa ) ); + + /* Create query */ + dns->query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY | + DNS_FLAG_RD ); + dns->query.dns.qdcount = htons ( 1 ); + dns->qinfo = ( void * ) dns_make_name ( fqdn, dns->query.payload ); + dns->qinfo->qtype = htons ( DNS_TYPE_A ); + dns->qinfo->qclass = htons ( DNS_CLASS_IN ); + + /* Open UDP connection */ + if ( ( rc = xfer_open_socket ( &dns->socket, SOCK_DGRAM, + ( struct sockaddr * ) &nameserver, + NULL ) ) != 0 ) { + DBGC ( dns, "DNS %p could not open socket: %s\n", + dns, strerror ( rc ) ); + goto err_open_socket; + } + + /* Send first DNS packet */ + dns_send_packet ( dns ); + + /* Attach parent interface, mortalise self, and return */ + resolv_plug_plug ( &dns->resolv, resolv ); + ref_put ( &dns->refcnt ); + free ( fqdn ); + return 0; + + err_open_socket: + err_alloc_dns: + ref_put ( &dns->refcnt ); + err_qualify_name: + free ( fqdn ); + err_no_nameserver: + return rc; +} + +/** DNS name resolver */ +struct resolver dns_resolver __resolver ( RESOLV_NORMAL ) = { + .name = "DNS", + .resolv = dns_resolv, +}; + +/****************************************************************************** + * + * Settings + * + ****************************************************************************** + */ + +/** DNS server setting */ +struct setting dns_setting __setting = { + .name = "dns", + .description = "DNS server", + .tag = DHCP_DNS_SERVERS, + .type = &setting_type_ipv4, +}; + +/** Domain name setting */ +struct setting domain_setting __setting = { + .name = "domain", + .description = "Local domain", + .tag = DHCP_DOMAIN_NAME, + .type = &setting_type_string, +}; + +/** + * Apply DNS settings + * + * @ret rc Return status code + */ +static int apply_dns_settings ( void ) { + struct sockaddr_in *sin_nameserver = + ( struct sockaddr_in * ) &nameserver; + int len; + + if ( ( len = fetch_ipv4_setting ( NULL, &dns_setting, + &sin_nameserver->sin_addr ) ) >= 0 ){ + sin_nameserver->sin_family = AF_INET; + DBG ( "DNS using nameserver %s\n", + inet_ntoa ( sin_nameserver->sin_addr ) ); + } + + /* Get local domain DHCP option */ + if ( ( len = fetch_string_setting_copy ( NULL, &domain_setting, + &localdomain ) ) >= 0 ) + DBG ( "DNS local domain %s\n", localdomain ); + + return 0; +} + +/** DNS settings applicator */ +struct settings_applicator dns_applicator __settings_applicator = { + .apply = apply_dns_settings, +}; diff --git a/gpxe/src/net/udp/slam.c b/gpxe/src/net/udp/slam.c new file mode 100644 index 0000000..6add99b --- /dev/null +++ b/gpxe/src/net/udp/slam.c @@ -0,0 +1,810 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Scalable Local Area Multicast protocol + * + * The SLAM protocol is supported only by Etherboot; it was designed + * and implemented by Eric Biederman. A server implementation is + * available in contrib/mini-slamd. There does not appear to be any + * documentation beyond a few sparse comments in Etherboot's + * proto_slam.c. + * + * SLAM packets use three types of data field: + * + * Nul : A single NUL (0) byte, used as a list terminator + * + * Raw : A block of raw data + * + * Int : A variable-length integer, in big-endian order. The length + * of the integer is encoded in the most significant three bits. + * + * Packets received by the client have the following layout: + * + * Int : Transaction identifier. This is an opaque value. + * + * Int : Total number of bytes in the transfer. + * + * Int : Block size, in bytes. + * + * Int : Packet sequence number within the transfer (if this packet + * contains data). + * + * Raw : Packet data (if this packet contains data). + * + * Packets transmitted by the client consist of a run-length-encoded + * representation of the received-blocks bitmap, looking something + * like: + * + * Int : Number of consecutive successfully-received packets + * Int : Number of consecutive missing packets + * Int : Number of consecutive successfully-received packets + * Int : Number of consecutive missing packets + * .... + * Nul + * + */ + +FEATURE ( FEATURE_PROTOCOL, "SLAM", DHCP_EB_FEATURE_SLAM, 1 ); + +/** Default SLAM server port */ +#define SLAM_DEFAULT_PORT 10000 + +/** Default SLAM multicast IP address */ +#define SLAM_DEFAULT_MULTICAST_IP \ + ( ( 239 << 24 ) | ( 255 << 16 ) | ( 1 << 8 ) | ( 1 << 0 ) ) + +/** Default SLAM multicast port */ +#define SLAM_DEFAULT_MULTICAST_PORT 10000 + +/** Maximum SLAM header length */ +#define SLAM_MAX_HEADER_LEN ( 7 /* transaction id */ + 7 /* total_bytes */ + \ + 7 /* block_size */ ) + +/** Maximum number of blocks to request per NACK + * + * This is a policy decision equivalent to selecting a TCP window + * size. + */ +#define SLAM_MAX_BLOCKS_PER_NACK 4 + +/** Maximum SLAM NACK length + * + * We only ever send a NACK for a single range of up to @c + * SLAM_MAX_BLOCKS_PER_NACK blocks. + */ +#define SLAM_MAX_NACK_LEN ( 7 /* block */ + 7 /* #blocks */ + 1 /* NUL */ ) + +/** SLAM slave timeout */ +#define SLAM_SLAVE_TIMEOUT ( 1 * TICKS_PER_SEC ) + +/** A SLAM request */ +struct slam_request { + /** Reference counter */ + struct refcnt refcnt; + + /** Data transfer interface */ + struct xfer_interface xfer; + /** Unicast socket */ + struct xfer_interface socket; + /** Multicast socket */ + struct xfer_interface mc_socket; + + /** Master client retry timer */ + struct retry_timer master_timer; + /** Slave client retry timer */ + struct retry_timer slave_timer; + + /** Cached header */ + uint8_t header[SLAM_MAX_HEADER_LEN]; + /** Size of cached header */ + size_t header_len; + /** Total number of bytes in transfer */ + unsigned long total_bytes; + /** Transfer block size */ + unsigned long block_size; + /** Number of blocks in transfer */ + unsigned long num_blocks; + /** Block bitmap */ + struct bitmap bitmap; + /** NACK sent flag */ + int nack_sent; +}; + +/** + * Free a SLAM request + * + * @v refcnt Reference counter + */ +static void slam_free ( struct refcnt *refcnt ) { + struct slam_request *slam = + container_of ( refcnt, struct slam_request, refcnt ); + + bitmap_free ( &slam->bitmap ); + free ( slam ); +} + +/** + * Mark SLAM request as complete + * + * @v slam SLAM request + * @v rc Return status code + */ +static void slam_finished ( struct slam_request *slam, int rc ) { + static const uint8_t slam_disconnect[] = { 0 }; + + DBGC ( slam, "SLAM %p finished with status code %d (%s)\n", + slam, rc, strerror ( rc ) ); + + /* Send a disconnect message if we ever sent anything to the + * server. + */ + if ( slam->nack_sent ) { + xfer_deliver_raw ( &slam->socket, slam_disconnect, + sizeof ( slam_disconnect ) ); + } + + /* Stop the retry timers */ + stop_timer ( &slam->master_timer ); + stop_timer ( &slam->slave_timer ); + + /* Close all data transfer interfaces */ + xfer_nullify ( &slam->socket ); + xfer_close ( &slam->socket, rc ); + xfer_nullify ( &slam->mc_socket ); + xfer_close ( &slam->mc_socket, rc ); + xfer_nullify ( &slam->xfer ); + xfer_close ( &slam->xfer, rc ); +} + +/**************************************************************************** + * + * TX datapath + * + */ + +/** + * Add a variable-length value to a SLAM packet + * + * @v slam SLAM request + * @v iobuf I/O buffer + * @v value Value to add + * @ret rc Return status code + * + * Adds a variable-length value to the end of an I/O buffer. Will + * always leave at least one byte of tailroom in the I/O buffer (to + * allow space for the terminating NUL). + */ +static int slam_put_value ( struct slam_request *slam, + struct io_buffer *iobuf, unsigned long value ) { + uint8_t *data; + size_t len; + unsigned int i; + + /* Calculate variable length required to store value. Always + * leave at least one byte in the I/O buffer. + */ + len = ( ( flsl ( value ) + 10 ) / 8 ); + if ( len >= iob_tailroom ( iobuf ) ) { + DBGC2 ( slam, "SLAM %p cannot add %zd-byte value\n", + slam, len ); + return -ENOBUFS; + } + /* There is no valid way within the protocol that we can end + * up trying to push a full-sized long (i.e. without space for + * the length encoding). + */ + assert ( len <= sizeof ( value ) ); + + /* Add value */ + data = iob_put ( iobuf, len ); + for ( i = len ; i-- ; ) { + data[i] = value; + value >>= 8; + } + *data |= ( len << 5 ); + assert ( value == 0 ); + + return 0; +} + +/** + * Send SLAM NACK packet + * + * @v slam SLAM request + * @ret rc Return status code + */ +static int slam_tx_nack ( struct slam_request *slam ) { + struct io_buffer *iobuf; + unsigned long first_block; + unsigned long num_blocks; + uint8_t *nul; + int rc; + + /* Mark NACK as sent, so that we know we have to disconnect later */ + slam->nack_sent = 1; + + /* Allocate I/O buffer */ + iobuf = xfer_alloc_iob ( &slam->socket, SLAM_MAX_NACK_LEN ); + if ( ! iobuf ) { + DBGC ( slam, "SLAM %p could not allocate I/O buffer\n", + slam ); + return -ENOMEM; + } + + /* Construct NACK. We always request only a single packet; + * this allows us to force multicast-TFTP-style flow control + * on the SLAM server, which will otherwise just blast the + * data out as fast as it can. On a gigabit network, without + * RX checksumming, this would inevitably cause packet drops. + */ + first_block = bitmap_first_gap ( &slam->bitmap ); + for ( num_blocks = 1 ; ; num_blocks++ ) { + if ( num_blocks >= SLAM_MAX_BLOCKS_PER_NACK ) + break; + if ( ( first_block + num_blocks ) >= slam->num_blocks ) + break; + if ( bitmap_test ( &slam->bitmap, + ( first_block + num_blocks ) ) ) + break; + } + if ( first_block ) { + DBGCP ( slam, "SLAM %p transmitting NACK for blocks " + "%ld-%ld\n", slam, first_block, + ( first_block + num_blocks - 1 ) ); + } else { + DBGC ( slam, "SLAM %p transmitting initial NACK for blocks " + "0-%ld\n", slam, ( num_blocks - 1 ) ); + } + if ( ( rc = slam_put_value ( slam, iobuf, first_block ) ) != 0 ) + return rc; + if ( ( rc = slam_put_value ( slam, iobuf, num_blocks ) ) != 0 ) + return rc; + nul = iob_put ( iobuf, 1 ); + *nul = 0; + + /* Transmit packet */ + return xfer_deliver_iob ( &slam->socket, iobuf ); +} + +/** + * Handle SLAM master client retry timer expiry + * + * @v timer Master retry timer + * @v fail Failure indicator + */ +static void slam_master_timer_expired ( struct retry_timer *timer, + int fail ) { + struct slam_request *slam = + container_of ( timer, struct slam_request, master_timer ); + + if ( fail ) { + /* Allow timer to stop running. We will terminate the + * connection only if the slave timer times out. + */ + DBGC ( slam, "SLAM %p giving up acting as master client\n", + slam ); + } else { + /* Retransmit NACK */ + start_timer ( timer ); + slam_tx_nack ( slam ); + } +} + +/** + * Handle SLAM slave client retry timer expiry + * + * @v timer Master retry timer + * @v fail Failure indicator + */ +static void slam_slave_timer_expired ( struct retry_timer *timer, + int fail ) { + struct slam_request *slam = + container_of ( timer, struct slam_request, slave_timer ); + + if ( fail ) { + /* Terminate connection */ + slam_finished ( slam, -ETIMEDOUT ); + } else { + /* Try sending a NACK */ + DBGC ( slam, "SLAM %p trying to become master client\n", + slam ); + start_timer ( timer ); + slam_tx_nack ( slam ); + } +} + +/**************************************************************************** + * + * RX datapath + * + */ + +/** + * Read and strip a variable-length value from a SLAM packet + * + * @v slam SLAM request + * @v iobuf I/O buffer + * @v value Value to fill in, or NULL to ignore value + * @ret rc Return status code + * + * Reads a variable-length value from the start of the I/O buffer. + */ +static int slam_pull_value ( struct slam_request *slam, + struct io_buffer *iobuf, + unsigned long *value ) { + uint8_t *data; + size_t len; + + /* Sanity check */ + if ( iob_len ( iobuf ) == 0 ) { + DBGC ( slam, "SLAM %p empty value\n", slam ); + return -EINVAL; + } + + /* Read and verify length of value */ + data = iobuf->data; + len = ( *data >> 5 ); + if ( ( len == 0 ) || + ( value && ( len > sizeof ( *value ) ) ) ) { + DBGC ( slam, "SLAM %p invalid value length %zd bytes\n", + slam, len ); + return -EINVAL; + } + if ( len > iob_len ( iobuf ) ) { + DBGC ( slam, "SLAM %p value extends beyond I/O buffer\n", + slam ); + return -EINVAL; + } + + /* Read value */ + iob_pull ( iobuf, len ); + *value = ( *data & 0x1f ); + while ( --len ) { + *value <<= 8; + *value |= *(++data); + } + + return 0; +} + +/** + * Read and strip SLAM header + * + * @v slam SLAM request + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int slam_pull_header ( struct slam_request *slam, + struct io_buffer *iobuf ) { + void *header = iobuf->data; + int rc; + + /* If header matches cached header, just pull it and return */ + if ( ( slam->header_len <= iob_len ( iobuf ) ) && + ( memcmp ( slam->header, iobuf->data, slam->header_len ) == 0 )){ + iob_pull ( iobuf, slam->header_len ); + return 0; + } + + DBGC ( slam, "SLAM %p detected changed header; resetting\n", slam ); + + /* Read and strip transaction ID, total number of bytes, and + * block size. + */ + if ( ( rc = slam_pull_value ( slam, iobuf, NULL ) ) != 0 ) + return rc; + if ( ( rc = slam_pull_value ( slam, iobuf, + &slam->total_bytes ) ) != 0 ) + return rc; + if ( ( rc = slam_pull_value ( slam, iobuf, + &slam->block_size ) ) != 0 ) + return rc; + + /* Update the cached header */ + slam->header_len = ( iobuf->data - header ); + assert ( slam->header_len <= sizeof ( slam->header ) ); + memcpy ( slam->header, header, slam->header_len ); + + /* Calculate number of blocks */ + slam->num_blocks = ( ( slam->total_bytes + slam->block_size - 1 ) / + slam->block_size ); + + DBGC ( slam, "SLAM %p has total bytes %ld, block size %ld, num " + "blocks %ld\n", slam, slam->total_bytes, slam->block_size, + slam->num_blocks ); + + /* Discard and reset the bitmap */ + bitmap_free ( &slam->bitmap ); + memset ( &slam->bitmap, 0, sizeof ( slam->bitmap ) ); + + /* Allocate a new bitmap */ + if ( ( rc = bitmap_resize ( &slam->bitmap, + slam->num_blocks ) ) != 0 ) { + /* Failure to allocate a bitmap is fatal */ + DBGC ( slam, "SLAM %p could not allocate bitmap for %ld " + "blocks: %s\n", slam, slam->num_blocks, + strerror ( rc ) ); + slam_finished ( slam, rc ); + return rc; + } + + /* Notify recipient of file size */ + xfer_seek ( &slam->xfer, slam->total_bytes, SEEK_SET ); + + return 0; +} + +/** + * Receive SLAM data packet + * + * @v mc_socket SLAM multicast socket + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int slam_mc_socket_deliver ( struct xfer_interface *mc_socket, + struct io_buffer *iobuf, + struct xfer_metadata *rx_meta __unused ) { + struct slam_request *slam = + container_of ( mc_socket, struct slam_request, mc_socket ); + struct xfer_metadata meta; + unsigned long packet; + size_t len; + int rc; + + /* Stop the master client timer. Restart the slave client timer. */ + stop_timer ( &slam->master_timer ); + stop_timer ( &slam->slave_timer ); + start_timer_fixed ( &slam->slave_timer, SLAM_SLAVE_TIMEOUT ); + + /* Read and strip packet header */ + if ( ( rc = slam_pull_header ( slam, iobuf ) ) != 0 ) + goto err_discard; + + /* Read and strip packet number */ + if ( ( rc = slam_pull_value ( slam, iobuf, &packet ) ) != 0 ) + goto err_discard; + + /* Sanity check packet number */ + if ( packet >= slam->num_blocks ) { + DBGC ( slam, "SLAM %p received out-of-range packet %ld " + "(num_blocks=%ld)\n", slam, packet, slam->num_blocks ); + rc = -EINVAL; + goto err_discard; + } + + /* Sanity check length */ + len = iob_len ( iobuf ); + if ( len > slam->block_size ) { + DBGC ( slam, "SLAM %p received oversize packet of %zd bytes " + "(block_size=%ld)\n", slam, len, slam->block_size ); + rc = -EINVAL; + goto err_discard; + } + if ( ( packet != ( slam->num_blocks - 1 ) ) && + ( len < slam->block_size ) ) { + DBGC ( slam, "SLAM %p received short packet of %zd bytes " + "(block_size=%ld)\n", slam, len, slam->block_size ); + rc = -EINVAL; + goto err_discard; + } + + /* If we have already seen this packet, discard it */ + if ( bitmap_test ( &slam->bitmap, packet ) ) { + goto discard; + } + + /* Pass to recipient */ + memset ( &meta, 0, sizeof ( meta ) ); + meta.whence = SEEK_SET; + meta.offset = ( packet * slam->block_size ); + if ( ( rc = xfer_deliver_iob_meta ( &slam->xfer, iobuf, + &meta ) ) != 0 ) + goto err; + + /* Mark block as received */ + bitmap_set ( &slam->bitmap, packet ); + + /* If we have received all blocks, terminate */ + if ( bitmap_full ( &slam->bitmap ) ) + slam_finished ( slam, 0 ); + + return 0; + + err_discard: + discard: + free_iob ( iobuf ); + err: + return rc; +} + +/** + * Receive SLAM non-data packet + * + * @v socket SLAM unicast socket + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int slam_socket_deliver ( struct xfer_interface *socket, + struct io_buffer *iobuf, + struct xfer_metadata *rx_meta __unused ) { + struct slam_request *slam = + container_of ( socket, struct slam_request, socket ); + int rc; + + /* Restart the master client timer */ + stop_timer ( &slam->master_timer ); + start_timer ( &slam->master_timer ); + + /* Read and strip packet header */ + if ( ( rc = slam_pull_header ( slam, iobuf ) ) != 0 ) + goto discard; + + /* Sanity check */ + if ( iob_len ( iobuf ) != 0 ) { + DBGC ( slam, "SLAM %p received trailing garbage:\n", slam ); + DBGC_HD ( slam, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto discard; + } + + /* Discard packet */ + free_iob ( iobuf ); + + /* Send NACK in reply */ + slam_tx_nack ( slam ); + + return 0; + + discard: + free_iob ( iobuf ); + return rc; + +} + +/** + * Close SLAM unicast socket + * + * @v socket SLAM unicast socket + * @v rc Reason for close + */ +static void slam_socket_close ( struct xfer_interface *socket, int rc ) { + struct slam_request *slam = + container_of ( socket, struct slam_request, socket ); + + DBGC ( slam, "SLAM %p unicast socket closed: %s\n", + slam, strerror ( rc ) ); + + slam_finished ( slam, rc ); +} + +/** SLAM unicast socket data transfer operations */ +static struct xfer_interface_operations slam_socket_operations = { + .close = slam_socket_close, + .vredirect = xfer_vopen, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = slam_socket_deliver, + .deliver_raw = xfer_deliver_as_iob, +}; + +/** + * Close SLAM multicast socket + * + * @v mc_socket SLAM multicast socket + * @v rc Reason for close + */ +static void slam_mc_socket_close ( struct xfer_interface *mc_socket, int rc ){ + struct slam_request *slam = + container_of ( mc_socket, struct slam_request, mc_socket ); + + DBGC ( slam, "SLAM %p multicast socket closed: %s\n", + slam, strerror ( rc ) ); + + slam_finished ( slam, rc ); +} + +/** SLAM multicast socket data transfer operations */ +static struct xfer_interface_operations slam_mc_socket_operations = { + .close = slam_mc_socket_close, + .vredirect = xfer_vopen, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = slam_mc_socket_deliver, + .deliver_raw = xfer_deliver_as_iob, +}; + +/**************************************************************************** + * + * Data transfer interface + * + */ + +/** + * Close SLAM data transfer interface + * + * @v xfer SLAM data transfer interface + * @v rc Reason for close + */ +static void slam_xfer_close ( struct xfer_interface *xfer, int rc ) { + struct slam_request *slam = + container_of ( xfer, struct slam_request, xfer ); + + DBGC ( slam, "SLAM %p data transfer interface closed: %s\n", + slam, strerror ( rc ) ); + + slam_finished ( slam, rc ); +} + +/** SLAM data transfer operations */ +static struct xfer_interface_operations slam_xfer_operations = { + .close = slam_xfer_close, + .vredirect = ignore_xfer_vredirect, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = xfer_deliver_as_raw, + .deliver_raw = ignore_xfer_deliver_raw, +}; + +/** + * Parse SLAM URI multicast address + * + * @v slam SLAM request + * @v path Path portion of x-slam:// URI + * @v address Socket address to fill in + * @ret rc Return status code + */ +static int slam_parse_multicast_address ( struct slam_request *slam, + const char *path, + struct sockaddr_in *address ) { + char path_dup[ strlen ( path ) /* no +1 */ ]; + char *sep; + char *end; + + /* Create temporary copy of path, minus the leading '/' */ + assert ( *path == '/' ); + memcpy ( path_dup, ( path + 1 ) , sizeof ( path_dup ) ); + + /* Parse port, if present */ + sep = strchr ( path_dup, ':' ); + if ( sep ) { + *(sep++) = '\0'; + address->sin_port = htons ( strtoul ( sep, &end, 0 ) ); + if ( *end != '\0' ) { + DBGC ( slam, "SLAM %p invalid multicast port " + "\"%s\"\n", slam, sep ); + return -EINVAL; + } + } + + /* Parse address */ + if ( inet_aton ( path_dup, &address->sin_addr ) == 0 ) { + DBGC ( slam, "SLAM %p invalid multicast address \"%s\"\n", + slam, path_dup ); + return -EINVAL; + } + + return 0; +} + +/** + * Initiate a SLAM request + * + * @v xfer Data transfer interface + * @v uri Uniform Resource Identifier + * @ret rc Return status code + */ +static int slam_open ( struct xfer_interface *xfer, struct uri *uri ) { + static const struct sockaddr_in default_multicast = { + .sin_family = AF_INET, + .sin_port = htons ( SLAM_DEFAULT_MULTICAST_PORT ), + .sin_addr = { htonl ( SLAM_DEFAULT_MULTICAST_IP ) }, + }; + struct slam_request *slam; + struct sockaddr_tcpip server; + struct sockaddr_in multicast; + int rc; + + /* Sanity checks */ + if ( ! uri->host ) + return -EINVAL; + + /* Allocate and populate structure */ + slam = zalloc ( sizeof ( *slam ) ); + if ( ! slam ) + return -ENOMEM; + slam->refcnt.free = slam_free; + xfer_init ( &slam->xfer, &slam_xfer_operations, &slam->refcnt ); + xfer_init ( &slam->socket, &slam_socket_operations, &slam->refcnt ); + xfer_init ( &slam->mc_socket, &slam_mc_socket_operations, + &slam->refcnt ); + slam->master_timer.expired = slam_master_timer_expired; + slam->slave_timer.expired = slam_slave_timer_expired; + /* Fake an invalid cached header of { 0x00, ... } */ + slam->header_len = 1; + /* Fake parameters for initial NACK */ + slam->num_blocks = 1; + if ( ( rc = bitmap_resize ( &slam->bitmap, 1 ) ) != 0 ) { + DBGC ( slam, "SLAM %p could not allocate initial bitmap: " + "%s\n", slam, strerror ( rc ) ); + goto err; + } + + /* Open unicast socket */ + memset ( &server, 0, sizeof ( server ) ); + server.st_port = htons ( uri_port ( uri, SLAM_DEFAULT_PORT ) ); + if ( ( rc = xfer_open_named_socket ( &slam->socket, SOCK_DGRAM, + ( struct sockaddr * ) &server, + uri->host, NULL ) ) != 0 ) { + DBGC ( slam, "SLAM %p could not open unicast socket: %s\n", + slam, strerror ( rc ) ); + goto err; + } + + /* Open multicast socket */ + memcpy ( &multicast, &default_multicast, sizeof ( multicast ) ); + if ( uri->path && + ( ( rc = slam_parse_multicast_address ( slam, uri->path, + &multicast ) ) != 0 ) ) { + goto err; + } + if ( ( rc = xfer_open_socket ( &slam->mc_socket, SOCK_DGRAM, + ( struct sockaddr * ) &multicast, + ( struct sockaddr * ) &multicast ) ) != 0 ) { + DBGC ( slam, "SLAM %p could not open multicast socket: %s\n", + slam, strerror ( rc ) ); + goto err; + } + + /* Start slave retry timer */ + start_timer_fixed ( &slam->slave_timer, SLAM_SLAVE_TIMEOUT ); + + /* Attach to parent interface, mortalise self, and return */ + xfer_plug_plug ( &slam->xfer, xfer ); + ref_put ( &slam->refcnt ); + return 0; + + err: + slam_finished ( slam, rc ); + ref_put ( &slam->refcnt ); + return rc; +} + +/** SLAM URI opener */ +struct uri_opener slam_uri_opener __uri_opener = { + .scheme = "x-slam", + .open = slam_open, +}; diff --git a/gpxe/src/net/udp/tftp.c b/gpxe/src/net/udp/tftp.c new file mode 100644 index 0000000..19525f7 --- /dev/null +++ b/gpxe/src/net/udp/tftp.c @@ -0,0 +1,1187 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * TFTP protocol + * + */ + +FEATURE ( FEATURE_PROTOCOL, "TFTP", DHCP_EB_FEATURE_TFTP, 1 ); + +/* TFTP-specific error codes */ +#define ETFTP_INVALID_BLKSIZE EUNIQ_01 +#define ETFTP_INVALID_TSIZE EUNIQ_02 +#define ETFTP_MC_NO_PORT EUNIQ_03 +#define ETFTP_MC_NO_MC EUNIQ_04 +#define ETFTP_MC_INVALID_MC EUNIQ_05 +#define ETFTP_MC_INVALID_IP EUNIQ_06 +#define ETFTP_MC_INVALID_PORT EUNIQ_07 + +/** + * A TFTP request + * + * This data structure holds the state for an ongoing TFTP transfer. + */ +struct tftp_request { + /** Reference count */ + struct refcnt refcnt; + /** Data transfer interface */ + struct xfer_interface xfer; + + /** URI being fetched */ + struct uri *uri; + /** Transport layer interface */ + struct xfer_interface socket; + /** Multicast transport layer interface */ + struct xfer_interface mc_socket; + + /** Data block size + * + * This is the "blksize" option negotiated with the TFTP + * server. (If the TFTP server does not support TFTP options, + * this will default to 512). + */ + unsigned int blksize; + /** File size + * + * This is the value returned in the "tsize" option from the + * TFTP server. If the TFTP server does not support the + * "tsize" option, this value will be zero. + */ + unsigned long tsize; + + /** Server port + * + * This is the port to which RRQ packets are sent. + */ + unsigned int port; + /** Peer address + * + * The peer address is determined by the first response + * received to the TFTP RRQ. + */ + struct sockaddr_tcpip peer; + /** Request flags */ + unsigned int flags; + /** MTFTP timeout count */ + unsigned int mtftp_timeouts; + + /** Block bitmap */ + struct bitmap bitmap; + /** Maximum known length + * + * We don't always know the file length in advance. In + * particular, if the TFTP server doesn't support the tsize + * option, or we are using MTFTP, then we don't know the file + * length until we see the end-of-file block (which, in the + * case of MTFTP, may not be the last block we see). + * + * This value is updated whenever we obtain information about + * the file length. + */ + size_t filesize; + /** Retransmission timer */ + struct retry_timer timer; +}; + +/** TFTP request flags */ +enum { + /** Send ACK packets */ + TFTP_FL_SEND_ACK = 0x0001, + /** Request blksize and tsize options */ + TFTP_FL_RRQ_SIZES = 0x0002, + /** Request multicast option */ + TFTP_FL_RRQ_MULTICAST = 0x0004, + /** Perform MTFTP recovery on timeout */ + TFTP_FL_MTFTP_RECOVERY = 0x0008, +}; + +/** Maximum number of MTFTP open requests before falling back to TFTP */ +#define MTFTP_MAX_TIMEOUTS 3 + +/** + * Free TFTP request + * + * @v refcnt Reference counter + */ +static void tftp_free ( struct refcnt *refcnt ) { + struct tftp_request *tftp = + container_of ( refcnt, struct tftp_request, refcnt ); + + uri_put ( tftp->uri ); + bitmap_free ( &tftp->bitmap ); + free ( tftp ); +} + +/** + * Mark TFTP request as complete + * + * @v tftp TFTP connection + * @v rc Return status code + */ +static void tftp_done ( struct tftp_request *tftp, int rc ) { + + DBGC ( tftp, "TFTP %p finished with status %d (%s)\n", + tftp, rc, strerror ( rc ) ); + + /* Stop the retry timer */ + stop_timer ( &tftp->timer ); + + /* Close all data transfer interfaces */ + xfer_nullify ( &tftp->socket ); + xfer_close ( &tftp->socket, rc ); + xfer_nullify ( &tftp->mc_socket ); + xfer_close ( &tftp->mc_socket, rc ); + xfer_nullify ( &tftp->xfer ); + xfer_close ( &tftp->xfer, rc ); +} + +/** + * Reopen TFTP socket + * + * @v tftp TFTP connection + * @ret rc Return status code + */ +static int tftp_reopen ( struct tftp_request *tftp ) { + struct sockaddr_tcpip server; + int rc; + + /* Close socket */ + xfer_close ( &tftp->socket, 0 ); + + /* Disable ACK sending. */ + tftp->flags &= ~TFTP_FL_SEND_ACK; + + /* Reset peer address */ + memset ( &tftp->peer, 0, sizeof ( tftp->peer ) ); + + /* Open socket */ + memset ( &server, 0, sizeof ( server ) ); + server.st_port = htons ( tftp->port ); + if ( ( rc = xfer_open_named_socket ( &tftp->socket, SOCK_DGRAM, + ( struct sockaddr * ) &server, + tftp->uri->host, NULL ) ) != 0 ) { + DBGC ( tftp, "TFTP %p could not open socket: %s\n", + tftp, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Reopen TFTP multicast socket + * + * @v tftp TFTP connection + * @v local Local socket address + * @ret rc Return status code + */ +static int tftp_reopen_mc ( struct tftp_request *tftp, + struct sockaddr *local ) { + int rc; + + /* Close multicast socket */ + xfer_close ( &tftp->mc_socket, 0 ); + + /* Open multicast socket. We never send via this socket, so + * use the local address as the peer address (since the peer + * address cannot be NULL). + */ + if ( ( rc = xfer_open_socket ( &tftp->mc_socket, SOCK_DGRAM, + local, local ) ) != 0 ) { + DBGC ( tftp, "TFTP %p could not open multicast " + "socket: %s\n", tftp, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Presize TFTP receive buffers and block bitmap + * + * @v tftp TFTP connection + * @v filesize Known minimum file size + * @ret rc Return status code + */ +static int tftp_presize ( struct tftp_request *tftp, size_t filesize ) { + unsigned int num_blocks; + int rc; + + /* Do nothing if we are already large enough */ + if ( filesize <= tftp->filesize ) + return 0; + + /* Record filesize */ + tftp->filesize = filesize; + + /* Notify recipient of file size */ + xfer_seek ( &tftp->xfer, filesize, SEEK_SET ); + xfer_seek ( &tftp->xfer, 0, SEEK_SET ); + + /* Calculate expected number of blocks. Note that files whose + * length is an exact multiple of the blocksize will have a + * trailing zero-length block, which must be included. + */ + num_blocks = ( ( filesize / tftp->blksize ) + 1 ); + if ( ( rc = bitmap_resize ( &tftp->bitmap, num_blocks ) ) != 0 ) { + DBGC ( tftp, "TFTP %p could not resize bitmap to %d blocks: " + "%s\n", tftp, num_blocks, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * TFTP requested blocksize + * + * This is treated as a global configuration parameter. + */ +static unsigned int tftp_request_blksize = TFTP_MAX_BLKSIZE; + +/** + * Set TFTP request blocksize + * + * @v blksize Requested block size + */ +void tftp_set_request_blksize ( unsigned int blksize ) { + if ( blksize < TFTP_DEFAULT_BLKSIZE ) + blksize = TFTP_DEFAULT_BLKSIZE; + tftp_request_blksize = blksize; +} + +/** + * MTFTP multicast receive address + * + * This is treated as a global configuration parameter. + */ +static struct sockaddr_in tftp_mtftp_socket = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl ( 0xefff0101 ), + .sin_port = htons ( 3001 ), +}; + +/** + * Set MTFTP multicast address + * + * @v address Multicast IPv4 address + */ +void tftp_set_mtftp_address ( struct in_addr address ) { + tftp_mtftp_socket.sin_addr = address; +} + +/** + * Set MTFTP multicast port + * + * @v port Multicast port + */ +void tftp_set_mtftp_port ( unsigned int port ) { + tftp_mtftp_socket.sin_port = htons ( port ); +} + +/** + * Transmit RRQ + * + * @v tftp TFTP connection + * @ret rc Return status code + */ +static int tftp_send_rrq ( struct tftp_request *tftp ) { + struct tftp_rrq *rrq; + const char *path; + size_t len; + struct io_buffer *iobuf; + + /* Strip initial '/' if present. If we were opened via the + * URI interface, then there will be an initial '/', since a + * full tftp:// URI provides no way to specify a non-absolute + * path. However, many TFTP servers (particularly Windows + * TFTP servers) complain about having an initial '/', and it + * violates user expectations to have a '/' silently added to + * the DHCP-specified filename. + */ + path = tftp->uri->path; + if ( *path == '/' ) + path++; + + DBGC ( tftp, "TFTP %p requesting \"%s\"\n", tftp, path ); + + /* Allocate buffer */ + len = ( sizeof ( *rrq ) + strlen ( path ) + 1 /* NUL */ + + 5 + 1 /* "octet" + NUL */ + + 7 + 1 + 5 + 1 /* "blksize" + NUL + ddddd + NUL */ + + 5 + 1 + 1 + 1 /* "tsize" + NUL + "0" + NUL */ + + 9 + 1 + 1 /* "multicast" + NUL + NUL */ ); + iobuf = xfer_alloc_iob ( &tftp->socket, len ); + if ( ! iobuf ) + return -ENOMEM; + + /* Build request */ + rrq = iob_put ( iobuf, sizeof ( *rrq ) ); + rrq->opcode = htons ( TFTP_RRQ ); + iob_put ( iobuf, snprintf ( iobuf->tail, iob_tailroom ( iobuf ), + "%s%coctet", path, 0 ) + 1 ); + if ( tftp->flags & TFTP_FL_RRQ_SIZES ) { + iob_put ( iobuf, snprintf ( iobuf->tail, + iob_tailroom ( iobuf ), + "blksize%c%d%ctsize%c0", 0, + tftp_request_blksize, 0, 0 ) + 1 ); + } + if ( tftp->flags & TFTP_FL_RRQ_MULTICAST ) { + iob_put ( iobuf, snprintf ( iobuf->tail, + iob_tailroom ( iobuf ), + "multicast%c", 0 ) + 1 ); + } + + /* RRQ always goes to the address specified in the initial + * xfer_open() call + */ + return xfer_deliver_iob ( &tftp->socket, iobuf ); +} + +/** + * Transmit ACK + * + * @v tftp TFTP connection + * @ret rc Return status code + */ +static int tftp_send_ack ( struct tftp_request *tftp ) { + struct tftp_ack *ack; + struct io_buffer *iobuf; + struct xfer_metadata meta = { + .dest = ( struct sockaddr * ) &tftp->peer, + }; + unsigned int block; + + /* Determine next required block number */ + block = bitmap_first_gap ( &tftp->bitmap ); + DBGC2 ( tftp, "TFTP %p sending ACK for block %d\n", tftp, block ); + + /* Allocate buffer */ + iobuf = xfer_alloc_iob ( &tftp->socket, sizeof ( *ack ) ); + if ( ! iobuf ) + return -ENOMEM; + + /* Build ACK */ + ack = iob_put ( iobuf, sizeof ( *ack ) ); + ack->opcode = htons ( TFTP_ACK ); + ack->block = htons ( block ); + + /* ACK always goes to the peer recorded from the RRQ response */ + return xfer_deliver_iob_meta ( &tftp->socket, iobuf, &meta ); +} + +/** + * Transmit next relevant packet + * + * @v tftp TFTP connection + * @ret rc Return status code + */ +static int tftp_send_packet ( struct tftp_request *tftp ) { + + /* Update retransmission timer */ + stop_timer ( &tftp->timer ); + start_timer ( &tftp->timer ); + + /* Send RRQ or ACK as appropriate */ + if ( ! tftp->peer.st_family ) { + return tftp_send_rrq ( tftp ); + } else { + if ( tftp->flags & TFTP_FL_SEND_ACK ) { + return tftp_send_ack ( tftp ); + } else { + return 0; + } + } +} + +/** + * Handle TFTP retransmission timer expiry + * + * @v timer Retry timer + * @v fail Failure indicator + */ +static void tftp_timer_expired ( struct retry_timer *timer, int fail ) { + struct tftp_request *tftp = + container_of ( timer, struct tftp_request, timer ); + int rc; + + /* If we are doing MTFTP, attempt the various recovery strategies */ + if ( tftp->flags & TFTP_FL_MTFTP_RECOVERY ) { + if ( tftp->peer.st_family ) { + /* If we have received any response from the server, + * try resending the RRQ to restart the download. + */ + DBGC ( tftp, "TFTP %p attempting reopen\n", tftp ); + if ( ( rc = tftp_reopen ( tftp ) ) != 0 ) + goto err; + } else { + /* Fall back to plain TFTP after several attempts */ + tftp->mtftp_timeouts++; + DBGC ( tftp, "TFTP %p timeout %d waiting for MTFTP " + "open\n", tftp, tftp->mtftp_timeouts ); + + if ( tftp->mtftp_timeouts > MTFTP_MAX_TIMEOUTS ) { + DBGC ( tftp, "TFTP %p falling back to plain " + "TFTP\n", tftp ); + tftp->flags = TFTP_FL_RRQ_SIZES; + + /* Close multicast socket */ + xfer_close ( &tftp->mc_socket, 0 ); + + /* Reset retry timer */ + start_timer_nodelay ( &tftp->timer ); + + /* The blocksize may change: discard + * the block bitmap + */ + bitmap_free ( &tftp->bitmap ); + memset ( &tftp->bitmap, 0, + sizeof ( tftp->bitmap ) ); + + /* Reopen on standard TFTP port */ + tftp->port = TFTP_PORT; + if ( ( rc = tftp_reopen ( tftp ) ) != 0 ) + goto err; + } + } + } else { + /* Not doing MTFTP (or have fallen back to plain + * TFTP); fail as per normal. + */ + if ( fail ) { + rc = -ETIMEDOUT; + goto err; + } + } + tftp_send_packet ( tftp ); + return; + + err: + tftp_done ( tftp, rc ); +} + +/** + * Process TFTP "blksize" option + * + * @v tftp TFTP connection + * @v value Option value + * @ret rc Return status code + */ +static int tftp_process_blksize ( struct tftp_request *tftp, + const char *value ) { + char *end; + + tftp->blksize = strtoul ( value, &end, 10 ); + if ( *end ) { + DBGC ( tftp, "TFTP %p got invalid blksize \"%s\"\n", + tftp, value ); + return -( EINVAL | ETFTP_INVALID_BLKSIZE ); + } + DBGC ( tftp, "TFTP %p blksize=%d\n", tftp, tftp->blksize ); + + return 0; +} + +/** + * Process TFTP "tsize" option + * + * @v tftp TFTP connection + * @v value Option value + * @ret rc Return status code + */ +static int tftp_process_tsize ( struct tftp_request *tftp, + const char *value ) { + char *end; + + tftp->tsize = strtoul ( value, &end, 10 ); + if ( *end ) { + DBGC ( tftp, "TFTP %p got invalid tsize \"%s\"\n", + tftp, value ); + return -( EINVAL | ETFTP_INVALID_TSIZE ); + } + DBGC ( tftp, "TFTP %p tsize=%ld\n", tftp, tftp->tsize ); + + return 0; +} + +/** + * Process TFTP "multicast" option + * + * @v tftp TFTP connection + * @v value Option value + * @ret rc Return status code + */ +static int tftp_process_multicast ( struct tftp_request *tftp, + const char *value ) { + union { + struct sockaddr sa; + struct sockaddr_in sin; + } socket; + char buf[ strlen ( value ) + 1 ]; + char *addr; + char *port; + char *port_end; + char *mc; + char *mc_end; + int rc; + + /* Split value into "addr,port,mc" fields */ + memcpy ( buf, value, sizeof ( buf ) ); + addr = buf; + port = strchr ( addr, ',' ); + if ( ! port ) { + DBGC ( tftp, "TFTP %p multicast missing port,mc\n", tftp ); + return -( EINVAL | ETFTP_MC_NO_PORT ); + } + *(port++) = '\0'; + mc = strchr ( port, ',' ); + if ( ! mc ) { + DBGC ( tftp, "TFTP %p multicast missing mc\n", tftp ); + return -( EINVAL | ETFTP_MC_NO_MC ); + } + *(mc++) = '\0'; + + /* Parse parameters */ + if ( strtoul ( mc, &mc_end, 0 ) == 0 ) + tftp->flags &= ~TFTP_FL_SEND_ACK; + if ( *mc_end ) { + DBGC ( tftp, "TFTP %p multicast invalid mc %s\n", tftp, mc ); + return -( EINVAL | ETFTP_MC_INVALID_MC ); + } + DBGC ( tftp, "TFTP %p is%s the master client\n", + tftp, ( ( tftp->flags & TFTP_FL_SEND_ACK ) ? "" : " not" ) ); + if ( *addr && *port ) { + socket.sin.sin_family = AF_INET; + if ( inet_aton ( addr, &socket.sin.sin_addr ) == 0 ) { + DBGC ( tftp, "TFTP %p multicast invalid IP address " + "%s\n", tftp, addr ); + return -( EINVAL | ETFTP_MC_INVALID_IP ); + } + DBGC ( tftp, "TFTP %p multicast IP address %s\n", + tftp, inet_ntoa ( socket.sin.sin_addr ) ); + socket.sin.sin_port = htons ( strtoul ( port, &port_end, 0 ) ); + if ( *port_end ) { + DBGC ( tftp, "TFTP %p multicast invalid port %s\n", + tftp, port ); + return -( EINVAL | ETFTP_MC_INVALID_PORT ); + } + DBGC ( tftp, "TFTP %p multicast port %d\n", + tftp, ntohs ( socket.sin.sin_port ) ); + if ( ( rc = tftp_reopen_mc ( tftp, &socket.sa ) ) != 0 ) + return rc; + } + + return 0; +} + +/** A TFTP option */ +struct tftp_option { + /** Option name */ + const char *name; + /** Option processor + * + * @v tftp TFTP connection + * @v value Option value + * @ret rc Return status code + */ + int ( * process ) ( struct tftp_request *tftp, const char *value ); +}; + +/** Recognised TFTP options */ +static struct tftp_option tftp_options[] = { + { "blksize", tftp_process_blksize }, + { "tsize", tftp_process_tsize }, + { "multicast", tftp_process_multicast }, + { NULL, NULL } +}; + +/** + * Process TFTP option + * + * @v tftp TFTP connection + * @v name Option name + * @v value Option value + * @ret rc Return status code + */ +static int tftp_process_option ( struct tftp_request *tftp, + const char *name, const char *value ) { + struct tftp_option *option; + + for ( option = tftp_options ; option->name ; option++ ) { + if ( strcasecmp ( name, option->name ) == 0 ) + return option->process ( tftp, value ); + } + + DBGC ( tftp, "TFTP %p received unknown option \"%s\" = \"%s\"\n", + tftp, name, value ); + + /* Unknown options should be silently ignored */ + return 0; +} + +/** + * Receive OACK + * + * @v tftp TFTP connection + * @v buf Temporary data buffer + * @v len Length of temporary data buffer + * @ret rc Return status code + */ +static int tftp_rx_oack ( struct tftp_request *tftp, void *buf, size_t len ) { + struct tftp_oack *oack = buf; + char *end = buf + len; + char *name; + char *value; + int rc = 0; + + /* Sanity check */ + if ( len < sizeof ( *oack ) ) { + DBGC ( tftp, "TFTP %p received underlength OACK packet " + "length %zd\n", tftp, len ); + rc = -EINVAL; + goto done; + } + if ( end[-1] != '\0' ) { + DBGC ( tftp, "TFTP %p received OACK missing final NUL\n", + tftp ); + rc = -EINVAL; + goto done; + } + + /* Process each option in turn */ + name = oack->data; + while ( name < end ) { + value = ( name + strlen ( name ) + 1 ); + if ( value == end ) { + DBGC ( tftp, "TFTP %p received OACK missing value " + "for option \"%s\"\n", tftp, name ); + rc = -EINVAL; + goto done; + } + if ( ( rc = tftp_process_option ( tftp, name, value ) ) != 0 ) + goto done; + name = ( value + strlen ( value ) + 1 ); + } + + /* Process tsize information, if available */ + if ( tftp->tsize ) { + if ( ( rc = tftp_presize ( tftp, tftp->tsize ) ) != 0 ) + goto done; + } + + /* Request next data block */ + tftp_send_packet ( tftp ); + + done: + if ( rc ) + tftp_done ( tftp, rc ); + return rc; +} + +/** + * Receive DATA + * + * @v tftp TFTP connection + * @v iobuf I/O buffer + * @ret rc Return status code + * + * Takes ownership of I/O buffer. + */ +static int tftp_rx_data ( struct tftp_request *tftp, + struct io_buffer *iobuf ) { + struct tftp_data *data = iobuf->data; + struct xfer_metadata meta; + int block; + off_t offset; + size_t data_len; + int rc; + + /* Sanity check */ + if ( iob_len ( iobuf ) < sizeof ( *data ) ) { + DBGC ( tftp, "TFTP %p received underlength DATA packet " + "length %zd\n", tftp, iob_len ( iobuf ) ); + rc = -EINVAL; + goto done; + } + if ( data->block == 0 ) { + DBGC ( tftp, "TFTP %p received data block 0\n", tftp ); + rc = -EINVAL; + goto done; + } + + /* Extract data */ + block = ( ntohs ( data->block ) - 1 ); + offset = ( block * tftp->blksize ); + iob_pull ( iobuf, sizeof ( *data ) ); + data_len = iob_len ( iobuf ); + if ( data_len > tftp->blksize ) { + DBGC ( tftp, "TFTP %p received overlength DATA packet " + "length %zd\n", tftp, data_len ); + rc = -EINVAL; + goto done; + } + + /* Deliver data */ + memset ( &meta, 0, sizeof ( meta ) ); + meta.whence = SEEK_SET; + meta.offset = offset; + if ( ( rc = xfer_deliver_iob_meta ( &tftp->xfer, iob_disown ( iobuf ), + &meta ) ) != 0 ) { + DBGC ( tftp, "TFTP %p could not deliver data: %s\n", + tftp, strerror ( rc ) ); + goto done; + } + + /* Ensure block bitmap is ready */ + if ( ( rc = tftp_presize ( tftp, ( offset + data_len ) ) ) != 0 ) + goto done; + + /* Mark block as received */ + bitmap_set ( &tftp->bitmap, block ); + + /* Acknowledge block */ + tftp_send_packet ( tftp ); + + /* If all blocks have been received, finish. */ + if ( bitmap_full ( &tftp->bitmap ) ) + tftp_done ( tftp, 0 ); + + done: + free_iob ( iobuf ); + if ( rc ) + tftp_done ( tftp, rc ); + return rc; +} + +/** Translation between TFTP errors and internal error numbers */ +static const int tftp_errors[] = { + [TFTP_ERR_FILE_NOT_FOUND] = ENOENT, + [TFTP_ERR_ACCESS_DENIED] = EACCES, + [TFTP_ERR_ILLEGAL_OP] = ENOTSUP, +}; + +/** + * Receive ERROR + * + * @v tftp TFTP connection + * @v buf Temporary data buffer + * @v len Length of temporary data buffer + * @ret rc Return status code + */ +static int tftp_rx_error ( struct tftp_request *tftp, void *buf, size_t len ) { + struct tftp_error *error = buf; + unsigned int err; + int rc = 0; + + /* Sanity check */ + if ( len < sizeof ( *error ) ) { + DBGC ( tftp, "TFTP %p received underlength ERROR packet " + "length %zd\n", tftp, len ); + return -EINVAL; + } + + DBGC ( tftp, "TFTP %p received ERROR packet with code %d, message " + "\"%s\"\n", tftp, ntohs ( error->errcode ), error->errmsg ); + + /* Determine final operation result */ + err = ntohs ( error->errcode ); + if ( err < ( sizeof ( tftp_errors ) / sizeof ( tftp_errors[0] ) ) ) + rc = -tftp_errors[err]; + if ( ! rc ) + rc = -ENOTSUP; + + /* Close TFTP request */ + tftp_done ( tftp, rc ); + + return 0; +} + +/** + * Receive new data + * + * @v tftp TFTP connection + * @v iobuf I/O buffer + * @v meta Transfer metadata + * @ret rc Return status code + */ +static int tftp_rx ( struct tftp_request *tftp, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct sockaddr_tcpip *st_src; + struct tftp_common *common = iobuf->data; + size_t len = iob_len ( iobuf ); + int rc = -EINVAL; + + /* Sanity checks */ + if ( len < sizeof ( *common ) ) { + DBGC ( tftp, "TFTP %p received underlength packet length " + "%zd\n", tftp, len ); + goto done; + } + if ( ! meta->src ) { + DBGC ( tftp, "TFTP %p received packet without source port\n", + tftp ); + goto done; + } + + /* Filter by TID. Set TID on first response received */ + st_src = ( struct sockaddr_tcpip * ) meta->src; + if ( ! tftp->peer.st_family ) { + memcpy ( &tftp->peer, st_src, sizeof ( tftp->peer ) ); + DBGC ( tftp, "TFTP %p using remote port %d\n", tftp, + ntohs ( tftp->peer.st_port ) ); + } else if ( memcmp ( &tftp->peer, st_src, + sizeof ( tftp->peer ) ) != 0 ) { + DBGC ( tftp, "TFTP %p received packet from wrong source (got " + "%d, wanted %d)\n", tftp, ntohs ( st_src->st_port ), + ntohs ( tftp->peer.st_port ) ); + goto done; + } + + switch ( common->opcode ) { + case htons ( TFTP_OACK ): + rc = tftp_rx_oack ( tftp, iobuf->data, len ); + break; + case htons ( TFTP_DATA ): + rc = tftp_rx_data ( tftp, iob_disown ( iobuf ) ); + break; + case htons ( TFTP_ERROR ): + rc = tftp_rx_error ( tftp, iobuf->data, len ); + break; + default: + DBGC ( tftp, "TFTP %p received strange packet type %d\n", + tftp, ntohs ( common->opcode ) ); + break; + }; + + done: + free_iob ( iobuf ); + return rc; +} + +/** + * Receive new data via socket + * + * @v socket Transport layer interface + * @v iobuf I/O buffer + * @v meta Transfer metadata + * @ret rc Return status code + */ +static int tftp_socket_deliver_iob ( struct xfer_interface *socket, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct tftp_request *tftp = + container_of ( socket, struct tftp_request, socket ); + + /* Enable sending ACKs when we receive a unicast packet. This + * covers three cases: + * + * 1. Standard TFTP; we should always send ACKs, and will + * always receive a unicast packet before we need to send the + * first ACK. + * + * 2. RFC2090 multicast TFTP; the only unicast packets we will + * receive are the OACKs; enable sending ACKs here (before + * processing the OACK) and disable it when processing the + * multicast option if we are not the master client. + * + * 3. MTFTP; receiving a unicast datagram indicates that we + * are the "master client" and should send ACKs. + */ + tftp->flags |= TFTP_FL_SEND_ACK; + + return tftp_rx ( tftp, iobuf, meta ); +} + +/** TFTP socket operations */ +static struct xfer_interface_operations tftp_socket_operations = { + .close = ignore_xfer_close, + .vredirect = xfer_vopen, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = tftp_socket_deliver_iob, + .deliver_raw = xfer_deliver_as_iob, +}; + +/** + * Receive new data via multicast socket + * + * @v mc_socket Multicast transport layer interface + * @v iobuf I/O buffer + * @v meta Transfer metadata + * @ret rc Return status code + */ +static int tftp_mc_socket_deliver_iob ( struct xfer_interface *mc_socket, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct tftp_request *tftp = + container_of ( mc_socket, struct tftp_request, mc_socket ); + + return tftp_rx ( tftp, iobuf, meta ); +} + +/** TFTP multicast socket operations */ +static struct xfer_interface_operations tftp_mc_socket_operations = { + .close = ignore_xfer_close, + .vredirect = xfer_vopen, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = tftp_mc_socket_deliver_iob, + .deliver_raw = xfer_deliver_as_iob, +}; + +/** + * Close TFTP data transfer interface + * + * @v xfer Data transfer interface + * @v rc Reason for close + */ +static void tftp_xfer_close ( struct xfer_interface *xfer, int rc ) { + struct tftp_request *tftp = + container_of ( xfer, struct tftp_request, xfer ); + + DBGC ( tftp, "TFTP %p interface closed: %s\n", + tftp, strerror ( rc ) ); + + tftp_done ( tftp, rc ); +} + +/** + * Check flow control window + * + * @v xfer Data transfer interface + * @ret len Length of window + */ +static size_t tftp_xfer_window ( struct xfer_interface *xfer ) { + struct tftp_request *tftp = + container_of ( xfer, struct tftp_request, xfer ); + + /* We abuse this data-xfer method to convey the blocksize to + * the caller. This really should be done using some kind of + * stat() method, but we don't yet have the facility to do + * that. + */ + return tftp->blksize; +} + +/** TFTP data transfer interface operations */ +static struct xfer_interface_operations tftp_xfer_operations = { + .close = tftp_xfer_close, + .vredirect = ignore_xfer_vredirect, + .window = tftp_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = xfer_deliver_as_raw, + .deliver_raw = ignore_xfer_deliver_raw, +}; + +/** + * Initiate TFTP/TFTM/MTFTP download + * + * @v xfer Data transfer interface + * @v uri Uniform Resource Identifier + * @ret rc Return status code + */ +static int tftp_core_open ( struct xfer_interface *xfer, struct uri *uri, + unsigned int default_port, + struct sockaddr *multicast, + unsigned int flags ) { + struct tftp_request *tftp; + int rc; + + /* Sanity checks */ + if ( ! uri->host ) + return -EINVAL; + if ( ! uri->path ) + return -EINVAL; + + /* Allocate and populate TFTP structure */ + tftp = zalloc ( sizeof ( *tftp ) ); + if ( ! tftp ) + return -ENOMEM; + tftp->refcnt.free = tftp_free; + xfer_init ( &tftp->xfer, &tftp_xfer_operations, &tftp->refcnt ); + tftp->uri = uri_get ( uri ); + xfer_init ( &tftp->socket, &tftp_socket_operations, &tftp->refcnt ); + xfer_init ( &tftp->mc_socket, &tftp_mc_socket_operations, + &tftp->refcnt ); + tftp->blksize = TFTP_DEFAULT_BLKSIZE; + tftp->flags = flags; + tftp->timer.expired = tftp_timer_expired; + + /* Open socket */ + tftp->port = uri_port ( tftp->uri, default_port ); + if ( ( rc = tftp_reopen ( tftp ) ) != 0 ) + goto err; + + /* Open multicast socket */ + if ( multicast ) { + if ( ( rc = tftp_reopen_mc ( tftp, multicast ) ) != 0 ) + goto err; + } + + /* Start timer to initiate RRQ */ + start_timer_nodelay ( &tftp->timer ); + + /* Attach to parent interface, mortalise self, and return */ + xfer_plug_plug ( &tftp->xfer, xfer ); + ref_put ( &tftp->refcnt ); + return 0; + + err: + DBGC ( tftp, "TFTP %p could not create request: %s\n", + tftp, strerror ( rc ) ); + tftp_done ( tftp, rc ); + ref_put ( &tftp->refcnt ); + return rc; +} + +/** + * Initiate TFTP download + * + * @v xfer Data transfer interface + * @v uri Uniform Resource Identifier + * @ret rc Return status code + */ +static int tftp_open ( struct xfer_interface *xfer, struct uri *uri ) { + return tftp_core_open ( xfer, uri, TFTP_PORT, NULL, + TFTP_FL_RRQ_SIZES ); + +} + +/** TFTP URI opener */ +struct uri_opener tftp_uri_opener __uri_opener = { + .scheme = "tftp", + .open = tftp_open, +}; + +/** + * Initiate TFTM download + * + * @v xfer Data transfer interface + * @v uri Uniform Resource Identifier + * @ret rc Return status code + */ +static int tftm_open ( struct xfer_interface *xfer, struct uri *uri ) { + return tftp_core_open ( xfer, uri, TFTP_PORT, NULL, + ( TFTP_FL_RRQ_SIZES | + TFTP_FL_RRQ_MULTICAST ) ); + +} + +/** TFTM URI opener */ +struct uri_opener tftm_uri_opener __uri_opener = { + .scheme = "tftm", + .open = tftm_open, +}; + +/** + * Initiate MTFTP download + * + * @v xfer Data transfer interface + * @v uri Uniform Resource Identifier + * @ret rc Return status code + */ +static int mtftp_open ( struct xfer_interface *xfer, struct uri *uri ) { + return tftp_core_open ( xfer, uri, MTFTP_PORT, + ( struct sockaddr * ) &tftp_mtftp_socket, + TFTP_FL_MTFTP_RECOVERY ); +} + +/** MTFTP URI opener */ +struct uri_opener mtftp_uri_opener __uri_opener = { + .scheme = "mtftp", + .open = mtftp_open, +}; + +/****************************************************************************** + * + * Settings + * + ****************************************************************************** + */ + +/** TFTP server setting */ +struct setting next_server_setting __setting = { + .name = "next-server", + .description = "TFTP server", + .tag = DHCP_EB_SIADDR, + .type = &setting_type_ipv4, +}; + +/** + * Apply TFTP configuration settings + * + * @ret rc Return status code + */ +static int tftp_apply_settings ( void ) { + static struct in_addr tftp_server = { 0 }; + struct in_addr last_tftp_server; + char uri_string[32]; + struct uri *uri; + + /* Retrieve TFTP server setting */ + last_tftp_server = tftp_server; + fetch_ipv4_setting ( NULL, &next_server_setting, &tftp_server ); + + /* If TFTP server setting has changed, set the current working + * URI to match. Do it only when the TFTP server has changed + * to try to minimise surprises to the user, who probably + * won't expect the CWURI to change just because they updated + * an unrelated setting and triggered all the settings + * applicators. + */ + if ( tftp_server.s_addr != last_tftp_server.s_addr ) { + snprintf ( uri_string, sizeof ( uri_string ), + "tftp://%s/", inet_ntoa ( tftp_server ) ); + uri = parse_uri ( uri_string ); + if ( ! uri ) + return -ENOMEM; + churi ( uri ); + uri_put ( uri ); + } + + return 0; +} + +/** TFTP settings applicator */ +struct settings_applicator tftp_settings_applicator __settings_applicator = { + .apply = tftp_apply_settings, +}; diff --git a/gpxe/src/proto/fsp.c b/gpxe/src/proto/fsp.c new file mode 100644 index 0000000..9a10991 --- /dev/null +++ b/gpxe/src/proto/fsp.c @@ -0,0 +1,243 @@ + /*********************************************************************\ + * Copyright (c) 2005 by Radim Kolar (hsn-sendmail.cz) * + * * + * You may copy or modify this file in any manner you wish, provided * + * that this notice is always included, and that you hold the author * + * harmless for any loss or damage resulting from the installation or * + * use of this software. * + * * + * This file provides support for FSP v2 protocol written from scratch * + * by Radim Kolar, FSP project leader. * + * * + * ABOUT FSP * + * FSP is a lightweight file transfer protocol and is being used for * + * booting, Internet firmware updates, embedded devices and in * + * wireless applications. FSP is very easy to implement; contact Radim * + * Kolar if you need hand optimized assembler FSP stacks for various * + * microcontrollers, CPUs or consultations. * + * http://fsp.sourceforge.net/ * + * * + * REVISION HISTORY * + * 1.0 2005-03-17 rkolar Initial coding * + * 1.1 2005-03-24 rkolar We really need to send CC_BYE to the server * + * at end of transfer, because next stage boot * + * loader is unable to contact FSP server * + * until session timeouts. * + * 1.2 2005-03-26 rkolar We need to query filesize in advance, * + * because NBI loader do not reads file until * + * eof is reached. + * REMARKS * + * there is no support for selecting port number of fsp server, maybe * + * we should parse fsp:// URLs in boot image filename. * + * this implementation has filename limit 255 chars. * + \*********************************************************************/ + +#ifdef DOWNLOAD_PROTO_FSP + +#define FSP_PORT 21 + +/* FSP commands */ +#define CC_GET_FILE 0x42 +#define CC_BYE 0x4A +#define CC_ERR 0x40 +#define CC_STAT 0x4D + +/* etherboot limits */ +#define FSP_MAXFILENAME 255 + +struct fsp_info { + in_addr server_ip; + uint16_t server_port; + uint16_t local_port; + const char *filename; + int (*fnc)(unsigned char *, unsigned int, unsigned int, int); +}; + +struct fsp_header { + uint8_t cmd; + uint8_t sum; + uint16_t key; + uint16_t seq; + uint16_t len; + uint32_t pos; +} PACKED; + +#define FSP_MAXPAYLOAD (ETH_MAX_MTU - \ + (sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct fsp_header))) + +static struct fsp_request { + struct iphdr ip; + struct udphdr udp; + struct fsp_header fsp; + unsigned char data[FSP_MAXFILENAME + 1 + 2]; +} request; + +struct fsp_reply { + struct iphdr ip; + struct udphdr udp; + struct fsp_header fsp; + unsigned char data[FSP_MAXPAYLOAD]; +} PACKED; + + +static int await_fsp(int ival, void *ptr, unsigned short ptype __unused, + struct iphdr *ip, struct udphdr *udp) +{ + if(!udp) + return 0; + if (ip->dest.s_addr != arptable[ARP_CLIENT].ipaddr.s_addr) + return 0; + if (ntohs(udp->dest) != ival) + return 0; + if (ntohs(udp->len) < 12+sizeof(struct udphdr)) + return 0; + return 1; +} + +static int proto_fsp(struct fsp_info *info) +{ + uint32_t filepos; + uint32_t filelength=0; + int i,retry; + uint16_t reqlen; + struct fsp_reply *reply; + int block=1; + + /* prepare FSP request packet */ + filepos=0; + i=strlen(info->filename); + if(i>FSP_MAXFILENAME) + { + printf("Boot filename is too long.\n"); + return 0; + } + strcpy(request.data,info->filename); + *(uint16_t *)(request.data+i+1)=htons(FSP_MAXPAYLOAD); + request.fsp.len=htons(i+1); + reqlen=i+3+12; + + rx_qdrain(); + retry=0; + + /* main loop */ + for(;;) { + int sum; + long timeout; + + /* query filelength if not known */ + if(filelength == 0) + request.fsp.cmd=CC_STAT; + + /* prepare request packet */ + request.fsp.pos=htonl(filepos); + request.fsp.seq=random(); + request.fsp.sum=0; + for(i=0,sum=reqlen;i> 8); + /* send request */ + if (!udp_transmit(info->server_ip.s_addr, info->local_port, + info->server_port, sizeof(request.ip) + + sizeof(request.udp) + reqlen, &request)) + return (0); + /* wait for retry */ +#ifdef CONGESTED + timeout = + rfc2131_sleep_interval(filepos ? TFTP_REXMT : TIMEOUT, retry); +#else + timeout = rfc2131_sleep_interval(TIMEOUT, retry); +#endif + retry++; + if (!await_reply(await_fsp, info->local_port, NULL, timeout)) + continue; + reply=(struct fsp_reply *) &nic.packet[ETH_HLEN]; + /* check received packet */ + if (reply->fsp.seq != request.fsp.seq) + continue; + reply->udp.len=ntohs(reply->udp.len)-sizeof(struct udphdr); + if(reply->udp.len < ntohs(reply->fsp.len) + 12 ) + continue; + sum=-reply->fsp.sum; + for(i=0;iudp.len;i++) + { + sum += ((uint8_t *)&(reply->fsp))[i]; + } + sum = (sum + (sum >> 8)) & 0xff; + if(sum != reply->fsp.sum) + { + printf("FSP checksum failed. computed %d, but packet has %d.\n",sum,reply->fsp.sum); + continue; + } + if(reply->fsp.cmd == CC_ERR) + { + printf("\nFSP error: %s",info->filename); + if(reply->fsp.len) + printf(" [%s]",reply->data); + printf("\n"); + return 0; + } + if(reply->fsp.cmd == CC_BYE && filelength == 1) + { + info->fnc(request.data,block,1,1); + return 1; + } + if(reply->fsp.cmd == CC_STAT) + { + if(reply->data[8] == 0) + { + /* file not found, etc. */ + filelength=0xffffffff; + } else + { + filelength= ntohl(*((uint32_t *)&reply->data[4])); + } + request.fsp.cmd = CC_GET_FILE; + request.fsp.key = reply->fsp.key; + retry=0; + continue; + } + + if(reply->fsp.cmd == CC_GET_FILE) + { + if(ntohl(reply->fsp.pos) != filepos) + continue; + request.fsp.key = reply->fsp.key; + retry=0; + i=ntohs(reply->fsp.len); + if(i == 1) + { + request.fsp.cmd=CC_BYE; + request.data[0]=reply->data[0]; + continue; + } + /* let last byte alone */ + if(i >= filelength) + i = filelength - 1; + if(!info->fnc(reply->data,block++,i,0)) + return 0; + filepos += i; + filelength -= i; + } + } + + return 0; +} + +int url_fsp(const char *name, int (*fnc)(unsigned char *, unsigned int, unsigned int, int)) +{ + struct fsp_info info; + /* Set the defaults */ + info.server_ip.s_addr = arptable[ARP_SERVER].ipaddr.s_addr; + info.server_port = FSP_PORT; + info.local_port = 1024 + random() & 0xfbff; + info.fnc = fnc; + + /* Now parse the url */ + /* printf("fsp-URI: [%s]\n", name); */ + /* quick hack for now */ + info.filename=name; + return proto_fsp(&info); +} +#endif diff --git a/gpxe/src/proto/igmp.c b/gpxe/src/proto/igmp.c new file mode 100644 index 0000000..d711421 --- /dev/null +++ b/gpxe/src/proto/igmp.c @@ -0,0 +1,167 @@ +/* + * Eric Biederman wrote this code originally. + * + */ + +#if 0 + +#include +#include + +static unsigned long last_igmpv1 = 0; +static struct igmptable_t igmptable[MAX_IGMP]; + +static long rfc1112_sleep_interval ( long base, int exp ) { + unsigned long divisor, tmo; + + if ( exp > BACKOFF_LIMIT ) + exp = BACKOFF_LIMIT; + divisor = RAND_MAX / ( base << exp ); + tmo = random() / divisor; + return tmo; +} + +static void send_igmp_reports ( unsigned long now ) { + struct igmp_ip_t igmp; + int i; + + for ( i = 0 ; i < MAX_IGMP ; i++ ) { + if ( ! igmptable[i].time ) + continue; + if ( now < igmptable[i].time ) + continue; + + igmp.router_alert[0] = 0x94; + igmp.router_alert[1] = 0x04; + igmp.router_alert[2] = 0; + igmp.router_alert[3] = 0; + build_ip_hdr ( igmptable[i].group.s_addr, 1, IP_IGMP, + sizeof ( igmp.router_alert ), + sizeof ( igmp ), &igmp ); + igmp.igmp.type = IGMPv2_REPORT; + if ( last_igmpv1 && + ( now < last_igmpv1 + IGMPv1_ROUTER_PRESENT_TIMEOUT ) ) { + igmp.igmp.type = IGMPv1_REPORT; + } + igmp.igmp.response_time = 0; + igmp.igmp.chksum = 0; + igmp.igmp.group.s_addr = igmptable[i].group.s_addr; + igmp.igmp.chksum = ipchksum ( &igmp.igmp, + sizeof ( igmp.igmp ) ); + ip_transmit ( sizeof ( igmp ), &igmp ); + DBG ( "IGMP sent report to %s\n", inet_ntoa ( igmp.igmp.group ) ); + /* Don't send another igmp report until asked */ + igmptable[i].time = 0; + } +} + +static void process_igmp ( unsigned long now, unsigned short ptype __unused, + struct iphdr *ip ) { + struct igmp *igmp; + int i; + unsigned iplen; + + if ( ( ! ip ) || ( ip->protocol != IP_IGMP ) || + ( nic.packetlen < ( sizeof ( struct iphdr ) + + sizeof ( struct igmp ) ) ) ) { + return; + } + + iplen = ( ip->verhdrlen & 0xf ) * 4; + igmp = ( struct igmp * ) &nic.packet[ sizeof( struct iphdr ) ]; + if ( ipchksum ( igmp, ntohs ( ip->len ) - iplen ) != 0 ) + return; + + if ( ( igmp->type == IGMP_QUERY ) && + ( ip->dest.s_addr == htonl ( GROUP_ALL_HOSTS ) ) ) { + unsigned long interval = IGMP_INTERVAL; + + if ( igmp->response_time == 0 ) { + last_igmpv1 = now; + } else { + interval = ( igmp->response_time * TICKS_PER_SEC ) /10; + } + + DBG ( "IGMP received query for %s\n", inet_ntoa ( igmp->group ) ); + for ( i = 0 ; i < MAX_IGMP ; i++ ) { + uint32_t group = igmptable[i].group.s_addr; + if ( ( group == 0 ) || + ( group == igmp->group.s_addr ) ) { + unsigned long time; + time = currticks() + + rfc1112_sleep_interval ( interval, 0 ); + if ( time < igmptable[i].time ) { + igmptable[i].time = time; + } + } + } + } + if ( ( ( igmp->type == IGMPv1_REPORT ) || + ( igmp->type == IGMPv2_REPORT ) ) && + ( ip->dest.s_addr == igmp->group.s_addr ) ) { + DBG ( "IGMP received report for %s\n", + inet_ntoa ( igmp->group ) ); + for ( i = 0 ; i < MAX_IGMP ; i++ ) { + if ( ( igmptable[i].group.s_addr == + igmp->group.s_addr ) && + ( igmptable[i].time != 0 ) ) { + igmptable[i].time = 0; + } + } + } +} + +struct background igmp_background __background = { + .send = send_igmp_reports, + .process = process_igmp, +}; + +void leave_group ( int slot ) { + /* Be very stupid and always send a leave group message if + * I have subscribed. Imperfect but it is standards + * compliant, easy and reliable to implement. + * + * The optimal group leave method is to only send leave when, + * we were the last host to respond to a query on this group, + * and igmpv1 compatibility is not enabled. + */ + if ( igmptable[slot].group.s_addr ) { + struct igmp_ip_t igmp; + + igmp.router_alert[0] = 0x94; + igmp.router_alert[1] = 0x04; + igmp.router_alert[2] = 0; + igmp.router_alert[3] = 0; + build_ip_hdr ( htonl ( GROUP_ALL_HOSTS ), 1, IP_IGMP, + sizeof ( igmp.router_alert ), sizeof ( igmp ), + &igmp); + igmp.igmp.type = IGMP_LEAVE; + igmp.igmp.response_time = 0; + igmp.igmp.chksum = 0; + igmp.igmp.group.s_addr = igmptable[slot].group.s_addr; + igmp.igmp.chksum = ipchksum ( &igmp.igmp, sizeof ( igmp ) ); + ip_transmit ( sizeof ( igmp ), &igmp ); + DBG ( "IGMP left group %s\n", inet_ntoa ( igmp.igmp.group ) ); + } + memset ( &igmptable[slot], 0, sizeof ( igmptable[0] ) ); +} + +void join_group ( int slot, unsigned long group ) { + /* I have already joined */ + if ( igmptable[slot].group.s_addr == group ) + return; + if ( igmptable[slot].group.s_addr ) { + leave_group ( slot ); + } + /* Only join a group if we are given a multicast ip, this way + * code can be given a non-multicast (broadcast or unicast ip) + * and still work... + */ + if ( ( group & htonl ( MULTICAST_MASK ) ) == + htonl ( MULTICAST_NETWORK ) ) { + igmptable[slot].group.s_addr = group; + igmptable[slot].time = currticks(); + } +} + +#endif diff --git a/gpxe/src/proto/nfs.c b/gpxe/src/proto/nfs.c new file mode 100644 index 0000000..943081a --- /dev/null +++ b/gpxe/src/proto/nfs.c @@ -0,0 +1,616 @@ +#if 0 + +#include +#include + +/* NOTE: the NFS code is heavily inspired by the NetBSD netboot code (read: + * large portions are copied verbatim) as distributed in OSKit 0.97. A few + * changes were necessary to adapt the code to Etherboot and to fix several + * inconsistencies. Also the RPC message preparation is done "by hand" to + * avoid adding netsprintf() which I find hard to understand and use. */ + +/* NOTE 2: Etherboot does not care about things beyond the kernel image, so + * it loads the kernel image off the boot server (ARP_SERVER) and does not + * access the client root disk (root-path in dhcpd.conf), which would use + * ARP_ROOTSERVER. The root disk is something the operating system we are + * about to load needs to use. This is different from the OSKit 0.97 logic. */ + +/* NOTE 3: Symlink handling introduced by Anselm M Hoffmeister, 2003-July-14 + * If a symlink is encountered, it is followed as far as possible (recursion + * possible, maximum 16 steps). There is no clearing of ".."'s inside the + * path, so please DON'T DO THAT. thx. */ + +#define START_OPORT 700 /* mountd usually insists on secure ports */ +#define OPORT_SWEEP 200 /* make sure we don't leave secure range */ + +static int oport = START_OPORT; +static struct sockaddr_in mount_server; +static struct sockaddr_in nfs_server; +static unsigned long rpc_id; + +/************************************************************************** +RPC_INIT - set up the ID counter to something fairly random +**************************************************************************/ +void rpc_init(void) +{ + unsigned long t; + + t = currticks(); + rpc_id = t ^ (t << 8) ^ (t << 16); +} + +/************************************************************************** +RPC_PRINTERROR - Print a low level RPC error message +**************************************************************************/ +static void rpc_printerror(struct rpc_t *rpc) +{ + if (rpc->u.reply.rstatus || rpc->u.reply.verifier || + rpc->u.reply.astatus) { + /* rpc_printerror() is called for any RPC related error, + * suppress output if no low level RPC error happened. */ + DBG("RPC error: (%ld,%ld,%ld)\n", ntohl(rpc->u.reply.rstatus), + ntohl(rpc->u.reply.verifier), + ntohl(rpc->u.reply.astatus)); + } +} + +/************************************************************************** +AWAIT_RPC - Wait for an rpc packet +**************************************************************************/ +static int await_rpc(int ival, void *ptr, + unsigned short ptype __unused, struct iphdr *ip, + struct udphdr *udp, struct tcphdr *tcp __unused) +{ + struct rpc_t *rpc; + if (!udp) + return 0; + if (arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr) + return 0; + if (ntohs(udp->dest) != ival) + return 0; + if (nic.packetlen < ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr) + 8) + return 0; + rpc = (struct rpc_t *)&nic.packet[ETH_HLEN]; + if (*(unsigned long *)ptr != ntohl(rpc->u.reply.id)) + return 0; + if (MSG_REPLY != ntohl(rpc->u.reply.type)) + return 0; + return 1; +} + +/************************************************************************** +RPC_LOOKUP - Lookup RPC Port numbers +**************************************************************************/ +static int rpc_lookup(struct sockaddr_in *addr, int prog, int ver, int sport) +{ + struct rpc_t buf, *rpc; + unsigned long id; + int retries; + long *p; + + id = rpc_id++; + buf.u.call.id = htonl(id); + buf.u.call.type = htonl(MSG_CALL); + buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */ + buf.u.call.prog = htonl(PROG_PORTMAP); + buf.u.call.vers = htonl(2); /* portmapper is version 2 */ + buf.u.call.proc = htonl(PORTMAP_GETPORT); + p = (long *)buf.u.call.data; + *p++ = 0; *p++ = 0; /* auth credential */ + *p++ = 0; *p++ = 0; /* auth verifier */ + *p++ = htonl(prog); + *p++ = htonl(ver); + *p++ = htonl(IP_UDP); + *p++ = 0; + for (retries = 0; retries < MAX_RPC_RETRIES; retries++) { + long timeout; + udp_transmit(addr->sin_addr.s_addr, sport, addr->sin_port, + (char *)p - (char *)&buf, &buf); + timeout = rfc2131_sleep_interval(TIMEOUT, retries); + if (await_reply(await_rpc, sport, &id, timeout)) { + rpc = (struct rpc_t *)&nic.packet[ETH_HLEN]; + if (rpc->u.reply.rstatus || rpc->u.reply.verifier || + rpc->u.reply.astatus) { + rpc_printerror(rpc); + return 0; + } else { + return ntohl(rpc->u.reply.data[0]); + } + } + } + return 0; +} + +/************************************************************************** +RPC_ADD_CREDENTIALS - Add RPC authentication/verifier entries +**************************************************************************/ +static long *rpc_add_credentials(long *p) +{ + int hl; + + /* Here's the executive summary on authentication requirements of the + * various NFS server implementations: Linux accepts both AUTH_NONE + * and AUTH_UNIX authentication (also accepts an empty hostname field + * in the AUTH_UNIX scheme). *BSD refuses AUTH_NONE, but accepts + * AUTH_UNIX (also accepts an empty hostname field in the AUTH_UNIX + * scheme). To be safe, use AUTH_UNIX and pass the hostname if we have + * it (if the BOOTP/DHCP reply didn't give one, just use an empty + * hostname). */ + + hl = (hostnamelen + 3) & ~3; + + /* Provide an AUTH_UNIX credential. */ + *p++ = htonl(1); /* AUTH_UNIX */ + *p++ = htonl(hl+20); /* auth length */ + *p++ = htonl(0); /* stamp */ + *p++ = htonl(hostnamelen); /* hostname string */ + if (hostnamelen & 3) { + *(p + hostnamelen / 4) = 0; /* add zero padding */ + } + memcpy(p, hostname, hostnamelen); + p += hl / 4; + *p++ = 0; /* uid */ + *p++ = 0; /* gid */ + *p++ = 0; /* auxiliary gid list */ + + /* Provide an AUTH_NONE verifier. */ + *p++ = 0; /* AUTH_NONE */ + *p++ = 0; /* auth length */ + + return p; +} + +/************************************************************************** +NFS_PRINTERROR - Print a NFS error message +**************************************************************************/ +static void nfs_printerror(int err) +{ + switch (-err) { + case NFSERR_PERM: + printf("Not owner\n"); + break; + case NFSERR_NOENT: + printf("No such file or directory\n"); + break; + case NFSERR_ACCES: + printf("Permission denied\n"); + break; + case NFSERR_ISDIR: + printf("Directory given where filename expected\n"); + break; + case NFSERR_INVAL: + printf("Invalid filehandle\n"); + break; // INVAL is not defined in NFSv2, some NFS-servers + // seem to use it in answers to v2 nevertheless. + case 9998: + printf("low-level RPC failure (parameter decoding problem?)\n"); + break; + case 9999: + printf("low-level RPC failure (authentication problem?)\n"); + break; + default: + printf("Unknown NFS error %d\n", -err); + } +} + +/************************************************************************** +NFS_MOUNT - Mount an NFS Filesystem +**************************************************************************/ +static int nfs_mount(struct sockaddr_in *server, char *path, char *fh, int sport) +{ + struct rpc_t buf, *rpc; + unsigned long id; + int retries; + long *p; + int pathlen = strlen(path); + + id = rpc_id++; + buf.u.call.id = htonl(id); + buf.u.call.type = htonl(MSG_CALL); + buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */ + buf.u.call.prog = htonl(PROG_MOUNT); + buf.u.call.vers = htonl(1); /* mountd is version 1 */ + buf.u.call.proc = htonl(MOUNT_ADDENTRY); + p = rpc_add_credentials((long *)buf.u.call.data); + *p++ = htonl(pathlen); + if (pathlen & 3) { + *(p + pathlen / 4) = 0; /* add zero padding */ + } + memcpy(p, path, pathlen); + p += (pathlen + 3) / 4; + for (retries = 0; retries < MAX_RPC_RETRIES; retries++) { + long timeout; + udp_transmit(server->sin_addr.s_addr, sport, server->sin_port, + (char *)p - (char *)&buf, &buf); + timeout = rfc2131_sleep_interval(TIMEOUT, retries); + if (await_reply(await_rpc, sport, &id, timeout)) { + rpc = (struct rpc_t *)&nic.packet[ETH_HLEN]; + if (rpc->u.reply.rstatus || rpc->u.reply.verifier || + rpc->u.reply.astatus || rpc->u.reply.data[0]) { + rpc_printerror(rpc); + if (rpc->u.reply.rstatus) { + /* RPC failed, no verifier, data[0] */ + return -9999; + } + if (rpc->u.reply.astatus) { + /* RPC couldn't decode parameters */ + return -9998; + } + return -ntohl(rpc->u.reply.data[0]); + } else { + memcpy(fh, rpc->u.reply.data + 1, NFS_FHSIZE); + return 0; + } + } + } + return -1; +} + +/************************************************************************** +NFS_UMOUNTALL - Unmount all our NFS Filesystems on the Server +**************************************************************************/ +static void nfs_umountall(struct sockaddr_in *server) +{ + struct rpc_t buf, *rpc; + unsigned long id; + int retries; + long *p; + + id = rpc_id++; + buf.u.call.id = htonl(id); + buf.u.call.type = htonl(MSG_CALL); + buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */ + buf.u.call.prog = htonl(PROG_MOUNT); + buf.u.call.vers = htonl(1); /* mountd is version 1 */ + buf.u.call.proc = htonl(MOUNT_UMOUNTALL); + p = rpc_add_credentials((long *)buf.u.call.data); + for (retries = 0; retries < MAX_RPC_RETRIES; retries++) { + long timeout = rfc2131_sleep_interval(TIMEOUT, retries); + udp_transmit(server->sin_addr.s_addr, oport, server->sin_port, + (char *)p - (char *)&buf, &buf); + if (await_reply(await_rpc, oport, &id, timeout)) { + rpc = (struct rpc_t *)&nic.packet[ETH_HLEN]; + if (rpc->u.reply.rstatus || rpc->u.reply.verifier || + rpc->u.reply.astatus) { + rpc_printerror(rpc); + } + break; + } + } +} + +/************************************************************************** +NFS_RESET - Reset the NFS subsystem +**************************************************************************/ +static void nfs_reset ( void ) { + /* If we have a mount server, call nfs_umountall() */ + if ( mount_server.sin_addr.s_addr ) { + nfs_umountall ( &mount_server ); + } + /* Zero the data structures */ + memset ( &mount_server, 0, sizeof ( mount_server ) ); + memset ( &nfs_server, 0, sizeof ( nfs_server ) ); +} + +/*************************************************************************** + * NFS_READLINK (AH 2003-07-14) + * This procedure is called when read of the first block fails - + * this probably happens when it's a directory or a symlink + * In case of successful readlink(), the dirname is manipulated, + * so that inside the nfs() function a recursion can be done. + **************************************************************************/ +static int nfs_readlink(struct sockaddr_in *server, char *fh __unused, + char *path, char *nfh, int sport) +{ + struct rpc_t buf, *rpc; + unsigned long id; + long *p; + int retries; + int pathlen = strlen(path); + + id = rpc_id++; + buf.u.call.id = htonl(id); + buf.u.call.type = htonl(MSG_CALL); + buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */ + buf.u.call.prog = htonl(PROG_NFS); + buf.u.call.vers = htonl(2); /* nfsd is version 2 */ + buf.u.call.proc = htonl(NFS_READLINK); + p = rpc_add_credentials((long *)buf.u.call.data); + memcpy(p, nfh, NFS_FHSIZE); + p += (NFS_FHSIZE / 4); + for (retries = 0; retries < MAX_RPC_RETRIES; retries++) { + long timeout = rfc2131_sleep_interval(TIMEOUT, retries); + udp_transmit(server->sin_addr.s_addr, sport, server->sin_port, + (char *)p - (char *)&buf, &buf); + if (await_reply(await_rpc, sport, &id, timeout)) { + rpc = (struct rpc_t *)&nic.packet[ETH_HLEN]; + if (rpc->u.reply.rstatus || rpc->u.reply.verifier || + rpc->u.reply.astatus || rpc->u.reply.data[0]) { + rpc_printerror(rpc); + if (rpc->u.reply.rstatus) { + /* RPC failed, no verifier, data[0] */ + return -9999; + } + if (rpc->u.reply.astatus) { + /* RPC couldn't decode parameters */ + return -9998; + } + return -ntohl(rpc->u.reply.data[0]); + } else { + // It *is* a link. + // If it's a relative link, append everything to dirname, filename TOO! + retries = strlen ( (char *)(&(rpc->u.reply.data[2]) )); + if ( *((char *)(&(rpc->u.reply.data[2]))) != '/' ) { + path[pathlen++] = '/'; + while ( ( retries + pathlen ) > 298 ) { + retries--; + } + if ( retries > 0 ) { + memcpy(path + pathlen, &(rpc->u.reply.data[2]), retries + 1); + } else { retries = 0; } + path[pathlen + retries] = 0; + } else { + // Else make it the only path. + if ( retries > 298 ) { retries = 298; } + memcpy ( path, &(rpc->u.reply.data[2]), retries + 1 ); + path[retries] = 0; + } + return 0; + } + } + } + return -1; +} +/************************************************************************** +NFS_LOOKUP - Lookup Pathname +**************************************************************************/ +static int nfs_lookup(struct sockaddr_in *server, char *fh, char *path, char *nfh, + int sport) +{ + struct rpc_t buf, *rpc; + unsigned long id; + long *p; + int retries; + int pathlen = strlen(path); + + id = rpc_id++; + buf.u.call.id = htonl(id); + buf.u.call.type = htonl(MSG_CALL); + buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */ + buf.u.call.prog = htonl(PROG_NFS); + buf.u.call.vers = htonl(2); /* nfsd is version 2 */ + buf.u.call.proc = htonl(NFS_LOOKUP); + p = rpc_add_credentials((long *)buf.u.call.data); + memcpy(p, fh, NFS_FHSIZE); + p += (NFS_FHSIZE / 4); + *p++ = htonl(pathlen); + if (pathlen & 3) { + *(p + pathlen / 4) = 0; /* add zero padding */ + } + memcpy(p, path, pathlen); + p += (pathlen + 3) / 4; + for (retries = 0; retries < MAX_RPC_RETRIES; retries++) { + long timeout = rfc2131_sleep_interval(TIMEOUT, retries); + udp_transmit(server->sin_addr.s_addr, sport, server->sin_port, + (char *)p - (char *)&buf, &buf); + if (await_reply(await_rpc, sport, &id, timeout)) { + rpc = (struct rpc_t *)&nic.packet[ETH_HLEN]; + if (rpc->u.reply.rstatus || rpc->u.reply.verifier || + rpc->u.reply.astatus || rpc->u.reply.data[0]) { + rpc_printerror(rpc); + if (rpc->u.reply.rstatus) { + /* RPC failed, no verifier, data[0] */ + return -9999; + } + if (rpc->u.reply.astatus) { + /* RPC couldn't decode parameters */ + return -9998; + } + return -ntohl(rpc->u.reply.data[0]); + } else { + memcpy(nfh, rpc->u.reply.data + 1, NFS_FHSIZE); + return 0; + } + } + } + return -1; +} + +/************************************************************************** +NFS_READ - Read File on NFS Server +**************************************************************************/ +static int nfs_read(struct sockaddr_in *server, char *fh, int offset, int len, + int sport) +{ + struct rpc_t buf, *rpc; + unsigned long id; + int retries; + long *p; + + static int tokens=0; + /* + * Try to implement something similar to a window protocol in + * terms of response to losses. On successful receive, increment + * the number of tokens by 1 (cap at 256). On failure, halve it. + * When the number of tokens is >= 2, use a very short timeout. + */ + + id = rpc_id++; + buf.u.call.id = htonl(id); + buf.u.call.type = htonl(MSG_CALL); + buf.u.call.rpcvers = htonl(2); /* use RPC version 2 */ + buf.u.call.prog = htonl(PROG_NFS); + buf.u.call.vers = htonl(2); /* nfsd is version 2 */ + buf.u.call.proc = htonl(NFS_READ); + p = rpc_add_credentials((long *)buf.u.call.data); + memcpy(p, fh, NFS_FHSIZE); + p += NFS_FHSIZE / 4; + *p++ = htonl(offset); + *p++ = htonl(len); + *p++ = 0; /* unused parameter */ + for (retries = 0; retries < MAX_RPC_RETRIES; retries++) { + long timeout = rfc2131_sleep_interval(TIMEOUT, retries); + if (tokens >= 2) + timeout = TICKS_PER_SEC/2; + + udp_transmit(server->sin_addr.s_addr, sport, server->sin_port, + (char *)p - (char *)&buf, &buf); + if (await_reply(await_rpc, sport, &id, timeout)) { + if (tokens < 256) + tokens++; + rpc = (struct rpc_t *)&nic.packet[ETH_HLEN]; + if (rpc->u.reply.rstatus || rpc->u.reply.verifier || + rpc->u.reply.astatus || rpc->u.reply.data[0]) { + rpc_printerror(rpc); + if (rpc->u.reply.rstatus) { + /* RPC failed, no verifier, data[0] */ + return -9999; + } + if (rpc->u.reply.astatus) { + /* RPC couldn't decode parameters */ + return -9998; + } + return -ntohl(rpc->u.reply.data[0]); + } else { + return 0; + } + } else + tokens >>= 1; + } + return -1; +} + +/************************************************************************** +NFS - Download extended BOOTP data, or kernel image from NFS server +**************************************************************************/ +static int nfs ( char *url __unused, struct sockaddr_in *server, + char *name, struct buffer *buffer ) { + static int recursion = 0; + int sport; + int err, namelen = strlen(name); + char dirname[300], *fname; + char dirfh[NFS_FHSIZE]; /* file handle of directory */ + char filefh[NFS_FHSIZE]; /* file handle of kernel image */ + int rlen, size, offs, len; + struct rpc_t *rpc; + + sport = oport++; + if (oport > START_OPORT+OPORT_SWEEP) { + oport = START_OPORT; + } + + mount_server.sin_addr = nfs_server.sin_addr = server->sin_addr; + mount_server.sin_port = rpc_lookup(server, PROG_MOUNT, 1, sport); + if ( ! mount_server.sin_port ) { + DBG ( "Cannot get mount port from %s:%d\n", + inet_ntoa ( server->sin_addr ), server->sin_port ); + return 0; + } + nfs_server.sin_port = rpc_lookup(server, PROG_NFS, 2, sport); + if ( ! mount_server.sin_port ) { + DBG ( "Cannot get nfs port from %s:%d\n", + inet_ntoa ( server->sin_addr ), server->sin_port ); + return 0; + } + + if ( name != dirname ) { + memcpy(dirname, name, namelen + 1); + } + recursion = 0; +nfssymlink: + if ( recursion > NFS_MAXLINKDEPTH ) { + DBG ( "\nRecursion: More than %d symlinks followed. Abort.\n", + NFS_MAXLINKDEPTH ); + return 0; + } + recursion++; + fname = dirname + (namelen - 1); + while (fname >= dirname) { + if (*fname == '/') { + *fname = '\0'; + fname++; + break; + } + fname--; + } + if (fname < dirname) { + DBG("can't parse file name %s\n", name); + return 0; + } + + err = nfs_mount(&mount_server, dirname, dirfh, sport); + if (err) { + DBG("mounting %s: ", dirname); + nfs_printerror(err); + /* just to be sure... */ + nfs_reset(); + return 0; + } + + err = nfs_lookup(&nfs_server, dirfh, fname, filefh, sport); + if (err) { + DBG("looking up %s: ", fname); + nfs_printerror(err); + nfs_reset(); + return 0; + } + + offs = 0; + size = -1; /* will be set properly with the first reply */ + len = NFS_READ_SIZE; /* first request is always full size */ + do { + err = nfs_read(&nfs_server, filefh, offs, len, sport); + if ((err <= -NFSERR_ISDIR)&&(err >= -NFSERR_INVAL) && (offs == 0)) { + // An error occured. NFS servers tend to sending + // errors 21 / 22 when symlink instead of real file + // is requested. So check if it's a symlink! + if ( nfs_readlink(&nfs_server, dirfh, dirname, + filefh, sport) == 0 ) { + printf("\nLoading symlink:%s ..",dirname); + goto nfssymlink; + } + nfs_printerror(err); + nfs_reset(); + return 0; + } + if (err) { + printf("\nError reading at offset %d: ", offs); + nfs_printerror(err); + nfs_reset(); + return 0; + } + + rpc = (struct rpc_t *)&nic.packet[ETH_HLEN]; + + /* size must be found out early to allow EOF detection */ + if (size == -1) { + size = ntohl(rpc->u.reply.data[6]); + } + rlen = ntohl(rpc->u.reply.data[18]); + if (rlen > len) { + rlen = len; /* shouldn't happen... */ + } + + if ( ! fill_buffer ( buffer, &rpc->u.reply.data[19], + offs, rlen ) ) { + nfs_reset(); + return 0; + } + + offs += rlen; + /* last request is done with matching requested read size */ + if (size-offs < NFS_READ_SIZE) { + len = size-offs; + } + } while (len != 0); + /* len == 0 means that all the file has been read */ + return 1; +} + +struct protocol nfs_protocol __protocol = { + .name = "nfs", + .default_port = SUNRPC_PORT, + .load = nfs, +}; + +#endif diff --git a/gpxe/src/proto/nmb.c b/gpxe/src/proto/nmb.c new file mode 100644 index 0000000..e1fc911 --- /dev/null +++ b/gpxe/src/proto/nmb.c @@ -0,0 +1,110 @@ +#if 0 + +#include "resolv.h" +#include "string.h" +#include +#include "nic.h" +#include "nmb.h" + +/* + * Convert a standard NUL-terminated string to an NBNS query name. + * + * Returns a pointer to the character following the constructed NBNS + * query name. + * + */ +static inline char * nbns_make_name ( char *dest, const char *name ) { + char nb_name[16]; + char c; + int i; + uint16_t *d; + + *(dest++) = 32; /* Length is always 32 */ + + /* Name encoding is as follows: pad the name with spaces to + * length 15, and add a NUL. Take this 16-byte string, split + * it into nibbles and add 0x41 to each nibble to form a byte + * of the resulting name string. + */ + memset ( nb_name, ' ', 15 ); + nb_name[15] = '\0'; + memcpy ( nb_name, name, strlen ( name ) ); /* Do not copy NUL */ + + d = ( uint16_t * ) dest; + for ( i = 0 ; i < 16 ; i++ ) { + c = nb_name[i]; + *( d++ ) = htons ( ( ( c | ( c << 4 ) ) & 0x0f0f ) + 0x4141 ); + } + dest = ( char * ) d; + + *(dest++) = 0; /* Terminating 0-length name component */ + return dest; +} + +/* + * Resolve a name using NMB + * + */ +static int nmb_resolv ( struct in_addr *addr, const char *name ) { + struct dns_query query; + struct dns_query_info *query_info; + struct dns_header *reply; + struct dns_rr_info *rr_info; + struct dns_rr_info_nb *rr_info_nb; + struct sockaddr_in nameserver; + + DBG ( "NMB resolving %s\n", name ); + + /* Set up the query data */ + nameserver.sin_addr.s_addr = INADDR_BROADCAST; + nameserver.sin_port = NBNS_UDP_PORT; + memset ( &query, 0, sizeof ( query ) ); + query.dns.id = htons ( 1 ); + query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY | + DNS_FLAG_RD | DNS_FLAG_BROADCAST ); + query.dns.qdcount = htons ( 1 ); + query_info = ( void * ) nbns_make_name ( query.payload, name ); + query_info->qtype = htons ( DNS_TYPE_NB ); + query_info->qclass = htons ( DNS_CLASS_IN ); + + /* Issue query, wait for reply */ + reply = dns_query ( &query, + ( ( ( char * ) query_info ) + + sizeof ( *query_info ) + - ( ( char * ) &query ) ), + &nameserver ); + if ( ! reply ) { + DBG ( "NMB got no response via %@ (port %d)\n", + nameserver.sin_addr.s_addr, + nameserver.sin_port ); + return 0; + } + + /* Search through response for useful answers. */ + rr_info = dns_find_rr ( &query, reply ); + if ( ! rr_info ) { + DBG ( "NMB got invalid response\n" ); + return 0; + } + + /* Check type of response */ + if ( ntohs ( rr_info->type ) != DNS_TYPE_NB ) { + DBG ( "NMB got answer type %hx (wanted %hx)\n", + ntohs ( rr_info->type ), DNS_TYPE_NB ); + return 0; + } + + /* Read response */ + rr_info_nb = ( struct dns_rr_info_nb * ) rr_info; + *addr = rr_info_nb->nb_address; + DBG ( "NMB found address %@\n", addr->s_addr ); + + return 1; +} + +struct resolver nmb_resolver __resolver = { + .name = "NMB", + .resolv = nmb_resolv, +}; + +#endif diff --git a/gpxe/src/tests/comboot/shuffle-simple.asm b/gpxe/src/tests/comboot/shuffle-simple.asm new file mode 100644 index 0000000..efc7d9b --- /dev/null +++ b/gpxe/src/tests/comboot/shuffle-simple.asm @@ -0,0 +1,40 @@ + bits 16 + org 100h + + jmp start + +shuffle_start: + push 0xB800 + pop es + mov cx, 80*24*2 + mov ax, 'AA' + xor di, di + rep stosw +.lbl: jmp .lbl +shuffle_end: + nop +shuffle_len equ (shuffle_end - shuffle_start + 1) + +start: + ; calculate physical address of shuffled part + xor eax, eax + push ds + pop ax + shl eax, 4 + add ax, shuffle_start + mov dword [source], eax + + mov ax, 0012h + mov di, shuffle_descriptors + mov cx, num_shuffle_descriptors + mov ebp, 0x7c00 + int 22h + int3 + +shuffle_descriptors: + dd 0x7C00 +source: dd 0 + dd shuffle_len + +num_shuffle_descriptors equ 1 + diff --git a/gpxe/src/tests/comboot/version.asm b/gpxe/src/tests/comboot/version.asm new file mode 100644 index 0000000..0114042 --- /dev/null +++ b/gpxe/src/tests/comboot/version.asm @@ -0,0 +1,136 @@ + bits 16 + org 100h + +_start: + ; first check for SYSLINUX + mov ah, 30h + int 21h + + cmp eax, 59530000h + jne .not_syslinux + cmp ebx, 4c530000h + jne .not_syslinux + cmp ecx, 4e490000h + jne .not_syslinux + cmp edx, 58550000h + jne .not_syslinux + + ; now get syslinux version + mov ax, 0001h + int 22h + + push cx + push dx + push di + push si + push es + + ; print version string + mov dx, str_version + mov ah, 09h + int 21h + + pop es + pop bx + push es + mov ax, 0002h + int 22h + + ; print copyright string + mov dx, str_copyright + mov ah, 09h + int 21h + + pop es + pop bx + mov ax, 0002h + int 22h + + ; print syslinux derivative id + mov dx, str_derivative + mov ah, 09h + int 21h + + pop ax + call print_hex_byte + + ; print version number + mov dx, str_version_num + mov ah, 09h + int 21h + + pop cx + push cx + mov ax, cx + and ax, 0FFh + call print_dec_word + + mov dl, '.' + mov ah, 02h + int 21h + + pop cx + mov ax, cx + shr ax, 8 + call print_dec_word + + ret + + +.not_syslinux: + mov dx, str_not_syslinux + mov ah, 09h + int 21h + ret + +; input: al = byte to print in hex +print_hex_byte: + push ax + shr al, 4 + call print_hex_nybble + pop ax + call print_hex_nybble + ret + +; input: bottom half of al = nybble to print in hex +print_hex_nybble: + push ax + mov bl, al + and bx, 1111b + mov dl, [str_hex + bx] + mov ah, 02h + int 21h + pop ax + ret + +str_hex: db "01234567890abcdef" + +; input: ax = word to print +print_dec_word: + mov cx, 10 + mov word [.count], 0 +.loop: + xor dx, dx + div cx + inc word [.count] + push dx + test ax, ax + jnz .loop + +.print: + pop dx + add dx, '0' + mov ah, 02h + int 21h + dec word [.count] + jnz .print + + ret + +.count: dw 0 + +str_not_syslinux: db "Not SYSLINUX or derivative (running on DOS?)$" +str_version: db "Version: $" +str_copyright: db 10, "Copyright: $" +str_derivative: db 10, "Derivative ID: 0x$" +str_version_num: db 10, "Version number: $" diff --git a/gpxe/src/tests/gdbstub_test.S b/gpxe/src/tests/gdbstub_test.S new file mode 100644 index 0000000..739b052 --- /dev/null +++ b/gpxe/src/tests/gdbstub_test.S @@ -0,0 +1,54 @@ + .arch i386 + + .section ".data", "aw", @progbits +watch_me: + .long 0xfeedbeef + + .section ".text", "ax", @progbits + .code32 +gdbstub_test: + /* 1. Read registers test */ + movl $0xea010203, %eax + movl $0xeb040506, %ebx + movl $0xec070809, %ecx + movl $0xed0a0b0c, %edx + movl $0x510d0e0f, %esi + movl $0xd1102030, %edi + int $3 + + /* 2. Write registers test */ + int $3 + + /* 3. Read memory test */ + subl $8, %esp + movl $0x11223344, 4(%esp) + movw $0x5566, 2(%esp) + movb $0x77, (%esp) + int $3 + + /* 4. Write memory test */ + int $3 + addl $8, %esp + + /* 5. Step test */ + int $3 + nop + + /* 6. Access watch test */ + movl $0x600d0000, %ecx + movl watch_me, %eax + movl $0xbad00000, %ecx + int $3 + movl $0x600d0001, %ecx + movl %eax, watch_me + movl $0xbad00001, %ecx + int $3 + + /* 7. Write watch test */ + movl $0x600d0002, %ecx + movl %eax, watch_me + movl $0xbad00002, %ecx + int $3 + +1: + jmp 1b diff --git a/gpxe/src/tests/gdbstub_test.gdb b/gpxe/src/tests/gdbstub_test.gdb new file mode 100755 index 0000000..52aa693 --- /dev/null +++ b/gpxe/src/tests/gdbstub_test.gdb @@ -0,0 +1,116 @@ +#!/usr/bin/gdb -x +# Test suite for GDB remote debugging +# Run: +# make bin/gpxe.hd.tmp +# make +# gdb +# (gdb) target remote :TCPPORT +# OR +# (gdb) target remote udp:IP:UDPPORT +# (gdb) source tests/gdbstub_test.gdb + +define gpxe_load_symbols + file bin/gpxe.hd.tmp +end + +define gpxe_assert + if $arg0 != $arg1 + echo FAIL $arg2\n + else + echo PASS $arg2\n + end +end + +define gpxe_start_tests + jump gdbstub_test +end + +define gpxe_test_regs_read + gpxe_assert $eax 0xea010203 "gpxe_test_regs_read eax" + gpxe_assert $ebx 0xeb040506 "gpxe_test_regs_read ebx" + gpxe_assert $ecx 0xec070809 "gpxe_test_regs_read ecx" + gpxe_assert $edx 0xed0a0b0c "gpxe_test_regs_read edx" + gpxe_assert $esi 0x510d0e0f "gpxe_test_regs_read esi" + gpxe_assert $edi 0xd1102030 "gpxe_test_regs_read edi" +end + +define gpxe_test_regs_write + set $eax = 0xea112233 + set $ebx = 0xeb445566 + set $ecx = 0xec778899 + set $edx = 0xedaabbcc + set $esi = 0x51ddeeff + set $edi = 0xd1010203 + c + gpxe_assert $eax 0xea112233 "gpxe_test_regs_write eax" + gpxe_assert $ebx 0xeb445566 "gpxe_test_regs_write ebx" + gpxe_assert $ecx 0xec778899 "gpxe_test_regs_write ecx" + gpxe_assert $edx 0xedaabbcc "gpxe_test_regs_write edx" + gpxe_assert $esi 0x51ddeeff "gpxe_test_regs_write esi" + gpxe_assert $edi 0xd1010203 "gpxe_test_regs_write edi" + + # This assumes segment selectors are always 0x10 or 0x8 (for code). + gpxe_assert $cs 0x08 "gpxe_test_regs_write cs" + gpxe_assert $ds 0x10 "gpxe_test_regs_write ds" +end + +define gpxe_test_mem_read + c + gpxe_assert ({int}($esp+4)) 0x11223344 "gpxe_test_mem_read int" + gpxe_assert ({short}($esp+2)) 0x5566 "gpxe_test_mem_read short" + gpxe_assert ({char}($esp)) 0x77 "gpxe_test_mem_read char" +end + +define gpxe_test_mem_write + set ({int}($esp+4)) = 0xaabbccdd + set ({short}($esp+2)) = 0xeeff + set ({char}($esp)) = 0x99 + c + gpxe_assert ({int}($esp+4)) 0xaabbccdd "gpxe_test_mem_write int" + gpxe_assert ({short}($esp+2)) (short)0xeeff "gpxe_test_mem_write short" + gpxe_assert ({char}($esp)) (char)0x99 "gpxe_test_mem_write char" +end + +define gpxe_test_step + c + si + gpxe_assert ({char}($eip-1)) (char)0x90 "gpxe_test_step" # nop = 0x90 +end + +define gpxe_test_awatch + awatch watch_me + + c + gpxe_assert $ecx 0x600d0000 "gpxe_test_awatch read" + if $ecx == 0x600d0000 + c + end + + c + gpxe_assert $ecx 0x600d0001 "gpxe_test_awatch write" + if $ecx == 0x600d0001 + c + end + + delete +end + +define gpxe_test_watch + watch watch_me + c + gpxe_assert $ecx 0x600d0002 "gpxe_test_watch" + if $ecx == 0x600d0002 + c + end + delete +end + +gpxe_load_symbols +gpxe_start_tests +gpxe_test_regs_read +gpxe_test_regs_write +gpxe_test_mem_read +gpxe_test_mem_write +gpxe_test_step +gpxe_test_awatch +gpxe_test_watch diff --git a/gpxe/src/tests/linebuf_test.c b/gpxe/src/tests/linebuf_test.c new file mode 100644 index 0000000..c3c3b37 --- /dev/null +++ b/gpxe/src/tests/linebuf_test.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +static const char data1[] = +"Hello world\r\n" +"This is a reasonably nice set of lines\n" +"with not many different terminators\r\n\r\n" +"There should be exactly one blank line above\n" +"and this line should never appear at all since it has no terminator"; + +void linebuf_test ( void ) { + struct line_buffer linebuf; + const char *data = data1; + size_t len = ( sizeof ( data1 ) - 1 /* be mean; strip the NUL */ ); + ssize_t frag_len; + char *line; + + memset ( &linebuf, 0, sizeof ( linebuf ) ); + while ( len ) { + frag_len = line_buffer ( &linebuf, data, len ); + if ( frag_len < 0 ) { + printf ( "line_buffer() failed: %s\n", + strerror ( frag_len ) ); + return; + } + data += frag_len; + len -= frag_len; + if ( ( line = buffered_line ( &linebuf ) ) ) + printf ( "\"%s\"\n", line ); + } + + empty_line_buffer ( &linebuf ); +} diff --git a/gpxe/src/tests/memcpy_test.c b/gpxe/src/tests/memcpy_test.c new file mode 100644 index 0000000..7626e63 --- /dev/null +++ b/gpxe/src/tests/memcpy_test.c @@ -0,0 +1,39 @@ +#include + +/* + * This file exists for testing the compilation of memcpy() with the + * various constant-length optimisations. + * + */ + +#define __regparm __attribute__ (( regparm(3) )) + +void __regparm memcpy_0 ( void *dest, void *src ) { memcpy ( dest, src, 0 ); } +void __regparm memcpy_1 ( void *dest, void *src ) { memcpy ( dest, src, 1 ); } +void __regparm memcpy_2 ( void *dest, void *src ) { memcpy ( dest, src, 2 ); } +void __regparm memcpy_3 ( void *dest, void *src ) { memcpy ( dest, src, 3 ); } +void __regparm memcpy_4 ( void *dest, void *src ) { memcpy ( dest, src, 4 ); } +void __regparm memcpy_5 ( void *dest, void *src ) { memcpy ( dest, src, 5 ); } +void __regparm memcpy_6 ( void *dest, void *src ) { memcpy ( dest, src, 6 ); } +void __regparm memcpy_7 ( void *dest, void *src ) { memcpy ( dest, src, 7 ); } +void __regparm memcpy_8 ( void *dest, void *src ) { memcpy ( dest, src, 8 ); } +void __regparm memcpy_9 ( void *dest, void *src ) { memcpy ( dest, src, 9 ); } +void __regparm memcpy_10 ( void *dest, void *src ) { memcpy ( dest, src, 10 ); } +void __regparm memcpy_11 ( void *dest, void *src ) { memcpy ( dest, src, 11 ); } +void __regparm memcpy_12 ( void *dest, void *src ) { memcpy ( dest, src, 12 ); } +void __regparm memcpy_13 ( void *dest, void *src ) { memcpy ( dest, src, 13 ); } +void __regparm memcpy_14 ( void *dest, void *src ) { memcpy ( dest, src, 14 ); } +void __regparm memcpy_15 ( void *dest, void *src ) { memcpy ( dest, src, 15 ); } +void __regparm memcpy_16 ( void *dest, void *src ) { memcpy ( dest, src, 16 ); } +void __regparm memcpy_17 ( void *dest, void *src ) { memcpy ( dest, src, 17 ); } +void __regparm memcpy_18 ( void *dest, void *src ) { memcpy ( dest, src, 18 ); } +void __regparm memcpy_19 ( void *dest, void *src ) { memcpy ( dest, src, 19 ); } +void __regparm memcpy_20 ( void *dest, void *src ) { memcpy ( dest, src, 20 ); } +void __regparm memcpy_21 ( void *dest, void *src ) { memcpy ( dest, src, 21 ); } +void __regparm memcpy_22 ( void *dest, void *src ) { memcpy ( dest, src, 22 ); } +void __regparm memcpy_23 ( void *dest, void *src ) { memcpy ( dest, src, 23 ); } +void __regparm memcpy_24 ( void *dest, void *src ) { memcpy ( dest, src, 24 ); } +void __regparm memcpy_25 ( void *dest, void *src ) { memcpy ( dest, src, 25 ); } +void __regparm memcpy_26 ( void *dest, void *src ) { memcpy ( dest, src, 26 ); } +void __regparm memcpy_27 ( void *dest, void *src ) { memcpy ( dest, src, 27 ); } +void __regparm memcpy_28 ( void *dest, void *src ) { memcpy ( dest, src, 28 ); } diff --git a/gpxe/src/tests/umalloc_test.c b/gpxe/src/tests/umalloc_test.c new file mode 100644 index 0000000..7a36186 --- /dev/null +++ b/gpxe/src/tests/umalloc_test.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +void umalloc_test ( void ) { + struct memory_map memmap; + userptr_t bob; + userptr_t fred; + + printf ( "Before allocation:\n" ); + get_memmap ( &memmap ); + + bob = umalloc ( 1234 ); + bob = urealloc ( bob, 12345 ); + fred = umalloc ( 999 ); + + printf ( "After allocation:\n" ); + get_memmap ( &memmap ); + + ufree ( bob ); + ufree ( fred ); + + printf ( "After freeing:\n" ); + get_memmap ( &memmap ); +} diff --git a/gpxe/src/tests/uri_test.c b/gpxe/src/tests/uri_test.c new file mode 100644 index 0000000..2548760 --- /dev/null +++ b/gpxe/src/tests/uri_test.c @@ -0,0 +1,145 @@ +#include +#include +#include +#include +#include +#include + +#define URI_MAX_LEN 1024 + +struct uri_test { + const char *base_uri_string; + const char *relative_uri_string; + const char *resolved_uri_string; +}; + +static struct uri_test uri_tests[] = { + { "http://www.fensystems.co.uk", "", + "http://www.fensystems.co.uk/" }, + { "http://etherboot.org/wiki/page1", "page2", + "http://etherboot.org/wiki/page2" }, + { "http://etherboot.org/wiki/page1", "../page3", + "http://etherboot.org/page3" }, + { "tftp://192.168.0.1/", "/tftpboot/vmlinuz", + "tftp://192.168.0.1/tftpboot/vmlinuz" }, +#if 0 + "http://www.etherboot.org/wiki", + "mailto:bob@nowhere.com", + "ftp://joe:secret@insecure.org:8081/hidden/path/to?what=is#this", +#endif +}; + +static int test_parse_unparse ( const char *uri_string ) { + char buf[URI_MAX_LEN]; + size_t len; + struct uri *uri = NULL; + int rc; + + /* Parse and unparse URI */ + uri = parse_uri ( uri_string ); + if ( ! uri ) { + rc = -ENOMEM; + goto done; + } + len = unparse_uri ( buf, sizeof ( buf ), uri ); + + /* Compare result */ + if ( strcmp ( buf, uri_string ) != 0 ) { + printf ( "Unparse of \"%s\" produced \"%s\"\n", + uri_string, buf ); + rc = -EINVAL; + goto done; + } + + rc = 0; + + done: + uri_put ( uri ); + if ( rc ) { + printf ( "URI parse-unparse of \"%s\" failed: %s\n", + uri_string, strerror ( rc ) ); + } + return rc; +} + +static int test_resolve ( const char *base_uri_string, + const char *relative_uri_string, + const char *resolved_uri_string ) { + struct uri *base_uri = NULL; + struct uri *relative_uri = NULL; + struct uri *resolved_uri = NULL; + char buf[URI_MAX_LEN]; + size_t len; + int rc; + + /* Parse URIs */ + base_uri = parse_uri ( base_uri_string ); + if ( ! base_uri ) { + rc = -ENOMEM; + goto done; + } + relative_uri = parse_uri ( relative_uri_string ); + if ( ! relative_uri ) { + rc = -ENOMEM; + goto done; + } + + /* Resolve URI */ + resolved_uri = resolve_uri ( base_uri, relative_uri ); + if ( ! resolved_uri ) { + rc = -ENOMEM; + goto done; + } + + /* Compare result */ + len = unparse_uri ( buf, sizeof ( buf ), resolved_uri ); + if ( strcmp ( buf, resolved_uri_string ) != 0 ) { + printf ( "Resolution of \"%s\"+\"%s\" produced \"%s\"\n", + base_uri_string, relative_uri_string, buf ); + rc = -EINVAL; + goto done; + } + + rc = 0; + + done: + uri_put ( base_uri ); + uri_put ( relative_uri ); + uri_put ( resolved_uri ); + if ( rc ) { + printf ( "URI resolution of \"%s\"+\"%s\" failed: %s\n", + base_uri_string, relative_uri_string, + strerror ( rc ) ); + } + return rc; +} + +int uri_test ( void ) { + unsigned int i; + struct uri_test *uri_test; + int rc; + int overall_rc = 0; + + for ( i = 0 ; i < ( sizeof ( uri_tests ) / + sizeof ( uri_tests[0] ) ) ; i++ ) { + uri_test = &uri_tests[i]; + rc = test_parse_unparse ( uri_test->base_uri_string ); + if ( rc != 0 ) + overall_rc = rc; + rc = test_parse_unparse ( uri_test->relative_uri_string ); + if ( rc != 0 ) + overall_rc = rc; + rc = test_parse_unparse ( uri_test->resolved_uri_string ); + if ( rc != 0 ) + overall_rc = rc; + rc = test_resolve ( uri_test->base_uri_string, + uri_test->relative_uri_string, + uri_test->resolved_uri_string ); + if ( rc != 0 ) + overall_rc = rc; + } + + if ( overall_rc ) + printf ( "URI tests failed: %s\n", strerror ( overall_rc ) ); + return overall_rc; +} diff --git a/gpxe/src/usr/autoboot.c b/gpxe/src/usr/autoboot.c new file mode 100644 index 0000000..98e79a7 --- /dev/null +++ b/gpxe/src/usr/autoboot.c @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2006 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Automatic booting + * + */ + +/** Time to wait for link-up */ +#define LINK_WAIT_MS 15000 + +/** Shutdown flags for exit */ +int shutdown_exit_flags = 0; + +/* SAN boot protocols */ +static struct sanboot_protocol sanboot_protocols[0] \ + __table_start ( struct sanboot_protocol, sanboot_protocols ); +static struct sanboot_protocol sanboot_protocols_end[0] \ + __table_end ( struct sanboot_protocol, sanboot_protocols ); + +/** + * Identify the boot network device + * + * @ret netdev Boot network device + */ +static struct net_device * find_boot_netdev ( void ) { + return NULL; +} + +/** + * Boot using next-server and filename + * + * @v filename Boot filename + * @ret rc Return status code + */ +int boot_next_server_and_filename ( struct in_addr next_server, + const char *filename ) { + struct uri *uri; + struct image *image; + char buf[ 23 /* tftp://xxx.xxx.xxx.xxx/ */ + strlen(filename) + 1 ]; + int filename_is_absolute; + int rc; + + /* Construct URI */ + uri = parse_uri ( filename ); + if ( ! uri ) { + printf ( "Out of memory\n" ); + return -ENOMEM; + } + filename_is_absolute = uri_is_absolute ( uri ); + uri_put ( uri ); + if ( ! filename_is_absolute ) { + /* Construct a tftp:// URI for the filename. We can't + * just rely on the current working URI, because the + * relative URI resolution will remove the distinction + * between filenames with and without initial slashes, + * which is significant for TFTP. + */ + snprintf ( buf, sizeof ( buf ), "tftp://%s/%s", + inet_ntoa ( next_server ), filename ); + filename = buf; + } + + image = alloc_image(); + if ( ! image ) { + printf ( "Out of memory\n" ); + return -ENOMEM; + } + if ( ( rc = imgfetch ( image, filename, + register_and_autoload_image ) ) != 0 ) { + printf ( "Could not load %s: %s\n", + filename, strerror ( rc ) ); + goto done; + } + if ( ( rc = imgexec ( image ) ) != 0 ) { + printf ( "Could not boot %s: %s\n", + filename, strerror ( rc ) ); + goto done; + } + + done: + image_put ( image ); + return rc; +} + +/** + * Boot using root path + * + * @v root_path Root path + * @ret rc Return status code + */ +int boot_root_path ( const char *root_path ) { + struct sanboot_protocol *sanboot; + + /* Quick hack */ + for ( sanboot = sanboot_protocols ; + sanboot < sanboot_protocols_end ; sanboot++ ) { + if ( strncmp ( root_path, sanboot->prefix, + strlen ( sanboot->prefix ) ) == 0 ) { + return sanboot->boot ( root_path ); + } + } + + return -ENOTSUP; +} + +/** + * Boot from a network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int netboot ( struct net_device *netdev ) { + struct setting vendor_class_id_setting + = { .tag = DHCP_VENDOR_CLASS_ID }; + struct setting pxe_discovery_control_setting + = { .tag = DHCP_PXE_DISCOVERY_CONTROL }; + struct setting pxe_boot_menu_setting + = { .tag = DHCP_PXE_BOOT_MENU }; + char buf[256]; + struct in_addr next_server; + unsigned int pxe_discovery_control; + int rc; + + /* Open device and display device status */ + if ( ( rc = ifopen ( netdev ) ) != 0 ) + return rc; + ifstat ( netdev ); + + /* Wait for link-up */ + printf ( "Waiting for link-up on %s...", netdev->name ); + if ( ( rc = iflinkwait ( netdev, LINK_WAIT_MS ) ) != 0 ) { + printf ( " no link detected\n" ); + return rc; + } + printf ( " ok\n" ); + + /* Configure device via DHCP */ + if ( ( rc = dhcp ( netdev ) ) != 0 ) + return rc; + route(); + + /* Try PXE menu boot, if applicable */ + fetch_string_setting ( NULL, &vendor_class_id_setting, + buf, sizeof ( buf ) ); + pxe_discovery_control = + fetch_uintz_setting ( NULL, &pxe_discovery_control_setting ); + if ( ( strcmp ( buf, "PXEClient" ) == 0 ) && + setting_exists ( NULL, &pxe_boot_menu_setting ) && + ( ! ( ( pxe_discovery_control & PXEBS_SKIP ) && + setting_exists ( NULL, &filename_setting ) ) ) ) { + printf ( "Booting from PXE menu\n" ); + return pxe_menu_boot ( netdev ); + } + + /* Try to download and boot whatever we are given as a filename */ + fetch_ipv4_setting ( NULL, &next_server_setting, &next_server ); + fetch_string_setting ( NULL, &filename_setting, buf, sizeof ( buf ) ); + if ( buf[0] ) { + printf ( "Booting from filename \"%s\"\n", buf ); + return boot_next_server_and_filename ( next_server, buf ); + } + + /* No filename; try the root path */ + fetch_string_setting ( NULL, &root_path_setting, buf, sizeof ( buf ) ); + if ( buf[0] ) { + printf ( "Booting from root path \"%s\"\n", buf ); + return boot_root_path ( buf ); + } + + printf ( "No filename or root path specified\n" ); + return -ENOENT; +} + +/** + * Close all open net devices + * + * Called before a fresh boot attempt in order to free up memory. We + * don't just close the device immediately after the boot fails, + * because there may still be TCP connections in the process of + * closing. + */ +static void close_all_netdevs ( void ) { + struct net_device *netdev; + + for_each_netdev ( netdev ) { + ifclose ( netdev ); + } +} + +/** + * Boot the system + */ +void autoboot ( void ) { + struct net_device *boot_netdev; + struct net_device *netdev; + + /* If we have an identifable boot device, try that first */ + close_all_netdevs(); + if ( ( boot_netdev = find_boot_netdev() ) ) + netboot ( boot_netdev ); + + /* If that fails, try booting from any of the other devices */ + for_each_netdev ( netdev ) { + if ( netdev == boot_netdev ) + continue; + close_all_netdevs(); + netboot ( netdev ); + } + + printf ( "No more network devices\n" ); +} diff --git a/gpxe/src/usr/dhcpmgmt.c b/gpxe/src/usr/dhcpmgmt.c new file mode 100644 index 0000000..6acf7f6 --- /dev/null +++ b/gpxe/src/usr/dhcpmgmt.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * DHCP management + * + */ + +int dhcp ( struct net_device *netdev ) { + int rc; + + /* Check we can open the interface first */ + if ( ( rc = ifopen ( netdev ) ) != 0 ) + return rc; + + /* Perform DHCP */ + printf ( "DHCP (%s %s)", netdev->name, netdev_hwaddr ( netdev ) ); + if ( ( rc = start_dhcp ( &monojob, netdev ) ) == 0 ) + rc = monojob_wait ( "" ); + + return rc; +} + +int pxebs ( struct net_device *netdev, unsigned int pxe_type ) { + int rc; + + /* Perform PXE Boot Server Discovery */ + printf ( "PXEBS (%s type %d)", netdev->name, pxe_type ); + if ( ( rc = start_pxebs ( &monojob, netdev, pxe_type ) ) == 0 ) + rc = monojob_wait ( "" ); + + return rc; +} diff --git a/gpxe/src/usr/ifmgmt.c b/gpxe/src/usr/ifmgmt.c new file mode 100644 index 0000000..0ebf561 --- /dev/null +++ b/gpxe/src/usr/ifmgmt.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Network interface management + * + */ + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +int ifopen ( struct net_device *netdev ) { + int rc; + + if ( ( rc = netdev_open ( netdev ) ) != 0 ) { + printf ( "Could not open %s: %s\n", + netdev->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Close network device + * + * @v netdev Network device + */ +void ifclose ( struct net_device *netdev ) { + netdev_close ( netdev ); +} + +/** + * Print network device error breakdown + * + * @v stats Network device statistics + * @v prefix Message prefix + */ +static void ifstat_errors ( struct net_device_stats *stats, + const char *prefix ) { + unsigned int i; + + for ( i = 0 ; i < ( sizeof ( stats->errors ) / + sizeof ( stats->errors[0] ) ) ; i++ ) { + if ( stats->errors[i].count ) + printf ( " [%s: %d x \"%s\"]\n", prefix, + stats->errors[i].count, + strerror ( stats->errors[i].rc ) ); + } +} + +/** + * Print status of network device + * + * @v netdev Network device + */ +void ifstat ( struct net_device *netdev ) { + printf ( "%s: %s on %s (%s)\n" + " [Link:%s, TX:%d TXE:%d RX:%d RXE:%d]\n", + netdev->name, netdev_hwaddr ( netdev ), netdev->dev->name, + ( ( netdev->state & NETDEV_OPEN ) ? "open" : "closed" ), + ( netdev_link_ok ( netdev ) ? "up" : "down" ), + netdev->tx_stats.good, netdev->tx_stats.bad, + netdev->rx_stats.good, netdev->rx_stats.bad ); + ifstat_errors ( &netdev->tx_stats, "TXE" ); + ifstat_errors ( &netdev->rx_stats, "RXE" ); +} + +/** + * Wait for link-up + * + * @v netdev Network device + * @v max_wait_ms Maximum time to wait, in ms + */ +int iflinkwait ( struct net_device *netdev, unsigned int max_wait_ms ) { + int key; + + while ( 1 ) { + if ( netdev_link_ok ( netdev ) ) + return 0; + if ( max_wait_ms-- == 0 ) + return -ETIMEDOUT; + step(); + if ( iskey() ) { + key = getchar(); + if ( key == CTRL_C ) + return -ECANCELED; + } + mdelay ( 1 ); + } +} diff --git a/gpxe/src/usr/imgmgmt.c b/gpxe/src/usr/imgmgmt.c new file mode 100644 index 0000000..bd53d82 --- /dev/null +++ b/gpxe/src/usr/imgmgmt.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Image management + * + */ + +/** + * Fetch an image + * + * @v uri_string URI as a string (e.g. "http://www.nowhere.com/vmlinuz") + * @v name Name for image, or NULL + * @v register_image Image registration routine + * @ret rc Return status code + */ +int imgfetch ( struct image *image, const char *uri_string, + int ( * image_register ) ( struct image *image ) ) { + char uri_string_redacted[ strlen ( uri_string ) + 3 /* "***" */ + + 1 /* NUL */ ]; + struct uri *uri; + const char *password; + int rc; + + if ( ! ( uri = parse_uri ( uri_string ) ) ) + return -ENOMEM; + + image_set_uri ( image, uri ); + + /* Redact password portion of URI, if necessary */ + password = uri->password; + if ( password ) + uri->password = "***"; + unparse_uri ( uri_string_redacted, sizeof ( uri_string_redacted ), + uri ); + uri->password = password; + + if ( ( rc = create_downloader ( &monojob, image, image_register, + LOCATION_URI, uri ) ) == 0 ) + rc = monojob_wait ( uri_string_redacted ); + + uri_put ( uri ); + return rc; +} + +/** + * Load an image + * + * @v image Image + * @ret rc Return status code + */ +int imgload ( struct image *image ) { + int rc; + + /* Try to load image */ + if ( ( rc = image_autoload ( image ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Execute an image + * + * @v image Image + * @ret rc Return status code + */ +int imgexec ( struct image *image ) { + return image_exec ( image ); +} + +/** + * Identify the only loaded image + * + * @ret image Image, or NULL if 0 or >1 images are loaded + */ +struct image * imgautoselect ( void ) { + struct image *image; + struct image *selected_image = NULL; + int flagged_images = 0; + + for_each_image ( image ) { + if ( image->flags & IMAGE_LOADED ) { + selected_image = image; + flagged_images++; + } + } + + return ( ( flagged_images == 1 ) ? selected_image : NULL ); +} + +/** + * Display status of an image + * + * @v image Executable/loadable image + */ +void imgstat ( struct image *image ) { + printf ( "%s: %zd bytes", image->name, image->len ); + if ( image->type ) + printf ( " [%s]", image->type->name ); + if ( image->flags & IMAGE_LOADED ) + printf ( " [LOADED]" ); + if ( image->cmdline ) + printf ( " \"%s\"", image->cmdline ); + printf ( "\n" ); +} + +/** + * Free an image + * + * @v image Executable/loadable image + */ +void imgfree ( struct image *image ) { + unregister_image ( image ); +} diff --git a/gpxe/src/usr/pxemenu.c b/gpxe/src/usr/pxemenu.c new file mode 100644 index 0000000..793cb62 --- /dev/null +++ b/gpxe/src/usr/pxemenu.c @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2009 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * PXE Boot Menus + * + */ + +/* Colour pairs */ +#define CPAIR_NORMAL 1 +#define CPAIR_SELECT 2 + +/** A PXE boot menu item */ +struct pxe_menu_item { + /** Boot Server type */ + unsigned int type; + /** Description */ + char *desc; +}; + +/** + * A PXE boot menu + * + * This structure encapsulates the menu information provided via DHCP + * options. + */ +struct pxe_menu { + /** Prompt string (optional) */ + const char *prompt; + /** Timeout (in seconds) + * + * Negative indicates no timeout (i.e. wait indefinitely) + */ + int timeout; + /** Number of menu items */ + unsigned int num_items; + /** Selected menu item */ + unsigned int selection; + /** Menu items */ + struct pxe_menu_item items[0]; +}; + +/** + * Parse and allocate PXE boot menu + * + * @v menu PXE boot menu to fill in + * @ret rc Return status code + * + * It is the callers responsibility to eventually free the allocated + * boot menu. + */ +static int pxe_menu_parse ( struct pxe_menu **menu ) { + struct setting pxe_boot_menu_prompt_setting = + { .tag = DHCP_PXE_BOOT_MENU_PROMPT }; + struct setting pxe_boot_menu_setting = + { .tag = DHCP_PXE_BOOT_MENU }; + uint8_t raw_menu[256]; + int raw_prompt_len; + int raw_menu_len; + struct dhcp_pxe_boot_menu *raw_menu_item; + struct dhcp_pxe_boot_menu_prompt *raw_menu_prompt; + void *raw_menu_end; + unsigned int num_menu_items; + unsigned int i; + int rc; + + /* Fetch raw menu */ + memset ( raw_menu, 0, sizeof ( raw_menu ) ); + if ( ( raw_menu_len = fetch_setting ( NULL, &pxe_boot_menu_setting, + raw_menu, + sizeof ( raw_menu ) ) ) < 0 ) { + rc = raw_menu_len; + DBG ( "Could not retrieve raw PXE boot menu: %s\n", + strerror ( rc ) ); + return rc; + } + if ( raw_menu_len >= ( int ) sizeof ( raw_menu ) ) { + DBG ( "Raw PXE boot menu too large for buffer\n" ); + return -ENOSPC; + } + raw_menu_end = ( raw_menu + raw_menu_len ); + + /* Fetch raw prompt length */ + raw_prompt_len = fetch_setting_len ( NULL, + &pxe_boot_menu_prompt_setting ); + if ( raw_prompt_len < 0 ) + raw_prompt_len = 0; + + /* Count menu items */ + num_menu_items = 0; + raw_menu_item = ( ( void * ) raw_menu ); + while ( 1 ) { + if ( ( ( ( void * ) raw_menu_item ) + + sizeof ( *raw_menu_item ) ) > raw_menu_end ) + break; + if ( ( ( ( void * ) raw_menu_item ) + + sizeof ( *raw_menu_item ) + + raw_menu_item->desc_len ) > raw_menu_end ) + break; + num_menu_items++; + raw_menu_item = ( ( ( void * ) raw_menu_item ) + + sizeof ( *raw_menu_item ) + + raw_menu_item->desc_len ); + } + + /* Allocate space for parsed menu */ + *menu = zalloc ( sizeof ( **menu ) + + ( num_menu_items * sizeof ( (*menu)->items[0] ) ) + + raw_menu_len + 1 /* NUL */ + + raw_prompt_len + 1 /* NUL */ ); + if ( ! *menu ) { + DBG ( "Could not allocate PXE boot menu\n" ); + return -ENOMEM; + } + + /* Fill in parsed menu */ + (*menu)->num_items = num_menu_items; + raw_menu_item = ( ( ( void * ) (*menu) ) + sizeof ( **menu ) + + ( num_menu_items * sizeof ( (*menu)->items[0] ) ) ); + memcpy ( raw_menu_item, raw_menu, raw_menu_len ); + for ( i = 0 ; i < num_menu_items ; i++ ) { + (*menu)->items[i].type = ntohs ( raw_menu_item->type ); + (*menu)->items[i].desc = raw_menu_item->desc; + /* Set type to 0; this ensures that the description + * for the previous menu item is NUL-terminated. + * (Final item is NUL-terminated anyway.) + */ + raw_menu_item->type = 0; + raw_menu_item = ( ( ( void * ) raw_menu_item ) + + sizeof ( *raw_menu_item ) + + raw_menu_item->desc_len ); + } + if ( raw_prompt_len ) { + raw_menu_prompt = ( ( ( void * ) raw_menu_item ) + + 1 /* NUL */ ); + fetch_setting ( NULL, &pxe_boot_menu_prompt_setting, + raw_menu_prompt, raw_prompt_len ); + (*menu)->timeout = + ( ( raw_menu_prompt->timeout == 0xff ) ? + -1 : raw_menu_prompt->timeout ); + (*menu)->prompt = raw_menu_prompt->prompt; + } else { + (*menu)->timeout = -1; + } + + return 0; +} + +/** + * Draw PXE boot menu item + * + * @v menu PXE boot menu + * @v index Index of item to draw + * @v selected Item is selected + */ +static void pxe_menu_draw_item ( struct pxe_menu *menu, + unsigned int index, int selected ) { + char buf[COLS+1]; + size_t len; + unsigned int row; + + /* Prepare space-padded row content */ + len = snprintf ( buf, sizeof ( buf ), " %c. %s", + ( 'A' + index ), menu->items[index].desc ); + while ( len < ( sizeof ( buf ) - 1 ) ) + buf[len++] = ' '; + buf[ sizeof ( buf ) - 1 ] = '\0'; + + /* Draw row */ + row = ( LINES - menu->num_items + index ); + color_set ( ( selected ? CPAIR_SELECT : CPAIR_NORMAL ), NULL ); + mvprintw ( row, 0, "%s", buf ); + move ( row, 1 ); +} + +/** + * Make selection from PXE boot menu + * + * @v menu PXE boot menu + * @ret rc Return status code + */ +static int pxe_menu_select ( struct pxe_menu *menu ) { + int key; + unsigned int key_selection; + unsigned int i; + int rc = 0; + + /* Initialise UI */ + initscr(); + start_color(); + init_pair ( CPAIR_NORMAL, COLOR_WHITE, COLOR_BLACK ); + init_pair ( CPAIR_SELECT, COLOR_BLACK, COLOR_WHITE ); + color_set ( CPAIR_NORMAL, NULL ); + + /* Draw initial menu */ + for ( i = 0 ; i < menu->num_items ; i++ ) + printf ( "\n" ); + for ( i = 0 ; i < menu->num_items ; i++ ) + pxe_menu_draw_item ( menu, ( menu->num_items - i - 1 ), 0 ); + + while ( 1 ) { + + /* Highlight currently selected item */ + pxe_menu_draw_item ( menu, menu->selection, 1 ); + + /* Wait for keyboard input */ + while ( ! iskey() ) + step(); + key = getkey(); + + /* Unhighlight currently selected item */ + pxe_menu_draw_item ( menu, menu->selection, 0 ); + + /* Act upon key */ + if ( ( key == CR ) || ( key == LF ) ) { + pxe_menu_draw_item ( menu, menu->selection, 1 ); + break; + } else if ( ( key == CTRL_C ) || ( key == ESC ) ) { + rc = -ECANCELED; + break; + } else if ( key == KEY_UP ) { + if ( menu->selection > 0 ) + menu->selection--; + } else if ( key == KEY_DOWN ) { + if ( menu->selection < ( menu->num_items - 1 ) ) + menu->selection++; + } else if ( ( key < KEY_MIN ) && + ( ( key_selection = ( toupper ( key ) - 'A' ) ) + < menu->num_items ) ) { + menu->selection = key_selection; + pxe_menu_draw_item ( menu, menu->selection, 1 ); + break; + } + } + + /* Shut down UI */ + endwin(); + + return rc; +} + +/** + * Prompt for (and make selection from) PXE boot menu + * + * @v menu PXE boot menu + * @ret rc Return status code + */ +static int pxe_menu_prompt_and_select ( struct pxe_menu *menu ) { + unsigned long start = currticks(); + unsigned long now; + unsigned long elapsed; + size_t len = 0; + int key; + int rc = 0; + + /* Display menu immediately, if specified to do so */ + if ( menu->timeout < 0 ) { + if ( menu->prompt ) + printf ( "%s\n", menu->prompt ); + return pxe_menu_select ( menu ); + } + + /* Display prompt, if specified */ + if ( menu->prompt ) + printf ( "%s", menu->prompt ); + + /* Wait for timeout, if specified */ + while ( menu->timeout > 0 ) { + if ( ! len ) + len = printf ( " (%d)", menu->timeout ); + if ( iskey() ) { + key = getkey(); + if ( key == KEY_F8 ) { + /* Display menu */ + printf ( "\n" ); + return pxe_menu_select ( menu ); + } else if ( ( key == CTRL_C ) || ( key == ESC ) ) { + /* Abort */ + rc = -ECANCELED; + break; + } else { + /* Stop waiting */ + break; + } + } + now = currticks(); + elapsed = ( now - start ); + if ( elapsed >= TICKS_PER_SEC ) { + menu->timeout -= 1; + do { + printf ( "\b \b" ); + } while ( --len ); + start = now; + } + } + + /* Return with default option selected */ + printf ( "\n" ); + return rc; +} + +/** + * Boot using PXE boot menu + * + * @ret rc Return status code + * + * Note that a success return status indicates that a PXE boot menu + * item has been selected, and that the DHCP session should perform a + * boot server request/ack. + */ +int pxe_menu_boot ( struct net_device *netdev ) { + struct pxe_menu *menu; + unsigned int pxe_type; + struct settings *pxebs_settings; + struct in_addr next_server; + char filename[256]; + int rc; + + /* Parse and allocate boot menu */ + if ( ( rc = pxe_menu_parse ( &menu ) ) != 0 ) + return rc; + + /* Make selection from boot menu */ + if ( ( rc = pxe_menu_prompt_and_select ( menu ) ) != 0 ) { + free ( menu ); + return rc; + } + pxe_type = menu->items[menu->selection].type; + + /* Free boot menu */ + free ( menu ); + + /* Return immediately if local boot selected */ + if ( ! pxe_type ) + return 0; + + /* Attempt PXE Boot Server Discovery */ + if ( ( rc = pxebs ( netdev, pxe_type ) ) != 0 ) + return rc; + + /* Attempt boot */ + pxebs_settings = find_settings ( PXEBS_SETTINGS_NAME ); + assert ( pxebs_settings ); + fetch_ipv4_setting ( pxebs_settings, &next_server_setting, + &next_server ); + fetch_string_setting ( pxebs_settings, &filename_setting, + filename, sizeof ( filename ) ); + return boot_next_server_and_filename ( next_server, filename ); +} diff --git a/gpxe/src/usr/route.c b/gpxe/src/usr/route.c new file mode 100644 index 0000000..f3a49f4 --- /dev/null +++ b/gpxe/src/usr/route.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2007 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. + */ + +#include +#include +#include +#include + +/** @file + * + * Routing table management + * + */ + +void route ( void ) { + struct ipv4_miniroute *miniroute; + + list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) { + printf ( "%s: %s/", miniroute->netdev->name, + inet_ntoa ( miniroute->address ) ); + printf ( "%s", inet_ntoa ( miniroute->netmask ) ); + if ( miniroute->gateway.s_addr != INADDR_NONE ) + printf ( " gw %s", inet_ntoa ( miniroute->gateway ) ); + if ( ! ( miniroute->netdev->state & NETDEV_OPEN ) ) + printf ( " (inaccessible)" ); + printf ( "\n" ); + } +} diff --git a/gpxe/src/util/.gitignore b/gpxe/src/util/.gitignore new file mode 100644 index 0000000..c256701 --- /dev/null +++ b/gpxe/src/util/.gitignore @@ -0,0 +1,7 @@ +nrv2b +zbin +hijack +prototester +elf2efi32 +elf2efi64 +efirom diff --git a/gpxe/src/util/Makefile b/gpxe/src/util/Makefile new file mode 100644 index 0000000..d72661e --- /dev/null +++ b/gpxe/src/util/Makefile @@ -0,0 +1,22 @@ +BLIB = ../bin/blib.a +CFLAGS = -Os + +all : hijack prototester mucurses_test + +hijack : hijack.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -Wall -lpcap -o $@ $< + +prototester.o : prototester.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -Wall -o $@ -c $< -idirafter ../include + +prototester : prototester.o $(BLIB) + $(CC) -o $@ $< -lc $(BLIB) + +mucurses_test.o : mucurses_test.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -Wall -o $@ -c $< + +mucurses_test : mucurses_test.o $(BLIB) + $(CC) -o $@ $< -lc $(BLIB) + +clean : + rm -f hijack prototester mucurses_test *.o diff --git a/gpxe/src/util/Option/ROM.pm b/gpxe/src/util/Option/ROM.pm new file mode 100644 index 0000000..a86d326 --- /dev/null +++ b/gpxe/src/util/Option/ROM.pm @@ -0,0 +1,501 @@ +package Option::ROM; + +# Copyright (C) 2008 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. + +=head1 NAME + +Option::ROM - Option ROM manipulation + +=head1 SYNOPSIS + + use Option::ROM; + + # Load a ROM image + my $rom = new Option::ROM; + $rom->load ( "rtl8139.rom" ); + + # Modify the PCI device ID + $rom->pci_header->{device_id} = 0x1234; + $rom->fix_checksum(); + + # Write ROM image out to a new file + $rom->save ( "rtl8139-modified.rom" ); + +=head1 DESCRIPTION + +C provides a mechanism for manipulating Option ROM +images. + +=head1 METHODS + +=cut + +############################################################################## +# +# Option::ROM::Fields +# +############################################################################## + +package Option::ROM::Fields; + +use strict; +use warnings; +use Carp; +use bytes; + +sub TIEHASH { + my $class = shift; + my $self = shift; + + bless $self, $class; + return $self; +} + +sub FETCH { + my $self = shift; + my $key = shift; + + return undef unless $self->EXISTS ( $key ); + my $raw = substr ( ${$self->{data}}, + ( $self->{offset} + $self->{fields}->{$key}->{offset} ), + $self->{fields}->{$key}->{length} ); + my $unpack = ( ref $self->{fields}->{$key}->{unpack} ? + $self->{fields}->{$key}->{unpack} : + sub { unpack ( $self->{fields}->{$key}->{pack}, shift ); } ); + return &$unpack ( $raw ); +} + +sub STORE { + my $self = shift; + my $key = shift; + my $value = shift; + + croak "Nonexistent field \"$key\"" unless $self->EXISTS ( $key ); + my $pack = ( ref $self->{fields}->{$key}->{pack} ? + $self->{fields}->{$key}->{pack} : + sub { pack ( $self->{fields}->{$key}->{pack}, shift ); } ); + my $raw = &$pack ( $value ); + substr ( ${$self->{data}}, + ( $self->{offset} + $self->{fields}->{$key}->{offset} ), + $self->{fields}->{$key}->{length} ) = $raw; +} + +sub DELETE { + my $self = shift; + my $key = shift; + + $self->STORE ( $key, 0 ); +} + +sub CLEAR { + my $self = shift; + + foreach my $key ( keys %{$self->{fields}} ) { + $self->DELETE ( $key ); + } +} + +sub EXISTS { + my $self = shift; + my $key = shift; + + return ( exists $self->{fields}->{$key} && + ( ( $self->{fields}->{$key}->{offset} + + $self->{fields}->{$key}->{length} ) <= $self->{length} ) ); +} + +sub FIRSTKEY { + my $self = shift; + + keys %{$self->{fields}}; + return each %{$self->{fields}}; +} + +sub NEXTKEY { + my $self = shift; + my $lastkey = shift; + + return each %{$self->{fields}}; +} + +sub SCALAR { + my $self = shift; + + return 1; +} + +sub UNTIE { + my $self = shift; +} + +sub DESTROY { + my $self = shift; +} + +sub checksum { + my $self = shift; + + my $raw = substr ( ${$self->{data}}, $self->{offset}, $self->{length} ); + return unpack ( "%8C*", $raw ); +} + +############################################################################## +# +# Option::ROM +# +############################################################################## + +package Option::ROM; + +use strict; +use warnings; +use Carp; +use bytes; +use Exporter 'import'; + +use constant ROM_SIGNATURE => 0xaa55; +use constant PCI_SIGNATURE => 'PCIR'; +use constant PNP_SIGNATURE => '$PnP'; + +our @EXPORT_OK = qw ( ROM_SIGNATURE PCI_SIGNATURE PNP_SIGNATURE ); +our %EXPORT_TAGS = ( all => [ @EXPORT_OK ] ); + +use constant JMP_SHORT => 0xeb; +use constant JMP_NEAR => 0xe9; + +sub pack_init { + my $dest = shift; + + # Always create a near jump; it's simpler + if ( $dest ) { + return pack ( "CS", JMP_NEAR, ( $dest - 6 ) ); + } else { + return pack ( "CS", 0, 0 ); + } +} + +sub unpack_init { + my $instr = shift; + + # Accept both short and near jumps + my $jump = unpack ( "C", $instr ); + if ( $jump == JMP_SHORT ) { + my $offset = unpack ( "xC", $instr ); + return ( $offset + 5 ); + } elsif ( $jump == JMP_NEAR ) { + my $offset = unpack ( "xS", $instr ); + return ( $offset + 6 ); + } elsif ( $jump == 0 ) { + return 0; + } else { + croak "Unrecognised jump instruction in init vector\n"; + } +} + +=pod + +=item C<< new () >> + +Construct a new C object. + +=cut + +sub new { + my $class = shift; + + my $hash = {}; + tie %$hash, "Option::ROM::Fields", { + data => undef, + offset => 0x00, + length => 0x20, + fields => { + signature => { offset => 0x00, length => 0x02, pack => "S" }, + length => { offset => 0x02, length => 0x01, pack => "C" }, + # "init" is part of a jump instruction + init => { offset => 0x03, length => 0x03, + pack => \&pack_init, unpack => \&unpack_init }, + checksum => { offset => 0x06, length => 0x01, pack => "C" }, + bofm_header => { offset => 0x14, length => 0x02, pack => "S" }, + undi_header => { offset => 0x16, length => 0x02, pack => "S" }, + pci_header => { offset => 0x18, length => 0x02, pack => "S" }, + pnp_header => { offset => 0x1a, length => 0x02, pack => "S" }, + }, + }; + bless $hash, $class; + return $hash; +} + +=pod + +=item C<< load ( $filename ) >> + +Load option ROM contents from the file C<$filename>. + +=cut + +sub load { + my $hash = shift; + my $self = tied(%$hash); + my $filename = shift; + + $self->{filename} = $filename; + + open my $fh, "<$filename" + or croak "Cannot open $filename for reading: $!"; + read $fh, my $data, ( 128 * 1024 ); # 128kB is theoretical max size + $self->{data} = \$data; + close $fh; +} + +=pod + +=item C<< save ( [ $filename ] ) >> + +Write the ROM data back out to the file C<$filename>. If C<$filename> +is omitted, the file used in the call to C will be used. + +=cut + +sub save { + my $hash = shift; + my $self = tied(%$hash); + my $filename = shift; + + $filename ||= $self->{filename}; + + open my $fh, ">$filename" + or croak "Cannot open $filename for writing: $!"; + print $fh ${$self->{data}}; + close $fh; +} + +=pod + +=item C<< length () >> + +Length of option ROM data. This is the length of the file, not the +length from the ROM header length field. + +=cut + +sub length { + my $hash = shift; + my $self = tied(%$hash); + + return length ${$self->{data}}; +} + +=pod + +=item C<< pci_header () >> + +Return a C object representing the ROM's PCI header, +if present. + +=cut + +sub pci_header { + my $hash = shift; + my $self = tied(%$hash); + + my $offset = $hash->{pci_header}; + return undef unless $offset != 0; + + return Option::ROM::PCI->new ( $self->{data}, $offset ); +} + +=pod + +=item C<< pnp_header () >> + +Return a C object representing the ROM's PnP header, +if present. + +=cut + +sub pnp_header { + my $hash = shift; + my $self = tied(%$hash); + + my $offset = $hash->{pnp_header}; + return undef unless $offset != 0; + + return Option::ROM::PnP->new ( $self->{data}, $offset ); +} + +=pod + +=item C<< checksum () >> + +Calculate the byte checksum of the ROM. + +=cut + +sub checksum { + my $hash = shift; + my $self = tied(%$hash); + + return unpack ( "%8C*", ${$self->{data}} ); +} + +=pod + +=item C<< fix_checksum () >> + +Fix the byte checksum of the ROM. + +=cut + +sub fix_checksum { + my $hash = shift; + my $self = tied(%$hash); + + $hash->{checksum} = ( ( $hash->{checksum} - $hash->checksum() ) & 0xff ); +} + +############################################################################## +# +# Option::ROM::PCI +# +############################################################################## + +package Option::ROM::PCI; + +use strict; +use warnings; +use Carp; +use bytes; + +sub new { + my $class = shift; + my $data = shift; + my $offset = shift; + + my $hash = {}; + tie %$hash, "Option::ROM::Fields", { + data => $data, + offset => $offset, + length => 0x0c, + fields => { + signature => { offset => 0x00, length => 0x04, pack => "a4" }, + vendor_id => { offset => 0x04, length => 0x02, pack => "S" }, + device_id => { offset => 0x06, length => 0x02, pack => "S" }, + device_list => { offset => 0x08, length => 0x02, pack => "S" }, + struct_length => { offset => 0x0a, length => 0x02, pack => "S" }, + struct_revision =>{ offset => 0x0c, length => 0x01, pack => "C" }, + base_class => { offset => 0x0d, length => 0x01, pack => "C" }, + sub_class => { offset => 0x0e, length => 0x01, pack => "C" }, + prog_intf => { offset => 0x0f, length => 0x01, pack => "C" }, + image_length => { offset => 0x10, length => 0x02, pack => "S" }, + revision => { offset => 0x12, length => 0x02, pack => "S" }, + code_type => { offset => 0x14, length => 0x01, pack => "C" }, + last_image => { offset => 0x15, length => 0x01, pack => "C" }, + runtime_length => { offset => 0x16, length => 0x02, pack => "S" }, + conf_header => { offset => 0x18, length => 0x02, pack => "S" }, + clp_entry => { offset => 0x1a, length => 0x02, pack => "S" }, + }, + }; + bless $hash, $class; + + # Retrieve true length of structure + my $self = tied ( %$hash ); + $self->{length} = $hash->{struct_length}; + + return $hash; +} + +############################################################################## +# +# Option::ROM::PnP +# +############################################################################## + +package Option::ROM::PnP; + +use strict; +use warnings; +use Carp; +use bytes; + +sub new { + my $class = shift; + my $data = shift; + my $offset = shift; + + my $hash = {}; + tie %$hash, "Option::ROM::Fields", { + data => $data, + offset => $offset, + length => 0x06, + fields => { + signature => { offset => 0x00, length => 0x04, pack => "a4" }, + struct_revision =>{ offset => 0x04, length => 0x01, pack => "C" }, + struct_length => { offset => 0x05, length => 0x01, pack => "C" }, + checksum => { offset => 0x09, length => 0x01, pack => "C" }, + manufacturer => { offset => 0x0e, length => 0x02, pack => "S" }, + product => { offset => 0x10, length => 0x02, pack => "S" }, + bcv => { offset => 0x16, length => 0x02, pack => "S" }, + bdv => { offset => 0x18, length => 0x02, pack => "S" }, + bev => { offset => 0x1a, length => 0x02, pack => "S" }, + }, + }; + bless $hash, $class; + + # Retrieve true length of structure + my $self = tied ( %$hash ); + $self->{length} = ( $hash->{struct_length} * 16 ); + + return $hash; +} + +sub checksum { + my $hash = shift; + my $self = tied(%$hash); + + return $self->checksum(); +} + +sub fix_checksum { + my $hash = shift; + my $self = tied(%$hash); + + $hash->{checksum} = ( ( $hash->{checksum} - $hash->checksum() ) & 0xff ); +} + +sub manufacturer { + my $hash = shift; + my $self = tied(%$hash); + + my $manufacturer = $hash->{manufacturer}; + return undef unless $manufacturer; + + my $raw = substr ( ${$self->{data}}, $manufacturer ); + return unpack ( "Z*", $raw ); +} + +sub product { + my $hash = shift; + my $self = tied(%$hash); + + my $product = $hash->{product}; + return undef unless $product; + + my $raw = substr ( ${$self->{data}}, $product ); + return unpack ( "Z*", $raw ); +} + +1; diff --git a/gpxe/src/util/catrom.pl b/gpxe/src/util/catrom.pl new file mode 100755 index 0000000..fe37e6b --- /dev/null +++ b/gpxe/src/util/catrom.pl @@ -0,0 +1,48 @@ +#!/usr/bin/perl -w + +use warnings; +use strict; + +use bytes; + +use constant MAX_ROM_LEN => 1024*1024; +use constant PCI_OFF => 0x18; +use constant INDICATOR_OFF => 0x15; + +my $total_len = 0; +my @romfiles = @ARGV + or die "Usage: $0 rom-file-1 rom-file-2 ... > multi-rom-file\n"; + +while ( my $romfile = shift @romfiles ) { + my $last = @romfiles ? 0 : 1; + + open ROM, "<$romfile" or die "Could not open $romfile: $!\n"; + my $len = read ( ROM, my $romdata, MAX_ROM_LEN ) + or die "Could not read $romfile: $!\n"; + close ROM; + + die "$romfile is not a ROM file\n" + unless substr ( $romdata, 0, 2 ) eq "\x55\xAA"; + + ( my $checklen ) = unpack ( 'C', substr ( $romdata, 2, 1 ) ); + $checklen *= 512; + die "$romfile has incorrect length field $checklen (should be $len)\n" + unless $len == $checklen; + + ( my $pci ) = unpack ( 'v', substr ( $romdata, PCI_OFF, 2 ) ); + die "Invalid PCI offset field in $romfile\n" + if $pci >= $len; + die "No PCIR signature in $romfile\n" + unless substr ( $romdata, $pci, 4 ) eq "PCIR"; + + ( my $indicator ) = + unpack ( 'C', substr ( $romdata, $pci + INDICATOR_OFF, 1 ) ); + my $msg = sprintf ( "$romfile: indicator was %02x, ", $indicator ); + $indicator &= ! ( 1 << 7 ); + $indicator |= ( $last << 7 ); + $msg .= sprintf ( "now %02x\n", $indicator ); + substr ( $romdata, $pci + INDICATOR_OFF, 1 ) = pack ( 'C', $indicator ); + warn $msg; + + print $romdata; +} diff --git a/gpxe/src/util/disrom.pl b/gpxe/src/util/disrom.pl new file mode 100755 index 0000000..1fb4cc3 --- /dev/null +++ b/gpxe/src/util/disrom.pl @@ -0,0 +1,81 @@ +#!/usr/bin/perl -w +# +# Copyright (C) 2008 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. + +use strict; +use warnings; + +use FindBin; +use lib "$FindBin::Bin"; +use Option::ROM qw ( :all ); + +my $romfile = shift || "-"; +my $rom = new Option::ROM; +$rom->load ( $romfile ); + +die "Not an option ROM image\n" + unless $rom->{signature} == ROM_SIGNATURE; + +my $romlength = ( $rom->{length} * 512 ); +my $filelength = $rom->length; +die "ROM image truncated (is $filelength, should be $romlength)\n" + if $filelength < $romlength; + +printf "ROM header:\n\n"; +printf " %-16s 0x%02x (%d)\n", "Length:", $rom->{length}, ( $rom->{length} * 512 ); +printf " %-16s 0x%02x (%s0x%02x)\n", "Checksum:", $rom->{checksum}, + ( ( $rom->checksum == 0 ) ? "" : "INCORRECT: " ), $rom->checksum; +printf " %-16s 0x%04x\n", "Init:", $rom->{init}; +printf " %-16s 0x%04x\n", "UNDI header:", $rom->{undi_header}; +printf " %-16s 0x%04x\n", "PCI header:", $rom->{pci_header}; +printf " %-16s 0x%04x\n", "PnP header:", $rom->{pnp_header}; +printf "\n"; + +my $pci = $rom->pci_header(); +if ( $pci ) { + printf "PCI header:\n\n"; + printf " %-16s %s\n", "Signature:", $pci->{signature}; + printf " %-16s 0x%04x\n", "Vendor ID:", $pci->{vendor_id}; + printf " %-16s 0x%04x\n", "Device ID:", $pci->{device_id}; + printf " %-16s 0x%02x%02x%02x\n", "Device class:", + $pci->{base_class}, $pci->{sub_class}, $pci->{prog_intf}; + printf " %-16s 0x%04x (%d)\n", "Image length:", + $pci->{image_length}, ( $pci->{image_length} * 512 ); + printf " %-16s 0x%04x (%d)\n", "Runtime length:", + $pci->{runtime_length}, ( $pci->{runtime_length} * 512 ); + if ( exists $pci->{conf_header} ) { + printf " %-16s 0x%04x\n", "Config header:", $pci->{conf_header}; + printf " %-16s 0x%04x\n", "CLP entry:", $pci->{clp_entry}; + } + printf "\n"; +} + +my $pnp = $rom->pnp_header(); +if ( $pnp ) { + printf "PnP header:\n\n"; + printf " %-16s %s\n", "Signature:", $pnp->{signature}; + printf " %-16s 0x%02x (%s0x%02x)\n", "Checksum:", $pnp->{checksum}, + ( ( $pnp->checksum == 0 ) ? "" : "INCORRECT: " ), $pnp->checksum; + printf " %-16s 0x%04x \"%s\"\n", "Manufacturer:", + $pnp->{manufacturer}, $pnp->manufacturer; + printf " %-16s 0x%04x \"%s\"\n", "Product:", + $pnp->{product}, $pnp->product; + printf " %-16s 0x%04x\n", "BCV:", $pnp->{bcv}; + printf " %-16s 0x%04x\n", "BDV:", $pnp->{bdv}; + printf " %-16s 0x%04x\n", "BEV:", $pnp->{bev}; + printf "\n"; +} diff --git a/gpxe/src/util/dskpad.pl b/gpxe/src/util/dskpad.pl new file mode 100755 index 0000000..3ae325e --- /dev/null +++ b/gpxe/src/util/dskpad.pl @@ -0,0 +1,12 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; + +use constant FLOPPYSIZE => 1440 * 1024; + +while ( my $filename = shift ) { + die "$filename is not a file\n" unless -f $filename; + die "$filename is too large\n" unless ( -s $filename <= FLOPPYSIZE ); + truncate $filename, FLOPPYSIZE or die "Could not truncate: $!\n"; +} diff --git a/gpxe/src/util/efirom.c b/gpxe/src/util/efirom.c new file mode 100644 index 0000000..9369db8 --- /dev/null +++ b/gpxe/src/util/efirom.c @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2009 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define eprintf(...) fprintf ( stderr, __VA_ARGS__ ) + +/** Command-line options */ +struct options { + uint16_t vendor; + uint16_t device; +}; + +/** + * Allocate memory + * + * @v len Length of memory to allocate + * @ret ptr Pointer to allocated memory + */ +static void * xmalloc ( size_t len ) { + void *ptr; + + ptr = malloc ( len ); + if ( ! ptr ) { + eprintf ( "Could not allocate %zd bytes\n", len ); + exit ( 1 ); + } + + return ptr; +} + +/** + * Get file size + * + * @v file File + * @v len File size + */ +static size_t file_size ( FILE *file ) { + ssize_t len; + + return len; +} + +/** + * Read information from PE headers + * + * @v pe PE file + * @ret machine Machine type + * @ret subsystem EFI subsystem + */ +static void read_pe_info ( void *pe, uint16_t *machine, + uint16_t *subsystem ) { + EFI_IMAGE_DOS_HEADER *dos; + union { + EFI_IMAGE_NT_HEADERS32 nt32; + EFI_IMAGE_NT_HEADERS64 nt64; + } *nt; + + /* Locate NT header */ + dos = pe; + nt = ( pe + dos->e_lfanew ); + + /* Parse out PE information */ + *machine = nt->nt32.FileHeader.Machine; + switch ( *machine ) { + case EFI_IMAGE_MACHINE_IA32: + *subsystem = nt->nt32.OptionalHeader.Subsystem; + break; + case EFI_IMAGE_MACHINE_X64: + *subsystem = nt->nt64.OptionalHeader.Subsystem; + break; + default: + eprintf ( "Unrecognised machine type %04x\n", *machine ); + exit ( 1 ); + } +} + +/** + * Convert EFI image to ROM image + * + * @v pe EFI file + * @v rom ROM file + */ +static void make_efi_rom ( FILE *pe, FILE *rom, struct options *opts ) { + struct { + EFI_PCI_EXPANSION_ROM_HEADER rom; + PCI_DATA_STRUCTURE pci __attribute__ (( aligned ( 4 ) )); + uint8_t checksum; + } *headers; + struct stat pe_stat; + size_t pe_size; + size_t rom_size; + void *buf; + void *payload; + unsigned int i; + uint8_t checksum; + + /* Determine PE file size */ + if ( fstat ( fileno ( pe ), &pe_stat ) != 0 ) { + eprintf ( "Could not stat PE file: %s\n", + strerror ( errno ) ); + exit ( 1 ); + } + pe_size = pe_stat.st_size; + + /* Determine ROM file size */ + rom_size = ( ( pe_size + sizeof ( *headers ) + 511 ) & ~511 ); + + /* Allocate ROM buffer and read in PE file */ + buf = xmalloc ( rom_size ); + memset ( buf, 0, rom_size ); + headers = buf; + payload = ( buf + sizeof ( *headers ) ); + if ( fread ( payload, pe_size, 1, pe ) != 1 ) { + eprintf ( "Could not read PE file: %s\n", + strerror ( errno ) ); + exit ( 1 ); + } + + /* Construct ROM header */ + headers->rom.Signature = PCI_EXPANSION_ROM_HEADER_SIGNATURE; + headers->rom.InitializationSize = ( rom_size / 512 ); + headers->rom.EfiSignature = EFI_PCI_EXPANSION_ROM_HEADER_EFISIGNATURE; + read_pe_info ( payload, &headers->rom.EfiMachineType, + &headers->rom.EfiSubsystem ); + headers->rom.EfiImageHeaderOffset = sizeof ( *headers ); + headers->rom.PcirOffset = + offsetof ( typeof ( *headers ), pci ); + headers->pci.Signature = PCI_DATA_STRUCTURE_SIGNATURE; + headers->pci.VendorId = opts->vendor; + headers->pci.DeviceId = opts->device; + headers->pci.Length = sizeof ( headers->pci ); + headers->pci.ClassCode[0] = PCI_CLASS_NETWORK; + headers->pci.ImageLength = ( rom_size / 512 ); + headers->pci.CodeType = 0x03; /* No constant in EFI headers? */ + headers->pci.Indicator = 0x80; /* No constant in EFI headers? */ + + /* Fix image checksum */ + for ( i = 0, checksum = 0 ; i < rom_size ; i++ ) + checksum += *( ( uint8_t * ) buf + i ); + headers->checksum -= checksum; + + /* Write out ROM */ + if ( fwrite ( buf, rom_size, 1, rom ) != 1 ) { + eprintf ( "Could not write ROM file: %s\n", + strerror ( errno ) ); + exit ( 1 ); + } +} + +/** + * Print help + * + * @v program_name Program name + */ +static void print_help ( const char *program_name ) { + eprintf ( "Syntax: %s [--vendor=VVVV] [--device=DDDD] " + "infile outfile\n", program_name ); +} + +/** + * Parse command-line options + * + * @v argc Argument count + * @v argv Argument list + * @v opts Options structure to populate + */ +static int parse_options ( const int argc, char **argv, + struct options *opts ) { + char *end; + int c; + + while (1) { + int option_index = 0; + static struct option long_options[] = { + { "vendor", required_argument, NULL, 'v' }, + { "device", required_argument, NULL, 'd' }, + { "help", 0, NULL, 'h' }, + { 0, 0, 0, 0 } + }; + + if ( ( c = getopt_long ( argc, argv, "v:d:h", + long_options, + &option_index ) ) == -1 ) { + break; + } + + switch ( c ) { + case 'v': + opts->vendor = strtoul ( optarg, &end, 16 ); + if ( *end ) { + eprintf ( "Invalid vendor \"%s\"\n", optarg ); + exit ( 2 ); + } + break; + case 'd': + opts->device = strtoul ( optarg, &end, 16 ); + if ( *end ) { + eprintf ( "Invalid device \"%s\"\n", optarg ); + exit ( 2 ); + } + break; + case 'h': + print_help ( argv[0] ); + exit ( 0 ); + case '?': + default: + exit ( 2 ); + } + } + return optind; +} + +int main ( int argc, char **argv ) { + struct options opts = { + }; + unsigned int infile_index; + const char *infile_name; + const char *outfile_name; + FILE *infile; + FILE *outfile; + + /* Parse command-line arguments */ + infile_index = parse_options ( argc, argv, &opts ); + if ( argc != ( infile_index + 2 ) ) { + print_help ( argv[0] ); + exit ( 2 ); + } + infile_name = argv[infile_index]; + outfile_name = argv[infile_index + 1]; + + /* Open input and output files */ + infile = fopen ( infile_name, "r" ); + if ( ! infile ) { + eprintf ( "Could not open %s for reading: %s\n", + infile_name, strerror ( errno ) ); + exit ( 1 ); + } + outfile = fopen ( outfile_name, "w" ); + if ( ! outfile ) { + eprintf ( "Could not open %s for writing: %s\n", + outfile_name, strerror ( errno ) ); + exit ( 1 ); + } + + /* Convert file */ + make_efi_rom ( infile, outfile, &opts ); + + fclose ( outfile ); + fclose ( infile ); + + return 0; +} diff --git a/gpxe/src/util/elf2efi.c b/gpxe/src/util/elf2efi.c new file mode 100644 index 0000000..886777d --- /dev/null +++ b/gpxe/src/util/elf2efi.c @@ -0,0 +1,803 @@ +/* + * Copyright (C) 2009 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. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define eprintf(...) fprintf ( stderr, __VA_ARGS__ ) + +#define EFI_FILE_ALIGN 0x20 + +struct pe_section { + struct pe_section *next; + EFI_IMAGE_SECTION_HEADER hdr; + uint8_t contents[0]; +}; + +struct pe_relocs { + struct pe_relocs *next; + unsigned long start_rva; + unsigned int used_relocs; + unsigned int total_relocs; + uint16_t *relocs; +}; + +struct pe_header { + EFI_IMAGE_DOS_HEADER dos; + uint8_t padding[128]; +#if defined(MDE_CPU_IA32) + EFI_IMAGE_NT_HEADERS32 nt; +#elif defined(MDE_CPU_X64) + EFI_IMAGE_NT_HEADERS64 nt; +#endif +}; + +static struct pe_header efi_pe_header = { + .dos = { + .e_magic = EFI_IMAGE_DOS_SIGNATURE, + .e_lfanew = offsetof ( typeof ( efi_pe_header ), nt ), + }, + .nt = { + .Signature = EFI_IMAGE_NT_SIGNATURE, + .FileHeader = { +#if defined(MDE_CPU_IA32) + .Machine = EFI_IMAGE_MACHINE_IA32, +#elif defined(MDE_CPU_X64) + .Machine = EFI_IMAGE_MACHINE_X64, +#endif + .TimeDateStamp = 0x10d1a884, + .SizeOfOptionalHeader = + sizeof ( efi_pe_header.nt.OptionalHeader ), + .Characteristics = ( EFI_IMAGE_FILE_DLL | +#if defined(MDE_CPU_IA32) + EFI_IMAGE_FILE_32BIT_MACHINE | +#endif + EFI_IMAGE_FILE_EXECUTABLE_IMAGE ), + }, + .OptionalHeader = { +#if defined(MDE_CPU_IA32) + .Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC, +#elif defined(MDE_CPU_X64) + .Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC, +#endif + .SectionAlignment = EFI_FILE_ALIGN, + .FileAlignment = EFI_FILE_ALIGN, + .SizeOfImage = sizeof ( efi_pe_header ), + .SizeOfHeaders = sizeof ( efi_pe_header ), + .NumberOfRvaAndSizes = + EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES, + }, + }, +}; + +/** Command-line options */ +struct options { + unsigned int subsystem; +}; + +/** + * Allocate memory + * + * @v len Length of memory to allocate + * @ret ptr Pointer to allocated memory + */ +static void * xmalloc ( size_t len ) { + void *ptr; + + ptr = malloc ( len ); + if ( ! ptr ) { + eprintf ( "Could not allocate %zd bytes\n", len ); + exit ( 1 ); + } + + return ptr; +} + +/** + * Align section within PE file + * + * @v offset Unaligned offset + * @ret aligned_offset Aligned offset + */ +static unsigned long efi_file_align ( unsigned long offset ) { + return ( ( offset + EFI_FILE_ALIGN - 1 ) & ~( EFI_FILE_ALIGN - 1 ) ); +} + +/** + * Generate entry in PE relocation table + * + * @v pe_reltab PE relocation table + * @v rva RVA + * @v size Size of relocation entry + */ +static void generate_pe_reloc ( struct pe_relocs **pe_reltab, + unsigned long rva, size_t size ) { + unsigned long start_rva; + uint16_t reloc; + struct pe_relocs *pe_rel; + uint16_t *relocs; + + /* Construct */ + start_rva = ( rva & ~0xfff ); + reloc = ( rva & 0xfff ); + switch ( size ) { + case 8: + reloc |= 0xa000; + break; + case 4: + reloc |= 0x3000; + break; + case 2: + reloc |= 0x2000; + break; + default: + eprintf ( "Unsupported relocation size %zd\n", size ); + exit ( 1 ); + } + + /* Locate or create PE relocation table */ + for ( pe_rel = *pe_reltab ; pe_rel ; pe_rel = pe_rel->next ) { + if ( pe_rel->start_rva == start_rva ) + break; + } + if ( ! pe_rel ) { + pe_rel = xmalloc ( sizeof ( *pe_rel ) ); + memset ( pe_rel, 0, sizeof ( *pe_rel ) ); + pe_rel->next = *pe_reltab; + *pe_reltab = pe_rel; + pe_rel->start_rva = start_rva; + } + + /* Expand relocation list if necessary */ + if ( pe_rel->used_relocs < pe_rel->total_relocs ) { + relocs = pe_rel->relocs; + } else { + pe_rel->total_relocs = ( pe_rel->total_relocs ? + ( pe_rel->total_relocs * 2 ) : 256 ); + relocs = xmalloc ( pe_rel->total_relocs * + sizeof ( pe_rel->relocs[0] ) ); + memset ( relocs, 0, + pe_rel->total_relocs * sizeof ( pe_rel->relocs[0] ) ); + memcpy ( relocs, pe_rel->relocs, + pe_rel->used_relocs * sizeof ( pe_rel->relocs[0] ) ); + free ( pe_rel->relocs ); + pe_rel->relocs = relocs; + } + + /* Store relocation */ + pe_rel->relocs[ pe_rel->used_relocs++ ] = reloc; +} + +/** + * Calculate size of binary PE relocation table + * + * @v pe_reltab PE relocation table + * @v buffer Buffer to contain binary table, or NULL + * @ret size Size of binary table + */ +static size_t output_pe_reltab ( struct pe_relocs *pe_reltab, + void *buffer ) { + struct pe_relocs *pe_rel; + unsigned int num_relocs; + size_t size; + size_t total_size = 0; + + for ( pe_rel = pe_reltab ; pe_rel ; pe_rel = pe_rel->next ) { + num_relocs = ( ( pe_rel->used_relocs + 1 ) & ~1 ); + size = ( sizeof ( uint32_t ) /* VirtualAddress */ + + sizeof ( uint32_t ) /* SizeOfBlock */ + + ( num_relocs * sizeof ( uint16_t ) ) ); + if ( buffer ) { + *( (uint32_t *) ( buffer + total_size + 0 ) ) + = pe_rel->start_rva; + *( (uint32_t *) ( buffer + total_size + 4 ) ) = size; + memcpy ( ( buffer + total_size + 8 ), pe_rel->relocs, + ( num_relocs * sizeof ( uint16_t ) ) ); + } + total_size += size; + } + + return total_size; +} + +/** + * Open input BFD file + * + * @v filename File name + * @ret ibfd BFD file + */ +static bfd * open_input_bfd ( const char *filename ) { + bfd *bfd; + + /* Open the file */ + bfd = bfd_openr ( filename, NULL ); + if ( ! bfd ) { + eprintf ( "Cannot open %s: ", filename ); + bfd_perror ( NULL ); + exit ( 1 ); + } + + /* The call to bfd_check_format() must be present, otherwise + * we get a segfault from later BFD calls. + */ + if ( bfd_check_format ( bfd, bfd_object ) < 0 ) { + eprintf ( "%s is not an object file\n", filename ); + exit ( 1 ); + } + + return bfd; +} + +/** + * Read symbol table + * + * @v bfd BFD file + */ +static asymbol ** read_symtab ( bfd *bfd ) { + long symtab_size; + asymbol **symtab; + long symcount; + + /* Get symbol table size */ + symtab_size = bfd_get_symtab_upper_bound ( bfd ); + if ( symtab_size < 0 ) { + bfd_perror ( "Could not get symbol table upper bound" ); + exit ( 1 ); + } + + /* Allocate and read symbol table */ + symtab = xmalloc ( symtab_size ); + symcount = bfd_canonicalize_symtab ( bfd, symtab ); + if ( symcount < 0 ) { + bfd_perror ( "Cannot read symbol table" ); + exit ( 1 ); + } + + return symtab; +} + +/** + * Read relocation table + * + * @v bfd BFD file + * @v symtab Symbol table + * @v section Section + * @v symtab Symbol table + * @ret reltab Relocation table + */ +static arelent ** read_reltab ( bfd *bfd, asymbol **symtab, + asection *section ) { + long reltab_size; + arelent **reltab; + long numrels; + + /* Get relocation table size */ + reltab_size = bfd_get_reloc_upper_bound ( bfd, section ); + if ( reltab_size < 0 ) { + bfd_perror ( "Could not get relocation table upper bound" ); + exit ( 1 ); + } + + /* Allocate and read relocation table */ + reltab = xmalloc ( reltab_size ); + numrels = bfd_canonicalize_reloc ( bfd, section, reltab, symtab ); + if ( numrels < 0 ) { + bfd_perror ( "Cannot read relocation table" ); + exit ( 1 ); + } + + return reltab; +} + +/** + * Process section + * + * @v bfd BFD file + * @v pe_header PE file header + * @v section Section + * @ret new New PE section + */ +static struct pe_section * process_section ( bfd *bfd, + struct pe_header *pe_header, + asection *section ) { + struct pe_section *new; + size_t section_memsz; + size_t section_filesz; + unsigned long flags = bfd_get_section_flags ( bfd, section ); + unsigned long code_start; + unsigned long code_end; + unsigned long data_start; + unsigned long data_mid; + unsigned long data_end; + unsigned long start; + unsigned long end; + unsigned long *applicable_start; + unsigned long *applicable_end; + + /* Extract current RVA limits from file header */ + code_start = pe_header->nt.OptionalHeader.BaseOfCode; + code_end = ( code_start + pe_header->nt.OptionalHeader.SizeOfCode ); +#if defined(MDE_CPU_IA32) + data_start = pe_header->nt.OptionalHeader.BaseOfData; +#elif defined(MDE_CPU_X64) + data_start = code_end; +#endif + data_mid = ( data_start + + pe_header->nt.OptionalHeader.SizeOfInitializedData ); + data_end = ( data_mid + + pe_header->nt.OptionalHeader.SizeOfUninitializedData ); + + /* Allocate PE section */ + section_memsz = bfd_section_size ( bfd, section ); + section_filesz = ( ( flags & SEC_LOAD ) ? + efi_file_align ( section_memsz ) : 0 ); + new = xmalloc ( sizeof ( *new ) + section_filesz ); + memset ( new, 0, sizeof ( *new ) + section_filesz ); + + /* Fill in section header details */ + strncpy ( ( char * ) new->hdr.Name, section->name, + sizeof ( new->hdr.Name ) ); + new->hdr.Misc.VirtualSize = section_memsz; + new->hdr.VirtualAddress = bfd_get_section_vma ( bfd, section ); + new->hdr.SizeOfRawData = section_filesz; + + /* Fill in section characteristics and update RVA limits */ + if ( flags & SEC_CODE ) { + /* .text-type section */ + new->hdr.Characteristics = + ( EFI_IMAGE_SCN_CNT_CODE | + EFI_IMAGE_SCN_MEM_NOT_PAGED | + EFI_IMAGE_SCN_MEM_EXECUTE | + EFI_IMAGE_SCN_MEM_READ ); + applicable_start = &code_start; + applicable_end = &code_end; + } else if ( flags & SEC_DATA ) { + /* .data-type section */ + new->hdr.Characteristics = + ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | + EFI_IMAGE_SCN_MEM_NOT_PAGED | + EFI_IMAGE_SCN_MEM_READ | + EFI_IMAGE_SCN_MEM_WRITE ); + applicable_start = &data_start; + applicable_end = &data_mid; + } else if ( flags & SEC_READONLY ) { + /* .rodata-type section */ + new->hdr.Characteristics = + ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | + EFI_IMAGE_SCN_MEM_NOT_PAGED | + EFI_IMAGE_SCN_MEM_READ ); + applicable_start = &data_start; + applicable_end = &data_mid; + } else if ( ! ( flags & SEC_LOAD ) ) { + /* .bss-type section */ + new->hdr.Characteristics = + ( EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA | + EFI_IMAGE_SCN_MEM_NOT_PAGED | + EFI_IMAGE_SCN_MEM_READ | + EFI_IMAGE_SCN_MEM_WRITE ); + applicable_start = &data_mid; + applicable_end = &data_end; + } + + /* Copy in section contents */ + if ( flags & SEC_LOAD ) { + if ( ! bfd_get_section_contents ( bfd, section, new->contents, + 0, section_memsz ) ) { + eprintf ( "Cannot read section %s: ", section->name ); + bfd_perror ( NULL ); + exit ( 1 ); + } + } + + /* Update RVA limits */ + start = new->hdr.VirtualAddress; + end = ( start + new->hdr.Misc.VirtualSize ); + if ( ( ! *applicable_start ) || ( *applicable_start >= start ) ) + *applicable_start = start; + if ( *applicable_end < end ) + *applicable_end = end; + if ( data_start < code_end ) + data_start = code_end; + if ( data_mid < data_start ) + data_mid = data_start; + if ( data_end < data_mid ) + data_end = data_mid; + + /* Write RVA limits back to file header */ + pe_header->nt.OptionalHeader.BaseOfCode = code_start; + pe_header->nt.OptionalHeader.SizeOfCode = ( code_end - code_start ); +#if defined(MDE_CPU_IA32) + pe_header->nt.OptionalHeader.BaseOfData = data_start; +#endif + pe_header->nt.OptionalHeader.SizeOfInitializedData = + ( data_mid - data_start ); + pe_header->nt.OptionalHeader.SizeOfUninitializedData = + ( data_end - data_mid ); + + /* Update remaining file header fields */ + pe_header->nt.FileHeader.NumberOfSections++; + pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( new->hdr ); + pe_header->nt.OptionalHeader.SizeOfImage = + efi_file_align ( data_end ); + + return new; +} + +/** + * Process relocation record + * + * @v bfd BFD file + * @v section Section + * @v rel Relocation entry + * @v pe_reltab PE relocation table to fill in + */ +static void process_reloc ( bfd *bfd, asection *section, arelent *rel, + struct pe_relocs **pe_reltab ) { + reloc_howto_type *howto = rel->howto; + asymbol *sym = *(rel->sym_ptr_ptr); + unsigned long offset = ( bfd_get_section_vma ( bfd, section ) + + rel->address ); + + if ( bfd_is_abs_section ( sym->section ) ) { + /* Skip absolute symbols; the symbol value won't + * change when the object is loaded. + */ + } else if ( strcmp ( howto->name, "R_X86_64_64" ) == 0 ) { + /* Generate an 8-byte PE relocation */ + generate_pe_reloc ( pe_reltab, offset, 8 ); + } else if ( ( strcmp ( howto->name, "R_386_32" ) == 0 ) || + ( strcmp ( howto->name, "R_X86_64_32" ) == 0 ) ) { + /* Generate a 4-byte PE relocation */ + generate_pe_reloc ( pe_reltab, offset, 4 ); + } else if ( strcmp ( howto->name, "R_386_16" ) == 0 ) { + /* Generate a 2-byte PE relocation */ + generate_pe_reloc ( pe_reltab, offset, 2 ); + } else if ( ( strcmp ( howto->name, "R_386_PC32" ) == 0 ) || + ( strcmp ( howto->name, "R_X86_64_PC32" ) == 0 ) ) { + /* Skip PC-relative relocations; all relative offsets + * remain unaltered when the object is loaded. + */ + } else { + eprintf ( "Unrecognised relocation type %s\n", howto->name ); + exit ( 1 ); + } +} + +/** + * Create relocations section + * + * @v pe_header PE file header + * @v pe_reltab PE relocation table + * @ret section Relocation section + */ +static struct pe_section * +create_reloc_section ( struct pe_header *pe_header, + struct pe_relocs *pe_reltab ) { + struct pe_section *reloc; + size_t section_memsz; + size_t section_filesz; + EFI_IMAGE_DATA_DIRECTORY *relocdir; + + /* Allocate PE section */ + section_memsz = output_pe_reltab ( pe_reltab, NULL ); + section_filesz = efi_file_align ( section_memsz ); + reloc = xmalloc ( sizeof ( *reloc ) + section_filesz ); + memset ( reloc, 0, sizeof ( *reloc ) + section_filesz ); + + /* Fill in section header details */ + strncpy ( ( char * ) reloc->hdr.Name, ".reloc", + sizeof ( reloc->hdr.Name ) ); + reloc->hdr.Misc.VirtualSize = section_memsz; + reloc->hdr.VirtualAddress = pe_header->nt.OptionalHeader.SizeOfImage; + reloc->hdr.SizeOfRawData = section_filesz; + reloc->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | + EFI_IMAGE_SCN_MEM_NOT_PAGED | + EFI_IMAGE_SCN_MEM_READ ); + + /* Copy in section contents */ + output_pe_reltab ( pe_reltab, reloc->contents ); + + /* Update file header details */ + pe_header->nt.FileHeader.NumberOfSections++; + pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( reloc->hdr ); + pe_header->nt.OptionalHeader.SizeOfImage += section_filesz; + relocdir = &(pe_header->nt.OptionalHeader.DataDirectory + [EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]); + relocdir->VirtualAddress = reloc->hdr.VirtualAddress; + relocdir->Size = reloc->hdr.Misc.VirtualSize; + + return reloc; +} + +/** + * Create debug section + * + * @v pe_header PE file header + * @ret section Debug section + */ +static struct pe_section * +create_debug_section ( struct pe_header *pe_header, const char *filename ) { + struct pe_section *debug; + size_t section_memsz; + size_t section_filesz; + EFI_IMAGE_DATA_DIRECTORY *debugdir; + struct { + EFI_IMAGE_DEBUG_DIRECTORY_ENTRY debug; + EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY rsds; + char name[ strlen ( filename ) + 1 ]; + } *contents; + + /* Allocate PE section */ + section_memsz = sizeof ( *contents ); + section_filesz = efi_file_align ( section_memsz ); + debug = xmalloc ( sizeof ( *debug ) + section_filesz ); + memset ( debug, 0, sizeof ( *debug ) + section_filesz ); + contents = ( void * ) debug->contents; + + /* Fill in section header details */ + strncpy ( ( char * ) debug->hdr.Name, ".debug", + sizeof ( debug->hdr.Name ) ); + debug->hdr.Misc.VirtualSize = section_memsz; + debug->hdr.VirtualAddress = pe_header->nt.OptionalHeader.SizeOfImage; + debug->hdr.SizeOfRawData = section_filesz; + debug->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | + EFI_IMAGE_SCN_MEM_NOT_PAGED | + EFI_IMAGE_SCN_MEM_READ ); + + /* Create section contents */ + contents->debug.TimeDateStamp = 0x10d1a884; + contents->debug.Type = EFI_IMAGE_DEBUG_TYPE_CODEVIEW; + contents->debug.SizeOfData = + ( sizeof ( *contents ) - sizeof ( contents->debug ) ); + contents->debug.RVA = ( debug->hdr.VirtualAddress + + offsetof ( typeof ( *contents ), rsds ) ); + contents->rsds.Signature = CODEVIEW_SIGNATURE_RSDS; + snprintf ( contents->name, sizeof ( contents->name ), "%s", + filename ); + + /* Update file header details */ + pe_header->nt.FileHeader.NumberOfSections++; + pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( debug->hdr ); + pe_header->nt.OptionalHeader.SizeOfImage += section_filesz; + debugdir = &(pe_header->nt.OptionalHeader.DataDirectory + [EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]); + debugdir->VirtualAddress = debug->hdr.VirtualAddress; + debugdir->Size = debug->hdr.Misc.VirtualSize; + + return debug; +} + +/** + * Write out PE file + * + * @v pe_header PE file header + * @v pe_sections List of PE sections + * @v pe Output file + */ +static void write_pe_file ( struct pe_header *pe_header, + struct pe_section *pe_sections, + FILE *pe ) { + struct pe_section *section; + unsigned long fpos = 0; + + /* Assign raw data pointers */ + fpos = efi_file_align ( pe_header->nt.OptionalHeader.SizeOfHeaders ); + for ( section = pe_sections ; section ; section = section->next ) { + if ( section->hdr.SizeOfRawData ) { + section->hdr.PointerToRawData = fpos; + fpos += section->hdr.SizeOfRawData; + fpos = efi_file_align ( fpos ); + } + } + + /* Write file header */ + if ( fwrite ( pe_header, sizeof ( *pe_header ), 1, pe ) != 1 ) { + perror ( "Could not write PE header" ); + exit ( 1 ); + } + + /* Write section headers */ + for ( section = pe_sections ; section ; section = section->next ) { + if ( fwrite ( §ion->hdr, sizeof ( section->hdr ), + 1, pe ) != 1 ) { + perror ( "Could not write section header" ); + exit ( 1 ); + } + } + + /* Write sections */ + for ( section = pe_sections ; section ; section = section->next ) { + if ( fseek ( pe, section->hdr.PointerToRawData, + SEEK_SET ) != 0 ) { + eprintf ( "Could not seek to %lx: %s\n", + section->hdr.PointerToRawData, + strerror ( errno ) ); + exit ( 1 ); + } + if ( section->hdr.SizeOfRawData && + ( fwrite ( section->contents, section->hdr.SizeOfRawData, + 1, pe ) != 1 ) ) { + eprintf ( "Could not write section %.8s: %s\n", + section->hdr.Name, strerror ( errno ) ); + exit ( 1 ); + } + } +} + +/** + * Convert ELF to PE + * + * @v elf_name ELF file name + * @v pe_name PE file name + */ +static void elf2pe ( const char *elf_name, const char *pe_name, + struct options *opts ) { + bfd *bfd; + asymbol **symtab; + asection *section; + arelent **reltab; + arelent **rel; + struct pe_relocs *pe_reltab = NULL; + struct pe_section *pe_sections = NULL; + struct pe_section **next_pe_section = &pe_sections; + struct pe_header pe_header; + FILE *pe; + + /* Open the file */ + bfd = open_input_bfd ( elf_name ); + symtab = read_symtab ( bfd ); + + /* Initialise the PE header */ + memcpy ( &pe_header, &efi_pe_header, sizeof ( pe_header ) ); + pe_header.nt.OptionalHeader.AddressOfEntryPoint = + bfd_get_start_address ( bfd ); + pe_header.nt.OptionalHeader.Subsystem = opts->subsystem; + + /* For each input section, build an output section and create + * the appropriate relocation records + */ + for ( section = bfd->sections ; section ; section = section->next ) { + /* Discard non-allocatable sections */ + if ( ! ( bfd_get_section_flags ( bfd, section ) & SEC_ALLOC ) ) + continue; + /* Create output section */ + *(next_pe_section) = process_section ( bfd, &pe_header, + section ); + next_pe_section = &(*next_pe_section)->next; + /* Add relocations from this section */ + reltab = read_reltab ( bfd, symtab, section ); + for ( rel = reltab ; *rel ; rel++ ) + process_reloc ( bfd, section, *rel, &pe_reltab ); + free ( reltab ); + } + + /* Create the .reloc section */ + *(next_pe_section) = create_reloc_section ( &pe_header, pe_reltab ); + next_pe_section = &(*next_pe_section)->next; + + /* Create the .reloc section */ + *(next_pe_section) = create_debug_section ( &pe_header, + basename ( pe_name ) ); + next_pe_section = &(*next_pe_section)->next; + + /* Write out PE file */ + pe = fopen ( pe_name, "w" ); + if ( ! pe ) { + eprintf ( "Could not open %s for writing: %s\n", + pe_name, strerror ( errno ) ); + exit ( 1 ); + } + write_pe_file ( &pe_header, pe_sections, pe ); + fclose ( pe ); + + /* Close BFD file */ + bfd_close ( bfd ); +} + +/** + * Print help + * + * @v program_name Program name + */ +static void print_help ( const char *program_name ) { + eprintf ( "Syntax: %s [--subsystem=] infile outfile\n", + program_name ); +} + +/** + * Parse command-line options + * + * @v argc Argument count + * @v argv Argument list + * @v opts Options structure to populate + */ +static int parse_options ( const int argc, char **argv, + struct options *opts ) { + char *end; + int c; + + while (1) { + int option_index = 0; + static struct option long_options[] = { + { "subsystem", required_argument, NULL, 's' }, + { "help", 0, NULL, 'h' }, + { 0, 0, 0, 0 } + }; + + if ( ( c = getopt_long ( argc, argv, "s:h", + long_options, + &option_index ) ) == -1 ) { + break; + } + + switch ( c ) { + case 's': + opts->subsystem = strtoul ( optarg, &end, 0 ); + if ( *end ) { + eprintf ( "Invalid subsytem \"%s\"\n", + optarg ); + exit ( 2 ); + } + break; + case 'h': + print_help ( argv[0] ); + exit ( 0 ); + case '?': + default: + exit ( 2 ); + } + } + return optind; +} + +int main ( int argc, char **argv ) { + struct options opts = { + .subsystem = EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION, + }; + unsigned int infile_index; + const char *infile; + const char *outfile; + + /* Initialise libbfd */ + bfd_init(); + + /* Parse command-line arguments */ + infile_index = parse_options ( argc, argv, &opts ); + if ( argc != ( infile_index + 2 ) ) { + print_help ( argv[0] ); + exit ( 2 ); + } + infile = argv[infile_index]; + outfile = argv[infile_index + 1]; + + /* Convert file */ + elf2pe ( infile, outfile, &opts ); + + return 0; +} diff --git a/gpxe/src/util/geniso b/gpxe/src/util/geniso new file mode 100755 index 0000000..d7ddbd2 --- /dev/null +++ b/gpxe/src/util/geniso @@ -0,0 +1,56 @@ +#!/bin/bash +# +# Generate a isolinux ISO boot image +# +# geniso foo.iso foo.lkrn +# +# the ISO image is the first argument so that a list of .lkrn images +# to include can be specified +# +case $# in +0|1) + echo Usage: $0 foo.iso foo.lkrn ... + exit 1 + ;; +esac +# This should be the default location of the isolinux.bin file +isolinux_bin=${ISOLINUX_BIN:-util/isolinux.bin} +if [ ! -r $isolinux_bin ] +then + echo $0: $isolinux_bin not found, please install, or set ISOLINUX_BIN in arch/i386/config correctly + exit 1 +fi +out=$1 +shift +dir=bin/iso.dir +mkdir -p $dir +cfg=$dir/isolinux.cfg +cp -p $isolinux_bin $dir +cat > $cfg <&2 + continue + fi + b=$(basename $f) + g=${b%.lkrn} + g=${g//[^a-z0-9]}.krn + case "$first" in + "") + echo DEFAULT $g + ;; + esac + first=$g + echo LABEL $b + echo "" KERNEL $g + cp -p $f $dir/$g +done >> $cfg +mkisofs -q -l -o $out -c boot.cat -b isolinux.bin -no-emul-boot -boot-load-size 4 -boot-info-table $dir +rm -fr $dir diff --git a/gpxe/src/util/genliso b/gpxe/src/util/genliso new file mode 100755 index 0000000..0a67eb3 --- /dev/null +++ b/gpxe/src/util/genliso @@ -0,0 +1,85 @@ +#!/bin/bash +# +# Generate a legacy floppy emulation ISO boot image +# +# genliso foo.liso foo.lkrn +# +# the ISO image is the first argument so that a list of .lkrn images +# to include can be specified +# +case $0 in +*genliso) + ;; +*genfdimg) + genfdimg=1 + ;; +esac +case $# in +0|1) + echo Usage: $0 foo.liso foo.lkrn ... + exit 1 + ;; +esac +case "`mtools -V`" in +Mtools\ version\ 3.9.9*|Mtools\ version\ 3.9.1[0-9]*|Mtools\ version\ 4.*) + ;; +*) + echo Mtools version 3.9.9 or later is required + exit 1 + ;; +esac +out=$1 +shift +dir=bin/liso.dir +mkdir -p $dir +case "$genfdimg" in +1) + img=$out + ;; +*) + img=$dir/boot.img + ;; +esac +mformat -f 1440 -C -i $img :: +cfg=bin/syslinux.cfg +cat > $cfg <&2 + continue + fi + # shorten name for 8.3 filesystem + b=$(basename $f) + g=${b%.lkrn} + g=${g//[^a-z0-9]} + g=${g:0:8}.krn + case "$first" in + "") + echo DEFAULT $g + ;; + esac + first=$g + echo LABEL $b + echo "" KERNEL $g + mcopy -m -i $img $f ::$g +done >> $cfg +mcopy -i $img $cfg ::syslinux.cfg +if ! syslinux $img +then + exit 1 +fi +case "$genfdimg" in +1) + ;; +*) + mkisofs -o $out -c boot.cat -b boot.img $dir + ;; +esac +rm -fr $dir diff --git a/gpxe/src/util/get-pci-ids b/gpxe/src/util/get-pci-ids new file mode 100755 index 0000000..6501a7f --- /dev/null +++ b/gpxe/src/util/get-pci-ids @@ -0,0 +1,135 @@ +#! /usr/bin/perl -w + +# get-pci-ids: extract pci vendor/device ids from linux net drivers + +# Copyright (C) 2003 Georg Baum + +# 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/gpxe/src/util/hijack.c b/gpxe/src/util/hijack.c new file mode 100644 index 0000000..ed89592 --- /dev/null +++ b/gpxe/src/util/hijack.c @@ -0,0 +1,628 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SNAPLEN 1600 + +/* + * FIXME: is there a way to detect the version of the libpcap library? + * Version 0.9 has pcap_inject; version 0.8 doesn't, but both report + * their version number as 2.4. + */ +#define HAVE_PCAP_INJECT 0 + +struct hijack { + pcap_t *pcap; + int fd; + int datalink; + int filtered; + unsigned long rx_count; + unsigned long tx_count; +}; + +struct hijack_listener { + struct sockaddr_un sun; + int fd; +}; + +struct hijack_options { + char interface[IF_NAMESIZE]; + int daemonise; +}; + +static int daemonised = 0; + +static int signalled = 0; + +static void flag_signalled ( int signal __attribute__ (( unused )) ) { + signalled = 1; +} + +#if ! HAVE_PCAP_INJECT +/** + * Substitute for pcap_inject(), if this version of libpcap doesn't + * have it. Will almost certainly only work under Linux. + * + */ +int pcap_inject ( pcap_t *pcap, const void *data, size_t len ) { + int fd; + char *errbuf = pcap_geterr ( pcap ); + + fd = pcap_get_selectable_fd ( pcap ); + if ( fd < 0 ) { + snprintf ( errbuf, PCAP_ERRBUF_SIZE, + "could not get file descriptor" ); + return -1; + } + if ( write ( fd, data, len ) != len ) { + snprintf ( errbuf, PCAP_ERRBUF_SIZE, + "could not write data: %s", strerror ( errno ) ); + return -1; + } + return len; +} +#endif /* ! HAVE_PCAP_INJECT */ + +/** + * Log error message + * + */ +static __attribute__ (( format ( printf, 2, 3 ) )) void +logmsg ( int level, const char *format, ... ) { + va_list ap; + + va_start ( ap, format ); + if ( daemonised ) { + vsyslog ( ( LOG_DAEMON | level ), format, ap ); + } else { + vfprintf ( stderr, format, ap ); + } + va_end ( ap ); +} + +/** + * Open pcap device + * + */ +static int hijack_open ( const char *interface, struct hijack *hijack ) { + char errbuf[PCAP_ERRBUF_SIZE]; + + /* Open interface via pcap */ + errbuf[0] = '\0'; + hijack->pcap = pcap_open_live ( interface, SNAPLEN, 1, 0, errbuf ); + if ( ! hijack->pcap ) { + logmsg ( LOG_ERR, "Failed to open %s: %s\n", + interface, errbuf ); + goto err; + } + if ( errbuf[0] ) + logmsg ( LOG_WARNING, "Warning: %s\n", errbuf ); + + /* Set capture interface to non-blocking mode */ + if ( pcap_setnonblock ( hijack->pcap, 1, errbuf ) < 0 ) { + logmsg ( LOG_ERR, "Could not make %s non-blocking: %s\n", + interface, errbuf ); + goto err; + } + + /* Get file descriptor for select() */ + hijack->fd = pcap_get_selectable_fd ( hijack->pcap ); + if ( hijack->fd < 0 ) { + logmsg ( LOG_ERR, "Cannot get selectable file descriptor " + "for %s\n", interface ); + goto err; + } + + /* Get link layer type */ + hijack->datalink = pcap_datalink ( hijack->pcap ); + + return 0; + + err: + if ( hijack->pcap ) + pcap_close ( hijack->pcap ); + return -1; +} + +/** + * Close pcap device + * + */ +static void hijack_close ( struct hijack *hijack ) { + pcap_close ( hijack->pcap ); +} + +/** + * Install filter for hijacked connection + * + */ +static int hijack_install_filter ( struct hijack *hijack, + char *filter ) { + struct bpf_program program; + + /* Compile filter */ + if ( pcap_compile ( hijack->pcap, &program, filter, 1, 0 ) < 0 ) { + logmsg ( LOG_ERR, "could not compile filter \"%s\": %s\n", + filter, pcap_geterr ( hijack->pcap ) ); + goto err_nofree; + } + + /* Install filter */ + if ( pcap_setfilter ( hijack->pcap, &program ) < 0 ) { + logmsg ( LOG_ERR, "could not install filter \"%s\": %s\n", + filter, pcap_geterr ( hijack->pcap ) ); + goto err; + } + + logmsg ( LOG_INFO, "using filter \"%s\"\n", filter ); + + pcap_freecode ( &program ); + return 0; + + err: + pcap_freecode ( &program ); + err_nofree: + return -1; +} + +/** + * Set up filter for hijacked ethernet connection + * + */ +static int hijack_filter_ethernet ( struct hijack *hijack, const char *buf, + size_t len ) { + char filter[55]; /* see format string */ + struct ether_header *ether_header = ( struct ether_header * ) buf; + unsigned char *hwaddr = ether_header->ether_shost; + + if ( len < sizeof ( *ether_header ) ) + return -1; + + snprintf ( filter, sizeof ( filter ), "broadcast or multicast or " + "ether host %02x:%02x:%02x:%02x:%02x:%02x", hwaddr[0], + hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5] ); + + return hijack_install_filter ( hijack, filter ); +} + +/** + * Set up filter for hijacked connection + * + */ +static int hijack_filter ( struct hijack *hijack, const char *buf, + size_t len ) { + switch ( hijack->datalink ) { + case DLT_EN10MB: + return hijack_filter_ethernet ( hijack, buf, len ); + default: + logmsg ( LOG_ERR, "unsupported protocol %s: cannot filter\n", + ( pcap_datalink_val_to_name ( hijack->datalink ) ? + pcap_datalink_val_to_name ( hijack->datalink ) : + "UNKNOWN" ) ); + /* Return success so we don't get called again */ + return 0; + } +} + +/** + * Forward data from hijacker + * + */ +static ssize_t forward_from_hijacker ( struct hijack *hijack, int fd ) { + char buf[SNAPLEN]; + ssize_t len; + + /* Read packet from hijacker */ + len = read ( fd, buf, sizeof ( buf ) ); + if ( len < 0 ) { + logmsg ( LOG_ERR, "read from hijacker failed: %s\n", + strerror ( errno ) ); + return -1; + } + if ( len == 0 ) + return 0; + + /* Set up filter if not already in place */ + if ( ! hijack->filtered ) { + if ( hijack_filter ( hijack, buf, len ) == 0 ) + hijack->filtered = 1; + } + + /* Transmit packet to network */ + if ( pcap_inject ( hijack->pcap, buf, len ) != len ) { + logmsg ( LOG_ERR, "write to hijacked port failed: %s\n", + pcap_geterr ( hijack->pcap ) ); + return -1; + } + + hijack->tx_count++; + return len; +}; + +/** + * Forward data to hijacker + * + */ +static ssize_t forward_to_hijacker ( int fd, struct hijack *hijack ) { + struct pcap_pkthdr *pkt_header; + const unsigned char *pkt_data; + ssize_t len; + + /* Receive packet from network */ + if ( pcap_next_ex ( hijack->pcap, &pkt_header, &pkt_data ) < 0 ) { + logmsg ( LOG_ERR, "read from hijacked port failed: %s\n", + pcap_geterr ( hijack->pcap ) ); + return -1; + } + if ( pkt_header->caplen != pkt_header->len ) { + logmsg ( LOG_ERR, "read partial packet (%d of %d bytes)\n", + pkt_header->caplen, pkt_header->len ); + return -1; + } + if ( pkt_header->caplen == 0 ) + return 0; + len = pkt_header->caplen; + + /* Write packet to hijacker */ + if ( write ( fd, pkt_data, len ) != len ) { + logmsg ( LOG_ERR, "write to hijacker failed: %s\n", + strerror ( errno ) ); + return -1; + } + + hijack->rx_count++; + return len; +}; + + +/** + * Run hijacker + * + */ +static int run_hijacker ( const char *interface, int fd ) { + struct hijack hijack; + fd_set fdset; + int max_fd; + ssize_t len; + + logmsg ( LOG_INFO, "new connection for %s\n", interface ); + + /* Open connection to network */ + memset ( &hijack, 0, sizeof ( hijack ) ); + if ( hijack_open ( interface, &hijack ) < 0 ) + goto err; + + /* Do the forwarding */ + max_fd = ( ( fd > hijack.fd ) ? fd : hijack.fd ); + while ( 1 ) { + /* Wait for available data */ + FD_ZERO ( &fdset ); + FD_SET ( fd, &fdset ); + FD_SET ( hijack.fd, &fdset ); + if ( select ( ( max_fd + 1 ), &fdset, NULL, NULL, 0 ) < 0 ) { + logmsg ( LOG_ERR, "select failed: %s\n", + strerror ( errno ) ); + goto err; + } + if ( FD_ISSET ( fd, &fdset ) ) { + len = forward_from_hijacker ( &hijack, fd ); + if ( len < 0 ) + goto err; + if ( len == 0 ) + break; + } + if ( FD_ISSET ( hijack.fd, &fdset ) ) { + len = forward_to_hijacker ( fd, &hijack ); + if ( len < 0 ) + goto err; + if ( len == 0 ) + break; + } + } + + hijack_close ( &hijack ); + logmsg ( LOG_INFO, "closed connection for %s\n", interface ); + logmsg ( LOG_INFO, "received %ld packets, sent %ld packets\n", + hijack.rx_count, hijack.tx_count ); + + return 0; + + err: + if ( hijack.pcap ) + hijack_close ( &hijack ); + return -1; +} + +/** + * Open listener socket + * + */ +static int open_listener ( const char *interface, + struct hijack_listener *listener ) { + + /* Create socket */ + listener->fd = socket ( PF_UNIX, SOCK_SEQPACKET, 0 ); + if ( listener->fd < 0 ) { + logmsg ( LOG_ERR, "Could not create socket: %s\n", + strerror ( errno ) ); + goto err; + } + + /* Bind to local filename */ + listener->sun.sun_family = AF_UNIX, + snprintf ( listener->sun.sun_path, sizeof ( listener->sun.sun_path ), + "/var/run/hijack-%s", interface ); + if ( bind ( listener->fd, ( struct sockaddr * ) &listener->sun, + sizeof ( listener->sun ) ) < 0 ) { + logmsg ( LOG_ERR, "Could not bind socket to %s: %s\n", + listener->sun.sun_path, strerror ( errno ) ); + goto err; + } + + /* Set as a listening socket */ + if ( listen ( listener->fd, 0 ) < 0 ) { + logmsg ( LOG_ERR, "Could not listen to %s: %s\n", + listener->sun.sun_path, strerror ( errno ) ); + goto err; + } + + return 0; + + err: + if ( listener->fd >= 0 ) + close ( listener->fd ); + return -1; +} + +/** + * Listen on listener socket + * + */ +static int listen_for_hijackers ( struct hijack_listener *listener, + const char *interface ) { + int fd; + pid_t child; + int rc; + + logmsg ( LOG_INFO, "Listening on %s\n", listener->sun.sun_path ); + + while ( ! signalled ) { + /* Accept new connection, interruptibly */ + siginterrupt ( SIGINT, 1 ); + siginterrupt ( SIGHUP, 1 ); + fd = accept ( listener->fd, NULL, 0 ); + siginterrupt ( SIGINT, 0 ); + siginterrupt ( SIGHUP, 0 ); + if ( fd < 0 ) { + if ( errno == EINTR ) { + continue; + } else { + logmsg ( LOG_ERR, "accept failed: %s\n", + strerror ( errno ) ); + goto err; + } + } + + /* Fork child process */ + child = fork(); + if ( child < 0 ) { + logmsg ( LOG_ERR, "fork failed: %s\n", + strerror ( errno ) ); + goto err; + } + if ( child == 0 ) { + /* I am the child; run the hijacker */ + rc = run_hijacker ( interface, fd ); + close ( fd ); + exit ( rc ); + } + + close ( fd ); + } + + logmsg ( LOG_INFO, "Stopped listening on %s\n", + listener->sun.sun_path ); + return 0; + + err: + if ( fd >= 0 ) + close ( fd ); + return -1; +} + +/** + * Close listener socket + * + */ +static void close_listener ( struct hijack_listener *listener ) { + close ( listener->fd ); + unlink ( listener->sun.sun_path ); +} + +/** + * Print usage + * + */ +static void usage ( char **argv ) { + logmsg ( LOG_ERR, + "Usage: %s [options]\n" + "\n" + "Options:\n" + " -h|--help Print this help message\n" + " -i|--interface intf Use specified network interface\n" + " -n|--nodaemon Run in foreground\n", + argv[0] ); +} + +/** + * Parse command-line options + * + */ +static int parse_options ( int argc, char **argv, + struct hijack_options *options ) { + static struct option long_options[] = { + { "interface", 1, NULL, 'i' }, + { "nodaemon", 0, NULL, 'n' }, + { "help", 0, NULL, 'h' }, + { }, + }; + int c; + + /* Set default options */ + memset ( options, 0, sizeof ( *options ) ); + strncpy ( options->interface, "eth0", sizeof ( options->interface ) ); + options->daemonise = 1; + + /* Parse command-line options */ + while ( 1 ) { + int option_index = 0; + + c = getopt_long ( argc, argv, "i:hn", long_options, + &option_index ); + if ( c < 0 ) + break; + + switch ( c ) { + case 'i': + strncpy ( options->interface, optarg, + sizeof ( options->interface ) ); + break; + case 'n': + options->daemonise = 0; + break; + case 'h': + usage( argv ); + return -1; + case '?': + /* Unrecognised option */ + return -1; + default: + logmsg ( LOG_ERR, "Unrecognised option '-%c'\n", c ); + return -1; + } + } + + /* Check there's nothing left over on the command line */ + if ( optind != argc ) { + usage ( argv ); + return -1; + } + + return 0; +} + +/** + * Daemonise + * + */ +static int daemonise ( const char *interface ) { + char pidfile[16 + IF_NAMESIZE + 4]; /* "/var/run/hijack-.pid" */ + char pid[16]; + int pidlen; + int fd = -1; + + /* Daemonise */ + if ( daemon ( 0, 0 ) < 0 ) { + logmsg ( LOG_ERR, "Could not daemonise: %s\n", + strerror ( errno ) ); + goto err; + } + daemonised = 1; /* Direct messages to syslog now */ + + /* Open pid file */ + snprintf ( pidfile, sizeof ( pidfile ), "/var/run/hijack-%s.pid", + interface ); + fd = open ( pidfile, ( O_WRONLY | O_CREAT | O_TRUNC ), + ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ) ); + if ( fd < 0 ) { + logmsg ( LOG_ERR, "Could not open %s for writing: %s\n", + pidfile, strerror ( errno ) ); + goto err; + } + + /* Write pid to file */ + pidlen = snprintf ( pid, sizeof ( pid ), "%d\n", getpid() ); + if ( write ( fd, pid, pidlen ) != pidlen ) { + logmsg ( LOG_ERR, "Could not write %s: %s\n", + pidfile, strerror ( errno ) ); + goto err; + } + + close ( fd ); + return 0; + + err: + if ( fd >= 0 ) + close ( fd ); + return -1; +} + +int main ( int argc, char **argv ) { + struct hijack_options options; + struct hijack_listener listener; + struct sigaction sa; + + /* Parse command-line options */ + if ( parse_options ( argc, argv, &options ) < 0 ) + exit ( 1 ); + + /* Set up syslog connection */ + openlog ( basename ( argv[0] ), LOG_PID, LOG_DAEMON ); + + /* Set up listening socket */ + if ( open_listener ( options.interface, &listener ) < 0 ) + exit ( 1 ); + + /* Daemonise on demand */ + if ( options.daemonise ) { + if ( daemonise ( options.interface ) < 0 ) + exit ( 1 ); + } + + /* Avoid creating zombies */ + memset ( &sa, 0, sizeof ( sa ) ); + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_RESTART | SA_NOCLDWAIT; + if ( sigaction ( SIGCHLD, &sa, NULL ) < 0 ) { + logmsg ( LOG_ERR, "Could not set SIGCHLD handler: %s", + strerror ( errno ) ); + exit ( 1 ); + } + + /* Set 'signalled' flag on SIGINT or SIGHUP */ + sa.sa_handler = flag_signalled; + sa.sa_flags = SA_RESTART | SA_RESETHAND; + if ( sigaction ( SIGINT, &sa, NULL ) < 0 ) { + logmsg ( LOG_ERR, "Could not set SIGINT handler: %s", + strerror ( errno ) ); + exit ( 1 ); + } + if ( sigaction ( SIGHUP, &sa, NULL ) < 0 ) { + logmsg ( LOG_ERR, "Could not set SIGHUP handler: %s", + strerror ( errno ) ); + exit ( 1 ); + } + + /* Listen for hijackers */ + if ( listen_for_hijackers ( &listener, options.interface ) < 0 ) + exit ( 1 ); + + close_listener ( &listener ); + + return 0; +} diff --git a/gpxe/src/util/makerom.pl b/gpxe/src/util/makerom.pl new file mode 100755 index 0000000..aed3a56 --- /dev/null +++ b/gpxe/src/util/makerom.pl @@ -0,0 +1,226 @@ +#!/usr/bin/perl -w + +use Getopt::Std; + +use constant MINROMSIZE => 8192; +use constant MAXROMSIZE => 262144; + +use constant PCI_PTR_LOC => 0x18; # from beginning of ROM +use constant PCI_HDR_SIZE => 0x18; +use constant PNP_PTR_LOC => 0x1a; # from beginning of ROM +use constant PNP_HDR_SIZE => 0x20; +use constant PNP_CHKSUM_OFF => 0x9; # bytes from beginning of PnP header +use constant PNP_DEVICE_OFF => 0x10; # bytes from beginning of PnP header +use constant PCI_VEND_ID_OFF => 0x4; # bytes from beginning of PCI header +use constant PCI_DEV_ID_OFF => 0x6; # bytes from beginning of PCI header +use constant PCI_SIZE_OFF => 0x10; # bytes from beginning of PCI header + +use constant UNDI_PTR_LOC => 0x16; # from beginning of ROM +use constant UNDI_HDR_SIZE => 0x16; +use constant UNDI_CHKSUM_OFF => 0x05; + +use strict; + +use vars qw(%opts); + +use bytes; + +sub getromsize ($) { + my ($romref) = @_; + my $i; + + print STDERR "BIOS extension ROM Image did not start with 0x55 0xAA\n" + if (substr($$romref, 0, 2) ne "\x55\xaa"); + my $size = ord(substr($$romref, 2, 1)) * 512; + for ($i = MINROMSIZE; $i < MAXROMSIZE and $i < $size; $i *= 2) { } + print STDERR "$size is a strange size for a boot ROM\n" + if ($size > 0 and $i > $size); + return ($size); +} + +sub addident ($) { + my ($romref) = @_; + + return (0) unless (my $s = $opts{'i'}); + # include the terminating NUL byte too + $s .= "\x00"; + my $len = length($s); + # Put the identifier in only if the space is blank + my $pos = length($$romref) - $len - 2; + return (0) if (substr($$romref, $pos, $len) ne ("\xFF" x $len)); + substr($$romref, $pos, $len) = $s; + return ($pos); +} + +sub pcipnpheaders ($$) { + my ($romref, $identoffset) = @_; + my ($pci_hdr_offset, $pnp_hdr_offset); + + $pci_hdr_offset = unpack('v', substr($$romref, PCI_PTR_LOC, 2)); + $pnp_hdr_offset = unpack('v', substr($$romref, PNP_PTR_LOC, 2)); + # Sanity checks + if ($pci_hdr_offset < PCI_PTR_LOC + 2 + or $pci_hdr_offset > length($$romref) - PCI_HDR_SIZE + or $pnp_hdr_offset < PNP_PTR_LOC + 2 + or $pnp_hdr_offset > length($$romref) - PNP_HDR_SIZE + or substr($$romref, $pci_hdr_offset, 4) ne 'PCIR' + or substr($$romref, $pnp_hdr_offset, 4) ne '$PnP') { + $pci_hdr_offset = $pnp_hdr_offset = 0; + } else { + printf "PCI header at %#x and PnP header at %#x\n", + $pci_hdr_offset, $pnp_hdr_offset if $opts{'v'}; + } + if ($pci_hdr_offset > 0) { + my ($pci_vendor_id, $pci_device_id); + # if no -p option, just report what's there + if (!defined($opts{'p'})) { + $pci_vendor_id = unpack('v', substr($$romref, $pci_hdr_offset+PCI_VEND_ID_OFF, 2)); + $pci_device_id = unpack('v', substr($$romref, $pci_hdr_offset+PCI_DEV_ID_OFF, 2)); + printf "PCI Vendor ID %#x Device ID %#x\n", + $pci_vendor_id, $pci_device_id; + } else { + substr($$romref, $pci_hdr_offset + PCI_SIZE_OFF, 2) + = pack('v', length($$romref) / 512); + ($pci_vendor_id, $pci_device_id) = split(/,/, $opts{'p'}); + substr($$romref, $pci_hdr_offset+PCI_VEND_ID_OFF, 2) + = pack('v', oct($pci_vendor_id)) if ($pci_vendor_id); + substr($$romref, $pci_hdr_offset+PCI_DEV_ID_OFF, 2) + = pack('v', oct($pci_device_id)) if ($pci_device_id); + } + } + if ($pnp_hdr_offset > 0) { + if (defined($identoffset)) { + # Point to device id string at end of ROM image + substr($$romref, $pnp_hdr_offset+PNP_DEVICE_OFF, 2) + = pack('v', $identoffset); + } + substr($$romref, $pnp_hdr_offset+PNP_CHKSUM_OFF, 1) = "\x00"; + my $sum = unpack('%8C*', substr($$romref, $pnp_hdr_offset, + PNP_HDR_SIZE)); + substr($$romref, $pnp_hdr_offset+PNP_CHKSUM_OFF, 1) = chr(256 - $sum); + } +} + +sub undiheaders ($) { + my ($romref) = @_; + my ($undi_hdr_offset); + + $undi_hdr_offset = unpack('v', substr($$romref, UNDI_PTR_LOC, 2)); + # Sanity checks + if ($undi_hdr_offset < UNDI_PTR_LOC + 2 + or $undi_hdr_offset > length($$romref) - UNDI_HDR_SIZE + or substr($$romref, $undi_hdr_offset, 4) ne 'UNDI') { + $undi_hdr_offset = 0; + } else { + printf "UNDI header at %#x\n", $undi_hdr_offset if $opts{'v'}; + } + if ($undi_hdr_offset > 0) { + substr($$romref, $undi_hdr_offset+UNDI_CHKSUM_OFF, 1) = "\x00"; + my $sum = unpack('%8C*', substr($$romref, $undi_hdr_offset, + UNDI_HDR_SIZE)); + substr($$romref, $undi_hdr_offset+UNDI_CHKSUM_OFF, 1) = chr(256 - $sum); + } +} + +sub writerom ($$) { + my ($filename, $romref) = @_; + + open(R, ">$filename") or die "$filename: $!\n"; + print R $$romref; + close(R); +} + +sub checksum ($) { + my ($romref) = @_; + + substr($$romref, 6, 1) = "\x00"; + my $sum = unpack('%8C*', $$romref); + substr($$romref, 6, 1) = chr(256 - $sum); + # Double check + $sum = unpack('%8C*', $$romref); + if ($sum != 0) { + print "Checksum fails\n" + } elsif ($opts{'v'}) { + print "Checksum ok\n"; + } +} + +sub makerom () { + my ($rom, $romsize); + + getopts('3xi:p:s:v', \%opts); + $ARGV[0] or die "Usage: $0 [-s romsize] [-i ident] [-p vendorid,deviceid] [-x] [-3] rom-file\n"; + open(R, $ARGV[0]) or die "$ARGV[0]: $!\n"; + # Read in the whole ROM in one gulp + my $filesize = read(R, $rom, MAXROMSIZE+1); + close(R); + defined($filesize) and $filesize >= 3 or die "Cannot get first 3 bytes of file\n"; + print "$filesize bytes read\n" if $opts{'v'}; + # If PXE image, just fill the length field and write it out + if ($opts{'x'}) { + substr($rom, 2, 1) = chr((length($rom) + 511) / 512); + writerom($ARGV[0], \$rom); + return; + } + # Size specified with -s overrides value in 3rd byte in image + # -s 0 means round up to next 512 byte block + if (defined($opts{'s'})) { + if (($romsize = oct($opts{'s'})) <= 0) { + # NB: This roundup trick only works on powers of 2 + $romsize = ($filesize + 511) & ~511 + } + } else { + # Shrink romsize down to the smallest power of two that will do + for ($romsize = MAXROMSIZE; + $romsize > MINROMSIZE and $romsize >= 2*$filesize; + $romsize /= 2) { } + } + if ($filesize > $romsize) { + print STDERR "ROM size of $romsize not big enough for data, "; + # NB: This roundup trick only works on powers of 2 + $romsize = ($filesize + 511) & ~511; + print "will use $romsize instead\n" + } + # Pad with 0xFF to $romsize + $rom .= "\xFF" x ($romsize - length($rom)); + if ($romsize >= 128 * 1024) { + print "Warning: ROM size exceeds extension BIOS limit\n"; + } + substr($rom, 2, 1) = chr(($romsize / 512) % 256); + print "ROM size is $romsize\n" if $opts{'v'}; + # set the product string only if we don't have one yet + my $pnp_hdr_offset = unpack('v', substr($rom, PNP_PTR_LOC, 2)); + my $identoffset = substr($rom, $pnp_hdr_offset+PNP_DEVICE_OFF, 2) eq "\0\0" ? addident(\$rom) : undef; + pcipnpheaders(\$rom, $identoffset); + undiheaders(\$rom); + # 3c503 requires last two bytes to be 0x80 + substr($rom, MINROMSIZE-2, 2) = "\x80\x80" + if ($opts{'3'} and $romsize == MINROMSIZE); + checksum(\$rom); + writerom($ARGV[0], \$rom); +} + +sub modrom () { + my ($rom); + + getopts('p:v', \%opts); + $ARGV[0] or die "Usage: $0 [-p vendorid,deviceid] rom-file\n"; + open(R, $ARGV[0]) or die "$ARGV[0]: $!\n"; + # Read in the whole ROM in one gulp + my $filesize = read(R, $rom, MAXROMSIZE+1); + close(R); + defined($filesize) and $filesize >= 3 or die "Cannot get first 3 bytes of file\n"; + print "$filesize bytes read\n" if $opts{'v'}; + pcipnpheaders(\$rom, undef); + undiheaders(\$rom); + checksum(\$rom); + writerom($ARGV[0], \$rom); +} + +# Main routine. See how we were called and behave accordingly +if ($0 =~ m:modrom(\.pl)?$:) { + modrom(); +} else { + makerom(); +} +exit(0); diff --git a/gpxe/src/util/mergerom.pl b/gpxe/src/util/mergerom.pl new file mode 100755 index 0000000..f9c5250 --- /dev/null +++ b/gpxe/src/util/mergerom.pl @@ -0,0 +1,98 @@ +#!/usr/bin/perl -w +# +# Copyright (C) 2008 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. + +use strict; +use warnings; + +use FindBin; +use lib "$FindBin::Bin"; +use Option::ROM qw ( :all ); + +sub merge_entry_points { + my $baserom_entry = \shift; + my $rom_entry = \shift; + my $offset = shift; + + if ( $$rom_entry ) { + my $old_entry = $$baserom_entry; + $$baserom_entry = ( $offset + $$rom_entry ); + $$rom_entry = $old_entry; + } +} + +my @romfiles = @ARGV; +my @roms = map { my $rom = new Option::ROM; $rom->load($_); $rom } @romfiles; + +my $baserom = shift @roms; +my $offset = $baserom->length; + +foreach my $rom ( @roms ) { + + # Update base length + $baserom->{length} += $rom->{length}; + + # Merge initialisation entry point + merge_entry_points ( $baserom->{init}, $rom->{init}, $offset ); + + # Merge BOFM header + merge_entry_points ( $baserom->{bofm_header}, $rom->{bofm_header}, $offset ); + + # Update PCI header, if present in both + my $baserom_pci = $baserom->pci_header; + my $rom_pci = $rom->pci_header; + if ( $baserom_pci && $rom_pci ) { + + # Update PCI lengths + $baserom_pci->{image_length} += $rom_pci->{image_length}; + if ( exists $baserom_pci->{runtime_length} ) { + if ( exists $rom_pci->{runtime_length} ) { + $baserom_pci->{runtime_length} += $rom_pci->{runtime_length}; + } else { + $baserom_pci->{runtime_length} += $rom_pci->{image_length}; + } + } + + # Merge CLP entry point + if ( exists ( $baserom_pci->{clp_entry} ) && + exists ( $rom_pci->{clp_entry} ) ) { + merge_entry_points ( $baserom_pci->{clp_entry}, $rom_pci->{clp_entry}, + $offset ); + } + } + + # Update PnP header, if present in both + my $baserom_pnp = $baserom->pnp_header; + my $rom_pnp = $rom->pnp_header; + if ( $baserom_pnp && $rom_pnp ) { + merge_entry_points ( $baserom_pnp->{bcv}, $rom_pnp->{bcv}, $offset ); + merge_entry_points ( $baserom_pnp->{bdv}, $rom_pnp->{bdv}, $offset ); + merge_entry_points ( $baserom_pnp->{bev}, $rom_pnp->{bev}, $offset ); + } + + # Fix checksum for this ROM segment + $rom->fix_checksum(); + + $offset += $rom->length; +} + +$baserom->pnp_header->fix_checksum() if $baserom->pnp_header; +$baserom->fix_checksum(); +$baserom->save ( "-" ); +foreach my $rom ( @roms ) { + $rom->save ( "-" ); +} diff --git a/gpxe/src/util/modrom.pl b/gpxe/src/util/modrom.pl new file mode 100755 index 0000000..695468c --- /dev/null +++ b/gpxe/src/util/modrom.pl @@ -0,0 +1,226 @@ +#!/usr/bin/perl -w + +use Getopt::Std; + +use constant MINROMSIZE => 8192; +use constant MAXROMSIZE => 262144; + +use constant PCI_PTR_LOC => 0x18; # from beginning of ROM +use constant PCI_HDR_SIZE => 0x18; +use constant PNP_PTR_LOC => 0x1a; # from beginning of ROM +use constant PNP_HDR_SIZE => 0x20; +use constant PNP_CHKSUM_OFF => 0x9; # bytes from beginning of PnP header +use constant PNP_DEVICE_OFF => 0x10; # bytes from beginning of PnP header +use constant PCI_VEND_ID_OFF => 0x4; # bytes from beginning of PCI header +use constant PCI_DEV_ID_OFF => 0x6; # bytes from beginning of PCI header +use constant PCI_SIZE_OFF => 0x10; # bytes from beginning of PCI header + +use constant UNDI_PTR_LOC => 0x16; # from beginning of ROM +use constant UNDI_HDR_SIZE => 0x16; +use constant UNDI_CHKSUM_OFF => 0x05; + +use strict; + +use vars qw(%opts); + +use bytes; + +sub getromsize ($) { + my ($romref) = @_; + my $i; + + print STDERR "BIOS extension ROM Image did not start with 0x55 0xAA\n" + if (substr($$romref, 0, 2) ne "\x55\xaa"); + my $size = ord(substr($$romref, 2, 1)) * 512; + for ($i = MINROMSIZE; $i < MAXROMSIZE and $i < $size; $i *= 2) { } + print STDERR "$size is a strange size for a boot ROM\n" + if ($size > 0 and $i > $size); + return ($size); +} + +sub addident ($) { + my ($romref) = @_; + + return (0) unless (my $s = $opts{'i'}); + # include the terminating NUL byte too + $s .= "\x00"; + my $len = length($s); + # Put the identifier in only if the space is blank + my $pos = length($$romref) - $len - 2; + return (0) if (substr($$romref, $pos, $len) ne ("\xFF" x $len)); + substr($$romref, $pos, $len) = $s; + return ($pos); +} + +sub pcipnpheaders ($$) { + my ($romref, $identoffset) = @_; + my ($pci_hdr_offset, $pnp_hdr_offset); + + $pci_hdr_offset = unpack('v', substr($$romref, PCI_PTR_LOC, 2)); + $pnp_hdr_offset = unpack('v', substr($$romref, PNP_PTR_LOC, 2)); + # Sanity checks + if ($pci_hdr_offset < PCI_PTR_LOC + 2 + or $pci_hdr_offset > length($$romref) - PCI_HDR_SIZE + or $pnp_hdr_offset < PNP_PTR_LOC + 2 + or $pnp_hdr_offset > length($$romref) - PNP_HDR_SIZE + or substr($$romref, $pci_hdr_offset, 4) ne 'PCIR' + or substr($$romref, $pnp_hdr_offset, 4) ne '$PnP') { + $pci_hdr_offset = $pnp_hdr_offset = 0; + } else { + printf "PCI header at %#x and PnP header at %#x\n", + $pci_hdr_offset, $pnp_hdr_offset; + } + if ($pci_hdr_offset > 0) { + my ($pci_vendor_id, $pci_device_id); + # if no -p option, just report what's there + if (!defined($opts{'p'})) { + $pci_vendor_id = unpack('v', substr($$romref, $pci_hdr_offset+PCI_VEND_ID_OFF, 2)); + $pci_device_id = unpack('v', substr($$romref, $pci_hdr_offset+PCI_DEV_ID_OFF, 2)); + printf "PCI Vendor ID %#x Device ID %#x\n", + $pci_vendor_id, $pci_device_id; + } else { + substr($$romref, $pci_hdr_offset + PCI_SIZE_OFF, 2) + = pack('v', length($$romref) / 512); + ($pci_vendor_id, $pci_device_id) = split(/,/, $opts{'p'}); + substr($$romref, $pci_hdr_offset+PCI_VEND_ID_OFF, 2) + = pack('v', oct($pci_vendor_id)) if ($pci_vendor_id); + substr($$romref, $pci_hdr_offset+PCI_DEV_ID_OFF, 2) + = pack('v', oct($pci_device_id)) if ($pci_device_id); + } + } + if ($pnp_hdr_offset > 0 and defined($identoffset)) { + # Point to device id string at end of ROM image + substr($$romref, $pnp_hdr_offset+PNP_DEVICE_OFF, 2) + = pack('v', $identoffset); + substr($$romref, $pnp_hdr_offset+PNP_CHKSUM_OFF, 1) = "\x00"; + my $sum = unpack('%8C*', substr($$romref, $pnp_hdr_offset, + PNP_HDR_SIZE)); + substr($$romref, $pnp_hdr_offset+PNP_CHKSUM_OFF, 1) = chr(256 - $sum); + } +} + +sub undiheaders ($) { + my ($romref) = @_; + my ($undi_hdr_offset); + + $undi_hdr_offset = unpack('v', substr($$romref, UNDI_PTR_LOC, 2)); + # Sanity checks + if ($undi_hdr_offset < UNDI_PTR_LOC + 2 + or $undi_hdr_offset > length($$romref) - UNDI_HDR_SIZE + or substr($$romref, $undi_hdr_offset, 4) ne 'UNDI') { + $undi_hdr_offset = 0; + } else { + printf "UNDI header at %#x\n", $undi_hdr_offset; + } + if ($undi_hdr_offset > 0) { + substr($$romref, $undi_hdr_offset+UNDI_CHKSUM_OFF, 1) = "\x00"; + my $sum = unpack('%8C*', substr($$romref, $undi_hdr_offset, + UNDI_HDR_SIZE)); + substr($$romref, $undi_hdr_offset+UNDI_CHKSUM_OFF, 1) = chr(256 - $sum); + } +} + +sub writerom ($$) { + my ($filename, $romref) = @_; + + open(R, ">$filename") or die "$filename: $!\n"; + print R $$romref; + close(R); +} + +sub checksum ($) { + my ($romref) = @_; + + substr($$romref, 5, 1) = "\x00"; + my $sum = unpack('%8C*', $$romref); + substr($$romref, 5, 1) = chr(256 - $sum); + # Double check + $sum = unpack('%8C*', $$romref); + if ($sum != 0) { + print "Checksum fails\n" + } elsif ($opts{'v'}) { + print "Checksum ok\n"; + } +} + +sub makerom () { + my ($rom, $romsize); + + getopts('3xi:p:s:v', \%opts); + $ARGV[0] or die "Usage: $0 [-s romsize] [-i ident] [-p vendorid,deviceid] [-x] [-3] rom-file\n"; + open(R, $ARGV[0]) or die "$ARGV[0]: $!\n"; + # Read in the whole ROM in one gulp + my $filesize = read(R, $rom, MAXROMSIZE+1); + close(R); + defined($filesize) and $filesize >= 3 or die "Cannot get first 3 bytes of file\n"; + print "$filesize bytes read\n" if $opts{'v'}; + # If PXE image, just fill the length field and write it out + if ($opts{'x'}) { + substr($rom, 2, 1) = chr((length($rom) + 511) / 512); + &writerom($ARGV[0], \$rom); + return; + } + # Size specified with -s overrides value in 3rd byte in image + # -s 0 means round up to next 512 byte block + if (defined($opts{'s'})) { + if (($romsize = oct($opts{'s'})) <= 0) { + # NB: This roundup trick only works on powers of 2 + $romsize = ($filesize + 511) & ~511 + } + } else { + $romsize = &getromsize(\$rom); + # 0 put there by *loader.S means makerom should pick the size + if ($romsize == 0) { + # Shrink romsize down to the smallest power of two that will do + for ($romsize = MAXROMSIZE; + $romsize > MINROMSIZE and $romsize >= 2*$filesize; + $romsize /= 2) { } + } + } + if ($filesize > $romsize) { + print STDERR "ROM size of $romsize not big enough for data, "; + # NB: This roundup trick only works on powers of 2 + $romsize = ($filesize + 511) & ~511; + print "will use $romsize instead\n" + } + # Pad with 0xFF to $romsize + $rom .= "\xFF" x ($romsize - length($rom)); + if ($romsize >= 128 * 1024) { + print "Warning: ROM size exceeds extension BIOS limit\n"; + } + substr($rom, 2, 1) = chr(($romsize / 512) % 256); + print "ROM size is $romsize\n" if $opts{'v'}; + my $identoffset = &addident(\$rom); + &pcipnpheaders(\$rom, $identoffset); + &undiheaders(\$rom); + # 3c503 requires last two bytes to be 0x80 + substr($rom, MINROMSIZE-2, 2) = "\x80\x80" + if ($opts{'3'} and $romsize == MINROMSIZE); + &checksum(\$rom); + &writerom($ARGV[0], \$rom); +} + +sub modrom () { + my ($rom); + + getopts('p:v', \%opts); + $ARGV[0] or die "Usage: $0 [-p vendorid,deviceid] rom-file\n"; + open(R, $ARGV[0]) or die "$ARGV[0]: $!\n"; + # Read in the whole ROM in one gulp + my $filesize = read(R, $rom, MAXROMSIZE+1); + close(R); + defined($filesize) and $filesize >= 3 or die "Cannot get first 3 bytes of file\n"; + print "$filesize bytes read\n" if $opts{'v'}; + &pcipnpheaders(\$rom); + &undiheaders(\$rom); + &checksum(\$rom); + &writerom($ARGV[0], \$rom); +} + +# Main routine. See how we were called and behave accordingly +if ($0 =~ m:modrom(\.pl)?$:) { + &modrom(); +} else { + &makerom(); +} +exit(0); diff --git a/gpxe/src/util/mucurses_test.c b/gpxe/src/util/mucurses_test.c new file mode 100644 index 0000000..586562d --- /dev/null +++ b/gpxe/src/util/mucurses_test.c @@ -0,0 +1,63 @@ +#include "../include/curses.h" +#include +#include +#include + +void get_iscsi_chap_secret( char * ); +void mdelay( int msecs ); + +int main ( void ) { + char secret[16]; + initscr(); + echo(); + werase(stdscr); + box( stdscr, '|', '-' ); + get_iscsi_chap_secret(secret); + + mvwprintw( stdscr, 3, 5, "password is \"%s\"", secret ); + mdelay(2500); + + stdscr->scr->exit(stdscr->scr); + + return 0; +} + +void get_iscsi_chap_secret( char *sec ) { + char *title = "Set new iSCSI CHAP secret", + *msg = "Configure the iSCSI access secret", + pw1[17], pw2[17]; + WINDOW *secret; + + secret = newwin( stdscr->height / 2, + stdscr->width / 2, + stdscr->height / 4, + stdscr->width / 4 ); + + wborder( secret, '|', '|', '-', '-', '+', '+', '+', '+' ); + mvwprintw( secret, 1, 2, "%s", title ); + mvwhline( secret, 2, 1, '-' | secret->attrs, secret->width - 2 ); + mvwprintw( secret, 4, 2, "%s", msg ); + mvwprintw( secret, 6, 3, "secret" ); + mvwprintw( secret, 8, 3, "confirm" ); + start: + mvwhline( secret, 6, 12, '_' | secret->attrs, 16 ); + mvwhline( secret, 8, 12, '_' | secret->attrs, 16 ); + + wmove( secret, 6, 12 ); + wgetnstr( secret, pw1, 16 ); + wmove( secret, 8, 12 ); + wgetnstr( secret, pw2, 16 ); + + if ( strcmp( pw1, pw2 ) == 0 ) { + strcpy( sec, pw1 ); + werase( secret ); + } + else { + mvwprintw( secret, 10, 3, "Passwords do not match" ); + goto start; + } +} + +void mdelay ( int msecs ) { + usleep( msecs * 1000 ); +} diff --git a/gpxe/src/util/nrv2b.c b/gpxe/src/util/nrv2b.c new file mode 100644 index 0000000..6bac4cd --- /dev/null +++ b/gpxe/src/util/nrv2b.c @@ -0,0 +1,1501 @@ +/************************************************************** + Form adapted from lzhuf.c + written by Haruyasu Yoshizaki 11/20/1988 + some minor changes 4/6/1989 + comments translated by Haruhiko Okumura 4/7/1989 + + minor beautifications and adjustments for compiling under Linux + by Markus Gutschke + 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/gpxe/src/util/parserom.pl b/gpxe/src/util/parserom.pl new file mode 100644 index 0000000..3d41b54 --- /dev/null +++ b/gpxe/src/util/parserom.pl @@ -0,0 +1,64 @@ +#!/usr/bin/perl -w +# +# Parse PCI_ROM and ISA_ROM entries from a source file on stdin and +# output the relevant Makefile variable definitions to stdout +# +# Based upon portions of Ken Yap's genrules.pl + +use strict; +use warnings; + +die "Syntax: $0 driver_source.c" unless @ARGV == 1; +my $source = shift; +open DRV, "<$source" or die "Could not open $source: $!\n"; + +( my $family, my $driver_name ) = ( $source =~ /^(.*?([^\/]+))\..$/ ) + or die "Could not parse source file name \"$source\"\n"; + +my $printed_family; + +sub rom { + ( my $type, my $image, my $desc, my $vendor, my $device ) = @_; + my $ids = $vendor ? "$vendor,$device" : "-"; + unless ( $printed_family ) { + print "\n"; + print "# NIC\t\n"; + print "# NIC\tfamily\t$family\n"; + print "DRIVERS += $driver_name\n"; + $printed_family = 1; + } + print "\n"; + print "# NIC\t$image\t$ids\t$desc\n"; + print "DRIVER_$image = $driver_name\n"; + print "ROM_TYPE_$image = $type\n"; + print "ROM_DESCRIPTION_$image = \"$desc\"\n"; + print "PCI_VENDOR_$image = 0x$vendor\n" if $vendor; + print "PCI_DEVICE_$image = 0x$device\n" if $device; + print "ROMS += $image\n"; + print "ROMS_$driver_name += $image\n"; +} + +while ( ) { + next unless /(PCI|ISA)_ROM\s*\(/; + + if ( /^\s*PCI_ROM\s*\( + \s*0x([0-9A-Fa-f]{4})\s*, # PCI vendor + \s*0x([0-9A-Fa-f]{4})\s*, # PCI device + \s*\"([^\"]*)\"\s*, # Image + \s*\"([^\"]*)\"\s* # Description + \)/x ) { + ( my $vendor, my $device, my $image, my $desc ) = ( lc $1, lc $2, $3, $4 ); + rom ( "pci", $image, $desc, $vendor, $device ); + rom ( "pci", lc "${vendor}${device}", $desc, $vendor, $device ); + } elsif ( /^\s*ISA_ROM\s*\( + \s*\"([^\"]*)\"\s*, # Image + \s*\"([^\"]*)\"\s* # Description + \)/x ) { + ( my $image, my $desc ) = ( $1, $2 ); + rom ( "isa", $image, $desc ); + } else { + warn "Malformed PCI_ROM or ISA_ROM macro on line $. of $source\n"; + } +} + +close DRV; diff --git a/gpxe/src/util/sortobjdump.pl b/gpxe/src/util/sortobjdump.pl new file mode 100755 index 0000000..1373a7f --- /dev/null +++ b/gpxe/src/util/sortobjdump.pl @@ -0,0 +1,40 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; + +# Sort the symbol table portion of the output of objdump -ht by +# section, then by symbol value, then by size. Used to enhance the +# linker maps produced by "make bin/%.map" by also showing the values +# of all non-global symbols. + +my %section_idx = ( "*ABS*" => ".", "*UND*" => "_" ); +my %lines; +while ( <> ) { + if ( /^\s+(\d+)\s+([\.\*]\S+)\s+[0-9a-fA-F]+\s+[0-9a-fA-F]/ ) { + # It's a header line containing a section definition; extract the + # section index and store it. Also print the header line. + print; + ( my $index, my $section ) = ( $1, $2 ); + $section_idx{$section} = sprintf ( "%02d", $index ); + } elsif ( /^([0-9a-fA-F]+)\s.*?\s([\.\*]\S+)\s+([0-9a-fA-F]+)\s+(\S+)/ ) { + # It's a symbol line - store it in the hash, indexed by + # ":::". is "0" if + # the symbol name is of the form xxx_end, "1" otherwise; this is + # done so that table end markers show up before any other symbols + # with the same value. + ( my $value, my $section, my $size, my $name ) = ( $1, $2, $3, $4 ); + die "Unrecognised section \"$section\"\n" + unless exists $section_idx{$section}; + my $section_idx = $section_idx{$section}; + my $end = ( $name =~ /_end$/ ) ? "0" : "1"; + my $key = $section_idx.":".$value.":".$size.":".$end; + $lines{$key} ||= ''; + $lines{$key} .= $_; + } else { + # It's a generic header line: just print it. + print; + } +} + +print $lines{$_} foreach sort keys %lines; diff --git a/gpxe/src/util/swapdevids.pl b/gpxe/src/util/swapdevids.pl new file mode 100755 index 0000000..c6255ae --- /dev/null +++ b/gpxe/src/util/swapdevids.pl @@ -0,0 +1,49 @@ +#!/usr/bin/perl -w +# +# Program to reverse the device identifier IDs in the PCIR and PnP +# structures in a ROM for old non-compliant BIOSes +# +# GPL, Ken Yap 2001 +# + +use bytes; + +use IO::Seekable; + +sub swaplocs ($$$) +{ + my ($dataref, $loc1, $loc2) = @_; + my ($t); + + $t = substr($$dataref, $loc1, 1); + substr($$dataref, $loc1, 1) = substr($$dataref, $loc2, 1); + substr($$dataref, $loc2, 1) = $t; +} + +sub printdevids ($$) +{ + my ($dataref, $loc) = @_; + + return (sprintf "%02x %02x %02x", unpack('C3', substr($$dataref, $loc, 3))); +} + +$#ARGV >= 0 or die "Usage: $0 romimage\n"; +$file = $ARGV[0]; +open(F, "+<$file") or die "$file: $!\n"; +binmode(F); +# Handle up to 64kB ROM images +$len = read(F, $data, 64*1024); +defined($len) or die "$file: $!\n"; +substr($data, 0, 2) eq "\x55\xAA" or die "$file: Not a boot ROM image\n"; +($pci, $pnp) = unpack('v2', substr($data, 0x18, 4)); +($pci < $len and $pnp < $len) or die "$file: Not a PCI PnP ROM image\n"; +(substr($data, $pci, 4) eq 'PCIR' and substr($data, $pnp, 4) eq '$PnP') + or die "$file: No PCI and PNP structures, not a PCI PNP ROM image\n"; +&swaplocs(\$data, $pci+13, $pci+15); +&swaplocs(\$data, $pnp+18, $pnp+20); +seek(F, 0, SEEK_SET) or die "$file: Cannot seek to beginning\n"; +print F $data; +close(F); +print "PCI devids now: ", &printdevids(\$data, $pci+13), "\n"; +print "PnP devids now: ", &printdevids(\$data, $pnp+18), "\n"; +exit(0); diff --git a/gpxe/src/util/symcheck.pl b/gpxe/src/util/symcheck.pl new file mode 100755 index 0000000..8925ca6 --- /dev/null +++ b/gpxe/src/util/symcheck.pl @@ -0,0 +1,191 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; + +use constant WARNING_SIZE => 512; + +my $symtab = {}; + +# Scan output of "objdump -w -t bin/blib.a" and build up symbol table +# +my $object; +while ( <> ) { + chomp; + if ( /^In archive/ ) { + # Do nothing + } elsif ( /^$/ ) { + # Do nothing + } elsif ( /^(\S+\.o):\s+file format/ ) { + $object = $1; + } elsif ( /^SYMBOL TABLE:/ ) { + # Do nothing + } elsif ( /^([0-9a-fA-F]+)\s(l|g|\s)......\s(\S+)\s+([0-9a-fA-F]+)\s+(\S+)$/ ) { + my $value = $1; + my $scope = $2; + my $section = $3; + my $size = $4; + my $symbol = $5; + $symtab->{$object}->{$symbol} = { + global => ( $scope ne "l" ), + section => ( $section eq "*UND*" ? undef : $section ), + value => ( $value ? hex ( $value ) : 0 ), + size => ( $size ? hex ( $size ) : 0 ), + }; + } else { + die "Unrecognized line \"$_\""; + } +} + +# Add symbols that we know will be generated or required by the linker +# +foreach my $object ( keys %$symtab ) { + my $obj_symbol = "obj_$object"; + $obj_symbol =~ s/\.o$//; + $obj_symbol =~ s/\W/_/g; + $symtab->{LINKER}->{$obj_symbol} = { + global => 1, + section => undef, + value => 0, + size => 0, + }; +} +foreach my $link_sym qw ( __prefix _prefix _prefix_load_offset + _prefix_size _prefix_progbits_size _prefix_size_pgh + __text16 _text16 _text16_load_offset + _text16_size _text16_progbits_size _text16_size_pgh + __data16 _data16 _data16_load_offset + _data16_size _data16_progbits_size _data16_size_pgh + __text _text __data _data _textdata_load_offset + _textdata_size _textdata_progbits_size + __rodata __bss _end + _payload_offset _max_align + _load_size _load_size_pgh _load_size_sect + pci_vendor_id pci_device_id ) { + $symtab->{LINKER}->{$link_sym} = { + global => 1, + section => '*ABS*', + value => 0, + size => 0, + }; +} + +# Add symbols that we know will be used by the debug system +# +foreach my $debug_sym qw ( dbg_autocolourise dbg_decolourise + dbg_hex_dump_da ) { + $symtab->{DEBUG}->{$debug_sym} = { + global => 1, + section => undef, + value => 0, + size => 0, + }; +} + +# Build up requires, provides and shares symbol tables for global +# symbols +# +my $globals = {}; +while ( ( my $object, my $symbols ) = each %$symtab ) { + while ( ( my $symbol, my $info ) = each %$symbols ) { + if ( $info->{global} ) { + my $category; + if ( ! defined $info->{section} ) { + $category = "requires"; + } elsif ( $info->{section} eq "*COM*" ) { + $category = "shares"; + } else { + $category = "provides"; + } + $globals->{$symbol}->{$category}->{$object} = 1; + } + } +} + +# Check for multiply defined, never-defined and unused global symbols +# +my $problems = {}; +while ( ( my $symbol, my $info ) = each %$globals ) { + my @provides = keys %{$info->{provides}}; + my @requires = keys %{$info->{requires}}; + my @shares = keys %{$info->{shares}}; + + if ( ( @provides == 0 ) && ( @shares == 1 ) ) { + # A symbol "shared" by just a single file is actually being + # provided by that file; it just doesn't have an initialiser. + @provides = @shares; + @shares = (); + } + + if ( ( @requires > 0 ) && ( @provides == 0 ) && ( @shares == 0 ) ) { + # No object provides this symbol, but some objects require it. + $problems->{$_}->{nonexistent}->{$symbol} = 1 foreach @requires; + } + + if ( ( @requires == 0 ) && ( @provides > 0 ) ) { + # No object requires this symbol, but some objects provide it. + foreach my $provide ( @provides ) { + if ( $provide eq "LINKER" ) { + # Linker-provided symbols are exempt from this check. + } elsif ( $symtab->{$provide}->{$symbol}->{section} =~ /^\.tbl\./ ) { + # Linker tables are exempt from this check. + } else { + $problems->{$provide}->{unused}->{$symbol} = 1; + } + } + } + + if ( ( @shares > 0 ) && ( @provides > 0 ) ) { + # A shared symbol is being initialised by an object + $problems->{$_}->{shared}->{$symbol} = 1 foreach @provides; + } + + if ( @provides > 1 ) { + # A non-shared symbol is defined in multiple objects + $problems->{$_}->{multiples}->{$symbol} = 1 foreach @provides; + } +} + +# Check for excessively large local symbols. Text and rodata symbols +# are exempt from this check +# +while ( ( my $object, my $symbols ) = each %$symtab ) { + while ( ( my $symbol, my $info ) = each %$symbols ) { + if ( ( ! $info->{global} ) && + ( ( defined $info->{section} ) && + ! ( $info->{section} =~ /^(\.text|\.rodata)/ ) ) && + ( $info->{size} >= WARNING_SIZE ) ) { + $problems->{$object}->{large}->{$symbol} = 1; + } + } +} + +# Print out error messages +# +my $errors = 0; +my $warnings = 0; +foreach my $object ( sort keys %$problems ) { + my @nonexistent = sort keys %{$problems->{$object}->{nonexistent}}; + my @multiples = sort keys %{$problems->{$object}->{multiples}}; + my @unused = sort keys %{$problems->{$object}->{unused}}; + my @shared = sort keys %{$problems->{$object}->{shared}}; + my @large = sort keys %{$problems->{$object}->{large}}; + + print "WARN $object provides unused symbol $_\n" foreach @unused; + $warnings += @unused; + print "WARN $object has large static symbol $_\n" foreach @large; + $warnings += @large; + print "ERR $object requires non-existent symbol $_\n" foreach @nonexistent; + $errors += @nonexistent; + foreach my $symbol ( @multiples ) { + my @other_objects = sort grep { $_ ne $object } + keys %{$globals->{$symbol}->{provides}}; + print "ERR $object provides symbol $symbol" + ." (also provided by @other_objects)\n"; + } + $errors += @multiples; + print "ERR $object misuses shared symbol $_\n" foreach @shared; +} + +print "$errors error(s), $warnings warning(s)\n"; +exit ( $errors ? 1 : 0 ); diff --git a/gpxe/src/util/zbin.c b/gpxe/src/util/zbin.c new file mode 100644 index 0000000..c1082b3 --- /dev/null +++ b/gpxe/src/util/zbin.c @@ -0,0 +1,353 @@ +#include +#include + +#define ENCODE +#define VERBOSE +#include "nrv2b.c" +FILE *infile, *outfile; + +#define DEBUG 0 + +struct input_file { + void *buf; + size_t len; +}; + +struct output_file { + void *buf; + size_t len; + size_t max_len; +}; + +struct zinfo_common { + char type[4]; + char pad[12]; +}; + +struct zinfo_copy { + char type[4]; + uint32_t offset; + uint32_t len; + uint32_t align; +}; + +struct zinfo_pack { + char type[4]; + uint32_t offset; + uint32_t len; + uint32_t align; +}; + +struct zinfo_subtract { + char type[4]; + uint32_t offset; + uint32_t divisor; + uint32_t pad; +}; + +union zinfo_record { + struct zinfo_common common; + struct zinfo_copy copy; + struct zinfo_pack pack; + struct zinfo_subtract subtract; +}; + +struct zinfo_file { + union zinfo_record *zinfo; + unsigned int num_entries; +}; + +static int read_file ( const char *filename, void **buf, size_t *len ) { + FILE *file; + struct stat stat; + + file = fopen ( filename, "r" ); + if ( ! file ) { + fprintf ( stderr, "Could not open %s: %s\n", filename, + strerror ( errno ) ); + goto err; + } + + if ( fstat ( fileno ( file ), &stat ) < 0 ) { + fprintf ( stderr, "Could not stat %s: %s\n", filename, + strerror ( errno ) ); + goto err; + } + + *len = stat.st_size; + *buf = malloc ( *len ); + if ( ! *buf ) { + fprintf ( stderr, "Could not malloc() %d bytes for %s: %s\n", + *len, filename, strerror ( errno ) ); + goto err; + } + + if ( fread ( *buf, 1, *len, file ) != *len ) { + fprintf ( stderr, "Could not read %d bytes from %s: %s\n", + *len, filename, strerror ( errno ) ); + goto err; + } + + fclose ( file ); + return 0; + + err: + if ( file ) + fclose ( file ); + return -1; +} + +static int read_input_file ( const char *filename, + struct input_file *input ) { + return read_file ( filename, &input->buf, &input->len ); +} + +static int read_zinfo_file ( const char *filename, + struct zinfo_file *zinfo ) { + void *buf; + size_t len; + + if ( read_file ( filename, &buf, &len ) < 0 ) + return -1; + + if ( ( len % sizeof ( *(zinfo->zinfo) ) ) != 0 ) { + fprintf ( stderr, ".zinfo file %s has invalid length %d\n", + filename, len ); + return -1; + } + + zinfo->zinfo = buf; + zinfo->num_entries = ( len / sizeof ( *(zinfo->zinfo) ) ); + return 0; +} + +static int alloc_output_file ( size_t max_len, struct output_file *output ) { + output->len = 0; + output->max_len = ( max_len ); + output->buf = malloc ( max_len ); + if ( ! output->buf ) { + fprintf ( stderr, "Could not allocate %d bytes for output\n", + max_len ); + return -1; + } + memset ( output->buf, 0xff, sizeof ( output->buf ) ); + return 0; +} + +static int process_zinfo_copy ( struct input_file *input, + struct output_file *output, + union zinfo_record *zinfo ) { + struct zinfo_copy *copy = &zinfo->copy; + size_t offset = copy->offset; + size_t len = copy->len; + unsigned int align = copy->align; + + if ( ( offset + len ) > input->len ) { + fprintf ( stderr, "Input buffer overrun on copy\n" ); + return -1; + } + + output->len = ( ( output->len + align - 1 ) & ~( align - 1 ) ); + if ( ( output->len + len ) > output->max_len ) { + fprintf ( stderr, "Output buffer overrun on copy\n" ); + return -1; + } + + if ( DEBUG ) { + fprintf ( stderr, "COPY [%#zx,%#zx) to [%#zx,%#zx)\n", offset, ( offset + len ), + output->len, ( output->len + len ) ); + } + + memcpy ( ( output->buf + output->len ), + ( input->buf + offset ), len ); + output->len += len; + return 0; +} + +static int process_zinfo_pack ( struct input_file *input, + struct output_file *output, + union zinfo_record *zinfo ) { + struct zinfo_pack *pack = &zinfo->pack; + size_t offset = pack->offset; + size_t len = pack->len; + unsigned int align = pack->align; + unsigned long packed_len; + + if ( ( offset + len ) > input->len ) { + fprintf ( stderr, "Input buffer overrun on pack\n" ); + return -1; + } + + output->len = ( ( output->len + align - 1 ) & ~( align - 1 ) ); + if ( output->len > output->max_len ) { + fprintf ( stderr, "Output buffer overrun on pack\n" ); + return -1; + } + + if ( ucl_nrv2b_99_compress ( ( input->buf + offset ), len, + ( output->buf + output->len ), + &packed_len, 0 ) != UCL_E_OK ) { + fprintf ( stderr, "Compression failure\n" ); + return -1; + } + + if ( DEBUG ) { + fprintf ( stderr, "PACK [%#zx,%#zx) to [%#zx,%#zx)\n", offset, ( offset + len ), + output->len, ( output->len + packed_len ) ); + } + + output->len += packed_len; + if ( output->len > output->max_len ) { + fprintf ( stderr, "Output buffer overrun on pack\n" ); + return -1; + } + + return 0; +} + +static int process_zinfo_subtract ( struct input_file *input, + struct output_file *output, + struct zinfo_subtract *subtract, + size_t datasize ) { + size_t offset = subtract->offset; + void *target; + long delta; + unsigned long old; + unsigned long new; + + if ( ( offset + datasize ) > output->len ) { + fprintf ( stderr, "Subtract at %#zx outside output buffer\n", + offset ); + return -1; + } + + target = ( output->buf + offset ); + delta = ( ( output->len / subtract->divisor ) - + ( input->len / subtract->divisor ) ); + + switch ( datasize ) { + case 1: { + uint8_t *byte = target; + old = *byte; + *byte += delta; + new = *byte; + break; } + case 2: { + uint16_t *word = target; + old = *word; + *word += delta; + new = *word; + break; } + case 4: { + uint32_t *dword = target; + old = *dword; + *dword += delta; + new = *dword; + break; } + default: + fprintf ( stderr, "Unsupported subtract datasize %d\n", + datasize ); + return -1; + } + + if ( DEBUG ) { + fprintf ( stderr, "SUBx [%#zx,%#zx) (%#lx+(%#lx/%#lx)-(%#lx/%#lx)) = %#lx\n", + offset, ( offset + datasize ), old, output->len, subtract->divisor, + input->len, subtract->divisor, new ); + } + + return 0; +} + +static int process_zinfo_subb ( struct input_file *input, + struct output_file *output, + union zinfo_record *zinfo ) { + return process_zinfo_subtract ( input, output, &zinfo->subtract, 1 ); +} + +static int process_zinfo_subw ( struct input_file *input, + struct output_file *output, + union zinfo_record *zinfo ) { + return process_zinfo_subtract ( input, output, &zinfo->subtract, 2 ); +} + +static int process_zinfo_subl ( struct input_file *input, + struct output_file *output, + union zinfo_record *zinfo ) { + return process_zinfo_subtract ( input, output, &zinfo->subtract, 4 ); +} + +struct zinfo_processor { + char *type; + int ( * process ) ( struct input_file *input, + struct output_file *output, + union zinfo_record *zinfo ); +}; + +static struct zinfo_processor zinfo_processors[] = { + { "COPY", process_zinfo_copy }, + { "PACK", process_zinfo_pack }, + { "SUBB", process_zinfo_subb }, + { "SUBW", process_zinfo_subw }, + { "SUBL", process_zinfo_subl }, +}; + +static int process_zinfo ( struct input_file *input, + struct output_file *output, + union zinfo_record *zinfo ) { + struct zinfo_common *common = &zinfo->common; + struct zinfo_processor *processor; + char type[ sizeof ( common->type ) + 1 ] = ""; + unsigned int i; + + strncat ( type, common->type, sizeof ( type ) - 1 ); + for ( i = 0 ; i < ( sizeof ( zinfo_processors ) / + sizeof ( zinfo_processors[0] ) ) ; i++ ) { + processor = &zinfo_processors[i]; + if ( strcmp ( processor->type, type ) == 0 ) + return processor->process ( input, output, zinfo ); + } + + fprintf ( stderr, "Unknown zinfo record type \"%s\"\n", &type[0] ); + return -1; +} + +static int write_output_file ( struct output_file *output ) { + if ( fwrite ( output->buf, 1, output->len, stdout ) != output->len ) { + fprintf ( stderr, "Could not write %d bytes of output: %s\n", + output->len, strerror ( errno ) ); + return -1; + } + return 0; +} + +int main ( int argc, char **argv ) { + struct input_file input; + struct output_file output; + struct zinfo_file zinfo; + unsigned int i; + + if ( argc != 3 ) { + fprintf ( stderr, "Syntax: %s file.bin file.zinfo " + "> file.zbin\n", argv[0] ); + exit ( 1 ); + } + + if ( read_input_file ( argv[1], &input ) < 0 ) + exit ( 1 ); + if ( read_zinfo_file ( argv[2], &zinfo ) < 0 ) + exit ( 1 ); + if ( alloc_output_file ( ( input.len * 4 ), &output ) < 0 ) + exit ( 1 ); + + for ( i = 0 ; i < zinfo.num_entries ; i++ ) { + if ( process_zinfo ( &input, &output, + &zinfo.zinfo[i] ) < 0 ) + exit ( 1 ); + } + + if ( write_output_file ( &output ) < 0 ) + exit ( 1 ); + + return 0; +} diff --git a/libfat/cache.c b/libfat/cache.c new file mode 100644 index 0000000..9e8df36 --- /dev/null +++ b/libfat/cache.c @@ -0,0 +1,65 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * cache.c + * + * Simple sector cache + */ + +#include +#include "libfatint.h" + +void * libfat_get_sector(struct libfat_filesystem *fs, libfat_sector_t n) +{ + struct libfat_sector *ls; + + for ( ls = fs->sectors ; ls ; ls = ls->next ) { + if ( ls->n == n ) + return ls->data; /* Found in cache */ + } + + /* Not found in cache */ + ls = malloc(sizeof(struct libfat_sector)); + if ( !ls ) { + libfat_flush(fs); + ls = malloc(sizeof(struct libfat_sector)); + + if ( !ls ) + return NULL; /* Can't allocate memory */ + } + + if ( fs->read(fs->readptr, ls->data, LIBFAT_SECTOR_SIZE, n) + != LIBFAT_SECTOR_SIZE ) { + free(ls); + return NULL; /* I/O error */ + } + + ls->n = n; + ls->next = fs->sectors; + fs->sectors = ls; + + return ls->data; +} + +void libfat_flush(struct libfat_filesystem *fs) +{ + struct libfat_sector *ls, *lsnext; + + lsnext = fs->sectors; + fs->sectors = NULL; + + for ( ls = lsnext ; ls ; ls = lsnext ) { + lsnext = ls->next; + free(ls); + } +} diff --git a/libfat/fat.h b/libfat/fat.h new file mode 100644 index 0000000..116e244 --- /dev/null +++ b/libfat/fat.h @@ -0,0 +1,110 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2001-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * fat.h + * + * Basic data structures for a FAT filesystem + */ + +#ifndef FAT_H +#define FAT_H + +#include "ulint.h" + +/* The poor excuse FAT has for a superblock -- in the boot sector */ +struct fat_bootsect { + le8_t bsJump[3]; /* Jump to code */ + char bsOemName[8]; /* Formatting program */ + le16_t bsBytesPerSec; /* Bytes/sector */ + le8_t bsSecPerClust; /* Sectors/cluster */ + le16_t bsResSectors; /* Reserved sectors */ + le8_t bsFATs; /* Number of FATs */ + le16_t bsRootDirEnts; /* Number of entries/root directory */ + le16_t bsSectors; /* Number of sectors [1] */ + le8_t bsMedia; /* Magic media type byte */ + le16_t bsFATsecs; /* Sectors/FAT */ + le16_t bsSecPerTrack; /* Sectors/track */ + le16_t bsHeads; /* Number of heads */ + le32_t bsHiddenSecs; /* Number of hidden sectors */ + le32_t bsHugeSectors; /* Number of sectors [2] */ + union { + /* FAT12/16 */ + struct { + le8_t bsDriveNumber; /* Drive number */ + le8_t bsReserved1; /* Reserved */ + le8_t bsBootSignature; /* 0x29 */ + le32_t bsVolumeID; /* Volume serial number */ + char bsVolumeLabel[11]; /* Volume name */ + char bsFileSysType[8]; /* File system type */ + + le8_t bsCode[448]; /* Boot sector code */ + } fat16; + + /* FAT32 */ + struct { + le32_t bpb_fatsz32; /* Sectors/FAT */ + le16_t bpb_extflags; /* Extended flags */ + le16_t bpb_fsver; /* Filesystem version */ + le32_t bpb_rootclus; /* Root directory cluster */ + le16_t bpb_fsinfo; /* FSINFO sector number */ + le16_t bpb_bkbootsec; /* Backup boot sector (superblock) */ + char bpb_reserved[12]; + + /* Same shit, different offset! */ + le8_t bsDriveNumber; /* Drive number */ + le8_t bsReserved1; /* Reserved */ + le8_t bsBootSignature; /* 0x29 */ + le32_t bsVolumeID; /* Volume serial number */ + char bsVolumeLabel[11]; /* Volume name */ + char bsFileSysType[8]; /* File system type */ + + le8_t bsCode[420]; /* Boot sector code */ + } fat32; + } u; + + le16_t bsSignature; /* 0xAA55 */ +}; + +#define BS_BOOTSIGNATURE 0x29 +#define BS_SIGNATURE 0xAA55 + +/* A FAT filesystem directory entry */ + +struct fat_dirent +{ + le8_t name[11]; /* Mangled filename */ + le8_t attribute; /* File type/attribute */ + le8_t caseflags; /* VFAT: case for basis and extension */ + le8_t ctime_ms; /* ms of creation time */ + le32_t ctime; /* Creation time */ + le16_t atime; /* Date portion (high 16 bits) of atime */ + le16_t clusthi; /* FAT32: high 16 bits of cluster */ + le32_t mtime; /* Modification time */ + le16_t clustlo; /* First cluster pointer */ + le32_t size; /* File size (bytes) */ +}; + +/* A VFAT filesystem continuation entry */ +struct fat_vfat_slot +{ + le8_t id; /* Sequence number for slot */ + le16_t name0[5]; /* 5 characters */ + le8_t attribute; /* Attribute byte */ + le8_t reserved; /* Reserved, MBZ */ + le8_t alias_csum; /* Short name checksum */ + le16_t name5[6]; /* 6 characters */ + le16_t firstclust; /* MBZ */ + le16_t name11[2]; /* 2 characters */ +}; + +#endif /* FAT_H */ diff --git a/libfat/fatchain.c b/libfat/fatchain.c new file mode 100644 index 0000000..e00f46a --- /dev/null +++ b/libfat/fatchain.c @@ -0,0 +1,132 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * fatchain.c + * + * Follow a FAT chain + */ + +#include "libfatint.h" +#include "ulint.h" + +/* + * Convert a cluster number (or 0 for the root directory) to a + * sector number. Return -1 on failure. + */ +libfat_sector_t libfat_clustertosector(const struct libfat_filesystem *fs, + int32_t cluster) +{ + if ( cluster == 0 ) + cluster = fs->rootcluster; + + if ( cluster == 0 ) + return fs->rootdir; + else if ( cluster < 2 || cluster >= fs->endcluster ) + return -1; + else + return fs->data + ((libfat_sector_t)(cluster-2) << fs->clustshift); +} + +/* + * Get the next sector of either the root directory or a FAT chain. + * Returns 0 on end of file and -1 on error. + */ + +libfat_sector_t libfat_nextsector(struct libfat_filesystem *fs, + libfat_sector_t s) +{ + int32_t cluster, nextcluster; + uint32_t fatoffset; + libfat_sector_t fatsect; + uint8_t *fsdata; + uint32_t clustmask = fs->clustsize - 1; + libfat_sector_t rs; + + if ( s < fs->data ) { + if ( s < fs->rootdir ) + return -1; + + /* Root directory */ + s++; + return ( s < fs->data ) ? s : 0; + } + + rs = s - fs->data; + + if ( ~rs & clustmask ) + return s+1; /* Next sector in cluster */ + + cluster = 2 + (rs >> fs->clustshift); + + if ( cluster >= fs->endcluster ) + return -1; + + switch ( fs->fat_type ) { + case FAT12: + /* Get first byte */ + fatoffset = cluster + (cluster >> 1); + fatsect = fs->fat + (fatoffset >> LIBFAT_SECTOR_SHIFT); + fsdata = libfat_get_sector(fs, fatsect); + if ( !fsdata ) + return -1; + nextcluster = fsdata[fatoffset & LIBFAT_SECTOR_MASK]; + + /* Get second byte */ + fatoffset++; + fatsect = fs->fat + (fatoffset >> LIBFAT_SECTOR_SHIFT); + fsdata = libfat_get_sector(fs, fatsect); + if ( !fsdata ) + return -1; + nextcluster |= fsdata[fatoffset & LIBFAT_SECTOR_MASK] << 8; + + /* Extract the FAT entry */ + if ( cluster & 1 ) + nextcluster >>= 4; + else + nextcluster &= 0x0FFF; + + if ( nextcluster >= 0x0FF8 ) + return 0; + break; + + case FAT16: + fatoffset = cluster << 1; + fatsect = fs->fat + (fatoffset >> LIBFAT_SECTOR_SHIFT); + fsdata = libfat_get_sector(fs, fatsect); + if ( !fsdata ) + return -1; + nextcluster = read16((le16_t *)&fsdata[fatoffset & LIBFAT_SECTOR_MASK]); + + if ( nextcluster >= 0x0FFF8 ) + return 0; + break; + + case FAT28: + fatoffset = cluster << 2; + fatsect = fs->fat + (fatoffset >> LIBFAT_SECTOR_SHIFT); + fsdata = libfat_get_sector(fs, fatsect); + if ( !fsdata ) + return -1; + nextcluster = read32((le32_t *)&fsdata[fatoffset & LIBFAT_SECTOR_MASK]); + nextcluster &= 0x0FFFFFFF; + + if ( nextcluster >= 0x0FFFFFF8 ) + return 0; + break; + + default: + return -1; /* WTF? */ + } + + return libfat_clustertosector(fs, nextcluster); +} diff --git a/libfat/libfat.h b/libfat/libfat.h new file mode 100644 index 0000000..4105314 --- /dev/null +++ b/libfat/libfat.h @@ -0,0 +1,85 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * libfat.h + * + * Headers for the libfat library + */ + +#ifndef LIBFAT_H +#define LIBFAT_H + +#include +#include + +#define LIBFAT_SECTOR_SHIFT 9 +#define LIBFAT_SECTOR_SIZE 512 +#define LIBFAT_SECTOR_MASK 511 + +typedef uint32_t libfat_sector_t; +struct libfat_filesystem; + +struct libfat_direntry { + libfat_sector_t sector; + int offset; + unsigned char entry[32]; +}; + +/* + * Open the filesystem. The readfunc is the function to read + * sectors, in the format: + * int readfunc(intptr_t readptr, void *buf, size_t secsize, + * libfat_sector_t secno) + * + * ... where readptr is a private argument. + * + * A return value of != secsize is treated as error. + */ +struct libfat_filesystem * +libfat_open(int (*readfunc)(intptr_t, void *, size_t, libfat_sector_t), + intptr_t readptr); + +void libfat_close(struct libfat_filesystem *); + +/* + * Convert a cluster number (or 0 for the root directory) to a + * sector number. Return -1 on failure. + */ +libfat_sector_t libfat_clustertosector(const struct libfat_filesystem *fs, + int32_t cluster); + +/* + * Get the next sector of either the root directory or a FAT chain. + * Returns 0 on end of file and -1 on error. + */ +libfat_sector_t libfat_nextsector(struct libfat_filesystem *fs, + libfat_sector_t s); + +/* + * Flush all cached sectors for this filesystem. + */ +void libfat_flush(struct libfat_filesystem *fs); + +/* + * Get a pointer to a specific sector. + */ +void * libfat_get_sector(struct libfat_filesystem *fs, libfat_sector_t n); + +/* + * Search a FAT directory for a particular pre-mangled filename. + * Copies the directory entry into direntry and returns 0 if found. + */ +int32_t libfat_searchdir(struct libfat_filesystem *fs, int32_t dirclust, + const void *name, struct libfat_direntry *direntry); + +#endif /* LIBFAT_H */ diff --git a/libfat/libfatint.h b/libfat/libfatint.h new file mode 100644 index 0000000..78d9951 --- /dev/null +++ b/libfat/libfatint.h @@ -0,0 +1,55 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * libfatint.h + * + * Internals for the libfat filesystem + */ + +#ifndef LIBFATINT_H +#define LIBFATINT_H + +#include "libfat.h" +#include "fat.h" + +struct libfat_sector { + libfat_sector_t n; /* Sector number */ + struct libfat_sector *next; /* Next in list */ + char data[LIBFAT_SECTOR_SIZE]; +}; + +enum fat_type { + FAT12, + FAT16, + FAT28 +}; + +struct libfat_filesystem { + int (*read)(intptr_t, void *, size_t, libfat_sector_t); + intptr_t readptr; + + enum fat_type fat_type; + unsigned int clustsize; + int clustshift; + int32_t endcluster; /* Highest legal cluster number + 1 */ + int32_t rootcluster; /* Root directory cluster */ + + libfat_sector_t fat; /* Start of FAT */ + libfat_sector_t rootdir; /* Start of root directory */ + libfat_sector_t data; /* Start of data area */ + libfat_sector_t end; /* End of filesystem */ + + struct libfat_sector *sectors; +}; + +#endif /* LIBFATINT_H */ diff --git a/libfat/open.c b/libfat/open.c new file mode 100644 index 0000000..2602b1a --- /dev/null +++ b/libfat/open.c @@ -0,0 +1,117 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * open.c + * + * Open a FAT filesystem and compute some initial values; return NULL + * on failure. + */ + +#include +#include "libfatint.h" +#include "ulint.h" + +struct libfat_filesystem * +libfat_open(int (*readfunc)(intptr_t, void *, size_t, libfat_sector_t), + intptr_t readptr) +{ + struct libfat_filesystem *fs = NULL; + struct fat_bootsect *bs; + int i; + uint32_t sectors, fatsize, minfatsize, rootdirsize; + uint32_t nclusters; + + fs = malloc(sizeof(struct libfat_filesystem)); + if ( !fs ) + goto barf; + + fs->sectors = NULL; + fs->read = readfunc; + fs->readptr = readptr; + + bs = libfat_get_sector(fs, 0); + if ( !bs ) + goto barf; + + if ( read16(&bs->bsBytesPerSec) != LIBFAT_SECTOR_SIZE ) + goto barf; + + for ( i = 0 ; i <= 8 ; i++ ) { + if ( (uint8_t)(1 << i) == read8(&bs->bsSecPerClust) ) + break; + } + if ( i > 8 ) + goto barf; + fs->clustsize = 1 << i; /* Treat 0 as 2^8 = 64K */ + fs->clustshift = i; + + sectors = read16(&bs->bsSectors); + if ( !sectors ) + sectors = read32(&bs->bsHugeSectors); + + fs->end = sectors; + + fs->fat = read16(&bs->bsResSectors); + fatsize = read16(&bs->bsFATsecs); + if ( !fatsize ) + fatsize = read32(&bs->u.fat32.bpb_fatsz32); + + fs->rootdir = fs->fat + fatsize * read8(&bs->bsFATs); + + rootdirsize = ((read16(&bs->bsRootDirEnts) << 5) + LIBFAT_SECTOR_MASK) + >> LIBFAT_SECTOR_SHIFT; + fs->data = fs->rootdir + rootdirsize; + + /* Sanity checking */ + if ( fs->data >= fs->end ) + goto barf; + + /* Figure out how many clusters */ + nclusters = (fs->end - fs->data) >> fs->clustshift; + fs->endcluster = nclusters + 2; + + if ( nclusters <= 0xff4 ) { + fs->fat_type = FAT12; + minfatsize = fs->endcluster + (fs->endcluster >> 1); + } else if ( nclusters <= 0xfff4 ) { + fs->fat_type = FAT16; + minfatsize = fs->endcluster << 1; + } else if ( nclusters <= 0xffffff4 ) { + fs->fat_type = FAT28; + minfatsize = fs->endcluster << 2; + } else + goto barf; /* Impossibly many clusters */ + + minfatsize = (minfatsize + LIBFAT_SECTOR_SIZE-1) >> LIBFAT_SECTOR_SHIFT; + + if ( minfatsize > fatsize ) + goto barf; /* The FATs don't fit */ + + if ( fs->fat_type == FAT28 ) + fs->rootcluster = read32(&bs->u.fat32.bpb_rootclus); + else + fs->rootcluster = 0; + + return fs; /* All good */ + + barf: + if ( fs ) + free(fs); + return NULL; +} + +void libfat_close(struct libfat_filesystem *fs) +{ + libfat_flush(fs); + free(fs); +} diff --git a/libfat/searchdir.c b/libfat/searchdir.c new file mode 100644 index 0000000..6fcde13 --- /dev/null +++ b/libfat/searchdir.c @@ -0,0 +1,63 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * searchdir.c + * + * Search a FAT directory for a particular pre-mangled filename. + * Copies the directory entry into direntry and returns the starting cluster + * if found; returns -2 on not found, -1 on error, 0 on empty file. + */ + +#include +#include "libfatint.h" + +int32_t libfat_searchdir(struct libfat_filesystem *fs, int32_t dirclust, + const void *name, struct libfat_direntry *direntry) +{ + struct fat_dirent *dep; + int nent; + libfat_sector_t s = libfat_clustertosector(fs, dirclust); + + while ( 1 ) { + if ( s == 0 ) + return -2; /* Not found */ + else if ( s == (libfat_sector_t)-1 ) + return -1; /* Error */ + + dep = libfat_get_sector(fs, s); + if ( !dep ) + return -1; /* Read error */ + + for ( nent = 0 ; nent < LIBFAT_SECTOR_SIZE ; + nent += sizeof(struct fat_dirent) ) { + if ( !memcmp(dep->name, name, 11) ) { + if ( direntry ) { + memcpy(direntry->entry, dep, sizeof (*dep)); + direntry->sector = s; + direntry->offset = nent; + } + if ( read32(&dep->size) == 0 ) + return 0; /* An empty file has no clusters */ + else + return read16(&dep->clustlo) + (read16(&dep->clusthi) << 16); + } + + if ( dep->name[0] == 0 ) + return -2; /* Hit high water mark */ + + dep++; + } + + s = libfat_nextsector(fs, s); + } +} diff --git a/libfat/ulint.h b/libfat/ulint.h new file mode 100644 index 0000000..7871262 --- /dev/null +++ b/libfat/ulint.h @@ -0,0 +1,122 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2001-2008 H. Peter Anvin - 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, Inc., 675 Mass Ave, Cambridge MA 02139, + * USA; either version 2 of the License, or (at your option) any later + * version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * ulint.h + * + * Basic operations on unaligned, littleendian integers + */ + +#ifndef ULINT_H +#define ULINT_H + +#include + +/* These are unaligned, littleendian integer types */ + +typedef uint8_t le8_t; /* 8-bit byte */ +typedef uint8_t le16_t[2]; /* 16-bit word */ +typedef uint8_t le32_t[4]; /* 32-bit dword */ + +/* Read/write these quantities */ + +static inline unsigned char +read8(le8_t *_p) +{ + return *_p; +} + +static inline void +write8(le8_t *_p, uint8_t _v) +{ + *_p = _v; +} + +#if defined(__i386__) || defined(__x86_64__) + +/* Littleendian architectures which support unaligned memory accesses */ + +static inline unsigned short +read16(le16_t *_p) +{ + return *((const uint16_t *)_p); +} + +static inline void +write16(le16_t *_p, unsigned short _v) +{ + *((uint16_t *)_p) = _v; +} + +static inline unsigned int +read32(le32_t *_p) +{ + return *((const uint32_t *)_p); +} + +static inline void +write32(le32_t *_p, uint32_t _v) +{ + *((uint32_t *)_p) = _v; +} + +#else + +/* Generic, mostly portable versions */ + +static inline unsigned short +read16(le16_t *_pp) +{ + uint8_t *_p = *_pp; + uint16_t _v; + + _v = _p[0]; + _v |= _p[1] << 8; + return _v; +} + +static inline void +write16(le16_t *_pp, uint16_t _v) +{ + uint8_t *_p = *_pp; + + _p[0] = _v & 0xFF; + _p[1] = (_v >> 8) & 0xFF; +} + +static inline unsigned int +read32(le32_t *_pp) +{ + uint8_t *_p = *_pp; + uint32_t _v; + + _v = _p[0]; + _v |= _p[1] << 8; + _v |= _p[2] << 16; + _v |= _p[3] << 24; + return _v; +} + +static inline void +write32(le32_t *_pp, uint32_t _v) +{ + uint8_t *_p = *_pp; + + _p[0] = _v & 0xFF; + _p[1] = (_v >> 8) & 0xFF; + _p[2] = (_v >> 16) & 0xFF; + _p[3] = (_v >> 24) & 0xFF; +} + +#endif + +#endif /* ULINT_H */ diff --git a/libinstaller/Makefile b/libinstaller/Makefile new file mode 100644 index 0000000..ef3711d --- /dev/null +++ b/libinstaller/Makefile @@ -0,0 +1,35 @@ +# _bin.c files required by both BTARGET and ITARGET installers +BINFILES = bootsect_bin.c ldlinux_bin.c \ + extlinux_bss_bin.c extlinux_sys_bin.c \ + mbr_bin.c gptmbr_bin.c + +PERL = perl + +all: $(BINFILES) + +bootsect_bin.c: ../core/ldlinux.bss bin2c.pl + $(PERL) bin2c.pl syslinux_bootsect < $< > $@ + +ldlinux_bin.c: ../core/ldlinux.sys bin2c.pl + $(PERL) bin2c.pl syslinux_ldlinux < $< > $@ + +extlinux_bss_bin.c: ../core/extlinux.bss bin2c.pl + $(PERL) bin2c.pl extlinux_bootsect < $< > $@ + +extlinux_sys_bin.c: ../core/extlinux.sys bin2c.pl + $(PERL) bin2c.pl extlinux_image 512 < $< > $@ + +mbr_bin.c: ../mbr/mbr.bin bin2c.pl + $(PERL) bin2c.pl syslinux_mbr < $< > $@ + +gptmbr_bin.c: ../mbr/gptmbr.bin bin2c.pl + $(PERL) bin2c.pl syslinux_gptmbr < $< > $@ + +tidy: + rm -f $(BINFILES) + +clean: tidy + +dist: tidy + +spotless: clean diff --git a/libinstaller/advconst.h b/libinstaller/advconst.h new file mode 120000 index 0000000..044572b --- /dev/null +++ b/libinstaller/advconst.h @@ -0,0 +1 @@ +../com32/include/syslinux/advconst.h \ No newline at end of file diff --git a/libinstaller/bin2c.pl b/libinstaller/bin2c.pl new file mode 100755 index 0000000..5a60ca9 --- /dev/null +++ b/libinstaller/bin2c.pl @@ -0,0 +1,78 @@ +#!/usr/bin/perl +## ----------------------------------------------------------------------- +## +## Copyright 1998-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +# +# bin2c.pl: binary file to C source converter +# + +eval { use bytes; }; +eval { binmode STDIN; }; + +($table_name, $pad) = @ARGV; + +if ( !defined($table_name) ) { + print STDERR "Usage: $0 table_name [pad] < input_file > output_file\n"; + exit 1; +} + +$pad = 1 if ($pad < 1); + +printf "unsigned char %s[] = {\n", $table_name; + +$pos = 0; +$linelen = 8; + +$total_len = 0; + +while ( ($n = read(STDIN, $data, 4096)) > 0 ) { + $total_len += $n; + for ( $i = 0 ; $i < $n ; $i++ ) { + $byte = substr($data, $i, 1); + if ( $pos >= $linelen ) { + print ",\n\t"; + $pos = 0; + } elsif ( $pos > 0 ) { + print ", "; + } else { + print "\t"; + } + printf("0x%02x", unpack("C", $byte)); + $pos++; + } +} + +$align = $total_len % $pad; +if ($align != 0) { + $n = $pad - $align; + $total_len += $n; + for ( $i = 0 ; $i < $n ; $i++ ) { + if ( $pos >= $linelen ) { + print ",\n\t"; + $pos = 0; + } elsif ( $pos > 0 ) { + print ", "; + } else { + print "\t"; + } + print '0x00'; + $pos++; + } +} + +printf "\n};\n\nunsigned int %s_len = %u;\n", $table_name, $total_len; + +@st = stat STDIN; + +printf "\nint %s_mtime = %d;\n", $table_name, $st[9]; + +exit 0; diff --git a/libinstaller/setadv.c b/libinstaller/setadv.c new file mode 100644 index 0000000..c768d1b --- /dev/null +++ b/libinstaller/setadv.c @@ -0,0 +1,159 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * setadv.c + * + * (Over)write a data item in the auxilliary data vector. To + * delete an item, set its length to zero. + * + * Return 0 on success, -1 on error, and set errno. + * + */ + +#include +#include +#include "syslxint.h" + +unsigned char syslinux_adv[2*ADV_SIZE]; + +#define ADV_MAGIC1 0x5a2d2fa5 /* Head signature */ +#define ADV_MAGIC2 0xa3041767 /* Total checksum */ +#define ADV_MAGIC3 0xdd28bf64 /* Tail signature */ + +static void cleanup_adv(unsigned char *advbuf) +{ + int i; + uint32_t csum; + + /* Make sure both copies agree, and update the checksum */ + set_32(advbuf, ADV_MAGIC1); + + csum = ADV_MAGIC2; + for (i = 8; i < ADV_SIZE-4; i += 4) + csum -= get_32(advbuf+i); + + set_32(advbuf+4, csum); + set_32(advbuf+ADV_SIZE-4, ADV_MAGIC3); + + memcpy(advbuf+ADV_SIZE, advbuf, ADV_SIZE); +} + +int syslinux_setadv(int tag, size_t size, const void *data) +{ + uint8_t *p; + size_t left; + uint8_t advtmp[ADV_SIZE]; + + if ((unsigned)tag-1 > 254) { + errno = EINVAL; + return -1; /* Impossible tag value */ + } + + if (size > 255) { + errno = ENOSPC; /* Max 255 bytes for a data item */ + return -1; + } + + left = ADV_LEN; + p = advtmp; + memcpy(p, syslinux_adv+2*4, left); /* Make working copy */ + + while (left >= 2) { + uint8_t ptag = p[0]; + size_t plen = p[1]+2; + + if (ptag == ADV_END) + break; + + if (ptag == tag) { + /* Found our tag. Delete it. */ + + if (plen >= left) { + /* Entire remainder is our tag */ + break; + } + memmove(p, p+plen, left-plen); + } else { + /* Not our tag */ + if (plen > left) + break; /* Corrupt tag (overrun) - overwrite it */ + + left -= plen; + p += plen; + } + } + + /* Now (p, left) reflects the position to write in and how much space + we have for our data. */ + + if (size) { + if (left < size+2) { + errno = ENOSPC; /* Not enough space for data */ + return -1; + } + + *p++ = tag; + *p++ = size; + memcpy(p, data, size); + p += size; + left -= size+2; + } + + memset(p, 0, left); + + /* If we got here, everything went OK, commit the write */ + memcpy(syslinux_adv+2*4, advtmp, ADV_LEN); + cleanup_adv(syslinux_adv); + + return 0; +} + +void syslinux_reset_adv(unsigned char *advbuf) +{ + /* Create an all-zero ADV */ + memset(advbuf+2*4, 0, ADV_LEN); + cleanup_adv(advbuf); +} + +static int adv_consistent(const unsigned char *p) +{ + int i; + uint32_t csum; + + if (get_32(p) != ADV_MAGIC1 || get_32(p+ADV_SIZE-4) != ADV_MAGIC3) + return 0; + + csum = 0; + for (i = 4; i < ADV_SIZE-4; i += 4) + csum += get_32(p+i); + + return csum == ADV_MAGIC2; +} + +/* + * Verify that an in-memory ADV is consistent, making the copies consistent. + * If neither copy is OK, return -1 and call syslinux_reset_adv(). + */ +int syslinux_validate_adv(unsigned char *advbuf) +{ + if (adv_consistent(advbuf+0*ADV_SIZE)) { + memcpy(advbuf+ADV_SIZE, advbuf, ADV_SIZE); + return 0; + } else if (adv_consistent(advbuf+1*ADV_SIZE)) { + memcpy(advbuf, advbuf+ADV_SIZE, ADV_SIZE); + return 0; + } else { + syslinux_reset_adv(advbuf); + return -1; + } +} diff --git a/libinstaller/syslinux.h b/libinstaller/syslinux.h new file mode 100644 index 0000000..efffb7c --- /dev/null +++ b/libinstaller/syslinux.h @@ -0,0 +1,51 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1998-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef SYSLINUX_H +#define SYSLINUX_H + +#include +#include "advconst.h" + +/* The standard boot sector and ldlinux image */ +extern unsigned char syslinux_bootsect[]; +extern unsigned int syslinux_bootsect_len; +extern int syslinux_bootsect_mtime; + +extern unsigned char syslinux_ldlinux[]; +extern unsigned int syslinux_ldlinux_len; +extern int syslinux_ldlinux_mtime; + +extern unsigned char syslinux_mbr[]; +extern unsigned int syslinux_mbr_len; +extern int syslinux_mbr_mtime; + +/* This takes a boot sector and merges in the syslinux fields */ +void syslinux_make_bootsect(void *); + +/* Check to see that what we got was indeed an MS-DOS boot sector/superblock */ +const char *syslinux_check_bootsect(const void *bs); + +/* This patches the boot sector and ldlinux.sys based on a sector map */ +int syslinux_patch(const uint32_t *sectors, int nsectors, + int stupid, int raid_mode); + +/* ADV information */ +#define ADV_SIZE 512 /* Total size */ +#define ADV_LEN (ADV_SIZE-3*4) /* Usable data size */ +extern unsigned char syslinux_adv[2*ADV_SIZE]; + +int syslinux_setadv(int tag, size_t size, const void *data); +void syslinux_reset_adv(unsigned char *advbuf); +int syslinux_validate_adv(unsigned char *advbuf); + +#endif diff --git a/libinstaller/syslxint.h b/libinstaller/syslxint.h new file mode 100644 index 0000000..120026d --- /dev/null +++ b/libinstaller/syslxint.h @@ -0,0 +1,74 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef SYSLXINT_H +#define SYSLXINT_H + +#include "syslinux.h" + +/* + * Access functions for littleendian numbers, possibly misaligned. + */ +static inline uint8_t get_8(const unsigned char *p) +{ + return *(const uint8_t *)p; +} + +static inline uint16_t get_16(const unsigned char *p) +{ +#if defined(__i386__) || defined(__x86_64__) + /* Littleendian and unaligned-capable */ + return *(const uint16_t *)p; +#else + return (uint16_t)p[0] + ((uint16_t)p[1] << 8); +#endif +} + +static inline uint32_t get_32(const unsigned char *p) +{ +#if defined(__i386__) || defined(__x86_64__) + /* Littleendian and unaligned-capable */ + return *(const uint32_t *)p; +#else + return (uint32_t)p[0] + ((uint32_t)p[1] << 8) + + ((uint32_t)p[2] << 16) + ((uint32_t)p[3] << 24); +#endif +} + +static inline void set_16(unsigned char *p, uint16_t v) +{ +#if defined(__i386__) || defined(__x86_64__) + /* Littleendian and unaligned-capable */ + *(uint16_t *)p = v; +#else + p[0] = (v & 0xff); + p[1] = ((v >> 8) & 0xff); +#endif +} + +static inline void set_32(unsigned char *p, uint32_t v) +{ +#if defined(__i386__) || defined(__x86_64__) + /* Littleendian and unaligned-capable */ + *(uint32_t *)p = v; +#else + p[0] = (v & 0xff); + p[1] = ((v >> 8) & 0xff); + p[2] = ((v >> 16) & 0xff); + p[3] = ((v >> 24) & 0xff); +#endif +} + +#define SECTOR_SHIFT 9 /* 512-byte sectors */ +#define SECTOR_SIZE (1 << SECTOR_SHIFT) + +#endif /* SYSLXINT_H */ diff --git a/libinstaller/syslxmod.c b/libinstaller/syslxmod.c new file mode 100644 index 0000000..fb2f1d7 --- /dev/null +++ b/libinstaller/syslxmod.c @@ -0,0 +1,287 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1998-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * syslxmod.c - Code to provide a SYSLINUX code set to an installer. + */ + +#define _XOPEN_SOURCE 500 /* Required on glibc 2.x */ +#define _BSD_SOURCE +#include +#include +#include +#include + +#include "syslinux.h" + +#define LDLINUX_MAGIC 0x3eb202fe + +enum bs_offsets { + bsJump = 0x00, + bsOemName = 0x03, + bsBytesPerSec = 0x0b, + bsSecPerClust = 0x0d, + bsResSectors = 0x0e, + bsFATs = 0x10, + bsRootDirEnts = 0x11, + bsSectors = 0x13, + bsMedia = 0x15, + bsFATsecs = 0x16, + bsSecPerTrack = 0x18, + bsHeads = 0x1a, + bsHiddenSecs = 0x1c, + bsHugeSectors = 0x20, + + /* FAT12/16 only */ + bs16DriveNumber = 0x24, + bs16Reserved1 = 0x25, + bs16BootSignature = 0x26, + bs16VolumeID = 0x27, + bs16VolumeLabel = 0x2b, + bs16FileSysType = 0x36, + bs16Code = 0x3e, + + /* FAT32 only */ + bs32FATSz32 = 36, + bs32ExtFlags = 40, + bs32FSVer = 42, + bs32RootClus = 44, + bs32FSInfo = 48, + bs32BkBootSec = 50, + bs32Reserved = 52, + bs32DriveNumber = 64, + bs32Reserved1 = 65, + bs32BootSignature = 66, + bs32VolumeID = 67, + bs32VolumeLabel = 71, + bs32FileSysType = 82, + bs32Code = 90, + + bsSignature = 0x1fe +}; + +#define bsHead bsJump +#define bsHeadLen (bsOemName-bsHead) +#define bsCode bs32Code /* The common safe choice */ +#define bsCodeLen (bsSignature-bs32Code) + +/* + * Access functions for littleendian numbers, possibly misaligned. + */ +static inline uint8_t get_8(const unsigned char *p) +{ + return *(const uint8_t *)p; +} + +static inline uint16_t get_16(const unsigned char *p) +{ +#if defined(__i386__) || defined(__x86_64__) + /* Littleendian and unaligned-capable */ + return *(const uint16_t *)p; +#else + return (uint16_t)p[0] + ((uint16_t)p[1] << 8); +#endif +} + +static inline uint32_t get_32(const unsigned char *p) +{ +#if defined(__i386__) || defined(__x86_64__) + /* Littleendian and unaligned-capable */ + return *(const uint32_t *)p; +#else + return (uint32_t)p[0] + ((uint32_t)p[1] << 8) + + ((uint32_t)p[2] << 16) + ((uint32_t)p[3] << 24); +#endif +} + +static inline void set_16(unsigned char *p, uint16_t v) +{ +#if defined(__i386__) || defined(__x86_64__) + /* Littleendian and unaligned-capable */ + *(uint16_t *)p = v; +#else + p[0] = (v & 0xff); + p[1] = ((v >> 8) & 0xff); +#endif +} + +static inline void set_32(unsigned char *p, uint32_t v) +{ +#if defined(__i386__) || defined(__x86_64__) + /* Littleendian and unaligned-capable */ + *(uint32_t *)p = v; +#else + p[0] = (v & 0xff); + p[1] = ((v >> 8) & 0xff); + p[2] = ((v >> 16) & 0xff); + p[3] = ((v >> 24) & 0xff); +#endif +} + +void syslinux_make_bootsect(void *bs) +{ + unsigned char *bootsect = bs; + + memcpy(bootsect+bsHead, syslinux_bootsect+bsHead, bsHeadLen); + memcpy(bootsect+bsCode, syslinux_bootsect+bsCode, bsCodeLen); +} + +/* + * Check to see that what we got was indeed an MS-DOS boot sector/superblock; + * Return NULL if OK and otherwise an error message; + */ +const char *syslinux_check_bootsect(const void *bs) +{ + int veryold; + int sectorsize; + long long sectors, fatsectors, dsectors; + long long clusters; + int rootdirents, clustersize; + const unsigned char *sectbuf = bs; + + veryold = 0; + + /* Must be 0xF0 or 0xF8..0xFF */ + if ( get_8(sectbuf+bsMedia) != 0xF0 && + get_8(sectbuf+bsMedia) < 0xF8 ) + goto invalid; + + sectorsize = get_16(sectbuf+bsBytesPerSec); + if ( sectorsize == 512 ) + ; /* ok */ + else if ( sectorsize == 1024 || sectorsize == 2048 || sectorsize == 4096 ) + return "only 512-byte sectors are supported"; + else + goto invalid; + + clustersize = get_8(sectbuf+bsSecPerClust); + if ( clustersize == 0 || (clustersize & (clustersize-1)) ) + goto invalid; /* Must be nonzero and a power of 2 */ + + sectors = get_16(sectbuf+bsSectors); + sectors = sectors ? sectors : get_32(sectbuf+bsHugeSectors); + + dsectors = sectors - get_16(sectbuf+bsResSectors); + + fatsectors = get_16(sectbuf+bsFATsecs); + fatsectors = fatsectors ? fatsectors : get_32(sectbuf+bs32FATSz32); + fatsectors *= get_8(sectbuf+bsFATs); + dsectors -= fatsectors; + + rootdirents = get_16(sectbuf+bsRootDirEnts); + dsectors -= (rootdirents+sectorsize/32-1)/sectorsize; + + if ( dsectors < 0 || fatsectors == 0 ) + goto invalid; + + clusters = dsectors/clustersize; + + if ( clusters < 0xFFF5 ) { + /* FAT12 or FAT16 */ + + if ( !get_16(sectbuf+bsFATsecs) ) + goto invalid; + + if ( get_8(sectbuf+bs16BootSignature) == 0x29 ) { + if ( !memcmp(sectbuf+bs16FileSysType, "FAT12 ", 8) ) { + if ( clusters >= 0xFF5 ) + return "more than 4084 clusters but claims FAT12"; + } else if ( !memcmp(sectbuf+bs16FileSysType, "FAT16 ", 8) ) { + if ( clusters < 0xFF5 ) + return "less than 4084 clusters but claims FAT16"; + } else if ( memcmp(sectbuf+bs16FileSysType, "FAT ", 8) ) { + static char fserr[] = "filesystem type \"????????\" not supported"; + memcpy(fserr+17, sectbuf+bs16FileSysType, 8); + return fserr; + } + } + } else if ( clusters < 0x0FFFFFF5 ) { + /* FAT32 */ + /* Moving the FileSysType and BootSignature was a lovely stroke of M$ idiocy */ + if ( get_8(sectbuf+bs32BootSignature) != 0x29 || + memcmp(sectbuf+bs32FileSysType, "FAT32 ", 8) ) + goto invalid; + } else { + goto invalid; + } + + return NULL; + + invalid: + return "this doesn't look like a valid FAT filesystem"; +} + +/* + * This patches the boot sector and the first sector of ldlinux.sys + * based on an ldlinux.sys sector map passed in. Typically this is + * handled by writing ldlinux.sys, mapping it, and then overwrite it + * with the patched version. If this isn't safe to do because of + * an OS which does block reallocation, then overwrite it with + * direct access since the location is known. + * + * Return 0 if successful, otherwise -1. + */ +int syslinux_patch(const uint32_t *sectors, int nsectors, + int stupid, int raid_mode) +{ + unsigned char *patcharea, *p; + int nsect = (syslinux_ldlinux_len+511) >> 9; + uint32_t csum; + int i, dw; + + if ( nsectors < nsect ) + return -1; + + /* Patch in options, as appropriate */ + if (stupid) { + /* Access only one sector at a time */ + set_16(syslinux_bootsect+0x1FC, 1); + } + + i = get_16(syslinux_bootsect+0x1FE); + if (raid_mode) + set_16(syslinux_bootsect+i, 0x18CD); /* INT 18h */ + set_16(syslinux_bootsect+0x1FE, 0xAA55); + + /* First sector need pointer in boot sector */ + set_32(syslinux_bootsect+0x1F8, *sectors++); + nsect--; + + /* Search for LDLINUX_MAGIC to find the patch area */ + for ( p = syslinux_ldlinux ; get_32(p) != LDLINUX_MAGIC ; p += 4 ); + patcharea = p+8; + + /* Set up the totals */ + dw = syslinux_ldlinux_len >> 2; /* COMPLETE dwords! */ + set_16(patcharea, dw); + set_16(patcharea+2, nsect); /* Does not include the first sector! */ + + /* Set the sector pointers */ + p = patcharea+8; + + memset(p, 0, 64*4); + while ( nsect-- ) { + set_32(p, *sectors++); + p += 4; + } + + /* Now produce a checksum */ + set_32(patcharea+4, 0); + + csum = LDLINUX_MAGIC; + for ( i = 0, p = syslinux_ldlinux ; i < dw ; i++, p += 4 ) + csum -= get_32(p); /* Negative checksum */ + + set_32(patcharea+4, csum); + + return 0; +} diff --git a/linux/Makefile b/linux/Makefile new file mode 100644 index 0000000..a469cef --- /dev/null +++ b/linux/Makefile @@ -0,0 +1,61 @@ +## ----------------------------------------------------------------------- +## +## Copyright 2001-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +## +## Linux FAT installer +## + +topdir = .. +include $(topdir)/MCONFIG + +OPTFLAGS = -g -Os +INCLUDES = -I. -I.. -I../libinstaller +CFLAGS = -W -Wall -D_FILE_OFFSET_BITS=64 $(OPTFLAGS) $(INCLUDES) +LDFLAGS = -s + +SRCS = syslinux.c \ + ../libinstaller/syslxmod.c \ + ../libinstaller/bootsect_bin.c \ + ../libinstaller/ldlinux_bin.c +OBJS = $(patsubst %.c,%.o,$(notdir $(SRCS))) + +.SUFFIXES: .c .o .i .s .S + +VPATH = .:../libinstaller + +all: installer + +tidy dist: + -rm -f *.o *.i *.s *.a .*.d *.tmp + +clean: tidy + -rm -f syslinux syslinux-nomtools + +spotless: clean + -rm -f *~ + +installer: syslinux syslinux-nomtools + +syslinux: $(OBJS) + $(CC) $(LDFLAGS) -o $@ $^ + +syslinux-nomtools: syslinux + ln -f $< $@ + +%.o: %.c + $(CC) $(UMAKEDEPS) $(CFLAGS) -c -o $@ $< +%.i: %.c + $(CC) $(UMAKEDEPS) $(CFLAGS) -E -o $@ $< +%.s: %.c + $(CC) $(UMAKEDEPS) $(CFLAGS) -S -o $@ $< + +-include .*.d *.tmp diff --git a/linux/syslinux b/linux/syslinux new file mode 100755 index 0000000000000000000000000000000000000000..4acc9558d0cfe5db134566c57135e774e3c2e1be GIT binary patch literal 25128 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 literal 0 HcmV?d00001 diff --git a/linux/syslinux-nomtools b/linux/syslinux-nomtools new file mode 100755 index 0000000000000000000000000000000000000000..4acc9558d0cfe5db134566c57135e774e3c2e1be GIT binary patch literal 25128 zcmeIb3sh6b`ZqiYAp`^x6%lV3El_AlBvz~!px!`jz2FT{D;I@r@B*H&BYLV8jMQub z)V8*#?Xeya+vC9-?Ewp-0gMH0#Zv5PZSApCt=%zH5s-^m^8IE9to1$r_qN`(-tSxM z3v2ewGxN;MGc(W3JoC(j9g*{*L?RLC|A~ncp->1XkxhVoew8C+0#Onf2_z%Q2qJ|S z4V#b!92g-2B00lP0^>x(08hw9ILu(c)X*@RHZ{VJ#)VOiB7_Y>{511NC7tMp3Iml!V@}_1MU~2lqqJL(SeMS=Evri(UCJbTJ z^oK7(IKcnr*Ps5l4XLZgZAeyaNKMmkQD>y9$2;PJG@)#>7c3LvQaWiIR~VYM7YwZ% z8vg2AuOC|EvN5PO_vFg=4}UlIui@%+ia!>H){jsZN-+fmzoReRsGi(v0@Z)~t$z$0 z1oyZn;Fq6(Uwi`o?g{wyCt%|f@T0bazY=EA6X8yeAy9hSp9sJH1RV4P9Pe8>=Q8M!~P#F|F(VrHia9WfFquOJ)S7f zgD1jcpMXDk0v--{-g^=m<+p)EPWHj}vGv2l2uVm-yD>d2A%jiQu?YzTth|P@@I;a@ zcOiU}Q_~Xk87avmWlPGM4J2ceE;Ws1$fgvXE?q~MblrNQPXptxCmYu%r@|*=YuXx; zkg_F}C2LdIO{vLblP)DGdEx}*gh*-W2^-SaB(bUKX+*b?##p1J-}Fr>X~5Z-vT@DE zO+>#jDPuiB?ubEB)7P-bnbooB`VAXsswsLTo0*gQ;o;P>K%!Kji)%`BbBUcEmZt-J( zv_Ff{yC3&^9CCUTLS6kKBDiTkccR0YU|^9!si_`xq<9KEK>Y^_uoF@B!3qHuljr+* zRAF-B4w6i%bIC&pB2%Q4PU8wAgz_CP|SsSRddN^K1Man#1(_cFDI z5Ryo3Z$gr(J(LhFwS5TLKy9$-CTfF`S!$0UWDB)1@@=Cw#{Dd6`x3H?+M@|EQd>z# z9<|31vW)wAiJ@7-(>h(5ZBaf=i0Q3|cDp^Nk#*(^;LCr8>fvdJj9YoZVh@!drrlqL zFO6gOcZW+$oz^Bqv-{H;)zUC)g84^a4k0Mi!N9;Waf0ZyiH35Bx+SWDq@3U3nN4HT}XaH0THj?kW` zaI^qZ&d@ecc$xrH4$)qraEJg?PSG||SSi4iW3)Vl6#~4A!Yvdg0&Jvk7lm7Xqvg+| zFrA03O@QJ5=%@KLp8Lsgvn6`zVr|1;fYpT0q8M#_zC^}nAhGs6fB5zBVpjM_48lY| z%``*_M|m*1t|}0pnl5pEoPF~58Kd)0(oFJah>9qN-7AL%JJ2V8uluFenWi8zoRh3v zWy#-6@=t@U1ztBa%L+q)n^VmWqIF2%aIKU0Q9pab17mTp3JUCxPbcF@2Fc5To{N*hI0xe?~e4Mc`tYZhI^fe|J0E{Yz(@d z(zM+@Ke8oyo!GAT;?JN8g=kydfT}G(sLej{5iFd75k&;Vj!Jz1nh`WIM)$<+h3Xq3Z|=tvv%j;lo!V02D7? zNAe@PqL=Vf9W+h8qL&)BcM+DB14TXuZ_}!W7lcf*o427NO{--2k?$gkAI3>Xq22kT zJ-{Q@o2X|*Tw)ImLqp`}_@G4E3pCR#ghcZxLM}=^yq{Eg-f;xTbMo*tMIQDi;Mj1I zh*Iagi`r)WkeWSo0_B#_5PY@Y{hpYQBQcxTVy)ZzkFpR8zCw$k$O;Dy(T?Pzwa)+% z&7f7$2qFC+{uszNob%Jte!!Oii0-eKryTW!dXZ_Wg_?0PoPSg$0xP(iBHc~f6{b~Q zCcO_A*<2S%yB)ECOWb3Y#M<@EsCs^ikdQK`nyb+ZOfnU@+eR6>?D|ps#s{?e3|$h| z7m=d*4*+u#KYl(6^PVWOS!5abP0*L3`7Jqi{Xf72X#D=B|6d?stx(FxNSOT?3Aeld zNW$=K-Ay|HI|_Cd>>;p~ zuswjTU5pm9-lEt7xrGAdmt8Fa7GmiItaOT14J<@i9Id6Jh;|a~Lk<#ZL5Ja-S06M6 z@|-8Msi7LS}!ZnJsZ-rac3Im-knIJm&*o zqxfe6QCG2A3k^L61`zVl4>ILUg1`UOR&@HSGy2Q;8BI`(1T|Ol%MQ~4_|b@>{RLsd zSQ5hz6LRs1jp464Qk#VOPj2|7r4Ls> zpgacyC?|9`nqwL59B68qgEkEKvHZRJw6=WKkyw6Whfs0gWXqPZb&)(86VnAB6RjPn zlT2H}*CL!7>Bp-b6^sO9`RhU)cP*WBwbN0XvHV>jMNFz(-4?ZJlmdC#URbn5d%lF` zH_(v@9SA)f?PHiC(MxTka;kR>MUs_Bh&Y&0)4@LlY}=wBOU_mO86m&bj{HCt2!y0h z9K^-)K?umtIfU}XI@-b>N^_3pC!>DB{;^ywuLlml3FO7Cpu@CK4g67r^DE(*(C3jZ zWTD8p+CM8Ehljn44m$QSI$SxjiHYXv(%mHFscDoSI_p}~j1WTv!es+ZG=BR}b( zAT#w%9z$rw9&dh|LWtnIvy;v)*!_F?Ct9zNIb-0}~;qXmm}rP4=u;4$WxV z)X0`?ZCs25Q}a3ZKg@_DP0M7tGlqnZnmm==37rfJ>`3dMcTaFMO3D%Z1^{ZStjWsV;u=;UGrw~UvcxNM5#xn(yy zf8uK7pGLNDmo<$}t8NC>4*M2ZL6?U8h>Qjrs^v9ZuJ2Lb9J|~&3Pf;MT+hpQ4Higk zes3`yx#j`DaA$ZxxH|3|`Rg61xorc&+cCe&cOL`57`C^fyw~7F;#q$V2$JX21Bm7> zufy~vT%9q!rocC3fxuTiZ_`Enpy|4O5-lWBJfv0Cr- z$MQz39b2}^95FJ}%1uD9ZCV7>QUSHDAC+AUI!rSr7JUdlGR>3}{R4LSUVLoRQH&kW zZ*u|pc6p{6RhPA8Mv&frv7ve~5;}u~qWM?*6ViLwT*_-{Hm+b3=knWdK9ZFxQb8z{ zQOv_nU>a|hn6}H9Ldv=f=W^gIY~yUPp>9RnhjCP_eze9B8Z+lBC++Y=1uK)$Dg7(1jkPUcjeTZ?4 zUkGxtEkmGm!MwmqC3 z5#s;pzlh)RKa5XXG25c~&+!BQyZK8B={^HyS$1$GbuoJkHi^9fLOLeicE_5#zpRh+ zO6Q@{;zxq6<5L^SluT0U2B=~Mg(!Sy%%2|J?ioxu1{mhxJ|nBHRJp@=*Kb1^X+Ea+Ur zL!N&=_)q%O6!yP~@MO3uov4rv&C-!$hBjTtD*KeHf}1LSNm@FqOzJC{GF_+CrL5I& zNYW`8l!dHGN_(1Bu1VjBWKxt#X?-=J%uHpqh_xEJ;`M!T$x>}_wlTGOv8L6u`Y*p&hGLo1SrQolG&U#}i0@BkmNXFI- z$8OEWG**pDQlo}4(l@50g9_xr>b9n)tyQw=%8acWsdihbOInkn)Mtz#o0%k*Y|Kbb z&mcmv`x{`~IJ6r58;7l+zMY`086;}@Qq6d!QVC>Tijvi$h!a94O;E1cpwD1abQ#Lk zdX#@dQtHMGrGP8Mm`G!6NXY;t|HBv)X$;^a1{}``nWd+p@S&RVs?}TB6lI3MX&K5S zP>`YDvs@){6v3VOhnscq7m*AiBSaoGkmGJ z2nj47Fq!Tt%Ms7;aE^RUvKt-92VP;+Abnx~t3MomG+rD`2*OsteiV16!I{RUaks#1 zLiqT{!)FPq|q3<_VNe7qTSsJ*uxIIgjFPS%Y!7|Dn|0jPB9%`x! z8cvtKB8@Gmu8@(c(pTC(EVxp+g~&^vmmi2RdXEWIHf|a6v6Op(n=Nij{xH|mXPa?; zSN&ky(;bm5JM<89BfFTQk=sgT^@DYTl{fYBn%lA&ocHi;hM!*GfZbDBysUJ3E>Z%5 zBD-fBwyiRHO@+ z@&6)>{L|kmfVY^5mM)21r{Hcei8NVX@6n|*>IaWzlJb)kI>jnR)D`qw%`dK^o-x1b zYb{KorrMD^lXU$o&2-@^n#QsvNN}RhHhd$7Z(}+;G#7S%&UAM2>q*e1$@e#lx%<^O zrPF0U89y_ALlbzrRD1^6C)N*Ul5|5E`N{KG-K`W^RxxqR3PyB3s9yhf%Y6N{x4%+9 z+^+vo(^2rv>5^9pt}(Amw-EC$+m9-tvo7_lbRqQ$TkE^^-u1(5y;Fg^Sz^N1GJ_8M z!v3BI?M;N-H6OdSgM1@Qx$W<%U-WS6X9MMP>Ph33<3mCwt3oEKLMAJThud&M7e}-L z|4(wr|CiIc9SY&P#|-XTL(KI%vLe85dMESc9o)o{V2F$6kXI{7mdW#(S8t5b{K_0- zxU-RSS$AdzGucb;Y^askTKF(h^RuhSX8SjiF?j~Q%A6AaHopeAqO;e#nK6y*OtX0h z??kLadzr*PwlI@qVfHbJ{IuVgB<2H)a$b5;vZH;KDkYgYWA4HwBvSaAzb)gd?Tn_{ zJYZW@NHCcE*aGHA$W7+R^$y15y(xH1quv<~xQdX5cTlvU$u^n5CC`5g4%z-bKl znJMxC2J(1T3e?N|$Ak&LkM*AJ*^igNg?U0ZCKpke!r*O;ijL~c{qypgitmd{7JAqPkjV63-NFQ_<-Bg3)~B)1Qd=Q%~O*Qbo*dhcwm2 znhX5pr*%$zLZJ18s6%=_c%^=jA>v#p*2Xy^LqtQUNWOa;9JTPt$q<=M$N1?ZYo!;0 z2~sE>SnthBOBMCrwsxklJd`P_4rU5VVV@4>4~ZE=!0BM3td{S(O2T%q)5DayQJKuVC`!ww6R z>5mFgKU!#h^B8}6gzW>qc!V|C&Oh&Gjki;hz@vtU^Pxn4GnVVlwh}YXjbKa@Wol;X z#4D=J!HbwG$t4K$exrlFTpnZSCainxA~Px?;G^ZJ$`U0%ZUooMCyj`=)$hrK4>y~-Z^N^DJ&)Y*9VFInM)9natPVpaV8;e3;~%`296%8ubC+7Iszr|fSX zPHXqZ@Pt^j8pz_W3{S9r+H027qN*#1xQ+SH=FXoSF4TfGq`DY5Re7y3{JKH$N5hnv zJs4b_;n>`{fSXtwTzD~bOtoHYlsr}iF?}^K);H+O7(}Po(&KDbyzNI&sPBrGYRPjh zRiw*oykzp+b*b$T7jZV!1-ZnUXI+B6s_jsM=IVjhE*<ymue3s2hu3=yA) z5;jp96I(@|E2uLYzd;fv&(oof_zCeYM$gZKYo!-k36jbRS8kpVQe82Xv>w-s7IG2i zf-9RoU4jrkO}u zInZ2vjQ?a`8st=5tD%~JqTNu0N>0s6cR&}uc`f}cL ztl4-CDVsdaD3f(-?{a?lP`+!7#Wb;;a(pnIVT)RV54Ht6h~tM2%)VRS%0(DknZhgR zeiwt8q}XVb5e1*-C^%!9|1d5=elp_oV3FPaq2xG|eStvUXR^E1SUSmfMM(tbYF55$ zs>Au*c=@igGU~fJBSXGx-T=XwnkL^BK7gsQ_JyGDss#{uA@W^g2iQg>n(JHXkdiz$ ziBBEGH~3gvdd%+}q#9rIfPRke;%xJ4&Zzrh%9_MoDAoCG1yWoZ3`AzxUK+jRwh)aXb21Tjk~xAJ{V(a zLog)h=K~;IiN$l0bxY(qJ)(d@skrD)F!Z4Eoxh60MPl6u47Psa_WJ=@o&&{M;Z9|ei zl8ArW&FpuaZx;!v6f~i8J`@qX3yKF@Z1TPn+#ziZ7O|tVABe@eVaceWKrsoFkUHrE zV|!<^qZ({W&7`Y}ybDlCUMNcDBE~up{!(ry*$+@*-VV&SH+%FY@vbW=x4W1;`QXVl z5FKmUk`EPg5x0Z09}qEZwTMCDDk9Td$g>pO@O2T0&_B0q?AYik5uQS65QA>bP~7D;g4fVAb?WeQAYV@pMt64Vr@K_hzu#G_fU^ zYdabi%u2!0V%z=bnA%x(!d;84dZUfM=bpfiBk^&@S$1H2tA@CNILx!&AcylFDONLu z7lW~%&jdBlZ;_99w2Q@8ns>h7N=r2uC zBN_YVz;$C*FbNfLC$xUKHszcACxbDUuNnL%f08;@3}(14zI-tM%3y0~r{=;+EK9Bx z)M9B;{7TP5{PH|gSkt5>|py1-hCvWLg;tU;4VvUlo#*T zXa7ynW#L~WU6z8V0Mll#fTLl47@?X6+3t5)vLcq9fS}D+D&%{sd(RuAMs``4x+thO zmMn)2wZf8luzd)by8t#+Da+#eA`RxN6v}G7%oworM6Yz&3B;wvCZD3^A+j^=Az*n`-!8cB;G z<~B=cMfjQG6(%g^7WnXI+%m+_>2HSQy}-u;Po8z@veZV6)YL}#xE>f2XpHj8TQC-c zl)RF+00b#YU+J5c;tckMe;OF2ks?4TEkGkulK>@@7mi3R8T5b-TooRdRcmMR1`7 zgyujhS4MPMOi^CejlK??*b^w32^WRlrB`Ds*~0R$DFX%bef_R;c}8W{NSE4 zRGBHpi}RFpaNg36!!j^W^Uy9!x8=aAW$23@2ey^fMfnk>KrQ77p;!Yp6a63*+N-bX z(|q`^@6uR86Dwq=P=t1o#qg>Z(GLbAh$tfvk)wddI}}l<`a`IX$C}p%g}d%7&#C#1 z38%|n`L0iYg`s zSXr+aCYnzd(*d_hm}VeYoeiBHbPrPoU)_TlQh^~qE?(1EIqwbr^bn?U9*%Q9pyMVN zaI-zn`&RoyK6(hMsNSnqntzm&i@dXr%0<3=Pso+k?jrfv>f7o9C4puK+!W^Haw1cbN^(bmov7Z=79DY>Dyp}RymkHl^76zI zVy%qY-C{oyxUl!FBPt>2daFHDriNDZ#_RYb(GQ)l9G_GHG!wGUrH7xBEhkLnZv_t? zazf6Uo44yx90e)&Ar7(xDn-8UQ|kSCDfP`ja7+_I*e58p{YH;GE351+=t(p*G=57u|iMX=Me)JKO6BP%}k*J5w3D2Y$zU0Ei^uaV?!4+iPw}L_eBX zzhKRUj|a4Uc%*zRvN~d#-R}RbyOMCT+c7g&4tm|{1n~w@CkhEjZ?&7Y`GwRpNkp|{ z?0PYW5t+tbYmnY-pP2N4Wnbb$(>%YB$8)6FwX_F)Y53)#;jZlQ%t7Lj@~J|$po2S4 z<)aU|8pBUcZHJi3DX&|1LyMZUwvccCz#z;Fn zX2?t;bFkj@!3+dRDt~Yhe;8CW7nU>7RA7jy8bGDF{DBx}G>wd=qkm+g>N0cbD^+(~ zVxd*U692<^%~hNxQ1$gIu@&B=?MOlZcF0HKqjwMBR!Iv>UNQN8@=5>~ zDFY~pih8P}R*vr{ ztgCGVE1CvD-fQJdL$?zvHFUe`ouZD&03jco0QTqYk*9z$%@dch<@0jm}p4m z14zozhKuCf^{y!an^ke~%UR7Ltc7=}=xQnDqXc*;b0@MY<3zp6P^+9P&r#cXgG6o^ zV|S7NW4TZrDU4~>NXD6-KVfhHh2dsPsFEqgwA72KxvqdcoH5)MGncP3PTo*8aYKa| zDY;7raM#9k2(G;2^X z&1RyxUaAq0gpJd8?(SsbxCbvzZe?dMMcY)d{EK4#rQQT~YSwo9ICf0OSs(o~XiW+D z{xPCw$)E`yQ_adP9cLBD7c9D4V*AKK9pC~qJF{q6j3DRbeOB-@JyWr3n<#=YNnl=< z^v^b_BED&`=o*$zP_G4jS7UW{Fu)>_zyR``Jp}opj==}C_5EHb71_KGXX*b`N+zAv zucur=d4YAaB~Ulzj^6X!Sv38-9Y2ZcqEe3WjlKGr#bJaU`p0(uIja|PMhPiSh-a2F zaaj*=xQw=$zJUKqoWWTy5dm7tTf`X)t<}VVW?SLlizQH?f-Lm-l%rv4=&zKlbMf_V zo0I6$f)aIcg%kBxvL2AEZL(s)Eh-i=Th4F|XwjEP(iRo6c!p0DXAHK*J2>XIUVf2S zZs_Q>%@>CJ_y}5|<+M}ThB<~Ob5j0W1=Z5?4T^dnTW{!FM?BHEf0-Kw-Q&pmDD^9YM zDA8z}>x!>;G0W({`zx~;YHNQKA+MUwELS=k80d0&*U6t{$WgJ(4Bkrwb;0Db?6*uD zzs|ar$)6Qy7xdcv1EO+kNbh$UprG->FMwo286tsf_!I30evX z(~IZq4Ce*zsQJm2nL1CB>B0oflQ1hFlFgIw1rM1O-4@$s(6xBUq8NsIzSRm4}`m%eZwwF$(2FR-sf5O$TRSR45u2aB>N}3929hC+gs%E7$R0MaTX>z1`O7P zRAl@+XamvJ>>ItJ6rfEq*l`g44=65thJZqm9sa#IRM=AIN~;2Y@98fw$j3{?$K1Q zv@D5CAHmS z61kpio8*4Y4H>3LQ|OouPA_H$qT}(idb#;xF7$?kNwQTiNg>tUHcK@Bo?VCaY*!p| zqePjW=YM{Hd5Y46bYqwF6JVU~G7~vaPG#&mkFepaYnU61a`Rowr0Q;q#BQl-Z_lb@OXt-tbXPQax2=<@x#8AtY(F94A;`rkIK+a8Njg~=10WW{KL3pU6A}BY zE`ay2FOKDNdJ|UYd{H!KO=Gd`4$3(Y<#ks!>aUk5XV{v%Y@+UcfSjNa_yCHF(YrhO z(e~Y)qb=CBpzEB!_v!}nFZ5=((?yQ)4g>&*A^g5xsN~*JxztMkqH^I^i1<;xy1__T zERe7AQ*i!H%SE)=J-0kppkhm~X2xoe#dHvYpdhFnRCAO#K#8 z`6RSlB*jkU3G*AJ{52xve?oJC^L$f9k#Zy6QI!*lP{8N*>c&+!Ndy$9gYT$lZYA$q zcwMhJ8+r|iWMB#?_EGYj7w8KDC#ES9R_Wk2CT#U}MIRF+u zZh+0lG;ziFS_v_~JicoGc>d21_yZ5w&-kBR6NMUZs#kC*ZIbO1KbM1lQQ3}kpyDO0 zimn1ozI#i@TxCZ^=LWo2njh=jd|1hL&1hCOO={A&?7kd#=T_FOEMarb5y_1zew=xW z-PU2@Yu%Ydy2qn`2E{GKPT$zV1Emk9O81xQhZ@czEfFi9K^N#kdh)lS>FB|hUk{(} zE<~Hvm;2pE(MAd+h}a)~kEU-3xygOs++z#uc?&wYu?1IhT}IEu04e8u50u&l(e0(M zCcQYM8k9*Yjd#Aaj&)Zv_KEMRY+IintHHkj7H#tJS86{iQ-+bFrFM4#kNUDqMj9SgxKLaiElSR&zj$J1o+F zo#0}t#vbWjVpfu>cU2g|(22~|B8Q_&N#J10W&1F#(`D)P?kXXy51>`d`R=1_TjC_N zW`3-wMAX-=*!uUCI^=j0kiZE+e?Zh!3y9`-RgQd+vmMV| zeB57JK_nVGw9qaz`2F9Qed-SBo-!G<&&OnNLQliW!8e}&bWv0GtXBx^67o=@RFQHFt8be8@yvq0u|4-`s z4Ht2mB31&tlj$~N(gRfoU zkBh2iUE&XSV21j8cf3x{lplGxufwx$2?G#llYU8(c@Uz?XqBsQo zN$^pesniKw_6v)Ea8K?buwPj%}?ou zcJ6A{FY@YNB*uvMON~Ef-x$!s`Mz7qth7m)$}E4}A!iIdFUfcA2KSM((8a&UbhZ_} zQyOisJ7r40`o(mHNVtf-rQm`5Wlkjqfs?Ri5bPPoCc!RonlVFgG>19qBVO!d%;vD_ z43|FMn=#|SyQKokVah}853?e^Dkbc|S}82LNxGqj?v64&7{T|J5`uDXB?-L#zJ-6A z)JorIBiPlgn1S=}ZAbV70eFE>!I4h)wbI5=ky&zyR`SgIbYqfkY@QC~gw0J0wD-L3 zH|y%(__HGZDT33B#~A+~@ngax7tC6!xAnm|0T3Ctl<2QoFzdzgpLWrGbqn%^FuY6F zR?I6z89n@=PP^x&P<^YTz2_n;CvZSk1W05DnhzeYaG`r=(4IbM$Arptsf?G3c3r9< zvY;9&95bL3ecTiQtrSwlMzsgkJAhTq$4dDx9$K`LLVmi)vS`U-EWEESdap8+8aM@#bJ%C91aXtuk{gStDvvq1FZt2)>Xy6&*2*jCh8 zvAYtdg|6^}J9M#VngJB9u{eiVY|Z@64xB^a_jIt~I)B_oF>6m&;LO7@X0nA1V2)kC zZ*DB*>v4uf#@F@mpZ3@enje(%rv;CbJ^V*Kw!O#fUR39aNz|GD_;1MSlD0hkU@VB>EVf z;K(8Rg+hp=sN}0#$v`jp@m356m z=oK`i*OOQ2_nSk?tOM^X$6zj9#6(xv?FBX1x4Nmqc8E>RI8VRrHWLHpO%)Xs0emaEZR<+EFvgnd|6jFjhm#mWNK){4kQxcp2lq1ZA5b|M%e$cS`uANd=J{pTX}?;%+rf93KzMPxuC?wPQajFI`fb1OU8Q^_(hLBuD3b{(dMXXYD!Ps3W?dZ5?JUi#$+p6+01M3xql8zeQ zY0kmDDi!fiw1-q!KX?$$Ofxhj)gjkcO-*|o$iI-N90|j-ah`8}*vc!Bm zF#tQ-W%>0lt!I%WKY>hYU|ouhXXonMENCo0BD1ibi|F0Ew&LYM-(~*W#zX0DwD4{O zBC*VZWtOd}qgKI6vu??aEf(IR)mU47N0N0(;U~A8I_Fd(zB8jlWV9sS!92hFi=&Nv z!@qWaas9rrtJv_XEc13!uce_Wt4Ff=n#I@Uz@D-&5j*X$gmo(yFRUCyj}pqu$#7b> zESpPKk0{f>ocf;HQC2Qi)GKOfi!gOkNk^8rNyLZu)=EDKg$fo68Ya=9WJj71E`5=v zjHc;?*yZ#?qhgNvB-DkM_A*J>b7c}s5^NHDm01x;R_|8oiqnk35`^|3Dyw+9(9G2I zz%Cp(w+;nyRkI3Hg&TCe3@*dFFk`ODckRF+qU15+pNgJ7AM^EvkG+6aKO}<&>sQ+{ znQ$7QPd9XX>DDpW@>Y?|&zQny72FS1B6bMvM+&0*7=1#b&M3wPnA&y-08wXtfOUAg zN+$zqmx{1b#~}kDY;!jD#njgIcBT-+5RHm`nL>%$ky_@YauHuYL^mXygk>sp!?`OM z5ETqP>@2d4EH&06VA5u%ax%%GVM3@I4TX-p2$B`{AzUocM~lJ94=;9>?0Sb(e%Qt2I|u`?3wJHs z=B2K=frjF{zeWQ)_5cqCT^{+l@hQQ{QJje*4ygV`>eqU(L3e5$}!S>NHx z><2V5P8cJc#)`K@n`Ngo)ko0-F`ckOj*4otdmmQn?%3Rpay6os@Q0O~#hMEc4r5}K zzv`DS`UCn?zoQR3n!l|#R#aQ@urlbL&ZX^;(LdhyZM;piU^&-$W3cE_cJG7AjA7h0 zV?(j=d@(lWC}ua6Q@Rr3snVgFNkrl(6W#adqt{W^poaWy5>yG*JvvH=t^r0YDI@F% z*rl)sJ8BO1{6DpqI^pbd9G$_VOU1l1;Scx5S&RYRj>c}_YfTA9!QUJLt1!%quj9W*#=F)#*ThA-2dU=G!t&e zY5&Zi$4^a{mz9Wek>?rE<2X3&A{>{F$3f{k-L^J9vq#fd@O{DAf<|GZrQmwOZS$!z z{`=N&+{2h`JR8yhd9QwOxk!v*2HQY5fsJ&MGNkkMoz|kp6%T|R=53A>f(2ilCfjb) z&F^|2>uZfm7DpR;?d%rZ=u&eJzPgO-q4NXI{=)S4-Hna(XnfGSj>u<4>5;} z4#lllY56Um^|a@2liO)D9ilB*=!iHLpd+H;ju=8?Uvxt)?hT3^iA2p7l6h!531y#< zYw=v;7c|$QLas+>u3yku^+<<>W(y1X7TaFqdh(ab<%a7m{0yinxo^!OM+m7%#Nd;U zMGYZsAKIJp4^kC(_StA`m2iv2a?v2uR8xk;^b+oEy5Df&>4gbf^CE~FP9NBRfx492 zNv~GG!ec|Y+^aC|#CZ#1JV-BK7%^!aL~avptsuJ2w{5^nUQBxSI;K7>m?Nsfz3 zxXrX0$L4`Lm!X*`@sgH@n}D$qSB-E-O_`eJICLGX)TVF1CAIX-G$fItoXP4oDA%WK zRc2~&7sJDirh^Mo!ML%NtelJ6S{pVf*(BT^5@^p%*Ci`)Ax)Xes&PD@B2U3C2$HR>tET>nyh+0kP^|Y-Nb7oQ+y-2cw z9y(9LRWH0tma!hCqIb}6B@Ki;o(uBG5r_YWImuy~}6#nlV z3EnmfcYDoQxLr5oHJ8-1R92T9KH@d9pZkGxH#N7lw6(W&wLNSjCaFo*?%u9wm*@Jx zDBJzo17U}>PiUXi9@ajseOCLN_Id5m?MvI^+Fx!*0wP_mI4f6T%$4oQb$vT`z~S7G z)4B8Zb9P1Uc+&*aGE<^SXWC-gZrX3EFpW3Wn0_+J^2VEH;2qD*3OA8ZqDkSW1_xF1Kahbh-2fdl3) zScWXb>z_6%LLk%B0o8H8kq495ePQKmUNI1B0GWolS>#0%4=&zLzYGHUjmxi7vnZ~lUX(Tf%@S-LDHHg5Tfl`pS~ zPe}Z)!(Z99{nZ^=@9&vjF#Ywc$jHdKbLSSkzUR$17rwV}*|O#Dz5l_!5B8Jka~CfA z@WYS(e)u27M~)slexl^#PySh2cJkBmic^)R&zP&KYtCBEeO6m{{=(;9T&%zJWy4pE zm%skkH{V|Q?)$4h{QKIEtzyP6}(DB?>-6D1jg zHy@uOe#D;ykf-tLW)OLXJWGO!3PR&J5<)a&Jb4apaE6kJUNhC>RHCaRO2q0lFqG?GgUW8dsHjs@Zjii%J zmhI$KvV+u-hj>^EbwrxT&*T>z0kq+N68s-niP^F`LFR`L@t6Seg<#Uf&;!rt2dIX>*9dyQ^w?nDv{|R@5l}UOwoh-y1U`*e?xJ@4) zqFkGV>%w#@)TLx>N>8IRC7p~t+!nwuB`saQR@+Y{qzPfZ%-EE&CY4FWbRbN9bUdeb zVMSsmiL=y2=IS=U-NRE($ZLlQiCnyR;o>RE)Z`5*j@fl?RCov?JodBO>D4|;Kch?E zh%7L(DCu;pRtrw@&pyV+NYeORz?ejou0OtN-+?pHVBl{ z27Qc;R>4K7tf{-DW1I)ZqCCrWYGI*HaTz4VQG1qpg#(i%s_mvQ~~#UDA; zYnZi^?6P6csQrmsdj=CJPVDJFIQOOTvHqO>@Pk1pXJs-}t&T^!xq)XjpW&9Ck(g z4T#Z6`?=4w5QaDHSNwa9_eJ1&x)P6C*taH58Fe0ZJNMc5Z=ud#dmvz0tK`5S*mGdF zUu(e$rBC*&*Q{pg-4+-^FTtqSJO@RU;|KmziZIVU@#}KHB8ss7p~#pD|a(Cavv*)~4yztM#dP z&KqwGt0{yPn3kxMx2EAO2#3w;93h+WfI})CG8WMKfCPl;QZ`V0hhx(QmS%|ekku?M zDM6D4072==No*2Pr)U!xI#4HQlM%BY0#D;{5U_T#!&N>jJpS=N|D$gZ(B6QBvg3Vr0Q6mf{-2|#IQ(djqIkkP>IhKwBf%Dq4Zc^o@kPK0K;Eq(7P-H4!kBMw%~uJD~)4(*R&l6X>Qm zbiRp#fks8Zqi-#=0GJNk{$Iay0X(qm7xJZVB#cLfbKuvXFU^OVl`!;O1?r}6D{KS8 zG`Jtn7a-(d$9wJ+kG{=t8hDhR{`4q)SoT0V{vD6Ldyz?%cSh9~l+ao&OH#}nSwM`B?#pzqh8-XXyK zcuVp2{!-vw>_>v#k9WKekG3&=k%#_209s%Dzka+jc+m7H9(^!3@V~%2{TLp77&ioX z{b?ZRk-G+NTE{d!;r|N&?|h#hECG+w@i|O?zVsdLhyNixn*NuM;nBA~u)rgG;Mf17 zIMiH$=}&JD@b&=D_*ez@(|QefG=EATeZbu literal 0 HcmV?d00001 diff --git a/linux/syslinux.c b/linux/syslinux.c new file mode 100644 index 0000000..37a54fd --- /dev/null +++ b/linux/syslinux.c @@ -0,0 +1,560 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 1998-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux.c - Linux installer program for SYSLINUX + * + * This is Linux-specific by now. + * + * This is an alternate version of the installer which doesn't require + * mtools, but requires root privilege. + */ + +/* + * If DO_DIRECT_MOUNT is 0, call mount(8) + * If DO_DIRECT_MOUNT is 1, call mount(2) + */ +#ifdef __KLIBC__ +# define DO_DIRECT_MOUNT 1 +#else +# define DO_DIRECT_MOUNT 0 /* glibc has broken losetup ioctls */ +#endif + +#define _GNU_SOURCE +#define _XOPEN_SOURCE 500 /* For pread() pwrite() */ +#define _FILE_OFFSET_BITS 64 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* FIGETBSZ, FIBMAP */ +#include /* FAT_IOCTL_SET_ATTRIBUTES, SECTOR_* */ +#ifndef FAT_IOCTL_SET_ATTRIBUTES +# define FAT_IOCTL_SET_ATTRIBUTES _IOW('r', 0x11, uint32_t) +#endif + +#include +#ifndef _PATH_MOUNT +# define _PATH_MOUNT "/bin/mount" +#endif +#ifndef _PATH_UMOUNT +# define _PATH_UMOUNT "/bin/umount" +#endif +#ifndef _PATH_TMP +# define _PATH_TMP "/tmp/" +#endif + +#include "syslinux.h" + +#if DO_DIRECT_MOUNT +# include +#endif + +const char *program; /* Name of program */ +const char *device; /* Device to install to */ +pid_t mypid; +char *mntpath = NULL; /* Path on which to mount */ +off_t filesystem_offset = 0; /* Filesystem offset */ +#if DO_DIRECT_MOUNT +int loop_fd = -1; /* Loop device */ +#endif + +void __attribute__((noreturn)) usage(void) +{ + fprintf(stderr, "Usage: %s [-sfr][-d directory][-o offset] device\n", program); + exit(1); +} + +void __attribute__((noreturn)) die(const char *msg) +{ + fprintf(stderr, "%s: %s\n", program, msg); + +#if DO_DIRECT_MOUNT + if ( loop_fd != -1 ) { + ioctl(loop_fd, LOOP_CLR_FD, 0); /* Free loop device */ + close(loop_fd); + loop_fd = -1; + } +#endif + + if ( mntpath ) + unlink(mntpath); + + exit(1); +} + +/* + * read/write wrapper functions + */ +ssize_t xpread(int fd, void *buf, size_t count, off_t offset) +{ + char *bufp = (char *)buf; + ssize_t rv; + ssize_t done = 0; + + while ( count ) { + rv = pread(fd, bufp, count, offset); + if ( rv == 0 ) { + die("short read"); + } else if ( rv == -1 ) { + if ( errno == EINTR ) { + continue; + } else { + die(strerror(errno)); + } + } else { + bufp += rv; + offset += rv; + done += rv; + count -= rv; + } + } + + return done; +} + +ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset) +{ + const char *bufp = (const char *)buf; + ssize_t rv; + ssize_t done = 0; + + while ( count ) { + rv = pwrite(fd, bufp, count, offset); + if ( rv == 0 ) { + die("short write"); + } else if ( rv == -1 ) { + if ( errno == EINTR ) { + continue; + } else { + die(strerror(errno)); + } + } else { + bufp += rv; + offset += rv; + done += rv; + count -= rv; + } + } + + return done; +} + +/* + * Create a block map for ldlinux.sys + */ +int make_block_map(uint32_t *sectors, int len, int dev_fd, int fd) +{ + int nsectors = 0; + int blocksize, nblock, block; + int i; + + (void)dev_fd; + + if (ioctl(fd, FIGETBSZ, &blocksize) < 0) + die("ioctl FIGETBSZ failed"); + + blocksize >>= SECTOR_BITS; /* sectors/block */ + + nblock = 0; + while (len > 0) { + block = nblock++; + if (ioctl(fd, FIBMAP, &block) < 0) + die("ioctl FIBMAP failed"); + + for (i = 0; i < blocksize; i++) { + if (len <= 0) + break; + + *sectors++ = (block*blocksize)+i; + nsectors++; + len -= (1 << SECTOR_BITS); + } + } + + return nsectors; +} + +/* + * Mount routine + */ +int do_mount(int dev_fd, int *cookie, const char *mntpath, const char *fstype) +{ + struct stat st; + + (void)cookie; + + if (fstat(dev_fd, &st) < 0) + return errno; + +#if DO_DIRECT_MOUNT + { + if ( !S_ISBLK(st.st_mode) ) { + /* It's file, need to mount it loopback */ + unsigned int n = 0; + struct loop_info64 loopinfo; + int loop_fd; + + for ( n = 0 ; loop_fd < 0 ; n++ ) { + snprintf(devfdname, sizeof devfdname, "/dev/loop%u", n); + loop_fd = open(devfdname, O_RDWR); + if ( loop_fd < 0 && errno == ENOENT ) { + die("no available loopback device!"); + } + if ( ioctl(loop_fd, LOOP_SET_FD, (void *)dev_fd) ) { + close(loop_fd); loop_fd = -1; + if ( errno != EBUSY ) + die("cannot set up loopback device"); + else + continue; + } + + if ( ioctl(loop_fd, LOOP_GET_STATUS64, &loopinfo) || + (loopinfo.lo_offset = filesystem_offset, + ioctl(loop_fd, LOOP_SET_STATUS64, &loopinfo)) ) + die("cannot set up loopback device"); + } + + *cookie = loop_fd; + } else { + snprintf(devfdname, sizeof devfdname, "/proc/%lu/fd/%d", + (unsigned long)mypid, dev_fd); + *cookie = -1; + } + + return mount(devfdname, mntpath, fstype, + MS_NOEXEC|MS_NOSUID, "umask=077,quiet"); + } +#else + { + char devfdname[128], mnt_opts[128]; + pid_t f, w; + int status; + + snprintf(devfdname, sizeof devfdname, "/proc/%lu/fd/%d", + (unsigned long)mypid, dev_fd); + + f = fork(); + if ( f < 0 ) { + return -1; + } else if ( f == 0 ) { + if ( !S_ISBLK(st.st_mode) ) { + snprintf(mnt_opts, sizeof mnt_opts, + "rw,nodev,noexec,loop,offset=%llu,umask=077,quiet", + (unsigned long long)filesystem_offset); + } else { + snprintf(mnt_opts, sizeof mnt_opts, "rw,nodev,noexec,umask=077,quiet"); + } + execl(_PATH_MOUNT, _PATH_MOUNT, "-t", fstype, "-o", mnt_opts, \ + devfdname, mntpath, NULL); + _exit(255); /* execl failed */ + } + + w = waitpid(f, &status, 0); + return ( w != f || status ) ? -1 : 0; + } +#endif +} + +/* + * umount routine + */ +void do_umount(const char *mntpath, int cookie) +{ +#if DO_DIRECT_MOUNT + int loop_fd = cookie; + + if ( umount2(mntpath, 0) ) + die("could not umount path"); + + if ( loop_fd != -1 ) { + ioctl(loop_fd, LOOP_CLR_FD, 0); /* Free loop device */ + close(loop_fd); + loop_fd = -1; + } + +#else + pid_t f = fork(); + pid_t w; + int status; + (void)cookie; + + if ( f < 0 ) { + perror("fork"); + exit(1); + } else if ( f == 0 ) { + execl(_PATH_UMOUNT, _PATH_UMOUNT, mntpath, NULL); + } + + w = waitpid(f, &status, 0); + if ( w != f || status ) { + exit(1); + } +#endif +} + +int main(int argc, char *argv[]) +{ + static unsigned char sectbuf[SECTOR_SIZE]; + unsigned char *dp; + const unsigned char *cdp; + int dev_fd, fd; + struct stat st; + int nb, left; + int err = 0; + char mntname[128]; + char *ldlinux_name, **argp, *opt; + const char *subdir = NULL; + uint32_t sectors[65]; /* 65 is maximum possible */ + int nsectors = 0; + const char *errmsg; + int mnt_cookie; + + int force = 0; /* -f (force) option */ + int stupid = 0; /* -s (stupid) option */ + int raid_mode = 0; /* -r (RAID) option */ + + (void)argc; /* Unused */ + + program = argv[0]; + mypid = getpid(); + + device = NULL; + + umask(077); + + for ( argp = argv+1 ; *argp ; argp++ ) { + if ( **argp == '-' ) { + opt = *argp + 1; + if ( !*opt ) + usage(); + + while ( *opt ) { + if ( *opt == 's' ) { + stupid = 1; + } else if ( *opt == 'r' ) { + raid_mode = 1; + } else if ( *opt == 'f' ) { + force = 1; /* Force install */ + } else if ( *opt == 'd' && argp[1] ) { + subdir = *++argp; + } else if ( *opt == 'o' && argp[1] ) { + /* Byte offset */ + filesystem_offset = (off_t)strtoull(*++argp, NULL, 0); + } else { + usage(); + } + opt++; + } + } else { + if ( device ) + usage(); + device = *argp; + } + } + + if ( !device ) + usage(); + + /* + * First make sure we can open the device at all, and that we have + * read/write permission. + */ + dev_fd = open(device, O_RDWR); + if ( dev_fd < 0 || fstat(dev_fd, &st) < 0 ) { + perror(device); + exit(1); + } + + if ( !S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode) ) { + die("not a device or regular file"); + } + + if ( filesystem_offset && S_ISBLK(st.st_mode) ) { + die("can't combine an offset with a block device"); + } + + xpread(dev_fd, sectbuf, SECTOR_SIZE, filesystem_offset); + fsync(dev_fd); + + /* + * Check to see that what we got was indeed an MS-DOS boot sector/superblock + */ + if( (errmsg = syslinux_check_bootsect(sectbuf)) ) { + fprintf(stderr, "%s: %s\n", device, errmsg); + exit(1); + } + + /* + * Now mount the device. + */ + if ( geteuid() ) { + die("This program needs root privilege"); + } else { + int i = 0; + struct stat dst; + int rv; + + /* We're root or at least setuid. + Make a temp dir and pass all the gunky options to mount. */ + + if ( chdir(_PATH_TMP) ) { + perror(program); + exit(1); + } + +#define TMP_MODE (S_IXUSR|S_IWUSR|S_IXGRP|S_IWGRP|S_IWOTH|S_IXOTH|S_ISVTX) + + if ( stat(".", &dst) || !S_ISDIR(dst.st_mode) || + (dst.st_mode & TMP_MODE) != TMP_MODE ) { + die("possibly unsafe " _PATH_TMP " permissions"); + } + + for ( i = 0 ; ; i++ ) { + snprintf(mntname, sizeof mntname, "syslinux.mnt.%lu.%d", + (unsigned long)mypid, i); + + if ( lstat(mntname, &dst) != -1 || errno != ENOENT ) + continue; + + rv = mkdir(mntname, 0000); + + if ( rv == -1 ) { + if ( errno == EEXIST || errno == EINTR ) + continue; + perror(program); + exit(1); + } + + if ( lstat(mntname, &dst) || dst.st_mode != (S_IFDIR|0000) || + dst.st_uid != 0 ) { + die("someone is trying to symlink race us!"); + } + break; /* OK, got something... */ + } + + mntpath = mntname; + } + + if (do_mount(dev_fd, &mnt_cookie, mntpath, "vfat") && + do_mount(dev_fd, &mnt_cookie, mntpath, "msdos")) { + rmdir(mntpath); + die("mount failed"); + } + + ldlinux_name = alloca(strlen(mntpath)+14+ + (subdir ? strlen(subdir)+2 : 0)); + if ( !ldlinux_name ) { + perror(program); + err = 1; + goto umount; + } + sprintf(ldlinux_name, "%s%s%s//ldlinux.sys", + mntpath, subdir ? "//" : "", subdir ? subdir : ""); + + if ((fd = open(ldlinux_name, O_RDONLY)) >= 0) { + uint32_t zero_attr = 0; + ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &zero_attr); + close(fd); + } + + unlink(ldlinux_name); + fd = open(ldlinux_name, O_WRONLY|O_CREAT|O_TRUNC, 0444); + if ( fd < 0 ) { + perror(device); + err = 1; + goto umount; + } + + cdp = syslinux_ldlinux; + left = syslinux_ldlinux_len; + while ( left ) { + nb = write(fd, cdp, left); + if ( nb == -1 && errno == EINTR ) + continue; + else if ( nb <= 0 ) { + perror(device); + err = 1; + goto umount; + } + + dp += nb; + left -= nb; + } + + fsync(fd); + /* + * Set the attributes + */ + { + uint32_t attr = 0x07; /* Hidden+System+Readonly */ + ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr); + } + + /* + * Create a block map. + */ + nsectors = make_block_map(sectors, syslinux_ldlinux_len, dev_fd, fd); + + close(fd); + sync(); + +umount: + do_umount(mntpath, mnt_cookie); + sync(); + rmdir(mntpath); + + if ( err ) + exit(err); + + /* + * Patch ldlinux.sys and the boot sector + */ + syslinux_patch(sectors, nsectors, stupid, raid_mode); + + /* + * Write the now-patched first sector of ldlinux.sys + */ + xpwrite(dev_fd, syslinux_ldlinux, SECTOR_SIZE, + filesystem_offset+((off_t)sectors[0] << SECTOR_BITS)); + + /* + * To finish up, write the boot sector + */ + + /* Read the superblock again since it might have changed while mounted */ + xpread(dev_fd, sectbuf, SECTOR_SIZE, filesystem_offset); + + /* Copy the syslinux code into the boot sector */ + syslinux_make_bootsect(sectbuf); + + /* Write new boot sector */ + xpwrite(dev_fd, sectbuf, SECTOR_SIZE, filesystem_offset); + + close(dev_fd); + sync(); + + /* Done! */ + + return 0; +} diff --git a/man/extlinux.1 b/man/extlinux.1 new file mode 100644 index 0000000..426eeb3 --- /dev/null +++ b/man/extlinux.1 @@ -0,0 +1,59 @@ +.TH extlinux "1" "18 December 2007" "SYSLINUX for ext2/ext3 filesystem" +.SH NAME +extlinux \- install the \s-1SYSLINUX\s+1 bootloader on a ext2/ext3 filesystem +.SH SYNOPSIS +.B extlinux +[\fIoptions\fP] \fIdirectory\fP +.SH DESCRIPTION +\fBEXTLINUX\fP is a new syslinux derivative, which boots from a Linux ext2/ext3 +filesystem. It works the same way as \fBSYSLINUX\fP, with a few slight modifications. +It is intended to simplify first-time installation of Linux, and for creation of +rescue and other special-purpose boot disks. +.PP +The installer is designed to be run on a mounted directory. For example, if you have an +ext2 or ext3 usb key mounted on /mnt, you can run the following command: +.IP +.B extlinux --install /mnt +.SH OPTIONS +.TP +\fB\-H\fR, \fB\-\-heads\fR=# +Force the number of heads. +.TP +\fB\-i\fR, \fB\-\-install\fR +Install over the current bootsector. +.TP +\fB\-O\fR, \fB\-\-clear\-once\fR +Clear the boot-once command. +.TP +\fB\-o\fR, \fB\-\-once\fR=\fIcommand\fR +Execute a command once upon boot. +.TP +\fB\-r\fR, \fB\-\-raid\fR +Fall back to the next device on boot failure. +.TP +\fB\-\-reset\-adv\fR +Reset auxiliary data. +.TP +\fB\-S\fR, \fB\-\-sectors\fR=\fI#\fR +Force the number of sectors per track. +.TP +\fB\-U\fR, \fB\-\-update\fR +Updates a previous \fBEXTLINUX\fP installation. +.TP +\fB\-z\fR, \fB\-\-zip\fR +Force zipdrive geometry (-H 64 -S 32). +.SH BUGS +I would appreciate hearing of any problems you have with \s-1SYSLINUX\s+1. I +would also like to hear from you if you have successfully used \s-1SYSLINUX\s+1, +especially if you are using it for a distribution. +.PP +If you are reporting problems, please include all possible information +about your system and your BIOS; the vast majority of all problems +reported turn out to be BIOS or hardware bugs, and I need as much +information as possible in order to diagnose the problems. +.PP +There is a mailing list for discussion among \s-1SYSLINUX\s+1 users and for +announcements of new and test versions. To join, send a message to +majordomo@linux.kernel.org with the line: +.SH SEE ALSO +.BR syslinux (1) diff --git a/man/gethostip.1 b/man/gethostip.1 new file mode 100644 index 0000000..02081f1 --- /dev/null +++ b/man/gethostip.1 @@ -0,0 +1,45 @@ +.TH "GETHOSTIP" "1" +.SH "NAME" +gethostip \(em convert an IP address into various formats +.SH "SYNOPSIS" +.PP +\fBgethostip\fR [\fB-dxnf\fP] [\fB\fIHOSTNAME|IP\fR\fP] +.SH "DESCRIPTION" +.PP +This manual page documents briefly the +\fBgethostip\fR command. +.PP +The \fBgethostip\fR utility converts the given hostname or +IP address into a variety formats. It is provided by the syslinux +package to make it easier to calculate the appropriate names for +pxelinux configuration files. These filenames can be the complete +hexadecimal representation for a given IP address, or a partial +hexadecimal representation to match a range of IP addresses. + +.SH "OPTIONS" +.PP +A summary of options is included below. +.IP "\fB-d\fP" 10 +Output the IP address in decimal format. +.IP "\fB-x\fP" 10 +Output the IP address in hexadecimal format. +.IP "\fB-n\fP" 10 +Output the host's canonical name. +.IP "\fB-f\fP" 10 +Full output. Outputs the IP address in all supported formats. +Same as \fB-xdn\fP. + +.SH "SEE ALSO" +.PP +\fBsyslinux\fR(1) + +.PP +More details can be found in the pxelinux documentation, which +can be found in +\fB/usr/share/doc/syslinux/pxelinux.doc.gz\fP on +\fBDebian GNU/Linux\fP systems. + +.SH "AUTHOR" +.PP +This manual page was compiled by dann frazier for +the \fBDebian GNU/Linux\fP system (but may be used by others). diff --git a/man/lss16toppm.1 b/man/lss16toppm.1 new file mode 100644 index 0000000..90447b9 --- /dev/null +++ b/man/lss16toppm.1 @@ -0,0 +1,27 @@ +.TH "LSS16TOPPM" "1" +.SH "NAME" +lss16toppm \(em Convert an LSS-16 image to PPM +.SH "SYNOPSIS" +.PP +\fBlss16toppm\fR [\fB-map\fP] [< file.lss] [> file.ppm] +.SH "DESCRIPTION" +.PP +This manual page documents briefly the \fBlss16toppm\fR command. + +.PP +The \fBlss16toppm\fR utility converts an LSS-16 image to a +PPM image. + +.SH "OPTIONS" +.PP +A summary of options is included below. +.IP "\fB-map\fP" 10 +Output the color map to standard error. +.SH "SEE ALSO" +.PP +\fBppmtolss16\fR(1) + +.SH "AUTHOR" +.PP +This manual page was compiled by dann frazier for +the \fBDebian GNU/Linux\fP system (but may be used by others). diff --git a/man/ppmtolss16.1 b/man/ppmtolss16.1 new file mode 100644 index 0000000..954e94b --- /dev/null +++ b/man/ppmtolss16.1 @@ -0,0 +1,64 @@ +.TH "PPMTOLSS16" "1" +.SH "NAME" +ppmtolss16 \(em Convert a PPM to an LSS16 image +.SH "SYNOPSIS" +.PP +\fBppmtolss16\fR [ \fB \fI#rrggbb\fR=\fIi\fR \fP \&...] [< input.ppm] [> output.rle] +.SH "DESCRIPTION" +.PP +This manual page documents briefly the \fBppmtolss16\fR command. + +.PP +The \fBppmtolss16\fR program converts a "raw" PPM file with +max 16 colors to a simple RLE-based format: + +.PP +\fBsimple RLE-based format\fR +.TS +tab(); +l l. +unint32 0x1413f3dmagic (littleendian) +unint16 xsizelittleendian +unint15 ysizelittleendian +16 x unint8 r,g,bcolor map +.TE +.PP +Color map is in 6-bit format (each byte is 0..63) +.PP +Then, a sequence of nybbles: +.PP +N ... if N is != previous pixel, one pixel of color N, otherwise +run sequence follows ... + +.PP +M ... if M > 0 then run length is M+1, otherwise run sequence is +encoded in two nybbles, littleendian, +17 + +.PP +The nybble sequences are on a per-row basis, runs may not extend across +rows and odd-nybble rows are zero-padded. + +.PP +At the start of a row, the "previous pixel" is assumed to be zero. + +.SH "OPTIONS" +.PP +A summary of options is included below. +.IP "\fB\fI#rrggbb\fR=\fIi\fR\fP" 10 +Specify that the color #rrggbb (hex) should be assigned index +i (decimal). + +.SH "BUG" +.PP +This program cannot handle comments in the header, nor "plain" ppm +format. + +.SH "SEE ALSO" +.PP +\fBppmtolss16\fR(1) + +.SH "AUTHOR" +.PP +This manual page was compiled by dann frazier for +the \fBDebian GNU/Linux\fP system (but may be used by others). Most of the content +was written by H. Peter Anvin. diff --git a/man/syslinux.1 b/man/syslinux.1 new file mode 100644 index 0000000..70d44d1 --- /dev/null +++ b/man/syslinux.1 @@ -0,0 +1,388 @@ +.TH SYSLINUX 1 "18 December 2007" "SYSLINUX" +.SH NAME +syslinux \- install the \s-1SYSLINUX\s+1 bootloader on a FAT filesystem +.SH SYNOPSIS +.B syslinux +[\fB\-sfr\fP] +[\fB\-d\fP \fIdirectory\fP] +[\fB\-o\fP \fIoffset\fP] +.I device +.SH DESCRIPTION +\fBSyslinux\fP is a boot loader for the Linux operating system which +operates off an MS-DOS/Windows FAT filesystem. It is intended to +simplify first-time installation of Linux, and for creation of rescue +and other special-purpose boot disks. +.PP +In order to create a bootable Linux floppy using \fBSyslinux\fP, prepare a +normal MS-DOS formatted floppy. Copy one or more Linux kernel files to +it, then execute the command: +.IP +.B syslinux /dev/fd0 +.PP +This will alter the boot sector on the disk and copy a file named +LDLINUX.SYS into its root directory. +.PP +On boot time, by default, the kernel will be loaded from the image named +LINUX on the boot floppy. This default can be changed, see the section +on the \fBsyslinux\fP configuration file. +.PP +If the Shift or Alt keys are held down during boot, or the Caps or Scroll +locks are set, \fBsyslinux\fP will display a +.BR lilo (8) +-style "boot:" prompt. The user can then type a kernel file name followed by +any kernel parameters. The \fBsyslinux\fP loader does not need to know about the +kernel file in advance; all that is required is that it is a file located in +the root directory on the disk. +.PP +\fBSyslinux\fP supports the loading of initial ramdisks (initrd) and the +bzImage kernel format. +.SH OPTIONS +.TP +.B \-s +Install a "safe, slow and stupid" version of \fBsyslinux\fP. This version may +work on some very buggy BIOSes on which \fBsyslinux\fP would otherwise fail. +If you find a machine on which the \-s option is required to make it boot +reliably, please send as much info about your machine as you can, and include +the failure mode. +.TP +.B \-f +Force install even if it appears unsafe. +.TP +.B \-r +RAID mode. If boot fails, tell the BIOS to boot the next device in +the boot sequence (usually the next hard disk) instead of stopping +with an error message. This is useful for RAID-1 booting. +.TP +\fB\-d\fP \fIsubdirectory\fP +Install the \s-1SYSLINUX\s+1 control files in a subdirectory with the +specified name (relative to the root directory on the device). +.TP +\fB\-o\fP \fIoffset\fP +Indicates that the filesystem is at an offset from the base of the +device or file. +.SH FILES +.SS "Configuration file" +All the configurable defaults in \fBsyslinux\fP can be changed by putting a +file called +.B syslinux.cfg +in the root directory of the boot floppy. This +is a text file in either UNIX or DOS format, containing one or more of +the following items (case is insensitive for keywords). +.PP +In the configuration file blank lines and comment lines beginning +with a hash mark (#) are ignored. +.TP +\fBdefault\fP \fIkernel\fP [ \fIoptions ...\fP ] +Sets the default command line. If \fBsyslinux\fP boots automatically, it will act +just as if the entries after "default" had been typed in at the "boot:" prompt. +.IP +If no configuration file is present, or no "default" entry is present in the +configuration file, the default is "linux auto". +.TP +NOTE: Earlier versions of \s-1SYSLINUX\s+1 used to automatically +append the string "auto" to whatever the user specified using +the DEFAULT command. As of version 1.54, this is no longer +true, as it caused problems when using a shell as a substitute +for "init." You may want to include this option manually. +.TP +.BI append " options ..." +Add one or more \fIoptions\fP to the kernel command line. These are added both +for automatic and manual boots. The options are added at the very beginning of +the kernel command line, usually permitting explicitly entered kernel options +to override them. This is the equivalent of the +.BR lilo (8) + "append" option. +.PP +.nf +.BI label\ label +.RS 2 +.BI kernel\ image +.BI append\ options\ ... +.RE +.fi +.RS +Indicates that if \fIlabel\fP is entered as the kernel to boot, \fBsyslinux\fP should +instead boot \fIimage\fP, and the specified "append" options should be used +instead of the ones specified in the global section of the file (before the +first "label" command.) The default for \fIimage\fP is the same as \fIlabel\fP, +and if no "append" is given the default is to use the global entry (if any). +Use "append -" to use no options at all. Up to 128 "label" entries are +permitted. +.TP +.B Notes: +Labels are mangled as if they were DOS filenames, and must be unique after +mangling. For example, two labels "v2.1.30" and "v2.1.31" will not be +distinguishable. +.IP +The "image" doesn't have to be a Linux kernel; it can be a boot sector or a +COMBOOT file (see below.) +.RE +.TP +.BI implicit\ flag_val +If \fIflag_val\fP is 0, do not load a kernel image unless it has been +explicitly named in a "label" statement. The default is 1. +.TP +.BI timeout\ timeout +Indicates how long to wait at the "boot:" prompt until booting automatically, in +units of 1/10 s. The timeout is cancelled as soon as the user types anything +on the keyboard, the assumption being that the user will complete the command +line already begun. A timeout of zero will disable the timeout completely, +this is also the default. The maximum possible timeout value is 35996; +corresponding to just below one hour. +.TP +\fBserial\fP \fIport\fP [ \fIbaudrate\fP ] +Enables a serial port to act as the console. "port" is a number (0 = /dev/ttyS0 += COM1, etc.); if "baudrate" is omitted, the baud rate defaults to 9600 bps. +The serial parameters are hardcoded to be 8 bits, no parity, 1 stop bit. +.IP +For this directive to be guaranteed to work properly, it +should be the first directive in the configuration file. +.TP +.BI font\ filename +Load a font in .psf format before displaying any output (except the copyright +line, which is output as ldlinux.sys itself is loaded.) \fBsyslinux\fP only loads +the font onto the video card; if the .psf file contains a Unicode table it is +ignored. This only works on EGA and VGA cards; hopefully it should do nothing +on others. +.TP +.BI kbdmap\ keymap +Install a simple keyboard map. The keyboard remapper used is \fIvery\fP +simplistic (it simply remaps the keycodes received from the BIOS, which means +that only the key combinations relevant in the default layout \- usually U.S. +English \- can be mapped) but should at least help people with AZERTY keyboard +layout and the locations of = and , (two special characters used heavily on the +Linux kernel command line.) +.IP +The included program +.BR keytab-lilo.pl (8) +from the +.BR lilo (8) + distribution can be used to create such keymaps. +.TP +.BI display\ filename +Displays the indicated file on the screen at boot time (before the boot: +prompt, if displayed). Please see the section below on DISPLAY files. If the +file is missing, this option is simply ignored. +.TP +.BI prompt\ flag_val +If \fIflag_val\fP is 0, display the "boot:" prompt only if the Shift or Alt key +is pressed, or Caps Lock or Scroll lock is set (this is the default). If +\fIflag_val\fP is 1, always display the "boot:" prompt. +.PP +.nf +.BI f1\ filename +.BI f2\ filename +.I ... +.BI f9\ filename +.BI f10\ filename +.BI f11\ filename +.BI f12\ filename +.fi +.RS +Displays the indicated file on the screen when a function key is pressed at the +"boot:" prompt. This can be used to implement pre-boot online help (presumably +for the kernel command line options.) +.RE +.IP +When using the serial console, press \fI\fP to get to +the help screens, e.g. \fI2\fP to get to the f2 screen. For +f10-f12, hit \fIA\fP, \fIB\fP, \fIC\fP. For +compatiblity with earlier versions, f10 can also be entered as +\fI0\fP. +.SS "Display file format" +DISPLAY and function-key help files are text files in either DOS or UNIX +format (with or without \fI\fP). In addition, the following special codes +are interpreted: +.TP +\fI\fP = \fI\fP = ASCII 12 +Clear the screen, home the cursor. Note that the screen is +filled with the current display color. +.TP +\fI\fP, \fI\fP = \fI\fP = ASCII 15 +Set the display colors to the specified background and foreground colors, where +\fI\fP and \fI\fP are hex digits, corresponding to the standard PC +display attributes: +.IP +.nf +.ta \w'5 = dark purple 'u +0 = black 8 = dark grey +1 = dark blue 9 = bright blue +2 = dark green a = bright green +3 = dark cyan b = bright cyan +4 = dark red c = bright red +5 = dark purple d = bright purple +6 = brown e = yellow +7 = light grey f = white +.fi +.IP +Picking a bright color (8-f) for the background results in the +corresponding dark color (0-7), with the foreground flashing. +.IP +colors are not visible over the serial console. +.TP +\fI\fPfilename\fI\fP, \fI\fP = \fI\fP = ASCII 24 +If a VGA display is present, enter graphics mode and display +the graphic included in the specified file. The file format +is an ad hoc format called LSS16; the included Perl program +"ppmtolss16" can be used to produce these images. This Perl +program also includes the file format specification. +.IP +The image is displayed in 640x480 16-color mode. Once in +graphics mode, the display attributes (set by \fI\fP code +sequences) work slightly differently: the background color is +ignored, and the foreground colors are the 16 colors specified +in the image file. For that reason, ppmtolss16 allows you to +specify that certain colors should be assigned to specific +color indicies. +.IP +Color indicies 0 and 7, in particular, should be chosen with +care: 0 is the background color, and 7 is the color used for +the text printed by \s-1SYSLINUX\s+1 itself. +.TP +\fI\fP, \fI\fP = \fI\fP = ASCII 25 +If we are currently in graphics mode, return to text mode. +.TP +\fI\fP..\fI\fB, \fI\fP..\fI\fP = ASCII 16-23 +These codes can be used to select which modes to print a +certain part of the message file in. Each of these control +characters select a specific set of modes (text screen, +graphics screen, serial port) for which the output is actually +displayed: +.IP +.nf +Character Text Graph Serial +------------------------------------------------------ + = = ASCII 16 No No No + = = ASCII 17 Yes No No + = = ASCII 18 No Yes No + = = ASCII 19 Yes Yes No + = = ASCII 20 No No Yes + = = ASCII 21 Yes No Yes + = = ASCII 22 No Yes Yes + = = ASCII 23 Yes Yes Yes +.fi +.IP +For example: +.nf +Text modeGraphics modeSerial port +.fi + ... will actually print out which mode the console is in! +.TP +\fI\fP = \fI\fP = ASCII 26 +End of file (DOS convention). +.SS Comboot Images and other operating systems +This version of \fBsyslinux\fP supports chain loading of other operating +systems (such as MS-DOS and its derivatives, including Windows 95/98), +as well as COMBOOT-style standalone executables (a subset of DOS .COM +files; see separate section below.) +.PP +Chain loading requires the boot sector of the foreign operating system +to be stored in a file in the root directory of the filesystem. +Because neither Linux kernels, boot sector images, nor COMBOOT files +have reliable magic numbers, \fBsyslinux\fP will look at the file +extension. The following extensions are recognised: +.PP +.nf +.ta \w'none or other 'u +none or other Linux kernel image +CBT COMBOOT image (not runnable from DOS) +BSS Boot sector (DOS superblock will be patched in) +BS Boot sector +COM COMBOOT image (runnable from DOS) +.fi +.PP +For filenames given on the command line, \fBsyslinux\fP will search for the +file by adding extensions in the order listed above if the plain +filename is not found. Filenames in KERNEL statements must be fully +qualified. +.PP +A COMBOOT file is a standalone executable in DOS .COM format. They +can, among other things, be produced by the Etherboot package by +Markus Gutschke and Ken Yap. The following requirements apply for +these files to be sufficiently "standalone" for \fBsyslinux\fP to be able to +load and run them: +.IP \(bu +The program must not execute any DOS calls (since there is no +DOS), although it may call the BIOS. The only exception is that +the program may execute INT 20h (Terminate Program) to return to +the \fBsyslinux\fP prompt. Note especially that INT 21h AH=4Ch, INT 21h +AH=31h or INT 27h are not supported. +.IP \(bu +Only the fields pspInt20 at offset 00h, pspNextParagraph at offset 02h and +pspCommandTail at offset 80h (contains the arguments from the \fBsyslinux\fP command +line) in the PSP are supported. All other fields will contain zero. +.IP \(bu +The program must not modify any main memory outside its 64K segment if it +returns to \fBsyslinux\fP via INT 20h. +.PP +\fBSyslinux\fP currently doesn't provide any form of API for the use of +COMBOOT files. If there is need, a future version may contain an INT +interface to some \fBsyslinux\fP functions; please contact me if you have a +need or ideas for such an API. +.SS Novice protection +\fBSyslinux\fP will attempt to detect if the user is trying to boot on a 286 +or lower class machine, or a machine with less than 608K of low ("DOS") +RAM (which means the Linux boot sequence cannot complete). If so, a +message is displayed and the boot sequence aborted. Holding down the +Ctrl key while booting disables this feature. +.PP +The compile time and date of a specific \fBsyslinux\fP version can be obtained +by the DOS command "type ldlinux.sys". This is also used as the +signature for the LDLINUX.SYS file, which must match the boot sector +.PP +Any file that \fBsyslinux\fP uses can be marked hidden, system or readonly if +so is convenient; \fBsyslinux\fP ignores all file attributes. The \s-1SYSLINUX\s+1 +installed automatically sets the readonly attribute on LDLINUX.SYS. +.SS Bootable CD-ROMs +\s-1SYSLINUX\s+1 can be used to create bootdisk images for El +Torito-compatible bootable CD-ROMs. However, it appears that many +BIOSes are very buggy when it comes to booting CD-ROMs. Some users +have reported that the following steps are helpful in making a CD-ROM +that is bootable on the largest possible number of machines: +.IP \(bu +Use the -s (safe, slow and stupid) option to \s-1SYSLINUX\s+1 +.IP \(bu +Put the boot image as close to the beginning of the +ISO 9660 filesystem as possible. +.PP +A CD-ROM is so much faster than a floppy that the -s option shouldn't +matter from a speed perspective. +.PP +Of course, you probably want to use ISOLINUX instead. See the +documentation file +.BR isolinux.doc . +.SS Booting from a FAT partition on a hard disk +\s-1SYSLINUX\s+1 can boot from a FAT filesystem partition on a hard +disk (including FAT32). The installation procedure is identical to the +procedure for installing it on a floppy, and should work under either +DOS or Linux. To boot from a partition, \s-1SYSLINUX\s+1 needs to be +launched from a Master Boot Record or another boot loader, just like +DOS itself would. A sample master boot sector (\fBmbr.bin\fP) is +included with \s-1SYSLINUX\s+1. +.SH BUGS +I would appreciate hearing of any problems you have with \s-1SYSLINUX\s+1. I +would also like to hear from you if you have successfully used \s-1SYSLINUX\s+1, +especially if you are using it for a distribution. +.PP +If you are reporting problems, please include all possible information +about your system and your BIOS; the vast majority of all problems +reported turn out to be BIOS or hardware bugs, and I need as much +information as possible in order to diagnose the problems. +.PP +There is a mailing list for discussion among \s-1SYSLINUX\s+1 users and for +announcements of new and test versions. To join, send a message to +majordomo@linux.kernel.org with the line: +.PP +.B subscribe syslinux +.PP +in the body of the message. The submission address is syslinux@linux.kernel.org. +.SH SEE ALSO +.BR lilo (8), +.BR keytab-lilo.pl (8), +.BR fdisk (8), +.BR mkfs (8), +.BR superformat (1). +.SH AUTHOR +This manual page is a modified version of the original \fBsyslinux\fP +documentation by H. Peter Anvin . The conversion to a manpage +was made by Arthur Korn . diff --git a/man/syslinux2ansi.1 b/man/syslinux2ansi.1 new file mode 100644 index 0000000..063e357 --- /dev/null +++ b/man/syslinux2ansi.1 @@ -0,0 +1,41 @@ +.TH SYSLINUX2ANSI 1 +.SH NAME +syslinux2ansi \- converts a syslinux-format screen to pc-ansi +.SH SYNOPSIS +.B syslinux2ansi +< filename.input > filename.output +.SH DESCRIPTION +.I Syslinux2ansi +is a filter which converts a screen formatted for syslinux to one +compatible with PC ANSI. It will only read from standard in, and has +no command line options. +.SH BUGS +Help and version command line options would be useful. +.PP +The ability to put input and output filenames on the command line might +be good as well. +.SS Bug reports +I would appreciate hearing of any problems you have with SYSLINUX. I +would also like to hear from you if you have successfully used SYSLINUX, +especially if you are using it for a distribution. +.PP +If you are reporting problems, please include all possible information +about your system and your BIOS; the vast majority of all problems +reported turn out to be BIOD or hardware bugs, and I need as much +information as possible in order to diagnose the problems. +.PP +There is a mailing list for discussion among SYSLINUX users and for +announcements of new and test versions. To join, send a message to +majordomo@linux.kernel.org with the line: +.PP +.B subscribe syslinux +.PP +in the body of the message. The submission address is +syslinux@linux.kernel.org. +.SH "SEE ALSO" +.BR syslinux(1), +.BR perl(1) +.SH AUTHOR +This manual page is a quick write-up for Debian done by Kevin Kreamer +, by looking over the 1 screenful of Perl that is +.B syslinux2ansi. diff --git a/mbr/Makefile b/mbr/Makefile new file mode 100644 index 0000000..c3eb97a --- /dev/null +++ b/mbr/Makefile @@ -0,0 +1,53 @@ +## ----------------------------------------------------------------------- +## +## Copyright 2007-2009 H. Peter Anvin - All Rights Reserved +## Copyright 2009 Intel Corporation; author: H. Peter Anvin +## +## 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +# +# Makefile for MBR +# + +topdir = .. +include $(topdir)/MCONFIG.embedded + +all: mbr.bin altmbr.bin gptmbr.bin isohdpfx.bin isohdppx.bin \ + mbr_c.bin altmbr_c.bin gptmbr_c.bin isohdpfx_c.bin isohdppx_c.bin \ + mbr_f.bin altmbr_f.bin gptmbr_f.bin isohdpfx_f.bin isohdppx_f.bin + +%.o: %.S + $(CC) $(MAKEDEPS) $(SFLAGS) -Wa,-a=$*.lst -c -o $@ $< + +%_c.o: %.S + $(CC) $(MAKEDEPS) $(SFLAGS) -Wa,-a=$*_c.lst -DCTRL_80 -c -o $@ $< + +%_f.o: %.S + $(CC) $(MAKEDEPS) $(SFLAGS) -Wa,-a=$*_f.lst -DFORCE_80 -c -o $@ $< + +.PRECIOUS: %.elf +%.elf: %.o mbr.ld + $(LD) $(LDFLAGS) -T mbr.ld -e _start -o $@ $< + +%.bin: %.elf checksize.pl + $(OBJCOPY) -O binary $< $@ + $(PERL) checksize.pl $@ + $(CHMOD) -x $@ + +mbr_bin.c: mbr.bin + +tidy dist: + rm -f *.o *.elf *.lst .*.d + +clean: tidy + +spotless: clean + rm -f *.bin + +-include .*.d diff --git a/mbr/adjust.h b/mbr/adjust.h new file mode 100644 index 0000000..42c12a3 --- /dev/null +++ b/mbr/adjust.h @@ -0,0 +1,53 @@ +/* -*- asm -*- ----------------------------------------------------------- + * + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * adjust.h + * + * Macros to adjust the drive number + */ + +#ifndef ADJUST_H +#define ADJUST_H + +#ifdef CTRL_80 + .macro ADJUST_DRIVE + testb $0x04, BIOS_kbdflags + jz 1f + movb $0x80, %dl +1: + .endm +#elif defined(FORCE_80) + .macro ADJUST_DRIVE + movb $0x80, %dl + .endm +#else + .macro ADJUST_DRIVE + .endm +#endif + +#endif /* ADJUST_H */ diff --git a/mbr/altmbr.S b/mbr/altmbr.S new file mode 100644 index 0000000..794ab62 --- /dev/null +++ b/mbr/altmbr.S @@ -0,0 +1,304 @@ +/* ----------------------------------------------------------------------- + * + * Copyright 2007-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#include "adjust.h" + + .code16 + .text + + .globl bootsec +stack = 0x7c00 +driveno = (stack-6) +sectors = (stack-8) +secpercyl = (stack-12) + +BIOS_kbdflags = 0x417 +BIOS_page = 0x462 + + /* gas/ld has issues with doing this as absolute addresses... */ + .section ".bootsec", "a", @nobits + .globl bootsec +bootsec: + .space 512 + + .text + .globl _start +_start: + cli + xorw %ax, %ax + movw %ax, %ds + movw %ax, %ss + movw $stack, %sp + movw %sp, %si + pushw %es /* es:di -> $PnP header */ + pushw %di + movw %ax, %es + sti + cld + + /* Copy down to 0:0x600 */ + movw $_start, %di + movw $(512/2), %cx + rep; movsw + + ljmpw $0, $next +next: + + ADJUST_DRIVE + pushw %dx /* dl -> drive number */ + + /* Check to see if we have EBIOS */ + pushw %dx /* drive number */ + movb $0x41, %ah /* %al == 0 already */ + movw $0x55aa, %bx + xorw %cx, %cx + xorb %dh, %dh + stc + int $0x13 + jc 1f + cmpw $0xaa55, %bx + jne 1f + shrw %cx /* Bit 0 = fixed disk subset */ + jnc 1f + + /* We have EBIOS; patch in the following code at + read_sector_cbios: movb $0x42, %ah ; jmp read_common */ + movl $0xeb42b4+((read_common-read_sector_cbios-4) << 24), \ + (read_sector_cbios) + +1: + popw %dx + + /* Get (C)HS geometry */ + movb $0x08, %ah + int $0x13 + andw $0x3f, %cx /* Sector count */ + pushw %cx /* Save sectors on the stack */ + movzbw %dh, %ax /* dh = max head */ + incw %ax /* From 0-based max to count */ + mulw %cx /* Heads*sectors -> sectors per cylinder */ + + /* Save sectors/cylinder on the stack */ + pushw %dx /* High word */ + pushw %ax /* Low word */ + + xorl %eax, %eax /* Base */ + cdq /* Root (%edx <- 0) */ + call scan_partition_table + + /* If we get here, we have no OS */ +missing_os: + call error + .ascii "Missing operating system.\r\n" + +/* + * read_sector: read a single sector pointed to by %eax to 0x7c00. + * CF is set on error. All registers saved. + */ +read_sector: + pushal + xorl %edx, %edx + movw $bootsec, %bx + pushl %edx /* MSW of LBA */ + pushl %eax /* LSW of LBA */ + pushw %es /* Buffer segment */ + pushw %bx /* Buffer offset */ + pushw $1 /* Sector count */ + pushw $16 /* Size of packet */ + movw %sp, %si + + /* This chunk is skipped if we have ebios */ + /* Do not clobber %eax before this chunk! */ + /* This also relies on %bx and %edx as set up above. */ +read_sector_cbios: + divl (secpercyl) + shlb $6, %ah + movb %ah, %cl + movb %al, %ch + xchgw %dx, %ax + divb (sectors) + movb %al, %dh + orb %ah, %cl + incw %cx /* Sectors are 1-based */ + movw $0x0201, %ax + +read_common: + movb (driveno), %dl + int $0x13 + leaw 16(%si), %sp /* Drop DAPA */ + popal + ret + +/* + * read_partition_table: + * Read a partition table (pointed to by %eax), and copy + * the partition table into the ptab buffer. + * + * Clobbers %si, %di, and %cx, other registers preserved. + * %cx = 0 on exit. + * + * On error, CF is set and ptab is overwritten with junk. + */ +ptab = _start+446 + +read_partition_table: + call read_sector + movw $bootsec+446, %si + movw $ptab, %di + movw $(16*4/2), %cx + rep ; movsw + ret + +/* + * scan_partition_table: + * Scan a partition table currently loaded in the partition table + * area. Preserve all registers. + * + * On entry: + * %eax - base (location of this partition table) + * %edx - root (offset from MBR, or 0 for MBR) + * + * These get pushed into stack slots: + * 28(%bp) - %eax - base + * 20(%bp) - %edx - root + */ + +scan_partition_table: + pushal + movw %sp, %bp + + /* Scan the primary partition table */ + movw $ptab, %si + movw $4, %cx + /* Is it a primary partition table? */ + andl %edx, %edx + jnz 7f + push %si + push %cx + +5: + decb (partition) + jz boot + addw $16, %bx + loopw 5b + + popw %cx /* %cx <- 4 */ + popw %si /* %si <- ptab */ + + /* No primary partitions found, look for extended/logical partitions */ +7: + movb 4(%si), %al + andb %al, %al + jz 12f /* Not a valid partition */ + cmpb $0x0f, %al /* 0x0f = Win9x extended */ + je 8f + andb $~0x80, %al /* 0x85 = Linux extended */ + cmpb $0x05, %al /* 0x05 = MS-DOS extended */ + jne 9f + + /* It is an extended partition. Read the extended partition and + try to scan it. If the scan returns, re-load the current + partition table and resume scan. */ +8: + movl 8(%si), %eax /* Partition table offset */ + addl %edx, %eax /* Compute location of new ptab */ + andl %edx, %edx /* Is this the MBR? */ + jnz 10f + movl %eax, %edx /* Offset -> root if this was MBR */ +10: + call read_partition_table + jc 11f + call scan_partition_table +11: + /* This returned, so we need to reload the current partition table */ + movl 28(%bp), %eax /* "Base" */ + call read_partition_table + + /* fall through */ +9: + /* Not an extended partition */ + andl %edx, %edx /* Are we inside an extended part? */ + jz 12f + /* If so, this is a logical partition */ + decb (partition) + je boot +12: + addw $16, %si + loopw 7b + + /* Nothing found, return */ + popal + ret + +/* + * boot: invoke the actual bootstrap. (%si) points to the partition + * table entry, and 28(%bp) has the partition table base. + */ +boot: + cmpb $0, 4(%si) + je missing_os + movl 8(%si), %eax + addl 28(%bp), %eax + movl %eax, 8(%si) /* Adjust in-memory partition table entry */ + call read_sector + jc disk_error + cmpw $0xaa55, (bootsec+510) + jne missing_os /* Not a valid boot sector */ + movw $driveno, %sp /* driveno == bootsec-6 */ + popw %dx /* dl -> drive number */ + popw %di /* es:di -> $PnP vector */ + popw %es + cli + jmpw *%sp /* %sp == bootsec */ + +disk_error: + call error + .ascii "Operating system load error.\r\n" + +/* + * Print error messages. This is invoked with "call", with the + * error message at the return address. + */ +error: + popw %si +2: + lodsb + movb $0x0e, %ah + movb (BIOS_page), %bh + movb $0x07, %bl + int $0x10 /* May destroy %bp */ + cmpb $10, %al /* Newline? */ + jne 2b + + int $0x18 /* Boot failure */ +die: + hlt + jmp die + +/* Location of the partition configuration byte */ +partition = _start + 439 diff --git a/mbr/altmbr.bin b/mbr/altmbr.bin new file mode 100644 index 0000000000000000000000000000000000000000..826a92bf00794a899787e19ce609a3f710965a87 GIT binary patch literal 439 zcmexmc%bh_--SI4HJ#7c!ut;V{e-s4X04g_|GR2l8(_=J5StP(YgDfwY+~(imO{F#2W| z7iZ?BE94iX7A2N|SjCmaC8@c3yj*DsX@-|}Gt{I7r3J7BXEA08bUsV_ZuX`6z!SEP zhaE>JeKY$}-EoZLq2mrlrY^Bx)n|o!Qv}iy55G9_f8V~^{rlK=DlmLrdN?hi^XWby zhlL?c@lq*&Sl~at?d&C<&4&dZeT|Ika$!+8P-1MuU&5wRZ^K$Dm)7mVk;Zrdq?S3Y z^U#Y8|BIMk?E0V9?I!bL$$y}52^-h|j^<+mj}8K@Yp7vi;BPSkssL(ac9Ti#bm4g6 z_#YS)cK>QaSMj%s|KIbgIx3$1*Z(IkpO~VMT2z!@1oU*=nk{@?c1bLo U+0P2taFu>MEAi#^Uj{M&0CwrE*#H0l literal 0 HcmV?d00001 diff --git a/mbr/altmbr_c.bin b/mbr/altmbr_c.bin new file mode 100644 index 0000000000000000000000000000000000000000..0a3be8e020f1fe466d244f161408582bf8e72885 GIT binary patch literal 439 zcmexmc%bh_--SI4HJ#7c!ut;V{v z+IO=r)d!xibv*1iI_aC)kLr$N91k6LFfw(C{i;4I+?yhhmU#HZk^lSl)$ZTNzEgqW z^U}j<37t>(0XZxTX^NLh`NIPL@oi@>@oYXU@aSt~T$c-r!hsTF8~zeDm3kZ2Qn|El z7mhT>3m~=3X`P2&Z1`Wq{9@Ptv~D+<7fb#Fg-h7L25>YV6L@qGXk9}M3j=?P5l{tC zBeR=KTBi%g3&;P!ps@Q_8@h_WRs8>+U)53Z?7#j$c_GK(kLFW_oczQTh18;={34*I d#|E?+01@cz=o^z<5`I>um3U-4**J>u%!S1 literal 0 HcmV?d00001 diff --git a/mbr/altmbr_f.bin b/mbr/altmbr_f.bin new file mode 100644 index 0000000000000000000000000000000000000000..e7178c46404369465ee5c13031fcdbc3e20434f8 GIT binary patch literal 439 zcmexmc%bh_--SI4HJ#7c!ut;V{BdIL-<;KeP|z-*(L5`@^7sG{XaFGhd`JybxgY z%`7g?%u83uFGwv)ECI2KD~n4~bM<(+(h|}PFYRWiNefB~U<=M-%o6B)miFE3OZ9;# zY#k3fj!yb!_M^Jv7{^1$9gIv}V!x`-3iqZ6q$M7HapeELeYN}dvF}u1_`LLRT0-a3 zeLxNiLz?2HQvR^Oe|+27OFWwo3q1N78Q0~)qHv(Z*oMD^O{Lz3wNx&x+l3>I@d8LK zb6V%27aRT;F~8XLKdswM=Eai#K;aTLumK#+#{?c71X|Zn!@|JdVgytH)X3~6lh*0N z@xt*xFevQ))rPL(Zx#Q)=T~)9Jo~TzPhQ9|_@nt$AtygEMIp7QD8C5k>9{pp_`2+p WST?ht6|mtd{diX5%j>@kBm)2=rmqbE literal 0 HcmV?d00001 diff --git a/mbr/checksize.pl b/mbr/checksize.pl new file mode 100755 index 0000000..c1984db --- /dev/null +++ b/mbr/checksize.pl @@ -0,0 +1,54 @@ +## ----------------------------------------------------------------------- +## +## Copyright 2007-2009 H. Peter Anvin - All Rights Reserved +## Copyright 2009 Intel Corporation; author: H. Peter Anvin +## +## 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +## +## checksize.pl +## +## Check the size of a binary file and pad it with zeroes to that size +## + +use bytes; + +($file, $maxsize, $padsize) = @ARGV; + +if (!defined($maxsize)) { + # Defaults based on the filename + if ($file =~ /^mbr[^0-9a-z]/) { + $maxsize = $padsize = 440; + } elsif ($file =~ /^gptmbr[^0-9a-z]/) { + $maxsize = $padsize = 424; + } elsif ($file =~ /^isohdp[fp]x[^0-9a-z]/) { + $maxsize = $padsize = 432; + } elsif ($file =~ /^altmbr[^0-9a-z]/) { + $maxsize = $padsize = 439; + } else { + die "$0: no default size for filename: $file\n"; + } +} + +$padsize = $maxsize unless(defined($padsize)); + +open(FILE, '+<', $file) or die; +@st = stat(FILE); +if (!defined($size = $st[7])) { + die "$0: $file: $!\n"; +} +if ($size > $maxsize) { + print STDERR "$file: too big ($size > $maxsize)\n"; + exit 1; +} elsif ($size < $padsize) { + seek(FILE, $size, 0); + print FILE "\0" x ($padsize-$size); +} + +exit 0; diff --git a/mbr/gptmbr.S b/mbr/gptmbr.S new file mode 100644 index 0000000..8d42e8b --- /dev/null +++ b/mbr/gptmbr.S @@ -0,0 +1,294 @@ +/* ----------------------------------------------------------------------- + * + * Copyright 2007-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#include "adjust.h" + + .code16 + .text + + .globl bootsec +stack = 0x7c00 + +/* Partition table header here */ +phdr = stack /* Above the stack, overwritten by bootsect */ +/* Partition table sector here */ +/* To handle > 32K we need to play segment tricks... */ +psec = _phdr + 512 + +/* BootGUID */ +bootguid = _start + 0x1a8 +/* Where we put DS:SI */ +dssi_out = _start + 0x1be + +BIOS_kbdflags = 0x417 +BIOS_page = 0x462 + + /* gas/ld has issues with doing this as absolute addresses... */ + .section ".bootsec", "a", @nobits + .globl bootsec +bootsec: + .space 512 + + .text + .globl _start +_start: + cli + xorw %ax, %ax + movw %ax, %ds + movw %ax, %ss + movw $stack, %sp + movw %sp, %si + pushw %es /* 4(%bp) es:di -> $PnP header */ + pushw %di /* 2(%bp) */ + movw %ax, %es + sti + cld + + /* Copy down to 0:0x600 */ + movw $_start, %di + movw $(512/2), %cx + rep; movsw + + ljmpw $0, $next +next: + + ADJUST_DRIVE + pushw %dx /* 0(%bp) = %dl -> drive number */ + + /* Check to see if we have EBIOS */ + pushw %dx /* drive number */ + movb $0x41, %ah /* %al == 0 already */ + movw $0x55aa, %bx + xorw %cx, %cx + xorb %dh, %dh + stc + int $0x13 + jc 1f + cmpw $0xaa55, %bx + jne 1f + shrw %cx /* Bit 0 = fixed disk subset */ + jnc 1f + + /* We have EBIOS; patch in the following code at + read_sector_cbios: movb $0x42, %ah ; jmp read_common */ + movl $0xeb42b4+((read_common-read_sector_cbios-4) << 24), \ + (read_sector_cbios) + +1: + popw %dx + + /* Get (C)HS geometry */ + movb $0x08, %ah + int $0x13 + andw $0x3f, %cx /* Sector count */ + movw %sp, %bp /* %bp -> frame pointer: LEAVE UNCHANGED */ + pushw %cx /* -2(%bp) Save sectors on the stack */ + movzbw %dh, %ax /* dh = max head */ + incw %ax /* From 0-based max to count */ + mulw %cx /* Heads*sectors -> sectors per cylinder */ + + /* Save sectors/cylinder on the stack */ + pushw %dx /* -4(%bp) High word */ + pushw %ax /* -6(%bp) Low word */ + + /* Load partition table header */ + xorl %eax,%eax + cltd + incw %ax /* %edx:%eax = 1 */ + movw $phdr, %bx + pushw %bx /* -8(%bp) phdr == bootsect */ + call read_sector + + /* Number of partition sectors */ + /* We assume the partition table is 32K or less, and that + the sector size is 512. */ + /* Note: phdr == 6(%bp) */ + movw (80+6)(%bp),%cx /* NumberOfPartitionEntries */ + movw (84+6)(%bp),%ax /* SizeOfPartitionEntry */ + pushw %ax + pushw %cx + mulw %cx + shrw $9,%ax + xchgw %ax,%cx + incw %cx + + /* Starting LBA of partition array */ + movl (72+6)(%bp),%eax + movl (76+6)(%bp),%edx + + pushw %bx +get_ptab: + call read_sector + call inc64 + loopw get_ptab + + /* Find the boot partition */ + popw %si /* Partition table in memory */ + popw %cx /* NumberOfPartitionEntries */ + popw %ax /* SizeOfPartitionEntry */ +find_part: + pushw %cx + pushw %si + addw $16,%si + movw $bootguid,%di + movw $8,%cx + repe; cmpsw + popw %si + popw %cx + je found_part + addw %ax,%si + loopw find_part + + call error + .ascii "Boot partition not found\r\n" + +found_part: + xchgw %ax,%cx /* Set up %cx for rep movsb further down */ + + movw $dssi_out,%di + pushw %di + + /* 80 00 00 00 ee 00 00 00 + - bootable partition, type EFI (EE), no CHS information */ + xorl %eax,%eax + movb $0x80,%al + stosl + movb $0xee,%al + stosl + movl 32(%si),%eax + movl 36(%si),%edx + call saturate_stosl /* Partition start */ + + movl 40(%si),%eax + movl 48(%si),%edx + subl 32(%si),%eax + sbbl 36(%si),%edx + call inc64 + call saturate_stosl /* Partition length */ + + rep; movsb /* GPT entry follows MBR entry */ + popw %si + +/* + * boot: invoke the actual bootstrap. %ds:%si points to the + * partition information in memory. The top word on the stack + * is phdr == 0x7c00 == the address of the boot sector. + */ +boot: + movl (32+16)(%si),%eax + movl (36+16)(%si),%edx + popw %bx + call read_sector + cmpw $0xaa55, -2(%bx) + jne missing_os /* Not a valid boot sector */ + movw %bp, %sp /* driveno == bootsec-6 */ + popw %dx /* dl -> drive number */ + popw %di /* es:di -> $PnP vector */ + popw %es + movl $0x54504721,%eax /* !GPT magic number */ + cli + jmpw *%sp /* %sp == bootsec */ + +missing_os: + call error + .ascii "OS not bootable\r\n" + +saturate_stosl: + pushl %eax + andl %edx,%edx + jz 1f + orl $-1,%eax +1: stosl + popl %eax + ret + +inc64: + addl $1,%eax + adcl $0,%edx + ret + +/* + * read_sector: read a single sector pointed to by %edx:%eax to + * %es:%bx. CF is set on error. All registers saved. + */ +read_sector: + pushal + pushl %edx /* MSW of LBA */ + pushl %eax /* LSW of LBA */ + pushw %es /* Buffer segment */ + pushw %bx /* Buffer offset */ + pushw $1 /* Sector count */ + pushw $16 /* Size of packet */ + movw %sp, %si + + /* This chunk is skipped if we have ebios */ + /* Do not clobber %es:%bx or %edx:%eax before this chunk! */ +read_sector_cbios: + divl -6(%bp) /* secpercyl */ + shlb $6, %ah + movb %ah, %cl + movb %al, %ch + xchgw %dx, %ax + divb -2(%bp) /* sectors */ + movb %al, %dh + orb %ah, %cl + incw %cx /* Sectors are 1-based */ + movw $0x0201, %ax + +read_common: + movb (%bp), %dl /* driveno */ + int $0x13 + leaw 16(%si), %sp /* Drop DAPA */ + popal + jc disk_error + addb $2, %bh /* bx += 512: point to the next buffer */ + ret + +disk_error: + call error + .ascii "Disk error on boot\r\n" + +/* + * Print error messages. This is invoked with "call", with the + * error message at the return address. + */ +error: + popw %si +2: + lodsb + movb $0x0e, %ah + movb (BIOS_page), %bh + movb $0x07, %bl + int $0x10 /* May destroy %bp */ + cmpb $10, %al /* Newline? */ + jne 2b + + int $0x18 /* Boot failure */ +die: + hlt + jmp die diff --git a/mbr/gptmbr.bin b/mbr/gptmbr.bin new file mode 100644 index 0000000000000000000000000000000000000000..0336cf11fce0e0536e342403953f49ea6c06253a GIT binary patch literal 424 zcmexmc%bh_--SI4HJ#7c!ut;V{f2s4X04g_|GRcRmf|-*(L5`@^7sG{XaFGaYs_)C9k{ z&d}`_*6kJ*5cvJ!!55qp9n-qq{L;F^f`VV{VR*5D;n9z{$cVtO=3@f;SFrEoVEDW| zF0w?C@z|r+FJ3S><>!|u6eJdvWR_&+=PBd?d1?8jc`3YH6Zh|94+mPap&@PchIeVJ z)4E+0(z-)bUidKpnHoT*L7FyDNE#^Q&G5p4;q#I>po9TX!X)~I6GLPDKcGkWI^RUa zv#0G)bPovm_5aC>G6w%(uq{bIixZP_QhB-30@4&Om9V5WpZE{7CE{>e^8v=R=1UBR z(-P8x(gN6mvlz1kI-jL|FZ*@i30ud*j-!*lmHq2D#_`Z`2P0Eg7{giN-V}kf#3Gi4 z<4lKNh%mTh7H2D@78T_eDFD3$vWS-}Zp{|HF1sX_&Fp6dY`984o|X9W`Y!_t002NM Bws`;m literal 0 HcmV?d00001 diff --git a/mbr/gptmbr_c.bin b/mbr/gptmbr_c.bin new file mode 100644 index 0000000000000000000000000000000000000000..fd4eecee10761fcd5c4a8dfce858ee0e21e9b609 GIT binary patch literal 424 zcmexmc%bh_--SI4HJ#7c!ut;V{7`oE2_|epYlY`;&^0>$nMaE-~UcY$3;FO~2;(!tcKnauR7fuX~ z_5Xk#;p==870;fwL(x4T1)0!_a98OC}3rY)M3(jK966k!E_Py-afhTMo4?B)d`d0R@;~2+7#~qAJU11Do zg?m#3(h`eU8jdp^ej&o(l3ARskXlrfU!(x^638N6uDCT@_`2+pST?ht6|mtd{diX5 K%j>@kAOHZIJGn&w literal 0 HcmV?d00001 diff --git a/mbr/gptmbr_f.bin b/mbr/gptmbr_f.bin new file mode 100644 index 0000000000000000000000000000000000000000..991847aa318bc8dd20ed665d9d093f7e30c3bab1 GIT binary patch literal 424 zcmexmc%bh_--SI4HJ#7c!ut;V{=xo2hwIb>}IG5 zesP_l+b^u!Eh-@J`@@4TI43%$b-Vecb%zB7zu3d@VgtjYA90Zpfnm+Z1op3B-^s!7 zd3ju9i6Y~%N3UPJU~tOMFHtB+EGo$?$;{7F$OH1y@=Nnlc)2F--^U&fv}Qv?+UgDO z(pIN+yC|e}hp4>pV*oNWfJ}olZJ>}eP{^C%g$2XsC2>Fr1E7RS^b043#`=FikMMQA ziHc`W+o9+l5c2E)lNV(S{=r~dl7JQ`Cgr5^a-{{NDPAgJNozjwA81R&;k4!hjA_l6 z7!Ic;qy?n~umxu^W(jmYOZ#5->%bGXj)xsbCw(jX*Kv&Fq2mrlrmirCv%$}ds?dI@9^FIU`}Eqq;eNi3V$&kERZm3}-c@#Xbj1|$Fg D$&I(I literal 0 HcmV?d00001 diff --git a/mbr/isohdpfx.S b/mbr/isohdpfx.S new file mode 100644 index 0000000..801115c --- /dev/null +++ b/mbr/isohdpfx.S @@ -0,0 +1,261 @@ +/* ----------------------------------------------------------------------- + * + * Copyright 2007-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * Modified MBR code used on an ISO image in hybrid mode. + * + * This doesn't follow the El Torito spec at all -- it is just a stub + * loader of a hard-coded offset, but that's good enough to load + * ISOLINUX. + */ + +#include "adjust.h" + + .code16 + .text + +HYBRID_MAGIC = 0x7078c0fb +isolinux_hybrid_signature = 0x7c00+64 +isolinux_start_hybrid = 0x7c00+64+4 + + .globl bootsec +/* Important: the top 6 words on the stack are passed to isolinux.bin */ +stack = 0x7c00 +partoffset = (stack-8) +driveno = (stack-14) +heads = (stack-16) +sectors = (stack-18) +ebios_flag = (stack-20) +secpercyl = (stack-24) + +BIOS_kbdflags = 0x417 +BIOS_page = 0x462 + + /* gas/ld has issues with doing this as absolute addresses... */ + .section ".bootsec", "a", @nobits + .globl bootsec +bootsec: + .space 512 + + .text + .globl _start +_start: + + cli + xorw %bp, %bp + movw %bp, %ss + movw $stack, %sp + sti + cld + + /* Check to see if we have a partition table entry */ + xorl %ebx, %ebx + xorl %ecx, %ecx +#ifdef PARTITION_SUPPORT + andw %si, %si /* %si == 0 -> no partition data */ + jz 1f + testb $0x7f, (%si) /* Invalid active flag field? */ + jnz 1f + cmpl $0x58504721, %eax /* !GPT signature in EAX? */ + jne 2f + cmpb $0xee, 4(%si) /* EFI partition type? */ + jne 2f + + /* We have GPT partition information */ + movl (32+16)(%si), %ecx + movl (36+16)(%si), %ebx + jmp 1f + + /* We have non-GPT partition information */ +2: + movl 8(%si), %ecx +#endif +1: + /* We have no partition information */ + pushl %ebx /* -4: partoffset_hi */ + pushl %ecx /* -8: partoffset_lo */ + pushw %es /* -10: es:di -> $PnP header */ + pushw %di /* -12: es:di -> $PnP header */ + + movw %bp, %ds + movw %bp, %es + + ADJUST_DRIVE + pushw %dx /* -14: dl -> drive number */ + + /* Copy down to 0:0x600 */ + movw $0x7c00, %si + movw $_start, %di + movw $(512/2), %cx + rep; movsw + + ljmpw $0, $next +next: + + /* Check to see if we have EBIOS */ + pushw %dx /* drive number */ + movb $0x41, %ah /* %al == 0 already */ + movw $0x55aa, %bx + xorw %cx, %cx + xorb %dh, %dh + stc + int $0x13 + jc 1f + cmpw $0xaa55, %bx + jne 1f + andw $1,%cx /* Bit 0 = fixed disk subset */ + jz 1f + + /* We have EBIOS; patch in the following code at + read_sector_cbios: movb $0x42, %ah ; jmp read_common */ + movl $0xeb42b4+((read_common-read_sector_cbios-4) << 24), \ + (read_sector_cbios) + jmp 1f +1: + popw %dx + pushw %cx /* EBIOS flag */ + + /* Get (C)HS geometry */ + movb $0x08, %ah + int $0x13 + andw $0x3f, %cx /* Sector count */ + popw %bx /* EBIOS flag */ + pushw %cx /* -16: Save sectors on the stack */ + movzbw %dh, %ax /* dh = max head */ + incw %ax /* From 0-based max to count */ + pushw %ax /* -18: Save heads on the stack */ + mulw %cx /* Heads*sectors -> sectors per cylinder */ + + pushw %bx /* -20: EBIOS flag */ + + /* Save sectors/cylinder on the stack */ + pushw %dx /* -22: High word */ + pushw %ax /* -24: Low word */ + + /* + * Load sectors. We do this one at a time mostly to avoid + * pitfalls and to share code with the stock MBR code. + */ + movw $0x7c00, %bx + movw $4, %cx /* Sector count */ + movl (lba_offset), %eax + +2: + call read_sector + jc disk_error + incl %eax + addb $(512 >> 8), %bh + loopw 2b + + /* + * Okay, that actually worked... update the stack pointer + * and jump into isolinux.bin... + */ + cmpl $HYBRID_MAGIC,(isolinux_hybrid_signature) + jne bad_signature + + cli + movw $ebios_flag, %sp + + /* + * Use a ljmpw here to work around a bug in some unknown version + * of gas or ld when it comes to jumping to an absolute symbol... + * + * Look more closely into it if we ever are short on space. + */ + ljmpw $0, $isolinux_start_hybrid + +bad_signature: + call error + .ascii "isolinux.bin missing or corrupt.\r\n" + +/* + * read_sector: read a single sector pointed to by %eax to %es:%bx. + * CF is set on error. All registers saved. + */ +read_sector: + pushal + xorl %edx, %edx + addl (partoffset), %eax + adcl (partoffset+4), %edx + pushl %edx /* MSW of LBA */ + pushl %eax /* LSW of LBA */ + pushw %es /* Buffer segment */ + pushw %bx /* Buffer offset */ + pushw $1 /* Sector count */ + pushw $16 /* Size of packet */ + movw %sp, %si + + /* This chunk is skipped if we have ebios */ + /* Do not clobber %eax before this chunk! */ + /* This also relies on %bx and %edx as set up above. */ +read_sector_cbios: + divl (secpercyl) + shlb $6, %ah + movb %ah, %cl + movb %al, %ch + xchgw %dx, %ax + divb (sectors) + movb %al, %dh + orb %ah, %cl + incw %cx /* Sectors are 1-based */ + movw $0x0201, %ax + +read_common: + movb (driveno), %dl + int $0x13 + leaw 16(%si), %sp /* Drop DAPA */ + popal + ret + +disk_error: + call error + .ascii "Operating system load error.\r\n" + +/* + * Print error messages. This is invoked with "call", with the + * error message at the return address. + */ +error: + popw %si +2: + lodsb + movb $0x0e, %ah + movb (BIOS_page), %bh + movb $0x07, %bl + int $0x10 /* May destroy %bp */ + cmpb $10, %al /* Newline? */ + jne 2b + + int $0x18 /* Boot failure */ +die: + hlt + jmp die + + /* Address of pointer to isolinux.bin */ +lba_offset = _start+432 diff --git a/mbr/isohdpfx.bin b/mbr/isohdpfx.bin new file mode 100644 index 0000000000000000000000000000000000000000..1ad3973ce8d0756a340ed5fd37819b4d6ded79fd GIT binary patch literal 432 zcmexm__pur9)_CVf6@$Zrx~713r-7U3-7zzcQj}pL(P5$ww(-&pO?PUW@BIo+Tyr- zRjA=fgKt023Kxkr{tjJLD$x9pv4lJAINL?GEl#gRUo%7nZs9m9-2Bi!I*@s14Eia!*QlZpVAub9BO_as3<7q{I%yz^(&Ve28I{S z44K9GIhlE-6?#dTc?!9i#l@L<=?eKp3d#9JMWqELdc0g|32BCx(wNzPRHq4x{i#k1 zN(*2M&SK0G=zNy;-Rwp6fhTMo4?B)d`eyd7y5ktfL&qJAOkHB1s?Q4drU;}Z9)2Om z;9rnhlvn~Xxwx{pBsEtdCqFSoA+@L|zX)hS+?p+XU3N(G`=A|p-7bzs?7ZsHjl<4ttr6r^pUP@zT z`%#@HEcT~5EhsI3EjWuYOQ7>v+IO=T)d!xibv*1iI_aC)yXuZ(91k6LFfw(CeX2ey z+?yhhmU#Gu9D{#BYEfbd$mHV6;*!){g`E7v6ou5HqWmJD1#xS(@O9ZGv212PD`3M_ P`thv9m)Cz81`YrKjAVt} literal 0 HcmV?d00001 diff --git a/mbr/isohdpfx_f.bin b/mbr/isohdpfx_f.bin new file mode 100644 index 0000000000000000000000000000000000000000..c0b6291082a3be068faa70f49e2ff00c12ff5378 GIT binary patch literal 432 zcmexm__pur9)_CVf6@$Zrx~713r-7U3-7zzcXU%j&_0Hm{S0h785ln=eWlCBz!0>> zardfF!;=Qzex4OB5^MY&x~f#5`5|KociM5b%WPYmUW>kFhzi`oaaOqbp?!29|F&Ze z0pA}62L3-cOpiXLHQG7U{60`oP|Eph&ztI3E;S4c zFPa%Li}Q0b^GYl9k}~rYax;sIGxO3F@{1Ib^NWf~3rh5OxzZBS3@@cIv;C+}6BheZ zofec9z!sdvm?hBpEbY75i|PYU*g76|9G&#d>|J%oF^-3hI~bX|#6DG@74A(DNJ~8Y zLXN?|Ahjs51Y~k?WpPPru0l?JVv0g)QBi&o(1N%%Tll){l2|sgpB1p-D*bp?;>+v5 H3 zE)4ul4Gd`x4ab=teM)PzbEx@!prW9Z^Vgm?)vsJ?7#LnOGh`O$=Va!UR_G;V<|*W6 z78hscr7PqYDJ16?6_pm0=<#x;C8QZ%N@HgGQJp3%_NO{6C@p|3IEyh$pz~SUce5AO z2cEEXJnT3+>6_WR>W*U^4;^@@SmW=|Riy&W4;f3i(~h(KXWQcRTJ$wTRNxklv%<{}?V|(vw;gi``2H|BC}1~3 z%}y4Ew1pekU$`*vH#IP%IW!z+dh{u+(axdf_koInQqEs{-c-MGsbOGv(aexpoS&1K zS6ZQ$l$ocHn^|0(nU}7RU!;(nUsP0DP@>1nm6niZcqxsU?MHQ*u-KpKw4k&Aw%{zr zEP>8vY2VFWR3CW4*7308=%jCE@2WeFaXfU~!N}Al_Nn@;aBqr0TH@gsat!_jsYQt; zAd`zLi%U{-6>{wq7R0UD!q;V&#Il+Ftbh$y>BqAYUta%Zpa}o~@imj9 literal 0 HcmV?d00001 diff --git a/mbr/isohdppx_f.bin b/mbr/isohdppx_f.bin new file mode 100644 index 0000000000000000000000000000000000000000..df17cf994661fbba0a0f731dbf1017997f8b77dc GIT binary patch literal 432 zcmexm__pur9)_CVf6@$Zrx~79{8pm)jitU+Ce2pSJs_e~prMB4T`5;uw~s+ucZ|tv z79fiwEjTTZExhk;-_cDCLHihL_A{{UWMKTf^i>cW14Gaj$K9(!4Nn?;`*~KlNUZU9 z=&DkI=7)?W+-b+zez0wEdM)~zAu4bS$64X#hxXBd{M(K>1blxO92Bsdp=KuwL)yX( z>@Qpx_?sFS(i|F&Gd=p0)@bKY^ZP(WK`H02J#VUCxzsQ)yl7^~EY8o#%qy+XOUles z$jvM+&df_!$S+bz&Mzt|Ehy3B%=V)?O<3$tby`qb09$YtW0pYYv$XGK zFRBkbVe5F $PnP header */ + pushw %di + movw %ax, %es + sti + cld + + /* Copy down to 0:0x600 */ + movw $_start, %di + movw $(512/2), %cx + rep; movsw + + ljmpw $0, $next +next: + + ADJUST_DRIVE + pushw %dx /* dl -> drive number */ + + /* Check to see if we have EBIOS */ + pushw %dx /* drive number */ + movb $0x41, %ah /* %al == 0 already */ + movw $0x55aa, %bx + xorw %cx, %cx + xorb %dh, %dh + stc + int $0x13 + jc 1f + cmpw $0xaa55, %bx + jne 1f + shrw %cx /* Bit 0 = fixed disk subset */ + jnc 1f + + /* We have EBIOS; patch in the following code at + read_sector_cbios: movb $0x42, %ah ; jmp read_common */ + movl $0xeb42b4+((read_common-read_sector_cbios-4) << 24), \ + (read_sector_cbios) + +1: + popw %dx + + /* Get (C)HS geometry */ + movb $0x08, %ah + int $0x13 + andw $0x3f, %cx /* Sector count */ + pushw %cx /* Save sectors on the stack */ + movzbw %dh, %ax /* dh = max head */ + incw %ax /* From 0-based max to count */ + mulw %cx /* Heads*sectors -> sectors per cylinder */ + + /* Save sectors/cylinder on the stack */ + pushw %dx /* High word */ + pushw %ax /* Low word */ + + xorl %eax, %eax /* Base */ + cdq /* Root (%edx <- 0) */ + call scan_partition_table + + /* If we get here, we have no OS */ +missing_os: + call error + .ascii "Missing operating system.\r\n" + +/* + * read_sector: read a single sector pointed to by %eax to 0x7c00. + * CF is set on error. All registers saved. + */ +read_sector: + pushal + xorl %edx, %edx + movw $bootsec, %bx + pushl %edx /* MSW of LBA */ + pushl %eax /* LSW of LBA */ + pushw %es /* Buffer segment */ + pushw %bx /* Buffer offset */ + pushw $1 /* Sector count */ + pushw $16 /* Size of packet */ + movw %sp, %si + + /* This chunk is skipped if we have ebios */ + /* Do not clobber %eax before this chunk! */ + /* This also relies on %bx and %edx as set up above. */ +read_sector_cbios: + divl (secpercyl) + shlb $6, %ah + movb %ah, %cl + movb %al, %ch + xchgw %dx, %ax + divb (sectors) + movb %al, %dh + orb %ah, %cl + incw %cx /* Sectors are 1-based */ + movw $0x0201, %ax + +read_common: + movb (driveno), %dl + int $0x13 + leaw 16(%si), %sp /* Drop DAPA */ + popal + ret + +/* + * read_partition_table: + * Read a partition table (pointed to by %eax), and copy + * the partition table into the ptab buffer. + * + * Clobbers %si, %di, and %cx, other registers preserved. + * %cx = 0 on exit. + * + * On error, CF is set and ptab is overwritten with junk. + */ +ptab = _start+446 + +read_partition_table: + call read_sector + movw $bootsec+446, %si + movw $ptab, %di + movw $(16*4/2), %cx + rep ; movsw + ret + +/* + * scan_partition_table: + * Scan a partition table currently loaded in the partition table + * area. Preserve all registers. + * + * On entry: + * %eax - base (location of this partition table) + * %edx - root (offset from MBR, or 0 for MBR) + * + * These get pushed into stack slots: + * 28(%bp) - %eax - base + * 20(%bp) - %edx - root + */ + +scan_partition_table: + pushal + movw %sp, %bp + + /* Search for active partitions */ + movw $ptab, %bx + movw $4, %cx + xorw %ax, %ax + push %bx + push %cx +5: + testb $0x80, (%bx) + jz 6f + incw %ax + movw %bx, %si +6: + addw $16, %bx + loopw 5b + + decw %ax /* Number of active partitions found */ + jz boot + jns too_many_active + + /* No active partitions found, look for extended partitions */ + popw %cx /* %cx <- 4 */ + popw %bx /* %bx <- ptab */ +7: + movb 4(%bx), %al + cmpb $0x0f, %al /* 0x0f = Win9x extended */ + je 8f + andb $~0x80, %al /* 0x85 = Linux extended */ + cmpb $0x05, %al /* 0x05 = MS-DOS extended */ + jne 9f + + /* It is an extended partition. Read the extended partition and + try to scan it. If the scan returns, re-load the current + partition table and resume scan. */ +8: + movl 8(%bx), %eax /* Partition table offset */ + movl 20(%bp), %edx /* "Root" */ + addl %edx, %eax /* Compute location of new ptab */ + andl %edx, %edx /* Is this the MBR? */ + jnz 10f + movl %eax, %edx /* Offset -> root if this was MBR */ +10: + call read_partition_table + jc 11f + call scan_partition_table +11: + /* This returned, so we need to reload the current partition table */ + movl 28(%bp), %eax /* "Base" */ + call read_partition_table + + /* fall through */ +9: + /* Not an extended partition */ + addw $16, %bx + loopw 7b + + /* Nothing found, return */ + popal + ret + +too_many_active: + call error + .ascii "Multiple active partitions.\r\n" + +/* + * boot: invoke the actual bootstrap. (%si) points to the partition + * table entry, and 28(%bp) has the partition table base. + */ +boot: + movl 8(%si), %eax + addl 28(%bp), %eax + movl %eax, 8(%si) /* Adjust in-memory partition table entry */ + call read_sector + jc disk_error + cmpw $0xaa55, (bootsec+510) + jne missing_os /* Not a valid boot sector */ + movw $driveno, %sp /* driveno == bootsec-6 */ + popw %dx /* dl -> drive number */ + popw %di /* es:di -> $PnP vector */ + popw %es + cli + jmpw *%sp /* %sp == bootsec */ + +disk_error: + call error + .ascii "Operating system load error.\r\n" + +/* + * Print error messages. This is invoked with "call", with the + * error message at the return address. + */ +error: + popw %si +2: + lodsb + movb $0x0e, %ah + movb (BIOS_page), %bh + movb $0x07, %bl + int $0x10 /* May destroy %bp */ + cmpb $10, %al /* Newline? */ + jne 2b + + int $0x18 /* Boot failure */ +die: + hlt + jmp die diff --git a/mbr/mbr.bin b/mbr/mbr.bin new file mode 100644 index 0000000000000000000000000000000000000000..8592b5987d2b2653d602516a67adbb3de95bcf89 GIT binary patch literal 440 zcmexmc%bh_--SI4HJ#7c!ut;V{e-s4X04g_|GR2l8(_=J5StP(YgDfwY+~(imPSGWuo~ z7iZ?BE94iX7A2N|SjCmaC8@c3yj*DsX@-|}Gt{I7r3J7BXEA08bUsV_ZuX`6z!SEP zhaE>JeKY$}-EoZLq2mrlrY^Bx)n|o!Qv}iy55G9_f8V~^{rlK=DlmLrdN?hi^XYCN zhlRoLKyctU_J$H>ht7M=hXo#e_9%(2w2X}Ia%ZvOFJV)uw_z<+O6zv#Nb3$0Nn^Z_ zrg*88Ij!^1i#7j?m|txBpVsXr^J2k&kk&IGdy*J@OLIyx3vyBw5|c|Z%Tg5z5{pVQ zOEUBGia{RecHu~4c9Ti#bm4em@E;g9cK>QaSMj&9{onJeIx3$1*Z(Ik}LgRxJo~smH6`dF9Q((09=%`j{pDw literal 0 HcmV?d00001 diff --git a/mbr/mbr.ld b/mbr/mbr.ld new file mode 100644 index 0000000..d14ba80 --- /dev/null +++ b/mbr/mbr.ld @@ -0,0 +1,73 @@ +/* + * Linker script for MBR + */ + +/* Script for -z combreloc: combine and sort reloc sections */ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", + "elf32-i386") +OUTPUT_ARCH(i386) +EXTERN(_start) +ENTRY(_start) +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = 0x600; + .text : + { + *(.text*) + *(.rodata*) + } =0x90909090 + + . = ALIGN(4); + .data : + { + *(.data*) + } + + . = ALIGN(128); + .bss : + { + *(.bss*) + } + + . = 0x7c00; + .bootsec : + { + *(.bootsec) + } + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /DISCARD/ : { *(.note.GNU-stack) } +} diff --git a/mbr/mbr_c.bin b/mbr/mbr_c.bin new file mode 100644 index 0000000000000000000000000000000000000000..951787617e8f494c817c9c6a835a93c7775b7d1f GIT binary patch literal 440 zcmexmc%bh_--SI4HJ#7c!ut;V{v z+IO=r)d!xibv*1iI_aC)kLr$N91k6LFfw(C{i;4I+?yhhmU#HZk^lSl)$ZTNzEgqW z^U}j<37t=O134@Vh6jQJzp*!zFgtYKYd$RS=(9&jbfslvbeB7e4SxxnO1%whsZv_E zJ4ae~m`EDqg*3%WrOauahhD7tU&Q=k+yAs~H<=d;{)4oh0ojwp;9Htgl39?Gs*spm zl3A9jP>@(ul39|OpH~d>K(`A=8nc^BTBi%g3xof_u(A7B8@h_WmF@qYU)53Z?7#j$ xc_GK(kLGoSoczQTh18;={34(sacj2lb=f7cY-T?zV8d1V@vOv`*MAvs0|54(w_5-J literal 0 HcmV?d00001 diff --git a/mbr/mbr_f.bin b/mbr/mbr_f.bin new file mode 100644 index 0000000000000000000000000000000000000000..f669ee75cedaf5828828cf293a9e624208fa52e7 GIT binary patch literal 440 zcmexmc%bh_--SI4HJ#7c!ut;V{BdIL-<;KeP|z-*(L5`@^7sG{XaFGhd`JyijEH z%`7g?%u83uFGwv)ECI2KD~n4~bM<(+(h|}PFYRWiNefB~U<=M-%o6B)miFE3OZ9;# zY#k3fj!yb!_M^Jv7{^1$9gIv}V!x`-3iqZ6q$M7HapeELeYN}dvF}u1_`LLRT0-a3 z-9QctgW-YTz;EmgCCm<;_nHq2Jo@ZW5?yH-8Qta1V#8m;rc!UiTB?-R?aqZuDikCZ zm1LG==I0fIJkagJk;d#Mlh*0N@xtIgFl_Ap)rPL(Z)N+x=T~)9Jo~TzPhQ9|_@jAU qAtygEMIp7QD8C43NZgt&d|h@)ESuTS3fOR!empDj<@H|%f&c*h3bn%k literal 0 HcmV?d00001 diff --git a/mbr/oldmbr.asm b/mbr/oldmbr.asm new file mode 100644 index 0000000..26fb022 --- /dev/null +++ b/mbr/oldmbr.asm @@ -0,0 +1,230 @@ +; ----------------------------------------------------------------------- +; +; Copyright 2003-2008 H. Peter Anvin - All Rights Reserved +; +; Permission is hereby granted, free of charge, to any person +; obtaining a copy of this software and associated documentation +; files (the "Software"), to deal in the Software without +; restriction, including without limitation the rights to use, +; copy, modify, merge, publish, distribute, sublicense, and/or +; sell copies of the Software, and to permit persons to whom +; the Software is furnished to do so, subject to the following +; conditions: +; +; The above copyright notice and this permission notice shall +; be included in all copies or substantial portions of the Software. +; +; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +; OTHER DEALINGS IN THE SOFTWARE. +; +; ----------------------------------------------------------------------- + +; +; mbr.asm +; +; Simple Master Boot Record, including support for EBIOS extensions. +; +; The MBR lives in front of the boot sector, and is responsible for +; loading the boot sector of the active partition. The EBIOS support +; is needed if the active partition starts beyond cylinder 1024. +; +; This MBR determines all geometry info at runtime. It uses only the +; linear block field in the partition table. It does, however, pass +; the partition table information unchanged to the target OS. +; +; This MBR should be "8086-clean", i.e. not require a 386. +; + +%include "bios.inc" + +; +; Note: The MBR is actually loaded at 0:7C00h, but we quickly move it down to +; 0600h. +; + section .text + cpu 8086 + org 0600h + +_start: cli + xor ax,ax + mov ds,ax + mov es,ax + mov ss,ax + mov sp,7C00h + sti + cld + mov si,sp ; Start address + mov di,0600h ; Destination address + mov cx,512/2 + rep movsw + +; +; Now, jump to the copy at 0600h so we can load the boot sector at 7C00h. +; Since some BIOSes seem to think 0000:7C00h and 07C0:0000h are the same +; thing, use a far jump to canonicalize the address. This also makes +; sure that it is a code speculation barrier. +; + + jmp 0:next ; Jump to copy at 0600h + +next: + mov [DriveNo], dl ; Drive number stored in DL +; +; Check for CHS parameters. This doesn't work on floppy disks, +; but for an MBR we don't care. +; + mov ah,08h ; Get drive parameters + int 13h + and cx,3Fh ; Max sector number + mov [Sectors],cx + xor ax,ax + mov al,dh + inc ax ; From 0-based to count + mul cx ; Heads*Sectors + mov [SecPerCyl],ax + ; Note: we actually don't care about the number of + ; cylinders, since that's the highest-order division + +; +; Now look for one (and only one) active partition. +; + mov si,PartitionTable + xor ax,ax + mov cx,4 +checkpartloop: + test byte [si],80h + jz .notactive + inc ax + mov di,si +.notactive: add si,byte 16 + loop checkpartloop + + cmp ax,byte 1 ; Better be only one + jnz not_one_partition + +; +; Now we have the active partition partition information in DS:DI. +; Check to see if we support EBIOS. +; + mov dl,[DriveNo] + mov ax,4100h + mov bx,055AAh + xor cx,cx + xor dh,dh + stc + int 13h + jc no_ebios + cmp bx,0AA55h + jne no_ebios + test cl,1 ; LBA device access + jz no_ebios +; +; We have EBIOS. Load the boot sector using LBA. +; + push di + mov si,dapa + mov bx,[di+8] ; Copy the block address + mov [si+8],bx + mov bx,[di+10] + mov [si+10],bx + mov dl,[DriveNo] + mov ah,42h ; Extended Read + jmp short common_tail +; +; No EBIOS. Load the boot sector using CHS. +; +no_ebios: + push di + mov ax,[di+8] + mov dx,[di+10] + div word [SecPerCyl] ; AX = cylinder DX = sec in cyl + ror ah,1 + ror ah,1 + mov cl,ah + mov ch,al ; CL = cyl[9:8], CH = cyl[7:0] + + mov ax,dx + div byte [Sectors] ; AL = head AH = sector + mov dh,al + inc ah + or cl,ah ; CX = cylinder and sector + + mov dl,[DriveNo] + mov bx,7C00h + mov ax,0201h ; Read one sector +common_tail: + int 13h + jc disk_error + pop si ; DS:SI -> partition table entry +; +; Verify that we have a boot sector, jump +; + cmp word [7C00h+510],0AA55h + jne missing_os + cli + jmp 0:7C00h ; Jump to boot sector; far + ; jump is speculation barrier + ; (Probably not neecessary, but + ; there is plenty of space.) + +not_one_partition: + ja too_many_os +missing_os: + mov si,missing_os_msg + jmp short die +too_many_os: +disk_error: + mov si,bad_disk_msg +die: +.msgloop: + lodsb + and al,al + jz .now + mov ah,0Eh ; TTY output + mov bh,[BIOS_page] ; Current page + mov bl,07h + int 10h + jmp short .msgloop +.now: + jmp short .now + + align 4, db 0 ; Begin data area + +; +; EBIOS disk address packet +; +dapa: + dw 16 ; Packet size +.count: dw 1 ; Block count +.off: dw 7C00h ; Offset of buffer +.seg: dw 0 ; Segment of buffer +.lba: dd 0 ; LBA (LSW) + dd 0 ; LBA (MSW) + +; CHS information +SecPerCyl: dw 0 ; Heads*Sectors +Sectors: dw 0 + +; Error messages +missing_os_msg db 'Missing operating system', 13, 10, 0 +bad_disk_msg db 'Operating system loading error', 13, 10, 0 + +; +; Maximum MBR size: 446 bytes; end-of-boot-sector signature also needed. +; Note that some operating systems (NT, DR-DOS) put additional stuff at +; the end of the MBR, so shorter is better. Location 440 is known to +; have a 4-byte attempt-at-unique-ID for some OSes. +; + +PartitionTable equ $$+446 ; Start of partition table + +; +; BSS data; put at 800h +; +DriveNo equ 0800h diff --git a/memdisk/Makefile b/memdisk/Makefile new file mode 100644 index 0000000..3bb97ff --- /dev/null +++ b/memdisk/Makefile @@ -0,0 +1,95 @@ +## ----------------------------------------------------------------------- +## +## Copyright 2001-2009 H. Peter Anvin - All Rights Reserved +## Copyright 2009 Intel Corporation; author: H. Peter Anvin +## +## 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +topdir = .. +include $(topdir)/MCONFIG.embedded +-include $(topdir)/version.mk + +INCLUDES = -I$(topdir)/com32/include +CFLAGS += -DDATE='"$(DATE)"' +LDFLAGS = $(GCCOPT) -g +NASM = nasm +NASMOPT = -O9999 +NFLAGS = -dDATE='"$(DATE)"' +NINCLUDE = + +SRCS = $(wildcard *.asm *.c *.h) + +# The DATE is set on the make command line when building binaries for +# official release. Otherwise, substitute a hex string that is pretty much +# guaranteed to be unique to be unique from build to build. +ifndef HEXDATE +HEXDATE := $(shell $(PERL) ../now.pl $(SRCS)) +endif +ifndef DATE +DATE := $(shell sh ../gen-id.sh $(VERSION) $(HEXDATE)) +endif + +# Important: init.o16 must be first!! +OBJS16 = init.o16 init32.o +OBJS32 = start32.o setup.o msetup.o e820func.o conio.o memcpy.o memset.o \ + unzip.o memdisk_chs.o memdisk_edd.o + +CSRC = setup.c msetup.c e820func.c conio.c unzip.c +SSRC = start32.S memcpy.S memset.S +NASMSRC = memdisk_chs.asm memdisk_edd.asm memdisk16.asm + +all: memdisk # e820test + +# tidy, clean removes everything except the final binary +tidy dist: + rm -f *.o *.s *.tmp *.o16 *.s16 *.bin *.lst *.elf e820test .*.d + +clean: tidy + +# spotless also removes the product binary +spotless: clean + rm -f memdisk .depend + +memdisk16.o: memdisk16.asm + +# Cancel rule +%.o: %.asm + +memdisk16.o: memdisk16.asm + ( $(NASM) -M -DDEPEND $(NFLAGS) $(NINCLUDE) -o $@ $< ; echo '' ) > .$@.d ; true + $(NASM) -f elf $(NASMOPT) $(NFLAGS) $(NINCLUDE) -o $@ -l $*.lst $< + +.PRECIOUS: %.bin +%.bin: %.asm + ( $(NASM) -M -DDEPEND $(NFLAGS) $(NINCLUDE) -o $@ $< ; echo '' ) > .$@.d ; true + $(NASM) -f bin $(NASMOPT) $(NFLAGS) $(NINCLUDE) -o $@ -l $*.lst $< + +memdisk_%.o: memdisk_%.bin + $(LD) -r -b binary -o $@ $< + +memdisk16.elf: $(OBJS16) + $(LD) -Ttext 0 -o $@ $^ + +memdisk32.elf: memdisk.ld $(OBJS32) + $(LD) -o $@ -T $^ + +%.bin: %.elf + $(OBJCOPY) -O binary $< $@ + +memdisk: memdisk16.bin memdisk32.bin postprocess.pl + $(PERL) postprocess.pl $@ memdisk16.bin memdisk32.bin + +e820test: e820test.c e820func.c msetup.c + $(CC) -m32 -g -W -Wall -DTEST -o $@ $^ + +# This file contains the version number, so add a dependency for it +setup.s: ../version + +# Include dependencies file +-include .*.d diff --git a/memdisk/conio.c b/memdisk/conio.c new file mode 100644 index 0000000..0285a98 --- /dev/null +++ b/memdisk/conio.c @@ -0,0 +1,363 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2001-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * conio.c + * + * Output to the screen + */ + +#include +#include "memdisk.h" +#include "conio.h" + +int putchar(int ch) +{ + com32sys_t regs; + + if ( ch == '\n' ) { + /* \n -> \r\n */ + putchar('\r'); + } + + regs.eax.w[0] = 0x0e00|(ch&0xff); + syscall(0x10, ®s, NULL); + + return ch; +} + +int puts(const char *s) +{ + int count = 0; + + while ( *s ) { + putchar(*s); + count++; + s++; + } + + return count; +} + +/* + * Oh, it's a waste of space, but oh-so-yummy for debugging. It's just + * initialization code anyway, so it doesn't take up space when we're + * actually running. This version of printf() does not include 64-bit + * support. "Live with it." + * + * Most of this code was shamelessly snarfed from the Linux kernel, then + * modified. + */ + +static inline int +isdigit(int ch) +{ + return (ch >= '0') && (ch <= '9'); +} + +static int skip_atoi(const char **s) +{ + int i=0; + + while (isdigit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +unsigned int atou(const char *s) +{ + unsigned int i = 0; + while (isdigit(*s)) + i = i*10 + (*s++ - '0'); + return i; +} + +static int strnlen(const char *s, int maxlen) +{ + const char *es = s; + while ( *es && maxlen ) { + es++; maxlen--; + } + + return (es-s); +} + +#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 char * number(char * str, long num, int base, int size, int precision + ,int type) +{ + char c,sign,tmp[66]; + const char *digits="0123456789abcdefghijklmnopqrstuvwxyz"; + int i; + + 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) + *str++ = ' '; + if (sign) + *str++ = sign; + if (type & SPECIAL) { + if (base==8) + *str++ = '0'; + else if (base==16) { + *str++ = '0'; + *str++ = digits[33]; + } + } + if (!(type & LEFT)) + while (size-- > 0) + *str++ = c; + while (i < precision--) + *str++ = '0'; + while (i-- > 0) + *str++ = tmp[i]; + while (size-- > 0) + *str++ = ' '; + return str; +} + +/* Forward decl. needed for IP address printing stuff... */ +int sprintf(char * buf, const char *fmt, ...); + +int vsprintf(char *buf, const char *fmt, va_list args) +{ + int len; + unsigned long num; + int i, base; + char * str; + 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 */ + + for (str=buf ; *fmt ; ++fmt) { + if (*fmt != '%') { + *str++ = *fmt; + 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 (isdigit(*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 (isdigit(*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) + *str++ = ' '; + *str++ = (unsigned char) va_arg(args, int); + while (--field_width > 0) + *str++ = ' '; + continue; + + case 's': + s = va_arg(args, char *); + len = strnlen(s, precision); + + if (!(flags & LEFT)) + while (len < field_width--) + *str++ = ' '; + for (i = 0; i < len; ++i) + *str++ = *s++; + while (len < field_width--) + *str++ = ' '; + continue; + + case 'p': + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + str = number(str, + (unsigned long) va_arg(args, void *), 16, + field_width, precision, flags); + continue; + + + case 'n': + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = (str - buf); + } else { + int * ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + *str++ = '%'; + 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: + *str++ = '%'; + if (*fmt) + *str++ = *fmt; + else + --fmt; + continue; + } + 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); + str = number(str, num, base, field_width, precision, flags); + } + *str = '\0'; + return str-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; +} + +int printf(const char *fmt, ...) +{ + char printf_buf[1024]; + va_list args; + int printed; + + va_start(args, fmt); + printed = vsprintf(printf_buf, fmt, args); + va_end(args); + + puts(printf_buf); + + return printed; +} diff --git a/memdisk/conio.h b/memdisk/conio.h new file mode 100644 index 0000000..9fa8991 --- /dev/null +++ b/memdisk/conio.h @@ -0,0 +1,31 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2001-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * conio.h + * + * Limited console I/O + */ + +#ifndef CONIO_H +#define CONIO_H + +#include +#include +#include + +int putchar(int); +int puts(const char *); +int printf(const char *, ...); +unsigned int atou(const char *); + +#endif diff --git a/memdisk/e820.h b/memdisk/e820.h new file mode 100644 index 0000000..04b4448 --- /dev/null +++ b/memdisk/e820.h @@ -0,0 +1,33 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2001-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * e820.h + * + * Common routines for e820 memory map management + */ + +#include + +struct e820range { + uint64_t start; + uint32_t type; +} __attribute__((packed)); + +extern struct e820range ranges[]; +extern int nranges; +extern uint32_t dos_mem, low_mem, high_mem; + +extern void e820map_init(void); +extern void insertrange(uint64_t, uint64_t, uint32_t); +extern void get_mem(void); +extern void parse_mem(void); diff --git a/memdisk/e820data b/memdisk/e820data new file mode 100644 index 0000000..14320aa --- /dev/null +++ b/memdisk/e820data @@ -0,0 +1,15 @@ +0000000000000000 000000000009bc00 1 1 +000000000009bc00 0000000000004400 2 1 +00000000000e9800 0000000000016800 2 1 +00000000000e9800 000000000000b400 2 1 +00000000000f5000 000000000000b000 2 9 +0000000000100000 0000000006ee0000 1 1 +0000000006fe0000 000000000000fc00 3 1 +0000000006fefc00 0000000000000400 4 1 +0000000006ff0000 0000000000002000 2 1 +0000000006ff2000 000000000000e000 1 1 +0000000007000000 0000000000100000 2 1 +00000000fff00000 0000000000100000 2 1 + +0000000000586000 0000000000168000 2 1 +000000000009ba00 0000000000000200 2 1 diff --git a/memdisk/e820func.c b/memdisk/e820func.c new file mode 100644 index 0000000..80471a9 --- /dev/null +++ b/memdisk/e820func.c @@ -0,0 +1,107 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2001-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * e820func.c + * + * E820 range database manager + */ + +#include +#ifdef TEST +#include +#else +#include "memdisk.h" /* For memset() */ +#endif +#include "e820.h" + +#define MAXRANGES 1024 +/* All of memory starts out as one range of "indeterminate" type */ +struct e820range ranges[MAXRANGES]; +int nranges; + + +void e820map_init(void) +{ + memset(ranges, 0, sizeof(ranges)); + nranges = 1; + ranges[1].type = -1U; +} + +static void insertrange_at(int where, uint64_t start, uint32_t type) +{ + int i; + + for ( i = nranges ; i > where ; i-- ) + ranges[i] = ranges[i-1]; + + ranges[where].start = start; + ranges[where].type = type; + + nranges++; + ranges[nranges].start = 0ULL; + ranges[nranges].type = -1U; +} + +void insertrange(uint64_t start, uint64_t len, uint32_t type) +{ + uint64_t last; + uint32_t oldtype; + int i, j; + + /* Remove this to make len == 0 mean all of memory */ + if ( len == 0 ) + return; /* Nothing to insert */ + + last = start+len-1; /* May roll over */ + + i = 0; + oldtype = -2U; + while ( start > ranges[i].start && ranges[i].type != -1U ) { + oldtype = ranges[i].type; + i++; + } + + /* Consider the replacement policy. This current one is "overwrite." */ + + if ( start < ranges[i].start || ranges[i].type == -1U ) + insertrange_at(i++, start, type); + + while ( i == 0 || last > ranges[i].start-1 ) { + oldtype = ranges[i].type; + ranges[i].type = type; + i++; + } + + if ( last < ranges[i].start-1 ) + insertrange_at(i, last+1, oldtype); + + /* Now the map is correct, but quite possibly not optimal. Scan the + map for ranges which are redundant and remove them. */ + i = j = 1; + oldtype = ranges[0].type; + while ( i < nranges ) { + if ( ranges[i].type == oldtype ) { + i++; + } else { + oldtype = ranges[i].type; + if ( i != j ) + ranges[j] = ranges[i]; + i++; j++; + } + } + + if ( i != j ) { + ranges[j] = ranges[i]; /* Termination sentinel copy */ + nranges -= (i-j); + } +} diff --git a/memdisk/e820test.c b/memdisk/e820test.c new file mode 100644 index 0000000..2de7fd4 --- /dev/null +++ b/memdisk/e820test.c @@ -0,0 +1,88 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2001-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * e820hack.c + * + * Test of INT 15:E820 canonicalization/manipulation routine + */ + +#include +#include +#include +#include +#include "e820.h" + +void *sys_bounce; /* Dummy */ + +extern void parse_mem(void); +extern uint32_t dos_mem, low_mem, high_mem; + +void __attribute__((noreturn)) die(void) +{ + abort(); +} + +void printranges(void) { + int i; + + for ( i = 0 ; i < nranges ; i++ ) { + printf("%016llx %016llx %d\n", + ranges[i].start, + ranges[i+1].start - ranges[i].start, + ranges[i].type); + } +} + +int main(void) +{ + uint64_t start, len; + uint32_t type; + char line[BUFSIZ], *p; + + e820map_init(); + printranges(); + + while ( fgets(line, BUFSIZ, stdin) ) { + p = strchr(line, ':'); + p = p ? p+1 : line; + if ( sscanf(p, " %llx %llx %d", &start, &len, &type) == 3 ) { + putchar('\n'); + printf("%016llx %016llx %d <-\n", start, len, type); + putchar('\n'); + insertrange(start, len, type); + printranges(); + } + } + + parse_mem(); + + putchar('\n'); + printf("DOS mem = %#10x (%u K)\n", dos_mem, dos_mem >> 10); + printf("Low mem = %#10x (%u K)\n", low_mem, low_mem >> 10); + printf("High mem = %#10x (%u K)\n", high_mem, high_mem >> 10); + putchar('\n'); + + /* Now, steal a chunk (2K) of DOS memory and make sure it registered OK */ + insertrange(dos_mem-2048, 2048, 2, 1); /* Type 2 = reserved */ + + printranges(); + parse_mem(); + + putchar('\n'); + printf("DOS mem = %#10x (%u K)\n", dos_mem, dos_mem >> 10); + printf("Low mem = %#10x (%u K)\n", low_mem, low_mem >> 10); + printf("High mem = %#10x (%u K)\n", high_mem, high_mem >> 10); + putchar('\n'); + + return 0; +} diff --git a/memdisk/fdgeo.pl b/memdisk/fdgeo.pl new file mode 100755 index 0000000..b76727d --- /dev/null +++ b/memdisk/fdgeo.pl @@ -0,0 +1,56 @@ +#!/usr/bin/perl +# +# Try automatic generation of geometries +# + +($k) = @ARGV; +$sec = int($k*2+0.5); + +if ($sec < 320*2) { + $c = 40; + $h = 1; + $type = 1; +} elsif ($sec < 640*2) { + $c = 40; + $h = 2; + $type = 1; +} elsif ($sec < 1200*2) { + $c = 80; + $h = 2; + $type = 3; +} elsif ($sec < 1440*2) { + $c = 80; + $h = 2; + $type = 2; +} elsif ($sec < 2880*2) { + $c = 80; + $h = 2; + $type = 4; +} elsif ($sec < 4096*2) { + $c = 80; + $h = 2; + $type = 6; +} else { + printf "%.1fK, %d sectors: ", $sec/2, $sec; + print "Considered a hard disk\n"; + exit 2; +} + +$ok = 0; +while ($c < 256) { + $s = int($sec/($c*$h)+0.5); + if ($s <= 63 && $sec == $c*$h*$s) { + $ok = 1; + last; + } + $c++; +} + +printf "%.1fK, %d sectors: ", $sec/2, $sec; +if ($ok) { + print "c=$c, h=$h, s=$s, type=$type\n"; + exit 0; +} else { + print "No valid geometry found (MEMDISK will fake it)\n"; + exit 1; +} diff --git a/memdisk/inflate.c b/memdisk/inflate.c new file mode 100644 index 0000000..9e1d79a --- /dev/null +++ b/memdisk/inflate.c @@ -0,0 +1,1083 @@ +#define DEBG(x) +#define DEBG1(x) +/* inflate.c -- Not copyrighted 1992 by Mark Adler + version c10p1, 10 January 1993 */ + +/* + * Adapted for booting Linux by Hannu Savolainen 1993 + * based on gzip-1.0.3 + * + * Nicolas Pitre , 1999/04/14 : + * Little mods for all variable to reside either into rodata or bss segments + * by marking constant variables with 'const' and initializing all the others + * at run-time only. This allows for the kernel uncompressor to run + * directly from Flash or ROM memory on embedded systems. + * + * Adapted for MEMDISK by H. Peter Anvin, April 2003 + */ + +/* + Inflate deflated (PKZIP's method 8 compressed) data. The compression + method searches for as much of the current string of bytes (up to a + length of 258) in the previous 32 K bytes. If it doesn't find any + matches (of at least length 3), it codes the next byte. Otherwise, it + codes the length of the matched string and its distance backwards from + the current position. There is a single Huffman code that codes both + single bytes (called "literals") and match lengths. A second Huffman + code codes the distance information, which follows a length code. Each + length or distance code actually represents a base value and a number + of "extra" (sometimes zero) bits to get to add to the base value. At + the end of each deflated block is a special end-of-block (EOB) literal/ + length code. The decoding process is basically: get a literal/length + code; if EOB then done; if a literal, emit the decoded byte; if a + length then get the distance and emit the referred-to bytes from the + sliding window of previously emitted data. + + There are (currently) three kinds of inflate blocks: stored, fixed, and + dynamic. The compressor deals with some chunk of data at a time, and + decides which method to use on a chunk-by-chunk basis. A chunk might + typically be 32 K or 64 K. If the chunk is incompressible, then the + "stored" method is used. In this case, the bytes are simply stored as + is, eight bits per byte, with none of the above coding. The bytes are + preceded by a count, since there is no longer an EOB code. + + If the data is compressible, then either the fixed or dynamic methods + are used. In the dynamic method, the compressed data is preceded by + an encoding of the literal/length and distance Huffman codes that are + to be used to decode this block. The representation is itself Huffman + coded, and so is preceded by a description of that code. These code + descriptions take up a little space, and so for small blocks, there is + a predefined set of codes, called the fixed codes. The fixed method is + used if the block codes up smaller that way (usually for quite small + chunks), otherwise the dynamic method is used. In the latter case, the + codes are customized to the probabilities in the current block, and so + can code it much better than the pre-determined fixed codes. + + The Huffman codes themselves are decoded using a multi-level table + lookup, in order to maximize the speed of decoding plus the speed of + building the decoding tables. See the comments below that precede the + lbits and dbits tuning parameters. + */ + + +/* + Notes beyond the 1.93a appnote.txt: + + 1. Distance pointers never point before the beginning of the output + stream. + 2. Distance pointers can point back across blocks, up to 32k away. + 3. There is an implied maximum of 7 bits for the bit length table and + 15 bits for the actual data. + 4. If only one code exists, then it is encoded using one bit. (Zero + would be more efficient, but perhaps a little confusing.) If two + codes exist, they are coded using one bit each (0 and 1). + 5. There is no way of sending zero distance codes--a dummy must be + sent if there are none. (History: a pre 2.0 version of PKZIP would + store blocks with no distance codes, but this was discovered to be + too harsh a criterion.) Valid only for 1.93a. 2.04c does allow + zero distance codes, which is sent as one code of zero bits in + length. + 6. There are up to 286 literal/length codes. Code 256 represents the + end-of-block. Note however that the static length tree defines + 288 codes just to fill out the Huffman codes. Codes 286 and 287 + cannot be used though, since there is no length base or extra bits + defined for them. Similarly, there are up to 30 distance codes. + However, static trees define 32 codes (all 5 bits) to fill out the + Huffman codes, but the last two had better not show up in the data. + 7. Unzip can check dynamic Huffman blocks for complete code sets. + The exception is that a single code would not be complete (see #4). + 8. The five bits following the block type is really the number of + literal codes sent minus 257. + 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits + (1+6+6). Therefore, to output three times the length, you output + three codes (1+1+1), whereas to output four times the same length, + you only need two codes (1+3). Hmm. + 10. In the tree reconstruction algorithm, Code = Code + Increment + only if BitLength(i) is not zero. (Pretty obvious.) + 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) + 12. Note: length code 284 can represent 227-258, but length code 285 + really is 258. The last length deserves its own, short code + since it gets used a lot in very redundant files. The length + 258 is special since 258 - 3 (the min match length) is 255. + 13. The literal/length and distance code bit lengths are read as a + single stream of lengths. It is possible (and advantageous) for + a repeat code (16, 17, or 18) to go across the boundary between + the two sets of lengths. + */ + +#ifdef RCSID +static char rcsid[] = "#Id: inflate.c,v 0.14 1993/06/10 13:27:04 jloup Exp #"; +#endif + +#define slide window + +/* Huffman code lookup table entry--this entry is four bytes for machines + that have 16-bit pointers (e.g. PC's in the small or medium model). + Valid extra bits are 0..13. e == 15 is EOB (end of block), e == 16 + means that v is a literal, 16 < e < 32 means that v is a pointer to + the next table, which codes e - 16 bits, and lastly e == 99 indicates + an unused code. If a code with e == 99 is looked up, this implies an + error in the data. */ +struct huft { + uch e; /* number of extra bits or operation */ + uch b; /* number of bits in this code or subcode */ + union { + ush n; /* literal, length base, or distance base */ + struct huft *t; /* pointer to next level of table */ + } v; +}; + + +/* Function prototypes */ +STATIC int huft_build OF((unsigned *, unsigned, unsigned, + const ush *, const ush *, struct huft **, int *)); +STATIC int huft_free OF((struct huft *)); +STATIC int inflate_codes OF((struct huft *, struct huft *, int, int)); +STATIC int inflate_stored OF((void)); +STATIC int inflate_fixed OF((void)); +STATIC int inflate_dynamic OF((void)); +STATIC int inflate_block OF((int *)); +STATIC int inflate OF((void)); + + +/* The inflate algorithm uses a sliding 32 K byte window on the uncompressed + stream to find repeated byte strings. This is implemented here as a + circular buffer. The index is updated simply by incrementing and then + ANDing with 0x7fff (32K-1). */ +/* It is left to other modules to supply the 32 K area. It is assumed + to be usable as if it were declared "uch slide[32768];" or as just + "uch *slide;" and then malloc'ed in the latter case. The definition + must be in unzip.h, included above. */ +/* unsigned wp; current position in slide */ +#define wp outcnt +#define flush_output(w) (wp=(w),flush_window()) + +/* Tables for deflate from PKZIP's appnote.txt. */ +static const unsigned border[] = { /* Order of the bit length code lengths */ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; +static const ush cplens[] = { /* Copy lengths for literal codes 257..285 */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + /* note: see note #13 above about the 258 in this list. */ +static const ush cplext[] = { /* Extra bits for literal codes 257..285 */ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99}; /* 99==invalid */ +static const ush cpdist[] = { /* Copy offsets for distance codes 0..29 */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +static const ush cpdext[] = { /* Extra bits for distance codes */ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + + + +/* Macros for inflate() bit peeking and grabbing. + The usage is: + + NEEDBITS(j) + x = b & mask_bits[j]; + DUMPBITS(j) + + where NEEDBITS makes sure that b has at least j bits in it, and + DUMPBITS removes the bits from b. The macros use the variable k + for the number of bits in b. Normally, b and k are register + variables for speed, and are initialized at the beginning of a + routine that uses these macros from a global bit buffer and count. + + If we assume that EOB will be the longest code, then we will never + ask for bits with NEEDBITS that are beyond the end of the stream. + So, NEEDBITS should not read any more bytes than are needed to + meet the request. Then no bytes need to be "returned" to the buffer + at the end of the last block. + + However, this assumption is not true for fixed blocks--the EOB code + is 7 bits, but the other literal/length codes can be 8 or 9 bits. + (The EOB code is shorter than other codes because fixed blocks are + generally short. So, while a block always has an EOB, many other + literal/length codes have a significantly lower probability of + showing up at all.) However, by making the first table have a + lookup of seven bits, the EOB code will be found in that first + lookup, and so will not require that too many bits be pulled from + the stream. + */ + +STATIC ulg bb; /* bit buffer */ +STATIC unsigned bk; /* bits in bit buffer */ + +STATIC const ush mask_bits[] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +#define NEXTBYTE() (uch)get_byte() +#define NEEDBITS(n) {while(k<(n)){b|=((ulg)NEXTBYTE())<>=(n);k-=(n);} + + +/* + Huffman code decoding is performed using a multi-level table lookup. + The fastest way to decode is to simply build a lookup table whose + size is determined by the longest code. However, the time it takes + to build this table can also be a factor if the data being decoded + is not very long. The most common codes are necessarily the + shortest codes, so those codes dominate the decoding time, and hence + the speed. The idea is you can have a shorter table that decodes the + shorter, more probable codes, and then point to subsidiary tables for + the longer codes. The time it costs to decode the longer codes is + then traded against the time it takes to make longer tables. + + This results of this trade are in the variables lbits and dbits + below. lbits is the number of bits the first level table for literal/ + length codes can decode in one step, and dbits is the same thing for + the distance codes. Subsequent tables are also less than or equal to + those sizes. These values may be adjusted either when all of the + codes are shorter than that, in which case the longest code length in + bits is used, or when the shortest code is *longer* than the requested + table size, in which case the length of the shortest code in bits is + used. + + There are two different values for the two tables, since they code a + different number of possibilities each. The literal/length table + codes 286 possible values, or in a flat code, a little over eight + bits. The distance table codes 30 possible values, or a little less + than five bits, flat. The optimum values for speed end up being + about one bit more than those, so lbits is 8+1 and dbits is 5+1. + The optimum values may differ though from machine to machine, and + possibly even between compilers. Your mileage may vary. + */ + + +STATIC const int lbits = 9; /* bits in base literal/length lookup table */ +STATIC const int dbits = 6; /* bits in base distance lookup table */ + + +/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */ +#define BMAX 16 /* maximum bit length of any code (16 for explode) */ +#define N_MAX 288 /* maximum number of codes in any set */ + + +STATIC unsigned hufts; /* track memory usage */ + + +STATIC int huft_build(b, n, s, d, e, t, m) +unsigned *b; /* code lengths in bits (all assumed <= BMAX) */ +unsigned n; /* number of codes (assumed <= N_MAX) */ +unsigned s; /* number of simple-valued codes (0..s-1) */ +const ush *d; /* list of base values for non-simple codes */ +const ush *e; /* list of extra bits for non-simple codes */ +struct huft **t; /* result: starting table */ +int *m; /* maximum lookup bits, returns actual */ +/* Given a list of code lengths and a maximum table size, make a set of + tables to decode that set of codes. Return zero on success, one if + the given code set is incomplete (the tables are still built in this + case), two if the input is invalid (all zero length codes or an + oversubscribed set of lengths), and three if not enough memory. */ +{ + unsigned a; /* counter for codes of length k */ + unsigned c[BMAX+1]; /* bit length count table */ + unsigned f; /* i repeats in table every f entries */ + int g; /* maximum code length */ + int h; /* table level */ + register unsigned i; /* counter, current code */ + register unsigned j; /* counter */ + register int k; /* number of bits in current code */ + int l; /* bits per table (returned in m) */ + register unsigned *p; /* pointer into c[], b[], or v[] */ + register struct huft *q; /* points to current table */ + struct huft r; /* table entry for structure assignment */ + struct huft *u[BMAX]; /* table stack */ + unsigned v[N_MAX]; /* values in order of bit length */ + register int w; /* bits before this table == (l * h) */ + unsigned x[BMAX+1]; /* bit offsets, then code stack */ + unsigned *xp; /* pointer into x */ + int y; /* number of dummy codes added */ + unsigned z; /* number of entries in current table */ + +DEBG("huft1 "); + + /* Generate counts for each bit length */ + memzero(c, sizeof(c)); + p = b; i = n; + do { + Tracecv(*p, (stderr, (n-i >= ' ' && n-i <= '~' ? "%c %d\n" : "0x%x %d\n"), + n-i, *p)); + c[*p]++; /* assume all entries <= BMAX */ + p++; /* Can't combine with above line (Solaris bug) */ + } while (--i); + if (c[0] == n) /* null input--all zero length codes */ + { + *t = (struct huft *)NULL; + *m = 0; + return 0; + } + +DEBG("huft2 "); + + /* Find minimum and maximum length, bound *m by those */ + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; /* minimum code length */ + if ((unsigned)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; /* maximum code length */ + if ((unsigned)l > i) + l = i; + *m = l; + +DEBG("huft3 "); + + /* Adjust last length count to fill out codes, if needed */ + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return 2; /* bad input: more codes than bits */ + if ((y -= c[i]) < 0) + return 2; + c[i] += y; + +DEBG("huft4 "); + + /* Generate starting offsets into the value table for each length */ + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { /* note that i == g from above */ + *xp++ = (j += *p++); + } + +DEBG("huft5 "); + + /* Make a table of values in order of bit lengths */ + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + +DEBG("h6 "); + + /* Generate the Huffman codes and for each, make the table entries */ + x[0] = i = 0; /* first Huffman code is zero */ + p = v; /* grab values in bit order */ + h = -1; /* no tables yet--level -1 */ + w = -l; /* bits decoded == (l * h) */ + u[0] = (struct huft *)NULL; /* just to keep compilers happy */ + q = (struct huft *)NULL; /* ditto */ + z = 0; /* ditto */ +DEBG("h6a "); + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; k <= g; k++) + { +DEBG("h6b "); + a = c[k]; + while (a--) + { +DEBG("h6b1 "); + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while (k > w + l) + { +DEBG1("1 "); + h++; + w += l; /* previous table always l bits */ + + /* compute minimum size table less than or equal to l bits */ + z = (z = g - w) > (unsigned)l ? l : z; /* upper limit on table size */ + if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ + { /* too few codes for k-w bit table */ +DEBG1("2 "); + f -= a + 1; /* deduct codes from patterns left */ + xp = c + k; + while (++j < z) /* try smaller tables up to z bits */ + { + if ((f <<= 1) <= *++xp) + break; /* enough codes to use up j bits */ + f -= *xp; /* else deduct codes from patterns */ + } + } +DEBG1("3 "); + z = 1 << j; /* table entries for j-bit table */ + + /* allocate and link in new table */ + if ((q = (struct huft *)malloc((z + 1)*sizeof(struct huft))) == + (struct huft *)NULL) + { + if (h) + huft_free(u[0]); + return 3; /* not enough memory */ + } +DEBG1("4 "); + hufts += z + 1; /* track memory usage */ + *t = q + 1; /* link to list for huft_free() */ + *(t = &(q->v.t)) = (struct huft *)NULL; + u[h] = ++q; /* table starts after link */ + +DEBG1("5 "); + /* connect to last table, if there is one */ + if (h) + { + x[h] = i; /* save pattern for backing up */ + r.b = (uch)l; /* bits to dump before this table */ + r.e = (uch)(16 + j); /* bits in this table */ + r.v.t = q; /* pointer to this table */ + j = i >> (w - l); /* (get around Turbo C bug) */ + u[h-1][j] = r; /* connect to last table */ + } +DEBG1("6 "); + } +DEBG("h6c "); + + /* set up table entry in r */ + r.b = (uch)(k - w); + if (p >= v + n) + r.e = 99; /* out of values--invalid code */ + else if (*p < s) + { + r.e = (uch)(*p < 256 ? 16 : 15); /* 256 is end-of-block code */ + r.v.n = (ush)(*p); /* simple code is just the value */ + p++; /* one compiler does not like *p++ */ + } + else + { + r.e = (uch)e[*p - s]; /* non-simple--look up in lists */ + r.v.n = d[*p++ - s]; + } +DEBG("h6d "); + + /* fill code-like entries with r */ + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + /* backwards increment the k-bit code i */ + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + /* backup over finished tables */ + while ((i & ((1 << w) - 1)) != x[h]) + { + h--; /* don't need to update q */ + w -= l; + } +DEBG("h6e "); + } +DEBG("h6f "); + } + +DEBG("huft7 "); + + /* Return true (1) if we were given an incomplete table */ + return y != 0 && g != 1; +} + + + +STATIC int huft_free(t) +struct huft *t; /* table to free */ +/* Free the malloc'ed tables built by huft_build(), which makes a linked + list of the tables it made, with the links in a dummy first entry of + each table. */ +{ + register struct huft *p, *q; + + + /* Go through linked list, freeing from the malloced (t[-1]) address. */ + p = t; + while (p != (struct huft *)NULL) + { + q = (--p)->v.t; + free((char*)p); + p = q; + } + return 0; +} + + +STATIC int inflate_codes(tl, td, bl, bd) +struct huft *tl, *td; /* literal/length and distance decoder tables */ +int bl, bd; /* number of bits decoded by tl[] and td[] */ +/* inflate (decompress) the codes in a deflated (compressed) block. + Return an error code or zero if it all goes ok. */ +{ + register unsigned e; /* table entry flag/number of extra bits */ + unsigned n, d; /* length and index for copy */ + unsigned w; /* current window position */ + struct huft *t; /* pointer to table entry */ + unsigned ml, md; /* masks for bl and bd bits */ + register ulg b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + + + /* make local copies of globals */ + b = bb; /* initialize bit buffer */ + k = bk; + w = wp; /* initialize window position */ + + /* inflate the coded data */ + ml = mask_bits[bl]; /* precompute masks for speed */ + md = mask_bits[bd]; + for (;;) /* do until end of block */ + { + NEEDBITS((unsigned)bl) + if ((e = (t = tl + ((unsigned)b & ml))->e) > 16) + do { + if (e == 99) + return 1; + DUMPBITS(t->b) + e -= 16; + NEEDBITS(e) + } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16); + DUMPBITS(t->b) + if (e == 16) /* then it's a literal */ + { + slide[w++] = (uch)t->v.n; + Tracevv((stderr, "%c", slide[w-1])); + if (w == WSIZE) + { + flush_output(w); + w = 0; + } + } + else /* it's an EOB or a length */ + { + /* exit if end of block */ + if (e == 15) + break; + + /* get length of block to copy */ + NEEDBITS(e) + n = t->v.n + ((unsigned)b & mask_bits[e]); + DUMPBITS(e); + + /* decode distance of block to copy */ + NEEDBITS((unsigned)bd) + if ((e = (t = td + ((unsigned)b & md))->e) > 16) + do { + if (e == 99) + return 1; + DUMPBITS(t->b) + e -= 16; + NEEDBITS(e) + } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16); + DUMPBITS(t->b) + NEEDBITS(e) + d = w - t->v.n - ((unsigned)b & mask_bits[e]); + DUMPBITS(e) + Tracevv((stderr,"\\[%d,%d]", w-d, n)); + + /* do the copy */ + do { + n -= (e = (e = WSIZE - ((d &= WSIZE-1) > w ? d : w)) > n ? n : e); +#if !defined(NOMEMCPY) && !defined(DEBUG) + if (w - d >= e) /* (this test assumes unsigned comparison) */ + { + memcpy(slide + w, slide + d, e); + w += e; + d += e; + } + else /* do it slow to avoid memcpy() overlap */ +#endif /* !NOMEMCPY */ + do { + slide[w++] = slide[d++]; + Tracevv((stderr, "%c", slide[w-1])); + } while (--e); + if (w == WSIZE) + { + flush_output(w); + w = 0; + } + } while (n); + } + } + + + /* restore the globals from the locals */ + wp = w; /* restore global window pointer */ + bb = b; /* restore global bit buffer */ + bk = k; + + /* done */ + return 0; +} + + + +STATIC int inflate_stored() +/* "decompress" an inflated type 0 (stored) block. */ +{ + unsigned n; /* number of bytes in block */ + unsigned w; /* current window position */ + register ulg b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + +DEBG(""); + return 0; +} + + + +STATIC int inflate_fixed() +/* decompress an inflated type 1 (fixed Huffman codes) block. We should + either replace this with a custom decoder, or at least precompute the + Huffman tables. */ +{ + int i; /* temporary variable */ + struct huft *tl; /* literal/length code table */ + struct huft *td; /* distance code table */ + int bl; /* lookup bits for tl */ + int bd; /* lookup bits for td */ + unsigned l[288]; /* length list for huft_build */ + +DEBG(" 1) + { + huft_free(tl); + + DEBG(">"); + return i; + } + + + /* decompress until an end-of-block code */ + if (inflate_codes(tl, td, bl, bd)) + return 1; + + + /* free the decoding tables, return */ + huft_free(tl); + huft_free(td); + return 0; +} + + + +STATIC int inflate_dynamic() +/* decompress an inflated type 2 (dynamic Huffman codes) block. */ +{ + int i; /* temporary variables */ + unsigned j; + unsigned l; /* last length */ + unsigned m; /* mask for bit lengths table */ + unsigned n; /* number of lengths to get */ + struct huft *tl; /* literal/length code table */ + struct huft *td; /* distance code table */ + int bl; /* lookup bits for tl */ + int bd; /* lookup bits for td */ + unsigned nb; /* number of bit length codes */ + unsigned nl; /* number of literal/length codes */ + unsigned nd; /* number of distance codes */ +#ifdef PKZIP_BUG_WORKAROUND + unsigned ll[288+32]; /* literal/length and distance code lengths */ +#else + unsigned ll[286+30]; /* literal/length and distance code lengths */ +#endif + register ulg b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + +DEBG(" 288 || nd > 32) +#else + if (nl > 286 || nd > 30) +#endif + return 1; /* bad lengths */ + +DEBG("dyn1 "); + + /* read in bit-length-code lengths */ + for (j = 0; j < nb; j++) + { + NEEDBITS(3) + ll[border[j]] = (unsigned)b & 7; + DUMPBITS(3) + } + for (; j < 19; j++) + ll[border[j]] = 0; + +DEBG("dyn2 "); + + /* build decoding table for trees--single level, 7 bit lookup */ + bl = 7; + if ((i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl)) != 0) + { + if (i == 1) + huft_free(tl); + return i; /* incomplete code set */ + } + +DEBG("dyn3 "); + + /* read in literal and distance code lengths */ + n = nl + nd; + m = mask_bits[bl]; + i = l = 0; + while ((unsigned)i < n) + { + NEEDBITS((unsigned)bl) + j = (td = tl + ((unsigned)b & m))->b; + DUMPBITS(j) + j = td->v.n; + if (j < 16) /* length of code in bits (0..15) */ + ll[i++] = l = j; /* save last length in l */ + else if (j == 16) /* repeat last length 3 to 6 times */ + { + NEEDBITS(2) + j = 3 + ((unsigned)b & 3); + DUMPBITS(2) + if ((unsigned)i + j > n) + return 1; + while (j--) + ll[i++] = l; + } + else if (j == 17) /* 3 to 10 zero length codes */ + { + NEEDBITS(3) + j = 3 + ((unsigned)b & 7); + DUMPBITS(3) + if ((unsigned)i + j > n) + return 1; + while (j--) + ll[i++] = 0; + l = 0; + } + else /* j == 18: 11 to 138 zero length codes */ + { + NEEDBITS(7) + j = 11 + ((unsigned)b & 0x7f); + DUMPBITS(7) + if ((unsigned)i + j > n) + return 1; + while (j--) + ll[i++] = 0; + l = 0; + } + } + +DEBG("dyn4 "); + + /* free decoding table for trees */ + huft_free(tl); + +DEBG("dyn5 "); + + /* restore the global bit buffer */ + bb = b; + bk = k; + +DEBG("dyn5a "); + + /* build the decoding tables for literal/length and distance codes */ + bl = lbits; + if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0) + { +DEBG("dyn5b "); + if (i == 1) { + error(" incomplete literal tree"); + huft_free(tl); + } + return i; /* incomplete code set */ + } +DEBG("dyn5c "); + bd = dbits; + if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0) + { +DEBG("dyn5d "); + if (i == 1) { + error(" incomplete distance tree"); +#ifdef PKZIP_BUG_WORKAROUND + i = 0; + } +#else + huft_free(td); + } + huft_free(tl); + return i; /* incomplete code set */ +#endif + } + +DEBG("dyn6 "); + + /* decompress until an end-of-block code */ + if (inflate_codes(tl, td, bl, bd)) + return 1; + +DEBG("dyn7 "); + + /* free the decoding tables, return */ + huft_free(tl); + huft_free(td); + + DEBG(">"); + return 0; +} + + + +STATIC int inflate_block(e) +int *e; /* last block flag */ +/* decompress an inflated block */ +{ + unsigned t; /* block type */ + register ulg b; /* bit buffer */ + register unsigned k; /* number of bits in bit buffer */ + + DEBG(""); + + /* bad block type */ + return 2; +} + + + +STATIC int inflate() +/* decompress an inflated entry */ +{ + int e; /* last block flag */ + int r; /* result code */ + unsigned h; /* maximum struct huft's malloc'ed */ + void *ptr; + + /* initialize window, bit buffer */ + wp = 0; + bk = 0; + bb = 0; + + + /* decompress until the last block */ + h = 0; + do { + hufts = 0; + gzip_mark(&ptr); + if ((r = inflate_block(&e)) != 0) { + gzip_release(&ptr); + return r; + } + gzip_release(&ptr); + if (hufts > h) + h = hufts; + } while (!e); + + /* Undo too much lookahead. The next read will be byte aligned so we + * can discard unused bits in the last meaningful byte. + */ + while (bk >= 8) { + bk -= 8; + unget_byte(); + } + + /* flush out slide */ + flush_output(wp); + + + /* return success */ +#ifdef DEBUG + fprintf(stderr, "<%u> ", h); +#endif /* DEBUG */ + return 0; +} + +/********************************************************************** + * + * The following are support routines for inflate.c + * + **********************************************************************/ + +static ulg crc_32_tab[256]; +static ulg crc; /* initialized in makecrc() so it'll reside in bss */ +#define CRC_VALUE (crc ^ 0xffffffffL) + +/* + * Code to compute the CRC-32 table. Borrowed from + * gzip-1.0.3/makecrc.c. + */ + +static void +makecrc(void) +{ +/* Not copyrighted 1990 Mark Adler */ + + unsigned long c; /* crc shift register */ + unsigned long e; /* polynomial exclusive-or pattern */ + int i; /* counter for all possible eight bit values */ + int k; /* byte being shifted into crc apparatus */ + + /* terms of polynomial defining this crc (except x^32): */ + static const int p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* Make exclusive-or pattern from polynomial */ + e = 0; + for (i = 0; i < sizeof(p)/sizeof(int); i++) + e |= 1L << (31 - p[i]); + + crc_32_tab[0] = 0; + + for (i = 1; i < 256; i++) + { + c = 0; + for (k = i | 256; k != 1; k >>= 1) + { + c = c & 1 ? (c >> 1) ^ e : c >> 1; + if (k & 1) + c ^= e; + } + crc_32_tab[i] = c; + } + + /* this is initialized here so this code could reside in ROM */ + crc = (ulg)0xffffffffL; /* shift register contents */ +} + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +/* + * Do the uncompression! + */ +int gunzip() +{ + int res; + + /* Decompress */ + if ((res = inflate())) { + switch (res) { + case 0: + break; + case 1: + error("invalid compressed format (err=1)"); + break; + case 2: + error("invalid compressed format (err=2)"); + break; + case 3: + error("out of memory"); + break; + default: + error("invalid compressed format (other)"); + } + return -1; + } + + return 0; +} diff --git a/memdisk/memcpy.S b/memdisk/memcpy.S new file mode 100644 index 0000000..6b986a0 --- /dev/null +++ b/memdisk/memcpy.S @@ -0,0 +1,86 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * memcpy.S + * + * Reasonably efficient memcpy, using aligned transfers at least + * for the destination operand. + */ + + .text + .globl memcpy + .type memcpy, @function +memcpy: + jecxz 1f + + pushl %esi + pushl %edi + pushl %eax /* Return value */ + + movl %eax,%edi + movl %edx,%esi + + /* Initial alignment */ + movl %edi,%edx + shrl $1,%edx + jnc 11f + movsb + decl %ecx +11: + movb %cl,%al + cmpl $2,%ecx + jb 13f + + shrl $1,%edx + jnc 12f + movsw + subl $2,%ecx +12: + /* Bulk transfer */ + movb %cl,%al + shrl $2,%ecx + rep; movsl + + /* Final alignment */ + testb $2,%al + jz 14f + movsw +13: +14: + testb $1,%al + jz 15f + movsb +15: + + popl %eax /* Return value */ + popl %edi + popl %esi +1: + ret + + .size memcpy, .-memcpy diff --git a/memdisk/memdisk b/memdisk/memdisk new file mode 100644 index 0000000000000000000000000000000000000000..b4bf61afcb72ad3e6ad6e135caf753c170df56d5 GIT binary patch literal 19584 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 JC$%@K{x^H-OM?Ia literal 0 HcmV?d00001 diff --git a/memdisk/memdisk.h b/memdisk/memdisk.h new file mode 100644 index 0000000..9d4a429 --- /dev/null +++ b/memdisk/memdisk.h @@ -0,0 +1,88 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2001-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * memdisk.h + * + * Miscellaneous header definitions + */ + +#ifndef MEMDISK_H +#define MEMDISK_H + +#include + +/* We use the com32 interface for calling 16-bit code */ +#include + +/* The real-mode segment */ +#define LOW_SEG 0x0800 + +#define __cdecl __attribute__((cdecl,regparm(0))) + +typedef void (*syscall_t)(uint8_t, com32sys_t *, com32sys_t *); +extern __cdecl syscall_t syscall; +extern void *sys_bounce; + +/* What to call when we're dead */ +extern void __attribute__((noreturn)) die(void); + +/* Standard routines */ +#define memcpy(a,b,c) __builtin_memcpy(a,b,c) +#define memset(a,b,c) __builtin_memset(a,b,c) +#define strcpy(a,b) __builtin_strcpy(a,b) + +static inline size_t strlen(const char *__a) +{ + const char *__D; + size_t __c; + + asm("repne;scasb" + : "=D" (__D), "=c" (__c) + : "D" (__a), "c" (-1), "a" (0), "m" (*__a)); + + return __D - __a - 1; +} + +/* memcpy() but returns a pointer to end of buffer */ +static inline void * +mempcpy(void *__d, const void *__s, unsigned int __n) +{ + memcpy(__d, __s, __n); + return (void *)((char *)__d + __n); +} + +/* memcmp() */ +static inline int +memcmp(const void *__a, const void *__b, unsigned int __n) +{ + const unsigned char *__aa = __a; + const unsigned char *__bb = __b; + int __d; + + while ( __n-- ) { + __d = *__bb++ - *__aa++; + if ( __d ) + return __d; + } + + return 0; +} + +/* Decompression */ +extern int check_zip(void *indata, uint32_t size, uint32_t *zbytes_p, + uint32_t *dbytes_p, uint32_t *orig_crc, + uint32_t *offset_p); +extern void *unzip(void *indata, uint32_t zbytes, uint32_t dbytes, + uint32_t orig_crc, void *target); + +#endif diff --git a/memdisk/memdisk.inc b/memdisk/memdisk.inc new file mode 100644 index 0000000..00537a8 --- /dev/null +++ b/memdisk/memdisk.inc @@ -0,0 +1,978 @@ +; -*- fundamental -*- (asm-mode sucks) +; **************************************************************************** +; +; memdisk.inc +; +; A program to emulate an INT 13h disk BIOS from a "disk" in extended +; memory. +; +; Copyright 2001-2009 H. Peter Anvin - All Rights Reserved +; Copyright 2009 Intel Corporation; author: H. Peter Anvin +; +; 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, Inc., 53 Temple Place Ste 330, +; Boston MA 02111-1307, USA; either version 2 of the License, or +; (at your option) any later version; incorporated herein by reference. +; +; **************************************************************************** + +%include "../version.gen" + +; %define DEBUG_TRACERS ; Uncomment to get debugging tracers + +%ifdef DEBUG_TRACERS + +%macro TRACER 1 + call debug_tracer + db %1 +%endmacro + +%else ; DEBUG_TRACERS + +%macro TRACER 1 +%endmacro + +%endif ; DEBUG_TRACERS + +; Flags we test our configuration against +%define CONFIG_READONLY 0x01 +%define CONFIG_RAW 0x02 +%define CONFIG_SAFEINT 0x04 +%define CONFIG_BIGRAW 0x08 ; MUST be 8! + + org 0h + +%define SECTORSIZE_LG2 9 ; log2(sector size) +%define SECTORSIZE (1 << SECTORSIZE_LG2) + + ; Parameter registers definition; this is the definition + ; of the stack frame. +%define P_DS word [bp+34] +%define P_ES word [bp+32] +%define P_EAX dword [bp+28] +%define P_HAX word [bp+30] +%define P_AX word [bp+28] +%define P_AL byte [bp+28] +%define P_AH byte [bp+29] +%define P_ECX dword [bp+24] +%define P_HCX word [bp+26] +%define P_CX word [bp+24] +%define P_CL byte [bp+24] +%define P_CH byte [bp+25] +%define P_EDX dword [bp+20] +%define P_HDX word [bp+22] +%define P_DX word [bp+20] +%define P_DL byte [bp+20] +%define P_DH byte [bp+21] +%define P_EBX dword [bp+16] +%define P_HBX word [bp+18] +%define P_HBXL byte [bp+18] +%define P_BX word [bp+16] +%define P_BL byte [bp+16] +%define P_BH byte [bp+17] +%define P_EBP dword [bp+8] +%define P_BP word [bp+8] +%define P_ESI dword [bp+4] +%define P_SI word [bp+4] +%define P_EDI dword [bp] +%define P_DI word [bp] + + section .text + ; These pointers are used by the installer and + ; must be first in the binary +Pointers: dw Int13Start + dw Int15Start + dw PatchArea + dw TotalSize + dw IretPtr + +IretPtr equ Int13Start.iret +Int13Start: + ; Swap stack + mov [cs:Stack],esp + mov [cs:SavedAX],ax + mov ax,ss + mov [cs:Stack+4],ax + mov ax,cs + mov ss,ax + mov sp,[cs:MyStack] + + ; See if DL points to our class of device (FD, HD) + push dx + push dx + xor dl,[cs:DriveNo] + pop dx + js .nomatch ; If SF=0, we have a class match here + ; 0x00 the sign bit for FD + ; 0x80 the sign bit for HD + jz .our_drive ; If ZF=1, we have an exact match + cmp dl,[cs:DriveNo] + jb .nomatch ; Drive < Our drive + dec dl ; Drive > Our drive, adjust drive # +.nomatch: + mov ax,[cs:SavedAX] + pushf + call far [cs:OldInt13] + pushf + push bp + mov bp,sp + cmp byte [cs:SavedAX+1],08h ; Get drive params function? + je .norestoredl + cmp byte [cs:SavedAX+1],15h ; Get disk type function? + jne .restoredl + test byte [bp+4],80h ; Hard disk? + jnz .norestoredl +.restoredl: ; DL should have number of drives + mov dl,[bp+4] +.norestoredl: + push ax + push ebx + push ds + mov ax,[bp+2] ; Flags + lds ebx,[cs:Stack] + mov [bx+4],al ; Arithmetic flags + pop ds + pop ebx + pop ax + pop bp + lss esp,[cs:Stack] +.iret: iret + +.our_drive: + ; Set up standard entry frame + push ds + push es + mov ds,ax + mov es,ax + mov ax,[SavedAX] + pushad + mov bp,sp ; Point BP to the entry stack frame + TRACER 'F' + ; Note: AH == P_AH here + cmp ah,Int13FuncsCnt-1 + ja Invalid_jump + xor al,al ; AL = 0 is standard entry condition + mov di,ax + shr di,7 ; Convert AH to an offset in DI + call [Int13Funcs+di] + +Done: ; Standard routine for return + mov P_AX,ax +DoneWeird: + TRACER 'D' + xor bx,bx + mov es,bx + mov bx,[StatusPtr] + mov [es:bx],ah ; Save status + and ah,ah + + lds ebx,[Stack] + ; This sets the low byte (the arithmetic flags) of the + ; FLAGS on stack to either 00h (no flags) or 01h (CF) + ; depending on if AH was zero or not. + setnz [bx+4] ; Set CF iff error + popad + pop es + pop ds + lss esp,[cs:Stack] + iret + +Reset: + ; Reset affects multiple drives, so we need to pass it on + TRACER 'R' + xor ax,ax ; Bottom of memory + mov es,ax + test dl,dl ; Always pass it on if we are + ; resetting HD + js .hard_disk ; Bit 7 set + ; Some BIOSes get very unhappy if we pass a reset floppy + ; command to them and don't actually have any floppies. + ; This is a bug, but we have to deal with it nontheless. + ; Therefore, if we are the *ONLY* floppy drive, and the + ; user didn't request HD reset, then just drop the command. + ; BIOS equipment byte, top two bits + 1 == total # of floppies + test byte [es:0x410],0C0h + jz success + jmp .pass_on ; ... otherwise pass it to the BIOS +.hard_disk: + ; ... same thing for hard disks, sigh ... + cmp byte [es:0x475],1 ; BIOS variable for number of hard + ; disks + jbe success + +.pass_on: + pop ax ; Drop return address + popad ; Restore all registers + pop es + pop ds + lss esp,[cs:Stack] ; Restore the stack + and dl,80h ; Clear all but the type bit + jmp far [cs:OldInt13] + + +Invalid: + pop dx ; Drop return address +Invalid_jump: + TRACER 'I' + mov ah,01h ; Unsupported function + jmp short Done + +GetDriveType: + test byte [DriveNo],80h + mov bl,02h ; Type 02h = floppy with changeline + jz .floppy + ; Hard disks only... + inc bx ; Type = 03h + mov dx,[DiskSize] ; Return the disk size in sectors + mov P_DX,dx + mov cx,[DiskSize+2] + mov P_CX,cx +.floppy: + mov P_AH,bl ; 02h floppy, 03h hard disk + pop ax ; Drop return address + xor ax,ax ; Success... + jmp DoneWeird ; But don't stick it into P_AX + +GetStatus: + xor ax,ax + mov es,ax + mov bx,[StatusPtr] + mov ah,[bx] ; Copy last status + ret + +ReadMult: + TRACER 'm' +Read: + TRACER 'R' + call setup_regs +do_copy: + TRACER '<' + call bcopy + TRACER '>' + movzx ax,P_AL ; AH = 0, AL = transfer count + ret + +WriteMult: + TRACER 'M' +Write: + TRACER 'W' + test byte [ConfigFlags],CONFIG_READONLY + jnz .readonly + call setup_regs + xchg esi,edi ; Opposite direction of a Read! + jmp short do_copy +.readonly: mov ah,03h ; Write protected medium + ret + + ; Verify integrity; just bounds-check +Seek: +Verify: + call setup_regs ; Returns error if appropriate + ; And fall through to success + +CheckIfReady: ; These are always-successful noop functions +Recalibrate: +InitWithParms: +DetectChange: +EDDDetectChange: +EDDLock: +SetMode: +success: + xor ax,ax ; Always successful + ret + +GetParms: + TRACER 'G' + mov dl,[DriveCnt] ; Cached data + mov P_DL,dl + test byte [DriveNo],80h + jnz .hd + mov P_DI,DPT + mov P_ES,cs + mov bl,[DriveType] + mov P_BL,bl +.hd: + mov ax,[Cylinders] + dec ax ; We report the highest #, not the count + xchg al,ah + shl al,6 + or al,[Sectors] + mov P_CX,ax + mov ax,[Heads] + dec ax + mov P_DH,al + + ; + ; Is this MEMDISK installation check? + ; + cmp P_HAX,'ME' + jne .notic + cmp P_HCX,'MD' + jne .notic + cmp P_HDX,'IS' + jne .notic + cmp P_HBX,'K?' + jne .notic + + ; MEMDISK installation check... + mov P_HAX,'!M' + mov P_HCX,'EM' + mov P_HDX,'DI' + mov P_HBX,'SK' + mov P_ES,cs + mov P_DI,MemDisk_Info + +.notic: + xor ax,ax + ret +; +; EDD functions -- only if enabled +; +%if EDD +EDDPresence: + TRACER 'E' + TRACER 'c' + + cmp P_BX,55AAh + jne Invalid + mov P_BX,0AA55h ; EDD signature + mov P_AX,03000h ; EDD 3.0 + mov P_CX,0003h ; Bit 0 - Fixed disk access subset + ; Bit 1 - Locking and ejecting subset + pop ax ; Drop return address + xor ax,ax ; Success + jmp DoneWeird ; Success, but AH != 0, sigh... + +EDDRead: + TRACER 'E' + TRACER 'r' + + call edd_setup_regs + call bcopy + xor ax,ax + ret + +EDDWrite: + TRACER 'E' + TRACER 'w' + + call edd_setup_regs + xchg esi,edi ; Opposite direction of a Read! + call bcopy + xor ax,ax + ret + +EDDVerify: +EDDSeek: + call edd_setup_regs ; Just bounds checking + xor ax,ax + ret + +EDDGetParms: + TRACER 'E' + TRACER 'p' + + mov es,P_DS + mov di,P_SI + mov si,EDD_DPT + + lodsw ; Length of our DPT + mov cx,[es:di] + cmp cx,26 ; Minimum size + jb .overrun + + cmp cx,ax + jb .oksize + mov cx,ax + +.oksize: + mov ax,cx + stosw + dec cx + dec cx + rep movsb + + xor ax,ax + ret + +.overrun: + mov ax,0100h + ret +%endif ; EDD + + ; Set up registers as for a "Read", and compares against disk + ; size. + ; WARNING: This fails immediately, even if we can transfer some + ; sectors. This isn't really the correct behaviour. +setup_regs: + + ; Convert a CHS address in P_CX/P_DH into an LBA in eax + ; CH = cyl[7:0] + ; CL[0:5] = sector (1-based) CL[7:6] = cyl[9:8] + ; DH = head + movzx ecx,P_CX + movzx ebx,cl ; Sector number + and bl,3Fh + dec ebx ; Sector number is 1-based + cmp bx,[Sectors] + jae .overrun + movzx edi,P_DH ; Head number + movzx eax,word [Heads] + cmp di,ax + jae .overrun + shr cl,6 + xchg cl,ch ; Now (E)CX <- cylinder number + mul ecx ; eax <- Heads*cyl# (edx <- 0) + add eax,edi + mul dword [Sectors] + add eax,ebx + ; Now eax = LBA, edx = 0 + + ; + ; setup_regs continues... + ; + ; Note: edi[31:16] and ecx[31:16] = 0 already + mov di,P_BX ; Get linear address of target buffer + mov cx,P_ES + shl ecx,4 + add edi,ecx ; EDI = address to fetch to + movzx ecx,P_AL ; Sector count + mov esi,eax + add eax,ecx ; LBA of final sector + 1 + shl esi,SECTORSIZE_LG2 ; LBA -> byte offset + add esi,[DiskBuf] ; Get address in high memory + cmp eax,[DiskSize] ; Check the high mark against limit + ja .overrun + shl ecx,SECTORSIZE_LG2-2 ; Convert count to dwords + ret + +.overrun: pop ax ; Drop setup_regs return address + mov ax,0200h ; Missing address mark + ret ; Return to Done + + ; Set up registers as for an EDD Read, and compares against disk size. +%if EDD +edd_setup_regs: + push es + mov si,P_SI ; DS:SI -> DAPA + mov es,P_DS + + mov dx,[es:si] + cmp dx,16 + jb .baddapa + + cmp dword [es:si+4],-1 + je .linear_address + + movzx ebx,word [es:si+4] ; Offset + movzx edi,word [es:si+6] ; Segment + shl edi,4 + add ebx,edi + jmp .got_address + +.linear_address: + cmp dx,24 ; Must be large enough to hold + ; linear address + jb .baddapa + + cmp dword [es:si+20],0 ; > 4 GB addresses not supported + mov ax,0900h ; "Data boundary error" - bogus, but + ; no really better code available + jne .error + + mov ebx,[es:si+16] + +.got_address: + cmp dword [es:si+12],0 ; LBA too large? + jne .overrun + + movzx ecx, word [es:si+2] ; Sectors to transfer + mov esi,[es:si+8] ; Starting sector + mov eax,esi + add eax,ecx + jc .overrun + cmp eax,[DiskSize] + ja .overrun + + shl ecx,SECTORSIZE_LG2-2 ; Convert to dwords + shl esi,SECTORSIZE_LG2 ; Convert to an offset + add esi,[DiskBuf] + mov edi,ebx + pop es + ret + +.baddapa: + mov ax,0100h ; Invalid command + pop es + pop ax ; Drop setup_regs return address + ret + +.overrun: + mov ax,0200h ; "Address mark not found" = + ; LBA beyond end of disk +.error: + and word [es:si+2],0 ; No sectors transferred + pop es + pop ax + ret + +EDDEject: + mov ax,0B200h ; Volume Not Removable + ret + +%endif ; EDD + + +; +; INT 15h intercept routines +; +int15_e820: + cmp edx,534D4150h ; "SMAP" + jne oldint15 + cmp ecx,20 ; Need 20 bytes + jb err86 + push ds + push cs + pop ds + push edx ; "SMAP" + and ebx,ebx + jne .renew + mov ebx,E820Table +.renew: + add bx,12 ; Advance to next + mov eax,[bx-4] ; Type + and eax,eax ; Null type? + jz .renew ; If so advance to next + mov [es:di+16],eax + mov eax,[bx-12] ; Start addr (low) + mov edx,[bx-8] ; Start addr (high) + mov [es:di],eax + mov [es:di+4],edx + mov eax,[bx] ; End addr (low) + mov edx,[bx+4] ; End addr (high) + sub eax,[bx-12] ; Derive the length + sbb edx,[bx-8] + mov [es:di+8],eax ; Length (low) + mov [es:di+12],edx ; Length (high) + cmp dword [bx+8],-1 ; Type of next = end? + jne .notdone + xor ebx,ebx ; Done with table +.notdone: + pop eax ; "SMAP" + mov edx,eax ; Some systems expect eax = edx = SMAP + mov ecx,20 ; Bytes loaded + pop ds +int15_success: + mov byte [bp+6], 02h ; Clear CF + pop bp + iret + +err86: + mov byte [bp+6], 03h ; Set CF + mov ah,86h + pop bp + iret + +Int15Start: + push bp + mov bp,sp + cmp ax,0E820h + je near int15_e820 + cmp ax,0E801h + je int15_e801 + cmp ax,0E881h + je int15_e881 + cmp ah,88h + je int15_88 +oldint15: pop bp + jmp far [cs:OldInt15] + +int15_e801: ; Get mem size for > 64 MB config + mov ax,[cs:Mem1MB] + mov cx,ax + mov bx,[cs:Mem16MB] + mov dx,bx + jmp short int15_success + +int15_e881: ; Get mem size for > 64 MB config + ; 32-bit code + mov eax,[cs:Mem1MB] + mov ecx,eax + mov ebx,[cs:Mem16MB] + mov edx,ebx + jmp short int15_success + +int15_88: ; Get extended mem size + mov ax,[cs:MemInt1588] + jmp short int15_success + +; +; Routine to copy in/out of high memory +; esi = linear source address +; edi = linear target address +; ecx = 32-bit word count +; +; Assumes cs = ds = es +; +bcopy: + push eax + push ebx + push edx + push ebp + + mov bx, real_int15_stub + + test byte [ConfigFlags], CONFIG_RAW|CONFIG_SAFEINT + jz .anymode ; Always do the real INT 15h + + smsw ax ; Unprivileged! + test al,01h + jnz .protmode ; Protmode -> do real INT 15h + +.realmode: + ; Raw or Safeint mode, and we're in real mode... + + test byte [ConfigFlags], CONFIG_SAFEINT + jnz .fakeint15 + +.raw: + TRACER 'r' + ; We're in real mode, do it outselves + + pushfd ; + push ds ; + push es ; + + cli + cld + + xor ebx,ebx + mov bx,cs + shl ebx,4 + lea edx,[Shaker+ebx] + mov [Shaker+2],edx + + ; Test to see if A20 is enabled or not + xor ax,ax + mov ds,ax + dec ax + mov es,ax + + mov ax,[0] + mov bx,ax + xor bx,[es:10h] + not ax + mov [0],ax + mov dx,ax + xor dx,[es:10h] + not ax + mov [0],ax + + or dx,bx + push dx ; Save A20 status + jnz .skip_a20e + + mov ax,2401h ; Enable A20 + int 15h +.skip_a20e: + mov dl,[ConfigFlags] + and dx,CONFIG_BIGRAW + add dx,8 + ; DX = 16 for BIGRAW, 8 for RAW + ; 8 is selector for a 64K flat segment, + ; 16 is selector for a 4GB flat segment. + + lgdt [cs:Shaker] + mov eax,cr0 + or al,01h + mov cr0,eax + + mov bx,16 ; Large flat segment + mov ds,bx + mov es,bx + + a32 rep movsd + + ; DX has the appropriate value to put in + ; the registers on return + mov ds,dx + mov es,dx + + and al,~01h + mov cr0,eax + + pop dx ; A20 status + pop es ; + pop ds ; + + and dx,dx + jnz .skip_a20d + mov ax,2400h ; Disable A20 + int 15h +.skip_a20d: + popfd ; + jmp .done + +.fakeint15: + ; We're in real mode with CONFIG_SAFEINT, invoke the + ; original INT 15h vector. We used to test for the + ; INT 15h vector being unchanged here, but that is + ; *us*; however, the test was wrong for years (always + ; negative) so instead of fixing the test do what we + ; tested and don't bother probing. + mov bx, fake_int15_stub + +.protmode: + TRACER 'p' +.anymode: + +.copy_loop: + push esi + push edi + push ecx + cmp ecx,4000h + jna .safe_size + mov ecx,4000h +.safe_size: + push ecx ; Transfer size this cycle + mov eax, esi + mov [Mover_src1], si + shr eax, 16 + mov [Mover_src1+2], al + mov [Mover_src2], ah + mov eax, edi + mov [Mover_dst1], di + shr eax, 16 + mov [Mover_dst1+2], al + mov [Mover_dst2], ah + mov si,Mover + mov ah, 87h + shl cx,1 ; Convert to 16-bit words + call bx ; INT 15h stub + pop eax ; Transfer size this cycle + pop ecx + pop edi + pop esi + jc .error + lea esi,[esi+4*eax] + lea edi,[edi+4*eax] + sub ecx, eax + jnz .copy_loop + ; CF = 0 +.error: +.done: + pop ebp + pop edx + pop ebx + pop eax + ret + +real_int15_stub: + int 15h + cli ; Some BIOSes enable interrupts on INT 15h + ret + +fake_int15_stub: + pushf + call far [OldInt15] + cli + ret + +%ifdef DEBUG_TRACERS +debug_tracer: pushad + pushfd + mov bp,sp + mov bx,[bp+9*4] + mov al,[cs:bx] + inc word [bp+9*4] + mov ah,0Eh + mov bx,7 + int 10h + popfd + popad + ret +%endif + + section .data + alignb 2 +Int13Funcs dw Reset ; 00h - RESET + dw GetStatus ; 01h - GET STATUS + dw Read ; 02h - READ + dw Write ; 03h - WRITE + dw Verify ; 04h - VERIFY + dw Invalid ; 05h - FORMAT TRACK + dw Invalid ; 06h - FORMAT TRACK AND SET BAD FLAGS + dw Invalid ; 07h - FORMAT DRIVE AT TRACK + dw GetParms ; 08h - GET PARAMETERS + dw InitWithParms ; 09h - INITIALIZE CONTROLLER WITH + ; DRIVE PARAMETERS + dw Invalid ; 0Ah + dw Invalid ; 0Bh + dw Seek ; 0Ch - SEEK TO CYLINDER + dw Reset ; 0Dh - RESET HARD DISKS + dw Invalid ; 0Eh + dw Invalid ; 0Fh + dw CheckIfReady ; 10h - CHECK IF READY + dw Recalibrate ; 11h - RECALIBRATE + dw Invalid ; 12h + dw Invalid ; 13h + dw Invalid ; 14h + dw GetDriveType ; 15h - GET DRIVE TYPE + dw DetectChange ; 16h - DETECT DRIVE CHANGE +%if EDD + dw Invalid ; 17h + dw Invalid ; 18h + dw Invalid ; 19h + dw Invalid ; 1Ah + dw Invalid ; 1Bh + dw Invalid ; 1Ch + dw Invalid ; 1Dh + dw Invalid ; 1Eh + dw Invalid ; 1Fh + dw Invalid ; 20h + dw ReadMult ; 21h - READ MULTIPLE + dw WriteMult ; 22h - WRITE MULTIPLE + dw SetMode ; 23h - SET CONTROLLER FEATURES + dw SetMode ; 24h - SET MULTIPLE MODE + dw Invalid ; 25h - IDENTIFY DRIVE + dw Invalid ; 26h + dw Invalid ; 27h + dw Invalid ; 28h + dw Invalid ; 29h + dw Invalid ; 2Ah + dw Invalid ; 2Bh + dw Invalid ; 2Ch + dw Invalid ; 2Dh + dw Invalid ; 2Eh + dw Invalid ; 2Fh + dw Invalid ; 30h + dw Invalid ; 31h + dw Invalid ; 32h + dw Invalid ; 33h + dw Invalid ; 34h + dw Invalid ; 35h + dw Invalid ; 36h + dw Invalid ; 37h + dw Invalid ; 38h + dw Invalid ; 39h + dw Invalid ; 3Ah + dw Invalid ; 3Bh + dw Invalid ; 3Ch + dw Invalid ; 3Dh + dw Invalid ; 3Eh + dw Invalid ; 3Fh + dw Invalid ; 40h + dw EDDPresence ; 41h - EDD PRESENCE DETECT + dw EDDRead ; 42h - EDD READ + dw EDDWrite ; 43h - EDD WRITE + dw EDDVerify ; 44h - EDD VERIFY + dw EDDLock ; 45h - EDD LOCK/UNLOCK MEDIA + dw EDDEject ; 46h - EDD EJECT + dw EDDSeek ; 47h - EDD SEEK + dw EDDGetParms ; 48h - EDD GET PARAMETERS + dw EDDDetectChange ; 49h - EDD MEDIA CHANGE STATUS +%endif + +Int13FuncsEnd equ $ +Int13FuncsCnt equ (Int13FuncsEnd-Int13Funcs) >> 1 + + + alignb 8, db 0 +Shaker dw ShakerEnd-$-1 ; Descriptor table limit + dd 0 ; Pointer to self + dw 0 + +Shaker_RMDS: dd 0x0000ffff ; 64K data segment + dd 0x00009300 + +Shaker_DS: dd 0x0000ffff ; 4GB data segment + dd 0x008f9300 + +ShakerEnd equ $ + + alignb 8, db 0 + +Mover dd 0, 0, 0, 0 ; Must be zero + dw 0ffffh ; 64 K segment size +Mover_src1: db 0, 0, 0 ; Low 24 bits of source addy + db 93h ; Access rights + db 00h ; Extended access rights +Mover_src2: db 0 ; High 8 bits of source addy + dw 0ffffh ; 64 K segment size +Mover_dst1: db 0, 0, 0 ; Low 24 bits of target addy + db 93h ; Access rights + db 00h ; Extended access rights +Mover_dst2: db 0 ; High 8 bits of source addy +Mover_dummy2: dd 0, 0, 0, 0 ; More space for the BIOS + + alignb 4, db 0 +MemDisk_Info equ $ ; Pointed to by installation check +MDI_Bytes dw MDI_Len ; Total bytes in MDI structure +MDI_Version db VERSION_MINOR, VERSION_MAJOR ; MEMDISK version + +PatchArea equ $ ; This gets filled in by the installer + +DiskBuf dd 0 ; Linear address of high memory disk +DiskSize dd 0 ; Size of disk in blocks +CommandLine dw 0, 0 ; Far pointer to saved command line + +OldInt13 dd 0 ; INT 13h in chain +OldInt15 dd 0 ; INT 15h in chain + +OldDosMem dw 0 ; Old position of DOS mem end +BootLoaderID db 0 ; Boot loader ID from header + db 0 ; pad + +DPT_ptr dw 0 ; If nonzero, pointer to DPT + ; Original DPT pointer follows + +MDI_Len equ $-MemDisk_Info + +; ---- MDI structure ends here --- +MemInt1588 dw 0 ; 1MB-65MB memory amount (1K) + +Cylinders dw 0 ; Cylinder count +Heads dw 0 ; Head count +Sectors dd 0 ; Sector count (zero-extended) + +Mem1MB dd 0 ; 1MB-16MB memory amount (1K) +Mem16MB dd 0 ; 16MB-4G memory amount (64K) + +DriveNo db 0 ; Our drive number +DriveType db 0 ; Our drive type (floppies) +DriveCnt db 0 ; Drive count (from the BIOS) + +ConfigFlags db 0 ; Bit 0 - readonly + +MyStack dw 0 ; Offset of stack +StatusPtr dw 0 ; Where to save status (zeroseg ptr) + +DPT times 16 db 0 ; BIOS parameter table pointer (floppies) +OldInt1E dd 0 ; Previous INT 1E pointer (DPT) + +%if EDD +EDD_DPT: +.length dw 30 +.info dw 0029h + ; Bit 0 - DMA boundaries handled transparently + ; Bit 3 - Device supports write verify + ; Bit 5 - Media is lockable +.cylinders dd 0 ; Filled in by installer +.heads dd 0 ; Filled in by installer +.sectors dd 0 ; Filled in by installer +.totalsize dd 0, 0 ; Filled in by installer +.bytespersec dw SECTORSIZE +.eddtable dw -1, -1 ; Invalid DPTE pointer + +%endif + + ; End patch area +Stack dd 0 ; Saved SS:ESP on invocation + dw 0 +SavedAX dw 0 ; AX saved on invocation + + alignb 4, db 0 ; We *MUST* end on a dword boundary + +E820Table equ $ ; The installer loads the E820 table here +TotalSize equ $ ; End pointer diff --git a/memdisk/memdisk.ld b/memdisk/memdisk.ld new file mode 100644 index 0000000..51c3e35 --- /dev/null +++ b/memdisk/memdisk.ld @@ -0,0 +1,140 @@ +/* ----------------------------------------------------------------------- + * + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * 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, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * Linker script for MEMDISK + */ + +/* Script for -z combreloc: combine and sort reloc sections */ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +EXTERN(_start) +ENTRY(_start) +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = 0x100000; + PROVIDE (__executable_start = .); + + .init : + { + KEEP (*(.init)) + } =0x90909090 + .text : + { + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + } =0x90909090 + .fini : + { + KEEP (*(.fini)) + } =0x90909090 + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + + /* Ensure the __preinit_array_start label is properly aligned. We + could instead move the label definition inside the section, but + the linker would then create the section even if it turns out to + be empty, which isn't pretty. */ + . = ALIGN(4); + PROVIDE (__preinit_array_start = .); + .preinit_array : { *(.preinit_array) } + PROVIDE (__preinit_array_end = .); + PROVIDE (__init_array_start = .); + .init_array : { *(.init_array) } + PROVIDE (__init_array_end = .); + PROVIDE (__fini_array_start = .); + .fini_array : { *(.fini_array) } + PROVIDE (__fini_array_end = .); + PROVIDE (__ctors_start = .); + .ctors : + { + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + PROVIDE (__ctors_end = .); + PROVIDE (__dtors_start = .); + .dtors : + { + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + PROVIDE (__dtors_end = .); + + /* Adjust the address for the data segment. Avoid mixing code and + data within same 128-byte chunk. */ + . = ALIGN(128); + + .data : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + _edata = .; + PROVIDE (edata = .); + . = ALIGN(16); + .bss : + { + __bss_start = .; + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. */ + . = ALIGN(4); + __bss_end = .; + } + _end = .; + PROVIDE (end = .); + + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /DISCARD/ : { *(.note.GNU-stack) } +} diff --git a/memdisk/memdisk16.asm b/memdisk/memdisk16.asm new file mode 100644 index 0000000..d43404e --- /dev/null +++ b/memdisk/memdisk16.asm @@ -0,0 +1,791 @@ +;; -*- fundamental -*- +;; ----------------------------------------------------------------------- +;; +;; Copyright 1994-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +;; Boston MA 02111-1307, USA; either version 2 of the License, or +;; (at your option) any later version; incorporated herein by reference. +;; +;; ----------------------------------------------------------------------- + +;; +;; init16.asm +;; +;; Routine to initialize and to trampoline into 32-bit +;; protected memory. This code is derived from bcopy32.inc and +;; com32.inc in the main SYSLINUX distribution. +;; + +%include '../version.gen' + +MY_CS equ 0x0800 ; Segment address to use +CS_BASE equ (MY_CS << 4) ; Corresponding address + +; Low memory bounce buffer +BOUNCE_SEG equ (MY_CS+0x1000) + +%define DO_WBINVD 0 + +%define STACK_HEAP_SIZE (128*1024) + + section .rodata align=16 + section .data align=16 + section .bss align=16 + +;; ----------------------------------------------------------------------- +;; Kernel image header +;; ----------------------------------------------------------------------- + + section .text ; Must be first in image + bits 16 + +cmdline times 497 db 0 ; We put the command line here +setup_sects db 0 +root_flags dw 0 +syssize dw 0 +swap_dev dw 0 +ram_size dw 0 +vid_mode dw 0 +root_dev dw 0 +boot_flag dw 0xAA55 + +_start: jmp short start + + db "HdrS" ; Header signature + dw 0x0203 ; Header version number + +realmode_swtch dw 0, 0 ; default_switch, SETUPSEG +start_sys_seg dw 0x1000 ; obsolete +version_ptr dw memdisk_version-0x200 ; version string ptr +type_of_loader db 0 ; Filled in by boot loader +loadflags db 1 ; Please load high +setup_move_size dw 0 ; Unused +code32_start dd 0x100000 ; 32-bit start address +ramdisk_image dd 0 ; Loaded ramdisk image address +ramdisk_size dd 0 ; Size of loaded ramdisk +bootsect_kludge dw 0, 0 +heap_end_ptr dw 0 +pad1 dw 0 +cmd_line_ptr dd 0 ; Command line +ramdisk_max dd 0xffffffff ; Highest allowed ramdisk address + +; +; These fields aren't real setup fields, they're poked in by the +; 32-bit code. +; +b_esdi dd 0 ; ES:DI for boot sector invocation +b_edx dd 0 ; EDX for boot sector invocation + + section .rodata +memdisk_version: + db "MEMDISK ", VERSION_STR, " ", DATE, 0 + +;; ----------------------------------------------------------------------- +;; End kernel image header +;; ----------------------------------------------------------------------- + +; +; Move ourselves down into memory to reduce the risk of conflicts; +; then canonicalize CS to match the other segments. +; + section .text + bits 16 +start: + mov ax,MY_CS + mov es,ax + movzx cx,byte [setup_sects] + inc cx ; Add one for the boot sector + shl cx,7 ; Convert to dwords + xor si,si + xor di,di + mov fs,si ; fs <- 0 + cld + rep movsd + mov ds,ax + mov ss,ax + xor esp,esp ; Stack at top of 64K segment + jmp MY_CS:.next +.next: + +; +; Copy the command line, if there is one +; +copy_cmdline: + xor di,di ; Bottom of our own segment (= "boot sector") + mov eax,[cmd_line_ptr] + and eax,eax + jz .endcmd ; No command line + mov si,ax + shr eax,4 ; Convert to segment + and si,0x000F ; Starting offset only + mov gs,ax + mov cx,496 ; Max number of bytes +.copycmd: + gs lodsb + and al,al + jz .endcmd + stosb + loop .copycmd +.endcmd: + xor al,al + stosb + +; +; Now jump to 32-bit code +; + sti + call init32 +; +; When init32 returns, we have been set up, the new boot sector loaded, +; and we should go and and run the newly loaded boot sector. +; +; The setup function will have poked values into the setup area. +; + movzx edi,word [cs:b_esdi] + mov es,word [cs:b_esdi+2] + mov edx,[cs:b_edx] + + cli + xor esi,esi ; No partition table involved + mov ds,si ; Make all the segments consistent + mov fs,si + mov gs,si + mov ss,si + mov esp,0x7C00 ; Good place for SP to start out + call 0:0x7C00 + int 18h ; A far return -> INT 18h + +; +; We enter protected mode, set up a flat 32-bit environment, run rep movsd +; and then exit. IMPORTANT: This code assumes cs == MY_CS. +; +; This code is probably excessively anal-retentive in its handling of +; segments, but this stuff is painful enough as it is without having to rely +; on everything happening "as it ought to." +; + section .rodata + + ; desc base, limit, flags +%macro desc 3 + dd (%2 & 0xffff) | ((%1 & 0xffff) << 16) + dd (%1 & 0xff000000) | (%2 & 0xf0000) | ((%3 & 0xf0ff) << 8) | ((%1 & 0x00ff0000) >> 16) +%endmacro + + align 8, db 0 +call32_gdt: dw call32_gdt_size-1 ; Null descriptor - contains GDT +.adj1: dd call32_gdt+CS_BASE ; pointer for LGDT instruction + dw 0 + + ; 0008: Code segment, use16, readable, dpl 0, base CS_BASE, 64K + desc CS_BASE, 0xffff, 0x009b + + ; 0010: Data segment, use16, read/write, dpl 0, base CS_BASE, 64K + desc CS_BASE, 0xffff, 0x0093 + + ; 0018: Data segment, use16, read/write, dpl 0, base 0, 4G + desc 0, 0xfffff, 0x809b + + ; 0020: Code segment, use32, read/write, dpl 0, base 0, 4G + desc 0, 0xfffff, 0xc09b + + ; 0028: Data segment, use32, read/write, dpl 0, base 0, 4G + desc 0, 0xfffff, 0xc093 + +call32_gdt_size: equ $-call32_gdt + +err_a20: db 'ERROR: A20 gate not responding!',13,10,0 + + section .bss + alignb 4 +SavedSSSP resd 1 ; Place to save SS:SP +Return resd 1 ; Return value +A20Test resw 1 ; Space to test A20 +A20Tries resb 1 + + section .data + alignb 4 +Target dd 0 ; Target address +Target_Seg dw 20h ; Target CS + +A20Type dw 0 ; Default = unknown + + section .text + bits 16 +; +; Routines to enable and disable (yuck) A20. These routines are gathered +; from tips from a couple of sources, including the Linux kernel and +; http://www.x86.org/. The need for the delay to be as large as given here +; is indicated by Donnie Barnes of RedHat, the problematic system being an +; IBM ThinkPad 760EL. +; +; We typically toggle A20 twice for every 64K transferred. +; +%define io_delay call _io_delay +%define IO_DELAY_PORT 80h ; Invalid port (we hope!) +%define disable_wait 32 ; How long to wait for a disable + +%define A20_DUNNO 0 ; A20 type unknown +%define A20_NONE 1 ; A20 always on? +%define A20_BIOS 2 ; A20 BIOS enable +%define A20_KBC 3 ; A20 through KBC +%define A20_FAST 4 ; A20 through port 92h + + align 2, db 0 +A20List dw a20_dunno, a20_none, a20_bios, a20_kbc, a20_fast +A20DList dw a20d_dunno, a20d_none, a20d_bios, a20d_kbc, a20d_fast +a20_adjust_cnt equ ($-A20List)/2 + +slow_out: out dx, al ; Fall through + +_io_delay: out IO_DELAY_PORT,al + out IO_DELAY_PORT,al + ret + +enable_a20: + pushad + mov byte [A20Tries],255 ; Times to try to make this work + +try_enable_a20: + +; +; Flush the caches +; +%if DO_WBINVD + call try_wbinvd +%endif + +; +; If the A20 type is known, jump straight to type +; + mov bp,[A20Type] + add bp,bp ; Convert to word offset +.adj4: jmp word [bp+A20List] + +; +; First, see if we are on a system with no A20 gate +; +a20_dunno: +a20_none: + mov byte [A20Type], A20_NONE + call a20_test + jnz a20_done + +; +; Next, try the BIOS (INT 15h AX=2401h) +; +a20_bios: + mov byte [A20Type], A20_BIOS + mov ax,2401h + pushf ; Some BIOSes muck with IF + int 15h + popf + + call a20_test + jnz a20_done + +; +; Enable the keyboard controller A20 gate +; +a20_kbc: + mov dl, 1 ; Allow early exit + call empty_8042 + jnz a20_done ; A20 live, no need to use KBC + + mov byte [A20Type], A20_KBC ; Starting KBC command sequence + + mov al,0D1h ; Write output port + out 064h, al + call empty_8042_uncond + + mov al,0DFh ; A20 on + out 060h, al + call empty_8042_uncond + + ; Apparently the UHCI spec assumes that A20 toggle + ; ends with a null command (assumed to be for sychronization?) + ; Put it here to see if it helps anything... + mov al,0FFh ; Null command + out 064h, al + call empty_8042_uncond + + ; Verify that A20 actually is enabled. Do that by + ; observing a word in low memory and the same word in + ; the HMA until they are no longer coherent. Note that + ; we don't do the same check in the disable case, because + ; we don't want to *require* A20 masking (SYSLINUX should + ; work fine without it, if the BIOS does.) +.kbc_wait: push cx + xor cx,cx +.kbc_wait_loop: + call a20_test + jnz a20_done_pop + loop .kbc_wait_loop + + pop cx +; +; Running out of options here. Final attempt: enable the "fast A20 gate" +; +a20_fast: + mov byte [A20Type], A20_FAST ; Haven't used the KBC yet + in al, 092h + or al,02h + and al,~01h ; Don't accidentally reset the machine! + out 092h, al + +.fast_wait: push cx + xor cx,cx +.fast_wait_loop: + call a20_test + jnz a20_done_pop + loop .fast_wait_loop + + pop cx + +; +; Oh bugger. A20 is not responding. Try frobbing it again; eventually give up +; and report failure to the user. +; + + dec byte [A20Tries] + jnz try_enable_a20 + + + ; Error message time + mov si,err_a20 +print_err: + lodsb + and al,al + jz die + mov bx,7 + mov ah,0xe + int 10h + jmp print_err + + +die: + sti +.hlt: hlt + jmp short .hlt + +; +; A20 unmasked, proceed... +; +a20_done_pop: pop cx +a20_done: popad + ret + +; +; This routine tests if A20 is enabled (ZF = 0). This routine +; must not destroy any register contents. +; +a20_test: + push es + push cx + push ax + mov cx,0FFFFh ; HMA = segment 0FFFFh + mov es,cx + mov cx,32 ; Loop count + mov ax,[A20Test] +.a20_wait: inc ax + mov [A20Test],ax + io_delay ; Serialize, and fix delay + cmp ax,[es:A20Test+CS_BASE+10h] + loopz .a20_wait +.a20_done: pop ax + pop cx + pop es + ret + +disable_a20: + pushad +; +; Flush the caches +; +%if DO_WBINVD + call try_wbinvd +%endif + + mov bp,[A20Type] + add bp,bp ; Convert to word offset +.adj5: jmp word [bp+A20DList] + +a20d_bios: + mov ax,2400h + pushf ; Some BIOSes muck with IF + int 15h + popf + jmp short a20d_snooze + +; +; Disable the "fast A20 gate" +; +a20d_fast: + in al, 092h + and al,~03h + out 092h, al + jmp short a20d_snooze + +; +; Disable the keyboard controller A20 gate +; +a20d_kbc: + call empty_8042_uncond + + mov al,0D1h + out 064h, al ; Write output port + call empty_8042_uncond + + mov al,0DDh ; A20 off + out 060h, al + call empty_8042_uncond + + mov al,0FFh ; Null command/synchronization + out 064h, al + call empty_8042_uncond + + ; Wait a bit for it to take effect +a20d_snooze: + push cx + mov cx, disable_wait +.delayloop: call a20_test + jz .disabled + loop .delayloop +.disabled: pop cx +a20d_dunno: +a20d_none: + popad + ret + +; +; Routine to empty the 8042 KBC controller. If dl != 0 +; then we will test A20 in the loop and exit if A20 is +; suddenly enabled. +; +empty_8042_uncond: + xor dl,dl +empty_8042: + call a20_test + jz .a20_on + and dl,dl + jnz .done +.a20_on: io_delay + in al, 064h ; Status port + test al,1 + jz .no_output + io_delay + in al, 060h ; Read input + jmp short empty_8042 +.no_output: + test al,2 + jnz empty_8042 + io_delay +.done: ret + +; +; Execute a WBINVD instruction if possible on this CPU +; +%if DO_WBINVD +try_wbinvd: + wbinvd + ret +%endif + + section .bss + alignb 4 +PMESP resd 1 ; Protected mode %esp + + section .idt nobits align=4096 + alignb 4096 +pm_idt resb 4096 ; Protected-mode IDT, followed by interrupt stubs + + + + +pm_entry: equ 0x100000 + + section .rodata + align 4, db 0 +call32_pmidt: + dw 8*256 ; Limit + dd pm_idt+CS_BASE ; Address + +call32_rmidt: + dw 0ffffh ; Limit + dd 0 ; Address + + section .text +; +; This is the main entrypoint in this function +; +init32: + mov ebx,call32_call_start+CS_BASE ; Where to go in PM + +call32_enter_pm: + mov ax,cs + mov ds,ax + cli + mov [SavedSSSP],sp + mov [SavedSSSP+2],ss + cld + call a20_test + jnz .a20ok + call enable_a20 + +.a20ok: + lgdt [call32_gdt] ; Set up GDT + lidt [call32_pmidt] ; Set up the IDT + mov eax,cr0 + or al,1 + mov cr0,eax ; Enter protected mode + jmp 20h:dword .in_pm+CS_BASE + + bits 32 +.in_pm: + xor eax,eax ; Available for future use... + mov fs,eax + mov gs,eax + + mov al,28h ; Set up data segments + mov es,eax + mov ds,eax + mov ss,eax + + mov esp,[PMESP+CS_BASE] ; Load protmode %esp if available + jmp ebx ; Go to where we need to go + +; +; This is invoked before first dispatch of the 32-bit code, in 32-bit mode +; +call32_call_start: + ; + ; Point the stack into low memory + ; We have: this segment, bounce buffer, then stack+heap + ; + mov esp, CS_BASE + 0x20000 + STACK_HEAP_SIZE + and esp, ~0xf + + ; + ; Set up the protmode IDT and the interrupt jump buffers + ; + mov edi,pm_idt+CS_BASE + + ; Form an interrupt gate descriptor + ; WARNING: This is broken if pm_idt crosses a 64K boundary; + ; however, it can't because of the alignment constraints. + mov ebx,pm_idt+CS_BASE+8*256 + mov eax,0x0020ee00 + xchg ax,bx + xor ecx,ecx + inc ch ; ecx <- 256 + + push ecx +.make_idt: + stosd + add eax,8 + xchg eax,ebx + stosd + xchg eax,ebx + loop .make_idt + + pop ecx + + ; Each entry in the interrupt jump buffer contains + ; the following instructions: + ; + ; 00000000 60 pushad + ; 00000001 B0xx mov al, + ; 00000003 E9xxxxxxxx jmp call32_handle_interrupt + + mov eax,0xe900b060 + mov ebx,call32_handle_interrupt+CS_BASE + sub ebx,edi + +.make_ijb: + stosd + sub [edi-2],cl ; Interrupt # + xchg eax,ebx + sub eax,8 + stosd + xchg eax,ebx + loop .make_ijb + + ; Now everything is set up for interrupts... + + push dword (BOUNCE_SEG << 4) ; Bounce buffer address + push dword call32_syscall+CS_BASE ; Syscall entry point + sti ; Interrupts OK now + call pm_entry-CS_BASE ; Run the program... + + ; ... on return ... + mov [Return+CS_BASE],eax + + ; ... fall through to call32_exit ... + +call32_exit: + mov bx,call32_done ; Return to command loop + +call32_enter_rm: + cli + cld + mov [PMESP+CS_BASE],esp ; Save exit %esp + xor esp,esp ; Make sure the high bits are zero + jmp 08h:.in_pm16 ; Return to 16-bit mode first + + bits 16 +.in_pm16: + mov ax,10h ; Real-mode-like segment + mov es,ax + mov ds,ax + mov ss,ax + mov fs,ax + mov gs,ax + + lidt [call32_rmidt] ; Real-mode IDT (rm needs no GDT) + mov eax,cr0 + and al,~1 + mov cr0,eax + jmp MY_CS:.in_rm + +.in_rm: ; Back in real mode + mov ax,cs ; Set up sane segments + mov ds,ax + mov es,ax + mov fs,ax + mov gs,ax + lss sp,[SavedSSSP] ; Restore stack + jmp bx ; Go to whereever we need to go... + +call32_done: + call disable_a20 + sti + mov ax,[Return] + ret + +; +; 16-bit support code +; + bits 16 + +; +; 16-bit interrupt-handling code +; +call32_int_rm: + pushf ; Flags on stack + push cs ; Return segment + push word .cont ; Return address + push dword edx ; Segment:offset of IVT entry + retf ; Invoke IVT routine +.cont: ; ... on resume ... + mov ebx,call32_int_resume+CS_BASE + jmp call32_enter_pm ; Go back to PM + +; +; 16-bit system call handling code +; +call32_sys_rm: + pop gs + pop fs + pop es + pop ds + popad + popfd + retf ; Invoke routine +.return: + pushfd + pushad + push ds + push es + push fs + push gs + mov ebx,call32_sys_resume+CS_BASE + jmp call32_enter_pm + +; +; 32-bit support code +; + bits 32 + +; +; This is invoked on getting an interrupt in protected mode. At +; this point, we need to context-switch to real mode and invoke +; the interrupt routine. +; +; When this gets invoked, the registers are saved on the stack and +; AL contains the register number. +; +call32_handle_interrupt: + movzx eax,al + xor ebx,ebx ; Actually makes the code smaller + mov edx,[ebx+eax*4] ; Get the segment:offset of the routine + mov bx,call32_int_rm + jmp call32_enter_rm ; Go to real mode + +call32_int_resume: + popad + iret + +; +; Syscall invocation. We manifest a structure on the real-mode stack, +; containing the call32sys_t structure from as well as +; the following entries (from low to high address): +; - Target offset +; - Target segment +; - Return offset +; - Return segment (== real mode cs) +; - Return flags +; +call32_syscall: + pushfd ; Save IF among other things... + pushad ; We only need to save some, but... + cld + + movzx edi,word [SavedSSSP+CS_BASE] + movzx eax,word [SavedSSSP+CS_BASE+2] + sub edi,54 ; Allocate 54 bytes + mov [SavedSSSP+CS_BASE],di + shl eax,4 + add edi,eax ; Create linear address + + mov esi,[esp+11*4] ; Source regs + xor ecx,ecx + mov cl,11 ; 44 bytes to copy + rep movsd + + movzx eax,byte [esp+10*4] ; Interrupt number + ; ecx == 0 here; adding it to the EA makes the + ; encoding smaller + mov eax,[ecx+eax*4] ; Get IVT entry + stosd ; Save in stack frame + mov eax,call32_sys_rm.return + (MY_CS << 16) ; Return seg:offs + stosd ; Save in stack frame + mov eax,[edi-12] ; Return flags + and eax,0x200cd7 ; Mask (potentially) unsafe flags + mov [edi-12],eax ; Primary flags entry + stosw ; Return flags + + mov bx,call32_sys_rm + jmp call32_enter_rm ; Go to real mode + + ; On return, the 44-byte return structure is on the + ; real-mode stack. +call32_sys_resume: + movzx esi,word [SavedSSSP+CS_BASE] + movzx eax,word [SavedSSSP+CS_BASE+2] + mov edi,[esp+12*4] ; Dest regs + shl eax,4 + add esi,eax ; Create linear address + and edi,edi ; NULL pointer? + jnz .do_copy +.no_copy: mov edi,esi ; Do a dummy copy-to-self +.do_copy: xor ecx,ecx + mov cl,11 ; 44 bytes + rep movsd ; Copy register block + + add dword [SavedSSSP+CS_BASE],44 ; Remove from stack + + popad + popfd + ret ; Return to 32-bit program diff --git a/memdisk/memdisk_chs.asm b/memdisk/memdisk_chs.asm new file mode 100644 index 0000000..4b06a85 --- /dev/null +++ b/memdisk/memdisk_chs.asm @@ -0,0 +1,2 @@ +%define EDD 0 +%include "memdisk.inc" diff --git a/memdisk/memdisk_edd.asm b/memdisk/memdisk_edd.asm new file mode 100644 index 0000000..d2e7b1c --- /dev/null +++ b/memdisk/memdisk_edd.asm @@ -0,0 +1,2 @@ +%define EDD 1 +%include "memdisk.inc" diff --git a/memdisk/memset.S b/memdisk/memset.S new file mode 100644 index 0000000..e641415 --- /dev/null +++ b/memdisk/memset.S @@ -0,0 +1,86 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * memset.S + * + * Reasonably efficient memset, using aligned transfers at least + * for the destination operand. + */ + + .globl memset + .type memset,@function + .text +memset: + jecxz 6f + + pushl %edi + pushl %ebx + pushl %eax /* Return value */ + + movl %eax,%edi + movb %dl,%dh + movzwl %dx,%eax + shll $16,%edx + orl %edx,%eax + + /* Initial alignment */ + movl %edi,%edx + shrl $1,%edx + jnc 1f + stosb + decl %ecx +1: + movb %cl,%bl + cmpl $2,%ecx + jb 3f + shrl $1,%edx + jnc 2f + stosw + subl $2,%ecx +2: + /* Bulk transfer */ + movb %cl,%bl + shrl $2,%ecx + rep; stosl + + testb $2,%bl + jz 4f + stosw +3: +4: + testb $1,%bl + jz 5f + stosb +5: + popl %eax /* Return value */ + popl %ebx + popl %edi +6: + ret + + .size memset, .-memset diff --git a/memdisk/msetup.c b/memdisk/msetup.c new file mode 100644 index 0000000..e511117 --- /dev/null +++ b/memdisk/msetup.c @@ -0,0 +1,178 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2001-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * msetup.c + * + * Initialization code for memory-based disk + */ + +#include +#ifdef TEST +# include +# include +#else +# include "memdisk.h" +# include "conio.h" +#endif +#include "e820.h" + +uint32_t dos_mem = 0; /* 0-1MB */ +uint32_t low_mem = 0; /* 1-16MB */ +uint32_t high_mem = 0; /* 16+ MB */ + +#ifndef TEST + +static inline int get_e820(void) +{ + struct e820_info { + uint64_t base; + uint64_t len; + uint32_t type; + } *buf = sys_bounce; + uint32_t copied; + int range_count = 0; + com32sys_t regs; + + memset(®s, 0, sizeof regs); + memset(buf, 0, sizeof *buf); + + do { + regs.eax.l = 0x0000e820; + regs.ecx.l = sizeof(*buf); + regs.edx.l = 0x534d4150; + regs.edi.w[0] = OFFS(buf); + regs.es = SEG(buf); + + syscall(0x15, ®s, ®s); + copied = (regs.eflags.l & 1) ? 0 : regs.ecx.l; + + if ( regs.eax.l != 0x534d4150 || copied < 20 ) + break; + + printf("e820: %08x%08x %08x%08x %d\n", + (uint32_t)(buf->base >> 32), (uint32_t)buf->base, + (uint32_t)(buf->len >> 32), (uint32_t)buf->len, + buf->type); + + insertrange(buf->base, buf->len, buf->type); + range_count++; + + } while ( regs.ebx.l ); + + return !range_count; +} + +static inline void get_dos_mem(void) +{ + com32sys_t regs; + + memset(®s, 0, sizeof regs); + syscall(0x12, ®s, ®s); + insertrange(0, (uint64_t)((uint32_t)regs.eax.w[0] << 10), 1); + printf(" DOS: %d K\n", regs.eax.w[0]); +} + +static inline int get_e801(void) +{ + int err; + com32sys_t regs; + + memset(®s, 0, sizeof regs); + + regs.eax.w[0] = 0xe801; + syscall(0x15, ®s, ®s); + + if ( !(err = regs.eflags.l & 1) ) { + if ( regs.eax.w[0] ) { + insertrange(0x100000, (uint64_t)((uint32_t)regs.eax.w[0] << 10), 1); + } + if ( regs.ebx.w[0] ) { + insertrange(0x1000000, (uint64_t)((uint32_t)regs.ebx.w[0] << 16), 1); + } + + printf("e801: %04x %04x\n", regs.eax.w[0], regs.ebx.w[0]); + } + + return err; +} + +static inline int get_88(void) +{ + com32sys_t regs; + int err; + + memset(®s, 0, sizeof regs); + + regs.eax.b[1] = 0x88; + syscall(0x15, ®s, ®s); + + + if ( !(err = regs.eflags.l & 1) ) { + if ( regs.eax.w[0] ) { + insertrange(0x100000, (uint64_t)((uint32_t)regs.eax.w[0] << 10), 1); + } + + printf(" 88: %04x\n", regs.eax.w[0]); + } + + return err; +} + +void get_mem(void) +{ + if ( get_e820() ) { + get_dos_mem(); + if ( get_e801() ) { + if ( get_88() ) { + puts("MEMDISK: Unable to obtain memory map\n"); + die(); + } + } + } +} + +#endif /* TEST */ + +#define PW(x) (1ULL << (x)) + +void parse_mem(void) +{ + struct e820range *ep; + + dos_mem = low_mem = high_mem = 0; + + /* Derive "dos mem", "high mem", and "low mem" from the range array */ + for ( ep = ranges ; ep->type != -1U ; ep++ ) { + if ( ep->type == 1 ) { + /* Only look at memory ranges */ + if ( ep->start == 0 ) { + if ( ep[1].start > PW(20) ) + dos_mem = PW(20); + else + dos_mem = ep[1].start; + } + if ( ep->start <= PW(20) && ep[1].start > PW(20) ) { + if ( ep[1].start > PW(24) ) + low_mem = PW(24) - PW(20); + else + low_mem = ep[1].start - PW(20); + } + if ( ep->start <= PW(24) && ep[1].start > PW(24) ) { + if ( ep[1].start > PW(32) ) + high_mem = PW(32) - PW(24); + else + high_mem = ep[1].start - PW(24); + } + } + } +} diff --git a/memdisk/postprocess.pl b/memdisk/postprocess.pl new file mode 100755 index 0000000..fcda478 --- /dev/null +++ b/memdisk/postprocess.pl @@ -0,0 +1,58 @@ +#!/usr/bin/perl +## ----------------------------------------------------------------------- +## +## Copyright 2001-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +# +# Postprocess the memdisk binary. Used during the 'make' process. +# We write memdisk16.bin out to the final memdisk kernel, pad it to an +# integral 512-byte sector length, write this number of sectors into the +# kernel header field "setup_sects", then append memdisk32.bin + +eval { use bytes; }; + +($out,$file16,$file32) = @ARGV; + +open(OUT, "> $out\0") or die "$0: Cannot create file: $out\n"; +eval { binmode OUT; }; +open(FILE, "< $file16\0") or die "$0: Cannot open file: $file16\n"; +eval { binmode FILE }; + +@info = stat(FILE); +$size = $info[7]; + +$sectors = ($size + 511) >> 9; +$xsize = $sectors << 9; + +read(FILE, $f16, $size); + +print OUT $f16; + +if ( $size != $xsize ) { + # Pad to a sector boundary + print OUT "\0" x ($xsize-$size); +} + +seek(OUT, 0x1f1, SEEK_SET); # setup_sects +# All sectors are setup except the first +print OUT pack("C", $sectors-1); + +seek(OUT, $xsize, SEEK_SET); +close(FILE); + +open(FILE, "+< $file32\0") or die "$0: Cannot open file: $file32\n"; + +while ( ($n = read(FILE, $f32, 65536)) > 0 ) { + print OUT $f32; +} + +close(FILE); +close(OUT); diff --git a/memdisk/setup.c b/memdisk/setup.c new file mode 100644 index 0000000..94a8011 --- /dev/null +++ b/memdisk/setup.c @@ -0,0 +1,1075 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2001-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#include +#include "e820.h" +#include "conio.h" +#include "version.h" +#include "memdisk.h" +#include "../version.h" + +const char memdisk_version[] = +"MEMDISK " VERSION_STR " " DATE; +const char copyright[] = +"Copyright " FIRSTYEAR "-" YEAR_STR " H. Peter Anvin et al"; + +extern const char _binary_memdisk_chs_bin_start[]; +extern const char _binary_memdisk_chs_bin_end[]; +extern const char _binary_memdisk_chs_bin_size[]; +extern const char _binary_memdisk_edd_bin_start[]; +extern const char _binary_memdisk_edd_bin_end[]; +extern const char _binary_memdisk_edd_bin_size[]; + +struct memdisk_header { + uint16_t int13_offs; + uint16_t int15_offs; + uint16_t patch_offs; + uint16_t total_size; + uint16_t iret_offs; +}; + +/* The Disk Parameter Table may be required */ +typedef union { + struct hd_dpt { + uint16_t max_cyl; /* Max cylinder */ + uint8_t max_head; /* Max head */ + uint8_t junk1[5]; /* Obsolete junk, leave at zero */ + uint8_t ctrl; /* Control byte */ + uint8_t junk2[7]; /* More obsolete junk */ + } hd; + struct fd_dpt { + uint8_t specify1; /* "First specify byte" */ + uint8_t specify2; /* "Second specify byte" */ + uint8_t delay; /* Delay until motor turn off */ + uint8_t sectors; /* Sectors/track */ + + uint8_t bps; /* Bytes/sector (02h = 512) */ + uint8_t isgap; /* Length of intersector gap */ + uint8_t dlen; /* Data length (0FFh) */ + uint8_t fgap; /* Formatting gap */ + + uint8_t ffill; /* Format fill byte */ + uint8_t settle; /* Head settle time (ms) */ + uint8_t mstart; /* Motor start time */ + uint8_t maxtrack; /* Maximum track number */ + + uint8_t rate; /* Data transfer rate */ + uint8_t cmos; /* CMOS type */ + uint8_t pad[2]; + + uint32_t old_fd_dpt; /* Extension: pointer to old INT 1Eh */ + } fd; +} dpt_t; + +/* EDD disk parameter table */ +struct edd_dpt { + uint16_t len; /* Length of table */ + uint16_t flags; /* Information flags */ + uint32_t c; /* Physical cylinders (count!) */ + uint32_t h; /* Physical heads (count!) */ + uint32_t s; /* Physical sectors/track (count!) */ + uint64_t sectors; /* Total sectors */ + uint16_t bytespersec; /* Bytes/sector */ + uint16_t dpte_off, dpte_seg; /* DPTE pointer */ +}; + +struct patch_area { + uint32_t diskbuf; + uint32_t disksize; + uint16_t cmdline_off, cmdline_seg; + + uint32_t oldint13; + uint32_t oldint15; + + uint16_t olddosmem; + uint8_t bootloaderid; + uint8_t _pad1; + + uint16_t dpt_ptr; + /* End of the official MemDisk_Info */ + uint16_t memint1588; + + uint16_t cylinders; + uint16_t heads; + uint32_t sectors; + + uint32_t mem1mb; + uint32_t mem16mb; + + uint8_t driveno; + uint8_t drivetype; + uint8_t drivecnt; + uint8_t configflags; + +#define CONFIG_READONLY 0x01 +#define CONFIG_RAW 0x02 +#define CONFIG_SAFEINT 0x04 +#define CONFIG_BIGRAW 0x08 /* MUST be 8! */ +#define CONFIG_MODEMASK 0x0e + + uint16_t mystack; + uint16_t statusptr; + + dpt_t dpt; + struct edd_dpt edd_dpt; +}; + +/* This is the header in the boot sector/setup area */ +struct setup_header { + char cmdline[0x1f1]; + uint8_t setup_secs; + uint16_t syssize; + uint16_t swap_dev; + uint16_t ram_size; + uint16_t vid_mode; + uint16_t root_dev; + uint16_t boot_flag; + uint16_t jump; + char header[4]; + uint16_t version; + uint32_t realmode_swtch; + uint32_t start_sys; + uint8_t type_of_loader; + uint8_t loadflags; + uint16_t setup_move_size; + uint32_t code32_start; + uint32_t ramdisk_image; + uint32_t ramdisk_size; + uint32_t bootsect_kludge; + uint16_t head_end_ptr; + uint16_t pad1; + uint32_t cmd_line_ptr; + uint32_t initrd_addr_max; + uint32_t esdi; + uint32_t edx; +}; + +struct setup_header * const shdr = (struct setup_header *)(LOW_SEG << 4); + +/* Access to high memory */ + +/* Access to objects in the zero page */ +static inline void +wrz_8(uint32_t addr, uint8_t data) +{ + *((uint8_t *)addr) = data; +} +static inline void +wrz_16(uint32_t addr, uint16_t data) +{ + *((uint16_t *)addr) = data; +} +static inline void +wrz_32(uint32_t addr, uint32_t data) +{ + *((uint32_t *)addr) = data; +} +static inline uint8_t +rdz_8(uint32_t addr) +{ + return *((uint8_t *)addr); +} +static inline uint16_t +rdz_16(uint32_t addr) +{ + return *((uint16_t *)addr); +} +static inline uint32_t +rdz_32(uint32_t addr) +{ + return *((uint32_t *)addr); +} + +/* Addresses in the zero page */ +#define BIOS_INT13 (0x13*4) /* INT 13h vector */ +#define BIOS_INT15 (0x15*4) /* INT 15h vector */ +#define BIOS_INT1E (0x1E*4) /* INT 1Eh vector */ +#define BIOS_INT40 (0x40*4) /* INT 13h vector */ +#define BIOS_INT41 (0x41*4) /* INT 41h vector */ +#define BIOS_INT46 (0x46*4) /* INT 46h vector */ +#define BIOS_BASEMEM 0x413 /* Amount of DOS memory */ +#define BIOS_EQUIP 0x410 /* BIOS equipment list */ +#define BIOS_HD_COUNT 0x475 /* Number of hard drives present */ + +/* + * Routine to seek for a command-line item and return a pointer + * to the data portion, if present + */ + +/* Magic return values */ +#define CMD_NOTFOUND ((char *)-1) /* Not found */ +#define CMD_BOOL ((char *)-2) /* Found boolean option */ +#define CMD_HASDATA(X) ((int)(X) >= 0) + +const char *getcmditem(const char *what) +{ + const char *p; + const char *wp = what; + int match = 0; + + for ( p = shdr->cmdline ; *p ; p++ ) { + switch ( match ) { + case 0: /* Ground state */ + if ( *p == ' ' ) + break; + + wp = what; + match = 1; + /* Fall through */ + + case 1: /* Matching */ + if ( *wp == '\0' ) { + if ( *p == '=' ) + return p+1; + else if ( *p == ' ' ) + return CMD_BOOL; + else { + match = 2; + break; + } + } + if ( *p != *wp++ ) + match = 2; + break; + + case 2: /* Mismatch, skip rest of option */ + if ( *p == ' ' ) + match = 0; /* Next option */ + break; + } + } + + /* Check for matching string at end of line */ + if ( match == 1 && *wp == '\0' ) + return CMD_BOOL; + + return CMD_NOTFOUND; +} + +/* + * Check to see if this is a gzip image + */ +#define UNZIP_ALIGN 512 + +extern void _end; /* Symbol signalling end of data */ + +void unzip_if_needed(uint32_t *where_p, uint32_t *size_p) +{ + uint32_t where = *where_p; + uint32_t size = *size_p; + uint32_t zbytes; + uint32_t startrange, endrange; + uint32_t gzdatasize, gzwhere; + uint32_t orig_crc, offset; + uint32_t target = 0; + int i, okmem; + + /* Is it a gzip image? */ + if (check_zip((void *)where, size, &zbytes, &gzdatasize, + &orig_crc, &offset) == 0) { + + if (offset + zbytes > size) { + /* Assertion failure; check_zip is supposed to guarantee this + never happens. */ + puts("internal error: check_zip returned nonsense\n"); + die(); + } + + /* Find a good place to put it: search memory ranges in descending order + until we find one that is legal and fits */ + okmem = 0; + for ( i = nranges-1 ; i >= 0 ; i-- ) { + /* We can't use > 4G memory (32 bits only.) Truncate to 2^32-1 + so we don't have to deal with funny wraparound issues. */ + + /* Must be memory */ + if ( ranges[i].type != 1 ) + continue; + + /* Range start */ + if ( ranges[i].start >= 0xFFFFFFFF ) + continue; + + startrange = (uint32_t)ranges[i].start; + + /* Range end (0 for end means 2^64) */ + endrange = ((ranges[i+1].start >= 0xFFFFFFFF || + ranges[i+1].start == 0) + ? 0xFFFFFFFF : (uint32_t)ranges[i+1].start); + + /* Make sure we don't overwrite ourselves */ + if ( startrange < (uint32_t)&_end ) + startrange = (uint32_t)&_end; + + /* Allow for alignment */ + startrange = (ranges[i].start + (UNZIP_ALIGN-1)) & ~(UNZIP_ALIGN-1); + + /* In case we just killed the whole range... */ + if ( startrange >= endrange ) + continue; + + /* Must be large enough... don't rely on gzwhere for this (wraparound) */ + if ( endrange-startrange < gzdatasize ) + continue; + + /* This is where the gz image should be put if we put it in this range */ + gzwhere = (endrange - gzdatasize) & ~(UNZIP_ALIGN-1); + + /* Cast to uint64_t just in case we're flush with the top byte */ + if ( (uint64_t)where+size >= gzwhere && where < endrange ) { + /* Need to move source data to avoid compressed/uncompressed overlap */ + uint32_t newwhere; + + if ( gzwhere-startrange < size ) + continue; /* Can't fit both old and new */ + + newwhere = (gzwhere - size) & ~(UNZIP_ALIGN-1); + printf("Moving compressed data from 0x%08x to 0x%08x\n", + where, newwhere); + + /* Our memcpy() is OK, because we always move from a higher + address to a lower one */ + memcpy((void *)newwhere, (void *)where, size); + where = newwhere; + } + + target = gzwhere; + okmem = 1; + break; + } + + if ( !okmem ) { + printf("Not enough memory to decompress image (need 0x%08x bytes)\n", + gzdatasize); + die(); + } + + printf("gzip image: decompressed addr 0x%08x, len 0x%08x: ", + target, gzdatasize); + + *size_p = gzdatasize; + *where_p = (uint32_t)unzip((void *)(where + offset), zbytes, + gzdatasize, orig_crc, (void *)target); + } +} + +/* + * Figure out the "geometry" of the disk in question + */ +struct geometry { + uint32_t sectors; /* 512-byte sector count */ + uint32_t c, h, s; /* C/H/S geometry */ + uint32_t offset; /* Byte offset for disk */ + uint8_t type; /* Type byte for INT 13h AH=08h */ + uint8_t driveno; /* Drive no */ + const char *hsrc, *ssrc; /* Origins of H and S geometries */ +}; + +/* Format of a DOS partition table entry */ +struct ptab_entry { + uint8_t active; + uint8_t start_h, start_s, start_c; + uint8_t type; + uint8_t end_h, end_s, end_c; + uint32_t start; + uint32_t size; +} __attribute__((packed)); + +/* Format of a FAT filesystem superblock */ +struct fat_extra { + uint8_t bs_drvnum; + uint8_t bs_resv1; + uint8_t bs_bootsig; + uint32_t bs_volid; + char bs_vollab[11]; + char bs_filsystype[8]; +} __attribute__((packed)); +struct fat_super { + uint8_t bs_jmpboot[3]; + char bs_oemname[8]; + uint16_t bpb_bytspersec; + uint8_t bpb_secperclus; + uint16_t bpb_rsvdseccnt; + uint8_t bpb_numfats; + uint16_t bpb_rootentcnt; + uint16_t bpb_totsec16; + uint8_t bpb_media; + uint16_t bpb_fatsz16; + uint16_t bpb_secpertrk; + uint16_t bpb_numheads; + uint32_t bpb_hiddsec; + uint32_t bpb_totsec32; + union { + struct { + struct fat_extra extra; + } fat16; + struct { + uint32_t bpb_fatsz32; + uint16_t bpb_extflags; + uint16_t bpb_fsver; + uint32_t bpb_rootclus; + uint16_t bpb_fsinfo; + uint16_t bpb_bkbootsec; + char bpb_reserved[12]; + /* Clever, eh? Same fields, different offset... */ + struct fat_extra extra; + } fat32 __attribute__((packed)); + } x; +} __attribute__((packed)); + +/* Format of a DOSEMU header */ +struct dosemu_header { + uint8_t magic[7]; /* DOSEMU\0 */ + uint32_t h; + uint32_t s; + uint32_t c; + uint32_t offset; + uint8_t pad[105]; +} __attribute__((packed)); + +#define FOUR(a,b,c,d) (((a) << 24)|((b) << 16)|((c) << 8)|(d)) + +const struct geometry *get_disk_image_geometry(uint32_t where, uint32_t size) +{ + static struct geometry hd_geometry; + struct dosemu_header dosemu; + unsigned int sectors, xsectors, v; + unsigned int offset; + int i; + const char *p; + + printf("command line: %s\n", shdr->cmdline); + + offset = 0; + if ( CMD_HASDATA(p = getcmditem("offset")) && (v = atou(p)) ) + offset = v; + + sectors = xsectors = (size-offset) >> 9; + + hd_geometry.hsrc = "guess"; + hd_geometry.ssrc = "guess"; + hd_geometry.sectors = sectors; + hd_geometry.offset = offset; + + /* Do we have a DOSEMU header? */ + memcpy(&dosemu, (char *)where+hd_geometry.offset, sizeof dosemu); + if ( !memcmp("DOSEMU", dosemu.magic, 7) ) { + /* Always a hard disk unless overruled by command-line options */ + hd_geometry.driveno = 0x80; + hd_geometry.type = 0; + hd_geometry.c = dosemu.c; + hd_geometry.h = dosemu.h; + hd_geometry.s = dosemu.s; + hd_geometry.offset += dosemu.offset; + sectors = (size-hd_geometry.offset) >> 9; + + hd_geometry.hsrc = hd_geometry.ssrc = "DOSEMU"; + } + + if ( CMD_HASDATA(p = getcmditem("c")) && (v = atou(p)) ) + hd_geometry.c = v; + if ( CMD_HASDATA(p = getcmditem("h")) && (v = atou(p)) ) { + hd_geometry.h = v; + hd_geometry.hsrc = "cmd"; + } + if ( CMD_HASDATA(p = getcmditem("s")) && (v = atou(p)) ) { + hd_geometry.s = v; + hd_geometry.ssrc = "cmd"; + } + + if ( !hd_geometry.h || !hd_geometry.s ) { + int h, s, max_h, max_s; + + max_h = hd_geometry.h; + max_s = hd_geometry.s; + + if (!(max_h|max_s)) { + /* Look for a FAT superblock and if we find something that looks + enough like one, use geometry from that. This takes care of + megafloppy images and unpartitioned hard disks. */ + const struct fat_extra *extra = NULL; + const struct fat_super *fs = (const struct fat_super *) + ((char *)where+hd_geometry.offset); + + if ((fs->bpb_media == 0xf0 || fs->bpb_media >= 0xf8) && + (fs->bs_jmpboot[0] == 0xe9 || fs->bs_jmpboot[0] == 0xeb) && + fs->bpb_bytspersec == 512 && + fs->bpb_numheads >= 1 && fs->bpb_numheads <= 256 && + fs->bpb_secpertrk >= 1 && fs->bpb_secpertrk <= 63) { + extra = fs->bpb_fatsz16 ? &fs->x.fat16.extra : &fs->x.fat32.extra; + if (!(extra->bs_bootsig == 0x29 && + extra->bs_filsystype[0] == 'F' && + extra->bs_filsystype[1] == 'A' && + extra->bs_filsystype[2] == 'T')) + extra = NULL; + } + if (extra) { + hd_geometry.driveno = extra->bs_drvnum & 0x80; + max_h = fs->bpb_numheads; + max_s = fs->bpb_secpertrk; + hd_geometry.hsrc = hd_geometry.ssrc = "FAT"; + } + } + + if (!(max_h|max_s)) { + /* No FAT filesystem found to steal geometry from... */ + if (sectors < 4096*2) { + int ok = 0; + unsigned int xsectors = sectors; + + hd_geometry.driveno = 0; /* Assume floppy */ + + while (!ok) { + /* Assume it's a floppy drive, guess a geometry */ + unsigned int type, track; + int c, h, s; + + if (xsectors < 320*2) { + c = 40; h = 1; type = 1; + } else if (xsectors < 640*2) { + c = 40; h = 2; type = 1; + } else if (xsectors < 1200*2) { + c = 80; h = 2; type = 3; + } else if (xsectors < 1440*2) { + c = 80; h = 2; type = 2; + } else if (xsectors < 2880*2) { + c = 80; h = 2; type = 4; + } else { + c = 80; h = 2; type = 6; + } + track = c*h; + while (c < 256) { + s = xsectors/track; + if (s < 63 && (xsectors % track) == 0) { + ok = 1; + break; + } + c++; + track += h; + } + if (ok) { + max_h = h; + max_s = s; + hd_geometry.hsrc = hd_geometry.ssrc = "fd"; + } else { + /* No valid floppy geometry, fake it by simulating broken + sectors at the end of the image... */ + xsectors++; + } + } + } else { + /* Assume it is a hard disk image and scan for a partition table */ + const struct ptab_entry *ptab = (const struct ptab_entry *) + ((char *)where+hd_geometry.offset+(512-2-4*16)); + + hd_geometry.driveno = 0x80; /* Assume hard disk */ + + if (*(uint16_t *)((char *)where+512-2) == 0xaa55) { + for ( i = 0 ; i < 4 ; i++ ) { + if ( ptab[i].type && !(ptab[i].active & 0x7f) ) { + s = (ptab[i].start_s & 0x3f); + h = ptab[i].start_h + 1; + + if ( max_h < h ) max_h = h; + if ( max_s < s ) max_s = s; + + s = (ptab[i].end_s & 0x3f); + h = ptab[i].end_h + 1; + + if ( max_h < h ) { + max_h = h; + hd_geometry.hsrc = "MBR"; + } + if ( max_s < s ) { + max_s = s; + hd_geometry.ssrc = "MBR"; + } + } + } + } + } + } + + if (!max_h) + max_h = xsectors > 2097152 ? 255 : 64; + if (!max_s) + max_s = xsectors > 2097152 ? 63 : 32; + + hd_geometry.h = max_h; + hd_geometry.s = max_s; + } + + if ( !hd_geometry.c ) + hd_geometry.c = xsectors/(hd_geometry.h*hd_geometry.s); + + if ( (p = getcmditem("floppy")) != CMD_NOTFOUND ) { + hd_geometry.driveno = CMD_HASDATA(p) ? atou(p) & 0x7f : 0; + } else if ( (p = getcmditem("harddisk")) != CMD_NOTFOUND ) { + hd_geometry.driveno = CMD_HASDATA(p) ? atou(p) | 0x80 : 0x80; + } + + if (hd_geometry.driveno & 0x80) { + hd_geometry.type = 0; /* Type = hard disk */ + } else { + if (hd_geometry.type == 0) + hd_geometry.type = 0x10; /* ATAPI floppy, e.g. LS-120 */ + } + + if ( (size-hd_geometry.offset) & 0x1ff ) { + puts("MEMDISK: Image has fractional end sector\n"); + } + if ( sectors % (hd_geometry.h*hd_geometry.s) ) { + puts("MEMDISK: Image seems to have fractional end cylinder\n"); + } + if ( (hd_geometry.c*hd_geometry.h*hd_geometry.s) > sectors ) { + puts("MEMDISK: Image appears to be truncated\n"); + } + + return &hd_geometry; +} + +/* + * Jump here if all hope is gone... + */ +void __attribute__((noreturn)) die(void) +{ + asm volatile("sti"); + for(;;) + asm volatile("hlt"); +} + +/* + * Find a $PnP installation check structure; return (ES << 16) + DI value + */ +static uint32_t pnp_install_check(void) +{ + uint32_t *seg; + unsigned char *p, csum; + int i, len; + + for (seg = (uint32_t *)0xf0000; seg < (uint32_t *)0x100000; seg += 4) { + if (*seg == ('$'+('P' << 8)+('n' << 16)+('P' << 24))) { + p = (unsigned char *)seg; + len = p[5]; + if (len < 0x21) + continue; + csum = 0; + for (i = len; i; i--) + csum += *p++; + if (csum != 0) + continue; + + return (0xf000 << 16) + (uint16_t)(unsigned long)seg; + } + } + + return 0; +} + +#define STACK_NEEDED 512 /* Number of bytes of stack */ + +/* + * Actual setup routine + * Returns the drive number (which is then passed in %dl to the + * called routine.) + */ +__cdecl syscall_t syscall; +void *sys_bounce; + +__cdecl void setup(__cdecl syscall_t cs_syscall, void *cs_bounce) +{ + unsigned int bin_size; + char *memdisk_hook; + struct memdisk_header *hptr; + struct patch_area *pptr; + uint16_t driverseg; + uint32_t driverptr, driveraddr; + uint16_t dosmem_k; + uint32_t stddosmem; + const struct geometry *geometry; + int total_size, cmdlinelen; + com32sys_t regs; + uint32_t ramdisk_image, ramdisk_size; + int bios_drives; + int do_edd = 1; /* 0 = no, 1 = yes, default is yes */ + int no_bpt; /* No valid BPT presented */ + + /* Set up global variables */ + syscall = cs_syscall; + sys_bounce = cs_bounce; + + /* Show signs of life */ + printf("%s %s\n", memdisk_version, copyright); + + if ( !shdr->ramdisk_image || !shdr->ramdisk_size ) { + puts("MEMDISK: No ramdisk image specified!\n"); + die(); + } + + ramdisk_image = shdr->ramdisk_image; + ramdisk_size = shdr->ramdisk_size; + + e820map_init(); /* Initialize memory data structure */ + get_mem(); /* Query BIOS for memory map */ + parse_mem(); /* Parse memory map */ + + printf("Ramdisk at 0x%08x, length 0x%08x\n", + ramdisk_image, ramdisk_size); + + unzip_if_needed(&ramdisk_image, &ramdisk_size); + + geometry = get_disk_image_geometry(ramdisk_image, ramdisk_size); + + if (getcmditem("edd") != CMD_NOTFOUND || + getcmditem("ebios") != CMD_NOTFOUND) + do_edd = 1; + else if (getcmditem("noedd") != CMD_NOTFOUND || + getcmditem("noebios") != CMD_NOTFOUND || + getcmditem("cbios") != CMD_NOTFOUND) + do_edd = 0; + else + do_edd = (geometry->driveno & 0x80) ? 1 : 0; + + /* Choose the appropriate installable memdisk hook */ + if (do_edd) { + bin_size = (int) &_binary_memdisk_edd_bin_size; + memdisk_hook = (char *) &_binary_memdisk_edd_bin_start; + } else { + bin_size = (int) &_binary_memdisk_chs_bin_size; + memdisk_hook = (char *) &_binary_memdisk_chs_bin_start; + } + + /* Reserve the ramdisk memory */ + insertrange(ramdisk_image, ramdisk_size, 2); + parse_mem(); /* Recompute variables */ + + /* Figure out where it needs to go */ + hptr = (struct memdisk_header *) memdisk_hook; + pptr = (struct patch_area *)(memdisk_hook + hptr->patch_offs); + + dosmem_k = rdz_16(BIOS_BASEMEM); + pptr->olddosmem = dosmem_k; + stddosmem = dosmem_k << 10; + /* If INT 15 E820 and INT 12 disagree, go with the most conservative */ + if ( stddosmem > dos_mem ) + stddosmem = dos_mem; + + pptr->driveno = geometry->driveno; + pptr->drivetype = geometry->type; + pptr->cylinders = geometry->c; + pptr->heads = geometry->h; + pptr->sectors = geometry->s; + pptr->disksize = geometry->sectors; + pptr->diskbuf = ramdisk_image + geometry->offset; + pptr->statusptr = (geometry->driveno & 0x80) ? 0x474 : 0x441; + + pptr->bootloaderid = shdr->type_of_loader; + + pptr->configflags = CONFIG_SAFEINT; /* Default */ + /* Set config flags */ + if ( getcmditem("ro") != CMD_NOTFOUND ) { + pptr->configflags |= CONFIG_READONLY; + } + if ( getcmditem("raw") != CMD_NOTFOUND ) { + pptr->configflags &= ~CONFIG_MODEMASK; + pptr->configflags |= CONFIG_RAW; + } + if ( getcmditem("bigraw") != CMD_NOTFOUND ) { + pptr->configflags &= ~CONFIG_MODEMASK; + pptr->configflags |= CONFIG_BIGRAW|CONFIG_RAW; + } + if ( getcmditem("int") != CMD_NOTFOUND ) { + pptr->configflags &= ~CONFIG_MODEMASK; + /* pptr->configflags |= 0; */ + } + if ( getcmditem("safeint") != CMD_NOTFOUND ) { + pptr->configflags &= ~CONFIG_MODEMASK; + pptr->configflags |= CONFIG_SAFEINT; + } + + printf("Disk is %s%d, %u%s K, C/H/S = %u/%u/%u (%s/%s), EDD %s, %s\n", + (geometry->driveno & 0x80) ? "hd" : "fd", + geometry->driveno & 0x7f, + geometry->sectors >> 1, + (geometry->sectors & 1) ? ".5" : "", + geometry->c, geometry->h, geometry->s, + geometry->hsrc, geometry->ssrc, + do_edd ? "on" : "off", + pptr->configflags & CONFIG_READONLY ? "ro" : "rw"); + + puts("Using "); + switch (pptr->configflags & CONFIG_MODEMASK) { + case 0: + puts("standard INT 15h"); + break; + case CONFIG_SAFEINT: + puts("safe INT 15h"); + break; + case CONFIG_RAW: + puts("raw"); + break; + case CONFIG_RAW|CONFIG_BIGRAW: + puts("big real mode raw"); + break; + default: + printf("unknown %#x", pptr->configflags & CONFIG_MODEMASK); + break; + } + puts(" access to high memory\n"); + + /* Set up a drive parameter table */ + if ( geometry->driveno & 0x80 ) { + /* Hard disk */ + pptr->dpt.hd.max_cyl = geometry->c-1; + pptr->dpt.hd.max_head = geometry->h-1; + pptr->dpt.hd.ctrl = (geometry->h > 8) ? 0x08: 0; + } else { + /* Floppy - most of these fields are bogus and mimic + a 1.44 MB floppy drive */ + pptr->dpt.fd.specify1 = 0xdf; + pptr->dpt.fd.specify2 = 0x02; + pptr->dpt.fd.delay = 0x25; + pptr->dpt.fd.sectors = geometry->s; + pptr->dpt.fd.bps = 0x02; + pptr->dpt.fd.isgap = 0x12; + pptr->dpt.fd.dlen = 0xff; + pptr->dpt.fd.fgap = 0x6c; + pptr->dpt.fd.ffill = 0xf6; + pptr->dpt.fd.settle = 0x0f; + pptr->dpt.fd.mstart = 0x05; + pptr->dpt.fd.maxtrack = geometry->c-1; + pptr->dpt.fd.cmos = geometry->type > 5 ? 5 : geometry->type; + + pptr->dpt.fd.old_fd_dpt = rdz_32(BIOS_INT1E); + } + + /* Set up an EDD drive parameter table */ + pptr->edd_dpt.sectors = geometry->sectors; + /* The EDD spec has this as <= 15482880 sectors (1024x240x63); + this seems to make very little sense. Try for something saner. */ + if (geometry->c <= 1024 && geometry->h <= 255 && geometry->s <= 63) { + pptr->edd_dpt.c = geometry->c; + pptr->edd_dpt.h = geometry->h; + pptr->edd_dpt.s = geometry->s; + pptr->edd_dpt.flags |= 0x0002; /* Geometry valid */ + } + if (!(geometry->driveno & 0x80)) { + /* Floppy drive. Mark it as a removable device with + media change notification; media is present. */ + pptr->edd_dpt.flags |= 0x0014; + } + + /* The size is given by hptr->total_size plus the size of the E820 + map -- 12 bytes per range; we may need as many as 2 additional + ranges (each insertrange() can worst-case turn 1 area into 3) + plus the terminating range, over what nranges currently show. */ + cmdlinelen = strlen(shdr->cmdline)+1; + total_size = hptr->total_size; /* Actual memdisk code */ + total_size += (nranges+3)*sizeof(ranges[0]); /* E820 memory ranges */ + total_size += cmdlinelen; /* Command line */ + total_size += STACK_NEEDED; /* Stack */ + printf("Total size needed = %u bytes, allocating %uK\n", + total_size, (total_size+0x3ff) >> 10); + + if ( total_size > dos_mem ) { + puts("MEMDISK: Insufficient low memory\n"); + die(); + } + + driveraddr = stddosmem - total_size; + driveraddr &= ~0x3FF; + + printf("Old dos memory at 0x%05x (map says 0x%05x), loading at 0x%05x\n", + stddosmem, dos_mem, driveraddr); + + /* Reserve this range of memory */ + wrz_16(BIOS_BASEMEM, driveraddr >> 10); + insertrange(driveraddr, dos_mem-driveraddr, 2); + parse_mem(); + + pptr->mem1mb = low_mem >> 10; + pptr->mem16mb = high_mem >> 16; + if ( low_mem == (15 << 20) ) { + /* lowmem maxed out */ + uint32_t int1588mem = (high_mem >> 10)+(low_mem >> 10); + pptr->memint1588 = (int1588mem > 0xffff) ? 0xffff : int1588mem; + } else { + pptr->memint1588 = low_mem >> 10; + } + + printf("1588: 0x%04x 15E801: 0x%04x 0x%04x\n", + pptr->memint1588, pptr->mem1mb, pptr->mem16mb); + + driverseg = driveraddr >> 4; + driverptr = driverseg << 16; + + /* Anything beyond the end is for the stack */ + pptr->mystack = (uint16_t)(stddosmem-driveraddr); + + pptr->oldint13 = rdz_32(BIOS_INT13); + pptr->oldint15 = rdz_32(BIOS_INT15); + + /* Adjust the E820 table: if there are null ranges (type 0) + at the end, change them to type end of list (-1). + This is necessary for the driver to be able to report end + of list correctly. */ + while ( nranges && ranges[nranges-1].type == 0 ) { + ranges[--nranges].type = -1; + } + + if (getcmditem("nopass") != CMD_NOTFOUND) { + /* nopass specified - we're the only drive by definition */ + printf("nopass specified - we're the only drive\n"); + bios_drives = 0; + pptr->drivecnt = 0; + pptr->oldint13 = driverptr+hptr->iret_offs; + no_bpt = 1; + } else { + /* Query drive parameters of this type */ + memset(®s, 0, sizeof regs); + regs.es = 0; + regs.eax.b[1] = 0x08; + regs.edx.b[0] = geometry->driveno & 0x80; + syscall(0x13, ®s, ®s); + + /* Note: per suggestion from the Interrupt List, consider + INT 13 08 to have failed if the sector count in CL is zero. */ + if ((regs.eflags.l & 1) || !(regs.ecx.b[0] & 0x3f)) { + printf("INT 13 08: Failure, assuming this is the only drive\n"); + pptr->drivecnt = 0; + no_bpt = 1; + } else { + printf("INT 13 08: Success, count = %u, BPT = %04x:%04x\n", + regs.edx.b[0], regs.es, regs.edi.w[0]); + pptr->drivecnt = regs.edx.b[0]; + no_bpt = !(regs.es|regs.edi.w[0]); + } + + /* Compare what INT 13h returned with the appropriate equipment byte */ + if ( geometry->driveno & 0x80 ) { + bios_drives = rdz_8(BIOS_HD_COUNT); + } else { + uint8_t equip = rdz_8(BIOS_EQUIP); + + if (equip & 1) + bios_drives = (equip >> 6)+1; + else + bios_drives = 0; + } + + if (pptr->drivecnt > bios_drives) { + printf("BIOS equipment byte says count = %d, go with that\n", + bios_drives); + pptr->drivecnt = bios_drives; + } + } + + /* Add ourselves to the drive count */ + pptr->drivecnt++; + + /* Discontiguous drive space. There is no really good solution for this. */ + if ( pptr->drivecnt <= (geometry->driveno & 0x7f) ) + pptr->drivecnt = (geometry->driveno & 0x7f) + 1; + + /* Pointer to the command line */ + pptr->cmdline_off = bin_size + (nranges+1)*sizeof(ranges[0]); + pptr->cmdline_seg = driverseg; + + /* Copy driver followed by E820 table followed by command line */ + { + unsigned char *dpp = (unsigned char *)(driverseg << 4); + + /* Adjust these pointers to point to the installed image */ + /* Careful about the order here... the image isn't copied yet! */ + pptr = (struct patch_area *)(dpp + hptr->patch_offs); + hptr = (struct memdisk_header *)dpp; + + /* Actually copy to low memory */ + dpp = mempcpy(dpp, memdisk_hook, bin_size); + dpp = mempcpy(dpp, ranges, (nranges+1)*sizeof(ranges[0])); + dpp = mempcpy(dpp, shdr->cmdline, cmdlinelen+1); + } + + /* Update various BIOS magic data areas (gotta love this shit) */ + + if ( geometry->driveno & 0x80 ) { + /* Update BIOS hard disk count */ + uint8_t nhd = pptr->drivecnt; + + if ( nhd > 128 ) + nhd = 128; + + wrz_8(BIOS_HD_COUNT, nhd); + } else { + /* Update BIOS floppy disk count */ + uint8_t equip = rdz_8(BIOS_EQUIP); + uint8_t nflop = pptr->drivecnt; + + if ( nflop > 4 ) /* Limit of equipment byte */ + nflop = 4; + + equip &= 0x3E; + if ( nflop ) + equip |= ((nflop-1) << 6) | 0x01; + + wrz_8(BIOS_EQUIP, equip); + + /* Install DPT pointer if this was the only floppy */ + if (getcmditem("dpt") != CMD_NOTFOUND || + ((nflop == 1 || no_bpt) + && getcmditem("nodpt") == CMD_NOTFOUND)) { + /* Do install a replacement DPT into INT 1Eh */ + pptr->dpt_ptr = hptr->patch_offs + offsetof(struct patch_area, dpt); + } + } + + /* Install the interrupt handlers */ + printf("old: int13 = %08x int15 = %08x int1e = %08x\n", + rdz_32(BIOS_INT13), rdz_32(BIOS_INT15), rdz_32(BIOS_INT1E)); + + wrz_32(BIOS_INT13, driverptr+hptr->int13_offs); + wrz_32(BIOS_INT15, driverptr+hptr->int15_offs); + if (pptr->dpt_ptr) + wrz_32(BIOS_INT1E, driverptr+pptr->dpt_ptr); + + printf("new: int13 = %08x int15 = %08x int1e = %08x\n", + rdz_32(BIOS_INT13), rdz_32(BIOS_INT15), rdz_32(BIOS_INT1E)); + + /* Reboot into the new "disk"; this is also a test for the interrupt hooks */ + puts("Loading boot sector... "); + + memset(®s, 0, sizeof regs); + // regs.es = 0; + regs.eax.w[0] = 0x0201; /* Read sector */ + regs.ebx.w[0] = 0x7c00; /* 0000:7C00 */ + regs.ecx.w[0] = 1; /* One sector */ + regs.edx.w[0] = geometry->driveno; + syscall(0x13, ®s, ®s); + + if ( regs.eflags.l & 1 ) { + puts("MEMDISK: Failed to load new boot sector\n"); + die(); + } + + if ( getcmditem("pause") != CMD_NOTFOUND ) { + puts("press any key to boot... "); + regs.eax.w[0] = 0; + syscall(0x16, ®s, NULL); + } + + puts("booting...\n"); + + /* On return the assembly code will jump to the boot vector */ + shdr->esdi = pnp_install_check(); + shdr->edx = geometry->driveno; +} diff --git a/memdisk/start32.S b/memdisk/start32.S new file mode 100644 index 0000000..593ffb3 --- /dev/null +++ b/memdisk/start32.S @@ -0,0 +1,29 @@ +/* ----------------------------------------------------------------------- + * + * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * 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, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * Simple stub to get us to the right point in the 32-bit code; + * this module must be linked first + */ + .section ".init", "ax" + .globl _start +_start: + /* Zero the bss */ + cld + movl $__bss_start, %edi + movl $__bss_end, %ecx + subl %edi, %ecx + xorl %eax, %eax + shrl $2, %ecx + rep ; stosl + jmp setup diff --git a/memdisk/testdata1 b/memdisk/testdata1 new file mode 100644 index 0000000..34ab566 --- /dev/null +++ b/memdisk/testdata1 @@ -0,0 +1,13 @@ +0000000000000000 000000000009bc00 1 +000000000009bc00 0000000000004400 2 +00000000000e9800 0000000000016800 2 +0000000000100000 0000000006ee0000 1 +0000000006fe0000 000000000000fc00 3 +0000000006fefc00 0000000000000400 4 +0000000006ff0000 0000000000002000 2 +0000000006ff2000 000000000000e000 1 +0000000007000000 0000000000100000 2 +00000000fff00000 0000000000100000 2 + +0000000000586000 0000000000168000 2 +000000000009ba00 0000000000000200 2 diff --git a/memdisk/testdata2 b/memdisk/testdata2 new file mode 100644 index 0000000..8bec5bf --- /dev/null +++ b/memdisk/testdata2 @@ -0,0 +1,10 @@ +0000000000000000 000000000009bc00 1 +000000000009bc00 0000000000004400 2 +00000000000e9800 0000000000016800 2 +0000000000100000 0000000006ee0000 1 +0000000006fe0000 000000000000fc00 3 +0000000006fefc00 0000000000000400 4 +0000000006ff0000 0000000000002000 2 +0000000006ff2000 000000000000e000 1 +0000000007000000 0000000000100000 2 +00000000fff00000 0000000000100000 2 diff --git a/memdisk/testdata3 b/memdisk/testdata3 new file mode 100644 index 0000000..38a4502 --- /dev/null +++ b/memdisk/testdata3 @@ -0,0 +1,14 @@ +0000000000000000 000000000009bc00 1 +000000000009bc00 0000000000004400 2 +00000000000e9800 0000000000016800 2 +0000000000100000 0000000006ee0000 1 +0000000006fe0000 000000000000fc00 3 +0000000006fefc00 0000000000000400 4 +0000002000000000 0000001000000000 1 +0000000006ff0000 0000000000002000 2 +0000000006ff2000 000000000000e000 1 +0000000007000000 0000000000100000 2 +00000000fff00000 0000000000100000 2 + +0000000000586000 0000000000168000 2 +000000000009ba00 0000000000000200 2 diff --git a/memdisk/unzip.c b/memdisk/unzip.c new file mode 100644 index 0000000..15b3f7d --- /dev/null +++ b/memdisk/unzip.c @@ -0,0 +1,390 @@ +/* + * unzip.c + * + * This is a collection of several routines from gzip-1.0.3 + * adapted for Linux. + * + * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 + * puts by Nick Holloway 1993, better puts by Martin Mares 1995 + * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 + * + * Adapted for MEMDISK by H. Peter Anvin, April 2003 + */ + +#include +#include "memdisk.h" +#include "conio.h" + +/* + * gzip declarations + */ + +#define OF(args) args +#define STATIC static + +#define memzero(s, n) memset ((s), 0, (n)) + +typedef uint8_t uch; +typedef uint16_t ush; +typedef uint32_t ulg; + +#define WSIZE 0x8000 /* Window size must be at least 32k, */ + /* and a power of two */ + +static uch *inbuf; /* input pointer */ +static uch window[WSIZE]; /* sliding output window buffer */ + +static unsigned insize; /* total input bytes read */ +static unsigned inbytes; /* valid bytes in inbuf */ +static unsigned outcnt; /* bytes in output buffer */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond,msg) {if(!(cond)) error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +static int fill_inbuf(void); +static void flush_window(void); +static void error(char *m); +static void gzip_mark(void **); +static void gzip_release(void **); + +static ulg crc_32_tab[256]; + +/* Get byte from input buffer */ +static inline uch get_byte(void) +{ + if ( inbytes ) { + uch b = *inbuf++; + inbytes--; + return b; + } else { + return fill_inbuf(); /* Input buffer underrun */ + } +} + +/* Unget byte from input buffer */ +static inline void unget_byte(void) +{ + inbytes++; + inbuf--; +} + +static ulg bytes_out = 0; /* Number of bytes output */ +static uch *output_data; /* Output data pointer */ +static ulg output_size; /* Number of output bytes expected */ + +static void *malloc(int size); +static void free(void *where); + +static ulg free_mem_ptr, free_mem_end_ptr; + +#include "inflate.c" + +static void *malloc(int size) +{ + void *p; + + if (size < 0) error("malloc error"); + + free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */ + + p = (void *)free_mem_ptr; + free_mem_ptr += size; + + if (free_mem_ptr >= free_mem_end_ptr) + error("out of memory"); + + return p; +} + +static void free(void *where) +{ + /* Don't care */ + (void)where; +} + +static void gzip_mark(void **ptr) +{ + *ptr = (void *) free_mem_ptr; +} + +static void gzip_release(void **ptr) +{ + free_mem_ptr = (long) *ptr; +} + +/* =========================================================================== + * Fill the input buffer. This is called only when the buffer is empty + * and at least one byte is really needed. + */ +static int fill_inbuf(void) +{ + /* This should never happen. We have already pointed the algorithm + to all the data we have. */ + printf("failed\nDecompression error: ran out of input data\n"); + die(); +} + +/* =========================================================================== + * Write the output window window[0..outcnt-1] and update crc and bytes_out. + * (Used for the decompressed data only.) + */ +static void flush_window(void) +{ + ulg c = crc; /* temporary variable */ + unsigned n; + uch *in, *out, ch; + + if ( bytes_out+outcnt > output_size ) + error("output buffer overrun"); + + in = window; + out = output_data; + for (n = 0; n < outcnt; n++) { + ch = *out++ = *in++; + c = crc_32_tab[(c ^ ch) & 0xff] ^ (c >> 8); + } + crc = c; + output_data = out; + bytes_out += (ulg)outcnt; + outcnt = 0; +} + +static void error(char *x) +{ + printf("failed\nDecompression error: %s\n", x); + die(); +} + +/* GZIP header */ +struct gzip_header { + uint16_t magic; + uint8_t method; + uint8_t flags; + uint32_t timestamp; + uint8_t extra_flags; + uint8_t os_type; +} __attribute__ ((packed)); +/* (followed by optional and variable length "extra", "original name", + and "comment" fields) */ + +struct gzip_trailer { + uint32_t crc; + uint32_t dbytes; +} __attribute__ ((packed)); + +/* PKZIP header. See + * . + */ +struct pkzip_header { + uint32_t magic; + uint16_t version; + uint16_t flags; + uint16_t method; + uint16_t modified_time; + uint16_t modified_date; + uint32_t crc; + uint32_t zbytes; + uint32_t dbytes; + uint16_t filename_len; + uint16_t extra_len; +} __attribute__ ((packed)); +/* (followed by optional and variable length "filename" and "extra" + fields) */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +/* pkzip flag byte */ +#define PK_ENCRYPTED 0x01 /* bit 0 set: file is encrypted */ +#define PK_DATADESC 0x08 /* bit 3 set: file has trailing "data + descriptor" */ +#define PK_UNSUPPORTED 0xFFF0 /* All other bits must be zero */ + + +/* Return 0 if (indata, size) points to a ZIP file, and fill in + compressed data size, uncompressed data size, CRC, and offset of + data. + + If indata is not a ZIP file, return -1. */ +int check_zip(void *indata, uint32_t size, uint32_t *zbytes_p, + uint32_t *dbytes_p, uint32_t *orig_crc, uint32_t *offset_p) { + struct gzip_header *gzh = (struct gzip_header *)indata; + struct pkzip_header *pkzh = (struct pkzip_header *)indata; + uint32_t offset; + + if (gzh->magic == 0x8b1f) { + struct gzip_trailer *gzt = indata + size - sizeof (struct gzip_trailer); + /* We only support method #8, DEFLATED */ + if (gzh->method != 8) { + error("gzip file uses invalid method"); + return -1; + } + if (gzh->flags & ENCRYPTED) { + error("gzip file is encrypted; not supported"); + return -1; + } + if (gzh->flags & CONTINUATION) { + error("gzip file is a continuation file; not supported"); + return -1; + } + if (gzh->flags & RESERVED) { + error("gzip file has unsupported flags"); + return -1; + } + offset = sizeof (*gzh); + if (gzh->flags & EXTRA_FIELD) { + /* Skip extra field */ + unsigned len = *(unsigned *)(indata + offset); + offset += 2 + len; + } + if (gzh->flags & ORIG_NAME) { + /* Discard the old name */ + uint8_t *p = indata; + while (p[offset] != 0 && offset < size) { + offset++; + } + offset++; + } + + if (gzh->flags & COMMENT) { + /* Discard the comment */ + uint8_t *p = indata; + while (p[offset] != 0 && offset < size) { + offset++; + } + offset++; + } + + if (offset > size) { + error ("gzip file corrupt"); + return -1; + } + *zbytes_p = size - offset - sizeof (struct gzip_trailer); + *dbytes_p = gzt->dbytes; + *orig_crc = gzt->crc; + *offset_p = offset; + return 0; + } + else if (pkzh->magic == 0x04034b50UL) { + /* Magic number matches pkzip file. */ + + offset = sizeof (*pkzh); + if (pkzh->flags & PK_ENCRYPTED) { + error("pkzip file is encrypted; not supported"); + return -1; + } + if (pkzh->flags & PK_DATADESC) { + error("pkzip file uses data_descriptor field; not supported"); + return -1; + } + if (pkzh->flags & PK_UNSUPPORTED) { + error("pkzip file has unsupported flags"); + return -1; + } + + /* We only support method #8, DEFLATED */ + if (pkzh->method != 8) { + error("pkzip file uses invalid method"); + return -1; + } + /* skip header */ + offset = sizeof (*pkzh); + /* skip filename */ + offset += pkzh->filename_len; + /* skip extra field */ + offset += pkzh->extra_len; + + if (offset + pkzh->zbytes > size) { + error ("pkzip file corrupt"); + return -1; + } + + *zbytes_p = pkzh->zbytes; + *dbytes_p = pkzh->dbytes; + *orig_crc = pkzh->crc; + *offset_p = offset; + return 0; + } + else { + /* Magic number does not match. */ + return -1; + } + + error ("Internal error in check_zip"); + return -1; +} + +/* + * Decompress the image, trying to flush the end of it as close + * to end_mem as possible. Return a pointer to the data block, + * and change datalen. + */ +extern void _end; + +void *unzip(void *indata, uint32_t zbytes, uint32_t dbytes, + uint32_t orig_crc, void *target) +{ + /* Set up the heap; it's the 64K after the bounce buffer */ + free_mem_ptr = (ulg)sys_bounce + 0x10000; + free_mem_end_ptr = free_mem_ptr + 0x10000; + + /* Set up input buffer */ + inbuf = indata; + /* Sometimes inflate() looks beyond the end of the compressed data, + but it always backs up before it is done. So we give it 4 bytes + of slack. */ + insize = inbytes = zbytes + 4; + + /* Set up output buffer */ + outcnt = 0; + output_data = target; + output_size = dbytes; + bytes_out = 0; + + makecrc(); + gunzip(); + + /* Verify that gunzip() consumed the entire input. */ + if (inbytes != 4) + error("compressed data length error"); + + /* Check the uncompressed data length and CRC. */ + if ( bytes_out != dbytes ) + error("uncompressed data length error"); + + if (orig_crc != CRC_VALUE) + error("crc error"); + + puts("ok\n"); + + return target; +} diff --git a/memdisk/version.h b/memdisk/version.h new file mode 100644 index 0000000..46bc7bb --- /dev/null +++ b/memdisk/version.h @@ -0,0 +1,25 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2002-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * version.h + * + * MEMDISK version data + */ + +#ifndef MEMDISK_VERSION_H +#define MEMDISK_VERSION_H + +#define FIRSTYEAR "2001" +#define COPYYEAR "2008" + +#endif diff --git a/memdump/Makefile b/memdump/Makefile new file mode 100644 index 0000000..05f2638 --- /dev/null +++ b/memdump/Makefile @@ -0,0 +1,67 @@ +## ----------------------------------------------------------------------- +## +## Copyright 2001-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +## +## memory dump utility +## + +topdir = .. +include $(topdir)/MCONFIG.embedded + +OPTFLAGS = +INCLUDES = -include code16.h -I. +LDFLAGS = -T com16.ld + +SRCS = main.c serial.c ymsend.c +OBJS = crt0.o $(patsubst %.c,%.o,$(notdir $(SRCS))) +LIBOBJS = conio.o memcpy.o memset.o skipatou.o strtoul.o \ + argv.o printf.o __divdi3.o __udivmoddi4.o + +.SUFFIXES: .c .o .i .s .S .elf .com + +TARGETS = memdump.com + +all: $(TARGETS) + +tidy dist: + -rm -f *.o *.i *.s *.a .*.d *.tmp *.elf + +clean: tidy + +spotless: clean + -rm -f *~ $(TARGETS) + +installer: + +memdump.elf: $(OBJS) libcom.a + $(LD) $(LDFLAGS) -o $@ $^ + +libcom.a: $(LIBOBJS) + -rm -f $@ + $(AR) cq $@ $^ + $(RANLIB) $@ + +memdump.com: memdump.elf + $(OBJCOPY) -O binary $< $@ + +%.o: %.c + $(CC) $(MAKEDEPS) $(CFLAGS) -c -o $@ $< +%.i: %.c + $(CC) $(MAKEDEPS) $(CFLAGS) -E -o $@ $< +%.s: %.c + $(CC) $(MAKEDEPS) $(CFLAGS) -S -o $@ $< +%.o: %.S + $(CC) $(MAKEDEPS) $(SFLAGS) -c -o $@ $< +%.s: %.S + $(CC) $(MAKEDEPS) $(SFLAGS) -E -o $@ $< + +-include .*.d *.tmp diff --git a/memdump/README b/memdump/README new file mode 100644 index 0000000..2b82577 --- /dev/null +++ b/memdump/README @@ -0,0 +1,19 @@ +This is a very simple COMBOOT program which can be used to dump memory +regions over a serial port. To use it, type on the SYSLINUX command +line: + +memdump , ,... + +For example: + +memdump 0 funnysystem- 0,0x600 0x9fc00,0x400 0xf0000,0x10000 + +... dumps three memory ranges (the standard BIOS memory ranges, often +useful) onto serial port 0. The can either be in the range 0-3 +for the standard BIOS serial port, or the I/O address of the UART. + +The data is transferred using the YMODEM protocol; the Unix +implementation of this protocol is called "rb" and is part of the +"lrzsz" (or "rzsz") package. If one uses a terminal program like +Minicom, there is often a way to invoke it from inside the terminal +program; in Minicom, this is done with the Ctrl-A R control sequence. diff --git a/memdump/__divdi3.c b/memdump/__divdi3.c new file mode 100644 index 0000000..3641396 --- /dev/null +++ b/memdump/__divdi3.c @@ -0,0 +1,29 @@ +/* + * arch/i386/libgcc/__divdi3.c + */ + +#include +#include + +extern uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t *rem); + +int64_t __divdi3(int64_t num, int64_t den) +{ + int minus = 0; + int64_t v; + + if ( num < 0 ) { + num = -num; + minus = 1; + } + if ( den < 0 ) { + den = -den; + minus ^= 1; + } + + v = __udivmoddi4(num, den, NULL); + if ( minus ) + v = -v; + + return v; +} diff --git a/memdump/__udivmoddi4.c b/memdump/__udivmoddi4.c new file mode 100644 index 0000000..8e7661f --- /dev/null +++ b/memdump/__udivmoddi4.c @@ -0,0 +1,31 @@ +#include + +uint64_t __udivmoddi4(uint64_t num, uint64_t den, uint64_t *rem_p) +{ + uint64_t quot = 0, qbit = 1; + + if ( den == 0 ) { + asm volatile("int $0"); + return 0; /* If trap returns... */ + } + + /* Left-justify denominator and count shift */ + while ( (int64_t)den >= 0 ) { + den <<= 1; + qbit <<= 1; + } + + while ( qbit ) { + if ( den <= num ) { + num -= den; + quot += qbit; + } + den >>= 1; + qbit >>= 1; + } + + if ( rem_p ) + *rem_p = num; + + return quot; +} diff --git a/memdump/argv.c b/memdump/argv.c new file mode 100644 index 0000000..a8c9874 --- /dev/null +++ b/memdump/argv.c @@ -0,0 +1,92 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * argv.c + * + * Parse a single C string into argc and argv (argc is return value.) + * memptr points to available memory. + */ + +#include +#include +#include + +#define ALIGN_UP(p,t) ((t *)(((uintptr_t)(p) + (sizeof(t)-1)) & ~(sizeof(t)-1))) + +extern char _end[]; /* Symbol created by linker */ +void *__mem_end = &_end; /* Global variable for use by malloc() */ + +int __parse_argv(char ***argv, const char *str) +{ + char *mem = __mem_end; + const char *p = str; + char *q = mem; + char *r; + char **arg; + int wasspace = 0; + int argc = 1; + + /* First copy the string, turning whitespace runs into nulls */ + for ( p = str ; ; p++ ) { + if ( *p <= ' ' ) { + if ( !wasspace ) { + wasspace = 1; + *q++ = '\0'; + } + } else { + if ( wasspace ) { + argc++; + wasspace = 0; + } + *q++ = *p; + } + + /* This test is AFTER we have processed the null byte; + we treat it as a whitespace character so it terminates + the last argument */ + if ( ! *p ) + break; + } + + /* Now create argv */ + arg = ALIGN_UP(q,char *); + *argv = arg; + *arg++ = mem; /* argv[0] */ + + q--; /* Point q to final null */ + for ( r = mem ; r < q ; r++ ) { + if ( *r == '\0' ) { + *arg++ = r+1; + } + } + + *arg++ = NULL; /* Null pointer at the end */ + __mem_end = arg; /* End of memory we used */ + + return argc; +} diff --git a/memdump/code16.h b/memdump/code16.h new file mode 100644 index 0000000..ca76565 --- /dev/null +++ b/memdump/code16.h @@ -0,0 +1,6 @@ +/* Must be included first of all */ +#ifdef __ASSEMBLY__ + .code16 +#else +__asm__ (".code16gcc"); +#endif diff --git a/memdump/com16.ld b/memdump/com16.ld new file mode 100644 index 0000000..08a1e95 --- /dev/null +++ b/memdump/com16.ld @@ -0,0 +1,127 @@ +/* + * Linker script for COM16 binaries + */ + +/* Script for -z combreloc: combine and sort reloc sections */ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", + "elf32-i386") +OUTPUT_ARCH(i386) +EXTERN(_start) +ENTRY(_start) +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = 0x100; + PROVIDE (__executable_start = .); + + .init : + { + KEEP (*(.init)) + } =0x90909090 + .text : + { + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + } =0x90909090 + .fini : + { + KEEP (*(.fini)) + } =0x90909090 + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + + /* Ensure the __preinit_array_start label is properly aligned. We + could instead move the label definition inside the section, but + the linker would then create the section even if it turns out to + be empty, which isn't pretty. */ + . = ALIGN(4); + PROVIDE (__preinit_array_start = .); + .preinit_array : { *(.preinit_array) } + PROVIDE (__preinit_array_end = .); + PROVIDE (__init_array_start = .); + .init_array : { *(.init_array) } + PROVIDE (__init_array_end = .); + PROVIDE (__fini_array_start = .); + .fini_array : { *(.fini_array) } + PROVIDE (__fini_array_end = .); + PROVIDE (__ctors_start = .); + .ctors : + { + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + PROVIDE (__ctors_end = .); + PROVIDE (__dtors_start = .); + .dtors : + { + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + PROVIDE (__dtors_end = .); + + /* Adjust the address for the data segment. Avoid mixing code and + data within same 128-byte chunk. */ + . = ALIGN(128); + + .data : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + _edata = .; + PROVIDE (edata = .); + __bss_start = .; + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. */ + . = ALIGN(32 / 8); + } + . = ALIGN(32 / 8); + _end = .; + PROVIDE (end = .); + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /DISCARD/ : { *(.note.GNU-stack) } +} diff --git a/memdump/conio.c b/memdump/conio.c new file mode 100644 index 0000000..bf54805 --- /dev/null +++ b/memdump/conio.c @@ -0,0 +1,42 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2001-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * conio.c + * + * Output to the screen + */ + +#include +#include "mystuff.h" + +int putchar(int ch) +{ + if ( ch == '\n' ) + putchar('\r'); + asm("movb $0x02,%%ah ; int $0x21" : : "d" (ch)); + return ch; +} + +/* Note: doesn't put '\n' like the stdc version does */ +int puts(const char *s) +{ + int count = 0; + + while ( *s ) { + putchar(*s); + count++; + s++; + } + + return count; +} diff --git a/memdump/crt0.S b/memdump/crt0.S new file mode 100644 index 0000000..92297ec --- /dev/null +++ b/memdump/crt0.S @@ -0,0 +1,53 @@ + .code16 + +#ifndef REGPARM +# error "This file assumes -mregparm=3 -DREGPARM=3" +#endif + + .section ".init","ax" + .globl _start + .type _start,@function +_start: + # Align the stack and make sure the high half is zero + andl $0xfff8,%esp + + # Clear the .bss + cld + xorl %eax,%eax + movw $__bss_start,%di + movw $_end+3,%cx + subw %di,%cx + shrw $2,%cx + rep ; stosl + + # Compute argc and argv (assumes REGPARM) + xorl %edx,%edx + movzbw 0x80,%bx + movb %dl,0x81(%bx) # Zero-terminate string + movb $0x81,%dl + pushl %eax # Make space for argv + movl %esp,%eax + calll __parse_argv + pushl %eax # argc + + # Initialize malloc + # calll __init_memory_arena + + # Now call main... (NOTE: gcc forces main to be regparm 0) + popl %eax # argc + popl %edx # argv + calll main + + # Here %eax is the exit code, fall through into exit + + .size _start,.-_start + + .globl exit + .type exit,@function +exit: + # Exit code already in %eax + movb $0x4c,%ah # Terminate program + int $0x21 +1: hlt + jmp 1b + .size exit,.-exit diff --git a/memdump/errno.h b/memdump/errno.h new file mode 100644 index 0000000..30aa046 --- /dev/null +++ b/memdump/errno.h @@ -0,0 +1,7 @@ +#ifndef ERRNO_H +#define ERRNO_H + +int errno; +void perror(const char *); + +#endif /* ERRNO_H */ diff --git a/memdump/inttypes.h b/memdump/inttypes.h new file mode 100644 index 0000000..9a6118b --- /dev/null +++ b/memdump/inttypes.h @@ -0,0 +1 @@ +#include diff --git a/memdump/io.h b/memdump/io.h new file mode 100644 index 0000000..a592402 --- /dev/null +++ b/memdump/io.h @@ -0,0 +1,40 @@ +#ifndef IO_H +#define IO_H + +static inline void outb(unsigned char v, unsigned short p) +{ + asm volatile("outb %1,%0" : : "d" (p), "a" (v)); +} + +static inline unsigned char inb(unsigned short p) +{ + unsigned char v; + asm volatile("inb %1,%0" : "=a" (v) : "d" (p)); + return v; +} + +static inline void outw(unsigned short v, unsigned short p) +{ + asm volatile("outw %1,%0" : : "d" (p), "a" (v)); +} + +static inline unsigned short inw(unsigned short p) +{ + unsigned short v; + asm volatile("inw %1,%0" : "=a" (v) : "d" (p)); + return v; +} + +static inline void outl(unsigned int v, unsigned short p) +{ + asm volatile("outl %1,%0" : : "d" (p), "a" (v)); +} + +static inline unsigned int inl(unsigned short p) +{ + unsigned int v; + asm volatile("inl %1,%0" : "=a" (v) : "d" (p)); + return v; +} + +#endif diff --git a/memdump/main.c b/memdump/main.c new file mode 100644 index 0000000..f408926 --- /dev/null +++ b/memdump/main.c @@ -0,0 +1,141 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include +#include "mystuff.h" +#include "ymsend.h" +#include "io.h" + +const char *program = "memdump"; + +void __attribute__((noreturn)) die(const char *msg) +{ + puts(program); + puts(": "); + puts(msg); + putchar('\n'); + exit(1); +} + +#ifdef DEBUG +# define dprintf printf +#else +# define dprintf(...) ((void)0) +#endif + +static inline __attribute__((const)) uint16_t ds(void) +{ + uint16_t v; + asm("movw %%ds,%0" : "=rm" (v)); + return v; +} + +#define GDT_ENTRY(flags,base,limit) \ + (((uint64_t)(base & 0xff000000) << 32) | \ + ((uint64_t)flags << 40) | \ + ((uint64_t)(limit & 0x00ff0000) << 32) | \ + ((uint64_t)(base & 0x00ffff00) << 16) | \ + ((uint64_t)(limit & 0x0000ffff))) + +static void get_bytes(void *buf, size_t len, struct file_info *finfo, + size_t pos) +{ + size_t end; + static uint64_t gdt[6]; + size_t bufl; + + pos += (size_t)finfo->pvt; /* Add base */ + end = pos+len; + + if (end <= 0x100000) { + /* Can stay in real mode */ + asm volatile("movw %3,%%fs ; " + "fs; rep; movsl ; " + "movw %2,%%cx ; " + "rep; movsb" + : : "D" (buf), "c" (len >> 2), "r" ((uint16_t)(len & 3)), + "rm" ((uint16_t)(pos >> 4)), "S" (pos & 15) + : "memory"); + } else { + bufl = (ds() << 4)+(size_t)buf; + gdt[2] = GDT_ENTRY(0x0093, pos, 0xffff); + gdt[3] = GDT_ENTRY(0x0093, bufl, 0xffff); + asm volatile("pushal ; int $0x15 ; popal" + : : "a" (0x8700), "c" ((len+1) >> 1), "S" (&gdt) + : "memory"); + } +} + +int main(int argc, char *argv[]) +{ + uint16_t bios_ports[4]; + const char *prefix; + char filename[1024]; + int i; + static struct serial_if sif = + { + .read = serial_read, + .write = serial_write, + }; + struct file_info finfo; + const char serial_banner[] = "Now begin Ymodem download...\r\n"; + + if (argc < 4) + die("usage: memdump port prefix start,len..."); + + finfo.pvt = (void *)0x400; + get_bytes(bios_ports, 8, &finfo, 0); /* Get BIOS serial ports */ + + for (i = 0; i < 4; i++) + printf("ttyS%i (COM%i) is at %#x\n", i, i+1, bios_ports[i]); + + sif.port = strtoul(argv[1], NULL, 0); + if (sif.port <= 3) { + sif.port = bios_ports[sif.port]; + } + + if (serial_init(&sif)) + die("failed to initialize serial port"); + + prefix = argv[2]; + + puts("Printing prefix...\n"); + sif.write(&sif, serial_banner, sizeof serial_banner-1); + + for (i = 3; i < argc; i++) { + uint32_t start, len; + char *ep; + + start = strtoul(argv[i], &ep, 0); + if (*ep != ',') + die("invalid range specification"); + len = strtoul(ep+1, NULL, 0); + + sprintf(filename, "%s%#x-%#x.bin", prefix, start, len); + finfo.name = filename; + finfo.size = len; + finfo.pvt = (void *)start; + + puts("Sending "); + puts(filename); + puts("...\n"); + + send_ymodem(&sif, &finfo, get_bytes); + } + + puts("Sending closing signature...\n"); + end_ymodem(&sif); + + return 0; +} diff --git a/memdump/malloc.h b/memdump/malloc.h new file mode 100644 index 0000000..70d0e63 --- /dev/null +++ b/memdump/malloc.h @@ -0,0 +1,54 @@ +/* + * malloc.h + * + * Internals for the memory allocator + */ + +#include +#include + +/* + * This is the minimum chunk size we will ask the kernel for; this should + * be a multiple of the page size on all architectures. + */ +#define MALLOC_CHUNK_SIZE 65536 +#define MALLOC_CHUNK_MASK (MALLOC_CHUNK_SIZE-1) + +/* + * This structure should be a power of two. This becomes the + * alignment unit. + */ +struct free_arena_header; + +struct arena_header { + size_t type; + size_t size; /* Also gives the location of the next entry */ + struct free_arena_header *next, *prev; +}; + +#ifdef DEBUG_MALLOC +#define ARENA_TYPE_USED 0x64e69c70 +#define ARENA_TYPE_FREE 0x012d610a +#define ARENA_TYPE_HEAD 0x971676b5 +#define ARENA_TYPE_DEAD 0xeeeeeeee +#else +#define ARENA_TYPE_USED 0 +#define ARENA_TYPE_FREE 1 +#define ARENA_TYPE_HEAD 2 +#endif + +#define ARENA_SIZE_MASK (sizeof(struct arena_header)-1) + +#define ARENA_ALIGN_UP(p) ((char *)(((uintptr_t)(p) + ARENA_SIZE_MASK) & ~ARENA_SIZE_MASK)) +#define ARENA_ALIGN_DOWN(p) ((char *)((uintptr_t)(p) & ~ARENA_SIZE_MASK)) + +/* + * This structure should be no more than twice the size of the + * previous structure. + */ +struct free_arena_header { + struct arena_header a; + struct free_arena_header *next_free, *prev_free; +}; + +extern struct free_arena_header __malloc_head; diff --git a/memdump/memcpy.S b/memdump/memcpy.S new file mode 100644 index 0000000..76eef73 --- /dev/null +++ b/memdump/memcpy.S @@ -0,0 +1,23 @@ +# +# memcpy.S +# +# Simple 16-bit memcpy() implementation +# + + .text + .code16gcc + .globl memcpy + .type memcpy, @function +memcpy: + cld + pushw %di + pushw %si + movw %ax,%di + movw %dx,%si + # The third argument is already in cx + rep ; movsb + popw %si + popw %di + ret + + .size memcpy,.-memcpy diff --git a/memdump/memdump.com b/memdump/memdump.com new file mode 100755 index 0000000000000000000000000000000000000000..ba4b29b69b49fbae1532d9c2bf85671c4b0e7dce GIT binary patch literal 5528 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 KoMZU$>c0U9fkg!X literal 0 HcmV?d00001 diff --git a/memdump/memset.S b/memdump/memset.S new file mode 100644 index 0000000..86e12ab --- /dev/null +++ b/memdump/memset.S @@ -0,0 +1,21 @@ +# +# memset.S +# +# Minimal 16-bit memset() implementation +# + + .text + .code16gcc + .globl memset + .type memset, @function +memset: + cld + pushw %di + movw %ax,%di + movb %dl,%al + # The third argument is already in %cx + rep ; stosb + popw %di + retl + + .size memset,.-memset diff --git a/memdump/mystuff.h b/memdump/mystuff.h new file mode 100644 index 0000000..7c5ac6f --- /dev/null +++ b/memdump/mystuff.h @@ -0,0 +1,24 @@ +#ifndef MYSTUFF_H +#define MYSTUFF_H + +#include + +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed int int32_t; +typedef unsigned int uint32_t; +typedef signed long long int64_t; +typedef unsigned long long uint64_t; + +unsigned int skip_atou(const char **s); +unsigned long strtoul(const char *, char **, int); + +static inline int +isdigit(int ch) +{ + return (ch >= '0') && (ch <= '9'); +} + +#endif /* MYSTUFF_H */ diff --git a/memdump/printf.c b/memdump/printf.c new file mode 100644 index 0000000..99b389f --- /dev/null +++ b/memdump/printf.c @@ -0,0 +1,297 @@ +/* + * Oh, it's a waste of space, but oh-so-yummy for debugging. It's just + * initialization code anyway, so it doesn't take up space when we're + * actually running. This version of printf() does not include 64-bit + * support. "Live with it." + * + * Most of this code was shamelessly snarfed from the Linux kernel, then + * modified. It's therefore GPL. + * + * printf() isn't actually needed to build syslinux.com, but during + * debugging it's handy. + */ + +#include +#include +#include "mystuff.h" + +static int strnlen(const char *s, int maxlen) +{ + const char *es = s; + while ( *es && maxlen ) { + es++; maxlen--; + } + + return (es-s); +} + +#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 char * number(char * str, long num, int base, int size, int precision + ,int type) +{ + char c,sign,tmp[66]; + const char *digits="0123456789abcdefghijklmnopqrstuvwxyz"; + int i; + + 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) + *str++ = ' '; + if (sign) + *str++ = sign; + if (type & SPECIAL) { + if (base==8) + *str++ = '0'; + else if (base==16) { + *str++ = '0'; + *str++ = digits[33]; + } + } + if (!(type & LEFT)) + while (size-- > 0) + *str++ = c; + while (i < precision--) + *str++ = '0'; + while (i-- > 0) + *str++ = tmp[i]; + while (size-- > 0) + *str++ = ' '; + return str; +} + +/* Forward decl. needed for IP address printing stuff... */ +int sprintf(char * buf, const char *fmt, ...); + +int vsprintf(char *buf, const char *fmt, va_list args) +{ + int len; + unsigned long num; + int i, base; + char * str; + 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 */ + + for (str=buf ; *fmt ; ++fmt) { + if (*fmt != '%') { + *str++ = *fmt; + 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 (isdigit(*fmt)) + field_width = skip_atou(&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 (isdigit(*fmt)) + precision = skip_atou(&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) + *str++ = ' '; + *str++ = (unsigned char) va_arg(args, int); + while (--field_width > 0) + *str++ = ' '; + continue; + + case 's': + s = va_arg(args, char *); + len = strnlen(s, precision); + + if (!(flags & LEFT)) + while (len < field_width--) + *str++ = ' '; + for (i = 0; i < len; ++i) + *str++ = *s++; + while (len < field_width--) + *str++ = ' '; + continue; + + case 'p': + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + str = number(str, + (unsigned long) va_arg(args, void *), 16, + field_width, precision, flags); + continue; + + + case 'n': + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = (str - buf); + } else { + int * ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + *str++ = '%'; + 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: + *str++ = '%'; + if (*fmt) + *str++ = *fmt; + else + --fmt; + continue; + } + 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); + str = number(str, num, base, field_width, precision, flags); + } + *str = '\0'; + return str-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; +} + +int printf(const char *fmt, ...) +{ + char printf_buf[1024]; + va_list args; + int printed; + + va_start(args, fmt); + printed = vsprintf(printf_buf, fmt, args); + va_end(args); + + puts(printf_buf); + + return printed; +} diff --git a/memdump/serial.c b/memdump/serial.c new file mode 100644 index 0000000..902dcc3 --- /dev/null +++ b/memdump/serial.c @@ -0,0 +1,80 @@ +#include "mystuff.h" +#include "ymsend.h" +#include "io.h" + +enum { + THR = 0, + RBR = 0, + DLL = 0, + DLM = 1, + IER = 1, + IIR = 2, + FCR = 2, + LCR = 3, + MCR = 4, + LSR = 5, + MSR = 6, + SCR = 7, +}; + +int serial_init(struct serial_if *sif) +{ + uint16_t port = sif->port; + uint8_t dll, dlm, lcr; + + /* Set 115200n81 */ + outb(0x83, port+LCR); + outb(0x01, port+DLL); + outb(0x00, port+DLM); + (void)inb(port+IER); /* Synchronize */ + dll = inb(port+DLL); + dlm = inb(port+DLM); + lcr = inb(port+LCR); + outb(0x03, port+LCR); + (void)inb(port+IER); /* Synchronize */ + + if (dll != 0x01 || dlm != 0x00 || lcr != 0x83) + return -1; /* This doesn't look like a serial port */ + + /* Disable interrupts */ + outb(port+IER, 0); + + /* Enable 16550A FIFOs if available */ + outb(port+FCR, 0x01); /* Enable FIFO */ + (void)inb(port+IER); /* Synchronize */ + if (inb(port+IIR) < 0xc0) + outb(port+FCR, 0x00); /* Disable FIFOs if non-functional */ + (void)inb(port+IER); /* Synchronize */ + + return 0; +} + +void serial_write(struct serial_if *sif, const void *data, size_t n) +{ + uint16_t port = sif->port; + const char *p = data; + uint8_t lsr; + + while (n--) { + do { + lsr = inb(port+LSR); + } while (!(lsr & 0x20)); + + outb(*p++, port+THR); + } +} + +void serial_read(struct serial_if *sif, void *data, size_t n) +{ + uint16_t port = sif->port; + char *p = data; + uint8_t lsr; + + while (n--) { + do { + lsr = inb(port+LSR); + } while (!(lsr & 0x01)); + + *p++ = inb(port+RBR); + } +} diff --git a/memdump/skipatou.c b/memdump/skipatou.c new file mode 100644 index 0000000..f71c534 --- /dev/null +++ b/memdump/skipatou.c @@ -0,0 +1,10 @@ +#include "mystuff.h" + +unsigned int skip_atou(const char **s) +{ + int i=0; + + while (isdigit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} diff --git a/memdump/stdint.h b/memdump/stdint.h new file mode 100644 index 0000000..9a5553b --- /dev/null +++ b/memdump/stdint.h @@ -0,0 +1,142 @@ +/* + * stdint.h + */ + +#ifndef _STDINT_H +#define _STDINT_H + +/* Exact types */ + +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long long int64_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +/* Small types */ + +typedef signed char int_least8_t; +typedef signed short int_least16_t; +typedef signed int int_least32_t; +typedef signed long long int_least64_t; + +typedef unsigned char uint_least8_t; +typedef unsigned short uint_least16_t; +typedef unsigned int uint_least32_t; +typedef unsigned long long uint_least64_t; + +/* Fast types */ + +typedef signed char int_fast8_t; +typedef signed short int_fast16_t; +typedef signed int int_fast32_t; +typedef signed long long int_fast64_t; + +typedef unsigned char uint_fast8_t; +typedef unsigned short uint_fast16_t; +typedef unsigned int uint_fast32_t; +typedef unsigned long long uint_fast64_t; + +/* Pointer types */ + +typedef int32_t intptr_t; +typedef uint32_t uintptr_t; + +/* Maximal types */ + +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + +/* + * To be strictly correct... + */ +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) + +# define INT8_MIN (-128) +# define INT16_MIN (-32767-1) +# define INT32_MIN (-2147483647-1) +# define INT64_MIN (-9223372036854775807LL-1) + +# define INT8_MAX (127) +# define INT16_MAX (32767) +# define INT32_MAX (2147483647) +# define INT64_MAX (9223372036854775807LL) + +# define UINT8_MAX (255U) +# define UINT16_MAX (65535U) +# define UINT32_MAX (4294967295U) +# define UINT64_MAX (18446744073709551615ULL) + +# define INT_LEAST8_MIN (-128) +# define INT_LEAST16_MIN (-32767-1) +# define INT_LEAST32_MIN (-2147483647-1) +# define INT_LEAST64_MIN (-9223372036854775807LL-1) + +# define INT_LEAST8_MAX (127) +# define INT_LEAST16_MAX (32767) +# define INT_LEAST32_MAX (2147483647) +# define INT_LEAST64_MAX (9223372036854775807LL) + +# define UINT_LEAST8_MAX (255U) +# define UINT_LEAST16_MAX (65535U) +# define UINT_LEAST32_MAX (4294967295U) +# define UINT_LEAST64_MAX (18446744073709551615ULL) + +# define INT_FAST8_MIN (-128) +# define INT_FAST16_MIN (-32767-1) +# define INT_FAST32_MIN (-2147483647-1) +# define INT_FAST64_MIN (-9223372036854775807LL-1) + +# define INT_FAST8_MAX (127) +# define INT_FAST16_MAX (32767) +# define INT_FAST32_MAX (2147483647) +# define INT_FAST64_MAX (9223372036854775807LL) + +# define UINT_FAST8_MAX (255U) +# define UINT_FAST16_MAX (65535U) +# define UINT_FAST32_MAX (4294967295U) +# define UINT_FAST64_MAX (18446744073709551615ULL) + +# define INTPTR_MIN (-2147483647-1) +# define INTPTR_MAX (2147483647) +# define UINTPTR_MAX (4294967295U) + +# define INTMAX_MIN (-9223372036854775807LL-1) +# define INTMAX_MAX (9223372036854775807LL) +# define UINTMAX_MAX (18446744073709551615ULL) + +/* ptrdiff_t limit */ +# define PTRDIFF_MIN (-2147483647-1) +# define PTRDIFF_MAX (2147483647) + +/* sig_atomic_t limit */ +# define SIG_ATOMIC_MIN (-2147483647-1) +# define SIG_ATOMIC_MAX (2147483647) + +/* size_t limit */ +# define SIZE_MAX (4294967295U) + +#endif /* STDC_LIMIT_MACROS */ + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) + +# define INT8_C(n) n +# define INT16_C(n) n +# define INT32_C(n) n +# define INT64_C(n) n ## LL + +# define UINT8_C(n) n ## U +# define UINT16_C(n) n ## U +# define UINT32_C(n) n ## U +# define UINT64_C(n) n ## ULL + +# define INTMAX_C(n) n ## LL +# define UINTMAX_C(n) n ## ULL + +#endif /* STDC_CONSTANT_MACROS */ + +#endif /* _STDINT_H */ diff --git a/memdump/stdio.h b/memdump/stdio.h new file mode 100644 index 0000000..97988c2 --- /dev/null +++ b/memdump/stdio.h @@ -0,0 +1,21 @@ +#ifndef STDIO_H +#define STDIO_H + +#include +#include + +typedef unsigned int off_t; + +int putchar(int); +int puts(const char *); +int sprintf(char * buf, const char *fmt, ...); +int vsprintf(char *buf, const char *fmt, va_list args); +int printf(const char *fmt, ...); + +#define stdin 0 +#define stdout 1 +#define stderr 2 + +#define fprintf(x, y, ...) printf(y, ## __VA_ARGS__) + +#endif /* STDIO_H */ diff --git a/memdump/stdlib.h b/memdump/stdlib.h new file mode 100644 index 0000000..6ead86a --- /dev/null +++ b/memdump/stdlib.h @@ -0,0 +1,14 @@ +#ifndef STDLIB_H +#define STDLIB_H + +#define NULL ((void *)0) + +typedef int ssize_t; +typedef unsigned int size_t; + +void __attribute__((noreturn)) exit(int); + +void *malloc(size_t); +void free(void *); + +#endif diff --git a/memdump/string.h b/memdump/string.h new file mode 100644 index 0000000..44f77da --- /dev/null +++ b/memdump/string.h @@ -0,0 +1,23 @@ +/* + * string.h + */ + +#ifndef _STRING_H +#define _STRING_H + +/* Standard routines */ +#define memcpy(a,b,c) __builtin_memcpy(a,b,c) +#define memset(a,b,c) __builtin_memset(a,b,c) +#define strcpy(a,b) __builtin_strcpy(a,b) +#define strlen(a) __builtin_strlen(a) + +/* This only returns true or false */ +static inline int memcmp(const void *__m1, const void *__m2, unsigned int __n) +{ + _Bool rv; + asm volatile("cld ; repe ; cmpsb ; setne %0" + : "=abd" (rv), "+D" (__m1), "+S" (__m2), "+c" (__n)); + return rv; +} + +#endif /* _STRING_H */ diff --git a/memdump/strtoul.c b/memdump/strtoul.c new file mode 100644 index 0000000..cd09d3d --- /dev/null +++ b/memdump/strtoul.c @@ -0,0 +1,72 @@ +/* + * strtoul.c + * + */ + +#include "mystuff.h" + +static inline int isspace(int c) +{ + return (c <= ' '); /* Close enough */ +} + +static inline int digitval(int ch) +{ + if (ch >= '0' && ch <= '9') { + return ch - '0'; + } else if (ch >= 'A' && ch <= 'Z') { + return ch - 'A' + 10; + } else if (ch >= 'a' && ch <= 'z') { + return ch - 'a' + 10; + } else { + return -1; + } +} + +unsigned long strtoul(const char *nptr, char **endptr, int base) +{ + int minus = 0; + unsigned long v = 0; + int d; + + while (isspace((unsigned char)*nptr)) { + nptr++; + } + + /* Single optional + or - */ + { + char c = *nptr; + if (c == '-' || c == '+') { + minus = (c == '-'); + nptr++; + } + } + + if (base == 0) { + if (nptr[0] == '0' && + (nptr[1] == 'x' || nptr[1] == 'X')) { + nptr += 2; + base = 16; + } else if (nptr[0] == '0') { + nptr++; + base = 8; + } else { + base = 10; + } + } else if (base == 16) { + if (nptr[0] == '0' && + (nptr[1] == 'x' || nptr[1] == 'X')) { + nptr += 2; + } + } + + while ((d = digitval(*nptr)) >= 0 && d < base) { + v = v * base + d; + nptr++; + } + + if (endptr) + *endptr = (char *)nptr; + + return minus ? -v : v; +} diff --git a/memdump/ymodem.txt b/memdump/ymodem.txt new file mode 100644 index 0000000..2264ff7 --- /dev/null +++ b/memdump/ymodem.txt @@ -0,0 +1,2108 @@ + + + + - 1 - + + + + XMODEM/YMODEM PROTOCOL REFERENCE + A compendium of documents describing the + + XMODEM and YMODEM + + File Transfer Protocols + + + + + This document was formatted 10-14-88. + + + + + + + + Edited by Chuck Forsberg + + + + + + + + + + This file may be redistributed without restriction + provided the text is not altered. + + Please distribute as widely as possible. + + Questions to Chuck Forsberg + + + + + + Omen Technology Inc + The High Reliability Software + 17505-V Sauvie Island Road + Portland Oregon 97231 + VOICE: 503-621-3406 :VOICE + TeleGodzilla BBS: 503-621-3746 Speed 19200(Telebit PEP),2400,1200,300 + CompuServe: 70007,2304 + GEnie: CAF + UUCP: ...!tektronix!reed!omen!caf + + + + + + + + + + + + + + + - 2 - + + + + 1. TOWER OF BABEL + + A "YMODEM Tower of Babel" has descended on the microcomputing community + bringing with it confusion, frustration, bloated phone bills, and wasted + man hours. Sadly, I (Chuck Forsberg) am partly to blame for this mess. + + As author of the early 1980s batch and 1k XMODEM extensions, I assumed + readers of earlier versions of this document would implement as much of + the YMODEM protocol as their programming skills and computing environments + would permit. This proved a rather naive assumption as programmers + motivated by competitive pressure implemented as little of YMODEM as + possible. Some have taken whatever parts of YMODEM that appealed to them, + applied them to MODEM7 Batch, Telink, XMODEM or whatever, and called the + result YMODEM. + + Jeff Garbers (Crosstalk package development director) said it all: "With + protocols in the public domain, anyone who wants to dink around with them + can go ahead." [1] + + Documents containing altered examples derived from YMODEM.DOC have added + to the confusion. In one instance, some self styled rewriter of history + altered the heading in YMODEM.DOC's Figure 1 from "1024 byte Packets" to + "YMODEM/CRC File Transfer Protocol". None of the XMODEM and YMODEM + examples shown in that document were correct. + + To put an end to this confusion, we must make "perfectly clear" what + YMODEM stands for, as Ward Christensen defined it in his 1985 coining of + the term. + + To the majority of you who read, understood, and respected Ward's + definition of YMODEM, I apologize for the inconvenience. + + 1.1 Definitions + + ARC ARC is a program that compresses one or more files into an archive + and extracts files from such archives. + + XMODEM refers to the file transfer etiquette introduced by Ward + Christensen's 1977 MODEM.ASM program. The name XMODEM comes from + Keith Petersen's XMODEM.ASM program, an adaptation of MODEM.ASM + for Remote CP/M (RCPM) systems. It's also called the MODEM or + MODEM2 protocol. Some who are unaware of MODEM7's unusual batch + file mode call it MODEM7. Other aliases include "CP/M Users' + Group" and "TERM II FTP 3". The name XMODEM caught on partly + because it is distinctive and partly because of media interest in + + + __________ + + 1. Page C/12, PC-WEEK July 12, 1987 + + + + + Chapter 1 + + + + + + + + X/YMODEM Protocol Reference June 18 1988 3 + + + + bulletin board and RCPM systems where it was accessed with an + "XMODEM" command. This protocol is supported by every serious + communications program because of its universality, simplicity, + and reasonable performance. + + XMODEM/CRC replaces XMODEM's 1 byte checksum with a two byte Cyclical + Redundancy Check (CRC-16), giving modern error detection + protection. + + XMODEM-1k Refers to the XMODEM/CRC protocol with 1024 byte data blocks. + + YMODEM Refers to the XMODEM/CRC (optional 1k blocks) protocol with batch + transmission as described below. In a nutshell, YMODEM means + BATCH. + + YMODEM-g Refers to the streaming YMODEM variation described below. + + True YMODEM(TM) In an attempt to sort out the YMODEM Tower of Babel, Omen + Technology has trademarked the term True YMODEM(TM) to represent + the complete YMODEM protocol described in this document, including + pathname, length, and modification date transmitted in block 0. + Please contact Omen Technology about certifying programs for True + YMODEM(TM) compliance. + + ZMODEM uses familiar XMODEM/CRC and YMODEM technology in a new protocol + that provides reliability, throughput, file management, and user + amenities appropriate to contemporary data communications. + + ZOO Like ARC, ZOO is a program that compresses one or more files into + a "zoo archive". ZOO supports many different operating systems + including Unix and VMS. + + + + + + + + + + + + + + + + + + + + + + + + Chapter 1 + + + + + + + + X/YMODEM Protocol Reference June 18 1988 4 + + + + 2. YMODEM MINIMUM REQUIREMENTS + + All programs claiming to support YMODEM must meet the following minimum + requirements: + + + The sending program shall send the pathname (file name) in block 0. + + + The pathname shall be a null terminated ASCII string as described + below. + + For those who are too lazy to read the entire document: + + + Unless specifically requested, only the file name portion is + sent. + + + No drive letter is sent. + + + Systems that do not distinguish between upper and lower case + letters in filenames shall send the pathname in lower case only. + + + + The receiving program shall use this pathname for the received file + name, unless explicitly overridden. + + + When the receiving program receives this block and successfully + opened the output file, it shall acknowledge this block with an ACK + character and then proceed with a normal XMODEM file transfer + beginning with a "C" or NAK tranmsitted by the receiver. + + + The sending program shall use CRC-16 in response to a "C" pathname + nak, otherwise use 8 bit checksum. + + + The receiving program must accept any mixture of 128 and 1024 byte + blocks within each file it receives. Sending programs may + arbitrarily switch between 1024 and 128 byte blocks. + + + The sending program must not change the length of an unacknowledged + block. + + + At the end of each file, the sending program shall send EOT up to ten + times until it receives an ACK character. (This is part of the + XMODEM spec.) + + + The end of a transfer session shall be signified by a null (empty) + pathname, this pathname block shall be acknowledged the same as other + pathname blocks. + + Programs not meeting all of these requirements are not YMODEM compatible, + and shall not be described as supporting YMODEM. + + Meeting these MINIMUM requirements does not guarantee reliable file + + + + Chapter 2 + + + + + + + + X/YMODEM Protocol Reference June 18 1988 5 + + + + transfers under stress. Particular attention is called to XMODEM's single + character supervisory messages that are easily corrupted by transmission + errors. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Chapter 2 + + + + + + + + X/YMODEM Protocol Reference June 18 1988 6 + + + + 3. WHY YMODEM? + + Since its development half a decade ago, the Ward Christensen modem + protocol has enabled a wide variety of computer systems to interchange + data. There is hardly a communications program that doesn't at least + claim to support this protocol. + + Advances in computing, modems and networking have revealed a number of + weaknesses in the original protocol: + + + The short block length caused throughput to suffer when used with + timesharing systems, packet switched networks, satellite circuits, + and buffered (error correcting) modems. + + + The 8 bit arithmetic checksum and other aspects allowed line + impairments to interfere with dependable, accurate transfers. + + + Only one file could be sent per command. The file name had to be + given twice, first to the sending program and then again to the + receiving program. + + + The transmitted file could accumulate as many as 127 extraneous + bytes. + + + The modification date of the file was lost. + + A number of other protocols have been developed over the years, but none + have displaced XMODEM to date: + + + Lack of public domain documentation and example programs have kept + proprietary protocols such as Blast, Relay, and others tightly bound + to the fortunes of their suppliers. + + + Complexity discourages the widespread application of BISYNC, SDLC, + HDLC, X.25, and X.PC protocols. + + + Performance compromises and complexity have limited the popularity of + the Kermit protocol, which was developed to allow file transfers in + environments hostile to XMODEM. + + The XMODEM protocol extensions and YMODEM Batch address some of these + weaknesses while maintaining most of XMODEM's simplicity. + + YMODEM is supported by the public domain programs YAM (CP/M), + YAM(CP/M-86), YAM(CCPM-86), IMP (CP/M), KMD (CP/M), rz/sz (Unix, Xenix, + VMS, Berkeley Unix, Venix, Xenix, Coherent, IDRIS, Regulus). Commercial + implementations include MIRROR, and Professional-YAM.[1] Communications + + + + + + + + Chapter 3 + + + + + + + + X/YMODEM Protocol Reference June 18 1988 7 + + + + programs supporting these extensions have been in use since 1981. + + The 1k block length (XMODEM-1k) described below may be used in conjunction + with YMODEM Batch Protocol, or with single file transfers identical to the + XMODEM/CRC protocol except for minimal changes to support 1k blocks. + + Another extension is the YMODEM-g protocol. YMODEM-g provides batch + transfers with maximum throughput when used with end to end error + correcting media, such as X.PC and error correcting modems, including 9600 + bps units by TeleBit, U.S.Robotics, Hayes, Electronic Vaults, Data Race, + and others. + + To complete this tome, edited versions of Ward Christensen's original + protocol document and John Byrns's CRC-16 document are included for + reference. + + References to the MODEM or MODEM7 protocol have been changed to XMODEM to + accommodate the vernacular. In Australia, it is properly called the + Christensen Protocol. + + + 3.1 Some Messages from the Pioneer + + #: 130940 S0/Communications 25-Apr-85 18:38:47 + Sb: my protocol + Fm: Ward Christensen 76703,302 [2] + To: all + + Be aware the article[3] DID quote me correctly in terms of the phrases + like "not robust", etc. + + It was a quick hack I threw together, very unplanned (like everything I + do), to satisfy a personal need to communicate with "some other" people. + + ONLY the fact that it was done in 8/77, and that I put it in the public + domain immediately, made it become the standard that it is. + + + + + + + + __________________________________________________________________________ + + 1. Available for IBM PC,XT,AT, Unix and Xenix + + 2. Edited for typesetting appearance + + 3. Infoworld April 29 p. 16 + + + + + Chapter 3 + + + + + + + + X/YMODEM Protocol Reference June 18 1988 8 + + + + I think its time for me to + + (1) document it; (people call me and say "my product is going to include + it - what can I 'reference'", or "I'm writing a paper on it, what do I put + in the bibliography") and + + (2) propose an "incremental extension" to it, which might take "exactly" + the form of Chuck Forsberg's YAM protocol. He wrote YAM in C for CP/M and + put it in the public domain, and wrote a batch protocol for Unix[4] called + rb and sb (receive batch, send batch), which was basically XMODEM with + (a) a record 0 containing filename date time and size + (b) a 1K block size option + (c) CRC-16. + + He did some clever programming to detect false ACK or EOT, but basically + left them the same. + + People who suggest I make SIGNIFICANT changes to the protocol, such as + "full duplex", "multiple outstanding blocks", "multiple destinations", etc + etc don't understand that the incredible simplicity of the protocol is one + of the reasons it survived to this day in as many machines and programs as + it may be found in! + + Consider the PC-NET group back in '77 or so - documenting to beat the band + - THEY had a protocol, but it was "extremely complex", because it tried to + be "all things to all people" - i.e. send binary files on a 7-bit system, + etc. I was not that "benevolent". I (emphasize > I < ) had an 8-bit UART, + so "my protocol was an 8-bit protocol", and I would just say "sorry" to + people who were held back by 7-bit limitations. ... + + Block size: Chuck Forsberg created an extension of my protocol, called + YAM, which is also supported via his public domain programs for UNIX + called rb and sb - receive batch and send batch. They cleverly send a + "block 0" which contains the filename, date, time, and size. + Unfortunately, its UNIX style, and is a bit weird[5] - octal numbers, etc. + BUT, it is a nice way to overcome the kludgy "echo the chars of the name" + introduced with MODEM7. Further, chuck uses CRC-16 and optional 1K + blocks. Thus the record 0, 1K, and CRC, make it a "pretty slick new + protocol" which is not significantly different from my own. + + Also, there is a catchy name - YMODEM. That means to some that it is the + "next thing after XMODEM", and to others that it is the Y(am)MODEM + + + __________ + + 4. VAX/VMS versions of these programs are also available. + + 5. The file length, time, and file mode are optional. The pathname and + file length may be sent alone if desired. + + + + + Chapter 3 + + + + + + + + X/YMODEM Protocol Reference June 18 1988 9 + + + + protocol. I don't want to emphasize that too much - out of fear that + other mfgrs might think it is a "competitive" protocol, rather than an + "unaffiliated" protocol. Chuck is currently selling a much-enhanced + version of his CP/M-80 C program YAM, calling it Professional Yam, and its + for the PC - I'm using it right now. VERY slick! 32K capture buffer, + script, scrolling, previously captured text search, plus built-in commands + for just about everything - directory (sorted every which way), XMODEM, + YMODEM, KERMIT, and ASCII file upload/download, etc. You can program it + to "behave" with most any system - for example when trying a number for + CIS it detects the "busy" string back from the modem and substitutes a + diff phone # into the dialing string and branches back to try it. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Chapter 3 + + + + + + + + X/YMODEM Protocol Reference June 18 1988 10 + + + + 4. XMODEM PROTOCOL ENHANCEMENTS + + This chapter discusses the protocol extensions to Ward Christensen's 1982 + XMODEM protocol description document. + + The original document recommends the user be asked whether to continue + trying or abort after 10 retries. Most programs no longer ask the + operator whether he wishes to keep retrying. Virtually all correctable + errors are corrected within the first few retransmissions. If the line is + so bad that ten attempts are insufficient, there is a significant danger + of undetected errors. If the connection is that bad, it's better to + redial for a better connection, or mail a floppy disk. + + + 4.1 Graceful Abort + + The YAM and Professional-YAM X/YMODEM routines recognize a sequence of two + consecutive CAN (Hex 18) characters without modem errors (overrun, + framing, etc.) as a transfer abort command. This sequence is recognized + when is waiting for the beginning of a block or for an acknowledgement to + a block that has been sent. The check for two consecutive CAN characters + reduces the number of transfers aborted by line hits. YAM sends eight CAN + characters when it aborts an XMODEM, YMODEM, or ZMODEM protocol file + transfer. Pro-YAM then sends eight backspaces to delete the CAN + characters from the remote's keyboard input buffer, in case the remote had + already aborted the transfer and was awaiting a keyboarded command. + + + 4.2 CRC-16 Option + + The XMODEM protocol uses an optional two character CRC-16 instead of the + one character arithmetic checksum used by the original protocol and by + most commercial implementations. CRC-16 guarantees detection of all + single and double bit errors, all errors with an odd number of error + bits, all burst errors of length 16 or less, 99.9969% of all 17-bit error + bursts, and 99.9984 per cent of all possible longer error bursts. By + contrast, a double bit error, or a burst error of 9 bits or more can sneak + past the XMODEM protocol arithmetic checksum. + + The XMODEM/CRC protocol is similar to the XMODEM protocol, except that the + receiver specifies CRC-16 by sending C (Hex 43) instead of NAK when + requesting the FIRST block. A two byte CRC is sent in place of the one + byte arithmetic checksum. + + YAM's c option to the r command enables CRC-16 in single file reception, + corresponding to the original implementation in the MODEM7 series + programs. This remains the default because many commercial communications + programs and bulletin board systems still do not support CRC-16, + especially those written in Basic or Pascal. + + XMODEM protocol with CRC is accurate provided both sender and receiver + + + + Chapter 4 XMODEM Protocol Enhancements + + + + + + + + X/YMODEM Protocol Reference June 18 1988 11 + + + + both report a successful transmission. The protocol is robust in the + presence of characters lost by buffer overloading on timesharing systems. + + The single character ACK/NAK responses generated by the receiving program + adapt well to split speed modems, where the reverse channel is limited to + ten per cent or less of the main channel's speed. + + XMODEM and YMODEM are half duplex protocols which do not attempt to + transmit information and control signals in both directions at the same + time. This avoids buffer overrun problems that have been reported by + users attempting to exploit full duplex asynchronous file transfer + protocols such as Blast. + + Professional-YAM adds several proprietary logic enhancements to XMODEM's + error detection and recovery. These compatible enhancements eliminate + most of the bad file transfers other programs make when using the XMODEM + protocol under less than ideal conditions. + + + 4.3 XMODEM-1k 1024 Byte Block + + Disappointing throughput downloading from Unix with YMODEM[1] lead to the + development of 1024 byte blocks in 1982. 1024 byte blocks reduce the + effect of delays from timesharing systems, modems, and packet switched + networks on throughput by 87.5 per cent in addition to decreasing XMODEM's + 3 per cent overhead (block number, CRC, etc.). + + Some environments cannot accept 1024 byte bursts, including some networks + and minicomputer ports. The longer block length should be an option. + + The choice to use 1024 byte blocks is expressed to the sending program on + its command line or selection menu.[2] 1024 byte blocks improve throughput + in many applications. + + An STX (02) replaces the SOH (01) at the beginning of the transmitted + block to notify the receiver of the longer block length. The transmitted + block contains 1024 bytes of data. The receiver should be able to accept + any mixture of 128 and 1024 byte blocks. The block number (in the second + and third bytes of the block) is incremented by one for each block + regardless of the block length. + + The sender must not change between 128 and 1024 byte block lengths if it + has not received a valid ACK for the current block. Failure to observe + + + __________ + + 1. The name hadn't been coined yet, but the protocol was the same. + + 2. See "KMD/IMP Exceptions to YMODEM" below. + + + + + Chapter 4 XMODEM Protocol Enhancements + + + + + + + + X/YMODEM Protocol Reference June 18 1988 12 + + + + this restriction allows transmission errors to pass undetected. + + If 1024 byte blocks are being used, it is possible for a file to "grow" up + to the next multiple of 1024 bytes. This does not waste disk space if the + allocation granularity is 1k or greater. With YMODEM batch transmission, + the optional file length transmitted in the file name block allows the + receiver to discard the padding, preserving the exact file length and + contents. + + 1024 byte blocks may be used with batch file transmission or with single + file transmission. CRC-16 should be used with the k option to preserve + data integrity over phone lines. If a program wishes to enforce this + recommendation, it should cancel the transfer, then issue an informative + diagnostic message if the receiver requests checksum instead of CRC-16. + + Under no circumstances may a sending program use CRC-16 unless the + receiver commands CRC-16. + + Figure 1. XMODEM-1k Blocks + + SENDER RECEIVER + "sx -k foo.bar" + "foo.bar open x.x minutes" + C + STX 01 FE Data[1024] CRC CRC + ACK + STX 02 FD Data[1024] CRC CRC + ACK + STX 03 FC Data[1000] CPMEOF[24] CRC CRC + ACK + EOT + ACK + + Figure 2. Mixed 1024 and 128 byte Blocks + + SENDER RECEIVER + "sx -k foo.bar" + "foo.bar open x.x minutes" + C + STX 01 FE Data[1024] CRC CRC + ACK + STX 02 FD Data[1024] CRC CRC + ACK + SOH 03 FC Data[128] CRC CRC + ACK + SOH 04 FB Data[100] CPMEOF[28] CRC CRC + ACK + EOT + ACK + + + + + + Chapter 4 XMODEM Protocol Enhancements + + + + + + + + X/YMODEM Protocol Reference June 18 1988 13 + + + + 5. YMODEM Batch File Transmission + + The YMODEM Batch protocol is an extension to the XMODEM/CRC protocol that + allows 0 or more files to be transmitted with a single command. (Zero + files may be sent if none of the requested files is accessible.) The + design approach of the YMODEM Batch protocol is to use the normal routines + for sending and receiving XMODEM blocks in a layered fashion similar to + packet switching methods. + + Why was it necessary to design a new batch protocol when one already + existed in MODEM7?[1] The batch file mode used by MODEM7 is unsuitable + because it does not permit full pathnames, file length, file date, or + other attribute information to be transmitted. Such a restrictive design, + hastily implemented with only CP/M in mind, would not have permitted + extensions to current areas of personal computing such as Unix, DOS, and + object oriented systems. In addition, the MODEM7 batch file mode is + somewhat susceptible to transmission impairments. + + As in the case of single a file transfer, the receiver initiates batch + file transmission by sending a "C" character (for CRC-16). + + The sender opens the first file and sends block number 0 with the + following information.[2] + + Only the pathname (file name) part is required for batch transfers. + + To maintain upwards compatibility, all unused bytes in block 0 must be set + to null. + + Pathname The pathname (conventionally, the file name) is sent as a null + terminated ASCII string. This is the filename format used by the + handle oriented MSDOS(TM) functions and C library fopen functions. + An assembly language example follows: + DB 'foo.bar',0 + No spaces are included in the pathname. Normally only the file name + stem (no directory prefix) is transmitted unless the sender has + selected YAM's f option to send the full pathname. The source drive + (A:, B:, etc.) is not sent. + + Filename Considerations: + + + + __________ + + 1. The MODEM7 batch protocol transmitted CP/M FCB bytes f1...f8 and + t1...t3 one character at a time. The receiver echoed these bytes as + received, one at a time. + + 2. Only the data part of the block is described here. + + + + + Chapter 5 XMODEM Protocol Enhancements + + + + + + + + X/YMODEM Protocol Reference June 18 1988 14 + + + + + File names are forced to lower case unless the sending system + supports upper/lower case file names. This is a convenience for + users of systems (such as Unix) which store filenames in upper + and lower case. + + + The receiver should accommodate file names in lower and upper + case. + + + When transmitting files between different operating systems, + file names must be acceptable to both the sender and receiving + operating systems. + + If directories are included, they are delimited by /; i.e., + "subdir/foo" is acceptable, "subdir\foo" is not. + + Length The file length and each of the succeeding fields are optional.[3] + The length field is stored in the block as a decimal string counting + the number of data bytes in the file. The file length does not + include any CPMEOF (^Z) or other garbage characters used to pad the + last block. + + If the file being transmitted is growing during transmission, the + length field should be set to at least the final expected file + length, or not sent. + + The receiver stores the specified number of characters, discarding + any padding added by the sender to fill up the last block. + + Modification Date The mod date is optional, and the filename and length + may be sent without requiring the mod date to be sent. + + Iff the modification date is sent, a single space separates the + modification date from the file length. + + The mod date is sent as an octal number giving the time the contents + of the file were last changed, measured in seconds from Jan 1 1970 + Universal Coordinated Time (GMT). A date of 0 implies the + modification date is unknown and should be left as the date the file + is received. + + This standard format was chosen to eliminate ambiguities arising from + transfers between different time zones. + + + + + + __________ + + 3. Fields may not be skipped. + + + + + Chapter 5 XMODEM Protocol Enhancements + + + + + + + + X/YMODEM Protocol Reference June 18 1988 15 + + + + Mode Iff the file mode is sent, a single space separates the file mode + from the modification date. The file mode is stored as an octal + string. Unless the file originated from a Unix system, the file mode + is set to 0. rb(1) checks the file mode for the 0x8000 bit which + indicates a Unix type regular file. Files with the 0x8000 bit set + are assumed to have been sent from another Unix (or similar) system + which uses the same file conventions. Such files are not translated + in any way. + + + Serial Number Iff the serial number is sent, a single space separates the + serial number from the file mode. The serial number of the + transmitting program is stored as an octal string. Programs which do + not have a serial number should omit this field, or set it to 0. The + receiver's use of this field is optional. + + + Other Fields YMODEM was designed to allow additional header fields to be + added as above without creating compatibility problems with older + YMODEM programs. Please contact Omen Technology if other fields are + needed for special application requirements. + + The rest of the block is set to nulls. This is essential to preserve + upward compatibility.[4] + + If the filename block is received with a CRC or other error, a + retransmission is requested. After the filename block has been received, + it is ACK'ed if the write open is successful. If the file cannot be + opened for writing, the receiver cancels the transfer with CAN characters + as described above. + + The receiver then initiates transfer of the file contents with a "C" + character, according to the standard XMODEM/CRC protocol. + + After the file contents and XMODEM EOT have been transmitted and + acknowledged, the receiver again asks for the next pathname. + + Transmission of a null pathname terminates batch file transmission. + + Note that transmission of no files is not necessarily an error. This is + possible if none of the files requested of the sender could be opened for + reading. + + + + __________ + + 4. If, perchance, this information extends beyond 128 bytes (possible + with Unix 4.2 BSD extended file names), the block should be sent as a + 1k block as described above. + + + + + Chapter 5 XMODEM Protocol Enhancements + + + + + + + + X/YMODEM Protocol Reference June 18 1988 16 + + + + Most YMODEM receivers request CRC-16 by default. + + The Unix programs sz(1) and rz(1) included in the source code file + RZSZ.ZOO should answer other questions about YMODEM batch protocol. + + Figure 3. YMODEM Batch Transmission Session (1 file) + + SENDER RECEIVER + "sb foo.*" + "sending in batch mode etc." + C (command:rb) + SOH 00 FF foo.c NUL[123] CRC CRC + ACK + C + SOH 01 FE Data[128] CRC CRC + ACK + SOH 02 FC Data[128] CRC CRC + ACK + SOH 03 FB Data[100] CPMEOF[28] CRC CRC + ACK + EOT + NAK + EOT + ACK + C + SOH 00 FF NUL[128] CRC CRC + ACK + + Figure 7. YMODEM Header Information and Features + + _____________________________________________________________ + | Program | Length | Date | Mode | S/N | 1k-Blk | YMODEM-g | + |___________|________|______|______|_____|________|__________| + |Unix rz/sz | yes | yes | yes | no | yes | sb only | + |___________|________|______|______|_____|________|__________| + |VMS rb/sb | yes | no | no | no | yes | no | + |___________|________|______|______|_____|________|__________| + |Pro-YAM | yes | yes | no | yes | yes | yes | + |___________|________|______|______|_____|________|__________| + |CP/M YAM | no | no | no | no | yes | no | + |___________|________|______|______|_____|________|__________| + |KMD/IMP | ? | no | no | no | yes | no | + |___________|________|______|______|_____|________|__________| + + 5.1 KMD/IMP Exceptions to YMODEM + + KMD and IMP use a "CK" character sequence emitted by the receiver to + trigger the use of 1024 byte blocks as an alternative to specifying this + option to the sending program. This two character sequence generally + works well on single process micros in direct communication, provided the + programs rigorously adhere to all the XMODEM recommendations included + + + + Chapter 5 XMODEM Protocol Enhancements + + + + + + + + X/YMODEM Protocol Reference June 18 1988 17 + + + + Figure 4. YMODEM Batch Transmission Session (2 files) + + SENDER RECEIVER + "sb foo.c baz.c" + "sending in batch mode etc." + C (command:rb) + SOH 00 FF foo.c NUL[123] CRC CRC + ACK + C + SOH 01 FE Data[128] CRC CRC + ACK + SOH 02 FC Data[128] CRC CRC + ACK + SOH 03 FB Data[100] CPMEOF[28] CRC CRC + ACK + EOT + NAK + EOT + ACK + C + SOH 00 FF baz.c NUL[123] CRC CRC + ACK + C + SOH 01 FB Data[100] CPMEOF[28] CRC CRC + ACK + EOT + NAK + EOT + ACK + C + SOH 00 FF NUL[128] CRC CRC + ACK + + Figure 5. YMODEM Batch Transmission Session-1k Blocks + + SENDER RECEIVER + "sb -k foo.*" + "sending in batch mode etc." + C (command:rb) + SOH 00 FF foo.c NUL[123] CRC CRC + ACK + C + STX 01 FD Data[1024] CRC CRC + ACK + SOH 02 FC Data[128] CRC CRC + ACK + SOH 03 FB Data[100] CPMEOF[28] CRC CRC + ACK + EOT + NAK + EOT + + + + Chapter 5 XMODEM Protocol Enhancements + + + + + + + + X/YMODEM Protocol Reference June 18 1988 18 + + + + ACK + C + SOH 00 FF NUL[128] CRC CRC + ACK + + Figure 6. YMODEM Filename block transmitted by sz + + -rw-r--r-- 6347 Jun 17 1984 20:34 bbcsched.txt + + 00 0100FF62 62637363 6865642E 74787400 |...bbcsched.txt.| + 10 36333437 20333331 34373432 35313320 |6347 3314742513 | + 20 31303036 34340000 00000000 00000000 |100644..........| + 30 00000000 00000000 00000000 00000000 + 40 00000000 00000000 00000000 00000000 + 50 00000000 00000000 00000000 00000000 + 60 00000000 00000000 00000000 00000000 + 70 00000000 00000000 00000000 00000000 + 80 000000CA 56 + + herein. Programs with marginal XMODEM implementations do not fare so + well. Timesharing systems and packet switched networks can separate the + successive characters, rendering this method unreliable. + + Sending programs may detect the CK sequence if the operating enviornment + does not preclude reliable implementation. + + Instead of the standard YMODEM file length in decimal, KMD and IMP + transmit the CP/M record count in the last two bytes of the header block. + + + 6. YMODEM-g File Transmission + + Developing technology is providing phone line data transmission at ever + higher speeds using very specialized techniques. These high speed modems, + as well as session protocols such as X.PC, provide high speed, nearly + error free communications at the expense of considerably increased delay + time. + + This delay time is moderate compared to human interactions, but it + cripples the throughput of most error correcting protocols. + + The g option to YMODEM has proven effective under these circumstances. + The g option is driven by the receiver, which initiates the batch transfer + by transmitting a G instead of C. When the sender recognizes the G, it + bypasses the usual wait for an ACK to each transmitted block, sending + succeeding blocks at full speed, subject to XOFF/XON or other flow control + exerted by the medium. + + The sender expects an inital G to initiate the transmission of a + particular file, and also expects an ACK on the EOT sent at the end of + each file. This synchronization allows the receiver time to open and + + + + Chapter 6 XMODEM Protocol Enhancements + + + + + + + + X/YMODEM Protocol Reference June 18 1988 19 + + + + close files as necessary. + + If an error is detected in a YMODEM-g transfer, the receiver aborts the + transfer with the multiple CAN abort sequence. The ZMODEM protocol should + be used in applications that require both streaming throughput and error + recovery. + + Figure 8. YMODEM-g Transmission Session + + SENDER RECEIVER + "sb foo.*" + "sending in batch mode etc..." + G (command:rb -g) + SOH 00 FF foo.c NUL[123] CRC CRC + G + SOH 01 FE Data[128] CRC CRC + STX 02 FD Data[1024] CRC CRC + SOH 03 FC Data[128] CRC CRC + SOH 04 FB Data[100] CPMEOF[28] CRC CRC + EOT + ACK + G + SOH 00 FF NUL[128] CRC CRC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Chapter 6 XMODEM Protocol Enhancements + + + + + + + + X/YMODEM Protocol Reference June 18 1988 20 + + + + 7. XMODEM PROTOCOL OVERVIEW + + 8/9/82 by Ward Christensen. + + I will maintain a master copy of this. Please pass on changes or + suggestions via CBBS/Chicago at (312) 545-8086, CBBS/CPMUG (312) 849-1132 + or by voice at (312) 849-6279. + + 7.1 Definitions + + 01H + 04H + 06H + 15H + 18H + 43H + + + 7.2 Transmission Medium Level Protocol + + Asynchronous, 8 data bits, no parity, one stop bit. + + The protocol imposes no restrictions on the contents of the data being + transmitted. No control characters are looked for in the 128-byte data + messages. Absolutely any kind of data may be sent - binary, ASCII, etc. + The protocol has not formally been adopted to a 7-bit environment for the + transmission of ASCII-only (or unpacked-hex) data , although it could be + simply by having both ends agree to AND the protocol-dependent data with + 7F hex before validating it. I specifically am referring to the checksum, + and the block numbers and their ones- complement. + + Those wishing to maintain compatibility of the CP/M file structure, i.e. + to allow modemming ASCII files to or from CP/M systems should follow this + data format: + + + ASCII tabs used (09H); tabs set every 8. + + + Lines terminated by CR/LF (0DH 0AH) + + + End-of-file indicated by ^Z, 1AH. (one or more) + + + Data is variable length, i.e. should be considered a continuous + stream of data bytes, broken into 128-byte chunks purely for the + purpose of transmission. + + + A CP/M "peculiarity": If the data ends exactly on a 128-byte + boundary, i.e. CR in 127, and LF in 128, a subsequent sector + containing the ^Z EOF character(s) is optional, but is preferred. + Some utilities or user programs still do not handle EOF without ^Zs. + + + + + + Chapter 7 Xmodem Protocol Overview + + + + + + + + X/YMODEM Protocol Reference June 18 1988 21 + + + + + The last block sent is no different from others, i.e. there is no + "short block". + Figure 9. XMODEM Message Block Level Protocol + + Each block of the transfer looks like: + <255-blk #><--128 data bytes--> + in which: + = 01 hex + = binary number, starts at 01 increments by 1, and + wraps 0FFH to 00H (not to 01) + <255-blk #> = blk # after going thru 8080 "CMA" instr, i.e. + each bit complemented in the 8-bit block number. + Formally, this is the "ones complement". + = the sum of the data bytes only. Toss any carry. + + 7.3 File Level Protocol + + 7.3.1 Common_to_Both_Sender_and_Receiver + All errors are retried 10 times. For versions running with an operator + (i.e. NOT with XMODEM), a message is typed after 10 errors asking the + operator whether to "retry or quit". + + Some versions of the protocol use , ASCII ^X, to cancel transmission. + This was never adopted as a standard, as having a single "abort" character + makes the transmission susceptible to false termination due to an + or being corrupted into a and aborting transmission. + + The protocol may be considered "receiver driven", that is, the sender need + not automatically re-transmit, although it does in the current + implementations. + + + 7.3.2 Receive_Program_Considerations + The receiver has a 10-second timeout. It sends a every time it + times out. The receiver's first timeout, which sends a , signals the + transmitter to start. Optionally, the receiver could send a + immediately, in case the sender was ready. This would save the initial 10 + second timeout. However, the receiver MUST continue to timeout every 10 + seconds in case the sender wasn't ready. + + Once into a receiving a block, the receiver goes into a one-second timeout + for each character and the checksum. If the receiver wishes to a + block for any reason (invalid header, timeout receiving data), it must + wait for the line to clear. See "programming tips" for ideas + + Synchronizing: If a valid block number is received, it will be: 1) the + expected one, in which case everything is fine; or 2) a repeat of the + previously received block. This should be considered OK, and only + indicates that the receivers got glitched, and the sender re- + transmitted; 3) any other block number indicates a fatal loss of + synchronization, such as the rare case of the sender getting a line-glitch + + + + Chapter 7 Xmodem Protocol Overview + + + + + + + + X/YMODEM Protocol Reference June 18 1988 22 + + + + that looked like an . Abort the transmission, sending a + + + 7.3.3 Sending_program_considerations + While waiting for transmission to begin, the sender has only a single very + long timeout, say one minute. In the current protocol, the sender has a + 10 second timeout before retrying. I suggest NOT doing this, and letting + the protocol be completely receiver-driven. This will be compatible with + existing programs. + + When the sender has no more data, it sends an , and awaits an , + resending the if it doesn't get one. Again, the protocol could be + receiver-driven, with the sender only having the high-level 1-minute + timeout to abort. + + + Here is a sample of the data flow, sending a 3-block message. It includes + the two most common line hits - a garbaged block, and an reply + getting garbaged. represents the checksum byte. + + Figure 10. Data flow including Error Recovery + + SENDER RECEIVER + times out after 10 seconds, + <--- + 01 FE -data- ---> + <--- + 02 FD -data- xx ---> (data gets line hit) + <--- + 02 FD -data- xx ---> + <--- + 03 FC -data- xx ---> + (ack gets garbaged) <--- + 03 FC -data- xx ---> + ---> + <--- + ---> + <--- + (finished) + + 7.4 Programming Tips + + + The character-receive subroutine should be called with a parameter + specifying the number of seconds to wait. The receiver should first + call it with a time of 10, then and try again, 10 times. + + After receiving the , the receiver should call the character + receive subroutine with a 1-second timeout, for the remainder of the + message and the . Since they are sent as a continuous stream, + timing out of this implies a serious like glitch that caused, say, + 127 characters to be seen instead of 128. + + + + Chapter 7 Xmodem Protocol Overview + + + + + + + + X/YMODEM Protocol Reference June 18 1988 23 + + + + + When the receiver wishes to , it should call a "PURGE" + subroutine, to wait for the line to clear. Recall the sender tosses + any characters in its UART buffer immediately upon completing sending + a block, to ensure no glitches were mis- interpreted. + + The most common technique is for "PURGE" to call the character + receive subroutine, specifying a 1-second timeout,[1] and looping + back to PURGE until a timeout occurs. The is then sent, + ensuring the other end will see it. + + + You may wish to add code recommended by John Mahr to your character + receive routine - to set an error flag if the UART shows framing + error, or overrun. This will help catch a few more glitches - the + most common of which is a hit in the high bits of the byte in two + consecutive bytes. The comes out OK since counting in 1-byte + produces the same result of adding 80H + 80H as with adding 00H + + 00H. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + __________ + + 1. These times should be adjusted for use with timesharing systems. + + + + + Chapter 7 Xmodem Protocol Overview + + + + + + + + X/YMODEM Protocol Reference June 18 1988 24 + + + + 8. XMODEM/CRC Overview + + Original 1/13/85 by John Byrns -- CRC option. + + Please pass on any reports of errors in this document or suggestions for + improvement to me via Ward's/CBBS at (312) 849-1132, or by voice at (312) + 885-1105. + + The CRC used in the Modem Protocol is an alternate form of block check + which provides more robust error detection than the original checksum. + Andrew S. Tanenbaum says in his book, Computer Networks, that the CRC- + CCITT used by the Modem Protocol will detect all single and double bit + errors, all errors with an odd number of bits, all burst errors of length + 16 or less, 99.997% of 17-bit error bursts, and 99.998% of 18-bit and + longer bursts.[1] + + The changes to the Modem Protocol to replace the checksum with the CRC are + straight forward. If that were all that we did we would not be able to + communicate between a program using the old checksum protocol and one + using the new CRC protocol. An initial handshake was added to solve this + problem. The handshake allows a receiving program with CRC capability to + determine whether the sending program supports the CRC option, and to + switch it to CRC mode if it does. This handshake is designed so that it + will work properly with programs which implement only the original + protocol. A description of this handshake is presented in section 10. + + Figure 11. Message Block Level Protocol, CRC mode + + Each block of the transfer in CRC mode looks like: + <255-blk #><--128 data bytes--> + in which: + = 01 hex + = binary number, starts at 01 increments by 1, and + wraps 0FFH to 00H (not to 01) + <255-blk #> = ones complement of blk #. + = byte containing the 8 hi order coefficients of the CRC. + = byte containing the 8 lo order coefficients of the CRC. + + 8.1 CRC Calculation + + 8.1.1 Formal_Definition + To calculate the 16 bit CRC the message bits are considered to be the + coefficients of a polynomial. This message polynomial is first multiplied + by X^16 and then divided by the generator polynomial (X^16 + X^12 + X^5 + + + + __________ + + 1. This reliability figure is misleading because XMODEM's critical + supervisory functions are not protected by this CRC. + + + + + Chapter 8 Xmodem Protocol Overview + + + + + + + + X/YMODEM Protocol Reference June 18 1988 25 + + + + 1) using modulo two arithmetic. The remainder left after the division is + the desired CRC. Since a message block in the Modem Protocol is 128 bytes + or 1024 bits, the message polynomial will be of order X^1023. The hi order + bit of the first byte of the message block is the coefficient of X^1023 in + the message polynomial. The lo order bit of the last byte of the message + block is the coefficient of X^0 in the message polynomial. + + Figure 12. Example of CRC Calculation written in C + + The following XMODEM crc routine is taken from "rbsb.c". Please refer to + the source code for these programs (contained in RZSZ.ZOO) for usage. A + fast table driven version is also included in this file. + + /* update CRC */ + unsigned short + updcrc(c, crc) + register c; + register unsigned crc; + { + register count; + + for (count=8; --count>=0;) { + if (crc & 0x8000) { + crc <<= 1; + crc += (((c<<=1) & 0400) != 0); + crc ^= 0x1021; + } + else { + crc <<= 1; + crc += (((c<<=1) & 0400) != 0); + } + } + return crc; + } + + 8.2 CRC File Level Protocol Changes + + 8.2.1 Common_to_Both_Sender_and_Receiver + The only change to the File Level Protocol for the CRC option is the + initial handshake which is used to determine if both the sending and the + receiving programs support the CRC mode. All Modem Programs should support + the checksum mode for compatibility with older versions. A receiving + program that wishes to receive in CRC mode implements the mode setting + handshake by sending a in place of the initial . If the sending + program supports CRC mode it will recognize the and will set itself + into CRC mode, and respond by sending the first block as if a had + been received. If the sending program does not support CRC mode it will + not respond to the at all. After the receiver has sent the it will + wait up to 3 seconds for the that starts the first block. If it + receives a within 3 seconds it will assume the sender supports CRC + mode and will proceed with the file exchange in CRC mode. If no is + + + + Chapter 8 Xmodem Protocol Overview + + + + + + + + X/YMODEM Protocol Reference June 18 1988 26 + + + + received within 3 seconds the receiver will switch to checksum mode, send + a , and proceed in checksum mode. If the receiver wishes to use + checksum mode it should send an initial and the sending program + should respond to the as defined in the original Modem Protocol. + After the mode has been set by the initial or the protocol + follows the original Modem Protocol and is identical whether the checksum + or CRC is being used. + + + 8.2.2 Receive_Program_Considerations + There are at least 4 things that can go wrong with the mode setting + handshake. + + 1. the initial can be garbled or lost. + + 2. the initial can be garbled. + + 3. the initial can be changed to a . + + 4. the initial from a receiver which wants to receive in checksum + can be changed to a . + + The first problem can be solved if the receiver sends a second after + it times out the first time. This process can be repeated several times. + It must not be repeated too many times before sending a and + switching to checksum mode or a sending program without CRC support may + time out and abort. Repeating the will also fix the second problem if + the sending program cooperates by responding as if a were received + instead of ignoring the extra . + + It is possible to fix problems 3 and 4 but probably not worth the trouble + since they will occur very infrequently. They could be fixed by switching + modes in either the sending or the receiving program after a large number + of successive s. This solution would risk other problems however. + + + 8.2.3 Sending_Program_Considerations + The sending program should start in the checksum mode. This will insure + compatibility with checksum only receiving programs. Anytime a is + received before the first or the sending program should set + itself into CRC mode and respond as if a were received. The sender + should respond to additional s as if they were s until the first + is received. This will assist the receiving program in determining + the correct mode when the is lost or garbled. After the first + is received the sending program should ignore s. + + + + + + + + + + Chapter 8 Xmodem Protocol Overview + + + + + + + + X/YMODEM Protocol Reference June 18 1988 27 + + + + 8.3 Data Flow Examples with CRC Option + + Here is a data flow example for the case where the receiver requests + transmission in the CRC mode but the sender does not support the CRC + option. This example also includes various transmission errors. + represents the checksum byte. + + Figure 13. Data Flow: Receiver has CRC Option, Sender Doesn't + + SENDER RECEIVER + <--- + times out after 3 seconds, + <--- + times out after 3 seconds, + <--- + times out after 3 seconds, + <--- + times out after 3 seconds, + <--- + 01 FE -data- ---> + <--- + 02 FD -data- ---> (data gets line hit) + <--- + 02 FD -data- ---> + <--- + 03 FC -data- ---> + (ack gets garbaged) <--- + times out after 10 seconds, + <--- + 03 FC -data- ---> + <--- + ---> + <--- + + Here is a data flow example for the case where the receiver requests + transmission in the CRC mode and the sender supports the CRC option. This + example also includes various transmission errors. represents the + 2 CRC bytes. + + + + + + + + + + + + + + + + + Chapter 8 Xmodem Protocol Overview + + + + + + + + X/YMODEM Protocol Reference June 18 1988 28 + + + + Figure 14. Receiver and Sender Both have CRC Option + + SENDER RECEIVER + <--- + 01 FE -data- ---> + <--- + 02 FD -data- ---> (data gets line hit) + <--- + 02 FD -data- ---> + <--- + 03 FC -data- ---> + (ack gets garbaged) <--- + times out after 10 seconds, + <--- + 03 FC -data- ---> + <--- + ---> + <--- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Chapter 8 Xmodem Protocol Overview + + + + + + + + X/YMODEM Protocol Reference June 18 1988 29 + + + + 9. MORE INFORMATION + + Please contact Omen Technology for troff source files and typeset copies + of this document. + + + 9.1 TeleGodzilla Bulletin Board + + More information may be obtained by calling TeleGodzilla at 503-621-3746. + Speed detection is automatic for 1200, 2400 and 19200(Telebit PEP) bps. + TrailBlazer modem users may issue the TeleGodzilla trailblazer command to + swith to 19200 bps once they have logged in. + + Interesting files include RZSZ.ZOO (C source code), YZMODEM.ZOO (Official + XMODEM, YMODEM, and ZMODEM protocol descriptions), ZCOMMEXE.ARC, + ZCOMMDOC.ARC, and ZCOMMHLP.ARC (PC-DOS shareware comm program with XMODEM, + True YMODEM(TM), ZMODEM, Kermit Sliding Windows, Telink, MODEM7 Batch, + script language, etc.). + + + 9.2 Unix UUCP Access + + UUCP sites can obtain the current version of this file with + uucp omen!/u/caf/public/ymodem.doc /tmp + A continually updated list of available files is stored in + /usr/spool/uucppublic/FILES. When retrieving these files with uucp, + remember that the destination directory on your system must be writeable + by anyone, or the UUCP transfer will fail. + + The following L.sys line calls TeleGodzilla (Pro-YAM in host operation). + TeleGodzilla determines the incoming speed automatically. + + In response to "Name Please:" uucico gives the Pro-YAM "link" command as a + user name. The password (Giznoid) controls access to the Xenix system + connected to the IBM PC's other serial port. Communications between + Pro-YAM and Xenix use 9600 bps; YAM converts this to the caller's speed. + + Finally, the calling uucico logs in as uucp. + + omen Any ACU 2400 1-503-621-3746 se:--se: link ord: Giznoid in:--in: uucp + + + + 10. REVISIONS + + 6-18-88 Further revised for clarity. Corrected block numbering in two + examples. + 10-27-87 Optional fields added for number of files remaining to be sent + and total number of bytes remaining to be sent. + 10-18-87 Flow control discussion added to 1024 byte block descritpion, + minor revisions for clarity per user comments. + + + + Chapter 10 Xmodem Protocol Overview + + + + + + + + X/YMODEM Protocol Reference June 18 1988 30 + + + + 8-03-87 Revised for clarity. + 5-31-1987 emphasizes minimum requirements for YMODEM, and updates + information on accessing files. + 9-11-1986 clarifies nomenclature and some minor points. + The April 15 1986 edition clarifies some points concerning CRC + calculations and spaces in the header. + + + 11. YMODEM Programs + + ZCOMM, A shareware little brother to Professional-YAM, is available as + ZCOMMEXE.ARC on TeleGodzilla and other bulletin board systems. ZCOMM may + be used to test YMODEM amd ZMODEM implementations. + + Unix programs supporting YMODEM are available on TeleGodzilla in RZSZ.ZOO. + This ZOO archive includes a ZCOMM/Pro-YAM/PowerCom script ZUPL.T to upload + a bootstrap program MINIRB.C, compile it, and then upload the rest of the + files using the compiled MINIRB. Most Unix like systems are supported, + including V7, Xenix, Sys III, 4.2 BSD, SYS V, Idris, Coherent, and + Regulus. + + A version for VAX-VMS is available in VRBSB.SHQ. + + Irv Hoff has added 1k blocks and basic YMODEM batch transfers to the KMD + and IMP series programs, which replace the XMODEM and MODEM7/MDM7xx series + respectively. Overlays are available for a wide variety of CP/M systems. + + Questions about Professional-YAM communications software may be directed + to: + Chuck Forsberg + Omen Technology Inc + 17505-V Sauvie Island Road + Portland Oregon 97231 + VOICE: 503-621-3406 :VOICE + Modem: 503-621-3746 Speed: 19200(Telebit PEP),2400,1200,300 + Usenet: ...!tektronix!reed!omen!caf + CompuServe: 70007,2304 + GEnie: CAF + + Unlike ZMODEM and Kermit, XMODEM and YMODEM place obstacles in the path of + a reliable high performance implementation, evidenced by poor reliability + under stress of the industry leaders' XMODEM and YMODEM programs. Omen + Technology provides consulting and other services to those wishing to + implement XMODEM, YMODEM, and ZMODEM with state of the art features and + reliability. + + + + + + + + + + Chapter 11 Xmodem Protocol Overview + + + + + + + + + + + + CONTENTS + + + 1. TOWER OF BABEL................................................... 2 + 1.1 Definitions................................................. 2 + + 2. YMODEM MINIMUM REQUIREMENTS...................................... 4 + + 3. WHY YMODEM?...................................................... 6 + 3.1 Some Messages from the Pioneer.............................. 7 + + 4. XMODEM PROTOCOL ENHANCEMENTS..................................... 10 + 4.1 Graceful Abort.............................................. 10 + 4.2 CRC-16 Option............................................... 10 + 4.3 XMODEM-1k 1024 Byte Block................................... 11 + + 5. YMODEM Batch File Transmission................................... 13 + 5.1 KMD/IMP Exceptions to YMODEM................................ 16 + + 6. YMODEM-g File Transmission....................................... 18 + + 7. XMODEM PROTOCOL OVERVIEW......................................... 20 + 7.1 Definitions................................................. 20 + 7.2 Transmission Medium Level Protocol.......................... 20 + 7.3 File Level Protocol......................................... 21 + 7.4 Programming Tips............................................ 22 + + 8. XMODEM/CRC Overview.............................................. 24 + 8.1 CRC Calculation............................................. 24 + 8.2 CRC File Level Protocol Changes............................. 25 + 8.3 Data Flow Examples with CRC Option.......................... 27 + + 9. MORE INFORMATION................................................. 29 + 9.1 TeleGodzilla Bulletin Board................................. 29 + 9.2 Unix UUCP Access............................................ 29 + + 10. REVISIONS........................................................ 29 + + 11. YMODEM Programs.................................................. 30 + + + + + + + + + + + + + + + + - i - + + + + + + + + + + + + + + + LIST OF FIGURES + + + Figure 1. XMODEM-1k Blocks.......................................... 12 + + Figure 2. Mixed 1024 and 128 byte Blocks............................ 12 + + Figure 3. YMODEM Batch Transmission Session (1 file)................ 16 + + Figure 4. YMODEM Batch Transmission Session (2 files)............... 16 + + Figure 5. YMODEM Batch Transmission Session-1k Blocks............... 16 + + Figure 6. YMODEM Filename block transmitted by sz................... 16 + + Figure 7. YMODEM Header Information and Features.................... 16 + + Figure 8. YMODEM-g Transmission Session............................. 19 + + Figure 9. XMODEM Message Block Level Protocol....................... 21 + + Figure 10. Data flow including Error Recovery........................ 22 + + Figure 11. Message Block Level Protocol, CRC mode.................... 24 + + Figure 12. Example of CRC Calculation written in C................... 25 + + Figure 13. Data Flow: Receiver has CRC Option, Sender Doesn't........ 27 + + Figure 14. Receiver and Sender Both have CRC Option.................. 28 + + + + + + + + + + + + + + + + + + + + + + - ii - diff --git a/memdump/ymsend.c b/memdump/ymsend.c new file mode 100644 index 0000000..b4b16a5 --- /dev/null +++ b/memdump/ymsend.c @@ -0,0 +1,167 @@ +/* + * Ymodem send routine. Only supports 1K blocks and CRC mode. + */ + +#include +#include +#include "ymsend.h" + +enum { + SOH = 0x01, + STX = 0x02, + EOT = 0x04, + ACK = 0x06, + NAK = 0x15, + CAN = 0x18, +}; + +/* + * Append a CRC16 to a block + */ +static void add_crc16(uint8_t *blk, int len) +{ + static const uint16_t crctab[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 + }; + uint16_t crc = 0; + + while (len--) + crc = crctab[(crc >> 8) ^ *blk++] ^ crc << 8; + + *blk++ = crc >> 8; + *blk = crc; +} + +static void send_ack_blk(struct serial_if *sif, const void *data, size_t bytes) +{ + uint8_t ack_buf; + + if (bytes) + sif->write(sif, data, bytes); + + do { + do { + sif->read(sif, &ack_buf, 1); + } while (ack_buf != ACK && ack_buf != NAK); + } while (ack_buf == NAK); +} + +static const uint8_t eot_buf = EOT; + +void send_ymodem(struct serial_if *sif, struct file_info *fileinfo, + void (*gen_data)(void *, size_t, struct file_info *, size_t)) +{ + uint8_t ack_buf, blk_buf[1024+5], *np, *q; + const char *p; + size_t len, lx, pos; + int blk; + + /* Wait for initial handshake */ + printf("Waiting for handshake...\n"); + do { + sif->read(sif, &ack_buf, 1); + } while (ack_buf != 'C'); + + /* Send initial batch header (filename, length etc.) */ + q = blk_buf + 3; + p = fileinfo->name; + while (*p) + *q++ = *p++; + *q++ = '\0'; + + lx = len = fileinfo->size; + do { + q++; + lx /= 10; + } while (lx); + + np = q; + lx = len; + do { + *--np = (lx % 10) + '0'; + lx /= 10; + } while (lx); + + while (q < blk_buf+1024+3) + *q++ = 0; + + blk = 0; + pos = 0; + do { + if (blk != 0) { + gen_data(blk_buf+3, 1024, fileinfo, pos); + pos += 1024; + len = (len < 1024) ? 0 : len-1024; + } + + blk_buf[0] = STX; + blk_buf[1] = blk; + blk_buf[2] = ~blk; + add_crc16(blk_buf+3, 1024); + + printf("Sending block %d...\r", blk); + send_ack_blk(sif, blk_buf, 1024+5); + + blk++; + } while(len); + + printf("\nSending EOT...\n"); + send_ack_blk(sif, &eot_buf, 1); + printf("Done.\n"); +} + +void end_ymodem(struct serial_if *sif) +{ + uint8_t ack_buf; + uint8_t blk_buf[128+5]; + + /* Wait for initial handshake */ + printf("END: Waiting for handshake...\n"); + do { + sif->read(sif, &ack_buf, 1); + } while (ack_buf != 'C'); + + memset(blk_buf, 0, sizeof blk_buf); + blk_buf[0] = SOH; + blk_buf[1] = 0; + blk_buf[2] = 0xff; + add_crc16(blk_buf+3, 128); + printf("Sending block 0...\n"); + send_ack_blk(sif, &blk_buf, 128+5); + + printf("Sending EOT...\n"); + sif->write(sif, &eot_buf, 1); + /* rb doesn't ack the EOT for an end batch transfer. */ + printf("Done.\n"); +} diff --git a/memdump/ymsend.h b/memdump/ymsend.h new file mode 100644 index 0000000..9bcd540 --- /dev/null +++ b/memdump/ymsend.h @@ -0,0 +1,27 @@ +#ifndef YMSEND_H +#define YMSEND_H + +#include "mystuff.h" + +struct serial_if { + int port; + void *pvt; + void (*read)(struct serial_if *, void *, size_t); + void (*write)(struct serial_if *, const void *, size_t); +}; + +struct file_info { + const char *name; + size_t size; + void *pvt; +}; + +void send_ymodem(struct serial_if *, struct file_info *, + void (*)(void *, size_t, struct file_info *, size_t)); +void end_ymodem(struct serial_if *); + +int serial_init(struct serial_if *sif); +void serial_read(struct serial_if *sif, void *data, size_t n); +void serial_write(struct serial_if *sif, const void *data, size_t n); + +#endif /* YMSEND_H */ diff --git a/mime/image/x-lss16.xml b/mime/image/x-lss16.xml new file mode 100644 index 0000000..b13f47e --- /dev/null +++ b/mime/image/x-lss16.xml @@ -0,0 +1,39 @@ + + + + LSS16 image + Vyjava LSS16 + Изображение — LSS16 + imatge LSS16 + Obrázek LSS16 + LSS16-Bild + LSS16 image + imagen LSS16 + LSS16 irudia + LSS16-kuva + image LSS16 + íomhá LSS16 + LSS16 kép + Citra LSS16 + Immagine LSS16 + LSS16 画像 + LSS16 그림 + LSS16 paveikslėlis + LSS16-bilde + LSS16-afbeelding + LSS16-bilete + Obraz LSS16 + Imagem LSS16 + изображение LSS16 + Figurë LSS16 + LSS16-bild + Зображення LSS16 + Ảnh LSS16 + lss16 + SYSLINUX' LSS16 image data + + + + + + diff --git a/modules/Makefile b/modules/Makefile new file mode 100644 index 0000000..80eb995 --- /dev/null +++ b/modules/Makefile @@ -0,0 +1,60 @@ +## ----------------------------------------------------------------------- +## +## Copyright 2001-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +## +## Non-COM32 simple Syslinux modules +## + +topdir = .. +include $(topdir)/MCONFIG.embedded + +INCLUDES = -I$(com32)/include + +BINS = pxechain.com gfxboot.com poweroff.com + +all: $(BINS) + +.PRECIOUS: %.o + +.PRECIOUS: %.elf +%.elf: c32entry.o %.o $(LIB) + $(LD) -Ttext 0x101000 -e _start -o $@ $^ + +%.c32: %.elf + $(OBJCOPY) -O binary $< $@ + +%.com: %.asm + ( $(NASM) -M -DDEPEND -o $@ $< ; echo '' ) > .$@.d ; true + $(NASM) $(NASMOPT) -f bin -o $@ -l $*.lst $< + +$(LIB): $(LIBOBJS) + rm -f $@ + $(AR) cq $@ $^ + $(RANLIB) $@ + +%.lss: %.ppm.gz $(PPMTOLSS16) + $(GZIPPROG) -cd $< | \ + $(PPMTOLSS16) \#000000=0 \#d0d0d0=7 \#f6f6f6=15 \ + > $@ + +%.ppm.gz: %.png + $(PNGTOPNM) $< | gzip -9 > $@ + +tidy dist: + rm -f *.o *.a *.lst *.elf *.map .*.d + +clean: tidy + +spotless: clean + rm -f $(BINS) + +-include .*.d diff --git a/modules/gfxboot.asm b/modules/gfxboot.asm new file mode 100644 index 0000000..c9fdd1b --- /dev/null +++ b/modules/gfxboot.asm @@ -0,0 +1,985 @@ +; **************************************************************************** +; +; gfxboot.asm +; +; Copyright 2008-2009 Sebastian Herbszt +; +; This module is based on the gfxboot integration patch by Steffen Winterfeldt: +; +; Copyright 2001-2008 Steffen Winterfeldt +; +; Some parts borrowed from Syslinux core: +; +; Copyright 1994-2009 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +; Boston MA 02111-1307, USA; either version 2 of the License, or +; (at your option) any later version; incorporated herein by reference. +; +; **************************************************************************** + + [map all gfxboot.map] + + absolute 0 +pspInt20: resw 1 +pspNextP: resw 1 + resb 124 +pspCmdLen: resb 1 +pspCmdArg: resb 127 + +;%define DEBUG + + section .text + org 100h + +_start: + ; Zero memory from the start of .bss to the stack + cld + mov di,section..bss.start + mov cx,sp + sub cx,di + shr cx,2 + xor eax,eax + rep stosd + + mov ax,2 + mov bx, msg_progname + int 22h + + mov ax,2 + mov bx, msg_crlf + int 22h + + push es + mov ax,0ah + mov cl,9 + int 22h + pop es + cmp al,32h + jnz not_pxelinux + + mov dl,0 ; fake drive number + mov cl,11 ; fake sector size 2048 bytes + +not_pxelinux: + mov [derivative_id],al + mov [drivenumber],dl + mov [sectorshift],cl + mov ax,1 + shl ax,cl + mov [sectorsize],ax + mov ax,trackbufsize + shr ax,cl + mov [BufSafe],ax + + xor cx,cx + mov cl,[pspCmdLen] + dec cx + and cx,cx + jne continue + + mov ax,2 + mov bx, msg_usage + int 22h + ret +continue: + mov di,pspCmdArg+1 + add di,cx + dec di + std + mov al,' ' + repe scasb + inc cx + cld + mov [pspCmdLen],cl + mov si,pspCmdArg+1 + mov di,si + add di,cx + xor al,al + stosb + mov si,pspCmdArg+1 + +; get config file name + mov ax,0eh + int 22h + +; open config file + mov si,bx ; es:bx config file name + mov ax,6 + int 22h + jc no_config_file + and eax,eax + jz no_config_file + jmp got_config_file +no_config_file: + push es + push bx + push cs + pop es + mov bx, msg_config_file + mov ax,2 + int 22h + mov bx, msg_space + mov ax,2 + int 22h + pop bx + pop es + mov ax,2 + int 22h + push cs + pop es + mov bx, msg_space + mov ax,2 + int 22h + mov bx, msg_missing + mov ax,2 + int 22h + mov ax,2 + mov bx, msg_crlf + int 22h + ret +got_config_file: + push cs + pop es + call parse_config + +; get_gfx_file + mov ax,cs + add ax,2000h + mov word [gfx_mem_start_seg],ax + mov ax,[pspNextP] + mov word [gfx_mem_end_seg],ax + + call gfx_init + jc error + + call gfx_setup_menu + jc exit + +input: + call gfx_input + jc exit + + cmp eax,1 + jz exit + + cmp eax,2 + jz boot + + jmp input + +boot: + call far [gfx_bc_done] + mov ax,cs + mov es,ax + mov bx,command_line + mov ax,3 + int 22h +exit: + call far [gfx_bc_done] +error: + ret + +cb_table dw cb_status ; 0 + dw cb_fopen ; 1 + dw cb_fread ; 2 + dw cb_getcwd ; 3 + dw cb_chdir ; 4 + dw cb_readsector ; 5 +cb_len equ ($-cb_table)/2 + +gfx_cb: + push cs + pop ds + + cmp al,cb_len + jae gfx_cb_error + + movzx bx,al + add bx,bx + call word [bx+cb_table] + jmp gfx_cb_end +gfx_cb_error: + mov al,0ffh +gfx_cb_end: + retf + +; Return status info +; +; return: +; edx filename buffer (64 bytes) +; +cb_status: + mov edx,cs + shl edx,4 + add edx,fname_buf + + xor al,al + ret + +; Open file +; +; return: +; al 0: ok, 1: file not found +; ecx file length (al = 0) +; +cb_fopen: + push ds + pop es + mov ax,6 + mov si,fname_buf + int 22h + jnc cb_fopen_ok +cb_fopen_failed: + mov al,1 + jmp cb_fopen_end +cb_fopen_ok: + mov ecx,eax + mov [f_handle],si + mov [f_size],ecx + xor al,al +cb_fopen_end: + ret + +; Read next chunk +; +; return: +; edx buffer address (linear) +; ecx data length (< 64k) +; +cb_fread: + cmp dword [f_size],0 + jz cb_fread_eof + push ds + pop es + mov ax,7 + mov si,[f_handle] + mov bx,trackbuf + mov cx,[BufSafe] + int 22h + mov al,1 + jc cb_fread_end + sub [f_size], ecx + or si,si + jnz cb_fread_noeof + and dword [f_size],0 +cb_fread_noeof: + mov edx,cs + shl edx,4 + add edx,trackbuf + jmp cb_fread_ok +cb_fread_eof: + xor ecx,ecx +cb_fread_ok: + xor al,al +cb_fread_end: + ret + +; Return current working directory +; +; return: +; edx filename +; +cb_getcwd: + mov edx,cs + shl edx,4 + add edx,gfx_slash + xor al,al + ret + +; Set current working directory +; +cb_chdir: + xor al,al + ret + +; Read sector +; +; edx sector +; +; return: +; edx buffer (linear address) +; +; Note: does not return on error! +; +cb_readsector: + push esi + push edi + push ds + pop es + mov ax,19h + xor esi,esi + xor edi,edi + mov cx,1 + mov bx,trackbuf + int 22h + pop edi + pop esi + mov al,1 + jc cb_readsector_end + mov edx,ds + shl dx,4 + add edx,trackbuf + xor al,al +cb_readsector_end: + ret + +gfx_init: + mov ax,0e801h + xor bx,bx + xor cx,cx + xor dx,dx + int 15h + jnc got_e801 + + mov ax,2 + mov bx, msg_memory + int 22h + stc + ret + +got_e801: + cmp ax,3c00h + jb mem_below_16mb + shl ebx,6 + add eax,ebx + +mem_below_16mb: + shl eax,10 + mov [gfx_bios_mem_size],eax + shr eax,20 + cmp ax,16 + jb skip_extended + + mov word [gfx_xmem_0],88h ; 8MB at 8MB + mov dword [gfx_save_area1],7f0000h ; 8MB-64k + +skip_extended: + movzx ebx,word [gfx_mem_start_seg] + shl ebx,4 + + movzx ecx,word [gfx_mem_end_seg] + shl ecx,4 + + mov dword [gfx_mem],ebx + mov dword [gfx_mem0_start],ebx + mov dword [gfx_mem0_end],ecx + + call gfx_read_file + jc gfx_init_end + + call gfx_get_sysconfig + + ; align 4 + mov eax,[gfx_mem0_start] + add eax,3 + and eax,~3 + mov [gfx_mem0_start],eax + +; setup jump table + les bx,[gfx_bc_jt] + + mov ax,[es:bx] + mov [gfx_bc_init],ax + mov [gfx_bc_init+2],es + + mov ax,[es:bx+2] + mov [gfx_bc_done],ax + mov [gfx_bc_done+2],es + + mov ax,[es:bx+4] + mov [gfx_bc_input],ax + mov [gfx_bc_input+2],es + + mov ax,[es:bx+6] + mov [gfx_bc_menu_init],ax + mov [gfx_bc_menu_init+2],es + +; ... + + mov esi,cs + shl esi,4 + add esi,gfx_sysconfig + call far [gfx_bc_init] + +gfx_init_end: + ret + +gfx_read_file: +; open file +; es:si - file name + + push cs + pop es + mov ax,6 + mov si,pspCmdArg+1 + int 22h + jnc gfx_file_read + stc + ret + +gfx_file_read: +; si - file handle +; eax - length of file in bytes, or -1 +; cx - file block size + + mov edx,[gfx_mem0_end] + sub edx,[gfx_mem0_start] + sub edx,0fh ; space to allow for aligning later + ; edx: max allowed size + + cmp eax,-1 ; unknown file size -> set to max allowed size + jnz .has_size + mov eax,edx +.has_size: + cmp eax,edx + jbe read_bootlogo + +gfx_file_too_big: + mov ax,2 + mov bx,msg_bootlogo_toobig + int 22h + stc + ret + +read_bootlogo: + mov [file_length],eax + mov edi,[gfx_mem] + +; read file +; si - file handle +; es:bx - buffer +; cx - number of blocks to read + +read: + push eax + mov ax,7 + mov bx,trackbuf + mov cx,[BufSafe] + int 22h + + push edi + push ecx + push si + push es + + mov si,trackbuf + push edi + call gfx_l2so + pop di + pop es + + rep movsb ; move ds:si -> es:di, length ecx + pop es + pop si + pop ecx + pop edi + + pop eax + + ; si == 0: EOF + or si,si + jz gfx_read_done + add edi,ecx + sub eax,ecx + ja read + jmp gfx_file_too_big +gfx_read_done: + sub eax,ecx + mov edx,[file_length] + sub edx,eax + ; edx = real file size + mov [gfx_archive_end],edx + add edx,[gfx_mem0_start] + add edx,0fh ; for alignment + mov [gfx_mem0_start],edx + +bootlogo_read_done: + call find_file + or eax,eax + jnz found_bootlogo + stc + ret + +found_bootlogo: + push edi + push eax + add eax,edi + push dword [gfx_mem] + pop dword [gfx_archive_start] + neg al + and eax,byte 0fh + jz no_align + add [gfx_archive_start],eax + +no_align: + pop eax + pop edi + sub edi,[gfx_mem] + mov ecx,[gfx_archive_start] + add edi,ecx + mov [gfx_file],edi + add [gfx_archive_end],ecx + add eax,edi + shr eax,4 + mov [gfx_bc_jt+2],ax + ret + +gfx_get_sysconfig: + mov ah,0 + cmp byte [derivative_id],33h + jnz not_isolinux + mov ah,2 +not_isolinux: + mov al,[drivenumber] + mov [gfx_boot_drive],al + cmp al,80h ; floppy ? + jae not_floppy + mov ah,1 +not_floppy: + mov byte [gfx_media_type],ah + mov ah,[sectorshift] + mov byte [gfx_sector_shift],ah + mov ax,cs + mov [gfx_bootloader_seg],ax + ret + +gfx_setup_menu: + push es + push ds + pop es + + mov word [menu_desc+menu_ent_list],0 + mov di,[menu_seg] + mov [menu_desc+menu_ent_list+2],di + + mov word [menu_desc+menu_default],dentry_buf + mov [menu_desc+menu_default+2],cs + + mov di,256 + mov [menu_desc+menu_arg_list],di + mov di,[menu_seg] + mov [menu_desc+menu_arg_list+2],di + + mov cx,[label_cnt] + mov [menu_desc+menu_entries],cx + + mov cx,256*2 + mov [menu_desc+menu_ent_size],cx + mov [menu_desc+menu_arg_size],cx + + mov esi,ds + shl esi,4 + add esi,menu_desc + + call far [gfx_bc_menu_init] + pop es + ret + +magic_ok: + xor eax,eax + cmp dword [es:bx],0b2d97f00h ; header.magic_id + jnz magic_ok_end + cmp byte [es:bx+4],8 ; header.version + jnz magic_ok_end + mov eax,[es:bx+8] +magic_ok_end: + ret + +find_file: + mov edi,[gfx_mem] + push edi + call gfx_l2so + pop bx + pop es + call magic_ok + or eax,eax + jnz find_file_end + +find_file_loop: + mov ecx,[gfx_mem0_start] + sub ecx,26 + 12 ; min cpio header + gfx header + cmp edi,ecx + jae find_file_end + push edi + call gfx_l2so + pop bx + pop es + cmp word [es:bx],71c7h + jnz find_file_end + mov ax,[es:bx+20] ; file name size + movzx esi,ax + + inc si + and si,~1 ; align + + mov eax,[es:bx+22] ; data size + rol eax,16 ; get word order right + mov ecx,eax + + inc ecx + and ecx,byte ~1 ; align + + add si,26 ; skip header + + add edi,esi + add bx,si + call magic_ok + or eax,eax + jnz find_file_end + + add edi,ecx + jmp find_file_loop + +find_file_end: + ret + +gfx_input: + mov edi,cs + shl edi,4 + add edi, command_line ; buffer (0: no buffer) + mov ecx, max_cmd_len ; buffer size +; xor eax,eax ; timeout value (0: no timeout) + mov eax,100 ; timeout value (0: no timeout) + + call far [gfx_bc_input] + ret + +gfx_l2so: + push eax + mov eax,[esp + 6] + shr eax,4 + mov [esp + 8],ax + and word [esp + 6],byte 0fh + pop eax + ret + +parse_config: + mov [f_handle],si + push es + mov ax,cs + add ax,1000h + mov es,ax + mov word [menu_seg],ax + xor eax,eax + mov cx,4000h + mov di,0 + rep stosd + pop es +.read: + call skipspace + jz .eof + jc .read + cmp al,'#' + je .nextline + or al,20h ; convert to lower case + mov di,configbuf + stosb +.read_loop: + call getc + jc .eof + cmp al,' ' + jbe .done + or al,20h ; convert to lower case + stosb + jmp .read_loop +.done: + call ungetc + + xor ax,ax + stosb +%ifdef DEBUG + mov ax,2 + mov bx, configbuf + int 22h + + mov ax,2 + mov bx, msg_crlf + int 22h +%endif + push si + push di + xor ecx,ecx + mov si,configbuf + mov di,label_keyword+1 + mov cl, byte [label_keyword] + call memcmp + pop di + pop si + jz .do_label + + push si + push di + xor ecx,ecx + mov si,configbuf + mov di,default_keyword+1 + mov cl, byte [default_keyword] + call memcmp + pop di + pop si + jz .do_default + +.nextline: + call skipline + jmp .read + +.do_label: + call skipspace + jz .eof + jc .noparm + call ungetc + push es + push di + mov ax,[menu_seg] + mov es,ax + mov di,[menu_off] + call getline + mov di,[menu_off] + add di,512 + mov [menu_off],di + pop di + pop es + inc word [label_cnt] + + jmp .read + +.do_default: + call skipspace + jz .eof + jc .noparm + call ungetc + push es + push di + push cs + pop es + mov di,dentry_buf + call getline + pop di + pop es + + jmp .read + +.eof: +.noparm: + ret + +skipline: + cmp al,10 + je .end + call getc + jc .end + jmp skipline +.end: + ret + +skipspace: +.loop: + call getc + jc .eof + cmp al,0Ah + je .eoln + cmp al,' ' + jbe .loop + ret +.eof: + cmp al,al + stc + ret +.eoln: + add al,0FFh + ret + +ungetc: + mov byte [ungetc_cnt],1 + mov byte [ungetcdata],al + ret + +getc: + cmp byte [ungetc_cnt],1 + jne .noungetc + mov byte [ungetc_cnt],0 + mov al,[ungetcdata] + clc + ret +.noungetc: + sub word [bufbytes],1 + jc .get_data + mov si,trackbuf + add si,[bufdata] + mov al,[si] + inc word [bufdata] + clc + ret +.get_data: + mov si,[f_handle] + and si,si + jz .empty + mov ax,7 + mov bx,trackbuf + mov cx,[BufSafe] + int 22h + mov word [bufdata],0 + jc .empty + mov [f_handle],si + mov [bufbytes],cx + jmp getc +.empty: + mov word [f_handle],0 + mov word [bufbytes],0 + stc + ret + +getline: + call skipspace + jz .eof + jc .eoln + call ungetc +.loop: + call getc + jc .ret + cmp al,' ' + jna .ctrl +.store: + stosb + jmp .loop +.ctrl: + cmp al,10 + je .ret + mov al,' ' + jmp .store +.eoln: + clc + jmp .ret +.eof: + stc +.ret: + xor al,al + stosb + ret + + +memcmp: + push si + push di + push ax +.loop: + mov al,[si] + mov ah,[di] + inc si + inc di + cmp al,ah + loope .loop + pop ax + pop di + pop si + ret + + section .data +label_keyword db 6,'label',0 +default_keyword db 7,'default',0 + +msg_progname db 'gfxboot: ',0 +msg_config_file db 'Configuration file',0 +msg_missing db 'missing',0 +msg_usage db 'Usage: gfxboot.com ',0dh,0ah,0 +msg_memory db 'Could not detect available memory size',0dh,0ah,0 +msg_bootlogo_toobig db 'bootlogo file too big',0dh,0ah,0 +msg_pxelinux db 'pxelinux is not supported',0dh,0ah,0 +msg_unknown_file_size db 'unknown file size',0dh,0ah,0 +msg_space db ' ',0 +msg_crlf db 0dh,0ah,0 + +gfx_slash db '/', 0 +db0 db 0 + +; menu entry descriptor +menu_entries equ 0 +menu_default equ 2 ; seg:ofs +menu_ent_list equ 6 ; seg:ofs +menu_ent_size equ 10 +menu_arg_list equ 12 ; seg:ofs +menu_arg_size equ 16 +sizeof_menu_desc equ 18 + +; system config data (52 bytes) +gfx_sysconfig equ $ +gfx_bootloader db 1 ; 0: boot loader type (0: lilo, 1: syslinux, 2: grub) +gfx_sector_shift db 9 ; 1: sector shift +gfx_media_type db 0 ; 2: media type (0: disk, 1: floppy, 2: cdrom) +gfx_failsafe db 0 ; 3: turn on failsafe mode (bitmask) + ; 0: SHIFT pressed + ; 1: skip gfxboot + ; 2: skip monitor detection +gfx_sysconfig_size db gfx_sysconfig_end-gfx_sysconfig ; 4: size of sysconfig data +gfx_boot_drive db 0 ; 5: BIOS boot drive +gfx_callback dw gfx_cb ; 6: offset to callback handler +gfx_bootloader_seg dw 0 ; 8: code/data segment used by bootloader; must follow gfx_callback +gfx_reserved_1 dw 0 ; 10 +gfx_user_info_0 dd 0 ; 12: data for info box +gfx_user_info_1 dd 0 ; 16: data for info box +gfx_bios_mem_size dd 0 ; 20: BIOS memory size (in bytes) +gfx_xmem_0 dw 0 ; 24: extended mem area 0 (start:size in MB; 12:4 bits) +gfx_xmem_1 dw 0 ; 26: extended mem area 1 +gfx_xmem_2 dw 0 ; 28: extended mem area 2 +gfx_xmem_3 dw 0 ; 20: extended mem area 3 +gfx_file dd 0 ; 32: start of gfx file +gfx_archive_start dd 0 ; 36: start of cpio archive +gfx_archive_end dd 0 ; 40: end of cpio archive +gfx_mem0_start dd 0 ; 44: low free memory start +gfx_mem0_end dd 0 ; 48: low free memory end +gfx_sysconfig_end equ $ + + section .bss align=4096 +trackbufsize equ 16384 +trackbuf resb trackbufsize +configbuf resb trackbufsize + +dentry_buf resb 512 +dentry_buf_len equ $ - dentry_buf + +max_cmd_len equ 2047 +command_line resb max_cmd_len+2 + + alignb 4 +derivative_id resb 1 +drivenumber resb 1 +sectorshift resb 1 + resb 1 ; Pad +sectorsize resw 1 +BufSafe resw 1 +file_length resd 1 + +bufbytes resw 1 +bufdata resw 1 +ungetc_cnt resb 1 +ungetcdata resb 1 + +f_handle resw 1 +f_size resd 1 +fname_buf resb 64 +fname_buf_len equ $ - fname_buf + +label_cnt resw 1 + +menu_desc resb sizeof_menu_desc +menu_seg resw 1 +menu_off resw 1 + +gfx_mem_start_seg resw 1 +gfx_mem_end_seg resw 1 + + alignb 4 +gfx_mem resd 1 ; linear address +gfx_save_area1 resd 1 ; 64k +gfx_save_area1_used resb 1 ; != 0 if area1 is in use + + alignb 4 +; interface to loadable gfx extension (seg:ofs values) +gfx_bc_jt resd 1 + +gfx_bc_init resd 1 +gfx_bc_done resd 1 +gfx_bc_input resd 1 +gfx_bc_menu_init resd 1 diff --git a/modules/gfxboot.com b/modules/gfxboot.com new file mode 100644 index 0000000000000000000000000000000000000000..44f61004f694da48c26f63a2983fdde6a44abe87 GIT binary patch literal 1850 zcma)7Z){Ul6hH5^d+)X2YlSfpNO=sXK@y}xCy=c`B>efr)W~Ka8yoAX)oz`yuVWY! z#!ATET96RppBqeo8a~X#3`8g{*;<~Vpw1EaAWDKv6MVB@NG5|t9 zcUcMRmIapO;M%Q>#ImsVnwfF4z~vsf6E-inoN(FOTL{J&>;i6h%in#<*ca;_bd!6a z3-o*3@bX+QX;~SD%JrLBfrKjJf}F+|D}~o(vHvxZAco5OA5O__Q-U0i4E>=g%i}r? zSv2oB(w9m=$!P3251Dpmx;S;Yd6ie6Gmkpda;)Y6K%0Zrye;|SsO3wu^reHjemIF3m6#3fOVdud3lG*u>tD|*MsSwr;4<3C_KxV zfg6F#fvZEa;%=L?12mlpqZ+D+_xx`Ha@R3`%Z-^i66Vd)YA>jsg5Z<70Sm(W)aV{S z&pDsQDA-Aw+)mnv6Lnt)b?U2-sNG7*+y-i$`7(#c$xz}TnsM<1%d#+2A${Z9$NVWV z)6LRC4{00u46!d9v{h&35UGc4RmdD6^+Sf~A25HLXuc>=&z7&7(THuLyJ%2xe!{?1 zS*3(a%P3Rw4-ZyT8Y|&I{R*zS(mvVZ*~u0$Go4{7NM+X00G^PKwfO2*VF0{XYktZ# zh-06L_4p>ySLYhn+{g{%l1sb3FP%f3d#Hh(3MNr<J*?Hh~`7KggiP@Wvn5`!S%Xfr{23>wX?p*_Wemb3)?(f{=loBO= zOl_2iX1nTIb2n@g%^xkh+bz_D&HAVxR}IJ4k#jpa{d?CG-c78a5th>VxU(U7`=O0c zhWVVAt^}^bv zG+h~lnqKoO@O15mDy6+-y3$*`V;+S-u6JuK-Le6w8C)gy$SXzjX|ADOoBPDNmaJ<; z^DiK4g}E7uC(Rnr)~RdEdJt-lx3Pn{R8VE+85)Mls0Cgp*&tfNb~d`(G8ckYs?IP2 z6fBE_aL7zSgE+dwGOFB~NM4mM-Zjj*pt{K5qT0rcmAMJSX&D!VQpTN_#f;7%DG9f| zhGVnXeIvQd%jlBR>2*}jU_DMtm_b(iaak~~`Tl#%3*-S%-8NIY>}AC-3ql!L3WAru z6Nb6Z(&nkpn1?O%V@sV$=^eerKdBO_PbuELU*sm}r8c(K*7P`g7H`Pg zL>rqzQ4pI$*qDfF5XO$CSWH{3z$>v`NrCZf$sYfMv`Lt1F3rZl!UMkrA6^7 K{#*FpzkdLQOu7gF literal 0 HcmV?d00001 diff --git a/modules/poweroff.asm b/modules/poweroff.asm new file mode 100644 index 0000000..f4f19a2 --- /dev/null +++ b/modules/poweroff.asm @@ -0,0 +1,102 @@ +; **************************************************************************** +; +; poweroff.asm +; +; Copyright 2009 Sebastian Herbszt +; +; APM poweroff module. +; +; 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, Inc., 53 Temple Place Ste 330, +; Boston MA 02111-1307, USA; either version 2 of the License, or +; (at your option) any later version; incorporated herein by reference. +; +; **************************************************************************** + + absolute 0 +pspInt20: resw 1 +pspNextP: resw 1 + resb 124 +pspCmdLen: resb 1 +pspCmdArg: resb 127 + + section .text + org 0x100 + +_start: + mov ax,5300h ; APM Installation Check (00h) + xor bx,bx ; APM BIOS (0000h) + int 15h + jnc check_sig + + mov bx, msg_notpresent + jmp error + +check_sig: + cmp bx,504Dh ; signature 'PM' + je check_ver + + mov bx, msg_notpresent + jmp error + +check_ver: + cmp ax,0101h ; Need version 1.1+ + jae check_state + + mov bx, msg_notsup + jmp error + +check_state: + test cx,8 ; bit 3 APM BIOS Power Management disabled + jz connect + + mov bx, msg_pmdisabled + jmp error + +connect: + mov ax,5301h ; APM Real Mode Interface Connect (01h) + xor bx,bx ; APM BIOS (0000h) + int 15h + jnc connect_ok + + mov bx, msg_connect + jmp error + +connect_ok: + mov ax,530Eh ; APM Driver Version (0Eh) + xor bx,bx ; APM BIOS (0000h) + mov cx,0101h ; APM Driver version 1.1 + int 15h + jnc apm0101_check + + mov bx, msg_notsup + jmp error + +apm0101_check: + cmp cx,0101h ; APM Connection version + jae apm0101_ok + + mov bx, msg_notsup + jmp error + +apm0101_ok: + mov ax,5307h ; Set Power State (07h) + mov bx,0001h ; All devices power managed by the APM BIOS + mov cx,0003h ; Power state off + int 15h + jnc off + + mov bx, msg_failed + +error: + mov ax,2 + int 22h +off: + ret + +msg_notpresent: db 'APM not present.',0dh,0ah,0 +msg_notsup: db 'APM 1.1+ not supported.',0dh,0ah,0 +msg_pmdisabled: db 'Power management disabled.',0dh,0ah,0 +msg_connect: db 'APM RM interface connect failed.',0dh,0ah,0 +msg_failed: db 'Power off failed.',0dh,0ah,0 diff --git a/modules/poweroff.com b/modules/poweroff.com new file mode 100644 index 0000000000000000000000000000000000000000..eaa1abdc230fb322229cc933d62a83e8fbfd3388 GIT binary patch literal 239 zcmdnN5Nvq+tY|Uo?mWiVQH{TS14_V5e_KXIpkO28YuE1wIT(O~vl(Ao?O=qd-of}< zX9pjUx08_(tR1LQy74DWlfVx4;N6T2JDC}Pk{r8lGwxtwIIDEnF~CYo`5|dLElJoQOQj<#*(h@VFN+A04)6$T{0J)1(#Q*>R literal 0 HcmV?d00001 diff --git a/modules/pxechain.asm b/modules/pxechain.asm new file mode 100644 index 0000000..35c1969 --- /dev/null +++ b/modules/pxechain.asm @@ -0,0 +1,558 @@ +; "$Id: pxechain.asm,v 1.2 2007/12/16 08:15:39 jhutz Exp $" +; -*- fundamental -*- (asm-mode sucks) vim:noet:com=\:; +; **************************************************************************** +; +; pxechain.asm +; +; A comboot program to chain from PXELINUX to another PXE network +; bootstrap program (NBP). This improves on PXELINUX's built-in PXE +; chaining support by arranging for the server address and boot filename +; reported by the PXE stack to be those from which the new NBP was +; loaded, allowing PXELINUX to be used to select from multiple NBP's, +; such as gPXE, another PXELINUX(*), Windows RIS, and so on. +; +; (*) This seems unnecessary at first, but it is very helpful when +; selecting from among self-contained network boot images. +; +; Copyright (c) 2007 Carnegie Mellon University +; Copyright (C) 1994-2007 H. Peter Anvin +; +; 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, Inc., 53 Temple Place Ste 330, +; Boston MA 02111-1307, USA; either version 2 of the License, or +; (at your option) any later version; incorporated herein by reference. +; +; **************************************************************************** + +;%define DEBUG +;%define NO_RUN + + absolute 0 +pspInt20: resw 1 +pspNextP: resw 1 + resb 124 +pspCmdLen: resb 1 +pspCmdArg: resb 127 + + section .text + org 0x100 + +%ifdef DEBUG +%macro MARK 1.nolist + mov ah,0x02 + mov dl,%1&0xff + int 0x21 +%if (%1 >> 8) & 0xff + mov dl,(%1 >> 8) & 0xff + int 0x21 +%if (%1 >> 16) & 0xff + mov dl,(%1 >> 16) & 0xff + int 0x21 +%if (%1 >> 24) & 0xff + mov dl,(%1 >> 24) & 0xff + int 0x21 +%endif +%endif +%endif + mov dl,' ' + int 0x21 +%endmacro +%macro SHOWD 1.nolist + mov al,%1 + call print_dec + mov ah,0x02 + mov dl,' ' + int 0x21 +%endmacro +%macro SHOWX 1.nolist + mov bx,%1 + call print_hex + mov ah,0x02 + mov dl,' ' + int 0x21 +%endmacro +%else +%macro MARK 1.nolist +%endmacro +%macro SHOWD 1.nolist +%endmacro +%macro SHOWX 1.nolist +%endmacro +%endif + +_start: + MARK 'INIT' + +; There should be exactly one command-line argument, which is of the form +; [[ipaddress]::]tftp_filename, just like filenames given to PXELINUX. +; Too few or too many arguments is an error. +; +; This code is based on mangle_name in pxelinux.asm +parse_args: + cld + xor cx,cx + mov cl,[pspCmdLen] + dec cx + mov si,pspCmdArg+1 + and cx,cx + je near usage ; no args is bad + add si,cx + dec si + std +.chomp: lodsb + cmp al,' ' + loopz .chomp + inc cx + cld + mov [pspCmdLen],cl + mov si,pspCmdArg+1 + cmp word [si],'::' ; Leading ::? + je near gotprefix + dec cx + jz noip + MARK 'SCAN' + +.more: + inc si + cmp byte [si],' ' + je near usage + cmp word [si],'::' + je near parse_ip + loop .more + +noip: + MARK 'NOIP' + mov ax,0x0e ; get config file name + int 0x22 + mov si,bx +%ifdef DEBUG + mov ah,0x02 + mov dl,'[' + int 0x21 + mov ax,0x02 + int 0x22 + mov ah,0x02 + mov dl,']' + int 0x21 + mov dl,' ' + int 0x21 +%endif + push ds + push es + pop ds + pop es + push es +.find_prefix: + lodsb + and al,al + jnz .find_prefix + dec si + + mov cx,si + sub cx,bx + MARK 'LEN' + SHOWD cl ; assume it's <256 for debugging + dec si + std +.find_slash: + lodsb + cmp al,'/' + je .slash + loop .find_slash +.slash: + cmp cx,127 + cld + jna .copy_prefix + pop ds + jmp too_long + +.copy_prefix: + SHOWD cl + MARK 'PFX' + mov si,bx + mov di,tftp_filename + mov bx,128 + sub bx,cx + rep movsb + pop ds + + mov cl,[pspCmdLen] + mov si,pspCmdArg+1 + jmp prefix_done + +usage: + xor cx,cx + mov si,msg_usage + jmp fail + +too_long: + xor cx,cx + mov si,msg_too_long + jmp fail + +parse_ip: + MARK 'PIP' + mov di,si + mov si,pspCmdArg+1 + call parse_dotquad + jc .notdq + cmp si,di ; is it the same place? + jne .notdq + mov [tftp_siaddr],eax + jmp gotprefix +.notdq: + MARK 'NDQ' + mov si,di + mov bx,pspCmdArg+1 + mov ax,0x0010 ; DNS resolve + int 0x22 + and eax,eax + jz noip + mov [tftp_siaddr],eax +gotprefix: + MARK 'GOTP' + dec cx ; skip the :: + dec cx + inc si + inc si + mov di,tftp_filename + mov bx,128 + +prefix_done: + SHOWD bl + MARK 'LEFT' + +; SI points at the filename, plus remaining arguments, +; CX contains their combined length. +; DI points to where the filename should be stored +; BX says how much space is left for the filename and NUL + + and cx,cx + jz usage ; no args is bad +.copy_filename: + lodsb +%ifdef DEBUG + mov dl,al + mov ah,0x2 + int 0x21 +%endif + cmp al,' ' + je usage + dec bx + jz too_long + stosb + loop .copy_filename + xor eax,eax + stosb + +; get PXE cached data + MARK 'GCI' + mov ax,0x0009 ; call PXE stack + mov bx,0x0071 ; PXENV_GET_CACHED_INFO + mov di,PXECacheParms + int 0x22 + and eax,eax + jz .fix_siaddr + mov cx,[gci_status] + mov si,msg_get_cache + jmp fail + +.fix_siaddr: + mov bx,[gci_bufferseg] + mov es,bx + mov bx,[gci_buffer] + mov eax,[es:bx+12] ; save our address (ciaddr) + mov [open_ciaddr],eax ; ... in case we have to do UDP open + mov eax,[tftp_siaddr] + and eax,eax + jnz .replace_addr + MARK 'ADDR' + mov eax,[es:bx+20] ; siaddr + mov [tftp_siaddr],eax + jmp .addr_done +.replace_addr: + mov [es:bx+20],eax +.addr_done: + mov si,tftp_filename ; copy the new filename... + lea di,[es:bx+108] ; to the "cached DHCP response" + mov cx,128 + rep movsb + mov bx,ds ; restore es before proceeding + mov es,bx + +; print out what we are doing +%ifdef DEBUG + mov ah,0x02 ; write character + mov dl,0x0d ; print a CRLF first + int 0x21 + mov dl,0x0a + int 0x21 +%endif + mov ax,0x0002 ; write string + mov bx,msg_booting + int 0x22 + mov ebx,[tftp_siaddr] + call print_dotquad + mov ah,0x02 ; write character + mov dl,' ' + int 0x21 + mov ax,0x0002 ; write string + mov bx,tftp_filename + int 0x22 + mov ah,0x02 ; write character + mov dl,0x0d + int 0x21 + mov dl,0x0a + int 0x21 + +%ifndef NO_RUN + mov ax,0x0009 ; call PXE stack + mov bx,0x0031 ; PXENV_UDP_CLOSE + mov di,PXECloseParms + int 0x22 + mov cx,[close_status] + mov si,msg_udp_close + and ax,ax + jnz fail + + mov ax,0x0009 ; call PXE stack + mov bx,0x0073 ; PXENV_RESTART_TFTP + mov di,PXERestartTFTPParms + int 0x22 + mov cx,[tftp_status] + mov si,msg_rst_tftp + call fail + + mov ax,0x0009 ; call PXE stack + mov bx,0x0030 ; PXENV_UDP_OPEN + mov di,PXEOpenParms + int 0x22 + mov cx,[open_status] + mov si,msg_udp_open + and ax,ax + jnz fail + ret +%endif + +fail: + MARK 'FAIL' + SHOWX cs + SHOWX ds + SHOWX es + SHOWX si +%ifdef DEBUG + mov ah,0x02 ; write character + mov dl,0x0d ; print a CRLF first + int 0x21 + mov dl,0x0a + int 0x21 +%endif + mov ax,0x0002 ; write string + mov bx,msg_progname ; print our name + int 0x22 + mov bx,si ; ... the error message + int 0x22 + mov ah,0x02 ; write character + jcxz .done + mov dl,' ' ; ... and the error code, in [] + int 0x21 + mov dl,'[' + int 0x21 + mov bx,cx + call print_hex + mov ah,0x02 ; write character + mov dl,']' + int 0x21 +.done: + mov dl,0x0d ; and finally a CRLF + int 0x21 + mov dl,0x0a + int 0x21 + ret + + +; print_hex +; +; Take a 16-bit integer in BX and print it as 2 hex digits. +; Destroys AX and DL. +; +print_hex: + mov al,bh + aam 16 + cmp ah,10 + jb .lt_a000 + add ah,'A'-'0'-10 +.lt_a000: add ah,'0' + mov dl,ah + mov ah,0x02 ; write character + int 0x21 + + cmp al,10 + jb .lt_a00 + add al,'A'-'0'-10 +.lt_a00: add al,'0' + mov dl,al + mov ah,0x02 ; write character + int 0x21 + + mov al,bl + aam 16 + cmp ah,10 + jb .lt_a0 + add ah,'A'-'0'-10 +.lt_a0: add ah,'0' + mov dl,ah + mov ah,0x02 ; write character + int 0x21 + + cmp al,10 + jb .lt_a + add al,'A'-'0'-10 +.lt_a: add al,'0' + mov dl,al + mov ah,0x02 ; write character + int 0x21 + ret + + +; print_dec +; +; Take an 8-bit integer in AL and print it in decimal. +; Destroys AX and DL. +; +print_dec: + cmp al,10 ; < 10? + jb .lt10 ; If so, skip first 2 digits + + cmp al,100 ; < 100 + jb .lt100 ; If so, skip first digit + + aam 100 + ; Now AH = 100-digit; AL = remainder + add ah,'0' + mov dl,ah + mov ah,0x02 + int 0x21 + +.lt100: + aam 10 + ; Now AH = 10-digit; AL = remainder + add ah,'0' + mov dl,ah + mov ah,0x02 + int 0x21 + +.lt10: + add al,'0' + mov dl,al + mov ah,0x02 + int 0x21 + ret + + +; print_dotquad +; +; Take an IP address (in network byte order) in EBX and print it +; as a dotted quad. +; Destroys EAX, EBX, ECX, EDX +; +print_dotquad: + mov cx,3 +.octet: + mov al,bl + call print_dec + jcxz .done + mov ah,0x02 + mov dl,'.' + int 0x21 + ror ebx,8 ; Move next char into LSB + dec cx + jmp .octet +.done: + ret + + +; parse_dotquad: +; Read a dot-quad pathname in DS:SI and output an IP +; address in EAX, with SI pointing to the first +; nonmatching character. +; +; Return CF=1 on error. +; +; No segment assumptions permitted. +; +parse_dotquad: + push cx + mov cx,4 + xor eax,eax +.parseloop: + mov ch,ah + mov ah,al + lodsb + sub al,'0' + jb .notnumeric + cmp al,9 + ja .notnumeric + aad ; AL += 10 * AH; AH = 0; + xchg ah,ch + jmp .parseloop +.notnumeric: + cmp al,'.'-'0' + pushf + mov al,ah + mov ah,ch + xor ch,ch + ror eax,8 + popf + jne .error + loop .parseloop + jmp .done +.error: + loop .realerror ; If CX := 1 then we're done + clc + jmp .done +.realerror: + stc +.done: + dec si ; CF unchanged! + pop cx + ret + + section .data +msg_booting: db 'TFTP boot: ',0 +msg_progname: db 'pxechain: ',0 +msg_usage: db 'usage: pxechain.cbt [[ipaddress]::]filename',0dh,0ah,0 +msg_too_long: db 'pxechain: filename is too long (max 127)',0dh,0ah,0 +msg_get_cache: db 'PXENV_GET_CACHED_INFO',0 +msg_rst_tftp: db 'PXENV_RESTART_TFTP',0 +msg_udp_close: db 'PXENV_UDP_CLOSE',0 +msg_udp_open: db 'PXENV_UDP_OPEN',0 + +PXECacheParms: +gci_status: dw 0 +gci_packettype: dw 3 ; PXENV_PACKET_TYPE_CACHED_REPLY +gci_buffersize: dw 0 +gci_buffer: dw 0 +gci_bufferseg: dw 0 +gci_bufferlim: dw 0 + +PXERestartTFTPParms: +tftp_status: dw 0 +tftp_filename: times 128 db 0 +tftp_bufsize: dd 0x00090000 ; available memory for NBP +tftp_bufaddr: dd 0x00007c00 ; PXE NBP load address +tftp_siaddr: dd 0 +tftp_giaddr: dd 0 +tftp_mcaddr: dd 0 +tftp_mcport: dw 0 +tftp_msport: dw 0 +tftp_timeout: dw 0 +tftp_reopendly: dw 0 + +PXECloseParms: +close_status: dw 0 + +PXEOpenParms: +open_status: dw 0 +open_ciaddr: dd 0 diff --git a/modules/pxechain.com b/modules/pxechain.com new file mode 100644 index 0000000000000000000000000000000000000000..8c4e0c5bd2a127f931e8bea1f3ffb07adc2f6001 GIT binary patch literal 998 zcmd6jQAiVU9LN9L{hNB~CTRpc%)20IAhv}N9rxhWO-C`8t|&ytbUPcUv+Y(s2wo;} z=gJ;>t%p#05a~fACWD)HJ~U-PU|wj~KPxactpZotl_xpW6KQ3!Y zj_Nt!5QhPoG>r5D%?IbNDVq4QZ_V=P1L6;9iZYCh0tb8A&e4Qnq&bgVK>ixjgVcE; zZPZm3=%$Fd$i7qfX8No~Tw=BI-lM&t?0u|KF$(EKJ&bd}w6rr(sTxtRVzng2=h$Y6 z2EFI8O{fumBAj`n3E@`{B06S#5h~@vL3|j*%8-U3=h$Z|bU5tx!#Fa@#?iLy+iPrm zQvShM<|boBAbt-dYT@Ak9@W=EaSPjg4WdRGj!97?1*@27QwcLJL*~^YM6r=#l~m+} zr>YpCsY0xWRZoX{W;w7k@nm&OifKW75JQns<08y_2Q%7fB1NjS9*Iy@nj)uhM3CJA z5@`gZ`ZN^JK$Kps(lAKWuQsL%`&YdN?E#S3byCTHT2N!U?y*UMGUbbE>v zmNEua+kwiwOyYN!%eX8a!nlP3Zo$mUGujkM;~_09fM))kTJbCYY?08!IocO0UiKY_ zEaS)5qa%7(cT{D_xgMxe>mJhQ5{U6$KU7e^_Su&29F>S2or2DNc05zx`~Uu-T}gL`HBg( zx^CN?w>(WYx2M6{&}?h;IGpyDJZ-hLxvi~kkGfMH-E4Gu8m_mr*-q3gE}IhofT8pM LJVgKu{(YN2p4dqJ literal 0 HcmV?d00001 diff --git a/mtools/Makefile b/mtools/Makefile new file mode 100644 index 0000000..b551780 --- /dev/null +++ b/mtools/Makefile @@ -0,0 +1,43 @@ +topdir = .. +include $(topdir)/MCONFIG + +OPTFLAGS = -g -Os +INCLUDES = -I. -I.. -I../libfat -I../libinstaller +CFLAGS = -W -Wall -D_FILE_OFFSET_BITS=64 $(OPTFLAGS) $(INCLUDES) +LDFLAGS = -s + +SRCS = syslinux.c \ + ../libinstaller/syslxmod.c \ + ../libinstaller/bootsect_bin.c \ + ../libinstaller/ldlinux_bin.c \ + $(wildcard ../libfat/*.c) +OBJS = $(patsubst %.c,%.o,$(notdir $(SRCS))) + +.SUFFIXES: .c .o .i .s .S + +VPATH = .:../libfat:../libinstaller + +all: installer + +tidy dist: + -rm -f *.o *.i *.s *.a .*.d *.tmp + +clean: tidy + -rm -f syslinux + +spotless: clean + -rm -f *~ + +installer: syslinux + +syslinux: $(OBJS) + $(CC) $(LDFLAGS) -o $@ $^ + +%.o: %.c + $(CC) $(UMAKEDEPS) $(CFLAGS) -c -o $@ $< +%.i: %.c + $(CC) $(UMAKEDEPS) $(CFLAGS) -E -o $@ $< +%.s: %.c + $(CC) $(UMAKEDEPS) $(CFLAGS) -S -o $@ $< + +-include .*.d *.tmp diff --git a/mtools/syslinux b/mtools/syslinux new file mode 100755 index 0000000000000000000000000000000000000000..dd0319cfe2dea835a56a518241981baa3feac91e GIT binary patch literal 25604 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "syslinux.h" +#include "libfat.h" + +char *program; /* Name of program */ +char *device; /* Device to install to */ +pid_t mypid; +off_t filesystem_offset = 0; /* Offset of filesystem */ + +void __attribute__((noreturn)) usage(void) +{ + fprintf(stderr, "Usage: %s [-sfr][-d directory][-o offset] device\n", program); + exit(1); +} + +void __attribute__((noreturn)) die(const char *msg) +{ + fprintf(stderr, "%s: %s\n", program, msg); + exit(1); +} + +/* + * read/write wrapper functions + */ +ssize_t xpread(int fd, void *buf, size_t count, off_t offset) +{ + char *bufp = (char *)buf; + ssize_t rv; + ssize_t done = 0; + + while ( count ) { + rv = pread(fd, bufp, count, offset); + if ( rv == 0 ) { + die("short read"); + } else if ( rv == -1 ) { + if ( errno == EINTR ) { + continue; + } else { + die(strerror(errno)); + } + } else { + bufp += rv; + offset += rv; + done += rv; + count -= rv; + } + } + + return done; +} + +ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset) +{ + const char *bufp = (const char *)buf; + ssize_t rv; + ssize_t done = 0; + + while ( count ) { + rv = pwrite(fd, bufp, count, offset); + if ( rv == 0 ) { + die("short write"); + } else if ( rv == -1 ) { + if ( errno == EINTR ) { + continue; + } else { + die(strerror(errno)); + } + } else { + bufp += rv; + offset += rv; + done += rv; + count -= rv; + } + } + + return done; +} + +/* + * Version of the read function suitable for libfat + */ +int libfat_xpread(intptr_t pp, void *buf, size_t secsize, libfat_sector_t sector) +{ + off_t offset = (off_t)sector * secsize + filesystem_offset; + return xpread(pp, buf, secsize, offset); +} + + +int main(int argc, char *argv[]) +{ + static unsigned char sectbuf[512]; + int dev_fd; + struct stat st; + int status; + char **argp, *opt; + char mtools_conf[] = "/tmp/syslinux-mtools-XXXXXX"; + const char *subdir = NULL; + int mtc_fd; + FILE *mtc, *mtp; + struct libfat_filesystem *fs; + libfat_sector_t s, *secp, sectors[65]; /* 65 is maximum possible */ + int32_t ldlinux_cluster; + int nsectors; + const char *errmsg; + + int force = 0; /* -f (force) option */ + int stupid = 0; /* -s (stupid) option */ + int raid_mode = 0; /* -r (RAID) option */ + + (void)argc; /* Unused */ + + mypid = getpid(); + program = argv[0]; + + device = NULL; + + for ( argp = argv+1 ; *argp ; argp++ ) { + if ( **argp == '-' ) { + opt = *argp + 1; + if ( !*opt ) + usage(); + + while ( *opt ) { + if ( *opt == 's' ) { + stupid = 1; + } else if ( *opt == 'r' ) { + raid_mode = 1; + } else if ( *opt == 'f' ) { + force = 1; /* Force install */ + } else if ( *opt == 'd' && argp[1] ) { + subdir = *++argp; + } else if ( *opt == 'o' && argp[1] ) { + filesystem_offset = (off_t)strtoull(*++argp, NULL, 0); /* Byte offset */ + } else { + usage(); + } + opt++; + } + } else { + if ( device ) + usage(); + device = *argp; + } + } + + if ( !device ) + usage(); + + /* + * First make sure we can open the device at all, and that we have + * read/write permission. + */ + dev_fd = open(device, O_RDWR); + if ( dev_fd < 0 || fstat(dev_fd, &st) < 0 ) { + perror(device); + exit(1); + } + + if ( !force && !S_ISBLK(st.st_mode) && !S_ISREG(st.st_mode) ) { + fprintf(stderr, "%s: not a block device or regular file (use -f to override)\n", device); + exit(1); + } + + xpread(dev_fd, sectbuf, 512, filesystem_offset); + + /* + * Check to see that what we got was indeed an MS-DOS boot sector/superblock + */ + if( (errmsg = syslinux_check_bootsect(sectbuf)) ) { + die(errmsg); + } + + /* + * Create an mtools configuration file + */ + mtc_fd = mkstemp(mtools_conf); + if ( mtc_fd < 0 || !(mtc = fdopen(mtc_fd, "w")) ) { + perror(program); + exit(1); + } + fprintf(mtc, + /* "MTOOLS_NO_VFAT=1\n" */ + "MTOOLS_SKIP_CHECK=1\n" /* Needed for some flash memories */ + "drive s:\n" + " file=\"/proc/%lu/fd/%d\"\n" + " offset=%llu\n", + (unsigned long)mypid, + dev_fd, + (unsigned long long)filesystem_offset); + fclose(mtc); + + /* + * Run mtools to create the LDLINUX.SYS file + */ + if ( setenv("MTOOLSRC", mtools_conf, 1) ) { + perror(program); + exit(1); + } + + /* This command may fail legitimately */ + system("mattrib -h -r -s s:/ldlinux.sys 2>/dev/null"); + + mtp = popen("mcopy -D o -D O -o - s:/ldlinux.sys", "w"); + if ( !mtp || + (fwrite(syslinux_ldlinux, 1, syslinux_ldlinux_len, mtp) + != syslinux_ldlinux_len) || + (status = pclose(mtp), !WIFEXITED(status) || WEXITSTATUS(status)) ) { + die("failed to create ldlinux.sys"); + } + + /* + * Now, use libfat to create a block map + */ + fs = libfat_open(libfat_xpread, dev_fd); + ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL); + secp = sectors; + nsectors = 0; + s = libfat_clustertosector(fs, ldlinux_cluster); + while ( s && nsectors < 65 ) { + *secp++ = s; + nsectors++; + s = libfat_nextsector(fs, s); + } + libfat_close(fs); + + /* Patch ldlinux.sys and the boot sector */ + syslinux_patch(sectors, nsectors, stupid, raid_mode); + + /* Write the now-patched first sector of ldlinux.sys */ + xpwrite(dev_fd, syslinux_ldlinux, 512, + filesystem_offset + ((off_t)sectors[0] << 9)); + + /* Move ldlinux.sys to the desired location */ + if (subdir) { + char target_file[4096], command[5120]; + char *cp = target_file, *ep = target_file+sizeof target_file-16; + const char *sd; + int slash = 1; + + cp += sprintf(cp, "'s:/"); + for (sd = subdir; *sd; sd++) { + if (*sd == '/' || *sd == '\\') { + if (slash) + continue; /* Remove duplicated slashes */ + slash = 1; + } else if (*sd == '\'' || *sd == '!') { + slash = 0; + if (cp < ep) *cp++ = '\''; + if (cp < ep) *cp++ = '\\'; + if (cp < ep) *cp++ = *sd; + if (cp < ep) *cp++ = '\''; + continue; + } else { + slash = 0; + } + + if (cp < ep) + *cp++ = *sd; + } + if (!slash) + *cp++ = '/'; + strcpy(cp, "ldlinux.sys'"); + + /* This command may fail legitimately */ + sprintf(command, "mattrib -h -r -s %s 2>/dev/null", target_file); + system(command); + + sprintf(command, "mmove -D o -D O s:/ldlinux.sys %s", target_file); + status = system(command); + + if ( !WIFEXITED(status) || WEXITSTATUS(status) ) { + fprintf(stderr, + "%s: warning: unable to move ldlinux.sys\n", + program); + + status = system("mattrib +r +h +s s:/ldlinux.sys"); + } else { + sprintf(command, "mattrib +r +h +s %s", target_file); + status = system(command); + } + } else { + status = system("mattrib +r +h +s s:/ldlinux.sys"); + } + + if ( !WIFEXITED(status) || WEXITSTATUS(status) ) { + fprintf(stderr, + "%s: warning: failed to set system bit on ldlinux.sys\n", + program); + } + + + /* + * Cleanup + */ + unlink(mtools_conf); + + /* + * To finish up, write the boot sector + */ + + /* Read the superblock again since it might have changed while mounted */ + xpread(dev_fd, sectbuf, 512, filesystem_offset); + + /* Copy the syslinux code into the boot sector */ + syslinux_make_bootsect(sectbuf); + + /* Write new boot sector */ + xpwrite(dev_fd, sectbuf, 512, filesystem_offset); + + close(dev_fd); + sync(); + + /* Done! */ + + return 0; +} diff --git a/now.pl b/now.pl new file mode 100644 index 0000000..a3b5a79 --- /dev/null +++ b/now.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +# +# Print the time (possibly the mtime of a file) as a hexadecimal integer +# If more than one file, print the mtime of the *newest* file. +# + +undef $now; + +foreach $file ( @ARGV ) { + ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime, + $ctime,$blksize,$blocks) = stat($file); + if ( !defined($now) || $now < $mtime ) { + $now = $mtime; + } +} + +if ( !defined($now) ) { + $now = time; +} + +printf "0x%08x\n", $now; diff --git a/sample/Makefile b/sample/Makefile new file mode 100644 index 0000000..9fa21c2 --- /dev/null +++ b/sample/Makefile @@ -0,0 +1,70 @@ +## ----------------------------------------------------------------------- +## +## Copyright 2001-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +## +## samples for syslinux users +## + +topdir = .. +include $(topdir)/MCONFIG.embedded + +INCLUDES = -I$(com32)/include + +PPMTOLSS16 = $(topdir)/utils/ppmtolss16 +LIBGCC := $(shell $(CC) $(GCCOPT) --print-libgcc) +LIB = liboldcom32.a + +LIBOBJS = conio.o atou.o skipatou.o printf.o c32exit.o + +com32 = $(topdir)/com32 +C_LIBS = $(com32)/libutil/libutil_com.a $(com32)/lib/libcom32.a $(LIBGCC) +LDFLAGS = -m elf_i386 -T $(com32)/lib/com32.ld + +all: syslogo.lss comecho.com hello.c32 hello2.c32 filetest.c32 c32echo.c32 \ + fd.c32 $(LIB) + +.PRECIOUS: %.o + +.PRECIOUS: %.elf +%.elf: c32entry.o %.o $(LIB) + $(LD) -Ttext 0x101000 -e _start -o $@ $^ + +%.c32: %.elf + $(OBJCOPY) -O binary $< $@ + +%.com: %.asm + ( $(NASM) -M -DDEPEND -o $@ $< ; echo '' ) > .$@.d ; true + $(NASM) $(NASMOPT) -f bin -o $@ -l $*.lst $< + +$(LIB): $(LIBOBJS) + rm -f $@ + $(AR) cq $@ $^ + $(RANLIB) $@ + +%.lss: %.ppm.gz $(PPMTOLSS16) + $(GZIPPROG) -cd $< | \ + $(PPMTOLSS16) \#000000=0 \#d0d0d0=7 \#f6f6f6=15 \ + > $@ + +%.ppm.gz: %.png + $(PNGTOPNM) $< | gzip -9 > $@ + +tidy dist: + rm -f *.o *.a *.lst *.elf .*.d + +# Don't specify *.com since mdiskchk.com can't be built using Linux tools +clean: tidy + rm -f *.lss *.o *.c32 comecho.com + +spotless: clean + +-include .*.d diff --git a/sample/README b/sample/README new file mode 100644 index 0000000..0a053f5 --- /dev/null +++ b/sample/README @@ -0,0 +1,6 @@ +This directory contains files intended to be used as sample code. +This includes COMBOOT, COM32, and MS-DOS programs as well as LSS +icons. + +For developing COM32 programs, you probably want to use the new com32 +toolkit (libcom32 and libutil), available in the com32 directory. diff --git a/sample/atou.c b/sample/atou.c new file mode 100644 index 0000000..84475b6 --- /dev/null +++ b/sample/atou.c @@ -0,0 +1,13 @@ +static inline int +isdigit(int ch) +{ + return (ch >= '0') && (ch <= '9'); +} + +unsigned int atou(const char *s) +{ + unsigned int i = 0; + while (isdigit(*s)) + i = i*10 + (*s++ - '0'); + return i; +} diff --git a/sample/c32echo.c b/sample/c32echo.c new file mode 100644 index 0000000..08c25a2 --- /dev/null +++ b/sample/c32echo.c @@ -0,0 +1,48 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2002-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * c32echo.c + * + * Simple COM32 program which only prints out its own command line + */ + +#include + +#define NULL ((void *)0) + +static inline void memset(void *buf, int ch, unsigned int len) +{ + asm volatile("cld; rep; stosb" + : "+D" (buf), "+c" (len) : "a" (ch) : "memory"); +} + +int __start(void) +{ + com32sys_t inreg; + const char *p; + + memset(&inreg, 0, sizeof inreg); + inreg.eax.b[1] = 0x02; /* Write Character */ + + for ( p = __com32.cs_cmdline ; *p ; p++ ) { + inreg.edx.b[0] = *p; + __com32.cs_intcall(0x21, &inreg, NULL); + } + + inreg.edx.b[0] = '\r'; + __com32.cs_intcall(0x21, &inreg, NULL); + inreg.edx.b[0] = '\n'; + __com32.cs_intcall(0x21, &inreg, NULL); + + return 0; +} diff --git a/sample/c32echo.c32 b/sample/c32echo.c32 new file mode 100755 index 0000000000000000000000000000000000000000..99f13a08dd3d59b284963d733f6755b2893261f5 GIT binary patch literal 162 zcmdn--{-92AHxIt7byrZ>^ux4G=Cm^$@F=3Z;1-a{uLlWHU", 0Dh, 0Ah, "$" diff --git a/sample/comecho.com b/sample/comecho.com new file mode 100644 index 0000000000000000000000000000000000000000..f894a2befa3b59e3553da131e9c8a051e6307461 GIT binary patch literal 36 qcmXp!*~Qnuu&Uvx;jv4uPmg-*+i6Zs9zuc-W4YO9cQUPY)RY literal 0 HcmV?d00001 diff --git a/sample/conio.c b/sample/conio.c new file mode 100644 index 0000000..6698dee --- /dev/null +++ b/sample/conio.c @@ -0,0 +1,60 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2001-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * conio.c + * + * Output to the screen + */ + +#include +#include + +#define NULL ((void *)0) + +static inline void memset(void *buf, int ch, unsigned int len) +{ + asm volatile("cld; rep; stosb" + : "+D" (buf), "+c" (len) : "a" (ch) : "memory"); +} + +int putchar(int ch) +{ + com32sys_t regs; + + memset(®s, 0, sizeof regs); + + if ( ch == '\n' ) { + /* \n -> \r\n */ + putchar('\r'); + } + + regs.eax.b[1] = 0x02; + regs.edx.b[0] = ch; + __com32.cs_intcall(0x21, ®s, NULL); + + return ch; +} + +/* Note: doesn't put '\n' like the stdc version does */ +int puts(const char *s) +{ + int count = 0; + + while ( *s ) { + putchar(*s); + count++; + s++; + } + + return count; +} diff --git a/sample/fd.c b/sample/fd.c new file mode 100644 index 0000000..468480d --- /dev/null +++ b/sample/fd.c @@ -0,0 +1,59 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2003-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * fd.c + * + * Chainload a floppy disk (currently rather braindead.) + */ + +#include +#define NULL ((void *)0) + +int printf(const char *, ...); +unsigned int atou(const char *); + +int __start(void) +{ + int whichfd = atou(__com32.cs_cmdline); + static com32sys_t inreg, outreg; /* In bss, so zeroed automatically */ + int retry; + + for ( retry = 0 ; retry < 6 ; retry++ ) { + printf(">"); + inreg.eax.w[0] = 0x0201; /* Read one sector */ + inreg.ecx.w[0] = 0x0001; /* Cyl 0 sector 1 */ + inreg.edx.b[1] = 0; /* Head 0 */ + inreg.edx.b[0] = whichfd; /* Drive number */ + inreg.es = SEG(__com32.cs_bounce); /* Read into the bounce buffer */ + inreg.ebx.w[0] = OFFS(__com32.cs_bounce); + __com32.cs_intcall(0x13, &inreg, &outreg); + + if ( (outreg.eflags.l & 1) == 0 ) + break; + } + + if ( (outreg.eflags.l & 1) == 0 ) { + printf("!\n"); + inreg.eax.w[0] = 0x000d; + inreg.edx.w[0] = 0; + inreg.edi.l = (uint32_t) __com32.cs_bounce; + inreg.ecx.l = 512; + inreg.ebx.l = whichfd & 0xff; + inreg.esi.l = 0; /* No partitions */ + inreg.ds = 0; /* No partitions */ + __com32.cs_intcall(0x22, &inreg, NULL); + } + + /* If we get here, badness happened */ + return 255; +} diff --git a/sample/fd.c32 b/sample/fd.c32 new file mode 100755 index 0000000000000000000000000000000000000000..1f243446c4fdac8b214e2fe3613e98f11f4c9203 GIT binary patch literal 1844 zcmYjSZ%k8H6o0S(%O1YBSdgJ}1s@>_ns#HG%xZvEL`BBrpOdMXIz(jZ=6fg$3w5_m zdRt#Y7PD;2KIq)CKkkD|mQ1$y`B5Ybp7z|IrpA( ze!p|ixu;jv`mv%#XLzWhRA8=MhR@ph;gs&dWknJVLmTO)iD4LgWO3879$jeJgD+Do z=mV`yXvT2S1?R~>=y}{Zg=VpT zu$50>b16QBVX{D%d?06uFgnViM1>;_Zp0YS>@>}}s485n57 z!_Em}K6X7vvFWS%K~~u_juxyv_waZ-91pa#wKkodIY+N0f`7yHq9yc$cmdSVcjDKu zjCjOyeucoKV^yHZ7OLNc4AL=5Skj~0|AaiE1toQ8uDHkSoJwtmJI6_Mi$!znGhuXV z7KI%Fk0_*LkPRbk@TFI{JM=V=UO42a{-L+_X8Ock>EKhVhF;X)voE>{aPAWis zqD7F6=$?*n&M?(1{Q21C(t=0XK^xI&@o*tb_+g0UVdY-N)mQ4Y<`+4)>*%W^VKLpbn0A_(%I6 z;aPfSch>=K8c&{$(t@<_Kd>)C>w{)iVS|X9#R5ErL@6XVu4b} zLh%ZMjjT6QCOy~Bc{6tDS#M^ITuN4YGwUU@H{+035*tbgu$%00KOTBF22X9nqj^G?*T*rCeD(Jd?hwG4ZxIfQT!u#x?yY}$@ zN?23HR5{H1O6hu(_ltD1kSeF7Rm={D(xXqQBJ;j{P=Jbowz}&K@1KD+ zMJ>=B0lLK+H`!}<`ltHSkU+ph^pADYlugM(j8$sA$ayzHBWbzGf8yaXcxBlLoaUjp zS?Gy}@J^qCKC%Q2D*~q4n#vn}_wgk31%PNh1J{$91_o$Bl`T-RpXp0cwi@6fY`fc3 zNwhUxqfA3BFf@{D7F89;L*1g7`ij~?+30>5G?EUFr9OhaxT%Dc)BI>4Qh^dx|0PRG zQ9?%w!Jw8lS5WXxNX9^ZEjd_6ubs40!yNEJ^W8F+K(3J)*sO zo*$YZIZGt!MWHdvH&id$fa~dnC9ZZ96;#>~T3vl%-oN~BRgIgNvI-H;qUb8^P0)#D zfU%*R6K!;X=?_m?01;x{q#qK;0;l1>|&seg~qO;?5xF;DC%iH5b4PN z$IyQkV&wd1nH|Mo0|roJZ5Xpqb!-Z)<_Gmk-8h=J_9S5>?~iO@ zigFl-^Z6GlUVQ20jg@a6JaqW2jw78%yN-3gef-49ciufE$vvmf^uG5$qv{&BXKU@Y ay6ro5*6(U~wehvxP0cN>ZF^qd%lr$ed7*g# literal 0 HcmV?d00001 diff --git a/sample/filetest.c b/sample/filetest.c new file mode 100644 index 0000000..b4ca2d8 --- /dev/null +++ b/sample/filetest.c @@ -0,0 +1,98 @@ +#include +#include + +#define NULL ((void *)0) +int printf(const char *, ...); +int putchar(int); + +static inline void memset(void *buf, int ch, unsigned int len) +{ + asm volatile("cld; rep; stosb" + : "+D" (buf), "+c" (len) : "a" (ch) : "memory"); +} + +static void strcpy(char *dst, const char *src) +{ + while ( *src ) + *dst++ = *src++; + + *dst = '\0'; +} + +static void printregs(const com32sys_t *r) +{ + printf("eflags = %08x ds = %04x es = %04x fs = %04x gs = %04x\n" + "eax = %08x ebx = %08x ecx = %08x edx = %08x\n" + "ebp = %08x esi = %08x edi = %08x esp = %08x\n", + r->eflags.l, r->ds, r->es, r->fs, r->gs, + r->eax.l, r->ebx.l, r->ecx.l, r->edx.l, + r->ebp.l, r->esi.l, r->edi.l, r->_unused_esp.l); +} + +int __start(void) +{ + unsigned int ax,cx,si,t; + com32sys_t inreg,outreg; + char *p; + + /* Test null system call */ + inreg.eflags.l = 0xffffffff; + inreg.eax.l = 0x11110000; + inreg.ecx.l = 0x22222222; + inreg.edx.l = 0x33333333; + inreg.ebx.l = 0x44444444; + inreg.ebp.l = 0x55555555; + inreg.esi.l = 0x66666666; + inreg.edi.l = 0x77777777; + inreg.ds = 0xaaaa; + inreg.es = 0xbbbb; + inreg.fs = 0xcccc; + inreg.gs = 0xdddd; + printregs(&inreg); + __com32.cs_intcall(0x22, &inreg, &outreg); + printregs(&outreg); + printf("----\n"); + + memset(&inreg, 0, sizeof inreg); + memset(&outreg, 0, sizeof outreg); + strcpy(__com32.cs_bounce, "test.txt"); + inreg.eax.w[0] = 0x0006; // Open file + inreg.esi.w[0] = OFFS(__com32.cs_bounce); + inreg.es = SEG(__com32.cs_bounce); + printregs(&inreg); + __com32.cs_intcall(0x22, &inreg, &outreg); + printregs(&outreg); + printf("----\n"); + + si = outreg.esi.w[0]; /* File handle */ + cx = outreg.ecx.w[0]; /* Block size */ + ax = outreg.eax.l; /* File length */ + + while ( si ) { + /* We can only read 64K per call */ + t = ( ax > 65536 ) ? 65536/cx : (ax+cx-1)/cx; + + memset(&inreg, 0, sizeof inreg); + inreg.esi.w[0] = si; + inreg.ecx.w[0] = t; /* Block count */ + inreg.eax.w[0] = 0x0007; // Read file + inreg.ebx.w[0] = OFFS(__com32.cs_bounce); + inreg.es = SEG(__com32.cs_bounce); + printregs(&inreg); + __com32.cs_intcall(0x22, &inreg, &outreg); + printregs(&outreg); + printf("----\n"); + si = outreg.esi.w[0]; + + /* Print the buffer */ + t = (ax < 65536) ? ax : 65536; + p = __com32.cs_bounce; + while ( t ) { + putchar(*p++); + t--; + ax--; + } + } + + return 0; +} diff --git a/sample/filetest.c32 b/sample/filetest.c32 new file mode 100755 index 0000000000000000000000000000000000000000..45a59f2d41ce5fab663107386d5a7b1eacf043a7 GIT binary patch literal 2687 zcmb_eZA@F&8NN2gHlEXaqdBQdlg^=+f2Y{|#Ym6%jb(==(V)M*>rk4%b0Q#4{~a6}pg;>Q*VMdVLAdlz+NY3AUh@t$*S zLbSi@k?wup_uTiqAJ2Ktu|Ljv=XN}F#jlR+5x6VA!E5*0R9b)ckBTH3u7=p8k>fae z_TfuU4d`P}-TiG^2Ya-srMcn4N(kz;kQju>VO*;fhdFNZX2_v33`pq(49LD(49Gwe z24rV92IO0V7)&YJjB-+`7ea zfg3mQnwY?AY>eZ2aslNTflE2f97m?zO1rqLK^YePEj?wqEf>++Nk5`jhDJS0Xj&q}@onJ;;}RVv?a!RML)1O3f-K31El(x<8<7Gui1Oh7^@Y{x|C}% zBZ)Se#IE_SFi2f-KpOLkK*lS!cT-91)6g^hFM5G>^o}3@=FwX&&J#*Q4=nYqqvvn^ zM$jA4;ui?@9*kI{Sk1Vw2Y}Hp^B4^jPco=t+cnl?R0}^@(xJ>1su=E=n9=x9p=S*fB4fNS0Wmprh@rc0Yr%Q(-;T5+#F z(k_a=9u8Cyd_6nz2Z!Dy)3t4HCb~zP5yL=}t|hgEpNyyefK{HT;ki&;UZpo%+d5dD z{9VDQN)z<&sK^|Y$H7R~{JS|vO1G7EAs`BJZ10T?VZ|9*UT(lrZUOW^&o^Upf9l-}3qX{Ogu zU1iiJL>jFxAA-vl^!iH|#gCBVr>5BZnzH+EnVHQjsZq5iiyWR~N}VnoANeXcjMS1T ziV7k{9;C8zlBi{rjmaXXM}0b9i~u7xj4ZG0hva*dDL{Q95=V&s$3>;lK zce!MW7}MuqphdeWa8zhrO|_+(FgfygwWmXXhL!R#KnR5_%>7TA^7 z46&0OoeM9WH)WO(jn3f&*ue`c_>~L(aG!icsnJ}cM|J$nth+vjJ&SFxxAc$t-H|%4UA@I=is!tn(=6euocj z1M5bd?lV&7$QE}wA9BEU`}yzz$aEPqI{DB}CeQIcAbEd%UlqI3ND@ z*SPA8Tt$^gSIP7??M$$LHwIiA%0oeAO0?MN-6dWbyiMvi2;K+(q z!5INvZRko!eE?T7ZA~;c;F1LRZ+~i0Y&z@)HO8L9jjhx#khPM56kWjjuI}DbXZsxo z9p%pbLk>r0Uaf-KwV`%zs0H7uAxqb(p~uU*PCucac|z}8*DYPA`!?MB&u!?PPw4&Y zJ{E2-hJ};5`lWr+kiC literal 0 HcmV?d00001 diff --git a/sample/hello.c b/sample/hello.c new file mode 100644 index 0000000..e5460c3 --- /dev/null +++ b/sample/hello.c @@ -0,0 +1,44 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2002-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * hello.c + * + * Simple COM32 image + */ + +#include + +#define NULL ((void *)0) + +static inline void memset(void *buf, int ch, unsigned int len) +{ + asm volatile("cld; rep; stosb" + : "+D" (buf), "+c" (len) : "a" (ch) : "memory"); +} + +int __start(void) +{ + const char *msg = "Hello, World!\r\n"; + com32sys_t inreg; + const char *p; + + memset(&inreg, 0, sizeof inreg); + + for ( p = msg ; *p ; p++ ) { + inreg.edx.b[0] = *p; + inreg.eax.b[1] = 0x02; /* Write Character */ + __com32.cs_intcall(0x21, &inreg, NULL); + } + + return 0; +} diff --git a/sample/hello.c32 b/sample/hello.c32 new file mode 100755 index 0000000000000000000000000000000000000000..197f84209a534c7cbe0ba27969c41ab7614fc546 GIT binary patch literal 141 zcmdn--{-92AHxItdldv2cCH5!nm-S|Wcs|iw?u_y{}hlQ8v_GFXZjy&zA~n6zRycv zG6MM%!h@UNnDmCIaCBbZsRI-+Jn-l9s{OSB0t}s(UrTnlsHhxsQPE`z$YKc2Qv5GE q2WXIc^AUkA_Lc*s4-F4AA2ErJKkSj3lasHb5T0L@lcLDW#Q*@cg*GAp literal 0 HcmV?d00001 diff --git a/sample/hello2.c b/sample/hello2.c new file mode 100644 index 0000000..1308a31 --- /dev/null +++ b/sample/hello2.c @@ -0,0 +1,60 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2002-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * hello2.c + * + * Simple COM32 image + * + * This version shows how to use the bounce buffer for data transfer + * to the BIOS or COMBOOT system calls. + */ + +#include + +#define NULL ((void *)0) + +static inline void memset(void *buf, int ch, unsigned int len) +{ + asm volatile("cld; rep; stosb" + : "+D" (buf), "+c" (len) : "a" (ch) : "memory"); +} + +static void strcpy(char *dst, const char *src) +{ + while ( *src ) + *dst++ = *src++; + + *dst = '\0'; +} + +static void writemsg(const char *msg) +{ + com32sys_t inreg; + + memset(&inreg, 0, sizeof inreg); + + strcpy(__com32.cs_bounce, msg); + inreg.eax.w[0] = 0x0002; /* Write string */ + inreg.ebx.w[0] = OFFS(__com32.cs_bounce); + inreg.es = SEG(__com32.cs_bounce); + __com32.cs_intcall(0x22, &inreg, NULL); +}; + +int __start(void) +{ + writemsg("Hello, World!\r\n" + "cmdline = "); + writemsg(__com32.cs_cmdline); + writemsg("\r\n"); + return 0; +} diff --git a/sample/hello2.c32 b/sample/hello2.c32 new file mode 100755 index 0000000000000000000000000000000000000000..e3ab034dbac6eeb742409dfee9502f4c4dcfe732 GIT binary patch literal 220 zcmdn--{-92AHxIt?QWGVd@eF4{%~&@L-QM+9s30Y z7+y^N|NsBOCqSteoj}%(n;@}zAj|MT^AVoI9;rDw`8o>W`9(P?io9IOxhXlBd8rDv J3Jknl3;@RfTJit@ literal 0 HcmV?d00001 diff --git a/sample/m16-640x640-syslinux.jpg b/sample/m16-640x640-syslinux.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3516bea279c7c30e376589d81db7d6f5bfb8e6a9 GIT binary patch literal 46191 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 XB%gHVU+70DTq0zFd&Q7{BP0LW-q9qc literal 0 HcmV?d00001 diff --git a/sample/mdiskchk.c b/sample/mdiskchk.c new file mode 100644 index 0000000..e7ebcde --- /dev/null +++ b/sample/mdiskchk.c @@ -0,0 +1,148 @@ +/* -*- c -*- ------------------------------------------------------------- * + * + * Copyright 2003-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * mdiskchk.c + * + * DOS program to check for the existence of a memdisk. + * + * This program can be compiled for DOS with the OpenWatcom compiler + * (http://www.openwatcom.org/): + * + * wcl -3 -osx -mt mdiskchk.c + */ + +#include +#include +#include /* For MK_FP() */ + +typedef unsigned long uint32_t; +typedef unsigned short uint16_t; +typedef unsigned char uint8_t; + +struct memdiskinfo { + uint16_t bytes; /* Bytes from memdisk */ + uint16_t version; /* Memdisk version */ + uint32_t base; /* Base of disk in high memory */ + uint32_t size; /* Size of disk in sectors */ + char far * cmdline; /* Command line */ + void far * oldint13; /* Old INT 13h */ + void far * oldint15; /* Old INT 15h */ + uint16_t olddosmem; + uint8_t bootloaderid; + + uint8_t _pad; + + /* We add our own fields at the end */ + int cylinders; + int heads; + int sectors; +}; + +struct memdiskinfo * query_memdisk(int drive) +{ + static struct memdiskinfo mm; + uint32_t _eax, _ebx, _ecx, _edx; + uint16_t _es, _di; + unsigned char _dl = drive; + uint16_t bytes; + + __asm { + .386 ; + mov eax, 454d0800h ; + mov ecx, 444d0000h ; + mov edx, 53490000h ; + mov dl, _dl ; + mov ebx, 3f4b0000h ; + int 13h ; + mov _eax, eax ; + mov _ecx, ecx ; + mov _edx, edx ; + mov _ebx, ebx ; + mov _es, es ; + mov _di, di ; + } + + if ( _eax >> 16 != 0x4d21 || + _ecx >> 16 != 0x4d45 || + _edx >> 16 != 0x4944 || + _ebx >> 16 != 0x4b53 ) + return NULL; + + memset(&mm, 0, sizeof mm); + + bytes = *(uint16_t far *)MK_FP(_es, _di); + + /* 27 is the most we know how to handle */ + if ( bytes > 27 ) + bytes = 27; + + _fmemcpy((void far *)&mm, (void far *)MK_FP(_es,_di), bytes); + + mm.cylinders = ((_ecx >> 8) & 0xff) + ((_ecx & 0xc0) << 2) + 1; + mm.heads = ((_edx >> 8) & 0xff) + 1; + mm.sectors = (_ecx & 0x3f); + + return &mm; +} + +const char *bootloadername(uint8_t id) +{ + static const struct { + uint8_t id, mask; + const char *name; + } *lp, list[] = + { + { 0x00, 0xf0, "LILO" }, + { 0x10, 0xf0, "LOADLIN" }, + { 0x31, 0xff, "SYSLINUX" }, + { 0x32, 0xff, "PXELINUX" }, + { 0x33, 0xff, "ISOLINUX" }, + { 0x34, 0xff, "EXTLINUX" }, + { 0x30, 0xf0, "SYSLINUX family" }, + { 0x40, 0xf0, "Etherboot" }, + { 0x50, 0xf0, "ELILO" }, + { 0x70, 0xf0, "GrUB" }, + { 0x80, 0xf0, "U-Boot" }, + { 0xA0, 0xf0, "Gujin" }, + { 0xB0, 0xf0, "Qemu" }, + { 0x00, 0x00, "unknown" } + }; + + for ( lp = list ; ; lp++ ) { + if ( ((id ^ lp->id) & lp->mask) == 0 ) + return lp->name; + } +} + +int main(int argc, char *argv[]) +{ + int d; + int found = 0; + struct memdiskinfo *m; + + for ( d = 0 ; d <= 0xff ; d++ ) { + if ( (m = query_memdisk(d)) != NULL ) { + printf("Drive %02X is MEMDISK %u.%02u:\n" + "\tAddress = 0x%08lx, len = %lu sectors, chs = %u/%u/%u,\n" + "\tloader = 0x%02x (%s),\n" + "\tcmdline = %Fs\n", + d, m->version >> 8, m->version & 0xff, + m->base, m->size, m->cylinders, m->heads, m->sectors, + m->bootloaderid, bootloadername(m->bootloaderid), + m->cmdline); + found++; + } + } + + return found; +} diff --git a/sample/mdiskchk.com b/sample/mdiskchk.com new file mode 100755 index 0000000000000000000000000000000000000000..07e1471b3ddc3696f865352ef7a5460de88f2c38 GIT binary patch literal 9680 zcmdsdjek_to%gvjcV6;JC?N=97@W+6$V+EfMPm{`819Hd2t)!R29XzGMM9H%6Ya)i zaGE+NH_7wVx^3-e+e&etZEYVHeb5c?tb~l6NwnA`Sd4&Z0qY%Zq#=ouFd66W_uNU; z+THyFmX~|aJuknv^E<_`u#LcG+s4 zsu6cRvB*|dRW0tV+FxT^S#{v4>b;Nc5pDB!6xhn2dUSiut_8N$`*$p|xeM>QYvE#- zYw@DeD$%xUf0gvu9@`VUo~WvRDkGKbsM=4)Te{m$IZx!w^9QH+E)P0sZoA#tlKYz7 zdFY(Igjj2g?ZnCjlcR0D^a5rax6^aISCfC8zNK&2iBuwI8c0h=!KuQxtJ6+!qB$~v!Hpwi_+|gnPB3~?dy4UJr@bZfE&JgRUS0OXZ{ziQ zyZf_ttMTx=X!qFLHKMFDD4%nxW9k3@(Y*4q4afI#?Oe&}ximOu3q2eEEhrhM*9&@A zA6ZSRl|x8CNbJTwtX;MH7$Ivt zyN?sHy1e;E32pZYLhfI7dfIOK{j1t;+BSs8kD_?|{F>JC{g_7IjB9emRk>oo-61VG z7@NCRa>*4#a>YnfhvYmMTeViQ$rVw#VhrCo2VY86=Zj7`cgWdn0nAg)Ue49qbV^L|2YHHK^@{8@s6FNDy9|W5yWM@N zWyQgmMj9WYJ<9({&|p)Ccr7Goq}e-5tILFdwYqd!NGLCAW$0of=tD=@Zmlj?tDB9N zRjV@ts#eFlu9Ts1O0kx0*z>C69>v4?-^x{gPP@86-v&7+?mU!mhRql3#HRd%rg*t6 z!SW3o=_fSkUq&P|9e8-lL&02a#vFB|+^qLAW%nAK@(jp5wW&{^W5FD!HfN4HtZ*c$ z3~cVHT;;+SC!cqoc*aQ+fv$t&&pT^!e9H`y$saVUgLiwm>L|SkfvA5un0VeP&S>=* znCe@V?`k35poUw{DFIC#nFBJC5>0}y*p!4GIIYxw5$HYnqVvS_41;oT{Kao#sQU`T zi%v23?loMEiNQWSfy<<1YU(v0nxm)F+K24Qqa-OXHiHJWoQwA38fniW%6%I9&~iSu)BebF@eQ=| z%kQ4ZGjZZuddW^Cqq`%xq3rjnc~iw|&#HCp9Pz)!2g_-Y_Q*4o0;A@&J``N9bMGJE zX^6g0&&gYPf1{ZbGw~#%F;Y#!UNc8zkJ)tvV|gy$PV*nA5cfCoL^9DqC14B-2|}VC z3omB)c3NWiZXRg#oCP>u}V0c*J>V#CbU3JcPEcp>pNG;W6i-IE*r%e>moB-92K_ zHR9o*+%UxT$V>X{Sh%UBzpdlT6D{XPorliaTQ?b~1tifiWg209z;CP^1d_0Y5cMA? z1!|Tv&G`^&C6fptzG9nX6x19Y#wkY3i1;h3|DpDMt+I zwIiIMUJ(mi9qQ#`PMX`)C*CJFBziyP-iNfPNfQzbNSP3_5cP265gq>A?{$c0S$D6m zc&n5TcsKd#xm%@M1Ydn(tN2qjn_)kvUQj)NnX8Xw2>^X_bDj{$Um2;>Fr}W`DqdA1 z`sii#VL+eejW@?(C|qlU-K{J^NGX-5Apet%bDe{-ae-#Z|u!0 zc3-9?kN4M`iIf&L8}bQ(oF59drxPWSf&ICMwMH^%7{_AXA(Bre12xflKD>%2gf1od zay=hfl}hmPhcd&f%n}(|K`$e5bt4aGWDiuVuMp#?vaM(tc&nlzZr~da-c4dhFo%ZFw~V zFq{hQnl!u>n{ay1$aRbpb~wNf4o4LUdvG|SUb8_mM)neYTt656$^r5w;r%yKrFm}> zpfN9(=E%!@>nvFzQ-)^IyargkB5$gc0_17wkc2%s97v8f)tM#h#1tE*C}|MTjCM_f z;2}O9Hed?AeCy0Mbs*5?x}yHMyn+Um>M8!lRLB~P8N}sbZ|e7n>`hgkO9yS#!%tFp zx9>5_SgV)yf+jYCsW|3!yv&+#9n@0!P!Ldf(So7nL~X~57}QsR(yE@V*u>1Yf1s9L zq^0o?G(0T)nc#6{Cm!dpUg)D0vB=L!Yh|Czw^oc$VFX8Js)2@OwD@L|Hz0gS8)DGe zoyGg59Z})WHgPGQK5@D%ob&U%Jo~N2_zO;Ia?*_kbt8tBi_`qPK`zF){B+_4XbJ|{ zm)@(Yn35%yCSfoD#cw?v$EcH9)SQUOsmmA)Hg~qX4L2@6w|4m4|`r+s{gKu57Rekg8AbEhCE1di|y`{UE zZ##uhZRnNWKhZxdlAAtF*FF}o!l!2IDAj(L&3#g)KLj|_IXVc&9}Kb?Hg$--vQ!l_ zmPnuY>J7I`ANfN@^{k+tVMEIiwN*e6XTsiWM)5*1-B)avGB5;R@E5$S>Zq@HzLbh4 z?|Ur!3sqrWB|YlR=A{qOX;9w>kCGX3bg93mgE+xS*}}26!AKVNV4G?ZbXagQr5{rSb!4u652_mRn z$#q1$Bg8LqYcz!KqDKB{yDY>2#NFra)?K;sYAUTrP$AYVBzTuMzzgahLp+HJ<34d^ zQSF#GwUntIhi9R5SBQ71S3|s8Xsrikm&DVAI>N@MVx(2#LZ(Hmo5cp`5CBMbufYVb zMiDe#Y+jcU@@8nw+p+^&Q|Yjl!#e_NGOz&%km2G~sWYMH-Ef9r0En?v$r1+fw{t13ksI`AU&@ zR^v^rwei*d8=82wIh*#oqJmbD(ALdT)v$LKA(8p5ojCKkboe*QfvW-m#wenJ)7M;eOdP8`eoT)jU3MYYJIYQc-o}5$t**RdXZ^Q*kdDzN^R7WOFgrx zXBPF`Bv%enPY%?;SD*H%^hne*ofFslO;-69*^BKOlf7|RZP_~}uZzJOH9wH%ySlPH zmH1+J)GdI)N5Z~H`HdZ&sZN#nrtWH3 z9dWA+emcR|3NA<=$O%~%>eD4z7;~RT`Z~XIgjS9q6`uJH{eZpB(8?%s_Tl4<_my_V=@79x;F70Nz+&=P;ZRnYLPh_hIujjn{3XHY-bBu zRJA$r%|TnlqeB(H{Uv$?`;0G~S7c7~6!uQJJbr>D7K4ZqV=?GQWGNi}ARR_J@~b!< z)P$Jw@9{_h)(pa&0GUs`X#!qCZj2)!bHlhG^I!2c#+1i#s3~6cUhmy5*Kse>n z9T?0KZ`HK}36j#vkooI*^0YDX&KE@~9z$V%|3e;6ew}^-eChqS=!M#RyoC6#}Uy@*z*U4TCM`j9YVX z(ko-S#;jo!N%!jTSLpDI0sk%rUs??Qk#beQNTvyK^?$V+m^e7`U*!jK=$VytD?`Se zfhAV|sohG{OX~e)>~vH{ji94q1RrBc$i$RiGBeSiHS3}WyGr8?b~HO7hNrC%LqQLY zsZOUc@(Ok#^4)|^SejgsNUMlryQ?)I!x!7cI9O1AZC3x;UvFR`E!(39!X85k@$JmM zk=&4VOci{h2QG@zzrs#uGUSake9!k_0-+~fn`Yo@A!(qi(`>n)9S9jX^Wht-*jLa${Ua+ltx_jKgg_j01?iJdV+3VJKknFHO%U z;L3ge3eg=(S01RQg_^XHtT2XuULuyk_(R*<&3&@Be@(qb7e$ zlQJ2NDFB3kXpPwh7_yhwF;m2b34&_jRzB*ra#AI8lar+pr1C&vHi+-C)az+r{hjKPDiZ#%%Fb4rp8Td;*T*= zeJ+{bPkJSYX@1F7tewz1iKt!jNQo z78c@WqP|C$7##AFL|#)=y2S^^#1)8t+e`qwnt@S|A^&tBeLOF(Nu>Z(c%yns%Q;wt zF+~Iipm%OjromBXl0{xUPSI39$5@&b5MBoaE>f$7%vtjY?NOgWii_?g@)B!42-;E@ zs#_#yb3=ulwa829u%ehKhKhiuCSCTV>f{>_8yq?SmI2dYSu|j2I+j|KOY44z{OcQC zuO(TQrY1QP-zp5%E@K1^e{EgsB(N#yOq-y?7?HFjJglkfnCNYfY||#1$084C`xVFQdis5<@=C#s53EA(q63q1w&L3RXxbpOqw`0$r?) zFmWJ3d)3S38(69hU@1jjgP1C#hKDp2{6aMqBS8aUi;_lYCz4v9Mtd08ua%%GU=Wa7 zKk9~Ouw+Tw*wGhfLd!?{<*n8iC@g$Z&!H1aj!~~F!LnGD?^#m9&{wNCHL2ms)&v@Q z@DE5<(F~iPlbTP*bPWgTCpAsIuKe#f>IXkXd973Vjs=ym8H_?@H=1Ef24U?jdOK5Z zm!b`J!j=^F$rdfQsNH%XX0eHehk~-#s<OHl)uM=_*jgPt^JU`k5V84HFw|Wozz0lENXr>XKny#Wfo!qG9yX5AfngFLS3h zVj(1mPzj&`}vySp*@BNS3F8OQZ)I)?hR zhQ4=cSnoseErvX#@{L8!Kg*emga8$$z%5|+8Jr4Z9+RYNY42{EC5q!qkRV{tw+g?Rk|orurTV3z9}4w8Y)pCN-(UD z#RReP!`aZ#UlsP$8W4SN;|qtS7nyI|n5v%D)@I6e27r|tdDJP@Hzv7YNoX(V?9n+m z1utZb)DizRLzL&l&*g0Bf$tJ>Z|+Rg!b$s$GiY|v!)D2-gE9aMeVC_>2B!5u0g7WX zZ=6mW;)Q3W5~B&~LQO@R*n&ngyLGBWK9*ioXRfgpd9AfZ>b0T^6-CxerCmA|$vSQ1 zMMATgtS(j?Bi|uKb%wfMfJJ=%1WV*?nY4d7(6S6^Rl2(miS20Z80{?VugOEuTF~En zsj$DcOrFgR%a&iUjOGyyUR<=~AxXbaQgs*%_+tFO%*1uE`a<&cN4!vv(!09pxa|Fw zKbQp%7C>%-y1)7xeQmZHVqCAvlpEr7E2IkX^0dwnnWg)uker*yb8E08X@G2@A2FcU z*{rQBL@&YQrt?y9YkihH+aJmadt#_VdWP5|${u4VM%Kq^eZsHi(E33NKImZB!b+)d zUlZ7*RgqF#fioLys#Y+x%Xk8?S!97^CEgk%$o#{QMvFv%9& z({vi4riGm~xc-DqQ>O2Ft2WNp7QwuuPb1bIA zX$~G;%B+Dq+SJ!*kTPNX`;=4z91@EeP`rLq)0E~h_0#rm%?q|?&ts7V94ql)QFpY{ zCt4UpOhsBU=5jCoyW8(ra_3!z+aKMrbJy<2_U!%p$M-$4zv{qus%u24w(iNNzMHK2 zn^|nRE4Q8OI6b$&ex>ntyZurmV;s*iVP*Y|;cToEUX&5>2FxwdW^x!z*Y6^WzkJ^pZ^L^uG68~DJYVnQVTAlsc zw7?mb;G{Z!q6DMK_|7+OtL9%bsh{{_(`r9fF9gQG=by@p%(;$go*f-I_g+Wu#~hRl z$4KvH@zO*mMG!>b%b4CMEiAY~d(Rj?8u0;kpdrs4#qS8Jarve^&XnWfAl&hx`y4dq zKF5}z5yupB%*Zv($L-a{H25ly-)@{yVH%7lNLEXK^7k5PEj{ziui0@^#eVp}euHr{ z9HEqc=!l)>9I;!N)+vvDF%jmOi27Mg+J-U&dRG#gx2T4Kueq;DHMq>Q3O)!F5_W#U36Cx02iKs7<6pl+X^CpwYVAODd zXtr`%Hl)O4WBe=m{Aa?jIXJRr^_q2L&ANL$YgU(%@(0WD+PImNZC;gZtS(=dY^>V+ zK(aA8$F_U>6MOePMOKM>c2z%GRV5NE0W5Fz#ua4a!WFDX+W+|es=EEeQ@yu#m(Af? zyxF$5#h8`)v+s5xS(iQ&aBQxwCp#O^vO@=6cfMx@+H)3vB!F1E&(3W1nQJ z*|kHgs;*gJ+p&kua7f?Q|1C&O*;loF=dS9BRg0gr&2!WgpnJy?JNNC~zl+TkYEsGH z{IJO;R@t`i+gG(?ySU4?d+$E{VoI!*c8C&az>l5;fED-de{A7_s=fO~Ta9$!0Deei z+mGKz=?HgX4&l;qhfQqVR`&GF#*;gtx9?6NBx`Vu!#23y;np5>EY==%{GIl=<91DS dxCWngEFWxjlnwsaabWOg4&UG}Vdj4A_zzhN$T|Q3 literal 0 HcmV?d00001 diff --git a/sample/printf.c b/sample/printf.c new file mode 100644 index 0000000..23aa144 --- /dev/null +++ b/sample/printf.c @@ -0,0 +1,306 @@ +/* + * Oh, it's a waste of space, but oh-so-yummy for debugging. It's just + * initialization code anyway, so it doesn't take up space when we're + * actually running. This version of printf() does not include 64-bit + * support. "Live with it." + * + * Most of this code was shamelessly snarfed from the Linux kernel, then + * modified. + * + * FIX THIS: Replace printf() implementation with BSD/MIT-licensed one + * from klibc + */ + +#include + +int puts(const char *); + +static inline int +isdigit(int ch) +{ + return (ch >= '0') && (ch <= '9'); +} + +unsigned int skip_atou(const char **s); +unsigned int atou(const char *s); + +static int strnlen(const char *s, int maxlen) +{ + const char *es = s; + while ( *es && maxlen ) { + es++; maxlen--; + } + + return (es-s); +} + +#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 char * number(char * str, long num, int base, int size, int precision + ,int type) +{ + char c,sign,tmp[66]; + const char *digits="0123456789abcdefghijklmnopqrstuvwxyz"; + int i; + + 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) + *str++ = ' '; + if (sign) + *str++ = sign; + if (type & SPECIAL) { + if (base==8) + *str++ = '0'; + else if (base==16) { + *str++ = '0'; + *str++ = digits[33]; + } + } + if (!(type & LEFT)) + while (size-- > 0) + *str++ = c; + while (i < precision--) + *str++ = '0'; + while (i-- > 0) + *str++ = tmp[i]; + while (size-- > 0) + *str++ = ' '; + return str; +} + +/* Forward decl. needed for IP address printing stuff... */ +int sprintf(char * buf, const char *fmt, ...); + +int vsprintf(char *buf, const char *fmt, va_list args) +{ + int len; + unsigned long num; + int i, base; + char * str; + 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 */ + + for (str=buf ; *fmt ; ++fmt) { + if (*fmt != '%') { + *str++ = *fmt; + 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 (isdigit(*fmt)) + field_width = skip_atou(&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 (isdigit(*fmt)) + precision = skip_atou(&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) + *str++ = ' '; + *str++ = (unsigned char) va_arg(args, int); + while (--field_width > 0) + *str++ = ' '; + continue; + + case 's': + s = va_arg(args, char *); + len = strnlen(s, precision); + + if (!(flags & LEFT)) + while (len < field_width--) + *str++ = ' '; + for (i = 0; i < len; ++i) + *str++ = *s++; + while (len < field_width--) + *str++ = ' '; + continue; + + case 'p': + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + str = number(str, + (unsigned long) va_arg(args, void *), 16, + field_width, precision, flags); + continue; + + + case 'n': + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = (str - buf); + } else { + int * ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + *str++ = '%'; + 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: + *str++ = '%'; + if (*fmt) + *str++ = *fmt; + else + --fmt; + continue; + } + 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); + str = number(str, num, base, field_width, precision, flags); + } + *str = '\0'; + return str-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; +} + +int printf(const char *fmt, ...) +{ + char printf_buf[1024]; + va_list args; + int printed; + + va_start(args, fmt); + printed = vsprintf(printf_buf, fmt, args); + va_end(args); + + puts(printf_buf); + + return printed; +} diff --git a/sample/sample.msg b/sample/sample.msg new file mode 100644 index 0000000..e93cd62 --- /dev/null +++ b/sample/sample.msg @@ -0,0 +1,13 @@ +sample.msg +Note that ... can be used to delimit something +that is effectively a comment. +This message is displayed before the image. +syslogo.lss +This message is displayed after the image. + +Please note colors do not work quite as expected in graphics mode! + +04 RED 07 02 GREEN 07 01 BLUE 07 +47 RED 07 27 GREEN 07 17 BLUE 07 + + diff --git a/sample/skipatou.c b/sample/skipatou.c new file mode 100644 index 0000000..1641a13 --- /dev/null +++ b/sample/skipatou.c @@ -0,0 +1,14 @@ +static inline int +isdigit(int ch) +{ + return (ch >= '0') && (ch <= '9'); +} + +unsigned int skip_atou(const char **s) +{ + int i=0; + + while (isdigit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} diff --git a/sample/syslinux_splash.jpg b/sample/syslinux_splash.jpg new file mode 100644 index 0000000000000000000000000000000000000000..655ddbe3ac419b2fc0c45b9d5495eedccf3bd09a GIT binary patch literal 56299 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

    NhjH?9YI&220*8%*p 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«>poghh7$|Qzi7Y=yL4Px(^jy%6Fu1vn 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%0A?c&EP1Km_5(I^eg6Bx! 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

    i#uXQ2o# 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

    ^i?DUkd+*B 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%0Akyi6Jevk6KtOF-4^aZp?p<3Rcq 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

    CgoKc}{4_f-x#s#;1tDYL88ySrOmGR(+ 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

    MYFDMcj{4i5 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⋒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

    T)^T_5^ev9fjNP9RkS^Ob`j2=^ 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

    #LBGeH5B!T)GI68V3S8M&sn}rHuZUVOvE-1Gy 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
    jfeB{KPwWPOHOZhNW5hb;ABMU9p0%0(lzs2x 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
    @M^X~cc6y>DMP?vg?{3%J2y=sWQO-%(7sVH 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%0DlORDnDt&KCMN*i^`sq5{f`koFP~!Sdsu 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 xRUmOu?*vVH7drPvyV0%nd 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 z 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

  1. -pkwP>{^xVQmjPs+|BdHi6-9|{6`y2Rm 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

    8{UW$Pt!WaTju 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�~;!EnFV#I$LVnA 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�WaBg`nv&tI%(|FU`-Sm(xHa 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

    geS7-{$< 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 z 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

    yrH(&e)x8bk{i zphaFB#c7n)uo%H)bpTbL5Jp-9i0ar?TnH7FMc!LggpHd8kJBND?ky*@NC6-?_j#{* z(P=rr+0WVBa|1y@m~W7Vxx33WEroxJih|m)uDKx3R%Z$4Jvl3BB~uKP64GZPgU0Dw zr_+5m91R(g0qjHQEu23x6c?Y8NwLxEp0?jQXYIFV=dJU@{kQaNAxj+x>B@0RDU?%q z_QSJig*CJ$SX6y9_{nM38yFDHAQ<_*qS-I8)q1WXm`K3ErE_v|>I~a|YZa;ol?sma zsQRiV=l&d75Zd~A_hSzz>tBZeR57TiJHU`?&DO9{INaC2j>OmM7T3dLBptvcx&bKQ zL+c*+8#^QeLQbrQ`!vJu)W4Y%n=elMPvUk8C(lvI=zgo-akMe8qW;Q(+W*3U09_ih(0rrB|!?CK8WbGFyD|Laely3(h=!R#%x5Xv+}??eywRE9iay7Ntf0+aw-o~gF~AC{wJ$;UQ$S&wVxWLj zo2~O!9f#FyTj^7l_6!n@l{Px>tOOy7hpmp4D5&_n+3s2CLXJ$I2_7C-mHoDynZs`h5O0Ekt(cNdNl047kI0Q(zEsn z8p88MK8lz2&wi2wu%9S9S8S@57)r!es`0Y(_K6~@qr}`^47vr&j^l;xW~WuKY+YWe zW{>l$Bstj_9WU*@)5+YNyMIzQRArYMd&tZ(ax25me+bn2bX zO(HSm66L1Gunnb0@qNV$&raI5Ez1kfJ14dc%M0tHzHPhm!d7F@0wKt@X*j^i09kN* zXd`gZ_+|6FUai#jqoG+r4yv@XD=$+aY6ph|ahM8GtsN1>K`O-IzHN(Z0DHU_d+BW) z+Sq^waHMkrUBnTRuWb>rbl7k5(!=!9s%?>@;)-pVvAB8B=eVo3($Ps9&PE&Kq(SwX zEe8wqnbX9rCdfX2Lfuk8hF$fGQIEjOsHvLMcZ_w&O}yX^d9}W|dB8VXhL+m=;t5xW zKql!yFboVOqE=(VA8B*uj>n4tjk~80G4at+&DvH60s3ea(-?L5>WuB>YMv6TT!E-X zOwhXmQ7f5X>onWvgHdZ}W$ZNFgY#%ZRg=XZ+EKB|+6npLKHY#U+?~4DXv4*+*&5kc zJP}HL1bakKL!8f3`$hu>tEUFeej*Xm!t7;n)O&;56T7(_!Nf(a8yp`22Ccu=J8!L` zEpCF7f{o!*!@Uo7VVPQO+GT94M{<}=HYeuSJJ*L}i1mCpPPKMmpgsDjP}Fz~`%Pyl;uqf;@S`a1^ncMW z^RHQ@RMM}2Ck>PchE@R<3+s)J^^~N-)4|`Yax<2F(9Ugr&iWv__lH0^#VQ&p>{Uy# zBImqO2f`G^l|*r~F3M`J%(CI=tnC~XihESJ7ccDeoPAj{R(N`fHQ&jq)p#jZeJAV2 zJ6uO)MT3O6)6>(|mZobUe%I>T4iZr~=-UnoUN{^L+R#G7c2;l*xcr|sZHI-x-s#&U zcv09MbZi3rVB<;8A)15HnOfjI&r({^X(K8>#k<=nv5w@dU}3#}Vx1ITc5a;xUUp%f z3o7fMTc?7T)oJg&JW46FUUXEc^`4_jt=Ak?YQ5!%!3?aI94b6N6P&kZDZG${Ra;@_ zTxUb7@cjT+1sGY&#!eA71!gFWff^-;V|w zUX1~X(q_xfg5HmC-}W14@2u@&>06_4-z}yB77eje54w+^*PY~zCqaZ=}7z3eRa+k=0MsmDrF871W?b37`O4m+s6yBV4E5eMcFiE{3*^ zt>Ad5T%HV3+cD;V;oz>Zf=ybK#Z_k2g!1CEdgl}+8)lUoIn3!rrxSyzY2zYtb>8+5 zoL;2g)#X|Vu&)DDEdYBufZkULP%{AblK@o%pk{62Vq{f1YA6f{jnB}+;WewwI0RW_h#sp-a_k=JyNOze2yS3LNL7KmOYF%+qxGDa9{;X6wMo8I4;<@g5aPMH=wV$!o@j3t`zm$wV zAhQuj(BB11P|pv5_{gb#Qu6H*feHK9;(!U;gqJ>>fv~%?5t>%(6Xu>crjdf2UfKM zFXq~?YDy_rcpcdbc`A*eJTTEjH_`Pba2*9yJ+k5<7`Vor+N2@_&qaryu~M*7Q1qBZ zuX{)In$2xhfXc@1_|{|O-h)A_Ch{fIvRuN<7by)x4v*0^cnw4X*XgG!XNaU+PYtPWS=3jQC$1pROX45LEbElVgORn9{l+=no^IW zeauw9f>Uv|2;3VlG9lw81E$5Zu!Z6I?stWX9WQsFAx+D$S;1WI> zZ2+HrE#M2;C4Vs-7;nMRay~p#y-n^bbW)n>&}5c#HZDjAEHsbJ9qhirb{;vn}ZrGGfdGvSudv^ZOuDv z%WM?@5qm>cHPfPg;Ge!NvHu&_P>B$>2&XnGd#K~OA|AUNU_AQZg`ED}-V!oB8G_vZWCOEG=wPeh16FlGq_ z(6ml+iM(NGMvBoCqX&=d)AN}4T9Qtj&z}>J4Wz`qiD(wD7BW9AWwgOYm}rG&P)bwD zk>eGtq+)-fr>T0sQh%SFM1R*u{jvt0Ky{qe8eRC%KS|SErC=LG*kF7e%GscG(zVx^hA3S2|xJW_E zk^)|Xq&wc^vEJeWi1Rzq%4hp{tb<81_6Vm@F|lcl?&Cw4*haB~L~bApA0H+7_$tA` zNS<3I`E;Vd%62QI7*rOY<@C$-YT9|Esoum_>kNz{rYQqY+0W-2Z#8qrSwwXN#|(fZ zmnJ@kd)m~kMe3?!E~S(Gn~G$DVW2&X1L3a5w`}Ik7Qw@7<#~nC)47i-9DXmT0+MZ1 zz18YtK$cIwD}JKMJ)=zP7@J{->g`6)Ab3vJtjxGTv8ydg4SfD2fEO_GAASH3IT+&u z`|(E#67o5X$a6|RTS6ZkT#o}Gm}or}Iw%krotYaF8B!Dv{3ps5fhrPTK4I?UE!`pQ zT=s(|M`1$=yC}^+P`x0mL~!^LE$~kH8Pl%2QxvHAq+yql2}pc+R~6NG!f9zdi3rGh z+GD5wJ&2ZL7P=21LrWGmL$9GLk5z=Dvxu8><*#_~7J2YP%Xod27BFQ-%^&Lk>RSrI z2kW_W<%d~(bi9~Dcepxn?#{gE!b)!ibWU(?i(8TZg*m-tMk~ryJ;Bg&VnIl+PlAV7 z9hIV*wCjt-GL+e`VuA`>7$~MMb_4Cs3Ouhimb^iQKZr8pk6}?~g|dG-KSUFVlH9V# z$9#`cyFdJZR4tx5EIUC}rCZT=MFII-WC}9)?PEJNKpRssxQ5zK7=0h#9 zAK$REzllqxKDpIn1Hos`IBm@^CT}-z1;cONUwO00{siEt6=SMFKyxZIA`Q6K8I`2(j9;F zRzz11D9_^`ba`q}q0oBB`PP-33}w2w{^{Qj$t4_8kT?iwF|Py(mCRq{0?Bzk8!YK^go% zWQk(T6%C)9cmNTlRayF7gybExBpOO$ip|{{0m);mahU~VzVY&*s6m#uqz(*U1%F^g zK7UCKM=63d8{&5`ZC8mo(?oacY{vGSHc#>*u5>^@{Gk2(UgsNI(W9M&h9=36_=C_; zqn2dpO(I1Jk_}@TV^VS4s>dlYRS9@?pDjto424$rvFjdpH%{L7avvG_n968S?+(w~ zBVa5e6_Vl`G8W@FQrWT++(j4eCglQ|6oha&=9$g;g9exNw4<1<2y!Gmz^x!5fuL=S z4x%@=gykvl7nMHeMS|Oe9`^*2JPf$k#O#!?{Rq(q-7sJ&OWPIBb&&X0_u4&R(_zbs zkya#jTCU`E8N0Y#&@^z@Z<{rMPC(k*8>Rkj2CC5%D?;vmI_hYDo|5_W5J z=F~eFSXVECe#rt65OKbkcn%Mcs8`|hfZ6y~Fulbh%uR`{63A*88BQTA;tdNW0$#h! zYSz}z2zXg$BzJ}Ehd1go{eF(uugdA^DQ+mP7YRXzK$yQ__vnUy^)m?87{~BWf3N&W z-osD_!DBYb?V$PC<0L6{04|>R0aq`>Nd}Qg4FX?mO$(8fI-n~!jCc*J;(>oO&!U84 zLX`ZL#xz0v3T-V~F>1$Is%m$YqS_sC)ozSbzcLSNwufjAj4Vt(fhu`Jg`)ZP+$=RN zXA~6`*XRE*jkZ3hz&GWhp z1nUK)8)e}FUtW2{386@*iog=R`=vL=cwU^Dh4?_O)l_O6>ZyB;f$OQJiuwb`C0gCh zDOTAIi#miM(?WEgq~N#IiKx=deL!}@agi=b1b4zzDs!$+h?Y#@#9WMbp@xSf$0*69 z8Fh$b2d;?$jVL%iYOJCkHw)<2T>#g8nnyn4a`m)H&~zFVn=aX2#udp)Ufznm8B}yh zQ|XQA^y6_c6nLZ_e=f|W{#BG}{nT4k^f$VPv)`mV7G21+A#75c(+sT^>+vl=0LMOt z1KQcy5piwfV~r~5`EevirGy8#sQeZnW`07{OS6zZ^T}tXitQ=sD>E332)xZ&LS@tB z{Vh{8I!xWD(ag8!GJf!DhEfbl9haDf19BYa9=h1cSa|27KDQ`KOBBVIqyx!EUsI%8 zp|25T+~dMpblMv<5+$FL0fjM$tro&c4oLfi5rqpoAdJm{Hx=Hn$$B<>aKvx8QS9JV zv`~0sTut%2q%5sSe!^my6}=YxEN@X@UbBNh)>_142@Tzr(T)rKMw?>jk_GuynL@GD zYGo#$J~(~5)TNBcvO^M#REi8U%lZn1~_xNJ*UyB4~HT+?^jeQ40OZpzdKrvis73h4{4Eq?IIlDjH-Us_;?!p@yPGs6I$ zliBuG!JYhL9j=^c25>|rUP7_fvE<5?$*SQOC%u#Uutoj?t>MtAsw0Wm?HlGV0&nIn zi(L3GRxdi}J6x@ml=9;{2_2s>EtI-^I5T3u>l6N*{cBH*`1d@fqjNX!X)Qym@l%X+ z2=0uLO8LsWrudKR`J4n2N=O533VAqA-FVH;51&#r`oVp-gs^)wYejM7C*GgTA8KAGs+Epkie2E)6qz zxDfe^M+ZUa6FZ?r1d(AJB!2N*gs9LfhzK905%$E4p#pCVDP>CugX&G;E^!Vyd@9;I2 zulbgIco!8kJq6}(c30wdV|V|ucTP;U=0ml6+P*w*y%y3hTt&IqxImr!m{Ax~j5Q*Z*C8(Ra}0~0pDeHHEtNZO(2%}C1z*I0}+O&O>cW4Ow#Z*DyC zL~gf4J!zc_>||78^}K+-O1tER$k36;r6W?@;%=m+K!$EN97dj zeKjn1y+Q-On+4p1X<4p47xT~o@j!bkKlCWprfz4E;y;p6tPNk8J_tu(^bEyh0^w`T=vv!{+o20pxI zLhg&1Ys*tl?6)Y0Eyc&acGuGtH{^sPQoMBCc|y`~=MTUb{-S7DRko3AWM~Een`tyQ z5qel9tL=E+N%>5na4SO9ezv~-iwFiDqB=^(f5rgy^YKHJp_6IeuJp=|7DnWnA-Ey0 z^+2FBmph_V%?)kQqWNPvYqc*vPPSHp3Z&Tp<^NO9jou5=OXZ|Vlb`Q#G>e5X2?l;BF&uuy0- z3i-RuPCKJB_)pY0S0A=9hvxLbRCZtc*9(fYPAvswUMW>%(ntuS=h1L{&Zk{=vsg?p zhowtF)?oZ0x-wn47_Rn)>jo*fc(c{%keUZWDb)YUhUa}IGEwQ`@J0tM^|uV*vlVf# zVzGKysjywPS1UogbXAWlrKNDl75;2+kNZk7@nM_ z6gTv1lt{RMA})E}KhRQ=C)NltiwA7isfSj4Sd1iC<7DEY7KP(hCX-6ufs4`73t*K5 z*+D0-;Wkf+XQ{fvimIB6X5T_hCm^Cxh{6S1iPeu*&*vlh6My#OQY6qb$ZO+Bn#90{ zfC^-p@fx{rsJ(d-=c=EMS_4$6_{$~EI~F5a!oVl1%wHm|ftYCuM=l6#a$n%!QDmjq z;HYAf*)@uoYk&`w5oY3yuD%K-9*R-EOq6*yco#X!s4FY?Mho6ZtF1rZ;#kV~Isx6( zrYO{IRg*FyLfP5}*%}&z4zIr3A%+g5b+djzC3wUn1^KCHP(aF2kS2pB@pFtOVePj? z-$sr8F0AjaMy9hEiAT|$OuVBAucQfq-whw;W1iR;|5#F5WP0fjMO=OQXWv4Im8r&y zdJH=lq>5Rm{-&{OVddyXrv;%Cr957p`hi6fg$y{So07yjWO9TLm~yg*$xG(xyx;?O z5{c9k!nB%{6Xt8SQXfPY8jCbVg~>bHPP$kwZddAyK&@`6EJs6r1t)gX5TKPLxNlJ0#d$kPTzm^hhBZQ@lqB z^^D<^6ED60KhBkxFaK}#U}tr|`d0aWYX^t%{BJM!_Wzy#?f3Y{7kA9Ix7i+upYT8M zo1LO?LW!H`k)}PYgLu0O0ysV+Y>zJWOm6Bif2*-rJ_Hp1px7u;hNv6~+qOoU^?)C$ z$Ucwg&DK^jD^wyO)Thc$wZitdt1q{yXZ+vjKV?1$nGgzdn31-Hc+n2kt7g<|fFw6Z z&6JP?f#Fh}8q}#B+UwLQYcP(ZKV5U-rVm$L8hdv!-|lz&?8HX{5kj@c_O|z`h-$B( zP!&%7d%7KlYmZe42V76Q>EaHj)YE1ZB(6_BNTV`c81)lFXJenqRD;Wrfp_f%a7mAn z!%5<}_NQ>y+)8%12%n&{C6(R@jY+ONZ!QueCkMo3F2?KeG-iSK>)H>5d`d5bu9Vv` z*D1oez=^e(_Vf|@gJTcXT5nZLY|t9^I`3Oew68{SQMAKVFPN*ROszx)DEz4r%0=26 zyg&NFK9Fo?ChI^7$Jahyys5^H&~d>3k(IPafwx%SaLWpsDmg%Ryjr_cN(_T9i8y=s zXpawlydl0;FvB(L2%J1az+Q*KerCJ~0=&pjY;!KksC|Wl+F@slJCDxFJ1K@w^*nHh zmj_nI_nlA!Ad-;8#R)#y7Hwk~>JFcz8SIdd>ptivAcv&JgXtIr74YaD8Zu9fTT(kst1MJoUY~ zuv-cj(@!4Ret~>18iYmME3x|2LJM5wO(M}lqzP&d{#sTxU+o_92I@GvdxzN06-xZV z@>N=dB4LV$NRU?121n9US5m^uMmIU|fHJYq#wXanI z&0BA}jOKbSlEa~I4(&~l%kZKL3*!M*p+y-~H_n7DnO5&V;1&aC0=G7U&fR!BAbf1q zLBO7}SCnvY@81cB|6B6^dQVUNn@9eu)D9E!U-jk7f6ITrPyS!xPwm|vn{kf`ykotp zY;$P=c>tzrq>PMs4t84@kyAWJvI=I6YjFE?lrVzDM{jOqC~J2coeQ{Kc3YbLPq*a^ z8}%+ej#VmU4AYGNyH}(C-KYP>IJHq$$q>7=GgjI6{=IL1=ltI=`?ud{|5slgygZ2S z|AWKozxV(Dw*23%(OGM7((9oU62t%YcSolqko-}aTw&OnKF=AuwN+n(fE2uD=M)}> z4Lp2B3%3|+x~P}`&-Hxw)&ADjH?c{4-|9Ad1E<^TwhEQ_*ALA?H3|8?)#(&!@$Yy= zE$l^zPdjzAPU)HPtLba?@T5I5<5$!7m#-?Z^pxkVa|hr13VSuv`K_Svtu$R9j>zMZqCf8(#*>F)5n0SkGH|03Ss@0F83-qe$>j(?6bwB$0atB+pl%#eT>2 zs*Cyu2IznCkB9olxBVJ@d}-E?=a-=7d0r(gcMb1;w9BenvEjlhcX^tYt$6>OPW6y7 zZ|qkRMdzaE$gDQ(osM`aVV;fRciLd+5czFrV)RqL1+4D~?pQ-}xwVp(x#p^*y4T|Q zBoA!0KEzUUX!t05;DHSxRXVgbg=uDLFRhgtX2uVr7L~m+%V1`YYSlyRd(E{LQLU?F z(LS%m6EdGHZq^}rp!;6L0NZ@ieHo~N?MH80;fEMbm{c#Z(C09^mN!7 zIY+SbMOIxcL4_5RxP(Z+8-pCALh89WI4rVi0m%3{YnFHQ;zaHKa5P}rze=LKZQO2Ed-B+^MxHTDIY;6{N0%)T|&!I>^2iUWN-MEs~trag+%4cVXLE8u8GPc&H6U#VKgI_C6S$fu+O*-?3tY{`8ebT$C|`oaY(>y)513Bn62W<$P5i)<&cvw* z$mCYv0|LR2ta%X)##RXy(paux+rOXVyaWT0kK zTV&xa!BZ)Spmr%EanMRXv9S0Y0@S~vzz;WRReK+X$#oPHUx6|-@Cr#w54h5rZ=5HW z@*D%bk+GoR|75G*ZJ#GslIr8O&8iPaOzW+-kS}fJ&Y<-^+C(n}ei)9jakJxfn(cma z^YJ0!lhh4838d8>HIjP`>%w4WJ~~~F7wh>dt$OdY^C7V;J>W*yQ`q?_l`gbOY8@^> z3dyY1><^kh#b>{XE=2U+$vvMFb=ysI3mw_Yo#x=Ll>}qPS-`h2v<|MOZgPd7nqba4 zg4$*TwFEQP5$tV1aM8UOwwf^_z;`4Wi^Nu`t+lFFzpYqo-p&J>y#y5`(d=zTv!9@g zB%1xrXbuwFG>PUQ9gUimTC!x%F62dh-%pkt*x7fpem*?WX!BK~yknHZ!n!e#_x;hJ zW`bN;mfIyhu!G-$Vm zh1%EFBjTF^NTJ*twYQI!zpCqT48cZ+k^<(NVRd~|7=r&_& zF;zGk#*|})sHh(=)2f{`29=op%qXnJ^k}M3B=Xd>X{u;2tP7+^_Ck*Im8DR1r*1Fg zHBW44d3PF?qDE?w$Zc9_CX1S)sAU;C&n;UyjTcoj7rzQOe=)kMwXAAic`B>f7oN)Y z?5j&<`}W19vIG0t!YD2VDT`Zm&ZD)B6^gL~5z~uLcEQGk;tQoKr))T2?IYK}TJ z(wjzgEXR4ltTSlVNS`W#SkD&R*G67e(i20zf!P8$B3x6*nKoIWiAMONYAoMyRMr`C zLf~ns7K`N-j3{;8u*QcGufqHJVFAYpO^111ja8)Zv4cykVz{PT(e-+YE+L7>&Y;;SL|e%qaWp$!c`4O~)RfpwDzs9Zf-w8>W7mRLY783MYQV3wN#fwAb$umH z(e;E;(nIo6&aB#d-x@?~a@s!a(Ub-2xn)G7OBsi>5a)vfDr(`8yO-d}sKUisN+_%y z9B%13ON9>RpYd#uDpI)l8FSU>AH!R``%OKJ^MCwk&jIBUu=T&g{$n}!$6sUtmTmu0 ztHS>!>_4i9|F-}5UuXZJEKfJtf1E7Fw;}6?-g?44g*1Nxh2<-Uq~hC@43d|;8%#tw zWZ~j^b>{}&v9fCoyon!*oUUkM>&_>;ivSUblnL-{jk+W5b?pVSP_U0kl@gjyLe#B^t?20>WpJE?|q7OXW6j{I@GtZ|;intX~6IY{HbwziD zXrQZsP(zo<^cI~w73>axAk+|0;|6xzrN474V}p73%c&cg*<&Z-OR}>ia!v~)yg!Gl zi9cWM-2NE*wlF^t3yFUO20ypKEyupC_O(OS?Uo`L57gQwaCOUffaa01uuL;5jArGT zIventaVl_5&%Ne|*<)5@oY#w$KSi%)8QU{_EnG2N)<=Sd1APWWQ;tG>!?C@duDCBb zkpZ?Zz?@&#J_b4EhSl6>{mzB66!RUR8!&$pCTno4ivc-iE(I!?KJZ)%7`ziC3Ur$j zQ5VPqk0)lp>bRhclf(+6zAe`)M}$^KcfE^Fhr5=GPVm5@Tw5R{t$TO8nm!;3QGMp# zOBCb+!@MjG#nmUz9eW~ zM5M!{>D5DLWm_o~n~o^v#gg1kRRy{DJYYo>n3@7WzVPSJMGcyMi+R+la9}&6S1}oget^8q6@D8Um6C~ZIzjrOubPi5-2LnVmSz{=%m`7Y1kX*O(tAg z2XY6C&4g%F3KxHVm(fQQc@t<%8bxg8{BOi6=Z+RBR@GQ60*E1p?X-=EFJl*oU$eV_?*otAHgHVE$vZEQq*}AmlSm#!zM9;rH%bBMS25mMJ0L* zOELjuZ&O&g38t?B(Xi~-8@_nRVlF62h{_?N7<@#;x=cbu%!-&T1pg|D1Wnx4OJK(W z8M8p~N|Un-$SCQZP8g^~piD>gep`vJl!49@o`7)X3bPX}0IZbBc*Yj!xLOVTDDm(d zM>sD1>vv*8phf3>Ye4S6dPGQ^h7WFlQ9ECo-SR`~*>`;Z!uAC;ea z)_bf#{DmV$c7GXPVCF0^9@egQCM?KmCO&s&l7C-NY1XmRv5Pu?OWk10AF`J^HaBw;?il4dc} zI<5mInT87{M&PF3a>(1QZtuKxE?}dsea=6Hmf54OZvTh}YuOx8nLU@hI%5*~7}I)A z*s<&Fp1t3TD+kZ@qJ-2%1%)(@P;Bx4%J|j+8ir$D-vaFTT;FG&;Q@ zM&xfy7jS?19i}BZ*T&T3mrcw!OuVt14E_F%C&Uka&1MxmgCGwDmGjKUbNtA`L^zZP zxG3xY1y1SLcpL)ErmcToAHH)a{=;)bqaDeFHFMj4{XIOWk3hN`vJuAI{U$4tSOGm} z$TF5zgy#^!J&q|q)P~12AVlEmEB%Tk4>67jJ>iHF{6sntm|FFj(usUN{(mZ`6%9;8 z_AX|n&nTpG3!YQr1^|XYdA|#SPh3+=nS)Bl`OeYxTn7Dh1V(v05sFPx*swJ!oAhsN z1bO_;*V8FJ%ow;u8JC!%@1OtQ&b|^qC}ZhVV#`mWEmyr*^XI&P|8E``sia~f2>s5Z z-7Jr73TetkI~HP8T}u79Rb)H6a!u9Mf_4)pD#boIC6*$c$#-h;qwFQ(dJs_w8X#(t z)AKk-lEj)k3~<82`2Hv-36aviU@{k(0!Co8r%REyL;oJb22<*aXciKAV6;uir%%&x zaBYgAx6V*%sl6vI{gz7*Tq3o#)W54WFcdc0cGY!QY1gBe-b(1Fd>~O+bN*^cLn`y& zMVhjzpsS?{0j?F^rSQki0`?pT99-Lx&@>tS6;uD#GlNN`&Y+EN)$xFD#3oPD!E_o+ zw5bd9PacCFw8TRM;QkNCF55IXh!FG;tfLB+nNHY zim49&kLE0$VZ;CXPG0w;8Px^pE&TMFlP*4NTet6{*tJBCtl@KZvM*W`Ez{R7J~PgP z_0$V@mQxCIg>S2HNn)^w7&v$4j)MgckA_>U-NZ=1&{2^h3_60r;;%hS5(fW)3m?4* zV1}@S6+<1F{vvqBM0zm2i`>9r8O1CGrm7WiamSx1%A%}c^vU7AFmH4Yr$Asmb}gTO zuKa>)MWPRJ|2NuEfM117@J5#qsMG%riSzIHKc@ZH>SpiD0)S=Of7M8%_5mk+1%KW^xc|G+aga6fuzsFjXl zkF^3g7*2d|;ssEJ6}>C@_kT?1_dZo+>}M@@<|0`A!mU1FCaTr#>K>1?D#H?Dm{mrB zM)~_#*IT{yT=X5?tJFqhPlL6Ns20dSvPEfmDlEY;-oC7g7*|>6BfKXYAnRL6u=z%fpv1Ra90& zw7*?FkcdRorT2@Q6;(f7|HEH}Yc}*T@@)t+JA*SA*Y0!(Qw!)`?(IkT(LnlOqG8wV z#Ga|iMZ+pBhMPY&K0MMU%xd`c3{CXrH)caf#%R(dZGwjv{eEvSavHsUzcs*=H%tpf zCreM;C$i8|O1XswE&SV3+OH2r-JU}c9W141KRGhkLp3hFKxgs)bGtI0j9|W=U13OG z8MN~Xb6W)yA`=w@mBeB{n32UGCK0(^+!4Y2#0?lh#u?vIE)&XYfx0~u62daOTCbFq zqX4yZ?n*aky)Uko za910nu9~jW&*Q6dz?VUXN|FYH7nnHuBd8afR}e8hujMO29>J^dxy|!4gUY(eo0B} zz-=6Q$dVQzsOn`CxNzgG?Pqyu`$ba(GZ35XrKICnJuE#Pn)C!en=hi6@yqdsO2PQ*XWOF3Dw=D`;3qPapW|~Kt95(lh`TcVQ$MXx z0!Tt7T`=^2d?4Y^DXis}pN&=4x}}!2z;G1w=g&2qZ0ZZnv*YlX5?l>TcDktC0TIUG z*X(VEKU9V?p44>Bbwn&&0{_H&xht={B1&_(4wo3lXnunz{{sqN113$U!=qz+XsJ5! z+^Ni(L(qKkUsLKYVR6Q87t@K%sLESKFFEgiCB!pPiKm;q5(&qoxvi{Eam42<47-h8 zsqNHEl!&PK;~SObM;OTJ`bA@MiWUtc>C%ww_iAWy#k++m%t_nl!{Xr8yXaGHUgonu zv+5!I_lF-)!+?m;S=r&;awd0+U{d67Hqf{I@^g;wXa5)56DO1Fhc?5o3~C7n{I`g? zg*l6O39VeTDqpl#9B)J;d`|iDB<9fQ;FG3t<2;hTcXA^6JhJq8)SnY@mvB7(oJXR8 zp5N@%imGRe>uar_pWG?+F{OPJ)s{3e*vh1EX}ygyRhJQ2>G*NAxg8_6OP!Z|e{~0) zgszscq)vg?O-_X~D{#Gvm6 z@u)(Hho_z^QvITm`Mtgt zcTRejSlSv4F8U*X5+0rlS^UVqg-oDdGd@h!f`k3U++6Oljw(?@XB zIRwGoJdi>`QG_m?A{4X`#E3F;&;VI-&+qVC6!n2`EyPYxEj|}7ntvJ zT1W~t&4&cIPfI1r$o^z8c2gjERp> zn$uqIj9}(@y5cWvLPPa=D+jU&kts2t+UDe9_!H26uRkmtV0dUR#f@dCgVklIJ-6UCof>1iABibr)m zG}Upj_?Eul!AEm_%#9)F#=z0$B)gLK;!6bkIk7e9q|CQB{yVnD|GHgZU%|}X@?p>( zwf+Zhm;WKVMc)KpeFsd!Wnu?J=1^n-qJGMK;PV(N&{6aaz%lCk2{D|v+YTwehJB)$ z)^J#6BGoNb`fWp{!3PK5Ce#H-jD%W+kt9Srz(EF1cbKPd>3Ne(@s4&bpJqcEsVQ;f zZO!TS9MpDxXb)P>u-|GmBhxHehqUh3FD}K&UAcc*sk|iNOgoG1u+Qa9ZZlfiY9Lco zaragaO6u?ZQmJHH=&RW{5P5L3W(DU^M6!~6%}480X+eVlhu)P%bRYbVZyw~MMBy3E zF_<$NLsEj`^?c<|0gCckkKxJ^gcus)4lXYKjLZsUDr>D-Af*V6pp+CsLAbKsWof#F zb2vDN1q%jHny1mV}a@1yg!FZF;V{IlHO}5I}h(6Be+Bj`|WWSQ+ z6DI2t-gkUJe@+{(O;4t_zw4uG=Qvn6lf~S#k%poHn2~CQ*pF?>6a}qj%e@+nI6i;) z;0tp{01;=UpQI7O0G47R^l^XrC~?;Lr5No9$(GK+n956cWj*;{5P9?A3sl_nG#aFb zW{vLZ=lx%PKIBGRg@1~>=K=iF?xE`Mn(MmSclGz){=uR4eUE)EN61nu!9Tr{{QKq6 zD;@s8Kz86Bxb~hMS{tcejVBpBJ4or-*xIw2cU?7m21_e=3_kHH`eFJDSQ`K9 zhaZGvp;RRoaveEHN*w143c-ss_8WyFuJ8?(z{2U2dRpTGb6ja1>8uO6PB5~nR5XK~ z`GqU7Gn=nfqHSW^tU#fHV+ex99eQlxJ_V95@=q7n+B|X2j=piHRZB--d3tP%%zk*o zYLYF>%|Bh-mD#O-LorASspq;O!aRpcoSo+bNAw(i^Y9YmOAe6EjsiyfdyZG_mvqgB zFW3zK1Z#Y)+CxA%$`TaIe)~<*2i3H|{80Txb}OGmNKIeyvF6)NJdsKD@l_7^j!T=L zNDj|DNocYi9B%UbP$%ZM=4fBEMa9PXQm)2vZD=eH6SM8f{3~(rva;vwI|rF^Ej^r( ztoX`7eWLW^phzTeZJ#V6jAhMS97{>?feJc|*lUx8@^#ls#SVzXNKN{No~eBX041Fu z`Gu+wZ>b`sKOR1%DO?k!>loU=p%|`Q6?Yhqrjo{L(4s}$IA~tD8v_1?cQVRnR5{L~ z#t4C|&wGYJyYNc7Jy|cO-hCMb#tNHVEknIab~zr$O#<=hMW@s3cP@dPu`T*Wcdsg^ zwy3Gq0(}s1_;}mhG-@$oMFOSFA&LhKkR9|dPJM_fjZtgBh!Yl!LZ!SGFeTMn?o`T# z=d^)Hvr9{MI629jnfP6@GS;Tx1eL(1jrqZ^QzM-}rJqwRue|!>hHz2rk7IWT! z(IAKYP0$=tr}?c%u?5Z*KtlMJVhfMr3Nf9{u83`U=TE0Xi9n$#xe+&#u}^^>F=ae? z5q)x}{se7S&|4^B*kcgGM!kmN!6zs~Q(h&n`a^lu=QU$`t6cCKwGla=<6>r@dnGQ4 zJ-+7tlWQK7;>r=r87xjW{ctnSHgd;WtXI*+{t0i1*bl~bciG7O=*`)6u$U>2fIo#S zT&#mJH(5tG+h#k`xM>Q=OR*o_bP&2h?^U_@nt`QO;qS8JmyJmnN9&Owmgiqd)tb&o3aXNi=O#D`>bR3MlD;ieBtEtz6Xfz6XO1+yDI zu%yKUal^RrM+_T`?FY2QbNc5>TG6L~OLrhlJ(7XdwXU!|TUk;0Ox%7y1X3N|`<2 zJw^o0i+-ndNy0b#3nS}=!ZTd5XMpF~tvkJbrlGRhZcql(i(#t-2%e2Dsp7K@bq0E! zL1vv|q9OKM5)UDw+y;UOIZ7E@>W{dbkvU+DYLHT7B3BFRS}z^MEXmvYW+pU5NX+g; zw}uTUj{<%)C6l>#=S0I44}4q+Bapz@>&^GA!KgiK@liUBdOwP?mIYWJ{&X&GqX-&x z0$@A+x`s}8e(Rn2?F|q0SWHZ_xcw9?i%%C=+B6vrQnwz}B9FBi?Z)48I8lh#{L@Wx zT}J=7Z4+s6vgW9?Wf?}Yo6<7E+%m}VEn~Hh(yaU28P)P5?sSw-;=20 ztQMSE3zl2d{Y#L0sOTl8%?ba7&Nwfqy7rqRvOiMspifH?U!YJxPy08l$WMuO_kF$7 zZsKck4V8w1^^4EQe1Fz2o0JiyS~;tCPAMYjFN{UFs+BkzgiFIgr%MhT2$zP#=!k~n zQ5~r``vS!m&WbUltlne}NgtDrc!*!llYE*Kz$@1WzZ9AY}jv- z+{hb)S}%kkBJkk%8h)1Yh5Y`U?WHMm$BX3>gt9A;-{+ZEdEETKD~|=gGo+fyCofp} zAv(Jtvo9+Cj1z%*l}FAQh%zu0mXcGDcKAtP6lT=hP%*1!R@M4?wU~Q1W*u0Bk}}{; zm8Yr|qV8U|^OLZJ&6uL+bRhn#_@rKoJLFbDtOt@@bR0H@=XJfdcJduZLrCoyOIbKR zCfOoz^{4^Pp(3@yqw&+Avhd@!c@qA-19CzVoi1Yh#xA;-8uBdt-FCe&}JB=lBW8Lvp(rmEgmt`Xw*pd~F! zv!4|)yqa^cU#a4el(dU9O&>}1q#`D++$C3kz|lE(Z!k#3iaX_OvXC@icxn~C=i(-k z*c-0Mt5m8ruF$LO+l(ms5gZP#K#%N1uRUTHs_;ZNO$ z(>SYl-?qrCKPqXQzLf=dl{3mZ-FIBSCII+TplX8QjB^A-2}tgIQ}GC&LL(qrPVJf8;a<0iYv!I%NHLS3KYT!o|EBv zn@8HE(Tm?FjdSEgU$fJj51)3ojThQ;xG0QTd>}MJp_s}Vblm76!Am!qKBU=4#Rr=7 zBFCpmvR)CkN2ZB?POQ{1+t38kxMi+pn=34=$dD|3g^@~)RmKlunc*lFhdssQxA!9g z$gfhCy|=vEsTDCF;&Uycxr%WMmd^>b*&`(wF~oc#j7<84zu7+AOnw@#9$u#ok1mSZ|p8+gaTPo=0bHDVL#;dew-R;7PgX zbnt=;r(8+X#%(q;;QfpV0Ln*Z^N!EMFEd$!k~E~6ugvfz)oT47SE4{8vX<2vuNGrz zF^=|F^fVClWvgI17Kr|)YAdk?HAbxbJd6KiLI|~G`gzsh^FI!9!|*j42hycNivMxL z-5c~H=6=v*OJ^aSsw%_CunROJaD_Goqn}^`b!hDlzEZYEe??MK0p%ooa}x4DC9D2sB69$Zq}>h$Ym7^PnExY1@i0g?zI&KQQ{y?(STwi6Xn6@p|>$oAhVJR07awB;`>Q=gYc^rOgynN%|70^+@Q5Nf61F zdZGR%!B~ROX*XNO*?c;ekne3XcN{$#(S;4~bM!7me+eJvv^^8O7UqBAmjY65A&}ok z^v?b!uAi~Kw%a%t;snl9<1Mek>0;{P8D_i-w{YB^~)E=_s&b2r~4pOam=4 z(}5IC<%pDHBm*$6`xv`(L!$3j_O>4noKxOgWNg7=x zl)EDkJc`>bdTp0BSPZq6$+*Zokn?piDDsvTvGdx$0?h6lC53Bp(n0kv8J!-hp1?if ztuTYU=)UXrK6DF(imkNX6%QSSLe*9-UKdDtRw&dmKnjIDTd7FE#93(XU18tai<5Q- zIIGiajam)S*uiGN!l4zW@yG8?E+II;mqMIk{Fo>!Z|&^FNq({94}^&VBL8^8dQ zb*kkg7OK{{rhvZv`rDTCq288l?Qe!GR1dcF_%GZnN>)1`=u{F#A=3dQnbf-)L@kCw z&XoqTuY*v32A5j#kzU;GJDpy=*=j}r+I$1RsQ30QOyNkwP%{8JeY!p3?Mz5Y3UJ>B zsEJex@W=)TS0H}RkYK>NEnxKoT+IqMXr1?9kxuIuol#YS?r#LGNzfxJs9JLYhR2I# z`l1D>#QxrRagp4w>T>9%+r#x{jbSNLRGrpG2 zjIr;SkA?JO7@628D>HvG_2l0mDe*szMppOy)P)jlx4F~H^KQSr%)Uj6Wc(xl4}Q0gg1mjVZk_e~@w zJ^|4T>an8+m;5>avlhMNX`mp+SqCpPI%yMQELX-HY?{2dZT1g8q*eG)YCY_&i5Lbs z`}7jsH#;>D)73e{3AdhJf9tJ`2r7teSX6!dE` z8Jdl|DLC;?2r)_N_VhmMUDkF-g1=Z9O!o3_THg)(E6HS17CvoK^_xh1SEf{n-94lM zmv|jYu7uviYYS`EC+*m}SV~4M$?{c<-7C$}C}MiT{wrPoG;DQ&iT1ju?YF};eTiKG zqHl}n5SE=wpXOYYD`0V|=&X!Q5!+_Q3`&CKus^1p^QFuswcHfr#E?4T%(aiv%4>7# zP^LM~s&46*eLJg5ij6{wnrMd4IGUtaN+%v2$v2~;TnS3FpoWzrEFOYofi%aA-k(3L8> zqW$EE*U)7n%U}{J7cK^ATRX>rYd-mAQkdwyDO7BxGrg@$3QLSfpLA_n&HGj4s^!I1+k~sz&qAdL zMlsHG8P3>6|Mv|`p3s(lkUr$d+66_EPaFb~t|?mb0wMHgodr6(InI*&axtup=d*aT z{SxkJva_!)LUMbf_?0eBC8bTrQe1tXJeAa0xw5Y`&5Jm2gbi%$(#Bbqvc`Q(Pt2~Q zoEy@Hcf*MlZy?VlDBaND>x(;D|DHzS8iM}^2pq({%gamV&iT{-jW^=~gD7CNOd~83 zoWCv#lf9IF=e;9q_alQxSydGRxODu;+mz)Y>opf|(Hvqu_h-u~c_Dei@;N7_oJc0# zwY#3KVt2r3JE?GPo1@SRGcr;)r?~o5UZjbsxvL3tfo!H6yr2uj}SMHk})ARSaXd| z+Q*NyH5rhj(cP^#XXsr(20qt*5Uxa6c#05zi}|fw9`;R{B!nAWss@}&XG?JgL@Ykb`Z!Ch1XRDJ z+39MX*~s2?EV@uDaEL)$gevj6q)$!a(FG@6{Se_{fB1pE{+U%@9cX@TY8BHSz9|%~ zSdhq-O;*#SFX>Cq=vmw-=EPAlyZ{Zy!_sl&513#0e~k2>+Uv+kuxL=)^C)n5h8U{r ziL9EQ=qi}QYeV?zmP3Bpqh#aN2M)a1wIdMTumG(?{_Ig!K->hm)P{Gf8#^P4Pk z3B17!n{vn({U)$qKKI-dI#~rym8b&sr*Za{2Q#qvU~5!o(%d((luoC%&9s)6(xN0p zTWRg3$t)5_ihpXhLr}KuyJq@+oDQt+ikiP&EZ$0G`G`29@Iqd`T1!hkKC z*Y`s1>w8)#NE}Q|F92gPTT+fZ$-Dk{*_VBLRWZHaZMgKSpc8h3{HIW_Et~FU;m=@O zB|V%|o}2qWA0q}?leVO}8$|l0P2L8|rGT6btjem4h%_RUg$jDW-G?H7A%48agQ>^+ zn4s%8sOCLkuJRN7W*+Tdr`=9Et?t{=8F6yXcZ)R)I2e^U?LP302-++S^}G>dRGx8+JeoDN}3#x6FDdKcHB7KfBK=|GdF{jboJ zT61bXKD~g_**&)E(M!IUgefERo&P2+_n`5^!$#NXrz$Y~n8wCV++tq7(MJ%>JnCHMS5#SOm_CnBwNxDPcREX8EN#*_kWz;Ywf)Ge(zocT^EK9G~ft;$wwBR zgrf`V_&!InDfbG_Qs?$AjpNI(Vu_uAl?6dKP!vk1^|XM;>QT&c016m>28zn1SPm0y zgX5+K;y-Ch7g47FjFe4!{I+EEPQRg7mp^k?ADY#NkE`C)dawV?UB6*8FMsB(uGQ9l z>Uz5L>ccMdcBtxWKXbKC@NKht`%%>~{kBA4{LDA{W+Zdr=+)21G~Y0rZ#;7N4RiR3 z>RJG*D634?eR{NEhhuA>y3#i5(-^JLeFu?X!#$awGqW=#TjpG!{g}aG zpXm%P;;%8pJW~p~%X3LY>&2VzV?4L03kW5a=yf^|PUmEpTSaY0?ME(Z`k)i@hsTk! zCK(t%bS|JBgQugvsyDrYNO!NkInXrxRc|_fy=Yya+SM!~hx7iRO)yM4sWT0V^u;Gl zd0lGvx(Os7;MUc$RyLXtU~$~IPYvuAJUq`$uij}58}05}0|{SeYc)8(qMn;gLs6#< zNc)|}`3NdA^Pd42>!Flv6pWN_vKCbn?TjD!C?b*pjUSsilXxC7G;Q=qgm>Yy4@z0p z{u|L1&$VQalByC62)TGu{xeN7|1Kln{qUm8LvE_qa2^Az`+iVAr&mgEwVH~MLOhk* z#B-;Z&~0@Kw-xLS54v~jk8j0mBt?Gr=d9K04|?akDyCXmFEQ(q8J;LrOF%u4CTrr} zq29@;&>4P!rGT>o{{yt}SF{DHrpJqnMT}Grk{U8(upHgn?x>njJ;!6RsU)%#Amgpx!T28-u-!F(&c(=u6scs;ddv5wb8_*PU3p^)21WImJJsHM z>G?KfGP#vXS_O^ovikJ;HKR0oOElKb!7CYsjg7IB7 zY&K?-ub&Js9Y6*#T z2|e=A!5l~j$+-+$e8sDCJDvH0`M?JRJ-aM~U(CC{rle~aknBOk-TCANA>Z@7ZFjju zb%lsPe0S@v{)G9r=4fRgvO?~R&HR2_PaqY>ETqD-CX=NHqm+4KVx#X&i=-2)EYBF^ zkkfT|i&@avauK4L9N(Ec4EQ5XO)vwz=jZL7+Pfj`bd3Mz&v}|UpUiGV&?9>I4j23f z{suIQ`SgKjtaBfD-rJ8J2MD~W5BJLlXs^vMTPpDvWEzq+P(8&r&^lz7x`?aDajcif z5ir0U525x`Yf6cEP}Cj>X)si(;GBf1CT)< zF?xwyj-6|ZH_CM4ydZpYf9!;TJ98%P$`w9bD*6(aPk|?&x4SggCE+l}gJV89?W%%B zL6pY^<;DX=N+->us-5-*jg|xF_uv9=23iUK=JXZycf0|ryt@v zO=S6S<7o6RxJ8>PS0Y7qKbpxnbVOo?#oyqPjhAa=IrYXnuF?NQnIgCloc*eKGT>NewP5^~%;jH5K4SQzDy7Dms2utitbYyVy*P*zj|SC(<55>bVgU)d-p=0IsT zgk%}ZwUppU(daqZ$rCp7IpWrxT5tP0d*x5U#^XiwNmCjaNtCPAhq0s)*`{{|yZ;h)pRo$hp@ictoWr!D*sMoPsp%wz~G?nrxswE$Ir z!BI*4X{c(1sO9ZpcE5gKTR-ar`3O_Ja21~E$XQCQ?>vEK9 zx@{IpyL;N>(kiYutTcW;+&^iL9MrfWy_BLSj3S-%hx>TaiymT1%e`7ja?r2T zE>PWuX3>%QTKs!j>u}ZWw7PT*54FaOi7w@iaGltoX5uzWr}Vzy(T5WOJ4`L5$jqIj zE=oOvH_6a?pr}VCaUcB~+~Aqiry?Y2G2mzqNi3AU8Ho8R40Y!W@u?(T-U3W?F=3RW zR>JS_kFd)lpkREJ1cHwd(CVHBU;>Gc5tMoi(4o{v6DVXBpfK*MCyY#mB3j7HFe~@F z!9AlXC7GH>$!5!7K}x9k4JmxKxNhI$+WcELWLKUyXW=c(tx7O}lPsKOC*I0M9W&|a zs4^DLy%lEOq0JpGNJXfU+o1i++`EJR$UxmBeo|czkgu1d|5Hsws%>L9{#+`mgozCw zS{c>ls4!#XM0e|ReOtI%Uth!bu}7*oOrik&3zd$YysF$_Vn!C{nlk9R(4F6djCg$w zqef*PE-f`F6=m@w9Z4VVXsDu~XC3X>(!7#@YB*A7;)P@OBDkN3=x!5QY(Q9o6NZkj&pOQSyW2L>&39{tMVd>6l?16oQUrl|M6%USfEI8J?*>{5a>| zp>kP(kW7r_pW%N#GxDJ-R!UoBZ>a9n-mY*x6+F~pLrBJ_c3UVjb9pe4{K&|@SY}gy z=C7nO79GqV#OqkNUZT}9qc+5&qIbUvycvMN-R$N;R|7Cly{}0J;oM2aOcRJ!5RgS4 z&p)P`z}p}&H*dW1CU6Nh@4jR_tmPxMnR&tZdVzoA&)q2ufK=^wz)tZU&!0zU-0hv> zZv44*C7Cbron|FU1%?tAB^lznSf`>a%CL|OAi3TyGBwk6cCB;gL%U1aCg~iO9))lX zCXN{1F4oftyIIg<0NZ!7jq)=5Rd=GoKD-5J@M)X3~}lMXO%}az9pa}0%C;mu1K}`+ahdtUpy|t zH+lhY-H*NHilNSjeaDL2D1pCWh9x0WNSfMY8lfwk=o#0lkk?i2%~q%W6V-b=sCP$6 zL$SQN_iy^Lq~4T;(mVD@r0cxXpPmdZ6MtV)&g3B61pab`@eO#gLm#hu5BxDxzW*^U zY0)CWb%CL#4Btv!VSXYKwTv#=MBxj|-2I&@q0tFRe-rV9>R#jfB38Nu@s2pV6l%F( z$W5bz>a9vilS*TV^Ez}+I^@&M z$rdC5qxgc##kI>cD$*)}ydZ=OrPD4&X}zMWoG+uU#=;zYR;xD#Od_b=&6N~Uip3??DEJ#qj$YYv>o@Z#K7KG4eL6;gFZT580*k_8uQ za|7tW0F9qFg2_jf87bmq{r2FmPOnR5d-c%>-wb2D9n~A}F8U?y?Obd?T+IMYLqa$g z-9SC$1IKRgU4SR?o%;~7!^892|BCVnnWhvn70=-TULFjq7G$=KdZ#|nuMp-H@Ek5Z zyKtN};6ApP?mS}4*?mZmP2pvg676wxT9C{q?c^va;-;60TD7EUf2CdG37p2gju6+> zR>y(!0u<)dOVtyyYb%9ltU=@$)&{ZQkUxB?OmUB`|LQPOT#I_R$8jaC!!--lkkjkO zT2B_1a=lyq=f(L+PZ?L+76(n^QIF?*fFo~E*0r#3;hfzB(x+|)KlzIiGB1o7aEC!z z65{}zupQAPHZM|?zTkeFiINO|b74buZ-htp7XG>ecmA=ST`j)B@Y4}LWfCH6LI~FB zP48n0y2N6~aTuM-Kw_+mi6+gE8Q8i05jnhOuCyWJurlS;P@s*0#a=q!b9YSzY0+H| z2E|$RrRV5_wF`@r8!8w=13PkMeI&fzxpk&oVsAqUt zHXvCMV+Km~DP8|cN})XU(OhgHI%<0zd0jvf-=#dtaOowbE^H8bgaY{-e@6XDdEj_m zC_-muu2R#|priL&teW+YhT*POfq%7Lc|6HiqCxqJ@tT6$kQEB_O}mcZ4Pfq%>F%N? zU?|r@YCNf}|A@{gbdb0>J#7u>q$;z6Y7KS+nsjpgGW$VN>G-m9CZwc5P{%k#CFmE@&7B&w`-(Y1nxFjHSC__04Hb+|7z>Mi6n8YFZ?z_h}gUVwd37x4L$d+*O9)vfz|1+KCyUMp`A(jxy*V`SmzZ`a(v_txXUK1|WmrljLPb#Gt zhMJB7$Cw90^d=*$V0a$A#LVHx_^uRI0A!ItVTeZBJ8?Fd8*Pk?!$GP@l60)jbPs<)7)~uluDm zr%i&5?0A17szX7jaq<|-tLPCd;9wDBOOdRyMojEo?lbF%^;>$>n!5kmvGU_O6ECaS zbcywbx@+K$xN^rI7uVOoEoC-zi>a&jCg4uTYmBq2MN3c#?1A%6AILC;wwF)Aot3g& z8O?|g8~l|;+~H?#_)%`P=t-TdFygN4aNpOyjoyyxC-{(ug`~LF3eSf|pU<+MLtgEQ7`#>&2X2Y%kf~|e zN`1i|UE&OCc`0SVz$^fqmiQn>1?$(-vquLLvGk(RY1Ie%_mTG9c-i7HW$BffKmT+Z z9eQPMcSrbwqrKk95(*nV-8S^9t;P_=gMz{vnfre@JKGA2J#EhinG^1#fAO=bM@bW`5o&tRA#+U<&3& zqt%HtvS%^oC>)5suM zE}b$_KzcdG2mmko9H;@j;snPYubsH&Kk?1e=;FV$J_zZ7r$w>#z4SFY{}>4-Y78rc za35RIX#!jjM0xMm5SrqFN#di>CM#y4WBNd=#}tjc^!Q(q1cAVhRnwl2E`TYMspi~U zv0Klbc<2>y+8DzBE+_^J4DfxY-USR&D<=cqjV{DXZfkT#bIj4S-L<^e(iRuF#$lLsS>M4CUO&VVRE;=n9rb7RPw=+x}2*O2uIFt%byl(IW1oT8! zJ*rxo*(HThWbULX*yVL)C{D2G8Lk|M;0JsBi>!yxDo!1hVnGa@3BVB+qmLRs_FL@p zX?-}l=r@g7JhO_nVsJth9b7hg9ax6OyP?zzi;|IEf@h}w!N7oodkUBH764XF6nYWj zFo0v-0O1GVD;E9!1^kELkIBhptKVhdPF1vCyvz|W4z>d7?D6GCQLy^^4p63tI zl4f-)UVQgzR}XM`7R%vLPNN;OrL^#G=yR(EtOssPL~X=tXY@^b5Z9?y)s2O_)SGDP zZ8U9-G_9>@RGbwaQBr#p1u+=|5t`9IS_Tt^kW7aDQ3<3l7_T4ED6JTxukG%8 zp-1|gxmnYLP)5US>%(l@hS|0c(+ISg46`X~5AC%DHP**uD2y-Kv-K3VhzpE?UW*Lo zdR8Bv5j9tIYlCv4xSCyD%P!uF7eDQ_FE4YebC#v^4U?-FrN19baa7~L8s*s)FNoF2 z&4p~$EHAIBux#gdDOD~+9m!LMI!_r|c!m0y;1wIk}*&@D=NV1XLH_uizbc&;^Cdb z8P(q&qm#`fSS;~^fwzHAQ+KXBPg8Q(rA}{dUyMDcUNw>>h`dfB`rKjlP-Yo~?)62C zJ#rkNfOptoaslV(5Kowixu%%NHASxmaL3{D)5Nfwrxiov#Pj;#9bKFTLaHpYnx+j) zgHi!h-^6lWMDHE#n>qSHH3kjF9g{5{x;JAvLbz?T}$yItB0>`lYD@)Tsmg9ITU3Q!GRUQeg=Yl6T!X}!CnS}Jrlv66+tZn zLCr)^vm&TwAgG!Ms#XM*3UF_V|0tvWkx~E1TK{E6{g+1l zm)81+8TAj1`iIu~2O0HEB0R9x-_NLT(jbr^^1vjD3@VgG$Q}vFUK)}NO030^)Lu)H zcx@!>w&mqkX=%_Iyn#-c!l`m~>ynK~WtRej$~Xx;9yEnz^H!Ku6Rn7@c>v9L7?1fU z43&s(bk>+v(jkbyQ*mip`UGj;oYUbsKlr2I;<7waY@s7%zcpz0nxqvK&rmv=M&}&> zG>zOmvnV#+mX-!N^t>e=#AamD>~||X?qzVk=yNIKW$x+EPn9Q|&{V3i7>e^x|a^~7&IPa(vzS1^NvxpoLx_1sn26rf)a7r?R zWX1q3FhbUr!@kHa=+CLy*rP2Dtq59r6Oq*}GohS*{cTGa9P&pyW`*eY;4=IJJqoG4 zn2IT}wK*gwQJ_%E*4@gLHNi%xHW#Vvd(99qU{a$n?c6Y9 zX0`~T-WVD^5^SJPK`A;aWxr{56WypvuUvW=x1levoVaaSgsRG)EsKI0ok7*~V@vXp ztMJZUI)Fp#%zz#UN-+u$=tZ`O1g9Z>60WbphtOGFO&wICh*Pk7=%}Jk-h8qM%JS#L z`{Ymbgl$|Y1vrlt`?BK}Km7@6FL^Dgc1IT*k)?oLE!;qQBK;F1-0XY%IEd=uKKxfG zzD^qF2m2sae=ehj3Vyvpv95lYs0GtrHEO+#*Q(YJY_)27tt0pyYind}Yp-mo=2*Ok zuWYprHuP{*wbeS@RO`rA>t%KitM!+*T1T7auv$q}JIYh7W^3(LcD23webv_5=4v&i zYTHYa8rpZfZ*`l!0ixK0lby(}g5im+WVhFN_3Ci6sdm~jQSB369e!qDh)D7B@)B#~ zKdRoyQ++S3`pb=d-%lYHYJ7N^qw#~(#)p5~)cVWRnZrfd*!iRMiQliB3@TMc49!-j zlSvE+z-e<*O(BN&ZMYq0SFXlrA*%eJ^|u%GjkQhs-ml8q=dD4fH`-L&O{qQXw9lPf zC|<@k*nvbrFO`=|vj0aEJRsZpAO0#_v!UwKgJnn3aCcaCj zh7CwaU`yh_&tp%;uF@wU1)mi35{Z;yiS#wMOkp9mVgMrlblml75kbUONVXu9#0dIVM!L~AO%w$x&%~w1~~R=WcRGst>vj(6LqPwIUA^Kz#jbgBgMDCFc+0$3*2u6SL4CN$9RRK zh!~qdD|^@1C7!TA^A1v()>-DVcZ4QaEW1j`ZigHV+WiiCWi?-Gi*DO!80NuqrvCh+ zzK651!+G>B4{pRNW-!95r(WO80CcXR@}WP;ivBll7W@?J_NP|GbPIwTXDzKz{e9@` zeAlk0m!M=(1AmUBYF+KFv_|T~Q$4&;Mic6#`Zxz#VlB>xUS|VVVXFavXe%E%d~_Fg zltPckHN1TY{V{NAzEio~h6>(`KBS+#z`uSF{+;ehM3N0%bf9DHYy6GzaxnPubg}rz zZWfCPxrtt*Eh2iL^12J=`Qky2hq2rq1JO;m{U5-E@VDp$Hhb;*QyD6U+Qn)-NF(Ro zB2tTd;eruK^ARlvGmewne3aRFyN*)qnfr6vx=lI7a#)Qmbc(&MP##Kts?D(6AO z(IVL23|JdW%Z4?0ng%QcHRwuI|G7MDBm*X$9S-*SbM~&=`_L6Ex)?{J=WLS)mx3{) zssT+2knuVQygB;XQxuz&HRJhn?OG^K4Ipcok_)c_op;#`)4@`dWQ@?n@giM$$uztP zGcAEl_h3zt4SxGgCSv@~m?rb6F{W1}EK5YbicO+{K^NVWsS+0Hsf{)VNX`>4oQ5Pl z(R0)|sl-IIneUVHIC?T-zqh2nO31kdW-UNHGj+Yp!#21RU>_@=tUAuGMB-$3>0ZI+ zUip+_^whtn4IV9@wtp#>fxa2*&G)Urs6FJi&pbx3`sOr-=XKqGGb-T;@_-N8i>sBh zdgnB)?DV43Nh`~&NHw`Rs6KnhEpRZ%s`+br^F2aW91WQrKSRL`q#5&2g%Ldu-h?^PJPR<(xZDsMNQ|}I1^!SLe z0Qm>$SgmD)da9RSYVYe8m(p7FzB1$+bwoc)yHRPL*ksQPGfcw`?!9OAQKLedg$Z~yVZ54_>h6PYX|pTbBIcTf^yi8QU$ z51FFQiA{c~F=nBK6NgU33Mq6omLLK?g+@4T5q*LV8bF1ztnj+w!PqGu3fj5g*+HSS zZ_r>CpTh2f1={?qVkzD+$ori0C(5eJgUEz2v;PY1W=4I0{ z5blIJFuMVD7l;{wY`&AwiDN$))dOGrBJo}9CZVKLt+}MW=yQ%Z|FYZAIT=kY zauR_u4h}_xqWzfsK*l`s;4y6~1RJVg@B<$NZ2;xc)Xic=8L`NBfe4ULsNP_NqslKl zJK}7KS_JZvC+VBV{nsI@Cgf2%WX*s?%|sgHo&kxO7$ZpS!UgjTJCH6mzQ{d=Q_tq> z*_9XA)4tMFib*sVgWjK`Zu}y%2a^XAW!2d-^S%Oiuc0Bf`uVCVg<#Ua;6S*&1~)5; zPg_(JE31iImR!ieOhPMvzV?heGB)}hHCoA9RMq*f*(0LFJ^YK(1yO>hXqvQ(K!T*+ zus`+xBn=MWUxJbVp!+AOPQ%&fdr73UiuFk-zpy0@j0lDsi=|9T0kgi1mk-2l=zWE@ zti$Q1tcNOj9-~X6Flw|U@ZyUTPa7d!-2@*o6=mqy5fN&1=Mt4{-NuT>r`KEAqhQbG zz;K_bYJ!@Yl{aPW#@R0X|B6i8Q|8(62yfuQs63wWgQv<9QF>Hjh)|eTJ#Zb? zB3#K(!0600h3$c|B+tb+bN_*P;DW{aiX4a72ajvrP$L%Q8T3FHct|uPa`r(tu2nWI z`b4>9u6TbjeIIu}*OF~98iSSuQA#D0+nXyf2dy5+Rf8xmA~iB$BJq%s#|!rD0y-#j z2m)>T6ATcyxW4A&%g>p zcK)G_V>Ee=uN;Y`zx8}DfGM<@AmrU1}+Yf_R<`fKM&W}*Z$b|<|ocR;>^3+Z?zh*r=V+Cw5C@!V&8!C^TAy5w>H2+YPM({P@jw|mjP zM^fkgg9+q&-sl}&KO61sOLlvwFLUUx)-q~1FzK;B@g`JVW?;k0E^dwKG>rx(u}QSf z1dxMqKq)WUgrbiX4L}rHO;nCl1j^t$agRu>FGK1P<(m)?2Z_*c3|gp8$VG9n}OJJ;>4EMKD&@$8w!`i#bd{A)LL6lnTb3Wb8q_BlsXYp0^ebr^l&B9 zHgRV!k;ji|T^G8WqA_uL__ao_`<@ct@o-~r)XRDLdz*?*#??7!jf6)%9@zL_b1p+o zR6tXnDIOXN{-lNKcXf~^CpVHJaQ?900;9)s1+IY1JsJL3n@f>`!I(m%fM}}Hmy8Lv z2k;`#smEtlNetQ3X^m#LX*PLY8LD=ir-8lNNqdwRHv!AIw?Kp2;S%^WoW~gaZY>A|?`nXJ;?W%OxYG5$73FTcJYwqArp@_F^_uCT<;M zX}XKaDcNh*<{}hk#-4Z}&GEBP@EnlMRy#X81b(u{hvPrtA$W~CT|D4{;?Mz6#*)PT zpl-xb??Vr9(V^bQnQdM6H4~%6O4p*LHgjGce_?)FBNB{C5#xp*RjOeOh`a!_-uPp< zo`upXH&-n!y+c&-cD11*?%EITN3S>Uxbqz)yVu#ZcPGp-#fH6u8?H^6kCEiAJ<{+- zvS1X44>MSuhfCNrtBCT$`i7I7(4#5>~1jbz@2Lc)yBe%=-m)^agL`xcp$V6u}I z#U(Yajfor;f4flvCx!y&E}j7l-ISIp7i4BG=d3K;D*XmAc+A0pB3$S~7an#J=}y!0 zbX)nMN?(ntqd1~&^TjwDT~CH5HI11PK2x40T1zM26~c*ziwXkNTxdb`WgUY2!;>)+mzAP4d1T5?=&C^7?^I?7#hw{3&)=BY zLr;43K{IZ>^Z1uxC4!h8N^1?XdOGOl=r}ft_dF_sJ zZxr(m3V5MBqFDYq-2FC#^NWDXusTvlO**R9`*x#+NJj0qz3!fK@TyWRRPL)UE0fKD zPK|?loYq!kVuR#G@oM-^saAIJy^O4E)Vcj6D+u+XTTIL)d2`Y6%q(U5SXTOKhd0Q1 z+drngcApG-^=6}YuBvEtg!Wt1vCgP8C@nm$no}!T4}W7DXz#tpHd8L{sbQLO83`3p z1mpRo(dg0R-Vp5j$=~PC=uhQt9DDrWY>Y_6xZW#nD)gJ4A0U&%#6#;bSCD!4@@6Qm z<9K&id2r(1;haES?b_*LOmO?jn?Vrb!w=`)P5@we}Vp6R!s{r#A=*sDSg*LOhyB zxsDE`?(M~V8{@E9CO{tRJm-n|KCeAOsaGgYjE)$Z?IlWl!laBZ@PD`;3H)U^@c3mm z1 z0UbT>A148;I$$LNO!YodJ;Y#hqJvYOp*Mz_n+{ADra6EGa8Cn_EDS7w`x;>8s19^M zZB#-X)%ET%Q$4H}xux?3QC3IaS!^=2b^n1?_?n3GVk7uIpP`I((w0@TuCT6@05#1k z>@%Mje<^kD6LVTho&Cf*l~U(FvGygk0;fLb4y2=`xKs3OwqB`c2iP}Kt4=DrXpL|h zZ+$68DtfGNjT-g_+D(=Mz48}-TVJ6@E~-2F8f2V%W6+YrP*VZ)ZGL2sc`UX<{JozC zft<|T)Hq(^`oiOIMZrQJk(t6&6=T}#;EtQklH3A}741{_BPP9dV0-*w7Nb`f0re~Q zhZbN;L}G!BBj!jcFZU);LTQDpT*pAq;Z}z~qudN%l6fjVNfeeU+1kcFd^W5cpJ+jA z>CYQ3H9lt59<^);yq9y`94V;T#Gp5kD>rHSaO-(qnM;kNi6UJv zh`Hg;&Q9b+@;iIIm0~WS<8d;%O~jcvVMw_r+ zS3N4|$<)4VI-WioUxz<%T`b=I`NPF1@bQXt>#lf)Z+~Sbecxfj+r@f1fs256Ky>jU zvsmBUve(;1iv_Teg-LsLz)|4ky1?I|&1uI?TD_+5X(S@Zr}NassSk&(0nB8l4aHwsVZbTRJ ztw1jgOfM@bKzjzzo*ii40NMv2VFtpIum`X4y9R!c1%6P8fgf0><>i4fm;)FLvp^la z)S-@ET2NuMs|W~x?S@`3@xnMPca(@gBPi+mty~A6t>+)-i@W(V9zWM+V5`v++Sfk& z%QIy^n_wIGj6rN8+jtf(hFFtA)r!CMy9&>{kUPS@1d2B1U+BnHsGqY4tBm9iG7Cj^ zX_j7R0QVb1XZTaM0bp9%_sxkhpR<2O$qRAQMu7v4b7Lt*kse~<>cuf!FeKcZ!;LaS z+mU|8kjY=|-&ViZ>9Fl68llS{wYR=r6P5_g?)amFuaGt>l+>c~b8P=8-YK86VN*`q zMIX&1n=;ircU48LC?0NT7|8vYR|~vM2HUf%#r<}Lzf_We1Z#Ik^*`c+qVRG>n-Y5a z#N2SvBj^%$66UhOTnXWh+0J}1-~KNzSj3xwTdzuRxNp+F5I z!?{2!t{e9TzdZBj+l22H`Zx80XS}k=*Ps&YL@u~bOHzt-MseftACI3XXYzEq4sU6m z$Vx>T%c4CcyIH$|3loIa&G_KESO>yIoG%m4%M)2EBPyZbMk_@1I4Ap_k|ovFXB9%m zLL4AzCdfm^L5>Gx0xnpLDQL@73S1Xh)F+Q9arcEhJOI=a&zynxC$v0`@(c2;9%SbA zpzOVbMF=TE1VG3oR8$w?8znvfqXn60Az1+(PGWW7)6MD@l}}Wg6y34F&2QLq_*3{J za5qUVOOnH+a0SLKme^$e6%HAu=$JyNgkzcuh4N<;`!6a~3W9F`xLCTs zuJK$LTsECvzy8;Y7EdjvTwmh_!;$3G+=EDBg0Yda5J!cX>^X`be^ejDEqI&lQ=*s! zrn(phW8v3B@x)C!cq7Lp9dopDyb*;MlmQbQi9Vp(&6zGrWyU>-$XQ5GKvm`EG$w?J ze4pBvA6T`-zNcL{xTJh-eBkZBwW5Fm^~O8y=)$)l`C}w_QnbMbjrW5VCe-SJlyI5G znPQY6)c^w!df-AZxTI#Zi$5QnrlNw%;c5~am;4eNmp-QSUgLUWJQ;jyzRc{kXhstQ zOv#HM=3>{#E&7#D_0gc-9iF#GKw~3d)*({ZH#yny;!{bS?`V{_x=#k*CMPL4*dEFt z>cVZ8=oIBmvUn-W9jpcArptO+mrcXafm6}(L-GjJp4zbCRg6bKCx8GrAAm9>o1duz z$|*LKE{rB(uB?$Fu4s(tD|%6e-7Mk(N&7KqPHetgpD}s;R3E1c`Y490qR!wy2?wJW z>S|QJ!`7z^tp?lpjZccKfvKe&%wvnqNpP8nir3-y2E(OJE!9epNAf(>FRqhtMT=G5 zGTH6wVd=qY>7USn$E?ixlvpZt5EC)NrQ!ZqtstDK4t-`X=s(B8f{c>EavxkeO3?(< z3qb_q&KeXRJCg>ECuxARQ&+4x;S_Ja zD}KtsSMq5srW}R=048{V+eE%}*2w>be_-wX<1uWtZfe*t7 z$<{%}!_<+|%~tzdO193SrAUBPq|w|EXPcW%@`Uj;DV#!a%JelweyRO?lfpSIe2y8G zf&s7u*gu$f8%;n2YLgA>2^nb+B`Xsw;#jl{+$AeZj4|WK9Cj$Kpj3n|oa%x_m+@}nGA;qcSyT%VOX{GN_evd6geS^^AO6)tkmwdM=CkZ zZ|d{g-be;$JcaGi7E%>+HIdIu8rtO(B>b4x02pwcC{)Z|>nUFF08yqRjC0}?=YJL&uRFsKy z#f==VnNBV1;q zkVr~YLtQqBU+x5L$DF7~MeGn2k&p;bU}W%MKrP(8jQF9Fd* zwor!12^O8Za>;owSZUo-jwr3+(5a}WCcW+(wq3pGn0>rJwjv_b2AS>S-CA!S7EUy8 zZ(sQ5BRNUg7=MUU0BKNt=SIE+&gW8q3z{)9NJm-%Ewi`rQ6|==kG9;}#%V^P?sN7p z`++p(P*$NwYoVqRO(6t3Kl9`oRyHRd5_;84?iy-9#*yp-fYUE^u3X;eO5?nu-|dhOX%M7|WIa9o(0wHeE&5^EuC&%C7vI zZPW(A>7YC7>Aq!n@sdmBe8736fYu_bC?`MdE8gK_;C!}Zwmwu7Iu3f?IXsvR>|XfY zru$mE-i@{X+nabh8oFbt%PL6G3VUmC@e=bFfrnC93Om=|t~mzSH*sbn7#^UJ5t~$q zgNVynnG6>>6E-^HK_v1wi3POEa60J==LaqOt*nOcK}Zo6&P(CG0GEUDy@X(zaC))$ zGh}q6L9zkP%i@Y2r6|TwbXyZB9@m-X$y+$jpS5%(ui5t@cd1$3I`h@S1qf5I0_S?< z1F1}fBX2D8maE>ouB;|wRCuhf0fwl$04Xx zBm$1Al&8ZYrUlgBt8HKTE8c;9`m@B{zoLz2s6kn5zZttlXaOiT>{f0OLo zP%_vke~*^?x?pWZW{ovlDCtb-J@FjsHgX~r@7J4HnT;7^SX*+4G`sr2$HTIi#f&&q`q1W=ZSAlg4i z8}`pp>U_M4hH`Yc0m)%1l5vFOXaka?R3s6d9KG6rk?%C4&Xs%mSQzNS^a(p%oMwj5jiidU`j%$8%>^6gSE zth2scEUbfyYI0c_wBN(!y~rQT=xL{{WD~Bq>-L;Rr#>9g)l9)Z0}=HZJ&dbrB;aT$ z#NTiQQaawU@i12+ZTS{lt~&H^O7~xU8+(o4*mxT*`5@YHkLK6xNPNZc3G>zV6s}lP z`4z;)?K^+6x)mQ<81x#&-am+c;X?@0<@$bmZ|A7Cd$YXvcEe!2%ljDa!X0%NWYKU| z+Zq4issGUG_V92Y|67Cq)Bj5)uQZ z6EDInTwJg2+`v0#4~wft^yM5xmbG%Pe-dZSiff zM(?9VcZFyk&{Sx#M8Fa`4e*d{0(OTu!qy-C`FOgXcuR%n!safW2A1 zN;Mf4iRnCxa_bF;?I!UziqL&Eb>V(1V}$ejIX}KI1Tys2{p9*nVvVBW?v};r40HDp zdM6AiXWJY0?3rSv=kC2fThC;^09XKje!Wm;0~lFG>JRV@pZ#NjNq2wA>Y2xG4Kj?^mlO9;S=z_+0i3NyMdia-kTKH+d%7+={|2O9u%= zr|Kjslx6%evSmQCg=GNu=EXXkK9C0jVuT)JgmD#qz23*pV@Zv1YQBPrBn=>t89j`W z0||FG{ptMty`%==c!fc+{T$oOr#9?ugaY4ngBCm`bsqmE{5tGfwdU5v+a9 zV?<85?*PXPa#-k1ah3a>OKS6v{xxiqYxV&xm++CIeX6>ledB*)C28u(JJS{fc!$!; zR}0rN+o+ez$@!TeWZGOwRk;%uZhZ0dwTa%+SBW)_VkgN?TPh=&Go8sP@@+C z^)SliO*)oFu)1@;Y!Vco@!+7sHx0$D3E+0MTE5=hy}P^HSx)a)-uQM02zPe^r}-3a zOS@Qm7i+_T^O1f{uC{BHo#o`(+%M3Q)9E$p9dxQVZNEhmzBZ-}qz7W4;ly)xm6a^D zW$CbAZ?v3BYVBH%+SSzBdpT;?Qfu$$sJ)k3`+#Yr+8?xs=QZS)PJJ{QPy|e>D3TOy znwgRfQ@f|**o3JnF(pumh@5d;mN9AEsoywnwCk+V9+FvpoFd0SuFL7pEwbq4csBOk z?Qx9t;@lL9-;r?}~W( zY|n3EbK0Y(hOJRFMSC7B8~lwn?7aaBCqhgX(YPYfq2Ex3!8*eC_0C00oAdoD;bT!A zNLi5r>SFLZgj_VpMo&rm^!h8jCqo{56MJy&dzkK6GKqz*I#~3j}vR32= z1M?SXQ-D@m{2^%O&EyAu2ChB^ zxRwj}h_NfWHIOiNg4Yb(bH5yKeU4WBSP{a_6eb0|r*Y4WJ$aqO8wlOYivA8rLU&|f zbcZg3lo*O8l0;E4hh<4A?iEPm91q-gc)crcwc@gpu(qZc_kuhlO1!;uL~q`ZHEE@6 z`M-Zw%Ig1p7S@oGeEGYRx)S7T5fo6-HPJ@Or?AH6GIMW~@ z6h*w9TPvB7_&S}A6`jHSvq^|%64CE4_ItdV;&Q0Uv>?VcFe-`uL_-Ih8ekqSdYOp- zt?aSSWG%P(R>8VlUO^Gd|@PEg&6@d?W!f` zKoy@h7Zmuxz>zi;a^8f415yd=F32KMk`5`^l^zriYq+}8fs9ezxb-B`;G9>J^cwBr&Kv&l%UvRRjwyDUfcdGlIjHD+Bh7DFIfBtX@ydf~YH3jAeU=DJp&YilTB zg4o^BVrW{69i7j~&Q2B}#5kx06dE%$DB|v*;!4YtZwsn#@T7E#@`*W-4$cq5wa1gc zp&RNv%3p`$nNx}dUflXbadIRItc>j=-@;}^J<=>qy_Hbavbyk45br_s@Al5Tb_hF8 zC8tJ^m^)AZC&yt(MiKbZ4Q3S$yzim}0G+L5)u`HPRI_T-Y&B}h8Uvzfedv|gjVt8Y zVgMB)tex{2zAiE}G-m_FY*hD{T`&x%!)C z-1t>Be$`4!xF8w#MtW69T#Gdd`ts`*$-PRsO_tQ_@PQH!q9TrRXS~=gpi7ZilcSEE zT-fQe(>i@>oHt7(bnq0$`gf;2Jh>SDRKy%kWHTpm|Cv=QB_^W}=59iD?uQ@v&voSQ zO)gL5C|6ABfox8i)yW2NF9^@?Vzorv>HlNz@Bh;{l7&(H{EGjIiSJE<97$#XNyv(G z76Fo3OF#fw^5)6qGr%CRMnHJ6q)l@E_HVuP>rBsp5@+xC+|T-X&PGgES66peS65e8 zSG~T;_*xN1CUFh%ZG#b$KKh-`n@MKRBk~7+?2IDRcZ4L~?kI^~c&Z4ui;wCMbxSP-ApxOs~e?ZvH zv@t3-r4bIF)2ghK3O8H!Fub9hW zM5=84Tg((jSyPu7#%Y4DKj1t)MW`KU^fgDi&ifvbjF@#Br1Uq7`67MT7nU-zVO8$?p6-pSr)Pfez z=rg?VdCRo>Jv1va_m3;Ua? zcu?X^rSTvxO6g3l(#oVSFB2^lBy;zGfv92%M`k?CWUy_(M@@4q6WDL%MkFo()fGcl z@AYU?$S|3iOnK_{@Z~^H5>DCic$ra<2Z@mNEok#KG`Mf-3#hDTa+!?Q4xC%{oX4`) z2Zh(YRgd*f&BX9_tizB+wqr`mu6|+M6E>W-_y9c+>wL|Z+-fEI1?S*LVDT+ zh%v{|Fle}z;!~GO!TP(B>i8DGyh;U8ZsWG}5Uwp7d27|&QB=b?V8$Jh`cU>n%%%3?x>I{aTI95Miq$zY^3yu@ED0y^B` zTSt@+819Z@-kG5YWgM9i*$WlybH&U6hOvhu_#8naa!0cnp2P(myYZD@iU=xYUQyUD z`uo)m)U(Br>mNL|#yWZ>F2!H%WWUJ{e;WS`TGiVGQ;I1R z@bRS-3BOBf>m!oPvmx}5!KLMgWk!D6rUiKT3 zB0uGRwpXhi8c=I*=#Xl&+SmWr-Q%t&{&%>eGJA3Dw^%^hoSw&B4pb-5pFB@KuwcMd zbE-X&{v#i@lMfg&>Y38vuaXV;NR#%6e~)BfwIo3_U{Jh2DHz-aPNdQyGX65TAO`gh z$cE<)_ji%u@N8`-ItLj;+z1j$CP$EJ2qP-?bOYK}~-B3kon5#u^iYis#LHw2cLx*zuVw3#5Ks6q7$sB>OS81~VK z?GOL2g253$t2K>#^o`?MU{E}>1}E%6cxoIi7047hPMD<+&EW#1#{E)kB{2JV6FCjB zGr>X!xDLdV_}Anv3<&R^H`_ELCJHenAg7~~N4pJuqa?pJi-9Pf>nPfANJ2+r#Tpc# zfP-WVpFH%tMJ}r>}y%MiS5d0N%sN9uo5^K>RC)= zn8g>L7bx;X&g9D{?4oX4n{S_Z3hFaBHrn2CD;?c4uZ*GJrQDeid5|&apD_!@;0wzR$6`s| zfVP)M{^9FFp067sl1ZBJq;oaj@MtOXD-DBzrx9bAfiCl%&vC<`G4hdR z@&5Xim{w!!6TX2d;$NIl{@@lYb%!%yo0Tbdtmf18h8WGXVP$0+7rOhLKJ6IWH;&&A z#zNY%W68UkyKftNhlJ|BMS7Lyxmjykls_=e*SzT8!eT<+T{k!SGS0o zYXc=2x)ayc#9I+vs~tcj4Tz%{1j_OfATs@#SNf^Lk86E!gVO(Fi(?6Mc zLjH?CRAzrc@m3hXN&c-@3Ij#lGzhAS2%MAx10(!%{5nL1FEQDPg`#l+rEE}7^l{75 zd**(~k~|a-S4JQMCEYyaPW^&WD3{>~GtdL|9J+MMd&wlJgmm_RN;2cG5>~2ay~rn+ zcTTv2X!tKddnEM>4qMc>?nJ0B26<+3g z-x!@Fry<7lz3h)xGDH(^)hs1=^}HeOv%l3t`*s06LGDCZa zT(lxSQJJ&Q+9Gw0bJsu(?7Ju%W(!xh|JScT?uy-dI$)v@gtMUq2W`^NT1qfK5X zAr!6dDZZ4}BO^8;JDrIJjAVXh;KL}(wr@oG$t1?KJ-OO9WNP-E{0OMefmlF!My5s@ zJ(`sbo@6%wZ(_iW!-%VFM4%8)w|2I&Ptm%<=EWI>rd+G_^mC)JkOcs)d~4aC;&m6l zs*~oY8S2mLIo4?~ zZgjAVM=}x;)v+vv5hQBJB7i^G*P4zdB-f%^M7J7tgJxks7e&vU`zr+9cem8b%d*2qXsVzeqORd-HPK#W8Czxmp2hC}9 z{ezdR7Z=Tn=>yUJ(C&3_POr|;`&8Ylnm73>pPgIbd^CyG&m{PU?2T6z;nY>Ww*CF6 z`OEfYPdX*U)D7-SbO*<{M!TL5{W!pQih?|G#uP;2?GgrIH`?F0iBn^|kBc30J)R7) z`<$XrhY%o%A=0aSvl5%K&atvG_T`n>?kFK5<*kUDR}=-^8Qmjxz$yeu?{Oyf8VF;WdbzC<_Gvr6v_3E?pwWDYh=>!F-B8=z%JLt$Rj;YW|&YZ zAJ2feWz7Pdtz5vXxcU;EU7IUlM-v~EprLP`R3Gu-F6dzNpxv}(J} z7I?mWR`GMiyqA^f(uHEF!%Csx+P9NB8jpiGQ>9>GE&6bM&x0q7_k)j<*oJeKeEn{E zzrm7H8tcl|SXYS}t5~P@ro7q8Ju%X*d*X-^pcRft$ux0z=eHA`T zMG0g*P5V+iMfYY$PgO2P*H`AvU3^!N`U!6XtZ#~%c;7GI7%6A2=w9%`emy))vd{ zUmco>Hsw$!M|lb)WmF5+trF@-A3`f zIQKbKZs)RcD;V^iS5qs{60tjPh+N}<85zs&jd*ue@4zJDI|>0yo-DF8Z-0N^Fep)l3~4i4zd2pn=a) zV@X}b6jG>z!2e@!p-xCKK{RSpNw!rHtsd3m@VsS(hHfP5CAhu2<@jtM($ZldCviaCHYc5MtQJ=*CW0&(bDFy#;v?qBcoyKmv7*y(gnh209R+8Tk=p2(>|Imtqp-sv-*jS*$P1H$d!( zJhI;lF934oN_7N}yX?dV50GOc&;TByv2Dk(Q8P6Xa2Y&$Rc!o%8{dw*m9#+f!ASK+ zoyHcJ=I_6E{%BXaH|=t_R%vuEw(f^IShh`L<95AJBBOCuzUscKHM;dmxm#~Gdd*g? z(fQcv)-F{aftl`LNsZY8%NHcDX~2#>*n@z8B{_J68XWDE;m`Iu_8Tk^hc9;j6PqV1NG32L$>+Pt)9p zVUy>ZmpWZjSyx*B3Kz4j=EOB4Mu&Gj9+gc);u&G|^uVUgA3;5jWqs8QJ_1r)i1~l% z!yo5Cv?HUvb!+a^1Z>T7BBt*j+SF5RQ>?g0pNiSmait8T^h*-BMTbl$A<|g}3pNvI zJxAn@CGWKvhS|ql7o2iJ-Gstia(h^Hir!rK{uLuwj^!IKSBi!a*}CSFfN#<4dsR(= zjAy|SDYOFigA-}gub1pYl5k z%vZGu>;5e)1v{cf4XAqJCH3|P-jYRn5xp4gu9dkfbQd+#%0etDiFo2QpP?3;6$5~; zRQLDy@ky#4)*UNy$FI+5k!bvS=|KMti_4##4blJV!FtAB8DER#@Ma9HmU;SLPIxpsQ_*F3QIJ~Mn?F-S5ove%<(A4ZEAGf z>YJDxz9nXHLRZK~;*WI1CR=$oc;8T~2!^tDXV<3j)t~FG5DN-DTJhDCbt(NVoA~Ut zM&8(z8Arxc)T{c9Hynqq<#sh?PZYYm*D?TGr5Ar$46=qe1xo5JKl(N)Oq4Wo{t$8> z#R*FjXG7c$kaw%C%3+v3SJWDaP{)@_YW z0%3-zFrpg$;m({$kz+DG1ytu-K&0KPD01)dam8(zrkC6%>tR>zHmJp?tc6AIZ%++v zRG)Ea$c*7Sm6CPOvyZz9ohL5&1Om@KCGSLz$*xnf91q?hK$=?_2V{02!_`bDZH`&p zPE4O7G%xXXF`wJD$Ul%dgELMk;ivnptdeRXTWe~ z)EsqojrX2#nBi+s@_T+oCcpaW{%mmziceKi4IP-w9^Qz-!hda@I20*D^=QorR|I1G z(jQOp@hQDb6_5P%b1nRd)^!ek&t|?|ihMjrL)Qv5ocUt*d5lRn=>-mTay9`<#+1NG z1Vo?m!#9qad>^h%GC?!4DW&liBvSsq`!z*XQ9)?n;Q!eSn?6wN1-dGXhPY&#jM-h> zY#!ezx=6%lZsX-(5{&P49E$|%@X5O#HAx+e!*D$a|1AVcz8TI2bQu`P34V=kDU_%6 z9y73xgNYHRUQdCz4!G-b(bUV?s{kSZUfPt0p7CLD!04!%GE`=?X|Ajt!AeN zL{Qq-MzaCu{Wi2!y9E7xAKHd*UhM%D7URBP=H z<%OvMiF6@P4MGPi7v=ipu2(Hzmd|Tcu?dLU1SV*_epjPU&}6wmuUjih$W!NECeH=zX_U8sTM*I;=;2Ova*gKy6fK;iGJPEAu(t(D>A zblyQa-~%O?74Tx7bw1R0y~{3~7<%T~1j57XxMh%?#A9KFyrZyL>Z z_Hp-V2YBKuOtJd*t@l;-$w*&1ybtIY-CF@k>S@Ph24Yubl7y8`9m!;hx#L6o`(?QU z1ZNctUI*>O)Z;Y~iP?xgwzd!uom3HDloqrfG z9D&gR)daDbMME7x{!cbLp00Lsk0o(DM%3AYFCHwJ*?gJGe_mv=Hr5CwJKp!- z$GYIa_@R7}rFc9NYc@+EcE0h_(I?<%HM6tsQvep3mvB#n&=_)hAIzb+V27;NGR{Z1 z8mL@cwJCvuvdC}Bm#%5SQ0Qzd#ZgC3J|L)sH$ODMat&%(wl)x2z9- z`}nPM-Z2pWqd72-H2bkmZyqa^S<6KO{6Y#qOnnQ*Zc;i-iak-dkUaX&H{SomSOiDH zIm=AV#rTtre^z zl@Ur3h^~}df@t3$ngC%xnR9xoF|Ud^y)Dv+xPD&Kd-p`S^#tKCi|-umqqjp3n-TR3 z7R96S;+~Ftv3fF3^Npx?K=p3L@k~a9vm=`(A?ZpahZYjU&wm>%^+vaM)~@|1a2@S( zPEg@tMV|Hx3_c$I0?PmWc;y((cQw#!d#AN>S0E`7l18(ou7y|heDM}gd=4>Y>e7Lx zasdi44JuHSF<^dGs2=Ir00q=%AKCb=AyfFyWQ?k&RW-S zT~^ASu0lq*4kEbj2k0!CgpA!45|Qx|2+0@Wwe%WKoz&F~^QzOkY*uTGqU53j4zc%e5KciRO}{k#ik@Rd4@W!TH)#~OCK(l8KGtGg3lu8y z8d%RM+k6~N_3E-kC}Lr62u+{R4A=K_&I%2Sdf-^e<@TjPOpi~o8q0vN1PeAW(O9#C zz6vuzE4qyxeT~`p+7ycfmB>A)KCZqh*28uU&;V&K6d1c&B8gSIpFmh;(c(WKAxZFHx1#^+0*SW4Ro%797HHx$(Za>ws*#Abl5P!!0>>r`yWVgj9U$OAW>wN6Yv9VWEi0nTWUX+%OECA1!gEX5#em%aD3k8$b9;`&qg zB=Y}w*YAVW>!UAgnFhkk@6G8Wk4GC;AyV!7;5R_fj=j!T-bdRMeR zveZFb17~NEAEJ;$(dvW@#U2%~x{%!3Wdui<*jf~Fw0&zJd&=%DDaP-o&2qciLqCV+ zwGHYgg1)Ptzq3GN9N(p-Q9Tv;qzq72uv8)yVcNnD;rmzEiiWe<_TWtxUV*OomQ<@b&Y*KHumZY zyBt}=`PkS)rz1Vq8)O8uk_cLGOj81AOy0B;K|$cW_}!MjR`PjUJQK(Asv z#^T&4(pW4FAe=*tAvlbqdh_B{Mu$XO?vkdnL=go41M*@Y)2YA0|Mz9y)=86;3c|*Z zv_;5DdR@meK3FX#y=8cd6TB|uz1h46hmVt}S*+fr&A->YZe+BrHv-jc36J(h6@UUp zh$gDu=ScuK>Gx<7+%8@Cfg$Uab_HtY9i=Gao0lyp9WWkwpMOHkV)cpQkr;g8q3Qew&G8gz!2A>bjo$eu_E_eBD*U_!6%@GGi1-Qw zEYOlS4Cf){GM-Jz-52z7aj?K!VYX=S+gOUciVGmlk4SXCCw5JH{^A`(+`noz<5-`y z%jSog*Mon9U~oqktzIypyO`d1+FRbwPoPzdx2`$>XEPkBrALav=_#M_6!&5{PcdlR z6Y*p4%LyCghT$VezJ(nel!{=voDIg5s$E=-hln}C>w{ll4!u|657AGURo0b6eR&>MNDT6*=^hiA%pI=?x7n*c*U?N? z`H`<<>_zuWNL*9$KF?{`$Wk`PMZhZwi_~i2B|)nM6#Tnjc^6$Yf*VlA^+4v&PVDto zI3a{2_gGTMACunVZp9zQ^UsHh&q}bO2EG2sB_&VGRlG_i5#$!XwRXGN#_~{b2rxiz zFkK0rpOYbe$YP;1MQxCDqV{TTm? zRUZ`zdgatyD<@Pd$J$Iv9&eatL7smu*Zp`|L9rN;3-m-*RFMmDdSknAx$_<`{gn|! zU8ELw^E*~62S4RSE6bU_l=OOJSV%+`I$wiQe;xnC5Lk{BMlpJS<5J>AT>n=6 zu}~XcICrRDZMO4&I*_6sc7mbay0m(fmjHkuSlsI$QjkS1gUus=^pi44F%&{Wm_&vF zN}%AUh%Z!3Q4@wu3}&sW-VGK}FdX#AKlr4fj0hkRI_5&gMHfKyU-(PPoG5G|>jos# z&g7jexsc8{NYH4;YX;aU?ceC3_c=6r2_u?u@UeuYUneOrFeK2rMeqy`8j%xiS0>OzyP333ggKW-s@`%s z1Er!LrU8q^X?7t2GPL1-hv}7zFtXrDG5p z5{X!JFsL*yFUyT;NBQ)J%05H>>(WPAT4~Iu=xC{x*OeINGq?|-i_2Y^mQaR9T1xjV zoNMu}ZQp26guf$yNtNtdj1u4k4S+JyXes?YieN1Iq4)iG`rB;1!awfsf4lF`9)DoW zUoy_pYaTDWtR;Vswn{-Rzo?*bJMja#v90_X-HhQlO#`P4AnL&@oqg_nwm&rKRpZr4BD zNo^UmLqqk<`@4Q>1@;a$Dik(U5U-SVh2m~$M=2N{fO*O_%rRl)Yt@6@4#yR_O4@+b z2FT97-Py`AOW8PujT!CYWy^R*=8rXD++&O zz(Q#`FmRd90X&E@raYE=BXMAk+N?}Ht?*NkKEZxEm@MGzwCbV5X7rRHv^jthn>mW` zh16QrH>JIbflt07c2C0~n4{eZ7P!+A7u{$OT&))#qrkWZ5&{Op5|1$@?Ls@#J*h#8 zA>>29a`HwD%4)?9-Ut^ugVDE-69y9@-kYmVi~(M~WeW`K#cg7>LR27EMNT4l@J4^3o*9;JAzXo&gN1 zhY1XurxR>t<6I@;&U=f&XOPjLP4q^K`yP12+|aeCBcd|OoJ-EtYS~6&*Z_YT665)*a|g5 z7n@{``#LhBF~3iSfLS9Ui{9;C@jbp#SaRY*ey@P6PScw_ z9k>jb$Ji~+65Oyo^4FgZVOPbWK*;4!hlMw}pMlfK_d!79e%=+Oe*WkUz(>mcpS}Hk zFS_6?&rb)HX#XSY=`DG~bGw74Q7e2x=~QNt z9lu*BX`66e?#d(y=?&}v4q%k=NVux5@kPUG5sWYz$CgC1<7pbTbWn|~Nj|*N71sy^ zbi^T4#FOn)(k%CaFgce=FqLfQVz7Chg<}HS!mea8s;T!RYFKP*HovgY);!XH13jG3 zaXsb#9}?vJ66gE3UOvAQSFwcsfn)@#kIGGyZx)#Q7a&DZtM;ac)w)QNms^O))B&sf$di_d&)Rd92}3xtQn4>ZBlb|}85DCS7@*a4hE6&pEW5U_ zbh3RdO+OE#`guh6*rbXIJ@h0&4^1Ay+K+Sq|4^%#!*|H+mdfr<{a22lS{r-a1;w_jyL`swaCVoK z*r&bnW7L)3E*INDGC+3|K<&r44-ipE570EF?(@F6+nbY5>yH8w(9;xdDC&urH(af3 zy*doN)B%ec2?{6H-R$cx;z0l6m4{hkV9G(8wH@cb$Svjz?VmCXKgaJP`@!O;Lv`*8 zKdXIr9t=XVUAbIb*l!QVwU0S#@elYinp$p&hz;v#4-=af#$kW!FHTHNlDu3wC$IN8 zSQt_>f?PN(FoiTp98wY|Iu<{PEc*Y6|7gX3T&yM~zi|A=gTQWx|Cm2KI*7%8g#Y{} z{^P$V{;Qf_I7WvmD?D?6x*lzZ!DJPF@(_~l5Mj^7sLFWkk~wk6)85E@<2@`e1R_al*ia zWZK5J(D3(k5bnR=JXcc@JB1R2EylNFd@;>qfC=F+fcO0jm8|7@PWE2Tb09CTc11cm zK^*!Asaw13(|YjftDQtF*ksAi^PG6u@KL zFVJfWqdQ7}4|B?DHH@Yq9wibB}kE(VI-g&kCA1TJ3!^T**qMrCm4eOISTiFO3Rtufms(3hlQsQ@eGXB)BSuk zyB9O~5MmN7q#hJ#-2q$!xMDmJVVHXbM<_xjZ~E_Hd91KQidp$Aj>!{{2$4bv8t#PQ z{gT-P4nX&cEAj4q38VUOH-iDAX&TElbVj(ugW+j)@y)pP*M5J$Q}zO&&r}h;TINLJ=byqKm+M_KBV$ zkx$)Ts^U9e8cNUCGTS? zPP46ZUsDWZ!iVM$K@;c(`ot|Aj7mqNTQF-woys~E#F(EAse z3*;OM7l&auSWtW+!L4Kmqzx0FF1-*jxoP^@^L#*VR`@ddaR6HkPF(@!ZMdeHUEpfH z52Y^k_w|Gh2!I3zcm=Es40{=lAMv=c2P>HV`rD#^yKm?XtM%S6fNf(H3|Dx@U@D}5 zB&PpmJqB%&Q(U|OS?p_?ddG<5C+}GhR`dyTl2*w|ca!Y-5qpFXgegiSTF3geGw=9@UG# zSK_&f)qs4>iTLrnMeM2PJo}`>tK(S6$4Y-##9t84q`Gm!*3tYM4`}G@ErI^mp>6S)ys14^s3$IGLlP? z#~0!IAaz%}-nYeX>1N>IFn-(DjUlK66ZA^QE2O38AAbM+=U}oQsZwv9XEKG1Okg}- zYJ~})fipN#RZLjoT0DD_xrmtKLqN9SIB*$_wLWxZ_4 zKh9&cWgGd95h-1iw8~Xn9e7Febl;z$&4t-?$*#5hnuceH?C@f@@2MiXvM5(_Z*#ku z4EekWaX`sp`Q;;@kob1)vfR$SEeY65<%i2&lnl~+n{BL}m$mKtzH9*ds;4oCdbA}#tpbdH`>_@*CQp^=3O^Lp2c`_aVbHhA z-FRNPAL7rWUEWe!s|EfniJwRb{w@P_eY#p)2Fp+QyRz%yPmQj&i-cFmu>TeVyCVYq z6T?V#(OC|El(<(S^6uVzy_jQa7U6^@J97zpREV?{D-546@>M@^;x5?)q0%G_!~|z5 zqub*`@eryUK=a%<4 @fBE<;FKNLZv_qutptzf1-?#J$9PMHyn!v?hYEaQ+Ws*K2H1LA%D1Nm8l$w#NV~YIWQx%iXU-etK+4} zW5@h*2(xhwTU{f@!AmqdZR&YqHY#FtmIND;=WzsK}ty zO|c@#U}J?|mg|CQw(g!lbl>9y9+h^x4^)C*@WWtN6zAV!EC3o>5ZEGTitvp@ze}{h z07MZ)+5=`Iqi}D5@ycZm(h5TlPT_QqbzeUOpThN={qkRAyjP~pg>-t%OAq@`A5O+k zunD6QO12A-KX|a=GoJRWW+`MY##7{JkV3Wg`D}*#j&Y!C?bCq%F5=(K57+p2p8kH1 zfBG7kSi}+y{MkkooQ02vKObw;zQ7AVyYgpItZ`By3F)q_FE3fwCmyO zX|7ka&hhs}IIY~>lE9)+V`oe-3W3&DqlcLiWjVgHXdX zM0X$G&6ca?!*nT!96*e=i{ca3B4j0|7+PGfnxpG6ER6?IH78)peY$@UwR5t9kN_9Z z-di1xOOw`)kOpE~?ulwp+m9{blB;L~zFUBHD8ST!0?+e4{+5Ev6JUEy(gkD6;m_`D zb}^gY(w7|VQk8Z^j2;Hz7ih{DRf@mSm@<_-vN5 zZQEaq{IxTO_3r0*TB$ZN{8=!bi0P%8{4NI0X=0wK5$mHxRlSZ*TieE&- zImHF|ktDpN(C3jA)zOayi-`gWUtP~9;Ul##CRT`*j&2_Rrd8a051*)4v^3;rUK^sQ zL`|wPJ+xRj%2^ewuwI*^5$hniTw!2U#Zpl;q9Q|p@@}iy4)*@*MKI^N0%1yLg5$!$ zAAYu6zYeh_d~0y z<_PLa`DWkfSZRG~y-S}X;*sKs_D2qnOjdY53Y+8XIxg5;1h&#a*j$N}Uv8iGWv*fa zcPMR7u4n)+@5ZAQ=wW!^vVp600?s3F6+_~Ur%{L|LHG&+?V%z`OH(NF=%5D`MQhoW z3e!pnV1&BA6CHqZ#XG`tDyR|zPW<7mLA@*W=)lOR z`=2v!xvRTI!DX{k7X{V2RZ#CVWkJ&{=ybBV9W2T6D0{#0EP6Y;;tWl+;n5l?!UHWD zaFH&|M$czi;{^PQ|4aG0`3}@No+Bis82&YOvc}{2EBFC@6`?W1MQ?_Ap9M-UUV+ns zLrZ+{-WNpdpjAMOBfVsMYYit8@v{LGhOkvrI1c;+z{k@*W(;3V_I_Xkf%ly!6nDZd zQW9smruPjuvqKq*#gLJWLXH_71GXBpjAOiZJ=;}@A~*i8A44&8Z@vE!aqIZQZ^(<( zLp`b)SaE$h+Pld(_2|b2*~lqP9Z_GGytwv}twkqIE{EaS=R@4v_-sm<$Qeq+5lM?nm?McmmY|3dG{iwn}!v-9g-8q3{M7>fpf*i6F5^oP@2eLxI&?- zGH6PxIi_A0*OsWsLIe9E-iJBnXxW032*(x0Au~THVfL#?+&~@ueV0DKFYk9bq?AF6 z^9#h>2*i;6?X$;5&P=8eu7vY}Ns(HEco^!j2~Mv-GyERo*|ew_YVQXO%XPvb2E!2d zZ}H>vTt?ZeAN%*9ehUY|)CfowphuHF#8jpv` zxU_adOL#n@1M|9eqQdzm$cmMzmdyh5sn7a zl^Dq1)oP}*0Xo5KSiV?RUoJ6ZQlJf-bY(|e`VgRkDt$Bf`)VeiwTAmn3BX`qZ0+-_ zK*FiaU}BS!wvE_6#U>CMmiu8i37!Dh&++f@p2_3kUOn!jG{k#C+P%d`AaO^^gvH~& z*ec+(e`1U>reoe>kiEYx(Ec^ZWZJCD2n!cF4VVKcH9ri>@`Xj`SPWxpF}qy|w+B>- z%{)0c%dkAKlimsZ_j}3(T1FE*{QT}aZwHu947_Y&GNCsGKaGEmP667`;Xh*~2gL@{ zzR^z)<>=Cfw;o2|m<<5CVf_EjwCt+|`ITo?MZ$!dxL0d5FKd^q$4qeT8Xj!fNVVOV zOg&wXs1>o!C9$wMe?57Gg}j@JPxVy@knRmTAl#Tdn^h(_H~>iVTbve?g+*7gJT%62 z65fgU1&?x2MlMIF2qoHYFgRSQ?tdt8CDn?Yan%=nkwMkX)2(aRYQOM@X7@5AvUk@xC|Un&A&nh z;?h-;8f3Bbge2b>a<0LB38T*6&g3dnB3ZUAgL#7DVXep!eu%R5No=Jyt}ZU*IC&IY zPhMxSu|GCkFk0lqWXQzN96CHxf87-Kf2OG1PEsoGk(7ZJTV}#N_#c>t3WnHW_cPK# zV6?l&5RQ#qWS{Ta-XkRnB}qEB?;0BwGqnV|zdFS$P+K z_9nE2$stxOlrSW388S&~+xQb#Gu@Jspm=3b9hDo|$n`#D?mY@Y9%-TJ>O^wO;(j-& zZR^8ZgFWcOcOJI*wdLZGB#>I<%qV=W=r&D6tlGTd;xdZ7kG`CqVOdjn;YPo%mccOk zZSmN114F?C<4QVQSTLs7tS@w%hXp;16u_poQ&z5op&8MOgSnDmdFwn5=xZTU7@PyJ=3?Es%MJppy5|EN@!g34>2JeO>rg@*#^J!B8Pz;A*G2;6VZ^o z*o5jkFE7#Y_Mxczh8+Oy>3|(u2>n31VR>JJ#5M%B!flv`1*OD`L^ljR&`nx5{hc*H zG8v_6GoT~@BLhEn zWm|g|x*fLcZk*+!gmWS}+0gW+7I&lQUYOHmu1PVYwA!>)!eFm{AXl506`c6>|CK8~ zqDd6wzz#B+#aK!LnhdmwRqBlozn5&+(TdTb* zE9?I^o)SUU{Duw}@V{O98>1KQcyDmMBzl zk52h8KoRZQCgtQ1G$n#H>Yrb_rmx9dDnYMu=!&+ zL-4piok6dM~SI zwSz*gd-L6E`m5BviPpy~)0mdp_o4pset>`N`2xku&Sk}`{lm|__H(&d&3wG3?ruuW zgItbjJ|vpM$tp00>SYx_cr|pbTFh{g979&j6$%Cpki`hAu)Hku2d^6r?xwTJ?Doku z?rQLgV(<=+bF0UpB4iI;>^vAb_};y#Mrs#^KGqKUIl%IA6rzLS=SMYb<^HS!d(2r4 zzki61d;Z{<4mqa}9f-_(N(BXC@U^?-r`*rNa=Fi{U zJ@J1Eg9HEIk4OL66#wVosCZP2#{VfCFy*;l`0d2NWf#2R4 z-YwG8D$)+?owCQ$6Z4hB`ZJ>hsh7=+urixJ@k8otWrsqW;QNEw>h8(A2ya1Y`+haS z|Nd<;yT!L&gW3HLF9b2Xn6bkaHT!ZG$9-|W)6 zpuB8Fg%X-A7)>@oVU!;lG?A8=B0zJ?8 zdTy}4>m42)z?llpb!8C5UM0BiFUG@L`17*t;>W< zA0O?}DzPGEsfGClhPi-yH?Bcik)#LQT)?&lhp**e5k9>1 zt)M?686ZNh4P&9|RGbhQe2ydsdis&{=!fC-1*S$?F2`s`mEupxgfS8U{i2?n$A?df>7#F(Lks^A^&`8gM= zGm4f2sCrn*4AYJL1w9cMir(>FzJM4C)ZKPSF>R31uw|067)%Ndx}PXgDdk@Qt+>fv zmTxFlEBxO3wD9)EB>?y{A>i=A2{0yxWLeJcLm3ZL1ga4cgkW-~s}a34kM$&K zFAXG|#!Y6(6I()mHdLF9+>R||pu!`XXcUTNFrT_YT|fbRrGz3dQ8?ykJ~T1LBV1S} zmlf>^J@op_+QB4f*mqt;gG@k2-s)+|?s(q`&=Yk-YnFZw70`A96k;1dnkQ^U#4jXM z^c{hGzVq%M%u=8on)dSXsK=>YZS%)KZdyH=(fQTq`3W|0O7VPFLR?{_DwGjYKLyz! z#Ff!>rP+SpskCd?o=;TDpbHe?Xz69!kMdG_7sSS3e+NfIm_0#vqQFJ!u4vz+9<#8h zk{$`Zx3Dpth{%f4g^&c)dNsSJiud?7Q9Q)rG%YZvpjg9B#!-DRVvs?8`KsHztX1n} zL4KJc_fgbA4tk>=51^yXeL#j#60jrOvqYR31eB561e*dOd+4}DuFv>T^pRdJprkxo zPbPbSZZC$8-d0Z@f+q_3CG)Cb%p^{~gelfeXERLPib0hGzWT`f&}?^Wc&FFF0NQvX zg~FJ+IHVwJ*dq}xPKN44QzU0?FZ3DZvuyLkK$+=biw# z_hC3^$7mUHAKj?=$v7@+O#X(O8K!?4!`|`~4X>f2?fZvjC=2KJ*8479Yj*yT$@m8a zib&EC+3SeWR^dH{6ooBS1sK_vkLO;#pU(QrmmHFeE+g1cPz3eVIdx^Kqsn z$w$#D#+kGNdK^PJ2t^_CD1cv~?ooEg^+TH-Y<)D3>eMOYDz!s0a=?v9*x|HTW zep!d>aMwEVO6eY@tBgs56F?LM=j0mUel1t)(dU33);HT{Cn0lArTjG>VV22065LQwEj%pO^f z9(s2~Jj}Nqai8{Ff9vI~zyipn6np@8eP9qt4ZXn@W)Bg9Mqbm)D;ym$LN6`>@+-x` zxu7(hZ#@xB0Ce174)yTOKkiAQAiqmK=fX!$e56P?k7e~%Nqpu|1~#m(1Nn$A`5edL zX@|dvFKFDSNx^+se4`ieG{e(SF7*;GCZh%Vj0T2z^rJ88*QfM;1eJF!FzAijWtmQ2 z3(S8=0aamtL7UNf`6NQ|+z%bK^Hf_(!+13@L+Xz9fu)r%UwLz+LGhLR9bg`(xNl6f zzX`=2Z1Vgniw2+0L`{lIg?fq1v7y}RmJ|sV1GJ7b53tPiNwl&fF^x0LPV_eh^1u*H z^ns&Mbfn)Qd?|74lm-7P?Edn34DX=lF4YFTytS@v!x3+y5l7q?3 zA-UJnT)q7xb46(vjs+vBMrGChw?kn3q7WS@MTC;PU=Euw&6GVKw zZr8gt`dHK-YwdD}KUOFrh4^}1Yn;;80{2dcT3}w(TVJE;f@T_&BG{$wm*IfBr|MVh z9kI@NEec`Gh(%~QQIo>SQurY=`Vfyu!TXgRHpKuH-ca^14#7lWb>y(ZYUPiZz{SJg z0NAUn7PQ0j7jY^_IZI-HJw+M)03`z9ugX%FTgx@*BD_tT=Rw`fB_jTa1o}cTsMD>crhIBiF>6S_L{%6qd z3*)b!>j%rQYa+Rre$pF`frEl;YeZZ-*S*2(p0k?>hEoJ#0cFM&;?X&Z@aWMS?-XR^ zC7(3K4kX$1sK?*S?5ahqD!*Njx9_3(Ny%Mb$6ryW(Y@vBX%d>JWYL%zQ_yQ*$bYn& z1(Puh&hjg@WpER;L@>zIr7UrPLiXlF$XzYf$M zVQPa|GQzz)(iUz?S97Z+zho(9aCXG0wn7ZT>@8Ou4!Z(N0GD!kGQZIL1Nz#1gm0}W+BAw^-A=G=%s{mhnU`D@=u!_$XZ zL8oG*Pv$?C(N0AwWPVKoKV-yF_>-eKsQ8~B_}Hn^kUUDI4|9k;>D^v5WaxWv%!n#+ zIT_iiO8dQ>SCsSg^pygaGb;h**&RNI4=_l!Se>+*g;X~Cx%yN6OtgssI(8u)++m6% z*d~Vn*u(f}CkY-^k1NcRxt<_Ej8_&S-$BUdcno$Yj3f7O>JEl`7@TL1=3LlMGOpQ_ z^WC;U|UU?B5RwM<6asOnC>gi=!6OrZ))YL4ORpCbV_J4-4tUV--+DrhQdM zSl~`bf?qAe#U6S?(iVZq()Z>4ih$D19vcii3fv+ZL@CGZNXwE9bt zfHhQGOl<iWPGh%owf@hA{$BP&-2K0sJh zX^f2^U>h^uRp9j;-M|Df6EtT$M*}}?4l&d6Bg(S5&@m}d|Z~Q}^i7`heMvD{hmsShyqYOo7 zdLzQ*c4g)S5@L(JIbP3l04v5J^>a}dxt&8yKyqjLXXnJ-963Aq&uNIVQN)nHkfGAWUbaRBMBP2ni$`?tSy}6lMfDzU43xWpZxh8; z49e6uY_Ku|uusPKV^*0_c~B9H>E2BG!Gy!FzKRU-Or6a4{1b82IDF3C=oiYf#<@t_ zwN9<;?TH640V@$NzFY&5Hw7!a-X?dJY;^AUZaZS7;QvIPm7lUalo7oC#p!$|9LJ6K z&CharIqwwNYt%hbK|Y20Jm34lPzhvwoh%DD`wq!8v+SJ6fI4I;H+TobWl?KP`2Lul zluxp8T)ai>$oCCMimm+MlawPXMg{Los+=|twGH9x@a)&ioqh2%RK$shq19!2&xk|) zypQ{*co0q3{Lqm&n2xF?`=C`SCY@enVd^Q$t)L@b?jP3gCr9HYXEMnMrSYpFx>8se9 z;FI+}s#(Y^(y4+1W@IHBxwz5bAVIPgr}#1xPPuh)zbx#9xyP^XZbTQKt#@-M3YTFy{?d^y+Szmz^f5^tG^W7@G7Pc-UcxuGUb3sIVBN1qW zLJyXJTm{I##V{p^Xl24IZ3ax!k+%NY9#|KSzp-^Vu{U@T0Gz=W={*wqfKdyqA}vHQ z$!GPO%i0^vg2w(&oN^TRN%V+27O`uRI07FugQPH3NQ*55UmWAE`w$abLOZ6Hn8HH; z!W4x|aTh3GN8;#HU{lbG#a&Nc_)4H+i!{lzvF>f&VzL~^*f^xP8cISFVbj?#)Zx!; zTE_|r*Z1@I-|t|`1&)Vo&Q*t{v0Aimy@K$U;`>~3HoqHB(G%AYzWW%`RCqK48=xx? z`Bxp}?w9-fLXa_E$lLuC-i}N>)yt+%$2Z>pWDP^|Z(3X5p#Wq_(n=qQ;sQn@m*L|m zk*!J%hBusIN_RO`1m^vqlww>)uAY?sPN-3;|LVRvaR_XFICSxYpHmDmp7yhj$&U}1 zpG)|j7ZRlrKD?1PmE!OZhc3!EPX<)(BPC%BhMi8;&X5wBwm4a;o&9|rF`+qXW59!q zML^$SJ6iA(9b?wGrEvws>?>9Q96xg(u8lJ&d7t!a!p zFQ$To#Y|>9b`E39Gf5>jK$27nhs1T59W;GokSI-%?bx<$+qP}%j&0kzV;gsD+qP}n z)|+ql?Vl5;GpegHx~sdQDl+r19(G3eMK(AIStYK~n)}dH2ceE^io(;UmlWbV`l<@m z!3bybC{hgyb)?P&B@>xxY6}3@Rne|zk)@Du$C(z(R7Ev1z4{8Yi}#Q?M8ejtDcBzN z!;CUYCv}pBY0KTy@L-9=_WPi!(u|M&Q%v;82nw*4#U8ql#0gbhXKafmpq*97WOsjh=gKhluqspVHh>zQ=G4yLPLAO1Gw81^Rn+Ar~kMv*&?3sL`sRB9^vs)_33kHVS{9F$?5dX#8e4OCS=y^NWU zj46WLRUd;(ZaDq4UCwRl+th7hyq5|PwAYUf_eAF%%-`(n$93pRo7cro#!pWoRTo-o zh1ZMDJ4f!bNOOp-#wmoP<9?upLZN3kGck?y!#B|N4iF?O5=pPE{nf9m~QH z%F0aI{OC*RCkoq)Z*6~B|hVaNJlsAT?b+?Nd+*I7ZeaovMoPR926o?h7mWb?NTOYNe9vT= z3~WJwp3FXRBxKcP1dolL#P8VNCy9xN9jMd0slG}gI@u2xKuZULd|lHw{GBNvUadV!>__(t zND)wrqp%l=Z@Ld0QkG3r}h zgp7D|QbwY2g8_c!PfQpzzIO3%VxNpazi5vF$pZ1B1{5SQ@$){moi}=@Joz6IKwGC& z1w#f%XAI5B<94`pQ;yNE$xM)_4f`Fdkf;s=xd^q8He^%p$N)%4*tX0F^{5|MeX3U? zpVDilzwomrBruo?j4EVa=U~fmXt>)Y_8`7_fZSH8^i~*(D#7q6fO&5?^3K?SxsFX~ zhLH`!)%aTnMH4@8Quoz$%ynSAcxOJ;8(n9Q)mTef9X?C1v@aS|U!nT$#HXykCopE{ zQ&s|x75M^o-HL1X&aq;PlQ-M=-(hYe=b_B#?Azu?Cokxf+W6WYw~Tc;MK_%VTn;j3 z1j=Tk;CNj_z45QQT8s*b5p0z%FczyaAclpol`r}1Mb1G)EtevLMkKbowfN7j_{#0= zN@rMDhNvM@$xwFsyd2d0ChBL|GQ)i0f&*!TZ-V8{EOs^ZPX2lY`^|})!5KVhSlvte zlJdCAXx=E4kb0%y`>lK(f6w11wUlO*BRTr_$5Qzo)GxB{&qQHgcvLV=3**u3&%1u~ zJHN*A2G>gYBNx~H0Qf{==ka*Hk%uS*|K|URL5QbBKbl}V*_QT^!*nr=tz?f`a+ai_ zefGY8N5CG*6S&=|zCy$BlTaq+a|MpMKOHVXW*M3FYWYCqtfE5%QS5oG)Ipd9!Jdke z<~F7M6Q#x*9cN-Ke<|<4wpQ7i@~`9OT=0<9nJHUKZE<>p!V$Z~F46nL5NAzUi~cLHGVSSDt{OM3@hp?^bGM&b3RO%5d9%M{^Vdhni~0 z1gDpsS5q^x5_BC=tLBKy;~~&U{Eks7X$dPr)NIO?RPEl2=a^K%G8FvZ_Mnh>RMvBP z%eK9b-_Ig)&y@e{co7;TflXPuSfe?obqBpk=PA9JtTns5VzthsDGoAun$%CJt{%bu z;qBG@2hZ^=^S&zz>8CqJH^aWplmhk$(_=AM)w!#z++b{NdB1bSo;@Q6H5vcA6>OU8 zs8v;m9buCM-RP=tzgf)9`J*Z>C!P`#J%R+${vs0&MTwnZhazEi8zAnICJMc8`t+pI zCv1o|4JWZhoJ0Y;Ha%GWt&b-2-$=zCacGjQZN-ZZDSXwx-)=Y`c)T@}Hwt*ze!;G+ zI`sNu3!VOn#VPXW7dJrrzMUjM1(@NE>+N@hItB(>J{@vCNL|(HTfR3vk*SV{)YXr& z4_}9F+lrrLnmu16MiSz2ZK%LJ`~=h=VIJ^XaW1j_2BqH1L#%b(i`o$*%|pn0t<&A? zkL>1D)?iG9yRYkmN6rBZ=0$+RuZi5_?U;-hb8HFGAUkFCIrr`yJmDC{?ggc8wcs;o zgHO2wX9&b!;SMK=K9tIpr(=a{A73Fc6<4MrE-E33CsWnxRgJj5+77uweL-|RxYJ@Y zdke@o-Z4-K@b&>$T85m_(a7sPsZGf;IzeOM$rIYZqvv)+9sQDnvxT4wQD&`#7jd5s z?0e4gCY>pgzoN*#@yh#JVbYo4(4Lji*NejOYbeI57vMU*J3K@5@~sLTpX1~lG%ANL z_(_n9dtg*kwwj>duhk!qr_iv3c2+Yu592h)RrkMObz z#PShQqeUrORT2ScFFz>_QQy|_V<=gs#Yc|!lMUWuO@Sc?m#$K5mGU_Me#js1emqil zjA>S07D?NAQsMXaP6=fZ#$7bG-+rqlUyzO~G^co`tuXbAbOgF)=K~y-4~$Gx-Zf#% zPapAy#KXhrBR8XV3EhO|tcEn~Z^#{Xdesl9F{L#A{9Mu9i?bszejMZuJt_9%8R_89 zQLEov>|w)`X@N@Z3^C4+mIm$@|D06OC7AkVvD}MHmP#5?tqzv%hFC@QrE12kykd8` zxwZ}z#>a`3-%|UkbQtR>8`1EOqk(Aa9_d!ycRRU^O+tgN;%j2Ask$n|^sP*!zGm%L zv+GVV0QyfUFj-M65w!2&f6E}>o_+)o?{yUosKt%HcIe4=;Ds)}dX7@gq4hwNMB9c# zR?p&LKE83lFh66$^$G{?uqSBdNngHTq@A_wjPgE%&+xA@HV7--bX^z(q>$YqfNTW+ zRF+_b+Y6DK^U(W+vaDz)q zctRukjK?{&^^ahvbTyjTHtdkM;!eP}u$@WLmCkBG!St^TeG!eGq*_w@TC@&xZACR{ zdlAKVQn)7^!9h5C^LoP34Vi!3$-c}L=5K}U=+igtjaVk{GgJC`VNM94 zYLlX@0lD_1nwxk-YVS%n-dgE+GT8*bo}F+=eNh29oa@&>Hi=c}I#;;?POo5~1s?)k z_y{7!2~KIF$B?H88}3)2kYaZ}hEaYx_djuwK6#9%dgk5-x-eYmHYU^(SO&))+jDtM z02?fQhBjI#Gxm4AA*KrYF@B)PImkaay?k+6W-)hCAm+!YoYyDcmq^>I7f#7grA*(` zo;p%XRWtj@XXFOE7?*9wjVDT>{k>XPh8x%7>V4r`6!>gx*m3RI`f3LHaeKRh>z3vj z#eLnr`4m$ZaGVvKbd>z3bic9Hv{48v&TeT56M;$h5zcSwZ`ab6Lkr5F-H9DJB<#3 zGlpE=XSS-v{jq(N*3ab2A3WFggI&vQFVN3q6_U#bs%M2$J)9eOhoXEmec}f_;DfRK z^lgq))xU-U$Lp)c&@mE()V-{U87-;kn>xYQaS0ET3o~@_!P!u=IlY9Sgo~B6h$AZ|2mFn11C&%X64`Eqg6~iSc#9=#QUv zb?5;E22Sd3#aB$HBOZ`h>9fSs$K5LAlKx+uog2!He}x8o_W64z4!sw#Ow4HBjZcUt z56*}P+dJa#Pe^VL9jWv%g{#5UPIIWZaMirtyOUy!jb4%uCl+<)CiM8)fQ=8^nYep= zqU9zjw5B^Ad>L@{uBy(G1geSqjbQ{7InN>-^3f{OW%20VN5;zGcMb1RLVQ-Fsv*~> z5cgkibOC*Ji4dt}a03P$W6y$^p+JvS{f=b)wt?3gAMhK^eSY3>WQ!P*Ig)6;fpOc- zRdXg?onCTQL@Y(8ISu;r2daUN7gQ;pU0YW!nY?K!G@5YCnl}&bb>~&4?*6-W{uNH^ z1Cy2*%&sRP@#G0&C^fi0sC1^6pa==f=nUL4L^;irCe+)tJqBjuMFGY|(oW_bz@1U4wbKOJY_SU81v?eQIqP9`^%A>9PqbBwY z8D5=uas{I1#RB{T+;Q@RU1bQ8*Nmj3D`UO`KcvJ-p~(6#m;?9ZcZtY-`5TQ6{o|3A zUi`rfAUn`PM_Rjc#RB7dCiNseSnVx^CCar^dqY6BDbUf64W1B&6g|4U&tMlrEiC_b zB1^D$h={ZT@*`7a{vJL}d~ zstiIvGT3cO_*?lqLQgyVeRwJaVc{61wiLzH$+zxnihWz1qT6uSe^jCsEcP`IAaQTe zDG1kw0J|_>E7Ev?IT^`P-3lLLzJ9RNoi>SS9A^zP2rt@${WpV>)3h%sKIpki?j-5O z_;GV68qazKMUOd+A99w)%nLYW<_YcGHw~eH6IL@bllIQhBfy$I5zXQAS3nV93V3c6 z&Ikn>FUgh%ZV#BMGO(Ls?-7j+OZh65zByIR8NfPG&J7;$q%?r`5Sd;la&tg#CT$jk zi@;qAG87B+j3f*%yTp5k!eZ7N1fj z+a>}>%bZWP=R$5LTT80juI+ld+3oeuiik0&<0ZD}Nur9Ua$hF!{ z*Y47DM}mi&=!}QbVnan1CuU*%PhHwXyral~X0_S|(pO-;=15xcjHj>&2xPOlGyfu0 zI6#!&?|K5rF9{#hKgZA-=Mvm4HpA`d0h~)?(PU-DakYPPrrNQ;Qh&!=c0?)sK=nyiXy4+@^g;C`z@#=>201Jm%j~OW>T{vqGgilF+-y*wW6C z00D<+2w!+ptUi21Z<$EK(RJ>8Q>=_S9qgz*42cu@EeR&f-Mma7-x$OX>$*7~`UY_%) z!6ODXICACGl~L@ZKXR{R?(s^$`S{#us{2vs35Y?2X-AOe)u`Bb^?s?LAvuS}U@uM<3Fzy=s~(#R*SnP zOE&tKp64?;_TK2bbamNkHt1Cq3A$IyuPBpGB&;~`p+YCSL`S*y1b(YwR(eX*syG zQ6kb^aW{(>S<!o})LFmI%HNEK2UtD0WOdIKq*~A>B_QBryL*N9@2#zDu00C~q zOemIHOgGWv39x1bxh2C3Z99oK$VcoJsq4E#U4sxmFzZdAcS+Q47uRI3yO+bFcNO(A zRBOBm$~3-!APd-K5UfY>34jrY*k^+8{-VgU6LSfi;5}k=n5}(1!K2?N)ot1f8^+E+ z9CqXHc4HusNxyY_&n<6=fa1zK@*R4b^$_Ew1JBlGX00KmuewIp&qZ2Rf+4L~?nqDJ4zr_cBez z%gA!_8<4zC$F-*OW|kQBaS&h&s(WEVqEpp$_T}05vc(@t5?cIH z?F%adIxqSIt8{-^kU2?bM5u+6SOiFc_SYIXLMr}Sn+9ZCe}C89Y^|xfM#E`+EiX|6 zrLA`}9Q`l1B&ya{bCzav^?-(x20l1t>XY)=$38trO{3Qo7)%Kt52vd{Q^~M<->QuT z@#oZLfd>p@J4`YB>{e>WHZ}ZWx<&6Eyh7YVNScAQb+aF9sMjKs|@etFK;0CdYmB z;XVDx9bh?I^(vQOp1zQ~n1l))| z-qsxQq**FQ$WD!iVZxQ`@ZgUE#;yO#qr}UCjgLV-fwcKlhGN{=m>86G#i;+z6Sz?} zaNTa~E_6G+3`Hx8SW&e^WYRaiIa2wrY~Ld*5JJU#5P2v*XP+EQ&8bJ;?d}HRZ)e~k z3Q7JeOlKY7Hx}(=_-BmKeTFH&lJ5bhG! zz|%K~$V8U)1}1uab`2_h4kewlgFAuqb>m4iv_j^zslhjnK$JKdqc^JA5@&j=WeEvR zyl!S7&Sw4}h+ug5Qvkx0^{<8>7E z?r~7RtUx3&`5o&c=&so@2zEW-wLJY;s*r4@EG2i!s7)T|eN-@uV? zKmt4LRtI{1T#!5@>gm{F>8O5|*x@KkXMy*~ClQ>DAeUvWUdIZ@ zdFxFE8Hx?3+uyL8>^M|Fo(4TnPI0lnzdXVX#8kwhUF5z88R)A%vBl(jNIV!(9zTNR zJOU~*OJ==fuB&FO3Z(%$T1U8Xgkhkl2z>OZGhIF(qu%EwdOl(&vuU?U@Ku7b@L=U$ zP~Nch_s7<7n1iHHdp2 zORVbP2O~9%CBCO4J*b+rz@&C|VPGIFdHovfZmt z_7EqL3KZ!~!5meEI3qb>vtB!I2*V`)8yOFuXYzebg!v3qJN(Fss>Sn3k z0tZG)*`QqjSx?E zZLnavg>R6U3Ob|Go5aOeg3Agn%bfK0&geL+C1>wk&|xFFR(A@#o_D<^&vP&zF)^@Bl79Bo{=LDb+zznG2>O z5yVYX@UR+$5u6+_rot_sXMD^p6eX?Uv)Fe12SskxVwfEzL)|#Y@W@zEd4Q=sTFCq( zABIy!&|>xD7pZ7q*M9)=lJqQFyv9+HJF)aJrG4Xf*n(GG?^#lC)I>$8ucM=<>wKZ) zs7WNA)w5V|Cz?`QAO(+{O1YY?8-GY`G<-r^ZtgsFLq`dz7;j3A@}d*!0a#*=G047&B|q<;TNb?A9?DedHZL??ITY&6 zzz}WNSGpjg*UdFHV`PtGkE&ob#i87VF4xV&Q4b7Rg3nqyEt-74jm&S2Gx-}3Vi>bp z&;XVY3#C zR%nVXH7T^zG9d{RmNJNlK)rVFJ1QbyNi!{-@qr<;&G-6287&}5&2bVR;Nr)Z2+^)X z3=x7s^8yKFQ^W-fBJSeimU8-Tp#gJoyy)cIQJrmRX)}7-K8q8dix@+pTTw(r*efG% zB|<~X6Wsdv^=E<7;VeQ){Ti5Y&HeY4vd+U$Y?z-my=qTSQ6f&MFDJY2bGhIwn= zp?u{5$9fAD1+iMU{bu#!^*x}%VUwO|RQ#RYAA^jaFVC@n;Lu)7oKWcR zeGb3=H*o&j+4B>Bu3km90@%yTERJcH{n$_?N4umeI#d)Zve9GbwFwbS!{&|D(poxdf(405Z<6Yowbu~9*vAEV|Hr2(4#22jD}wlCf+%fu5Z{$TB+jjz+J zP0=W;FC8L3cTe_nID185FnSsXO{7p(e}g~LYXwFUJ^Q11P#H)QpsU39jCB-;JyJL9 zM8rz;7hd=9zQ^zZZezc-+{Y#nZ-~@nS~w4~!JRg+K#z-lh@z@jvBn)ntUaf*%GMc6 zCzy8)EkDV?6LDULqEAQQ@^W)&2j-K!9B%gaSLE4UH<%3>9t-m5J0Bt+)v$s=qEkSs z+x})>L_v1A+q6`l1ri6*PruQ&DKnLbKSL30!3Jdadab1{S?PIx^@I*^^#*-la z@OKb5EGxN%eA6rucyx)7#PNafTTj7;ue|(KQRI*|0c>Rltm{3Hz$*-`36okZ?OD!z zeHZ?@W|^tnX*y2t?EkkH!{txX;WxT{nlE#A9A^M$2lwzC-A?)`TBOjL0Uysf3*D!@ z_c3{A7=#~##2D_X<-d~wuopY!{b0W{b3WO{k_;X1%kKXVk4V|yaB?h@soA5}4y`Sl z;G3ldIc(4}EU2W9M8vM^Y}_{S3raBe&}Ql$an1@yE!urDU}AxGT4s9FAUK%rvOXjb zocdXe&r*@W=Pd+YGV%cehApHBvrkQ6E|lU5DU8O2hS=wN^{FzMn1_UDXOXr!osGRa ziG)8Eb>Ud9vL;Ym6m4MC3e*TcwMo)_xgZ*?EMOjNgA$XHtr`ltDAtCu+t|q0HF+&5 zf$i+%-tM60Ntd})*SA}V6$#hh^eZha1FknRB$PeQ7fsIBMxl~tO{AQ>ak_v4AR-QR z+J@T*>`vonoLm239L6??kv}wbGk}l@Elu7yQV)H&eeJ|^rT;IsEQE@w{i z=6@&((s7Uah_6<2fJ9PaO-qXnwn9t=^VQ+2Dq3ACh7AybafAiD(OuMSidQmB-Q_X_ zArm@0X7!`5ebO!)559?KoDJ)+x7ZV>F&2>Y<0tnXi?L@iP3|ag-?NJwHHM@{UoGyl zg+bZLQgfeXm`i^;_OAl2veF{j$je3HvVdc>FR(R?(VI0Rb0$_B$1z3}`_^ik;tG^k2s`CT(Oc#3Hv> z<>7J*DUrVUE0z_Q`|dqS`d``(N+(LDR7y)O*h_F^1-m~E<)^$6l88q`5AQP+%PEtA z-)|5+RfdgCl4QX%L!R`&hIQ(l_;iKP<3z8Z{vn1g<9g(jJH5o&^QIaUjmKWaNX@Wt zwYI7lZ5N&~-!6AzC!5JP&U+o6;ft~6-tS1a4#Si0Qk51x+=)N=Bh&8>s839??gj)A z0Vr=`tr;yoYpZAK^w*b>#^`d@i1Fmclb#a_c&I&vnlU4 z&ftyyN}s!&z8$$T)Chhsw=3r^Tik<3C-SaWZyv&s06H3t6!@c5>Bi-8LcT56BI~UB zq^jGe@2~X*vi=+R+$-pQPqi)dhnt_~=Zbfoy~?QgK@XZ^*l&Hm_nU~H^RJz!y&qZm z9*3MAcUA8SHq$kK?6OGAHQb%)f1ueMe?3h2@pj`2W#2olUsKl1y(e(pTyAa%%}GxB z_Vo|s^`3KE9!GW<-e-tjQH;DbG}DcQV^5o;YqU?hly9zHQxQKAh=t_TQGO5(VvJ$A z^H6fs?>-l_6>}Ikss%?V&kMt00lXgVqtj*P=ApP(DS~6Le9Fw-aQpo$ho!3!E-)T6 z^ygKpeM_4*GK$#kDp8 zEDd<=15;Ki)>t}|w>X`FT(mJE?GJ|?Rn-wl$p`=F3gd7SPM?=tWnihjz+|yeK}Ep} z|Ln7NY{eL~Wb)2dIGF%>3VnL8y-gze7{bCvqn`ymfgHl>g+|N{adlNGP~4GIu;L8I zPTPFsxW%;0w476~F=F~s2ArjVpCe!ehl{>R47)jPB9-^UyEO(6D_?$OR1&66~WR zs1BYH4I)yRFwqOa42^9LFBnYw)!SIMO`<1CQq;T}rg*rjKVLdJ66Y7nryCQOSR@4v z8h;(i8Z*a8T75VtMr94tE1MaFU8;_zqI$IxN}c;eYWvHDKqoXH1vKyTFA$?#)o00u z&GO{ll$r^mJ}KL2kYe_sl8?IbCi7YwhCx1FQTMPEt-(J=wA30%2Rl-2&~H zIHbhXkWfj(wuQ+deo|uMAo#MW? zW_`^t*Ufk?{LzOEmV#sU-VTNahXxQl0-MF1#uOQsYy?ANcrn80ek*P8Op`FuC=l~} zv7);v2UbQS8c(sCo7)vfd3#XB-eBBrxab)(rp(-cN_r^}m+nzvh*7t3T+bv64o2p% zGeCO|g&0xMGLAfeBC^bNfr_2$$yC^wi-lnc@5KJSp&pU!sanfIFTZz==pg zRJL z?+NEwld8mtEw3W$QQO~dh8R@aJrRG$hQsI=BC(n;$=! zctq?mXVWe@8ux-$Rx~=_R@n>YDQud?BuZe6Z)4_?BaJAl3WQrTKGXw`ui~kP;mWIM zran^3@=#>WqqS@b6Vm?E=P5;i&G6Nw^n^13pbFgk_>#9H-lDBQ1@2xE$C2cJ)}KIu zrL1VzTcBSyktZDO2=~S85yFt{ju~#fkHN2SsT`^K`|`|=Dxz#F-MX`qyu8x{E%pk0 zoaYxig9{Z`6x0lC%8M-!iBuiH$yy+|u?};{`vDSj2ZD6x=PUi&;fzU;tM@P+q!kM0TDgJ?&)-hn>7+>}&${?CX7}u+F zTZ*@t(K-Vsl1@y1!LhiupJzc44kOv5heJ+F4ab_%lrvd7As-Yq7Ilg+%h(?cFPbOM zSXGNI35-;E*kdc1hG^X2l$syc-;I6JPCNH_{DAv_{-y&n=yY^ z(M~&Bqsela3>Sk;3W)Fx7K3m}f=b)ytw8fej@7}ZS#2QgJkQ4(M_vIRuQxMlN^{4K zwQ*8@ts}KZ+>04FTFY75h%=CmVFysQ|F-519Mf4>l!@j2gs-&eM_EyrY_h3Cp|z?C z5oK7b;A(ERIS%%P?RuJz8Pi+xbGIR;Vk0wS(hTbGWiD|c=Mu&Q#R)*On#q#TbA^kV zl+d_)p}8oZll^l@7!>a0gnDV9En->+xR6FiwL)8h=EJ8Q?`8238V1`{!HD+G0EwKN zc*ugG#lpL89CbUNiNrM6AX8K}$AoGj9M!xkXby)|jJ!a~Xh~izst=?b%b1(J>LMe$ z9zoeo8Y{M^HgOzMFE81kqEjpNptgt<{1}__heRQbNL)ntF`T%9i;!rAF+h(<)}!KN z&lN~`XMEnJscF3ZBxKtVF;}J8>Vy|2Y{vDgA_^of>X=nu3IIfx$fBnG+k{O(d&=At zmTH;5Y8l&XYP*B%5qL^7c>f$If}iF(pTJ&y7a->_VYQEov|4#o{KCBeNbtGZz^-bG zpf4F7^LPWNN~Sbw-|UXU+k9+}3-fR5d8sK3$s34#fZ)G!ZoLt_iDURUS_s*;$wWm^INySM z3}vlu_DS5MCUBpfkasT?6AS+Wh+a5gf(-d##3f#)rebu0%nm?l_|0>Lv2MR=-1x~p zPOM3kfa8+wIRAPA3fRp(geRIrVGDhijI0HoWjALYQQ>r1K+UOBMpkHjDAX*<=)7Uj zg`T-woJ@)3pDRkT~=`eEn=Q>^{{(P{ToYA$d`133(C zUyrCw`9l>dl(=*PECLi_0GtAHk}haS9Bv9|u_37-dZbUjdwKEaO|#xElZ4_LZ?7uQ|cqU1M|FOuvyO+Zlhx=170h=((SSoUos>8s+0HbcPlvy%s>8zoZ z3K0Px5gmVr8$_Pk3N$_vU}V*)A!9ycvTtM^?`N)xtAg9%pm_yKhvDhBEc&fr@ zGca$_WIw{*D+JC31M!EAQ>tM5@<7nqN7wcIjt}pZ&ZJe%VuBqY=4%LO&s_zn*5p1m zfN?%7$Oy?r4d3Wt5#WhpJIt7zDGj7ys25bEs}|&}j{tExr8sOFF!u35L85j*n6M8{ zdJG8$c&E00tdW{zSgOI$GdW5toyS|ohY>LY4=xU)k?Ihc+JIqB@1%W`DK9Ewc7>q7 zV0Nqs7&l9^+^1?U#)f8l>7{fmbN0=|9Ka<`S*H58fceFhUYk;x3s<`kWigO7DNA1J zl%?#_twVqBY{59+n7KJP-1TA*DU@3XAcQS(PM~BV}{0xG7Yn4DMfyK4x;r^Ipu;kg#jp z^_Ry=^WL=j4aJo?z5~{Aqf@qk>b&unAJDh&MYrZuxn88E2&)ejSRXT~%B9fv9$=h* zFrn4b#XLSJ40hB{IzbwTF4&Z(ppU%w7J3Vypl_XkC-D(=^Ei`8df5WF6xcS?0W#uxDT4z1@9*NvWGTu zeA^#(C|USxTmV%}OJ&WjYzk{!-fST-liUOA%B%U!wi>fGZWpwjR{hEL8uLbF&B`$x zOd9LgpxBUN!RQj8GXPmkYxvhO(@^)^Em{Ye8YyF)9s8;d8L0-2&MH*~JXCTjocdB^ z^GL2FR9=~GH;Fu!zSzXPevj~a{7DHSO5yguV!LR=WYO_#IlM)2-DZ`wf zqip7PeGipu)r+fPj0b%D)Cw>vJLdiNs_jqktS@Vtfa)1B@FLXNFk9kgpx&NKhAVPx z^c?cjX!eADTI^>}y1G{E^h=p&Ck|_Y)+_C`o*WY8Scl^T@s;g@bWOG;ic42#Qt@DA$X{8fAn)L(df`_8vS_A8r09e0 ziuk=63s1_$(T?7SEFMC0>6&Y_w)EuY0!p(6N2uS#isze?@7oi4T%Q3;Ew@wHZB?SL;y==|gC>DbJ~fXtg;R%B|PN^Q#?e|4@sICBs%VTA@!l zE_FAL;oF*nt7Fp5ot{tPr?9D;oCIks71FK;6Ps))Iyn_mAO*7kz$Kwdx0$Ivx(uB( zrKzL-&0R4PRQJ}45>;h%*NXyg-+$2JQ9=EfvAUD^&rGW7aNCLY_rKPsR?C=ti7qTR zFu800OmlA2Z7i=L|D(Bc%o~fJ%}ZsUfo3x|QWM6?`qKtev1gZB=N_ zODE0cV6Aa}wiV#*=T6$HR*eeowiV+4!!H^YylpE+{^LtlP4>6euJfPHx0kN0!nk}L zx>Jf_Gq&mti*pSdOFS-(ZMu)n&NS;v*B-1-7^yjH|M2z>%$mzaUS88}&gWqNGi>bs z(xY`h(@f8%=U*4~DuaGotepRvv7}vIb;#y*Sbhi0i0S`z>W$(*8ZS0uqw1vOY*(E4 zrwz%g8iP|;E!cwYpdMn($O2QwzA9efRYaKg&7ONnLLv4&$WK$9>mPd=bgP%vj5Vxx ztYXOKH#;2D`4h-T5pe6A_2Q-*DjUPP6#eOwK%oBLV6<34w8c`7Zh8f&VVof8A3|yEkJ>`t(C#|kKZZ;o|jDVdw7E8ok z)TD;hE!GnJ5wsn*erP6qkoCABG-QG~T~UEz)$#~T=Q&!@bc|r9O;p)>C&q=QOfQg%pz@<2JAgL*p(Um@ ziz#>s5M&)0z?SoGS=HQNzUrg1`yxzeN!h%0-RaQ`OD zD++GwfR#$b4*bD*B7CnmBS~5UHnFOc2m8IFp@WWaI(n)E?4D}znN2c`@-gavix0-I zDa}^1f7v)ztNXkg`#w{6C&jTQ?bZf_D*I@?G{V_hMd1~FItF?0j-g|W2+_m(!-}#* z`~8_kl0ksday$#D(($tQ5(STti?gsne&mg#+Lpuxmx4-!E0Eps0+`_h7cEr_kwYL8v z?^GEqu-Y)#VA|KM7N=$BBbU=8!y6^p`}RR1_MBN&>qDI~|E*(Z8f}eO5AhT$6TZ=$ ziGeZN2n$D`tJ)Te1m*u#9WINOpt$enqFy-yyEs7tpajqt^H}3Hl&j&wXZPQI7AX*Z zX+W?AP@O^c0e?GDbmsI+nq6?h&OY-aH<*1f9w=|CjDj{`jR&6_z!#RdY6X`LG*;(m z;!q?uvf-i_wy)nA8}eEd+x{Y+IwmpTzEQ}u>x0qP($i)fNc1NMvt?zxcd1tuo1e30 zW(@NnAzQO2*qxIe@#Lk#pV7=Km836MvaQ{*K$g zhO>?_lztC}CC|cQUJHlkjlkCS!)7v{E)1N&BH<=Yg zDF*KP_;Af_I`|@80^( z0uQyU+zV1HFQBVEJ6O3KM0Bg+zFLhDzMB+Wr(#RwrWb^{IoOiLaGOdo*aMxym2MG{3*01_1^llfml3TzrD@?Q^gb&!ew zL*nfmuNWdo_{{)F>zc=!2Sm1?Ve?2bWV&IIEyl40Bw5m zqzcA<7fB*naIiSaQc|ot4M8h3@xnO@*K);OXVgPadNZNjCCbpQAY_5I1qV?vB5ii- zgO${yXRmgfA&|bktVF(-*D7f-JDe9Gg(qblMHTeQ7)1(0!;!wAePzy$Xi%GLFvHux@ zs1MQsN(;H{m0c4=(WC^N6g&QR$q;zSkP2W*$nC7`iXy5e1<(-RMoBFihkJz7OJgE# zJ04s3e;xQbLnb*&+M~5A^<#z81u9WOPK%i-uaq@aG{t@aoDdXzLtt#AIoZ<`&(|eM zg|(5C1&iA|AX3xfyq_^(aeNB5z;5_4iU6ROqNqFwy@a79Jzo)arR{K%vPTTAX1WXZdyaEG;U*bnt!r}_pg!VNp z$4^d%qAb*A!GlYN8e$PJCW0zv5h#=n5yZ+{4v`vC!GeeB{RP-x`1>D|#=0d)9ySJM zNEQ=TrNb>kvMB8@4pO0lRc&!jiAv0qLlueYJ4J554#I~vk6jFj%i*gSq z)6Gj&42UI+%-cmiHl;KTiyogj$J_}5?DFzizg)|ZvY8hTNJEN%f!TWi@=gToo+YAR zjGUM{0tu2re|`cBS>=yBIwMxB<-Y+(wP+46WT4p3hW-o zVU^mX*Rn=3(GGM&KKuQ0e`Ya-)8`8`Wm1(w7z}D2!TzG;N5J+Xc!_9Do(0!sSPR7? zHHcG5VWks!;aH+Lq+NL$GvCWIp~+CT2Wvg5(Fw5NZx&R~j*{+3tgb)AQKp!9(97~D zmIpJL8Sh%@wgkLDR=Fpm{1B>Wu*$5Vr(Sjb&*Aj+H45?tPq8z_tH!23HgMkR%WrXA zwuEp44HMi#1~5S=hfrTe2&Gcy z#wbC48?Bi1(~>ze8BzWiK4ZFfxOa+A=_hfifM#BoB8LL|v;j-8F;Y^wYDVOOqBwJBXYZLybsCZfNfm$hX_XULRj_T!!6EdOS z05*MK)~ul{<5I3BXyPn?A%)3ez;`eMMpyz0o}KK3!lylEjZ>;*AuXrSnJeav6HCIDmJ(7; zO6PNfdc9;RJi}&WkePrjD)7CzAnzMS5ka}?OGyT0F_#$19+^p>#j5f5i`fCdDFU=i zp?qs&=?w%-lQNzIkSV_F{RPDuvxV0afd6#^)4z-b5BnFeF4kB++X!OXD2tRVD$N!i zPXbv?OXB2873zN0yVYFu-Li+Al|U&Z6*85oQ%0bjeVUWkIO_i~ z^^W14G*7&DY}>YN+qQqPZEtLAV<#Kib~d){WMliu{eP}=o%5n=x~qC-UiQqVtG{)H zlYXRuT9RBTGpErW zzEbmn8Z5aIwyi*=kV5D@-pbQOOIE zy@P`re2{B8L6kD{dewf>){XdyW@LrxT;Bs-wH!gxKZR=Abf(WXt;npZ@LuJA;LB)L z5QpUm)H>^PmvOH)hPdMuKy|ywz>v>E(DGuH{!6mREM*sw?2t(IB z_XSuB?|PTft7`%il{1$n_YHK__6Ty-^+G1HCcB+Rv(SG*5Ab8gG^X2u zbR{AfaAU$WrplrOWeZ+LLZu5{mj4;kAEP~=Ak3393Sr8V)59z+%u~?AEGx`Y(!(tO z39srfsTu%UJtFqVIcq(sCa7RDl#n5LZQg$h>NZ4Oa?OVBmn*xfOEZ@&Pus3&eJJfacuxlj$PV#hWT*X(32PwFIO=K9l&n6hjzR^k+G zB1~2<>uQEpNZC%0Inr`b*f8D$Sik_Jh^VzH6J^Du!oePFL`_)}TQoxNRdw`1%q6-o zOA}Mpj_)zHcfbz2(#Ce^tXmeJiBZy3kjqBIN@PyANMtIQ@xnF+#!hRig)T8kCyx|q zs$B!Y+`{iR^b&XlkK>Gv;nEK<5!>&aX?JQw$7JC9%ZnDrWk9EM7=tkIK#?z1CtYdi z9rk9?&naQb4IBa0;;F6ygC>qF*OFhtM=VbNcKq|t2!uk`47O^nOQFv5w~{MI9K^Zw z`CkBXV+=)C1{*M}7w)X$fL6Pu$A#rr2X}SHj(kUcCxt6BbjRG#O<4XcC!C3a6)Bt7 z0~hHN)D2ZMJRWOZjw4#%WtPl+VL1N4`7G^UIhN(#7K<&@^pQ5*tb%E=^4 z$|aA8eu@KykyfElR-Xe!&mFSh0%wz42qmk_5W}LP3{6RZFW4=BpW!A9tBw8C&yoqH z!%7HN8!HUsQz*Od3zRFnpZ(8_{g}pL;$T;vc=$0_9zT<$AXgDTle8dL89$TEH{8p~so`f&Ux`vBigtdMpjZP$ zRV%htmkgPr{+ypN{4w0m*ppSRWX^PoDOD<&bx@5)egv;Xv6Y$kzxC)0yJW#?az;wa z3`R7yp$e<@uU^=5<8U%2ol!CjC*Ji>frO){KYqE9@hv(*FELy}bLIcodeOq^Ot1MAHu9Hs~@^CG*Ghk<{Z~CJRqPr0HM< zA~~fysv0r>q+MXFT%WloiB4X3yfIuz?TI2Yy%TC`1R;C}DD{+Pnd&yv8ivDfKETvepX6p6l!lem2f^{sTGfilm8lP?YJY%MLG($yrzcnY( z^z2#szmp)i26tapK?e=auOY`mc6YWk_V4Pw4Y<|5%Cp*t^#e<%p z2Cj)x7S^Vy{JNBG-2sS~AK%RT_Z%u*qJ)-Wm84`!{_F@)j#QAM%G9G-ApX7+sZ*th zO)d6ch#X8qF+!&`n_rbW{z`~Rkv1VY3nyQrN*)n`xJkvYY)XmJlJrtAB248u+Jq24 z2D}zJ{E52Nt+)WkGv|QKun7;WuPLi(MP*vig~_qB9=KsZoc zor#aQ7)0vxt~?M4kZx3;U076|U7Y+s?f;DFS002Qm!olMboPuLm<%gZ|4m60nYHP7 zuNgT7beYs#$Xw8%$PJ)8HE{8R)dhKL_AvdD%N53oiadHE8r@$HXXtFpLOA^3(&h&X z68I6>v)I^f9{vYE<}I zj;-Nm;b&Ebta8KNR_@i4A*#q>JtijvM6@`dudB7(YZS<9cN(Fun`|;2D(#58HMz>?xE3$(zH+Py6!iDy(?>YAYKm9{v*s}S>aEmNTvUg`|0MYPz& zsuHi)uqzE)b}mby7hV2*b;tZT)tTq*w{a?ir8>QAst^i4Ok>^9 zIK7CPv0<*IArPz`4yt+S!icUg{-NoJu0J&0vz+EBZ)HF6Keuef{;n52!GE*oR;pNi zDK0Fxu>~6Cr}*}M$oeMge_es1AA)`s2?bQ{)0@H%6OQ8SjvwQ3PU&{W__OhuF|G)Vw=ePZhYtr`%{XbA0&h6!gwM+hw zwW~<}u=e}KxnuXaRl|ODPx}`mr-%>7fH|^Ed958ts6~{&YbtQ24Th*tevz|0Mf=gfEni1#W zD9<5AUKgPi+$F*FVt|XlCxvq6>VCjBZtah-=wG{YAV$-Wr6*e!pPLSk=(V?}>31{y znoHF??Pp1|P;{&*SSV0|_O83irO zM9?g$D2F|xMF0adox)jN2Gm8y9e^5oo%v)Cs~NL6LSN!3hAABiQ&$bZjJ+)Epd|z5 zfrK{#mBt$_e?#>XAoWy}3v)x_qahFCg(7H&j&mAfN|%L*N#dJCR}DO1Lv$%Iec}4q z_qNd;+n}{OJ;@eg{=$C~=gc=Lf&WZGU9O)GW8sj&$J zFROH{*P79Uc1Ywo!3ytlzUOY*A+d^hXqAG{JJ%%(i}sAb*KtV10+XBsLqZU|Nq>0d zj_IW{b`VF$99=tW>)kJS%(dk87zkCxuQmdp8}#ANS$tFZ1qHx}Sqd z&U3R)rN_4SbN!07!F}aKp^29s8?Ec2tziaiu_d;Fou6kcWvchsjYbqA5qWp&QOL+185WnR9ARWR~)3f+_~mrIVOJ@QR!vX{KdyyS^h?%8J_gJ zf2Z^BaqMUGe5bJ!SuZul;T@Fj@D8KUpW4=Zw=>ZmKk-wr+ zNNzcff^T5VV1K1u$jgoUV1-w8Rkn)JMg9ZK`5e8rcR6ue*8$~`vctmZ2_c*1W6hOS zK4OJTUE?qP`bi1^mBAsEp+Q&$d;picj2%fj*~lM&Mo^-6u2{hmB^Vs3KvoMj9IK_4 zt<$inHLsPMp%BbkmEW6F>}_R-wcA^u#}JaEH5u&C zF^+V=0y*N)A0$N!SU%?Tm1$a7WWZ$E$xfFwL%3FMo*6})SB9kjrr75xjkC%wYeW|* z1fvJNksOMfW&TwecyE3!dwUn8EfAq_@7C^T4V1AdZI%u6XL*XeTem6yLr%q50F@?B zTIShS1U*S3FRVhZ$wm5NCg?6%BOlysPF*gj;gn`Le;RjLOZx;795A2kA##YC8)yxc z9H%lZUM(z1W>po!bUM~a4962?4;75?{y>DSLtm!x+BL$9k@B0d3*{&E4?%?B9szig z+HOcvd=x>1u)PayM;PYydHd0mC!0UNB;c6+9G&A;F*wp=MtFR33(QW1?BpDtHXOwb z8JT0)#b|&7hcS)T@*tTLw_WL)*GZL zrd5lMc>+g3r?thSYNLig^RnALiT{d*T(!zo_Hbp2N+ne2cSma7#<0x|ZI=u`KsQP= z-@wGZ_4_14iL?f4J~8gQ$J2VZGZwIv&2fSr}bMe0VXE2f#s|>5m;P=-NDJSiLbUPAKW7J{u(nR*u%2QaAJy)D~JmWM&Y# zZV}5RjB&u|M9MFyD!rWfZCTkgvlSXW3%mQRfyjDI?71&o_;SN!j>R*E z8#d8z%x5@-A=~7*yMk^P|IWyVlxy-*mtRXkREVe8%`4Xiaq+s5F9;p_XuXn!{DG>0 zNXRU>V+(`(kl}nA1j=LFqLSTRYo~xNFH`~z!_P6^hVXn5|8g}Scb6P+DZXW!g zz6iX&=UpkQ(Hv6ArloJEL4xL$RF(K<091M5GZ0leaRrnJLm{EVcO=Ji zWAq)j&z+k?1wM&e&-37U%Pg*Z&_>AsJ1N?t4FJ3e@&;bW>;u0RwPuqbC_)FL96jQ6 z&eaRsHz-#`Q3Y>1;!7IKvE%~*VapcDQApVZr@X7~V5!V$BR@SSHsN1AR@03WqQfi-DWuuC?|czK(BTHI#8uCDN*G>dpf4sCzj4{t}zprO4-M> z1Ge42$&N7V20s;{CLl^D?%;FbofLQ@>^gd`St+r<>!R4*`a&@QH{1`FBjCpT^Ws=% z{Xm`A+AMjazs8XQA-EAnm6hIsvZ&=Asr2BlR==WAISQ1Yf=W2yMb#7tuJU>Bhi%&k zkm|KkOCdb4pm@KU|2@?@GH-md(iL-4MFR3g z2iG1p&|xZ+7)YKM5er0v(W5yUSql>%QCyT1ETVlfE)A72H_7#Ho~7L(SIKY#>qN07 z94UOJEnJC#?1(q|Uc-{K2O?rEqHhE;EIfMfWnNja17O5`s3_l~*C=<37KZ7t5X_&q z9htvkGR*g!B^~8VT13_etN7;p&N&54#hZK2>95UgjQkR+WU&m0o2SH?al7lTfvavo zS%g3SzM~C;P-Oz(oP-Q}wQ4Xf`u~c)c6k4Lq_&Yplw5v)6gP*@VLbGqU7b~Gc(@v& zyp2y@8;8B1g#t}mqLk3;qJe#~FL6`1tH*8Tavb?}jv*v~AQYBTPGO=UXp?>*BZn3^ zR~N2{cRDDpEg?_O6LmJHFKV7Z-A=^BVAC1K5tngp3%b{kq~wK(Rz;>IjPB<8&~Ay*l~yWEF~?`4~N%=I!t_KiWV$Sm7m9q z_ODU&Ai(W*K#wPyvB`C0aFhTkz!)t1P>N_j8-vwj;w~duRTnO6@7&(Ys;PQ>dd;*d z_ZZ*`Z350B!;@v)Uy>jmy-BN)TA0X;${Jd3Je`$<+>>zY{H(HM=khoIl)K>)iPL`? z90^&+i&CjO*b)1T8}s*ymjipoGsMKBpQ*iWggSSA`$;+=`v5`S8Vq`Y4v?*i&w#MOa=5p_jDox4N#dGeTYmPuK3JiV3 zl1}J$95Y&y zy;Ai;m$jy;`+5@Y%@#Ud0rhX&k)xPOY1HI=&%sPVY~;-}hDNWO37#hH>NT2g&(PYT zismfS_)#oVxhheZYc=yU7zCxoe zU~9^!R1Glcv?{zSwDUl>#h3~X05PR^&`acL8b3Dl&2ZOazy+@ejYAr8MGx&FRJ&D` z<@ro8@l)O^UEp6New6+g{uDB7fz3joH0 zL8E?kb9bx5tw+vNnsUhKl;4@g{qmZ=CEWltnZ&CO3P7>XL!WY|NIf6 zajnehv{Ue8D-V{kMFhmH__71}CUK)pPx3PA+a~Z|5T&W?ffB$?jCR;jUB&xiAxT_G!TBX$hlF2~ zo6#L4IugxlsUN#ru@2Yn-Q)j&i6VUx`4OVKQ|U{<6=~BL=KA_*5>1ldBV8-HH%*As z-?7!r6Ayv4P@j9c7mGOD=@#^d67j#L~T5v^p$4~Z{Bizf-#+2jtg#=mH|oV^G^?3 z#nZWQL>~Fm;Mt*3ZizGLZ^vs!-kCN_=N>BRxj{5SpC*|k)J+@fRmf@yLxOBOBR{ob84H@laM})4 z#^T;uA4tTcfJF~uP4m`40E*2gfz&2$>7cD|R|$gzTm%jI zxsSZ=R_O^AIL?;v$I?63Orb`n)dxlSLc;^LwN)WVN@_?D%TwRN(T9bfvnvkj%vHOzNv(g zZLc7bK{VdokCLqBQx#%GGs-(-2Ag9ku=fUx6xi$M9qb~L97pcZ<#AfUq*pXX6dr;- zHUkul$-uw(ctZPdyt|cb4ZRXQ95W4EXG85<{xGT(C2v}P&Og9?xw2(&+tSohQCQ~u8MZiVHea32`&E^+|PKy3>EVQmw`pC zedWEgAJF@#0qpJbFgOSIw--Iczw^2v{Y1s&G&qBRagN(I0?mj%vY;2^%4=fH5*)V} zi0pSy@sGyd9}x#5-xR4Kx=}Q6l;vFe(#Vu1H^0^rqtGtH3n|OKCoR zw*33FK`sQ_cB}`V`=x!dH@|kAyJs#5v)&_bjMUSbplSXfjphwGVH51IywTu}4eDbZoB4fck-PLgP=uGBaAT3!1* zlk*sx;HWNGoE@vXFZeMAMg96=I;~fsh1+vO#0<~W@kFPMsT$gjjG$7BXKV{SW?TvW zDUXpuHtsOIjGXu+ID)49f3Su=bc~PaegeobdDU}LJdD2dS_l~D9gi#g$91_m>*~$F zu~rvC>_-dkp=T#u*>dJZ`OiV~ja~~fxGze;qy!~r@G@hg;}tl8_(A>|OlRh?zA1w> z`6WAvBgY82Ik41}=fj*gZTTA7V9gI9qp1PsadtXqFhx;?rMrWL&yz+mJE9k%q3&@V zg(7GEWN$jbZhlCEjd~IIvD{3R#W)dc{VE+AJ|>hj*cSTXQo0fC2ZqO1M7SVe$jn!u`r$fifIMJ`kzgGCEy|J1eq_IK)h(-FY?b+E2ds1Ih|`iS~l8HqQul zQ9)$p^Ax@;Q>n$<2~LaJMrTSqSY@Zi9Q5+)aK6-K7Vay_H6m$RGQIdHrgnVj@2*xNkLd>-O)X4ew!AJnstevSA|>7T%wn(oYh; z;Wz)y^NSI}>Nz@0@$nc?vX7q{rFz-$;tb>g!Md}zWenEF-&!s?6sS15Ngk@yZ=T3h zg&_4AQ$W$FecS0?s8?4pV)nHf>7Q=otgV4Ogy?M>Lg17K1=bh^< zCV$r{0njkvu*y}F6v7^ohF&5Oud-Ie}4DA3%OgcyA2aA4?>1<1!-E>L8 zqiDg=oawaa-5$;#;YNn0w0ey3p+T$BcC}W$^e2)MVw!erNtcZtK zZvv*#^+*G4A{XjxClIJj-+XhyN&ps8)!+3TCDn3p1fliF$O;XdyuT;Ey%NU<2u~?Z z8MUrkclb1a#e*wNsGq)H=}1~N8!=NX(UdOnNR_)K$o_Fl5Cfc1)nhPpd(s=o@-psg z1d4mwXrUV4+hO61fHwII1{BH_$|ETWu*o4ng?XoY3j@D-s;iT#zFZo&CJ#&E?rz%jytpUDhopZK9-CwzT})F`m`@ zj5ZcKP{kvx5%p&pspTzBzMMsMLGp9!7{b)FC0Fz%PW2+c5-tq3Gl(~Zq5*)7iG#2e zE&?kt2PX7d*#ppM0!nd;{T^+gyxx5BO7mzqx;5^%yWf)V@k@ z-?7uptq^aw#|C|YaDZtIzk7T?P6nXK9@o7X+TM(1eGiLf2kc^gO+4TG@AWYfU?6*+ zrh;6n5avjt9rJ#H{zU+19$nBzS^|6N=+uigqj>x5b?}!Lv22ed!?W5g00h4`FqKz7 z%;2fZt-cZV1WAZFZx4Php$gIDpWyOOX`I_zkyOp%aDcbed)C~B?h986=K&hK5tcWp z|Aw^~MPOKjbC)e})hW2}PVnQoeT&vWyFF&b-#y_m=!FQV(njh2hSiy<)1o_-|x<=Mt-X`#naTCC8{&rZTZ@`I1QTQ_b(-W~hz5gPe}^-LF~2t2(~vczBjKtgwrZ3tOyxbB?MFVwk+OkKCXY zC5!Mq8a$(nge6g^fl6o9hn`=mhFVvuv}27;?Hi% z3K?|m&*$$`+OPuX9(Mn3&iXzUJLu36WNscEF(rWGQy+lb#X)aMFh_Z@V3S%schPDo zmZK9^{yKj!A_(0KuS6wr>G<7O4aVi_90nO0jRG+U5%XrqD??L?qlb~f+cx84g|Zdy zrHV1r8sq@9rtT#uf^&D=0lMA20yp>W%AD=xPkWL@V+=NW%5s9fs}$9n{#D8(QA{u4 zcqgZ__o7SY!M&zeq2`J%!jx)yt7f-ov%1RVnSsG8`K-c%Mgeg^3M^xco!dcQ(OJw> zTZ~~2*u*8Le&^UkFc860hk2B)bT=b!?IzfYW`~nl;!xQ`I|1ktXbHN`dkG7WGBH#u zUbQc5r4>|L=KWXHtZ&y^Ia1Z2xo`ivgTB8^>z8LBRBRSku@l4XdJMMfHN#1kgsyQY zW6)p8^ts5Z-?U2|$+BxPZ2M9t&?S}IR%$Q`J1}TFcyLlSo7Waxn`c>>AJ?H(>pe%* z=)P{$Y%`%vn!j)5wsZwdI%4E%bZ5}QLtGRzUc3fPg%KSs zCw~z)7J5X=xa5f@I;r{MAYr>nNK?6Vgc8NeKN+&o(j$zJiae95M}4b|Op3*6s)s$V z)zT$4%zoawcHOz}&O}r_80;BtE8ty(h(I7E*a%iX<_u~PvXFu7rIA>(~Dr%yf!U7R%WU@#+zp04?IJj z3<`K^7Z@AT!1s()C#3+iPuTl;xGL#;uJW*GABdu|KlO>hLlzP&V|?@1!-&P-%pjlU zdqkE1xWZk^8eh;q?6d*}gI5b>#b0Ef0;hk-lY0!bq}_UkiVpF_Do%v_C++Le5Jb$) zJ?$4ha1_%y?U$V%G6|rn=w29jyZ{Ei z@9Vih*C^v$DpT&QCdnn6Fn{}?o%}r2zxo$>wRhwrLtnWi4vO!Hn520wjVotz?IXg9 zyrZb7P76=kDs389QRs`PvUtKs8N%_4jvSWogX2S+>fPehJXVn3=V&@ z-FU1gL0FD4SPCu&{Vp=m-e*Tnf=%Q&;kv`GAe7UCB(c>kqQNu z=>}xx(%Dl!21JfMjHy~H)bSy2moxY2hR7Td<}vwHE5yZjEE?$9+v?^K3=#Tm7%3V1 z5tF!EPFXlRU(l-M97vcdvq@>L1}<@!CPMdtJW?ojNV|i03HE4=AzPYC>m!%&TQP?lyb_n{Vbl^XDLG17HN+e)ApBN2+k|8kj^ zoF;EhU-nZwgrN8`9i2X{b70PH_q^G2V>C_RT%+ktsB1<`lJRlG zIFYb~wfgh9!(rk6Nc8CDQ=lG9=wS8WZ-qQVrGeZraFcl>IuQAgknji)kY_<^5`@=* ziq$iU$l-v3^a)Yv6zD2;@pY%`{*vr>Nn`owsN2Y(cS5rrnW00d2-7ehn!RVI8;zGF zMN{BJ4`$@JuQe=}oi&I~=pD=!|GRGQ=X?9Mv?2H!&j{&7eIO%I?Lgf?>x;cf zPpnIK*br}UjpNrL#-Md?p3bKDLFhe3SaoWz!8PT}y#DY>`;1lEcMun*DzPj`Q~bmQ z$ANZyDWV`i0do&7sG`S^-K_4!ArkSd4;TJH=G}|huiNUO@7-)Xu3x*rLc??Z8lLEVnOP(*IUH=PGHk)Q zX3eQ+qw89KTGRTcyJ&S*&uwRkW6JAmHeSKVt}`mpdZk&T_O|D#9@TJI3oIi)A>lQ2 z>^wlTCot$OS<-jv^q`ok|B@`ubWDd+!AUNJkkxlCwLFKpPjjh--7PK?Q1SF1(p+LGa5 zix-?x2ARC8p^{X7fmH;d%G3b+>z$VLL-9c>V5M1nyv%MfCg^2%O=;7Wp#1`~hiig? z%D)~_W)*^99NL!tNMRx@!<_p2?tIjZ6{td!a6%lD^`QZ2$Wn&1a}{OzY(G!i;JJI{ zE#7sC)nSu2z6l9%_Bx8G146Q3B2f$taT0ddbkK5%B;t=ZwWC06m91d` zK_nc_Q}_P<-1&wNr-IY(FXQB~+x=}8ABX4bT=0)nl??kE8f;mR!~Lc|9QjXFqZQRS z^0yuo+u5N7mkHZ1u4O#&gRHC6epD2@=)`{TxT;V~oNq zmF-4oeMA3%7v8-JQLMB{zWs3Buz6NC&5iqJ;aOEfUIdUrUdn}R0<|$Ptb^{>+LjpD zsyt7~w0JqqAsCO;icNDS&yh~BRBlGVXSU9@?9&Nx|b~*tzfLkT@HqZgo;#jv3*MZN{DI#uyu8zY zH!aF@+?J_QEsnyzSk1k)>}rU-Cdd@A&%l#&s9zXF&Z(Qu*E_GTzncKZ~Fqiy19+%zT@4B*71b+^~ zCi4bvxXM5d$qfYLV4dU7zNp`Z6n$N0U>Zi%pnX4Y-1%~%hT>fFu^D<^{y{C^%G7DN*BqBVSVQ_>N^R6~ z%wBTYZEvVCI=kV^G(mUz)#_g@BM9N;rtv<)C^)r2?RzLmU`|D9R}2|^XkWes#TlUe&A%5d1B>u6i(aK(Xre)C z!9p>6d&ZbZEhK_bA333vZ;Mi(Kk}+7w8s9PxIU4eO6a74>s0G zd1gJ;J80ok@{ZGOcI7rlN3xWTjhmqB!m&%6hAtlsvO;X{y`O_z#``x-C1vrP6Ogfx z#J)V=Ipz{xcMN(?yKh?pA)J6`zk~(w^>8?Wofzh>~r+vid_FU82pK zvDJVui^@V9WhC`=c86j$f+L+tj4oj1mOZ293dj5~#V~6bhuhiunR<=mA0>j9OeGmT zHe;E3pL7`|=M)J?u&DVixOh~>9U%Rl$FFIe({GQ(DT*A$-ErkU#f0}2_T8Lj*3>-s zqLjo=&?!!{9P}Vfg0({5I$NLkS&hH=Ls@p;xB$y4j;j#md-zHL^#B(HMZO~Hi~}Qn zZM#!Wq-T;wC{3FLG&l(7zi{dR9u8Z=ntj3Vug|ZYPvhHoZ-0k_Z$G866Bp#~Ytiqq z@A&WM-qG(3gxEy&YdP-px2&1m1&bfXIYEh{~t0yr%se z+$rme-_T(kLPZLcJdFTj9y5rP39$I6C3Q+RP_@T<%MMIb7zB8M7vXGt2@;yT#W=e2 zw1(QoNdP2w2I%6YE5x@sNTjxvc!>ncd`^U%G*+unSu5ByFNvQnN2X@@UWrrL>OH(C(0FA>g9gT{Wp{)FFVZ~+8=MJ#*Jn`QWVZ7MD znN}RyUvC$azpK*Tmuhbcr!psN$^vv56s7M4^0uoR5)jgs0`+(r!2#4G-FF@GoQ_IO zoo+WJ9`oe1{>!N`j|&xAnmQ9*j?(+NCr9>Q%G$TT@9X;AeRS+|3G#b6%BBwi&>EBO z(8%AX4mx)DV>2!^M^TX*J&X`i^YLxrS3k+;6PT2G+sV@4y1{Fj8SsAw3qXdpcKs+} zAo;3E{jPgt85wAT@bDxE5h(|w5`N(;4MwtJLns774`vs+qvyno%Dfa~MtH3O3;e!x zqFoxaDCwIT;QDj(IbKXstmENmtU^vv>Q4n-VkisL+BX#qg5R5%lSk%ll+&HXbQL#8P7o7ofezmFdy7H-oZYR4N&>dVcaZ z!X(7Y3@<6_L}<9NjTN-|h2tV3O&l)_X8IHLTUHgwZTlz>ZjXUz9J%G&u`uJtY5{U65W+m z1`4_A;#KvVxVO#FbWajkqEYkqL+NOEnePi=P+`?^FB%{b5Ht< zA(g<^ftBb_@Ed*+Ov`nl2c}F8wN=Z>)Bw)HFd_%&%;4^VZ&w6*6b3fh$@FdL&*z$P zHEgnsnXj{T+q=@eBLF$~6o~Ar?+v~C80Ma459Ie2#n$bklxH$%M$F2K5@|CjKwV2{ zUYT0&p4A|9Gj!A%^&Sloao>i z=o!~+%jkAN%W90_lO$-IC6za7rt~JL_jFBFyvP-d5Ge&O93EruN+dgUQ-Wm8LoX(>C_EhAGK`xBT`$v8;1HYZD=yG z18BRQg8QMR6-p9#>{i7s02+qYS_5l?G(VMpTVo|hxLC>(K(Tv@do_U*V>#Zo7W?`MVN zUEgI|3#7@%$81jq%<<)rqQ7~@#fc=eIybTwW3^VK1}}JGnCfH{N}{mThy}Va<9A_* zOQj49re~nI@cDWw7=gKqSUWiUUe&?6%AYZJi+!+6&c!&CwXD3G$ zDG6il16nb3t;Or|@!;IPuGXv)THmm;q=X0oQs9(}5=mR2T(Zo1-po4TtS_|~#0CEY z<5Pi`<(p0%*2;V9UOFGRH}JjGgAUut2OkPn|LK_k2mcQSGmY&m7G6DLahs%(gzl2J zXt41-`WpCzfCMzOjSEO&-QLp@P*Rql@$=y9((3X?H)XKKwx7Ujh6`7~>=Dox?6(R= zbqCHUKu5{?aFY1CyJQ-(PCN=2lO7~ft_8Du(I6a8rRpHJ}+*sH5lS zsI-RPh#nn1Wo4aBjSWPI(hY`ZTWywXARtZuRcnn@gR{?KZh$uTpfThrY+f*V(!Ve* zx9_EZ`R45QPX!j)f%ibA&VNAPWqJX$OQ$nyNbYKJB?ffgz6+QonOXAf5_jq9n6{2a zxwc}&(v2tnIqeQt`eWT&lq{9>&~kwtCpHqQqxTZ|)=9(b7v`+c-Z@z37rE&G=_V8< zx+h$_(3L5drnuK2eAL{nc*h`{%zUN-;kI5Tmm4gb8(DF!$3&~(%1Byr7$15Tr-a2S*(k4ee5y1iOb{tIb@_DSH z6=83hy+lI2X_8p z2L8V}jf6W#%iuBEI5~p_!8cLt8vgeQqRBH+bKLZ}kOppCRg6QaFNk1G|d6O z*i~Eq{rPU;dzPiOuH@@6FXc!*KSX)bBvg)CrUgYT8z5Q5WiiPRDS^0S=e0te;}t-& z=9)W{&{g_F>=vA@uCk0~)6-|b(03U7guqgVhaU92{1}V_+rJrpY1n&gK28%coQFaU zlSfz4Nd{`QMFlhKAqgi3jyn7$Ou)Ug)NkdCm--AdNCKvadPXkI8;DBcfz)#ZN*$U` zi~$~q2P=B9RM{AtZ;gMwcWs~mHK3sB_Lvt5yYA};AQ7k*ecEk#yyO{K7n-*IcL@raWF4V=?V{GH;39Ahr1*~MMHkMTLWTu69?PJtOJk}sHr zDE{;?&AaTWduN(>x5Kz`U~X1V^DO!<<8sDo5vP|vkW40(nJYV#J?|#{Jr`-*naoL7 z148weTIgX3CuKq_iGidP*=keSCgpIubLiqs5|IGLymQTBZW7Ve1bVICYr!u#%_U<7p;|?>;8V-TV+3^SOSnxm zO`XiE92=Jw3G6R>fo4*(!DV1SYw3ZVl8?&5+z;r-6)?mwo*jb&*p8Axx?|%;32fz4 zZo~oH>k*W1yfDE`VpFL%`*L|y5-Yed9ku0C8aMN0L6LTyYK#Loy8Qv#El^I7S!80` z`rM%#VUliyljoghCxLHdURDaQ#f%Z@3yI@TmymA0Oj_m2`r%2Pg6TFI<+VfV`oE{# z=rvahs%z(tCUwd$Sc+9(DgT_CL=Uf5|rwal!#%JTK5R_eXsToYckZEuyOnOiTmt@IS9r+ps`46?92-HZ8l zIM!?<*TTk*40=lf!)eW=|EWviJ%wiMtN?GC>zA*^a<1Qz1+0s2`#6-ZkWwXFe`4<( zt|pfvQ?K2(Z1PJD{^fr4Ew;6j2zg`7X%R0eC@{U^*b>^YULUh;(2^j(`AN!SW|=8a zJ6gJnPa1(s@ZNfDOtJv3xDcaMe^*Ry3pia4UQVX7qz`STYMg43`~+@pF6V_7(}Jf{ zKwUYxX8xcpssj@>hxb&?DrwmH=+wLD5C_Htf5GKVN^2XD5evN8jmpJaGAtk&SDAm} z;v&(H6YX04bRFH!Pj=%q`{Bb|w_kPU!Wmyxu`B3U#RfgP@8wZt6_Q=PGev}Vgts^K zN4uvN$X%4((^_CF$z3O1xR>%S=e?M-GR~I>@zw{O zO9R}yjF5#r6+Vu+goNrQExjM7sQ)8D&36XC_XHlAKpFqIj9T}skp$9{75WSKLys!+ zF>C+E6arvu>jh*X@i z%z6sIUL~LJ|zH~$SSL>U+k$i!p?f^H%@z+7vA9) zyKWOO^rtaq6a$^s60ryA;9pCXz!EuK8`Q!1&~C4<@cswvw=PG?|7(U^WaewzvWk)aIhJ@K-AGV{WA49kI;i*)Z5Q7VSX=Ez7vd% zpj5+{4ScOTw={Fh$ug!*d0hB3fdx`*v0)Lc%_&__lo|t807u! zl(|5|$kYqp$m*|r8`<~l1-hS^qtr#y?M1j9;V@Ko6UIzr@z;{i)C5rLHS5#o>j{qdEF5gW<-xH> zCK&O`a!{9zF6)DjL-YF3mXm@~zdBiT7)iU8+DLOoBrTK?j1BNAd@1h+waP zU%INkD3a+^uK#0z{>z@R+e-4;FyY?hJyj@rG>LY6z|vJp5Fz4eS8EPmPgn&(dLSt2 zo~~rBV5jcd;&HpS>rhMYm$Srj<&|Fu zB{}Jd%ZuZl3q3wDDA*~b=NDe}UFZmn%}hzYps%J_v!Cj_HNvRKy=717yuk{HS?KW~ zpXz){P*z7d3hF5I)ieB5c39Dh9Zv=T{@)<$QP7r$2SZim-FQjvnSky4wDvmqk3KX@-KiCKVB$VRec(KIXvd`RF;@R0? zvEeN~KVbd{EjnK;Gy0qGPnpicF$Y~DGMF}P{17UTs&y-Z@jdukfMI%eS9N*|*S#yN zIjrLZ<(yK=%qlIg4h3~8I~@k!=))InA)oLdE=F~h955*_XCZ0J%f6VnphlmFZEqsU7H})8GXMZ@^pT&-NJ^P&FI7g>ls@7#8jR~h| z1aB$pQHht_uv@rJtFpCXJH|STDuYyk^|u}Ny{w{tKGqZu!hEAxuYIz;y2`0s&`^kK zeAUzW-D{$t9qW6Le{kOKAAA7WFopM3Lb+4E_qX~9;&$ZsB2OHB4Z2wC=5p(3jx$EW`b?%`XUhQuu-(;Xv4C9Rh=7@NTAe^e`yNwb9pXT!#IAKB}% zG3eXh$nAub3Cw-F<$b%@eLMDj*pkaVEA>kB{(PSSv~s_VYOI7|dZ<)PhXs9)Lz=gX zqpM_>4@pGbdKPwg5@}eT)1Rs<56M`rLCyXC(4G~hB%yr&Ai;*<7rmja_6@bf-OTAg z4jrq}{69Vv_>Y^$GL943)x%Lgi#^d4%W9;ZQwQcmNpPD!vhOjoL*$C`;>vt zXNng&Dl&vwYcjK4e{jffBT2o;7t$E|iJZOHPtC$*m@(aZqSS!7F zZS+SJTd;xr1Z~KD?q)T!n9Edbvd*C0H8MBh0DgM_y}jv=G4YP{L_TZe!N20buYYqN za~I5ixG4j;j{WW16W)Gg|68y7r}Xt*y#JU(y4oRLJghImM=oV4HuzTMDC>M|`GWoE z@+(wgewU)~GRMseoC}o6D?ym+A4@sRT*YDu_Ij|TelQQQo8W(GJ6VT z=ow=^CCA}cu*+^POejtBl@~-;nMfg({>T1GYaruPN9egIs)4q4owU?#&$k$isTpSc9%u=Xp;!th<+8Ct@Q<* zMIlGzqsO>1X?rROMJX-ygMrgOpc9mk(_$m!GHMtH11RPRqi?FnOR7p-*;De;7N?X~ z1nr-#Cw0=Cd-s?l&1ifx@2wx*hq?dYPIk)wz%_&>bqhD6m1&Ywz#C%9Xte>j2YwZ= zP+WTqw1iL#d~<6ByOVCDz>gHRa0hcG@Z}kh8d~93@X%Vo@qyn$lQWG|m15 zFAZ7iMoFvJpv`>a<%TC)XsNmfVPVar^%+uFQQU8n9kZ_|vvAQiL}I1shTJTb8xlPQ z#h!z^jXLE1fGdfJ6a?-0cmzD&L+@#GZCjtc<36#Hgc{4alxfXh@-vJ4IP=2pHIqbC z`#X-*z58M9i+u9Fdh562d(U|6UiQeR*v)4ge_OMLQX-pjG(V8k|Ni!RfOh}dRC4ry zKvucS|L_{wixs1NoQSv8)Evx(64s$NpUmO&4r+WdDcXE0pHMYf@MsBRjE4%H*Qs@wto;*Csj4xG&Vq%H zR*@%nik%0xoHha0`edRdn^3dMGUzT&3z!0|>5u%FhUU&Nw|_z%99cSuG-pOL%ZfgT z?7&IV2aQ)M$uY>lY|aTzJN2>!qxPJ03xBf|B z8PO0ArvNn|=eJcE)rokW+2N+NKr$PYtT~y&fZ>9b!hLXhXaP3g&1 zm!=jJlB^qgRcUYSK2BxYV0`}j+Q-mIAmt<_q=Tl^JPiFZ7cs|K6jqIWuNXykfR;Xm zg|jI~yZ|Go|M+29jeTkY`XQZY`>vv*rS<{H-T*u}%SSg0+s@7ZJwBPUK$FR&_`|K# zgd>M#VXPi?)5*Q-gxZm%p0xUqA|A&WL~66Hupu9xfsDGrC4`e_NQV2TxMHF5AYP91 zBz(yM1q@~NfVN`R0GSh-WK2Dj#Sb$mxx8fl&4J#^w5(GwYMJ!N3&Mt1A>ge;<#4&= zrqvHbs{JTx`ARJ9W1^7H9i-oHoBg1%SwPFhP8juMYls15rKWdE1n2CHz{U<4fYJ}P z_1UY@()X|a0qq`{u(BjIvb34M)qi&F+&-mGrt@w$I%~h(YACm$P#E}TX#;gz-a|eX zvixwYj}g+XTHoKsdGL!xCGS;-y+DJHh`L29d3Yu$1e`J9=Z8hlwOp+@<8K^@wNM@<6 zSku4tgwfN7=b1+I}*{s=fT+wr!CUqbSOXnQXFn!aK z2MhUxf#hY~J#^LpE9rtHCA)hBYVENTjvjsGBovGC^(}P1Bt^c^)w?)Cgq|ctln`sGIC3@`|IP~pr`XkD-I!=R#l7RaxUMA@8T+Px2@hweQZ#pX;7QUn= zb!WVN;oRdFbr~!c^Wcb0I?A%@Kv$2p<}5B|QwRGoM_!UT_lh?ZU;0P`OwT{CwfKx( z>UFTz(i2E?L~8ELXyJ@6z+)|_>zkQQ$uoFkvu3$L+Jifk5Obo8;XP1b7~H9)*?n=X zu4YZwHU3;d-xJNoD|uW|;uNXH;%iuc(InwtMkRvw9dFTXE$YYPV_88?$rr6l%_fBl zO&5w~g2%VCWP@V_~k`Ml9{+93WP4c$d- zt}esz`WsNd@@v<)rN9XYP!iTT zCPYBbN0Wbnv&CvrNs?^Ng++j37O(+?CCXzclu_vl;g;Cwej%ubgn5Ez){C!+I0vHN zm@Qg*;N7$uls9z^7y&}oJR3obch3ZT_mdGY`7vONKsPMQ6g>{AxxKN@vxc$uc-H+R z9k9VRu?H#n-!}cJw3h6#@1zjcKiUDw|L%<+MrpsKh6S$O@9^wjZfq0So`FNQgf|v1 ztUX@WKiSG}{emH;^}FBrLJ63PaIQpQXkIsW#(!#{OXkY>EawjAwgIb1lR9}91dP)ijdCOhNM@|a4d@p#eROdu9> ztgLDe?kK=){sIWx>7`5`JYU-SI1vy3LLZ&4Ah-fmYzD`Dz}Jm<&DD!5A3MqL*K~*a z|3ra%SK+PcdKpCoaT znacO(gc)pKrji7c=#ZdMH5?~pHsOayh&E9YX(5}ZAn*t}b7hD;!7gAqf?ZS-?y1kv zTSHA`$^4w;)Uu8gMBcv;#eht(m3Ny zpT6H2idG4je2Ox<>4{{}tAQ!Nld3fgnM8tJ<)>P+Vzks~zti1766TVKDi4+~A06e#ngs%GUS301ZeDA+<(vtRlNRAnyI{1^KNAU0UAE6-8~pY%loVC%ZJJeRZ;W? zQMlfIg{kg8YGHCs1eJ|UVN7BjM1FT!n||xZWzdm+R*Pe6kV_R_hXzF*N}DptNwbJI zbNl_Yedhf8D_rI2VjcW7k@~A&7lsdYgK{Y$)MxAuPS&8?tFY3_g*;;4{sAG%uw{Nh zoiHWnZH9)5!o(cF&hpNy>f$|EhRY#DbHIq8NyLm3cqI8qvhcE)_H*i zT-9ezEIVIjfT#rc1(kc}K^D%wUUPp-^oyG=#Hn<+$H=z+Qg0+IYP`ZpwI3TVBy`}i z@u3b^G|CHCFKzFlCn1$hxFv&F@$9M2AsFMhQ1 z=HXi!2}R@!AiVAp7M*9%_$!~XI(GD-B_P8LD0_$PcE-w~{yLJa1X#5G_V@9lV-PF= zjwgK7qKj)ND`rI>;o&vvX!xF0_CBf`uBaQnRWe^xGUHb==7X&C0w_qu9uI{&W+ z3|^;Mjkx>o^?^whtI})TG@TjoFYEUxYH)DoYkl{jj2~Hp^o@vD2s%L89OPWHSxEI^ zbR{Q(Qi^I?q~VkhKT3b1G*SxUWMKQ&z|TB)meYB$$}LcB&44qGUfVmd{L;Wip@DBZ zyulS^C*d^Gu%>Y<$H2(thcQ!J6Q1T2?I9=ayc~0_FpGlI3mfUmYJ#F1J->)9(C#pC zt8AzZ4pjc6JbdY*JDxlPRe!q`p&Zhl)3)6wcU4xppi7L|d&*e%yUIYGgdmpqk3XEBLSvve9 zVd?Yz`s7(!nv0aGvz39Xf!xMIyFlj2GC?-ejDa*CDC?6`7=vL?M3vF8M^EwaeM%k( zC#kM&aXjcDna99IU1b-iwE$JLo%x4M_daj(mF9JI6|so@@@+<{kYH2;Za!JXS=~V) zrsC44A_ZeH#nPXP1b!ke^1&X3mnY|OzulQ0ihGlq6^OI%bmkH_Ct~S!aD%w zob_9wH|26fGxz>%TFBTyxS^_Eu8L@gQM!c=f(EdePO(_frvhr!k<7fHXDar(}_~xI*>tEm_3rAYTuadHxW&3Pa)$*Qk8t zt_~}XAs59E#Bcm>jf3q@Wggtwv<(``@Bv8v<`qK2=#f0_2zX}L5FF=!aYI>@Ms}V; z^Lq_c67?(1JCWfcAr9o-7B0{B`59NdEP9?O_245}Wx>x~n4qcI)nqYvM~ea3*eYVT zF41S;D`d+DqRKNvQi;2%u;Ef#n8snTP~CB+togVgi&9Z$OAMrs4J@`zc(K)u;Ulq7 zWO4_$)@egx9g`q7oFIUbz`r5*wh1}pnhKP74^E5g3Wf;7yD_d2IT~{PYpV{CF#4Bx zf-IcBM&sad6^Ew$EoQ!AbFxyB-)C}sMkn6Q6~8O0>fb{w)X||odzpEvbLIEoqS-ad z?Lx*L*vR{Cu6Y}FYb=JI4r4UJPKbxyL`GF3VrsC&fHMrN4^I67WOh!oEngp}+Td<< z7I^5inJODNUTbT-TkED7vtY9fEUH*Oy2$X(z)1%6FHS<{TYZ;QY%iX#eRDUMp{v{F zZmQJMj`!uG@0;Yk+SK-P8?1b-sV-z~GuW22f=oG5wx1O1tiBa+^-gA|;Z7l47Vl(u zXkQ1XFnYcsjLz3+L}Bn)bA<#}7`f$f!m}j(lCkqgY2vUC3=vy5KEhT>?(1?a$y*() zhd5MbHAtw&KmbLBibeX(%!hKlfrs_j0}~o9e0FeRD$D5)xrn5X%aL%MzrJ_1m8IDPRS#wcO3@J+dz@&+b#GA4S-sa+gY&^f2Xpqj4fAtU6OvT_p|x;lppv9m>yPH6u11N`kgP->rN*QLDx!CY z5~{^>1eFAb+)|MU>B(~fURC6`az1XU-VQRVTI~aEsoss6eCBmu{&K^`zp!|sUp-Td zCCK(vPCP_tvBV7;+6P;Tq(a zWy1S$9u7*#pRwDdEfP-Xr8-#YvwN!bu6W-clzkoHB6bK#o^i_;sZ1pUM!P9Y0(+u& zjh*hLO$1~vu5YO7!Wga+AE7hLbbjW^)f9jGru#FXtfA14i$RG~Gk$`ld~GQoD&=fK zpRNbD-*96uAPl$vfx39;VH(17r!#2k<`LJ+@6Ej0_1;-ewvMABl4h4YLe~VlT?5?s zUMp4FRb?L5Db&>n9`lgO3`1a|zciV#tJ{UnG~b$`hJhLLr{n21#hH zb_?h*EXqNNn@TEPfmMFW7E7(kiu1@^jyPX6eY(w|CwlRwKynkT;z4js!ZPnhGJ&mZV?D8drs4f&j0Y*! zfI;6OZi~B6tWm?YnzWGdiu0>V?HE#Y+2pG{ktW$4pq9WaduY?OHufU=>aKXhIiWnZ z95tbxRN@}%m&Cw{c=^CkVS}0MK_PdgB_Ef_;I@5=F`>#GgOtedl(~SLRAP@uNUDAA zQiM#g%BRuvk6h{|%a`~EV56YcF21u_PJdI-&8n=kwdZ_gW)?{ixm33dl3QUAe!$1( zr)bCz6GR#6C{Bu8#+3xu$qbSqtKSaQXiD#1paN5pIiBjX9N~j#G{2O(+|UKqu{eDi z2tK_EAe?PDAhM?4j7q&0e?O~3vXe#D8V z7Yzo5MF>hW@ts3$yOL)#n*)*1;z#dXIf#_x4Fo71S%~z9uc{pJ&d5!crFjQQYH%{e zurHYm(Ecs*Bp?o(>1JXhS}j>2=0U~Tu$4x;-@^Vl>xfoSKTJ3}yb|Q+$X=&8nh=>X zo%sXMP)diKXf48%Dmp4b^8r5+F2va1A=c{Zc&+cP;H|c3m>M#+p@6dEXc~JYBLsHe zVnu3FN(@w;l6c7`YpnF#j(#UsU?n~Fbp6ZBo^^zu#`#1B^J=xc5uqwsg?E*L0yy zf7BO;uUIohQ#9K&4vTZAzC!+NBs4cUkd4vfVfrGJiSq}^bwh5tK(Jt@kD4lsWX;x~ zI2-7i*WyuiWP#5)(eG`mdS;cWo9wQ$O9Rbi39O$9NX{xkp5#(yRcB=X0&qXV2QoNz z_%g+Y@`fm_d>8$3p)8&e77=>T!b>T`L7Wq0$6e%#b568~gqIijc`NfA^}Pi%Kz~Yl zKU=0D{2X1CT@n?D(cj5~q<~EuaPzJQ5OpGYBXw9EZXk)0#@0{wyCY1}hMEzz7O5cd zrM9Rncam`|*T=LH20O19XNGG9g`3lOwHC1^$Nn@osljLc-fJO@bEVLKlv?+zCydn^ zGNp1n&2%CReQ&rYJO37fL)I68^n5*1gG$%ugM1`~F9X3Y?V4$%#nJ=Jr1T$Q%Y*v$ zThrzvYt#ME)1$V-l8xi@Or2)uF^a^=!T3r@Rq2PHDqQZQ5k}U=fb{f> zYS$#cs((r-LN-A75ge>FsQ$G$WdtVRiim+r&!;KbK=tjfq#m5A0jLg`X$j!!0hZDCj{aXbyJ zp)`>HQAy24+i@N_zgU$=@c~+u7s34B3S3hF)xddKE+4uE0Q)wt!EEe!T1h=_=)Nph z09^|}3l`JtX?_l`1ymH$be&U<2W$2~*!7%OsHPRe8v@~sb8CjtzOBMcW`+_9hSS1n zxg!~o)ilv`iAsGaTKVNOrER@3oD#RwJ|3zZkPPc%c-{ogF!$@(!qw-Topc|z^ z&mW_zA<==W-6GB9b0e0}y_@Sa>h&FD4XN9AK>Az*L3_@0QD?+eNUHRSaw9CUm1z-h zoW)6@wViUkwm@iw+Rg{HEOzoQMy*P zMIr<)u1SVD->(xeJ~Bnpmrdr{uHx`K-5I)q=ecaZAy3-_n$%wB8wA^~<8<_@{+qfp zcK$+ZE!(;wcav!f;K54zOCOpRF=VyGweyuQ{$BL5(d7*%GsxoPWXZjX2KKwL@^lAk zo@Br2RP}`+0BL`Pc9b#j&e~Y(1t$PW&8195;sr_=Zn6h)fj>Lj>X##;jw@&12zJk> z(E>0gOE8Sebc@B^Q#IuB7f`P)1Lh@^95kX>kq)6reF5(jRE6jRW>^|NcYhhd)fqvP^M@W@*02^1uvw}M`Q;Gu>|wFLUA%WU_p~&s z41LVd{y<2=0t4rlfP2!?xY<%*^P7l}`4mavi6cfJ8zMGz37wL#I=PB(0fime0sJ^E z$@ZMI+_H#sE`e=7m%=!|>s@wfPw^t{_IJ_B)v=C0zb|VvyfNbJ)1VpcX8d-fp_`{_3o}QB6272q=?3nnqxbI9hmb@Mcpx58!3) zX5b?v)b|z`K$s^01TDd7#fEcL5=&&Wl{pih0i!MIF^0kng_m-;5ZE=d&<#1}tQ`m~ zbO@?qZ4}@~KK2v}sQZ(}f&Ki{E*3nf@EID>Xn={HQ5bg^D<&>{+7QK;ZNJ~HY zg1F4u5}0F4z)oi+0P^w<{dY2y&d&?6&A zosl?#M$>kcc95xpIty9Y4Vs2Jt1dY$m92+W37xsFxVYZasmD<3I+lttm8jo!&;W|N zHgagPcoX1cCBt~+lpciz#ZVN#R-dwmgYg!_vDo#R%O;DagiWsjp~#lZe*c*@Jb6Tx zYJea07_;AglF1xw6u^nj@;sIK69reEunKY}wCs$1=)D>Qq=5h}UmK(y<5+Cjnsh2a zuPZbrpK2Iw7N4`0QP$dSl|G2}xI>o(r_kyo39=)^{Q_s894c404MK)-p&nuaQH6z! zL=d{~{Ap}2c3YOJs1U!aL|!;F2&zuUM7+kZn0TDVKV9V=@}Piy1`KXg)FKHI10lqi zNQGI`Wyr%UjX7!})kQNVvN6NFpmUOJ79KShp;)t-AECCf1cO0G)o0MwzA)rdIjlp4 z0AkA&#h#RGwcZ?}Ji|5>9_7kDL*y^yH~DcH9_00PiAETu=A(9aQXHk`5<#8MrBmen zoXn5dwa^Ji3H(m{zSUY}E}6%_1IX9pf}=IqzfmaM6Hcv-DFV{w2kLa$m@^kPg@o2K zcAudJc>3X1CuEJ7wINIo0L-HBsVkMJ;SG|8;Wv|igXoO%YqB#MkqdYtF@#~ zm8K)k;)r#Q%H-b;OKjf6mR2}|UR5jjs?`RGeB|dV4d4>km*e}hnmCf|x0Q_q&x{5e zL846N1HPY&wxBK(KW8>M$jzxfXt-EY%6Bz|rbE;FwPYX5w{a{# zFbP^D>?3Al>y4{KX6eZNzWILsjSAU|=4B6-b<530f??$EQa!90j1rNd0w-$A%dTh! z>mVKJjR9gfag0Yhy6TElO3NN1Bvkpt{p_;1m4^kcjd*QCD!cB`aYF|PYDr&cUcTeK z#nkxDlw|Z39$9C}20(*Q8(>HiQ7MU-Q05UUOQLjxiz$-VYpxE7`!-8bjmJ&r1z2r2 zo&b7}>dSq{w>n`S5B)U`jwMRnS^`bIbd-o?(x_|C4#pu&{G3z{e9~yQqXa$WOCKR* zcciteq~{oEWAc&o2o!#s%bo-p3fp!TB~TyV7*Qk(|1z4AYbe`9=xG1ZvcL!H)%Kgx z;e3Mh3CvqcQZqfW&g71V{hjWZP75-p>5$kV(?TB;EOVYS3z{a7rwyKp-XG#{*npB!@$j@#0CpT zp@gK}5uJl}Ow`q1CPmUPTJfhbg@J_#eISX=9wbEEIHzaW+`!hqZIpbB7V2PjhEXX zPR={>JLrW+NCEdPnZeAJdsaC1=cBB^SP|qw_7MB|wtXfNk^!+`dzFEhrF-yNh}9^y zs~GlpKSjC$V(sikkQH4Y^@SB2ZJokBA^GBHvD#p@V)ush0HVKt5qC>r zeF)D3Uj@*Go#fd$e{0rga;hwPfipu;TPRs%S;LZiHEL_v+}g-FAu@p|(AKK#(a7B) ztom`5cw!cvD`Tm#Z{w__r~!opDjCPP^|@A34z%c~=*6@ZdsOrDFpie!C69_3%8Wbr zt7*#$ka6eJT+%uS?bK#72a>@OY@Qe%6jCV7*WK2&-F?Ryx|sN;YLPR|R!c0_pG2B2 zJ0BUk1TE9_)AP($p=TN|7Mm{L^0nRd0crZg98A-T+vNW%;~HJ|J;GNXSCXGU0_YZP}r9FH7^ z=6dM2KGaUR|8YB;?PA*YS}ozb>2y8OPru|g4e=|r?zyX` zCDUVGV587S_got4r7^80PugLj4Arc|kAEa~h-n6(wVp!FrN*EVfI_9G#$<+kI<3VE z4hO%0FX)>McR0t-%?zswEzb-MrQ)^OU#lyj71>|IDq#&VG*L$`uuSPC4_juK)ykM~ zOtY<;vQD$TC*fF_XZp&-us2QjQjBJ5A9pf@eFKFgkikVCt;i~^gEVr1V`?j5gykEC zdAO&1s3M*0(_ZD#4z?H`G8smib90>g>`OpfaCvOpha_Gl^0XM1`9>E z>huC!{qYHu?(Iz&)3-)#f&Jk&LF2K;%X6IWZDp&XF{+pIC8WC0VTNsr4q%_At-0XR zG~F1Lmu;4&@wmm%R9%Q|o@s{J!!pHCbsDN|t}Vel{a>opf2nf+r5a@Ym%8|0>dk*! zZ?pe1RU~E4@j1bK6m=V>OAxi*r;*XMnAh@>Cy3!49ZzAXc7a6Zj15x*(5vGYkR9k% zaP=l~5Xlen4C|JSUg=>uGm3qPO7rQ;0>P5Ly^ku;S<_!9F`8LNOegOuLo@W;_;o*c;<1G+Y!SNU%o-_TY zlrvZX9xlxyceoJngVI@(+{#@YVRl$5>2^O~62jc3xTr4d}bw@()aFK11U!nrKYPyYK^Pmv7c3m|mu0qlp>#qchopfMy8Pmbq15Wxc& zN1MI?t!_g(Vg4LSgR?Z(EB4U~y^V~HWFk4Dwo0nZ*PJrPQ5T>2Lb&(3nis~@Ny^au z!kRHDy+IBaY5Q%E=o0d=0SZ;-q+;V+)CK${Sr>Y7P-yjZ<15u0AV$uU zO{$dnU{Y>{4cnMBtbs+F^_9u=D-&%Xo@Dt}pyiPP9gzLlASYZidf1>`Zk7=s*}OTC z-@pOcU!9fZKQ*@A-T$m+;4n6}+Rk;HsmaC(Vih~(brUrPb7 zAtjd)+Q7-_mSULTM_Yw|_I@4fhCK(NHQCmO`cweZN&=Quhlqn&OSM))-b-4WTbI(H z31)?GJr*tOA1?)g@E(*|5xqGFvL;rZMMIw0&RKxX{m6|caV`Tt$5UKI#488wR6_$N z`GF`@!I>?w#6>OvCCzANT}}|wnqJXiD!pNCRYWW7+Qy&`sHKV;yMgWUF-v8|C31VQR>^(G(+8zgT;TzeC$*7PrAVW)(S=4oE1A+<`Ps-x@wdT__a@ufCLRu*RuDM z888z_y?Y^=-PzQ5;> zn(8TZ*UW2sx3_1i-j0hp$fU$w)_-(Wg^Qa>#Xf^?^5L-lBKZGQYtc?5PfqFc8VxL@ zyJdv?5%taB?e|5xUK*V!Dm>5F~8bzfyF1{3bvcA5jrWw7#Y` z-5XDl&BmK=2}xzfHp9->Q&tmzDFO9%M?J}+R1Lu9vBgpzzLA$xo?VrIgj0M!azL?O zHc_eF_??iXfo9`Sd?Lvwu0sVWgd}sgGu^w4l#apL`IR{ARfn5G7)ZIh*dQ{ZS>mv8 zoe&9DPsvClOFg|%!OgH3_@?W+C(N>j7NHK>vVMj9T;kCb-si&;C4B1*Ds*s~d=r&8y1u)D{7=K3a4(3 zRdxBs^Dq$9%uy(4=2cY$xo>vOoen{aDi$xXVE?H)ilvXlWO|WGe8nsbeOw3Che7-#a>@?eKoC-%i>3cP4&SuoFS>HJ?>bXBVD z@R^lN&KEZgRk9TXw|TKI4j9Vk!cfaej^`wZWaw_k;)s$f>Cgbhu38qkN`&y(5G$K} z6?|r#L>;|8c3yVw2XcH-jhmW>)s-HA(?H~}=_0)WZ{r`mL@g3dceym^((Ej5ZZSorEW${PE4m z`)jom6)Xk7x|(uz!d-T7=cX5ahf+h)N23t_oBcYp2C27)nKV1}SXbzh(vDW$VIkc- zA+=r{;MZo6SkB`yt{kV)$a;D=LN!p1*uZYYnS;wGJa4~{r-Ab-O$S|KooIZJWh1xd zul7-D89vCC)djo8Y2wRIwxQmIJRLrsaD&!Shs=O09l!`rAjcE`TJv|Kdvx4booBlFv2L?-FXj31>b16j zysdyi@8J82KIim!pY&B-#!AiADogCu>u=G8fT5~eZ*2vN?8>>*$g%TPJ}P&z!Ttwf zj74_Cdf<1c5L>}q2x=>yH?7dvE@L;Ttf2;{PNNA~DFfpN*gRZNL+>Dr#e<8k zyeTpzmm(f6a6=&1+vs@4M69&1W1j%xSDxZ>)X7@fd^ztq&Jw1 z4ZI~CDVSwJ!xvxT5{imw8geB2(^tDf1kfhZF8yGs1I& zn_D1?wn<*bPLUhINI0$G^wzQI%rpaE>cFj*;Thq#b@dCfESb3 zA%=@h#;Z}M>NT zfuIb&;(f0w{91&1vh2pv_mxZ??6SfURSTBTLAF&K^w9ZoomYrq zx_Lw(=4EqJqQ;j_s6BiLHe%{0xcx zgGsVrZ0JBq1l<5yDve0yOtA<;Sf~0gv-qX_*Qxn)m;yENd(dfJ7Mk8I5+e>8{(mR{ z!_?{d3%a6FJm^By_={%n1?v!^h8fu8jDpq6fc56wH80(my;K%`uOz^Q0!bNCaNYY{ z3_azIn#q|try*9d6E}2-C!EtGl1$5!C zL!tNMBG?YzK*-{Yi_xDhI`wX2$UCF*n^*PuRVoY4qN6{%*KQSRaFhGrMW3ZA3s?xz zW*KU5JTir?OJJ_0hImi)N1e6+a;i_a&vqthJmTd67X@oAuFXA& zQET*fSz@;7nmxy=1zD&38CJkWL&nt=YxBTTZP&*PeWP1#U4SltytJ>IAyNCPXB#Z5 zZ0~=~KJTCzm4CDFX6Zam2E@%PA;3RQOD|m?L-fsV{dIlRnZ7E>jL&=fm+H_deFxuX zHH*IYYg7%L0%z!=)?=E=9DS9_wOApKpb~M_j)7(8cXkH}sKx8kLVBvL#6BsI-3w-Zuv2O^_7|N&@g&JK2Azt|$)I>pU@wf3x|0QZh^Y$6lEwzuCUfo>reDtLazd6Dp&N@&Nv)TVC&DD9{>A zVwH2lKd>s5Gxp5^qZg7c)Hq|7jpn2Hl}w;1C(38~RiTIQ)`JY@oy}Ss2BST6X#gkh zu<-aYXm70Ax=60+3isI~v6QAo;5YHH`mfCW(WO&-wSg65HwWhA=USLjV>CoyAqT0& zn$CYjRb+H{abRKk)8TuYY@)Sl7kPMGu;A+kWm7`+#IFj7a>3s=r%u)l1%^mcZt}3- z4B)a>ciE&;_rJ`R6Mwei0JlWSBW@{*vBS1f}=QiR6os(T-MhwzTrBB}f zbHa==x?u-c$QoVJSdIWo3od_gn-)n?BCMxRmegID0!z_?@8?sA{7Vx>?-VU71W@VX zb1q-2VGwp0xgMN7N-75^&A}_P87N9DrM&Ab9v#WX$OZ-7JuUD`kMP!%t<6g)WFG~( zan*EiF?emDhWJf~^pX~I!RpaPGcN~MOg6cCplr}Z$Fc^6y`3Z*Zt7Gs$2X?Hp;64Epucof$F&wo;;R%pZ(J1??O)VJIURkkGcL zJ=ly=1UZe|!iIW5?jx~VyLW#thH5<1rAyqwdz z|4mK~a-HTLOSD>TgNKa@X4Yw6#>MQ@w7K7S|IOG=eN&5cEuMDU9V`Ucsym1O=H^|S zkVVy>|C(Kn|4q}MPrwPMf@(An!S14pGr^IR_}8rnF?`afCItO zSd(1HO8D}D-2@>|fLn^1CD;96&Kb+=SfMHE>PifO-Wa1zu?wc-W3TM`Q-hdLfvf85 z=$BB8wZsG<+9dXUML?BW=mWdN8OI0-V^{&W|-Br0x zE!B}cEyiUU>r`5tJ+9P6*Z1NW*(&R;{ypSw2|MV=L`2~zs`+``9hrDcqETVFkxO`UHaSQs86^GscYA%11r2_`5zmslY+dD- zn(T%5HYg)roXT;_nOV;L>qIZbo;0LkY=|fg^0*SAGxnu9Zx0k3Rru=btI~UyoH;%` z){|BrIy}<)>^V9-*41q1Iy}y<8+@7*(9&r!6$EScSQwrHXLb%eMJfg(Kp8u|;`;?&7}QbfOPtuOWx1PR+$` zBg>_f&1tqvhFGODXP7!VQ3Ic*HxxJ?lfJ0Qm4i=44 z6WN5|h8lSv-4x3@EB1Ph>w4j9uZxIp1kCTKrZ=B6c4;e7+4U({eO|GPZ0eYzveC4+ z9oCi>Lj|jUC`JCtSU@dB5h8;=6P4RHO3`&OeRw+7?%FxKs4V9c>@JtsMfiVS^W;Dq ze+`9;X3)`7r@z;qs3}iKBDnSXd5~=K+R8 zIk%e4<`;IWMYZP_)(=Dbj&`Cx?n1ycZG9B!^HAgow1JP$j^{ARMH+I^NJB@XZIb&c zNkBi6wnoAU5#+{ibxI}2$Z?IbOPL_UUa2tUr_zU5KsQ<(v!W91yOhBsdgWf#kMsM( z{mVLz=U;BFm%B>;Hg);y2h%W=-};yX9!g(8m%Ix@)#v+kCTsi+R+*F=9k5fx{EdvK z&4yCPfFag zt|WZT@Mox2y_XoZQr&1nrS!0fyUr-1bIV@)2RL2;N7PaTXB>D5R>b||`hm?OV;|Ba z%unn;l1aA@RrCmCyCOOapl2GtowAnn*1^I-T_LRacBXeR7k z=R3KPZLj2OA~ZDU+d4D(Kz|tWVLp9{xC~AW(Q$L2wD{}hwO_8X*NJi{fxa@ZX& zH~f;>!nR(8Ngt1|;IBy+GJrru5-f5_pJ434q30Js-dEo(T@{;+-kH60bC-ig1RHfx|59O7r{G8qLQQ?1n2k z&p;0aMc!Co8tIZ;t<^%kc0B!12(MUdm;kK?4$Bc6;4kOQ&AZGrM*SPL-U0N+Os82n z7PdLF*+h;h#v02M!{6UQ8I9TFXhLKc7PD#VC;LfN#r9A3%AMDodZ-cxq->!_R_WNR zxjn&>pKX&wRzI7sGh0G=MV<*!An6!N-;<{7?W5!TRGN)L!c# zpV*Hm^6;Fuq%-q_dZH;IS@Ml_8PHv4VBJTm5KMu7 zdPjI4gb}1aCT6g$cTzdENh?S8iaKLoB7Dx9pw@7G&J*`jva8wDQ#o=HKwuy2T>;Ns zM6uX+IQ(A3B8#v^@@yrEF@~l1o{`K1lnF7Hh=HyqfU2Z0jREdjECYrT3(X=-r~Dmz zJLF)mkD`lwVwK@m4L23_(a3o3)+>-kG2%!7{biC_=yVJRI!o=4V0NO zbvK50BXz2Nw5Jxv)NND~6TJB!vCWLICg0+lX^~BSL4I(kIO+Mvmly+Cp|c> zD}k=%GaV&>2~8vZ59$WWsZ;eAH|O)}9<4}Piq#W)G{C@!jm^qaq2O#{7oI%QzZ4kf zEqM4Ts=F#9Vde98uRHn2D1jcoV&tGYFK*N*s>6=s;7%L`1Ueb4Lvz%8*dS{$1l>ja z+L~FO+zR=Gi63Dt?Dh_We6{uR^qCd4nk_lge!3B${qR-slr!z_P{ygf=)2Fp+G4kz zfY8z^-DKla+sTP0r`-=gT+NyBXyn6Pj8SMojWd-y7^l5*T_jJtxHsukD^?@rpq_3f z(;%O&mVWSRYF68D;(8O$X`y(RxQs~5^K;NR)YW17azwEBW^oPO;GMZ5e>4;WPS6@5=$ybZgs!>nWWec#dZ zwpnGj2$RnR?=oQyAbBYo^)g|e6}b|Psfrq%fv=LqSSFG$0rUYM>qbwz)!LAsKAWfo zV*zjsWq+yX31<*u5I%jBugr?rybbp(su&I3wlb@PiSm^xJ&D-(7WF)ebUuhrptZoM zysO*8%D9}C5{H6Js@8mfMp+5;OQZB)PM(jRT$aj`4W`wmjvxLEPTC}#8a|Q3w9%!G zd3+S9M$F~|kd=8_Ku@16D>?d7qC8PMtV!4GS4)B@Ej{F*0C5sRU5bA!Uz`SB1LDU- zmau==v_aSCEO)nkRrFFfyFXt^2yMvU8B~&4k-K!T2hCgbVn1qC@oElDn6t$_=l%q# z@3~T(C7&_ltHOz&Cf@><<`gYcS|xU8OO%+~5z36V-|MG%KB&4|d>L%;mz@BXNJEbd z!`b10jkLfi5X)%Noka+)9OQ#B8N;ZQV#m%=Cos2Q+~YtDU2|ooWydZ_1~P8!k|rSI z#4Z6pGf{J4lz^TYtsK#dm6;utV#annhZ5QD#7WDEStM~~<}m}RXGe={80Tture^ed zZ1G@f&yN~b^zhSjV}_GmowYm|k)~%y4?%%V7e*xH%$Q*`VAF;X3Dfn#!GX9XFJ{=q z)!A!851)_)I|9po5Ikn z^4glDT&8%|z6sWuv74_dy}l^8B%xz92s-BB$2V#W+|oU+)UlvEbBaQNH`%F5Hr%^w z=VjEFJR&9^Yan5;^aiIVf{I}q6l&!ODgSAL5_1T@Y5x)mp})GfgsUM<=!6NMIXaIwYJ$g`1g?Qb&$qN4aC+8( zz;CKco+-6R5%s8(ya3G^f)XsAqA_B5=7^%H6dJHXhKR3##F`!Fij#aTNzVG5gWk>S zM9H-B)V^evn=_gx^cc@q_X2AC^i-0v0^NleMuY&@fftNc26=Jb0rPCIKqi_{+ zwj=)Ap+B ze~}}`A1c}zEZK;Xh*(>Fev}8vG9z!~hapQI{%!WEOwZ5UFGKqKpoes1wK6AY1tHOT z0gg9gSYXq$ksxntXMQbMzY5i%dIb^2uSL1ofjURv2bcQ1cnG|i)nhdxK>HBjR zdy@QKc@WPZ0I>el|Eg92Yx_i5g$|Izy?D--s@N6lrGmJo#^9O6x+sgvDKp}AA}BN3 zgwjlj#{tAUt*(rtf$~_OD3WrvA`Iw?|o=A4LBG9=PZLVQB_`VSbJD(B!&4#khrO3X_{OC52&kxxCgAg64Mw zVIv1Gin~n)O+vy$0xYr5cizFW!u07;ns`tf{KDW=(7w*Emjkmij38>z#*_^EKPGh4%5D znIs<3C)~lk&mS2o!#vuJ7Fb=f2aUNV`NGOT?zshE$D4rLLMWE?&3lVgH9~`@_qGcN zyNztUJJsP_aG%)N!qkh^Zg}s885Myip|jONi7iChO(S-xQ`R|&igo1~X~ou=O6@}& zzw`-6{7sut7NQ)Uvt-O%nrM?>+B5-RDv!o=+|@j)yD*a+@)XV6tIFq^x#f=KaiUUj zVYAY!o?9Y5MdZ{0DM23|3m!-t^kA=-Jg)3BcG7BqEt*c_4YuVWGYiL$EM1X6F*9#} zOD;97Kdkf2dTr!jb~;2TZC4Yb0fQjcviQMEUT( zHi|qX&w6H}kN2eSnk_wi*gtFDv7_AXo5_0dm!9r|w-s$dtpC!+9PfvON zywy3CQl=g4v#2;;S;$V?$z2OenkDQzdU2cSJaa6ShGvXOd(U*zawAKcWUO0ybQ5b! zZ%@wk<;@#<@yDaQGAaA+W(?*wURhMr=H@Rh&O5pvQyAbG1me$HWEsZ96teT0Y_l|p z3j42AkUwOCx>y|$kCHZ?<4lg;eCW-)r^Fqij!-B#(qyf!s#5we3!3=b3lk(WU32MB z=%id2W?IAVg|D>9l4k_O)k&Ij`k)!w(lT-qAd+GhY)_hC$kl9+LrmVtdEL19iy~y+ z^b2!Rz{;1Ymioi^-GniWqAv$x_*kR_)89V4+2z*6oZ#imjh10T`wk{SV)OC`5w?$i zXt|6O_t+TsmYc;zGt~!)*F2>cv(tF9A5@{;=mvIc8vk@FGFH+=sMMP@n9o~YQDLnczq6hq0`Ij#xt}bTwi;R9 z96>JXhS_X5a$wn?q_c#F^f%g)sANgUE)^cTHfl0q&b0)TggU~=o_meNmTZ(ptxkh6 z#2zd#i{nd$d|p;Qe4x8V0Jp6)sGM&UrQ%OD*0T#lk&hd#(q&UgXRuuBOK2Borcsp{ z@PQ#EK&(4D2IWVRh^a3-!-f489pA5BqgV75HJhr=bb4#DVF^+4yM$sHVKCaQb#4+H zb%v}m6lHD_CC$^)`xm!#lhWsOmq4_c$;FlB&8eh??MY_X$oLzy(`;-D1EF7m(^2zj zwA9+2UO_#4{Wii({&bkt(NZQO%YLVR`(jzCLrx;C*&c$LVG(yo7 zKk{h})C#p$y~9;9>s$IIr!N2K{^OTzn|_>@VoKmHlvdj~Sc-Kdh~Y|)Z}36D zuicLzBf~#)<%l>?ZN47FI1+8!d?*N_+YG#8M*gSYH|od`qJqA+K}IVEdqGAQmi0iJ zkIg4!i1i1^W$?j;d4ppiz;m-FQ0$hu4s?~7;dKiw!m1(Q8!?Jqm9cGz*@flRzJ6OP>VZ1)ey$HTcR14RK`&}U6hjB$^8D};7N9)IIz%E6i?UUW0f z`F92yo~Jl}>lH5td-xCYLI@JXlFb#iB&DGa2tqKyDfzTV&eQihB7Q_6K-=N5;v;SF zc(BmR+!62lru@P=hrY*|;+w06zK>+#lkuhR8rBFOB>`t0E^aYjyA8+1g1%v8#g}p9 zc#>b_8jzT2pzeLJlLtz2?6sjz?K!iAp8^?% zFoZHwy?NwAnPzk|TYm%o+>EBF^V=fzHJA(kAA+rkt&XPEmzhsXP!gd@$Pl}M45QxP zAbuP24{_l4wF(jD0l`pUjF!+&gRWWQhs@i?0~Jb5`WU$LlhW9Cm!EeCe9m00b{Ldro8n_wI;zyjLOIBjF=Y zQ+muDVmBLJF5z3w8T8STpDC+|aYhkGH-Y;CJ33+N7n2i?uFkNEz*wbTMxwd-rN--} z!pjC9nbMz4u|*=D_n+!iW11~A&{eioOx`9cI5Aa6Ct9WOzC55prlssER(B6>-rdFThUr z1$!^nkupFBzH;(M{Ly6dn$)}T1~JG_oBlIP%7ETim7FR4TMu~H#q55m)AG{1S?@Nv z)NeGsqYsbUX!gW%U6m_I{dmQdeO@qDpcU z;EAI4?3^rvFx^~(n5Y&!bg-)qh@<`3kfOM;0b=Z0zhV08m4a$M(SBq~fivF5O9ly$ zszi-E|JV|eqA=t7EgA?+S%UrmNHL%}Mp%;(w*q{S<$ruvjxH7&`X7*ico(PaIN5D2 zG!Irv7Zin;Vo&Ob>W>MKCA;TX#+4W-`sn7zOY`cv^zSiIkanYmzLbDB(PPKx^;!6K-THd}5cJ(6;j@R;zT)qy~l!LSm;gvQjk48FVeAj<_Vv!xbfR z^Dlq0uaYh5DDmM%46hMSM%g0YAOvev`ls;>MsfCs$&RwqeQ2=-+A$|)llEkbjhZk1 zFwdw}-ob7@@#?E8^bnt7OD>tI@Q~NQ933^Zx6l-|(WLlA-B^RBmbj)K^nmeLwc~N> z+C?iI9b0E@Fv+BN_DmF@o@erHf#yl6`9;X{o4_#1M87bE^=3_IDkp!siZofV#E7&a zfmPY`>VP&5ODK`DA{q>Qo-!sg8WH5%U+5R`Hl^@2|VgK zQ32gH&XE^()oGU$M}rTVl|#E;uw281(9*8$GBh>AEK+f>in$ufH7z3^urGwuK3w?X z!GfjQeSwz%I${x+TXAMADtx4s;>`Xdx0tVANh><1GP`j@^iEBfF z3P=)B3MYZqL5%+FFTTC8u_pg6wkrzpm<}imjse!*i7YRx*y=fXO!|nO&64b>7StVqb6QmZnK}n2B+{QF%>Jg&=BRnL%&9?B6$U~cN z;oDqMT=k$fK=GMdQ(Wbw9vR93gm3BF&?ZUR2EHR-DB>9gLHnvHb%GlaHl~nP<8auJ z2m(wn=FTmf78y!=(4JtYyKXtK+O%C%^I{;Ep=%e1GQMn2RHnekmMM%oCuOpnb1&L- zJ53s`gU2QNWQ2oql->_RXm&L)1ElZ4rLC6Z8lVQ)6s5#VI?CG+8IQ6z?tBTKM| zXNRqDBTEdbaW8}v1!lMzUvO`H0NB*2Pe&H+8=g$42i6=AAlH@>iiC7{S=2Vr@)00+ zw$cV0@eCzY52p`M!dOeHoGhGN!Z=H+?k!4pR&Qg=hw_rfC;kF?LDG0B5+YLdaCG~AglBnqVa5p7_aYjfvT%V&bL&hT|wJaO4~yPtxEOijXK=1CyF!47CD1tg>YPflhM zS_1#)Mu-7)O#r$@&A`Y?T7*JNScFE114+yx^kX8{d#@3d5I+kwK-Em*tu_hwupOx1 zCj<&afqi)a$?E_3n>PJdm8>P zG-zO`5kT4phJOXTKi=z*OJ{o6Pd>F(WElF`p|@>S70u52QqSUb@Q{s@?nR`Y44t|i zjN~68>tSN{=Uo%RGj;G$J4@~)#{V^1Cft+I;OpRV7=g(UVb>z0Om6y8E9rIcu%?$? ziGa4c7$r<}_W4f)QE|ByzB?8K*)g#&bCcpB6=r4Qs?5p;JvlWkN=jOK0(8vu=qR}9 zaj~#9peib}QVA94$O||8pJf)%(hq1UKuOC-Oi8OYBF12jedzaO<$>Cz-kN|ys5kim zvum>_Nxh`~21D^ELR2JXe@Uih9PJm1R=yxft-SpXb83VA2}9xIOJs~>aZZ+MMBx|e zQY-TT!>#p*dIa-?7|J0qgfK8CNgZ-Gw;>L~xDo$Y#>bF3D!xlJgQ0LmmZ0a;H>Y+)C#BHVO|jE{X_BI zr{7@M>3{i!!u_gEPy%gzLZuCC6A-~%*^{gk+6Km;_M0q}B<}^)MQl$JW+i{@dEyf$ zX>0vK;{yhDi9T3LKqPb~h8UNKB+w>1y3#LH>P~k7`yFPL*p|q6X+wsHL}s%^WaACy zWr38ah|u~K^~wntAb;D0WThn58w|J7FrFB2D)`M1%qh<@x#<*z!GVSA!D-=qmaIE8W8p(SrD||d}xguRM ztp%QiLX# z(39Yhj*)}7$>Xq$s40@6pkyzh4jS?+S+M@w^u&zJMI}qqz}8jqB>aP`htj~B_h{9b2W&cK`_ zt9S-}_6Vx_uAem=jz8u>NyL$aoH-7yaO~3DaYL>#ci7#TwRR*#3f-7D6ft+;X+BTo z!nG|5=hvw(Q0mk>Ln1B=+>z^S-<^zL?vB>o&QnpI$zu_o$$Ml55(%)4I+M2qI+N$} zFr61Nl)(!v&g6*%Z*c1;dgCIA+I2%)XXz;;!RUzuqj!ss)xN_`Z`bK+wc;|4(qczg zW{yOT6s3KeillvukJ7k9^P>T1V9@ER)Zua-XmuLTsI{rSGjxlOSF`h;u5>!bDM9Pm z$E?)(H0wN-H$0rm3oS|GiG-kXi!n}(Ems9TPlSY|e49y{sz2&~9ZxI7UG%!55$5-n zsPc)ocQp(>s(&5NECB4Y+rgx2Ir-#i_yx~l;yxqz0m4pnVMw9`zXvypX`8h)rbz-G&5y~_TU#I^vFSr&mfD&i5?xX%pVn#nt#whl zP&!Cd-GdR$_BQO|5N%8&IC>8v2_(hQhSVAptvyX<`paK9glDxts>30?sK*J288)eW z)}mfIK%NF>_eH$XhePu)ghTrPshc4@Zz%%^9{G(m9DNBJ|Vbl0y09GCEB04E>?+VriKG4`pBa zJ_jFnc$gnow3$vc3F_y@qJEnPruBUU!LGTjCQzVtF#TU=SX`_aKxx<>f;iq+(PUnc zREQwoNCEQ~N5t5|&Eo&YyIsN|yjccb#R_m3O;fK1UEnZ=rdkIZ|5G1r0@YhMgtyzk zuv|?mpB#V}3I1E@a$o6WC?irFIf9fVAbo9YJZTuio0|Z^K}k0*=Nd+kmpDL02}g0qX$A@T+5Zze>sb>J?0b% zhkN_<+os*n!Bc6uWw9}P*4mHV`7*Ij{U44mw)@wv`qv&|k9-`|pE)66yXlydA{AEc zxMgpst_w=S*9yKQ7XsTo{oVT4X6TCVhOB?Ion+kjw|@J99!^ywtJsuyO>gbS$%X&W zrk&$XYHK1BC9LWeQMl246QU3BtdAy*P!LSU(k2VR11kd6XE@PEmt&0k4b^C5c!1ew zSG;1SH|{Czx^Oa7l(d386?@)4eA=st1Ziy)9LB#$FInMja?v@GQ;d16_2oC;9^f(~ z&{_>KCEaHMza~qgY%8tVO|wx)LOpkogWb49K^HUDhgTgO4is$`WvJx8!}3XQ!~L~L z_dLUebZf4xFWEfjRicf5Vn8pNqr^`*6#z4wCx4cI|3s#ZYuO#ZQ(Ee9U)NMk;+OEH zU(DAWg+&{hzDPlKc9XVK(b}KyeICDU2ovD+8}-(yk`>g8@b{{~`&lK;UUpmQA(*Y9 z$Mg?e#`%Qj=Vy)_{RSnf7XPxs4z9S^q^#6=YqRe6xQnZ2%eG^|xv%9viavdOBH2}7 zap|K7&3G$5FdAzRugL(Nhf$73M*kXW{Sl(D%_R!%aa8=b$Y*URCJGwJL&-sT2uyp= zgg*R~G?c&J1f!O=em7rlWS3k2UW!!~zy8)^e((a`_a1VI@INvN9jO|qV@{L)^|+{; zXHQM1zR=QKr)g=3)yUcf{?Nf$W0mhIK1W#=mO z%3#e8%D9=@)n;{4g<&teEqvsf3tqi-EOnG^n;^YKtY{L0|1kfd!kw!!HS0p|FReo{ zOtvcW`;Flj2Dtbqo4cCI=R&&jJ@?wcsRobAefoD2;+nh|ayM?S)Nes-m zfe))z8`(-03C$H$#UJs8Ur)0!|13KVe%&ewx5B!0>NDncM{M4fxUOsoG7<$i=_DE6 zwbj&Z3wU@aPQPC{uzPA;ch9~$Lp5R*)8;@8lHbNE{-z8b2wo!;)LEKxuB(V}tKzvb_!$&Zdw--Jm4GIaqSpTQzz|imj zPgZoiyhM2!sVZmiF**6N3UiiJS$uPF+hqK;O>$C3rQZe9{hlj-3>-7mDfHB9=Nb12 zbhX(2!u-?hhlBf_Oab4eYMlQ^kN)qB-23z@z1Ih8kN()fC#nD_-z0x1(IBn5Zo|#< z?{K?r?!m8PyK63{^=8S(C@T-fuiqar&?e}J`??H7SiI-nlBr)dzi$K?C&y;8Y7dtK zK7IIob+{F$8LD4j{G_M$f9`NM|6c0ccgQb*iN5eLYT4gmg7um1++n=ayk@sXvUH6w zTxcq2UsgIBuuh~;B|bBL4K^`oEx_A zSKNZnG0*}?9>4kU9gI{-bY>6Dm2F?$7cAJ5{^QAgT{eUqm$%(A9shede^3P~O*!9y z?U~<}Gy0S}0q(Cjc7FSwI5ZsC}S^(&wtiMN|_Jo4<$A+C!|TT~uVC+zK>56Of|+ zb?Duh%Q&NdO3({l66{@k$v&{w(tXNR7x+;CvQv^C!gcd77xTbWwJsV8#Gxmcq;oQf z?O34AV9;uvGuGvcX|o-%gQ>G_v_q#sA?g*hPI+!Fg;nv`8$V5`9@NcGBG5GIOG6Ox1!t-4i~c}x0y}} z$Y~kE`m;W~6vfsjb~O3|&mv733}!ubE?E)@iE3(vtrAGqT3sH@8sWu4h#+K1!hQ?A zI&v^2>39aM0QZ2tk(<$JUc{9Q6?`1IDBAgnyuB0AvA2LMQW} z{sM~_v%$I@|9HU=#Ed%ilbq34)vxC-AuPGS+c8r|&f$>bZokLfPm**GnK}fp(nmRm zJcG*y(&3VJH7qK0XAlxw1jJhQ^EJh4hfVO%f1F&#{iV{18DZCs*vU?MnmtC>f2K(N zY7HeWzJcx6zm1{aN6>zZll#*#vGm~B#GX3Yrc&i67G&Iaoa)kCG1jy37`&^falfk9 z^xb(`V4iX;0{@RIA?m*P_p6j$Ld(zg>3tJG!#I=g)hn8`wRT=Fy7N^cGG>{yhX2 zc9nPT?_}I21zJOuR>aLPyEa!7fzzwR1+%}xF*s8e?lOoJ4SW)Vko>6Fwj)2KY$R;M z^4d1}cIOsQWOt_Xya0rX1KNq~txI?4P-@8IW-0d&I;IR0!)i9RBj40%CDT7W4R~8k zkpgmOog@G5e2Hfw5>4iF_vQUgAL4j%{eq2Et2@jmJbu5SQT8(1{|XBTDo5KlLZ?f+ zEqA`|9%i~{^8U>2;-zJ}+&{SWC$mdBv~0UBhHSEyuE6|`>LQ*9&E>tg()#&ukZ#aJ zakk;(OzuNvuqTj5jJnI2R7(n8yiY6~u!lu*xj)h(7(kW_o}7xj|BM!i#6a~D0qGt>F0i=~+LClmDHKJVTz|Oh?}pw6BS^c$dD1 z6`0*5s(FdWcFSp^EYGNv$sJ!^W(-#z>%+v45E~!pIbS9D2&Yf~w*eSV2r#HEoA<0? z5}|1GwV*CWi@v|liA`O#$a_o1$6*QPmKGGGUq6y$dA%T&?(>4(2gL7g2Be>N5bsCh zKe$ez3d-Og{up!K%2#;8_iXRZn?Mz;z<($iLG3ffPeWk+GV|beplMFLsL{-M8D8g; zH90ZAn(dRuZDu0hd*RyAlWtNnM7J@Uc3-FrRzKcOJUQ^DyZzr5 ze^#``FqFhN1rrJeL%ut->tAyioG8i!8QN=RP1vCYI_5z<I)M`AaD*I1~v zv-`MteprCepJ!x0M@%IUjF>Cy&F6>Z;+~pTXaerV%HJ*t=@L9!=%aroy44x4=Sx(* zBH_o`4*g#XAUY9?Mr}RYfu|V0CbXm)>rDa$R@-NK2moGXO7nTM{_7qL)Y1HJtvCEzhjiv;>-}4hE{z{5z@GW%^;+EnY)A;?h-Y5*uw4;7JidZrdJ%VM z)&g{n=xV{~(#tHcmoKEg8V$y9ix;T9Lh9+|Rn#3{k0^bunEr}bPg9VwLj{Nhm%1N(L17WmCC@|tzNbUJ$mh`khFIN2?hwMS4hg(I!Co)QX0cymq5Dg1j&iAZv%hD1f_GX*(k7yaWncl#aowJ*-Ve4F$ROpj1HDvquR6)~r!N zQNA@uD9Jbk$hNrG$&4A-I0`});><>{>4zb`O9F?D3}`nfR7m5%V2(F>PYCHf+YymW zHwtr0z|uzdU{K^&OuA#h1(B~CZdFG{me$N=$TM7DnG0qP(Hdl*B3bk6%^tqH-#~m1 z{syM?cm}Jtlaj-ebFf(aLck{hx4%MSS)X!@9zoiX)dRhWI<5T z(P(N7u3I0bkVNEtz-rJ`nM`o0aMT-17gTxcWRqj$iA7V}Cr0{t)e^$;gYe~83+n|G z)ahNs7&gpIYF#NoXFTe*!VmFe98%l0sbdW-C$$pRB7cFm1wosa*C+xEIXG<=+Q>Hp z#@7MdVVq9;lNRJG;<%b$3}bnPYC>bFY$yZN%RsGxt^~A(RRx%-T^x!coi)80imlQ# z6-CH<{fo5iYjYDdQ9+PEXGF)R1?2$le1mmntS&@+j3*PwIb-Gulz3khnR-Q}5C=jj z3%SA($YjtO1#H9`$Om9{(oJK(7tu&sr)h=YJz1+(E$IszI-$KF)o?409GOxC@M!Cp zXDA&bUOGsO?L*^MAy_r3F7AH}in85-kPbmh`tuCw1aq2Eao{xTijEdeU$5%PkrXh= zRzb0=%km$D-< z!7x)qm^|~QiBO)yx_O-Ex==K}r=WBFxtpA*9_3U0F%rSy8j>WnC=B3eoW2s!RY4wd*zCD%N%lwO4P@_0)>G?s`b=+etz# zm%5!Gl(=C<>{H@1E8-?4ZraJ*pw#D9)HNpc%(5RT^{s6`4uN`Z+mA@SuM(dHtE$Mh}5ZDppr5W77tb22m*#XmE1BA>Phj+K(H96Rtf^e6b=~~ z!fI152cq?96o7cRS*!a#2&4WqK&)!7l!J1oY3lEyx1VicSzj5rAF40RYc5XHWz{s9 zIh9-Y3)b)UcuZ!%1>{x5q=@3Au9n0mle;8taubv@qVvMM@a8~CQS(wLUY1*`T4`ynUk;a)S5&!a5M5zd%rMQv zTQuhZcR7yU1|Mj|t;H-6$=fOiW8S8|!F+;rg*W(N%s{okC5Hwzv`Y>RYHybv8i*>MTX5FGujPw#*=>{nCUersoZ+Az`23z7i7Z? z`w;0wHyP%p%ftBk<(ediyV6v2lglV2bL&M*J&?vx>q0u7v%#e6t*dD7c847h(MUa- z9h~{G+-y#41u@NK%RIt$FKY#B5CvXq6>0>TY@H($*EI8mBZQ0{7xSKU`ej*rmaN~m zu5QZmu37TN17>VUF(}Vb2d3#5;75A_9VZ`5X(=iVr7TmFqX@;cER08DmMSh&Am?FO zB-w7-mnIcNkd|dYbY_Vvz0hixBAO&UGq+=+{hKN8X@L zh~Ze668fGhhqgpmn4&wOZg*VxE2P=(^PEVfi5}IpA|xu~*>((gPlt!6Q^4hDD!irx z(_qSe*97)0B=-#Xx(?r(Oa>p~>$k0N)HUE6Iy}s5^2sv^f2I|F3<(tq&`mR%hCOT1 z=h}%wR^0xmHNfUK74I52@OCr6Jp()mjWRK)8EY^O-bYgt+%G5}U&zqI>jpSnJ)O{l zay^5FfdObG8bPmxpBdm|NIo3*u1)x+0nY|d&4kyEA5ePm6cw>9r}+MnmIqifMO?@n zfTSlTE?xl_q5$D8uZcy?%;IYFc0^Xqf*}FtktQy<+TjCgdKPsvi_tg^Z;Ul*m>ERl zFa6b9 zo9%LOD!qWpPbHEkwZQtw{mS1DqbWdye5d@epCdWd_JbGSeS~zj-#g+vCXn{|ss}Ay zsd%&jnixq+<}F?q!!%ohbPNCo7c7fn5MAO+E5qoDFo#cK>9%|Agc^JNgr$ipZy)&9ES-LNLTdx|E2#x5~al_$ThccoZ)J|-8fy$m9caB>-nkE z8g`-c17`rE#2GF3kQmPC(pY&=$eqp8*X`>5{X(d9omKV!aa{lH4q}cZAE& z&eNPsubRip?=GQH7wJ9b2TTFTFCuSO`~7kaOlv3PUq1Y+?%aNLx|{XvVd1ow^IwjM#KyL^Jn#%mxTyy#ttN*8lmY_=!P2p8qqgEL*u zpNR-xoz^|vOFzwt-(LIuxDO!-IpgJG>yh{Y2MxJ|OVA#?y9lznwjT?QpkUE*1_yOY|uB(a)p^>LCeA=aF3V47@iOl z378zs_8|Gz**pR67;lt6kd92Cya}?^PivWEmi)MRf?Mo_C8;CcY`)3>4#OaXS68A| zyFchhAHdG*^`lWRojSd7=(HTTxzX=j4O*cyxe6!aX+S!5x?J*GI))~`!(JlIxLEFq zcG&TXe;^%8GX&7&Z)E5$aU#u#G5GYgxbuL?408@(*3rGtkZ&Ms)Oxc&p$UxnRU@fe z0iO0O9jqr#oy__rn4-?)VKyiIIei8OwOaLr=z-lHD2FY_sd_}czH)2TGaSTM)7A@m zJASpEKRhp%&Yu(gclzj#5wUWO08?NS0bMoy^*>902nTXO)e})0GzL&Y~ z_KVdzQ*>exkZ;lOWI77X#}%}~PRjOsHH0g6)4@2J3W+)4>yUr#i|-8JS9EXUibkpM zeNB3p01Q%M6<1tH-kq^xlkY{q|WR!2Jn^hDlxJpgE&F-N8+cJ1tBDAEaGu@FUlkd z;i$yk3S#Hv5wiM_0UzIy3BAOEo6cfK^Y8o&J=P+f^$Vt>bBOV)_#R~W`n04ZkUHjP z-IyWd|IB7#ewvY$c>$@QbFJ}2CXVouMXsw@vge=0DfG*mJUwsKtw0|N^v~-ZW`cGw zT{_JwEi?T&okn@UHU%VHe-70En!M!;BxgPWASP*sd}$6#G!2g)(zJ)jo2*G|?0@_O zdwTvi&l!jxlAM9S3DP5Fh8_QEC8pR;6RA5ak-Cs|-C;@BG0E1;$H&!1%HF2zD*ovW z>b(R10X+{X_vSOQI@~?4F{jZEvO1Gr4*W+>8I}kQ>Nx)RnKZ)P?D=7Tq^#7-ho?06 z95iq8GiI|Y3xf1=&=p$T3DWcaqo20Rdz!NBCCypJAAHHP$A$QC{)|0!Rw&zyEd963 zn2jfk%FAfx)}q>l+xLd#n^*YT*0jKIR!2m%CkAI1V|chYrH-55q!g zs6s3m16)r57?XnOnh_a-J}TV-Pn(B_!;qxGi>4eJT#3QY)rW&w@*uY*q#>MbztSXD zRQDw>&7K1Ab>$m%#f$$G)IFPAF-Qd)N=i%wqPgJcY`ExKU=jI88!<2`R$c^Qa z46jBf4nn_oI>T=Dn5BQ~K+T-6^PawIhdEC0!72e;O^|uI45OQ2WW^{?2aIg!TuVC7 zlCCCK0NTb8Wri=}&LC)o+4if*FfW7h@+dD?)>#I0mIs~5pzeUCwr`<}n}2Fc&HN>; za6&FNhvN6PTnf!%J03;hc%Wk~(#UASkTfFQ)VZ*&!}*iU6lBPTH5th?V=7|`P(f5i zm6XYT7Rs7N?^{|C@yDEO!Kf+%=r@^$r(ep@L8}|~bVcEhoPN68J5zYv2%Y%f(ZAc{ zD@@xUyiF}LYeJk=Dm%xkr)m{#V~sHBL>=U6!KXm=wfow8T{E&trVC?JNaWfOc{W5f zjba(}hy6$=uuLU$81{up^7)8-%yc2jYt`7YP}1n<2kBO)0MHnTwN`${sR z*xJDoJ(6h!@%gS3iVV3bfnRuBW~xo#TP{(wab~!pOgeq%X|uyLEw|72OfEqhrn&y9 zSsc+TF?(432TQM+T^AkHRJint1g8$Jmxq|fFa9&P)nF@PcGDp(l(@TDoun%&OJrSg z$%JD}czdKOw*2dtQM;SzA<{_jN&`3udy}a+_#ldf{O~&pC+@eB0+T{`snl?#Z`f?M zd*^Ysp53$8mf#>4PI?{TsMnc2e4c&Xi4`Kc&?RLCqj)mBkk*)*BMjr4$>oRE2wp#f zzrv|Jemr>I=mkrjjtQE#n+Kt@4WpV5zvTDtesumfnX*$FwVl)YAOZgBdQI}Ta89Zo z(dj_sPmpcDIGFwu@(?@B3ku~YDr-s{W{I;4L?!7`K8E{~3(zs=4RPS|>!jqzbg7(N2&D-W&MT+i zn}Qk|M6HyZe3gu$78$jZR@d2YXY1YLYJWoJ6#v^HgZK*03NYQjJdR=Rv;UJ=tIYj@EK;;ZO9I$|L*#b2a3AuQ&5o)dW3>hN*I-PPO$LRgW#;n9kW z`sy2J<^0XzUM}+K`|pomRA^DRM?^^xO$CX%(HXY-B`KEFC?x+`Nppc0{cb!t`T8D|2ZUsH=8T(~%3{^_u$c=d`l=y%^4ngy%8 zy310Kng?NcjKaR8l0uGpKJZ?_vzPyb;ro(TgQIB#mE{azgOz*@o4x=3A&Ywa;9*;VajPD-@<5e`B7c?dTl}OjcF?K?=Ato4c+rWHwJ^!o zE{Y5%zI3(v;x1vq`iU-FuU^r~hVMNrTryx69&pFGnOD*{z)#5LsSjz}ru{BKd`ED{ zQ1=ShhpY879PZfSJ;Zx?e6D&&q?mN%pqLd^eOK`PEh+I=)D=|A$lRbX(x~hG-O-=% zxETaOP0;U>2Kr02Nes$;vh>3@-k1Ni{xghK{w%yWDUThQ|4Gi$hjFuf*e!*EJ8;Yn zx$PliKk05J!U;{0k7?|a@~bW>oo>p$)l+V*f5`u;!c^~%qA{Rn7_`*_-w%*v z9X}NnK-UKLzJ5a!h^ADH46{K8D?yDZ*iJ>vqm)`#^b19$W&2grekwCdS3~Q=2+InB zSXV?YQfx`N4m;%drj0y*4W&PxN+Bv>Gn3LqJ00z6zNV8o>k!L&<9uFH(-eP&oVH7d z-8qGyUyJ%tW6kHxcs8DIk8;*Lk+j>saE_cm2P`%OrTfA8%MO$X9-tG`V@4=suFkP@ zoLx7*;b17YT+kzAwp54*34Pd0Kg?GD0cwW@{Wx$6zFj@xlP9Vs5_g)knQ$c$Hn^mf zPQ@?+f`u23$WHiSS@TreVmKOK{esJ^iqXSzE%I<1N9oBqVm?3K9;v`QwjqOt>lSTv z9pXx1*Y5k$?Ye;1-l`jcs}&Quo9@ZG(N<2GuwDpEDh$BcWYT zwQaG(j}oa8dmt={bp~N0^o2O454ai3V|!?%0uX@iAbkscOuG6=ssb=h8$z&%MdOdL z`Oe{^gSO@)H8HbML(oiz6#P$WnB)q>vbc>9Y_}VtH}+qg-2>V3|7C$s>)^vW07P@{ zFKC(2Mmu4Rgi_^BLa9qDvzJo$Vd>-B+(Bm2TQoE&@xd~)9-ovX+87?SCd&BvX#wfS z;hIcU_L*1IMr7WV3N~eX*dgY}cdWQSt{03U7M0>=2&NPq4vCFlmOcW9O%zoHHatu9TE|ACiPtxOYRooE2rvHazlA z)sz`84)S5e-O1o4Wkf^N)SQyvaC?#}%K=mh-WYfSOUoJFKJDPYl$`C}L|OjT`7;sz z&(06<-#^nnV7$psch3(G_}jDV|K%k`B_-3Qi-q0 z5(MI6V?95mB4=7qv1VmOOH>;E@Xv1#KkP#z-Oc7pr}sxiAgcSA8-z!hUDOGj%cJz~`90{#ePy2EWAro|;EzpJeu+GKLYu$RQQ@xF-YU z2tFuEM=^Q3Lk}J&NA7Tp^#wLE&>9N6SiQ9qF6c6LI;_S15~aj_CEp?%>~q40bg5#a z==@HU`-J@!jQC%zZa40ArtH%o!VoLEt&|VrBRH8mafG!(_P6AJz!qc%5suWS4eDgE zDe2zwNci-<;~tasM!Nr@-J*d)%xe_B$yzNxEm0ewk%l`3Lb`;@JorH8Uw80;D8B^5 zr!!RND|fr2tOQP#2Al8Hg##Yn*=kMQ@tZX-ArFAzk-?C!?)M+zP7-(Kms45ko|25) zUz0b3bLxuzy7?74L$`{^+9L_o*KQ2A^1#~)8$%{aF^FC8ICfq+{u|mok8pM0zx@iA z6Ul(YcGT=M(~V%2TjdE2CbXb5wCZeeMcXH1)3Et)Y(>nbjDeWf{%@I!LdA%mG8c%H z2=P0Y*PDJr*B2Bx9ru-_mCz8)49Fjaal_T2Kyu=UUM=jjlgyaro+m~39^g1b;4xQj zTN@=S`_Dw(s2QSq51Jwfp`@yle3VDRC#KIrS)cK);nTC=bR?_e%Gx-nl6b0(G!{b& zFVfh}Y3NxJc4oGusoGyZ&Yq}a9_^D2ev!0Xc6FeWjIRsyE)X61x`tV8e^V&_?Qii6 zt=O2f3;t2s8YD`GJ$!w{IKbqZLtGY17V?9*71r%Gl4CqX(nKN>Fn8vo!u7^sM}}n& zQZguUla)44Oj>k1qc<`WPQrITLKRQR;C zPrC&rMXBUTg_eb6fk<9mMox!mMd&wSgr*A9eUl&IsR*&CA_&Lb&%e;Y4b2yhhewbR z)$>juwVdA||4lYye?F=FnP<89^D!Nx0P2SQ_d)0!2`U+HIiJYo&<+}npQU6R?oixQ`#eB|_)ZY`cOVc0a&(&ivKwY)4vlb9YCE z$PZ4FeG7}?djINy7zq#j+x_hJ)#ue>|B-zWtQS9E%=7=G#`&N8KQ#Z(6Aazi)9Uo& ze@XtIYR#`TviW~%jmH1v|M~C9|3kB|tVuJh_dBvDo}Xf-4Tv{uZ{{C&uY)xI&Tjd< z*t{C`J5tV-kHj`6oBKOB&3Pp430Xxx%^rwGrb*5^-+#|@b&R=U=UF*Ymd??H{QKC! zEk>-5X+HzaPC%HQfwM1m+rEYu(|yPuuw!C z1{hO*0za;uOnBYQft@HuQlalL3xo%Lb4V`Ie6zq?F9|i-C4l@iw+S7eHE7 zV$Z-8i+oL3H(?$?yi>*cGKs};JZ!!l&jGVplM1T1K*uF)rnIvaT(C>DOdwqeWbOnW z1KRx>9JPyG*@)~*VY8aBYOtZ&Ho+- zEqs@g5_l>C%e1C(s^7MsV1 zudlEi)JjLo)B97F%lm{yw&QYgobONea{Tx$G@5_s-{NulU&n9%sv{Ni%vQhD?rL`w z@~{8;S3w_)F{jq!YByg#Jj~Y14XF?0j@drqqQf#PiR{_9!F0TscGpF|?y39#4I|jX z{(IcRaqIl>GK}`0>-kGx<_y_qxrxPe8Tm^GU<{%8LgjjRWnj}4Z3Nb2@D`wFC+9Wo$li2c4W z1f<2mezk-P4 z6Bs02#qtm?9kF7`Ji82>>9`kNx5B{bPn}6Pe&6o~UFXlODf#^8lO%^D&)?{bdzjDh zZGY6oM32GE1YC*EICT2M$)F!}Pn`a!Gq~!)pl=iCXdF41p@8s>##kof)(;4-r%3jA zQkd$I2m!_&VLs}QdT_Tl7zQJ<+3AnScTPYy6p=H%Yz+ojVC#xhBSa}-K8F2^%gDJL z54vQ%1~%=0hzRa=S|F)lXVB^oPn>RR*t!T1i244JjLCR?8Q@o>$kqtBz$K~i2$~2J zg^-UYq{d;Cpsz6=t`+vD&^WztOiF;(B?xH05!Dx?Q9v0%27sM@9axGK|1=f$d5sd6Lf!e*Po=f&Cd_MJpU*Nin8)UoM~s z=ifd#m0WlfQz-B!BcbMA|2y74K!g zP&CbvB4R9Q0wJCZkA+CJGa+V44t+dpA`s_CTDkaf`FKnI`|A;rzj zMj5@t|Ik2aNSrx?BQJ_!rz)rLKl1(0)nD+yX^*EhALm=)%m`rLJ2(mA%T9;~RVTt{ zo*)KV#`%-Lf;LgNeIeW_goERG#`_}2!Q~rM)%6pi{ITQ{=ZNwx zkaC}hdOdLxU`}L)6UD}B4lp43T^A0E<(w{qTK3KCLFuyFqNBl`pf(;Mge@6+E{vz9!Jc+N1e`sGFTf{n8>$Bc@imbST_ftacBGeZ2nPh9@9L)LgdE5 zOf@gz$TB%^&bAu8L7P)3;xROXuq>=E*&p%Kew#EDDVg0XNE)AAfIwAnlr}n9)5JFa zn{zelM^gxS=>=>`DLMj?%8pR<0oP{G{ya{Rg%Ra@gZ3BZJxEI;!{kU90TArM2>Ct2 z?skLoDv7UTvjYIO^WVsSn`Cs7(UHF&wBnhEx7#-bvp)Mf&*l7b&wuAljF#GuD9;p5yd~WM!J(PUlbKn~KtxeMJO=v_>jJ;vAINYDM1I-f0k|;yk z0nWcV#)68_3!9P(?EFQ{&mfCpHhO66PbwpX0Ojy6s5C8RlP}<25te?07rX!R1VOljp8SOZ z{qUDBe|~|Y{F!qhUpb`4Km6r^NeaJW2f=Nl@C)8GioX1rmM+L}BJ?VQWt%!_Pt0kH z&B(g`KVd{NML_j@I;J+8?Je2g(XaLWsu~gV-{Qq>yj{*W+r{w-f6X-0pD7Q+1wI2X>r{4qef4%g~&CR)cfl0Pu9mDe|!adTc$m`=?HuE9up@9;=5&I-P~P`jx$EOgPed^pfVb;iP2oAYFD! zY#<^^##&$o7yTP;*!(+^8mpk1yv-$YsWT*QoV~JXRXjkV>{!D+tN2M^m zQBo040O%(fJbNp4^=qtaR2 z0tFp6%*tdG^WWB7pAsBq3xORPpCyS*Y?4Y%?<6hES|p|6LsXc#XnwMFGPgeBXd!Fk zi{i>Ois#k{@c&= zn_nPkj&4U@zCKCZN;ymUL=fzI#?5wClTAbCU4h;cD}=JBDuZqjYNxV#u={W)3V~mk z_s`sVPn(9ff?J|qA^9(_+5HCM2!E@_OYofYT7CaL<4z7YRVe8W_ZNEBlKCQ8 zMf5P+MeGG{b&U{g_={o)Ul|4Abk^&s`XEN0hlDPf0Qz>TX4|c5)o#@i?WR;Sg5y@S zzT@R@Y^fM+_RAlf;2kVZkj)!ILT}kqL-YV1o)G1C0QhO}!epB-f76~zrYk|t6)V+l z`5Rel$iMhrbaXWsh${f|!HFg?TQioW_FHTMrt8g8+HA3ghfQa47%Y~fJMs+PnRi;c z6(LhU!K6(}J!M8M;`0*WE4_e&bbkVYi1vso!$<4V=x5%dG3Z|nzR-pV9v2DqVk0t}ip;5)(| z{9!-<@@}3WYVtVo$)u?!SVwp8LICrJ;oSgwjo>1VSZ>E3e`L<3BOyRx4^J@s?QbcU zaj$1!!kj-~)e^2^h54!gPiE8z2rX!y@OwaNVkzoT!)Fn2Be#a{G_G0(Zc{XRIS z`)hht427TF!400jELakk-JftXCAPRQ*k1+htLXv{6uF`k1 zkr>L=h%}&T5b>y?)jD21KH^4_ezAGp<2MdH(T`~eE?m-q!^91yE-N?uxc7+ebs8H8067a6&EUz;_z?O1_dFMg zBThx~kv8D57%g)#b|BFZ^cdOkpJKm48+`9Ha7fUKvo7W4Cn+&aI!b$hM?MIZeTk0V zm&c6YjrjXR?stBYASA_;s6}UTK1PToL6y6p5Q0lJTzSF-xf^HLk=`6kYjcv^adW8B zNPm*Dj`PCx^bZ0(^@EF23geaR84~OhsaEoRiWi3ANv1sECnexP56DR<3P^`0dmkhL zbU`i5yyFl|gqMNEAu>s7+~IEkFPBMa{_=HVAMKGGp0f_|Vrb%zWG z770!Me!`iP$ie>v{(P*lcWtQTm>O4HlF1IfcMUgwX1JT@vDRd+5ew|-FAoo2UjOCG zu`uMsz}n}Af9%lQi!4u!aif}qh?DWOqske$IlCoh1X$AB(A3!e2Q{|;L5yt}63#!q z7h08N)MVZJGY*88j!)XK_Dg0h#@c`H{39{6zyF?&n;#QZ+aI*YH?+52<0CC-5{p}0 z82fCrgI|IF8yJDwI!-bJ!&ViywMjGd$M~Z#R=MR1^zbz;uVium%uQx~{q^1Ra{HC^ zsoLN2?kWgB#H7cM9K~dIGA@^_<@y`&j_*Flpco%-H(#8WL{`Q8d|^|cDi0Fx_i(M_ zvWfft7n!ESadfeygI2QVKhZmVJ8^j@l7c_#M+gIep?WLiB_u+<`f0oTM3mb<@Z=xt zBV4MD#H)4s3KSCSX?H;UMiAz7bq9wfpK$fa0N78*LKS)D0*o*x)m+aW9P}E(MYavR zQhKyl-ag+S%T+!lG~jwXO^!%zmknPkj8V`;PqK&{%$!2vDknpw_%a#?4+@hNy4pFQ zJ5W36BophpPNE*S%io??TcUzskovzKQ;n9XBI(#v_bOVG|DJ!8BtIp_lVB8+VQB3> zfIL_+{=H_CfPbS}sbKk7e%WtlR3dOB{^qokl?r#^Dq^h-f-=h+=&|XpNj# zL`TDuHvBd)O=-$S^)#Y-T2Vc{sGd$#?^{GQYAd1l@=A-3o@3#942<*?lZ?@HCcQio ze*d0Y4s;&D*KEWC45m?T63MIuX9!;kaznf`9`*Vcad>0zBEwT)=q9#fPQDOE>vzeQ z;&@HI6eP>FtO!-F28qna#yUl%>$F?07nxe^u~x~4)8+cWKd9r=e-3^JvWpQ?!>4Jd&v z9a~rvvU8#*+tSEVwu4y{AURR_RQc=M44*kApRuX`|e?PlneTR+dUaRiCKSi z#6sMTv)lWhesUTzC^`9$mDkJCsJKjFbdOEl4L`fTs9+R>T6oU7^*6yoftN?(^z%b< z!GxzK{mX&|T>fQ2FQLIVNetkLqnGA{hxoD3zHA#w*yO!^O0UV>;R;_G;w;oiJI*u1Jc&%okM(^L*;0|eAsb;@f_RaGS4Tx& zVlE^YI z$}i0A_#;{AW6=Emo_yynSNi{{`4QU8kKkR|&E8U2*_Xd5J(aNkkV25O&hp3AZuY<& z~lqsc4*KTT#CUH#wA(!jE}Kl+wQD^B4NZ zW~%V;W!BjXHYl9^9^ zUWnTtAW!SDg_aQ;hmL|Y2q(8M5z8pra6!(|z-Bl!9lc*B*?A)g9}MX?_)uW->6AMo z=zsrJcd5}3XC-Vcu>wSW{eV6!p_Ih+sqt>f?VAG2Z2oBB1x-ARt}I;}kNanH`w{}) zy6_$OC{7^um2^{#eudr)_V+3IUuN_JMR5})?Csl!RQ;Hp!J8c#;Ixi={Xt~y($K7g z*i1M{?^X}{JxE>GcP zdbh*tR0xMNv-u2CT@oN!QXyQ2;2t6K_s7lWr0)dbXhPxiofF}w<3G%Op=o>UEX*61 z>#Z;t1n@LV7)-}kVaHNuuqQ2>!kgjQj@vFDmb2Y5xn7m5CCi65!s4)e5bX!@k+#Ld zDNhZ=QVrpEEHw@MN6}*JhC6RF<_x7p(3U%*`S5=f&RTfAyG?IR6nlIWZ$#gJwK@D13ko^&z_P)fZ3A}RY|`2 z+51I#I`zFotfsjW zCdP)HF3Hq9)=5I}xT$ncK_)Ai*fgMk$VPL)P!|nGC6U_vfN{FO6TW&#ojcSK&#ywV zF2ep{eRBd7io>fUHFtL`^gT8W!avQn$FLIquT0QHO3Ruvg?z`_=*|BUQZ!UJ6Hx&E z-4T?6!C(MXj7mC)*3XYP!FNU1S9mxs8Jd(OHnLd3w5MB>7_Mr72AYbO5bi#oCgtVN zwKWnFOA5@5i{x%5ZlsMZpyiRK`k*7sz$P@X+2+`QihT9zCwi>+ z7rd*Q$^GH143){kqc3LozEFU$fJ3$l|NQU1K*SQ|LLQpg!|Fd2xpM!=%R>AbW4MIl z5_%mS51NFOnOpqrh+gMF#X&E2(C?EwfjqWZkrg$`e2Q;}@bQD2HQ!02+aXvwo$EBL z`oy8d;MqC7vIfaA`FFxiOj3nC7|8G#4mG*6CuUnp!Hj*paF^!!WLj`LSIygoFC=)v zI~LBLesnw!0>$Z{HJ)Arq!WFnRJn@@6MEDUB0`LP2 zcx#3adYXZ9E(KXay;xMGCiMYkz)BnMd7Hijl3QQ!RZ}9>k>cS=p35MY43IvP3UNhb zRP53$5x38~AIZ!lxV!a_$*jcpB5&dKX8a3(Rb`532tA`7f2&^{xCACYGpTto5<-z_ zFC3@(1DO0}@eADl`AfC=z>;imwV^2QI1T5;`m!TG?4&e$S2w<>xdB`uT3#MPG!>6H zOz_5tZkU)xi6RYGXST)wXF+H-45;jKoCq}?c%-g!aPyo!>ZH9%K97&=aVmMR!TrS- zFnWYBHz$;Kwwjn)V6M8@rb?IqS2fZ*8m6nY`wgn-JnQskvQ46jmt@!BwDCCN`N_Gw zCLc4B3BkxFu@W*a&>hN|mxiV*cw7kS3gp`rX(MgPjD~PwzCp>w=pM_L$KgSY#|?c= zg2x`yM_yO^T#NJT5^N(PZQ_Q+&gfl>1q!jMipmEN&eUH!6qnp^PR}gFXjlmQhf(|t z*#VA@NOhm~+jxJhA=R`Q);oM&L%zI6e8(kQK}-L8cs0s|bLZcOC=E0pky8|i z4$o^INO|-OA=yWA;5T(!%M^JM_&V`yo7g2pNxC4qH-<<}8jziBsO&b8 zPH1I=^w+!RySvqV1vdqfj5?Y@u4tmy^vxLuEUc^;Nr&p)OT+GEZP; zfF(nNSczu;b*IuwMRNo%C27K5)fGtcDI^aN{N)Qw)nyt}E7d4-VyBL*&CE2BqIj}p zsSQn-1i~{@LwK-EGpJx^UwSw}s(J$7kwHuFr8-y;j%~g*g7K|+@mQl5yg$Kk;}QaI zV#+GU{ejI)f6TTtT{{C^cA@2O7SiJyxg+a4)`T_iDf4?dY+YeKE`vPuxk;XLi=`M7 zMM}@Ui)G@54NZ$7^(xuG3(KOSfbhnm0J6uFjHhx%L^ef4)jS`Ci}?r8R1=KAc|AT2+gd^FOn5GUzv~dt+pyjXs3MJQJ3iFkKEy7hLFj2GlwTy zq)mP0iG54#x2K2IEO|aGy1~^Vy!pen{d9BBH!rkUi2a)XR+uS1n-vs~Fs$N8=a6~` z{`DizK$y!8pTuGj8adGycqZLO@*KpA+Hz(m=SF|?rh2I%$ho6$HVe3!3&O7jVRm&+%3B8tJKU?DLqZ6CBQ z^y`u=xO5MvdCLQmyis0ngm)|S)zsB9^M(QBp?`i%9x&k9JmKYnXQYVdD@E8UE0c#q zqzMHemB^B?q{?`=Bu&_PmzKgZ3w&M@i*E;`<}-hGB%d2UtuIWrbi2m~=GHhcVE`J& zW~rfRGa$FgfD|5w$zKLd-{OF`4oEL6ZgAi4KXMBh-k04$(>XK8{lRruLJZQ5`_D^a zu3~OY)B<0`#{$T-+(6#R3cPonruJsB&+J|6#S)&bSfnpE5@u`|tk%0dKCyg@<`l{mTK(eQ9jB+a<4I@S4cOfk(OE*;F|7|C`_X$fugDH;y&!+c8K4a> z_|Rn=(z)|{Hy{}bwp`ES+dHW#+O2Nf>r6#iTJ!nx3E!}!l}BlyzJ%^zDX>k!{wIA% z*{*iZ_${d)U#nL8Q~Vv;lD)v5xeW_dfQR$q`FhVEgCPYcdC)~6XjZb@`006nM9EIr zJW0}xw-yWf&IYR|OIX4?nb_9P%Tp>3)4ba+Oy45IW9=(HEEOKq{)azM55olizfcvw z$Ub89N_zOpIf6&bFJK26jxii$dYr$ZFTNzD&b2DP4p9DafIpYdnf~IxChME9_+-v% z@DeUW(?HU~`NL+ngnX5oTk;wV$+eqoZ;7N2yJh-Q`n+gL!d}JDtKUQQFY1$|XR06qM-nya(=j~JtVCS4}T%k*8buC^B$mIx+`jpWglqi;)y#mViH0ag}M zBU%twU&RWB2V+(Ep=a`hF}`-CZF_pvAm94tB>T5e3NMJz7c@SU1TV#3O;8Y84!>~F zX@%YRYQi_|9k5M+^#J51y+Mj#F%9sjXYww z{n1;@JUEqp{{DL>+4l~X)LvqDBFMjQR}XMkX$A4O5N-Z>hIYf6Ca%R*FG*h54F;_b zv=}Ur-uRVzK}f;DEP2^xD_H&SlR1f&XDVtw^~5NMy7Y|>x&y`Yc)2jL45tIYNt5$t z`ayarmFiOZQn89*^-E38$%xDro6lr!(e}o6q+S?^BbudSiCM|W4-kS4sHzt`s)aF< zO8h52FzBON+g}~hVc1TrHew;qPiYa7cV9^IFZNz8_&zs_(l;VC5$jB2)AeF67xVie z<>(5E#DZST)6)v(Clc+~Lkc&S-g$T8yn2bo~Gg{dwt-<%s(d zG29c=*HNDqD4hLvw%$EL+V3?y5y3PPn1q<)9ld=lZIBR>q2KlSP{LH*f9Nf6xlsrTjk5cA5N#$9|GPVVk@(lyGl7a^ zrt;|%?Gg44VdCw`bVqSDY0Eh|HxSul?G{3)GEWt&AH6+dp0}dM$l2lH z@(F?~yQE{u2A%qLpqHWHlb3Q?x5Iy4{iI9?g6RXq6w^3nDUv2@Lo7~?3>1!}9+qp0 z5NP=YB*pW3xw}c3+VFR#myei~Ic+NT=r4{ExVf7V1z!u5NqGiA_4B}R>uEfk&@8-Z zjy3pcG>-e7u#@L#cyX8=g-(M}SIF3pF9>VKI=kjtDR{jY?%yXK`28M|UJLpqIj72; z+2O}G(&-uq@I05?yWfAG%EpL8@tLbX(PL=(P=fptMCJwMtV;ehq=X&wlvkyLk6~==S@M*nfY^2KQuMFGaNjg<~Q%I3{1$ePk>e>hcg?BQeK>zuoOl-6!xZ3^#RjF$U<-^Z)k?e=h z2K{8HRG!z{<^5_0*@5WyqQ9Swxb-s`0C!&%zdSwfGvgOmEa7rr;$LL)-DOEMMw6=Hhj3~#J=}<(3jt)&Yh(vH>eBl5+|*e0OadM+il82lw>DZy3TH&)M_lZCu z9%v`Efd&*5L~7tAN;%~KE&+BmZU1q(B|i$im!BTABP;2JZ#7FA8tjNWlu;^Bdfdk@ zTqhpfq^YP#eA>xz)k3eaXTf34f*}N*&o;QGDx<#_osjbt{{2R2tr@FG-G7+9i&>Q@;KqS57H$d-61cjHskLmAy*-C z;73{4B^YXn3e?i(AU(yW`Vw-2t`=Y3{IPT3kS?TmE$+87de1`$d}8xvHdpy7A+}2F zlnSi~s~4>V|L`~9nq4xlKV+lUXr4HKe%yO?-*1v#!JmiAhlkmE^97>MAV%oM z2}VpK{Tbg^|R?dpC7kr_Z%Z#%o^ zC$vCrzrtO!$^|@h-(D^smSl?$A5Q6qe;cgsKklbrcc+`}J?e-G|Hg@y^N;Jz!{+`g zv01jD|92z9gH(*Q&jEAc=oI-Er&#=d*%jh#J?DngD1%Yxn z+Y(dIJ3n#Az^}e|@#>HL=f#UY(X!+BLHNEOT)%h$Iu}BxVdmaAyXAHTm%*Xy)_8+< zyPzqYXU_e0^ZW$8{gH_LiWr-idIaKxZl8CLGcZ7qTX)!EIJ|IM>uerRp$xpUxFrks z^8=YhY@o1lK$>ZT2U}E-FEq@H{F8jsPI56(=7c45;g=5YLq4J)4(JxH43oy+(==7P z&ExU~O$2ri*2eAg9z&v*U-rxOj>z%w^^~^6<#N0H>yE{h&EZD~Urv*({JKx;ca4g0szL5;6OJFJ~SO{bBR8i>T>?>St-Y#D)z;auWDq%30!}FuS z?f&(QW}>wTQjn-{a&4Q52+ zf<6+XdUj8n6$A7@x;<%894)K${NZ`Q&nqFsjLC%s?i`HSz+lcdkB3pW+K z!gwdz>(ac$2$-*|bzW`OWauPK2%T}7Y3bFDG7*~jx*uJRuOg>4`rur*!mu@pKAd1x zH)LdyextEgpJ&_cY`y;qMgITT`}_Vha%5o`KL6rR(UZL=fdo4=V28w$*~NepHoPt1 z#B*ln=GTVCW&<=!Lu~I%&S!t>MN&)MYH%i*J=b$zn{##&TBVXyDwV3FQq_5L*a2PN zIq&ufUDfGT`@?4KqE+oXy^DSi56FQk?MA=$7VuTinyu#WC+JL%qI=WRbf$&647LpJ zk=lSC$#<{lR5_!wc%;@gR8J>(1p;#&G(jYsx2l7;P7k!nfhiPV<@g*WnYa(pveh3Fkz-7NgMQymsx){fnxlRYUr zA7}FbUrpzX(t=xXvTm`W6`OtiqON{HFEL8AH=b>Pcd^6JLFeO@lZB3)K@X`%@Y6lv z-^Jq-`dupGZ{q6=4h+BH*v>?^ca2-Li##2!{4p$EbZfW=u|!f0S1gK5UVjsB1=1JB zjgpOeuetmoVUDkaNanL~nj{b(LT36>)`En;W+}{FLwSu0q6!O!O{~1!?p0NX)5L@~ zh?(%{h@D$>G81cQfzMHZJ1`={Y)t+P$Z%0H7~THSH48@+(AW_6J10e8KpKa}z9jOt zuwi7tDobG9lAo;cr%J=<`I#_KmkZC z{a}s6s3GJy;v2n~ole%Xacti>Vjjg3pC|R7TkICB>M)>d145MrK?gJV|CF4I0Vlqn zvY(zqY(w*rBS9(BDlFg&=TZoO(jLZq5ceO1&%XoKVHde@2D_xU#*Gr@i=q-PCwLIr zK)UfH!=6An8HoKO{#yWj8J!nme0ppPBsK}WJigL^O28lV-fHRdmJ2*^@&3?XgXogK z*M7tXN71rneUQ#%fvQ_#aYB2)?#wd8FP<60BPIH1(P##QeQqNW4%rlyC8Vkq>8ze$lsM5h&B=&{*& zUO7+$pVhFRa2Vj^s4c$gz#cT8ZSE!hJc~8_ns<0p;;oVPRnoBWW=rgL&J*Nf;Ke6` zG1ruA>7F-IJa)Ma6-NcjLnclnFL-6+iez%O(Awj%09Ch3Tpfy&nIcNtf<=hVxYI4p zA705g+a+xAjDrPJlTh7YWL0$4pHL8hxxY|(>Y)QLLA1oVGI}?h4eBg`XtHK}tt@70 z;eBK;b!11Q3if!x$wFXiZv8o|6<^F>7<|X%{wdf3RbURNPehcJ)Dd{2q{ShY!l5#A zj?WHX(!p5-+9)fPyN29K z0=ys{G4BxUpV08xb~YCZPgKU;IGRohSUdVLod>Jcy)#>&Qiz1aL`GpayTX|O6ra5L z)^}>>Z$@PHjV^2N`wr)im`DwQt~u$!$(oaw;G#Lm+yjr?f)sVv$L6SmHww z^bPOSX*tWs8AM{FkiSMVCQ~+u$23AaOc8AUFZ@Z9!hJ&IVp^%;*;0^%r7W8A1g$AV z(9SXHrb4JOG9H>?JLb82*n(h-C-ea=LweCigP+dU;pT}%5Z(i8FPyVxcaY~e%>1GP zD{5vMt46r0XJNR7y@Cnq(Y;?Vl?>dRIyi;66M|`r*6J8Gr5j;`%d;v@k9P^&L-Ck1VyLrjcL*S(^CrityAE`13NN62SmMRQV>V@^jX{PF41}Vt zc_1XO*@RmNtxc%jPB!$hCqAsq9LrLaJOqbZ-JuV|7=z>lu?;!{+J>k-QRu61MhXIN zxMXy)JplmB;zS-6fC0}|Fa;AyvTb%LhW}j-n0HLVL*e2l*t0uYLnw>6BVq~DKEcb- zSBk;HTk;+siYNWCRmn>g(MEzi)8k z@h-DSk1wQMwsFeSO@J#A7t?Y5z}NRy2?yDpdAQzT7ilfRczeD z;IJM%p>crq?XPdV6>rrGpC?>=6P5~_>j5q`=Tk7p_bjMV2qk0fOYU#r3CBd2)IbE@ zp_EGs3t3QK4?#DA**nO1^XmX~pqmBiNlJG@mRoG+n;F!DZcSz$M>7_)eLlNm!+Lui z%B4IGKp*cZCjGfHdmJDX7NY z{MNe!TA+S|PV-r{K3T94<9crh=?{m;}mlm^~BTy3|c^mLkL&*;v{}$@tP6@WIvYu$x z#=#o3e|_3?T>~ayg8E}dMplu9a&Bo)h9=q=GcA};w01yLpjpFYz?}|+bpk&TgpmmO zfJUgC2$WGKE1?oN$3w3Bh_J#&6~2+Hv||6fc3rFl(GB~I}Vz}M1PV; z(9^)V_mzZ-R)LgtI7(;~SuMnQf~6*D!MX$!;vgd#QWy`=PqrQ~Uklw;O39fQDF7l? z#N(1Jzo*UPf~0{&l2&^`>SU2@Fom$MV{|7?WkCTD&V!BAH`}-YhjPCZ0~>GGXif^^ z&}NB!Uaj$zl2boBnqnmn(v;>wg9B4|cD?l=s65th^96=B;h z1(QqbTZjSiN*9%Lt_WJQzQN~}a*GF5gi0NM(p)%|k8*8CdMuEj&M{xYkHBH~C{<=O ziHlGO5keJQg+LXtt`r` z1zP``?GosNX4oR&6QwQdE4T-dj!Bqe!{?*$=p0#S?5wQAwm|Vcu6&$4Tp4s;wb_Ux zBz*op_tw|At)g(Ld_*~+lsy_9EK;F!@Ue7WT3E1bB{)5N07~ z8BzQql}2PI5Jk&RXtN}DQemqhT}jwOkC4gb9xP5o_0A&^xTm^wQ+m25ENSVC?fbB9H=GE`6`HPis>b7nbxUV)h7pZ)FJ;@=}s30nN}+I!1F{yBFF!wgjy5Hn_!@3f%MAlp`ka9sz)~b5Nhq zvy`n;VV2~Hp0o`t@rWH61Hp}{cMFTFRj8B~5yyym0)79NFqDZrm$=zp<*5bYGdfOE z=cKrkXk&;Sa2ydc&?)DPE)?{WgySnbZsGB%vle4M(TsXcJcB#@le;I#H+XU<#I?F) zAtc1Vbk!N$`6O2*<{36s~1h!+GI(Q6saY;nc!IqXsu-^616T5GRNxIIs7`w z!0JE9R5DexY?l5tuq7`m*^u+xiK;u$a#6OPN)*;6W_N3gv4G^YZT(|Ivq15~ZM<_;65kXM9SN8xMJzj~a#2FgMnzb*N zG&GFCcX=u2xvtdMNb5q|lnRw3$xC^XkA!Km-j#=uO#(v!C3TJP#E3BCor)C^$J2ch zsH3EO(7p;}4n{?)jdXPt2liV_Y{+V}kY2xuAH@X=Jg&c3d@m%v2i!^I%Vz1M#BmYkLI~UsD&?S+g`vT zQ**91O$uF3UsQ{#$H+W0;X#1sAVQs|4NyB!w;d!kNmgTR&DPScZ-^xXH+aC4bnbFH z3u%5_fscQd`IfeVgyEe03p4VOGpHRCKl{)w2!i*V!5OM;V>7<)>i& z$wyvYip7psZHIgb6~$%edjMaA1r%6_185oJY@ExTGtUjm0jMs6x%|s7;>K*rhhStW zV^=@=J~7c^#aC@HVkuslv0p96H0w2439$= zyf-L+=-Pp9Z)BiL!Nz2q+&iGIo^BIVp z@1Q4;Q*XR)w7R`Ue-NE4iVF*K0fHX>oV?X0&;8B#NWZrO<t*>ghwZxe~VydATg!n3jJ|p#9(IycRJUA zArxL{3p0ZmHN^{&`~~LzC!vOTnvaOQE2kq5V9Lf4nV=WoyTXHvN0(geH_q!{oVof0 zwnDJT>=i~3P+fQqO#L+-l9Jg;s{eZJp>(6Wb=dij#!o~C&eWk@6YaMRcJ*9EzCXy| z!c>v)9nmlKm{NY=sQBUsu?suG$$ANd;q4&0bTI&*S- z(}^P)8woRuPQ;e#Wd*PeKk0$b3n6K=#$YylTFOd{@zn|JLF?^`jBTPS-o@fE9UiaZ zDVG+>FUn04Jzr%nK716mVRs?AQ}NY0Rqf_kTHoU=saDDH(PH4%us1R~Tw<0Y&#!2{ z)_xRDPMqv8##a%SNq|ukw;!24^pN&IB)yD7D4ozEPpqRPj)A}7(-lN;Gn=AQtjK;N z3@D$LhSU)bGPmL|CRj#pE_}z!lXVqrHn1_cCl0!lh0+FNrS2fpeBKoit=P+luXgTG zOIid#40LOwio&Z8@Bju@EihGW2JdHxH#dA8g%@C;wMr2X4M>vJ;hjQSgnVqwqrzN( z7NYchZ_@C|SU;pgKqM>X0VZ#|E6}@hN^d^M2z6D`gN}z}Z{Yts-JxbQATzB4tsEn) z%5!NxJV2#AWdAa8afd+$H(~U^R2&Ohdj0{uh*a0fr^wQG<_y(y>TzB!jbO3bZhWzu zFeaJ3XLo}iu#qoo6pt)JwcvyWx&|o%`}DuLG4>({m^bs_%A4aVMZs}RzfkR&i9n0A zMDYR~z0;kmypLEp)YkoRf~22bQ74cj`48`?8`1B-0VRH zRcX;2^pOI8pyCiJ77@p>V2*klC4d@qeTR7$JWccnHwM<~J-mDZam+xdWS|Mn+D-K2 z!f+rv$Vd;eJl?F|#~_48HqLqul!%8G&}Lvde0n%NV;_5_`qX(96^ zR_J809ksr5=-?350N5vakwApxpMckeWX@<~yqG-Edm%H2l5>`D>1Hucexr0XCUz?i zQ-+0#%h0(|$~k2kW=EKA2}N1n8QsD^kwxL37$R>Dl1{i(l4O%o)x?1EVVM_)dUzmA zFfc{g!r=>PnxB)c%Pjqb7t9mt;Gbc#QLmox-7fSP3|+q41x0*=4Tz)pD`=41>}a>f z3lKg7&X7YR>T7Ft7DGyZNSDPhu`_l>xZ!J(*_tV%5!pM)@a!E$R+CWY=&b$8<_S;J zlg$n2l287e!`oXrH-^zXAzMGQ41~+7!@4kxygGrdO|}bM%zq8mU=Fp8fN5LzYtQ*% z;?Lplzs$YO=4duO+J>v6tM!k0r&#O+pYVMPpgWM7wz!i}UUrd`MaLLa9%15fcS3Oi zyYPi{1r~?HtS8e}RH~uB5*T0V!4Y24h{Mr_@w(-J770FjMFe=1&*E2jZ%Oy6$n}&i zpy+glJA8x(O>|o%F3TRZwqxk*BL`OTjl73}&u42u4wLqXJ^uLgUmc-iY1!EW+1coRiVok!kuxg68|g-kzL&K7qRuE*%~fy*vpS3q zoz`1KYoS?_>oiMRR&EYAs{m+>r;-QMfyls;c1HQ=93?_MIx;S{x{EEVS-%g2dq>b<${Coc-~cEh zKaX<%Z+!n7&cc_l`fvX0k9q%FJT8=vWAA@YPD-W!y#M`o{CWP=IedCZ_g}ASr_T70 z{K^kmS$kDJrBaO6f`L$DTI*mo&^&|jl>(j#tV5yQp+tO9aLdo(KPMzbfsGst^xu~X zdH4!{I)sU!&hZ3O?d8H9PVVt-yg$TrJT=u8?=#&c`t=RiTQJWk=_LxO)4;;kc@48(YeSsgSEhnLVe z|I6vgj6yB%9!5S8{uK`zZ$)qMX0utndj9gPD0 zsE8~(#IsH@2uO#d!$57pK4Zi)@-n=O1v&c z7PnPKGA(b=wPND*-u{$Etj*nI1=Wj%Wc8suv#dwq-RZ#DUt7@dJQri5*U{{r?lpI$ zWa&s>oR>bJA8XN%QnDWs%kFMW=NJqfF_;9!sI%zzkj6OcyScx_YU9*~MKa|UCrnNt zm=e>LE9v7u=bUh zt&5nAAkOelxlmvarfRL$7!2CodLt?<%0j$b8#-o95UhE20vmj2MzJ78X`v!Z`**5# zR9X~>hqgjDs#|0mNhQx@iIlh9#FTHLcdn8ZJ{2Qoi&nW zuHc1EeN^iVZ9H^dZT3fJ)!Mu6`MHIQ8uhbLyE+&)`lCiil~df4IN|yHd&CAe5Po(Y zCw#kJvy-Y_uQ&TPI<@P~Zk$M#c@jiz)CN|nK|$-Cjbx2Ry&GdT`MtS^&D4FCmBl{1 zYrNMMpDWNPR_;|V28~9i&TUjgsa~iivTPRiYwcb%%SFAgA1ieA!g{aK@9X?g(FzBH zSYcU5+peA&f~2Sy4r7I`(V;>2eP8EESEIt)=9z3&Qo2&m1ym;{zFdX4-8_?}YLY4% zc`y1MCq-rs@0#^i!z?UDg)uq^*&xLe2#%!46ouV)Hc=qx&`oqG;_<$vN66?>U{?FB zpAzCg7ACr@q-o#QUCH2mTX%PIIXfR)mvys|L;{O+;oZ#^fNHY9cawM|J6#9zayg_k zD(!+(n8H* zK^67Wi-z3n*+qXqyror2lo(XPqziW=80W7CGTDry;79n5{Iw(Xl<50O89pCNYN<>2 z^X7RMpQifZ%}p?$7{(Ix@Vwb-jfQVw3x4b1eXpd9`@GCm>3+qa1y&iV0avt=dcPAD zXyX+nXera5az(XMN^eKh5d<*$Ry6CF)Dt5YJGWJy4)1D%reM}e!IX$^&tyqCR?=>r ziIQThq&n=yMYEI+=W463nI%L<#p$JUO)m6}y%oyjtRi!Nn)&*q#^7pZC3 zMbbnaF%89X`qahD=TEo}cg;fouG;9uCPB)uYQGno1XVJ;w2>ML{#rdVH$bIAB0(d1 zzF>N)A)Z&O8oiuiMK7w?`%$5L5+5W9n-Qmg9*N@&`(V#O;jxYtnm@A+6$;uGcEj&I zn;y_V?}5fN3&&fu4_-T2Lj4=zq5t-B35hJHNsNHf%3-cH)$4i{NhfG`4u2#s%7X|# zgYgq)h}7T28O^Uk6QY1qv+Jctx9>bDN`1n@_x^GctebVIk{E@Y`cV~@@>*HDTEouB zI;IIHSp@C&g)rRd^Z*Yi zvx~_r$gyvlE-%~7rZKgJyAw2Sv|?jw3wOuRzFxeNG{QeGOuW}oiBSjW+EU3MfT(6s zR0+iwrlIE?bJ~#-pX$Xpm&5l{Hq@99ANKuuDKGfO-~fxY7_N}Lo^x@-fko^|`W-C&qc7o9q2%om-w z`NTcybZgaqzuD+p?We|hqhC+jPtu;R5kTC4a>OLmx~*1<1qD@QsbN8JRVb>Nmd<&> zW_+)S8j0C|FNbR}7r{IjI%yW+S*q4Wjay0OsIb-T_Rikvg+;whAitBaUzMC`HY5Kdc|{?) zbTOMHZX}?>sMdwm1z08D%hd=)m~!wUI>B)Bs-*Y!r%|(BP+g3dxU!@al~g;``g?86 zdP%ES?W>Ywy`*tDjOv^yQ*JiXjxW{03v8$IzDHY}qa8y(Z@zKW{N-aMMOmVad@vkf z98X!Ijl5kQywkWr8#&lU@3oTh{!vuhb?3#)+RK8g2I4IysRQAO8VxQ$h3Wq!+o|kt zr&${~Zslc3wK5wg$%XG3M=%eyVZRkII|Z&9{jFCAibQmxk8a^?*zIW~(lIEamPDQ( z!MO5NTrn^{OV>OZj|@)Ur6|(`c80CddHvE93VE{}jo#|Tu&ZQ|#`^@&9CTaNVIz{< zF$BF2{rZQFww=Vv3Auje7Aqh0K$CE^tu0pG(nUczS#F$rS7MStY+43A4H~j!P9-5S z)rgm$A#w611lmj{L@_2pC@-A}QjQ_1RXdVcQeMW9oL4Vev4g!B5=1Z}#Y~B7OEWr- zN?Gz+DqivrRseB2>RvQQt*)bKh37T2!eOrQ zVvVLQm^uLFknyD{lyOveJ=1b@RCsnksM!56%KVasqThIL+)%L|U_~LhvGbw&Q*=*t zhW*9&OZ=jH7R4WeU83(~=>>L2dx2fW5I&n(NJ?~0#(fqWM-sd>TYhwU-g>k2*E!2Y z7qb5n?6Oqm$L@|goVQu-jqWsWme&UD>Zn$?Ee$G-CF?^2E7*th|4&)i=cxl~-%;oD}iI^y_K$D~6WH@+SrMV$d9%lnT`m7NG!OQsgtD zpkWqZ{!cC#m<7eCM%6WoN~%s{XqH3{2~>V;_TpuME206feZPF^a$E52%&1T(8H0re zQwCdzI419iZ?3LWs2-mcqJ3V1o@t=2Yshz4x3CBFs%NdnsD9CFH7-XNop&8!9qUKm zI^^4G>l_7Aqc&_vmUpd?=NGM39I_ZS|Mmmx9?&SOx-pl7LK5UH!3JuDbqf|)uxBF`Ao`O{Aj?JL1UrmTg{rgzs|qh?(hhJPSnQff84(RulE1m%)RAT zcz^HD|5Q9F#r?mJ|C9ge-{t@P&Hfy~|B!IQ)qHIY#NlU<&t)31h`jD0+_VwqB@**Kax(&KnWaskgmC zhrSkN8KNhQEBq4PZ~(riGWik4vv&sap5q)JoRL*@Dqjc57db`Qia?3^eHcxapB1e| ze99cXki)=@s`6196=KPS5dBRcVxvb9$SKOsbUV*85)3P4OObP$Op${@h%p9*mMVvF z#PZds8)RIBf)0Ru`pG>iWC|}1 z3l)+YnaylLQX(T#6Vn4XexWU2s)}Vp%{7;lYhDCaGh;X$UHEE~$QVyt5GO{d#$7+u8A{YI_ZuVY7_KQ&7*Q||oJoT4-? zD1OrV8bg8SDEscjzuI0uhVmSS9A9px*+*FGmGel!|LIRy1C!h8$~>WNoL#&bz3KE@ z&>uG3E#<>YQChT>_GM|=R*EzblbGy0~kNL_F~kM|=A9AhUH(KwOg#(d3kWvVuuJ$2XwRKjjZC@5-Me z2pGE4xpy53aP(s7PD3Ar>9|u|r*S+SN|=O{0$Y ztQpP4)e3#q9FF?cVZ#J2rl1}G0Xy)o4scKeUA9A4Ys2Py(6UCYMm++q#0F;^+{C4) zJhTD%nNmVUaUm{F%TkbvQamWjxYi;(8AAd8?IcE{IjH`z0s>nYHS6-SWn3wse57{a zYhSH2NRwd6ws{WE$Kt7!newQ4< zfws_Zwj%m>&xdzl3xu0Lc3rT}aB+U#=<|UlsV;GG(y9)I0xA=EotZE-x|O}?fE26? zAREAWzdfq8dl|m!u|f13)jA5>Qc8KFTO%=FNvVE0YV`YE>?mHy*+6>L!2ni5EyH(u zHjsJ`l(Y=r2_EpDZ#A{}8g}xB&nbElp;Cy>(=ZPAJXtI^%wpkN4sRIQtaSdq{x>i% zs?B}A=R9jvhne`s#pQCx&18zUVkAkg-u=+YWJ;;!7rji}?ukmtnIw}rPAdn_>hpDM z<|GZ~yxD0E-j1rB%nKWcC>AYM4pPPmn|rGY1I@nu@8@nIcko9|un4vH$ImePhvR*D z!iE$@gJHLa2Jbf<>03OuQ9DCd#z$LDXv4(w&_8IjZ@VCGYwfyFzg1y|OKYPy-Hb&L zXH+>6BNMp+Cn1=U${RQdP#cgxUWSB6BLzsxV&S@(1n4U}*k0ag45T5Q($6|NnMu9A zVufkdN5ghs!jw~C-YS@)y|azKp#m>~Tq_U&jJoIN%F?K$0(BHj***h`zy;wh9ovcC z!cNt1GZVzm8Jb-nO`ppuG350smO&?Knb=Mo0rabFMqt~BiT{Wah`t){dvnx$Z!m`i zV5kkwttC5OuMVq>zIGs9fykSMC}yl$d-!I!8@c1g4xSpe#UlD53P&9lnMjT_h!6cL z>m?}wwZS>>mMO(mEOt}TchCBlw%L*ZY+V_19fKJ5@pO)~mNbaVUbRLiOKy@zgJzd$ z#&Y02=n4RegObFBsM~1KZze*pO4-*I`uB>EOm3=i2&3&qPcTK0P>bdi2g+R z;Nq+X<|bj0RVDiY~*x;)D#+y-rh8DMPsmSG`6H5^vAjh{cD&iyE zV$7m=TLmSZ5f&?!B1Tw@A$-&A*2yKc-=^`GV%;2~i(9?Xzvysr$x^+qRgdWmP$XUt zaq)`;`Jk{-7dIcJSijK`j0|fQkfqq%*3i>emP7GbjI%CU!)C49iaHk?`=Hr*(`pzH zvFUAGVrU#4qMSIU7&f3IE$(4CCf>zMuu-ea+*OVr>n`{ybNyWUJEX4zo?zwf7 zlchD$UAtyR;lOIa#JA{T*tO0@`wZyZJ;z^zgpo=hbo3xJ-(>MnbkTY}hX8a&gUo#1 z>VB}_(;-;jz*DkN@uJ=SDPzCNQyr;EfMa_{E(V~gH)>LoKS}9_Lh~X?SM={U4xzD} zoE>IkEPRfKMkavvz;yJc0qi|&CCo)CtI5-X_)aNG`Mq2wu}z9%x=I){TA9Q)DFDzx zy3skyB=$&AF3wcq8>A=|4}>y_-BH61dIC%Iq6I*x&53^j__dDFGTrRGkL#(Cu7&4p}4IUXvJ2n*ciF^ypjayoHsrs8BGn2Y}y2)$>L#j;N#lpCYQgP zs5>BgIgM%q(8as&(r5;y-B$Nq8p*&)>@_-#{+pjth&C{&avnc9;;#3qd_Ls9j8!Lo zXtrtdN-TW^5#1q+=RQdg{X*Dkb083Md57APFWt%_7;J~`-n_XpzzNl#HLzNXHWh8p}(Cj1k)UuA3 zcdncq`VnTGXU{z6$Jg}R!)GZpkUnQop=h=uDuzm96nm8*(n-yhm@m@VR_Hi=RG$Ty zbf=O<-v{&&q?j#v2Y;&cFZd~U@asVaTD#2}pYu^A=b3Rrm^(elto_Y)y>y_;RW@q{ z#1oMmd@!-Xlz42O}*b2z#Ojp6~^ z#h<=r9Jjx39K|mf#{;-2Pb~N~wf70sH?D9Q``Ak_vMN67o0xCKa_@GuZi@6Kev%b1BH9t`Bx#w{-kB$LzQC zkBjzUog5LcliN!M6NebJfnmom&B_Byd+3mB7Jf=C~p!1Z>1-<33 ze0+_M>Aoj9b+Y5mpfdbaD0(FxeC^G0VV~s#h3cEl7YcV#gXwgs|`p&E5PneBZY%K{YTgZGDth4yH z#{%lgT#=sh__6cd6>!;aPWC(cbtRUg$hZMt^#}!wNQ%w`7&zk*k~-OSc+!xI0>mMiLFgEl15nqbQyjEk+efQO$Ch zTP*HqF&a)Z8uvJACK^idh0$VEu@u!TA9IVP9W6$~aiih5(WDfku@oytlTpobRI_}- zO_p~w8I8w{#^Xj)Q;Y^vd}%Zp)htIf%P+Xe4!2``>@h`(MlnR0~%3oHQ^yrdN0sr|qpb_vnbW z4A#yMi^=#eDqZtYu)h8=rCMXTnPI*AD@;sBuLB)w;;Y``A_9Jn`+HyFA_o02?(cht zi&(U$xVA^P_R)Ufn_GNti)g;$#VyqSs`s`?PDJwH*S0+HuYYWdn$1@{wRN6;#Y0_U97Q2n$PPsUX48WbreEE^64eu{qUa26wlz%e`9pZK9m7BVvi80C z#{VdCZ`l`9$vMQoUcdmoF>Wx*GK4m+% z;V94J`Ny^Y&t0AEbO)Vox5t(7vO}cFqUxQ%87zZfC2oJ!L~K{U&OQdaI-(TPxcke= z;g6I_l%I`(z}c7tn1xyO+by)O z@W1re_3YZC!-C?`@f-U5=Bi-?%(1>oi#n3wPIBYgb|@2o9fR7G+8v`|H7n5>Z^`pGY1>h7+;CGmRM9)OhqHmp#@WMKc;H^eFOHg zK_kLc)-V~t33XB>F|~Ttc;v)m4HK>76~`@ffK%tF?J%M^CRmx(4n-_bm`?Qcb$2Uo zN!mq3>6Fo7R956gtxqpCOjsmxM9QJ1VZv*&7$(n-NpL|brUtvFWCRv|_=YJRG1Ib$ zY#OUluXQi*{c6;>t20i07DD%zxGRle2j4}I^V-0`1(Kjxh^$jGbv6{=c1 zQ0t<{M`L=QUYd(uCH>sfCmdbT9G&<3MmOv=WhiZl=}OP>*lWu5fO@Z6SYR?(J3qyu z^#P`_&QJb^v_-lDnR;Via*K?SakZ4sp@wRW)td%`+?CxMAJKHe$$ zKAteN2WOPx6?bYUi^aKA47K54Xz5F(AhMy(=gh9Oh<5e`_sS?9sM@ZCLURs#uQQtK`wBrP~5aylk09c6^6A~E6W-dJd4Ur8~YCbp0U{SlbT)n1Z$QAgs9 z?GVNO*jvdx-`;J&^w(P5u`C1BkpR?H`EY%& zt5j(WsbeY>E)@@0F^L6onKu7AGM$?=wI|lCH13XdOO4TrNp&lQ!3|^@xilS0LzW;{ zdbd;%c{?WAt(CXsgkx!#G+JreSiDaUAUgI5{!tzL)K0-9`{af93(>JOT-qwu1a7=n zu?3vOL_``%9Y7Pi5|bj&zE#7RXmF&-zcAD_5fcZYtG;R2gm1^c+W?kppA(|& zA007fHp#2MC71ed&fgC*nT#ppau%G`I7(WPsTf7~l56<4E|N0ZGPI(jbI%AJ2 zmxt$A8~=`mIk*)o;)>hI{hPIdok3Ut{=o^cE-$|9p>+I}S0f zKFSCE+}xY`?ah`v_QyriZ4pA{N1o57KHjTPB>VedIdT5yg^{L)9_O87HpDmHiXO2b zj*=LIQR=)V%M9jeP1b&}^bNgymo(oZ7s1t29pXP0;nc(Ehf_H(lDT}{buLQ=nPL8Gg+8vyw>x3Emjd{94>0!%fV0`{GMrs6L8-^Pc+Ak`P3DQ( zIhiWQ8;_)~#IFY##Pz26raHo105+Ua!IjxqoafISWE>k%QKbVGKm!|8N#+G+D7RpR zDvOj!5w0^E*fE10N62au8{7%QooH~r4ekZQy%2DPq7|y5(iiW7ddW~PcS5-$#iJ*o zTq~5I|CJ<18&+&$4txdX{MiO3=3u%9>_lYgi5eZ-z+R{n zKXM3D8yHW4t9Y$a>)F6~4%|`%W`Qd59JuA(P&^0j@op%d0{3J$6wiSBVt4n8JOgfJ z4;asY`%;0`jZ{dZezsF>H}U|VN4cCsmG@WYXo-oDUprLh9O*LPHR^xxt$)X1Feza) zejL1WJ}dX;F<1v18R-|q2LI;py@>mi9O*T@B9O0+m3+?W9Rz#r)7eIJB0-K`7%Bf| zMxGC!(TQh-zecm=H2C=!=oPdKBYY%wv`6`jO@8Ga+<;#AZl!{5v`UI;HA8#VqP^-s ztLqk0>XvMEN#g14UOguiSAfGJ2veFrGjWU=$JmPFIHemXD<6#HBn^qLAps$0r^Jh7 zZBV-p8xN^l`HH$PA5hn|*F_n$ukJ~OCeO9bo$G!rg5p>7zLZiIX04<%exZ~&j|AjMoj-7 z_5XAJ;Rxu}kcpu9MF>t_d}R+#DhUMcmm&BaG^k^^i#&ySDiprFdUcq`Lz=N$c+luk zS@g$w5k$s4*;yxQh4P;MG{|$@j1uMm&mcs#gONV+e6>=sgH zzmSK&aM6fIt+ZlLF*B2zS8Ih#2G&d7h>8yC;e`D4#o;3zav^XTfyI^5zMUiBDDbttsEDZe#e?d&O>GjJ}`Gi|LX6j*`g81U9FRu7fR9}kX zOG$kxi7#dKr7XT2t1rjm3l5$p76-$hutVruCg^ig;NM|1(vvZdLL8*xr-_HCd-H9G zWW+CiK@x4YnO%DuhVzS56=zDHLBDK#R&(bg<<+4ta4CPK8Qm_&jZgk^L!YJv{gaxf zX<2;kthbcj9d4|hX(W@dpwR-B4 z7e{(h8KJghVy+FEbptVcsHzWuR>o|h?;~Joj!2gXFU{p2`b`Y8Cj#!K0O4V%ey=`F zFBdlq+7*Lb)ih5e+lUQ61MmeZ>ZFiL;0-DI@<+k+2)g5fwTx)7ijOYf66n`fdd;jp^! zHmD2gP9{pio{Mi36jJ_4cx5gm)=8A6@9SyL+A2ArL{DyC(xoIKi z6`|qZ|S*e^-3F9xs>YN+=z}3#h8xi?BZ>B;+ zQfvVv3hzDSqt6ku@rI{H2Op-_xD*CAvngVB zOL_6N4$?q;1f56BX}tdM&bXnY3SbTh1x2E8q}Bu<_)ZgVC}5hhg~Jy)yxI&2j1JWn z`211Qj;Dk-d!3c|A~e%#)CZ&lFl*DZc>Gp~zR~0S_MpjrtLpP{F0Z|UYJ>{a=4PS59bEgWXSylmy)B_q;m@$ zhkj3y-k?(=%5dvvvoPXim!^xaCg|!1 zS$;g4TwMpFAX=DiIRVzeY@KtG@)1~x!M5Z@1-lRKG$ZtI8BMmU2&|aE#*phf)G>6{ zcHI=vO%k+_0Il7v<~Wv7=+710%LG_;pmAa)QQZ5use#o}XQt8xZ5rjcIZ~9y3JAB9 z_j7L<6U(sA+DwB5esN>t*6{xxl)P~8)*nnISwt5sw*j2e|vO{hSqXMV!h&5UV% z?GOCrBtrFqQ4MQ7he{nR#zZi4*hQS|aL~>1)-f0;t?MMOIcGzG(2rI@IFk=15S~nV zHqEEgd=mbdqxyp?)GH7-N|nkq!D`ZW4>97JHy>{?+Jw{WIpZ6D{4s=irA?}CR(KJw z007V9m0;9HrIUCi07zCM;V{(ZLpD~OOxX8q<6WyM=$li}qbRBIarM^0CKv~EeCmsw z={_$?=t6Q(ap)pL>+W(00VB?2vAtZp1cpT=g+xhHRB?WDtmQPP3ibf!=^U-q1Pyhn zO#wpaM9`-g{4vAFr$?&;y^Yl=E2d4W(@##4=$M)yGP9-jq2KlQ!%q_$XW^~{oI56e zD#JkyCC8@#I0R!aY{4um6-TfDSDO%nblFRa))G9x9nFLEvYXi{bd2{3H%nr5{|c)iaRuc(75_d}g*rO;H0&Vp7b#p($=Y1o5Rfi{gr_py*ZLsE?zjHy> zc?zT*QIrk`L%SrWkOKH3(k1#bVw)nS2gdbfZ}FU*O+bfDk@+%W69};~|2-9sg*vq4 zM}o36PQc1dQS>-!VuhDWpnX`|h*!gx3!~tp6I!nd)90p+}VLCXiP1f6B|JSJ80%Xn>h%ki; zUl@sykpTGD#3CddUlomzQS6|0G9IBA|JMg3Bs5=+jK` z%tdg*R}7^6iI(9jATIljeZdk;;FF8defjdGT8wqfuQGAYzzFr%vZvag&2~OpVGoA* z56*2p$JbzySYThaM9s5%Ed;5hTnlwrqi~6?E%vDBk>NG>UM#QHJNKyYl0qsU{&hP) zq>zi*;!*jic$||pRVb#n&B3tS|4GpKb?gs+{p23GnSy%=|BuKro7sY-Q>KYw@PM;$ zanNNEF@mb}B2y?ILNClpLwpQ4MJEgH{yX%4nmgH2!3q6wPSvhZ?NY4v`RvYF;JpS# ze&lzBA&vWLd#&oe%oHkz?#o!+0sGHK_af2o3OGLZ1y(30$0ryI1i}hr!g%g`%k3(v z48(zMy0OX_Gz1nVAIC?UYE-SbtD1jD`EW4M4j*A?6-!jH9IJT25f5RhGF^GbMc;AW zzu_N#Dho|r`y1GM7QS;m53Vq6k4)(^pMCW4kZie`(%Uo{Lh(@)ZujPqhJ<9SiXJ88 z%~dc_l1$2a1KDymPNKmJB=J?yrGMa@)I5&qhzgpF||Ba82iFUC3Cn$zN$gMy+vk08h6HRX- zg7FydTtCg$8~JJwPolGwCFQUDB zj!p_Rh8+~ylm5+NG|5+&j9B9NlmnA2kVh>6u`zbtf z)^~^Ae151Pb4LfHZun~FK^+2l=%noUEcPhkJ6e?UXy>9nD(_Bww@lCcpFel>9?Mon z+=bD6hR>v5JEfvISlZLCfHyv_0wS2$Np_S;Bf(r z@<$|1#qp<8yy6B^_}&qBwIQzzVfKpw%jZR`OmdVzXgVOJ4AYl@@&GePOSY#MI zHCRs$gY^u)`x8Xvt;aVZ#+?C$f*FZ*ltZd7*LSn$+yLtfM^=qSbhfa%r@^D=z^Pt{ z_mFtNgY(W5tIp9A2Sc&au#r(f6ZV|V{Yqv{p$|X*f`>YoE6@+)VE7ZI120XS?9CBI zL%<`U5La!cD*lCSctlOEPhcEgWpp!fGA@S})8Y*15Yq@kV|D?dBT2)eqp{$L2uZKc z$8^sks1+1*N zRQ-*pq3+w@*2iAZMm6@3zk5S%jvCrQUCn6i^fHR zqC&U@4vdWbqI*JE2y0|A6_S9WoH(&3_z~Xn!QLs^hKvcP23HkK@A4XvieSVrJ!l2} zV>TuB77--I0qtpWT}bL}I3D=Tc6ogi8!SG7AK@E#u+7TGL;2pjAx91PZ;BbfJ*H!~ zQ10GBE04keqcFHwl4c<7Rgzs!cn<&z5FdsjFR}pw+1NngP-%C{Q=afOH%~$$0ULG{ z?~QvH-Y>2KG>`)?t_YteG~7JJ^h<_g*=w{;=OarvLw+v_gEIidgq?7H=m*hHCY`rYw8QX zUTD-YbUytq)9JdcroPxjk^a7<8(Vrehh%ZcidIm9I zBTx;1-aUXOMxCRm^7OUJZx+zjSI->4*inQ#_T^IuRFBR{JK}cf1w^cb+dtts?v6LW z%Pr6X)B^zd5nnPm@(lobOq-x%?PCP$N5l=g@95hL;Z0+FlcNu79HF^Ek%FnHEQ?y8 zPGHDb{(LMy({Q5iC-QrDsX@PxU-1rZDyH;BMSH@+UtUIVmjeybi@sN{&^V&6SMuu_ zl5=>+U&qGRzW6#({ncT8lxCU-w3HF`1(vPis(D6ws$wfAL zJNp3oq;&0hLslloNR8as(dk_ORHO+rKH&tw0yn~A zePo|1{BX7n?}d0onJFw4a#9wDk&&M_J1ae3VC(=hL_Uq2u5u%XCX>7PpIqx4w^t zjV{7#uw#MEqJh&#u_*O%;%8tRJfcz*A=tkD5ns0PM-o{2*zt3H{i*-Y&=@_>yLacP zrwtCjVZd$4(H=sG4z#OLb6_H?KIq@mNGQaG5$u+qbm;!>ziR&H%^oc*M>xAVnjiaH zPfCCFy($)(=JFo--O8{lF;{pM+C}$-m$~ZL3l2u24MfaM)OozzM+>H)y5Cq{30FGQ zc4Xh-Yk&NSysJp;M7smR3%5QoA_nhyVMs481dc+qykw)UYLq5Y+XHCYDL8B>d@rME zcQoLMqdjWDhZzwl^#kTyuU_PDM%^;M8#d}z5Uy|3#mjvBrq?YNG+r4JkC3Dpst{_C z)1J^j$z&rnt3d3oXOjNP(DW!;RS@TK$ia9hCYbcAS@8X59vAr z|KtdDr}5>YVLXcbL~Ra}q2@8^Xb+t5Q)4+1p7h!qCsBaTlG+p~hejvnl#SrH#=A?g zT~y-V^|h~8o(;tJdGN{{=*Tj1;Tz3VGWE8Jg|{xPhxR^l}q8>B4dutBw$l) zm{!uIwuFfNQA}#9%HabkJQpH6N!{Z}k0Qn9ziNkmbRMy}yZTvc`l%BmMGSlm;QH90 zLR1^TKO+UuFKIh)s*utM#q{0M%jl8sRUzldZak5C&A<1t#Kv}&bmSR??V zNx>C~uu-cjlpDs@D^`Th9r};WCZp-xyN(tiK9ac@GzOz)7f-YDj{4AUwDC!k{-rv= zd>s5PoZhF`>wQv>$zPoz@f#|}ZtPo8?oY*70Y+ac@wdo7kb(+D0phvS&aW6t`mOiL z#|sMT?K|>I>V4eQ*TL#+d(A!m9FYQBX?BM{^%_n#K9JuWee~idjVWhFmacV%nM^sh z2;{-6=C)@BU7C$YBJdJUES~*<1t<<;q(x1>y~d&Rc)K30@8~qTw+<%TvA=$fXtxVr zc!1Af$m1DTHtQ}n+f(7LO*x}PBYgg~#w-Ei*b@(7=(KA6oDU#DdW*Qgu_JPE()r4b zAD(+l;dh2bu`=>o6FtGHikidAHs#rEIlu*gfL2EGR4%bVSD^d+EM_UW zd^s7~C%O;#?1YoM$g?bM9-oGju{Z~3s}kL@IWlW7<_~x>B=kdGYesxhvHGf`?)mwk zF{GFfQ3k69g%2JzJHx({H5Jj{bnO~r;W{aQbiJ4>2{rpSN9o49>POnLo*#MR0Hdsf z#Rw0HvUEq!j(d43l%vlXXW@&*G>d6@_+C%9VX zVIXBvqIk@8nEj8;J1eZjJrPTaoZ?VG`MXv3#KVbc4n-6}>y2r* zU^xuu9BenY+1c_oOZ(3=X*(3qf6_1#km5}8I3#phWF>}wedbPA*|Am6g^`&^VIR|;7pauo6rBQHtuxmKZcdrHEc;snduN8SS`1;?;KKed)b;*V@ z?N8rNoJT}~M_M1XoF?CwkIHvPe_uWl{2=!OCYwO{XXg?2F#zeB;&N)C| zzp4-M#S1zb`$ni$JR?7IBsy=seA_2NGms|+Dy!anE{+2XH59oRmDenp`=`##`N6nW zfiKUVG51MDdG^bZva_I4;)eaP0)*Lef8oUx)eY6rRjfdS4Nn$$;!reNi*0ZHk;4bz zVFKP1-J^%y?b=(dOvMXoP+jkI||jqU(VATop*AjMZUCWU}a? zALZ*c-YhQZ9+T8oy{qoNuDTlU0cvZ1;~32@e;K>xnczU@YvEWZXwCz;0dRjAZ_vs# z>RjPBjo*N{pTE7dM{33_b zLaUIxpp7+1H|t;!QED@GyXL)=S!}kPOWj6Z$)Ik-!L;KAw0&!uy~eD!*@A-CZY2dv zDlaGWHzF|&-Q>&q&eKw138pIb2@qXn=~!9uwsC8llWO_w&Z(a_4v#B^;iY^(Jy{1U zVDwM~=TUTu>{91Pz9~lKn?vSjIk>y*xVmhPhNX_Je^bZW`Iv&|%5K&0OoEoV=dAqo zl=9NB>lsJc5PsM`p3kc!B(NYk^~b{7jBiM=(q)>unxN!6KrmX6c16g2S>#O~Muzf! z_6zM4=jYfn-T>f3Vhx^hp=&90Wns}$Sd@h&OJPYCmMw*4S$J$IJf??;WEVJO7?7cD z3evndDu0Fg;rBO}DC+R9M+d?teS`^KWNI2H$x|`>5A6d9WSR*;%H<~KXlp^^ z38GAap#n)(SDJr%b;UdR)H1L#zWqkn?Eek6zz^O`@D+3B!8i3Cwr+#7^*^=ukkM(w z`ky0@4yy3=GM`wXsw&Sh(N}1cM>v23 zk?O>SDnmr%7ePWi4j%n_?EH^@!gMMOe%TqYoJFfS2W`uAM87$4Ji_c$AbBU*N0vp@ zM{=D!RV1SkpLhE;9xzL1aW$6|>51xSs(wq3>vdbLe;3P<_ODJEVc)PHZ>{~!8V^ZH z3&W@@bbc7^!KKn$9INA-iXz7uWzY85-w>}$m23&b+LQ* z$IS>kC=3KT{mL5XA3Nf1F_%*(QOcSalHVoy;~V7T?K%5hG!3e%E8Z=C!4iGQVo}fs znP^P0fnY?-=~Rw0QHc2{%alm^S9y5lJd$p(1~g?5husFGHwC>3&^s1Jj{b#XiU53t zPtQnULsEQpe9ZeUPYh)Te=t0wx!Fu~arFWHMAZv1()B*f=;#E{k_H%zflFA|%kWE$ zEOpo+$E8<}y`e`W6SO{MV%+*#Rz$eW!e5M6_gM`lPgilz)RCnl^f1A)`nf_bA6p7E zL!c4k|HpXs%Z?nZj4qC5=-abrr}48WUSNqOMf_#lC4l75M!q$_88-_sqys^+6n>33 z{TpxX!+y0hXgATs9V*A^f7|{pPiT@7SHG&8aFj+r+=vT#!Jn4RenX%`;nr`52?<7~ zFjFvkiz^WF8D2SB*g&*X0S1r@p}7f!(2_1G8#bebS#q0IjxKifU#tUAZS&3~Pnqqi ztwskYJSu`pvLGK*e$6V}HQ>pc4Gbllh3@8@O#)dF;~FT0*=@ynngj0S+IhBM6Rp1` z;1wv+Y|uH+UaRl8CY;A~$aIdzL+17dp95q=5E2OU-`GE4V}#G1p$`A#?}aCVbc5&! zTJqn-@NLjf%&hA}b271Fwpa|MfM+X918K7tfkedhC+Vt|EyWKqElM8*pJbbZyW4R}l zeGq#nScB;_Qrpy%!+(PWlz}l5R*t#}VG9Llw%UpV5pPY;+1ag&AEP2}UuueUMl2sW zIuv&_bV^Wvy$V9~GQ;BnK1h~Ny)XnW9v~=>R60Nh6=V`DpKRpg8L9}v)W2JCwtIOf zhzHS8O-kOOdg@K_#FT2PXP)>r&Y42zF<^k!9}HVS$#jG5dQ47tTAhVQ*8q%)G#2>; zcL{yxlgDW46iyG{euWwyQp9J`nWgc{l(7R}<23<#5nNLsVD;lV@bOqQSYA`N)pF!O zkCJgsTZ>#30v2)@-I@Y9A?qW>`*+SD3fgEc1Q2X}?cciNrEQITj{8pFVTJaRR56|Q zjgq7si4eCLylP!6?nmNx9*R~X#e!iWVS$vDdYSx&6_275@;P<%c?KTwq-7{Rp ztswZwmLf0%pVH@3xSN`*+VwLc&7A8ypp#VTf{>^Ba$Aj|keLyvlSZ|U zDH<`tzjidt(6E&BMid4L0pWZfI`3iI3f8X+cX*=4Iez$ueze9Ic{z*YHTv?2DvKXX zBDt-;5y!)Lawi>tuI|ZLl$TDC?@!|Fv!q>blBR@8kRW%CPC~`K{M>g>q#>-k-ykLO z4&DLB{fTnXE+=2#<4vMkmFOL)&UE$O+xjULHLbg`^G`apm5eg`CAv9P=jSAZLwenQ z24be|$=t9RZ}N|AxEV$KhdhG>e*RoL@)Rx^0Krp#YAt5(pV5M|AEI4shg)B%kaWZ$TPnRZvYnZ~0TW@tW^gSQQQ4GD{vcgCsaP_-a_CYTeaxqex{Wo@+Jiow zM$o{tD&I*zD{L+Py`T74SAmnoVu7gGpbu^#m4#y5q zDE;Vv>0LiOltr#m;YJA&@N{buV<>8rX^cu03_ z#L3u+Q{35`Z$TTAG&?)veobcz@dH*XQ;JhiI*V38FrX+Wc`z0lgYU(!U?&1u@n>}f zZoS}tOiyvR^P2dVF(%OQl=lF2iLQG06}*|UbUv%qJEWL#6!43!K?*j3887zSD#s^O^q% z(G6x83TCzuMmOPG(!I}In%|&Nq8HkX)PW=AL!&zl%cvwa&tWE|(EcSgz;3(5;3 z6P0t$pPjRItv?#{8jU)9K(|wCEsMvotb2aZYSnwKOI0`W1y6CQ#%g(KrjSUMg1svx zE%ov>Zb+c>aDzgw>tdaw?8ZJ?80OE;qjTx2`xbq5H7`Q;-qO8EzyJ>M-41AR0c?ef zS(udjLMg)~pqvAMUB3SlD|^;eqAr(kCx0AQDl%prSV(2kVIl@e@Ic6#`9S6pM{WcLN~qB2>|X zs3+9A(HUe%!Ebbim?k8$(c5PZ#xC93vtYsC0ii1eAdolvNOt}e#nxdeOklI704Ab! z^wMUcd*OqZ-7p*tp1ZX@Xj9gF8{Yt+vj;4ny!dlR#tv&3RaYsEMMKoY?774(Tu6z;zJ)9t_3W=3GJl$r2b8n zb7pNB(22M@-X$rop-YkEjJ;(L=M=_UnmE%dec|zACv}hb)%NHi9*YCz9yhh-mfsMkmJ6DB|#TN#&J1t0s6wKH+zrCI?v@D zbpy^3b8sdnfpT>Cj<_KXBa~BEf?{tG)QQ33_;Zn(EbhV%4^{tvbPC#SdqLc`I|(CH zYL=ml0FWW-BgsW<+#7nD8hJv4T8i7T3US53o$w9?RK=M$Kgi&eU_eXC_!30}CNz?- z#GyUx)b(DaR4Es#?CulZ__$}|7Or9fA7qB)G|J%)32cn%_8e>$x5eVovD3c!*AWA5 zW(#rIG4aO%GW{gF$boKQJZYstL;DiG*1Hgd5D&iTz|#XA121E+ z7IGFxMPS18YR!WTkyyCGMbYHs(@YZ7vt^e4=17&Z<(2l+gak&*(lf)nqtzg9;lyXN zChHaH&4_RrGd$+IR!_BY{zjCjpf>-gMP#AxDozszWT9^$zq%YeHVO?wL20!2(Gsw^ zbeyb~rH8&#`rQIA<_Mb&$Q3};jo+Mjg#$WIXNDB>&F3{L*dA$z>ay{EIlfs3%h|tp z>B1~hNJnSt-#WpZf`>A9U6UfW>EG`oDWV@DocvW$$u!a&)SV04ErGUt**KY ze#oO;B%(=Iv1{P=$icL<2i%yf7Bm`=y(c1NA4+9$Vn(^|;J3srZ=3Synh1+X_wh?V z>&rc)6_U|B^h-+n*E#C9!v z_T!J(v&Z7JU%wVd>2r|&jNQJ>NH*Kwv};e3*R^Xn+S91Tb#rJ>A+T$pQl;+_MmY!{G6~FD$`Xd%Yoa=W5SFpah=JSoQ z2tkccaOkP03UNlkt;IVf@{NQISl}0)jd+Hl5aek!=E>_am`2}o!tZv$F(2ar)_zNb zR{PBes+M5=nAIk&R%yo4LQsP${eowoY>S*G@7~MmbrjhwQN7tO`4q#o0H5#`bWt1w zUS-ADw8v=}uP!n&EM7a&b^J*86oZ)xDNeHp3j{VE$PTwOE#!F&pv0)-G_Y10RK<^uYXJaFGtMnt8?5-R3Zi~M-W{lije6C8Gi3S zaSAUF|KTk$w@QJs(R;x>z_=M>Djk<+lVe5#~pvPxX;P?j#(Z=&o@v4m0CH@y~~tSzD9D&S)$ya1go z*=24D|3<&PgG^lnSqB@Q5=V&>9hrg2n}>lj#dpO~jxfN@!OSt_i_tRJU{rM&^#!3>2F`f49&Z=GpG)-n_11h2Au!;*&?;E6_a@8|;-^EY z70#M)bfyrVBFvzqBglU|5(^AuUWn1!Fk=O7!U8b|1)>8qZ{c10=vs}Q?U>yQ&#q7* zhS^XvC9l#b7>7uZDK607H>=M3VdbQT+nduzjypteoLbW{*pA}S;W6TzH~^2jjHH_U z6h&s0dF?zB%!}!M3P(kUvdLg3rI&wSii{0aI)Z_It`F69Cen-}&57g;&+$-@&eurI zB{h$tHvaeseC-8Ui_3Kg*{w}?KeNujBuk2(i?n^R}OoX{apD@O2utlXrWFNt_ zd@2~LJS@Wa6XYED%q>4F=i&(t=y4bYXT&51vF=0#*fvh;a7(<`Q|TBu9xMiM4`7L7 z)XRI!7qSsKuGZ{AKsV7M34@WdiW0$w@lNTrpXF*m8&ESB_I4?|~E1^`j%_1m>$yEXSu;>sF>L z(x8hoUs8DF2sv5-lIw1YVVYxTRMuBUB9rtW{1pc6&pS`bPw3G4SoX;zkA~p?s6-i< zMbOD+ap%BP8PD;41jAR_Q(q;LSnbI}#b4#a6)eI5h#sJr7MCCTG5Xbgui7t=`ymx- z1^rq-`VPj*iB>j@mA%l)YC1kBs~BajQT9?RYYt)+F@%U&?#9a}dU-op-#5z>^`}~S zyPBx4g@UB=czxG3%Zu^yqFG*wmzT`)a=g53mLJE8(#QJj_rxnNc*{n^llQ^x0b}(aY_1^vzTN@|3>}fYa`osZr;d53?2#*urERPG1 zld0K2E#AP3f(cNq#{nYoa|`A5>aZFE(B)5~QA>>Bg-(QF12)W9xh`Pd^qSpxxh`O8 z=WnX<@`_o07B7Ekme=Cto>`utKoq0aC zp{QA;nwu}FdD-p6rkA=RK5K-*&x2vN7ZE1J7qFdXEw2L0VG#OQ1A}3I7?Fq!A(fF; z18a7g4CY2)xGnx;Bg5c^5KZE)*#uj{?;WRa0#@d3j zGo0_p=ulK(u(#lDE_f(|3;yOxyI5RsI0NoravgQVy@kE}tFvz*je&LkEu?XwQzx|qpF7Bvvc5b173q7wkEhGTh+!z!rG(fRtApwf8cDil> zRzTWl2E0z2a@83nFq)=ZU`9dpjB6C*1)XmH>|8D+qont4*ssbpW0c@f<(e=G8rXQVaAzq!0R&3k^^3rah4qLs@%zv zZu;n4ohi+Nh$qeO8t0h>8snJ-4Q{~fw#&?=WC<>v>KPz!S6fc4+o?mhx}C%tQXtH1 z&zQHKF)KY2tocl>kY{Q|*l0s$dkS%=Yy|&p^!u?viK4zNDaT4$opEeP9HL1bJan*ZqQ7H=WA}29qs?kjjn7%iY!=>-F z^uf}3L&{*OY=mzUA{Coo8K6RQjVJUIM?NoqQMwVYGNUphg%9CspYAR)l^nFLa@hC0LE`lOh zIjwHBuB{z@4;^EB_7&L0l7#H97wul7ACoOq(jM}epJ~lw$RKQ z{cJdBw2~x*g5IDQt#7>a!?_FnIm{QvOgE>A;N57E$KYKnd?8xtG5GXzrt>%TbduFa z$Pi3&lVo)eL2UVzW8d4=H_cj-GWAiv+NpQjNz&9uHL~uE#l+v$`9d*XFx2@#ji?Vh zkbbvJO0wA1Mc7B9kdK8`VbLf&uQv3;vRT-DuNPK~!dAEQCMwj=LrJ?~{Zlk)77mQw zmd(Pmi}UlSuwoWA>bg|2nlMPbo8?V_+`opvm& zbz1}Lv{PaIZN@rzLpBTTlZS=f4>kMb5!(L1K6zMpQ9rj&9u^LDb~0vf7-Pg@yzsB` zOrP6~;niwZ;)T)FO3-Iwh|dHao{0vYiP6PK(HWgJ)kzor(d57+gU+WTdg=$ISF;v2bCj&s3alq!lyZ?U34!7@uL6*BSoRX zL#kBD!m_m#xTD+GPc?bm94=jkAI&@d(e;soNzM7Yn2eUVVu{Z1B>qmb09!z$zcy-} zfj!w_qr4C=X?B1yJJpuhU}B{t5#_dw?{lHeCcdR!dhnP+jnt_fcAy@Jyzt zyFgjLN3qH?kpi$rCFCdNUpA0W7&AVx+rJq~3^}9{CTycg>5k?egO?xLp>avZ5+GaSO#L z{i#f1FK`x1u32_m$UEfzw(+monH9q*Lcdgnl3C$oZ-ryC!i)003NK<6DmyEPvAbG@ znl|=W>lK|Y$D^}?URKh}T24k4RJDRw?<%#}Bowu>hK{gp)O%TwdDXSx@Dxmm{XO-3 zdlPjx?XV{+y=7q@+1ydixC3UB?Gl0B4{P{cB%R-Q*HreaOgl`O!Hm%D^tx_2UI&!y z*jnVK7ZuZsN-0I-U>>Fv;g!nzlRt7_7420gHz_r}t4&Hta63-x$U3bb+sQPgWP`FG zr40JMPAU3_ca?@Py^^E`(D*oIXiI!1Pt`vkWbjFyo(Y;@=C_|QamMLEl4tNG z*Mm8RGq{iN;n^$K-hcmY<1Z&89kOhnh?`~V6pi%BP;6RuX(l64w#AnEM5y17s5f|u60g}2_z!YSoW)|hyP zn6~_lq{li{daS=tdaNHTJ=S}rhalqw>G%8#Sw7r;)|rXb|vtr3F$0}!lZ2tJGN@)gKv6aq$yjV>NSU>GEt zx{cws&(((AVYS5#(y7Lh^T;_1 zFy?nHSghb1KW#cVc*wJ*%E@@&qb1|bQd2f|s3KpBFRe+7DKyBJdPnGU~MjsjvL&*-|{g zJK%tm;fAOzQk7$?JZCru-bRv;-=RTU{mbXNq+l9cJs%$_)8lzxm>mNdN8cR0w?ShV zgS_&jc+S84b#RE!4wc1%){9E=6OM2E1wQGk&b=^C?`0@VmJ}N7JS74kZ35wgg#nM~ zffXhp$9Gp?eCM4opka%y&_oAQeCtWgt0rZ_6r!{FQtb#wqcF%>3)#(yQ^C^Ig&(m( z6t5HP{Hr>;U3GcFb`;++Y&u zCA}bBsN8k966TyE0?I&4*Sfu*&iak&JMlyd_n`a%rW5lqb`y2n5h+=iC22m8uOvPo z*UVSlTKc!5+R*{^f!`e_I^nIaIp2q`wL+to*I}4Ko-(i;$PjBdo#T%%xHCaGe08~9 zP$CS>itODB23XBuMN`aG=#IXi4&+pbixkR#zPiD{vLKShzOcT_=ET$*6ynoEl8P8G zTx6e`_!vHb)BUnMxJ8kOtsiipG>;g9R`Em*7Fi)&c#=;-=MzjC$8%ZT&&ZSwF`&y- zQ0joeVC)?r@Ds&V5N@dJPE!#pOk?(z8+>z2XW- z#R@*sG*BAr137H5+9U(Z&nPm2rJolhf=QzkDEuf8R_W~8W0G@hI_MVk`D%unC8Bsv z5i>(P0plo-fWIbC(*x-hI}pg+DFB{#Zf|BFM`z)|0^0-X!YuOEs81X)3Wt!E8#1Q~ z!l|K+(v#_H#8Xn1S13vZ&4FMnnhu0$n&Id_ZSb*{V8R;hG}$94(Yur zRgy0(Rd0DOm>f9+Gw_@tFu@|DwAi@&VZ;p(ff#lpFIehubA#cuanRt|^S5BV4oCchgDuxj^8;;0)Ko5&IR~Y?xI`^){4#>Hw7~T;LseD`$VBJUI{}X<}G&5koy1BJ|qlFUtt7(|C=CW|>5rj==Ml4g;DrY?WSml1=xd70 zD(k~ewYeXmgECY{sxjG;<0Bryq^YpZ1>LR!kx@th^3JLVNjl~GBc~I` zeSi-=5Lp-i6fY8@WBi4;1fj^<-_2&TdiDJI?d|Q+r4H@R+6 zNFK_pvtD={opmTRoC#)cTLzLLG%=+#G>XN}N{|p;d9lIZRxra22}BgeV#N?~u<2nl z6nz~}{wKU?p_DYVJJxGRKFgM9?~FR2q+Q6VpQ#JBuCF}WI$^TtiM;AKAF_x*62i!Z zj&DS0giuC-kokXk(em4O!vyYJv`6@&zA>m}=!UDaU0k6ZGDU9Ibbl4x_Q=tKR~UA= zNaZAs|H!Hb$$~+xf3Ey;N{J*cP}88@4XO4K_aL+|z}UT(;Up62S=Ye?+7PLpL}HZ0 zVSw{gB=og_Y$V7=2Vv}dk~0mBgNI*qh>e68nh*!#{>*YICuoEKXwu`9j%Jfn^MlMO z6+6m-30upIP_ZJaO1X&h^!MiOR3^nhX-f$eMU-8DMj15Y&r{`AJLID*f=4L-Vx2C=xY@Y zMJFCHgQ+D$!*O}UFj$QOxt9f};Z3c`vnK{3VxlC|2(u7<XZeP@)~J5N_NyGP*J%}74Qow-%IvP1nN`$&j++3Pt9hcSNadUmOIvGP8m#D4CU>eIu>Ec8ukWk)@(6Rnn`Ib_6&WW>fr;@^ zb`uoKJHi%0Ql~#)P*H+K-pf1sf0NMoe49e=WbtRLkjIY~swAFF62OqH;3}7Fj9yk? zeifDu8XY|1Yb?tg5P-i~<343=LG4WYvU|r&(4BfFN$>bWbJ8U-M1B}`C zFhD){C53Jz2L<_xUS?)i26E9E-;XhME~S%2oJZCSj_z?m#gU`{_Q*+BS&5e7)D{c5 zoR;3?Gd-R#6V&!OTdYt&>9h(D_{oCAIE0wQ29@?FQNU6?={q+(hat2aJ{lcSJ`-FmcHw(oQ8|@eYO|wTm8#M_oEOO=6F3W&lPe4hL=A$qGQ$ zA7;PgPSfia<8>b!kj^v5{YCJlOmy+{_mf|szWW*zN5Vgf_>V{VSw6LYsU^kSsg&=Z zr6$k@!z4QDk|vFFyY^Pf3R>%08!AIO{zG>cHQi&kq8c^FdI)2EC}j#r6>fbWT@GX8 zKXlV+W0VQ%=ql1sMZVH>uiKbRLRUFN4MJlHx1WgT>-xUfz`EMoxck}lq$={m?^Y`X=^Y5%TNhMcz;T--$9-W}EeyBW#cR&(L;@MU^hMBN-s-f>oq3VY z;#fo#U;5Xwt{B)JV|wANnhU9n+5{$lL1{|4$zG>q30 z0CrrhCo+PxlZ`-WPo{iMzx|f{UK8nA@5%*7OR0_924Vi%pC|j)yUXE}Z<&+GI0-J)8q`n~(k_S`X znP8Egn-eW-6$Kg0hSO>oeV|-Y<$~T?)N35Pz0-Pe^zX-nOJEqI7J#uoDQQtwR211n zqB@w}6$6X|_%o;Y6y4`R8f*2W9M0_MQf7C5qRlr+6U8Ki_14MCfKV1P^EAil9G}4b zGbb}GsJ3&=FLx^?4^r-mQOTtJov)hi7T4^g9<;3ba-95IS%CozBqw1 zZv*&+i1;)LM2)ELgAJX8VkFVm^h(6(-?6_I{yfQpH5L7Qk#}lf2h|o6{8h;-EKRUL z2V~M*eUHdH1`I>9}oR- z^BQAbYOOV!y_sZ#=rvu&tGc+xQH@<3k{@~i+~3q&tm`hZ!L@E7Kh ztJa{n(HMtRuJwRXS6}nf6#jLFzh25;HT+eTzv}qwOdolBhVC=3>8T1%PQTVzud)9x z8qn}89CI%#ZeE*ISfTfCKfI=QD%$K6@k=@H$QX*RF>oUPycR!SL}*luMEqV;Pf+wX z%hA;?TeQ#VHJ^nA0dOrn%|)g@oq76q^|*6O8k<$i*kW;GOMje&A+u-%vwP z%!^MA^hM`y75R-K-LJPx{9JTnSb!a5qvb-9y~DP5_nIT~s2*W_s@ELGCm$OUCfG(V zhu0hzNLHz@gVoCCB@}(_1ZUgnYYsXTA6oS)SZ}BXzTM**m<6UrmxizT(O$gu58g-r zV)dGz?3slfyepj#wu^Y1t_g`p;9cu%GPr()*I*5i`Ed|0QQ}1#3isXD*lN6jhyjYjPOwU_X8*S$Cw9& zf@UD+Adi2$KL<{;|2OB*>0LJHUvJq1;@}%L@%%u)s#yOM^Y$bz3D*A{aYE10#u1r4 zbI!LtYsP1{OL6Hhq(aC!A~(vlB9a#sa&%2Crt)X+hTK4)6=={&-@-lD^}wo(M(Io{%KS9no~l;pRCd1r-b6ev51Bd@HT*@SOG z#=-THJd9^kaxd8kA2tx&!c!~W%yZJF=?;Kz>iBr2R5&@#nG~F69R;Qu{()TdQDaP& z{rFlO)5KSzJ{iA}_Lw>w#T@76JKc%oHJ$CKpO2mY(Zxv4Qh6Hj6^vJ~42S`X+(___ z)_Fr*omy*)B-*M{-bs#?SCFNj;!euo@AEDw12OiNd7TTWR+;#5?>8~WkbX`kLF07U z%88x@fsrsUXlzu#8<{ys=c|IL+8z=oy^n)Sy z_vIZFVm0$ECL&|{ji{W2)IfB~6dwd$GJNMW&(CE@1DULRp5;8E_$M!NMu_ZwmZGwm z&k&6gL@w@=PYQG)MyX){KP+CA9*~l6;8e!4m43Y=M7j^eo;zRZHl^GFCbP*XN=L+5 z-m7zTEWPJj4Tqwm8ji#p7bMhdCt;wakPc^%|Wx<$-@$j`ksrt%0vc6;Dt{!+`oCH zQ(mqt`2olc9j2Xw#OLG$(8waBl)@D@M#^1k(&XyCyS{!e(#zu2NSq>g=yedjR9oZjzd}wze-rf5m08Bf z(N?k>=>jq)b)FLh$qQ1__C9S&g>p%x9`l4FHMC}B)*mFR3T1-dQk_Cvw?azAy*L1^T8@5x+fqrdtNs|19eF zJZ@k%Ns6459+`v!q=*xSz@HSL5@S%p402FSH2sUYv*BZuAUKZ3o39g!~D3-1~>C-yznD`FV&g?s{epQO1w#8^gDwYOB$aet)ch13k%; z;x22z$Aw@!GeB$Iuo0~jBEP`YW3<2qpH0vIVpRy}1066N9%(TijT z9vO>jn$Z7jlCt~m2&NM!>Q8{HK*Rs*U)#E6{S|kpcbZr7jE2qxGCnn$tM^8Q_RoZ{Jcoo9u~e91A%dfVf^?b29trZVzTTp zDiTC8SVyqn{!j)yWz;#l(V8DCCXhC!wlpamG*8 zE)hZFeQ7R3f1W=FrjaX1F&Bb{2>RMBw6DTPxwPA6YYnps>xjZKgO2KcXu6QVE-I+k zj+;xgK`$eiSj_qvzP{!}AEwf{qpQwaZ$8!0#P%mt{BWY&f^&`$HqgG}r?jgTN1hP6 zsM>+Eu~W!$DN+$M*?8x^Alayk$G9?pki>BO%1XBJ5YG|QZ{+-eej}hPxcmJ@poetp zo)sG}Lq8@z^w+OJ+c?z}dA%>cInt0ZoJ!0i7)PElNk8ty2koHOdQIyg3K$@O1XV|7*DZg6%6%W8_pvPj*h(n2e0za!4IP8(G#RC$)jj?mKgV+<0_$lbOJ?&IJ z3)Zu=*kPo87OB=Pl}iISTDkaMi7#Ey>M-JW!UvCrs-8jP{YI!8o#KoQ*|b3GV5k-h zBE|$w(wu!l_a^wHqJJkJvYW`*f*qoBnv_MKzv&&_0qTkU#-K3-eTI!~kMhlb<6FAq zabn8bpFiRah>by1Vcz*i;=Z*a{zbFVT(B|hvpf8EcwUux7 zlxwY~)z`{RUvaq{cwT5RXzwQm!E_@|FeLM8&nt-QYl{i-kKBqtu@L?;+A9UE2Ph}? z@K{F_1ng9T$$?tH@S^cl#wANdw~o-Qcc5DfbkVknk#OtIu&xrA)9YwD)Rg9%HV<)^ ze4{b3?*D;YYgEDrVQgl?9zKox?ZaGb?#TWA`^hh$8~!6_dytY?3hK#m4&zv1kFaM> z_8(I4ZFcp0lhW_a-hONE1rmMQ?C4X1|HPOtwJ|5y#@cbk1bWBuwIg*us%r1P!9;s| z32YkcVvG~5EU}}9>%Bc(e_0RL+(Xez9lhD-2ftg(#NLZx-}wb(&M_pN{z_)e?I)g^ zkqxPj=$U-LrX6NI_9(1y{mw$*bm$zkKk5qYE)lIVMP_L@? z3s4YOkz*rth>bS=CRWqPgpGtLbnym~FDHY!2zVBVXfwbRK=g@0Z7`1L!sIM6)Ko!5 z6u7a1;#gdD#=f5vlIj}tO4x{?Ym1h@!txX>BF_@F^TM5$r|izVX6+p%>Vy_Qbo=jQ zk0ML%*2(HsWkF=dou8kd(WNPd@es$G(KBGs<>F1`zr-n6b($H>uh ze{#+I&4F#`sMcpnOP^q{@-a|Xbx`dcnG}t?X_=0t29X7!9bxj0VJdVuh_vD0y?tf^sSc(L4E`Q{RTN7#6|(o&fF%LfsGR-ztaTEtym z_>1xC-pOhx^NyhnY54N7mUE}-kY5e3$1Ajd=}(k-hGY9uVWENxFyOOi#!oF00{r;i zR0_2tpl`Muh-D3T=f}?fL=Mk888>pOmJF;3CY;$6SElr+AP1Su?zkX(0)qUzk1r_v zfh|@Ms6CN8QmmwqETQMtA)Y|)ev~Mq8K~m|Ny_+%K}Z^)a+eVyAC*Oi!IQlCcss|z z%eZ_oQBPx2yuyJVj5r{EXvKAfmxTX#1EYaEXZ5B+(!HqWwZu8QdjQ9-WUsDV~4D%<+A|n3DZQ;D%XpBDe zo5O})(+0~&QXB*2iyjL=MPSe)&=!{dc$0bUOty=~{Yd-)_` z@&gvm*8hxZz>mDluAz4<^2;8nmr~H#@aJEsPvmmdZR2IKi*H`pWXd~_FrM$n!{bt6 zbN7hfxIMyX(CEtfD3`a@qrV@0nt6xR${{B6!m6^~aZ0^Hxn#C?7|bV7k?LV7)T7{u z!blV;B0QW28@l5XcLSSTJW}uTR5#j)&rXtm)h{N{|owhlqV;n&iyoQn85qe#|=bN`b+mtQyD)#2@I zvbhl-8q3Km%H8{4|M`>lC%g|SyzpV^s8W1>y}I+CgUa}vXEb~cy7Keexwjlqg!;|V z&F^Ugosv&Z%KybVQ;YiF@aMSTmJ0vnx<$8i?3PbT$NvSTZo&O8r||C}z!ts#9Ou7o zR^CpyedYgu`iD(~=d@sNm&lo0r@|BGjhd79}Q3B(?L*- zc~1_mpd95K_vOo%P8E~4ZZ|%^GZ@^58-Eev8|N{G342W*hiuS4bS^vOCFzis*PUVsu4or-%-NW*^>S5XA~=2eT?Xb#`v8-Wkie!too{)7optDT>mcg;>c@Axwc@9*EOI5zT{ zs?Tt+7~pr{`$nTOZ;Q&91k-iB^{##A8UxW|`U66+W=26q#L(;;q01yjaU{lEol^qZ z4G6ROz07T4n|dVkmCq5O6*i``zg~p$9O+HxLa>7Bx_sPK6io1@*Mcn>=ay0z5Dt1E zily{UDWpD2jJ+1|9lGAl#^S{`ED&!ZAP9kX1!TZ<15@IxTd)vl3C{6hJ>?qV{3`e8 z$|ZZG50J2sk6%P9b7a^LAS*Jg!c4gZWbPnBk=lTU7kDnO&da&R1f~_M5RR*Rd`RJ| zcjy{XXq+O|0Ef^9fJL-4pm#uaCdpFdKG5r{%tRtP1dJ-88U~aMHr%$a)=<3lJgl=d zbayqQNZKByeX)+4P-tTpJTFM#2$se!5I24^iPl`=ATeh7+Q0VJ6G_KN(qZbeB`kP6 z;=}=xL>t5RD;)R2oA^-%piS_Kuv&#Q$>@Wp^m6ep5q`{c0ADqk6EFLg$8YlejUyuI z>+O8Y8GKF-?a;xgGy2e~c1E>syH_2K-m2@w*k@3WUw_RXO7YIEI}LiRgyN$&_coZP z!D*r*4ZHV_;Sq+f?}PKszZVL`E0K7fN7}m>ombn<)=zYYpwi7_S@W)PlF4M;*ymoa zmcg9hap0E-T#9|J_S^8a9RJ$ty{*Fc|KO)VwO@Oi9Dc7>wGRF4(lYLT@6s~p z)(1pw8+5x`vy6D~u6cfL8ShzhIQYpjR-%Jw!bS&C^wL5HQREgBA-aPk4O-34#pUQ- zqu*(?lBD3$i$SAL6p@JDb_c`exs#<_X%rkDMQgAWI@n^tIvic7(WbnC?FhpjSf-#| zYgR3j&?HtWCcvAPNw^rC*=C@1NfUs*#w9EY(EFu3D2aL_I-&21LqFhB2Qexj3Lp3E<@MNjGPXf<4>t(@TYmGLMmF z;>~4^8_Lp#yW7;G^c{piyoKk)YjYY<4A&TRhR0z*x3_3;&B$Z?;eSLfofd_=TcZZy za1J~?LIthY!zkWQKB4Z8=`P=K_8Rh62MS$;K1Uh&vvif<8JT29g2zUqTx_1YD3CJ= zW<2;4-OTR;M&49UmN;pxGaAto@zr=Vigx4Cihs*(SpMd!8U`mT`&{dG&YN#geoldz}S}rL3`w z#6{gtLOLl~i&JWhlSs6;pFscJ-4y#S0BP98EP*h`=jZnRYdc*WM)qFr2!Pz^yg(1T zLJLpw_cu{2?Q8-Nr8hw*xXs$v4@9;?QM-4b5?l68T)4v_GQr>G%zEp^17;TX|J^ey z6eM+3vx^M@cC?obY5z_3cD}mt9@1ajjra6eDsH>ijM8s*_Ezowi5@S&nt4EnQAOC@ zU#Uz!h{RI!+SzHTmL&Rovsyib$f&#Q?z7Z#cavD^f;&4a6{Q2QDE{PQBunQ}CvX6r zGb8h9I`b#rJu*c(Uh+l)TwQeFNgSqko?SZ#23EPu9Lwip;~ARe6FP&TjO?=|C`y~n z+*k0hdmhn+V{^%=E75Uft;Zf~QOZDAqo{KAaI0&3QV&A~=i!k7$C$;*UtdfYY!G{Ci#LiZO1%siGp@5?J$JM{P(f6YMy%>Ej>)%V! z_lo|#tiFQ{p?yDA-;2ig6ZO4pe1D<7SB&o!`Q0rT-(Sk_Mf1C>23(F6s1a9U1#-yf z7GV^~F&Er z-iMBJ(d-N>r^#Z@FiFMedYvdIPed*+6;C7lCU%g~26_`YmFn0{+*E8JHyk8I>xtT; zjB4BJ5_bGg2CJWx?adQ_C|6`9VqvJK48{4oXyuF|_arJymX?Zosf37LQD~@NOtrcO zVvPK5mPKe^2(*ax8w&GUuvpD7RdO%7>vYDphk012kS*KW`0_0L=~<(hY1dGh5=B%|Yyt2{r3} z6%VHh-<0s(!IzQzo9?2*YNsLA&)!%2=BVjYhOWvf4xo8HAP>`|9-<#x#MRqY_syu$ z?|1u!>|Kui>px}@6u&x;z7JnHkDNOv57Tje(Wwoa-44t)y5!r@xY@h>^5KZ`PIj3~ z$5upaZhl$(GK_8x|6AOFxFiwhm!${e)=}esOFJ4UYYpU=<*#T1|69&c^ut6`AFBOM zv-2h~cj9|mBi3$=6DjJaPvsXhuwyy}mYoy2rzPjhX0_8D zHfxQ7-90*+F6i-bvszY>JR}oW~~4{POrq(BglO z#Vl#ZK0?Q+wJz$7QN3|?@g{MKSse@-{b6>T<3OU{!N1U}UE7Ho@#h{@!_X&30%z*Y=G(RZ9lsI!9B=Gx<8oBLX!o+tJdcA}<(*{?qyEO4%g%F7 zPBe%r_h>X5jZXQuP88|`F!7^T*c=sO_*8~}T{z``I6ok3i60m|l>j9oSOwn~is<;9 z&5{oOG?!iFaz7LFFFCW-M{;1=$m(b3jn1&q#|O$7W#1f183z(4S}FgifwxNnn?Wc< zci9{|Ux@BbKFs=T_AU-P&)?;2h=JrL!!d0&IgwP7QW|lw5nSZIs~NinmC{s^n_|K6 zZJN^sNlL4ws8tds-?rgM_@p#nHku~^^hfj{5?CqyIF9#&gw`L|6(!`-I&@;`P=0yx z$M*^Ud&08acD)nH*qs6~6avvITr3h|_cC|F+|CsW%`I~~7ZyU$%G$XQBowZMoh+*X zBG2w*D-#y^O26!)yMh!uFU+MIZ#?2OV=NkNnpI_tG|e)KjdGV=W9_T5R8o4Y%#I!X zRmQ{a9xLU4f1jn+Ztb#E#8Y}K)#@D`mfG>|{z~;W(cS;O<;E(oeW|g!o3_kYtKGH4 zSZm$0yjUOIzqDA#OfIYct7M2eND`IkjA;>$R{nZ~FE%q7it~Tba-KU`Dx`OuR37?c z?f6gD+yo2)Y6?_FZADep@Wv}{w#6^2B^3GLNTdLxioZIc_sJhk=iYUgB|XU*&u3ZE zPZJe639jr|ZpOqtz^x^2%@n#RU!GXf6eM9H5KZ!Rk&JX0Q{Cp8ighO|D zbn|1}K}g^ow0qiVDEy*RP**@_qGLjF%_|yIbd zx|F^BCj~;`ls|d-jy{i=frwx%Q>3nM^5^e*v?=HtG{KLuzYZ5>P0OkwR z&S8Rlr@t4Md5h@Zt!8Wg^mpR2a_M94U|zCj?sNyP%(t9CM+^s}x`-?Y&zyhErqh`p zo_;YdZ!#H0gwH-ko)lxtoqi!Mia&XJn)|&>Ys^jf7(YATjweX$up|ZrZ;4*y zS-A{%(QVfl!|{zj*=oXB{GGN4cHzRuo1nZsSm6B8^kQB(Z1fw0VYNRT{4_ZI-MGG# z7i_pbe?wWAA|Lt}9W=-Hi!dG6+Ft}<1pIDs2!j4=hlWUu(3{HE`F+YnF7x+;?5`R5 z2YP7k|JwKXUam?qU#!Yx_*Nj3Q7R3!m^;M+`sZoXqt7_XFW1c7{7YTw4_CA02(vEW zeTVjkR4ARIMuqJ%QDt`p1jm>$Xf1+WJk5y;n)Nu~hg+pqqUg&pUFFZjG@Rgq3s^TyH}l@OU1tSG^d%?e_6}xw7iO&(b)V?Vvc4yeFcV9V9J;01`V~Q?ug~klNl$WpW z^rm^>qamKXCCM6j7QPAD-}(@7zI+$i$l?XRBu zw%(m@W$K)NzjtSJT;uzG5#MvVCwTZd8qIz81dnaUt1Wy;!j*~=u2i(ZAxIH{B|a!e zusMH7!byAzjy~wljM^i8^|+!}Q^ER-sl-Uzp4|+DedP)}k&XFT!=?mPo{gH_(OGrS z$fY7SNTW4^ZPHn!tsX_?`;@o!35p)pqr8*-v;)0C;;1QV87k(*IjPLo_C+MyF|r*L zF=t(3`8@>EM(}%vZ*~N!m^V(1ztLiL-tb%Vhx!TsQHz;{p?4?FTluj6M?9y+Y-sr7 z75zv^Pou&QmI`K#SxBA%*>NrdRmRjt;m&=@u2J;m2Zy}kbBVe){vGOqs3k((JA^FU z6+CM1N#P0JL)_)4-6tOXLYPB_VfpLSz+S~{M5r<0Yx7{ro(dGh2$6@J92Vcqb6!w- z`o5spVEE?$$`{Yl>52T>=Tz+&xP<^I9@J)yVCAP6{rs8ZQUauqj~+GecqZUSY96X& zueMWsM?ZyHn8|WY|G_ZkU;~oF7R9mVV_Na+AL`e_UtoeZs#eHTd~B@BK34HWFj;OT z$4Ov>`x!ZM7*m8`In3$eC>J%voBR16lp#ix3NNmIJbT9f`b_B^Eft>p{qIjsjjzvK z`hFMxT*S}DjH|wIEdBzp&5uqw7pqs=Q?D@1$twTAD9&ZsW3tr$&ROBU&8mJc144dj z{GD&Jsx^Hpa`rz>dFm6V|gR4&E4}>`XCo0L3I!oeO(fUOhW z%vN~{F3RNL&`i`vM8H88kC-&yl3Si@J~`nAh`1p~9R1sTc4dCsE`j(H^P9h3F9Y+F zN6!A{H_{z%y_NYD^Q8oH-~32&a28lTuEsOV*9FGQ99s%x!VxQaP^c{5)p4Vx3{U;n z#`j0MbiR3HFTh~mSa|U5S9=Z9&3Ma=9v%3P;1s3nH_oePqkiKJc}N}|9p%iDVZT~y z(08YP_NE86=lklY+J7^^up;POBJw=sFa{S|Ar$Epv%1aDND(p%<6&Kd=vDa!AvqxH zk=atK`$n~6He-M_`u!9z1$SO;)w&%zJEX&gzEg+_-~P9w2R|U3XFm-aj{8SUr#cLy ztmS-gyM@|m|f#gbGH2C&1Vzgu!1RCBCn|PzYVgZ70AWeUEUep<>XCT z7A&qT{Km7XW4Gv_AuHgVKmEy}gghfGctzljqgQYX#j?GI`%YCbs}u|6ioHrvuVPjz z7haxN>y%}kjBWHzj{ol)4B)7*`yZGx9#lOv+YN;8GRxl%BluCX_D+X?v{eY?CU&AJbo4Iy| z0R^M8_Mo4k7#nCso?+qxj+-aa&dG{SykZqSU5Cv>EUo^o1&%|t{1Br zA8x#2Z@?8@qEqkfPlqaSxTWgBPFf!6*l_|zt--K5yclFe2o^;UQ86tX;x3vQjR37j zZsBdUb#5Z!FfAe%*%I%Y-NNE(6*@QGe0n(9uIB!o<0LViU$k0DOl2FU>2^NffhpN3 z4g_OjvUDnmDbXp89Am>|bSjAnd7>P{RJ3C%e-wE`!{q3C)eE^i3I$qTvkg>q4k<(N z%EKt%Vk&!~zXhzmylD-kZa?2O$Cj=W6*9IMKN0{5p0Py-k^nl2vK>Oh!C`^oAb75) zPs?w{bpK`$I+wtYzW@4oq6%C;S)&kJAz5=Y8Uh=J`bDqRxQtpgtHcn9pX?Rm#MNYw$2U7==OtHonhQWA7))obp2id{twwMQDP7}+uJA8}rZU4vGCzFb?9@$BqLdb&$ zNHf_jl!1n*inH}xlUdBzv_JQKuwB9a<*f(|z+mZLgM}yJqRU-lBKUhIH}U6kL)1eM z9b7MFvynhD(kiEHkParK%ost82!X748Y{exl~USt@_?ZD?l_~UtfIk;|E`dl`Vwbb zZA}zV*g)gFwr%LVe;4xReo8NhujAEGx!Q#yYMf5jK~oee2dASYq#`RQqKe_CcOg*o zHUJd{+=J0ZE1Em4wyl1w>;Jf!3G+&9J( zP2iqBDwB>osmWfIZC$s^9CmNe7z6-xZ3xdwB*%W^!;N;m+5KY=H!u^yc5&rPmkP=z zgsrx{rFZRY&Bhzwpz;4aQC9cyaR;6Y zgxjmRcON?sfaXSk8iVKd+Vu?jG2^9nwZpk|DBHkbSH%y_K_*+w8QrP{bW5_cUatoA zN;#nhOO;{0r_$S>E}>ePsy(bqeb~S%$5ds=2~0!bQ@aFp>l*J|tTl(VO!kDTRbhj7 zbe+ovUhQPEFIcaqJ0MtUN=(S3q0m;d-*_{s4f`!OqkEd`rO4|p&5fJ}6^)vx2EGBj09c|3>}BpfMb^&S0c&N^?5khh}~Fmbz_en#PpY92Jhv zs~0WN+>6{^II}f%isSEY10PIt6UE#Q8XI}JkB$Y5Mtw~}*G z!K$t1n@*!Hx)mRRj_;>Vt-xS~-B7MTwi~KwUt=+-5<``k1f-S(TfC(P@p}at$83Vp z?*q^RFv9%D*?fNFeDLvACdRFw5gZ15bJk#Ug02!HSov58reVL?81Q+Oe&1%m40_GZ zu-R@9%z>`xl43KS<0&FQ)1$66%FfYZF$t5J?^DOrd^eQN@3o~zLK~hf86PT0oQ0gK zah0WA1AKcYSS5mlpLb@(GTekuVL6LMRmpNvfYW`u}=@sW*32p>~SJvz#g3i>W( z4jajf2WsMmC9w~1Og3(V@crNvWE&ZEWBTYErVKqn16Htwq6Z2Q;Bvm7{C)XIHbf&6 zNcEw);*&^5kN^B^w)|L#-W{7-E*Hc5+iOt?TIEIwsUSc7g{KKr0w94FRPR#_=<0|O=0S3(x zmp`JzsYl@k)(#dC%q!>nJ0!B=JQ}Rf%?}&cn|k+R$o&zY3#VEd`sAum3u5>68BOPd z7shzAp5vmhTm%ZHM4n7evAP7zI{SEl?Fq7;KET3O$!eLFS1e-2bKm1@o(w(|e9%+^ z5O#mpin4c}*`1C6{)aF;e&8^0(9vXN)N4lP@Q^7@y^?%{S>W|`h{MI%;pHdS-GPlO zcJTq)<>4s!n0?T3K%Sz9h}jN4iiK@TH1TV+LkJ47335%q3DG%?zG%`8rn(+%uY^S# z!bUTu3!-_XLCTW|%4q_oyhjX0#|4uNzIJB+U-5!kR$7XiB>hAm>g~6aHOU$mtE@G% zF#0x#upFrlc$9^m6qTm3%G+$blhmXwo`Bndw5W}z`2LZ$4j zaW*p9WFl|X zO85Axgq}-afdy8lV~Xo$H`+KfAhT7J#7>&%vJUV3ySHY6A8p2|`@m4ForIX7#-gg?Q0DM4$zrM1?_YASg z~R&$|vfrg@VIC3LslNS@a^ zqh_}~YIeY$s`t@J5ARcq`s7x&uX4RQ6qUb1i}%!L9ZfohOORzA4gQ!1t4*llfJ`^Ku)?~9kpso1uKZX6M8B4fW=+AIvW1eYsg~^ z=cFv09MZd%JuA?iLQd}_;%^T6g%K1M?p_w+1uhp<3o+#0_~SCTg`M(RsP_m~^PU|V` zBJW1ke)j@o^{F_w`m(t6WqpxrcjTLYy`>?&?du&9!LB~kt7kPng>>ALeNfzo;~le6tuv}UFgpf=<@S;f{pPUYq)&AHA{t6@9~Ac> z9jsbc$SV7Q_Tg%DY8EjWhhQufb1m%Cup|;q!*bn1DhLezr(t6k$7xWqLup|6zw}{A z2tl0K8sDk;Nfab5<*152Po3(xF5`d7!B`I2rZAINq?QOFMDqgj1 zld^iTRe?MQOK!k|=%uwJg@CG7#afakfrtHSkE*?}7uo8ex&i;YjAp&Oqg?Rs4hZ;H zsWhrLD8y7@H{i*M1ax-;UPh3|dmurZkf0~~K%G72em;{X&<`)taDEwy=G@% zS&dqujiOxa7T#Fwm#8eg)%r!XHNsF>HghEgv7_&`W+T1tSg4yQ=XACLEA7)PjJraJ zS8@Hu`%$%^qtm{-dLN?iU=6UaeA8=ocfLcxz9G=4Hf-t2ZZWo{rokkDN~5lr{Njt0 zLL2e0v=~)ANhwUL>PA(MQwr0n7NV-;AF8Zau$QF|R8)LvFG?S!s8_KUS%*kwgYKgc6K`g@eKSF$ z#6WSf3&4istlPy4ae=gp||UPPZu%2GR=E?VjY9MWx_f45nCxLzgDY>oUz)VebU{IhvEvHJgepE*{bXNU@4~mzHjtl5&gB>x9uuXq0QK&(q>0gjmhY!Fs&+g zElH)QOi(I`xY}x^RKya)0voLGAWa;>khgqvVmF<8;SCU_^scE z*m8%L(b?jQ;$AR$9ziZr7$d^rFl;FNK4JihIR+}hV})3s`1DGgtBHPfQ76*y>=I_f^~UL}g-LnF4)(?9V>=D5}9s-mZ5ab@nf% zc`U|XiSaW4TtdI>CbCwj?>=bVVMsgb$HWiE>)8r+9B1r}Zywq)ydwbg^;)cdCo#T> z;PAmo${~#fNKBX3;SIY&Z@#9-LtBrFg%r3wJs$4s@x%KS>rpA*7+B-o?in?MDKI*g zODzkK{RT*Yon4%3xXSy0cHDNOFcA55v=-!E+gozCVcP4}5Rfb6`9%3T*WQj= z&31EWJ3qI9*)%?s4C=bRY%Q{hAVmKGb-`FKVoIS6%+`xx&5mwAR2nuLi3)q6^vk6F zrNgP^rt>YRqr2vfl3hzI+&F?bpWewr)enL)dYPj zcZ?G8j4pbH)lw-HtY9B{!@liy$tYJ{+S3qwd^y?Fjy!WPSXFvM>@j8~Q5IIxYv(!FJ$hl&7#-r^|$@Hirt$%Y8s8 zxcZG+7fiyRq!w1$1)vMm;lmc>Fm*wCF+FBzUyyOz>|qOXxNkuYcPz-Ev>8%3t7sV{ zFji3;omWXThm+8Oy}a2QHQw8%J^^Ep+hWJ;krG3@lw@C@?W3y&4 z8%04{oqElpVT*#4Iv@HqJCU$JU+6I!;m>{kGwj!|`#U0aN`*8aYd>V+t_f?_clDuZ z@JTt@LVu{5cips7n_1GPOh&5mNm`+-Yw5ar^g#~dJ4PYhAmliXljCTLtDv&AB!vO$ z-Hz?nN0y}2Ive1zsFgvvD5YMjbKbJ^D3zqP_LH4Cu)tta%qAmZ98s{jxIL9Pl?9*z zJ>Sx48`~rS#!bMyOu(?2Cy!5KHjYtlJsL-dvs$%2s@8hec{Iwclmdv0+K6ucEHkQ= zrq!kwMjYD1Hn}l~G%SN&qhS@js!ZR3+`^mLwR7ohXZ+&2F48!>$LOG;5nS-{5_gaO zdstY1ga1+B0hkLP;}cH&f4)9Cu|w15ETNAP+C3^B$6yC_vZ)!p0H(-?#loU01BSdo zrm`~=`$QlBvI(A^Vb=2bX3KTnp~Yhjrm{R~Hh(e;(Y% z7uRK*Wn-7HV6DU}5dl)N8M&&=X1$HcGR<-(WxT+WFUo1H?!-T z6gs_a>U45#Ajj#wKHk;qa(b`JmR?`03wTWtI&@YfUXz`ti+G`$iryFFFldJ27S^zO zc9;M*!E#Ag6b6dI4uBLC(8yme+GpM9j_qVOND3Zx4{jx^D%JNUrtHRU!%5m&IAeGj zr)?7m!{U1Ok*uc&ePg@vAJWK`iW^uP`pNT)u&UI_yERt8)qXRiyXH2gvSzaINYUQoH zg}=+^U97w+UW#g*Kd}JC#BRym4U;xI)k``e>$jtGj^yCuPy$&}yFg7Eole@ds*vT$ zeJKW%E-D^GuD*p_zODcL*eDD8fYc!OEZaS&8QQ`%7Hwgg;6k+;ldDyfrOBTiD|Gkj z+_f@otFn60WUFF~0VMYg{gNXzMK*ElB*z4>cJY!oe`T%n6vtWglHx5fl#PY;a_jc3 z1F(xceQ4A%(O|Mi?RFKwtk1WM@|3nB0O8|iY2$6RFJ*T*tuPI}C{9HV(yAws7t#t- zkjvr-Ij@h)!E@q{RdDOCFJ~-JYrz6j@2EFEv8Q@Qc?$awKusYi zrOQ3oRbxvl(dT;OZ*|*Kep!^ZT{r$#vz`Tt zqV#HKDHWE(KhDkxhZXD=GNUqgQAsJ5g!-+eRf*rE){%;al(lyKg+2ug>w^8>) z2P5~?Q@mkgKvqLqfhk+Hw4>bSCul;H-|a+De7Cc<`z~z~RAR!EQBwSzr2I9@cTO{( zUs#=7b%32X(w;u3(tX}HJD1)ted)a{6!wDh(mPHpz2T*F532XOJvvePaFS9+ZRD~s&w-bAjd-5(G1W6WE-o^AB zRg&NmRgz$ksG)ah!%DIR7R8tCZV0ttNkJM6oY`_RTl-_WMoQC)01l%vHhJDy*q(8o z!5DW?bj%$r(#Od+;*+`Vm1XV?IA#*h$|Qa1g_%-h6$XhG*fA3NlTGdEGmOIe5G>H! zZ0b06U`g}fi~$(O=>yE({0Ke?1D) zsa~ScQPOS@Yy8C+Oy%7k09aDV9;3g&IsU_P@L-gS)t`h=yN5zk*aM?a#8am`o#OFN zFm*iB8AoOn7jaI~J#$n{S#Lao%1)HZ+2+FYOs2i2{L-RM&UtEg)5(cM&w4kP&gB>N z^U~<&2rrz?&gy-={F1(2Ci@z9&$2{WG>VMZ**5Jnsc#~4t5`QDrPSgSor%^4Mog0t z+v?VsmP>_t@LeRCjUS7&)MAVEojH7g^_k7pwpEX#TQIu`RP z=lmJ#IDh(+gFj!XAF$nPVwF}PCWYum;m9%4Mbv8Ena=$AB=3Ck=0dnr^zjFEg5fi^ zRz%Qq#xA-^jX1eetc2+(ML_j0z)^K24j3c!Q}hSDQJP)Qefz|cA1EmZ1%7uv%{+?3 zFQby{Nh{|_B22~Tj6~#jN+&W;Pg=4Cg|}23ZLkwtKQ9wMn^{|FpTr3C!FDs3V_EVRFhtE!YATBWF}RJh9R;wt;FswGv`E$UT2Cnr`+D@OBp;FONS zwYS(aum3dX^}r&WX3L3xXUQgw{eCT9FKhdg8mG-@gt>brlPdur<9_Odvw!)))ROGwDyJ?t4zORsupG`Xv+tg1C z`2-GAvHvQ~CPR2(iYr-QiaxIYn0mliPBpWu)kWU^6BRrHirAMq!Q)sG3!J+pP)3Oc z3v8yv2NXL79jT+mzH54n7$T5;h0JDzqdGl0|izJmWk+aTs@IQ?Yn*(|e((N<`$n{v^@JFH; zO0=Bwo0K^ZtK(FHj!-NS#`3)+DR#s^w#ud~fG|ZolM<#4;@Af?VA7?P3*)fAj>V zlg})!nu&k4y}kx+%L!g=d+U#|I=s*^St1OWLxCNMvzU2^H7VgEtPN-rz9N!s=ueLJ z?2r@a6-u=|c{25l#h^ee9~=^NC#)V;V<&JdO!jWzWR%*S;P$|&d#en;tcpGA-M%Vc z(!v*ZC>b?kAGrGk;Hitm;teUxx-Z8y{D!z<-X?K8LgP?X-cX2GSGf;A0J!*IV0;#L z-VzJxK^2IcMCQ@DJDx#`>lwwR9*O`KXy+k3RbAgXW4SK#+)Pwv zCW*|;E+zKIXnTycV&4lg!nFH?Zsag^*dKlcM<)+$?{2o(E}Xf)yar{M6kVwuVlU|N zvBz#wN#6*})P)Ni);EQM(y~?y;)bi)=oVAQ@7#nFW}C=xA8eN!DO%btcf5@U%w`ArnH3e&z^C2f55I4l6mDZNiN8g19N5oPf=SOQO-E6g3QedJWs&Qtyy7l&~aAaYRhG8F+*5D3}; za{?FsQ;PmJf`j3P2*b5026%zVPe7no|>u`t)J<^kyQ zIHCj0XOGX@ST6;uh={TFuva(1d_oud87~a|HS#tN|A*JMWswt|X@-mk+u=NwFzF(b z0(b1s@f7IDXph;v$L@221>`$L1)ys@{pBg&B|GVOe1C=KH9HV-w>_Z6|q%uV2qQvpNb#RSm zzqZhS+#6Z)BMGf&GGqIzYm<5+t)(LB7j;*^-2N=rK)ChQWYYqQ7C4VVIkit~zuu9N{(SBNC26wk2wDw$bTon^-8@Iuc{Z{Gp(j%zRGof89kg?O_s4bZ0$Q7f8M6GC}Mq-Y4jd%7kMi01H+dnQ>4k6=EY^ zehNm@wRcSsN(3$SZ{g2)K>&pQm0~Rv^5~yla6aeh3;c3EAACOjBNbzDID9qR9A3e0 z2-I#CYaC2v>W%$BP+PW{LB;V1dJ=ar!B5mVkiz_T?CJ#pSUAL;h~@!oh{WSxq)UlL z;_4JFc-aPZ_UcGNXf`98$)z5aee%Fbvs-7d$Y zQCCWRB?COKgXLJQ& zF_{JX98i&?qH`G%vG8RcjA^-Zxp0ZFpOnQdYw5K{#08Zz^2;a`h2Pruh4P(lM~n8g zRz-_a^`_f$#D}+XXky371YP_;YvuE;2yMaAG!DtX_?(_d2 zd;i*)Mv^3q!t)jVifr%G9c;8zM&2^;_bFC&Dp65QLom+TQR}ltzqsD4RY``Yw{$6=3?+;nzfj=uwfZU4^GJQ; z%Bg+9%q;kR+62T^`{goaZaeW|o9=Pvec8av4MsLoVq{jmD_HVAr&_RNd`b+FjUk22 zK(3#Uk(7cVVad$|3%EzFeX^if_>+y!lR}K*H3q`PbHp>y48wX%wRW1;p0mH<;`5uR zejJO8kXk2;OJs7jQK~^DE&90f`GAn+>yt2HXgKpul9OIqz`6E%8!^FT5N1KHExi_0B%jmB!D?0i2xv#>_`(z_4=kQr?*`wj)LpPZD zem`<$P=Qj(J{PrSv74t@ZhQ>Fb|_9h?9o$m_`vixfQ2{!3+@~<7noIm(rd)yK3iqccn?rm zJrCphAGr#mak8kxGUvH)z{DtX0Oq_Lhst?54wA97G#X%3er0du3K#4$8nn`Pm=$6} z>k=zL9nAnQzQnYGNi%U-BVrC+s}fbH{e;iIjv1MyI(K{Gr1;@*?R2C?u$<28*nsU) zvED8H63svuD|YNCRRC(OTIZ4qV|yG~%<h=3?pZfc9p?gfdsd^Oo5#=Pq-OXf}N9 zE^@Vd*SQSa9l48QM`jg>^9~SO$7U~Ddfw{1iSKY&6sN~jh8%wjIHxEwTZM7bvuf@I z$J{NX-j9~Pd3TvGbu8i0V?{ODDCa6rZ8nm`YPx-u)H0kqB=Z$;T zRPv;0?>nZw@6N1)rEl7pJ*b6AkvK_~<2c6ZSn;&h&gj4~qXTnBuVTO|H*1W1g@U`x zb=;uv?3a#atn;svN?na%jpbIe);W~d#ZgjeLSqS_a%mGV>RS5rJ9Ha2tk8IR@C>CK zvdm4icX3Puvhc^Vq{kOjePemn3Tl`QpN#FNX646jR$lq~BKx)@LyB0y$z)g08`GWo z=`eaCh@5J{C#)gSanuDk1Io5OF}@*A2qE`3o%8q@T61%2iqJs6<9#4s;Hls~;!lAQ z=U1&gP|8~;GMnlemv5WtI+6=*#BnnC>^~iR#&mLDdpaehd1QsHHgQCU1k72EZs7E4 zHM}1AJ0kS)Cf9{X8}?tA_`L<1HcUp3sG8^0ne3fTuD8<(wUsuOqs>zG~Jg@6BhDH!#(0o~rRi zf)8v%KxEN*Z|A|JCkQpUyh~ZjySi^GYn7@?YfugKT2{hTeQ!l8wsgg>b82IU#KjDz zNAIx~1NSaPu~SqSV2K2&f7|Et?6t^RkVhVQr}Z#548^Rp#hE z^c3h8)!Xs-o^T)>j>u14bUty>h3KNC0GB1f+WIWTGyPcelgL{%+$)l8T=N@~`<%Ke972L)d6r*&=%`SRzvA11WXO$d+K_}- zi*?zkWhj1rR@;cw`2=3r0Uqb>W1-SwCShdso>i(_Wt)T3z&?IG_$^vzKUQZyR%b6( zXD?Q#5UW%8ZR2Y#7QrG4i47=-*2u;tl8;R`7ps$d(qs#pvFN5J3;JPWx2t({GMPc~ z3vFhwf86o7w5gm1*|8M59(to8u(T)}VT;fr3&WyG(ZFiNF~D#XJnks!EN7v4(su?W zfbSX8t+{wntk&a0$0zF=)-Wp3OH_s>^_9W~rv}=#~ju2!yC|uNY%O z@qecxx_jT;Gvxj6W$WMyO{+sOT1i48@fb}N(Zl~OdbH48F_OL{QK6O?`tox?{BNLz z(-7DQ0N-32C&3#kiWFEeKl60Cm@OAc5CsytoOK_(GS zKeDPj@$(9{!fwYYm$M@50;Bnqq3u>oJ4m+q;sFhBRph?(xG9XOo>eM;N;5irdrVZv$@InG(F zv{|*Y&8ijM)z)nb12-{QbrQGuK!)Mc(B;XG3rRU!#1GM&Eg@frBAtvCQ*F1z$Xfu1 zh*65B9Mcs;Q?hB+wl=w6K?Ak5=zCO(cPNIKTr&-P@`bIp5}i5CwzX329M2AUaVGh2 znhjc)9d2O6iqTf&awf;(S%Q;hbObv@@Rl7hE97AFi5`?*@IfL0MxC2)5^wISVujL@ zjFth%zeUv0LV_MJ^`(2cR=QK|+ds)I%7TbO!^g^b&vp4{U>c2L&Cq1$Pyoy4E`&66 zmCs7@MJX1#d=XmHLqnRvQ_r*QcD*({E-a+fXjL5Vgb#^At!ziJS?%X1>Cr8;OSiCn zx`lS?mPaP$9kK@aI-OAR98w#db+zck5Oqne%gLMdG!D_RSvYSgN)Lms7YutDiwbPA zo$twB{Qdj*3(}hXeBW)pYf)osKhdfLbJqpvnY0*=eI2-n6aj$9et;=qhj8Sd;?XbM z0Rc8Ue9pOUvhN0TBF_FUW?#ozTI&^WImIuWPVp#d?7qKv-{uGNr^YYDDRo)Z`r)&% z%o~AV*X>Uy=%eIKe0fvAmHjyB$vU@Q(4E~rJ_M#84va@npIh2N%aomhVc+mmvyeA@ zbfs2=FOo-j%2v}!wXAb#OTzUiSFhwvFV?CT^=`RQuSn-WT=V*7?O*C88kt9wjnaaD zIgM45Ss&>`rK~*tQXv$LIX{1{059Xcm56OJfrK!5Uzk&)>dZ3&Gl>_Q?_!SJ1-dZJ6r$X4-;=neKQaq3unPfm;0c#*(uvNMy7G4!^m z$629%GBvTE&Ap7hhc3OPV<^6ui4X4kw&*Pv+l$-zQqY^vFrajt;(jQiLT2bm#-|4pZ?-PaYSHh- zt4;&$C*t)>kx&ls5q@-HJye?)^jiERW)Y^ddd;o(n>|-)=*O2#ODk7saXGuB)a+0W zi_226-HEnq7Taw$CqXLpG$y#^URN8~e#?1_-a1UYR@cuOQ;(C?OrIsrKu>5r)Vic> z%vt*!J#|&hB1|H#hulQ$D!wOkC-*#}NNOFur=nDBk_7w;OPZD;*Bo;&&la267ihdd z!(e30qy>j9KEgew4oL1v6F)CG@#V=cw+guvUMORglAk(_O0I|5SS8mB?~_14nZO&L zg7SG5}2(A zUy?P|)KP(mfG4q&dKwIvkVfL!8xP^EbhKZM->b)FGp4SkVObkhHQ$94N!%Pzpa)QuhxB z(TI8u2~US5ks~Efj3l^mEUy5Fnpzfb{{v3vADNp?%MAcgw z1>-USAGb}SWoT}|A>R$InRsv&Gl*p4fQ92wZl}v2(0G6Xt6GT~6#BlIPGwLSOBRrt zfp9>#C~jy8*cig%##f?ziVj5>a*3j3DmPIbyTzWAdrI~LKzO85wq?d?$lbda3sYH6 z#)>M*4sB|bT1$`eZl#G7bZfW)0}lTjVpLXKFBc&l;pN|Zfk2-fgI1Y}l4yKsJJj>W zHXaY3Z{zWxZ9Kl6iAQ}o8WE;`dozy&wG-Y9>-_}~i{I~)7sTk^dkjeBmpd=B4@GEZ zWZ4UzhA~_)BWjp}U`|5ag#0(Yc0b(j3nhQ_;{eRM~mb4QjJZ%$ER#OHgFSxMJgy{cM^n0rUitY=CqnMUJ7tw?^*8 zLvMdCUpV@vai&Tu0^M}pm%10V)W#Qt8}7FPGcTL~39VoZ4ll{Z0m&N~QBq z2YWA$XQSa_3CuRIT&NfhMDAriD+(_yzkBMZNFxU_T zswUr6K)$-8>CJE>&vY3lSiaIM=0hLp_W@ZZ!1v8w$4c#NlxEQ#gB^T473sV}r8vnl zlq^=X_zi}jXq?Pasnmls|LtuSe7|`5d`JEh`_b=r?p4Uh?v`|{%n4jE_Gx>hmpzNk zQopvj@c{1fXK*B6OPrf)Mg%7DIgALS`sy5WjxNb*rAxfRrirsU2v1L6h?Lb3Z{C?n zg$4sbKL5VBodydsfgqJ!8V=eI+drbACg$u`K>@D)(a^ucj76x~9}PeF98+L9<1NK4 zqV_ltNW3HofE4Br27V87+hgj*Yu}$>IBvhcBdTzV&ZpPjHT2_+f~la^0aU{X1hZjp zdhab3m@{#4KP4)pmxgmU*J7%dR~;#O(~eVEZO0g;nVTL}cfa{n{f3L7-Jx^{dc(e8 zJ;>2S$F*+EFIg0_LTfwMFF{h{>a%rCxVk7NN;Rgsa5xbs=h_Jn1HFmx=fHd~$D)U4 zf>facWNo zi+gYGONPNE?E-`VO66|leJ31c|8@SL;H=Vaa~RV+hbcXqPFKbQ#TA@28XXw{8#_wz^()spDb9-!KC^0tM3vgJf6kR0Q1Y z)^&&#qnt&YC4iRi+pO&c zZ+#=?nxo5Ny>>xYJ{(9mkEGrW=Udt!(k*pXYSho^&P?(ASTS9mqumiq59Q+?fE%U* z^op|3_>XY3^BHj$>`3C^_iudE3Yz?q(Rrs*zNkpDQOJm}|JLXCZ%;n#e^09L8NMcP zBW;WIVy$r@%~f-7hG_Yw7CKfduWR*J)GDZ2d#n1|)3u2)p{?Wt7DK{ky4_~(exI{=4k3IjQc%L#Roa0q4}M{+hkBwen#y2Nu>91yrWEV zHK_<*SM5!)b}nbXsv6#!B3)Qlt<`>84(H}z24-VA{!iuvvSO$$EmYu2GL8bGKODok znR)ZY6K?+}sq-RKY1IQ`$~5PV(Fwo^kMMr!TWBxu?e%OSjh_`!!o}>^4QiuZ^k=iX zU-oTxon`M{lk1p22>oV_yr(=yC(!iR} zdv7<$#O4fHgtn~Y#$0U3w`RrK=av0jE-!-REOe#*mKG-D)=;@@b~|r?@q8m*=jiIK z7D}S>F!P9)v2+Q=*9UvAG2-aHap6&>bu0Z7d;)O~zZJ}Hxcya>>_M(Kzk>vlf18Wu z1277rQPEXVA(GuD{O)d1czX~@Zxnuax7d5T_uf_)nFa1P2X7;(sPtz?eb@l?h<51s z9@j|aCA9PArx3EQtg6NHO1JqgnxV-kh|MV9Ew`IkR5*wN*DP|hYd4FnN_6Gwvr1D% z;($M<^NcpkZGRF>EhGb+JUrZgmC8Nr`{7zz?OMu0r&jGv9lwNW65iF`G*XmpCqhOr zvAs<4iNDTz9$_)l3@GTTTZ7R%YkZYnO{nAEA)h|`BUwtW9Bk}8;A-N7$z?uqFoRzT z20jvt4Y(v@W?@M17w{+a`&_y=L)rE94*YNiyQT~FsEfwTaQ6&=*_tjFCl3D)(^1nf zzEp(mdg{%?r{2(#oqRe`I3td&?a#|T-}$%x2yHFb1Y~~|bEzS3`f|M(^Kp~~XO03m zZp~V)=qy6F-eOWgLT9)rI~2BE*7*tn4Z_%qtew+*0-RJaz4PbW3*)ePBcJlA0yJ-@ zBUVkqZZbBS7r}|-e*qu1e^mE6`~j0OkUC384!#ja(8iqoB0YhV)mzF$7O?i(1IZA# z9VR*)7|-Z5uH;}G!{XV;Zq>In}ccJ z_B7<|0JJ-K8BLCim8uQl8?1rEAH4n7AdMuu*^m)<0_Gf?gd+_G$e^-BYF;~;jVF+Q z`UDOwV+xRIX5^uSx{t-JKVg?A;N1M4ba3*CvIWW?CnR8~H#U~R0=UJ{Ag^tHmRt*b z4r4Hy<%&m%wqrgdv6*7IGNA@`gRKL;4!*)7HUir#?0T#SlvMV*UPB7e*APR6jS7#f zDC``-fA@Cw-V#zCyp>xphhb#&jRFR>d)@?72qp3S6akcvV}n7gzQMd-hxyedm+;PrCqpyc+1W{Z z3nY3Ap$REs26a1*Crb&R;qcOfvqs&>m$y}P(j*8~_l1(MjC@W9szCZ4KB?Ipcg7Kd zDaq$6pW}Cm*Zkv%f4n-5*f~W|KE!zFJdIH+GGDZ9sMj3^;RUdg^ncdi2JMZHPn7mJ zK8>87#eDg_ZpLckEq2v1GT zn!-q1pO#9U3RBcX;S_P!{EmM|YPJr#S^8y*wUVqqiHWb;}vmL zF%L-)#&@-Da%1ew}F`D%^& zs%jG>*3;-5HOcX@yZ@8Xf+kvSb1>FB`vn>DrW(p2^G82u%5HPyuS&QLIhobRcDg#(iKUzJFl2qv87ZGeQ~o ze*I`8)iWi;KU3(1Eq8YCza%3`ELLYiW0_7{5MS^%D}2~jEX0!@4*Uv$Yc?Sf?0mZ;;)f&F@ zcg|`#aUuhVt4cqpO3)j~&*duB>z?Ia<{pl6@%k^3YtAb&;$EFX71<=?JUs7)GhB~( zJOWdCsZSU9{DRwmM8`sAO69nU+6Ez9Z0@0Ogti~9HRc}na_%x(&k?VtYc3~L-RG>8 ztgrP_aJtmg{rEb~i~p1uoq2;elAbVmJyO}GJqhnT4OE>C9Aw#Ew;$q5V2l9YwHs)R&C=1yT?pnWfU}QPvKuSp-qlxaKrjI#p*2AtyWPHmn*suo&tug;Xq6r2t=4eX4` zkFkjDnEV*X_B&TJ=EVC++YEn-+L?e%A{u<=rUNTV;r$Rpc8N-*2?&G!hUX)XFtkFX;mDB<471g)lEvf9kCx;U+vKBd z*c1yks2eP=^_H52ENDhbutR+RB-V&NvmhN=s{gQo4HaIq1GmI@y_0&b!4q(Oj?ngQ<1hi#Atvj z^d-^}V6PBrcpQ?auXcq;t5-`p26#x1HzSG^^^iAJrt>yZALes6?iJhPw}G%=oR14h zEK~j{@I_+u`*64od&bL~?}4v0H5KY{Je*ADo9wCH)XQqU(UMO)dKo#_)Y7VVVq5n` zlpQmVF$K;GX8L5VJH$rS7x25*67ik!=RmZZRS_!tDLjER3gj62!7gzh=(tZBq$!FG zBmq*mOwscDOUVLH;4G2rk_d+XBi;X@KB~%s!6SS0Z)0f8-mMt>-Xst`Z!`kzW)J+^ z>2lN;%RqP5eIW+RNssk|bC0xHi(79(i+ZsWCQg9Aw||5NJ2ZNiM*mUaFD#IPDpNpc zq`Z#Gr;f^a(x~n=g|3(HA2>98(1#UadWsWB@Hk_#i?ufAp+mg9nW0rU<{I&SAjm*+s#--uns%it3$d+>yt?Ic$}#>v&kk4}=J;kFS?IPW%z1wD~18O)nB zh1Ke&pM<6*YlgRBP0;9~r&guD;Uw%17>rgApD;`x;)uI_!lqSF3GkW%jXR>x<9S6o zf;SRc^j=$m2<4I4ncs)@(fDnCPpAy^j2;!?=QY5EG{n|pBcPHZezG3E5x8d|@dhWu zRBuOI_!A({qtRm)BRUipV~~dn2f6(mM;*aHz{Bxy{&|A=oLdYR}RVQ@i0RHLP<6hz5;o)#q{lwcwoo!dEPp%!|6CG}dy-;3~ zil{7CGRWr&`}W*qy{AnrYyd_;xxe%&Vlc`ju!Y~FHeS=3?}P6HnO_bqn2Z_zMLd(c z`b->)XWljcnz5=%P=s~GCY952H=$V?f5}jzbpDWj&)^@=GU0nUMl9hKUtR%uq3||5 zN5Ev+X_#7Cgtt^&elBxA7+~9*(<1`)Hkn2{j0@LfTPXLtI^Z}e+#C9%ONe@lg$Ke9 zoF_6|(=j?l{cVGtYus2515NIx>Jd|JD133?-40yj*-(m_Vy1)}bidY*NUFKNfC=JT z_MkUeB>RKJuI|YlYxUQgO(4gja2#17lV>1~<76w9AYxALPC^&6sKgo?vZ^HBW}XAG z@xh_x=<%BKVcEO^cEm`0X$Rv>hcUZAE3ojQ|ss=rW1hrz@`**!pl`EfET0#Koo+WOfA5U z)D1`jybB{nsg?r`0Ykp!*^|RApxDyRM1>~mY+XaDL=A16O=uXY6cw{f(o}zor;cJ+ zFDN6y%>;w{ItWqyZLub4k$iTdQRMtXH3>zgh>agr_)Aq9)fC_NDdZab_$xc-ecumn z2mQ*Whsz2ZeCFQ@#C(h}-z!@4F<*c~8HNzJgTeg}tE6LNFzWO?02#mi(fXua)v?|| zWnF;?WEk7fPQf9>0QT-a3ak1kY}Uu#sy_CjeaMJg$1%hBaMvY_aQ&+hz=SQ*$p}=T!1v@n-k&#D^$`XrUOA0!t8>{}wWu&=R&qb=Jh`G5+kG>+ zqx=QjIYnSre=4r%&5jMe5gvb-pdLgK=wnGlV*8$m)N8mD=7ZPrD3*f3kv$btR{%JA zc+db{Nh37Ds8V=ihOe0}IbS;s#ncHpyBh{U$ABa+}N>dr%qL%xUXzMaHx_ zTv7N?80N8s(=k=97DfZO-ZA~o30l4SfVbWpEvQ8PP(6w2C(1(106%=ow8mV6p^oLK zyfY46h|w$tXP3pj%=(jl`BLTWE#xvMDJxfRZf74|cQ~%-`n`|sM z50uYH=RHSp84L$gin&SZ6evN@v?(c;VL3tAd_JGfF(xbp%*htXy^}>1w5pdGpG}PI1%o6B|qWO)h7+e6o=NDEL8dKAd4_pdQN1m^O$v zKZzLRZ3Lx+e&rHLQfRM(76y5;C)xCvbNlG&SUWT~W0I05i-tpEHF%IWnZRcbj`MEC z*PVOHx?7(&bjw(G0oT1(_>6VC2iG_vB~Xms({5w9KYl8K^YyB*zkdw3M-Px3PJ*ie_BEfV56zsj_v->{6Ed^?@?hM?;v`=;N1apvW(HL%1w$g0DH>3Rk#C> zvRh)!Kh^dfKAL_;+fM1r+Q#R++4e}gwCIb+GVUG5dj`k4^?pZ$H>7d2F`V^$`oZU@ z*yCle;O+E9pPfSb(6IHs+$50~gyu*2Dn{nqzL@xaU#X{u^8HP=6xDXAT}3)cE<%bz z-Ya8pY$}F}jR+*>=B?DuwfFa?WD?rd8|6y7)O}ZkOR`*TEAD86maR&u@wU?X#qd<$ zpcSp!9BKWAI+qs7afIL>kNpWi+fIb4A|>qz&Ovx?7Q*)aed&#cgCSdppRrSOF~6I? z$dV=lKH^`wiz<%LX;Y1)A`sY#Zr)#k-_Fjy=2XWcCV!kN6 zqoI1?)#3ip3Dvf%Sr78xMMULwlPKh%)96sZ2Sn67st3EV)hK60BC;;Z_1Md~k_%75 z+z#bc$2etZG+ueTAvQUN+Td(CubRl@(1Jj>HXC2HTcMS^3}P(OdoT8LuMaa~7jL1b zm~~{7xVq+;)h~$oIm@j&n~9TtFLlwKH+ss(e(#FH!Qp50d43-6G(@Q{>=mgHVd`c? z#U&$AEP4tEx55@Y%+#xy@9?fg9=|2CJKKK~$&1u`TdcXbQX{-aIE2w1X4o*MzQXJT z;5uE-d&vg*)^i9I`FGvFFa0G5XZkn&ETX=g*iGgSs)_zhH&I_AO%}SzBGzP~ndpZ?Tg+J&Ro$>RWEZ}}5aCC50O79`p2Agqo>*Oh!m6G#-F|Q~7?y4!czrT? z!L9O3OXBJFqC8#Y?axs)0e+HRGe5V}@{9CRQe_#tI+7q_xvS-@2xm{OAUnhUI^r3I ziJ1Q4}Ib{DEnSfu*C_i!we()h#x7T_N{q zfhoim1t!-VV98k7)tkLX>a*Oa&iit7qjW=5a3W~{xP9>F6Cerlxa?Y$<@Hx?HJWrC zhSdJcw^Gh!?RL&rZ6_U6GML+pYx)bU`IuOhjS!)=YEO0H8+V~&!{;kDytQJ(TkZ{S z*&E)9Z+Pn|8?Gz==hfv|Bg<6KHNpCNEZ7hUT1iO2I53~ypdPzg?pMIBU0dSkmyv6M|&pB>V5wb%^E*en<0HNej{s7bh`0Bp44Yqi;Xk|Ys-^9aR& zWG4N4`^Jq^maJvz&?bc5mx?8 zTz71RPgb13hB5Atkvn!TxO;8T$=ks#vgWDCm}@pCMlR{5%~ri}S-Cv1?X^^~9wb7<4|kULlJ@a0;@P#=Cx4IWhi8U;a1cnNncfGs6d7Xrx4$okD%dEYqr`<2 zhv#DArOtKeyb7$%Y`Nef8l2BPJ468HXl)vQ2}uK@y-6LyllJL%xx^Q9ZxUb>?0>~A zt_RoW6u=X@cSumJ;6&ZJn^Pg-KAR8k&`*H=B5*(bK_kK#WQ8KqPQF>z=X-=6DorxG zaFW@Di_BW=gkme5yu?7W4L_g?(WaJgy|&d$8M+-I!UOaej%YYB^6<$_n)fM(4b63I z#Oj8`WwDkMSgg8kqf}uLQUjc7gd*S}`~H6{X94$3ACo*nP26-omTpx_;gjVI)_Bhj z7qKgL@nAzmvIA zpiOL(u8LH`H1w@X{z@i#nQYrr;pvL8^R3k(tnB~YH6k2v$Wq&HYRoHh);`h52ry)> zTn(%3R?Mh+WDETGMgrAx)Ir412ko9oYb&4f=sl(MlWh%=N(@w{pI}Nqkq!ZM2}OR0 zO39dI*2H2jQc5}euF?9bTWmG1(Eh#E_^CoMzM#Kixve!6vgf3*8@J?7$ZtEzbjy4J z!dutBTZ9t4j}Cr)Kg$vtQ6$X?YDWgFA#sfy=(&x$Y=5>4bg+A+;qI2CXTITBB-@4_ zMe5xeEu4V;#wO^lqUN)16wD&?R;%elhrt+CXJF`VGp3u2F_qTmPo?FU3TH3cpqWzp zaWi6~Po@3;>W0mqI^D)kl9Wb-IeX0}lW#6R?k1!;3gsxon@>JL%2P;fqzT7QzY`Ol zo=rz1xcf5Ui=yDeF#mlBA!p`;Nvz_;zXHqs?JSiXR3YlrZ9XMmKZ-ucdA$Y3V4hAt zC|3}w!}i~4ZPtq)RiI}w`!+lLyQIH>kTM(j9s1)K#zxE|#;uQxz`WM%mHQM@mxXp@ zj^T2JmgAx1*7qMc@T~G#2lg05`DdCwo9FJbBc3UgkIX0}=yWKJR&R?nc~<3%VJf^B zB4k@V#4pSoCZUf?NOwb{!Jp+b>I>D18(rdCti0DI{sV8sVkZo~?U_abC#sDE@MwYLAC<+UhM=<3l4SV_8~w?!G8<#G62%V#=fDQ%WzP@wEX7rOpLym}p zYkqaByl(XbXhw!8tQ{i%uerRYfOosS(KPUX$GeJ@asSIrWjTOj7_50nk3sO`hqwwC z(<+;hifsMr@HZAF4Had)f}}$3v9#b99cbYkXalL$(bClNN1jwNA#%GDFhuKXscZ4X zjuuHBMF{!Yj-vI+sK_64Bxf~Uq8K`=it*$~(9qQv?&^z#>T2jn^^kf*6LJR2>j0A+ zvTBm@DU4KD)&R~9X5RKl9h5U-ikY$HPZN|){HFK6{Ujm`y9<>sA)cAy*49vjwRPh@-=`B_#4-`X1)&2zip{5u0!$$Atr#V`)1hR``i)jG-NOnx&*k za%NT}>^Ru8{rD=Fg_?91)FS8h3!oM;5jQh~IEaazcQkQ@^G#MbkFU^pS2!8+H&W4#?-N@zRfFKc+}~N2a`SDTbFB`38-thYK_G zc^Ht3TDa2K&3!A8-DR{Ea)KuFU{nDY~lZ-Du?dGg?xCUiR;m&n@CV zu98A9`isQ~gE0+JN@h{aaF4zBI8r12IRyAZ>#!8lA$ix+rIg@E!VYA}E!;GaZSK8C zDnvmpp!s0_jZB!eQN`fH`E-{v7j(YUVU1GgOFEno<>zIZoA}?8@B5e6Gkj#;?k|D4 zc`(cu(&jkR8Q-7JMXg!vl0&@si{PKfT9jVXM7A&*tZTw8(B~pY+JVdllPg~}YnAun zFM5z?gXL&MkK(u9Xz)z2421KIa^skVZ`xiwO+M?R5TB$#j@qJhM$BQ~(3zLEMzV!! z6Ks~yioTOG<3(D*Iik25NxirtCI_8q5=6ef7AvG$QTG9TGTm~CRUF3Z+Wt}Ic+=&P zR3PfRgPNhh^^Ns(8**A4Oi`yBPFTW<tG z{Q;_Jl%<)z=M8raM+}%yqoP;l;E-3(Zl6|`Y)GAC*j?#3kQ*wH5|eZD-3Ocl zKFwa!wM&)*I)pi#ZhJ7}ie0_DLkE*_c&t*kt~o_^M%Q&`D#CC?@iM|d*hyVur^`&E zsE(of8&fH&P#8OOJi~vdy7nZDqp`zeDq4xY8nv{2j8#j0H7b(oKGq$6H7cPhE?x<~ zS{W`)BRX=1Nv}**kxYe7slg2v%3Vpig*=;o49)^alT zX7P4m5Ki?;zo37zhnn0?G5D<bgXr@EM~1?4D5l?)m;I{SJQUiM z-rCR;(HSwg5)Ejz$6G4w?Qn|cV^^IMQU~abl}G&Ao6EnA#4$2d6o*CTa2W~<%x0Ju zVs)$NK^;ypIU}BG7lwslGxR&zS+Rv?tly7}yo|Hk$AG?!F)nwXQ#G2FWU9ZcHA;+* zNF5u!AyDwqk`;m7>;G*TEV{-y3`wB$m7U6E(cTWHGSBBf_q4Hsik3r>psOXJ5(^m| zh%4(5Z@V1Keh0Oakswa@tx=5N*IACdKj|A$bjd#M_EAk_$BMp+R>T~9PO}G&93AUf zyLd|#B76hZ4mPMa3yCNQL?ao5dsR^Gt+@DNoF|&lN)n#L%(2o)hh?8kC);<^5zv8= zO*s;;oygFUn@16hu8Nvu%qpTWK;h5i@?sJD&nYzI_kpM&sXh0A^XuWC-v?pVC}}S- z?8E5Z*q0~9P6F~5@vPXcmPowByid=pS}N8N)XL{Bs8aqSQpGf)$~ZViWEEFmsWz*q zSix01K>|yVaQ(Wu#|Zmq->&MG3V!{Kvu~4yRO2$8d&s{o?4v?8_bRuS1>u}9AHe*~ z+zF1G{S4)OSIc}(x4h1|+n(jHwULI6CJ2`Y+_-eT4yd;J)a@GOGGBNQO>qXFfO5hGBIBrRH3_ANGm~@@RpYw-*1I`!nEW+Fzb*JaTrAM6>``0}d$*oH z5@&NXO$0i54&U34aIuX8QCsx4{!g|4f407^K?Rdhdfk}Gh%iGm&i6xKuM_b&UDDgf z-0#CRJ0D&z7pT5R4sdshmWdd%9K}_Py9`8@p9GMV43y*8Mg2WDLcAEwb(o zk@l_sU?&4#{6N$J$-wQ8##gwQ@PkhO`L0&1ci%MHo$5LN4!3St{TXlPPVbp1h#Q+6 z6~{?dhW{kz{vD0_5Aj}douz#VFcHo!2Tx-yi1=k>g^m*HXy|c zaoYL+7;zGLk}3=y2YSDE3sYuI8Qzx|FM-q6lF6x6??ji*|2~;YS7sq3N2CBDfj|p~ z1;y;dOo*vCt1*~K*vP73H7auG?QF)&7{Oq_3?8a*h=yGcdq=O3`wFW+%ch>a@#gTi zX#3+KzWPD$fOZ=8z0q{yBU6>w+wJ`%|a{h0xcaVGinXKOi{9j@J)xlnb|2y2u^m@h1_vcTEXuR@Ou_tu*Yc50Tp%1N%%%yo5I6hplabYayLsTLf3QdP`M~p+r=#Y zr#Wd-&lUJxp!>)U+rL`8MzDZ33!Bq`Z=>OL^c%jj+28#6d@{8^5n>M`AD0uj{QHiI zGC!xIhrqMGK4VDTK2vm>{-&5`0cR`(=4%K%7O<#e9{|!v77q<6fIuTVhR=1QC39*R zNC5lr2Lm6I#`vR0!8{ePClhw0z~DaQfFl!w;tnuKcZ5LS1{@GOna+ndL(DWoK&JU1 zQ%ZSeh;zkzA5)JiWiEM?K8osYL#Z*b8>yh2dy^Xh7*8thax-hrlFON*9Q(77rvMh!~#@PvW6I@Np8Fhe~*oGq*k2Dr(!rBgPZ^j zMvvHf?r)nNz@O9yN73^Jlt_V?19DkdgeS*j4HtlefGDx2$T^5snsvrM)H|VUY&j=m zC%^@EF{K_esfo459`N1Xjbvp(q7+4W5JIiKoC4LCUn$%73l?;sU*iQpR+eA*{S2#i zJPxB_PrUQ~pKq+s zcw~A#JSm(S24%saKkBQTxwIKCaG(>Zg=1ptFBEtjQ(-@WzuwV+sgwyvS%B43sixg% zHQ{4E8J5!E=|w zRJa5=_bu6(fYUp|tUWr3d-NbV!KjWyn!nRfKta%RAoq|18N@m?k=N!(e`pZc9Zcs~ zUDKGa(J@QC`f1`eCv`l6Yjb_yN1xHR zzBagLgQ!G$_i^TFRrceLVqavQG*Wo=b%E+wuwOOuMXhmGtZ_!2tg7%*RGQsV(O64F@uZ8PIelQ=^I;GiH8T zZ&d0X12fv_VQO+$DiDJcL(D_YxZ$Ljnk@)!apI$S&?PI}Ec=VOw~*fW5y7kFHYUMg zUX8-mB$H`usJlnL2ln-pp^I2F3zS3;W46&vina(x{^18ocavP<)cF0m zR8aoK`1b$cbubXe;is^-PvLgG^95YLDu+Ag#lzpoyirc{W_U;0qZGj)Ns@eHIUPFo zbB6jVlsZl%Gs}A(LKgEb`fT-%jI-2-)1I7g@;x7IFqG5)O-q+$X={B?5(rgdkpXzK z86{yHGcOHe1dCvY$QOt)*8_oMFHoHstHUH&KBtD)e(SWz{2?Vn35X@6=xRRY!opf7 zO7@cCmQDs~$b0Fbe~i=uqfrn~Pj2uiH08uKKN2We4|~$3c`Bs|kps;XZP9#gxFd=S z?X|5S_ShTwC!Ay)$DwIW$1@Ku6pr15dni*Kf~;_YlQKlO{t#_r`DF5-Kt*W6H&b6q zhA4WLDW8_y0Fsb0JEF?nqI`zHI-%9#{9$r5E(Xja%0Mi1B4x* z)lkWAvJp8&;M)h#%`}WC-r!VEKpbNs%Ik+ab;A}Ou}SdmA%L*{j!AFyO{-2Kxg1OU zNYhPa-iqXrn&n<)WYVNdRW)P6Q=Xy#37w-HPHE6ch(Mb27x7MsvKZT{=M#Blrw$fw zC=z>`{3bF^ci8-)>6a>Jm*#2yKvB<_SDDgT`zKaCN!cr%rrI9iJUTRECeXFJjfN3jdiF3i0B=n8jUaB1yeaz2~~oV7^}k(Dk# z1bYuNzlp}g(0K5CfPzT-$*lM=B%(^(3N%Bsenbt!4S=6FPWcP5m#`(>6V#X&vE>40 zg+2)#UMUr4$fYO$Cl#ea+tmSWUJiigUC!ayQ!X0xi}FKygph~n@?t0oMAA%S2endZ zwi=h+%kvB1=y>Dx@BfH0O@VihCR>P%^bk854ty&Fs|}97h-bk043=}8C7EMQrpi-F z1ryNTU`>BMz^Hk~O~BB>-y{__3I`2$2IxW`e=<&lDkr}FZpxpwR^&Ii)%fQ+X4UsiYlLX&iK(A)R9 z!h>los?T@sx2*m>Z~R{PEo;M^gWFHn$>I|pS2ygY)8~y3$~`xW}PM{ zf$JxS*%*;weUHMFsN~M2N_al8w>4Bu%Q+ynl6ODXckL_t|m)m=AMQV z*SjiZVFQGC^?89ooXq{@m1XTIdRg(r_=V0dJB9jOoZDRb?ep-`}LY^F&0Izh2Eh z@1;`mGR6rn1A6gQ(U5@Rk>4n?I-Y3?h*Z$*4;Yf_AgqEfEqD*p^C3P65^foe)kG3U zn6=s|heQrx0`-%$q}ZqDYwJaeZih5JI3YR7Y=UqIE8@oAkZS#W9hyh&i7Bi7>7=~yX{7m2na1g1=t3{hc1$_*4iop^|kv=LNv zztOItUef2n@hoZ!*HgAXv}I`}n=p5Q0*RFAZ*8j4&owN7KVm;h~WqMTDps_iJPiAAdfVLaGsDiqO^$ zFlF=|V%{l({IjHd5sLY3;)mE$Ky&X4`S87t=Z(nLl4p-SjHk2%-AcI`T?#RMw7F8v z7mzWCcuANcyCVhj03h?j5BTn7!Gfg2s1UYZ{E~>#@3UD5p~CIZpX0spC?lIpv~jN{ zlsn{P6>gbb5XSy^h6PAPkcOB7CRfG)2RnF3$>io#oL#jke`yz(6i3ZFSm+dC!%7B6 z4Y`6-#qiiwP&-QwLC@vEmwv0M6uu~-^BwxIh4s+}3w_tJc7vVX?QjH}EwgD<>(x%T zR&95>Zz{#I-Gg0Is%C2FnP}a}5j~4}Z^3SxilJS8Uq_;)s3K;qx%bKXh=LBI&H3CP z$Y(2?f>}x=dWKpU{hT1nj?r%Y6x7C>m3QZvrBoKU8UUDgISA@CM<%4fT@HOy{__;T}Gl zS}cH!h`qt4MfXC5<`hoQ8Mta2wKJ%=kf&pOVD~)bPG{Z8mPehv56Y=7X)2+;letgE z;QXO${l!yZkMe&ACRC@=!tj~+gwxTJejZ&2&j@8FAl%3=akH#$4*>i!``_DdX8-$Y z?;szu|AkL~*#G`+_P?d+>~TK4xdn!&ml65bulK|oe|`3JD?8=*DXD;4~Z{XE^cZk9g6H;RYi;BiuRCC{w#h z4LRIqmYxA-=x@J3RR)~UyJ4Rm%%oZ6Uvbj*}iZ#Fk)}y4afd`M-+BQXM{(a zY;^gp)?31o=fK21V)v-j1z-`wL;M5)j=bQejZ}I?xd$gFvgRMCVPlE!vIq&bwl(BH(x6P~KL{m9jYd1qv&oc-48+Xo>&# z|1Gwm_|Jb9XjD|J{~{{yn;3XPKwfvuC00&H@MoNcvvr?+U@38y|%)Qw9C5sR!HR|ok&sR_n3PrhiS-gOyq>~{) zum>OjxJy^9$|cUPaW2|dXYEe4bJeMci$yzB`$W_l2=8-o)vjb=kWLZD zCV{ur0N_9kT+~&&N?@wiJC#=Js@bVF>KXB-@veg2OQj;TE)zT(by_n3q|y3C!X4p3 z5YLKtZz@pKLMX!WFhU4`qups$OC7Ts4B2V4I>utLpL*q@R=udyOBJlpz>eNk+Z7-m ztt!m#0(-^5-xXoxD_T2l49tyxt+ol;5;3#1gQ9vaisiReoWHCO+up896d~}H-bm;x znlv1ALA>#Ac5btGKnwPf%I)vw^Sk*ykGW4KEwce^0#kOcqXSYKS>MVw{DNv29IG=kH zWcU2-u{RtY3-1mZ{xzU_JHyEw{yUg&q2}PmpR3}T_Yjsv944NT8>aR{AXi$ryhDm0 zwQuNEtHYSEQbJl}O@ZHF+fj~nI2=r{o^P1QE3%{j9@oC>ZUUHg3syZU8PQw z*$aEwea8T#*Vkh-mn7Y}nFur56=6~;o&Vv7{6QvK*a7kUvQe(c3j3KY^H{PyqqbEC zZVCRr#myXX#Md#oykOkd`PAB#VW13+`NL=b|UgWmXLK=&|2x(EEAV$Eb+H8xhf_3n_PLjy&_ z0I@CARXgDnrPT_V@dY3qF4(<^{hZ*IxTk4R9bQUbV56O{oChL~gLObLR_o;uqv>p+ zVOkvV!` zta*m2NK#V9E5ilQxN4Ou)BuL3@O7=9cf=V!BIOI)`2$HczYXGgY@lkq80g0?Akk8r zAZ3W*oXizx9x&{45H0R+eR@ssW;3`>knSinCr86;-{*MS6Y}FEGXnS@hchBS_-eH( zn%*3r#YQwGFQ5!g~^fLGex%k91{JR{33 zk9r~_ki8xo`AYYo1-s^fP|~0FmSa?G%GAiTRJ1Z3jnRyCI9BeYXvZnt32CA8-YtnZ zAb|~iHo*%m&DrPgcCnZp@9y5;-|tL=J~rJ!l|^s5SWG7%$c=X4=9-Y1FZ|XEcFCRf zdgp(JGh8y+!^rUD>|i+?dH8rEZL;qDYmpAVlzdGcU9^jA%lvANNGsofUmQGk~e|U;0Ty-o&Y?nfs@Bnh|Mb8 z*G!HhW17Wz9yej``R;kEQi1BJIEbki1EH=F2{1~eV>Tdwx2TerT&ZRCb`&qg8Mn&r z1%<_V7a$83b}jgUQ*nTVZM(R91yuZf*9qAI-!um_#{*ZM z*jW$K{qAL@TrJX*TKt&q(*bKK)QDlmez#ufbfG<6{gFeu-^JC3rTdXmj87Dn79yp9 z-nuLb;{#Yw2;-I?EJq90`&Ch_Mwff0pex~hKsjE!IWlpIgZu!~%eJ^M4w(Z)`txe7 zBahZmywsAYW6(Lk6&BwXt2LuaAzp>#Tcb)87KgFJwaL~)$DKx}SnD=^ieOtSQsS9y z%9ZmY}I&+YO#7da-yM8sZpy{+tr3F+c(RK=K%I@ zKCEM(Y+b|Z#R4x>sW-aqpDU5$jRTjvHNeae@t($HD$Hc5f>qK_<5Qv<1HHjJr0?hZ zT=j`QHprstIuvOL{^bw0^LY^Z54N$GbnEN~#eHJb5EQoj-g3c@2HYyQojb;#d1~g( zZ=_%44yK$}tpnDhpx5F2COCn{%I-=fow>%8GYc8ef_yS}EScE!l?8d*_8QEPz97e< z5$0_1P|xH6pPC;_rifS>mI#MIu#{FZI{cX_7dyJgcvT?b=R@gQp0ZZ4-Y!)zvXrw? zff{M^4sDwOz{kiF;mpFY#2_)qyfUhcV1#Dglst1sR0l?3Fg@WkHAlIeShoG1Q6>{R zu(dmd(%&{pDbw)WViPe1AIKADwTc@CdwMx+i7!pQ`oMV`9nU}v)Pf|th0JXuVb}WLdJ_#U$KDe$O0-q)6L78{rDI+*t`>g0MN8jW&K3hli}12?o`#&=1nPG2+2w7ud{wNuo7lB4T8*owyYqrw z^Q=`a0eN-eja{=?tGRnTiY?B%Yn{{0$AJ>PfSv6M&UV(V>}xLT}4 z)BIty37nX9L@fV`8-@1s;X`Su`vOxFPp0=k25wk;fdTKP%fLv^yTck$*3XSOqcQf? zH>kZJpM2$9=8bIf4~Kfi894;v7cQ#A2c%Z)lw6=zU467rveYFCat^zD)vBy+Rxq0p z=W*GrRXS^0?MGS>CarFAWHxDSI#50rX-2rdW=T=_bz47|Q)?DvwYh4RiR*8#SyC?4 zlJn!%N@hhdCVRkRNd9%Q9AEqMbZ+}qCgeg2$MjP&K;0pQ(!W7@F|3p4I;P!&WML8t zUi0^O!BJAdD_L+g>*M`sbJtXIC~H*J;Q-EnT-=Rb4rHCydr|S`qeoq^9~P8kGFn}> zC(AnTMN#S7bYUSZj5U;C0r{HM=9pSd_I1KQRZ zTXzq%!?U&aDzMH}!aWzM&S%LzWvlLrGh`o~wsCaaGqx&|l!-NMD1Nk{^zpyhye|aC zM{BVk!?jr3!rC_&0hM4Es7_0ON-jLcq_GIaYZ7VR%fr2+Rb#tNd=1MTS?Bs#B9_jq z3=#u%=Z|NL5bg64?*UUQl*n;7!6BM(@%RhIP`i)Gl(RsFJ?yb;(?OG9xQ2A86hp}I z?Nl`G5W^={K|T)!t>r`wSBW;wQ`6!IfJ6#&M+NMn)GEb`a#Yly*v%`XrJ1IdzVe1dbj!0W^A@>Qmj=k>Kn1zvH@(I zyL#VQ9allN37d!&vQ4>FJxk=-BtMya+B%7+?M&}=%SZoZ>8Pqwtx4;WDf(!sk;p7IO8nDoA^eS<@>vTfW~cr#cdlVqF-g%&Sbtt?yu%?wZ4~F6Wc5A92bfPsw%$p- zCP}o|Ptv|t+uh<-6|Xe1wAjm-C1>@LE-6?gSDmIRd7W`TzmR+d2M|Q?7C?-2c!qip zpM&<3g)*5*Ug5s8Adb|#d0jNu) zn5T=hmbc|=>sTA$wpkeI5n4Z|hnZAbq~QkvPke;4c@CF5S|(9^t=7+CXxJpRd{P@J z(&6qKDB9UCol0A39Gp}w_i8Y(bU1*1jTT|7VJ~H0JBaY1I{R`M^mM3Q)Kn_DljO<3 z=1N$ERG}~$m2yFa1e~=4TDv=WRS$&e&2q#Q`l4wXr z|1h*a!D-6Ut$Nax&_aI~Z@KH=Xy5Boh*i{pU#27rJx~blsBYo zLvv*ewPQ|WfDyxbWN0)AZ!qWsomxU)Y7P?3)|qs_DC@!YW$AVP;o(8_ZilnLSK+*Y zNew%Y-76zTHR`BM=y)kXegn5ELf~=+M*uyX3BCG#zrXodaN94mOb@%#AIb3duaCBK zueS>acJ(OiIXf#Not3dGZ2xL?dxl#;X3g49lbk-4cespGwb?%jY7N2dul5hww&4H; zs8kov70|Wro9@^*AxfXbq?5-z{sPUy*!ex+yf9GQBL-ZJl@i|u z#TYcwQB0YlfuBmH`dnk{i>bIecmmzj=ffEXrW+`tVi>G)?z3y)yD<8ufbr2X8c?5Y zHb>I$jALY=1!j=dy`YsgAhA#B5{8pIZ?sgpWh*)?w?LvuS-SQCHIR6~e8PEPuqaGb zf-!?AS}2>0`jXmmYP5T)a`2?8KMYEHR;>RdAL&ie1G=qpI4&~K##zf+U2MTLTpI1d z8Zw5_x11zSp(n*vLUeXf#3p3!I$Of^5}+at>x%+gOOE(T@D}0gJT;MsglYqh{0Lk# z3wvJ;$t6pDhO?2uCzMi%J)d5rQjhRY(0`C)ga14{(D#RjRO*x$!T`2wr!Y*!-d>iL zGQ^}*+M!d!!4j@g#o7j#5j#f3j4LEh)kz z+e74ahyvYRr1g3Es#$wamPc$HGMD1(j1;XhY%y9mM;f*}O|?rJ=R+h;1n#`Q4g8Z2B&)RD%`V%dZN zd*{r&ejwdT)I#8Z97V|;@ye5`1p-0#RJjD$|AtgPFiZMZMT z(V5j7txJ6A4|hp?6!w%sb~Vh?-YBC$!)C4BtyKYc8b3+FpLUqxf~l$L3HfG@pq+jY zf3a59LS!<$(s0)r=N&e?U`+wRoO@p6RueS(FfCCwI<71ef0d2DbPN%Art z2E8TfKE`Oa#q?q2-nJ6=wq@+C1tHkp5C|D-MH0gvauolA;)t^;m!J?cI&7yNq|~^o zlNwFi_N6YX@aIwZCvI<^ES>WOjy=t9t-QKjrHR^Y-aewHk*J6wfe`|vr`jxaTa^pV zh7eULTE*>VrBc?#1*Dq-278C$wDY3*<`=#M@#W_7ddBeHv3e*2#R&(AGEBs?3fKTBJ|JbxfEfYx!nMXWbC>-^}27MW&mEr~0R@r7pEo%nAVOu>4GyhbQ!-K5LmjsKFo9;16o> z2Q|>tK%VxzdD!_?2OnX=Pv;l-sHg=OI-Z71)2={u&8w#HaR~UEkmaN=`IO1S;Fm(m zWMeKW^-2p^DdJ^9?x)^qHRxm5f2mXJHXH9Mt#adConL5;>0oKCqRm0gr(Y@yxl`-W zm&!V3JJjNbAb`^1Ri^=G2ROKPMM_FmYw56yFUxI~!}jp;>~?r_ixNe zn#+e`A&Kk8A9M7+Z8GP`NA}@*y;>H=0Igz%-%>aD$n7 z>TfO`4_J{f9f-ogAv?4=SnSJESBVe%Is5Bn<1IqxP|6e(V2or6#B~hJ`JnLMo?r;r zBeN+xX`qY3X5FAK7uvbx9*QzP1Gklx0Z7eyD+x%I`p;LDE1W}#V%B^5QoZTv-693rZq}BMv2jPqZ$zcM3XERisdN! z)A!QbRHSeEjtE?>m!eO{s;J9l7?mO%8of}_Q}4+BQm~Gx4#9)RG&C!yclgaVANmhf zFL8Egcws&Tr{FUMS?QRs!SX8sM3(KFWrV=8;K(c>+oD!ojgm9VP`Z}m*x$ox?bDk# zMqrsv_{rgQ!8)mX)=>azT`d*coxFLx)W?EB7xl+IgEZ=o`=*3>Z2tt5 zvrYFYqYvI_9OB%9vny?5q9NDTJvG<;9?iu-|Mr#Tj==4ZILm z@SW0|YO@Q?a;d%8w|zq+q`n=b4r1R9EwAYNEf?35bhs$Q@Q@`v(!E}u&<%ZbgQ)dsq90jx+XN}ZwP ztXS3xFsqJzLKF~CM&>{5R63j~a&>BQ z{JR`f|3Twq{vZs$D`D6rfm4H4dgY_-aIIK>oiF9{`Ov8vs%CVXm|U)}>Nmf1OZ868 z+7&q!N2PYhu5_fN>ij8BE$|odX0_(oPgb*iyIE~Io#A6y}MGk(w zoLvX;<|ys}4;Ik^P;1_HlH0{Z%Iz54G1f=3T#o?e$eMf;T`onLGS-_ir|tlExm;}< z9qj84aG&=jv#pMf^6X4lPPhk5pSUBokF{3gn2M}iT{lE15~hM zpaMUS$bR^oOxXxmbo^kUu~1syI*||`C>%(bk5?)cfveqjBOxpY!x?QudC!|!2itD{ z{@jU;s{MO0KydU~m$0U*H%aIzyHa#%eOKqgp|HZPNVTx}l-{_}Rra&-0C+%$zkJ$P z&6csn`>LP0-@6mzn=uG4a}QHSN)K~1R`k8Gvp>S!V(BNNzk=$Iv=aDmtu5hQsBzgn zuXRk6%d7SnLL4}tbBf*1H-oDST#}*BledAKf^~9|!GdpQ5i$w41NU*~jWC7HaPlGE z?mvrXQSu7cgS9PxV^et@95>!c<-K)4T|zSe`FKzn5(*`b-en*+k$-d}d?-i))?X=;HGCtP0DiOJMihc40y zF?T>zk-dv|boNWLU1|v@XLt5E^8x^TZ%!BDe3uNx`1g2ESM_$O+;*SgtBGm|)N|yM zz{&Pza*6ToDnHl$tCet&0q7Mnn)#oVKq`@0iTmufJI!?dAUC}oXOLCA!asBLrvpi9 zZ)>@PGYeB`1MMC4ZpFyI^U)NkzwEIk+WyW#|2p1X2fFLF-&D_;6ENPuv@$@-bv5d- zNYG#2a+K!lZmJXJfcMW1&X@sG(?KQX{2|s`yImHE zvUPompq;~c9>6asSFG!<67O!`><(|zjXP_>VM}DIS{MQq7&Cp~65$@<`)yLJPFYgp zcC%crbP|ZL!7icRfz=?AM~NvSus7se%Ys$d6p;59oqLHZ#p9y8(}qDu+-#A8-Gh?iERgUpGb9O?=_9-7jk0>U48 zkJ_V`9G$I@yqD;AQ!D3bz}fi}x+YoU zMEFCB#!Mlefx#)LQFQRK>72Vs!)*rhpNlEUlo?Eo0Bq;DC|Ie;mS`pRpB+#mWfO4|C39KJe0eku^fJJ{M1u=4X^{|0`&EDE5H zd#|(Ns$H}EdMNo3B+ZWTXsOM&aIMiIN}kW z!0M+8%?oc_VM~S2$jPS@K|7<;6a$%rS=D@WJOCYrT;VXL30^ z<_zjnSZQlI9{NiK2GM{i3@4PtyziT5ScdxuR8`3wgmV9n!taz+ZaeTD~kUq{uBR(CfurqcP~a@M;O0V z2YcsmCx5WBuWKpyHu0v~?lfAz$Yq^I{wWyT<#%$a+}?KXa2u&&YB3xW6--r;0|(F` z9YxIM(RqitDgU=RL5;p>5cLR-fe_H&+aKm)dw8_HrJ#{I~RCjR_vewN~wOht`0%@p|5k}esD#`1T ze59nHOA3*az1!(RqKK@q7p>8oj`~)OeY*xZb+dnysxU%O>vXx&~v&_l(dlS zu@5vUnyn07cfTK zbhuZfb_U3ekAFHhID$Y19$fAfs4n+Vua-nG^LrSgOob-Y5r)Ohl;b-Z0DN1iHA-+* zb>U_zUZ(5Waz_03mLdbPhaPML83Ks-h7J8+&=FoP2uU;evnl&@+P z>=U~b&+^;PM7JxyVg#l7MOt^15zmItP}*{|hR)k1?4k^!7aq09d|MA?8S8I42SWF( zt{vM|AgO*>4ueG7cM{uc_wPst;qiJ~E7rT!a`&>zD*If#q{#0$`?=Q#u|fcwYzA<9 z3m;@qs$YM@tB-=gQt&*Tg&Nt+GCcuSOFLVf=L7nrw`C}yPx);aYyViLynwIBBBg1YY5YgT65^HfV8>FBmNfn~aew>>Ew?HfVyOSbgyv zR}9Qj-?b$gK(HY#ClIWSDPI=5wMzY>BX=g}EWw?5VXrOpYzTTPvzIDXWXUBKU|%+x0LpPh>&W zCfo}i3V#HD4hTXOF(z;P=!Ny~F?Ah^Qt^g6>9Sm~aGpk^6Df{4`q)wlPgo1;PvA

    1D>wUc4WO|>8;Hx!-XagH>qon{Ywc!qE*{ch|rLS4JE74vpxM4;WX)j2S%R5T= zqH>ndMRh<_4-?IFC52l7YOJPXvO2&F(6|MHPD+lx?+x%O-WC{UMTEy49}+-@)YbUL z#<;=stWm5NTk7(WpzXZ3MppNXENwYT_l?qBSMvkC$BxZ0p%HG4Yw^~0Tvp2X`bV3Q zi+#n7aD0VL*rzvwneX?zZ0xsU2dElH#b>)fis)%GL7J?1%f-|*RKd=!ffaK15+zD1 zT9|2tsOl9Q^-QZrRd3I#m)PBYyjrX`tcO8Fjx}G5;iWi13u$?-G2Mww5>3cD#WXkk zfc z=@Yb!#Bvz`LDOwRGb(oWR4aK&PBwIjH>QIL0#*}-iiwtV$J08Z8{V8L5Odfg5;eKw^uRv1uv=FKJ0QD4q|N9KL~YucW4IHomI0}icZ zILsv+S}Wi%|2lB6X0as|*PjcAIl`f}77j{hhMBySy0uG)gc)R9n!FjR^3YO1>&&Q% z(HNxo7k9J8d|j>X;(8=r;BRo)`qM8sRdiO z1-fI*OxQ)9<}=nYn zOh&#d8K=#sAUk==;1&vCLsZnq8)fkXUO!g*z)UWI>c0?}5ZqhQ4?Q&|c62!$qK_eW zX7{8voA^k_ZYHe++i@F!--K;cyZFZPBoLtHeVd71P_@xRb&Je(Y3^i8NG8+#-Cv(Kg8!!bqwbr(ZV$F|H*pC=$5 z9ODB!aC-nr{8LuHJ61L(@*{q4W5%hmLtXW0)}v2H|?|ltQ>YiX5dg8 zM7}VC4ZTMr+7p`;06IY}46NT$^^L9gZW9M>3TbR8 z^1zW|6?N;w7BlZR=;_*II(0LbuTW($xQgo-qDCQeWB zVG+?fMRa+Pe)fHEOcFEi6EWQ=s3E?5PsWws_y2qH3I?{9zK z&zN(K%#?tLV<%1bYk~#Znd88pIL_85_jv~He-uB^_|*?3v2S^x<7ZN&AN^o=GUf8Y z$h$el7};=8daC;=L5KrGFDcvC=bd7`(EsI!SnXmOFR&LPevIk7Vj+T4U3Z(D-!!68 zMm#?i>3?EdM6C_ZbPQ*Mbw^qJMLg@Yt}4&Ov3Pb~thFo8LNxw(N-A8agL8lyxs24{YD_fB=?l7M%WWIEkDrRuZUl%11bURy)hNbu8(KO~+&WsXC%Od>%T; z+>8w43~a8ZsRG&S=_Va111vGF5h477l~#DLXrb~zC7hwHl(HrfWmc1@Ib&KrsTooU z{z%ym6LWH1>E}4`_K(K<^maaL8wP}Otz{LEwM_xC;+gK`S=dC)`egO^=1Z>eWKFR6 zE$q#H7tcms!=3ebYGI$xvYXBcZiqPuG?ooYY(`4uK-1BGNa!eKL|FZq)!UQivVggC zR?ekk&ZT3{rQ@7S=kw-rht$lpXp!?UEoQ<|3}czBX&jS9!9(}40mkSXNlpVv5geJw zQ+^aC=KFs(G>1B`6))OdS!UFJW_Py&C<)VvJoCn<6TS_{r^99DbnNan-y_x(Ocroi zTW_J!`B)FDS^G6LYd^>uh+}s7bP(C53NZ*7<_$3j$+Ef$BCETn&nA4dip?bQ&=s4F zQEpC1_qf4!rRkb@0qhV@Dwy@V{TZBe>-{u3Gvf_5Yd*bZ%{Nbg+f>GN1^SSuv9#nC#ND*x)ut`IRRf0a5;Isee}=Or7W8Q_;BT$Y1)vwPIA|M$ z{+ivhD(0c6bkasEqjFX}#}T%F#BaEfcEd)U+rb5O-ip?xNy+#Hl*bgdjPfd_0N|(} z=T+Rn=dl-jAoZ7gKUp4SK$bef0j;{@HFW_XcnA={SS^k7$%^PWnmVal7E0DOAIF>P zycM#8EzY9EMkB*HFr;sxfL2U`67@sW6p-G1!?NgvW@-b`;j7g!U*J2JjSi%lDy5j7 zF2w)%^C^i=6Bb?;CNjv4r&QQD8)G_3%m!XXe0Per@o=!+o9xC2sBgecf(&*C_J!z= zW1x(t9!WcqvbXHl+;1pa1E0)gW7l*kolvJflH7s?!Tu8d~Fqm#`x4GEy?p3QTt{&>{D_dk>5dorP)q?FN4*r zBQx(KxkSYO0i7sPskx8njT5;aU#AHP)$H_I!bZ^7mdz;U#>ORfC{0PTM{6(lS^5R(7XMr!kZA6&eUE85bDJpoW+{dxhKk{G3 zIf41nTQq(DZD)?CklUHwPhba5MdMu^n4PmY-)ID|MX&@9OrKalf1;=pewcY(%GM|;pE08m zRkVDE7z_{H>*b&u{7>eDy2agj`SK)=`9CBfdUzgQ(s{@cHNL+c!pW6wzdN-zidKLi z!_n}ZC4u9=Cl_c5cjomzki7%|!9F8ul5!7IF|2-hN(e_qD`>2IGNzLx&nNt#gMdN) z;7=mcw>~sjj%R60Ipr-prSe@)0=7xQ696#JpzF}r^WbhaYBhlQJ8#JPdXKH~zMjY5 zi_oydI{D^5MRi&^IN~L%962qJ zdv`b|GjqNvLR8>V)I6YtX4+pmeDGiPTJdTA+!lFD}GHO;BKp0=4$C4f-D z4^nj~Yng`j+YZPocKKsWJ+b0u!@hq@KoUhJweAh>Gy2v=C!n;g-^!9*1}`57{03KLGvB@QrehVi-#$1H00Xr&!A}PwBkJ&{sYN zGkqo{O@1HwYD{0b+NSuy+w$39FbE|ro$2CtVE?40ca-}R-7YGa{JxSDF{UfAFlYk| z7aE3Vm9Ie@I)v^vatt!f#VD#zAoigBd^Qa*Rsl*@9A8PLZAOQ9RH6|O)WJ`aqXb~qwO5XvHq}gYTO&n zd&@DbWP%4!#zOcrG{DKRQK=`I?<+`idLQTzP`m1YVhl3D(1JamQ@IE>Y$W#te=tCH z3L`I2>uVX3kHY-PW8L#6s0q84!r0x=3zz%nKwQfz4lpu_UyqfPBf^Rq@LW8cy0TomMDwiIB&JL2l6ckumTP#ot z8M91%!*V#YRy+7Rt9(|zp6My9Cy5^`h7@j0top?(qE-)J6k-|!%q4HaQ(x1*xa}VF&qH8FQ!GEyv zvd~u($kjwmx@^8oU+$=}{DUVLQx=7R)~0jsMq0&6t{i?o&;#*+IA_~|iY~dhyC{ss z&u{n)KyIC3=s1LvG$!QPq}jXDst~-DeGlG$n&Tkw^7DW6>c-6I2gz*Kw$Woq~Rf5?I3XbH?^ce0pz z(9Rx7WHdIXO;u`!qXK3uY%?v#pIhQJsg0t;Dx42rS}0`xRLU93PCUGZjR+$MHOc?l za=hm3OgBFe|8`l~z{Qc$$a{~92c&pS8O0D5-t%VU$vX4{bs?MlJx80%6!1{-x<)J$#g=* z?=zb(sZcVzAJQVt=er$fW6Mg}p67IXNY`?DmY{HX$a-iu6}F$_Q$kDtQRMjjf3g)m zNRcqHtK)JsT9tO=3K*Pjn9aq=>5}907nBVGAF@~&CB3=~6yY$^L`0J2nfmTPjV@Ve z&d3ccB&dejrgovl4x|&yV0tXh$ki9eG+f3o(20y@fl|M1P9iT;+bxE9Ulg}7MiBM` z{AO=@hg-a}&PwxljdOs^fnn)r+BdpdW{k6m^Fh@%{LrURl6AbE=!na|b-BEkbh-Rn zmrs`C?&9I^x*UE-U79YZS0jv2(;W@*xe!ipsd(9KSO2RLUOHRS^GgnXEQmz`4uA(c zCKDYs68;|P^;o-Gijc6$&a%-9!CIHP-DKx>50O#u7%Fm6CnqTO0h23NSA~-IgV}J> zg`N1o;TGgYgC8=Xnz>r9Vj{|Rr~9T-EbB_Z;=2u6G5s#0j6jv4=XGcr+bkNjgXx`?7NBqHqNxb@ExU z)y@E!_jioOFxjGEAf0Z8wXh3S||-dFSA$u(^3w6m!b{wqZAi`!aPDc5}G@w26dhg}JOq=$ZQiA5I9eU4P*_e{~mO zy1Dc}p_2h{N_g^<8&w--iaKhvhmE#038RDqNCA}cw@X}3;`L+OFOZH)e94F2w&Kb-gJqLiE+wj0IkkK~&eSwxM1d32wj47OZ(w6uLb zUIqVy*Df-IFn*d~o=wIW*6fgS9aom&ztA0JIH8*r|Ag(|{*hyboeJkXRd-TPBs9N` zwg0)Yx&qoIySpeNta#$kCf7bS2Z$UPhg$;+ zqox7{lHVvC7)z9tkw}njLF3)+Bbjt9D6qVe-Svd5>(K3y9=6-v)nep7V-2^q*38c& za+!oV;I;VCb=hLn9VIwWLu6MzGXj?p-6tsumx@vnr5@;}m=51hRK^St4YNWrQ*-|g z^%ikGPPuY+b#W|QU5Wwecp{pW?Yxu-IXGT7oMl>u3JbYrQcCb?0Akf8#55*qZA31# zo0nRlOIS!ldDP5k??Gm1un8J#FnOC-E5cEBU95Zs>>caGvignFeg_?W3& zs#WVhbuq7NM6sl`#he=yR*hOa9NE54fV5y8QAyFInm~miY4v5lBD3<5S8Y^R1|6ro!+SIfkG778x2cx8A8!K8SMErV}6r4ci z^$qOU1ee8 zl0_K1p9icppVFm`O_e+DTIIK%eatOG#p@Is#PkHLT8<+<(?ZEg|H=Ny!>kb@6cGo@ z@db*{ntm%%^qfY~Mk@M-A?++MXO+&ev*=v|=p7uiiN6>QV6^FsElDM=DVac;ddMTi z^rrQmU(Yc^CjF&?_93s#+kl?Y01w(;ZR-&y9NMplV4!SS=gWyUS+h&X%2h)kW!$Z& zD`Wf0an!|}5Wkh*B#iK^;yY!GZgWbQ_4A^_cjgK{G+Z7FE*r97#1Po`__-m{#jU*U zf4rdxDaVGUh^9EfG_@yRs71DwB`qM?nrC?;CabYb*S<-D!1z ztc9IFsa@z47qhBV*Rk^mlp3`%91i35wvfF%mG^NBe~swZAqG=I^7KInI^Unor(>vx zv6@2gB^V$uGD#cHm?;7PIc*ss$i0kAnRZJw=YF?XYF1HayrXA-_DiQ?gu6IDPnXUw zirpsG47=*4^& zx)flZ*oYfBz3VxkA?4eU&j@lvX@Mjz)r(<-p4ik9D9%K_pF}Ur z6>vPH1q?~U=P*ZhD2SN9zJnL~+$$+*poKk9M-LRE=WZTU zKJps0Vl_CFJBdXd(`s;r%&)oM!lS;3E|uL!qO<$NjxvCm*5#o%DFaD2xF)_XvHuK) zAiBDB%#cn%5MveoS2GtLuot7z@5){<(%s$zIk*lOrq=o5=QBkP(l&L-3af71rGYhx zYhmXKS*@>jj8_YPB+KNBsV>9fpN8rR5@EZT-cXwNs;o5=fn*SyFDHwk62-A$fPt;i zpB@c+dQ&vEM@oL}52mCDK!qr$pfqHh&&bj7B>BX8kR2+a+dS##t#&8tIGRza_721C zX$Q2Bsx3n{N+NdIj?-18P+f^*nGt2Q3VVoZNNYrkgkopsjaJ(^^;9ZM)r@8!%|^RZ ztzRfRctP_Og)!T#DK@YmAK6wau64#D6&o3(SoG4OPcHj08J2uGlPFglk&ML_E@N+o znJsk(G3^hboC&KZI{dmUbrQgPDs4zEhVL8HR0=dMOq< zPjUg&!6rG241#CRgkk^>GZ^J|S&WxC1?;teIWRiO{y{Ff& zIUX)-_k~TQXd^qZjTGiiYNuTqVs%i2)lDK15)vvq!X>QeH(c#MqwdEm+Jw&Sx%Cs# zUhWo_#P(Ch)IgY82Pl)-Xo&dMCRGld0U{y(Q-LO>QCuIN)B6htiJ4d zqtP`;U|}|h3Fps|Stga4E~d2b)hlDkBRw`b-|-uv+2oo@xaGDAbbm(%f#T#4f}hQR zw9lr&@IeMlqVx`^90jIsIrp=uE2WTaX!p46^+5953GWZSA;{@(q!f1iguwR^df-xy z#R*MbXCR~rNM44cZw$w%tJP6v(V>PG2l6+9%C}7?=?U4^NYv90*AC_rWq!XhL9kDH!K`9L{3gy8T zs5Bl4rlnZjTq!QH)VRDX*2|`q=6=di@3K*@Sf-i}hqj(4usDcJFF9E(ll_E#>?o30uwS+q1!A~S{S!gje3Nu6$^|9jnD-m zA}TFJZ1PGScnfdD2zt9*kOKqxq9e0QAvmSH5gDZO{^$Imv9x^}r1M@7E^OoJ6+aVE zRcVw(Zu`f*f_^cg2$-bbfaP@_%yvYKvm(F!BMc$y$A(9?(b)MT96nv2E_6y$==N=A zFQ0#%iL}LMa}z#hW-dsxAw_Xf}K{gVYF1r$&POZ zvc?=(XOwZM8L(dLwK61!B(=Xg>L{A1#;BWwVtd`oYPC_19z0T11&`xvST)d0tjz>e z+B(P#+kg2>9JA$7&QezfZmUscIoxvXOLNp%g?GiOffYx&FV5F!N0g9`m=rno2sU6a zkP*B(56#<0&Z*Z@zv`m1<-jNn|I!>hCyFRyVilAUWlXIT)6B%KA|oynaHNbJq?orP5Hf>p5GHXwAt6i8J`!CgKD6ofa5YiMaf{N7`dEC=&U(5-ijxXp zf{tPEZIKR#f1R^nSLf^QZSS{DAalJKXMh(9VOZ-_qg82Ypl z(8rY41`L5GZhvdt_Ro{HzZKp7){5g}I9wm+mptq2`LpJdA6l~`(9sA&z0 zByz}1t1WTb+1eh=T2h7;gksML<@`j~!(EPGPpX}^C0&AY#CgtCg^bjH(mmZyCd3DnR;P} zul_s9(=@NcY?L34%rgoPc^TtfO_%fyU(%~#B5!;fB71ryDeak01cs7JzhF?Bg8cs;#1BU{!SsKtrd}^C-ei6}BZJ}AN!&>Mw@=eIC&?FtCLsxp3*@85C!v1g#tu~bTL8s%zR5smsg?{S;0PuX;mwx2Z6Ofifu z@XaLNimW%sDpjRfa<>y;JTcv#DVi!B1_37QRO^4LA+8Otdb(3;x4Lq1A%O5J11RMH zkTTef!G3LL8-p7k?ski)SHa^@tCW?vaxKH4m#Ty@33yOX!>Y^OTCsLk2`7TzvaTsu zVS^YiLHsP_54(E>=PIUeMBW$(ptr#ONi!E3|jSTjW6HD4L zWGM!LKVOJatBqTxqjo3bbh`wY0@XY34Wx#T*|p~?5iiBJLrit54EwVa`{5dq02Q9G zz@B<8m$K3j3#aTww&){LWuG0oZ&gAT$Su70Ip z{diSlr^YRA8V_s78$4ckLF>kN3hz~R2UZ5j@szD81*(Z%-pSz*ozLC+zP`Vg%%720 z40UjX1u6z(m>;TIWuVa8(fvI?9>LGp6Zidno`7)pD(34%Ti( zpSZ)la2Qy@E~p$?>XUMWTC*8OgdSw+OkXIyH8Svw+6VPcZP{RlnK^jxc#;lg%Vi$6 zVy7%igrPP=;Jrzkn`6Yzgb&4XtJ{8e*{qpRg^z}nf_edT*(nUa%_f>NgcC{#7sIhH zo8Na|HbKXum?BRU%){#wp6GZuE6__PamEI!fD;|_6B_h!EUsBWflHPa41Lu*)@u55 zC>n=VO*78d$|mZ+be`@w8F z>8~)7(3IMVu~~e{;zZ5@Nz4mcNQ~kb=};trv#>#Cbw^=?OgJ%WmkQZ60Z@3O(W8o2 zyDk*0#y2^u_)&-p)-;z^>9U-Jn43{QwpOzOt8qVYYlUOP4+td{k|Ejdjh5wNr$}>H zVP8sv;`E|sKoS4bm2bfMngo2OpI;f_8DnXcN=h~BC*eI$1yx7?D#T~rAsO>&N9Z{L zvHf3<`nxX_VDWYjWSSFsAi~K;&jbZ4^A~#8!&dicPVC@7N@$~#RMLvx0diBzgah`@ zeZVk{l!>|IOAWh)dDrmiZ|2X3(|+j0_9RZku(n%yFyA>2=4!LV26YXRT;*v|GUD53Zy*|Cu(1m!#TvttMee)A5#BnMn z+n);M=cmqq`umfm6-CuDp04hc%#ftd{B(iInuQbfZ8lyvU=99M>e$1O_)guYG3wg% zmT=H537Ppzk~f)#+p=Ut9?m!fY--!8Y|t8@C$$*PJ~ry%6RDwBfM764fPqjzpzx|u zEy`17U__oI?*SO!w4sNM$3L~L2Gk)_;2702w>6>zw>pTSftB@J#lS-gl@W$!T?YPq zm%ZAA4!s(eyB#xMhdsM5B%T8GRSW>>QjjpC5{0jJCpF`7CW}!>&(3R&cimFGQ|or# zAG?&`p|U2Tt8pocqBv#feKu%qE5yQ?wH!06|2s^q^Zws=Zea-iueztIwR3)3YgC=4 z{?4V9NpJ{C$w#h`7m2-Qp$V3ydr{}Pgn@SrPR8IjkXcc$wSMfxlK=tyJx6{3K17p@ zFoiV|6`Q%KPB1ht9Ps7Hqc^vEpX>%ge*{q~3#nV8zBy^<^>Q98*7&IS8!|ZB+unX| z(|ixU=V_n$jU8H?H!}Xgp^YQ%TlW4NBZfVjT6-h!N$7tid3mJBD9o5@H6n2?;%U4{ zQ|v3=e#`>v->b&MfYLb*1ANp(dqDldDZ>e=sC?c=fkkwy^>es!pJ~?_0`z2x)+3V}6}pZ>?`g9Q z@@`Cp*{fDR)KU<|_!BM5RWa&!^&W-Ua!m0I*fMctVn z>^^15M%@xyNK!)nBElKEF@hl{UwLt>wqZ|szoh3`gkjhDyTjN}sByuCthJbYv03umlIa9iG1!&IUIO*E5yMK@w&bq zt)Y?pBbqeAM155SU*f_paoO5^c>TLCI|i$+KAwuOr}Ogr6C;X*EO4H}F!Z4v`w+om z`Wr?c#dYwj@Tr=ERHD~gzngobxYN5@)?6kB&^2TM|Hj$p5qUi1IeG1$8Ph>CZjc~>*5VEXf`|ggakAl zIQDujgsIAkBU&8YgU=lZm$zad)^vS(npFVUNJ z-c=Zmhps3PQ8i$6J|IKVC#4FPGUD3D{ZN2ZQUW={NX0frBgHfSImIRV{(3jvf8l6gV5FwyOpR zW0EE-qID?JHG^+9blMdURgLlwC&^n)=x}CC>_Xx~q(G0UZ`+M^`gRCbyLY?q1l_x{ zEmEURXSQx)_9Wu%Ge#hZr)ao&$3A-_)Ow$C=g1o!B-5v~3NMEE@-$dY^o;N(a8z^( zpk%yt?sWWF>61!37kvydXpEb-K845}q3GUj*#sxW-(p$c=&PxJVFpXqelEKr3Ufiv zqZBq0hx>TyqCd8Z!Rq6@+3D17F*jQKx(Bxn;r+$yat`;oOLGS^bv}!22&AX3&-up1 zq(%kDx}4sT8QQEj-06{v98n>cXPnrk2M{Cz^i{rHq4B1jK~elQn&}M&K)bJHF!LvU zzfX0j^|8!h&s}jkb01ZJI$An=5DYoOiP4`&?JCY(dXoW1xT6})WYIV~YYKy@0co;$%++tXQKN+#w= zp-g9mZer9AiRCnB%0NQy>?Qb8D(E;ARxLOYy2|? z#ABu4@!f1}F1R4O?5Q_UGGLK?%Dw_QPB5ISBM<>Ez$Q;w*VJEyjD8>in>E2xf;Dd+j7HCALux#5hGnit~DWv1V% zLN(Z~@?KUxZvaLNG1$sDN|^eL>U@+( zAOsPg=rG%Kh#axNP^)I6Cd0PG7*%?KQLp$!kHgDi5#BgtX&WegVc`4fnB{l!;$Y_x z{!6*#MgAy`tb2&8vExmUl>sO7q3S1sMy{5HOlbUDV69P<&Pu>VY;z@J9;4}9Gq=KI z*b9Ee?CG${6{?=$}ekHKg-Sw3vgbI1*#vQ8D5m zF>0-45CAP11weLxn9$F^_U4b1`}t09I{uODr3KR2OK%P`KWyv~t1gCk;Yr;2wRig) zxd967tq=k(7b(noj;E!>5Fx*0@2bAmAjxygCXZ!(fYablx0q(r`!vG;*&D zXh#q$$b2*Fy& zfsIC@?R)b;{1D&|Jaj-OM?1aY9A=~@Qif5**|hILy=(lDP_H=Y!}bQ^r@05L++6(N zec;~`>c5+g1~-5=*nr@W$6I1=r*Qac@2?oXeMwQ7Bk=Gadj4ocoTA^m1xCXgX@N+= zBZQd}?NO}ZKA;ScX*_0&EbVEu1Bb+$&E~-6A~RRw83TC6L%^Osux``&pMx!#l^B{Y zm|LCRd-Hw(*bCgL5pYouQGv|_qG0QRXT4ft+Pg>BesRWpUvIQd8Y|C#B>4Iq&E{y zCb3&v#pU95IzL8!4d@%NWT;9&2PnzU5g%6&`PZ)xfMk+>MG>)U5ASr~WbW_+1_D}! z-}>SL= z_Op>uvhkU_TOMQ`BfDxt#kP;Ze$Tw?52TdlW$ReXV_=kk7sGI9Sv2J$f}KUqlYSbU zNS3KOFrug!hK{bMz}d(|*_6Es*%u0lIF>geGEmI3`KVyL^9_zxW>*Q2eZXhOKxR)} zc=!OhBd&<=L3e=Rytf==r23vC>#bwhQB+9Dlc<$v z(JLJ|W2-kW2$o}94PIEfYgVx0rP%kzz_{Uq0_kG<%-vz!VM@@k1w<|PI%E&}eSS(c zw?L$?>E~Z>|GqQqUGMa6{n1Xa%xrD_pKAaAY;AqLlf$lxLIpIgko7G`c!&9qmVo#_ z5NYCMUbO_P>$JMHVyE;bLY2)xH!%2U7FY?H6;RWrH6nf8JlYyMq!P|@#@8$Jchm-3#X1~j$iHF2* zIX*@tnGY!h6>^T`g%478h-E-J>9~rIBKI&DtXWZfj;|Fc|#O zy=XPAn(}vmUG;6Xd{wN;4`-Mz6BDI!V!$=~6M7||)l1=XVR%EI;?2$Pu#&15Tb<3x zdX%nzii~;%Y(0(|K9`$A1HZsSyRl@SMljDJbmv0&{qAL@TrGAxzcee27w?HmIwkAo<>W zgw_LQt`N!sVjKBq9A}75PXV_S9n+%&*HzO2|-5{h8Mvdo%TL z3zzF&Rur-#kI$8)sHk}dQ@U&t{1{cMUnbjm)ujk2;AhP9$ME@Yh!IyFZN z16fE}y`a$Se3s#|CZija{sW(@Wq38K8$d;APXw!!zbS)R5G)2D#-vz=3A1keV|4m` zKf5^n{`UQ+XW8H-Zp<4*R5Zr>4}SvUtUM0)-c06CKtWJ0Beggcw+onVnloU+6i&lV zP6XP;^cG7vF7li}+nHWT3g(G+HoPa8iSN1C`%Q6XFG#u10wFp?q1qR-kUe4p5JPWK zLgdZ@ot_IsBqfZ=A+y*M+A`gvb`}H$-2??|l7^>KPUy3P?`^l!laSM~nf|0C}(f7x*B_J5Ky1PD=bp2mQQNysUI#{Y-4xwjiWyGzpl)WO_gH`)ouJ zn0#hhx>v2gm67f2yeG!9S*vs^B8)y5MrgnaSM_qG)w-&G#6K`bS*cO0Rom4Df8p-B z4Y+^M;8K}@KS2~oJ%zrq6CE*Fxd$hMYqK64J9GsT4)xDll?uAs0AmxT(I6bjg!}VS z>=Q$dcsZGh`E-i=3HSVYgRR485l}yIPR%OZTdi(hlj~Ir2rF|q41AXmeKJxS``&~? z8wP>4OXDDi#_@mwg8THE&aMjrl_S0~+Icq5tti^DWsG6mOs=->G=)D=Cs!995A&4+Abr<_Rj2TMhj zVsaix%e-E&I5Dm{E}TDf2gAXXY{P@a>8d@6UwFeo8XCb~jtrNjgino2KJJ;QRmM;#QgF+7i*nus4=(GHYn$)3ht`>^#~V zEQw^c3=*5b9=rh1ya2vH<$pJzV_Y$CwLoWvRP$toT6FGmt99r=7zFx6XOFfrf5aKc zYOo`g0kE8(_)cS`$Ot2k3+)_*QVxl20swwR{^(g&4irKFn5!TWsz_^K>59biAX$aK zAqiCTS9H**{i`h6&i|Ik2N)w^e@1I$!;I6yL`;V5`4Y5PhZm_$HqKc1k2*BKQ=$3N zUTqAxDF=9M0NWkBd^)^S5s1GGK^_I0hTBUwG7&!n6HZ%DtJXul4pAIHmDn+1sXZUF zf9=phBUwrWJPQ~NXQJ;tx(xF|W_xE9gGuL51fOw;%|V1+`B&)DZJ3y0^ZD8!S>;sv z1yCx`>qp})Cnd|%CUWEpzy0vTQ3eEW>njQ;xKHNC+;1t~lYBTof1x-s^R{APEqnbN{01{O;)2>W!tzfV4rMKUis4)EB%zo-oc}$_ljsapz?{tg-u0vlwcuZ@{8d6Iw@6ir2Tf2uSRtQh#4(xQ z$U`K{SkQ46oi|#gSjnfa3-YbhUJ1k(cv40Oln&o0R@q?|e`Uq;Uf$Y%Qo%bIB zU`zOtKLohSLuvaMvQR$baLRTlTMdTGmRSSvulZH}XkmS32rpWRchcgDcDadyZ zj9N~|-}sx9dOK3*ojMi$<#_z4f+%F6h|C656F{(5Ccx2EEMj+|EY%0qw@nP+**k+ zqeG%Qj0}q33@1Y2qE*zs^!K+9&@28Ix`H1WOiN4=4rISsYshSLIEQ?L858_qA#Gp% zz@PNUHCR8_1nZtmhlw0~^p!OAH{@=T>f+NSuu}Buiz#dK`xz3F_riby>A!_hr*kS0 zzz}Lx+xS8xS$IV<^EtEx>*x@4rg_S8F^k6PiZmm4U53a)cVPgJ_*W}beLgay;#Ji- zqXX4l^5_RoAAK(}`dPi?9KGO#$MvMQdPapO&B$F$b%$mn~n9~c87L|gze0D+9AauS{iO2)WL+&qbyU@9(NRnP65EN3tgs*Nm zH~PdC>C%FwJ&)X13E5?9Q#6s+in3$;CVJa|56~N5$;aXdzg8ywV`3WqFqrYA$ckB5h;3Z*}B0WaGw!dD>^`g^GMm92>$MBxIeiW@o zX!4(4?*n@Kr00L*-~Kw9JPfH}R+_YKC-`STjGw7g-0=l=qlDWR0 zZUpSl=aZ>r0nB#67-=T{)opN$J2bhm2O=zred!MF$+Y-CfOW~CjdK|#CBcTy{4_%; z=(P%mROHGI8ijBn^5KViqg;6_3gHKi`?6=&xU5`?eWOMLl_LlGMPZkp`*Cs@IA9z| zAII6S_u&z3-sxw zrvdF`R@AH)uR|2lDzDg=sLY^Qr!EwvhsqI{;jD;BJDWbH%Q017c3&oK%o#)jKcyf@!yLX7{H8^2w&b{1`1wC6Au5an1Q^( z48&KE*QC2}7#`QdSN~os$ZOJF>|9oA@ZbG|@)?}aGm&;3GF*-y9-TuUm-A|mmvit+ zE$8s)X#Z%>iC4f}pI2Iya(qFq61AjAS#(j6;t0;2tz=JPTW8$5dwby-di8I0>*mc{ zH@*UWeD61g@@?X;lBsg*{i{OzPK3sqm1)N6;>Rn zy+L@SUj4ftDaq=bC9<-H=f-lcutl=(+`sA0``zKBpEf1m7uRrIW1xIiqLZmA#*Tj1 zN@K}c{e7X^ELAaE4}GS02K7RgRjrH{Y^>EPuV|c!`R0TZyq~X|Apd^njXr;-Ya3;$ zfA=hViwBscdxpJXU*)?`n6ETYczLSpI_6VC%}TLTgt@m?&0E!rPj@(3D42Tv?(Fu_ zJzs!0|MEi3PtG;3AFI(sRN0PR&o~JppxaHr83fQ0w~MLKC`vuqUsvlFh_Dwjsy}ol zPDKw0*;@1o&6}5dOlHady9G|DnzDT9qPNjJT?ltL@7BPGS%2=|+Y>TPv1}b@g)N zkBSF5<0)}z#0%YIW3X=>-1fUPZ;x0#S>+@n$2#Dlo-mfi0w>G>*SSN}a#N*f*=&L| zKSXzg(nC(Z`obUE6$$N6Mxa_*QJx4{GiT(5`5lhL$5ZjIJ`s|%6De>I8IGR2p33|| zWIl_?;7Dsp&AV2$Q`vx=tRzR9(;C+F{5#T{FQN`Ql5dnKNNZwE0RA}rAx9)Vrh#!= zLKV2iiP9-=G@AChV}I1SDoQ!PI2uVzwRVIcb2QCSvdHb)5P9BXPVe9K26Gt9BKT~(VBk0 zYU)H+xD1SfF!66BBd|dy#kzFIFH2d%l9TaP!TfBTSoArbD9Kc8P)&jNyEJ}4b^{ZZ z7VDJ@?ItfZNV1k*_tmCY{(nyz6s+hHKCU{cR` zyHxS_eQ|j`3&b;?u0b;`WQ&~{oitf>WF)VDW+hBh6(}EhSi$I=bIvX1BO}?M^&N18 zQ-5LF6XjcMxVUMvA88W3rU#jaHSFNIP+lIYhffNNVnaD=Y9N@Y02}pZI=qFdAPuff zQQNgq<>~!ISKUgvZcfo;@9x2?+)*avRX`>o?2pscv(hEjh0rPTpp8y2MT zj#y1+^;)w?+4&S!W>!g_)_y23PkcBiY9NWjx*+UWv6dXWN7sKY^n@8MAvElcF&(_F zNtG~X)yW1O!Y{@4FPEG3i8WQH#^l#b)2O+6o-4bIPIPszJX5G^0^eF6V~nu@cBi3J zRNMDOhoX-aj?T9C(8lv9SJ>I}t}$ryYw~m5(Q))9~k@et__sGfx zXhzSWEJ0!z&>g#+C0?D9h#OoNjF5Mdv(;2xXW>nz#Vp34BFK`HYJV4Nx_e@Og^Pn#9irT2b@*v(-Q9OyMp9?;Ai_#Tt~gs)4HgaEN)Qvn z4%cGX^|&|Bx>>ZWjHi@@kbdjXvL02Hf$vk+hjvm3`L5m&mYRb2oranc8*d{IZ3Gv&eAI28n9h=;j9F1 zgr{;3WaxP|2v3VwapqT)C*W$OE`X#RF zlVdFV`uZK|EM^8Dd%e|f9g>%HzGZ??U#rwFI&a!&dsjFt=vXc$*q=X-zIdPlBBT(K zdtRaaX2@(b1rFQtDVk=0BG*8DbOS{{JPy{#;u#y0xZhya^D8lAr{bS!yR6%6v^w_N zJ$}VWcXh}U#FyY_k?5PcOvJS&!d3WYdZaEuCEVX0Afl{{cQ9B%e( z_!V{RhYlV43aks`iJSTW*LJZE9N#q&qwry~#--*f7r%QV>e?0b!-MfIT2qlMo%>+C zfvi<@uf1aq`K2qacA)1Ylb2k5Q(3(nMSF1w7`B9I)Ju4y;u=wgaWmAAoK5&JWj!`& zg`g?YrZy95Q|q-9vZv4#)b5H}e&p-hZf@ylBkKe_N_rGO*>bVmpW;jlK5$w5OYALK z!CW3zi&>?zHROq+8@bG1`Bj=DKc=K5?8%n4FT6d6|6`Nz|!70=4`WEH0((Yn@~Rpg<; zwK{92p#=H`^E95}HkqnXck{WnzS3NA263I_Q4lcHAt?at8mM_x&J| zR7i}?N~QO5mX~w`Uj`$n{M}kwA3pOkwNDW#0!q>I+1RF!G4RZH-%X^n6%RXCO?=RS zS%+5!{9!&XKn;ATP&WpZBCilBKS{bz?4k5XsraJu#;A+cv^y^0g{fNc$;bVKel5${Rs z$8=B3^*OpC%B!n=$4f^;IFXKQoVriYn|?|Ofa&TMw5fMP&c{1t!F7t!oiLFVMMm>{?g(fa@uG!d>SQK`6k3iW!Qx+C5*6 zMj3e&R~@7+vql1IB00#7DbOZ_L|UfvZQ@P3GmIBTmg(9=4JY;8sk%A%v?V-DVPO4? zjvxNygXcbXI^g*lq@uZKk(`h)%FO2TrdyGjsUhQ_@+%Qtf#YOrqoXRKr*z99zHqp> zO=M6_+}bmaheAN!hM$xf^8VIm2ovaM-h46f=VUDW0ye@ADD17g@nzP?``cmf7A++) z$&P$Nlr4r6aXs>2lK@RHK;MpNW8`=18)klx)vL|RDuuZFfRXtyJ?(HZTXI76;R4go zk5OXsfT7_AD!Ck{=VM$M2B;0f7_K{_$kW_`UhYuD4P-0-0fQ0VAhMD%@Qf0l&qm8Y z?7{9lEaVp$R<@W-=VMB6r&4iX7-|{&E?>xrap3oMxbCictDrRMlSP+QRIL1v9AmNG z?Nl!-jjN7+E_59uW*^4WeiwIUo_KtJyNmEa|~Rrb%O!^w~+P zUc)L@@jCWuGhW>6c@<9{o`C3dPc~z^B5aLAEo<54#92IK7Rk{AwKrq$QQUfWzM%Wv zmw8-}SRtoCa$aBF(9jO}jJsja-$o|H+u@9pYjonNB<-wI7BK|voM9<|QgecJ052+0 z_zq1bH;}$pN!PNMxIAn-aY_z)4JnR#sclwEn}#I3#e`x&CWQp;f11FvZ|Zfo zaXpX^SQrp0VX$*~Xd;3#1zM0~PSywxF|n5=q`Krtv&5`Jv0!WhHk?=i;SClt=r_Ud z)}o57sO|IP-V56g#|npJNt8@iP*)sh(r}b~Yz+5FhuB?0Gb!5QYNS~~N zS-F+BFnaWv&e6c&2z3L;3PFfk$mhgW%%d{3U!n0htNldbt{k7b}8i=&)+u z=D^D2VUu|lHkfCj+fyTu%{ zNTKOMLdCO72#_hfEc?PwCRIK!Ze)Ot4ZCO&iF#)tLOQo4E?yZxq!ArkHRDM`fY_qnS9 z9wfXenl)g3%{Q)15N0!s*<^F~!p;2}!^@4{X1uO3o-;? z_o@zePnn{>?&VZSA}0;aJ>-uldZ$~9&c*6rCbnk-sDEPehTN;nN#amF3JBWcDLF^$ zts4z0NP8;`KibDgU%3aLyWC{cHqbaZckLr9MM|v_+@SBR&2n8cWz5;&*ID0^HHSp6z%RHqkrGe7%^p69D=pjPjd9Urvzy? z0nI5l5=Cp(@b~=@Mr`n+4Gl>OkXld-KvMgKqiU{*+ zSIY+5p=M4gLlMFhO7LWL`+~Fm$5Wwb6sD?uk`T3-RVM9?>nf5$;uB0W<6Rw@U0L#) zUDcM=BIbZn0JUua=RGG<4(_|bHw87_$t^J(?SwsT10+R2%DK^+^IT zS@;i&Jia>jnr0ZBJq=9lOF~NpM}42kZ(RH@O69JHp+9#!G|PS|3ley zqn=XDf_vehvZvyr(!n5^Ws(heI@&^y=8M!_yJ4w*C_wTd<%HFh1~8!c_bYdpD5DFW zkq!`*I4jj8W=Kz>L2NmW)tG(X8PU~Q#p{Zl{>uYqrvUoQvlLy$idH`8gqJH~W=2l>Kp@;=iayS>TcWTmesNqqbO)kQOL z%Ala!6J3Smh=sWV4YN6GJjM2*{}QeNjb=P?y`1!JF^&#aBV!X0;M*1MAvPD&i8M2T z|DVsj$GG#BCS^3Y7Ue4>3Pbj0>g0zM%B;~nZ&fNlCa>xp)8W+7rHtl$zu`1oS#oARx;W*}?lu54vEce63tvCaQ;Wa%#;3Wr$A%K`q z=Z~CDGS~@UAHI`NV7C5sM%j3VxMFSCZ*g{*bEcj*M|+?xe12hobc_dz_y7CPun$ldIkF=`yYwkB( z#SyMTf6M=t=;Y1+>y+N)Lfv`G8oFTLF@u8*W{_W%0R-VD zeLfY~02bl}{-@hrPL_e+Cz2R}sR~rAoPoHDtO(E(34cA=3e6^!cj~FIoL?=+aOJFu z>AZkAUNx@T@-1IDTv~ssRGQtlHMD>$l6Fv!iw1AXMZDF#?6y&zdj;ZLu6tH8yiLE8 z{-!MQ72AsJvdAYN&Q^KVT2lxQX0g}vXA3%FcUkJb!E`vVgVlDY z(rOzrhkIAAYMpAS*zQm}o#cK$likuPRF-b>pA2q~T*oI9MruJ309~2@Qj7o2+O^QZ zG2B%7+ey-#wS#f!Tuji4R<>tD?1C#(OthL_){`nH;0Pc={G^Pg$Sm9yW*`x}h2a-e z+M^>t=Ry711$oB!G@DO*%wZVfN<>+Mdf`=)yFCTq-$ zm?!-gUp_0E*D!U^s@5vbpjsDl^wb`Xa49cuMoIIAAW;+L3P+(@tl`I?_^tB07G06f zDcP07_XXR#WA$K#rwmqb4<-+JizHCILUG~1N=(tkX|fGS1Lq!u<33tPj65AJV=bVZ z6Nzw4#wQ67CgFdILg4vY|`+Kvc6Zz!_EJkax7xEWfFu3ocCPBOy+-vbt$%iKQ z3_yM3jTok-=G6)wRgSRzqdM~P>DT(+g^x?WdkbBj#Pms4O})KIl@Jtu^l#aJZ{&M( zrFg0VoM`?XH2nrvl8wPqT51Zrm;pqkvLHRU-~JH=EoMiyjoA%nLHBNm7CTT?#fVYW zr4VkCk-yQIB;xTmCLVu7^_Ct0hzFdE{S176`7-Me)gmBJf|}~!*T4OSvrwwx@Hps| zmr;fCq?My;>b16fufN@xj2<^#F|zE~?R@$~)aC|HTe%$MOZJ+(h(n-+ zq)lFN{`SDfFiFD+Q}y9=V#Y~{(@d-=6+B5*W<7~(3va#%)lleAgkL`QB0vrAXWRsv z5c!R+kz}3gY#BIpO_3W^{yfnI>INO$OZZ%&W`41pzzorDbGQ(r>GVS&M#B%jX;C>+ zIIQ&dB>P|c(At;U`+1*jGw-MK56H^Q{Q2l{M~Dt8`p3RE30U3VpAYZQeGA%C{cB&` zE^mBs{U|E<%z8bYQr4MCAG!=gngTIB?1+QhPvq)E+LnH1XDh|#(fHs1KpA=?^thsP z1{_JqO!2N;Y&U7z>BUd6QF)#ACh|R#HQB3^wH*}_Ix3hQ6%=|#C;nbE?P<#$+>z1| zGhFY1-)dfw9CTt=F%Z;M8-$Z{6!vNzce@~1_%nCI4hX0q&lv3pt)rDuxm1Z(GI5`j z=PXbf)oy0J(I#)G41F997 z0ln+FTFDuN{8$WHP!i5SvsJEYdInT0G6VePY$a!qIDs>B{G{Q}jL{u~yT(4#Lz`?$ z46mpxoPfb?xU1@E$eNLv@YjU4I3H8eL^AzcOOww*e00;_&PMdN^CbG)`CR(jc?$jQ zd_DTx*_8fvHlx3tC(+-|hV-}dRQlU_GX0gQJRLN+^8^~)*_Z}*HmAXzjcIUaGaB6a zEE?R|ga&suqrsieqQRX_XmDpU8r=CT8r<1{26r~0!JVhm;Lb)gxU)G8?tB3a?tDEO z?16xY(H}M;#?4~qDb(1k{xouIw_Hb$Rqd}qkX2vnDYB}$jwGvk8`5M|eJxQ|6*s5K z*y5{^W$bKYx*V&VOqiopS5anNcRgv=RoBpFtKR0s*=m+Zopr4>ls%huEBNZqFq>PX|yrPL*kJ2RY4HX+od z#^n>}v|0N}WZG`HhDxi-Ux7%guGZ3MRdEf8R@FA6(5m)20(LL~Uv^d61|)2>{iDe7$83+DEF z`K&i&-*(H3=Y4i$>HBwVlRg`Fd)`dE6wOk#+q|S8D=;7GETj5hH>M|cf^S^Z z4cc%S1x%Es$vPTunPtt(N*JJ>pS@Hc30<(kCV5_nx&Qa&aPId-e>ul6Je0C`NEaCz zEKdibymvcYu&L(Q2;sa*rjmrz50}lKI^9<3Z8@HDq*!WJsmPh&K)2p3V}yHxXqXgw z09%8EMLZF{E?uTiTq`tTS!YJ+Q@)4po-pt_8m(JlM%}7vB&JWtmVT z3I~q1@!Em{tXr}Sug?RKAZm?LXIoOQ8u*cqQlH*R8rkR+-s%cjF0;s2= z5RZTy$7DA>30S})Z#LtQa8$Ssjw+*)B!Ns;D4*J`WCW^&>V-)4!s_aKk?MOZs=sShJC$%c#Fylabt;Ov8Q7c@JcL^uodrf; zaUOg^S}gh-?!JLFp((Vr+jSY=Pq{B%P0$B*!qeTYRM5a(EFNe6&VNsyWll^7zB;f4 zZ=W91#1%E9U@#5wN(^%J*ivW93TqOs2`MJKT{{xNT2*W~Z2!2JF1!&Ho>-A%6I_ki zJG^&0oVg9GWjR{<#4ny<)^Li4jeMtEfXT{*gW4jK$y}pH226-SDhjVOiXr=!VGu0w zPO~!7pH@ZsX(ScHdAz8W^z^4V^W}K_NIapAKFNp;hsDBTv^`hYZeY4;_I2;OpuA#} zt7St(;rK|UDv}v0l|o3V0TXWl0<|hXhuD@>n;-{pqoi%c_4PZ&7JA8Eo^}6!?EUL{ z8cDJ+jP_sY*%y(m`PvOOLKGkgORb)T5VBdV(*SO}r&qr`1r$;(bDAo!q-lGuZ2#Lg zw&ReQm01TswtHsQ`z@x&f;>c?A|oRs{zYCooqe@<(n!F4k1oJTAxbANwpagXRBdw4{LJ?t0F=q z0R2Z;lJH)#aSB_cCM^-w(6#!o8*7vdb|l90NVRp9y>(2*ntsW2BDiiJ>=gm*WNd51oye zGOLiZxNhW&2|x1An0{#gjH!TG-S+iGLDw0%+6KPuO=ch1G#1Unl_@ZJ0H*0i!&|hh zm90c`8nY{pT(6{ZJ_%{V#^#qm%nkEP(~s{h%J(hDiJGZOEHpW`cyo1eT|Z;`0W9sr zom-GrdihM_)l$6`9mltv6>t-mj$%Q;S@pqNNypJ$5jtuiqpXY!c6tN}@nZ}5AxTjP z$4`IhHfiI=4xo5Beda(pjE>RcnvmLTBaUMNlx<$Y{=dqC)VS(im@Nt&Q(;AUqh&4c z!ZPTmiNcL`$WXW(4FGfL9%W=7sCoDZSJx6O6W+K>k#8!;v4eJMCrAzLCQXH+?k_|3 zquZO@{T;{dRQT$yz^m)LeZFS<%d=yEO1klF3f+Jne4Ej`cx2f4EZ(%PUiEIS;h!`1 zsUAnJsDjNLY(mS@kA!Xn((+9j%j;bOQ%{{r(yDHv>COM{e z$GEVMEMsFC(a^XtSzdljcWZ_`m(Qn|fr?qya;YFQGHwcg$$F{KIlZ5*kOJP2TTgNo zW52p&7wnby4^mz?bcOgk3i<1q*lFh5Lgs+Fw#wGivK|~R|XqHSaAJU#{*e2H=5?*q&cn_lqkqmnkKiWM- zv*q;FF!H!p_CjR#mYjK3ODUZ;AjZdz1i|#4@j`8;oR#A!uNE2kW$g|_7qIdxnUJe% zAnz;4S+8^`gT6jL2g=esvzFv0nBBq_;z;z}H?Uvp<8X%Fo5$#=xk{IckT~>4yHP2d z>(%VOriign4^R|;y}oE(!PlyBb@-8S7jh9{yRayYpGZpZ*Cv+Cr-nms6HwkW|M}+f z^g82RFkaa35{};I-Ect<(s0=OL-hu3JA6nw!ypd|tR}=ZeC*BtZg!6x{jE3k0`^u! z@c=li#zsNcpXzEd4&=2C`%U)z7tsNdTOPozjN~>v+Wz=`Z=yx|mJ^M2U|$zjgZuGE zQ7NzLU#rt?HP-dWVWxbI=ybYbz{87(gs^R^yoap11Rh* z0)LJH#UiIR>1DFoYbV>Yn+rB*F4(xa#`PuH`<;=OW9P4!L0FQXIt4vl zaJhOPrcM&&te6@TP>=~ISOW+ZS{PDdyjdW1Rwhhf?owe+hM6!`)~cP-QDcs9m+I*K zP%5q14KUP;3-szOfs`Kl{aXH5;PX#q5=M_bYXp8NEc@$7#%TZ6ZBqBKbD6F>vSHv_ zU#j>Jk*l9rsqRLl68*7EzKJShE42rDk4DwA(T0#11wz;SO^Cn5vwU7TqPA~HObe50awb(~;%qvv7N^Rf`JpTjA z;l|qCOTx95^f4AG8f++qD=P&SOKd}L1~SS+DZjDvLBRHX`@G0{(@|9j_R`X(!jaq! zdazehn9peSv%L$)?BapQp+eUx(|9P-AByLThyKu3g2NP&gfX!kdf|Y8{ff*JzO^+` zjC|Goja8rU?Oj`|k73PFbpwNbRbQQ=XDD&!(_7E@-hB*3iB=MuAmIIB?+)3<-kpqN zm6A1{NxoNgMxLl@-XIxI2ZN`A6g-lQl)L+0GEr9S^+WG(GT9PtU6k=k)BWY9$^TrG zW7jLA0P%-Z!0^mzxgqV+ttnrKSB-P{MmACV)aGm2U-wg*Q}8Ud`^k&hmP#}L-k4*5E-DkR|>|-c|494 z~xKB&d!>0x*d5^!o)qrK&d?w~sG3iXP~v`%Nf9S_iIh?CAty z{j7d&OA#foaWO}3%@HGI+qB0BwW0^(Ow@)(+b9QboxRKH&6)o#j+HUQ%EGJ@dD6dC{~eIf#k-gX8^QdlB+m>wH`#L`4#$NK`;$X#>#nS^TeSJ)ml(EIeLN7(}0b#ZDH%oR@Nbf{uAOnVF$5Ju+jxddA zjqCeOapP6b@$qBmVU$<#oS!>lkF~Z>&+!aH!UL$lyQMoxLH`<)p-HwgT{SK2RoBz3 zqdG83iC8X?+8e(E2-(pbiX)4p_I7hA?%ns^at>q%-yUG&?oM2MI7h;TbkU_3`Cc^6 z91fFEpIjsRGihgo$@=Fr@sG-}DiQo`I#Zp|y3~dV$qCKk$e#}Jr`z|({^Bvmvx)CP zjXK@1Z!Ysjj9XP#>z-&03U`XH#t2T35sjF*Q!;G{ov2PqK0X>R{*i}1aVbu5rvm>Tz!{p zkb;FVqSHD?E(Z#SmI693m_%Wy2V~GX*PKIrJ(TJLP{^MJ9w`-M?FD_M|2Cn)5Su6f z6RCTIb9dlJ`#w}PrGm4rtApoq7vTq&@Iv;cmVB8V#-3)g zbm7b}ksIskt*w%xu4lg~+BR?M#S!IycPLaHu2DsVwR?pVjLM@FqsCNl@^05?%xMcW zJ{!GT71JPDh;}nZ0gU3E494#zO|}I^`Wc?GxKwsWh+te?vrHQmk+lBoR#f}h$p&>~ z@=v|a%^CI-L4@}{`fAKE9PT^D>G=$BJ*QH+eEruW6*f7h0D}4-$A&)O7c}&8e`Pxq zvixhbb8sNr`CI_C(yERg9DJpIR%>6_&;6AXy8pH2^WysLm-Mny{=#8aYO986qi4cy zX*~tFJUD1xx*q=DuS`imSP*QlBhetR-tw27*%CZRxqSUILvXuYNhZHtGKH)6&m~0vE!L^ zUSIZJ!>wMc_eZL61~w#0;WxvePoK7_1r@#+FCeQsLvn_adWTA@P1o3;Tg|svr+G=d zpmFq)Q(zAc-YGKMm-U7Us8bZ;+z-Nq>4kP;s(lj6qZO7>I5Su*fu(sA!E!pqRggSQ z>Xc^KSTuiz66e=zCOKJ*O)YeQHDjK$%G!+Sxf%yDXR`mRk4X;4!g%XY=PFFJ~J197Pdwl_9XFiABh$KG}9Mk)(dA?pXpEQOyEHhcn|}gqD6F)qCR94Pk_{Ex{#$+b7HI07^jQ7T+F6~4D~nN>nlKr z&P}t44o=kXbD+NDiAC*E?{6qL^_Z_UTqJB1HJRER!hyE%L)6fDgI8hg(vmF!Bx<$O&3htA&ITr`2fl{V#{l1A9sv)j1F(|qOhYP`TkN8{Oj zZ?Fi)kMbd>FFRvO+w_2Ra165K=}GG7xs{xjT68agc4)v7JpkjET&ln6W(-o1yRBp;)>9 z2nW;%E-D@{=5Gi?(6&zXlpelHYnE@a`l~l%q^hKw1$AFO`S z$33kp4oef}w8is_7ryvW?305Y)Zvsd!o@yo-z!v?zCqN@O(*Mk=jyU%Z~aJXPvsa- zgSP3DF2-acBG%YTpC2o&+vA^hOwG&L{4s`#_-nQd1jhLt%WX=-0Ma&?Kjxk>rfbap zt*B{>h`y7Y4RQs?X>@cWy^+Yw?pMZpS3tSQWAsa!poRr?d<~6l*QVZl)_LD*I z6fvB9WNxsW^HU8HEje4{tA*y$M1xGF!ANuAc!l1du(VL%E&wDGc2|IUCm9-pNu`PS z(DJbsCt=u|vJnYvBDzUNwj%MuAI9#qw{UOudvG^Jsi+%QD&k< zel!BL+uQ(X52HqJ0GJ+WV_inGT)V-@Pa06)5EvBnTN0wT9Tjg%>1|21tmS-&kT4se zuz5fCk^JJBQK^1HBS~@-2fN+Jvs)QcBF4J6u0JgjfIazK*Pny|Ki08gi93{PA%Hxs zR`2w((=GrZn1mcyev?wl)5hA{56P+{yWk^p*)!Fh{}7Uf-bUU1J%1@bX2`8f8E!aAmrP zu#H~5A%;GB*%;G#5PF`J=ddv?p&lUEjThXx^AIv*^LMl5c&Jd-hKH`4(IpSyXP-E0 zOmDR!1boVd*)m8&makF+83?vn0c%6?dyU7efo3O7oOo@jg zUJtx+(X6+h0$x!lTa}GBSyw8%qWF{FUC{3kQe2OmrI5E=e68W-VDy%*bPXH`75Zk` zCh_B4WLacbgM3UH0A1HF(D_*Vf?_}O(LAtO*y7>pD%$=3t~$REHjHI!q?u$Y&k+ZMUK#p%MmO= z0+Gzn>X+;k>oN6`wj(SNX3m{^5k}Gm30paDHTP*uX)=Rc0;L!%Cg6EwqKb}# zS+rmjD6mxVg|J>+3xai1t@a$*-nMeQi>4>YL>a=FCs{kh@mFD+9H=~zQVlvRG~<5C zV{2t`CEbXMr<#7Om2qU%CrO9wcRa_Ky=c!HmLBmvnY0D5cH^h*=*3L}3$>6nW(p!J z9!G4=7n*~79eWUfqlFM!y(khhFncGD`Oi2{0|oov_*`9WQT{~kQlHA|sfKoLhD~Mg zw@in7z(ods>#_6H9($&P8>{V(Pb%4}Ohe7~tkuD{?IWSp4k?)}tK+HX*X@RpXnNa= zde=h<2t7)KY$9X)KEZ~Ifc8_k-EN(*v)G>e4a-_YeA?QQwl(+z6O}+zrK{hV2V>Xe z;d^2G({T#(4Yyt|`P7wRF0+Oa*DjoOToAHgun}{oK z@oo`#6Hl~SBKO_kJ2;|X(|;EV{BL;q`tJxW9!e4)5dWqQ3zP`+r9-OLIMZXx%LDemY9W1$vVp-TF@{_R*P8*k0 z4sj+G+`s@=+YnqW6P#=HuA||t?p-Gvp4Gh#;nfHp9tQ~Wtlj##*@h#K*M`(U3ejA% zy7$#}uW?cDbda!Y>l!>N zEW+8Z9O#Vew^x$(=(wLHoq0y(Ig@hE&(C#cqupS5(9E{t_!%>Miw7j7sGwB=Hkdeo zW8*hm+`>g@ru3Ji`64)$cM1zu@D*VJWAXu?giNFS@Q&2D$#VTTk&Zr>mg(t3KUh2_ za{^T(IVX#UU(IFvEm;T#8$A4KEaGocBSV?ZKrf-M=z!kE_eeBjB6D%{K2PazNkPFC z1`rs};3kPLr|eGH4+W|AxnpwVhHllzk0M<5F=EvKZBx{VbLY9h%HspNOaP{B(c@IK zn{v^sDZ$bTS|Mr^Y8^ltlaER*ECHjQr4MTX{1|(-lF>?Vq@kG4NV7|6pzx4q{IW{} zgdVsY?g@v^A$k^(JAQWklHDU{kvo;KAmQA{Cv8*~y(7bNzfTuABU|2NJr7R#iLsO) zpcQh<4Br?Ffg6GDb)>}rK6#IPxY`SO9j6QXY%1!L!% z13X&rl^pP3&kY85u(CrVsa!5s_KWl3Xd7o#5rLc6zo3BYk~_YIWTIT&KXCfMtWTE{ z+-RsPafv#@TjYazzd%w_@)(PrsDs%!vfszG!t&?udYvX$zyqm(HQIHdSym^IzjW1RCYQgY&5e@=(0$&1=Tg<5nt_ZG)prFvSizfcFRjff0((9&MM*9i@|jf~q1J3wG}aWPnvIcsD+XJ2x0& zU4qwIe9ti%mZ3LVj#)95IvhSu`ZJg?fjly+Sb6~UF`Bi$FZG2zgv4zZ36%&>R5>YU z4*_xT*ui)xjmc24&bYe3?&=(JYoKzfGN;*Wpsu=6rzUgY;&HkR8>3sD()FrbMjGHXVW0aP&1}-&iI83a%3x~ zv)Q6On@|nP*jKH{9IC-Ns>>bTQf=>pY=2)hgL5CMRihl)3}rAV&NwLPp544(G=m^a z$+`4HH0aei(P)5YvFgYP0K~Yt6y}_%jJrpT7nIRZtz6>zn_X^H%^X#WasV+Xhx9SL zkgdh^NriO107-D^8?r0+!&vpIWS26Jn-O*$H1cQ(48+> zZGYeggQYK3STz$(vmv3Fd@oLgOa{Un<g%CiO;gqFuVALZ6H2Xt_f~O2tl4*mU%al{!BIO+UOon36o03x# zw9-s=qSA2iB`G^3PYr@!Rg~B&T-Kni!VaNi)NA&Y)90RV!D#_R;LH!Vwzz*lnPp-- zTySDlB{m-xkj{y?Dx6xMdbfO1Xc$Hgl+#d*lEKja)s5l~^tH`sd-Dw`)?95or8P05dF!1I`pCj3pTwWu) zjbbh{+p)4)kqv ztB2u|dzuwAh79}ry*cG@C6MVF#bo1p=Ue5gC6#WKhf(5zv?aH{KGUP7FI$Z^q&BB9 zK`qT+L9HgzRI2i;Q#o=DeuKZYqjKdplh7i;mN7l+KR5B!Yv|!TVc0%AtGRyZs0y`T z9yukeGM2DwA2=hd+CFcns%5D1Yq^a793E91t3DQswO-q&b>ab>;d_th12SUVAQ_8z zK!*^kh|&$Qh;w)~6W#+P3VI_lbW7>pP^nzr{T=zKg6J>dMzy41ss{&{Djk@iYZ|PA zz$BES*;l?#_xK6Q;}^(0l=Y`${ZlR&ceF=KCXbq}X0zA1^G6F{kd5O2laY^XO%1}& z$w>4&6klGQDZ2FaG9oKwT5Q!xDDW0ttCd8~0+COYaAGCIN1^+Yr^!cGZ)(bClZiVW zhB{;nDYB!Q`7R!YW6vFu9h1+YQLIUr3>jrr!;o;2+eZ((2l)b~wxKp99vF$uzzwEtzrFvNBqoYn5?r zW^_8aQeNgXI=qbk`<(KB2cO@@%J&~>67qTBF!@+-eN)@v>3vPeRsMee=e607_$4B$lbbyR| zBw&^i%lwe#=t@?2EFNb|lJ&zi8rZSGhvVEs2|FB5doyI*P=xD`9x;uTK=G^szHJgJ zIsL1vH{unA_!^6wWgq2(7jSG$k=7!cE9Oh74uUQTu*Ny|p(Fbs&QT;nxA$Y=`8ZgC zdjN0qW!VNokuVDa=Ea>pim|(hN@k5V8k83K-p%G7J2dbY+GPTZI*FS>FmGGk*Z34y zzxqYIt+(6ttL`ty`i%u$CGc~_WIpzhCvpQ|pJ4iDC^i?D%{H1ub?c|Ci&pm+?C^Q3 zd)4f8P~lJ1#ZA54Z8hFp)Z60bP5b7$(<}Fk{c)%U{tK+5 zv{xgeHW`*?i01TYUA}g$w&}@eMjmH@!&?{=d5q><)7he6yT!Fu5vxZF$rxA=2Z#H3 z69`5911wy_o%92Lcne=I>!JjN=Vd{>>C~;olS&%!4IOw4Q9c$d#z+d~gV3FaM?1b+ zk;!2H@kmxr?25AJ+$|gUg7QOnw;aHi{|G#M7JHA6B*n#j`ev$#UHo$h19Yc@8T6z* zoA};R{1DLJ{}=_uzrzyvV+4}|!xiJ%0%v^g&ET#M^EE4q8{q0fTo+`f$cdgG&%@RptzaLLSJ#Ra0IfXqiw9{Zoydc&D*_+?Hb2}BTbJJME zbXa*(wJ;^iAN43VEo+vV*k6uP(kG-lmy*G+iTJXR%$pDVlvH%|K>dj_cKCR`+d+=; zIgppxJuu!Mys>@^5dmy=U;#q``FoZUv6#Seu-D}r{eI35%gO-lwio>(ep>sy_whya z?DW+Q>On zIUGz|4TkkO)o}88%QdSCGPR!)bety}4YOCwbu_{927_J~oy24#lSr~^Uy^9sk<~DI4SD44Kjg7O^HYRS4CpKJTv#F%p<2( z?0FO|pG=&SCD1N{3jq2VX`R=t^KK$M4C?!$m+VNl*WQ3lz4{WAj-8&^f=(&E1v#M!6gtA>`C z*hZ&y)-LGnW2WPL(;pj&FSW{uqKJb&qQB>lh;i@33)M&ikycG z+>P*-W1-Pn{{zL$$D7P3OI}a28$^A*ziFYV@WU?=t26Ybq@;kvsvqd%^v-g|LlPtW^4B?~X_501H zXyZ0P~)M?8?NK}S|XvasE$6{F% z(%Wi*coV8tz-0EDYQ@Bq;FU)3^95QSN_A9B%yFEnEprB;!~$*WCNL06}`BayH32j(qB0-&|dHJ8y1otP4wE6Ww$qL)>4DW&biX!$y11>Z~Ac{acq@ z@~*}-9E?UaT|ILLYfr$~p{O_o_Rx8NX0PiP=ceOeNJ88)Ba54PY2~Q`Hls(9*(+2X zcKCcZN+&v1l6|n7p|*A0his%J%!`Kn@kDGq;;ko+*cb$c65nhH(dJVeU1IYS-)jwF zgAu4eH6j({8km{GTmzNCe%>_5(IaHw$|EH$gh7YJ##C70jXdCi*Cy|b z9K%rB#$e&g{yZ$|GyRiAF`r2G3EeX2*G(NmmpAG+R%K4DcYe9l!GboL9_Oj^Szb}$ z$FvHEd0U+P79IIX@5V5fECv=o!?h1xYD&c%kz-LczKS77AK=R$q?Nq9B}5J*DRK(S zjxdaCc1D?!XO_X4U#OTPY6W`C@9N*jQ5jq{T4PzpVsSFA5qjAQ5y*V@twV7V2LF^_ zcSoVyO#MpAVLqzgaKAwZ$}2(!Cwa?NY$XxkKZLQA7nGHZqInyZ%xBbV@{^+bCHo{h zWX3=67Mb@_jzW6N*Gkf@PL~ub4x2uh1|_G>M|P zdlt6zpSQO-T)j^J;Ro5Nyf_l_2K$d3o2eO@$5}5ID{j5kl-7S2NMx$=WH^%*_R?O*&4j7|YA_D&?nF zSv7pwF^p5tdUx48LNj|*siU9*s3VR+Fno_xHlzvXpT+L3IB!SDIkGlJHtE}sx@T1> zWml=18v`aju`x_QFS9pg=^(vrXGN8a?mF4~xKvHAQp#R{QYE8G_N0``Syi%IQ3U39 zv)uYR6p;tDQ+9#Hp-KOQZo!eMlqdP>Cx94Fm-Pssc#&fnPD#hBrCVb7K2Kqlc7G&p zcx!7;K9du7PGKj={$O{KMZX)8|G#*KmYd3knIS4!>O>Oea*FW-rW64o_g$&>kSB=P zZ{y*eJ#mpj$E}e1AT(D>GvwHGDybD8vXpgV0H4D@VL^FIkH5b4lKMGvcA+qA;UAUn z$8^3u#8`N@>MKsYj3B6DU#0W$VejsqS{~4vCg0;s&XH#${B{2~^0a&E-Agz9aH-hE zXFCdd;@(Da$Uf2F$y}Es)BaA76kZp$_V(^YRRFDl)4oK4KFo0)&Z%HdNnnD`E7dr! ztnx}Vndg+s5~9O-n4t53NB#uc>USzoQUyvSMS&CDIh0jLqpIB`p(? zo&H9%6Yq{!-fVXI{j}NX_Z1yYbZ7Jj&Q5=XvtxDzQ>3_95?GDq>ojjtHr*;+LPN6U zR-! zSH{8rN-6q3XqfN|c{IxDiDUns1u@rZK`caplo`4UuF`9T6oMp$Bv45ET^->we~zMq z-l7XaQ=E}XKcOH?x*%r7CtYA=cLB(*~<6%xQC;RJ#6Ph-7M zMvn?{ELm5r|Mg~oxHuq?{gtC#O9x~TR%14b8~`?;kdJlE>`6-u;}4!RZ}ipi z#t>j^IFs=yhN@jhBGqLFoNsD{=V54-V~)$>ZYi~$1y0qG-MJ;XsMi=j!LW2 zxPRr8w3TT1NA1P0grgg5hAb>B|Ff}>F{xH6kBl@bR~;pJlTy_b)G@Y>N-xc{suE`< z(o)EE1_4Tt>qD<^lq)gD9UeGqu=PM53GS1KTqceCgRskFRQ}}%Sox=u!7oOH{pufu z2$laABSLi@A|z(3nmt?TxFvyQ^2)UY}{jBkulb$Af(-@q-6)@%&wU#agT&hw!&I$TqC)dn)lkr?IR z;Gn$bjIVOq!MbywHvOpPrj^@|w>ugQ%2}Iq_}N>uSIXR^y;5F_)05H_oHnU%fUm_Y zpjm~Bj>g5*ftu&5CXK06$B9(3T5<+vA}wUqNw01-bjXR0S(4|uRvDTwY7>YIuCqgoS4wHEMZVqVrZ zP&J|SKO4+pc0X&`Sub_t$=$ed+l~_rAt?dg98B2+laPsS4(ISI!KoQ=Qsk4H1A1u! z`g}O$k_D&y*>D^SPUW-Vlr1>b&xTXUM9^~8UW3mc^S%LSxE9Lx*3cWd%kjdb5Lt== zJ*Ku8wKAQIMo~wjQ#G5a646~_L%88-lWAmmuirj}^#| zb=*Pdkt4N6Oj{8GAV#h~_J+zlB+J_XvKr86md5>;O=OC%0@8&pGD*<44gC zX+}l|mVQkKhK{;5ZQy7cFB)y>$&{qHcha5howk`F``cO$GntE4Ocw^b8d@^Q!R+1)gg2)!?a}N5!Pg8f(-WubUW-vPoZPc9x+P3M_6CeeQp-X*%}7uInvVsC0+o9E=?1-G;lB4maXXt0#Xu?u zk=H%ba*pa#ycg7Y;k(p4^U?V@I&WOagsD6CqXboLx+|tp1w=!rcqRV-8xYm zH2v)1+#_i(a`(^!KV6`T)G>0%Pd}r+K~m?J4mvRZNWLi=cW71Lez=CcF&fYA*&2C^ ze9*VfuQ3_z>>s#B_u={{F3x`-mEcE11^64)RP9l9H78rVFVCCx?wfYA_v*6MLhT)6 zN}Nrmq!yc!vOOiW&z+Jd&B9k6czJ_?mz=LXaGncT+)Tb&s^p-JReHBk2v}jqv&5&kQs5XZbL1KyC$niT?mM4G=SyB-2kMgrZwcwB< z$LxMoyse9;-;m*oq4y=zL6}M}U>DWw;SqXvSr`3ffOOseyGQ0&6cAz*pvl%N zcPpA;e zY$KpkG1VxEvTa9f3ErOB2LO(ymZg%gcsSmv=fv!PD{?_d#-Dp$+UF|{KUUJlV>89WfpgQmm!q-4-L5^eUCh*MFBc!qJG;3E^5uHe8*?a1#&*2NsBt ze!xw+duKE~N;d6n0>Wr|B%6-(^7VA29BI)Mrn?@{3l<^f_`O0*I9KTWFXOoq>&)>= zjd$Ipq{cg*T1k0=CHP0@I>_sc$=V72D6b`oF+pR(f-=8KxX1joYIk7xwA3Ss51S{S zN~+rNMr`9^GS6TU4IgTBh;g7Zfktjo)U+F5r#%_@&!B0S^>#_T5GCbilMuuvd$LG1 zUIY%n$vlmdMTac02yh=0;od&l@z|s*6@k=)xmltkGJNV27+T94qXDCU6DTsD#iC37 zKkr#gKXndtFzV-X#0V}ale5?s>a1S&jb&~7h6h3Gq)416bO&w0inZU;MoE_k&esY8 ziC0wo?dbMpr3&;(e(AlLB3;No$yrb7sm^*z<)>D7sspD|HJ^`!ktP=pm+fgErV@E; z{2HNWE!T8|@niR)i$O;VLjM{MwGcXfFdO&b{|lyT%~e}UQ)#Qud&u>CJV`K8az+ef zecI%F##lF$RyRvUv4aQ9&Z)oHtM~)kJ!%&U?m3HkopT~J=T3t z2MVOsD5)J4LU(2u;+cz>yu5P!>@WOVAf1-;=ap9_xDttV*#B(?_G_Q28&xPjwL;~o z6{@Bi*R|wgvwgCX%3dfM3*13#3Ug0Beq6Yf3%I<<7>-@IAt3jMA%WqN<{E;tqR>7P zBzLF0cU!)XRp{=5NGvGwDEbi%dNG{|59!@*@DLXUDm)=x0o#erCY{8#jOPk-fFqWl zq<3*#kx1~G&H%uy&b`R=B7 z*8I8Ay(oxhP?K;H{-UKR{&^<;VHfMQoAtByUyumUJf(6EYnzfSeaecZz!HRPPuX~T z2~$1$TQ#HGOXxE<8^shbo&*m|18G1juTjU0-%(TtyKn&;hY3J^V(D<<*zlr2aWT}h zn052w=#}yzOM0ThuNZ$a-M@l_mGH`zXzICVyH`N4_o)#(5ZW3)3-`GEcK8t0u($go zj{;}xAD(i1@ewn47);Z=q;{LGS`hbyXI`d%hTf1L6M7V96ngNtr57%;w(Y;uUQO-z zSKO=@S3e~;E4tpa3-lTnKed~G={2vOwm}tmbA#$g#rO!=Yuq$0&w77((`x@jaWO>h zzqYsdLHX3D%OCu{gky5pzn%4F9w(e5t?1pk<56$8oSSLE1HMbahBXrxc4FgUK1O(O z;*2?^-Z|$lH}4k>I04k-lifNL&#%4`*82Sn&n0O%mUUMHD7L6fiAX91TVzt9U32QW zunsUfD=x;W>l#!_pM8B6ryg^2>BBNtdZ1{-m-Tl;LIwAvPwBaLp?caYgZ(>Bje^|a}4_2 zHHU1em0LM%1e1>PvAws!T9v;BNkBN8kN%&(E&d5OU-?S&Sos6zaZf%&TF>3k84T>k z7T-^kjQxAB$yX;shQ-(u2u^7Y!C6aLo;0 zrbu6l7Cng+ZM0tHjn0jVa;QjorQvRC6zE{qk;__FW;;%!cVniyjj8E=X_3L!2 zy7{7fmH8ZgjcyO0hDn=ncmLyQ_y45#zgpscu;P8~YdkvH&lIUohVi?0>#E!BImes4 zVr0D#>^u98Vj|vRY5*q2}-y8LX@hkF+k>phAnTd<$SqJZ= z__t)qPvOU9^YW4+i?y!#@6R_FDgEYBTE!iIh6Ds>Ev_Ev=wPvw`;2?TYK%sTV`&#D z8L%wy53chx-Z*8~ZgOFyrA@yuQdWK~gN?<$g4~b5Jni=KHL>UqL69$9z4a4c`8pGy zc?IXiO+3P6y!9&i2NJaP-FN;6UCc_3^_u$!1&!1p9Wx)D?YHEo>tIMD6#A@s{nMdA zKf@tQ8gtKwZ=4rf$JCW-8K5dp2lX-oR5=sWrfRNV{~ErTO5|tXzCTMpZ$s6#%``wa z;&|hC*_GGoP1)>?xaiU6a?yLwpFbD3H}9IFe$!&D%U~hev*iMK>rg=M9u?Xa&oO@b za*mS(_%d4pM;wY>RPmn;kkBn2M3kPxr3w7`VitsXsEgJA;{)_yOPp~SK}8=!DB|9F zstksY3F6m?~04>GvB_BLYL|-1u0Bb`(qsW=thB9ir%efV{JrU+K;o71E&uB!PSThc1pi%!=mge4KQKRPWkF)tAq>B)C6OB!F zPT3?9$8t@g0kYav0FG$Yr9U$bRmU0k*%1hjOSm*oGzy=CEW_2r8;U;0{tSi`TL^V^ zLlT27!zk*w0sEQmifPSjxbz6zX*fZ1&Y^5Kirj4Du0}qIzEQ|2Gc%7Hly?1$vK`L!v2KzS6SDO+jbe zWIpy5p57aJSpKZP9Q|rc%x|)ti0l}>Ai9Hm!3SL$r&>faT2t%4?f!_` zwX_j+btIzYI836fgiPG+6a0!e)Mgw4-UspO&!P?EZBB>c2(7j04aBfGPC^83SrSCW zfFa}4_7LsOIST-6#5wUs-P*c>N#pZyTSP36Wyu^6C|oH7u>tlb5yLqDwlZD4+3iFa zPKan2)*5q0=CtYv4D2H?ZomG5TO|rYuBR5WCEV*ws-aIKJhPOu1U`R)oWBo7SykN5sO5f z`Zlp!P)yAHEF>U5Ynsv1|L8)my=@Tw!nP-*V;_6peydM%)5O_`o0Un(kYEMYS>s3E zI;{IhkZ~VrTlbN(jQhw@%7p}X179k>>`JocRuX)TTS<_0D+yNJO4|0Vq@BE#7`#yM zgj+*k-x`d&O|b56V_$88#MP#qyxL?^yv5v_i0FRugxg5)N8d)G%Sd2cMljJi;KZ>v zJ^pO+oE*m&lV}SYUI=4$k7a3nO9?*bmJ+1hQi9|y<+ZweFd>Fxfc>`^6vR3S*4;!< zr^7=qF(?JmUoP;5gKm!S`qTHksbFjaGXE&Of2||yCni~sTIB`ixzar>JygiB%k*P3 z@PM?eyc`9qt}8*xbw##qeRL(687rT5y-8mrv({5?I6>M4BC&?;hc@niMo@LDfdXC- z#4a?ct5E==)4KY(ej(xOzUmVRJrp%&WDTlk9TuHe0Z+8Zo37Z|3}-(HZwz~8i~EfJ zF^2!WRKCol^G!a>fu8{U8heANNkI%NA=(xc>dlvnRp@4}pKfi)3_^5GWM5s>fcV?e zTY5zyo6s}5$!vLB^f)&768M zpOIlN4wyI}_X8Zx^dxWJPs~2a!y;$x9rF&^Htvw?j-XX{%qO1znS?$8EbFov*q6Z@s&09*I+L;4WczpcMN1(0K9c!KFrXV`4b- zm!A6T>cXu)zFzcvwTzc)?&&{i|>vrL+`E#q$EVQo9n(y+c zO7goXet;%th_kYcC`uj$Zkk+N6y+UOP(_j9EBAMU{a~ObL&886%YpFFi!Dr2PmZcb zldAMfpwDO);0-tuN%b~r6VzbHn*7WC@Zn}`Iv`CJVO!);{T>m_C)qou>XoJ>3=D;jza1a zJsfohl{|e$JuP*TPsHlPzna0-Ncsju;endS5i0iTo2!Pc7jKo<_~36+^J4>o{$v;N z8hDDoyR28fHJz2}^nXxqCG=R#wWN<-JaZUu{86%jmjry7On;G_0kwrYf5Rny{QeGp zxyZDj`$zWaF>(j!*AZ5F2&8*}m-K{CEm=V#m?;WSa)2$C00_z74Q4~Uq^3&)fE!wF zDiffg{A*mhk%#ltr!MT2>DDcJ?3Ir;2_b$kFx;`FzP>VJB>DP*?X$HR1V9U*8p)8C zbJ2Yl32`D}((Me+d*JtV1M+dy?4e?AH|SvS1s!Z}(!t=t(8)L4wVE%J>NPCsV>Etg zik#D>6=sj3>Cn(Q>c-Hhum#O_PMUh5Gactyg);!j9&H9F>X9kN**53-C?Yax?>M}) zCgvCwl(TS)8tImBgG8c7jsLqsyqS$R;oIJMqB+0!7ltgyO=9 z`4DLC?j0aD%7%wQSaQnKH|HHqXV`O@=;AG!TZfgH_~_AUxtL9G9mv1c+#QA@yhEKD zB}_!WR$P1HPyNYqLf2U-Y8s)mX$VsX7oCC_`|mw=mOL8|VOa)lFboS^8J_}XOSt+B zXY3)3W)Rv_GguJK?(5!PFPbFL-D0ns=wI6XU|o!jn>xU}!(0+`-2#S9seribZP%j zbXZS_Ydj*V2gJ0FnUA)qxSa(a}3=U`yk}^@sglh6V=!{Fku%hD3+1 ziOH8IN@lljX__G8aim!u0f^ke%iCxtdJoV53A^oLk;7#5G-OW^d-%^EMZIF=tUd@~ zH&2Od?#3ii*C<#eQ*O@OSLIz=)6%=yVm@BpiaoeK{x>YXN8JA&dXAjn#grC$56*Wt zK+k!>-?Zhg-L(@9csq(!$Gd?a=`>Z2UL5SGG^r|*=JmmV6-QB#3Moj36k+~gJcG@$ ztK;P)Zpoi_kG3wr)|TJ~|3!O3qj}VogxDhMg0S|&v9(I>uOABJydxUz3MoYbbW6r7 zicdV^x|(};=*|~(o4|Y&c+7}tke(9Q!6UH#2*@Bwvp(&DSs(YjbkHjXl9{Ao;fV@7 zQiOf;fsdA~v3u=sOFp95H`)}rS*9$EyO9%_?r*JE3;V(7ISD=WgIR$t?pv7{ywbsD zjBYTUeXicEx_EIK&t~tJb5f7?kgnppQ{Q;dik_|LX61? z9RuPUB|S~!0HAM@mG@!3J(r%aU>x*-Hlwkl42**q4!wus_SSPc0a4diB^v0Ke6pYx z)N=(lCuRE>Qa3Y><8vLr%!r`)B5E^3AExicHCB!G6$fGW!(b~`c2+|)$1QJYQIf%T zL!u?8HyeuQ@iBwfVy!U6Q8tvYbE>sMkxY)mnU;fwV&Va>@<_}pqx|lVh~l*1_%uJE zSfDbCNb_8==sxZw))JJ&TB!tj7$g)Ea;5_KJf`=hk8MLDYS&i-(GReUwY%Zm8~7t2 z3+h}mh$}vmQtxlQsTUY!qh@Zj&Xkq0aCug=FUwT_M3p3^u(BiYT3uWM;&5=x)fI2-7*RkJ^oKhczd06hF2M3K_C#IcUW>leN`vV{^2w z@rO&wH9h0l;Dyj@>;v;YapxQij00ve=dRd8RUjwF4pVArPBdS2bUZ1I?TRNf-wS|f zBkhlx(@z9OkB!|S8};^jc?VI~zT1 zSRl+!iycJk=EFR|+e8FW2h3w-CbPoTOsuJ+#4q7{)7L(oG;sP6H@7MZXN=|EFic+T zg47xUrum~s4qfOj7Vh9qs&G>PIZEZ<(1Y@c4rbYt z1S+Cb@V{?QP;_k`3aYp%!*=270h0sl_c1=m49Ru{6_W3OV!}#MCT<4L_cNce5$Q;nowsGbi>0Yve zYt+Le+jx}mEDL~UA4q7$=YAW1BPC<)5##ELpbhQ~$y6}mYc5|nR*FSrJDE3BkrnDe zv(d3=4+e+wM+>;DA#5t+T28ZZR&%wz>2f^A^EE9mzNiV|ihbu*{ekOZs5ACh@$IoL_2WYtEMM62w<;Z8Covvw_BwziW@$*dh8_d#OZ z2Z?c8%8o`^OxQ`L#A;irkVaWd*h!|uYFmnuMp;bQNv6bVTWXX>SxneTrr5P@CCiQG zoMYTXc*%D$g_QDVuV>^Th6+*ihoNzZA@&wHxyQ!OC+2m`EC!T+FFgm?Cp;Jk4%22s z28h2YN0KBX7A7Sq@gCB>i`f8ZAqijLzz&ZvO$>J7X5$ZbIpocesL52kxlI?lW4NA; zRqDd+@80{v#hpxOPKQVI!u8Gne*TR<;bRB~{qkYAQmmEtZs!l)UKkAa7*WGLpuu}n zcM*CMzc~2ZHqax_!9f-Omf=71FMO&cr&9jLamr3*->Du{_P;4Phvka%jVS#yCSZwj z0U^G*o4YH^t;_!(`T^bx?FsZ613$!~Xr@$bF6@abchP}-aduOy)SL$qdV%kSMW~Cl zPhGTRo=uOoEm%jQJ{3)5!}_z?VndJwqA{C41|)~hHS(g2PUxF(9bY_(#$(?L3iyIw z#De($E}Jt0?U~K#VoF9*kd|or9ys9&Vt(?sVqIR4LPE`D=u)e z>Hd(g9($!@)IXwWxjP=BrOSJ^7|~-yc?Fp=PTWT+pdYfu6mVOC-(O-o?qDzrhEnQ| z^9+30Bt#g}cI?&F8*#JjkAa)I00wG`@2IbCF7o(32UX{@z(qJP4$r3!1>Ok0PY0f! z@oRPKkJjmG2JD%4q)NQy@$3zB0q5n*m%Fr*@i7*^)*xYo2u2O^6^eYJlV31cHunxi zUm`fZu@1d}Ud(Ah3D59-N{ldmHR2XyH#EW$bD%HV^rlJ%N0{Xj_Jnu-93Q9D{9xg+ z?BbLrvnXd|gnok9rw&4Lh4#q;C;DyRPNZo55jr@I=8QDlu{{)I!F)>jHQNt`gz5-Y zwmG!>4sEOXB6mHMH$&u>Q_ZVG{ZV#uMclX3g*zaVrARcy<0UvloP}{{ta!4VT=&3Z zi5qN=KB38fgoDTN5ceU9orFu1(ZMumb~0a*mUTm}DZNz-QFTh91C#+SfAtSuu^>82 zALgy>lxS;Cr|x_@jKHXK34OO95T~;FF~7wU6%%kAl+yTB$7lb2&zGeJI46q5J4g#ZSwL z8*B-uSQa>=(zZ?=1-OMY$;7>^yvr50wI|2(B`U`To+Ob4(psRce@D8m>hV%LuD1Tv zhe+*V&-lS$Ifj#7YAw?#q_weOX{9Di1Sa;;g_V~(5O%O4O!sEa4P5p31_ha8^oW83-9(edb4+mcoyO+4n-86ULq`1FTaFi z>5wv;VDCyVwwm0^Gs1-$*H9e>USKi{*gE=~{R~(0C1;3S9n1k+d&MsW}C-MO8*gqdCpQy{{vE3KUo zi-%IOC>^$JYVQDfQqoyaffPHL7tZ}&-%o?KxC=b*9w>7f#1HnUcN9-`w?lgnHpP?` zTyBEdVzzjk10lhUC=3+|#fbH=4-|;AZz}i04^){(lK#9PteCF(W6KE4gv0(=vjLfK z45;H7Y}ovDs_wt^VN;Ge#=KTA4z*sG8Xm zHp_ZqrO4{y=|&eBy=P^}4f*vIqc{ge9W4u&n(!MG>{av zv#Tn4L~6ocxj(-d1wCM&Zc*AyU+_h6iXH~0s5`*2=og~d#MgJ*GM9@#Y;r3x_xEaDiE(mre59%IlJB1K_nDs`1d&|}ia`0xNelej48YFi=s8LLf-%%k5 zBm6~D;SLO8Ij2#}$>0zM(PP zKvN>6z=aRi=1u^Dj*3%BoH@1&FT*1G+=jB9n^y1g`mEXOzHT?0Z(HrAT@+uU-RTq` zF}d(*tJ}M1b-NeMUi0d#Rlgz=27l3$jGrPGIp~H4qn=3Bp>}p7=%U3J_>g&dxUVf< zRtD7rIXZ$WlN30fzk+ZM`1wpH;h1RX)+l8WD+STr21~0BJ@D6er4$N zNltPr#^4(rNhM2D*7Jw0L5GRG?Qq8w@C3W8H+G$Zv2F$7Y*?=l#~8N6txHQ>TI!(( zIZEJRDzp%2lA&Dv+@aN{J9~)A<38!i#Z*us(2v(HQ z-7*!^2zjAU9PND#NTlo1q*VCf2Z8a85ah80jRmm$8v!S-PT)L=M)nNma7x zDH@URF%Tn_w91HD#2B{Rt(5yt)p4+#ED z8IqW=V7n7}V-pzGymyg3Mq8A~9|6KvPc#hM1TrJfKPcEA1ABk(1-nQVLQjI7djr*> zi9631EvjkEk!YO1BAJ`wH%5GI8snj5+;J#$3mHeDswujKYBxL0Zm(1SxoJ(hc-!c- zO^CSSsUY88U&!!USS1r)`@CVnLwq4h=MLi#+Yjgd7*?@Pp?IEA)l9fIm-TMrwFx=y z_p4&~_JTXkIOjhv$F6=>i%9V$?$Y_-Pw&(Fhj*!bgxsI3es!*I>YWX{B?Nb@^x(e0 z-sRVIPwt^F=-za84=9cr>Utv?eWl(YuF!sq^+tyOF+lUsPH8kqWq;7O}I*$Fkz>@me6o=1}0O7|tv zlO`}N-wp9@Jt%S*fam*}^9dmErk%DAKhmyQwGaOh--sCSkOUeSxT*QDxVd}87)zsD z_;TilMlW!sDBfY(B5?!9sU1z--yc_R@P8zBxZMqe5$lJn9Pa}#i>U-5wF7Z_@Q;-Q zK@IRpghB2=u?cN}g7?5|C<>8LJhA&WB~M~=GQd62#NeqtF(xa%7~BzDlGS~PSfFc^ zS6gPbsi7bA9s3EF>~(J$Pi1gSCr$$yA3RWBjY}?Z`O1v_=CXNY9^9xd0BPhS1RW7+ z130aXp;-x(qoSC?;zW=?Ccj8zybMh|GS7f8r8W4Fohwad{3;EFO#nVpDHMN5X(Z|@ zXb6&hl;#g_-S^)0^unDMg$dy;%IgqQYnF+lovpGH;A0WplUro9(ic ztK*B5RP^*h_s3e^5dW;&m+Cs%LC%}#2CJg+kTI7* zUAQjVX3?!w#GN4JsP{1d=$z=8s>!1}(g~&CXoe!dgUK)?IbF$4?=MGl9ewT7ww!va zbpvL>ny>By&dALoh>SjV6d&KU++j;r8)edlS99rBC5dklU<0Y7Z$#8hik2WSmmo3U zW^kR@bf?E8F+!5CX&DQ@jPhhKM|XVQNy?4{{fKSJE^V-oM$bs7h99|{L3}v_YdHgZ zIqk?mMhU&JRtLSkVa9t^wY!J{WlO#c2Q#Ny$c~ z)W_pU^F}3JJ_4T@>i(=EZ*l8w)~nO^=pu{qBIj&)OyJRlQ`uhzh#jPx(>lMVmyqyo zHXbIc;huq$eL93g4Bz2*`DUy1Th!o*fnRfJEj^T3S-S+=4114CSz@?^)9}Ht_}np) zl~+W4-s9$dI4jjkaLSZn+Td410zH=DL!xpHrI$-w&WXy!4T^VOyW`QyCLPrz9dX%B zmQ<7KDAY|V1Tk2b7){e$zbqMKlx#;O6|w_0m7P!spd6|p{%2&-?%a-PsHG8G*<2Jx zK5d`KLJ~7Ok}+v%-#=uDt-4@rnH+4H>-Qhq56vL>*u}41{2Dt-j3~DNovreXh!ScO z+KdVSo@QQww@h)XKJT~VRQrfG+Iiri0C4)K{ag&C5@5(WvRscg4{jOM<5l^FCnSD{ z)hd)~^T_i_2J=i(af^({10qjHkyhQr3Fn#4=@;3|hpKYSf}viO=&$E8fV+b7^rt z^EMmI%V4x`I%1`eqO6_|N7?OE`3JRPAd#Yk@^gd|clL7ex22C-614AR5IiNLFH+)` zc2XPjQHzRzSin@oKrcmbmGbE6tAJ7|)q>&9mSfv^MrfOEg6)h=b~>+RuuJ*l)OwnW z)4qxs3E)7xetTt*|91j*=XXPlpUaM3fz;ubzcZ1KU@97JGgf9#3h~CY4-@V0l5U(a zRcOn-Zq@w=-ie$wA2QG&1KX>()E z$tO!aOuaBOjI@;zWM#yBZpP|Z5#J1I!@+doG8K~lQS2yvuE&ma+e2oOQHY#h>X*}R zjXKc_>2zpF2W{amN6bq2;*=bX2~D{ureJTfELt!nonIP9v3XOEPGuiyWrVFt_-F>o zws$bqS9>^$ueM_d0bs{6v=jcukWP$y$(FtcsA|@3jw%tUmN!x}6gJQazCcD{;G`@+ z9x(gn{o?v3>A55&b~A@NlgCpUS?BQFzughm1rZ8WDxY=v)xP6B#K}Imoy2&zSYx6^(X7k>$X!(XQH7PFjQSbO0GT<(XByHP}WNnn}3W@9=^99y0h8EY;ZukQAJvo8Kv{obf^6&5i;JSNnIX6f;*9J2Ad9Q^h8wy5O}!h*&SLg#y8Cx8 zm=UsZazW?>*+jNV@8+f;+mY49ldeQOJvrIqKhsRTCo%vh75p`%5a`xFpDvG!L=qwi z(&3898ck)zvc?^aR+r6iM^oyPbCXgT9jgrIr1^`UMWnT5wo4HOILoETmh>oMc7O~= z42L30kJ2n-dK0laU-~VdC%3}VlZd5(TOg%-=}5#@FQWncuWyb2OZh*x8FkHxlE1FZ+rV~B!IiyPu9^zs#-2Q)B z!~bXd>y1uXxj$#849fKa`e(qnB+6~Yv3Q&ajLsWK!w8-az za$vE}dxaa4?{!QJ?^uY3HBiV+Zca(X^&F#8lHlf8cs{)@DfgIVa-NVww*}9!tAR9A z%9Dhg<1opxtpB>!pc+mJOwjRX-(J+OdatiL-PSpOzqxp4STgf+76&_OI?plOEtDb6 zjG_P>@zLxAEu&(d_TZiuvZBXLzeD{r!K zX|u$5a?%TEFLL5;D_dJ+`OQ~4?=DPM7g(26$zsW%=3g2nK7N4f|=W-Y<1n|K+of`a&C^o>ee;6bWw^d zUdS!tgp$!9KfCpYz$*`PVqURntn>KDDn_0@R`H3wg{I^AZwcUtc>3*woaUAoPx znSMrosi_2605v(AnQ_}`J0LBSxR zpD$<@3A*awr^H<7wN#mV&y8F}kOe9j2=uc7$aQ!3P!MBpdb_yeUxaLG;R>*xB#nV{#HfxYhK?X^=&uVwAO3 zW?{5oh%F4MMWwb+?qEqpbZ6pLN)F(>`s^srL6>#R$QR72r9Sy%g4*DbSEZ{Jlh)#zJ*>Lu~ z8oQLOU?&E%0FEG61KMfG1iR8fg(^8b=x{P9AC$`Xs@6{DvLTv(mHig*3$xz>3={(5 zeP%iF7XxBfTfhn<7=TkB4i^EiSoOE{T}VNUGn;h#Mw7SWauvSLb(60Mqk(apimnF- z*sBXFa1-yihT;Ge{JD$YB^c$GKeH10&AlHk9BYX${OSAfF%)mlx=wtFrNiFJ0b9hrxisW*NPtlRni8Kv7TmJisuaf#Lw zxB-qKp+?I6OU`5u zNC-aE`-2FImdh1V8rj}TP`g6f7SI1PobhfT{{h}-;0?XS-RF9MT`B)@%4O%E6qo<( zR}TJH{_{Ug{_|X#+9+%hySt_jRz9-hUSNN2odHulJ;!Qd`#C*14SiQS79q|yn877H z2#ed;#MNx!$#`lKlyCUwM39z?RW=WGqaxe#F7ArK*mI}B4Cr&<*@hj4V^$40KsS=1 zr>X<@=TFh24F=U7ERCv>h^pZRH{Tu?fPS2ByPPxcL4a1`V{V;_b@f$FK{^? zc#lQVT6~X@q*!VrU4eLLB!>?&!x?m8s32e*VxB&9ds#%SF1-D)ROcSL?1%4p(jt$n z=oqj{0Y7}Sae0*91%FtK@$Y{Gvs;{LIJACL6~9Q_C1qU<=`#ghmb8l@Jx%1LT9F`8 z0>)r+dbYAUPudZU$oUGHXdX^!kKw0#Z-(JL?zv@Ckt>b|~y(-rlrU&LFC#BxVLU>mmVV-#ERJ-(UJvTD2W=ZrGnwsJ%p!_ujttU=0*VldvPLEpL)7n`C8Ct0Qc1OS-SFKo6%Q zBo>`{;4a753N8tgslJu_qLS|Eyv^OSCQ(k@YF>!`jYiSL=d5VVB3ETs$Z` zB^WjThhAr!Kesxq>nrgZ?vdB+UyeBWB=+Id4^FYPRdRMK4!UiwZY}%?(TFYOLXBC06yq4S?7*knoD-ydXvx!5w`8pf6IPE0?!Q`@2pREAK~@QE!0T zDizCR#E~L!e`4ghD3S`*0=J0&5!dW}3zg02_wM^Z?E1B@{vtqLYXLz>^1>HR<=YkA zl)U%U9UO{BQt-%6_}z{HMC<2KL}a-O`m>?lUy1@*z(@ig2ZMJGd3>PHbMh7%l42}j zJ-P#?GBO}qx|E+|rBgaALv$cT5L4==dQjb&`0Tw@JrfLr4?1So4zBr*m4$wm#ZH6< z_*}533UtrAm%X#*>6=&3zz7np6a|InJ@=4TEAX)WC)Bn{DFGQ%IH#iCp{ns&y;~=7 zj1bO#sYHtnm%Smo%Qk${u?DI;lyU5eG$#^}($*b&l`I{Pi~WYC;L)BrW1$o*votg?hvT?wna5*CdPJ#Dn18iT8;p%H^k1RHDPuoRQJ z652)U>L-Fo;G7bIBb9jesKs7@!Xi2k_kmEz`&QHtE`T>(PEdA#^`Y(Ewt~ND+!gw%QWngM>-ki}W0)NT=O%ZrC1vskmHQVj$ww!P$waAuQ z+H?S9J)>4ErAU|BkOgX~1Fv$(U z-4T@wcpm^8mJaLW<)QRY1#IeaOrH73)k6ZbBPOg3z{5iO7T9quRggzDh>yx_YwP7< zN!Gsf!-2zfYKe_^+3GZ$-s>}3=yGZ%Tqhe%$gcEDqsl-{bvLr(Y4B}FHj&tV?=D;2 zM)#rzo44I1d`f~asY9)n27$Kz>6^|k3_;BdsSdMM^*K>(1W`*)&G~t+e(|>cO9!eS zo~P_jEP)-HsJB<_+{1ND_rU0Jpzwes!hewmo=cH`&N*;dt!seV~YshmF1ZFf4H zO*$L_7HTGDxP5ij!Lx&?Got5clu<(A)8QyVrK}=loay1}G8KeVEtN6|0Hb}ql(qjI z+W+`)Q93M*(qWVtMl}mYS;J8K%^jeDf1T^W6kExsoZQgK%7Z{u=QO!X8^EI=^40b1 z>2_L5_&B)Q`UrjsjYSQ*wRECsBSvpSy&tjaHK?U&LKbDw)z$;9q`^h#ZE zwsOu7KUAE2I+TipGRT5bLHu^M*!6=`iq`nSBt%X%u|c};+V7AO{nTpwl+Jaj63|U2 zo%1qEl+zh6Ribrv(M;#OR0(ogmd<_YBJUcno7`I@Dsr2&S_aK@($3Hp5zSQ$rYeJL zi*nmn8J$(-6q5@@Mk|Vxb(z$~t;#NE60a%|ubSGq9Hh=VVVvY~TtBPd*e7~9s{{`B z%?-_GRuLd2(1+F;6so3dCS9)m=IRPIx288M;L+aSDp!pKjCOUBIlz3{Zh&Pq+AXzw zvdCHUqW;U)egxB+rI%?~L3nxfrbBC8OZHh7dE0Jvo06-~D1x+~M`d*2jJ@<=>zaO| zyRkR(h!rX)?^l*nq91URX@FGazW={MZvR*EL2`q zsLX|GtI9ZBW>v4wN?c~u0PBrL^9Fa+s~e14K&R{4(wu}9Qg%Q}d3)=xpU5pzdofsD zJhi5cc0(?#UUQkFufXM!KH_iCt!=}dbNWFod1ZLpY&YsRxFj$ZJ}OH2%u;1ts(g@L z%85$t=gppU$kdJdGDz(UQnn_4ZJrV3NZB_=NhTfXcV45IK~5{#rOryS%nC?nP6mZH z%ak(&31*p!?hh)YsB(w^)Z5{Ibj+nE9v@sH*2UA3x&%SdjB6<;&US z%lU0%Ab4SqWM;I?j)e?Zpxx}Yf8hg>3cSo`LSP1^T#c82n=Alm!s;gR4f7lIcDvPV zUtD*%6XnBX8{K9DF2(fN#bs*oGHHL>MYGb_z<+pU}Kb(_njAR&~3%Y3JKcHXXEHo3%MyhP{CX{Xz+ zH{{&Lre6uOoUOE(SGnb_Gz3<;mDEL1xz$wUhFl=?Buh+L2*|zu*}~aGuW`u0Dj&X#o*wDT_O@8Iqo;|8oE<=EvN z#SWRU+v{PsSHYH7dL9v612VESWNTuP?|362+jXrhRoN^67Y*e9Ro!?VN`g4c$;?8q zEGRKcX#Tm|Y+rTkUU42$nC4X%j;;=@AtHm)DUxb9P{Cb}7hwzm-lF&Mv+GyvfPO2t z|NQk|4La6lUoBe(UUXGbnVO-9AtTlaL&fA3s3kF0NWRWtb&7k#2Pzq`cyQT#EVX2nY+bl#19gM!QFvaeyE9GcjKVur@M(S6q4b9^q3cC~OL@$%x{AFL;fCumOcD zl7Dmlj?Yh_H}(b#)_NohQgoQ)QU&x)q5DGGba?imL00eKvB<5q7Li#P#(ZSeM3)`I z4p^y`{9mHV9R*^UiMWW*Mit!#3GI@}bc&bCFqGzJVPtn+p93!2Ic2jVUaQiwsiI?L z^;%a_fH^9ak~t)}^inb^^4glrsgg(`C1XD>1XURavW8vdRqIv#wAEGDV=GUgHZW3F zIv@3L{@TS+R2?r$;%O{tCcds;oU@RQmifH(?y`9#;5g8$O$aEJNc&`b%M|ekvOw4) z4Y40hoPf&I)@c{mA{@-iw5$$gxjL(U2FTcLb(&P*{0$+KHLG?B*>OvdaB)+&YS&Wo zRK2Pk@u`p08skGAsZpY@9S$e}`*}HCurFD%_9i1N+{``P$fz-a5uNy5^OQXK9di=O z^e)iVABqdGoipwRo;uxk#M8Ckk>e}rC>v8wr32fjsmOC-)ac@&hd+Lmr{Hgfr zTs-tuCfc)GMEtko6o`R@*v^6&0K|4S#7~j^0QxfwMXwh^HE(#Vr(bqDR(cI)4|aCAslj8U-3OQmIFrI}epgc0g zkNLPMaNuG47;y@Jv3VPJ1Cd92)}#qXeewl+7NaW0*NF?u!!cmlrg;+H(lBsFrPBhM z-Mmzc2l__hBI;_C<9>8zvOdHH6V6RxG@z`LTCJ-oHJ80HH}`|q{i{ici~P! zU@AGqlN0MmcXpqIcJrv$5g1&aiM))gP3{hP{6@G)_K#~GlI>>wY%8~G2pTXIAIpFx zGIc9SuMyKl>n!1^L`IWIf+$<+nI?$xdJw0VjkbuXp(2P=G;9}-HAUbgn zP7H+f-=`tOL1=Cu0#Ri@g+QDZe#mz!qa(?I081cZz1T+(s8}F?XI}v26jD80La?+| zaTLA!Rr6Kj`ts(Y*)_TXj0<&UxtQ%vz1!KsXFD_PJ(VG?XpBZ}DWxL^)46DLVP(&lrAL!CA!3nVhSLuOG!XTW2!MgBS5U>7z2bCi3Hw~s0GfKbU+sD z`8Gv93xj_phKBeL_lI6^e?+Djdu&JMM!Q}rX##apPM{ht8fuBj5>ANQrHf{EXxZom z6qbd}uVS8=xQSfc^@0i|&ZiHuKm}DC+UFAsf0NW;eP&j5>@yu>KwBS zWVV6OmKn4GkbA~!-&7?AhgMAz?w z89*sLeD}a2RC^S<>v9!WjG$x>AQjE!W-xI(QrUXK+~N>{vOaZUe>XEyJE+kz(Tym& zd%QPCG<)ftDM*@;&s%|H6d{Q@-GSR4MQe#uG^9~XivYIdd=lXd0^gI4)VH^yVvr)K zB>A(!o0z+~qstfxP^Z5}+ZuSkJMG%xDbQ$=>h#Zf>zUhv>563Luk|r{) zZK--+S@}BdLV^2$_u;E@n=TAxeF4wQ=--VlKI(+q-p3chUY!> zyovB0(DDg=Nz+!N00Nl%;)VE8kWhOoJ{R^yA%+VWP%{k`Rv%YTQ`@W-G)@H{@I1t! zDu3_zT~2~@%WS0d*f|c-_-$pz6mg#>`!A2Rm9waMdV4CF_RB-ZlmZgJ_|Xy!66a?p z$zsx{w3Q=E6T?D(tAthSG1%xS^iJZhx+X%&$Ra+PjffhVVScm_H$r+e21Xc$O));# z;3S%>mP(vVJ@C9<6r#0KE<~VdBk5IMkKG*`yQ3M>uKo=Hb`;c6HF;uY%bjK65b_in zxib5qrOL6bmVkxCC|(kc5viMwe-+g4H2tmL5_ zvjvPUHm3ETbsH|(*Vs4!Lc6%wJGutbLyCG>vLR`sDXftIVTlaP&aLfE98_XSW^-H- z{PBd@r%Gz_H=df<4c|7UETq^@>RWzX@vD%g3dTiM?~X*wR5S1G?wzI?|9~1**c=@x@0xJqR}+0W#0lEcni1ph-3;6kzOBM)r(P}0O7EACLQ@p zI~O8N-S_=a9@(VfA=L!|9J?Y!F@^FXS65Mrjri$_CfcI-KGGfu%->5es;4kDl2BVN@00>4F@Yi&Rbg--@QzjOM@+o|m4X z?unn&I-xW0e$vn7V_@aGwuWf>Y_eex?rIf!CA2ZTjOTVDG~nI}6zNUe0G~wMFj}%G zcw{tXtoE^`6I7IFYYohqI*yoExdglB;+?2@%x_zd&snmeqH1+Psp~MaNK3vWv;%MCzDPoL*oJOx6j2&FW77}q``2+Yqei+De)=6 zRXR$#d}Bf7!ZF5(4^Tf~EawzA-aNp!&+%hHl~E|F=O1?*j|cyKhHXfU=HG@=i4<)= zd3V~|GxmsUwSt1`uhU8?yOl2)%BCavl7VE6!xTIB;pkWTQh*^VNEjo`S2|oisW8lX zKOIHXPbw0foG8W#4q!_dBeTY#!NW!BBGCpJpxF``nHWC&_+9zC;of&44v)UbjbX%W zHUvLLm;Nkv&QaCrjDGGMiJjM|LM49|xKjny?bW4u@0sJ3^^Q5!7_DL^H#;DhD@=XY ziac5=nJX#3pdh~VP&q$mo+WxSi2CphD9Aa6-8nf)Szmhdk3O>6c5G`NS<$Js|Gw#g!ciN4$kU>J8i=@V%fndvClq zDj72LcW>_`UfDPyl8_~Jv|&$kL+=CM`co?qF@B%>soB2jwXV*u#m;x(5fkL)oX-`C zay@k?UINS~=2H0`0>@ws|Jx>d~i&1^o>8jZPSJm#@~JV|&RB^$YDGf3hp0mc+e zEr*ct($1>Vr$%Wx_Kecx4l-WaSykGE4TFrGRB<<~c^a@qtVj_4^@0rXtH7>kYwZ#= zxC1739(Z$U-HeCK*A4VwP^`#^D^qoHwJb6~i>28NqmnX-ki;V5*^K}saO0fHrxUuQ zI(gv*2pNX^f&61k;3!XMyg^@Yp z`>-$jKts`lb&#aO(m`U!mpjcz66R`N+@hZE-MxVz5=0IrGoSk0#av10w~R}G z?UC15_AH2tbr8F8h!Mz~vijad8m4VMY`?v0#ne`yBeGKMEfb1bn%>FHM|i^|$wcUNw^O6;2US=Wn zN;4uYEAG}vYf;2}9Z8i5JRXmP(IAS$D^8>@0rwh~g>OjHs&prm7-h|o8HU0Jp`%Hft{}b(rLZhpJg1ewOav8w#z!GC8L+F_V!Rz8MRGVt z`k4+k%t7jgSs{tAe^YUChEp~M%%RwpVMr~kuL1j4uF;P*1*s--@kb+ z(~Y4;vM5Z6A6Zq3%x1Gy!q`45b1xfE^z_HQ5T~zfto`)EM+Puje?V_|kZgU>4I)$#x7_r>_35iPTpU}Y?D>dn#?=E>U>Q1o<-m~YgbvKL-{o+dpRDOyJdIk~)2T+%l% z-$%iQG_E!anfU>gduBzK2O_C*(TSwOmClfDqidlkp?rWTSFV9w)p zq5xe~8~sge$>lLLUzM}AP@RRw+k&CFy1k{MG?6Gy+Cd|u27sW#L$V)R4BKlcmJx&dedvAS^EdXKWQ_gO_SY4*ciZ)=&SeYb z?dsz7hms2-krBOE$TpKS!=j#rE%J&8*gTCS;F>A4ofc5pt}=p7FBDQCeBQDPnT^hY zQlUdHba=)1B`Hbhv2wmbJHWJItZVopFkd|MrI$Sb3jJB9!Az>~<}>Jb`ju0f!p>-b zGfQ6?-5dJT0RkniWIomo6q(gf&+%K>A!uR2hzYGFFq4$~u?GMkeBWP=M!!x2d_Ci_ zuqG_Vg11LcM`oXO^GR!(_Yzhr2;gzhjc@&;d6g%UGz=H?&hPjCRr(%5D)B4Kdd%cOWJqig3SU1YB{4xn zPt~Sj1N4yOtt=Bl6NI=zJ-S4(PC&3UuBqc}}u@@D#0K*V8f-yC8GlP*k-0X?CDVg=FiY zIJ>OVHNm(wgfldC7|y2OBR552DXIuW(R-M)#=UY#hVh7_nxsXjd+LrLgKO&W_T&ykSv99U29mhh}XYLX|oZRhqbu^n^>W$m_ZE`#!_KsWU8RES`pm=i+^6z#*>C zcxKZ9U3EQVF2mWxL)W6WbBd{`#;3BVc8k7m;42rIur zmnMEV3x*#0ufMD}^jqGppq04yKq{S>Nm7K-gJ5dJS=TDph4QrG}hAyNg}* zY?fAj#q-%$CMK=+UtrWtx}6uq8Lwdhm+PFIvPP@<&!8xHrO)(4$7~A}>A6H5dce3b znXt_J!h3%)xDz?fBLYOsf+U+q07e=0P&(V-Cz46fW2SyGl$@eI_O$P?&=`l2c!C`$ z4)~J3bpjXe$>gyof5VuHc!S)64Es)rIf_@bGF2iLfVr@1LPBAiylQ{d+jaZ;6px#W z+3Y=Eucu1=qQJB)m@R=F#Ov$_U&U{r%>(J7Km5w4k#e!vx^6c+*Kc5*dY8>hj>cZH zyrTDKK*f8b*%APEK#0GNhsfVM-JwVVBT5;!dfe{GbFH8fkSYzm4?b*==kT45Im57Y ztjL07bi=2a(`@VDvAP?OM%yK)L0N{{0FLJcLK zu`}r{clh7SaM6n<6z3zaRMfc9Uf&IRBn-pMg2321cubKy#b&3|BY}7Lce6B{jVlVX z;UuJz6MJM-7I%Qdw`6KeeyR98(2M}~o66%kX)beK_%nQ&ol7Od@bBtL;9D#oUrcCL z8rnqhG7L=_Se=llhITC!tqU0iz+ldrKeqtmTW7tipIc|GI{ax~HM>1We3VZ)PpDM> zoJy6vbdVH@Q4A@H)GS|7q|D;ZNSOmgwETlEWD7Gx{x}xOXN${s(YOjuCKqEI!s()5 z<1CIH!mG-kvZhc%tLH?-k8<(jkEqS_jA9%fC+av^bxPTF%E>wnyjT?#J3IJR(Q*yJVWs9ZU!?jIc-9@SnRxqZsnQ$lj7AKq-Yqt`gn z6K_pf2zO~{qC=dRjKiaK0D#}tww&?dQan|*)Qb-{ zGpo#52LrOj^%gyDr`La6cwz2YawaM<1MaiaS8&wR(&Am9E|>JXkREZ82PZNJ)5Xk% zN;cvH8$gGrc^i6reX-)AV?Q?wqCZh23#o$z>Fedzg2}BCZjj(-#HJ2hLI3MxX6>0@wD4#Z@ zM=7WQM5zVUPx*YSmD?1UxAm(oREXTJKn4R$vtwZ)jmL~2IvNg?NpM<8dT~^7`%c7v z!|9DaOJ7*AwU8^)7lvp8N(To=H0tN#97Fwenb$zPFbbW?LOeA*^eA5HM{~Z)8_gGW zW|R3kPL%w3@~@l0t##OrMQnWin8bF6AQ2;yS-c!8FJf0f4F7OgIoe*gaH?I zkhck>;b?uA_BR2vf4D)R%0`815_dlbb^jMnpNOXhiIGtwY4#(}3b~zM@Ia=U3;H>S zyCU6uha8kq3m=O@w1HxK_wsj--h!db1x7DZ5=xY<(i4y?=IC374{xOZTYT05Xv_?k zK08U5zEMSFWahAuS*5JqA>s7lEqLe@;GZ(j=g^6{P4Ek5Xcx<2f}DS>^MfeCHAYbN z6B$XIXJ4s%O7P`X;LGXY%MtkU8t~YA>d>=z^ghz{j)m+q`b5n7_~;p#nwsZk37|Kxf2hRX`H*)Y@Cbtp~vN4)#iWWbESr7mlUgRo!@l6rURD*=L=;cM?E_3u4Nh6i% zGw_{9_N_fK=~aE98dZ+R`8AuX8j@@5h$YSmm;DgqvQCcl()DRE(UU=_ZVemCaC-hQ zWdtmi$bB2P^~r_JPikdl@y&_T;I?Uq=I{lZAkfF+_^C*oO}Ff{(KZ%b1n z8Bos>3S*SfUwJn0&Asd%;e=kOm3Z zG!3bo1XX?tsIv%EB?+qX6i~GYR5b~z`Xo>T4Qf9LYX2#qyr@?PNl*t*0W}7h9I>^AVB z>ENLH4Zk;)&A+9RQ#w2>AAI9DWv8<5R1Yfq-;|t#!%F!ZQTk_0z!Kj|g!txe?yfAi zF8_b%=W9NF!vDy9p2U|UriY}7A>T^mB}w_T<&qKE9NhWyA_I9FppDm50HRcP@gG!B zZ3aVcB3}Lf{=XO|{)fLWz3IT6|Hq?yH=7j)v&oOD2c!)GWbQU_CqgQlh;TMqpv3hU z1+c^f2OdzqMc@Nd>>)o2-1Hvw3|MV{^vIC>1>Fa^y9+Ovgs26KKVDtE(GhWPmi;lX zW*0ETX-IJs=a_;zFBrU?AYq+z>_m3WvC#4qoq$SF#6$uh`3>ZufZYd-{ zp3V8i-km<87t^S6F>4m0!OE+xC+>jVjoH04{jVIhH=?!H(o=f`f2N;)%}I^C0gC9 zCY){O*KJW3H}!V6)p&DJZ;P8Z?VIaP6E52wj}&$o#ERJ-(UNiGG1ni5di-#$SBl(s z?gvllHsKA1)D+h)B!hA3ff`54(f~Q5$EZ34m!d)xpeM1!3?N$Pf@n<##ez6E+!vQ_ zh+cnTX&dgOANa#t_Z3*t?ut_N~63s^mZdQ4tLhaQQ>hhjE8+I}8$H#G9x z@su`TT?r>P-hK&SY9l%?U)FFVmT)7U&899)>!~+*?*-V+uc@(sW)m=*7`q6yvBd3o z)`zvx_ouP{-ji;XDTbs!9gLSSAv;>}t9Z9#0I{^b)H?NNLyZAyk66Hfw@QKmz6`FP z^EoVt*1H3C&6n$fI%rfz#sJsQexQRDShV8ZkMUHcj)+;<9Po}q#J-`hwPomaRk|s( zqPZrtKI!?-c2}*+^p~M@=%cHvYM711RCOq-lT1s6Sap0vS^_rED}QdjfU3FY^f%Ap z>aW!(U=7i!=EPfIWO|}HNCqj;7kiLJ4OtW>7F9^kpFD3)6HiLSsHcE4JV(RCCw8MjvuB4~R1x)G&vnhHB6+b8)g&)8F?)x9g|L5vQL>GL7L+^qp zIxX6uF5UO^P6rUX(wqBYHUlag+#(ek1o-5~PpQx&G&cegiDD(_GTLrUy|Dt-pK%Cp z6y!*iX~67RKx@3|M-LEY6K8Zf6F5F@TWM*|X1m-$Ne0=EL@_%`tkf>=k$>~Dnf#YG z%{R^7Me_5j*w#yKonaN8!_>RJ?8h75=Ywvfhe!=f5BpbMU6 zO7h(IMtBy$+TtEl%e08B#&MJ0EvL8E7JB_xw39c-h@$Hr9(r)(9z1{j*F1H!t{wBU zc>pYE`#~zVv-^GMV=zHIUC16rq?Jr2(AmEL+G&h~=?2H%R0rzqY&OKr9>Q5JPjUEf zN)|0+pS0QJ!R*3WOSVt%ai6$kpnzfWly`rlr|tw#G0D~!x6Q%d{Dd(p&U+o0g#F3^ z4U`Ts!= zX1i^63Lu#?E*&`tF9>F9`lJJNMSU`7yaTH>p!r6^@&4&r9}dd zC>3j!Lu6@c%XBhShMP2DZ1HE8zfrM+A9^U9h&;poct=-kg6UH7IX zV?Ag{eFG9?D{{h4^eO&&IzDlXIVx+7~85>E`VE?Nwr)Y7!E8b0FIe zWG57`tkJQSyp6#n35l%-lEEU>+74-dgCVx zQdL3fzB|*eg~;@VDy)XCh@AAZq}S{?qNFs~sI+By%c_=rM6f)#w10<65~5RNRmHjfpD+ zsm9wtS_TYf`kXRjE?SV<(eX`^kkU!m z;h^+15(bcDc;<|)*>I;gM@+miG{bnbtLRV?62)m2>02`fX@q|3vmqrqWo%6iDWy{> zkm8*(wq_iX*{KvrsLEYU-Id_0)}iJ%tNksK7}9j6~s7NH)e82;2=G&7(dGSrT!;4M^2gNLk2|3?H62gCj`E zPGvzdI%S-}aYz}+VnH%6#yErHkYdP^gk;D+UN!(ibahjI)lAz|S7h&q#yTh~N{lWA6s=q?Ep~iL z@cI8jT^xIZm77;)O*+Xxn0mHjn}Ok zne^${2y-w^ggMxjT07K+KON1s87{hiU?n8B5hKGoXcrZJSUTUsDoUd#b>V*Jv#?-w z@>5||;Yz{o|H{%Yofzn3Ss1-x8ORekMSW1|B3_td5qYtvGvf0i3@?;O_(|0uR6_g4 zY%(DqSMYxs7`1H)r)VLevEEEpN?fkLsjQowc}W$8I5Q6yXweJY3f`xZdC)DKyok42W;(TmeM*KMSB6mHZDwp=l6?zmM|vrcXK)U&_+8^wOjauF#_Un#(rgSfTm9JOB1>4-*!5(5JiEco?DY6`YHg z&8sfKsPdrbSz`uOCwrbZ$NM%;{KWUs0UjKc#9XSmgrn;hu51l$K{9_SO=IcIAQ&*9 zxvOi-X{pvS>+)l#S{Zwu@II>`3@q$SifTx&+p0~<#xV5Od8^4l_VxHtN!LfMD1YQf zhy%0Lu3rIHB8yj5@x=YkwHR@1o3u;C=Y8VxVBMW&`&#bKeQO32{qeLgm>#MIKJE3Q zp2(Ya^Rj&-=W>6b(SRj}&oqLw?2zz}0KqgxlPVADQ+9U}J=+=>zQ@L1u_p=ny4N=< zwqVud5wX{d)hWQrh6_Lu13G&liOvwn3S^Yd9>L{z zKtgxF6YKE+8 zPHYTDy;wDKD99F>wW8gp`ev1`T3t&PeuyDY*`6r1z9~dS%Ievbw=LbEy&pXEkn~&o z{^1f*2)_k2>Xqb8W-##Pi#l9XfHS0=n$2Uv@#lwwB?W51^F-}Rs&^X9+#&IK#__3W zFqIH}UBs%X-PxuvOtR)=xfuJ&nr%P~Ifzm!>l0vs`TMBag&~1Wvc#Hq!S`je8nuRt zq^gzEs_{a}dgcHh^^9ujpcbC#xrQr|Qe~e5x;GZV*vKyH+TE1e)sm5o>wJFpF3NUF zW_DMzKr-8t*-X_sEK4+@J5hDawr7$lxXeB`CDeA~F)WsDE6FQpF*-mM_L4SF%Hj16 zUpP*r_(IcLeAMXsdfm=1u3n)*7588?8tH8n1{s{5Vk@s)7)JPF|;>1Z_ zY_w9IVkHul^}!Qug{;c>B5zmL$3)`h7O&fWciuTmo);rO2raK85i4&NSBJ*fO)cF- zEblDgz7srZDgnhA`$L(bK)*5hY1}KcBbB>byW1DnSFa)(fIXmnm|pn2-i&UJdPPZh z%RUhs?M6f)@Hhe9+bYn0CwX2X23dsHLP!-F+rr3}Y=`#&PfA4V6)&U@ua3esxhJTB zMJC2~H^TV;teOJP|?{%DqDSI*AcNp5B6!I2$AsPXzd$)wx?yRqndE(%(Gy$V;2 zw#Dd`N*EQ6cn;M^bj&MnU`DM~YnOr?U460=R&oCna#S#$z+l00!vh6QWv$9cx zS0NqSZhq=^^eR?Hqnd7oeP*M|7_3FvF?n{VqiMZ7Zo8))8yOBIGQ^of+;2{4w-j-d z@x6@a+ibRIUtjJun>Y9epXo0nkwH66bqr13pdf=w41Ns-)`L;_w|iASOww$xeuy+RB`~o zkyDDnIQnZT2F92N1!fq;Snq_)s1FGvhZSVYnv#cKe49o{4 zh-UfBcWcC+S5=MZT8E$-`TR7VRjC|phKjWrkoh@vL$52DE?TdbsU=UQFSANMGW&Oa&;2XUMLNR^LML6`Xi{00KjE}&`rvmXAQ2C@+syg}=x``*9 ze5+R&SK0NayN)0SO(u$&;u@`+XR(y$XCg)E&MIQbgtS@{7l|Iau^Up6LQjA{ecuY- z%xxvZM0VK_VzK>L;33QJ3i%{28SvSNxq8Fe0~__+!QFQI9X-c%-(eIDd4VYkw01FW zG-2e|#NEaxC2fuv1$U6o`5XZoBN!O1J+~~1eiOZ(yO8#%y`ISBlnGfaPwrO$KoAGe zegc3`O6dT47ed+a&V}plv7SE+++dU_!f%}y#PZb#RoDj(A=%z!T45v#7pnpF;Itu;ySp>k2%wTo^mtB%0?+Y&B#(prqIp~53JY9b3z zclh7#z+Vb5ULvE+>iyXD(d3q2~dudsHD{t?)o$F6zuIxf$ zT{vP-#-fekq7AvQJdx%PY~1nq5mI|3Iyue;yLecc*+cv01l)xMmu4*RyjZdo2qz%? z?}dLqmNr;qa4asnZ(f8rfT9-K^X3RWxp-u)N9(r;P^VlP+LBO!noD~hO!+yM$O1xkTt(1=zP ziv|5-wjUDbraBYQfLXrRMi#cfle}h!@P>IS%BC?$<}FYfbX+lh)PS^0#S*(zG|wt_ zu1Liq#Y(H|{%`7S?S$XXjx);*wB$K9X+Qj+UoEn0>jNC>Cye>+>GWSX=y7^2B*h9O z)&l`#*YB%%Qps*0Glt>nA|zPV5oSEF^^|&C?UN>-ZoqM7NZ^hZ%5A~SDn|&=V?q@tl&n-?moEaJ*(_3fEIK-gc!u1& zJy%AR4dz>wCVY)}1oT4_J(8eZyN5@nQkPCN=4?6;#~28DW_+F)4fZ6fjVBDKqzAb6HJ{O0CS&;O>gHOS8!{hd4%yT0VNfxyzYIbdj`_TGejO>!hBggT7YzBFc+8r7i^bU6MbUseEr=Qei>Y1k z`E68oOI#bybEllF!$Bjs8uNPWowxSV;iz+4c7Hy~$50Nc2n&DHzJ8_N%w=J#ta^CV z6};)4I~}0^D_QB2v7GkkS6~Dq9vS}v7r>N~Lbfet^SN{a65&`98E(!rD4T_pzGejl zfMdkq>qeX-kUJ$}r_s6Y!y^3kx0By@&{Ausu!Bqo{6Tk>@Q?hrwDT!C9!)+sYMLJB zIi3QUjRG~i{z)8(?{qu`Zi<&p-)nX;oQ07SU5DUo7ZNsyD~Jt7Hg?6~tiS#CTWCaA z*iK>=EbZQNtupoT`Cg=b_0d#@l9y3~^)@yz?vk1LK1P+JF-j7Qd1A}3fS zeW)5&f!!ZX*_5pOf!LsuBGIE9U{F;5nba!Fqm$>7i0_^#nv;QI6qwzBU@AvR0Hi@c z{*2HgyDN2$Ee#}6Y-b>Tavtbu^)RaB8sX!ClFc7v)7f`L&< z81%+m(TGmax_+CB_2Im8mKV>S#k+O^$@#=m(~6;cy!Q(vWaJQQHoYy73NkUs!Gj?w zjxFKN&H8FG5YFn6nE2xxT7I%yv-f7>XkmsdIMSdIIaM4i(0gyY5rsD8r7N1%faV3H zBHQB_;r6^K1xoTruk|DIKpLt>usBJIQI!)9WDE_eTt@-sVsGW?{oQb9TgY%iI~t`Q zr0aF=LpG+0M1VRoxVfxuT;o#WoZAF~?sd9h6S`gx$J*@__c*7R51Y^!GhE*j)GXCkSGZ&K1@f3XZljUC$g&mI<9e>>vHtTaHA!o!m=LM5CCBw2O1F}Q~Qv2`v{bMnU-a-~?+oXt&?U3St&-7CvfZRBO zgPPMks9@rX`Wi(xifEo*&kj@mOou1WZiVBL<8%~wvQ(exhZUq;r#>T|{`~y>*xbXE z6@@QPY*z|d4rR=!<#E>15F$iEGt$5O$eNWO4VB4{kwWD0N)W)P=D2r1L~4q8x)C8M zHF0DU5K1|bA;?2(6%b9Ex=foCeYsIDrdryQ)DZo=YpU|n#uL?ju%N}bcy0BN07k~W z5j2dj#vV~;=tR)RXQN#o<*kv8FZ-`tLmay%aa>@qQ#7ar+VVbxbMA%dV9~^|T-L-s zXLM7(a+q4D^@PAUpLeW%d=YEtiLiRAj5JaBY^w{nusd*!A4;F zKa?j>!6H(mg&5a4xewb98}3B<)n2_Xg)JHSOp#B1F z>hd0oTy8R5M$B>s6U8fE6$(h|tr&5D)D|#en(R9V1)=|A!wY9)%b!kX6dcfPQ? zYDHD0pgo->+_m7&)V$fPluPf{)kd!(RQsi4Hw?e?g=EYWIV=_7#295tw3Ppw?o@nHx4kUYtI0#>3O~p_HXAu=kM-(B?3UOFa(xe?7 zuX?BSKuAiD1I$CS-(?43{MB{e3Rr7cH+ z=qu&p?G1w)OKrW<+V37oX24M5psQGPvaSzC8u2$e9;tn;IW}e79E|j9vevF*mVZ0W*yN~Wrae#Gx#6AQDq6_QZ=$R;taX)UGjv!+@iitG${B-j zDd#Gvy{Mg87+M{=GY;;G=qGKGf5!}F)}B|7{$ zg7aePZ5~u83m&X80sJcPeoxVOUrgqa0)n)^i0~_iW%3lP1O*AzN}i%HE6<>{@_rSU zV2m49bW7idJdJW!W4Pvfz6$bX1G0)N=$*_F;jOOZ>57M@b#gE1eUW2f>10l^%4^3@ z1%ZzZCXi=zAIMvTHb+G*859fQNDktS-A7Ay%kn>{6T@MIHc9h2v~J9g9>m z0$n=eBt8-3ACE=mWH<%=6{EGAnk=dW(rKGkFiPE$OqG-XdDW=eaF)DlGWaw&F~&!= z_(N1HAHQA7A}>yuoBm{j@cYT4{GxcS6X)fxpeJA+4qBEQ_7TBAXM}! zm^VFi)54Eg$lpr*P{%5OqpgKuN_- zBpif92*4*f6fB5n{AQa38-0qz0Vk$lJzQH)V&IMkOY$U><(R~{oHLRDcaS3#K?-?@ z%tDUP1b>$E)>JQq;b3W^+{x6mp0`AI%t@c+0xr@VZO(AE)220-bi9&+0I>~AU3o^s zvbSm0q+T2AkvXe>&|*>j=xC^vV#tBIFjiYQxF3Ji>N?h-NjaD#VsbGdLAsntnTaIc z(iSt#Jmi_ibm7wmHe@Edznb2dR7$9QCsywkN zf6Q)RR_n=oA|imXX3H%dW^ceQl&kJvj; z+%-fZGPe!KR9>@hHrgrwMrXsn+v z!I}+!*u{*C%+ULQR~Y5oU%uJOr(Mw>&j#;{{{|PCf;dAGMruRK>!2yEky1ocMPC}U zXklg~C5>;BI4`T4;z!W<+gGaemc8iX9ag&}$$Y-jolSWYP?(Y}bcahBk#l>$MN}T9 zCPQ%j#ly~ga6;LeieHO!V1|hMcbW$C!ne}&9=3(H7+;S`NGoFQb5>)E6ak{1e4LfE zt(-765UF2|jKGox_{6JDvc5z0(MT(${<`j=-=35XM#fgU0d%vpwqsVkvaAGS4HCwIeTtq!Ypu@qa#*u{>ISZ8~GR)BLxfG<*2IdFv_v zd1i*G0`Tl4_YY#FpXL4m9Dm$wQ)w2Q2w9Ab0M5_&FZ<4nOJ3q#g}$aR>V^*k1aq9R zBWysG^?*vHlr^&1X9Q8xdg z`{!!)pz@95l%2}H17G*QDLDs+Rp%Q~`sSbTS)v875Z~O*-Ie9m<^K=;psVTUy8^X( zjvB{euuHKIcR7CQQT3Q|*)cpIEjvF&Vf}zy>NZgk4ud_4GeQ_VCr%%ce_cGHZ=5L# zS|-E6e|V~3aTeU-_GrxJkAZ)Cw-C8TUX(G?cXPVHz;BJmK4N=oIw&GIav2rQMvHrt z;i76|-(&eWb^~x;x_}*`YSt{E7#UKQ2b{zt1Q1fc=GE03akK1?{eigf2N>h_P zUJkt>byxf8{K=b&U)(tW;dP6z7R_L+J*dwuauqIE6dl#gtKGLQd0WN_$1st0(AU`% z09?L%Svz$03+hXq8loTNeGLmb?LEw48PI75dQNgmd+5KNrVXI<)i7}8F5N*eoB9t7 zw^SFpQs(WsRh&Owb0A`m%MJo+M`t0%hc=G7TmnRF5OE{v^C z+Lx?M28)a?O>VvE62;lA=>l3RJ5IH-dvgX5C`humupx{3IGn+q-TmOBk=5DdOQ%%+ zKBV=?7N11|&$P`nLCgW}W;-Qw-+!ycVy`UKuxa_(^e+&{t)-L+#RIM z2RY|~=bd)W&>;tJk!FKeMYfBTmy0_zuEkXt!sy*#zD*pWcLam|u=ldIO}(GUsUHRI zEo=DQqbr+*`yQGk-9dmy#xwJ=3qvSGI~D5d1t_RK!3Oq6y`fheFdBbwxi(Ku;EjoT z(exn-LeHVb#GNkPF$aP`wB@l-dE+^7)(VFiHVi_}`jEV+&nXZO z#k;>F$2`;Jq|eU9=^Y0sFVqK=14z1K7}pycBjX_P8r>YaVvOc=E;->HWiRD?*-u-} zUFr=lDxXbXM~{^3oj!1&q}On=%wS$VPfhYG+4|9;TdIk3ua76sz7TD9;)Qpjapz)~ zFCZ3oZr=?!5!Un3y_?NoZxwyCQXHaE9`S_eRnzyvINwjPSYCDSW)n2O>>#~D8eGR# z4@JkDW2+T`ntfyM_SV~q6q6MduVX7v-f{4Rzwp3(P~W7oNDo~Sg!uY zUAc7sdxoy{_`Mg($&)N?u6MsD=HX{`Xk-zx+~d4a~6? z@sNQ78wpd1b5G_G{BnkERir|8#Cd5RgV6-L#qaxz0VTTAXV9e;S%5Z0THF>n{jc^vyR zMaEkuv$Rt=$K+vw`5AYLHv<`p5%&mBmQoHtY+20kDN~}9jAk+P`per}j+!^*WpL6) zqN!BVpiApLKk(tk0YsYoUeoqb%McBU=a-2DGX7e%TP~lh5z-L`ZK#mm z3?YZXMy~nF%8G3b-;% z*m;3BmKPa$J-ckRwj(8IbYy|#k};WSf(-2LjrStvOyAVdvyOi|#Vg&mj(YE-)+*WQ ze-2m9a1?8<81o_@cK_*4Q#V94>+F!13%*nUauV4{y;;c30L`hBO&)13Qd)f+z=^qg zJ&o=o{TUq~R{J#lq$ne+6@kOqbpbu6x41KL!x7;nLISrQoG<_Ho>~b>b=VF};>wvf z5YEe&FXJPxm@Ut7e^2}=#wa%Kk|)xj~JY^NSv#?-}FUhOGT z3a&2N>3jEl{Uu*)w|pefEhi!@i#x~x)Q5Y6KgL319}U}xz5f4vO$HkV1t=!{rF^~} z(pO@z|D^cMo#@_sw__wv{Q)LKDL;^mR5_9;-u)QQnh$(T#j(B#DH_OvdGI;$>F7b4 z1Zsc6K)s`_?wgC&P4BFE`sP(iQt$2SX1j^Xq4BbY2DRk1@UUaaf6VsLQJy7%MG4{V z25e(V{B7w2Ei4wrA>RM`GZ}?NY3Zp@NQS2_39$UU+iLvO>D@Hjy-u@11C^IYLAQIn zts+TtMc=>eu_J5DxkGq_mvDKZQ@Q`RKz?M%Y%1+f6z8E)GBGVo8d*YOM8>-H7QN+E zg?pMWg1pE*r@vA7vH8+0^T8b#1Q%pWz?|R;XjMW6GDcMb?;L2r7$D_&J_bMGBpwxr z^1||=Pf5m9((1LYyT9Bt*(uJOPP5ayti!#ljlS+wO6ywws-K;;OR@SGyMjYZZYziS zXkH>FkN9#*ashs6pyPJ{Qx9Aj(#|ir!#v+C9jlx2m#GT8S#7IQ$jdpkr~io4DihYX*giY z*c&YbWriv~j~oHAvfC6i5Rb`n?%TY&H~^pH!*!ILEJvN&r9e`l5#YIKq7A;@_Riki zTr}Uoq`zu4qPV#)4_<2RICdh5`)UvdB@R2_)ak7PmPP3wlc{ExT;+xA^oOsf#DrRN zPqI8XCFVr(e*Fz&BOO^m8JlOVI-CTgLH|eO7EVZy|eP;3033AxlvmtsN#4i8yzry8YNd2g8b^7IQ-Rt)Ux_oKB=g$75FMc z#xp;s#OIYlOrTzdFZFk|np1Vu7ifmqrGjW&UpBi82)^Dlp+XIcwXg}O_pa53@?1pCA2Uk0Ps?vDq;=G)4tL9!^@`YLjUyrmx21vAWHBPA;zvo+V2{9hJWDnJGvVTuv3kAsr+(8=QQI{puu zykh16nTgikHQ^rK74JNZH5UM+zytg+@QpNCf)BFEweTeqz2qlCI5|l)69PWZU5*LM z(FO^=;gOFCb;>fz??g>B>Hb~poWa!rUE51~!LM#Wp?+saP>|?2g~+gVRKQ>c@;~uO zx!5x~ivk@{1r1RaO0?y~+5&3wg>5-$ZFPv^)#C$unbsIK1RJOTsT(xtqz{f8+^D_H z2e+!NN_---#&)oeBO7J47O{8wbjWP8yiuF$#<#k~pMaQ#3w%a~E4x!#Jct~gF?ka^ z9khJ38il1RmCFa!gJfs2x>4ST!di+EbwCL?YUKfDVB@RXUOV8dmbB!T&RcPFS=TM? zJAm55m!H*=JioIV+P533IA7S%envy(uiQ}CY^dI8y;IzXYymb)$tmxjrfzSi#7L6W zj8m>@?k2sN(wb)04a8`nl!9%H9=JGwZ@b96QfmW9`fQYAQI;_penzTay{tKcUykJB zK!Q_o4nM0EV*Tt^y0Vpu)e4~Wf#V=!`Sn^M_}o@sHuG}d`Kp7#DbtT`(S+^)QEjLl z<9y9FV4mi1jc#9{47&SHO%-6HL}gl)ia{dKxX!bpVbUsQq058QhQNz24A6g`Kz7kW+SDjk4yS{SUr2_v`iorC&>x(Wh=5#lZocZf|2MwI@YD!;8LeoXR;CMSl z5n8zimpjy?fxD@wU%ajV(&;r`HydOfeNroEFgI`8XK$}g_~g56^dRkZ@A~TE7rHDK z@V4>V4exd-mZ)|qCmq!3`wKyWQ{)O7I$`9(B>5BX|8#G86Vme`)Wr7)65gQ;T? zs*Bd!-PWsmw{?A`?y{{;4-Z*&g~gJ!Qn~U@-CU)V1mz${tN_VU?a4Bf7g2fFGle0- z?aw~2VA=Lr9u?{_81tS>$#%eMc~$&mN@X^=N0ruJe_{)aF&5;^Zg9Pjh$1W5U@SD* z;QFu%Rvg3R!?G;PIA-=-<-`_yEq87AL1EllR{3+LA)ObF*I&odW|xn#jGK2+Pp!FI z;iudo+05_eO~hd97L!e=Ws0~_yA@iVkI{BbVP_=g7uB=cPOv7#NXXgc&*!Wz^B;`j z`+o?EC%6!POT|azU3cui_x`Z<(lq8)O>oK?1%RSsN*_f)U+dOu3ko?&VO^ng&iVae z`**z3l?oWqK~xIPOZ-*_*rI$`s1^_{#TOO)Q9F_Xc6$ zRG6rj3&21&3bg_Ps}{H!QBq%IikvVe=A`#Rq5h&ky(+|LcqAO)wF~$!Oho-ntDqC` znZh`o@Trgba*gS3|T^D5~BQsk{-0}IwmXt3GB4P9aJwHkIB<-7_vqgF*$vY{3QW_ zLA4KNWJrqvf&Fdi4k<@IImaJ|X0ji=S)m`9qnG z%^%A21IdkI&k=6D6uqZ;{1znNnz7$Xzch&E91UiG^Nm6Pv)O?`=eUmf{6zkG;e?TI zC!%|EF@1mT`eSBhRmzPUhCG{8>xz^hpjse{LAiQ=YNq1bE*EY;{HNRRdx7GI+Ye{H zH&o1W`{8PKx*W-|w;yEns;u07z_8zMkkpUHvwOL6-3TNuc2~nFRq>aEUn=7d2@p1$ zIi0qxjhbA)q4qjxpk(K|QnO?>|ds4;$> z&ZfpM(w{v#h}^aj4IQF;F8YQ+O4oIW?*0OT=)yHj>UK8T0fl!5eKkrUfRRS=jevQY%2FWMbrmw3I#< zQ73t%A;!o=vESf*yk?_gOdXOCyC#{=u?BFw=2^X4mo9p#W;LZI8O8h4^!Y1AjV3c4 zow=WA6sJ{Y*0LI%EfXDXUY#1@6!DZLMIo95o@R0{hB~(Qo;TGnaoU(~_2nKH%@2_8 zE<~krdG~ixn@3}gn+7RHYfWmYJ2{+9zh6+8>|n_!tO;4#kqptIpuZ$02K|cdx&cKD zAxoQZ8AvrT87q4N8xwV_mT0MAjMDSw!E7Ski{%A*1ob#t6pyfj!4Xl9TZbnmjSvpV z&hb;NU3mKD95;`%Upb)qZP*dJ_W|B^PzVRKPe#8U`QCU~Ao~`)2aQoQhVeJapy`p+ z%=myJ^{j~p%_D_CeXFT#xU51R6=dnMRT@F#e+w5p--7ghBlw^AWZ|AYwt+e8MXP777yor59eJ{UFjpG^~^zosb)D)Sx5E z5=w`eIFRp=tbWfkr?ja}sHn$q?~TV69-lQkjb6KT^$PY${l=IjTAtKpH6qzcV?w$U z7~;L)fr4*K`u|}z2F!)i6AvUS%rgAu<&J*y(m4gShStdw5A4*(uf&8 z9B%-{(wQ-!DCMMtblBn(SREAz>TnjsdX9marS*Xq&WJKztZ$TX%Js0z-M;(q{OK%c)CHC2hXJ2;N@vLO!< zym8Vk?Ldn~im5Ty{ZS9~e^VMk!Bme0Q^#f#=Z3&a2MrQZ7gyedR{`tcd6ni=nD|S~ ziz#O@u?^9KgCQA9Z-?@!=&>CSi0WYml2e~d$ZnD9r-PXSkuA`_Fnh-~k8HiSq*s^M zgtVDu450(|SAz#dG$Ad^0OL<+))IBm3^hA-i{VF*=aWSg#vS30=kCf| zk)B>OmL+4ZDe8N(_FXgq07*5EVY#a*F`#g@TPhHP?3U`?Hhx@{~8d9teh#v>n0 z7soa&J*0ux@Gje@$8;;dDUHqJ7hjG@6d;D;?GfmOOr6<~udQ)d zqLq~}zGQ7Bh)q`)Hv7UG+=;7qyN+Y7Z?m>$efOQpM(ZosH@}lIFG^*IN^CZ`FW!D% zXA4Rwm8_+T23D#(4d9A}vp_~mSRV{f8y%J9N{3Hd!Sl^l5MZ3@=g*S8fFfGL(@jST za8A>g(1cfd8oaFaEPM_;81LGZb)E)qgN2PC>eV%s^nU;JDO~~KIY;H7OE90zJFJa0 zolSSi&QQn%86tUSvvuEL(+7FiqIxP#1LA~===^eNC$O)Q3Ebaj!yWARSGATuR}#e= z?cwBKCrH{PXqOH_)bnKwB3Lq#2;`493}=)Lq7cOdji9ke$Q|~WbBlsvQg9;{c%^~S zPa$@{;HDXR3RXNK&2iG~o=h7PLoiIhq<5SeT+U!LN0d(ZG5v}OU$;4W&SVrB=Us0 z74gX}hzyD$L-DAf@ZW_;;=Ay_rf~i&<{YsdPeZCM{bXZI%`wogTsePCSK9cpZM<%= zw*PDTU(?-P_TII-t3uMtAZ*|I;d}2fm(QC{>19MeE_mxLaJVWet_gJ~9|Z`b(CbkL zh4Fx~36`hD&|1uZS1_HBy^y7?5ts#jfI4X-5frNLt70(=qR*novg8tJ&VzLm%4ALehFLkgsa0xdNKDW0A$p$OmJTe3-oo|AEP++J7!&uwpP-r%mgc8+Y#)VM z<>J1$y!(5R-(e>}xUt{@omJhkqsFB>9+O8pG->NUl2^ao#cY?n`{A<>+MEv_2V?Z4 zgKx0(JPRX3uPNHWpo{2=P_8LY(xWkrmJ57=KRCeGet>Je$P#~~Ufd~A z6(&Sa?tjD*w8-o5f1?CB@+Ij_XUp5W$n&Foi%`|1+ZcM0L78dfk&{O;jr_gYsm z(AA7Svbu@Xa{Iqv=RGl&Z}NVy{d+uOxMuAK6AVd_D?N-z+X>@DaCm8ef(iThTLCib zE-+HP4z0`KPSrlb(6%b>8*0d|VvzWpkc`oe9wr}M>U>opA@F3D59SoQLmHf8n3exO zd;i{_Mvg3ujsZ^Dt(V>ezMG?wT;!O0oW&Re>6F#b6eyLP( zqsog>{qjqBzfg>OjIOrOb`C|udLQ{dx4rEHdtqNYyn;e};S;TlbFQvyLCtpTM;R84S zJ=yDLls+{vI*#dnBoWO&oDwADDPZqSptuMYF=jPl=lQaIknjF@;_0DxV)8sC@}$l^ zY*pC05+_UYQ%if98I$dMl`w zS8fIC?gIZVn?PfN(V_vb%C~WrJt5NE_$XfMCd}uia~UpYmv_>NKoPKmW$4_nkob$M5D(wa zwqNXQ=oc`bb>orT+YFyq>%ob8{3kao%TY~=zmMvJmG$>FWq8TWjd+&7ym;jNfArF# zD7weFQmLaBJy_R75iRJK5H*7BxWn*D^;3W5^2||E3aVpMM2|i=I+|#2GFBj9)1a*B z_3(XKNy=E++%pA{y+aS_2H*0tbWwo_2(3QnQT0$uf7Yji;G4b|Vo-Z-dEx7sy%d%_ zvkrS|=jVg3B|*>JF#78y=-RidrtO~JD;AwCARbRTi<%+>kLAwHyNP|1W1+>x+qxo0 z?OIaiV7ttPIc|WSc40OmIF2cPmHA$sp7RvMGyo?7PdpPWHua{X={(QJM4W+t{EUGV z_6teTgG&mO+kO1!=bsB&_E8S|yZ;{bWAUrJ_p|?APeRI7asfvIY+qfb%(31@Pt5J* zTu)5FA4yQIT%N5X66xlc1N4sSBck?Pcc=8PI*VLeNI|h6?9lpE{d?g*eVs<}e2n+>!DX|vB6qRnvE-%{MEm-f|fkp=w zzt#H8(OjX5WP3*r1i9nhINa53$L6(ztT(?CTmxHLWv^(NBJM<+>I%T$52ON;Z*=mG z*hHgVYIfLTHGD?KR}BpCryrxASXz^vf6lPhIWulAhta}cEEnSWhcJ}mtKc2H98K%E zerIasP5ojumI5X!WkDsgBY!d$sihs9`}XeS8B z3e5sepE%c!nu{-uP^-tUo=W54D(87Ah@QR&oq)`_8KY50;!t=2%0R$8*YiKZu7RmX z3zYm7d7ZRm`NWdO;cTp!Y1torM7+;)$^(HL)K(#090IEH;k#Z+R!@Z{CIWs)=Hgp3ISk2omiDtu{qqZ z*YJ>PYJcV1E9izysG(-^rYvzthzm_Kb`C z8v%H04zge09R)oHBbL5i1$d<7kVxjI*K6X1WOBbDqVMUj5eSjdRX%Rj9Ki^51gG@O z(7!%X#*9)02w;(s1XD|aso7yZrLO|U4{R#__T#wd{1~kz*NO@;i{_-#SA7{M{I5jX z*Je3LuH`{u>AixG;n;qm#!e9oeUPXngQ=hok4{KYMH!Kdx00w7M@f;xU>}oT zm`krVjcQZ7tSRl2w3gU6HNGqBq6)ov1;6Ud`Pnil76TdQ8ARTTk! zu=asfZO`K-L|FF9tk1V4_(SaIhr#8NJNx|i?CzH5+QYaH{$eN1KR(H2?ZubwYRZUuRDwQtm)h*&%cBcG{{abzZaB?NPR4(7Y zClz}SbVjINNajuTiKb{0fU&%bGEsuU77MFXN^a04pA^sxMPaZG(y?Ktv3VI$iKJuX z5fY;XtWN<^wNOaMVv$vOBV}3{@0|+2{DQZaKl1^siI7|2EC|bMA^fzF@*eZpolJzm z+q$HLy1`Y5l{_S+efb`oou)|XE6~X+z-j#P5cTMD)FchIngYAO2JEgKwp>~RcFzu5 zSpoa$K1-NyqtDh64o{QdpVrtU&@_@FtMkf!gUntwmDLsJcFRGJnTE8Uqg=BwmSG$n z;>lI-{z0alg`E82l&NR0-Z`i?!xpfdKW1A&gpoUDBQJuI$3L=LrDB}=2s0Gp!IJ*x z9@ds2(vAKpvwtcmDe%`;<$Wp+sIbfGsnrnzf7Gl+g~W^`O&A(YS`>5!&JWHuKU@A7 zIeGY9P@ceV+Pb(oymMG>tjsi;_0Au@dhAf99tNiTvLaHp|6iOv*b(_PfCk*uVD+(63NjZv`D!&>VLu&px5njW8zr!Q6;#q3G6d6wQii6LA zR-H#sFsieV8%9+gbqlH{lBYhxupNFJtHTOeNrB|melEW^T_{f7@B^_0#f_toaTNV5 zbmw!CP&`y4TS{cx<#=f3;EY&lA00W`1Pak{C$sCN_sY%m8;`|;!?<+iIMfTnBpn5? zw4(8N6o}O+VLZ&T3(}h6eqb_HVkY#~p9lP1j8C^Ycg}_9O^6(rc9-0_36t!g!WP_F zo*c)FN?9|Eab8Mdh7vH96c|>mtP+SqlqkXHct+Wohc5G=mO9MGFY+-dJW%1r2fdP& z4uQ5euz3nff$(0?GX<8LIL`-35In9PI{B*Yzry`|SS#MBy%qY(-%c>AY@0H-oA3Bi9yAW1zIaqM|{Z-Q~gaLWp##q;HC z;!o)jW!yMlouy>cq8;ZAKD;D{1NJZp*h(9X<6bN%4>;?H5MlOSV+~Xkv%{H2I+j?W z<8Q80oR%rcU%^PkODlRd~d zdZ_-`3Ae16T!2pgU}!YR{e)+hmUBg#XE8*w$Y{?_2_l2Mv1rA`iHgQLnUmng*ba45 zSRQL`6?9st<~oxtvQRN2ppNTj7JH8NyAk;a$63Qbpj`3PVUbvrFy_;GLo$yj#of^y zJWoGl+8vmn&=+aBYMip;`2z|S9ldhRR=K=3Mx<0Ri81!6uUJs!QoF9hR__m+eiJTV zF04hG(rvxA?x3OWzO8OauhUpvhn8K=xYI;#!n0z4acIT*fsGI!7LWsTymnadaDU+g zpZVX$tcq5VZ&bbKDL56%-KsT4H z>mv7tBHbJ{OisHZS4GU@Fc7pR(_mPS*g5z)o`QeD9^luXQ@fSFt!;^XVLkB7Ap=Lv$(aq1!590M&mkKOE%ad4JwVIH2vD6Qw`BOdTMj%=Rn4d>%XE;YQx6Gc2KG}@A2_{X^a3u?_>%9A1@#Bh0xzR!YsTtY=4 zauDBt7N`58WHBk*y>vT6akO;uJPX z*dY1V_yZF*y6*QGvgXs5-7@Bh#pa`;r zJ)pgYjR-^#y%x;wc@-o+TPZJI;2TCsDd;hre(^fVrKbi0XS5QZN$65X=LW@U11-T`JDULpN-uVlaY0;vBMHeWO3e(i-y-WR=v z{V%>3wz~fSZer?9QE*ieqr4pl;Yg$BU&2;?_(xXQp74)>OYhhQAbHt?Hr=U4cxQ@I zrnrz};!&$diQk#mp!6)HvSvHA=(y8uBuYNfwCEX3Q2CZOE|JLk9Dl$(BDoznAkMte zBKO=GFQ?PH0smcuBHY<>P!R8P_*E#7vOs*mLLf*C0Y8cxm?-GWD^D2W8u;b}gCCOi zG`q*m^jB4$sAmZwlom~w2`6#x_GQ9emfkM(uH_f;98Sx~Ws8&%EvJbrdAB0f5KFtK zd2FJS#sG6mF$=CZ&r&pdlqE&yr<0%He|-F(8UD`_|L69nPl_r9L)m`8B?URCr0QL) zj`;7nqd*?S8dF?NtAX;Zs#XjC00oj&D=Ft>aT*gDi1@1?c`igB)kjM9Bh0oWw_BN^ zPd3iJ#2Ye8z^4v?O=(<19DJ1;lzE7!%aiqZI<5Fo2}2*}W>W^{jxi4EAm*Sv3*i2L zZwoTQHN!4#@!pRDx-FvosTdXj&R1S^&INV7$l!F^Dx)VY#*My?-yiRw zsC?wuJ`bjD^r7G+UV_Hm*P+JY^+3tanc~>5_#BG|iHb=*Mvla>+9D`6!0ZDhJbTb2 z+kCYp&5T*kJQiWiIL&GNtSY{rd9I{K6+?T!!-;esMn{E0qk!4=b8_u>M6>~G+<4C3 zF<4QZLA{9c-ht7qIzQN8W_zMCYuQ2w`ir+?c<+mKb{Go3Mtc7Hm?dJtWfxqq*!OYz zWc;t;E>Q=2=JaMk@IM~A&6CbE6itAZCAopItM4Du{6k*VdxNt#-J>_{c+*7+Q5;58 z=Pvxq!5AH%a9I`U(Nt1cyPBj_C~E zquU*tws9n@mS-l<6s0DYJvOx)b{Uai+$@P;6;(Va0`vMar}}9eHPbfB#MmXWv4akY zY6da%rZP2($Ym|*a!A{+DWICfqn?GX+Vv;tjrEMmdUQ)3GG{JsShXL~3i5yiC#%lUE{jp+4w zSuym;r({7w^b?@vKJKaES;IGHR{a;JL=(NeEgO8HMIDn$bnLxBE+d$o(@f z;Sp|ALJ^pAp@w)(BV6?}DHca82i3yY9E+Zs4ocsYy7Q&#J2&yldwBCRnlBlW0!ReB zTuiv;LHmATz-fK22oIhIp7^zvq6-)axtQ)mb$^oN1jLdqN4&m8iMoR==>RX>_eYB5zCGT_S7T32@hpbs^S25BEFpx-Y!d~@7vhxUQzCd z9~FkEln$)n@=84P+Qo$)m8~3qS3g(YTLS@TEgd9|#4p1Trf}>|XU?g+2m=u{LZjTL zb=REHwrwwwWP;@^`}W={+E!Klq(7uPf$ey^GZTKH^q@@T6x9E=qZ{-?AMdEDvpu38 zh7D)WBT|YRib*Rqt{*DZg|!rf=auAv70n^jxsximvIuQ{`?^&1LTbI4ZaoB4djOrPLq0cTCeQbn12Gmxusqf=)o7SZm z6c+3$VJzkF_!GUdddaH!&YMpUk|-#EM1`O?^pIvFcmBI+!KD};SlWtCTP-a(vzkv< z2H2{w=jtq-EH$K@*{dG?yH`RhApFM?I_=X(oVpjZh10#uZ%cY6zWu5rfQ;Ez+iR>tPD^| zrm0^Lhw^;MuYbr#1JoYU0`Exh#IXW^8pgArG zzrp9obO6Af@mGY*zM^<5C=b_aTm4q!x8C5a*&Xznje^DFVBN%`4+}UDBedri(dNE> z@}~Z-H)y%KP|HPU2dRATDNsB1WpY9bJc(;L(Pl_fC83ma_i2j>K z@soWl{sGS;))>^h>!Ej9O6hpXGS32xZ9FM}wG_Zbz~PKwfaa^{lt?m?&w{x51W2-F zO^*=u(AA62{rmM=u|cLZW)EC;;-Ox(oS}@rRL(!PWn}6GYG>jy3mFCbiY_v@a`>@E zxFYhB`#@%G4KAMkTp%PYBZ|rL&y7T78eUoRo8y7j6iL=4}Fp}k{wdWl}Q*S zkD0Rm6hwt}0-IS#%zfM4meVF^`=co|OuP)xp};=nc;wtURy+xieV| zM=JMiZEd}&ciXM@OZe*_gm#wh#qb7w~kjH zsDne#jpLmPZ#(4_EJn&)?%37PGvi_?#WO~G|E<@oWz9BM}joPA17p~`~n-&xz?N?YP-*jpk6kR6>g0*JNUK2Xd%siQ5}()ZVt=xOYk6kQNIlbBVJd z>^@U|Rd$p8FT49|_5Xgtuw1&AFrd5HXU5H}3}wOY&EW9qMUflo^ieQeJAd>J(i@^Tb3_m z8R(ya(8nuVU(xt$GIvAWdt_DIB>8+@L>=Z&M$_n3sxdte1+S6oO;4YCA16|qss?Hj zQVUN2q$17S39Te^@fV#R->%_&MU0@o(pAJ$$`TZhD9g2!6{1mZzruv){i&w~60on@ zRqC*aKb~+6vQ1dYA@rvA<* zUM<>_5Leok%&V8H*6N1%QKFfA8M|+c&@LBNS{en_WED-1PFfSy(_3squTL=47EfJ8 zSyZ+389Np)YJ($1hAr7NtWcg3YcTgmAH1=(zh)Yv3G*NypjqPiI;tTioh&&9Fh~7fke$=@NYCD?Am`D_t?*x4W;4FoDF)+;!=#g8Fi8Q49$Ys zHd=YMxs^v>xRRn)Qaan(X~8h~*r_R(VV#2a9v@Kv1SKu4#0Qwwkv-c;?4A09VB-QN zad25FN9-Gkyhwk*fs@6Gri=afwv+J#>=Y3S^cnioUEs^kZ6-&_wnVPPrcLiFo=7wH zSN(L9ypbxW>T*gpoNV)xZVA%Pq6!JtJ^*X>1dIJ3;Ui`p)BRIK6xNUnNn3wWvGCtR zL@0*CqInUdm6Z$j1d;bLh8P+vGhZaD#5#kS@#jnP?&lat4C~To8dg?VRnkeK)0Br4 z7N-Ey@NPLcWgkX%6U0h;&+^FWpPkG;oVih?bMPFwZ5#}cKn7_nxv%8@LKi~i(sVeF z@OEMxWEx&n%TQ&7Wk^4Da-&l!K@RU@Jj$;8Io2WTG2L>8-rv9H{EOaSRG)B2e|Dof zlkb=Gh`jryhuerSqEi@L`(qY+=CE_pDLSX_C=6!)E$RW4$K}%F^3$jL+U%}7cz2)D z4}GDriL+VR3hEl5Zs}g;DCKgFp5fq=;3cRYVYtL(@uL95`dzkBAG*7b)}XRJ1eN0w zgv%MsU#jWMZ+5)?7Pzn`QgN|g23K-ys%8D}0=Ky5D{&K^wV}j~LArL3u*hk`WtB}1 zpH$#wUFcn!?^2YNinTDijWVt{lvSQFn@S1his!2X9-c|z<%SX z#`sHyQ&3(nk~R{@wo6B3kIP^lqA8IG2Ap-&$c$W%7wPSRHh9GwvH;hV6oF)-w8~&a zNZKOx6fBC|v<8}^!7OFG($xIK+EG0z77rfeWUBPeE(rMzvF6EbCa0a+v(sv)cg3m8 zNTP?YK&EL)^n{bcRLPd6^hG3jT*LE*Wxqw6i12J&l@tCm&VsJdoWf6=X@|f~rd|tn z6KN*yndfiL(luF5g}N-CQ#^HZsFD}WMrdU{z#%}wvb=+t^^~oHX~jZoj#IEcDbw9t z*;MxpcUPWeR_5_EQoon?SP)i<;I5Uhc);ZkI-4thJ)+#-q z5!q=U!S5~1pjg&&McXILFpApWW0>YEX^pdbH6}K(G@d|1St4<<&0?CA520N?B(PZ= zKMh%dX3ghLXiAF}i+JsYY`JpetI*gK?&W`IUye4uf#Kn@v@?%fN`iHe7{E7}ti{Jm zXtp%=GOPtF6^{prJZ4B0qVC7Yx_hRJy&tdMKE9uU%&wsDG^%77V}GSdLQ?WX8d(aS zz-%0?x|1in)izL#$7!Zl=#k@w^jwEd$@cWfTx`H(if$ZOE7jCW<&;VyaWtz{WvP5H zZZrL^q(SlQ(5zN%4XCwp%jg&1$(!F=R(<>JG7C{0dLuQ0wH@Ai(@gM~EI13anglh6 zmYV8ana*BTvp_c_oZ__W1?Qcy{@rbbLXU^Z|X*){z~* z&4QEE`U-_9#$)0>=glFCv#n-Xu!+4ZJXABumKPj&% z%EZ)qOOZ%;wVih`bvKOv+bQ3L68on zf;`GgplYG;yF!fj_LoQugF-JmhEFJ1D!OzvE1_yV z(mMt!{Cdiktq%%4*46YLX_L8j>#4gNChsu$V~#mK4y;uZjDdBl`qVy*Ay%*hXTMCY zC_H?>df<8XOEFi1r`Blq)0bo~ldD#=|0yBe%*T5mO0~1CoJ&;2@m3_&;s715Sl?6H z-Y;67hGf3-OX=y?J2%?iw9(mUeBXy_=5UJoQ$!p_`tj0yC6)kXZ`u;iL$jtmb+b*} z7$u0pgJlS7DcLl+gCWo-gEq$LPca4FYfJrdCU<7!i>ABp7`tY-EQrh;OISXZImImb2c6;R|AZ3C0+zxY}7$XRYK_^!;zf zQk;i$+Lud>OlOxBN}lfNQqS;oiKjJ1Ir;{EKDvnhmVQ1uQ!BpURrcu6__~|&!mwJs z*9iG#_rXTWTZOggDwY@b7NRR`;li=vKHGW(`0|sr;yK@^wRWbGINPrAA02=3@r)4$ zk-xA@KBiuE*(ctHRV>j_la2=W+2~5DqJYBa%jycO*LE57kc**A&jyVn40hl*=oGUO z;2nft-m+}bEZe zNojX&uj{?lZkyIdFxu_fjg;1M?G_Hf7Z?M_Nga8Lx*$5c=DHM_04O>-F+FW3vp`k+ zt?&Q!Tp-rx|C!oD_23@dZ}sY5b%KZ`*ec}5w<~b7jiHq7_pE_bCCHc3!`os`L{2w6 zh%H!FDmRLjQ&iBcH+P%*HJ^}Z-|XmPoOu!tNMiRZM@<6`t80t_J=b%^6>lEMJqW%u zFncr!7d*@>?p7QH#_M2Vh^P`e8%Z|~>S^fxvBX$97$vgHgZtx*dFWV50U&Wt==`RX z|H5jRPDxM=Q^_C%29b0z{Q#zW`QT8DkPTb9CN24O%40?C&K*IA(+FmFGIxv0fQyRP zQS3bmHItF6j0X!by93GN{5ZR$d~|+gigs zmNpmZcCrjvB>7jUa5!Occ=F8+XZ`;+m6^6p*?n*c@}yhZ%!w>#hu%xqwQyCC^KPxA zEG>)cv#OIxIV9jsi)ZQmfy$711%#YjG30@4g9VLcZpRA4q=aD8{akCto+e|I#)B** zfGYQDhjp(~Djn!+Ojh_ICX&sfZ1qNcm?gNB&fzL&i(A?sBk>XweaFY#D7+R|Z~pDa zFjY#m#CXnD#oaI`HLiyZ<%;`xIe!D|^s|O?Tp#clWBI>H3N9;BOMsmm!H14@7r3 z^Adv}6Q0s;8EHPfrPoygk9==L&y#MeX|UC@gxyVn-8Ep# zF~W|@WjOb3PDc9`{F61jiRwe|E^W<35Oq3+N+B8s6P$ggMOb4JCaQ zD;hzkK1@F4#IUl<9>-tR@b@|Xs>E;U-P7H&=v{RvDI)rSa1NDNfy+8sMv^S*02(^% zerbP?aa$Fq5Le>_1p(vo7XGx#v&!zU$kYwwH20QXmdVA9vFrJ3Gn2V5s5yKO32@;A zco9VjgM$IiVVmflz}KWaDesDP0fgSX^+RA1lGOmd`Idg#MQ_bjCY~U+OMLb>ndoD+ zcqKb{<;fG2y}g(Ht6&-hv+Z6VRxfn4j?Eirure~x@0>1doY57W23>p` zXyqP0|E!bvL+>Qi;+0I=uvr9&8iV5x1AKpvS4zw#iAlGNar8Mj{?Ngp7O!OT-3uapX2Rl%z%;MG*{eHDB^1$^HCuU!y$d7oOJxU&m%Z@6QNrxDD& zTNjQ>C-pY}u0!uti17Qha!fh=p*Ie-cqIe;N%cvh@A#1qAjSaS?A?zsJXJ^kRPTTU zCrOM+i4nz7xAnT&b@C!Re8@~Pu>!;r1GYPZ#!0=`qx=~Xh9QS$=uIRCB~400!+&k$ z)ZEWSBOg5?MoY<-a}ta|A;GkESP(tKZ_a2N6kphWS-zWR&OZN&4}@lm?TL5oO~luQ zJKVnU$BQfep*b5PlE)kW^`G=#bQghEzr5Y9?(A0{U(Rp6$5A+X%!nO51|{lob+r3* zcW3nQHtG|qcr zHV{iAnBRr|<<-K;Hwq5k%{aZQWwiX@Ic-1k@7$^D{1RO)e{}(D=I;FA?Rc}xovYx( za_ss$?r7&<^A{Tw_{$+Y5jnJl*J!T9Oef)vgU1%cObxQQzENH%v2!8>$2Phk%y=jUN~jXHQ=D=`ZIj4agO^m8c0W=T`Z@-an}{ z;m(6uMA>+tX~M>`w9Jv>acWnVzeO252v3-gaiF0i13Hf94lzmO?97hyH-;&>^f1xe zXc=OJb-E|MsQ^?}jWI zz)ZvPMB5r&5%?5&kiF1DtJ1;7C#pPvP5k%QZwHM|yWMQ`JKY{L#0cuj?}PLH8K`L` z!15;rAOIC;xtMQ$Dq8Adcx}fi^SY$zx=SDH!o;xzwsg1nCkDjd{QKYkZbx(2ZUCAp zM?-4ga5l2n?Z3ryO-*!Q00sLw}Bbry3Jm*?`-p{c6aQ~VcBndxkii!^u+Bf6_n-9 zDkmPi=RRkcm|u(y2TfrPZoeZp9B<@4a~#+!SUtg<&>>sqDC%kV?ck`@!z^7z=MV+( z&kik<)6;tU2s6AZwHo#A(V*Ws8=Tb}zok@%Ux(+tcY`<@rYrNL>v_uHz9gmRsV zFvjE7Nk5G)IKRLoaS&@y@Xcd$G#j~>oxz({`>69KU*e$75kEIk<|WP{kGEL!CAG;n zB^08^t(SvFzk5?` zsUYMrkZe6_wvn6QwD}LjxVn4sfGc9aU2hWb49v6zQnE+#1<9>QMw!Dt4D*S+lUg?_ zgZj}?cTnOW{IT?wczSSN1veg9LR<%L zQ}NY;-XXw?u*;c0MJ3XmEp|585+D+S+$Iw8An|FUBq?+N0R8~jqt@%z(Ruv@$&2a* zem(3SHG2Jz_~+^QNx#*o_xfq5W2FFoKHd`BX7OJz%gBSOrgNaOD!PE>{>5D)6`ri( zfSTQQ{e;8^#^H)T#0n0u4CA8=;G**{bjN`;@QVe-VXNO$a|gv;GaTk`@+YnKZ->o# zf57CFCKzfWGv>RqrWG5m$xrc)jZV<$ZZq<6=2@ajkGtNeyLhrGbN%$QD>2`qFRP%@ zc7EDI9j%WpLzrpSWgeJcMNK2cf9{T_?wsGW=3w)x79G%&%ck;5>fC~NE5BhzMA~2p zV%b?@T+Gqk&5;*@G|If+uAIHVT^B=F?e%dTXKjVD7f?ce*P+WtkOE>PE z5_d;rP2j!HEuK493+QEjK?@hU@H|FB9zwO1l z7Hi7C$jj7^c`QF~A2qw(^Y%ylaazZfd~(w2wL0tz!#3!EB7J<)c~f9m)>0{QYl({- z4VtM!aJtlFoUSKOMIr!3hP}}Te%x&~p>_fEj+od#qwg2?7ic;QoG=J*a$v7H?hrLU zi9HHHk`14=K$dm~WhwZq-HU7G$Xy&3By8X}L5Qyc-5Kq!Q6!a7miJg;aKIehjgKq5E13YgK52oz$uCw8HnW9!(TSYLl--rMJ7S>;`6O=T=RZdnWVMjlikwQA zC=8@7SYQ=}&tOzMzp?74j~R~11lhmk*`^noe>`6}I^T$;^2q54j)aYTaMta-1jg?1 zq^JpJo`bKk#ht;j1FF3dzle9? zd;9@A&FuVG#AL5X+c}CB2g+8&3d7sMg-;f_^XDZ;iU&p7I;!(BjwPY<0=6_^w&n?63Vn)}i*iV222O~! zu0oBJJ$eg9z6F~z;m`-~#PS~GJRoC3zNb0H>@A-=jQS+71G2++{bo<-$k;Tm29z#n zI9RFkgJd(9h}cDr%?O@a6hWCX^50QA*(~yg;-Q0T6D8@0q67vjq$I;$6r32#G|_P( z*p%A8^P=tY`{eekG=Yc|FjRXKhG*ia%;w@jQ@?8Nb8E~I(+7CH7bU*8uTQz3^#LXb znX`~mC#H_z=X=mLXHfU&n7k&XB&mM3vkGuD^)#;$s@0BFCuCr>@~0st9@P7z;?MCA z1FR)(c`S3wBiH#<0fYxL_Hsp6hVQv(t-8{xJ=tUb}QYBDy zf(_4`hrx6Zj_>mNO@wCJiw3;ja2eeZXYs5!zc_e)V9A8gm?+JE2FFus2j|6eqdJKn zMWqwur}N!2hc)V4xWLq7Jk5)SCt&8oV58A1f^A z8kUgodCl_$Du0&?^6AtT#f0&m;Xe^*Yvnz>HH32^YW^44CK~G4=yve5NQrf>FtfXO z&&f=wh-&Bog}n_&Q_(i4fjo09Ds13nu)hyzZh(kCs1sID`^{eE2z{URV$e7}0`UoJ zkfAZm-J((~Kh|tQd@*(}iSel3uNRVO=9K?7jQc+&%^YkhANaEf-G+x>w9DP({#j+m z(?RE~&jO1)xj2XuMa__%0XyUB55T?Jy)JwL=9RB4kQRD#kKzmA5ASzi?#&PqGkQ;< zz1I-T`FN8tR`PAdW>G_nch8O*e3joFCBKoGs#*;E&l=pkiH8!JWn4aIr? zOe2Trv}pH-=T0J9gJ2h4=b7_kL{9?wZ|j?R0V9L_{}}B!`5&VKK}w4FjLC|80EIY% zW={qp;0Pocre{fV-H&kEeUP%j* zX0&+NZamX*!y%*F*Ql6AL^~l@wb+0A%wBN(_SsjuSkt;ABEzw~vga+jPDAIX3*aXQ zb5W$``c7QZIi?gR%beiPykn*t!ik4NR_sH>lAV2)_lr)d+-j~h?*45f@WVe+QzaE0 zC>^-ueL7e;><=T6lE{-GN*TkmoQcc}h-bu=G1PPdUL`Sewcx!ulT6OL{t zLGXcDb7x=Gtt>X^;v0q}(-f2MNu4eDn==zX=a~ont+}w&LDlUSJYEHhVxn0DRkuvM zt&3|T0y>Mse1q!AvxBiO9GzKRcPe7M`0}K42$K9h4lMDJg8O60li@#SY_EQ{Y4cj) zhoxTYzngetLPuD3FqKOB)!S#VU$WDgU*Jc?7(1e6)^WTM?0Vcgh^8t)K0trrC_W>y z6BfHHoKuKcV;Ty^osP}dChn+7z#LD{!7gE0uldQW(>zt(i>S8l;=~Qbvqf+flpq|1$uFl6mN`YnA>pfx$`LDF|62P zlFz8K-Hq7={|;AX06V$d3bn8ZMR-q=g93SJf*-||zxXbGV;Qi!tPg(|H&mBb``d1; zn)0-pJ$2rF%AY#JAM*Vsy-q}95q{(kLtg{ec(+cubc)gL^w)e6*RH^|Ys0mhz*VB` zaJQ-G$^zYXSwXl0T`d(|MWU;$jgFFaSP?!ganivb+lzS+KYvAp__2T1Vb3A~nRAG8 z48EiyE62!carY!L_-7bMbdNDTVcJlkdQBi3s$CPv4)58u-5A@5x>3}z>3w7P=vykr zJ+*&B`9xo0tUT@~`bP>8{#{8$>&9p=CW#3-+&(@rupXzN+81-XU)mnsjV7Qi6Av;} z@Ug)@a&-JxDzaUHOygvMOtRh)x~!a`eD2|kqA(lplfBxW8m3@-am%m+ULL1_Ju61k zua;rxkK8m|wOu}7_#?U4;G^>@n0jaJy3-HBnL7^b4=YK_6b~YW@X35uNw>RM&ngv^ z_^MJWBt>1P{r;x%Klr~j-hYmw{inC3ovW|({rb31XnyXc$5h_#5|rPzH$mIjFEjf0*HL&ky9q%9o4#;@8N!$M@ev-J|6pSG znXB^!-e&q37VgHGfYvm_t4B&^?7TcXab5#^p?+Q2A)CrEwDoLNV_5OXU3kx&{t~1V zYm_xvt~{$%p25z&jme9#VsAP87wChSL+zRKdsDtp9HT%&Z8CcAFxD1NTK$>wq7^V~ zZ##c1g9Rr9qYZwqN6OpJt6(xlX$t4AmJ^{<_QDwl9{G8rJ2(uThcm{*1B{$P72ylm zl{Fbz`65ayJ$I= zAx0s9;ZyWJptHy&N{>7NZiu&Z#)LsRVu3zgS9X9~CLZqaQ}+X$la};|m0rf;Q&ARu z5W2=tP=Ix)7vZzg4YJPOImD{N~pZn{^z2t7|en>}#uf+Y|UlWWKCgK=U&A$kmip=ZQ* zhzGt&TJSf%0gHn?LNxkcwop;YVz2**PhVmraq!Z|ZXbeOc<6G%T#D@J2CWU+J(hV&4d>=b5 zExgr)Mqk(b;>l3-SiIwSEo4r{PYA zp+CNaKTqpU33jiiMd!R%hoRGBUH;he>}=4u^5?B1o`V?`My4s@bJHtl`vvfJICr+8 zhV~y_Z0EG<6{>y;>vZz?EDXkYDs3;PLtsi<^(w=TL2$cpZY91_s;gaW_3Ew8A$74r zU36L7v5!eD()YrXo>)ICOD&<4m^g4x$#s0{dcIkNyB z-YsIdx(0_uRHl56ZA1XJPs+>%1D#~d6@E@{G!yNG1Ld@L(K zV15;thMeI{pziG{Xq5!Q1%+T>`rfceFW^9!1A?d|IB+sy+m;OwS*F)#khPeJkYrvK z4+)sPn1vWjn94u`gf9^!gLP*5M_pF2!AIhQq(2HkmBqDifg2h|-V5*Syd#=vYOIse z)UDuq1mUs>=2Uz&YoD;!T2y&YROTNIy4Go{EAMh{eC0-0PM<76hLo?fdYmd}S9cLU z=W^;?Wk{Ead_)+XqNKA4d-w?Y9lfSVa92EvTB)J|p2*i|c1X7C5@1aOWYy_FWmz^7 ztj;?snKJ=L=ajS1pJL#k*5eMdf^uT)I4#sV={xP17`taVXZrT=j#N-k+Sw^@#uQe1 z`0mi@goic4^90ruUN#aNaEdz`ASv%^ej+)1He5~7{Ya!hC;j}CY zk6Ybde*mf<1)}FnOdc526on^8Pfm(LuOL0COR(w_4tC;Nq4GbMOIHOIxPElRo_3dR ztEeC|%>@+%JaXaVXr}FBy|YfYZ(XNY*lWGia5Qh{%pU_(YrLTYo?{pNaU_&iEl>zx zKNz$G_+GWtWF8)lP_Y5LfoEeptH22}!a%pARp*)JMIOISZKqn`VS3OXJJ&iqZMM#S z>&wRUc4|jLb3HuOxDGur+8tgC<3ieMop#zUJ8;fIPa5T5A*+!`w7p>*AX~4qGv~E8 z8^fqzg!u^B*^OAz41cVRb{M$fxRBn>nr#1wnGu+J$>5y4(vqMl)+%I-s3I_`4gIXt zg468t7uX(f#}p2FLBMrf{**>`wA(p4Z}f5ez#}{wE}vi_sL*Rao@=p`i3Ndg0nHfQ zP}sr$ZqDAz+SSu;^Ch`l2!_V&NDNz(IW}|gtN}HlqeG}6l|^; z$|I^03El89SD)na_+tsO3FBoioEaVa^seFs{PBS6aG;~BC5=wI-|ev8ly+qaMpY2? zpJ-p%#XgTqElacp4r6iAYB#ke%5mep=E<>MwyU7)=|xH(njnF+vZp$c*p~B{DH8GA zt)x-&b(5cPOEq=G%J+S3yh=esMReNgVeij*VY#H!??f%D=%iwAKzP`0ol;CP)g+nkw>0Tw&i{(1G;ec2VnkKAEh-hM7yO&K}@m6wnI)6kC{0|pvx;=)0p4)wbIUSvZP$nO5hYc=(JDX@jNN9@IUW&2CSUx6zYr|H@kyIw?R?8 zbbRo2(Cl_Qa_QlV2em>MW<-=#jI!q2ez#7}@}fpftW0^5+fJO{6120E4&O7?2e0bA zSA%+^(LC#0s7Nj$N@;?MahX9!jMjm{I+Pj}y#_QMm(}#L!)~X3WPlrlJ**!Qab*Q7 zVP$CsX=R<7QKx=#A`0_5F+L_b@KJB^qpgX8kQSs^af2h8e?6Ea*a7@&f4rnv1>7Qr zUU9=PaK{+Ej|G&3MFLwK8JKmA3z<{tY)svcR)T)iCa@rQs?S%dakLjJb=4}ha~cSr zlMc$xZZBStHBC76{?XKziN-{DPH&I^E%m3eDjI^c~)!Xkh$bG%Iyr-7# zD@%`=G`5q53@b&pgW2THAP94Zt+BV4V#`gG@2M4PmI`pvR%+~9Y9JHGH5ytC=KYu? zX@~Ys8!`KDIs;Odfa8u;jlrNSx(kpzEF0tMvJI%B5R+SZw&Bcsae-bFUx4LaHrvfE zGDhzW^ZM?dTLm17d+Q$2tRKBY`V1&mg_ZF{m<%i9iQU-7c~B9^|cZBN8DZW3qakQ|PZA&xRefE-Gv)e*vOk^FC%Dk*E%l6e>wIz4kvbo!0 zVY!57$i((>+Bs?+x0**Fgd4wgj*m?WwhUA#{+8Bt+lU}BS6NJZ2k{&C(s zX}mJFU11~Kj&IkvjqqM1JL|jwrJ>X2ktR)|y&0g}wn>-XNwayz>6p+?-qe3!+sj-4}Ph9GRZvBt=LAFj1$#p+`7 z$ResXC`K1X%91+(&29b@^XD0lw* z&`?o?vUDJ;`jesg*p(j3uFMn~!4_)Mz=)_~;iD;x0R?}5R61L*Z%v#+PxiIFTu_Mb z=J%n`>diW9pU&8#Q+DT!jX7mcrakse+CD5x)AwG>w%fxPcG4?eUNC?}T~C{2!sR#n z-kKl^Nc=jzfKCuaY?cg{HbB(e>8rUBQs2o?8E=NxzN>cBM!)sCIjB}&n{(W1w_r;K z?b94fuSG98%)#(C3um~Mv=d5#c+rrMov=M9Ap07K*ck)`;?0hRD)zdSg9Ls#Y#rIx zv!-0OJ8#6Wcw5RX-?rs)&%X0$8|$Fmd?^sC^o6qkXg0@3Z~p!NRjnE#<)Hjtrn8NBw^M9{wT-xF&=-xASq2@f}*P#R*r@9ph~)9 z=n}3#lV;-k;AN+CWbzpm&?QMiD?7qtJT4GZ1v^$tqzs{OhZn(Q65OC0+;9?%KCoeU z?4kxQem#=G%&x4A3yMHbVvho2n_^8X7hyL0YL$Jh3b%jB(vjYPI0*4##VASmnOv2n zl?|mC>M=K~J%e(A7AMlkw8-)~_QM&@_zwN#SBB5l#QS51Z18iz>*?!3^ z;{`Q+!v>+n@YV_D>$G3yYD(=h^pRtkLB__!VErSkzpvCk!us+l zRucUQte%%SFLBot>>84WQxn#i1RHpQ3bc%u2q8=C;ft+#NxMHMwiT){3IF$NvK6poAw zXc}!N$GWc$w~lUhwmz{lR!+GG69C+$OJ(71{gie_j$fIrzKEfY>A7nkM6}lsLA3Mk zZ+&KwQZKz3^NIU-9@dHjs`?WPj*2(4xumHqF4U`Py4&fI0$$TiRc~?}t2(S((H-{! zFS;COmk*8Q`Z4zU`Y{T~>V7%L{2MbcL8aB07_*?gE&~&?PX;EC5XzYMG-()r-`DQ9 zc`8Iu_lsKbxqJZz*y@plj$7*DyE>`ee{t1PSsqT;n8=Cl>D za|F3lq2mZIMKy8eU5*wL=_0Tbqv%|q`*p~8$!VRQ(y@eII4u|kH+C(+7dNPnVidWd zr*s50RFCPzst-tW5Fgh}WfKb1PkB(d;)IAb(hip@G)@dV)TzDssI~qv+1y7Je?d7S zoSsVeB;l#;U(WEc2R%K)he6)hcQvt7KdPVM9;-5qjgx+MD$0?RO3J0f0e8G88$}rM zg?av1U@E3}2P=x>r%?G`$y$zmGj9mh^0-r#Gz6`LCBT5M*gJ64!2hq@qUcrws$;op zsU*8O9+^>a><#6gw5WD?uz`MgF zq0;upxVZv%Tso}_5A_L%6RW!!^@RIzmf|y-4r#vr5I~Htm=c=-roo|85keiML2<>% z=Aw!4Vup@tDe!cDPle{!N=JKX(5Nk>LDQWUyOBBCPeE{jzOIia=(>7=36C!(!CWdF zSeH~S1F?G39!~;)JTG1p%}l6RR>w2PQ(tK?OmQX$28vh$J9vbr94waY;1@t^n=Y&qO z?UeUE=n5$Ftu2b2Z_ImN$JgsNB*)Z}MRd85>_+S=x~r27E!uUuZ>#Ffqejr` z3|`TSvz6c)*()bY$YT2>3BtD7C$&VuX%m-qZC^bO^-E4!{aM~stnT-VQz8S9Y=sXB6x# zKfJ&^lZfH7!fYS{7HQ+5VKxwh8fhal%*KJ^`RLLiqis}p8?<*YoJEJifZjP1cM;c2 z8|^*ga$rynMDsauIkrA8E(ekmk*pn;gAM!(MCRD~yvQ6|50A{T!QbccZ1=yAv?B>O z_O5rXUYJEf>_N!}#Yx(7e#NLpoECT&hUF3at>)&Kpk^`{xC_n|gMpLJ!?J*J;icSnIQlrsu z@DxBre0;xN%bK~|A$MND_SHgB!d_4D5{gnGO6Z3Gq0?S2M?n_Y4=M>U-R=lepwmdW z6#PJ$(qv};c-Hqi&~gNgxB7FF`Rs$^q@&Fo?;Whc;F8^7{zaGKrK1No4}k$t=@0W zk8?WSp|$n*A1Aj(_&qMC%rs3dF+b{6=YJ*kc z4aY3@hSv4}8XjX}=wVFue~!zLSYL$8Qdmo2zBDe=!$zG$^lK$ab{oUXfk6U=z;ZK)SuK&`d5oSDaHt7&Fa_8>I z2d>DMONGtYIX9BLijGe%xz0pOoT`OL1ckg0%GGj?v>u1<4Y{SD-8=x*c!~EZ5nEcp zjTdf1Z5|ppVziVOV9fTDPH#DRSOizJjlixWYl~L= z1@QJofvRHQRG$8X`VD89O~%oUqeJwAI<5EN3oLh@429|I7*v^hF00bTTi%gG8jXie zff$VihM@6N!W5Z}2d6`16mC#_EEK%d4T6bc>czBY9HS6vjox#hA4`>6KI!=9{YN8f z8{H{7X}qBhce~l@h>YYG^FVBYVtzo%vT|6MYjPh&Id?zv!O3Br9(70aC5~|DRV&i;A<^K0RH;(l zlV%R8!pDH8W!Qv}U_{qs(SRXZVIvWIZOU$1TaV-@AH?;0|C_je?>~#{_tLq3&&Kt8 zUxDlQQn-Gvwvy{A8q-;eL*fNYFUg@(M3C+thI zK6B6dS`r_RJ88@hWq&>Q@BOdh{=NS+?%&Jc{yi)A?|lvK|4Neouj2l$X0`x=sQiEJ zyKEC})X(Y-v}>)s2RuYk5#3lrbboWLL+x9d`T=!FIBEzKIPv#^asF4d6^+neTx}89 zxeQNe9@l=jK$`K}zekcU3aQ#i+!3X2(#Vj(jnc@VAH|6>$7rjCuBqlY=fd|UENX8q zxe8$BVV&V1VA(0%+EJH@3MOyMQs8iz7o4A+a`FQ0!E0#mTdbkIf8#Z@m$`=aQrFPl zJ=f5e-2r;d?(0^gNxs;f_Mp`kwPCr?%{EWB&7q9y#tPia+9WKiD&?^7e|*WJ`jWk5 zAL5ok!;gO>-)U|M>e*U#OVISMq9t$f+6|Uc8!V+YSYizdLpeV3b0>b}Il4tW(^#G< zadfniwX(@_Cb)v$<+>9b-W^`dT>uBs-Q5muUb&)FEi zvfCEvZh{>^k(hobz{ITPQsUdCR}@UC4*^UAX78BXIo>Tk2lMa>$fAg$ah(%60(llp zJOF{U7ld?ctK1{u$Tl0VnvLI_MyGw$LWxA+d3l`_j7G~a^u`6u|7%or@}Y;hBcT5$ z=k16bS_u5g6=?NZP&#wa#Le-+^K4P~p%C=l^ zo(u7s8v?|K_-3LX-dQ-I=-gcSqbnqa>mZq23fLrfJ9Hm0P zP@rQg!CUkK;Kv1w2e`~uNPz?hR8kWc7;@Lh#U!D`W5j>RwV_K68^OheU>72uL2I#d z#kWQ4O$Mq8D`#VG)odj0xxV{?{crUD0?GRs?q4hXzslv^Y9*2Xx%NH(^LPKR@BUxk z{lESV{J#LR7cSQE30vg=hJw`Q|AoF5R`;(5dQaimJLx=yHvTW^I~BK*?K*{{OL$HJ za4v@>cE2edW_`QVnskDb&<1ut;a;UujydYb-|cQy-1~+)JRLI^8zcn%adKGyF>-#K zOm4Xq{l`-6R(%-}`_~M20DOQ0DQaIig#|!fD+GnLw(tDi*{tnvI?tTVP4dwZHH~*3 z_&)^`d}jufyY}oI!Y=a)nkf>fp}KGpp<+{iF2lP)oAH0^i!Ge-hrTjnq`d#Fmcf0h zJ(Mk8n>kcvO=%6iuK`k+L{**;&FkeFQ#@zG(rfS$JZGG-{eu7gh?74!YQ7$H4tt%G zX1}R?!ud^h1bF*7!$Cg|OTSv`yzjgfX0asF~ z3|#&y%&TyeU=`8%wJxC~K@cyfc^^P+Ws19cw-whylNdJCYc~3wE`=#0ZxqxWWAYA$ zE_4guY7PjoY7xt1&U;^1&0P#2EF%0_udIyjitdQM?qEXcAY4QPwiX|W3%&{Sfg31xx!)z$Ou@^q9AYUE-(k^R6}u5)wuxUyG>OM2gWgH~ zb(2zS^I+Nw1INY0y^J^p^|A}mWZid}mt87OxrHMV%XQZ*7B{fSTa9Mcbr;>vt$X7I zz0fYaOqohtjE8Emv`BGB%uFZ27bF4RSK%kTj^V)aY5A`)R>6~%PG!A5KIo_ z7d)7vZsLrt@ROwg&5DBs(-a&&xL%{%Y_^?X_%94T3PTf6e6Jd?H8hAuq36xSM%6&S z2j~G724NhM9ycP*Zv!g~Zj0<+)bdqmY3Ae`7@d>xp9odW>%d3NbPKmo*dp^OJ=%hQ zN#zptz1#Kbqq>CG)u#cR3;00L`$3E)ntLPv!e*wed0|&FBg= z_R)ely^QSBdHAwuR{=vH&2AD6% zp8WSvyzpDTdbni7|%o! zUE%d|oLz)rq}(FFl);P%=f{sZVmJdX2Dl@_L1zfM*JWfNg{|_ZG2Z4z%i&KlB|;wGu%P&rzz7~`=&g!l1 zp!ceE+|MalvikjQkAE%i#SOhds}rSk%;kCTcEqYwSGxZO)4}x{ujS-+fF{}6LV zimH|Ds-2UgoTysMta{Wsl{NP>Yqr|2TRmR2oISL&Zl~XDh;dc2D|azSK5AS%MM-Am zUcU}}b}mrwXV<3ezsL`)dU?f+yy|pY|J`Z#>nE&!Wkr4HcfZvTwQDPCpEQACSk?U% zRlBX1uhKll2sVy6S}9c6R%2qN3ggtA;90eV7?X3T z+9Glx<3{BxE_6K-bN5 zvez*keYf9y+vk<_vd&58yemooi6U?k6}!2dfY?x_Ap9O6SbkOjOz^+yhzO0WtivjK zc3L83MV@xu0vV&l-;ZR!L1?~sVYFoHSTDDa*%a#G&|idZcn2E_rcGC~H^_eV)Ltn< zyx!;y&QBVhZoApd{q*xsTER(L!Fe|gBCP*k*tdOrMeOv`w*K`s`}$G; zdUkT&OCac#a|!U?Q}{N}za6#er=9ju0>1IvVW-}O@6U|-&6i#5?>~PsJ8{}-V?X|8 z4Eg-z1Xle|fkB>nw+lk(r(X-gWy7BNSBJK4dLPUAQF zYVzUXNgYZ`=I3tnC|5Q=B2n78iuw6n6Vs37s!7zKz}L*Lr}dXj*hq4_WNj&wjL#tZo+O5H{4!Tf4CL@-u2wSE6I7HSM$w@xI()fdmJz8`x+N;WfR(`; z#f*F^EsY990fibyG0sUmJw~A*t4?L35}zq;-i#`Y-YW6+3P5>21tQzWZ_Rfsa@7d5 zeteJ-QM5A-1m_U4YQotz+PPw3%XFtaR)Y4c$6WiR``+;wPDpu%RGg!?@JyDh1~tM& zRhG)wTHjXPR@1=ih8b9sRg<+~p9n7K%Fu=bOJ^B{?CvbehZ#BX>4n52psBU=l1tWq4wZ z8Fj*ZAqkc*sQnqyYoQpGqtD4`5-x&au&|L*mV0Cru}lJMaDbku9+{5#d`jjr|Io)T zM}>2zGHgd{Wqx<#&*Y+xXFUGPM-Qp^e}pTQ@tH4K6*pQ=wDqhMLjMQo zl0Bewde&*dD$$iUnPk?PxOeE+gH|}Z+<+=1W37wAy2H@ZqIT1Hp?Y@RF~4Cd%(z=r{w2#VHJ8fpDR zBXTPHK%v+^Gd50qCaP32dVS?iE{KgS{7Vw`%8i(qjFJPB75q_F;UsMUSLCZ6Zy@?Y zB@ll+3O;BYOL+A`_~S`RC4Ectr&DjdswST%P@qx?k7bhbS*j3L)azN+4BlNaOQVIW z$Tvi8W$Mk*IxE2V?9BqW!@DFnyE*=o*7GvYT-?4UfZ?x@@sJ{iFYM) z8?XWJSxy1k%4{qf)DD%}UOK-LTo#x~(Cg7sube?Op=hM2Ls%ho^S7WA8&7tXBoQ)) zFSjBSz*1N24Qf?wmoNqP^qPz8j4msJ+r*pR>sW{2g-vFW)hgvpI6O;*BhWp`m+EGY=q z=tb77-$-$@3WE(;2$Z$N#f^{`YuC;iTlT1=6iJ7*feC?Phq5W4957XATD5j5jc&L& zT8|=%WJ>Q~22o-ml?85xeE3ud(UsAr9qMyATMD#?aJD~t2>(bimqjPL{JIeW?*X9K z4`SPyJD*0DBb~r9^KO{lXi*oGI*j*M+FE8Qrmb0ETF&%Raq!GM+|7b?Qvoc$Ny;gH z7X0B0&E5)$F`QD07XH)=$fT3mGtgv1Hh?tql?6b&AY2m!k6){8gBcctxnfQ+EvgBA zo6lg_p?48_(bWnwQHD61KLmpY&sUnUG}J1~#oTi6QyCz0`t`}zdLMci-pjONB`S## z+K_vZw^(IgWR+G~7^xN}zOfei^Wfsb(2X1?zf~|PUoapeIPmFcwb2Q2l_i>@Q8-Dl zc|uVznPicV)mUYTp_(4rMndn>E~}&?AJ7ut-=tegDG3m-qHHbf zy5R{6$)nped9aDY;)y%-CRUROmg-{6&f^ozf0?n0S%qkUA!1T1$O{Im-jZVaMnJOr zq~Kk+v$01nF4Icc=42li#`;UY{ECb5-b<~VFC^83XgM^^OPSJJfxGWTgF1w}*=UtH zir5pEGjVQ`RQ!?_YYbAVu&Kft={Bu}52p=$XqaX|qzbd;)K<`~T z&m=AS*tI{VxnA>b9IEr$R%wUWK+TBKu!-H)q3zbP$pQinuj=V0fJTFB`-O;J$959Y zh0nD+K)-3TKN#g;UC2~@(sr1!v}?c&)&f#MEG8U%XG)PBDdroi(I$0_cH>RbWMhD& zwwkgfB-+e^PqZhM4db36l`ukwxJP}qz1;(VyU4^48O|~rxkL~vj@QYTi%zBJR0{>m zs0Ze-P4y`gaSF=Wm&O=b36YRUt1;_Wm^ZOoj_ow3c0>B-q&d>8_Rt|opXGg{wx_tk zti1idQG8-DE3-kPGY2RI%ej4PU>H@Gm3YVoy=|^DH?befO))B77gS{V{7s>lkKq5V z3!0LeFp#DXR`K&#UDwwrq$D8^R!PCzn)z~)4Z|UicEwn zRSq&Pq;HGN95Y$-;_}AcA-ztzE@Cru+;B@r`gK05r5GyK>8Ntb%jju**h|xhZ?Xhq zTw{zOZZqoGOrF+$v-3#C+0JBu>-CpD_jc}Id6b>kxd`3ahsmAeI-x)IN6QHY?02UW zWiq%xOC5$uosI73#V30ciwM}eMyGz(a<-ir|26hT{+I=?MPV*;f)3XG64_{75iwVR?)^fQ%&iRS|eEKs-_OTwdHe4;Bx7DP3j zn{;q7?-m4^9Ibo9tN2C9icusy<_#QFxDt!*{jH)9B`Xl|>R918zfX(VdJ(kP2S zvHvNCz~PZ^RCK(8zy_6W(~+spsuEu@Q+vy&G=b4NJHMgH0KrPGnVSQ{YzQS>vV06o_(<{twk=y=h(iO*6M*N z`d2oq-XcA`SyNK4)2y92KASDlrTjXb%Iwn@>&#jVp*U@8oyid2YxOBb#B2R|{21y} zHnFLD2Pbsbx$thBmNQ241_p#;Cwe@43&XGQUCvXRMdxC=5ca5lZsd5+^1RXfU7?u2 zE%1(E1qZt(Fo;@!qfm7I_BYtOt3q#wUoSG zWRt9H2{f6)$>`b*FS&zL+!fU^ioiLDsG2g&)Ls5w7a-=gozYlEQ!6ejbV-%gMAEf{ zcg}tZ#mvBp5|@V8E{#_jsMhPW&(E-}CYQyT$*<^wL+K*+zE!|59!5i5dTY_etr=_xS(cH2#0*_*jkp&wj%4Jao|} z9k7tv-UPTo#PN3;bp11S+|h^vJzq{(K6{pih;UF{@r7OMBhuIIWa&k;e_aeHRn3kX zb@F)7ZJr#Tyv*^~yF8IQ$de*IK!ZJR5{!007+nNAPJ^aKhqE$Jm;0 zn~fYpRzY0tz*){fi3lc`x;DC-4uc8wpef;5w|~&FoV42=j-{sdhzmyO!IaX(@SJ-Q z+nS*P15NMUX^-RC*YE@*cOrU|0$lG_D{s0v7BQEAL5z+l!uG^pETGdUSKS#K0Fqm$ zwl|o%qbt%a+z|d);E26~M*ZgLpjpNamY=vp`cwB3b^tai!tx5Xybx5dnYZH{`j@gz zqJX7U%N*xA7$UI6rltI3gza?ZNIrM+lgt<~O>f!KOk`oD-k|^PtT~`KuRP+hN^^?Z z?Xk>9Jl!qE*#<_AnaP6gsNS#VcueH=(D^+7)BnJfViGe8(!*Xa$0H}F;q)U1YQY*{ zI7kZ~vsI;sfZzC2iG9v4G;X@%_6GLry#fxU5N}Bdhq}t z3x5h-BN+6WavnJp6AOgwbe;kN96w)#1VUUp4?xRc*$Be z%#mXRTF>zrBJ!bL31dRkys<>}YoaTwFOr#aQXBP=YPsA3N6gWCw+ohl3p3GSsz-Dp z8FMGD<2TN59NyV*jAI-Lrrva%O?%737-~7Q!KDG>T;4H2`qP;&c5-RMgE0;0WJVJ; z#OrFzjfO*|t5uW|i@ac%hR)1{}(^wP|2Gj_t&NwKG~m9@MNLP5ad73o85P)L6)`e*JV)bG5;tQ zRrI2YQnaUIJ89NmH%T9r-(Pk+=VycZ$;seVr`PW}rCX0P6E2WSrM6Ro|5tWi1<`_b zH;4JXQRXq3e&hsmMD+TgiS~ZOxT&Sv3(k|4O6_LKAoz~wD@C)S><+2_Z64u02V*3M-7%anB{2L_i9MmcgH7HR~-h-0S)rV*~jkEvy zvjGa~++`Rn=a}#1tls#od89Wr@aKbt^CuGHm}%u8jzC4HZuoQ9=|>;Dg_`fipM#iz z|Bd_PKm!ZRqC4@z#qYKE2Z*gp-yHhpFvUs3Lipi-(mF;OHFnJ$kHg1fD0++<3NF<# z|6iaf4hW9l4@Z+=Q8c!6R&KcTQ&hhMDAwX~mNal$1pnD~sTJ}cRy{0)ubTg{R4$iy z6Zs!2weRx(TiSnm{iCC%Y5|fT@Q_W~8fL*>=<)O8gpeZZV*C8$MB>07PCn1vW(!Jg z#^B-t&bGmYtSLU^@g!VyPMetI1d~9qRtwIfJQWd|?H9aU;^Sv+58c(30NnA%{2Wc? z>}Ac=Lxl{G|LIu&`zNLw$D0GJ4xrErc3DA9GiayaYz+o@ZpFms@K51@gNnKsxr2=O z1zvAE9whZcbfa+asX#;?4BlW$8fFX_;8~vygwiKojz%n>F{NiF!aUi8s7S9&J)g*sgaB}cJ^6S+i zm?ghr-N;%u`E@j34i>>+;_cPob8f&`aX`P4m8uoB68%b6D(|V4=$Bb($eXhku|}*# z47FN>9mg`LlHPypxTTWbf9$xWlHUKJ-ke^fHKG@3s8i@qlM@dhN|^zz8vALR1FahS zX`2PAHV&LWKVibsiRWN`XUmkVSC2QFR!^C%tidYNl{Hvp!my`J8z zSx?!xt!w)GF8+Q0SNH#^?|-)WZ^iy!uI|;!ru}cXR{FmGe~bUvWd*SSo9t)o)Iq72 z*|q9vtF=7}rgL}U<8A07bZ61X^YOlkuDU4MvpbpGIpdJNKAw4t$I)_jHT8C`@aBV@ z1%mlqh>4e-e52r$pFZ6y;{QG^Iu*3q9l6(VNbK!6r*Oc*2dZQD+ME6wdgCj1u>;K) zVmz2p9<5r=33l75?9_HDyJ+27&Ssv6H(36{o5ok@!@Fk`Il-R>i>o{5#JhyUQ-0x3 zC-j%Qa44PlFbFV<52mKR6s?u2k1KnRp@+2%apC;ZgCnIsLb5;$mwvQ>A@Asx#a3%@ zc-rWyr%Jrp%`txp0G{%Eb&D{VNG$4|FhkKZQIri7=$*BUVhkpVvsdqWt)sGvq9XnV zOjzu+j-0aP7;ME}p5sYQ#jc`oMSr(?_Mo+^9m{b}cyORhrw1uG$NOWWdruLiqyMD9RSNc>_FtxXD z+&jlzE`seDP-o#q3qd9mQ@7des+|H%y9%bRN20*-!Z29Tr(U=Dx}{OPtWvxOvo#`m zU;9M(bPnn^d#}sd^p~G#eTuvg7Ct&Dui(X#LmXd6ox6Pn)5(13jy@Ef@U{q2WbQ^0 zAEqFeHuIdwxuA-05ec0&wXRThp-aqffn1%`_C>8<)<$Sp`9nPZ^ zerj~u$E}xYpGGP_d5fDM{NRw|#)Uf~*>>TJb+?f%_cVtcl$%5XV`wNppIlID=k!k6 zHhw38snL%Yfr4qjM3@@EWWtz@mOv(^w9he}1vg$uF_O?1NgzubT(ez2JZb9uTVjK2&d8t2HOFW})H`c7k1#E~ zim1X7^}Gp=3yM!;mQCr=NvCsmSa1BMA)-X)wozV>+_~80?7ap||F3c6SXKb)0f6cM zxkEH(kG%yD6x|J-bWA6jFo`kMn5@LTmyauR3G|3dUc&fPMq&PtgsDzyW`KuRu|-K% zF)?+Sue#Z%a*4}IQ29LP!d*@l<`}Jg>YSdPH2aEXf)0ZYUzb`(P$R~O8Wv1;>Gfn( zx}Wp{Oy$z6`pI$Hx@>^T3p8SKyGYpzw}lK!VnX?4U@DjFm@Yt?WnfBliY-hXlciG@ zOlGH)m1$y9J7vLiSnoA!iIr)^RQtg1zazOcQ+3be@|5c9pm);f8*IlmC?=j@OEmaM(?z4UFaS39gPG!?nlj8*osN#7{6hXp&@CMwYgtJ zC2vm-l{)>^@thg{3tL?Dj|2R=_o|tJ0Sh*HgtY!?DSFCP;|PXaCcX-$Dl+;b-q`pA zBzBUSGR@y4R@eIPlLT%p5l%vVmtn8je+;bh7WFx7;mc!?Qk=1-rh0ZpXEl{(2M&{* zUztZ8iEc63)8?@xdjsq33duNRdo8-p6co|77~xh1VP5 zAC=<~la)@V?cc^|nW(M5BrO*~?_X^VPhqP|$*lQIUiRDij^p=|gIua_nQD{Fm8xMs zbH?}G4Ty4QV;szyP1n$LwtdN}3Jb+mwdhr68lbF9r%^n$4X#J{yX!NiCc}Ri&!8D? z#avyG#uB{uGk8h?m{z+3Y^Sq*C-%1oCa|YGN~@)Dj72ENr)1b0h812gn-MvH<4{-2Cu)*_!S#}g=k~- z#S!I~*onb~{>Con!wCE+O5kb`iI+dPpkCAT>S6@1<5X&YgdmtO+nv(8$!z}d3v-*% zl^u*@wC~BS0M{^fLgt^>Z&#~_KB+$6??@ywXZPCxYB%G5rCVeAKrVwSA?Ew!uA6EyrG}K9lieNNY3=p6WF-=^p_`3oi962 zGj|v61XvhdG?&))>VAX2S)MKj z$;Y76&=y(U>Y?A^&o*>M=NEREqaNRj+da&8-&xU+^qD!bLdf)zdGfR3rO#Lw`LJP1Z#}JzIhm8whM;4MfoPNKn%ejJvfBXo#z39 zQ`7)aNSC2OXbETRV~{+fvZA557af%jrQd{1ClG%$=#yQXWwA5h(f^4h<`$kr@i@zQ zTBuRok;T3+oMqMtDjnAdY%(i;hCoV;$Mpme(^kJw&2)W@PkjE=23=d@DQ4n^tOzF} zpu*S%B2~^hCzxvnO8UCUI3R4O;&3EUbQIgDtkBmvommXQ2S9CpjZvPf0KzR z(pyR+1r;?&?I2Wp)nUD|;J^YQ(FKOTWY)Xn4%s|F$qRiw@Ib8=pGnMA6nlnYy4s2nRBe}5KG_`#y4 z%D_M>1jh)eebSRIyM;xEngjyC;x3Ixi#|kigi*F4LRO*P!5+-zJw2cs zin(ztUN9@Jdn*ajQ8+bfevP|VYvw+||LQg?LiRNbu7Ts2<=at9(;Mm&l}@RgqOGH$ zN}#i~EvwS~+xR!)!bDzAGKvFgZC>-8ZuB_h{f<)wN<||Z3+~EDFe-7{88^;4#h9ZT zI_cb(`E_Fr$3|$^Raw&9{qvmj$OAs0K4VKFWG~Z(F(PsBwIjfrsT*TY?_-*|vKgW> z#kL|JcKoL;;)Xfu4l1Q7tWv@b_lMz_r|t~xVhXuK9DBaopblt67gc-sFJW`P$8}D# zPYBI=96|1f#zB5c3Cv}*C-0k%;;&Aw+@0lpciG=~&3PPMF!lYt{2|M=Vl`MFIc2j0 zu0*A?RGUn%8z+(?MFmbd<0*>U>7(*aX7?UO3)b~qq+*z1q*vZd9(8Y>FL=Gn#@8m3 z=rKb(b|jP;{3MT(jt-ZTL&7jgqIttNlDoMyifvs*XIM1*{1CT2x;6gbr-&fcEM^4` z4T4-_T&r$fpMk5^H}Rz0vPy34t5^8!`b1pJ=J$ycvsR+-e`+`1GPOc8Q>8@*<5Bke zTco?Y1(n)R_hn9;mO>N;rxBCIT}Npzf-!zy{j%DHrpCRCq*Ce?mKvy=)e89oHHH|V zen;0tz*@7_&CGZVplG+JiBxl={WrrvMCGq$U;DgaDCiud3jpZWcGiV`$~{Rr{)r?R zlI64fCH|pIU+z@SwR05N*S5j%H^gsCaR%Fnc9Q0L=-j@O1B}a54I(&;kM=)h2I=rSbrCdTZ6gAXQj|-Sw5F8=d|Hv2w zeY&mix%u6?V<`FAi1bDSZ^8s(sKA>-q1GwMv!l=a{M;Z`sr5Q)2&ngAqRl_0(a*o*530#$aqomF zWZ|yG>gbMWlB@%*FA&qx^3FkUZPWo4l$f(|x}d{e z1+jppZ2Eq-K>ZDnT(bRc)vj@%Jv9)|{MhOZ>#f)$dQVhh2q8<0%&GEOm+s^EAMn+J z=Wg@Tpevk^0b1=0uozkUm6x37vkdH#|0o%brLuH?vl)`t0W1%GZmY{~keeoq6d;U=8xaIz;cdVbq>moPIUld5O!m z;VfvY;$8r1G+aB;LgCyz$g6vs-!>QNEt%L#Zg#f{Lm|^v##6Q6<+g4Nr%b~kw!-ef z0O~FOMej9D{eyTAz46$f6~r|u*GR`mFMnXw-dbPDNhR3CDDyrPg5 z0=rC>adw|y{Y1|)=h@IoTiTyt{I$SqiefH`fxWXwEcfmH5OVl0g{&nC8K%+5_ zVg-nTK2e$F!N9#xJYc~i-z##5ZC&VJp*O{<>tTDkYSRjCtK>$|H z9#BpWHfe`j+cW6^x*M&m2xwHfI5q=#)5H`2ypCT*dBb@p4MgVDnrgIbvJox$9p+@) zkx@?sm$R{aYC(YrLAUrmU7>76x3m#p2}4(bk^95)g(UG?Ir}~9*u9}NMzhe2*`8?r ztvZGIaVM7Om<~yMU__I|66v@np_~%v_?XXTUNO+lB<60ziRp0bL0lcNaq6_ooE_3`qmyc*Bhx|rs4+gguG`BvG)r6UpiD$CDZU%%f(z& z?Y(nIh~)5!J!J+=d{o%>t#DJ`gxN`ot--<;m?QB>eNB9*D9Fk{1a!$QA4k_6D$u#R zcX4Pg7zynv^g1C+saQA~*ocDtJCbE}ibOjH=kRM}WU@r2mIAB@1w9k*aj_W~3Sn$l zD*fJo@Z?Y%-mrw4QjYY+?*)K{`?U&8+lk~~cDk9sc(SR2*(z-$b-*fvrk8Mh7{qFS zvT-ctyB(5=fzP$vruJ?8Xo#?9K5DI<&oAtBLDoM4zq(C0@Qsj|X$+3Wt_bN;Xg>5q zlr4rWzcqF>=oPDrO`}F7mjHTtgAE$RA0xT@_myZChR%*-5gMMvKjH8RB;w%A{6io% zBHqN<0|Pc;*%roS-FaLIa?{Zl9Rwi=H_g&l+J>VuYEn5HMx=5SfS38R+@J(lL{3Ov zRlvUnAfDJ~ucK-hU)4^ryYep;Rivah=PX7|S`(jy(H+cEQQ>uviNBbBh-@&l=IDoR zU7V>?RJ%zO;1w)7Qo=CmGl5Q4jhv~XLL-HC`P7H#2xCyyZ=;*8X^aaHlfAm*4XD=v zLGuE&Q2bKiTJ}Bxh%vsQU(N3a-e1l3*1+@Z+(v~=U3%z-4>AZb2XA-SGu)B&JSLg7 z4QIP=E;HKh(6~5F&hJi`WK4uagv@YRVh~*M(!&jV?QM5S1~NgMeOtkrP>Z>+#o=l0Om`jDwRX`#S8|jqw__ZSX*&`>zpw zr9{0wuSlVt0In`)KLT!EQa#rrHt}?e#I-8OZi|QkTXbE$0K3LuTkud{mlqcO_;QAF zQ{uzR0jE?zsBQTZgtxhCB}^x&KgVK{~HjqLXhB31PSMv0`4C9zLumKhM_G zA(9#_R??@_qh&y#-0cp6K!U;x-vzgw7Sahd8S2dA($6}m*>d5FetMj0qhi9ZDc)xB<0)#cW)E&Z ztmSg5Mw&WIA+9~ikKo32KD*At{qZ17hh1Z%Fm;TF{33mEW_D(jcczn0UjB=0Y^r!I z$kh}*!6)ZIB2T5+FU--sX02Cky#VzF&ajY5ng^w8DMQi9davqbK7KyW=GugHVS$W; zhRhp`BT1l|m@NLL#4io*SpUB`YL;vS2>G4zp_h44^0Fmx&|2~qy?XX&nQLS~GmS&C z0IndgYNw^(;_^z6+BJm;?|4m$?}+zu_=^nMxUK_l6eaN)!Zk`jH%TBh6G8<~W~DTJM*S1d9&7iKQZVRyQembrv;_5P$h(04 zb^>ZzT^E-Ks;)1XX|tM}X@$2qeYZD~YWi&G?DfyiUOjoVI+& zon*jehX9T6!>#3k(l`~K09y)&Lq<7%!Q{pFJ4>O%_qrqfGd<$h8jT@sR$;zq8mYmM z@rHn%?go3*!0drY0QTM$Y}B=85hQ=lh$o*I903S3*kq~DdAM7P;{#}uFm~*j2kJEm zwv(TZiLn33NF1e`n@=R~5bQ0j1lK-*xAMJ5E{>bRDk28^?Wd6c6&(64VsWc6`2G?i zGmA^lUHU1hA|72#kOV}7rS6Ffh$`!f8#V~DKQss-zfrmT5bBYJKqlKbzmS0 z=dg985g7y&71Q`=Uyf%liwHc_XxvEusm@JAE(-&>m!X#(p9H_Zfw)^fgPx}eZXJr^ z10E)ll^(?{w)3xWzTf^dI;Tk+`CDM%eAZY5Wo`{2L&D!fSoCQcoc)&8J7K}LU3Hlu ze5wiEgVaB^@zndU1a0On43Z1$R3DNgI``OW@G((zHc;q;ht62pLKwg8iGN=Q^3D&G zV~M{*6&%p@k&jrq*9|O&!!g zY`h{@=pUni(V9&M={G+@sXf0Uu8MQL6z8x@Ekmchq61ozLVF@#_mi zP=kiik|!2rLYo^1{0*g<4w{;F1A<~0Hk4NYD(>LYzl&p^yM434+&H(LZ+ltGy_ADA z!cgO>#|ggGFv5IMPDi$n-pb4!{=bvtCVGZ`b zTb7P~Adpa-V(&{V13N9Aq{XbA)(Sn*{1|}(+E%)r&EOAVc+r~>dTfHT+Whm2+5DMC z*FRn2BRGv6?S82v(fjvWepV?@b-X4v%A9=)=PanpbjwPz$Tjxyc_-Kg!PnN8jY*QJ z=3}2iWl1(o4azbUIqFO}G7@qF^OW_u7lb-NkulPaNs@{)MHNlvJY7fPUMyG@G2s0* zEulJ-W;r$glsd~uu+l+oYE|)lZ@vM~Rzob{3YL#QbKW19D(zl$?(}k9^p3`G89oxx zoPQug3!KO74gar8DYxRpx4>Q+;(=Gq0=J1jd&7A!bX;CsNA91a5cFZ#srJ+K zKUk!bgH(f9d;)2c(IT8u$8$XDWtB3877PXI8+3<6K<> zjt;`I8SK)Mz?FOIoWVI}DSpCjW`V1H!16et6ydb@9Z2D7_IlmZ6J(9ysC-t92Jipl7N<} z)iRUu(ma(K?9CgXlq`OnjQ<<)Rkh0R%)HJ~1pRz7?s5bFg#5DiXeE%JM6&g`L0ybw zq2dDpm3OeJ&b(RLtE5|1aimZRS}UyqJq4l-U0%?~wsdCDe_?36$-ogUz){EZsyQ#* zR^}^0lZvyusKTo!otNw?RCQkmSEF1jKWRnPSn+mH32B{2M0;XBiK6~~>Fi>bK?)nc z2CRS3)Iwh2QLGTgGjGb!f@bjjw-nqotp;~vKB-<|oL8{GC#{v27xyW`lQ#Cuc(q}X=3&2fyMaCDxJDi7boo-x zmb!eI42yse?HR3jX6zrf;VTHWOSqh*^c^d@?x4w^-WM4;vYPe@uHtwV z*qise_rW=?r@56K%`tv87o-^z4NGF1VnKyJ(f57C-<=oF;FC_T1!fQe3Y`JY)NxCM5nKML{sFB;y7qJ|ft3+Gh zt4bXi>$zek)wb4n&aOL;S5JQ|OnrX`Mrf*1ZA*FZf+Tl0Pb)%iHV5_J?>~3f14cbx zJ_9OS3^3j$h<82?CI6^mr{+C~#8dI7N&IB8DnAMEGe2o*&4$OHGJf zB)Xr37AX1|F{^#i>&II_2b~*$fuO}T)}Hbe0h>p~1}Jhoi^FP@jit- z(P;4~e7(1mSKk0JaN@hp-hrKz?DN3pyFVYBz-R3@Akw!rL9M;I1vsQH*AzEzG#Vy) z;|&0LTm1_7XgJtT9pija-Rk!`d`u*&w7Fr+G-$W`{BSF zBLDz=wOs~$9{iavtvJ{BL-npYGu?jvd+*QJ6JkW~TlMEUnLZ)6=l5}WOg$6oDCJ)! z<|5=|0-NUuN%`QL4z|UAt)Z1>%!Jjgq2bX?haR=e_87;o&QNIm) zqQ9J5R{_5@wtqN4@H2ouaQo=9d0wf*a)~$5WJU} zs{3KBrmj5<^)V>a$K@WMqrDZQgY%dc(hpuefrkts zar_`KX6>Sk?wTB`rwX~U9OoP6p>eKqn)3#el+NxUL)KqEtOHNC{r9L(fcHBQ zp}fNxA>in>`wqh8qv2lg!#o#QVF6tK{09WCPjtWR6A$eI3m)k6{V_OvKEFw}1xWhUWIFEKpCqEl%wJ)BD0)cKm7q&Q#f9DR zpQPDRq-wR*3ud$JOt3PGW2)yiHwhU`t}+K1HQX{`=;uO|xWT_KlYl9nP>OQ`!!*-x zGh+}Yp(R{VZaQ$^QRIe#UP;B?HMO&A$j1k+(Eka zcv)YVCJz0JLa|MD!Dta?Wm3Ld62nrk^TU2?oW}|`F&m^bD&X6MOHQV9&x2%OYOwYlzq5IHekx-k9}Wk2 zfJ_n?T-8VdMHn0LIJG>mWWPvOG9Q(vCP&iotg1@uqZ2=r70AyQQ%%Yh^(_%y4O@B_ zGHP49AI$V#5L176S4AI5Ep>cqI+4jP&J-yGW2ugAsNZ=c zT|aA{6Qm}mBcU<_s|uNh+BnC@f8I^Y^Ih*M(dn)FM@{=NX$io&D|y}FzGOqRo0s!d zVfI}5+O}w6bl=^`{5ndBfn)jCqbO(J_>W&p(I5!_m@`ex2XnjU^x>U0)YYPm~ z0+9A5>Q=`N9-N6VzX;UcN&t5kog&M?u|}Nrf;IZf-)i)>UT}wW>!Y~_5BthNni=wC zp65yg7;Sv#S?mFWh4#$yLl8SDLSM@LmNN94e}P6 zV9Txfo&5>f`8?F;Ukv8>dwerR?&mne=df0bJx^z@R-f>e{aBskNws&^Pcro~gOR&w z?li8V@8o_xm#!)PJ-#RbPqZw4rp4Raoc6;C=OA(QGG51Nt}CxEVP2O@)M({`k&Ypy z&}U0aAtvW7vwp>VY?QT?W5uSmn>aVdb;V%VTDDA6ZRNYPDI07e2Qy_b}`X%-o0ivQhUgFcn zK@AYG<;Cc&FeJl`*J0=Se(d@{biqwc3sqr=NxxF9*TinaUa$3S<7&nQU*TA^dXMO0 zM|V^9=qvquOIFdus38dhQwVucus7|pMXb-p`&`pG_`w$`fqOonbK}KE!&7~HZvtC` zR69&r^xJfM#+w!D(7vau7G=Q5%~C~! zXz8vAn$U@LXNe6(ynqDonpHIqe@oy_e5;f;7nOR>EQDcg35LF_C;6_~q3zEq6HQj(Z2p+k16NTH&=Jjrn zh}Yn$%ARsutM$n*22A?P4+eP?G}|c2iN+uWBSDFqx7w|!czf0p>v_~Gd~U9uxySNo zpIZTv@8FuBVh|StNfj!#JK@z<0jaElTG8GAK1({(rMrM1Utjxp>wxL?Dm|c7BXEqd z4H^XfoDgI$b|=K`FpzL=`3VopF<@$Z=K1sa`Apsp%^(dPM_-_;_mIem`_>F{61)Q; zrXT&R{-4B)%N?2bO>B~#a#`)%dtk>rq9iki%fL}sx7d%MD5f6eT9hp|uGDq%t=yY^ zvg;9H5fu_1Exm{+bG-L&?Va))S`vv(rx?R=9|8GQB-@R;fMzlYU!rYw%!5>o8LP1( zOGq6Pg3J>%-%y{;D?6uiM>i0iEu{5`Jx+RHhmYPvsJQFJb`gHuUf_UZo}+huso)6> z(OE?U-_K~h>{cGuNm@zgB~%{wLQG7GS^q{8enU<;ctz-SqKyKHZFDddaBU*I%1kiz zKr4Tte3y%6ArL#UuD%o@Mt4H38b`NWZKed*S1ZRhQYg-XZz7*i1=Eq&zzbQs()F0y zF!T=Gp*;pvEyyS)zUp_s1N`pa1)~Fs1%cOmLr%FBvK;?X=>f7qix>t!YHqpIe9gxP0N_hK>@a#If67JMUg+KY?K0_ zK_aj3E+Zw8!oK<+{kfP^o;t@XI1ZJK9MvUCCHPv{AcGmj2o2mE;C@Mu>lRQl4+q2| z1*pjG`BmZD4Fop51B#rzwCss;{4vW*Kz?`C9s9yFY9WYRBfn~(3aU0RzTBz5Gqoc@ zy}-o-_OBsUVWD1W^;cfdh>VX_5B8b|a#U`Q5VG^S1Zh9u&H+B`nSg(9-EW`EXsp0% zhERz2uCMOhH$ktEfQbG0mJ9RBV|%zwfC?7^*9e}6K9_%EG^Gxv7;Rd=ti%-zv54v6 zKSN8XUH@#V1IeR0VIo1(fx<-{d(Gi4JiG42uQ;^Vlxd(+Qx+F6O?G=3$jJ}ne7l}% z7|z=h-8(b~XLEKBPGd2tU7z`7^D?{AJ>Y;DqC@xvU0?mB`ZntMFw(2v-BVp6h>^UO zv;$SdMAfKU^#rtNOuKJYZvVIVWnXHW&B`Vy#x+m_^;k{%OYmE~+YkelZ=B*_uSLw^ zi7$ywICKqJh3ik;io|x?h=i|NK&zrE!4M$Q*LbMX0On43_&qKz0b14ME5CQP+mAr| zwy&&{E63G-D1392T(=zw3CAu+YQYq8ko?Q75i|Ke&qHRNLO`;A z+lxr7LJOp-hdoPEWvn7W5sEnO~1}fpzwR# z`0P<5)|TPkrL6ghVW#9=P(Hjp+}HP;J5vJ&M9as68x%*NILV`*BUH|IP3LWuA~+5o zOmQDJHXqMne$a;fK<`B^`rCGl2-9N9_&j5^BpHmr_Kutes3x9L$a`_eeCTY+;=64ox!9g|`Y*|Dx zLWPFyd|K+wl_FG&C`eamVv>0WI;Ol9-Y92x5%^ZkpN?G9Ilq*_52cDA&>68SYD@{- zWV(EpM=@8iHzVM*sS}rZ1D97RdaN+So1pJ3_l<{~++L^RW-9+-KfwkTNrHPvg7jsk z8Sj_>&gPF)dXGk*gKAF*${308Ar%$kCT(~@;iJ$&1yD-;8lYqBj)mc4J|g(|NFQVx z0Exc&6mrQs)>^hFE)?&%8`f_}>x*_|b~gcfv!5|tHZF$(lL5AkEfc+8{ZeP})!cB<* z%~7Mi_Q?_oP`_;8)oIW*!$^|yWsoUP$Tok*|7RN)6n!2_lf+)_4`VC+K;)wa2=be> zqV9tC#BZlq{Y+h20ZG!Gz>v;xO8lW8tGzn0j5`)rF(2@GiV2SGL%1of+$FtZD-82s z8IU$HCprBo#fsERojoQzPRy&!?X&3==C0`ZYMa zas4Cb)KJxVjgi>Xc*}n#j`}xJmB8IK{U{SaZ#}_LfH`1a*&GnW zfBzsRg4CnCw213Exg(SnKaqcwQ&qJ+rdh?zxIu-jP2?&V*n(-{d& zGexX4r9-6n?%>d8WnQR$87GxmG$`T7k?c0brAg^v7C;%|>I8q?6Z!toC;%Fx=M5g~ zfmYQ4`KT$c0kZ$I2p50l;XN$A6Wyv1LfudTsOhHm4_5N3?H;vbwr_NG-Dj*h;BX;A zY}BpL2K1=W{wyj4R$3IUU$}2mE4CzK(H4$Xn0=P6##9`fT2gyOLST7zF~8ndIBL9j zR;OOf{>T570A;*Rm09PxGwJ@JB1F2 zCfHVlEPXRel19t#}sa$k5Q z5KVOJnyuuW&1I(f5m5{iZh%&xeszNNJ6VyMF_KxTHyG9sOVuEK%p^n%GUZx@d!ytn z&zZDM@j-0^gDwI_31 ze!Mv3XHivLu2C3q{rn32`$nXw@WFl!Wd2m7_XGm@zs{RGf#+`P!+<4DJR7Emct`;>yjdbyppbK`_m#=JC&yRJc!LSihg_MfNUn4R|o#R7<{bex2a zv>;P1l5_HGIex3{_9v<{a=-ML<^uY@K;{mQCoQh2*v{R%AJZ9hhRX!r+QE3VXRLHf zN8VKl5lGU|H?7k+hy}S(kA?kX1nE~ zPW|JIC5Y5Dq&`SCEnM8JrQ?P_oi$eEPv0T;-1T?-uoq|7H=P$Nw8XXR*CvB{!Sk?} z)8hcVKrYOhRJVe_B@DGupwl}TB2%t#f&+o7BotKGp0e<{WMhMF88OBvuCx6TbG}&n z<&@>Us_jTwY5{74amezPhHlEm?FNexR_lgYsAgBEEcSAvfF-KEK^Ej9wbIiTvFNnd z=;cy+^=6FzMs2|iPn(2D;M;_Gil*rhA-E)->IU^HB%$k4d(OUmYnb9O#2^}azDDsL zn30ouPw;lO=&5JH*dKBBQRc7Vu<0;LCuCW>A)t{U^h`eTxn$5#1qD9av|`SYXZvHE zaG4RwI4?rZ{VbR&inEL4^E4=os9aj)IX2WdqN7L7uHIWwp-#h)|KJHa$F~&AC;OA` z>gPak1iTv^?8{+9xX~Xe#H3ttKg#>w`y*vO?E=n%GSJlCo>5lQ+&FPu7h>LB)zD%1OeO z121DU8&1K=5M|O>#HwTE!>%6N={s@ledmsbtnLxunz8w#1nre@5e6o>PgWW&qr?~Qkz4pzg;${WSv&2{3m$Z7R@k?LCf_D-=-lWC; zLDjNl_!E+)M|$)VKv6N<=|;=z9t0i0G7UzvX7os`ohpd#x)R}LPR`mOZps! zLIrG&`;`lJ_zL}nu&~DVTlGP&SP)X3p*@1Gh1$|%l64UjOMgoL{rQ~En=HpY0%>_r z7OxYAF}=q5vPiSH%y1J7^75z{(rTVO?C(xZXsDoqWg2eIl{t29A?<1+OlD^g6qm?>KkL zd}jIbG*8T9KIvBYrQ-eh<*08f65_!6vr-;m6-7H|%b1W+x7a}ZS52aD@4UqE1;J4w zFP~*DE~4r$kC&Q|TBP^Mv`sZ&PTwqz1vf!5ALRL`8Jr1u#jKjXt}u-TJAs^-g}h}= zD$?s+jf$eR11G^n%eu|qfJ@VcCF^2ge(5lh6wF6WG_$-F@YUm6>aXe{g5RSAiwIW+ z8(Q|wyuMo2k3~fcXl^p1i*s;8@|uN?oEb?boBzO@(`(s@2;cf0SpKTBoKi})C{ST> z+id|~_Xp*g0I8fG;`G=&%U;lQXP;4qvcPLw|7N;VBd||F4H6o!$vS6tATaSjHpPYu z$)~c<`%_Fx63!1A8SWwuYL(SC*7a^T@=Ht``|4hfwYB4%M-mRMX1FwGgMtOwmsKc% zSDJtu>96W6E5_7btFa0xcQyP<{j!Uh4II=}J*}ZUtw|GxUf?}g$j?nWDC+zLbz*s6 z3dt)ScL^sSuI5?*HB%P3elB@~BkfzY#uiBJ!wrV_v<5bJMtZL&l?F za|?P=;Y!eZl?fA$f}j($J?<~L#8t(4(NX(s0!em~Kz7~2J>jNN{wT&!Mu{VPRs&7IkG2}1n04uF9It4V;a{u6vHMiFtJO(31r z#C_i2edNNoIt@W)7|OeCcwP+pk}S2Bt^01sM5?bqRv|OtD?Rb4@i(h4&q^SLJ#UD; z(M3wjRb9=!-mGj&zhzf@%-?s$}Md zHAsyx2@-gJL=7ZxD|!A2nivF4stni;JlO*_>o2(i-=p>qF} zMv&?CQa%K&l5c|A&Eju$IO?GZGL;~I@5-3C^Fxs z9)Zc=Vut>be`N(R8yCAA7fPaj?C%ldDDy8EVqaBE030k(Sa9q((Meka&jy*fdc0dU z5*m#v!vow(oBLm)6M`^MzRWquFgBNF2dCgY3K$WD7$m!Sawxri9W)yJza2xuqC)sxT+`ai>-=K z5*yRXkn&DQF9h$)H?+2LX1qZ80JnVarr-6=uDc4p>Gn2>=5j359vfFT{8VY1IyUa6 z^jBx}fo8ovB$!+1X$s+&4Cd6}zMA%p^&YW!5gM}IUa~O;9QP}}zkMJho_W*QOsaCw zWN4fewFG9@d0k%lV?2;CEt)~)ceeL+IBwCisH}|yS0TEqSe5gh^!~}0mQbs(=t#U} zLerbDE6XYOuQ@z&peqOIp`ZnI27`qMz^F?FsQM-}@-D2~LjRnctq;{VI@c7)j8U&j zDq=qp<3y}Yl{mfNkYo7CX10jD+V*lhM3ecCCA^j}TE!d1>wC6f^pwBOCvZJzqF3SD zpFf=W#F81S0ft&+IFWo?4CR;5*OLxy@(r5u-$3vo?G#5-LCX1Px+0;*<6S}JQJcnw zZ`ZMkJT8%7^+$wo`A$N;+gh3$%ksie#n8?ZHem9USjQD6=A-j2TfTh5(N3#lW>>04 zTu0kOI(79mCz~%`+6m{heoeuWR7l00A5)s7$_$oo?lEn&ry?cc?uCY8^B;MQ_}1WM zP0`BfTuRmyg}lVl*A{i2J-i*);GV^(uhJ^D&KM)&w9C`F)xq*(-iz3lxGw1CNk{Ac zWzXcXT8w-*0s282+0M`sPhqy(zr8VA(149&_+Ws&0~E2+U1+9 zm3F?z*%aN>oS)X4E;ZW# zhvvOP9a_r!D!ZWZ1?T+0Qe^pf0&h}xrS`&n%1 za!HZ;4V?lEhqoq$VRGvx>+j=VR?oJG)FRrj5K?wjPJO`(6BV3Q?Sg*#3sJY$%`3L` zPiPJc8{u>@Nil0hJKvd^bu(kzM9+xc9av(S!`P&uH?T(|#8oKlYvONuWPN*jE2V~> zp5xyo< zR_voqVl0^9c4qqIo%KWD)EcsE!^_?yaYYHMs?P!RmlU3T7%@2VmDRIqG5k^<6Y~@K z;+cC|8lR^9a>3x#^+c=9mk*oH4 z%cV=ophWKQywKg2AkKtP4TJHhUk~e&DPqZPO}yjS{5fYqIh7$oQ^fEW7BQVmM#HiV zI(5U5#!$vO;RP~>vGBWX#eL0Z=~;1(hAX%yQ~jjMakTQoMJ_r!2O27-95xNpQOdAL z;;d{~ZM#EKU+aC_D!;&Khh|b;tkyk6*zclzQm|s(0WM=R@9T5!5+B9e zlyDrAe8}g7=-MP{%0=OfMpGLa-)pzt@Z^?^Z>k*8)6$lZ)qaEUM$I$8#WJ4g-NQ@F znvnm>a5+v8y&-hlWlAv?Nzs8+Z}^RZ-hh1`jAQWf2O>RopoZ-(J>kX@L3*v_pI23n zlU@|TM6)_&?+_Y3+St>|sa$P1K+r+G<*N{Gh7e!*zVLcz@?~NiRL|#(a1m@!T&n2b zX@P7eA`~tN(Xnt)rTq5CeCB5nv1j|U=Cv3S7Z5q z&9L$dD--D|BI~T`Dk;XSM(cEyo3iNE(M-KZ>nIB^r0Z<-lP@X9b0h0YM(f(BoRdt$ z3T@Kqti$V+>n@~{W0Fkem5<2BW5rj+3oCW@Ob>t6$xY~Ua|2A70fz@4DT5y=N%!iQ z_v#8$`kMdW3k9uuXsT9Aral_wgDblMd~+lhm65<0A_2}Jm|_pFx-48E>=*03;N?{+ zaD4TX5O~NK2aF~9hZ*&%J^@r-lN|y8e!b-aKk2#)Pb`OJCHI`F+#w$a6XtMa* z83h)0!0`CM)13MTe*PZ-Q$Vc0UXcA4e-}mJGW4A-_@6i=pBed&dNRPCD0L`qAJBof zRY-4*fDkUFe2YbE(1Op#l>e&1(bif2K$_1A0k&fO?>(s|;(zT{%iq`kw_pF~-Bx1x zvu}yje?If^aw&Eji+yRL^%DRklKl|Fh2F)8Wd`Dj6*3wVzU*jan+22L@{Wgf!h2@4 z?y)tu4B72sg!VXG@%bl(BAksz-h4sM%(1$HUxJu*V(_U80yB6v42HkXf*Xu8wKEE) z);_YTn;75S#kT|S$pDMdUJwEP6?o4gZxniqU;P>UfkA*^Fwgc&`{l=1J}}|zG4}EC zBKZ3aOwe;b`Wp{(ZZ>{F^uf?__zMsdMWW~?2r+#4CTnK1$R~i)QEe3gaQA}G*uF|p zy(Os@-T1nJB=f~LG4IsaLaA&CVtBx@Wj zM#l$bvQ6fFW3;!p;^qG!dYct!7I=+gL4LE6n3n^Q|AmAitZYBh!~%SZlSROMff~R* zvD6NJG!Rv(fWY|&5uZuN22$b@n2bd^bUDAWuv2VNKikC19KLp*ZSLp-X(8j`Mc&+{ zS2rV!_CM$LIwqbN@|oHdDd21wBVb?yWhk*KBy=k+Z+jj;Z@=J6?T6=q6= z*eqI7IvQSifR%GAu+)vu25CXc?>L#H+h8d%6zb`7CBWT@c(TOEi^4t*PbG-!u>B&7 z#)%=WM`@~wc{1alzn9)S&!5vyR!AYe3=OvyV2u{i{ZIe38Yn;sx>Zt;A%o z&`YaWS+-)I3o%-KI_@(U+MqvcE`G4iMQb+Vsj;x1p)TW%8G#xyzml98Q9_yzi+AVG zEQ3k}2V?mh2fVmY(1x~ws_Yc`lG8PE`t<|Vn_twP72bhcig=hh%`h=LQlMz9oN{i;9?Do zSK~`q4d}qOuF+tf^M5q|n&fnGdVqg#N-40b)bY5Tb=18WMZCxpqQ-eQ zERPuenY2V*9zpS9VLaQr%&} z#0mtA4m=bXUof0Vph!lp_9+S%svHlq^r1fE#XnV4Y4E`G%s@i(JMm4>JlGjCeI@ zoV}_OI5L%9&^bXKAs>Hkw~z}kPQXAB*g2oxw&<&^LEjZHQC%q2K4;oOnSaXBWw z^W~Jw2}wiA5M&4Pzm!BeUS6{x+k(Cgg5@HY>%V0k<&W(0+>oKy^k=zTcSS{zd%4^R zdteHrRqEzhyDCRUOOMSeSNMRKLW<%4#WzuMU@~uXOdg(Y8b?eaqaH(-XylR5xF3`5 zjQJ-JTIrIOw;M0b!yEO&T-|JnHlf2iupwVe+jfznx-Teo)m(aFB8uv7e`X%4MNany z(^0NaG0hXlGLDAD&4tn#m6b?#(|ST(E%eDT(;H(#i0I0{STJH>@@Ad^0n2tY9JKQE z$~qo~J`Itsy~rh|=q`LP!Nd(QzfvC8bYeWjFu0S!ik^UC#@NP3@9Id^x<-VI_KAG2 zaq?RswGW*WMeAZZMC46KQ59WTqYr409g!_p>qeyF!`{%Qx?r-Ny}yWaDZ%e>$`vna zl8e$jlX0Wq8Z%6gul=thu7oM0NQ!6Rhax3$@MUg?v4g@;q_tu~+V^v%qOF3tI3^( zDXi@8?H6da!W(HkTMQ<_Wrd{&m*vW-1xogdIwZ}{b2(K92414;^O(j5Ij-|E7NaxB zak*5f5v#!hg`+`q_27FB8oj%y>|PMgwP~Onub{V;nlTKVP57R7%9VYXAi`ZR>d=Fb z0k2uv-8;bTU(SVS^gtgz9l*rU*)i<%$Brl;IP!gEQ2L>YWstWJWw%_(qQw3RI`sO^ zX=V4UTKR$uFst|+s@+S^?)T|yuADOqoMOUEsvF$u@&;YUm{BlcZi%x=y4?1Hd73H?Y~6Ui zxSC2+F(4sG9+(M@i+2k;Gt^@%MM*JeP@4D&-&Vi*8}$U!ng6>G{ik+GmZ5dN6jePV22680Nl@mwyp->2G`2%H^Ixw%O-73FeA zv6W@DX^7Q8$DnYO%O!Jg$_{pE;KDJ=J|hX64i35SfHOlbj)c-2g zN;&EOQ~Iv|{T2FO$D;pr*3$nxag7jVs)2@w(D-UJ2e8Oe6nP8G#?Cy55)$KJLwns2 zTa@bYlL^e)o2hV;_Cpb2%mFBxXfTin#SI{^3?+&erT}P2*5S{5jGK9zWaFD#yt`be zj}2ncradvJ^Nj^|2IadA(WQQ{a3>0a zjIOEEvOqA!_n?yiBUJ$*;{8f!>#rJTVxp3BfGc7kO2zWf_H06kqe5GPx|@o528sv8 zv`seNPP6GF5pdV%t=I)x+0}*5(g3im?22Tst+`7PVhj&l45+#qUCK`1`M%SCrTu>z z%z}Rf!w2gCtKR>^zHjXRPpW&B@B9C^-~UfL?au$}942&t_$5W!?d9f_dS`r&8cUdH}aN8V_Hfel=g=}9h9 zQ*I6h;^SaI45M7*v4WzU>!MRC%TSK`X-7uVjgpfb4U|%O*28N6Y2oKJRpX-o&gJx` zjB;6w#z#7>>BdJ<$_!iF+mFoQ6AxHGY1m?yj1=H?43mnbk-DNe<4G+&Ca19&M9G@= zM)s24L(lv#O~?zxsuS{%IZR@qKbi1H#R>W(NN8GhGbl{cGxILl+hU5zs2yAJS&4S2 zRD=NZ2Rb#xYX_Vd%np1FRPryR!zFk4yurJy09a*8?~wILqhWvEq-C3jHF2n%QqAV< z(I@!(0P6wrAQq%q(y;GdI#c)3AL+u4@sFTkr^{X8@zul~N+2(!rxhS%BJM#yW4&Oe z1NIbFh%;<1&pppZ?hG+<7iuOFi9{qW-34Y^6if$G7y2-5Y+jec8L-RLjXuy_4SR5K zdf4jph)<{v3)*{r-a2CUIVd-+_ejw30S#M5u%Lr-wAy6D&(vaj@Q36bsslzUByO$) zN~;mE%EBblpj>}zK~h@4&@(nAyR6M+2-~e-Zdkb@b$?U*^R%ZltM|6}76B0Otu4vN zY-3xs#nYbd-OdBT3a#G=$8h6pyvMRf+|PJDmkzGjZTmmxYspc}nY zigZt7qZ(62v(^C*>X5k{a%t>r&Kg|q z$nA&vVr}1_U)Rz+-$LSYb!>{b0JD?CDGRMK8@uvi7Af)_BQFO z9N_=lgOgUXsor!vvU{|WyXSg9%i9;=iP((iY|;N3(p|9pbe?F$*>aQeW2g$iM5i23 z0>%S6_hd>_CW>Rim_d%{Fp-FDWVWNUU;|Ew>5F@D{R+ux*VdCwVsF%mG<#ZHiHK>y z>mW*vf@Gy5Q(e^`BH)&;-5#tBj&khbz_Z2v=Kz=YJ_ov5c`)?yvwFV#ysj@lukTmh z?ZcZ!abg17p&fs&7^wMQDJ@}ATC^9f4*;h}@5a4bA7DI-?om0I!s&PY`WT26*W>yS z7d*M}`Ve6dEGPin`XFeFxnCn9^z1z#LO;MK%lGJ&y34J>8MmlRgx9gMz8Yxij_yaL z0i1OsD}%&=C;f%*PB4IlYdwc%nqgRRFgFVniy*JVY{D36K{`$a!+&|qYOI@-_28D* zWR6QTfFF6eG^OTpu`xOMgpAGy^6aO(RIPpVGmV*MHQfYm98#4HP88T}!t8NctKI4k zPFlS_rEbCqm7){dFN7tR%xYN`h2fSe#-D?^^V~sOtn!Wb{dpWfIxZtca)*IwB+`|6lbZLBcI*~95F*!!G}(+?U-2RKqrsn>@o>KSC!i2Kb*x^9dVNUn>-e= zm7I*3-njBWfO)rQ>O-;jjrkQxLXF4B`R^2x2(_A3`eLg-Z@1=|904#dp39Y=cylcs zp8m6)2iNicn*^6|I#~K&$NvvK02BVd-{1fICH{XWotLfl;5-=wSSd*O|D7OvIOi?t z@uwJe1nVdlZi%H9mS8M1 z&_3Zv$UudE-%OpiH$nIT6Cy3uI2-_c1K$~#a4}Gh+Uz^_MD-ldy69~EG3TBJn=O{C z1zMx>O&NHZ95j5iNX$;5jKH}L7Ia}17%kv~&Ws_jQ{c5+i`~d!x-Y+2>Y(uo`Ub`a3gz{=LydGeY;uNbb8cN|#V&Y}% zNMARyXmAOb{?z$VgNs@6A7?9>@YHNkSqRwBCB9~`ca|;gGrm}@Yc;vT75L;UUn!lO zEq@4q8LKXq4)G8(@mWW#bh~YdJx;vw$9$&M=91Q|1)rs-st`iPcocD`2&1ky^3VXX z0wVNACJl_4whnd*lXhQzR0bQ|%+evK(BNTZ4io`C4aS%Xf_Vj^V5Z)BuxxcE89+sH zH-r7_v~$!te#iE&=MLp3FBenTPXu9k&5}rHEU52Yf(;)y^HLg88V4Eh7+>RrY#u>N zA>|>mOB#hwP6Bi%=PON<%oATShr94Tr%mz!100eyLs}OT@7kN#;_UN!xc>b}rie&X z1g0AB!We>{=cphV(nFcM#9nEAMq}BFlP_wBN)1-cW9#tEKbop{FNu;|E+HvI`5*IK z?jMqGB~(C0(Rf=B1!$&g#Qje?htfjHvvB)!Zit>~k&H|^XWF9@3QwX!y!}u=S*Yr8 zB;22Mq6lg8Md;#Q$MKKY@`~?4z5?k`1b$i4C8Wcadwn1J+NF^)EQ`{cP$}icXsm-- znp_(FF-vHEiB*)TJdPCQ5w$lf^#5T6(5Bl98Zp~6DI6KPeY|r=2PyMwf5Ngt(9vs_ zhiI3^QJOCt?b4h*gaP>-zl6av)pCfBN%&kVC|-NvP|s!|7o_E}V)MRFQvK4a848%rH~a|n6MBKbd@$%~rm%M_OIGGfg$V0z8=aD<2=iU9|!MGg8sshA<0%>VZn?en9Im z+Djq~K`)dkIaNERfStn#*H%;3+v=%%L^%u#u(%)55`FVB`=+(onR_z_6JD-ALu+rV z!JOQ7*6T=P5`c1hpPp#vP=a2gEvHUt-zZJ2G|^EelFt`Pm;M-Z5!LW_Up<8E#w;=2 z8i(l>M`_)}vvtYt;ko)jO|X==--Zn8Y_V~Uy*0>EsoemOh&dlOdXJzMT1*RZ zB2Bastu%5g5jK-%aV14XhK1eQ#2<5Uv{gEldZy^)f)NsgS1Eum3Z3WY+UP>W5) zYW|Cl){Qq=T^aReP;67NUJp(Jk=xX$N5%S}a~@QpN~cm6OinGft_L2BfAYHFWEhE&-AgcrRk$CkrBUc&(3>Oma zUJnkv`#d$6{s*ZC-xDlV*Zu7MZ>4I-P@dYP0*DcF+&7wfQup=E$N1(`JTYsXRcZ76 zb$PG(qHs^zvu!4ghLPx{_-jf-CAtK5YRN0Kz68|uSJV~ZO&pe!S>x2nJ05@^9j?g*ezP$Mg0~zc6}7oCtVw& z{4z3cSUIYY)O2`8{yR>7hs=%@6&Y(z_R0a2ee^S0LIal7#$^d30m=iikNyHw^35E40k)_XZ<-IBJaAcB47! zyltD)d+q8DhAZDno6fcq&Z)3z*2zgeq1Nn6ceNP@Q&H_ZfrTi(U&i0uZ7< z!_2oNU7X5tz138>T5>AU*oQD#Z7iBLdP0|=ef?q+&Ef_&-#b-T6k^pvp>G0gyB}|U zMeigVE?0Dg+R2d2Zhn<%%CR?|>CLbEKgvs)^~w^rErTvU&lHg`A3RCE>A*BEx0-`xB9dV4T);y%?JrAa(r?lM!Ci#(3{W zvyH1ntVXH=8)&R~j3+C2Aw|r{aVnQXesn4S^!@K*I`jp)+J2G!U#=V;CgMNT%8&d1 z>+k>e=uLajZI2QhK>Lv30c}AZ5Ays5xNINqsZFFup4>52FYpo3nDN}%9b77ATM>#WBk zi%S#J{q> zOTm3qckbNU%a1Dx!1Kxi60%PQjkC6W+U`QN-<9kmN0%b-M0EGa$iH3u4}7Bggbd*M z{J&f+m7d4>f2nf#$p61V{Ey!8`AIVLhbT=({fJ!fx8(Uw2ol^XC)1zuf{F~JotbyJ zz?8(vUYUzgjEHN`kq?~y#m=sUxjZh{PT(w7E(ZpMlgTwAmduEK~V-A$zqs z!QFvn4gI-mjh$c;Vqz$Cd25|DEN2|}Aq7KotlGz89pD`&q6T8Zl1X%OoQ=+l+>>xh zbF9k8V{Ai0;|o^^^YO+WTKuACoE*2c@Qscv8UW?uw5$^f#Ru*EV?Dp{<2_0#kKP)sECsoWU)5zMkt#!kNv)dgxfE z#9W*PcD*F?>1c&SIy8xGI&8ctwyxOyOuB-tV7B-5%HLnYaZcG5<8%8TIC!jjP0O>g1RA1fc`I#pdbTOL!SjKNcJ}T9 z`U}@Hm=8uWLDQH08Toc=R^m7Z8jwJiSOJVRj9vpE0=RG>lC>>HHD!bIBI^d(V|>rJOZ5gsM(L_o{XEz z%CwA5urkM_St4Tfm$8c3`PkcN5}T+1bC{VG%&vq*i;yBIRD|ZmIdCb6vlT8er?Epu ztvrqdp(!sGP5Aa8pw7Jd$dJ^nuaQ2mJ<)V zNyYm=5~}|E3d>)rLPS_pDD_$q019|=J8IqUSfa0)a1!{fP$>Leu7Zu#V5__&yEDqN z#04NxnseBpW_F9#4m7k2Vl39?^CtQ#M)||5sDQ22^rl~0zw6nUCKDzNO0M>)iwL~w z^DW?V9bP?j_4c=4DD39bR!IyrxkTw}L<9U$TO->3d5bh~=l-=YNG4~7&#~RG$F-X$+I zu}n?I@I3J)qV-Nw5SfEXow130Sy=mbl-MYJU`az|n?iAb(7q+^G7-cvJ$ABfmg7iy z73Utxvd@`~Nd(ps9Me2N=G%UtQSkB8L@SxI5sQfPF>Uby)uHA`qoWSpX+wYRqB}BOfT!yjazFTq zU-EKib4=knMRpH-rGZQZo5suRNy)eGU)c@L@-k#jJ|EBrUdgyHJJl64B`0iNdoj=vI~ zu>o0JUxMo4mA{@%NKXNjiFabC%5n#`BCgqn>fk)&b)L}db|_F1 zYBlD#jHI<%fY^l*R938H;I6(8Wy3{ESpF9=U;J|mE!z_p^%McBX#lTSRMh&BJI2Dp zOV@vpKF3{zYo&IfY91w##j#P!0KB;)I? zB#GKK*HT9*%P!&MP9<1g!Q_QFDsAlKe_KetMT#^UVk>d|5VE5ay9n; zQ$H-#9^Zey{`=3U*-yOwa9QH*2a014{(NF!6^k3-D1?{L2-R-%I|nJxM3Th3rT+fY zAP{77N;V`W2Ni8DMS&x7tq;b)?SZom{dF+r@FAwFJbDIm6%4uMEjaRvQI9<1L-$wF zQl9_3Gycf68sm=(|7PY+E?s=0idRNo{wus&Qsp;1aB*$gslkm==!qxzn*l`uco{6J zQvrw*ip;xH-!=Rlk^I#7Fy%iYXDFR7W7u`m4P&y`Cfu(WKJ_J?n(-`6U>aSOqJ}1tlQ0DO!W`EgXyF1#UcJLA>fBNAEQfwM%KW)4oa&^KY zRk!5NKmLdS)_0U)MW{rxhnRvJnxv3Ty8cSsVChBNSuSTDU3YrUQu@tBT2eg`&8E5^1u&78#@tX$k!g_tF^Cpj7Sfw_F! z-DvGK9ITZtio;~#G7=Fjd@EcpNzTO%XJqaovRFn#M0=vwu>Po_S}#11d_}--R6mjG z0$s{HRBGKg!GbK15<}+UN%;*ZjPNKu1(@I>eo{n~Tv0l4C|0OZ^p4ok_H+u}i4Mfd zMZCw_k>8``!OBQ-S);Mwpre&k zK8`MW(L8Bd&tFuLGu{Xubc1T%1;(+3O%aWoV%-A|;@x)zHy0Ql>5PpYDeAt*f&sB@ z5wx1Z*YL$zV<4nc#YQ9}MkMeU@i*w9NHe=|VUN?s=Y2KdBOtbotT+CRdyPVaWD>B; z_b9CFVzS4xGjOUmKvN;(!J_4($>9y=@V;^BIS1q=IYRC1L9UDw3Po=Acx1Y8=C4?B z9?DsBV|1f|1e20pH&Jbr2t2 za>GQuZnt0lC82ObeBo_V`9j+!3C-2gcT5JpDqjX)M*aKY4G&q60<34&DW>y-mcI$x zNo6^tO=BU$8L!s#_5*YY$1pxTp|$erN=*PqC#OJ>qwi2g!e+b)^p8(RoP-1SA+3!k zuO{4uWN9ErU>Iv_;xE2OgKKN=!UdV~+TD|KCN#ZJ7S-0oLuK0K+6%AJ)?SRy?hR}? zyn+geN6Yk$w~*ellr=kNZ6esOyq7kf)M{XVlzo(fJQ7>K5caY$6ZQJdM3^*OI1A30 zAnjVDLA4$b>m7>`TD>V@i0*Lkvpm=EFvsIb9Ox+RV|*Xl<3iBq3q~jpvedh5vq3_0 z%WgGB4RQO?H>7?Q`_9_g9yO_07wE8dqnZ}R*UD>EAny&%iKL;4bs0EgG=`YnMM%3! z{jOt-7^J#RI7Ci}Gr%Od3#=(93l}3ov+iK_>p`R0J~=-<{qSIf$z7F-FpZM35VoOR zE5EUrmTu2FbHb0(-d;Si$XAJ}=gtz^_Or(DC;QE5yIX)uxxC-uTRDbzUTT4%;YeY> z1BaT{L|7zpzI-Mc2l)V_t+67XU2i(QV|LPX7Um89tnuD{(;A=;VWC_q8Rh8XLFI?~ z2WVY>(15K!TL6x4;Ul==jwuK=`(Cj32&#}yPbI?04aT{hlOK!|J+R;$#Ul&8wMW#6 zPfT+@SKW9myrz@bKc`ON2u4E!bj&Q!&Tso@v0A_T~jc_ozO!$)mvHK z-1*_%E0$ABQ%>xHI9=wCs5kMyX%#~W(^fz%MmQQm@_0N5=yGWVp`3+vCFTUtSk`N6@%@ooh;n~6Y!GjHy# zNVbXWXVCa40>G_^I&snUGj?{eVQpAXZtY4UV27~U(Z^#k&Z5{F%fP>IF0_TDtRK@E zY?P3=5)+1SY0_web$E{TuF?oWv9`*vyT~bfU?AV-PXPM2)nHIPk>N<-H zvu7r+d~d(&bX&c5_OSCmZ8#vPb!*?M#T23Onzho4dK53zffYL&oku(c*97L{uP?9Y z!N9{9PjqdSbx|50V~A69q&o+(f5zLlC@$@+Zo6U1TuPuzvVs)8ft{R!2YY>8+K5RP8fk{9}<>e^lg9UeI!%n z`viYkJCtip+I=Zcb74akjzOwgk%fgLq*PQ}qv#RIv>K=5L7uemMgi=uEH6H1g0X&b zvyJ!AY*n>;5^E3nvJ+aGsS3(;J7lV|x?8%4XJr>NonTnJC{&Hihohp?aMYT0;Mpko z!%3#aI~Rt%DBs(da#zIDnof$8fp@tI?3Ix7Wh8ljI<@h#F@;`>=|;^QU<=0#?eX|9 z|A#Y~PC|~*f8%(olyOW+kdkAFwI~DfM<1ow@IP*VLclbM#>kEzekl}AQfs6H}EgPt};{EPe-=%-g!DIQ7Du`cMEg!0p-0Z%81@qWpv4BI1Z@zZDY zuUE7ie)vJ)Uvq$&;Z^AmJiX@WVB_?@du9D^#{5R8pZMa_6G7ZD^T`Usy@H$;E`5Fm z!>-v|;z4-8p!R@<&TzvzE5|3sh`!z?Mh57c6FFvTY&>`Nlw>pA*Qp@c>r8XkbS8!y zSfN3;F9d5(&7S(%=6S}T*>wo?J| zHCqpak^!fOmEc$LsRyTkj4*-gONfX1Gd!rYgAtp6lU-jH>1V1M;?L3VnkiuH3+usM zID7-tcHgaBX(DX)FSNASigZ-v7)}d+Mb<(NW)xany9n4Z3i>hnpDI$OPw z-HfxFS=Xa->eJ4D*dSlf|E~;(ekFeX*PcJd|NRE%e`C~09RFOl&AEOt^T!`2?sdMV zC-#N&G$|F3E?_{D zljB1~J&D12cjNDK`nr?ONiXsqdAbZ>T>d(I%I6XW?qXi?@;feK^80v_|Giw1-*MqnDmWKe6*SYn)Tom5IOZ*Ydgg2q45B9p+1DQ1 z{o<48B}ay=GhLy3CWiI|nh?)lEKwl-F>5ZgLGQ63TH=QgYRxgwm9L&S$u?J7y?}WRSd$8?^gH}T9xO2snm39 zhZ_*R(KbfzG(;${5Ewhv7xH0?-xTExSa_o)w&0MqXBUo&0t&MwcaSV9*+)^ySumZb zdr7^9j*?VC+)9v*MvO$eKK%xq9L^2<_`|4e_Xe%@01cIF`?NC}owm`Fx6|l?P^f%G znL=J_MPjM-BDrKQW01JkanN{~A_`f^Inpr{=%3tK>R^vMug#H0AUW-34IpixX%kAB zx55sL6fPHkVuje#4%=He+Ma$Yq+E;`MjJZv!xX1tWuIyjqn1l&63Wz@`jS%K(!=`( zx*)WC8M=reg7LTYdPN|y7C#A(Bs$FJcq+045GfxJed7zY&4d7A$O()!V^_j|G9E4G zMP`FDvm&hueUNcyaq!QC6ksf6915msV?6;F;}eG3fdvnxB#|Otf|sVX}7-`j~E-xtQ<+iR3z?Ln(Hmz}afQwR^|I-f4T( z#^Y3&Ng!9F?(WwTf{}+Y8!pa}P47GvfaRyV4{39F+IWjYE0>f>CDv5UmLAsZSz|Z? zVlUm6N}8!Auzzv>khP_-+~2ObpRCAc^C_wSLn1*C@g$AM0Gnqmd(dvQEb_lj0$Mxn z?P_-;xZQp~!X|2>iFa_sxm$JqZqT7+ua?q4x9FZ45dBwW{~gvd+~q=cTJ7$rbMgU& z$g#myH6Wc8h|=F8u9)B)1(S7*FXS$Tf_=0b5@DNmM5J(xUA3zsba!~|v9M@_^A^&B zK8(NoWqbkuk@;YOZVikvpg~ld$jI8!8!;-sY#4$*A{Bt~n+?cY7_qd6N>b)m9oEMp zs?}5KD7H517(^Mx#F;u{<3WKoyo8d<5c5P9FPw5KorxV28i=&tdlzVtE@SfR z5%d<`%7(T@0;SZUqz20)HQ0ZiCV3nEFTK}&G5s%mE0q)aU-U()KvImI z(1`y4>|~q$e;qCZSl8*7vj07-)#LWR_0psL$2ZvjZ-@Osr#nif0MkAso@tmkFy+<) zqZD~S>7kw)^1}C57@t^Hi-In%(XWG2X7PKtk1r<_%V&LoZw3BTz7?pvT20l?_w>Xv z#|Y~5Y~w*wBJ6QtpQ0p^39U0;aeA@>lz#uG@A038@D-sa^SQM{!8o3lZl9tmY3kkX z%IYPmj&`b;RZ6r>{zl|c6>^C%+?e|l9)*Oyx+3r)zlT4Ip2l%ps!~Rs@Cu}1S!tG0 z39~h+I(e0hn*Yq|lI$~hPEE8c2*`UcLa%IKqBk&suXAT6zukUc{Qmv-@YA_$_R76= zVwJ|!KvOP{EDpI{GIy3`ODgNDsM!?1Bx+Ge)FFk05&^6JDb{NubGx}}4imn)k;a{T z;&#a0`fm57mD3=(REic4ERf~pWFp*hee%=fi;N27u+b(8%ADXbQHK5&dOTF7drMds zNgFKwjeDmTIZvu?*+I9~1$=DZZG@w2KkmI##y@F+EjOC`2?9WiUgJH42E2 zcYcb8&W2y~E~T^OeG6<_YI9h*YG0=0)RCxPow(jFWA1k$Wm zbW8uHH=EOH!oFG@7&~InMXOpwuS{!q_sPQsU>zJBq+S=J1M`o4XWYvEC-{=_zf0xA z=k-MV@A9Mk_bu#y21)Z@E=wBch_|kR8~U>~oe2GGTVM|IjfYXFoBl>Qs$c3wnzt!i z0xvjk_C9;=MxLpty$xK7dAVM~2w<$b4lh{RdUH~f*G+Ft(;^vAd$Ii1^lpZ+vn}_Q z0$Pm8ep4vl^dn59{Wi0HYac1`c)=Ww){N4xZC0<*aI`}znfzPm*L2fdIRY}C6u1W2 zW=4_^BU49?A`3f#Sjw?M%d08XVwH-ClCWr%%E`O5(K5m$^Sa}_@k@Zjr)RLtn)k+a zUcEyIz0H|NRa4 zKS(U?!AYapPUZmDz9i0q-o-!AX%w~e>xGPDsi-E*IuBx|pAEQEr zH+815xywL9OPS~nA5$23pMm++hZjja{c`jb?VUI_D)}D6{~FJ_9Tj#nFbBy|d9-DW zvU-7hUMO_555o5{QGS0{%Oo0~(8cD`)$>Y5U+JzOd<|tSEZhH4%vsOF3gt%bGWRn~ z{efP!G$}J#X4a&*R(w*wKQUZzkYqF4Dq88(8TaGCM(oU%oU~>x(sNoxeCpBd<8$f% zJv2GmCk4sX=Rg1J)k@7g|0^f~Jf8nw_56R^8H_qTdw|he-`G$7WXa<{{y|2?XuNLQ z!#ACik+oBwG zCL0HR(&zvpqCr!t7b8OB-_G0TZM%DZ#uauBk#eH;h{js&VH0rQMe994XX-~b5Uj*O z8*gC*2;1XxU~t~2sFG%_bByUpmAXAL9^-YBqUAO6IU6G&Jo)Vje8I zjN;D#bwG;0<+{kyC4XCx+b@bZMq8BiRy3A?@g}$GXRQ5m>6eryaJ_AJ=FUYkFbr>E zjwlsoQ?Kcr@IX?=VR3W1vU{ej{wanSM=)&C|wU?cv|%vpj!ACF~CL zhzUE8e((>!(eEVshiz+7)jxA(te;kw27-aG?+U_rLvcN{D1zt?EO^g0bEDQe~ zQ9UL;#jvreWafk`o`_D**J#c8voW4u=p(S1ezSwT1b#s(;_f{-if&UVne_WOT2{Qv z-DuFEO5R4m33q002(7;i15L{Rm0&>N3UoM*b=q~fV;6l^ZtEh?WFDM-S1>WKf|&V`OmvLxv1HCzZ@X_|MTDf zgV6b+_P>?G!({&F=a2XQudn|f43qI6xJ()ItS>&gcZO-J`Fa0%C}NYP8a(W`DX zARiB?+{_-24$gSl){ZQwXd)CvYKVPnApm)e}goexS^bb&W+rfAtJQxx7DX z|HZPd9CTivxxp#{FU%NzuAADRXu6#9;bF6l^4v$iZ!`HXIYE=?M6TN2IXh)*sjKzx zf@s?<%gAG`SLp%m4mz#2@Tn%jm&g&58z`E|vtzO1=-2~Bol+{F*OE_Typ7F+bQ}IJ z5)wT41lT11RS&Ci`;Th<(f;on@c&`^^;x?+N}K>uxqQLakAe2BJ6^N(mp4cLC}M*SR|n|CvhiYTS>~<0r^};gvg^eL4BB zRH-EPf2s1A|KVHg|2OT^)8y_Kr3v|u@-^8ZXyK z!(=4ST!>9at(K2gk5?jSMoJLjXzu@k+-a-$592SY|E)f+CgT6s9_9b9^M_yK&YBJD zVjW6DCGs#4ZayM!9=cRw!jS6tu=L_!toyby1A!(quED=0G(jPTNJ@(WEUb%?A2gq# zXs9YPtQ>{j?OAX1rad_BH3pO&=e%zZ+OG?x+ZQ#XTBFtIk0=A4tb%pwW>by#_VBbf zvb%!z81!K$(TN( zBnqZ9ocSu_Fb6}_-V;OkDC)sdMA7jm!DMLGrx`8%@aLmn+R;EJFW)|mOYXTr$2LD zBbPsIX%n7)(YK=Zv-0lK5~)S4o7+TrIpC!9S#c@WEfo4DL~7QG*dEJX2P@h|ED1s( zkU9SP%Hq8hxofe4v=Zh~YL7_7nQ(%0{+%Hra15g^p5#Q#t^YA$2sUIHisl+GoH6eC014 z**FD!PM!Xs_qNj_T7hQL=3lflG|1`^I<~}vY9$37*);O`k&`v^jRcUK`9lZ+y(ekw zU@R9W0O4ijQjFVuw82|qJWvoxU)VvG1~5-m+Z`^{vi~A2pvx zkizGPFq!Ba7YJ{iLXsC5I(Zr765&pS`Zm;yGEGK6v=H=m1pSG6x|&(fddV(Dc!|Z1 znd7Vj_T!}pPZ3!_YXz)iNFg_PaVwn4_(x*6*=->VT{;RJV&BM$nJ!G~v!T?L+%hM* ziX!g7S1Am@&oT2%8m5e9#x4`lnke&S-f_m5KwtDZZy&KF$!I4zN7h^jI{*Ba00~yin zXzb6{^F^|16g=!5MIKF5pn#;J$_WK(oOt09qk>JeD3%E@is>l9$O$W1g>5XQx-VNq>B~E-l#cWI$=mmk=qT)gT5e%XgF#N zhUg1-i^ktYJ;)@Ol|V(d4-!H$>YO&Ad!;bfe0fP=ggex_4$<^7O^E zPv5F|HbPmUkh7z)4m4dT{N!R*LXM(vaYv?l>iAR@RJIm;(O7bFJvfbvnBM}!l8Sym z_-l3prMRRzmiRG5Bm?IB+q5ApXNp@v$)Ovgu=5f{=eUw_}lj)fIuNYA>$&)qfRh+lY@GQA=PXFeMq^CNHIEW)`* z@u4|Jv(F*oZoI(VEyCbe>=He8f(gG1@3;q-2bhvEbm!hKoQJ+2x*R9xlC28(u?bk5 zA8=f78TGo|?hkrrm>zTnUCvx{OsB)+%<&L7jY&^QorO{TtT*hKRiOgcL>QxLXV@!B z^8&0;vO;%;(Fl$Hhozz(!!A;4a+1}Fd&`NRDKL+RZx?|TVtdgcOTQKh+>26U%0PC| zLZNW18sjyIy6+bG3Tbs;Q9HYqp^J_x?r=Z!0<4W5pY#Rbu6+ERQS?3v3U3jDY56_n zP!wCo@0=FN=s2XPph;|YbL#>G-xCl#Stb1%NXNO zB0#Ks4}QmSq}nP=ZulQy54g?wKe-xzQULJ$^S@TARulf;_1dHT-#0k_TW^|4+h0+b zIQd(53ug|}^&20~ho+%#%ZUV@`6lcwQVP*zEu}b$9Ght_7V!0hShrRx3r5{UqazN@ZLOS3p>m|tYN6+#p9p7CJ(*@ye+-o= zUJu>Um1IrZ_mUE1}4BNGF>zw2*Uq9T;aH z(P_KeZ1nA3f3%~)`9aZag&tzb>P@e-E6v{irI%yiu3grf+>$A*))WdcJR*_p6$ej` zMMifK_ELvCJU?xWdIK?Hq%k_Rchs-D){j5pz%oXm(DDi0w_ADLe}_4Qr(ys-?hB~G z=q28EXQ(d*x0*6%`VhGlLJOCHzh2VLf^VTtH8_)ys{O`ng*E|*+REu5qSlG;ayVm( z#OR3lU~EbG62mKFepnXEMP66Tn@P-#AGd)17$$|v`j{M9A(oB3-tr{^;NILRB}MX8ER=o&(XEJtJaV(m|%L{aPJj6&`$Aey@)oct$aD^%tP2l-@2KSX5;Is47QAYq* z1aM`MJ^Lc^8AHc2$j6R!v^PK6*=SARv9C;6X%$YDYXE zqn3t0b@~8%q;6&j6i1Vj@#oUkRU0i@+QUq@S=Z+o>$EbR=;L;HYPsP!@R+TOlSIc5M)~8g6N%bnkdKcQ6bbM_ zh*`pkzFg(w$3~S-e>;keGG=G>lZh@{)Dc)4d2<)eB&7yx2ss1jsRV60z=xl%pbkcn zv%LA-g?szT#nTqOg;~8q`Xz#B|+TPq$kwX(S5lm;lniWXzdaF;M52Omx-*Bs`L4_#owLKTN{>qqx)$ziF1SK)gA zR6IESG zsC~8_ODv}onCv5)yr_n;CC$7VoNfZd-?cM?CH2r65%W5RdX~qr!6$e8fD`4-)}#Rh ziZQsfkMSi*8%d2}kW#+QV33JjeKF!p;pr)6Y>Gzs-}s0%@fY92F%y1FKvI6EtyX5> z{)$o0p$HDPtP^X{{@Zzb2!gxaXtf6I;m|r1BI&8?peoo)k+@FVjbYm!bwJ=c6hfsC z=kcJOf&8G0(r&cLi1K|U`6ISLYCuxn?+vg5v`fKnPC@jr-@kt! zIq#*&)v@~4p%5Z*C{J-go(c-U!H5C#Ep*JU_?C;eBlD)Yp%eS0bJ~^?Hgy!Dobe@k z8j_?d1!II1IcbQhlJ^BzR197_`p}2vsu~}=y>8no8AVbo)qgpdHI> zv(;{r9dgYmZ8U$f4$ZzBKUwuydqh}pKhR4%-ILzn%sSHMEB2(?w?XpS!}3?B)$Wcu zCmk62VNG820$3N^!U69OB~ixiW&^-7x^{|L+NT|u>Y=z_NE>1U{b)jvM1SMGS^`RF zaA;mT#xn=DWzJLtQ*53#hC`T;RTulS$r_#KezAb(s)YX!ZA4(e0=KN0=Tgc zi>y;3Us<^^v5j?T?n-U1id~t)W^g=`&}d86#Z~wi7(cR^`;1a!OP_U+cNiK{gSPdeYBaMjvKpFid1-BRTOI3# z*vjYUFs$*m+kk4nc)kfVWjCPKSKX4M%97j0L)NMG*@6mJoLa!YHWGT)$eDeMt*?}& z`$|?x(_ZMLm#W=RXH%TC(1a#xB$9{>MJ+!nqH+xAh|QZW?yOFu+rSf?g<#jTi%AVaWvkB&HE@>b|7f2dP*?6Hx?l73@co#TtStPi zhM!2sXx6$yPjVkwR%a!3)Dkz0gga^^a8Yeb&#`)gtRYy!&ezqsp?GCiSVz_B!e8uP z``8LA3-Ns&&kz(Q*P&V0(NDb$^z{dw-k>x3kRe%11Q*d>PRZ3zKltn5sU{w#Fe^zN zv@;hZFKdLdGNn5Zd+t1J>nUmLnVac4`la%+LIIBQp03cUEATPPHewm@fJ+5r!^ov9 zJCTN5tc}QOr;_4Hbq>-lV~d|t(vkI{gD1~6sH!Mb4GH_78Cl`K0kVc$BdaQr^^@x^ ztzo0fuFfiWf(p>znaWJ$4s8UI`Ux{d>k=<}lsOSO3rEJc@UE=d!xq?dZ|Zn6+Sk6g zY~gP4RxsYwvsEdTj>t3>7Li{gCjdX@K|_0OQ^Qc|t=+6ovMof?fh=0bBv&f9#iTpHImSNCr10m4V1t zWzC)7Bk&IOR;u)$h)-T*DZimL1JD!lmvD{P4N)I(dxy2|y+WkXz=5bH_%2Kw9~CrI z6WFs)p#ko2JkhabTzp94@i2Jn*oAct)9JC>LxlnXZbs{cQc{`elSz+;$~8X_Gd{-G znv%Me0uQG1z0-P$Q4oY_H$LfLG!OJ(l*U!E>Vf9W%$H$ZV5W62~h<_8@e&@h9KE?BZe);FBx>)7sXjJ_dQUYi{C*TpX1v1 zp~Z12j_2i;G#SL|Yk1b0_>ac(J?MF?PA?K(5OoylLk0he_klg2g~jHk-3!??$%J?v z1X1ix2!>YYZ`~A|7O03fwzJ0IgFR{tUbja!R;-lDdQ*H#Y}MGX-RyN+iRLPLb7;8g zp@mXTJDqN4)IpOP(LmKycWz^z(s(b;Qz}yD+Fnew+x1tqv1xZ(^t8uzhrdhFn4-c^ zs_cb&y9OMuDd0GZz1L0vw=n9Cc+UDD9(2%t+puBj>}K!0JKC`tw6TA)ABwy~VRH*8TbwC$oLD-{aZHgn4g^O;!{ll> zY#cvM1ZRX(!#Uz)2?JaWF@mT;93iq0YluF?BO#ZoA+izch<;oLTo7CtTq0UGQ4OGQ zL2+eqiE*uQ(Q);OB)A%q2+|4?4N?!15z-S9RympiQXi5b(jyWkQYezD1=X!+3KkMJ zQaF-2(mWy`cL8n(bz161s5&ze>N0xK!53P(uBE@Hi3PK=*U8!~>c8HrXXR;|?0z`H zBy+NRZA9_jlO5uG_UX)>TY?wNyBuA8s_E@US}I0o zVPquR&`~Lk@pW~KP&%?yhCXR1JD@CrxxtN7T4-)aT#PD`j2S&7fFMa`et#WD)~~1^ zlE1^~IOn*-7nwVJ+X6xKNnEcW7GD|fzUOV6t#z@3{^-GqD)Lh25G{^gYaP3tzmxV z?aYO6X-^eaO%}>wz%;WFkz-3N2KpvoH3RF^cQjwM0JtE~N~TySY%1Qg8?81yLBode zr_<0~!LXD!mqup1ac)v&8Tw-^?1+FpZzZsauI6&xK zpPcN09Yo*Ra7r5N5D_l?xySD#z+lNx`3A13FZp^orss+aSHZ!jfFI##b6>14rFvZV zlMy;)p^7o5ccp0<6EjRP#k`8BJ~D2-@|WU8E;62tAmF2b7~$2{;b@YYj*=qu+~OOs z@J=P$KO~wl1*Q}i;4$3D*=^F)Zo+LaOa|zXUu6O`daL5Mb>ponzJOD>v-5L^$J|Xk zhC4Hi{dEU-FXim}e3?<&-GR6`bV|fjSJX~!bFc>2G zfZT4KOUi2nE9ormGP=FgyA{S4SMi;o6EujhXsT1L|Dp(%CVr*dHg+gZ3A-+nhf|uB z<(%At$yg=uCKGqTv?HEG_0vg=n`}_YXy@m8P4;~(Z7IKa&)CV&r47)_D=}0BTKdUK<9r{1#OY7(0bI(@TD_n}m|q zUDl_tvX2yHmh>PZc)`$irb6lnosab996Aecb+?N&1=lIma?ED_jd6Iz1ra<^lV=4D zmyi6H(*6^}n~A2RWgYQK!C1&aFNj41HxH?DYEw%%By^^W59-`xN<(n6Ny|to67ceP zRK6`j%HVwJc@A_QBK;y^LP5xQl$(lrMNFC#Ra93_cB&O1i9Cqgq72jN zIpXvc53?!q6gs7$SaYasZQ0yBq-DhHlqj%is@*WP2{bA0o#Q>cj1%cJe;V3-O0Urb z!7Cw1Y1K<9^$kId44FAehR|52yj=5f7Me!p_ z?^djnTlXS-YGj3Edp?W<;R;Xdn3yjL_wbkH#}!5MD&*u*mCSG+UZQNt^*JL0hv64o zQbiUMzuPjxn8?7v7Z{m?(z(U&RuuCqK6c4kw-|g*7FhmnL?x5+uu^3<0kQRvNu0HD zIMGNAhj?obel2DXF||Ef*9SJp;n|ESqtggkBcz4rU`N!)OoE>;{>LKxqWQmS&lB-K z4$Fs+@jt#n{EzN18UBOHlF>i9!@SJz8^U}Dd%0#jkymuWL9b{yZ8r*qaxV zm{Py{p-`ZAL;aJmh4022t8H?P0&y9k#h*IlJQD20Nr3*u_{C{FC*aWkRLF_4>_Tt1X73ji#JaxuexCZ>&8W zHAd${u3gzwJ983K37LweRzVxhQEHWjUgdde6&h~uCk+jy?0VhzT(6p6FMmQM)-?*y z0)k>B7NFLuQHUbqpgkIN+Cz=Z)F&cwsL(togN8!swpx<=M&Eqm;9P@{6<0ul%px?g zzeNhC0_m~TZ&LlF3Vxl)Y+9dwb6k@6x@L%F!hZQ{wj2SQ2}|p z+`DCLG>MA|#_u7OL7^ZXD}J^rhjr`EBpDfMWOIzW)-U%NLl6*{je!CA*+WezK0z@* zrD-UvRx|y3FcAuc2Xybm&=4CI|MN2G#?SvgXu@R|kqRvlGa6s#>EWrqU$KO>wtyvO;?cgB(Wx85W~ZY=mG?`QG~?a?X6+C} zZNG}P|7dh{!RG%|{CxlLo()y@OXNfl`y)DmVXdybV6w$z@bI5ds9J?998XQ6>Ao^-FjYlSbKP4xeSGOQ zB7l-ZP|*T-e8}bo1+^{b8p^Hgn>uo z&Otiu)m7lSVs#^%p}1{el%;paa*EbP(HaX=v%fznTwrf@xh_pytQ}|9+NTu0f40iP z-lUB7CMYJOSotq${3hyOWY<4TsXorGZVIimle_(tp&Mz3Rk4C=XG}N)ObNX*!LiXV zfknS!p5y-a#qYcDU;q2a=}HE~leE`i>l8&^{FdR*FFaHq$y)T%l(t;`L9Ydjj{>jJ zZD3lW(03_yc0^-z6OcaxTI0>6K@Zw*4RE7@|4p_aLjWQ(K2d=d@fBE{&SQ!g#)>!2 zjvzioQR@vI8q;$u!-2$(saI1+!J|^`|DyhHy!@2xubbolS4x#+{NKm?uirrb*X*Zc ze-(uZ4Iq|t6p3rs^j>Wk-%Rtc#%v~|I0g9W7QT%=^iANilqCmrE19783kB`^zVy8e zKu>Sv)0KQrpQ1GZrD~DS>LOLTrUpUs)$|}T%#YxTPfO3?f90oWXvIHQo<=qWW1Aj) ze^#axCn)}U3w-9*63~mfP#$HHrdf%LM4U*kcN&^ zw3V~E=cmR>KqV7l%`12OF>uwmH<1#y;X7}GKb z+&WehO|9L0(^GG9T1-9tBb&J$CHWX5=iir)ZL|NUtL2w`|1VXl_2m11<#GRi{r!(| z@#5Q`%M!bvL{V}DpK1{X4K{#L|DY*nJxivuz*Z?gMWvkFRMh38Z16|f|LZwg8;`$O z{=etdWd6Uy$NYcaVE><=HBL`^%>)mK%2lEcjTKN#%DFp7*FmBbCvr?o=Kpiz`TsbL zc0^^#+&yU69fmSDAcbmD0oK;3om-85hn((Qj0>QR2NST_OSsp0w}rxReIbHja*6y7 zL$ooOO`(9z$h0={D}U+8#_5_DRWU4S-*#FGc{`RJQh&v$gCe)a`DkSQVW<20v~6S8 zJC?+Jf6zYZytl1C{VA^*(hhe7^3a1*5AwN>8Rank6j^N$WCj#p<~h-!|di%*z@r9Io|p6%-0*yxvRH@%!s=rj}l$qvhsz zb}7~DIb6R_MGFh}#>T62p%BH`#@|%}KGj7-HOQkdnj-ZVXP>nKS9Hd@DhK=EfWk5= zHpRJwt1w)iZybs%IK{Uk@|G2ptEDp%BLHD`Et*EneN@OlDJxz%BTMtsY;45jb~rdo zpSi$3OzU#(fCR8|BNx9J-~K5M#}dP0UifQ@gmRFQkQZchdg8Oi@7gTqa^_;1JVh-Z zuZA2NThWqO_+8=Q1kt=THElscBEa5X#})u00z}sf-x6Mf6dC2CyVz#Bq)u5AEu^e4 z&o)T|MSQDEO*X8!^gTqUt?vJo@fVc;%cWB7d0hUlK7Ty_zs3E(l05slEP4B{G;?$P zW*Pt|GW{;7EmphAM*_VJUAQ>zDa8wx0Vp}Wsi@*F$mbPcF4i17^$ERyS}Zzp}TE*p&Vl5u;_|R6v_*eg0pJTCCPPY=KCM5nfEb*gg-x!U&N2r z528!a0u)e8Fy%3N4I`cTH&7ltyZO=R`)O*;r|jf?M?o9Hkc}^d#e&G+tAPmze5^0M z^vUf>MRuBcE18W?!ZUSDjCSG?2=q5)3?->~u(pf|3qT5=O9mLJKD1G&Ck_n5UVms8 zgSk{Fnt)oHXlinxd^G8i2sj{`bV3dJ>Y<$*P8B1j# zKVwcrRiOy7%hBfKWZ26YmJ^?&$S3u}onmO$DCQ+SJ>Xal)Oa!c1!bNVwp>Nt3c?Ff z#*ZU0CyHZ7G3>OZ;FwzEz3XD%D=&}8Dz+s^fy<|<| zHBc%H#wak7?1f^@IwWnZmIi!B>>bJ;nTnJ>__b&y`RyLhF~!k~XB7GDu?JhSp73K+ zIDbdmm*xRW`yY*R=2V}C^K_)MPh<+}Q*p1hppoJ|Z8s7RjCP<(HQ?bnEpA9-WqBty zCDf!N{0VchWDfbnX4JBI%M=g>570v`7$}3^WblHlWc3nI>x2J^@Gx>0l+!@+Ko0Y* zqkvyW;fLNpS~^g`h)D8Lkz9P43_)BQSFy&~l2#KvOiyo=;YRH)CM}oAqDr^VkECb2 zV2?~mXfuyTL=61hpfgfwECc}U_Kf<2cB3WZ0*G|>#6gvhFs*AAekt?iZz_6)2Qt=} z{EQLz*gU1PZ=PDOtWwdareYkaTCGNu7?P9StJtYH4t)&1jL{RHjYxSP%OMmU;N!dT z%%ydL`EeE)XSH(YOJWiZ34bd|6@`oBn0Jj zHkQf7HZT?|>XzOAIqgQ&f5hJ;sfpc*SLSHK6hNMYf_*6{AZexDiIIWe?i*vLw(2X7 znXyAN|H9e^jNfbSXdnmAuSfS!L~$A-?gq-_x+sSk2v;)fiQ3&2ss(VZd2cYU6S_Z~8pX zXM$6Yue>D%R$BNm)=Xh;rGo-)2ny1M^;wFI7&19Qn3twJv6!RMMf**0dbV#9GC*=2 zN)KCrQmRIY?*5MtasSutfBA&}QtyAI`tw@i{jc`u|NkxQe|u+VjczMx0<6m8+PoD; zy9y&^o)ic7iREgtp)|%7B|^6AqN}A4EVRlZ91B|(vLmLt+DYe!=y^{@loSo}CGo;6==NZM4t0kN-wpyy^q5|?M?KZKW>3Z+ z&g>@9kIh+2dn5a#ciwGD9FoBpJt^ReaM}Q2ip} z4%?Ms4I>EwPa8W3e%0e;2y&C*BJL5kTJ}r~VF$NdR+m&GwNF(-JMQJZ!)IIiK;j!| z2X z0Dr}b={W-bij_mZg@QxGouGsQ{T=&)%TVwM0%cs?L5RMigOJ$h_QmWY9l|jOWLcc% z9yEG10siCM|CeL$3)=rwEA>(-F8|k_KgNIg2J(Nu*-6~}xlF`b7_OIeuH)lCw+*XU zqI9L>nUCRM*k!lnTmu(4^A~|{{r4K$DZhB}9D!e>YMb=B{PMzy#u2{TY0?wIPwv9{ z;4A?Q^9u+ttPqNR0~gi!D;LcwtTPW-zB{vy0~E4gFm-95=yURul{B_Mi#&dPf3^+- zqHv;mMd$n*5&D62V9=i`F9T;z!S~TvFZ8D?48H!-y7Si}nEu2I1>?8a#Nrv{r$q-~ zOg-+cI3fwBx^UsJlv7sr2|FjXfG)Cwnw$O@ob=nxU zyUh<2LYjIiP0Y%v0~_|+%}(PK>mQ{}4kNjv`5eBc@`Gi@3_IPojnfWD%QVn(2^%|y z286XTKc!I)n+4f5gQeZ8$jecX$ z8GS&6o8T}Gd~9*YAM>E!H98{;aiRl@_mpQO_QR?za|Zyos{vs`ZvqQ5Ax?lu;GzXr zX`HkUb^%h;{>tCSe;Jj$qQ**a$Q#V?9Q~mHmhcCflDSu6>(^?(?KIm7s7gMR7_t$Q z3ZQC9C<9Ey07c@g7t&kT>e-ly^AxVQilF3K(sY*2g*U^cJjJY48SwS`*_;007Z<2VfKFw=^xwmSeQZmLd7Y*f&11B^50Ii&{T8-zeS|RI?8<~YX zl}RYvjoQsoTkkt3Z+JOV*Ca!7FS@cN55BV7u8Nrdm0QV@1%MIvTLjIHMAOg&~?W zbKDrVr3si_l4vTIV(SLmDfR(IQ%4e($KchgJ*DI0@CqE+!``pJg1Q*=^Ko*ak_}^;*Ud3aQ{6t?z#^2KR1uO$u!zD^|^l zgWj2}SD;|qg;c_c*R|W-#_?&J77ro#>8Ff`zso`eR4&S_q9PV%`QV^jwzT|RQH65p z;GjaE&=xMZjw#`pQS=+fol`h5q(D_Al=avanL-hvIsZ5ktSGy!yI6tn9&fGFh2wB= z-XGEJ1RJA!NoVjkY2Ub0+Akj_*i{7A?@)B|i39^l8V@g&$xt5KY3#Rl#huzYyl0RReEwnZg!jw)JC{HOq9f>5E+L*nTn>Jh4KRR~-QX?ghzxDlvNwkX-q*UbS@oz3HpR}+Uu zekEq><;+TX^C$j&-tV^u)K!JH|M_x>`3#-el!Fdup(70sgaEz8aoQH+3Pj9&5|~}$ zt`Mv{sYQ`HO>d)u%nup06{5+L!}irzLNtU1`19dD{B?nvMxFZ`@_Tgjl(AvL$56Bx zt55`9wbD8&M;(o^BaXqCF(Bi#Cdn5B^6qHRJH>8Yb1`Lw(P?z8EtQkHnhD);Q8@|! z-@x$V4GSF%ix?Ea5eJT=raKn=aDz@HDK}rOk!}xT3@a(4<%O)1ksQvaQ0Vk{9p<(3 z_BAy^*gxT(EhtMTia2oM3QBV0z$KN0egn;I<(MwC!}iEhS`FiLOZXea8&E0?OR6wV zC~qR%qAU295MU-bYh}tM$0?#oxkl%H)09QRG@*t-@a9sW!Dt|+-Gr*&ft&MLL6KVD zzX8OZISWTTBxk@hUiS-%kWgzgL%R-`=j*^(UU_5KM`^cz0sXe$BIqhYzx7tAw--^9 zTf+Aw4QzPY8_}hV32!*_S5^nZWkgXpGa%1~9qRCqkEk%junI%(60{!uZB0yG7GN#z8|T7v3{E)u_rICV(Gf=_oD(WMHln! zYVxP>jhn^vdlDK`>tGJ1*s@D!SISXi?Fa?qZdwbdRPUWoTBbBg7zn{2Nq7A-_KGiV3L@fP;Al%Gna=R_moIVJ<5e+IkpwAU0MEZ&~Ag};+> zRmy-y1~7QrZb|)cS@IHbdZ=ivX}~Q(S*40lK$53{^04=WAI3k^$x}4aIDc>Vd+$iJ zEtQ+B3SHlNvzdqNVo6IO-|v9Aeasw9nRlhoWtJuA3SSpw16i;Yf~^VI%6(w_XMBG@ zVqnBQ-e3e&HtKQVNJ=8qpP)X|67Xj@O<_zDwL_x`^O|dwBGp>d+@JXAvQ^3jjr205 zBxpGov=S*ylAx7bP^F=%rGQqgohyH}o8Q3L2LhF<&twDmIe90921bKZ*zUYy3@PUT5`=O&nM3<7)IWv+|dM_mLtU6Mk2LiZYux?l+qe?pcmG zhVjx0KI4`od5D#WHziskJPz8gJM_FuygQ?&JZ+BXv;lcSQCsqWfvFFGB)gl2*4ivP14SE%62&aDRV0%X_dEXMK&UydVz9T6y$A@OCa(= zTTqAYAA$;Ld&^!yjkkmE1Np$ zG?n)6gp}r;h1I-rV3kAb1ek0>p-cCv#&Fmf+T8*3XIa0ic=?k}H?{2J0&aW8b?B`V zV2Hyc$2V&$nmtP4lW3ZU6r8e)q6Yp(c1fk%=Oz7~30rvWvJVZ%>0Af-tw?#w)z1&D zmFzW%8i_rrzz1u}mKkZ>K*HYtz(gqdjC0!KGxL-Lzl;Y%9pFu;py?tdz>YUfTNsez zR=a7fk<3x82!qA6urSlo5!s+NjrX2WB-Q=s{~ln4{yhUU-;yTCry$VegH$ zdR)c!OvlY^Pfcn!%9v)vXy;4a`-Sg+;PT?mXXiVCMP(@>^DiUTUN)W?lM zBDxhN&VcXOzUKC-X`CP2E4Go@rRQrw+TL!lbglel)9X-rTU8Ac+*&f@Eyd-Lj8RYX zBAskJa$=w`GRBjgs&KImW1;0hfRGV+(*R(EQyI3cg+@Ao2zH`k{ORjYw8FLa_+JBb ze+mCMBG{B#89Xu(5Zxdq!8Lx{+2uq)Pk{oYvgFIt!%>AJo;ADevUlOO>@$d?hXl4MT;X$ z7dTfT7E#{FDyEsVcJQN~#hpYYkkWQ|vne?GjdB@QwFHy~*vWafX@UtLtX+B@t9^Vv z6r-fTeG-hbW3Izpkvks8D4N+#v6~@#vmL>vxKsL>MhfTYZrng14MK-G60@7eXV)L} zUZd&6NosEjBr=M}_dMkB^oWfXrE70CvDQm{!#VoedibgrICn-A)LH1`6P(D?OFsqv z{9rl5CMo0)vamDfqf1G*f;Ae46CcIoUf2q9NC4UQ_vaX7$BZu~#<^qJGOMJZUHE}h zV!`j>IPlofbl3U~Gm{<&0pXPblbKNu1m(X{?Wixr^|K}DTH4TL ztR*xwjkt(;n5l3}$h&mrB}$<#6q^w7AWXC#v6C=osL@%+0hpk0|3~`umBlAu6ejBR zLx+y1!}f#UiyzrJ#H7U>QzO>`D;1P~BN>kV5!@TM@&60l=@@;1zEJ#+O1T#I|9f73 zjQ{a1;{OcVC(Z6C;R2|N6EQ#rDnTO2bR7FnWdNekyIK-Os$<5$6z(^;o$z{35_qU` z2!L<~7a><=0>!iL35iRh$$f5eV)+;UaL22I_^@K%Ij=Z6@lE)30d7cM5Fa50{0;mi zMo31z9|kg{@NFC7ZOXe*O6>BzDn11%dA{RWQlz;xO5cd!aX_!)C=93x7I_RXpq*WUoV%+lf1-vu zCLPv+CP4E*WM)!yN)*BDNI^;yHE*h-MH~w&#N-mhM{kL1Q2I!t;M5)A;u$qexnVh? z;a%5R+=3NB%8m-Mw-4J6l6H zTq&{Ud`}nGJ6vgZCPJx9**P;wExh6f@yh}^p=wtJ(a)avch_*qkJTZfLQk6ny=x2xjqd2f zOOlIx;Lz^1I7~4vqDPU`15C~Y2i25)FJ*!fY*pJ9a%Z zOLO3$U*2tAD~cpdcU=)4JwoJ5JZTrGnPPWzkrCclMM?st7P5%R%MOZG{rMq2TZdNT z8WyhU%-Lb0Xq`1IxQ$d_6s_}NJOb)gr-nMVPgx6FM*DL!tTrOcz&hBMPyEI4b- zjE$lVdZDagFjwekr7+d)G>a5OE&1sRgI+ZI=Rz~%O>tX`oeK*-!VqKKR;STbn|r)m z+bbV;cfD2+zs%fvEu})W5>=>1J`V(nrSP$UpyFq}0&4qZ1@*I#8Dhgc?!4Y+j2D~6 zc#%2Ai%ny^NRE+pu5eEw+jyymQ%Q+1x;w?U=VzGET~GXdb)%tI4+O+%Dn$(G>bWXK z5nn|LnRkIh4~o&92sf8rH~JLn^b!GRsTFQSYfeBx_cEF}ps3p`ig3+=RN+!F=A>&y zQUj6#F$!9Rq3#hG-hvYki0p{Y1u`ORftd2_4sQZ_pBQLg$+ggPQDj-6*XdC5RE*kx z(m6c^nc7{A4?x%+2R;SYzwv{Qis7+oEWHBU0>>MB95{iM@Li`hdXqmuwe&lihX`9?m#)JWVAUM$-zXCO zC!zFj?|OsQR!H5^&~L@7GT9o#y7<)9=AosmIp#pUHHNw6AormNhPh|$a2r6G`ofc8 zJ96p=Esvm2jWG_zemGF8HH{_sP5 zcJalvvQ}4r>odky$sb$MIygAM0mf(}nwzz0a@N0$A#Z}V-!|;V;Pue9b4FeLrlYPr zaMZPLI_l~JM}7EBM_qg1sO#T!)Q1lo_498!>iXtUGoL)Bmv@s%?kxS#!-8GGFM-YD zQ4l5>XDYWuwx7i*`2dJbnoO`0-25~0NE%Tle-tZ`I;_lfVEiXmAPXjsW(cPG)kar& z!02jUZFJQKjPCHOjjr~9(bd1&=nfw+y60bQbX)VJzQ3gVxHa=utC=agh4&ZU=++(Y z*%9&~j(FnJeK_T2x@XgsA8eOR#be#|)N+01% z^JaT<<&o`MDZZdMQ;qQwddg`lDJ!cL(QiF5PF#jdp*~_OQ}El+h-!gbQ^U)pQJPZa zD^IEN<)>8r%2TR-`6<=D@|0>{eoBX5c}j<0eoFPPJf-?)pAu>(+PAB~8LvdPS%0b@ z2yrdM>o%~|O=^fVg{W>}XfwM9Mx8Sae?6 zHB~XDndxps8!3t;bK@jbf!ZZZ2oHYt!KLqWLZ% z()@4!WltNT-~eI`%g^mK#H_ZR&QXW;KS*PKO@t;V^as zjzGovrc}M3YrhCn*i%kZ6J0CG3#9VmjdH9q|?qIK8ag;v=OIZ}4rhx6W&nz$D#Lsi~ z-MQ^eey;zb)#X<6AgBhy9qDj#$nohALJ%xAr^fIU31JCj7{nZmsV66#+44JfrdGuO z%*663%e{yl+3^*pz;Mk$_^=t(@KU{&Jf3ukR&SYELI4Fbu5@j>*LQ5=- zaaS->C+A3=a*PA@%FZ;JYHUS!82)Y(v^2M}hU%_lwz`8HJ059JAVFr0-HB{)%(ltu z;z-=!2uIX5-)+Qr86xshgWn53Nc3meF*V;J9~ zAHwAMi@~s|Dj&ikqKA3=Gfv$Ot&)nMehHll!@g*QCOB2Jp`zh+LHP}NkRTNq?wPV7 zIWVKOg?nT3fMWimqOlbvPTi_0S_9LWLo^b)_iRH_4%yZU0GO7)4#vs`k;w+W5alpA zrTSBfL6F6F#wgg4QqmO>~S%4jv z(IPsFNaG-7!f|BM#AUzvvlnipa`rlzsp&USyr8UJfGKY(+d+H!^=!5g172ujJy{-u z8%0DfK42FfTZt}w#0_%F1NQ)bQ-}$7(-5K0A6wdH>O0BFqu447qiyngs)B+}tVadK zXOREGD|a@tFVI>&c!(6ZLH;XM4i8QFuTrWWKKlQD1Nm?GrhR&9A2+()_8=hz#y%!w z!6A|f-^ttlDnW5dl~pEf9%ijMAsu!VYUwM_GJ`=32W1BL9Z216OK6G zUp~=-8znmU`;$T<%2B*hF;NmlJc{!jIobaUEN7ejFH`=0Dg96Vc|EEBdEEcsVE>E! zvGMJ%iqpIyMbN#uKXGR%`W~Vnj4PR&GFqqsVCzrF`&I>Aj1>8iZa_p|(?9+eMF=Dx zFf;DN8nbp`yfjRiLK%tPzkjc?O(()3(q&~G+SroRDfq0?)*5y>6AG}i6>Ga?tO$Om zg&&}eeQaZ2W>iS+X(pOZkG!bY%!|IL*V_<*(a?(-VHl$jlbhm0Vw$&PWIv45D1+3= z>~HauVQLAmLYeD!hK>HBpI;s8bo*9C7)D@{p4|FWqbeeYHaAqp3N>c3=)r}8lVJksel@*kzB`~(A#P4Zu*o_PO*JK>}J_Z8*8 z0@D_yC{2ievNWObxrDDGfoY@P(e%cuEQ&<{)HHMM#QuZ}AZ%YM2|2*PcpU^QZ~PHh zkBm0zfK0i7yV?T7TF)@b?>KNBO22?LWi+{iREg6;lOnU?pRAp)fVp z(G;Rk(r~y&N_CqrvW3EMeId>(E|K3IGy(wFLP2juY2WRT;J8ZEMyzhcM|c%FUCh$E zBcY2?H^8*uB0TJLU!S&ZXhl>7PQ#Fx{Ic`jw*K^|RA7aWKCc*{KSBY9l%Zf{GStIo zzyW;aFCE!9u59Ym`-9%wPD>DC+T4qlh5}hTLdAACP>rOZB8xUYKVmXwy>a1_vwi>p zV1OLjE_i@C0myoou5%r@`}h*Pgp&wGB)li!sM&OdQxcfF4<>;B+4^qFZhOm?@o_1+ ze_O~zl;=INl_&$v+{3^jl(|xSrUapK92N@Q)N0fc2PFY%m^c(I3P}je!xHw<)S&)O ziqDc`5tvb|K8~BjC**N*EXW3^piq!;9I;+eulVdq?7}7)rLH>H`i*AKwJ25x%`MTZ z4c~0|rlBGQCO!ZIG&Tq;yCsvAHGdk~eW1x5WdVB@=2KvAC+M9ey z``>b@l(7GOeE<6v`oHt@PAeh)iNb{RcfO#+0VJEP7v8UH)JBE*=D%?LU8jLDn?WH9 ziA@E!brvlBAy=MTJMzJ3Kjkm^XIH<8Lp#*a5u7Nu*2KGnBcy0uc$d;q7VB!3pD#uI z%EtPX>g3{{^~d+DKauszn*q><*JYdB-!XO~BkrYU9PHe%&Pg*<;T)IK|2`%R{OBQ zmN)jW6Z=>kOM% zzsmK`ym0K!X3oO(*HPBBzy6*+J4BA$YgX$`v#+w=`O=r=Qr;c1K9l;qjp1tPSzQa+ zI1$yP7MCK-I}4Z(ri|V%-9Den_@i+wVrO>y*jCsCiojIwlpLOCikcw2xoJ6nHT1+A z04#Cp@v>MA4-3><Ak)__BTtVNK+tw2X7qR&Hy z#NW39lyG&1+ZL?V<$*qDF!*&Vu;c#`WDcN540`cToznAw&(2Rr9W<9f=of#G|MQ=c*1XPZQb~8}>^n`!}#lf#z05yAOgcSr7 zBqxft>G6c;CMXVm)qr9wM0;T_*NRL5G!gz{67GukL(OZO&5sQ}oUA;h%D2|Wi z?7?L69&SSAj5MYs=CdO!+rWWhK@`w-N3RQm=^g)fyE!VFKt^bTXhLQ;BG*)EZ?dy^ zZ)q?Yo!!4TDSMlUXB8YJvikX#_@cZ*A9?A&ZM03j_xhtwuS*`>+87l?^vWnA1&CIo z2S!;Uu2}LIT^JA4duIN?h{O`DM#zz4#VR}USiaN3N2Hl)2_S_Vc5Dj?NHduV4j=9O^(9GNRUz~^06th_HxM4*o4o%e7Y`R@d%O-pG-cz%Y9ZPh3>%!5 zV{CnN^%T@dfp!o&Hh;D8t2n^U{K)Sy*U@YG^+@YA<_?e|8$|EsE00hqHg|aYkt>QJASc6 zsj}jpPDM`ksJghAT&$;p2}|ew3$DEr#a6*QP#(rab!ngD*AaAVgONS!Ns;U53XS&R8lvF(MwSgdEW6eg|{ z(jzPXci{hl6D+@={9mdj^8eNAhmZXK8}R=@V~~vgA_`M-|Kc6nMcZ_885y{tKU))< z=?ev}ItB#WSh$h@T_Q!P{?S63)2k+OjMAVU`N!AT|F8^bN<%f2-9>!v4GZ82{;8 z?En6t*K7}y&%aT5f(!JSI8rbFdA@%#sr+WAKtX<*%qUBQym$&dpk%X@LrM2s!k;wn zlOe^E+7DOmk~8-)bZW|Kk;Brxb;p>A)RDoc2v`IflDkB>wxH7u88oAhN-RsY7y5HQ zSYE+(e&t(t=y61Ht4I&s?4M(~v+ybHsNS&dt3sV?o+=7Y@`qjNt&~W>9#5c6o{Upx zObOUAz2s5`S4?K5CP2<OF(vkeOTJVw_bD@Fv>{;9WW;!FXEMp3O&ZX|oox?9 zC`;PSb`~I=J3t?56;+Z!7!xgxuf9YU3aGOYrGwk2WK)L8$>a}+O~I zpEz|sJqmjtv6Un-?X|G#fx|JOMkr8oep;)L{vX_2i};4DJY z*b$**JOd>neDd4k)O6>0 zs>%6`-H8#a8u4R`wyzL3(ziXmNnPwB(C7>PDg!T*TzMr>og5+emuO2L#vaM0#tRpE zA7#vxbh+4%Z}gnI$Z)=6uZ2P@8++u-L~Rxd(WJf)b1;5?p&%Wddrt&9sC!1rPF7v~ zoe%d+eA3^qERep&{$k}o-_SHRiB*eM23*nNreCV4iQi%I{*N@O{U3SJe>dfA0Y9Gk zp_@Nu*69Z0STIu2z%>x7M~T9>XawVF&$XVZSf|sCmX{2;$z$MFE)%c%BW#Q?*-|ThkFP5v25FDa%=#&o*Ews8W@7Zac=Ra z{JsO{W)tf9WtNtG51SPo8k0+r%s1`KnLAw-3QterpQi>RQ59t^+h1mGVao?W&|#xw zP)P+Km_a7b=e9evS!14lJw4e#R>1GAF z)lQAr3K^Ro=No$#H&)TsM!hGnnNr_qlP0!xqY`l@K-#rLG(r>l4#&dWr6^a~xRMx^ zsSjCOb*NY8=J;@U#Z{1_F`O*MN~ytKrFilv{C^qw-@CBGx%0*3|H@(cFcJUl@bFRo z|N8QO=hz;eH4^f_Do)7%ods0$E>LbBt}hlKjh#6Q=aOV?t8oc`XQ_of1(|XyP%jQb z3B0)*oxexYGgGe4@Pzn0geLG!9GEs+*6Xp|;4JViF!WGC>sODgbWN}}!WIf$_a?O| zEi_VyFIy5q*AT(@WWXTmQE*|?o(d{Oz2b(|qD*NmEvzh*WP>5;f zmOh#&NGxLeOXm(VS8y6V5*x{N3PlExbX^d;CY<)g^#2k&q~G{(+H17z({|UYlxjy& zyHQw%5Kb@a-QU__EZLERqw&Ufum)pFIb$l#L=WBbGke(Xwrpr&C}Y-{9f)y$S|5XU z^DXyLN$VrAi0A;~xf}b->aK}B9T|2fNDF}r*e6oew+#Ni2Y(fFw^5{IO{K{A(*U&r z0RCNOf2lT!k~jXF3Ljg={}L~9U*`NT*AAc8lKQ{L^Zy&1|D(q7X^Qu+D(3TliLQa( z6so|LBbZOuGwVg8LzS;rXXT6{-Cw(0zLt8H?n<^V4F6qe= z`yTYpEDwDi4=j#g7B~|!*>h%=GHY4+to8%WCdZ0%pyA^Q;W{uR}!1!lV4E!lIgfi5c90(~ZS1NHHzQbV9TWAI2 zHqMuRh|xc(anUl~-1yA~76Cp0_oD$>s;=;%2@%23ida??9Spe-dM+lc+%3iX5UleA zTEk#Un3_ZwbK*1jg}2%dU=@9gZb|P#meoJS||+VuW9#x~AA*!}iM`BI_EgocDF_2WCt zQ%Lru&TQ_7t6d^PS}z`mHVQ(#Lr3;XBY#faMbrgT44eRHXrCw%n>jEt`^<22VKl&l zt0EJIq9?hH2sxBI5wlJT@^FjTh6v{DvTC0-L}JIu_fm0s61JVi9a&Q#EyF=Wp;L)S z%`J6;?BmwKl>)J5yeL zffFV}5m)U?iNOgYUrZ05roGx-amvG&} zvHusf5*;10JHaQI3}q54QJLtSFPfUNKl8ey1^D$${anpzAkI_4>;0 z6;Slo06DiI1965h@>=jQ3OC#mF|G$%;&a&KxxezRWuQo^xDM4^0xeUmxH>hNllKEUnlS?#r;OB5KDq z?ZD_!)uVy>0VXI#gEZp0(Yk~9ba~~jRvuEoGZ>Ya2zNu5Yg!H>C4<4i_F5qpeG~$( zaUx%Rd)x>N2wEFk5`7j52NcdgM7xgJv*@tXP)_1~CM7|&MS%5CD{4a&c_=6`WQ<&$Q)3FFP2@lW zq4mvJL~;0wO^6Kol00Fe-HJ{8gHtW=UZxk`G0Eb-xj?%#7d*&kVMJHhK!UV(1c8v; zx{!<&->A_1{7!qDVd*@=WHhw#uFfUo1Sy<8T?#@NWx(SNLw8O=@@eS>pWv+ucxq5~ zJj-8;-HNd-xieaM^rjEh`xHm)Kp|^&F*`t4_k(U1L|7Ouleb0g?y`{n(<(n#_WFeRC;}Z1i{j*XynC%+cxuC(&A*P0c)eo+)0g?Jy3Z(MU5mz_;92HR?qN!NFDF&z~ zPon4@g~GFEoOePJWCRCL%8nR|Nhd9=Vl9LlUB%|5cn9zmm!C{6>-U!lvD3Eo;YB8W zTB)?+OJ&Oq5SJOv0>8qBI*DO{*O~8sqyra?elD34e#{V!m*x?5TgI~irbb^IG&2iPm5Ga@ULVG#Td$Cc*6x7 zCnc~fQ}wxh+4TO_uePPSB@%la_#i>Q572xS8E2 zS^e=!MJ>cWTWUOT)a_+&?u6zne)FgF zo2U|nrq?efEUq4?d|YJe9ZDOXNS0u|V&vMP-#g&EI!AGUwQw6wAY@6tuXA_bXQnP# z8q;^g8q=B9vp8ybo<)Ni@IjOaB4(zSiEx4{B21x>hHnj<8R+DFJa>YRoLwRxt2^s( z&35r1kj$5>yI6%?#=9^W6^&=(KM(l^U-bPPzUuqd?v_Ybn;}AdM9)@iNoYf4(BR}t zq0D7x_RoN@^dsax$l^`E4XVV(kH@hWPbq(t=fbVxzdPeE=>J(M)vJ|+|L0-(@%``X zzyF;#nyLPuQF&Sh$Oe%I?pc0N2N87VDF~UtQ?!Ji`@x-_FUX&+;9j(FU49=@rkcb8 zF5J}(zNV3}A~uAwifonD$@rXv*Q*}Xsvgx@wX8Z8-^m+M+*~atU+da)gzdjKC7rjxH$jiDzMq|Sr5b; z%A2lcFhuwe7jwL7dQ)bO=Csr}`8{NLXRmx4JKEv5iuB(be2vIvClXQfQ*~B6>n7q6->MT--XGz*}9xuz*vO_Q5gR zLj2RMTs?a}+Z2LY#wz-8+oVvQFTO-c3dMSo+LHz(rY?to6T^Lz$OOY97ZycrAi!&9 zM)qk_PBX~5By{WL9t<<0bE>j~5m^-ub}jwPjiN;hu580(fwE`TNc>BU3zAQ!D3VvY z^ZZ)TJL!dZmASYB8ou@dpM83{S-1*)uYIe9HP?{9dp1y&hL3Z*zSL@CbIK$Q5Yh!e6`Vp<8r z3ckB5t6P6oHFXSQ;!IJVNkrQtHAz`){mBc5b|vnE$V-;62k-q*kw5u>O+a#E zIN`)5jL%9B|~v9@T?!$!cPVMZ?br@ydM=iD^WJtpbK+93eG$xRT4?E6yMJ{a*MH599B)7H=6sh{6cU#QWDDMjff`(AV&oyFY9 zf2p7LQ`*?I{$lNyqa}`|gUQNA8yD;UAX#apX5b-tX(jBvGt(|MTynlyf?6@7hoz|9 z3LV=gsr~XO%AEfvC9Ay$Rcx28HjaqRX=|0jbL+&lTlGN+Y|XZfTj28QE~IRS8?U`s z)8=bjKL)hL&CiF4&!rPS)&Q3%r~B)R^BWTXq)TQ%dfA0 zh{m+NX5r<3s@g!DkoQ#xy!^(O{KNpay}oZnZ%)pZ%BQ*wdu4-eLf=VGByozJw4K^3 zw*6uRⅇ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%0DL{nM5VO>{&RqBiZ$`bb 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

    5>#a~|;G$CUMSVKb`r9B3+Uw|=4 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 z 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

    Ylf6W@W5%& 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 zŲ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⋙`%JM+E;K@_v+1V3TDHl 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

    3!P2h#xTp=*~ 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℞vo`eo2t>LsAHX<);)I5!5EInArpkaM2tE!hma%YH4RF zf|syK#M{RChHp~3WUVFDugSD$WIW;xR>N=8d>j&kS4UKWo?aP!(QpWia0iui22*{U zyo$GQbzVHU04cGpGmjNdDz7{$na8boVudgH&mSqr$^U3mT~?ItXz;GmdwyzSLt(1x zw<>6qxQRW^a)pDe+74Gu16B9JReOP|2jQxtK-J@L)y%0%+CMa`{UlSz1t78(rAt*k zNL*1|&60xl9UOTHNWx(}UA+qltb49O{7P8mW`WS!0_4`GlF6_HbheGdJu?O?Z|1|AKa`%x*d>Na7+{Z zuwGrH2&7vKd3JJWwh7m1pi_F$s%Sadao*fI-Rc!pz@*k$693{~jxaiMpeXl}**TD^ zt329qHxg~E*1eCPs$D z?HmLQ$uCG6$0yG2ZfG+kg4a-DN#;7BGw2D(%?Lzu1CY9*vmeM1F(fcv8jP?p$-y2E zB*_>$TNNTimd8k5StWNnOxQ$2YTA!rt}OD6)75o95UgEoZrKRoWXj*kzIF*v8+f$V&K7s&Uu z4m#~U^j^Up11xF1eh!b@C!ubRTF39}^>T37>ApGqaIlX(%&&jkdVlzRYu_91;Bde7 zJq)NFS^eV9$%iARQ;K7Bc8|Ynb;J%kff1cP{T#fi!v#W(X#?i}s6eRN?su_Bs5L{O z)(wH^O`KuA^z41?A(6XI)^qlazdZI6^H}WP-{( z{Czb_ob_F*xMVX5bg#Dj_FfRn%6JctB--Q(YGzgN;-u>RGXda3AyXY3yyHp8j$8Zf zKp@IO?IVyoiC4R@3d`Yz}m{L}F?5bZ-@{i2P6Fv?lcVJ;d6=`462!}LM3v(zvJGL67uSB}Dh7iNOm7OtA1mpZ&iX;g zK5H#0m>>(InDgqsTBwJ^zt7b*jub3q=@|GaT9$f@DX`_~4Je%*i;kkRLmdqy z*$t&xl)5JT+@;Sl)f2{f?VPmV()%z&Fv(21M(NB$zNaScssUi*erh2V?T*kw$xR&N z`zzZtDHVysK2wY_aO)eyqhA7?F?m*=8jgQ*t26WCKI$ zmLiEA>PTL2r8#)hDduM4_eL9dK^@R0S0|vEpD-1`b+Md>A`u)KiKzVm_jET^q2pKS zW-N4yM7o(b3IjrCl&}d81~;BPfiafaytHGOR4m{Ot7xl>db*lh#9HLe=j^l_k2xfO zF|$35b2Y{VMxF>Vs`ucu8(s>WNDY7q`GscjRlqOQhXj7@tAJqz*h)C!!oTv(F zoC&}mjAt25p2Y^!<1b&*)S(ldD4xgwmhO`JoRYC^REF!km1i@cPN>5_3m(n%9d4MR z26k!$Q&$3OFjvqYGU)p(aB8_|D!U1hS>W9%IV93+Cu3HsOep z)KSJdBFSCCq71GkaUap}?+8~I0YHuox%Y}N_CT98X(+oZh;{XNwLpy$bz9AsYD`83 zXd%=T|3;s-?*N-xai;obz0QLM|L%I#AMNh5XT64D$FZJ7^SY%kMX8UoR1ZztO-0m- z+r+7e_9!nSo*kVz1av%2)So)p1VlL0j~i(WWKk%#CO)O~KH|>fzL(5FxoC z)%Ed|h9KyJfCvaaX!GB*e7bJaUBJZV!IvO60Kje=F8U9{Vc~e!3!$%yKqKXt3y5M9 zs8I8nK?Jo3Ho64yU!&2n*;1{(I4qBzmQ`hS5S>(RRaXgx#ML0f`5Yx+mcT|nO(qsL zG!49_f4NOgcWzsj)ZKJ_C(NV`z!V+5IuEv0^<*>~%0&Zp3hX-lYBAmPI%FPjk_^C9 z@hppp&`~?C(Lh+2uWy4-5Eq0wOunNC0|uIoCsKinaL5R9yUIZqrw7 zwo^jVVJ*7}l3L%z6u@Dj4iyImW@_6Ve6wN{xbaoPKzU#s0D5&aV2MESn*xY%Y;}86I)d5mJ$SiV&L>Fkhvk{`e}2FXMC&pQeL! zdV|huki~fL7cDWR%ydkD$SR(d_TTSNPBTT<>bva>ypC@JNSl{BG0?P(HjA&c*8A53+Yeq zi9P+-QJfl4&6y&OKwzV3rcBdJY8ob*6$IYLzd-7tZKB6#Ixn!9E9RdlwTPL=Xg7-* zSSQgFsA1F<2Fg zSkr*StIX8(2Xfw{@Rj5^hoLEDwpWgLSyXN$*ApA}(hY4Hs?J5L4$o?f7w1|v-$@|? z3UafMnU8Pi(v+JVWNw|5w4pKloXU$2fjGK-5bE6KXkEP9<0qX^my~dEUU!1Z4H)7` z8Dc5LHyiyj8efgfOXg~MLxvAr)79h|JGcXX+@Mur-ar5c3;E>@~ACs{naG0D+6xh zD!xI<>aMNX)WS0p@qn!FHJ;Q&9a=PV=afa8vc>9Ekg(RG+H5?VqLNDIPa)fEOm2wz zSy1U7L82x(e`bgVNfY9xjgVL3VRJ!3vdy}Bw$Ywy1fo$mYTP;Qs{}O@On3rLMyJSN z8Q_T4Sl;=dQ7iB9t%NjMq#BmdXx?ri5lvPiqRFo7`scYXQsfXCC6AIT+sUeAJ0QMc z>5kuzBk-9g&B;ooImcEelU+Nujx3XL?e@y4^fF<^(Imb2SImZEcPW}D5n?&cBwc!g2bcF3R2_G~( zN4_J%dMKGVJ)x$J@D>vNut!fI^=fI?>XW!wj)W$=_8)m&(|PF^7T!#ZD?h(#jwKh2 z^s?wA7ZB#mGb@FvqGBL}wyEJ+c{=VZnmONbHi{BlxZl{5%6>BGiNIyX$~nf&lMr0N zvj*F8qXq$O6`;;FvpJZO^M;b;nI_PKqKh`Ls-MruPWh{Xi;GKlIm;{&bNPMl7;CZ$ zcSSg_cqo(R!70tMWN{jch@?0l)1!{3U6J6KUTLf~_kE7&&G@YEFLFq&OH@goXKHjX zs-x+lu~^R0TnW5kg>+$re-+G7o9V8%_6`pY6f4o{XuEIhOHa5rNMbN_vW8&ut_Rzx zkx`qe1U8R0u{PPf4bou>$t{62y8`n(udiR2)#}xZOILYgMkA zqsOM0GtJ=75BuTpOM#Jp2a9sFdRTx1whd7K4^qJZxWx1 zms)xh0Fgai{4#?hrs9jnY-=Wx-D@hOWRD7Wx=n%PbO?H_c}`!y=^$%H(yBN*Ob9WBuvnS`d8!uk440*zxtO|cJl3d^GV zs`d+6!WG!DCLa*M`3k)bt8jMetFDkDhKL?px%5)5U=M79a|-j+fIU{%#cfwO2^yuK z58!ZDx*p^`WdEO{I@hhL(ZnTWz3J1qez04F*h@fV0;UjD&B{L+pp`LF7{XM`EH(}n zZbTIKHgrW*=Y*EI!0)-5ciKnHuH`6q9xmn$Je~&LsV=P+LM3lhH#} zVsg9druu+dBAG^G$F)g{bKu%#CH1F2VNuk?_#~gvLRwpAr+GdzHni3{x~)DZj(xX4 zxF|`d+~>(;ycee+nHlxV zkY$#f_4t{0Y(3cIK5Q0m^Of)DLToQhR}{H5^-ZFR^os}RIe zJhp_JEcGD^;f|k=6@G;0SA_V+L)WuZNEvUr%s!n%q}pse$x^MW4&?4DdWQsD0VI`i z8%hbzKd=PIl}EHX4>=th13brNf(Y97!O`*IcZ#G1mRiehL>0FVjtWLk7{69-EkXcJ zxn?u>y6~YzsgS4s77vQv>>4omwNBcM*bC>c)ZF5?d!1>V@rt%$tye=R8q% z`5!+JTo5gzYVyCswS3S(>^qtbF#C1U@MQZzO~+7{7XWS+-8gn=-f4$V6VLyQ6FNDo z5uNBS8e~3`k4&2o0Y-3>h+hYZ7=xT7o?ggU{wwP4EBpiLwV!Q${S@7?r0`U==|Y>X zX#=NxeAIo{?zE0~-vRI5#1AK}x4pynM~4TkgU*QzpPzk1RI6H>aJM>L z2S((#zCEgn>(K)fU9~17{sbBmj5S1ks!z5YCJ~Gx{>%*%u3Bus95FDsP9SANT0#s~ zj%KNuR}5%50kllfED`$|IjAXzXqW4{xF`T^OddUQVp6Imt3g>QCa0yV3A?cq;goTk zLjouXe&!O@0zz|rbg{jI_Xv7sl$!A(B>dU7HjZ*OLB{828{AFSf?!iN$THR)PJS-j zUs(kt1k|U7Q5N~R7PXUsdgSd6VH_xnc0dbAo+1#p8#yEHv?=GeT~yZSIVu>%SoISl z#=+pAg{gZ1vvhI?SO|dN%fF~UvWbX`Pck(q=P%^~7PC4I556RWTQ5z=<0!vSRHNf| z>?|$}`kxr?*H&O+t2UV$(@!5100;3uZt>_WJqNPW0pUPWxIi{Ky|>N-@bPx(>Efe3~9p-Ppu@AqW*rPl&VH4QKh3`Dri*h(VA#5);%N zJu=O$YU``;440RqaB?Wan(}4FGu8UI6IDAA1N-~YDCC_o1B&Y#C=CR%3)Konv}=?n zy9y3!S6elxtrZ&7_bw=b4;H}IKJLPKd+9d31GXnz%>NM>f=J5n;o8$JTS}uOMTgkq zVD^qMN_NjQaHx%v^asV^-197XAf3|;3|C0!^T64omk;JXdSb&~iYv18z(!ih5g$Fw zCs$3q&ElJ5o)_sP4S{R$*Jx=edKoQ^#?ex=8m?>>Twd8fiMcgP`jioP4Yg*U)7>eG zR2GuSdS#XD5IZ9b`Q;($_IdJ~<3x-b$@k{U(-)FD0%4{JPBeZr=a zt|U3q2Bu)vBp&>+&0-}lL5o&`ESD%LY%UQYH`a0`uP@{O=r~!1dY}k$$Z^_Z4abNp za(_jRK?+t8za?3=%?^yy=qybJeX6NX zI$^wl$gF!3P>`0MJ7H(U?Fq|Vt*BMXY^(ssk--Lf6-MC?U{NpY)n|eJ=aAH@yEnB_ z*BvRXeIMKZI8a+^BI#(H%wO#FQ4rGAU%Tcv(x5UOvSezntJ+JXugrT{IzjerM?;ex z92#Q1X$*bszui0P61QxF$f^lq4ay*}%p+s=02UcGy?h!OB%sbk`ndA-aaD8q=Pr>O ztm_7$RUV7+gG+3^HUYvuoET~LZI)<|()=ars8&oaPsS{q|(eBB~4~NJ5wO42| z*s#F3xHKn?gf7+zYi#X?w-amp%eIr+Mxh{1;>GFK6wR?_WYMDHL|xwWx{#ZOKKnUc z+_~p-^0%1l7WinhxG0RdVUKJ%csH9^i3ZmO^4;Vb5`?DHuG0jo@}ks{!VzMo6K@#* zY6M3Jzr;0q_n~r5@A46^(ROCDNi!*(Gvrjga+xPoV(4_=NATWt~P6=biy=?1UEO66tI z*Ew3Xi8g4hB9ey2G>-R$Ek;6(3yp${LeByHJ|37j%9x=IzO9&n!DhjvKq-9GYFL*Q z!BE4tM>}7c<;HZyM~6gIR55eMUxWoF`N^4jd{Cj zR5z1ma4*fN+nw|QFT9yDrO!kyMyzvHmXX7PEX8q+e}$O9jSHS%LEBd)HA89=>!(egnIwn;zw_SDrFM_-$wS+y!% z()MrKmKysqjeK)zAAZ(f3AvmfpF8B4;GMWm^$oOhfzr#nqSQTOS<(%DB@&QUvlE09 z^E!M6ixSBX_{N0a8Fi4uwA?+|r6l4C(gI+J#l?`OY9-{9L{U}soD8<%XS>){u$pBe z#moMGyqEpM!QT%Ke>hkIi2Bo)|Nhhe%Wv{QI+|UtKi&A|$>a0Mb@DjRdXGoR^zpPe zd3-UQPV$Z3-NsOGw)x`4Gx(`s?uq;f|2=uJ`TS2?TTiy0KHqxw;_350ZEn5TeD>l` z(dO^50W&ZfjiNtYOybIV^UD8C|9i|qDJM5sdVZm3jlI=K9nRN^$Wxdu8APu$u+aQ= zcnUxNc9x73#7vVRcu#N4etESao%ko&_&kfjN-sjvHy@u(uM}~(9o>v)n70fp;o%8A zok0XlP>c2-qgh}y9QVO^Qhl3^`W%B264Dvx{qGJwa3b_UbTm6vm@;~o_7Y5xjG;&- z@G-w2SuwOglKh0{5WN|zj!{Ow9VIEoc?C`k1Qbtr4CsaXY=RaUA^{BIsjXSITDz!z z#GYgq<4Ho%=hc#QloZ7)p3Mg6TLRtu(C)lF{LqPZ4}OY%*gZbpJ?Q+jjqYd=juYMc z(Nkqea#OX8EFMj7*vv-?2YO?#f{jS~l2`2oe-8lf$;WvlJ(Qb6Kd)#U7eR#Kf z936c)J~}*UZA8&YlIZQE*D|=HK$Q(ayT~>adf`8*bIdQ0nn8W1acqs9Kn3MzML`b6 ziuU0s4R#~9F}&M&+KjHUbeiZu1Xd>HPptRDRFTUeVb4F>bc#xcqP6eA_cqhz~`yj&f-_Vw1Ll@Zvk68;JgTm7%&+U zv^i_Eg!5Z!8hM(og5R6OM;V6;jwcUh30N-d?k%AN>L;DTQ16HD=Qhg}DfMBLLgHD- z8!(<_y(DU*X7wyxIK3vmH(l-T6ZTqzzl~A08b4Ztf26TF^g>qs>X703Qe?m;)Nay( z$@CX=6J2#4HbgIP$A{xK`XeNgZdR?k{E_A%XeYa1qwooESbqsS8~Gef{Lgm-qko_|HLO>XNxANPQcmr~WtKfZcB- zsBphwgutuVLuyHujm9CQ*qfvw{FjbTXCd4Q_qjgGRd+M=wT~m>wU^Jqhy5dXlMwGa zZ^kYb`KZ$3hyFzRcCdos*rY%~4`$A-#!XniM?YA7@DAKz)kErzmCI(}G24acnfMz? zw$`E&Of4IFxD@`>t-)7$p#3Jb&Q@wg*ln|mM}-c`gKfRU;dq+VZ%aOei7K1kIFnRTeZoN~c<1A&72R87Eyd9nr+b^?kD zY$F9ZlG6e@*3@oN)+fw@Ya&ijH9^Z3gZsgj!1(s{_AbSm`r8p5`sK;Bf&h{la=*sK z1VSFLiS*Xylc(A(9Ee7mnwb7s$`S8_YY>JOYW($AxCE7MnKW-mw`tcl1*J6xWLT7`w}ly9px7*Q1z(F)+} zX2w*rd`Yu=h`f{nGcV)>wuFF*IdxMU#|uUzH$`MZ+wJ3icsB^w2pbcc=P9rr(1<5U zGh1X{VQD?wXT?H=UYELrIN;Pq;|N^&T|A7OET|{63mQy{LQ@RpR8`1rnC1!*op5@q zTz{I4XZeDpJ-`ur+G04iYWm)scQ7JEAUpvs%Qk}%d9Dt+@m``eI2;Opuo9J2QSIr$ zs_26*Rdpx&#VkjRw=+lt><~0ZB(x#aV)!9siWq|Rmcj2wFM2JeP%g;qd!Qo%2UErGirX2}#@AuwsMU!QkQ~)e0ZA7; z#_O4@nQLtm=8+GS#sv^Zgd(ySLpF{Xf}1`W%+B#2?^rO*2y7Ulc?FPmgVYC!-94MM zfJvHvmIZ^f&yC3zMPM~7j5SiKDQ)nukU{-nY~;u!is+AeCO~H(-E?yW@n#B~dxwpk zOH>|}4nrD#og5SMWMwF$9UC)KmYyE{*ot>KxzV+Dj8yIHWalMU!VqEQ z;8#X)mj+*RBd_6QaRaUeu(%U#O++s1SFmS(00DD_Ez;Ek%j@j~HXA~I2aGW^V0Owm zK}hwTjVD<6m)y(DiRgi&4jA;nx4aAigr5zZrt@Z$B~80bVr;M!#geW?32Rh1Yz9!Gp;Cf|iVs&Ztn5H=N%C z=62D~LiSXK)FIcbs|2+~aSyLN8d;()h5PZX{>P_^VS!+aI8ruNpua+J&;+=`P<(G8 z=Ba0#0m>5JXX?BZDaSvm|FdMWprhgBw86m#NN6!&F1g;S6(}tRPD) zK?b{vb?}!RK?H3J9);O_u?w`Jyakh4eqq<`kbf5@$`(goRn^J(%dVTMt-s;;&iH|FO+8+ z0|a86Za0;XlOqUsfieZrK`u-2QZvF!ino#RjAV(IysG9liUbwM{ira!%5YP*2r9Rg3%et<0l4}hfld_kX&Ch$3F+ENe)~upm`=gKGu_ z7tRnY%BUJZHwtwRY}#<*Ho;_>JOcdgC-`-%VyE1@JB!l3-6PsqYz&hx%#x)>j-e+yt=N9O8sl*tB9`JO6?p*(EsW!JC~a zBu??$D}a#nLS^B3vA6R+xt&X6TJ8c=qw(ix33M*1APdA7c=%5+c!7MyD?{+C(|@}0g9{^;QVNYS$2$}It}&zcKKK-B;$}u><8@X= zap=odwPecZUM3DO>(*m7( zo7#BjG>Jg>PuoFPnPm3f;+@qC**&=;PvR_AH#EsOwl0GQWoF70_7Eg9AshRI?MT88 zuYcDeeMJTMzOCh~F8$CY@(_Sto#t#roHC=m&^=%0?KW*DkmO|!^?q9-_!x-bZ`U;u z{LdOxFq6Aw)bUJ`%>#W#boqS4`A8d9}DPFFF7f;TSbzmy0lH7`7mpNJE2kA53cSmB6;pQgeJWs)pM&EptS&`a9lRZ^haG0|u zCjgr2)Ye1^{iS8jA!7=pCdw2ow@U^PP;C2&09-s>NCJRl5>>cMDgXyl6*B;YV6T`0 zz|Rwal|Iv>e2CW2`Dc5C@vv>82iI8J21bJKusQCRuJ_-0dSW@<-Wrb-%E{56A7> znB-%tTiNG;HT`Xj@2hM*iy{%p6=do9D+pTP8mBN5C~0z1mP?vk*o45Z0oyvT%g0YE zWd6VDMnKb7d(;Jbw$D&ukfZ|u@L!mQUp84!8V$1?Qc?@w0zS@4&jL7xT7CuKSfDB| zga5E?0y^o=J@|TefsRFXaE0v^TikY|1bW3U;O{R`2XFz|6jGoOW11# z@gmWkY8eZ-sibFJbgd5*A$kqDpXgXwyX;2+UUh>R67)ybE5#8-XnhliWrrX;+dbCG zf_%QpDQvf*S?)oXG*68N&@=0#%?>D|>gD;yOl$qwaCigM{KZ&7^@YMT>OmhuC<)GG zs}?O*Q1CK6%%)17Mm&gP>eN#>A)%!dl-wf@>)7y$ zX(KsD$kMk^`f5q;pkYH`FNaACveJP}Y)4zikmBr}6Tt#EaWoyrm=V~nT~92W$4Ae| z&@|3yf~0|N%x4@+$P=AHSDFa@Ax&fI2wJYydD}he;*hIGdKcjdoz z{0EEgK5P7kr_UA9SBU@c^|LSZ-ycZ-z4$`^eWCyUH`0G$kLP|mu0tkS~lu1D{LjSck?fP^K<{7!Bi5e4Y$Veq@%)9a|^f5j9|B z@vB1+$Uh*4+k-~8i)*I@kCyC{6`O7|pv>wH0qJL*VG`-UI%kjv(q(((Y)~%9A?vYi zX;F|P8((IOV5~-!#eVnbsp5?RdOs81m)tVArW*s-PJo16)w3wn5y3+^@2EQur1!xa zv?ro|s!zGkR@jQUP=%g^<&sE|5=?d}F)4t62LaSC7UW%ustFPno|#MH=V1Z$|$Oxj{H!Frc&ZvEq4TBmfkGQuT38cW8e$c%RR#^i+7fa)o0a>6dk&{U^aHHm5){24cn9l6aO+f;qBmRe+S ze{>nT`vtd80}UVCp}MF1x~bEk)IjXsRO2`}lPubmluu?S+6p?|Wbu_YAOzQ&k|!QL+bKPp`pw-!LeGjU0Z9Y!{Jtxu1!^xwE|GZvxe8Y7+R*^dPcR@Qq z>#^Ad0t{0EP$PESq#oB_O|QGZ&XQR|jZA_eT?}WDM9UG)w|jWR-Z1|otfg9=4+qDs z-MzQPBi-X3F=}6rJHHA@St>UX;oUp$8x=KOUgjhT>!)|1%l}YujkL8}+UNY~|eh5F+7l@+HKNlMo+~v8lFT7U z?)dU#=H>B1BWN%#^{=`;pe*w6lk5*>0y3}>{67E=y)9|Zab!OO~O5sD^b z^n022Vj-nj$;_Cihu9MC*BXg_5`rrXk-<|%k0FEY7k_T%TAkY=a}kM7jt*Z7?~RD? zqh99e1{wqUNW!8YZ3dXxj^XYkRTd(tsz_PSG`zys)}yHw zJ;<=Eu`WqfwKLVk)^Ui~(s*bmct~bkkK{54*~5etv=%B=%8(3&cqs^OGe3}NVfPT9 z@+{5Haln{09XKW^ku{qduvVvr&EiQkBN2^C3KR6%StQUX9;_(jJWU~-Ardi#H*1om zmt7ETHX0}DU$~&~37Q}e8D~ixt=LJwd8(^;{2O><7mdPYyj(|AC6u^u$cALR08TpA7hK9VDt!;wMkY!^n{Aa9+) z_-^uC0e^r(K$p>#!D53#v#NTTQ_tkV;rMq9Qz9{vo+DWTFMAc-(0J|=sD=K9`Q(V? zwI5;6a7R~krWvq27MmK_=~V}H>aCGaBI)XGBMqJ__5Ci@PoEE{n7D`N<$gaG0aFI9 zA`NL9@dOjC}dDUPh8w}Ak$$u$S1aYGTQ8#C1pGtCqgD}x3$j|fT*6S9jpUxCFsPgvo;v-Et%7G5$Pq$7wmdr$zra&T;roGBO@ zCO~`J05N(nGo5nrTCFHCHufs-v5C3LOQ1}=0zns8PrcQa_mrB-N_N>Or{i2LBU+hU z+~6-4S7`P2Fb}iO?wRYq>?f~5m%Ed%c4hR}`@VpV62uL0SawgGTd+Tdvq$xs0z9q` zLX&~I9{n5AMH{f3N1+SRf5qXj)*8LkrI*)Z-Xlvd7cPWk0eH27Tr%ni0gBk|`&eyh zoRddJU%;SV%5OVzE!z(Ck~W>y@*4aMEX#Mu?OLUoNgBe5SQQbT!<@0 zgmaFbLi|Y&p1JxaArP$s0O+{{0B93D2#}|)7wEMk_Mr14zBZcZ&Ly^4oM3c7N9=~T z2|LmMEy9kB(^vsG%oY^@$E8j`jIvul;68im%M3JwItbgg2#$U>TJ%X~5hH}|6Yy2o z$!^J+ie=Y(I>iiYJ($6$n*L)IIw!-wYAMIed&$aS-_DW4XZq}j+AtK=feHVk{aupx|Rgu?}!VMz;}8hQaj?J zV(@tL7*eiMmyZf_KtPpQu$30_M8?fwfXW0Qdcl%3DModJ#IqQ&%%d)7WE=3`qu*t$ zJzpFW-p!-Ec62(+U#c$~XQQGwf-1x0>W#*0MMA)DA{HOqCo0f?-RG`%Pj9-qU+~eC zRJxv)&6Nw02V@P?^79_lic9f9cKZ!IIXEZtyBqJW*vQP6gX;0zYn^h#JLVy{%!0fN zjSiTE3BLpKtlrOWHva$zV0}bw?d>V#1-{im_=Hh260aW~iP5H4QXPWzsB>|Qos?&^ zS~G56CEAr%qq>G?6l>kBis6R`*70f~kE^3BQ9IYqWd{@z%jAa<)&)QyO{JlhRR`3y z)$%l?&_P{WEyqx+G=yMK*W`P8S#KoQcGvUp;=oZBTGcn(RQ_Sv3-Q$#8@w5#M_hyo zh_NyM;n}L~UYU%i5DFEdG_ERQCyCL+07Ltq!Z!EUXBfDaRW%?d()$W#rAY!R7a(oM zLqo1%YfVCe5)YS`Tw|PBMY$*fDpw_}^~)h)It&4J>ev*77mAi^NUE!#LNQEF0geiM znAo88S8ilt-twm0OaNy_?((cK$On6W-8Ob}#|>+XGj-4bL0rM^s2Ii5D8W7_W@wb) z1+?$h1B2TE8F?4IGdaZhZE%IXgxTFLp^4FHmenlrtMp%q@SSDQdkd&(e0ByPJSy9z zGt{!6T6fHH7w-lO!~Jcy_}ijGK(wtTI43l&paAgxvw%u} zu`>}6JwAnAo``D>c}n*1#;{qr^W}^#Fkw9~3s@ES=r} zZMz2d@SM12YqT(%>KFc?y%&uy6B+NEn_VOUusKb7G2}(K0;>YmY?@*Y4+?^$8>%TP zs&6(#VTJmGGX-dEuMcU|3?_0GqntoB!YBDAinC0$*uWZ(Eq%X$;#FiZ3-IwG$a}#g zt5L%!OVlbc&uR$CMEFH+H0qEqjjEShMkQU=PdB~PjmEOM1NKwti(U3+dkOMB;mXV- zi;#3wOwvWs+pWc%&|!`Npw!0441h?DU`KPhR2wO(|LL zAplUd|M%9`v!^fo_&-mce*MM&`(GjdZ!L$`il8YA`?8uuzy1#M)1n( za+;#&MyF(NqtR@5`tOx(!d_}ET}rMeNpA{U<%3Y3oP$HUWjTZ;3D!V~isg&A73o0E zINej6N5h$-(qlAD0XoyhhtL}{3N9Kl0Hhnq2DI}!<@P!|HfLdpIZd&PK}>HLDgyjO zFG=?aE5)7F?Xn~{bhk+hn#0WhnPlTtD`F$qsOIm1oB5{eKx{KAOLADq;xV@xlaM-q z@SJ=e-j!R!zkPv#^%wEIo6B&EkPy(!cKi#u%?00vt~nRFasd3a{>q7X?sV&=Qs8TC zT{m)G28n<-Kz=o}6sFj_w!rMW;ZdSf_AmNV_`YSF`b z>*)Bfb9iug^5N*{@VH}Cl}7j$-vNh#u)GY7dH8Eie!pxyoBG-M3^Lm<#a&^^o$i$K z9#GflcJ4coAP5rb^Os@LVqlh6vdmvNd8x=5Hcw<1yiE2@s(Ht2-|Y%rvAOnZc6$Q% z-jxZOZ}sngm?ZOWKlEuZfF~l$8#1k{KZiiPkT0CI!7GT_p$3Otc90e3BX~y< z@>RcmuL9g*9JJ9OkD|W0_~qj{E6fc7UC*aCily0lv3?3PiCN7h$I7Cy2kK@Lg#+E# zbUitHlu&%C;VszKjsI)mg4gp{-`N!Cj_jusPs z$U&+rkbrkj+}G+)gAdE{Hd#7}X6tHHjL5K2$V%a+DdTLLAnyCWdfltha#(rSx+2>?hFKKI0 z0Y04$l6Am^YIivT9ZNd(Sf>f>(zx53%)k@@zY@-}aRkupOv9Au|1>?X?tD<%IryvK z-agtfd&Y^h@bfFP!_XgnTi*a+K%c+ikK2q+8RMy=p$e;EbXSJt?v9@J{&mq(@rvl1 zQMd<%E8x6{#jB;3&_3$6-XC>->h7I1*N$bY}YfBzEy^Gp2CdyoIgud3862qQR!@%j`Qp%^W+s0(pb9L8=a?gz6~ zm5Drt>8OiO_4{;`4%N#4c{6w_emfeBIrB*Z%H6gO>Wn8$sc;fVu}eHr0jNBdGrWzH1!_w*$5kSttE@GBPxoVO2QaC>AJB%h0 zstpK<*EP(OK?IKIXHF((-hw@XD$%>e#pX|0hw_|CTR>Bdl4%anmSM#WPUFyGq5P_{ z6VB|3;5OuI#JkTK>|kwth{y-&!&@&&#EUe%Zt#jN@l7fsT9TNIuDOc&iOgUB>QrO} z{dZsKf9BACPo6y4^!5MGUVQz9{`(UD{R{o~h5q~9pdbKz*pYj@MKh8H%!PxFjsm85s_9A}eX0dtEW@P@5)pjE_2gQxp9pMJ_;@$=Laf0ozHF<<)^zDCuOfqJYA>CLF zll+FxZCTO2!oa#X0S$KOsmrororUOU$QOLj>U4Ma_m8`Z!2VdAK&Uy1IaF}kv!@R$_&GRKpzyh zkiDw^OgAk$`0&1~X47pQbdKAtljs>M*X1O`piajPzae1#?DeF!L62eTAn+}hH+k?N z6h{R^e2$WlQ_7?WDGwHV)L0B~MaW&;e z!@FA6ynr4o(*A42RCX9^58!uneAUMNb1n5uHe2;j(2**4TtncqIivy(^}oOMXc~5NH5uJT z(;)*&Ij61gVG3NT@Gp?cfuSD{gVAGk+5z$jt#Z!A)vdP<4izewU1;5#H4?;3dO-YL zCbgm!&%sKrhEBq>tHkzzmfDI|ynARHXj=VMS>jUJJ&*Aujp@F-Q) z!Y6&DVDc+1tCShj4-VhA-m|P!UE?IPLe*wqPI_gpXXyY%_7c@Fu);)liqg)&Vld4C z{;~cl&bk*_-*+n0e|QxKqd-M{;%;44#dGBqG=*0cyDg^=+}h)b$TF(7;qa(?-1^6d z)=5W8s0nBb8&NiIzAe71dqw@qVXEjHA`?9nTG@hzDiLJQS<&X_TZ8vPRIkAG@%JIu z3k}zu23!i4B|8Fk?u_01<(+2ZxZjJ>8{``x*^~kbHw`fR|JMls zz(z{eHj#N0M=a2gLwS*8=gLLa?Bis^xza4<upbI80sn7zIwZcsUqlA$6zEMlxsJewqUw zGj>>Iyc$nCxF#G3VdLcjVY85t>}BaGiFXR|QH?y^EhXZbYz7)j->Sk_OOmKI2_Hx! z9*>Z86=H44sX0`q3`9#fn}8*#=y9n?ULQ}#xa7*Ayr`Z*1)b{E!F0~LS%=A+4550mS*PZW2zw|mq* zJnBIF1Z+rLqRhbLWnR)5mmc;vIO7t~;MgJdcA{=UM_mf;u%tq3-;U@eAg?tv1ZIH7 zZq6-L?#Jh!jL}RCAY2@XJt{tU=eTn6qSZCf9lp^x#GhK!TC53YOJAMN0;kyR##6>& z-EOp^2-!_!!?x#6vo{^%=&4N+dNE)B|uspKq82K`mJhYsM#JsKpV5}1)|XOS#@T7Lz@ z?d<5gzmKBSlH#M?eS>{?2e(|rVD?ovhbcXx)(V5Q4-Y(zyPJj+OdlmqW@st`X47PM zOh&zUlEZm`I*r0!uPy@Ip@#sy(~>yrU1(JZ9vF_rkf;G34QBNe;P`4>64gf3LfUGS zoU7)Fgur}pJpU7%f(^{*?9DKq_Aa!3$J#oif&n5m$&E+mDX7jAF{uy%Si7mqGfr4+hv8a``>0q9 zA_j0(Oa_B>wN>go%$o8sIc=WLFx~ArMBSW5XM^|(N%Nee>6wjBDL`@zn_(^@ko#a` zH>Nboy7JY;} zX&e0={#=unCJ>2btQak|$^pf`X^G9}p=0&^7Yo|igQ;zEXT_KObsjop;Ka^)Rb z#H!kq_GCzS6a3ul$P$bK^!mdITe3o8O+K23eLwiQY3H*H&ed2gHZev<2KeYKbRZsQ zRmR!cEKeXy{W6Hui}3&2`AAqV=x_2 zU7Lh=H7FQOYAdK&za-&@dXbY=J=ma6@fi*WhDa6UE5);2HJXAes(g@K&8^&vM_qL# z!E&^*hvKnVTbb&ohhANwL#-1XQapCF8{Gx?G=xt3u;}!Db|0w*KQz2KCe{M4-}WNb z=vcWp51J&1=Dm6PiR&i=j8w3q#{@2BVEpbIVnV#Mt`p}03goz4s&y+@UADK8HtOb2 z&I>|+KwMa@h&1TO3%lT0PDoBCmsxbLINAVSKeX6IvtN;^If!(@6HPy#SdJ=9eX`PO z8m{8W(H@B?ysg2#l*EE!WN4E`5_Y%6D_egUU)vxXl%b*k%!I0dXHg|rM2hwm=P}+8 zh`0bSjfOrA%zUkL#5){ovoN()o8jWC$aJ7nQdI4x&3wJ|Km^BJ61P*$`{6%1Or6~8 zrfd+NQPEtR&}hTuz;_}6f}&7w^G*P;oltj`@IuL25HO3N5||+wIToL?S2oRu!_$-! zPwqoV>FtZx5Ft^1EP|tj$3o-a^{IP*WN%@*rMO9Sq3{%olin#qG^)CD*#akOBgxRC z-6w}t$&kUlS%6|?yThGQNzfw)hhmPrs>&K=L1myvcS54N0*Y{gzEH$jPA7xs-dC8M zv-6cE#Xd<#p^8j%)=K>bxf9DXS<+0SARfk{ED^=P&%g4|a%pSy5?&|Y zSMOF5j!x$%o#zu|wh+D;L|ie%<#Ht!PAWpPUDYxt10Vns(Q@IDB!0Z`rpzJd(&t5!JGax3?K>*&?jw=}m$!7>fohLw|-znXfFpBfYzV{H+J>%>et_ zD8Qxu?jqPSkFUpMHDf2HaLm|20TU3@8OL9ov*nK#7c#QUiKW5~sc+ih0EX61b6FRM zLh148?c=lT=_1Yw#=3Y<@l^z`_Njjv0$6Fz(uypABW~n?idzJ}weC$sx|7W&qkAEG z(vT$x$0^vggh)rYq*D|8vpxxV!l^Lfe4Ok`L`CXiIs}oKgi=7;K?5XZF51>j3&Msa z-5eKs8mKJE@hg+)j^glEbS_?unlP}Cki*f+iFajNJj1UXost?*)@BKfja5PC|UO>Saw9W8=^}{Gtj7l00FuP6&bak$kCDa*H_e&&IwkD^Xc;+mnm zh)a$@8sEKmPaEU*doN{`tR){tNf(AyJVgd;t1{KwRWedEny(!YT0%BnZa zSr${8X7xPec``g5+?dQ3c3Gxd)g!d%>ME-?c`*k>1{NyuR<2s zVVwV>7{u8r1fzyOxJWT;$yI#QXnaS%m~abVN(4zx4ls7x!QnxRt%`J$Rl^dRZE74B zr!S*7`JtQKO;+7?yF-*(K2WphdZl%uIi@3WC)QbXJ>LRd%==Mj#w&9Oe1DGS*KM1W zSZ{gsFkb@gshXhnvFF_+Z3S0<{4kHadORZ;FoY+wk<;81;VaK#)y7vizlw?-+NFqr zFVk2A3qWUgoqW{|5D2U!fn4R zJg*&na&EES{T+P{FnikKKB}OYF$I+51g+8a)vbZ2;7TzZnB_*M-oclKQ{B{o6x?P+ zesHV684|?nVzyA$jFtl^Pb#@|4+}K5q6KklEL+Cn`+CUFBAF1DDRTA6pcH|WUTJ1X zBuJ`$inlrdC-W|N0CvQ+D1y!{uYCXp%P>IUVn}4bH-w@e7OxQWi0}a0Q-lYT7+E%U zJ+h9H4DKl%J|DbD2a0LU-=N(>za+W24*Lx6;q?@}l`E@OK2n?z-3yCOnbBRh7+MPJ zRdb99jkYP~-csnc;)G;KXf4?Sqb=|hR_wfWk2!ZA;*C1*CVudtoFBC{wnM7*q@m;r z9)SV>ei7&0^DBNI4iZR;3QjY_3C5_Q4p8=HLQEFoHF4H#n$3=Z=hw%FyZd{)5D~{m z?N-`#mS$iQlH*a)Xz3_&5d{o{z^DMKo5Z~f6S~VRtcakRgnJn|!h(&Fm^0u1FL2rQ z-CWh`hULKZHkCMtJG=n2nL2k@%^PpTU^L6$e&aK7tn#vyW-J$j9 zzpd+zshDTI4U0~>w)kigk*^_w^47+Izf&@=RM#izfqYXhjhpa~PQhOjtVOsB6hNzI zOg)AQ^-!=2ZYMqA*hww!^=3mByyxTSEY6}8;r}*1 zux0bs6X#5lgFz?~c`AZNHX5No)P;o_ld3=pNaNEfb5e#=MXLWok;e3nl-v%w1prEj zK=lggK`Pzy_lL)=H^;l*orqmGG3W~mq{%fr+$V5aLwF?Uj0fnDfNlxGod7L{(!2-$ zy`g3B^s7e-#$#jm2bVo%kAnvl`^R^Xh#a;I4$X?7eco;&rf96xfOVZ#2KMCvA21oh z#dyC%&sjrn#7^+(0i$|P2(TibBitq*)j62XsgR>(oJDO2ijT==p0llphgoDlmQxkz zz)fzTHUze;3;AYG+W)U6vg+Q} zPV!{dAFq=OBgZ_R4N)MYv!3XW^JAO$RINs%6=+BV5tV;n>jhCZ*_N&DY>T zCR(r#hA-5jbhQ_;Ju)~D1)Dguz#jOwALnO3pFZz(~FoSUt~Oghw3|wjh{buPOJVS_@n8RY?1_i^v+!{!VF?gp^{VN=Tud zLSF|Z9rjV+IW^0(aL@uPbC5VGf{2E}S+)k0M`fqSw63Ph zaPICD{MfdTB&DrWPt3>8ct;xzY&scd({PG9^}yimnVVz|tI|!Wo5(JF?^x6*C5$>t zn`M%50Wk9}J|g}iPT6HnCZa@bt+Iz`l>b+H@t1~H`CisGGmThQ(D%l~b{`=N(^qZrF)U6%^l_J|h zZm|^oh*i7J;fXDoM>DK$H%wj4$mrUS9ti^mG1_!G;E_7->{szLxfeh+uz%b7eAfEd z)gt(mH1U!ro@^YPP;ELB)=HpfsZPEKby=%J(ah<%3*;C+KN@I3%xvj6o|5`a&Pe+> z=^l&nZTYwY^U9K%ugYFIzbAU>L`$h{+HsX4%VuHh5JjfjS`a7KQWW@;lRqnu3)--4 z8(H8hqfIX8wlBbS*l0|~S_>zYck+~(55x(rx70iR;o!KnyZ3fm=MG4+OgcgZh{+Hc z=lh;{6q??orl8VI2z&inTcS&QmX$3wkY*HYNFHK#L&pKqwS~|ph14PX5zu^jQ;O?< zlU@U&(eI*Ir$pqcsYL8MLAHw#x$^?p8_3o4eYLG7F3B&k70&Pog0CtB@Ql$QZbUyh zot>Y<4HQWpa05F(svTN?6$X&pYy~%Yn3Gj%pU4~dCGAHKH=kV-j|jg>1JzQXYvNtY z?Pp4D=d)KfuY_Bh(O#h!-X^;nZ)3E7IKvFOHbZaiymuZoSpy=iX^F;sT8d37@t1*I zBt|D*0*AtzQYz2DY7M}1PWA?9R*`0{B*j`;hP62f)=IcLDy=3DM+mhhA1|wYHX!68 zN3=_bIwZi(Bh++5L`9?VMv#cSWsWjO#TPA@t*pLK#Qv@QHDIeSymFED zQcvM_poFMXd{aLmyBqNf>&SKF-;AN?Yk435BC{?HhOv_40txeN2vZgA3}2AxcnlHK z)w#t}8#5?cQDAESbp6>2BbLfi9bB$2e}&KqC_KF6OzjnCtz(Hl{PX6MYrOIKYyx1H z^y$U*I}gm`b|NsEK3A%@#e7t^*kT%Hk$vTfy$X@ERTINl_s+bQ%q*5xtUA%1sF`4i zggbcfy{gAmA6EsE*L*Qx(JWK=D_Vhx!9!>R*qK^l8R&S@)QJI3lL+kkX&r_H@TSZG zj{@(dbkh6QPTQX?D?UrkUhXj;MsbgEgUi8KxC+)eb1|V?o_Xy9cW9!^nnckt5Jlgv z&!w90k19e+>-QpxI+=QVLHKO&Zls}@PNo@IPfSO~js^?{tT}Md2(vm(h|F@I24g*{ zPHHxCQXGec&braP=y z{_VF&FotuQ3NwJOgmM(26w-3|I)BenWo0$Gx=4E$oR;MQ8v_8H2LiUU8RUHp8wY5y zosHhBRpCWz7@=`4q)>O0%5!v&ksv-2Bfw24LG6Vg!adr|mBx^8RtjS*2ZClN=?MP6 zdlnDV0T?~AXxS?!(YmYuv15=BS`H#^G|9KihjGf?aisWy*$R4IGQn8*R~eVQp(XY7 z-!cuNCMoVf#baa=%kXv1lK&=4o6{T6ne=`supFO($yjf-55C_u%21@u$rgd)iE;2J z=`(^zc=!0Irr(LlE9WKuuARTR$OC{1L8tVr&;kV$$>BLriEjsMP5O21?Rs7wcQY{w}$4A5L|aJpl>5sZE6sf^ zMhfO=Y*(ddyyG^OW?@7ytxCfv6RpTISd;n%bD>mq(b8zQUB|$(M2(IJr#e$(dtRc( zHs@({vPAEQ=gOV9&FRP@2a(j#Y!PO4w97$y238PS7(FP(<#}4JZZ}ZTU+U#ETx=c} z-Z}U@HM5>xdowJ_j#)@7Zcwb4S0H`)|HvJuPoCIKRUjRQmJ2 zM|dpuQ{OWk;l^&M{*po_S^BkWesj+=o#1II(m`E&9d=_$_K)Q-ePJ%<-Wlt4Zm^#ELf)tVZG4v-Erh_H?-ey9<*=6p5nC z4t5;OxUOGMNtNkvVhf8zme4!%+@m%9TTxYP39SE76!E8!E?Ij`mQ=8QlmFv5!F<|w z0{%g)U1P9k{k%hjS~ri+5|>(2Yc40*6m=AI9dz8-Wa!A)*l=VK3@Z^ z9%b)-M%gQj5^lFCa>X1fGCfQC3CD7?cp2k&at=(k44wl4-s51uIPlhY5omE3&za5f zWvc^-9+J0T%ry4&{o;#9)^|ehAB-KKnMM&A0M*$h6o4~K(N{f~RNzw}vO7P>`qpv` zRftSlc+u1z*;2VBPC+;Lm7YBKaHHuj=Y7!6RJ5PP;1nOV_uiwS-f`<6AKJ&Q?&0hI zr?rQXE_%ZWtolDN{tvnW-bVnS>i9ofPoBJZ>gWG{qN;z1|MSP?|9t-~S8p zf2$Wc6z2&Yd1^IV5M-X2rgBqoZw4)6j9tc4kuwHU)k3CHwNoH7B(1>>f#RPP z^numkdqu=*U;VG$Kv~(0W3?hgl*Gp3LJ<|~1f$*P@BvO_0WY+GIa%~e%f)f(p-pIw z-Ym;dfyc|Hlqd?+5F0t$oKh}XNb3UVn2y_aP3~~H3}GNLus}}}Kq>4!j1~SuNn-Gn zjFpv1ID_MeK%1_UU3q8&8^ho$8fN{KUiPZzLdV7IKxenS!37jxXa{!*p=L!C>P~kc zeKhjFo6#^)fR3Si4PRhFBv=DXYOjEdMZs!kd z2Mr7&#v8b0C!lTYARs)P5X=DB(K$p96{=ieNJV3gLEnv2iv2btdHzj1PlAU> z{vW(8@=}=o8+UbS&c05@W2aYEa1=@^_xr2#Z3@A5OXxthK-#}x=HSIGEVerm!$YaK`O~}8fQ+=4b7Q}|cs~eRr z2d@%%k#u)7H09+~6J2)?Er=91w}bbQp8&gD@FkZ~jo8$yFL z*PG5_u-c(a6!_9ZGWw)HgU4h#rm%dv$0_uM=>%6)&yN*9wyJyfus?=PVxge?w7%~8 zU3!>deW&<ap?P}x{#5@v?9zD{&_%X(FKXZ5l>&osEBi|xr=Tb=V zwMVl>wdlS^sJ*4@Y+NsEmxWmqLm(cS3rf|i=p2M~NMTXP17lN5{=St|0g{wh#0DCp}g6l?W z`j?;=MDg})mz1H-aM1@H#w#(V(mFc5>8PJ0{=~7Q!YNn0$^=YU>(x%->@c~!hd3jpy_hMk zF08GHr}2VWGRid{XGsj9?t{aOlc4Tncibu_#CX%%?R+?HDQbg%>mD5+b`JLr-!-Ep z)nch>4F7b6-NP0q7Yv%ts^QDBUjShWv&a)vKa|Pap$AiihOs0h6msZTL;juhz`%ol zN~%%x6s@U0qMZk17!;@K@^r_P_zRjD?_JTPsk<%)9apuSaN|597{ZZ}y^phBj9?#@ z-Wv~xAokLMaV#ON+BOz$Gp4h9mqJ0 zL~gbf+hYq#V(fUcpFrGHaTs&XpwbZ1yjEJ~gFflVZ zb&n?s){}zMIi2CC-_@O!o~{e_)7Z3FvwJU(LRdF?e(c-w_Mq!I{WK2c>Q8mt_*wmQ zs$&9a`swEjME2kR2wv=6^%jQ`3{v+ZzaVg_>2Rnh97Qq=Ze(VP(O8=wf*=jkSB#{z z)w!-3g7tmDyCI{PIIc}zc#}^?Ix`A-Qi`zHidO2yd7>+z5_*~^gEQH|851Dp%VS^p z5W0KTo|0sS0AlhuSv?C<|JkVlM(A1*CYWRuJB4!q1K{32x3ErFVAj zj>3YlmkwATc1}JV6_U-Xan8p8!d}=~?e{4o)q;Nu1{ruH9Q_$Su18#bO_cIN;N-nPC2^QQYBj@z9U_3h42@i|=}?>&5{ zKW`v|v!RGe*~nr@dS-2brOZMEo2;tVzcG993~Ekh-RW4(SN&DkBgVOoEpl-i5$DRD zjR)dyJGeD-_7R+PY@}v{84T1f0k8o)vEl~C!!B4kI5DsJI=lFWql6U@eAA(gx|CCZ zEBhF0F7Ad@G_3GNuT+|902LdM-ZTlR9y4{yUfCk5uODqnkTgxaGa}*?U*GJU@+22@ zTX6ZLM4yxe1tphPP3HqqQyL*0gOo}ipfUjxS(sZa43!OS7%AP(55IUeDtT>DgNWgR|R!^oE zCYzz(ujLWxM35kdunhr5(E)}{C-MAd$qiDNYVpG*j7TM-1AX*v;_XnSg%2H&X2qcg znKwjzHkQ~em`jJ1Q?o!U8sPHE*+R?tIdQ}cSLcVZ;uVP4DDCqRuuX(Zx7oCVZ~_Ju zNzc#04%3)xIFX`FYzhoUU>AUN1N>YMjy;b%s^W}la5;0BoH8CqOumGJ(YGa0*M|(1 zwO9xMP>6(tct^eDkxypm^@Fgw@Lu!m<>RXZCQzB&=T~;a$iK-s0|8G&1t@ZbX#jtK z^2gmG5?ll2ypZFFXPJ$hka}dvwz8uK3mdLO`-^u00LhBK z9F!KI%bfngui$1eyt|J6#8(8v^g<{eHc!_mb)LMg;u#Gi+Cs=l;y!|5Crjf_&I>$w zmj^bCZs!LXuOB6{{5=j!=*qH-`AFZyY2+l9;^6S)!_m>-0yhNYNvu59};HRBS-1ZtQgw4gI|RaCfCN!S|Qea(^LT_(}>GN?I5{VM%N}Ypjf;qc&Cq$D!?X;$5`!eCEE; z5Is2_!5cY>Tv}X{<4B9E7S02~&8g(}#CK6?qbLE5^EH(nRgu+;^1?{Qpy?oF?5Ngg zidem#j?Pl?lGr1`bBM?hC5{kqM_w`>r<4Y;$~t&K6o&s!A!x{ZaCMOm63yO01-AyG z6tG=Gt3G|xpr)WK7-~I0F|2x|P`yH_7+Ow!WFE4g<~<;SZ|b)G8EJDuVo zlx#Jjo$Uu<#p@q`2L5&=Q}w1L{kfOLOOqQ_-W%71R45eoTi@d>tj-+AFCX=D-oSYj zGs$%YgI(Ay&m0>&u%05xnYLCCld@gw4?8v^op(5sILjeq!&sr@czRJGM$mFq8_vzP zB3%G}oJ=z#j8k2WAh?6#`gB$G!l}+)E;TV7%2(PEufUP3ZnXHGLdUJzKB7lD;~S0w zLq2*0ef=f*ORzTvd#!9y)0&QFu$`(Uq)UT;w>E9uWYObBb&srbF;gPq0->JlCo#KO!EtJVM0O}ojou+Da4)bnA@A7K%^ zFdJxMvt#Ukbg9XF!uC1msIC*HIbp%1HtaIw$cCQ-tx8~N=vl(Mo!##g+wxs|&lGu? zDhsl3op%E+7{YFZhv27}2wy@Z?c~XcyJF*5(@7Dis6H^GE2Km}i$NNTGOsfff2n^l z(ke!D9*q+8{Y3{Na>gb3CY%gA40+fC@C@zxvN*3Y6y{+r_Wm`P%j0MayM$U1^CF8N z)Mu%7V=f4c(e)~7_Y-iHVi&3L46Ja$S08<;z_#^f)Vs*Wqwy? z-@*QW>*fwEzG5i~au>`~Uw;`+w%KrRBNF5E{KhQTs{Kn`Izw zKC{6Sx@0y};*T|9RkRei3hljPQe|`B@(F0JuI2 z`W}GPDrEVe4yM8SaXt#oFz>3?h0c-rxSd`q=_Jf0(8N z09+j+=d(t7h5=jGic(jree!Jm6imh;6A<}HTd5R&eDbW(kd=&R3M)4nZwf6t?@j<= z=DKe<@^gr~13{e<)c!^a5_%9wi1LR3nvbu3QHTPZ?D0)N21Yg5b8OeWuqNuHAfEk` zXCkmYPZ~qg3n2}x1nA)5QhN8_-gNKdCK?~u7;9`pSBH=}q65yS>J>)J#e zO=VE&L8`3)3P9|#to~Je^mW(0){E5dLTYFowV=9Pw%|Bf={SIGQ9jXCxW0=dQ?L6= za)Z$luxTDkk0IS%pDn}f?&0YhfeM-JQ;=rU(sSB>la3U9Mh|}wc2tu_!!6M+Mkf`% zMYI>=JgIsPiq1tt#oS6K^#=cLf=9s8b@?i0xB$xr9Ef%opCI(1F%J@Cp@}(#D!iNB z(QGgP!d7?4=YrKXpavZaRMl%Jso0D-nRT#VoI;Im{d&6B&xV_h)dr1+(P%b2RkJ!r zs?es|_~N-13dm<%pX z`^WdKmx`Jl0_hB)dhFb^fyb3jqBjDc%Z&{;n-q1udA+&S)D(-oU~B{Q7Gz8uO>Drf zjk+bxW)i2Fuu|!#=Si+`_S;xpPZM2F?aC}qoGRVk^t!Q+zk)0dAYN;pvhy5lrVFFM z;PjD_EXSP031s@gO$F+3FF~ ztpi`ex_g5n=!qHk;1h=QV*Jts@kzBq@R8{EMPjEl+@x?LaIM)23e@5 zs_9i@C>(2!iLp|7Q?+9tqB0Z>r3q$3c1*d&9yLupv;;Tg z;Us#PbFG({j}y!E3eXNj8-qXTzTMZ2!{d5Zw!14^t2lSW3ZRlI!3nyzWp5dmG0q$Y zg#bScFiZ#*P-dv-DFv#Bo)Xk!>kruaOx>kH{W&$G*ltE(8)3bRY~pe(4e*5-n!$u| z9t~%T$3x^7=h}Id40kp5Hf}K5V9uTZBO7fRiuQ50J&YQ-J+PjJ1>EAL^j-${hEnTN z5Lg@ria;!H({_UME`vZC zy9x#iK#0T0-hCL_T}f93sHgyE0l`XaV6{)Zn0odS%=62^YEUd|r$M znM-B-K_rBtneUT;$j++R${Mr)Nd?P^0hln;r;;4-A4>hY=Cf1V|F$M-hyCYW`fNUd z#5k>H!G%ZZn?tN74P4ndK?$%xAiJK)RDmJfd zrckB+RDV1?CS~5$SkDt8z@DRp3xfybhel-T+-IHg3+#NsvM}0KKfg8Lrv7^Lh@bkk z6)z_#;yhJ-6(K+aeflx_|16lNAB_WznYmfa?J(gVgz^UQX)*vI8CqSd+lRt&{JIEI zbg~jK^6O$RrL=C-KF;_T*48NPzZmEcg|4VySDVoawY9%NJNOF%CDRZ30(kSk5Pfo= z!bA(dU~cs-$PM4^e!tt@J^t>b+ifByS5(Dk%bkq?LWLgk~`j;Ft1(2qef`y+Z(J!O%)o7zy3`T=#)LZ&$ z(w$|;0f*?ZGhX)?F~c=Ta)%|f-oW(c8dwQeay@&UN$}?Ij-Vd4#x$rEO9GqT@J?aH z(C%T>9hzE|4DA$ZlF+%^HMX-ccQ>&X_29PlrW0zmmmlCr|Xm&D2^;T0sfX)m$~bEU8JqufIYgOt<^#cp6hc?kAZ1 z+s^Wl9r}^!n(%?9UZ4W@`7?GS3G_8p=G!_u5JLu`T5nl$!xz4vW=5j2`3CRr5{#W( zPvQ~4%hD}x;w|sJ$9C9lK+@7soUkcSwqvKx?K(fAQ7$jp5ioj34ott^Hq}d09Bdgz zcRH^`9f1yEzKU@-A9=#ZNT3@OB2Y$WFbJ>cbX+xhTI71k)j9sIjpD1f+nA($Y%Xs zoE;c#Wqf&Qa7*aflRznSMhn7q1R4PSs(M4b&l;59+W?p6bXDyVH`LFOg-}I}l7bf-`S4 z$nJR^NuyCOyP4oMO|+!V=@%?f2EWL9JV5MK*NS_1qIr>i6{cVtjlZjr2PigL&<&Wt zWm@0pbuQ@mHTiUfik-gr_4tHEJ@J?=qOuoszl|3qh;<`0XIR!^R=lDhOB}1`jrRi$ zUq;g$)3<|*K$V@KXSV0MC+dp>S+W$;p^gmiKwDy!fRB)y&}@hOT4Z!^DBbyvLTT*c z$oSPdHkzAT<6$>Jd>{U^PofF*5>}%Xx7H z7T2-QF*sQk50fb;pA92x`lSp_(=TNRntt(0^)=mz3_BhU!gP`hk{$4`BYTly-r1h)d`_ubJNU+Hkq&waxtC)GJ|y|kbR~Jl*#Ggk}LRsn6JbC`s>8% zMs&0J81$n81{+O--|4RvGx8chP|><0jPQ0L0v9~Oi=#Z=6GfP^J3s{uOM$)`ja>@( z6{-n}`z%5Kn_FN>O7?pnZdLlI=hx)uWyS*_9JW(iX-6wp7wU8v$8tjRi#Y4A!?IDMBV-XfIxr04M)NjqP|8RE@sC1#mX9$xLDa3CWqiPOU}q% zm`r>lY(OJ9oO}slC?jt5jxyhO6+^TX`q+%j9Tb5z^a+R{0H2ewhhVr0-Tsn*|E`4b z&&t>D_Q5$;kDPH-;;}Oa6p_w1(?%zSw_aaiH-(F0t)h4!?)SU4V)eC~<0*-^h~dvz zLi$!Oh?>oSV1f~{d@uCk+oX8iU~#b@izTgf%ta6a>g|5eD8uQVvVyYX={yjuqbm=EZ8RIl3mE#aURLjx)Wfbxzi*%pnzvgq+%Wpgo-9sUrMfi-W|9 zRz>kq{;Ylm4axNx{S3c2HLSgwp@}Hftgu=$B`a*mgTuHCuF!fIxGIA;gM#=AqabTY zZXl`@)Jzxp6ZgS;r)@#eipz{ERvD;Ec2r^rvGmXCkwqv8Ryn|AJ)ajJn%KMnL{`4(wxj5nY_GB27__E!@0%IvflOr2dQUhfO(5mCr_Vr$%ns7 z0?lpyzV$+V=hz(j8(<&O4gdV~$w&E>hv}w$Z3b=nkW5l#Vg9q*Cj_<5xZhziy%_g% z*}kO8&s!fci9;7+rBUFJZZ}$4p}0}~XmvHhZDK4((y?XyE^7-jf0Q0G z+w@8CX4wBIxdPzhLM8$r7RqqkpABYt@r&@{GeybuWDH5no5fGo8*Kr+#@e7n;dtNP z&POp7Re$DHt-l)0hTU;*noRS#$Okq>?fJQjfAm)u1?jJRee|F85z*gUKXqF_LMWr} zL}ID}E)wGqI%*Wlg4oCu&e>4aX#i2wX)QCV1>w)($ra=oO`l@P%>&+y47h2y0X_R? zw0spk)&u#J|)jb*Gn)!$m3ha|C)@JtX*r2N{SuKcZt|wiWe6)vE za#Hp>98;*k2#Bp);FRB88ldL6keZ@loIJzfU#w!3by`sJ)eibI{`MP)$9*3*Iy+sb zO1jTM7}L>ZJWw-mw$~N#dry@shz;cyE2`l9O_{5Oqbn7RWVMK8X{>c$$3HSleg_Lv zJ()cDRW0lP>aBnA48pfmCD@Z-yo&_hzb^O^0XkQF9EMl#vG@kdE|Y7;7{+%0FhLzZ9_>iy(CR^|Ug@9@PG7N5|}W6lq27+>3%P$h6yd zhIhUrHlq(s9d}CDI>1A;`>u7|>He^Le9%7l?&Z8GUd7o6BmCetJ?sbIoW~M}P)LvF z4gO}gv-@uD&}T6i1>Jt&_101A`2Bo?1~cGng#Jl9B<g&aVnl&l{}H zSPERh9S0-+MhG|t&x)QDHQojDcd{7JVj}kiL}4jm|NfY=SmL5LIGiSLle2x>wpQS1Obc-= z8gt0LA4Q@Mh178eIU#4|hN3&(t~*o){^oi!O!22hj?Oq|T|aYA}-vqWw14#%3nVo`hWWB1_jpjBt3MJT45 zq0pyfc{@KwGy)N}LfK}<4l=mxyaN&1a<=rYT9VtNSxi7TTZi^Cs*Zoxi#cDFA}r14 znSdE(@TfmXOs<=W0`!}zz2C=lv<;?nR`s1Ue-l?d?l)iFhW#|12^yR2 zdBo@>z8Q?;el0|4X6xZe?gI>FO`Y{u{M8bo{2@e#ie{x$s2=5!)6wehM^+k$x<7HL zSU2Vgb|t1Kt>f=o$7HDnLPWzb2Bcj$Hg8O`utTo97aE_mjyvr)?Y-SjtI)N}eJ$Fv zoOO3!Jqndm*RTJgqiD5ep>u)3YW6utOlz{aTlmMb3JP2b2fD52x4G?RbFZ?IT}iHc zi6YIx!I<93p;=G3Zw1I;}TT<)U1#Y>AW$`r5x z4C8i)dX7LxS`{|817kHE=%gAnypG@%7kywVmVPowNp^Qtk&wmnj8Z3d-n9% ziztt8azn&&M!x@spl^6v6RlE#+qq_ZkklbS(pkD4)puPqaS2d+sjZgj(`WZpu#qty z_Y?i=XAjU~+=w6z!6G3-_R3u=^W;|u538^lu?)_++R@2) z4i%#f)J>f+KS@JLh&5i!a!EiAGBscWrPJiaGYmKUtIfesnNVPvcG7quLBpC&d9Q^d zCVi>hfs2sl=8LG_p3wd%x&e>YadnnS+#!&~@N|%Heyw#H!kE-WXPM561GNGCb>P1m z)^^_Yf=^`^467Gn@R%a=9(%!_OW$cz(AYHz=mM49>$|3IC&zR_qJ{USOM6pCl2m!w zOf8vG$<$bL*62jifwMNejTaMOBb*6_*eBhi`NAP~thZp?D+E6GA%Pdoyb8Q<-c)zk z=IW7|sEID4L9L`uOb%yGMFiCGH^WBOvDJnC3PMu@oj|LD$SGpwZ~=D65*kY%Ic{KY z2$fgO&?pwU2mjt$cgeVCP5+#|;CYH^&AX2ny6u1ab#!ha1+$xf32WWKMX#U-qIV0rBJJgcr{ilUzM>7PcP9RTn>C!d}ll7nKtGAH|=-# zK&#Ot`s)U8#=nIy;4^2}_Ckm=FJM8G$kOdlrL|C5Nu0`H0P-UKQS}(!HMWv>#g3FJ zYMt|uh_tVMV_W3L54X5;Zg6rFTOscK%*{Uf(%n7SV4RlT77WMQd}E1gVfCcGmHL%Wb5?xu}Y=)AfC{`XN}LCz&d zu^nzzHyxi=Bxe>{dT(g)=!&lI>4<-{b8IJ#a?*8$bGqte>R`-$1CjOs&6gNHQ(G|HtHLD13LFgV}mO4AZUBg5Sv6+ z6;88thd-<X7;GCaTWw7)!7yubBYgh>Pzx%G%5yRc) zrj}mRLXEuw|t-<6%ddX8wR&thz=`+hW{3bmKS0%?z*#(b&`JGRTk-?<{C1| z?=f(Ko{MnYDrTaU^Of0X@#sRw&V5p1S~TUCUDR<}IF0+dO|@e}^??Qb6f_b1`#fFJ zWJ=Jbbp@tuE1_dqN^=R@TVpTK@vZ!^Y(H}}JLR?x{4n@3fJFtObA+CmC-mgkfL70! zQF}Rhy!C38jMU0>ZKv}dr$vOG9IvEG_Gy(MB#wm(X=hrh0})FP>p$*C5@YR>cz#wO zV|fvQuLz zC~x>OkzmWnBrTI=kZ>7wH`R(?!=pR0*ZG4lG`91s=zS{{*$c5*wdmdxr7a??pD&ir z>Wk&=#WlAJ0*@Lqioz$ zPeDNk7j(dOOm4MXk2!UxdkIxy4G)Y-b*1E|Am8~62AW4w=K?nv{99tdW`52DdceBC z=-C;ipD0K`fMHz7Rc$_RJs)~;JYza?dOI78uMmr8<3V(l zj{4)P+iqHblQ$ijebX7%yq%+p)m!)3w@h8y*NF6aHnY(HF0k(s828W-!DWccJOIJu zTbfgB3flK99Y8Duvvxfbdq(>#%!aYfGqwM59)Y1aOGs`B+g1W&lNgkk5X%aSk;w9N zT^skR+rcmDLN){U3kH;r?-;V9B%cKWFh0oM;lV*DoL+etO6VpSwH>OsYHkP%gQ!m(-^OK@^i%beO;sgwJm}(h<~vj(kPr1kB~Zs`SXC`{ zc<8#SSEwa4O$|@^6z2eNvXc5UFr+ALKKQi#c~i>%Z+RA_w(k#m7Yhou!C*UeZ^>gz{u)4#kEy_5JL89PjN+!eA77#-7iUD6 z+bD7~*9-etdtrxVS0SjuUfIX;Gs6xJH3xDW$%$r9<}f9Wl*4*w}uT8c!kY^J9-f;h2;X1FW_TJ_igU6zisXAw~iMRTH51| zCzD3wgpjNHec<|j30h2pPM8`qfaPe%7D%rr5%R(W*Au>CJFNiaH>TmA%_2Y7BLX_)`~#Gw1bYSq6}7VMsW*I?zPD8Gld% zR^Y_hP+d!DEdk~>m-q=j+*&NW*Gt{0cv+}JcDHt>q8(FtBJ%QhvzYtV%$QC5h|xdHGTHsAd_#)bImAVI(Mbt?c5@dL$ov;v3nkAd44eT^t-&&ASxoQ=fSu zu0{1Sayb@#l%$aUn?WMXl> zhU+!^4lKr7aJv%Wf$r4TN&OI~6sAI+9pWait4f{55aPbS8SHurQ=!dXn?#1SnfbqnE?YN|i?2NM>Y z^G!HOD6YQi;B+M+m0(}Ct9L+#x(aMWA#hgZet21{7TcD*WY~p6_to4SCkRLBF8Pr< z%>Kw^15H#8P9bwLFCKVIRsg0WOg>z(dBtcSw}K`)qdRX}o%9Fno>7$xy}SyLK4S zh~kmPgOrYp`<`V+1R$kYjwg$>!uyKgG9+3uE~I!;o6TS+L?$O^z0q_7Wf(y2CrKCQ z++Z4$ zY*`aCVUF#h0kl?*_fW%{Y2EE6m4+onY85PASj^zw9ZkqlkDMnHaMvVU0W)oDo&ScOMW3;-<-9c8>p4gz_>%Ey7+qWduJOYn-t!Sm8oCy!4;$ z^}qZkSFGRcdj09fH%}g)Pp*^4dDeS8N~Vt!MY?6`myO;X$JkW=y?F5qem+tElRx3V zuU~9_{im(1CtFXSZ#{eQ^!cAQx1N9f?CGDP%|HDP|IHMHWl{8}i%DErZ(jMo>3@$| z2r#+H((?-ieS52sI-D2L`&1FS$sl^2L1vzBho|uKZ)eF!LCjP=dKO>B!R?Y=Jt26P`o#W~@5Kw{JU2Qh;rf zJ>&_G0ljda%}7y;@#rQ3cXm^=Y_)b#Kyl#U7wV}cZJS!s6-M{L=O>u(n|9}*b#fBDIXsSbqodv9PJ8deyWQjH=)>{R;Yn*FQbc*8w-Y)k*-?;a z3?YtSKbgkqAlD23Nu47{8@z}wg$68EBsnBLnRiyuNjw;96Vj{yvZiARpk@U^z zDud)B3NaT>5t`eBY=U}eqZz&U`g!zTQP$D!r8>C1SOII=KUcrL-;FjEUim-G=)=kG z22rAaPDdCgH2OB5DiUG3aq-IgHugV3L`3%+swc1FiThR2?<21<(fjT<#IosGH_j&E z0>r$bqEX zOTlkQU5Sa85Z0&qJ%X&$pae_bPkZx5qpDylIqj8W- zug00KrZ*7m9iSE#0qCf^+F#hv3N~^w%P%bdBqEdGqMMH9j@N4meXo#~*NjY-EVB-W z%TmLFSN|IS)L{T{9z3e$dAO2IO7wb`4#csEpe07RLTwZ!NYmBl)QmK&xX%5yx?X@- zi_^r`H2I=->>|rmyhE@PxILtyY2*U1#`PK6s(}saT5$XP&^8#9gS<~ybIiFgxz4zgUs zQ`tqq{MJ21_K3@3Vb!8h_~u*$AE4=h4OM~FUd)E^XdUz@F@&>Fl*urjBD4~KVXl)k zgxAq@9GhJ72G2oXZr*Tcp*H*Ht#3YVM`~l2HaB0o|63w&8#gIE41t~P=w+d5W};?4 zJtG})Gg^AM`Q-X#**^?Nx~HEvKQ`?jTk?-5AHAwiW$82d$McVpOGGmXX{w|!^>8*& z3x~J5a6qv_V7t8AXYnu{+=y3=j?P6hg=ezteBB7di{hSPuX_>WBjdO(#~#|zvn2UO z;LN=<)b+b$V76nHDO}NX2d0iZZIkHrOlaSB-*nptEsQ(vv{mN|!lmoYt?fe578h+6 zi=I%?^Q2hx^rM)w?Fo_-etlM~{wz?PvUR%ko(Jkto(vJ@TZpkk=?_171fqF#@o*`+ z;=n$|ng+FBx?NdLdi+s5L^Zxa)Nq3~biIZZmhB!Ny*=LVDgvN&j4^XI^a{bYYR6Cp zTtYZll)bfOhFWdxxYR3@1Lf1;0{ET24F>kxU;y6$7wK>CJA7l9=CAx0D);{|{=Y*0 zGwM(7Ec&S-|JmAlp?-JeKhIu#{p^eU=lA$;9yfdk)^{#9Tf&?I+y!d)hLabe@4>GQ zQ(WM150do!;#848(SDluAj8cK*jou^mn1xAn3iie;S`VNVN?H%2Qwn%a~zr zmk(v);!k#2KmPb5hwCU<%sl|@yFCqabU=a8RFODV zzcz!X3Iw)8UXv4#utO_3@dh2W0P-UEsv~|F6|alsdS?@H_3=<$Qjuove3dezWep$A zXxRddWwi-%jK1GtAZ#ysTw1;PB`~(B7774sOa|QkHtY7p8=y6#j0Ckn;~=>z;4Iv_ zmx%cvDt_h_{HqORSg4_fexdnidznh61Vp)M8m=LFvb(wJ%GuN-dP&NNptQ@qR^fX^ zLbm--X2>MJO_D9#3SD}V+ZKK29|SchJije69>c>GHaKpT2}&QP<& z@C-?alYBBBX(t0#Lj{C|8&UfVlu3Yq6)gf6J!LfCr0;_P4k{N08ny?R0t#?h9&K(! zhd_^Afo?k`^C{@ePEwr;#KMZTkv%MdzmdC2L6|`F2tG{eLT7xgi(wcmIxG~aGc;+& zqgz6tb;ui&V0yM9c~b=v<6c68ceZlDxC>B|2{H?!Dc8 z{jTN8B@aBHxk#ra7dUi-*Jzf_D0FSPLB0Db(4XA=W)T^dV=OmSSlP~9K?a8b*-Q7- zk7?p`V5@JHr6UZ`vRQk z!wVP(8W__)|9x_IM4i5Af=o>p=GGn2^r1Q55mJJ=I*_$a-FHwdPvJ#{ zK!0SVC}cV6>7^H@1PX^~o@+2fGt&^%Sp%DXH5E!uryg12t{#wX)1lk;4&NWWYjs*S zP7x4_CFsj{bfo=8qf@RYMnpWLmmn=rrQ^$|2ZjBzPn!1GDS=5}aa7PyDlmWtEAU=n z89sJ_w=Qzkg>P{dP6oJ!P5&0CjeDl6yR@L*2tAuM z4d$L!r<`55rM5?pmY)sc^E_IP+I#O68}sJf z?sq5Mlf!rIy>_RyU(#kgnRY2S)K2uoKs^Vl4&16HpjGo2O%#BDZ5z|gp%-o03^gSQ zfuVyn)-9VBp)-IFxM5GCM{`i$YVlOA>6I%S4pbV{#f~6h5R=ifnolsA|UT52CkO> zK7GFV^_D09eevYUlP~h$KhXZ~>o4|yU+n+>m)idg($ibn1kOeZ3i|hLC#XPrA@jl8 zm;F3btcZFv>0By{RVCEZM(Jr+7w<&x+9w?h zr=XaDb_WPGTe@mx;CskIMiAh9kRY}E>nxc`$@eLWl?qePVyD@#+xs=i8x5;g@H^9b z25vH*G#Yz;6({Z!36FZef;8(YG&i$&wJ2NMrLNWhUy|+OPLp-=n`C%o+$Gf6YX6L-lZOFX6w%VSA(l zR}VZE|5^0k>iz1vZ^7RWtivBW>*L&ezplU1I|S4*|Efv=yt)S3fKZ0IAcLgElQ%eg z8v|WE*6hkrl7W#DgVVZB0&bVW%$r&r(6)R3I4JU|rzm3>E3#CcH*YJgE)YSrxb4PG zQtYER0vTTk*Ywgb_NQ7-SCTgewoz5F@F*gymO_An$cO!PE&23rimJ5oaIy-M(sRN$ z0ygO3p9j}+UIwPsW>~le2>rEt4)VtAl7l3{WfZJ>M<5rJ4l8Gg{GUVX6%w#v?v2MXP3b6VnH zcsK1S7>*Lrd@`I&Z-Q5hu5q1Ha!=%w{}RyDCUKfA^ah}?=n5VJ`wpPbF)AZM&j7iy ztAnDnS;f=RT`S`0((hVFI4kWP8$s_n6jjLpzy6ZkG#dZF9@MYq@%eS4zw%bdCx_ku zC{WeyWAp2z8kjT^DLgx7^JJ|msN=n>?9^;q-A;MSf}J8M3VY>0b#g0yo!G!r)A2MO zbal3)`VXH4zq>toc6=|V;ji#oKe7mY#XSLapr-DxAns5F8%PkMmEvlsvDs z;R60TkgwtnG4@@8H@btM>c=}k;o4x@AS!c27Khh4!3&Z7VkQRfw!}GMC z+ZgPN(Ov&IszY}n>f>8Y$8c-9%VL0+4&il6iVIJ_4#j21H6#|&Rj>hNFZHjJCL+O! zM@_*NH1V8%os3Lu{LtvScg)QGkb~N2yR3K`{rqs!R8t8Lj@>uCC&UL2+s+5cMdqa+ z+*?e|K1BWuY`gH?kf>48q`yu|Xi^^xi!|x4lb3wy`^lhyvv3f<)S!f5sVQKxN(`%U zb~&N@o}_uVpt%b4349!m`(2n%qk$cBR81ru9dZY_>R&ai8gFaQxj|w)&SXh!b6{Y}iFf^IP`;heaOicJSd6nx0ZN zn5nM@3s#NG1!R=4UE(#J`<~*kvz}G3*FjgDydFvO1O@9^GK|&EtI1nEcup@99(GyM zwIYdCNWG?&w>t%jBK=6fN)fFwHm2bi5~2znAZJbadTVd@V6XL#m9(GA=H=2(bLJ(9 zXm?&5wNuOt>bf%{%LtmENloO34WWZ1CoP90mIdKP#VH z+hswroFbxF;M$Jlz2!*LCERaQ*!5Mh!Kf}*v=H2)IgD__!|#*PJjRl@NC8wgiF+4D zEf0q*_?%|M$ksrUzSlcDG>lMEGi_M*ItTXH8ufkG-mkse3S+1z*=u?qI}NyK?pBLp zsJ;gO%;n-uky$Y{h7oUf7~vMsUsE7fr-_#)YZ+>+zrq&qkCba@-pEwJ_jX6d?3IU3 zk?cOrJ$)fxI!ypnb&Ts$$z+M>an7Z?jF{VQG;myBbheVksEVClpmrE(@l^vvS8B{3 zOl1Q^tSF4mKojUelx1lJL|wVk(2%s#ZhGMclcl?s8h8*U$GiHq9D=SOo0`XS7waWa$dat@aDu?+Tw{r{)0H{qa8$r%tLOtAT`uZS!=jDq`p~(rTHfY@P7D5 zt4jW{7}4+S_@hVLVKxBwphU8MN5H=`d^2TtSNOgk;6+kS0FlK#LR z!(GB=dumiFV;yQy|Hz9pgi6UG%cSh?i3y~sd44w$v{wJfcJ6(C5Uk^AiR_xBP>wd& z(e}Wnmw}YhBxc81qd@WV-vNtmXkGo}?ofVPFqg7bT>jkC_fOo+-aqj`=kxB*EhJIZ zf~Y#;rwV9l`0ykXfqmkbQTLkFK!-5Bgr|jsEccz#z)5Aqi68|~gY!5?MW9R}3qGOH zo}!*}rjdS~_w-;q1(jO+Xxx4cW@Lp~5b%MWcX))ryLO_U$w9J?X_hb=jh%O5_cxHZ zDO|BMmy?>(oK#Lh-$H*g%0+K{vzD!)xO)X8134IUlV9b2*uZAh%MwyZ{RLQjvw}&I z4dW4H1+W?OAixgEMsA@CU|=Y{M7xMa;{>cD zATxP}S<#`yB(zmH(5A&KyH;g72Msk5r)S0Mup)nbPBG?%NIq zQx5*n6l511sl)~vOGd|gMR!k_Tu;O&8xfByJaU=@IK`S)7_m1>sToGKMVgq)9D6jq z;qcx}E^Z)##i|gUxn>JsOK?dcD?E?c2mY`gMJu+~UMbG3VNWGU$9@=PcLO|r#%H62 zfUC&O+Q&L0@^8g67?xC_4~C0xM%jtkbtHY)YWa_LTmJIj?-2hJJqh8F00Z{?_D zn8fF^zw1_2tE#Idfw7&Ljb|dH?z-2iRqMV^$LxBWm{vWX>7WKuMr~VCaX9h_6Du3s z4eY1+=AvrD*EuLN$Em0>kpE!B`RC?TLKk|?;b+k8g&Lu7G|jW@!g8x5E*K%aQ1nRz zgT6mpsp_aGGjep3lQ|p%%k#m1ZQqjk7<({*|A1$4FF6mpu?|8rCQK8kI;CivS-)0$ z!@29piO>G)xqyyRW6oiMgx!Ws4=)rab`KwEvx=JSb zEXp|zz}nF|gK@LHEDJP4P3AQuEtWJ`IM(krW{6#>G*~WU)EEsB|r-|Q!C7?)va|1Sx_C)mTE2!d%mp98`F0kHdWM$ly;&a|?A#6((` z5@3fQJwuz*A>MA~tPnrj6_n_J5aAtD=x`AMyP#hgNhhAk4G(+b?ZPA9@L=-WCPldA z|B(M1wZ2OJhi9vgABX(k<7aEP@xQ)${)aL(JYzJZSn#3$~ye!NT&F76V3V}avRvW-fD5vKe?Lok(> z-Bk=Swo6{)NtMZ}rk!9|Oq%=Y{yZvhXArc635 zUgFZ?aZ(EHbB9_gxQU^P>3?fHqy*8pXslE%Rl@Btk=U`y{t#}}9H=i_=S~fFx`uo^ z5I#iww3EEwy(C_{ogI&XWrJy@r&)o;!VU(|U^DE+46H!&CWHd8&fv%fwf2}`G+ck|QB71Jy9_UIuHas@`o&3==v-u2VghkkzE&Dy8L zcuJ21cNHBjV2`^$A1zDIi&yK~0xbnnIq&q_`T2yY{j|GX(SEFXXf9YpH9xXUo4A$- zTYtxy%{rlB>thqzUpMe9#-@{|ggsPGDcXaZ(_3S*+#fj^4I){9p5CCM$D_lQ1Fj-{ zu047#va2@Y8U+dM=*sZX z9iaXuKc>%hEuJulPidh79H@CDt?@LN*MyhDc;e^j5Y;}dZt4-oI&X@P4K+E84bofp zY~8bnzyjIDfO0%q=1fm-iTklsw;9T|K{#b(FoQjh+5{eR|Wgh@2 z>1cYpC@^xU%<;gKproeXNRm>@+eX#owlVJ&AKJxSI88K6W8rDDnJg?MY4<$6ESmCq zaWT<(9`9HbhzhG90g1`Yr=C%IKIp!=^=9v|vHjfgPjmcWZe5kZU2p8FGW@_RbNI_{ z^RTgdq)M4brE+kDw}W!{$Sc=4JlcE({run!PCfNXzuwtx{$_6U4llEuLmQ7Z5tnQ))I|Zh2!k=oK@DP||2195%{g`&T zlchvho==}VdGur@l-DAFA9u8i)pU@jQE5qRSS7pYU@nQ%b({0vhoF1UWgnArR#UpNuc z+A=SAl}H+kd3|B*L>$&Et~F~!i(xXusy-H-Z$~Mhe-!^Zb17{3IZmH6+t{8(!GQ-O zrZU{weRtS!UOJnj5r$f!Mts;)I=IOes)C@Co& zf58Aqal=x?B@qToWLRb&J_BKcQNZA23>xuE)T24VrfXtwYki-zK#a14ze~wlk|i8# z$xX+-W)RcIKa#lx+hEJocoC>CvXtsJxTDF9P?_~gLGXut-?KytfdKuSjK?Gq+V)N^3aN4b7)|*%o<58G~(8 zzL+hrFIA|iS8=>(fk41ZVC`@AD`sus0?#Gqjy97GF9g?I)Sb#?8yaB-UJ)bY0%EX? zxVkKDfcMOYH~Vm)yQ!Nq*RpoJ?94Sthb&W(6J2-sXK2N@6ne!XEtJg=qDLTS$Q02F zfzzNnKBbQU;PA8LwZ>SOjg37~Mrcz@-d!hR99n|XsKG27tyW>&YHEy8V_LxBN9m`c zA-PHR;cUlo^-5ysF311%V4;ywK;K;IBu$EOhf~W^i4tXIOtN4DGdR#ICASn>E8m|r zN^@yn=<29$XN4MlgJp*scM z`;2brA|vCj+$M@dGT|~)qQsOy6pzG0X9ae|HNqkjY{Q5}7VF7B$Hhoin(XYbvRgU5B)z)*~cdvq(=e6B7>Lvp1+~wCe|q@{GDUb6VhL*M!F|Z5dNx zJUDGEZm|lcrc(Ha23d^qfk+rj7|kDoMJk5&t;8D@#HT$(a{gkpwDNK%FeRWwP&`Z9 z3FbRP2=KHL0WcaMC8z-~giCgm5wpT(nHU207`ZQBwUKjybAi*e&w)2mWWlXnW7!S)OGrU*GvJ$=`IScG=^7iN*GtFd=WzPrezLZrLN!~V(erRF^90OO-kCneX zElN3)CvM@$(^Jht-t^LoW^|Wx(MzCeWsTKwOU06XA3MdP3|j z9-oCVXDP^MgS)0A&f18b9#Y`z9%>E$yJg1Dc`>7$iLfeM5cFag$Gb^N=#Bz z2|75Dpp$B{2qrFInSkRAwd_hrB~@7q+rIw^bxtuAiC{dthRKSIE|1tm~w&vas&0k;75TdP(6CHQDXD>H5_S zK8j>-l^B_m>8A<;%EZzn)Ny}9Z&A%G(X#jWQx!t7*AxqN|AVKm>r+)OY|>%vF!*ypY85Bcyh)7S<-W`f=LrUII2^i76ls+t-Mt%xMSh z3V6;@K{;NDGcav#MQ1#@*b-VL(9vxT&e}u&U9Ui^SMCMA!&m>kHGyajfH%+Dw(v;G zvNiP}z3k>`JCul##wUUt%mW}0ee{tI268JZjAqS54E-3}Nm5%#D)!;H-)Af09EpQ7 zFLn}3L8M5?GsG@_M?M|rMD_rw-ohWJv51(m0k=5__o`8t$`(L#yb-LVW12&fJ;Qi*cE68|CRFS> z_TUdkVat#oFzZbYU%O-;F-AL!envMoA23)Uq$m3snuidO(~1+HXM@0hN5Zm_ZjYiU)sstt|Oc4!)U(qPHP#ioE*cn zOcwuu0HsR=8Vmx~8M1j@$=ZuqM_J1v0Ki4VL>b{l?9?Ffle0(Ladv{X?t;X4&fKe! zUQ<97O9hb{k_;uJ0O<}#MbG;oIiHU zrVBGqf;0MQXLoz=r{>|#|83AN`b|bkVZuB{=kT6Hc`~??Ffh`qsafvHT@m{E{6_Ns zRxef`ttYT1KW5ngFXo|~r1p6}Y%ix>zL?xiKw=+h;DtAUMI%OV{g!Vc+SBpC@)_oy zh4#%RZ)obV(k_F*lW$+4b_e6)%mI{&DaVBbsqwGnmmfNm84TOo+dtab+iku*Jk$k* zm)nT6q>J?^!jI8G)0Q3{nu|j4TzT++{E^rrEQXXKhnZnQBX2%gub@aIBOXsb2e4PO z+TGjNN*P{h zZkDHHUWnFCE>_-%(S?KldASS8>BOXu(VAL%!ZsiC-nK6Kk(nawg6M!18;g(8_+fbI zctgyP4fjaxH4rpolFzp>kyk#9s@|?>|2)CWiEIJxu*Hz_Ih7`&mFkJpdte`@Lm&cA z#$C~}Y28rs`-9D`#%ox-U!qif9GtoNyQr=7Z8>e)IGWbqk~vw2x#W2=cf#Km>r>4) zRidX-fsGB%keO~Bv?G~RkkoF|3M!-Jsb!tb^31FU8iJbowN+z&%C16zkVcJ19gM4@iL9Il-|kdBLNB)E#%pB_XxFuqM1NJ{ROP z#MLs-I-vGh8cTj-^u+|0%MD!QZHz^-=zAMu0BoZMu7{!+g?DO7@Ip-JhMI+d>BqS^y6`goFE9^qk!+rUuINoGMUI z40fNGQ?J?8%O~P=ZSty%5O0xj5;uv|+v5uA%~-Ji;cUSx>9;8V`HjiA2o?8@h`8yb z+v=Z%aPx7GBjzGh+f6Injq~Ey(YNokfw@Dgr@#T=_);4S+Js};PM26NZSucRVe9Ym zjYZTiVH8>I;ul^T^q`&f#<+bIKE=~W)#@GUC!`5Whho=f zExuS+QOu&M;>b1!>A9)oBGgC>8~u*O28Kc4hqc|B zF7W|!Iu(y0Fhg82U)ZHkwZUoRInoO-frho?BBhp0L!oL1#Vfe)_Ue-Ug1gI&0{bv7 z6x8*L#Hz7sxryT?aS&xU2@3-+7{)#_qNTmPti-_x6p36+08J4egqN+EeS7 zNiA@B>lNsPY|pW-%#q}0Eb%h4aFOG~x<^6PQDsfzUs&Z7VWEB8N9{mg)=az+LWc;R zPARH=je)@}I74_q4R49o#&#K~yk7s6Ryj3T#zLPgk; z!+uv~HgK6Q`?LCC!vJ;WrAj2kmg<-N*4Z%clly}o0ir-PKl@`DyYrUq~cy*%L&tFFU43t(EGhjhacDsv{1&$s6h5? z9)T)0sioN>-;!Nr`^JU1g(C|H{xCRT(wZ5b&1o3z6upB-TffHTV-8I*K!;(>0_ZTo z0A@g$zgi84JOiM^DXZ0hBbZ+H%8>ur5eSsoA{4NJ^cgZrQgdzK=-;ro;q9iFP{g4S z$h>?!TFy_FhmHKBlYrCtNXT4*@um6h7O`YV{z)9!~g47a)g0W>T7M=T> zo~H8PF4+c~F)q$tC7g`U%9|LCpUUuzmPO3y9A|jIsJ^qxl%b&3(k;B@R;QUlB=ZOe z6$TLNZbxjxV%=#ogS{KH39kU6XJeAS%_&xhbp&fqDerWG9cau|K z9-!9wqww^({GxEH$ikmsZHQrbcV?Mz31gz=xgf?FTIuOU*Y{dhA zZ?Pv?g&XU3ua4(ir7h9f@c3g!HvMV0>s0MDYiLYsnJ1X5&(9|%KbOTT8?eugh-bCi zy9&v1jbyUckoRQV7uWdoLk8FXc^`oNtTQlH)vnQ%3S=#5K(0imOwK=RFgKf$p~k-* zVrkE}a{^B0SY0$kga+Lt;guNCux`*%%;uHhS~v4@!^JMGFbLH#XzxUt9^V|A~W=pucpPjpfEE6Qc!_IyZ;ON}! zLAWxRaniX8f$a$Bsqp6c%3Omo6&&kPrV8Pr4^o^6jgptf$)=Ksw7!IX1i`YtXZ313 zd~kU5jdvIsnd|KEW~-GASgk+=7HlM}`3YP!%nwMgekKRlu~gK0WUg2Cd&H}gOrZPP z^Ge7GG;9Qxhb{e1(@r1F-;133OO6p}kSE7H+}0-1xUm^mG^$Ito@erRHJ(jnm&Mgs z4h%;S)A^PhcWPDfwLiz7%}1BKAIB()EQ99+J?12D_E3wtwYR%_&^T-ydGM{C$9#P3 z=9W~D6-kLbPRF=}~n9sD|j;;ao+Ofl}& z_KR%X&X+~6Lcjx$r=FtdF;aPm!j#T;rl~q%#?uRt0>)(PGsSIC88@qv5ZqUKmQ4L7 z#*K)`O60F2HqL}6G(*8btm}x7WJb$k4UI|kPi6`&YnKcsLD(|!ktSK3A`hTz0* zCDq!CsOHk}obZ&mO=)M8=a`!V&KKa7yZ>s7`_@7aZA{G1MK8rysmDrHsY$YHcdQDya3nLe?kAcv6E?_+Of-go zR#D|vBzIRIUnCqD8LvHC%85YcPC8i^9hEt=%yE`rU~m?J7-i-xGp8fQx~oAtOnVtJ z{F0ZKH-klk!;Yt@=3p^ecjqgV*Gj8SZ6!w4i)H3f~U41b~<0#gcg|WL}T)H)lq{RmoS)Yzc6HmH;o7@3u?oe`Vh} zi$SL-{VQw)rfKx!CIZS1ugcz&TqW4TY$4G7S3B?g4JdVrR3mnvH)2h|QQ3586QCte zFq&4K9e64_+%7L$S_>FMA79>pI2y5t35T(???w?g1!5^5Gp|!+mwdmBJQk zTGa;a@q-|sWLTuZUW&^r3_lz#2Po4cCbE9oh@FFhr`h3dv)_W_gML!tBc*$S-I(4J zg`})n8)k+~uRh~Hd0!0cUuW;mODzHr@sFE~lY5Y`ctZsakmdlar%#1pUoF~6r|%6v zvQ#3#wHhR^7h`dT_<5r~>Z+hq1k)SOk3(JjBKpo|k@lZIq-c zlTH=KpdF~2Dx%0d5Ccim`XF;YyTF+Kl(7e#6`o>m1LkNso1G2nourFwotd#GXLd0_ z#6a$Fq>b=g-P&bWw|vO$950$gc{H#SZS#pZ23?Cg?MzQL3~9cFs`>?Nh{Ayv$(X{| z7UKcgq~KlfAvgon7#{zJ`S*LYIuAf9ExbQ?BlH3EL-F`h zx=j}i6CYZ~E^w4;p((!Gkc0e(=ohOw_keL%>PCu*g%KUx2I0nZ(}LV(84B2+nzo7( z1SR7Eua^mT#^&Z6*)<&(Sr|48iX3O;@YXIn+|FPS?}e2iuSxtwphwQ(At%RZzCC;+ zcwv%zBVh|2#7xlM^E);qQ|t^zMZ2Z&W?qsGKMzO8+2bYJI1f1A;H{r+4_c&jdccSq zTu#~bTpdi3z2Ag#Un~!Nb^OaO7&C{2G(lgphUBaW6<4-r@uHwj45b-~*fyijh!=`8 zbQv)&m@~$7D~Yc-TAObmYHwKWfEqQmSC&O2TsZ0G=agn`n0HIziKZ`nB#4Y{BpQau z^GThs{9kt2y@*D%`m7%WyoiXLREKa+kjAXj8&IghH0gFQh796C=#J#X=u1EDv-VL? zhlq-gewHRvzNZlzt(#b3ZSnsW(<`!k?`6GwcnNn~dYT1M!X`!EDr*!(%!hbM7Fw<( z-+Z_ft-uS>uCz{+ycTW=PriADlPTiSZPQdqP73^Y8a9H@u92_Mf|#!bn{>=8q>5k^`4L!ecKvcf!o z*G_dJmm;CQT5Ti1-FP8>#4OP$K7zMQ+HnE<91HOg@EYJp_|iJRz-zJj19g)?3(GH2+3Lr#7FnsXET3m*Uu{?JVlmgWv$}E{g4X#FeXX+q%Ms1yfcKk} zlv)78VnH7UYGm3~3RjMNx`}9xR)A(cY{KL&F|(g#fW<*7XjMO(`iH~Fn*S5y4a48h&jC6)|L5wX)rU`l{GU&rJbQeb|MP47dmzpTgUewDWfu!u ziwSJb)8uWZb(Ut`E&&7^MYsNKB>(xykj=(6-My%{qDQudpyEv zH`yN_cQH#_r z$?Xg2dkj`)OIFR+O{#79gfqY)Hqfw%hY955$+%kwGMW6Af7&_v0e!AFcYjHK+B`Ve z+&%hb-9{LuD16uyW_rdjnlKHxumJt%Cs_M!<6!FtsIvKL=grR1FB}f+Xt!~An7rOQ zNH&xG&4Z(zt#@xW50d?N2m5=64Pbo^v&<|fHd3*o$ZGeKq6VweSSnaTyHo2&Z!QyNQBx9#354~MG?0b=1;Y12WpLwXKB|sO;y>6 zUSm>&oK%w6pHrmG&hWRHr(<;FupTVco~gs2WiN9&jzF@Xm-r+@mRK06Yv#%AGD#9; zXUq#5We?urs@WR&10UkFlf*uTpSOwEZ}Yzk{nr_Me0n1Z;3w064<9{x_$Z|R@bxYI z_f_-1M{d5@1^F3i;wR*P@5GX=cLq;mH*P!D0LsQepf|}RnXWSqIN&&I8YTwm$6}dWaVDR#eLgjZe6s5i zLb`bQ#+ z)1?f2Y^VdJ-1)s(v5%C@zYx%~HwIc&njik0J#- zRTs}r0aqjT4Z zQA&;B&zBC4Q{NS(n1=MoDDd$6io`Rd(U@3;{KiBwlvfhxkl*p;ZFGKR7ZB|;AT!mS z_Ah|q=h;i^^286yU^k1ML`$LX$_^{+s$ymyo3^?@(LfjZCRscRlxY$^3as()YINke zm^;w4M`)m{@5a&w)BcO&3wCyQkl>QvEjS?({56Ylw@r=$lFgc{TknDnoJycbMmgGD ztKFhRNwIf9e8XU!oeP9{A+f+E{?cMJ9}$k)oiY;8N=B@(BmTjt0(r ze$`GCM(S_En5TUk^rA>Q<$OPKfTw#lCUN`CCHfr}iu*s7rNGl8!|GxYqQI1}_+^P^ zTXvGPUBF$>smG!zx8W_S(q#baWyZ9hA+Y3w^_jR59M)gZld((f;mZDs@k8IdX=1*2 z4C4CUhPyL`m!veN!EyCMPe>Q@)q;RBftrF`Drc*$Z-Id^_EJ$@+6lpGi|>VXY7 zIq9^Tt!`QrO*ErycGKgmtK#slUOW}uHPn9a0GW-yHx3SW_I6cU`tV%~tp>;2xb%@Z zwE)<2i7SwKt57GdUjjS%Y|!{LbP zS`~x3Xz~higm8BA(hOsnBMo>*A)_q+Tq3zRC!NCRUKz#wI{*5e!{)o)!~MqA&g-4V zc61cFZ@tpxk~Vo=K*h0%tkp<5p;w2)tafy;o$oxNQLa0vZCP-#HW#Ur4{d7I#e6-*7VJhgV8HY?&ep&z{m4M zJv`WZceJznz4z?(!RGgG8@or|!_9X`Kftq{txXJn>%H1;93Ej<+(Ykayz=8Lf9T7In)Qsa7eU!Of{@xb~B&}ddP(O%#k@UHr0e< zl8Wi!VJ0eH;3#vHm|6|3o0s|OCUmxKMThxx$K zGE6o9JsYB7qo!RvnjmPBq>>0;q-5LqS4mQ{^h;=&0D ziffn9+7Wl;Io6R7tFU|Om|Fj;3xs$Cy_y*%p%1&-4_hmE0`Q~B?h=XhmUtQz5S~61 z^|_7&)2W^mNseD9_vMu+b*7JB7*YjyZro>uk>KDM=Q|kymYG_s)v6feQhbtlLFZiG z85#Lf$XclDqk?uY(+Gub^Ry$#hVpV5G7UlM`JDi*;KM=IHpgZX5x2bNUrD5J@@zuV zBDsKn&!>OTEzvql(b2-AVN1p&{ijCkL{|#cAtqq;y^f!?#8He;1ZAF)3kT!wVt}K? zeV`?qyL<6G_J2U8)C}-E=G&V6_V7I&TmJFLK7MS!J+a@O+HcR^>lGxale6~Z%gd|+ zZn7+bzSZy+q?OS2sNRw-iSxd{a5#Ha*ZHgPU0wDguQmjN>(+beJCS31Ic^Ug+(;j2!{$JNNWp9lx2ia85G0wp| zyAiyy7r%m;dF2aqzm7acC!)yXf9A)Lr<8U13I_DihvRXl9czQ)KShl2vk__*i`ZQ8 zK8ioa=$I9+1|MQ3{U=$9iRFr@=ic@u5TF3IKcJ2a*!q*s>6jg}P&%kgmmXNte(b4P zWR&(}UCZR?g|<=9C6gumrOmvzf1rd@;s=%>qAK08KF; z{Q92B`CqZ(_Ritf-rpMs?`s>$tG&IW{pLaAf8I3?F$x3~*n9ojf4l$Y7xPYvR@U8a zY`p=Vpz{6Z){hk}?{5BB+2@gU_e4dw(?$&1FB(k zc-q7vM&c_*MPR9B*Hhk`FxM2a(X23U?B>sQb1}JVU7r86cd#w&Ar(1#mn_3IpO7Pj zliR?cX+xp?6X$2Ozv3u0qgllK#sIhhQ?x0ambo;BNvb)H#E%w!oJon~-fGo{U;iCG+QpA;{Md=qf9U^QyXU6%?yxao zV)st5yq=t^30L~&+r|-87g%ee+lYB<*O(i8_X>Dq3cM|QfEuR8!J+)x-o+$L2WYwC zrbchx?0s+F@RJ|*psQWz+SJ{9yScN=PyD(Ea1vmcM?XkO*~Z51*50OzYD?2T2DK481(DLCZ0!QhXhT@ zH`xFGZ||V)e(vn*pRiL~KiKwZK`3mQGiQ+7Sce9ZCvuHp>85$swBLL&4whZ!vU40& zsb~#5CR6ELq+=>JO0gjZX9kI#ZB%`}-f0}rd${hPz`S(Y#NTMz{J8FKG7#QFk>OX* z2MdXkWZHDF=rR?J&E}o!%^KDW!})|rAU+qSQSEFfr~ry`pbHjD5JO43^DQC*3?hu)>Tm=D^wTunO8~>s^6YG^a={45p>KCy@Eh;1iiu& zuLO`DK?!&gjEYE*a8&frE6B8nUyh&6ktT-|-};@s=&)Mv<=lFejX$kFDN9n;L+@-8 z6_ZB8T1-)YBjjCl+5&bz?`%u0#s%~smZ04XO@R=W;MBB$-A#_s^jBz7fWJ0z^V&sa zENo@rG&^>%C9{9Z-9Z2hBbn~6VXHuC38d8;P`E~TP`4IG%P+fK7Ny_UPmE04zD7Yj zMZ{26pE!<1FZG5zQ4zMV^BYd*ZX?1n>Lq4mGE1Yc2@~U zp5YU%Fbh5;CtHag?QDd_h-&20^cdSzG&DNHoK)UK)EnUbLFR;AZ$E$6J9+`OmtH-F5k7Lz=WHlW^#Pdb#D)` zv;#+(7mc)NxN5^0R;@N=y=qk}Wi!PhStWy3{&IX0?NGyYH2c|qUuR;bnN!z znc41+6wvCmq&CgyM;GQMm0Ftv4C{_Iy}fZSG10Mg6%2lVi6;9^dFutrsXl-F2){Nl zQg;3k!wO!dmhhifs{}ak;_`}BF0$jogedSaP8`7))Izp|6w6-Hy=rHIVRq8$n?JJH z|6hJ=Z8@SL0REXDtJsb3ODb+pi0mAX`-nf;7NHagfph4#M9L&K*b_n{P#bUZR@#l( znM4jv7t5||^egwNTGm?i)-2HMu-Ly`&NuF~(vB}!6UPfi^ z6-S~|eo+rKriJy3b&C+W({x<@NfB+;(!gazinU4ixelvO7FVwT>wHK~#-wVqrnGVq zGR{Ekijl`q^M~eYG<$eRQz!gcdg}$xrli{hX_a}lxt5=3TT(-?`6^rWwfr0fJUlO@ zfa>4G>ZkFy(JM#~-nLs}=3>QinO_mlz;BT677u)xXJ zCTwU;{a#Tt5uM2e(|d_zO8zFQke%J;(azfjs$rjVV06F5^uo26SRuPqv6vj($xmRRhmt8*?x^ zk-;j3oSe*RO@L-CNV0&e3`s|!h!k-Jwo2}fbYrd*##(k~mpo-(9qesxZ*3kPt?Qye zoNbbQ9X9v(4vwZKXbv;mw8wM+zrc1z*u|OTjFXtfD8_OCF`Nz%Q+dZ~<}nF5M_oL^ zJ?dx=eG_yIWp606_}J?F`q%d)ubcy-YOC7;WH@IDKzQABd0@}d0x8cH72;<&4ofy} zknf9iMzUn8dEpU5fNBN+?(3Csd{-32cZ=tD+Xf%o)P^x3u9eWTtD7--^ z2&7erD!Y#6NPM@E#LvniTlTAoBnx+o#W{a0dS7hnD;Ba}GT%^UO*-U{%&@Wi62FiM z_hm;Mli68hFkv9<0~idYGl-2rL&3+a-_DIHfFubF4sJNjlZ=Jq26QV`WQfzCx)Kw@ zFIj01t|SMq>FOo^><|qb)=cK-O};UJ?>;+MtMS+U4&le5^`5pH?Drj6J9s%)UM7!K zn!Ff|SIzx{oxOvdqhBx%h*V?&k&Omonp2?Xo<#;V>?Cw3T%mA;%r!0}r-bO^jJLgI=IN|qDNBB`inR%qZL8V+9> zT+CQG>cxT~ACi~J{=weQzl5v}2b&1zbyPJEiyb|_lTp-sS%mEK#}T>wwdmQ2G^LT< zPn6t~$}dJ`P2UfW4U0KY$AjcN?U2=g2ZG6Qc7ou4j?_D_YjpTd(oPp`I0kt~XV8rR z8DW%JcFmxR*|);&vcVC1Cb?>zdQBOih$2X=#Z5Xq= zX#Lm@X$)IQ%u7gh4o8H(PHN4$0n)FpSioT(RqCE%fQq4lYs7Q}+Ai|CeHqaJYTP}P z)^vq=yW{3zJ(2bu68^ok*XHoFNJ-wOyve~(pUVa5ESHQ%XP6C#`H)8?@lQfUAr}=r zS&6)4EwH7G%|&0qSywYNwEy3gIiM?$!PIPglt&aP(V9V8atAT*Bwl@1~uQe3$1X;@y#?A?faJxftOdEKrt4;|N1V9+ETMHEBf7tL?`={WFn>Ux4_!D(0)i zKL1{_BY5OUWm4;jjiAr;;#7IhS&%X*m8Q2GJ ze+%<0GQ$5_gr(QOMbl8aUMee%TF^1hF~3E9hOK@jvb5_#8k3$ zX+iM0v!h+p?QIoDXE1F9}bK#(nGBe};={K-`6$%$eg_4ox+s5v@=FU;$ zts-%4N6LW2E$a;tva$90JW^JIi006JhT?(nXqe%2ip0?pGaQrSqE?HvW8D9<_OJ|Gv)T5ER5uga_NOkWfue?8ub;PX$+JAIjZ;D4WjAac`8HeIW@JEmV7mg({@ zvI^ybT)T5FS5;W=--CM$M*ZDJ>EbU{`7U}z@DwA^@i&vILT&W(5&+%=SBXT@Ek}7x zj`EuKLM3kgCYCO# zEZkm+FBXa+$`@;hy%t~W%%g7HHA(`m+f<@Dx`p8_0NO`EC>{UYhGd(pJDv*w{}YVr z+3C-c_G2m`Cn4QxwVghk$+T<3#G%!!ge_c<#ORBD9klYVz2UqEl*!&}z|1BcrNGT_ zCJ<|C<`JXl>V{3#4&kE5mh{9`h7)9uD!oVDx)wobOhDd%X=9Sr7b z9oxb+U$Kdo3H(~rjUi1Jrqinvax+J%RUH>zR+Fh9D5%z+3v)4CW?M!Uv_#orK7l3k zWh1d!QB(k>Gv_npnDA(h6Vu5HFI&86eKEPOhw70FoXBT?%g~N)3-s!vTXQDTM4MPb zny1tfc9!BfNj4`K!Dd5;*--`!woorU?X*-{MG;JXzq8fc+S~har{Nbr6A6i-9IN8& za8P`&YHloJFeIhy@%wdSscnM_N%D~FqQx1=D7P?OF&qYy-`Ck@oNmH#k(G+ABpj!+ zLm3({5Anh4t;di4x@wXnp0Nkev^YW5!G^qed_5%1N;);R4$|zZA&jUDdae|qeO&YP z8)r>NEA>gQvt+E~A=rRNLAUiV@==`!x)`lvk5Bl;fX*ZkbV#=Zgv7vjX+4cM=%R%H zWO@+4=qh|pHRxJ7S5<`6E2^7y3t9|kgVk7W%0@-27Qsa&V|;sg()4e8hqcJitX5<1 zF=d_h?e8FefhOKXlml5^M_0LvOgA%JMiXzNNGv_SmSF&S$?G%UKGacDhbzRSbqLM# z<~Yko(OUP#F=DDl#o0u@T!ww0kX5zc;w3O5bfUgvDIT^TB$IKFyz+5lVV;(6N_{jG z2_7Ea5(QyTu-ORDLAX{Ogx3`CaCEdJ$&9!$9|e`zW^~1*88~LSRhn0cTaL2H3YR$d zwvnuVXhm8!AY~xKunb~J$};G7+apqq=!uv|2~27w$E95*PPG&eRrpme@aYwBu8RZf zwc^lkc^EUTJKi^TNHiy&__bGm(%SiP72WFLakEMy_`dw|cw87Ikt-oC$L_}GvhsM* zD=S@uW##de*Z$Qz@>Ld&8{4X|qGBYnIQ!zmOws(}23DOLXTW!{S%G9bfWu0X7rdBu z`X~Z8hTDLz1SIv^c~+o1-`RKw`%gh_T=HMzSXi@oZ^crgSD{!Hikpo>sO?j4R%Y1Hz5 z7J|k8&y5wm%aqqZj-;L~wnc=WTPogAlD$(k8&`ONSbA6;{nX|up4UCmrHQNa(yh*w zcY@|=Wy@X%>Tw7@w(DrBBs4c1j{2`pmP(!DTqo_+ViXwi26!ruJt^ShjR_uP7a&gO+& z%gu*BiU(l~<$N{+_mzxZC$bn1uAJXPB~^?lP0135B(=3@HcnXoPwReU$zC=}iK4_W zrml!tHas_yI<-nbwh(qsi3{hI7Yh|`v!#ATX^Ft91{5Y4RzYlR2!uoh{#t(Ng*}Zh zO9K5R85rp?RKzzx(c3wv5jyD%(V#5ntV^Q5K>CWVZ!4QOxtOpKVExR+t>So9g^k`s&mEVuxAq!&r~qBS6C4G`h0SB zUBoC(mUh5aKNOk~A=+II6mAk`P2QfC-S$KX4~@x8Wnc6R^1`No1cKz_5IHw$55 zYC<(EM9r{ZWOkJPC6bmDJg|#M)*|w7Kx|Y$%-beEopri!Z=7dxaQ`_jMpplk4?Cxw ze%duvVE{#EG_KQKOx4e+(s^2pxCX~+Oo{)({91{H`h#IO}lV1K4w52 z6|EGP@nqcXUM{Cy7MZ&7o15K06uRYxImn%g0N(zKF71*YjkO8)EoACj+r9tYZJONM zYSWk+aIovtEfTD4K_s})knXZdtr03%{Oi~sX{8FpRMn+@E3#71`pGtAp31|iI=s+l zO4||k<~#OH?keugH*XKM(yzqqv7R6o)a-4X+$5y5Jou||EU9~4-0?z^w zBZUD0()RkCb68+*XIsV3oot@yYE7|IoXJqJ8tqU95+wO){lp?_IxXeVPeVsP+1+Gg zQKuEtIUDYno}W8#aAC6PG_2n#pReb46r!A&3=q%kim?8ZjBxy0xT%UVtl(?)knM3O zsx(^3a7eVK3pI_lG9NKdF5DhyBSUXKV$x20bKdE<^K+~??sq#tGi7bA%P+{##;_h} zr^9)Fnm$j$DXT0&VPhV-N4mr?J+B4CVOnq@<gTdwtWqK74uEeZd^VS&8`tjXJjTHfF)X3q@>3qM>u`#q?mN0sucto zcrD;mRWJfN7`PpKbWr0b`dFeM^jC%zivAvj6bWh{vqRN6II4hQYjbz2@usmIMrVjl zc_Oy*=2=2k__l~E(J(*=40u|^Ss z8k4=5avMStKvJk6h6O@EXWB&zo}@kQ^)5|?b*6UvKz(v9ZzAGx_d0AZ#EoGKBEnlR z0x3WZr|4KjOfKwMKckC(am5AAG*WXCQ5D<_(d14;i!`xfAMogRJFU(LdV)c)^{&dx z;iW$dg?Y?aS;^x{kw)Z>3dOVVtsHY6d#=t0N9VH&6U>k>Zlak74ar2i4UIbd5y3hG zf-c6b7OW#>VV8{W#>#&I*VcND!jmA<>!K1Jve7!m0wUuUu@I7k63#Y+g^GkNCLo&n zv?O$uI4Bg4S_x%fQ|Vh!Nz~g4c1JsPe3#g8#GzDDt`yE>vO;M}He4bV$&DdAht7>L zAHg$qQ^Ms%sR>o3>$8AiK1Nl=TJXqo8*1s4EIk5qL@nM7k3ghIEau#ni!`Y7N0_^I zBSNxRt2cwP(2lSJTylPlu5EnS)6s|yb)Eolg$YVZ(mOuT=G*9=R@TnbmOxf^+Q2Hn+6z!;-4hlP z2yB{+GK22;ls*E`Gl^vYMud+j1C7&J5f}0N&SW2x8JY?uji`0_D5f!zh>5(l3Q5Go z*)>mr{OKE(*iY|Toze#gb$%W+%AOdu5L?(ei}7hyI?$cEf@tKP=^$U281J~-ML8!* zhO&M^CWd(R1$P)VfIP4Z*o-ZZC}^cbud$Z_)tQ)nhy^GxJu&uXaiUDOlpJuWGnn9U zXtIFWK-loKT&SBa`#a<``eu%B`V1-Z#O{CyG51RkPGGR<^pp)-Nf@6Q(_sXp4=aRT99v$qxNj|Md8&;^On9?S>?y^wyaE9iZy;fR`W^c3kmOKSMDYILLmLuAl zkVq{f-w<;~7cwMozem%Q0eVy%=i|P$$@1>4@SdSK72a4Un+R1dyh|(MxAsCPqEaWT zH%pXjZ2(XIT8?3ObZ^b#BB3Cz^7y3Ujo|=avwP*gC=i{#V&Qhn=E?mWXb7998&8wGMqda;YJ{0Qejex6 zIH<+y8tl8Y+0;N9vKIv93W9e+WGs z#Ox>q17DgP4ZP&4FTsW;tSlD^#$GXa6J{tr_A3m&V)UQ>(NCpxrN4TA$m;D*Su;^fCT@2>;iA z;oqYt51+05pS87zYmc6+J%0M=$^Thhd-C|%v;UKiME~jDP?8B|}Hl=jLs8MYc zcbsL!>l7vl{r9`?fW7PkAJI+r$H(1HD|ypt!E7^8@eJ@Gp`K&*)1xHfhcbrbH8P~c zgRDzlyaee3WP%?`AJ~etiD~R$beBmt9l4TKvvred8%wz}xK>!Usw~=BBZ+hn$y0yY zIr;(Z2R3(qNq*WqIN01h`eohPAflq3n(FihU6j(mG=^~LVG>o!FW)%W`T?qJzS?=S zbMy-i`1Q`wZsYJUdA)a#Y$p4g2S6*od$W0v?7utM-#cuqB*|fxndQVrDpr&Xuh7e- zouRpSVJ7|y>|=38hJs{8Va)(i0;}nJg^i>X5DBNrZUM58>!{4@=c9UZKJ1J#6TZmX zMXc^NvJP}WYo(q%efA`IiykzaA7SIRQaB2o_9=XQyP2#4*7|Ebd3U(UQm8xsWrDQ; zY`5XMTseE`KkIgm!)F*ts#LC%`;Xuf><5pIFGpE%o(}w{gu{g$FTX&_Bk~xjlxQwp(>l5wvWlD4@X)l8{#Pl^n>5P=ZSkyhnDpnP zkYk6|gk86abn1o4w|3r3JAKs;IiD@jOMWL+HfdPKbA!sWtgT;Ie~#agTtod5Wh`yOH8F;5n&L@b3!dKzoSp7^#%oc zxLSab_-&_uk}HWGj-=BM;(*yisR#nmYJjd-RtSi`*Mr*(zIHE@^kl?$7S(R$y#Y=P zCF`(x&Vk7vaFg0z`VIfsm4*S)ouq-olfw>8k#%kP@irZP@L(`4Xc*mWq!p7Gze<%d z8po+#4s4f&5fbu#rnGztyqDT2+!F(Ro7m=Xln)4v4@)qV50o=kzX*M~w$RkF(%ci9 zAdu~kyImw4Oe4a`S-~V(YzA))Q$qdF%JqXYrdE%-lH(<_=eGD+-L#yVR#gBPXaY$O zUhx6I3H6hnrkHk@mW6?g9+Ua8XR2Rzgsj*qmujVm_lsm9J8}(7`U)eCP)ex~1 zY+r2^OZ~jROmF!mr@pmie6JVSd~CJJS;?zFf~Be)sY{hP%TgrsmT18BM3-VDOQUDG zgns;+1jVF9r`1~-q?g@1ZHq3#)9|i|U<31|9-|$giH6uR$bq}TJ+_q=T<7!2>P5@C z;|`^77JBk{;2m%6B1`N^En3pjcvsk%GoGidrxS4fp^-9qr0g)h>VKfid^mnl)SJz8J zV~188m&j9;E>~K(pRA!y_a*CgmzT}*@e~$l4yez?X)OF^ezC6Co6Dy_Bl`jFbh1H7 zlZ8QE6rE#OcZX~j>7WUq8$Bx>0rgzKoF4BYEU0(xnpXfv&{{y(#HbR{PIy2*i#H`(-r%&ewd^ogjw$M@<-ZmA@K1?*Mz3YdyjFlwS<~keF`F)A= z?RMgI2c>NNr$4UeXvDjQX>3Rk%lLY;a7sBhiJPev(H#tzEirPTByXg!%_m*(Qnt1`<1)1IDNxoDV7ot*93zlieTYEh6_Y zz-+%bN8$TSLreRUhK9GxMtIo}tsz&3$fGN!Kr($qp?u zDZDmHcll|-#{zxs07%joK3TS;y$Rqoe^+si|fR+bcB=E~uHeA+F{&IxU zDS(`F9EgoX0F4LZF+U3`B(|J$FZj^C%(__*_`W%~v^vA03Zi=HhpZZ$>hgwmjd-9BJs*3miv3kz|5H%iwpFW-@M_`z(V7RG*iOCe3g{=v-;Rb4tTj+yKCxY;z zk?|6#WLjNGl6UNh1$YJTrFM2aK0QS#pU>rV(Ro4v#JKN)L_h-y1rSI0RAzf)q7n;<80~E_HO57KGI@tW{#BX6$0)r}_|YA6g$5(8ed-tBLxO zz@UJ}$^pRxLfnhRq47v-JK=Hk51vT0=tWB2+PM>P}FA(rRkJNPq9&|4kg3 zt+@dbmMDxy!v`EJg0O9m(~)r@Thj}Ak?)Psd1gCFbMU!F5 z8<4a2Zr;t5$O=uod-c>-dp^U%^t>b}Q;pVXv7?DMc+(@ZukbBCxV~F4fXZik6^Gz9 z-KW^q@LY;Zp2AK-N-z@m2&*&}<+DI=AMn}LQEzyEYBh9KbRLa1gmWkg4E;&U4EAh+ zZeY|Akf{iEBQPX5TF2`5!XTGIAkVNp20UZ?qa?snIC&M>znb-qM#L#PCC`%IOb|7D z)Zhv@Q?yU^6T!u+JT`PHsHJ;jDfH*a{HuXZtw^I z*%=|9hfUeFg!y&}w^72cGmRaNZ$@Oh?eA{e3$d|qp7!zm-S(nGx`E^XJBvilhz+TF z<`W01MmKR3?j|VfJAm5U6#WotyE$DCIRfJugS{J_k=ls8ve+~MXdt>%$P9uI&haJj z(?nAF$YX)BoGm33EM@qC>fk)A(Y2?}aP4r=TtQh2i&jfDMKN=jr9(~+-Ytmi_hXCewXXH98Gn69XN zWb5o$(4Q2S%ILIAS&l;!F}PQ6#m2eZ>AIOSsoLUD$&(K6&s9HmDfw$utSv)AxW1`C z2run=G9I+is*+I}8$_ZuHJiHV)QrIZGAcz>Z3IMGgp*MW3vQW%9)d~wbZ^Vrtx}(I zy9Blj2F}WbImWDCTEf-tN_km27yW{?k#@g0uCH0KQJw?oGnAF1d*S|lQH&ZT_Dkvo zvxw39H4hp9hd_A0Tkj4IclLG-ds2mbr9S6*$;@r0Azg+XA-Ue{?d_YlT8~A1mGM|a z5{x77Jg?gL(;RKlKJ?MLPL8IRlh{9ZRbpP1GtqaK{3IDowlfxIWPsyRa@$3vE3#WQ zS`1+cY{wcioHJ$vT6>Ik6oN?r#0V3gJHsr4)D6=5O<}1CNQ&U#Clum+kjUC;-(e{1 z4_Bw3qYcF=PKe^SUOJq-LeC!DnRKT5xovVCYva`xIcGD0yd3}aiKMlMN2Z{W$O?G4 zgo9OJ$dG(Q8Zf>Ec_|h;E7^(_XyaaP8gmjW*yb?KgGKVK>~lTZ&!hw70fnzWr}QSr z`EcZxv*WXKc1hxBz-Hd7UK8((=tO%HH%>1_87&4Ua`)$*y;x@`h;fI|2FhP``{=hr zR9m={pUW^Y=aUw2&TyB>UL?wPdgdgPSsrZ$bMtJCb(Fy#oXg7A-tO+^tG$Dx#x{w# zg5}f3DQrY%qv~8Ia2f&>H83G^n!jq_By3t36pf<~1d-EXY^@hp=YTENSSmO!5K+Mj zYS}8j7sW@+RL_&O?usZ}?BEQ50^43`ICl*`om&(du7V$g>H4UKN@Tb(dnm}Kru4nUF%k*)-(CX9FWFy&lNX0=XXX3 zi-oz$ zN&avT_V%~-wj1WTCTwkygKak**M%|SaGk8FVsTDHE38;OT)|vifwj02V?m3z2WUQs zt2UcAo`wSDj;9x`!Vk}#U9a?{9N3!Faly}VNOD_7f}T^#(aMZT)uM5dR<8bfI-c1RPH!0b%$x8Z6j&KO{GE@+wz_R643|DWLyUM?Ng0uER5QO+t`lZRo^~6}!2Osz?tZ|RbA|=!XQw%ZziJyaozebKO%a|a$->TwePdXA%#zJ% zWXA&$t>iQ72uvrCK z3>j>1Cx8W5@;sxRW;w)LzNApCk1u#+-jJxe3=7t))5UUy(0y6Iy_}yc)6f|$ED&7P z<+yN&3AA1ST2A`vrI&ODj=700A}6lP4fDx$aLdO4zOX?QAQ2dBhEs;}IlDxlcz@Uv zQ>|mzH^B|ws#=I*TV8ylkikKU0Dtcvp#^p@8(eU+Z2b|oHcBiZp7Db znum?u?Iyn5NLDX^hh3}tfGtRiO=swvU_h@GI5JJ70}f3y4})fU_9{@KAzjI-0S%wp_ zBRx*ht3T(pRs<9GhGtu6z7G7)Ve-iNej~LXAt*=SFnkGxpIUoqneu4pyax>7xHiUz<;!;UB+u*~vHqz_Q^$N0t!Yo1>UE>gM=>fiH% zAiG8nV58Ajuwsq2RB6Gk7J`K)<;P$tFHR7JMU-3ohUjXtO)i*MBmRp$zf<>xn&{}*C$seh26&74OOVeMYlD_C31?Qq7*Ri(^#&lTA2Ikj%k zjA)I$CR`E_bP2K6DM2_f>4@@mu2*TdQTxzVI&OctLM3-?l8#&Jw`=ye5=vWsd6pyQ zo1pIwP{NEFZ>|+pc`IeC_Euf30(y6S&JO+`S@lt?v2L#LOs1}XK$^YQM4W2ueM{S5 z0{#9TJtN@^D^H6;Gpjy`hD`wV`V(3>vYxijDHw^lvF3xEklCbu<`LTbbi%UeQ?vkd zYb&$QskV97O8Y`tA|zQBaGK#pX;u}+8#FjkXI%?S-STMfAs=`@7iRZ|MAe!*o^4Dy zhc!o>K31H}Y(Q)%b%w-w}v@*Xhubd__uRo z^ex^1$n_Y5<}Nz20Q*92K`xQ1>F33UASc&zV3JKJp51p$5p_G5M;nUEpgOxbWv7OH z=?o}4BOq+MjPAez8}uQ(kxfego{h$jaNiucD?CStikvbY)B1%4p=WO$@SaxV|>Goh9(#Ch~`UBS>*Pqnltgg3A!cjbH) z&%Gn=9c|a$XB=})!e?!_>OscQ5g7tB{!PHkByk3 z)a5zWU9p)ykRZg|#j6+D$;rv;+Ugp4s>YJWJX+GZ*H=HHbPF9}wb=ycrAj!zL z5Zc1iY@R=OKuryX`NvM11oO#xdWl$^wRD%5G%xQjr^n)ZK_I@IlkRsF%6pJi$LE$9{Eb*emyD=O(rTJoU=EqKsS9gKL@=z{tab|%r z^F>Q*S-|bs$0W`G`y^Pm(oj@UF1uWMc#Uc|W1L*Fq4_e|S`P2XuQ#XT>DFJp^37Q>`w23BA!CZ>prHvrH_Vg5!#PO`>r(2b9z)w?(Bc6L8`VxGh=hu27wbpE{kzZ$S)%BB5mA`t$Ev|7@q%e^ycsdM! zVx5G_EVqe_Abt;AEtX1+7WUN`^UbJO-!)vs4Xh?&MQWg4T4l1A9{#rcG9QrRze80l z-97jpGwPt7z76n3PnKVz#Ydew#Bakq@0oWdJE%}dcU!z!3>(B+`ox?kc}dE0ZwW{? z<%q=ag^4t>;U2|;8?eYs$p~R@)=SFly!F8_yVf(CTzGNpMKFwq}c%wKOGyNkh@dAtGTGJ0s%_Yyzp{k+b1RCL9<7ql!~g zPv+@u{O|~1bTf=#Vv-1aq05^y%p6R|D z7XuCVVz}#30CK|$Y+d`8DlU;h2TEBY;VOtZ;xOfcF4&FOhF$_TqmK@fD_ltji3#wx zaz$o^f<@dC`9RCge8rp`U6m=&OFQYoD>+jmLnf+w=5}ZhoP(r577E4vJRXVBFEUi$ z42R;2i)<_^sZuO(*j=T!8QId8J{B-Rt74R1e#0xx4m2SJdSP=g&ywJGKz8~GLI(RRGFP_3HU{+tQE^S+BF&33>jmyw|; zeW~mOny~U1u6AI&6GNeXK6w`i>j^aKN)`Z~32z`ILBk#(aX=Or!z1XolPM{CSkSO z9@{a|uj{FxXe3IcJXxn2WPM@7G!;!5RtQ1L*GOz&qoaI`5>~1KR2melCu^&Ymc^L;?Y$$Sc$p5;U>F_aE3!&d8|3sDmnVbQohBU zmpR(%CufCEjbavaC$}^`pw#8P4MxM^2TfphHn(;^ngnw4px07g<&8)G9uB&|@rmUu z#zddP*6Y|%z1NycPC6O7PlzcI2Nxj*QXn^yBVE)fEZ2}*(B_Mv){}endzw9WI*;@gl^~CVnBOo0h{pR|MyyYq|`$=(q8L|ur{9dy4RXMW9Kb28Rc|}yKLdp zOEHUY((0m3M8u?XVHS@1Z^_(T@;sSC&HS8}6w%R2=I8k&nb@dS8Mjj9wNI7FA0JrY zz6JC7psIM}Z#c7onHP6w-wIVdKE+#cZeNF_o4DvZ`z|#9v~j5ZUg*wUFOo3XXglPhff?{@EM zuc^B|aJ?~Z4w z$JvNabt*NAl^N+0j#R2wQ7>GQ<8qL7>?dt|;Za^!p|&`LxssQPA9=4DN*7S zS!9!z_%B|ioMH+W{NrM($7 zZIXTGDC+@QONWvJhP6ELbPl7+qE|JAzG&uCly-HAE@u@zkqizDBc#mBSw+MYuI2M2 z*VKKnYw?hx0#8eUbIzA5q*2I4@ic)di{??-bN*zWt)ZV-L7H+26&qx$@_6OHI_S*t}^9$S@*L%21=BXGtZ1o!I_^0d9*njx+Fbqt+w-EZ)Vg-O5F5A zRwRJjfjKZ{gqe{mj?bD7OtUn%BUNC% z8j2pKqteF3Xo$A@He;8uTyXi`^sq2p7aw`vC@Lstsx0*t6Qyo9W>jHz0|T+jCW{q@ zw|whrap=v+GiOb=LLVW_t{`o=yjaFz1=?mURP_%^S&$s3I)AE^0IXBoYtR=xWSm8D`!6&$ZCUvg|c)Mb~1<7{-E8Fxt9 zZgwamTWqBF7zEhZtSF_Zw>J}4HT?9iB!Fog95i3=ylFId_l}yc_ulPpzprg1jorP* zE_yAvLYrG#jl;v{c4K#^!DZ-)S7ztUo5uH>Z<>4i?`z!3(c!!OAWA;p+)#Y);Lp$L zn8Wq?tPZ!d#D5I4i3$=47YoY>z6j-8tlx3ENNBUdR&v2yh!lEW*`QwVbx>})* ziNs(C&+8qsX_RuJT&hD_IehQa!Ao-ORz@yZDn`wMB)NJ7oRukAlD{RA+nw?Xipm^e zcRETZxu`zM((N_QvY+A6^Mj0^;uxL$9c}0G%VOA?Z3OB-qcK>KkB2P|cU2{Xh!7~R zJKYf{Fg)7XrT~{C{Dzi6e(Wlrj^O=^W~q}MhB?^ztj}U_>XJU_NDR zG1=J4Z4#$bZc})F$F7EK!L)YymMk}HNRe4at1@Oy*p@*SoYN%Z%?&GgF4+$yeFcF0$)X!A3(#y7K^yaVh^}&N1yi6q8_#tLJIAUL zan~p$T3f;Hm*`bWRpFnMs2YTcT6T^w#4!yENtbR~;A;e0cVX@xt|SMqw;rzkbyaDW zd2E!`vJaPn-%mmQu{f^8{G-P0xP}}d1rbiMo@9~rB!}4r9&gZ!2$YSjIX1FLE76vq zNEn0~x5FZ-&4!zuDL%{UCZgurjiP1c1rWu|UeLMg zoPm-{!e^WYx2*pb2OZ6#^!uh5*c9&uNb)XFFwPu> z)}0yBBJX9%2beFWp`du+uqF$O(ii2naB+z*vk}YCDX?$a>U2?#K^&QMo!a2l3Q<`t zl8=}w&eG@LC*%8GftrFd6(S~$x$JWvM};u*MN`e{@l<7--67Ba5u>LE@bUHmJ1iM* z#H*pE@vu{?z3X=<+%RUcB`$xb4dfdJ9JrSFyoHnS(%l9Q-z1tmQDns#bE&$Ie>alu-#FUC87OoPYC_6m%wpa-@Bm!Zl8N z$NGr0qDZRwO0vbsHL6BEQ)n{h8ET^3D`!lVRJQD_T@hE-^Kqou(;VB%u4LUi0Hr{C z(|jMDZ(()JPzT%YO@-M|*<4yzDVYtiX3HcsWyfib=blRllM#DXvq*O~Z&#P+6x}he zn(CCm$4hjoA>GDlR=G$^@S>tpiVx;Ajed)>0}HyDoot$5_OEx9{p$t$*PF0^ed6hwxynu}UK@5X?Sf=6aOsBDTrQ_HO&ttEAApc!cH0$Iv)oG8>Xt&9w zrsh7zvdbzfbC3X8br|M9ZoGoSyScr6Fk8xIm;2b*BdEx%*H4Aaal)nPcb>Msc2z zaHvV8*g1ZHbmlVNi*3@DWfZYelEeb#kfkJ?q>%O)y%S!x#;)Px>@x4;aXHD(lY$5< zvWtTP1@>a6bdPZw&cDgGusMUgFk)21q$wT|?k5u?b0Q0qAp-u;sf%5T)7&={ePACmQHsn0lt*a0*9M00VU5axL4OsH5zZ zbb|qg0p;x3Od%UJ0t_ zBLH-jtOt+1nT1Ytt?In8`BZq(uu4#bCkJo~{i0Wzbb#nIXggV;q_c=&P3T~=%A#jx zUGd_IzthqK+oSqTR8%wzE@5m(qtcqALlHt3tV5a%$9(~jW!c<2lvY^MN$^P#Krnl_ zEFM|L*Q=Z7AB;MhQbu7D-Oh(B*u-7C{y2oJH6Ed{QZa6!863K7T`mVk%f-~%Hulww zX75HaP_G}sN5o<&;-s5AJ@g6;2I3+hBnZhNe)(KqkomFp9@SQaz=W#V+)Ai{~6 z9JtilS(YM^DtInz48zR+(AeB=95mlFc41`CFrCE6LnoL;;=a5$24$3K)aW0}jKJN(Qi9bmk!& z&ZDh;X8=Q(iRq})gkQbc+xqdaxet@ZE|bTRWuq4s<=^4l+H{+{e1j>MLJBlB7ZzKZ z!xYDqvg^NF<{pgpELqXw!^9GLSBz(<*^K{^Riq~>ONm)_J{VoPLR>0kuN(OId&9&T zCb)#9-!VfxY;2jfHn}zBn`Pu0aN%3qg5OP2=pgN}pX!>Fcl^gBK(A01=z4)TAAd^Z zsCWI6hBscvhE1g1j%kMD^{k5B4EL=z{f%V%Ed?rPTj8dp_U5eJv@x?cK^tjW;3l@x z7V?mC!YS804Eit4kv#e}a>U=8?kUwbpzl%}Vll=~Bya)=xhjbSvu|Fud*YlFzA&wr z!Y&_Mq@F_~Qf)Ux9h3X@5;tP~SK29Dt*|`dKc+89%gd%THdt6c#UW(~v`UgsJYVXa zj_c$Br(-m89&qo_E#a>j@__3~eje~34mK8y<@=_7%K5%M#1PH*jTLLPV2Q4f?3)+I z(+NI@@^> zNvgqpY78j3HOhq)j{Drq=z6A+Y~=nzROB<4kx$5s9+a5UQblHT_tG~MzH!a$(a*Go zXsBl?aifrIBS?l^F$ZY^;7c_tKf}SU@`8{MfPc5`w%fDCD1z}7lLm-zn316`vbrjK zQIh;bo=i)|yQ-T}E;}@xCifvAh46kNCe9?b67JMBlPrvKZUarWivdyvNcI`gFG?Zs zoB{#1Sc;fLr)V%9HF>m?@K<2-S@O|H{A*4}lNS88RC3@BLojqo>3Yqo8YaOectQeu zgtAo@8YIakt)XP=jjv;A?F_0iIrkReS}nSM2(VR(FYp&<@<(BMHY(DMDZk!_Wppih zx&gE>uER17in=lZT)BKX%ERv7t9P#t*-M;rOkk24O5MnTU@?KC5?)w|pLmFo+5mu% zbB+&C+bg(xFfPtekOl~088=3@Z=tgSi)ww!4OCf6ci9dSJU~k} zW5W#WMe>A$dz<0fz;~7-x1f2|9G6i)P;o?-(JdG`$r`zUCi9&U9wt zkWHXP?J0C9sjKh4$A9sZGw5 z=3SAsGFMv8m8QMz!$WE9L(}p=d{1mcW=0hBLV0WBUKLmZMDk98EjeBBM(# zgF?B{@xYS~+k28SvD_IU)_^JO^)Xp<=QKU0Y-%GkJgFPpK-CX^7W@2huSd2MG?Q~$ zSRnd8c1CC~lCTYk$Uxh}d|+)i6p&Wr=qpqHgc7_A>1%)!*3~0F#;qy}h@n#MLG~BM zo!*#U>+#+9^QJ>v0@~_nxeb}qD5KO2-gGQ+JF%Zdzf}`1MAvXozF!7&D^(_9z)N8$ zLD=W+cH7G)4+hs(t0UC(av+$@&``l8J7y8VqZpqGR7Ezf9aPAI4XU zzi`TCUI!STy}4koqOAd9ja_maa&U>?ny^zgeW4teDY-;_rXi9`R5InU6t_}FRFN)E zgi6C}XU(Iv(|p!U$@AFlH=03jHZg;rLC=>mgk0a9QkE#Wn$DfG1Tu!Gcj==hn{Yd! zRzqfIj88cdg9+ut_`bt>7$@=^(r1B4*X5~-AD2^(1OQ}PD)QtamN=qxN%@(z4orGP zzM8m0KZ~T}fNLQwV%w|$siI0`_(dT~G3DPLph^;g2s|BGNKpX7I}r+!2nJk=vdE4e zVe&=^ZIP-?twOqn8$Al%uI^O6CgA~K3t;P^&|=v41TwJ1rC2=gq*~4>Le~=X+~cE zN}-L*A%X*V#>|_wB1%DhZ7gv`)}M~4XuD=Qr;{U?b6`|yF9hi@Yp@g}2LhlwC zoN@}70Ck!<6#m4(Y<}Jz5gSEM*9}6em6|n}VUhT{as=*Dc?F8Aew@?*%-h)rE@YUs zct*(V*C_)r@hTpk@lgr`jW$l$*{-vWG1N0mBaPg;LrR^u^%RQ!1I4Hgvg!qx6B) z{Efj|Ai^%0mMCeMmR`71q&MJxQd%=+cptdyy0oa(3Pbe#VW{Nhfv>J>eK-AvVd+S~ zGD4pPFO9Tb8i%iV^aUicO52g z`r?j-vX65mLq8RwO~uCfn~}5{X06VEctO|!v|o1C6ePVUvg|{sUyMMM$rLa>t}?9= z9s7{7SE14Z6E+)E?Z$5KQsAqZL!ZqQm@*y!ZFs>c5t_|$pRACt&eoQLp#`!MPtQCl z=ZVi5SmCy(^7F&H7*)M5lw#BP1pTi$UWFB&_yulKr|*TnIO$vv#FvZKWAlH&1I!qNnO?RcljHx(L@-CU|CARN0=|lhZC}}X`+zsT&5Wd@R);uNIyQbc@lNJez!5T!>;$y1B;B0%?*0ef*@ zWBGQ<5s+riO%q?j_0m2$A3B?Nrmh6@RcU5I+OU%5DcJ?w(V+AEPGY}B<8DiYqKVPB z=`i&3{&JL#@raml|4WfDv(hgf@iLim`C??tM4CkrEFQt3DT)ZqaTSU}i^eNTeFdUm z!o@s6zWUuX`KmooqaiRZs5&@JhsWt@_B;@`V)f<^+x|F>{N_pt<3e0$xI?7^jh!uL zxk@^a4mwCR6_w&G;r^mLsV8y785(nBuFk&$9!v zoIRF}HCF}R*3f_Q9uz8!RzWGPnz@Eg}ZZ%+zA{vdX! zW(-#C*Jnj8pY2e2!Rq|;REk_4s#q#~>Au{Fy0HT^CB${G_}2(=eWNGn#^PM> zQ)~rVN(}4e5R~{K&!8Jci;Y@sA44#s)3A7@Ur_Xi=MNrWyHZlxgjN{)8DD2-UU4n8 zD#hKhBX62kr>R(xm1PGlPIr&kRAdAD9q3u3O5(7!F0ZBu5X55mJ*%)0(`%J ze-SF(`^V}%K3QQr5KX5O2z1g4v6|56^k>CTNg zv=4E6S!tjVQ?l5ws#VUU-yp0i9olP0RrUNYBWKy*S+}@^?`tc#Lz1p=BiVf2+}UkV z)YX*;uuckzuxKmMexsu&R_j(*+u4Vqf2vc8x9SUfy(-nF4OkhOt)1l!#onI~Ro@S& z&m=}HpuYIIC`S%{5_7#^$%`LQ-#RUaq3X5Raz1vm*G-wKVE4YM^?aOdoUs)hGQW~e zw;}UOEtX|YD)@b+&9$wllv-iEh-K|MCkjnJ;FenS;~Qqc=KY?mUaYRxF}O&NEy_XnGA zm6SiJ2sABIPz5czD48wIv>kbQ)f}%dm076VhiC!@mwwjeGD+r3_kq^G=+p~leO=m- z`T4}^#yk;kHCakCu`S3#avSrpL=Re_WTsiM&EW$lQDfgE%`tTpTpirgtV6ENuq8{pBQ z`&Ty1uP9ML&}&jpI!lLK_PA0_qld?7`L-Rl!V*tM-g?1Z^dx+>gG@msKS^xvlR3)z z;Wbo@k72gqQ+|ZsoRujFE}Eg$DMt?AR!pLlw>!Jdz5Sz|z1?;5=7o8+`E&5f#ud<+ zR(7{G_pux_t!G*{&9OZ-oL3+e7ihb<4NTK+1By~B&%LLAZ|rXG9k@3nUgZ4asw0;@ zgXJ`S(zy86wneG$HjKCo9-2-f=NWpdzy`t}riGU)bT<0o6#_%L4S{@yV}H>^<%{Jrhs}dyDj>79&a6tw@+X__4^t zaKd%f@`m%k^EW`yP?JLznWcv{s3*>HPK>;%E9xm(#9dhBNd}wn$=iuf)xz7aiK(uL zY-U9>D${6yXS^I)Ge9|;@u|gO)yj!M_uD&%l>e%8%Z{BrqXO(7QXK7 zj#JN3AN7Ps?1mc38=%FxtuPIwZm}d@h{_d0f0yWS3m_^MDeB)8n7pu5RdDoKAmk|@ zWF?QyP3Ts#WHYIONQzX!5VRtR4wF>_N7n`)Xv{fS%>prK`J4?;DHPplH$TP%;mdGD z2kQRD=t(dP9`tfC$XXbqUjl3j5o}U?b;Kr}k^$I|T$8H8rGqkeWI5VoCqB$h(_y=d z8`(kDCssE%8Ot}cb#)M+t92N6)%o1!%xH= zf7XFZrOz4obOf^hb0c|`L(hk-0r(F=Ccu6j7c*g#8@Z7j(EvR1g#(Xh2^^X(;XBH+ zw)#@#H1VizzkQ@MJN>o>wf%#=pMN2;{;+X$w6pvDVRLu$ZG)i0?>bib=frwAMekN z)ak$-CJL`#?Lce%zK^>>hbbRen=|o@^EXO)Pff+Jr9BRmK;@Ys{qM%|c?e6-4-pgN zB|XU!`Ty$U$G7tTZz%u& z>#h9%R{sB=FaIw~>?4BxiP`r${VQb6Ba9jo+V5<=-4Al$!AOO~qt==(6CO0;el?r^ zYi0i|lGZc{GEO-HyedLmF=`e_Fz=^Bv>^gU4~>|S(PPn-v!IsJEQ2c*Z!4jkUuRI9 zwTI1CG49p&i)`G^m&FXr1mnG;P>vR5c;U$Ma*+49ap4lr)n2dFrHo9E&4QrQwWmPR zyNSfzL1|#dUlM6BR!Bo~{6Unq=$f2p92Yw|0rtc|dwF+%bhZ%5(8y2b|@c z*B5$#(Cp7ej}&2#^->SjZjyOFbL80HIhLK-aT+--x`Z+&?36vYBW14SSE{U zLbgRXefbdm6_N!&Gj~@XU(^lu0%VM|hd&=`)Od!nW2oQtEYtc4v0^d6NOKfF6-{d_ zo5i-&(B|?>%4rWXz#%sqTR-eI4;ugTu5ox2nGyLrkgg0X-Q{RTsu?%7)!3+XQ!+La zL!V=Vx!6yf`J5UhSAHy9WMcZ$vXP;@O;ZE$r*pW!w|m%FHzj00d62=QNjvUG6=v>E z#uaj2+|1}g9EagvM(V|$rg}2(q0f4v4z>#?L>NK1vI%k_1~yB*+aR^LsCJ06(eJ^I z(bpNAdd1R-26D8u|6s@6BFfz9N*9P2JsBk?HcbXkfLu2Wn#JF$7Vi3f6HESQ=Su%y zmjB%tAhf=ml!@nb+lFLhXMtxpCVWWl(@&RL@jV zxk{1NlvlR~+g4Ni zrnar-_U%o%mfE*>6%D%-vwIW{$13a!bjsM9f%z7ln8jicu4oyME(1sY9V`mDPy5Hf)U#LU}b z=C^<)7wc@^M$c^&I{v~AZVvd)(4I#?59?7K7VFr#8f`%ZmH1x8b|74=*V(|I z!M(h}wVEzt@%(k)dfcvY0ln$(>ma!qOeG zU5Kq?cwbbRad^uY%@gW!Lat&)$mE4~ATJ+99Ws4Fv0qo;lb{M(v}WEslnqu&2gN(@ zZR82rJVJaMJU105!c63og#%npFTbSAXByh!$fo+o0ti@dry_e?z9;xFnAdz$sgyOX zZk7&JcM&?LP<3ZfFyMxT_RmpsU!(C4LBvS?rqe{z!H8?quVzIm4C1CR5o1CIbijPc z3E&tsWOSz_rUp1FAzs!6Iwc@%5{Kk*AG{VoAgP3~C zWfwc68#v^jcW&*lsbe~;mYnqub9j;^WQuDb#4C;6qxA?9a%EMovTzDLxIUZlbFb@N z>-=*w1_KzNnh67Z`trgWOJY?@Y+_e=>A0I_6h)?;{^Yef_LB1ao89EfRKQ;7GV<_d zSB^TytUXL)VR~QkysoZ$(OdKqjcRL6>&q4EZmb%Mmprx%@{-a-=S4xU3hU3j|K2th zu#3=?i)uarI_RAIDXxS`kP=)Gp9Lv@4j3^rnjJo0Wih>V|De(lhFR-(ZkhS`;V{jZ<;o|tkWht(DQUKquq4OKDsk_ z8hd)u%#hW(g7tO3U1etd^a^I#o`QaY(cIIBl{F>r=x3eNvttz66nVGP>WtXrxx(Ku z>h6g4)`v0cYBH_fjv7*1OOz9%9UBZY4C7W%V#o<&;*>6w6d5flb7TC{m!WmjOV|f1 zBH_tn`KB~B(f`q@!=H*i9A;3Zay#EKnJ zu6eQQbZ(N}uG`8b{bR$p9KTdgT|K*scH&EY`KICqgh`Ay5*y&$3k_?klEMVzw(RV^ zYQAagvRQL}jA200EzKnb#t1GyY#-P{wgom7=U{loz&+NF5iGt&@^Z}i3RwqL_~AQE z9Y20&(3I`pGxvSk+&$cTv$M5xa zLw=IQ0%mg*NCI{Y$^0}L<@xD)xey<_75p+@8S}5dpauzsesOX#bB%{wWA)*THP)W~ zx+YcN-;9+WJ=B%1P!W22eC6Jt(i2;0ZT2BP!b(_h`DOXl%nQ2qNbP~k4`uQSqA&f< z{!=Y$^ljcL9j1R<8B_LoI|5_xF3$r%_2faujff7qK^>tBQI!YL;Us^d_@!2I>v?)3 zem&D1dz@KU%(W-<=y~e7^gLx2)pP5)Qbx1TP3I{q)jel}{m@Hi!FS53%2yV3A&3Xi zM=y)L?Y-wQ&zw6tco$wG!hlpo%TS z8TiPdsqE&mFNSQB_~Uze#$OHzT>lH!c9q2vyhE7Dve`EcPN!BpDr1R53N#0GNuePZ z0nVn1Ffn23RUl>)65F*Fjf|ZH|H`mFyoga2PgYlB)qP4rs+MSpicTHHBTKhr)=-li zwo!F+NwMgRgZl|@dvDMgxKpnCmg^ZUc55Fw3i+yvK{T*!b?38%U@ zmKHIA$qi1P(61s=9!@D{~x)QJ*6Fsb6Je7-Bw02VcEFUx}7Z5JblPCIB|6f6_T!#;hXUm9twE@W12ycY5h*_TYy4!1Mm>*^}z?zZ#zZ#}D!R zKe;{s-**4A?#WFAfSQ8;pFVr~w1oek-Nt|Vmj1ubZvB66{eS;6{eMNSsr_afZ(b*a z@o?U~)Trz>lexion9Zs8Fj~s8MsX)8iY7gWbv*)r0!Sp=$zdlS6G+-p+-^b9BiqS% z06>S0E@UgGE6H0F>tj*i2G7sn+`$sdTw8gs$7Pp?83)TZ%D%MQ8C{lqnj?Bx#NuV8 z=QwSl#4Vby;(LH(eu-QQ8C-_QV)`OS(=H-3>f0RGp}Sx+@pX2ADmqlEO<^t^99IYI2hprmhy!T9osp=+8Md} zqOD@yU*11zzS-N{HcO!6a55(D6??Jbh?q z;Mx}df|Ge-H%jBemLSzqHac7PSJ z8Zca%!7zjM! z^}82~*e`#@Upj%gzwz~1K%rE3z*5Ww|G}i1)kRTF{V$~x6%7R?Xl0uRJGU?-6uwsn zme;Kw48q#wmO6~YeZm!WVV{EjuN+mKvRv@VBco|I3zid`jWq*bt(zmqV##o;!qr`` zh}LcKsoZR{V$&IX<_w;U<40ahb1@_tT>$njpz+IZ!2j?wC$YWfc{4ccm&4%g@ERfl z?2S=J-W>vxzhpw?I0O8IfD5!CoU*QHt!ye3SyF>O*%L+@KFDOHl)8(e{-WWm*`E1~ zEIhrf(UugMmAx}`;^vZ+nJTVi%??KB{iar&2;b_-A%BTSAd^(Kgi&C5 zQncrtj$G}8{M(SP6LwCeWrx!vgX5odUU>Z&f3C~pb%z14^nFzfFDqCHX<&I-yApEg z$VH;hn?KUzZri(4sH#L+vt#I+V1AK>pd>WqJsgzxkVdDI%Da8^&`g9j5NcrFR8e_} zB6#2+Yvsdsl3ir2G3msik>O=B$Vm}yF6Y6p^ASA+$ynRD1XRpr)WG-oe{VrbuM`4Z0^x10|B}&^*2he=CE*D`*nw zChj^m91a@R!4G(pvF)sN=Lt!)qpUZ`hv^W_6WiJHFgt~V1IJ5lN|OaBP3BM>GDqSM zKoP{8wezg#-^2K?fQjHFF~tfv#od_QJRca|ZQM7l^Lm>5n5nw+GuVCWtCPGa5g(jn zgt$-_V5py6Aj8~;p=KAj!{i?YNCFWRnX$&OW= zXW)V!jB!QsNDge+<`CpwToo*3tYz_?Yk~3!8jgdfVFsS@aRjoL(|l(FU9tf@AEL>~ z9IDl8W0FF0mb#y;C6cPJL{M?LdVJO{;h;2!IS*mj_x`bZZ(Wgp|3AY~aWS1t(PX8k zE6Mg3vTZ8U2G8anbI{^kasrqwYpVdgHV(85JIccO!FrjJ#Q%70-{v0)b6HT~r88Xf zhIp9aqM6(>CT_C{`b)H6a5lo+>(#IwkL5tj9v|hjc0_p`8?`J?&z{8#( z^s*2mITUzye801iy8aa#*h_J3GMJ)iZD?@Er*vCx=)3dZDGK&v{!cUktuM;|J%0H3 zVZi@AUA>L}e9Qmc@_)Dd-`COq61D5pxmLdxVFs(1ylQ*+yls4C2JV9> zZ*=q(dWiv4xD@HhY)nZM`@5W&xP)`$G(g&8au}IQfD0pzNeW1dCbk6XKKvGRff*D4 zfzHKgI^~%Sc_P{un;=uPF3#JA>C`4HV8ZLT?x|8)L%2abpc0H^p9`)Vwog|TdY$%! z;!bCx>!ANtbsZUVG|;2*-JxCe6>!~)y8O2&>LQp)9miLv4i_g9xiZj~dc_QzS0(i&$Y=eZ!?b z5Y(114bR1%C;!TZz@1nDMe2@!e_d4v)rIoGRNT1f+1Xn-#2cfl6+hH&)#v?ZfEV!WB{h-|9n=W z|8MR8zZ(4?kV3NMh7cMLkW`4iEXdkTz8;S|T{-d;Adzx2clzm&jWpV1%LLfuw{eH5 zIEvLp*!`nW`x1O&rZkNc&C#k!F^s09$t|KSGjCRo%;KO)Nau)gQ9}1Y=EaGl3$EEr zM?(KMo5=!(islRvi+4=pB5@$L$jZpLhh$0q`k5ts3oz)Bq#nrurrV&sVHGf5Zoph* z!Pa09d}O3flE&LtjcpqdpMEVQ%?`Y~s3*-qIy!?tB<)x<(d9W0I?L#S2_*mZUs9RA z;TV2>zkbD9-K=ja!O|5yT(o3vrN}sn$v@^+hB>zU40{@eV#F= z-p^yZY>X70nUX(4Bp5AoU_;4VU?dq@WwK5|%ONICwaTKXpsg2(JzCUGzU59IxImf} zg<{d}d2u$9`LJx~z0rN4HK?(cTv$Yz6y8h6l1qeAXYjt@iCRiJHhSWa)RVB96n_Qp zE15fSJ`O%jJ(k?K>a;>9uyiQl8YCzd2PSHPxF{44aXl+SCv`+WU(N@}HT81nbI?sk zXh&YGEK*@yBYQqnRtBYK5S^iVu;HzMghUqApfR`dNdQ)sJy$7VF+c3U%V{Y+a1IWq zfT`=&wZg7SeHG~KtlG}lT3QIp;$xh1_Fr?NH1viRSLhk&)~paI%y0`&I>Tb5Eu*Vo zZ1w%RZ+OZG2AN{fj729|nj;mim|u`ebo$)v)IJ%dNwB*XLOBkLjU=WtDNv$$()Upm zK4{u+3(3yz&QYjAalr4Pe|UPECiLHg|2ye0bblTLcy;@~r?>GRzpeh~Hvi|X{^vhF z|7Vpd2hbk=cLj;VNk(=SByza(y0Llm?w|p;qI_%ayxsi10X$&i^$t)ZCjXgv3s388 z4iqt7uqgOi#(~guDAh179qP@MT;S|@rdbI>50qhz^0e)2H4c-dW3o$!77&M|$ICBY z<@x9^8)7`JVx6l1L~p-o0Ce@+UF2}y?d%@yCfNm08c0kwkspA^0Qm=NPm?A4LaHAg z!J-~wCzcc1909%>jI0w>$;*uj!^n<{<(EkJ?Fdo&dh^X8Y@3@h4@$!6DSI)i8$#MT zH1b=OL_?;mCXeU2#gHZ>Q9L3?w}DLV1F^W}919Sh2(*A4R}M)y&l0I<>d@*tN|C3& zIK?{XkaQZ(LZ@QwsF+zivsUx|)=Abm_cTHeS51+}m$fAs1Ow?c=Be%XV1~->?p?oY z=zinZ`xA0}Xx-G!5aD%reX}+o@vN1iOX7J(+A70k&yA^(IhPv2sH$*{xCeicJRdEX z@WEE|Xs>y&<;Gq1+p=&m(QgY%&FZ%mJ>yfIxNhc*-yYmAba1U%UpOEU`)CWlMJFjR z7JvTbV^WlT`!nK90Nt#r?f-70b&u`2=Ad3XZ2@BV z6)SQ4X!n*f!8}TUt(sd}TACBXP8a|>2qHK6?hQOk-a7*Ts7s?oKRGr20f7H`K5Tnt z0K(-_hZqSb9Tr&=zXwCmN`B%Z-!|S-eVRDgm{KKKH~isZqu}fk)4et<2Z*WDk z3oXHLcDCdOwuAA%-M}fnhIx-e@=72>hKod&*73R6#a8Zek+;m)iv)ltQ|ii%%h&3* zh8f%lO?RNl?3hn*-K>xgF(UJNv$wT*w6nL{q=lh=k>NBkL8x_UsCk}}8Ax>OI3j$( zk@(H{tKB}!$6CZgJG07vwN3RwS&=okf6aUEbog`WN2iFL(=1P`ExOw1^#%srj0GRjXR;7gK*R-u4IvQ zvzBi_6gM35pK>*0%aCyVBS9sYY2nn_=4@~rIl$u0ENz{YEI6!{yvUL`QNw&f@-B)= z*Vs1p9i?u~=xQ~;*ZcRCb%yK!B{vVowt;$-#Km_Z)OS2Wzxu%j|?-uV}6Ig;9 zDpii}L(mvMem_^L-RiZ;nLVh$b}6oZTWnp=SKN<3`DVADx!k@bZF#vT#ecYUDwu`; z$Lz5;A^@1e|36xLREq!q?9tO({{NQ$zvcgL`TsA;|3~9G&C2V0X=6ivA8_ZDIDJWg z7Gdj83|H?zx7azx?^C+=2)mE;99Eo^zBNM2bGQ?-eeR>m&KNht$v8<{#zwY8-=9#? zg~Ya7Mc89%IcO>$_ITvPCtC7>9xdEORsZ6gOWw)k68~zRom0SA-B)0ZOU{N(nY?4z z)Yv9+Vsmqz22$88@^!w*j*H3paQ)b&7ovMCud`VMbe=-Bd2+}};(%6dp_|lYurg5Y z)gdhnHF&pOULV(6bUn=A!v)b5ueCI{l-0k#{^#Uo)IZbhf1if_pHCh?TDztHZt1^U z`tO$h`{wlDxDUJAo=OvoJc2@sKH%!tX2eDwI*l}NjCAs zPIz!mK*JNM)po=8vk6xXJvcMzsp%CV^r}co5-2*#$hlKd1(LYgD31Y&G*h7dnCyA0 zn-|&D7t6@7PFhrd>Uzk`O==oFW_;yFC`8PRr!! z#;h8hi(2h1YHuj?IQ}lVu69oFSCe{+(!ovY4^wT!9~+_FQ(jTaMs61d(TTZ`x-`nz z=<-YHT^vU}zfrG5)N8a{s`FlAzfgo#CGbRf>vN1ge<8BZ_vB>A6A)|BcvJSJ1-4Q- z3w@tTTkE9M*9SLWKwSlGftb2L_fz-;CIf>9q1p^r;Z3Li;rZR7o*e!B99HA#=LY^c z_?bWP$9rSNIvc;{_!d_fYcJR`%P(;PSu&sOHjcI%e{b&XV1lMqK)Z69=k0p=@iAF{y7I$#`JpN=448jb z=EIij$sDZZ+~mSTSNK2*J9zmCnKB9+29sVM4`#yNAK^y0AbzgdD{61ki%xIcOGcd@ z-NN|1j&x10P{n1nP(55{IwJF$K9$w`w-`n-;>fA4gXLw%wK~=I(azh( zHjrHFl*Bz7EV!V`odQO(V52d?55PF5Xg`n+7gWq)da;pg9UZ)BZgKOj{jJU2t;QS9 zK#oCyd+iLBOba&L@1Kn`p1}X1J`n?RePQ|k+T+L1O8%el_?G|shWy{tTmJ8s|GVY? zZd(3dWFw4dSX{w!ut@uaglDw^tWX!A>0CBhi%@A!=)bVnLX6eiG$9}<^`(tk%KT+= zqzDR4qfopd2{H6$XZOeEPn$bO&9{fi+7q}SB-r=ac!Z={!iEf38+`~3JyOW)COH;z z88%fx|HaK+8V^F+C~xK6LNPHyyvRB?O_9-hbg2VW5ZRM@V5tsgnsRD;t_YxUhEGaQ)69jhzMzYp4evC*Y zP&LZN3pXEiGR$Z=NJ`L6KZJGp$k!}V1|5#6!xfL=AYm>-GaydBN?>j-ehHkD6x|fa z84{U5Ma~Qi*CjGkos$mCfsyFZ>!bY@8xgAL+il_;CPofIiBW)Ml-0Rg!2fN+ybd1U z{wPn0$Fk6l6+D=*`-xt+;}a2%8S^~~GtK5!3u@=IY2bBW$Tu3bHr--tYeuAU|2b1~ zJlDp;v>A=R6q6Z}J!&8`wB5wzg}zUDIAq^Wbou}YGk?witW!JNsq%rEk|RbT;2~X> z>F7*F!=yU}9sy!iZ)ZohwgJr3qj0Btu90WyW?Ygdg3bPCjG08vQVEL9GD0bW+U?RM zXp>W#LNDT%ZL)5jhXTT3pR=;q0o%J46E9i;U}AzBi?M%DM(NX8&>(wZi_zdzFTB0J z?JFLSqr-RmrR~G4GZ;AT#s&B)JODHzW1J#NG+W6;FYr+Pc0O#j0ILqy= z_xHxZFU_sJ-J^rOH&qQ2RP?g`cuIp5`rvH?n4M4hI)C`&sdCte5D}$xwz0{C_cv)~ zWSJ?Xt{5;Y@%^~Cwh>Ov%7>kPfwTKWXmy3N@U_WMZc32Ecx7cJFh(hvpm;JwBQW&n z|4LMeK?JZe@~33!9bO=!xY1Se4^qgGLnocnF|oL`fFC=lvp&+p6q})G3rHN+!lH72 zKrKLjbqt`A4ZOdp%P}m< zzsfyMJXCu7V91OMX#i-4wAJkZ(KP398^*t9M99^VF)K!e-&yIP>5A;Sv=BcX$$UcA zPtC(0clK4d{i@z9_2@cUw4wJMki259mGP+AzYw-?Ks0DUgYLxGxEt*JVw{BNyi(!w zvviapVRfF7L&s?cg*@a}XDKTO8J)q$$K_qpe2akz?BVfiK3TbGe3xdlY~@dwjghkxn4;?mrbb5k+EOYr94LFG-r%fV|I?;7ldn zPNxsuw>h#c$#KU6A=8O_xVUi+xC}Pe~Ws1e~ zX|h5KP0iGt!rnXZ0R=|ca!5q(bgxEaHf<$9&Pfz6Kx&Bj$@}1* zz-8lg&sj)eA^KzTBa6>e4L-o#-eDvGxKBzK)|cr&-~N+D#-DHgInDm_;gd&CLi^80 zxB9v@dOUM7Xs$#D*n4nuLslyH^V8v-7DYeR0mqZRw{ix42(*M0qzr!V# z>{q;H0oF%NdsRZCL$ouoMS$M*tHU*nRZ8S6iy0rx)m>fS*R|0WF`QaLtO}xN1F15R zLh5GGEjV5Yp7zq>gO}4>y*`B_cb;CZ6f8IaZhL2Ub9?)sxx2U9D4FD)o+p#~lE%Eg zzAFu9f_+)o-$JL)`Gq>Lr;BspybX!|>EDA!m~j8YcjR)vbo{S}kJeU8_y5z|`0wBJ z{{QRk{eOG^-^Tx{q!VJMLouHa^q}+~QP7X`FduMhJS?(eR)>-b>!Pw_2t7tK-r-eT zj;oD=Cffd3uhqA3YZS`x2hdA59}km3+8Gww)@N&fbLT)}@7C6W$A^tKjjf~FL+)sk znkS~6MCt$_JrxGWie%|IWzSpkTxd;rU|;dVVU4Nqc2=~8oq zy}}Uyig^HA)j95TF}E``h`3%df6>i}rESt~l9!t9S3-5Hoi7>kIIXOOoTVlD6I03h zIv55SD>p}V(FC1mbN^uP?f%isI@D?HQA4Ph`b!fQG(D%vMwd#OP}GxSkb2=q zG7!XIO>Y#*k}ZiKF{!9@qH5T)#5vaN^Q_-qkHEF zlx`@Kv(-_Ur$LAVwG1e=Wj#ntzgOB9iB2bmGp0}dFxof#?}b&&a#A;cD`MH(0BdDP zHNbqmGgXRuKZ8#mPu-jN%MF`dFg|9LaP1%*102H~sJ-DSQ$NS!lap*1vr9VM=Lq{X z4F{Bj&-QiSAJ<}Ow*|!D<*BHyeCQ7Mu*tS0E`I^9)BMB(Y~dql`-Rb7hh45(-8lNb z1?by{Y%X2%4po?l&@KIXg@4aNyaKnjnA~?+5bS;~g@qQAmx;F%#ITdw1)3ojFF}Bq@I}@z<<+w$wXY z%No01lMz|bl~5nRXBmYHoj%f6Y&@GRyoR6MiZUn*K$)aVy&9V%BgJoZsH4bUE@+>W znSD%2>AcluA};)^i0`am5cSefJqR%j2$GF8kH+CDsst?i#R-~j{Ebh|@>=q|y13ts zU64|FOZ9z9vMx~8zeh(0olOW4uw5c`2)-sV7$zDfCZ(>`b|^PBfCX&~8Wlm4#*tr` zCMYF9Yo3(MA}p7H)p?U6oPs0$e8*kM_wAT4lce6-JZ|m1IlR9{W{;d{n~6ZLm0^Bv z0$mNW0m|g);#nS~l#mu)qus{{jqmYtTjFzKB3LP7 zjE~4iL0+!;PULUX51C`Poi*HS9P$pHBi-l6PWoV2J6eU(Gd;Ifd|3l~W ztV@aral`3eI@q^Tp-DY+zZMouX3d&s%M9RhoTozs4|I*7HqCIpbCCcjMzxI+U?T$1 zThr#d{@KlsdDV@16FtcWa0sz`YDhR56NlgcmhX>0+=Dl-EvtL=b<*sLTzf%x686CB zvT?ZO`lM6lM?#3VDV2Y0{L*~4Z)&qH*dH5kRT9Uc7Q9(rp3vy_-cP#|n|%=lQ-kwM zokC(*EYmixqB6r_`4 zpWY2R$k6f!HW&2E^pT1Qo@L#1034!6{RlJQF09fOWJ1t0S?H{0$yLDG{IVf=QsmAt zfj82SpiT^@UJ6+o7%#AtEhrMRY`05i;Myg+bdJGuB_(2F5Joc~uVhWdbRC2kL@7UTb>wRtfOP9uR zJ{*|=#1OJO@|wG0etVP;NZ*6poiw3JOJJKmk+S|dsmU=QOos)Q{N47tb{8G)1Re9a*W=^ zW*T+Ua#=A6`8O44yM$9^J^ny~4yRVlBwi%L?6+}Nj2@(|4=dJ{GxU9#%>~fMyfODT z(Zu?Sq#{|O0L4a(P)zfxBX-pIP;m0B%%b14@=uZi9XQ=lOq} zwL0H6|L?=6xA}j+@&5P5Y?b!l>+k=QC%5^Z|2+E-d_PJVuWblE#s6z<_1V*4|DQd6 zc$@$EtNinu6d zS6TvqvmA0xQOi*X0s|~sLu&JJ1O8EZ-t-6!u+jm><4h|cK%^$2CzJ6AX|6AxLf-WlL*~piG zNNromSw<<6T|IQbkmi3n*xXN6?a$pkda!oq4ltS-pqRt0gGOUF0Y0?oG#OK`+n0m^ zo9BZu$AU8#0-(ow(mErdK>|9@rrKjwmFJ>RO)KLJNI)xSAzqn|VdElsYgkgck>p-X6EcF?Gp^qgHK^oM#n91g0QHYUUo6AyDP;8X9cp=gt<3q;LEXeP1~2 zIJ&6fyy)cGE4(ENcWjo=H2UqOJ$s82$evZfECDO7D}y$}Y-uSKoY8_hV>>blDb72% zRxSD2N?}*A$eYINqvW~w=+)-dkB9r4TMd3*a}1H~8Xunn%*g7SN6!Amn2T@=NTn>4 z%6z)xTh+w(bJBK(@t(b1J;*SkD4?diKz`4G&W-NxM`AB)p8S=3&UKw)gv>u+URV+n zVj2#e^C^^lO zL0XJVmVpEXz9yuG^#&Ku)RmPLWezO!n22@+7Gp490(lL-l}9f;d$Ge;efp}7U+ny; z!{r)m>>f1^Ok+z;fA?zs29{2)cXW00pUeqX^DZqWPj_hnJH3B@5iQ}?6R}1%CUWZd z(v1hd%ZQ7bJ&=LO`3`ScLKO;F#=QQ|3?_? z$)o=tu0DKpEC2oa`oA#B&6KD#o`V?z3BX7bE`w~eAXa)!ZlP)PZ1C3;HpUu2t=TBQ zyqM5N{Z2CZpJ!fp8Dycnm^uW!t|xVGCCt9$)b>)Gq4{kS)qrpo|FXJXdc_*JjpX6u z^$E-9>?0gDuqQ0k+?~C_TB9C(06b z_0gkWS9>Pw=%gK-IDBoRHhH^*U}QVM3%pzPh{Y26(tmzwZ#*;r+&$b$vZ9p^&@%J4 zan^5DNq`wA{)Q=W9n1o0;=%4oqq zj_C&h?>pWD1+*O^7yGB$kd#|9J1lK8&8DH5{!%zb7*f;tk}mKN(N2;Qq|IaZyk|22 zhh14U#2AFE0)VVPg8v@hZTARJXzl)%pfB>nhm=W)y0^W5GQP0Hzaxp)!Vo~JOEOj+ zQwCa`g?+y0FJ5*rV8h!OB-8rczrUcslX5gLc2bMzVSw8G`wDmiz{T+zxD}bi1T@ub zj{D;x^OL`+yt=Mk5`NeNk!2sQ(Xe=MP_q#QB1{j;uAqGfrgZVJEnuV-w0VfWFgQ)U zGmr2!?vk#DzwtTFQ$NSwR32vMm51MT3{mwbwf_QUvG`KkLN+uz4jN?qix0!@Xn?Z&ErP-LOESs7xKSc*} z3`NrafH@e%ljgXa!f(n-lAR*{B{v2ak5mS`OtRlTuUOK~vjRl`?VLiJP;5{veJPn8 zgx#;B10X)8%9t;Ml5ZSmJZqI$A<0ip1liFIc!61GU>GTEKt{DG@ys~ISfUyG7wTq; zyqDq3P7~M|M>d`#x)TwWwP+KPEs}-FHFRJ7 zy5tKFol=yH@LbI6*g(wP>TMj#(&&fl4}8)%nBno zV-rC03UkMqH)I1SFOC5W`prx8>N3fPol}H4n>})rOP`Bgl?zklVrmrx^ucHtv6pPY zvZ^UMt4Wx>5Bc1l$joS-@*O8u7gYsP>$QY+vox)``XNNRX^^@$v6*BwBZrAoz#7t? z$GZW*jiN3Sjl+&|(PYk>+{%I>j2CJ2$xG?qSZ5NyU^7gq(ll>XT&z47BVEx*mtVrx z5Mgg>@bCL(ef)wKLBR`K@WK_emZdxz_rh3~(wy95v$WE(dx7+k>yOM$tt^@MCUP>` zW6Dkj-$mx=oNvQ}f>2pw&;*KA9TWqO3JzVfF<2ZAd|1Rr17OMrmu7$I?Q}@OP{h{; zB&=;#5c@Oh$IlI~!EX%5!S4(}@H_hDy}R(6;@+dqw zaMyze4em0~0I9A51$6Eruqx;cZK-cw*!#iw9GG#@@>UQ>#rSwRitoQTS!B|BPbG%@ zg4ZB{V2)QKfvl`D`4f{sraB+N1v?Qrd3Mltz&Aum+?vt-N8A4u-4CB-061Cxv-WKD zS?K@y^vR=J`@er5`A;+*1_w*1mJ_+BEVhoyjxg^>(oK)EF2%a3uoQf|v)d%og684Q z|7|1>-J8QVKQ`ZP{@i@C`Ks}z`KGa({H6SI_uX4AaG0z;jK8MV53yw+{1}3%w!%dKU{uf zX7PCWu^Bpil^>7f2Z!Y0AeeY}-KoW=<3H-97ftHE83{VtOaIxVFr%@T7-NlU7O^)} zt@MVU{#u3(mGhk;x(SR2)w;PuGO0!Hh!gbXor`PDlY&V73x^sNESt z9Zc=Zaf(PzKw?6#g=L^!NRx))nnzjdtS_x~NZL8T$V>?OBD@n}Dl|bAj6fAwkq;{? zlDJ2!O)=jchEVB#@a$ijWROOyO^ffOgY&pvK)^m!|m`uykm~GSmm0eZ_zsu-0m`376m*)GQU-#ie5T3KaM2%=uznK<=0*h77oCbBF|r>p777We#Z3r&f^UPF3Bv0c-uN&n%S z2+cBQOzZGf=gZgRO>OhlCw!B@{V`d}2cx1k=8SA`*cb4$pNuK}obQakc*S*U>hqw9 z{kmj}z@FpAH|j(w3$QN&=U;XO`m(#%n0h}t^Q%I8t$N-qN~TDJT$3gJwU*1Th%8EW znhm{0s$G4VYBMdAK2!dBF-ub(t6Zj)%Q?e2w9?YRsCKd38|!BF4h|!_nea3(_|3&y zd-1|V@&94x^%1IwFeS(p@xT@t3Q>Bt=D5X(@JQ^a|z zI~?U%-nDcIvkR6ZS*W(Ou&~b5rvgrYocw&D5>#eK%zqYmfHwcI>`E0>RIHi`k2RTS z`-mctnxpgO-K^k&rjMeWLyFwDCFX_e_tJmX;S=?&eR73_1D$?MJ6-fxY>F1yEW1^X zVRJ8&M<(Nt-XJk`Jwg#gQe+s^o~@4hd9t~`Ly0jduO{cd#Ei=6D0LZCjkA+Pl_hY% z)n>S$>UnEntuFc!5{@jYA{E}mfyvy?P1mSu&QebC*>l$ z87D)LJ8~jL&O7i=xZyaZ9m2z%S^&BdpbhvnL>htYc!|e>N!e%^ixwDs9&95#5Ww`x zbW{x`dW0H5SN9XXJ%V=bns(4rm_}n8bV5l~@`Z2Y^4M_Wpd~|*G`azkWL z0=sb*dzoi1Iw{(0cRMw0@=o{{y(cbH)|<&6`WAz(-Jqc0{6cW>@szlc zmP>@-yS(UKDyp8R=XW0+W^!`RnY(fcjILiaBm0D&ak>b$^ z68fhdjjusS{(1kt?3ScKvKFpdhGPY364p#*k5C=INYtuvTkfrwZ^n7z@*H1?(b3iV zmtiXh^O-2Rt-OEIIUNsYKk|HZts@GWVwg4zXTrx{JiyW62IKMKr#x_dbv@C>&2NGAd+eWX{s{qG8*<Ac~pp9)!ibwfbfj+Mt;qwXsGjBW!=naPdq?e+To@73)z1XE( zQ^Y>(Fj?Y}(TUL*w62;T7v_ia9k;*iJ8pDddu5X6BbhQRCFl<;77LzB*t@8+_Vlne z;>W8hJeUy(e|OfkKmFf@*XO=2*}%M+v}^dMuXdm)|nOe+`qq|RvL3GYRl;jKUo|N8y+%|%w)FS zClN#7q^wa4hd53z3+4Ma%*eV0#*%ltX8+NZ4~mc@Uq2U5KJuxcVx;CL$3Aq-qZ%#r z-|Is|=sC!CI)r~De-|0<_DS7;A~lp_At!4qufe5V17lRaVI!w$FFTp5<~Y2Slx&;g z3_cBZuMe@cn}OIFF#hj%6z{~*xgt@yxNsm6L5ulB)aD4!v`u;6VHzOji|k~~;u2Vi zGYss1hB3R)*lH=s2gTy_g&A^BdTy1Ml5&D^@EgLr-fs&AJ``wBS>_CU{*mxJ5!QUO z7FCkqg_z9X7?+{PNH#1tcstFA`8(GJ?6vBW~ z{pDr&O+THCL!4!?acK@)T3oLya~h z>AZz2FPk~U+>|-C-olZOn^~HBMQ|HA@y28VgOwNgARAIdHtXJYQ!a0_&}@9uw$3ux zL|mYJJX+3AmXGsszg;BRaF`DZok34Ky}jHByxz30oAPvfx#7FIRj77+^kF@lykIz` ztNd1+@bAL^8~gk(6#sec(bI<^|Njsk-SYomh5zULADHN2??5I0un#7%_>4QRlKZ1< zA0KK%0K;9Z3qR=TX2Re`bjxIY#G7rRzQzMIneCt|EsnKwb>|80Hl?7|Ak--*nUxCG z(>)vn+N$H|+I~KA(zWr$7csE*uzGn#a4^Dt$~8a*;py_rOZgixkcaUV%Fzdfsc0NT zT798ie`#zK*fM7xUy$oB2d{vkKdqO%EQo%5L5t%unHXTcO=EH`{7;rI*LMM&g8v^r zdAwG_|F`)+zMcP{#nR5=iQL4h0{NAlm2^!CzE}K;c31BXnrJR{_@1STY%%hCBCme> zVdtn(H@{xJ+1&au`Bc|e!44Y{a5SZZ?_WVby#est;vJDDr=5=!_J}OYKBnEVx(WDc zEqF;N4)Avkvx$>GlZ$HIcI}&zrzpZImpzqmOF`*l=%ZX1p9W>8a|E)YE~cTjnlt8> zI1&4u#$c)g$kbrdiE>dm`GP<)yx zoNqj6qcnAVn^!i+GrD4UgB)XaIz(1TnlKRs@Ffxp;Y(g+G>&7aH4DuCQV#UZcaGwy zyeBotFSXc<&+=aM^aVNHP_$C|{c$>T?*_>UM8{PzC;R`>su2#(6ptTKY*_oDPs2*8Q*m4}%K z$77PpVto8h^w|uei;+T$#I3HuVK%@>t-^u|jv<~yik@bp;}56pn8@5k#Ie?H5vLs; zptv6yam9d3UMxfhWPM>$XOaavAGMM=P&qIul?A$>8N0oS{$}5LQvKQI^oNV?Tt+Figq;);FUV^q_G2K1(-*J`t(-uiQ%cm?= zf3}r6oEb0N#0mn!GrV<)SMXkcsnsniPrC1gIQ^g-1nw>KADUk+cKe)Q;JIsfysC%63HpCbNSm=h`%5{|&;W5F3wu461rHN#Y}eje#|+ZvfI zA7A>JSv=-anZD4QwEYp24PYbATx*h92R21v`)zPs&xVo<0il~w7Dn6NY}Z!19kHa; zHbkv6&n`_aBrjrq^zOk065}6?s})+C@#S>v7!QXk)&R|SS=TITpfEx{;0PB_^4UTsiwiB&W$9L>fd82d-UzoG3Vm$Z>`?OyrUqt!;$fKX ztIl%8idUcBE3f#<%8GHlGiyF;5P|a%(j9Y{(jxM#8@muy*chm6OmfSL-Vdwafa)X=T`w87?$5?vuvg% z^JL^+9=orjZmFD04tm^mUx&WF+*#DYf9rQj!EyzD=E>a7#Ihwngw^`0M?~T?7zI*_ zRM4hESGMBIWbCl)`@EGr9AJi~l}|zYL-VH8f%8lWdbo`A&~-hi8EId=1vl!&vF5{&F zE!m<5TRtswnnI4GTfHI&0O(bzfKfDUFvw&M5*DMKEnb~@JflHQ_FC*;?TjR)L*nnd z?DDejH6b1=-rgsQu~#8FKE*>aUum1{{q|Ki=?o6#H8$zvH`=7jz$NlZyW}N93|uYA zKzrrlnw$COmH#&TY5U{#cz|jCAEo@S@b=lWTl)Xsnf@tDsMbRsgWO(*M|E33tAUcm8w zJTE}l%&qs{{~q}d&@rdksCe)h@}Gy#o;|6;|M;)$|MBquBu{Sf|DS6AGeEh=`Nfrq zfNA!>58<7E{{uyE8~^F6{Cgk){sxyU+gR9IOkeE3JHG{Nd$_+_^^>w0AJfPlss_y&@@+FUImaPlwrha+!}Qwh3Io zox&IocSd-ZKVa=?FK?q~4?IJ277j3qv0iNwitl&dCEsWLYzUmq{`k1tX(ewuEf^1m z4a61(_^>#0`PN_K7!G9&$!io!kuA)+NtXniQh|KVL+Jxskv1t&Qrf>12TCbfHCs2S zw#h1UmZM|_GbVMA#p-sE2;qnzl;fSEIP4t#u=nmL+1&jl`DyduU~~8AmvxE?gGsb9 z^ku@P(2|FeFk@z#{^(Kyh`enaZ2bULHec<$**W?J2mE^HXt!~An7rOQNH&xG&4Z(z zt#@xW50d?N2m5=6jg=%h%rdi_Y)KkjQM4oKq1k3T8>O9YVJ7|y?4v}yp{O>)td&tF z^^}t?Pu(kQB<xH5B=SU(Rya_P$^K0bd6pYI+mg`#+5qS_xDD-0*>EZMYeXCA2MgTNVx=MfK;Q55SINJ zQ5`cAs^Y(1Ch%|)cP!4GZK$42Xf)UPOB)u7OD}J@m+K~26u)|5qC}Z?S6##>DrScl zHhO=JU6T|(NqE#!|0q-fX$hMT0EUe?k97~-K7eY=y4$mCx0UR_p= z&Jh3@{X%!-d;eIyhtaIX>(w31;FvG!A@Ooy!I!38SOjjvz%mt4XK&ta{^8Lkxg)yF za(19gFi=VPrGsoisTSO}QmQ<-`(|jWHm@6-mLLcl-%E;_k8vhsrB$8Ck8LcH*({gyIaiFEp{`3En;;RY%Q2Za28lUdCRqbUH*?U%6vilU-)0i z{`c1Z^WR(kOE5Fh0)+D*SyNr1_C?XoGF3@PI63BPg`4OU!7JrAg3e+dq1V-zw{xXC zl9%_Ad&+Y6Q(|;cS`$@@40~-&ZFEV|P)_(Vv!aw+`No!!(?s3aGFGYUP_Kq6eihPk z(o$c06}Pi)=Gm7!frQhpU(IKE$||WoO$%9ANYK)k7W zKtJ97O+o|EEYK*7lPUGg3%Y8Mh6;3{+Qu+U6?f`S3&Xe37UftD!3WkXEe6boXlrnl-(3J{>1)U8VAmj2W9UvZXo zyUpXY-_JgW`kNmA>)Dgg|Lf_KN4Nf8xAOm6`Two_|BK51OWuvqBq-xPY;U_%@T7D4 z;FKpB(HCGIrO0Cssm62(hgg6mlH)9gmZf3(q*Z^`Wa>6rD{uLf^5;!88ut5LnCp(f^j6c-e zKl=L=B4lec>@L5u$plaLe{Nt@ydKU3>9>`Yl{qW!`w5z*qL>*BX*LcC?s~%^B*(N3^OYn} z@Rxyt<=do{$b{5-KozH#3u~*ZN(|?c0MF^wm}ANnC#V)|n9Ta`AHzA14N!xtAnGRE z|FRj^xBIxh{l}9h51-xIe|%&5&+dOZMouAzqtk0&hY*}X|E)fK@-VdjeDvhj|LZHu z|01^`frEfSa3vN)3l$q#>sR5KCHc!QSUMKl_qC?8T-WU9UPK6#$Z&2i#!Ga*!#_uEbT zdm-^#)2Z%FnQ(%;IpVRigl3ogV-8U191XhMR5)~r-!w$Tq;^pagL1=s=LGYv+bv=i z(Q~aphS5}b&ZwNRGwdJbtKmKtqhpYtt1(P#5Y!Yr%f&R7RdDwOaqPc4{ntKg4X%&< zr{e!-&sHCW`2X?aTl?Q{DgS$REC0Kd|NSqN|KX$lsPwGcIi5-w!41}*Ay(SVM#tF* z$c+dY)z3!l>?5pZGZIl zc17q@JYE=u^H<5UQu{rbJIlI*Inw{`zI*egp4>aThsjm&9Y3&dR_XaCG|W^Xofztx zql=NFx8YfnLD9Sydb{P(-Dq`~4hus*b5cTQ6{X7AacR!G8KA}NB5jQ@oiBOf8<-=4l&y*>0@uk%E~V23 zD3F@E0UQYTOg03frHTH-C5&j67ga8=P-1pSg^`SDp~3d$bX4fi`OD%$UX(v8GSn|% zq_pBQ;7QNt#MCn`4@+6U{U41>12~_AOe+!LLyHJ2TbmgZ*7VMqx299^I{7nVOFM4% zAsPYB!*oY#GWBF0fXSGB(I61IN&|S60_I{gfK_ex-oi{l*BX~>8(dPt-?UqHeOwQ- zpmKwuSkN!y1*p2~3zR(Mel5gaN&hl#^2ARpACdQfX9D>X&c({0h=jyJ8WEHw^bpU~ zgVKy;b#aFax%}q}t!9z`oVJgP(fIh9iadO!Ig z{bxUK{i}nFD?T_&|N9i^*6s>G$F1<^R`~OuB>brqxm2sW1T{t_DBo|tO7NvmBrC~@ zRK6L`!651D@(v?_t^!>qUnal45bE;f%F4?7ka-}-Z{%L{5kkoE_>>~OvK$5ZTmgfV zyxYysDeSvxx=?+>-Q58IasS|Wn1@(4S?HWlmQ+|!JXedZ>bO1dcpa+xZhN1cO(P)NgV@2&p# zmIb(F0lqK`aC6FDsCJt5XXGddrCvo>+v%NFs%m$zv5C6ctUm@gB{ZIrH$L7=J8!>l zzHS_C{eZp`b^iTkZ*yC|Hhykw(M@D1qKmhaM3R>KnMb)qe}V{4eNH!XrzY6}9hqHtA&EF%lUKDP6xVi! zxCuKPz@KR=O^B}5WT@sVAd#xJCL=?d!-{zE1l#8j|sn{XNts*xsSr`HLN$t zi=u-Omx=*0G)oVX`z|sK*8H{eo&}psZrHdxUp{2Re%8GiHr%w|#DGLm zX1t!1PowJ*EFHJd4XrxJtxnwZl1PX0X*430VF-)_L^f> zaEh$p*X8~n%gaR3zQE6y?-RsE^x#*k8i~mcpfdM_;lm!_s3?vYtwq3<*Ap{q{u^V{ zXW7i-ynPwqC(<@Bj!HOzMh+wdp7LY?fE;pE%8f9PAZTePubu&Ek{{y?Ck!1^G(EXCN(0J)Zvz^ zSj_msmYD}u8rLTbrzicFr7u>gOQMcDcF~Vvf$8gl8@=%^+U$1o7G~-Y7{h_nH)tMT zHR;!aQW*;rmSi$)z3-Hp%Z=T=w~e=fhEtw8ZLLqoS%{&|c%)7oBPv^W%gU_eik0F< z5~Fq&&xq;JfhS=k6vq~WtkpRIre@JC!17B`WIK$t?vJ=crTAkEDM1-zAVxA|2TPqp zk&>E8gUj??7DDgUuso| zDgMmmfRS|iUuiX{uZfFs{k0&e!1dPxPYy97H^pob?z^_Vq`d)4CX#QU-I;lnIBVj~ z^NZsJT8M9~L7a^+|Bp6oEP>=T?8F^i_W3sA6UGx5N0umDqg|wf}w zl3!m~5lq~EBO59ERg~&|9-8x4!56oDAzVjmb&Ko%1h~$J+SzcOW9|Fb#dRg_zG@uP zaGbNDsOB-h5rX@2%xpDA!!vbb7>&5eFF=|#SXTX+Xv>@G7spt%7XKC)%ZW|@qY;-6 zoU~3~=mY*BVXnsPhsbWZhqiJSo>&#*|jaZ}~Ps;o0D;-ociMyeLN;XkA@6SA}IHa#A zy2b_gQyy0T>8LmO4?Vy>2;T4ku4=q|fGzl{V~dc~v}czh7dMOaM6o`wE%juctJDed z{Jy6h>S{Z=h*`JTJkMs-ZPh{As@v1m#GE@tb5vaiEx%wr^ZsRz3q?y-fU)-#!9c&;h(rTDq-*Rsi=x_a zS0G7vryM@5w&ZsM-^kS(bB4;{RS43L$C>fmbjG@$`69 z+xjg0sWv8p0SiB`2Us?P(3_BruC8)7YyN1~HBVjuEi=xpw)T{rX~Npmmxu3X3R?=4 zEid%Or1h2->(>cp>NNM57U248`eoplvwBsnJ3Glu{{$2>xgQMbGUcthbYNGdl=C{=3fX5@{Gvu{S)C;}*sfj`u1%m1Ifw*il%xDrK2!pNQmEC(DzfRMIgWC>;@ zjcv(Tl9kBP$X1Xgp^+^+#ulTQZfWpnMmyc3pBNGo!^T3av)Sz5w>SH497w)gAi(Ca z1maxTMhPTYNVsI*P2S7hEDI!egmM!WJ79idy>m`gbyxSy$g*+BCMzdsX1eR=)TvYF zuTGs}w3215!Wd^;Q$U z?9Onh36+xXG_f%~A8s|D^>2g|@8@k3IaXXV8awt_78>4fvi|V%4n+tXHi3q?JAz6)yy+1z3##NZq;5f`u{-b z;}D^}h|vEG(S%vl_j^9_F$X+OkE%RNInI69w2uWF8F^J z{J;M}{J(SEx|Kg^o1QmkTYFZP;xjmi)ef!%7@%P>8m9<4y?%6bw87;8%c17QbS#og z^t$!EIn=$}+MbjWt}{gnC{r|G?p@HhwfH7+GqMOOqgCmEEi>xK@lUcfKuIGsUjvVG`_;@=Nb_{K}Koite1Y&R=BZpf<9yTQNXn(?J~L za3PUpwZ^#?tujJ1Z0rt&$5^5&fnZoV{0zf9gL4G}qsdsl2#NsVYndMD+hiBR@EIU*Fzk9nea~^#bSunHsw>3L;j$?Q^%5$c}E4Y%ZTLFq$=Z@vnnrcZ*2X)uZZ$nCp zgUR9RUVGi;Jzwr|iy98NeC)3G5@&v!I(v+fc+$}37$m)) z|0sC{$N2Jw28!0-#aJES!C;$cIC)3wJp7b3SPES~i?Q-^SMwU`JdgF|9jOeP&kA6$ z&y1Bwk~7!v@k)g`^zvNiMYxY-o(D$Qa&ZLq+oYWHEC44yXBC?ya@WZ|a-h3T9Q>!e z^nT&reEfgH^LozbKIXaqSF?7V+y7U+ej)zzz25)Jd4Ku*Ljs6Y%y90ju!7{lXR0n! z>1*)G0{0C#@zagx;;CQX?xt|FJ3X+cA04s0mF@IZGLOTQjVs9hafRdEuLRbaSc zp-BdT%oM+H9QxhH9ADUcu&b-1Yinq8?~WZiEd7@9&fq1*02dewEIWb<8ZRiNa`V&( z>j6EgBViCqP@c~ObkwS2R-J5!&`FsbjG3ku^GKtx@zOd_cc2(_GyuG~yf_34JtBTLA4C41p&1#9Lqfxxd^bAef z_ZMKIb2la0W*ZF=OeKjL`J2J8vDfR}3V)IK`eLTRJi8-cp%HQRCDXROCHnSeQ9U`8 z(f&}f-EzhZlp4c4m#LaR`my*tW`Vj1&$$c? zC5l&ZjnSMi9>=^2{lF&uf=wbh(-i=QreUF&4g_!=k~TT`5n?=xP2f{ybn8R4&W=#) z4n9XbXrlsap2d*X=_vl^H>5lS)1?8E(~vVWW|z{2Qknq}hlLwO7A_So3aTX6h*SUx zB$X@kJ|iu`HNBllazT+}hpHpD6t}VHk{AKDOQ-d4%R=l7fxfj@i~3`NPcw$@W0(z_ z1a@l$mV?K5=CWqp?z%HC5ln|ZLeyn-o{E&KVFO4IfPzo!IdxZVlNjs1o^u-Z zYoqF?FV+G?_nS2x4rTytVC;o93Mv3|F}W$$z54lhdD~5h85*430iPNNj9d^gM^nP` zXfiDM?317Dfm%-YrKn}sez@v}(Q?aDwV5u`n$LzscJIuyc}7vxL5ZU!d!ez?!W+<> zBr+BnALuYGt!+WNr<3-U7-0(yj{lUX8}adB+}G7bUFE|}d$ZcAVp<~-^I+0ohINuJ z5!v9j5GrhbDnRzOG<~$MRpq1fQS`WkZ)gP@o0@_lc+rXX_ZXxbMzusti&`a`8rz$K zZNX;d4oWdj5(y_FK#y{8>HA6T=ls>)^m*Q{B$5dZW0jQ^=wi2qrL|M_E&|6#Y8%p>F-x#C`JELVOKiAm;k ze8FpyqO6SJcAv2Dl?1}k%knHp-VCVQ%k7%$*e6rxdQFj&A+9BSiaN(XS$pT*kYlIZ zy8SR~6F32)G>vnj6?TA;TNr9bpPqo8%C+AjwFD7QzlJT>sF3iC--S2w3$>wJ(lr_uaW zY+u%yj7O=CIv5`9mD&&>SnuTD32mg8Zuw*eT{s>|Bfsa@c`~DrH%v)o_>G$kVt<23 zkYinoBwld*)Nd4EQ86cEDhBV1*M4_ZE4R2*p}{paGJ72jI^R?5juw2nIgv&6?GZAd@z8n@5LAfiTJJOm8 zV#WkRh27c3hX5J}_l9nCVZY8 zViXZ5d&WVO`($GCx#rF5hJT|f6Tus`{wU}Q%#x`F`Xhj?3 zm3bmtnS>rpP_2b3pK~?KSZij5&llDz-<5FQmwyZaah_{V z;9+i(ff(Dp`pD2l#y9#)w!kzTm9;({FQ1a(u`04&no8~7UWq=zH1 zIDCah${3z18P+P&6&MUxuai(rVLT2USsMT+Mb*q3ASo&tk;wc(VY zmMKfku*o17J|Pyq($d5>G+mEHVu0uPnA(>TqR;LV_Z!9%5n%{gdpLY{n7KyV3=+}K ztwOT90LP?M0-8AtSfSWu>00+WlsOiCn$m`FGV|&V@vs5RpDp?`Xb)$hVz`SCEEI`0 z#8-m6zlJ|ChIMoeGM&;={-_=wt=FvMpq2cX22r@hd+Ks%^$v;d%`M>w&4glm=fR4e zwNOvtvo{8J_HJryx-rz**c7Z~kZiITEW2dh$!`TKblzc#?KI;Jru3_+yQ{6YnKzdu z6nIqbTzuBqI5l^pvpFZ*Wo~{?Eex&wG9T8y5Fu(2)J)$LtJ zorNMWu?bjTV@T{$Z{4lkZNc8IjvlYnS18!s-P*o6)Z5k?>ZX@#^So>7kYQ|XZ|Pud zt*Npe5*S%eTiZk3UE7+vTRYnMyEQrAbv8ERDRKB$!cg7Dh9LG^#8!?fa;1+TF`7~g z6MxYQEY!I|Iml4-mtE7U$m?-}L8M_} zhbI*tI!i^8@$_IKyC6L01<7dz%S?=v&K=NEbnfrT0U0?Runu3MQd1N=n_#>Q-8gzY z%&H0xhCPr?X54C|z5DH_&K_WV(NV9C7^MLi3iUPGO2dEuNG? z?b7;zTWOdfB9aTwITA#OafZRd6{Y41+~;L{c9U6g`?;E&J9Iy1qhj^7Z`;}{7CE^- ztSQCA$L>y4zvB@)LY({U>_T+wB7@xYeI5VIwGGU&t_^GNsu8}8fL^TmwKC^^dfr8M z7PYKObJlUMyR38r@i}(zQ{jPR(Nl$m%a#=+w6{wuQK|Hflv~+1OAmJAl{n;Xl9CiGDBO4mgoJH&IC(=HsctdSU zQRABsAu3TIq^HUCDAwKV6TXseRGo15QN(8W4T`kQU zR$y}m-zq@rSxnb;1}aJ^l%se*JLR8nv`lsB+Vs^GuGTB~<@DHvYiHI)2kho`JE)8W zV&!ZLsGGNy{kBeuR$J#LMLA|!DE<`@1sLRrp9cAVEhmdf*C z@a)T_;RWo(JJjx6S`pvppND+K!IG(RegrKw<@L6b@dK1*cZRG>)X06&`Z<_$UVdG} z9HP{l+brA1bxXGxvHcjBP)l~KP|e*AWiiXQNwJ>PfiK&+>6{U^y2P>52%go(({BkW z86{IfWbLHOQcJ+95=r*T%F(kl7&e{pS!k(AMZ$9?;y-6|RKBYf#u(b|2-iVQ!Z_Qo zbi&nnyp^AArN&vKbq;PrV4yaQ&8vRKqSQ*vbSD@Nj{tfPky}xb1GPpLTF=rd--n{e zMlWIyehV)iJ|a93hWLz8b57hr73QH6Pi(w2(S4o*FZ05Ob3k+}X6{ij_&K*Huvj#D zB%U&pS~6aiV}_W&AnWRg^G)3I^=cq7+XJ*V8`v>2Lm_@9bBhjfyKGDU-DtX zYL6pC+M&sT1jnYJt!X+ccykuEg?venvUB*=a3rD)5$z}XHKWA6uV`0j0{!3LgaHM9d7(pU;unPSo=1~iYA*zxD*;110;Sh;$|K62QoM7u27(eVni zk??%OG5*Oy_?&mfq?iImny6|BmkNvMMiaxil*{JHC0WL#7#Ti}{U&zGsSaLI!dUoX zWj)TRYnEnffH4*<>hc7X+8zgrO%w@k19sqUmlkz%1WGpPToMKkdv&dI)_jGvyzA^T z?iIj`S~QKQmil-5P^dc@r&%q0M2Z4XYaNjSQrc<@`E#LW9+QXhxnQbLR$%y2X6KW3 z4~%7%W`$j5l{If7x>-sKY}r#hGjLN^t@XW);#s}U>*t+5(Nf#_C0~gH|AZJTQJr_n zvHNh;5}tq8>zqr@R{6V}H{`Hv*EESOl-y?^MLWnW4Y3`J+r)r(`NACdwEfnml*q2e0f@8aR$RC9pHWX^hl2`IjTEiGgrlPKdmLe2XROw~$pzhbAcAnSQ zaixrYY*b5Kt+{Pr%x@WNZSU;r*xUsRU2N@QBu?&~Ze|-&s183ZE9sOhhQ;pF3c?gm z2P8r}>sp-P=Aqe2 z`)=}_OSsR4aUOSM?;gqP%O|e`pOg578{OTvOrFx*y-5_6sguKAX40xoDyHT0Ja2Ka?F*eD%bsOAs zvp9D}&}VYgDszow<3=|+)sHYk-igH899Yi2B7a_9q!z*r3 z6P_6-2(~YVeg;F#FT)wW$VlsiUG_e>w(=#&WCw&*)xL_l8?&k=Fd+nAcV}hU%7!c9 z4FI-oQj;uMmXaLf{T$~IoIW#reb&Os%cHGXu#7C4C<`D z(I3qHF9TXU{;n^7IkWx0u6q3%_x&$xs@5;q{|o1T7wrE9`~P=){x^}8|1}zq^_}s` zBM_ufOuSMlzRdSpXvSfQv*tRfyA`zHK|q?gI*v-_xpPO9vF@Ck@39YLF zsXb6`#65u)POR&``7PmdArcF?5ozIW4vczT5MRf?xZ?u`hLVOjqh#as=0xK#uqc+^ ziW6h)%?W`&wy$rG+M?Gw>tVfv=Is~O%}3Pn^&3^YQp0&57Zd!$zxn0%dK}E zvS`|Ab8-j^$15e?!LUqewKl4Pza!_9c$GBQ^p?*I(~Yc$G#jJ|$xm))u{QtV^?ysW z{f_T{tX{V+OaHG~xc_lM|6kDm7xe%4JpWJ58&9)Lg8bWi^SUSE42DErvp^KG_bT~D zS68sHxvjN5*t@lHhq|V^#+mQ})2#M#kk8@Pf)G?6NG0)FdlAgcW)E9{xu1W+-^z0a zYoALO%#~TydRCXCurtFdf0F9}r6Z%Ol;`!V637NehfqODY5mAw#DVK%Jerj~9cZ4P z0ps_iF|EQLUqdcn$itWm2M&O9fC3A91jC5|nuIoTHqdCKDZBHzb1>$}Uu&m)r3(gm z7E}(7yrW^I7q!R5)T*JhNm^W)y87n6)jTNF&N+I!92ZPtgbt$Q?V#Qoj=OmqhK;I7 z?!OxTS1Zr)E>>o@3MaN zTSpVIqSxDo)se+G+EB~1QDjz>z?ttB;CC1rRx6h>Mq39P2@v4ym>D`kX`+c=4ASqi z@;W(L^kACK+K{+2)9`GJT}O!;L|Hi^F{Q;d+!bLtqLV3p_hqHRZ>5xUbopqB(9|7= z%Vb9B-p%LChJ83T-l;%@fc2NlRgHmY>@qIl#w z)GKZs!Qn_rFtP*4UD*G5PyVsZOw!kb?ZK|bHZT=7wY4^>@L#Y!6tqJ(whLp) zr+zFAD>krULqJhnrek2k4WMk}tGw11Q`+Z9gEc>GUQvT!l#L~sps_9oWqEDD5+SdY zZEHf=KIk5$+E5IySSCv)hKI6*uQi!L{#I7CfYUOz3^}(JcfpGN$f$X+eUn-m$%V#g+ zkt)zaD#}$dkS4YPMA`DNr#7sq04C5(3Q>uz(IHfwM zF-c*YD!>?FP+}tqQoRXYf%iH_M54L9iR6gYZj^itq+?QSF0JV#RyDPV$(9arIfG^; zp-H$*X^1I0PG_*@0U3Rl3%s;PS+l8Ag?yGb&?}d|n*=mOGA6(Sm7FN5g|d zam^=6C?1Fi-%Ty{!7o;aluv+Sk11`Sa^Ab-{V7^gp=j@S@h8AGOngayJY zE+cX~Rj^e?bzS5PRmNz6`7BbC$P0aZ66HZVjX)9>9L4~<$7WCv1Nk2fqKz>|Fz_vw zQpD8Y8rE`A3HNS|pv9{V8MSnqX2cSePOJl2DrIZRF~g3Q;$$L8CQ$1EsfZ(kUdFY4 zSms0a{pDQb`Q#bqS4{G0l=~y78{-CGh_c;=amI*!t~SI_#6eRS5F>b6je_7g!6}4r zO-Nz+W+O2bjGZQuw(AnRq|rHq4NOFwr- zrXT>9PZw-ZZ*g3Atm6jaYKEqhy4|8D>bgBqzHdF&Z0=MVZ~5>?20PIBn* z#xHWRnt@LcLmFt&V-=ifk||HqOvFPN9}Gvy<44{n-6|I*#jHZ;U_&_n*2-n!42furaZPL!7Yf`W<%fz$e$&Vj3HRAC7Yw{jcwsa1dfHk_2Gdh zVq7rJ91xdRC_PNUQ7s}%rBU6+whZh=$Q^5tcn=@3t3U-)V!~tdF<6DJ#+zAf$a)M; zudH0hC=DE~kU@|F=|OTz0-M)t{Z~QM48euGL>DoIcBimvhLBh~3-B@>oRC?5IQVd%p(UUf zlx1{ZA#Ym2GNeS>_MxN*I8o==sa<)xlo#ka@N?C!}U9Ak&P;qn_vXTqSZV9n4MqJ*U;g|*3#q8p3KCF0O+bi#HvCArP~9!m z3qha2I5N~Laf8ONBk#~Xg*P+8c;aJx@R%$)oseuohDxMSdP)0%xUI*zFkPMjq({}I z21!ZeAsEcZl!}6+P0{V9gcI6hK&*+INW*Lb!ihvO4cvtiz%e0Ai^9S2)SMhIObze{ z`E8#v)M?`wTf#Lki$3@&U~pI!%58@QdDm#gJ0fU^195EAbWscL1;fRt7LN=0h0UpC z>?YOWm^R|#iPVa%;gtnPF>4hxRf_`FA+k+Fi|fMYDCU5|Ti~B{=d48u4EfAUkZ_>f z$C#Ld1BuaOYY2amj-{9jhPCKwtfCw=ci}govV+X2Kt4GxO)Pq)KJDc}#;6iQT>##* z00xaaUI;;GPEr-74CP&#K9Wo5d+4KmRU?^z*2tBMs!@tGL|gBmh(^437U>K-Fu1-q z2sj=_JIoxQh#-3nvYDm$VO*j<^kb2)+8$>TM4JUZEhQI2B7aBNaCMDN=d@{6P+UZ3 zVAQ77EYu$2`}%^#Fw-PvIE0Gg7ApKgU|ZqjLW{K_j6z;Za3RHs0pW9pal@H%n<

    QtDe0WuuyuE@Mm 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 CpF4>F literal 0 HcmV?d00001 diff --git a/syslinux.spec b/syslinux.spec new file mode 100644 index 0000000..4d9f8a3 --- /dev/null +++ b/syslinux.spec @@ -0,0 +1,264 @@ +# -*- rpm -*- +%define RPMVERSION 3.82 +%define VERSION 3.82 +Summary: Kernel loader which uses a FAT, ext2/3 or iso9660 filesystem or a PXE network +Name: syslinux +Version: %{RPMVERSION} +Release: 1 +License: GPL +Group: System/Boot +Source0: ftp://ftp.kernel.org/pub/linux/utils/boot/syslinux/%{name}-%{VERSION}.tar.gz +ExclusiveArch: i386 i486 i586 i686 athlon pentium4 x86_64 +Packager: H. Peter Anvin +Buildroot: %{_tmppath}/%{name}-%{VERSION}-root +BuildPrereq: nasm >= 0.98.39, perl +Autoreq: 0 +%ifarch x86_64 +Requires: mtools, libc.so.6()(64bit) +%define my_cc gcc +%else +Requires: mtools, libc.so.6 +%define my_cc gcc -m32 +%endif + +# NOTE: extlinux belongs in /sbin, not in /usr/sbin, since it is typically +# a system bootloader, and may be necessary for system recovery. +%define _sbindir /sbin + +%package devel +Summary: Development environment for SYSLINUX add-on modules +Group: Development/Libraries +Requires: syslinux + +%description +SYSLINUX is a suite of bootloaders, currently supporting DOS FAT +filesystems, Linux ext2/ext3 filesystems (EXTLINUX), PXE network boots +(PXELINUX), or ISO 9660 CD-ROMs (ISOLINUX). It also includes a tool, +MEMDISK, which loads legacy operating systems from these media. + +%description devel +The SYSLINUX boot loader contains an API, called COM32, for writing +sophisticated add-on modules. This package contains the libraries +necessary to compile such modules. + +%package extlinux +Summary: The EXTLINUX bootloader, for booting the local system. +Group: System/Boot +Requires: syslinux + +%description extlinux +The EXTLINUX bootloader, for booting the local system, as well as all +the SYSLINUX/PXELINUX modules in /boot. + +%package tftpboot +Summary: SYSLINUX modules in /tftpboot, available for network booting +Group: Applications/Internet +Requires: syslinux + +%description tftpboot +All the SYSLINUX/PXELINUX modules directly available for network +booting in the /tftpboot directory. + +%prep +%setup -q -n syslinux-%{VERSION} + +%build +make CC='%{my_cc}' clean +make CC='%{my_cc}' installer +make CC='%{my_cc}' -C sample tidy + +%install +rm -rf %{buildroot} +make CC='%{my_cc}' install-all \ + INSTALLROOT=%{buildroot} BINDIR=%{_bindir} SBINDIR=%{_sbindir} \ + LIBDIR=%{_libdir} DATADIR=%{_datadir} \ + MANDIR=%{_mandir} INCDIR=%{_includedir} \ + TFTPBOOT=/tftpboot EXTLINUXDIR=/boot/extlinux +make CC='%{my_cc}' -C sample tidy +mkdir -p %{buildroot}/etc +( cd %{buildroot}/etc && ln -s ../boot/extlinux/extlinux.conf . ) + +%clean +rm -rf %{buildroot} + +%files +%defattr(-,root,root) +%doc COPYING NEWS doc/* +%doc sample +%{_mandir}/man*/* +%{_bindir}/* +%{_datadir}/syslinux/*.com +%{_datadir}/syslinux/*.exe +%{_datadir}/syslinux/*.c32 +%{_datadir}/syslinux/*.bin +%{_datadir}/syslinux/*.0 +%{_datadir}/syslinux/memdisk + +%files devel +%{_datadir}/syslinux/com32 + +%files extlinux +%{_sbindir}/extlinux +/boot/extlinux +%config /etc/extlinux.conf + +%files tftpboot +/tftpboot + +%post extlinux +# If we have a /boot/extlinux.conf file, assume extlinux is our bootloader +# and update it. +if [ -f /boot/extlinux/extlinux.conf ]; then \ + extlinux --update /boot/extlinux ; \ +elif [ -f /boot/extlinux.conf ]; then \ + mkdir -p /boot/extlinux && \ + mv /boot/extlinux.conf /boot/extlinux/extlinux.conf && \ + extlinux --update /boot/extlinux ; \ +fi + +%postun + +%changelog +* Thu May 29 2008 H. Peter Anvin +- Use install targets; clean up various paths. + +* Thu Jan 10 2008 H. Peter Anvin +- Add man pages. + +* Mon Nov 19 2007 Bernard Li +- Added netpbm-progs (provides pngtopnm) to BuildPrereq (this should be + changed to BuildRequires since it is deprecated...) + +* Thu Mar 15 2007 H. Peter Anvin +- Move extlinux /boot stuff into /boot/extlinux. + +* Thu Jan 25 2007 H. Peter Anvin +- Hacks to make the 32-bit version build correctly on 64-bit machines. + +* Mon Sep 19 2006 H. Peter Anvin +- Add a syslinux-tftpboot module. +- Factor extlinux into its own package. +- Move to %{_datadir} (/usr/share). + +* Wed Sep 21 2005 H. Peter Anvin +- If /boot/extlinux.conf exist, run extlinux --update. + +* Fri Sep 9 2005 H. Peter Anvin +- Copy, don't link, *.c32 into /boot; rpm doesn't like breaking links. + +* Tue Aug 23 2005 H. Peter Anvin +- Put *.c32 into /boot. + +* Thu Dec 30 2004 H. Peter Anvin +- libsyslinux dropped in syslinux 3.00. +- Additional documentation. +- Add extlinux. + +* Tue Dec 14 2004 H. Peter Anvin +- Add a devel package for the com32 library added in 2.12. + +* Wed Apr 16 2003 H. Peter Anvin 2.04-1 +- 2.04 release +- Add support for libsyslinux.so* +- Templatize for inclusion in CVS tree + +* Thu Apr 10 2003 H. Peter Anvin +- 2.03 release +- Add support for libsyslinux.a +- Add keytab-lilo.pl to the /usr/lib/syslinux directory +- Modernize syntax +- Support building on x86-64 + +* Thu Feb 13 2003 H. Peter Anvin +- 2.02 release; no longer setuid + +* Thu Jan 30 2003 H. Peter Anvin +- Prepare for 2.01 release; make /usr/bin/syslinux setuid root + +* Fri Oct 25 2002 H. Peter Anvin +- Upgrade to 2.00. + +* Tue Aug 27 2002 H. Peter Anvin +- Upgrade to 1.76. + +* Fri Jun 14 2002 H. Peter Anvin +- Upgrade to 1.75. + +* Sat Jun 1 2002 H. Peter Anvin +- Upgrade to 1.74. + +* Sun May 26 2002 H. Peter Anvin +- Upgrade to 1.73. + +* Tue Apr 23 2002 H. Peter Anvin +- Upgrade to 1.72. + +* Wed Apr 17 2002 H. Peter Anvin +- Upgrade to 1.71. +- Update the title. + +* Wed Apr 17 2002 H. Peter Anvin +- Upgrade to 1.70. + +* Sat Feb 3 2002 H. Peter Anvin +- Upgrade to 1.67. + +* Tue Jan 1 2002 H. Peter Anvin +- Upgrade to 1.66. + +* Sat Dec 15 2001 H. Peter Anvin +- Upgrade to 1.65; make appropriate changes. + +* Sat Aug 24 2001 H. Peter Anvin +- Upgrade to 1.64. + +* Mon Aug 6 2001 H. Peter Anvin +- Upgrade to 1.63. +- Use make install since the stock SYSLINUX distribution now supports + INSTALLROOT. + +* Sat Apr 24 2001 H. Peter Anvin +- Upgrade to 1.62. + +* Sat Apr 14 2001 H. Peter Anvin +- Fix missing %files; correct modes. + +* Fri Apr 13 2001 H. Peter Anvin +- Upgrade to 1.61 +- Install auxilliary programs in /usr/lib/syslinux + +* Sat Feb 10 2001 Matt Wilson +- 1.52 + +* Wed Jan 24 2001 Matt Wilson +- 1.51pre7 + +* Mon Jan 22 2001 Matt Wilson +- 1.51pre5 + +* Fri Jan 19 2001 Matt Wilson +- 1.51pre3, with e820 detection + +* Tue Dec 12 2000 Than Ngo +- rebuilt with fixed fileutils + +* Thu Nov 9 2000 Than Ngo +- update to 1.49 +- update ftp site +- clean up specfile +- add some useful documents + +* Tue Jul 18 2000 Nalin Dahyabhai +- add %%defattr (release 4) + +* Wed Jul 12 2000 Prospector +- automatic rebuild + +* Thu Jul 06 2000 Trond Eivind Glomsrød +- use %%{_tmppath} +- change application group (Applications/Internet doesn't seem + right to me) +- added BuildRequires + +* Tue Apr 04 2000 Erik Troan +- initial packaging diff --git a/utils/Makefile b/utils/Makefile new file mode 100644 index 0000000..9df9595 --- /dev/null +++ b/utils/Makefile @@ -0,0 +1,60 @@ +## ----------------------------------------------------------------------- +## +## Copyright 1998-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +# +# SYSLINUX utilities +# + +topdir = .. +include $(topdir)/MCONFIG + +CFLAGS = -W -Wall -Os -fomit-frame-pointer -D_FILE_OFFSET_BITS=64 +LDFLAGS = -O2 -s + +TARGETS = mkdiskimage isohybrid gethostip +ASIS = keytab-lilo lss16toppm md5pass ppmtolss16 sha1pass syslinux2ansi + +ISOHDPFX = ../mbr/isohdpfx.bin ../mbr/isohdpfx_f.bin ../mbr/isohdpfx_c.bin \ + ../mbr/isohdppx.bin ../mbr/isohdppx_f.bin ../mbr/isohdppx_c.bin + +all: $(TARGETS) + +%.o: %.c + $(CC) $(UMAKEDEPS) $(CFLAGS) -c -o $@ $< + +mkdiskimage: mkdiskimage.in ../mbr/mbr.bin bin2hex.pl + $(PERL) bin2hex.pl < ../mbr/mbr.bin | cat mkdiskimage.in - > $@ + chmod a+x $@ + +isohybrid: isohybrid.in $(ISOHDPFX) bin2hex.pl + cp -f isohybrid.in $@ + for f in $(ISOHDPFX) ; do $(PERL) bin2hex.pl < $$f >> $@ ; done + chmod a+x $@ + +gethostip: gethostip.o + $(CC) $(LDFLAGS) -o $@ $^ + +tidy dist: + rm -f *.o .*.d + +clean: tidy + rm -f $(TARGETS) + +spotless: clean + +installer: all + +install: installer + mkdir -m 755 -p $(INSTALLROOT)$(BINDIR) + install -m 755 $(TARGETS) $(ASIS) $(INSTALLROOT)$(BINDIR) + +-include .*.d diff --git a/utils/bin2hex.pl b/utils/bin2hex.pl new file mode 100644 index 0000000..778b699 --- /dev/null +++ b/utils/bin2hex.pl @@ -0,0 +1,48 @@ +#!/usr/bin/perl +## ----------------------------------------------------------------------- +## +## Copyright 2003-2008 H. Peter Anvin - All Rights Reserved +## +## Permission is hereby granted, free of charge, to any person +## obtaining a copy of this software and associated documentation +## files (the "Software"), to deal in the Software without +## restriction, including without limitation the rights to use, +## copy, modify, merge, publish, distribute, sublicense, and/or +## sell copies of the Software, and to permit persons to whom +## the Software is furnished to do so, subject to the following +## conditions: +## +## The above copyright notice and this permission notice shall +## be included in all copies or substantial portions of the Software. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +## OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +## HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +## WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +## FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +## OTHER DEALINGS IN THE SOFTWARE. +## +## ----------------------------------------------------------------------- + +eval { use bytes; }; +eval { binmode STDIN; }; + +$len = 0; +while ( read(STDIN,$ch,1) ) { + $cc = ord($ch); + $s = sprintf("%x", $cc); + print $s; + $len += length($s); + if ( $len > 72 ) { + print "\n"; + $len = 0; + } else { + print " "; + $len++; + } +} +print "\n" if ( $len ); +print "*\n"; +exit 0; diff --git a/utils/gethostip b/utils/gethostip new file mode 100755 index 0000000000000000000000000000000000000000..78bef7f6aa5486bd1fa7b6ba987d01ca080717a1 GIT binary patch literal 4336 zcmds4e{56N6~1;}z+|SlA?YYY<+XLTu#ra+lJTQsq6vXSq%@ELVJS5)d3K(eV+X%y zYN|+tzNSs&WU|_<)u#PH*G{T5wpH3zQMH;tiGWUsq5ZXWsOr>pbCgs=9ix%Pc>BI* zKk9l++Wy^5&O7Ja^PO|g{e6A1y=$k(;}LH0iZVfM^jXQ*44(DpEzLp}K2axX#2T># zRkSsa0t0d!;8cz}2~oGpN5Te2|9^(wn6Fuz5{KOc0eb&w(gHi%@2S7gybXDz(#WIdtrzAZ^ezjAr|f5 z5Yy^o(ReDY7zw2*?~Bx&zU|tx-|=Oh^g&Zj>?&}M4ejZlEnV~b$Nuq?@2ZhM{Gy}% zZ$Daj`~})7KpY>Ag;%b42+XPDG@RM};khlJWNJ3fep<=;;zA#{$Pijw1)Sia68%t# z9x2hoCHn3XeOrmXu0&rAec^e@$1%NEtZ>HvweP$7{X#+g=^|Z(LW4tzc*ro-q!|hc z4DkSm_G=>4`8aB|XgriM!kP%DqozmjG(AJO|jf-7fovm%5CZ!?Y4KLPM@t?=hJeV2IF^MG* zjV|YEJegO;6X?$pOa%K?jtOIb%fBE*416i-hsm)hOma+inj8Z(LXHU^CC7q!mVBuY z&Vw(YUsn&r6%P@CI^W^$JhySMM%8M{S; zr(LV#OCP(IndlVfK7gBD%Zzl|=B_$AGt=3{?*J32Gg>!@cP zotf(#!?{sMXAktv)Q26NJ<+#QKLQ9cUGM(pKD`0!McxzDzvgTMNEdvcQg=Gb_)s4#KjFQjm4_)puvI)~`14J2!~aGrC2y=~jQwi&F+RU_-EuAqco*~f0}KX{<=X3t)0VO7SHIV4T&2nI4` zuhM(I(^}NchLe74Q8QOti;>h<=OM?QV6`oMz}{S>@YU;pvN9YLO20(HK*KSO}Cy(w4egbs|Io0v)Y^HnQQ zHNVzM`@E?oR@yI_a<{!nuR%720lM-}+eWw`PpY|Th8hX_#aq=tn5(<(jXEWLCqB6s z0ex#XBzA2i=o4mrrY(>Oba$_Kv$c#LFFzi9yxn{C$LJIK%t!w80*@WD&T;DLxvNtj zoxTDm4iUz(BKI-25*lo`E=rHYf`Jv20SplauF9HSb*S&j(BUcd)(qAMmW%nNqV;m{ zKu_**w0CNz8n+x`<$4fl>TQ|pz~Xn^#q+jz43pA$Gt(a6SZ3P&U{a>tHz5V>>7Ns2 zUGQwVoqXS#^|P^=Oigemn=P#OS9lL%Ce4*^Q;KA*dC9(-DdXhc$gkl$*c@`<`7n-i zKX)0a9Ku<^yM?D1%YH*03~!Zd4Y{vgOUEOJWuBKjEjL7m6-B8KS~?QfMXj+_-jk3! zyN@@^s-`8wh9Rqlkr;@oW>}M-ikiAiuL>Y*4Iz`I6!)K6LsM@t;h;{dT6iEjq{f7n zFcGExSW4AIBo&L*CsHO3bm5%ZdN?*L;+P^QVSLh{WRcDb`sot=xgwqS*$YK_9Qw;e zx(>axc-E9X!o!ZE6vT3N-Z-=adM{yK@e@oORqGJcOM)-;#W|WW;5hG=INsCnoWp zVeIi|aMysJh0Zs_1PE7+<5vyN!Um3DSF=AIAok&2&=)}(;!^Gc=lg*D;2VttjxHzG zqaHaWn9FtWO6U;(=fd}y>+}g?c=4{R1U&?50=0tnf%-v7kb~XT*0xoy!!v1}+^lR= zn&hU2hRuzQ4J~rrJ}kTr)y&t|Z^EtLZ#f=L8qq}jb4m!dWH?5AUKoy+?~bO6mI5C$K`iJgzr zZvZ`z7#a%4O?RRE7o2OIYm;l+`JDlU>(?!`v9ONUsIv$7djJL*x?6ZrusjHIaXN51 za6!kC|F}4w2{*AH7>|2#61eq{oxKlE9O)Y%=13g(=vm;dLgt=@=wP_VcY?T2iQ}Fg zhrqq;7GhcUfVdxt<6f+U6W`cwyR0Ymfw+Gua}TcvPKI3EE>v+8viB=ay;FqagER}A+ZIfB z +#include +#include +#include +#include +#include +#define _GNU_SOURCE /* For getopt_long */ +#include + +const struct option options[] = +{ + { "hexadecimal", 0, NULL, 'x' }, + { "decimal", 0, NULL, 'd' }, + { "dotted-quad", 0, NULL, 'd' }, + { "full-output", 0, NULL, 'f' }, + { "name", 0, NULL, 'n' }, + { "help", 0, NULL, 'h' }, + { NULL, 0, NULL, 0 } +}; + +const char *program; + +void usage(int exit_code) +{ + fprintf(stderr, "Usage: %s [-dxnf] hostname/ip...\n", program); + exit(exit_code); +} + +int main(int argc, char *argv[]) +{ + int opt; + int output = 0; + int i; + char *sep; + int err = 0; + + struct hostent *host; + + program = argv[0]; + + while ( (opt = getopt_long(argc, argv, "dxfnh", options, NULL)) != -1 ) { + switch ( opt ) { + case 'd': + output |= 2; /* Decimal output */ + break; + case 'x': + output |= 4; /* Hexadecimal output */ + break; + case 'n': + output |= 1; /* Canonical name output */ + break; + case 'f': + output = 7; /* Full output */ + break; + case 'h': + usage(0); + break; + default: + usage(EX_USAGE); + break; + } + } + + if ( optind == argc ) + usage(EX_USAGE); + + if ( output == 0 ) + output = 7; /* Default output */ + + for ( i = optind ; i < argc ; i++ ) { + sep = ""; + host = gethostbyname(argv[i]); + if ( !host ) { + herror(argv[i]); + err = 1; + continue; + } + + if ( host->h_addrtype != AF_INET || host->h_length != 4 ) { + fprintf(stderr, "%s: No IPv4 address associated with name\n", argv[i]); + err = 1; + continue; + } + + if ( output & 1 ) { + printf("%s%s", sep, host->h_name); + sep = " "; + } + + if ( output & 2 ) { + printf("%s%u.%u.%u.%u", sep, + ((unsigned char *)host->h_addr)[0], + ((unsigned char *)host->h_addr)[1], + ((unsigned char *)host->h_addr)[2], + ((unsigned char *)host->h_addr)[3]); + sep = " "; + } + + if ( output & 4 ) { + printf("%s%02X%02X%02X%02X", sep, + ((unsigned char *)host->h_addr)[0], + ((unsigned char *)host->h_addr)[1], + ((unsigned char *)host->h_addr)[2], + ((unsigned char *)host->h_addr)[3]); + sep = " "; + } + + putchar('\n'); + } + + return err; +} diff --git a/utils/isohybrid b/utils/isohybrid new file mode 100755 index 0000000..5086882 --- /dev/null +++ b/utils/isohybrid @@ -0,0 +1,358 @@ +#!/usr/bin/perl +## ----------------------------------------------------------------------- +## +## Copyright 2002-2008 H. Peter Anvin - All Rights Reserved +## Copyright 2009 Intel Corporation; author: H. Peter Anvin +## +## 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +# +# Post-process an ISO 9660 image generated with mkisofs/genisoimage +# to allow "hybrid booting" as a CD-ROM or as a hard disk. +# + +use bytes; +use Fcntl; + +# User-specifyable options +%opt = ( + # Fake geometry (zipdrive-style...) + 'h' => 64, + 's' => 32, + # Partition number + 'entry' => 1, + # Partition offset + 'offset' => 0, + # Partition type + 'type' => 0x17, # "Windows hidden IFS" + # MBR ID + 'id' => undef, +); + +%valid_range = ( + 'h' => [1, 256], + 's' => [1, 63], + 'entry' => [1, 4], + 'offset' => [0, 64], + 'type' => [0, 255], + 'id' => [0, 0xffffffff], + 'hd0' => [0, 2], + 'partok' => [0, 1], +); + +# Boolean options just set other options +%bool_opt = ( + 'nohd0' => ['hd0', 0], + 'forcehd0' => ['hd0', 1], + 'ctrlhd0' => ['hd0', 2], + 'nopartok' => ['partok', 0], + 'partok' => ['partok', 1], +); + +sub usage() { + print STDERR "Usage: $0 [options] filename.iso\n", + "Options:\n", + " -h Number of default geometry heads\n", + " -s Number of default geometry sectors\n", + " -entry Specify partition entry number (1-4)\n", + " -offset Specify partition offset (default 0)\n", + " -type Specify partition type (default 0x17)\n", + " -id Specify MBR ID (default random)\n", + " -forcehd0 Always assume we are loaded as disk ID 0\n", + " -ctrlhd0 Assume disk ID 0 if the Ctrl key is pressed\n", + " -partok Allow booting from within a partition\n"; + exit 1; +} + +# Parse a C-style integer (decimal/octal/hex) +sub doh($) { + my($n) = @_; + return ($n =~ /^0/) ? oct $n : $n+0; +} + +sub get_random() { + # Get a 32-bit random number + my $rfd, $rnd; + my $rid; + + if (open($rfd, "< /dev/urandom\0") && read($rfd, $rnd, 4) == 4) { + $rid = unpack("V", $rnd); + } + + close($rfd) if (defined($rfd)); + return $rid if (defined($rid)); + + # This sucks but is better than nothing... + return ($$+time()) & 0xffffffff; +} + +sub get_hex_data() { + my $mbr = ''; + my $line, $byte; + while ( $line = ) { + chomp $line; + last if ($line eq '*'); + foreach $byte ( split(/\s+/, $line) ) { + $mbr .= chr(hex($byte)); + } + } + return $mbr; +} + +while ($ARGV[0] =~ /^\-(.*)$/) { + $o = $1; + shift @ARGV; + if (defined($bool_opt{$o})) { + ($o, $v) = @{$bool_opt{$o}}; + $opt{$o} = $v; + } elsif (exists($opt{$o})) { + $opt{$o} = doh(shift @ARGV); + if (defined($valid_range{$o})) { + ($l, $h) = @{$valid_range{$o}}; + if ($opt{$o} < $l || $opt{$o} > $h) { + die "$0: valid values for the -$o parameter are $l to $h\n"; + } + } + } else { + usage(); + } +} + +($file) = @ARGV; + +if (!defined($file)) { + usage(); +} + +open(FILE, "+< $file\0") or die "$0: cannot open $file: $!\n"; +binmode FILE; + +# +# First, actually figure out where mkisofs hid isolinux.bin +# +seek(FILE, 17*2048, SEEK_SET) or die "$0: $file: $!\n"; +read(FILE, $boot_record, 2048) == 2048 or die "$0: $file: read error\n"; +($br_sign, $br_cat_offset) = unpack("a71V", $boot_record); +if ($br_sign ne ("\0CD001\1EL TORITO SPECIFICATION" . ("\0" x 41))) { + die "$0: $file: no boot record found\n"; +} +seek(FILE, $br_cat_offset*2048, SEEK_SET) or die "$0: $file: $!\n"; +read(FILE, $boot_cat, 2048) == 2048 or die "$0: $file: read error\n"; + +# We must have a Validation Entry followed by a Default Entry... +# no fanciness allowed for the Hybrid mode [XXX: might relax this later] +@ve = unpack("v16", $boot_cat); +$cs = 0; +for ($i = 0; $i < 16; $i++) { + $cs += $ve[$i]; +} +if ($ve[0] != 0x0001 || $ve[15] != 0xaa55 || $cs & 0xffff) { + die "$0: $file: invalid boot catalog\n"; +} +($de_boot, $de_media, $de_seg, $de_sys, $de_mbz1, $de_count, + $de_lba, $de_mbz2) = unpack("CCvCCvVv", substr($boot_cat, 32, 32)); +if ($de_boot != 0x88 || $de_media != 0 || + ($de_segment != 0 && $de_segment != 0x7c0) || $de_count != 4) { + die "$0: $file: unexpected boot catalog parameters\n"; +} + +# Now $de_lba should contain the CD sector number for isolinux.bin +seek(FILE, $de_lba*2048+0x40, SEEK_SET) or die "$0: $file: $!\n"; +read(FILE, $ibsig, 4); +if ($ibsig ne "\xfb\xc0\x78\x70") { + die "$0: $file: bootloader does not have a isolinux.bin hybrid signature.". + "Note that isolinux-debug.bin does not support hybrid booting.\n"; +} + +# Get the total size of the image +(@imgstat = stat(FILE)) or die "$0: $file: $!\n"; +$imgsize = $imgstat[7]; +if (!$imgsize) { + die "$0: $file: cannot determine length of file\n"; +} +# Target image size: round up to a multiple of $h*$s*512 +$h = $opt{'h'}; +$s = $opt{'s'}; +$cylsize = $h*$s*512; +$frac = $imgsize % $cylsize; +$padding = ($frac > 0) ? $cylsize - $frac : 0; +$imgsize += $padding; +$c = int($imgsize/$cylsize); +if ($c > 1024) { + print STDERR "Warning: more than 1024 cylinders ($c).\n"; + print STDERR "Not all BIOSes will be able to boot this device.\n"; + $cc = 1024; +} else { + $cc = $c; +} + +# Preserve id when run again +if (defined($opt{'id'})) { + $id = $opt{'id'}; +} else { + seek(FILE, 440, SEEK_SET) or die "$0: $file: $!\n"; + read(FILE, $id, 4); + if ($id eq "\x00\x00\x00\x00") { + $id = pack("V", get_random()); + } +} + +# Print the MBR and partition table +seek(FILE, 0, SEEK_SET) or die "$0: $file: $!\n"; + +for ($i = 0; $i <= $opt{'hd0'}+3*$opt{'partok'}; $i++) { + $mbr = get_hex_data(); +} +if ( length($mbr) > 432 ) { + die "$0: Bad MBR code\n"; +} + +$mbr .= "\0" x (432 - length($mbr)); + +$mbr .= pack("VV", $de_lba*4, 0); # Offset 432: LBA of isolinux.bin +$mbr .= $id; # Offset 440: MBR ID +$mbr .= "\0\0"; # Offset 446: actual partition table + +# Print partition table +$offset = $opt{'offset'}; +$psize = $c*$h*$s; +$bhead = int($offset/$s) % $h; +$bsect = ($offset % $s) + 1; +$bcyl = int($offset/($h*$s)); +$bsect += ($bcyl & 0x300) >> 2; +$bcyl &= 0xff; +$ehead = $h-1; +$esect = $s + ((($cc-1) & 0x300) >> 2); +$ecyl = ($cc-1) & 0xff; +$fstype = $opt{'type'}; # Partition type +$pentry = $opt{'entry'}; # Partition slot + +for ( $i = 1 ; $i <= 4 ; $i++ ) { + if ( $i == $pentry ) { + $mbr .= pack("CCCCCCCCVV", 0x80, $bhead, $bsect, $bcyl, $fstype, + $ehead, $esect, $ecyl, 0, $psize); + } else { + $mbr .= "\0" x 16; + } +} +$mbr .= "\x55\xaa"; + +print FILE $mbr; + +# Pad the image to a fake cylinder boundary +seek(FILE, $imgstat[7], SEEK_SET) or die "$0: $file: $!\n"; +if ($padding) { + print FILE "\0" x $padding; +} + +# Done... +close(FILE); + +exit 0; +__END__ +fa 31 ed 8e d5 bc 0 7c fb fc 66 31 db 66 31 c9 66 53 66 51 6 57 8e dd 8e c5 +52 be 0 7c bf 0 6 b9 0 1 f3 a5 ea 2b 6 0 0 52 b4 41 bb aa 55 31 c9 30 f6 f9 +cd 13 72 16 81 fb 55 aa 75 10 83 e1 1 74 b 66 c7 6 d1 6 b4 42 eb 15 eb 0 5a +51 b4 8 cd 13 83 e1 3f 5b 51 f b6 c6 40 50 f7 e1 53 52 50 bb 0 7c b9 4 0 66 +a1 b0 7 e8 44 0 f 82 80 0 66 40 80 c7 2 e2 f2 66 81 3e 40 7c fb c0 78 70 75 +9 fa bc ec 7b ea 44 7c 0 0 e8 83 0 69 73 6f 6c 69 6e 75 78 2e 62 69 6e 20 +6d 69 73 73 69 6e 67 20 6f 72 20 63 6f 72 72 75 70 74 2e d a 66 60 66 31 d2 +66 3 6 f8 7b 66 13 16 fc 7b 66 52 66 50 6 53 6a 1 6a 10 89 e6 66 f7 36 e8 +7b c0 e4 6 88 e1 88 c5 92 f6 36 ee 7b 88 c6 8 e1 41 b8 1 2 8a 16 f2 7b cd +13 8d 64 10 66 61 c3 e8 1e 0 4f 70 65 72 61 74 69 6e 67 20 73 79 73 74 65 +6d 20 6c 6f 61 64 20 65 72 72 6f 72 2e d a 5e ac b4 e 8a 3e 62 4 b3 7 cd 10 +3c a 75 f1 cd 18 f4 eb fd 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +* +fa 31 ed 8e d5 bc 0 7c fb fc 66 31 db 66 31 c9 66 53 66 51 6 57 8e dd 8e c5 +b2 80 52 be 0 7c bf 0 6 b9 0 1 f3 a5 ea 2d 6 0 0 52 b4 41 bb aa 55 31 c9 30 +f6 f9 cd 13 72 16 81 fb 55 aa 75 10 83 e1 1 74 b 66 c7 6 d3 6 b4 42 eb 15 +eb 0 5a 51 b4 8 cd 13 83 e1 3f 5b 51 f b6 c6 40 50 f7 e1 53 52 50 bb 0 7c +b9 4 0 66 a1 b0 7 e8 44 0 f 82 80 0 66 40 80 c7 2 e2 f2 66 81 3e 40 7c fb +c0 78 70 75 9 fa bc ec 7b ea 44 7c 0 0 e8 83 0 69 73 6f 6c 69 6e 75 78 2e +62 69 6e 20 6d 69 73 73 69 6e 67 20 6f 72 20 63 6f 72 72 75 70 74 2e d a 66 +60 66 31 d2 66 3 6 f8 7b 66 13 16 fc 7b 66 52 66 50 6 53 6a 1 6a 10 89 e6 +66 f7 36 e8 7b c0 e4 6 88 e1 88 c5 92 f6 36 ee 7b 88 c6 8 e1 41 b8 1 2 8a +16 f2 7b cd 13 8d 64 10 66 61 c3 e8 1e 0 4f 70 65 72 61 74 69 6e 67 20 73 +79 73 74 65 6d 20 6c 6f 61 64 20 65 72 72 6f 72 2e d a 5e ac b4 e 8a 3e 62 +4 b3 7 cd 10 3c a 75 f1 cd 18 f4 eb fd 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 +* +fa 31 ed 8e d5 bc 0 7c fb fc 66 31 db 66 31 c9 66 53 66 51 6 57 8e dd 8e c5 +f6 6 17 4 4 74 2 b2 80 52 be 0 7c bf 0 6 b9 0 1 f3 a5 ea 34 6 0 0 52 b4 41 +bb aa 55 31 c9 30 f6 f9 cd 13 72 16 81 fb 55 aa 75 10 83 e1 1 74 b 66 c7 6 +da 6 b4 42 eb 15 eb 0 5a 51 b4 8 cd 13 83 e1 3f 5b 51 f b6 c6 40 50 f7 e1 +53 52 50 bb 0 7c b9 4 0 66 a1 b0 7 e8 44 0 f 82 80 0 66 40 80 c7 2 e2 f2 66 +81 3e 40 7c fb c0 78 70 75 9 fa bc ec 7b ea 44 7c 0 0 e8 83 0 69 73 6f 6c +69 6e 75 78 2e 62 69 6e 20 6d 69 73 73 69 6e 67 20 6f 72 20 63 6f 72 72 75 +70 74 2e d a 66 60 66 31 d2 66 3 6 f8 7b 66 13 16 fc 7b 66 52 66 50 6 53 6a +1 6a 10 89 e6 66 f7 36 e8 7b c0 e4 6 88 e1 88 c5 92 f6 36 ee 7b 88 c6 8 e1 +41 b8 1 2 8a 16 f2 7b cd 13 8d 64 10 66 61 c3 e8 1e 0 4f 70 65 72 61 74 69 +6e 67 20 73 79 73 74 65 6d 20 6c 6f 61 64 20 65 72 72 6f 72 2e d a 5e ac b4 +e 8a 3e 62 4 b3 7 cd 10 3c a 75 f1 cd 18 f4 eb fd 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +* +fa 31 ed 8e d5 bc 0 7c fb fc 66 31 db 66 31 c9 21 f6 74 21 f6 4 7f 75 1c 66 +3d 21 47 50 58 75 10 80 7c 4 ee 75 a 66 8b 4c 30 66 8b 5c 34 eb 4 66 8b 4c +8 66 53 66 51 6 57 8e dd 8e c5 52 be 0 7c bf 0 6 b9 0 1 f3 a5 ea 50 6 0 0 +52 b4 41 bb aa 55 31 c9 30 f6 f9 cd 13 72 16 81 fb 55 aa 75 10 83 e1 1 74 +b 66 c7 6 f6 6 b4 42 eb 15 eb 0 5a 51 b4 8 cd 13 83 e1 3f 5b 51 f b6 c6 40 +50 f7 e1 53 52 50 bb 0 7c b9 4 0 66 a1 b0 7 e8 44 0 f 82 80 0 66 40 80 c7 +2 e2 f2 66 81 3e 40 7c fb c0 78 70 75 9 fa bc ec 7b ea 44 7c 0 0 e8 83 0 69 +73 6f 6c 69 6e 75 78 2e 62 69 6e 20 6d 69 73 73 69 6e 67 20 6f 72 20 63 6f +72 72 75 70 74 2e d a 66 60 66 31 d2 66 3 6 f8 7b 66 13 16 fc 7b 66 52 66 +50 6 53 6a 1 6a 10 89 e6 66 f7 36 e8 7b c0 e4 6 88 e1 88 c5 92 f6 36 ee 7b +88 c6 8 e1 41 b8 1 2 8a 16 f2 7b cd 13 8d 64 10 66 61 c3 e8 1e 0 4f 70 65 +72 61 74 69 6e 67 20 73 79 73 74 65 6d 20 6c 6f 61 64 20 65 72 72 6f 72 2e +d a 5e ac b4 e 8a 3e 62 4 b3 7 cd 10 3c a 75 f1 cd 18 f4 eb fd 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +* +fa 31 ed 8e d5 bc 0 7c fb fc 66 31 db 66 31 c9 21 f6 74 21 f6 4 7f 75 1c 66 +3d 21 47 50 58 75 10 80 7c 4 ee 75 a 66 8b 4c 30 66 8b 5c 34 eb 4 66 8b 4c +8 66 53 66 51 6 57 8e dd 8e c5 b2 80 52 be 0 7c bf 0 6 b9 0 1 f3 a5 ea 52 +6 0 0 52 b4 41 bb aa 55 31 c9 30 f6 f9 cd 13 72 16 81 fb 55 aa 75 10 83 e1 +1 74 b 66 c7 6 f8 6 b4 42 eb 15 eb 0 5a 51 b4 8 cd 13 83 e1 3f 5b 51 f b6 +c6 40 50 f7 e1 53 52 50 bb 0 7c b9 4 0 66 a1 b0 7 e8 44 0 f 82 80 0 66 40 +80 c7 2 e2 f2 66 81 3e 40 7c fb c0 78 70 75 9 fa bc ec 7b ea 44 7c 0 0 e8 +83 0 69 73 6f 6c 69 6e 75 78 2e 62 69 6e 20 6d 69 73 73 69 6e 67 20 6f 72 +20 63 6f 72 72 75 70 74 2e d a 66 60 66 31 d2 66 3 6 f8 7b 66 13 16 fc 7b +66 52 66 50 6 53 6a 1 6a 10 89 e6 66 f7 36 e8 7b c0 e4 6 88 e1 88 c5 92 f6 +36 ee 7b 88 c6 8 e1 41 b8 1 2 8a 16 f2 7b cd 13 8d 64 10 66 61 c3 e8 1e 0 +4f 70 65 72 61 74 69 6e 67 20 73 79 73 74 65 6d 20 6c 6f 61 64 20 65 72 72 +6f 72 2e d a 5e ac b4 e 8a 3e 62 4 b3 7 cd 10 3c a 75 f1 cd 18 f4 eb fd 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +* +fa 31 ed 8e d5 bc 0 7c fb fc 66 31 db 66 31 c9 21 f6 74 21 f6 4 7f 75 1c 66 +3d 21 47 50 58 75 10 80 7c 4 ee 75 a 66 8b 4c 30 66 8b 5c 34 eb 4 66 8b 4c +8 66 53 66 51 6 57 8e dd 8e c5 f6 6 17 4 4 74 2 b2 80 52 be 0 7c bf 0 6 b9 +0 1 f3 a5 ea 59 6 0 0 52 b4 41 bb aa 55 31 c9 30 f6 f9 cd 13 72 16 81 fb 55 +aa 75 10 83 e1 1 74 b 66 c7 6 ff 6 b4 42 eb 15 eb 0 5a 51 b4 8 cd 13 83 e1 +3f 5b 51 f b6 c6 40 50 f7 e1 53 52 50 bb 0 7c b9 4 0 66 a1 b0 7 e8 44 0 f +82 80 0 66 40 80 c7 2 e2 f2 66 81 3e 40 7c fb c0 78 70 75 9 fa bc ec 7b ea +44 7c 0 0 e8 83 0 69 73 6f 6c 69 6e 75 78 2e 62 69 6e 20 6d 69 73 73 69 6e +67 20 6f 72 20 63 6f 72 72 75 70 74 2e d a 66 60 66 31 d2 66 3 6 f8 7b 66 +13 16 fc 7b 66 52 66 50 6 53 6a 1 6a 10 89 e6 66 f7 36 e8 7b c0 e4 6 88 e1 +88 c5 92 f6 36 ee 7b 88 c6 8 e1 41 b8 1 2 8a 16 f2 7b cd 13 8d 64 10 66 61 +c3 e8 1e 0 4f 70 65 72 61 74 69 6e 67 20 73 79 73 74 65 6d 20 6c 6f 61 64 +20 65 72 72 6f 72 2e d a 5e ac b4 e 8a 3e 62 4 b3 7 cd 10 3c a 75 f1 cd 18 +f4 eb fd 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +* diff --git a/utils/isohybrid.in b/utils/isohybrid.in new file mode 100644 index 0000000..a92b572 --- /dev/null +++ b/utils/isohybrid.in @@ -0,0 +1,258 @@ +#!/usr/bin/perl +## ----------------------------------------------------------------------- +## +## Copyright 2002-2008 H. Peter Anvin - All Rights Reserved +## Copyright 2009 Intel Corporation; author: H. Peter Anvin +## +## 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +# +# Post-process an ISO 9660 image generated with mkisofs/genisoimage +# to allow "hybrid booting" as a CD-ROM or as a hard disk. +# + +use bytes; +use Fcntl; + +# User-specifyable options +%opt = ( + # Fake geometry (zipdrive-style...) + 'h' => 64, + 's' => 32, + # Partition number + 'entry' => 1, + # Partition offset + 'offset' => 0, + # Partition type + 'type' => 0x17, # "Windows hidden IFS" + # MBR ID + 'id' => undef, +); + +%valid_range = ( + 'h' => [1, 256], + 's' => [1, 63], + 'entry' => [1, 4], + 'offset' => [0, 64], + 'type' => [0, 255], + 'id' => [0, 0xffffffff], + 'hd0' => [0, 2], + 'partok' => [0, 1], +); + +# Boolean options just set other options +%bool_opt = ( + 'nohd0' => ['hd0', 0], + 'forcehd0' => ['hd0', 1], + 'ctrlhd0' => ['hd0', 2], + 'nopartok' => ['partok', 0], + 'partok' => ['partok', 1], +); + +sub usage() { + print STDERR "Usage: $0 [options] filename.iso\n", + "Options:\n", + " -h Number of default geometry heads\n", + " -s Number of default geometry sectors\n", + " -entry Specify partition entry number (1-4)\n", + " -offset Specify partition offset (default 0)\n", + " -type Specify partition type (default 0x17)\n", + " -id Specify MBR ID (default random)\n", + " -forcehd0 Always assume we are loaded as disk ID 0\n", + " -ctrlhd0 Assume disk ID 0 if the Ctrl key is pressed\n", + " -partok Allow booting from within a partition\n"; + exit 1; +} + +# Parse a C-style integer (decimal/octal/hex) +sub doh($) { + my($n) = @_; + return ($n =~ /^0/) ? oct $n : $n+0; +} + +sub get_random() { + # Get a 32-bit random number + my $rfd, $rnd; + my $rid; + + if (open($rfd, "< /dev/urandom\0") && read($rfd, $rnd, 4) == 4) { + $rid = unpack("V", $rnd); + } + + close($rfd) if (defined($rfd)); + return $rid if (defined($rid)); + + # This sucks but is better than nothing... + return ($$+time()) & 0xffffffff; +} + +sub get_hex_data() { + my $mbr = ''; + my $line, $byte; + while ( $line = ) { + chomp $line; + last if ($line eq '*'); + foreach $byte ( split(/\s+/, $line) ) { + $mbr .= chr(hex($byte)); + } + } + return $mbr; +} + +while ($ARGV[0] =~ /^\-(.*)$/) { + $o = $1; + shift @ARGV; + if (defined($bool_opt{$o})) { + ($o, $v) = @{$bool_opt{$o}}; + $opt{$o} = $v; + } elsif (exists($opt{$o})) { + $opt{$o} = doh(shift @ARGV); + if (defined($valid_range{$o})) { + ($l, $h) = @{$valid_range{$o}}; + if ($opt{$o} < $l || $opt{$o} > $h) { + die "$0: valid values for the -$o parameter are $l to $h\n"; + } + } + } else { + usage(); + } +} + +($file) = @ARGV; + +if (!defined($file)) { + usage(); +} + +open(FILE, "+< $file\0") or die "$0: cannot open $file: $!\n"; +binmode FILE; + +# +# First, actually figure out where mkisofs hid isolinux.bin +# +seek(FILE, 17*2048, SEEK_SET) or die "$0: $file: $!\n"; +read(FILE, $boot_record, 2048) == 2048 or die "$0: $file: read error\n"; +($br_sign, $br_cat_offset) = unpack("a71V", $boot_record); +if ($br_sign ne ("\0CD001\1EL TORITO SPECIFICATION" . ("\0" x 41))) { + die "$0: $file: no boot record found\n"; +} +seek(FILE, $br_cat_offset*2048, SEEK_SET) or die "$0: $file: $!\n"; +read(FILE, $boot_cat, 2048) == 2048 or die "$0: $file: read error\n"; + +# We must have a Validation Entry followed by a Default Entry... +# no fanciness allowed for the Hybrid mode [XXX: might relax this later] +@ve = unpack("v16", $boot_cat); +$cs = 0; +for ($i = 0; $i < 16; $i++) { + $cs += $ve[$i]; +} +if ($ve[0] != 0x0001 || $ve[15] != 0xaa55 || $cs & 0xffff) { + die "$0: $file: invalid boot catalog\n"; +} +($de_boot, $de_media, $de_seg, $de_sys, $de_mbz1, $de_count, + $de_lba, $de_mbz2) = unpack("CCvCCvVv", substr($boot_cat, 32, 32)); +if ($de_boot != 0x88 || $de_media != 0 || + ($de_segment != 0 && $de_segment != 0x7c0) || $de_count != 4) { + die "$0: $file: unexpected boot catalog parameters\n"; +} + +# Now $de_lba should contain the CD sector number for isolinux.bin +seek(FILE, $de_lba*2048+0x40, SEEK_SET) or die "$0: $file: $!\n"; +read(FILE, $ibsig, 4); +if ($ibsig ne "\xfb\xc0\x78\x70") { + die "$0: $file: bootloader does not have a isolinux.bin hybrid signature.". + "Note that isolinux-debug.bin does not support hybrid booting.\n"; +} + +# Get the total size of the image +(@imgstat = stat(FILE)) or die "$0: $file: $!\n"; +$imgsize = $imgstat[7]; +if (!$imgsize) { + die "$0: $file: cannot determine length of file\n"; +} +# Target image size: round up to a multiple of $h*$s*512 +$h = $opt{'h'}; +$s = $opt{'s'}; +$cylsize = $h*$s*512; +$frac = $imgsize % $cylsize; +$padding = ($frac > 0) ? $cylsize - $frac : 0; +$imgsize += $padding; +$c = int($imgsize/$cylsize); +if ($c > 1024) { + print STDERR "Warning: more than 1024 cylinders ($c).\n"; + print STDERR "Not all BIOSes will be able to boot this device.\n"; + $cc = 1024; +} else { + $cc = $c; +} + +# Preserve id when run again +if (defined($opt{'id'})) { + $id = $opt{'id'}; +} else { + seek(FILE, 440, SEEK_SET) or die "$0: $file: $!\n"; + read(FILE, $id, 4); + if ($id eq "\x00\x00\x00\x00") { + $id = pack("V", get_random()); + } +} + +# Print the MBR and partition table +seek(FILE, 0, SEEK_SET) or die "$0: $file: $!\n"; + +for ($i = 0; $i <= $opt{'hd0'}+3*$opt{'partok'}; $i++) { + $mbr = get_hex_data(); +} +if ( length($mbr) > 432 ) { + die "$0: Bad MBR code\n"; +} + +$mbr .= "\0" x (432 - length($mbr)); + +$mbr .= pack("VV", $de_lba*4, 0); # Offset 432: LBA of isolinux.bin +$mbr .= $id; # Offset 440: MBR ID +$mbr .= "\0\0"; # Offset 446: actual partition table + +# Print partition table +$offset = $opt{'offset'}; +$psize = $c*$h*$s; +$bhead = int($offset/$s) % $h; +$bsect = ($offset % $s) + 1; +$bcyl = int($offset/($h*$s)); +$bsect += ($bcyl & 0x300) >> 2; +$bcyl &= 0xff; +$ehead = $h-1; +$esect = $s + ((($cc-1) & 0x300) >> 2); +$ecyl = ($cc-1) & 0xff; +$fstype = $opt{'type'}; # Partition type +$pentry = $opt{'entry'}; # Partition slot + +for ( $i = 1 ; $i <= 4 ; $i++ ) { + if ( $i == $pentry ) { + $mbr .= pack("CCCCCCCCVV", 0x80, $bhead, $bsect, $bcyl, $fstype, + $ehead, $esect, $ecyl, 0, $psize); + } else { + $mbr .= "\0" x 16; + } +} +$mbr .= "\x55\xaa"; + +print FILE $mbr; + +# Pad the image to a fake cylinder boundary +seek(FILE, $imgstat[7], SEEK_SET) or die "$0: $file: $!\n"; +if ($padding) { + print FILE "\0" x $padding; +} + +# Done... +close(FILE); + +exit 0; +__END__ diff --git a/utils/keytab-lilo b/utils/keytab-lilo new file mode 100755 index 0000000..9e34160 --- /dev/null +++ b/utils/keytab-lilo @@ -0,0 +1,95 @@ +#!/usr/bin/perl + +eval { use bytes; }; +eval { binmode STDOUT; }; + +$DEFAULT_MAP = "us"; +$DEFAULT_EXT = ".kmap"; + +sub usage +{ + print STDERR + "usage: $0 [ -p old_code=new_code ] ...\n". + (" "x(8+length $0))."[path]default_layout[.kmap] ] ". + "[path]kbd_layout[.kmap]\n"; + exit 1; +} + + +while ($ARGV[0] eq "-p") { + shift(@ARGV); + &usage unless $ARGV[0] =~ /=/; + $table[eval($`)] = eval($'); + shift(@ARGV); +} +&usage unless defined $ARGV[0]; +load_map("def",defined $ARGV[1] ? $ARGV[0] : undef); +load_map("kbd",defined $ARGV[1] ? $ARGV[1] : $ARGV[0]); +&build_table("plain","shift","ctrl","altgr","shift_ctrl", + "altgr_ctrl","alt","shift_alt","ctrl_alt"); +for ($i = 0; $i < 256; $i++) { + printf("%c",$table[$i] ? $table[$i] : $i) || die "print: $!"; +} +close STDOUT || die "close: $!"; + + +sub load_map +{ + local ($pfx,$map) = @_; + local ($empty,$current); + + $map = $DEFAULT_MAP unless defined $map; + $map .= $DEFAULT_EXT unless $map =~ m|/[^/]+\.[^/]+$|; + if (!open(FILE,"loadkeys -m $map |")) { + print STDERR "loadkeys -m $map: $!\n"; + exit 1; + } + undef $current; + $empty = 1; + while () { + chop; + if (/^(static\s+)?u_short\s+(\S+)_map\[\S*\]\s+=\s+{\s*$/) { + die "active at beginning of map" if defined $current; + $current = $pfx.":".$2; + next; + } + undef $current if /^};\s*$/; + next unless defined $current; + s/\s//g; + $map{$current} .= $_; + $empty = 0; + } + close FILE; + return unless $empty; + print STDERR "Keymap is empty\n"; + exit 1; +} + + +sub build_table +{ + local (@maps) = @_; + local (@tmp); + + $set = 0; + for $map (@maps) { + $code = $set; + for (split(",",$map{"def:".$map})) { + die "bad map entry $_ (def, map $map)" unless /^0x\S\S(\S\S)$/; + $tmp[$code] = hex $1 unless $tmp[$code]; + $code++; + } + $set += 256; + } + $set = 0; + for $map (@maps) { + $code = $set; + for (split(",",$map{"kbd:".$map})) { + die "bad map entry $_ (kbd, map $map)" unless /^0x\S\S(\S\S)$/; + $table[$tmp[$code]] = hex $1 unless $table[$tmp[$code]]; + $code++; + } + $set += 256; + } + $table[0] = 0; +} diff --git a/utils/lss16toppm b/utils/lss16toppm new file mode 100755 index 0000000..18b7f64 --- /dev/null +++ b/utils/lss16toppm @@ -0,0 +1,111 @@ +#!/usr/bin/perl +## ----------------------------------------------------------------------- +## +## Copyright 2001-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +## +## lss16toppm: +## Convert an LSS-16 image to PPM +## +## Usage: +## +## lss16toppm [-map] < file.lss > file.ppm +## +## The -map causes the color map to be output on stderr. +## + +eval { use bytes; }; +eval { binmode STDIN; }; +eval { binmode STDOUT; }; + +$map = 0; +foreach $arg ( @ARGV ) { + if ( $arg eq '-map' ) { + $map = 1; + } else { + print STDERR "$0: Unknown option: $arg\n"; + exit 127; + } +} + +if ( read(STDIN, $header, 56) != 56 ) { + print STDERR "$0: Short file\n"; + exit 1; +} + +($magic, $xsize, $ysize, @colorset) = unpack("Vvvc48", $header); + +if ( $magic != 0x1413f33d ) { + print STDERR "$0: Invalid file format\n"; + exit 1; +} + +%color = (); +for ( $i = 0 ; $i < 16 ; $i++ ) { + $r = int((shift @colorset) * 255 / 63 + 0.5); + $g = int((shift @colorset) * 255 / 63 + 0.5); + $b = int((shift @colorset) * 255 / 63 + 0.5); + + $color{$i} = pack("ccc", $r, $g, $b); + + if ( $map ) { + printf STDERR "#%02x%02x%02x=%d\n", $r, $g, $b, $i; + } +} + +sub get_nybble() { + my($ch,$n); + if ( defined($nybble_buf) ) { + $n = $nybble_buf; + undef $nybble_buf; + } else { + if ( read(STDIN, $ch, 1) != 1 ) { + print STDERR "$0: Short read on input (file corrupt)\n"; + exit 1; + } + $ch = ord($ch); + $nybble_buf = $ch >> 4; + $n = $ch & 0xF; + } + return $n; +} + +print "P6\n"; +print "$xsize $ysize\n"; +print "255\n"; + +for ( $y = 0 ; $y < $ysize ; $y++ ) { + $x = 0; + $last = 0; + undef $nybble_buf; # Nybble buffer starts clear on each line + while ( $x < $xsize ) { + $n = get_nybble(); + + if ( $n != $last ) { + print $color{$n}; + $last = $n; + $x++; + } else { + $c = get_nybble(); + if ( $c == 0 ) { + # Double-nybble run + $c = get_nybble(); + $c += get_nybble() << 4; + $c += 16; + } + # Truncate overlong runs + $c = $xsize-$x if ( $c > $xsize-$x ); + # Output run + print $color{$n} x $c; + $x += $c; + } + } +} diff --git a/utils/md5pass b/utils/md5pass new file mode 100755 index 0000000..3404f1d --- /dev/null +++ b/utils/md5pass @@ -0,0 +1,34 @@ +#!/usr/bin/perl + +use bytes; +use Crypt::PasswdMD5; +use MIME::Base64; + +sub random_bytes($) { + my($n) = @_; + my($v, $i); + + if ( open(RANDOM, '<', '/dev/random') || + open(RANDOM, '<', '/dev/urandom') ) { + read(RANDOM, $v, $n); + } else { + # No real RNG available... + srand($$ ^ time); + $v = ''; + for ( $i = 0 ; $i < $n ; $i++ ) { + $v .= ord(int(rand() * 256)); + } + } + + return $v; +} + + +($pass, $salt) = @ARGV; + +unless (defined($salt)) { + $salt = MIME::Base64::encode(random_bytes(6), ''); + $salt =~ tr/\+/./; # . not + +} + +print unix_md5_crypt($pass, $salt), "\n"; diff --git a/utils/mkdiskimage b/utils/mkdiskimage new file mode 100755 index 0000000..64958ac --- /dev/null +++ b/utils/mkdiskimage @@ -0,0 +1,335 @@ +#!/usr/bin/perl +## ----------------------------------------------------------------------- +## +## Copyright 2002-2008 H. Peter Anvin - All Rights Reserved +## Copyright 2009 Intel Corporation; author: H. Peter Anvin +## +## 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +# +# Creates a blank MS-DOS formatted hard disk image +# + +use bytes; +use integer; +use Fcntl; +use Errno; +use Cwd; +use IO::Handle; # For flush() + +sub absolute_path($) { + my($f) = @_; + my($c); + + return $f if ( $f =~ /^\// ); + $c = cwd(); + $c = '' if ( $c eq '/' ); + return $c.'/'.$f; +} + +sub is_linux() { + return !!eval '{ '. + 'use POSIX; '. + '($sysname, $nodename, $release, $version, $machine) = POSIX::uname(); '. + "return \$sysname eq \'Linux\'; }"; +} + +sub get_random() { + # Get a 32-bit random number + my $rfd, $rnd; + my $rid; + + if (open($rfd, "< /dev/urandom\0") && read($rfd, $rnd, 4) == 4) { + $rid = unpack("V", $rnd); + } + + close($rfd) if (defined($rfd)); + return $rid if (defined($rid)); + + # This sucks but is better than nothing... + return ($$+time()) & 0xffffffff; +} + +sub get_hex_data() { + my $mbr = ''; + my $line, $byte; + while ( $line = ) { + chomp $line; + last if ($line eq '*'); + foreach $byte ( split(/\s+/, $line) ) { + $mbr .= chr(hex($byte)); + } + } + return $mbr; +} + +$is_linux = is_linux(); +if ( $is_linux ) { + # IOCTL numbers + $BLKRRPART = 0x125f; + $BLKGETSIZE = 0x1260; +} + +%opt = (); +@args = (); + +while (defined($a = shift(@ARGV))) { + if ( $a =~ /^\-/ ) { + foreach $o ( split(//, substr($a,1)) ) { + $opt{$o} = 1; + if ($o eq 'i') { + $id = shift(@ARGV); + } + } + } else { + push(@args, $a); + } +} + +($file,$c,$h,$s) = @args; +$c += 0; $h += 0; $s += 0; + +$pentry = 1; +$pentry = 2 if ( $opt{'2'} ); +$pentry = 3 if ( $opt{'3'} ); +$pentry = 4 if ( $opt{'4'} ); + +if ( $opt{'z'} ) { + $h = $h || 64; + $s = $s || 32; +} + +if ( $opt{'M'} && $h && $s ) { + # Specify size in megabytes, not in cylinders + $c = ($c*1024*2)/($h*$s); +} + +$is_open = 0; + +if ( $c == 0 && $file ne '' ) { + $len = 0; + if ( sysopen(OUTPUT, $file, O_RDWR, 0666) ) { + $is_open = 1; + + if ( (@filestat = stat(OUTPUT)) && S_ISREG($filestat[2]) ) { + $len = $filestat[7] >> 9; + } elsif ( $is_linux && S_ISBLK($filestat[2]) ) { + $blksize = pack("L!", 0); + if ( ioctl(OUTPUT, $BLKGETSIZE, $blksize) == 0 ) { + $len = unpack("L!", $blksize); # In 512-byte sectors! + } + } + } + + if ( !$len ) { + print STDERR "$0: $file: don't know how to determine the size of this device\n"; + exit 1; + } + + $c = $len/($h*$s); +} + +if ( $file eq '' || $c < 1 || $h < 1 || $h > 256 || $s < 1 || $s > 63 ) { + print STDERR "Usage: $0 [-doFMz4][-i id] file c h s (max: 1024 256 63)\n"; + print STDERR " -d add DOSEMU header\n"; + print STDERR " -o print filesystem offset to stdout\n"; + print STDERR " -F format partition as FAT32\n"; + print STDERR " -M \"c\" argument is megabytes, calculate cylinders\n"; + print STDERR " -z use zipdisk geometry (h=64 s=32)\n"; + print STDERR " -4 use partition entry 4 (standard for zipdisks)\n"; + print STDERR " -i specify the MBR ID\n"; + exit 1; +} + +if ($c > 1024) { + print STDERR "Warning: more than 1024 cylinders ($c).\n"; + print STDERR "Not all BIOSes will be able to boot this device.\n"; + $cc = 1024; +} else { + $cc = $c; +} + +$cylsize = $h*$s*512; + +if ( !$is_open ) { + sysopen(OUTPUT, $file, O_CREAT|O_RDWR|O_TRUNC, 0666) + or die "$0: Cannot open: $file\n"; +} +binmode OUTPUT; + +# Print out DOSEMU header, if requested +if ( $opt{'d'} ) { + $emuhdr = "DOSEMU\0" . pack("VVVV", $h, $s, $c, 128); + $emuhdr .= "\0" x (128 - length($emuhdr)); + print OUTPUT $emuhdr; +} + +# Print the MBR and partition table +$mbr = get_hex_data(); +if ( length($mbr) > 440 ) { + die "$0: Bad MBR code\n"; +} + +$mbr .= "\0" x (440 - length($mbr)); +if (defined($id)) { + $id = to_int($id); +} else { + $id = get_random(); +} +$mbr .= pack("V", $id); # Offset 440: MBR ID +$mbr .= "\0\0"; # Offset 446: actual partition table + +print OUTPUT $mbr; + +# Print partition table +$psize = $c*$h*$s-$s; +$bhead = ($h > 1) ? 1 : 0; +$bsect = 1; +$bcyl = ($h > 1) ? 0 : 1; +$ehead = $h-1; +$esect = $s + ((($cc-1) & 0x300) >> 2); +$ecyl = ($cc-1) & 0xff; +if ( $c > 1024 ) { + $fstype = 0x0e; +} elsif ( $psize > 65536 ) { + $fstype = 0x06; +} else { + $fstype = 0x04; +} +for ( $i = 1 ; $i <= 4 ; $i++ ) { + if ( $i == $pentry ) { + print OUTPUT pack("CCCCCCCCVV", 0x80, $bhead, $bsect, $bcyl, $fstype, + $ehead, $esect, $ecyl, $s, $psize); + } else { + print OUTPUT "\0" x 16; + } +} +print OUTPUT "\x55\xaa"; + +# Output blank file +$totalsize = $c*$h*$s; +$tracks = $c*$h; + +$track = "\0" x (512*$s); + +# Print fractional track +print OUTPUT "\0" x (512 * ($s-1)); + +for ( $i = 1 ; $i < $tracks ; $i++ ) { + print OUTPUT $track; +} + +# Print mtools temp file +$n = 0; +while ( !defined($tmpdir) ) { + $tmpdir = "/tmp/mkdiskimage.$$.".($n++); + if ( !mkdir($tmpdir, 0700) ) { + die "$0: Failed to make temp directory: $tmpdir\n" + if ( $! != EEXIST ); + undef $tmpdir; + } +} + +$cfgfile = $tmpdir.'/mtools.conf'; +$imglink = $tmpdir.'/disk.img'; +die "$0: Failed to create symlink $imglink\n" + if ( !symlink(absolute_path($file), $imglink) ); + +$header_size = ($opt{'d'} ? 128 : 0); + +# Start of filesystem +$offset = $s*512 + $header_size; + +# Start of partition table entry +$pstart = $header_size + 446 + 16*($pentry-1); + +open(MCONFIG, "> ${cfgfile}") or die "$0: Cannot make mtools config\n"; +print MCONFIG "drive z:\n"; +print MCONFIG "file=\"${imglink}\"\n"; +print MCONFIG "cylinders=${c}\n"; +print MCONFIG "heads=${h}\n"; +print MCONFIG "sectors=${s}\n"; +print MCONFIG "offset=${offset}\n"; +print MCONFIG "mformat_only\n"; +close(MCONFIG); + +# Output the filesystem offset to stdout if appropriate +if ( $opt{'o'} ) { + print $offset, "\n"; +} + +$ENV{'MTOOLSRC'} = $cfgfile; +if ( $opt{'F'} ) { + system('mformat', '-F', 'z:'); +} else { + system('mformat', 'z:'); +} + +# Clean up in /tmp +unlink($cfgfile); +unlink($imglink); +rmdir($tmpdir); + +# MTOOLS doesn't write the bsHiddenSecs field correctly +seek(OUTPUT, $offset + 0x1c, 0); +print OUTPUT pack("V", ($offset-$header_size)>>9); + +# Set the partition type +if ( $opt{'F'} ) { + if ( $c > 1024 ) { + $fstype = 0x0c; # FAT32 LBA + } else { + $fstype = 0x0b; + } +} else { + if ( $c > 1024 ) { + $fstype = 0x0e; # FAT16 LBA + } elsif ( $psize > 65536 ) { + $fstype = 0x06; # FAT16 > 32MB + } else { + $fstype = 0x04; # FAT16 <= 32MB + } + seek(OUTPUT, $offset + 0x36, 0); + read(OUTPUT, $fsname, 8); + + # FAT12: adjust partition type + if ( $fsname eq 'FAT12 ' ) { + $fstype = 0x01; # FAT12 + } +} +seek(OUTPUT, $pstart+4, 0); +print OUTPUT pack("C", $fstype); + +flush OUTPUT; + +# Just in case this is a block device, try to flush the partition table +if ( $is_linux ) { + ioctl(OUTPUT, $BLKRRPART, 0); +}; + +exit 0; +__END__ +fa 31 c0 8e d8 8e d0 bc 0 7c 89 e6 6 57 8e c0 fb fc bf 0 6 b9 0 1 f3 a5 ea +1f 6 0 0 52 52 b4 41 bb aa 55 31 c9 30 f6 f9 cd 13 72 13 81 fb 55 aa 75 d +d1 e9 73 9 66 c7 6 8d 6 b4 42 eb 15 5a b4 8 cd 13 83 e1 3f 51 f b6 c6 40 f7 +e1 52 50 66 31 c0 66 99 e8 66 0 e8 21 1 4d 69 73 73 69 6e 67 20 6f 70 65 72 +61 74 69 6e 67 20 73 79 73 74 65 6d 2e d a 66 60 66 31 d2 bb 0 7c 66 52 66 +50 6 53 6a 1 6a 10 89 e6 66 f7 36 f4 7b c0 e4 6 88 e1 88 c5 92 f6 36 f8 7b +88 c6 8 e1 41 b8 1 2 8a 16 fa 7b cd 13 8d 64 10 66 61 c3 e8 c4 ff be be 7d +bf be 7 b9 20 0 f3 a5 c3 66 60 89 e5 bb be 7 b9 4 0 31 c0 53 51 f6 7 80 74 +3 40 89 de 83 c3 10 e2 f3 48 74 5b 79 39 59 5b 8a 47 4 3c f 74 6 24 7f 3c +5 75 22 66 8b 47 8 66 8b 56 14 66 1 d0 66 21 d2 75 3 66 89 c2 e8 ac ff 72 +3 e8 b6 ff 66 8b 46 1c e8 a0 ff 83 c3 10 e2 cc 66 61 c3 e8 62 0 4d 75 6c 74 +69 70 6c 65 20 61 63 74 69 76 65 20 70 61 72 74 69 74 69 6f 6e 73 2e d a 66 +8b 44 8 66 3 46 1c 66 89 44 8 e8 30 ff 72 13 81 3e fe 7d 55 aa f 85 6 ff bc +fa 7b 5a 5f 7 fa ff e4 e8 1e 0 4f 70 65 72 61 74 69 6e 67 20 73 79 73 74 65 +6d 20 6c 6f 61 64 20 65 72 72 6f 72 2e d a 5e ac b4 e 8a 3e 62 4 b3 7 cd 10 +3c a 75 f1 cd 18 f4 eb fd 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +* diff --git a/utils/mkdiskimage.in b/utils/mkdiskimage.in new file mode 100644 index 0000000..9231fa2 --- /dev/null +++ b/utils/mkdiskimage.in @@ -0,0 +1,317 @@ +#!/usr/bin/perl +## ----------------------------------------------------------------------- +## +## Copyright 2002-2008 H. Peter Anvin - All Rights Reserved +## Copyright 2009 Intel Corporation; author: H. Peter Anvin +## +## 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +# +# Creates a blank MS-DOS formatted hard disk image +# + +use bytes; +use integer; +use Fcntl; +use Errno; +use Cwd; +use IO::Handle; # For flush() + +sub absolute_path($) { + my($f) = @_; + my($c); + + return $f if ( $f =~ /^\// ); + $c = cwd(); + $c = '' if ( $c eq '/' ); + return $c.'/'.$f; +} + +sub is_linux() { + return !!eval '{ '. + 'use POSIX; '. + '($sysname, $nodename, $release, $version, $machine) = POSIX::uname(); '. + "return \$sysname eq \'Linux\'; }"; +} + +sub get_random() { + # Get a 32-bit random number + my $rfd, $rnd; + my $rid; + + if (open($rfd, "< /dev/urandom\0") && read($rfd, $rnd, 4) == 4) { + $rid = unpack("V", $rnd); + } + + close($rfd) if (defined($rfd)); + return $rid if (defined($rid)); + + # This sucks but is better than nothing... + return ($$+time()) & 0xffffffff; +} + +sub get_hex_data() { + my $mbr = ''; + my $line, $byte; + while ( $line = ) { + chomp $line; + last if ($line eq '*'); + foreach $byte ( split(/\s+/, $line) ) { + $mbr .= chr(hex($byte)); + } + } + return $mbr; +} + +$is_linux = is_linux(); +if ( $is_linux ) { + # IOCTL numbers + $BLKRRPART = 0x125f; + $BLKGETSIZE = 0x1260; +} + +%opt = (); +@args = (); + +while (defined($a = shift(@ARGV))) { + if ( $a =~ /^\-/ ) { + foreach $o ( split(//, substr($a,1)) ) { + $opt{$o} = 1; + if ($o eq 'i') { + $id = shift(@ARGV); + } + } + } else { + push(@args, $a); + } +} + +($file,$c,$h,$s) = @args; +$c += 0; $h += 0; $s += 0; + +$pentry = 1; +$pentry = 2 if ( $opt{'2'} ); +$pentry = 3 if ( $opt{'3'} ); +$pentry = 4 if ( $opt{'4'} ); + +if ( $opt{'z'} ) { + $h = $h || 64; + $s = $s || 32; +} + +if ( $opt{'M'} && $h && $s ) { + # Specify size in megabytes, not in cylinders + $c = ($c*1024*2)/($h*$s); +} + +$is_open = 0; + +if ( $c == 0 && $file ne '' ) { + $len = 0; + if ( sysopen(OUTPUT, $file, O_RDWR, 0666) ) { + $is_open = 1; + + if ( (@filestat = stat(OUTPUT)) && S_ISREG($filestat[2]) ) { + $len = $filestat[7] >> 9; + } elsif ( $is_linux && S_ISBLK($filestat[2]) ) { + $blksize = pack("L!", 0); + if ( ioctl(OUTPUT, $BLKGETSIZE, $blksize) == 0 ) { + $len = unpack("L!", $blksize); # In 512-byte sectors! + } + } + } + + if ( !$len ) { + print STDERR "$0: $file: don't know how to determine the size of this device\n"; + exit 1; + } + + $c = $len/($h*$s); +} + +if ( $file eq '' || $c < 1 || $h < 1 || $h > 256 || $s < 1 || $s > 63 ) { + print STDERR "Usage: $0 [-doFMz4][-i id] file c h s (max: 1024 256 63)\n"; + print STDERR " -d add DOSEMU header\n"; + print STDERR " -o print filesystem offset to stdout\n"; + print STDERR " -F format partition as FAT32\n"; + print STDERR " -M \"c\" argument is megabytes, calculate cylinders\n"; + print STDERR " -z use zipdisk geometry (h=64 s=32)\n"; + print STDERR " -4 use partition entry 4 (standard for zipdisks)\n"; + print STDERR " -i specify the MBR ID\n"; + exit 1; +} + +if ($c > 1024) { + print STDERR "Warning: more than 1024 cylinders ($c).\n"; + print STDERR "Not all BIOSes will be able to boot this device.\n"; + $cc = 1024; +} else { + $cc = $c; +} + +$cylsize = $h*$s*512; + +if ( !$is_open ) { + sysopen(OUTPUT, $file, O_CREAT|O_RDWR|O_TRUNC, 0666) + or die "$0: Cannot open: $file\n"; +} +binmode OUTPUT; + +# Print out DOSEMU header, if requested +if ( $opt{'d'} ) { + $emuhdr = "DOSEMU\0" . pack("VVVV", $h, $s, $c, 128); + $emuhdr .= "\0" x (128 - length($emuhdr)); + print OUTPUT $emuhdr; +} + +# Print the MBR and partition table +$mbr = get_hex_data(); +if ( length($mbr) > 440 ) { + die "$0: Bad MBR code\n"; +} + +$mbr .= "\0" x (440 - length($mbr)); +if (defined($id)) { + $id = to_int($id); +} else { + $id = get_random(); +} +$mbr .= pack("V", $id); # Offset 440: MBR ID +$mbr .= "\0\0"; # Offset 446: actual partition table + +print OUTPUT $mbr; + +# Print partition table +$psize = $c*$h*$s-$s; +$bhead = ($h > 1) ? 1 : 0; +$bsect = 1; +$bcyl = ($h > 1) ? 0 : 1; +$ehead = $h-1; +$esect = $s + ((($cc-1) & 0x300) >> 2); +$ecyl = ($cc-1) & 0xff; +if ( $c > 1024 ) { + $fstype = 0x0e; +} elsif ( $psize > 65536 ) { + $fstype = 0x06; +} else { + $fstype = 0x04; +} +for ( $i = 1 ; $i <= 4 ; $i++ ) { + if ( $i == $pentry ) { + print OUTPUT pack("CCCCCCCCVV", 0x80, $bhead, $bsect, $bcyl, $fstype, + $ehead, $esect, $ecyl, $s, $psize); + } else { + print OUTPUT "\0" x 16; + } +} +print OUTPUT "\x55\xaa"; + +# Output blank file +$totalsize = $c*$h*$s; +$tracks = $c*$h; + +$track = "\0" x (512*$s); + +# Print fractional track +print OUTPUT "\0" x (512 * ($s-1)); + +for ( $i = 1 ; $i < $tracks ; $i++ ) { + print OUTPUT $track; +} + +# Print mtools temp file +$n = 0; +while ( !defined($tmpdir) ) { + $tmpdir = "/tmp/mkdiskimage.$$.".($n++); + if ( !mkdir($tmpdir, 0700) ) { + die "$0: Failed to make temp directory: $tmpdir\n" + if ( $! != EEXIST ); + undef $tmpdir; + } +} + +$cfgfile = $tmpdir.'/mtools.conf'; +$imglink = $tmpdir.'/disk.img'; +die "$0: Failed to create symlink $imglink\n" + if ( !symlink(absolute_path($file), $imglink) ); + +$header_size = ($opt{'d'} ? 128 : 0); + +# Start of filesystem +$offset = $s*512 + $header_size; + +# Start of partition table entry +$pstart = $header_size + 446 + 16*($pentry-1); + +open(MCONFIG, "> ${cfgfile}") or die "$0: Cannot make mtools config\n"; +print MCONFIG "drive z:\n"; +print MCONFIG "file=\"${imglink}\"\n"; +print MCONFIG "cylinders=${c}\n"; +print MCONFIG "heads=${h}\n"; +print MCONFIG "sectors=${s}\n"; +print MCONFIG "offset=${offset}\n"; +print MCONFIG "mformat_only\n"; +close(MCONFIG); + +# Output the filesystem offset to stdout if appropriate +if ( $opt{'o'} ) { + print $offset, "\n"; +} + +$ENV{'MTOOLSRC'} = $cfgfile; +if ( $opt{'F'} ) { + system('mformat', '-F', 'z:'); +} else { + system('mformat', 'z:'); +} + +# Clean up in /tmp +unlink($cfgfile); +unlink($imglink); +rmdir($tmpdir); + +# MTOOLS doesn't write the bsHiddenSecs field correctly +seek(OUTPUT, $offset + 0x1c, 0); +print OUTPUT pack("V", ($offset-$header_size)>>9); + +# Set the partition type +if ( $opt{'F'} ) { + if ( $c > 1024 ) { + $fstype = 0x0c; # FAT32 LBA + } else { + $fstype = 0x0b; + } +} else { + if ( $c > 1024 ) { + $fstype = 0x0e; # FAT16 LBA + } elsif ( $psize > 65536 ) { + $fstype = 0x06; # FAT16 > 32MB + } else { + $fstype = 0x04; # FAT16 <= 32MB + } + seek(OUTPUT, $offset + 0x36, 0); + read(OUTPUT, $fsname, 8); + + # FAT12: adjust partition type + if ( $fsname eq 'FAT12 ' ) { + $fstype = 0x01; # FAT12 + } +} +seek(OUTPUT, $pstart+4, 0); +print OUTPUT pack("C", $fstype); + +flush OUTPUT; + +# Just in case this is a block device, try to flush the partition table +if ( $is_linux ) { + ioctl(OUTPUT, $BLKRRPART, 0); +}; + +exit 0; +__END__ diff --git a/utils/ppmtolss16 b/utils/ppmtolss16 new file mode 100755 index 0000000..5af9083 --- /dev/null +++ b/utils/ppmtolss16 @@ -0,0 +1,393 @@ +#!/usr/bin/perl +## ----------------------------------------------------------------------- +## +## Copyright 2004-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +## +## ppmtolss16 +## +## Convert a PNM file with max 16 colors to a simple RLE-based format: +## +## uint32 0x1413f33d ; magic (littleendian) +## uint16 xsize ; littleendian +## uint16 ysize ; littleendian +## 16 x uint8 r,g,b ; color map, in 6-bit format (each byte is 0..63) +## +## Then, a sequence of nybbles: +## +## N ... if N is != previous pixel, one pixel of color N +## ... otherwise run sequence follows ... +## M ... if M > 0 then run length is M +## ... otherwise run sequence is encoded in two nybbles, +## littleendian, +16 +## +## The nybble sequences are on a per-row basis; runs may not extend +## across rows and odd-nybble rows are zero-padded. +## +## At the start of row, the "previous pixel" is assumed to be zero. +## +## Usage: +## +## ppmtolss16 [#rrggbb=i ...] < input.ppm > output.rle +## +## Command line options of the form #rrggbb=i indicate that +## the color #rrggbb (hex) should be assigned index i (decimal) +## + +eval { use bytes; }; +eval { binmode STDIN; }; +eval { binmode STDOUT; }; + +$magic = 0x1413f33d; + +# Get a token from the PPM header. Ignore comments and leading +# and trailing whitespace, as is required by the spec. +# This routine eats exactly one character of trailing whitespace, +# unless it is a comment (in which case it eats the comment up +# to and including the end of line.) +sub get_token() { + my($token, $ch); + my($ch); + + do { + $ch = getc(STDIN); + return undef if ( !defined($ch) ); # EOF + if ( $ch eq '#' ) { + do { + $ch = getc(STDIN); + return undef if ( !defined($ch) ); + } while ( $ch ne "\n" ); + } + } while ( $ch =~ /^[ \t\n\v\f\r]$/ ); + + $token = $ch; + while ( 1 ) { + $ch = getc(STDIN); + last if ( $ch =~ /^[ \t\n\v\f\r\#]$/ ); + $token .= $ch; + } + if ( $ch eq '#' ) { + do { + $ch = getc(STDIN); + } while ( defined($ch) && $ch ne "\n" ); + } + return $token; +} + +# Get a token, and make sure it is numeric (and exists) +sub get_numeric_token() { + my($token) = get_token(); + + if ( $token !~ /^[0-9]+$/ ) { + print STDERR "Format error on input\n"; + exit 1; + } + + return $token + 0; +} + +# Must be called before each pixel row is read +sub start_new_row() { + $getrgb_leftover_bit_cnt = 0; + $getrgb_leftover_bit_val = 0; +} + +# Get a single RGB token depending on the PNM type +sub getrgb($) { + my($form) = @_; + my($rgb,$r,$g,$b); + + if ( $form == 6 ) { + # Raw PPM, most common + return undef unless ( read(STDIN,$rgb,3) == 3 ); + return unpack("CCC", $rgb); + } elsif ( $form == 3 ) { + # Plain PPM + $r = get_numeric_token(); + $g = get_numeric_token(); + $b = get_numeric_token(); + return ($r,$g,$b); + } elsif ( $form == 5 ) { + # Raw PGM + return undef unless ( read(STDIN,$rgb,1) == 1 ); + $r = unpack("C", $rgb); + return ($r,$r,$r); + } elsif ( $form == 2 ) { + # Plain PGM + $r = get_numeric_token(); + return ($r,$r,$r); + } elsif ( $form == 4 ) { + # Raw PBM + if ( !$getrgb_leftover_bit_cnt ) { + return undef unless ( read(STDIN,$rgb,1) == 1 ); + $getrgb_leftover_bit_val = unpack("C", $rgb); + $getrgb_leftover_bit_cnt = 8; + } + $r = ( $getrgb_leftover_bit_val & 0x80 ) ? 0x00 : 0xff; + $getrgb_leftover_bit_val <<= 1; + $getrgb_leftover_bit_cnt--; + + return ($r,$r,$r); + } elsif ( $form == 1 ) { + # Plain PBM + my($ch); + + do { + $ch = getc(STDIN); + return undef if ( !defined($ch) ); + return (255,255,255) if ( $ch eq '0' ); # White + return (0,0,0) if ( $ch eq '1'); # Black + if ( $ch eq '#' ) { + do { + $ch = getc(STDIN); + return undef if ( !defined($ch) ); + } while ( $ch ne "\n" ); + } + } while ( $ch =~ /^[ \t\n\v\f\r]$/ ); + return undef; + } else { + die "Internal error: unknown format: $form\n"; + } +} + +sub rgbconvert($$$$) { + my($r,$g,$b,$maxmult) = @_; + my($rgb); + + $r = int($r*$maxmult); + $g = int($g*$maxmult); + $b = int($b*$maxmult); + $rgb = pack("CCC", $r, $g, $b); + return $rgb; +} + +foreach $arg ( @ARGV ) { + if ( $arg =~ /^\#([0-9a-f])([0-9a-f])([0-9a-f])=([0-9]+)$/i ) { + $r = hex($1) << 4; + $g = hex($2) << 4; + $b = hex($3) << 4; + $i = $4 + 0; + } elsif ( $arg =~ /^\#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})=([0-9]+)$/i ) { + $r = hex($1); + $g = hex($2); + $b = hex($3); + $i = $4 + 0; + } elsif ( $arg =~ /^\#([0-9a-f]{3})([0-9a-f]{3})([0-9a-f]{3})=([0-9]+)$/i ) { + $r = hex($1) >> 4; + $g = hex($2) >> 4; + $b = hex($3) >> 4; + $i = $4 + 0; + } elsif ( $arg =~ /^\#([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})=([0-9]+)$/i ) { + $r = hex($1) >> 8; + $g = hex($2) >> 8; + $b = hex($3) >> 8; + $i = $4 + 0; + } else { + print STDERR "$0: Unknown argument: $arg\n"; + next; + } + + if ( $i > 15 ) { + print STDERR "$0: Color index out of range: $arg\n"; + next; + } + + $rgb = rgbconvert($r, $g, $b, 64/256); + + if ( defined($index_forced{$i}) ) { + print STDERR "$0: More than one color index $i\n"; + exit(1); + } + $index_forced{$i} = $rgb; + $force_index{$rgb} = $i; +} + +$form = get_token(); +die "$0: stdin is not a PNM file" if ( $form !~ /^P([1-6])$/ ); +$form = $1+0; + +$xsize = get_numeric_token(); +$ysize = get_numeric_token(); +if ( $form == 1 || $form == 4 ) { + $maxcol = 255; # Internal convention +} else { + $maxcol = get_numeric_token(); +} +$maxmult = 64/($maxcol+1); # Equal buckets conversion + +@data = (); + +for ( $y = 0 ; $y < $ysize ; $y++ ) { + start_new_row(); + for ( $x = 0 ; $x < $xsize ; $x++ ) { + die "$0: Premature EOF at ($x,$y) of ($xsize,$ysize)\n" + if ( !defined(@pnmrgb = getrgb($form)) ); + # Convert to 6-bit representation + $rgb = rgbconvert($pnmrgb[0], $pnmrgb[1], $pnmrgb[2], $maxmult); + $color_count{$rgb}++; + push(@data, $rgb); + } +} + +# Sort list of colors according to freqency +@colors = sort { $color_count{$b} <=> $color_count{$a} } keys(%color_count); + +# Now we have our pick of colors. Sort according to intensity; +# this is more or less an ugly hack to cover for the fact that +# using PPM as input doesn't let the user set the color map, +# which the user really needs to be able to do. + +sub by_intensity() { + my($ra,$ga,$ba) = unpack("CCC", $a); + my($rb,$gb,$bb) = unpack("CCC", $b); + + my($ia) = $ra*0.299 + $ga*0.587 + $ba*0.114; + my($ib) = $rb*0.299 + $gb*0.587 + $bb*0.114; + + return ( $ia <=> $ib ) if ( $ia != $ib ); + + # If same, sort based on RGB components, + # with highest priority given to G, then R, then B. + + return ( $ga <=> $gb ) if ( $ga != $gb ); + return ( $ra <=> $rb ) if ( $ra != $rb ); + return ( $ba <=> $bb ); +} + +@icolors = sort by_intensity @colors; + +# Insert forced colors into "final" array +@colors = (undef) x 16; +foreach $rgb ( keys(%force_index) ) { + $i = $force_index{$rgb}; + $colors[$i] = $rgb; + $color_index{$rgb} = $i; +} + +undef %force_index; + +# Insert remaining colors in the remaining slots, +# in luminosity-sorted order +$nix = 0; +while ( scalar(@icolors) ) { + # Advance to the next free slot + $nix++ while ( defined($colors[$nix]) && $nix < 16 ); + last if ( $nix >= 16 ); + $rgb = shift @icolors; + if ( !defined($color_index{$rgb}) ) { + $colors[$nix] = $rgb; + $color_index{$rgb} = $nix; + } +} + +while ( scalar(@icolors) ) { + $rgb = shift @icolors; + $lost++ if ( !defined($color_index{$rgb}) ); +} + +if ( $lost ) { + printf STDERR + "$0: Warning: color palette truncated (%d colors ignored)\n", $lost; +} + +undef @icolors; + +# Output header +print pack("Vvv", $magic, $xsize, $ysize); + +# Output color map +for ( $i = 0 ; $i < 16 ; $i++ ) { + if ( defined($colors[$i]) ) { + print $colors[$i]; + } else { + # Padding for unused color entries + print pack("CCC", 63*$i/15, 63*$i/15, 63*$i/15); + } +} + +sub output_nybble($) { + my($ny) = @_; + + if ( !defined($ny) ) { + if ( defined($nybble_tmp) ) { + $ny = 0; # Force the last byte out + } else { + return; + } + } + + $ny = $ny & 0x0F; + + if ( defined($nybble_tmp) ) { + $ny = ($ny << 4) | $nybble_tmp; + print chr($ny); + $bytes++; + undef $nybble_tmp; + } else { + $nybble_tmp = $ny; + } +} + +sub output_run($$$) { + my($last,$this,$run) = @_; + + if ( $this != $last ) { + output_nybble($this); + $run--; + } + while ( $run ) { + if ( $run >= 16 ) { + output_nybble($this); + output_nybble(0); + if ( $run > 271 ) { + $erun = 255; + $run -= 271; + } else { + $erun = $run-16; + $run = 0; + } + output_nybble($erun); + output_nybble($erun >> 4); + } else { + output_nybble($this); + output_nybble($run); + $run = 0; + } + } +} + +$bytes = 0; +undef $nybble_tmp; + +for ( $y = 0 ; $y < $ysize ; $y++ ) { + $last = $prev = 0; + $run = 0; + for ( $x = 0 ; $x < $xsize ; $x++ ) { + $rgb = shift(@data); + $i = $color_index{$rgb} + 0; + if ( $i == $last ) { + $run++; + } else { + output_run($prev, $last, $run); + $prev = $last; + $last = $i; + $run = 1; + } + } + # Output final datum for row; we're always at least one pixel behind + output_run($prev, $last, $run); + output_nybble(undef); # Flush row +} + +$pixels = $xsize * $ysize; +$size = ($pixels+1)/2; +printf STDERR "%d pixels, %d bytes, (%2.2f%% compression)\n", + $pixels, $bytes, 100*($size-$bytes)/$size; diff --git a/utils/sha1pass b/utils/sha1pass new file mode 100755 index 0000000..3be2dbc --- /dev/null +++ b/utils/sha1pass @@ -0,0 +1,34 @@ +#!/usr/bin/perl + +use bytes; +use Digest::SHA1; +use MIME::Base64; + +sub random_bytes($) { + my($n) = @_; + my($v, $i); + + if ( open(RANDOM, '<', '/dev/random') || + open(RANDOM, '<', '/dev/urandom') ) { + read(RANDOM, $v, $n); + } else { + # No real RNG available... + srand($$ ^ time); + $v = ''; + for ( $i = 0 ; $i < $n ; $i++ ) { + $v .= ord(int(rand() * 256)); + } + } + + return $v; +} + + +($pass, $salt) = @ARGV; + +unless (defined($salt)) { + $salt = MIME::Base64::encode(random_bytes(6), ''); +} +$pass = Digest::SHA1::sha1_base64($salt, $pass); + +print '$4$', $salt, '$', $pass, "\$\n"; diff --git a/utils/syslinux2ansi b/utils/syslinux2ansi new file mode 100755 index 0000000..085f6c9 --- /dev/null +++ b/utils/syslinux2ansi @@ -0,0 +1,53 @@ +#!/usr/bin/perl +# +# Perl script to convert a Syslinux-format screen to PC-ANSI +# to display in a color xterm or on the Linux console +# + +@ansicol = (0,4,2,6,1,5,3,7); + +$getting_file = 0; +$enable = 1; + +while ( read(STDIN, $ch, 1) > 0 ) { + if ( $ch eq "\x1A" ) { # EOF + last; + } elsif ( $ch eq "\x0C" ) { # Clear screen + print "\x1b[2J" if ( $enable && !$getting_file ); + } elsif ( $ch eq "\x0F" ) { # Attribute change + if ( !$getting_file ) { + if ( read(STDIN, $attr, 2) == 2 ) { + $attr = hex $attr; + if ( $enable ) { + print "\x1b[0;"; + if ( $attr & 0x80 ) { + print "5;"; + $attr &= ~0x80; + } + if ( $attr & 0x08 ) { + print "1;"; + $attr &= ~0x08; + } + printf "%d;%dm", + $ansicol[$attr >> 4] + 40, $ansicol[$attr & 7] + 30; + } + } + } + } elsif ( $ch eq "\x18" ) { # Display image + # We can't display an image; pretend to be a text screen + # Ignore all input until end of line + $getting_file = 1; + } elsif ( (ord($ch) & ~07) == 0x10 ) { # Mode controls + $enable = (ord($ch) & 0x01); # Emulate the text screen + } elsif ( $ch eq "\x0D" ) { # Carriage return + # Ignore + } elsif ( $ch eq "\x0A" ) { # Line feed + if ( $getting_file ) { + $getting_file = 0; + } else { + print $ch if ( $enable ); + } + } else { + print $ch if ( $enable && !$getting_file ); + } +} diff --git a/version b/version new file mode 100644 index 0000000..2fc9418 --- /dev/null +++ b/version @@ -0,0 +1 @@ +3.82 2009 diff --git a/version.gen b/version.gen new file mode 100644 index 0000000..0cad1f7 --- /dev/null +++ b/version.gen @@ -0,0 +1,6 @@ +%define VERSION 3.82 +%define VERSION_STR "3.82" +%define VERSION_MAJOR 3 +%define VERSION_MINOR 82 +%define YEAR 2009 +%define YEAR_STR "2009" diff --git a/version.h b/version.h new file mode 100644 index 0000000..0c8fc34 --- /dev/null +++ b/version.h @@ -0,0 +1,6 @@ +#define VERSION 3.82 +#define VERSION_STR "3.82" +#define VERSION_MAJOR 3 +#define VERSION_MINOR 82 +#define YEAR 2009 +#define YEAR_STR "2009" diff --git a/version.mk b/version.mk new file mode 100644 index 0000000..5619a87 --- /dev/null +++ b/version.mk @@ -0,0 +1,6 @@ +VERSION := 3.82 +VERSION_STR := "3.82" +VERSION_MAJOR := 3 +VERSION_MINOR := 82 +YEAR := 2009 +YEAR_STR := "2009" diff --git a/version.pl b/version.pl new file mode 100755 index 0000000..a08ce83 --- /dev/null +++ b/version.pl @@ -0,0 +1,39 @@ +#!/usr/bin/perl +# +# Read the "version" file and produce some macro declarations +# + +use Fcntl; + +sub defx($$$) { + my($def, $name, $val) = @_; + + $def =~ s/\; +chomp $vfile; +close(VERSION); + +unless ( $vfile =~ /^(([0-9]+)\.([0-9]+))\s+([0-9]+)$/ ) { + die "$0: Cannot parse version format\n"; +} +$version = $1; +$vma = $2+0; +$vmi = $3+0; +$year = $4; + +sysopen(VI, $vout, O_WRONLY|O_CREAT|O_TRUNC) + or die "$0: Cannot create $vout: $!\n"; +print VI defx($def, 'VERSION', $version); +print VI defx($def, 'VERSION_STR', '"'.$version.'"'); +print VI defx($def, 'VERSION_MAJOR', $vma); +print VI defx($def, 'VERSION_MINOR', $vmi); +print VI defx($def, 'YEAR', $year); +print VI defx($def, 'YEAR_STR', '"'.$year.'"'); +close(VI); diff --git a/win32/Makefile b/win32/Makefile new file mode 100644 index 0000000..964b74c --- /dev/null +++ b/win32/Makefile @@ -0,0 +1,87 @@ +## ----------------------------------------------------------------------- +## +## Copyright 1998-2008 H. Peter Anvin - 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, Inc., 53 Temple Place Ste 330, +## Boston MA 02111-1307, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +# +# Makefile for SYSLINUX Win32 +# +# This is separated out mostly so we can have a different set of Makefile +# variables. +# + +OSTYPE = $(shell uname -msr) +ifeq ($(findstring CYGWIN,$(OSTYPE)),CYGWIN) +## Compiling on Cygwin +WINPREFIX := +WINCFLAGS := -mno-cygwin -W -Wall -Os -fomit-frame-pointer -D_FILE_OFFSET_BITS=64 +WINLDFLAGS := -mno-cygwin -Os -s +else +## Compiling on some variant of MinGW +ifeq ($(findstring MINGW32,$(OSTYPE)),MINGW32) +WINPREFIX := +else +WINPREFIX := $(shell ./find-mingw.sh gcc) +endif +WINCFLAGS := -W -Wall -Wno-sign-compare -Os -fomit-frame-pointer \ + -D_FILE_OFFSET_BITS=64 +WINLDFLAGS := -Os -s +endif +WINCFLAGS += -I. -I.. -I../libfat -I../libinstaller + +WINCC := $(WINPREFIX)gcc +WINAR := $(WINPREFIX)ar +WINRANLIB := $(WINPREFIX)ranlib + +WINCC_IS_GOOD := $(shell $(WINCC) $(WINCFLAGS) $(WINLDFLAGS) -o hello.exe hello.c >/dev/null 2>&1 ; echo $$?) + +.SUFFIXES: .c .o .i .s .S + +SRCS = syslinux.c \ + ../libinstaller/syslxmod.c \ + ../libinstaller/bootsect_bin.c \ + ../libinstaller/ldlinux_bin.c \ + ../libinstaller/mbr_bin.c \ + $(wildcard ../libfat/*.c) +OBJS = $(patsubst %.c,%.o,$(notdir $(SRCS))) + +VPATH = .:../libfat:../libinstaller + +TARGETS = syslinux.exe + +ifeq ($(WINCC_IS_GOOD),0) +all: $(TARGETS) +else +all: + rm -f $(TARGETS) +endif + +tidy dist: + -rm -f *.o *.i *.s *.a .*.d *.tmp *_bin.c hello.exe + +clean: tidy + +spotless: clean + -rm -f *~ $(TARGETS) + +installer: + +syslinux.exe: $(OBJS) + $(WINCC) $(WINLDFLAGS) -o $@ $^ + + +%.o: %.c + $(WINCC) $(UMAKEDEPS) $(WINCFLAGS) -c -o $@ $< +%.i: %.c + $(WINCC) $(UMAKEDEPS) $(WINCFLAGS) -E -o $@ $< +%.s: %.c + $(WINCC) $(UMAKEDEPS) $(WINCFLAGS) -S -o $@ $< + +-include .*.d *.tmp diff --git a/win32/README b/win32/README new file mode 100644 index 0000000..a1578f9 --- /dev/null +++ b/win32/README @@ -0,0 +1,8 @@ +Building the Win32 installer requires the MinGW compiler, available at: + + http://www.mingw.org/ + +Prepackaged versions of the MinGW cross-compiler in RPM format for +Linux are available at: + + http://mirzam.it.vu.nl/mingw/ diff --git a/win32/find-mingw.sh b/win32/find-mingw.sh new file mode 100755 index 0000000..7a4491c --- /dev/null +++ b/win32/find-mingw.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +cc="$1" + +for prefix in \ + mingw- \ + i386-pc-mingw32- \ + i486-pc-mingw32- \ + i586-pc-mingw32- \ + i686-pc-mingw32- \ + i386-pc-mingw32msvc- \ + i486-pc-mingw32msvc- \ + i586-pc-mingw32msvc- \ + i686-pc-mingw32msvc- \ + i386-mingw32- \ + i486-mingw32- \ + i586-mingw32- \ + i686-mingw32- \ + i386-mingw32msvc- \ + i486-mingw32msvc- \ + i586-mingw32msvc- \ + i686-mingw32msvc-; do + if "${prefix}${cc}" -v > /dev/null 2>&1; then + echo "$prefix" + exit 0 + fi +done + +# No prefix, no idea what to do now... +echo missing- +exit 1 diff --git a/win32/hello.c b/win32/hello.c new file mode 100644 index 0000000..b1f7594 --- /dev/null +++ b/win32/hello.c @@ -0,0 +1,13 @@ +/* + * Test program for C compiler; if this doesn't compile, the + * C compiler is seriously broken. + */ + +#include +#include + +int main(void) +{ + printf("Hello, World!\n"); + return 0; +} diff --git a/win32/syslinux.c b/win32/syslinux.c new file mode 100644 index 0000000..1699125 --- /dev/null +++ b/win32/syslinux.c @@ -0,0 +1,540 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2003 Lars Munch Christensen - All Rights Reserved + * Copyright 1998-2008 H. Peter Anvin - All Rights Reserved + * + * Based on the Linux installer program for SYSLINUX by H. Peter Anvin + * + * 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * syslinux-mingw.c - Win2k/WinXP installer program for SYSLINUX + */ + +#include +#include +#include + +#include "syslinux.h" +#include "libfat.h" + +#ifdef __GNUC__ +# define noreturn void __attribute__((noreturn)) +#else +# define noreturn void +#endif + +void error(char* msg); + +/* Begin stuff for MBR code */ + +#include + +#define SECTOR_SIZE 512 +#define PART_TABLE 0x1be +#define PART_SIZE 0x10 +#define PART_COUNT 4 +#define PART_ACTIVE 0x80 + +// The following struct should be in the ntddstor.h file, but I didn't have it. +// TODO: Make this a conditional compilation +typedef struct _STORAGE_DEVICE_NUMBER { + DEVICE_TYPE DeviceType; + ULONG DeviceNumber; + ULONG PartitionNumber; +} STORAGE_DEVICE_NUMBER, *PSTORAGE_DEVICE_NUMBER; + +BOOL GetStorageDeviceNumberByHandle( HANDLE handle, const STORAGE_DEVICE_NUMBER *sdn ) { + BOOL result = FALSE; + DWORD count; + + if ( DeviceIoControl( handle, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, + 0, (LPVOID)sdn, sizeof( *sdn ), &count, NULL ) ) { + result = TRUE; + } + else { + error("GetDriveNumber: DeviceIoControl failed"); + } + + return( result ); +} + +int GetBytesPerSector( HANDLE drive ) { + int result = 0; + DISK_GEOMETRY g; + DWORD count; + + if ( DeviceIoControl( drive, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, + &g, sizeof( g ), &count, NULL ) ) { + result = g.BytesPerSector; + } + + return( result ); +} + +BOOL FixMBR(int driveNum, int partitionNum, int write_mbr, int set_active) { + BOOL result = TRUE; + HANDLE drive; + + char driveName[128]; + + sprintf( driveName, "\\\\.\\PHYSICALDRIVE%d", driveNum ); + + drive = CreateFile( driveName, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + NULL ); + + if( drive == INVALID_HANDLE_VALUE ) { + error("Accessing physical drive"); + result = FALSE; + } + + if( result ) { + unsigned char sector[SECTOR_SIZE]; + DWORD howMany; + + if( GetBytesPerSector( drive ) != SECTOR_SIZE ) { + fprintf(stderr, "Error: Sector size of this drive is %d; must be %d\n", + GetBytesPerSector( drive ), SECTOR_SIZE ); + result = FALSE; + } + + if ( result ) { + if ( ReadFile( drive, sector, sizeof( sector ), &howMany, NULL ) == 0 ) { + error("Reading raw drive"); + result = FALSE; + } else if ( howMany != sizeof( sector ) ) { + fprintf(stderr, "Error: ReadFile on drive only got %d of %d bytes\n", + (int)howMany, sizeof( sector ) ); + result = FALSE; + } + } + + // Copy over the MBR code if specified (-m) + if ( write_mbr ) { + if ( result ) { + if ( syslinux_mbr_len >= PART_TABLE ) { + fprintf(stderr, "Error: MBR will not fit; not writing\n" ); + result = FALSE; + } else { + memcpy( sector, syslinux_mbr, syslinux_mbr_len ); + } + } + } + + // Check that our partition is active if specified (-a) + if ( set_active ) { + if ( sector[ PART_TABLE + ( PART_SIZE * ( partitionNum - 1 ) ) ] != 0x80 ) { + int p; + for ( p = 0; p < PART_COUNT; p++ ) + sector[ PART_TABLE + ( PART_SIZE * p ) ] = ( p == partitionNum - 1 ? 0x80 : 0 ); + } + } + + if ( result ) { + SetFilePointer( drive, 0, NULL, FILE_BEGIN ); + + if ( WriteFile( drive, sector, sizeof( sector ), &howMany, NULL ) == 0 ) { + error("Writing MBR"); + result = FALSE; + } else if ( howMany != sizeof( sector ) ) { + fprintf(stderr, "Error: WriteFile on drive only wrote %d of %d bytes\n", + (int)howMany, sizeof( sector ) ); + result = FALSE; + } + } + + if( !CloseHandle( drive ) ) { + error("CloseFile on drive"); + result = FALSE; + } + } + + return( result ); +} + +/* End stuff for MBR code */ + +const char *program; /* Name of program */ +const char *drive; /* Drive to install to */ + +/* + * Check Windows version. + * + * On Windows Me/98/95 you cannot open a directory, physical disk, or + * volume using CreateFile. + */ +int checkver(void) +{ + OSVERSIONINFO osvi; + + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osvi); + + return (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) && + ((osvi.dwMajorVersion > 4) || + ((osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion == 0))); +} + +/* + * Windows error function + */ +void error(char* msg) +{ + LPVOID lpMsgBuf; + + /* Format the Windows error message */ + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) &lpMsgBuf, 0, NULL ); + + /* Print it */ + fprintf(stderr, "%s: %s", msg, (char*) lpMsgBuf); + + /* Free the buffer */ + LocalFree(lpMsgBuf); +} + +/* + * Wrapper for ReadFile suitable for libfat + */ +int libfat_readfile(intptr_t pp, void *buf, size_t secsize, libfat_sector_t sector) +{ + uint64_t offset = (uint64_t)sector * secsize; + LONG loword = (LONG)offset; + LONG hiword = (LONG)(offset >> 32); + LONG hiwordx = hiword; + DWORD bytes_read; + + if ( SetFilePointer((HANDLE)pp, loword, &hiwordx, FILE_BEGIN) != loword || + hiword != hiwordx || + !ReadFile((HANDLE)pp, buf, secsize, &bytes_read, NULL) || + bytes_read != secsize ) { + fprintf(stderr, "Cannot read sector %u\n", sector); + exit(1); + } + + return secsize; +} + +noreturn usage(void) +{ + fprintf(stderr, "Usage: syslinux.exe [-sfmar][-d directory] : [bootsecfile]\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + HANDLE f_handle, d_handle; + DWORD bytes_read; + DWORD bytes_written; + DWORD drives; + UINT drive_type; + + static unsigned char sectbuf[512]; + char **argp, *opt; + static char drive_name[] = "\\\\.\\?:"; + static char drive_root[] = "?:\\"; + static char ldlinux_name[] = "?:\\ldlinux.sys" ; + const char *errmsg; + struct libfat_filesystem *fs; + libfat_sector_t s, *secp, sectors[65]; /* 65 is maximum possible */ + uint32_t ldlinux_cluster; + int nsectors; + const char *bootsecfile = NULL; + const char *subdir = NULL; + + int force = 0; /* -f (force) option */ + int mbr = 0; /* -m (MBR) option */ + int setactive = 0; /* -a (set partition active) */ + int stupid = 0; /* -s (stupid) option */ + int raid_mode = 0; /* -r (RAID) option */ + + (void)argc; + + if (!checkver()) { + fprintf(stderr, "You need to be running at least Windows NT; use syslinux.com instead.\n"); + exit(1); + } + + program = argv[0]; + drive = NULL; + + for ( argp = argv+1 ; *argp ; argp++ ) { + if ( **argp == '-' ) { + opt = *argp + 1; + if ( !*opt ) + usage(); + + while ( *opt ) { + switch ( *opt ) { + case 's': /* Use "safe, slow and stupid" code */ + stupid = 1; + break; + case 'r': /* RAID mode */ + raid_mode = 1; + break; + case 'f': /* Force install */ + force = 1; + break; + case 'm': /* Install MBR */ + mbr = 1; + break; + case 'a': /* Mark this partition active */ + setactive = 1; + break; + case 'd': + if ( argp[1] ) + subdir = *++argp; + break; + default: + usage(); + break; + } + opt++; + } + } else { + if ( bootsecfile ) + usage(); + else if ( drive ) + bootsecfile = *argp; + else + drive = *argp; + } + } + + if ( !drive || !isalpha(drive[0]) || drive[1] != ':' || drive[2] ) + usage(); + + /* Test if drive exists */ + drives = GetLogicalDrives(); + if(!(drives & ( 1 << (tolower(drive[0]) - 'a')))) { + fprintf(stderr, "No such drive %c:\n", drive[0]); + exit(1); + } + + /* Determines the drive type */ + drive_name[4] = drive[0]; + ldlinux_name[0] = drive[0]; + drive_root[0] = drive[0]; + drive_type = GetDriveType(drive_root); + + /* Test for removeable media */ + if ((drive_type == DRIVE_FIXED) && (force == 0)) { + fprintf(stderr, "Not a removable drive (use -f to override) \n"); + exit(1); + } + + /* Test for unsupported media */ + if ((drive_type != DRIVE_FIXED) && (drive_type != DRIVE_REMOVABLE)) { + fprintf(stderr, "Unsupported media\n"); + exit(1); + } + + /* + * First open the drive + */ + d_handle = CreateFile(drive_name, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL ); + + if (d_handle == INVALID_HANDLE_VALUE) { + error("Could not open drive"); + exit(1); + } + + /* + * Make sure we can read the boot sector + */ + if ( !ReadFile(d_handle, sectbuf, 512, &bytes_read, NULL) ) { + error("Reading boot sector"); + exit(1); + } + if (bytes_read != 512) { + fprintf(stderr, "Could not read the whole boot sector\n"); + exit(1); + } + + /* Check to see that what we got was indeed an MS-DOS boot sector/superblock */ + if( (errmsg = syslinux_check_bootsect(sectbuf)) ) { + fprintf(stderr, "%s\n", errmsg); + exit(1); + } + + /* Change to normal attributes to enable deletion */ + /* Just ignore error if the file do not exists */ + SetFileAttributes(ldlinux_name, FILE_ATTRIBUTE_NORMAL); + + /* Delete the file */ + /* Just ignore error if the file do not exists */ + DeleteFile(ldlinux_name); + + /* Create ldlinux.sys file */ + f_handle = CreateFile(ldlinux_name, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | + FILE_ATTRIBUTE_HIDDEN, + NULL ); + + if (f_handle == INVALID_HANDLE_VALUE) { + error("Unable to create ldlinux.sys"); + exit(1); + } + + /* Write ldlinux.sys file */ + if (!WriteFile(f_handle, syslinux_ldlinux, syslinux_ldlinux_len, &bytes_written, NULL)) { + error("Could not write ldlinux.sys"); + exit(1); + } + + if (bytes_written != syslinux_ldlinux_len) { + fprintf(stderr, "Could not write whole ldlinux.sys\n"); + exit(1); + } + + /* Now flush the media */ + if(!FlushFileBuffers(f_handle)) { + error("FlushFileBuffers failed"); + exit(1); + } + + /* Map the file (is there a better way to do this?) */ + fs = libfat_open(libfat_readfile, (intptr_t)d_handle); + ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL); + secp = sectors; + nsectors = 0; + s = libfat_clustertosector(fs, ldlinux_cluster); + while ( s && nsectors < 65 ) { + *secp++ = s; + nsectors++; + s = libfat_nextsector(fs, s); + } + libfat_close(fs); + + /* + * Patch ldlinux.sys and the boot sector + */ + syslinux_patch(sectors, nsectors, stupid, raid_mode); + + /* + * Rewrite the file + */ + if ( SetFilePointer(f_handle, 0, NULL, FILE_BEGIN) != 0 || + !WriteFile(f_handle, syslinux_ldlinux, syslinux_ldlinux_len, &bytes_written, NULL) || + bytes_written != syslinux_ldlinux_len ) { + error("Could not write ldlinux.sys"); + exit(1); + } + + /* If desired, fix the MBR */ + if( mbr || setactive ) { + STORAGE_DEVICE_NUMBER sdn; + if( GetStorageDeviceNumberByHandle( d_handle, &sdn ) ) { + if( !FixMBR(sdn.DeviceNumber, sdn.PartitionNumber, mbr, setactive) ) { + fprintf(stderr, "Did not successfully update the MBR; continuing...\n"); + } + } else { + fprintf(stderr, "Could not find device number for updating MBR; continuing...\n"); + } + } + + /* Close file */ + CloseHandle(f_handle); + + /* Move the file to the desired location */ + if (subdir) { + char new_ldlinux_name[strlen(subdir)+16]; + char *cp = new_ldlinux_name+3; + const char *sd; + int slash = 1; + + new_ldlinux_name[0] = drive[0]; + new_ldlinux_name[1] = ':'; + new_ldlinux_name[2] = '\\'; + + for (sd = subdir; *sd; sd++) { + char c = *sd; + + if (c == '/' || c == '\\') { + if (slash) + continue; + c = '\\'; + slash = 1; + } else { + slash = 0; + } + + *cp++ = c; + } + + /* Skip if subdirectory == root */ + if (cp > new_ldlinux_name+3) { + if (!slash) + *cp++ = '\\'; + + memcpy(cp, "ldlinux.sys", 12); + + /* Delete any previous file */ + SetFileAttributes(new_ldlinux_name, FILE_ATTRIBUTE_NORMAL); + DeleteFile(new_ldlinux_name); + if (!MoveFile(ldlinux_name, new_ldlinux_name)) + SetFileAttributes(ldlinux_name, FILE_ATTRIBUTE_READONLY | + FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN); + else + SetFileAttributes(new_ldlinux_name, FILE_ATTRIBUTE_READONLY | + FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN); + } + } + + + + /* Make the syslinux boot sector */ + syslinux_make_bootsect(sectbuf); + + /* Write the syslinux boot sector into the boot sector */ + if ( bootsecfile ) { + f_handle = CreateFile(bootsecfile, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_ARCHIVE, + NULL ); + if (f_handle == INVALID_HANDLE_VALUE) { + error("Unable to create bootsector file"); + exit(1); + } + if (!WriteFile(f_handle, sectbuf, 512, &bytes_written, NULL)) { + error("Could not write boot sector file"); + exit(1); + } + CloseHandle(f_handle); + } else { + SetFilePointer(d_handle, 0, NULL, FILE_BEGIN); + WriteFile( d_handle, sectbuf, 512, &bytes_written, NULL ) ; + } + + if(bytes_written != 512) { + fprintf(stderr, "Could not write the whole boot sector\n"); + exit(1); + } + + /* Close file */ + CloseHandle(d_handle); + + /* Done! */ + return 0; +} diff --git a/win32/syslinux.exe b/win32/syslinux.exe new file mode 100755 index 0000000000000000000000000000000000000000..41ac6caaca6e57c24af144d132ec4702a63182a7 GIT binary patch literal 27648 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 Ms{QZq{|gHI54Z=2lK=n! literal 0 HcmV?d00001 -- 2.39.5

    $ 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�hXPadgvFM)OnWiTs`DMp@5Kez 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
    Q4esBs_-#YB&X1#Af5OxKEV`D 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

    Bof`S|;gcGCt%mD4L${1J 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

  2. |uW8Q*EmNYBZ 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�N{E}IE^p> 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 R-h|kPbb+`5vS}R7{eS*oJ+c4* literal 0 HcmV?d00001 diff --git a/com32/cmenu/display.c b/com32/cmenu/display.c new file mode 100644 index 0000000..5391d7f --- /dev/null +++ b/com32/cmenu/display.c @@ -0,0 +1,37 @@ +/* -*- c -*- ------------------------------------------------------------- * + * + * Copyright 2004-2006 Murali Krishnan Ganapathy - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +#include "help.h" +#include "com32io.h" +#include "menu.h" +#include "tui.h" +#include +#include +#include + + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + csprint("Usage: display.c32 \n",0x07); + exit(1); + } + + init_help(NULL); // No base dir, so all filenames must be absolute + runhelp(argv[1]); + close_help(); + return 0; +} diff --git a/com32/cmenu/display.c32 b/com32/cmenu/display.c32 new file mode 100755 index 0000000000000000000000000000000000000000..feb79046310bed1bdb45d3be6ca33020205fb018 GIT binary patch literal 11612 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 ChPb2v literal 0 HcmV?d00001 diff --git a/com32/cmenu/libmenu/com32io.c b/com32/cmenu/libmenu/com32io.c new file mode 100644 index 0000000..31aec5d --- /dev/null +++ b/com32/cmenu/libmenu/com32io.c @@ -0,0 +1,146 @@ +/* -*- c -*- ------------------------------------------------------------- * + * + * Copyright 2004-2005 Murali Krishnan Ganapathy - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include "com32io.h" +#include "syslnx.h" + +com32sys_t inreg,outreg; // Global register sets for use + +/* Print character and attribute at cursor */ +void cprint(char chr,char attr,unsigned int times,char disppage) +{ + REG_AH(inreg) = 0x09; + REG_AL(inreg) = chr; + REG_BH(inreg) = disppage; + REG_BL(inreg) = attr; + REG_CX(inreg) = times; + __intcall(0x10,&inreg,&outreg); +} + +void setdisppage(char num) // Set the display page to specified number +{ + REG_AH(inreg) = 0x05; + REG_AL(inreg) = num; + __intcall(0x10,&inreg,&outreg); +} + +char getdisppage() // Get current display page +{ + REG_AH(inreg) = 0x0f; + __intcall(0x10,&inreg,&outreg); + return REG_BH(outreg); +} + +void getpos(char * row, char * col, char page) +{ + REG_AH(inreg) = 0x03; + REG_BH(inreg) = page; + __intcall(0x10,&inreg,&outreg); + *row = REG_DH(outreg); + *col = REG_DL(outreg); +} + +void gotoxy(char row,char col, char page) +{ + REG_AH(inreg) = 0x02; + REG_BH(inreg) = page; + REG_DX(inreg) = (row << 8)+col; + __intcall(0x10,&inreg,&outreg); +} + +unsigned char sleep(unsigned int msec) +{ + unsigned long micro = 1000*msec; + + REG_AH(inreg) = 0x86; + REG_CX(inreg) = (micro >> 16); + REG_DX(inreg) = (micro & 0xFFFF); + __intcall(0x15,&inreg,&outreg); + return REG_AH(outreg); +} + +void beep() +{ + REG_AH(inreg) = 0x0E; + REG_AL(inreg) = 0x07; + REG_BH(inreg) = 0; + __intcall(0x10,&inreg,&outreg); +} + +void scrollupwindow(char top, char left, char bot, char right, char attr,char numlines) +{ + REG_AH(inreg) = 0x06; + REG_AL(inreg) = numlines; + REG_BH(inreg) = attr; // Attribute to write blanks lines + REG_DX(inreg) = (bot << 8) + right; // BOT RIGHT corner of window + REG_CX(inreg) = (top << 8) + left; // TOP LEFT of window + __intcall(0x10,&inreg,&outreg); +} + +char inputc(char * scancode) +{ + syslinux_idle(); /* So syslinux can perform periodic activity */ + REG_AH(inreg) = 0x10; + __intcall(0x16,&inreg,&outreg); + if (scancode) *scancode = REG_AH(outreg); + return REG_AL(outreg); +} + +void getcursorshape(char *start, char *end) +{ + char page = getdisppage(); + REG_AH(inreg) = 0x03; + REG_BH(inreg) = page; + __intcall(0x10,&inreg,&outreg); + *start = REG_CH(outreg); + *end = REG_CL(outreg); +} + +void setcursorshape(char start, char end) +{ + REG_AH(inreg) = 0x01; + REG_CH(inreg) = start; + REG_CL(inreg) = end; + __intcall(0x10,&inreg,&outreg); +} + +char getchar(void) +{ + REG_AH(inreg) = 0x08; + __intcall(0x21,&inreg,&outreg); + return REG_AL(outreg); +} + +void setvideomode(char mode) +{ + REG_AH(inreg) = 0x00; + REG_AL(inreg) = mode; + __intcall(0x10,&inreg,&outreg); +} + +unsigned char checkkbdbuf() +{ + REG_AH(inreg) = 0x11; + __intcall(0x16,&inreg,&outreg); + return !(outreg.eflags.l & EFLAGS_ZF); +} + +// Get char displayed at current position +unsigned char getcharat(char page) +{ + REG_AH(inreg) = 0x08; + REG_BH(inreg) = page; + __intcall(0x16,&inreg,&outreg); + return REG_AL(outreg); +} diff --git a/com32/cmenu/libmenu/com32io.h b/com32/cmenu/libmenu/com32io.h new file mode 100644 index 0000000..78ce72f --- /dev/null +++ b/com32/cmenu/libmenu/com32io.h @@ -0,0 +1,112 @@ +/* -*- c -*- ------------------------------------------------------------- * + * + * Copyright 2004-2005 Murali Krishnan Ganapathy - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef __COM32IO_H__ +#define __COM32IO_H__ + +#include + +#ifndef NULL +#define NULL ((void *)0) +#endif + +/* BIOS Assisted output routines */ + +void cswprint(const char *str, char attr, char left); +// Print a C str (NUL-terminated) respecting the left edge of window +// i.e. \n in str will move cursor to column left +// Print a C str (NUL-terminated) + +static inline void csprint(const char *str, char attr) +{ + cswprint(str,attr,0); +} + +void cprint(char chr,char attr,unsigned int times, char disppage); // Print a char + +void setdisppage(char num); // Set the display page to specified number + +char getdisppage(); // Get current display page + +void gotoxy(char row,char col, char page); + +void getpos(char * row, char * col, char page); + +char inputc(char * scancode); // Return ASCII char by val, and scancode by reference + +static inline void putch(char x, char attr, char page) +{ + cprint(x,attr,1,page); +} + +void setcursorshape(char start,char end); // Set cursor shape +void getcursorshape(char *start,char *end); // Get shape for current page + +// Get char displayed at current position in specified page +unsigned char getcharat(char page); + +static inline void cursoroff(void) /* Turns off cursor */ +{ + setcursorshape(32,33); +} + +static inline void cursoron(void) /* Turns on cursor */ +{ + setcursorshape(6,7); +} + +static inline unsigned char readbiosb(unsigned int ofs) +{ + return *((unsigned char *)MK_PTR(0,ofs)); +} + +static inline char getnumrows() +{ + return readbiosb(0x484); // Actually numrows - 1 +} + +static inline char getnumcols(void) +{ + return readbiosb(0x44a); // Actually numcols +} + +static inline char getshiftflags(void) +{ + return readbiosb(0x417); +} + +void scrollupwindow(char top, char left, char bot,char right,char attr,char numlines); //Scroll up given window + +static inline void scrollup(void) //Scroll up display screen by one line +{ + scrollupwindow(0,0,getnumrows(),getnumcols(),0x07,1); +} + +void setvideomode(char mode); // Set the video mode. + +static inline char getvideomode(void) // Get the current video mode +{ + return readbiosb(0x449); +} + +unsigned char sleep(unsigned int msec); // Sleep for specified time + +void beep(); // A Bell + +unsigned char checkkbdbuf(); // Check to see if there is kbd buffer is non-empty? + +static inline void clearkbdbuf() // Clear the kbd buffer (how many chars removed?) +{ + while (checkkbdbuf()) inputc(NULL); +} + +#endif diff --git a/com32/cmenu/libmenu/com32io.o b/com32/cmenu/libmenu/com32io.o new file mode 100644 index 0000000000000000000000000000000000000000..0316dc13b033213a63c9dd1ed1a43b306aaecdad GIT binary patch literal 2688 zcma)+O-vkh7{=dWSy*amp(#eH=xSCADjT3+TVkRKZL!fB)259U0@;sQSXp*9JF^r_ z)hJqbgNgNGnwX|Y^^w1_=8cpqqHa*mjg9jr%@6IzYB{Xr8+28+p zKmT8|FYkV8k6{?%J%$8iMTwjUxSq~3nq{3-%jEU-3&Hzg2+cI&_bYXMtv_Ghj-SPU zmX?;LBN$Eouzr3ywtlU!_nHwlyI3`O!~A6Ce5%xS7sd#_Fnc*< z%%w_emFycR&(LkXVIio6suzMw_m``^!3n4w?`93#lW;yDpoz)H?VSy8`q52Dd?OI z9bk)c8TMIyNmkmFPbV-PF1I<1Hf~1nmZ$IfYs&tqU6IOi>0-kjD3B`EYMoWnE1w7^ z6)Mg9rc~#PTE4QcUvjFab#t+BJ-X-m`sRV&o}TV-bMMm!Tf&{uZPAW!M_b#D_V%`} zaPxjM8Ga(>xc1iVEh15SV$_MnK_~BW*c;`|Ow=*Q9f>AmPE4Y4+m>j;8XYyWuw^aB zjG{@a?ZgrzX#0Pkdk|>gtzv?Hr`IYdfJzSU1Z!5za=cpP2I4}nX}-qkST{g5KYIy# z1K9NO>OF+9#(VBt#kCv@mHwR!^dd^>$(b?@^EtH9&M)nn{;Y24m&g!|*gDSg`@ z%|VfA7Ur9u-(D%dFL`$5&HtdP{T)<$yXa;GmQT;q zLAlKx%ERC$tX{u=%~D-2e=O;F`t^O#-lOZ%IDK{hHGc=#k2g%c8$614EA>9`G~NfO zHTE3dgVcH_@5Z8AsB>U1^kdXx;4$cS>KDN?(2r8T2L1`{^*au|1qbvv{indg&|&J2 zz!#t&q5cf~1iG8L`~_pIkFHnnDQ)XWThW9JnNDIjmXA4-&gRV_u?mi2a4?;960uB1 z62oR|Xh&^Fv(%HiJU^A^%=0DQRG|ddHT;% gUr;~hbBXpV. + * + * An excellent reference on the underlying algorithm (and related + * algorithms) is: + * + * B. Schneier, Applied Cryptography: protocols, algorithms, + * and source code in C, John Wiley & Sons, 1994. + * + * Note that in that book's description of DES the lookups for the initial, + * pbox, and final permutations are inverted (this has been brought to the + * attention of the author). A list of errata for this book has been + * posted to the sci.crypt newsgroup by the author and is available for FTP. + * + * ARCHITECTURE ASSUMPTIONS: + * It is assumed that the 8-byte arrays passed by reference can be + * addressed as arrays of u_int32_t's (ie. the CPU is not picky about + * alignment). + */ + + +#define LOWSPACE + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +typedef unsigned long my_u_int32_t; +typedef unsigned char my_u_char_t; + +/* Re-entrantify me -- all this junk needs to be in + * struct crypt_data to make this really reentrant... */ +static my_u_char_t inv_key_perm[64]; +static my_u_char_t inv_comp_perm[56]; +static my_u_char_t u_sbox[8][64]; +static my_u_char_t un_pbox[32]; +static my_u_int32_t en_keysl[16], en_keysr[16]; +static my_u_int32_t de_keysl[16], de_keysr[16]; + +#ifndef LOWSPACE +static my_u_int32_t ip_maskl[8][256], ip_maskr[8][256]; +static my_u_int32_t fp_maskl[8][256], fp_maskr[8][256]; +static my_u_int32_t key_perm_maskl[8][128], key_perm_maskr[8][128]; +static my_u_int32_t comp_maskl[8][128], comp_maskr[8][128]; +#endif + +static my_u_int32_t saltbits; +static my_u_int32_t old_salt; +static my_u_int32_t old_rawkey0, old_rawkey1; + +#ifdef LOWSPACE +static my_u_int32_t common[8][256]; +#endif + +/* Static stuff that stays resident and doesn't change after + * being initialized, and therefore doesn't need to be made + * reentrant. */ +static my_u_char_t init_perm[64], final_perm[64]; +static my_u_char_t m_sbox[4][4096]; + +#ifndef LOWSPACE +static my_u_int32_t psbox[4][256]; +#endif + +/* A pile of data */ +static const my_u_char_t ascii64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static const my_u_char_t IP[64] = { + 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7 +}; + +static const my_u_char_t key_perm[56] = { + 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, + 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 +}; + +static const my_u_char_t key_shifts[16] = { + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 +}; + +static const my_u_char_t comp_perm[48] = { + 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32 +}; + +/* + * No E box is used, as it's replaced by some ANDs, shifts, and ORs. + */ + +static const my_u_char_t sbox[8][64] = { + { + 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 + }, + { + 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 + }, + { + 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 + }, + { + 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 + }, + { + 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 + }, + { + 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 + }, + { + 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 + }, + { + 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 + } +}; + +static const my_u_char_t pbox[32] = { + 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, + 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 +}; + +static const my_u_int32_t bits32[32] = +{ + 0x80000000, 0x40000000, 0x20000000, 0x10000000, + 0x08000000, 0x04000000, 0x02000000, 0x01000000, + 0x00800000, 0x00400000, 0x00200000, 0x00100000, + 0x00080000, 0x00040000, 0x00020000, 0x00010000, + 0x00008000, 0x00004000, 0x00002000, 0x00001000, + 0x00000800, 0x00000400, 0x00000200, 0x00000100, + 0x00000080, 0x00000040, 0x00000020, 0x00000010, + 0x00000008, 0x00000004, 0x00000002, 0x00000001 +}; + +static const my_u_int32_t bits28[28] = +{ + 0x08000000, 0x04000000, 0x02000000, 0x01000000, + 0x00800000, 0x00400000, 0x00200000, 0x00100000, + 0x00080000, 0x00040000, 0x00020000, 0x00010000, + 0x00008000, 0x00004000, 0x00002000, 0x00001000, + 0x00000800, 0x00000400, 0x00000200, 0x00000100, + 0x00000080, 0x00000040, 0x00000020, 0x00000010, + 0x00000008, 0x00000004, 0x00000002, 0x00000001 +}; + +static const my_u_int32_t bits24[24] = +{ + 0x00800000, 0x00400000, 0x00200000, 0x00100000, + 0x00080000, 0x00040000, 0x00020000, 0x00010000, + 0x00008000, 0x00004000, 0x00002000, 0x00001000, + 0x00000800, 0x00000400, 0x00000200, 0x00000100, + 0x00000080, 0x00000040, 0x00000020, 0x00000010, + 0x00000008, 0x00000004, 0x00000002, 0x00000001 +}; + +static const my_u_char_t bits8[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; +// static const my_u_int32_t *bits28, *bits24; + + +static int +ascii_to_bin(char ch) +{ + if (ch > 'z') + return(0); + if (ch >= 'a') + return(ch - 'a' + 38); + if (ch > 'Z') + return(0); + if (ch >= 'A') + return(ch - 'A' + 12); + if (ch > '9') + return(0); + if (ch >= '.') + return(ch - '.'); + return(0); +} + +static void +des_init(void) +{ + +#ifdef LOWSPACE + int i, j, b; +#else + int i, j, b, k, inbit, obit; + my_u_int32_t *p, *il, *ir, *fl, *fr; +#endif + static int des_initialised = 0; + + if (des_initialised==1) + return; + + old_rawkey0 = old_rawkey1 = 0L; + saltbits = 0L; + old_salt = 0L; + // bits24 = (bits28 = bits32 + 4) + 4; + + /* + * Invert the S-boxes, reordering the input bits. + */ + for (i = 0; i < 8; i++) + for (j = 0; j < 64; j++) { + b = (j & 0x20) | ((j & 1) << 4) | ((j >> 1) & 0xf); + u_sbox[i][j] = sbox[i][b]; + } + + /* + * Convert the inverted S-boxes into 4 arrays of 8 bits. + * Each will handle 12 bits of the S-box input. + */ + for (b = 0; b < 4; b++) + for (i = 0; i < 64; i++) + for (j = 0; j < 64; j++) + m_sbox[b][(i << 6) | j] = + (my_u_char_t)((u_sbox[(b << 1)][i] << 4) | + u_sbox[(b << 1) + 1][j]); + + /* + * Set up the initial & final permutations into a useful form, and + * initialise the inverted key permutation. + */ + for (i = 0; i < 64; i++) { + init_perm[final_perm[i] = IP[i] - 1] = (my_u_char_t)i; + inv_key_perm[i] = 255; + } + + /* + * Invert the key permutation and initialise the inverted key + * compression permutation. + */ + for (i = 0; i < 56; i++) { + inv_key_perm[key_perm[i] - 1] = (my_u_char_t)i; + inv_comp_perm[i] = 255; + } + + /* + * Invert the key compression permutation. + */ + for (i = 0; i < 48; i++) { + inv_comp_perm[comp_perm[i] - 1] = (my_u_char_t)i; + } + + /* + * Set up the OR-mask arrays for the initial and final permutations, + * and for the key initial and compression permutations. + */ + +#ifndef LOWSPACE + for (k = 0; k < 8; k++) { + for (i = 0; i < 256; i++) { + *(il = &ip_maskl[k][i]) = 0L; + *(ir = &ip_maskr[k][i]) = 0L; + *(fl = &fp_maskl[k][i]) = 0L; + *(fr = &fp_maskr[k][i]) = 0L; + for (j = 0; j < 8; j++) { + inbit = 8 * k + j; + if (i & bits8[j]) { + if ((obit = init_perm[inbit]) < 32) + *il |= bits32[obit]; + else + *ir |= bits32[obit-32]; + if ((obit = final_perm[inbit]) < 32) + *fl |= bits32[obit]; + else + *fr |= bits32[obit - 32]; + } + } + } + for (i = 0; i < 128; i++) { + *(il = &key_perm_maskl[k][i]) = 0L; + *(ir = &key_perm_maskr[k][i]) = 0L; + for (j = 0; j < 7; j++) { + inbit = 8 * k + j; + if (i & bits8[j + 1]) { + if ((obit = inv_key_perm[inbit]) == 255) + continue; + if (obit < 28) + *il |= bits28[obit]; + else + *ir |= bits28[obit - 28]; + } + } + *(il = &comp_maskl[k][i]) = 0L; + *(ir = &comp_maskr[k][i]) = 0L; + for (j = 0; j < 7; j++) { + inbit = 7 * k + j; + if (i & bits8[j + 1]) { + if ((obit=inv_comp_perm[inbit]) == 255) + continue; + if (obit < 24) + *il |= bits24[obit]; + else + *ir |= bits24[obit - 24]; + } + } + } + } +#endif + + /* + * Invert the P-box permutation, and convert into OR-masks for + * handling the output of the S-box arrays setup above. + */ + for (i = 0; i < 32; i++) + un_pbox[pbox[i] - 1] = (my_u_char_t)i; + +#ifndef LOWSPACE + for (b = 0; b < 4; b++) + for (i = 0; i < 256; i++) { + *(p = &psbox[b][i]) = 0L; + for (j = 0; j < 8; j++) { + if (i & bits8[j]) + *p |= bits32[un_pbox[8 * b + j]]; + } + } +#endif + des_initialised = 1; +} + + +#ifdef LOWSPACE + +static void +setup_ip_maskl(void) +{ + int i, j, k, inbit, obit; + my_u_int32_t *il; + + for (k = 0; k < 8; k++) { + for (i = 0; i < 256; i++) { + *(il = &common[k][i]) = 0L; + for (j = 0; j < 8; j++) { + inbit = 8 * k + j; + if (i & bits8[j]) { + if ((obit = init_perm[inbit]) < 32) + *il |= bits32[obit]; + } + } + } + } +} + +static void +setup_ip_maskr(void) +{ + int i, j, k, inbit, obit; + my_u_int32_t *ir; + + for (k = 0; k < 8; k++) { + for (i = 0; i < 256; i++) { + *(ir = &common[k][i]) = 0L; + for (j = 0; j < 8; j++) { + inbit = 8 * k + j; + if (i & bits8[j]) { + if ((obit = init_perm[inbit]) >= 32) + *ir |= bits32[obit-32]; + } + } + } + } +} + +static void +setup_fp_maskl(void) +{ + int i, j, k, inbit, obit; + my_u_int32_t *fl; + + for (k = 0; k < 8; k++) { + for (i = 0; i < 256; i++) { + *(fl = &common[k][i]) = 0L; + for (j = 0; j < 8; j++) { + inbit = 8 * k + j; + if (i & bits8[j]) { + if ((obit = final_perm[inbit]) < 32) + *fl |= bits32[obit]; + } + } + } + } +} + +static void +setup_fp_maskr(void) +{ + int i, j, k, inbit, obit; + my_u_int32_t *fr; + + for (k = 0; k < 8; k++) { + for (i = 0; i < 256; i++) { + *(fr = &common[k][i]) = 0L; + for (j = 0; j < 8; j++) { + inbit = 8 * k + j; + if (i & bits8[j]) { + if ((obit = final_perm[inbit]) >= 32) + *fr |= bits32[obit - 32]; + } + } + } + } +} + +static void +setup_key_perm_maskl(void) +{ + int i, j, k, inbit, obit; + my_u_int32_t *il; + + for (k = 0; k < 8; k++) { + for (i = 0; i < 128; i++) { + *(il = &common[k][i]) = 0L; + for (j = 0; j < 7; j++) { + inbit = 8 * k + j; + if (i & bits8[j + 1]) { + if ((obit = inv_key_perm[inbit]) == 255) + continue; + if (obit < 28) + *il |= bits28[obit]; + } + } + } + } +} + +static void +setup_key_perm_maskr(void) +{ + int i, j, k, inbit, obit; + my_u_int32_t *ir; + + for (k = 0; k < 8; k++) { + for (i = 0; i < 128; i++) { + *(ir = &common[k][i]) = 0L; + for (j = 0; j < 7; j++) { + inbit = 8 * k + j; + if (i & bits8[j + 1]) { + if ((obit = inv_key_perm[inbit]) == 255) + continue; + if (obit >= 28) + *ir |= bits28[obit - 28]; + } + } + } + } +} + +static void +setup_comp_maskl(void) +{ + int i, j, k, inbit, obit; + my_u_int32_t *il; + + for (k = 0; k < 8; k++) { + for (i = 0; i < 128; i++) { + *(il = &common[k][i]) = 0L; + for (j = 0; j < 7; j++) { + inbit = 7 * k + j; + if (i & bits8[j + 1]) { + if ((obit=inv_comp_perm[inbit]) == 255) + continue; + if (obit < 24) + *il |= bits24[obit]; + } + } + } + } +} + +static void +setup_comp_maskr(void) +{ + int i, j, k, inbit, obit; + my_u_int32_t *ir; + + for (k = 0; k < 8; k++) { + for (i = 0; i < 128; i++) { + *(ir = &common[k][i]) = 0L; + for (j = 0; j < 7; j++) { + inbit = 7 * k + j; + if (i & bits8[j + 1]) { + if ((obit=inv_comp_perm[inbit]) == 255) + continue; + if (obit >= 24) + *ir |= bits24[obit - 24]; + } + } + } + } +} + +static void +setup_psbox(void) +{ + int i, j, b; + my_u_int32_t *p; + + for (b = 0; b < 4; b++) + for (i = 0; i < 256; i++) { + *(p = &common[b][i]) = 0L; + for (j = 0; j < 8; j++) { + if (i & bits8[j]) + *p |= bits32[un_pbox[8 * b + j]]; + } + } +} + +#endif + +static void +setup_salt(my_u_int32_t salt) +{ + my_u_int32_t obit, saltbit; + int i; + + if (salt == old_salt) + return; + old_salt = salt; + + saltbits = 0L; + saltbit = 1; + obit = 0x800000; + for (i = 0; i < 24; i++) { + if (salt & saltbit) + saltbits |= obit; + saltbit <<= 1; + obit >>= 1; + } +} + + +static my_u_int32_t char_to_int(const char *key) +{ + my_u_int32_t byte0,byte1,byte2,byte3; + byte0 = (my_u_int32_t) (my_u_char_t) key[0]; + byte1 = (my_u_int32_t) (my_u_char_t) key[1]; + byte2 = (my_u_int32_t) (my_u_char_t) key[2]; + byte3 = (my_u_int32_t) (my_u_char_t) key[3]; + + return byte0 << 24 | byte1 << 16 | byte2 << 8 | byte3 ; +} + + +static int +des_setkey(const char *key) +{ + my_u_int32_t k0, k1, rawkey0, rawkey1; + int shifts, round; + + des_init(); + + /* rawkey0 = ntohl(*(const my_u_int32_t *) key); + * rawkey1 = ntohl(*(const my_u_int32_t *) (key + 4)); + */ + + rawkey0 = char_to_int(key); + rawkey1 = char_to_int(key+4); + + if ((rawkey0 | rawkey1) + && rawkey0 == old_rawkey0 + && rawkey1 == old_rawkey1) { + /* + * Already setup for this key. + * This optimisation fails on a zero key (which is weak and + * has bad parity anyway) in order to simplify the starting + * conditions. + */ + return(0); + } + old_rawkey0 = rawkey0; + old_rawkey1 = rawkey1; + + /* + * Do key permutation and split into two 28-bit subkeys. + */ + +#ifdef LOWSPACE + setup_key_perm_maskl(); + k0 = common[0][rawkey0 >> 25] + | common[1][(rawkey0 >> 17) & 0x7f] + | common[2][(rawkey0 >> 9) & 0x7f] + | common[3][(rawkey0 >> 1) & 0x7f] + | common[4][rawkey1 >> 25] + | common[5][(rawkey1 >> 17) & 0x7f] + | common[6][(rawkey1 >> 9) & 0x7f] + | common[7][(rawkey1 >> 1) & 0x7f]; + setup_key_perm_maskr(); + k1 = common[0][rawkey0 >> 25] + | common[1][(rawkey0 >> 17) & 0x7f] + | common[2][(rawkey0 >> 9) & 0x7f] + | common[3][(rawkey0 >> 1) & 0x7f] + | common[4][rawkey1 >> 25] + | common[5][(rawkey1 >> 17) & 0x7f] + | common[6][(rawkey1 >> 9) & 0x7f] + | common[7][(rawkey1 >> 1) & 0x7f]; +#else + k0 = key_perm_maskl[0][rawkey0 >> 25] + | key_perm_maskl[1][(rawkey0 >> 17) & 0x7f] + | key_perm_maskl[2][(rawkey0 >> 9) & 0x7f] + | key_perm_maskl[3][(rawkey0 >> 1) & 0x7f] + | key_perm_maskl[4][rawkey1 >> 25] + | key_perm_maskl[5][(rawkey1 >> 17) & 0x7f] + | key_perm_maskl[6][(rawkey1 >> 9) & 0x7f] + | key_perm_maskl[7][(rawkey1 >> 1) & 0x7f]; + k1 = key_perm_maskr[0][rawkey0 >> 25] + | key_perm_maskr[1][(rawkey0 >> 17) & 0x7f] + | key_perm_maskr[2][(rawkey0 >> 9) & 0x7f] + | key_perm_maskr[3][(rawkey0 >> 1) & 0x7f] + | key_perm_maskr[4][rawkey1 >> 25] + | key_perm_maskr[5][(rawkey1 >> 17) & 0x7f] + | key_perm_maskr[6][(rawkey1 >> 9) & 0x7f] + | key_perm_maskr[7][(rawkey1 >> 1) & 0x7f]; +#endif + + /* + * Rotate subkeys and do compression permutation. + */ + shifts = 0; + for (round = 0; round < 16; round++) { + my_u_int32_t t0, t1; + + shifts += key_shifts[round]; + + t0 = (k0 << shifts) | (k0 >> (28 - shifts)); + t1 = (k1 << shifts) | (k1 >> (28 - shifts)); + +#ifdef LOWSPACE + setup_comp_maskl(); + de_keysl[15 - round] = + en_keysl[round] = common[0][(t0 >> 21) & 0x7f] + | common[1][(t0 >> 14) & 0x7f] + | common[2][(t0 >> 7) & 0x7f] + | common[3][t0 & 0x7f] + | common[4][(t1 >> 21) & 0x7f] + | common[5][(t1 >> 14) & 0x7f] + | common[6][(t1 >> 7) & 0x7f] + | common[7][t1 & 0x7f]; + + setup_comp_maskr(); + de_keysr[15 - round] = + en_keysr[round] = common[0][(t0 >> 21) & 0x7f] + | common[1][(t0 >> 14) & 0x7f] + | common[2][(t0 >> 7) & 0x7f] + | common[3][t0 & 0x7f] + | common[4][(t1 >> 21) & 0x7f] + | common[5][(t1 >> 14) & 0x7f] + | common[6][(t1 >> 7) & 0x7f] + | common[7][t1 & 0x7f]; +#else + de_keysl[15 - round] = + en_keysl[round] = comp_maskl[0][(t0 >> 21) & 0x7f] + | comp_maskl[1][(t0 >> 14) & 0x7f] + | comp_maskl[2][(t0 >> 7) & 0x7f] + | comp_maskl[3][t0 & 0x7f] + | comp_maskl[4][(t1 >> 21) & 0x7f] + | comp_maskl[5][(t1 >> 14) & 0x7f] + | comp_maskl[6][(t1 >> 7) & 0x7f] + | comp_maskl[7][t1 & 0x7f]; + + de_keysr[15 - round] = + en_keysr[round] = comp_maskr[0][(t0 >> 21) & 0x7f] + | comp_maskr[1][(t0 >> 14) & 0x7f] + | comp_maskr[2][(t0 >> 7) & 0x7f] + | comp_maskr[3][t0 & 0x7f] + | comp_maskr[4][(t1 >> 21) & 0x7f] + | comp_maskr[5][(t1 >> 14) & 0x7f] + | comp_maskr[6][(t1 >> 7) & 0x7f] + | comp_maskr[7][t1 & 0x7f]; +#endif + } + return(0); +} + + +static int +do_des( my_u_int32_t l_in, my_u_int32_t r_in, my_u_int32_t *l_out, my_u_int32_t *r_out, int count) +{ + /* + * l_in, r_in, l_out, and r_out are in pseudo-"big-endian" format. + */ + my_u_int32_t l, r, *kl, *kr, *kl1, *kr1; + my_u_int32_t f, r48l, r48r; + int round; + + if (count == 0) { + return(1); + } else if (count > 0) { + /* + * Encrypting + */ + kl1 = en_keysl; + kr1 = en_keysr; + } else { + /* + * Decrypting + */ + count = -count; + kl1 = de_keysl; + kr1 = de_keysr; + } + + /* + * Do initial permutation (IP). + */ + +#ifdef LOWSPACE + setup_ip_maskl(); + l = common[0][l_in >> 24] + | common[1][(l_in >> 16) & 0xff] + | common[2][(l_in >> 8) & 0xff] + | common[3][l_in & 0xff] + | common[4][r_in >> 24] + | common[5][(r_in >> 16) & 0xff] + | common[6][(r_in >> 8) & 0xff] + | common[7][r_in & 0xff]; + setup_ip_maskr(); + r = common[0][l_in >> 24] + | common[1][(l_in >> 16) & 0xff] + | common[2][(l_in >> 8) & 0xff] + | common[3][l_in & 0xff] + | common[4][r_in >> 24] + | common[5][(r_in >> 16) & 0xff] + | common[6][(r_in >> 8) & 0xff] + | common[7][r_in & 0xff]; +#else + l = ip_maskl[0][l_in >> 24] + | ip_maskl[1][(l_in >> 16) & 0xff] + | ip_maskl[2][(l_in >> 8) & 0xff] + | ip_maskl[3][l_in & 0xff] + | ip_maskl[4][r_in >> 24] + | ip_maskl[5][(r_in >> 16) & 0xff] + | ip_maskl[6][(r_in >> 8) & 0xff] + | ip_maskl[7][r_in & 0xff]; + r = ip_maskr[0][l_in >> 24] + | ip_maskr[1][(l_in >> 16) & 0xff] + | ip_maskr[2][(l_in >> 8) & 0xff] + | ip_maskr[3][l_in & 0xff] + | ip_maskr[4][r_in >> 24] + | ip_maskr[5][(r_in >> 16) & 0xff] + | ip_maskr[6][(r_in >> 8) & 0xff] + | ip_maskr[7][r_in & 0xff]; +#endif + + while (count--) { + /* + * Do each round. + */ + kl = kl1; + kr = kr1; + round = 16; + while (round--) { + /* + * Expand R to 48 bits (simulate the E-box). + */ + r48l = ((r & 0x00000001) << 23) + | ((r & 0xf8000000) >> 9) + | ((r & 0x1f800000) >> 11) + | ((r & 0x01f80000) >> 13) + | ((r & 0x001f8000) >> 15); + + r48r = ((r & 0x0001f800) << 7) + | ((r & 0x00001f80) << 5) + | ((r & 0x000001f8) << 3) + | ((r & 0x0000001f) << 1) + | ((r & 0x80000000) >> 31); + /* + * Do salting for crypt() and friends, and + * XOR with the permuted key. + */ + f = (r48l ^ r48r) & saltbits; + r48l ^= f ^ *kl++; + r48r ^= f ^ *kr++; + /* + * Do sbox lookups (which shrink it back to 32 bits) + * and do the pbox permutation at the same time. + */ + +#ifdef LOWSPACE + setup_psbox(); + f = common[0][m_sbox[0][r48l >> 12]] + | common[1][m_sbox[1][r48l & 0xfff]] + | common[2][m_sbox[2][r48r >> 12]] + | common[3][m_sbox[3][r48r & 0xfff]]; +#else + f = psbox[0][m_sbox[0][r48l >> 12]] + | psbox[1][m_sbox[1][r48l & 0xfff]] + | psbox[2][m_sbox[2][r48r >> 12]] + | psbox[3][m_sbox[3][r48r & 0xfff]]; +#endif + /* + * Now that we've permuted things, complete f(). + */ + f ^= l; + l = r; + r = f; + } + r = l; + l = f; + } + /* + * Do final permutation (inverse of IP). + */ + +#ifdef LOWSPACE + setup_fp_maskl(); + *l_out = common[0][l >> 24] + | common[1][(l >> 16) & 0xff] + | common[2][(l >> 8) & 0xff] + | common[3][l & 0xff] + | common[4][r >> 24] + | common[5][(r >> 16) & 0xff] + | common[6][(r >> 8) & 0xff] + | common[7][r & 0xff]; + setup_fp_maskr(); + *r_out = common[0][l >> 24] + | common[1][(l >> 16) & 0xff] + | common[2][(l >> 8) & 0xff] + | common[3][l & 0xff] + | common[4][r >> 24] + | common[5][(r >> 16) & 0xff] + | common[6][(r >> 8) & 0xff] + | common[7][r & 0xff]; +#else + *l_out = fp_maskl[0][l >> 24] + | fp_maskl[1][(l >> 16) & 0xff] + | fp_maskl[2][(l >> 8) & 0xff] + | fp_maskl[3][l & 0xff] + | fp_maskl[4][r >> 24] + | fp_maskl[5][(r >> 16) & 0xff] + | fp_maskl[6][(r >> 8) & 0xff] + | fp_maskl[7][r & 0xff]; + *r_out = fp_maskr[0][l >> 24] + | fp_maskr[1][(l >> 16) & 0xff] + | fp_maskr[2][(l >> 8) & 0xff] + | fp_maskr[3][l & 0xff] + | fp_maskr[4][r >> 24] + | fp_maskr[5][(r >> 16) & 0xff] + | fp_maskr[6][(r >> 8) & 0xff] + | fp_maskr[7][r & 0xff]; +#endif + return(0); +} + + +#if 0 +static int +des_cipher(const char *in, char *out, my_u_int32_t salt, int count) +{ + my_u_int32_t l_out, r_out, rawl, rawr; + int retval; + union { + my_u_int32_t *ui32; + const char *c; + } trans; + + des_init(); + + setup_salt(salt); + + trans.c = in; + rawl = ntohl(*trans.ui32++); + rawr = ntohl(*trans.ui32); + + retval = do_des(rawl, rawr, &l_out, &r_out, count); + + trans.c = out; + *trans.ui32++ = htonl(l_out); + *trans.ui32 = htonl(r_out); + return(retval); +} +#endif + + +void +setkey(const char *key) +{ + int i, j; + my_u_int32_t packed_keys[2]; + my_u_char_t *p; + + p = (my_u_char_t *) packed_keys; + + for (i = 0; i < 8; i++) { + p[i] = 0; + for (j = 0; j < 8; j++) + if (*key++ & 1) + p[i] |= bits8[j]; + } + des_setkey(p); +} + + +void +encrypt(char *block, int flag) +{ + my_u_int32_t io[2]; + my_u_char_t *p; + int i, j; + + des_init(); + + setup_salt(0L); + p = block; + for (i = 0; i < 2; i++) { + io[i] = 0L; + for (j = 0; j < 32; j++) + if (*p++ & 1) + io[i] |= bits32[j]; + } + do_des(io[0], io[1], io, io + 1, flag ? -1 : 1); + for (i = 0; i < 2; i++) + for (j = 0; j < 32; j++) + block[(i << 5) | j] = (io[i] & bits32[j]) ? 1 : 0; +} + +char *crypt(const char *key, const char *setting) +{ + my_u_int32_t count, salt, l, r0, r1, keybuf[2]; + my_u_char_t *p, *q; + static char output[21]; + + des_init(); + + /* + * Copy the key, shifting each character up by one bit + * and padding with zeros. + */ + q = (my_u_char_t *)keybuf; + while (q - (my_u_char_t *)keybuf - 8) { + *q++ = *key << 1; + if (*(q - 1)) + key++; + } + if (des_setkey((char *)keybuf)) + return(NULL); + +#if 0 + if (*setting == _PASSWORD_EFMT1) { + int i; + /* + * "new"-style: + * setting - underscore, 4 bytes of count, 4 bytes of salt + * key - unlimited characters + */ + for (i = 1, count = 0L; i < 5; i++) + count |= ascii_to_bin(setting[i]) << ((i - 1) * 6); + + for (i = 5, salt = 0L; i < 9; i++) + salt |= ascii_to_bin(setting[i]) << ((i - 5) * 6); + + while (*key) { + /* + * Encrypt the key with itself. + */ + if (des_cipher((char *)keybuf, (char *)keybuf, 0L, 1)) + return(NULL); + /* + * And XOR with the next 8 characters of the key. + */ + q = (my_u_char_t *)keybuf; + while (q - (my_u_char_t *)keybuf - 8 && *key) + *q++ ^= *key++ << 1; + + if (des_setkey((char *)keybuf)) + return(NULL); + } + strncpy(output, setting, 9); + + /* + * Double check that we weren't given a short setting. + * If we were, the above code will probably have created + * wierd values for count and salt, but we don't really care. + * Just make sure the output string doesn't have an extra + * NUL in it. + */ + output[9] = '\0'; + p = (my_u_char_t *)output + strlen(output); + } else +#endif + { + /* + * "old"-style: + * setting - 2 bytes of salt + * key - up to 8 characters + */ + count = 25; + + salt = (ascii_to_bin(setting[1]) << 6) + | ascii_to_bin(setting[0]); + + output[0] = setting[0]; + /* + * If the encrypted password that the salt was extracted from + * is only 1 character long, the salt will be corrupted. We + * need to ensure that the output string doesn't have an extra + * NUL in it! + */ + output[1] = setting[1] ? setting[1] : output[0]; + + p = (my_u_char_t *)output + 2; + } + setup_salt(salt); + /* + * Do it. + */ + if (do_des(0L, 0L, &r0, &r1, (int)count)) + return(NULL); + /* + * Now encode the result... + */ + l = (r0 >> 8); + *p++ = ascii64[(l >> 18) & 0x3f]; + *p++ = ascii64[(l >> 12) & 0x3f]; + *p++ = ascii64[(l >> 6) & 0x3f]; + *p++ = ascii64[l & 0x3f]; + + l = (r0 << 16) | ((r1 >> 16) & 0xffff); + *p++ = ascii64[(l >> 18) & 0x3f]; + *p++ = ascii64[(l >> 12) & 0x3f]; + *p++ = ascii64[(l >> 6) & 0x3f]; + *p++ = ascii64[l & 0x3f]; + + l = r1 << 2; + *p++ = ascii64[(l >> 12) & 0x3f]; + *p++ = ascii64[(l >> 6) & 0x3f]; + *p++ = ascii64[l & 0x3f]; + *p = 0; + + return(output); +} diff --git a/com32/cmenu/libmenu/des.h b/com32/cmenu/libmenu/des.h new file mode 100644 index 0000000..67fc6b7 --- /dev/null +++ b/com32/cmenu/libmenu/des.h @@ -0,0 +1,8 @@ + +#ifndef _DES_H_ +#define _DES_H_ + +// des crypt +extern char *crypt (const char *key, const char *salt); + +#endif diff --git a/com32/cmenu/libmenu/des.o b/com32/cmenu/libmenu/des.o new file mode 100644 index 0000000000000000000000000000000000000000..9dde6c786eef12680e98b239650d972900df153f GIT binary patch literal 8236 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 literal 0 HcmV?d00001 diff --git a/com32/cmenu/libmenu/help.c b/com32/cmenu/libmenu/help.c new file mode 100644 index 0000000..31944c7 --- /dev/null +++ b/com32/cmenu/libmenu/help.c @@ -0,0 +1,200 @@ +/* -*- c -*- ------------------------------------------------------------- * + * + * Copyright 2004-2005 Murali Krishnan Ganapathy - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#include "help.h" +#include +#include "string.h" +#include "com32io.h" +#include // to read entire file into memory + +char helpbasedir[HELPDIRLEN]; // name of help directory limited to HELPDIRLEN + +// Find the occurence of the count'th \n in buffer (or NULL) if not found +char * findline(char*buffer,int count) +{ + int ctr; + char *p= buffer-1; + + if (count < 1) return buffer; + for (ctr=0; ctr < count; ctr++) { + p = strchr(p+1,'\n'); + if (p==NULL) return NULL; + } + return p; +} + +// return the number of lines in buffer +int countlines(char*buffer) +{ + int ans; + const char *p; + + p = buffer-1; + ans = 1; + while(p) {p = strchr(p+1,'\n'); ans++; } + return ans; +} + + +// Print numlines of text starting from buf +void printtext(char*buf, int from) +{ + char *p,*f; + char right,bot,nlines; + + // clear window to print + right = getnumcols() - HELP_RIGHT_MARGIN; + bot = getnumrows() - HELP_BOTTOM_MARGIN; + nlines = bot-HELP_BODY_ROW+1; + scrollupwindow(HELP_BODY_ROW,HELP_LEFT_MARGIN,bot,right,0x07,nlines); + + f = findline(buf,from); + if (!f) return; // nothing to print + if (*f=='\n') f++; // start of from+1st line + p = findline(f,nlines); + if (p && (*p=='\n')) *p = '\0'; // change to NUL + gotoxy(HELP_BODY_ROW,HELP_LEFT_MARGIN,HELPPAGE); + cswprint(f,0x07,HELP_LEFT_MARGIN); + if (p) *p = '\n'; // set it back +} + +void showhelp(const char *filename) +{ + char nc,nr,ph; + char *title,*text; + union { char *buffer; void *vbuf; } buf; // This is to avoild type-punning issues + + char line[512]; + size_t size; + char scan; + int rv,numlines,curr_line; + + nc = getnumcols(); + nr = getnumrows(); + ph = nr - HELP_BOTTOM_MARGIN - HELP_BODY_ROW - 1; + cls(); + drawbox(0,0,nr,nc-1,HELPPAGE,0x07,HELPBOX); + + drawhorizline(2,0,nc-1,HELPPAGE,0x07,HELPBOX,0); // dumb==0 + if (filename == NULL) { // print file contents + gotoxy(HELP_BODY_ROW,HELP_LEFT_MARGIN,HELPPAGE); + cswprint("Filename not given",0x07,HELP_LEFT_MARGIN); + while (1) { + inputc(&scan); + if (scan == ESCAPE) break; + } + cls(); + return; + } + + rv = loadfile(filename,(void **)&buf.vbuf, &size); // load entire file into memory + if (rv < 0) { // Error reading file or no such file + sprintf(line, "Error reading file or file not found\n file=%s",filename); + gotoxy(HELP_BODY_ROW,HELP_LEFT_MARGIN,HELPPAGE); + cswprint(line,0x07,HELP_LEFT_MARGIN); + while (1) { + inputc(&scan); + if (scan == ESCAPE) break; + } + cls(); + return; + } + + title = buf.buffer; + text = findline(title,1); // end of first line + *text++='\0'; // end the title string and increment text + + // Now we have a file just print it. + gotoxy(1,(nc-strlen(title))/2,HELPPAGE); + csprint(title,0x07); + numlines = countlines(text); + curr_line = 0; + scan = ESCAPE+1; // anything except ESCAPE + + while(scan != ESCAPE) { + printtext(text,curr_line); + gotoxy(HELP_BODY_ROW-1,nc-HELP_RIGHT_MARGIN,HELPPAGE); + if (curr_line > 0) + putch(HELP_MORE_ABOVE,0x07,HELPPAGE); + else putch(' ',0x07,HELPPAGE); + gotoxy(nr-HELP_BOTTOM_MARGIN+1,nc-HELP_RIGHT_MARGIN,HELPPAGE); + if (curr_line < numlines - ph) + putch(HELP_MORE_BELOW,0x07,HELPPAGE); + else putch(' ',0x07,HELPPAGE); + + inputc(&scan); // wait for user keypress + + switch(scan) { + case HOMEKEY: + curr_line = 0; + break; + case ENDKEY: + curr_line = numlines; + break; + case UPARROW: + curr_line--; + break; + case DNARROW: + curr_line++; + break; + case PAGEUP: + curr_line -= ph; + break; + case PAGEDN: + curr_line += ph; + break; + default: + break; + } + if (curr_line > numlines - ph) curr_line = numlines-ph; + if (curr_line < 0) curr_line = 0; + } + cls(); + return; +} + +void runhelp(const char *filename) +{ + char dp; + char fullname[HELPDIRLEN+16]; + + dp = getdisppage(); + if (dp != HELPPAGE) setdisppage(HELPPAGE); + cursoroff(); + if (helpbasedir[0] != 0) { + strcpy(fullname,helpbasedir); + strcat(fullname,"/"); + strcat(fullname,filename); + showhelp(fullname); + } + else showhelp (filename); // Assume filename is absolute + if (dp != HELPPAGE) setdisppage(dp); +} + +void runhelpsystem(unsigned int helpid) +{ + char filename[15]; + + sprintf(filename,"hlp%5d.txt",helpid); + runhelp(filename); +} + +void init_help(const char *helpdir) +{ + if (helpdir != NULL) + strcpy(helpbasedir,helpdir); + else helpbasedir[0] = 0; +} + +void close_help(void) +{ +} diff --git a/com32/cmenu/libmenu/help.h b/com32/cmenu/libmenu/help.h new file mode 100644 index 0000000..06832d8 --- /dev/null +++ b/com32/cmenu/libmenu/help.h @@ -0,0 +1,49 @@ +/* -*- c -*- ------------------------------------------------------------- * + * + * Copyright 2004-2005 Murali Krishnan Ganapathy - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef __HELP_H_ +#define __HELP_H_ + +#include "menu.h" +#include "com32io.h" +#include "tui.h" +#include + +// How many rows for the title +#define HELP_TITLE_HEIGHT 1 +#define HELP_BODY_ROW (HELP_TITLE_HEIGHT+3) +#define HELP_LEFT_MARGIN 2 +#define HELP_RIGHT_MARGIN 2 // Assume all lines dont cross this +#define HELP_BOTTOM_MARGIN 2 // Number of lines not use from bottom of screen + +#define HELPBOX BOX_SINSIN +#define HELPDIRLEN 64 +#define HELPPAGE 2 + +#define HELP_MORE_ABOVE 24 // to print when more is available above +#define HELP_MORE_BELOW 25 // same as above but for below + +// Display one screen of help information +void showhelp(const char *filename); + +// Start the help system using id helpid +void runhelpsystem(unsigned int helpid); + +// Start help system with specified file +void runhelp(const char *filename); + +// Directory where help files are located +void init_help(const char *helpdir); +// Free internal datastructures +void close_help(void); + +#endif diff --git a/com32/cmenu/libmenu/help.o b/com32/cmenu/libmenu/help.o new file mode 100644 index 0000000000000000000000000000000000000000..71c7ab50ea3bd54543ba4811d9f0a0047bc1f394 GIT binary patch literal 3256 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 literal 0 HcmV?d00001 diff --git a/com32/cmenu/libmenu/menu.c b/com32/cmenu/libmenu/menu.c new file mode 100644 index 0000000..56a7426 --- /dev/null +++ b/com32/cmenu/libmenu/menu.c @@ -0,0 +1,1300 @@ +/* -*- c -*- ------------------------------------------------------------- * + * + * Copyright 2004-2005 Murali Krishnan Ganapathy - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#include "menu.h" +#include "com32io.h" +#include + +// Local Variables +static pt_menusystem ms; // Pointer to the menusystem +char TITLESTR[] = "COMBOOT Menu System for SYSLINUX developed by Murali Krishnan Ganapathy"; +char TITLELONG[] = " TITLE too long "; +char ITEMLONG[] = " ITEM too long "; +char ACTIONLONG[] = " ACTION too long "; +char STATUSLONG[] = " STATUS too long "; +char EMPTYSTR[] = ""; + +/* Forward declarations */ +int calc_visible(pt_menu menu,int first); +int next_visible(pt_menu menu,int index); +int prev_visible(pt_menu menu,int index); +int next_visible_sep(pt_menu menu,int index); +int prev_visible_sep(pt_menu menu,int index); +int calc_first_early(pt_menu menu,int curr); +int calc_first_late(pt_menu menu,int curr); +int isvisible(pt_menu menu,int first, int curr); + + +/* Basic Menu routines */ + +// This is same as inputc except it honors the ontimeout handler +// and calls it when needed. For the callee, there is no difference +// as this will not return unless a key has been pressed. +char getch(char *scan) +{ + unsigned long i; + TIMEOUTCODE c; + t_timeout_handler th; + + // Wait until keypress if no handler specified + if ((ms->ontimeout==NULL) && (ms->ontotaltimeout==NULL)) return inputc(scan); + + th = ms->ontimeout; + while (1) // Forever do + { + for (i=0; i < ms->tm_numsteps; i++) + { + if (checkkbdbuf()) return inputc(scan); + sleep(ms->tm_stepsize); + if ( (ms->tm_total_timeout == 0) || (ms->ontotaltimeout==NULL)) + continue; // Dont bother with calculations if no handler + ms->tm_sofar_timeout += ms->tm_stepsize; + if (ms->tm_sofar_timeout >= ms->tm_total_timeout) { + th = ms->ontotaltimeout; + ms->tm_sofar_timeout = 0; + break; // Get out of the for loop + } + } + if (!th) continue; // no handler dont call + c = th(); + switch(c) + { + case CODE_ENTER: // Pretend user hit enter + *scan = ENTERA; + return '\015'; // \015 octal = 13 + case CODE_ESCAPE: // Pretend user hit escape + *scan = ESCAPE; + return '\033'; // \033 octal = 27 + default: + break; + } + } + return 0; +} + +/* Print a menu item */ +/* attr[0] is non-hilite attr, attr[1] is highlight attr */ +void printmenuitem(const char *str,uchar* attr) +{ + uchar page = getdisppage(); + uchar row,col; + int hlite=NOHLITE; // Initially no highlighting + + getpos(&row,&col,page); + while ( *str ) { + switch (*str) + { + case '\b': + --col; + break; + case '\n': + ++row; + break; + case '\r': + col=0; + break; + case BELL: // No Bell Char + break; + case ENABLEHLITE: // Switch on highlighting + hlite = HLITE; + break; + case DISABLEHLITE: // Turn off highlighting + hlite = NOHLITE; + break; + default: + putch(*str, attr[hlite], page); + ++col; + } + if (col > getnumcols()) + { + ++row; + col=0; + } + if (row > getnumrows()) + { + scrollup(); + row= getnumrows(); + } + gotoxy(row,col,page); + str++; + } +} + +int find_shortcut(pt_menu menu,uchar shortcut, int index) +// Find the next index with specified shortcut key +{ + int ans; + pt_menuitem mi; + + // Garbage in garbage out + if ((index <0) || (index >= menu->numitems)) return index; + ans = index+1; + // Go till end of menu + while (ans < menu->numitems) + { + mi = menu->items[ans]; + if ((mi->action == OPT_INVISIBLE) || (mi->action == OPT_SEP) + || (mi->shortcut != shortcut)) + ans ++; + else return ans; + } + // Start at the beginning and try again + ans = 0; + while (ans < index) + { + mi = menu->items[ans]; + if ((mi->action == OPT_INVISIBLE) || (mi->action == OPT_SEP) + || (mi->shortcut != shortcut)) + ans ++; + else return ans; + } + return index; // Sorry not found +} + +// print the menu starting from FIRST +// will print a maximum of menu->menuheight items +void printmenu(pt_menu menu, int curr, uchar top, uchar left, uchar first) +{ + int x,row; // x = index, row = position from top + int numitems,menuwidth; + char fchar[5],lchar[5]; // The first and last char in for each entry + const char *str; // and inbetween the item or a seperator is printed + uchar *attr; // attribute attr + char sep[MENULEN];// and inbetween the item or a seperator is printed + pt_menuitem ci; + + numitems = calc_visible(menu,first); + if (numitems > menu->menuheight) numitems = menu->menuheight; + + menuwidth = menu->menuwidth+3; + clearwindow(top,left-2, top+numitems+1, left+menuwidth+1, + ms->menupage, ms->fillchar, ms->shadowattr); + drawbox(top-1,left-3,top+numitems,left+menuwidth, + ms->menupage,ms->normalattr[NOHLITE],ms->menubt); + memset(sep,ms->box_horiz,menuwidth); // String containing the seperator string + sep[menuwidth-1] = 0; + // Menu title + x = (menuwidth - strlen(menu->title) - 1) >> 1; + gotoxy(top-1,left+x,ms->menupage); + printmenuitem(menu->title,ms->normalattr); + row = -1; // 1 less than inital value of x + for (x=first; x < menu->numitems; x++) + { + ci = menu->items[x]; + if (ci->action == OPT_INVISIBLE) continue; + row++; + if (row >= numitems) break; // Already have enough number of items + // Setup the defaults now + lchar[0] = fchar[0] = ' '; + lchar[1] = fchar[1] = '\0'; // fchar and lchar are just spaces + str = ci->item; // Pointer to item string + attr = (x==curr ? ms->reverseattr : ms->normalattr); // Normal attributes + switch (ci->action) // set up attr,str,fchar,lchar for everything + { + case OPT_INACTIVE: + attr = (x==curr? ms->revinactattr : ms->inactattr); + break; + case OPT_SUBMENU: + lchar[0] = SUBMENUCHAR; lchar[1] = 0; + break; + case OPT_RADIOMENU: + lchar[0] = RADIOMENUCHAR; lchar[1] = 0; + break; + case OPT_CHECKBOX: + lchar[0] = (ci->itemdata.checked ? CHECKED : UNCHECKED); + lchar[1] = 0; + break; + case OPT_SEP: + fchar[0] = '\b'; fchar[1] = ms->box_ltrt; fchar[2] = ms->box_horiz; fchar[3] = ms->box_horiz; fchar[4] = 0; + lchar[0] = ms->box_horiz; lchar[1] = ms->box_rtlt; lchar[2] = 0; + str = sep; + break; + case OPT_EXITMENU: + fchar[0] = EXITMENUCHAR; fchar[1] = 0; + break; + default: // Just to keep the compiler happy + break; + } + gotoxy(top+row,left-2,ms->menupage); + cprint(ms->spacechar,attr[NOHLITE],menuwidth+2,ms->menupage); // Wipe area with spaces + gotoxy(top+row,left-2,ms->menupage); + csprint(fchar,attr[NOHLITE]); // Print first part + gotoxy(top+row,left,ms->menupage); + printmenuitem(str,attr); // Print main part + gotoxy(top+row,left+menuwidth-1,ms->menupage); // Last char if any + csprint(lchar,attr[NOHLITE]); // Print last part + } + // Check if we need to MOREABOVE and MOREBELOW to be added + // reuse x + row = 0; + x = next_visible_sep(menu,0); // First item + if (! isvisible(menu,first,x)) // There is more above + { + row = 1; + gotoxy(top,left+menuwidth,ms->menupage); + cprint(MOREABOVE,ms->normalattr[NOHLITE],1,ms->menupage); + } + x = prev_visible_sep(menu,menu->numitems); // last item + if (! isvisible(menu,first,x)) // There is more above + { + row = 1; + gotoxy(top+numitems-1,left+menuwidth,ms->menupage); + cprint(MOREBELOW,ms->normalattr[NOHLITE],1,ms->menupage); + } + // Add a scroll box + x = ((numitems-1)*curr)/(menu->numitems); + if ((x>0) && (row==1)) { + gotoxy(top+x,left+menuwidth,ms->menupage); + cprint(SCROLLBOX,ms->normalattr[NOHLITE],1,ms->menupage); + } + if (ms->handler) ms->handler(ms,menu->items[curr]); +} + +// Difference between this and regular menu, is that only +// OPT_INVISIBLE, OPT_SEP are honoured +void printradiomenu(pt_menu menu, int curr, uchar top, uchar left, int first) +{ + int x,row; // x = index, row = position from top + int numitems,menuwidth; + char fchar[5],lchar[5]; // The first and last char in for each entry + const char *str; // and inbetween the item or a seperator is printed + uchar *attr; // all in the attribute attr + char sep[MENULEN];// and inbetween the item or a seperator is printed + pt_menuitem ci; + + numitems = calc_visible(menu,first); + if (numitems > menu->menuheight) numitems = menu->menuheight; + + menuwidth = menu->menuwidth+3; + clearwindow(top,left-2, top+numitems+1, left+menuwidth+1, + ms->menupage, ms->fillchar, ms->shadowattr); + drawbox(top-1,left-3,top+numitems,left+menuwidth, + ms->menupage,ms->normalattr[NOHLITE],ms->menubt); + memset(sep,ms->box_horiz,menuwidth); // String containing the seperator string + sep[menuwidth-1] = 0; + // Menu title + x = (menuwidth - strlen(menu->title) - 1) >> 1; + gotoxy(top-1,left+x,ms->menupage); + printmenuitem(menu->title,ms->normalattr); + row = -1; // 1 less than inital value of x + for (x=first; x < menu->numitems; x++) + { + ci = menu->items[x]; + if (ci->action == OPT_INVISIBLE) continue; + row++; + if (row > numitems) break; + // Setup the defaults now + fchar[0] = RADIOUNSEL; fchar[1]='\0'; // Unselected ( ) + lchar[0] = '\0'; // Nothing special after + str = ci->item; // Pointer to item string + attr = ms->normalattr; // Always same attribute + fchar[0] = (x==curr ? RADIOSEL : RADIOUNSEL); + switch (ci->action) // set up attr,str,fchar,lchar for everything + { + case OPT_INACTIVE: + attr = ms->inactattr; + break; + case OPT_SEP: + fchar[0] = '\b'; fchar[1] = ms->box_ltrt; fchar[2] = ms->box_horiz; fchar[3] = ms->box_horiz; fchar[4] = 0; + lchar[0] = ms->box_horiz; lchar[1] = ms->box_rtlt; lchar[3] = 0; + str = sep; + break; + default: // To keep the compiler happy + break; + } + gotoxy(top+row,left-2,ms->menupage); + cprint(ms->spacechar,attr[NOHLITE],menuwidth+2,ms->menupage); // Wipe area with spaces + gotoxy(top+row,left-2,ms->menupage); + csprint(fchar,attr[NOHLITE]); // Print first part + gotoxy(top+row,left,ms->menupage); + printmenuitem(str,attr); // Print main part + gotoxy(top+row,left+menuwidth-1,ms->menupage); // Last char if any + csprint(lchar,attr[NOHLITE]); // Print last part + } + // Check if we need to MOREABOVE and MOREBELOW to be added + // reuse x + row = 0; + x = next_visible_sep(menu,0); // First item + if (! isvisible(menu,first,x)) // There is more above + { + row = 1; + gotoxy(top,left+menuwidth,ms->menupage); + cprint(MOREABOVE,ms->normalattr[NOHLITE],1,ms->menupage); + } + x = prev_visible_sep(menu,menu->numitems); // last item + if (! isvisible(menu,first,x)) // There is more above + { + row = 1; + gotoxy(top+numitems-1,left+menuwidth,ms->menupage); + cprint(MOREBELOW,ms->normalattr[NOHLITE],1,ms->menupage); + } + // Add a scroll box + x = ((numitems-1)*curr)/(menu->numitems); + if ((x > 0) && (row == 1)) + { + gotoxy(top+x,left+menuwidth,ms->menupage); + cprint(SCROLLBOX,ms->normalattr[NOHLITE],1,ms->menupage); + } + if (ms->handler) ms->handler(ms,menu->items[curr]); +} + +void cleanupmenu(pt_menu menu, uchar top,uchar left,int numitems) +{ + if (numitems > menu->menuheight) numitems = menu->menuheight; + clearwindow(top,left-2, top+numitems+1, left+menu->menuwidth+4, + ms->menupage, ms->fillchar, ms->fillattr); // Clear the shadow + clearwindow(top-1, left-3, top+numitems, left+menu->menuwidth+3, + ms->menupage, ms->fillchar, ms->fillattr); // main window +} + +/* Handle a radio menu */ +pt_menuitem getradiooption(pt_menu menu, uchar top, uchar left, uchar startopt) + // Return item chosen or NULL if ESC was hit. +{ + int curr,i,first,tmp; + uchar asc,scan; + uchar numitems; + pt_menuitem ci; // Current item + + numitems = calc_visible(menu,0); + // Setup status line + gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); + cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage); + + // Initialise current menu item + curr = next_visible(menu,startopt); + + gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); + cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,1); + gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); + printmenuitem(menu->items[curr]->status,ms->statusattr); + first = calc_first_early(menu,curr); + while (1) // Forever + { + printradiomenu(menu,curr,top,left,first); + ci = menu->items[curr]; + + asc = getch(&scan); + switch (scan) + { + case HOMEKEY: + curr = next_visible(menu,0); + first = calc_first_early(menu,curr); + break; + case ENDKEY: + curr = prev_visible(menu,numitems-1); + first = calc_first_late(menu,curr); + break; + case PAGEDN: + for (i=0; i < 5; i++) curr = next_visible(menu,curr+1); + first = calc_first_late(menu,curr); + break; + case PAGEUP: + for (i=0; i < 5; i++) curr = prev_visible(menu,curr-1); + first = calc_first_early(menu,curr); + break; + case UPARROW: + curr = prev_visible(menu,curr-1); + if (curr < first) first = calc_first_early(menu,curr); + break; + case DNARROW: + curr = next_visible(menu,curr+1); + if (! isvisible(menu,first,curr)) + first = calc_first_late(menu,curr); + break; + case LTARROW: + case ESCAPE: + return NULL; + break; + case RTARROW: + case ENTERA: + case ENTERB: + if (ci->action == OPT_INACTIVE) break; + if (ci->action == OPT_SEP) break; + return ci; + break; + default: + // Check if this is a shortcut key + if (((asc >= 'A') && (asc <= 'Z')) || + ((asc >= 'a') && (asc <= 'z')) || + ((asc >= '0') && (asc <= '9'))) + { + tmp = find_shortcut(menu,asc,curr); + if ((tmp > curr) && (! isvisible(menu,first,tmp))) + first = calc_first_late(menu,tmp); + if (tmp < curr) + first = calc_first_early(menu,tmp); + curr = tmp; + } + else { + if (ms->keys_handler) // Call extra keys handler + ms->keys_handler(ms,menu->items[curr],(scan << 8) | asc); + } + break; + } + // Update status line + gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); + cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage); + printmenuitem(menu->items[curr]->status,ms->statusattr); + } + return NULL; // Should never come here +} + +/* Handle one menu */ +pt_menuitem getmenuoption(pt_menu menu, uchar top, uchar left, uchar startopt) + // Return item chosen or NULL if ESC was hit. +{ + int curr,i,first,tmp; + uchar asc,scan; + uchar numitems; + pt_menuitem ci; // Current item + t_handler_return hr; // Return value of handler + + numitems = calc_visible(menu,0); + // Setup status line + gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); + cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage); + + // Initialise current menu item + curr = next_visible(menu,startopt); + + gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); + cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,1); + gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); + printmenuitem(menu->items[curr]->status,ms->statusattr); + first = calc_first_early(menu,curr); + while (1) // Forever + { + printmenu(menu,curr,top,left,first); + ci = menu->items[curr]; + asc = getch(&scan); + switch (scan) + { + case HOMEKEY: + curr = next_visible(menu,0); + first = calc_first_early(menu,curr); + break; + case ENDKEY: + curr = prev_visible(menu,numitems-1); + first = calc_first_late(menu,curr); + break; + case PAGEDN: + for (i=0; i < 5; i++) curr = next_visible(menu,curr+1); + first = calc_first_late(menu,curr); + break; + case PAGEUP: + for (i=0; i < 5; i++) curr = prev_visible(menu,curr-1); + first = calc_first_early(menu,curr); + break; + case UPARROW: + curr = prev_visible(menu,curr-1); + if (curr < first) first = calc_first_early(menu,curr); + break; + case DNARROW: + curr = next_visible(menu,curr+1); + if (! isvisible(menu,first,curr)) + first = calc_first_late(menu,curr); + break; + case LTARROW: + case ESCAPE: + return NULL; + break; + case RTARROW: + case ENTERA: + case ENTERB: + if (ci->action == OPT_INACTIVE) break; + if (ci->action == OPT_CHECKBOX) break; + if (ci->action == OPT_SEP) break; + if (ci->action == OPT_EXITMENU) return NULL; // As if we hit Esc + // If we are going into a radio menu, dont call handler, return ci + if (ci->action == OPT_RADIOMENU) return ci; + if (ci->handler != NULL) // Do we have a handler + { + hr = ci->handler(ms,ci); + if (hr.refresh) // Do we need to refresh + { + // Cleanup menu using old number of items + cleanupmenu(menu,top,left,numitems); + // Recalculate the number of items + numitems = calc_visible(menu,0); + // Reprint the menu + printmenu(menu,curr,top,left,first); + } + if (hr.valid) return ci; + } + else return ci; + break; + case SPACEKEY: + if (ci->action != OPT_CHECKBOX) break; + ci->itemdata.checked = !ci->itemdata.checked; + if (ci->handler != NULL) // Do we have a handler + { + hr = ci->handler(ms,ci); + if (hr.refresh) // Do we need to refresh + { + // Cleanup menu using old number of items + cleanupmenu(menu,top,left,numitems); + // Recalculate the number of items + numitems = calc_visible(menu,0); + // Reprint the menu + printmenu(menu,curr,top,left,first); + } + } + break; + default: + // Check if this is a shortcut key + if (((asc >= 'A') && (asc <= 'Z')) || + ((asc >= 'a') && (asc <= 'z')) || + ((asc >= '0') && (asc <= '9'))) + { + tmp = find_shortcut(menu,asc,curr); + if ((tmp > curr) && (! isvisible(menu,first,tmp))) + first = calc_first_late(menu,tmp); + if (tmp < curr) + first = calc_first_early(menu,tmp); + curr = tmp; + } + else { + if (ms->keys_handler) // Call extra keys handler + ms->keys_handler(ms,menu->items[curr],(scan << 8) | asc); + } + break; + } + // Update status line + gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage); + cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage); + printmenuitem(menu->items[curr]->status,ms->statusattr); + } + return NULL; // Should never come here +} + +/* Handle the entire system of menu's. */ +pt_menuitem runmenusystem(uchar top, uchar left, pt_menu cmenu, uchar startopt, uchar menutype) + /* + * cmenu + * Which menu should be currently displayed + * top,left + * What is the position of the top,left corner of the menu + * startopt + * which menu item do I start with + * menutype + * NORMALMENU or RADIOMENU + * + * Return Value: + * Returns a pointer to the final item chosen, or NULL if nothing chosen. + */ +{ + pt_menuitem opt,choice; + uchar startat,mt; + uchar row,col; + + if (cmenu == NULL) return NULL; + startover: + // Set the menu height + cmenu->menuheight = ms->maxrow - top-3; + if (cmenu->menuheight > ms->maxmenuheight) + cmenu->menuheight = ms->maxmenuheight; + if (menutype == NORMALMENU) + opt = getmenuoption(cmenu,top,left,startopt); + else // menutype == RADIOMENU + opt = getradiooption(cmenu,top,left,startopt); + + if (opt == NULL) + { + // User hit Esc + cleanupmenu(cmenu,top,left,calc_visible(cmenu,0)); + return NULL; + } + // Are we done with the menu system? + if ((opt->action != OPT_SUBMENU) && (opt->action != OPT_RADIOMENU)) + { + cleanupmenu(cmenu,top,left,calc_visible(cmenu,0)); + return opt; // parent cleanup other menus + } + // Either radiomenu or submenu + // Do we have a valid menu number? The next hack uses the fact that + // itemdata.submenunum = itemdata.radiomenunum (since enum data type) + if (opt->itemdata.submenunum >= ms->nummenus) // This is Bad.... + { + gotoxy(12,12,ms->menupage); // Middle of screen + csprint("ERROR: Invalid submenu requested.",0x07); + cleanupmenu(cmenu,top,left,calc_visible(cmenu,0)); + return NULL; // Pretend user hit esc + } + // Call recursively for submenu + // Position the submenu below the current item, + // covering half the current window (horizontally) + row = ms->menus[(unsigned int)opt->itemdata.submenunum]->row; + col = ms->menus[(unsigned int)opt->itemdata.submenunum]->col; + if (row == 0xFF) row = top+opt->index+2; + if (col == 0xFF) col = left+3+(cmenu->menuwidth >> 1); + mt = (opt->action == OPT_SUBMENU ? NORMALMENU : RADIOMENU ); + startat = 0; + if ((opt->action == OPT_RADIOMENU) && (opt->data != NULL)) + startat = ((t_menuitem *)opt->data)->index; + + choice = runmenusystem(row, col, + ms->menus[(unsigned int)opt->itemdata.submenunum], + startat, mt ); + if (opt->action == OPT_RADIOMENU) + { + if (choice != NULL) opt->data = (void *)choice; // store choice in data field + if (opt->handler != NULL) opt->handler(ms,opt); + choice = NULL; // Pretend user hit esc + } + if (choice==NULL) // User hit Esc in submenu + { + // Startover + startopt = opt->index; + goto startover; + } + else + { + cleanupmenu(cmenu,top,left,calc_visible(cmenu,0)); + return choice; + } +} + +// Finds the indexof the menu with given name +uchar find_menu_num(const char *name) +{ + int i; + pt_menu m; + + if (name == NULL) return (uchar)(-1); + for (i=0; i < ms->nummenus; i++) + { + m = ms->menus[i]; + if ((m->name) && (strcmp(m->name,name)==0)) return i; + } + return (uchar)(-1); +} + +// Run through all items and if they are submenus +// with a non-trivial "action" and trivial submenunum +// replace submenunum with the menu with name "action" +void fix_submenus() +{ + int i,j; + pt_menu m; + pt_menuitem mi; + + i = 0; + for (i=0; i < ms->nummenus; i++) + { + m = ms->menus[i]; + for (j=0; j < m->numitems; j++) + { + mi = m->items[j]; + // if item is a submenu and has non-empty non-trivial data string + if (mi->data && strlen(mi->data) > 0 && + ((mi->action == OPT_SUBMENU) || (mi->action == OPT_RADIOMENU)) ) { + mi->itemdata.submenunum = find_menu_num (mi->data); + } + } + } +} + +/* User Callable functions */ + +pt_menuitem showmenus(uchar startmenu) +{ + pt_menuitem rv; + uchar oldpage,tpos; + + fix_submenus(); // Fix submenu numbers incase nick names were used + + // Setup screen for menusystem + oldpage = getdisppage(); + setdisppage(ms->menupage); + cls(); + clearwindow(ms->minrow, ms->mincol, ms->maxrow, ms->maxcol, + ms->menupage, ms->fillchar, ms->fillattr); + tpos = (ms->numcols - strlen(ms->title) - 1) >> 1; // center it on line + gotoxy(ms->minrow,ms->mincol,ms->menupage); + cprint(ms->tfillchar,ms->titleattr,ms->numcols,ms->menupage); + gotoxy(ms->minrow,ms->mincol+tpos,ms->menupage); + csprint(ms->title,ms->titleattr); + + cursoroff(); // Doesn't seem to work? + + + // Go, main menu cannot be a radio menu + rv = runmenusystem(ms->minrow+MENUROW, ms->mincol+MENUCOL, + ms->menus[(unsigned int)startmenu], 0, NORMALMENU); + + // Hide the garbage we left on the screen + cursoron(); + if (oldpage == ms->menupage) cls(); else setdisppage(oldpage); + + // Return user choice + return rv; +} + +pt_menusystem init_menusystem(const char *title) +{ + int i; + + ms = NULL; + ms = (pt_menusystem) malloc(sizeof(t_menusystem)); + if (ms == NULL) return NULL; + ms->nummenus = 0; + // Initialise all menu pointers + for (i=0; i < MAXMENUS; i++) ms->menus[i] = NULL; + + ms->title = (char *)malloc(TITLELEN+1); + if (title == NULL) + strcpy(ms->title,TITLESTR); // Copy string + else strcpy(ms->title,title); + + // Timeout settings + ms->tm_stepsize = TIMEOUTSTEPSIZE; + ms->tm_numsteps = TIMEOUTNUMSTEPS; + + ms->normalattr[NOHLITE] = NORMALATTR; + ms->normalattr[HLITE] = NORMALHLITE; + + ms->reverseattr[NOHLITE] = REVERSEATTR; + ms->reverseattr[HLITE] = REVERSEHLITE; + + ms->inactattr[NOHLITE] = INACTATTR; + ms->inactattr[HLITE] = INACTHLITE; + + ms->revinactattr[NOHLITE] = REVINACTATTR; + ms->revinactattr[HLITE] = REVINACTHLITE; + + ms->statusattr[NOHLITE] = STATUSATTR; + ms->statusattr[HLITE] = STATUSHLITE; + + ms->statline = STATLINE; + ms->tfillchar= TFILLCHAR; + ms->titleattr= TITLEATTR; + + ms->fillchar = FILLCHAR; + ms->fillattr = FILLATTR; + ms->spacechar= SPACECHAR; + ms->shadowattr = SHADOWATTR; + + ms->menupage = MENUPAGE; // Usually no need to change this at all + + // Initialise all handlers + ms->handler = NULL; + ms->keys_handler = NULL; + ms->ontimeout=NULL; // No timeout handler + ms->tm_total_timeout = 0; + ms->tm_sofar_timeout = 0; + ms->ontotaltimeout = NULL; + + // Setup ACTION_{,IN}VALID + ACTION_VALID.valid=1; + ACTION_VALID.refresh=0; + ACTION_INVALID.valid = 0; + ACTION_INVALID.refresh = 0; + + // Figure out the size of the screen we are in now. + // By default we use the whole screen for our menu + ms->minrow = ms->mincol = 0; + ms->numcols = getnumcols(); + ms->numrows = getnumrows(); + ms->maxcol = ms->numcols - 1; + ms->maxrow = ms->numrows - 1; + + // How many entries per menu can we display at a time + ms->maxmenuheight = ms->maxrow - ms->minrow - 3; + if (ms->maxmenuheight > MAXMENUHEIGHT) + ms->maxmenuheight= MAXMENUHEIGHT; + + // Set up the look of the box + set_box_type(MENUBOXTYPE); + return ms; +} + +void set_normal_attr(uchar normal, uchar selected, uchar inactivenormal, uchar inactiveselected) +{ + if (normal != 0xFF) ms->normalattr[0] = normal; + if (selected != 0xFF) ms->reverseattr[0] = selected; + if (inactivenormal != 0xFF) ms->inactattr[0] = inactivenormal; + if (inactiveselected != 0xFF) ms->revinactattr[0] = inactiveselected; +} + +void set_normal_hlite(uchar normal, uchar selected, uchar inactivenormal, uchar inactiveselected) +{ + if (normal != 0xFF) ms->normalattr[1] = normal; + if (selected != 0xFF) ms->reverseattr[1] = selected; + if (inactivenormal != 0xFF) ms->inactattr[1] = inactivenormal; + if (inactiveselected != 0xFF) ms->revinactattr[1] = inactiveselected; +} + +void set_status_info(uchar statusattr, uchar statushlite, uchar statline) +{ + if (statusattr != 0xFF) ms->statusattr[NOHLITE] = statusattr; + if (statushlite!= 0xFF) ms->statusattr[HLITE] = statushlite; + // statline is relative to minrow + if (statline >= ms->numrows) statline = ms->numrows - 1; + ms->statline = statline; // relative to ms->minrow, 0 based +} + +void set_title_info(uchar tfillchar, uchar titleattr) +{ + if (tfillchar != 0xFF) ms->tfillchar = tfillchar; + if (titleattr != 0xFF) ms->titleattr = titleattr; +} + +void set_misc_info(uchar fillchar, uchar fillattr,uchar spacechar, uchar shadowattr) +{ + if (fillchar != 0xFF) ms->fillchar = fillchar; + if (fillattr != 0xFF) ms->fillattr = fillattr; + if (spacechar != 0xFF) ms->spacechar = spacechar; + if (shadowattr!= 0xFF) ms->shadowattr= shadowattr; +} + +void set_box_type(boxtype bt) +{ + uchar *bxc; + ms->menubt = bt; + bxc = getboxchars(bt); + ms->box_horiz = bxc[BOX_HORIZ]; // The char used to draw top line + ms->box_ltrt = bxc[BOX_LTRT]; + ms->box_rtlt = bxc[BOX_RTLT]; +} + +void set_menu_options(uchar maxmenuheight) +{ + if (maxmenuheight != 0xFF) ms->maxmenuheight = maxmenuheight; +} + +// Set the window which menusystem should use +void set_window_size(uchar top, uchar left, uchar bot, uchar right) +{ + + uchar nr,nc; + if ((top > bot) || (left > right)) return; // Sorry no change will happen here + nr = getnumrows(); + nc = getnumcols(); + if (bot >= nr) bot = nr-1; + if (right >= nc) right = nc-1; + ms->minrow = top; + ms->mincol = left; + ms->maxrow = bot; + ms->maxcol = right; + ms->numcols = right - left + 1; + ms->numrows = bot - top + 1; + if (ms->statline >= ms->numrows) ms->statline = ms->numrows - 1; // Clip statline if need be +} + +void reg_handler( t_handler htype, void * handler) +{ + // If bad value set to default screen handler + switch(htype) { + case HDLR_KEYS: + ms->keys_handler = (t_keys_handler) handler; + break; + default: + ms->handler = (t_menusystem_handler) handler; + break; + } +} + +void unreg_handler(t_handler htype) +{ + switch(htype) { + case HDLR_KEYS: + ms->keys_handler = NULL; + break; + default: + ms->handler = NULL; + break; + } +} + +void reg_ontimeout(t_timeout_handler handler, unsigned int numsteps, unsigned int stepsize) +{ + ms->ontimeout = handler; + if (numsteps != 0) ms->tm_numsteps = numsteps; + if (stepsize != 0) ms->tm_stepsize = stepsize; +} + +void unreg_ontimeout() +{ + ms->ontimeout = NULL; +} + +void reg_ontotaltimeout (t_timeout_handler handler, unsigned long numcentiseconds) +{ + if (numcentiseconds != 0) { + ms->ontotaltimeout = handler; + ms->tm_total_timeout = numcentiseconds*10; // to convert to milliseconds + ms->tm_sofar_timeout = 0; + } +} + +void unreg_ontotaltimeout() +{ + ms->ontotaltimeout = NULL; +} + + +int next_visible(pt_menu menu, int index) +{ + int ans; + if (index < 0) ans = 0 ; + else if (index >= menu->numitems) ans = menu->numitems-1; + else ans = index; + while ((ans < menu->numitems-1) && + ((menu->items[ans]->action == OPT_INVISIBLE) || + (menu->items[ans]->action == OPT_SEP))) + ans++; + return ans; +} + +int prev_visible(pt_menu menu, int index) // Return index of prev visible +{ + int ans; + if (index < 0) ans = 0; + else if (index >= menu->numitems) ans = menu->numitems-1; + else ans = index; + while ((ans > 0) && + ((menu->items[ans]->action == OPT_INVISIBLE) || + (menu->items[ans]->action == OPT_SEP))) + ans--; + return ans; +} + +int next_visible_sep(pt_menu menu, int index) +{ + int ans; + if (index < 0) ans = 0 ; + else if (index >= menu->numitems) ans = menu->numitems-1; + else ans = index; + while ((ans < menu->numitems-1) && + (menu->items[ans]->action == OPT_INVISIBLE)) + ans++; + return ans; +} + +int prev_visible_sep(pt_menu menu, int index) // Return index of prev visible +{ + int ans; + if (index < 0) ans = 0; + else if (index >= menu->numitems) ans = menu->numitems-1; + else ans = index; + while ((ans > 0) && + (menu->items[ans]->action == OPT_INVISIBLE)) + ans--; + return ans; +} + +int calc_visible(pt_menu menu,int first) +{ + int ans,i; + + if (menu == NULL) return 0; + ans = 0; + for (i=first; i < menu->numitems; i++) + if (menu->items[i]->action != OPT_INVISIBLE) ans++; + return ans; +} + +// is curr visible if first entry is first? +int isvisible(pt_menu menu,int first, int curr) +{ + if (curr < first) return 0; + return (calc_visible(menu,first)-calc_visible(menu,curr) < menu->menuheight); +} + +// Calculate the first entry to be displayed +// so that curr is visible and make curr as late as possible +int calc_first_late(pt_menu menu,int curr) +{ + int ans,i,nv; + + nv = calc_visible(menu,0); + if (nv <= menu->menuheight) return 0; + // Start with curr and go back menu->menuheight times + ans = curr+1; + for (i=0; i < menu->menuheight; i++) + ans = prev_visible_sep(menu,ans-1); + return ans; +} + +// Calculate the first entry to be displayed +// so that curr is visible and make curr as early as possible +int calc_first_early(pt_menu menu,int curr) +{ + int ans,i,nv; + + nv = calc_visible(menu,0); + if (nv <= menu->menuheight) return 0; + // Start with curr and go back till >= menu->menuheight + // items are visible + nv = calc_visible(menu,curr); // Already nv of them are visible + ans = curr; + for (i=0; i < menu->menuheight - nv; i++) + ans = prev_visible_sep(menu,ans-1); + return ans; +} + +// Create a new menu and return its position +uchar add_menu(const char *title, int maxmenusize) +{ + int num,i; + pt_menu m; + + num = ms->nummenus; + if (num >= MAXMENUS) return -1; + m = NULL; + m = (pt_menu) malloc(sizeof(t_menu)); + if (m == NULL) return -1; + ms->menus[num] = m; + m->numitems = 0; + m->name = NULL; + m->row = 0xFF; + m->col = 0xFF; + if (maxmenusize < 1) + m->maxmenusize = MAXMENUSIZE; + else m->maxmenusize = maxmenusize; + m->items = (pt_menuitem *) malloc(sizeof(pt_menuitem)*(m->maxmenusize)); + for (i=0; i < m->maxmenusize; i++) m->items[i] = NULL; + + m->title = (char *)malloc(MENULEN+1); + if (title) + { + if (strlen(title) > MENULEN - 2) + strcpy(m->title,TITLELONG); + else strcpy(m->title,title); + } + else strcpy(m->title,EMPTYSTR); + m ->menuwidth = strlen(m->title); + ms->nummenus ++; + return ms->nummenus - 1; +} + +void set_menu_name(const char *name) // Set the "name" of this menu +{ + pt_menu m; + + m = ms->menus[ms->nummenus-1]; + if (m->name) // Free up previous name + { + free(m->name); + m -> name = NULL; + } + + if (name) + { + m->name = (char *)malloc(strlen(name)+1); + strcpy(m->name,name); + } +} + +// Create a new named menu and return its position +uchar add_named_menu(const char * name, const char *title, int maxmenusize) +{ + add_menu(title,maxmenusize); + set_menu_name(name); + return ms->nummenus - 1; +} + +void set_menu_pos(uchar row,uchar col) // Set the position of this menu. +{ + pt_menu m; + + m = ms->menus[ms->nummenus-1]; + m->row = row; + m->col = col; +} + +pt_menuitem add_sep() // Add a separator to current menu +{ + pt_menuitem mi; + pt_menu m; + + m = (ms->menus[ms->nummenus-1]); + mi = NULL; + mi = (pt_menuitem) malloc(sizeof(t_menuitem)); + if (mi == NULL) return NULL; + m->items[(unsigned int)m->numitems] = mi; + mi->handler = NULL; // No handler + mi->item = mi->status = mi->data = NULL; + mi->action = OPT_SEP; + mi->index = m->numitems++; + mi->parindex = ms->nummenus-1; + mi->shortcut = 0; + mi->helpid=0; + return mi; +} + +// Add item to the "current" menu +pt_menuitem add_item(const char *item, const char *status, t_action action, + const char *data, uchar itemdata) +{ + pt_menuitem mi; + pt_menu m; + const char *str; + uchar inhlite=0; // Are we inside hlite area + + m = (ms->menus[ms->nummenus-1]); + mi = NULL; + mi = (pt_menuitem) malloc(sizeof(t_menuitem)); + if (mi == NULL) return NULL; + m->items[(unsigned int) m->numitems] = mi; + mi->handler = NULL; // No handler + + // Allocate space to store stuff + mi->item = (char *)malloc(MENULEN+1); + mi->status = (char *)malloc(STATLEN+1); + mi->data = (char *)malloc(ACTIONLEN+1); + + if (item) { + if (strlen(item) > MENULEN) { + strcpy(mi->item,ITEMLONG); + } else { + strcpy(mi->item,item); + } + if (strlen(mi->item) > m->menuwidth) m->menuwidth = strlen(mi->item); + } else strcpy(mi->item,EMPTYSTR); + + if (status) { + if (strlen(status) > STATLEN) { + strcpy(mi->status,STATUSLONG); + } else { + strcpy(mi->status,status); + } + } else strcpy(mi->status,EMPTYSTR); + + mi->action=action; + str = mi->item; + mi->shortcut = 0; + mi->helpid = 0xFFFF; + inhlite = 0; // We have not yet seen an ENABLEHLITE char + // Find the first char in [A-Za-z0-9] after ENABLEHLITE and not arg to control char + while (*str) + { + if (*str == ENABLEHLITE) + { + inhlite=1; + } + if (*str == DISABLEHLITE) + { + inhlite = 0; + } + if ( (inhlite == 1) && + (((*str >= 'A') && (*str <= 'Z')) || + ((*str >= 'a') && (*str <= 'z')) || + ((*str >= '0') && (*str <= '9')))) + { + mi->shortcut=*str; + break; + } + ++str; + } + if ((mi->shortcut >= 'A') && (mi->shortcut <= 'Z')) // Make lower case + mi->shortcut = mi->shortcut -'A'+'a'; + + if (data) { + if (strlen(data) > ACTIONLEN) { + strcpy(mi->data,ACTIONLONG); + } else { + strcpy(mi->data,data); + } + } else strcpy(mi->data,EMPTYSTR); + + switch (action) + { + case OPT_SUBMENU: + mi->itemdata.submenunum = itemdata; + break; + case OPT_CHECKBOX: + mi->itemdata.checked = itemdata; + break; + case OPT_RADIOMENU: + mi->itemdata.radiomenunum = itemdata; + if (mi->data) free(mi->data); + mi->data = NULL; // No selection made + break; + default: // to keep the compiler happy + break; + } + mi->index = m->numitems++; + mi->parindex = ms->nummenus-1; + return mi; +} + +// Set the shortcut key for the current item +void set_item_options(uchar shortcut,int helpid) +{ + pt_menuitem mi; + pt_menu m; + + m = (ms->menus[ms->nummenus-1]); + if (m->numitems <= 0) return; + mi = m->items[(unsigned int) m->numitems-1]; + + if (shortcut != 0xFF) mi->shortcut = shortcut; + if (helpid != 0xFFFF) mi->helpid = helpid; +} + +// Free internal datasutructures +void close_menusystem(void) +{ +} + +// append_line_helper(pt_menu menu,char *line) +void append_line_helper(int menunum, char *line) +{ + pt_menu menu; + pt_menuitem mi,ri; + char *app; + int ctr; + char dp; + + + dp = getdisppage(); + menu = ms->menus[menunum]; + for (ctr = 0; ctr < (int) menu->numitems; ctr++) + { + mi = menu->items[ctr]; + app = NULL; //What to append + switch (mi->action) { + case OPT_CHECKBOX: + if (mi->itemdata.checked) app = mi->data; + break; + case OPT_RADIOMENU: + if (mi->data) { // Some selection has been made + ri = (pt_menuitem) (mi->data); + app = ri->data; + } + break; + case OPT_SUBMENU: + append_line_helper(mi->itemdata.submenunum,line); + break; + default: + break; + } + if (app) { + strcat(line," "); + strcat(line,app); + } + } +} + + +// Generate string based on state of checkboxes and radioitem in given menu +// Assume line points to large enough buffer +void gen_append_line(const char *menu_name,char *line) +{ + int menunum; + + menunum = find_menu_num(menu_name); + if (menunum < 0) return; // No such menu + append_line_helper(menunum,line); +} diff --git a/com32/cmenu/libmenu/menu.h b/com32/cmenu/libmenu/menu.h new file mode 100644 index 0000000..9b009db --- /dev/null +++ b/com32/cmenu/libmenu/menu.h @@ -0,0 +1,291 @@ +/* -*- c -*- ------------------------------------------------------------- * + * + * Copyright 2004-2005 Murali Krishnan Ganapathy - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* This program can be compiled for DOS with the OpenWatcom compiler + * (http://www.openwatcom.org/): + * + * wcl -3 -osx -mt .c + */ + +#ifndef __MENU_H__ +#define __MENU_H__ + +#include "com32io.h" +#include "tui.h" +#include "syslnx.h" +#include "scancodes.h" +#include + +// TIMEOUT PARAMETERS +/* If no key is pressed within TIMEOUTNUMSTEPS * TIMEOUTSTEPSIZE milliseconds + and if a timeout handler is registered, then that will be called. + The handler should either either take control from there on, or return without + producing any change in the current video settings. + + For e.g. the handler could + * Could just quit the menu program + * beep and return. + + TIMEOUTSTEPSIZE is the interval for which the program sleeps without checking for + any keystroke. So increasing this will make the response of the system slow. + Decreasing this will make a lot of interrupt calls using up your CPU. Default + value of TIMEOUTSTEPSIZE of 0.1 seconds should be right in most cases. + + TIMEOUTNUMSTEPS of 3000 corresponds to a wait time of 300 seconds or 5 minutes +*/ + +#define TIMEOUTSTEPSIZE 10 +#define TIMEOUTNUMSTEPS 30000L + +// Attributes +#define NORMALATTR 0x17 +#define NORMALHLITE 0x1F // Normal Highlight attribute +#define REVERSEATTR 0x70 +#define REVERSEHLITE 0x78 // Reverse Hightlight attribute +#define INACTATTR 0x18 +#define INACTHLITE 0x10 // Inactive Highlight attribute +#define REVINACTATTR 0x78 +#define REVINACTHLITE 0x70 // Reverse Inactive Highlight attr + +#define STATUSATTR 0x74 +#define STATUSHLITE 0x7B // Status highlight + +#define FILLCHAR 177 +#define FILLATTR 0x01 +#define SHADOWATTR 0x00 +#define SPACECHAR ' ' + +#define TFILLCHAR ' ' +#define TITLEATTR 0x70 + +#define ENABLEHLITE '<' // Char which turns on highlight +#define DISABLEHLITE '>' // Char which turns off highlight +#define NOHLITE 0 // The offset into attrib array for non-hilite +#define HLITE 1 // The offset for Hlite attrib + +#define MOREABOVE 24 // char to print when more menu items available above +#define MOREBELOW 25 // more items available below +#define SCROLLBOX 176 // Filled char to display + +// Attributes of the menu system +#define MAXMENUS 150 // Maximum number of menu's allowed +#define MAXMENUSIZE 60 // Default value for max num of entries in each menu +#define MAXMENUHEIGHT 20 // Maximum number of entries displayed +#define MENUBOXTYPE BOX_SINSIN // Default box type Look at tui.h for other values + +// Upper bounds on lengths +// We copy the given string, so user can reuse the space used to store incoming arguments. +#define MENULEN 78 // Each menu entry is atmost MENULEN chars +#define STATLEN 78 // Maximum length of status string +#define TITLELEN 78 // Maximum length of title string +#define ACTIONLEN 255 // Maximum length of an action string + +// Layout of menu +#define MENUROW 3 // Row where menu is displayed (relative to window) +#define MENUCOL 4 // Col where menu is displayed (relative to window) +#define MENUPAGE 1 // show in display page 1 +#define STATLINE 24 // Line number where status line starts (relative to window) + +// Used for printing debugging messages +#define DEBUGLINE 23 // debugging info goes here + +// Other Chars +#define SUBMENUCHAR 175 // This is >> symbol +#define RADIOMENUCHAR '>' // > symbol for radio menu? +#define EXITMENUCHAR 174 // This is << symbol +#define CHECKED 251 // Check mark +#define UNCHECKED 250 // Light bullet +#define RADIOSEL '.' // Current Radio Selection +#define RADIOUNSEL ' ' // Radio option not selected + +typedef unsigned char uchar; + +// Types of menu's +#define NORMALMENU 1 +#define RADIOMENU 2 + +typedef enum {OPT_INACTIVE, OPT_SUBMENU, OPT_RUN, OPT_EXITMENU, OPT_CHECKBOX, + OPT_RADIOMENU, OPT_SEP, OPT_INVISIBLE, + OPT_RADIOITEM} t_action; + +typedef union { + uchar submenunum; // For submenu's + uchar checked; // For check boxes + uchar radiomenunum; // Item mapping to a radio menu +} t_itemdata; + +struct s_menuitem; +struct s_menu; +struct s_menusystem; + +typedef struct { + unsigned int valid :1; // Is action valid? + unsigned int refresh:1; // Should we recompute menu stuff? + unsigned int reserved:6; // For future expansion +} t_handler_return; + +t_handler_return ACTION_VALID,ACTION_INVALID; // Specific values + +typedef t_handler_return (*t_item_handler)(struct s_menusystem *, struct s_menuitem *); +typedef void (*t_menusystem_handler)(struct s_menusystem *, struct s_menuitem *); +typedef void (*t_keys_handler)(struct s_menusystem *, struct s_menuitem *, + unsigned int scancode); + // Last parameter = HIGH BYTE = scan code , LOW BYTE = ASCII CODE + +typedef enum {HDLR_SCREEN, HDLR_KEYS } t_handler; +// Types of handlers for menu system + +// TIMEOUT is the list of possible values which can be returned by the handler +// instructing the menusystem what to do. The default is CODE_WAIT +typedef enum {CODE_WAIT, CODE_ENTER, CODE_ESCAPE } TIMEOUTCODE; +typedef TIMEOUTCODE (*t_timeout_handler)(void); + +typedef struct s_menuitem { + char *item; + char *status; + char *data; // string containing kernel to run.. but... + // for radio menu's this is a pointer to the item selected or NULL (initially) + // for submenu's this string could be name of menu + void * extra_data; // Any other data user can point to + unsigned int helpid; // Used for Context sensitive help + t_item_handler handler; // Pointer to function of type menufn + t_action action; + t_itemdata itemdata; // Data depends on action value + uchar shortcut; // one of [A-Za-z0-9] shortcut for this menu item + uchar index; // Index within the menu array + uchar parindex; // Index of the menu in which this item appears. + +} t_menuitem; + +typedef t_menuitem *pt_menuitem; // Pointer to type menuitem + +typedef struct s_menu { + pt_menuitem *items; // pointer to array of pointer to menuitems + char *title; // Title string for menu + char *name; // menu can be referred to by this string + int maxmenusize; // the size of array allocated + uchar numitems; // how many items do we actually have + uchar menuwidth; + uchar row,col; // Position where this menu should be displayed + uchar menuheight; // Maximum number of items to be displayed +} t_menu; + +typedef t_menu *pt_menu; // Pointer to type menu + +typedef struct s_menusystem { + pt_menu menus[MAXMENUS]; + char *title; + t_menusystem_handler handler; // Menu system handler + t_keys_handler keys_handler; // Handler for unknown keys + t_timeout_handler ontimeout; // Timeout handler + unsigned long tm_numsteps; + // Time to wait for key press=numsteps * stepsize milliseconds + unsigned int tm_stepsize; // Timeout step size (in milliseconds) + // Total timeout max time spent idle before we call handler + unsigned long tm_total_timeout; // (in milli seconds) + unsigned long tm_sofar_timeout; // All accumulated timeout + // total timeout handler + t_timeout_handler ontotaltimeout; // Total timeout handler + + int maxmenuheight; + uchar nummenus; + uchar normalattr[2]; // [0] is non-hlite attr, [1] is hlite attr + uchar reverseattr[2]; + uchar inactattr[2]; + uchar revinactattr[2]; + uchar statusattr[2]; + uchar fillchar; + uchar fillattr; + uchar spacechar; + uchar tfillchar; + uchar titleattr; + uchar shadowattr; + uchar statline; + uchar menupage; + uchar maxrow,minrow,numrows; // Number of rows in the window + uchar maxcol,mincol,numcols; // Number of columns in the window + + // Menu box look + boxtype menubt; // What type of boxes should be drawn + char box_horiz,box_ltrt,box_rtlt; // Some chars of the box, for redrawing portions of the box + +} t_menusystem; + +typedef t_menusystem *pt_menusystem; // Pointer to type menusystem + +pt_menuitem showmenus(uchar startmenu); + +pt_menusystem init_menusystem(const char *title); + +void close_menusystem(); // Deallocate memory used + +void set_normal_attr(uchar normal, uchar selected, uchar inactivenormal, uchar inactiveselected); + +void set_normal_hlite(uchar normal, uchar selected, uchar inactivenormal, uchar inactiveselected); + +void set_status_info(uchar statusattr, uchar statushlite, uchar statline); + +void set_title_info(uchar tfillchar, uchar titleattr); + +void set_misc_info(uchar fillchar, uchar fillattr,uchar spacechar, uchar shadowattr); +void set_box_type(boxtype bt); + +void set_window_size(uchar top, uchar left, uchar bot, uchar right); // Set the window which menusystem should use + +void set_menu_options(uchar maxmenuheight); +// maximum height of a menu + +void reg_handler(t_handler htype, void * handler); // Register handler + +void unreg_handler( t_handler htype); + +void reg_ontimeout(t_timeout_handler, unsigned int numsteps, unsigned int stepsize); +// Set timeout handler, set 0 for default values. +// So stepsize=0 means numsteps is measured in centiseconds. +void unreg_ontimeout(); + +void reg_ontotaltimeout(t_timeout_handler, unsigned long numcentiseconds); +void unreg_ontotaltimeout(); + +// Find the number of the menu given the name +// Returns -1 if not found +uchar find_menu_num(const char *name); + +// Create a new menu and return its position +uchar add_menu(const char *title, int maxmenusize); + +// Create a named menu and return its position +uchar add_named_menu(const char *name, const char *title, int maxmenusize); + +void set_menu_pos(uchar row,uchar col); // Set the position of this menu. + +// Add item to the "current" menu +pt_menuitem add_item(const char *item, const char *status, t_action action, const char *data, uchar itemdata); + +// Set shortcut key and help id +void set_item_options(uchar shortcut,int helpid); + +// Set the shortcut key for the current item +static inline void set_shortcut(uchar shortcut) +{ + set_item_options(shortcut,0xFFFF); +} + +// Add a separator to the "current" menu +pt_menuitem add_sep(); + +// Generate string based on state of checkboxes and radioitem in given menu +// and append string to existing contents of "line" +// line must have enough space allocated +void gen_append_line(const char *menu_name,char *line); + +#endif diff --git a/com32/cmenu/libmenu/menu.o b/com32/cmenu/libmenu/menu.o new file mode 100644 index 0000000000000000000000000000000000000000..7eda558273e552878cf668d0ae67253934af6496 GIT binary patch literal 15964 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 +#include +#include "tui.h" + +#define MAX_LINE 512 +// Max line length in a pwdfile +p_pwdentry userdb[MAX_USERS]; // Array of pointers +int numusers; // Actual number of users + +// returns true or false, i.e. 1 or 0 +char authenticate_user(const char * username, const char* pwd) +{ + char salt[12]; + int i; + + for (i=0; i< numusers; i++) { + if (userdb[i] == NULL) continue; + if (strcmp(username,userdb[i]->username)==0) { + strcpy(salt, userdb[i]->pwdhash); + salt[2] = '\0'; + if (strcmp(userdb[i]->pwdhash,crypt(pwd,salt))==0) return 1; + } + } + return 0; +} + +// Does user USERNAME have permission PERM +char isallowed(const char *username, const char *perm) +{ + int i; + char *dperm; + char *tmp; + + // If no users, then everybody is allowed to do everything + if (numusers == 0) return 1; + if (strcmp(username,GUEST_USER) == 0) return 0; + dperm = (char *) malloc(strlen(perm)+3); + strcpy(dperm+1,perm); + dperm[0] = ':'; + dperm[strlen(perm)+1]=':'; + dperm[strlen(perm)+2]=0; + // Now dperm = ":perm:" + for (i=0; i < numusers; i++) { + if (strcmp(userdb[i]->username,username)==0) // Found the user + { + if (userdb[i]->perms == NULL) return 0; // No permission + tmp = strstr(userdb[i]->perms,dperm); // Search for permission + free (dperm); // Release memory + if (tmp == NULL) return 0; else return 1; + } + } + // User not found return 0 + free (dperm); + return 0; +} + +// Initialise the list of of user passwords permissions from file +void init_passwords(const char *filename) +{ + int i; + char line[MAX_LINE], *p,*user,*pwdhash,*perms; + FILE *f; + + for (i=0; i < MAX_USERS; i++) userdb[i] = NULL; + numusers = 0; + + if ( !filename ) return; // No filename specified + + f = fopen(filename,"r"); + if ( !f ) return; // File does not exist + + // Process each line + while ( fgets(line, sizeof line, f) ) { + // Replace EOLN with \0 + p = strchr(line, '\r'); + if ( p ) *p = '\0'; + p = strchr(line, '\n'); + if ( p ) *p = '\0'; + + // If comment line or empty ignore line + p = line; + while (*p==' ') p++; // skip initial spaces + if ( (*p == '#') || (*p == '\0')) continue; // Skip comment lines + + user = p; // This is where username starts + p = strchr(user,':'); + if (p == NULL) continue; // Malformed line skip + *p = '\0'; + pwdhash = p+1; + if (*pwdhash == 0) continue; // Malformed line (no password specified) + p = strchr(pwdhash,':'); + if (p == NULL) { // No perms specified + perms = NULL; + } else { + *p = '\0'; + perms = p+1; + if (*perms == 0) perms = NULL; + } + // At this point we have user,pwdhash and perms setup + userdb[numusers] = (p_pwdentry)malloc(sizeof(pwdentry)); + strcpy(userdb[numusers]->username,user); + strcpy(userdb[numusers]->pwdhash,pwdhash); + if (perms == NULL) + userdb[numusers]->perms = NULL; + else { + userdb[numusers]->perms = (char *)malloc(strlen(perms)+3); + (userdb[numusers]->perms)[0] = ':'; + strcpy(userdb[numusers]->perms + 1,perms); + (userdb[numusers]->perms)[strlen(perms)+1] = ':'; + (userdb[numusers]->perms)[strlen(perms)+2] = 0; + // Now perms field points to ":perms:" + } + numusers++; + } + fclose(f); +} + +void close_passwords() +{ + int i; + + for (i=0; i < numusers; i++) + if (userdb[i] != NULL) free(userdb[i]); + numusers = 0; +} diff --git a/com32/cmenu/libmenu/passwords.h b/com32/cmenu/libmenu/passwords.h new file mode 100644 index 0000000..00e5702 --- /dev/null +++ b/com32/cmenu/libmenu/passwords.h @@ -0,0 +1,27 @@ +#ifndef _PASSWORDS_H_ +#define _PASSWORDS_H_ + +char authenticate_user(const char * username, const char* pwd); + +char isallowed(const char *username, const char * perm); + +// Initialise the list of of user passwords permissions from file +void init_passwords(const char *filename); +// Free all space used for internal data structures +void close_passwords(); + +#define MAX_USERS 128 // Maximum number of users +#define USERNAME_LENGTH 12 // Max length of user name +#define PWDHASH_LENGTH 40 // Max lenght of pwd hash + +typedef struct { + char username[USERNAME_LENGTH+1]; + char pwdhash[PWDHASH_LENGTH+1]; + char *perms; // pointer to string containing ":" delimited permissions +} pwdentry; + +typedef pwdentry *p_pwdentry; + +#define GUEST_USER "guest" + +#endif diff --git a/com32/cmenu/libmenu/passwords.o b/com32/cmenu/libmenu/passwords.o new file mode 100644 index 0000000000000000000000000000000000000000..036f99ecbf983862c33e76f392ae3e99ef6973c3 GIT binary patch literal 2508 zcma)7ZD?Cn7=CY(ZEDcArCC#3Y`Z$sHKr3w(RJ0a=}Ma3w!yZkxi_qp zf-Pxe*(D&z5D^jmrT&m2sHGqTJKKq%D2QVGE9*?5u0M3jG(PXS=Qim@!9#DJ^E}Uc z-uIrb+m{~eiztdhR#V7HWkwWonNF*K20B1)8c1)tgN0(P;d+zROkUr!ajR4+^&Gp0 z`P{YjJ&fGWoISTX_{`G-3!7dCQSjS!*JaZnI54a&#u|SzJZ+6^;=pvtfTd z)XXKQ*WYEU8dfgs_X-=Ooof*H|BIN@{obMyxwN;-r~lD)#IqQ|@I_QO&S^R^BKxaM z;#qtE8WQEkmH8QkJd1bl#B5lFYS@2L$)5JRIVR;^5BuH7LWY?2au>ShJDbb$w(wjT zhU~U*Dy4M3I{S-I({soptkfNb8NVKC=V8}#_%T{VieMrAMae+u#UHjm`2Cw)x5F$J zX|K#`gxc4r)24(Byt7%VGKDQ_&E@XgRl@Fv-3pb&&KiCG`W3>4R&0bP6aBkXGO%Bw z(!v$q@N?&fo)i0@-t_WTX1`lf(vR@eu*RR@cLhPTGl=*9EePqaO2I<*Ms~|K*Ra1T zBo_@UQMIg*GS_sKlZkGo z;JKVH*7S4~=gML*pT6SVHMP&P-v@sy_8Z=hxKgH|pUD20wN6f|EjLh&m8uaL*TVif z%ANMtWN(_qJ~Lcm6b^)0GM{oSDa8YkV{M#l?)`E(&?JqeR81%9?dj?CHT3omHu_ov z4+UC$EzQmC!C-Tnui=zB;yW4DO}**iMxuZ=JE=#9fqK%!ajTb9Cjz=Uqf=lcsz)g> ztZ5WTCYW$^Fc73bEHOE$P9bC}p{oJZY|`{-?0J5@|J&|Gpn_+w9Khd`S*Iib`I(_Z z=-*bB=^(DyG6u57<2jCR-F=ciX>r^c8N*=ZVC1`EwVSwz^AP#V$M4DWt--_V zRGDvPAJOa3tY+o2xP_Ivi*A%GopJYn1m=|$m|LrDj&hRtO6;U?5?-RBIdKa3GtTWO zo;u8uzo8-vCRt0{B|n?8@w{+|h0ihU^FHxdUKAeR&3)N8zSkl^=KVH4Xyeb?_^^$~ z!H=R=0m$_ +#include +#include "syslnx.h" + +com32sys_t inreg,outreg; // Global registers for this module + +char issyslinux(void) +{ + REG_EAX(inreg) = 0x00003000; + REG_EBX(inreg) = REG_ECX(inreg) = REG_EDX(inreg) = 0xFFFFFFFF; + __intcall(0x21,&inreg,&outreg); + return (REG_EAX(outreg) == 0x59530000) && + (REG_EBX(outreg) == 0x4c530000) && + (REG_ECX(outreg) == 0x4e490000) && + (REG_EDX(outreg) == 0x58550000); +} + +void runsyslinuxcmd(const char *cmd) +{ + strcpy(__com32.cs_bounce, cmd); + REG_AX(inreg) = 0x0003; // Run command + REG_BX(inreg) = OFFS(__com32.cs_bounce); + REG_ES(inreg) = SEG(__com32.cs_bounce); + __intcall(0x22, &inreg, &outreg); +} + +void gototxtmode(void) +{ + REG_AX(inreg) = 0x0005; + __intcall(0x22,&inreg,&outreg); +} + +void syslinux_idle(void) +{ + REG_AX(inreg) = 0x0013; + __intcall(0x22,&inreg,&outreg); +} + +unsigned int getversion(char *deriv,unsigned int *numfun) +{ + REG_AX(inreg) = 0x0001; + __intcall(0x22,&inreg,&outreg); + if (deriv) *deriv= REG_DL(outreg); + if (numfun) *numfun = REG_AX(outreg); + return REG_CX(outreg); +} + +void runsyslinuximage(const char*cmd, long ipappend) +{ + unsigned int numfun = 0; + char *ptr,*cmdline; + + getversion(NULL,&numfun); + // Function 16h not supported Fall back to runcommand + if (numfun < 0x16) runsyslinuxcmd(cmd); + // Try the Run Kernel Image function + // Split command line into + strcpy(__com32.cs_bounce,cmd); + ptr = __com32.cs_bounce; + // serach for first space or end of string + while ( (*ptr) && (*ptr != ' ')) ptr++; + if (!*ptr) cmdline = ptr; // no command line + else { + *ptr++='\0'; // terminate kernal name + cmdline = ptr+1; + while (*cmdline != ' ') cmdline++; // find first non-space + } + // Now call the interrupt + REG_BX(inreg) = OFFS(cmdline); + REG_ES(inreg) = SEG(cmdline); + REG_SI(inreg) = OFFS(__com32.cs_bounce); + REG_DS(inreg) = SEG(__com32.cs_bounce); + REG_EDX(inreg) = 0; + + __intcall(0x22,&inreg,&outreg); // If successful does not return +} diff --git a/com32/cmenu/libmenu/syslnx.h b/com32/cmenu/libmenu/syslnx.h new file mode 100644 index 0000000..755b969 --- /dev/null +++ b/com32/cmenu/libmenu/syslnx.h @@ -0,0 +1,64 @@ +/* -*- c -*- ------------------------------------------------------------- * + * + * Copyright 2004-2005 Murali Krishnan Ganapathy - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef __SYSLNX_H__ +#define __SYSLNX_H__ + +#include + +//Macros which help user not have to remember the structure of register +// Data structure + +#define REG_AH(x) ((x).eax.b[1]) +#define REG_AL(x) ((x).eax.b[0]) +#define REG_AX(x) ((x).eax.w[0]) +#define REG_EAX(x) ((x).eax.l) + +#define REG_BH(x) ((x).ebx.b[1]) +#define REG_BL(x) ((x).ebx.b[0]) +#define REG_BX(x) ((x).ebx.w[0]) +#define REG_EBX(x) ((x).ebx.l) + +#define REG_CH(x) ((x).ecx.b[1]) +#define REG_CL(x) ((x).ecx.b[0]) +#define REG_CX(x) ((x).ecx.w[0]) +#define REG_ECX(x) ((x).ecx.l) + +#define REG_DH(x) ((x).edx.b[1]) +#define REG_DL(x) ((x).edx.b[0]) +#define REG_DX(x) ((x).edx.w[0]) +#define REG_EDX(x) ((x).edx.l) + +#define REG_DS(x) ((x).ds) +#define REG_ES(x) ((x).es) +#define REG_FS(x) ((x).fs) +#define REG_GS(x) ((x).gs) + +#define REG_SI(x) ((x).esi.w[0]) +#define REG_ESI(x) ((x).esi.l) + +#define REG_DI(x) ((x).edi.w[0]) +#define REG_EDI(x) ((x).edi.l) + +char issyslinux(void); /* Check if syslinux is running */ + +void runsyslinuxcmd(const char *cmd); /* Run specified command */ + +void gototxtmode(void); /* Change mode to text mode */ + +void syslinux_idle(void); /* Call syslinux idle loop */ + +/* Run command line with ipappend, returns if kernel image not found + If syslinux version too old, then defaults to runsyslinuxcmd */ +void runsyslinuximage(const char*cmd, long ipappend); + +#endif diff --git a/com32/cmenu/libmenu/syslnx.o b/com32/cmenu/libmenu/syslnx.o new file mode 100644 index 0000000000000000000000000000000000000000..19c7b07aa44c97df6a45350710068686d4634cca GIT binary patch literal 1880 zcma)7U1%It6h5;*jg_=(Yytr-j%9@en`Jkxw1(!PR{H~?SYx!d*tpr9jf?wJc4u@W zN*mUqqf`*|NgpcpLBSUh)V42cw;B;2>WdV8@aL(Tf>^K*Dy-i(bFP^LK@Z&d&Uena z=bU@y&hCi^ruJHvB}OcX%L)@Y6AL}t0~nE;rB5E2ZeL6IUH_ZXQZfy@CFkfp&&MZQ z@VNR{uh%>Eo!fWjcCeS?r*63GQy&Mh{yHc&tVfO>>+1eZYxn=m{pw8qtFwKr-@o{F zzp16MaS2TR#r99Bl0P&I_or7ArLzerYg%ky>JO^mD8qht>HhQ}V&wG0>A_qlXuthj zdT{8OKL1L<{BKu^y|RIIm#y6|TQ79n#4g+W-TxBW#?LJ|eJHkU-FI@r_WtlMw|_{L z&IV2Vl0ZA`VpV@k6ZE$Z%2JZEqO9pO?B>$&(Jl@h4qUP(9&_p~1@jCwR-+claVJbc zj>X9|)64W@-ss{aBcV@un-?eTKtFKM8_0DQC&NBw4yRu4EXQ&kOsWh2zCC*;?2&y3 zXGZPqnQfVIdpw)nnagGGu}2+?>ts-5tm)dB;%)+EIOC|_r92L#jOcd|vM`SN-B^tOuQiQu!e+$eGu&X*o{-;q#!g)QOF|y4 zK+ftb694Dzg|@gP@p}^r&vq*&GX&(Wtylwcy~p<%##b8^llhGFu)(!Nu1x8f8Lans5VD=#Y^S3A$kIG?ytZ2f;R60j+#qs;cI5k$9QpW{L_S7Dy&a0X)V^Q&A;m`( ypCF=+qr48}xt~}5qT*|cZz{e+#J=~Ge+cBhb87!g@oUBJ73bJ5;!Y@jq4+nwA1nF* literal 0 HcmV?d00001 diff --git a/com32/cmenu/libmenu/tui.c b/com32/cmenu/libmenu/tui.c new file mode 100644 index 0000000..cb8c193 --- /dev/null +++ b/com32/cmenu/libmenu/tui.c @@ -0,0 +1,357 @@ +/* -*- c -*- ------------------------------------------------------------- * + * + * Copyright 2004-2006 Murali Krishnan Ganapathy - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#include "tui.h" +#include +#include +#include + +com32sys_t inreg,outreg; // Global register sets for use + +char bkspstr[] = " \b$"; +char eolstr[] = "\n$"; +#define GETSTRATTR 0x07 + +// Reads a line of input from stdin. Replace CR with NUL byte +// password <> 0 implies not echoed on screen +// showoldvalue <> 0 implies currentvalue displayed first +// If showoldvalue <> 0 then caller responsibility to ensure that +// str is NULL terminated. +void getuserinput(char *stra, unsigned int size, unsigned int password, + unsigned int showoldvalue) +{ + unsigned char c,scan; + char *p,*q; // p = current char of string, q = tmp + char *last; // The current last char of string + char *str; // pointer to string which is going to be allocated + char page; + char row,col; + char start,end; // Cursor shape + char fudge; // How many chars should be removed from output + char insmode; // Are we in insert or overwrite + + page = getdisppage(); + getpos(&row,&col,page); // Get current position + getcursorshape(&start,&end); + insmode = 1; + + str = (char *)malloc(size+1); // Allocate memory to store user input + memset(str,0,size+1); // Zero it out + if (password != 0) showoldvalue = 0; // Password's never displayed + + if (showoldvalue != 0) strcpy(str,stra); // If show old value copy current value + + last = str; + while (*last) {last++;} // Find the terminating null byte + p = str+ strlen(str); + + if (insmode == 0) + setcursorshape(1,7); // Block cursor + else setcursorshape(6,7); // Normal cursor + + // Invariants: p is the current char + // col is the corresponding column on the screen + if (password == 0) // Not a password, print initial value + { + gotoxy(row,col,page); + csprint(str,GETSTRATTR); + } + while (1) { // Do forever + c = inputc(&scan); + if (c == '\r') break; // User hit Enter getout of loop + if (scan == ESCAPE) // User hit escape getout and nullify string + { *str = 0; + break; + } + fudge = 0; + // if scan code is regognized do something + // else if char code is recognized do something + // else ignore + switch(scan) { + case HOMEKEY: + p = str; + break; + case ENDKEY: + p = last; + break; + case LTARROW: + if (p > str) p--; + break; + case CTRLLT: + if (p==str) break; + if (*p == ' ') + while ((p > str) && (*p == ' ')) p--; + else { + if (*(p-1) == ' ') { + p--; + while ((p > str) && (*p == ' ')) p--; + } + } + while ((p > str) && ((*p == ' ') || (*(p-1) != ' '))) p--; + break; + case RTARROW: + if (p < last) p++; + break; + case CTRLRT: + if (*p==0) break; // At end of string + if (*p != ' ') + while ((*p!=0) && (*p != ' ')) p++; + while ((*p!=0) && ((*p == ' ') && (*(p+1) != ' '))) p++; + if (*p==' ') p++; + break; + case DELETE: + q = p; + while (*(q+1)) {*q = *(q+1); q++; } + if (last > str) last--; + fudge = 1; + break; + case INSERT: + insmode = 1-insmode; // Switch mode + if (insmode == 0) + setcursorshape(1,7); // Block cursor + else setcursorshape(6,7); // Normal cursor + break; + + default: // Unrecognized scan code, look at the ascii value + switch (c) { + case '\b': // Move over by one + q=p; + while ( q <= last ) { *(q-1)=*q; q++;} + if (last > str) last--; + if (p > str) p--; + fudge = 1; + break; + case '\x15': /* Ctrl-U: kill input */ + fudge = last-str; + while ( p > str ) *p--=0; + p = str; *p=0; last = str; + break; + default: // Handle insert and overwrite mode + if ((c >= ' ') && (c < 128) && + ((unsigned int)(p-str) < size-1) ) { + if (insmode == 0) { // Overwrite mode + if (p==last) last++; + *last = 0; + *p++ = c; + } else { // Insert mode + if (p==last) { // last char + last++; + *last=0; + *p++=c; + } else { // Non-last char + q=last++; + while (q >= p) { *q=*(q-1); q--;} + *p++=c; + } + } + } + else beep(); + } + break; + } + // Now the string has been modified, print it + if (password == 0) { + gotoxy(row,col,page); + csprint(str,GETSTRATTR); + if (fudge > 0) cprint(' ',GETSTRATTR,fudge,page); + gotoxy(row,col+(p-str),page); + } + } + *p = '\0'; + if (password == 0) csprint("\r\n",GETSTRATTR); + setcursorshape(start,end); // Block cursor + // If user hit ESCAPE so return without any changes + if (scan != ESCAPE) strcpy(stra,str); + free(str); +} + +/* Print a C string (NUL-terminated) */ +void cswprint(const char *str,char attr,char left) +{ + char page = getdisppage(); + char newattr=0,cha,chb; + char row,col; + char nr,nc; + + nr = getnumrows(); + nc = getnumcols(); + getpos(&row,&col,page); + while ( *str ) { + switch (*str) + { + case '\b': + --col; + break; + case '\n': + ++row; + col = left; + break; + case '\r': + //col=left; + break; + case BELL: // Bell Char + beep(); + break; + case CHRELATTR: // change attribute (relatively) + case CHABSATTR: // change attribute (absolute) + cha = *(str+1); + chb = *(str+2); + if ((((cha >= '0') && (cha <= '9')) || + ((cha >= 'A') && (cha <= 'F'))) && + (((chb >= '0') && (chb <= '9')) || + ((chb >= 'A') && (chb <= 'F')))) // Next two chars are legal + { + if ((cha >= 'A') && (cha <= 'F')) + cha = cha - 'A'+10; + else cha = cha - '0'; + if ((chb >= 'A') && (chb <= 'F')) + chb = chb - 'A'+10; + else chb = chb - '0'; + newattr = (cha << 4) + chb; + attr = (*str == CHABSATTR ? newattr : attr ^ newattr); + str += 2; // Will be incremented again later + } + break; + default: + putch(*str, attr, page); + ++col; + } + if (col >= nc) + { + ++row; + col=left; + } + if (row > nr) + { + scrollup(); + row= nr; + } + gotoxy(row,col,page); + str++; + } +} + +void clearwindow(char top, char left, char bot, char right, char page, char fillchar, char fillattr) +{ + char x; + for (x=top; x < bot+1; x++) + { + gotoxy(x,left,page); + cprint(fillchar,fillattr,right-left+1,page); + } +} + +void cls(void) +{ + unsigned char dp = getdisppage(); + gotoxy(0,0,dp); + cprint(' ',GETSTRATTR,(1+getnumrows())*getnumcols(),dp); +} + +//////////////////////////////Box Stuff + +// This order of numbers must match +// the values of BOX_TOPLEFT,... in the header file + +unsigned char SINSIN_CHARS[] = {218,192,191,217, //Corners + 196,179, // Horiz and Vertical + 195,180,194,193,197}; // Connectors & Middle + +unsigned char DBLDBL_CHARS[] = {201,200,187,188, // Corners + 205,186, // Horiz and Vertical + 199,182,203,202,206}; // Connectors & Middle + +unsigned char SINDBL_CHARS[] = {214,211,183,189, // Corners + 196,186, // Horiz & Vert + 199,182,210,208,215}; // Connectors & Middle + +unsigned char DBLSIN_CHARS[] = {213,212,184,190, // Corners + 205,179, // Horiz & Vert + 198,181,209,207,216}; // Connectors & Middle + +unsigned char * getboxchars(boxtype bt) +{ + switch (bt) + { + case BOX_SINSIN: + return SINSIN_CHARS; + break; + case BOX_DBLDBL: + return DBLDBL_CHARS; + break; + case BOX_SINDBL: + return SINDBL_CHARS; + break; + case BOX_DBLSIN: + return DBLSIN_CHARS; + break; + default: + return SINSIN_CHARS; + break; + } + return SINSIN_CHARS; +} + +// Draw box and lines +void drawbox(char top,char left,char bot, char right, + char page, char attr,boxtype bt) +{ + unsigned char *box_chars; // pointer to array of box chars + unsigned char x; + + box_chars = getboxchars(bt); + // Top border + gotoxy(top,left,page); + cprint(box_chars[BOX_TOPLEFT],attr,1,page); + gotoxy(top,left+1,page); + cprint(box_chars[BOX_TOP],attr,right-left,page); + gotoxy(top,right,page); + cprint(box_chars[BOX_TOPRIGHT],attr,1,page); + // Bottom border + gotoxy(bot,left,page); + cprint(box_chars[BOX_BOTLEFT],attr,1,page); + gotoxy(bot,left+1,page); + cprint(box_chars[BOX_BOT],attr,right-left,page); + gotoxy(bot,right,page); + cprint(box_chars[BOX_BOTRIGHT],attr,1,page); + // Left & right borders + for (x=top+1; x < bot; x++) + { + gotoxy(x,left,page); + cprint(box_chars[BOX_LEFT],attr,1,page); + gotoxy(x,right,page); + cprint(box_chars[BOX_RIGHT],attr,1,page); + } +} + +void drawhorizline(char top, char left, char right, char page, char attr, + boxtype bt, char dumb) +{ + unsigned char start,end; + unsigned char *box_chars = getboxchars(bt); + if (dumb==0) { + start = left+1; + end = right-1; + } else { + start = left; + end = right; + } + gotoxy(top,start,page); + cprint(box_chars[BOX_HORIZ],attr,end-start+1,page); + if (dumb == 0) + { + gotoxy(top,left,page); + cprint(box_chars[BOX_LTRT],attr,1,page); + gotoxy(top,right,page); + cprint(box_chars[BOX_RTLT],attr,1,page); + } +} diff --git a/com32/cmenu/libmenu/tui.h b/com32/cmenu/libmenu/tui.h new file mode 100644 index 0000000..92f9386 --- /dev/null +++ b/com32/cmenu/libmenu/tui.h @@ -0,0 +1,88 @@ +/* -*- c -*- ------------------------------------------------------------- * + * + * Copyright 2004-2005 Murali Krishnan Ganapathy - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef __TUI_H__ +#define __TUI_H__ + +#include +#include "syslnx.h" +#include "com32io.h" +#include "scancodes.h" + + +#ifndef NULL +#define NULL ((void *)0) +#endif + +#define BELL 0x07 +// CHRELATTR = ^N, CHABSATTR = ^O +#define CHABSATTR 15 +#define CHRELATTR 14 + +void clearwindow(char top, char left, char bot, char right, + char page, char fillchar, char fillattr); + +void cls(void); /* Clears the entire current screen page */ + +// Generic user input, +// password = 0 iff chars echoed on screen +// showoldvalue <> 0 iff current displayed for editing +void getuserinput(char *str, unsigned int size, + unsigned int password, unsigned int showoldvalue); + +static inline void getstring(char *str, unsigned int size) +{ + getuserinput(str,size,0,0); +} + +static inline void editstring(char *str, unsigned int size) +{ + getuserinput(str,size,0,1); +} + +static inline void getpwd(char * str, unsigned int size) +{ + getuserinput(str,size,1,0); +} + +// Box drawing Chars offsets into array +#define BOX_TOPLEFT 0x0 +#define BOX_BOTLEFT 0x1 +#define BOX_TOPRIGHT 0x2 +#define BOX_BOTRIGHT 0x3 +#define BOX_TOP 0x4 // TOP = BOT = HORIZ +#define BOX_BOT 0x4 +#define BOX_HORIZ 0x4 +#define BOX_LEFT 0x5 +#define BOX_RIGHT 0x5 +#define BOX_VERT 0x5 // LEFT=RIGHT=VERT +#define BOX_LTRT 0x6 +#define BOX_RTLT 0x7 +#define BOX_TOPBOT 0x8 +#define BOX_BOTTOP 0x9 +#define BOX_MIDDLE 0xA + +typedef enum {BOX_SINSIN,BOX_DBLDBL, BOX_SINDBL, BOX_DBLSIN} boxtype; + +unsigned char * getboxchars(boxtype bt); + +void drawbox(char top,char left,char bot, char right, + char page, char attr,boxtype bt); + +// Draw a horizontal line +// dumb == 1, means just draw the line +// dumb == 0 means check the first and last positions and depending on what is +// currently on the screen make it a LTRT and/or RTLT appropriately. +void drawhorizline(char top, char left, char right, char page, char attr, + boxtype bt, char dumb); + +#endif diff --git a/com32/cmenu/libmenu/tui.o b/com32/cmenu/libmenu/tui.o new file mode 100644 index 0000000000000000000000000000000000000000..c5b35112397a8219fa2dd960a9772e4d80dbfc9e GIT binary patch literal 4548 zcma)Adu&tJ89&!{5)+c#&?X^3*1F|Z3JF*-BO(uXY9KElO(_Ymifq(zTnL2NwXX?K z%A(mRxsE(-+O&r>^`A*&52>vL%V@hg4?;?YS}0u^v|FjPPQno}CMT$atW@zcbG?wZu4f-TXbHG_Fh?2TasG`sfK+MsU zrKw8Ayj5tTWY@QMe=AN2geG!`76m9l0i1*NN|tsR%g&6{D;3}}fa_3>DwM|Z&U_67 zg&J@QHQ*@6?r78cLalPF1F`0`aqWso1rfA%B?rruV-0Mlky8m*M;j&G(YC;+UMWP0 z6m7CxRf^$e(VPm_*RJSZ+8jt_euf4U)C=QMMT#(ODK{-f{YphT4blU~+{Tt}^Of_Z z8<-ymf{HDT`XuaJ=xj!!Rm0AI%mZ6Ro?l&n`gI{&U2ahewmj00?%kMJbEFP6T{lm4}j)#b1R$cm5Hm=u4)^7E`26 zitf7@-7w zoX^PNbLgnPBIg#h(fPC-{x#-zHpv5(kS5BVZK|y=*SSp|cmjNUhLizyjZ>8e3P>;r zfi@jK0~kUUaTj9Nf{ztigwyK=psa7A#0et-3{}E>Wiwk3<`j{l}8FU|~XP z#-X?NS+vQ7wZF;+u;}yvakILa|pq#d#IUzTaK(&JT z0v$G~t4Fz79-u1%S8@L_$rk)u{8!)xwXKS%b@+%)jF<*DBsWywI5=C4xS_xD`W!sc zn&_L%WPELXvs$98L$T6^PNJTZ+Sx=$wC*$K?!HahbUCD`NlxHN9ItW~%&oo^G)H@- z$p&Qbn05nf5wVRD8_d&g#Xm)6n`u(1dL}U2FqqdEKMxUahWHq$lM-(&dEm-_=;Qo9 z+bjIZu@Zia&*S`7=u5U45pm>jNUO`*(SrB@Tw2(v@gSzaGWA|wA{v+f9O3!U?CZ%z zKT}v>t;CNE)xxHqGvn-pCY=1#YLkvn;x+iJ+pG%9Rv7zubmZ#9$up;SKL5w}+2s#^|IP&HOBeslKKj$?*n1}@fA_(KE9@~`u~~S2h9_Sj(Ia@u#gmKQ zSWO!@*4Rp$TDPyVt+RjKzSg$3qN2*-sHn7+Zu4~6nq8_cU$cG{WA?z|1FEYNsQPu> zYfyeqk6rZ~QklKWrMj5CGZ0{Q68gQmKu7X)*d5I7_8vIk>4kZ(SM}I2|C)g6a_>jQ z|G)7l%&0x-CZR#U6aQ)DB!6Lve5}BGk)R2BWYhzPY%yr^Cp&U`LhxoFjZ!$R*Gc|1 z;+2CVd&BPe^x%*ic9ajr$5ecfog!!g!>$5!dWfS-7;A=%^2-o*G@la>#aoMqMi@>; z4#bm=o-G0ad>n_}8iN6n4UKj@22K2_^d!=BNN2%3WYmc!Un5CHi4 zXubJ@PS5-KJlGgPpid*grAE5k!fq#18pe*#gw?Vyc8b?Pt}An|TI z^mQ5$_WQwqj{1HVNdA4m7qIL)!M_AF2KgVya{~JB3IFdKN+9Wfavx7Wxr{;j-{Pr6 zLyjN@&Bw1dVL-2bhEa3KgdwPOC;R97L;6X@VP$g$~&>!&n1AARQ4?E!M>G8T5ZcVrEFbgbHat}9q z!SC^~&iw%&teD5!L(tvp_v~Tbph`eBlW^DP5~2Z@65?R;I~2k#LQg$N`rSgmhY90Z)HtVwBZwmfP!9RmNpn0AnL>xCE=Gjk(ILCn$=a+e2d_B3En689|-=0;7(I)V}9uJ{n literal 0 HcmV?d00001 diff --git a/com32/cmenu/menugen.py b/com32/cmenu/menugen.py new file mode 100644 index 0000000..70ec1f8 --- /dev/null +++ b/com32/cmenu/menugen.py @@ -0,0 +1,307 @@ +#!/usr/bin/env python + +import sys, re, getopt + +class Menusystem: + + types = {"run" : "OPT_RUN", + "inactive" : "OPT_INACTIVE", + "checkbox" : "OPT_CHECKBOX", + "radiomenu": "OPT_RADIOMENU", + "sep" : "OPT_SEP", + "invisible": "OPT_INVISIBLE", + "radioitem": "OPT_RADIOITEM", + "exitmenu" : "OPT_EXITMENU", + "login" : "login", # special type + "submenu" : "OPT_SUBMENU"} + + entry_init = { "item" : "", + "info" : "", + "data" : "", + "ipappend" : 0, # flag to send in case of PXELINUX + "helpid" : 65535, # 0xFFFF + "shortcut":"-1", + "state" : 0, # initial state of checkboxes + "argsmenu": "", # name of menu containing arguments + "perms" : "", # permission required to execute this entry + "_updated" : None, # has this dictionary been updated + "type" : "run" } + + menu_init = { "title" : "", + "row" : "0xFF", # let system decide position + "col" : "0xFF", + "_updated" : None, + "name" : "" } + + system_init ={ "videomode" : "0xFF", + "title" : "Menu System", + "top" : "1", + "left" : "1" , + "bot" : "21", + "right":"79", + "helpdir" : "/isolinux/help", + "pwdfile" : "", + "pwdrow" : "23", + "editrow" : "23", + "skipcondn" : "0", + "skipcmd" : ".exit", + "startfile": "", + "onerrorcmd":".repeat", + "exitcmd" : ".exit", + "exitcmdroot" : "", + "timeout" : "600", + "timeoutcmd":".beep", + "totaltimeout" : "0", + "totaltimeoutcmd" : ".wait" + } + + shift_flags = { "alt" : "ALT_PRESSED", + "ctrl" : "CTRL_PRESSED", + "shift": "SHIFT_PRESSED", + "caps" : "CAPSLOCK_ON", + "num" : "NUMLOCK_ON", + "ins" : "INSERT_ON" + } + + reqd_templates = ["item","login","menu","system"] + + def __init__(self,template): + self.state = "system" + self.code_template_filename = template + self.menus = [] + self.init_entry() + self.init_menu() + self.init_system() + self.vtypes = " OR ".join(self.types.keys()) + self.vattrs = " OR ".join(filter(lambda x: x[0] != "_", self.entry.keys())) + self.mattrs = " OR ".join(filter(lambda x: x[0] != "_", self.menu.keys())) + + def init_entry(self): + self.entry = self.entry_init.copy() + + def init_menu(self): + self.menu = self.menu_init.copy() + + def init_system(self): + self.system = self.system_init.copy() + + def add_menu(self,name): + self.add_item() + self.init_menu() + self.menu["name"] = name + self.menu["_updated"] = 1 + self.menus.append( (self.menu,[]) ) + + def add_item(self): + if self.menu["_updated"]: # menu details have changed + self.menus[-1][0].update(self.menu) + self.init_menu() + if self.entry["_updated"]: + if not self.entry["info"]: + self.entry["info"] = self.entry["data"] + if not self.menus: + print "Error before line %d" % self.lineno + print "REASON: menu must be declared before a menu item is declared" + sys.exit(1) + self.menus[-1][1].append(self.entry) + self.init_entry() + + def set_item(self,name,value): + if not self.entry.has_key(name): + msg = ["Unknown attribute %s in line %d" % (name,self.lineno)] + msg.append("REASON: Attribute must be one of %s" % self.vattrs) + return "\n".join(msg) + if name=="type" and not self.types.has_key(value): + msg = [ "Unrecognized type %s in line %d" % (value,self.lineno)] + msg.append("REASON: Valid types are %s" % self.vtypes) + return "\n".join(msg) + if name=="shortcut": + if (value <> "-1") and not re.match("^[A-Za-z0-9]$",value): + msg = [ "Invalid shortcut char '%s' in line %d" % (value,self.lineno) ] + msg.append("REASON: Valid values are [A-Za-z0-9]") + return "\n".join(msg) + elif value <> "-1": value = "'%s'" % value + elif name in ["state","helpid","ipappend"]: + try: + value = int(value) + except: + return "Value of %s in line %d must be an integer" % (name,self.lineno) + self.entry[name] = value + self.entry["_updated"] = 1 + return "" + + def set_menu(self,name,value): + if not self.menu.has_key(name): + return "Error: Unknown keyword %s" % name + self.menu[name] = value + self.menu["_updated"] = 1 + return "" + + def set_system(self,name,value): + if not self.system.has_key(name): + return "Error: Unknown keyword %s" % name + if name == "skipcondn": + try: # is skipcondn a number? + a = int(value) + except: # it is a "-" delimited sequence + value = value.lower() + parts = [ self.shift_flags.get(x.strip(),None) for x in value.split("-") ] + self.system["skipcondn"] = " | ".join(filter(None, parts)) + else: + self.system[name] = value + + def set(self,name,value): + # remove quotes if given + if (value[0] == value[-1]) and (value[0] in ['"',"'"]): # remove quotes + value = value[1:-1] + if self.state == "system": + err = self.set_system(name,value) + if not err: return + if self.state == "menu": + err = self.set_menu(name,value) + # change state to entry it menu returns error + if err: + err = None + self.state = "item" + if self.state == "item": + err = self.set_item(name,value) + + if not err: return + + # all errors so return item's error message + print err + sys.exit(1) + + def print_entry(self,entry,fd): + entry["type"] = self.types[entry["type"]] + if entry["type"] == "login": #special type + fd.write(self.templates["login"] % entry) + else: + fd.write(self.templates["item"] % entry) + + def print_menu(self,menu,fd): + if menu["name"] == "main": self.foundmain = 1 + fd.write(self.templates["menu"] % menu) + if (menu["row"] != "0xFF") or (menu["col"] != "0xFF"): + fd.write(' set_menu_pos(%(row)s,%(col)s);\n' % menu) + + + def output(self,filename): + curr_template = None + contents = [] + self.templates = {} + regbeg = re.compile(r"^--(?P[a-z]+) BEGINS?--\n$") + regend = re.compile(r"^--[a-z]+ ENDS?--\n$") + ifd = open(self.code_template_filename,"r") + for line in ifd.readlines(): + b = regbeg.match(line) + e = regend.match(line) + if e: # end of template + if curr_template: + self.templates[curr_template] = "".join(contents) + curr_template = None + continue + if b: + curr_template = b.group("name") + contents = [] + continue + if not curr_template: continue # lines between templates are ignored + contents.append(line) + ifd.close() + + missing = None + for x in self.reqd_templates: + if not self.templates.has_key(x): missing = x + if missing: + print "Template %s required but not defined in %s" % (missing,self.code_template_filename) + + if filename == "-": + fd = sys.stdout + else: fd = open(filename,"w") + self.foundmain = None + fd.write(self.templates["header"]) + fd.write(self.templates["system"] % self.system) + for (menu,items) in self.menus: + self.print_menu(menu,fd) + for entry in items: self.print_entry(entry,fd) + fd.write(self.templates["footer"]) + fd.close() + if not self.foundmain: + print "main menu not found" + print self.menus + sys.exit(1) + + def input(self,filename): + if filename == "-": + fd = sys.stdin + else: fd = open(filename,"r") + self.lineno = 0 + self.state = "system" + for line in fd.readlines(): + self.lineno = self.lineno + 1 + if line and line[-1] in ["\r","\n"]: line = line[:-1] + if line and line[-1] in ["\r","\n"]: line = line[:-1] + line = line.strip() + if line and line[0] in ["#",";"]: continue + + try: + # blank line -> starting a new entry + if not line: + if self.state == "item": self.add_item() + continue + + # starting a new section? + if line[0] == "[" and line[-1] == "]": + self.state = "menu" + self.add_menu(line[1:-1]) + continue + + # add property of current entry + pos = line.find("=") # find the first = in string + if pos < 0: + print "Syntax error in line %d" % self.lineno + print "REASON: non-section lines must be of the form ATTRIBUTE=VALUE" + sys.exit(1) + attr = line[:pos].strip().lower() + value = line[pos+1:].strip() + self.set(attr,value) + except: + print "Error while parsing line %d: %s" % (self.lineno,line) + raise + fd.close() + self.add_item() + +def usage(): + print sys.argv[0]," [options]" + print "--input= is the name of the .menu file declaring the menu structure" + print "--output= is the name of generated C source" + print "--template= is the name of template to be used" + print + print "input and output default to - (stdin and stdout respectively)" + print "template defaults to adv_menu.tpl" + sys.exit(1) + +def main(): + tfile = "adv_menu.tpl" + ifile = "-" + ofile = "-" + opts,args = getopt.getopt(sys.argv[1:], "hi:o:t:",["input=","output=","template=","help"]) + if args: + print "Unknown options %s" % args + usage() + for o,a in opts: + if o in ["-i","--input"]: + ifile = a + elif o in ["-o", "--output"]: + ofile = a + elif o in ["-t","--template"]: + tfile = a + elif o in ["-h","--help"]: + usage() + + inst = Menusystem(tfile) + inst.input(ifile) + inst.output(ofile) + +if __name__ == "__main__": + main() diff --git a/com32/cmenu/password b/com32/cmenu/password new file mode 100644 index 0000000..3caffe2 --- /dev/null +++ b/com32/cmenu/password @@ -0,0 +1,18 @@ +# This file should be available as /isolinux/password +# for complex.c to use. +# +# All lines starting with # and empty lines are ignored +# +# All non-comment lines here are of the form +# USERNAME:PWDHASH:PERM1:PERM2:...: +# +# where USERNAME is maximum of 12 chars, +# PWDHASH is maximum of 40 chars (DES ENCRYPTED) +# PERM1,... are arbitrary strings +# +# The current lines correspond to +# user1:secret1, user2:secret2, user3:secret3 + +user1:LcMRo3YZGtP0c:editcmd +user2:FqewzyxP78a7A: +user3:MKjmc.IHoXBNU:root diff --git a/com32/cmenu/simple.c b/com32/cmenu/simple.c new file mode 100644 index 0000000..92e8ab1 --- /dev/null +++ b/com32/cmenu/simple.c @@ -0,0 +1,79 @@ +/* -*- c -*- ------------------------------------------------------------- * + * + * Copyright 2004-2005 Murali Krishnan Ganapathy - 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#ifndef NULL +#define NULL ((void *) 0) +#endif + +#include "menu.h" +#include "com32io.h" +#include + +int main(void) +{ + t_menuitem * curr; + + // Change the video mode here + // setvideomode(0) + + // Choose the default title and setup default values for all attributes.... + init_menusystem(NULL); + set_window_size(1,1,23,78); // Leave one row/col border all around + + // Choose the default values for all attributes and char's + // -1 means choose defaults (Actually the next 4 lines are not needed) + //set_normal_attr (-1,-1,-1,-1); + //set_status_info (-1,-1); + //set_title_info (-1,-1); + //set_misc_info(-1,-1,-1,-1); + + // menuindex = add_named_menu("name"," Menu Title ",-1); + // add_item("Item string","Status String",TYPE,"any string",NUM) + // TYPE = OPT_RUN | OPT_EXITMENU | OPT_SUBMENU | OPT_CHECKBOX | OPT_INACTIVE + // "any string" useful for storing kernel names + // In case of OPT_SUBMENU, "any string" can be set to "name" of menu to be linked + // in which case value NUM is ignored + // NUM = index of submenu if OPT_SUBMENU, + // 0/1 default checked state if OPT_CHECKBOX + // unused otherwise. + + add_named_menu("testing"," Testing ",-1); + add_item("Self Loop","Go to testing",OPT_SUBMENU,"testing",0); + add_item("Memory Test","Perform extensive memory testing",OPT_RUN, "memtest",0); + add_item("Exit this menu","Go one level up",OPT_EXITMENU,"exit",0); + + add_named_menu("rescue"," Rescue Options ",-1); + add_item("Linux Rescue","linresc",OPT_RUN,"linresc",0); + add_item("Dos Rescue","dosresc",OPT_RUN,"dosresc",0); + add_item("Windows Rescue","winresc",OPT_RUN,"winresc",0); + add_item("Exit this menu","Go one level up",OPT_EXITMENU,"exit",0); + + add_named_menu("main"," Main Menu ",-1); + add_item("Prepare","prep",OPT_RUN,"prep",0); + add_item("Rescue options...","Troubleshoot a system",OPT_SUBMENU,"rescue",0); + add_item("Testing...","Options to test hardware",OPT_SUBMENU,"testing",0); + add_item("Exit to prompt", "Exit the menu system", OPT_EXITMENU, "exit", 0); + + curr = showmenus(find_menu_num("main")); // Initial menu is the one called "main" + + if (curr) + { + if (curr->action == OPT_RUN) + { + if (issyslinux()) runsyslinuxcmd(curr->data); + else csprint(curr->data,0x07); + return 1; + } + csprint("Error in programming!",0x07); + } + return 0; +} diff --git a/com32/cmenu/simple.c32 b/com32/cmenu/simple.c32 new file mode 100755 index 0000000000000000000000000000000000000000..f651891a63a66b6f3b0c4ea7c792d6b417561f11 GIT binary patch literal 17008 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 literal 0 HcmV?d00001 diff --git a/com32/cmenu/test.c32 b/com32/cmenu/test.c32 new file mode 100755 index 0000000000000000000000000000000000000000..11fa8578f715f5a2af2d34b90b3fe968c2e5ed43 GIT binary patch literal 32140 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